/*
 * card.c: General smart card functions
 *
 * Copyright (C) 2001, 2002  Juha Yrjölä <juha.yrjola@iki.fi>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <assert.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>
#include <limits.h>

#include "reader-tr03119.h"
#include "internal.h"
#include "asn1.h"
#include "common/compat_strlcpy.h"

#ifdef ENABLE_SM
static int sc_card_sm_load(sc_card_t *card, const char *path, const char *module);
static int sc_card_sm_unload(sc_card_t *card);
static int sc_card_sm_check(sc_card_t *card);
#endif

int sc_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2)
{
	if (card == NULL)
		return SC_ERROR_INVALID_ARGUMENTS;
	if (card->ops->check_sw == NULL)
		return SC_ERROR_NOT_SUPPORTED;
	return card->ops->check_sw(card, sw1, sw2);
}

void sc_format_apdu(sc_card_t *card, sc_apdu_t *apdu,
		    int cse, int ins, int p1, int p2)
{
	if (card == NULL || apdu == NULL) {
		return;
	}
	memset(apdu, 0, sizeof(*apdu));
	apdu->cla = (u8) card->cla;
	apdu->cse = cse;
	apdu->ins = (u8) ins;
	apdu->p1 = (u8) p1;
	apdu->p2 = (u8) p2;
}

void sc_format_apdu_cse_lc_le(struct sc_apdu *apdu)
{
	/* TODO calculating the APDU case, Lc and Le should actually only be
	 * done in sc_apdu2bytes, but to gradually change OpenSC we start here. */
	/* Let sc_detect_apdu_cse set short or extended  and test for chaining */

	if (!apdu)
		return;
	if (apdu->datalen > SC_MAX_APDU_DATA_SIZE
			|| apdu->resplen > SC_MAX_APDU_RESP_SIZE) {
		/* extended length  or data chaining and/or get response */
		if (apdu->datalen <= SC_MAX_EXT_APDU_DATA_SIZE)
			apdu->lc = apdu->datalen;
		if (apdu->resplen <= SC_MAX_EXT_APDU_RESP_SIZE)
			apdu->le = apdu->resplen;
		if (apdu->resplen && !apdu->datalen)
			apdu->cse = SC_APDU_CASE_2;
		if (!apdu->resplen && apdu->datalen)
			apdu->cse = SC_APDU_CASE_3;
		if (apdu->resplen && apdu->datalen)
			apdu->cse = SC_APDU_CASE_4;
	} else {
		/* short length */
		if (apdu->datalen <= SC_MAX_APDU_DATA_SIZE)
			apdu->lc = apdu->datalen;
		if (apdu->resplen <= SC_MAX_APDU_RESP_SIZE)
			apdu->le = apdu->resplen;
		if (!apdu->resplen && !apdu->datalen)
			apdu->cse = SC_APDU_CASE_1;
		if (apdu->resplen && !apdu->datalen)
			apdu->cse = SC_APDU_CASE_2_SHORT;
		if (!apdu->resplen && apdu->datalen)
			apdu->cse = SC_APDU_CASE_3_SHORT;
		if (apdu->resplen && apdu->datalen)
			apdu->cse = SC_APDU_CASE_4_SHORT;
	}
}

void sc_format_apdu_ex(struct sc_apdu *apdu,
		u8 cla, u8 ins, u8 p1, u8 p2,
		const u8 *data, size_t datalen,
		u8 *resp, size_t resplen)
{
	if (!apdu) {
		return;
	}

	memset(apdu, 0, sizeof(*apdu));
	apdu->cla = cla;
	apdu->ins = ins;
	apdu->p1 = p1;
	apdu->p2 = p2;
	apdu->resp = resp;
	apdu->resplen = resplen;
	apdu->data = data;
	apdu->datalen = datalen;
	sc_format_apdu_cse_lc_le(apdu);
}

static sc_card_t * sc_card_new(sc_context_t *ctx)
{
	sc_card_t *card;

	if (ctx == NULL)
		return NULL;

	card = calloc(1, sizeof(struct sc_card));
	if (card == NULL)
		return NULL;
	card->ops = malloc(sizeof(struct sc_card_operations));
	if (card->ops == NULL) {
		free(card);
		return NULL;
	}

	card->ctx = ctx;
	if (sc_mutex_create(ctx, &card->mutex) != SC_SUCCESS) {
		free(card->ops);
		free(card);
		return NULL;
	}

	card->type = -1;
	card->app_count = -1;

	return card;
}

static void sc_card_free(sc_card_t *card)
{
	sc_free_apps(card);
	sc_free_ef_atr(card);

	free(card->ops);

	if (card->algorithms != NULL)   {
		int i;
		for (i=0; i<card->algorithm_count; i++)   {
			struct sc_algorithm_info *info = (card->algorithms + i);
			if (info->algorithm == SC_ALGORITHM_EC) {
				struct sc_ec_parameters ep = info->u._ec.params;

				free(ep.named_curve);
				free(ep.der.value);
			}
		}
		free(card->algorithms);

		card->algorithms = NULL;
		card->algorithm_count = 0;
	}

	sc_file_free(card->cache.current_ef);
	sc_file_free(card->cache.current_df);

	if (card->mutex != NULL) {
		int r = sc_mutex_destroy(card->ctx, card->mutex);
		if (r != SC_SUCCESS)
			sc_log(card->ctx, "unable to destroy mutex");
	}
	sc_mem_clear(card, sizeof(*card));
	free(card);
}

size_t sc_get_max_recv_size(const sc_card_t *card)
{
	size_t max_recv_size;
	if (card == NULL || card->reader == NULL) {
		return 0;
	}
	max_recv_size = card->max_recv_size;

	/* initialize max_recv_size to a meaningful value */
	if (card->caps & SC_CARD_CAP_APDU_EXT) {
		if (!max_recv_size)
			max_recv_size = 65536;
	} else {
		if (!max_recv_size)
			max_recv_size = 256;
	}

	/*  Override card limitations with reader limitations. */
	if (card->reader->max_recv_size != 0
			&& (card->reader->max_recv_size < card->max_recv_size))
		max_recv_size = card->reader->max_recv_size;

	return max_recv_size;
}

