001: /*
002: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
004: *
005: * This program is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU General Public License version
007: * 2 only, as published by the Free Software Foundation.
008: *
009: * This program is distributed in the hope that it will be useful, but
010: * WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * General Public License version 2 for more details (a copy is
013: * included at /legal/license.txt).
014: *
015: * You should have received a copy of the GNU General Public License
016: * version 2 along with this work; if not, write to the Free Software
017: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
018: * 02110-1301 USA
019: *
020: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
021: * Clara, CA 95054 or visit www.sun.com if you need additional
022: * information or have any questions.
023: */
024:
025: package javax.microedition.media;
026:
027: import java.io.Serializable;
028: import java.io.IOException;
029: import java.security.*;
030:
031: /**
032: * This class is for Media Player permissions.
033: * <P>
034: * Care should be taken before granting permission to record
035: * or make a snapshot. See the Addendum: Multi-media security for details.
036: * <P>
037: * The <code>name</code> is the locator of the resource for which snapshot
038: * or record is to be allowed.
039: * If <code>name</code> is "*" it applies to all locators.
040: * If the last character of the locator is "*",
041: * the permission applies to all locators that start with
042: * the name (without the "*").
043: * <P>
044: * The actions to be granted are passed to the constructor in a string containing
045: * a list of one or more comma-separated keywords. The possible keywords are
046: * "record" and "snapshot". Their meaning is defined as follows:
047: * <P>
048: * <DL>
049: * <DT> <tt>record</tt>
050: * <DD> record permission.
051: * Allows {@link javax.microedition.media.control.RecordControl#setRecordLocation}
052: * and {@link javax.microedition.media.control.RecordControl#setRecordStream} to be used
053: * <DT> <tt>snapshot</tt>
054: * <DD> snapshot permission.
055: * Allows {@link javax.microedition.media.control.VideoControl#getSnapshot}
056: * to be used
057: * </DL>
058: * <P>
059: * The actions string is converted to lowercase before processing.
060: * @see java.security.Permission
061: * @see java.lang.SecurityManager
062: * @serial exclude
063: */
064:
065: public final class PlayerPermission extends Permission {
066:
067: /**
068: * Record action.
069: */
070: private final static int RECORD = 0x1;
071:
072: /**
073: * Snapshot action.
074: */
075: private final static int SNAPSHOT = 0x2;
076: /**
077: * All actions (RECORD,SNAPSHOT);
078: */
079: private final static int ALL = RECORD | SNAPSHOT;
080: /**
081: * No actions.
082: */
083: private final static int NONE = 0x0;
084:
085: /**
086: * The actions mask.
087: *
088: */
089: private transient int mask;
090:
091: /**
092: * The actions string.
093: *
094: * @serial
095: */
096: private String actions; // Left null as long as possible, then
097:
098: // created and re-used in the getAction function.
099:
100: /**
101: * initialize a PlayerPermission object. Common to all constructors.
102: * Also called during de-serialization.
103: *
104: * @param mask the actions mask to use.
105: * @throws IllegalArgumentException if the mask contain zero bits or
106: * contains any bits other than RECORD or SNAPSHOT or
107: * if name has a zero length.
108: * @throws NullPointerException if name is <code>null</code>.
109: */
110: private void init(int mask) {
111:
112: if ((mask & ALL) != mask)
113: throw new IllegalArgumentException("invalid actions mask");
114:
115: if (mask == NONE)
116: throw new IllegalArgumentException("invalid actions mask");
117:
118: if (getName().length() == 0) { // Throws NPE if name is null
119: throw new IllegalArgumentException("invalid name");
120: }
121: this .mask = mask;
122: }
123:
124: /**
125: * Creates a new PlayerPermission object with the specified name
126: * and actions. <i>name</i> is the locator to which the permissions apply.
127: * <i>actions</i> contains a comma-separated list of the
128: * desired actions granted on the property. Possible actions are
129: * "record" and "snapshot".
130: * @param name the locator to which the permission applies.
131: * @param actions the actions string.
132: * @throws NullPointerException if <code>name</code> or <code>actions</code>
133: * is <code>null</code>.
134: * @throws IllegalArgumentException if <CODE>action</CODE> is invalid or
135: * if <code>name</code> is zero length.
136: */
137: public PlayerPermission(String name, String actions) {
138: super (name);
139: init(getMask(actions));
140: }
141:
142: /**
143: * Checks if this PlayerPermission object "implies" the specified
144: * permission.
145: * <P>
146: * More specifically, this method returns true if:<p>
147: * <ul>
148: * <li> <i>p</i> is an instanceof PlayerPermission,<p>
149: * <li> <i>p</i>'s actions are a subset of this object's actions.
150: * <li> <i>p</i>'s name is implied by this object's name:
151: * <ul>
152: * <li>if this object's name does not end in "*" and
153: * this object's name is equal to <i>p</i>'s name.
154: * <li>if this object's name ends in "*" and
155: * this object's name, without the final "*",
156: * is a prefix of <i>p</i>'s name.
157: * </ul>
158: * For example, "*" implies any other locator,
159: * "capture:*" implies "capture://audio".
160: * </ul>
161: * @param p the permission to check against.
162: *
163: * @return true if the specified permission is implied by this object,
164: * false if not.
165: */
166: public boolean implies(Permission p) {
167: if (!(p instanceof PlayerPermission))
168: return false;
169:
170: PlayerPermission that = (PlayerPermission) p;
171:
172: // Check of this PlayerPermission's name implies the other's name
173: boolean match = false;
174: String name = getName();
175: if (name.endsWith("*")) {
176: // Ends in a wildcard and the rest of this name is a prefix of that's
177: match = that.getName().startsWith(
178: name.substring(0, name.length() - 1));
179: } else {
180: // No wildcard, must match exactly
181: match = name.equals(that.getName());
182: }
183: // we get the effective mask. i.e., the "and" of this and that.
184: // They must be equal to that.mask for implies to return true.
185:
186: return match && ((this .mask & that.mask) == that.mask);
187: }
188:
189: /**
190: * Checks two PlayerPermission objects for equality. Checks that <i>obj</i> is
191: * a PlayerPermission, and has the same name and actions as this object.
192: * <P>
193: * @param obj the object we are testing for equality with this object.
194: * @return true if obj is a PlayerPermission, and has the same name and
195: * actions as this PlayerPermission object.
196: */
197: public boolean equals(Object obj) {
198: if (obj == this )
199: return true;
200:
201: if (!(obj instanceof PlayerPermission))
202: return false;
203:
204: PlayerPermission that = (PlayerPermission) obj;
205:
206: if (getName() != that.getName())
207: return false;
208:
209: return (this .mask == that.mask);
210: }
211:
212: /**
213: * Returns the hash code value for this object.
214: * The value returned complies with the requirements of the
215: * the hashCode method of the superclass.
216: * @return a hash code value for this object.
217: */
218:
219: public int hashCode() {
220: return this .getName().hashCode();
221: }
222:
223: /**
224: * Converts an actions String to an actions mask.
225: *
226: * @param action the action string.
227: * @return the actions mask.
228: */
229: private static int getMask(String actions) {
230:
231: int mask = NONE;
232:
233: char[] a = actions.toCharArray();
234:
235: int i = a.length - 1;
236: if (i < 0)
237: return mask;
238:
239: while (i != -1) {
240: char c;
241:
242: // skip whitespace
243: while ((i != -1)
244: && ((c = a[i]) == ' ' || c == '\r' || c == '\n'
245: || c == '\f' || c == '\t'))
246: i--;
247:
248: // check for the known strings
249: int matchlen;
250:
251: if (i >= 5 && (a[i - 5] == 'r' || a[i - 5] == 'R')
252: && (a[i - 4] == 'e' || a[i - 4] == 'E')
253: && (a[i - 3] == 'c' || a[i - 3] == 'C')
254: && (a[i - 2] == 'o' || a[i - 2] == 'O')
255: && (a[i - 1] == 'r' || a[i - 1] == 'R')
256: && (a[i] == 'd' || a[i] == 'D')) {
257: matchlen = 6;
258: mask |= RECORD;
259:
260: } else if (i >= 7 && (a[i - 7] == 's' || a[i - 7] == 'S')
261: && (a[i - 6] == 'n' || a[i - 6] == 'N')
262: && (a[i - 5] == 'a' || a[i - 5] == 'A')
263: && (a[i - 4] == 'p' || a[i - 4] == 'P')
264: && (a[i - 3] == 's' || a[i - 3] == 'S')
265: && (a[i - 2] == 'h' || a[i - 2] == 'H')
266: && (a[i - 1] == 'o' || a[i - 1] == 'O')
267: && (a[i] == 't' || a[i] == 'T')) {
268: matchlen = 8;
269: mask |= SNAPSHOT;
270:
271: } else {
272: // parse error
273: throw new IllegalArgumentException(
274: "invalid permission: " + actions);
275: }
276:
277: // make sure we didn't just match the tail of a word
278: // like "ackbarfaccept". Also, skip to the comma.
279: boolean seencomma = false;
280: while (i >= matchlen && !seencomma) {
281: switch (a[i - matchlen]) {
282: case ',':
283: seencomma = true;
284: /*FALLTHROUGH*/
285: case ' ':
286: case '\r':
287: case '\n':
288: case '\f':
289: case '\t':
290: break;
291: default:
292: throw new IllegalArgumentException(
293: "invalid permission: " + actions);
294: }
295: i--;
296: }
297:
298: // point i at the location of the comma minus one (or -1).
299: i -= matchlen;
300: }
301:
302: return mask;
303: }
304:
305: /**
306: * Return the canonical string representation of the actions.
307: * Always returns present actions in the following order:
308: * record, snapshot.
309: *
310: * @return the canonical string representation of the actions.
311: */
312: private static String getActions(int mask) {
313: if (mask == ALL) {
314: return "record,snapshot";
315: } else if (mask == SNAPSHOT) {
316: return "snapshot";
317: } else {
318: return "record";
319: }
320: }
321:
322: /**
323: * Returns the "canonical string representation" of the actions.
324: * That is, this method always returns present actions in the following order:
325: * record, snapshot. For example, if this PlayerPermission object
326: * allows both record and snapshot actions, a call to <code>getActions</code>
327: * will return the string "record,snapshot".
328: * @return the canonical string representation of the actions.
329: */
330: public String getActions() {
331: if (actions == null)
332: actions = getActions(this .mask);
333:
334: return actions;
335: }
336:
337: /**
338: * Return the current action mask.
339: *
340: * @return the actions mask.
341: */
342:
343: private int getMask() {
344: return mask;
345: }
346:
347: /**
348: * WriteObject is called to save the state of the PlayerPermission
349: * to a stream. The actions are serialized, and the superclass
350: * takes care of the name.
351: */
352:
353: private synchronized void writeObject(java.io.ObjectOutputStream s)
354: throws IOException {
355: // Write out the actions. The superclass takes care of the name
356: // call getActions to make sure actions field is initialized
357: if (actions == null)
358: getActions();
359: s.defaultWriteObject();
360: }
361:
362: /**
363: * readObject is called to restore the state of the PlayerPermission from
364: * a stream.
365: */
366: private synchronized void readObject(java.io.ObjectInputStream s)
367: throws IOException, ClassNotFoundException {
368: // Read in the action, then initialize the rest
369: s.defaultReadObject();
370: init(getMask(actions));
371: }
372: }
|