El mar, 19-04-2011 a las 18:26 +0300, Martin Paljak escribió:
> Hello,
> On Apr 19, 2011, at 18:08 , Juan Antonio Martinez wrote:
> > Studing eidenv code and how I did it in opendnie, I suggest a change to
> > make eidenv.c card independent, and rely in each card driver the work of
> > get (when available) proper results:
> > 
> > 1- Create a new generic cardctl call SC_CARDCTL_GET_EXTRADATA
[...]
> A great idea in general, that would make eidenv a more generic utility, 
> as the need is universal but the current implementation a patchwork.
[...]
> > Contras
> > - Needs a consensus on variable list,names and so
> I guess there's some kind of (international/EU) standard for travel documents
> (ICAO MRTD?) that define names for common fields of such documents.
> That could be used as a reference, probably there's even a standardized
> translation available somewhere under *.eu 

Fine, but there are so many elements in SmartCards that aren't
enumerated in ICAO documents...

> > - Needs to be written (easy, but someone has to do the job)
> True :)

OK, I did it :-)

Attached comes my proposed patch. Tested against current (r5385) svn
trunk

Tested with my DNIe card (by patching OpenDNIe code), but need to be
tested also with belpic and esteid cards...

Goals of this patch:
- Include some extra cmdline options to get more control
- Does not touch any card that doesn't provide extra data
- Can be easily improved. If approved, I can provide an extra patch to
allow query of card "extra info" capabilities, and select which elements
should be requested by providing a tag list in command line
- the card driver can change "description" field, to provide a more
informative text (or even take care on i18n issues)

Comments, suggestions, and blames are welcomed
Juan Antonio

BTW: perhaps "eidenv" name should be changed to "eid-tool"...

diff -rbuN --exclude=.svn opensc/src/libopensc/card-belpic.c opensc-eidenv/src/libopensc/card-belpic.c
--- opensc/src/libopensc/card-belpic.c	2011-02-10 08:32:46.000000000 +0100
+++ opensc-eidenv/src/libopensc/card-belpic.c	2011-04-21 20:10:27.000000000 +0200
@@ -87,6 +87,8 @@
 
 #include "internal.h"
 #include "log.h"
+#include "cardctl.h"
+#include "asn1.h"
 
 #ifdef BELPIC_PIN_PAD
 #ifndef HAVE_GUI
@@ -1498,6 +1500,192 @@
 }
 #endif
 
