/*
   rdesktop: A Remote Desktop Protocol client.
   Smart Card support
   Copyright (C) Alexi Volkov <alexi@myrealbox.com> 2006
   Copyright 2010 Pierre Ossman <ossman@cendio.se> for Cendio AB
   Copyright 2011 Henrik Andersson <hean01@cendio.se> for Cendio AB

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
#include <strings.h>
#include <sys/types.h>
#include <time.h>
#include <arpa/inet.h>
#ifndef MAKE_PROTO
#ifdef __APPLE__
#include <PCSC/wintypes.h>
#include <PCSC/pcsclite.h>
#include <PCSC/winscard.h>
#else
#include <wintypes.h>
#include <pcsclite.h>
#include <winscard.h>
#ifdef PCSCLITE_VERSION_NUMBER
#include <reader.h>
#endif
#endif /* PCSC_OSX */
#include "rdesktop.h"
#include "scard.h"

/* variable segment */

#define SCARD_MAX_MEM 102400
#ifndef SCARD_AUTOALLOCATE
#define SCARD_AUTOALLOCATE -1
#endif
#define	OUT_STREAM_SIZE	4096

#ifdef B_ENDIAN
#define swap32(x)	((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) |	\
			(((x) & 0xff0000) >> 8) | (((x) & 0xff000000) >> 24))

#define	swap16(x)	((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8))
#else
#define	swap32(x)	(x)
#define	swap16(x)	(x)
#endif

static pthread_mutex_t **scard_mutex = NULL;

static uint32 curDevice = 0, curId = 0, curBytesOut = 0;
static PSCNameMapRec nameMapList = NULL;
static int nameMapCount = 0;

static pthread_t queueHandler;
static pthread_mutex_t queueAccess;
static pthread_cond_t queueEmpty;
static pthread_mutex_t hcardAccess;

static PMEM_HANDLE threadListHandle = NULL;
static PThreadListElement threadList = NULL;


static PSCThreadData queueFirst = NULL, queueLast = NULL;
static int threadCount = 0;

static PSCHCardRec hcardFirst = NULL;

static void *queue_handler_function(void *data);

/* code segment */

#endif /* MAKE_PROTO */
void
scardSetInfo(uint32 device, uint32 id, uint32 bytes_out)
{
	curDevice = device;
	curId = id;
	curBytesOut = bytes_out;
}

#ifndef MAKE_PROTO

static RD_NTSTATUS
scard_create(uint32 device_id, uint32 accessmask, uint32 sharemode, uint32 create_disposition,
	     uint32 flags_and_attributes, char *filename, RD_NTHANDLE * phandle)
{
	return RD_STATUS_SUCCESS;
}

static RD_NTSTATUS
scard_close(RD_NTHANDLE handle)
{
	return RD_STATUS_SUCCESS;
}

static RD_NTSTATUS
scard_read(RD_NTHANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32 * result)
{
	return RD_STATUS_SUCCESS;
}

static RD_NTSTATUS
scard_write(RD_NTHANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32 * result)
{
	return RD_STATUS_SUCCESS;
}
#endif /* MAKE_PROTO */

/* Enumeration of devices from rdesktop.c        */
/* returns numer of units found and initialized. */
/* optarg looks like ':"ReaderName=ReaderAlias"' */
/* when it arrives to this function.             */

int
scard_enum_devices(uint32 * id, char *optarg)
{
	char *name = optarg + 1;
	char *alias;
	int count = 0;
	PSCNameMapRec tmpMap;

	MYPCSC_DWORD rv;
	SCARDCONTEXT hContext;

	/* code segment  */
	rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
	if (rv != SCARD_S_SUCCESS)
	{
		error("scard_enum_devices: PCSC service not available\n");
		return 0;
	}
	else
		rv = SCardReleaseContext(hContext);

	count = 0;

	if (0 != pthread_mutex_init(&queueAccess, NULL))
	{
		error("scard_enum_devices: Can't initialize queue access mutex\n");
		return 0;
	}

	if (0 != pthread_cond_init(&queueEmpty, NULL))
	{
		error("scard_enum_devices: Can't initialize queue control cv\n");
		return 0;
	}

	if (0 != pthread_mutex_init(&hcardAccess, NULL))
	{
		error("scard_enum_devices: Can't initialize hcard list access mutex\n");
		return 0;
	}

	if (0 !=
	    pthread_create(&queueHandler, NULL, (void *(*)(void *)) queue_handler_function, NULL))
	{
		error("scard_enum_devices: Can't create queue handling Thread\n");
		return 0;
	}

	strncpy(g_rdpdr_device[*id].name, "SCARD\0\0\0", 8);
	toupper_str(g_rdpdr_device[*id].name);
	g_rdpdr_device[*id].local_path = "/dev/scard";
	g_rdpdr_device[*id].pdevice_data = NULL;
	g_rdpdr_device[*id].handle = 0;
	g_rdpdr_device[*id].device_type = DEVICE_TYPE_SCARD;
	count++;
	(*id)++;

	if (*optarg == ':')
	{
		while ((optarg = next_arg(name, ',')) && *id < RDPDR_MAX_DEVICES)
		{
			int len;
			char *vendor = NULL;
			alias = next_arg(name, '=');
			vendor = next_arg(alias, ';');

			if (strlen(name) > 0)
			{
				if (!strlen(alias))
				{
					alias = name;
					vendor = "\0";
				}

				printf("Static/aliased Device:\n");
				printf("  Lin name: [%s]\n", name);
				printf("  Win name: [%s]\n", alias);
				printf("  Vendor  : [%s]\n", vendor);
				nameMapCount++;

				if (nameMapList == NULL)
					nameMapList = xmalloc(nameMapCount * sizeof(TSCNameMapRec));
				else
					nameMapList =
						xrealloc(nameMapList,
							 nameMapCount * sizeof(TSCNameMapRec));

				tmpMap = nameMapList + nameMapCount - 1;

				len = strlen(alias);
				strncpy(tmpMap->alias, alias, (len > 127) ? (127) : (len));
				len = strlen(name);
				strncpy(tmpMap->name, name, (len > 127) ? (127) : (len));

				if (vendor)
				{
					len = strlen(vendor);
					if (len > 0)
					{
						memset(tmpMap->vendor, 0, 128);
						strncpy(tmpMap->vendor, vendor,
							(len > 127) ? (127) : (len));
					}
					else
						tmpMap->vendor[0] = '\0';
				}
				else
					tmpMap->vendor[0] = '\0';
			}
			name = optarg;
		}
	}

	return count;
}

#ifndef MAKE_PROTO
typedef struct _scard_handle_list_t
{
	struct _scard_handle_list_t *next;
	/* pcsc handles is datatype long which 
	   is arch sizedependent */
	long handle;
	/* rdp server handles are always 32bit */
	uint32_t server;
} _scard_handle_list_t;

static uint32_t g_scard_handle_counter = 0;
static _scard_handle_list_t *g_scard_handle_list = NULL;

static void _scard_handle_list_add(long handle);
static void _scard_handle_list_remove(long handle);
static uint32_t _scard_handle_list_get_server_handle(long handle);
static long _scard_handle_list_get_pcsc_handle(uint32_t server);

void
_scard_handle_list_add(long handle)
{
	_scard_handle_list_t *list = g_scard_handle_list;
	/* we dont care of order of list so to simplify the add 
	   we add new items to front of list */
	_scard_handle_list_t *item = xmalloc(sizeof(_scard_handle_list_t));
	item->next = list;
	item->handle = handle;

	/* lookup first unused handle id */
	int overlap = 0;
	if (g_scard_handle_counter == 0)
		g_scard_handle_counter++;

	while (_scard_handle_list_get_pcsc_handle(g_scard_handle_counter))
	{
		g_scard_handle_counter++;

		if (g_scard_handle_counter == 0 && overlap)
			assert(!"broken smartcard client software, handles are not freed and there is no more handles left to allocate.");

		if (g_scard_handle_counter == 0)
			overlap = g_scard_handle_counter = 1;

	}

	item->server = g_scard_handle_counter;
	g_scard_handle_list = item;
}

void
_scard_handle_list_remove(long handle)
{
	_scard_handle_list_t *item, *list, *prev_item;
	prev_item = NULL;
	item = list = g_scard_handle_list;

	while (item)
	{
		if (item->handle == handle)
		{
			/* unlink from list */
			if (prev_item)
				prev_item->next = item->next;
			else
				g_scard_handle_list = item->next;

			xfree(item);
			break;
		}

		/* store previous item for relinking */
		prev_item = item;
		item = item->next;
	}
}

uint32_t
_scard_handle_list_get_server_handle(long handle)
{
	_scard_handle_list_t *item;
	item = g_scard_handle_list;
	while (item)
	{
		if (item->handle == handle)
			return item->server;
		item = item->next;
	}
	return 0;
}

long
_scard_handle_list_get_pcsc_handle(uint32_t server)
{
	_scard_handle_list_t *item;
	item = g_scard_handle_list;
	while (item)
	{
		if (item->server == server)
			return item->handle;
		item = item->next;
	}
	return 0;
}

static void *
SC_xmalloc(PMEM_HANDLE * memHandle, unsigned int size)
{
	PMEM_HANDLE handle = NULL;
	if (size > 0 && memHandle)
	{
		handle = xmalloc(size + sizeof(MEM_HANDLE));
		if (handle)
		{
			handle->prevHandle = NULL;
			handle->nextHandle = NULL;
			handle->dataSize = size;
			if (*memHandle)
			{
				handle->prevHandle = *memHandle;
				(*memHandle)->nextHandle = handle;
			}
			*memHandle = handle;
			return handle + 1;
		}
		else
			return NULL;
	}
	else
		return NULL;
}

static void
SC_xfree(PMEM_HANDLE * handle, void *memptr)
{
	if (memptr != NULL)
	{
		PMEM_HANDLE lcHandle = (PMEM_HANDLE) memptr - 1;
		if (lcHandle->dataSize > 0)
		{
			memset(memptr, 0, lcHandle->dataSize);
			if (lcHandle->nextHandle)
				lcHandle->nextHandle->prevHandle = lcHandle->prevHandle;
			if (lcHandle->prevHandle)
				lcHandle->prevHandle->nextHandle = lcHandle->nextHandle;
			if (*handle == lcHandle)
			{
				if (lcHandle->prevHandle)
					*handle = lcHandle->prevHandle;
				else
					*handle = lcHandle->nextHandle;
			}
			xfree(lcHandle);
		}
	}
}

static void
SC_xfreeallmemory(PMEM_HANDLE * handle)
{
	if (handle && (*handle))
	{
		if ((*handle)->prevHandle)
		{
			(*handle)->prevHandle->nextHandle = NULL;
			SC_xfreeallmemory(&((*handle)->prevHandle));
		}
		if ((*handle)->nextHandle)
		{
			(*handle)->nextHandle->prevHandle = NULL;
			SC_xfreeallmemory(&((*handle)->nextHandle));
		}
		memset(*handle, 0, (*handle)->dataSize + sizeof(MEM_HANDLE));
		xfree(*handle);
		*handle = NULL;
	}
}

/* ---------------------------------- */

static char *
getName(char *alias)
{
	int i;
	PSCNameMapRec tmpMap;
	for (i = 0, tmpMap = nameMapList; i < nameMapCount; i++, tmpMap++)
	{
		if (strcmp(tmpMap->alias, alias) == 0)
			return tmpMap->name;
	}
	return alias;
}

