Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/XRDP/sesman/chansrv/   (X11 Server Version 0.10.6.1©)  Datei vom 7.6.2026 mit Größe 79 kB image not shown  

Quelle  smartcard.c

  Sprache: C
 

/**
 * xrdp: A Remote Desktop Protocol server.
 *
 * Copyright (C) Laxmikant Rashinkar 2013 LK.Rashinkar@gmail.com
 * Copyright (C) Jay Sorg 2013 jay.sorg@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */


/**
 * @file sesman/chansrv/smartcard.c
 *
 * smartcard redirection support
 *
 * This file implements some of the PDUs detailed in [MS-RDPESC].
 *
 * The PDUs use DCE IDL structs. These are required to be re-interpreted
 * in DCE NDR (Netword Data Representation)
 *
 * For more information on this subject see DCE publication C706
 * "DCE 1.1: Remote Procedure Call" 1997. In particular:-
 * Section 4.2 : Describes the IDL
 * Section 14 : Describes the NDR
 */


#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif

#include <string.h>
#include "os_calls.h"
#include "string_calls.h"
#include "smartcard.h"
#include "smartcard_internal.h"
#include "log.h"
#include "irp.h"
#include "devredir.h"
#include "smartcard_pcsc.h"
#include "chansrv.h"

/*
 * TODO
 *
 * o ensure that all wide calls are handled correctly
 *
 * o need to query client for build number and determine whether we should use
 *   SCREDIR_VERSION_XP or SCREDIR_VERSION_LONGHORN
 *
 * o need to call scard_release_resources()
 *
 * o why is win 7 sending SCARD_IOCTL_ACCESS_STARTED_EVENT first
 * 0000 00 01 00 00 04 00 00 00 e0 00 09 00 00 00 00 00 ................
 * 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
 * 0020 28 b7 9d 02
 */


/*
 * Notes:
 *
 * XP and Server 2003 use version    SCREDIR_VERSION_XP       functions 5 - 58
 * Vista and Server 2008 use version SCREDIR_VERSION_LONGHORN functions 5 - 64
 * if TS Client's build number is >= 4,034 use SCREDIR_VERSION_LONGHORN
 */


/* [MS-RDPESC] 3.1.4 */
#define SCARD_IOCTL_ESTABLISH_CONTEXT        0x00090014 /* EstablishContext     */
#define SCARD_IOCTL_RELEASE_CONTEXT          0x00090018 /* ReleaseContext       */
#define SCARD_IOCTL_IS_VALID_CONTEXT         0x0009001C /* IsValidContext       */
#define SCARD_IOCTL_LIST_READER_GROUPS       0x00090020 /* ListReaderGroups     */
#define SCARD_IOCTL_LIST_READERS_A           0x00090028 /* ListReaders ASCII    */
#define SCARD_IOCTL_LIST_READERS_W           0x0009002C /* ListReaders Wide     */
#define SCARD_IOCTL_INTRODUCE_READER_GROUP   0x00090050 /* IntroduceReaderGroup */
#define SCARD_IOCTL_FORGET_READER_GROUP      0x00090058 /* ForgetReader         */
#define SCARD_IOCTL_INTRODUCE_READER         0x00090060 /* IntroduceReader      */
#define SCARD_IOCTL_FORGET_READER            0x00090068 /* IntroduceReader      */
#define SCARD_IOCTL_ADD_READER_TO_GROUP      0x00090070 /* AddReaderToGroup     */
#define SCARD_IOCTL_REMOVE_READER_FROM_GROUP 0x00090078 /* RemoveReaderFromGroup*/
#define SCARD_IOCTL_GET_STATUS_CHANGE_A      0x000900A0 /* GetStatusChangeA     */
#define SCARD_IOCTL_GET_STATUS_CHANGE_W      0x000900A4 /* GetStatusChangeW     */
#define SCARD_IOCTL_CANCEL                   0x000900A8 /* Cancel               */
#define SCARD_IOCTL_CONNECT_A                0x000900AC /* ConnectA             */
#define SCARD_IOCTL_CONNECT_W                0x000900B0 /* ConnectW             */
#define SCARD_IOCTL_RECONNECT                0x000900B4 /* Reconnect            */
#define SCARD_IOCTL_DISCONNECT               0x000900B8 /* Disconnect           */
#define SCARD_IOCTL_BEGIN_TRANSACTION        0x000900BC /* BeginTransaction     */
#define SCARD_IOCTL_END_TRANSACTION          0x000900C0 /* EndTransaction       */
#define SCARD_IOCTL_STATE                    0x000900C4 /* State                */
#define SCARD_IOCTL_STATUS_A                 0x000900C8 /* StatusA              */
#define SCARD_IOCTL_STATUS_W                 0x000900CC /* StatusW              */
#define SCARD_IOCTL_TRANSMIT                 0x000900D0 /* Transmit             */
#define SCARD_IOCTL_CONTROL                  0x000900D4 /* Control              */
#define SCARD_IOCTL_GETATTRIB                0x000900D8 /* GetAttrib            */
#define SCARD_IOCTL_SETATTRIB                0x000900DC /* SetAttrib            */
#define SCARD_IOCTL_ACCESS_STARTED_EVENT     0x000900E0 /* SCardAccessStartedEvent */
#define SCARD_IOCTL_LOCATE_CARDS_BY_ATR      0x000900E8 /* LocateCardsByATR     */

/* scope used in EstablishContextCall */
#define SCARD_SCOPE_USER                     0x00000000
#define SCARD_SCOPE_TERMINAL                 0x00000001
#define SCARD_SCOPE_SYSTEM                   0x00000002

/* disposition - action to take on card */
#define SCARD_LEAVE_CARD                     0x00000000
#define SCARD_RESET_CARD                     0x00000001
#define SCARD_UNPOWER_CARD                   0x00000002
#define SCARD_EJECT_CARD                     0x00000003

#define MAX_SMARTCARDS                       16

/* stores info about a smart card */
typedef struct smartcard
{
    tui32 DeviceId;
} SMARTCARD;

/* globals */
SMARTCARD   *smartcards[MAX_SMARTCARDS];
int          g_smartcards_inited = 0;
static tui32 g_device_id = 0;
static int   g_scard_index = 0;

/* externs */
extern tui32 g_completion_id;
extern int   g_rdpdr_chan_id;    /* in chansrv.c */


/******************************************************************************
**                   static functions local to this file                     **
******************************************************************************/

static struct stream *scard_make_new_ioctl(IRP *irp, tui32 ioctl);
static int  scard_add_new_device(tui32 device_id);
static int  scard_get_free_slot(void);
static void scard_release_resources(void);
static void scard_send_EstablishContext(IRP *irp, int scope);
static void scard_send_ReleaseContext(IRP *irp,
                                      char *context, int context_bytes);
static void scard_send_IsContextValid(IRP *irp,
                                      char *context, int context_bytes);
static void scard_send_ListReaders(IRP *irp,
                                   char *context, int context_bytes,
                                   char *groups, int cchReaders,
                                   int wide);
static void scard_send_GetStatusChange(IRP *irp,
                                       char *context, int context_bytes,
                                       int wide,
                                       tui32 timeout, tui32 num_readers,
                                       READER_STATE *rsa);
static void scard_send_Connect(IRP *irp,
                               char *context, int context_bytes,
                               int wide,
                               READER_STATE *rs);
static void scard_send_Reconnect(IRP *irp,
                                 char *context, int context_bytes,
                                 char *card, int card_bytes,
                                 READER_STATE *rs);
static void scard_send_BeginTransaction(IRP *irp,
                                        char *context, int context_bytes,
                                        char *card, int card_bytes);
static void scard_send_EndTransaction(IRP *irp,
                                      char *context, int context_bytes,
                                      char *card, int card_bytes,
                                      tui32 dwDisposition);
static void scard_send_Status(IRP *irp, int wide,
                              char *context, int context_bytes,
                              char *card, int card_bytes,
                              int cchReaderLen, int cbAtrLen);
static void scard_send_Disconnect(IRP *irp,
                                  char *context, int context_bytes,
                                  char *card, int card_bytes,
                                  int dwDisposition);
static int  scard_send_Transmit(IRP *irp,
                                char *context, int context_byte,
                                char *card, int card_bytes,
                                char *send_data, int send_bytes,
                                int recv_bytes,
                                struct xrdp_scard_io_request *send_ior,
                                struct xrdp_scard_io_request *recv_ior);
static int scard_send_Control(IRP *irp, char *context, int context_bytes,
                              char *card, int card_bytes,
                              char *send_data, int send_bytes,
                              int recv_bytes, int control_code);
static int scard_send_Cancel(IRP *irp, char *context, int context_bytes);
static int scard_send_GetAttrib(IRP *irp, char *card, int card_bytes,
                                READER_STATE *rs);

/******************************************************************************
**                    local callbacks into this module                       **
******************************************************************************/


static void scard_handle_EstablishContext_Return(struct stream *s, IRP *irp,
        tui32 DeviceId, tui32 CompletionId,
        tui32 IoStatus);

static void scard_handle_ReleaseContext_Return(struct stream *s, IRP *irp,
        tui32 DeviceId, tui32 CompletionId,
        tui32 IoStatus);


