static av_cold int dnxhd_init_rc(DNXHDEncContext *ctx)
{
    FF_ALLOCZ_OR_GOTO(ctx->m.avctx, ctx->mb_rc, 8160*(ctx->m.avctx->qmax + 1)*sizeof(RCEntry), fail);
    if (ctx->m.avctx->mb_decision != FF_MB_DECISION_RD)
        FF_ALLOCZ_OR_GOTO(ctx->m.avctx, ctx->mb_cmp, ctx->m.mb_num*sizeof(RCCMPEntry), fail);

    ctx->frame_bits = (ctx->cid_table->coding_unit_size - 640 - 4 - ctx->min_padding) * 8;
    ctx->qscale = 1;
    ctx->lambda = 2<<LAMBDA_FRAC_BITS; // qscale 2
    return 0;
 fail:
    return -1;
}


GF_Err av1dmx_check_format(GF_Filter *filter, GF_AV1DmxCtx *ctx, GF_BitStream *bs, u32 *last_obu_end)
{
	GF_Err e;
	if (last_obu_end) (*last_obu_end) = 0;
	//probing av1 bs mode
	if (ctx->bsmode != NOT_SET) return GF_OK;

	if (!ctx->state.config)
		ctx->state.config = gf_odf_av1_cfg_new();

	ctx->is_av1 = ctx->is_vp9 = ctx->is_vpX = GF_FALSE;
	ctx->codecid = 0;
	if (ctx->vp_cfg) gf_odf_vp_cfg_del(ctx->vp_cfg);
	ctx->vp_cfg = NULL;
	ctx->cur_fps = ctx->fps;
	if (!ctx->fps.num || !ctx->fps.den) {
		ctx->cur_fps.num = 25000;
		ctx->cur_fps.den = 1000;
	}

	ctx->pts_from_file = GF_FALSE;
	if (gf_media_probe_ivf(bs)) {
		u32 width = 0, height = 0;
		u32 codec_fourcc = 0, timebase_den = 0, timebase_num = 0, num_frames = 0;
		ctx->bsmode = IVF;

		e = gf_media_parse_ivf_file_header(bs, &width, &height, &codec_fourcc, &timebase_num, &timebase_den, &num_frames);
		if (e) return e;

		switch (codec_fourcc) {
		case GF_4CC('A', 'V', '0', '1'):
			ctx->is_av1 = GF_TRUE;
			ctx->codecid = GF_CODECID_AV1;
			break;
		case GF_4CC('V', 'P', '9', '0'):
			ctx->is_vp9 = GF_TRUE;
			ctx->codecid = GF_CODECID_VP9;
			ctx->vp_cfg = gf_odf_vp_cfg_new();
			break;
		case GF_4CC('V', 'P', '8', '0'):
			ctx->codecid = GF_CODECID_VP8;
			ctx->vp_cfg = gf_odf_vp_cfg_new();
			break;
		case GF_4CC('V', 'P', '1', '0'):
			ctx->codecid = GF_CODECID_VP10;
			ctx->vp_cfg = gf_odf_vp_cfg_new();
			GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[IVF] %s parsing not implemented, import might be uncomplete or broken\n", gf_4cc_to_str(codec_fourcc) ));
			break;
		default:
			ctx->codecid = codec_fourcc;
			GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[IVF] Unsupported codec FourCC %s\n", gf_4cc_to_str(codec_fourcc) ));
			return GF_NON_COMPLIANT_BITSTREAM;
		}
		if (ctx->vp_cfg && !ctx->is_vp9) {
			ctx->is_vpX = GF_TRUE;
			ctx->vp_cfg->profile = 1;
			ctx->vp_cfg->level = 10;
			ctx->vp_cfg->bit_depth = 8;
			//leave the rest as 0
		}

		ctx->state.width = ctx->state.width < width ? width : ctx->state.width;
		ctx->state.height = ctx->state.height < height ? height : ctx->state.height;
		ctx->state.tb_num = timebase_num;
		ctx->state.tb_den = timebase_den;

		if ((!ctx->fps.num || !ctx->fps.den) && ctx->state.tb_num && ctx->state.tb_den && ! ( (ctx->state.tb_num<=1) && (ctx->state.tb_den<=1) ) ) {
			ctx->cur_fps.num = ctx->state.tb_num;
			ctx->cur_fps.den = ctx->state.tb_den;
			GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[AV1Dmx] Detected IVF format FPS %d/%d\n", ctx->cur_fps.num, ctx->cur_fps.den));
			ctx->pts_from_file = GF_TRUE;
		} else {
			GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[AV1Dmx] Detected IVF format\n"));
		}
		ctx->file_hdr_size = (u32) gf_bs_get_position(bs);
		if (last_obu_end) (*last_obu_end) = (u32) gf_bs_get_position(bs);
		return GF_OK;
	} else if (gf_media_aom_probe_annexb(bs)) {
		GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[AV1Dmx] Detected Annex B format\n"));
		ctx->bsmode = AnnexB;
	} else {
		gf_bs_seek(bs, 0);
		e = aom_av1_parse_temporal_unit_from_section5(bs, &ctx->state);
		if (e && !gf_list_count(ctx->state.frame_state.frame_obus) ) {
			gf_filter_setup_failure(filter, e);
			ctx->bsmode = UNSUPPORTED;
			return e;
		}
		if (ctx->state.obu_type != OBU_TEMPORAL_DELIMITER) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[AV1Dmx] Error OBU stream start with %s, not a temporal delimiter - NOT SUPPORTED\n", gf_av1_get_obu_name(ctx->state.obu_type) ));
			gf_filter_setup_failure(filter, e);
			ctx->bsmode = UNSUPPORTED;
			return e;
		}
		GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[AV1Dmx] Detected OBUs Section 5 format\n"));
		ctx->bsmode = OBUs;

		gf_av1_reset_state(&ctx->state, GF_FALSE);
		gf_bs_seek(bs, 0);
	}
	ctx->is_av1 = GF_TRUE;
	ctx->state.unframed = GF_TRUE;
	ctx->codecid = GF_CODECID_AV1;
	return GF_OK;
}


void mk_vhost_free_all()
{
    struct host *host;
    struct host_alias *host_alias;
    struct error_page *ep;
    struct mk_list *head_host;
    struct mk_list *head_alias;
    struct mk_list *head_error;
    struct mk_list *tmp1, *tmp2;

    mk_list_foreach_safe(head_host, tmp1, &config->hosts) {
        host = mk_list_entry(head_host, struct host, _head);
        mk_list_del(&host->_head);

        mk_mem_free(host->file);

        /* Free aliases or servernames */
        mk_list_foreach_safe(head_alias, tmp2, &host->server_names) {
            host_alias = mk_list_entry(head_alias, struct host_alias, _head);
            mk_list_del(&host_alias->_head);
            mk_mem_free(host_alias->name);
            mk_mem_free(host_alias);
        }

        /* Free error pages */
        mk_list_foreach_safe(head_error, tmp2, &host->error_pages) {
            ep = mk_list_entry(head_error, struct error_page, _head);
            mk_list_del(&ep->_head);
            mk_mem_free(ep->file);
            mk_mem_free(ep->real_path);
            mk_mem_free(ep);
        }

        mk_ptr_free(&host->documentroot);
        mk_mem_free(host->host_signature);
        mk_ptr_free(&host->header_host_signature);

        /* Free source configuration */
        if (host->config) mk_config_free(host->config);
        mk_mem_free(host);
    }
}


int mk_request_header_toc_parse(struct headers_toc *toc, const char *data, int len)
{
    int i = 0;
    int header_len;
    int colon;
    char *q;
    char *p = (char *) data;
    char *l = p;

    toc->length = 0;

    for (i = 0; l < (data + len) && p && i < MK_HEADERS_TOC_LEN; i++) {
        if (*p == '\r') goto out;

        /* Locate the colon character and the end of the line by CRLF */
        colon = -1;
        for (q = p; *q != 0x0D; ++q) {
            if (*q == ':' && colon == -1) {
                colon = (q - p);
            }
        }

        /* it must be a LF after CR */
        if (*(q + 1) != 0x0A) {
            return -1;
        }

        /*
         * Check if we reach the last header, take in count the first one can
         * be also the last.
         */
        if (data + len == (q - 1) && colon == -1) {
            break;
        }

        /*
         * By this version we force that after the colon must exists a white
         * space before the value field
         */
        if (*(p + colon + 1) != 0x20) {
            return -1;
        }

        /* Each header key must have a value */
        header_len = q - p - colon - 2;
        if (header_len == 0) {
            return -1;
        }

        /* Register the entry */
        toc->rows[i].init = p;
        toc->rows[i].end = q;
        toc->rows[i].status = 0;
        p = (q + mk_crlf.len);
        l = p;
        toc->length++;
    }

 out:
    return toc->length;
}


static int iasecc_parse_ef_atr(struct sc_card *card)
{
	struct sc_context *ctx = card->ctx;
	struct iasecc_private_data *pdata = (struct iasecc_private_data *) card->drv_data;
	struct iasecc_version *version = &pdata->version;
	struct iasecc_io_buffer_sizes *sizes = &pdata->max_sizes;
	int rv;

	LOG_FUNC_CALLED(ctx);
	rv = sc_parse_ef_atr(card);
	LOG_TEST_RET(ctx, rv, "MF selection error");

	if (card->ef_atr->pre_issuing_len < 4)
		LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid pre-issuing data");

	version->ic_manufacturer =	card->ef_atr->pre_issuing[0];
	version->ic_type =		card->ef_atr->pre_issuing[1];
	version->os_version =		card->ef_atr->pre_issuing[2];
	version->iasecc_version =	card->ef_atr->pre_issuing[3];
	sc_log(ctx, "EF.ATR: IC manufacturer/type %X/%X, OS/IasEcc versions %X/%X",
		version->ic_manufacturer, version->ic_type, version->os_version, version->iasecc_version);

	if (card->ef_atr->issuer_data_len < 16)
		LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid issuer data");

	sizes->send =	 card->ef_atr->issuer_data[2] * 0x100 + card->ef_atr->issuer_data[3];
	sizes->send_sc = card->ef_atr->issuer_data[6] * 0x100 + card->ef_atr->issuer_data[7];
	sizes->recv =	 card->ef_atr->issuer_data[10] * 0x100 + card->ef_atr->issuer_data[11];
	sizes->recv_sc = card->ef_atr->issuer_data[14] * 0x100 + card->ef_atr->issuer_data[15];

	card->max_send_size = sizes->send;
	card->max_recv_size = sizes->recv;

	/* Most of the card producers interpret 'send' values as "maximum APDU data size".
	 * Oberthur strictly follows specification and interpret these values as "maximum APDU command size".
	 * Here we need 'data size'.
	 */
	if (card->max_send_size > 0xFF)
		card->max_send_size -= 5;

	sc_log(ctx,
	       "EF.ATR: max send/recv sizes %"SC_FORMAT_LEN_SIZE_T"X/%"SC_FORMAT_LEN_SIZE_T"X",
	       card->max_send_size, card->max_recv_size);

	LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}


pgp_find_blob(sc_card_t *card, unsigned int tag)
{
	struct pgp_priv_data *priv = DRVDATA(card);
	pgp_blob_t *blob = NULL;
	int r;

	/* check if current selected blob is which we want to test */
	if (priv->current->id == tag) {
		return priv->current;
	}
	/* look for the blob representing the DO */
	r = pgp_seek_blob(card, priv->mf, tag, &blob);
	if (r < 0) {
		sc_log(card->ctx, "Failed to seek the blob representing the tag %04X. Error %d.", tag, r);
		return NULL;
	}
	return blob;
}


static void print_serial(sc_card_t *in_card)
{
	int r;
	sc_serial_number_t serial;

	r = sc_lock(card);
	if (r == SC_SUCCESS)
		r = sc_card_ctl(in_card, SC_CARDCTL_GET_SERIALNR, &serial);
	sc_unlock(card);
	if (r)
		fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_GET_SERIALNR, *) failed\n");
	else
		util_hex_dump_asc(stdout, serial.value, serial.len, -1);
}


static int asepcos_list_files(sc_card_t *card, u8 *buf, size_t blen)
{
	int       r, rv = 0, dfFID, efFID;
	sc_path_t bpath, tpath;
	sc_file_t *tfile = NULL;

	/* 1. get currently selected DF */
	r = asepcos_get_current_df_path(card, &bpath);
	if (r != SC_SUCCESS)
		return r;
	/* 2. re-select DF to get the FID of the child EFs/DFs */
	r = sc_select_file(card, &bpath, &tfile);
	if (r != SC_SUCCESS)
		return r;
	if (tfile->prop_attr_len != 6 || tfile->prop_attr == NULL) {
		sc_file_free(tfile);
		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to parse proprietary FCI attributes");
		return SC_ERROR_INTERNAL;
	}
	dfFID = (tfile->prop_attr[2] << 8) | tfile->prop_attr[3];
	efFID = (tfile->prop_attr[4] << 8) | tfile->prop_attr[5];
	sc_file_free(tfile);
	/* 3. select every child DF to get the FID of the next child DF */
	while (dfFID != 0) {
		/* put DF FID on the list */
		if (blen < 2)
			return SC_ERROR_BUFFER_TOO_SMALL;
		*buf++ = (dfFID >> 8) & 0xff;
		*buf++ = dfFID & 0xff;
		rv   += 2;
		blen -= 2;
		/* select DF to get next DF FID */
		tpath = bpath;
		r = sc_append_file_id(&tpath, dfFID);
		if (r != SC_SUCCESS)
			return r;
		r = sc_select_file(card, &tpath, &tfile);
		if (r != SC_SUCCESS)
			return r;
		if (tfile->prop_attr_len != 6 || tfile->prop_attr == NULL)
			return SC_ERROR_INTERNAL;
		dfFID = (tfile->prop_attr[0] << 8) | tfile->prop_attr[1];
		sc_file_free(tfile);
	}
	/* 4. select every child EF ... */
	while (efFID != 0) {
		/* put DF FID on the list */
		if (blen < 2)
			return SC_ERROR_BUFFER_TOO_SMALL;
		*buf++ = (efFID >> 8) & 0xff;
		*buf++ = efFID & 0xff;
		rv   += 2;
		blen -= 2;
		/* select EF to get next EF FID */
		tpath = bpath;
		r = sc_append_file_id(&tpath, efFID);
		if (r != SC_SUCCESS)
			return r;
		r = sc_select_file(card, &tpath, &tfile);
		if (r != SC_SUCCESS)
			return r;
		if (tfile->prop_attr_len < 2 || tfile->prop_attr == NULL)
			return SC_ERROR_INTERNAL;
		efFID = (tfile->prop_attr[0] << 8) | tfile->prop_attr[1];
		sc_file_free(tfile);
	}

	return rv;
}


