* sc-investigate: Removed.
authorWerner Koch <wk@gnupg.org>
Wed, 20 Oct 2004 08:54:45 +0000 (08:54 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 20 Oct 2004 08:54:45 +0000 (08:54 +0000)
* Makefile.am (sc_investigate): Removed.

* pcsc-wrapper.c (load_pcsc_driver): Load get_status_change func.
(handle_open): Succeed even without a present card.
(handle_status, handle_reset): New.

* apdu.c (apdu_open_reader): Load pcsc_get_status_change fucntion.
(pcsc_get_status): Implemented.
(reset_pcsc_reader): Implemented.
(open_pcsc_reader): Succeed even with no card inserted.
(open_ccid_reader): Set LAST_STATUS.

* iso7816.c (iso7816_select_application): Always use 0 for P1.

12 files changed:
NEWS
README
doc/ChangeLog
doc/gpg-agent.texi
doc/scdaemon.texi
scd/ChangeLog
scd/Makefile.am
scd/apdu.c
scd/ccid-driver.c
scd/iso7816.c
scd/pcsc-wrapper.c
scd/sc-investigate.c [deleted file]

diff --git a/NEWS b/NEWS
index 595684d..9ba9dda 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,10 @@
 Noteworthy changes in version 1.9.12
 -------------------------------------------------
 
+ * [scdaemon] Partly rewrote the PC/SC code.
+
+ * Removed the sc-investigate tool.  It is now in the separate
+   gscutils package.
 
 
 Noteworthy changes in version 1.9.11 (2004-10-01)
diff --git a/README b/README
index c08c16a..c14534e 100644 (file)
--- a/README
+++ b/README
@@ -472,7 +472,7 @@ HOW TO EXPORT A PRIVATE KEY
 There is also limited support to export a private key in PKCS-12
 format. However there is no MAC applied.
 
- gpgsm --export-secret-key-p12 userIDey  >foo.p12
+ gpgsm --export-secret-key-p12 userID  >foo.p12
 
 
 SMARTCARD INTRO
index c8e955e..c5ff576 100644 (file)
@@ -1,3 +1,8 @@
+2004-10-05  Werner Koch  <wk@g10code.com>
+
+       * gpg-agent.texi (Invoking GPG-AGENT): Tell that GPG_TTY needs to
+       be set in all cases.
+
 2004-09-30  Werner Koch  <wk@g10code.com>
 
        * gpg.texi: New.
index 26b5634..f361cbf 100644 (file)
@@ -43,10 +43,8 @@ fi
 @end smallexample
 
 @noindent
-If you want to use a curses based pinentry (which is usually also the
-fallback mode for a GUI based pinentry), you should add these lines to
-your @code{.bashrc} or whatever initialization file is used for all shell
-invocations:
+You should aleays add the following lines to your @code{.bashrc} or
+whatever initialization file is used for all shell invocations:
 
 @smallexample
 GPG_TTY=`tty`
index 42dedb6..8720001 100644 (file)
@@ -170,7 +170,8 @@ default is 32768 (first USB device).
 
 @item --ctapi-driver @var{library}
 Use @var{library} to access the smartcard reader.  The current default
-is @code{libtowitoko.so}.
+is @code{libtowitoko.so}.  Note that the use of this interface is
+deprecated; it may be removed in future releases.
 
 
 @item --allow-admin
index 7fbc524..eba39c7 100644 (file)
@@ -1,3 +1,24 @@
+2004-10-20  Werner Koch  <wk@g10code.com>
+
+       * sc-investigate: Removed.
+       * Makefile.am (sc_investigate): Removed.
+       
+       * pcsc-wrapper.c (load_pcsc_driver): Load get_status_change func.
+       (handle_open): Succeed even without a present card.
+       (handle_status, handle_reset): New.
+       
+       * apdu.c (apdu_open_reader): Load pcsc_get_status_change fucntion.
+       (pcsc_get_status): Implemented.
+       (reset_pcsc_reader): Implemented.
+       (open_pcsc_reader): Succeed even with no card inserted.
+       (open_ccid_reader): Set LAST_STATUS.
+
+       * iso7816.c (iso7816_select_application): Always use 0 for P1.
+
+2004-10-18  Werner Koch  <wk@g10code.com>
+
+       * ccid-driver.c (ccid_get_atr): Reset T=1 state info.
+
 2004-10-14  Werner Koch  <wk@g10code.com>
 
        * app-openpgp.c (parse_login_data): New. 
index d1d669a..0d83271 100644 (file)
@@ -18,7 +18,7 @@
 
 ## Process this file with automake to produce Makefile.in
 
-bin_PROGRAMS = scdaemon sc-investigate sc-copykeys
+bin_PROGRAMS = scdaemon sc-copykeys
 pkglib_PROGRAMS = pcsc-wrapper
 
 AM_CPPFLAGS = -I$(top_srcdir)/intl -I$(top_srcdir)/common
@@ -56,22 +56,6 @@ scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \
          $(LIBUSB_LIBS) $(OPENSC_LIBS) -lgpg-error @LIBINTL@ \
          @DL_LIBS@
 
-sc_investigate_SOURCES = \
-       sc-investigate.c scdaemon.h \
-       apdu.c apdu.h \
-       ccid-driver.c ccid-driver.h \
-       iso7816.c iso7816.h \
-       tlv.c tlv.h \
-       atr.c atr.h \
-       app.c app-common.h app-help.c $(card_apps) 
-
-sc_investigate_LDADD = \
-       ../jnlib/libjnlib.a ../common/libcommon.a \
-       $(LIBGCRYPT_LIBS) $(pth_libs) \
-       $(KSBA_LIBS) $(LIBUSB_LIBS) $(OPENSC_LIBS) \
-         @LIBINTL@  -lgpg-error @DL_LIBS@
-
-
 sc_copykeys_SOURCES = \
        sc-copykeys.c scdaemon.h \
        apdu.c apdu.h \
index 0d9ef3d..f4b32d1 100644 (file)
@@ -125,6 +125,8 @@ struct reader_table_s {
     rapdu_t handle;
   } rapdu;
 #endif /*USE_G10CODE_RAPDU*/
+  char *rdrname;     /* Name of the connected reader or NULL if unknown. */
+  int last_status;
   int status;
   unsigned char atr[33];
   size_t atrlen;           /* A zero length indicates that the ATR has
@@ -169,13 +171,47 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
 #define PCSC_UNPOWER_CARD    2
 #define PCSC_EJECT_CARD      3
 
-struct pcsc_io_request_s {
+#define PCSC_UNKNOWN    0x0001  
+#define PCSC_ABSENT     0x0002  /* Card is absent.  */
+#define PCSC_PRESENT    0x0004  /* Card is present.  */
+#define PCSC_SWALLOWED  0x0008  /* Card is present and electrical connected. */
+#define PCSC_POWERED    0x0010  /* Card is powered.  */
+#define PCSC_NEGOTIABLE 0x0020  /* Card is awaiting PTS.  */
+#define PCSC_SPECIFIC   0x0040  /* Card is ready for use.  */
+
+#define PCSC_STATE_UNAWARE     0x0000  /* Want status.  */
+#define PCSC_STATE_IGNORE      0x0001  /* Ignore this reader.  */
+#define PCSC_STATE_CHANGED     0x0002  /* State has changed.  */
+#define PCSC_STATE_UNKNOWN     0x0004  /* Reader unknown.  */
+#define PCSC_STATE_UNAVAILABLE 0x0008  /* Status unavailable.  */
+#define PCSC_STATE_EMPTY       0x0010  /* Card removed.  */
+#define PCSC_STATE_PRESENT     0x0020  /* Card inserted.  */
+#define PCSC_STATE_ATRMATCH    0x0040  /* ATR matches card. */
+#define PCSC_STATE_EXCLUSIVE   0x0080  /* Exclusive Mode.  */
+#define PCSC_STATE_INUSE       0x0100  /* Shared mode.  */
+#define PCSC_STATE_MUTE               0x0200  /* Unresponsive card.  */
+
+
+struct pcsc_io_request_s 
+{
   unsigned long protocol; 
   unsigned long pci_len;
 };
 
 typedef struct pcsc_io_request_s *pcsc_io_request_t;
 
+struct pcsc_readerstate_s
+{
+  const char *reader;
+  void *user_data;
+  unsigned long current_state;
+  unsigned long event_state;
+  unsigned long atrlen;
+  unsigned char atr[33];
+};
+
+typedef struct pcsc_readerstate_s *pcsc_readerstate_t;
+
 long (* DLSTDCALL pcsc_establish_context) (unsigned long scope,
                                            const void *reserved1,
                                            const void *reserved2,
@@ -184,12 +220,21 @@ long (* DLSTDCALL pcsc_release_context) (unsigned long context);
 long (* DLSTDCALL pcsc_list_readers) (unsigned long context,
                                       const char *groups,
                                       char *readers, unsigned long*readerslen);
+long (* DLSTDCALL pcsc_get_status_change) (unsigned long context,
+                                           unsigned long timeout,
+                                           pcsc_readerstate_t readerstates,
+                                           unsigned long nreaderstates);
 long (* DLSTDCALL pcsc_connect) (unsigned long context,
                                  const char *reader,
                                  unsigned long share_mode,
                                  unsigned long preferred_protocols,
                                  unsigned long *r_card,
                                  unsigned long *r_active_protocol);
+long (* DLSTDCALL pcsc_reconnect) (unsigned long card,
+                                   unsigned long share_mode,
+                                   unsigned long preferred_protocols,
+                                   unsigned long initialization,
+                                   unsigned long *r_active_protocol);
 long (* DLSTDCALL pcsc_disconnect) (unsigned long card,
                                     unsigned long disposition);
 long (* DLSTDCALL pcsc_status) (unsigned long card,
@@ -211,7 +256,6 @@ long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
 
 
 
-
 \f
 /* 
       Helper
@@ -254,11 +298,13 @@ new_reader_slot (void)
   reader_table[reader].dump_status_reader = NULL;
 
   reader_table[reader].used = 1;
+  reader_table[reader].last_status = 0;
 #ifdef NEED_PCSC_WRAPPER
   reader_table[reader].pcsc.req_fd = -1;
   reader_table[reader].pcsc.rsp_fd = -1;
   reader_table[reader].pcsc.pid = (pid_t)(-1);
 #endif
+
   return reader;
 }
 
@@ -662,18 +708,293 @@ dump_pcsc_reader_status (int slot)
 }
 
 
-
 static int
-pcsc_get_status (int slot, unsigned int *status)
+reset_pcsc_reader (int slot)
 {
-  *status = 1|2|4;  /* FIXME!!!! */
+#ifdef NEED_PCSC_WRAPPER
+  long err;
+  reader_table_t slotp;
+  size_t len;
+  int i, n;
+  unsigned char msgbuf[9];
+
+  slotp = reader_table + slot;
+
+  if (slotp->pcsc.req_fd == -1 
+      || slotp->pcsc.rsp_fd == -1 
+      || slotp->pcsc.pid == (pid_t)(-1) )
+    {
+      log_error ("pcsc_get_status: pcsc-wrapper not running\n");
+      return SW_HOST_CARD_IO_ERROR;
+    }
+
+  msgbuf[0] = 0x05; /* RESET command. */
+  len = 0;
+  msgbuf[1] = (len >> 24);
+  msgbuf[2] = (len >> 16);
+  msgbuf[3] = (len >>  8);
+  msgbuf[4] = (len      );
+  if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
+    {
+      log_error ("error sending PC/SC RESET request: %s\n",
+                 strerror (errno));
+      goto command_failed;
+    }
+
+  /* Read the response. */
+  if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
+    {
+      log_error ("error receiving PC/SC RESET response: %s\n",
+                 i? strerror (errno) : "premature EOF");
+      goto command_failed;
+    }
+  len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+  if (msgbuf[0] != 0x81 || len < 4)
+    {
+      log_error ("invalid response header from PC/SC received\n");
+      goto command_failed;
+    }
+  len -= 4; /* Already read the error code. */
+  if (len > DIM (slotp->atr))
+    {
+      log_error ("PC/SC returned a too large ATR (len=%x)\n", len);
+      goto command_failed;
+    }
+  err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+  if (err)
+    {
+      log_error ("PC/SC RESET failed: %s\n", pcsc_error_string (err));
+      goto command_failed;
+    }
+
+  /* The open fucntion may return a zero for the ATR length to
+     indicate that no card is present.  */
+  n = len;
+  if (n)
+    {
+      if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
+        {
+          log_error ("error receiving PC/SC RESET response: %s\n",
+                     i? strerror (errno) : "premature EOF");
+          goto command_failed;
+        }
+    }
+  slotp->atrlen = len;
+
+  return 0;
+
+ command_failed:
+  close (slotp->pcsc.req_fd);
+  close (slotp->pcsc.rsp_fd);
+  slotp->pcsc.req_fd = -1;
+  slotp->pcsc.rsp_fd = -1;
+  kill (slotp->pcsc.pid, SIGTERM);
+  slotp->pcsc.pid = (pid_t)(-1);
+  slotp->used = 0;
+  return -1;
+
+#else /* !NEED_PCSC_WRAPPER */
+  long err;
+  char reader[250];
+  unsigned long nreader, atrlen;
+  unsigned long card_state, card_protocol;
+
+  if (reader_table[slot].pcsc.card)
+    {
+      err = pcsc_disconnect (reader_table[slot].pcsc.card, PCSC_LEAVE_CARD);
+      if (err)
+        {
+          log_error ("pcsc_disconnect failed: %s (0x%lx)\n",
+                     pcsc_error_string (err), err);
+          return SW_HOST_CARD_IO_ERROR;
+        }
+      reader_table[slot].pcsc.card = 0;
+    }
+
+  err = pcsc_connect (reader_table[slot].pcsc.context,
+                      reader_table[slot].rdrname,
+                      PCSC_SHARE_EXCLUSIVE,
+                      PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
+                      &reader_table[slot].pcsc.card,
+                      &reader_table[slot].pcsc.protocol);
+  if (err)
+    {
+      log_error ("pcsc_connect failed: %s (0x%lx)\n",
+                  pcsc_error_string (err), err);
+      reader_table[slot].pcsc.card = 0;
+      return SW_HOST_CARD_IO_ERROR;
+    }      
+
+  
+  atrlen = 33;
+  nreader = sizeof reader - 1;
+  err = pcsc_status (reader_table[slot].pcsc.card,
+                     reader, &nreader,
+                     &card_state, &card_protocol,
+                     reader_table[slot].atr, &atrlen);
+  if (err)
+    {
+      log_error ("pcsc_status failed: %s (0x%lx)\n",
+                  pcsc_error_string (err), err);
+      reader_table[slot].atrlen = 0;
+      return SW_HOST_CARD_IO_ERROR;
+    }
+  if (atrlen >= DIM (reader_table[0].atr))
+    log_bug ("ATR returned by pcsc_status is too large\n");
+  reader_table[slot].atrlen = atrlen;
+
   return 0;
+#endif /* !NEED_PCSC_WRAPPER */
 }
 
+
 static int
-reset_pcsc_reader (int slot)
+pcsc_get_status (int slot, unsigned int *status)
 {
-  return SW_HOST_NOT_SUPPORTED;
+#ifdef NEED_PCSC_WRAPPER
+  long err;
+  reader_table_t slotp;
+  size_t len, full_len;
+  int i, n;
+  unsigned char msgbuf[9];
+  unsigned char buffer[12];
+
+  slotp = reader_table + slot;
+
+  if (slotp->pcsc.req_fd == -1 
+      || slotp->pcsc.rsp_fd == -1 
+      || slotp->pcsc.pid == (pid_t)(-1) )
+    {
+      log_error ("pcsc_get_status: pcsc-wrapper not running\n");
+      return SW_HOST_CARD_IO_ERROR;
+    }
+
+  msgbuf[0] = 0x04; /* STATUS command. */
+  len = 0;
+  msgbuf[1] = (len >> 24);
+  msgbuf[2] = (len >> 16);
+  msgbuf[3] = (len >>  8);
+  msgbuf[4] = (len      );
+  if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
+    {
+      log_error ("error sending PC/SC STATUS request: %s\n",
+                 strerror (errno));
+      goto command_failed;
+    }
+
+  /* Read the response. */
+  if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
+    {
+      log_error ("error receiving PC/SC STATUS response: %s\n",
+                 i? strerror (errno) : "premature EOF");
+      goto command_failed;
+    }
+  len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+  if (msgbuf[0] != 0x81 || len < 4)
+    {
+      log_error ("invalid response header from PC/SC received\n");
+      goto command_failed;
+    }
+  len -= 4; /* Already read the error code. */
+  err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+  if (err)
+    {
+      log_error ("pcsc_status failed: %s (0x%lx)\n",
+                 pcsc_error_string (err), err);
+      return SW_HOST_CARD_IO_ERROR;
+    }
+
+  full_len = len;
+  
+  n = 8 < len ? 8 : len;
+  if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != 8)
+    {
+      log_error ("error receiving PC/SC STATUS response: %s\n",
+                 i? strerror (errno) : "premature EOF");
+      goto command_failed;
+    }
+
+  full_len -= len;
+  /* Newer versions of the wrapper might send more status bytes.
+     Read them. */
+  while (full_len)
+    {
+      unsigned char dummybuf[128];
+
+      n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf);
+      if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n)
+        {
+          log_error ("error receiving PC/SC TRANSMIT response: %s\n",
+                     i? strerror (errno) : "premature EOF");
+          goto command_failed;
+        }
+      full_len -= n;
+    }
+   
+  /* We are lucky: The wrapper already returns the data in the
+     required format. */
+  *status = buffer[3];
+
+  return 0;
+
+ command_failed:
+  close (slotp->pcsc.req_fd);
+  close (slotp->pcsc.rsp_fd);
+  slotp->pcsc.req_fd = -1;
+  slotp->pcsc.rsp_fd = -1;
+  kill (slotp->pcsc.pid, SIGTERM);
+  slotp->pcsc.pid = (pid_t)(-1);
+  slotp->used = 0;
+  return -1;
+
+#else /*!NEED_PCSC_WRAPPER*/
+
+  long err;
+  struct pcsc_readerstate_s rdrstates[1];
+  
+  memset (rdrstates, 0, sizeof *rdrstates);
+  rdrstates[0].reader = reader_table[slot].rdrname;
+  rdrstates[0].current_state = PCSC_STATE_UNAWARE;
+  err = pcsc_get_status_change (reader_table[slot].pcsc.context,
+                                0,
+                                rdrstates, 1);
+  if (err == 0x8010000a) /* Timeout.  */
+    err = 0;
+  if (err)
+    {
+      log_error ("pcsc_get_status_change failed: %s (0x%lx)\n",
+                 pcsc_error_string (err), err);
+      return SW_HOST_CARD_IO_ERROR;
+    }
+
+
+  /*   log_debug  */
+  /*     ("pcsc_get_status_change: %s%s%s%s%s%s%s%s%s%s\n", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_IGNORE)? " ignore":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_CHANGED)? " changed":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_UNKNOWN)? " unknown":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_UNAVAILABLE)?" unavail":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_EMPTY)? " empty":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_PRESENT)? " present":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_ATRMATCH)? " atr":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_EXCLUSIVE)? " excl":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_INUSE)? " unuse":"", */
+  /*      (rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" ); */
+
+  *status = 0;
+  if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
+    *status |= 2;
+  if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
+    *status |= 4;
+  /* We indicate a useful card if it is not in use by another
+     application.  This is because we only use exclusive access
+     mode.  */
+  if ( (*status & 6) == 6
+       && !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
+    *status |= 1;
+  
+  return 0; 
+#endif /*!NEED_PCSC_WRAPPER*/
 }
 
 