static void scard_handle_IsContextValid_Return(struct stream *s, IRP *irp,
        tui32 DeviceId, tui32 CompletionId,
        tui32 IoStatus);

static void scard_handle_ListReaders_Return(struct stream *s, IRP *irp,
        tui32 DeviceId, tui32 CompletionId,
        tui32 IoStatus);

static void scard_handle_GetStatusChange_Return(struct stream *s, IRP *irp,
        tui32 DeviceId, tui32 CompletionId,
        tui32 IoStatus);

static void scard_handle_Connect_Return(struct stream *s, IRP *irp,
                                        tui32 DeviceId, tui32 CompletionId,
                                        tui32 IoStatus);

static void scard_handle_Reconnect_Return(struct stream *s, IRP *irp,
        tui32 DeviceId, tui32 CompletionId,
        tui32 IoStatus);

static void scard_handle_BeginTransaction_Return(struct stream *s, IRP *irp,
        tui32 DeviceId, tui32 CompletionId,
        tui32 IoStatus);

static void scard_handle_EndTransaction_Return(struct stream *s, IRP *irp,
        tui32 DeviceId,
        tui32 CompletionId,
        tui32 IoStatus);

static void scard_handle_Status_Return(struct stream *s, IRP *irp,
                                       tui32 DeviceId, tui32 CompletionId,
                                       tui32 IoStatus);

static void scard_handle_Disconnect_Return(struct stream *s, IRP *irp,
        tui32 DeviceId, tui32 CompletionId,
        tui32 IoStatus);


static void scard_handle_Transmit_Return(struct stream *s, IRP *irp,
        tui32 DeviceId,
        tui32 CompletionId,
        tui32 IoStatus);

static void scard_handle_Control_Return(struct stream *s, IRP *irp,
                                        tui32 DeviceId,
                                        tui32 CompletionId,
                                        tui32 IoStatus);

static void scard_handle_Cancel_Return(struct stream *s, IRP *irp,
                                       tui32 DeviceId,
                                       tui32 CompletionId,
                                       tui32 IoStatus);

static void scard_handle_GetAttrib_Return(struct stream *s, IRP *irp,
        tui32 DeviceId,
        tui32 CompletionId,
        tui32 IoStatus);

/******************************************************************************
**                                                                           **
**          externally accessible functions, defined in smartcard.h          **
**                                                                           **
******************************************************************************/


/**
 *****************************************************************************/

int
scard_device_announce(tui32 device_id)
{
    LOG_DEVEL(LOG_LEVEL_DEBUG, "entered: device_id=%d", device_id);
    int rv;

    if (g_smartcards_inited)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "already init");
        rv = (device_id == g_device_id) ? 0 : 1;
    }
    else
    {
        g_memset(&smartcards, 0sizeof(smartcards));
        g_smartcards_inited = 1;
        g_scard_index = scard_add_new_device(device_id);

        if (g_scard_index < 0)
        {
            LOG_DEVEL(LOG_LEVEL_DEBUG, "scard_add_new_device failed with DeviceId=%d", g_device_id);
            rv = 1;
        }
        else
        {
            LOG_DEVEL(LOG_LEVEL_DEBUG, "added smartcard with DeviceId=%d to list", g_device_id);
            g_device_id = device_id;
            rv = 0;
        }
    }
    return rv;
}

/**
 *
 *****************************************************************************/

int
scard_get_wait_objs(tbus *objs, int *count, int *timeout)
{
    return scard_pcsc_get_wait_objs(objs, count, timeout);
}

/**
 *
 *****************************************************************************/

int
scard_check_wait_objs(void)
{
    return scard_pcsc_check_wait_objs();
}

/**
 *
 *****************************************************************************/

int
scard_init(void)
{
    LOG_DEVEL(LOG_LEVEL_INFO, "scard_init:");
    return scard_pcsc_init();
}

/**
 *
 *****************************************************************************/

int
scard_deinit(void)
{
    LOG_DEVEL(LOG_LEVEL_INFO, "scard_deinit:");
    scard_pcsc_deinit();
    scard_release_resources();
    g_smartcards_inited = 0;
    return 0;
}

/**
 *
 *****************************************************************************/

int
scard_send_establish_context(void *user_data, int scope)
{
    IRP *irp;

    /* setup up IRP */
    if ((irp = devredir_irp_new()) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
        return 1;
    }

    irp->scard_index = g_scard_index;
    irp->CompletionId = g_completion_id++;
    irp->DeviceId = g_device_id;
    irp->callback = scard_handle_EstablishContext_Return;
    irp->user_data = user_data;

    /* send IRP to client */
    scard_send_EstablishContext(irp, scope);

    return 0;
}

/**
 * Release a previously established Smart Card context
 *****************************************************************************/

int
scard_send_release_context(void *user_data,
                           char *context, int context_bytes)
{
    IRP *irp;

    /* setup up IRP */
    if ((irp = devredir_irp_new()) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
        return 1;
    }

    irp->scard_index = g_scard_index;
    irp->CompletionId = g_completion_id++;
    irp->DeviceId = g_device_id;
    irp->callback = scard_handle_ReleaseContext_Return;
    irp->user_data = user_data;

    /* send IRP to client */
    scard_send_ReleaseContext(irp, context, context_bytes);

    return 0;
}

/**
 * Checks if a previously established context is still valid
 *****************************************************************************/

int
scard_send_is_valid_context(void *user_data, char *context, int context_bytes)
{
    IRP *irp;

    /* setup up IRP */
    if ((irp = devredir_irp_new()) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
        return 1;
    }

    irp->scard_index = g_scard_index;
    irp->CompletionId = g_completion_id++;
    irp->DeviceId = g_device_id;
    irp->callback = scard_handle_IsContextValid_Return;
    irp->user_data = user_data;

    /* send IRP to client */
    scard_send_IsContextValid(irp, context, context_bytes);

    return 0;
}

/**
 *
 *****************************************************************************/

int
scard_send_list_readers(void *user_data, char *context, int context_bytes,
                        char *groups, int cchReaders, int wide)
{
    IRP *irp;

    /* setup up IRP */
    if ((irp = devredir_irp_new()) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
        return 1;
    }
    irp->scard_index = g_scard_index;
    irp->CompletionId = g_completion_id++;
    irp->DeviceId = g_device_id;
    irp->callback = scard_handle_ListReaders_Return;
    irp->user_data = user_data;

    /* send IRP to client */
    scard_send_ListReaders(irp, context, context_bytes, groups,
                           cchReaders, wide);

    return 0;
}

/**
 * Send get change in status command
 *
 * @param  con          connection to client
 * @param  wide         TRUE if unicode string
 * @param  timeout      timeout in milliseconds, -1 for infinity
 * @param  num_readers  number of entries in rsa
 * @param  rsa          array of READER_STATEs
 *****************************************************************************/

int
scard_send_get_status_change(void *user_data, char *context, int context_bytes,
                             int wide, tui32 timeout, tui32 num_readers,
                             READER_STATE *rsa)
{
    IRP *irp;

    /* setup up IRP */
    if ((irp = devredir_irp_new()) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
        return 1;
    }

    irp->scard_index = g_scard_index;
    irp->CompletionId = g_completion_id++;
    irp->DeviceId = g_device_id;
    irp->callback = scard_handle_GetStatusChange_Return;
    irp->user_data = user_data;

    /* send IRP to client */
    scard_send_GetStatusChange(irp, context, context_bytes, wide, timeout,
                               num_readers, rsa);

    return 0;
}

/**
 * Open a connection to the smart card located in the reader
 *
 * @param  con   connection to client
 * @param  wide  TRUE if unicode string
 *****************************************************************************/

int
scard_send_connect(void *user_data, char *context, int context_bytes,
                   int wide, READER_STATE *rs)
{
    IRP *irp;

    /* setup up IRP */
    if ((irp = devredir_irp_new()) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
        return 1;
    }

    irp->scard_index = g_scard_index;
    irp->CompletionId = g_completion_id++;
    irp->DeviceId = g_device_id;
    irp->callback = scard_handle_Connect_Return;
    irp->user_data = user_data;

    /* send IRP to client */
    scard_send_Connect(irp, context, context_bytes, wide, rs);

    return 0;
}

/**
 * The reconnect method re-establishes a smart card reader handle. On success,
 * the handle is valid once again.
 *
 * @param  con        connection to client
 * @param  sc_handle  handle to device
 * @param  rs         reader state where following fields are set
 *                        rs.shared_mode_flag
 *                        rs.preferred_protocol
 *                        rs.init_type
 *****************************************************************************/

int
scard_send_reconnect(void *user_data, char *context, int context_bytes,
                     char *card, int card_bytes, READER_STATE *rs)
{
    IRP *irp;

    /* setup up IRP */
    if ((irp = devredir_irp_new()) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
        return 1;
    }

    irp->scard_index = g_scard_index;
    irp->CompletionId = g_completion_id++;
    irp->DeviceId = g_device_id;
    irp->callback = scard_handle_Reconnect_Return;
    irp->user_data = user_data;

    /* send IRP to client */
    scard_send_Reconnect(irp, context, context_bytes, card, card_bytes, rs);

    return 0;
}

/**
 * Lock smart card reader for exclusive access for specified smart
 * card reader handle.
 *
 * @param  con   connection to client
 *****************************************************************************/