static int piv_select_file(sc_card_t *card, const sc_path_t *in_path,
	sc_file_t **file_out)
{
	piv_private_data_t * priv = PIV_DATA(card);
	int r;
	int i;
	const u8 *path;
	int pathlen;
	sc_file_t *file = NULL;
	u8 * rbuf = NULL;
	size_t rbuflen = 0;

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	path = in_path->value;
	pathlen = in_path->len;

	/* only support single EF in current application */
	/*
	 * PIV emulates files, and only does so because sc_pkcs15_* uses
	 * select_file and read_binary. The emulation adds path emulated structures
	 * so piv_select_file will find it.
	 * there is no dir. Only direct access to emulated files
	 * thus opensc-tool and opensc-explorer can not read the emulated files
	 */

	if (memcmp(path, "\x3F\x00", 2) == 0) {
		if (pathlen > 2) {
			path += 2;
			pathlen -= 2;
		}
	}

	i = piv_find_obj_by_containerid(card, path);

	if (i < 0)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND);

	/*
	 * pkcs15 will use a 2 byte path or a 4 byte path
	 * with cece added to path to request only the cert from the cert obj
	 * PIV "Container ID" is used as the path, and are two bytes long
	 */
	priv->return_only_cert = (pathlen == 4 && path[2] == 0xce && path[3] == 0xce);

	priv->selected_obj = i;
	priv->rwb_state = -1;

	/* make it look like the file was found. */
	/* We don't want to read it now  unless we need the length */

	if (file_out) {
		/* we need to read it now, to get length into cache */
		r = piv_get_cached_data(card, i, &rbuf, &rbuflen);

		if (r < 0)
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND);

		/* get the cert or the pub key out and into the cache too */
		if (priv->return_only_cert || piv_objects[i].flags & PIV_OBJECT_TYPE_PUBKEY) {
			r = piv_cache_internal_data(card, i);
			if (r < 0)
				LOG_FUNC_RETURN(card->ctx, r);
		}

		file = sc_file_new();
		if (file == NULL)
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);

		file->path = *in_path;
		/* this could be like the FCI */
		file->type =  SC_FILE_TYPE_DF;
		file->shareable = 0;
		file->ef_structure = 0;
		if (priv->return_only_cert)
			file->size = priv->obj_cache[i].internal_obj_len;
		else
			file->size = priv->obj_cache[i].obj_len;

		file->id = (piv_objects[i].containerid[0]<<8) + piv_objects[i].containerid[1];

		*file_out = file;
	}

	LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);

}


authentic_match_card(struct sc_card *card)
{
	struct sc_context *ctx = card->ctx;
	int i;

	sc_log_hex(ctx, "try to match card with ATR", card->atr.value, card->atr.len);
	i = _sc_match_atr(card, authentic_known_atrs, &card->type);
	if (i < 0)   {
		sc_log(ctx, "card not matched");
		return 0;
	}

	sc_log(ctx, "'%s' card matched", authentic_known_atrs[i].name);
	return 1;
}


coolkey_get_cplc_data(sc_card_t *card, global_platform_cplc_data_t *cplc_data)
{
	size_t len = sizeof(global_platform_cplc_data_t);
	u8 *receive_buf = (u8 *)cplc_data;
	return coolkey_apdu_io(card, GLOBAL_PLATFORM_CLASS, ISO7816_INS_GET_DATA, 0x9f, 0x7f,
			NULL, 0, &receive_buf, &len,  NULL, 0);
}


static int entersafe_select_path(sc_card_t *card,
								const u8 pathbuf[16], const size_t len,
								sc_file_t **file_out)
{
	 u8 n_pathbuf[SC_MAX_PATH_SIZE];
	 const u8 *path=pathbuf;
	 size_t pathlen=len;
	 int bMatch = -1;
	 unsigned int i;
	 int r;

	 if (pathlen%2 != 0 || pathlen > 6 || pathlen <= 0)
		  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);

	 /* if pathlen == 6 then the first FID must be MF (== 3F00) */
	 if (pathlen == 6 && ( path[0] != 0x3f || path[1] != 0x00 ))
		  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);

	 /* unify path (the first FID should be MF) */
	 if (path[0] != 0x3f || path[1] != 0x00)
	 {
		  n_pathbuf[0] = 0x3f;
		  n_pathbuf[1] = 0x00;
		  for (i=0; i< pathlen; i++)
			   n_pathbuf[i+2] = pathbuf[i];
		  path = n_pathbuf;
		  pathlen += 2;
	 }

	 /* check current working directory */
	 if (card->cache.valid
		 && card->cache.current_path.type == SC_PATH_TYPE_PATH
		 && card->cache.current_path.len >= 2
		 && card->cache.current_path.len <= pathlen )
	 {
		  bMatch = 0;
		  for (i=0; i < card->cache.current_path.len; i+=2)
			   if (card->cache.current_path.value[i] == path[i]
				   && card->cache.current_path.value[i+1] == path[i+1] )
					bMatch += 2;
	 }

	 if ( card->cache.valid && bMatch > 2 )
	 {
		  if ( pathlen - bMatch == 2 )
		  {
			   /* we are in the right directory */
			   return entersafe_select_fid(card, path[bMatch], path[bMatch+1], file_out);
		  }
		  else if ( pathlen - bMatch > 2 )
		  {
			   /* two more steps to go */
			   sc_path_t new_path;

			   /* first step: change directory */
			   r = entersafe_select_fid(card, path[bMatch], path[bMatch+1], NULL);
			   SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "SELECT FILE (DF-ID) failed");

		   	   memset(&new_path, 0, sizeof(sc_path_t));

			   new_path.type = SC_PATH_TYPE_PATH;
			   new_path.len  = pathlen - bMatch-2;
			   memcpy(new_path.value, &(path[bMatch+2]), new_path.len);
			   /* final step: select file */
			   return entersafe_select_file(card, &new_path, file_out);
		  }
		  else /* if (bMatch - pathlen == 0) */
		  {
			   /* done: we are already in the
				* requested directory */
			   sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
				"cache hit\n");
			   /* copy file info (if necessary) */
			   if (file_out) {
					sc_file_t *file = sc_file_new();
					if (!file)
						 SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY);
					file->id = (path[pathlen-2] << 8) +
						 path[pathlen-1];
					file->path = card->cache.current_path;
					file->type = SC_FILE_TYPE_DF;
					file->ef_structure = SC_FILE_EF_UNKNOWN;
					file->size = 0;
					file->namelen = 0;
					file->magic = SC_FILE_MAGIC;
					*file_out = file;
			   }
			   /* nothing left to do */
			   return SC_SUCCESS;
		  }
	 }
	 else
	 {
		  /* no usable cache */
		  for ( i=0; i<pathlen-2; i+=2 )
		  {
			   r = entersafe_select_fid(card, path[i], path[i+1], NULL);
			   SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "SELECT FILE (DF-ID) failed");
		  }
		  return entersafe_select_fid(card, path[pathlen-2], path[pathlen-1], file_out);
	 }
}


iasecc_pin_verify(struct sc_card *card, unsigned type, unsigned reference,
		const unsigned char *data, size_t data_len, int *tries_left)
{
	struct sc_context *ctx = card->ctx;
	struct sc_pin_cmd_data pin_cmd;
	unsigned chv_ref = reference;
	int rv;

	LOG_FUNC_CALLED(ctx);
	sc_log(ctx,
	       "Verify PIN(type:%X,ref:%i,data(len:%"SC_FORMAT_LEN_SIZE_T"u,%p)",
	       type, reference, data_len, data);

	if (type == SC_AC_AUT)   {
		rv =  iasecc_sm_external_authentication(card, reference, tries_left);
		LOG_FUNC_RETURN(ctx, rv);
	}
	else if (type == SC_AC_SCB)   {
		if (reference & IASECC_SCB_METHOD_USER_AUTH)   {
			type = SC_AC_SEN;
			reference = reference & IASECC_SCB_METHOD_MASK_REF;
		}
		else   {
			sc_log(ctx, "Do not try to verify non CHV PINs");
			LOG_FUNC_RETURN(ctx, SC_SUCCESS);
		}
	}

	if (type == SC_AC_SEN)   {
		rv = iasecc_se_at_to_chv_reference(card, reference,  &chv_ref);
		LOG_TEST_RET(ctx, rv, "SE AT to CHV reference error");
	}

	memset(&pin_cmd, 0, sizeof(pin_cmd));
	pin_cmd.pin_type = SC_AC_CHV;
	pin_cmd.pin_reference = chv_ref;
	pin_cmd.cmd = SC_PIN_CMD_VERIFY;

	rv = iasecc_pin_get_policy(card, &pin_cmd);
	LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error");

	pin_cmd.pin1.data = data;
	pin_cmd.pin1.len = data_len;

	rv = iasecc_pin_is_verified(card, &pin_cmd, tries_left);
	if (data && !data_len)
		LOG_FUNC_RETURN(ctx, rv);

	if (!rv)   {
		if (iasecc_chv_cache_is_verified(card, &pin_cmd))
			LOG_FUNC_RETURN(ctx, SC_SUCCESS);
	}
	else if (rv != SC_ERROR_PIN_CODE_INCORRECT && rv != SC_ERROR_SECURITY_STATUS_NOT_SATISFIED)   {
		LOG_FUNC_RETURN(ctx, rv);
	}

	iasecc_chv_cache_clean(card, &pin_cmd);

	rv = iasecc_chv_verify(card, &pin_cmd, tries_left);
	LOG_TEST_RET(ctx, rv, "PIN CHV verification error");

	rv = iasecc_chv_cache_verified(card, &pin_cmd);

	LOG_FUNC_RETURN(ctx, rv);
}


pgp_get_card_features(sc_card_t *card)
{
	struct pgp_priv_data *priv = DRVDATA(card);
	u8 *hist_bytes = card->reader->atr_info.hist_bytes;
	size_t hist_bytes_len = card->reader->atr_info.hist_bytes_len;
	size_t i;
	pgp_blob_t *blob, *blob6e, *blob73;

	/* parse card capabilities from historical bytes in ATR */
	if (hist_bytes_len > 0) {
		/* category indicator 0x00, 0x10 or 0x80 => compact TLV (ISO) */
		switch (hist_bytes[0]) {
			case 0x00:
				pgp_parse_hist_bytes(card, hist_bytes+1, hist_bytes_len-4);
				break;
			case 0x80:
				pgp_parse_hist_bytes(card, hist_bytes+1, hist_bytes_len-1);
				break;
			case 0x10:
				pgp_parse_hist_bytes(card, hist_bytes+2, hist_bytes_len-2);
				break;
		}
	}

	/* v1.1 does not support lifecycle via ACTIVATE & TERMINATE: set default */
	priv->ext_caps &= ~EXT_CAP_LCS;

	if (priv->bcd_version >= OPENPGP_CARD_2_0) {
		/* get card capabilities from "historical bytes" DO */
		if ((pgp_get_blob(card, priv->mf, 0x5f52, &blob) >= 0) &&
		    (blob->data != NULL) && (blob->data[0] == 0x00)) {

			pgp_parse_hist_bytes(card, hist_bytes+1, hist_bytes_len-4);

			/* get card status from historical bytes status indicator */
			if ((blob->data[0] == 0x00) && (blob->len >= 4)) {
				priv->state = blob->data[blob->len-3];
				/* state not CARD_STATE_UNKNOWN => LCS supported */
				if (priv->state != CARD_STATE_UNKNOWN)
					priv->ext_caps |= EXT_CAP_LCS;
			}
		}
	}

	if (priv->bcd_version >= OPENPGP_CARD_3_1) {
		card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO;
	}

	if ((pgp_get_blob(card, priv->mf, 0x006e, &blob6e) >= 0) &&
	    (pgp_get_blob(card, blob6e, 0x0073, &blob73) >= 0)) {

		/* get "extended capabilities" DO */
		if ((pgp_get_blob(card, blob73, 0x00c0, &blob) >= 0) &&
		    (blob->data != NULL) && (blob->len > 0)) {
			/* v2.0+: bit 0x04 in first byte means "algorithm attributes changeable" */
			if ((blob->data[0] & 0x04) &&
					(priv->bcd_version >= OPENPGP_CARD_2_0))
				priv->ext_caps |= EXT_CAP_ALG_ATTR_CHANGEABLE;
			/* bit 0x08 in first byte means "support for private use DOs" */
			if (blob->data[0] & 0x08)
				priv->ext_caps |= EXT_CAP_PRIVATE_DO;
			/* bit 0x10 in first byte means "support for CHV status byte changeable" */
			if (blob->data[0] & 0x10)
				priv->ext_caps |= EXT_CAP_C4_CHANGEABLE;
			/* bit 0x20 in first byte means "support for Key Import" */
			if (blob->data[0] & 0x20)
				priv->ext_caps |= EXT_CAP_KEY_IMPORT;
			/* bit 0x40 in first byte means "support for Get Challenge" */
			if (blob->data[0] & 0x40) {
				card->caps |= SC_CARD_CAP_RNG;
				priv->ext_caps |= EXT_CAP_GET_CHALLENGE;
			}
			/* v2.0+: bit 0x80 in first byte means "support Secure Messaging" */
			if ((blob->data[0] & 0x80) &&
					(priv->bcd_version >= OPENPGP_CARD_2_0))
				priv->ext_caps |= EXT_CAP_SM;

			if ((priv->bcd_version >= OPENPGP_CARD_2_0) && (blob->len >= 10)) {
				/* v2.0+: max. challenge size is at bytes 3-4 */
				priv->max_challenge_size = bebytes2ushort(blob->data + 2);
				/* v2.0+: max. cert size it at bytes 5-6 */
				priv->max_cert_size = bebytes2ushort(blob->data + 4);

				if (priv->bcd_version < OPENPGP_CARD_3_0) {
					/* v2.x: SM algorithm is at byte 2: 0 == 3DES */
					priv->sm_algo = blob->data[0];
					if ((priv->sm_algo == SM_ALGO_NONE) && (priv->ext_caps & EXT_CAP_SM))
						priv->sm_algo = SM_ALGO_3DES;

					/* v2.x: max. send/receive sizes are at bytes 7-8 resp. 9-10 */
					card->max_send_size = bebytes2ushort(blob->data + 6);
					card->max_recv_size = bebytes2ushort(blob->data + 8);
				}
				else {
					/* v3.0+: SM algorithm is at byte 2: 0 == UNKNOWN */
					priv->sm_algo = blob->data[0];
					if ((priv->sm_algo == SM_ALGO_NONE) && (priv->ext_caps & EXT_CAP_SM))
						priv->sm_algo = SM_ALGO_UNKNOWN;
				}
			}
		}

		/* get max. PIN length from "CHV status bytes" DO */
		if ((pgp_get_blob(card, blob73, 0x00c4, &blob) >= 0) &&
			(blob->data != NULL) && (blob->len > 1)) {
			/* 2nd byte in "CHV status bytes" DO means "max. PIN length" */
			card->max_pin_len = blob->data[1];
		}

		/* get supported algorithms & key lengths from "algorithm attributes" DOs */
		for (i = 0x00c1; i <= 0x00c3; i++) {
			unsigned long flags;

			/* Is this correct? */
			/* OpenPGP card spec 1.1 & 2.x, section 7.2.9 & 7.2.10 / v3.x section 7.2.11 & 7.2.12 */
			flags = SC_ALGORITHM_RSA_PAD_PKCS1;
			flags |= SC_ALGORITHM_RSA_HASH_NONE;
			/* Can be generated in card */
			flags |= SC_ALGORITHM_ONBOARD_KEY_GEN;

			/* OpenPGP card spec 1.1 & 2.x section 4.3.3.6 / v3.x section 4.4.3.7 */
			if ((pgp_get_blob(card, blob73, i, &blob) >= 0) && (blob->data != NULL)) {
				if (blob->len >= 3 && blob->data[0] == 0x01) {	/* RSA [RFC 4880] */
					unsigned int keybits = bebytes2ushort(blob->data + 1);

					_sc_card_add_rsa_alg(card, keybits, flags, 0);
				}
				/* v3.0+: [RFC 4880 & 6637] 0x12 = ECDH, 0x13 = ECDSA */
			}
		}

		if (priv->bcd_version >= OPENPGP_CARD_3_0) {
			/* v3.0+: get length info from "extended length information" DO */
			if ((pgp_get_blob(card, blob6e, 0x7f66, &blob) >= 0) &&
				(blob->data != NULL) && (blob->len >= 8)) {
				/* kludge: treat as SIMPLE DO and use appropriate offsets */
				card->max_send_size = bebytes2ushort(blob->data + 2);
				card->max_recv_size = bebytes2ushort(blob->data + 6);
			}
		}
	}

	return SC_SUCCESS;
}