@@ -889,6 +1210,8 @@ close_pcsc_reader (int slot)
 #else /*!NEED_PCSC_WRAPPER*/
 
   pcsc_release_context (reader_table[slot].pcsc.context);
+  xfree (reader_table[slot].rdrname);
+  reader_table[slot].rdrname = NULL;
   reader_table[slot].used = 0;
   return 0;
 #endif /*!NEED_PCSC_WRAPPER*/
@@ -999,8 +1322,14 @@ open_pcsc_reader (const char *portstr)
   slotp->pcsc.rsp_fd = rp[0];
 
   /* Wait for the intermediate child to terminate. */
-  while ( (i=pth_waitpid (pid, NULL, 0)) == -1 && errno == EINTR)
+#ifdef USE_GNU_PTH
+#define WAIT pth_waitpid 
+#else
+#define WAIT waitpid 
+#endif
+  while ( (i=WAIT (pid, NULL, 0)) == -1 && errno == EINTR)
     ;
+#undef X
 
   /* Now send the open request. */
   msgbuf[0] = 0x01; /* OPEN command. */
@@ -1041,12 +1370,23 @@ open_pcsc_reader (const char *portstr)
       log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));
       goto command_failed;
     }
+
+  slotp->last_status = 0;
+
+  /* The open fucntion may return a zero for the ATR length to
+     indicate that no card is present.  */
   n = len;
