[Spice-commits] 13 commits - client/application.cpp client/application.h client/Makefile.am client/smartcard_channel.cpp client/smartcard_channel.h client/x11 configure.ac python_modules/ptypes.py server/char_device.h server/Makefile.am server/reds.c server/smartcard.c server/smartcard.h server/spice-experimental.h spice.proto

Alon Levy alon at kemper.freedesktop.org
Tue Dec 7 03:35:29 PST 2010


 client/Makefile.am           |   10 
 client/application.cpp       |   65 +++++
 client/application.h         |   12 
 client/smartcard_channel.cpp |  481 ++++++++++++++++++++++++++++++++++++++
 client/smartcard_channel.h   |  135 ++++++++++
 client/x11/Makefile.am       |    9 
 configure.ac                 |   22 +
 python_modules/ptypes.py     |    2 
 server/Makefile.am           |   11 
 server/char_device.h         |   11 
 server/reds.c                |   54 +++-
 server/smartcard.c           |  532 +++++++++++++++++++++++++++++++++++++++++++
 server/smartcard.h           |   18 +
 server/spice-experimental.h  |    8 
 spice.proto                  |    8 
 15 files changed, 1363 insertions(+), 15 deletions(-)

New commits:
commit 9dfeeaefbe10163863192703ca35ac65e02bc0db
Author: Alon Levy <alevy at redhat.com>
Date:   Tue Nov 30 20:34:20 2010 +0200

    client/smartcard: add files to Makefile.am for make dist

diff --git a/client/Makefile.am b/client/Makefile.am
index 166ce5e..da859f4 100644
--- a/client/Makefile.am
+++ b/client/Makefile.am
@@ -28,8 +28,10 @@ else
 GL_SRCS =
 endif
 
+SMARTCARD_SRC_ENABLED = smartcard_channel.cpp smartcard_channel.h
+
 if SUPPORT_SMARTCARD
-SMARTCARD_SRCS = smartcard_channel.cpp
+SMARTCARD_SRCS = $(SMARTCARD_SRC_ENABLED)
 else
 SMARTCARD_SRCS =
 endif
@@ -140,6 +142,6 @@ GDI_FILES =				\
 
 MAINTAINERCLEANFILES = $(spice_built_sources)
 
-EXTRA_DIST = $(RED_COMMON_SRCS) $(spice_built_sources) $(GL_SRCS) $(GDI_FILES) $(SMARTCARD_SRCS)
+EXTRA_DIST = $(RED_COMMON_SRCS) $(spice_built_sources) $(GL_SRCS) $(GDI_FILES) $(SMARTCARD_SRC_ENABLED)
 
 BUILT_SOURCES = $(spice_built_sources)
commit 7e0a1dfa75d9c967b96929c56dce4cecb7151a85
Author: Alon Levy <alevy at redhat.com>
Date:   Wed Sep 15 15:55:37 2010 +0200

    smartcard: configure option --enable-smartcard

diff --git a/client/Makefile.am b/client/Makefile.am
index 5a14f5f..166ce5e 100644
--- a/client/Makefile.am
+++ b/client/Makefile.am
@@ -28,6 +28,12 @@ else
 GL_SRCS =
 endif
 
+if SUPPORT_SMARTCARD
+SMARTCARD_SRCS = smartcard_channel.cpp
+else
+SMARTCARD_SRCS =
+endif
+
 RED_COMMON_SRCS =			\
 	application.cpp			\
 	application.h			\
@@ -134,6 +140,6 @@ GDI_FILES =				\
 
 MAINTAINERCLEANFILES = $(spice_built_sources)
 
-EXTRA_DIST = $(RED_COMMON_SRCS) $(spice_built_sources) $(GL_SRCS) $(GDI_FILES)
+EXTRA_DIST = $(RED_COMMON_SRCS) $(spice_built_sources) $(GL_SRCS) $(GDI_FILES) $(SMARTCARD_SRCS)
 
 BUILT_SOURCES = $(spice_built_sources)
diff --git a/client/x11/Makefile.am b/client/x11/Makefile.am
index 45ff7fc..42b8b0a 100644
--- a/client/x11/Makefile.am
+++ b/client/x11/Makefile.am
@@ -27,6 +27,7 @@ INCLUDES = \
 	$(CEGUI_CFLAGS)					\
 	$(WARN_CFLAGS)                                  \
 	$(SPICE_NONPKGCONFIG_CFLAGS)			\
+	$(SMARTCARD_CFLAGS)				\
 	$(NULL)
 
 
@@ -158,6 +159,12 @@ else
 RED_OGL_SRCS =
 endif
 
+if SUPPORT_SMARTCARD
+RED_SCARD_SRCS = $(CLIENT_DIR)/smartcard_channel.cpp
+else
+RED_SCARD_SRCS =
+endif
+
 bin_PROGRAMS = spicec
 
 spicec_SOURCES =			\
@@ -191,6 +198,7 @@ spicec_SOURCES =			\
 	$(RED_GUI_SRCS)			\
 	$(RED_TUNNEL_SRCS)		\
 	$(RED_OGL_SRCS)			\
+	$(RED_SCARD_SRCS)		\
 	$(NULL)
 
 spicec_LDFLAGS = \
@@ -200,6 +208,7 @@ spicec_LDFLAGS = \
 	$(CEGUI_LIBS)					\
 	$(JPEG_LIBS)					\
 	$(Z_LIBS)					\
+	$(SMARTCARD_LIBS)				\
 	$(SPICE_NONPKGCONFIG_LIBS)
 
 spicec_LDADD =						\
diff --git a/configure.ac b/configure.ac
index 8742fab..4f3b118 100644
--- a/configure.ac
+++ b/configure.ac
@@ -113,6 +113,16 @@ AC_ARG_ENABLE(opengl,
 [  --enable-opengl         Enable opengl requirement / support (not recommended)],
 [  have_opengl=yes])
 AM_CONDITIONAL(SUPPORT_GL, test "x$have_opengl" = "xyes")
+
+have_smartcard=no
+AC_ARG_ENABLE(smartcard,
+[  --enable-smartcard         Enable network redirection],
+[  have_smartcard=yes])
+AM_CONDITIONAL(SUPPORT_SMARTCARD, test "x$have_smartcard" = "xyes")
+if test "x$have_smartcard" = "xyes"; then
+   AC_DEFINE(USE_SMARTCARD, [1], [Define if supporting smartcard proxying])
+fi
+
 dnl =========================================================================
 dnl Check deps
 
@@ -168,6 +178,16 @@ if test "x$have_tunnel" = "xyes"; then
 	AC_DEFINE([HAVE_SLIRP], [], [Define if we have slirp])
 fi
 
+if test "x$have_smartcard" = "xyes"; then
+    PKG_CHECK_MODULES(CAC_CARD, cac_card >= 0.0.1)
+    SMARTCARD_LIBS="$CAC_CARD_LIBS"
+    SMARTCARD_CFLAGS="$CAC_CARD_CFLAGS"
+    SPICE_REQUIRES+=" cac_card"
+    AC_SUBST(SMARTCARD_LIBS)
+    AC_SUBST(SMARTCARD_CFLAGS)
+fi
+
+
 PKG_CHECK_MODULES(PIXMAN, pixman-1 >= 0.17.7)
 AC_SUBST(PIXMAN_CFLAGS)
 AC_SUBST(PIXMAN_LIBS)
@@ -422,5 +442,7 @@ echo "
 
         GUI:                      ${use_gui}
 
+        Smartcard:                ${have_smartcard}
+
         Now type 'make' to build $PACKAGE
 "
diff --git a/server/Makefile.am b/server/Makefile.am
index ff7b485..2c86a2c 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -17,6 +17,7 @@ INCLUDES = \
 	-DRED_STATISTICS			\
 	$(WARN_CFLAGS)                          \
 	$(VISIBILITY_HIDDEN_CFLAGS)		\
+	$(SMARTCARD_CFLAGS)				\
 	$(NULL)
 
 spice_built_sources = generated_marshallers.c generated_marshallers.h generated_demarshallers.c
@@ -81,6 +82,15 @@ else
 TUNNEL_SRCS =
 endif
 
+if SUPPORT_SMARTCARD
+SMARTCARD_SRCS =		\
+	smartcard.c			\
+	smartcard.h			\
+	$(NULL)
+else
+SMARTCARD_SRCS =
+endif
+
 libspice_server_la_SOURCES =			\
 	demarshallers.h				\
 	glz_encoder.c				\
@@ -121,6 +131,7 @@ libspice_server_la_SOURCES =			\
 	zlib_encoder.h				\
 	char_device.h				\
 	$(TUNNEL_SRCS)				\
+	$(SMARTCARD_SRCS)			\
 	$(COMMON_SRCS)				\
 	$(GL_SRCS)				\
 	$(NULL)
commit d99ec6c35b02a64950c4397644a9a81fad1d4492
Author: Alon Levy <alevy at redhat.com>
Date:   Wed Sep 15 15:55:11 2010 +0200

    smartcard: server side (not enabled yet)