gpk_generate_key(sc_card_t *card, struct sc_cardctl_gpk_genkey *args)
{
	sc_apdu_t	apdu;
	int		r;
	u8		buffer[256];

	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
		"gpk_generate_key(%u)\n", args->privlen);
	if (args->privlen != 512 && args->privlen != 1024) {
		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
			"Key generation not supported for key length %d",
			args->privlen);
		return SC_ERROR_NOT_SUPPORTED;
	}

	memset(&apdu, 0, sizeof(apdu));
	apdu.cse = SC_APDU_CASE_2_SHORT;
	apdu.cla = 0x80;
	apdu.ins = 0xD2;
	apdu.p1  = 0x80 | (args->fid & 0x1F);
	apdu.p2  = (args->privlen == 1024) ? 0x11 : 0;
	apdu.le  = args->privlen / 8 + 2;
	apdu.resp = buffer;
	apdu.resplen = 256;

	r = sc_transmit_apdu(card, &apdu);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");

	/* Return the public key, inverted.
	 * The first two bytes must be stripped off. */
	if (args->pubkey_len && apdu.resplen > 2) {
		r = reverse(args->pubkey, args->pubkey_len,
				buffer + 2, apdu.resplen - 2);
		SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Failed to reverse buffer");
		args->pubkey_len = r;
	}

	return r;
}


gpk_select_file(sc_card_t *card, const sc_path_t *path,
		sc_file_t **file)
{
	unsigned short int	pathtmp[SC_MAX_PATH_SIZE/2];
	unsigned short int	*pathptr;
	size_t			pathlen, n;
	int			locked = 0, r = 0, use_relative = 0, retry = 1;
	u8			leaf_type;

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	/* Handle the AID case first */
	if (path->type == SC_PATH_TYPE_DF_NAME) {
		if (path->len > 16)
			return SC_ERROR_INVALID_ARGUMENTS;
		r = gpk_select(card, GPK_SEL_AID,
					path->value, path->len, file);
		goto done;
	}

	/* Now we know we're dealing with 16bit FIDs, either as
	 * an absolute path name (SC_PATH_TYPE_PATH) or a relative
	 * FID (SC_PATH_TYPE_FILE_ID)
	 *
	 * The API should really tell us whether this is a DF or EF
	 * we're selecting. All we can do is read tea leaves...
	 */
	leaf_type = GPK_SEL_EF;

try_again:
	if ((path->len & 1) || path->len > sizeof(pathtmp))
		return SC_ERROR_INVALID_ARGUMENTS;
	pathptr = pathtmp;
	for (n = 0; n < path->len; n += 2)
		pathptr[n>>1] = (path->value[n] << 8)|path->value[n+1];
	pathlen = path->len >> 1;

	/* See whether we can skip an initial portion of the
	 * (absolute) path */
	if (path->type == SC_PATH_TYPE_PATH) {
		/* Do not retry selecting if this cannot be a DF */
		if ((pathptr[0] == GPK_FID_MF && pathlen > 2)
		 || (pathptr[0] != GPK_FID_MF && pathlen > 1))
			retry = 0;
		use_relative = match_path(card, &pathptr, &pathlen, file != 0);
		if (pathlen == 0)
			goto done;
	} else {
		/* SC_PATH_TYPE_FILEID */
		if (pathlen > 1)
			return SC_ERROR_INVALID_ARGUMENTS;
		use_relative = 1;
	}

	if (pathlen == 1 && pathptr[0] == GPK_FID_MF) {
		/* Select just the MF */
		leaf_type = GPK_SEL_MF;
	} else {
		if (!locked++) {
			r = sc_lock(card);
			SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_lock() failed");
		}

		/* Do we need to select the MF first? */
		if (!use_relative) {
			r = gpk_select_id(card, GPK_SEL_MF, GPK_FID_MF, NULL);
			if (r)
				sc_unlock(card);
			SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select MF");

			/* Consume the MF FID if it's there */
			if (pathptr[0] == GPK_FID_MF) {
				pathptr++;
				pathlen--;
			}
			if (pathlen == 0)
				goto done;
		}

		/* Next comes a DF, if at all.
		 * This loop can deal with nesting levels > 1 even
		 * though the GPK4000 doesn't support it. */
		while (pathlen > 1) {
			r = gpk_select_id(card, GPK_SEL_DF, pathptr[0], NULL);
			if (r)
				sc_unlock(card);
			SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Unable to select DF");
			pathptr++;
			pathlen--;
		}
	}

	/* Remaining component will be a DF or EF. How do we find out?
	 * All we can do is try */
	r = gpk_select_id(card, leaf_type, pathptr[0], file);
	if (r) {
		/* Did we guess EF, and were wrong? If so, invalidate
		 * path cache and try again; this time aiming for a DF */
		if (leaf_type == GPK_SEL_EF && retry) {
			card->cache.current_path.len = 0;
			leaf_type = GPK_SEL_DF;
			goto try_again;
		}
	}

done:
	if (locked)
		sc_unlock(card);
	return r;
}


static int rtecp_cipher(sc_card_t *card, const u8 *data, size_t data_len,
		u8 *out, size_t out_len, int sign)
{
	sc_apdu_t apdu;
	u8 *buf, *buf_out;
	size_t i;
	int r;

	assert(card && card->ctx && data && out);
	buf_out = malloc(out_len + 2);
	buf = malloc(data_len);
	if (!buf || !buf_out)
	{
		free(buf);
		free(buf_out);
		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY);
	}

	for (i = 0; i < data_len; ++i)
		buf[i] = data[data_len - 1 - i];

	if (sign)
		sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A);
	else
		sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86);
	apdu.lc = data_len;
	apdu.data = buf;
	apdu.datalen = data_len;
	apdu.resp = buf_out;
	apdu.resplen = out_len + 2;
	apdu.le = out_len > 256 ? 256 : out_len;
	if (apdu.lc > 255)
		apdu.flags |= SC_APDU_FLAGS_CHAINING;
	r = sc_transmit_apdu(card, &apdu);
	if (!sign)
	{
		assert(buf);
		sc_mem_clear(buf, data_len);
	}
	assert(buf);
	free(buf);
	if (r)
		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "APDU transmit failed: %s\n", sc_strerror(r));
	else
	{
		if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)
		{
			assert(buf_out);
			for (i = 0; i < apdu.resplen; ++i)
				out[i] = buf_out[apdu.resplen - 1 - i];
			r = (i > 0) ? (int)i : SC_ERROR_INTERNAL;
		}
		else
			r = sc_check_sw(card, apdu.sw1, apdu.sw2);
	}
	if (!sign)
	{
		assert(buf_out);
		sc_mem_clear(buf_out, out_len + 2);
	}
	assert(buf_out);
	free(buf_out);
	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);

}


pgp_calculate_and_store_fingerprint(sc_card_t *card, time_t ctime,
                                    u8* modulus, u8* exponent,
                                    sc_cardctl_openpgp_keygen_info_t *key_info)
{
	u8 fingerprint[SHA_DIGEST_LENGTH];
	size_t mlen = key_info->modulus_len >> 3;  /* 1/8 */
	size_t elen = key_info->exponent_len >> 3;  /* 1/8 */
	u8 *fp_buffer = NULL;  /* fingerprint buffer, not hashed */
	size_t fp_buffer_len;
	u8 *p; /* use this pointer to set fp_buffer content */
	size_t pk_packet_len;
	unsigned int tag;
	pgp_blob_t *fpseq_blob;
	u8 *newdata;
	int r;

	LOG_FUNC_CALLED(card->ctx);

	if (modulus == NULL || exponent == NULL || mlen == 0 || elen == 0) {
		sc_log(card->ctx, "Null data (modulus or exponent)");
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
	}

	/* http://tools.ietf.org/html/rfc4880  page 41, 72 */
	pk_packet_len =   1   /* version number */
	                + 4   /* creation time */
	                + 1   /* algorithm */
	                + 2   /* algorithm-specific fields: RSA modulus+exponent */
	                + mlen
	                + 2
	                + elen;

	fp_buffer_len = 3 + pk_packet_len;
	p = fp_buffer = calloc(fp_buffer_len, 1);
	if (!p) {
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
	}

	p[0] = 0x99;   /* http://tools.ietf.org/html/rfc4880  page 71 */
	ushort2bebytes(++p, (unsigned short)pk_packet_len);
	/* start pk_packet */
	p += 2;
	*p = 4;        /* Version 4 key */
	ulong2bebytes(++p, (unsigned long)ctime);    /* Creation time */
	p += 4;
	*p = 1;        /* RSA */
	/* algorithm-specific fields */
	ushort2bebytes(++p, (unsigned short)key_info->modulus_len);
	p += 2;
	memcpy(p, modulus, mlen);
	p += mlen;
	ushort2bebytes(++p, (unsigned short)key_info->exponent_len);
	p += 2;
	memcpy(p, exponent, elen);
	p = NULL;

	/* hash with SHA-1 */
	SHA1(fp_buffer, fp_buffer_len, fingerprint);
	free(fp_buffer);

	/* store to DO */
	tag = 0x00C6 + key_info->keytype;
	sc_log(card->ctx, "Write to DO %04X.", tag);
	r = pgp_put_data(card, 0x00C6 + key_info->keytype, fingerprint, SHA_DIGEST_LENGTH);
	LOG_TEST_RET(card->ctx, r, "Cannot write to DO.");

	/* update the blob containing fingerprints (00C5) */
	sc_log(card->ctx, "Update the blob containing fingerprints (00C5)");
	fpseq_blob = pgp_find_blob(card, 0x00C5);
	if (!fpseq_blob) {
		sc_log(card->ctx, "Not found 00C5");
		goto exit;
	}
	/* save the fingerprints sequence */
	newdata = malloc(fpseq_blob->len);
	if (!newdata) {
		sc_log(card->ctx, "Not enough memory to update fingerprints blob.");
		goto exit;
	}
	memcpy(newdata, fpseq_blob->data, fpseq_blob->len);
	/* move p to the portion holding the fingerprint of the current key */
	p = newdata + 20*(key_info->keytype - 1);
	/* copy new fingerprint value */
	memcpy(p, fingerprint, 20);
	/* set blob's data */
	pgp_set_blob(fpseq_blob, newdata, fpseq_blob->len);
	free(newdata);

exit:
	LOG_FUNC_RETURN(card->ctx, r);
}


static int entersafe_create_file(sc_card_t *card, sc_file_t *file)
{
	 SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	 if (file->type == SC_FILE_TYPE_WORKING_EF) {
		  sc_entersafe_create_data data;
		  memset(&data,0,sizeof(data));

		  data.data.ef.file_id[0] = (file->id>>8)&0xFF;
		  data.data.ef.file_id[1] = file->id&0xFF;
		  data.data.ef.size[0] = (file->size>>8)&0xFF;
		  data.data.ef.size[1] = file->size&0xFF;
		  memset(data.data.ef.ac,ENTERSAFE_AC_ALWAYS,sizeof(data.data.ef.ac));
		  data.data.ef.ac[0] = process_acl_entry(file,SC_AC_OP_READ,ENTERSAFE_AC_ALWAYS);
		  data.data.ef.ac[1] = process_acl_entry(file,SC_AC_OP_UPDATE,ENTERSAFE_AC_ALWAYS);

		  return entersafe_create_ef(card, &data);
	 } else
		  return SC_ERROR_INVALID_ARGUMENTS;
}


static int asepcos_set_sec_attributes(sc_card_t *card, const u8 *data, size_t len,
	int is_ef)
{
	int r, type = is_ef != 0 ? 0x02 : 0x04;
	sc_apdu_t apdu;

	sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x8a, type, 0xab);
	apdu.cla    |= 0x80;
	apdu.lc      = len;
	apdu.datalen = len;
	apdu.data    = data;
	r = sc_transmit_apdu(card, &apdu);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
	return sc_check_sw(card, apdu.sw1, apdu.sw2);
}


authentic_select_mf(struct sc_card *card, struct sc_file **file_out)
{
	struct sc_context *ctx = card->ctx;
	struct sc_path mfpath;
	int rv;

	struct sc_apdu apdu;
	unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE];

	LOG_FUNC_CALLED(ctx);

	sc_format_path("3F00", &mfpath);
	mfpath.type = SC_PATH_TYPE_PATH;

	if (card->cache.valid == 1
			&& card->cache.current_df
			&& card->cache.current_df->path.len == 2
			&& !memcmp(card->cache.current_df->path.value, "\x3F\x00", 2))   {
		if (file_out)
			sc_file_dup(file_out, card->cache.current_df);

		LOG_FUNC_RETURN(ctx, SC_SUCCESS);
	}

	sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0x00, 0x00);

	apdu.resp = rbuf;
	apdu.resplen = sizeof(rbuf);

	rv = sc_transmit_apdu(card, &apdu);
	LOG_TEST_RET(ctx, rv, "APDU transmit failed");
	rv = sc_check_sw(card, apdu.sw1, apdu.sw2);
	LOG_TEST_RET(ctx, rv, "authentic_select_file() check SW failed");

	if (card->cache.valid == 1)   {
		sc_file_free(card->cache.current_df);
		card->cache.current_df = NULL;

		sc_file_free(card->cache.current_ef);
		card->cache.current_ef = NULL;
	}

	rv = authentic_set_current_files(card, &mfpath, apdu.resp, apdu.resplen, file_out);
	LOG_TEST_RET(ctx, rv, "authentic_select_file() cannot set 'current_file'");

	LOG_FUNC_RETURN(ctx, rv);
}


iasecc_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2)
{
	if (sw1 == 0x62 && sw2 == 0x82)
		return SC_SUCCESS;

	return iso_ops->check_sw(card, sw1, sw2);
}


