* README: Doc --disable-card-support and --without-readline.
authorWerner Koch <wk@gnupg.org>
Thu, 9 Sep 2004 18:18:36 +0000 (18:18 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 9 Sep 2004 18:18:36 +0000 (18:18 +0000)
* configure.ac: Check for readline.  Make enable-card-support the
default.  New option --without-readline.  Allow the use of either
the development or the stable libusb.

* cardglue.h: Add members for CA fingerprints.
* cardglue.c (agent_release_card_info): Invalid them.
(learn_status_cb): Store them.

* app-common.h, app-openpgp.c, iso7816.c, iso7816.h
* apdu.c, apdu.h, ccid-driver.c, ccid-driver.h
* card-util.c: Updated from current gnupg-1.9.

* ccid-driver.h (CCID_DRIVER_ERR_ABORTED): New.
* ccid-driver.c (ccid_open_reader): Support the stable 0.1 version
of libusb.
(ccid_get_atr): Handle short messages.
* apdu.c (my_rapdu_get_status): Implemented.
* apdu.c: Include <signal.h>.
* apdu.c (reader_table_s):  Add function pointers for the backends.
(apdu_close_reader, apdu_get_status, apdu_activate)
(send_apdu): Make use of them.
(new_reader_slot): Intialize them to NULL.
(dump_ccid_reader_status, ct_dump_reader_status): New.
(dump_pcsc_reader_status): New.
(open_ct_reader, open_pcsc_reader, open_ccid_reader)
(open_osc_reader, open_rapdu_reader): Intialize function pointers.
(ct_activate_card, ct_send_apdu, pcsc_send_apdu, osc_send_apdu)
(error_string): Removed.  Replaced by apdu_strerror.
(get_ccid_error_string): Removed.
(ct_activate_card): Remove the unused loop.
(reset_ct_reader): Implemented.
(ct_send_apdu): Activate the card if not yet done.
(pcsc_send_apdu): Ditto.
* ccid-driver.h: Add error codes.
* ccid-driver.c: Implement more or less proper error codes all
over the place.
* apdu.c (apdu_send_direct): New.
(get_ccid_error_string): Add some error code mappings.
(send_apdu): Pass error codes along for drivers already supporting
them.
(host_sw_string): New.
(get_ccid_error_string): Use above.
(send_apdu_ccid): Reset the reader if it has not yet been done.
(open_ccid_reader): Don't care if the ATR can't be read.
(apdu_activate_card): New.
(apdu_strerror): New.
(dump_reader_status): Only enable it with opt.VERBOSE.
* iso7816.c (map_sw): Add mappings for the new error codes.
* apdu.c (open_ct_reader, open_pcsc_reader, open_ccid_reader)
(reset_ccid_reader, open_osc_reader): Call dump_reader_status only
in verbose mode.
* app-openpgp.c (do_getattr): Fix for sending CA-FPR.
* app-openpgp.c (app_openpgp_readkey): Fixed check for valid
exponent.
* app-openpgp.c (do_setattr): Sync FORCE_CHV1.
* card-util.c (change_login): Kludge to allow reading data from a
file.
(card_edit): Pass ARG_STRING to change_login.
(card_status): Print CA fingerprints.
(change_cafpr): New.
(card_edit): New command CAFPR.

* errors.h (G10ERR_NO_CARD, G10ERR_CANCELED): New error codes.

* errors.c (g10_errstr): New error codes G10ERR_NO_CARD,
G10ERR_CANCELED.

21 files changed:
ChangeLog
NEWS
README
TODO
configure.ac
g10/ChangeLog
g10/apdu.c
g10/apdu.h
g10/app-openpgp.c
g10/card-util.c
g10/cardglue.c
g10/cardglue.h
g10/ccid-driver.c
g10/ccid-driver.h
g10/iso7816.c
include/ChangeLog
include/errors.h
po/en@boldquot.gmo
po/en@quot.gmo
util/ChangeLog
util/errors.c

index c6d8e09..ff62810 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,10 @@
 2004-09-09  Werner Koch  <wk@g10code.com>
 
-       * configure.ac: Check for readline.
+       * README: Doc --disable-card-support and --without-readline.
+
+       * configure.ac: Check for readline.  Make enable-card-support the
+       default.  New option --without-readline.  Allow the use of either
+       the development or the stable libusb.
 
 2004-07-27  Werner Koch  <wk@g10code.de>
 
diff --git a/NEWS b/NEWS
index 5cf3d27..0a2df5b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,8 +1,13 @@
 Noteworthy changes in version 1.3.6 (2004-05-22)
 ------------------------------------------------
 
-    * Readline support at all prompt if the systems provides a
-      readline library.
+    * Readline support at all prompts is now available if the systems
+      provides a readline library.  The build time option
+      --without-readline may be used to disable this feature.
+
+    * Support for the OpenPGP smartcard is now enabled by default.
+      Use the option --disable-card-support to build without support
+      for smartcards.
 
     * New --keyid-format option that selects short (99242560), long
       (DB698D7199242560), 0xshort (0x99242560), or 0xlong
diff --git a/README b/README
index 22d1ad0..6a7912c 100644 (file)
--- a/README
+++ b/README
                       auto - Compile linux, egd and unix in and 
                              automagically select at runtime.
   
-    --with-egd-socket=<name>
+     --with-egd-socket=<name>
                      This is only used when EGD is used as random
                      gatherer. GnuPG uses by default "~/.gnupg/entropy"
                      as the socket to connect EGD.  Using this option the
                      "~/" uses the socket in the home directory of the user
                      and one starting with a "=" uses a socket in the
                      GnuPG home directory which is "~/.gnupg" by default.
-   
+     --without-readline
+                     Do not include support for the readline libary
+                     even if it is available.  The default is to check
+                     whether the readline libarry is a availbale and
+                     use it to allow fancy command line editing.
+  
      --with-included-zlib
                      Forces usage of the local zlib sources. Default is
                     to use the (shared) library of the system.
                     platforms where memory is an issue, it can be set
                     as low as 5.
 
+     --disable-card-support
+                    Do not include smartcard support.  The default is
+                    to include support if all required libraries are
+                    available.
+
 
     Installation Problems
     ---------------------
diff --git a/TODO b/TODO
index e63f05a..d51ce1b 100644 (file)
--- a/TODO
+++ b/TODO
@@ -72,9 +72,6 @@
 
   * Get new assembler stuff from gmp 3.1
 
-  * use DEL and ^H for erasing the previous character (util/ttyio.c).
-    or better readline.
-
   * add test cases for invalid data (scrambled armor or other random data)
 
   * add checking of armor trailers.  Try to detect garbled header
index 9bde518..00c18f9 100644 (file)
@@ -104,10 +104,20 @@ fi
 
 AC_MSG_CHECKING([whether OpenPGP card support is requested])
 AC_ARG_ENABLE(card-support,
-    [  --enable-card-support   enable OpenPGP card support],
-    card_support=$enableval, card_support=no)
+              AC_HELP_STRING([--disable-card-support],
+                             [enable OpenPGP card support]),
+              card_support=$enableval, card_support=yes)
 AC_MSG_RESULT($card_support)
 
+AC_MSG_CHECKING([whether readline support is requested])
+AC_ARG_WITH(readline,
+              AC_HELP_STRING([--without-readline],
+                             [do not support fancy command line editing]),
+              [readline_support="$withval"], [readline_support=yes])  
+case "$readline_support" in yes);; no);; *)readline_support=yes;; esac
+AC_MSG_RESULT($readline_support)
+
+
 dnl See if we are disabling any algorithms or features for a smaller
 dnl binary
 
@@ -1072,23 +1082,24 @@ AC_SUBST(ZLIBS)
 #
 # libusb allows us to use the integrated CCID smartcard reader driver.
 #
-# Note, that we need the CVS (unstable) version. 
-#
 LIBUSB_LIBS=""
 if test "$card_support" = yes; then