int
scard_send_begin_transaction(void *user_data, char *context, int context_bytes,
                             char *card, int card_bytes)
{
    IRP *irp;

    /* setup up IRP */
    if ((irp = devredir_irp_new()) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
        return 1;
    }

    irp->scard_index = g_scard_index;
    irp->CompletionId = g_completion_id++;
    irp->DeviceId = g_device_id;
    irp->callback = scard_handle_BeginTransaction_Return;
    irp->user_data = user_data;

    /* send IRP to client */
    scard_send_BeginTransaction(irp, context, context_bytes, card, card_bytes);

    return 0;
}

/**
 * Release a smart card reader after being locked by a previously
 * successful call to Begin Transaction
 *
 * @param  con        connection to client
 * @param  sc_handle  handle to smartcard
 *****************************************************************************/

int
scard_send_end_transaction(void *user_data, char *context, int context_bytes,
                           char *card, int card_bytes,
                           tui32 dwDisposition)
{
    IRP *irp;

    /* setup up IRP */
    if ((irp = devredir_irp_new()) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
        return 1;
    }

    irp->scard_index = g_scard_index;
    irp->CompletionId = g_completion_id++;
    irp->DeviceId = g_device_id;
    irp->callback = scard_handle_EndTransaction_Return;
    irp->user_data = user_data;

    /* send IRP to client */
    scard_send_EndTransaction(irp, context, context_bytes,
                              card, card_bytes, dwDisposition);

    return 0;
}

/**
 * Get the status of a connection for a valid smart card reader handle
 *
 * @param  con   connection to client
 * @param  wide  TRUE if unicode string
 *****************************************************************************/

int
scard_send_status(void *user_data, int wide, char *context, int context_bytes,
                  char *card, int card_bytes,
                  int cchReaderLen, int cbAtrLen)
{
    IRP *irp;

    /* setup up IRP */
    if ((irp = devredir_irp_new()) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
        return 1;
    }

    irp->scard_index = g_scard_index;
    irp->CompletionId = g_completion_id++;
    irp->DeviceId = g_device_id;
    irp->callback = scard_handle_Status_Return;
    irp->user_data = user_data;

    /* send IRP to client */
    scard_send_Status(irp, wide, context, context_bytes, card, card_bytes,
                      cchReaderLen, cbAtrLen);

    return 0;
}

/**
 * Release a smart card reader handle that was acquired in ConnectA/ConnectW
 *
 * @param  con        connection to client
 * @param  sc_handle  handle to smartcard
 *****************************************************************************/

int
scard_send_disconnect(void *user_data, char *context, int context_bytes,
                      char *card, int card_bytes, int dwDisposition)
{
    IRP *irp;

    /* setup up IRP */
    if ((irp = devredir_irp_new()) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
        return 1;
    }

    irp->scard_index = g_scard_index;
    irp->CompletionId = g_completion_id++;
    irp->DeviceId = g_device_id;
    irp->callback = scard_handle_Disconnect_Return;
    irp->user_data = user_data;

    /* send IRP to client */
    scard_send_Disconnect(irp, context, context_bytes,
                          card, card_bytes, dwDisposition);

    return 0;
}

/**
 * The Transmit_Call structure is used to send data to the smart card
 * associated with a valid context.
 *****************************************************************************/

int
scard_send_transmit(void *user_data, char *context, int context_bytes,
                    char *card, int card_bytes,
                    char *send_data, int send_bytes, int recv_bytes,
                    struct xrdp_scard_io_request *send_ior,
                    struct xrdp_scard_io_request *recv_ior)
{
    IRP *irp;

    /* setup up IRP */
    if ((irp = devredir_irp_new()) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
        return 1;
    }

    irp->scard_index = g_scard_index;
    irp->CompletionId = g_completion_id++;
    irp->DeviceId = g_device_id;
    irp->callback = scard_handle_Transmit_Return;
    irp->user_data = user_data;

    /* send IRP to client */
    scard_send_Transmit(irp, context, context_bytes, card, card_bytes,
                        send_data, send_bytes,
                        recv_bytes, send_ior, recv_ior);

    return 0;
}

/**
 * Communicate directly with the smart card reader
 *****************************************************************************/

int
scard_send_control(void *user_data, char *context, int context_bytes,
                   char *card, int card_bytes,
                   char *send_data, int send_bytes,
                   int recv_bytes, int control_code)
{
    IRP *irp;

    /* setup up IRP */
    if ((irp = devredir_irp_new()) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
        return 1;
    }

    irp->scard_index = g_scard_index;
    irp->CompletionId = g_completion_id++;
    irp->DeviceId = g_device_id;
    irp->callback = scard_handle_Control_Return;
    irp->user_data = user_data;

    /* send IRP to client */
    scard_send_Control(irp, context, context_bytes,
                       card, card_bytes,
                       send_data, send_bytes,
                       recv_bytes, control_code);

    return 0;
}

/**
 * Cancel any outstanding calls
 *****************************************************************************/

int
scard_send_cancel(void *user_data, char *context, int context_bytes)
{
    IRP *irp;

    /* setup up IRP */
    if ((irp = devredir_irp_new()) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
        return 1;
    }

    irp->scard_index = g_scard_index;
    irp->CompletionId = g_completion_id++;
    irp->DeviceId = g_device_id;
    irp->callback = scard_handle_Cancel_Return;
    irp->user_data = user_data;

    /* send IRP to client */
    scard_send_Cancel(irp, context, context_bytes);

    return 0;
}

/**
 * Get reader attributes
 *****************************************************************************/

int
scard_send_get_attrib(void *user_data, char *card, int card_bytes,
                      READER_STATE *rs)
{
    IRP *irp;

    /* setup up IRP */
    if ((irp = devredir_irp_new()) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
        return 1;
    }

    irp->scard_index = g_scard_index;
    irp->CompletionId = g_completion_id++;
    irp->DeviceId = g_device_id;
    irp->callback = scard_handle_GetAttrib_Return;
    irp->user_data = user_data;

    /* send IRP to client */
    scard_send_GetAttrib(irp, card, card_bytes, rs);

    return 0;
}

/******************************************************************************
**                                                                           **
**                   static functions local to this file                     **
**                                                                           **
******************************************************************************/


/**
 * Create a new stream and insert specified IOCTL
 *
 * @param  irp    information about the I/O
 * @param  ioctl  the IOCTL code
 *
 * @return stream with IOCTL inserted in it, NULL on error
 *****************************************************************************/

static struct stream *
scard_make_new_ioctl(IRP *irp, tui32 ioctl)
{
    /*
     * format of device control request
     *
     * DeviceIoRequest
     * u16       RDPDR_CTYP_CORE
     * u16       PAKID_CORE_DEVICE_IOREQUEST
     * u32       DeviceId
     * u32       FileId
     * u32       CompletionId
     * u32       MajorFunction
     * u32       MinorFunction
     *
     * u32       OutputBufferLength SHOULD be 2048
     * u32       InputBufferLength
     * u32       IoControlCode
     * 20 bytes  padding
     * xx bytes  InputBuffer (variable)
     */


    struct stream *s;

    xstream_new(s, 1024 * 4);

    devredir_insert_DeviceIoRequest(s,
                                    irp->DeviceId,
                                    irp->FileId,
                                    irp->CompletionId,
                                    IRP_MJ_DEVICE_CONTROL,
                                    IRP_MN_NONE);

    xstream_wr_u32_le(s, 2048);        /* OutputBufferLength               */
    s_push_layer(s, iso_hdr, 4);       /* InputBufferLength - insert later */
    xstream_wr_u32_le(s, ioctl);       /* Ioctl Code                       */
    out_uint8s(s, 20);                 /* padding                          */

    /* [MS-RPCE] 2.2.6.1 */
    xstream_wr_u32_le(s, 0x00081001);  /* len 8, LE, v1                    */
    xstream_wr_u32_le(s, 0xcccccccc);  /* filler                           */

    return s;
}

/**
 * Create a new smart card device entry and insert it into smartcards[]
 *
 * @param  device_id  DeviceId of new card
 *
 * @return index into smartcards[] on success, -1 on failure
 *****************************************************************************/

static int
scard_add_new_device(tui32 device_id)
{
    int        index;
    SMARTCARD *sc;

    if ((index = scard_get_free_slot()) < 0)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "scard_get_free_slot failed");
        return -1;
    }

    sc = g_new0(SMARTCARD, 1);
    if (sc == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "system out of memory");
        return -1;
    }

    sc->DeviceId = device_id;
    smartcards[index] = sc;

    return index;
}

/**
 * Find first unused entry in smartcards
 *
 * @return index of first unused entry in smartcards or -1 if smartcards
 * is full
 *****************************************************************************/

static int
scard_get_free_slot(void)
{
    int i;

    for (i = 0; i < MAX_SMARTCARDS; i++)
    {
        if (smartcards[i] == NULL)
        {
            LOG_DEVEL(LOG_LEVEL_DEBUG, "found free slot at index %d", i);
            return i;
        }
    }

    LOG_DEVEL(LOG_LEVEL_ERROR, "too many smart card devices; rejecting this one");
    return -1;
}