static char *
getVendor(char *name)
{
	int i;
	PSCNameMapRec tmpMap;
	for (i = 0, tmpMap = nameMapList; i < nameMapCount; i++, tmpMap++)
	{
		if (strcmp(tmpMap->name, name) == 0)
			return tmpMap->vendor;
	}
	return NULL;
}


static char *
getAlias(char *name)
{
	int i;
	PSCNameMapRec tmpMap;
	for (i = 0, tmpMap = nameMapList; i < nameMapCount; i++, tmpMap++)
	{
		if (strcmp(tmpMap->name, name) == 0)
			return tmpMap->alias;
	}
	return name;
}

static int
hasAlias(char *name)
{
	int i;
	PSCNameMapRec tmpMap;
	for (i = 0, tmpMap = nameMapList; i < nameMapCount; i++, tmpMap++)
	{
		if (strcmp(tmpMap->name, name) == 0)
			return 1;
	}
	return 0;
}

static void
inRepos(STREAM in, unsigned int read)
{
	SERVER_DWORD add = 4 - read % 4;
	if (add < 4 && add > 0)
	{
		in_uint8s(in, add);
	}
}

static void
outRepos(STREAM out, unsigned int written)
{
	SERVER_DWORD add = (4 - written % 4) % 4;
	if (add > 0)
	{
		out_uint8s(out, add);
	}
}


static void
outBufferStartWithLimit(STREAM out, int length, int highLimit)
{
	int header = (length < 0) ? (0) : ((length > highLimit) ? (highLimit) : (length));
	out_uint32_le(out, header);
	out_uint32_le(out, 0x00000001);	/* Magic DWORD - any non zero */
}


static void
outBufferStart(STREAM out, int length)
{
	outBufferStartWithLimit(out, length, 0x7FFFFFFF);
}

static void
outBufferFinishWithLimit(STREAM out, char *buffer, unsigned int length, unsigned int highLimit)
{
	int header = (length < 0) ? (0) : ((length > highLimit) ? (highLimit) : (length));
	out_uint32_le(out, header);

	if (length <= 0)
	{
		out_uint32_le(out, 0x00000000);
	}
	else
	{
		if (header < length)
			length = header;
		out_uint8p(out, buffer, length);
		outRepos(out, length);
	}
}

static void
outBufferFinish(STREAM out, char *buffer, unsigned int length)
{
	outBufferFinishWithLimit(out, buffer, length, 0x7FFFFFFF);
}

static void
outForceAlignment(STREAM out, unsigned int seed)
{
	SERVER_DWORD add = (seed - (out->p - out->data) % seed) % seed;
	if (add > 0)
		out_uint8s(out, add);
}

static unsigned int
inString(PMEM_HANDLE * handle, STREAM in, char **destination, SERVER_DWORD dataLength, RD_BOOL wide)
{
	unsigned int Result = (wide) ? (2 * dataLength) : (dataLength);
	PMEM_HANDLE lcHandle = NULL;
	char *buffer = SC_xmalloc(&lcHandle, Result + 2);
	char *reader;

	/* code segment */

	if (wide)
	{
		int i;
		in_uint8a(in, buffer, 2 * dataLength);
		for (i = 0; i < dataLength; i++)
			if ((buffer[2 * i] < 0) || (buffer[2 * i + 1] != 0))
				buffer[i] = '?';
			else
				buffer[i] = buffer[2 * i];
	}
	else
	{
		in_uint8a(in, buffer, dataLength);
	}

	buffer[dataLength] = '\0';
	reader = getName(buffer);
	*destination = SC_xmalloc(handle, strlen(reader) + 1);
	strcpy(*destination, reader);

	SC_xfreeallmemory(&lcHandle);
	return Result;
}

static unsigned int
outString(STREAM out, char *source, RD_BOOL wide)
{
	PMEM_HANDLE lcHandle = NULL;
	char *reader = getAlias(source);
	unsigned int dataLength = strlen(reader) + 1;
	unsigned int Result = (wide) ? (2 * dataLength) : (dataLength);

	/* code segment */

	if (wide)
	{
		int i;
		char *buffer = SC_xmalloc(&lcHandle, Result);

		for (i = 0; i < dataLength; i++)
		{
			if (source[i] < 0)
				buffer[2 * i] = '?';
			else
				buffer[2 * i] = reader[i];
			buffer[2 * i + 1] = '\0';
		}
		out_uint8p(out, buffer, 2 * dataLength);
	}
	else
	{
		out_uint8p(out, reader, dataLength);
	}

	SC_xfreeallmemory(&lcHandle);
	return Result;
}

static void
inReaderName(PMEM_HANDLE * handle, STREAM in, char **destination, RD_BOOL wide)
{
	SERVER_DWORD dataLength;
	in->p += 0x08;
	in_uint32_le(in, dataLength);
	inRepos(in, inString(handle, in, destination, dataLength, wide));
}

static void
inSkipLinked(STREAM in)
{
	SERVER_DWORD len;
	in_uint32_le(in, len);
	if (len > 0)
	{
		in_uint8s(in, len);
		inRepos(in, len);
	}
}

/* ---------------------------------- */
/*  Smart Card processing functions:  */
/* ---------------------------------- */

static MYPCSC_DWORD
SC_returnCode(MYPCSC_DWORD rc, PMEM_HANDLE * handle, STREAM in, STREAM out)
{
	SC_xfreeallmemory(handle);
	out_uint8s(out, 256);
	return rc;
}

static MYPCSC_DWORD
SC_returnNoMemoryError(PMEM_HANDLE * handle, STREAM in, STREAM out)
{
	return SC_returnCode(SCARD_E_NO_MEMORY, handle, in, out);
}

static MYPCSC_DWORD
TS_SCardEstablishContext(STREAM in, STREAM out)
{
	MYPCSC_DWORD rv;
	MYPCSC_SCARDCONTEXT myHContext;
	SERVER_SCARDCONTEXT hContext;

	/* code segment  */

	DEBUG_SCARD(("SCARD: SCardEstablishContext()\n"));
	rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &myHContext);

	hContext = 0;
	if (myHContext)
	{
		_scard_handle_list_add(myHContext);
		hContext = _scard_handle_list_get_server_handle(myHContext);
	}


	if (rv)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
	}
	else
	{
		DEBUG_SCARD(("SCARD: -> Success (context: 0x%08x [0x%lx])\n", hContext,
			     myHContext));
	}



	out_uint32_le(out, 0x00000004);
	out_uint32_le(out, hContext);	/* must not be 0 (Seems to be pointer), don't know what is this (I use hContext as value) */
	/* i hope it's not a pointer because i just downcasted it - jlj */
	out_uint32_le(out, 0x00000004);
	out_uint32_le(out, hContext);
	outForceAlignment(out, 8);
	return rv;
}

static MYPCSC_DWORD
TS_SCardReleaseContext(STREAM in, STREAM out)
{
	MYPCSC_DWORD rv;
	MYPCSC_SCARDCONTEXT myHContext;
	SERVER_SCARDCONTEXT hContext;

	in->p += 0x1C;
	in_uint32_le(in, hContext);
	myHContext = _scard_handle_list_get_pcsc_handle(hContext);

	DEBUG_SCARD(("SCARD: SCardReleaseContext(context: 0x%08x [0x%lx])\n", (unsigned) hContext,
		     myHContext));

	rv = SCardReleaseContext(myHContext);

	_scard_handle_list_remove(myHContext);

	if (rv)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
	}
	else
	{
		DEBUG_SCARD(("SCARD: -> Success\n"));
	}

	outForceAlignment(out, 8);
	return rv;
}

static MYPCSC_DWORD
TS_SCardIsValidContext(STREAM in, STREAM out)
{
	MYPCSC_DWORD rv;
	SERVER_SCARDCONTEXT hContext;
	MYPCSC_SCARDCONTEXT myHContext;
	char *readers;
	DWORD readerCount = 1024;
	PMEM_HANDLE lcHandle = NULL;

	in->p += 0x1C;
	in_uint32_le(in, hContext);

	myHContext = _scard_handle_list_get_pcsc_handle(hContext);

	DEBUG_SCARD(("SCARD: SCardIsValidContext(context: 0x%08x [0x%lx])\n",
		     (unsigned) hContext, myHContext));
	/* There is no realization of SCardIsValidContext in PC/SC Lite so we call SCardListReaders */

	readers = SC_xmalloc(&lcHandle, 1024);
	if (!readers)
		return SC_returnNoMemoryError(&lcHandle, in, out);

	rv = SCardListReaders(myHContext, NULL, readers, &readerCount);

	if (rv)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
		rv = SCARD_E_INVALID_HANDLE;
	}
	else
	{
		DEBUG_SCARD(("SCARD: -> Success\n"));
	}

	outForceAlignment(out, 8);
	SC_xfreeallmemory(&lcHandle);
	return rv;
}


static MYPCSC_DWORD
TS_SCardListReaders(STREAM in, STREAM out, RD_BOOL wide)
{
#define readerArraySize 1024
	MYPCSC_DWORD rv;
	SERVER_SCARDCONTEXT hContext;
	MYPCSC_SCARDCONTEXT myHContext;
	SERVER_DWORD dataLength;
	MYPCSC_DWORD cchReaders = readerArraySize;
	unsigned char *plen1, *plen2, *pend;
	char *readers, *cur;
	PMEM_HANDLE lcHandle = NULL;

	in->p += 0x2C;
	in_uint32_le(in, hContext);
	myHContext = _scard_handle_list_get_pcsc_handle(hContext);
	DEBUG_SCARD(("SCARD: SCardListReaders(context: 0x%08x [0x%lx])\n",
		     (unsigned) hContext, myHContext));
	plen1 = out->p;
	out_uint32_le(out, 0x00000000);	/* Temp value for data length as 0x0 */
	out_uint32_le(out, 0x01760650);
	plen2 = out->p;
	out_uint32_le(out, 0x00000000);	/* Temp value for data length as 0x0 */

	dataLength = 0;
	readers = SC_xmalloc(&lcHandle, readerArraySize);
	if (!readers)
		return SC_returnNoMemoryError(&lcHandle, in, out);


	readers[0] = '\0';
	readers[1] = '\0';
	rv = SCardListReaders(myHContext, NULL, readers, &cchReaders);
	cur = readers;
	if (rv != SCARD_S_SUCCESS)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
	}
	else
	{
		int i;
		PSCNameMapRec tmpMap;
		DEBUG_SCARD(("SCARD: -> Success\n"));
		for (i = 0, tmpMap = nameMapList; i < nameMapCount; i++, tmpMap++)
		{
			dataLength += outString(out, tmpMap->alias, wide);
		}

		int lenSC = strlen(cur);
		if (lenSC == 0)
			dataLength += outString(out, "\0", wide);
		else
			while (lenSC > 0)
			{
				if (!hasAlias(cur))
				{
					DEBUG_SCARD(("SCARD:    \"%s\"\n", cur));
					dataLength += outString(out, cur, wide);
				}
				cur = (void *) ((unsigned char *) cur + lenSC + 1);
				lenSC = strlen(cur);
			}
	}

	dataLength += outString(out, "\0", wide);
	outRepos(out, dataLength);

	pend = out->p;
	out->p = plen1;
	out_uint32_le(out, dataLength);
	out->p = plen2;
	out_uint32_le(out, dataLength);
	out->p = pend;

	outForceAlignment(out, 8);
	SC_xfreeallmemory(&lcHandle);
	return rv;
}


