[Spice-commits] 19 commits - client/audio_channels.h client/canvas.cpp client/canvas.h client/display_channel.cpp client/display_channel.h client/playback_channel.cpp client/record_channel.cpp client/red_channel.cpp client/red_channel.h client/red_client.cpp client/red_client.h client/red_gdi_canvas.cpp client/red_gdi_canvas.h client/red_gl_canvas.cpp client/red_gl_canvas.h client/red_sw_canvas.cpp client/red_sw_canvas.h common/messages.h configure.ac NEWS server/inputs_channel.c server/main_channel.c server/main_channel.h server/red_channel.c server/red_channel.h server/reds.c server/reds.h server/red_tunnel_worker.c server/red_worker.c server/smartcard.c server/snd_worker.c server/spice-experimental.h server/spice.h server/spice-server.syms server/spicevmc.c spice.proto

Yonit Halperin yhalperi at kemper.freedesktop.org
Wed Nov 2 02:36:22 PDT 2011


 NEWS                        |    8 
 client/audio_channels.h     |   12 
 client/canvas.cpp           |   25 +
 client/canvas.h             |  152 ----------
 client/display_channel.cpp  |  316 ++++++++++++++-------
 client/display_channel.h    |   29 --
 client/playback_channel.cpp |   22 +
 client/record_channel.cpp   |   39 +-
 client/red_channel.cpp      |   80 +++++
 client/red_channel.h        |   21 +
 client/red_client.cpp       |  156 +++++++++-
 client/red_client.h         |   17 +
 client/red_gdi_canvas.cpp   |    4 
 client/red_gdi_canvas.h     |    2 
 client/red_gl_canvas.cpp    |    4 
 client/red_gl_canvas.h      |    2 
 client/red_sw_canvas.cpp    |    6 
 client/red_sw_canvas.h      |    2 
 common/messages.h           |    2 
 configure.ac                |    4 
 server/inputs_channel.c     |    4 
 server/main_channel.c       |  332 ++++++++++++++--------
 server/main_channel.h       |   38 +-
 server/red_channel.c        |  109 ++++++-
 server/red_channel.h        |   39 ++
 server/red_tunnel_worker.c  |    3 
 server/red_worker.c         |    3 
 server/reds.c               |  636 +++++++++++++++++++++++++-------------------
 server/reds.h               |   22 +
 server/smartcard.c          |    4 
 server/snd_worker.c         |   66 +---
 server/spice-experimental.h |    3 
 server/spice-server.syms    |    1 
 server/spice.h              |   27 +
 server/spicevmc.c           |    4 
 spice.proto                 |    9 
 36 files changed, 1411 insertions(+), 792 deletions(-)

New commits:
commit aefe01c02c452bbc79532cb2011eaf12f8ecf58f
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Oct 9 14:44:14 2011 +0200

    Release 0.9.2

diff --git a/NEWS b/NEWS
index 8b56038..a862449 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,11 @@
+Major changes in 0.9.2:
+=======================
+* !Development Release!
+* server: semi-seamless migration support (RHBZ 738266)
+* client: semi-seamless migration support (RHBZ 725009, 738270)
+* Various bugfixes / cleanups
+* require spice-protocol >= 0.9.1
+
 Major changes in 0.9.1:
 =======================
 * !Development Release!
diff --git a/configure.ac b/configure.ac
index 203e82f..afdb926 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@ AC_PREREQ([2.57])
 
 m4_define([SPICE_MAJOR], 0)
 m4_define([SPICE_MINOR], 9)
-m4_define([SPICE_MICRO], 1)
+m4_define([SPICE_MICRO], 2)
 
 AC_INIT(spice, [SPICE_MAJOR.SPICE_MINOR.SPICE_MICRO], [], spice)
 
diff --git a/server/spice-server.syms b/server/spice-server.syms
index f1374bd..a8361a2 100644
--- a/server/spice-server.syms
+++ b/server/spice-server.syms
@@ -89,5 +89,6 @@ global:
     spice_server_record_set_mute;
     spice_server_record_set_volume;
     spice_server_get_num_clients;
+    spice_server_migrate_connect;
 } SPICE_SERVER_0.8.2;
 
commit c39d842ff16be4303c48b4faa19f22cd7312b3c7
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Tue Sep 20 14:59:36 2011 +0300

    client: support semi-seamless migration between spice servers with different protocols.
    
    It can't actually happen right now, since switch-host migration scheme will take
    place if the src/target server has protocol 1.
    (cherry picked from commit 4b2bf4d88c253502003aa5e4b93a045742eec9b4 branch 0.8)

diff --git a/client/red_channel.cpp b/client/red_channel.cpp
index d85265b..e8c058c 100644
--- a/client/red_channel.cpp
+++ b/client/red_channel.cpp
@@ -298,6 +298,20 @@ bool RedChannelBase::test_capability(uint32_t cap)
     return test_capability(_remote_caps, cap);
 }
 
+void RedChannelBase::swap(RedChannelBase* other)
+{
+    int tmp_ver;
+
+    RedPeer::swap(other);
+    tmp_ver = _remote_major;
+    _remote_major = other->_remote_major;
+    other->_remote_major = tmp_ver;
+
+    tmp_ver = _remote_minor;
+    _remote_minor = other->_remote_minor;
+    other->_remote_minor = tmp_ver;
+}
+
 SendTrigger::SendTrigger(RedChannel& channel)
     : _channel (channel)
 {
@@ -493,6 +507,12 @@ void RedChannel::do_migration_disconnect_src()
 void RedChannel::do_migration_connect_target()
 {
     LOG_INFO("");
+    ASSERT(get_client().get_protocol() != 0);
+    if (get_client().get_protocol() == 1) {
+        _marshallers = spice_message_marshallers_get1();
+    } else {
+        _marshallers = spice_message_marshallers_get();
+    }
     _loop.add_socket(*this);
     _socket_in_loop = true;
     on_connect_mig_target();
diff --git a/client/red_channel.h b/client/red_channel.h
index ba78acc..ee5b185 100644
--- a/client/red_channel.h
+++ b/client/red_channel.h
@@ -66,6 +66,8 @@ public:
      uint32_t get_peer_major() { return _remote_major;}
      uint32_t get_peer_minor() { return _remote_minor;}
 
+     virtual void swap(RedChannelBase* other);
+
 protected:
     void set_common_capability(uint32_t cap);
     void set_capability(uint32_t cap);
diff --git a/client/red_client.cpp b/client/red_client.cpp
index 4fc442f..2f6c6ce 100644
--- a/client/red_client.cpp
+++ b/client/red_client.cpp
@@ -136,6 +136,7 @@ Migrate::Migrate(RedClient& client)
     , _connected (false)
     , _thread (NULL)
     , _pending_con (0)
+    , _protocol (0)
 {
 }
 
@@ -192,7 +193,7 @@ void Migrate::swap_peer(RedChannelBase& other)
                 curr->set_valid(false);
                 if (!--_pending_con) {
                     lock.unlock();
-                    _client.set_target(_host.c_str(), _port, _sport);
+                    _client.set_target(_host.c_str(), _port, _sport, _protocol);
                     abort();
                 }
                 return;
@@ -215,6 +216,13 @@ void Migrate::connect_one(MigChannel& channel, const RedPeer::ConnectionOptions&
     channel.connect(options, connection_id, _host.c_str(), _password);
     ++_pending_con;
     channel.set_valid(true);
+    if (_protocol == 0) {
+        if (channel.get_peer_major() == 1) {
+            _protocol = 1;
+        } else {
+            _protocol = 2;
+        }
+    }
 }
 
 void Migrate::run()
@@ -235,7 +243,7 @@ void Migrate::run()
         for (++iter; iter != _channels.end(); ++iter) {
             conn_type = _client.get_connection_options((*iter)->get_type());
             con_opt = RedPeer::ConnectionOptions(conn_type, _port, _sport,
-						 _client.get_protocol(),
+                                                 _protocol,
                                                  _auth_options, _con_ciphers);
             connect_one(**iter, con_opt, connection_id);
         }
@@ -445,11 +453,16 @@ RedClient::~RedClient()
     delete[] _agent_caps;
 }
 
-void RedClient::set_target(const std::string& host, int port, int sport)
+void RedClient::set_target(const std::string& host, int port, int sport, int protocol)
 {
+    if (protocol != get_protocol()) {
+        LOG_INFO("old protocol %d, new protocol %d", get_protocol(), protocol);
+    }
+
     _port = port;
     _sport = sport;
     _host.assign(host);
+    set_protocol(protocol);
 }
 
 void RedClient::push_event(Event* event)
@@ -684,10 +697,8 @@ void RedClient::on_channel_disconnect_mig_src_completed(RedChannel& channel)
         _pixmap_cache.clear();
         _glz_window.clear();
         memset(_sync_info, 0, sizeof(_sync_info));
-
         LOG_INFO("calling main to connect and wait for handle_init to tell all the other channels to connect");
         RedChannel::connect_migration_target();
-
         AutoRef<MigrateEndEvent> mig_end_event(new MigrateEndEvent());
         get_process_loop().push_event(*mig_end_event);
     }
diff --git a/client/red_client.h b/client/red_client.h
index 20d5ffb..f4cd46b 100644
--- a/client/red_client.h
+++ b/client/red_client.h
@@ -82,6 +82,7 @@ private:
     Mutex _lock;
     Condition _cond;
     int _pending_con;
+    int _protocol;
 };
 
 class ChannelFactory {
@@ -238,7 +239,7 @@ public:
     void activate_interval_timer(Timer* timer, unsigned int millisec);
     void deactivate_interval_timer(Timer* timer);
 
-    void set_target(const std::string& host, int port, int sport);
+    void set_target(const std::string& host, int port, int sport, int protocol = 0);
     void set_password(const std::string& password) { _password = password;}
     void set_auto_display_res(bool auto_display_res) { _auto_display_res = auto_display_res;}
     void set_display_setting(DisplaySetting& setting) { _display_setting = setting;}
commit 3a1473760c0bb5da719e39ebe5cce02f5325904c
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Mon Sep 19 09:19:40 2011 +0300

    client: display channel - destroy all surfaces on disconnect
    
    Fix not destroying surfaces and other data (e.g., streams) upon disconnection.
    (cherry picked from commit 010b22cd771b7e81363b4b6521e4265b093fcd25 branch 0.8)

diff --git a/client/display_channel.cpp b/client/display_channel.cpp
index f7fdbbc..2e0613c 100644
--- a/client/display_channel.cpp
+++ b/client/display_channel.cpp
@@ -1045,9 +1045,7 @@ void DisplayChannel::on_disconnect()
         _surfaces_cache[0]->clear();
     }
 
-    if (screen()) {
-        screen()->set_update_interrupt_trigger(NULL);
-    }
+    clear();
 
     AutoRef<DetachChannelsEvent> detach_channels(new DetachChannelsEvent(*this));
     get_client().push_event(*detach_channels);
@@ -1056,7 +1054,6 @@ void DisplayChannel::on_disconnect()
         get_client().push_event(*unlock_event);
         detach_from_screen(get_client().get_application());
     }
-    get_client().deactivate_interval_timer(*_streams_timer);
     AutoRef<SyncEvent> sync_event(new SyncEvent());
     get_client().push_event(*sync_event);
     (*sync_event)->wait();
@@ -1112,7 +1109,7 @@ void DisplayChannel::destroy_off_screen_surfaces()
     }
 }
 