gnuk_delete_key(sc_card_t *card, u8 key_id)
{
	sc_context_t *ctx = card->ctx;
	int r = SC_SUCCESS;
	char *data = NULL;

	LOG_FUNC_CALLED(ctx);

	if (key_id < 1 || key_id > 3) {
		sc_log(ctx, "Key ID %d is invalid. Should be 1, 2 or 3.", key_id);
		LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
	}

	/* delete fingerprint */
	sc_log(ctx, "Delete fingerprints");
	r = pgp_put_data(card, 0xC6 + key_id, NULL, 0);
	LOG_TEST_RET(ctx, r, "Failed to delete fingerprints");
	/* delete creation time */
	sc_log(ctx, "Delete creation time");
	r = pgp_put_data(card, 0xCD + key_id, NULL, 0);
	LOG_TEST_RET(ctx, r, "Failed to delete creation time");

	/* rewrite Extended Header List */
	sc_log(ctx, "Rewrite Extended Header List");

	if (key_id == 1)
		data = "\x4D\x02\xB6";
	else if (key_id == 2)
		data = "\x4D\x02\xB8";
	else if (key_id == 3)
		data = "\x4D\x02\xA4";

	r = pgp_put_data(card, 0x4D, (const u8 *)data, strlen((const char *)data) + 1);

	LOG_FUNC_RETURN(ctx, r);
}


iasecc_qsign_data_sha1(struct sc_context *ctx, const unsigned char *in, size_t in_len,
				struct iasecc_qsign_data *out)
{
	SHA_CTX sha;
	SHA_LONG pre_hash_Nl, *hh[5] = {
		&sha.h0, &sha.h1, &sha.h2, &sha.h3, &sha.h4
	};
	int jj, ii;
	int hh_size = sizeof(SHA_LONG), hh_num = SHA_DIGEST_LENGTH / sizeof(SHA_LONG);

	LOG_FUNC_CALLED(ctx);

	if (!in || !in_len || !out)
		LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);

	sc_log(ctx,
	       "sc_pkcs15_get_qsign_data() input data length %"SC_FORMAT_LEN_SIZE_T"u",
	       in_len);
	memset(out, 0, sizeof(struct iasecc_qsign_data));

	SHA1_Init(&sha);
	SHA1_Update(&sha, in, in_len);

	for (jj=0; jj<hh_num; jj++)
		for(ii=0; ii<hh_size; ii++)
			out->pre_hash[jj*hh_size + ii] = ((*hh[jj] >> 8*(hh_size-1-ii)) & 0xFF);
	out->pre_hash_size = SHA_DIGEST_LENGTH;
	sc_log(ctx, "Pre SHA1:%s", sc_dump_hex(out->pre_hash, out->pre_hash_size));

	pre_hash_Nl = sha.Nl - (sha.Nl % (sizeof(sha.data) * 8));
	for (ii=0; ii<hh_size; ii++)   {
		out->counter[ii] = (sha.Nh >> 8*(hh_size-1-ii)) &0xFF;
		out->counter[hh_size+ii] = (pre_hash_Nl >> 8*(hh_size-1-ii)) &0xFF;
	}
	for (ii=0, out->counter_long=0; ii<(int)sizeof(out->counter); ii++)
		out->counter_long = out->counter_long*0x100 + out->counter[ii];
	sc_log(ctx, "Pre counter(%li):%s", out->counter_long, sc_dump_hex(out->counter, sizeof(out->counter)));

	if (sha.num)   {
		memcpy(out->last_block, in + in_len - sha.num, sha.num);
		out->last_block_size = sha.num;
		sc_log(ctx, "Last block(%"SC_FORMAT_LEN_SIZE_T"u):%s",
		       out->last_block_size,
		       sc_dump_hex(out->last_block, out->last_block_size));
	}

	SHA1_Final(out->hash, &sha);
	out->hash_size = SHA_DIGEST_LENGTH;
	sc_log(ctx, "Expected digest %s\n", sc_dump_hex(out->hash, out->hash_size));

	LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}


static int coolkey_initialize(sc_card_t *card)
{
	int r;
	coolkey_private_data_t *priv = NULL;
	coolkey_life_cycle_t life_cycle;
	coolkey_object_info_t object_info;
	int combined_processed = 0;

	/* already found? */
	if (card->drv_data) {
		return SC_SUCCESS;
	}
	sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"Coolkey Applet found");

	priv = coolkey_new_private_data();
	if (priv == NULL) {
		r = SC_ERROR_OUT_OF_MEMORY;
		goto cleanup;
	}
	r = coolkey_get_life_cycle(card, &life_cycle);
	if (r < 0) {
		goto cleanup;
	}

	/* Select a coolkey read the coolkey objects out */
	r = coolkey_select_applet(card);
	if (r < 0) {
		goto cleanup;
	}

	priv->protocol_version_major = life_cycle.protocol_version_major;
	priv->protocol_version_minor = life_cycle.protocol_version_minor;
	priv->pin_count = life_cycle.pin_count;
	priv->life_cycle = life_cycle.life_cycle;

	/* walk down the list of objects and read them off the token */
	for(r=coolkey_list_object(card, COOLKEY_LIST_RESET, &object_info); r >= 0;
		r= coolkey_list_object(card, COOLKEY_LIST_NEXT, &object_info)) {
		unsigned long object_id = bebytes2ulong(object_info.object_id);
		unsigned short object_len = bebytes2ulong(object_info.object_length);
	    /* also look at the ACL... */

		/* the combined object is a single object that can store the other objects.
		 * most coolkeys provisioned by TPS has a single combined object that is
		 * compressed greatly increasing the effectiveness of compress (since lots
		 * of certs on the token share the same Subject and Issuer DN's). We now
		 * process it separately so that we can have both combined objects managed
		 * by TPS and user managed certs on the same token */
		if (object_id == COOLKEY_COMBINED_OBJECT_ID) {
			u8 *object = malloc(object_len);
			if (object == NULL) {
				r = SC_ERROR_OUT_OF_MEMORY;
				break;
			}
			r = coolkey_read_object(card, COOLKEY_COMBINED_OBJECT_ID, 0, object, object_len,
											priv->nonce, sizeof(priv->nonce));
			if (r < 0) {
				free(object);
				break;
			}
			r = coolkey_process_combined_object(card, priv, object, r);
			free(object);
			if (r != SC_SUCCESS) {
				break;
			}
			combined_processed = 1;
			continue;
		}
		r = coolkey_add_object(priv, object_id, NULL, object_len, 0);
		if (r != SC_SUCCESS)
			sc_log(card->ctx, "coolkey_add_object() returned %d", r);

	}
	if (r != SC_ERROR_FILE_END_REACHED) {
		goto cleanup;
	}
	/* if we didn't pull the cuid from the combined object, then grab it now */
	if (!combined_processed) {
		global_platform_cplc_data_t cplc_data;
		/* select the card manager, because a card with applet only will have
		   already selected the coolkey applet */

		r = gp_select_card_manager(card);
		if (r < 0) {
			goto cleanup;
		}

		r = coolkey_get_cplc_data(card, &cplc_data);
		if (r < 0) {
			goto cleanup;
		}
		coolkey_make_cuid_from_cplc(&priv->cuid, &cplc_data);
		priv->token_name = (u8 *)strdup("COOLKEY");
		if (priv->token_name == NULL) {
			r= SC_ERROR_OUT_OF_MEMORY;
			goto cleanup;
		}
		priv->token_name_length = sizeof("COOLKEY")-1;
	}
	card->drv_data = priv;
	return SC_SUCCESS;

cleanup:
	if (priv) {
		coolkey_free_private_data(priv);
	}
	return r;
}


sc_get_gpk_driver(void)
{
	return sc_get_driver();
}


authentic_manage_sdo_encode_prvkey(struct sc_card *card, struct sc_pkcs15_prkey *prvkey,
			unsigned char **out, size_t *out_len)
{
	struct sc_context *ctx = card->ctx;
	struct sc_pkcs15_prkey_rsa rsa;
	unsigned char *blob = NULL, *blob01 = NULL;
	size_t blob_len = 0, blob01_len = 0;
	int rv;

	if (!prvkey || !out || !out_len)
		LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid arguments");
	if (prvkey->algorithm != SC_ALGORITHM_RSA)
		LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid SDO operation");

	rsa = prvkey->u.rsa;
	/* Encode private RSA key part */
	rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_P, rsa.p.data, rsa.p.len, &blob, &blob_len);
	LOG_TEST_RET(ctx, rv, "SDO RSA P encode error");

	rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_Q, rsa.q.data, rsa.q.len, &blob, &blob_len);
	LOG_TEST_RET(ctx, rv, "SDO RSA Q encode error");

	rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_PQ, rsa.iqmp.data, rsa.iqmp.len, &blob, &blob_len);
	LOG_TEST_RET(ctx, rv, "SDO RSA PQ encode error");

	rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_DP1, rsa.dmp1.data, rsa.dmp1.len, &blob, &blob_len);
	LOG_TEST_RET(ctx, rv, "SDO RSA DP1 encode error");

	rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE_DQ1, rsa.dmq1.data, rsa.dmq1.len, &blob, &blob_len);
	LOG_TEST_RET(ctx, rv, "SDO RSA DQ1 encode error");

	rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PRIVATE, blob, blob_len, &blob01, &blob01_len);
	LOG_TEST_RET(ctx, rv, "SDO RSA Private encode error");

	free (blob);
	blob = NULL;
	blob_len = 0;

	/* Encode public RSA key part */
	sc_log(ctx,
	       "modulus.len:%"SC_FORMAT_LEN_SIZE_T"u blob_len:%"SC_FORMAT_LEN_SIZE_T"u",
	       rsa.modulus.len, blob_len);
	rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PUBLIC_MODULUS, rsa.modulus.data, rsa.modulus.len, &blob, &blob_len);
	LOG_TEST_RET(ctx, rv, "SDO RSA Modulus encode error");

	sc_log(ctx,
	       "exponent.len:%"SC_FORMAT_LEN_SIZE_T"u blob_len:%"SC_FORMAT_LEN_SIZE_T"u",
	       rsa.exponent.len, blob_len);
	rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PUBLIC_EXPONENT, rsa.exponent.data, rsa.exponent.len, &blob, &blob_len);
	LOG_TEST_RET(ctx, rv, "SDO RSA Exponent encode error");

	rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA_PUBLIC, blob, blob_len, &blob01, &blob01_len);
	LOG_TEST_RET(ctx, rv, "SDO RSA Private encode error");

	free (blob);

	rv = authentic_update_blob(ctx, AUTHENTIC_TAG_RSA, blob01, blob01_len, out, out_len);
	LOG_TEST_RET(ctx, rv, "SDO RSA encode error");

	free(blob01);

	LOG_FUNC_RETURN(ctx, rv);
}


static coolkey_private_data_t *coolkey_new_private_data(void)
{
	coolkey_private_data_t *priv;
	/* allocate priv and zero all the fields */
	priv = calloc(1, sizeof(coolkey_private_data_t));
	if (!priv)
		return NULL;
	/* set other fields as appropriate */
	priv->key_id = COOLKEY_INVALID_KEY;
	list_init(&priv->objects_list);
	list_attributes_comparator(&priv->objects_list, coolkey_compare_id);
	list_attributes_copy(&priv->objects_list, coolkey_list_meter, 1);

	return priv;
}


static int asepcos_create_file(sc_card_t *card, sc_file_t *file)
{
	if (file->type == SC_FILE_TYPE_DF) {
		int r, type;
		sc_apdu_t apdu;
		u8  sbuf[SC_MAX_APDU_BUFFER_SIZE], *p = &sbuf[0];

		*p++ = (file->id >> 8) & 0xff;
		*p++ = file->id & 0xff;
		if (file->size > 0xffff) {
			*p++ = (file->size >> 24) & 0xff;
			*p++ = (file->size >> 16) & 0xff;
			*p++ = (file->size >> 8 ) & 0xff;
			*p++ = file->size & 0xff;
			type = 1;
		} else {
			*p++ = (file->size >> 8) & 0xff;
			*p++ = file->size & 0xff;
			type = 0;
		}
		if (file->namelen != 0 && file->namelen <= 16) {
			memcpy(p, file->name, file->namelen);
			p += file->namelen;
		}
		sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xe0, 0x38, type);
		apdu.cla    |= 0x80;
		apdu.lc      = p - sbuf;
		apdu.datalen = p - sbuf;
		apdu.data    = sbuf;

		r = sc_transmit_apdu(card, &apdu);
		SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
		if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
			return sc_check_sw(card, apdu.sw1, apdu.sw2);

		r = sc_select_file(card, &file->path, NULL);
		if (r != SC_SUCCESS)
			return r;
		/* set security attributes */
		r = asepcos_set_security_attributes(card, file);
		if (r != SC_SUCCESS) {
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to set security attributes");
			return r;
		}
		return SC_SUCCESS;
	} else if (file->type == SC_FILE_TYPE_WORKING_EF) {
		int r;
		sc_apdu_t apdu;
		u8  descr_byte = file->ef_structure & 7;
		u8  sbuf[SC_MAX_APDU_BUFFER_SIZE], *p = &sbuf[0];

		*p++ = 0x85;
		p++;
		/* file id  */
		*p++ = (file->id >> 8) & 0xff;
		*p++ = file->id & 0xff;
		/* record size */
		if (file->ef_structure == SC_FILE_EF_TRANSPARENT) {
			*p++ = 0x00;
			*p++ = 0x00;
		} else {
			*p++ = (file->record_length >> 8) & 0xff;
			*p++ = file->record_length & 0xff;
		}
		/* number of records or file size */
		if (file->ef_structure == SC_FILE_EF_TRANSPARENT) {
			*p++ = (file->size >> 8) & 0xff;
			*p++ = file->size & 0xff;
		} else {
			*p++ = (file->record_count >> 8) & 0xff;
			*p++ = file->record_count & 0xff;
		}
		/* set the length of the inner TLV object */
		sbuf[1] = p - sbuf - 2;		/* FIXME */

		sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xe0, descr_byte, 0x00);
		apdu.lc      = p - sbuf;
		apdu.datalen = p - sbuf;
		apdu.data    = sbuf;
		r = sc_transmit_apdu(card, &apdu);
		SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
		if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
			return sc_check_sw(card, apdu.sw1, apdu.sw2);

		/* set security attributes */
		r = asepcos_set_security_attributes(card, file);
		if (r != SC_SUCCESS) {
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to set security attributes");
			return r;
		}
		return asepcos_activate_file(card, file->id, 1);
	} else if (file->type == SC_FILE_TYPE_INTERNAL_EF) {
		/* for internal EF we 'misuse' the prop_attr field of the
		 * sc_file_t object to store the data send to the card in
		 * the CREATE EF call.
		 */
		int r, atype = SC_APDU_CASE_3_SHORT;
		sc_apdu_t apdu;

		if (file->prop_attr_len > 255)
			atype = SC_APDU_CASE_3_EXT;

		sc_format_apdu(card, &apdu, atype, 0xe0, 0x08, 0x00);
		apdu.lc      = file->prop_attr_len;
		apdu.datalen = file->prop_attr_len;
		apdu.data    = file->prop_attr;

		r = sc_transmit_apdu(card, &apdu);
		SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
		if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
			return sc_check_sw(card, apdu.sw1, apdu.sw2);
		/* set security attributes */
		r = asepcos_set_security_attributes(card, file);
		if (r != SC_SUCCESS) {
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to set security attributes");
			return r;
		}
		return asepcos_activate_file(card, file->id, 1);
	} else
		return SC_ERROR_INVALID_ARGUMENTS;
}