static MYPCSC_DWORD
TS_SCardConnect(STREAM in, STREAM out, RD_BOOL wide)
{
	MYPCSC_DWORD rv;
	SCARDCONTEXT myHContext;
	SERVER_SCARDCONTEXT hContext;
	char *szReader;
	SERVER_DWORD dwShareMode;
	SERVER_DWORD dwPreferredProtocol;
	MYPCSC_SCARDHANDLE myHCard;
	SERVER_SCARDHANDLE hCard;

	MYPCSC_DWORD dwActiveProtocol;
	PMEM_HANDLE lcHandle = NULL;

	in->p += 0x1C;
	in_uint32_le(in, dwShareMode);
	in_uint32_le(in, dwPreferredProtocol);
	inReaderName(&lcHandle, in, &szReader, wide);
	in->p += 0x04;
	in_uint32_le(in, hContext);

	myHContext = _scard_handle_list_get_pcsc_handle(hContext);

	DEBUG_SCARD(("SCARD: SCardConnect(context: 0x%08x [0x%lx], share: 0x%08x, proto: 0x%08x, reader: \"%s\")\n", (unsigned) hContext, myHContext, (unsigned) dwShareMode, (unsigned) dwPreferredProtocol, szReader ? szReader : "NULL"));

	rv = SCardConnect(myHContext, szReader, (MYPCSC_DWORD) dwShareMode,
			  (MYPCSC_DWORD) dwPreferredProtocol, &myHCard, &dwActiveProtocol);

	hCard = 0;
	if (myHCard)
	{
		_scard_handle_list_add(myHCard);
		hCard = _scard_handle_list_get_server_handle(myHCard);
	}

	if (rv != SCARD_S_SUCCESS)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
	}
	else
	{
		char *szVendor = getVendor(szReader);
		DEBUG_SCARD(("SCARD: -> Success (hcard: 0x%08x [0x%lx])\n",
			     (unsigned) hCard, myHCard));
		if (szVendor && (strlen(szVendor) > 0))
		{
			DEBUG_SCARD(("SCARD: Set Attribute ATTR_VENDOR_NAME\n"));
			pthread_mutex_lock(&hcardAccess);
			PSCHCardRec hcard = xmalloc(sizeof(TSCHCardRec));
			if (hcard)
			{
				hcard->hCard = hCard;
				hcard->vendor = szVendor;
				hcard->next = NULL;
				hcard->prev = NULL;

				if (hcardFirst)
				{
					hcardFirst->prev = hcard;
					hcard->next = hcardFirst;
				}
				hcardFirst = hcard;
			}
			pthread_mutex_unlock(&hcardAccess);
		}
	}

	out_uint32_le(out, 0x00000000);
	out_uint32_le(out, 0x00000000);
	out_uint32_le(out, 0x00000004);
	out_uint32_le(out, 0x016Cff34);
	/* if the active protocol > 4 billion, this is trouble. odds are low */
	out_uint32_le(out, (SERVER_DWORD) dwActiveProtocol);
	out_uint32_le(out, 0x00000004);
	out_uint32_le(out, hCard);

	outForceAlignment(out, 8);
	SC_xfreeallmemory(&lcHandle);
	return rv;
}

static MYPCSC_DWORD
TS_SCardReconnect(STREAM in, STREAM out)
{
	MYPCSC_DWORD rv;
	SERVER_SCARDCONTEXT hContext;
	SERVER_SCARDHANDLE hCard;
	MYPCSC_SCARDHANDLE myHCard;
	SERVER_DWORD dwShareMode;
	SERVER_DWORD dwPreferredProtocol;
	SERVER_DWORD dwInitialization;
	MYPCSC_DWORD dwActiveProtocol;

	in->p += 0x20;
	in_uint32_le(in, dwShareMode);
	in_uint32_le(in, dwPreferredProtocol);
	in_uint32_le(in, dwInitialization);
	in->p += 0x04;
	in_uint32_le(in, hContext);
	in->p += 0x04;
	in_uint32_le(in, hCard);


	myHCard = _scard_handle_list_get_pcsc_handle(hCard);
	DEBUG_SCARD(("SCARD: SCardReconnect(context: 0x%08x, hcard: 0x%08x [%lx], share: 0x%08x, proto: 0x%08x, init: 0x%08x)\n", (unsigned) hContext, (unsigned) hCard, myHCard, (unsigned) dwShareMode, (unsigned) dwPreferredProtocol, (unsigned) dwInitialization));
	rv = SCardReconnect(myHCard, (MYPCSC_DWORD) dwShareMode, (MYPCSC_DWORD) dwPreferredProtocol,
			    (MYPCSC_DWORD) dwInitialization, &dwActiveProtocol);
	if (rv != SCARD_S_SUCCESS)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
	}
	else
	{
		DEBUG_SCARD(("SCARD: -> Success (proto: 0x%08x)\n", (unsigned) dwActiveProtocol));
	}

	out_uint32_le(out, (SERVER_DWORD) dwActiveProtocol);
	outForceAlignment(out, 8);
	return rv;
}

static MYPCSC_DWORD
TS_SCardDisconnect(STREAM in, STREAM out)
{
	MYPCSC_DWORD rv;
	SERVER_SCARDCONTEXT hContext;
	MYPCSC_SCARDCONTEXT myHContext;
	SERVER_SCARDHANDLE hCard;
	MYPCSC_SCARDHANDLE myHCard;
	SERVER_DWORD dwDisposition;

	in->p += 0x20;
	in_uint32_le(in, dwDisposition);
	in->p += 0x04;
	in_uint32_le(in, hContext);
	in->p += 0x04;
	in_uint32_le(in, hCard);

	myHContext = _scard_handle_list_get_pcsc_handle(hContext);
	myHCard = _scard_handle_list_get_pcsc_handle(hCard);

	DEBUG_SCARD(("SCARD: SCardDisconnect(context: 0x%08x [0x%lx], hcard: 0x%08x [0x%lx], disposition: 0x%08x)\n", (unsigned) hContext, myHContext, (unsigned) hCard, myHCard, (unsigned) dwDisposition));

	pthread_mutex_lock(&hcardAccess);
	PSCHCardRec hcard = hcardFirst;
	while (hcard)
	{
		if (hcard->hCard == hCard)
		{
			if (hcard->prev)
				hcard->prev->next = hcard->next;
			if (hcard->next)
				hcard->next->prev = hcard->prev;
			if (hcardFirst == hcard)
				hcardFirst = hcard->next;
			xfree(hcard);
			break;
		}
		hcard = hcard->next;
	}
	pthread_mutex_unlock(&hcardAccess);

	rv = SCardDisconnect(myHCard, (MYPCSC_DWORD) dwDisposition);

	_scard_handle_list_remove(myHCard);

	if (rv != SCARD_S_SUCCESS)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
	}
	else
	{
		DEBUG_SCARD(("SCARD: -> Success\n"));
	}

	outForceAlignment(out, 8);
	return rv;
}

/* Currently unused */
#if 0
static int
needStatusRecheck(MYPCSC_DWORD rv, MYPCSC_LPSCARD_READERSTATE_A rsArray, SERVER_DWORD dwCount)
{
	int i, recall = 0;
	if (rv == SCARD_S_SUCCESS)
	{
		MYPCSC_LPSCARD_READERSTATE_A cur;
		for (i = 0, cur = rsArray; i < dwCount; i++, cur++)
		{
			if (cur->dwEventState & SCARD_STATE_UNKNOWN)
			{
				cur->dwCurrentState = cur->dwEventState;
				recall++;
			}
		}
	}
	return recall;
}

static RD_BOOL
mappedStatus(MYPCSC_DWORD code)
{
	code >>= 16;
	code &= 0x0000FFFF;
	return (code % 2);
}
#endif

static void
copyReaderState_MyPCSCToServer(MYPCSC_LPSCARD_READERSTATE_A src, SERVER_LPSCARD_READERSTATE_A dst,
			       MYPCSC_DWORD readerCount)
{
	MYPCSC_LPSCARD_READERSTATE_A srcIter;
	SERVER_LPSCARD_READERSTATE_A dstIter;
	MYPCSC_DWORD i;

	for (i = 0, srcIter = src, dstIter = dst; i < readerCount; i++, srcIter++, dstIter++)
	{
		dstIter->szReader = srcIter->szReader;
		dstIter->pvUserData = srcIter->pvUserData;
		dstIter->dwCurrentState = srcIter->dwCurrentState;
		dstIter->dwEventState = srcIter->dwEventState;
		dstIter->cbAtr = srcIter->cbAtr;
		memcpy(dstIter->rgbAtr, srcIter->rgbAtr, MAX_ATR_SIZE * sizeof(unsigned char));
	}
}

static void
copyReaderState_ServerToMyPCSC(SERVER_LPSCARD_READERSTATE_A src, MYPCSC_LPSCARD_READERSTATE_A dst,
			       SERVER_DWORD readerCount)
{
	SERVER_LPSCARD_READERSTATE_A srcIter;
	MYPCSC_LPSCARD_READERSTATE_A dstIter;
	SERVER_DWORD i;

	for (i = 0, srcIter = src, dstIter = dst; i < readerCount; i++, srcIter++, dstIter++)
	{
		dstIter->szReader = srcIter->szReader;
		dstIter->pvUserData = srcIter->pvUserData;
		dstIter->dwCurrentState = srcIter->dwCurrentState;
		dstIter->dwEventState = srcIter->dwEventState;
		dstIter->cbAtr = srcIter->cbAtr;
		memcpy(dstIter->rgbAtr, srcIter->rgbAtr, MAX_ATR_SIZE * sizeof(unsigned char));
	}
}