-    AC_CHECK_LIB(usb, usb_find_device,
+    AC_CHECK_LIB(usb, usb_bulk_write,
                  [ LIBUSB_LIBS="$LIBUSB_LIBS -lusb"
                    AC_DEFINE(HAVE_LIBUSB,1,
                    [defined if libusb is available])
                  ])
 fi
 AC_SUBST(LIBUSB_LIBS)
+AC_CHECK_FUNCS(usb_create_match)
 
 #
 # Check for readline support
 #
-AC_CHECK_LIB(readline, add_history)
-AC_CHECK_HEADERS([readline/readline.h])
+if test "$readline_support" = yes ; then
+    AC_CHECK_LIB(readline, add_history)
+    AC_CHECK_HEADERS([readline/readline.h])
+fi
 
 
 # Allow users to append something to the version string without
index bd01c87..9824018 100644 (file)
@@ -1,5 +1,79 @@
 2004-09-09  Werner Koch  <wk@g10code.com>
 
+       * cardglue.h: Add members for CA fingerprints.
+       * cardglue.c (agent_release_card_info): Invalid them.
+       (learn_status_cb): Store them.
+
+       * app-common.h, app-openpgp.c, iso7816.c, iso7816.h
+       * apdu.c, apdu.h, ccid-driver.c, ccid-driver.h
+       * card-util.c: Updated from current gnupg-1.9.
+
+       Changes are:
+
+       * ccid-driver.h (CCID_DRIVER_ERR_ABORTED): New.
+       * ccid-driver.c (ccid_open_reader): Support the stable 0.1 version
+       of libusb.
+       (ccid_get_atr): Handle short messages.
+       * apdu.c (my_rapdu_get_status): Implemented.
+       * apdu.c: Include <signal.h>.
+       * apdu.c (reader_table_s):  Add function pointers for the backends.
+       (apdu_close_reader, apdu_get_status, apdu_activate) 
+       (send_apdu): Make use of them.
+       (new_reader_slot): Intialize them to NULL.
+       (dump_ccid_reader_status, ct_dump_reader_status): New.
+       (dump_pcsc_reader_status): New.
+       (open_ct_reader, open_pcsc_reader, open_ccid_reader) 
+       (open_osc_reader, open_rapdu_reader): Intialize function pointers.
+       (ct_activate_card, ct_send_apdu, pcsc_send_apdu, osc_send_apdu) 
+       (error_string): Removed.  Replaced by apdu_strerror.
+       (get_ccid_error_string): Removed.
+       (ct_activate_card): Remove the unused loop.
+       (reset_ct_reader): Implemented.
+       (ct_send_apdu): Activate the card if not yet done.
+       (pcsc_send_apdu): Ditto.
+       * ccid-driver.h: Add error codes.
+       * ccid-driver.c: Implement more or less proper error codes all
+       over the place.
+       * apdu.c (apdu_send_direct): New.
+       (get_ccid_error_string): Add some error code mappings.
+       (send_apdu): Pass error codes along for drivers already supporting
+       them.
+       (host_sw_string): New.
+       (get_ccid_error_string): Use above.
+       (send_apdu_ccid): Reset the reader if it has not yet been done.
+       (open_ccid_reader): Don't care if the ATR can't be read.
+       (apdu_activate_card): New.
+       (apdu_strerror): New.
+       (dump_reader_status): Only enable it with opt.VERBOSE.
+       * iso7816.c (map_sw): Add mappings for the new error codes.
+       * apdu.c (open_ct_reader, open_pcsc_reader, open_ccid_reader)
+       (reset_ccid_reader, open_osc_reader): Call dump_reader_status only
+       in verbose mode.
+       * app-openpgp.c (do_getattr): Fix for sending CA-FPR.
+       * app-openpgp.c (app_openpgp_readkey): Fixed check for valid
+       exponent.
+       * app-openpgp.c (do_setattr): Sync FORCE_CHV1.
+       * card-util.c (change_login): Kludge to allow reading data from a
+       file.
+       (card_edit): Pass ARG_STRING to change_login.
+       (card_status): Print CA fingerprints.
+       (change_cafpr): New.
+       (card_edit): New command CAFPR.
+
+2004-04-30  Werner Koch  <wk@gnupg.org>
+
+       * g10.c (main) <gpgconf>: Use gpg.conf and not /dev/null as
+       default filename.
+
+2004-04-28  Werner Koch  <wk@gnupg.org>
+
+       * card-util.c (card_edit): Remove PIN verification.
+       (generate_card_keys): New arg SERIALNO.  Do PIN verification here
+       after resetting forced_chv1.
+
+       
+2004-09-09  Werner Koch  <wk@g10code.com>
+
        * signal.c (got_fatal_signal): Do readline cleanup.  Print signal
        number if we can't print the name. Use new autoconf macro
        HAVE_DECL_SYS_SIGLIST.
index 42b3372..5f800c9 100644 (file)
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#include <signal.h>
 #ifdef USE_GNU_PTH
 # include <pth.h>
 # include <unistd.h>
 #endif
 #ifdef HAVE_OPENSC
 # include <opensc/opensc.h>
+# ifdef USE_GNU_PTH
+# undef USE_GNU_PTH
+# endif
 #endif
 
+/* If requested include the definitions for the remote APDU protocol
+   code. */
+#ifdef USE_G10CODE_RAPDU
+#include "rapdu.h"
+#endif /*USE_G10CODE_RAPDU*/
+
 #if defined(GNUPG_SCD_MAIN_HEADER)
 #include GNUPG_SCD_MAIN_HEADER
 #elif GNUPG_MAJOR_VERSION == 1
@@ -59,8 +69,6 @@
 
 
 #define MAX_READER 4 /* Number of readers we support concurrently. */
-#define CARD_CONNECT_TIMEOUT 1 /* Number of seconds to wait for
-                                  insertion of the card (1 = don't wait). */
 
 
 #ifdef _WIN32
 struct reader_table_s {
   int used;            /* True if slot is used. */
   unsigned short port; /* Port number:  0 = unused, 1 - dev/tty */
-  int is_ccid;         /* Uses the internal CCID driver. */
+
+  /* Function pointers intialized to the various backends.  */
+  int (*close_reader)(int);
+  int (*reset_reader)(int);
+  int (*get_status_reader)(int, unsigned int *);
+  int (*send_apdu_reader)(int,unsigned char *,size_t,
+                          unsigned char *, size_t *);
+  void (*dump_status_reader)(int);
+
   struct {
     ccid_driver_t handle;
   } ccid;
-  int is_ctapi;        /* This is a ctAPI driver. */
   struct {
     unsigned long context;
     unsigned long card;
@@ -97,15 +112,21 @@ struct reader_table_s {
 #endif /*NEED_PCSC_WRAPPER*/
   } pcsc;
 #ifdef HAVE_OPENSC
-  int is_osc;          /* We are using the OpenSC driver layer. */
   struct {
     struct sc_context *ctx;
     struct sc_card *scard;
   } osc;
 #endif /*HAVE_OPENSC*/
+#ifdef USE_G10CODE_RAPDU
+  struct {
+    rapdu_t handle;
+  } rapdu;
+#endif /*USE_G10CODE_RAPDU*/
   int status;
   unsigned char atr[33];
-  size_t atrlen;
+  size_t atrlen;           /* A zero length indicates that the ATR has
+                              not yet been read; i.e. the card is not
+                              ready for use. */
   unsigned int change_counter;
 #ifdef USE_GNU_PTH
   int lock_initialized;
@@ -222,12 +243,13 @@ new_reader_slot (void)
       reader_table[reader].lock_initialized = 1;
     }
 #endif /*USE_GNU_PTH*/
+  reader_table[reader].close_reader = NULL;
+  reader_table[reader].reset_reader = NULL;
+  reader_table[reader].get_status_reader = NULL;
+  reader_table[reader].send_apdu_reader = NULL;
+  reader_table[reader].dump_status_reader = NULL;
+
   reader_table[reader].used = 1;
-  reader_table[reader].is_ccid = 0;
-  reader_table[reader].is_ctapi = 0;
-#ifdef HAVE_OPENSC
-  reader_table[reader].is_osc = 0;
-#endif
 #ifdef NEED_PCSC_WRAPPER
   reader_table[reader].pcsc.req_fd = -1;
   reader_table[reader].pcsc.rsp_fd = -1;
@@ -238,34 +260,73 @@ new_reader_slot (void)
 
 
 static void
-dump_reader_status (int reader)
+dump_reader_status (int slot)
 {
-  if (reader_table[reader].is_ccid)
-    log_info ("reader slot %d: using ccid driver\n", reader);
-  else if (reader_table[reader].is_ctapi)
+  if (!opt.verbose)
+    return;
+
+  if (reader_table[slot].dump_status_reader)
+    reader_table[slot].dump_status_reader (slot);
+
+  if (reader_table[slot].status != -1
+      && reader_table[slot].atrlen)
     {
-      log_info ("reader slot %d: %s\n", reader,
-                reader_table[reader].status == 1? "Processor ICC present" :
-                reader_table[reader].status == 0? "Memory ICC present" :
-                "ICC not present" );
+      log_info ("slot %d: ATR=", slot);
+      log_printhex ("", reader_table[slot].atr, reader_table[slot].atrlen);
     }
-  else
+}
+
+
+
+static const char *
+host_sw_string (long err)
+{
+  switch (err)
     {
-      log_info ("reader slot %d: active protocol:", reader);
-      if ((reader_table[reader].pcsc.protocol & PCSC_PROTOCOL_T0))
-        log_printf (" T0");
-      else if ((reader_table[reader].pcsc.protocol & PCSC_PROTOCOL_T1))
-        log_printf (" T1");
-      else if ((reader_table[reader].pcsc.protocol & PCSC_PROTOCOL_RAW))
-        log_printf (" raw");
-      log_printf ("\n");
+    case 0: return "okay";
+    case SW_HOST_OUT_OF_CORE: return "out of core";
+    case SW_HOST_INV_VALUE: return "invalid value";
+    case SW_HOST_NO_DRIVER: return "no driver";
+    case SW_HOST_NOT_SUPPORTED: return "not supported";
+    case SW_HOST_LOCKING_FAILED: return "locking failed";
+    case SW_HOST_BUSY: return "busy";
+    case SW_HOST_NO_CARD: return "no card";
+    case SW_HOST_CARD_INACTIVE: return "card inactive";
+    case SW_HOST_CARD_IO_ERROR: return "card I/O error";
+    case SW_HOST_GENERAL_ERROR: return "general error";
+    case SW_HOST_NO_READER: return "no reader";
+    case SW_HOST_ABORTED: return "aborted";
+    default: return "unknown host status error";
     }
+}
 
-  if (reader_table[reader].status != -1)
+
+const char *
+apdu_strerror (int rc)
+{
+  switch (rc)
     {
-      log_info ("reader %d: ATR=", reader);
-      log_printhex ("", reader_table[reader].atr,
-                    reader_table[reader].atrlen);
+    case SW_EOF_REACHED    : return "eof reached";
+    case SW_EEPROM_FAILURE : return "eeprom failure";
+    case SW_WRONG_LENGTH   : return "wrong length";
+    case SW_CHV_WRONG      : return "CHV wrong";
+    case SW_CHV_BLOCKED    : return "CHV blocked";
+    case SW_USE_CONDITIONS : return "use conditions not satisfied";
+    case SW_BAD_PARAMETER  : return "bad parameter";
+    case SW_NOT_SUPPORTED  : return "not supported";
+    case SW_FILE_NOT_FOUND : return "file not found";
+    case SW_RECORD_NOT_FOUND:return "record not found";
+    case SW_REF_NOT_FOUND  : return "reference not found";
+    case SW_BAD_P0_P1      : return "bad P0 or P1";
+    case SW_INS_NOT_SUP    : return "instruction not supported";
+    case SW_CLA_NOT_SUP    : return "class not supported";
+    case SW_SUCCESS        : return "success";
+    default:
+      if ((rc & ~0x00ff) == SW_MORE_DATA)
+        return "more data available";
+      if ( (rc & 0x10000) )
+        return host_sw_string (rc);
+      return "unknown status error";
     }
 }
 
@@ -290,120 +351,82 @@ ct_error_string (long err)
     }
 }
 
-/* Wait for the card in READER and activate it.  Return -1 on error or
-   0 on success. */
-static int
-ct_activate_card (int reader)
-{
-  int rc, count;
-
-  for (count = 0; count < CARD_CONNECT_TIMEOUT; count++)
-    {
-      unsigned char dad[1], sad[1], cmd[11], buf[256];
-      unsigned short buflen;
-
-      if (count)
-        ; /* FIXME: we should use a more reliable timer than sleep. */
-
-      /* Check whether card has been inserted. */
-      dad[0] = 1;     /* Destination address: CT. */    
-      sad[0] = 2;     /* Source address: Host. */
-
-      cmd[0] = 0x20;  /* Class byte. */
-      cmd[1] = 0x13;  /* Request status. */
-      cmd[2] = 0x00;  /* From kernel. */
-      cmd[3] = 0x80;  /* Return card's DO. */
-      cmd[4] = 0x00;
-
-      buflen = DIM(buf);
-
-      rc = CT_data (reader, dad, sad, 5, cmd, &buflen, buf);
-      if (rc || buflen < 2 || buf[buflen-2] != 0x90)
-        {
-          log_error ("ct_activate_card: can't get status of reader %d: %s\n",
-                     reader, ct_error_string (rc));
-          return -1;
-        }
 
-      /* Connected, now activate the card. */           
-      dad[0] = 1;    /* Destination address: CT. */    
-      sad[0] = 2;    /* Source address: Host. */
+static void
+ct_dump_reader_status (int slot)
+{
+  log_info ("reader slot %d: %s\n", slot,
+            reader_table[slot].status == 1? "Processor ICC present" :
+            reader_table[slot].status == 0? "Memory ICC present" :
+            "ICC not present" );
+}
 
-      cmd[0] = 0x20;  /* Class byte. */
-      cmd[1] = 0x12;  /* Request ICC. */
-      cmd[2] = 0x01;  /* From first interface. */
-      cmd[3] = 0x01;  /* Return card's ATR. */
-      cmd[4] = 0x00;
 
-      buflen = DIM(buf);
+/* Wait for the card in SLOT and activate it.  Return a status word
+   error or 0 on success. */
+static int
+ct_activate_card (int slot)
+{
+  int rc;
+  unsigned char dad[1], sad[1], cmd[11], buf[256];
+  unsigned short buflen;
+  
+  /* Check whether card has been inserted. */
+  dad[0] = 1;     /* Destination address: CT. */    
+  sad[0] = 2;     /* Source address: Host. */
 
-      rc = CT_data (reader, dad, sad, 5, cmd, &buflen, buf);
-      if (rc || buflen < 2 || buf[buflen-2] != 0x90)
-        {
-          log_error ("ct_activate_card(%d): activation failed: %s\n",
-                     reader, ct_error_string (rc));
-          if (!rc)
-            log_printhex ("  received data:", buf, buflen);
-          return -1;
-        }
+  cmd[0] = 0x20;  /* Class byte. */
+  cmd[1] = 0x13;  /* Request status. */
+  cmd[2] = 0x00;  /* From kernel. */
+  cmd[3] = 0x80;  /* Return card's DO. */
+  cmd[4] = 0x00;
 
-      /* Store the type and the ATR. */
-      if (buflen - 2 > DIM (reader_table[0].atr))
-        {
-          log_error ("ct_activate_card(%d): ATR too long\n", reader);
-          return -1;
-        }
+  buflen = DIM(buf);
 
-      reader_table[reader].status = buf[buflen - 1];
-      memcpy (reader_table[reader].atr, buf, buflen - 2);
-      reader_table[reader].atrlen = buflen - 2;
-      return 0;
+  rc = CT_data (slot, dad, sad, 5, cmd, &buflen, buf);
+  if (rc || buflen < 2 || buf[buflen-2] != 0x90)
+    {
+      log_error ("ct_activate_card: can't get status of reader %d: %s\n",
+                 slot, ct_error_string (rc));
+      return SW_HOST_CARD_IO_ERROR;
     }
-  log_info ("ct_activate_card(%d): timeout waiting for card\n", reader);
-  return -1;
-}
 
+  /* Connected, now activate the card. */           
+  dad[0] = 1;    /* Destination address: CT. */    
+  sad[0] = 2;    /* Source address: Host. */
 
-/* Open a reader and return an internal handle for it.  PORT is a
-   non-negative value with the port number of the reader. USB readers
-   do have port numbers starting at 32769. */
-static int
-open_ct_reader (int port)
-{
-  int rc, reader;
+  cmd[0] = 0x20;  /* Class byte. */
+  cmd[1] = 0x12;  /* Request ICC. */
+  cmd[2] = 0x01;  /* From first interface. */
+  cmd[3] = 0x01;  /* Return card's ATR. */
+  cmd[4] = 0x00;
 
-  if (port < 0 || port > 0xffff)
-    {
-      log_error ("open_ct_reader: invalid port %d requested\n", port);
-      return -1;
-    }
-  reader = new_reader_slot ();
-  if (reader == -1)
-    return reader;
-  reader_table[reader].port = port;
+  buflen = DIM(buf);
 
-  rc = CT_init (reader, (unsigned short)port);
-  if (rc)
+  rc = CT_data (slot, dad, sad, 5, cmd, &buflen, buf);
+  if (rc || buflen < 2 || buf[buflen-2] != 0x90)
     {
-      log_error ("apdu_open_ct_reader failed on port %d: %s\n",
-                 port, ct_error_string (rc));
-      reader_table[reader].used = 0;
-      return -1;
+      log_error ("ct_activate_card(%d): activation failed: %s\n",
+                 slot, ct_error_string (rc));
+      if (!rc)
+        log_printhex ("  received data:", buf, buflen);
+      return SW_HOST_CARD_IO_ERROR;
     }
 
-  rc = ct_activate_card (reader);
-  if (rc)
+  /* Store the type and the ATR. */
+  if (buflen - 2 > DIM (reader_table[0].atr))
     {
-      reader_table[reader].used = 0;
-      return -1;
+      log_error ("ct_activate_card(%d): ATR too long\n", slot);
+      return SW_HOST_CARD_IO_ERROR;
     }
 
-  reader_table[reader].is_ctapi = 1;
-  dump_reader_status (reader);
-  return reader;
+  reader_table[slot].status = buf[buflen - 1];
+  memcpy (reader_table[slot].atr, buf, buflen - 2);
+  reader_table[slot].atrlen = buflen - 2;
+  return 0;
 }
 
+
 static int
 close_ct_reader (int slot)
 {
@@ -415,13 +438,17 @@ close_ct_reader (int slot)
 static int
 reset_ct_reader (int slot)
 {
-  return SW_HOST_NOT_SUPPORTED;
+  /* FIXME: Check is this is sufficient do do a reset. */
+  return ct_activate_card (slot);
 }
 
 
 static int
 ct_get_status (int slot, unsigned int *status)
 {
+  *status = 1|2|4;  /* FIXME */
+  return 0;
+
   return SW_HOST_NOT_SUPPORTED;
 }
 
@@ -436,6 +463,11 @@ ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
   unsigned char dad[1], sad[1];
   unsigned short ctbuflen;
   
+  /* If we don't have an ATR, we need to reset the reader first. */
+  if (!reader_table[slot].atrlen
+      && (rc = reset_ct_reader (slot)))
+    return rc;
+
   dad[0] = 0;     /* Destination address: Card. */    
   sad[0] = 2;     /* Source address: Host. */
   ctbuflen = *buflen;
@@ -444,12 +476,56 @@ ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
   rc = CT_data (slot, dad, sad, apdulen, apdu, &ctbuflen, buffer);
   *buflen = ctbuflen;
 
-  /* FIXME: map the errorcodes to GNUPG ones, so that they can be
-     shared between CTAPI and PCSC. */
-  return rc;
+  return rc? SW_HOST_CARD_IO_ERROR: 0;
 }
 
 
+
+/* Open a reader and return an internal handle for it.  PORT is a
+   non-negative value with the port number of the reader. USB readers
+   do have port numbers starting at 32769. */
+static int
+open_ct_reader (int port)
+{
+  int rc, reader;
+
+  if (port < 0 || port > 0xffff)
+    {
+      log_error ("open_ct_reader: invalid port %d requested\n", port);
+      return -1;
+    }
+  reader = new_reader_slot ();
+  if (reader == -1)
+    return reader;
+  reader_table[reader].port = port;
+
+  rc = CT_init (reader, (unsigned short)port);
+  if (rc)
+    {
+      log_error ("apdu_open_ct_reader failed on port %d: %s\n",
+                 port, ct_error_string (rc));
+      reader_table[reader].used = 0;
+      return -1;
+    }
+
+  /* Only try to activate the card. */
+  rc = ct_activate_card (reader);
+  if (rc)
+    {
+      reader_table[reader].atrlen = 0;
+      rc = 0;
+    }
+
+  reader_table[reader].close_reader = close_ct_reader;
+  reader_table[reader].reset_reader = reset_ct_reader;
+  reader_table[reader].get_status_reader = ct_get_status;
+  reader_table[reader].send_apdu_reader = ct_send_apdu;
+  reader_table[reader].dump_status_reader = ct_dump_reader_status;
+
+  dump_reader_status (reader);
+  return reader;
+}
+
 \f
 #ifdef NEED_PCSC_WRAPPER
 static int
@@ -568,76 +644,322 @@ pcsc_error_string (long err)
        PC/SC Interface
  */
 
+static void
+dump_pcsc_reader_status (int slot)
+{
+  log_info ("reader slot %d: active protocol:", slot);
+  if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T0))
+    log_printf (" T0");
+  else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
+    log_printf (" T1");
+  else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_RAW))
+    log_printf (" raw");
+  log_printf ("\n");
+}
+
+
+
 static int