size_t sc_get_max_send_size(const sc_card_t *card)
{
	size_t max_send_size;

	if (card == NULL || card->reader == NULL) {
		return 0;
	}

	max_send_size = card->max_send_size;

	/* initialize max_send_size to a meaningful value */
	if (card->caps & SC_CARD_CAP_APDU_EXT
			&& card->reader->active_protocol != SC_PROTO_T0) {
		if (!max_send_size)
			max_send_size = 65535;
	} else {
		if (!max_send_size)
			max_send_size = 255;
	}

	/*  Override card limitations with reader limitations. */
	if (card->reader->max_send_size != 0
			&& (card->reader->max_send_size < card->max_send_size))
		max_send_size = card->reader->max_send_size;

	return max_send_size;
}

int sc_connect_card(sc_reader_t *reader, sc_card_t **card_out)
{
	sc_card_t *card;
	sc_context_t *ctx;
	struct sc_card_driver *driver;
	int i, r = 0, idx, connected = 0;

	if (card_out == NULL || reader == NULL)
		return SC_ERROR_INVALID_ARGUMENTS;
	ctx = reader->ctx;
	SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE);
	if (reader->ops->connect == NULL)
		LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED);

	card = sc_card_new(ctx);
	if (card == NULL)
		LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
	r = reader->ops->connect(reader);
	if (r)
		goto err;

	connected = 1;
	card->reader = reader;
	card->ctx = ctx;

	if (reader->flags & SC_READER_ENABLE_ESCAPE)
		sc_detect_escape_cmds(reader);

	memcpy(&card->atr, &reader->atr, sizeof(card->atr));
	memcpy(&card->uid, &reader->uid, sizeof(card->uid));

	_sc_parse_atr(reader);

	/* See if the ATR matches any ATR specified in the config file */
	if ((driver = ctx->forced_driver) == NULL) {
		sc_log(ctx, "matching configured ATRs");
		for (i = 0; ctx->card_drivers[i] != NULL; i++) {
			driver = ctx->card_drivers[i];

			if (driver->atr_map == NULL ||
			    !strcmp(driver->short_name, "default")) {
				driver = NULL;
				continue;
			}
			sc_log(ctx, "trying driver '%s'", driver->short_name);
			idx = _sc_match_atr(card, driver->atr_map, NULL);
			if (idx >= 0) {
				struct sc_atr_table *src = &driver->atr_map[idx];

				sc_log(ctx, "matched driver '%s'", driver->name);
				/* It's up to card driver to notice these correctly */
				card->name = src->name;
				card->type = src->type;
				card->flags = src->flags;
				break;
			}
			driver = NULL;
		}
	}

	if (driver != NULL) {
		/* Forced driver, or matched via ATR mapping from config file */
		card->driver = driver;

		memcpy(card->ops, card->driver->ops, sizeof(struct sc_card_operations));
		if (card->ops->match_card != NULL)
			if (card->ops->match_card(card) != 1)
				sc_log(ctx, "driver '%s' match_card() failed: %s (will continue anyway)", card->driver->name, sc_strerror(r));

		if (card->ops->init != NULL) {
			r = card->ops->init(card);
			if (r) {
				sc_log(ctx, "driver '%s' init() failed: %s", card->driver->name, sc_strerror(r));
				goto err;
			}
		}
	}
	else {
		sc_card_t uninitialized = *card;
		sc_log(ctx, "matching built-in ATRs");
		for (i = 0; ctx->card_drivers[i] != NULL; i++) {
			/* FIXME If we had a clean API description, we'd probably get a
			 * cleaner implementation of the driver's match_card and init,
			 * which should normally *not* modify the card object if
			 * unsuccessful. However, after years of relentless hacking, reality
			 * is different: The card object is changed in virtually every card
			 * driver so in order to prevent unwanted interaction, we reset the
			 * card object here and hope that the card driver at least doesn't
			 * allocate any internal resources that need to be freed. If we
			 * had more time, we should refactor the existing code to not
			 * modify sc_card_t until complete success (possibly by combining
			 * `match_card()` and `init()`) */
			*card = uninitialized;

			struct sc_card_driver *drv = ctx->card_drivers[i];
			const struct sc_card_operations *ops = drv->ops;

			sc_log(ctx, "trying driver '%s'", drv->short_name);
			if (ops == NULL || ops->match_card == NULL)   {
				continue;
			}
			else if (!(ctx->flags & SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER)
				   	&& !strcmp("default", drv->short_name))   {
				sc_log(ctx , "ignore 'default' card driver");
				continue;
			}

			/* Needed if match_card() needs to talk with the card (e.g. card-muscle) */
			*card->ops = *ops;
			if (ops->match_card(card) != 1)
				continue;
			sc_log(ctx, "matched: %s", drv->name);
			memcpy(card->ops, ops, sizeof(struct sc_card_operations));
			card->driver = drv;
			r = ops->init(card);
			if (r) {
				sc_log(ctx, "driver '%s' init() failed: %s", drv->name, sc_strerror(r));
				if (r == SC_ERROR_INVALID_CARD) {
					card->driver = NULL;
					continue;
				}
				goto err;
			}
			break;
		}
	}
	if (card->driver == NULL) {
		sc_log(ctx, "unable to find driver for inserted card");
		r = SC_ERROR_INVALID_CARD;
		goto err;
	}
	if (card->name == NULL)
		card->name = card->driver->name;

	/* initialize max_send_size/max_recv_size to a meaningful value */
	card->max_recv_size = sc_get_max_recv_size(card);
	card->max_send_size = sc_get_max_send_size(card);

	sc_log(ctx,
	       "card info name:'%s', type:%i, flags:0x%lX, max_send/recv_size:%"SC_FORMAT_LEN_SIZE_T"u/%"SC_FORMAT_LEN_SIZE_T"u",
	       card->name, card->type, card->flags, card->max_send_size,
	       card->max_recv_size);

#ifdef ENABLE_SM
        /* Check, if secure messaging module present. */
	r = sc_card_sm_check(card);
	if (r)   {
		sc_log(ctx, "cannot load secure messaging module");
		goto err;
	}
#endif
	*card_out = card;

	LOG_FUNC_RETURN(ctx, SC_SUCCESS);
err:
	if (connected)
		reader->ops->disconnect(reader);
	if (card != NULL)
		sc_card_free(card);
	LOG_FUNC_RETURN(ctx, r);
}