static MYPCSC_DWORD
TS_SCardGetStatusChange(STREAM in, STREAM out, RD_BOOL wide)
{
	MYPCSC_DWORD rv;
	SERVER_SCARDCONTEXT hContext;
	MYPCSC_SCARDCONTEXT myHContext;
	SERVER_DWORD dwTimeout;
	SERVER_DWORD dwCount;
	SERVER_LPSCARD_READERSTATE_A rsArray, cur;
	MYPCSC_LPSCARD_READERSTATE_A myRsArray;
	long i;
	PMEM_HANDLE lcHandle = NULL;

	in->p += 0x18;
	in_uint32_le(in, dwTimeout);
	in_uint32_le(in, dwCount);
	in->p += 0x08;
	in_uint32_le(in, hContext);
	in->p += 0x04;

	myHContext = _scard_handle_list_get_pcsc_handle(hContext);

	DEBUG_SCARD(("SCARD: SCardGetStatusChange(context: 0x%08x [0x%lx], timeout: 0x%08x, count: %d)\n", (unsigned) hContext, myHContext, (unsigned) dwTimeout, (int) dwCount));

	if (dwCount > 0)
	{
		rsArray = SC_xmalloc(&lcHandle, dwCount * sizeof(SERVER_SCARD_READERSTATE_A));
		if (!rsArray)
			return SC_returnNoMemoryError(&lcHandle, in, out);
		memset(rsArray, 0, dwCount * sizeof(SERVER_SCARD_READERSTATE_A));
		/* skip two pointers at beginning of struct */
		for (i = 0, cur = (SERVER_LPSCARD_READERSTATE_A) ((unsigned char **) rsArray + 2);
		     i < dwCount; i++, cur++)
		{
			in->p += 0x04;
			in_uint8a(in, cur, SERVER_SCARDSTATESIZE);
		}

		for (i = 0, cur = rsArray; i < dwCount; i++, cur++)
		{
			SERVER_DWORD dataLength;

			/* Do endian swaps... */
			cur->dwCurrentState = swap32(cur->dwCurrentState);
			cur->dwEventState = swap32(cur->dwEventState);
			cur->cbAtr = swap32(cur->cbAtr);

			in->p += 0x08;
			in_uint32_le(in, dataLength);
			inRepos(in,
				inString(&lcHandle, in, (char **) &(cur->szReader), dataLength,
					 wide));

			if (strcmp(cur->szReader, "\\\\?PnP?\\Notification") == 0)
				cur->dwCurrentState |= SCARD_STATE_IGNORE;

			DEBUG_SCARD(("SCARD:    \"%s\"\n", cur->szReader ? cur->szReader : "NULL"));
			DEBUG_SCARD(("SCARD:        user: %p, state: 0x%08x, event: 0x%08x\n",
				     cur->pvUserData, (unsigned) cur->dwCurrentState,
				     (unsigned) cur->dwEventState));
		}
	}
	else
	{
		rsArray = NULL;
	}

	myRsArray = SC_xmalloc(&lcHandle, dwCount * sizeof(MYPCSC_SCARD_READERSTATE_A));
	if (!myRsArray)
		return SC_returnNoMemoryError(&lcHandle, in, out);
	memset(myRsArray, 0, dwCount * sizeof(SERVER_SCARD_READERSTATE_A));
	copyReaderState_ServerToMyPCSC(rsArray, myRsArray, (SERVER_DWORD) dwCount);

	rv = SCardGetStatusChange(myHContext, (MYPCSC_DWORD) dwTimeout,
				  myRsArray, (MYPCSC_DWORD) dwCount);
	copyReaderState_MyPCSCToServer(myRsArray, rsArray, (MYPCSC_DWORD) dwCount);

	if (rv != SCARD_S_SUCCESS)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
	}
	else
	{
		DEBUG_SCARD(("SCARD: -> Success\n"));
	}

	out_uint32_le(out, dwCount);
	out_uint32_le(out, 0x00084dd8);
	out_uint32_le(out, dwCount);

	for (i = 0, cur = rsArray; i < dwCount; i++, cur++)
	{
		DEBUG_SCARD(("SCARD:    \"%s\"\n", cur->szReader ? cur->szReader : "NULL"));
		DEBUG_SCARD(("SCARD:        user: %p, state: 0x%08x, event: 0x%08x\n",
			     cur->pvUserData, (unsigned) cur->dwCurrentState,
			     (unsigned) cur->dwEventState));

		/* Do endian swaps... */
		cur->dwCurrentState = swap32(cur->dwCurrentState);
		cur->dwEventState = swap32(cur->dwEventState);
		cur->cbAtr = swap32(cur->cbAtr);

		out_uint8p(out, (void *) ((unsigned char **) cur + 2),
			   sizeof(SERVER_SCARD_READERSTATE_A) - 2 * sizeof(unsigned char *));
	}
	outForceAlignment(out, 8);
	SC_xfreeallmemory(&lcHandle);
	return rv;
}

static MYPCSC_DWORD
TS_SCardCancel(STREAM in, STREAM out)
{
	MYPCSC_DWORD rv;
	SERVER_SCARDCONTEXT hContext;
	MYPCSC_SCARDCONTEXT myHContext;

	in->p += 0x1C;
	in_uint32_le(in, hContext);

	myHContext = _scard_handle_list_get_pcsc_handle(hContext);

	DEBUG_SCARD(("SCARD: SCardCancel(context: 0x%08x [0x%08lx])\n", (unsigned) hContext,
		     (unsigned long) myHContext));
	rv = SCardCancel(myHContext);
	if (rv != SCARD_S_SUCCESS)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
	}
	else
	{
		DEBUG_SCARD(("SCARD: -> Success\n"));
	}
	outForceAlignment(out, 8);
	return rv;
}

static MYPCSC_DWORD
TS_SCardLocateCardsByATR(STREAM in, STREAM out, RD_BOOL wide)
{
	int i, j, k;
	MYPCSC_DWORD rv;
	SERVER_SCARDCONTEXT hContext;
	MYPCSC_SCARDCONTEXT myHContext;

	/* The SCARD_ATRMASK_L struct doesn't contain any longs or DWORDs -
	   no need to split into SERVER_ and MYPCSC_ */
	LPSCARD_ATRMASK_L pAtrMasks, cur;
	SERVER_DWORD atrMaskCount = 0;
	SERVER_DWORD readerCount = 0;
	SERVER_LPSCARD_READERSTATE_A rsArray, ResArray, rsCur;
	MYPCSC_LPSCARD_READERSTATE_A myRsArray;
	PMEM_HANDLE lcHandle = NULL;

	in->p += 0x2C;
	in_uint32_le(in, hContext);
	in_uint32_le(in, atrMaskCount);
	pAtrMasks = SC_xmalloc(&lcHandle, atrMaskCount * sizeof(SCARD_ATRMASK_L));
	if (!pAtrMasks)
		return SC_returnNoMemoryError(&lcHandle, in, out);
	in_uint8a(in, pAtrMasks, atrMaskCount * sizeof(SCARD_ATRMASK_L));

	in_uint32_le(in, readerCount);
	rsArray = SC_xmalloc(&lcHandle, readerCount * sizeof(SCARD_READERSTATE));
	if (!rsArray)
		return SC_returnNoMemoryError(&lcHandle, in, out);
	memset(rsArray, 0, readerCount * sizeof(SCARD_READERSTATE));

	myHContext = _scard_handle_list_get_pcsc_handle(hContext);

	DEBUG_SCARD(("SCARD: SCardLocateCardsByATR(context: 0x%08x [0x%08lx], atrs: %d, readers: %d)\n", (unsigned) hContext, (unsigned long) myHContext, (int) atrMaskCount, (int) readerCount));

	for (i = 0, cur = pAtrMasks; i < atrMaskCount; i++, cur++)
	{
		cur->cbAtr = swap32(cur->cbAtr);

		DEBUG_SCARD(("SCARD:    ATR: "));
		for (j = 0; j < pAtrMasks->cbAtr; j++)
		{
		DEBUG_SCARD(("%02x%c",
				     (unsigned) (unsigned char) cur->rgbAtr[j],
				     (j == pAtrMasks->cbAtr - 1) ? ' ' : ':'))}
		DEBUG_SCARD(("\n"));
		DEBUG_SCARD(("SCARD:         "));
		for (j = 0; j < pAtrMasks->cbAtr; j++)
		{
		DEBUG_SCARD(("%02x%c",
				     (unsigned) (unsigned char) cur->rgbMask[j],
				     (j == pAtrMasks->cbAtr - 1) ? ' ' : ':'))}
		DEBUG_SCARD(("\n"));
	}

	for (i = 0, rsCur = (SERVER_LPSCARD_READERSTATE_A) ((unsigned char **) rsArray + 2);
	     i < readerCount; i++, rsCur++)
	{
		in_uint8s(in, 4);
		in_uint8a(in, rsCur, SERVER_SCARDSTATESIZE);
	}

	ResArray = SC_xmalloc(&lcHandle, readerCount * sizeof(SERVER_SCARD_READERSTATE_A));
	if (!ResArray)
		return SC_returnNoMemoryError(&lcHandle, in, out);

	for (i = 0, rsCur = rsArray; i < readerCount; i++, rsCur++)
	{
		/* Do endian swaps... */
		rsCur->dwCurrentState = swap32(rsCur->dwCurrentState);
		rsCur->dwEventState = swap32(rsCur->dwEventState);
		rsCur->cbAtr = swap32(rsCur->cbAtr);

		inReaderName(&lcHandle, in, (char **) &rsCur->szReader, wide);
		DEBUG_SCARD(("SCARD:    \"%s\"\n", rsCur->szReader ? rsCur->szReader : "NULL"));
		DEBUG_SCARD(("SCARD:        user: %p, state: 0x%08x, event: 0x%08x\n",
			     rsCur->pvUserData, (unsigned) rsCur->dwCurrentState,
			     (unsigned) rsCur->dwEventState));
	}
	memcpy(ResArray, rsArray, readerCount * sizeof(SERVER_SCARD_READERSTATE_A));

	/* FIXME segfault here. */
	myRsArray = SC_xmalloc(&lcHandle, readerCount * sizeof(MYPCSC_SCARD_READERSTATE_A));
	if (!myRsArray)
		return SC_returnNoMemoryError(&lcHandle, in, out);
	copyReaderState_ServerToMyPCSC(rsArray, myRsArray, readerCount);
	rv = SCardGetStatusChange(myHContext, 0x00000001, myRsArray, readerCount);
	copyReaderState_MyPCSCToServer(myRsArray, rsArray, readerCount);
	if (rv != SCARD_S_SUCCESS)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
	}
	else
	{
		DEBUG_SCARD(("SCARD: -> Success\n"));
		cur = pAtrMasks;
		for (i = 0, cur = pAtrMasks; i < atrMaskCount; i++, cur++)
		{
			for (j = 0, rsCur = rsArray; j < readerCount; j++, rsCur++)
			{
				RD_BOOL equal = 1;
				for (k = 0; k < cur->cbAtr; k++)
				{
					if ((cur->rgbAtr[k] & cur->rgbMask[k]) !=
					    (rsCur->rgbAtr[k] & cur->rgbMask[k]))
					{
						equal = 0;
						break;
					}
				}
				if (equal)
				{
					rsCur->dwEventState |= 0x00000040;	/* SCARD_STATE_ATRMATCH 0x00000040 */
					memcpy(ResArray + j, rsCur, sizeof(SCARD_READERSTATE));
					DEBUG_SCARD(("SCARD:    \"%s\"\n",
						     rsCur->szReader ? rsCur->szReader : "NULL"));
					DEBUG_SCARD(("SCARD:        user: %p, state: 0x%08x, event: 0x%08x\n", rsCur->pvUserData, (unsigned) rsCur->dwCurrentState, (unsigned) rsCur->dwEventState));
				}
			}
		}
	}

	out_uint32_le(out, readerCount);
	out_uint32_le(out, 0x00084dd8);
	out_uint32_le(out, readerCount);

	for (i = 0, rsCur = ResArray; i < readerCount; i++, rsCur++)
	{
		/* Do endian swaps... */
		rsCur->dwCurrentState = swap32(rsCur->dwCurrentState);
		rsCur->dwEventState = swap32(rsCur->dwEventState);
		rsCur->cbAtr = swap32(rsCur->cbAtr);

		out_uint8p(out, (void *) ((unsigned char **) rsCur + 2),
			   sizeof(SCARD_READERSTATE) - 2 * sizeof(unsigned char *));
	}

	outForceAlignment(out, 8);
	SC_xfreeallmemory(&lcHandle);
	return rv;
}

static DWORD
TS_SCardBeginTransaction(STREAM in, STREAM out)
{
	MYPCSC_DWORD rv;
	SERVER_SCARDCONTEXT hCard;
	MYPCSC_SCARDCONTEXT myHCard;

	in->p += 0x30;
	in_uint32_le(in, hCard);
	myHCard = _scard_handle_list_get_pcsc_handle(hCard);
	DEBUG_SCARD(("SCARD: SCardBeginTransaction(hcard: 0x%08x [0x%lx])\n",
		     (unsigned) hCard, myHCard));
	rv = SCardBeginTransaction(myHCard);
	if (rv != SCARD_S_SUCCESS)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
	}
	else
	{
		DEBUG_SCARD(("SCARD: -> Success\n"));
	}
	outForceAlignment(out, 8);
	return rv;
}