diff --git a/server/reds.c b/server/reds.c
index 3edf474..b4ec6e1 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -59,6 +59,9 @@
 #ifdef USE_TUNNEL
 #include "red_tunnel_worker.h"
 #endif
+#ifdef USE_SMARTCARD
+#include "smartcard.h"
+#endif
 
 SpiceCoreInterface *core = NULL;
 static SpiceKbdInstance *keyboard = NULL;
@@ -1251,6 +1254,7 @@ static int read_from_vdi_port(void)
     VDIReadBuf *dispatch_buf;
     int total = 0;
     int n;
+
     if (inside_call) {
         return 0;
     }
@@ -3432,9 +3436,13 @@ __visible__ void spice_server_char_device_wakeup(SpiceCharDeviceInstance* sin)
 }
 
 #define SUBTYPE_VDAGENT "vdagent"
+#define SUBTYPE_SMARTCARD "smartcard"
 
 const char *spice_server_char_device_recognized_subtypes_list[] = {
     SUBTYPE_VDAGENT,
+#ifdef USE_SMARTCARD
+    SUBTYPE_SMARTCARD,
+#endif
     NULL,
 };
 
@@ -3460,6 +3468,13 @@ static int spice_server_char_device_add_interface(SpiceServer *s,
         char_device->st = &vdagent_char_device_state;
         attach_to_red_agent(char_device);
     }
+#ifdef USE_SMARTCARD
+    else if (strcmp(char_device->subtype, SUBTYPE_SMARTCARD) == 0) {
+        if (smartcard_device_connect(char_device) == -1) {
+            return -1;
+        }
+    }
+#endif
     return 0;
 }
 
@@ -3474,6 +3489,11 @@ static void spice_server_char_device_remove_interface(SpiceBaseInstance *sin)
             reds_agent_remove();
         }
     }
+#ifdef USE_SMARTCARD
+    else if (strcmp(char_device->subtype, SUBTYPE_SMARTCARD) == 0) {
+        smartcard_device_disconnect(char_device);
+    }
+#endif
 }
 
 __visible__ int spice_server_add_interface(SpiceServer *s,
@@ -3759,6 +3779,10 @@ static void do_spice_init(SpiceCoreInterface *core_interface)
     }
     inputs_init();
 
+#ifdef USE_SMARTCARD
+    smartcard_channel_init();
+#endif
+
     reds->mouse_mode = SPICE_MOUSE_MODE_SERVER;
     atexit(reds_exit);
 }