-  if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
+  if (n)
     {
-      log_error ("error receiving PC/SC OPEN response: %s\n",
-                 i? strerror (errno) : "premature EOF");
-      goto command_failed;
+      if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
+        {
+          log_error ("error receiving PC/SC OPEN response: %s\n",
+                     i? strerror (errno) : "premature EOF");
+          goto command_failed;
+        }
+      /* If we got to here we know that a card is present
+         and usable.  Thus remember this.  */
+      slotp->last_status = (1|2|4| 0x8000);
     }
   slotp->atrlen = len;
 
@@ -1132,41 +1472,63 @@ open_pcsc_reader (const char *portstr)
       p += strlen (p) + 1;
     }
 
+  reader_table[slot].rdrname = xtrymalloc (strlen (portstr? portstr : list)+1);
+  if (!reader_table[slot].rdrname)
+    {
+      log_error ("error allocating memory for reader name\n");
+      pcsc_release_context (reader_table[slot].pcsc.context);
+      reader_table[slot].used = 0;
+      return -1;
+    }
+  strcpy (reader_table[slot].rdrname, portstr? portstr : list);
+  xfree (list);
+
   err = pcsc_connect (reader_table[slot].pcsc.context,
-                      portstr? portstr : list,
+                      reader_table[slot].rdrname,
                       PCSC_SHARE_EXCLUSIVE,
                       PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
                       &reader_table[slot].pcsc.card,
                       &reader_table[slot].pcsc.protocol);
-  if (err)
+  if (err == 0x8010000c) /* No smartcard.  */
+    reader_table[slot].pcsc.card = 0;
+  else if (err)
     {
       log_error ("pcsc_connect failed: %s (0x%lx)\n",
                   pcsc_error_string (err), err);
       pcsc_release_context (reader_table[slot].pcsc.context);
+      xfree (reader_table[slot].rdrname);
+      reader_table[slot].rdrname = NULL;
       reader_table[slot].used = 0;
       xfree (list);
       return -1;
     }      
-  
-  atrlen = 32;
-  /* (We need to pass a dummy buffer.  We use LIST because it ought to
-     be large enough.) */
-  err = pcsc_status (reader_table[slot].pcsc.card,
-                     list, &listlen,
-                     &card_state, &card_protocol,
-                     reader_table[slot].atr, &atrlen);
-  xfree (list);
-  if (err)
+
+  reader_table[slot].atrlen = 0;
+  reader_table[slot].last_status = 0;
+  if (!err)
     {
-      log_error ("pcsc_status failed: %s (0x%lx)\n",
-                  pcsc_error_string (err), err);
-      pcsc_release_context (reader_table[slot].pcsc.context);
-      reader_table[slot].used = 0;
-      return -1;
+      char reader[250];
+      unsigned long readerlen;
+
+      atrlen = 32;
+      readerlen = sizeof reader -1 ;
+      err = pcsc_status (reader_table[slot].pcsc.card,
+                         reader, &readerlen,
+                         &card_state, &card_protocol,
+                         reader_table[slot].atr, &atrlen);
+      if (err)
+        log_error ("pcsc_status failed: %s (0x%lx) %lu\n",
+                   pcsc_error_string (err), err, readerlen);
+      else
+        {
+          if (atrlen >= DIM (reader_table[0].atr))
+            log_bug ("ATR returned by pcsc_status is too large\n");
+          reader_table[slot].atrlen = atrlen;
+          /* If we got to here we know that a card is present
+             and usable.  Thus remember this.  */
+          reader_table[slot].last_status = (1|2|4| 0x8000);
+        }
     }
-  if (atrlen >= DIM (reader_table[0].atr))
-    log_bug ("ATR returned by pcsc_status is too large\n");
-  reader_table[slot].atrlen = atrlen;
 
   reader_table[slot].close_reader = close_pcsc_reader;
   reader_table[slot].reset_reader = reset_pcsc_reader;
@@ -1311,6 +1673,12 @@ open_ccid_reader (const char *portstr)
       slotp->atrlen = 0;
       err = 0;
     }
+  else
+    {
+      /* If we got to here we know that a card is present
+         and usable.  Thus remember this.  */
+      reader_table[slot].last_status = (1|2|4| 0x8000);
+    }
 
   reader_table[slot].close_reader = close_ccid_reader;
   reader_table[slot].shutdown_reader = shutdown_ccid_reader;
@@ -1971,11 +2339,21 @@ apdu_open_reader (const char *portstr)
       if (!pcsc_list_readers)
         pcsc_list_readers    = dlsym (handle, "SCardListReadersA");
 #endif
+      pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange");
+#ifdef _WIN32
+      if (!pcsc_get_status_change)
+        pcsc_get_status_change = dlsym (handle, "SCardGetStatusChangeA");
+#endif
       pcsc_connect           = dlsym (handle, "SCardConnect");
 #ifdef _WIN32
       if (!pcsc_connect)
         pcsc_connect         = dlsym (handle, "SCardConnectA");
 #endif