static DWORD
TS_SCardEndTransaction(STREAM in, STREAM out)
{
	MYPCSC_DWORD rv;
	SERVER_SCARDCONTEXT hCard;
	MYPCSC_SCARDCONTEXT myHCard;
	SERVER_DWORD dwDisposition = 0;

	in->p += 0x20;
	in_uint32_le(in, dwDisposition);
	in->p += 0x0C;
	in_uint32_le(in, hCard);

	myHCard = _scard_handle_list_get_pcsc_handle(hCard);

	DEBUG_SCARD(("SCARD: SCardEndTransaction(hcard: 0x%08x [0x%lx], disposition: 0x%08x)\n",
		     (unsigned) hCard, (unsigned long) myHCard, (unsigned) dwDisposition));
	rv = SCardEndTransaction(myHCard, (MYPCSC_DWORD) dwDisposition);
	if (rv != SCARD_S_SUCCESS)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
	}
	else
	{
		DEBUG_SCARD(("SCARD: -> Success\n"));
	}
	outForceAlignment(out, 8);
	return rv;
}


static void
copyIORequest_MyPCSCToServer(MYPCSC_LPSCARD_IO_REQUEST src, SERVER_LPSCARD_IO_REQUEST dst)
{
	unsigned char *srcBytes, *dstBytes;
	size_t bytesToCopy = src->cbPciLength - sizeof(MYPCSC_SCARD_IO_REQUEST);
	srcBytes = ((unsigned char *) src + sizeof(MYPCSC_SCARD_IO_REQUEST));
	dstBytes = ((unsigned char *) dst + sizeof(SERVER_SCARD_IO_REQUEST));
	dst->dwProtocol = swap32((uint32_t) src->dwProtocol);
	dst->cbPciLength = swap32((uint32_t) src->cbPciLength
				  - sizeof(MYPCSC_SCARD_IO_REQUEST) +
				  sizeof(SERVER_SCARD_IO_REQUEST));
	memcpy(dstBytes, srcBytes, bytesToCopy);
}

static void
copyIORequest_ServerToMyPCSC(SERVER_LPSCARD_IO_REQUEST src, MYPCSC_LPSCARD_IO_REQUEST dst)
{
	unsigned char *srcBytes, *dstBytes;
	size_t bytesToCopy = src->cbPciLength - sizeof(SERVER_SCARD_IO_REQUEST);
	srcBytes = ((unsigned char *) src + sizeof(SERVER_SCARD_IO_REQUEST));
	dstBytes = ((unsigned char *) dst + sizeof(MYPCSC_SCARD_IO_REQUEST));
	dst->dwProtocol = swap32(src->dwProtocol);
	dst->cbPciLength = src->cbPciLength	/* already correct endian */
		- sizeof(SERVER_SCARD_IO_REQUEST) + sizeof(MYPCSC_SCARD_IO_REQUEST);
	memcpy(dstBytes, srcBytes, bytesToCopy);
}


static DWORD
TS_SCardTransmit(STREAM in, STREAM out)
{
	MYPCSC_DWORD rv;
	SERVER_DWORD map[7], linkedLen;
	void *tmp;
	SERVER_SCARDCONTEXT hCard;
	MYPCSC_SCARDCONTEXT myHCard;
	SERVER_LPSCARD_IO_REQUEST pioSendPci, pioRecvPci;
	MYPCSC_LPSCARD_IO_REQUEST myPioSendPci, myPioRecvPci;
	unsigned char *sendBuf = NULL, *recvBuf = NULL;
	SERVER_DWORD cbSendLength, cbRecvLength;
	MYPCSC_DWORD myCbRecvLength;
	PMEM_HANDLE lcHandle = NULL;

	in->p += 0x14;
	in_uint32_le(in, map[0]);
	in->p += 0x04;
	in_uint32_le(in, map[1]);
	pioSendPci = SC_xmalloc(&lcHandle, sizeof(SERVER_SCARD_IO_REQUEST));
	if (!pioSendPci)
		return SC_returnNoMemoryError(&lcHandle, in, out);
	in_uint8a(in, pioSendPci, sizeof(SERVER_SCARD_IO_REQUEST));
	in_uint32_le(in, map[2]);
	in_uint32_le(in, cbSendLength);
	in_uint32_le(in, map[3]);
	in_uint32_le(in, map[4]);
	in_uint32_le(in, map[5]);
	in_uint32_le(in, cbRecvLength);
	if (map[0] & INPUT_LINKED)
		inSkipLinked(in);

	in->p += 0x04;
	in_uint32_le(in, hCard);
	myHCard = _scard_handle_list_get_pcsc_handle(hCard);

	if (map[2] & INPUT_LINKED)
	{
		in_uint32_le(in, linkedLen);
		pioSendPci->cbPciLength = linkedLen + sizeof(SERVER_SCARD_IO_REQUEST);
		tmp = SC_xmalloc(&lcHandle, pioSendPci->cbPciLength);
		if (!tmp)
			return SC_returnNoMemoryError(&lcHandle, in, out);
		in_uint8a(in, (void *) ((unsigned char *) tmp + sizeof(SERVER_SCARD_IO_REQUEST)),
			  linkedLen);
		memcpy(tmp, pioSendPci, sizeof(SERVER_SCARD_IO_REQUEST));
		SC_xfree(&lcHandle, pioSendPci);
		pioSendPci = tmp;
		tmp = NULL;
	}
	else
		pioSendPci->cbPciLength = sizeof(SERVER_SCARD_IO_REQUEST);

	if (map[3] & INPUT_LINKED)
	{
		in_uint32_le(in, linkedLen);
		sendBuf = SC_xmalloc(&lcHandle, linkedLen);
		if (!sendBuf)
			return SC_returnNoMemoryError(&lcHandle, in, out);
		in_uint8a(in, sendBuf, linkedLen);
		inRepos(in, linkedLen);
	}
	else
		sendBuf = NULL;

	if (cbRecvLength)
	{
		recvBuf = SC_xmalloc(&lcHandle, cbRecvLength);
		if (!recvBuf)
			return SC_returnNoMemoryError(&lcHandle, in, out);
	}

	if (map[4] & INPUT_LINKED)
	{
		pioRecvPci = SC_xmalloc(&lcHandle, sizeof(SERVER_SCARD_IO_REQUEST));
		if (!pioRecvPci)
			return SC_returnNoMemoryError(&lcHandle, in, out);
		in_uint8a(in, pioRecvPci, sizeof(SERVER_SCARD_IO_REQUEST));
		in_uint32_le(in, map[6]);
		if (map[6] & INPUT_LINKED)
		{
			in_uint32_le(in, linkedLen);
			pioRecvPci->cbPciLength = linkedLen + sizeof(SERVER_SCARD_IO_REQUEST);
			tmp = SC_xmalloc(&lcHandle, pioRecvPci->cbPciLength);
			if (!tmp)
				return SC_returnNoMemoryError(&lcHandle, in, out);
			in_uint8a(in,
				  (void *) ((unsigned char *) tmp +
					    sizeof(SERVER_SCARD_IO_REQUEST)), linkedLen);
			memcpy(tmp, pioRecvPci, sizeof(SERVER_SCARD_IO_REQUEST));
			SC_xfree(&lcHandle, pioRecvPci);
			pioRecvPci = tmp;
			tmp = NULL;
		}
		else
			pioRecvPci->cbPciLength = sizeof(SERVER_SCARD_IO_REQUEST);
	}
	else
		pioRecvPci = NULL;

	DEBUG_SCARD(("SCARD: SCardTransmit(hcard: 0x%08x [0x%08lx], send: %d bytes, recv: %d bytes)\n", (unsigned) hCard, (unsigned long) myHCard, (int) cbSendLength, (int) cbRecvLength));

	myCbRecvLength = cbRecvLength;
	myPioSendPci = SC_xmalloc(&lcHandle,
				  sizeof(MYPCSC_SCARD_IO_REQUEST)
				  + pioSendPci->cbPciLength - sizeof(SERVER_SCARD_IO_REQUEST));
	if (!myPioSendPci)
		return SC_returnNoMemoryError(&lcHandle, in, out);
	copyIORequest_ServerToMyPCSC(pioSendPci, myPioSendPci);
	/* always a send, not always a recv */
	if (pioRecvPci)
	{
		myPioRecvPci = SC_xmalloc(&lcHandle,
					  sizeof(MYPCSC_SCARD_IO_REQUEST)
					  + pioRecvPci->cbPciLength
					  - sizeof(SERVER_SCARD_IO_REQUEST));
		if (!myPioRecvPci)
			return SC_returnNoMemoryError(&lcHandle, in, out);
		copyIORequest_ServerToMyPCSC(pioRecvPci, myPioRecvPci);
	}
	else
	{
		myPioRecvPci = NULL;
	}
	rv = SCardTransmit(myHCard, myPioSendPci, sendBuf, (MYPCSC_DWORD) cbSendLength,
			   myPioRecvPci, recvBuf, &myCbRecvLength);
	cbRecvLength = myCbRecvLength;

	/* FIXME: handle responses with length > 448 bytes */
	if (cbRecvLength > 448)
	{
		warning("Card response limited from %d to 448 bytes!\n", cbRecvLength);
		DEBUG_SCARD(("SCARD:    Truncated %d to %d\n", (unsigned int) cbRecvLength, 448));
		cbRecvLength = 448;
	}

	if (pioRecvPci)
	{
		/*
		 * pscs-lite mishandles this structure in some cases.
		 * make sure we only copy it if it is valid.
		 */
		if (myPioRecvPci->cbPciLength >= sizeof(MYPCSC_SCARD_IO_REQUEST))
			copyIORequest_MyPCSCToServer(myPioRecvPci, pioRecvPci);
	}

	if (rv != SCARD_S_SUCCESS)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
	}
	else
	{
		DEBUG_SCARD(("SCARD: -> Success (%d bytes)\n", (int) cbRecvLength));
#if 0
		if ((pioRecvPci != NULL) && (mypioRecvPci->cbPciLength > 0))
		{
			out_uint32_le(out, (DWORD) pioRecvPci);	/* if not NULL, this 4 bytes indicates that pioRecvPci is present */
		}
		else
#endif
			out_uint32_le(out, 0);	/* pioRecvPci 0x00; */

		outBufferStart(out, cbRecvLength);	/* start of recvBuf output */

#if 0
		if ((pioRecvPci) && (mypioRecvPci->cbPciLength > 0))
		{
			out_uint32_le(out, mypioRecvPci->dwProtocol);
			int len = mypioRecvPci->cbPciLength - sizeof(mypioRecvPci);
			outBufferStartWithLimit(out, len, 12);
			outBufferFinishWithLimit(out,
						 (char *) ((DWORD) pioRecvPci + sizeof(pioRecvPci)),
						 len, 12);
		}
#endif

		outBufferFinish(out, (char *) recvBuf, cbRecvLength);
	}
	outForceAlignment(out, 8);
	SC_xfreeallmemory(&lcHandle);
	return rv;
}