diff --git a/server/smartcard.c b/server/smartcard.c
new file mode 100644
index 0000000..7c0a5aa
--- /dev/null
+++ b/server/smartcard.c
@@ -0,0 +1,532 @@
+#include "server/char_device.h"
+#include "server/red_channel.h"
+#include "server/smartcard.h"
+#include "vscard_common.h"
+
+#define SMARTCARD_MAX_READERS 10
+
+typedef struct SmartCardDeviceState {
+    SpiceCharDeviceState base;
+    reader_id_t          reader_id;
+    uint32_t             attached;
+    uint8_t             *buf;
+    uint32_t             buf_size;
+    uint8_t             *buf_pos;
+    uint32_t             buf_used;
+} SmartCardDeviceState;
+
+enum {
+    PIPE_ITEM_TYPE_ERROR=1,
+    PIPE_ITEM_TYPE_READER_ADD_RESPONSE,
+    PIPE_ITEM_TYPE_MSG,
+};
+
+typedef struct ErrorItem {
+    PipeItem base;
+    reader_id_t reader_id;
+    uint32_t error;
+} ErrorItem;
+
+typedef struct ReaderAddResponseItem {
+    PipeItem base;
+    uint32_t reader_id;
+} ReaderAddResponseItem;
+
+typedef struct MsgItem {
+    PipeItem base;
+    VSCMsgHeader* vheader;
+} MsgItem;
+
+typedef struct SmartCardChannel {
+    RedChannel base;
+} SmartCardChannel;
+
+static SmartCardChannel *g_smartcard_channel = NULL;
+
+static struct Readers {
+    uint32_t num;
+    SpiceCharDeviceInstance* sin[SMARTCARD_MAX_READERS];
+} g_smartcard_readers = {0, {NULL}};
+
+static SpiceCharDeviceInstance* smartcard_readers_get_unattached();
+static SpiceCharDeviceInstance* smartcard_readers_get(reader_id_t reader_id);
+static int smartcard_char_device_add_to_readers(SpiceCharDeviceInstance *sin);
+static void smartcard_char_device_attach(
+    SpiceCharDeviceInstance *char_device, SmartCardChannel *smartcard_channel);
+static void smartcard_char_device_detach(
+    SpiceCharDeviceInstance *char_device, SmartCardChannel *smartcard_channel);
+static void smartcard_channel_write_to_reader(
+    SmartCardChannel *smartcard_channel, VSCMsgHeader *vheader);
+
+static void smartcard_char_device_on_message_from_device(
+    SmartCardDeviceState *state, VSCMsgHeader *header);
+static void smartcard_on_message_from_device(
+    SmartCardChannel *smartcard_channel, VSCMsgHeader *vheader);
+static SmartCardDeviceState* smartcard_device_state_new();
+static void smartcard_device_state_free(SmartCardDeviceState* st);
+
+void smartcard_char_device_wakeup(SpiceCharDeviceInstance *sin)
+{
+    SmartCardDeviceState* state = SPICE_CONTAINEROF(
+                            sin->st, SmartCardDeviceState, base);
+    SpiceCharDeviceInterface *sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base);
+    VSCMsgHeader *vheader = (VSCMsgHeader*)state->buf;
+    int n;
+    int remaining;
+
+    while ((n = sif->read(sin, state->buf_pos, state->buf_size - state->buf_used)) > 0) {
+        state->buf_pos += n;
+        state->buf_used += n;
+        if (state->buf_used < sizeof(VSCMsgHeader)) {
+            continue;
+        }
+        if (vheader->length > state->buf_size) {
+            state->buf_size = MAX(state->buf_size*2, vheader->length + sizeof(VSCMsgHeader));
+            state->buf = spice_realloc(state->buf, state->buf_size);
+            ASSERT(state->buf != NULL);
+        }
+        if (state->buf_used - sizeof(VSCMsgHeader) < vheader->length) {
+            continue;
+        }
+        smartcard_char_device_on_message_from_device(state, vheader);
+        remaining = state->buf_used - sizeof(VSCMsgHeader) > vheader->length;
+        if (remaining > 0) {
+            memcpy(state->buf, state->buf_pos, remaining);
+        }
+        state->buf_pos = state->buf;
+        state->buf_used = remaining;
+    }
+}
+
+void smartcard_char_device_on_message_from_device(
+    SmartCardDeviceState *state,
+    VSCMsgHeader *vheader)
+{
+    VSCMsgHeader *sent_header;
+
+    switch (vheader->type) {
+        case VSC_Init:
+            return;
+            break;
+        case VSC_ReaderAddResponse:
+            /* The device sends this for vscclient, we send one ourselves,
+             * a second would be an error. */
+            return;
+            break;
+        case VSC_Reconnect:
+            /* Ignore VSC_Reconnect messages, spice channel reconnection does the same. */
+            return;
+            break;
+        default:
+            break;
+    }
+    ASSERT(state->reader_id != VSCARD_UNDEFINED_READER_ID);
+    ASSERT(g_smartcard_channel != NULL);
+    sent_header = spice_memdup(vheader, sizeof(*vheader) + vheader->length);
+    sent_header->reader_id = state->reader_id;
+    smartcard_on_message_from_device(g_smartcard_channel, sent_header);
+}
+
+static void smartcard_readers_detach_all(SmartCardChannel *smartcard_channel)
+{
+    int i;
+
+    for (i = 0 ; i < g_smartcard_readers.num; ++i) {
+        smartcard_char_device_detach(g_smartcard_readers.sin[i],
+                                     smartcard_channel);
+    }
+}
+
+static int smartcard_char_device_add_to_readers(SpiceCharDeviceInstance *char_device)
+{
+    SmartCardDeviceState* state = SPICE_CONTAINEROF(
+                            char_device->st, SmartCardDeviceState, base);
+
+    if (g_smartcard_readers.num >= SMARTCARD_MAX_READERS) {
+        return -1;
+    }
+    state->reader_id = g_smartcard_readers.num;
+    g_smartcard_readers.sin[g_smartcard_readers.num++] = char_device;
+    return 0;
+}
+
+static SpiceCharDeviceInstance *smartcard_readers_get(reader_id_t reader_id)
+{
+    ASSERT(reader_id < g_smartcard_readers.num);
+    return g_smartcard_readers.sin[reader_id];
+}
+
+static SpiceCharDeviceInstance *smartcard_readers_get_unattached()
+{
+    int i;
+    SmartCardDeviceState* state;
+
+    for (i = 0; i < g_smartcard_readers.num; ++i) {
+        state = SPICE_CONTAINEROF(g_smartcard_readers.sin[i]->st,
+                                  SmartCardDeviceState, base);
+        if (!state->attached) {
+            return g_smartcard_readers.sin[i];
+        }
+    }
+    return NULL;
+}
+
+static SmartCardDeviceState* smartcard_device_state_new()
+{
+    SmartCardDeviceState *st;
+
+    st = spice_new0(SmartCardDeviceState, 1);
+    st->base.wakeup = smartcard_char_device_wakeup;
+    st->reader_id = VSCARD_UNDEFINED_READER_ID;
+    st->attached = FALSE;
+    st->buf_size = APDUBufSize + sizeof(VSCMsgHeader);
+    st->buf = spice_malloc(st->buf_size);
+    st->buf_pos = st->buf;
+    st->buf_used = 0;
+    return st;
+}
+
+static void smartcard_device_state_free(SmartCardDeviceState* st)
+{
+    free(st->buf);
+    free(st);
+}
+
+void smartcard_device_disconnect(SpiceCharDeviceInstance *char_device)
+{
+    SmartCardDeviceState *st = SPICE_CONTAINEROF(char_device->st,
+        SmartCardDeviceState, base);
+
+    smartcard_device_state_free(st);
+}
+
+int smartcard_device_connect(SpiceCharDeviceInstance *char_device)
+{
+    SmartCardDeviceState *st;
+
+    st = smartcard_device_state_new();
+    char_device->st = &st->base;
+    if (smartcard_char_device_add_to_readers(char_device) == -1) {
+        smartcard_device_state_free(st);
+        return -1;
+    }
+    return 0;
+}
+
+static void smartcard_char_device_attach(
+    SpiceCharDeviceInstance *char_device, SmartCardChannel *smartcard_channel)
+{
+    SmartCardDeviceState *st = SPICE_CONTAINEROF(char_device->st, SmartCardDeviceState, base);
+
+    if (st->attached == TRUE) {
+        return;
+    }
+    st->attached = TRUE;
+    VSCMsgHeader vheader = {.type = VSC_ReaderAdd, .reader_id=st->reader_id,
+        .length=0};
+    smartcard_channel_write_to_reader(smartcard_channel, &vheader);
+}
+
+static void smartcard_char_device_detach(
+    SpiceCharDeviceInstance *char_device, SmartCardChannel *smartcard_channel)
+{
+    SmartCardDeviceState *st = SPICE_CONTAINEROF(char_device->st, SmartCardDeviceState, base);
+
+    if (st->attached == FALSE) {
+        return;
+    }
+    st->attached = FALSE;
+    VSCMsgHeader vheader = {.type = VSC_ReaderRemove, .reader_id=st->reader_id,
+        .length=0};
+    smartcard_channel_write_to_reader(smartcard_channel, &vheader);
+}
+
+static int smartcard_channel_config_socket(RedChannel *channel)
+{
+    return TRUE;
+}
+
+static uint8_t *smartcard_channel_alloc_msg_rcv_buf(RedChannel *channel, SpiceDataHeader *msg_header)
+{
+    //red_printf("allocing %d bytes", msg_header->size);
+    return spice_malloc(msg_header->size);
+}
+
+static void smartcard_channel_release_msg_rcv_buf(RedChannel *channel, SpiceDataHeader *msg_header,
+                                               uint8_t *msg)
+{
+    red_printf("freeing %d bytes", msg_header->size);
+    free(msg);
+}
+
+static void smartcard_channel_send_data(RedChannel *channel, PipeItem *item, VSCMsgHeader *vheader)
+{
+    ASSERT(channel);
+    ASSERT(vheader);
+    red_channel_init_send_data(channel, SPICE_MSG_SMARTCARD_DATA, item);
+    red_channel_add_buf(channel, vheader, sizeof(VSCMsgHeader));
+    if (vheader->length > 0) {
+        red_channel_add_buf(channel, (uint8_t*)(vheader+1), vheader->length);
+    }
+    red_channel_begin_send_message(channel);
+}
+
+static void smartcard_channel_send_message(RedChannel *channel, PipeItem *item,
+    uint32_t reader_id, VSCMsgType type, uint8_t* data, uint32_t len)
+{
+    VSCMsgHeader mhHeader;
+    //SpiceMarshaller* m = msg->marshaller();
+
+    mhHeader.type = type;
+    mhHeader.length = len;
+    mhHeader.reader_id = reader_id;
+    //_marshallers->msg_SpiceMsgData(m, &msgdata);
+    //spice_marshaller_add(m, (uint8_t*)&mhHeader, sizeof(mhHeader));
+    //spice_marshaller_add(m, data, len);
+    //marshaller_outgoing_write(msg);
+
+    smartcard_channel_send_data(channel, item, &mhHeader);
+}
+
+static void smartcard_channel_send_error(
+    SmartCardChannel *smartcard_channel, PipeItem *item)
+{
+    ErrorItem* error_item = (ErrorItem*)item;
+    VSCMsgError error;
+
+    error.code = error_item->error;
+    smartcard_channel_send_message(&smartcard_channel->base, item, error_item->reader_id,
+        VSC_Error, (uint8_t*)&error, sizeof(error));
+}
+
+static void smartcard_channel_send_reader_add_response(
+    SmartCardChannel *smartcard_channel, PipeItem *item)
+{
+    ReaderAddResponseItem* rar_item = (ReaderAddResponseItem*)item;
+    VSCMsgReaderAddResponse rar;
+
+    smartcard_channel_send_message(&smartcard_channel->base, item, rar_item->reader_id,
+        VSC_ReaderAddResponse, (uint8_t*)&rar, sizeof(rar));
+}
+
+static void smartcard_channel_send_msg(
+    SmartCardChannel *smartcard_channel, PipeItem *item)
+{
+    MsgItem* msg_item = (MsgItem*)item;
+
+    smartcard_channel_send_data(&smartcard_channel->base, item, msg_item->vheader);
+}
+
+static void smartcard_channel_send_item(RedChannel *channel, PipeItem *item)
+{
+    SmartCardChannel *smartcard_channel = (SmartCardChannel *)channel;
+
+    red_channel_reset_send_data(channel);
+    switch (item->type) {
+    case PIPE_ITEM_TYPE_ERROR:
+        smartcard_channel_send_error(smartcard_channel, item);
+        break;
+    case PIPE_ITEM_TYPE_READER_ADD_RESPONSE:
+        smartcard_channel_send_reader_add_response(smartcard_channel, item);
+        break;
+    case PIPE_ITEM_TYPE_MSG:
+        smartcard_channel_send_msg(smartcard_channel, item);
+    }
+}
+
+
+static void smartcard_channel_release_pipe_item(RedChannel *channel, PipeItem *item, int item_pushed)
+{
+    free(item);
+    if (item->type == PIPE_ITEM_TYPE_MSG) {
+        free(((MsgItem*)item)->vheader);
+    }
+}
+
+static void smartcard_channel_disconnect(RedChannel *channel)
+{
+    smartcard_readers_detach_all((SmartCardChannel*)channel);
+    red_channel_destroy(channel);
+    g_smartcard_channel = NULL;
+}
+
+/* this is called from both device input and client input. since the device is
+ * a usb device, the context is still the main thread (kvm_main_loop, timers)
+ * so no mutex is required. */
+static void smartcard_channel_pipe_add(SmartCardChannel *channel, PipeItem *item)
+{
+    red_channel_pipe_add(&channel->base, item);
+}
+
+static void smartcard_push_error(SmartCardChannel* channel, reader_id_t reader_id, VSCErrorCode error)
+{
+    ErrorItem *error_item = spice_new0(ErrorItem, 1);
+
+    error_item->base.type = PIPE_ITEM_TYPE_ERROR;
+    error_item->reader_id = reader_id;
+    error_item->error = error;
+    smartcard_channel_pipe_add(channel, &error_item->base);
+}
+
+static void smartcard_push_reader_add_response(SmartCardChannel *channel, uint32_t reader_id)
+{
+    ReaderAddResponseItem *rar_item = spice_new0(ReaderAddResponseItem, 1);
+
+    rar_item->base.type = PIPE_ITEM_TYPE_READER_ADD_RESPONSE;
+    rar_item->reader_id = reader_id;
+    smartcard_channel_pipe_add(channel, &rar_item->base);
+}
+
+static void smartcard_push_vscmsg(SmartCardChannel *channel, VSCMsgHeader *vheader)
+{
+    MsgItem *msg_item = spice_new0(MsgItem, 1);
+
+    msg_item->base.type = PIPE_ITEM_TYPE_MSG;
+    msg_item->vheader = vheader;
+    smartcard_channel_pipe_add(channel, &msg_item->base);
+}
+
+void smartcard_on_message_from_device(SmartCardChannel *smartcard_channel,
+    VSCMsgHeader* vheader)
+{
+    smartcard_push_vscmsg(smartcard_channel, vheader);
+}
+
+static void smartcard_remove_reader(SmartCardChannel *smartcard_channel, reader_id_t reader_id)
+{
+    SpiceCharDeviceInstance *char_device = smartcard_readers_get(reader_id);
+    SmartCardDeviceState *state;
+
+    if (char_device == NULL) {
+        smartcard_push_error(smartcard_channel, reader_id,
+            VSC_GENERAL_ERROR);
+        return;
+    }
+
+    state = SPICE_CONTAINEROF(char_device->st, SmartCardDeviceState, base);
+    if (state->attached == FALSE) {
+        smartcard_push_error(smartcard_channel, reader_id,
+            VSC_GENERAL_ERROR);
+        return;
+    }
+    smartcard_char_device_detach(char_device, smartcard_channel);
+}
+
+static void smartcard_add_reader(SmartCardChannel *smartcard_channel, uint8_t *name)
+{
+    // TODO - save name somewhere
+    SpiceCharDeviceInstance *char_device =
+            smartcard_readers_get_unattached();
+    SmartCardDeviceState *state;
+
+    if (char_device != NULL) {
+        state = SPICE_CONTAINEROF(char_device->st, SmartCardDeviceState, base);
+        smartcard_char_device_attach(char_device, smartcard_channel);
+        smartcard_push_reader_add_response(smartcard_channel, state->reader_id);
+    } else {
+        smartcard_push_error(smartcard_channel, VSCARD_UNDEFINED_READER_ID,
+            VSC_CANNOT_ADD_MORE_READERS);
+    }
+}
+
+static void smartcard_channel_write_to_reader(
+    SmartCardChannel *smartcard_channel, VSCMsgHeader *vheader)
+{
+    SpiceCharDeviceInstance *sin;
+    SpiceCharDeviceInterface *sif;
+    uint32_t n;
+
+    ASSERT(vheader->reader_id >= 0 &&
+           vheader->reader_id <= g_smartcard_readers.num);
+    sin = g_smartcard_readers.sin[vheader->reader_id];
+    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base);
+    n = sif->write(sin, (uint8_t*)vheader,
+                   vheader->length + sizeof(VSCMsgHeader));
+    // TODO - add ring
+    ASSERT(n == vheader->length + sizeof(VSCMsgHeader));
+}
+
+static int smartcard_channel_handle_message(RedChannel *channel, SpiceDataHeader *header, uint8_t *msg)
+{
+    VSCMsgHeader* vheader = (VSCMsgHeader*)msg;
+    SmartCardChannel* smartcard_channel = (SmartCardChannel*)channel;
+
+    ASSERT(header->size == vheader->length + sizeof(VSCMsgHeader));
+    switch (vheader->type) {
+        case VSC_ReaderAdd:
+            smartcard_add_reader(smartcard_channel, msg + sizeof(VSCMsgHeader));
+            return TRUE;
+            break;
+        case VSC_ReaderRemove:
+            smartcard_remove_reader(smartcard_channel, vheader->reader_id);
+            return TRUE;
+            break;
+        case VSC_ReaderAddResponse:
+            /* We shouldn't get this - we only send it */
+            return TRUE;
+            break;
+        case VSC_Init:
+        case VSC_Error:
+        case VSC_ATR:
+        case VSC_CardRemove:
+        case VSC_APDU:
+            break; // passed on to device
+        default:
+            printf("ERROR: unexpected message on smartcard channel\n");
+            return TRUE;
+    }
+
+    if (vheader->reader_id >= g_smartcard_readers.num) {
+        red_printf("ERROR: received message for non existent reader: %d, %d, %d", vheader->reader_id,
+            vheader->type, vheader->length);
+        return FALSE;
+    }
+    smartcard_channel_write_to_reader(smartcard_channel, vheader);
+    return TRUE;
+}
+
+static void smartcard_link(Channel *channel, RedsStreamContext *peer,
+                        int migration, int num_common_caps,
+                        uint32_t *common_caps, int num_caps,
+                        uint32_t *caps)
+{
+    if (g_smartcard_channel) {
+        red_channel_destroy(&g_smartcard_channel->base);
+    }
+    g_smartcard_channel =
+        (SmartCardChannel *)red_channel_create(sizeof(*g_smartcard_channel),
+                                        peer, core,
+                                        migration, FALSE /* handle_acks */,
+                                        smartcard_channel_config_socket,
+                                        smartcard_channel_disconnect,
+                                        smartcard_channel_handle_message,
+                                        smartcard_channel_alloc_msg_rcv_buf,
+                                        smartcard_channel_release_msg_rcv_buf,
+                                        smartcard_channel_send_item,
+                                        smartcard_channel_release_pipe_item);
+    if (!g_smartcard_channel) {
+        return;
+    }
+    red_channel_init_outgoing_messages_window(&g_smartcard_channel->base);
+}
+
+static void smartcard_shutdown(Channel *channel)
+{
+}
+
+static void smartcard_migrate(Channel *channel)
+{
+}
+
+void smartcard_channel_init()
+{
+    Channel *channel;
+
+    channel = spice_new0(Channel, 1);
+    channel->type = SPICE_CHANNEL_SMARTCARD;
+    channel->link = smartcard_link;
+    channel->shutdown = smartcard_shutdown;
+    channel->migrate = smartcard_migrate;
+    reds_register_channel(channel);
+}
+
diff --git a/server/smartcard.h b/server/smartcard.h
new file mode 100644
index 0000000..790eb87
--- /dev/null
+++ b/server/smartcard.h
@@ -0,0 +1,18 @@
+#ifndef __SMART_CARD_H__
+#define __SMART_CARD_H__
+
+#include "server/spice-experimental.h"
+
+// Maximal length of APDU
+#define APDUBufSize 270
+
+/** connect to smartcard interface, used by smartcard channel
+ * returns -1 if failed, 0 if successfull
+ */
+int smartcard_device_connect(SpiceCharDeviceInstance *char_device);
+void smartcard_device_disconnect(SpiceCharDeviceInstance *char_device);
+
+void smartcard_channel_init();
+
+#endif // __SMART_CARD_H__
+
commit 757686384fc47746891b6280b9f27404420c8dea
Author: Alon Levy <alevy at redhat.com>
Date:   Sat Oct 23 18:29:45 2010 +0200

    smartcard: client: add keyboard shortcuts for remove/insert virtual card