/**
 * Release resources prior to shutting down
 *****************************************************************************/

static void
scard_release_resources(void)
{
    int i;

    for (i = 0; i < MAX_SMARTCARDS; i++)
    {
        if (smartcards[i] != NULL)
        {
            g_free(smartcards[i]);
            smartcards[i] = NULL;
        }
    }
}

/**
 *
 *****************************************************************************/

static void
scard_send_EstablishContext(IRP *irp, int scope)
{
    struct stream *s;
    int            bytes;

    if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_ESTABLISH_CONTEXT)) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
        return;
    }

    s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
    out_uint32_le(s, 0x00000000);
    out_uint32_le(s, scope);
    out_uint32_le(s, 0x00000000);

    s_mark_end(s);

    s_pop_layer(s, mcs_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 8;
    out_uint32_le(s, bytes);

    s_pop_layer(s, iso_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 28;
    out_uint32_le(s, bytes);

    bytes = (int) (s->end - s->data);

    /* send to client */
    send_channel_data(g_rdpdr_chan_id, s->data, bytes);

    free_stream(s);
}

/**
 * Release a previously established Smart Card context
 *****************************************************************************/

static void
scard_send_ReleaseContext(IRP *irp, char *context, int context_bytes)
{
    /* see [MS-RDPESC] 3.1.4.2 */

    SMARTCARD     *sc;
    struct stream *s;
    int            bytes;

    if ((sc = smartcards[irp->scard_index]) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
        return;
    }

    if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_RELEASE_CONTEXT)) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
        return;
    }

    s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
    out_uint32_le(s, 0x00000000);
    out_uint32_le(s, context_bytes);
    out_uint32_le(s, 0x00020000);
    out_uint32_le(s, context_bytes);
    out_uint8a(s, context, context_bytes);

    s_mark_end(s);

    s_pop_layer(s, mcs_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 8;
    out_uint32_le(s, bytes);

    s_pop_layer(s, iso_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 28;
    out_uint32_le(s, bytes);

    bytes = (int) (s->end - s->data);

    /* send to client */
    send_channel_data(g_rdpdr_chan_id, s->data, bytes);

    free_stream(s);
}

/**
 * Checks if a previously established context is still valid
 *****************************************************************************/

static void
scard_send_IsContextValid(IRP *irp, char *context, int context_bytes)
{
    /* see [MS-RDPESC] 3.1.4.3 */

    SMARTCARD     *sc;
    struct stream *s;
    int            bytes;

    if ((sc = smartcards[irp->scard_index]) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
        return;
    }

    if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_IS_VALID_CONTEXT)) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
        return;
    }

    /*
     * command format
     *
     * ......
     *       20 bytes    padding
     * u32    4 bytes    len 8, LE, v1
     * u32    4 bytes    filler
     *       16 bytes    unused (s->p currently pointed here at unused[0])
     * u32    4 bytes    context len
     * u32    4 bytes    context
     */


    s_push_layer(s, mcs_hdr, 4); /* bytes, set later */

    /* insert context */
    out_uint32_le(s, context_bytes);
    out_uint8a(s, context, context_bytes);

    s_mark_end(s);

    s_pop_layer(s, mcs_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 8;
    out_uint32_le(s, bytes);

    s_pop_layer(s, iso_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 28;
    out_uint32_le(s, bytes);

    bytes = (int) (s->end - s->data);

    /* send to client */
    send_channel_data(g_rdpdr_chan_id, s->data, bytes);

    free_stream(s);
}

/*****************************************************************************/
static void
align_s(struct stream *s, unsigned int boundary)
{
    unsigned int over = (unsigned int)(s->p - s->data) % boundary;
    if (over != 0)
    {
        out_uint8s(s, boundary - over);
    }
}

/**
 *
 *****************************************************************************/

static void
scard_send_ListReaders(IRP *irp, char *context, int context_bytes,
                       char *groups, int cchReaders, int wide)
{
    /* see [MS-RDPESC] 2.2.2.4
     *
     * IDL:-
     *
     * typedef struct _REDIR_SCARDCONTEXT {
     *    [range(0,16)] unsigned long cbContext;
     *    [unique] [size_is(cbContext)] byte *pbContext;
     *    } REDIR_SCARDCONTEXT;
     *
     * struct _ListReaders_Call {
     *     REDIR_SCARDCONTEXT Context;
     *     [range(0, 65536)] unsigned long cBytes;
     *     [unique] [size_is(cBytes)] const byte *mszGroups;
     *     long fmszReadersIsNULL;
     *     unsigned long cchReaders;
     *     } ListReaders_Call;
     *
     * Type summary:-
     *
     * Context.cbContext  Unsigned 32-bit word
     * Context.pbContext  Embedded full pointer to conformant array of bytes
     * cBytes             Unsigned 32-bit word
     * mszGroups          Embedded full pointer to conformant array of bytes
     * fmszReaders        32-bit word
     * cchReaders         Unsigned 32-bit word
     *
     * NDL:-
     *
     * Offset   Decription
     * 0        Context.cbContext
     * 4        Referent Identifier for pbContext
     * 8        cBytes
     * 12       Referent Identifier for mszGroups (or NULL)
     * 16       fmszReadersIsNULL
     * 20       cchReaders
     * 24       Conformant Array pointed to by pbContext
     * ??       Conformant Array pointed to by mszGroups
     *
     */


    SMARTCARD     *sc;
    struct stream *s;
    int            bytes;
    int            bytes_groups = 0// Length of NDR for groups + 2 terminators
    int            val = 0;    // Referent Id for mszGroups (assume NULL)
    int            groups_len = 0// strlen(groups)
    tui32          ioctl;

    if ((sc = smartcards[irp->scard_index]) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
        return;
    }

    ioctl = (wide) ? SCARD_IOCTL_LIST_READERS_W :
            SCARD_IOCTL_LIST_READERS_A;

    if ((s = scard_make_new_ioctl(irp, ioctl)) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
        return;
    }

    if (groups != NULL && *groups != '\0')
    {
        groups_len = g_strlen(groups);
        if (wide)
        {
            bytes_groups = (utf8_as_utf16_word_count(groups, groups_len) + 2) * 2;
        }
        else
        {
            bytes_groups = groups_len + 2;
        }
        val = 0x00020004;
    }

    s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
    out_uint32_le(s, 0x00000000);
    // REDIR_SCARDCONTEXT Context;
    out_uint32_le(s, context_bytes);
    out_uint32_le(s, 0x00020000);
    // [range(0, 65536)] unsigned long cBytes;
    out_uint32_le(s, bytes_groups);
    // [unique] [size_is(cBytes)] const byte *mszGroups; (pointer)
    out_uint32_le(s, val);
    // long fmszReadersIsNULL;
    out_uint32_le(s, 0x00000000);
    // unsigned long cchReaders;
    out_uint32_le(s, cchReaders);

    // At the end of the struct come the pointed-to structures

    // Context field pbContext is a Uni-dimensional conformant array
    out_uint32_le(s, context_bytes);
    out_uint8a(s, context, context_bytes);

    // mszGroups is also a Uni-dimensional conformant array of bytes
    if (bytes_groups > 0)
    {
        align_s(s, 4);
        out_uint32_le(s, bytes_groups);
        if (wide)
        {
            out_utf8_as_utf16_le(s, groups, groups_len);
            out_uint16_le(s, 0);
            out_uint16_le(s, 0);
        }
        else
        {
            out_uint8p(s, groups, groups_len);
            out_uint8(s, 0);
            out_uint8(s, 0);
        }
    }

    s_mark_end(s);

    s_pop_layer(s, mcs_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 8;
    out_uint32_le(s, bytes);

    s_pop_layer(s, iso_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 28;
    out_uint32_le(s, bytes);

    bytes = (int) (s->end - s->data);

    /* send to client */
    send_channel_data(g_rdpdr_chan_id, s->data, bytes);

    LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "scard_send_ListReaders:", s->data, bytes);

    free_stream(s);
}

/*****************************************************************************/
/**
 * Outputs the pointed-to-data for one of these IDL pointer types:-
 *     [string] const wchar_t* str;  (wide != 0)
 *     [string] const char* str;     (wide == 0)
 *
 * It is assumed that the referent identifier for the string has already
 * been sent
 *
 * @param s Output stream
 * @param str UTF-8 string to output
 * @param wide Whether to output as a wide string or ASCII
 *
 * Note that wchar_t on Windows is 16-bit
 * TODO: These strings have two terminators. Is this necessary?
 */

static void
out_conformant_and_varying_string(struct stream *s, const char *str, int wide)
{
    align_s(s, 4);
    unsigned int len = strlen(str);
    if (wide)
    {
        unsigned int num_chars = utf8_as_utf16_word_count(str, len);
        // Max number, offset and actual count
        out_uint32_le(s, num_chars + 2);
        out_uint32_le(s, 0);
        out_uint32_le(s, num_chars + 2);
        out_utf8_as_utf16_le(s, str, len);
        out_uint16_le(s, 0);  // Terminate string
        out_uint16_le(s, 0); // ?
    }
    else
    {
        out_uint32_le(s, len + 2);
        out_uint32_le(s, 0);
        out_uint32_le(s, len + 2);
        out_uint8p(s, str, len);
        out_uint8(s, 0);
        out_uint8(s, 0);
    }
}

/**
 * Get change in status
 *
 * @param  irp          I/O resource pkt
 * @param  wide         TRUE if unicode string
 * @param  timeout      timeout in milliseconds, -1 for infinity
 * @param  num_readers  number of entries in rsa
 * @param  rsa          array of READER_STATEs
 *****************************************************************************/

static void
scard_send_GetStatusChange(IRP *irp, char *context, int context_bytes,
                           int wide, tui32 timeout,
                           tui32 num_readers, READER_STATE *rsa)
{
    /* see [MS-RDPESC] 2.2.2.11 for ASCII
     * see [MS-RDPESC] 2.2.2.12 for Wide char
     *
     * Here is a breakdown of the Wide-char variant
     *
     * IDL:-
     *
     * typedef struct _REDIR_SCARDCONTEXT {
     *    [range(0,16)] unsigned long cbContext;
     *    [unique] [size_is(cbContext)] byte *pbContext;
     *    } REDIR_SCARDCONTEXT;
     *
     * typedef struct _ReaderState_Common_Call {
     *    unsigned long dwCurrentState;
     *    unsigned long dwEventState;
     *    [range(0,36)] unsigned long cbAtr;
     *    byte rgbAtr[36];
     *    } ReaderState_Common_Call;
     *
     * typedef struct _ReaderStateW {
     *   [string] const wchar_t* szReader;
     *   ReaderState_Common_Call Common;
     *   } ReaderStateW;
     *
     * struct _GetStatusChangeW_Call {
     *    REDIR_SCARDCONTEXT Context;
     *    unsigned long dwTimeOut;
     *    [range(0,11)] unsigned long cReaders;
     *    [size_is(cReaders)] ReaderStateW* rgReaderStates;
     *    } GetStatusChangeW_Call;
     *
     * Type summary:-
     *
     * Context.cbContext  Unsigned 32-bit word
     * Context.pbContext  Embedded full pointer to conformant array of bytes
     * dwTimeOut          Unsigned 32-bit word
     * cReaders           Unsigned 32-bit word
     * rgReaderStates
     *                    Embedded full pointer to array of rgReaderStates
     * rgReaderStates.szReader
     *                    Embedded full pointer to conformant and varying
     *                    string of [Windows] wchar_t
     * rgReaderStates.Common.dwCurrentState
     *                    Unsigned 32-bit word
     * rgReaderStates.Common.dwEventState
     *                    Unsigned 32-bit word
     * rgReaderStates.Common.cbAtr
     *                    Unsigned 32-bit word
     * rgReaderStates.Common.rgbAtr[36]
     *                    Uni-dimensional fixed array
     *
     * NDL:-
     * Offset   Decription
     * 0        Context.cbContext
     * 4        Referent Identifier for pbContext
     * 8        dwTimeOut;
     * 12       cReaders;
     * 16       Referent Identifier for rgReaderStates
     * 20       Conformant Array pointed to by pbContext
     * ??       Conformant Array pointed to by rgReaderStates. Each element
     *          of this array has a pointer to a string for the name
     * ??       String names pointed to in the above array.
     */


    SMARTCARD     *sc;
    READER_STATE  *rs;
    struct stream *s;
    tui32          ioctl;
    int            bytes;
    unsigned int   i;

    if ((sc = smartcards[irp->scard_index]) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
        return;
    }

    ioctl = (wide) ? SCARD_IOCTL_GET_STATUS_CHANGE_W :
            SCARD_IOCTL_GET_STATUS_CHANGE_A;

    if ((s = scard_make_new_ioctl(irp, ioctl)) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
        return;
    }

    s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
    out_uint32_le(s, 0x00000000);
    // REDIR_SCARDCONTEXT Context;
    out_uint32_le(s, context_bytes);
    out_uint32_le(s, 0x00020000);

    // unsigned long dwTimeOut;
    out_uint32_le(s, timeout);
    // [range(0,11)] unsigned long cReaders;
    out_uint32_le(s, num_readers);
    // [size_is(cReaders)] ReaderStateW* rgReaderStates;
    out_uint32_le(s, 0x00020004);

    // At the end of the struct come the pointed-to structures

    // Context field pbContext is a Uni-dimensional conformant array
    out_uint32_le(s, context_bytes);
    out_uint8a(s, context, context_bytes);

    // rgReaderState is a Uni-dimensional conformant array
    align_s(s, 4);
    out_uint32_le(s, num_readers);

    /* insert card reader state */
    for (i = 0; i < num_readers; i++)
    {
        rs = &rsa[i];
        //  [string] const wchar_t* szReader (wide)
        //  [string] const char_t* szReader (ASCII)
        out_uint32_le(s, 0x00020008 + (i * 4));
        //  unsigned long dwCurrentState;
        out_uint32_le(s, rs->current_state);
        //  unsigned long dwEventState;
        out_uint32_le(s, rs->event_state);
        //  [range(0,36)] unsigned long cbAtr;
        out_uint32_le(s, rs->atr_len);
        //  byte rgbAtr[36];
        out_uint8p(s, rs->atr, 33);
        out_uint8s(s, 3);
    }

    /* insert card reader names */
    for (i = 0; i < num_readers; i++)
    {
        rs = &rsa[i];
        out_conformant_and_varying_string(s, rs->reader_name, wide);
    }

    s_mark_end(s);

    s_pop_layer(s, mcs_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 8;
    out_uint32_le(s, bytes);

    s_pop_layer(s, iso_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 28;
    out_uint32_le(s, bytes);

    bytes = (int) (s->end - s->data);

    /* send to client */
    send_channel_data(g_rdpdr_chan_id, s->data, bytes);

    LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "scard_send_GetStatusChange:", s->data, bytes);

    free_stream(s);
}

/**
 * Send connect command
 *
 * @param  irp  I/O resource pkt
 * @param  wide TRUE if unicode string
 * @param  rs   reader state
 *****************************************************************************/

static void
scard_send_Connect(IRP *irp, char *context, int context_bytes,
                   int wide, READER_STATE *rs)
{
    /* see [MS-RDPESC] 2.2.2.13 for ASCII
     * see [MS-RDPESC] 2.2.2.14 for Wide char
     *
     * Here is a breakdown of the Wide-char variant
     *
     * IDL:-
     *
     * typedef struct _REDIR_SCARDCONTEXT {
     *    [range(0,16)] unsigned long cbContext;
     *    [unique] [size_is(cbContext)] byte *pbContext;
     *    } REDIR_SCARDCONTEXT;
     *
     * typedef struct _Connect_Common {
     *     REDIR_SCARDCONTEXT Context;
     *     unsigned long dwShareMode;
     *     unsigned long dwPreferredProtocols;
     * } Connect_Common;
     *
     * typedef struct _ConnectW_Call {
     *     [string] const wchar_t* szReader;
     *     Connect_Common Common;
     * } ConnectW_Call;
     *
     * Type summary:-
     *
     * szReader           Embedded full pointer to conformant and varying
     *                    string of [Windows] wchar_t
     * Common.Context.cbContext
     *                    Unsigned 32-bit word
     * Common.Context.pbContext
     *                    Embedded full pointer to conformant array of bytes
     * Common.dwShareMode Unsigned 32-bit word
     * Common.dwPreferredProtocols
     *                    Unsigned 32-bit word
     *
     * NDL:-
     *
     * Offset   Decription
     * 0        Referent Identifier for szReader
     * 4        Context.cbContext
     * 8        Referent Identifier for pbContext
     * 12       dwShareMode
     * 16       dwPreferredProtocols
     * 20       Conformant Array pointed to by szReader
     * ??       Conformant Array pointed to by pbContext
     *
     */

    SMARTCARD     *sc;
    struct stream *s;
    tui32          ioctl;
    int            bytes;

    if ((sc = smartcards[irp->scard_index]) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
        return;
    }

    ioctl = (wide) ? SCARD_IOCTL_CONNECT_W :
            SCARD_IOCTL_CONNECT_A;

    if ((s = scard_make_new_ioctl(irp, ioctl)) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
        return;
    }

    s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
    out_uint32_le(s, 0x00000000);
    // [string] const wchar_t* szReader;
    out_uint32_le(s, 0x00020000);

    // REDIR_SCARDCONTEXT Context;
    out_uint32_le(s, context_bytes);
    out_uint32_le(s, 0x00020004);

    // unsigned long dwShareMode;
    out_uint32_le(s, rs->dwShareMode);
    // unsigned long dwPreferredProtocols;
    out_uint32_le(s, rs->dwPreferredProtocols);

    /* insert card reader name */
    out_conformant_and_varying_string(s, rs->reader_name, wide);

    /* insert context */
    align_s(s, 4);
    out_uint32_le(s, context_bytes);
    out_uint8a(s, context, context_bytes);
    out_uint32_le(s, 0); // ?

    s_mark_end(s);

    s_pop_layer(s, mcs_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 8;
    out_uint32_le(s, bytes);

    s_pop_layer(s, iso_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 28;
    out_uint32_le(s, bytes);

    bytes = (int) (s->end - s->data);

    /* send to client */
    send_channel_data(g_rdpdr_chan_id, s->data, bytes);

    free_stream(s);
}

/**
 * The reconnect method re-establishes a smart card reader handle. On success,
 * the handle is valid once again.
 *
 * @param  con        connection to client
 * @param  sc_handle  handle to device
 * @param  rs         reader state where following fields are set
 *                        rs.shared_mode_flag
 *                        rs.preferred_protocol
 *                        rs.init_type
 *****************************************************************************/

static void
scard_send_Reconnect(IRP *irp, char *context, int context_bytes,
                     char *card, int card_bytes, READER_STATE *rs)
{
    /* see [MS-RDPESC] 2.2.2.15 */
    /* see [MS-RDPESC] 3.1.4.36 */

    SMARTCARD     *sc;
    struct stream *s;
    int            bytes;

    if ((sc = smartcards[irp->scard_index]) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
        return;
    }

    if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_RECONNECT)) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
        return;
    }

    /*
     * command format
     *
     * ......
     *       20 bytes    padding
     * u32    4 bytes    len 8, LE, v1
     * u32    4 bytes    filler
     *       24 bytes    unused (s->p currently pointed here at unused[0])
     * u32    4 bytes    dwShareMode
     * u32    4 bytes    dwPreferredProtocols
     * u32    4 bytes    dwInitialization
     * u32    4 bytes    context length
     * u32    4 bytes    context
     * u32    4 bytes    handle length
     * u32    4 bytes    handle
     */


    xstream_seek(s, 24); /* TODO */

    out_uint32_le(s, rs->dwShareMode);
    out_uint32_le(s, rs->dwPreferredProtocols);
    out_uint32_le(s, rs->init_type);
    out_uint32_le(s, context_bytes);
    out_uint8a(s, context, context_bytes);
    out_uint32_le(s, card_bytes);
    out_uint8a(s, card, card_bytes);

    s_mark_end(s);

    s_pop_layer(s, iso_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 28;
    out_uint32_le(s, bytes);

    bytes = (int) (s->end - s->data);

    /* send to client */
    send_channel_data(g_rdpdr_chan_id, s->data, bytes);

    free_stream(s);
}

/**
 * Lock smart card reader for exclusive access for specified smart
 * card reader handle.
 *
 * @param  con   connection to client
 *****************************************************************************/

static void
scard_send_BeginTransaction(IRP *irp, char *context, int context_bytes,
                            char *card, int card_bytes)
{
    /* see [MS-RDPESC] 4.9 */

    SMARTCARD     *sc;
    struct stream *s;
    int            bytes;

    if ((sc = smartcards[irp->scard_index]) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
        return;
    }

    if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_BEGIN_TRANSACTION)) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
        return;
    }

    s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
    out_uint32_le(s, 0x00000000);
    out_uint32_le(s, context_bytes);
    out_uint32_le(s, 0x00020000);
    out_uint32_le(s, card_bytes);
    out_uint32_le(s, 0x00020004);
    out_uint32_le(s, 0x00000000);

    /* insert context */
    out_uint32_le(s, context_bytes);
    out_uint8a(s, context, context_bytes);

    /* insert card */
    out_uint32_le(s, card_bytes);
    out_uint8a(s, card, card_bytes);

    out_uint32_le(s, 0x00000000);

    s_mark_end(s);

    s_pop_layer(s, mcs_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 8;
    out_uint32_le(s, bytes);

    s_pop_layer(s, iso_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 28;
    out_uint32_le(s, bytes);

    bytes = (int) (s->end - s->data);

    /* send to client */
    send_channel_data(g_rdpdr_chan_id, s->data, bytes);

    free_stream(s);
}

/**
 * Release a smart card reader after being locked by a previously
 * successful call to Begin Transaction
 *
 * @param  con        connection to client
 * @param  sc_handle  handle to smartcard
 *****************************************************************************/

static void
scard_send_EndTransaction(IRP *irp, char *context, int context_bytes,
                          char *card, int card_bytes,
                          tui32 dwDisposition)
{
    /* see [MS-RDPESC] 3.1.4.32 */

    SMARTCARD     *sc;
    struct stream *s;
    int            bytes;

    if ((sc = smartcards[irp->scard_index]) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
        return;
    }

    if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_END_TRANSACTION)) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
        return;
    }

    s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
    out_uint32_le(s, 0x00000000);
    out_uint32_le(s, context_bytes);
    out_uint32_le(s, 0x00020000);
    out_uint32_le(s, card_bytes);
    out_uint32_le(s, 0x00020004);
    out_uint32_le(s, dwDisposition);

    /* insert context */
    out_uint32_le(s, context_bytes);
    out_uint8a(s, context, context_bytes);

    /* insert card */
    out_uint32_le(s, card_bytes);
    out_uint8a(s, card, card_bytes);

    out_uint32_le(s, 0);

    s_mark_end(s);

    s_pop_layer(s, mcs_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 8;
    out_uint32_le(s, bytes);

    s_pop_layer(s, iso_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 28;
    out_uint32_le(s, bytes);

    bytes = (int) (s->end - s->data);

    /* send to client */
    send_channel_data(g_rdpdr_chan_id, s->data, bytes);

    free_stream(s);
}

/**
 * Get the status of a connection for a valid smart card reader handle
 *
 * @param  con   connection to client
 * @param  wide  TRUE if unicode string
 *****************************************************************************/

static void
scard_send_Status(IRP *irp, int wide, char *context, int context_bytes,
                  char *card, int card_bytes,
                  int cchReaderLen, int cbAtrLen)
{
    /* see [MS-RDPESC] 2.2.2.18 */

    SMARTCARD     *sc;
    struct stream *s;
    int            bytes;
    tui32          ioctl;

    if ((sc = smartcards[irp->scard_index]) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
        return;
    }

    ioctl = wide ? SCARD_IOCTL_STATUS_W : SCARD_IOCTL_STATUS_A;
    if ((s = scard_make_new_ioctl(irp, ioctl)) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl");
        return;
    }
    /*
              30 00 00 00
              00 00 00 00
              04 00 00 00
              00 00 02 00
              04 00 00 00
              04 00 02 00
              01 00 00 00
              00 00 00 00 dwReaderLen
              40 00 00 00 dwAtrLen
              04 00 00 00
              07 00 00 00
              04 00 00 00
              09 00 00 00 hCard
              00 00 00 00
    */

    s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
    out_uint32_le(s, 0x00000000);
    out_uint32_le(s, context_bytes);
    out_uint32_le(s, 0x00020000);
    out_uint32_le(s, card_bytes);
    out_uint32_le(s, 0x00020004);
    out_uint32_le(s, 0x00000001);
    out_uint32_le(s, cchReaderLen); /* readerLen, see [MS-RDPESC] 4.11 */
    out_uint32_le(s, cbAtrLen); /* atrLen,    see [MS-RDPESC] 4.11 */

    /* insert context */
    out_uint32_le(s, context_bytes);
    out_uint8a(s, context, context_bytes);

    /* insert card */
    out_uint32_le(s, card_bytes);
    out_uint8a(s, card, card_bytes);

    out_uint32_le(s, 0);

    s_mark_end(s);

    s_pop_layer(s, mcs_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 8;
    out_uint32_le(s, bytes);

    s_pop_layer(s, iso_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 28;
    out_uint32_le(s, bytes);

    bytes = (int) (s->end - s->data);

    LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "", s->data, bytes);

    /* send to client */
    send_channel_data(g_rdpdr_chan_id, s->data, bytes);

    free_stream(s);
}

/**
 * Release a smart card reader handle that was acquired in ConnectA/ConnectW
 *
 * @param  con        connection to client
 * @param  sc_handle  handle to smartcard
 *****************************************************************************/

static void
scard_send_Disconnect(IRP *irp, char *context, int context_bytes,
                      char *card, int card_bytes, int dwDisposition)
{
    /* see [MS-RDPESC] 3.1.4.30 */

    SMARTCARD     *sc;
    struct stream *s;
    int            bytes;

    if ((sc = smartcards[irp->scard_index]) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
        return;
    }

    if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_DISCONNECT)) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl failed");
        return;
    }

    s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
    out_uint32_le(s, 0x00000000);
    out_uint32_le(s, context_bytes);
    out_uint32_le(s, 0x00020000);
    out_uint32_le(s, card_bytes);
    out_uint32_le(s, 0x00020004);
    out_uint32_le(s, dwDisposition);

    /* insert context */
    out_uint32_le(s, context_bytes);
    out_uint8a(s, context, context_bytes);

    /* insert card */
    out_uint32_le(s, card_bytes);
    out_uint8a(s, card, card_bytes);

    out_uint32_le(s, 0x00000000);

    s_mark_end(s);

    s_pop_layer(s, mcs_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 8;
    out_uint32_le(s, bytes);

    s_pop_layer(s, iso_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 28;
    out_uint32_le(s, bytes);

    bytes = (int) (s->end - s->data);

    /* send to client */
    send_channel_data(g_rdpdr_chan_id, s->data, bytes);

    free_stream(s);
}

/**
 * The Transmit_Call structure is used to send data to the smart card
 * associated with a valid context.
 *****************************************************************************/

static int
scard_send_Transmit(IRP *irp, char *context, int context_bytes,
                    char *card, int card_bytes, char *send_data,
                    int send_bytes, int recv_bytes,
                    struct xrdp_scard_io_request *send_ior,
                    struct xrdp_scard_io_request *recv_ior)
{
    /* see [MS-RDPESC] 2.2.2.19 */

    SMARTCARD     *sc;
    struct stream *s;
    int            bytes;
    int            val;

    if ((sc = smartcards[irp->scard_index]) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
        return 1;
    }

    if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_TRANSMIT)) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl");
        return 1;
    }

    LOG_DEVEL(LOG_LEVEL_DEBUG, "send_bytes %d recv_bytes %d send dwProtocol %d cbPciLength %d "
              "extra_bytes %d recv dwProtocol %d cbPciLength %d extra_bytes %d",
              send_bytes, recv_bytes, send_ior->dwProtocol, send_ior->cbPciLength,
              send_ior->extra_bytes, recv_ior->dwProtocol, recv_ior->cbPciLength,
              recv_ior->extra_bytes);

    /*
     * command format
     *
     * ......
     *       20 bytes    padding
     * u32    4 bytes    len 8, LE, v1
     * u32    4 bytes    filler
     *       12 bytes    unused (s->p currently pointed here at unused[0])
     * u32    4 bytes    map0
     *        4 bytes    unused
     * u32    4 bytes    map1
     * u32    4 bytes    dwProtocol
     * u32    4 bytes    cbPciLength
     * u32    4 bytes    map2
     * u32    4 bytes    cbSendLength
     * u32    4 bytes    map3
     * u32    4 bytes    map4
     * u32    4 bytes    map5
     * u32    4 bytes    map6
     * u32    4 bytes    cbRecvLength
     * u32    4 bytes    len of sc_handle
     * u32    4 bytes    sc_handle
     */


    //g_writeln("send_bytes %d", send_bytes);
    //g_writeln("recv_bytes %d", recv_bytes);