+      pcsc_reconnect         = dlsym (handle, "SCardReconnect");
+#ifdef _WIN32
+      if (!pcsc_reconnect)
+        pcsc_reconnect       = dlsym (handle, "SCardReconnectA");
+#endif
       pcsc_disconnect        = dlsym (handle, "SCardDisconnect");
       pcsc_status            = dlsym (handle, "SCardStatus");
 #ifdef _WIN32
@@ -1990,7 +2368,9 @@ apdu_open_reader (const char *portstr)
       if (!pcsc_establish_context
           || !pcsc_release_context  
           || !pcsc_list_readers     
+          || !pcsc_get_status_change
           || !pcsc_connect          
+          || !pcsc_reconnect
           || !pcsc_disconnect
           || !pcsc_status
           || !pcsc_begin_transaction
@@ -2001,11 +2381,13 @@ apdu_open_reader (const char *portstr)
           /* Note that set_timeout is currently not used and also not
              available under Windows. */
           log_error ("apdu_open_reader: invalid PC/SC driver "
-                     "(%d%d%d%d%d%d%d%d%d%d)\n",
+                     "(%d%d%d%d%d%d%d%d%d%d%d%d)\n",
                      !!pcsc_establish_context,
                      !!pcsc_release_context,  
                      !!pcsc_list_readers,     
+                     !!pcsc_get_status_change,     
                      !!pcsc_connect,          
+                     !!pcsc_reconnect,          
                      !!pcsc_disconnect,
                      !!pcsc_status,
                      !!pcsc_begin_transaction,
@@ -2049,7 +2431,11 @@ apdu_open_remote_reader (const char *portstr,
                             writefnc, writefnc_value,
                             closefnc, closefnc_value);
 #else
+#ifdef _WIN32 
+  errno = ENOENT;
+#else
   errno = ENOSYS;
+#endif
   return -1;
 #endif
 }
@@ -2102,9 +2488,17 @@ apdu_reset (int slot)
   if ((sw = lock_slot (slot)))
     return sw;
 
+  reader_table[slot].last_status = 0;
   if (reader_table[slot].reset_reader)
     sw = reader_table[slot].reset_reader (slot);
 
+  if (!sw)
+    {
+      /* If we got to here we know that a card is present
+         and usable.  Thus remember this.  */
+      reader_table[slot].last_status = (1|2|4| 0x8000);
+    }
+
   unlock_slot (slot);
   return sw;
 }
@@ -2139,7 +2533,16 @@ apdu_activate (int slot)
           /* We don't have an ATR or a card is present though inactive:
              do a reset now. */
           if (reader_table[slot].reset_reader)
-            sw = reader_table[slot].reset_reader (slot);
+            {
+              reader_table[slot].last_status = 0;
+              sw = reader_table[slot].reset_reader (slot);
+              if (!sw)
+                {
+                  /* If we got to here we know that a card is present
+                     and usable.  Thus remember this.  */
+                  reader_table[slot].last_status = (1|2|4| 0x8000);
+                }
+            }
         }
     }
   
@@ -2201,7 +2604,22 @@ apdu_get_status (int slot, int hang,
   unlock_slot (slot);
 
   if (sw)
-    return sw;
+    {
+      reader_table[slot].last_status = 0;
+      return sw;
+    }
+
+  /* Keep track of changes.  We use one extra bit to test whether we
+     have checked the status at least once. */
+  if ( s != (reader_table[slot].last_status & 0x07ff)
+       || !reader_table[slot].last_status )
+    {
+      reader_table[slot].change_counter++;
+      /* Make sure that the ATR is invalid so that a reset will be by
+         activate.  */
+      reader_table[slot].atrlen = 0;
+    }
+  reader_table[slot].last_status = (s | 0x8000);
 
   if (status)
     *status = s;
index 0a876f0..01c8a99 100644 (file)
@@ -1307,6 +1307,8 @@ ccid_get_atr (ccid_driver_t handle,
   /* Note that we ignore the error code on purpose. */
   bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters, seqno);
 
+  handle->t1_ns = 0;
+  handle->t1_nr = 0;
 
   /* Send an S-Block with our maximun IFSD to the CCID.  */
   if (!handle->auto_ifsd)
index 4861466..9eff9d3 100644 (file)
@@ -126,15 +126,8 @@ iso7816_map_sw (int sw)
 gpg_error_t
 iso7816_select_application (int slot, const char *aid, size_t aidlen)
 {
-  static char const openpgp_aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
   int sw;
-  int p1 = 0x0C; /* No FCI to be returned. */
-  
-  if (aidlen == sizeof openpgp_aid
-      && !memcmp (aid, openpgp_aid, sizeof openpgp_aid))
-    p1 = 0; /* The current openpgp cards don't allow 0x0c. */
-
-  sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, p1, aidlen, aid);
+  sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid);
   return map_sw (sw);
 }
 
index 4f47ee9..93e78fd 100644 (file)
@@ -86,6 +86,26 @@ static int verbose;
 #define PCSC_UNPOWER_CARD    2
 #define PCSC_EJECT_CARD      3
 