diff --git a/client/application.cpp b/client/application.cpp
index ae19785..a484bbc 100644
--- a/client/application.cpp
+++ b/client/application.cpp
@@ -339,6 +339,10 @@ enum AppCommands {
 #ifdef USE_GUI
     APP_CMD_SHOW_GUI,
 #endif // USE_GUI
+#ifdef USE_SMARTCARD
+    APP_CMD_SMARTCARD_INSERT,
+    APP_CMD_SMARTCARD_REMOVE,
+#endif
     APP_CMD_EXTERNAL_BEGIN = 0x400,
     APP_CMD_EXTERNAL_END = 0x800,
 };
@@ -391,6 +395,10 @@ Application::Application()
 #ifdef USE_GUI
     _commands_map["show-gui"] = APP_CMD_SHOW_GUI;
 #endif // USE_GUI
+#ifdef USE_SMARTCARD
+    _commands_map["smartcard-insert"] = APP_CMD_SMARTCARD_INSERT;
+    _commands_map["smartcard-remove"] = APP_CMD_SMARTCARD_REMOVE;
+#endif
 
     _canvas_types.resize(1);
 #ifdef WIN32
@@ -413,6 +421,10 @@ Application::Application()
 #ifdef USE_GUI
                                                           ",show-gui=shift+f7"
 #endif // USE_GUI
+#ifdef USE_SMARTCARD
+                                                          ",smartcard-insert=shift+f8"
+                                                          ",smartcard-remove=shift+f9"
+#endif
                                                           , _commands_map));
     _hot_keys = parser->get();
 
@@ -1007,6 +1019,14 @@ void Application::do_command(int command)
         show_gui();
         break;
 #endif // USE_GUI
+#ifdef USE_SMARTCARD
+    case APP_CMD_SMARTCARD_INSERT:
+        virtual_card_insert();
+        break;
+    case APP_CMD_SMARTCARD_REMOVE:
+        virtual_card_remove();
+        break;
+#endif
     default:
         AppMenuItemMap::iterator iter = _app_menu_items.find(command);
         ASSERT(iter != _app_menu_items.end());
diff --git a/client/smartcard_channel.cpp b/client/smartcard_channel.cpp
index d585c9a..994671f 100644
--- a/client/smartcard_channel.cpp
+++ b/client/smartcard_channel.cpp
@@ -228,6 +228,38 @@ void SmartCardChannel::cac_card_events_thread_main()
     }
 }
 
+void virtual_card_insert()
+{
+    if (g_smartcard_channel == NULL) {
+        return;
+    }
+    g_smartcard_channel->virtual_card_insert();
+}
+
+void SmartCardChannel::virtual_card_insert()
+{
+    if (_readers_by_id.size() == 0) {
+        return;
+    }
+    vcard_emul_force_card_insert(_readers_by_id.begin()->second->vreader);
+}
+
+void virtual_card_remove()
+{
+    if (g_smartcard_channel == NULL) {
+        return;
+    }
+    g_smartcard_channel->virtual_card_remove();
+}
+
+void SmartCardChannel::virtual_card_remove()
+{
+    if (_readers_by_id.size() == 0) {
+        return;
+    }
+    vcard_emul_force_card_remove(_readers_by_id.begin()->second->vreader);
+}
+
 #define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb"
 #define CERTIFICATES_ARGS_TEMPLATE "db=\"%s\" use_hw=no soft=(,Virtual Card,CAC,,%s,%s,%s)"
 