-open_pcsc_reader (const char *portstr)
+pcsc_get_status (int slot, unsigned int *status)
+{
+  *status = 1|2|4;  /* FIXME!!!! */
+  return 0;
+}
+
+static int
+reset_pcsc_reader (int slot)
+{
+  return SW_HOST_NOT_SUPPORTED;
+}
+
+
+/* Actually send the APDU of length APDULEN to SLOT and return a
+   maximum of *BUFLEN data in BUFFER, the actual returned size will be
+   set to BUFLEN.  Returns: CT API error code. */
+static int
+pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+                unsigned char *buffer, size_t *buflen)
 {
 #ifdef NEED_PCSC_WRAPPER
-/* Open the PC/SC reader using the pcsc_wrapper program.  This is
-   needed to cope with different thread models and other peculiarities
-   of libpcsclite. */
-  int slot;
+  long err;
   reader_table_t slotp;
-  int fd, rp[2], wp[2];
-  int n, i;
-  pid_t pid;
-  size_t len;
+  size_t len, full_len;
+  int i, n;
   unsigned char msgbuf[9];
-  int err;
 
-  slot = new_reader_slot ();
-  if (slot == -1)
-    return -1;
+  if (!reader_table[slot].atrlen
+      && (err = reset_pcsc_reader (slot)))
+    return err;
+
+  if (DBG_CARD_IO)
+    log_printhex ("  PCSC_data:", apdu, apdulen);
+
   slotp = reader_table + slot;
 
-  /* Fire up the pcsc wrapper.  We don't use any fork/exec code from
-     the common directy but implement it direclty so that this file
-     may still be source copied. */
-  
-  if (pipe (rp) == -1)
+  if (slotp->pcsc.req_fd == -1 
+      || slotp->pcsc.rsp_fd == -1 
+      || slotp->pcsc.pid == (pid_t)(-1) )
     {
-      log_error ("error creating a pipe: %s\n", strerror (errno));
-      slotp->used = 0;
-      return -1;
+      log_error ("pcsc_send_apdu: pcsc-wrapper not running\n");
+      return SW_HOST_CARD_IO_ERROR;
     }
-  if (pipe (wp) == -1)
+
+  msgbuf[0] = 0x03; /* TRANSMIT command. */
+  len = apdulen;
+  msgbuf[1] = (len >> 24);
+  msgbuf[2] = (len >> 16);
+  msgbuf[3] = (len >>  8);
+  msgbuf[4] = (len      );
+  if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
+       || writen (slotp->pcsc.req_fd, apdu, len))
     {
-      log_error ("error creating a pipe: %s\n", strerror (errno));
-      close (rp[0]);
-      close (rp[1]);
-      slotp->used = 0;
-      return -1;
+      log_error ("error sending PC/SC TRANSMIT request: %s\n",
+                 strerror (errno));
+      goto command_failed;
     }
-      
-  pid = fork ();
-  if (pid == -1)
+
+  /* Read the response. */
+  if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
     {
-      log_error ("error forking process: %s\n", strerror (errno));
-      close (rp[0]);
-      close (rp[1]);
-      close (wp[0]);
-      close (wp[1]);
-      slotp->used = 0;
-      return -1;
+      log_error ("error receiving PC/SC TRANSMIT 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_transmit failed: %s (0x%lx)\n",
+                 pcsc_error_string (err), err);
+      return SW_HOST_CARD_IO_ERROR;
     }
-  slotp->pcsc.pid = pid;
-
-  if (!pid)
-    { /*
-         === Child ===
-       */
 
-      /* Double fork. */
-      pid = fork ();
-      if (pid == -1)
-        _exit (31); 
-      if (pid)
-        _exit (0); /* Immediate exit this parent, so that the child
-                      gets cleaned up by the init process. */
+   full_len = len;
+   
+   n = *buflen < len ? *buflen : len;
+   if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n)
+     {
+       log_error ("error receiving PC/SC TRANSMIT response: %s\n",
+                  i? strerror (errno) : "premature EOF");
+       goto command_failed;
+     }
+   *buflen = n;
 
-      /* Connect our pipes. */
-      if (wp[0] != 0 && dup2 (wp[0], 0) == -1)
-        log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
-      if (rp[1] != 1 && dup2 (rp[1], 1) == -1)
+   full_len -= len;
+   if (full_len)
+     {
+       log_error ("pcsc_send_apdu: provided buffer too short - truncated\n");
+       err = SW_HOST_INV_VALUE;
+     }
+   /* We need to read any rest of the response, to keep the
+      protocol runnng. */
+   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;
+     }
+
+   return err;
+
+ 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_io_request_s send_pci;
+  unsigned long recv_len;
+  
+  if (!reader_table[slot].atrlen
+      && (err = reset_pcsc_reader (slot)))
+    return err;
+
+  if (DBG_CARD_IO)
+    log_printhex ("  PCSC_data:", apdu, apdulen);
+
+  if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
+      send_pci.protocol = PCSC_PROTOCOL_T1;
+  else
+      send_pci.protocol = PCSC_PROTOCOL_T0;
+  send_pci.pci_len = sizeof send_pci;
+  recv_len = *buflen;
+  err = pcsc_transmit (reader_table[slot].pcsc.card,
+                       &send_pci, apdu, apdulen,
+                       NULL, buffer, &recv_len);
+  *buflen = recv_len;
+  if (err)
+    log_error ("pcsc_transmit failed: %s (0x%lx)\n",
+               pcsc_error_string (err), err);
+  
+  return err? SW_HOST_CARD_IO_ERROR:0; 
+#endif /*!NEED_PCSC_WRAPPER*/
+}
+
+
+static int
+close_pcsc_reader (int slot)
+{
+#ifdef NEED_PCSC_WRAPPER
+  long err;
+  reader_table_t slotp;
+  size_t len;
+  int i;
+  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 ("close_pcsc_reader: pcsc-wrapper not running\n");
+      return 0;
+    }
+
+  msgbuf[0] = 0x02; /* CLOSE 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 CLOSE 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 CLOSE 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_close failed: %s (0x%lx)\n",
+               pcsc_error_string (err), err);
+  
+  /* We will the wrapper in any case - errors are merely
+     informational. */
+  
+ 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 0;
+
+#else /*!NEED_PCSC_WRAPPER*/
+
+  pcsc_release_context (reader_table[slot].pcsc.context);
+  reader_table[slot].used = 0;
+  return 0;
+#endif /*!NEED_PCSC_WRAPPER*/
+}
+
+static int
+open_pcsc_reader (const char *portstr)
+{
+#ifdef NEED_PCSC_WRAPPER
+/* Open the PC/SC reader using the pcsc_wrapper program.  This is
+   needed to cope with different thread models and other peculiarities
+   of libpcsclite. */
+  int slot;
+  reader_table_t slotp;
+  int fd, rp[2], wp[2];
+  int n, i;
+  pid_t pid;
+  size_t len;
+  unsigned char msgbuf[9];
+  int err;
+
+  slot = new_reader_slot ();
+  if (slot == -1)
+    return -1;
+  slotp = reader_table + slot;
+
+  /* Fire up the pcsc wrapper.  We don't use any fork/exec code from
+     the common directy but implement it direclty so that this file
+     may still be source copied. */
+  
+  if (pipe (rp) == -1)
+    {
+      log_error ("error creating a pipe: %s\n", strerror (errno));
+      slotp->used = 0;
+      return -1;
+    }
+  if (pipe (wp) == -1)
+    {
+      log_error ("error creating a pipe: %s\n", strerror (errno));
+      close (rp[0]);
+      close (rp[1]);
+      slotp->used = 0;
+      return -1;
+    }
+      
+  pid = fork ();
+  if (pid == -1)
+    {
+      log_error ("error forking process: %s\n", strerror (errno));
+      close (rp[0]);
+      close (rp[1]);
+      close (wp[0]);
+      close (wp[1]);
+      slotp->used = 0;
+      return -1;
+    }
+  slotp->pcsc.pid = pid;
+
+  if (!pid)
+    { /*
+         === Child ===
+       */
+
+      /* Double fork. */
+      pid = fork ();
+      if (pid == -1)
+        _exit (31); 
+      if (pid)
+        _exit (0); /* Immediate exit this parent, so that the child
+                      gets cleaned up by the init process. */
+
+      /* Connect our pipes. */
+      if (wp[0] != 0 && dup2 (wp[0], 0) == -1)
+        log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
+      if (rp[1] != 1 && dup2 (rp[1], 1) == -1)
         log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
       
       /* Send stderr to the bit bucket. */
@@ -724,6 +1046,12 @@ open_pcsc_reader (const char *portstr)
     }
   slotp->atrlen = len;
 
+  reader_table[slot].close_reader = close_pcsc_reader;
+  reader_table[slot].reset_reader = reset_pcsc_reader;
+  reader_table[slot].get_status_reader = pcsc_get_status;
+  reader_table[slot].send_apdu_reader = pcsc_send_apdu;
+  reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
+
   dump_reader_status (slot); 
   return slot;
 
@@ -834,6 +1162,13 @@ open_pcsc_reader (const char *portstr)
   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;
+  reader_table[slot].get_status_reader = pcsc_get_status;
+  reader_table[slot].send_apdu_reader = pcsc_send_apdu;
+  reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
+
 /*   log_debug ("state    from pcsc_status: 0x%lx\n", card_state); */
 /*   log_debug ("protocol from pcsc_status: 0x%lx\n", card_protocol); */
 
@@ -843,245 +1178,100 @@ open_pcsc_reader (const char *portstr)
 }
 
 
-static int
-pcsc_get_status (int slot, unsigned int *status)
+
+\f
+#ifdef HAVE_LIBUSB
+/* 
+     Internal CCID driver interface.
+ */
+
+
+static void
+dump_ccid_reader_status (int slot)
 {
-  return SW_HOST_NOT_SUPPORTED;
+  log_info ("reader slot %d: using ccid driver\n", slot);
 }
 
-/* Actually send the APDU of length APDULEN to SLOT and return a
-   maximum of *BUFLEN data in BUFFER, the actual returned size will be
-   set to BUFLEN.  Returns: CT API error code. */
 static int
-pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
-                unsigned char *buffer, size_t *buflen)
+close_ccid_reader (int slot)
 {
-#ifdef NEED_PCSC_WRAPPER
-  long err;
-  reader_table_t slotp;
-  size_t len, full_len;
-  int i, n;
-  unsigned char msgbuf[9];
+  ccid_close_reader (reader_table[slot].ccid.handle);
+  reader_table[slot].used = 0;
+  return 0;
+}                       
+  
 