int sc_disconnect_card(sc_card_t *card)
{
	sc_context_t *ctx;

	if (!card)
		return SC_ERROR_INVALID_ARGUMENTS;

	ctx = card->ctx;
	LOG_FUNC_CALLED(ctx);

	if (card->lock_count != 0)
		return SC_ERROR_NOT_ALLOWED;
	if (card->ops->finish) {
		int r = card->ops->finish(card);
		if (r)
			sc_log(ctx, "card driver finish() failed: %s", sc_strerror(r));
	}

	if (card->reader->ops->disconnect) {
		int r = card->reader->ops->disconnect(card->reader);
		if (r)
			sc_log(ctx, "disconnect() failed: %s", sc_strerror(r));
	}

#ifdef ENABLE_SM
	/* release SM related resources */
	sc_card_sm_unload(card);
#endif

	sc_card_free(card);
	LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}

int sc_reset(sc_card_t *card, int do_cold_reset)
{
	int r, r2;

	if (card == NULL)
		return SC_ERROR_INVALID_ARGUMENTS;
	if (card->reader->ops->reset == NULL)
		return SC_ERROR_NOT_SUPPORTED;

	r = sc_mutex_lock(card->ctx, card->mutex);
	if (r != SC_SUCCESS)
		return r;

	r = card->reader->ops->reset(card->reader, do_cold_reset);
	sc_invalidate_cache(card);

	r2 = sc_mutex_unlock(card->ctx, card->mutex);
	if (r2 != SC_SUCCESS) {
		sc_log(card->ctx, "unable to release lock");
		r = r != SC_SUCCESS ? r : r2;
	}

	return r;
}

int sc_lock(sc_card_t *card)
{
	int r = 0, r2 = 0;
	int was_reset = 0;
	int reader_lock_obtained  = 0;

	if (card == NULL)
		return SC_ERROR_INVALID_ARGUMENTS;

	LOG_FUNC_CALLED(card->ctx);

	r = sc_mutex_lock(card->ctx, card->mutex);
	if (r != SC_SUCCESS)
		return r;
	if (card->lock_count == 0) {
		if (card->reader->ops->lock != NULL) {
			r = card->reader->ops->lock(card->reader);
			while (r == SC_ERROR_CARD_RESET || r == SC_ERROR_READER_REATTACHED) {
				sc_invalidate_cache(card);
				if (was_reset++ > 4) /* TODO retry a few times */
					break;
				r = card->reader->ops->lock(card->reader);
			}
			if (r == 0)
				reader_lock_obtained = 1;
		}
		if (r == 0)
			card->cache.valid = 1;
	}
	if (r == 0)
		card->lock_count++;

	if (r == 0 && was_reset > 0) {
#ifdef ENABLE_SM
		if (card->sm_ctx.ops.open)
			card->sm_ctx.ops.open(card);
#endif
	}

	r2 = sc_mutex_unlock(card->ctx, card->mutex);
	if (r2 != SC_SUCCESS) {
		sc_log(card->ctx, "unable to release card->mutex lock");
		r = r != SC_SUCCESS ? r : r2;
	}

	/* give card driver a chance to do something when reader lock first obtained */
	if (r == 0 && reader_lock_obtained == 1  && card->ops->card_reader_lock_obtained)
		r = card->ops->card_reader_lock_obtained(card, was_reset);

	LOG_FUNC_RETURN(card->ctx, r);
}

int sc_unlock(sc_card_t *card)
{
	int r, r2;

	if (!card)
		return SC_ERROR_INVALID_ARGUMENTS;

	LOG_FUNC_CALLED(card->ctx);

	r = sc_mutex_lock(card->ctx, card->mutex);
	if (r != SC_SUCCESS)
		return r;

	if (card->lock_count < 1) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	if (--card->lock_count == 0) {
		if (card->flags & SC_CARD_FLAG_KEEP_ALIVE) {
			/* Multiple processes accessing the card will most likely render
			 * the card cache useless. To not have a bad cache, we explicitly
			 * invalidate it. */
			sc_invalidate_cache(card);
		}
		/* release reader lock */
		if (card->reader->ops->unlock != NULL)
			r = card->reader->ops->unlock(card->reader);
	}
	r2 = sc_mutex_unlock(card->ctx, card->mutex);
	if (r2 != SC_SUCCESS) {
		sc_log(card->ctx, "unable to release lock");
		r = (r == SC_SUCCESS) ? r2 : r;
	}

	return r;
}