diff --git a/client/smartcard_channel.h b/client/smartcard_channel.h
index ee0d0d0..60c6db5 100644
--- a/client/smartcard_channel.h
+++ b/client/smartcard_channel.h
@@ -92,6 +92,8 @@ public:
     SmartCardChannel(RedClient& client, uint32_t id);
     void handle_smartcard_data(RedPeer::InMessage* message);
 
+    void virtual_card_remove();
+    void virtual_card_insert();
     static ChannelFactory& Factory();
 protected:
     virtual void on_connect();
commit a2afcde061a488713119e03774915ea752757824
Author: Alon Levy <alevy at redhat.com>
Date:   Wed Sep 15 15:54:43 2010 +0200

    smartcard: client side (not enabled yet)

diff --git a/client/application.cpp b/client/application.cpp
index c380373..ae19785 100644
--- a/client/application.cpp
+++ b/client/application.cpp
@@ -47,6 +47,10 @@
 #include <stdio.h>
 #include <time.h>
 
+#ifdef USE_SMARTCARD
+#include <smartcard_channel.h>
+#endif
+
 #define STICKY_KEY_PIXMAP ALT_IMAGE_RES_ID
 #define STICKY_KEY_TIMEOUT 750
 
@@ -364,6 +368,9 @@ Application::Application()
 #endif // USE_GUI
     , _during_host_switch(false)
     , _state (DISCONNECTED)
+#ifdef USE_SMARTCARD
+    , _smartcard_options(new SmartcardOptions())
+#endif
 {
     DBG(0, "");
     Platform::set_process_loop(*this);
@@ -447,6 +454,9 @@ Application::~Application()
 
     _main_screen->unref();
     destroy_monitors();
+#ifdef USE_SMARTCARD
+    delete _smartcard_options;
+#endif
 }
 
 void Application::init_menu()
@@ -2148,6 +2158,12 @@ void Application::register_channels()
         _client.register_channel_factory(TunnelChannel::Factory());
     }
 #endif
+#ifdef USE_SMARTCARD
+    if (_enabled_channels[SPICE_CHANNEL_SMARTCARD] && _smartcard_options->enable) {
+        smartcard_init(_smartcard_options); // throws Exception
+        _client.register_channel_factory(SmartCardChannel::Factory());
+    }
+#endif
 }
 
 bool Application::process_cmd_line(int argc, char** argv)
@@ -2177,6 +2193,12 @@ bool Application::process_cmd_line(int argc, char** argv)
         SPICE_OPT_DISPLAY_COLOR_DEPTH,
         SPICE_OPT_DISABLE_DISPLAY_EFFECTS,
         SPICE_OPT_CONTROLLER,
+#ifdef USE_SMARTCARD
+        SPICE_OPT_SMARTCARD,
+        SPICE_OPT_NOSMARTCARD,
+        SPICE_OPT_SMARTCARD_CERT,
+        SPICE_OPT_SMARTCARD_DB,
+#endif
     };
 
 #ifdef USE_GUI
@@ -2235,6 +2257,15 @@ bool Application::process_cmd_line(int argc, char** argv)
 
     parser.add(SPICE_OPT_CONTROLLER, "controller", "enable external controller");
 
+#ifdef USE_SMARTCARD
+    parser.add(SPICE_OPT_SMARTCARD, "smartcard", "enable smartcard channel");
+    parser.add(SPICE_OPT_NOSMARTCARD, "nosmartcard", "disable smartcard channel");
+    parser.add(SPICE_OPT_SMARTCARD_CERT, "smartcard-cert", "Use virtual reader+card with given cert(s)",
+        "smartcard-cert", true);
+    parser.set_multi(SPICE_OPT_SMARTCARD_CERT, ',');
+    parser.add(SPICE_OPT_SMARTCARD_DB, "smartcard-db", "Use given db for smartcard certs");
+#endif
+
     for (int i = SPICE_CHANNEL_MAIN; i < SPICE_END_CHANNEL; i++) {
         _peer_con_opt[i] = RedPeer::ConnectionOptions::CON_OP_INVALID;
     }
@@ -2340,6 +2371,20 @@ bool Application::process_cmd_line(int argc, char** argv)
             }
             _enable_controller = true;
             return true;
+#ifdef USE_SMARTCARD
+        case SPICE_OPT_SMARTCARD:
+            _smartcard_options->enable= true;
+            break;
+        case SPICE_OPT_NOSMARTCARD:
+            _smartcard_options->enable= false; // default
+            break;
+        case SPICE_OPT_SMARTCARD_CERT:
+            do {
+                _smartcard_options->certs.insert(
+                    _smartcard_options->certs.end(), std::string(val));
+            } while ((val=parser.next_argument()));
+            break;
+#endif
         case CmdLineParser::OPTION_HELP:
             parser.show_help();
             return false;
diff --git a/client/application.h b/client/application.h
index 5a5a488..f9bbd53 100644
--- a/client/application.h
+++ b/client/application.h
@@ -29,6 +29,9 @@
 #include "foreign_menu.h"
 #include "controller.h"
 
+#ifdef USE_SMARTCARD
+struct SmartcardOptions;
+#endif
 class RedScreen;
 class Application;
 class ScreenLayer;
@@ -268,6 +271,12 @@ public:
     void message_box_test();
 #endif
 
+#ifdef USE_SMARTCARD
+    const SmartcardOptions* get_smartcard_options() const {
+        return _smartcard_options;
+    }
+#endif
+
     static int main(int argc, char** argv, const char* version_str);
 
 private:
@@ -386,6 +395,9 @@ private:
     bool _during_host_switch;
 
     State _state;
+#ifdef USE_SMARTCARD
+    SmartcardOptions* _smartcard_options;
+#endif
 };
 
 #endif