static int entersafe_create_df(sc_card_t *card, sc_entersafe_create_data * data)
{
	int r;
	sc_apdu_t apdu;

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	memcpy(data->data.df.init_key, init_key, sizeof(init_key));

	sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xE0,0x01,0x00);
	apdu.cla=0x84;
	apdu.data=(u8*)&data->data.df;
	apdu.lc=apdu.datalen=sizeof(data->data.df);

	r = entersafe_transmit_apdu(card, &apdu,init_key,sizeof(init_key),0,1);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
	return sc_check_sw(card, apdu.sw1, apdu.sw2);
}


iasecc_pin_change(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left)
{
	struct sc_context *ctx = card->ctx;
	struct sc_apdu apdu;
	unsigned reference = data->pin_reference;
	unsigned char pin_data[0x100];
	int rv;

	LOG_FUNC_CALLED(ctx);
	sc_log(ctx, "Change PIN(ref:%i,type:0x%X,lengths:%i/%i)", reference, data->pin_type, data->pin1.len, data->pin2.len);

	if ((card->reader->capabilities & SC_READER_CAP_PIN_PAD))   {
		if (!data->pin1.data && !data->pin1.len && !data->pin2.data && !data->pin2.len)   {
			rv = iasecc_chv_change_pinpad(card, reference, tries_left);
			sc_log(ctx, "iasecc_pin_cmd(SC_PIN_CMD_CHANGE) chv_change_pinpad returned %i", rv);
			LOG_FUNC_RETURN(ctx, rv);
		}
	}

	if (!data->pin1.data && data->pin1.len)
		LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid PIN1 arguments");

	if (!data->pin2.data && data->pin2.len)
		LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid PIN2 arguments");

	rv = iasecc_pin_verify(card, data->pin_type, reference, data->pin1.data, data->pin1.len, tries_left);
	sc_log(ctx, "iasecc_pin_cmd(SC_PIN_CMD_CHANGE) pin_verify returned %i", rv);
	LOG_TEST_RET(ctx, rv, "PIN verification error");

	if ((unsigned)(data->pin1.len + data->pin2.len) > sizeof(pin_data))
		LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Buffer too small for the 'Change PIN' data");

	if (data->pin1.data)
		memcpy(pin_data, data->pin1.data, data->pin1.len);
	if (data->pin2.data)
		memcpy(pin_data + data->pin1.len, data->pin2.data, data->pin2.len);

	sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0, reference);
	apdu.data = pin_data;
	apdu.datalen = data->pin1.len + data->pin2.len;
	apdu.lc = apdu.datalen;

	rv = sc_transmit_apdu(card, &apdu);
	LOG_TEST_RET(ctx, rv, "APDU transmit failed");
	rv = sc_check_sw(card, apdu.sw1, apdu.sw2);
	LOG_TEST_RET(ctx, rv, "PIN cmd failed");

	LOG_FUNC_RETURN(ctx, rv);
}


pgp_parse_hist_bytes(sc_card_t *card, u8 *ctlv, size_t ctlv_len)
{
	struct pgp_priv_data *priv = DRVDATA(card);
	const u8 *ptr;

	/* IS07816-4 hist bytes: 3rd function table */
	if ((ptr = sc_compacttlv_find_tag(ctlv, ctlv_len, 0x73, NULL)) != NULL) {
		/* bit 0x40 in byte 3 of TL 0x73 means "extended Le/Lc" */
		if (ptr[2] & 0x40) {
			card->caps |= SC_CARD_CAP_APDU_EXT;
			priv->ext_caps |= EXT_CAP_APDU_EXT;
		}
		/* bit 0x80 in byte 3 of TL 0x73 means "Command chaining" */
		if ((ptr[2] & 0x80) &&
		    (priv->bcd_version >= OPENPGP_CARD_3_0)) {
			priv->ext_caps |= EXT_CAP_CHAINING;
		}
	}

	if ((priv->bcd_version >= OPENPGP_CARD_3_0) &&
	    ((ptr = sc_compacttlv_find_tag(ctlv, ctlv_len, 0x31, NULL)) != NULL)) {
		// ToDo ...
	}
}


static int rtecp_construct_fci(sc_card_t *card, const sc_file_t *file,
		u8 *out, size_t *outlen)
{
	u8 buf[64], *p = out;

	assert(card && card->ctx && file && out && outlen);
	assert(*outlen  >=  (size_t)(p - out) + 2);
	*p++ = 0x6F; /* FCI template */
	p++; /* for length */

	/* 0x80 - Number of data bytes in the file, excluding structural information */
	buf[0] = (file->size >> 8) & 0xFF;
	buf[1] = file->size & 0xFF;
	sc_asn1_put_tag(0x80, buf, 2, p, *outlen - (p - out), &p);

	/* 0x82 - File descriptor byte */
	if (file->type_attr_len)
	{
		assert(sizeof(buf) >= file->type_attr_len);
		memcpy(buf, file->type_attr, file->type_attr_len);
		sc_asn1_put_tag(0x82, buf, file->type_attr_len,
				p, *outlen - (p - out), &p);
	}
	else
	{
		switch (file->type)
		{
		case SC_FILE_TYPE_WORKING_EF:
			buf[0] = 0x01;
			break;
		case SC_FILE_TYPE_DF:
			buf[0] = 0x38;
			break;
		case SC_FILE_TYPE_INTERNAL_EF:
		default:
			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED);
		}
		buf[1] = 0;
		sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p);
	}
	/* 0x83 - File identifier */
	buf[0] = (file->id >> 8) & 0xFF;
	buf[1] = file->id & 0xFF;
	sc_asn1_put_tag(0x83, buf, 2, p, *outlen - (p - out), &p);

	if (file->prop_attr_len)
	{
		assert(sizeof(buf) >= file->prop_attr_len);
		memcpy(buf, file->prop_attr, file->prop_attr_len);
		sc_asn1_put_tag(0x85, buf, file->prop_attr_len,
				p, *outlen - (p - out), &p);
	}
	if (file->sec_attr_len)
	{
		assert(sizeof(buf) >= file->sec_attr_len);
		memcpy(buf, file->sec_attr, file->sec_attr_len);
		sc_asn1_put_tag(0x86, buf, file->sec_attr_len,
				p, *outlen - (p - out), &p);
	}
	out[1] = p - out - 2; /* length */
	*outlen = p - out;
	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 0);
}


authentic_pin_change(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left)
{
	struct sc_context *ctx = card->ctx;
	struct authentic_private_data *prv_data = (struct authentic_private_data *) card->drv_data;
	struct sc_apdu apdu;
	unsigned char pin_data[SC_MAX_APDU_BUFFER_SIZE];
	size_t offs;
	int rv;

	rv = authentic_pin_get_policy(card, data);
	LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error");

	memset(prv_data->pins_sha1[data->pin_reference], 0, sizeof(prv_data->pins_sha1[0]));

	if (!data->pin1.data && !data->pin1.len && !data->pin2.data && !data->pin2.len)   {
		if (!(card->reader->capabilities & SC_READER_CAP_PIN_PAD))
			LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "PIN pad not supported");
		rv = authentic_pin_change_pinpad(card, data->pin_reference, tries_left);
		sc_log(ctx, "authentic_pin_cmd(SC_PIN_CMD_CHANGE) chv_change_pinpad returned %i", rv);
		LOG_FUNC_RETURN(ctx, rv);
	}

	if (card->max_send_size && (data->pin1.len + data->pin2.len > (int)card->max_send_size))
		LOG_TEST_RET(ctx, SC_ERROR_INVALID_PIN_LENGTH, "APDU transmit failed");

	memset(pin_data, data->pin1.pad_char, sizeof(pin_data));
	offs = 0;
	if (data->pin1.data && data->pin1.len)   {
		memcpy(pin_data, data->pin1.data, data->pin1.len);
		offs += data->pin1.pad_length;
	}
	if (data->pin2.data && data->pin2.len)
		memcpy(pin_data + offs, data->pin2.data, data->pin2.len);

	sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, offs ? 0x00 : 0x01, data->pin_reference);
	apdu.data = pin_data;
	apdu.datalen = offs + data->pin1.pad_length;
	apdu.lc = offs + data->pin1.pad_length;

	rv = sc_transmit_apdu(card, &apdu);
	LOG_TEST_RET(ctx, rv, "APDU transmit failed");
	rv = sc_check_sw(card, apdu.sw1, apdu.sw2);

	LOG_FUNC_RETURN(ctx, rv);
}


iasecc_pin_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd_data,
		int *tries_left)
{
	struct sc_context *ctx = card->ctx;
	struct sc_pin_cmd_data pin_cmd;
        struct sc_acl_entry acl = pin_cmd_data->pin1.acls[IASECC_ACLS_CHV_VERIFY];
	int rv = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED;

	LOG_FUNC_CALLED(ctx);

	if (pin_cmd_data->pin_type != SC_AC_CHV)
		LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "PIN type is not supported for the verification");

	sc_log(ctx, "Verify ACL(method:%X;ref:%X)", acl.method, acl.key_ref);
	if (acl.method != IASECC_SCB_ALWAYS)
		LOG_FUNC_RETURN(ctx, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED);

	pin_cmd = *pin_cmd_data;
	pin_cmd.pin1.data = (unsigned char *)"";
	pin_cmd.pin1.len = 0;

	rv = iasecc_chv_verify(card, &pin_cmd, tries_left);

	LOG_FUNC_RETURN(ctx, rv);
}


static int entersafe_create_mf(sc_card_t *card, sc_entersafe_create_data * data)
{
	int r;
	sc_apdu_t apdu;

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	memcpy(data->data.df.init_key, init_key, sizeof(init_key));

	sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xE0,0x00,0x00);
	apdu.cla=0x84;
	apdu.data=(u8*)&data->data.df;
	apdu.datalen=apdu.lc=sizeof(data->data.df);

	switch(card->type)
	{
	case SC_CARD_TYPE_ENTERSAFE_3K:
	{
		 r = entersafe_transmit_apdu(card, &apdu,trans_code_3k,sizeof(trans_code_3k),0,1);
	}break;
	case SC_CARD_TYPE_ENTERSAFE_FTCOS_PK_01C:
	case SC_CARD_TYPE_ENTERSAFE_EJAVA_PK_01C:
	case SC_CARD_TYPE_ENTERSAFE_EJAVA_PK_01C_T0:
	case SC_CARD_TYPE_ENTERSAFE_EJAVA_H10CR_PK_01C_T1:
	case SC_CARD_TYPE_ENTERSAFE_EJAVA_D11CR_PK_01C_T1:
	case SC_CARD_TYPE_ENTERSAFE_EJAVA_C21C_PK_01C_T1:
	case SC_CARD_TYPE_ENTERSAFE_EJAVA_A22CR_PK_01C_T1:
	case SC_CARD_TYPE_ENTERSAFE_EJAVA_A40CR_PK_01C_T1:
	{
		 r = entersafe_transmit_apdu(card, &apdu,trans_code_ftcos_pk_01c,sizeof(trans_code_ftcos_pk_01c),0,1);
	}break;
	default:
	{
		 r = SC_ERROR_INTERNAL;
	}break;
	}

	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
	return sc_check_sw(card, apdu.sw1, apdu.sw2);
}


static int entersafe_set_security_env(sc_card_t *card,
									  const sc_security_env_t *env,
									  int se_num)
{
	 assert(card);
	 assert(env);

	 SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	 if(card->drv_data){
		  free(card->drv_data);
		  card->drv_data=0;
	 }

	 card->drv_data = calloc(1,sizeof(*env));
	 if(!card->drv_data)
		  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_OUT_OF_MEMORY);

	 memcpy(card->drv_data,env,sizeof(*env));
	 SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS);
}


auth_init(struct sc_card *card)
{
	struct auth_private_data *data;
	struct sc_path path;
	unsigned long flags;
	int rv = 0;

	data = calloc(1, sizeof(struct auth_private_data));
	if (!data)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);

	card->cla = 0x00;
	card->drv_data = data;

	card->caps |= SC_CARD_CAP_RNG;
	card->caps |= SC_CARD_CAP_USE_FCI_AC;

	if (auth_select_aid(card))   {
		sc_log(card->ctx, "Failed to initialize %s", card->name);
		LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_CARD, "Failed to initialize");
	}

	flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_PAD_ISO9796;
	flags |= SC_ALGORITHM_RSA_HASH_NONE;
	flags |= SC_ALGORITHM_ONBOARD_KEY_GEN;

	_sc_card_add_rsa_alg(card, 512, flags, 0);
	_sc_card_add_rsa_alg(card, 1024, flags, 0);
	_sc_card_add_rsa_alg(card, 2048, flags, 0);

	sc_format_path("3F00", &path);
	rv = auth_select_file(card, &path, NULL);

	LOG_FUNC_RETURN(card->ctx, rv);
}


pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info)
{
	sc_apdu_t apdu;
	/* temporary variables to hold APDU params */
	u8 apdu_case;
	u8 *apdu_data;
	size_t apdu_le;
	size_t resplen = 0;
	int r = SC_SUCCESS;

	LOG_FUNC_CALLED(card->ctx);

	/* FIXME the compilers don't assure that the buffers set here as
	 * apdu_data are present until the end of the function */
	/* set Control Reference Template for key */
	if (key_info->keytype == SC_OPENPGP_KEY_SIGN)
		apdu_data = (unsigned char *) "\xb6";
		/* as a string, apdu_data will end with '\0' (B6 00) */
	else if (key_info->keytype == SC_OPENPGP_KEY_ENCR)
		apdu_data = (unsigned char *) "\xb8";
	else if (key_info->keytype == SC_OPENPGP_KEY_AUTH)
		apdu_data = (unsigned char *) "\xa4";
	else {
		sc_log(card->ctx, "Unknown key type %X.", key_info->keytype);
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
	}

	if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && key_info->modulus_len != 2048) {
		sc_log(card->ctx, "Gnuk does not support other key length than 2048.");
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
	}

	/* set attributes for new-generated key */
	r = pgp_update_new_algo_attr(card, key_info);
	LOG_TEST_RET(card->ctx, r, "Cannot set attributes for new-generated key");

	/* Test whether we will need extended APDU. 1900 is an
	 * arbitrary modulus length which for sure fits into a short APDU.
	 * This idea is borrowed from GnuPG code.  */
	if (card->caps & SC_CARD_CAP_APDU_EXT
		&& key_info->modulus_len > 1900
		&& card->type != SC_CARD_TYPE_OPENPGP_GNUK) {
		/* We won't store to apdu variable yet, because it will be reset in
		 * sc_format_apdu() */
		apdu_le = card->max_recv_size;
		apdu_case = SC_APDU_CASE_4_EXT;
	}
	else {
		apdu_case = SC_APDU_CASE_4_SHORT;
		apdu_le = 256;
		resplen = MAXLEN_RESP_PUBKEY;
	}
	if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) {
		resplen = MAXLEN_RESP_PUBKEY_GNUK;
	}

	/* prepare APDU */
	sc_format_apdu(card, &apdu, apdu_case, 0x47, 0x80, 0);
	apdu.data = apdu_data;
	apdu.datalen = 2;  /* Data = B600 */
	apdu.lc = 2;
	apdu.le = apdu_le;

	/* buffer to receive response */
	apdu.resplen = (resplen > 0) ? resplen : apdu_le;
	apdu.resp = calloc(apdu.resplen, 1);
	if (apdu.resp == NULL) {
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY);
	}

	/* send */
	sc_log(card->ctx, "Waiting for the card to generate key...");
	r = sc_transmit_apdu(card, &apdu);
	sc_log(card->ctx, "Card has done key generation.");
	if (r < 0) {
		sc_log(card->ctx, "APDU transmit failed. Error %s.", sc_strerror(r));
		goto finish;
	}

	/* check response */
	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
	/* instruct more in case of error */
	if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) {
		sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Please verify PIN first.");
		goto finish;
	}

	/* parse response data and set output */
	pgp_parse_and_set_pubkey_output(card, apdu.resp, apdu.resplen, key_info);
	pgp_update_card_algorithms(card, key_info);