+#define PCSC_UNKNOWN    0x0001  
+#define PCSC_ABSENT     0x0002  /* Card is absent.  */
+#define PCSC_PRESENT    0x0004  /* Card is present.  */
+#define PCSC_SWALLOWED  0x0008  /* Card is present and electrical connected. */
+#define PCSC_POWERED    0x0010  /* Card is powered.  */
+#define PCSC_NEGOTIABLE 0x0020  /* Card is awaiting PTS.  */
+#define PCSC_SPECIFIC   0x0040  /* Card is ready for use.  */
+
+#define PCSC_STATE_UNAWARE     0x0000  /* Want status.  */
+#define PCSC_STATE_IGNORE      0x0001  /* Ignore this reader.  */
+#define PCSC_STATE_CHANGED     0x0002  /* State has changed.  */
+#define PCSC_STATE_UNKNOWN     0x0004  /* Reader unknown.  */
+#define PCSC_STATE_UNAVAILABLE 0x0008  /* Status unavailable.  */
+#define PCSC_STATE_EMPTY       0x0010  /* Card removed.  */
+#define PCSC_STATE_PRESENT     0x0020  /* Card inserted.  */
+#define PCSC_STATE_ATRMATCH    0x0040  /* ATR matches card. */
+#define PCSC_STATE_EXCLUSIVE   0x0080  /* Exclusive Mode.  */
+#define PCSC_STATE_INUSE       0x0100  /* Shared mode.  */
+#define PCSC_STATE_MUTE               0x0200  /* Unresponsive card.  */
+
 struct pcsc_io_request_s {
   unsigned long protocol; 
   unsigned long pci_len;
@@ -93,12 +113,25 @@ struct pcsc_io_request_s {
 
 typedef struct pcsc_io_request_s *pcsc_io_request_t;
 
+struct pcsc_readerstate_s
+{
+  const char *reader;
+  void *user_data;
+  unsigned long current_state;
+  unsigned long event_state;
+  unsigned long atrlen;
+  unsigned char atr[33];
+};
+
+typedef struct pcsc_readerstate_s *pcsc_readerstate_t;
+
 
 static int driver_is_open;     /* True if the PC/SC driver has been
                                   initialzied and is ready for
-                                  operations.  The follwoing variables
+                                  operations.  The following variables
                                   are then valid. */
 static unsigned long pcsc_context;  /* The current PC/CS context. */
+static char *current_rdrname;
 static unsigned long pcsc_card;
 static unsigned long pcsc_protocol;
 static unsigned char current_atr[33];
@@ -112,12 +145,21 @@ long (* pcsc_release_context) (unsigned long context);
 long (* pcsc_list_readers) (unsigned long context,
                             const char *groups,
                             char *readers, unsigned long*readerslen);
+long (* pcsc_get_status_change) (unsigned long context,
+                                 unsigned long timeout,
+                                 pcsc_readerstate_t readerstates,
+                                 unsigned long nreaderstates);
 long (* pcsc_connect) (unsigned long context,
                        const char *reader,
                        unsigned long share_mode,
                        unsigned long preferred_protocols,
                        unsigned long *r_card,
                        unsigned long *r_active_protocol);
+long (* pcsc_reconnect) (unsigned long card,
+                         unsigned long share_mode,
+                         unsigned long preferred_protocols,
+                         unsigned long initialization,
+                         unsigned long *r_active_protocol);
 long (* pcsc_disconnect) (unsigned long card,
                           unsigned long disposition);
 long (* pcsc_status) (unsigned long card,
@@ -284,7 +326,9 @@ load_pcsc_driver (const char *libname)
   pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
   pcsc_release_context   = dlsym (handle, "SCardReleaseContext");
   pcsc_list_readers      = dlsym (handle, "SCardListReaders");
+  pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange");
   pcsc_connect           = dlsym (handle, "SCardConnect");
+  pcsc_reconnect         = dlsym (handle, "SCardReconnect");
   pcsc_disconnect        = dlsym (handle, "SCardDisconnect");
   pcsc_status            = dlsym (handle, "SCardStatus");
   pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction");
@@ -295,7 +339,9 @@ load_pcsc_driver (const char *libname)
   if (!pcsc_establish_context
       || !pcsc_release_context  
       || !pcsc_list_readers     
+      || !pcsc_get_status_change
       || !pcsc_connect          
+      || !pcsc_reconnect          
       || !pcsc_disconnect
       || !pcsc_status
       || !pcsc_begin_transaction
@@ -307,11 +353,13 @@ load_pcsc_driver (const char *libname)
          available under Windows. */
       fprintf (stderr,
                "apdu_open_reader: invalid PC/SC driver "
-               "(%d%d%d%d%d%d%d%d%d%d)\n",
+               "(%d%d%d%d%d%d%d%d%d%d%d%d)\n",
                !!pcsc_establish_context,
                !!pcsc_release_context,  
                !!pcsc_list_readers,     
+               !!pcsc_get_status_change,     
                !!pcsc_connect,          
+               !!pcsc_reconnect,          
                !!pcsc_disconnect,
                !!pcsc_status,
                !!pcsc_begin_transaction,
@@ -327,8 +375,8 @@ load_pcsc_driver (const char *libname)
 
 
 /* Handle a open request.  The argument is expected to be a string
-   with the port indentification. ARGBUF is always guaranteed to be
-   terminted by a 0 which is not counted in ARGLEN. We may modifiy
+   with the port identification.  ARGBUF is always guaranteed to be
+   terminted by a 0 which is not counted in ARGLEN.  We may modifiy
    ARGBUF. */
 static void
 handle_open (unsigned char *argbuf, size_t arglen)
@@ -350,6 +398,7 @@ handle_open (unsigned char *argbuf, size_t arglen)
     {
       fprintf (stderr, PGM ": PC/SC has already been opened\n");
       request_failed (-1);
+      return;
     }
 
   err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL, &pcsc_context);
@@ -398,45 +447,64 @@ handle_open (unsigned char *argbuf, size_t arglen)
       p += strlen (p) + 1;
     }
 
+  current_rdrname = malloc (strlen (portstr && *portstr? portstr:list)+1);
+  if (!current_rdrname)
+    {
+      fprintf (stderr, PGM": error allocating memory for reader name\n");
+      exit (1);
+    }
+  strcpy (current_rdrname, portstr && *portstr? portstr:list);
+  free (list);
+
   err = pcsc_connect (pcsc_context,
-                      portstr && *portstr? portstr : list,
+                      current_rdrname,
                       PCSC_SHARE_EXCLUSIVE,
                       PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
                       &pcsc_card,
                       &pcsc_protocol);
-  if (err)
+  if (err == 0x8010000c) /* No smartcard.  */
+    {
+      pcsc_card = 0;
+    }
+  else if (err)
     {
       fprintf (stderr, PGM": pcsc_connect failed: %s (0x%lx)\n",
                pcsc_error_string (err), err);
       pcsc_release_context (pcsc_context);
-      free (list);
+      free (current_rdrname);
+      current_rdrname = NULL;
       request_failed (err);
       return;
     }      
   
-  atrlen = 32;
-  /* (We need to pass a dummy buffer.  We use LIST because it ought to
-     be large enough.) */
-  err = pcsc_status (pcsc_card,
-                     list, &listlen,
-                     &card_state, &card_protocol,
-                     atr, &atrlen);
-  free (list);
-  if (err)
-    {
-      fprintf (stderr, PGM": pcsc_status failed: %s (0x%lx)\n",
-               pcsc_error_string (err), err);
-      pcsc_release_context (pcsc_context);
-      request_failed (err);
-      return;
-    }
-  if (atrlen >= sizeof atr || atrlen >= sizeof current_atr)
+  current_atrlen = 0;
+  if (!err)
     {
-      fprintf (stderr, PGM": ATR returned by pcsc_status is too large\n");
-      exit (4);
+      char reader[250];
+      unsigned long readerlen;
+
+      atrlen = 33;
+      readerlen = sizeof reader -1;
+      err = pcsc_status (pcsc_card,
+                         reader, &readerlen,
+                         &card_state, &card_protocol,
+                         atr, &atrlen);
+      if (err)
+        fprintf (stderr, PGM": pcsc_status failed: %s (0x%lx)\n",
+                 pcsc_error_string (err), err);
+      else
+        {
+          if (atrlen >= sizeof atr || atrlen >= sizeof current_atr)
+            {
+              fprintf (stderr, PGM": ATR returned by pcsc_status"
+                       " is too large\n");
+              exit (4);
+            }
+          memcpy (current_atr, atr, atrlen);
+          current_atrlen = atrlen;
+        }
     }
-  memcpy (current_atr, atr, atrlen);
-  current_atrlen = atrlen;
+
   driver_is_open = 1;
   request_succeeded (current_atr, current_atrlen);
 }
@@ -452,8 +520,11 @@ handle_close (unsigned char *argbuf, size_t arglen)
     {
       fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
       request_failed (-1);
+      return;
     }
 
+  free (current_rdrname);
+  current_rdrname = NULL;
   pcsc_release_context (pcsc_context);
 
   request_succeeded (NULL, 0);
@@ -461,7 +532,133 @@ handle_close (unsigned char *argbuf, size_t arglen)
 
 
 
-/* Handle a transmit request.  The argument is expected to be a bufer
+/* Handle a status request.  We expect no arguments.  We may modifiy
+   ARGBUF. */
+static void
+handle_status (unsigned char *argbuf, size_t arglen)
+{
+  long err;
+  struct pcsc_readerstate_s rdrstates[1];
+  int status;
+  unsigned char buf[20];
+
+  if (!driver_is_open)
+    {
+      fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
+      request_failed (-1);
+      return;
+    }
+
+  memset (rdrstates, 0, sizeof *rdrstates);
+  rdrstates[0].reader = current_rdrname;
+  rdrstates[0].current_state = PCSC_STATE_UNAWARE;
+  err = pcsc_get_status_change (pcsc_context,
+                                0,
+                                rdrstates, 1);
+  if (err == 0x8010000a) /* Timeout.  */
+    err = 0;
+  if (err)
+    {
+      fprintf (stderr, PGM": pcsc_get_status_change failed: %s (0x%lx)\n",
+               pcsc_error_string (err), err);
+      request_failed (err);
+      return;
+    }
+
+  status = 0;
+  if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
+    status |= 2;
+  if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
+    status |= 4;
+  /* We indicate a useful card if it is not in use by another
+     application.  This is because we only use exclusive access
+     mode.  */
+  if ( (status & 6) == 6
+       && !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
+    status |= 1;
+  
+  /* First word is identical to the one used by apdu.c. */
+  buf[0] = 0;
+  buf[1] = 0;
+  buf[2] = 0;
+  buf[3] = status;
+  /* The second word is the native PCSC state.  */
+  buf[4] = (rdrstates[0].event_state >> 24);
+  buf[5] = (rdrstates[0].event_state >> 16);
+  buf[6] = (rdrstates[0].event_state >>  8);
+  buf[7] = (rdrstates[0].event_state >>  0);
+
+  request_succeeded (buf, 8);
+}
+
+
+/* Handle a reset request.  We expect no arguments.  We may modifiy
+   ARGBUF. */
+static void
+handle_reset (unsigned char *argbuf, size_t arglen)
+{
+  long err;
+  char reader[250];
+  unsigned long nreader, atrlen;
+  unsigned long card_state, card_protocol;
+
+  if (!driver_is_open)
+    {
+      fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
+      request_failed (-1);
+      return;
+    }
+
+  if (pcsc_card)
+    {
+      err = pcsc_disconnect (pcsc_card, PCSC_LEAVE_CARD);
+      if (err)
+        {
+          fprintf (stderr, PGM": pcsc_disconnect failed: %s (0x%lx)\n",
+                     pcsc_error_string (err), err);
+          request_failed (err);
+          return;
+        }
+      pcsc_card = 0;
+    }
+
+  err = pcsc_connect (pcsc_context,
+                      current_rdrname,
+                      PCSC_SHARE_EXCLUSIVE,
+                      PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
+                      &pcsc_card,
+                      &pcsc_protocol);
+  if (err)
+    {
+      fprintf (stderr, PGM": pcsc_connect failed: %s (0x%lx)\n",
+               pcsc_error_string (err), err);
+      pcsc_card = 0;
+      request_failed (err);
+      return;
+    }      
+
+  
+  atrlen = 33;
+  nreader = sizeof reader - 1;
+  err = pcsc_status (pcsc_card,
+                     reader, &nreader,
+                     &card_state, &card_protocol,
+                     current_atr, &atrlen);
+  if (err)
+    {
+      fprintf (stderr, PGM": pcsc_status failed: %s (0x%lx)\n",
+               pcsc_error_string (err), err);
+      current_atrlen = 0;
+      request_failed (err);
+      return;
+    }
+
+  request_succeeded (current_atr, current_atrlen);
+}
+
+
+
+/* Handle a transmit request.  The argument is expected to be a buffer
    with the APDU.  We may modifiy ARGBUF. */
 static void
 handle_transmit (unsigned char *argbuf, size_t arglen)
@@ -479,8 +676,8 @@ handle_transmit (unsigned char *argbuf, size_t arglen)
     {
       fprintf (stderr, PGM ": PC/SC has not yet been opened\n");
       request_failed (-1);
+      return;
     }
-
   if ((pcsc_protocol & PCSC_PROTOCOL_T1))
     send_pci.protocol = PCSC_PROTOCOL_T1;
   else
@@ -502,15 +699,6 @@ handle_transmit (unsigned char *argbuf, size_t arglen)
 
 
 
-
-
-
-
-
-
-
-
-
 static void
 print_version (int with_help)
 {
@@ -613,6 +801,14 @@ main (int argc, char **argv)
           handle_transmit (argbuffer, arglen);
           break;
 
+        case 4:
+          handle_status (argbuffer, arglen);
+          break;
+
+        case 5:
+          handle_reset (argbuffer, arglen);
+          break;
+
         default:
           fprintf (stderr, PGM ": invalid request 0x%02X\n", c);
           exit (1);
diff --git a/scd/sc-investigate.c b/scd/sc-investigate.c
deleted file mode 100644 (file)
index 1f19206..0000000
+++ /dev/null
@@ -1,812 +0,0 @@
-/* sc-investigate.c - A tool to look around on smartcards.
- *     Copyright (C) 2003 Free Software Foundation, Inc.
- *
- * This file is part of GnuPG.
- *
- * GnuPG is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * GnuPG is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <ctype.h>
-#include <unistd.h>
-#ifdef USE_GNU_PTH
-# include <pth.h>
-#endif
-
-#ifdef HAVE_READLINE_READLINE_H
-#include <readline/readline.h>
-#include <readline/history.h>
-#endif
-
-#define JNLIB_NEED_LOG_LOGV
-#include "scdaemon.h"
-#include <gcrypt.h>
-
-#include "apdu.h" /* for open_reader */
-#include "atr.h"
-#include "app-common.h"
-#include "iso7816.h"
-
-#define _(a) (a)
-
-#define CONTROL_D ('D' - 'A' + 1)
-
-
-enum cmd_and_opt_values 
-{ 
-  oInteractive    = 'i',
-  oVerbose       = 'v',
-  oQuiet          = 'q',
-  oReaderPort     = 500,
-  octapiDriver,
-
-  oDebug,
-  oDebugAll,
-
-  oDisableCCID,
-
-
-  oGenRandom,
-
-aTest };
-
-
-static ARGPARSE_OPTS opts[] = {
-  
-  { 301, NULL, 0, "@Options:\n " },
-
-  { oInteractive, "interactive", 0, "start in interactive explorer mode"},
-  { oQuiet,       "quiet", 0, "quiet" },
-  { oVerbose, "verbose",   0, "verbose" },
-  { oReaderPort, "reader-port", 2, "|N|connect to reader at port N"},
-  { octapiDriver, "ctapi-driver", 2, "|NAME|use NAME as ctAPI driver"},
-  { oDisableCCID, "disable-ccid", 0,
-#ifdef HAVE_LIBUSB
-                                    "do not use the internal CCID driver"
-#else
-                                    "@"
-#endif
-  },
-  { oDebug,    "debug"     ,4|16, "set debugging flags"},
-  { oDebugAll, "debug-all" ,0, "enable full debugging"},
-  { oGenRandom, "gen-random", 4, "|N|generate N bytes of random"},
-  {0}
-};
-
-#ifndef HAVE_OPENSC
-#ifdef USE_GNU_PTH
-/* Pth wrapper function definitions. */
-GCRY_THREAD_OPTION_PTH_IMPL;
-#endif /*USE_GNU_PTH*/
-#endif /*!HAVE_OPENSC*/
-
-static void interactive_shell (int slot);
-static void dump_other_cards (int slot);
-
-static const char *
-my_strusage (int level)
-{
-  const char *p;
-  switch (level)
-    {
-    case 11: p = "sc-investigate (GnuPG)";
-      break;
-    case 13: p = VERSION; break;
-    case 17: p = PRINTABLE_OS_NAME; break;
-    case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
-      break;
-    case 1:
-    case 40: p =  _("Usage: sc-investigate [options] (-h for help)\n");
-      break;
-    case 41: p =  _("Syntax: sc-investigate [options] [args]]\n"
-                    "Have a look at smartcards\n");
-    break;
-    
-    default: p = NULL;
-    }
-  return p;
-}
-
-/* Used by gcry for logging */
-static void
-my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
-{
-  /* translate the log levels */
-  switch (level)
-    {
-    case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
-    case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
-    case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
-    case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
-    case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
-    case GCRY_LOG_BUG:  level = JNLIB_LOG_BUG; break;
-    case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
-    default:            level = JNLIB_LOG_ERROR; break;  
-    }
-  log_logv (level, fmt, arg_ptr);
-}
-
-
-int
-main (int argc, char **argv )
-{
-  ARGPARSE_ARGS pargs;
-  int slot, rc;
-  const char *reader_port = NULL;
-  unsigned long gen_random = 0;
-  int interactive = 0;
-
-  set_strusage (my_strusage);
-  gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
-  log_set_prefix ("sc-investigate", 1); 
-
-  /* Try to auto set the character set.  */
-  set_native_charset (NULL); 
-
-  /* Libgcrypt requires us to register the threading model first.  We
-     can't use pth at all if we are using OpenSC becuase OpenSC uses
-     ptreads.  Note that this will also do the pth_init. */
-#ifndef HAVE_OPENSC
-#ifdef USE_GNU_PTH
-  rc = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth);
-  if (rc)
-    {
-      log_fatal ("can't register GNU Pth with Libgcrypt: %s\n",
-                 gpg_strerror (rc));
-    }
-#endif /*USE_GNU_PTH*/
-#endif /*!HAVE_OPENSC*/
-
-  /* Check that the libraries are suitable.  Do it here because
-     the option parsing may need services of the library */
-  if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
-    {
-      log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
-                 NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
-    }
-
-
-  gcry_set_log_handler (my_gcry_logger, NULL);
-  /* FIXME? gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);*/
-
-  pargs.argc = &argc;
-  pargs.argv = &argv;
-  pargs.flags=  1;  /* do not remove the args */
-  while (arg_parse (&pargs, opts) )
-    {
-      switch (pargs.r_opt)
-        {
-        case oVerbose: opt.verbose++; break;
-        case oQuiet: opt.quiet++; break;
-        case oDebug: opt.debug |= pargs.r.ret_ulong; break;
-        case oDebugAll: opt.debug = ~0; break;
-        case oReaderPort: reader_port = pargs.r.ret_str; break;
-        case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break;
-        case oDisableCCID: opt.disable_ccid = 1; break;
-        case oGenRandom: gen_random = pargs.r.ret_ulong; break;
-        case oInteractive: interactive = 1; break;
-        default : pargs.err = 2; break;
-       }
-    }
-  if (log_get_errorcount(0))
-    exit(2);
-
-  if (opt.verbose < 2)
-    opt.verbose = 2; /* Hack to let select_openpgp print some info. */
-
-  if (argc)
-    usage (1);
-
-  slot = apdu_open_reader (reader_port);
-  if (slot == -1)
-    exit (1);
-  
-  if (!gen_random && !opt.quiet)
-    {
-      rc = atr_dump (slot, stdout); 
-      if (rc)
-        log_error ("can't dump ATR: %s\n", gpg_strerror (rc));
-    }
-
-  if (interactive)
-    interactive_shell (slot);
-  else
-    {
-      struct app_ctx_s appbuf;
-
-      /* Fixme: We better use app.c directly. */
-      memset (&appbuf, 0, sizeof appbuf);
-      appbuf.slot = slot;
-      rc = app_select_openpgp (&appbuf);
-      if (rc)
-        {
-          if (!opt.quiet)
-            log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
-          memset (&appbuf, 0, sizeof appbuf);
-          appbuf.slot = slot;
-          rc = app_select_dinsig (&appbuf);
-          if (rc)
-            {
-              if (!opt.quiet)
-                log_info ("selecting dinsig failed: %s\n", gpg_strerror (rc));
-              dump_other_cards (slot);
-            }
-          else
-            {
-              appbuf.initialized = 1;
-              log_info ("dinsig application selected\n");
-            }
-        }
-      else
-        {
-          appbuf.initialized = 1;
-          log_info ("openpgp application selected\n");
-
-          if (gen_random)
-            {
-              size_t nbytes;
-              unsigned char *buffer;
-          
-              buffer = xmalloc (4096);
-              do 
-                {
-                  nbytes = gen_random > 4096? 4096 : gen_random;
-                  rc = app_get_challenge (&appbuf, nbytes, buffer);
-                  if (rc)
-                    log_error ("app_get_challenge failed: %s\n",gpg_strerror (rc));
-                  else
-                    {
-                      if (fwrite (buffer, nbytes, 1, stdout) != 1)
-                        log_error ("writing to stdout failed: %s\n",
-                                   strerror (errno));
-                      gen_random -= nbytes;
-                    }
-                }
-              while (gen_random && !log_get_errorcount (0));
-              xfree (buffer);
-            }
-        }
-    }
-  
-  return log_get_errorcount (0)? 2:0;
-}
-
-
-
-void
-send_status_info (CTRL ctrl, const char *keyword, ...)
-{
-  /* DUMMY */
-}
-
-
-
-/* Dump BUFFER of length NBYTES in a nicely human readable format. */ 
-static void
-dump_buffer (const unsigned char *buffer, size_t nbytes)
-{
-  int i;
-
-  while (nbytes)
-    {
-      for (i=0; i < 16 && i < nbytes; i++)
-        printf ("%02X%s ", buffer[i], i==8? " ":"");
-      for (; i < 16; i++)
-        printf ("  %s ", i==8? " ":"");
-      putchar (' ');
-      putchar (' ');
-      for (i=0; i < 16 && i < nbytes; i++)
-        if (isprint (buffer[i]))
-          putchar (buffer[i]);
-        else
-          putchar ('.');
-      nbytes -= i;
-      buffer += i;
-      for (; i < 16; i++)
-        putchar (' ');
-      putchar ('\n');
-    }
-}
-
-
-static void
-dump_or_store_buffer (const char *arg,
-                      const unsigned char *buffer, size_t nbytes)
-{
-  const char *s = strchr (arg, '>');
-  int append;
-  FILE *fp;
-
-  if (!s)
-    {
-      dump_buffer (buffer, nbytes);
-      return;
-    }
-  if ((append = (*++s == '>')))
-    s++;
-  fp = fopen (s, append? "ab":"wb");
-  if (!fp)
-    {
-      log_error ("failed to create `%s': %s\n", s, strerror (errno));
-      return;
-    }
-  if (nbytes && fwrite (buffer, nbytes, 1, fp) != 1)
-      log_error ("failed to write to `%s': %s\n", s, strerror (errno));
-  if (fclose (fp))
-      log_error ("failed to close `%s': %s\n", s, strerror (errno));
-}
-
-
-/* Convert STRING into a a newly allocated buffer and return the
-   length of the buffer in R_LENGTH.  Detect xx:xx:xx... sequence and
-   unhexify that one. */
-static unsigned char *
-pin_to_buffer (const char *string, size_t *r_length)
-{
-  unsigned char *buffer = xmalloc (strlen (string)+1);
-  const char *s;
-  size_t n;
-
-  for (s=string, n=0; *s; s += 3)
-    {
-      if (hexdigitp (s) && hexdigitp (s+1) && (s[2]==':'||!s[2]))
-        {
-          buffer[n++] = xtoi_2 (s);
-          if (!s[2])
-            break;
-        }
-      else
-        {
-          memcpy (buffer, string, strlen (string));
-          *r_length = strlen (string);
-          return buffer;
-        }
-    }
-  *r_length = n;
-  return buffer;
-}
-
-
-static char *
-my_read_line (int use_readline, char *prompt)
-{
-  static char buf[256];
-
-#ifdef HAVE_READLINE
-  if (use_readline)
-    {
-      char *line = readline (prompt);
-      if (line)
-        trim_spaces (line);
-      if (line && strlen (line) > 2 )
-        add_history (line);
-      return line;
-    }
-#endif
-  /* Either we don't have readline or we are not running
-     interactively */
-#ifndef HAVE_READLINE
-  printf ("%s", prompt );
-#endif
-  fflush(stdout);
-  if (!fgets(buf, sizeof(buf), stdin))
-    return NULL;
-  if (!strlen(buf))
-    return NULL;
-  if (buf[strlen (buf)-1] == '\n')
-    buf[strlen (buf)-1] = 0;
-  trim_spaces (buf);
-  return buf;
-}
-
-/* Run a shell for interactive exploration of the card. */
-static void
-interactive_shell (int slot)
-{
-  enum cmdids
-    {
-      cmdNOP = 0,
-      cmdQUIT, cmdHELP,
-      cmdSELECT,
-      cmdCHDIR,
-      cmdLS,
-      cmdAPP,
-      cmdREAD,
-      cmdREADREC,
-      cmdREADSHORTREC,
-      cmdDEBUG,
-      cmdVERIFY,
-      cmdCHANGEREF,
-      cmdREADPK,
-
-      cmdINVCMD
-    };
-  static struct 
-  {
-    const char *name;
-    enum cmdids id;
-    const char *desc;
-  } cmds[] = {
-    { "quit"   , cmdQUIT  , "quit this menu" },
-    { "q"      , cmdQUIT  , NULL   },
-    { "help"   , cmdHELP  , "show this help" },
-    { "?"      , cmdHELP  , NULL   },
-    { "debug"  , cmdDEBUG, "set debugging flags" },
-    { "select" , cmdSELECT, "select file (EF)" },
-    { "s"      , cmdSELECT, NULL },
-    { "chdir"  , cmdCHDIR, "change directory (select DF)"},
-    { "cd"     , cmdCHDIR,  NULL },
-    { "ls"     , cmdLS,    "list directory (some cards only)"},
-    { "app"    , cmdAPP,   "select application"},
-    { "read"   , cmdREAD,  "read binary" },
-    { "rb"     , cmdREAD,  NULL },
-    { "readrec", cmdREADREC,  "read record(s)" },
-    { "rr"     , cmdREADREC,  NULL },
-    { "rsr"    , cmdREADSHORTREC,  "readshortrec RECNO SHORT_EF" },
-    { "verify" , cmdVERIFY, "verify CHVNO PIN" },
-    { "ver"    , cmdVERIFY, NULL },
-    { "changeref", cmdCHANGEREF, "change reference data" },
-    { "readpk",    cmdREADPK,    "read a public key" },
-    { NULL, cmdINVCMD } 
-  };
-  enum cmdids cmd = cmdNOP;
-  int use_readline = isatty (fileno(stdin));
-  char *line;
-  gpg_error_t err = 0;
-  unsigned char *result = NULL;
-  size_t resultlen;
-
-#ifdef HAVE_READLINE
-  if (use_readline)
-    using_history ();
-#endif
-
-  for (;;)
-    {
-      int arg_number;
-      const char *arg_string = "";
-      const char *arg_next = "";
-      char *p;
-      int i;
-      
-      if (err)
-        printf ("command failed: %s\n", gpg_strerror (err));
-      err = 0;
-      xfree (result);
-      result = NULL;
-
-      printf ("\n");
-      do
-        {
-          line = my_read_line (use_readline, "cmd> ");
-       }
-      while ( line && *line == '#' );
-
-      arg_number = 0; 
-      if (!line || *line == CONTROL_D)
-        cmd = cmdQUIT; 
-      else if (!*line)
-        cmd = cmdNOP;
-      else {
-        if ((p=strchr (line,' ')))
-          {
-            char *endp;
-
-            *p++ = 0;
-            trim_spaces (line);
-            trim_spaces (p);
-            arg_number = strtol (p, &endp, 0);
-            arg_string = p;
-            if (endp != p)
-              {
-                arg_next = endp;
-                while ( spacep (arg_next) )
-                  arg_next++;
-              }
-          }
-
-        for (i=0; cmds[i].name; i++ )
-          if (!ascii_strcasecmp (line, cmds[i].name ))
-            break;
-        
-        cmd = cmds[i].id;
-      }
-      
-      switch (cmd)
-        {
-        case cmdHELP:
-          for (i=0; cmds[i].name; i++ )
-            if (cmds[i].desc)
-              printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) );
-          break;
-
-        case cmdQUIT:
-          goto leave;
-
-        case cmdNOP:
-          break;
-
-        case cmdDEBUG:
-          if (!*arg_string)
-            opt.debug = opt.debug? 0 : 2048;
-          else
-            opt.debug = arg_number;
-          break;
-
-        case cmdSELECT:
-          err = iso7816_select_file (slot, arg_number, 0, NULL, NULL);
-          break;
-
-        case cmdCHDIR:
-          err = iso7816_select_file (slot, arg_number, 1, NULL, NULL);
-          break;
-
-        case cmdLS:
-          err = iso7816_list_directory (slot, 1, &result, &resultlen);
-          if (!err || gpg_err_code (err) == GPG_ERR_ENOENT)
-            err = iso7816_list_directory (slot, 0, &result, &resultlen);
-          /* FIXME: Do something with RESULT. */
-          break;
-
-        case cmdAPP:
-          {
-            app_t app;
-
-            app = select_application (NULL, slot, *arg_string? arg_string:NULL);
-            if (app)
-              {
-                char *sn;
-
-                app_get_serial_and_stamp (app, &sn, NULL);
-                log_info ("application `%s' ready; sn=%s\n",
-                          app->apptype?app->apptype:"?", sn? sn:"[none]");
-                release_application (app);
-              }
-          }
-          break;
-
-        case cmdREAD:
-          err = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
-          if (!err)
-            dump_or_store_buffer (arg_string, result, resultlen);
-          break;
-
-        case cmdREADREC:
-          if (*arg_string == '*' && (!arg_string[1] || arg_string[1] == ' '))
-            {
-              /* Fixme: Can't write to a file yet. */
-              for (i=1, err=0; !err; i++)
-                {
-                  xfree (result); result = NULL;
-                  err = iso7816_read_record (slot, i, 1, 0,
-                                             &result, &resultlen);
-                  if (!err)
-                    dump_buffer (result, resultlen);
-                }
-              if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
-                err = 0;
-            }
-          else
-            {
-              err = iso7816_read_record (slot, arg_number, 1, 0,
-                                         &result, &resultlen);
-              if (!err)
-                dump_or_store_buffer (arg_string, result, resultlen);
-            }
-          break;
-
-        case cmdREADSHORTREC:
-          {
-            int short_ef;
-
-            short_ef = strtol (arg_next, NULL, 0);
-            
-            if (short_ef < 1 || short_ef > 254)
-              printf ("error: short EF must be between 1 and 254\n");
-            else
-              {
-                err = iso7816_read_record (slot, arg_number, 1, short_ef,
-                                           &result, &resultlen);
-                if (!err)
-                  dump_or_store_buffer (arg_string, result, resultlen);
-              }
-          }
-          break;
-
-        case cmdVERIFY:
-          if (arg_number < 0 || arg_number > 255 || (arg_number & 127) > 31)
-            printf ("error: invalid CHVNO\n");
-          else 
-            {
-              unsigned char *pin;
-              size_t pinlen;
-
-              pin = pin_to_buffer (arg_next, &pinlen);
-              err = iso7816_verify (slot, arg_number, pin, pinlen);
-              xfree (pin);
-            }
-            break;
-
-        case cmdCHANGEREF:
-          {
-            const char *newpin = arg_next;
-            
-            while ( *newpin && !spacep (newpin) )
-              newpin++;
-            while ( spacep (newpin) )
-              newpin++;
-
-            if (arg_number < 0 || arg_number > 255 || (arg_number & 127) > 31)
-              printf ("error: invalid CHVNO\n");
-            else if (!*arg_next || !*newpin || newpin == arg_next)
-              printf ("usage: changeref CHVNO OLDPIN NEWPIN\n");
-            else
-              {
-                char *oldpin = xstrdup (arg_next);
-                unsigned char *oldpin_buf, *newpin_buf;
-                size_t oldpin_len, newpin_len;
-
-                for (p=oldpin; *p && !spacep (p); p++ )
-                  ;
-                *p = 0;
-                oldpin_buf = pin_to_buffer (oldpin, &oldpin_len);
-                newpin_buf = pin_to_buffer (newpin, &newpin_len);
-
-                err = iso7816_change_reference_data (slot, arg_number,
-                                                     oldpin_buf, oldpin_len,
-                                                     newpin_buf, newpin_len);
-
-                xfree (newpin_buf);
-                xfree (oldpin_buf);
-                xfree (oldpin);
-              }
-          }
-          break;
-
-        case cmdREADPK:
-          if (arg_number < 1 || arg_number > 255)
-            printf ("usage: readpk CRTBYTE1\n");
-          else
-            {
-              unsigned char crt[2];
-            
-              crt[0] = arg_number;
-              crt[1] = 0;
-              err = iso7816_read_public_key(slot, crt, 2,
-                                            &result, &resultlen);
-              if (!err) 
-                dump_or_store_buffer (arg_string, result, resultlen);
-            }
-            break;
-
-
-        case cmdINVCMD:
-        default:
-          printf ("\n");
-          printf ("Invalid command  (try \"help\")\n");
-          break;
-        } /* End command switch. */
-    } /* End of main menu loop. */
-
- leave:
-  ;
-}
-
-
-
-/* Figure out whether the current card is a German Geldkarte and print
-   what we know about it. */
-static int
-dump_geldkarte (int slot)
-{
-  unsigned char *r = NULL;
-  size_t rlen;
-  const char *t;
-
-  if (iso7816_read_record (slot, 1, 1, 0xbc, &r, &rlen))
-    return -1;
-  /* We require that the record is at least 24 bytes, the first byte
-     is 0x67 and the filler byte is correct. */
-  if (rlen < 24 || *r != 0x67 || r[22])
-    return -1;
-  
-  /* The short Bankleitzahl consists of 3 bytes at offset 1.  */
-  switch (r[1])
-    {
-    case 0x21: t = "Oeffentlich-rechtliche oder private Bank"; break;
-    case 0x22: t = "Privat- oder Geschäftsbank"; break;
-    case 0x25: t = "Sparkasse"; break;
-    case 0x26:
-    case 0x29: t = "Genossenschaftsbank"; break;
-    default: 
-      xfree (r);
-      return -1;  /* Probably not a Geldkarte. */
-    }
-
-  printf ("KBLZ .....: %02X-%02X%02X (%s)\n", r[1], r[2], r[3], t);
-  printf ("Card-No ..: %02X%02X%02X%02X%02X\n", r[4], r[5], r[6], r[7], r[8]);
-   
-/*   Byte 10 enthält im linken Halbbyte eine Prüfziffer, die nach dem */
-/*   Verfahren 'Luhn formula for computing modulus 10' über die Ziffern der */
-/*   ersten 9 Byte berechnet ist. */
-  
-/*   Das rechte Halbbyte wird zu 'D' gesetzt.  */
-  
-/*   Für die Berechnung der Luhn-Prüfziffer sind die folgenden Schritte */
-/*   durchzuführen: */
-  
-/*   Schritt 1:        Mit der rechtesten Ziffer beginnend ist einschließlich dieser */
-/*   Ziffer jede übernächste Ziffer zu verdoppeln (mit 2 multiplizieren). */
-  
-/*   Schritt 2:        Die einzelnen Ziffern der Produkte aus Schritt 1 und die bei */
-/*   diesen Multiplikationen unberührt gebliebenen Ziffern sind zu addieren. */
-  
-/*   Schritt 3:        Das Ergebnis der Addition aus Schritt 2 ist von dem auf die */
-/*   nächst höhere Zahl mit der Einerstelle 0 aufgerundeten Ergebnis der */
-/*   Addition aus Schritt 2 abzuziehen. Wenn das Ergebnis der Addition aus */
-/*   Schritt 2 bereits eine Zahl mit der Einerstelle 0 ergibt (z.B. 30, 40, */
-/*   usw.), ist die Prüfziffer 0. */
-  
-/*   Beispiel: Kartennummer ohne Prüfziffer: 992 839 871 */
-  
-/*    9   9   2   8   3   9   8   7   1 */
-  
-/*   x 2     x 2     x 2     x 2     x 2       Schritt 1 */
-  
-/*   18       4       6      16       2 */
-  
-/*   1+8 +9  +4  +8  +6  +9 +1+6 +7  +2 = 61   Schritt 2 */
-  
-/*   70-61 = 9                                 Schritt 3 */
-  
-/*   Prüfziffer zu 992 839 871 = 9 */
-
-
-  printf ("Expires at: %02X/%02X\n", r[11], r[10] );
-  printf ("Valid from: %02X.%02X.%02X\n", r[14], r[13], r[12]);
-  printf ("Country ..: %02X%02X\n", r[15], r[16]);
-  printf ("Currency .: %c%c%c\n", isascii (r[17])? r[17]:' ',
-          isascii (r[18])? r[18]:' ', isascii (r[19])? r[19]:' ');
-  printf ("Cur.-Mult : %s\n", 
-          r[20] == 0x01? "0.01":
-          r[20] == 0x02? "0.1":
-          r[20] == 0x04? "1":
-          r[20] == 0x08? "10":
-          r[20] == 0x10? "100":
-          r[20] == 0x20? "1000": "?");
-  printf ("ZKA ChipID: %02X\n", r[21]);
-  printf ("OS version: %02X\n", r[23]);
-
-  xfree (r);
-  return 0;
-} 
-
-
-
-/* Try to figure out the type of teh card and dump its contents. */
-static void
-dump_other_cards (int slot)
-{
-
-  if (!dump_geldkarte (slot))
-    return; 
-
-} 
-