diff --git a/client/smartcard_channel.cpp b/client/smartcard_channel.cpp
new file mode 100644
index 0000000..d585c9a
--- /dev/null
+++ b/client/smartcard_channel.cpp
@@ -0,0 +1,449 @@
+#include <spice/enums.h>
+
+#include "client/red_client.h"
+#include "mutex.h"
+
+extern "C" {
+#include "vscard_common.h"
+#include "vreader.h"
+#include "vcard_emul.h"
+#include "vevent.h"
+}
+
+#include "smartcard_channel.h"
+
+#define APDUBufSize 270
+
+#define MAX_ATR_LEN 40
+
+//#define DEBUG_SMARTCARD
+
+#ifdef DEBUG_SMARTCARD
+void PrintByteArray(uint8_t *arrBytes, unsigned int nSize)
+{
+    int i;
+
+    for (i=0; i < nSize; i++) {
+        DBG(0, "%X ", arrBytes[i]);
+    }
+    DBG(0, "\n");
+}
+#define DEBUG_PRINT_BYTE_ARRAY(arrBytes, nSize) PrintByteArray(arrBytes, nSize)
+#else
+#define DEBUG_PRINT_BYTE_ARRAY(arrBytes, nSize)
+#endif
+
+SmartCardChannel* g_smartcard_channel = NULL; // used for insert/remove of virtual card. Can be
+                                // changed if we let Application store the SmartCard instance.
+
+class SmartCardHandler: public MessageHandlerImp<SmartCardChannel, SPICE_CHANNEL_SMARTCARD> {
+public:
+    SmartCardHandler(SmartCardChannel& channel)
+        : MessageHandlerImp<SmartCardChannel, SPICE_CHANNEL_SMARTCARD>(channel) {}
+};
+
+
+SmartCardChannel::SmartCardChannel(RedClient& client, uint32_t id)
+    : RedChannel(client, SPICE_CHANNEL_SMARTCARD, id, new SmartCardHandler(*this))
+{
+    SmartCardHandler* handler = static_cast<SmartCardHandler*>(get_message_handler());
+
+    g_smartcard_channel = this;
+    handler->set_handler(SPICE_MSG_SMARTCARD_DATA,
+                        &SmartCardChannel::handle_smartcard_data);
+}
+
+ReaderData* SmartCardChannel::reader_data_from_vreader(VReader* vreader)
+{
+    if (vreader == NULL) {
+        assert(_readers_by_vreader.size() > 0);
+        return _readers_by_vreader.begin()->second;
+    }
+    if (_readers_by_vreader.count(vreader) > 0) {
+        return _readers_by_vreader.find(vreader)->second;
+    }
+    assert(_unallocated_readers_by_vreader.count(vreader) > 0);
+    return _unallocated_readers_by_vreader.find(vreader)->second;
+}
+
+ReaderData* SmartCardChannel::reader_data_from_reader_id(reader_id_t reader_id)
+{
+    if (_readers_by_id.count(reader_id) > 0) {
+        return _readers_by_id.find(reader_id)->second;
+    }
+    return NULL;
+}
+
+/** On VEVENT_READER_INSERT we call this, send a VSC_ReaderAdd, and wait for a VSC_ReaderAddResponse
+ */
+void SmartCardChannel::add_unallocated_reader(VReader* vreader, const char* name)
+{
+    ReaderData* data = new ReaderData();
+
+    data->vreader = vreader;
+    data->reader_id = VSCARD_UNDEFINED_READER_ID;
+    data->name = spice_strdup(name);
+    _unallocated_readers_by_vreader.insert(std::pair<VReader*, ReaderData*>(vreader, data));
+}
+
+/** called upon the VSC_ReaderAddResponse
+ */
+ReaderData* SmartCardChannel::add_reader(reader_id_t reader_id)
+{
+    ReaderData* data;
+    size_t unallocated = _unallocated_readers_by_vreader.size();
+
+    assert(unallocated > 0);
+    data = _unallocated_readers_by_vreader.begin()->second;
+    data->reader_id = reader_id;
+    _readers_by_vreader.insert(
+        std::pair<VReader*, ReaderData*>(data->vreader, data));
+    assert(_readers_by_vreader.count(data->vreader) == 1);
+    _readers_by_id.insert(std::pair<reader_id_t, ReaderData*>(reader_id, data));
+    assert(_readers_by_id.count(reader_id) == 1);
+    _unallocated_readers_by_vreader.erase(_unallocated_readers_by_vreader.begin());
+    assert(_unallocated_readers_by_vreader.size() == unallocated - 1);
+    assert(_unallocated_readers_by_vreader.count(data->vreader) == 0);
+    return data;
+}
+
+void* SmartCardChannel::cac_card_events_thread_entry(void* data)
+{
+    static_cast<SmartCardChannel*>(data)->cac_card_events_thread_main();
+    return NULL;
+}
+
+VEventEvent::VEventEvent(SmartCardChannel* smartcard_channel, VEvent* vevent)
+    : _smartcard_channel(smartcard_channel)
+    , _vreader(vevent->reader)
+    , _vevent(vevent)
+{
+}
+
+VEventEvent::~VEventEvent()
+{
+    vevent_delete(_vevent);
+}
+
+void ReaderAddEvent::response(AbstractProcessLoop& events_loop)
+{
+    static int num = 0;
+    char name[20];
+
+    sprintf(name, "test%4d", num++);
+    _smartcard_channel->add_unallocated_reader(_vreader, name);
+    _smartcard_channel->send_reader_added(name);
+}
+
+void ReaderRemoveEvent::response(AbstractProcessLoop& events_loop)
+{
+    ReaderData* data;
+
+    data = _smartcard_channel->reader_data_from_vreader(_vreader);
+    _smartcard_channel->send_reader_removed(data->reader_id);
+    _smartcard_channel->remove_reader(data);
+}
+
+void CardInsertEvent::response(AbstractProcessLoop& events_loop)
+{
+    ReaderData* data = _smartcard_channel->reader_data_from_vreader(
+                                                    _vreader);
+
+    if (data->reader_id == VSCARD_UNDEFINED_READER_ID) {
+        data->card_insert_pending = true;
+    } else {
+        _smartcard_channel->send_atr(_vreader);
+    }
+}
+
+void CardRemoveEvent::response(AbstractProcessLoop& events_loop)
+{
+    ReaderData* data = _smartcard_channel->reader_data_from_vreader(
+                                                    _vreader);
+
+    ASSERT(data->reader_id != VSCARD_UNDEFINED_READER_ID);
+    _smartcard_channel->send_message(data->reader_id, VSC_CardRemove, NULL, 0);
+}
+
+void SmartCardChannel::remove_reader(ReaderData* data)
+{
+    // TODO - untested code (caccard doesn't produce these events yet)
+    if (_readers_by_vreader.count(data->vreader) > 0) {
+        _readers_by_vreader.erase(_readers_by_vreader.find(data->vreader));
+        _readers_by_id.erase(_readers_by_id.find(data->reader_id));
+    } else {
+        _unallocated_readers_by_vreader.erase(
+            _unallocated_readers_by_vreader.find(data->vreader));
+    }
+    free(data->name);
+    delete data;
+}
+
+void SmartCardChannel::cac_card_events_thread_main()
+{
+    VEvent *vevent = NULL;
+    bool cont = true;
+
+    while (cont) {
+        vevent = vevent_wait_next_vevent();
+        if (vevent == NULL) {
+            break;
+        }
+        switch (vevent->type) {
+        case VEVENT_READER_INSERT:
+            LOG_INFO("VEVENT_READER_INSERT");
+            {
+                AutoRef<ReaderAddEvent> event(new ReaderAddEvent(this, vevent));
+                get_client().push_event(*event);
+            }
+            break;
+        case VEVENT_READER_REMOVE:
+            LOG_INFO("VEVENT_READER_REMOVE");
+            {
+                AutoRef<ReaderRemoveEvent> event(new ReaderRemoveEvent(this, vevent));
+                get_client().push_event(*event);
+            }
+            break;
+        case VEVENT_CARD_INSERT:
+            LOG_INFO("VEVENT_CARD_INSERT");
+            {
+                AutoRef<CardInsertEvent> event(new CardInsertEvent(this, vevent));
+                get_client().push_event(*event);
+            }
+            break;
+        case VEVENT_CARD_REMOVE:
+            LOG_INFO("VEVENT_CARD_REMOVE");
+            {
+                AutoRef<CardRemoveEvent> event(new CardRemoveEvent(this, vevent));
+                get_client().push_event(*event);
+            }
+            break;
+        case VEVENT_LAST:
+            cont = false;
+        default:
+           /* anything except VEVENT_LAST and default
+            * gets to VEventEvent which does vevent_delete in VEventEvent~ */
+            vevent_delete(vevent);
+        }
+    }
+}
+
+#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb"
+#define CERTIFICATES_ARGS_TEMPLATE "db=\"%s\" use_hw=no soft=(,Virtual Card,CAC,,%s,%s,%s)"
+
+SmartcardOptions::SmartcardOptions() :
+dbname(CERTIFICATES_DEFAULT_DB),
+enable(false)
+{
+}
+
+static VCardEmulError init_vcard_local_certs(const char* dbname, const char* cert1,
+    const char* cert2, const char* cert3)
+{
+    char emul_args[200];
+    VCardEmulOptions *options = NULL;
+
+    snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE,
+        dbname, cert1, cert2, cert3);
+    options = vcard_emul_options(emul_args);
+    if (options == NULL) {
+        LOG_WARN("not using certificates due to initialization error");
+    }
+    return vcard_emul_init(options);
+}
+
+static bool g_vcard_inited = false;
+
+void smartcard_init(const SmartcardOptions* options)
+{
+    if (g_vcard_inited) {
+        return;
+    }
+    if (options->certs.size() == 3) {
+        const char* dbname = options->dbname.c_str();
+        if (init_vcard_local_certs(dbname, options->certs[0].c_str(),
+            options->certs[1].c_str(), options->certs[2].c_str()) != VCARD_EMUL_OK) {
+            throw Exception("smartcard: emulated card initialization failed (check certs/db)");
+        }
+    } else {
+        if (options->certs.size() > 0) {
+            LOG_WARN("Ignoring smartcard certificates - must be exactly three for virtual card emulation");
+        }
+        if (vcard_emul_init(NULL) != VCARD_EMUL_OK) {
+            throw Exception("smartcard: vcard initialization failed (check hardware/drivers)");
+        }
+    }
+    g_vcard_inited = true;
+}
+
+void SmartCardChannel::on_connect()
+{
+    _event_thread = new Thread(SmartCardChannel::cac_card_events_thread_entry, this);
+}
+
+void SmartCardChannel::on_disconnect()
+{
+    VEvent *stop_event;
+
+    if (_event_thread == NULL) {
+        return;
+    }
+    stop_event = vevent_new(VEVENT_LAST, NULL, NULL);
+    vevent_queue_vevent(stop_event);
+    _event_thread->join();
+    delete _event_thread;
+    _event_thread = NULL;
+}
+
+
+void SmartCardChannel::send_reader_removed(reader_id_t reader_id)
+{
+    send_message(reader_id, VSC_ReaderRemove, NULL, 0);
+}
+
+void SmartCardChannel::send_reader_added(const char* reader_name)
+{
+    send_message(VSCARD_UNDEFINED_READER_ID,
+        VSC_ReaderAdd, (uint8_t*)reader_name, strlen(reader_name));
+}
+
+void SmartCardChannel::send_atr(VReader* vreader)
+{
+    unsigned char atr[ MAX_ATR_LEN];
+    int atr_len = MAX_ATR_LEN;
+    reader_id_t reader_id = reader_data_from_vreader(vreader)->reader_id;
+
+    assert(reader_id != VSCARD_UNDEFINED_READER_ID);
+    vreader_power_on(vreader, atr, &atr_len);
+    DBG(0, "ATR: ");
+    DEBUG_PRINT_BYTE_ARRAY(atr, atr_len);
+    send_message(reader_id, VSC_ATR, (uint8_t*)atr, atr_len);
+}
+
+void SmartCardChannel::send_message(uint32_t reader_id, VSCMsgType type, uint8_t* data, uint32_t len)
+{
+    VSCMsgHeader mhHeader;
+    Message* msg = new Message(SPICE_MSGC_SMARTCARD_DATA);
+    SpiceMarshaller* m = msg->marshaller();
+
+    mhHeader.type = type;
+    mhHeader.length = len;
+    mhHeader.reader_id = reader_id;
+    spice_marshaller_add(m, (uint8_t*)&mhHeader, sizeof(mhHeader));
+    spice_marshaller_add(m, data, len);
+    post_message(msg);
+}
+
+VSCMessageEvent::VSCMessageEvent(SmartCardChannel* smartcard_channel,
+    VSCMsgHeader* vheader)
+    : _smartcard_channel(smartcard_channel)
+    , _vheader(NULL)
+{
+    _vheader = (VSCMsgHeader*)spice_memdup(vheader,
+                        sizeof(VSCMsgHeader) + vheader->length);
+    ASSERT(_vheader);
+}
+
+VSCMessageEvent::~VSCMessageEvent()
+{
+    free(_vheader);
+}
+
+void VSCMessageEvent::response(AbstractProcessLoop& loop)
+{
+    static int recv_count = 0;
+    int dwSendLength;
+    int dwRecvLength;
+    uint8_t* pbSendBuffer = _vheader->data;
+    uint8_t pbRecvBuffer[APDUBufSize+sizeof(uint32_t)];
+    VReaderStatus reader_status;
+    uint32_t rv;
+    ReaderData* data;
+
+    switch (_vheader->type) {
+        case (VSC_ReaderAddResponse):
+            data = _smartcard_channel->add_reader(_vheader->reader_id);
+            if (data->card_insert_pending) {
+                data->card_insert_pending = false;
+                _smartcard_channel->send_atr(data->vreader);
+            }
+            return;
+            break;
+        case VSC_APDU:
+            break;
+        case VSC_Error:
+            {
+                VSCMsgError *error = (VSCMsgError*)_vheader->data;
+                LOG_WARN("VSC Error: reader %d, code %d",
+                    _vheader->reader_id, error->code);
+            }
+            return;
+        default:
+            LOG_WARN("unhandled VSC %d of length %d, reader %d",
+                _vheader->type, _vheader->length, _vheader->reader_id);
+            return;
+    }
+
+    /* Transmit recieved APDU */
+    dwSendLength = _vheader->length;
+    dwRecvLength = sizeof(pbRecvBuffer);
+
+    DBG(0, " %3d: recv APDU: ", recv_count++);
+    DEBUG_PRINT_BYTE_ARRAY(pbSendBuffer, _vheader->length);
+
+    ReaderData* reader_data = _smartcard_channel->reader_data_from_reader_id(
+                                                        _vheader->reader_id);
+    if (reader_data == NULL) {
+        LOG_WARN("got message for non existant reader");
+        return;
+    }
+
+    VReader* vreader = reader_data->vreader;
+
+    reader_status = vreader_xfr_bytes(vreader,
+        pbSendBuffer, dwSendLength,
+        pbRecvBuffer, &dwRecvLength);
+    if (reader_status == VREADER_OK) {
+        DBG(0, " sent APDU: ");
+        DEBUG_PRINT_BYTE_ARRAY(pbRecvBuffer, dwRecvLength);
+        _smartcard_channel->send_message (
+            _vheader->reader_id,
+            VSC_APDU,
+            pbRecvBuffer,
+            dwRecvLength
+        );
+    } else {
+        rv = reader_status; /* warning: not meaningful */
+        _smartcard_channel->send_message (
+            _vheader->reader_id,
+            VSC_Error,
+            (uint8_t*)&rv,
+            sizeof (uint32_t)
+        );
+    }
+}
+
+void SmartCardChannel::handle_smartcard_data(RedPeer::InMessage* message)
+{
+    VSCMsgHeader* mhHeader = (VSCMsgHeader*)(message->data());
+
+    AutoRef<VSCMessageEvent> event(new VSCMessageEvent(this, mhHeader));
+    get_client().push_event(*event);
+}
+
+class SmartCardFactory: public ChannelFactory {
+public:
+    SmartCardFactory() : ChannelFactory(SPICE_CHANNEL_SMARTCARD) {}
+    virtual RedChannel* construct(RedClient& client, uint32_t id)
+    {
+        return new SmartCardChannel(client, id);
+    }
+};
+
+static SmartCardFactory factory;
+
+ChannelFactory& SmartCardChannel::Factory()
+{
+    return factory;
+}
+
diff --git a/client/smartcard_channel.h b/client/smartcard_channel.h
new file mode 100644
index 0000000..ee0d0d0
--- /dev/null
+++ b/client/smartcard_channel.h
@@ -0,0 +1,133 @@
+#ifndef __SMART_CARD_H__
+#define __SMART_CARD_H__
+
+#include <map>
+
+#include <vreadert.h>
+#include <vscard_common.h>
+#include <eventt.h>
+
+#include "red_channel.h"
+#include "red_peer.h"
+
+class Application;
+
+struct SmartcardOptions {
+    std::vector<std::string> certs;
+    std::string dbname;
+    bool enable;
+    SmartcardOptions();
+};
+
+void smartcard_init(const SmartcardOptions* options);
+
+struct ReaderData {
+    ReaderData() :
+        vreader(NULL),
+        reader_id(VSCARD_UNDEFINED_READER_ID),
+        name(NULL),
+        card_insert_pending(false)
+    {}
+    VReader *vreader;
+    reader_id_t reader_id;
+    char* name;
+    bool card_insert_pending;
+};
+
+void virtual_card_remove();
+void virtual_card_insert();
+
+class SmartCardChannel;
+
+class VEventEvent : public Event {
+public:
+    VEventEvent(SmartCardChannel* smartcard_channel, VEvent* vevent);
+    ~VEventEvent();
+    SmartCardChannel* _smartcard_channel;
+    VReader* _vreader;
+    VEvent* _vevent;
+};
+
+class ReaderAddEvent: public VEventEvent {
+public:
+    ReaderAddEvent(SmartCardChannel* smartcard_channel, VEvent* vevent)
+        : VEventEvent(smartcard_channel, vevent) {}
+    virtual void response(AbstractProcessLoop& events_loop);
+};
+
+class ReaderRemoveEvent: public VEventEvent {
+public:
+    ReaderRemoveEvent(SmartCardChannel* smartcard_channel, VEvent* vevent)
+        : VEventEvent(smartcard_channel, vevent) {}
+    virtual void response(AbstractProcessLoop& events_loop);
+};
+
+class CardInsertEvent: public VEventEvent {
+public:
+    CardInsertEvent(SmartCardChannel* smartcard_channel, VEvent* vevent)
+        : VEventEvent(smartcard_channel, vevent) {}
+    virtual void response(AbstractProcessLoop& events_loop);
+};
+
+class CardRemoveEvent: public VEventEvent {
+public:
+    CardRemoveEvent(SmartCardChannel* smartcard_channel, VEvent* vevent)
+        : VEventEvent(smartcard_channel, vevent) {}
+    virtual void response(AbstractProcessLoop& events_loop);
+};
+
+class VSCMessageEvent: public Event {
+public:
+    VSCMessageEvent(SmartCardChannel* smartcard_channel,
+        VSCMsgHeader* vheader);
+    ~VSCMessageEvent();
+    SmartCardChannel* _smartcard_channel;
+    VSCMsgHeader* _vheader;
+    virtual void response(AbstractProcessLoop& events_loop);
+};
+
+class SmartCardChannel : public RedChannel {
+
+public:
+    SmartCardChannel(RedClient& client, uint32_t id);
+    void handle_smartcard_data(RedPeer::InMessage* message);
+
+    static ChannelFactory& Factory();
+protected:
+    virtual void on_connect();
+    virtual void on_disconnect();
+
+private:
+    static void* cac_card_events_thread_entry(void* data);
+    void cac_card_events_thread_main();
+    void send_message(reader_id_t reader_id, VSCMsgType type, uint8_t* data, uint32_t len);
+
+    Thread* _event_thread;
+
+    Application* _app;
+
+    VReaderList *_reader_list;
+    typedef std::map<reader_id_t, ReaderData*> readers_by_id_t;
+    readers_by_id_t _readers_by_id;
+    typedef std::map<VReader*, ReaderData*> readers_by_vreader_t;
+    readers_by_vreader_t _readers_by_vreader;
+    readers_by_vreader_t _unallocated_readers_by_vreader;
+
+    ReaderData* reader_data_from_vreader(VReader* vreader);
+    ReaderData* reader_data_from_reader_id(reader_id_t reader_id);
+    void add_unallocated_reader(VReader* vreader, const char* name);
+    ReaderData* add_reader(reader_id_t reader_id);
+    void remove_reader(ReaderData* data);
+    void send_reader_added(const char* reader_name);
+    void send_reader_removed(reader_id_t reader_id);
+    void send_atr(VReader* vreader);
+
+    friend class ReaderAddEvent;
+    friend class ReaderRemoveEvent;
+    friend class CardInsertEvent;
+    friend class CardRemoveEvent;
+    friend class VSCMessageEvent;
+};
+
+#endif // __SMART_CARD_H__
+
commit 611e542c6c2b6f9aa0dea90f4f5949d1e52fd543
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Sep 13 20:02:42 2010 +0200

    smartcard: add to spice.proto