#if 0
    s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
    out_uint32_be(s, 0x00000000);
    out_uint32_be(s, 0x04000000);
    out_uint32_be(s, 0x00000200); // map 0
    out_uint32_be(s, 0x04000000);
    out_uint32_be(s, 0x04000200); // map 1
    out_uint32_be(s, 0x01000000);
    out_uint32_be(s, 0x00000000);
    out_uint32_be(s, 0x00000000);

    //out_uint32_be(s, 0x05000000);
    out_uint32_le(s, send_bytes);

    out_uint32_be(s, 0x08000200);
    out_uint32_be(s, 0x0c000200);
    out_uint32_be(s, 0x00000000);

    //out_uint32_be(s, 0x02010000);
    out_uint32_le(s, recv_bytes);

    out_uint32_be(s, 0x04000000);
    out_uint32_be(s, 0x05000000);
    out_uint32_be(s, 0x04000000);
    out_uint32_be(s, 0x0b000000);

    //out_uint32_be(s, 0x05000000);
    //out_uint32_be(s, 0x00b00704);
    //out_uint32_be(s, 0x10000000);
    out_uint32_le(s, send_bytes);
    out_uint8p(s, send_data, send_bytes);
    align_s(s, 4);

    out_uint32_be(s, 0x01000000);
    out_uint32_be(s, 0x00000000);
    out_uint32_be(s, 0x00000000);