-  if (DBG_CARD_IO)
-    log_printhex ("  PCSC_data:", apdu, apdulen);
+static int
+reset_ccid_reader (int slot)
+{
+  int err;
+  reader_table_t slotp = reader_table + slot;
+  unsigned char atr[33];
+  size_t atrlen;
 
-  slotp = reader_table + slot;
+  err = ccid_get_atr (slotp->ccid.handle, atr, sizeof atr, &atrlen);
+  if (err)
+    return err;
+  /* If the reset was successful, update the ATR. */
+  assert (sizeof slotp->atr >= sizeof atr);
+  slotp->atrlen = atrlen;
+  memcpy (slotp->atr, atr, atrlen);
+  dump_reader_status (slot); 
+  return 0;
+}                       
+  
 
-  if (slotp->pcsc.req_fd == -1 
-      || slotp->pcsc.rsp_fd == -1 
-      || slotp->pcsc.pid == (pid_t)(-1) )
-    {
-      log_error ("pcsc_send_apdu: pcsc-wrapper not running\n");
-      return -1;
-    }
-
-  msgbuf[0] = 0x03; /* TRANSMIT command. */
-  len = apdulen;
-  msgbuf[1] = (len >> 24);
-  msgbuf[2] = (len >> 16);
-  msgbuf[3] = (len >>  8);
-  msgbuf[4] = (len      );
-  if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
-       || writen (slotp->pcsc.req_fd, apdu, len))
-    {
-      log_error ("error sending PC/SC TRANSMIT 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 TRANSMIT 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_transmit failed: %s (0x%lx)\n",
-                 pcsc_error_string (err), err);
-      return -1;
-    }
-
-   full_len = len;
-   
-   n = *buflen < len ? *buflen : len;
-   if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n)
-     {
-       log_error ("error receiving PC/SC TRANSMIT response: %s\n",
-                  i? strerror (errno) : "premature EOF");
-       goto command_failed;
-     }
-   *buflen = n;
-
-   full_len -= len;
-   if (full_len)
-     {
-       log_error ("pcsc_send_apdu: provided buffer too short - truncated\n");
-       err = -1; 
-     }
-   /* We need to read any rest of the response, to keep the
-      protocol runnng. */
-   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;
-     }
-
-   return err;
-
- 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;
+static int
+get_status_ccid (int slot, unsigned int *status)
+{
+  int rc;
+  int bits;
 
-#else /*!NEED_PCSC_WRAPPER*/
+  rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits);
+  if (rc)
+    return -1;
 
-  long err;
-  struct pcsc_io_request_s send_pci;
-  unsigned long recv_len;
-  
-  if (DBG_CARD_IO)
-    log_printhex ("  PCSC_data:", apdu, apdulen);
+  if (bits == 0)
+    *status = 1|2|4;
+  else if (bits == 1)
+    *status = 2;
+  else 
+    *status = 0;
 
-  if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
-      send_pci.protocol = PCSC_PROTOCOL_T1;
-  else
-      send_pci.protocol = PCSC_PROTOCOL_T0;
-  send_pci.pci_len = sizeof send_pci;
-  recv_len = *buflen;
-  err = pcsc_transmit (reader_table[slot].pcsc.card,
-                       &send_pci, apdu, apdulen,
-                       NULL, buffer, &recv_len);
-  *buflen = recv_len;
-  if (err)
-    log_error ("pcsc_transmit failed: %s (0x%lx)\n",
-               pcsc_error_string (err), err);
-  
-  return err? -1:0; /* FIXME: Return appropriate error code. */
-#endif /*!NEED_PCSC_WRAPPER*/
+  return 0;
 }
 
 
+/* Actually send the APDU of length APDULEN to SLOT and return a
+   maximum of *BUFLEN data in BUFFER, the actual returned size will be
+   set to BUFLEN.  Returns: Internal CCID driver error code. */
 static int
-close_pcsc_reader (int slot)
+send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
+                unsigned char *buffer, size_t *buflen)
 {
-#ifdef NEED_PCSC_WRAPPER
   long err;
-  reader_table_t slotp;
-  size_t len;
-  int i;
-  unsigned char msgbuf[9];
+  size_t maxbuflen;
 
-  slotp = reader_table + slot;
+  /* If we don't have an ATR, we need to reset the reader first. */
+  if (!reader_table[slot].atrlen
+      && (err = reset_ccid_reader (slot)))
+    return err;
 
-  if (slotp->pcsc.req_fd == -1 
-      || slotp->pcsc.rsp_fd == -1 
-      || slotp->pcsc.pid == (pid_t)(-1) )
-    {
-      log_error ("close_pcsc_reader: pcsc-wrapper not running\n");
-      return 0;
-    }
-
-  msgbuf[0] = 0x02; /* CLOSE 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 CLOSE request: %s\n",
-                 strerror (errno));
-      goto command_failed;
-    }
+  if (DBG_CARD_IO)
+    log_printhex ("  APDU_data:", apdu, apdulen);
 
-  /* Read the response. */
-  if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
-    {
-      log_error ("error receiving PC/SC CLOSE 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];
+  maxbuflen = *buflen;
+  err = ccid_transceive (reader_table[slot].ccid.handle,
+                         apdu, apdulen,
+                         buffer, maxbuflen, buflen);
   if (err)
-    log_error ("pcsc_close failed: %s (0x%lx)\n",
-               pcsc_error_string (err), err);
-  
-  /* We will the wrapper in any case - errors are merely
-     informational. */
+    log_error ("ccid_transceive failed: (0x%lx)\n",
+               err);
   
- 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 0;
-
-#else /*!NEED_PCSC_WRAPPER*/
-
-  pcsc_release_context (reader_table[slot].pcsc.context);
-  reader_table[slot].used = 0;
-  return 0;
-#endif /*!NEED_PCSC_WRAPPER*/
-}
-
-static int
-reset_pcsc_reader (int slot)
-{
-  return SW_HOST_NOT_SUPPORTED;
-}
-
-
-
-
-\f
-#ifdef HAVE_LIBUSB
-/* 
-     Internal CCID driver interface.
- */
-
-static const char *
-get_ccid_error_string (long err)
-{
-  if (!err)
-    return "okay";
-  else
-    return "unknown CCID error";
+  return err; 
 }
 
+/* Open the reader and try to read an ATR.  */
 static int
 open_ccid_reader (void)
 {
@@ -1105,102 +1295,145 @@ open_ccid_reader (void)
                       slotp->atr, sizeof slotp->atr, &slotp->atrlen);
   if (err)
     {
-      slotp->used = 0;
-      return -1;
+      slotp->atrlen = 0;
+      err = 0;
     }
 
-  slotp->is_ccid = 1;
+  reader_table[slot].close_reader = close_ccid_reader;
+  reader_table[slot].reset_reader = reset_ccid_reader;
+  reader_table[slot].get_status_reader = get_status_ccid;
+  reader_table[slot].send_apdu_reader = send_apdu_ccid;
+  reader_table[slot].dump_status_reader = dump_ccid_reader_status;
 
   dump_reader_status (slot); 
   return slot;
 }
 
+
+
+#endif /* HAVE_LIBUSB */
+
+
+\f
+#ifdef HAVE_OPENSC
+/* 
+     OpenSC Interface.
+
+     This uses the OpenSC primitives to send APDUs.  We need this
+     because we can't mix OpenSC and native (i.e. ctAPI or PC/SC)
+     access to a card for resource conflict reasons.
+ */
+
+
 static int
-close_ccid_reader (int slot)
+close_osc_reader (int slot)
 {
-  ccid_close_reader (reader_table[slot].ccid.handle);
+  /* FIXME: Implement. */
   reader_table[slot].used = 0;
   return 0;
-}                       
-  
+}
 
 static int
-reset_ccid_reader (int slot)
+reset_osc_reader (int slot)
 {
-  int err;
-  reader_table_t slotp = reader_table + slot;
-  unsigned char atr[33];
-  size_t atrlen;
+  return SW_HOST_NOT_SUPPORTED;
+}
 
-  err = ccid_get_atr (slotp->ccid.handle, atr, sizeof atr, &atrlen);
-  if (err)
-    return -1;
-  /* If the reset was successful, update the ATR. */
-  assert (sizeof slotp->atr >= sizeof atr);
-  slotp->atrlen = atrlen;
-  memcpy (slotp->atr, atr, atrlen);
-  dump_reader_status (slot); 
-  return 0;
-}                       
-  
 
 static int
-get_status_ccid (int slot, unsigned int *status)
+osc_get_status (int slot, unsigned int *status)
 {
-  int rc;
-  int bits;
-
-  rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits);
-  if (rc)
-    return -1;
-
-  if (bits == 0)
-    *status = 1|2|4;
-  else if (bits == 1)
-    *status = 2;
-  else 
-    *status = 0;
-
-  return 0;
+  return SW_HOST_NOT_SUPPORTED;
 }
 
 
 /* Actually send the APDU of length APDULEN to SLOT and return a
    maximum of *BUFLEN data in BUFFER, the actual returned size will be
-   set to BUFLEN.  Returns: Internal CCID driver error code. */
+   set to BUFLEN.  Returns: OpenSC error code. */
 static int
-send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
+osc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
                 unsigned char *buffer, size_t *buflen)
 {
   long err;
-  size_t maxbuflen;
+  struct sc_apdu a;
+  unsigned char data[SC_MAX_APDU_BUFFER_SIZE];
+  unsigned char result[SC_MAX_APDU_BUFFER_SIZE];
 
   if (DBG_CARD_IO)
     log_printhex ("  APDU_data:", apdu, apdulen);
 
-  maxbuflen = *buflen;
-  err = ccid_transceive (reader_table[slot].ccid.handle,
-                         apdu, apdulen,
-                         buffer, maxbuflen, buflen);
-  if (err)
-    log_error ("ccid_transceive failed: (0x%lx)\n",
-               err);
-  
-  return err? -1:0; /* FIXME: Return appropriate error code. */
-}
+  if (apdulen < 4)
+    {
+      log_error ("osc_send_apdu: APDU is too short\n");
+      return SW_HOST_INV_VALUE;
+    }
 
-#endif /* HAVE_LIBUSB */
+  memset(&a, 0, sizeof a);
+  a.cla = *apdu++;
+  a.ins = *apdu++;
+  a.p1 = *apdu++;
+  a.p2 = *apdu++;
+  apdulen -= 4;
 
+  if (!apdulen)
+    a.cse = SC_APDU_CASE_1;
+  else if (apdulen == 1) 
+    {
+      a.le = *apdu? *apdu : 256;
+      apdu++; apdulen--;
+      a.cse = SC_APDU_CASE_2_SHORT;
+    }
+  else
+    {
+      a.lc = *apdu++; apdulen--;
+      if (apdulen < a.lc)
+        {
+          log_error ("osc_send_apdu: APDU shorter than specified in Lc\n");
+          return SW_HOST_INV_VALUE;
 
-\f
-#ifdef HAVE_OPENSC
-/* 
-     OpenSC Interface.
+        }
+      memcpy(data, apdu, a.lc);
+      apdu += a.lc; apdulen -= a.lc;
 
-     This uses the OpenSC primitives to send APDUs.  We need this
-     because we can't mix OpenSC and native (i.e. ctAPI or PC/SC)
-     access to a card for resource conflict reasons.
- */
+      a.data = data;
+      a.datalen = a.lc;
+      
+      if (!apdulen)
+        a.cse = SC_APDU_CASE_3_SHORT;
+      else
+        {
+          a.le = *apdu? *apdu : 256;
+          apdu++; apdulen--;
+          if (apdulen)
+            {
+              log_error ("osc_send_apdu: APDU larger than specified\n");
+              return SW_HOST_INV_VALUE;
+            }
+          a.cse = SC_APDU_CASE_4_SHORT;
+        }
+    }
+
+  a.resp = result;
+  a.resplen = DIM(result);
+
+  err = sc_transmit_apdu (reader_table[slot].osc.scard, &a);
+  if (err)
+    {
+      log_error ("sc_apdu_transmit failed: %s\n", sc_strerror (err));
+      return SW_HOST_CARD_IO_ERROR;
+    }
+
+  if (*buflen < 2 || a.resplen > *buflen - 2)
+    {
+      log_error ("osc_send_apdu: provided buffer too short to store result\n");
+      return SW_HOST_INV_VALUE;
+    }
+  memcpy (buffer, a.resp, a.resplen);
+  buffer[a.resplen] = a.sw1;
+  buffer[a.resplen+1] = a.sw2;
+  *buflen = a.resplen + 2;
+  return 0;
+}
 
 static int
 open_osc_reader (int portno)
@@ -1286,32 +1519,156 @@ open_osc_reader (int portno)
   slotp->atrlen = slotp->osc.scard->atr_len;
   memcpy (slotp->atr, slotp->osc.scard->atr, slotp->atrlen);
 
-  slotp->is_osc = 1;
+  reader_table[slot].close_reader = close_osc_reader;
+  reader_table[slot].reset_reader = reset_osc_reader;
+  reader_table[slot].get_status_reader = osc_get_status;
+  reader_table[slot].send_apdu_reader = osc_send_apdu;
+  reader_table[slot].dump_status_reader = NULL;
 
   dump_reader_status (slot); 
   return slot;
 }
 
+#endif /* HAVE_OPENSC */
+
+
+\f
+#ifdef USE_G10CODE_RAPDU
+/* 
+     The Remote APDU Interface.
+
+     This uses the Remote APDU protocol to contact a reader.
+
+     The port number is actually an index into the list of ports as
+     returned via the protocol.
+ */
+
 
 static int