static MYPCSC_DWORD
TS_SCardStatus(STREAM in, STREAM out, RD_BOOL wide)
{
	MYPCSC_DWORD rv;
	SERVER_SCARDCONTEXT hCard;
	MYPCSC_SCARDCONTEXT myHCard;
	SERVER_DWORD dwState = 0, dwProtocol = 0, dwReaderLen, dwAtrLen;
	MYPCSC_DWORD state, protocol, readerLen, atrLen;
	SERVER_DWORD dataLength;
	PMEM_HANDLE lcHandle = NULL;
	char *readerName;
	unsigned char *atr;

	in->p += 0x24;
	in_uint32_le(in, dwReaderLen);
	in_uint32_le(in, dwAtrLen);
	in->p += 0x0C;
	in_uint32_le(in, hCard);
	in->p += 0x04;
	myHCard = _scard_handle_list_get_pcsc_handle(hCard);
	DEBUG_SCARD(("SCARD: SCardStatus(hcard: 0x%08x [0x%08lx], reader len: %d bytes, atr len: %d bytes)\n", (unsigned) hCard, (unsigned long) myHCard, (int) dwReaderLen, (int) dwAtrLen));

	if (dwReaderLen <= 0 || dwReaderLen == SCARD_AUTOALLOCATE || dwReaderLen > SCARD_MAX_MEM)
		dwReaderLen = SCARD_MAX_MEM;
	if (dwAtrLen <= 0 || dwAtrLen == SCARD_AUTOALLOCATE || dwAtrLen > SCARD_MAX_MEM)
		dwAtrLen = SCARD_MAX_MEM;

#if 1
	/*
	 * Active client sometimes sends a readerlen *just* big enough
	 * SCardStatus doesn't seem to like this. This is a workaround,
	 * aka hack!
	 */
	dwReaderLen = 200;
#endif

	readerName = SC_xmalloc(&lcHandle, dwReaderLen + 2);
	if (!readerName)
		return SC_returnNoMemoryError(&lcHandle, in, out);

	atr = SC_xmalloc(&lcHandle, dwAtrLen + 1);
	if (!atr)
		return SC_returnNoMemoryError(&lcHandle, in, out);

	state = dwState;
	protocol = dwProtocol;
	readerLen = dwReaderLen;
	atrLen = dwAtrLen;
	rv = SCardStatus(myHCard, readerName, &readerLen, &state, &protocol, atr, &atrLen);
	dwAtrLen = atrLen;
	dwReaderLen = readerLen;
	dwProtocol = protocol;
	dwState = state;


	if (rv != SCARD_S_SUCCESS)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
		return SC_returnCode(rv, &lcHandle, in, out);
	}
	else
	{
		int i;

		DEBUG_SCARD(("SCARD: -> Success (state: 0x%08x, proto: 0x%08x)\n",
			     (unsigned) dwState, (unsigned) dwProtocol));
		DEBUG_SCARD(("SCARD:        Reader: \"%s\"\n", readerName ? readerName : "NULL"));
		DEBUG_SCARD(("SCARD:        ATR: "));
		for (i = 0; i < dwAtrLen; i++)
		{
			DEBUG_SCARD(("%02x%c", atr[i], (i == dwAtrLen - 1) ? ' ' : ':'));
		}
		DEBUG_SCARD(("\n"));

		if (dwState & (SCARD_SPECIFIC | SCARD_NEGOTIABLE))
			dwState = 0x00000006;
		else
#if 0
		if (dwState & SCARD_SPECIFIC)
			dwState = 0x00000006;
		else if (dwState & SCARD_NEGOTIABLE)
			dwState = 0x00000005;
		else
#endif
		if (dwState & SCARD_POWERED)
			dwState = 0x00000004;
		else if (dwState & SCARD_SWALLOWED)
			dwState = 0x00000003;
		else if (dwState & SCARD_PRESENT)
			dwState = 0x00000002;
		else if (dwState & SCARD_ABSENT)
			dwState = 0x00000001;
		else
			dwState = 0x00000000;

		void *p_len1 = out->p;
		out_uint32_le(out, dwReaderLen);
		out_uint32_le(out, 0x00020000);
		out_uint32_le(out, dwState);
		out_uint32_le(out, dwProtocol);
		out_uint8p(out, atr, dwAtrLen);
		if (dwAtrLen < 32)
		{
			out_uint8s(out, 32 - dwAtrLen);
		}
		out_uint32_le(out, dwAtrLen);

		void *p_len2 = out->p;
		out_uint32_le(out, dwReaderLen);
		dataLength = outString(out, readerName, wide);
		dataLength += outString(out, "\0", wide);
		outRepos(out, dataLength);
		void *psave = out->p;
		out->p = p_len1;
		out_uint32_le(out, dataLength);
		out->p = p_len2;
		out_uint32_le(out, dataLength);
		out->p = psave;
	}
	outForceAlignment(out, 8);
	SC_xfreeallmemory(&lcHandle);
	return rv;
}

static MYPCSC_DWORD
TS_SCardState(STREAM in, STREAM out)
{
	MYPCSC_DWORD rv;
	SERVER_SCARDCONTEXT hCard;
	MYPCSC_SCARDCONTEXT myHCard;
	SERVER_DWORD dwState = 0, dwProtocol = 0, dwReaderLen, dwAtrLen;
	MYPCSC_DWORD state, protocol, readerLen, atrLen;
	PMEM_HANDLE lcHandle = NULL;
	char *readerName;
	unsigned char *atr;

	in->p += 0x24;
	in_uint32_le(in, dwAtrLen);
	in->p += 0x0C;
	in_uint32_le(in, hCard);
	in->p += 0x04;
	myHCard = _scard_handle_list_get_pcsc_handle(hCard);

	DEBUG_SCARD(("SCARD: SCardState(hcard: 0x%08x [0x%08lx], atr len: %d bytes)\n",
		     (unsigned) hCard, (unsigned long) myHCard, (int) dwAtrLen));

	dwReaderLen = SCARD_MAX_MEM;
	if (dwAtrLen <= 0 || dwAtrLen == SCARD_AUTOALLOCATE || dwAtrLen > SCARD_MAX_MEM)
		dwAtrLen = SCARD_MAX_MEM;

	readerName = SC_xmalloc(&lcHandle, dwReaderLen + 2);
	if (!readerName)
		return SC_returnNoMemoryError(&lcHandle, in, out);

	atr = SC_xmalloc(&lcHandle, dwAtrLen + 1);
	if (!atr)
		return SC_returnNoMemoryError(&lcHandle, in, out);

	state = dwState;
	protocol = dwProtocol;
	readerLen = dwReaderLen;
	atrLen = dwAtrLen;
	rv = SCardStatus(myHCard, readerName, &readerLen, &state, &protocol, atr, &atrLen);
	dwAtrLen = atrLen;
	dwReaderLen = readerLen;
	dwProtocol = protocol;
	dwState = state;

	if (rv != SCARD_S_SUCCESS)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
		return SC_returnCode(rv, &lcHandle, in, out);
	}
	else
	{
		int i;

		DEBUG_SCARD(("SCARD: -> Success (state: 0x%08x, proto: 0x%08x)\n",
			     (unsigned) dwState, (unsigned) dwProtocol));
		DEBUG_SCARD(("SCARD:        ATR: "));
		for (i = 0; i < dwAtrLen; i++)
		{
			DEBUG_SCARD(("%02x%c", atr[i], (i == dwAtrLen - 1) ? ' ' : ':'));
		}
		DEBUG_SCARD(("\n"));

		if (dwState & (SCARD_SPECIFIC | SCARD_NEGOTIABLE))
			dwState = 0x00000006;
		else
#if 0
		if (dwState & SCARD_SPECIFIC)
			dwState = 0x00000006;
		else if (dwState & SCARD_NEGOTIABLE)
			dwState = 0x00000005;
		else
#endif
		if (dwState & SCARD_POWERED)
			dwState = 0x00000004;
		else if (dwState & SCARD_SWALLOWED)
			dwState = 0x00000003;
		else if (dwState & SCARD_PRESENT)
			dwState = 0x00000002;
		else if (dwState & SCARD_ABSENT)
			dwState = 0x00000001;
		else
			dwState = 0x00000000;

		out_uint32_le(out, dwState);
		out_uint32_le(out, dwProtocol);
		out_uint32_le(out, dwAtrLen);
		out_uint32_le(out, 0x00000001);
		out_uint32_le(out, dwAtrLen);
		out_uint8p(out, atr, dwAtrLen);
		outRepos(out, dwAtrLen);
	}
	outForceAlignment(out, 8);
	SC_xfreeallmemory(&lcHandle);
	return rv;
}



#ifndef WITH_PCSC120

/* Currently unused */
#if 0
static MYPCSC_DWORD
TS_SCardListReaderGroups(STREAM in, STREAM out)
{
	MYPCSC_DWORD rv;
	SERVER_SCARDCONTEXT hContext;
	MYPCSC_SCARDCONTEXT myHContext;
	SERVER_DWORD dwGroups;
	MYPCSC_DWORD groups;
	char *szGroups;
	PMEM_HANDLE lcHandle = NULL;

	in->p += 0x20;
	in_uint32_le(in, dwGroups);
	in->p += 0x04;
	in_uint32_le(in, hContext);

	myHContext = _scard_handle_list_get_pcsc_handle(hContext);

	DEBUG_SCARD(("SCARD: SCardListReaderGroups(context: 0x%08x [0x%08lx], groups: %d)\n",
		     (unsigned) hContext, (unsigned int) myHContext, (int) dwGroups));

	if (dwGroups <= 0 || dwGroups == SCARD_AUTOALLOCATE || dwGroups > SCARD_MAX_MEM)
		dwGroups = SCARD_MAX_MEM;

	szGroups = SC_xmalloc(&lcHandle, dwGroups);
	if (!szGroups)
		return SC_returnNoMemoryError(&lcHandle, in, out);

	groups = dwGroups;
	rv = SCardListReaderGroups(myHContext, szGroups, &groups);
	dwGroups = groups;

	if (rv)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
		return SC_returnCode(rv, &lcHandle, in, out);
	}
	else
	{
		int i;
		char *cur;

		DEBUG_SCARD(("SCARD: -> Success\n"));
		for (i = 0, cur = szGroups; i < dwGroups; i++, cur += strlen(cur) + 1)
		{
			DEBUG_SCARD(("SCARD:    %s\n", cur));
		}
	}


	out_uint32_le(out, dwGroups);
	out_uint32_le(out, 0x00200000);
	out_uint32_le(out, dwGroups);
	out_uint8a(out, szGroups, dwGroups);
	outRepos(out, dwGroups);
	out_uint32_le(out, 0x00000000);

	outForceAlignment(out, 8);
	SC_xfreeallmemory(&lcHandle);
	return rv;
}
#endif