#else

    //g_printf("send cbPciLength %d\n", send_ior->cbPciLength);
    //g_printf("send extra_bytes %d\n", send_ior->extra_bytes);
    //g_printf("recv cbPciLength %d\n", recv_ior->cbPciLength);
    //g_printf("recv extra_bytes %d\n", recv_ior->extra_bytes);

    s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
    out_uint32_le(s, 0x00000000);

    out_uint32_le(s, context_bytes);
    out_uint32_le(s, 0x00020000); /* map0 */

    out_uint32_le(s, card_bytes);
    out_uint32_le(s, 0x00020004); /* map1 */

    out_uint32_le(s, send_ior->dwProtocol);
    out_uint32_le(s, send_ior->cbPciLength - 8);

    val = send_ior->extra_bytes > 0 ? 1 : 0;
    out_uint32_le(s, val); /* map2 */

    out_uint32_le(s, send_bytes);

    val = send_bytes > 0 ? 0x00020008 : 0;
    out_uint32_le(s, val); /* map3 */

    val = recv_ior->cbPciLength > 0 ? 0x0002000c : 0;
    out_uint32_le(s, val); /* map 4 */

    out_uint32_le(s, 0); // map5
    out_uint32_le(s, recv_bytes);

    /* map0 */
    out_uint32_le(s, context_bytes);
    out_uint8a(s, context, context_bytes);

    /* map1 */
    out_uint32_le(s, card_bytes);
    out_uint8a(s, card, card_bytes);

    if (send_ior->extra_bytes > 0)
    {
        out_uint32_le(s, send_ior->extra_bytes);
        out_uint8a(s, send_ior->extra_data, send_ior->extra_bytes);
    }

    if (send_bytes > 0)
    {
        out_uint32_le(s, send_bytes);
        out_uint8a(s, send_data, send_bytes);
        align_s(s, 4);
    }

    if (recv_ior->cbPciLength > 0)
    {
        /* map4 */
        out_uint32_le(s, recv_ior->dwProtocol);
        out_uint32_le(s, recv_ior->cbPciLength - 8);
        val = recv_ior->extra_bytes > 0 ? 1 : 0;
        out_uint32_le(s, val); /* map6*/
        if (val)
        {
            out_uint32_le(s, recv_ior->extra_bytes);
            out_uint8a(s, recv_ior->extra_data, recv_ior->extra_bytes);
        }
    }
