/*
* @(#)PlayerPermission.java 1.10 06/03/11
*
* Copyright 2005, 2006 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*/
package javax.microedition.media;
import java.io.Serializable;
import java.io.IOException;
import java.security.*;
/**
* This class is for Media Player permissions.
*
* Care should be taken before granting permission to record
* or make a snapshot. See the Addendum: Multi-media security for details.
*
* The name
is the locator of the resource for which snapshot
* or record is to be allowed.
* If name
is "*" it applies to all locators.
* If the last character of the locator is "*",
* the permission applies to all locators that start with
* the name (without the "*").
*
* The actions to be granted are passed to the constructor in a string containing
* a list of one or more comma-separated keywords. The possible keywords are
* "record" and "snapshot". Their meaning is defined as follows:
*
* record
* record permission.
* Allows {@link javax.microedition.media.control.RecordControl#setRecordLocation}
* and {@link javax.microedition.media.control.RecordControl#setRecordStream} to be used
* snapshot
* snapshot permission.
* Allows {@link javax.microedition.media.control.VideoControl#getSnapshot}
* to be used
*
* The actions string is converted to lowercase before processing.
* @see java.security.Permission
* @see java.lang.SecurityManager
* @serial exclude
*/
public final class PlayerPermission extends Permission {
/**
* Record action.
*/
private final static int RECORD = 0x1;
/**
* Snapshot action.
*/
private final static int SNAPSHOT = 0x2;
/**
* All actions (RECORD,SNAPSHOT);
*/
private final static int ALL = RECORD|SNAPSHOT;
/**
* No actions.
*/
private final static int NONE = 0x0;
/**
* The actions mask.
*
*/
private transient int mask;
/**
* The actions string.
*
* @serial
*/
private String actions; // Left null as long as possible, then
// created and re-used in the getAction function.
/**
* initialize a PlayerPermission object. Common to all constructors.
* Also called during de-serialization.
*
* @param mask the actions mask to use.
* @throws IllegalArgumentException if the mask contain zero bits or
* contains any bits other than RECORD or SNAPSHOT or
* if name has a zero length.
* @throws NullPointerException if name is null
.
*/
private void init(int mask)
{
if ((mask & ALL) != mask)
throw new IllegalArgumentException("invalid actions mask");
if (mask == NONE)
throw new IllegalArgumentException("invalid actions mask");
if (getName().length() == 0) { // Throws NPE if name is null
throw new IllegalArgumentException("invalid name");
}
this.mask = mask;
}
/**
* Creates a new PlayerPermission object with the specified name
* and actions. name is the locator to which the permissions apply.
* actions contains a comma-separated list of the
* desired actions granted on the property. Possible actions are
* "record" and "snapshot".
* @param name the locator to which the permission applies.
* @param actions the actions string.
* @throws NullPointerException if name
or actions
* is null
.
* @throws IllegalArgumentException if action
is invalid or
* if name
is zero length.
*/
public PlayerPermission(String name, String actions)
{
super(name);
init(getMask(actions));
}
/**
* Checks if this PlayerPermission object "implies" the specified
* permission.
*
* More specifically, this method returns true if:
*
* p is an instanceof PlayerPermission,
* p's actions are a subset of this object's actions.
* p's name is implied by this object's name:
*
* if this object's name does not end in "*" and
* this object's name is equal to p's name.
* if this object's name ends in "*" and
* this object's name, without the final "*",
* is a prefix of p's name.
*
* For example, "*" implies any other locator,
* "capture:*" implies "capture://audio".
*
* @param p the permission to check against.
*
* @return true if the specified permission is implied by this object,
* false if not.
*/
public boolean implies(Permission p) {
if (!(p instanceof PlayerPermission))
return false;
PlayerPermission that = (PlayerPermission) p;
// Check of this PlayerPermission's name implies the other's name
boolean match = false;
String name = getName();
if (name.endsWith("*")) {
// Ends in a wildcard and the rest of this name is a prefix of that's
match = that.getName().startsWith(name.substring(0,name.length()-1));
} else {
// No wildcard, must match exactly
match = name.equals(that.getName());
}
// we get the effective mask. i.e., the "and" of this and that.
// They must be equal to that.mask for implies to return true.
return match && ((this.mask & that.mask) == that.mask);
}
/**
* Checks two PlayerPermission objects for equality. Checks that obj is
* a PlayerPermission, and has the same name and actions as this object.
*
* @param obj the object we are testing for equality with this object.
* @return true if obj is a PlayerPermission, and has the same name and
* actions as this PlayerPermission object.
*/
public boolean equals(Object obj) {
if (obj == this)
return true;
if (! (obj instanceof PlayerPermission))
return false;
PlayerPermission that = (PlayerPermission) obj;
if (getName() != that.getName())
return false;
return (this.mask == that.mask);
}
/**
* Returns the hash code value for this object.
* The value returned complies with the requirements of the
* the hashCode method of the superclass.
* @return a hash code value for this object.
*/
public int hashCode() {
return this.getName().hashCode();
}
/**
* Converts an actions String to an actions mask.
*
* @param action the action string.
* @return the actions mask.
*/
private static int getMask(String actions) {
int mask = NONE;
char[] a = actions.toCharArray();
int i = a.length - 1;
if (i < 0)
return mask;
while (i != -1) {
char c;
// skip whitespace
while ((i!=-1) && ((c = a[i]) == ' ' ||
c == '\r' ||
c == '\n' ||
c == '\f' ||
c == '\t'))
i--;
// check for the known strings
int matchlen;
if (i >= 5 && (a[i-5] == 'r' || a[i-5] == 'R') &&
(a[i-4] == 'e' || a[i-4] == 'E') &&
(a[i-3] == 'c' || a[i-3] == 'C') &&
(a[i-2] == 'o' || a[i-2] == 'O') &&
(a[i-1] == 'r' || a[i-1] == 'R') &&
(a[i] == 'd' || a[i] == 'D'))
{
matchlen = 6;
mask |= RECORD;
} else if (i >= 7 && (a[i-7] == 's' || a[i-7] == 'S') &&
(a[i-6] == 'n' || a[i-6] == 'N') &&
(a[i-5] == 'a' || a[i-5] == 'A') &&
(a[i-4] == 'p' || a[i-4] == 'P') &&
(a[i-3] == 's' || a[i-3] == 'S') &&
(a[i-2] == 'h' || a[i-2] == 'H') &&
(a[i-1] == 'o' || a[i-1] == 'O') &&
(a[i] == 't' || a[i] == 'T'))
{
matchlen = 8;
mask |= SNAPSHOT;
} else {
// parse error
throw new IllegalArgumentException(
"invalid permission: " + actions);
}
// make sure we didn't just match the tail of a word
// like "ackbarfaccept". Also, skip to the comma.
boolean seencomma = false;
while (i >= matchlen && !seencomma) {
switch(a[i-matchlen]) {
case ',':
seencomma = true;
/*FALLTHROUGH*/
case ' ': case '\r': case '\n':
case '\f': case '\t':
break;
default:
throw new IllegalArgumentException(
"invalid permission: " + actions);
}
i--;
}
// point i at the location of the comma minus one (or -1).
i -= matchlen;
}
return mask;
}
/**
* Return the canonical string representation of the actions.
* Always returns present actions in the following order:
* record, snapshot.
*
* @return the canonical string representation of the actions.
*/
private static String getActions(int mask)
{
if (mask == ALL) {
return "record,snapshot";
} else if (mask == SNAPSHOT) {
return "snapshot";
} else {
return "record";
}
}
/**
* Returns the "canonical string representation" of the actions.
* That is, this method always returns present actions in the following order:
* record, snapshot. For example, if this PlayerPermission object
* allows both record and snapshot actions, a call to getActions
* will return the string "record,snapshot".
* @return the canonical string representation of the actions.
*/
public String getActions()
{
if (actions == null)
actions = getActions(this.mask);
return actions;
}
/**
* Return the current action mask.
*
* @return the actions mask.
*/
private int getMask() {
return mask;
}
/**
* WriteObject is called to save the state of the PlayerPermission
* to a stream. The actions are serialized, and the superclass
* takes care of the name.
*/
private synchronized void writeObject(java.io.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();
}
/**
* readObject is called to restore the state of the PlayerPermission from
* a stream.
*/
private synchronized void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
// Read in the action, then initialize the rest
s.defaultReadObject();
init(getMask(actions));
}
}