static MYPCSC_DWORD
TS_SCardGetAttrib(STREAM in, STREAM out)
{
	MYPCSC_DWORD rv;
	SERVER_SCARDCONTEXT hCard;
	MYPCSC_SCARDCONTEXT myHCard;
	SERVER_DWORD dwAttrId, dwAttrLen;
	MYPCSC_DWORD attrLen;
	unsigned char *pbAttr;
	PMEM_HANDLE lcHandle = NULL;

	in->p += 0x20;
	in_uint32_le(in, dwAttrId);
	in->p += 0x04;
	in_uint32_le(in, dwAttrLen);
	in->p += 0x0C;
	in_uint32_le(in, hCard);
	myHCard = _scard_handle_list_get_pcsc_handle(hCard);
	dwAttrId = dwAttrId & 0x0000FFFF;

	DEBUG_SCARD(("SCARD: SCardGetAttrib(hcard: 0x%08x [0x%08lx], attrib: 0x%08x (%d bytes))\n",
		     (unsigned) hCard, (unsigned long) myHCard,
		     (unsigned) dwAttrId, (int) dwAttrLen));

	if (dwAttrLen > MAX_BUFFER_SIZE)
		dwAttrLen = MAX_BUFFER_SIZE;


	if (dwAttrLen > SCARD_AUTOALLOCATE)
		pbAttr = NULL;
	else if ((dwAttrLen < 0) || (dwAttrLen > SCARD_MAX_MEM))
	{
		dwAttrLen = (SERVER_DWORD) SCARD_AUTOALLOCATE;
		pbAttr = NULL;
	}
	else
	{
		pbAttr = SC_xmalloc(&lcHandle, dwAttrLen);
		if (!pbAttr)
			return SC_returnNoMemoryError(&lcHandle, in, out);
	}

	attrLen = dwAttrLen;
	rv = SCardGetAttrib(myHCard, (MYPCSC_DWORD) dwAttrId, pbAttr, &attrLen);
	dwAttrLen = attrLen;

	if (dwAttrId == 0x00000100 && rv != SCARD_S_SUCCESS)
	{
		DEBUG_SCARD(("SCARD:    Faking attribute ATTR_VENDOR_NAME\n"));
		pthread_mutex_lock(&hcardAccess);
		PSCHCardRec hcard = hcardFirst;
		while (hcard)
		{
			if (hcard->hCard == hCard)
			{
				dwAttrLen = strlen(hcard->vendor);
				memcpy(pbAttr, hcard->vendor, dwAttrLen);
				rv = SCARD_S_SUCCESS;
				break;
			}
			hcard = hcard->next;
		}
		pthread_mutex_unlock(&hcardAccess);
		DEBUG_SCARD(("[0x%.8x]\n", (unsigned int) rv));
	}

	if (rv != SCARD_S_SUCCESS)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
		return SC_returnCode(rv, &lcHandle, in, out);
	}
	else
	{
		DEBUG_SCARD(("SCARD: -> Success (%d bytes)\n", (int) dwAttrLen));

		out_uint32_le(out, dwAttrLen);
		out_uint32_le(out, 0x00000200);
		out_uint32_le(out, dwAttrLen);
		if (!pbAttr)
		{
			out_uint8s(out, dwAttrLen);
		}
		else
		{
			out_uint8p(out, pbAttr, dwAttrLen);
		}
		outRepos(out, dwAttrLen);
		out_uint32_le(out, 0x00000000);
	}
	outForceAlignment(out, 8);
	return rv;
}

/* Currently unused */
#if 0
static MYPCSC_DWORD
TS_SCardSetAttrib(STREAM in, STREAM out)
{
	MYPCSC_DWORD rv;
	SERVER_SCARDCONTEXT hCard;
	MYPCSC_SCARDCONTEXT myHCard;
	SERVER_DWORD dwAttrId;
	SERVER_DWORD dwAttrLen;
	unsigned char *pbAttr;
	PMEM_HANDLE lcHandle = NULL;

	in->p += 0x20;
	in_uint32_le(in, dwAttrId);
	in->p += 0x04;
	in_uint32_le(in, dwAttrLen);
	in->p += 0x0C;
	in_uint32_le(in, hCard);
	myHCard = scHandleToMyPCSC(hCard);

	dwAttrId = dwAttrId & 0x0000FFFF;

	DEBUG_SCARD(("SCARD: SCardSetAttrib(hcard: 0x%08x [0x%08lx], attrib: 0x%08x (%d bytes))\n",
		     (unsigned) hCard, (unsigned long) myHCard,
		     (unsigned) dwAttrId, (int) dwAttrLen));

	if (dwAttrLen > MAX_BUFFER_SIZE)
		dwAttrLen = MAX_BUFFER_SIZE;

	pbAttr = SC_xmalloc(&lcHandle, dwAttrLen);
	if (!pbAttr)
		return SC_returnNoMemoryError(&lcHandle, in, out);

	in_uint8a(in, pbAttr, dwAttrLen);
	rv = SCardSetAttrib(myHCard, (MYPCSC_DWORD) dwAttrId, pbAttr, (MYPCSC_DWORD) dwAttrLen);

	if (rv != SCARD_S_SUCCESS)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
	}
	else
	{
		DEBUG_SCARD(("SCARD: -> Success\n"));
	}

	out_uint32_le(out, 0x00000000);
	out_uint32_le(out, 0x00000200);
	out_uint32_le(out, 0x00000000);
	out_uint32_le(out, 0x00000000);
	outForceAlignment(out, 8);
	SC_xfreeallmemory(&lcHandle);
	return rv;
}
#endif

#endif

static MYPCSC_DWORD
TS_SCardControl(STREAM in, STREAM out)
{
	MYPCSC_DWORD rv;
	SERVER_SCARDCONTEXT hContext;
	MYPCSC_SCARDCONTEXT myHContext;
	SERVER_SCARDHANDLE hCard;
	MYPCSC_SCARDHANDLE myHCard;
	SERVER_DWORD map[3];
	SERVER_DWORD dwControlCode;
	unsigned char *pInBuffer, *pOutBuffer;
	SERVER_DWORD nInBufferSize, nOutBufferSize, nOutBufferRealSize, nBytesReturned;
	MYPCSC_DWORD sc_nBytesReturned;
	PMEM_HANDLE lcHandle = NULL;

	pInBuffer = NULL;
	pOutBuffer = NULL;

	in->p += 0x14;
	in_uint32_le(in, map[0]);
	in->p += 0x04;
	in_uint32_le(in, map[1]);
	in_uint32_le(in, dwControlCode);
	in_uint32_le(in, nInBufferSize);
	in_uint32_le(in, map[2]);
	in->p += 0x04;
	in_uint32_le(in, nOutBufferSize);
	in->p += 0x04;
	in_uint32_le(in, hContext);
	in->p += 0x04;
	in_uint32_le(in, hCard);
	if (map[2] & INPUT_LINKED)
	{
		/* read real input size */
		in_uint32_le(in, nInBufferSize);
		if (nInBufferSize > 0)
		{
			pInBuffer = SC_xmalloc(&lcHandle, nInBufferSize);
			if (!pInBuffer)
				return SC_returnNoMemoryError(&lcHandle, in, out);
			in_uint8a(in, pInBuffer, nInBufferSize);
		}
	}

	myHCard = _scard_handle_list_get_pcsc_handle(hCard);
	myHContext = _scard_handle_list_get_pcsc_handle(hContext);

	DEBUG_SCARD(("SCARD: SCardControl(context: 0x%08x [0x%08lx], hcard: 0x%08x [0x%08lx], code: 0x%08x, in: %d bytes, out: %d bytes)\n", (unsigned) hContext, (unsigned long) myHContext, (unsigned) hCard, (unsigned long) myHCard, (unsigned) dwControlCode, (int) nInBufferSize, (int) nOutBufferSize));

	/* Is this a proper Windows smart card ioctl? */
	if ((dwControlCode & 0xffff0000) == (49 << 16))
	{
		/* Translate to local encoding */
		dwControlCode = (dwControlCode & 0x3ffc) >> 2;
		dwControlCode = SCARD_CTL_CODE(dwControlCode);
	}
	else
	{
		warning("Bogus smart card control code 0x%08x\n", dwControlCode);
	}

#if 0
	if (nOutBufferSize > 0)
	{
		nOutBufferRealSize = nOutBufferSize;
	}
	else
#endif
		nOutBufferRealSize = 1024;

	nBytesReturned = nOutBufferRealSize;

	nBytesReturned = nOutBufferRealSize;
	pOutBuffer = SC_xmalloc(&lcHandle, nOutBufferRealSize);
	if (!pOutBuffer)
		return SC_returnNoMemoryError(&lcHandle, in, out);

	sc_nBytesReturned = nBytesReturned;

#ifdef WITH_PCSC120
	rv = SCardControl(myHCard, pInBuffer, (MYPCSC_DWORD) nInBufferSize, pOutBuffer,
			  &sc_nBytesReturned);
#else
	rv = SCardControl(myHCard, (MYPCSC_DWORD) dwControlCode, pInBuffer,
			  (MYPCSC_DWORD) nInBufferSize, pOutBuffer,
			  (MYPCSC_DWORD) nOutBufferRealSize, &sc_nBytesReturned);
#endif
	nBytesReturned = sc_nBytesReturned;

	if (rv != SCARD_S_SUCCESS)
	{
		DEBUG_SCARD(("SCARD: -> Failure: %s (0x%08x)\n",
			     pcsc_stringify_error(rv), (unsigned int) rv));
	}
	else
	{
		DEBUG_SCARD(("SCARD: -> Success (out: %d bytes)\n", (int) nBytesReturned));
	}

#ifdef PCSCLITE_VERSION_NUMBER
	if (dwControlCode == SCARD_CTL_CODE(3400))
	{
		int i;
		SERVER_DWORD cc;

		for (i = 0; i < nBytesReturned / 6; i++)
		{
			memcpy(&cc, pOutBuffer + 2 + i * 6, 4);
			cc = ntohl(cc);
			cc = cc - 0x42000000;
			cc = (49 << 16) | (cc << 2);
			cc = htonl(cc);
			memcpy(pOutBuffer + 2 + i * 6, &cc, 4);
		}
	}
#endif

	out_uint32_le(out, nBytesReturned);
	out_uint32_le(out, 0x00000004);
	out_uint32_le(out, nBytesReturned);
	if (nBytesReturned > 0)
	{
		out_uint8p(out, pOutBuffer, nBytesReturned);
		outRepos(out, nBytesReturned);
	}

	outForceAlignment(out, 8);
	SC_xfreeallmemory(&lcHandle);
	return rv;
}

static MYPCSC_DWORD
TS_SCardAccessStartedEvent(STREAM in, STREAM out)
{
	DEBUG_SCARD(("SCARD: SCardAccessStartedEvent()\n"));
	out_uint8s(out, 8);
	return SCARD_S_SUCCESS;
}


