1 /*
   2  * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.smartcardio;
  27 
  28 import java.util.*;
  29 
  30 import java.security.*;
  31 
  32 import sun.security.jca.*;
  33 import sun.security.jca.GetInstance.*;
  34 
  35 /**
  36  * A factory for CardTerminal objects.
  37  *
  38  * It allows an application to
  39  * <ul>
  40  * <li>obtain a TerminalFactory by calling
  41  * one of the static factory methods in this class
  42  * ({@linkplain #getDefault} or {@linkplain #getInstance getInstance()}).
  43  * <li>use this TerminalFactory object to access the CardTerminals by
  44  * calling the {@linkplain #terminals} method.
  45  * </ul>
  46  *
  47  * <p>Each TerminalFactory has a <code>type</code> indicating how it
  48  * was implemented. It must be specified when the implementation is obtained
  49  * using a {@linkplain #getInstance getInstance()} method and can be retrieved
  50  * via the {@linkplain #getType} method.
  51  *
  52  * <P>The following standard type names have been defined:
  53  * <dl>
  54  * <dt><code>PC/SC</code>
  55  * <dd>an implementation that calls into the PC/SC Smart Card stack
  56  * of the host platform.
  57  * Implementations do not require parameters and accept "null" as argument
  58  * in the getInstance() calls.
  59  * <dt><code>None</code>
  60  * <dd>an implementation that does not supply any CardTerminals. On platforms
  61  * that do not support other implementations,
  62  * {@linkplain #getDefaultType} returns <code>None</code> and
  63  * {@linkplain #getDefault} returns an instance of a <code>None</code>
  64  * TerminalFactory. Factories of this type cannot be obtained by calling the
  65  * <code>getInstance()</code> methods.
  66  * </dl>
  67  * Additional standard types may be defined in the future.
  68  *
  69  * <p><strong>Note:</strong>
  70  * Provider implementations that accept initialization parameters via the
  71  * <code>getInstance()</code> methods are strongly
  72  * encouraged to use a {@linkplain java.util.Properties} object as the
  73  * representation for String name-value pair based parameters whenever
  74  * possible. This allows applications to more easily interoperate with
  75  * multiple providers than if each provider used different provider
  76  * specific class as parameters.
  77  *
  78  * <P>TerminalFactory utilizes an extensible service provider framework.
  79  * Service providers that wish to add a new implementation should see the
  80  * {@linkplain TerminalFactorySpi} class for more information.
  81  *
  82  * @see CardTerminals
  83  * @see Provider
  84  *
  85  * @since   1.6
  86  * @author  Andreas Sterbenz
  87  * @author  JSR 268 Expert Group
  88  */
  89 public final class TerminalFactory {
  90 
  91     private final static String PROP_NAME =
  92                         "javax.smartcardio.TerminalFactory.DefaultType";
  93 
  94     private final static String defaultType;
  95 
  96     private final static TerminalFactory defaultFactory;
  97 
  98     static {
  99         // lookup up the user specified type, default to PC/SC
 100         String type = AccessController.doPrivileged(
 101              (PrivilegedAction<String>) () -> System.getProperty(PROP_NAME, "PC/SC")).trim();
 102         TerminalFactory factory = null;
 103         try {
 104             factory = TerminalFactory.getInstance(type, null);
 105         } catch (Exception e) {
 106             // ignore
 107         }
 108         if (factory == null) {
 109             // if that did not work, try the Sun PC/SC factory
 110             try {
 111                 type = "PC/SC";
 112                 Provider sun = Security.getProvider("SunPCSC");
 113                 if (sun == null) {
 114                     @SuppressWarnings("deprecation")
 115                     Object o = Class.forName("sun.security.smartcardio.SunPCSC").newInstance();
 116                     sun = (Provider)o;
 117                 }
 118                 factory = TerminalFactory.getInstance(type, null, sun);
 119             } catch (Exception e) {
 120                 // ignore
 121             }
 122         }
 123         if (factory == null) {
 124             type = "None";
 125             factory = new TerminalFactory
 126                         (NoneFactorySpi.INSTANCE, NoneProvider.INSTANCE, "None");
 127         }
 128         defaultType = type;
 129         defaultFactory = factory;
 130     }
 131 
 132     private static final class NoneProvider extends Provider {
 133 
 134         private static final long serialVersionUID = 2745808869881593918L;
 135         final static Provider INSTANCE = new NoneProvider();
 136         private NoneProvider() {
 137             super("None", 1.0d, "none");
 138         }
 139     }
 140 
 141     private static final class NoneFactorySpi extends TerminalFactorySpi {
 142         final static TerminalFactorySpi INSTANCE = new NoneFactorySpi();
 143         private NoneFactorySpi() {
 144             // empty
 145         }
 146         protected CardTerminals engineTerminals() {
 147             return NoneCardTerminals.INSTANCE;
 148         }
 149     }
 150 
 151     private static final class NoneCardTerminals extends CardTerminals {
 152         final static CardTerminals INSTANCE = new NoneCardTerminals();
 153         private NoneCardTerminals() {
 154             // empty
 155         }
 156         public List<CardTerminal> list(State state) throws CardException {
 157             if (state == null) {
 158                 throw new NullPointerException();
 159             }
 160             return Collections.emptyList();
 161         }
 162         public boolean waitForChange(long timeout) throws CardException {
 163             throw new IllegalStateException("no terminals");
 164         }
 165     }
 166 
 167     private final TerminalFactorySpi spi;
 168 
 169     private final Provider provider;
 170 
 171     private final String type;
 172 
 173     private TerminalFactory(TerminalFactorySpi spi, Provider provider, String type) {
 174         this.spi = spi;
 175         this.provider = provider;
 176         this.type = type;
 177     }
 178 
 179     /**
 180      * Get the default TerminalFactory type.
 181      *
 182      * <p>It is determined as follows:
 183      *
 184      * when this class is initialized, the system property
 185      * <code>javax.smartcardio.TerminalFactory.DefaultType</code>
 186      * is examined. If it is set, a TerminalFactory of this type is
 187      * instantiated by calling the {@linkplain #getInstance
 188      * getInstance(String,Object)} method passing
 189      * <code>null</code> as the value for <code>params</code>. If the call
 190      * succeeds, the type becomes the default type and the factory becomes
 191      * the {@linkplain #getDefault default} factory.
 192      *
 193      * <p>If the system property is not set or the getInstance() call fails
 194      * for any reason, the system defaults to an implementation specific
 195      * default type and TerminalFactory.
 196      *
 197      * @return the default TerminalFactory type
 198      */
 199     public static String getDefaultType() {
 200         return defaultType;
 201     }
 202 
 203     /**
 204      * Returns the default TerminalFactory instance. See
 205      * {@linkplain #getDefaultType} for more information.
 206      *
 207      * <p>A default TerminalFactory is always available. However, depending
 208      * on the implementation, it may not offer any terminals.
 209      *
 210      * @return the default TerminalFactory
 211      */
 212     public static TerminalFactory getDefault() {
 213         return defaultFactory;
 214     }
 215 
 216     /**
 217      * Returns a TerminalFactory of the specified type that is initialized
 218      * with the specified parameters.
 219      *
 220      * <p> This method traverses the list of registered security Providers,
 221      * starting with the most preferred Provider.
 222      * A new TerminalFactory object encapsulating the
 223      * TerminalFactorySpi implementation from the first
 224      * Provider that supports the specified type is returned.
 225      *
 226      * <p> Note that the list of registered providers may be retrieved via
 227      * the {@linkplain Security#getProviders() Security.getProviders()} method.
 228      *
 229      * <p>The <code>TerminalFactory</code> is initialized with the
 230      * specified parameters Object. The type of parameters
 231      * needed may vary between different types of <code>TerminalFactory</code>s.
 232      *
 233      * @implNote
 234      * The JDK Reference Implementation additionally uses the
 235      * {@code jdk.security.provider.preferred}
 236      * {@link Security#getProperty(String) Security} property to determine
 237      * the preferred provider order for the specified algorithm. This
 238      * may be different than the order of providers returned by
 239      * {@link Security#getProviders() Security.getProviders()}.
 240      *
 241      * @param type the type of the requested TerminalFactory
 242      * @param params the parameters to pass to the TerminalFactorySpi
 243      *   implementation, or null if no parameters are needed
 244      * @return a TerminalFactory of the specified type
 245      *
 246      * @throws NullPointerException if type is null
 247      * @throws NoSuchAlgorithmException if no Provider supports a
 248      *   TerminalFactorySpi of the specified type
 249      */
 250     public static TerminalFactory getInstance(String type, Object params)
 251             throws NoSuchAlgorithmException {
 252         Instance instance = GetInstance.getInstance("TerminalFactory",
 253             TerminalFactorySpi.class, type, params);
 254         return new TerminalFactory((TerminalFactorySpi)instance.impl,
 255             instance.provider, type);
 256     }
 257 
 258     /**
 259      * Returns a TerminalFactory of the specified type that is initialized
 260      * with the specified parameters.
 261      *
 262      * <p> A new TerminalFactory object encapsulating the
 263      * TerminalFactorySpi implementation from the specified provider
 264      * is returned.  The specified provider must be registered
 265      * in the security provider list.
 266      *
 267      * <p> Note that the list of registered providers may be retrieved via
 268      * the {@linkplain Security#getProviders() Security.getProviders()} method.
 269      *
 270      * <p>The <code>TerminalFactory</code> is initialized with the
 271      * specified parameters Object. The type of parameters
 272      * needed may vary between different types of <code>TerminalFactory</code>s.
 273      *
 274      * @param type the type of the requested TerminalFactory
 275      * @param params the parameters to pass to the TerminalFactorySpi
 276      *   implementation, or null if no parameters are needed
 277      * @param provider the name of the provider
 278      * @return a TerminalFactory of the specified type
 279      *
 280      * @throws NullPointerException if type is null
 281      * @throws IllegalArgumentException if provider is null or the empty String
 282      * @throws NoSuchAlgorithmException if a TerminalFactorySpi implementation
 283      *   of the specified type is not available from the specified provider
 284      * @throws NoSuchAlgorithmException if no TerminalFactory of the
 285      *   specified type could be found
 286      * @throws NoSuchProviderException if the specified provider could not
 287      *   be found
 288      */
 289     public static TerminalFactory getInstance(String type, Object params,
 290             String provider) throws NoSuchAlgorithmException, NoSuchProviderException {
 291         Instance instance = GetInstance.getInstance("TerminalFactory",
 292             TerminalFactorySpi.class, type, params, provider);
 293         return new TerminalFactory((TerminalFactorySpi)instance.impl,
 294             instance.provider, type);
 295     }
 296 
 297     /**
 298      * Returns a TerminalFactory of the specified type that is initialized
 299      * with the specified parameters.
 300      *
 301      * <p> A new TerminalFactory object encapsulating the
 302      * TerminalFactorySpi implementation from the specified provider object
 303      * is returned. Note that the specified provider object does not have to be
 304      * registered in the provider list.
 305      *
 306      * <p>The <code>TerminalFactory</code> is initialized with the
 307      * specified parameters Object. The type of parameters
 308      * needed may vary between different types of <code>TerminalFactory</code>s.
 309      *
 310      * @param type the type of the requested TerminalFactory
 311      * @param params the parameters to pass to the TerminalFactorySpi
 312      *   implementation, or null if no parameters are needed
 313      * @param provider the provider
 314      * @return a TerminalFactory of the specified type
 315      *
 316      * @throws NullPointerException if type is null
 317      * @throws IllegalArgumentException if provider is null
 318      * @throws NoSuchAlgorithmException if a TerminalFactorySpi implementation
 319      *   of the specified type is not available from the specified Provider
 320      */
 321     public static TerminalFactory getInstance(String type, Object params,
 322             Provider provider) throws NoSuchAlgorithmException {
 323         Instance instance = GetInstance.getInstance("TerminalFactory",
 324             TerminalFactorySpi.class, type, params, provider);
 325         return new TerminalFactory((TerminalFactorySpi)instance.impl,
 326             instance.provider, type);
 327     }
 328 
 329     /**
 330      * Returns the provider of this TerminalFactory.
 331      *
 332      * @return the provider of this TerminalFactory.
 333      */
 334     public Provider getProvider() {
 335         return provider;
 336     }
 337 
 338     /**
 339      * Returns the type of this TerminalFactory. This is the value that was
 340      * specified in the getInstance() method that returned this object.
 341      *
 342      * @return the type of this TerminalFactory
 343      */
 344     public String getType() {
 345         return type;
 346     }
 347 
 348     /**
 349      * Returns a new CardTerminals object encapsulating the terminals
 350      * supported by this factory.
 351      * See the class comment of the {@linkplain CardTerminals} class
 352      * regarding how the returned objects can be shared and reused.
 353      *
 354      * @return a new CardTerminals object encapsulating the terminals
 355      * supported by this factory.
 356      */
 357     public CardTerminals terminals() {
 358         return spi.engineTerminals();
 359     }
 360 
 361     /**
 362      * Returns a string representation of this TerminalFactory.
 363      *
 364      * @return a string representation of this TerminalFactory.
 365      */
 366     public String toString() {
 367         return "TerminalFactory for type " + type + " from provider "
 368             + provider.getName();
 369     }
 370 
 371 }