-close_osc_reader (int slot)
+rapdu_status_to_sw (int status)
 {
-  /* FIXME: Implement. */
+  int rc;
+
+  switch (status)
+    {
+    case RAPDU_STATUS_SUCCESS:  rc = 0; break;
+
+    case RAPDU_STATUS_INVCMD:  
+    case RAPDU_STATUS_INVPROT:  
+    case RAPDU_STATUS_INVSEQ:  
+    case RAPDU_STATUS_INVCOOKIE:
+    case RAPDU_STATUS_INVREADER:  rc = SW_HOST_INV_VALUE;  break;
+
+    case RAPDU_STATUS_TIMEOUT:  rc = SW_HOST_CARD_IO_ERROR; break;
+    case RAPDU_STATUS_CARDIO:   rc = SW_HOST_CARD_IO_ERROR; break;
+    case RAPDU_STATUS_NOCARD:   rc = SW_HOST_NO_CARD; break;
+    case RAPDU_STATUS_CARDCHG:  rc = SW_HOST_NO_CARD; break;
+    case RAPDU_STATUS_BUSY:     rc = SW_HOST_BUSY; break;
+    case RAPDU_STATUS_NEEDRESET: rc = SW_HOST_CARD_INACTIVE; break;
+
+    default: rc = SW_HOST_GENERAL_ERROR; break;
+    }
+
+  return rc;
+}
+
+
+
+static int
+close_rapdu_reader (int slot)
+{
+  rapdu_release (reader_table[slot].rapdu.handle);
   reader_table[slot].used = 0;
   return 0;
 }
 
+
 static int
-reset_osc_reader (int slot)
+reset_rapdu_reader (int slot)
 {
-  return SW_HOST_NOT_SUPPORTED;
+  int err;
+  reader_table_t slotp;
+  rapdu_msg_t msg = NULL;
+
+  slotp = reader_table + slot;
+
+  err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_RESET);
+  if (err)
+    {
+      log_error ("sending rapdu command RESET failed: %s\n",
+                err < 0 ? strerror (errno): rapdu_strerror (err));
+      rapdu_msg_release (msg);
+      return rapdu_status_to_sw (err);
+    }
+  err = rapdu_read_msg (slotp->rapdu.handle, &msg);
+  if (err)
+    {
+      log_error ("receiving rapdu message failed: %s\n",
+                err < 0 ? strerror (errno): rapdu_strerror (err));
+      rapdu_msg_release (msg);
+      return rapdu_status_to_sw (err);
+    }
+  if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
+    {
+      int sw = rapdu_status_to_sw (msg->cmd);
+      log_error ("rapdu command RESET failed: %s\n",
+                 rapdu_strerror (msg->cmd));
+      rapdu_msg_release (msg);
+      return sw;
+    }
+  if (msg->datalen >= DIM (slotp->atr))
+    {
+      log_error ("ATR returned by the RAPDU layer is too large\n");
+      rapdu_msg_release (msg);
+      return SW_HOST_INV_VALUE; 
+    }
+  slotp->atrlen = msg->datalen;
+  memcpy (slotp->atr, msg->data, msg->datalen);
+
+  rapdu_msg_release (msg);
+  return 0;
 }
 
 
 static int
-ocsc_get_status (int slot, unsigned int *status)
+my_rapdu_get_status (int slot, unsigned int *status)
 {
-  return SW_HOST_NOT_SUPPORTED;
+  int err;
+  reader_table_t slotp;
+  rapdu_msg_t msg = NULL;
+  int oldslot;
+
+  slotp = reader_table + slot;
+
+  oldslot = rapdu_set_reader (slotp->rapdu.handle, slot);
+  err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_GET_STATUS);
+  rapdu_set_reader (slotp->rapdu.handle, oldslot);
+  if (err)
+    {
+      log_error ("sending rapdu command GET_STATUS failed: %s\n",
+                err < 0 ? strerror (errno): rapdu_strerror (err));
+      return rapdu_status_to_sw (err);
+    }
+  err = rapdu_read_msg (slotp->rapdu.handle, &msg);
+  if (err)
+    {
+      log_error ("receiving rapdu message failed: %s\n",
+                err < 0 ? strerror (errno): rapdu_strerror (err));
+      rapdu_msg_release (msg);
+      return rapdu_status_to_sw (err);
+    }
+  if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
+    {
+      int sw = rapdu_status_to_sw (msg->cmd);
+      log_error ("rapdu command GET_STATUS failed: %s\n",
+                 rapdu_strerror (msg->cmd));
+      rapdu_msg_release (msg);
+      return sw;
+    }
+  *status = msg->data[0];
+
+  rapdu_msg_release (msg);
+  return 0;
 }
 
 
@@ -1319,91 +1676,151 @@ ocsc_get_status (int slot, unsigned int *status)
    maximum of *BUFLEN data in BUFFER, the actual returned size will be
    set to BUFLEN.  Returns: OpenSC error code. */
 static int
-osc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
-                unsigned char *buffer, size_t *buflen)
+my_rapdu_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+                    unsigned char *buffer, size_t *buflen)
 {
-  long err;
-  struct sc_apdu a;
-  unsigned char data[SC_MAX_APDU_BUFFER_SIZE];
-  unsigned char result[SC_MAX_APDU_BUFFER_SIZE];
+  int err;
+  reader_table_t slotp;
+  rapdu_msg_t msg = NULL;
+  size_t maxlen = *buflen;
+
+  slotp = reader_table + slot;
 
+  *buflen = 0;
   if (DBG_CARD_IO)
     log_printhex ("  APDU_data:", apdu, apdulen);
 
   if (apdulen < 4)
     {
-      log_error ("osc_send_apdu: APDU is too short\n");
-      return SC_ERROR_CMD_TOO_SHORT;
+      log_error ("rapdu_send_apdu: APDU is too short\n");
+      return SW_HOST_INV_VALUE;
     }
 
-  memset(&a, 0, sizeof a);
-  a.cla = *apdu++;
-  a.ins = *apdu++;
-  a.p1 = *apdu++;
-  a.p2 = *apdu++;
-  apdulen -= 4;
-
-  if (!apdulen)
-    a.cse = SC_APDU_CASE_1;
-  else if (apdulen == 1) 
+  err = rapdu_send_apdu (slotp->rapdu.handle, apdu, apdulen);
+  if (err)
     {
-      a.le = *apdu? *apdu : 256;
-      apdu++; apdulen--;
-      a.cse = SC_APDU_CASE_2_SHORT;
+      log_error ("sending rapdu command APDU failed: %s\n",
+                err < 0 ? strerror (errno): rapdu_strerror (err));
+      rapdu_msg_release (msg);
+      return rapdu_status_to_sw (err);
     }
-  else
+  err = rapdu_read_msg (slotp->rapdu.handle, &msg);
+  if (err)
     {
-      a.lc = *apdu++; apdulen--;
-      if (apdulen < a.lc)
-        {
-          log_error ("osc_send_apdu: APDU shorter than specified in Lc\n");
-          return SC_ERROR_CMD_TOO_SHORT;
+      log_error ("receiving rapdu message failed: %s\n",
+                err < 0 ? strerror (errno): rapdu_strerror (err));
+      rapdu_msg_release (msg);
+      return rapdu_status_to_sw (err);
+    }
+  if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
+    {
+      int sw = rapdu_status_to_sw (msg->cmd);
+      log_error ("rapdu command APDU failed: %s\n",
+                 rapdu_strerror (msg->cmd));
+      rapdu_msg_release (msg);
+      return sw;
+    }
+  
+  if (msg->datalen > maxlen)
+    {
+      log_error ("rapdu response apdu too large\n");
+      rapdu_msg_release (msg);
+      return SW_HOST_INV_VALUE; 
+    }
+
+  *buflen = msg->datalen;
+  memcpy (buffer, msg->data, msg->datalen);
+
+  rapdu_msg_release (msg);
+  return 0;
+}
+
+static int
+open_rapdu_reader (int portno,
+                   const unsigned char *cookie, size_t length,
+                   int (*readfnc) (void *opaque,
+                                   void *buffer, size_t size),
+                   void *readfnc_value,
+                   int (*writefnc) (void *opaque,
+                                    const void *buffer, size_t size),
+                   void *writefnc_value,
+                   void (*closefnc) (void *opaque),
+                   void *closefnc_value)
+{
+  int err;
+  int slot;
+  reader_table_t slotp;
+  rapdu_msg_t msg = NULL;
 
-        }
-      memcpy(data, apdu, a.lc);
-      apdu += a.lc; apdulen -= a.lc;
+  slot = new_reader_slot ();
+  if (slot == -1)
+    return -1;
+  slotp = reader_table + slot;
 
-      a.data = data;
-      a.datalen = a.lc;
-      
-      if (!apdulen)
-        a.cse = SC_APDU_CASE_3_SHORT;
-      else
-        {
-          a.le = *apdu? *apdu : 256;
-          apdu++; apdulen--;
-          if (apdulen)
-            {
-              log_error ("osc_send_apdu: APDU larger than specified\n");
-              return SC_ERROR_CMD_TOO_LONG;
-            }
-          a.cse = SC_APDU_CASE_4_SHORT;
-        }
+  slotp->rapdu.handle = rapdu_new ();
+  if (!slotp->rapdu.handle)
+    {
+      slotp->used = 0;
+      return -1;
     }
 
-  a.resp = result;
-  a.resplen = DIM(result);
 
-  err = sc_transmit_apdu (reader_table[slot].osc.scard, &a);
+  rapdu_set_iofunc (slotp->rapdu.handle,
+                    readfnc, readfnc_value,
+                    writefnc, writefnc_value,
+                    closefnc, closefnc_value);
+  rapdu_set_cookie (slotp->rapdu.handle, cookie, length);
+
+  /* First try to get the current ATR, but if the card is inactive
+     issue a reset instead.  */
+  err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_GET_ATR);
+  if (err == RAPDU_STATUS_NEEDRESET)
+    err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_RESET);
   if (err)
     {
-      log_error ("sc_apdu_transmit failed: %s\n", sc_strerror (err));
-      return err;
+      log_info ("sending rapdu command GET_ATR/RESET failed: %s\n",
+                err < 0 ? strerror (errno): rapdu_strerror (err));
+      goto failure;
     }
-
-  if (*buflen < 2 || a.resplen > *buflen - 2)
+  err = rapdu_read_msg (slotp->rapdu.handle, &msg);
+  if (err)
     {
-      log_error ("osc_send_apdu: provided buffer too short to store result\n");
-      return SC_ERROR_BUFFER_TOO_SMALL;
+      log_info ("receiving rapdu message failed: %s\n",
+                err < 0 ? strerror (errno): rapdu_strerror (err));
+      goto failure;
     }
-  memcpy (buffer, a.resp, a.resplen);
-  buffer[a.resplen] = a.sw1;
-  buffer[a.resplen+1] = a.sw2;
-  *buflen = a.resplen + 2;
-  return 0;
+  if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
+    {
+      log_info ("rapdu command GET ATR failed: %s\n",
+                 rapdu_strerror (msg->cmd));
+      goto failure;
+    }
+  if (msg->datalen >= DIM (slotp->atr))
+    {
+      log_error ("ATR returned by the RAPDU layer is too large\n");
+      goto failure;
+    }
+  slotp->atrlen = msg->datalen;
+  memcpy (slotp->atr, msg->data, msg->datalen);
+
+  reader_table[slot].close_reader = close_rapdu_reader;
+  reader_table[slot].reset_reader = reset_rapdu_reader;
+  reader_table[slot].get_status_reader = my_rapdu_get_status;
+  reader_table[slot].send_apdu_reader = my_rapdu_send_apdu;
+  reader_table[slot].dump_status_reader = NULL;
+
+  dump_reader_status (slot); 
+  rapdu_msg_release (msg);
+  return slot;
+
+ failure:      
+  rapdu_msg_release (msg);
+  rapdu_release (slotp->rapdu.handle);
+  slotp->used = 0;
+  return -1;
 }
 
-#endif /* HAVE_OPENSC */
+#endif /*USE_G10CODE_RAPDU*/
 
 
 \f
@@ -1583,23 +2000,46 @@ apdu_open_reader (const char *portstr)
 }
 
 