finish:
	free(apdu.resp);
	LOG_FUNC_RETURN(card->ctx, r);
}


gpk_select_id(sc_card_t *card, int kind, unsigned int fid,
		sc_file_t **file)
{
	sc_path_t	*cp = &card->cache.current_path;
	u8		fbuf[2];
	int		r;

	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
		"gpk_select_id(0x%04X, kind=%u)\n", fid, kind);

	fbuf[0] = fid >> 8;
	fbuf[1] = fid & 0xff;

	r = gpk_select(card, kind, fbuf, 2, file);

	/* Fix up the path cache.
	 * NB we never cache the ID of an EF, just the DF path */
	if (r == 0) {
		unsigned short int	*path;

		switch (kind) {
		case GPK_SEL_MF:
			cp->len = 0;
			/* fallthru */
		case GPK_SEL_DF:
			assert(cp->len + 1 <= SC_MAX_PATH_SIZE / 2);
			path = (unsigned short int *) cp->value;
			path[cp->len++] = fid;
		}
	} else {
		cp->len = 0;
	}
	return r;
}


gpk_pkfile_init(sc_card_t *card, struct sc_cardctl_gpk_pkinit *args)
{
	sc_apdu_t	apdu;
	int		r;

	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
		"gpk_pkfile_init(%u)\n", args->privlen);

	memset(&apdu, 0, sizeof(apdu));
	apdu.cse = SC_APDU_CASE_1;
	apdu.cla = 0x80;
	apdu.ins = 0x12;
	apdu.p1  = args->file->id & 0x1F;
	apdu.p2  = args->privlen / 4;

	r = sc_transmit_apdu(card, &apdu);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");

	return r;
}


auth_process_fci(struct sc_card *card, struct sc_file *file,
            const unsigned char *buf, size_t buflen)
{
	unsigned char type, attr[SC_OBERTHUR_MAX_ATTR_SIZE];
	int attr_len = sizeof(attr);

	LOG_FUNC_CALLED(card->ctx);
	attr_len = sizeof(attr);
	if (tlv_get(buf, buflen, 0x82, attr, &attr_len))
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
	type = attr[0];

	attr_len = sizeof(attr);
	if (tlv_get(buf, buflen, 0x83, attr, &attr_len))
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
	file->id = attr[0]*0x100 + attr[1];

	attr_len = sizeof(attr);
	if (tlv_get(buf, buflen, type==0x01 ? 0x80 : 0x85, attr, &attr_len))
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
	if (attr_len<2 && type != 0x04)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);

	switch (type) {
	case 0x01:
		file->type = SC_FILE_TYPE_WORKING_EF;
		file->ef_structure = SC_FILE_EF_TRANSPARENT;
		file->size = attr[0]*0x100 + attr[1];
		break;
	case 0x04:
		file->type = SC_FILE_TYPE_WORKING_EF;
		file->ef_structure = SC_FILE_EF_LINEAR_VARIABLE;
		file->size = attr[0];
		attr_len = sizeof(attr);
		if (tlv_get(buf, buflen, 0x82, attr, &attr_len))
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
		if (attr_len!=5)
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
		file->record_length = attr[2]*0x100+attr[3];
		file->record_count = attr[4];
		break;
	case 0x11:
		file->type = SC_FILE_TYPE_INTERNAL_EF;
		file->ef_structure = SC_CARDCTL_OBERTHUR_KEY_DES;
		file->size = attr[0]*0x100 + attr[1];
		file->size /= 8;
		break;
	case 0x12:
		file->type = SC_FILE_TYPE_INTERNAL_EF;
		file->ef_structure = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC;

		file->size = attr[0]*0x100 + attr[1];
		if (file->size==512)
			file->size = PUBKEY_512_ASN1_SIZE;
		else if (file->size==1024)
			file->size = PUBKEY_1024_ASN1_SIZE;
		else if (file->size==2048)
			file->size = PUBKEY_2048_ASN1_SIZE;
		else   {
			sc_log(card->ctx,
			       "Not supported public key size: %"SC_FORMAT_LEN_SIZE_T"u",
			       file->size);
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
		}
		break;
	case 0x14:
		file->type = SC_FILE_TYPE_INTERNAL_EF;
		file->ef_structure = SC_CARDCTL_OBERTHUR_KEY_RSA_CRT;
		file->size = attr[0]*0x100 + attr[1];
		break;
	case 0x38:
		file->type = SC_FILE_TYPE_DF;
		file->size = attr[0];
		if (SC_SUCCESS != sc_file_set_type_attr(file,attr,attr_len))
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
		break;
	default:
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
	}

	attr_len = sizeof(attr);
	if (tlv_get(buf, buflen, 0x86, attr, &attr_len))
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);
	if (attr_len<8)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED);

	if (file->type == SC_FILE_TYPE_DF) {
		add_acl_entry(card, file, SC_AC_OP_CREATE, attr[0]);
		add_acl_entry(card, file, SC_AC_OP_CRYPTO, attr[1]);
		add_acl_entry(card, file, SC_AC_OP_LIST_FILES, attr[2]);
		add_acl_entry(card, file, SC_AC_OP_DELETE, attr[3]);
		add_acl_entry(card, file, SC_AC_OP_PIN_DEFINE, attr[4]);
		add_acl_entry(card, file, SC_AC_OP_PIN_CHANGE, attr[5]);
		add_acl_entry(card, file, SC_AC_OP_PIN_RESET, attr[6]);
		sc_log(card->ctx, "SC_FILE_TYPE_DF:CRYPTO %X", attr[1]);
	}
	else if (file->type == SC_FILE_TYPE_INTERNAL_EF)  { /* EF */
		switch (file->ef_structure) {
		case SC_CARDCTL_OBERTHUR_KEY_DES:
			add_acl_entry(card, file, SC_AC_OP_UPDATE, attr[0]);
			add_acl_entry(card, file, SC_AC_OP_PSO_DECRYPT, attr[1]);
			add_acl_entry(card, file, SC_AC_OP_PSO_ENCRYPT, attr[2]);
			add_acl_entry(card, file, SC_AC_OP_PSO_COMPUTE_CHECKSUM, attr[3]);
			add_acl_entry(card, file, SC_AC_OP_PSO_VERIFY_CHECKSUM, attr[4]);
			add_acl_entry(card, file, SC_AC_OP_INTERNAL_AUTHENTICATE, attr[5]);
			add_acl_entry(card, file, SC_AC_OP_EXTERNAL_AUTHENTICATE, attr[6]);
			break;
		case SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC:
			add_acl_entry(card, file, SC_AC_OP_UPDATE, attr[0]);
			add_acl_entry(card, file, SC_AC_OP_PSO_ENCRYPT, attr[2]);
			add_acl_entry(card, file, SC_AC_OP_PSO_VERIFY_SIGNATURE, attr[4]);
			add_acl_entry(card, file, SC_AC_OP_EXTERNAL_AUTHENTICATE, attr[6]);
			break;
		case SC_CARDCTL_OBERTHUR_KEY_RSA_CRT:
			add_acl_entry(card, file, SC_AC_OP_UPDATE, attr[0]);
			add_acl_entry(card, file, SC_AC_OP_PSO_DECRYPT, attr[1]);
			add_acl_entry(card, file, SC_AC_OP_PSO_COMPUTE_SIGNATURE, attr[3]);
			add_acl_entry(card, file, SC_AC_OP_INTERNAL_AUTHENTICATE, attr[5]);
			break;
		}
	}
	else   {
		switch (file->ef_structure) {
		case SC_FILE_EF_TRANSPARENT:
			add_acl_entry(card, file, SC_AC_OP_WRITE, attr[0]);
			add_acl_entry(card, file, SC_AC_OP_UPDATE, attr[1]);
			add_acl_entry(card, file, SC_AC_OP_READ, attr[2]);
			add_acl_entry(card, file, SC_AC_OP_ERASE, attr[3]);
			break;
		case SC_FILE_EF_LINEAR_VARIABLE:
			add_acl_entry(card, file, SC_AC_OP_WRITE, attr[0]);
			add_acl_entry(card, file, SC_AC_OP_UPDATE, attr[1]);
			add_acl_entry(card, file, SC_AC_OP_READ, attr[2]);
			add_acl_entry(card, file, SC_AC_OP_ERASE, attr[3]);
			break;
		}
	}

	file->status = SC_FILE_STATUS_ACTIVATED;
	file->magic = SC_FILE_MAGIC;

	LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}


gpk_update_binary(sc_card_t *card, unsigned int offset,
		const u8 *buf, size_t count, unsigned long flags)
{
	struct gpk_private_data *priv = DRVDATA(card);

	if (offset & priv->offset_mask) {
		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid file offset (not a multiple of %d)",
				priv->offset_mask + 1);
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	return iso_ops->update_binary(card, offset >> priv->offset_shift,
			buf, count, flags);
}


static int entersafe_write_key(sc_card_t *card, sc_entersafe_wkey_data *data)
{
	 struct sc_pkcs15_prkey_rsa* rsa=data->key_data.rsa;

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	 switch(data->usage)
	 {
	 case 0x22:
		  if(rsa->modulus.len < 256)
			   return entersafe_write_small_rsa_key(card,data->key_id,rsa);
		  else
			   return entersafe_write_large_rsa_key(card,data->key_id,rsa);
		  break;
	 case 0x2A:
		  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED);
		  break;
	 default:
		  return entersafe_write_symmetric_key(card,data->key_id,data->usage,
											   data->key_data.symmetric.EC,
											   data->key_data.symmetric.ver,
											   data->key_data.symmetric.key_val,
											   data->key_data.symmetric.key_len);
		  break;
	 }
	 SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS);
}


static int entersafe_read_binary(sc_card_t *card,
								 unsigned int idx, u8 *buf, size_t count,
								 unsigned long flags)
{
	sc_apdu_t apdu;
	u8 recvbuf[SC_MAX_APDU_BUFFER_SIZE];
	int r;

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	assert(count <= card->max_recv_size);
	sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB0,
		       (idx >> 8) & 0xFF, idx & 0xFF);

	apdu.cla=idx > 0x7fff ? 0x80:0x00;
	apdu.le = count;
	apdu.resplen = count;
	apdu.resp = recvbuf;

	r = entersafe_transmit_apdu(card, &apdu,0,0,0,0);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
	if (apdu.resplen == 0)
		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
	memcpy(buf, recvbuf, apdu.resplen);

	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen);
}


get_full_pgp_aid(sc_card_t *card, sc_file_t *file)
{
	int r = SC_ERROR_INVALID_ARGUMENTS;

	if (file != NULL) {
		/* explicitly get the full aid */
		r = sc_get_data(card, 0x004F, file->name, sizeof file->name);
		file->namelen = MAX(r, 0);
	}

	return r;
}


Version ModuleSQL::GetVersion()
{
	return Version("Provides MySQL support", VF_VENDOR);
}


	SQLConnection(Module* p, ConfigTag* tag) : SQL::Provider(p, "SQL/" + tag->getString("id")),
		config(tag), connection(NULL)
	{
	}


	void Submit(SQL::Query* q, const std::string& qs) CXX11_OVERRIDE
	{
		ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Executing MySQL query: " + qs);
		Parent()->Dispatcher->LockQueue();
		Parent()->qq.push_back(QQueueItem(q, qs, this));
		Parent()->Dispatcher->UnlockQueueWakeup();
	}


	MySQLresult* DoBlockingQuery(const std::string& query)
	{

		/* Parse the command string and dispatch it to mysql */
		if (CheckConnection() && !mysql_real_query(connection, query.data(), query.length()))
		{
			/* Successfull query */
			MYSQL_RES* res = mysql_use_result(connection);
			unsigned long rows = mysql_affected_rows(connection);
			return new MySQLresult(res, rows);
		}
		else
		{
			/* XXX: See /usr/include/mysql/mysqld_error.h for a list of
			 * possible error numbers and error messages */
			SQL::Error e(SQL::QREPLY_FAIL, InspIRCd::Format("%u: %s", mysql_errno(connection), mysql_error(connection)));
			return new MySQLresult(e);
		}
	}


	bool HasColumn(const std::string& column, size_t& index) CXX11_OVERRIDE
	{
		for (size_t i = 0; i < colnames.size(); ++i)
		{
			if (colnames[i] == column)
			{
				index = i;
				return true;
			}
		}
		return false;
	}


struct sighand_struct *__lock_task_sighand(struct task_struct *tsk,
					   unsigned long *flags)
{
	struct sighand_struct *sighand;

	for (;;) {
		local_irq_save(*flags);
		rcu_read_lock();
		sighand = rcu_dereference(tsk->sighand);
		if (unlikely(sighand == NULL)) {
			rcu_read_unlock();
			local_irq_restore(*flags);
			break;
		}

		spin_lock(&sighand->siglock);
		if (likely(sighand == tsk->sighand)) {
			rcu_read_unlock();
			break;
		}
		spin_unlock(&sighand->siglock);
		rcu_read_unlock();
		local_irq_restore(*flags);
	}

	return sighand;
}


void task_clear_jobctl_trapping(struct task_struct *task)
{
	if (unlikely(task->jobctl & JOBCTL_TRAPPING)) {
		task->jobctl &= ~JOBCTL_TRAPPING;
		wake_up_bit(&task->jobctl, JOBCTL_TRAPPING_BIT);
	}
}