+/* Select and read a transparent EF */
+static int belpic_read_transp(sc_card_t *card, const char *pathstring, unsigned char *buf, int buflen)
+{
+	sc_path_t path;
+	int r;
+
+	sc_format_path(pathstring, &path);
+	r = sc_select_file(card, &path, NULL);
+	if (r < 0)
+		fprintf(stderr, "\nFailed to select file %s: %s\n", pathstring, sc_strerror(r));
+	else {
+		r = sc_read_binary(card, 0, buf, buflen, 0);
+		if (r < 0)
+			fprintf(stderr, "\nFailed to read %s: %s\n", pathstring, sc_strerror(r));
+	}
+
+	return r;
+}
+
+/* Hex-encode the buf, 2*len+1 bytes must be reserved. E.g. {'1','2'} -> {'3','1','3','2','\0'} */
+static const char hextable[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'E'};
+static void bintohex(char *buf, int len)
+{
+	int i;
+	for (i = len - 1; i >= 0; i--) {
+		buf[2 * i + 1] = hextable[((unsigned char) buf[i]) % 16];
+		buf[2 * i] = hextable[((unsigned char) buf[i]) / 16];
+	}
+}
+
+static int belpic_export_data(sc_card_t *card, struct sc_cardctl_extra_info *info, int index, char *data) {
+	char *val;
+	if ( info[index].requested == 0) return SC_SUCCESS; /* not requested */
+	if ( data==NULL) data="";
+	val=calloc(1+strlen(data),sizeof(char));
+	if (!val ) {
+		sc_log(card->ctx,"calloc failed for export_data index %d",index);
+		return SC_ERROR_OUT_OF_MEMORY;
+	}
+	strncpy(val,data,strlen(data));
+	info[index].value=val;
+	return SC_SUCCESS;
+}
+
+static int belpic_get_extra_info(sc_card_t *card, struct sc_cardctl_extra_info *info )
+{
+	/* Contents of the ID file (3F00\DF01\4031) */
+	struct {
+		char cardnumber[12 + 1];
+		char chipnumber[2 * 16 + 1];
+		char validfrom[10 + 1];
+		char validtill[10 + 1];
+		char deliveringmunicipality[50 + 1];  /* UTF8 */
+		char nationalnumber[12 + 1];
+		char name[90 + 1]; /* UTF8 */
+		char firstnames[75 + 1]; /* UTF8 */
+		char initial[3 + 1]; /* UTF8 */
+		char nationality[65 + 1]; /* UTF8 */
+		char birthlocation[60 + 1]; /* UTF8 */
+		char birthdate[12 + 1];
+		char sex[1 + 1];
+		char noblecondition[30 + 1]; /* UTF8 */
+		char documenttype[5 + 1];
+		char specialstatus[5 + 1];
+	} id_data;
+	int cardnumberlen = sizeof(id_data.cardnumber);
+	int chipnumberlen = sizeof(id_data.chipnumber);
+	int validfromlen = sizeof(id_data.validfrom);
+	int validtilllen = sizeof(id_data.validtill);
+	int deliveringmunicipalitylen = sizeof(id_data.deliveringmunicipality);
+	int nationalnumberlen = sizeof(id_data.nationalnumber);
+	int namelen = sizeof(id_data.name);
+	int firstnameslen = sizeof(id_data.firstnames);
+	int initiallen = sizeof(id_data.initial);
+	int nationalitylen = sizeof(id_data.nationality);
+	int birthlocationlen = sizeof(id_data.birthlocation);
+	int birthdatelen = sizeof(id_data.birthdate);
+	int sexlen = sizeof(id_data.sex);
+	int nobleconditionlen = sizeof(id_data.noblecondition);
+	int documenttypelen = sizeof(id_data.documenttype);
+	int specialstatuslen = sizeof(id_data.specialstatus);
+
+	struct sc_asn1_entry id[] = {
+		{"cardnumber", SC_ASN1_UTF8STRING, 1, 0, id_data.cardnumber, &cardnumberlen},
+		{"chipnumber", SC_ASN1_OCTET_STRING, 2, 0, id_data.chipnumber, &chipnumberlen},
+		{"validfrom", SC_ASN1_UTF8STRING, 3, 0, id_data.validfrom, &validfromlen},
+		{"validtill", SC_ASN1_UTF8STRING, 4, 0, id_data.validtill, &validtilllen},
+		{"deliveringmunicipality", SC_ASN1_UTF8STRING, 5, 0, id_data.deliveringmunicipality, &deliveringmunicipalitylen},
+		{"nationalnumber", SC_ASN1_UTF8STRING, 6, 0, id_data.nationalnumber, &nationalnumberlen},
+		{"name", SC_ASN1_UTF8STRING, 7, 0, id_data.name, &namelen},
+		{"firstname(s)", SC_ASN1_UTF8STRING, 8, 0, id_data.firstnames, &firstnameslen},
+		{"initial", SC_ASN1_UTF8STRING, 9, 0, id_data.initial, &initiallen},
+		{"nationality", SC_ASN1_UTF8STRING, 10, 0, id_data.nationality, &nationalitylen},
+		{"birthlocation", SC_ASN1_UTF8STRING, 11, 0, id_data.birthlocation, &birthlocationlen},
+		{"birthdate", SC_ASN1_UTF8STRING, 12, 0, id_data.birthdate, &birthdatelen},
+		{"sex", SC_ASN1_UTF8STRING, 13, 0, id_data.sex, &sexlen},
+		{"noblecondition", SC_ASN1_UTF8STRING, 14, 0, id_data.noblecondition, &nobleconditionlen},
+		{"documenttype", SC_ASN1_UTF8STRING, 15, 0, id_data.documenttype, &documenttypelen},
+		{"specialstatus", SC_ASN1_UTF8STRING, 16, 0, id_data.specialstatus, &specialstatuslen},
+		{NULL, 0, 0, 0, NULL, NULL}
+	};
+
+	/* Contents of the Address file (3F00\DF01\4033) */
+	struct {
+		char streetandnumber[63 + 1]; /* UTF8 */
+		char zipcode[4 + 1];
+		char municipality[40 + 1]; /* UTF8 */
+	} address_data;
+	int streetandnumberlen = sizeof(address_data.streetandnumber);
+	int zipcodelen = sizeof(address_data.zipcode);
+	int municipalitylen = sizeof(address_data.municipality);
+	struct sc_asn1_entry address[] = {
+		{"streetandnumber", SC_ASN1_UTF8STRING, 1, 0, address_data.streetandnumber, &streetandnumberlen},
+		{"zipcode", SC_ASN1_UTF8STRING, 2, 0, address_data.zipcode, &zipcodelen},
+		{"municipal", SC_ASN1_UTF8STRING, 3, 0, address_data.municipality, &municipalitylen},
+		{NULL, 0, 0, 0, NULL, NULL}};
+
+	unsigned char buff[512];
+	int r = SC_SUCCESS;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	r = belpic_read_transp(card, "3f00df014031", buff, sizeof(buff));
+	if (r < 0) {
+		sc_log(card->ctx,"Failed to read Identity File ");
+		goto belpic_get_extrainfo_out;
+	}
+	memset(&id_data, 0, sizeof(id_data));
+
+	r = sc_asn1_decode(card->ctx, id, buff, r, NULL, NULL);
+	if (r < 0) {
+		sc_log(card->ctx,"Failed to decode the ID file: %s", sc_strerror(r));
+		goto belpic_get_extrainfo_out;
+	}
+
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_CARDNUMBER, id_data.cardnumber);
+	bintohex(id_data.chipnumber, chipnumberlen);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_CHIPNUMBER, id_data.chipnumber);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_VALIDFROM, id_data.validfrom);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_VALIDTO, id_data.validtill);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_ISSUERLOCATION, id_data.deliveringmunicipality);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_NATIONALNUMBER, id_data.nationalnumber);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_SURNAME, id_data.name);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_GIVENNAME1, id_data.firstnames);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_INITIALS, id_data.initial);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_NATIONALITY, id_data.nationality);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_BIRTHLOCATION, id_data.birthlocation);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_BIRTHDATE, id_data.birthdate);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_SEX, id_data.sex);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_NOBLECONDITION, id_data.noblecondition);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_DOCTYPE, id_data.documenttype);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_DOCSTATUS, id_data.specialstatus);
+
+	r = belpic_read_transp(card, "3f00df014033", buff, sizeof(buff));
+	if (r < 0) {
+		sc_log(card->ctx,"Failed to read Address file");
+		goto belpic_get_extrainfo_out;
+	}
+
+	memset(&address_data, 0, sizeof(address_data));
+
+	r = sc_asn1_decode(card->ctx, address, buff, r, NULL, NULL);
+	if (r < 0) {
+		sc_log(card->ctx, "Failed to decode the Address file: %s", sc_strerror(r));
+		goto belpic_get_extrainfo_out;
+	}
+
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_STREETANDNUMBER, address_data.streetandnumber);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_ZIPCODE, address_data.zipcode);
+	belpic_export_data(card,info,SC_CARDCTL_EXTRAINFO_MUNICIPALITY, address_data.municipality);
+
+	/* arriving here means success */
+	r = SC_SUCCESS;
+
+belpic_get_extrainfo_out:
+	SC_FUNC_RETURN(card->ctx,SC_LOG_DEBUG_VERBOSE,r);
+}
+
+static int belpic_card_ctl(sc_card_t * card, unsigned long cmd, void *ptr)
+{
+	int r;
+	if ( !card || !ptr ) return SC_ERROR_INVALID_ARGUMENTS;
+	if (cmd != SC_CARDCTL_GET_EXTRA_INFO ) return SC_ERROR_NOT_SUPPORTED;
+	return belpic_get_extra_info(card,ptr);
+}
+
 static struct sc_card_driver *sc_get_driver(void)
 {
 	if (iso_ops == NULL)
@@ -1514,6 +1702,7 @@
 	belpic_ops.set_security_env = belpic_set_security_env;
 
 	belpic_ops.compute_signature = belpic_compute_signature;
+	belpic_ops.card_ctl = belpic_card_ctl;
 	belpic_ops.get_challenge = iso_ops->get_challenge;
 	belpic_ops.get_response = iso_ops->get_response;
 	belpic_ops.check_sw = iso_ops->check_sw;
diff -rbuN --exclude=.svn opensc/src/libopensc/cardctl.h opensc-eidenv/src/libopensc/cardctl.h
--- opensc/src/libopensc/cardctl.h	2011-02-18 09:19:21.000000000 +0100
+++ opensc-eidenv/src/libopensc/cardctl.h	2011-04-21 19:11:24.000000000 +0200
@@ -41,6 +41,7 @@
 	SC_CARDCTL_GET_SERIALNR,
 	SC_CARDCTL_GET_SE_INFO,
 	SC_CARDCTL_GET_CHV_REFERENCE_IN_SE,
+	SC_CARDCTL_GET_EXTRA_INFO,
 
 	/*
 	 * GPK specific calls
@@ -248,6 +249,62 @@
 };
 
 /*
+ * data structures and defines for requesting extra,
+ * non pkcs15 related info from card
+ */
+enum {
+	/* some serial numbers */
+	SC_CARDCTL_EXTRAINFO_CARDNUMBER,
+	SC_CARDCTL_EXTRAINFO_CHIPNUMBER,
+	SC_CARDCTL_EXTRAINFO_NATIONALNUMBER,
+	SC_CARDCTL_EXTRAINFO_ISSUERNUMBER,
+	SC_CARDCTL_EXTRAINFO_SWVERSION,
+	/* information on document */
+	SC_CARDCTL_EXTRAINFO_VALIDFROM,
+	SC_CARDCTL_EXTRAINFO_VALIDTO,
+	SC_CARDCTL_EXTRAINFO_EXPEDITIONDATE,
+	SC_CARDCTL_EXTRAINFO_DOCTYPE,
+	SC_CARDCTL_EXTRAINFO_DOCSTATUS,
+	SC_CARDCTL_EXTRAINFO_ISSUERLOCATION,
+	/* information about user */
+	SC_CARDCTL_EXTRAINFO_SURNAME,
+	SC_CARDCTL_EXTRAINFO_GIVENNAME1,
+	SC_CARDCTL_EXTRAINFO_GIVENNAME2,
+	SC_CARDCTL_EXTRAINFO_INITIALS,
+	SC_CARDCTL_EXTRAINFO_NOBLECONDITION,
+	SC_CARDCTL_EXTRAINFO_BIRTHDATE,
+	SC_CARDCTL_EXTRAINFO_BIRTHLOCATION,
+	SC_CARDCTL_EXTRAINFO_NATIONALITY,
+	SC_CARDCTL_EXTRAINFO_SEX,
+	/* information about municipality */
+	SC_CARDCTL_EXTRAINFO_STREETANDNUMBER,
+	SC_CARDCTL_EXTRAINFO_ZIPCODE,
+	SC_CARDCTL_EXTRAINFO_MUNICIPALITY,
+	SC_CARDCTL_EXTRAINFO_COUNTRY,
+	SC_CARDCTL_EXTRAINFO_STATE,
+	/* card statistics (card dependent data) */
+	SC_CARDCTL_EXTRAINFO_STATS1,
+	SC_CARDCTL_EXTRAINFO_STATS2,
+	SC_CARDCTL_EXTRAINFO_STATS3,
+	SC_CARDCTL_EXTRAINFO_STATS4,
+	SC_CARDCTL_EXTRAINFO_STATS5,
+	/* optional card dependend data */
+	SC_CARDCTL_EXTRAINFO_REMARK1,
+	SC_CARDCTL_EXTRAINFO_REMARK2,
+	SC_CARDCTL_EXTRAINFO_REMARK3,
+	SC_CARDCTL_EXTRAINFO_REMARK4
+};
+
+struct sc_cardctl_extra_info {
+	int		index;		/* some of above enums */
+	const char *	name;		/* alias name */
+	char *		description;	/* printable data description */
+	char *		env_name;	/* environment var to be set */
+	char *		value;		/* data value or NULL if unavailable */
+	int		requested;	/* true to ask card driver to provide */
+};
+
+/*
  * GPK lock file.
  * Parent DF of file must be selected.
  */
diff -rbuN --exclude=.svn opensc/src/libopensc/card-mcrd.c opensc-eidenv/src/libopensc/card-mcrd.c
--- opensc/src/libopensc/card-mcrd.c	2011-02-10 08:32:47.000000000 +0100
+++ opensc-eidenv/src/libopensc/card-mcrd.c	2011-04-21 20:09:30.000000000 +0200
@@ -1380,6 +1380,113 @@
 	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, iso_ops->pin_cmd(card, data, tries_left));
 }
 