+/* Open an remote reader and return an internal slot number or -1 on
+   error. This function is an alternative to apdu_open_reader and used
+   with remote readers only.  Note that the supplied CLOSEFNC will
+   only be called once and the slot will not be valid afther this.
+
+   If PORTSTR is NULL we default to the first availabe port.
+*/  
+int
+apdu_open_remote_reader (const char *portstr,
+                         const unsigned char *cookie, size_t length,
+                         int (*readfnc) (void *opaque,
+                                         void *buffer, size_t size),
+                         void *readfnc_value,
+                         int (*writefnc) (void *opaque,
+                                          const void *buffer, size_t size),
+                         void *writefnc_value,
+                         void (*closefnc) (void *opaque),
+                         void *closefnc_value)
+{
+#ifdef USE_G10CODE_RAPDU
+  return open_rapdu_reader (portstr? atoi (portstr) : 0,
+                            cookie, length,
+                            readfnc, readfnc_value,
+                            writefnc, writefnc_value,
+                            closefnc, closefnc_value);
+#else
+  errno = ENOSYS;
+  return -1;
+#endif
+}
+
+
 int
 apdu_close_reader (int slot)
 {
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
-  if (reader_table[slot].is_ctapi)
-    return close_ct_reader (slot);
-#ifdef HAVE_LIBUSB
-  else if (reader_table[slot].is_ccid)
-    return close_ccid_reader (slot);
-#endif
-#ifdef HAVE_OPENSC
-  else if (reader_table[slot].is_osc)
-    return close_osc_reader (slot);
-#endif
-  else
-    return close_pcsc_reader (slot);
+  if (reader_table[slot].close_reader)
+    return reader_table[slot].close_reader (slot);
+  return SW_HOST_NOT_SUPPORTED;
 }
 
 /* Enumerate all readers and return information on whether this reader
@@ -1626,23 +2066,52 @@ apdu_reset (int slot)
   if ((sw = lock_slot (slot)))
     return sw;
 
-  if (reader_table[slot].is_ctapi)
-    sw = reset_ct_reader (slot);
-#ifdef HAVE_LIBUSB
-  else if (reader_table[slot].is_ccid)
-    sw = reset_ccid_reader (slot);
-#endif
-#ifdef HAVE_OPENSC
-  else if (reader_table[slot].is_osc)
-    sw = reset_osc_reader (slot);
-#endif
-  else
-    sw = reset_pcsc_reader (slot);
+  if (reader_table[slot].reset_reader)
+    sw = reader_table[slot].reset_reader (slot);
+
+  unlock_slot (slot);
+  return sw;
+}
+
 
+/* Activate a card if it has not yet been done.  This is a kind of
+   reset-if-required.  It is useful to test for presence of a card
+   before issuing a bunch of apdu commands.  It does not wait on a
+   locked card. */
+int
+apdu_activate (int slot)
+{
+  int sw;
+  unsigned int s;
+
+  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+    return SW_HOST_NO_DRIVER;
+  
+  if ((sw = trylock_slot (slot)))
+    return sw;
+
+  if (reader_table[slot].get_status_reader)
+    sw = reader_table[slot].get_status_reader (slot, &s);
+
+  if (!sw)
+    {
+      if (!(s & 2))  /* Card not present.  */
+        sw = SW_HOST_NO_CARD;
+      else if ( ((s & 2) && !(s & 4))
+                || !reader_table[slot].atrlen )
+        {
+          /* 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);
+        }
+    }
+  
   unlock_slot (slot);
   return sw;
 }
 
+  
 
 unsigned char *
 apdu_get_atr (int slot, size_t *atrlen)
@@ -1660,29 +2129,9 @@ apdu_get_atr (int slot, size_t *atrlen)
   return buf;
 }
 
-  
-    
-static const char *
-error_string (int slot, long rc)
-{
-  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
-    return "[invalid slot]";
-  if (reader_table[slot].is_ctapi)
-    return ct_error_string (rc);
-#ifdef HAVE_LIBUSB
-  else if (reader_table[slot].is_ccid)
-    return get_ccid_error_string (rc);
-#endif
-#ifdef HAVE_OPENSC
-  else if (reader_table[slot].is_osc)
-    return sc_strerror (rc);
-#endif
-  else
-    return pcsc_error_string (rc);
-}
-
 
-/* Retrieve the status for SLOT. The function does obnly wait fot the
+    
+/* Retrieve the status for SLOT. The function does only wait for the
    card to become available if HANG is set to true. On success the
    bits in STATUS will be set to
 
@@ -1691,7 +2140,7 @@ error_string (int slot, long rc)
      bit 2 = card active
      bit 3 = card access locked [not yet implemented]
 
-   For must application, tetsing bit 0 is sufficient.
+   For must application, testing bit 0 is sufficient.
 
    CHANGED will receive the value of the counter tracking the number
    of card insertions.  This value may be used to detect a card
@@ -1710,18 +2159,8 @@ apdu_get_status (int slot, int hang,
   if ((sw = hang? lock_slot (slot) : trylock_slot (slot)))
     return sw;
 
-  if (reader_table[slot].is_ctapi)
-    sw = ct_get_status (slot, &s);
-#ifdef HAVE_LIBUSB
-  else if (reader_table[slot].is_ccid)
-    sw = get_status_ccid (slot, &s);
-#endif
-#ifdef HAVE_OPENSC
-  else if (reader_table[slot].is_osc)
-    sw = osc_get_status (slot, &s);
-#endif
-  else
-    sw = pcsc_get_status (slot, &s);
+  if (reader_table[slot].get_status_reader)
+    sw = reader_table[slot].get_status_reader (slot, &s);
 
   unlock_slot (slot);
 
@@ -1744,18 +2183,13 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
 {
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
-  if (reader_table[slot].is_ctapi)
-    return ct_send_apdu (slot, apdu, apdulen, buffer, buflen);
-#ifdef HAVE_LIBUSB
-  else if (reader_table[slot].is_ccid)
-    return send_apdu_ccid (slot, apdu, apdulen, buffer, buflen);
-#endif
-#ifdef HAVE_OPENSC
-  else if (reader_table[slot].is_osc)
-    return osc_send_apdu (slot, apdu, apdulen, buffer, buflen);
-#endif
+
+  if (reader_table[slot].send_apdu_reader)
+    return reader_table[slot].send_apdu_reader (slot,
+                                                apdu, apdulen,
+                                                buffer, buflen);
   else
-    return pcsc_send_apdu (slot, apdu, apdulen, buffer, buflen);
+    return SW_HOST_NOT_SUPPORTED;
 }
 
 /* Send an APDU to the card in SLOT.  The APDU is created from all
@@ -1819,9 +2253,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
   if (rc || resultlen < 2)
     {
       log_error ("apdu_send_simple(%d) failed: %s\n",
-                 slot, error_string (slot, rc));
+                 slot, apdu_strerror (rc));
       unlock_slot (slot);
-      return SW_HOST_INCOMPLETE_CARD_RESPONSE;
+      return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
     }
   sw = (result[resultlen-2] << 8) | result[resultlen-1];
   /* store away the returned data but strip the statusword. */
@@ -1886,9 +2320,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
           if (rc || resultlen < 2)
             {
               log_error ("apdu_send_simple(%d) for get response failed: %s\n",
-                         slot, error_string (slot, rc));
+                         slot, apdu_strerror (rc));
               unlock_slot (slot);
-              return SW_HOST_INCOMPLETE_CARD_RESPONSE;
+              return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
             }
           sw = (result[resultlen-2] << 8) | result[resultlen-1];
           resultlen -= 2;
@@ -1977,5 +2411,180 @@ apdu_send_simple (int slot, int class, int ins, int p0, int p1,
 }
 
 
+/* This is a more generic version of the apdu sending routine.  It
+   takes an already formatted APDU in APDUDATA or length APDUDATALEN
+   and returns the with the APDU including the status word.  With
+   HANDLE_MORE set to true this function will handle the MORE DATA
+   status and return all APDUs concatenated with one status word at
+   the end.  The function does not return a regular status word but 0
+   on success.  If the slot is locked, the fucntion returns
+   immediately.*/
+int 
+apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
+                  int handle_more,
+                  unsigned char **retbuf, size_t *retbuflen)
+{
+#define RESULTLEN 256
+  unsigned char apdu[5+256+1];
+  size_t apdulen;
+  unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
+                                         the driver. */
+  size_t resultlen;
+  int sw;
+  long rc; /* we need a long here due to PC/SC. */
+  int class;
+
+  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+    return SW_HOST_NO_DRIVER;
+
+  if ((sw = trylock_slot (slot)))
+    return sw;
+
+  /* We simply trucntate a too long APDU.  */
+  if (apdudatalen > sizeof apdu)
+    apdudatalen = sizeof apdu;
+  apdulen = apdudatalen;
+  memcpy (apdu, apdudata, apdudatalen);
+  class = apdulen? *apdu : 0;
+
+
+  rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
+  if (rc || resultlen < 2)
+    {
+      log_error ("apdu_send_direct(%d) failed: %s\n",
+                 slot, apdu_strerror (rc));
+      unlock_slot (slot);
+      return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+    }
+  sw = (result[resultlen-2] << 8) | result[resultlen-1];
+  /* Store away the returned data but strip the statusword. */
+  resultlen -= 2;
+  if (DBG_CARD_IO)
+    {
+      log_debug (" response: sw=%04X  datalen=%d\n", sw, resultlen);
+      if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
+        log_printhex ("     dump: ", result, resultlen);
+    }
+
+  if (handle_more && (sw & 0xff00) == SW_MORE_DATA)
+    {
+      unsigned char *p = NULL, *tmp;
+      size_t bufsize = 4096;
+
+      /* It is likely that we need to return much more data, so we
+         start off with a large buffer. */
+      if (retbuf)
+        {
+          *retbuf = p = xtrymalloc (bufsize + 2);
+          if (!*retbuf)
+            {
+              unlock_slot (slot);
+              return SW_HOST_OUT_OF_CORE;
+            }
+          assert (resultlen < bufsize);
+          memcpy (p, result, resultlen);
+          p += resultlen;
+        }
+
+      do
+        {
+          int len = (sw & 0x00ff);
+          
+          if (DBG_CARD_IO)
+            log_debug ("apdu_send_direct(%d): %d more bytes available\n",
+                       slot, len);
+          apdulen = 0;
+          apdu[apdulen++] = class;
+          apdu[apdulen++] = 0xC0;
+          apdu[apdulen++] = 0;
+          apdu[apdulen++] = 0;
+          apdu[apdulen++] = len; 
+          memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
+          resultlen = RESULTLEN;
+          rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
+          if (rc || resultlen < 2)
+            {
+              log_error ("apdu_send_direct(%d) for get response failed: %s\n",
+                         slot, apdu_strerror (rc));
+              unlock_slot (slot);
+              return rc ? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+            }
+          sw = (result[resultlen-2] << 8) | result[resultlen-1];
+          resultlen -= 2;
+          if (DBG_CARD_IO)
+            {
+              log_debug ("     more: sw=%04X  datalen=%d\n", sw, resultlen);
+              if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA))
+                log_printhex ("     dump: ", result, resultlen);
+            }
+
+          if ((sw & 0xff00) == SW_MORE_DATA
+              || sw == SW_SUCCESS
+              || sw == SW_EOF_REACHED )
+            {
+              if (retbuf && resultlen)
+                {
+                  if (p - *retbuf + resultlen > bufsize)
+                    {
+                      bufsize += resultlen > 4096? resultlen: 4096;
+                      tmp = xtryrealloc (*retbuf, bufsize + 2);
+                      if (!tmp)
+                        {
+                          unlock_slot (slot);
+                          return SW_HOST_OUT_OF_CORE;
+                        }
+                      p = tmp + (p - *retbuf);
+                      *retbuf = tmp;
+                    }
+                  memcpy (p, result, resultlen);
+                  p += resultlen;
+                }
+            }
+          else
+            log_info ("apdu_send_sdirect(%d) "
+                      "got unexpected status %04X from get response\n",
+                      slot, sw);
+        }
+      while ((sw & 0xff00) == SW_MORE_DATA);
+      
+      if (retbuf)
+        {
+          *retbuflen = p - *retbuf;
+          tmp = xtryrealloc (*retbuf, *retbuflen + 2);
+          if (tmp)
+            *retbuf = tmp;
+        }
+    }
+  else
+    {
+      if (retbuf)
+        {
+          *retbuf = xtrymalloc ((resultlen? resultlen : 1)+2);
+          if (!*retbuf)
+            {
+              unlock_slot (slot);
+              return SW_HOST_OUT_OF_CORE;
+            }
+          *retbuflen = resultlen;
+          memcpy (*retbuf, result, resultlen);
+        }
+    }
+
+  unlock_slot (slot);
+
+  /* Append the status word - we reseved the two extra bytes while
+     allocating the buffer. */
+  if (retbuf)
+    {
+      (*retbuf)[(*retbuflen)++] = (sw >> 8);
+      (*retbuf)[(*retbuflen)++] = sw;
+    }
+
+  if (DBG_CARD_IO && retbuf)
+    log_printhex ("      dump: ", *retbuf, *retbuflen);
+  return 0;
+#undef RESULTLEN
+}
 
 
index f74bab7..a0654a2 100644 (file)
@@ -53,19 +53,39 @@ enum {
   SW_HOST_NO_DRIVER     = 0x10004,
   SW_HOST_NOT_SUPPORTED = 0x10005,
   SW_HOST_LOCKING_FAILED= 0x10006,
-  SW_HOST_BUSY          = 0x10007
+  SW_HOST_BUSY          = 0x10007,
+  SW_HOST_NO_CARD       = 0x10008,
+  SW_HOST_CARD_INACTIVE = 0x10009,
+  SW_HOST_CARD_IO_ERROR = 0x1000a,
+  SW_HOST_GENERAL_ERROR = 0x1000b,
+  SW_HOST_NO_READER     = 0x1000c,
+  SW_HOST_ABORTED       = 0x1000d
 };
 
 
 
 /* Note , that apdu_open_reader returns no status word but -1 on error. */
 int apdu_open_reader (const char *portstr);
+int apdu_open_remote_reader (const char *portstr,
+                             const unsigned char *cookie, size_t length,
+                             int (*readfnc) (void *opaque,
+                                             void *buffer, size_t size),
+                             void *readfnc_value,
+                             int (*writefnc) (void *opaque,
+                                              const void *buffer, size_t size),
+                             void *writefnc_value,
+                             void (*closefnc) (void *opaque),
+                             void *closefnc_value);
 int apdu_close_reader (int slot);
 int apdu_enum_reader (int slot, int *used);
 unsigned char *apdu_get_atr (int slot, size_t *atrlen);
 
+const char *apdu_strerror (int rc);
 
-/* The apdu send functions do return status words. */
+
+/* These apdu functions do return status words. */
+
+int apdu_activate (int slot);
 int apdu_reset (int slot);
 int apdu_get_status (int slot, int hang,
                      unsigned int *status, unsigned int *changed);