static void do_notify_parent_cldstop(struct task_struct *tsk,
				     bool for_ptracer, int why)
{
	struct siginfo info;
	unsigned long flags;
	struct task_struct *parent;
	struct sighand_struct *sighand;
	cputime_t utime, stime;

	if (for_ptracer) {
		parent = tsk->parent;
	} else {
		tsk = tsk->group_leader;
		parent = tsk->real_parent;
	}

	info.si_signo = SIGCHLD;
	info.si_errno = 0;
	/*
	 * see comment in do_notify_parent() about the following 4 lines
	 */
	rcu_read_lock();
	info.si_pid = task_pid_nr_ns(tsk, task_active_pid_ns(parent));
	info.si_uid = from_kuid_munged(task_cred_xxx(parent, user_ns), task_uid(tsk));
	rcu_read_unlock();

	task_cputime(tsk, &utime, &stime);
	info.si_utime = cputime_to_clock_t(utime);
	info.si_stime = cputime_to_clock_t(stime);

 	info.si_code = why;
 	switch (why) {
 	case CLD_CONTINUED:
 		info.si_status = SIGCONT;
 		break;
 	case CLD_STOPPED:
 		info.si_status = tsk->signal->group_exit_code & 0x7f;
 		break;
 	case CLD_TRAPPED:
 		info.si_status = tsk->exit_code & 0x7f;
 		break;
 	default:
 		BUG();
 	}

	sighand = parent->sighand;
	spin_lock_irqsave(&sighand->siglock, flags);
	if (sighand->action[SIGCHLD-1].sa.sa_handler != SIG_IGN &&
	    !(sighand->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP))
		__group_send_sig_info(SIGCHLD, &info, parent);
	/*
	 * Even if SIGCHLD is not generated, we must wake up wait4 calls.
	 */
	__wake_up_parent(tsk, parent);
	spin_unlock_irqrestore(&sighand->siglock, flags);
}


void info_callback(const char *msg, void *client_data)
{
    FILE *stream = (FILE*)client_data;
    fprintf(stream, "[INFO] %s", msg);
}


void warning_callback(const char *msg, void *client_data)
{
    FILE *stream = (FILE*)client_data;
    fprintf(stream, "[WARNING] %s", msg);
}


int main(int argc, char *argv[])
{
    opj_dinfo_t* dinfo;
    opj_event_mgr_t event_mgr;      /* event manager */
    int tnum;
    unsigned int snum;
    opj_mj2_t *movie;
    mj2_tk_t *track;
    mj2_sample_t *sample;
    unsigned char* frame_codestream;
    FILE *file, *outfile;
    char outfilename[50];
    mj2_dparameters_t parameters;

    if (argc != 3) {
        printf("Usage: %s mj2filename output_location\n", argv[0]);
        printf("Example: %s foreman.mj2 output/foreman\n", argv[0]);
        return 1;
    }

    file = fopen(argv[1], "rb");

    if (!file) {
        fprintf(stderr, "failed to open %s for reading\n", argv[1]);
        return 1;
    }

    /*
    configure the event callbacks (not required)
    setting of each callback is optional
    */
    memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
    event_mgr.error_handler = error_callback;
    event_mgr.warning_handler = warning_callback;
    event_mgr.info_handler = info_callback;

    /* get a MJ2 decompressor handle */
    dinfo = mj2_create_decompress();

    /* catch events using our callbacks and give a local context */
    opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, stderr);

    /* setup the decoder decoding parameters using user parameters */
    memset(&parameters, 0, sizeof(mj2_dparameters_t));
    movie = (opj_mj2_t*) dinfo->mj2_handle;
    mj2_setup_decoder(movie, &parameters);

    if (mj2_read_struct(file, movie)) { /* Creating the movie structure*/
        return 1;
    }

    /* Decode first video track */
    tnum = 0;
    while (movie->tk[tnum].track_type != 0) {
        tnum ++;
    }

    track = &movie->tk[tnum];

    fprintf(stdout, "Extracting %d frames from file...\n", track->num_samples);

    for (snum = 0; snum < track->num_samples; snum++) {
        sample = &track->sample[snum];
        frame_codestream = (unsigned char*) malloc(sample->sample_size -
                           8); /* Skipping JP2C marker*/
        fseek(file, sample->offset + 8, SEEK_SET);
        fread(frame_codestream, sample->sample_size - 8, 1,
              file); /* Assuming that jp and ftyp markers size do*/

        {
            int num = snprintf(outfilename, sizeof(outfilename),
                               "%s_%05d.j2k", argv[2],
                               snum);
            if (num >= sizeof(outfilename)) {
                fprintf(stderr, "maximum length of output prefix exceeded\n");
                free(frame_codestream);
                return 1;
            }
        }

        outfile = fopen(outfilename, "wb");
        if (!outfile) {
            fprintf(stderr, "failed to open %s for writing\n", outfilename);
            free(frame_codestream);
            return 1;
        }
        fwrite(frame_codestream, sample->sample_size - 8, 1, outfile);
        fclose(outfile);
        free(frame_codestream);
    }
    fclose(file);
    fprintf(stdout, "%d frames correctly extracted\n", snum);

    /* free remaining structures */
    if (dinfo) {
        mj2_destroy_decompress((opj_mj2_t*)dinfo->mj2_handle);
    }

    return 0;
}


getStmtError(ODBCStmt *stmt)
{
	assert(isValidStmt(stmt));
	return stmt->Error;
}


off_t _q_iosend(FILE *outfp, FILE *infp, off_t nbytes) {
    if (nbytes == 0) return 0;

    unsigned char buf[QIOSEND_CHUNK_SIZE];
    off_t total = 0; // total size sent
    while (total < nbytes) {
        size_t chunksize; // this time sending size
        if (nbytes - total <= sizeof(buf)) chunksize = nbytes - total;
        else chunksize = sizeof(buf);

        // read
        size_t rsize = fread(buf, 1, chunksize, infp);
        if (rsize == 0) break;
        DEBUG("read %zu", rsize);

        // write
        size_t wsize = fwrite(buf, 1, rsize, outfp);
        if (wsize == 0) break;
        DEBUG("write %zu", wsize);

        total += wsize;
        if (rsize != wsize) {
            DEBUG("size mismatch. read:%zu, write:%zu", rsize, wsize);
            break;
        }
    }

    if (total > 0) return total;
    return -1;
}


destroySWFShape(SWFShape shape)
{
	int i;
	if(shape->fills != NULL)
	{
		// Fills have to be destroyed by users.
		/*
		for ( i=0; i<shape->nFills; ++i )
			destroySWFFillStyle(shape->fills[i]);
		*/
		free(shape->fills);
	}
	if(shape->records != NULL)
	{
		for(i = 0; i < shape->nRecords; i++)
		{
			free(shape->records[i].record.stateChange);
		}
	 	free(shape->records);
	}

	if(shape->edgeBounds != NULL)
		free(shape->edgeBounds);

	for ( i=0; i<shape->nLines; ++i )
		free(shape->lines[i]);

	if ( shape->lines != NULL )
		free(shape->lines);

	destroySWFOutput(shape->out);

#if TRACK_ALLOCS
	ming_gc_remove_node(shape->gcnode);
#endif

	destroySWFCharacter((SWFCharacter) shape);
}


SWFShape_drawScaledLine(SWFShape shape, int dx, int dy)
{
	ShapeRecord record;

	if ( shape->isEnded )
		return;

	if ( dx == 0 && dy == 0 )
		return;

	record = newShapeRecord(shape, SHAPERECORD_LINETO);

	SWF_assert(SWFOutput_numSBits(dx) < 18);
	SWF_assert(SWFOutput_numSBits(dy) < 18);

	record.record.lineTo->dx = dx;
	record.record.lineTo->dy = dy;

	shape->xpos += dx;
	shape->ypos += dy;

	SWFRect_includePoint(SWFCharacter_getBounds(CHARACTER(shape)),
											 shape->xpos, shape->ypos, shape->lineWidth);
	SWFRect_includePoint(shape->edgeBounds, shape->xpos, shape->ypos, 0);
}


SWFShape_moveScaledPenTo(SWFShape shape, int x, int y)
{
	ShapeRecord record;
	if ( shape->isEnded )
		return;

	record = addStyleRecord(shape);

	record.record.stateChange->moveToX = shape->xpos = x;
	record.record.stateChange->moveToY = shape->ypos = y;

	record.record.stateChange->flags |= SWF_SHAPE_MOVETOFLAG;

	if ( shape->nRecords == 0 ||
			 (shape->nRecords == 1 &&
				shape->records[0].type == SHAPERECORD_STATECHANGE) )
	{
		SWFRect_setBounds(SWFCharacter_getBounds(CHARACTER(shape)), x, x, y, y);
		SWFRect_setBounds(shape->edgeBounds, x, x, y, y);
	}
}


newSWFShapeFromBitmap(SWFBitmap bitmap, int flag)
{
	SWFShape shape = newSWFShape();
	SWFFillStyle fill;
	int width, height;

	if ( flag != SWFFILL_TILED_BITMAP &&
	     flag != SWFFILL_CLIPPED_BITMAP &&
	     flag != SWFFILL_NONSMOOTHED_TILED_BITMAP &&
	     flag != SWFFILL_NONSMOOTHED_CLIPPED_BITMAP)
	{
		SWF_error("Invalid bitmap fill flag");
	}

	fill = SWFShape_addBitmapFillStyle(shape, bitmap, flag);

	width = SWFBitmap_getWidth(bitmap);
	height = SWFBitmap_getHeight(bitmap);

	SWFShape_setRightFillStyle(shape, fill);

	// XXX - scale shouldn't be hardcoded! (here, or in newSWFBitmapFillStyle)
	SWFShape_drawScaledLine(shape, width * 20, 0);
	SWFShape_drawScaledLine(shape, 0, height * 20);
	SWFShape_drawScaledLine(shape, -width * 20, 0);
	SWFShape_drawScaledLine(shape, 0, -height * 20);

	return shape;
}


int SWFShape_getFlags(SWFShape shape)
{
	if(shape->useVersion == SWF_SHAPE4)
		return shape->flags;
	else
		return 0;
}


SWFShape_end(SWFShape shape)
{
	int i;
	byte* buffer;

	if ( shape->isEnded )
		return;

	shape->isEnded = TRUE;

	buffer = SWFOutput_getBuffer(shape->out);
	buffer[0] =
		(SWFOutput_numBits(shape->nFills) << 4) + SWFOutput_numBits(shape->nLines);

	for ( i=0; i<shape->nRecords; ++i )
	{
		if ( i < shape->nRecords-1 ||
				 shape->records[i].type != SHAPERECORD_STATECHANGE )
		{
			SWFShape_writeShapeRecord(shape, shape->records[i], shape->out);
		}

		free(shape->records[i].record.stateChange); /* all in union are pointers */
	}

	SWFOutput_writeBits(shape->out, 0, 6); /* end tag */
	SWFOutput_byteAlign(shape->out);

	/* addStyleHeader creates a new output and adds the existing one after
		 itself- so even though it's called afterwards it's written before,
		 as it should be */
	if ( BLOCK(shape)->type > 0 )
	{
		switch (shape->useVersion)
		{
		case SWF_SHAPE1:
			BLOCK(shape)->type = SWF_DEFINESHAPE;
			break;
		case SWF_SHAPE2:
			BLOCK(shape)->type = SWF_DEFINESHAPE2;
			break;
		case SWF_SHAPE4:
			BLOCK(shape)->type = SWF_DEFINESHAPE4;
			break;
		}
		SWFShape_addStyleHeader(shape);
	}
	free(shape->records);
	shape->records = NULL;
	shape->nRecords = 0;
}


mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c)
{
  struct RProc *p;
  mrb_sym mid = mrb->c->ci->mid;
  mrb_callinfo *ci;
  mrb_value val;
  mrb_int n;

  check_block(mrb, b);
  ci = mrb->c->ci;
  n = mrb_ci_nregs(ci);
  p = mrb_proc_ptr(b);
  ci = cipush(mrb, n, CINFO_SKIP, c, p, mid, 0 /* dummy */);
  ci->nk = 0;
  if (argc >= CALL_MAXARGS) {
    ci->n = 15;
    n = 3;
  }
  else {
    ci->n = (uint8_t)argc;
    n = argc + 2;
  }
  mrb_stack_extend(mrb, n);
  mrb->c->ci->stack[0] = self;
  if (ci->n == 15) {
    mrb->c->ci->stack[1] = mrb_ary_new_from_values(mrb, argc, argv);
    argc = 1;
  }
  else if (argc > 0) {
    stack_copy(mrb->c->ci->stack+1, argv, argc);
  }
  mrb->c->ci->stack[argc+1] = mrb_nil_value(); /* clear blk */

  if (MRB_PROC_CFUNC_P(p)) {
    ci->cci = CINFO_DIRECT;
    val = MRB_PROC_CFUNC(p)(mrb, self);
    cipop(mrb);
  }
  else {
    val = mrb_run(mrb, p, self);
  }
  return val;
}


mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv, mrb_value blk)
{
  mrb_value val;
  int ai = mrb_gc_arena_save(mrb);

  if (!mrb->jmp) {
    struct mrb_jmpbuf c_jmp;
    ptrdiff_t nth_ci = mrb->c->ci - mrb->c->cibase;

    MRB_TRY(&c_jmp) {
      mrb->jmp = &c_jmp;
      /* recursive call */
      val = mrb_funcall_with_block(mrb, self, mid, argc, argv, blk);
      mrb->jmp = 0;
    }
    MRB_CATCH(&c_jmp) { /* error */
      while (nth_ci < (mrb->c->ci - mrb->c->cibase)) {
        cipop(mrb);
      }
      mrb->jmp = 0;
      val = mrb_obj_value(mrb->exc);
    }
    MRB_END_EXC(&c_jmp);
    mrb->jmp = 0;
  }
  else {
    mrb_method_t m;
    struct RClass *c;
    mrb_callinfo *ci = mrb->c->ci;
    mrb_int n = mrb_ci_nregs(ci);
    ptrdiff_t voff = -1;

    if (!mrb->c->stbase) {
      stack_init(mrb);
    }
    if (ci - mrb->c->cibase > MRB_CALL_LEVEL_MAX) {
      mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
    }
    if (mrb->c->stbase <= argv && argv < mrb->c->stend) {
      voff = argv - mrb->c->stbase;
    }
    if (argc < 0) {
      mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative argc for funcall (%i)", argc);
    }
    c = mrb_class(mrb, self);
    m = mrb_method_search_vm(mrb, &c, mid);
    mrb_stack_extend(mrb, n + argc + 3);
    if (argc >= 15) {
      ci->stack[n+1] = mrb_ary_new_from_values(mrb, argc, argv);
      argc = 15;
    }
    if (MRB_METHOD_UNDEF_P(m)) {
      uint16_t ac = (uint16_t)argc;
      m = prepare_missing(mrb, self, mid, &c, (uint32_t)n, &ac, mrb_nil_value(), 0);
      argc = (mrb_int)ac;
      mid = MRB_SYM(method_missing);
    }
    ci = cipush(mrb, n, 0, c, NULL, mid, (uint16_t)argc);
    if (MRB_METHOD_PROC_P(m)) {
      struct RProc *p = MRB_METHOD_PROC(m);

      mrb_vm_ci_proc_set(ci, p);
      if (!MRB_PROC_CFUNC_P(p)) {
        mrb_stack_extend(mrb, p->body.irep->nregs + argc);
      }
    }
    if (voff >= 0) {
      argv = mrb->c->stbase + voff;
    }
    ci->stack[0] = self;
    if (argc < 15) {
      if (argc > 0)
        stack_copy(ci->stack+1, argv, argc);
      ci->stack[argc+1] = blk;
    }
    else {
      ci->stack[2] = blk;
    }

    if (MRB_METHOD_CFUNC_P(m)) {
      ci->cci = CINFO_DIRECT;
      val = MRB_METHOD_CFUNC(m)(mrb, self);
      cipop(mrb);
    }
    else {
      ci->cci = CINFO_SKIP;
      val = mrb_run(mrb, MRB_METHOD_PROC(m), self);
    }
  }
  mrb_gc_arena_restore(mrb, ai);
  mrb_gc_protect(mrb, val);
  return val;
}


mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
{
  mrb_callinfo *ci = mrb->c->ci;
  if (ci->cci == CINFO_NONE) {
    return exec_irep(mrb, self, p);
  }
  else {
    mrb_value ret;
    if (MRB_PROC_CFUNC_P(p)) {
      if (MRB_PROC_NOARG_P(p)) {
        check_method_noarg(mrb, ci);
      }
      cipush(mrb, 0, CINFO_DIRECT, mrb_vm_ci_target_class(ci), p, ci->mid, ci->n|(ci->nk<<4));
      ret = MRB_PROC_CFUNC(p)(mrb, self);
      cipop(mrb);
    }
    else {
      mrb_int keep = mrb_ci_bidx(ci) + 1; /* receiver + block */
      ret = mrb_top_run(mrb, p, self, keep);
    }
    if (mrb->exc && mrb->jmp) {
      mrb_exc_raise(mrb, mrb_obj_value(mrb->exc));
    }
    return ret;
  }
}


int git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source)
{
	size_t total_size;
	git_tree_entry *copy;

	assert(source);

	total_size = sizeof(git_tree_entry) + source->filename_len + 1;

	copy = git__malloc(total_size);
	GITERR_CHECK_ALLOC(copy);

	memcpy(copy, source, total_size);

	*dest = copy;
	return 0;
}


int git_tree__prefix_position(const git_tree *tree, const char *path)
{
	const git_vector *entries = &tree->entries;
	struct tree_key_search ksearch;
	size_t at_pos;

	if (!path)
		return 0;

	ksearch.filename = path;
	ksearch.filename_len = strlen(path);

	/* be safe when we cast away constness - i.e. don't trigger a sort */
	assert(git_vector_is_sorted(&tree->entries));

	/* Find tree entry with appropriate prefix */
	git_vector_bsearch2(
		&at_pos, (git_vector *)entries, &homing_search_cmp, &ksearch);

	for (; at_pos < entries->length; ++at_pos) {
		const git_tree_entry *entry = entries->contents[at_pos];
		if (homing_search_cmp(&ksearch, entry) < 0)
			break;
	}

	for (; at_pos > 0; --at_pos) {
		const git_tree_entry *entry = entries->contents[at_pos - 1];
		if (homing_search_cmp(&ksearch, entry) > 0)
			break;
	}

	return (int)at_pos;
}


int git_tree_walk(
	const git_tree *tree,
	git_treewalk_mode mode,
	git_treewalk_cb callback,
	void *payload)
{
	int error = 0;
	git_buf root_path = GIT_BUF_INIT;

	if (mode != GIT_TREEWALK_POST && mode != GIT_TREEWALK_PRE) {
		giterr_set(GITERR_INVALID, "Invalid walking mode for tree walk");
		return -1;
	}

	error = tree_walk(
		tree, callback, &root_path, payload, (mode == GIT_TREEWALK_PRE));

	git_buf_free(&root_path);

	return error;
}


static size_t subpath_len(const char *path)
{
	const char *slash_pos = strchr(path, '/');
	if (slash_pos == NULL)
		return strlen(path);

	return slash_pos - path;
}


void SaveTensors(
    OpKernelContext* context,
    checkpoint::TensorSliceWriter::CreateBuilderFunction builder_func,
    bool save_slices) {
  const Tensor& filename_t = context->input(0);
  {
    const int64_t size = filename_t.NumElements();
    OP_REQUIRES(
        context, size == 1,
        errors::InvalidArgument(
            "Input 0 (filename) must be a string scalar; got a tensor of ",
            size, "elements"));
  }

  // Path, names, and slices if save_slices is true.
  const int kFixedInputs = save_slices ? 3 : 2;
  const Tensor& tensor_names_t = context->input(1);
  OP_REQUIRES(context,
              FastBoundsCheck(tensor_names_t.NumElements() + kFixedInputs,
                              std::numeric_limits<int>::max()),
              errors::InvalidArgument("Too many inputs to SaveTensors"));
  const int N = static_cast<int>(tensor_names_t.NumElements());
  const tstring* tensor_shapes_and_slices_ptr = nullptr;
  if (save_slices) {
    const Tensor& tensor_shapes_and_slices_t = context->input(2);
    OP_REQUIRES(
        context,
        tensor_shapes_and_slices_t.NumElements() == static_cast<int64>(N),
        errors::InvalidArgument("Expected ", N,
                                " elements for the tensor "
                                "shapes and slices but got ",
                                tensor_shapes_and_slices_t.NumElements()));
    tensor_shapes_and_slices_ptr =
        tensor_shapes_and_slices_t.flat<tstring>().data();
  }
  OP_REQUIRES(context, context->num_inputs() == N + kFixedInputs,
              errors::InvalidArgument("Expected totally ", N + kFixedInputs,
                                      " inputs as input #1 (which is a string "
                                      "tensor of saved names) contains ",
                                      N, " names, but received ",
                                      context->num_inputs(), " inputs"));

  VLOG(1) << "About to save tensors to file " << filename_t.flat<tstring>()(0)
          << "...";
  checkpoint::TensorSliceWriter writer(filename_t.flat<tstring>()(0),
                                       std::move(builder_func));

  Status s;
  auto tensor_names_flat = tensor_names_t.flat<tstring>();

  // Process tensors in sorted name order.  This allows us to avoid seeking
  // during restoration in the common case where we are restoring a full
  // checkpoint.
  std::vector<size_t> sorted_name_idx(tensor_names_flat.size());
  std::iota(sorted_name_idx.begin(), sorted_name_idx.end(), 0);
  std::sort(sorted_name_idx.begin(), sorted_name_idx.end(),
            [&tensor_names_flat](size_t a, size_t b) {
              return tensor_names_flat(a) < tensor_names_flat(b);
            });

  for (const size_t i : sorted_name_idx) {
    const string& name = tensor_names_flat(i);
    const Tensor& input = context->input(i + kFixedInputs);
    TensorShape shape(input.shape());
    TensorSlice slice(input.dims());
    if (save_slices && !tensor_shapes_and_slices_ptr[i].empty()) {
      const tstring& shape_spec = tensor_shapes_and_slices_ptr[i];
      TensorShape slice_shape;
      OP_REQUIRES_OK(context, checkpoint::ParseShapeAndSlice(
                                  shape_spec, &shape, &slice, &slice_shape));
      OP_REQUIRES(context, slice_shape.IsSameSize(input.shape()),
                  errors::InvalidArgument(
                      "Slice in shape_and_slice "
                      "specification does not match the "
                      "shape of the tensor to  save: ",
                      shape_spec, ", tensor: ", input.shape().DebugString()));
    }

#define WRITER_ADD(T)                                           \
  case DataTypeToEnum<T>::value:                                \
    s = writer.Add(name, shape, slice, input.flat<T>().data()); \
    break;

    switch (input.dtype()) {
      TF_CALL_SAVE_RESTORE_TYPES(WRITER_ADD)
      default:
        context->SetStatus(errors::Unimplemented("Saving data type ",
                                                 DataTypeString(input.dtype()),
                                                 " not yet supported"));
        return;
    }
#undef WRITER_ADD
    if (!s.ok()) {
      context->SetStatus(s);
      return;
    }
  }

  s = writer.Finish();
  if (!s.ok()) {
    context->SetStatus(s);
  }
}


static int dispatch_rw_block_io(struct xen_blkif_ring *ring,
				struct blkif_request *req,
				struct pending_req *pending_req)
{
	struct phys_req preq;
	struct seg_buf *seg = pending_req->seg;
	unsigned int nseg;
	struct bio *bio = NULL;
	struct bio **biolist = pending_req->biolist;
	int i, nbio = 0;
	int operation;
	int operation_flags = 0;
	struct blk_plug plug;
	bool drain = false;
	struct grant_page **pages = pending_req->segments;
	unsigned short req_operation;

	req_operation = req->operation == BLKIF_OP_INDIRECT ?
			req->u.indirect.indirect_op : req->operation;

	if ((req->operation == BLKIF_OP_INDIRECT) &&
	    (req_operation != BLKIF_OP_READ) &&
	    (req_operation != BLKIF_OP_WRITE)) {
		pr_debug("Invalid indirect operation (%u)\n", req_operation);
		goto fail_response;
	}

	switch (req_operation) {
	case BLKIF_OP_READ:
		ring->st_rd_req++;
		operation = REQ_OP_READ;
		break;
	case BLKIF_OP_WRITE:
		ring->st_wr_req++;
		operation = REQ_OP_WRITE;
		operation_flags = REQ_SYNC | REQ_IDLE;
		break;
	case BLKIF_OP_WRITE_BARRIER:
		drain = true;
	case BLKIF_OP_FLUSH_DISKCACHE:
		ring->st_f_req++;
		operation = REQ_OP_WRITE;
		operation_flags = REQ_PREFLUSH;
		break;
	default:
		operation = 0; /* make gcc happy */
		goto fail_response;
		break;
	}

	/* Check that the number of segments is sane. */
	nseg = req->operation == BLKIF_OP_INDIRECT ?
	       req->u.indirect.nr_segments : req->u.rw.nr_segments;

	if (unlikely(nseg == 0 && operation_flags != REQ_PREFLUSH) ||
	    unlikely((req->operation != BLKIF_OP_INDIRECT) &&
		     (nseg > BLKIF_MAX_SEGMENTS_PER_REQUEST)) ||
	    unlikely((req->operation == BLKIF_OP_INDIRECT) &&
		     (nseg > MAX_INDIRECT_SEGMENTS))) {
		pr_debug("Bad number of segments in request (%d)\n", nseg);
		/* Haven't submitted any bio's yet. */
		goto fail_response;
	}

	preq.nr_sects      = 0;

	pending_req->ring      = ring;
	pending_req->id        = req->u.rw.id;
	pending_req->operation = req_operation;
	pending_req->status    = BLKIF_RSP_OKAY;
	pending_req->nr_segs   = nseg;

	if (req->operation != BLKIF_OP_INDIRECT) {
		preq.dev               = req->u.rw.handle;
		preq.sector_number     = req->u.rw.sector_number;
		for (i = 0; i < nseg; i++) {
			pages[i]->gref = req->u.rw.seg[i].gref;
			seg[i].nsec = req->u.rw.seg[i].last_sect -
				req->u.rw.seg[i].first_sect + 1;
			seg[i].offset = (req->u.rw.seg[i].first_sect << 9);
			if ((req->u.rw.seg[i].last_sect >= (XEN_PAGE_SIZE >> 9)) ||
			    (req->u.rw.seg[i].last_sect <
			     req->u.rw.seg[i].first_sect))
				goto fail_response;
			preq.nr_sects += seg[i].nsec;
		}
	} else {
		preq.dev               = req->u.indirect.handle;
		preq.sector_number     = req->u.indirect.sector_number;
		if (xen_blkbk_parse_indirect(req, pending_req, seg, &preq))
			goto fail_response;
	}

	if (xen_vbd_translate(&preq, ring->blkif, operation) != 0) {
		pr_debug("access denied: %s of [%llu,%llu] on dev=%04x\n",
			 operation == REQ_OP_READ ? "read" : "write",
			 preq.sector_number,
			 preq.sector_number + preq.nr_sects,
			 ring->blkif->vbd.pdevice);
		goto fail_response;
	}

	/*
	 * This check _MUST_ be done after xen_vbd_translate as the preq.bdev
	 * is set there.
	 */
	for (i = 0; i < nseg; i++) {
		if (((int)preq.sector_number|(int)seg[i].nsec) &
		    ((bdev_logical_block_size(preq.bdev) >> 9) - 1)) {
			pr_debug("Misaligned I/O request from domain %d\n",
				 ring->blkif->domid);
			goto fail_response;
		}
	}

	/* Wait on all outstanding I/O's and once that has been completed
	 * issue the flush.
	 */
	if (drain)
		xen_blk_drain_io(pending_req->ring);

	/*
	 * If we have failed at this point, we need to undo the M2P override,
	 * set gnttab_set_unmap_op on all of the grant references and perform
	 * the hypercall to unmap the grants - that is all done in
	 * xen_blkbk_unmap.
	 */
	if (xen_blkbk_map_seg(pending_req))
		goto fail_flush;

	/*
	 * This corresponding xen_blkif_put is done in __end_block_io_op, or
	 * below (in "!bio") if we are handling a BLKIF_OP_DISCARD.
	 */
	xen_blkif_get(ring->blkif);
	atomic_inc(&ring->inflight);

	for (i = 0; i < nseg; i++) {
		while ((bio == NULL) ||
		       (bio_add_page(bio,
				     pages[i]->page,
				     seg[i].nsec << 9,
				     seg[i].offset) == 0)) {

			int nr_iovecs = min_t(int, (nseg-i), BIO_MAX_PAGES);
			bio = bio_alloc(GFP_KERNEL, nr_iovecs);
			if (unlikely(bio == NULL))
				goto fail_put_bio;

			biolist[nbio++] = bio;
			bio->bi_bdev    = preq.bdev;
			bio->bi_private = pending_req;
			bio->bi_end_io  = end_block_io_op;
			bio->bi_iter.bi_sector  = preq.sector_number;
			bio_set_op_attrs(bio, operation, operation_flags);
		}

		preq.sector_number += seg[i].nsec;
	}

	/* This will be hit if the operation was a flush or discard. */
	if (!bio) {
		BUG_ON(operation_flags != REQ_PREFLUSH);

		bio = bio_alloc(GFP_KERNEL, 0);
		if (unlikely(bio == NULL))
			goto fail_put_bio;

		biolist[nbio++] = bio;
		bio->bi_bdev    = preq.bdev;
		bio->bi_private = pending_req;
		bio->bi_end_io  = end_block_io_op;
		bio_set_op_attrs(bio, operation, operation_flags);
	}

	atomic_set(&pending_req->pendcnt, nbio);
	blk_start_plug(&plug);

	for (i = 0; i < nbio; i++)
		submit_bio(biolist[i]);

	/* Let the I/Os go.. */
	blk_finish_plug(&plug);

	if (operation == REQ_OP_READ)
		ring->st_rd_sect += preq.nr_sects;
	else if (operation == REQ_OP_WRITE)
		ring->st_wr_sect += preq.nr_sects;

	return 0;

 fail_flush:
	xen_blkbk_unmap(ring, pending_req->segments,
	                pending_req->nr_segs);
 fail_response:
	/* Haven't submitted any bio's yet. */
	make_response(ring, req->u.rw.id, req_operation, BLKIF_RSP_ERROR);
	free_req(ring, pending_req);
	msleep(1); /* back off a bit */
	return -EIO;

 fail_put_bio:
	for (i = 0; i < nbio; i++)
		bio_put(biolist[i]);
	atomic_set(&pending_req->pendcnt, 1);
	__end_block_io_op(pending_req, -EINVAL);
	msleep(1); /* back off a bit */
	return -EIO;
}