-void DisplayChannel::on_disconnect_mig_src()
+void DisplayChannel::clear(bool destroy_primary)
 {
     _palette_cache.clear();
     destroy_streams();
@@ -1122,7 +1119,16 @@ void DisplayChannel::on_disconnect_mig_src()
     _update_mark = 0;
     _next_timer_time = 0;
     get_client().deactivate_interval_timer(*_streams_timer);
-    destroy_off_screen_surfaces();
+    if (destroy_primary) {
+        destroy_all_surfaces();
+    } else {
+        destroy_off_screen_surfaces();
+    }
+}
+
+void DisplayChannel::on_disconnect_mig_src()
+{
+    clear(false);
     // Not clrearing the primary surface till we receive a new one (or a timeout).
     if (_surfaces_cache.exist(0)) {
         AutoRef<MigPrimarySurfaceTimer> mig_timer(new MigPrimarySurfaceTimer());
diff --git a/client/display_channel.h b/client/display_channel.h
index 30a76e2..61109ed 100644
--- a/client/display_channel.h
+++ b/client/display_channel.h
@@ -179,6 +179,7 @@ private:
     void activate_streams_timer();
     void stream_update_request(uint32_t update_time);
     void reset_screen();
+    void clear(bool destroy_primary = true);
 
     static void set_clip_rects(const SpiceClip& clip, uint32_t& num_clip_rects, SpiceRect*& clip_rects);
 
commit 00ff038cc90ef49f350a2fc033115d10b72995a7
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Sep 18 21:50:06 2011 +0300

    client: display channel migration
    (cherry picked from commit cad3c585444f940f60c12789f4174f2d32bec70f branch 0.8)
    
    Conflicts:
    
    	client/display_channel.cpp

diff --git a/client/display_channel.cpp b/client/display_channel.cpp
index b166c70..f7fdbbc 100644
--- a/client/display_channel.cpp
+++ b/client/display_channel.cpp
@@ -87,6 +87,28 @@ private:
     DisplayChannel& _channel;
 };
 
+class DestroyAllSurfacesEvent: public SyncEvent {
+public:
+    DestroyAllSurfacesEvent(DisplayChannel& channel, bool include_primary = true)
+        : _channel(channel)
+        , _include_primary(include_primary)
+    {
+    }
+
+    virtual void do_response(AbstractProcessLoop& events_loop)
+    {
+        if (_include_primary) {
+            _channel.do_destroy_all_surfaces();
+        } else {
+            _channel.do_destroy_off_screen_surfaces();
+        }
+    }
+
+private:
+    DisplayChannel& _channel;
+    bool _include_primary;
+};
+
 class CreateSurfaceEvent: public SyncEvent {
 public:
    CreateSurfaceEvent(DisplayChannel& channel, int surface_id, int width, int height,
@@ -547,6 +569,21 @@ void ResetTimer::response(AbstractProcessLoop& events_loop)
     _client.deactivate_interval_timer(this);
 }
 
+#define MIGRATION_PRIMARY_SURFACE_TIMEOUT (1000 * 5)
+
+class MigPrimarySurfaceTimer: public Timer {
+public:
+    virtual void response(AbstractProcessLoop& events_loop)
+    {
+        DisplayChannel *channel =  static_cast<DisplayChannel*>(events_loop.get_owner());
+        if (channel->_mig_wait_primary) {
+            channel->destroy_primary_surface();
+            channel->_mig_wait_primary = false;
+        }
+        channel->get_process_loop().deactivate_interval_timer(this);
+    }
+};
+
 class DisplayHandler: public MessageHandlerImp<DisplayChannel, SPICE_CHANNEL_DISPLAY> {
 public:
     DisplayHandler(DisplayChannel& channel)
@@ -574,6 +611,7 @@ DisplayChannel::DisplayChannel(RedClient& client, uint32_t id,
     , _gl_interrupt_recreate (*this)
 #endif
     , _interrupt_update (*this)
+    , _mig_wait_primary (false)
 {
     DisplayHandler* handler = static_cast<DisplayHandler*>(get_message_handler());
 
@@ -621,11 +659,11 @@ DisplayChannel::~DisplayChannel()
         screen()->set_update_interrupt_trigger(NULL);
     }
 
-    //destroy_canvas(); fixme destroy all
-    destroy_strams();
+    destroy_streams();
+    do_destroy_all_surfaces();
 }
 
-void DisplayChannel::destroy_strams()
+void DisplayChannel::destroy_streams()
 {
     Lock lock(_streams_lock);
     for (unsigned int i = 0; i < _streams.size(); i++) {
@@ -1024,6 +1062,75 @@ void DisplayChannel::on_disconnect()
     (*sync_event)->wait();
 }
 
+void DisplayChannel::do_destroy_all_surfaces()
+{
+   SurfacesCache::iterator s_iter;
+
+    for (s_iter = _surfaces_cache.begin(); s_iter != _surfaces_cache.end(); s_iter++) {
+       delete (*s_iter).second;
+    }
+    _surfaces_cache.clear();
+}
+
+void DisplayChannel::do_destroy_off_screen_surfaces()
+{
+    SurfacesCache::iterator s_iter;
+    Canvas *primary_canvas = NULL;
+
+    for (s_iter = _surfaces_cache.begin(); s_iter != _surfaces_cache.end(); s_iter++) {
+        if (s_iter->first == 0) {
+            primary_canvas = s_iter->second;
+        } else {
+            delete s_iter->second;
+        }
+    }
+    _surfaces_cache.clear();
+    if (primary_canvas) {
+        _surfaces_cache[0] = primary_canvas;
+    }
+}
+
+void DisplayChannel::destroy_all_surfaces()
+{
+    AutoRef<DestroyAllSurfacesEvent> destroy_event(new DestroyAllSurfacesEvent(*this));
+
+    get_client().push_event(*destroy_event);
+    (*destroy_event)->wait();
+    if (!(*destroy_event)->success()) {
+        THROW("destroy all surfaces failed");
+    }
+}
+
+void DisplayChannel::destroy_off_screen_surfaces()
+{
+    AutoRef<DestroyAllSurfacesEvent> destroy_event(new DestroyAllSurfacesEvent(*this, false));
+
+    get_client().push_event(*destroy_event);
+    (*destroy_event)->wait();
+    if (!(*destroy_event)->success()) {
+        THROW("destroy all surfaces failed");
+    }
+}
+
+void DisplayChannel::on_disconnect_mig_src()
+{
+    _palette_cache.clear();
+    destroy_streams();
+    if (screen()) {
+        screen()->set_update_interrupt_trigger(NULL);
+    }
+    _update_mark = 0;
+    _next_timer_time = 0;
+    get_client().deactivate_interval_timer(*_streams_timer);
+    destroy_off_screen_surfaces();
+    // Not clrearing the primary surface till we receive a new one (or a timeout).
+    if (_surfaces_cache.exist(0)) {
+        AutoRef<MigPrimarySurfaceTimer> mig_timer(new MigPrimarySurfaceTimer());
+        get_process_loop().activate_interval_timer(*mig_timer, MIGRATION_PRIMARY_SURFACE_TIMEOUT);
+        _mig_wait_primary = true;
+    }
+}
+
 bool DisplayChannel::create_sw_canvas(int surface_id, int width, int height, uint32_t format)
 {
     try {
@@ -1365,26 +1472,50 @@ void DisplayChannel::handle_stream_destroy(RedPeer::InMessage* message)
 
 void DisplayChannel::handle_stream_destroy_all(RedPeer::InMessage* message)
 {
-    destroy_strams();
+    destroy_streams();
 }
 
 void DisplayChannel::create_primary_surface(int width, int height, uint32_t format)
 {
+    bool do_create_primary = true;
 #ifdef USE_OPENGL
-   Canvas *canvas;
+    Canvas *canvas;
 #endif
-   _mark = false;
-    attach_to_screen(get_client().get_application(), get_id());
-    clear_area();
+    _mark = false;
+
+    /*
+     * trying to avoid artifacts when the display hasn't changed much
+     * between the disconnection from the migration src and the
+     * connection to the target.
+     */
+    if (_mig_wait_primary) {
+        ASSERT(_surfaces_cache.exist(0));
+        if (_x_res != width || _y_res != height || format != format) {
+            LOG_INFO("destroy the primary surface of the mig src session");
+            destroy_primary_surface();
+        } else {
+            LOG_INFO("keep the primary surface of the mig src session");
+            _surfaces_cache[0]->clear();
+            clear_area();
+            do_create_primary = false;
+        }
+    }
 
-    AutoRef<CreatePrimarySurfaceEvent> event(new CreatePrimarySurfaceEvent(*this, width, height,
+    if (do_create_primary) {
+        LOG_INFO("");
+        attach_to_screen(get_client().get_application(), get_id());
+        clear_area();
+        AutoRef<CreatePrimarySurfaceEvent> event(new CreatePrimarySurfaceEvent(*this, width, height,
                                                                            format));
-    get_client().push_event(*event);
-    (*event)->wait();
-    if (!(*event)->success()) {
-        THROW("Create primary surface failed");
+        get_client().push_event(*event);
+        (*event)->wait();
+        if (!(*event)->success()) {
+            THROW("Create primary surface failed");
+        }
     }
 
+    _mig_wait_primary = false;
+
     _x_res = width;
     _y_res = height;
     _format = format;
diff --git a/client/display_channel.h b/client/display_channel.h
index f30311d..30a76e2 100644
--- a/client/display_channel.h
+++ b/client/display_channel.h
@@ -114,6 +114,7 @@ public:
 protected:
     virtual void on_connect();
     virtual void on_disconnect();
+    virtual void on_disconnect_mig_src();
 
 private:
     void set_draw_handlers();
@@ -129,13 +130,17 @@ private:
     void destroy_canvas(int surface_id);
     void create_canvas(int surface_id, const std::vector<int>& canvas_type, int width, int height,
                        uint32_t format);
-    void destroy_strams();
+    void destroy_streams();
     void update_cursor();
 
     void create_primary_surface(int width, int height, uint32_t format);
     void create_surface(int surface_id, int width, int height, uint32_t format);
     void destroy_primary_surface();
     void destroy_surface(int surface_id);
+    void destroy_all_surfaces();
+    void do_destroy_all_surfaces();
+    void destroy_off_screen_surfaces();
+    void do_destroy_off_screen_surfaces();
 
     void handle_mode(RedPeer::InMessage* message);
     void handle_mark(RedPeer::InMessage* message);
@@ -217,11 +222,13 @@ private:
 #endif
     InterruptUpdate _interrupt_update;
 
+    bool _mig_wait_primary;
     friend class SetModeEvent;
     friend class CreatePrimarySurfaceEvent;
     friend class DestroyPrimarySurfaceEvent;
     friend class CreateSurfaceEvent;
     friend class DestroySurfaceEvent;
+    friend class DestroyAllSurfacesEvent;
     friend class ActivateTimerEvent;
     friend class VideoStream;
     friend class StreamsTrigger;
@@ -229,6 +236,7 @@ private:
     friend class StreamsTimer;
     friend class AttachChannelsEvent;
     friend class DetachChannelsEvent;
+    friend class MigPrimarySurfaceTimer;
 };
 
 #endif
commit 87664af999a534090c37ffb53aec2a6cd970cdcc
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Sep 18 21:42:18 2011 +0300

    client: playback/record channels: implement on_disconnect
    (cherry picked from commit d3ed9d5e9d52ddcadcb3c8c77dd827b50071d813 branch 0.8)

diff --git a/client/audio_channels.h b/client/audio_channels.h
index 695573a..2722e20 100644
--- a/client/audio_channels.h
+++ b/client/audio_channels.h
@@ -37,6 +37,9 @@ public:
 
     static ChannelFactory& Factory();
 
+protected:
+    virtual void on_disconnect();
+
 private:
     void handle_mode(RedPeer::InMessage* message);
     void handle_start(RedPeer::InMessage* message);
@@ -48,6 +51,8 @@ private:
 
     void set_data_handler();
 
+    void clear();
+
 private:
     WavePlaybackAbstract* _wave_player;
     uint32_t _mode;
@@ -67,12 +72,14 @@ public:
 
     static ChannelFactory& Factory();
 
+protected:
+    virtual void on_connect();
+    virtual void on_disconnect();
+
 private:
     void handle_start(RedPeer::InMessage* message);
     void handle_stop(RedPeer::InMessage* message);
 
-    virtual void on_connect();
-
     virtual void add_event_source(EventSources::File& event_source);
     virtual void remove_event_source(EventSources::File& event_source);
     virtual void add_event_source(EventSources::Trigger& event_source);
@@ -82,6 +89,7 @@ private:
     void send_start_mark();
     void release_message(RecordSamplesMessage *message);
     RecordSamplesMessage * get_message();
+    void clear();
 
 private:
     WaveRecordAbstract* _wave_recorder;
diff --git a/client/playback_channel.cpp b/client/playback_channel.cpp
index 731b520..d3445e5 100644
--- a/client/playback_channel.cpp
+++ b/client/playback_channel.cpp
@@ -172,19 +172,37 @@ PlaybackChannel::PlaybackChannel(RedClient& client, uint32_t id)
     set_capability(SPICE_PLAYBACK_CAP_CELT_0_5_1);
 }
 
-PlaybackChannel::~PlaybackChannel(void)
+void PlaybackChannel::clear()
 {
-    delete _wave_player;
+    if (_wave_player) {
+        _playing = false;
+        _wave_player->stop();
+        delete _wave_player;
+        _wave_player = NULL;
+    }
+    _mode = SPICE_AUDIO_DATA_MODE_INVALID;
 
     if (_celt_decoder) {
         celt051_decoder_destroy(_celt_decoder);
+        _celt_decoder = NULL;
     }
 
     if (_celt_mode) {
         celt051_mode_destroy(_celt_mode);
+        _celt_mode = NULL;
     }
 }
 
+void PlaybackChannel::on_disconnect()
+{
+    clear();
+}
+
+PlaybackChannel::~PlaybackChannel(void)
+{
+    clear();
+}
+
 bool PlaybackChannel::abort(void)
 {
     return (!_wave_player || _wave_player->abort()) && RedChannel::abort();
diff --git a/client/record_channel.cpp b/client/record_channel.cpp
index 738f98f..0476031 100644
--- a/client/record_channel.cpp
+++ b/client/record_channel.cpp
@@ -101,14 +101,7 @@ RecordChannel::~RecordChannel(void)
         _messages.pop_front();
         delete mes;
     }
-    delete _wave_recorder;
-
-    if (_celt_encoder) {
-        celt051_encoder_destroy(_celt_encoder);
-    }
-    if (_celt_mode) {
-        celt051_mode_destroy(_celt_mode);
-    }
+    clear();
 }
 
 bool RecordChannel::abort(void)
@@ -128,6 +121,11 @@ void RecordChannel::on_connect()
     post_message(message);
 }
 
+void RecordChannel::on_disconnect()
+{
+    clear();
+}
+
 void RecordChannel::send_start_mark()
 {
     Message* message = new Message(SPICE_MSGC_RECORD_START_MARK);
@@ -177,6 +175,23 @@ void RecordChannel::handle_start(RedPeer::InMessage* message)
     _wave_recorder->start();
 }
 
+void RecordChannel::clear()
+{
+    if (_wave_recorder) {
+        _wave_recorder->stop();
+        delete _wave_recorder;
+        _wave_recorder = NULL;
+    }
+    if (_celt_encoder) {
+        celt051_encoder_destroy(_celt_encoder);
+        _celt_encoder = NULL;
+    }
+    if (_celt_mode) {
+        celt051_mode_destroy(_celt_mode);
+        _celt_mode = NULL;
+    }
+}
+
 void RecordChannel::handle_stop(RedPeer::InMessage* message)
 {
     RecordHandler* handler = static_cast<RecordHandler*>(get_message_handler());
@@ -186,13 +201,7 @@ void RecordChannel::handle_stop(RedPeer::InMessage* message)
         return;
     }
     ASSERT(_celt_mode && _celt_encoder);
-    _wave_recorder->stop();
-    celt051_encoder_destroy(_celt_encoder);
-    _celt_encoder = NULL;
-    celt051_mode_destroy(_celt_mode);
-    _celt_mode = NULL;
-    delete _wave_recorder;
-    _wave_recorder = NULL;
+    clear();
 }
 
 RecordSamplesMessage* RecordChannel::get_message()
commit 0a5e9cbbcf9a3ebc6c7e3ccd3e7026998d2488d9
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Sep 18 22:50:50 2011 +0300

    client: main channel migration: do partial cleanup when switching hosts
    
    Implement on_disconnect_mig_src and on_connect_mig_target in order to avoid
    unnecessary cleanups done in on_(disconnet|connect).
    In addition, do not request guest display settings changes after migration.
    (cherry picked from commit f91d202eb3bf631cf5e70277d1aabffec7da9393 branch 0.8)

diff --git a/client/red_client.cpp b/client/red_client.cpp
index 84440c1..4fc442f 100644
--- a/client/red_client.cpp
+++ b/client/red_client.cpp
@@ -498,6 +498,15 @@ void RedClient::on_disconnect()
     (*sync_event)->wait();
 }
 
+void RedClient::on_disconnect_mig_src()
+{
+    _application.deactivate_interval_timer(*_agent_timer);
+    delete[] _agent_msg_data;
+    _agent_msg_data = NULL;
+    _agent_msg_pos = 0;
+    _agent_tokens = 0;
+}
+
 void RedClient::delete_channels()
 {
     Lock lock(_channels_lock);
diff --git a/client/red_client.h b/client/red_client.h
index 7b3e507..20d5ffb 100644
--- a/client/red_client.h
+++ b/client/red_client.h
@@ -279,6 +279,8 @@ protected:
     virtual void on_connecting();
     virtual void on_connect();
     virtual void on_disconnect();
+    virtual void on_connect_mig_target() {}
+    virtual void on_disconnect_mig_src();
 
 private:
     void on_channel_disconnected(RedChannel& channel);
commit c73d5c10e677b8ab67101545bb17eb46afd1e251
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Sep 18 21:33:02 2011 +0300

    client: handle SPICE_MSG_MAIN_MIGRATE_END
    
    (1) disconnect all channels from the migration src
    (2) after all channels are disconnected, clean global resources
    (3) send SPICE_MSGC_MAIN_MIGRATE_END to migration target
    (4) wait for SPICE_MSG_MAIN_INIT
    (4) switch all channels to migration target
    (cherry picked from commit 510a4ff7c4f188fe6d0fb12198b8f9fdb74b9a2d branch 0.8)
    
    Conflicts:
    
    	client/red_channel.h

diff --git a/client/red_channel.cpp b/client/red_channel.cpp
index 8632600..d85265b 100644
--- a/client/red_channel.cpp
+++ b/client/red_channel.cpp
@@ -30,6 +30,15 @@
 #include "openssl/evp.h"
 #include "openssl/x509.h"
 
+void MigrationDisconnectSrcEvent::response(AbstractProcessLoop& events_loop)
+{
+    static_cast<RedChannel*>(events_loop.get_owner())->do_migration_disconnect_src();
+}
+
+void MigrationConnectTargetEvent::response(AbstractProcessLoop& events_loop)
+{
+    static_cast<RedChannel*>(events_loop.get_owner())->do_migration_connect_target();
+}
 
 RedChannelBase::RedChannelBase(uint8_t type, uint8_t id, const ChannelCaps& common_caps,
                                const ChannelCaps& caps)
@@ -440,6 +449,57 @@ void RedChannel::disconnect()
     _action_cond.notify_one();
 }
 
+void RedChannel::disconnect_migration_src()
+{
+    clear_outgoing_messages();
+
+    Lock lock(_action_lock);
+    if (_state == CONNECTING_STATE || _state == CONNECTED_STATE) {
+        AutoRef<MigrationDisconnectSrcEvent> migrate_event(new MigrationDisconnectSrcEvent());
+        _loop.push_event(*migrate_event);
+    }
+}
+
+void RedChannel::connect_migration_target()
+{
+    LOG_INFO("");
+    AutoRef<MigrationConnectTargetEvent> migrate_event(new MigrationConnectTargetEvent());
+    _loop.push_event(*migrate_event);
+}
+
+void RedChannel::do_migration_disconnect_src()
+{
+    if (_socket_in_loop) {
+        _socket_in_loop = false;
+        _loop.remove_socket(*this);
+    }
+
+    clear_outgoing_messages();
+    if (_outgoing_message) {
+        _outgoing_message->release();
+        _outgoing_message = NULL;
+    }
+    _incomming_header_pos = 0;
+    if (_incomming_message) {
+        _incomming_message->unref();
+        _incomming_message = NULL;
+    }
+
+    on_disconnect_mig_src();
+    get_client().migrate_channel(*this);
+    get_client().on_channel_disconnect_mig_src_completed(*this);
+}
+
+void RedChannel::do_migration_connect_target()
+{
+    LOG_INFO("");
+    _loop.add_socket(*this);
+    _socket_in_loop = true;
+    on_connect_mig_target();
+    set_state(CONNECTED_STATE);
+    on_event();
+}
+
 void RedChannel::clear_outgoing_messages()
 {
     Lock lock(_outgoing_lock);
diff --git a/client/red_channel.h b/client/red_channel.h
index 6a5a9f6..ba78acc 100644
--- a/client/red_channel.h
+++ b/client/red_channel.h
@@ -106,6 +106,16 @@ public:
     virtual void on_event();
 };
 
+class MigrationDisconnectSrcEvent: public Event {
+public:
+    virtual void response(AbstractProcessLoop& events_loop);
+};
+
+class MigrationConnectTargetEvent: public Event {
+public:
+    virtual void response(AbstractProcessLoop& events_loop);
+};
+
 struct SyncInfo {
     Mutex* lock;
     Condition* condition;
@@ -126,6 +136,9 @@ public:
     virtual void disconnect();
     virtual bool abort();
 
+    virtual void disconnect_migration_src();
+    virtual void connect_migration_target();
+
     virtual CompoundInMessage *receive();
 
     virtual void post_message(RedChannel::OutMessage* message);
@@ -140,6 +153,8 @@ protected:
     virtual void on_connect() {}
     virtual void on_disconnect() {}
     virtual void on_migrate() {}
+    virtual void on_disconnect_mig_src() { on_disconnect();}
+    virtual void on_connect_mig_target() { on_connect();}
     void handle_migrate(RedPeer::InMessage* message);
     void handle_set_ack(RedPeer::InMessage* message);
     void handle_ping(RedPeer::InMessage* message);
@@ -159,6 +174,8 @@ private:
     virtual void on_event();
     void on_message_received();
     void on_message_complition(uint64_t serial);
+    void do_migration_disconnect_src();
+    void do_migration_connect_target();
 
     static void* worker_main(void *);
 
@@ -203,6 +220,8 @@ private:
     uint64_t _disconnect_reason;
 
     friend class SendTrigger;
+    friend class MigrationDisconnectSrcEvent;
+    friend class MigrationConnectTargetEvent;
 };
 
 
diff --git a/client/red_client.cpp b/client/red_client.cpp
index afde7d2..84440c1 100644
--- a/client/red_client.cpp
+++ b/client/red_client.cpp
@@ -26,6 +26,7 @@
 #include "utils.h"
 #include "debug.h"
 #include "marshallers.h"
+#include <algorithm>
 
 #ifndef INFINITY
 #define INFINITY HUGE
@@ -123,6 +124,11 @@ void ClipboardReleaseEvent::response(AbstractProcessLoop& events_loop)
         VD_AGENT_CLIPBOARD_RELEASE, 0, NULL);
 }
 
+void MigrateEndEvent::response(AbstractProcessLoop& events_loop)
+{
+    static_cast<RedClient*>(events_loop.get_owner())->send_migrate_end();
+}
+
 Migrate::Migrate(RedClient& client)
     : _client (client)
     , _running (false)
@@ -395,6 +401,7 @@ RedClient::RedClient(Application& application)
     , _agent_caps(NULL)
     , _migrate (*this)
     , _glz_window (_glz_debug)
+    , _during_migration (false)
 {
     Platform::set_clipboard_listener(this);
     MainChannelLoop* message_loop = static_cast<MainChannelLoop*>(get_message_handler());
@@ -413,6 +420,7 @@ RedClient::RedClient(Application& application)
 
     message_loop->set_handler(SPICE_MSG_MAIN_MIGRATE_BEGIN, &RedClient::handle_migrate_begin);
     message_loop->set_handler(SPICE_MSG_MAIN_MIGRATE_CANCEL, &RedClient::handle_migrate_cancel);
+    message_loop->set_handler(SPICE_MSG_MAIN_MIGRATE_END, &RedClient::handle_migrate_end);
     message_loop->set_handler(SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST,
                               &RedClient::handle_migrate_switch_host);
     message_loop->set_handler(SPICE_MSG_MAIN_INIT, &RedClient::handle_init);
@@ -424,6 +432,8 @@ RedClient::RedClient(Application& application)
     message_loop->set_handler(SPICE_MSG_MAIN_AGENT_DISCONNECTED, &RedClient::handle_agent_disconnected);
     message_loop->set_handler(SPICE_MSG_MAIN_AGENT_DATA, &RedClient::handle_agent_data);
     message_loop->set_handler(SPICE_MSG_MAIN_AGENT_TOKEN, &RedClient::handle_agent_tokens);
+
+    set_capability(SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
     start();
 }
 
@@ -491,6 +501,7 @@ void RedClient::on_disconnect()
 void RedClient::delete_channels()
 {
     Lock lock(_channels_lock);
+    _pending_mig_disconnect_channels.clear();
     while (!_channels.empty()) {
         RedChannel *channel = *_channels.begin();
         _channels.pop_front();
@@ -618,7 +629,7 @@ bool RedClient::abort()
 
 void RedClient::handle_migrate_begin(RedPeer::InMessage* message)
 {
-    DBG(0, "");
+    LOG_INFO("");
     SpiceMsgMainMigrationBegin* migrate = (SpiceMsgMainMigrationBegin*)message->data();
     //add mig channels
     _migrate.start(migrate);
@@ -626,9 +637,59 @@ void RedClient::handle_migrate_begin(RedPeer::InMessage* message)
 
 void RedClient::handle_migrate_cancel(RedPeer::InMessage* message)
 {
+    LOG_INFO("");
     _migrate.abort();
 }
 
+void RedClient::handle_migrate_end(RedPeer::InMessage* message)
+{
+    LOG_INFO("");
+
+    Lock lock(_channels_lock);
+    ASSERT(_pending_mig_disconnect_channels.empty());
+    Channels::iterator iter = _channels.begin();
+    for (; iter != _channels.end(); ++iter) {
+        (*iter)->disconnect_migration_src();
+        _pending_mig_disconnect_channels.push_back(*iter);
+    }
+    RedChannel::disconnect_migration_src();
+     _pending_mig_disconnect_channels.push_back(this);
+     _during_migration = true;
+}
+
+void RedClient::on_channel_disconnect_mig_src_completed(RedChannel& channel)
+{
+    Lock lock(_channels_lock);
+    Channels::iterator pending_iter = std::find(_pending_mig_disconnect_channels.begin(),
+                                                _pending_mig_disconnect_channels.end(),
+                                                &channel);
+
+    LOG_INFO("");
+    if (pending_iter == _pending_mig_disconnect_channels.end()) {
+        THROW("unexpected channel");
+    }
+
+    _pending_mig_disconnect_channels.erase(pending_iter);
+    /* clean shared data when all channels have disconnected */
+    if (_pending_mig_disconnect_channels.empty()) {
+        _pixmap_cache.clear();
+        _glz_window.clear();
+        memset(_sync_info, 0, sizeof(_sync_info));
+
+        LOG_INFO("calling main to connect and wait for handle_init to tell all the other channels to connect");
+        RedChannel::connect_migration_target();
+
+        AutoRef<MigrateEndEvent> mig_end_event(new MigrateEndEvent());
+        get_process_loop().push_event(*mig_end_event);
+    }
+}
+
+void RedClient::send_migrate_end()
+{
+    Message* message = new Message(SPICE_MSGC_MAIN_MIGRATE_END);
+    post_message(message);
+}
+
 ChannelFactory* RedClient::find_factory(uint32_t type)
 {
     Factorys::iterator iter = _factorys.begin();
@@ -973,12 +1034,37 @@ void RedClient::set_mouse_mode(uint32_t supported_modes, uint32_t current_mode)
     }
 }
 
+/* returns true if we should wait for a response from the agent */
+bool RedClient::init_guest_display()
+{
+    if (_agent_connected) {
+        if (_auto_display_res) {
+            send_agent_monitors_config();
+        }
+
+        if (_auto_display_res || !_display_setting.is_empty()) {
+            _application.activate_interval_timer(*_agent_timer, AGENT_TIMEOUT);
+        } else {
+            return false;
+        }
+    } else {
+        if (_auto_display_res || !_display_setting.is_empty()) {
+            LOG_WARN("no agent running, display options have been ignored");
+        }
+        return false;
+    }
+    return true;
+}
+
 void RedClient::handle_init(RedPeer::InMessage* message)
 {
     SpiceMsgMainInit *init = (SpiceMsgMainInit *)message->data();
+    LOG_INFO("");
     _connection_id = init->session_id;
     set_mm_time(init->multi_media_time);
-    calc_pixmap_cach_and_glz_window_size(init->display_channels_hint, init->ram_hint);
+    if (!_during_migration) {
+        calc_pixmap_cach_and_glz_window_size(init->display_channels_hint, init->ram_hint);
+    }
     set_mouse_mode(init->supported_mouse_modes, init->current_mouse_mode);
     _agent_tokens = init->agent_tokens;
     _agent_connected = !!init->agent_connected;
@@ -989,20 +1075,19 @@ void RedClient::handle_init(RedPeer::InMessage* message)
         _marshallers->msgc_main_agent_start(msg->marshaller(), &agent_start);
         post_message(msg);
         send_agent_announce_capabilities(true);
-        if (_auto_display_res) {
-            send_agent_monitors_config();
-        }
+    }
 
-        if (_auto_display_res || !_display_setting.is_empty()) {
-            _application.activate_interval_timer(*_agent_timer, AGENT_TIMEOUT);
-        } else {
+    if (!_during_migration) {
+        if (!init_guest_display()) {
             send_main_attach_channels();
         }
     } else {
-        if (_auto_display_res || !_display_setting.is_empty()) {
-            LOG_WARN("no agent running, display options have been ignored");
+        LOG_INFO("connecting all channels after migration");
+        Channels::iterator iter = _channels.begin();
+        for (; iter != _channels.end(); ++iter) {
+            (*iter)->connect_migration_target();
         }
-        send_main_attach_channels();
+        _during_migration = false;
     }
 }
 
diff --git a/client/red_client.h b/client/red_client.h
index 17391e4..7b3e507 100644
--- a/client/red_client.h
+++ b/client/red_client.h
@@ -207,6 +207,10 @@ public:
     virtual void response(AbstractProcessLoop& events_loop);
 };
 
+class MigrateEndEvent: public Event {
+public:
+    virtual void response(AbstractProcessLoop& events_loop);
+};
 
 class RedClient: public RedChannel,
                  public Platform::ClipboardListener {
@@ -217,6 +221,7 @@ public:
     friend class ClipboardRequestEvent;
     friend class ClipboardNotifyEvent;
     friend class ClipboardReleaseEvent;
+    friend class MigrateEndEvent;
 
     RedClient(Application& application);
     ~RedClient();
@@ -277,6 +282,8 @@ protected:
 
 private:
     void on_channel_disconnected(RedChannel& channel);
+    void on_channel_disconnect_mig_src_completed(RedChannel& channel);
+    void send_migrate_end();
     void migrate_channel(RedChannel& channel);
     void send_agent_announce_capabilities(bool request);
     void send_agent_monitors_config();
@@ -287,6 +294,7 @@ private:
 
     void handle_migrate_begin(RedPeer::InMessage* message);
     void handle_migrate_cancel(RedPeer::InMessage* message);
+    void handle_migrate_end(RedPeer::InMessage* message);
     void handle_init(RedPeer::InMessage* message);
     void handle_channels(RedPeer::InMessage* message);
     void handle_mouse_mode(RedPeer::InMessage* message);
@@ -298,6 +306,7 @@ private:
     void handle_migrate_switch_host(RedPeer::InMessage* message);
     void dispatch_agent_message(VDAgentMessage* msg, void* data);
 
+    bool init_guest_display();
     void on_agent_reply(VDAgentReply* reply);
     void on_agent_announce_capabilities(VDAgentAnnounceCapabilities* caps,
                                         uint32_t msg_size);
@@ -354,6 +363,7 @@ private:
     Factorys _factorys;
     typedef std::list<RedChannel*> Channels;
     Channels _channels;
+    Channels _pending_mig_disconnect_channels;
     PixmapCache _pixmap_cache;
     uint64_t _pixmap_cache_size;
     Mutex _sync_lock;
@@ -367,6 +377,8 @@ private:
     Mutex _mm_clock_lock;
     uint64_t _mm_clock_last_update;
     uint32_t _mm_time;
+
+    bool _during_migration;
 };
 
 #endif
commit 6cd3ffba6fbb53102bbaf69bcdba29e5a1db458d
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Sep 18 21:21:00 2011 +0300

    client: handle SpiceMsgMainMigrationBegin (semi-seamless migration)
    
    RHBZ 725009, 738270
    (cherry picked from commit 31ed2519a752b7332ed40d0d7ab02e938c0e65cb branch 0.8)
    
    Conflicts:
    
    	client/red_client.cpp

diff --git a/client/red_client.cpp b/client/red_client.cpp
index efd9feb..afde7d2 100644
--- a/client/red_client.cpp
+++ b/client/red_client.cpp
@@ -260,9 +260,15 @@ void* Migrate::worker_main(void *data)
 
 void Migrate::start(const SpiceMsgMainMigrationBegin* migrate)
 {
+    std::string cert_subject;
+    uint32_t peer_major;
+    uint32_t peer_minor;
+
     DBG(0, "");
     abort();
-    if ((_client.get_peer_major() == 1) && (_client.get_peer_minor() < 1)) {
+    peer_major = _client.get_peer_major();
+    peer_minor = _client.get_peer_minor();
+    if ((peer_major == 1) && (peer_minor < 1)) {
         LOG_INFO("server minor version incompatible for destination authentication"
                  "(missing dest pubkey in SpiceMsgMainMigrationBegin)");
         OldRedMigrationBegin* old_migrate = (OldRedMigrationBegin*)migrate;
@@ -274,8 +280,19 @@ void Migrate::start(const SpiceMsgMainMigrationBegin* migrate)
         _host.assign((char *)migrate->host_data);
         _port = migrate->port ? migrate->port : -1;
         _sport = migrate->sport ? migrate->sport : -1;
-        _auth_options.type_flags = SPICE_SSL_VERIFY_OP_PUBKEY;
-        _auth_options.host_pubkey.assign(migrate->pub_key_data, migrate->pub_key_data + migrate->pub_key_size);
+        if ((peer_major == 1) || (peer_major == 2 && peer_minor < 1)) {
+            _auth_options.type_flags = SPICE_SSL_VERIFY_OP_PUBKEY;
+            _auth_options.host_pubkey.assign(migrate->pub_key_data, migrate->pub_key_data +
+                                             migrate->pub_key_size);
+        } else {
+            _auth_options.type_flags = SPICE_SSL_VERIFY_OP_SUBJECT;
+            _auth_options.CA_file =  _client.get_host_auth_options().CA_file;
+            if (migrate->cert_subject_size != 0) {
+                _auth_options.host_subject.assign(migrate->cert_subject_data,
+                                                  migrate->cert_subject_data +
+                                                  migrate->cert_subject_size);
+            }
+        }
     }
 
     _con_ciphers = _client.get_connection_ciphers();
commit f22caf9aee2aa64313468b04efa615e2e1c7f8b3
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Sep 18 14:52:04 2011 +0300

    client: rewrite surfaces cache
    
    use std::map instead of a specific template (CHash).
    There is no need for special template. Moreover, using
    std::map will allow easy iteration over the surfaces.
    (cherry picked from commit fcb3b4ce5231218bcf949da4270bd85a2cfb3535 branch 0.8)
    
    Conflicts:
    
    	client/display_channel.cpp

diff --git a/client/canvas.cpp b/client/canvas.cpp
index 3b5f4b6..139b663 100644
--- a/client/canvas.cpp
+++ b/client/canvas.cpp
@@ -24,14 +24,35 @@
 #include "utils.h"
 #include "debug.h"
 
+static SpiceCanvas* surfaces_cache_op_get(SpiceImageSurfaces *surfaces, uint32_t surface_id)
+{
+    SurfacesCache* surfaces_cache = static_cast<SurfacesCache*>(surfaces);
+    if (!surfaces_cache->exist(surface_id)) {
+        return NULL;
+    }
+    return (*surfaces_cache)[surface_id]->get_internal_canvas();
+}
+
+SurfacesCache::SurfacesCache()
+{
+    static SpiceImageSurfacesOps surfaces_ops = {
+        surfaces_cache_op_get,
+    };
+    ops = &surfaces_ops;
+}
+
+bool SurfacesCache::exist(uint32_t surface_id)
+{
+    return (this->count(surface_id) != 0);
+}
 
 Canvas::Canvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-               GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces)
+               GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces)
     : _canvas (NULL)
     , _pixmap_cache (pixmap_cache)
     , _palette_cache (palette_cache)
     , _glz_decoder(glz_decoder_window, _glz_handler, _glz_debug)
-    , _csurfaces(csurfaces)
+    , _surfaces_cache(csurfaces)
 {
 }
 
diff --git a/client/canvas.h b/client/canvas.h
index 0b8f2b6..a9cdecd 100644
--- a/client/canvas.h
+++ b/client/canvas.h
@@ -30,6 +30,7 @@
 #include "glz_decoder.h"
 #include "jpeg_decoder.h"
 #include "zlib_decoder.h"
+#include <map>
 
 enum CanvasType {
     CANVAS_TYPE_INVALID,
@@ -38,102 +39,6 @@ enum CanvasType {
     CANVAS_TYPE_GDI,
 };
 
-template <class T, int HASH_SIZE, class Base = EmptyBase>
-class CHash : public Base {
-public:
-    CHash()
-    {
-        memset(_hash, 0, sizeof(_hash));
-    }
-
-    ~CHash()
-    {
-    }
-
-    void add(uint32_t id, T* data)
-    {
-        Item** item = &_hash[key(id)];
-
-        while (*item) {
-            PANIC_ON((*item)->id == id);
-            item = &(*item)->next;
-        }
-        *item = new Item(id, data);
-    }
-
-    bool is_present(uint32_t id)
-    {
-        Item* item = _hash[key(id)];
-
-        for (;;) {
-            if (!item) {
-                return false;
-            }
-
-            if (item->id != id) {
-                item = item->next;
-                continue;
-            }
-
-            return true;
-        }
-    }
-
-    T* get(uint32_t id)
-    {
-        Item* item = _hash[key(id)];
-
-        for (;;) {
-            PANIC_ON(!item);
-
-            if (item->id != id) {
-                item = item->next;
-                continue;
-            }
-
-            return item->data;
-        }
-    }
-
-    void remove(uint32_t id)
-    {
-        Item** item = &_hash[key(id)];
-
-        while (*item) {
-            if ((*item)->id == id) {
-                Item *rm_item = *item;
-                *item = rm_item->next;
-                delete rm_item;
-                return;
-            }
-            item = &(*item)->next;
-        }
-        THROW("id %lu, not found", id);
-    }
-
-private:
-    inline uint32_t key(uint32_t id) {return id % HASH_SIZE;}
-
-private:
-    class Item {
-    public:
-        Item(uint32_t in_id, T* data)
-            : id (in_id)
-            , next (NULL)
-            , data (data) {}
-
-        ~Item()
-        {
-        }
-
-        uint64_t id;
-        Item* next;
-        T* data;
-    };
-
-    Item* _hash[HASH_SIZE];
-};
-
 class PixmapCacheTreat {
 public:
     static inline pixman_image_t *get(pixman_image_t *surf)
@@ -200,44 +105,6 @@ public:
     }
 };
 
-class SpiceImageSurfacesBase;
-
-typedef CHash<SpiceCanvas, 1024, SpiceImageSurfacesBase> CSurfaces;
-
-class SpiceImageSurfacesBase {
-public:
-    SpiceImageSurfaces base;
-
-    static void op_put(SpiceImageSurfaces *c, uint32_t surface_id, SpiceCanvas *surface)
-    {
-        CSurfaces* cache = reinterpret_cast<CSurfaces*>(c);
-        cache->add(surface_id, surface);
-    }
-
-    static SpiceCanvas* op_get(SpiceImageSurfaces *s, uint32_t surface_id)
-    {
-        CSurfaces* cache = reinterpret_cast<CSurfaces*>(s);
-        return cache->get(surface_id);
-    }
-
-    static void op_del(SpiceImageSurfaces *c, uint32_t surface_id)
-    {
-        CSurfaces* cache = reinterpret_cast<CSurfaces*>(c);
-        cache->remove(surface_id);
-    }
-
-    SpiceImageSurfacesBase()
-    {
-        static SpiceImageSurfacesOps cache_ops = {
-            op_get
-        };
-        base.ops = &cache_ops;
-    }
-};
-
-class Canvas;
-
-typedef CHash<Canvas, 1024, SpiceImageSurfacesBase> CCanvases;
 
 class CachedPalette {
 public:
@@ -399,10 +266,20 @@ public:
     }
 };
 
+class Canvas;
+
+typedef std::map<uint32_t, Canvas*> SurfacesCanvasesMap;
+
+class SurfacesCache: public SpiceImageSurfaces, public SurfacesCanvasesMap {
+public:
+    SurfacesCache();
+    bool exist(uint32_t surface_id);
+};
+
 class Canvas {
 public:
     Canvas(PixmapCache& bits_cache, PaletteCache& palette_cache,
-           GlzDecoderWindow &glz_decoder_window, CSurfaces& csurfaces);
+           GlzDecoderWindow &glz_decoder_window, SurfacesCache& csurfaces);
     virtual ~Canvas();
 
     virtual void copy_pixels(const QRegion& region, RedDrawable* dc,
@@ -442,7 +319,7 @@ protected:
 
     PixmapCache& pixmap_cache() { return _pixmap_cache;}
     PaletteCache& palette_cache() { return _palette_cache;}
-    CSurfaces& csurfaces() { return _csurfaces; }
+    SurfacesCache& surfaces_cache() { return _surfaces_cache;}
 
     GlzDecoder& glz_decoder() {return _glz_decoder;}
     JpegDecoder& jpeg_decoder() { return _jpeg_decoder;}
@@ -453,7 +330,6 @@ private:
 
 protected:
     SpiceCanvas* _canvas;
-    CSurfaces _surfaces;
 
 private:
     PixmapCache& _pixmap_cache;
@@ -466,7 +342,7 @@ private:
     JpegDecoder _jpeg_decoder;
     ZlibDecoder _zlib_decoder;
 
-    CSurfaces& _csurfaces;
+    SurfacesCache& _surfaces_cache;
 
     unsigned long _base;
     unsigned long _max;
diff --git a/client/display_channel.cpp b/client/display_channel.cpp
index 5ab7493..b166c70 100644
--- a/client/display_channel.cpp
+++ b/client/display_channel.cpp
@@ -547,42 +547,6 @@ void ResetTimer::response(AbstractProcessLoop& events_loop)
     _client.deactivate_interval_timer(this);
 }
 
-void DisplaySurfacesManger::add_surface(int surface_id, SpiceCanvas *surface)
-{
-     surfaces.add(surface_id, surface);
-}
-
-void DisplaySurfacesManger::del_surface(int surface_id)
-{
-    surfaces.remove(surface_id);
-}
-
-void DisplaySurfacesManger::add_canvas(int surface_id, Canvas *canvas)
-{
-    canvases.add(surface_id, canvas);
-}
-
-void DisplaySurfacesManger::del_canvas(int surface_id)
-{
-    canvases.remove(surface_id);
-}
-
-CSurfaces& DisplaySurfacesManger::get_surfaces()
-{
-    return surfaces;
-}
-
-bool DisplaySurfacesManger::is_present_canvas(int surface_id)
-{
-
-    return canvases.is_present(surface_id);
-}
-
-Canvas* DisplaySurfacesManger::get_canvas(int surface_id)
-{
-    return canvases.get(surface_id);
-}
-
 class DisplayHandler: public MessageHandlerImp<DisplayChannel, SPICE_CHANNEL_DISPLAY> {
 public:
     DisplayHandler(DisplayChannel& channel)
@@ -719,11 +683,11 @@ void DisplayChannel::copy_pixels(const QRegion& dest_region,
 {
     Canvas *canvas;
 
-    if (!surfaces_mngr.is_present_canvas(0)) {
+    if (!_surfaces_cache.exist(0)) {
         return;
     }
 
-    canvas = surfaces_mngr.get_canvas(0);
+    canvas = _surfaces_cache[0];
     canvas->copy_pixels(dest_region, NULL, &dest_pixmap);
 }
 
@@ -732,8 +696,8 @@ void DisplayChannel::recreate_ogl_context_interrupt()
 {
     Canvas* canvas;
 
-    if (surfaces_mngr.is_present_canvas(0)) { //fix me to all surfaces
-        canvas = surfaces_mngr.get_canvas(0);
+    if (_surfaces_cache.exist(0)) { //fix me to all surfaces
+        canvas = _surfaces_cache[0];
         ((GCanvas *)(canvas))->touch_context();
         ((GCanvas *)canvas)->textures_lost();
         delete canvas;
@@ -743,13 +707,13 @@ void DisplayChannel::recreate_ogl_context_interrupt()
         THROW("create_ogl_canvas failed");
     }
 
-    canvas = surfaces_mngr.get_canvas(0);
+    canvas = _surfaces_cache[0];
     ((GCanvas *)(canvas))->touch_context();
 }
 
 void DisplayChannel::recreate_ogl_context()
 {
-    if (surfaces_mngr.is_present_canvas(0) && surfaces_mngr.get_canvas(0)->get_pixmap_type() ==
+    if (_surfaces_cache.exist(0) && _surfaces_cache[0]->get_pixmap_type() ==
         CANVAS_TYPE_GL) {
         if (!screen()->need_recreate_context_gl()) {
             _gl_interrupt_recreate.trigger();
@@ -889,12 +853,12 @@ void DisplayChannel::update_interrupt()
     Canvas *canvas;
 #endif
 
-    if (!surfaces_mngr.is_present_canvas(0) || !screen()) {
+    if (!_surfaces_cache.exist(0) || !screen()) {
         return;
     }
 
 #ifdef USE_OPENGL
-    canvas = surfaces_mngr.get_canvas(0);
+    canvas = _surfaces_cache[0];
     if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
         ((GCanvas *)(canvas))->pre_gl_copy();
     }
@@ -918,7 +882,7 @@ void DisplayChannel::pre_migrate()
 void DisplayChannel::post_migrate()
 {
 #ifdef USE_OPENGL
-    if (surfaces_mngr.get_canvas(0)->get_pixmap_type() == CANVAS_TYPE_GL) {
+    if (_surfaces_cache.exist(0) && _surfaces_cache[0]->get_pixmap_type() == CANVAS_TYPE_GL) {
         _gl_interrupt_recreate.trigger();
     }
 #endif
@@ -929,15 +893,11 @@ void DisplayChannel::post_migrate()
 void DisplayChannel::copy_pixels(const QRegion& dest_region,
                                  RedDrawable& dest_dc)
 {
-    Canvas *canvas;
-
-    if (!surfaces_mngr.is_present_canvas(0)) {
+    if (!_surfaces_cache.exist(0)) {
         return;
     }
 
-    canvas = surfaces_mngr.get_canvas(0);
-
-    canvas->copy_pixels(dest_region, dest_dc);
+    _surfaces_cache[0]->copy_pixels(dest_region, dest_dc);
 }
 
 class ActivateTimerEvent: public Event {
@@ -1043,11 +1003,8 @@ void DisplayChannel::on_connect()
 
 void DisplayChannel::on_disconnect()
 {
-    if (surfaces_mngr.is_present_canvas(0)) {
-        Canvas *canvas;
-
-        canvas = surfaces_mngr.get_canvas(0);
-        canvas->clear();
+    if (_surfaces_cache.exist(0)) {
+        _surfaces_cache[0]->clear();
     }
 
     if (screen()) {
@@ -1073,9 +1030,8 @@ bool DisplayChannel::create_sw_canvas(int surface_id, int width, int height, uin
         SCanvas *canvas = new SCanvas(surface_id == 0, width, height, format,
                                       screen()->get_window(),
                                       _pixmap_cache, _palette_cache, _glz_window,
-                                      surfaces_mngr.get_surfaces());
-        surfaces_mngr.add_canvas(surface_id, canvas);
-        surfaces_mngr.add_surface(surface_id, canvas->get_internal_canvas());
+                                      _surfaces_cache);
+        _surfaces_cache[surface_id] = canvas;
         if (surface_id == 0) {
             LOG_INFO("display %d: using sw", get_id());
         }
@@ -1097,12 +1053,11 @@ bool DisplayChannel::create_ogl_canvas(int surface_id, int width, int height, ui
                                       _pixmap_cache,
                                       _palette_cache,
                                       _glz_window,
-                                      surfaces_mngr.get_surfaces());
+                                      _surfaces_cache);
 
         screen()->untouch_context();
 
-        surfaces_mngr.add_canvas(surface_id, canvas);
-        surfaces_mngr.add_surface(surface_id, canvas->get_internal_canvas());
+        _surfaces_cache[surface_id] = canvas;
         _rendertype = rendertype;
         if (surface_id == 0) {
             LOG_INFO("display %d: using ogl", get_id());
@@ -1121,9 +1076,8 @@ bool DisplayChannel::create_gdi_canvas(int surface_id, int width, int height, ui
     try {
         GDICanvas *canvas = new GDICanvas(width, height, format,
                                           _pixmap_cache, _palette_cache, _glz_window,
-                                          surfaces_mngr.get_surfaces());
-        surfaces_mngr.add_canvas(surface_id, canvas);
-        surfaces_mngr.add_surface(surface_id, canvas->get_internal_canvas());
+                                          _surfaces_cache);
+        _surfaces_cache[surface_id] = canvas;
         if (surface_id == 0) {
             LOG_INFO("display %d: using gdi", get_id());
         }
@@ -1139,11 +1093,13 @@ void DisplayChannel::destroy_canvas(int surface_id)
 {
     Canvas *canvas;
 
-    if (!surfaces_mngr.is_present_canvas(surface_id)) {
+    if (!_surfaces_cache.exist(surface_id)) {
+        LOG_INFO("surface does not exist: %d", surface_id);
         return;
     }
 
-    canvas = surfaces_mngr.get_canvas(surface_id);
+    canvas = _surfaces_cache[surface_id];
+    _surfaces_cache.erase(surface_id);
 
 #ifdef USE_OPENGL
     if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
@@ -1151,9 +1107,6 @@ void DisplayChannel::destroy_canvas(int surface_id)
     }
 #endif
 
-    surfaces_mngr.del_canvas(surface_id);
-    surfaces_mngr.del_surface(surface_id);
-
     delete canvas;
 }
 
@@ -1171,10 +1124,13 @@ void DisplayChannel::create_canvas(int surface_id, const std::vector<int>& canva
             recreate = false;
         }
 #endif
-
         screen()->set_update_interrupt_trigger(NULL);
     }
 
+    if (_surfaces_cache.exist(surface_id)) {
+        LOG_WARN("surface already exists: %d", surface_id);
+    }
+
     for (i = 0; i < canvas_types.size(); i++) {
 
         if (canvas_types[i] == CANVAS_OPTION_SW && create_sw_canvas(surface_id, width, height, format)) {
@@ -1208,7 +1164,7 @@ void DisplayChannel::handle_mode(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayMode *mode = (SpiceMsgDisplayMode *)message->data();
 
-    if (surfaces_mngr.is_present_canvas(0)) {
+    if (_surfaces_cache.exist(0)) {
         destroy_primary_surface();
     }
     create_primary_surface(mode->x_res, mode->y_res,
@@ -1244,10 +1200,8 @@ void DisplayChannel::reset_screen()
 
 void DisplayChannel::handle_reset(RedPeer::InMessage *message)
 {
-    if (surfaces_mngr.is_present_canvas(0)) {
-        Canvas *canvas;
-        canvas = surfaces_mngr.get_canvas(0);
-        canvas->clear();
+    if (_surfaces_cache.exist(0)) {
+        _surfaces_cache[0]->clear();
     }
 
     _palette_cache.clear();
@@ -1326,10 +1280,14 @@ void DisplayChannel::handle_stream_create(RedPeer::InMessage* message)
         THROW("stream exist");
     }
 
+    if (!_surfaces_cache.exist(surface_id)) {
+        THROW("surface does not exist: %d", surface_id);
+    }
+
     uint32_t num_clip_rects;
     SpiceRect* clip_rects;
     set_clip_rects(stream_create->clip, num_clip_rects, clip_rects);
-    _streams[stream_create->id] = new VideoStream(get_client(), *surfaces_mngr.get_canvas(surface_id),
+    _streams[stream_create->id] = new VideoStream(get_client(), *_surfaces_cache[surface_id],
                                                   *this, stream_create->codec_type,
                                                   !!(stream_create->flags & SPICE_STREAM_FLAGS_TOP_DOWN),
                                                   stream_create->stream_width,
@@ -1432,7 +1390,7 @@ void DisplayChannel::create_primary_surface(int width, int height, uint32_t form
     _format = format;
 
 #ifdef USE_OPENGL
-    canvas = surfaces_mngr.get_canvas(0);
+    canvas = _surfaces_cache[0];
 
     if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
         ((GCanvas *)(canvas))->touch_context();
@@ -1455,7 +1413,7 @@ void DisplayChannel::create_surface(int surface_id, int width, int height, uint3
 #ifdef USE_OPENGL
     Canvas *canvas;
 
-    canvas = surfaces_mngr.get_canvas(surface_id);
+    canvas = _surfaces_cache[surface_id];
 
     if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
         ((GCanvas *)(canvas))->touch_context();
@@ -1467,11 +1425,8 @@ void DisplayChannel::destroy_primary_surface()
 {
     if (screen()) {
 #ifdef USE_OPENGL
-        if (surfaces_mngr.is_present_canvas(0)) {
-            Canvas *canvas;
-
-            canvas = surfaces_mngr.get_canvas(0);
-            if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+        if (_surfaces_cache.exist(0)) {
+            if (_surfaces_cache[0]->get_pixmap_type() == CANVAS_TYPE_GL) {
                 screen()->unset_type_gl();
                 screen()->untouch_context();
             }
@@ -1538,7 +1493,7 @@ void DisplayChannel::handle_copy_bits(RedPeer::InMessage* message)
     Canvas *canvas;
     SpiceMsgDisplayCopyBits* copy_bits = (SpiceMsgDisplayCopyBits*)message->data();
     PRE_DRAW;
-    canvas = surfaces_mngr.get_canvas(copy_bits->base.surface_id);
+    canvas = _surfaces_cache[copy_bits->base.surface_id];
     canvas->copy_bits(*copy_bits, message->size());
     POST_DRAW;
     if (copy_bits->base.surface_id == 0) {
@@ -1550,7 +1505,7 @@ void DisplayChannel::handle_draw_fill(RedPeer::InMessage* message)
 {
     Canvas *canvas;
     SpiceMsgDisplayDrawFill* fill = (SpiceMsgDisplayDrawFill*)message->data();
-    canvas = surfaces_mngr.get_canvas(fill->base.surface_id);
+    canvas = _surfaces_cache[fill->base.surface_id];
     DRAW(fill);
 }
 
@@ -1558,7 +1513,7 @@ void DisplayChannel::handle_draw_opaque(RedPeer::InMessage* message)
 {
     Canvas *canvas;
     SpiceMsgDisplayDrawOpaque* opaque = (SpiceMsgDisplayDrawOpaque*)message->data();
-    canvas = surfaces_mngr.get_canvas(opaque->base.surface_id);
+    canvas = _surfaces_cache[opaque->base.surface_id];
     DRAW(opaque);
 }
 
@@ -1566,7 +1521,7 @@ void DisplayChannel::handle_draw_copy(RedPeer::InMessage* message)
 {
     Canvas *canvas;
     SpiceMsgDisplayDrawCopy* copy = (SpiceMsgDisplayDrawCopy*)message->data();
-    canvas = surfaces_mngr.get_canvas(copy->base.surface_id);
+    canvas = _surfaces_cache[copy->base.surface_id];
     DRAW(copy);
 }
 
@@ -1574,7 +1529,7 @@ void DisplayChannel::handle_draw_blend(RedPeer::InMessage* message)
 {
     Canvas *canvas;
     SpiceMsgDisplayDrawBlend* blend = (SpiceMsgDisplayDrawBlend*)message->data();
-    canvas = surfaces_mngr.get_canvas(blend->base.surface_id);
+    canvas = _surfaces_cache[blend->base.surface_id];
     DRAW(blend);
 }
 
@@ -1582,7 +1537,7 @@ void DisplayChannel::handle_draw_blackness(RedPeer::InMessage* message)
 {
     Canvas *canvas;
     SpiceMsgDisplayDrawBlackness* blackness = (SpiceMsgDisplayDrawBlackness*)message->data();
-    canvas = surfaces_mngr.get_canvas(blackness->base.surface_id);
+    canvas = _surfaces_cache[blackness->base.surface_id];
     DRAW(blackness);
 }
 
@@ -1590,7 +1545,7 @@ void DisplayChannel::handle_draw_whiteness(RedPeer::InMessage* message)
 {
     Canvas *canvas;
     SpiceMsgDisplayDrawWhiteness* whiteness = (SpiceMsgDisplayDrawWhiteness*)message->data();
-    canvas = surfaces_mngr.get_canvas(whiteness->base.surface_id);
+    canvas = _surfaces_cache[whiteness->base.surface_id];
     DRAW(whiteness);
 }
 
@@ -1598,7 +1553,7 @@ void DisplayChannel::handle_draw_invers(RedPeer::InMessage* message)
 {
     Canvas *canvas;
     SpiceMsgDisplayDrawInvers* invers = (SpiceMsgDisplayDrawInvers*)message->data();
-    canvas = surfaces_mngr.get_canvas(invers->base.surface_id);
+    canvas = _surfaces_cache[invers->base.surface_id];
     DRAW(invers);
 }
 
@@ -1606,7 +1561,7 @@ void DisplayChannel::handle_draw_rop3(RedPeer::InMessage* message)
 {
     Canvas *canvas;
     SpiceMsgDisplayDrawRop3* rop3 = (SpiceMsgDisplayDrawRop3*)message->data();
-    canvas = surfaces_mngr.get_canvas(rop3->base.surface_id);
+    canvas = _surfaces_cache[rop3->base.surface_id];
     DRAW(rop3);
 }
 
@@ -1614,7 +1569,7 @@ void DisplayChannel::handle_draw_stroke(RedPeer::InMessage* message)
 {
     Canvas *canvas;
     SpiceMsgDisplayDrawStroke* stroke = (SpiceMsgDisplayDrawStroke*)message->data();
-    canvas = surfaces_mngr.get_canvas(stroke->base.surface_id);
+    canvas = _surfaces_cache[stroke->base.surface_id];
     DRAW(stroke);
 }
 
@@ -1622,7 +1577,7 @@ void DisplayChannel::handle_draw_text(RedPeer::InMessage* message)
 {
     Canvas *canvas;
     SpiceMsgDisplayDrawText* text = (SpiceMsgDisplayDrawText*)message->data();
-    canvas = surfaces_mngr.get_canvas(text->base.surface_id);
+    canvas = _surfaces_cache[text->base.surface_id];
     DRAW(text);
 }
 
@@ -1630,7 +1585,7 @@ void DisplayChannel::handle_draw_transparent(RedPeer::InMessage* message)
 {
     Canvas *canvas;
     SpiceMsgDisplayDrawTransparent* transparent = (SpiceMsgDisplayDrawTransparent*)message->data();
-    canvas = surfaces_mngr.get_canvas(transparent->base.surface_id);
+    canvas = _surfaces_cache[transparent->base.surface_id];
     DRAW(transparent);
 }
 
@@ -1638,7 +1593,7 @@ void DisplayChannel::handle_draw_alpha_blend(RedPeer::InMessage* message)
 {
     Canvas *canvas;
     SpiceMsgDisplayDrawAlphaBlend* alpha_blend = (SpiceMsgDisplayDrawAlphaBlend*)message->data();
-    canvas = surfaces_mngr.get_canvas(alpha_blend->base.surface_id);
+    canvas = _surfaces_cache[alpha_blend->base.surface_id];
     DRAW(alpha_blend);
 }
 
diff --git a/client/display_channel.h b/client/display_channel.h
index 647fb66..f30311d 100644
--- a/client/display_channel.h
+++ b/client/display_channel.h
@@ -80,21 +80,6 @@ private:
     DisplayChannel& _channel;
 };
 
-class DisplaySurfacesManger {
-public:
-    void add_surface(int surface_id, SpiceCanvas *surface);
-    void del_surface(int surface_id);
-    void add_canvas(int surface_id, Canvas *canvas);
-    void del_canvas(int surface_id);
-
-    CSurfaces& get_surfaces();
-    bool is_present_canvas(int surface_id);
-    Canvas* get_canvas(int surface_id);
-private:
-    CSurfaces surfaces;
-    CCanvases canvases;
-};
-
 class DisplayChannel: public RedChannel, public ScreenLayer {
 public:
     DisplayChannel(RedClient& client, uint32_t id,
@@ -191,8 +176,9 @@ private:
     void reset_screen();
 
     static void set_clip_rects(const SpiceClip& clip, uint32_t& num_clip_rects, SpiceRect*& clip_rects);
+
 private:
-    DisplaySurfacesManger surfaces_mngr;
+    SurfacesCache _surfaces_cache;
     PixmapCache& _pixmap_cache;
     PaletteCache _palette_cache;
     GlzDecoderWindow& _glz_window;
diff --git a/client/red_gdi_canvas.cpp b/client/red_gdi_canvas.cpp
index 038f784..ed091af 100644
--- a/client/red_gdi_canvas.cpp
+++ b/client/red_gdi_canvas.cpp
@@ -34,7 +34,7 @@
 
 GDICanvas::GDICanvas(int width, int height, uint32_t format,
 		     PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-                     GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces)
+                     GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces)
     : Canvas (pixmap_cache, palette_cache, glz_decoder_window, csurfaces)
     , _pixmap (0)
 {
@@ -45,7 +45,7 @@ GDICanvas::GDICanvas(int width, int height, uint32_t format,
                                       &_pixmap->get_mutex(),
                                       format, &pixmap_cache.base,
                                       &palette_cache.base,
-                                      &csurfaces.base,
+                                      &csurfaces,
                                       &glz_decoder(),
                                       &jpeg_decoder(),
                                       &zlib_decoder()))) {
diff --git a/client/red_gdi_canvas.h b/client/red_gdi_canvas.h
index 76de12b..02b08b0 100644
--- a/client/red_gdi_canvas.h
+++ b/client/red_gdi_canvas.h
@@ -33,7 +33,7 @@ class GDICanvas: public Canvas {
 public:
     GDICanvas(int width, int height, uint32_t format,
 	      PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-              GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces);
+              GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces);
     virtual ~GDICanvas();
 
     virtual void thread_touch() {}
diff --git a/client/red_gl_canvas.cpp b/client/red_gl_canvas.cpp
index 92529ce..abe0855 100644
--- a/client/red_gl_canvas.cpp
+++ b/client/red_gl_canvas.cpp
@@ -36,7 +36,7 @@
 GCanvas::GCanvas(int width, int height, uint32_t format, RedWindow *win,
                  RenderType rendertype,
                  PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-                 GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces)
+                 GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces)
     : Canvas(pixmap_cache, palette_cache, glz_decoder_window, csurfaces)
     , _pixmap (0)
     , _textures_lost (false)
@@ -48,7 +48,7 @@ GCanvas::GCanvas(int width, int height, uint32_t format, RedWindow *win,
                                      SPICE_SURFACE_FMT_DEPTH(format),
                                      &pixmap_cache.base,
                                      &palette_cache.base,
-                                     &csurfaces.base,
+                                     &csurfaces,
                                      &glz_decoder(),
                                      &jpeg_decoder(),
                                      &zlib_decoder()))) {
diff --git a/client/red_gl_canvas.h b/client/red_gl_canvas.h
index 83e6512..a8c901c 100644
--- a/client/red_gl_canvas.h
+++ b/client/red_gl_canvas.h
@@ -36,7 +36,7 @@ public:
     GCanvas(int width, int height, uint32_t format, RedWindow *win,
             RenderType rendertype,
             PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-            GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces);
+            GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces);
     virtual ~GCanvas();
 
     void set_mode();
diff --git a/client/red_sw_canvas.cpp b/client/red_sw_canvas.cpp
index 782caca..860e2a5 100644
--- a/client/red_sw_canvas.cpp
+++ b/client/red_sw_canvas.cpp
@@ -37,7 +37,7 @@
 SCanvas::SCanvas(bool onscreen,
                  int width, int height, uint32_t format, RedWindow *win,
                  PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-                 GlzDecoderWindow &glz_decoder_window, CSurfaces& csurfaces)
+                 GlzDecoderWindow &glz_decoder_window, SurfacesCache& csurfaces)
     : Canvas (pixmap_cache, palette_cache, glz_decoder_window, csurfaces)
     , _pixmap (0)
 {
@@ -50,7 +50,7 @@ SCanvas::SCanvas(bool onscreen,
                                          _pixmap->get_stride(),
                                          &pixmap_cache.base,
                                          &palette_cache.base,
-                                         &csurfaces.base,
+                                         &csurfaces,
                                          &glz_decoder(),
                                          &jpeg_decoder(),
                                          &zlib_decoder());
@@ -58,7 +58,7 @@ SCanvas::SCanvas(bool onscreen,
         _canvas = canvas_create(width, height, format,
                                 &pixmap_cache.base,
                                 &palette_cache.base,
-                                &csurfaces.base,
+                                &csurfaces,
                                 &glz_decoder(),
                                 &jpeg_decoder(),
                                 &zlib_decoder());
diff --git a/client/red_sw_canvas.h b/client/red_sw_canvas.h
index 2f807c7..807c128 100644
--- a/client/red_sw_canvas.h
+++ b/client/red_sw_canvas.h
@@ -33,7 +33,7 @@ public:
     SCanvas(bool onscreen,
             int width, int height, uint32_t format, RedWindow *win,
             PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-            GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces);
+            GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces);
     virtual ~SCanvas();
 
     virtual void thread_touch() {}
commit 76966571748efa32a706d21830349ab2431aa4e6
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Sep 18 14:23:36 2011 +0300

    server: turn spice_server_migrate_start into a valid call
    
    We will add a qemu call to spice_server_migrate_start when migration starts.
    For now, it does nothing, but we may need this notification in the future.
    (cherry picked from commit b8213167717979e6f2fb52646e43eb458634e6a1 branch 0.8)

diff --git a/server/reds.c b/server/reds.c
index 045e275..acd8495 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -4019,16 +4019,10 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_info(SpiceServer *s, const char* des
 SPICE_GNUC_VISIBLE int spice_server_migrate_start(SpiceServer *s)
 {
     ASSERT(reds == s);
-
-    if (1) {
-        /* seamless doesn't work, fixing needs protocol change. */
-        return -1;
-    }
-
+    red_printf("");
     if (!reds->mig_spice) {
         return -1;
     }
-    reds_mig_started();
     return 0;
 }
 
commit f683815ad53cc39f485ddac9770e23282ca5c340
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Oct 9 13:13:41 2011 +0200

    server: handling semi-seamless migration in the target side
    
    (1) not sending anything to a migrated client till we recieve SPICE_MSGC_MIGRATE_END
    (2) start a new client migration (handle client_migrate_info) only after SPICE_MSGC_MIGRATE_END
        from the previous migration was received for this client
    (3) use the correct ticket
    
    Note: we assume the same channles are linked before and ater migration. i.e.,
          SPICE_MSGC_MAIN_ATTACH_CHANNELS is not sent from the clients.

diff --git a/server/main_channel.c b/server/main_channel.c
index ffc593d..b2439b2 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -129,6 +129,8 @@ struct MainChannelClient {
 #endif
     int mig_wait_connect;
     int mig_connect_ok;
+    int mig_wait_prev_complete;
+    int init_sent;
 };
 
 enum NetTestStage {
@@ -138,6 +140,9 @@ enum NetTestStage {
     NET_TEST_STAGE_RATE,
 };
 
+static void main_channel_release_pipe_item(RedChannelClient *rcc,
+                                           PipeItem *base, int item_pushed);
+
 int main_channel_is_connected(MainChannel *main_chan)
 {
     return red_channel_is_connected(&main_chan->base);
@@ -289,6 +294,11 @@ static PipeItem *main_multi_media_time_item_new(
 
 static void main_channel_push_channels(MainChannelClient *mcc)
 {
+    if (red_client_during_migrate_at_target(mcc->base.client)) {
+        red_printf("warning: ignoring unexpected SPICE_MSGC_MAIN_ATTACH_CHANNELS"
+                   "during migration");
+        return;
+    }
     red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_CHANNELS_LIST);
 }
 
@@ -451,7 +461,7 @@ static uint64_t main_channel_handle_migrate_data(RedChannelClient *base,
     return TRUE;
 }
 
-void main_channel_push_init(MainChannelClient *mcc, int connection_id,
+void main_channel_push_init(MainChannelClient *mcc,
     int display_channels_hint, int current_mouse_mode,
     int is_client_mouse_allowed, int multi_media_time,
     int ram_hint)
@@ -459,7 +469,7 @@ void main_channel_push_init(MainChannelClient *mcc, int connection_id,
     PipeItem *item;
 
     item = main_init_item_new(mcc,
-             connection_id, display_channels_hint, current_mouse_mode,
+             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);
 }
@@ -612,6 +622,13 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
     MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
 
+    if (!mcc->init_sent && base->type != SPICE_MSG_MAIN_INIT) {
+        red_printf("Init msg for client %p was not sent yet "
+                   "(client is probably during migration). Ignoring msg type %d",
+                   rcc->client, base->type);
+        main_channel_release_pipe_item(rcc, base, FALSE);
+        return;
+    }
     red_channel_client_init_send_data(rcc, base->type, base);
     switch (base->type) {
         case SPICE_MSG_MAIN_CHANNELS_LIST:
@@ -646,6 +663,7 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
                 mcc->ping_id);
             break;
         case SPICE_MSG_MAIN_INIT:
+            mcc->init_sent = TRUE;
             main_channel_marshall_init(m,
                 SPICE_CONTAINEROF(base, InitPipeItem, base));
             break;
@@ -710,6 +728,25 @@ void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, int su
     }
 }
 
+void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
+{
+    if (!red_client_during_migrate_at_target(mcc->base.client)) {
+        red_printf("unexpected SPICE_MSGC_MIGRATE_END");
+        return;
+    }
+    if (!red_channel_client_test_remote_cap(&mcc->base,
+                                            SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
+        red_printf("unexpected SPICE_MSGC_MIGRATE_END, "
+                   "client does not support semi-seamless migration");
+            return;
+    }
+    red_client_migrate_complete(mcc->base.client);
+    if (mcc->mig_wait_prev_complete) {
+        red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_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);
@@ -797,6 +834,9 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
                 }
     case SPICE_MSGC_DISCONNECTING:
         break;
+    case SPICE_MSGC_MAIN_MIGRATE_END:
+        main_channel_client_handle_migrate_end(mcc);
+        break;
     default:
         red_printf("unexpected type %d", type);
     }
@@ -989,8 +1029,13 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta
         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)) {
-            red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_BEGIN);
-            mcc->mig_wait_connect = TRUE;
+            if (red_client_during_migrate_at_target(mcc->base.client)) {
+                red_printf("client %p: wait till previous migration completes", mcc->base.client);
+                mcc->mig_wait_prev_complete = TRUE;
+            } else {
+                red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_BEGIN);
+                mcc->mig_wait_connect = TRUE;
+            }
             mcc->mig_connect_ok = FALSE;
             main_channel->num_clients_mig_wait++;
         }
@@ -1011,6 +1056,7 @@ void main_channel_migrate_cancel_wait(MainChannel *main_chan)
             mcc->mig_wait_connect = FALSE;
             mcc->mig_connect_ok = FALSE;
         }
+        mcc->mig_wait_prev_complete = FALSE;
     }
     main_chan->num_clients_mig_wait = 0;
 }
diff --git a/server/main_channel.h b/server/main_channel.h
index d97857d..c5d407e 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -78,7 +78,7 @@ void main_channel_push_agent_data(MainChannel *main_chan, uint8_t* data, size_t
 void main_channel_client_start_net_test(MainChannelClient *mcc);
 // TODO: huge. Consider making a reds_* interface for these functions
 // and calling from main.
-void main_channel_push_init(MainChannelClient *mcc, int connection_id, int display_channels_hint,
+void main_channel_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_push_notify(MainChannel *main_chan, uint8_t *mess, const int mess_len);
diff --git a/server/red_channel.c b/server/red_channel.c
index 51415cb..2ce0094 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -1215,7 +1215,7 @@ void red_channel_client_pipe_remove_and_release(RedChannelClient *rcc,
  * pretty tied together.
  */
 
-RedClient *red_client_new()
+RedClient *red_client_new(int migrated)
 {
     RedClient *client;
 
@@ -1223,6 +1223,7 @@ RedClient *red_client_new()
     ring_init(&client->channels);
     pthread_mutex_init(&client->lock, NULL);
     client->thread_id = pthread_self();
+    client->migrated = migrated;
 
     return client;
 }
@@ -1286,6 +1287,18 @@ void red_client_set_main(RedClient *client, MainChannelClient *mcc) {
     client->mcc = mcc;
 }
 
+void red_client_migrate_complete(RedClient *client)
+{
+    ASSERT(client->migrated);
+    client->migrated = FALSE;
+    reds_on_client_migrate_complete(client);
+}
+
+int red_client_during_migrate_at_target(RedClient *client)
+{
+    return client->migrated;
+}
+
 /*
  * Functions to push the same item to multiple pipes.
  */
diff --git a/server/red_channel.h b/server/red_channel.h
index e30401c..cce6965 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -450,13 +450,15 @@ struct RedClient {
     pthread_t thread_id;
 
     int disconnecting;
-
+    int migrated;
 };
 
-RedClient *red_client_new(void);
+RedClient *red_client_new(int migrated);
 MainChannelClient *red_client_get_main(RedClient *client);
 // main should be set once before all the other channels are created
 void red_client_set_main(RedClient *client, MainChannelClient *mcc);
+void red_client_migrate_complete(RedClient *client);
+int red_client_during_migrate_at_target(RedClient *client);
 
 void red_client_migrate(RedClient *client);
 // disconnects all the client's channels (should be called from the client's thread)
diff --git a/server/reds.c b/server/reds.c
index 43fdc2a..045e275 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -191,6 +191,18 @@ typedef struct RedsStatValue {
 
 #endif
 
+typedef struct RedsMigPendingLink {
+    RingItem ring_link; // list of links that belongs to the same client
+    SpiceLinkMess *link_msg;
+    RedsStream *stream;
+} RedsMigPendingLink;
+
+typedef struct RedsMigTargetClient {
+    RingItem link;
+    RedClient *client;
+    Ring pending_links;
+} RedsMigTargetClient;
+
 typedef struct RedsState {
     int listen_socket;
     int secure_listen_socket;
@@ -206,7 +218,8 @@ typedef struct RedsState {
     int mig_wait_disconnect;
     int mig_inprogress;
     int expect_migrate;
-    int mig_target;
+    Ring mig_target_clients;
+    int num_mig_target_clients;
     RedsMigSpice *mig_spice;
     int num_of_channels;
     Ring channels;
@@ -219,7 +232,6 @@ typedef struct RedsState {
     SpiceTimer *vdi_port_write_timer;
     int vdi_port_write_timer_started;
 
-    TicketAuthentication taTicket;
     SSL_CTX *ctx;
 
 #ifdef RED_STATISTICS
@@ -295,6 +307,8 @@ struct ChannelSecurityOptions {
 };
 
 static void migrate_timeout(void *opaque);
+static RedsMigTargetClient* reds_mig_target_client_find(RedClient *client);
+static void reds_mig_target_client_free(RedsMigTargetClient *mig_client);
 
 static ChannelSecurityOptions *channels_security = NULL;
 static int default_channel_security =
@@ -593,6 +607,8 @@ static int reds_main_channel_connected(void)
 
 void reds_client_disconnect(RedClient *client)
 {
+    RedsMigTargetClient *mig_client;
+
     if (!client || client->disconnecting) {
         return;
     }
@@ -607,6 +623,10 @@ void reds_client_disconnect(RedClient *client)
     // TODO: we need to handle agent properly for all clients!!!! (e.g., cut and paste, how?)
     // We shouldn't initialize the agent when there are still clients connected
 
+    mig_client = reds_mig_target_client_find(client);
+    if (mig_client) {
+        reds_mig_target_client_free(mig_client);
+    }
     ring_remove(&client->link);
     reds->num_clients--;
     red_client_destroy(client);
@@ -693,14 +713,14 @@ static void reds_update_mouse_mode(void)
 
 static void reds_agent_remove(void)
 {
-    if (!reds->mig_target) {
-        reds_reset_vdp();
-    }
+    // TODO: agent is broken with multiple clients. also need to figure out what to do when
+    // part of the clients are during target migration.
+    reds_reset_vdp();
 
     vdagent = NULL;
     reds_update_mouse_mode();
 
-    if (reds_main_channel_connected() && !reds->mig_target) {
+    if (reds_main_channel_connected()) {
         main_channel_push_agent_disconnected(reds->main_channel);
     }
 }
@@ -740,7 +760,7 @@ static int write_to_vdi_port(void)
     int total = 0;
     int n;
 
-    if (!vdagent || reds->mig_target) {
+    if (!vdagent) {
         return 0;
     }
 
@@ -844,8 +864,8 @@ static int read_from_vdi_port(void)
     }
     inside_call = 1;
 
-    if (reds->mig_target || !vdagent) {
-        // discard data only if we are migrating or vdagent has not been
+    if (!vdagent) {
+        // discard data only if we are migrating (?) or vdagent has not been
         // initialized.
         inside_call = 0;
         return 0;
@@ -931,7 +951,7 @@ void reds_handle_agent_mouse_event(const VDAgentMouseState *mouse_state)
     if (!inputs_inited()) {
         return;
     }
-    if (reds->mig_target || !(ring_item = ring_get_head(&reds->agent_state.internal_bufs))) {
+    if (!(ring_item = ring_get_head(&reds->agent_state.internal_bufs))) {
         reds->pending_mouse_event = TRUE;
         vdi_port_write_timer_start();
         return;
@@ -1346,7 +1366,6 @@ void reds_on_main_receive_migrate_data(MainMigrateData *data, uint8_t *end)
     reds_main_channel_restore_vdi_wqueue(data, pos, end);
     ASSERT(state->num_client_tokens + state->num_tokens == REDS_AGENT_WINDOW_SIZE);
 
-    reds->mig_target = FALSE;
     while (write_to_vdi_port() || read_from_vdi_port());
 }
 
@@ -1486,6 +1505,75 @@ int reds_expects_link_id(uint32_t connection_id)
     return 1;
 }
 
+static void reds_mig_target_client_add(RedClient *client)
+{
+    RedsMigTargetClient *mig_client;
+
+    ASSERT(reds);
+    red_printf("");
+    mig_client = spice_malloc0(sizeof(RedsMigTargetClient));
+    mig_client->client = client;
+    ring_init(&mig_client->pending_links);
+    ring_add(&reds->mig_target_clients, &mig_client->link);
+    reds->num_mig_target_clients++;
+
+}
+
+static RedsMigTargetClient* reds_mig_target_client_find(RedClient *client)
+{
+    RingItem *item;
+
+    RING_FOREACH(item, &reds->mig_target_clients) {
+        RedsMigTargetClient *mig_client;
+
+        mig_client = SPICE_CONTAINEROF(item, RedsMigTargetClient, link);
+        if (mig_client->client == client) {
+            return mig_client;
+        }
+    }
+    return NULL;
+}
+
+static void reds_mig_target_client_add_pending_link(RedsMigTargetClient *client,
+                                                    SpiceLinkMess *link_msg,
+                                                    RedsStream *stream)
+{
+    RedsMigPendingLink *mig_link;
+
+    ASSERT(reds);
+    ASSERT(client);
+    mig_link = spice_malloc0(sizeof(RedsMigPendingLink));
+    mig_link->link_msg = link_msg;
+    mig_link->stream = stream;
+
+    ring_add(&client->pending_links, &mig_link->ring_link);
+}
+
+static void reds_mig_target_client_free(RedsMigTargetClient *mig_client)
+{
+    RingItem *now, *next;
+
+    ring_remove(&mig_client->link);
+    reds->num_mig_target_clients--;
+
+    RING_FOREACH_SAFE(now, next, &mig_client->pending_links) {
+        RedsMigPendingLink *mig_link = SPICE_CONTAINEROF(now, RedsMigPendingLink, ring_link);
+        ring_remove(now);
+        free(mig_link);
+    }
+    free(mig_client);
+}
+
+static void reds_mig_target_client_disconnect_all()
+{
+    RingItem *now, *next;
+
+    RING_FOREACH_SAFE(now, next, &reds->mig_target_clients) {
+        RedsMigTargetClient *mig_client = SPICE_CONTAINEROF(now, RedsMigTargetClient, link);
+        reds_client_disconnect(mig_client->client);
+    }
+}
+
 // TODO: now that main is a separate channel this should
 // actually be joined with reds_handle_other_links, become reds_handle_link
 static void reds_handle_main_link(RedLinkInfo *link)
@@ -1496,6 +1584,7 @@ static void reds_handle_main_link(RedLinkInfo *link)
     uint32_t *caps;
     uint32_t connection_id;
     MainChannelClient *mcc;
+    int mig_target = FALSE;
 
     red_printf("");
     ASSERT(reds->main_channel);
@@ -1509,18 +1598,13 @@ static void reds_handle_main_link(RedLinkInfo *link)
         reds_send_link_result(link, SPICE_LINK_ERR_OK);
         while((connection_id = rand()) == 0);
         reds->agent_state.num_tokens = 0;
-        memcpy(&(reds->taTicket), &taTicket, sizeof(reds->taTicket));
-        reds->mig_target = FALSE;
+        mig_target = FALSE;
     } else {
-        // migration - check if this is one of the expected connection_id's
-        if (!reds_expects_link_id(link_mess->connection_id)) {
-            reds_send_link_result(link, SPICE_LINK_ERR_BAD_CONNECTION_ID);
-            reds_link_free(link);
-            return;
-        }
+        // TODO: make sure link_mess->connection_id is the same
+        // connection id the migration src had (use vmstate to store the connection id)
         reds_send_link_result(link, SPICE_LINK_ERR_OK);
         connection_id = link_mess->connection_id;
-        reds->mig_target = TRUE;
+        mig_target = TRUE;
     }
 
     reds->mig_inprogress = FALSE;
@@ -1534,11 +1618,11 @@ static void reds_handle_main_link(RedLinkInfo *link)
     link->link_mess = NULL;
     reds_link_free(link);
     caps = (uint32_t *)((uint8_t *)link_mess + link_mess->caps_offset);
-    client = red_client_new();
+    client = red_client_new(mig_target);
     ring_add(&reds->clients, &client->link);
     reds->num_clients++;
     mcc = main_channel_link(reds->main_channel, client,
-                            stream, connection_id, reds->mig_target,
+                            stream, connection_id, mig_target,
                             link_mess->num_common_caps,
                             link_mess->num_common_caps ? caps : NULL, link_mess->num_channel_caps,
                             link_mess->num_channel_caps ? caps + link_mess->num_common_caps : NULL);
@@ -1551,9 +1635,10 @@ static void reds_handle_main_link(RedLinkInfo *link)
         reds->agent_state.plug_generation++;
     }
 
-    if (!reds->mig_target) {
-        reds->agent_state.num_client_tokens = REDS_AGENT_WINDOW_SIZE;
-        main_channel_push_init(mcc, connection_id, red_dispatcher_count(),
+    reds->agent_state.num_client_tokens = REDS_AGENT_WINDOW_SIZE;
+
+    if (!mig_target) {
+        main_channel_push_init(mcc, red_dispatcher_count(),
             reds->mouse_mode, reds->is_client_mouse_allowed,
             reds_get_mm_time() - MM_TIME_DELTA,
             red_dispatcher_qxl_ram_size());
@@ -1561,6 +1646,8 @@ static void reds_handle_main_link(RedLinkInfo *link)
         main_channel_client_start_net_test(mcc);
         /* Now that we have a client, forward any pending agent data */
         while (read_from_vdi_port());
+    } else {
+        reds_mig_target_client_add(client);
     }
 }
 
@@ -1614,7 +1701,8 @@ static void reds_channel_do_link(RedChannel *channel, RedClient *client,
     }
 
     caps = (uint32_t *)((uint8_t *)link_msg + link_msg->caps_offset);
-    channel->client_cbs.connect(channel, client, stream, reds->mig_target,
+    channel->client_cbs.connect(channel, client, stream,
+                                red_client_during_migrate_at_target(client),
                                 link_msg->num_common_caps,
                                 link_msg->num_common_caps ? caps : NULL,
                                 link_msg->num_channel_caps,
@@ -1622,11 +1710,55 @@ static void reds_channel_do_link(RedChannel *channel, RedClient *client,
                                 caps + link_msg->num_common_caps : NULL);
 }
 
+void reds_on_client_migrate_complete(RedClient *client)
+{
+    RedsMigTargetClient *mig_client;
+    MainChannelClient *mcc;
+    RingItem *item;
+
+    red_printf("%p", client);
+    mcc = red_client_get_main(client);
+    mig_client = reds_mig_target_client_find(client);
+    if (!mig_client) {
+        red_printf("Error: mig target client was not found");
+        return;
+    }
+
+    // TODO: not doing net test. consider doing it on client_migrate_info
+    main_channel_push_init(mcc, red_dispatcher_count(),
+                           reds->mouse_mode, reds->is_client_mouse_allowed,
+                           reds_get_mm_time() - MM_TIME_DELTA,
+                           red_dispatcher_qxl_ram_size());
+
+    RING_FOREACH(item, &mig_client->pending_links) {
+        RedsMigPendingLink *mig_link;
+        RedChannel *channel;
+
+        mig_link = SPICE_CONTAINEROF(item, RedsMigPendingLink, ring_link);
+        channel = reds_find_channel(mig_link->link_msg->channel_type,
+                                    mig_link->link_msg->channel_id);
+        if (!channel) {
+            red_printf("warning: client %p channel (%d, %d) (type, id) wasn't found",
+                       client,
+                       mig_link->link_msg->channel_type,
+                       mig_link->link_msg->channel_id);
+            continue;
+        }
+        reds_channel_do_link(channel, client, mig_link->link_msg, mig_link->stream);
+    }
+
+    reds_mig_target_client_free(mig_client);
+
+    /* Now that we have a client, forward any pending agent data */
+    while (read_from_vdi_port());
+}
+
 static void reds_handle_other_links(RedLinkInfo *link)
 {
     RedChannel *channel;
     RedClient *client = NULL;
     SpiceLinkMess *link_mess;
+    RedsMigTargetClient *mig_client;
 
     link_mess = link->link_mess;
     if (reds->main_channel) {
@@ -1654,8 +1786,16 @@ static void reds_handle_other_links(RedLinkInfo *link)
     reds_send_link_result(link, SPICE_LINK_ERR_OK);
     reds_show_new_channel(link, link_mess->connection_id);
     reds_stream_remove_watch(link->stream);
-    reds_channel_do_link(channel, client, link_mess, link->stream);
-    free(link_mess);
+
+    mig_client = reds_mig_target_client_find(client);
+    if (red_client_during_migrate_at_target(client)) {
+        ASSERT(mig_client);
+        reds_mig_target_client_add_pending_link(mig_client, link_mess, link->stream);
+    } else {
+        ASSERT(!mig_client);
+        reds_channel_do_link(channel, client, link_mess, link->stream);
+        free(link_mess);
+    }
     link->stream = NULL;
     link->link_mess = NULL;
     reds_link_free(link);
@@ -1683,10 +1823,9 @@ static void reds_handle_ticket(void *opaque)
                         (unsigned char *)password, link->tiTicketing.rsa, RSA_PKCS1_OAEP_PADDING);
 
     if (ticketing_enabled) {
-        int expired = !link->link_mess->connection_id && taTicket.expiration_time < ltime;
-        char *actual_sever_pass = link->link_mess->connection_id ? reds->taTicket.password :
-                                                                   taTicket.password;
-        if (strlen(actual_sever_pass) == 0) {
+        int expired =  taTicket.expiration_time < ltime;
+
+        if (strlen(taTicket.password) == 0) {
             reds_send_link_result(link, SPICE_LINK_ERR_PERMISSION_DENIED);
             red_printf("Ticketing is enabled, but no password is set. "
                        "please set a ticket first");
@@ -1694,7 +1833,7 @@ static void reds_handle_ticket(void *opaque)
             return;
         }
 
-        if (expired || strncmp(password, actual_sever_pass, SPICE_MAX_PASSWORD_LENGTH) != 0) {
+        if (expired || strncmp(password, taTicket.password, SPICE_MAX_PASSWORD_LENGTH) != 0) {
             reds_send_link_result(link, SPICE_LINK_ERR_PERMISSION_DENIED);
             reds_link_free(link);
             return;
@@ -3049,7 +3188,10 @@ static void migrate_timeout(void *opaque)
     red_printf("");
     ASSERT(reds->mig_wait_connect || reds->mig_wait_disconnect);
     if (reds->mig_wait_connect) {
+        /* we will fall back to the switch host scheme when migration completes */
         main_channel_migrate_cancel_wait(reds->main_channel);
+        /* in case part of the client haven't yet completed the previous migration, disconnect them */
+        reds_mig_target_client_disconnect_all();
         reds_mig_cleanup();
     } else {
         reds_mig_disconnect();
@@ -3108,9 +3250,7 @@ static void attach_to_red_agent(SpiceCharDeviceInstance *sin)
     state->read_filter.discard_all = FALSE;
     reds->agent_state.plug_generation++;
 
-    if (!reds->mig_target) {
-        main_channel_push_agent_connected(reds->main_channel);
-    }
+    main_channel_push_agent_connected(reds->main_channel);
 }
 
 SPICE_GNUC_VISIBLE void spice_server_char_device_wakeup(SpiceCharDeviceInstance* sin)
@@ -3413,6 +3553,7 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
     reds->num_clients = 0;
     main_dispatcher_init(core);
     ring_init(&reds->channels);
+    ring_init(&reds->mig_target_clients);
 
     if (!(reds->mig_timer = core->timer_add(migrate_timeout, NULL))) {
         red_error("migration timer create failed");
@@ -3847,7 +3988,7 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
 
     reds->expect_migrate = TRUE;
 
-
+    /* main channel will take care of clients that are still during migration (at target)*/
     if (main_channel_migrate_connect(reds->main_channel, reds->mig_spice)) {
         reds_mig_started();
     } else {
diff --git a/server/reds.h b/server/reds.h
index 9feb9ab..450825d 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -147,6 +147,6 @@ void reds_on_main_migrate_connected(void); //should be called when all the clien
                                            // are connected to the target
 void reds_on_main_receive_migrate_data(MainMigrateData *data, uint8_t *end);
 void reds_on_main_mouse_mode_request(void *message, size_t size);
-
+void reds_on_client_migrate_complete(RedClient *client);
 #endif
 
commit c88e927fc734fdb8240d925eb9e6a87b203c5bb3
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Thu Oct 6 12:34:22 2011 +0200

    server: move the linking of channels to a separate routine

diff --git a/server/reds.c b/server/reds.c
index 8799e00..43fdc2a 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -1597,13 +1597,36 @@ static void openssl_init(RedLinkInfo *link)
     BN_set_word(link->tiTicketing.bn, f4);
 }
 
+static void reds_channel_do_link(RedChannel *channel, RedClient *client,
+                                 SpiceLinkMess *link_msg,
+                                 RedsStream *stream)
+{
+    uint32_t *caps;
+
+    ASSERT(channel);
+    ASSERT(link_msg);
+    ASSERT(stream);
+
+    if (link_msg->channel_type == SPICE_CHANNEL_INPUTS && !stream->ssl) {
+        char *mess = "keyboard channel is insecure";
+        const int mess_len = strlen(mess);
+        main_channel_push_notify(reds->main_channel, (uint8_t*)mess, mess_len);
+    }
+
+    caps = (uint32_t *)((uint8_t *)link_msg + link_msg->caps_offset);
+    channel->client_cbs.connect(channel, client, stream, reds->mig_target,
+                                link_msg->num_common_caps,
+                                link_msg->num_common_caps ? caps : NULL,
+                                link_msg->num_channel_caps,
+                                link_msg->num_channel_caps ?
+                                caps + link_msg->num_common_caps : NULL);
+}
+
 static void reds_handle_other_links(RedLinkInfo *link)
 {
     RedChannel *channel;
     RedClient *client = NULL;
-    RedsStream *stream;
     SpiceLinkMess *link_mess;
-    uint32_t *caps;
 
     link_mess = link->link_mess;
     if (reds->main_channel) {
@@ -1630,24 +1653,12 @@ static void reds_handle_other_links(RedLinkInfo *link)
 
     reds_send_link_result(link, SPICE_LINK_ERR_OK);
     reds_show_new_channel(link, link_mess->connection_id);
-    if (link_mess->channel_type == SPICE_CHANNEL_INPUTS && !link->stream->ssl) {
-        char *mess = "keyboard channel is insecure";
-        const int mess_len = strlen(mess);
-        main_channel_push_notify(reds->main_channel, (uint8_t*)mess, mess_len);
-    }
-    stream = link->stream;
-    reds_stream_remove_watch(stream);
+    reds_stream_remove_watch(link->stream);
+    reds_channel_do_link(channel, client, link_mess, link->stream);
+    free(link_mess);
     link->stream = NULL;
     link->link_mess = NULL;
     reds_link_free(link);
-    caps = (uint32_t *)((uint8_t *)link_mess + link_mess->caps_offset);
-    channel->client_cbs.connect(channel, client, stream, reds->mig_target,
-                                link_mess->num_common_caps,
-                                link_mess->num_common_caps ? caps : NULL,
-                                link_mess->num_channel_caps,
-                                link_mess->num_channel_caps ?
-                                caps + link_mess->num_common_caps : NULL);
-    free(link_mess);
 }
 
 static void reds_handle_link(RedLinkInfo *link)
commit e62521ca51dae8c6d943c345e9ff2124727eacaa
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Sep 18 13:09:52 2011 +0300

    server: handle spice_server_migrate_end
    
    If the migration has completed successfully:
    (1) send MSG_MAIN_MIGRATE_END to the clients that are connected to the target
    (2) send MSG_MAIN_SWITCH_HOST to all the other clients
    
    If the migration failed, send MSG_MAIN_MIGRATE_CANCEL to clients that are
    connected to the target.
    
    (cherry picked from commit 4b82580fc36228af13db4ac3c403753d6b5c40b5 branch 0.8;
     Was modified to support multiple clients, and the separation of main_channel from reds)
    
    Conflicts:
    
    	server/reds.c

diff --git a/server/main_channel.c b/server/main_channel.c
index 5b16b30..ffc593d 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -548,11 +548,6 @@ static void main_channel_marshall_migrate(SpiceMarshaller *m)
     spice_marshall_msg_migrate(m, &migrate);
 }
 
-void main_channel_push_migrate_cancel(MainChannel *main_chan)
-{
-    red_channel_pipes_add_type(&main_chan->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
-}
-
 void main_channel_push_multi_media_time(MainChannel *main_chan, int time)
 {
     MultiMediaTimePipeItem info = {
@@ -563,33 +558,43 @@ void main_channel_push_multi_media_time(MainChannel *main_chan, int time)
         main_multi_media_time_item_new, &info);
 }
 
-static PipeItem *main_migrate_switch_item_new(RedChannelClient *rcc,
-                                              void *data, int num)
+static void main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice *mig_target)
 {
-    RefsPipeItem *item = spice_malloc(sizeof(*item));
-
-    item->refs = data;
-    red_channel_pipe_item_init(rcc->channel, &item->base,
-                               SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST);
-    return &item->base;
+    ASSERT(mig_target);
+    free(main_channel->mig_target.host);
+    main_channel->mig_target.host = strdup(mig_target->host);
+    free(main_channel->mig_target.cert_subject);
+    if (mig_target->cert_subject) {
+        main_channel->mig_target.cert_subject = strdup(mig_target->cert_subject);
+    }
+    main_channel->mig_target.port = mig_target->port;
+    main_channel->mig_target.sport = mig_target->sport;
 }
 
-void main_channel_push_migrate_switch(MainChannel *main_chan)
+void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target)
 {
-    int *refs = spice_malloc0(sizeof(int));
-
-    *refs = main_chan->base.clients_num;
-    red_channel_pipes_new_add_push(&main_chan->base,
-        main_migrate_switch_item_new, (void*)refs);
+    main_channel_fill_mig_target(main_chan, mig_target);
+    red_channel_pipes_add_type(&main_chan->base, SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST);
 }
 
-static void main_channel_marshall_migrate_switch(SpiceMarshaller *m)
+static void main_channel_marshall_migrate_switch(SpiceMarshaller *m, RedChannelClient *rcc)
 {
     SpiceMsgMainMigrationSwitchHost migrate;
+    MainChannel *main_ch;
 
     red_printf("");
-
-    reds_fill_mig_switch(&migrate);
+    main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+    migrate.port = main_ch->mig_target.port;
+    migrate.sport = main_ch->mig_target.sport;
+    migrate.host_size = strlen(main_ch->mig_target.host) + 1;
+    migrate.host_data = (uint8_t *)main_ch->mig_target.host;
+    if (main_ch->mig_target.cert_subject) {
+        migrate.cert_subject_size = strlen(main_ch->mig_target.cert_subject) + 1;
+        migrate.cert_subject_data = (uint8_t *)main_ch->mig_target.cert_subject;
+    } else {
+        migrate.cert_subject_size = 0;
+        migrate.cert_subject_data = NULL;
+    }
     spice_marshall_msg_main_migrate_switch_host(m, &migrate);
 }
 
@@ -659,7 +664,7 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
                 SPICE_CONTAINEROF(base, MultiMediaTimePipeItem, base));
             break;
         case SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST:
-            main_channel_marshall_migrate_switch(m);
+            main_channel_marshall_migrate_switch(m, rcc);
             break;
     };
     red_channel_client_begin_send_message(rcc);
@@ -679,14 +684,6 @@ static void main_channel_release_pipe_item(RedChannelClient *rcc,
             }
             break;
         }
-        case SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST: {
-            RefsPipeItem *data = (RefsPipeItem*)base;
-            if (!--*(data->refs)) {
-                free(data->refs);
-                reds_mig_release();
-            }
-            break;
-        }
         default:
             break;
     }
@@ -985,16 +982,7 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta
 {
     RingItem *client_link;
 
-    ASSERT(mig_target);
-    free(main_channel->mig_target.host);
-    main_channel->mig_target.host = strdup(mig_target->host);
-    free(main_channel->mig_target.cert_subject);
-    if (mig_target->cert_subject) {
-        main_channel->mig_target.cert_subject = strdup(mig_target->cert_subject);
-    }
-    main_channel->mig_target.port = mig_target->port;
-    main_channel->mig_target.sport = mig_target->sport;
-
+    main_channel_fill_mig_target(main_channel, mig_target);
     main_channel->num_clients_mig_wait = 0;
 
     RING_FOREACH(client_link, &main_channel->base.clients) {
@@ -1026,3 +1014,44 @@ void main_channel_migrate_cancel_wait(MainChannel *main_chan)
     }
     main_chan->num_clients_mig_wait = 0;
 }
+
+int main_channel_migrate_complete(MainChannel *main_chan, int success)
+{
+    RingItem *client_link;
+    int semi_seamless_count = 0;
+
+    red_printf("");
+
+    if (ring_is_empty(&main_chan->base.clients)) {
+        red_printf("no peer connected");
+        return 0;
+    }
+
+    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) {
+                red_printf("client %p MIGRATE_END", mcc->base.client);
+                red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_END);
+                semi_seamless_count++;
+            } else {
+                red_printf("client %p MIGRATE_CANCEL", mcc->base.client);
+                red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
+            }
+        } else {
+            if (success) {
+                red_printf("client %p SWITCH_HOST", mcc->base.client);
+                red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST);
+            }
+        }
+        mcc->mig_connect_ok = FALSE;
+        mcc->mig_wait_connect = FALSE;
+   }
+   return semi_seamless_count;
+}
+
diff --git a/server/main_channel.h b/server/main_channel.h
index f3702e7..d97857d 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -83,8 +83,6 @@ void main_channel_push_init(MainChannelClient *mcc, int connection_id, int displ
     int ram_hint);
 void main_channel_push_notify(MainChannel *main_chan, uint8_t *mess, const int mess_len);
 void main_channel_push_migrate(MainChannel *main_chan);
-void main_channel_push_migrate_switch(MainChannel *main_chan);
-void main_channel_push_migrate_cancel(MainChannel *main_chan);
 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);
@@ -95,10 +93,14 @@ uint64_t main_channel_client_get_bitrate_per_sec(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);
+
 /* semi seamless migration */
 
 /* returns the number of clients that we are waiting for their connection */
 int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target);
 void main_channel_migrate_cancel_wait(MainChannel *main_chan);
-void main_channel_migrate_complete(MainChannel *main_chan, int success);
+/* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was sent*/
+int main_channel_migrate_complete(MainChannel *main_chan, int success);
 #endif
diff --git a/server/reds.c b/server/reds.c
index bc93e4a..8799e00 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -205,6 +205,7 @@ typedef struct RedsState {
     int mig_wait_connect;
     int mig_wait_disconnect;
     int mig_inprogress;
+    int expect_migrate;
     int mig_target;
     RedsMigSpice *mig_spice;
     int num_of_channels;
@@ -2983,7 +2984,7 @@ typedef struct RedsMigCertPubKeyInfo {
     uint32_t len;
 } RedsMigCertPubKeyInfo;
 
-void reds_mig_release(void)
+static void reds_mig_release(void)
 {
     if (reds->mig_spice) {
         free(reds->mig_spice->cert_subject);
@@ -2998,29 +2999,14 @@ static void reds_mig_started(void)
     red_printf("");
     ASSERT(reds->mig_spice);
 
-    reds->mig_wait_connect = TRUE;
     reds->mig_inprogress = TRUE;
-
-    if (reds->listen_watch != NULL) {
-        core->watch_update_mask(reds->listen_watch, 0);
-    }
-
-    if (reds->secure_listen_watch != NULL) {
-        core->watch_update_mask(reds->secure_listen_watch, 0);
-    }
+    reds->mig_wait_connect = TRUE;
     core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
 }
 
 static void reds_mig_finished(int completed)
 {
     red_printf("");
-    if (reds->listen_watch != NULL) {
-        core->watch_update_mask(reds->listen_watch, SPICE_WATCH_EVENT_READ);
-    }
-
-    if (reds->secure_listen_watch != NULL) {
-        core->watch_update_mask(reds->secure_listen_watch, SPICE_WATCH_EVENT_READ);
-    }
 
     if (!reds_main_channel_connected()) {
         red_printf("no peer connected");
@@ -3028,28 +3014,13 @@ static void reds_mig_finished(int completed)
     }
     reds->mig_inprogress = TRUE;
 
-    if (completed) {
-        RingItem *link, *next;
-
+    if (main_channel_migrate_complete(reds->main_channel, completed)) {
         reds->mig_wait_disconnect = TRUE;
         core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
-
-        // TODO: so now that main channel is separate, how exactly does migration of it work?
-        //  - it can have an empty migrate - that seems ok
-        //  - I can try to fill it's migrate, then move stuff from reds.c there, but a lot of data
-        //    is in reds state right now.
-        //    currently the migrate callback of main_channel does nothing
-        main_channel_push_migrate(reds->main_channel);
-
-        RING_FOREACH_SAFE(link, next, &reds->clients) {
-            red_client_migrate(SPICE_CONTAINEROF(link, RedClient, link));
-        }
     } else {
-        main_channel_push_migrate_cancel(reds->main_channel);
-        // TODO: all the seemless migration is broken. Before MC we waited for disconection of one client,
-        // no we need to wait to all the clients (see mig_timer)?
         reds_mig_cleanup();
     }
+    reds_mig_release();
 }
 
 static void reds_mig_switch(void)
@@ -3058,30 +3029,8 @@ static void reds_mig_switch(void)
         red_printf("warning: reds_mig_switch called without migrate_info set");
         return;
     }
-    main_channel_push_migrate_switch(reds->main_channel);
-}
-
-void reds_fill_mig_switch(SpiceMsgMainMigrationSwitchHost *migrate)
-{
-    RedsMigSpice *s = reds->mig_spice;
-
-    if (s == NULL) {
-        red_printf(
-            "error: reds_fill_mig_switch called without migrate info set");
-        bzero(migrate, sizeof(*migrate));
-        return;
-    }
-    migrate->port = s->port;
-    migrate->sport = s->sport;
-    migrate->host_size = strlen(s->host) + 1;
-    migrate->host_data = (uint8_t *)s->host;
-    if (s->cert_subject) {
-        migrate->cert_subject_size = strlen(s->cert_subject) + 1;
-        migrate->cert_subject_data = (uint8_t *)s->cert_subject;
-    } else {
-        migrate->cert_subject_size = 0;
-        migrate->cert_subject_data = NULL;
-    }
+    main_channel_migrate_switch(reds->main_channel, reds->mig_spice);
+    reds_mig_release();
 }
 
 static void migrate_timeout(void *opaque)
@@ -3873,6 +3822,11 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
     ASSERT(migration_interface);
     ASSERT(reds == s);
 
+    if (reds->expect_migrate) {
+        red_printf("warning: consecutive calls without migration. Canceling previous call");
+        main_channel_migrate_complete(reds->main_channel, FALSE);
+    }
+
     sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
 
     if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) {
@@ -3880,10 +3834,16 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
         return -1;
     }
 
+    reds->expect_migrate = TRUE;
+
+
     if (main_channel_migrate_connect(reds->main_channel, reds->mig_spice)) {
-        reds->mig_wait_connect = TRUE;
         reds_mig_started();
     } else {
+        if (reds->num_clients == 0) {
+            reds_mig_release();
+            red_printf("no client connected");
+        }
         sif->migrate_connect_complete(migration_interface);
     }
 
@@ -3894,13 +3854,13 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_info(SpiceServer *s, const char* des
                                           int port, int secure_port,
                                           const char* cert_subject)
 {
+    red_printf("");
     ASSERT(!migration_interface);
     ASSERT(reds == s);
 
     if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) {
         return -1;
     }
-
     return 0;
 }
 
@@ -3937,20 +3897,40 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_client_state(SpiceServer *s)
 SPICE_GNUC_VISIBLE int spice_server_migrate_end(SpiceServer *s, int completed)
 {
     SpiceMigrateInterface *sif;
+    int ret = 0;
+
+    red_printf("");
+
     ASSERT(migration_interface);
     ASSERT(reds == s);
-    reds_mig_finished(completed);
+
     sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
+    if (!reds->expect_migrate && reds->num_clients) {
+        red_printf("spice_server_migrate_info was not called, disconnecting clients");
+        reds_disconnect();
+        ret = -1;
+        goto complete;
+    }
+
+    reds->expect_migrate = FALSE;
+    reds_mig_finished(completed);
+    ret = 0;
+complete:
     if (sif->migrate_end_complete) {
         sif->migrate_end_complete(migration_interface);
     }
-    return 0;
+    return ret;
 }
 
 /* interface for switch-host migration */
 SPICE_GNUC_VISIBLE int spice_server_migrate_switch(SpiceServer *s)
 {
     ASSERT(reds == s);
+    red_printf("");
+    if (!reds->num_clients) {
+       return 0;
+    }
+    reds->expect_migrate = FALSE;
     reds_mig_switch();
     return 0;
 }
diff --git a/server/reds.h b/server/reds.h
index 13fec42..9feb9ab 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -133,8 +133,6 @@ void reds_client_disconnect(RedClient *client);
 typedef struct MainMigrateData MainMigrateData;
 void reds_marshall_migrate_data_item(SpiceMarshaller *m, MainMigrateData *data);
 void reds_fill_channels(SpiceMsgChannels *channels_info);
-void reds_fill_mig_switch(SpiceMsgMainMigrationSwitchHost *migrate);
-void reds_mig_release(void);
 int reds_num_of_channels(void);
 int reds_num_of_clients(void);
 #ifdef RED_STATISTICS
commit fdb464bb052cab4a822a9bdc0835e33e0b90857f
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Sep 18 11:10:44 2011 +0300

    spice.proto: add SPICE_MSG_MAIN_MIGRATE_END & SPICE_MSGC_MAIN_MIGRATE_END
    
    (cherry picked from commit cfbd07710562e522179ae5a7085a789489a821bb branch 0.8)

diff --git a/spice.proto b/spice.proto
index 78c1fad..266e03a 100644
--- a/spice.proto
+++ b/spice.proto
@@ -219,6 +219,8 @@ channel MainChannel : BaseChannel {
       uint8 *cert_subject_data[cert_subject_size] @zero_terminated  @marshall;
     } @ctype(SpiceMsgMainMigrationSwitchHost) migrate_switch_host;
 
+    Empty migrate_end;
+
  client:
     message {
 	uint64 cache_size;
@@ -243,6 +245,8 @@ channel MainChannel : BaseChannel {
     message {
         uint32 num_tokens;
     } @ctype(SpiceMsgcMainAgentTokens) agent_token;
+
+    Empty migrate_end;
 };
 
 enum8 clip_type {
commit 70d1161430fd48c46a75d0742cb30315e11bc043
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Sep 18 10:31:38 2011 +0300

    server,proto: tell the clients to connect to the migration target before migraton starts
    
    (1) send SPICE_MSG_MAIN_MIGRATE_BEGIN upon spice_server_migrate_connect
        (to all the clients that support it)
    (2) wait for SPICE_MSGC_MAIN_MIGRATE_(CONNECTED|CONNECT_ERROR) from all the relevant clients,
        or a timeout, in order to complete client_migrate_info monitor command
    (cherry picked from commit 5560c56ef05c74da5e0e0825dc1f134019593cad branch 0.8;
     Was modified to support the separation of main channel from reds, and multiple clients)
    
    Conflicts:
    
    	server/reds.c

diff --git a/common/messages.h b/common/messages.h
index 8151dc0..a54190f 100644
--- a/common/messages.h
+++ b/common/messages.h
@@ -66,6 +66,8 @@ typedef struct SpiceMsgMainMigrationBegin {
     uint16_t pub_key_type;
     uint32_t pub_key_size;
     uint8_t *pub_key_data;
+    uint32_t cert_subject_size;
+    uint8_t *cert_subject_data;
 } SpiceMsgMainMigrationBegin;
 
 typedef struct SpiceMsgMainMigrationSwitchHost {
diff --git a/server/main_channel.c b/server/main_channel.c
index 49028b3..5b16b30 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -110,16 +110,6 @@ typedef struct NotifyPipeItem {
     int mess_len;
 } NotifyPipeItem;
 
-typedef struct MigrateBeginPipeItem {
-    PipeItem base;
-    int port;
-    int sport;
-    char *host;
-    uint16_t cert_pub_key_type;
-    uint32_t cert_pub_key_len;
-    uint8_t *cert_pub_key;
-} MigrateBeginPipeItem;
-
 typedef struct MultiMediaTimePipeItem {
     PipeItem base;
     int time;
@@ -137,6 +127,8 @@ struct MainChannelClient {
     SpiceTimer *ping_timer;
     int ping_interval;
 #endif
+    int mig_wait_connect;
+    int mig_connect_ok;
 };
 
 enum NetTestStage {
@@ -283,33 +275,6 @@ static PipeItem *main_notify_item_new(RedChannelClient *rcc, void *data, int num
     return &item->base;
 }
 
-typedef struct MigrateBeginItemInfo {
-    int port;
-    int sport;
-    char *host;
-    uint16_t cert_pub_key_type;
-    uint32_t cert_pub_key_len;
-    uint8_t *cert_pub_key;
-} MigrateBeginItemInfo;
-
-// TODO: MC: migration is not tested at all with multiclient.
-static PipeItem *main_migrate_begin_item_new(
-    RedChannelClient *rcc, void *data, int num)
-{
-    MigrateBeginPipeItem *item = spice_malloc(sizeof(MigrateBeginPipeItem));
-    MigrateBeginItemInfo *info = data;
-
-    red_channel_pipe_item_init(rcc->channel, &item->base,
-                               SPICE_MSG_MAIN_MIGRATE_BEGIN);
-    item->port = info->port;
-    item->sport = info->sport;
-    item->host = info->host;
-    item->cert_pub_key_type = info->cert_pub_key_type;
-    item->cert_pub_key_len = info->cert_pub_key_len;
-    item->cert_pub_key = info->cert_pub_key;
-    return &item->base;
-}
-
 static PipeItem *main_multi_media_time_item_new(
     RedChannelClient *rcc, void *data, int num)
 {
@@ -550,35 +515,23 @@ static void main_channel_marshall_notify(SpiceMarshaller *m, NotifyPipeItem *ite
     spice_marshaller_add(m, item->mess, item->mess_len + 1);
 }
 
-void main_channel_push_migrate_begin(MainChannel *main_chan, int port, int sport,
-    char *host, uint16_t cert_pub_key_type, uint32_t cert_pub_key_len,
-    uint8_t *cert_pub_key)
-{
-    MigrateBeginItemInfo info = {
-        .port =port,
-        .sport = sport,
-        .host = host,
-        .cert_pub_key_type = cert_pub_key_type,
-        .cert_pub_key_len = cert_pub_key_len,
-        .cert_pub_key = cert_pub_key,
-    };
-
-    red_channel_pipes_new_add_push(&main_chan->base,
-        main_migrate_begin_item_new, &info);
-}
-
-static void main_channel_marshall_migrate_begin(SpiceMarshaller *m,
-    MigrateBeginPipeItem *item)
+static void main_channel_marshall_migrate_begin(SpiceMarshaller *m, RedChannelClient *rcc)
 {
     SpiceMsgMainMigrationBegin migrate;
-
-    migrate.port = item->port;
-    migrate.sport = item->sport;
-    migrate.host_size = strlen(item->host) + 1;
-    migrate.host_data = (uint8_t *)item->host;
-    migrate.pub_key_type = item->cert_pub_key_type;
-    migrate.pub_key_size = item->cert_pub_key_len;
-    migrate.pub_key_data = item->cert_pub_key;
+    MainChannel *main_ch;
+
+    main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+    migrate.port = main_ch->mig_target.port;
+    migrate.sport = main_ch->mig_target.sport;
+    migrate.host_size = strlen(main_ch->mig_target.host) + 1;
+    migrate.host_data = (uint8_t *)main_ch->mig_target.host;
+    if (main_ch->mig_target.cert_subject) {
+        migrate.cert_subject_size = strlen(main_ch->mig_target.cert_subject) + 1;
+        migrate.cert_subject_data = (uint8_t *)main_ch->mig_target.cert_subject;
+    } else {
+        migrate.cert_subject_size = 0;
+        migrate.cert_subject_data = NULL;
+    }
     spice_marshall_msg_main_migrate_begin(m, &migrate);
 }
 
@@ -699,8 +652,7 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
             main_channel_marshall_migrate(m);
             break;
         case SPICE_MSG_MAIN_MIGRATE_BEGIN:
-            main_channel_marshall_migrate_begin(m,
-                SPICE_CONTAINEROF(base, MigrateBeginPipeItem, base));
+            main_channel_marshall_migrate_begin(m, rcc);
             break;
         case SPICE_MSG_MAIN_MULTI_MEDIA_TIME:
             main_channel_marshall_multi_media_time(m,
@@ -741,6 +693,26 @@ static void main_channel_release_pipe_item(RedChannelClient *rcc,
     free(base);
 }
 
+void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, int success)
+{
+    red_printf("client %p connected: %d", mcc->base.client, success);
+    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;
+        ASSERT(main_channel->num_clients_mig_wait);
+        if (!--main_channel->num_clients_mig_wait) {
+            reds_on_main_migrate_connected();
+        }
+    } else {
+        if (success) {
+            red_printf("client %p MIGRATE_CANCEL", mcc->base.client);
+            red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
+        }
+    }
+}
+
 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);
@@ -764,12 +736,10 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
         main_channel_push_channels(mcc);
         break;
     case SPICE_MSGC_MAIN_MIGRATE_CONNECTED:
-        red_printf("connected");
-        reds_on_main_migrate_connected();
+        main_channel_client_handle_migrate_connected(mcc, TRUE);
         break;
     case SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR:
-        red_printf("mig connect error");
-        reds_on_main_migrate_connect_error();
+        main_channel_client_handle_migrate_connected(mcc, FALSE);
         break;
     case SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST:
         reds_on_main_mouse_mode_request(message, size);
@@ -1001,6 +971,58 @@ MainChannel* main_channel_init(void)
                                         main_channel_handle_parsed,
                                         &channel_cbs);
     ASSERT(channel);
-
+    red_channel_set_cap(channel, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
     return (MainChannel *)channel;
 }
+
+RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc)
+{
+    ASSERT(mcc);
+    return &mcc->base;
+}
+
+int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target)
+{
+    RingItem *client_link;
+
+    ASSERT(mig_target);
+    free(main_channel->mig_target.host);
+    main_channel->mig_target.host = strdup(mig_target->host);
+    free(main_channel->mig_target.cert_subject);
+    if (mig_target->cert_subject) {
+        main_channel->mig_target.cert_subject = strdup(mig_target->cert_subject);
+    }
+    main_channel->mig_target.port = mig_target->port;
+    main_channel->mig_target.sport = mig_target->sport;
+
+    main_channel->num_clients_mig_wait = 0;
+
+    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)) {
+            red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_BEGIN);
+            mcc->mig_wait_connect = TRUE;
+            mcc->mig_connect_ok = FALSE;
+            main_channel->num_clients_mig_wait++;
+        }
+    }
+    return main_channel->num_clients_mig_wait;
+}
+
+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) {
+            red_printf("client %p cancel wait connect", mcc->base.client);
+            mcc->mig_wait_connect = FALSE;
+            mcc->mig_connect_ok = FALSE;
+        }
+    }
+    main_chan->num_clients_mig_wait = 0;
+}
diff --git a/server/main_channel.h b/server/main_channel.h
index 2ae05e8..f3702e7 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -57,6 +57,8 @@ struct MainMigrateData {
 typedef struct MainChannel {
     RedChannel base;
     uint8_t recv_buf[RECEIVE_BUF_SIZE];
+    RedsMigSpice mig_target; // TODO: add refs and release (afrer all clients completed migration in one way or the other?)
+    int num_clients_mig_wait;
 } MainChannel;
 
 
@@ -80,9 +82,6 @@ void main_channel_push_init(MainChannelClient *mcc, int connection_id, int displ
     int current_mouse_mode, int is_client_mouse_allowed, int multi_media_time,
     int ram_hint);
 void main_channel_push_notify(MainChannel *main_chan, uint8_t *mess, const int mess_len);
-// TODO: consider exporting RedsMigSpice from reds.c
-void main_channel_push_migrate_begin(MainChannel *main_chan, int port, int sport, char *host,
-    uint16_t cert_pub_key_type, uint32_t cert_pub_key_len, uint8_t *cert_pub_key);
 void main_channel_push_migrate(MainChannel *main_chan);
 void main_channel_push_migrate_switch(MainChannel *main_chan);
 void main_channel_push_migrate_cancel(MainChannel *main_chan);
@@ -94,5 +93,12 @@ uint32_t main_channel_client_get_link_id(MainChannelClient *mcc);
 int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
 uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
 int main_channel_is_connected(MainChannel *main_chan);
+RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
 
+/* semi seamless migration */
+
+/* returns the number of clients that we are waiting for their connection */
+int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target);
+void main_channel_migrate_cancel_wait(MainChannel *main_chan);
+void main_channel_migrate_complete(MainChannel *main_chan, int success);
 #endif
diff --git a/server/red_channel.h b/server/red_channel.h
index d044253..e30401c 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -450,6 +450,7 @@ struct RedClient {
     pthread_t thread_id;
 
     int disconnecting;
+
 };
 
 RedClient *red_client_new(void);
@@ -457,7 +458,6 @@ MainChannelClient *red_client_get_main(RedClient *client);
 // main should be set once before all the other channels are created
 void red_client_set_main(RedClient *client, MainChannelClient *mcc);
 
-
 void red_client_migrate(RedClient *client);
 // disconnects all the client's channels (should be called from the client's thread)
 void red_client_destroy(RedClient *client);
diff --git a/server/reds.c b/server/reds.c
index 0c201e7..bc93e4a 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -191,8 +191,6 @@ typedef struct RedsStatValue {
 
 #endif
 
-typedef struct RedsMigSpice RedsMigSpice;
-
 typedef struct RedsState {
     int listen_socket;
     int secure_listen_socket;
@@ -295,6 +293,7 @@ struct ChannelSecurityOptions {
     ChannelSecurityOptions *next;
 };
 
+static void migrate_timeout(void *opaque);
 
 static ChannelSecurityOptions *channels_security = NULL;
 static int default_channel_security =
@@ -538,6 +537,12 @@ static RedChannel *reds_find_channel(uint32_t type, uint32_t id)
 static void reds_mig_cleanup(void)
 {
     if (reds->mig_inprogress) {
+        if (reds->mig_wait_connect) {
+            SpiceMigrateInterface *sif;
+            ASSERT(migration_interface);
+            sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
+            sif->migrate_connect_complete(migration_interface);
+        }
         reds->mig_inprogress = FALSE;
         reds->mig_wait_connect = FALSE;
         reds->mig_wait_disconnect = FALSE;
@@ -1060,13 +1065,6 @@ void reds_on_main_migrate_connected(void)
     }
 }
 
-void reds_on_main_migrate_connect_error(void)
-{
-    if (reds->mig_wait_connect) {
-        reds_mig_cleanup();
-    }
-}
-
 void reds_on_main_mouse_mode_request(void *message, size_t size)
 {
     switch (((SpiceMsgcMainMouseModeRequest *)message)->mode) {
@@ -2976,18 +2974,6 @@ static void set_one_channel_security(int id, uint32_t security)
 
 #define REDS_SAVE_VERSION 1
 
-struct RedsMigSpice {
-    char pub_key[SPICE_TICKET_PUBKEY_BYTES];
-    uint32_t mig_key;
-    char *host;
-    char *cert_subject;
-    int port;
-    int sport;
-    uint16_t cert_pub_key_type;
-    uint32_t cert_pub_key_len;
-    uint8_t* cert_pub_key;
-};
-
 typedef struct RedsMigSpiceMessage {
     uint32_t connection_id;
 } RedsMigSpiceMessage;
@@ -3007,24 +2993,12 @@ void reds_mig_release(void)
     }
 }
 
-static void reds_mig_continue(void)
-{
-    RedsMigSpice *s = reds->mig_spice;
-
-    red_printf("");
-    main_channel_push_migrate_begin(reds->main_channel, s->port, s->sport,
-        s->host, s->cert_pub_key_type, s->cert_pub_key_len, s->cert_pub_key);
-
-    reds_mig_release();
-
-    reds->mig_wait_connect = TRUE;
-    core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
-}
-
 static void reds_mig_started(void)
 {
     red_printf("");
+    ASSERT(reds->mig_spice);
 
+    reds->mig_wait_connect = TRUE;
     reds->mig_inprogress = TRUE;
 
     if (reds->listen_watch != NULL) {
@@ -3034,24 +3008,7 @@ static void reds_mig_started(void)
     if (reds->secure_listen_watch != NULL) {
         core->watch_update_mask(reds->secure_listen_watch, 0);
     }
-
-    if (!reds_main_channel_connected()) {
-        red_printf("not connected to peer");
-        goto error;
-    }
-
-    if ((SPICE_VERSION_MAJOR == 1) && (reds->peer_minor_version < 2)) {
-        red_printf("minor version mismatch client %u server %u",
-                   reds->peer_minor_version, SPICE_VERSION_MINOR);
-        goto error;
-    }
-
-    reds_mig_continue();
-    return;
-
-error:
-    reds_mig_release();
-    reds_mig_disconnect();
+    core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT);
 }
 
 static void reds_mig_finished(int completed)
@@ -3127,11 +3084,16 @@ void reds_fill_mig_switch(SpiceMsgMainMigrationSwitchHost *migrate)
     }
 }
 
-static void migrate_timout(void *opaque)
+static void migrate_timeout(void *opaque)
 {
     red_printf("");
     ASSERT(reds->mig_wait_connect || reds->mig_wait_disconnect);
-    reds_mig_disconnect();
+    if (reds->mig_wait_connect) {
+        main_channel_migrate_cancel_wait(reds->main_channel);
+        reds_mig_cleanup();
+    } else {
+        reds_mig_disconnect();
+    }
 }
 
 uint32_t reds_get_mm_time(void)
@@ -3492,7 +3454,7 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
     main_dispatcher_init(core);
     ring_init(&reds->channels);
 
-    if (!(reds->mig_timer = core->timer_add(migrate_timout, NULL))) {
+    if (!(reds->mig_timer = core->timer_add(migrate_timeout, NULL))) {
         red_error("migration timer create failed");
     }
     if (!(reds->vdi_port_write_timer = core->timer_add(vdi_port_write_retry, NULL)))
@@ -3875,19 +3837,55 @@ SPICE_GNUC_VISIBLE int spice_server_set_agent_copypaste(SpiceServer *s, int enab
     return 0;
 }
 
+/* returns FALSE if info is invalid */
+static int reds_set_migration_dest_info(const char* dest,
+                                        int port, int secure_port,
+                                        const char* cert_subject)
+{
+    RedsMigSpice *spice_migration = NULL;
+
+    reds_mig_release();
+    if ((port == -1 && secure_port == -1) || !dest) {
+        return FALSE;
+    }
+
+    spice_migration = spice_new0(RedsMigSpice, 1);
+    spice_migration->port = port;
+    spice_migration->sport = secure_port;
+    spice_migration->host = strdup(dest);
+    if (cert_subject) {
+        spice_migration->cert_subject = strdup(cert_subject);
+    }
+
+    reds->mig_spice = spice_migration;
+
+    return TRUE;
+}
+
 /* semi-seamless client migration */
 SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char* dest,
                                                     int port, int secure_port,
                                                     const char* cert_subject)
 {
     SpiceMigrateInterface *sif;
+
     red_printf("");
     ASSERT(migration_interface);
     ASSERT(reds == s);
 
-    red_printf("not implemented yet");
     sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
-    sif->migrate_connect_complete(migration_interface);
+
+    if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) {
+        sif->migrate_connect_complete(migration_interface);
+        return -1;
+    }
+
+    if (main_channel_migrate_connect(reds->main_channel, reds->mig_spice)) {
+        reds->mig_wait_connect = TRUE;
+        reds_mig_started();
+    } else {
+        sif->migrate_connect_complete(migration_interface);
+    }
 
     return 0;
 }
@@ -3896,27 +3894,16 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_info(SpiceServer *s, const char* des
                                           int port, int secure_port,
                                           const char* cert_subject)
 {
-    RedsMigSpice *spice_migration = NULL;
-
+    ASSERT(!migration_interface);
     ASSERT(reds == s);
 
-    if ((port == -1 && secure_port == -1) || !dest)
+    if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) {
         return -1;
-
-    spice_migration = spice_new0(RedsMigSpice, 1);
-    spice_migration->port = port;
-    spice_migration->sport = secure_port;
-    spice_migration->host = strdup(dest);
-    if (cert_subject) {
-        spice_migration->cert_subject = strdup(cert_subject);
     }
 
-    reds_mig_release();
-    reds->mig_spice = spice_migration;
     return 0;
 }
 
-/* interface for seamless migration */
 SPICE_GNUC_VISIBLE int spice_server_migrate_start(SpiceServer *s)
 {
     ASSERT(reds == s);
diff --git a/server/reds.h b/server/reds.h
index 7720b30..13fec42 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -99,6 +99,13 @@ struct SpiceMigrateState {
     int dummy;
 };
 
+typedef struct RedsMigSpice {
+    char *host;
+    char *cert_subject;
+    int port;
+    int sport;
+} RedsMigSpice;
+
 ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte);
 ssize_t reds_stream_write(RedsStream *s, const void *buf, size_t nbyte);
 ssize_t reds_stream_writev(RedsStream *s, const struct iovec *iov, int iovcnt);
@@ -134,11 +141,12 @@ int reds_num_of_clients(void);
 void reds_update_stat_value(uint32_t value);
 #endif
 
-// callbacks from main channel messages
+/* callbacks from main channel messages */
+
 void reds_on_main_agent_start(void);
 void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size);
-void reds_on_main_migrate_connected(void);
-void reds_on_main_migrate_connect_error(void);
+void reds_on_main_migrate_connected(void); //should be called when all the clients
+                                           // are connected to the target
 void reds_on_main_receive_migrate_data(MainMigrateData *data, uint8_t *end);
 void reds_on_main_mouse_mode_request(void *message, size_t size);
 
diff --git a/spice.proto b/spice.proto
index abf3ec3..78c1fad 100644
--- a/spice.proto
+++ b/spice.proto
@@ -167,9 +167,8 @@ channel MainChannel : BaseChannel {
 	uint16 sport;
 	uint32 host_size;
 	uint8 *host_data[host_size] @zero_terminated @marshall @nonnull;
-	pubkey_type pub_key_type;
-	uint32 pub_key_size;
-	uint8 *pub_key_data[pub_key_size] @zero_terminated  @marshall @nonnull;
+	uint32 cert_subject_size;
+	uint8 *cert_subject_data[cert_subject_size] @zero_terminated @marshall;
     } @ctype(SpiceMsgMainMigrationBegin) migrate_begin = 101;
 
     Empty migrate_cancel;
commit fc3aa53211a85387df52befcaa8e8f86f7e811f2
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Mon Oct 3 11:53:24 2011 +0200

    configure: spice-protocol >= 0.9.1 (semi-seamless migration protocol)
    (cherry picked from commit 55ccc022ec9829523ebe36fdf0ec7c593ce76c22 branch 0.8)
    
    Conflicts:
    
    	configure.ac

diff --git a/configure.ac b/configure.ac
index 2afc559..203e82f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -132,7 +132,7 @@ AM_CONDITIONAL(SUPPORT_CLIENT, test "x$enable_client" = "xyes")
 dnl =========================================================================
 dnl Check deps
 
-PKG_CHECK_MODULES(PROTOCOL, spice-protocol >= 0.9.0)
+PKG_CHECK_MODULES(PROTOCOL, spice-protocol >= 0.9.1)
 AC_SUBST(PROTOCOL_CFLAGS)
 
 AC_CHECK_LIBM
commit 38a96b408494a94630232ab574a8c54c05112f07
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Mon Oct 3 11:47:33 2011 +0200

    server: handle migration interface addition
    
    (cherry picked from commit 3ac0075cdac8fa42de47a7882022795e96cb1fee branch 0.8)
    
    Conflicts:
    
    	server/reds.h

diff --git a/server/reds.c b/server/reds.c
index 7e1b090..0c201e7 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -74,6 +74,7 @@
 
 SpiceCoreInterface *core = NULL;
 static SpiceCharDeviceInstance *vdagent = NULL;
+static SpiceMigrateInstance *migration_interface = NULL;
 
 /* Debugging only variable: allow multiple client connections to the spice
  * server */
@@ -3365,6 +3366,20 @@ SPICE_GNUC_VISIBLE int spice_server_add_interface(SpiceServer *s,
         red_printf("unsupported net wire interface");
         return -1;
 #endif
+    } else if (strcmp(interface->type, SPICE_INTERFACE_MIGRATION) == 0) {
+        red_printf("SPICE_INTERFACE_MIGRATION");
+        if (migration_interface) {
+            red_printf("already have migration");
+            return -1;
+        }
+
+        if (interface->major_version != SPICE_INTERFACE_MIGRATION_MAJOR ||
+            interface->minor_version > SPICE_INTERFACE_MIGRATION_MINOR) {
+            red_printf("unsupported migration interface");
+            return -1;
+        }
+        migration_interface = SPICE_CONTAINEROF(sin, SpiceMigrateInstance, base);
+        migration_interface->st = spice_new0(SpiceMigrateState, 1);
     }
 
     return 0;
@@ -3865,7 +3880,15 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char*
                                                     int port, int secure_port,
                                                     const char* cert_subject)
 {
+    SpiceMigrateInterface *sif;
+    red_printf("");
+    ASSERT(migration_interface);
+    ASSERT(reds == s);
+
     red_printf("not implemented yet");
+    sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
+    sif->migrate_connect_complete(migration_interface);
+
     return 0;
 }
 
@@ -3926,8 +3949,14 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_client_state(SpiceServer *s)
 
 SPICE_GNUC_VISIBLE int spice_server_migrate_end(SpiceServer *s, int completed)
 {
+    SpiceMigrateInterface *sif;
+    ASSERT(migration_interface);
     ASSERT(reds == s);
     reds_mig_finished(completed);
+    sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base);
+    if (sif->migrate_end_complete) {
+        sif->migrate_end_complete(migration_interface);
+    }
     return 0;
 }
 
diff --git a/server/reds.h b/server/reds.h
index 188bed5..7720b30 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -95,6 +95,10 @@ struct SpiceNetWireState {
     struct TunnelWorker *worker;
 };
 
+struct SpiceMigrateState {
+    int dummy;
+};
+
 ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte);
 ssize_t reds_stream_write(RedsStream *s, const void *buf, size_t nbyte);
 ssize_t reds_stream_writev(RedsStream *s, const struct iovec *iov, int iovcnt);
commit cd402151def11c5748065de28d124747df09447b
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Mon Oct 3 11:21:38 2011 +0200

    server/spice.h: semi-seamless migration interface, RHBZ #738266
    
    semi-seamless migration details:
    
    migration source side
    ---------------------
    (1) spice_server_migrate_connect (*): tell client to link
        to the target side - send SPICE_MSG_MAIN_MIGRATE_BEGIN.
        This should be called upon client_migrate_info cmd.
        client_migrate_info is asynchronous.
    (2) Complete spice_server_migrate_connect only when the client has been connected
        to the target - wait for SPICE_MSGC_MAIN_MIGRATE_(CONNECTED|CONNECT_ERROR) or a timeout.
    (3) spice_server_migrate_end: tell client migration it can switch to the target - send
        SPICE_MSG_MAIN_MIGRATE_END.
    (4) client cleans up all data related to the connection to the source and switches to the target.
        It sends SPICE_MSGC_MAIN_MIGRATE_END.
    
    migration target side
    ---------------------
    (1) the server identifies itself as a migraiton target since the client is linked with (connection_id != 0)
    (2) server doesn't start the channels' logic (channel->link) till it receives SPICE_MSGC_MAIN_MIGRATE_END
        from the client.
    
    *   After migration starts, the target qemu is blocked and cannot accept new spice client
        connections. Thus, we trigger the connection to the target upon client_migrate_info
        command.
    (cherry picked from commit 6e56bea67c5648b0c81990171d4bc0cf1a402043 branch 0.8)
    
    Conflicts:
    
    	server/spice.h

diff --git a/server/reds.c b/server/reds.c
index 31e255f..7e1b090 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -3860,6 +3860,15 @@ SPICE_GNUC_VISIBLE int spice_server_set_agent_copypaste(SpiceServer *s, int enab
     return 0;
 }
 
+/* semi-seamless client migration */
+SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char* dest,
+                                                    int port, int secure_port,
+                                                    const char* cert_subject)
+{
+    red_printf("not implemented yet");
+    return 0;
+}
+
 SPICE_GNUC_VISIBLE int spice_server_migrate_info(SpiceServer *s, const char* dest,
                                           int port, int secure_port,
                                           const char* cert_subject)
diff --git a/server/spice-experimental.h b/server/spice-experimental.h
index 482ac44..6997aa0 100644
--- a/server/spice-experimental.h
+++ b/server/spice-experimental.h
@@ -29,16 +29,13 @@ void spice_server_net_wire_recv_packet(SpiceNetWireInstance *sin,
                                        const uint8_t *pkt, int len);
 
 /* spice seamless client migration (broken) */
-
 enum {
     SPICE_MIGRATE_CLIENT_NONE = 1,
     SPICE_MIGRATE_CLIENT_WAITING,
     SPICE_MIGRATE_CLIENT_READY,
 };
 
-int spice_server_migrate_start(SpiceServer *s);
 int spice_server_migrate_client_state(SpiceServer *s);
-int spice_server_migrate_end(SpiceServer *s, int completed);
 
 #endif // __SPICE_EXPERIMENTAL_H__
 
diff --git a/server/spice.h b/server/spice.h
index 74f9fdb..974975a 100644
--- a/server/spice.h
+++ b/server/spice.h
@@ -475,8 +475,26 @@ int spice_server_set_agent_copypaste(SpiceServer *s, int enable);
 int spice_server_get_sock_info(SpiceServer *s, struct sockaddr *sa, socklen_t *salen);
 int spice_server_get_peer_info(SpiceServer *s, struct sockaddr *sa, socklen_t *salen);
 
-/* spice switch-host client migration */
+/* migration interface */
+#define SPICE_INTERFACE_MIGRATION "migration"
+#define SPICE_INTERFACE_MIGRATION_MAJOR 1
+#define SPICE_INTERFACE_MIGRATION_MINOR 1
+typedef struct SpiceMigrateInterface SpiceMigrateInterface;
+typedef struct SpiceMigrateInstance SpiceMigrateInstance;
+typedef struct SpiceMigrateState SpiceMigrateState;
+
+struct SpiceMigrateInterface {
+    SpiceBaseInterface base;
+    void (*migrate_connect_complete)(SpiceMigrateInstance *sin);
+    void (*migrate_end_complete)(SpiceMigrateInstance *sin);
+};
+
+struct SpiceMigrateInstance {
+    SpiceBaseInstance base;
+    SpiceMigrateState *st;
+};
 
+/* spice switch-host client migration */
 int spice_server_migrate_info(SpiceServer *s, const char* dest,
                               int port, int secure_port,
                               const char* cert_subject);
@@ -485,4 +503,11 @@ int spice_server_migrate_switch(SpiceServer *s);
 /* server status */
 int spice_server_get_num_clients(SpiceServer *s);
 
+/* spice (semi-)seamless client migration */
+int spice_server_migrate_connect(SpiceServer *s, const char* dest,
+                                 int port, int secure_port,
+                                 const char* cert_subject);
+int spice_server_migrate_start(SpiceServer *s);
+int spice_server_migrate_end(SpiceServer *s, int completed);
+
 #endif
commit 0bf518cd3daad526a16468e82c1d6c715535e9ba
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Wed Oct 5 09:05:13 2011 +0200

    server: set & test channel capabilities in red_channel
    
    The code for setting and testing channel capabilities was
    unnecessarily duplicated. Now it is in red_channel.
    RedsChannel was dropped from Reds; It was used only for holding
    the channels common capabilities, which are now held in RedChannel.

diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index 24fc621..c8b42e3 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -496,7 +496,9 @@ static void inputs_connect(RedChannel *channel, RedClient *client,
     icc = (InputsChannelClient*)red_channel_client_create(sizeof(InputsChannelClient),
                                                           channel,
                                                           client,
-                                                          stream);
+                                                          stream,
+                                                          num_common_caps, common_caps,
+                                                          num_caps, caps);
     icc->motion_count = 0;
     inputs_pipe_add_init(&icc->base);
 }
diff --git a/server/main_channel.c b/server/main_channel.c
index 43c0f3f..49028b3 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -39,17 +39,13 @@
 #include "server/demarshallers.h"
 #include "common/ring.h"
 #include "common/messages.h"
-#include "reds.h"
 #include "main_channel.h"
+#include "reds.h"
 #include "red_channel.h"
 #include "generated_marshallers.h"
 
 #define ZERO_BUF_SIZE 4096
 
-// approximate max receive message size for main channel
-#define RECEIVE_BUF_SIZE \
-    (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
-
 #define REDS_MAX_SEND_IOVEC 100
 
 #define NET_TEST_WARMUP_BYTES 0
@@ -143,11 +139,6 @@ struct MainChannelClient {
 #endif
 };
 
-struct MainChannel {
-    RedChannel base;
-    uint8_t recv_buf[RECEIVE_BUF_SIZE];
-};
-
 enum NetTestStage {
     NET_TEST_STAGE_INVALID,
     NET_TEST_STAGE_WARMUP,
@@ -911,12 +902,18 @@ uint32_t main_channel_client_get_link_id(MainChannelClient *mcc)
     return mcc->connection_id;
 }
 
-MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client,
-                                              RedsStream *stream, uint32_t connection_id)
+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);
+                                                                           client, stream,
+                                                                           num_common_caps,
+                                                                           common_caps,
+                                                                           num_caps,
+                                                                           caps);
 
     mcc->connection_id = connection_id;
     mcc->bitrate_per_sec = ~0;
@@ -942,7 +939,9 @@ MainChannelClient *main_channel_link(MainChannel *channel, RedClient *client,
     // into usage somewhere (not an issue until we return migration to it's
     // former glory)
     red_printf("add main channel client");
-    mcc = main_channel_client_create(channel, client, stream, connection_id);
+    mcc = main_channel_client_create(channel, client, stream, connection_id,
+                                     num_common_caps, common_caps,
+                                     num_caps, caps);
     return mcc;
 }
 
diff --git a/server/main_channel.h b/server/main_channel.h
index 713bcd0..2ae05e8 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -45,7 +45,20 @@ struct MainMigrateData {
     uint32_t write_queue_size;
 };
 
-typedef struct MainChannel MainChannel;
+// 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.
+#define REDS_AGENT_WINDOW_SIZE 10
+#define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
+
+// approximate max receive message size for main channel
+#define RECEIVE_BUF_SIZE \
+    (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
+
+typedef struct MainChannel {
+    RedChannel base;
+    uint8_t recv_buf[RECEIVE_BUF_SIZE];
+} MainChannel;
+
 
 MainChannel *main_channel_init(void);
 RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t link_id);
@@ -82,9 +95,4 @@ int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
 uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
 int main_channel_is_connected(MainChannel *main_chan);
 
-// 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.
-#define REDS_AGENT_WINDOW_SIZE 10
-#define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
-
 #endif
diff --git a/server/red_channel.c b/server/red_channel.c
index 8993cc3..51415cb 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -358,11 +358,43 @@ static void red_channel_add_client(RedChannel *channel, RedChannelClient *rcc)
     channel->clients_num++;
 }
 
-RedChannelClient *red_channel_client_create(
-    int size,
-    RedChannel *channel,
-    RedClient  *client,
-    RedsStream *stream)
+static void red_channel_client_set_remote_caps(RedChannelClient* rcc,
+                                               int num_common_caps, uint32_t *common_caps,
+                                               int num_caps, uint32_t *caps)
+{
+    rcc->remote_caps.num_common_caps = num_common_caps;
+    rcc->remote_caps.common_caps = spice_memdup(common_caps, num_common_caps * sizeof(uint32_t));
+
+    rcc->remote_caps.num_caps = num_caps;
+    rcc->remote_caps.caps = spice_memdup(caps, num_caps * sizeof(uint32_t));
+}
+
+static void red_channel_client_destroy_remote_caps(RedChannelClient* rcc)
+{
+    rcc->remote_caps.num_common_caps = 0;
+    free(rcc->remote_caps.common_caps);
+    rcc->remote_caps.num_caps = 0;
+    free(rcc->remote_caps.caps);
+}
+
+int red_channel_client_test_remote_common_cap(RedChannelClient *rcc, uint32_t cap)
+{
+    return test_capabilty(rcc->remote_caps.common_caps,
+                          rcc->remote_caps.num_common_caps,
+                          cap);
+}
+
+int red_channel_client_test_remote_cap(RedChannelClient *rcc, uint32_t cap)
+{
+    return test_capabilty(rcc->remote_caps.caps,
+                          rcc->remote_caps.num_caps,
+                          cap);
+}
+
+RedChannelClient *red_channel_client_create(int size, RedChannel *channel, RedClient  *client,
+                                            RedsStream *stream,
+                                            int num_common_caps, uint32_t *common_caps,
+                                            int num_caps, uint32_t *caps)
 {
     RedChannelClient *rcc;
 
@@ -384,6 +416,9 @@ RedChannelClient *red_channel_client_create(
     rcc->outgoing.cb = &channel->outgoing_cb;
     rcc->outgoing.pos = 0;
     rcc->outgoing.size = 0;
+
+    red_channel_client_set_remote_caps(rcc, num_common_caps, common_caps, num_caps, caps);
+
     if (!channel->channel_cbs.config_socket(rcc)) {
         goto error;
     }
@@ -564,10 +599,36 @@ void red_channel_register_client_cbs(RedChannel *channel, ClientCbs *client_cbs)
     }
 }
 
-void red_channel_set_caps(RedChannel *channel, int num_caps, uint32_t *caps)
+int test_capabilty(uint32_t *caps, int num_caps, uint32_t cap)
 {
-    channel->num_caps = num_caps;
-    channel->caps = caps;
+    uint32_t index = cap / 32;
+    if (num_caps < index + 1) {
+        return FALSE;
+    }
+
+    return (caps[index] & (1 << (cap % 32))) != 0;
+}
+
+static void add_capability(uint32_t **caps, int *num_caps, uint32_t cap)
+{
+    int nbefore, n;
+
+    nbefore = *num_caps;
+    n = cap / 32;
+    *num_caps = MAX(*num_caps, n + 1);
+    *caps = spice_renew(uint32_t, *caps, *num_caps);
+    memset(*caps + nbefore, 0, (*num_caps - nbefore) * sizeof(uint32_t));
+    (*caps)[n] |= (1 << (cap % 32));
+}
+
+void red_channel_set_common_cap(RedChannel *channel, uint32_t cap)
+{
+    add_capability(&channel->local_caps.common_caps, &channel->local_caps.num_common_caps, cap);
+}
+
+void red_channel_set_cap(RedChannel *channel, uint32_t cap)
+{
+    add_capability(&channel->local_caps.caps, &channel->local_caps.num_caps, cap);
 }
 
 void red_channel_set_data(RedChannel *channel, void *data)
@@ -585,6 +646,7 @@ void red_channel_client_destroy(RedChannelClient *rcc)
     if (rcc->send_data.marshaller) {
         spice_marshaller_destroy(rcc->send_data.marshaller);
     }
+    red_channel_client_destroy_remote_caps(rcc);
     free(rcc);
 }
 
@@ -600,9 +662,15 @@ void red_channel_destroy(RedChannel *channel)
         red_channel_client_destroy(
             SPICE_CONTAINEROF(link, RedChannelClient, channel_link));
     }
-    if (channel->caps) {
-        free(channel->caps);
+
+    if (channel->local_caps.num_common_caps) {
+        free(channel->local_caps.common_caps);
+    }
+
+    if (channel->local_caps.num_caps) {
+        free(channel->local_caps.caps);
     }
+
     free(channel);
 }
 
@@ -979,7 +1047,9 @@ void red_channel_disconnect(RedChannel *channel)
 
 RedChannelClient *red_channel_client_create_dummy(int size,
                                                   RedChannel *channel,
-                                                  RedClient  *client)
+                                                  RedClient  *client,
+                                                  int num_common_caps, uint32_t *common_caps,
+                                                  int num_caps, uint32_t *caps)
 {
     RedChannelClient *rcc;
 
@@ -987,6 +1057,7 @@ RedChannelClient *red_channel_client_create_dummy(int size,
     rcc = spice_malloc0(size);
     rcc->client = client;
     rcc->channel = channel;
+    red_channel_client_set_remote_caps(rcc, num_common_caps, common_caps, num_caps, caps);
     red_channel_add_client(channel, rcc);
     return rcc;
 }
@@ -994,6 +1065,7 @@ RedChannelClient *red_channel_client_create_dummy(int size,
 void red_channel_client_destroy_dummy(RedChannelClient *rcc)
 {
     red_channel_remove_client(rcc);
+    red_channel_client_destroy_remote_caps(rcc);
     free(rcc);
 }
 
diff --git a/server/red_channel.h b/server/red_channel.h
index 2ebb6b6..d044253 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -143,7 +143,7 @@ typedef uint64_t (*channel_handle_migrate_data_get_serial_proc)(RedChannelClient
 
 
 typedef void (*channel_client_connect_proc)(RedChannel *channel, RedClient *client, RedsStream *stream,
-                                            int migration, int num_common_caps, uint32_t *common_caps,
+                                            int migration, int num_common_cap, uint32_t *common_caps,
                                             int num_caps, uint32_t *caps);
 typedef void (*channel_client_disconnect_proc)(RedChannelClient *base);
 typedef void (*channel_client_migrate_proc)(RedChannelClient *base);
@@ -178,6 +178,15 @@ typedef struct {
     channel_client_migrate_proc migrate;
 } ClientCbs;
 
+typedef struct RedChannelCapabilities {
+    int num_common_caps;
+    uint32_t *common_caps;
+    int num_caps;
+    uint32_t *caps;
+} RedChannelCapabilities;
+
+int test_capabilty(uint32_t *caps, int num_caps, uint32_t cap);
+
 struct RedChannelClient {
     RingItem channel_link;
     RingItem client_link;
@@ -206,12 +215,16 @@ struct RedChannelClient {
     int id; // debugging purposes
     Ring pipe;
     uint32_t pipe_size;
+
+    RedChannelCapabilities remote_caps;
 };
 
 struct RedChannel {
     uint32_t type;
     uint32_t id;
 
+    RingItem link; // channels link for reds
+
     SpiceCoreInterface *core;
     int migrate;
     int handle_acks;
@@ -232,8 +245,7 @@ struct RedChannel {
     ChannelCbs channel_cbs;
     ClientCbs client_cbs;
 
-    int num_caps;
-    uint32_t *caps;
+    RedChannelCapabilities local_caps;
 
     void *data;
 
@@ -265,19 +277,23 @@ RedChannel *red_channel_create_parser(int size,
 
 void red_channel_register_client_cbs(RedChannel *channel, ClientCbs *client_cbs);
 // caps are freed when the channel is destroyed
-void red_channel_set_caps(RedChannel *channel, int num_caps, uint32_t *caps);
+void red_channel_set_common_cap(RedChannel *channel, uint32_t cap);
+void red_channel_set_cap(RedChannel *channel, uint32_t cap);
 void red_channel_set_data(RedChannel *channel, void *data);
 
 RedChannelClient *red_channel_client_create(int size, RedChannel *channel, RedClient *client,
-                                            RedsStream *stream);
-
+                                            RedsStream *stream,
+                                            int num_common_caps, uint32_t *common_caps,
+                                            int num_caps, uint32_t *caps);
 // TODO: tmp, for channels that don't use RedChannel yet (e.g., snd channel), but
 // do use the client callbacks. So the channel clients are not connected (the channel doesn't
 // have list of them, but they do have a link to the channel, and the client has a list of them)
 RedChannel *red_channel_create_dummy(int size, uint32_t type, uint32_t id);
 RedChannelClient *red_channel_client_create_dummy(int size,
                                                   RedChannel *channel,
-                                                  RedClient  *client);
+                                                  RedClient  *client,
+                                                  int num_common_caps, uint32_t *common_caps,
+                                                  int num_caps, uint32_t *caps);
 void red_channel_client_destroy_dummy(RedChannelClient *rcc);
 
 
@@ -294,6 +310,9 @@ int red_channel_client_is_connected(RedChannelClient *rcc);
 void red_channel_client_destroy(RedChannelClient *rcc);
 void red_channel_destroy(RedChannel *channel);
 
+int red_channel_client_test_remote_common_cap(RedChannelClient *rcc, uint32_t cap);
+int red_channel_client_test_remote_cap(RedChannelClient *rcc, uint32_t cap);
+
 /* shutdown is the only safe thing to do out of the client/channel
  * thread. It will not touch the rings, just shutdown the socket.
  * It should be followed by some way to gurantee a disconnection. */
diff --git a/server/red_tunnel_worker.c b/server/red_tunnel_worker.c
index 6c36fec..1e8267e 100644
--- a/server/red_tunnel_worker.c
+++ b/server/red_tunnel_worker.c
@@ -3445,7 +3445,8 @@ static void handle_tunnel_channel_link(RedChannel *channel, RedClient *client,
     }
 
     tcc = (TunnelChannelClient*)red_channel_client_create(sizeof(TunnelChannelClient),
-                                                          channel, client, stream);
+                                                          channel, client, stream,
+                                                          0, NULL, 0, NULL);
 
     tcc->worker = worker;
     tcc->worker->channel_client = tcc;
diff --git a/server/red_worker.c b/server/red_worker.c
index 6756af9..4337d16 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -9449,7 +9449,8 @@ static CommonChannelClient *common_channel_client_create(int size,
 {
     MainChannelClient *mcc = red_client_get_main(client);
     RedChannelClient *rcc =
-        red_channel_client_create(size, &common->base, client, stream);
+        red_channel_client_create(size, &common->base, client, stream,
+                                  0, NULL, 0, NULL);
     CommonChannelClient *common_cc = (CommonChannelClient*)rcc;
     common_cc->worker = common->worker;
 
diff --git a/server/reds.c b/server/reds.c
index 65c98ae..31e255f 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -192,14 +192,6 @@ typedef struct RedsStatValue {
 
 typedef struct RedsMigSpice RedsMigSpice;
 
-typedef struct RedsChannel {
-    struct RedsChannel *next;
-    RedChannel *base;
-
-    int num_common_caps;
-    uint32_t *common_caps;
-} RedsChannel;
-
 typedef struct RedsState {
     int listen_socket;
     int secure_listen_socket;
@@ -217,7 +209,7 @@ typedef struct RedsState {
     int mig_target;
     RedsMigSpice *mig_spice;
     int num_of_channels;
-    RedsChannel *channels;
+    Ring channels;
     int mouse_mode;
     int is_client_mouse_allowed;
     int dispatcher_allows_client_mouse;
@@ -302,7 +294,6 @@ struct ChannelSecurityOptions {
     ChannelSecurityOptions *next;
 };
 
-static void reds_dispose_channel(RedsChannel *channel);
 
 static ChannelSecurityOptions *channels_security = NULL;
 static int default_channel_security =
@@ -515,42 +506,32 @@ void reds_update_stat_value(uint32_t value)
 
 void reds_register_channel(RedChannel *channel)
 {
-    RedsChannel *reds_channel;
-
     ASSERT(reds);
-    // TODO: should channels be released upon some destructor?
-    reds_channel = spice_malloc0(sizeof(RedsChannel));
-    reds_channel->base = channel;
-    reds_channel->next = reds->channels;
-    reds->channels = reds_channel;
+    ring_add(&reds->channels, &channel->link);
     reds->num_of_channels++;
 }
 
 void reds_unregister_channel(RedChannel *channel)
 {
-    RedsChannel **now = &reds->channels;
-
-    while (*now) {
-        if ((*now)->base == channel) {
-            RedsChannel *free_channel = *now;
-            *now = free_channel->next;
-            reds_dispose_channel(free_channel);
-            free(free_channel);
-            reds->num_of_channels--;
-            return;
-        }
-        now = &(*now)->next;
+    if (ring_item_is_linked(&channel->link)) {
+        ring_remove(&channel->link);
+        reds->num_of_channels--;
+    } else {
+        red_printf("not found");
     }
-    red_printf("not found");
 }
 
-static RedsChannel *reds_find_channel(uint32_t type, uint32_t id)
+static RedChannel *reds_find_channel(uint32_t type, uint32_t id)
 {
-    RedsChannel *channel = reds->channels;
-    while (channel && !(channel->base->type == type && channel->base->id == id)) {
-        channel = channel->next;
+    RingItem *now;
+
+    RING_FOREACH(now, &reds->channels) {
+        RedChannel *channel = SPICE_CONTAINEROF(now, RedChannel, link);
+        if (channel->type == type && channel->id == id) {
+            return channel;
+        }
     }
-    return channel;
+    return NULL;
 }
 
 static void reds_mig_cleanup(void)
@@ -987,11 +968,11 @@ SPICE_GNUC_VISIBLE int spice_server_get_num_clients(SpiceServer *s)
 static int secondary_channels[] = {
     SPICE_CHANNEL_MAIN, SPICE_CHANNEL_DISPLAY, SPICE_CHANNEL_CURSOR, SPICE_CHANNEL_INPUTS};
 
-static int channel_is_secondary(RedsChannel *channel)
+static int channel_is_secondary(RedChannel *channel)
 {
     int i;
     for (i = 0 ; i < sizeof(secondary_channels)/sizeof(secondary_channels[0]); ++i) {
-        if (channel->base->type == secondary_channels[i]) {
+        if (channel->type == secondary_channels[i]) {
             return TRUE;
         }
     }
@@ -1000,21 +981,20 @@ static int channel_is_secondary(RedsChannel *channel)
 
 void reds_fill_channels(SpiceMsgChannels *channels_info)
 {
-    RedsChannel *channel;
-    int i;
+    RingItem *now;
     int used_channels = 0;
 
     channels_info->num_of_channels = reds->num_of_channels;
-    channel = reds->channels;
-    for (i = 0; i < reds->num_of_channels; i++, channel = channel->next) {
-        ASSERT(channel);
+    RING_FOREACH(now, &reds->channels) {
+        RedChannel *channel = SPICE_CONTAINEROF(now, RedChannel, link);
         if (reds->num_clients > 1 && !channel_is_secondary(channel)) {
             continue;
         }
-        channels_info->channels[used_channels].type = channel->base->type;
-        channels_info->channels[used_channels].id = channel->base->id;
+        channels_info->channels[used_channels].type = channel->type;
+        channels_info->channels[used_channels].id = channel->id;
         used_channels++;
     }
+
     channels_info->num_of_channels = used_channels;
     if (used_channels != reds->num_of_channels) {
         red_printf("sent %d out of %d", used_channels, reds->num_of_channels);
@@ -1388,46 +1368,22 @@ static int sync_write(RedsStream *stream, const void *in_buf, size_t n)
     return TRUE;
 }
 
-static void reds_channel_set_common_caps(RedsChannel *channel, int cap, int active)
-{
-    int nbefore, n;
-
-    nbefore = channel->num_common_caps;
-    n = cap / 32;
-    channel->num_common_caps = MAX(channel->num_common_caps, n + 1);
-    channel->common_caps = spice_renew(uint32_t, channel->common_caps, channel->num_common_caps);
-    memset(channel->common_caps + nbefore, 0,
-           (channel->num_common_caps - nbefore) * sizeof(uint32_t));
-    if (active) {
-        channel->common_caps[n] |= (1 << cap);
-    } else {
-        channel->common_caps[n] &= ~(1 << cap);
-    }
-}
-
-static void reds_channel_init_auth_caps(RedsChannel *channel)
+static void reds_channel_init_auth_caps(RedChannel *channel)
 {
     if (sasl_enabled) {
-        reds_channel_set_common_caps(channel, SPICE_COMMON_CAP_AUTH_SASL, TRUE);
+        red_channel_set_common_cap(channel, SPICE_COMMON_CAP_AUTH_SASL);
     } else {
-        reds_channel_set_common_caps(channel, SPICE_COMMON_CAP_AUTH_SPICE, TRUE);
+        red_channel_set_common_cap(channel, SPICE_COMMON_CAP_AUTH_SPICE);
     }
-    reds_channel_set_common_caps(channel, SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION, TRUE);
-}
-
-static void reds_dispose_channel(RedsChannel *channel)
-{
-    free(channel->common_caps);
-    channel->common_caps = NULL;
-    channel->num_common_caps = 0;
+    red_channel_set_common_cap(channel, SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION);
 }
 
 static int reds_send_link_ack(RedLinkInfo *link)
 {
     SpiceLinkHeader header;
     SpiceLinkReply ack;
-    RedsChannel common_caps = { 0, };
-    RedsChannel *channel;
+    RedChannel *channel;
+    RedChannelCapabilities *channel_caps;
     BUF_MEM *bmBuf;
     BIO *bio;
     int ret = FALSE;
@@ -1441,13 +1397,16 @@ static int reds_send_link_ack(RedLinkInfo *link)
 
     channel = reds_find_channel(link->link_mess->channel_type, 0);
     if (!channel) {
-        channel = &common_caps;
+        ASSERT(link->link_mess->channel_type == SPICE_CHANNEL_MAIN);
+        ASSERT(reds->main_channel);
+        channel = &reds->main_channel->base;
     }
 
     reds_channel_init_auth_caps(channel); /* make sure common caps are set */
 
-    ack.num_common_caps = channel->num_common_caps;
-    ack.num_channel_caps = channel->base ? channel->base->num_caps : 0;
+    channel_caps = &channel->local_caps;
+    ack.num_common_caps = channel_caps->num_common_caps;
+    ack.num_channel_caps = channel_caps->num_caps;
     header.size += (ack.num_common_caps + ack.num_channel_caps) * sizeof(uint32_t);
     ack.caps_offset = sizeof(SpiceLinkReply);
 
@@ -1473,17 +1432,14 @@ static int reds_send_link_ack(RedLinkInfo *link)
         goto end;
     if (!sync_write(link->stream, &ack, sizeof(ack)))
         goto end;
-    if (!sync_write(link->stream, channel->common_caps, channel->num_common_caps * sizeof(uint32_t)))
+    if (!sync_write(link->stream, channel_caps->common_caps, channel_caps->num_common_caps * sizeof(uint32_t)))
+        goto end;
+    if (!sync_write(link->stream, channel_caps->caps, channel_caps->num_caps * sizeof(uint32_t)))
         goto end;
-    if (channel->base) {
-        if (!sync_write(link->stream, channel->base->caps, channel->base->num_caps * sizeof(uint32_t)))
-            goto end;
-    }
 
     ret = TRUE;
 
 end:
-    reds_dispose_channel(&common_caps);
     BIO_free(bio);
     return ret;
 }
@@ -1542,6 +1498,8 @@ static void reds_handle_main_link(RedLinkInfo *link)
     MainChannelClient *mcc;
 
     red_printf("");
+    ASSERT(reds->main_channel);
+
     link_mess = link->link_mess;
     if (!reds->allow_multiple_clients) {
         reds_disconnect();
@@ -1576,10 +1534,6 @@ static void reds_handle_main_link(RedLinkInfo *link)
     link->link_mess = NULL;
     reds_link_free(link);
     caps = (uint32_t *)((uint8_t *)link_mess + link_mess->caps_offset);
-    if (!reds->main_channel) {
-        reds->main_channel = main_channel_init();
-        ASSERT(reds->main_channel);
-    }
     client = red_client_new();
     ring_add(&reds->clients, &client->link);
     reds->num_clients++;
@@ -1645,7 +1599,7 @@ static void openssl_init(RedLinkInfo *link)
 
 static void reds_handle_other_links(RedLinkInfo *link)
 {
-    RedsChannel *channel;
+    RedChannel *channel;
     RedClient *client = NULL;
     RedsStream *stream;
     SpiceLinkMess *link_mess;
@@ -1687,12 +1641,12 @@ static void reds_handle_other_links(RedLinkInfo *link)
     link->link_mess = NULL;
     reds_link_free(link);
     caps = (uint32_t *)((uint8_t *)link_mess + link_mess->caps_offset);
-    channel->base->client_cbs.connect(channel->base, client, stream, reds->mig_target,
-                                      link_mess->num_common_caps,
-                                      link_mess->num_common_caps ? caps : NULL,
-                                      link_mess->num_channel_caps,
-                                      link_mess->num_channel_caps ?
-                                          caps + link_mess->num_common_caps : NULL);
+    channel->client_cbs.connect(channel, client, stream, reds->mig_target,
+                                link_mess->num_common_caps,
+                                link_mess->num_common_caps ? caps : NULL,
+                                link_mess->num_channel_caps,
+                                link_mess->num_channel_caps ?
+                                caps + link_mess->num_common_caps : NULL);
     free(link_mess);
 }
 
@@ -2459,8 +2413,8 @@ static void reds_handle_read_link_done(void *opaque)
         return;
     }
 
-    auth_selection = link_mess->num_common_caps > 0 &&
-        (caps[0] & (1 << SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION));;
+    auth_selection = test_capabilty(caps, link_mess->num_common_caps,
+                                    SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION);
 
     if (!reds_security_check(link)) {
         if (link->stream->ssl) {
@@ -3521,6 +3475,7 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
     ring_init(&reds->clients);
     reds->num_clients = 0;
     main_dispatcher_init(core);
+    ring_init(&reds->channels);
 
     if (!(reds->mig_timer = core->timer_add(migrate_timout, NULL))) {
         red_error("migration timer create failed");
@@ -3577,7 +3532,7 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
     }
 #endif
 
-    reds->main_channel = NULL;
+    reds->main_channel = main_channel_init();
     inputs_init();
 
     reds->mouse_mode = SPICE_MOUSE_MODE_SERVER;
diff --git a/server/smartcard.c b/server/smartcard.c
index 056caa6..f9cafdf 100644
--- a/server/smartcard.c
+++ b/server/smartcard.c
@@ -493,7 +493,9 @@ static void smartcard_connect(RedChannel *channel, RedClient *client,
 {
     RedChannelClient *rcc;
 
-    rcc = red_channel_client_create(sizeof(RedChannelClient), channel, client, stream);
+    rcc = red_channel_client_create(sizeof(RedChannelClient), channel, client, stream,
+                                    num_common_caps, common_caps,
+                                    num_caps, caps);
     red_channel_client_ack_zero_messages_window(rcc);
 }
 
diff --git a/server/snd_worker.c b/server/snd_worker.c
index 98f3cd1..048da34 100644
--- a/server/snd_worker.c
+++ b/server/snd_worker.c
@@ -118,8 +118,6 @@ struct SndChannel {
     snd_channel_handle_message_proc handle_message;
     snd_channel_on_message_done_proc on_message_done;
     snd_channel_cleanup_channel_proc cleanup;
-    int num_caps;
-    uint32_t *caps;
 };
 
 typedef struct AudioFrame AudioFrame;
@@ -196,18 +194,6 @@ static uint32_t playback_compression = SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
 
 static void snd_receive(void* data);
 
-static int check_cap(uint32_t *caps, int num_caps, uint32_t cap)
-{
-    uint32_t i = cap / 32;
-
-    cap = 1 << (cap % 32);
-    if (i >= num_caps) {
-        return FALSE;
-    }
-
-    return caps[i] & cap;
-}
-
 static SndChannel *snd_channel_get(SndChannel *channel)
 {
     channel->refs++;
@@ -239,7 +225,6 @@ static void snd_disconnect_channel(SndChannel *channel)
     channel->stream->watch = NULL;
     reds_stream_free(channel->stream);
     spice_marshaller_destroy(channel->send_data.marshaller);
-    free(channel->caps);
     snd_channel_put(channel);
 }
 
@@ -579,8 +564,10 @@ static int snd_playback_send_volume(PlaybackChannel *playback_channel)
     SndChannel *channel = &playback_channel->base;
     SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
 
-    if (!check_cap(channel->caps, channel->num_caps, SPICE_PLAYBACK_CAP_VOLUME))
+    if (!red_channel_client_test_remote_cap(channel->channel_client,
+                                            SPICE_PLAYBACK_CAP_VOLUME)) {
         return TRUE;
+    }
 
     return snd_send_volume(channel, &st->volume, SPICE_MSG_PLAYBACK_VOLUME);
 }
@@ -603,8 +590,10 @@ static int snd_playback_send_mute(PlaybackChannel *playback_channel)
     SndChannel *channel = &playback_channel->base;
     SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
 
-    if (!check_cap(channel->caps, channel->num_caps, SPICE_PLAYBACK_CAP_VOLUME))
+    if (!red_channel_client_test_remote_cap(channel->channel_client,
+                                            SPICE_PLAYBACK_CAP_VOLUME)) {
         return TRUE;
+    }
 
     return snd_send_mute(channel, &st->volume, SPICE_MSG_PLAYBACK_MUTE);
 }
@@ -695,8 +684,10 @@ static int snd_record_send_volume(RecordChannel *record_channel)
     SndChannel *channel = &record_channel->base;
     SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
 
-    if (!check_cap(channel->caps, channel->num_caps, SPICE_RECORD_CAP_VOLUME))
+    if (!red_channel_client_test_remote_cap(channel->channel_client,
+                                            SPICE_RECORD_CAP_VOLUME)) {
         return TRUE;
+    }
 
     return snd_send_volume(channel, &st->volume, SPICE_MSG_RECORD_VOLUME);
 }
@@ -706,8 +697,10 @@ static int snd_record_send_mute(RecordChannel *record_channel)
     SndChannel *channel = &record_channel->base;
     SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
 
-    if (!check_cap(channel->caps, channel->num_caps, SPICE_RECORD_CAP_VOLUME))
+    if (!red_channel_client_test_remote_cap(channel->channel_client,
+                                            SPICE_RECORD_CAP_VOLUME)) {
         return TRUE;
+    }
 
     return snd_send_mute(channel, &st->volume, SPICE_MSG_RECORD_MUTE);
 }
@@ -941,12 +934,12 @@ static SndChannel *__new_channel(SndWorker *worker, int size, uint32_t channel_i
     channel->handle_message = handle_message;
     channel->on_message_done = on_message_done;
     channel->cleanup = cleanup;
-    channel->num_caps = num_caps;
-    channel->caps = spice_memdup(caps, num_caps * sizeof(uint32_t));
 
     channel->channel_client = red_channel_client_create_dummy(sizeof(RedChannelClient),
                                                               worker->base_channel,
-                                                              client);
+                                                              client,
+                                                              0, NULL,
+                                                              num_caps, caps);
     return channel;
 
 error2:
@@ -1140,6 +1133,7 @@ static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsSt
     CELTEncoder *celt_encoder;
     CELTMode *celt_mode;
     int celt_error;
+    RedChannelClient *rcc;
 
     snd_disconnect_channel(worker->connection);
 
@@ -1169,13 +1163,15 @@ static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsSt
         goto error_2;
     }
     worker->connection = &playback_channel->base;
+    rcc = playback_channel->base.channel_client;
     snd_playback_free_frame(playback_channel, &playback_channel->frames[0]);
     snd_playback_free_frame(playback_channel, &playback_channel->frames[1]);
     snd_playback_free_frame(playback_channel, &playback_channel->frames[2]);
 
     playback_channel->celt_mode = celt_mode;
     playback_channel->celt_encoder = celt_encoder;
-    playback_channel->mode = check_cap(caps, num_caps, SPICE_PLAYBACK_CAP_CELT_0_5_1) ?
+    playback_channel->mode = red_channel_client_test_remote_cap(rcc,
+                                                                SPICE_PLAYBACK_CAP_CELT_0_5_1) ?
         playback_compression : SPICE_AUDIO_DATA_MODE_RAW;
 
     on_new_playback_channel(worker);
@@ -1431,8 +1427,6 @@ static void remove_worker(SndWorker *worker)
 void snd_attach_playback(SpicePlaybackInstance *sin)
 {
     SndWorker *playback_worker;
-    int num_caps;
-    uint32_t *caps;
     RedChannel *channel;
     ClientCbs client_cbs = {0,};
 
@@ -1449,12 +1443,8 @@ void snd_attach_playback(SpicePlaybackInstance *sin)
     client_cbs.migrate = snd_playback_migrate_channel_client;
     red_channel_register_client_cbs(channel, &client_cbs);
     red_channel_set_data(channel, playback_worker);
-
-    num_caps = 1;
-    caps = spice_new(uint32_t, 1);
-    caps[0] = (1 << SPICE_PLAYBACK_CAP_CELT_0_5_1) |
-              (1 << SPICE_PLAYBACK_CAP_VOLUME);
-    red_channel_set_caps(channel, num_caps, caps);
+    red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_CELT_0_5_1);
+    red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_VOLUME);
 
     playback_worker->base_channel = channel;
     add_worker(playback_worker);
@@ -1464,8 +1454,6 @@ void snd_attach_playback(SpicePlaybackInstance *sin)
 void snd_attach_record(SpiceRecordInstance *sin)
 {
     SndWorker *record_worker;
-    int num_caps;
-    uint32_t *caps;
     RedChannel *channel;
     ClientCbs client_cbs = {0,};
 
@@ -1482,12 +1470,8 @@ void snd_attach_record(SpiceRecordInstance *sin)
     client_cbs.migrate = snd_record_migrate_channel_client;
     red_channel_register_client_cbs(channel, &client_cbs);
     red_channel_set_data(channel, record_worker);
-
-    num_caps = 1;
-    caps = spice_new(uint32_t, 1);
-    caps[0] = (1 << SPICE_RECORD_CAP_CELT_0_5_1) |
-              (1 << SPICE_RECORD_CAP_VOLUME);
-    red_channel_set_caps(channel, num_caps, caps);
+    red_channel_set_cap(channel, SPICE_RECORD_CAP_CELT_0_5_1);
+    red_channel_set_cap(channel, SPICE_RECORD_CAP_VOLUME);
 
     record_worker->base_channel = channel;
     add_worker(record_worker);
@@ -1538,8 +1522,8 @@ void snd_set_playback_compression(int on)
         if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection) {
             SndChannel* sndchannel = now->connection;
             PlaybackChannel* playback = (PlaybackChannel*)now->connection;
-            if (!check_cap(sndchannel->caps, sndchannel->num_caps,
-                           SPICE_PLAYBACK_CAP_CELT_0_5_1)) {
+            if (!red_channel_client_test_remote_cap(sndchannel->channel_client,
+                                                    SPICE_PLAYBACK_CAP_CELT_0_5_1)) {
                 ASSERT(playback->mode == SPICE_AUDIO_DATA_MODE_RAW);
                 continue;
             }
diff --git a/server/spicevmc.c b/server/spicevmc.c
index 9ccc0d1..8580984 100644
--- a/server/spicevmc.c
+++ b/server/spicevmc.c
@@ -206,7 +206,9 @@ static void spicevmc_connect(RedChannel *channel, RedClient *client,
         return;
     }
 
-    rcc = red_channel_client_create(sizeof(RedChannelClient), channel, client, stream);
+    rcc = red_channel_client_create(sizeof(RedChannelClient), channel, client, stream,
+                                    num_common_caps, common_caps,
+                                    num_caps, caps);
     if (!rcc) {
         return;
     }


More information about the Spice-commits mailing list