@@ -77,6 +97,10 @@ int apdu_send (int slot, int class, int ins, int p0, int p1,
 int apdu_send_le (int slot, int class, int ins, int p0, int p1,
                   int lc, const char *data, int le,
                   unsigned char **retbuf, size_t *retbuflen);
+int apdu_send_direct (int slot,
+                      const unsigned char *apdudata, size_t apdudatalen,
+                      int handle_more,
+                      unsigned char **retbuf, size_t *retbuflen);
 
 
 #endif /*APDU_H*/
index f409519..3dc015b 100644 (file)
@@ -561,7 +561,7 @@ do_getattr (APP app, CTRL ctrl, const char *name)
         {
           if (valuelen >= 60)
             for (i=0; i < 3; i++)
-              send_fpr_if_not_null (ctrl, "KEY-FPR", i+1, value+i*20);
+              send_fpr_if_not_null (ctrl, table[idx].name, i+1, value+i*20);
         }
       else
         send_status_info (ctrl, table[idx].name, value, valuelen, NULL, 0);
@@ -1619,7 +1619,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
   memcpy (*m, a, alen);
   
   a = find_tlv (keydata, keydatalen, 0x0082, &alen);
-  if (!e)
+  if (!a)
     {
       log_error ("response does not contain the RSA public exponent\n");
       rc = gpg_error (GPG_ERR_CARD);
index c93d028..2d7f008 100644 (file)
@@ -336,6 +336,11 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
                    info.chvretry[0], info.chvretry[1], info.chvretry[2]);
       fprintf (fp, "sigcount:%lu:::\n", info.sig_counter);
 
+      fputs ("cafpr:", fp);
+      print_sha1_fpr_colon (fp, info.cafpr1valid? info.cafpr1:NULL);
+      print_sha1_fpr_colon (fp, info.cafpr2valid? info.cafpr2:NULL);
+      print_sha1_fpr_colon (fp, info.cafpr3valid? info.cafpr3:NULL);
+      putc ('\n', fp);
       fputs ("fpr:", fp);
       print_sha1_fpr_colon (fp, info.fpr1valid? info.fpr1:NULL);
       print_sha1_fpr_colon (fp, info.fpr2valid? info.fpr2:NULL);
@@ -362,6 +367,21 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
                    info.disp_sex == 2? _("female") : _("unspecified"));
       print_name (fp, "URL of public key : ", info.pubkey_url);
       print_name (fp, "Login data .......: ", info.login_data);
+      if (info.cafpr1valid)
+        {
+          tty_fprintf (fp, "CA fingerprint %d .:", 1);
+          print_sha1_fpr (fp, info.cafpr1);
+        }
+      if (info.cafpr2valid)
+        {
+          tty_fprintf (fp, "CA fingerprint %d .:", 2);
+          print_sha1_fpr (fp, info.cafpr2);
+        }
+      if (info.cafpr3valid)
+        {
+          tty_fprintf (fp, "CA fingerprint %d .:", 3);
+          print_sha1_fpr (fp, info.cafpr3);
+        }
       tty_fprintf (fp,    "Signature PIN ....: %s\n",
                    info.chv1_cached? _("not forced"): _("forced"));
       tty_fprintf (fp,    "Max. PIN lengths .: %d %d %d\n",
@@ -491,19 +511,46 @@ change_url (void)
 }
 
 static int
-change_login (void)
+change_login (const char *args)
 {
   char *data;
+  int n;
   int rc;
 
-  data = cpr_get ("cardedit.change_login",
-                  _("Login data (account name): "));
-  if (!data)
-    return -1;
-  trim_spaces (data);
-  cpr_kill_prompt ();
+  if (args && *args == '<')  /* Read it from a file */
+    {
+      FILE *fp;
 
-  if (strlen (data) > 254 )
+      for (args++; spacep (args); args++)
+        ;
+      fp = fopen (args, "rb");
+      if (!fp)
+        {
+          tty_printf ("can't open `%s': %s\n", args, strerror (errno));
+          return -1;
+        }
+      data = xmalloc (254);
+      n = fread (data, 1, 254, fp);
+      fclose (fp);
+      if (n < 0)
+        {
+          tty_printf ("error reading `%s': %s\n", args, strerror (errno));
+          xfree (data);
+          return -1;
+        }
+    }
+  else
+    {
+      data = cpr_get ("cardedit.change_login",
+                      _("Login data (account name): "));
+      if (!data)
+        return -1;
+      trim_spaces (data);
+      cpr_kill_prompt ();
+      n = strlen (data);
+    }
+
+  if (n > 254 )
     {
       tty_printf (_("Error: Login data too long "
                     "(limit is %d characters).\n"), 254);    
@@ -511,7 +558,7 @@ change_login (void)
       return -1;
     }
 
-  rc = agent_scd_setattr ("LOGIN-DATA", data, strlen (data) );
+  rc = agent_scd_setattr ("LOGIN-DATA", data, n );
   if (rc)
     log_error ("error setting login data: %s\n", gpg_strerror (rc));
   xfree (data);
@@ -590,6 +637,51 @@ change_sex (void)
 }
 
 
+static int
+change_cafpr (int fprno)
+{
+  char *data;
+  const char *s;
+  int i, c, rc;
+  unsigned char fpr[20];
+
+  data = cpr_get ("cardedit.change_cafpr", _("CA fingerprint: "));
+  if (!data)
+    return -1;
+  trim_spaces (data);
+  cpr_kill_prompt ();
+
+  for (i=0, s=data; i < 20 && *s; )
+    {
+      while (spacep(s))
+        s++;
+      if (*s == ':')
+        s++;
+      while (spacep(s))
+        s++;
+      c = hextobyte (s);
+      if (c == -1)
+        break;
+      fpr[i++] = c;
+      s += 2;
+    }
+  xfree (data);
+  if (i != 20 || *s)
+    {
+      tty_printf (_("Error: invalid formatted fingerprint.\n"));
+      return -1;
+    }
+
+  rc = agent_scd_setattr (fprno==1?"CA-FPR-1":
+                          fprno==2?"CA-FPR-2":
+                          fprno==3?"CA-FPR-3":"x", fpr, 20 );
+  if (rc)
+    log_error ("error setting cafpr: %s\n", gpg_strerror (rc));
+  return rc;
+}
+
+
+
 static void
 toggle_forcesig (void)
 {
@@ -700,7 +792,7 @@ card_edit (STRLIST commands)
   enum cmdids {
     cmdNOP = 0,
     cmdQUIT, cmdHELP, cmdLIST, cmdDEBUG,
-    cmdNAME, cmdURL, cmdLOGIN, cmdLANG, cmdSEX,
+    cmdNAME, cmdURL, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
     cmdFORCESIG, cmdGENERATE, cmdPASSWD,
     cmdINVCMD
   };
@@ -722,6 +814,7 @@ card_edit (STRLIST commands)
     { N_("login") , cmdLOGIN , N_("change the login name") },
     { N_("lang")  , cmdLANG  , N_("change the language preferences") },
     { N_("sex")   , cmdSEX   , N_("change card holder's sex") },
+    { N_("cafpr"),  cmdCAFPR,  N_("change a CA fingerprint") },
     { N_("forcesig"),
                   cmdFORCESIG, N_("toggle the signature force PIN flag") },
     { N_("generate"),
@@ -840,7 +933,7 @@ card_edit (STRLIST commands)
           break;
 
         case cmdLOGIN:
-          change_login ();
+          change_login (arg_string);
           break;
 
         case cmdLANG:
@@ -851,6 +944,14 @@ card_edit (STRLIST commands)
           change_sex ();
           break;
 
+        case cmdCAFPR:
+          if ( arg_number < 1 || arg_number > 3 )
+            tty_printf ("usage: cafpr N\n"
+                        "       1 <= N <= 3\n");
+          else
+            change_cafpr (arg_number);
+          break;
+
         case cmdFORCESIG:
           toggle_forcesig ();
           break;
index a192462..7bb3c84 100644 (file)
@@ -1,5 +1,5 @@
 /* cardglue.c - mainly dispatcher for card related functions.
- * Copyright (C) 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -237,6 +237,7 @@ agent_release_card_info (struct agent_card_info_s *info)
   xfree (info->pubkey_url); info->pubkey_url = NULL;
   xfree (info->login_data); info->login_data = NULL;
   info->fpr1valid = info->fpr2valid = info->fpr3valid = 0;
+  info->cafpr1valid = info->cafpr2valid = info->cafpr3valid = 0;
 }
 
 
@@ -529,7 +530,21 @@ learn_status_cb (void *opaque, const char *line)
       else if (no == 3)
         parm->fpr3valid = unhexify_fpr (line, parm->fpr3);
     }
-  
+  else if (keywordlen == 6 && !memcmp (keyword, "CA-FPR", keywordlen))
+    {
+      int no = atoi (line);
+      while (*line && !spacep (line))
+        line++;
+      while (spacep (line))
+        line++;
+      if (no == 1)
+        parm->cafpr1valid = unhexify_fpr (line, parm->cafpr1);
+      else if (no == 2)
+        parm->cafpr2valid = unhexify_fpr (line, parm->cafpr2);
+      else if (no == 3)
+        parm->cafpr3valid = unhexify_fpr (line, parm->cafpr3);
+    }
+
   return 0;
 }
 
index f6f64bc..764064e 100644 (file)
@@ -1,5 +1,5 @@
-/* call-agent.h - Divert operations to the agent
- * Copyright (C) 2003 Free Software Foundation, Inc.
+/* cardglue.h - Divert operations to the agent
+ * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -37,6 +37,12 @@ struct agent_card_info_s {
   int  disp_sex;     /* 0 = unspecified, 1 = male, 2 = female */
   char *pubkey_url;  /* malloced. */
   char *login_data;  /* malloced. */
+  char cafpr1valid;
+  char cafpr2valid;
+  char cafpr3valid;
+  char cafpr1[20];
+  char cafpr2[20];
+  char cafpr3[20];
   char fpr1valid;
   char fpr2valid;
   char fpr3valid;
@@ -93,10 +99,15 @@ typedef struct ctrl_ctx_s *ctrl_t;
 #define GPG_ERR_NOT_IMPLEMENTED   G10ERR_GENERAL
 #define GPG_ERR_BAD_BER           G10ERR_GENERAL
 #define GPG_ERR_EOF               (-1)
+#define GPG_ERR_CARD_NOT_PRESENT  G10ERR_NO_CARD
+#define GPG_ERR_CARD_RESET        G10ERR_GENERAL
 
 #define GPG_ERR_EBUSY             G10ERR_GENERAL
 #define GPG_ERR_ENOENT            G10ERR_OPEN_FILE
 #define GPG_ERR_EACCES            G10ERR_UNSUPPORTED
+#define GPG_ERR_EIO               G10ERR_GENERAL
+#define GPG_ERR_ENODEV            G10ERR_GENERAL
+#define GPG_ERR_CANCELED          G10ERR_CANCELED
 
 typedef int gpg_error_t;
 typedef int gpg_err_code_t;
index 1316938..0fc1685 100644 (file)
@@ -412,7 +412,13 @@ read_device_info (ccid_driver_t handle, struct usb_device *dev)
 {
   int cfg_no;
 
-  for (cfg_no=0; cfg_no < dev->descriptor->bNumConfigurations; cfg_no++)
+  for (cfg_no=0; cfg_no <
+#ifdef HAVE_USB_CREATE_MATCH
+         dev->descriptor->bNumConfigurations
+#else
+         dev->descriptor.bNumConfigurations
+#endif
+         ; cfg_no++)
     {
       struct usb_config_descriptor *config = dev->config + cfg_no;
       int ifc_no;
@@ -442,17 +448,18 @@ read_device_info (ccid_driver_t handle, struct usb_device *dev)
             }
         }
     }
-  return -1; /* No suitable device found. */
+  return CCID_DRIVER_ERR_NO_READER; /* No suitable device found. */
 }
 
 