int sc_list_files(sc_card_t *card, u8 *buf, size_t buflen)
{
	int r;

	if (card == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	LOG_FUNC_CALLED(card->ctx);

	if (card->ops->list_files == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
	r = card->ops->list_files(card, buf, buflen);

	LOG_FUNC_RETURN(card->ctx, r);
}

int sc_create_file(sc_card_t *card, sc_file_t *file)
{
	int r;
	char pbuf[SC_MAX_PATH_STRING_SIZE];
	const sc_path_t *in_path;

	if (card == NULL || file == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}

	in_path = &file->path;
	r = sc_path_print(pbuf, sizeof(pbuf), in_path);
	if (r != SC_SUCCESS)
		pbuf[0] = '\0';

	sc_log(card->ctx,
	       "called; type=%d, path=%s, id=%04i, size=%"SC_FORMAT_LEN_SIZE_T"u",
	       in_path->type, pbuf, file->id, file->size);
	/* ISO 7816-4: "Number of data bytes in the file, including structural information if any"
	 * can not be bigger than two bytes */
	if (file->size > 0xFFFF)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);

	if (card->ops->create_file == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);

	r = card->ops->create_file(card, file);
	LOG_FUNC_RETURN(card->ctx, r);
}

int sc_delete_file(sc_card_t *card, const sc_path_t *path)
{
	int r;
	char pbuf[SC_MAX_PATH_STRING_SIZE];

	if (card == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}

	r = sc_path_print(pbuf, sizeof(pbuf), path);
	if (r != SC_SUCCESS)
		pbuf[0] = '\0';

	sc_log(card->ctx, "called; type=%d, path=%s", path->type, pbuf);
	if (card->ops->delete_file == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
	r = card->ops->delete_file(card, path);

	LOG_FUNC_RETURN(card->ctx, r);
}

int sc_read_binary(sc_card_t *card, unsigned int idx,
		   unsigned char *buf, size_t count, unsigned long flags)
{
	size_t max_le = sc_get_max_recv_size(card);
	size_t todo = count;
	int r;

	if (card == NULL || card->ops == NULL || buf == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	sc_log(card->ctx, "called; %"SC_FORMAT_LEN_SIZE_T"u bytes at index %d",
	       count, idx);
	if (count == 0)
		LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);

#ifdef ENABLE_SM
	if (card->sm_ctx.ops.read_binary)   {
		r = card->sm_ctx.ops.read_binary(card, idx, buf, count);
		if (r)
			LOG_FUNC_RETURN(card->ctx, r);
	}
#endif

	if (card->ops->read_binary == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);

	/* lock the card now to avoid deselection of the file */
	r = sc_lock(card);
	LOG_TEST_RET(card->ctx, r, "sc_lock() failed");

	while (todo > 0) {
		size_t chunk = todo > max_le ? max_le : todo;

		r = card->ops->read_binary(card, idx, buf, chunk, flags);
		if (r == 0 || r == SC_ERROR_FILE_END_REACHED)
			break;
		if ((idx > SIZE_MAX - (size_t) r)
				|| (size_t) r > todo) {
			/* `idx + r` or `todo - r` would overflow */
			r = SC_ERROR_OFFSET_TOO_LARGE;
		}
		if (r < 0) {
			sc_unlock(card);
			LOG_FUNC_RETURN(card->ctx, r);
		}

		todo -= (size_t) r;
		buf  += (size_t) r;
		idx  += (size_t) r;
	}

	sc_unlock(card);

	LOG_FUNC_RETURN(card->ctx, count - todo);
}

int sc_write_binary(sc_card_t *card, unsigned int idx,
		    const u8 *buf, size_t count, unsigned long flags)
{
	size_t max_lc = sc_get_max_send_size(card);
	size_t todo = count;
	int r;

	if (card == NULL || card->ops == NULL || buf == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	sc_log(card->ctx, "called; %"SC_FORMAT_LEN_SIZE_T"u bytes at index %d",
	       count, idx);
	if (count == 0)
		LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);

	if (card->ops->write_binary == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);

	/* lock the card now to avoid deselection of the file */
	r = sc_lock(card);
	LOG_TEST_RET(card->ctx, r, "sc_lock() failed");

	while (todo > 0) {
		size_t chunk = todo > max_lc ? max_lc : todo;

		r = card->ops->write_binary(card, idx, buf, chunk, flags);
		if (r == 0 || r == SC_ERROR_FILE_END_REACHED)
			break;
		if ((idx > SIZE_MAX - (size_t) r)
				|| (size_t) r > todo) {
			/* `idx + r` or `todo - r` would overflow */
			r = SC_ERROR_OFFSET_TOO_LARGE;
		}
		if (r < 0) {
			sc_unlock(card);
			LOG_FUNC_RETURN(card->ctx, r);
		}

		todo -= (size_t) r;
		buf  += (size_t) r;
		idx  += (size_t) r;
	}

	sc_unlock(card);

	LOG_FUNC_RETURN(card->ctx, count - todo);
}

int sc_update_binary(sc_card_t *card, unsigned int idx,
		     const u8 *buf, size_t count, unsigned long flags)
{
	size_t max_lc = sc_get_max_send_size(card);
	size_t todo = count;
	int r;

	if (card == NULL || card->ops == NULL || buf == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	sc_log(card->ctx, "called; %"SC_FORMAT_LEN_SIZE_T"u bytes at index %d",
	       count, idx);
	if (count == 0)
		LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);

#ifdef ENABLE_SM
	if (card->sm_ctx.ops.update_binary)   {
		r = card->sm_ctx.ops.update_binary(card, idx, buf, count);
		if (r)
			LOG_FUNC_RETURN(card->ctx, r);
	}
#endif

	if (card->ops->update_binary == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);

	/* lock the card now to avoid deselection of the file */
	r = sc_lock(card);
	LOG_TEST_RET(card->ctx, r, "sc_lock() failed");

	while (todo > 0) {
		size_t chunk = todo > max_lc ? max_lc : todo;

		r = card->ops->update_binary(card, idx, buf, chunk, flags);
		if (r == 0 || r == SC_ERROR_FILE_END_REACHED)
			break;
		if ((idx > SIZE_MAX - (size_t) r)
				|| (size_t) r > todo) {
			/* `idx + r` or `todo - r` would overflow */
			r = SC_ERROR_OFFSET_TOO_LARGE;
		}
		if (r < 0) {
			sc_unlock(card);
			LOG_FUNC_RETURN(card->ctx, r);
		}

		todo -= (size_t) r;
		buf  += (size_t) r;
		idx  += (size_t) r;
	}

	sc_unlock(card);

	LOG_FUNC_RETURN(card->ctx, count - todo);
}


int sc_erase_binary(struct sc_card *card, unsigned int idx, size_t count,  unsigned long flags)
{
	int r;
	size_t todo = count;

	if (card == NULL || card->ops == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	sc_log(card->ctx,
	       "called; erase %"SC_FORMAT_LEN_SIZE_T"u bytes from offset %d",
	       count, idx);
	if (count == 0)
		LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);

	if (card->ops->erase_binary == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);

	/* lock the card now to avoid deselection of the file */
	r = sc_lock(card);
	LOG_TEST_RET(card->ctx, r, "sc_lock() failed");

	while (todo > 0) {
		r = card->ops->erase_binary(card, idx, todo, flags);
		if (r == 0 || r == SC_ERROR_FILE_END_REACHED)
			break;
		if ((idx > SIZE_MAX - (size_t) r)
				|| (size_t) r > todo) {
			/* `idx + r` or `todo - r` would overflow */
			r = SC_ERROR_OFFSET_TOO_LARGE;
		}
		if (r < 0) {
			sc_unlock(card);
			LOG_FUNC_RETURN(card->ctx, r);
		}

		todo -= (size_t) r;
		idx  += (size_t) r;
	}

	sc_unlock(card);

	LOG_FUNC_RETURN(card->ctx, count - todo);
}


int sc_select_file(sc_card_t *card, const sc_path_t *in_path,  sc_file_t **file)
{
	int r;
	char pbuf[SC_MAX_PATH_STRING_SIZE];

	if (card == NULL || in_path == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}

	r = sc_path_print(pbuf, sizeof(pbuf), in_path);
	if (r != SC_SUCCESS)
		pbuf[0] = '\0';

	/* FIXME We should be a bit less strict and let the upper layers do
	 * the initialization (including reuse of existing file objects). We
	 * implemented this here because we are lazy. */
	if (file != NULL)
		*file = NULL;

	sc_log(card->ctx, "called; type=%d, path=%s", in_path->type, pbuf);
	if (in_path->len > SC_MAX_PATH_SIZE)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);

	if (in_path->type == SC_PATH_TYPE_PATH) {
		/* Perform a sanity check */
		size_t i;

		if ((in_path->len & 1) != 0)
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);

		for (i = 0; i < in_path->len/2; i++) {
			u8 p1 = in_path->value[2*i],
			   p2 = in_path->value[2*i+1];

			if ((p1 == 0x3F && p2 == 0x00) && i != 0)
				LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
		}
	}
	if (card->ops->select_file == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
	r = card->ops->select_file(card, in_path, file);
	LOG_TEST_RET(card->ctx, r, "'SELECT' error");

	if (file) {
		if (*file)
			/* Remember file path */
			(*file)->path = *in_path;
		else
			/* FIXME We should be a bit less strict and let the upper layers do
			 * the error checking. We implemented this here because we are
			 * lazy.  */
			r = SC_ERROR_INVALID_DATA;
	}

	LOG_FUNC_RETURN(card->ctx, r);
}


int sc_get_data(sc_card_t *card, unsigned int tag, u8 *buf, size_t len)
{
	int	r;

	sc_log(card->ctx, "called, tag=%04x", tag);
	if (card->ops->get_data == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
	r = card->ops->get_data(card, tag, buf, len);

	LOG_FUNC_RETURN(card->ctx, r);
}

int sc_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t len)
{
	int	r;

	sc_log(card->ctx,"called, tag=%04x", tag);

	if (card->ops->put_data == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
	r = card->ops->put_data(card, tag, buf, len);

	LOG_FUNC_RETURN(card->ctx, r);
}

int sc_get_challenge(sc_card_t *card, u8 *rnd, size_t len)
{
	int r;

	if (len == 0)
		return SC_SUCCESS;

	if (card == NULL || rnd == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}

	LOG_FUNC_CALLED(card->ctx);

	if (card->ops == NULL || card->ops->get_challenge == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);

	r = sc_lock(card);
	if (r != SC_SUCCESS)
		LOG_FUNC_RETURN(card->ctx, r);

	while (len > 0) {
		r = card->ops->get_challenge(card, rnd, len);
		if (r == 0)
			r = SC_ERROR_INVALID_DATA;
		if (r < 0) {
			sc_unlock(card);
			LOG_FUNC_RETURN(card->ctx, r);
		}

		rnd += (size_t) r;
		len -= (size_t) r;
	}

	sc_unlock(card);

	LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}

int sc_read_record(sc_card_t *card, unsigned int rec_nr, u8 *buf,
		   size_t count, unsigned long flags)
{
	int r;

	if (card == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	LOG_FUNC_CALLED(card->ctx);

	if (card->ops->read_record == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);

	r = card->ops->read_record(card, rec_nr, buf, count, flags);
	if (r == SC_SUCCESS) {
		r = count;
	}

	LOG_FUNC_RETURN(card->ctx, r);
}

int sc_write_record(sc_card_t *card, unsigned int rec_nr, const u8 * buf,
		    size_t count, unsigned long flags)
{
	int r;

	if (card == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	LOG_FUNC_CALLED(card->ctx);

	if (card->ops->write_record == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);

	r = card->ops->write_record(card, rec_nr, buf, count, flags);
	if (r == SC_SUCCESS) {
		r = count;
	}

	LOG_FUNC_RETURN(card->ctx, r);
}

int sc_append_record(sc_card_t *card, const u8 * buf, size_t count,
		     unsigned long flags)
{
	int r;

	if (card == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	LOG_FUNC_CALLED(card->ctx);

	if (card->ops->append_record == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);

	r = card->ops->append_record(card, buf, count, flags);
	if (r == SC_SUCCESS) {
		r = count;
	}

	LOG_FUNC_RETURN(card->ctx, r);
}

int sc_update_record(sc_card_t *card, unsigned int rec_nr, const u8 * buf,
		     size_t count, unsigned long flags)
{
	int r;

	if (card == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	LOG_FUNC_CALLED(card->ctx);

	if (card->ops->update_record == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);

	r = card->ops->update_record(card, rec_nr, buf, count, flags);
	if (r == SC_SUCCESS) {
		r = count;
	}

	LOG_FUNC_RETURN(card->ctx, r);
}

int sc_delete_record(sc_card_t *card, unsigned int rec_nr)
{
	int r;

	if (card == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	LOG_FUNC_CALLED(card->ctx);

	if (card->ops->delete_record == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);

	r = card->ops->delete_record(card, rec_nr);

	LOG_FUNC_RETURN(card->ctx, r);
}

int
sc_card_ctl(sc_card_t *card, unsigned long cmd, void *args)
{
	int r = SC_ERROR_NOT_SUPPORTED;

	if (card == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	LOG_FUNC_CALLED(card->ctx);

	if (card->ops->card_ctl != NULL)
		r = card->ops->card_ctl(card, cmd, args);

	/* suppress "not supported" error messages */
	if (r == SC_ERROR_NOT_SUPPORTED) {
		sc_log(card->ctx, "card_ctl(%lu) not supported", cmd);
		return r;
	}
	LOG_FUNC_RETURN(card->ctx, r);
}

int _sc_card_add_algorithm(sc_card_t *card, const sc_algorithm_info_t *info)
{
	sc_algorithm_info_t *p;

	if (info == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	p = (sc_algorithm_info_t *) realloc(card->algorithms, (card->algorithm_count + 1) * sizeof(*info));
	if (!p) {
		return SC_ERROR_OUT_OF_MEMORY;
	}
	card->algorithms = p;
	p += card->algorithm_count;
	card->algorithm_count++;
	*p = *info;
	return SC_SUCCESS;
}

int _sc_card_add_symmetric_alg(sc_card_t *card, unsigned int algorithm,
			       unsigned int key_length, unsigned long flags)
{
	sc_algorithm_info_t info;

	memset(&info, 0, sizeof(info));
	info.algorithm = algorithm;
	info.key_length = key_length;
	info.flags = flags;

	return _sc_card_add_algorithm(card, &info);
}

static int
_sc_card_add_ec_alg_int(sc_card_t *card, unsigned int key_length,
			unsigned long flags, unsigned long ext_flags,
			struct sc_object_id *curve_oid,
			int algorithm)
{
	sc_algorithm_info_t info;

	memset(&info, 0, sizeof(info));
	sc_init_oid(&info.u._ec.params.id);

	info.algorithm = algorithm;
	info.key_length = key_length;
	info.flags = flags;

	info.u._ec.ext_flags = ext_flags;
	if (curve_oid)
		info.u._ec.params.id = *curve_oid;

	return _sc_card_add_algorithm(card, &info);
}

int  _sc_card_add_ec_alg(sc_card_t *card, unsigned int key_length,
			unsigned long flags, unsigned long ext_flags,
			struct sc_object_id *curve_oid)
{
	return _sc_card_add_ec_alg_int(card, key_length, flags, ext_flags,
		curve_oid, SC_ALGORITHM_EC);
}

int  _sc_card_add_eddsa_alg(sc_card_t *card, unsigned int key_length,
			unsigned long flags, unsigned long ext_flags,
			struct sc_object_id *curve_oid)
{
	/* For simplicity, share the ec union with the curve information */
	return _sc_card_add_ec_alg_int(card, key_length, flags, ext_flags,
		curve_oid, SC_ALGORITHM_EDDSA);
}

int  _sc_card_add_xeddsa_alg(sc_card_t *card, unsigned int key_length,
			unsigned long flags, unsigned long ext_flags,
			struct sc_object_id *curve_oid)
{
	/* For simplicity, share the ec union with the curve information */
	return _sc_card_add_ec_alg_int(card, key_length, flags, ext_flags,
		curve_oid, SC_ALGORITHM_XEDDSA);
}

sc_algorithm_info_t * sc_card_find_alg(sc_card_t *card,
		unsigned int algorithm, unsigned int key_length, void *param)
{
	int i;

	for (i = 0; i < card->algorithm_count; i++) {
		sc_algorithm_info_t *info = &card->algorithms[i];

		if (info->algorithm != algorithm)
			continue;
		if (param)   {
			if (info->algorithm == SC_ALGORITHM_EC ||
				info->algorithm == SC_ALGORITHM_EDDSA ||
				info->algorithm == SC_ALGORITHM_XEDDSA)
				if (sc_compare_oid((struct sc_object_id *)param, &info->u._ec.params.id))
					return info;
		}
		if (info->key_length != key_length)
			continue;
		return info;
	}
	return NULL;
}

sc_algorithm_info_t * sc_card_find_ec_alg(sc_card_t *card,
		unsigned int key_length, struct sc_object_id *curve_name)
{
	return sc_card_find_alg(card, SC_ALGORITHM_EC, key_length, curve_name);
}

sc_algorithm_info_t * sc_card_find_eddsa_alg(sc_card_t *card,
		unsigned int key_length, struct sc_object_id *curve_name)
{
	return sc_card_find_alg(card, SC_ALGORITHM_EDDSA, key_length, curve_name);
}

sc_algorithm_info_t * sc_card_find_xeddsa_alg(sc_card_t *card,
		unsigned int key_length, struct sc_object_id *curve_name)
{
	return sc_card_find_alg(card, SC_ALGORITHM_XEDDSA, key_length, curve_name);
}

int _sc_card_add_rsa_alg(sc_card_t *card, unsigned int key_length,
			 unsigned long flags, unsigned long exponent)
{
	sc_algorithm_info_t info;

	memset(&info, 0, sizeof(info));
	info.algorithm = SC_ALGORITHM_RSA;
	info.key_length = key_length;
	info.flags = flags;
	info.u._rsa.exponent = exponent;

	return _sc_card_add_algorithm(card, &info);
}

sc_algorithm_info_t * sc_card_find_rsa_alg(sc_card_t *card,
		unsigned int key_length)
{
	return sc_card_find_alg(card, SC_ALGORITHM_RSA, key_length, NULL);
}

sc_algorithm_info_t * sc_card_find_gostr3410_alg(sc_card_t *card,
		unsigned int key_length)
{
	return sc_card_find_alg(card, SC_ALGORITHM_GOSTR3410, key_length, NULL);
}

static int match_atr_table(sc_context_t *ctx, const struct sc_atr_table *table, struct sc_atr *atr)
{
	u8 *card_atr_bin;
	size_t card_atr_bin_len;
	char card_atr_hex[3 * SC_MAX_ATR_SIZE];
	size_t card_atr_hex_len;
	unsigned int i = 0;

	if (ctx == NULL || table == NULL || atr == NULL)
		return -1;
	card_atr_bin = atr->value;
	card_atr_bin_len = atr->len;
	sc_bin_to_hex(card_atr_bin, card_atr_bin_len, card_atr_hex, sizeof(card_atr_hex), ':');
	card_atr_hex_len = strlen(card_atr_hex);

	sc_debug(ctx, SC_LOG_DEBUG_MATCH, "ATR     : %s", card_atr_hex);

	for (i = 0; table[i].atr != NULL; i++) {
		const char *tatr = table[i].atr;
		const char *matr = table[i].atrmask;
		size_t tatr_len = strlen(tatr);
		u8 mbin[SC_MAX_ATR_SIZE], tbin[SC_MAX_ATR_SIZE];
		size_t mbin_len, tbin_len, s, matr_len;
		size_t fix_hex_len = card_atr_hex_len;
		size_t fix_bin_len = card_atr_bin_len;

		sc_debug(ctx, SC_LOG_DEBUG_MATCH, "ATR try : %s", tatr);

		if (tatr_len != fix_hex_len) {
			sc_debug(ctx, SC_LOG_DEBUG_MATCH, "ignored - wrong length");
			continue;
		}
		if (matr != NULL) {
			sc_debug(ctx, SC_LOG_DEBUG_MATCH, "ATR mask: %s", matr);

			matr_len = strlen(matr);
			if (tatr_len != matr_len)
				continue;
			tbin_len = sizeof(tbin);
			sc_hex_to_bin(tatr, tbin, &tbin_len);
			mbin_len = sizeof(mbin);
			sc_hex_to_bin(matr, mbin, &mbin_len);
			if (mbin_len != fix_bin_len) {
				sc_debug(ctx, SC_LOG_DEBUG_MATCH, "length of atr and atr mask do not match - ignored: %s - %s", tatr, matr);
				continue;
			}
			for (s = 0; s < tbin_len; s++) {
				/* reduce tatr with mask */
				tbin[s] = (tbin[s] & mbin[s]);
				/* create copy of card_atr_bin masked) */
				mbin[s] = (card_atr_bin[s] & mbin[s]);
			}
			if (memcmp(tbin, mbin, tbin_len) != 0)
				continue;
		} else {
			if (strncasecmp(tatr, card_atr_hex, tatr_len) != 0)
				continue;
		}
		return i;
	}
	return -1;
}

int _sc_match_atr(sc_card_t *card, const struct sc_atr_table *table, int *type_out)
{
	int res;

	if (card == NULL)
		return -1;
	res = match_atr_table(card->ctx, table, &card->atr);
	if (res < 0)
		return res;
	if (type_out != NULL)
		*type_out = table[res].type;
	return res;
}

scconf_block *_sc_match_atr_block(sc_context_t *ctx, struct sc_card_driver *driver, struct sc_atr *atr)
{
	struct sc_card_driver *drv;
	struct sc_atr_table *table;
	int res;

	if (ctx == NULL)
		return NULL;
	if (driver) {
		drv = driver;
		table = drv->atr_map;
		res = match_atr_table(ctx, table, atr);
		if (res < 0)
			return NULL;
		return table[res].card_atr;
	} else {
		unsigned int i;

		for (i = 0; ctx->card_drivers[i] != NULL; i++) {
			drv = ctx->card_drivers[i];
			table = drv->atr_map;
			res = match_atr_table(ctx, table, atr);
			if (res < 0)
				continue;
			return table[res].card_atr;
		}
	}
	return NULL;
}

int _sc_add_atr(sc_context_t *ctx, struct sc_card_driver *driver, struct sc_atr_table *src)
{
	struct sc_atr_table *map, *dst;

	map = (struct sc_atr_table *) realloc(driver->atr_map,
			(driver->natrs + 2) * sizeof(struct sc_atr_table));
	if (!map)
		return SC_ERROR_OUT_OF_MEMORY;
	driver->atr_map = map;

	dst = &driver->atr_map[driver->natrs++];
	memset(dst, 0, sizeof(*dst));
	memset(&driver->atr_map[driver->natrs], 0, sizeof(struct sc_atr_table));
	dst->atr = strdup(src->atr);
	if (!dst->atr)
		return SC_ERROR_OUT_OF_MEMORY;

	if (src->atrmask) {
		dst->atrmask = strdup(src->atrmask);
		if (!dst->atrmask)
			return SC_ERROR_OUT_OF_MEMORY;
	}
	else {
		dst->atrmask = NULL;
	}

	if (src->name) {
		dst->name = strdup(src->name);
		if (!dst->name)
			return SC_ERROR_OUT_OF_MEMORY;
	}
	else {
		dst->name = NULL;
	}

	dst->type = src->type;
	dst->flags = src->flags;
	dst->card_atr = src->card_atr;

	return SC_SUCCESS;
}


int _sc_free_atr(sc_context_t *ctx, struct sc_card_driver *driver)
{
	unsigned int i;

	for (i = 0; i < driver->natrs; i++) {
		struct sc_atr_table *src = &driver->atr_map[i];

		if (src->atr)
			free((void *)src->atr);
		if (src->atrmask)
			free((void *)src->atrmask);
		if (src->name)
			free((void *)src->name);
		src->card_atr = NULL;
		src = NULL;
	}
	if (driver->atr_map)
		free(driver->atr_map);
	driver->atr_map = NULL;
	driver->natrs = 0;

	return SC_SUCCESS;
}


scconf_block *sc_get_conf_block(sc_context_t *ctx, const char *name1, const char *name2, int priority)
{
	int i;
	scconf_block *conf_block = NULL;

	for (i = 0; ctx->conf_blocks[i] != NULL; i++) {
		scconf_block **blocks;

		blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], name1, name2);
		if (blocks != NULL) {
			conf_block = blocks[0];
			free(blocks);
		}
		if (conf_block != NULL && priority)
			break;
	}
	return conf_block;
}

void sc_invalidate_cache(struct sc_card *card)
{
	if (card) {
		sc_file_free(card->cache.current_ef);
		sc_file_free(card->cache.current_df);
		memset(&card->cache, 0, sizeof(card->cache));
		card->cache.valid = 0;
	}
}

void sc_print_cache(struct sc_card *card)
{
	struct sc_context *ctx = NULL;

	if (card == NULL)
		return;
	ctx = card->ctx;

	if (!card->cache.valid || (!card->cache.current_ef && !card->cache.current_df))   {
		sc_log(ctx, "card cache invalid");
		return;
	}

	if (card->cache.current_ef)
		sc_log(ctx, "current_ef(type=%i) %s", card->cache.current_ef->path.type,
				sc_print_path(&card->cache.current_ef->path));

	if (card->cache.current_df)
		sc_log(ctx,
		       "current_df(type=%i, aid_len=%"SC_FORMAT_LEN_SIZE_T"u) %s",
		       card->cache.current_df->path.type,
		       card->cache.current_df->path.aid.len,
		       sc_print_path(&card->cache.current_df->path));
}

int sc_copy_ec_params(struct sc_ec_parameters *dst, struct sc_ec_parameters *src)
{
	if (!dst || !src)
		return SC_ERROR_INVALID_ARGUMENTS;

	memset(dst, 0, sizeof(*dst));
	if (src->named_curve)   {
		dst->named_curve = strdup(src->named_curve);
		if (!dst->named_curve)
			return SC_ERROR_OUT_OF_MEMORY;
	}
	dst->id = src->id;
	if (src->der.value && src->der.len)   {
		dst->der.value = malloc(src->der.len);
		if (!dst->der.value)
			return SC_ERROR_OUT_OF_MEMORY;
		memcpy(dst->der.value, src->der.value, src->der.len);
		dst->der.len = src->der.len;
	}
	src->type = dst->type;
	src->field_length = dst->field_length;

	return SC_SUCCESS;
}

scconf_block *
sc_match_atr_block(sc_context_t *ctx, struct sc_card_driver *driver, struct sc_atr *atr)
{
	return _sc_match_atr_block(ctx, driver, atr);
}

#ifdef ENABLE_SM
static int
sc_card_sm_unload(struct sc_card *card)
{
	if (card->sm_ctx.module.ops.module_cleanup)
		card->sm_ctx.module.ops.module_cleanup(card->ctx);

	if (card->sm_ctx.module.handle)
		sc_dlclose(card->sm_ctx.module.handle);
	card->sm_ctx.module.handle = NULL;
	return 0;
}


static int
sc_card_sm_load(struct sc_card *card, const char *module_path, const char *in_module)
{
	struct sc_context *ctx = NULL;
	int rv = SC_ERROR_INTERNAL;
	char *module = NULL;
#ifdef _WIN32
	char temp_path[PATH_MAX];
	size_t temp_len;
	const char path_delim = '\\';
	char expanded_val[PATH_MAX];
	DWORD expanded_len;
#else
	const char path_delim = '/';
#endif

	if (card == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	ctx = card->ctx;
	LOG_FUNC_CALLED(ctx);
	if (!in_module)
		return sc_card_sm_unload(card);

#ifdef _WIN32
	if (!module_path || strlen(module_path) == 0)   {
		temp_len = PATH_MAX-1;
		rv = sc_ctx_win32_get_config_value(NULL, "SmDir", "Software\\OpenSC Project\\OpenSC",
				temp_path, &temp_len);
		if (rv == SC_SUCCESS) {
			temp_path[temp_len] = '\0';
			module_path = temp_path;
		}
	}
	expanded_len = PATH_MAX;
	expanded_len = ExpandEnvironmentStringsA(module_path, expanded_val, expanded_len);
	if (0 < expanded_len && expanded_len < sizeof expanded_val)
		module_path = expanded_val;
#endif
	sc_log(ctx, "SM module '%s' located in '%s'", in_module, module_path);
	if (module_path && strlen(module_path) > 0)   {
		int sz = strlen(in_module) + strlen(module_path) + 3;
		module = malloc(sz);
		if (module)
			snprintf(module, sz, "%s%c%s", module_path, path_delim, in_module);
	}
	else   {
		module = strdup(in_module);
	}

	if (!module)
		return SC_ERROR_OUT_OF_MEMORY;

	sc_log(ctx, "try to load SM module '%s'", module);
	do  {
		struct sm_module_operations *mod_ops = &card->sm_ctx.module.ops;
		void *mod_handle;

		card->sm_ctx.module.handle = sc_dlopen(module);
		if (!card->sm_ctx.module.handle)   {
			sc_log(ctx, "cannot open dynamic library '%s': %s", module, sc_dlerror());
			break;
		}
		mod_handle = card->sm_ctx.module.handle;

		mod_ops->initialize = sc_dlsym(mod_handle, "initialize");
		if (!mod_ops->initialize)   {
			sc_log(ctx, "SM handler 'initialize' not exported: %s", sc_dlerror());
			break;
		}

		mod_ops->get_apdus  = sc_dlsym(mod_handle, "get_apdus");
		if (!mod_ops->get_apdus)   {
			sc_log(ctx, "SM handler 'get_apdus' not exported: %s", sc_dlerror());
			break;
		}

		mod_ops->finalize  = sc_dlsym(mod_handle, "finalize");
		if (!mod_ops->finalize)
			sc_log(ctx, "SM handler 'finalize' not exported -- ignored");

		mod_ops->module_init  = sc_dlsym(mod_handle, "module_init");
		if (!mod_ops->module_init)
			sc_log(ctx, "SM handler 'module_init' not exported -- ignored");

		mod_ops->module_cleanup  = sc_dlsym(mod_handle, "module_cleanup");
		if (!mod_ops->module_cleanup)
			sc_log(ctx, "SM handler 'module_cleanup' not exported -- ignored");

		mod_ops->test  = sc_dlsym(mod_handle, "test");
		if (mod_ops->test)
			sc_log(ctx, "SM handler 'test' not exported -- ignored");

		rv = 0;
		break;
	} while(0);

	if (rv)
		sc_card_sm_unload(card);

	card->sm_ctx.sm_mode = SM_MODE_ACL;
	if (module)
		free(module);

	SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, rv);
}


/* get SM related configuration settings and initialize SM session, SM module, ... */
static int
sc_card_sm_check(struct sc_card *card)
{
	const char *sm = NULL, *module_name = NULL, *module_path = NULL, *module_data = NULL, *sm_mode = NULL;
	struct sc_context *ctx = card->ctx;
	scconf_block *atrblock = NULL, *sm_conf_block = NULL;
	int rv, ii;

	LOG_FUNC_CALLED(ctx);

	/* get the name of card specific SM configuration section */
	atrblock = _sc_match_atr_block(ctx, card->driver, &card->atr);
	if (atrblock == NULL)
		LOG_FUNC_RETURN(ctx, SC_SUCCESS);
	sm = scconf_get_str(atrblock, "secure_messaging", NULL);
	if (!sm)
		LOG_FUNC_RETURN(ctx, SC_SUCCESS);

	/* get SM configuration section by the name */
	sc_log(ctx, "secure_messaging configuration block '%s'", sm);
        for (ii = 0; ctx->conf_blocks[ii]; ii++) {
		scconf_block **blocks;

		blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[ii], "secure_messaging", sm);
		if (blocks) {
			sm_conf_block = blocks[0];
			free(blocks);
		}
		if (sm_conf_block != NULL)
			break;
	}

	if (!sm_conf_block)
		LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_CONFIGURATION, "SM configuration block not preset");

	/* check if an external SM module has to be used */
	module_path = scconf_get_str(sm_conf_block, "module_path", DEFAULT_SM_MODULE_PATH);
	module_name = scconf_get_str(sm_conf_block, "module_name", DEFAULT_SM_MODULE);
	sc_log(ctx, "SM module '%s' in  '%s'", module_name, module_path);
	if (!module_name)
		LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_CONFIGURATION, "Invalid SM configuration: module not defined");

	rv = sc_card_sm_load(card, module_path, module_name);
	LOG_TEST_RET(ctx, rv, "Failed to load SM module");

	strlcpy(card->sm_ctx.module.filename, module_name, sizeof(card->sm_ctx.module.filename));
	strlcpy(card->sm_ctx.config_section, sm, sizeof(card->sm_ctx.config_section));

	/* allocate resources for the external SM module */
	if (card->sm_ctx.module.ops.module_init)   {
		module_data = scconf_get_str(sm_conf_block, "module_data", NULL);

		rv = card->sm_ctx.module.ops.module_init(ctx, module_data);
		LOG_TEST_RET(ctx, rv, "Cannot initialize SM module");
	}

	/* initialize SM session in the case of 'APDU TRANSMIT' SM mode */
	sm_mode = scconf_get_str(sm_conf_block, "mode", NULL);
	if (sm_mode && !strcasecmp("Transmit", sm_mode))   {
		if (!card->sm_ctx.ops.open || !card->sm_ctx.ops.get_sm_apdu || !card->sm_ctx.ops.free_sm_apdu)
			LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "'Transmit' SM asked but not supported by card driver");

		card->sm_ctx.sm_mode = SM_MODE_TRANSMIT;
		rv = card->sm_ctx.ops.open(card);
		LOG_TEST_RET(ctx, rv, "Cannot initialize SM");
	}

	sc_log(ctx, "SM mode:%X", card->sm_ctx.sm_mode);
	SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, rv);
}
#endif