#endif

    s_mark_end(s);

    s_pop_layer(s, mcs_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 8;
    out_uint32_le(s, bytes);

    s_pop_layer(s, iso_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 28;
    out_uint32_le(s, bytes);

    bytes = (int) (s->end - s->data);

    /* send to client */
    send_channel_data(g_rdpdr_chan_id, s->data, bytes);

    LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "scard_send_Transmit:", s->data, bytes);

    free_stream(s);
    return 0;
}

/**
 * Communicate directly with the smart card reader
 *****************************************************************************/

static int
scard_send_Control(IRP *irp, char *context, int context_bytes,
                   char *card, int card_bytes, char *send_data,
                   int send_bytes, int recv_bytes, int control_code)
{
    /* see [MS-RDPESC] 2.2.2.19 */

    SMARTCARD     *sc;
    struct stream *s;
    int            bytes;
    int            val;

    if ((sc = smartcards[irp->scard_index]) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
        return 1;
    }

    if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_CONTROL)) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl");
        return 1;
    }

    s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
    out_uint32_le(s, 0x00000000);
    out_uint32_le(s, context_bytes);
    out_uint32_le(s, 0x00020000); /* map0 */
    out_uint32_le(s, card_bytes);
    out_uint32_le(s, 0x00020004); /* map1 */
    out_uint32_le(s, control_code);
    out_uint32_le(s, send_bytes);
    val = send_bytes > 0 ? 0x00020008 : 0;
    out_uint32_le(s, val);        /* map2 */
    out_uint32_le(s, 0x00000000);
    out_uint32_le(s, recv_bytes);
    out_uint32_le(s, context_bytes);
    out_uint8a(s, context, context_bytes);
    out_uint32_le(s, card_bytes);
    out_uint8a(s, card, card_bytes);
    if (send_bytes > 0)
    {
        out_uint32_le(s, send_bytes);
        out_uint8a(s, send_data, send_bytes);
    }
    else
    {
        out_uint32_le(s, 0x00000000);
    }

    s_mark_end(s);

    s_pop_layer(s, mcs_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 8;
    out_uint32_le(s, bytes);

    s_pop_layer(s, iso_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 28;
    out_uint32_le(s, bytes);

    bytes = (int) (s->end - s->data);

    LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "", s->data, bytes);

    /* send to client */
    send_channel_data(g_rdpdr_chan_id, s->data, bytes);

    free_stream(s);
    return 0;
}

/**
 * Cancel any outstanding calls
 *****************************************************************************/