static RD_NTSTATUS
scard_device_control(RD_NTHANDLE handle, uint32 request, STREAM in, STREAM out)
{
	SERVER_DWORD Result = 0x00000000;
	unsigned char *psize, *pend, *pStatusCode;
	SERVER_DWORD addToEnd = 0;

	/* Processing request */

	out_uint32_le(out, 0x00081001);	/* Header lines */
	out_uint32_le(out, 0xCCCCCCCC);
	psize = out->p;
	out_uint32_le(out, 0x00000000);	/* Size of data portion */
	out_uint32_le(out, 0x00000000);	/* Zero bytes (may be usefull) */
	pStatusCode = out->p;
	out_uint32_le(out, 0x00000000);	/* Status Code */

	switch (request)
	{
			/* SCardEstablishContext */
		case SC_ESTABLISH_CONTEXT:
			{
				Result = (SERVER_DWORD) TS_SCardEstablishContext(in, out);
				break;
			}
			/* SCardReleaseContext */
		case SC_RELEASE_CONTEXT:
			{
				Result = (SERVER_DWORD) TS_SCardReleaseContext(in, out);
				break;
			}
			/* SCardIsValidContext */
		case SC_IS_VALID_CONTEXT:
			{
				Result = (SERVER_DWORD) TS_SCardIsValidContext(in, out);
				break;
			}
			/* SCardListReaders */
		case SC_LIST_READERS:	/* SCardListReadersA */
		case SC_LIST_READERS + 4:	/* SCardListReadersW */
			{
				RD_BOOL wide = request != SC_LIST_READERS;
				Result = (SERVER_DWORD) TS_SCardListReaders(in, out, wide);
				break;
			}
			/* ScardConnect */
		case SC_CONNECT:	/* ScardConnectA */
		case SC_CONNECT + 4:	/* SCardConnectW */
			{
				RD_BOOL wide = request != SC_CONNECT;
				Result = (SERVER_DWORD) TS_SCardConnect(in, out, wide);
				break;
			}
			/* ScardReconnect */
		case SC_RECONNECT:
			{
				Result = (SERVER_DWORD) TS_SCardReconnect(in, out);
				break;
			}
			/* ScardDisconnect */
		case SC_DISCONNECT:
			{
				Result = (SERVER_DWORD) TS_SCardDisconnect(in, out);
				break;
			}
			/* ScardGetStatusChange */
		case SC_GET_STATUS_CHANGE:	/* SCardGetStatusChangeA */
		case SC_GET_STATUS_CHANGE + 4:	/* SCardGetStatusChangeW */
			{
				RD_BOOL wide = request != SC_GET_STATUS_CHANGE;
				Result = (SERVER_DWORD) TS_SCardGetStatusChange(in, out, wide);
				break;
			}
			/* SCardCancel */
		case SC_CANCEL:
			{
				Result = (SERVER_DWORD) TS_SCardCancel(in, out);
				break;
			}
			/* SCardLocateCardsByATR */
		case SC_LOCATE_CARDS_BY_ATR:	/* SCardLocateCardsByATRA */
		case SC_LOCATE_CARDS_BY_ATR + 4:	/* SCardLocateCardsByATRW */
			{
				RD_BOOL wide = request != SC_LOCATE_CARDS_BY_ATR;
				Result = (SERVER_DWORD) TS_SCardLocateCardsByATR(in, out, wide);
				break;
			}
			/* SCardBeginTransaction */
		case SC_BEGIN_TRANSACTION:
			{
				Result = (SERVER_DWORD) TS_SCardBeginTransaction(in, out);
				break;
			}
			/* SCardBeginTransaction */
		case SC_END_TRANSACTION:
			{
				Result = (SERVER_DWORD) TS_SCardEndTransaction(in, out);
				break;
			}
			/* ScardTransmit */
		case SC_TRANSMIT:
			{
				Result = (SERVER_DWORD) TS_SCardTransmit(in, out);
				break;
			}
			/* SCardControl */
		case SC_CONTROL:
			{
				Result = (SERVER_DWORD) TS_SCardControl(in, out);
				break;
			}
			/* SCardGetAttrib */
#ifndef WITH_PCSC120
		case SC_GETATTRIB:
			{
				Result = (SERVER_DWORD) TS_SCardGetAttrib(in, out);
				break;
			}
#endif
		case SC_ACCESS_STARTED_EVENT:
			{
				Result = (SERVER_DWORD) TS_SCardAccessStartedEvent(in, out);
				break;
			}
		case SC_STATUS:	/* SCardStatusA */
		case SC_STATUS + 4:	/* SCardStatusW */
			{
				RD_BOOL wide = request != SC_STATUS;
				Result = (SERVER_DWORD) TS_SCardStatus(in, out, wide);
				break;
			}
		case SC_STATE:	/* SCardState */
			{
				Result = (SERVER_DWORD) TS_SCardState(in, out);
				break;
			}
		default:
			{
				warning("SCARD: Unknown function %d\n", (int) request);
				Result = 0x80100014;
				out_uint8s(out, 256);
				break;
			}
	}

#if 0
	out_uint32_le(out, 0x00000000);
#endif
	/* Setting modified variables */
	pend = out->p;
	/* setting data size */
	out->p = psize;
	out_uint32_le(out, pend - psize - 16);
	/* setting status code */
	out->p = pStatusCode;
	out_uint32_le(out, Result);
	/* finish */
	out->p = pend;

	addToEnd = (pend - pStatusCode) % 16;
	if (addToEnd < 16 && addToEnd > 0)
	{
		out_uint8s(out, addToEnd);
	}

	return RD_STATUS_SUCCESS;
}

/* Thread functions */

static STREAM
duplicateStream(PMEM_HANDLE * handle, STREAM s, uint32 buffer_size, RD_BOOL isInputStream)
{
	STREAM d = SC_xmalloc(handle, sizeof(struct stream));
	if (d != NULL)
	{
		if (isInputStream)
			d->size = (size_t) (s->end) - (size_t) (s->data);
		else if (buffer_size < s->size)
			d->size = s->size;
		else
			d->size = buffer_size;

		d->data = SC_xmalloc(handle, d->size);

		d->end = (void *) ((size_t) (d->data) + (size_t) (s->end) - (size_t) (s->data));
		d->p = (void *) ((size_t) (d->data) + (size_t) (s->p) - (size_t) (s->data));
		d->iso_hdr =
			(void *) ((size_t) (d->data) + (size_t) (s->iso_hdr) - (size_t) (s->data));
		d->mcs_hdr =
			(void *) ((size_t) (d->data) + (size_t) (s->mcs_hdr) - (size_t) (s->data));
		d->sec_hdr =
			(void *) ((size_t) (d->data) + (size_t) (s->sec_hdr) - (size_t) (s->data));
		d->sec_hdr =
			(void *) ((size_t) (d->data) + (size_t) (s->sec_hdr) - (size_t) (s->data));
		d->rdp_hdr =
			(void *) ((size_t) (d->data) + (size_t) (s->rdp_hdr) - (size_t) (s->data));
		d->channel_hdr =
			(void *) ((size_t) (d->data) + (size_t) (s->channel_hdr) -
				  (size_t) (s->data));
		if (isInputStream)
			memcpy(d->data, s->data, (size_t) (s->end) - (size_t) (s->data));
		else
			memcpy(d->data, s->data, (size_t) (s->p) - (size_t) (s->data));
	}
	return d;
}

/* Currently unused */
#if 0
static void
freeStream(PMEM_HANDLE * handle, STREAM s)
{
	if (s != NULL)
	{
		if (s->data != NULL)
			SC_xfree(handle, s->data);
		SC_xfree(handle, s);
	}
}
#endif

static PSCThreadData
SC_addToQueue(RD_NTHANDLE handle, uint32 request, STREAM in, STREAM out)
{
	PMEM_HANDLE lcHandle = NULL;
	PSCThreadData data = SC_xmalloc(&lcHandle, sizeof(TSCThreadData));

	if (!data)
		return NULL;
	else
	{
		data->memHandle = lcHandle;
		data->device = curDevice;
		data->id = curId;
		data->handle = handle;
		data->request = request;
		data->in = duplicateStream(&(data->memHandle), in, 0, SC_TRUE);
		if (data->in == NULL)
		{
			SC_xfreeallmemory(&(data->memHandle));
			return NULL;
		}
		data->out =
			duplicateStream(&(data->memHandle), out, OUT_STREAM_SIZE + curBytesOut,
					SC_FALSE);
		if (data->out == NULL)
		{
			SC_xfreeallmemory(&(data->memHandle));
			return NULL;
		}
		data->next = NULL;

		pthread_mutex_lock(&queueAccess);

		if (queueLast)
			queueLast->next = data;
		queueLast = data;
		if (!queueFirst)
			queueFirst = data;

		pthread_cond_broadcast(&queueEmpty);
		pthread_mutex_unlock(&queueAccess);
	}
	return data;
}

static void
SC_destroyThreadData(PSCThreadData data)
{
	if (data)
	{
		PMEM_HANDLE handle = data->memHandle;
		SC_xfreeallmemory(&handle);
	}
}

static PSCThreadData
SC_getNextInQueue()
{
	PSCThreadData Result = NULL;

	pthread_mutex_lock(&queueAccess);

	while (queueFirst == NULL)
		pthread_cond_wait(&queueEmpty, &queueAccess);

	Result = queueFirst;
	queueFirst = queueFirst->next;
	if (!queueFirst)
	{
		queueLast = NULL;
	}
	Result->next = NULL;

	pthread_mutex_unlock(&queueAccess);

	return Result;
}

static void
SC_deviceControl(PSCThreadData data)
{
	size_t buffer_len = 0;
	scard_device_control(data->handle, data->request, data->in, data->out);
	buffer_len = (size_t) data->out->p - (size_t) data->out->data;
	rdpdr_send_completion(data->device, data->id, 0, buffer_len, data->out->data, buffer_len);
	SC_destroyThreadData(data);
}


static void *
thread_function(PThreadListElement listElement)
{
	pthread_mutex_lock(&listElement->busy);
	while (1)
	{
		while (listElement->data == NULL)
			pthread_cond_wait(&listElement->nodata, &listElement->busy);

		SC_deviceControl(listElement->data);
		listElement->data = NULL;
	}
	pthread_mutex_unlock(&listElement->busy);

	pthread_exit(NULL);
	return NULL;
}

static void
SC_handleRequest(PSCThreadData data)
{
	int Result = 0;
	PThreadListElement cur;

	for (cur = threadList; cur != NULL; cur = cur->next)
	{
		if (cur->data == NULL)
		{
			pthread_mutex_lock(&cur->busy);
			/* double check with lock held.... */
			if (cur->data != NULL)
			{
				pthread_mutex_unlock(&cur->busy);
				continue;
			}

			/* Wake up thread */
			cur->data = data;
			pthread_cond_broadcast(&cur->nodata);
			pthread_mutex_unlock(&cur->busy);
			return;
		}
	}

	cur = SC_xmalloc(&threadListHandle, sizeof(TThreadListElement));
	if (!cur)
		return;

	threadCount++;

	pthread_mutex_init(&cur->busy, NULL);
	pthread_cond_init(&cur->nodata, NULL);
	cur->data = data;

	Result = pthread_create(&cur->thread, NULL, (void *(*)(void *)) thread_function, cur);
	if (0 != Result)
	{
		error("[THREAD CREATE ERROR 0x%.8x]\n", Result);
		SC_xfree(&threadListHandle, cur);
		SC_destroyThreadData(data);
		data = NULL;
	}
	cur->next = threadList;
	threadList = cur;
}

static void *
queue_handler_function(void *data)
{
	PSCThreadData cur_data = NULL;
	while (1)
	{
		cur_data = SC_getNextInQueue();
		switch (cur_data->request)
		{
			case SC_ESTABLISH_CONTEXT:
			case SC_RELEASE_CONTEXT:
				{
					SC_deviceControl(cur_data);
					break;
				}
			default:
				{
					SC_handleRequest(cur_data);
					break;
				}
		}
	}
	return NULL;
}

static RD_NTSTATUS
thread_wrapper(RD_NTHANDLE handle, uint32 request, STREAM in, STREAM out)
{
	if (SC_addToQueue(handle, request, in, out))
		return RD_STATUS_PENDING | 0xC0000000;
	else
		return RD_STATUS_NO_SUCH_FILE;
}

DEVICE_FNS scard_fns = {
	scard_create,
	scard_close,
	scard_read,
	scard_write,
	thread_wrapper
};
#endif /* MAKE_PROTO */

void
scard_lock(int lock)
{
	if (!scard_mutex)
	{
		int i;

		scard_mutex =
			(pthread_mutex_t **) xmalloc(sizeof(pthread_mutex_t *) * SCARD_LOCK_LAST);

		for (i = 0; i < SCARD_LOCK_LAST; i++)
		{
			scard_mutex[i] = NULL;
		}
	}

	if (!scard_mutex[lock])
	{
		scard_mutex[lock] = (pthread_mutex_t *) xmalloc(sizeof(pthread_mutex_t));
		pthread_mutex_init(scard_mutex[lock], NULL);
	}

	pthread_mutex_lock(scard_mutex[lock]);
}

void
scard_unlock(int lock)
{
	pthread_mutex_unlock(scard_mutex[lock]);
}

void
scard_reset_state()
{
	curDevice = 0;
	curId = 0;
	curBytesOut = 0;

	queueFirst = queueLast = NULL;
}
