diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2e95719
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+SOURCES/spice-0.14.0.tar.bz2
diff --git a/.spice.metadata b/.spice.metadata
new file mode 100644
index 0000000..f0a186a
--- /dev/null
+++ b/.spice.metadata
@@ -0,0 +1 @@
+93e42588d1aac0a3c127ada1e5d8f40be84776a9 SOURCES/spice-0.14.0.tar.bz2
diff --git a/SOURCES/0001-inputs-channel-Check-message-size-handling-migration.patch b/SOURCES/0001-inputs-channel-Check-message-size-handling-migration.patch
new file mode 100644
index 0000000..3438403
--- /dev/null
+++ b/SOURCES/0001-inputs-channel-Check-message-size-handling-migration.patch
@@ -0,0 +1,32 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Fri, 6 Oct 2017 09:38:31 +0100
+Subject: [spice-server] inputs-channel: Check message size handling migration
+ data
+
+Prevent possible buffer reading overflow.
+Note that message pointer must be valid and data are checked
+value by value so even on overflow you just get an error.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Christophe Fergeau <cfergeau@redhat.com>
+---
+ server/inputs-channel.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/server/inputs-channel.c b/server/inputs-channel.c
+index 8e17cc724..11a338a26 100644
+--- a/server/inputs-channel.c
++++ b/server/inputs-channel.c
+@@ -505,6 +505,11 @@ static bool inputs_channel_handle_migrate_data(RedChannelClient *rcc,
+     SpiceMigrateDataHeader *header;
+     SpiceMigrateDataInputs *mig_data;
+ 
++    if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataInputs)) {
++        spice_warning("bad message size %u", size);
++        return FALSE;
++    }
++
+     header = (SpiceMigrateDataHeader *)message;
+     mig_data = (SpiceMigrateDataInputs *)(header + 1);
+ 
diff --git a/SOURCES/0002-red-channel-Remove-red_channel_init_outgoing_message.patch b/SOURCES/0002-red-channel-Remove-red_channel_init_outgoing_message.patch
new file mode 100644
index 0000000..1b06a0a
--- /dev/null
+++ b/SOURCES/0002-red-channel-Remove-red_channel_init_outgoing_message.patch
@@ -0,0 +1,66 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Fri, 6 Oct 2017 08:57:42 +0100
+Subject: [spice-server] red-channel: Remove
+ red_channel_init_outgoing_messages_window
+
+This function does not make much sense anymore.
+Is called by RedVmcChannel which doesn't use RedChannelClient ACKs
+so the variable changed are not used.
+Moreover, at red_vmc_channel_constructed() time, there will be no
+clients yet, so red_channel_init_outgoing_messages() will be a no-op.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Christophe Fergeau <cfergeau@redhat.com>
+---
+ server/red-channel.c | 8 --------
+ server/red-channel.h | 3 ---
+ server/spicevmc.c    | 2 --
+ 3 files changed, 13 deletions(-)
+
+diff --git a/server/red-channel.c b/server/red-channel.c
+index b5094829e..8c507f202 100644
+--- a/server/red-channel.c
++++ b/server/red-channel.c
+@@ -421,14 +421,6 @@ void red_channel_push(RedChannel *channel)
+     g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_push, NULL);
+ }
+ 
+-// TODO: this function doesn't make sense because the window should be client (WAN/LAN)
+-// specific
+-void red_channel_init_outgoing_messages_window(RedChannel *channel)
+-{
+-    g_list_foreach(channel->priv->clients,
+-                   (GFunc)red_channel_client_init_outgoing_messages_window, NULL);
+-}
+-
+ void red_channel_pipes_add(RedChannel *channel, RedPipeItem *item)
+ {
+     RedChannelClient *rcc;
+diff --git a/server/red-channel.h b/server/red-channel.h
+index e0fe94fec..281ed0c9e 100644
+--- a/server/red-channel.h
++++ b/server/red-channel.h
+@@ -157,9 +157,6 @@ void red_channel_destroy(RedChannel *channel);
+ /* return true if all the channel clients support the cap */
+ bool red_channel_test_remote_cap(RedChannel *channel, uint32_t cap);
+ 
+-/* should be called when a new channel is ready to send messages */
+-void red_channel_init_outgoing_messages_window(RedChannel *channel);
+-
+ // helper to push a new item to all channels
+ typedef RedPipeItem *(*new_pipe_item_t)(RedChannelClient *rcc, void *data, int num);
+ int red_channel_pipes_new_add(RedChannel *channel, new_pipe_item_t creator, void *data);
+diff --git a/server/spicevmc.c b/server/spicevmc.c
+index 6b9b96fc8..a1685483d 100644
+--- a/server/spicevmc.c
++++ b/server/spicevmc.c
+@@ -246,8 +246,6 @@ red_vmc_channel_constructed(GObject *object)
+     red_channel_set_cap(RED_CHANNEL(self), SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4);
+ #endif
+ 
+-    red_channel_init_outgoing_messages_window(RED_CHANNEL(self));
+-
+     reds_register_channel(reds, RED_CHANNEL(self));
+ }
+ 
diff --git a/SOURCES/0003-reds-Remove-leak-allocating-migration-state.patch b/SOURCES/0003-reds-Remove-leak-allocating-migration-state.patch
new file mode 100644
index 0000000..dc6b99f
--- /dev/null
+++ b/SOURCES/0003-reds-Remove-leak-allocating-migration-state.patch
@@ -0,0 +1,40 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Tue, 19 Sep 2017 08:28:02 +0100
+Subject: [spice-server] reds: Remove leak allocating migration state
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Christophe Fergeau <cfergeau@redhat.com>
+---
+ server/reds.c | 2 +-
+ server/reds.h | 4 ----
+ 2 files changed, 1 insertion(+), 5 deletions(-)
+
+diff --git a/server/reds.c b/server/reds.c
+index 12c33570a..1b1ab94ea 100644
+--- a/server/reds.c
++++ b/server/reds.c
+@@ -3378,7 +3378,7 @@ SPICE_GNUC_VISIBLE int spice_server_add_interface(SpiceServer *reds,
+             return -1;
+         }
+         reds->migration_interface = SPICE_CONTAINEROF(sin, SpiceMigrateInstance, base);
+-        reds->migration_interface->st = spice_new0(SpiceMigrateState, 1);
++        reds->migration_interface->st = (SpiceMigrateState *)(intptr_t)1; // dummy pointer
+     }
+ 
+     return 0;
+diff --git a/server/reds.h b/server/reds.h
+index 4f5fc28c3..cea002c51 100644
+--- a/server/reds.h
++++ b/server/reds.h
+@@ -35,10 +35,6 @@ static inline QXLInterface * qxl_get_interface(QXLInstance *qxl)
+     return SPICE_CONTAINEROF(qxl->base.sif, QXLInterface, base);
+ }
+ 
+-struct SpiceMigrateState {
+-    int dummy;
+-};
+-
+ /* main thread only */
+ void reds_handle_channel_event(RedsState *reds, int event, SpiceChannelEventInfo *info);
+ 
diff --git a/SOURCES/0004-tests-Check-leaks-registering-migration-interface.patch b/SOURCES/0004-tests-Check-leaks-registering-migration-interface.patch
new file mode 100644
index 0000000..3ba2f44
--- /dev/null
+++ b/SOURCES/0004-tests-Check-leaks-registering-migration-interface.patch
@@ -0,0 +1,64 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Tue, 19 Sep 2017 08:27:38 +0100
+Subject: [spice-server] tests: Check leaks registering migration interface
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Christophe Fergeau <cfergeau@redhat.com>
+---
+ server/tests/test-leaks.c | 36 ++++++++++++++++++++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+
+diff --git a/server/tests/test-leaks.c b/server/tests/test-leaks.c
+index 04dcac4f4..7032000aa 100644
+--- a/server/tests/test-leaks.c
++++ b/server/tests/test-leaks.c
+@@ -143,12 +143,48 @@ static void vmc_leaks(void)
+     basic_event_loop_destroy();
+ }
+ 
++static void migrate_cb(SpiceMigrateInstance *sin)
++{
++}
++
++static const SpiceMigrateInterface migrate_interface = {
++    .base = {
++        .type          = SPICE_INTERFACE_MIGRATION,
++        .description   = "migration",
++        .major_version = SPICE_INTERFACE_MIGRATION_MAJOR,
++        .minor_version = SPICE_INTERFACE_MIGRATION_MINOR,
++    },
++    .migrate_connect_complete = migrate_cb,
++    .migrate_end_complete = migrate_cb,
++};
++
++static void migration_leaks(void)
++{
++    SpiceCoreInterface *core;
++    SpiceServer *server = spice_server_new();
++    SpiceMigrateInstance migrate;
++
++    g_assert_nonnull(server);
++
++    core = basic_event_loop_init();
++    g_assert_nonnull(core);
++
++    g_assert_cmpint(spice_server_init(server, core), ==, 0);
++
++    migrate.base.sif = &migrate_interface.base;
++    spice_server_add_interface(server, &migrate.base);
++
++    spice_server_destroy(server);
++    basic_event_loop_destroy();
++}
++
+ int main(int argc, char *argv[])
+ {
+     g_test_init(&argc, &argv, NULL);
+ 
+     g_test_add_func("/server/server leaks", server_leaks);
+     g_test_add_func("/server/vmc leaks", vmc_leaks);
++    g_test_add_func("/server/migration leaks", migration_leaks);
+ 
+     return g_test_run();
+ }
diff --git a/SOURCES/0005-Notify-client-of-the-creation-of-new-channels-dynami.patch b/SOURCES/0005-Notify-client-of-the-creation-of-new-channels-dynami.patch
new file mode 100644
index 0000000..8937a67
--- /dev/null
+++ b/SOURCES/0005-Notify-client-of-the-creation-of-new-channels-dynami.patch
@@ -0,0 +1,179 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Thu, 24 Aug 2017 23:37:25 +0100
+Subject: [spice-server] Notify client of the creation of new channels
+ dynamically
+
+This allows the server to add channels after the client is connected.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
+---
+ server/main-channel-client.c | 51 ++++++++++++++++++++++++++++++++++++++++++++
+ server/main-channel-client.h |  3 +++
+ server/main-channel.c        |  6 ++++++
+ server/main-channel.h        |  3 +++
+ server/reds.c                |  2 ++
+ 5 files changed, 65 insertions(+)
+
+diff --git a/server/main-channel-client.c b/server/main-channel-client.c
+index b7b60eddb..61a2830fa 100644
+--- a/server/main-channel-client.c
++++ b/server/main-channel-client.c
+@@ -62,6 +62,7 @@ struct MainChannelClientPrivate {
+     int mig_wait_prev_try_seamless;
+     int init_sent;
+     int seamless_mig_dst;
++    bool initial_channels_list_sent;
+     uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE];
+ };
+ 
+@@ -119,6 +120,12 @@ typedef struct RedMultiMediaTimePipeItem {
+     uint32_t time;
+ } RedMultiMediaTimePipeItem;
+ 
++typedef struct RedRegisteredChannelPipeItem {
++    RedPipeItem base;
++    uint32_t channel_type;
++    uint32_t channel_id;
++} RedRegisteredChannelPipeItem;
++
+ #define ZERO_BUF_SIZE 4096
+ 
+ static const uint8_t zero_page[ZERO_BUF_SIZE] = {0};
+@@ -446,6 +453,20 @@ RedPipeItem *main_multi_media_time_item_new(uint32_t mm_time)
+     return &item->base;
+ }
+ 
++RedPipeItem *registered_channel_item_new(RedChannel *channel)
++{
++    RedRegisteredChannelPipeItem *item;
++
++    item = g_new0(RedRegisteredChannelPipeItem, 1);
++    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_REGISTERED_CHANNEL);
++
++    uint32_t type, id;
++    g_object_get(channel, "channel-type", &type, "id", &id, NULL);
++    item->channel_type = type;
++    item->channel_id = id;
++    return &item->base;
++}
++
+ void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
+                                                   int success,
+                                                   int seamless)
+@@ -927,6 +948,25 @@ static void main_channel_marshall_agent_connected(SpiceMarshaller *m,
+     spice_marshall_msg_main_agent_connected_tokens(m, &connected);
+ }
+ 
++static void main_channel_marshall_registered_channel(RedChannelClient *rcc,
++                                                     SpiceMarshaller *m,
++                                                     RedRegisteredChannelPipeItem *item)
++{
++    struct {
++        SpiceMsgChannels info;
++        SpiceChannelId ids[1];
++    } channels_info_buffer;
++    SpiceMsgChannels* channels_info = &channels_info_buffer.info;
++
++    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_CHANNELS_LIST);
++
++    channels_info->channels[0].type = item->channel_type;
++    channels_info->channels[0].id = item->channel_id;
++    channels_info->num_of_channels = 1;
++
++    spice_marshall_msg_main_channels_list(m, channels_info);
++}
++
+ void main_channel_client_send_item(RedChannelClient *rcc, RedPipeItem *base)
+ {
+     MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc);
+@@ -947,6 +987,7 @@ void main_channel_client_send_item(RedChannelClient *rcc, RedPipeItem *base)
+     switch (base->type) {
+         case RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST:
+             main_channel_marshall_channels(rcc, m, base);
++            mcc->priv->initial_channels_list_sent = true;
+             break;
+         case RED_PIPE_ITEM_TYPE_MAIN_PING:
+             main_channel_marshall_ping(rcc, m,
+@@ -1003,6 +1044,16 @@ void main_channel_client_send_item(RedChannelClient *rcc, RedPipeItem *base)
+         case RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS:
+             main_channel_marshall_agent_connected(m, rcc, base);
+             break;
++        case RED_PIPE_ITEM_TYPE_MAIN_REGISTERED_CHANNEL:
++            /* The spice protocol requires that the server receive a ATTACH_CHANNELS
++             * message from the client before sending any CHANNEL_LIST message. If
++             * we've already sent our initial CHANNELS_LIST message, then it should be
++             * safe to send new ones for newly-registered channels. */
++            if (mcc->priv->initial_channels_list_sent) {
++                main_channel_marshall_registered_channel(rcc, m,
++                    SPICE_UPCAST(RedRegisteredChannelPipeItem, base));
++            }
++            break;
+         default:
+             break;
+     };
+diff --git a/server/main-channel-client.h b/server/main-channel-client.h
+index 26b7e20b8..2cf2e3424 100644
+--- a/server/main-channel-client.h
++++ b/server/main-channel-client.h
+@@ -122,12 +122,15 @@ enum {
+     RED_PIPE_ITEM_TYPE_MAIN_NAME,
+     RED_PIPE_ITEM_TYPE_MAIN_UUID,
+     RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS,
++    RED_PIPE_ITEM_TYPE_MAIN_REGISTERED_CHANNEL,
+ };
+ 
+ RedPipeItem *main_mouse_mode_item_new(SpiceMouseMode current_mode, int is_client_mouse_allowed);
+ 
+ RedPipeItem *main_multi_media_time_item_new(uint32_t mm_time);
+ 
++RedPipeItem *registered_channel_item_new(RedChannel *channel);
++
+ G_END_DECLS
+ 
+ #endif /* MAIN_CHANNEL_CLIENT_H_ */
+diff --git a/server/main-channel.c b/server/main-channel.c
+index eca857f6b..4dc130e40 100644
+--- a/server/main-channel.c
++++ b/server/main-channel.c
+@@ -149,6 +149,12 @@ static void main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice
+     main_channel->mig_target.sport = mig_target->sport;
+ }
+ 
++void
++main_channel_registered_new_channel(MainChannel *main_chan, RedChannel *channel)
++{
++    red_channel_pipes_add(RED_CHANNEL(main_chan), registered_channel_item_new(channel));
++}
++
+ void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target)
+ {
+     main_channel_fill_mig_target(main_chan, mig_target);
+diff --git a/server/main-channel.h b/server/main-channel.h
+index eb3bcec3a..0cb5be728 100644
+--- a/server/main-channel.h
++++ b/server/main-channel.h
+@@ -66,6 +66,9 @@ void main_channel_push_mouse_mode(MainChannel *main_chan, SpiceMouseMode current
+ void main_channel_push_agent_connected(MainChannel *main_chan);
+ void main_channel_push_agent_disconnected(MainChannel *main_chan);
+ void main_channel_push_multi_media_time(MainChannel *main_chan, uint32_t time);
++/* tell MainChannel we have a new channel ready */
++void main_channel_registered_new_channel(MainChannel *main_chan,
++                                         RedChannel *channel);
+ 
+ int main_channel_is_connected(MainChannel *main_chan);
+ 
+diff --git a/server/reds.c b/server/reds.c
+index 1b1ab94ea..99b1fd76b 100644
+--- a/server/reds.c
++++ b/server/reds.c
+@@ -387,6 +387,8 @@ void reds_register_channel(RedsState *reds, RedChannel *channel)
+ {
+     spice_assert(reds);
+     reds->channels = g_list_prepend(reds->channels, channel);
++    // create new channel in the client if possible
++    main_channel_registered_new_channel(reds->main_channel, channel);
+ }
+ 
+ void reds_unregister_channel(RedsState *reds, RedChannel *channel)
diff --git a/SOURCES/0006-stream-device-Add-device-to-handle-streaming.patch b/SOURCES/0006-stream-device-Add-device-to-handle-streaming.patch
new file mode 100644
index 0000000..22cdee7
--- /dev/null
+++ b/SOURCES/0006-stream-device-Add-device-to-handle-streaming.patch
@@ -0,0 +1,194 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Sat, 21 Jan 2017 11:24:58 +0000
+Subject: [spice-server] stream-device: Add device to handle streaming
+
+Add a stub device in guest.
+The aim of this device is to make it possible for the guest to send a
+stream through a DisplayChannel (in the sense of protocol channel).
+This stub allows the guest to send some data and you can see some debug
+lines of data arrived on host logs.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
+---
+ server/Makefile.am     |   1 +
+ server/char-device.h   |   1 +
+ server/reds.c          |   2 +
+ server/stream-device.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 134 insertions(+)
+ create mode 100644 server/stream-device.c
+
+diff --git a/server/Makefile.am b/server/Makefile.am
+index 5d5590af9..f08ddf883 100644
+--- a/server/Makefile.am
++++ b/server/Makefile.am
+@@ -166,6 +166,7 @@ libserver_la_SOURCES =				\
+ 	stat.h					\
+ 	stream.c				\
+ 	stream.h				\
++	stream-device.c				\
+ 	sw-canvas.c				\
+ 	tree.c					\
+ 	tree.h					\
+diff --git a/server/char-device.h b/server/char-device.h
+index dccd576da..54a1ef939 100644
+--- a/server/char-device.h
++++ b/server/char-device.h
+@@ -236,6 +236,7 @@ RedCharDevice *spicevmc_device_connect(RedsState *reds,
+                                        uint8_t channel_type);
+ void spicevmc_device_disconnect(RedsState *reds,
+                                 SpiceCharDeviceInstance *char_device);
++RedCharDevice *stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin);
+ 
+ SpiceCharDeviceInterface *spice_char_device_get_interface(SpiceCharDeviceInstance *instance);
+ 
+diff --git a/server/reds.c b/server/reds.c
+index 99b1fd76b..b24f61ab2 100644
+--- a/server/reds.c
++++ b/server/reds.c
+@@ -3223,6 +3223,8 @@ static int spice_server_char_device_add_interface(SpiceServer *reds,
+     else if (strcmp(char_device->subtype, SUBTYPE_PORT) == 0) {
+         if (strcmp(char_device->portname, "org.spice-space.webdav.0") == 0) {
+             dev_state = spicevmc_device_connect(reds, char_device, SPICE_CHANNEL_WEBDAV);
++        } else if (strcmp(char_device->portname, "com.redhat.stream.0") == 0) {
++            dev_state = stream_device_connect(reds, char_device);
+         } else {
+             dev_state = spicevmc_device_connect(reds, char_device, SPICE_CHANNEL_PORT);
+         }
+diff --git a/server/stream-device.c b/server/stream-device.c
+new file mode 100644
+index 000000000..f3a147b80
+--- /dev/null
++++ b/server/stream-device.c
+@@ -0,0 +1,130 @@
++/* spice-server character device to handle a video stream
++
++   Copyright (C) 2017 Red Hat, Inc.
++
++   This library is free software; you can redistribute it and/or
++   modify it under the terms of the GNU Lesser General Public
++   License as published by the Free Software Foundation; either
++   version 2.1 of the License, or (at your option) any later version.
++
++   This library is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++   Lesser General Public License for more details.
++
++   You should have received a copy of the GNU Lesser General Public
++   License along with this library; if not, see <http://www.gnu.org/licenses/>.
++*/
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include "char-device.h"
++
++#define TYPE_STREAM_DEVICE stream_device_get_type()
++
++#define STREAM_DEVICE(obj) \
++    (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_STREAM_DEVICE, StreamDevice))
++#define STREAM_DEVICE_CLASS(klass) \
++    (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_STREAM_DEVICE, StreamDeviceClass))
++#define STREAM_DEVICE_GET_CLASS(obj) \
++    (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_STREAM_DEVICE, StreamDeviceClass))
++
++typedef struct StreamDevice StreamDevice;
++typedef struct StreamDeviceClass StreamDeviceClass;
++
++struct StreamDevice {
++    RedCharDevice parent;
++};
++
++struct StreamDeviceClass {
++    RedCharDeviceClass parent_class;
++};
++
++static GType stream_device_get_type(void) G_GNUC_CONST;
++static StreamDevice *stream_device_new(SpiceCharDeviceInstance *sin, RedsState *reds);
++
++G_DEFINE_TYPE(StreamDevice, stream_device, RED_TYPE_CHAR_DEVICE)
++
++static RedPipeItem *
++stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *sin)
++{
++    SpiceCharDeviceInterface *sif;
++    int n;
++
++    sif = spice_char_device_get_interface(sin);
++
++    do {
++        uint8_t buf[256];
++        n = sif->read(sin, buf, sizeof(buf));
++        spice_debug("read %d bytes from device", n);
++    } while (n > 0);
++
++    return NULL;
++}
++
++static void
++stream_device_send_msg_to_client(RedCharDevice *self, RedPipeItem *msg, RedClient *client)
++{
++}
++
++static void
++stream_device_send_tokens_to_client(RedCharDevice *self, RedClient *client, uint32_t tokens)
++{
++    spice_printerr("Not implemented!");
++}
++
++static void
++stream_device_remove_client(RedCharDevice *self, RedClient *client)
++{
++}
++
++RedCharDevice *
++stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin)
++{
++    SpiceCharDeviceInterface *sif;
++
++    StreamDevice *dev = stream_device_new(sin, reds);
++
++    sif = spice_char_device_get_interface(sin);
++    if (sif->state) {
++        sif->state(sin, 1);
++    }
++
++    return RED_CHAR_DEVICE(dev);
++}
++
++static void
++stream_device_dispose(GObject *object)
++{
++}
++
++static void
++stream_device_class_init(StreamDeviceClass *klass)
++{
++    GObjectClass *object_class = G_OBJECT_CLASS(klass);
++    RedCharDeviceClass *char_dev_class = RED_CHAR_DEVICE_CLASS(klass);
++
++    object_class->dispose = stream_device_dispose;
++
++    char_dev_class->read_one_msg_from_device = stream_device_read_msg_from_dev;
++    char_dev_class->send_msg_to_client = stream_device_send_msg_to_client;
++    char_dev_class->send_tokens_to_client = stream_device_send_tokens_to_client;
++    char_dev_class->remove_client = stream_device_remove_client;
++}
++
++static void
++stream_device_init(StreamDevice *self)
++{
++}
++
++static StreamDevice *
++stream_device_new(SpiceCharDeviceInstance *sin, RedsState *reds)
++{
++    return g_object_new(TYPE_STREAM_DEVICE,
++                        "sin", sin,
++                        "spice-server", reds,
++                        "client-tokens-interval", 0ULL,
++                        "self-tokens", ~0ULL,
++                        NULL);
++}
diff --git a/SOURCES/0007-stream-device-Start-parsing-new-protocol-from-guest.patch b/SOURCES/0007-stream-device-Start-parsing-new-protocol-from-guest.patch
new file mode 100644
index 0000000..872697a
--- /dev/null
+++ b/SOURCES/0007-stream-device-Start-parsing-new-protocol-from-guest.patch
@@ -0,0 +1,178 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Fri, 13 Jan 2017 18:20:43 +0000
+Subject: [spice-server] stream-device: Start parsing new protocol from guest
+
+Parse the data sent from the guest to the streaming device.
+At the moment, the data is simply discarded after it is parsed.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
+---
+ server/stream-device.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 124 insertions(+), 4 deletions(-)
+
+diff --git a/server/stream-device.c b/server/stream-device.c
+index f3a147b80..58edb3d11 100644
+--- a/server/stream-device.c
++++ b/server/stream-device.c
+@@ -19,6 +19,8 @@
+ #include <config.h>
+ #endif
+ 
++#include <spice/stream-device.h>
++
+ #include "char-device.h"
+ 
+ #define TYPE_STREAM_DEVICE stream_device_get_type()
+@@ -35,6 +37,9 @@ typedef struct StreamDeviceClass StreamDeviceClass;
+ 
+ struct StreamDevice {
+     RedCharDevice parent;
++    StreamDevHeader hdr;
++    uint8_t hdr_pos;
++    bool has_error;
+ };
+ 
+ struct StreamDeviceClass {
+@@ -46,21 +51,136 @@ static StreamDevice *stream_device_new(SpiceCharDeviceInstance *sin, RedsState *
+ 
+ G_DEFINE_TYPE(StreamDevice, stream_device, RED_TYPE_CHAR_DEVICE)
+ 
++typedef bool StreamMsgHandler(StreamDevice *dev, SpiceCharDeviceInstance *sin)
++    SPICE_GNUC_WARN_UNUSED_RESULT;
++
++static StreamMsgHandler handle_msg_format, handle_msg_data;
++
++static bool handle_msg_invalid(StreamDevice *dev, SpiceCharDeviceInstance *sin,
++                               const char *error_msg) SPICE_GNUC_WARN_UNUSED_RESULT;
++
+ static RedPipeItem *
+ stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *sin)
+ {
++    StreamDevice *dev = STREAM_DEVICE(self);
+     SpiceCharDeviceInterface *sif;
+     int n;
++    bool handled = false;
++
++    if (dev->has_error) {
++        return NULL;
++    }
+ 
+     sif = spice_char_device_get_interface(sin);
+ 
+-    do {
+-        uint8_t buf[256];
++    /* read header */
++    while (dev->hdr_pos < sizeof(dev->hdr)) {
++        n = sif->read(sin, (uint8_t *) &dev->hdr, sizeof(dev->hdr) - dev->hdr_pos);
++        if (n <= 0) {
++            return NULL;
++        }
++        dev->hdr_pos += n;
++        if (dev->hdr_pos >= sizeof(dev->hdr)) {
++            dev->hdr.type = GUINT16_FROM_LE(dev->hdr.type);
++            dev->hdr.size = GUINT32_FROM_LE(dev->hdr.size);
++        }
++    }
++
++    switch ((StreamMsgType) dev->hdr.type) {
++    case STREAM_TYPE_FORMAT:
++        if (dev->hdr.size != sizeof(StreamMsgFormat)) {
++            handled = handle_msg_invalid(dev, sin, "Wrong size for StreamMsgFormat");
++        } else {
++            handled = handle_msg_format(dev, sin);
++        }
++        break;
++    case STREAM_TYPE_DATA:
++        handled = handle_msg_data(dev, sin);
++        break;
++    case STREAM_TYPE_CAPABILITIES:
++        /* FIXME */
++    default:
++        handled = handle_msg_invalid(dev, sin, "Invalid message type");
++        break;
++    }
++
++    /* current message has been handled, so reset state and get ready to parse
++     * the next message */
++    if (handled) {
++        dev->hdr_pos = 0;
++    }
++
++    return NULL;
++}
++
++static bool
++handle_msg_invalid(StreamDevice *dev, SpiceCharDeviceInstance *sin, const char *error_msg)
++{
++    static const char default_error_msg[] = "Protocol error";
++
++    if (!error_msg) {
++        error_msg = default_error_msg;
++    }
++
++    int msg_size = sizeof(StreamMsgNotifyError) + strlen(error_msg) + 1;
++    int total_size = sizeof(StreamDevHeader) + msg_size;
++
++    RedCharDevice *char_dev = RED_CHAR_DEVICE(dev);
++    RedCharDeviceWriteBuffer *buf =
++        red_char_device_write_buffer_get_server_no_token(char_dev, total_size);
++    buf->buf_used = total_size;
++
++    StreamDevHeader *const hdr = (StreamDevHeader *)buf->buf;
++    hdr->protocol_version = STREAM_DEVICE_PROTOCOL;
++    hdr->padding = 0;
++    hdr->type = GUINT16_TO_LE(STREAM_TYPE_NOTIFY_ERROR);
++    hdr->size = GUINT32_TO_LE(msg_size);
++
++    StreamMsgNotifyError *const error = (StreamMsgNotifyError *)(hdr+1);
++    error->error_code = GUINT32_TO_LE(0);
++    strcpy((char *) error->msg, error_msg);
++
++    red_char_device_write_buffer_add(char_dev, buf);
++
++    dev->has_error = true;
++    return false;
++}
++
++static bool
++handle_msg_format(StreamDevice *dev, SpiceCharDeviceInstance *sin)
++{
++    StreamMsgFormat fmt;
++    SpiceCharDeviceInterface *sif = spice_char_device_get_interface(sin);
++    int n = sif->read(sin, (uint8_t *) &fmt, sizeof(fmt));
++    if (n == 0) {
++        return false;
++    }
++    if (n != sizeof(fmt)) {
++        return handle_msg_invalid(dev, sin, NULL);
++    }
++    fmt.width = GUINT32_FROM_LE(fmt.width);
++    fmt.height = GUINT32_FROM_LE(fmt.height);
++
++    return true;
++}
++
++static bool
++handle_msg_data(StreamDevice *dev, SpiceCharDeviceInstance *sin)
++{
++    SpiceCharDeviceInterface *sif = spice_char_device_get_interface(sin);
++    int n;
++    while (1) {
++        uint8_t buf[16 * 1024];
+         n = sif->read(sin, buf, sizeof(buf));
++        /* TODO */
+         spice_debug("read %d bytes from device", n);
+-    } while (n > 0);
++        if (n <= 0) {
++            break;
++        }
++        dev->hdr.size -= n;
++    }
+ 
+-    return NULL;
++    return dev->hdr.size == 0;
+ }
+ 
+ static void
diff --git a/SOURCES/0008-stream-channel-Write-a-base-channel-to-implement-the.patch b/SOURCES/0008-stream-channel-Write-a-base-channel-to-implement-the.patch
new file mode 100644
index 0000000..8d3b2c1
--- /dev/null
+++ b/SOURCES/0008-stream-channel-Write-a-base-channel-to-implement-the.patch
@@ -0,0 +1,271 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Sat, 21 Jan 2017 15:29:18 +0000
+Subject: [spice-server] stream-channel: Write a base channel to implement the
+ streaming
+
+Currently only compile, not used and not much sense
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
+---
+ server/Makefile.am      |   2 +
+ server/stream-channel.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++
+ server/stream-channel.h |  53 +++++++++++++++
+ 3 files changed, 230 insertions(+)
+ create mode 100644 server/stream-channel.c
+ create mode 100644 server/stream-channel.h
+
+diff --git a/server/Makefile.am b/server/Makefile.am
+index f08ddf883..e2e3ce861 100644
+--- a/server/Makefile.am
++++ b/server/Makefile.am
+@@ -166,6 +166,8 @@ libserver_la_SOURCES =				\
+ 	stat.h					\
+ 	stream.c				\
+ 	stream.h				\
++	stream-channel.c			\
++	stream-channel.h			\
+ 	stream-device.c				\
+ 	sw-canvas.c				\
+ 	tree.c					\
+diff --git a/server/stream-channel.c b/server/stream-channel.c
+new file mode 100644
+index 000000000..fd735f562
+--- /dev/null
++++ b/server/stream-channel.c
+@@ -0,0 +1,175 @@
++/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/*
++   Copyright (C) 2017 Red Hat, Inc.
++
++   This library is free software; you can redistribute it and/or
++   modify it under the terms of the GNU Lesser General Public
++   License as published by the Free Software Foundation; either
++   version 2.1 of the License, or (at your option) any later version.
++
++   This library is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++   Lesser General Public License for more details.
++
++   You should have received a copy of the GNU Lesser General Public
++   License along with this library; if not, see <http://www.gnu.org/licenses/>.
++*/
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include "red-channel-client.h"
++#include "stream-channel.h"
++#include "reds.h"
++#include "common-graphics-channel.h"
++
++#define TYPE_STREAM_CHANNEL_CLIENT stream_channel_client_get_type()
++
++#define STREAM_CHANNEL_CLIENT(obj) \
++    (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_STREAM_CHANNEL_CLIENT, StreamChannelClient))
++#define STREAM_CHANNEL_CLIENT_CLASS(klass) \
++    (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_STREAM_CHANNEL_CLIENT, StreamChannelClientClass))
++#define IS_STREAM_CHANNEL_CLIENT(obj) \
++    (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_STREAM_CHANNEL_CLIENT))
++#define IS_STREAM_CHANNEL_CLIENT_CLASS(klass) \
++    (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_STREAM_CHANNEL_CLIENT))
++#define STREAM_CHANNEL_CLIENT_GET_CLASS(obj) \
++    (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_STREAM_CHANNEL_CLIENT, StreamChannelClientClass))
++
++typedef struct StreamChannelClient StreamChannelClient;
++typedef struct StreamChannelClientClass StreamChannelClientClass;
++
++/* we need to inherit from CommonGraphicsChannelClient
++ * to get buffer handling */
++struct StreamChannelClient {
++    CommonGraphicsChannelClient parent;
++};
++
++struct StreamChannelClientClass {
++    CommonGraphicsChannelClientClass parent_class;
++};
++
++GType stream_channel_client_get_type(void) G_GNUC_CONST;
++
++G_DEFINE_TYPE(StreamChannelClient, stream_channel_client, TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT)
++
++struct StreamChannel {
++    RedChannel parent;
++};
++
++struct StreamChannelClass {
++    RedChannelClass parent_class;
++};
++
++G_DEFINE_TYPE(StreamChannel, stream_channel, RED_TYPE_CHANNEL)
++
++static void stream_channel_client_on_disconnect(RedChannelClient *rcc);
++
++static void
++stream_channel_client_class_init(StreamChannelClientClass *klass)
++{
++    RedChannelClientClass *channel_class = RED_CHANNEL_CLIENT_CLASS(klass);
++
++    channel_class->on_disconnect = stream_channel_client_on_disconnect;
++}
++
++static void
++stream_channel_client_init(StreamChannelClient *client)
++{
++}
++
++static void
++stream_channel_client_on_disconnect(RedChannelClient *rcc)
++{
++}
++
++static StreamChannelClient*
++stream_channel_client_new(StreamChannel *channel, RedClient *client, RedsStream *stream,
++                          int mig_target, RedChannelCapabilities *caps)
++{
++    StreamChannelClient *rcc;
++
++    rcc = g_initable_new(TYPE_STREAM_CHANNEL_CLIENT,
++                         NULL, NULL,
++                         "channel", channel,
++                         "client", client,
++                         "stream", stream,
++                         "monitor-latency", FALSE,
++                         "caps", caps,
++                         NULL);
++
++    return rcc;
++}
++
++static void
++stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
++{
++    switch (pipe_item->type) {
++    default:
++        spice_error("invalid pipe item type");
++    }
++
++    red_channel_client_begin_send_message(rcc);
++}
++
++StreamChannel*
++stream_channel_new(RedsState *server, uint32_t id)
++{
++    return g_object_new(TYPE_STREAM_CHANNEL,
++                        "spice-server", server,
++                        "core-interface", reds_get_core_interface(server),
++                        "channel-type", SPICE_CHANNEL_DISPLAY,
++                        // TODO this id should be after all qxl devices
++                        "id", id,
++                        "migration-flags", 0,
++                        "handle-acks", TRUE, // TODO sure ??
++                        NULL);
++}
++
++static void
++stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStream *stream,
++                       int migration, RedChannelCapabilities *caps)
++{
++    StreamChannel *channel = STREAM_CHANNEL(red_channel);
++    StreamChannelClient *client;
++
++    spice_return_if_fail(stream != NULL);
++
++    client = stream_channel_client_new(channel, red_client, stream, migration, caps);
++    spice_return_if_fail(client != NULL);
++}
++
++static void
++stream_channel_constructed(GObject *object)
++{
++    ClientCbs client_cbs = { NULL, };
++    RedChannel *red_channel = RED_CHANNEL(object);
++    RedsState *reds = red_channel_get_server(red_channel);
++
++    G_OBJECT_CLASS(stream_channel_parent_class)->constructed(object);
++
++    client_cbs.connect = stream_channel_connect;
++    red_channel_register_client_cbs(red_channel, &client_cbs, NULL);
++
++    reds_register_channel(reds, red_channel);
++}
++
++static void
++stream_channel_class_init(StreamChannelClass *klass)
++{
++    GObjectClass *object_class = G_OBJECT_CLASS(klass);
++    RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass);
++
++    object_class->constructed = stream_channel_constructed;
++
++    channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_DISPLAY, NULL);
++    channel_class->handle_message = red_channel_client_handle_message;
++
++    channel_class->send_item = stream_channel_send_item;
++}
++
++static void
++stream_channel_init(StreamChannel *channel)
++{
++}
+diff --git a/server/stream-channel.h b/server/stream-channel.h
+new file mode 100644
+index 000000000..e50e17e9e
+--- /dev/null
++++ b/server/stream-channel.h
+@@ -0,0 +1,53 @@
++/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
++/*
++   Copyright (C) 2017 Red Hat, Inc.
++
++   This library is free software; you can redistribute it and/or
++   modify it under the terms of the GNU Lesser General Public
++   License as published by the Free Software Foundation; either
++   version 2.1 of the License, or (at your option) any later version.
++
++   This library is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++   Lesser General Public License for more details.
++
++   You should have received a copy of the GNU Lesser General Public
++   License along with this library; if not, see <http://www.gnu.org/licenses/>.
++*/
++
++#ifndef STREAM_CHANNEL_H_
++#define STREAM_CHANNEL_H_
++
++#include "red-channel.h"
++
++G_BEGIN_DECLS
++
++/**
++ * This type it's a RedChannel class which implement display
++ * channel with input only by stream.
++ * A pointer to StreamChannel can be converted to a RedChannel.
++ */
++typedef struct StreamChannel StreamChannel;
++typedef struct StreamChannelClass StreamChannelClass;
++
++#define TYPE_STREAM_CHANNEL stream_channel_get_type()
++
++#define STREAM_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_STREAM_CHANNEL, StreamChannel))
++#define STREAM_CHANNEL_CLASS(klass) \
++    (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_STREAM_CHANNEL, StreamChannelClass))
++#define IS_STREAM_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_STREAM_CHANNEL))
++#define IS_STREAM_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_STREAM_CHANNEL))
++#define STREAM_CHANNEL_GET_CLASS(obj) \
++    (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_STREAM_CHANNEL, StreamChannelClass))
++
++GType stream_channel_get_type(void) G_GNUC_CONST;
++
++/**
++ * Create StreamChannel.
++ */
++StreamChannel* stream_channel_new(RedsState *server, uint32_t id);
++
++G_END_DECLS
++
++#endif /* STREAM_CHANNEL_H_ */
diff --git a/SOURCES/0009-stream-channel-Start-implementing-DisplayChannel-pro.patch b/SOURCES/0009-stream-channel-Start-implementing-DisplayChannel-pro.patch
new file mode 100644
index 0000000..d35ed88
--- /dev/null
+++ b/SOURCES/0009-stream-channel-Start-implementing-DisplayChannel-pro.patch
@@ -0,0 +1,88 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Tue, 14 Mar 2017 12:12:22 +0000
+Subject: [spice-server] stream-channel: Start implementing DisplayChannel
+ properly
+
+Handle messages from clients.
+Send some messages to clients.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
+---
+ server/stream-channel.c | 41 ++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 40 insertions(+), 1 deletion(-)
+
+diff --git a/server/stream-channel.c b/server/stream-channel.c
+index fd735f562..df6936513 100644
+--- a/server/stream-channel.c
++++ b/server/stream-channel.c
+@@ -113,6 +113,25 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
+     red_channel_client_begin_send_message(rcc);
+ }
+ 
++static bool
++handle_message(RedChannelClient *rcc, uint16_t type, uint32_t size, void *msg)
++{
++    switch (type) {
++    case SPICE_MSGC_DISPLAY_INIT:
++    case SPICE_MSGC_DISPLAY_PREFERRED_COMPRESSION:
++        return true;
++    case SPICE_MSGC_DISPLAY_STREAM_REPORT:
++        /* TODO these will help tune the streaming reducing/increasing quality */
++        return true;
++    case SPICE_MSGC_DISPLAY_GL_DRAW_DONE:
++        /* client should not send this message */
++        return false;
++    default:
++        return red_channel_client_handle_message(rcc, type, size, msg);
++    }
++}
++
++
+ StreamChannel*
+ stream_channel_new(RedsState *server, uint32_t id)
+ {
+@@ -138,6 +157,22 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStrea
+ 
+     client = stream_channel_client_new(channel, red_client, stream, migration, caps);
+     spice_return_if_fail(client != NULL);
++
++    // TODO set capabilities like  SPICE_DISPLAY_CAP_MONITORS_CONFIG
++    // see guest_set_client_capabilities
++    RedChannelClient *rcc = RED_CHANNEL_CLIENT(client);
++    red_channel_client_push_set_ack(rcc);
++
++    // TODO what should happen on migration, dcc return if on migration wait ??
++    red_channel_client_ack_zero_messages_window(rcc);
++
++    // "emulate" dcc_start
++    // TODO only if "surface"
++    red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES);
++    // TODO red_surface_create_item_new
++    // TODO surface data ??
++    // TODO monitor configs ??
++    red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_MARK);
+ }
+ 
+ static void
+@@ -152,6 +187,10 @@ stream_channel_constructed(GObject *object)
+     client_cbs.connect = stream_channel_connect;
+     red_channel_register_client_cbs(red_channel, &client_cbs, NULL);
+ 
++    // TODO, send monitor to support multiple monitors in the future
++//    red_channel_set_cap(red_channel, SPICE_DISPLAY_CAP_MONITORS_CONFIG);
++    red_channel_set_cap(red_channel, SPICE_DISPLAY_CAP_STREAM_REPORT);
++
+     reds_register_channel(reds, red_channel);
+ }
+ 
+@@ -164,7 +203,7 @@ stream_channel_class_init(StreamChannelClass *klass)
+     object_class->constructed = stream_channel_constructed;
+ 
+     channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_DISPLAY, NULL);
+-    channel_class->handle_message = red_channel_client_handle_message;
++    channel_class->handle_message = handle_message;
+ 
+     channel_class->send_item = stream_channel_send_item;
+ }
diff --git a/SOURCES/0010-stream-device-Create-channel-for-stream-device.patch b/SOURCES/0010-stream-device-Create-channel-for-stream-device.patch
new file mode 100644
index 0000000..41b7341
--- /dev/null
+++ b/SOURCES/0010-stream-device-Create-channel-for-stream-device.patch
@@ -0,0 +1,151 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Sat, 21 Jan 2017 14:02:29 +0000
+Subject: [spice-server] stream-device: Create channel for stream device
+
+So can be used by the device to communicate with the clients.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
+---
+ server/stream-channel.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++--
+ server/stream-device.c  | 14 +++++++++++++
+ 2 files changed, 64 insertions(+), 2 deletions(-)
+
+diff --git a/server/stream-channel.c b/server/stream-channel.c
+index df6936513..baf3d58a6 100644
+--- a/server/stream-channel.c
++++ b/server/stream-channel.c
+@@ -19,6 +19,8 @@
+ #include <config.h>
+ #endif
+ 
++#include <common/generated_server_marshallers.h>
++
+ #include "red-channel-client.h"
+ #include "stream-channel.h"
+ #include "reds.h"
+@@ -64,6 +66,13 @@ struct StreamChannelClass {
+ 
+ G_DEFINE_TYPE(StreamChannel, stream_channel, RED_TYPE_CHANNEL)
+ 
++enum {
++    RED_PIPE_ITEM_TYPE_SURFACE_CREATE = RED_PIPE_ITEM_TYPE_COMMON_LAST,
++    RED_PIPE_ITEM_TYPE_FILL_SURFACE,
++};
++
++#define PRIMARY_SURFACE_ID 0
++
+ static void stream_channel_client_on_disconnect(RedChannelClient *rcc);
+ 
+ static void
+@@ -103,9 +112,46 @@ stream_channel_client_new(StreamChannel *channel, RedClient *client, RedsStream
+ }
+ 
+ static void
++fill_base(SpiceMarshaller *m)
++{
++    SpiceMsgDisplayBase base;
++
++    base.surface_id = PRIMARY_SURFACE_ID;
++    base.box = (SpiceRect) { 0, 0, 1024, 768 };
++    base.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, NULL };
++
++    spice_marshall_DisplayBase(m, &base);
++}
++
++static void
+ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
+ {
++    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
++
+     switch (pipe_item->type) {
++    case RED_PIPE_ITEM_TYPE_SURFACE_CREATE: {
++        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_CREATE);
++        SpiceMsgSurfaceCreate surface_create = {
++            PRIMARY_SURFACE_ID,
++            1024, 768,
++            SPICE_SURFACE_FMT_32_xRGB, SPICE_SURFACE_FLAGS_PRIMARY
++        };
++        spice_marshall_msg_display_surface_create(m, &surface_create);
++        break;
++    }
++    case RED_PIPE_ITEM_TYPE_FILL_SURFACE: {
++        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_FILL);
++
++        fill_base(m);
++
++        SpiceFill fill;
++        fill.brush = (SpiceBrush) { SPICE_BRUSH_TYPE_SOLID, { .color = 0 } };
++        fill.rop_descriptor = SPICE_ROPD_OP_PUT;
++        fill.mask = (SpiceQMask) { 0, { 0, 0 }, NULL };
++        SpiceMarshaller *brush_pat_out, *mask_bitmap_out;
++        spice_marshall_Fill(m, &fill, &brush_pat_out, &mask_bitmap_out);
++        break;
++    }
+     default:
+         spice_error("invalid pipe item type");
+     }
+@@ -169,8 +215,10 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStrea
+     // "emulate" dcc_start
+     // TODO only if "surface"
+     red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES);
+-    // TODO red_surface_create_item_new
+-    // TODO surface data ??
++    // TODO pass proper data
++    red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_SURFACE_CREATE);
++    // surface data
++    red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_FILL_SURFACE);
+     // TODO monitor configs ??
+     red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_MARK);
+ }
+diff --git a/server/stream-device.c b/server/stream-device.c
+index 58edb3d11..0c9173ae0 100644
+--- a/server/stream-device.c
++++ b/server/stream-device.c
+@@ -22,6 +22,8 @@
+ #include <spice/stream-device.h>
+ 
+ #include "char-device.h"
++#include "stream-channel.h"
++#include "reds.h"
+ 
+ #define TYPE_STREAM_DEVICE stream_device_get_type()
+ 
+@@ -37,9 +39,11 @@ typedef struct StreamDeviceClass StreamDeviceClass;
+ 
+ struct StreamDevice {
+     RedCharDevice parent;
++
+     StreamDevHeader hdr;
+     uint8_t hdr_pos;
+     bool has_error;
++    StreamChannel *stream_channel;
+ };
+ 
+ struct StreamDeviceClass {
+@@ -204,7 +208,10 @@ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin)
+ {
+     SpiceCharDeviceInterface *sif;
+ 
++    StreamChannel *stream_channel = stream_channel_new(reds, 1); // TODO id
++
+     StreamDevice *dev = stream_device_new(sin, reds);
++    dev->stream_channel = stream_channel;
+ 
+     sif = spice_char_device_get_interface(sin);
+     if (sif->state) {
+@@ -217,6 +224,13 @@ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin)
+ static void
+ stream_device_dispose(GObject *object)
+ {
++    StreamDevice *dev = STREAM_DEVICE(object);
++
++    if (dev->stream_channel) {
++        // close all current connections and drop the reference
++        red_channel_destroy(RED_CHANNEL(dev->stream_channel));
++        dev->stream_channel = NULL;
++    }
+ }
+ 
+ static void
diff --git a/SOURCES/0011-stream-device-Handle-streaming-data-from-device-to-c.patch b/SOURCES/0011-stream-device-Handle-streaming-data-from-device-to-c.patch
new file mode 100644
index 0000000..2e53f13
--- /dev/null
+++ b/SOURCES/0011-stream-device-Handle-streaming-data-from-device-to-c.patch
@@ -0,0 +1,231 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Sat, 21 Jan 2017 10:47:40 +0000
+Subject: [spice-server] stream-device: Handle streaming data from device to
+ channel
+
+Handle stream data from device sending to the channel.
+The StreamChannel will forward the data to the clients using standard
+DisplayChannel messages, and will create and destroy streams as
+necessary.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
+---
+ server/stream-channel.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++
+ server/stream-channel.h |   8 ++++
+ server/stream-device.c  |   4 +-
+ 3 files changed, 113 insertions(+), 2 deletions(-)
+
+diff --git a/server/stream-channel.c b/server/stream-channel.c
+index baf3d58a6..8931d8794 100644
+--- a/server/stream-channel.c
++++ b/server/stream-channel.c
+@@ -20,11 +20,13 @@
+ #endif
+ 
+ #include <common/generated_server_marshallers.h>
++#include <spice/stream-device.h>
+ 
+ #include "red-channel-client.h"
+ #include "stream-channel.h"
+ #include "reds.h"
+ #include "common-graphics-channel.h"
++#include "display-limits.h"
+ 
+ #define TYPE_STREAM_CHANNEL_CLIENT stream_channel_client_get_type()
+ 
+@@ -46,6 +48,10 @@ typedef struct StreamChannelClientClass StreamChannelClientClass;
+  * to get buffer handling */
+ struct StreamChannelClient {
+     CommonGraphicsChannelClient parent;
++
++    /* current video stream id, <0 if not initialized or
++     * we are not sending a stream */
++    int stream_id;
+ };
+ 
+ struct StreamChannelClientClass {
+@@ -58,6 +64,10 @@ G_DEFINE_TYPE(StreamChannelClient, stream_channel_client, TYPE_COMMON_GRAPHICS_C
+ 
+ struct StreamChannel {
+     RedChannel parent;
++
++    /* current video stream id, <0 if not initialized or
++     * we are not sending a stream */
++    int stream_id;
+ };
+ 
+ struct StreamChannelClass {
+@@ -69,8 +79,22 @@ G_DEFINE_TYPE(StreamChannel, stream_channel, RED_TYPE_CHANNEL)
+ enum {
+     RED_PIPE_ITEM_TYPE_SURFACE_CREATE = RED_PIPE_ITEM_TYPE_COMMON_LAST,
+     RED_PIPE_ITEM_TYPE_FILL_SURFACE,
++    RED_PIPE_ITEM_TYPE_STREAM_CREATE,
++    RED_PIPE_ITEM_TYPE_STREAM_DATA,
++    RED_PIPE_ITEM_TYPE_STREAM_DESTROY,
+ };
+ 
++typedef struct StreamCreateItem {
++    RedPipeItem base;
++    SpiceMsgDisplayStreamCreate stream_create;
++} StreamCreateItem;
++
++typedef struct StreamDataItem {
++    RedPipeItem base;
++    // NOTE: this must be the last field in the structure
++    SpiceMsgDisplayStreamData data;
++} StreamDataItem;
++
+ #define PRIMARY_SURFACE_ID 0
+ 
+ static void stream_channel_client_on_disconnect(RedChannelClient *rcc);
+@@ -86,6 +110,7 @@ stream_channel_client_class_init(StreamChannelClientClass *klass)
+ static void
+ stream_channel_client_init(StreamChannelClient *client)
+ {
++    client->stream_id = -1;
+ }
+ 
+ static void
+@@ -127,6 +152,7 @@ static void
+ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
+ {
+     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
++    StreamChannelClient *client = STREAM_CHANNEL_CLIENT(rcc);
+ 
+     switch (pipe_item->type) {
+     case RED_PIPE_ITEM_TYPE_SURFACE_CREATE: {
+@@ -152,6 +178,32 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
+         spice_marshall_Fill(m, &fill, &brush_pat_out, &mask_bitmap_out);
+         break;
+     }
++    case RED_PIPE_ITEM_TYPE_STREAM_CREATE: {
++        StreamCreateItem *item = SPICE_UPCAST(StreamCreateItem, pipe_item);
++        client->stream_id = item->stream_create.id;
++        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_CREATE);
++        spice_marshall_msg_display_stream_create(m, &item->stream_create);
++        break;
++    }
++    case RED_PIPE_ITEM_TYPE_STREAM_DATA: {
++        StreamDataItem *item = SPICE_UPCAST(StreamDataItem, pipe_item);
++        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA);
++        spice_marshall_msg_display_stream_data(m, &item->data);
++        red_pipe_item_ref(pipe_item);
++        spice_marshaller_add_by_ref_full(m, item->data.data, item->data.data_size,
++                                         marshaller_unref_pipe_item, pipe_item);
++        break;
++    }
++    case RED_PIPE_ITEM_TYPE_STREAM_DESTROY: {
++        if (client->stream_id < 0) {
++            return;
++        }
++        SpiceMsgDisplayStreamDestroy stream_destroy = { client->stream_id };
++        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DESTROY);
++        spice_marshall_msg_display_stream_destroy(m, &stream_destroy);
++        client->stream_id = -1;
++        break;
++    }
+     default:
+         spice_error("invalid pipe item type");
+     }
+@@ -259,4 +311,55 @@ stream_channel_class_init(StreamChannelClass *klass)
+ static void
+ stream_channel_init(StreamChannel *channel)
+ {
++    channel->stream_id = -1;
++}
++
++void
++stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt)
++{
++    RedChannel *red_channel = RED_CHANNEL(channel);
++
++    // send destroy old stream
++    red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_STREAM_DESTROY);
++
++    // TODO send new create surface if required
++
++    // allocate a new stream id
++    channel->stream_id = (channel->stream_id + 1) % NUM_STREAMS;
++
++    // send create stream
++    StreamCreateItem *item = g_new0(StreamCreateItem, 1);
++    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_STREAM_CREATE);
++    item->stream_create.id = channel->stream_id;
++    item->stream_create.flags = SPICE_STREAM_FLAGS_TOP_DOWN;
++    item->stream_create.codec_type = fmt->codec;
++    item->stream_create.stream_width = fmt->width;
++    item->stream_create.stream_height = fmt->height;
++    item->stream_create.src_width = fmt->width;
++    item->stream_create.src_height = fmt->height;
++    item->stream_create.dest = (SpiceRect) { 0, 0, fmt->width, fmt->height };
++    item->stream_create.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, NULL };
++    red_channel_pipes_add(red_channel, &item->base);
++}
++
++void
++stream_channel_send_data(StreamChannel *channel, const void *data, size_t size, uint32_t mm_time)
++{
++    if (channel->stream_id < 0) {
++        // this condition can happen if the guest didn't handle
++        // the format stop that we send so think the stream is still
++        // started
++        return;
++    }
++
++    RedChannel *red_channel = RED_CHANNEL(channel);
++
++    StreamDataItem *item = g_malloc(sizeof(*item) + size);
++    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_STREAM_DATA);
++    item->data.base.id = channel->stream_id;
++    item->data.base.multi_media_time = mm_time;
++    item->data.data_size = size;
++    // TODO try to optimize avoiding the copy
++    memcpy(item->data.data, data, size);
++    red_channel_pipes_add(red_channel, &item->base);
+ }
+diff --git a/server/stream-channel.h b/server/stream-channel.h
+index e50e17e9e..156a75d31 100644
+--- a/server/stream-channel.h
++++ b/server/stream-channel.h
+@@ -48,6 +48,14 @@ GType stream_channel_get_type(void) G_GNUC_CONST;
+  */
+ StreamChannel* stream_channel_new(RedsState *server, uint32_t id);
+ 
++struct StreamMsgFormat;
++
++void stream_channel_change_format(StreamChannel *channel,
++                                  const struct StreamMsgFormat *fmt);
++void stream_channel_send_data(StreamChannel *channel,
++                              const void *data, size_t size,
++                              uint32_t mm_time);
++
+ G_END_DECLS
+ 
+ #endif /* STREAM_CHANNEL_H_ */
+diff --git a/server/stream-device.c b/server/stream-device.c
+index 0c9173ae0..6e78b1a99 100644
+--- a/server/stream-device.c
++++ b/server/stream-device.c
+@@ -164,6 +164,7 @@ handle_msg_format(StreamDevice *dev, SpiceCharDeviceInstance *sin)
+     }
+     fmt.width = GUINT32_FROM_LE(fmt.width);
+     fmt.height = GUINT32_FROM_LE(fmt.height);
++    stream_channel_change_format(dev->stream_channel, &fmt);
+ 
+     return true;
+ }
+@@ -176,11 +177,10 @@ handle_msg_data(StreamDevice *dev, SpiceCharDeviceInstance *sin)
+     while (1) {
+         uint8_t buf[16 * 1024];
+         n = sif->read(sin, buf, sizeof(buf));
+-        /* TODO */
+-        spice_debug("read %d bytes from device", n);
+         if (n <= 0) {
+             break;
+         }
++        stream_channel_send_data(dev->stream_channel, buf, n, reds_get_mm_time());
+         dev->hdr.size -= n;
+     }
+ 
diff --git a/SOURCES/0012-stream-channel-Allows-not-fixed-size.patch b/SOURCES/0012-stream-channel-Allows-not-fixed-size.patch
new file mode 100644
index 0000000..354002e
--- /dev/null
+++ b/SOURCES/0012-stream-channel-Allows-not-fixed-size.patch
@@ -0,0 +1,115 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Sat, 21 Jan 2017 19:03:11 +0000
+Subject: [spice-server] stream-channel: Allows not fixed size
+
+Remove the fixed size stream and support any display size.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
+---
+ server/stream-channel.c | 31 +++++++++++++++++++++++++------
+ 1 file changed, 25 insertions(+), 6 deletions(-)
+
+diff --git a/server/stream-channel.c b/server/stream-channel.c
+index 8931d8794..59f566f50 100644
+--- a/server/stream-channel.c
++++ b/server/stream-channel.c
+@@ -68,6 +68,8 @@ struct StreamChannel {
+     /* current video stream id, <0 if not initialized or
+      * we are not sending a stream */
+     int stream_id;
++    /* size of the current video stream */
++    unsigned width, height;
+ };
+ 
+ struct StreamChannelClass {
+@@ -78,6 +80,7 @@ G_DEFINE_TYPE(StreamChannel, stream_channel, RED_TYPE_CHANNEL)
+ 
+ enum {
+     RED_PIPE_ITEM_TYPE_SURFACE_CREATE = RED_PIPE_ITEM_TYPE_COMMON_LAST,
++    RED_PIPE_ITEM_TYPE_SURFACE_DESTROY,
+     RED_PIPE_ITEM_TYPE_FILL_SURFACE,
+     RED_PIPE_ITEM_TYPE_STREAM_CREATE,
+     RED_PIPE_ITEM_TYPE_STREAM_DATA,
+@@ -137,12 +140,12 @@ stream_channel_client_new(StreamChannel *channel, RedClient *client, RedsStream
+ }
+ 
+ static void
+-fill_base(SpiceMarshaller *m)
++fill_base(SpiceMarshaller *m, const StreamChannel *channel)
+ {
+     SpiceMsgDisplayBase base;
+ 
+     base.surface_id = PRIMARY_SURFACE_ID;
+-    base.box = (SpiceRect) { 0, 0, 1024, 768 };
++    base.box = (SpiceRect) { 0, 0, channel->width, channel->height };
+     base.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, NULL };
+ 
+     spice_marshall_DisplayBase(m, &base);
+@@ -153,22 +156,29 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
+ {
+     SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
+     StreamChannelClient *client = STREAM_CHANNEL_CLIENT(rcc);
++    StreamChannel *channel = STREAM_CHANNEL(red_channel_client_get_channel(rcc));
+ 
+     switch (pipe_item->type) {
+     case RED_PIPE_ITEM_TYPE_SURFACE_CREATE: {
+         red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_CREATE);
+         SpiceMsgSurfaceCreate surface_create = {
+             PRIMARY_SURFACE_ID,
+-            1024, 768,
++            channel->width, channel->height,
+             SPICE_SURFACE_FMT_32_xRGB, SPICE_SURFACE_FLAGS_PRIMARY
+         };
+         spice_marshall_msg_display_surface_create(m, &surface_create);
+         break;
+     }
++    case RED_PIPE_ITEM_TYPE_SURFACE_DESTROY: {
++        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_DESTROY);
++        SpiceMsgSurfaceDestroy surface_destroy = { PRIMARY_SURFACE_ID };
++        spice_marshall_msg_display_surface_destroy(m, &surface_destroy);
++        break;
++    }
+     case RED_PIPE_ITEM_TYPE_FILL_SURFACE: {
+         red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_FILL);
+ 
+-        fill_base(m);
++        fill_base(m, channel);
+ 
+         SpiceFill fill;
+         fill.brush = (SpiceBrush) { SPICE_BRUSH_TYPE_SOLID, { .color = 0 } };
+@@ -267,7 +277,7 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStrea
+     // "emulate" dcc_start
+     // TODO only if "surface"
+     red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES);
+-    // TODO pass proper data
++    // pass proper data
+     red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_SURFACE_CREATE);
+     // surface data
+     red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_FILL_SURFACE);
+@@ -312,6 +322,8 @@ static void
+ stream_channel_init(StreamChannel *channel)
+ {
+     channel->stream_id = -1;
++    channel->width = 1024;
++    channel->height = 768;
+ }
+ 
+ void
+@@ -322,7 +334,14 @@ stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt)
+     // send destroy old stream
+     red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_STREAM_DESTROY);
+ 
+-    // TODO send new create surface if required
++    // send new create surface if required
++    if (channel->width != fmt->width || channel->height != fmt->height) {
++        channel->width = fmt->width;
++        channel->height = fmt->height;
++        red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_DESTROY);
++        red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_CREATE);
++        // TODO monitors config ??
++    }
+ 
+     // allocate a new stream id
+     channel->stream_id = (channel->stream_id + 1) % NUM_STREAMS;
diff --git a/SOURCES/0013-stream-channel-Allows-to-register-callback-to-get-ne.patch b/SOURCES/0013-stream-channel-Allows-to-register-callback-to-get-ne.patch
new file mode 100644
index 0000000..7ed0b16
--- /dev/null
+++ b/SOURCES/0013-stream-channel-Allows-to-register-callback-to-get-ne.patch
@@ -0,0 +1,67 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Sat, 21 Jan 2017 19:08:12 +0000
+Subject: [spice-server] stream-channel: Allows to register callback to get new
+ stream request
+
+The channel needs to communicate when it receive a new
+stream request from the guest.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
+---
+ server/stream-channel.c | 12 ++++++++++++
+ server/stream-channel.h |  6 ++++++
+ 2 files changed, 18 insertions(+)
+
+diff --git a/server/stream-channel.c b/server/stream-channel.c
+index 59f566f50..be6f7c7d1 100644
+--- a/server/stream-channel.c
++++ b/server/stream-channel.c
+@@ -70,6 +70,10 @@ struct StreamChannel {
+     int stream_id;
+     /* size of the current video stream */
+     unsigned width, height;
++
++    /* callback to notify when a stream should be started or stopped */
++    stream_channel_start_proc start_cb;
++    void *start_opaque;
+ };
+ 
+ struct StreamChannelClass {
+@@ -382,3 +386,11 @@ stream_channel_send_data(StreamChannel *channel, const void *data, size_t size,
+     memcpy(item->data.data, data, size);
+     red_channel_pipes_add(red_channel, &item->base);
+ }
++
++void
++stream_channel_register_start_cb(StreamChannel *channel,
++                                 stream_channel_start_proc cb, void *opaque)
++{
++    channel->start_cb = cb;
++    channel->start_opaque = opaque;
++}
+diff --git a/server/stream-channel.h b/server/stream-channel.h
+index 156a75d31..ba098df49 100644
+--- a/server/stream-channel.h
++++ b/server/stream-channel.h
+@@ -49,6 +49,7 @@ GType stream_channel_get_type(void) G_GNUC_CONST;
+ StreamChannel* stream_channel_new(RedsState *server, uint32_t id);
+ 
+ struct StreamMsgFormat;
++struct StreamMsgStartStop;
+ 
+ void stream_channel_change_format(StreamChannel *channel,
+                                   const struct StreamMsgFormat *fmt);
+@@ -56,6 +57,11 @@ void stream_channel_send_data(StreamChannel *channel,
+                               const void *data, size_t size,
+                               uint32_t mm_time);
+ 
++typedef void (*stream_channel_start_proc)(void *opaque, struct StreamMsgStartStop *start,
++                                          StreamChannel *channel);
++void stream_channel_register_start_cb(StreamChannel *channel,
++                                      stream_channel_start_proc cb, void *opaque);
++
+ G_END_DECLS
+ 
+ #endif /* STREAM_CHANNEL_H_ */
diff --git a/SOURCES/0014-stream-channel-Support-client-connection-disconnecti.patch b/SOURCES/0014-stream-channel-Support-client-connection-disconnecti.patch
new file mode 100644
index 0000000..d71ee50
--- /dev/null
+++ b/SOURCES/0014-stream-channel-Support-client-connection-disconnecti.patch
@@ -0,0 +1,131 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Sat, 21 Jan 2017 19:03:19 +0000
+Subject: [spice-server] stream-channel: Support client
+ connection/disconnection
+
+When a new client is connected we must restart the stream so new
+clients can receive correct data without having to wait for the
+next full screen (which on idle screen could take ages).
+On disconnection we should tell the guest to stop streaming
+not wasting resources to stream not needed data.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
+---
+ server/stream-channel.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 81 insertions(+)
+
+diff --git a/server/stream-channel.c b/server/stream-channel.c
+index be6f7c7d1..2ad9ebae3 100644
+--- a/server/stream-channel.c
++++ b/server/stream-channel.c
+@@ -121,8 +121,32 @@ stream_channel_client_init(StreamChannelClient *client)
+ }
+ 
+ static void
++request_new_stream(StreamChannel *channel, StreamMsgStartStop *start)
++{
++    if (channel->start_cb) {
++        channel->start_cb(channel->start_opaque, start, channel);
++    }
++}
++
++static void
+ stream_channel_client_on_disconnect(RedChannelClient *rcc)
+ {
++    RedChannel *red_channel = red_channel_client_get_channel(rcc);
++
++    // if there are still some client connected keep streaming
++    // TODO, maybe would be worth sending new codecs if they are better
++    if (red_channel_is_connected(red_channel)) {
++        return;
++    }
++
++    StreamChannel *channel = STREAM_CHANNEL(red_channel);
++    channel->stream_id = -1;
++    channel->width = 0;
++    channel->height = 0;
++
++    // send stream stop to device
++    StreamMsgStartStop stop = { 0, };
++    request_new_stream(channel, &stop);
+ }
+ 
+ static StreamChannelClient*
+@@ -258,18 +282,75 @@ stream_channel_new(RedsState *server, uint32_t id)
+                         NULL);
+ }
+ 
++#define MAX_SUPPORTED_CODECS SPICE_VIDEO_CODEC_TYPE_ENUM_END
++
++// find common codecs supported by all clients
++static uint8_t
++stream_channel_get_supported_codecs(StreamChannel *channel, uint8_t *out_codecs)
++{
++    RedChannelClient *rcc;
++    int codec;
++
++    static const uint16_t codec2cap[] = {
++        0, // invalid
++        SPICE_DISPLAY_CAP_CODEC_MJPEG,
++        SPICE_DISPLAY_CAP_CODEC_VP8,
++        SPICE_DISPLAY_CAP_CODEC_H264,
++        SPICE_DISPLAY_CAP_CODEC_VP9,
++    };
++
++    bool supported[SPICE_N_ELEMENTS(codec2cap)];
++
++    for (codec = 0; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
++        supported[codec] = true;
++    }
++
++    FOREACH_CLIENT(channel, rcc) {
++        for (codec = 1; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
++            // if do not support codec delete from list
++            if (!red_channel_client_test_remote_cap(rcc, codec2cap[codec])) {
++                supported[codec] = false;
++            }
++        }
++    }
++
++    // surely mjpeg is supported
++    supported[SPICE_VIDEO_CODEC_TYPE_MJPEG] = true;
++
++    int num = 0;
++    for (codec = 1; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
++        if (supported[codec]) {
++            out_codecs[num++] = codec;
++        }
++    }
++
++    return num;
++}
++
+ static void
+ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStream *stream,
+                        int migration, RedChannelCapabilities *caps)
+ {
+     StreamChannel *channel = STREAM_CHANNEL(red_channel);
+     StreamChannelClient *client;
++    struct {
++        StreamMsgStartStop base;
++        uint8_t codecs_buffer[MAX_SUPPORTED_CODECS];
++    } start_msg;
++    StreamMsgStartStop *const start = &start_msg.base;
+ 
+     spice_return_if_fail(stream != NULL);
+ 
+     client = stream_channel_client_new(channel, red_client, stream, migration, caps);
+     spice_return_if_fail(client != NULL);
+ 
++    // request new stream
++    start->num_codecs = stream_channel_get_supported_codecs(channel, start->codecs);
++    // send in any case, even if list is not changed
++    // notify device about changes
++    request_new_stream(channel, start);
++
++
+     // TODO set capabilities like  SPICE_DISPLAY_CAP_MONITORS_CONFIG
+     // see guest_set_client_capabilities
+     RedChannelClient *rcc = RED_CHANNEL_CLIENT(client);
diff --git a/SOURCES/0015-stream-channel-Do-not-show-an-empty-blank-screen-on-.patch b/SOURCES/0015-stream-channel-Do-not-show-an-empty-blank-screen-on-.patch
new file mode 100644
index 0000000..8d52cf4
--- /dev/null
+++ b/SOURCES/0015-stream-channel-Do-not-show-an-empty-blank-screen-on-.patch
@@ -0,0 +1,64 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Sat, 21 Jan 2017 09:01:18 +0000
+Subject: [spice-server] stream-channel: Do not show an empty blank screen on
+ start
+
+Start showing something when we have a surface and stream
+instead of showing a blank screen which is now not useful.
+Was useful for debugging purposes to understand that the
+new channel was sending messages correctly to client and
+client could handle them.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
+---
+ server/stream-channel.c | 16 ++++++++++++----
+ 1 file changed, 12 insertions(+), 4 deletions(-)
+
+diff --git a/server/stream-channel.c b/server/stream-channel.c
+index 2ad9ebae3..e89563b82 100644
+--- a/server/stream-channel.c
++++ b/server/stream-channel.c
+@@ -360,8 +360,13 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStrea
+     red_channel_client_ack_zero_messages_window(rcc);
+ 
+     // "emulate" dcc_start
+-    // TODO only if "surface"
+     red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES);
++
++    // only if "surface"
++    if (channel->width == 0 || channel->height == 0) {
++        return;
++    }
++
+     // pass proper data
+     red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_SURFACE_CREATE);
+     // surface data
+@@ -407,8 +412,8 @@ static void
+ stream_channel_init(StreamChannel *channel)
+ {
+     channel->stream_id = -1;
+-    channel->width = 1024;
+-    channel->height = 768;
++    channel->width = 0;
++    channel->height = 0;
+ }
+ 
+ void
+@@ -421,11 +426,14 @@ stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt)
+ 
+     // send new create surface if required
+     if (channel->width != fmt->width || channel->height != fmt->height) {
++        if (channel->width != 0 && channel->height != 0) {
++            red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_DESTROY);
++        }
+         channel->width = fmt->width;
+         channel->height = fmt->height;
+-        red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_DESTROY);
+         red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_CREATE);
+         // TODO monitors config ??
++        red_channel_pipes_add_empty_msg(red_channel, SPICE_MSG_DISPLAY_MARK);
+     }
+ 
+     // allocate a new stream id
diff --git a/SOURCES/0016-char-device-Do-not-stop-and-clear-interface-on-reset.patch b/SOURCES/0016-char-device-Do-not-stop-and-clear-interface-on-reset.patch
new file mode 100644
index 0000000..adb8442
--- /dev/null
+++ b/SOURCES/0016-char-device-Do-not-stop-and-clear-interface-on-reset.patch
@@ -0,0 +1,67 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Tue, 24 Jan 2017 11:36:27 +0000
+Subject: [spice-server] char-device: Do not stop and clear interface on reset
+
+Currently, red_char_device_reset() stops the device, clears all pending
+messages, and clears its device instance. After this function is called,
+the char device will not work again until it is assigned a new device
+instance and restarted. This is fine for the vdagent char device, which
+is currently the only user of this function. But for the stream device,
+we want to be able to reset the char device to a working state (e.g.
+clear all pending messages, etc) without stopping or disabling the char
+device. So this function will now only reset the char device to a clean
+working state, and the _stop() and _reset_dev_instance() calls will be
+moved up to the caller.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
+---
+ server/char-device.c | 2 --
+ server/reds.c        | 6 +++++-
+ 2 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/server/char-device.c b/server/char-device.c
+index 658f9f364..f8a098bd8 100644
+--- a/server/char-device.c
++++ b/server/char-device.c
+@@ -823,7 +823,6 @@ void red_char_device_reset(RedCharDevice *dev)
+     GList *client_item;
+     RedCharDeviceWriteBuffer *buf;
+ 
+-    red_char_device_stop(dev);
+     dev->priv->wait_for_migrate_data = FALSE;
+     spice_debug("char device %p", dev);
+     while ((buf = g_queue_pop_tail(&dev->priv->write_queue))) {
+@@ -845,7 +844,6 @@ void red_char_device_reset(RedCharDevice *dev)
+         dev_client->num_client_tokens += dev_client->num_client_tokens_free;
+         dev_client->num_client_tokens_free = 0;
+     }
+-    red_char_device_reset_dev_instance(dev, NULL);
+ }
+ 
+ void red_char_device_wakeup(RedCharDevice *dev)
+diff --git a/server/reds.c b/server/reds.c
+index b24f61ab2..401d242fb 100644
+--- a/server/reds.c
++++ b/server/reds.c
+@@ -470,6 +470,7 @@ static void reds_reset_vdp(RedsState *reds)
+ {
+     RedCharDeviceVDIPort *dev = reds->agent_dev;
+     SpiceCharDeviceInterface *sif;
++    RedCharDevice *char_dev;
+ 
+     dev->priv->read_state = VDI_PORT_READ_STATE_READ_HEADER;
+     dev->priv->receive_pos = (uint8_t *)&dev->priv->vdi_chunk_header;
+@@ -502,7 +503,10 @@ static void reds_reset_vdp(RedsState *reds)
+      *  The tokens are also reset to avoid mismatch in upon agent reconnection.
+      */
+     dev->priv->agent_attached = FALSE;
+-    red_char_device_reset(RED_CHAR_DEVICE(dev));
++    char_dev = RED_CHAR_DEVICE(dev);
++    red_char_device_stop(char_dev);
++    red_char_device_reset(char_dev);
++    red_char_device_reset_dev_instance(char_dev, NULL);
+ 
+     sif = spice_char_device_get_interface(reds->vdagent);
+     if (sif->state) {
diff --git a/SOURCES/0017-stream-device-Start-supporting-resetting-device-when.patch b/SOURCES/0017-stream-device-Start-supporting-resetting-device-when.patch
new file mode 100644
index 0000000..b26eb53
--- /dev/null
+++ b/SOURCES/0017-stream-device-Start-supporting-resetting-device-when.patch
@@ -0,0 +1,166 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Wed, 25 Jan 2017 22:42:00 +0000
+Subject: [spice-server] stream-device: Start supporting resetting device when
+ close/open on guest
+
+When guest close the device the host device has to be reset too.
+This make easier to restart the guest device which can happen in case
+of reboot, agent issues or if we want to update the agent.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
+---
+ server/stream-channel.c | 34 ++++++++++++++++++++++++++++++++++
+ server/stream-channel.h |  7 ++++++-
+ server/stream-device.c  | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 89 insertions(+), 1 deletion(-)
+
+diff --git a/server/stream-channel.c b/server/stream-channel.c
+index e89563b82..51b8badf9 100644
+--- a/server/stream-channel.c
++++ b/server/stream-channel.c
+@@ -483,3 +483,37 @@ stream_channel_register_start_cb(StreamChannel *channel,
+     channel->start_cb = cb;
+     channel->start_opaque = opaque;
+ }
++
++void
++stream_channel_reset(StreamChannel *channel)
++{
++    struct {
++        StreamMsgStartStop base;
++        uint8_t codecs_buffer[MAX_SUPPORTED_CODECS];
++    } start_msg;
++    StreamMsgStartStop *const start = &start_msg.base;
++    RedChannel *red_channel = RED_CHANNEL(channel);
++
++    // send destroy old stream
++    red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_STREAM_DESTROY);
++
++    // destroy display surface
++    if (channel->width != 0 && channel->height != 0) {
++        red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_DESTROY);
++    }
++
++    channel->stream_id = -1;
++    channel->width = 0;
++    channel->height = 0;
++
++    if (!red_channel_is_connected(red_channel)) {
++        return;
++    }
++
++    // try to request a new stream, this should start a new stream
++    // if the guest is connected to the device and a client is already connected
++    start->num_codecs = stream_channel_get_supported_codecs(channel, start->codecs);
++    // send in any case, even if list is not changed
++    // notify device about changes
++    request_new_stream(channel, start);
++}
+diff --git a/server/stream-channel.h b/server/stream-channel.h
+index ba098df49..bd075a951 100644
+--- a/server/stream-channel.h
++++ b/server/stream-channel.h
+@@ -48,7 +48,12 @@ GType stream_channel_get_type(void) G_GNUC_CONST;
+  */
+ StreamChannel* stream_channel_new(RedsState *server, uint32_t id);
+ 
+-struct StreamMsgFormat;
++/**
++ * Reset channel at initial state
++ */
++void stream_channel_reset(StreamChannel *channel);
++
++struct StreamMsgStreamFormat;
+ struct StreamMsgStartStop;
+ 
+ void stream_channel_change_format(StreamChannel *channel,
+diff --git a/server/stream-device.c b/server/stream-device.c
+index 6e78b1a99..9e401f8ed 100644
+--- a/server/stream-device.c
++++ b/server/stream-device.c
+@@ -43,6 +43,7 @@ struct StreamDevice {
+     StreamDevHeader hdr;
+     uint8_t hdr_pos;
+     bool has_error;
++    bool opened;
+     StreamChannel *stream_channel;
+ };
+ 
+@@ -203,6 +204,35 @@ stream_device_remove_client(RedCharDevice *self, RedClient *client)
+ {
+ }
+ 
++static void
++stream_device_stream_start(void *opaque, StreamMsgStartStop *start,
++                           StreamChannel *stream_channel G_GNUC_UNUSED)
++{
++    StreamDevice *dev = (StreamDevice *) opaque;
++
++    if (!dev->opened) {
++        return;
++    }
++
++    int msg_size = sizeof(*start) + sizeof(start->codecs[0]) * start->num_codecs;
++    int total_size = sizeof(StreamDevHeader) + msg_size;
++
++    RedCharDevice *char_dev = RED_CHAR_DEVICE(dev);
++    RedCharDeviceWriteBuffer *buf =
++        red_char_device_write_buffer_get_server_no_token(char_dev, total_size);
++    buf->buf_used = total_size;
++
++    StreamDevHeader *hdr = (StreamDevHeader *)buf->buf;
++    hdr->protocol_version = STREAM_DEVICE_PROTOCOL;
++    hdr->padding = 0;
++    hdr->type = GUINT16_TO_LE(STREAM_TYPE_START_STOP);
++    hdr->size = GUINT32_TO_LE(msg_size);
++
++    memcpy(&hdr[1], start, msg_size);
++
++    red_char_device_write_buffer_add(char_dev, buf);
++}
++
+ RedCharDevice *
+ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin)
+ {
+@@ -212,6 +242,7 @@ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin)
+ 
+     StreamDevice *dev = stream_device_new(sin, reds);
+     dev->stream_channel = stream_channel;
++    stream_channel_register_start_cb(stream_channel, stream_device_stream_start, dev);
+ 
+     sif = spice_char_device_get_interface(sin);
+     if (sif->state) {
+@@ -234,6 +265,23 @@ stream_device_dispose(GObject *object)
+ }
+ 
+ static void
++stream_device_port_event(RedCharDevice *char_dev, uint8_t event)
++{
++    if (event != SPICE_PORT_EVENT_OPENED && event != SPICE_PORT_EVENT_CLOSED) {
++        return;
++    }
++
++    StreamDevice *dev = STREAM_DEVICE(char_dev);
++
++    // reset device and channel on close/open
++    dev->opened = (event == SPICE_PORT_EVENT_OPENED);
++    dev->hdr_pos = 0;
++    dev->has_error = false;
++    red_char_device_reset(char_dev);
++    stream_channel_reset(dev->stream_channel);
++}
++
++static void
+ stream_device_class_init(StreamDeviceClass *klass)
+ {
+     GObjectClass *object_class = G_OBJECT_CLASS(klass);
+@@ -245,6 +293,7 @@ stream_device_class_init(StreamDeviceClass *klass)
+     char_dev_class->send_msg_to_client = stream_device_send_msg_to_client;
+     char_dev_class->send_tokens_to_client = stream_device_send_tokens_to_client;
+     char_dev_class->remove_client = stream_device_remove_client;
++    char_dev_class->port_event = stream_device_port_event;
+ }
+ 
+ static void
diff --git a/SOURCES/0018-stream-device-Create-channel-when-needed.patch b/SOURCES/0018-stream-device-Create-channel-when-needed.patch
new file mode 100644
index 0000000..5d9cec6
--- /dev/null
+++ b/SOURCES/0018-stream-device-Create-channel-when-needed.patch
@@ -0,0 +1,89 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Fri, 25 Aug 2017 00:02:54 +0100
+Subject: [spice-server] stream-device: Create channel when needed
+
+This allows a better id allocation as devices are created after
+fixed ones.
+Also will allow to support more easily multiple monitor.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
+---
+ server/stream-device.c | 38 ++++++++++++++++++++++++++++++++------
+ 1 file changed, 32 insertions(+), 6 deletions(-)
+
+diff --git a/server/stream-device.c b/server/stream-device.c
+index 9e401f8ed..ae108788b 100644
+--- a/server/stream-device.c
++++ b/server/stream-device.c
+@@ -72,7 +72,7 @@ stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *si
+     int n;
+     bool handled = false;
+ 
+-    if (dev->has_error) {
++    if (dev->has_error || !dev->stream_channel) {
+         return NULL;
+     }
+ 
+@@ -238,11 +238,7 @@ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin)
+ {
+     SpiceCharDeviceInterface *sif;
+ 
+-    StreamChannel *stream_channel = stream_channel_new(reds, 1); // TODO id
+-
+     StreamDevice *dev = stream_device_new(sin, reds);
+-    dev->stream_channel = stream_channel;
+-    stream_channel_register_start_cb(stream_channel, stream_device_stream_start, dev);
+ 
+     sif = spice_char_device_get_interface(sin);
+     if (sif->state) {
+@@ -265,6 +261,33 @@ stream_device_dispose(GObject *object)
+ }
+ 
+ static void
++allocate_channels(StreamDevice *dev)
++{
++    if (dev->stream_channel) {
++        return;
++    }
++
++    SpiceServer* reds = red_char_device_get_server(RED_CHAR_DEVICE(dev));
++
++    int id = reds_get_free_channel_id(reds, SPICE_CHANNEL_DISPLAY);
++    g_return_if_fail(id >= 0);
++
++    StreamChannel *stream_channel = stream_channel_new(reds, id);
++
++    dev->stream_channel = stream_channel;
++
++    stream_channel_register_start_cb(stream_channel, stream_device_stream_start, dev);
++}
++
++static void
++reset_channels(StreamDevice *dev)
++{
++    if (dev->stream_channel) {
++        stream_channel_reset(dev->stream_channel);
++    }
++}
++
++static void
+ stream_device_port_event(RedCharDevice *char_dev, uint8_t event)
+ {
+     if (event != SPICE_PORT_EVENT_OPENED && event != SPICE_PORT_EVENT_CLOSED) {
+@@ -275,10 +298,13 @@ stream_device_port_event(RedCharDevice *char_dev, uint8_t event)
+ 
+     // reset device and channel on close/open
+     dev->opened = (event == SPICE_PORT_EVENT_OPENED);
++    if (dev->opened) {
++        allocate_channels(dev);
++    }
+     dev->hdr_pos = 0;
+     dev->has_error = false;
+     red_char_device_reset(char_dev);
+-    stream_channel_reset(dev->stream_channel);
++    reset_channels(dev);
+ }
+ 
+ static void
diff --git a/SOURCES/0019-stream-device-Limit-sending-queue-from-guest-to-serv.patch b/SOURCES/0019-stream-device-Limit-sending-queue-from-guest-to-serv.patch
new file mode 100644
index 0000000..a242052
--- /dev/null
+++ b/SOURCES/0019-stream-device-Limit-sending-queue-from-guest-to-serv.patch
@@ -0,0 +1,206 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Wed, 19 Apr 2017 16:24:54 +0100
+Subject: [spice-server] stream-device: Limit sending queue from guest to
+ server
+
+Do not allow the guest to fill host memory.
+Also having a huge queue mainly cause to have a higher video
+latency.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
+---
+ server/stream-channel.c | 41 ++++++++++++++++++++++++++++++++++++++++-
+ server/stream-channel.h | 10 ++++++++++
+ server/stream-device.c  | 35 ++++++++++++++++++++++++++++++++++-
+ 3 files changed, 84 insertions(+), 2 deletions(-)
+
+diff --git a/server/stream-channel.c b/server/stream-channel.c
+index 51b8badf9..ec4bf021d 100644
+--- a/server/stream-channel.c
++++ b/server/stream-channel.c
+@@ -71,9 +71,15 @@ struct StreamChannel {
+     /* size of the current video stream */
+     unsigned width, height;
+ 
++    StreamQueueStat queue_stat;
++
+     /* callback to notify when a stream should be started or stopped */
+     stream_channel_start_proc start_cb;
+     void *start_opaque;
++
++    /* callback to notify when queue statistics changes */
++    stream_channel_queue_stat_proc queue_cb;
++    void *queue_opaque;
+ };
+ 
+ struct StreamChannelClass {
+@@ -98,6 +104,7 @@ typedef struct StreamCreateItem {
+ 
+ typedef struct StreamDataItem {
+     RedPipeItem base;
++    StreamChannel *channel;
+     // NOTE: this must be the last field in the structure
+     SpiceMsgDisplayStreamData data;
+ } StreamDataItem;
+@@ -454,6 +461,27 @@ stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt)
+     red_channel_pipes_add(red_channel, &item->base);
+ }
+ 
++static inline void
++stream_channel_update_queue_stat(StreamChannel *channel,
++                                 int32_t num_diff, int32_t size_diff)
++{
++    channel->queue_stat.num_items += num_diff;
++    channel->queue_stat.size += size_diff;
++    if (channel->queue_cb) {
++        channel->queue_cb(channel->queue_opaque, &channel->queue_stat, channel);
++    }
++}
++
++static void
++data_item_free(RedPipeItem *base)
++{
++    StreamDataItem *pipe_item = SPICE_UPCAST(StreamDataItem, base);
++
++    stream_channel_update_queue_stat(pipe_item->channel, -1, -pipe_item->data.data_size);
++
++    g_free(pipe_item);
++}
++
+ void
+ stream_channel_send_data(StreamChannel *channel, const void *data, size_t size, uint32_t mm_time)
+ {
+@@ -467,10 +495,13 @@ stream_channel_send_data(StreamChannel *channel, const void *data, size_t size,
+     RedChannel *red_channel = RED_CHANNEL(channel);
+ 
+     StreamDataItem *item = g_malloc(sizeof(*item) + size);
+-    red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_STREAM_DATA);
++    red_pipe_item_init_full(&item->base, RED_PIPE_ITEM_TYPE_STREAM_DATA,
++                            data_item_free);
+     item->data.base.id = channel->stream_id;
+     item->data.base.multi_media_time = mm_time;
+     item->data.data_size = size;
++    item->channel = channel;
++    stream_channel_update_queue_stat(channel, 1, size);
+     // TODO try to optimize avoiding the copy
+     memcpy(item->data.data, data, size);
+     red_channel_pipes_add(red_channel, &item->base);
+@@ -485,6 +516,14 @@ stream_channel_register_start_cb(StreamChannel *channel,
+ }
+ 
+ void
++stream_channel_register_queue_stat_cb(StreamChannel *channel,
++                                      stream_channel_queue_stat_proc cb, void *opaque)
++{
++    channel->queue_cb = cb;
++    channel->queue_opaque = opaque;
++}
++
++void
+ stream_channel_reset(StreamChannel *channel)
+ {
+     struct {
+diff --git a/server/stream-channel.h b/server/stream-channel.h
+index bd075a951..f961d7157 100644
+--- a/server/stream-channel.h
++++ b/server/stream-channel.h
+@@ -67,6 +67,16 @@ typedef void (*stream_channel_start_proc)(void *opaque, struct StreamMsgStartSto
+ void stream_channel_register_start_cb(StreamChannel *channel,
+                                       stream_channel_start_proc cb, void *opaque);
+ 
++typedef struct StreamQueueStat {
++    uint32_t num_items;
++    uint32_t size;
++} StreamQueueStat;
++
++typedef void (*stream_channel_queue_stat_proc)(void *opaque, const StreamQueueStat *stats,
++                                               StreamChannel *channel);
++void stream_channel_register_queue_stat_cb(StreamChannel *channel,
++                                           stream_channel_queue_stat_proc cb, void *opaque);
++
+ G_END_DECLS
+ 
+ #endif /* STREAM_CHANNEL_H_ */
+diff --git a/server/stream-device.c b/server/stream-device.c
+index ae108788b..f87538d49 100644
+--- a/server/stream-device.c
++++ b/server/stream-device.c
+@@ -44,6 +44,7 @@ struct StreamDevice {
+     uint8_t hdr_pos;
+     bool has_error;
+     bool opened;
++    bool flow_stopped;
+     StreamChannel *stream_channel;
+ };
+ 
+@@ -72,7 +73,7 @@ stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *si
+     int n;
+     bool handled = false;
+ 
+-    if (dev->has_error || !dev->stream_channel) {
++    if (dev->has_error || dev->flow_stopped || !dev->stream_channel) {
+         return NULL;
+     }
+ 
+@@ -181,6 +182,9 @@ handle_msg_data(StreamDevice *dev, SpiceCharDeviceInstance *sin)
+         if (n <= 0) {
+             break;
+         }
++        // TODO collect all message ??
++        // up: we send a single frame together
++        // down: guest can cause a crash
+         stream_channel_send_data(dev->stream_channel, buf, n, reds_get_mm_time());
+         dev->hdr.size -= n;
+     }
+@@ -233,6 +237,33 @@ stream_device_stream_start(void *opaque, StreamMsgStartStop *start,
+     red_char_device_write_buffer_add(char_dev, buf);
+ }
+ 
++static void
++stream_device_stream_queue_stat(void *opaque, const StreamQueueStat *stats G_GNUC_UNUSED,
++                                StreamChannel *stream_channel G_GNUC_UNUSED)
++{
++    StreamDevice *dev = (StreamDevice *) opaque;
++
++    if (!dev->opened) {
++        return;
++    }
++
++    // very easy control flow... if any data stop
++    // this seems a very small queue but as we use tcp
++    // there's already that queue
++    if (stats->num_items) {
++        dev->flow_stopped = true;
++        return;
++    }
++
++    if (dev->flow_stopped) {
++        dev->flow_stopped = false;
++        // TODO resume flow...
++        // avoid recursion if we need to call get data from data handling from
++        // data handling
++        red_char_device_wakeup(&dev->parent);
++    }
++}
++
+ RedCharDevice *
+ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin)
+ {
+@@ -277,6 +308,7 @@ allocate_channels(StreamDevice *dev)
+     dev->stream_channel = stream_channel;
+ 
+     stream_channel_register_start_cb(stream_channel, stream_device_stream_start, dev);
++    stream_channel_register_queue_stat_cb(stream_channel, stream_device_stream_queue_stat, dev);
+ }
+ 
+ static void
+@@ -303,6 +335,7 @@ stream_device_port_event(RedCharDevice *char_dev, uint8_t event)
+     }
+     dev->hdr_pos = 0;
+     dev->has_error = false;
++    dev->flow_stopped = false;
+     red_char_device_reset(char_dev);
+     reset_channels(dev);
+ }
diff --git a/SOURCES/0020-stream-channel-Activate-streaming-report-from-client.patch b/SOURCES/0020-stream-channel-Activate-streaming-report-from-client.patch
new file mode 100644
index 0000000..859488f
--- /dev/null
+++ b/SOURCES/0020-stream-channel-Activate-streaming-report-from-client.patch
@@ -0,0 +1,65 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Wed, 7 Jun 2017 13:13:21 +0100
+Subject: [spice-server] stream-channel: Activate streaming report from client
+
+Setting the capability is not enough, each stream must be enabled
+so do so if client support them.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
+---
+ server/stream-channel.c | 19 +++++++++++++++++++
+ 1 file changed, 19 insertions(+)
+
+diff --git a/server/stream-channel.c b/server/stream-channel.c
+index ec4bf021d..7e15dd363 100644
+--- a/server/stream-channel.c
++++ b/server/stream-channel.c
+@@ -27,6 +27,7 @@
+ #include "reds.h"
+ #include "common-graphics-channel.h"
+ #include "display-limits.h"
++#include "stream.h" // TODO remove, put common stuff
+ 
+ #define TYPE_STREAM_CHANNEL_CLIENT stream_channel_client_get_type()
+ 
+@@ -95,6 +96,7 @@ enum {
+     RED_PIPE_ITEM_TYPE_STREAM_CREATE,
+     RED_PIPE_ITEM_TYPE_STREAM_DATA,
+     RED_PIPE_ITEM_TYPE_STREAM_DESTROY,
++    RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT,
+ };
+ 
+ typedef struct StreamCreateItem {
+@@ -230,6 +232,20 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item)
+         spice_marshall_msg_display_stream_create(m, &item->stream_create);
+         break;
+     }
++    case RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT: {
++        if (client->stream_id < 0
++            || !red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_STREAM_REPORT)) {
++            return;
++        }
++        SpiceMsgDisplayStreamActivateReport msg;
++        msg.stream_id = client->stream_id;
++        msg.unique_id = 1; // TODO useful ?
++        msg.max_window_size = RED_STREAM_CLIENT_REPORT_WINDOW;
++        msg.timeout_ms = RED_STREAM_CLIENT_REPORT_TIMEOUT;
++        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT);
++        spice_marshall_msg_display_stream_activate_report(m, &msg);
++        break;
++    }
+     case RED_PIPE_ITEM_TYPE_STREAM_DATA: {
+         StreamDataItem *item = SPICE_UPCAST(StreamDataItem, pipe_item);
+         red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA);
+@@ -459,6 +475,9 @@ stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt)
+     item->stream_create.dest = (SpiceRect) { 0, 0, fmt->width, fmt->height };
+     item->stream_create.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, NULL };
+     red_channel_pipes_add(red_channel, &item->base);
++
++    // activate stream report if possible
++    red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT);
+ }
+ 
+ static inline void
diff --git a/SOURCES/0021-reds-Disable-TLS-1.0.patch b/SOURCES/0021-reds-Disable-TLS-1.0.patch
new file mode 100644
index 0000000..6b338dd
--- /dev/null
+++ b/SOURCES/0021-reds-Disable-TLS-1.0.patch
@@ -0,0 +1,36 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Sun, 11 Feb 2018 18:27:41 +0000
+Subject: [spice-server] reds: Disable TLS 1.0
+
+TLS 1.0 is considered now insecure.
+TLS 1.1 was introduced in 2006.
+Our SPICE clients uses OpenSSL to use TLS and the support for TLS 1.1
+in OpenSSL was introduced in 2006 too so even in systems like
+Windows XP which are not officially supporting TLS 1.0 will work
+with SPICE and TLS 1.1.
+This fixes https://bugzilla.redhat.com/show_bug.cgi?id=1521053.
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+Acked-by: Victor Toso <victortoso@redhat.com>
+---
+ server/reds.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/server/reds.c b/server/reds.c
+index 401d242..0af5643 100644
+--- a/server/reds.c
++++ b/server/reds.c
+@@ -2836,9 +2836,10 @@ static int reds_init_ssl(RedsState *reds)
+     SSL_METHOD *ssl_method;
+ #endif
+     int return_code;
+-    /* When some other SSL/TLS version becomes obsolete, add it to this
++    /* Limit connection to TLSv1.1 or newer.
++     * When some other SSL/TLS version becomes obsolete, add it to this
+      * variable. */
+-    long ssl_options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
++    long ssl_options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
+ 
+     /* Global system initialization*/
+     g_once(&openssl_once, openssl_global_init, NULL);
diff --git a/SOURCES/0022-cursor-Delay-release-of-QXL-guest-cursor-resources.patch b/SOURCES/0022-cursor-Delay-release-of-QXL-guest-cursor-resources.patch
new file mode 100644
index 0000000..f6d4e69
--- /dev/null
+++ b/SOURCES/0022-cursor-Delay-release-of-QXL-guest-cursor-resources.patch
@@ -0,0 +1,71 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Christophe Fergeau <cfergeau@redhat.com>
+Date: Tue, 10 Apr 2018 17:32:48 +0200
+Subject: [spice-server] cursor: Delay release of QXL guest cursor resources
+
+There's an implicit API/ABI contract between QEMU and SPICE that SPICE
+will keep the guest QXL resources alive as long as QEMU can hold a
+pointer to them. This implicit contract was broken in 1c6e7cf7 "Release
+cursor as soon as possible", causing crashes at migration time.
+While the proper fix would be in QEMU so that spice-server does not need
+to have that kind of knowledge regarding QEMU internal implementation,
+this commit reverts to the pre-1c6e7cf7 behaviour to avoid a regression
+while QEMU is being fixed.
+
+This version of the fix is based on a suggestion from Frediano Ziglio.
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1540919
+
+Signed-off-by: Christophe Fergeau <cfergeau@redhat.com>
+Acked-by: Frediano Ziglio <fziglio@redhat.com>
+---
+ server/red-parse-qxl.c | 4 ++++
+ server/red-parse-qxl.h | 1 +
+ server/red-worker.c    | 2 +-
+ 3 files changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/server/red-parse-qxl.c b/server/red-parse-qxl.c
+index 33f3692..c436214 100644
+--- a/server/red-parse-qxl.c
++++ b/server/red-parse-qxl.c
+@@ -24,6 +24,7 @@
+ #include <common/lz_common.h>
+ #include "spice-bitmap-utils.h"
+ #include "red-common.h"
++#include "red-qxl.h"
+ #include "memslot.h"
+ #include "red-parse-qxl.h"
+ 
+@@ -1497,4 +1498,7 @@ void red_put_cursor_cmd(RedCursorCmd *red)
+         red_put_cursor(&red->u.set.shape);
+         break;
+     }
++    if (red->qxl) {
++        red_qxl_release_resource(red->qxl, red->release_info_ext);
++    }
+ }
+diff --git a/server/red-parse-qxl.h b/server/red-parse-qxl.h
+index 4a576ca..f0407b5 100644
+--- a/server/red-parse-qxl.h
++++ b/server/red-parse-qxl.h
+@@ -99,6 +99,7 @@ typedef struct RedSurfaceCmd {
+ } RedSurfaceCmd;
+ 
+ typedef struct RedCursorCmd {
++    QXLInstance *qxl;
+     QXLReleaseInfoExt release_info_ext;
+     uint8_t type;
+     union {
+diff --git a/server/red-worker.c b/server/red-worker.c
+index 8a63fde..ccf5df9 100644
+--- a/server/red-worker.c
++++ b/server/red-worker.c
+@@ -112,7 +112,7 @@ static gboolean red_process_cursor_cmd(RedWorker *worker, const QXLCommandExt *e
+         free(cursor_cmd);
+         return FALSE;
+     }
+-    red_qxl_release_resource(worker->qxl, cursor_cmd->release_info_ext);
++    cursor_cmd->qxl = worker->qxl;
+     cursor_channel_process_cmd(worker->cursor_channel, cursor_cmd);
+     return TRUE;
+ }
diff --git a/SOURCES/0023-sound-Don-t-mute-recording-when-client-reconnects.patch b/SOURCES/0023-sound-Don-t-mute-recording-when-client-reconnects.patch
new file mode 100644
index 0000000..7afa627
--- /dev/null
+++ b/SOURCES/0023-sound-Don-t-mute-recording-when-client-reconnects.patch
@@ -0,0 +1,225 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Christophe Fergeau <cfergeau@redhat.com>
+Date: Fri, 25 May 2018 11:12:40 +0200
+Subject: [spice-server] sound: Don't mute recording when client reconnects
+
+When a new record channel is added, the code relies on a snd_send() call
+in record_channel_client_constructed() to send RECORD_START to the
+client. However, at this point, snd_send() is non-functional because
+the red_channel_client_pipe_add() call it makes is a no-op because
+prepare_pipe_add() makes a connection check through
+red_channel_client_is_connected() queueing the item. This connection
+check returns FALSE at ::constructed() time as the channel client will
+only become connected towards the end of
+red_channel_client_initable_init() which runs after the object
+instantiation is complete.
+
+This commit solves this issue by making PlaybackChannelClient and
+RecordChannelClient implement GInitable, and move the code interacting
+with the client in their _initable_init() function, as at this point the
+objects will be able to send data.
+
+This causes a bug where starting recording and then
+disconnecting/reconnecting the client does not successfully reenable
+recording. This is a regression introduced by commit d8dc09
+'sound: Convert SndChannelClient to RedChannelClient'
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1549132
+
+Signed-off-by: Christophe Fergeau <cfergeau@redhat.com>
+Acked-by: Victor Toso <victortoso@redhat.com>
+---
+ server/sound.c | 120 ++++++++++++++++++++++++++++++++++---------------
+ 1 file changed, 83 insertions(+), 37 deletions(-)
+
+diff --git a/server/sound.c b/server/sound.c
+index 9073626..8c6cf8a 100644
+--- a/server/sound.c
++++ b/server/sound.c
+@@ -103,6 +103,11 @@ typedef struct SndChannelClientClass {
+     RedChannelClientClass parent_class;
+ } SndChannelClientClass;
+ 
++static void playback_channel_client_initable_interface_init(GInitableIface *iface);
++static void record_channel_client_initable_interface_init(GInitableIface *iface);
++static GInitableIface *playback_channel_client_parent_initable_iface;
++static GInitableIface *record_channel_client_parent_initable_iface;
++
+ G_DEFINE_TYPE(SndChannelClient, snd_channel_client, RED_TYPE_CHANNEL_CLIENT)
+ 
+ 
+@@ -149,7 +154,9 @@ typedef struct PlaybackChannelClientClass {
+     SndChannelClientClass parent_class;
+ } PlaybackChannelClientClass;
+ 
+-G_DEFINE_TYPE(PlaybackChannelClient, playback_channel_client, TYPE_SND_CHANNEL_CLIENT)
++G_DEFINE_TYPE_WITH_CODE(PlaybackChannelClient, playback_channel_client, TYPE_SND_CHANNEL_CLIENT,
++                        G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, playback_channel_client_initable_interface_init))
++ 
+ 
+ 
+ typedef struct SpiceVolumeState {
+@@ -207,6 +214,7 @@ typedef struct RecordChannelClass {
+ } RecordChannelClass;
+ 
+ G_DEFINE_TYPE(RecordChannel, record_channel, TYPE_SND_CHANNEL)
++ 
+ 
+ 
+ #define TYPE_RECORD_CHANNEL_CLIENT record_channel_client_get_type()
+@@ -230,7 +238,8 @@ typedef struct RecordChannelClientClass {
+     SndChannelClientClass parent_class;
+ } RecordChannelClientClass;
+ 
+-G_DEFINE_TYPE(RecordChannelClient, record_channel_client, TYPE_SND_CHANNEL_CLIENT)
++G_DEFINE_TYPE_WITH_CODE(RecordChannelClient, record_channel_client, TYPE_SND_CHANNEL_CLIENT,
++                        G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, record_channel_client_initable_interface_init))
+ 
+ 
+ /* A list of all Spice{Playback,Record}State objects */
+@@ -1044,7 +1053,6 @@ playback_channel_client_constructed(GObject *object)
+     RedChannelClient *rcc = RED_CHANNEL_CLIENT(playback_client);
+     RedChannel *red_channel = red_channel_client_get_channel(rcc);
+     SndChannel *channel = SND_CHANNEL(red_channel);
+-    RedClient *red_client = red_channel_client_get_client(rcc);
+     SndChannelClient *scc = SND_CHANNEL_CLIENT(playback_client);
+ 
+     G_OBJECT_CLASS(playback_channel_client_parent_class)->constructed(object);
+@@ -1070,18 +1078,6 @@ playback_channel_client_constructed(GObject *object)
+ 
+     spice_debug("playback client %p using mode %s", playback_client,
+                 spice_audio_data_mode_to_string(playback_client->mode));
+-
+-    if (!red_client_during_migrate_at_target(red_client)) {
+-        snd_set_command(scc, SND_PLAYBACK_MODE_MASK);
+-        if (channel->volume.volume_nchannels) {
+-            snd_set_command(scc, SND_VOLUME_MUTE_MASK);
+-        }
+-    }
+-
+-    if (channel->active) {
+-        playback_channel_client_start(scc);
+-    }
+-    snd_send(scc);
+ }
+ 
+ static void snd_set_peer(RedChannel *red_channel, RedClient *client, RedsStream *stream,
+@@ -1250,27 +1246,6 @@ record_channel_client_finalize(GObject *object)
+     G_OBJECT_CLASS(record_channel_client_parent_class)->finalize(object);
+ }
+ 
+-static void
+-record_channel_client_constructed(GObject *object)
+-{
+-    RecordChannelClient *record_client = RECORD_CHANNEL_CLIENT(object);
+-    RedChannel *red_channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(record_client));
+-    SndChannel *channel = SND_CHANNEL(red_channel);
+-    SndChannelClient *scc = SND_CHANNEL_CLIENT(record_client);
+-
+-    G_OBJECT_CLASS(record_channel_client_parent_class)->constructed(object);
+-
+-    if (channel->volume.volume_nchannels) {
+-        snd_set_command(scc, SND_VOLUME_MUTE_MASK);
+-    }
+-
+-    if (channel->active) {
+-        record_channel_client_start(scc);
+-    }
+-    snd_send(scc);
+-}
+-
+-
+ static void snd_set_record_peer(RedChannel *red_channel, RedClient *client, RedsStream *stream,
+                                 G_GNUC_UNUSED int migration,
+                                 RedChannelCapabilities *caps)
+@@ -1478,6 +1453,43 @@ snd_channel_client_init(SndChannelClient *self)
+ {
+ }
+ 
++static gboolean playback_channel_client_initable_init(GInitable *initable,
++                                                      GCancellable *cancellable,
++                                                      GError **error)
++{
++    gboolean success;
++    RedClient *red_client = red_channel_client_get_client(RED_CHANNEL_CLIENT(initable));
++    SndChannelClient *scc = SND_CHANNEL_CLIENT(initable);
++    RedChannel *red_channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(initable));
++    SndChannel *channel = SND_CHANNEL(red_channel);
++
++    success = playback_channel_client_parent_initable_iface->init(initable, cancellable, error);
++    if (!success) {
++        return FALSE;
++    }
++
++    if (!red_client_during_migrate_at_target(red_client)) {
++        snd_set_command(scc, SND_PLAYBACK_MODE_MASK);
++        if (channel->volume.volume_nchannels) {
++            snd_set_command(scc, SND_VOLUME_MUTE_MASK);
++        }
++    }
++
++    if (channel->active) {
++        playback_channel_client_start(scc);
++    }
++    snd_send(SND_CHANNEL_CLIENT(initable));
++
++    return TRUE;
++}
++
++static void playback_channel_client_initable_interface_init(GInitableIface *iface)
++{
++    playback_channel_client_parent_initable_iface = g_type_interface_peek_parent (iface);
++
++    iface->init = playback_channel_client_initable_init;
++}
++
+ static void
+ playback_channel_client_class_init(PlaybackChannelClientClass *klass)
+ {
+@@ -1505,11 +1517,45 @@ playback_channel_client_init(PlaybackChannelClient *playback)
+     snd_playback_alloc_frames(playback);
+ }
+ 
++static gboolean record_channel_client_initable_init(GInitable *initable,
++                                                    GCancellable *cancellable,
++                                                    GError **error)
++{
++    gboolean success;
++    RecordChannelClient *record_client = RECORD_CHANNEL_CLIENT(initable);
++    RedChannel *red_channel = red_channel_client_get_channel(RED_CHANNEL_CLIENT(record_client));
++    SndChannel *channel = SND_CHANNEL(red_channel);
++    SndChannelClient *scc = SND_CHANNEL_CLIENT(record_client);
++
++    success = record_channel_client_parent_initable_iface->init(initable, cancellable, error);
++    if (!success) {
++        return FALSE;
++    }
++
++    if (channel->volume.volume_nchannels) {
++        snd_set_command(scc, SND_VOLUME_MUTE_MASK);
++    }
++
++    if (channel->active) {
++        record_channel_client_start(scc);
++    }
++    snd_send(SND_CHANNEL_CLIENT(initable));
++
++    return TRUE;
++}
++
++static void record_channel_client_initable_interface_init(GInitableIface *iface)
++{
++    record_channel_client_parent_initable_iface = g_type_interface_peek_parent (iface);
++
++    iface->init = record_channel_client_initable_init;
++}
++
++
+ static void
+ record_channel_client_class_init(RecordChannelClientClass *klass)
+ {
+     GObjectClass *object_class = G_OBJECT_CLASS(klass);
+-    object_class->constructed = record_channel_client_constructed;
+     object_class->finalize = record_channel_client_finalize;
+ }
+ 
diff --git a/SOURCES/0024-tls-Parse-spice.cnf-OpenSSL-configuration-file.patch b/SOURCES/0024-tls-Parse-spice.cnf-OpenSSL-configuration-file.patch
new file mode 100644
index 0000000..72ad808
--- /dev/null
+++ b/SOURCES/0024-tls-Parse-spice.cnf-OpenSSL-configuration-file.patch
@@ -0,0 +1,190 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Christophe Fergeau <cfergeau@redhat.com>
+Date: Mon, 18 Jun 2018 12:39:37 +0200
+Subject: [PATCH] tls: Parse spice.cnf OpenSSL configuration file
+
+SPICE tries to use the OpenSSL system-wide defaults as much as possible
+for the TLS ciphers and protocols it uses. However, this is not enough
+for some customers who want it to use a more restrictive set of TLS
+features. spice-server should not try to override the system defaults
+OpenSSL uses, so this is not going to be hardcoded in spice-server code.
+
+This is addressed with crypto policies in recent fedoras, and is being
+solved upstream through https://github.com/openssl/openssl/pull/4848
+This issue has become pressing enough that we need to solve it in el7
+which unfortunately does not have any of these system-wide settings.
+
+As a stop-gap measure, this downstream-only patch adds a
+/etc/pki/tls/spice.cnf configuration file which can be used to configure
+the TLS ciphers/protocols used for SPICE. This is only meant to be a
+temporary solution, and will be superseded by crypto-policies when they
+land in RHEL.
+
+Signed-off-by: Christophe Fergeau <cfergeau@redhat.com>
+---
+ docs/Makefile.am      |   1 +
+ docs/spice.cnf.sample |  15 +++++++
+ server/reds.c         | 102 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 118 insertions(+)
+ create mode 100644 docs/spice.cnf.sample
+
+diff --git a/docs/Makefile.am b/docs/Makefile.am
+index 45667a6..909ed15 100644
+--- a/docs/Makefile.am
++++ b/docs/Makefile.am
+@@ -6,6 +6,7 @@ EXTRA_DIST =					\
+ 	spice_style.txt				\
+ 	spice_threading_model.html		\
+ 	spice_threading_model.txt		\
++	spice.cnf.sample			\
+ 	$(NULL)
+ 
+ if BUILD_MANUAL
+diff --git a/docs/spice.cnf.sample b/docs/spice.cnf.sample
+new file mode 100644
+index 0000000..e5404ae
+--- /dev/null
++++ b/docs/spice.cnf.sample
+@@ -0,0 +1,15 @@
++# SPICE OpenSSL configuration file
++#
++# Sample configuration file for SPICE TLS communication
++# Edit the file according to your needs, and put it in
++# /etc/pki/tls/spice.cnf
++#
++# See "SUPPORTED CONFIGURATION FILE COMMANDS" in SSL_CONF_cmd(3)
++# for the valid options, as well a ciphers(1) for the format
++# of CipherString
++
++# Configure available ciphers
++CipherString = DEFAULT:-RC4:-3DES:-DES
++
++# Only enable TLSv1.2 (and newer TLS versions the day OpenSSL supports them)
++Protocol = ALL,-SSLv2,-SSLv3,-TLSv1,-TLSv1.1
+diff --git a/server/reds.c b/server/reds.c
+index 0af5643..846e44d 100644
+--- a/server/reds.c
++++ b/server/reds.c
+@@ -33,6 +33,7 @@
+ #include <sys/mman.h>
+ #include <ctype.h>
+ 
++#include <openssl/conf.h>
+ #include <openssl/err.h>
+ 
+ #if HAVE_SASL
+@@ -2827,6 +2828,102 @@ static gpointer openssl_global_init(gpointer arg)
+     return NULL;
+ }
+ 
++#define SPICE_OPENSSL_CNF_FILENAME "/etc/pki/tls/spice.cnf"
++
++static int reds_ssl_config_file_apply(RedsState *reds, STACK_OF(CONF_VALUE) *sect)
++{
++    int openssl_status;
++    int return_value = 0;
++    SSL_CONF_CTX *cctx = NULL;
++    unsigned int i;
++
++    cctx = SSL_CONF_CTX_new();
++    SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER);
++    SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE);
++    SSL_CONF_CTX_set_ssl_ctx(cctx, reds->ctx);
++
++    for (i = 0; i < sk_CONF_VALUE_num(sect); i++) {
++        CONF_VALUE *option_value;
++        option_value = sk_CONF_VALUE_value(sect, i);
++        g_message("setting TLS option '%s' to '%s' from %s configuration file",
++                   option_value->name, option_value->value,
++                   SPICE_OPENSSL_CNF_FILENAME);
++        openssl_status = SSL_CONF_cmd(cctx, option_value->name, option_value->value);
++        switch(openssl_status) {
++        case 1: /* fallthrough */
++        case 2:
++            /* The option was successfully processed */
++            break;
++        case 0:
++            g_warning("failure to set option '%s'", option_value->name);
++            return_value = -1;
++            break;
++        case -2:
++            g_warning("unknown option '%s'", option_value->name);
++            return_value = -1;
++            break;
++        case -3:
++            g_warning("missing value for option '%s'", option_value->name);
++            return_value = -1;
++            break;
++        default:
++            g_warning("unknown SSL_CONF_cmd return value: %d", openssl_status);
++            return_value = -1;
++            break;
++        }
++    }
++
++    openssl_status = SSL_CONF_CTX_finish(cctx);
++    if (!openssl_status) {
++        g_warning("SSL_CONF_CTX_finish() failed");
++        return_value = -1;
++    }
++
++    SSL_CONF_CTX_free(cctx);
++
++    return return_value;
++}
++
++static int reds_ssl_config_file_try_load(RedsState *reds)
++{
++    int status = -1;
++    int openssl_status;
++    CONF *ssl_conf = NULL;
++    STACK_OF(CONF_VALUE) *default_section;
++    long error_line = -1;
++
++    if (!g_file_test(SPICE_OPENSSL_CNF_FILENAME, G_FILE_TEST_IS_REGULAR)) {
++        /* The configuration file is not mandatory, it's only meant to be used
++         * when the sysadmin does not want to use the system-wide OpenSSL defaults
++         */
++        return 0;
++    }
++
++    ssl_conf = NCONF_new(NULL);
++    openssl_status = NCONF_load(ssl_conf, SPICE_OPENSSL_CNF_FILENAME, &error_line);
++    if (openssl_status <= 0) {
++        if (error_line <= 0) {
++            g_warning("error loading config file %s", SPICE_OPENSSL_CNF_FILENAME);
++        } else {
++            g_warning("error parsing config file %s at %ld", SPICE_OPENSSL_CNF_FILENAME, error_line);
++        }
++        goto end;
++    }
++
++    default_section = NCONF_get_section(ssl_conf, "default");
++    if (default_section == NULL) {
++        g_warning("could not find any content in %s config file (no toplevel section?)", SPICE_OPENSSL_CNF_FILENAME);
++        goto end;
++    }
++
++    status = reds_ssl_config_file_apply(reds, default_section);
++
++end:
++    NCONF_free(ssl_conf);
++
++    return status;
++}
++
+ static int reds_init_ssl(RedsState *reds)
+ {
+     static GOnce openssl_once = G_ONCE_INIT;
+@@ -2911,6 +3008,11 @@ static int reds_init_ssl(RedsState *reds)
+     sk_zero(cmp_stack);
+ #endif
+ 
++    /* must be last to override whatever was configured previously */
++    if (reds_ssl_config_file_try_load(reds) != 0) {
++        return -1;
++    }
++
+     return 0;
+ }
+ 
diff --git a/SOURCES/0025-ssl-Allow-to-use-ECDH-ciphers-with-OpenSSL-1.0.patch b/SOURCES/0025-ssl-Allow-to-use-ECDH-ciphers-with-OpenSSL-1.0.patch
new file mode 100644
index 0000000..16ad761
--- /dev/null
+++ b/SOURCES/0025-ssl-Allow-to-use-ECDH-ciphers-with-OpenSSL-1.0.patch
@@ -0,0 +1,30 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Christophe Fergeau <cfergeau@redhat.com>
+Date: Wed, 20 Jun 2018 17:02:14 +0200
+Subject: [spice-server] ssl: Allow to use ECDH ciphers with OpenSSL 1.0
+
+Without an explicit call to SSL_CTX_set_ecdh_auto(reds->ctx, 1), OpenSSL
+1.0 (still used by el7) would not use ECDH ciphers (this is now
+automatic with OpenSSL 1.1.0). This commit adds this missing call. It's
+based on a suggestion from David Jasa
+
+Signed-off-by: Christophe Fergeau <cfergeau@redhat.com>
+Acked-by: Frediano Ziglio <fziglio@redhat.com>
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1566597
+---
+ server/reds.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/server/reds.c b/server/reds.c
+index 846e44d..a7b9c38 100644
+--- a/server/reds.c
++++ b/server/reds.c
+@@ -2955,6 +2955,7 @@ static int reds_init_ssl(RedsState *reds)
+     ssl_options |= SSL_OP_NO_COMPRESSION;
+ #endif
+     SSL_CTX_set_options(reds->ctx, ssl_options);
++    SSL_CTX_set_ecdh_auto(reds->ctx, 1);
+ 
+     /* Load our keys and certificates*/
+     return_code = SSL_CTX_use_certificate_chain_file(reds->ctx, reds->config->ssl_parameters.certs_file);
diff --git a/SOURCES/0026-Fix-flexible-array-buffer-overflow.patch b/SOURCES/0026-Fix-flexible-array-buffer-overflow.patch
new file mode 100644
index 0000000..6c5eaec
--- /dev/null
+++ b/SOURCES/0026-Fix-flexible-array-buffer-overflow.patch
@@ -0,0 +1,301 @@
+From c182f8e4a445e93842faf6c2bd8583894da36a1a Mon Sep 17 00:00:00 2001
+From: Frediano Ziglio <fziglio@redhat.com>
+Date: Fri, 18 May 2018 11:41:57 +0100
+Subject: [PATCH] Fix flexible array buffer overflow
+
+This is kind of a DoS, possibly flexible array in the protocol
+causes the network size check to be ignored due to integer overflows.
+
+The size of flexible array is computed as (message_end - position),
+then this size is added to the number of bytes before the array and
+this number is used to check if we overflow initial message.
+
+An example is:
+
+    message {
+        uint32 dummy[2];
+        uint8 data[] @end;
+    } LenMessage;
+
+which generated this (simplified remove useless code) code:
+
+    { /* data */
+        data__nelements = message_end - (start + 8);
+
+        data__nw_size = data__nelements;
+    }
+
+    nw_size = 8 + data__nw_size;
+
+    /* Check if message fits in reported side */
+    if (nw_size > (uintptr_t) (message_end - start)) {
+        return NULL;
+    }
+
+Following code:
+- data__nelements == message_end - (start + 8)
+- data__nw_size == data__nelements == message_end - (start + 8)
+- nw_size == 8 + data__nw_size == 8 + message_end - (start + 8) ==
+  8 + message_end - start - 8 == message_end -start
+- the check for overflow is (nw_size > (message_end - start)) but
+  nw_size == message_end - start so the check is doing
+  ((message_end - start) > (message_end - start)) which is always false.
+
+If message_end - start < 8 then data__nelements (number of element
+on the array above) computation generate an integer underflow that
+later create a buffer overflow.
+
+Add a check to make sure that the array starts before the message ends
+to avoid the overflow.
+
+Difference is:
+    diff -u save/generated_client_demarshallers1.c common/generated_client_demarshallers1.c
+    --- save/generated_client_demarshallers1.c	2018-06-22 22:13:48.626793919 +0100
+    +++ common/generated_client_demarshallers1.c	2018-06-22 22:14:03.408163291 +0100
+    @@ -225,6 +225,9 @@
+         uint64_t data__nelements;
+
+         { /* data */
+    +        if (SPICE_UNLIKELY((start + 0) > message_end)) {
+    +            goto error;
+    +        }
+             data__nelements = message_end - (start + 0);
+
+             data__nw_size = data__nelements;
+    @@ -243,6 +246,9 @@
+         *free_message = nofree;
+         return data;
+
+    +   error:
+    +    free(data);
+    +    return NULL;
+     }
+
+     static uint8_t * parse_msg_set_ack(uint8_t *message_start, uint8_t *message_end, SPICE_GNUC_UNUSED int minor, size_t *size, message_destructor_t *free_message)
+    @@ -301,6 +307,9 @@
+         SpiceMsgPing *out;
+
+         { /* data */
+    +        if (SPICE_UNLIKELY((start + 12) > message_end)) {
+    +            goto error;
+    +        }
+             data__nelements = message_end - (start + 12);
+
+             data__nw_size = data__nelements;
+    @@ -5226,6 +5235,9 @@
+             uint64_t cursor_data__nw_size;
+             uint64_t cursor_data__nelements;
+             { /* data */
+    +            if (SPICE_UNLIKELY((start2 + 22) > message_end)) {
+    +                goto error;
+    +            }
+                 cursor_data__nelements = message_end - (start2 + 22);
+
+                 cursor_data__nw_size = cursor_data__nelements;
+    @@ -5305,6 +5317,9 @@
+             uint64_t cursor_data__nw_size;
+             uint64_t cursor_data__nelements;
+             { /* data */
+    +            if (SPICE_UNLIKELY((start2 + 22) > message_end)) {
+    +                goto error;
+    +            }
+                 cursor_data__nelements = message_end - (start2 + 22);
+
+                 cursor_data__nw_size = cursor_data__nelements;
+    @@ -5540,6 +5555,9 @@
+         SpiceMsgPlaybackPacket *out;
+
+         { /* data */
+    +        if (SPICE_UNLIKELY((start + 4) > message_end)) {
+    +            goto error;
+    +        }
+             data__nelements = message_end - (start + 4);
+
+             data__nw_size = data__nelements;
+    @@ -5594,6 +5612,9 @@
+         SpiceMsgPlaybackMode *out;
+
+         { /* data */
+    +        if (SPICE_UNLIKELY((start + 8) > message_end)) {
+    +            goto error;
+    +        }
+             data__nelements = message_end - (start + 8);
+
+             data__nw_size = data__nelements;
+    diff -u save/generated_client_demarshallers.c common/generated_client_demarshallers.c
+    --- save/generated_client_demarshallers.c	2018-06-22 22:13:48.626793919 +0100
+    +++ common/generated_client_demarshallers.c	2018-06-22 22:14:03.004153195 +0100
+    @@ -225,6 +225,9 @@
+         uint64_t data__nelements;
+
+         { /* data */
+    +        if (SPICE_UNLIKELY((start + 0) > message_end)) {
+    +            goto error;
+    +        }
+             data__nelements = message_end - (start + 0);
+
+             data__nw_size = data__nelements;
+    @@ -243,6 +246,9 @@
+         *free_message = nofree;
+         return data;
+
+    +   error:
+    +    free(data);
+    +    return NULL;
+     }
+
+     static uint8_t * parse_msg_set_ack(uint8_t *message_start, uint8_t *message_end, SPICE_GNUC_UNUSED int minor, size_t *size, message_destructor_t *free_message)
+    @@ -301,6 +307,9 @@
+         SpiceMsgPing *out;
+
+         { /* data */
+    +        if (SPICE_UNLIKELY((start + 12) > message_end)) {
+    +            goto error;
+    +        }
+             data__nelements = message_end - (start + 12);
+
+             data__nw_size = data__nelements;
+    @@ -6574,6 +6583,9 @@
+             }
+
+             { /* data */
+    +            if (SPICE_UNLIKELY((start2 + 2 + cursor_u__nw_size) > message_end)) {
+    +                goto error;
+    +            }
+                 cursor_data__nelements = message_end - (start2 + 2 + cursor_u__nw_size);
+
+                 cursor_data__nw_size = cursor_data__nelements;
+    @@ -6670,6 +6682,9 @@
+             }
+
+             { /* data */
+    +            if (SPICE_UNLIKELY((start2 + 2 + cursor_u__nw_size) > message_end)) {
+    +                goto error;
+    +            }
+                 cursor_data__nelements = message_end - (start2 + 2 + cursor_u__nw_size);
+
+                 cursor_data__nw_size = cursor_data__nelements;
+    @@ -6907,6 +6922,9 @@
+         SpiceMsgPlaybackPacket *out;
+
+         { /* data */
+    +        if (SPICE_UNLIKELY((start + 4) > message_end)) {
+    +            goto error;
+    +        }
+             data__nelements = message_end - (start + 4);
+
+             data__nw_size = data__nelements;
+    @@ -6961,6 +6979,9 @@
+         SpiceMsgPlaybackMode *out;
+
+         { /* data */
+    +        if (SPICE_UNLIKELY((start + 6) > message_end)) {
+    +            goto error;
+    +        }
+             data__nelements = message_end - (start + 6);
+
+             data__nw_size = data__nelements;
+    @@ -7559,6 +7580,9 @@
+         SpiceMsgTunnelSocketData *out;
+
+         { /* data */
+    +        if (SPICE_UNLIKELY((start + 2) > message_end)) {
+    +            goto error;
+    +        }
+             data__nelements = message_end - (start + 2);
+
+             data__nw_size = data__nelements;
+    @@ -7840,6 +7864,9 @@
+         }
+
+         { /* compressed_data */
+    +        if (SPICE_UNLIKELY((start + 1 + u__nw_size) > message_end)) {
+    +            goto error;
+    +        }
+             compressed_data__nelements = message_end - (start + 1 + u__nw_size);
+
+             compressed_data__nw_size = compressed_data__nelements;
+    diff -u save/generated_server_demarshallers.c common/generated_server_demarshallers.c
+    --- save/generated_server_demarshallers.c	2018-06-22 22:13:48.627793944 +0100
+    +++ common/generated_server_demarshallers.c	2018-06-22 22:14:05.231208847 +0100
+    @@ -306,6 +306,9 @@
+         uint64_t data__nelements;
+
+         { /* data */
+    +        if (SPICE_UNLIKELY((start + 0) > message_end)) {
+    +            goto error;
+    +        }
+             data__nelements = message_end - (start + 0);
+
+             data__nw_size = data__nelements;
+    @@ -324,6 +327,9 @@
+         *free_message = nofree;
+         return data;
+
+    +   error:
+    +    free(data);
+    +    return NULL;
+     }
+
+     static uint8_t * parse_msgc_disconnecting(uint8_t *message_start, uint8_t *message_end, SPICE_GNUC_UNUSED int minor, size_t *size, message_destructor_t *free_message)
+    @@ -1259,6 +1265,9 @@
+         SpiceMsgcRecordPacket *out;
+
+         { /* data */
+    +        if (SPICE_UNLIKELY((start + 4) > message_end)) {
+    +            goto error;
+    +        }
+             data__nelements = message_end - (start + 4);
+
+             data__nw_size = data__nelements;
+    @@ -1313,6 +1322,9 @@
+         SpiceMsgcRecordMode *out;
+
+         { /* data */
+    +        if (SPICE_UNLIKELY((start + 6) > message_end)) {
+    +            goto error;
+    +        }
+             data__nelements = message_end - (start + 6);
+
+             data__nw_size = data__nelements;
+    @@ -1841,6 +1853,9 @@
+         SpiceMsgcTunnelSocketData *out;
+
+         { /* data */
+    +        if (SPICE_UNLIKELY((start + 2) > message_end)) {
+    +            goto error;
+    +        }
+             data__nelements = message_end - (start + 2);
+
+             data__nw_size = data__nelements;
+    @@ -2057,6 +2072,9 @@
+         }
+
+         { /* compressed_data */
+    +        if (SPICE_UNLIKELY((start + 1 + u__nw_size) > message_end)) {
+    +            goto error;
+    +        }
+             compressed_data__nelements = message_end - (start + 1 + u__nw_size);
+
+             compressed_data__nw_size = compressed_data__nelements;
+
+Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
+---
+ spice-common/python_modules/demarshal.py | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/spice-common/python_modules/demarshal.py b/spice-common/python_modules/demarshal.py
+index 1ea131d..7172762 100644
+--- a/spice-common/python_modules/demarshal.py
++++ b/spice-common/python_modules/demarshal.py
+@@ -318,6 +318,7 @@ def write_validate_array_item(writer, container, item, scope, parent_scope, star
+         writer.assign(nelements, array.size)
+     elif array.is_remaining_length():
+         if element_type.is_fixed_nw_size():
++            writer.error_check("%s > message_end" % item.get_position())
+             if element_type.get_fixed_nw_size() == 1:
+                 writer.assign(nelements, "message_end - %s" % item.get_position())
+             else:
+-- 
+2.17.1
+
diff --git a/SOURCES/0027-dcc-Fix-QUIC-fallback-in-get_compression_for_bitmap.patch b/SOURCES/0027-dcc-Fix-QUIC-fallback-in-get_compression_for_bitmap.patch
new file mode 100644
index 0000000..f64c2cb
--- /dev/null
+++ b/SOURCES/0027-dcc-Fix-QUIC-fallback-in-get_compression_for_bitmap.patch
@@ -0,0 +1,48 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Christophe Fergeau <cfergeau@redhat.com>
+Date: Fri, 20 Jul 2018 11:44:51 +0200
+Subject: [PATCH] dcc: Fix QUIC fallback in get_compression_for_bitmap()
+
+There was a small regression introduced in get_compression_for_bitmap()
+by f401eb07f dcc: Rewrite dcc_image_compress.
+If SPICE_IMAGE_COMPRESSION_AUTO_GLZ is specified, and the bitmap has a
+stride which is bigger than its width (ie it has padding), then
+get_compression_for_bitmap() will return SPICE_IMAGE_COMPRESSION_OFF
+while in that case, we used to use QUIC for compression.
+
+This happens because that function in the AUTO_GLZ case first checks if
+QUIC should be used, if not, it decides to use GLZ, but then decides it
+can't because of the stride, so falls back to OFF, while it used to
+fall back to QUIC.
+
+This commit only slightly reworks a preexisting if (!can_lz_compress())
+check so that it's unconditional rather than depending on the previous
+checks having been unsuccessful.
+
+This issue could be observed by using a spice-html5 without support for
+uncompressed bitmaps with end-of-line padding by simply starting a f28
+VM and connecting to it/moving the mouse cursor in it.
+
+Signed-off-by: Christophe Fergeau <cfergeau@redhat.com>
+Acked-by: Frediano Ziglio <fziglio@redhat.com>
+---
+ server/dcc.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/server/dcc.c b/server/dcc.c
+index 3bf75a7..b632905 100644
+--- a/server/dcc.c
++++ b/server/dcc.c
+@@ -806,8 +806,10 @@ static SpiceImageCompression get_compression_for_bitmap(SpiceBitmap *bitmap,
+                     bitmap_get_graduality_level(bitmap) == BITMAP_GRADUAL_HIGH) {
+                     return SPICE_IMAGE_COMPRESSION_QUIC;
+                 }
+-            } else if (!can_lz_compress(bitmap) ||
+-                       drawable->copy_bitmap_graduality == BITMAP_GRADUAL_HIGH) {
++            } else if (drawable->copy_bitmap_graduality == BITMAP_GRADUAL_HIGH) {
++                return SPICE_IMAGE_COMPRESSION_QUIC;
++            }
++            if (!can_lz_compress(bitmap)) {
+                 return SPICE_IMAGE_COMPRESSION_QUIC;
+             }
+         }
diff --git a/SOURCES/0028-memslot-Fix-off-by-one-error-in-group-slot-boundary-.patch b/SOURCES/0028-memslot-Fix-off-by-one-error-in-group-slot-boundary-.patch
new file mode 100644
index 0000000..ad8a9aa
--- /dev/null
+++ b/SOURCES/0028-memslot-Fix-off-by-one-error-in-group-slot-boundary-.patch
@@ -0,0 +1,100 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Christophe Fergeau <cfergeau@redhat.com>
+Date: Thu, 29 Nov 2018 14:18:39 +0100
+Subject: [PATCH] memslot: Fix off-by-one error in group/slot boundary check
+
+RedMemSlotInfo keeps an array of groups, and each group contains an
+array of slots. Unfortunately, these checks are off by 1, they check
+that the index is greater or equal to the number of elements in the
+array, while these arrays are 0 based. The check should only check for
+strictly greater than the number of elements.
+
+For the group array, this is not a big issue, as these memslot groups
+are created by spice-server users (eg QEMU), and the group ids used to
+index that array are also generated by the spice-server user, so it
+should not be possible for the guest to set them to arbitrary values.
+
+The slot id is more problematic, as it's calculated from a QXLPHYSICAL
+address, and such addresses are usually set by the guest QXL driver, so
+the guest can set these to arbitrary values, including malicious values,
+which are probably easy to build from the guest PCI configuration.
+
+This patch fixes the arrays bound check, and adds a test case for this.
+
+Signed-off-by: Christophe Fergeau <cfergeau@redhat.com>
+---
+ server/memslot.c                |  4 ++--
+ server/tests/test-qxl-parsing.c | 32 ++++++++++++++++++++++++++++++++
+ 2 files changed, 34 insertions(+), 2 deletions(-)
+
+diff --git a/server/memslot.c b/server/memslot.c
+index 7074b43..8c59c38 100644
+--- a/server/memslot.c
++++ b/server/memslot.c
+@@ -99,14 +99,14 @@ unsigned long memslot_get_virt(RedMemSlotInfo *info, QXLPHYSICAL addr, uint32_t
+     MemSlot *slot;
+ 
+     *error = 0;
+-    if (group_id > info->num_memslots_groups) {
++    if (group_id >= info->num_memslots_groups) {
+         spice_critical("group_id too big");
+         *error = 1;
+         return 0;
+     }
+ 
+     slot_id = memslot_get_id(info, addr);
+-    if (slot_id > info->num_memslots) {
++    if (slot_id >= info->num_memslots) {
+         print_memslots(info);
+         spice_critical("slot_id %d too big, addr=%" PRIx64, slot_id, addr);
+         *error = 1;
+diff --git a/server/tests/test-qxl-parsing.c b/server/tests/test-qxl-parsing.c
+index 9c0c3b1..83f2083 100644
+--- a/server/tests/test-qxl-parsing.c
++++ b/server/tests/test-qxl-parsing.c
+@@ -85,6 +85,33 @@ static void deinit_qxl_surface(QXLSurfaceCmd *qxl)
+     free(from_physical(qxl->u.surface_create.data));
+ }
+ 
++static void test_memslot_invalid_group_id(void)
++{
++    RedMemSlotInfo mem_info;
++    int error;
++    init_meminfo(&mem_info);
++
++    memslot_get_virt(&mem_info, 0, 16, 1, &error);
++}
++
++static void test_memslot_invalid_slot_id(void)
++{
++    RedMemSlotInfo mem_info;
++    int error;
++    init_meminfo(&mem_info);
++
++    memslot_get_virt(&mem_info, 1 << mem_info.memslot_id_shift, 16, 0, &error);
++}
++
++static void test_memslot_invalid_addresses(void)
++{
++    g_test_trap_subprocess("/server/memslot-invalid-addresses/subprocess/group_id", 0, 0);
++    g_test_trap_assert_stderr("*group_id too big*");
++
++    g_test_trap_subprocess("/server/memslot-invalid-addresses/subprocess/slot_id", 0, 0);
++    g_test_trap_assert_stderr("*slot_id 1 too big*");
++}
++
+ static void test_no_issues(void)
+ {
+     RedMemSlotInfo mem_info;
+@@ -262,6 +289,11 @@ int main(int argc, char *argv[])
+ {
+     g_test_init(&argc, &argv, NULL);
+ 
++    /* try to use invalid memslot group/slot */
++    g_test_add_func("/server/memslot-invalid-addresses", test_memslot_invalid_addresses);
++    g_test_add_func("/server/memslot-invalid-addresses/subprocess/group_id", test_memslot_invalid_group_id);
++    g_test_add_func("/server/memslot-invalid-addresses/subprocess/slot_id", test_memslot_invalid_slot_id);
++
+     /* try to create a surface with no issues, should succeed */
+     g_test_add_func("/server/qxl-parsing-no-issues", test_no_issues);
+ 
diff --git a/SPECS/spice.spec b/SPECS/spice.spec
new file mode 100644
index 0000000..e75c578
--- /dev/null
+++ b/SPECS/spice.spec
@@ -0,0 +1,468 @@
+Name:           spice
+Version:        0.14.0
+Release:        7%{?dist}
+Summary:        Implements the SPICE protocol
+Group:          User Interface/Desktops
+License:        LGPLv2+
+URL:            http://www.spice-space.org/
+Source0:        http://www.spice-space.org/download/releases/%{name}-%{version}.tar.bz2
+Patch1:         0001-inputs-channel-Check-message-size-handling-migration.patch
+Patch2:         0002-red-channel-Remove-red_channel_init_outgoing_message.patch
+Patch3:         0003-reds-Remove-leak-allocating-migration-state.patch
+Patch4:         0004-tests-Check-leaks-registering-migration-interface.patch
+Patch5:         0005-Notify-client-of-the-creation-of-new-channels-dynami.patch
+Patch6:         0006-stream-device-Add-device-to-handle-streaming.patch
+Patch7:         0007-stream-device-Start-parsing-new-protocol-from-guest.patch
+Patch8:         0008-stream-channel-Write-a-base-channel-to-implement-the.patch
+Patch9:         0009-stream-channel-Start-implementing-DisplayChannel-pro.patch
+Patch10:        0010-stream-device-Create-channel-for-stream-device.patch
+Patch11:        0011-stream-device-Handle-streaming-data-from-device-to-c.patch
+Patch12:        0012-stream-channel-Allows-not-fixed-size.patch
+Patch13:        0013-stream-channel-Allows-to-register-callback-to-get-ne.patch
+Patch14:        0014-stream-channel-Support-client-connection-disconnecti.patch
+Patch15:        0015-stream-channel-Do-not-show-an-empty-blank-screen-on-.patch
+Patch16:        0016-char-device-Do-not-stop-and-clear-interface-on-reset.patch
+Patch17:        0017-stream-device-Start-supporting-resetting-device-when.patch
+Patch18:        0018-stream-device-Create-channel-when-needed.patch
+Patch19:        0019-stream-device-Limit-sending-queue-from-guest-to-serv.patch
+Patch20:        0020-stream-channel-Activate-streaming-report-from-client.patch
+Patch21:        0021-reds-Disable-TLS-1.0.patch
+Patch22:        0022-cursor-Delay-release-of-QXL-guest-cursor-resources.patch
+Patch23:        0023-sound-Don-t-mute-recording-when-client-reconnects.patch
+Patch24:        0024-tls-Parse-spice.cnf-OpenSSL-configuration-file.patch
+Patch25:        0025-ssl-Allow-to-use-ECDH-ciphers-with-OpenSSL-1.0.patch
+Patch26:        0026-Fix-flexible-array-buffer-overflow.patch
+Patch27:        0027-dcc-Fix-QUIC-fallback-in-get_compression_for_bitmap.patch
+Patch28:        0028-memslot-Fix-off-by-one-error-in-group-slot-boundary-.patch
+
+# https://bugzilla.redhat.com/show_bug.cgi?id=613529
+%if 0%{?rhel}
+ExclusiveArch:  x86_64
+%else
+ExclusiveArch:  i686 x86_64 armv6l armv7l armv7hl
+%endif
+
+BuildRequires:  pkgconfig
+BuildRequires:  glib2-devel >= 2.22
+BuildRequires:  spice-protocol >= 0.12.10
+BuildRequires:  celt051-devel
+BuildRequires:  pixman-devel alsa-lib-devel openssl-devel libjpeg-turbo-devel
+BuildRequires:  libcacard-devel cyrus-sasl-devel
+BuildRequires:  lz4-devel
+BuildRequires:  pyparsing python-six
+BuildRequires:  opus-devel
+BuildRequires:  git
+BuildRequires:  autoconf automake libtool
+
+%description
+The Simple Protocol for Independent Computing Environments (SPICE) is
+a remote display system built for virtual environments which allows
+you to view a computing 'desktop' environment not only on the machine
+where it is running, but from anywhere on the Internet and from a wide
+variety of machine architectures.
+
+
+%package server
+Summary:        Implements the server side of the SPICE protocol
+Group:          System Environment/Libraries
+Obsoletes:      spice-client < %{version}-%{release}
+# Ensure SSL_CONF_CTX_set_ssl_ctx (needed by Patch24) is present
+# https://bugzilla.redhat.com/show_bug.cgi?id=1627693
+Requires:       openssl-libs >= 1.0.2k-16
+
+%description server
+The Simple Protocol for Independent Computing Environments (SPICE) is
+a remote display system built for virtual environments which allows
+you to view a computing 'desktop' environment not only on the machine
+where it is running, but from anywhere on the Internet and from a wide
+variety of machine architectures.
+
+This package contains the run-time libraries for any application that wishes
+to be a SPICE server.
+
+
+%package server-devel
+Summary:        Header files, libraries and development documentation for spice-server
+Group:          Development/Libraries
+Requires:       %{name}-server%{?_isa} = %{version}-%{release}
+Requires:       pkgconfig
+Requires:       spice-protocol >= 0.12.3
+
+%description server-devel
+This package contains the header files, static libraries and development
+documentation for spice-server. If you like to develop programs
+using spice-server, you will need to install spice-server-devel.
+
+
+%prep
+%autosetup -S git_am
+
+
+%build
+autoreconf -fi
+%configure --enable-smartcard --disable-client
+make %{?_smp_mflags} WARN_CFLAGS='' V=1
+
+
+%install
+make DESTDIR=%{buildroot} install
+rm -f %{buildroot}%{_libdir}/libspice-server.a
+rm -f %{buildroot}%{_libdir}/libspice-server.la
+mkdir -p %{buildroot}%{_libexecdir}
+
+
+%post server -p /sbin/ldconfig
+%postun server -p /sbin/ldconfig
+
+
+%files server
+%doc COPYING README NEWS docs/spice.cnf.sample
+%{_libdir}/libspice-server.so.1*
+
+%files server-devel
+%{_includedir}/spice-server
+%{_libdir}/libspice-server.so
+%{_libdir}/pkgconfig/spice-server.pc
+
+
+%changelog
+* Tue Dec 18 2018 Christophe Fergeau <cfergeau@redhat.com> - 0.14.0-7
+- Fix off-by-one error during guest-to-host memory address conversion
+  Resolves: CVE-2019-3813
+- Add patch for upstream commit 48179332d9da0. This should help with corrupted
+  spice-html5 displays
+  Resolves: rhbz#1573739
+- Add missing minimum openssl version Requires for patch #24
+  Resolves: rhbz#1627693
+
+* Thu Aug 09 2018 Frediano Ziglio <fziglio@redhat.com> - 0.14.0-6
+- Fix flexible array buffer overflow
+  Resolves: rhbz#1596008
+
+* Wed Jun 20 2018 Christophe Fergeau <cfergeau@redhat.com> - 0.14.0-5
+- Don't mute Record channel on client reconnection
+  Resolves: rhbz#1549132
+- Allow to configure TLS protocol versions and ciphers which SPICE will use for
+  TLS communications
+  Resolves: rhbz#1562213
+- Enable ECDH ciphers with OpenSSL 1.0
+  Resolves: rhbz#1566597
+
+* Fri Apr 27 2018 Christophe Fergeau <cfergeau@redhat.com> - 0.14.0-4
+- Revert back to spice 0.12 behaviour where QXL guest resources for cursor
+  commands are only released when the current cursor is replaced. This avoids
+  a QEMU regression causing crashes during migration
+  Resolves: rhbz#1567944
+
+* Tue Apr 03 2018 Christophe Fergeau <cfergeau@redhat.com> - 0.14.0-3
+- Disable TLSv1.0
+  Resolves: rhbz#1521053
+
+* Thu Oct 12 2017 Christophe Fergeau <cfergeau@redhat.com> - 0.14.0-2
+- Add streaming patches for use with spice-streaming-agent
+  Related: rhbz#1478356
+
+* Wed Oct 11 2017 Christophe Fergeau <cfergeau@redhat.com> - 0.14.0-1
+- Rebase to 0.14.0 release
+  Resolves: rhbz#1472948
+
+* Fri Sep 22 2017 Christophe Fergeau <cfergeau@redhat.com> 0.13.90-2
+- Add lz4-devel BuildRequires
+  Resolves: rhbz#1460191
+
+* Wed Jul 26 2017 Christophe Fergeau <cfergeau@redhat.com> 0.13.90-1
+- Rebase to latest upstream release
+  Resolves: rhbz#1472948
+
+* Fri Jul 14 2017 Jonathon Jongsma <jjongsma@redhat.com> - 0.12.8-4
+- build with opus support
+  Resolves: rhbz#1456832
+
+* Fri Jun 30 2017 Christophe Fergeau <cfergeau@redhat.com> 0.12.8-3
+- Prevent potential buffer/integer overflows with invalid MonitorsConfig messages
+  sent from an authenticated client
+  Resolves: CVE-2017-7506
+
+* Tue Apr 25 2017 Christophe Fergeau <cfergeau@redhat.com> 0.12.8-2
+- Drop clients immediatly if the magic they send is wrong
+  Resolves: rhbz#1416692
+
+* Mon Jan 16 2017 Christophe Fergeau <cfergeau@redhat.com> 0.12.8-1
+- Rebase to spice-server 0.12.8
+  Resolves: rhbz#1388947
+  Resolves: rhbz#1377551
+  Resolves: rhbz#1283202
+* Fri Dec 09 2016 Frediano Ziglio <fziglio@redhat.com> - 0.12.4-20
+- Fix buffer overflow in main_channel_alloc_msg_rcv_buf when reading large
+  messages.
+  Resolves: CVE-2016-9577
+- Fix remote DoS via crafted message.
+  Resolves: CVE-2016-9578
+* Fri Sep 09 2016 Christophe Fergeau <cfergeau@redhat.com> 0.12.4-19
+- Ensure SPICE_MIGRATE_COMPLETED is sent in all cases when it's needed.
+  Resolves: rhbz#1352836
+* Fri Jul 01 2016 Christophe Fergeau <cfergeau@redhat.com> - 0.12.4-18
+- Fix crash when connecting to VM using smartcard passthrough
+  Resolves: rhbz#1340899
+- Fix hang after unredirecting a USB device
+  Resolves: rhbz#1338752
+- Backport spice_qxl_set_max_monitors()
+  Resolves: rhbz#1283202
+* Wed Apr 27 2016 Christophe Fergeau <cfergeau@redhat.com> - 0.12.4-17
+- Fix crash when the client sends a wrong header (for example when using spice-html5)
+  Resolves: rhbz#1281442
+- Fix crash when guest provides wrong address
+  Resolves: rhbz#1264356
+- Fix thread-safety issue causing a crash when playing a Youtube video spanning
+  multiple monitors
+  Resolves: rhbz#1253375
+- Add patches reducing QEMU wake-ups
+  Related: rhbz#912763, rhbz#1186146
+- Fix use-after-free after resetting a VM
+  Resolves: rhbz#1281455
+- Send KeepAlive probes every 10 minutes
+  Resolves: rhbz#1298590
+- Add client to guest volume synchronization
+  Resolves: rhbz#1264107
+
+* Mon Apr 25 2016 Christophe Fergeau <cfergeau@redhat.com> - 0.12.4-16
+- Use autosetup
+  Related: CVE-2016-0749
+- Fix heap-based memory corruption within smartcard handling
+  Resolves: CVE-2016-0749
+- Fix host memory access from guest with invalid primary surface parameters
+  Resolves: CVE-2016-2150
+
+* Wed Sep 23 2015 Frediano Ziglio <fziglio@redhat.com> 0.12.4-15
+- CVE-2015-5260 CVE-2015-5261 fixed various security flaws
+  Resolves: rhbz#1267134
+
+* Thu Sep 10 2015 Frediano Ziglio <fziglio@redhat.com> 0.12.4-14
+- Validate surface_id
+  Resolves: rhbz#1260971
+
+* Tue Jul 21 2015 Frediano Ziglio <fziglio@redhat.com> 0.12.4-13
+- Clean stale statistics file before creating a new one
+  Resolves: rhbz#1177326
+
+* Fri Jul 10 2015 Fabiano Fidêncio <fidencio@redhat.com> 0.12.4-12
+- Fix a backport issue on Patch0040.
+  Related: rhbz#1071176
+  Resolves: rhbz#1241860
+
+* Thu Jul 09 2015 Fabiano Fidêncio <fidencio@redhat.com> 0.12.4-11
+- Don't assert on invalid client message
+  Resolves: rhbz#1227410
+- Don't truncate large 'now' values in _spice_timer_set
+  Resolves: rhbz#1227408
+- Avoid race conditions reading monitor configs from guest
+  Resolves: rhbz#1239128
+- Lock the pixmap image cache for the entire fill_bits call
+  Resolves: rhbz#1235443
+
+* Wed Jul 08 2015 Fabiano Fidêncio <fidencio@redhat.com> 0.12.4-10
+- Fix qemu segmentation fault (core dumped) when boot KVM guest with
+  spice in FIPS enabled mode.
+  Resolves: rhbz#1071176
+
+* Mon Jan 05 2015 Marc-Andre Lureau <marcandre.lureau@redhat.com> 0.12.4-9
+- Allow recent TLS/SSL methods, block SSLv2/SSLv3. Resolves: rhbz#1175540
+
+* Tue Oct 21 2014 Christophe Fergeau <cfergeau@redhat.com> 0.12.4-8
+- Fix defects reported by Coverity
+  Resolves: rhbz#885717
+- Validate surface bounding box sent from QXL driver
+  Resolves: rhbz#1052856
+- Fix assertion sometimes happening during migration while a client is
+  connected
+  Resolves: rhbz#1035184
+- Fix crash when restarting VM with old client
+  Resolves: rhbz#1145919
+
+* Thu Sep 18 2014 Christophe Fergeau <cfergeau@redhat.com> 0.12.4-7
+- Fix assert in mjpeg_encoder_adjust_params_to_bit_rate()
+  Resolves: rhbz#1086823
+- Fix "Spice-ERROR **: reds.c:1464:reds_send_link_ack: assertion
+  `link->link_mess->channel_type == SPICE_CHANNEL_MAIN' failed" assertion
+  Resolves: rhbz#1058625
+- Lower a monitor-config warning to debug level
+  Resolves: rhbz#1119220
+- mjpeg: Don't warn on unsupported image formats
+  Resolves: rhbz#1070028
+
+* Thu Aug 07 2014 Marc-Andre Lureau <marcandre.lureau@redhat.com> 0.12.4-6
+- Fix invalid surface clearing
+  Resolves: rhbz#1029646
+
+* Wed Jan 29 2014 Christophe Fergeau <cfergeau@redhat.com> 0.12.4-5
+- Fix qemu crash during migration with reboot
+  Resolves: rhbz#1016795
+- Monitor whether the client is alive
+  Resolves: rhbz#1016790
+
+* Tue Oct 15 2013 Christophe Fergeau <cfergeau@redhat.com> 0.12.4-3
+- Fix spice-server crash when client sends a password which is too long
+  Resolves: CVE-2013-4282
+
+* Fri Sep 13 2013 Christophe Fergeau <cfergeau@redhat.com> 0.12.4-2
+- Add upstream patch fixing rhbz#995041
+
+* Fri Aug  2 2013 Hans de Goede <hdegoede@redhat.com> - 0.12.4-1
+- Add patches from upstream git to fix sound-channel-free crash (rhbz#986407)
+- Add Obsoletes for dropped spice-client sub-package
+
+* Mon Jul 22 2013 Yonit Halperin <yhalperi@redhat.com> 0.12.4-1
+- New upstream release 0.12.4
+- Require libjpeg-turbo-devel instead of libjpeg-devel
+- Remove "BuildRequires: spice-protocol" from spice-server
+- Add "Requires: spice-protocol" to spice-server-devel.
+
+* Thu May 23 2013 Christophe Fergeau <cfergeau@redhat.com> 0.12.3-2
+- Stop building spicec, it's obsolete and superseded by remote-viewer
+  (part of virt-viewer)
+
+* Tue May 21 2013 Christophe Fergeau <cfergeau@redhat.com> 0.12.3-1
+- New upstream release 0.12.3
+- Drop all patches (they were all upstreamed)
+
+* Mon Apr 15 2013 Hans de Goede <hdegoede@redhat.com> - 0.12.2-4
+- Add fix from upstream for a crash when the guest uses RGBA (rhbz#952242)
+
+* Thu Mar 07 2013 Adam Jackson <ajax@redhat.com> 0.12.2-4
+- Rebuild for new libsasl2 soname in F19
+
+* Mon Jan 21 2013 Hans de Goede <hdegoede@redhat.com> - 0.12.2-3
+- Add a number of misc. bug-fixes from upstream
+
+* Fri Dec 21 2012 Adam Tkac <atkac redhat com> - 0.12.2-2
+- rebuild against new libjpeg
+
+* Thu Dec 20 2012 Hans de Goede <hdegoede@redhat.com> - 0.12.2-1
+- New upstream release 0.12.2
+
+* Fri Sep 28 2012 Hans de Goede <hdegoede@redhat.com> - 0.12.0-1
+- New upstream release 0.12.0
+- Some minor spec file cleanups
+- Enable building on arm
+
+* Thu Sep 6 2012 Soren Sandmann <ssp@redhat.com> - 0.11.3-1
+- BuildRequire pyparsing
+
+* Thu Sep 6 2012 Soren Sandmann <ssp@redhat.com> - 0.11.3-1
+- Add capability patches
+- Add capability patches to the included copy of spice-protocol
+
+    Please see the comment above Patch6 and Patch7
+    regarding this situation.
+
+* Thu Sep 6 2012 Soren Sandmann <ssp@redhat.com> - 0.11.3-1
+- Update to 0.11.3 and drop upstreamed patches
+- BuildRequire spice-protocol 0.12.1
+
+* Sat Jul 21 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.10.1-6
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Mon May 14 2012 Alon Levy <alevy@redhat.com>
+- Fix mjpeg memory leak and bad behavior.
+- Add usbredir to list of channels for security purposes. (#819484)
+
+* Sun May 13 2012 Alon Levy <alevy@redhat.com>
+- Add double free fix. (#808936)
+
+* Tue Apr 24 2012 Alon Levy <alevy@redhat.com>
+- Add 32 bit fixes from git master. (#815717)
+
+* Tue Feb 28 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.10.1-2
+- Rebuilt for c++ ABI breakage
+
+* Mon Jan 23 2012 Hans de Goede <hdegoede@redhat.com> - 0.10.1-1
+- New upstream release 0.10.1
+
+* Sat Jan 14 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.10.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
+
+* Thu Nov 10 2011 Alon Levy <alevy@redhat.com> - 0.10.0-1
+- New upstream release 0.10.0
+- support spice-server.i686
+
+* Wed Sep 28 2011 Marc-André Lureau <marcandre.lureau@redhat.com> - 0.9.1-2
+- Provides spice-xpi-client alternative in spice-client
+
+* Thu Aug 25 2011 Hans de Goede <hdegoede@redhat.com> - 0.9.1-1
+- New upstream release 0.9.1
+
+* Mon Jul 25 2011 Marc-André Lureau <marcandre.lureau@redhat.com> - 0.9.0-1
+- New upstream release 0.9.0
+
+* Wed Apr 20 2011 Hans de Goede <hdegoede@redhat.com> - 0.8.1-1
+- New upstream release 0.8.1
+
+* Fri Mar 11 2011 Hans de Goede <hdegoede@redhat.com> - 0.8.0-2
+- Fix being unable to send ctrl+alt+key when release mouse is bound to
+  ctrl+alt (which can happen when used from RHEV-M)
+
+* Tue Mar  1 2011 Hans de Goede <hdegoede@redhat.com> - 0.8.0-1
+- New upstream release 0.8.0
+
+* Fri Feb 11 2011 Hans de Goede <hdegoede@redhat.com> - 0.7.3-1
+- New upstream release 0.7.3
+
+* Wed Feb 09 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.7.2-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
+
+* Wed Jan 19 2011 Hans de Goede <hdegoede@redhat.com> - 0.7.2-1
+- New upstream release 0.7.2
+
+* Fri Dec 17 2010 Hans de Goede <hdegoede@redhat.com> - 0.7.1-1
+- New upstream release 0.7.1
+- Drop all patches (all upstreamed)
+- Enable smartcard (CAC) support
+
+* Wed Nov 17 2010 Hans de Goede <hdegoede@redhat.com> - 0.6.3-4
+- Fix the info layer not showing when used through the XPI
+- Do not let the connection gui flash by when a hostname has been specified
+  on the cmdline
+- Fix spice client locking up when dealing with XIM input (#654265)
+- Fix modifier keys getting stuck (#655048)
+- Fix spice client crashing when dealing with XIM ibus input (#655836)
+- Fix spice client only showing a white screen in full screen mode
+
+* Sat Nov  6 2010 Hans de Goede <hdegoede@redhat.com> - 0.6.3-3
+- Log to ~/.spicec/cegui.log rather then to CEGUI.log in the cwd, this
+  fixes spicec from aborting when run in a non writable dir (#650253)
+
+* Fri Nov  5 2010 Hans de Goede <hdegoede@redhat.com> - 0.6.3-2
+- Various bugfixes from upstream git:
+  - Make spicec work together with the Firefox XPI for RHEV-M
+  - Make sure the spicec window gets properly raised when first shown
+
+* Mon Oct 18 2010 Hans de Goede <hdegoede@redhat.com> - 0.6.3-1
+- Update to 0.6.3
+- Enable GUI
+
+* Thu Sep 30 2010 Gerd Hoffmann <kraxel@redhat.com> - 0.6.1-1
+- Update to 0.6.1.
+
+* Tue Aug 31 2010 Alexander Larsson <alexl@redhat.com> - 0.6.0-1
+- Update to 0.6.0 (stable release)
+
+* Tue Jul 20 2010 Alexander Larsson <alexl@redhat.com> - 0.5.3-1
+- Update to 0.5.3
+
+* Tue Jul 13 2010 Gerd Hoffmann <kraxel@redhat.com> - 0.5.2-4
+- Quote %% in changelog to avoid macro expansion.
+
+* Mon Jul 12 2010 Gerd Hoffmann <kraxel@redhat.com> - 0.5.2-3
+- %%configure handles CFLAGS automatically, no need to fiddle
+  with %%{optflags} manually.
+
+* Mon Jul 12 2010 Gerd Hoffmann <kraxel@redhat.com> - 0.5.2-2
+- Fix license: LGPL.
+- Cleanup specfile, drop bits not needed any more with
+  recent rpm versions (F13+).
+- Use optflags as-is.
+-
+
+* Fri Jul 9 2010 Gerd Hoffmann <kraxel@redhat.com> - 0.5.2-1
+- initial package.
+