static int
scard_send_Cancel(IRP *irp, char *context, int context_bytes)
{
    /* see [MS-RDPESC] 3.1.4.27 */

    SMARTCARD     *sc;
    struct stream *s;
    int            bytes;

    if ((sc = smartcards[irp->scard_index]) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
        return 1;
    }

    if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_CANCEL)) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl");
        return 1;
    }

    s_push_layer(s, mcs_hdr, 4); /* bytes, set later */
    out_uint32_le(s, 0x00000000);
    out_uint32_le(s, context_bytes);
    out_uint32_le(s, 0x00020000);
    out_uint32_le(s, context_bytes);
    out_uint8a(s, context, context_bytes);

    s_mark_end(s);

    s_pop_layer(s, mcs_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 8;
    out_uint32_le(s, bytes);

    s_pop_layer(s, iso_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 28;
    out_uint32_le(s, bytes);

    bytes = (int) (s->end - s->data);

    /* send to client */
    send_channel_data(g_rdpdr_chan_id, s->data, bytes);

    free_stream(s);
    return 0;
}

/**
 * Get reader attributes
 *****************************************************************************/

static int
scard_send_GetAttrib(IRP *irp, char *card, int card_bytes, READER_STATE *rs)
{
    /* see [MS-RDPESC] 2.2.2.21 */

    SMARTCARD     *sc;
    struct stream *s;
    int            bytes;

    if ((sc = smartcards[irp->scard_index]) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "smartcards[%d] is NULL", irp->scard_index);
        return 1;
    }

    if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_GETATTRIB)) == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "scard_make_new_ioctl");
        return 1;
    }

    /*
     * command format
     *
     * ......
     *       20 bytes    padding
     * u32    4 bytes    len 8, LE, v1
     * u32    4 bytes    filler
     *       24 bytes    unused (s->p currently pointed here at unused[0])
     * u32    4 bytes    dwAttribId
     *        4 bytes    unused
     * u32    4 bytes    dwAttrLen
     *        8 bytes    unused
     * u32    4 bytes    handle len
     * u32    4 bytes    handle
     */


    xstream_seek(s, 24); /* TODO */
    out_uint32_le(s, rs->dwAttribId);
    out_uint32_le(s, 0);
    out_uint32_le(s, rs->dwAttrLen);
    xstream_seek(s, 8);
    out_uint32_le(s, card_bytes);
    out_uint8a(s, card, card_bytes);

    s_mark_end(s);

    s_pop_layer(s, iso_hdr);
    bytes = (int) (s->end - s->p);
    bytes -= 28;
    out_uint32_le(s, bytes);

    bytes = (int) (s->end - s->data);

    /* send to client */
    send_channel_data(g_rdpdr_chan_id, s->data, bytes);

    free_stream(s);
    return 0;
}

/******************************************************************************
**                                                                           **
**                    local callbacks into this module                       **
**                                                                           **
******************************************************************************/


/**
 *
 *****************************************************************************/

static void
scard_handle_EstablishContext_Return(struct stream *s, IRP *irp,
                                     tui32 DeviceId, tui32 CompletionId,
                                     tui32 IoStatus)
{
    tui32 len;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
    /* sanity check */
    if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
        return;
    }
    /* get OutputBufferLen */
    xstream_rd_u32_le(s, len);
    scard_function_establish_context_return(irp->user_data, s, len, IoStatus);
    devredir_irp_delete(irp);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
}

/**
 *
 *****************************************************************************/

static void
scard_handle_ReleaseContext_Return(struct stream *s, IRP *irp,
                                   tui32 DeviceId, tui32 CompletionId,
                                   tui32 IoStatus)
{
    tui32 len;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
    /* sanity check */
    if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
        return;
    }
    /* get OutputBufferLen */
    xstream_rd_u32_le(s, len);
    scard_function_release_context_return(irp->user_data, s, len, IoStatus);
    devredir_irp_delete(irp);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
}

/**
 *
 *****************************************************************************/

static void
scard_handle_IsContextValid_Return(struct stream *s, IRP *irp,
                                   tui32 DeviceId, tui32 CompletionId,
                                   tui32 IoStatus)
{
    tui32 len;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");

    /* sanity check */
    if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
        return;
    }

    /* get OutputBufferLen */
    xstream_rd_u32_le(s, len);
    scard_function_is_context_valid_return(irp->user_data, s, len, IoStatus);
    devredir_irp_delete(irp);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
}

/**
 *
 *****************************************************************************/

static void
scard_handle_ListReaders_Return(struct stream *s, IRP *irp,
                                tui32 DeviceId, tui32 CompletionId,
                                tui32 IoStatus)
{
    tui32 len;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
    /* sanity check */
    if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
        return;
    }
    /* get OutputBufferLen */
    xstream_rd_u32_le(s, len);
    scard_function_list_readers_return(irp->user_data, s, len, IoStatus);
    devredir_irp_delete(irp);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
}

/**
 *
 *****************************************************************************/

static void
scard_handle_GetStatusChange_Return(struct stream *s, IRP *irp,
                                    tui32 DeviceId, tui32 CompletionId,
                                    tui32 IoStatus)
{
    tui32 len;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");
    /* sanity check */
    if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
        return;
    }
    /* get OutputBufferLen */
    xstream_rd_u32_le(s, len);
    scard_function_get_status_change_return(irp->user_data, s, len, IoStatus);
    devredir_irp_delete(irp);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
}

/**
 *
 *****************************************************************************/

static void
scard_handle_Connect_Return(struct stream *s, IRP *irp,
                            tui32 DeviceId, tui32 CompletionId,
                            tui32 IoStatus)
{
    tui32 len;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");

    /* sanity check */
    if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
        return;
    }

    /* get OutputBufferLen */
    xstream_rd_u32_le(s, len);

    scard_function_connect_return(irp->user_data, s, len, IoStatus);
    devredir_irp_delete(irp);

    LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
}

/**
 *
 *****************************************************************************/

static void
scard_handle_Reconnect_Return(struct stream *s, IRP *irp,
                              tui32 DeviceId, tui32 CompletionId,
                              tui32 IoStatus)
{
    tui32 len;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");

    /* sanity check */
    if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
        return;
    }

    /* get OutputBufferLen */
    xstream_rd_u32_le(s, len);
    scard_function_reconnect_return(irp->user_data, s, len, IoStatus);
    devredir_irp_delete(irp);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
}

/**
 *
 *****************************************************************************/

static void
scard_handle_BeginTransaction_Return(struct stream *s, IRP *irp,
                                     tui32 DeviceId, tui32 CompletionId,
                                     tui32 IoStatus)
{
    tui32 len;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");

    /* sanity check */
    if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
        return;
    }

    /* get OutputBufferLen */
    xstream_rd_u32_le(s, len);
    scard_function_begin_transaction_return(irp->user_data, s, len, IoStatus);
    devredir_irp_delete(irp);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
}

/**
 *
 *****************************************************************************/

static void
scard_handle_EndTransaction_Return(struct stream *s, IRP *irp,
                                   tui32 DeviceId, tui32 CompletionId,
                                   tui32 IoStatus)
{
    tui32 len;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");

    /* sanity check */
    if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
        return;
    }

    /* get OutputBufferLen */
    xstream_rd_u32_le(s, len);
    scard_function_end_transaction_return(irp->user_data, s, len, IoStatus);
    devredir_irp_delete(irp);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
}

/**
 *
 *****************************************************************************/

static void
scard_handle_Status_Return(struct stream *s, IRP *irp,
                           tui32 DeviceId, tui32 CompletionId,
                           tui32 IoStatus)
{
    tui32 len;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");

    /* sanity check */
    if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
        return;
    }

    /* get OutputBufferLen */
    xstream_rd_u32_le(s, len);
    scard_function_status_return(irp->user_data, s, len, IoStatus);
    devredir_irp_delete(irp);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
}

/**
 *
 *****************************************************************************/

static void
scard_handle_Disconnect_Return(struct stream *s, IRP *irp,
                               tui32 DeviceId, tui32 CompletionId,
                               tui32 IoStatus)
{
    tui32 len;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");

    /* sanity check */
    if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
        return;
    }

    /* get OutputBufferLen */
    xstream_rd_u32_le(s, len);
    scard_function_disconnect_return(irp->user_data, s, len, IoStatus);
    devredir_irp_delete(irp);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
}

/**
 *
 *****************************************************************************/

static void
scard_handle_Transmit_Return(struct stream *s, IRP *irp, tui32 DeviceId,
                             tui32 CompletionId, tui32 IoStatus)
{
    tui32 len;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");

    /* sanity check */
    if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
        return;
    }

    /* get OutputBufferLen */
    xstream_rd_u32_le(s, len);
    scard_function_transmit_return(irp->user_data, s, len, IoStatus);
    devredir_irp_delete(irp);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
}

/**
 *
 *****************************************************************************/

static void
scard_handle_Control_Return(struct stream *s, IRP *irp, tui32 DeviceId,
                            tui32 CompletionId, tui32 IoStatus)
{
    tui32 len;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");

    /* sanity check */
    if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
        return;
    }

    /* get OutputBufferLen */
    xstream_rd_u32_le(s, len);
    scard_function_control_return(irp->user_data, s, len, IoStatus);
    devredir_irp_delete(irp);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
}

/**
 *
 *****************************************************************************/

static void
scard_handle_Cancel_Return(struct stream *s, IRP *irp, tui32 DeviceId,
                           tui32 CompletionId, tui32 IoStatus)
{
    tui32 len;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");

    /* sanity check */
    if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
        return;
    }

    /* get OutputBufferLen */
    xstream_rd_u32_le(s, len);
    scard_function_cancel_return(irp->user_data, s, len, IoStatus);
    devredir_irp_delete(irp);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
}

/**
 *
 *****************************************************************************/

static void
scard_handle_GetAttrib_Return(struct stream *s, IRP *irp, tui32 DeviceId,
                              tui32 CompletionId, tui32 IoStatus)
{
    tui32 len;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "entered");

    /* sanity check */
    if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "DeviceId/CompletionId do not match those in IRP");
        return;
    }

    /* get OutputBufferLen */
    xstream_rd_u32_le(s, len);
    scard_function_get_attrib_return(irp->user_data, s, len, IoStatus);
    devredir_irp_delete(irp);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "leaving");
}

Messung V0.5 in Prozent
C=97 H=93 G=94

¤ Dauer der Verarbeitung: 0.37 Sekunden  (vorverarbeitet am  2026-07-10) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.