diff --git a/spice.proto b/spice.proto
index 3c0911d..4eeb159 100644
--- a/spice.proto
+++ b/spice.proto
@@ -1084,6 +1084,13 @@ channel TunnelChannel : BaseChannel {
     } @ctype(SpiceMsgcTunnelSocketTokens) socket_token;
 };
 
+channel SmartcardChannel : BaseChannel {
+server:
+    Data data = 101;
+client:
+    Data data = 101;
+};
+
 protocol Spice {
     MainChannel main = 1;
     DisplayChannel display;
@@ -1092,4 +1099,5 @@ protocol Spice {
     PlaybackChannel playback;
     RecordChannel record;
     TunnelChannel tunnel;
+    SmartcardChannel smartcard;
 };
commit 7461ee17cf4e59c30d3c7d83c1bb7b029c6cfb9b
Author: Alon Levy <alevy at redhat.com>
Date:   Wed Sep 15 10:06:58 2010 +0200

    server: add spice_server_char_device_remove_interface

diff --git a/server/reds.c b/server/reds.c
index d71ecb1..3edf474 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -3463,6 +3463,19 @@ static int spice_server_char_device_add_interface(SpiceServer *s,
     return 0;
 }
 
+static void spice_server_char_device_remove_interface(SpiceBaseInstance *sin)
+{
+    SpiceCharDeviceInstance* char_device =
+            SPICE_CONTAINEROF(sin, SpiceCharDeviceInstance, base);
+
+    red_printf("remove CHAR_DEVICE %s", char_device->subtype);
+    if (strcmp(char_device->subtype, SUBTYPE_VDAGENT) == 0) {
+        if (vdagent) {
+            reds_agent_remove();
+        }
+    }
+}
+
 __visible__ int spice_server_add_interface(SpiceServer *s,
                                            SpiceBaseInstance *sin)
 {
@@ -3605,11 +3618,7 @@ __visible__ int spice_server_remove_interface(SpiceBaseInstance *sin)
         snd_detach_record(SPICE_CONTAINEROF(sin, SpiceRecordInstance, base));
 
     } else if (strcmp(interface->type, SPICE_INTERFACE_CHAR_DEVICE) == 0) {
-        red_printf("remove SPICE_INTERFACE_CHAR_DEVICE");
-        if (vdagent && sin == &vdagent->base) {
-            reds_agent_remove();
-        }
-
+        spice_server_char_device_remove_interface(sin);
     } else {
         red_error("VD_INTERFACE_REMOVING unsupported");
         return -1;
commit 5220e0a7062a5fa5dadd386b9d3a0794a4844b17
Author: Alon Levy <alevy at redhat.com>
Date:   Wed Sep 15 10:06:18 2010 +0200

    server: print subtype when adding CHAR_DEVICE interfaces

diff --git a/server/reds.c b/server/reds.c
index eee8f35..d71ecb1 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -3451,6 +3451,7 @@ static int spice_server_char_device_add_interface(SpiceServer *s,
     SpiceCharDeviceInterface* sif;
 
     sif = SPICE_CONTAINEROF(char_device->base.sif, SpiceCharDeviceInterface, base);
+    red_printf("CHAR_DEVICE %s", char_device->subtype);
     if (strcmp(char_device->subtype, SUBTYPE_VDAGENT) == 0) {
         if (vdagent) {
             red_printf("vdagent already attached");
@@ -3552,7 +3553,6 @@ __visible__ int spice_server_add_interface(SpiceServer *s,
         snd_attach_record(SPICE_CONTAINEROF(sin, SpiceRecordInstance, base));
 
     } else if (strcmp(interface->type, SPICE_INTERFACE_CHAR_DEVICE) == 0) {
-        red_printf("SPICE_INTERFACE_CHAR_DEVICE");
         if (interface->major_version != SPICE_INTERFACE_CHAR_DEVICE_MAJOR ||
             interface->minor_version < SPICE_INTERFACE_CHAR_DEVICE_MINOR) {
             red_printf("unsupported char device interface");
commit 0ac9ca51f4cecce8d1146c23c3032f3dcda6954b
Author: Alon Levy <alevy at redhat.com>
Date:   Wed Sep 15 10:05:32 2010 +0200

    server: add static to spice_server_char_device_add_interface

diff --git a/server/reds.c b/server/reds.c
index aff8b6f..eee8f35 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -3443,7 +3443,7 @@ __visible__ const char** spice_server_char_device_recognized_subtypes()
     return spice_server_char_device_recognized_subtypes_list;
 }
 
-int spice_server_char_device_add_interface(SpiceServer *s,
+static int spice_server_char_device_add_interface(SpiceServer *s,
                                            SpiceBaseInstance *sin)
 {
     SpiceCharDeviceInstance* char_device =
commit beca39aa87e5644c6d8210860037cb2220256b7a
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Oct 4 22:02:41 2010 +0200

    spice codegen: fix copy-o, no such variable value

diff --git a/python_modules/ptypes.py b/python_modules/ptypes.py
index 0ae57ec..9c4b7de 100644
--- a/python_modules/ptypes.py
+++ b/python_modules/ptypes.py
@@ -240,7 +240,7 @@ class EnumBaseType(Type):
 
     def c_enumname_by_name(self, name):
         if self.has_attr("prefix"):
-            return self.attributes["prefix"][0] + self.names[value]
+            return self.attributes["prefix"][0] + name
         return codegen.prefix_underscore_upper(self.name.upper(), name)
 
     def is_primitive(self):
commit b2f1b80a634cdda2a86d48ee7b823d287371f3fe
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Aug 29 17:21:28 2010 +0300

    server: fix print text on vdagent interface addition

diff --git a/server/reds.c b/server/reds.c
index ca6522d..aff8b6f 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -3453,7 +3453,7 @@ int spice_server_char_device_add_interface(SpiceServer *s,
     sif = SPICE_CONTAINEROF(char_device->base.sif, SpiceCharDeviceInterface, base);
     if (strcmp(char_device->subtype, SUBTYPE_VDAGENT) == 0) {
         if (vdagent) {
-            red_printf("vdi port already attached");
+            red_printf("vdagent already attached");
             return -1;
         }
         char_device->st = &vdagent_char_device_state;
commit 673ade8a6fdfd9b9160f1ddc838f4ddcc80502ec
Author: Alon Levy <alevy at redhat.com>
Date:   Tue Aug 31 12:42:30 2010 +0300

    server: add char_device.h header, use in reds.c

diff --git a/server/char_device.h b/server/char_device.h
new file mode 100644
index 0000000..486df6f
--- /dev/null
+++ b/server/char_device.h
@@ -0,0 +1,11 @@
+#ifndef __CHAR_DEVICE_H__
+#define __CHAR_DEVICE_H__
+
+#include "server/spice-experimental.h"
+
+struct SpiceCharDeviceState {
+    void (*wakeup)(SpiceCharDeviceInstance *sin);
+};
+
+#endif // __CHAR_DEVICE_H__
+
diff --git a/server/reds.c b/server/reds.c
index c7181ee..ca6522d 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -55,6 +55,7 @@
 #include "demarshallers.h"
 #include "marshaller.h"
 #include "generated_marshallers.h"
+#include "server/char_device.h"
 #ifdef USE_TUNNEL
 #include "red_tunnel_worker.h"
 #endif
@@ -174,10 +175,6 @@ enum {
     VDI_PORT_READ_STATE_READ_DATA,
 };
 
-struct SpiceCharDeviceState {
-    void (*wakeup)(SpiceCharDeviceInstance *sin);
-};
-
 void vdagent_char_device_wakeup(SpiceCharDeviceInstance *sin);
 struct SpiceCharDeviceState vdagent_char_device_state = {
     .wakeup = &vdagent_char_device_wakeup,
commit 14f2b0f52a48b987f5ae5bde06820081ad50af3d
Author: Alon Levy <alevy at redhat.com>
Date:   Sun Aug 29 17:16:26 2010 +0300

    spice-experimental.h: add multiple include protection

diff --git a/server/spice-experimental.h b/server/spice-experimental.h
index 7c1fdf2..526062f 100644
--- a/server/spice-experimental.h
+++ b/server/spice-experimental.h
@@ -1,5 +1,9 @@
-/* char device interfaces */
+#ifndef __SPICE_EXPERIMENTAL_H__
+#define __SPICE_EXPERIMENTAL_H__
+
+#include "spice.h"
 
+/* char device interfaces */
 #define SPICE_INTERFACE_CHAR_DEVICE "char_device"
 #define SPICE_INTERFACE_CHAR_DEVICE_MAJOR 1
 #define SPICE_INTERFACE_CHAR_DEVICE_MINOR 1
@@ -63,3 +67,5 @@ int spice_server_migrate_start(SpiceServer *s);
 int spice_server_migrate_client_state(SpiceServer *s);
 int spice_server_migrate_end(SpiceServer *s, int completed);
 
+#endif // __SPICE_EXPERIMENTAL_H__
+


More information about the Spice-commits mailing list