-/* Open the reader with the internal number READERNO and return a a
+/* Open the reader with the internal number READERNO and return a 
    pointer to be used as handle in HANDLE.  Returns 0 on success. */
 int 
 ccid_open_reader (ccid_driver_t *handle, int readerno)
 {
+#ifdef HAVE_USB_CREATE_MATCH
+  /* This is the development version of libusb. */  
   static int initialized;
-
   int rc;
   usb_match_handle *match = NULL;
   struct usb_device *dev = NULL;
@@ -469,9 +476,9 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
   if (rc)
     {
       DEBUGOUT_1 ("usb_create_match failed: %d\n", rc);
-      return -1;
+      return CCID_DRIVER_ERR_NO_READER;
     }
-
+  
   while (usb_find_device(match, dev, &dev) >= 0) 
     {
       DEBUGOUT_3 ("%-40s %04X/%04X\n", dev->filename,
@@ -482,7 +489,7 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
           if (!*handle)
             {
               DEBUGOUT ("out of memory\n");
-              rc = -1;
+              rc = CCID_DRIVER_ERR_OUT_OF_CORE;
               free (*handle);
               *handle = NULL;
               goto leave;
@@ -503,6 +510,7 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
               DEBUGOUT_1 ("usb_open failed: %d\n", rc);
               free (*handle);
               *handle = NULL;
+              rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
               goto leave;
             }
 
@@ -513,16 +521,9 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
           if (rc)
             {
               DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
-#if defined(GNUPG_MAJOR_VERSION) \
-    || (defined(GNUPG_SCD_MAIN_HEADER) && defined(HAVE_JNLIB_LOGGING))
-              log_info ("CCID can't be used - "
-                        "please check that the device file "
-                        "(e.g. /proc/bus/usb) "
-                        "has appropriate permissions\n" );
-#endif
-
               free (*handle);
               *handle = NULL;
+              rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
               goto leave;
             }
 
@@ -536,7 +537,6 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
       readerno--;
     }
 
-
  leave:
   if (idev)
     usb_close (idev);
@@ -548,6 +548,96 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
     rc = -1; /* In case we didn't enter the while loop at all. */
 
   return rc;
+#else /* Stable 0.1 version of libusb.  */
+  static int initialized;
+  int rc = 0;
+  struct usb_bus *busses, *bus;
+  struct usb_device *dev = NULL;
+  usb_dev_handle *idev = NULL;
+
+  *handle = NULL;
+  if (!initialized)
+    {
+      usb_init ();
+      initialized = 1;
+    }
+  
+  usb_find_busses();
+  usb_find_devices();
+  busses = usb_get_busses();
+
+  for (bus = busses; bus; bus = bus->next) 
+    {
+      for (dev = bus->devices; dev; dev = dev->next)
+        {
+          DEBUGOUT_3 ("%-40s %04X/%04X\n", dev->filename,
+                      dev->descriptor.idVendor, dev->descriptor.idProduct);
+
+          if (!readerno)
+            {
+              *handle = calloc (1, sizeof **handle);
+              if (!*handle)
+                {
+                  DEBUGOUT ("out of memory\n");
+                  rc = CCID_DRIVER_ERR_OUT_OF_CORE;
+                  free (*handle);
+                  *handle = NULL;
+                  goto leave;
+                }
+
+              rc = read_device_info (*handle, dev);
+              if (rc)
+                {
+                  DEBUGOUT ("device not supported\n");
+                  free (*handle);
+                  *handle = NULL;
+                  continue;
+                }
+
+              idev = usb_open (dev);
+              if (!idev)
+                {
+                  DEBUGOUT_1 ("usb_open failed: %s\n", strerror (errno));
+                  free (*handle);
+                  *handle = NULL;
+                  rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
+                  goto leave;
+                }
+
+              /* fixme: Do we need to claim and set the interface as
+                 determined by read_device_info ()? */
+              rc = usb_claim_interface (idev, 0);
+              if (rc)
+                {
+                  DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
+                  free (*handle);
+                  *handle = NULL;
+                  rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
+                  goto leave;
+                }
+
+              (*handle)->idev = idev;
+              idev = NULL;
+              /* FIXME: Do we need to get the endpoint addresses from the
+                 structure and store them with the handle? */
+              
+              goto leave; /* ready. */
+            }
+          readerno--;
+        }
+    }
+
+ leave:
+  if (idev)
+    usb_close (idev);
+  /* fixme: Do we need to release dev or is it supposed to be a
+     shallow copy of the list created internally by usb_init ? */
+
+  if (!rc && !*handle)
+    rc = -1; /* In case we didn't enter the while loop at all. */
+
+  return rc;
+#endif /* Stable version 0.1 of libusb.  */
 }
 
 
@@ -613,7 +703,7 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen)
     DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno));
   else
     DEBUGOUT_1 ("usb_bulk_write failed: %d\n", rc);
-  return -1;
+  return CCID_DRIVER_ERR_CARD_IO_ERROR;
 }
 
 
@@ -639,7 +729,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
   if (rc < 0)
     {
       DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (errno));
-      return -1;
+      return CCID_DRIVER_ERR_CARD_IO_ERROR;
     }
 
   *nread = msglen = rc;
@@ -647,23 +737,23 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
   if (msglen < 10)
     {
       DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen);
-      return -1;
+      return CCID_DRIVER_ERR_INV_VALUE;
     }
   if (buffer[0] != expected_type)
     {
       DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]);
-      return -1;
+      return CCID_DRIVER_ERR_INV_VALUE;
     }
   if (buffer[5] != 0)    
     {
       DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]);
-      return -1;
+      return CCID_DRIVER_ERR_INV_VALUE;
     }
   if (buffer[6] != seqno)    
     {
       DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n",
                   seqno, buffer[6]);
-      return -1;
+      return CCID_DRIVER_ERR_INV_VALUE;
     }
 
   if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80)
@@ -680,6 +770,13 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
     DEBUGOUT_CONT_1 (" %02X", buffer[i]);
   DEBUGOUT_LF ();
 
+  switch ((buffer[7] & 0x03))
+    {
+    case 0: /* no error */ break;
+    case 1: return CCID_DRIVER_ERR_CARD_INACTIVE;
+    case 2: return CCID_DRIVER_ERR_NO_CARD;
+    case 3: /* RFU */ break;
+    }
   return 0;
 }
 
@@ -703,7 +800,7 @@ ccid_poll (ccid_driver_t handle)
   if (rc < 0)
     {
       DEBUGOUT_1 ("usb_intr_read error: %s\n", strerror (errno));
-      return -1;
+      return CCID_DRIVER_ERR_CARD_IO_ERROR;
     }
 
   msglen = rc;
@@ -712,7 +809,7 @@ ccid_poll (ccid_driver_t handle)
   if (msglen < 1)
     {
       DEBUGOUT ("intr-in msg too short\n");
-      return -1;
+      return CCID_DRIVER_ERR_INV_VALUE;
     }
 
   if (msg[0] == RDR_to_PC_NotifySlotChange)
@@ -739,7 +836,8 @@ ccid_poll (ccid_driver_t handle)
 }
 
 
-
+/* Note that this fucntion won't return the error codes NO_CARD or
+   CARD_INACTIVE */
 int 
 ccid_slot_status (ccid_driver_t handle, int *statusbits)
 {
@@ -760,7 +858,8 @@ ccid_slot_status (ccid_driver_t handle, int *statusbits)
   if (rc)
     return rc;
   rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno);
-  if (rc)
+  if (rc && rc != CCID_DRIVER_ERR_NO_CARD
+      && rc != CCID_DRIVER_ERR_CARD_INACTIVE)
     return rc;
   *statusbits = (msg[7] & 3);
 
@@ -891,10 +990,7 @@ ccid_get_atr (ccid_driver_t handle,
       tpdulen = msglen - 10;
       
       if (tpdulen < 4) 
-        {
-          DEBUGOUT ("cannot yet handle short blocks!\n");
-          return -1; 
-        }
+        return CCID_DRIVER_ERR_ABORTED; 
 
 #ifdef DEBUG_T1
       fprintf (stderr, "T1: got %c-block seq=%d err=%d\n",
@@ -1026,7 +1122,7 @@ ccid_transceive (ccid_driver_t handle,
 
           /* Construct an I-Block. */
           if (apdulen > 254)
-            return -1; /* Invalid length. */
+            return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
 
           tpdu = msg+10;
           /* NAD: DAD=1, SAD=0 */
@@ -1089,10 +1185,9 @@ ccid_transceive (ccid_driver_t handle,
       
       if (tpdulen < 4) 
         {
-          DEBUGOUT ("cannot yet handle short blocks!\n");
-          return -1
+          usb_clear_halt (handle->idev, 0x82);
+          return CCID_DRIVER_ERR_ABORTED
         }
-
 #ifdef DEBUG_T1
       fprintf (stderr, "T1: got %c-block seq=%d err=%d\n",
                ((msg[11] & 0xc0) == 0x80)? 'R' :
@@ -1140,7 +1235,7 @@ ccid_transceive (ccid_driver_t handle,
                   DEBUGOUT_2 ("provided buffer too short for received data "
                               "(%u/%u)\n",
                               (unsigned int)n, (unsigned int)maxresplen);
-                  return -1;
+                  return CCID_DRIVER_ERR_INV_VALUE;
                 }
               
               memcpy (resp, p, n); 
@@ -1171,7 +1266,7 @@ ccid_transceive (ccid_driver_t handle,
               if (++retries > 3)
                 {
                   DEBUGOUT ("3 failed retries\n");
-                  return -1;
+                  return CCID_DRIVER_ERR_CARD_IO_ERROR;
                 }
               msg = send_buffer;
               tpdulen = last_tpdulen;
@@ -1179,7 +1274,7 @@ ccid_transceive (ccid_driver_t handle,
           else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns)
             { /* Reponse does not match our sequence number. */
               DEBUGOUT ("R-block with wrong seqno received on more bit\n");
-              return -1;
+              return CCID_DRIVER_ERR_CARD_IO_ERROR;
             }
           else if (sending)
             { /* Send next chunk. */
@@ -1191,7 +1286,7 @@ ccid_transceive (ccid_driver_t handle,
           else
             {
               DEBUGOUT ("unexpected ACK R-block received\n");
-              return -1;
+              return CCID_DRIVER_ERR_CARD_IO_ERROR;
             }
         }
       else 
@@ -1218,7 +1313,7 @@ ccid_transceive (ccid_driver_t handle,
               DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi);
             }
           else
-            return -1;
+            return CCID_DRIVER_ERR_CARD_IO_ERROR;
         }
     } /* end T=1 protocol loop. */
 
index 8b86eb1..0cb52e1 100644 (file)
 #ifndef CCID_DRIVER_H
 #define CCID_DRIVER_H
 
+/* The CID driver returns the same error codes as the statsu words
+   used by GnuPG's apdu.h.  For ease of maintenance they should always
+   match.  */
+#define CCID_DRIVER_ERR_OUT_OF_CORE    0x10001 
+#define CCID_DRIVER_ERR_INV_VALUE      0x10002
+#define CCID_DRIVER_ERR_INCOMPLETE_CARD_RESPONSE = 0x10003
+#define CCID_DRIVER_ERR_NO_DRIVER      0x10004
+#define CCID_DRIVER_ERR_NOT_SUPPORTED  0x10005
+#define CCID_DRIVER_ERR_LOCKING_FAILED 0x10006
+#define CCID_DRIVER_ERR_BUSY           0x10007
+#define CCID_DRIVER_ERR_NO_CARD        0x10008
+#define CCID_DRIVER_ERR_CARD_INACTIVE  0x10009
+#define CCID_DRIVER_ERR_CARD_IO_ERROR  0x1000a
+#define CCID_DRIVER_ERR_GENERAL_ERROR  0x1000b
+#define CCID_DRIVER_ERR_NO_READER      0x1000c
+#define CCID_DRIVER_ERR_ABORTED        0x1000d
 
 struct ccid_driver_s;
 typedef struct ccid_driver_s *ccid_driver_t;
index fd3f048..cbb314e 100644 (file)
@@ -85,6 +85,13 @@ map_sw (int sw)
     case SW_HOST_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break;
     case SW_HOST_LOCKING_FAILED: ec = GPG_ERR_BUG; break;
     case SW_HOST_BUSY:           ec = GPG_ERR_EBUSY; break;
+    case SW_HOST_NO_CARD:        ec = GPG_ERR_CARD_NOT_PRESENT; break;
+    case SW_HOST_CARD_INACTIVE:  ec = GPG_ERR_CARD_RESET; break;
+    case SW_HOST_CARD_IO_ERROR:  ec = GPG_ERR_EIO; break;
+    case SW_HOST_GENERAL_ERROR:  ec = GPG_ERR_GENERAL; break;
+    case SW_HOST_NO_READER:      ec = GPG_ERR_ENODEV; break;
+    case SW_HOST_ABORTED:        ec = GPG_ERR_CANCELED; break;
+
     default:
       if ((sw & 0x010000))
         ec = GPG_ERR_GENERAL; /* Should not happen. */
index 19122ed..29e3cba 100644 (file)
@@ -1,3 +1,7 @@
+2004-09-09  Werner Koch  <wk@g10code.com>
+
+       * errors.h (G10ERR_NO_CARD, G10ERR_CANCELED): New error codes.
+
 2004-04-27  Werner Koch  <wk@gnupg.org>
 
        * mpi.h: Renamed prototype parameter name to avoid gcc warnings.
index 0dde0f9..38664e1 100644 (file)
@@ -75,7 +75,8 @@
 #define G10ERR_UNU_PUBKEY     53
 #define G10ERR_UNU_SECKEY     54
 #define G10ERR_KEYSERVER      55
-
+#define G10ERR_CANCELED       56
+#define G10ERR_NO_CARD        57
 
 #ifndef HAVE_STRERROR
 char *strerror( int n );
index 9b84cd4..0daf7f9 100644 (file)
Binary files a/po/en@boldquot.gmo and b/po/en@boldquot.gmo differ
index 8dec4e7..93099a2 100644 (file)
Binary files a/po/en@quot.gmo and b/po/en@quot.gmo differ
index fd87232..bc0433d 100644 (file)
@@ -1,5 +1,8 @@
 2004-09-09  Werner Koch  <wk@g10code.com>
 
+       * errors.c (g10_errstr): New error codes G10ERR_NO_CARD,
+       G10ERR_CANCELED.
+
        * ttyio.c (tty_get): Add readline support.
 
        * iobuf.c (iobuf_skip_rest): New.  Orginal patch by Florian
index 10956a4..feb84fc 100644 (file)
@@ -105,6 +105,8 @@ g10_errstr( int err )
       X(UNU_PUBKEY     ,N_("unusable public key"))
       X(UNU_SECKEY     ,N_("unusable secret key"))
       X(KEYSERVER      ,N_("keyserver error"))
+      X(CANCELED       ,N_("canceled"))
+      X(NO_CARD        ,N_("no card"))
       default: p = buf; sprintf(buf, "g10err=%d", err); break;
     }
 #undef X