+/* moved by -jamc from tools/eidenv.c to make use of get_extrainfo cardctl */
+static struct {
+        int info_index;
+        int rec_number;
+} esteid_data[] = {
+        {SC_CARDCTL_EXTRAINFO_SURNAME, 1},
+        {SC_CARDCTL_EXTRAINFO_GIVENNAME1, 2},
+        {SC_CARDCTL_EXTRAINFO_GIVENNAME2, 3},
+        {SC_CARDCTL_EXTRAINFO_SEX, 4},
+        {SC_CARDCTL_EXTRAINFO_NATIONALITY, 5},
+        {SC_CARDCTL_EXTRAINFO_BIRTHDATE, 6},
+        {SC_CARDCTL_EXTRAINFO_NATIONALNUMBER, 7},
+        {SC_CARDCTL_EXTRAINFO_CARDNUMBER, 8},
+        {SC_CARDCTL_EXTRAINFO_VALIDTO, 9},
+        {SC_CARDCTL_EXTRAINFO_BIRTHLOCATION, 10},
+        {SC_CARDCTL_EXTRAINFO_EXPEDITIONDATE, 11},
+        {SC_CARDCTL_EXTRAINFO_DOCTYPE, 12},
+        {SC_CARDCTL_EXTRAINFO_REMARK1, 13},
+        {SC_CARDCTL_EXTRAINFO_REMARK2, 14},
+        {SC_CARDCTL_EXTRAINFO_REMARK3, 15},
+        {SC_CARDCTL_EXTRAINFO_REMARK4, 16},
+        {-1, 0}
+};
+
+static int esteid_get_extrainfo( sc_card_t *card, struct sc_cardctl_extra_info *info ) {
+	sc_path_t path;
+	int r = SC_ERROR_INTERNAL;
+	int i = 0;
+	unsigned char buff[512];
+	int key_used[4];
+	char *key;
+	char *value;
+
+	SC_FUNC_CALLED( card->ctx, SC_LOG_DEBUG_NORMAL );
+
+	/* evaluate stats */
+	sc_format_path("3F00EEEE0013", &path);
+	r = sc_select_file(card, &path, NULL);
+	if ( r != SC_SUCCESS ) {
+		sc_log(card->ctx, "Failed to select key counters: %s\n", sc_strerror(r));
+		goto get_extrainfo_out;
+	}
+	/* print the counters */
+	for (i = 1; i <= 4; i++) {
+		r = sc_read_record(card, i, buff, 128, SC_RECORD_BY_REC_NR);
+		key_used[i - 1] = 0xffffff - ((unsigned char) buff[0xc] * 65536
+								+ (unsigned char) buff[0xd] * 256
+								+ (unsigned char) buff[0xe]);
+	}
+	for (i = 0; i < 2; i++) {
+		key=calloc(32,sizeof(char));
+		value=calloc(32,sizeof(char));
+		if ( info[SC_CARDCTL_EXTRAINFO_STATS1+i].requested == 0 ) continue; 
+		if ( !key || !value ) {
+			sc_log(card->ctx,"Failed to allocate memory for stats");
+			r = SC_ERROR_OUT_OF_MEMORY;
+			goto get_extrainfo_out;
+		}
+		snprintf(key,32,"Key generation #%d usage",i);
+		snprintf(value,32,"sign: %d\t auth: %d",key_used[i], key_used[i + 2]);
+		info[SC_CARDCTL_EXTRAINFO_STATS1+i].description = key;
+		info[SC_CARDCTL_EXTRAINFO_STATS1+i].value = value;
+	}
+
+	/* read the datafile */
+
+	sc_format_path("3F00EEEE5044", &path);
+	r = sc_select_file(card, &path, NULL);
+	if (r) {
+		sc_log(card->ctx, "Failed to select DF: %s\n", sc_strerror(r));
+		goto get_extrainfo_out;
+	}
+
+	for (i = 0; esteid_data[i].rec_number != 0; i++) {
+		r = sc_read_record(card, esteid_data[i].rec_number, buff, 50, SC_RECORD_BY_REC_NR);
+		if (r < 0) {
+			sc_log (card->ctx, "Failed to read record %d from card: %s\n",
+						esteid_data[i].rec_number, sc_strerror (r));
+			goto get_extrainfo_out;
+		}
+		buff[r] = '\0';
+		if ( info[esteid_data[i].info_index].requested == 0 ) continue;
+		value=calloc(2+strlen((char*) buff),sizeof(char));
+		if (!value) {
+			sc_log(card->ctx,"Failed to allocate memory for record %d",esteid_data[i].rec_number);
+			r = SC_ERROR_OUT_OF_MEMORY;
+			goto get_extrainfo_out;
+		}
+		info[esteid_data[i].info_index].value=value;
+	}
+
+	/* arriving here means everything is ok */
+	r= SC_SUCCESS;
+
+get_extrainfo_out:
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+}
+
+static int mcrd_card_ctl(sc_card_t * card, unsigned long cmd, void *ptr)
+{
+	int r;
+	if ( !card || !ptr ) return SC_ERROR_INVALID_ARGUMENTS;
+	if (cmd != SC_CARDCTL_GET_EXTRA_INFO ) return SC_ERROR_NOT_SUPPORTED;
+	if ( is_esteid_card(card) != SC_SUCCESS ) return SC_ERROR_NOT_SUPPORTED;
+	return esteid_get_extrainfo(card,ptr);
+}
+
 /* Driver binding */
 static struct sc_card_driver *sc_get_driver(void)
 {
@@ -1394,6 +1501,7 @@
 	mcrd_ops.select_file = mcrd_select_file;
 	mcrd_ops.set_security_env = mcrd_set_security_env;
 	mcrd_ops.compute_signature = mcrd_compute_signature;
+	mcrd_ops.card_ctl = mcrd_card_ctl;
 	mcrd_ops.pin_cmd = mcrd_pin_cmd;
 
 	return &mcrd_drv;
diff -rbuN --exclude=.svn opensc/src/tools/eidenv.c opensc-eidenv/src/tools/eidenv.c
--- opensc/src/tools/eidenv.c	2011-03-30 11:12:13.000000000 +0200
+++ opensc-eidenv/src/tools/eidenv.c	2011-04-21 19:14:05.000000000 +0200
@@ -31,57 +31,39 @@
 #include "libopensc/opensc.h"
 #include "libopensc/asn1.h"
 #include "libopensc/cards.h"
-#include "libopensc/esteid.h"
+#include "libopensc/cardctl.h"
 #include "util.h"
 
 static char *opt_reader = NULL;
 static int stats = 0;
 static int opt_wait = 0;
 static char *exec_program = NULL;
+static char *opt_pin = NULL;
+static char *opt_driver = NULL;
 static int exit_status = EXIT_FAILURE;
+static int verbose = 0;
 
 static const struct option options[] = {
 	{"reader", required_argument, NULL, 'r'},
-	{"print", no_argument, NULL, 'p'},
+	{"driver", required_argument, NULL, 'd'},
+	{"list", no_argument, NULL, 'l'},
 	{"exec", required_argument, NULL, 'x'},
+	{"pin", required_argument, NULL, 'p'},
 	{"stats", no_argument, NULL, 't'},
 	{"help", no_argument, NULL, 'h'},
 	{"wait", no_argument, NULL, 'w'},
 	{"version", no_argument, NULL, 'V'},
+	{"verbose", no_argument, NULL, 'v'},
 	{NULL, 0, NULL, 0}
 };
 
 /* Probably not used, but needed to build on Windows */
 static const char *app_name = "eidenv";
 
-static struct {
-	const char *name;
-	const char *env_name;
-	int recno;
-} esteid_data[] = {
-	{"Surname", "ESTEID_SURNAME", 1},
-	{"Given names 1", "ESTEID_GIVEN_NAMES1", 2},
-	{"Given names 2", "ESTEID_GIVEN_NAMES2", 3},
-	{"Sex", "ESTEID_SEX", 4},
-	{"Citizenship", "ESTEID_CITIZENSHIP", 5},
-	{"Date of birth", "ESTEID_DATE_OF_BIRTH", 6},
-	{"Personal ID code", "ESTEID_PERSONAL_ID", 7},
-	{"Document number", "ESTEID_DOCUMENT_NR", 8},
-	{"Expiry date", "ESTEID_EXPIRY_DATE", 9},
-	{"Place of birth", "ESTEID_PLACE_OF_BIRTH", 10},
-	{"Issuing date", "ESTEID_ISSUING_DATE", 11},
-	{"Permit type", "ESTEID_PERMIT_TYPE", 12},
-	{"Remark 1", "ESTEID_REMARK1", 13},
-	{"Remark 2", "ESTEID_REMARK2", 14},
-	{"Remark 3", "ESTEID_REMARK3", 15},
-	{"Remark 4", "ESTEID_REMARK4", 16},
-	{NULL, NULL, 0}
-};
-
 static void show_version(void)
 {
 	fprintf(stderr,
-		"eidenv - EstEID utility version " PACKAGE_VERSION "\n"
+		"eidenv - EID utility version " PACKAGE_VERSION "\n"
 		"\n"
 		"Copyright (c) 2004 Martin Paljak <mar...@martinpaljak.net>\n"
 		"Licensed under LGPL v2\n");
@@ -92,11 +74,14 @@
 	show_version();
 	fprintf(stderr,
 		"-h --help      -  show this text and exit\n"
-		"-v --version   -  show version and exit\n"
-		"-r --reader    -  the reader to use\n"
+		"-V --version   -  show version and exit\n"
+		"-r --reader    -  the reader driver to use\n"
+		"-d --driver    -  the card driver to use\n"
+		"-p --pin       -  let the user specify pin\n"
 		"-w --wait      -  wait for a card to be inserted\n"
-		"-p --print     -  print the datafile\n"
+		"-l --list      -  print the datafile to stdout\n"
 		"-t --stats     -  show usage counts of keys\n"
+		"-v --verbose   -  Verbose operation. Use several times to enable debug output.\n"
 		"-x --exec      -  execute a program with data in env vars.\n");
 }
 
@@ -104,12 +89,18 @@
 {
 	int c;
 
-	while ((c = getopt_long(argc, argv,"pwtr:x:hV", options, (int *) 0)) != EOF) {
+	while ((c = getopt_long(argc, argv,"lp:wtr:d:x:hVv", options, (int *) 0)) != EOF) {
 
 		switch (c) {
+		case 'p':
+			opt_pin = optarg;
+			break;
 		case 'r':
 			opt_reader = optarg;
 			break;
+		case 'd':
+			opt_driver = optarg;
+			break;
 		case 't':
 			stats = !stats;
 			break;
@@ -122,7 +113,7 @@
 			show_help();
 			exit(EXIT_SUCCESS);
 			break;
-		case 'p':
+		case 'l':
 			break;
 		case 'w':
 			opt_wait = 1;
@@ -131,6 +122,9 @@
 			show_version();
 			exit(EXIT_SUCCESS);
 			break;
+		case 'v':
+			verbose++;
+			break;
 		default:
 			show_help();
 			exit(EXIT_FAILURE);
@@ -138,107 +132,58 @@
 	}
 }
 
-static void do_esteid(sc_card_t *card)
-{
-	sc_path_t path;
-	int r, i;
-	unsigned char buff[512];
-
-	if (stats) {
-		int key_used[4];
-		sc_format_path("3f00eeee0013", &path);
-		r = sc_select_file(card, &path, NULL);
-		if (r) {
-			fprintf(stderr, "Failed to select key counters: %s\n", sc_strerror(r));
-			goto out;
-		}
-	
-		/* print the counters */
-		for (i = 1; i <= 4; i++) {
-			r = sc_read_record(card, i, buff, 128, SC_RECORD_BY_REC_NR);
-			key_used[i - 1] = 0xffffff - ((unsigned char) buff[0xc] * 65536
-									+ (unsigned char) buff[0xd] * 256
-									+ (unsigned char) buff[0xe]);
-		}
-		for (i = 0; i < 2; i++) {
-			printf("Key generation #%d usage:\n\tsign: %d\n\tauth: %d\n",
-					 i, key_used[i], key_used[i + 2]);
-		}
-		exit_status = EXIT_SUCCESS;
-		goto out;
-	}
-	
-	/* Or just read the datafile */
-	sc_format_path("3f00eeee5044", &path);
-	r = sc_select_file(card, &path, NULL);
-	if (r) {
-		fprintf(stderr, "Failed to select DF: %s\n", sc_strerror(r));
-		goto out;
-	}
-
-	for (i = 0; esteid_data[i].recno != 0; i++) {
-		r = sc_read_record(card, esteid_data[i].recno, buff, 50, SC_RECORD_BY_REC_NR);
-		if (r < 0) {
-			fprintf (stderr, "Failed to read record %d from card: %s\n",
-						esteid_data[i].recno, sc_strerror (r));
-			goto out;
-		} 
-		buff[r] = '\0';
-		if (exec_program) {
-			unsigned char * cp;
-			cp = malloc(strlen(esteid_data[i].env_name) + 
-				strlen((char *) buff) + 2);
-			if (cp) { 
-				strcpy((char *) cp,esteid_data[i].env_name);
-				strcat((char *) cp,"=");
-				strcat((char *) cp,(char *) buff);
-				putenv((char *) cp);
-			}
-		} else {
-			printf("%s: %s\n", esteid_data[i].name, buff);
-		}
-	}
-	
-	exit_status = EXIT_SUCCESS;
-
-out:
-	return;
-}
-
-/* Select and read a transparent EF */
-static int read_transp(sc_card_t *card, const char *pathstring, unsigned char *buf, int buflen)
-{
-	sc_path_t path;
-	int r;
-
-	sc_format_path(pathstring, &path);
-	r = sc_select_file(card, &path, NULL);
-	if (r < 0)
-		fprintf(stderr, "\nFailed to select file %s: %s\n", pathstring, sc_strerror(r));
-	else {
-		r = sc_read_binary(card, 0, buf, buflen, 0);
-		if (r < 0)
-			fprintf(stderr, "\nFailed to read %s: %s\n", pathstring, sc_strerror(r));
-	}
-
-	return r;
-}
-
-/* Hex-encode the buf, 2*len+1 bytes must be reserved. E.g. {'1','2'} -> {'3','1','3','2','\0'} */
-static const char hextable[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'E'};
-static void bintohex(char *buf, int len)
-{
-	int i;
-	for (i = len - 1; i >= 0; i--) {
-		buf[2 * i + 1] = hextable[((unsigned char) buf[i]) % 16];
-		buf[2 * i] = hextable[((unsigned char) buf[i]) / 16];
-	}
-}
+struct sc_cardctl_extra_info info [] = {
+	/* some card numbers */
+        { SC_CARDCTL_EXTRAINFO_CARDNUMBER, "cardnumber", "Card Number", "SC_ENV_CARDNUMBER", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_CHIPNUMBER, "chipnumber", "Chip Number", "SC_ENV_CHIPNUMBER", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_NATIONALNUMBER, "nationalnumber", "National ID Number", "SC_ENV_NATIONALNUMBER", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_ISSUERNUMBER, "issuernumber", "Issuer ID Number", "SC_ENV_ISSUERNUMBER", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_SWVERSION, "swversion", "Card Software Version", "SC_ENV_SWVERSION", NULL, 1 },
+        /* information on document */
+        { SC_CARDCTL_EXTRAINFO_VALIDFROM, "validfrom" , "Valid From", "SC_ENV_VALIDFROM", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_VALIDTO, "validto" , "Valid To", "SC_ENV_VALIDTO", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_EXPEDITIONDATE, "expeditiondate" , "Expedition Date", "SC_ENV_EXPEDITIONDATE", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_DOCTYPE, "doctype" , "Document Type", "SC_ENV_DOCTYPE", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_DOCSTATUS, "docstatus" , "Document Status", "SC_ENV_VALIDFROM", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_ISSUERLOCATION, "issuerlocation" , "Issuer Location", "SC_ENV_ISSUERLOCATION", NULL, 1 },
+        /* information about user */
+        { SC_CARDCTL_EXTRAINFO_SURNAME, "surname" , "SurName", "SC_ENV_SURNAME", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_GIVENNAME1, "givenname1" , "Given Name", "SC_ENV_GIVENNAME1", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_GIVENNAME2, "givewnname2" , "Given Name 2", "SC_ENV_GIVENNAME2", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_INITIALS, "initials" , "Initials", "SC_ENV_INITIALS", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_NOBLECONDITION, "noblecondition" , "Noble Condition", "SC_ENV_NOBLECONDITION", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_BIRTHDATE, "birthdate" , "Birth Date", "SC_ENV_BIRTHDATE", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_BIRTHLOCATION, "birthlocation" , "Birth Location", "SC_ENV_BIRTHLOCATION", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_NATIONALITY, "nationality" , "Nationality", "SC_ENV_NATIONALITY", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_SEX, "sex" , "Sex (M/F)", "SC_ENV_SEX", NULL, 1 },
+        /* information about municipality */
+        { SC_CARDCTL_EXTRAINFO_STREETANDNUMBER, "streetandnumber" , "Street/Number Address", "SC_ENV_STREETANDNUMBER", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_ZIPCODE, "zipcode" , "Zip Code", "SC_ENV_ZIPCODE", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_MUNICIPALITY, "municipality" , "Municipality", "SC_ENV_MUNICIPALITY", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_COUNTRY, "country" , "Country", "SC_ENV_COUNTRY", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_STATE, "state" , "State", "SC_ENV_STATE", NULL, 1 },
+        /* card statistics (card dependent data) - Not translated into environment variables */
+        { SC_CARDCTL_EXTRAINFO_STATS1, "stats1" , "Statistics 1", NULL, NULL, 0 },
+        { SC_CARDCTL_EXTRAINFO_STATS2, "stats2" , "Statistics 2", NULL, NULL, 0 },
+        { SC_CARDCTL_EXTRAINFO_STATS3, "stats3" , "Statistics 3", NULL, NULL, 0 },
+        { SC_CARDCTL_EXTRAINFO_STATS4, "stats4" , "Statistics 4", NULL, NULL, 0 },
+        { SC_CARDCTL_EXTRAINFO_STATS5, "stats5" , "Statistics 5", NULL, NULL, 0 },
+        /* optional card dependend data */
+        { SC_CARDCTL_EXTRAINFO_REMARK1, "remark1" , "Extra info 1", "SC_ENV_REMARK1", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_REMARK2, "remark2" , "Extra info 2", "SC_ENV_REMARK2", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_REMARK3, "remark3" , "Extra info 3", "SC_ENV_REMARK3", NULL, 1 },
+        { SC_CARDCTL_EXTRAINFO_REMARK4, "remark4" , "Extra info 4", "SC_ENV_REMARK4", NULL, 1 },
+	{ -1, NULL, NULL, NULL, NULL, 0 }
+};
 
-static void exportprint(const char *key, const char *val)
+static void export_print(int index)
 {
-	if (exec_program) {
+	char * val = info[index].value;
+	if ( val == NULL ) val = "";
+	if ( exec_program ) {
 		char * cp;
+		if ( info[index].env_name == NULL ) return; /* no env var to create */
+		char *key = info[index].env_name;
 		cp = malloc(strlen(key) + strlen(val) + 2);
 		if (cp) { 
 			strcpy(cp, key);
@@ -246,134 +191,11 @@
 			strcat(cp, val);
 			putenv(cp);
 		}
-	} else
+	} else {
+		char * key = info[index].description;
+		if ( key == NULL ) key = "(empty)"; 
 		printf("%s: %s\n", key, val);
-}
-
-static void do_belpic(sc_card_t *card)
-{
-	/* Contents of the ID file (3F00\DF01\4031) */
-	struct {
-		char cardnumber[12 + 1];
-		char chipnumber[2 * 16 + 1];
-		char validfrom[10 + 1];
-		char validtill[10 + 1];
-		char deliveringmunicipality[50 + 1];  /* UTF8 */
-		char nationalnumber[12 + 1];
-		char name[90 + 1]; /* UTF8 */
-		char firstnames[75 + 1]; /* UTF8 */
-		char initial[3 + 1]; /* UTF8 */
-		char nationality[65 + 1]; /* UTF8 */
-		char birthlocation[60 + 1]; /* UTF8 */
-		char birthdate[12 + 1];
-		char sex[1 + 1];
-		char noblecondition[30 + 1]; /* UTF8 */
-		char documenttype[5 + 1];
-		char specialstatus[5 + 1];
-	} id_data;
-	int cardnumberlen = sizeof(id_data.cardnumber);
-	int chipnumberlen = sizeof(id_data.chipnumber);
-	int validfromlen = sizeof(id_data.validfrom);
-	int validtilllen = sizeof(id_data.validtill);
-	int deliveringmunicipalitylen = sizeof(id_data.deliveringmunicipality);
-	int nationalnumberlen = sizeof(id_data.nationalnumber);
-	int namelen = sizeof(id_data.name);
-	int firstnameslen = sizeof(id_data.firstnames);
-	int initiallen = sizeof(id_data.initial);
-	int nationalitylen = sizeof(id_data.nationality);
-	int birthlocationlen = sizeof(id_data.birthlocation);
-	int birthdatelen = sizeof(id_data.birthdate);
-	int sexlen = sizeof(id_data.sex);
-	int nobleconditionlen = sizeof(id_data.noblecondition);
-	int documenttypelen = sizeof(id_data.documenttype);
-	int specialstatuslen = sizeof(id_data.specialstatus);
-
-	struct sc_asn1_entry id[] = {
-		{"cardnumber", SC_ASN1_UTF8STRING, 1, 0, id_data.cardnumber, &cardnumberlen},
-		{"chipnumber", SC_ASN1_OCTET_STRING, 2, 0, id_data.chipnumber, &chipnumberlen},
-		{"validfrom", SC_ASN1_UTF8STRING, 3, 0, id_data.validfrom, &validfromlen},
-		{"validtill", SC_ASN1_UTF8STRING, 4, 0, id_data.validtill, &validtilllen},
-		{"deliveringmunicipality", SC_ASN1_UTF8STRING, 5, 0, id_data.deliveringmunicipality, &deliveringmunicipalitylen},
-		{"nationalnumber", SC_ASN1_UTF8STRING, 6, 0, id_data.nationalnumber, &nationalnumberlen},
-		{"name", SC_ASN1_UTF8STRING, 7, 0, id_data.name, &namelen},
-		{"firstname(s)", SC_ASN1_UTF8STRING, 8, 0, id_data.firstnames, &firstnameslen},
-		{"initial", SC_ASN1_UTF8STRING, 9, 0, id_data.initial, &initiallen},
-		{"nationality", SC_ASN1_UTF8STRING, 10, 0, id_data.nationality, &nationalitylen},
-		{"birthlocation", SC_ASN1_UTF8STRING, 11, 0, id_data.birthlocation, &birthlocationlen},
-		{"birthdate", SC_ASN1_UTF8STRING, 12, 0, id_data.birthdate, &birthdatelen},
-		{"sex", SC_ASN1_UTF8STRING, 13, 0, id_data.sex, &sexlen},
-		{"noblecondition", SC_ASN1_UTF8STRING, 14, 0, id_data.noblecondition, &nobleconditionlen},
-		{"documenttype", SC_ASN1_UTF8STRING, 15, 0, id_data.documenttype, &documenttypelen},
-		{"specialstatus", SC_ASN1_UTF8STRING, 16, 0, id_data.specialstatus, &specialstatuslen},
-		{NULL, 0, 0, 0, NULL, NULL}
-	};
-
-	/* Contents of the Address file (3F00\DF01\4033) */
-	struct {
-		char streetandnumber[63 + 1]; /* UTF8 */
-		char zipcode[4 + 1];
-		char municipality[40 + 1]; /* UTF8 */
-	} address_data;
-	int streetandnumberlen = sizeof(address_data.streetandnumber);
-	int zipcodelen = sizeof(address_data.zipcode);
-	int municipalitylen = sizeof(address_data.municipality);
-	struct sc_asn1_entry address[] = {
-		{"streetandnumber", SC_ASN1_UTF8STRING, 1, 0, address_data.streetandnumber, &streetandnumberlen},
-		{"zipcode", SC_ASN1_UTF8STRING, 2, 0, address_data.zipcode, &zipcodelen},
-		{"municipal", SC_ASN1_UTF8STRING, 3, 0, address_data.municipality, &municipalitylen},
-		{NULL, 0, 0, 0, NULL, NULL}};
-
-	unsigned char buff[512];
-	int r;
-
-	r = read_transp(card, "3f00df014031", buff, sizeof(buff));
-	if (r < 0)
-		goto out;
-
-	memset(&id_data, 0, sizeof(id_data));
-
-	r = sc_asn1_decode(card->ctx, id, buff, r, NULL, NULL);
-	if (r < 0) {
-		fprintf(stderr, "\nFailed to decode the ID file: %s\n", sc_strerror(r));
-		goto out;
 	}
-
-	exportprint("BELPIC_CARDNUMBER", id_data.cardnumber);
-	bintohex(id_data.chipnumber, chipnumberlen);
-	exportprint("BELPIC_CHIPNUMBER", id_data.chipnumber);
-	exportprint("BELPIC_VALIDFROM", id_data.validfrom);
-	exportprint("BELPIC_VALIDTILL", id_data.validtill);
-	exportprint("BELPIC_DELIVERINGMUNICIPALITY", id_data.deliveringmunicipality);
-	exportprint("BELPIC_NATIONALNUMBER", id_data.nationalnumber);
-	exportprint("BELPIC_NAME", id_data.name);
-	exportprint("BELPIC_FIRSTNAMES", id_data.firstnames);
-	exportprint("BELPIC_INITIAL", id_data.initial);
-	exportprint("BELPIC_NATIONALITY", id_data.nationality);
-	exportprint("BELPIC_BIRTHLOCATION", id_data.birthlocation);
-	exportprint("BELPIC_BIRTHDATE", id_data.birthdate);
-	exportprint("BELPIC_SEX", id_data.sex);
-	exportprint("BELPIC_NOBLECONDITION", id_data.noblecondition);
-	exportprint("BELPIC_DOCUMENTTYPE", id_data.documenttype);
-	exportprint("BELPIC_SPECIALSTATUS", id_data.specialstatus);
-
-	r = read_transp(card, "3f00df014033", buff, sizeof(buff));
-	if (r < 0)
-		goto out;
-
-	memset(&address_data, 0, sizeof(address_data));
-
-	r = sc_asn1_decode(card->ctx, address, buff, r, NULL, NULL);
-	if (r < 0) {
-		fprintf(stderr, "\nFailed to decode the Address file: %s\n", sc_strerror(r));
-		goto out;
-	}
-
-	exportprint("BELPIC_STREETANDNUMBER", address_data.streetandnumber);
-	exportprint("BELPIC_ZIPCODE", address_data.zipcode);
-	exportprint("BELPIC_MUNICIPALITY", address_data.municipality);
-
-out:
-	return;
 }
 
 int main(int argc, char **argv)
@@ -381,7 +203,8 @@
 	sc_context_t *ctx = NULL;
 	sc_context_param_t ctx_param;
 	sc_card_t *card = NULL;
-	int r;
+	int r=SC_SUCCESS;
+	int n=0;
 
 	/* get options */
 	decode_options(argc, argv);
@@ -397,21 +220,62 @@
 		sc_strerror(r));
 		return 1;
 	}
+
+	if (verbose > 1) {
+		ctx->debug = verbose;
+		sc_ctx_log_to_file(ctx,"stderr");
+	}
+
+	if (opt_driver != NULL) {
+		r = sc_set_card_driver(ctx, opt_driver);
+		if (r) {
+			fprintf(stderr, "Driver '%s' not found!\n", opt_driver);
+			goto out;
+		}
+	}
+
 	r = util_connect_card(ctx, &card, opt_reader, opt_wait, 0);
 	if (r) {
 		fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(r));
 		return 1;
 	}
 
-	/* Check card type */
-	if (card->type == SC_CARD_TYPE_MCRD_ESTEID_V10 || card->type == SC_CARD_TYPE_MCRD_ESTEID_V11 || card->type == SC_CARD_TYPE_MCRD_ESTEID_V30)
-		do_esteid(card);
-	else if (card->type == SC_CARD_TYPE_BELPIC_EID)
-		do_belpic(card);
-	else {
-		fprintf(stderr, "Not an EstEID or Belpic card!\n");
+	if ( opt_pin ) {
+		int tries_left=0;
+		/*  verify  */
+		r = sc_verify(card, SC_AC_CHV, 0,
+				(u8*)opt_pin, strlen(opt_pin), &tries_left);
+		if (r) {
+			fprintf(stderr, "Error: PIN verification failed: %s",
+					sc_strerror(r));
+			if (r == SC_ERROR_PIN_CODE_INCORRECT)
+				fprintf(stderr, " (tries left %d)", tries_left);
+			putc('\n', stderr);
 		goto out;
 	}
+	}
+
+	if ( stats ) {
+		for ( n=0; info[n].index != -1; n++) info[n].requested=0;
+		info[SC_CARDCTL_EXTRAINFO_STATS1].requested = 1;
+		info[SC_CARDCTL_EXTRAINFO_STATS2].requested = 1;
+		info[SC_CARDCTL_EXTRAINFO_STATS3].requested = 1;
+		info[SC_CARDCTL_EXTRAINFO_STATS4].requested = 1;
+		info[SC_CARDCTL_EXTRAINFO_STATS5].requested = 1;
+	}
+
+	/* invoke sc_card_ctl */
+	r = sc_card_ctl(card, SC_CARDCTL_GET_EXTRA_INFO, info);
+	if ( r != SC_SUCCESS ) {
+		fprintf(stderr, "Error: Get extra info failed: %s\n", sc_strerror(r));
+		goto out;
+	}
+
+	for (n=0; info[n].index != -1; n++) {
+		if ( info[n].requested == 0 ) continue;
+		if ( info[n].value==NULL) info[n].value = "";
+		export_print(n);
+	}
 	
 	if (exec_program) {
 		char *const largv[] = {exec_program, NULL};
@@ -421,12 +285,16 @@
 		execv(exec_program, largv);
 		/* we should not get here */
 		perror("execv()");
-		exit(1);
+		exit(exit_status);
 	}
 	
+	exit_status = EXIT_SUCCESS;
 out:
+	if (card) {
 	sc_unlock(card);
 	sc_disconnect_card(card);
+	}
+	if (ctx)
 	sc_release_context(ctx);
 	exit(exit_status);
 }
_______________________________________________
opensc-devel mailing list
opensc-devel@lists.opensc-project.org
http://www.opensc-project.org/mailman/listinfo/opensc-devel

Reply via email to