/* | 
|
 * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. | 
|
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 
|
 * | 
|
 * This code is free software; you can redistribute it and/or modify it | 
|
 * under the terms of the GNU General Public License version 2 only, as | 
|
 * published by the Free Software Foundation.  Oracle designates this | 
|
 * particular file as subject to the "Classpath" exception as provided | 
|
 * by Oracle in the LICENSE file that accompanied this code. | 
|
 * | 
|
 * This code is distributed in the hope that it will be useful, but WITHOUT | 
|
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
|
 * version 2 for more details (a copy is included in the LICENSE file that | 
|
 * accompanied this code). | 
|
 * | 
|
 * You should have received a copy of the GNU General Public License version | 
|
 * 2 along with this work; if not, write to the Free Software Foundation, | 
|
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | 
|
 * | 
|
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | 
|
 * or visit www.oracle.com if you need additional information or have any | 
|
 * questions. | 
|
*/  | 
|
package javax.smartcardio;  | 
|
import java.io.*;  | 
|
import java.security.Permission;  | 
|
/** | 
|
 * A permission for Smart Card operations. A CardPermission consists of the | 
|
 * name of the card terminal the permission applies to and a set of actions | 
|
 * that are valid for that terminal. | 
|
 * | 
|
 * <p>A CardPermission with a name of <code>*</code> applies to all | 
|
 * card terminals. The actions string is a comma separated list of the actions | 
|
 * listed below, or <code>*</code> to signify "all actions." | 
|
 * | 
|
 * <p>Individual actions are: | 
|
 * <dl> | 
|
 * <dt>connect | 
|
 * <dd>connect to a card using | 
|
 * {@linkplain CardTerminal#connect CardTerminal.connect()} | 
|
 * | 
|
 * <dt>reset | 
|
 * <dd>reset the card using {@linkplain Card#disconnect Card.disconnect(true)} | 
|
 * | 
|
 * <dt>exclusive | 
|
 * <dd>establish exclusive access to a card using | 
|
 * {@linkplain Card#beginExclusive} and {@linkplain Card#endExclusive | 
|
 * endExclusive()} | 
|
 * | 
|
 * <dt>transmitControl | 
|
 * <dd>transmit a control command using | 
|
 * {@linkplain Card#transmitControlCommand Card.transmitControlCommand()} | 
|
 * | 
|
 * <dt>getBasicChannel | 
|
 * <dd>obtain the basic logical channel using | 
|
 * {@linkplain Card#getBasicChannel} | 
|
 * | 
|
 * <dt>openLogicalChannel | 
|
 * <dd>open a new logical channel using | 
|
 * {@linkplain Card#openLogicalChannel} | 
|
 * | 
|
 * </dl> | 
|
 * | 
|
 * @since   1.6 | 
|
 * @author  Andreas Sterbenz | 
|
 * @author  JSR 268 Expert Group | 
|
*/  | 
|
public class CardPermission extends Permission {  | 
|
private static final long serialVersionUID = 7146787880530705613L;  | 
|
private final static int A_CONNECT = 0x01;  | 
|
private final static int A_EXCLUSIVE = 0x02;  | 
|
private final static int A_GET_BASIC_CHANNEL = 0x04;  | 
|
private final static int A_OPEN_LOGICAL_CHANNEL = 0x08;  | 
|
private final static int A_RESET = 0x10;  | 
|
private final static int A_TRANSMIT_CONTROL = 0x20;  | 
|
    // sum of all the actions above | 
|
private final static int A_ALL = 0x3f;  | 
|
    private final static int[] ARRAY_MASKS = { | 
|
A_ALL,  | 
|
A_CONNECT,  | 
|
A_EXCLUSIVE,  | 
|
A_GET_BASIC_CHANNEL,  | 
|
A_OPEN_LOGICAL_CHANNEL,  | 
|
A_RESET,  | 
|
A_TRANSMIT_CONTROL,  | 
|
};  | 
|
private final static String S_CONNECT = "connect";  | 
|
private final static String S_EXCLUSIVE = "exclusive";  | 
|
private final static String S_GET_BASIC_CHANNEL = "getBasicChannel";  | 
|
private final static String S_OPEN_LOGICAL_CHANNEL = "openLogicalChannel";  | 
|
private final static String S_RESET = "reset";  | 
|
private final static String S_TRANSMIT_CONTROL = "transmitControl";  | 
|
private final static String S_ALL = "*";  | 
|
private final static String[] ARRAY_STRINGS = {  | 
|
S_ALL,  | 
|
S_CONNECT,  | 
|
S_EXCLUSIVE,  | 
|
S_GET_BASIC_CHANNEL,  | 
|
S_OPEN_LOGICAL_CHANNEL,  | 
|
S_RESET,  | 
|
S_TRANSMIT_CONTROL,  | 
|
};  | 
|
private transient int mask;  | 
|
    /** | 
|
     * @serial | 
|
*/  | 
|
private volatile String actions;  | 
|
    /** | 
|
     * Constructs a new CardPermission with the specified actions. | 
|
     * <code>terminalName</code> is the name of a CardTerminal or <code>*</code> | 
|
     * if this permission applies to all terminals. <code>actions</code> | 
|
     * contains a comma-separated list of the individual actions | 
|
     * or <code>*</code> to signify all actions. For more information, | 
|
     * see the documentation at the top of this {@linkplain CardPermission | 
|
     * class}. | 
|
     * | 
|
     * @param terminalName the name of the card terminal, or <code>*</code> | 
|
     * @param actions the action string (or null if the set of permitted | 
|
     *   actions is empty) | 
|
     * | 
|
     * @throws NullPointerException if terminalName is null | 
|
     * @throws IllegalArgumentException if actions is an invalid actions | 
|
     *   specification | 
|
*/  | 
|
public CardPermission(String terminalName, String actions) {  | 
|
super(terminalName);  | 
|
        if (terminalName == null) { | 
|
throw new NullPointerException();  | 
|
}  | 
|
mask = getMask(actions);  | 
|
}  | 
|
private static int getMask(String actions) {  | 
|
if ((actions == null) || (actions.length() == 0)) {  | 
|
throw new IllegalArgumentException("actions must not be empty");  | 
|
}  | 
|
        // try exact matches for simple actions first | 
|
for (int i = 0; i < ARRAY_STRINGS.length; i++) {  | 
|
if (actions == ARRAY_STRINGS[i]) {  | 
|
return ARRAY_MASKS[i];  | 
|
}  | 
|
}  | 
|
if (actions.endsWith(",")) {  | 
|
throw new IllegalArgumentException("Invalid actions: '" + actions + "'");  | 
|
}  | 
|
int mask = 0;  | 
|
String[] split = actions.split(",");  | 
|
outer:  | 
|
for (String s : split) {  | 
|
for (int i = 0; i < ARRAY_STRINGS.length; i++) {  | 
|
if (ARRAY_STRINGS[i].equalsIgnoreCase(s)) {  | 
|
mask |= ARRAY_MASKS[i];  | 
|
continue outer;  | 
|
}  | 
|
}  | 
|
throw new IllegalArgumentException("Invalid action: '" + s + "'");  | 
|
}  | 
|
return mask;  | 
|
}  | 
|
private static String getActions(int mask) {  | 
|
if (mask == A_ALL) {  | 
|
return S_ALL;  | 
|
}  | 
|
boolean first = true;  | 
|
StringBuilder sb = new StringBuilder();  | 
|
for (int i = 0; i < ARRAY_MASKS.length; i++) {  | 
|
int action = ARRAY_MASKS[i];  | 
|
if ((mask & action) == action) {  | 
|
if (first == false) {  | 
|
sb.append(",");  | 
|
                } else { | 
|
first = false;  | 
|
}  | 
|
sb.append(ARRAY_STRINGS[i]);  | 
|
}  | 
|
}  | 
|
return sb.toString();  | 
|
}  | 
|
    /** | 
|
     * Returns the canonical string representation of the actions. | 
|
     * It is <code>*</code> to signify all actions defined by this class or | 
|
     * the string concatenation of the comma-separated, | 
|
     * lexicographically sorted list of individual actions. | 
|
     * | 
|
     * @return the canonical string representation of the actions. | 
|
*/  | 
|
public String getActions() {  | 
|
if (actions == null) {  | 
|
actions = getActions(mask);  | 
|
}  | 
|
return actions;  | 
|
}  | 
|
    /** | 
|
     * Checks if this CardPermission object implies the specified permission. | 
|
     * That is the case, if and only if | 
|
     * <ul> | 
|
     * <li><p><code>permission</code> is an instance of CardPermission,</p> | 
|
     * <li><p><code>permission</code>'s actions are a proper subset of this | 
|
     *   object's actions, and</p> | 
|
     * <li><p>this object's <code>getName()</code> method is either | 
|
     *   <code>*</code> or equal to <code>permission</code>'s <code>name</code>. | 
|
     *   </p> | 
|
     * </ul> | 
|
     * | 
|
     * @param permission the permission to check against | 
|
     * @return true if and only if this CardPermission object implies the | 
|
     *   specified permission. | 
|
*/  | 
|
public boolean implies(Permission permission) {  | 
|
if (permission instanceof CardPermission == false) {  | 
|
return false;  | 
|
}  | 
|
CardPermission other = (CardPermission)permission;  | 
|
if ((this.mask & other.mask) != other.mask) {  | 
|
return false;  | 
|
}  | 
|
String thisName = getName();  | 
|
if (thisName.equals("*")) {  | 
|
return true;  | 
|
}  | 
|
if (thisName.equals(other.getName())) {  | 
|
return true;  | 
|
}  | 
|
return false;  | 
|
}  | 
|
    /** | 
|
     * Compares the specified object with this CardPermission for equality. | 
|
     * This CardPermission is equal to another Object <code>object</code>, if | 
|
     * and only if | 
|
     * <ul> | 
|
     * <li><p><code>object</code> is an instance of CardPermission,</p> | 
|
     * <li><p><code>this.getName()</code> is equal to | 
|
     * <code>((CardPermission)object).getName()</code>, and</p> | 
|
     * <li><p><code>this.getActions()</code> is equal to | 
|
     * <code>((CardPermission)object).getActions()</code>.</p> | 
|
     * </ul> | 
|
     * | 
|
     * @param obj the object to be compared for equality with this CardPermission | 
|
     * @return true if and only if the specified object is equal to this | 
|
     *   CardPermission | 
|
*/  | 
|
public boolean equals(Object obj) {  | 
|
if (this == obj) {  | 
|
return true;  | 
|
}  | 
|
if (obj instanceof CardPermission == false) {  | 
|
return false;  | 
|
}  | 
|
CardPermission other = (CardPermission)obj;  | 
|
return this.getName().equals(other.getName()) && (this.mask == other.mask);  | 
|
}  | 
|
    /** | 
|
     * Returns the hash code value for this CardPermission object. | 
|
     * | 
|
     * @return the hash code value for this CardPermission object. | 
|
*/  | 
|
    public int hashCode() { | 
|
return getName().hashCode() + 31 * mask;  | 
|
}  | 
|
private void writeObject(ObjectOutputStream s) throws IOException {  | 
|
// Write out the actions. The superclass takes care of the name.  | 
|
        // Call getActions to make sure actions field is initialized | 
|
if (actions == null) {  | 
|
getActions();  | 
|
}  | 
|
s.defaultWriteObject();  | 
|
}  | 
|
private void readObject(ObjectInputStream s)  | 
|
throws IOException, ClassNotFoundException {  | 
|
        // Read in the actions, then restore the mask. | 
|
s.defaultReadObject();  | 
|
mask = getMask(actions);  | 
|
}  | 
|
}  |