1/*
2 * Copyright (c) 1999, 2015, 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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package TVJar;
24
25import java.security.Permission;
26import java.security.PermissionCollection;
27import java.util.ArrayList;
28import java.util.Collections;
29import java.util.Enumeration;
30import java.util.Iterator;
31import java.util.StringJoiner;
32import java.util.StringTokenizer;
33
34public class TVPermission extends Permission {
35
36    /**
37     * Watch
38     */
39    private final static int WATCH = 0x1;
40
41    /**
42     * Preview
43     */
44    private final static int PREVIEW = 0x2;
45
46    /**
47     * No actions
48     */
49    private final static int NONE = 0x0;
50
51    /**
52     * All actions
53     */
54    private final static int ALL = WATCH | PREVIEW;
55
56    // the actions mask
57    private int mask;
58
59    // the actions string
60    private String actions;
61
62    // the canonical name of the channel
63    private String cname;
64
65    // true if the channelname is a wildcard
66    private boolean wildcard;
67
68    // num range on channel
69    private int[] numrange;
70
71    // various num constants
72    private final static int NUM_MIN = 1;
73    private final static int NUM_MAX = 128;
74
75    public TVPermission(String channel, String action) {
76        this(channel, getMask(action));
77    }
78
79    TVPermission(String channel, int mask) {
80        super(channel);
81        init(channel, mask);
82    }
83
84    private synchronized int[] parseNum(String num)
85            throws Exception {
86
87        if (num == null || num.equals("") || num.equals("*")) {
88            wildcard = true;
89            return new int[]{NUM_MIN, NUM_MAX};
90        }
91
92        int dash = num.indexOf('-');
93
94        if (dash == -1) {
95            int p = 0;
96            try {
97                p = Integer.parseInt(num);
98            } catch (NumberFormatException nfe) {
99                throw new IllegalArgumentException("invalid input" + num);
100            }
101            return new int[]{p, p};
102        } else {
103            String low = num.substring(0, dash);
104            String high = num.substring(dash + 1);
105            int l, h;
106
107            if (low.equals("")) {
108                l = NUM_MIN;
109            } else {
110                try {
111                    l = Integer.parseInt(low);
112                } catch (NumberFormatException nfe) {
113                    throw new IllegalArgumentException("invalid input" + num);
114                }
115            }
116
117            if (high.equals("")) {
118                h = NUM_MAX;
119            } else {
120                try {
121                    h = Integer.parseInt(high);
122                } catch (NumberFormatException nfe) {
123                    throw new IllegalArgumentException("invalid input" + num);
124                }
125            }
126            if (h < l || l < NUM_MIN || h > NUM_MAX) {
127                throw new IllegalArgumentException("invalid num range");
128            }
129
130            return new int[]{l, h};
131        }
132    }
133
134    /**
135     * Initialize the TVPermission object.
136     */
137    private synchronized void init(String channel, int mask) {
138
139        // Parse the channel name.
140        int sep = channel.indexOf(':');
141
142        if (sep != -1) {
143            String num = channel.substring(sep + 1);
144            cname = channel.substring(0, sep);
145            try {
146                numrange = parseNum(num);
147            } catch (Exception e) {
148                throw new IllegalArgumentException("invalid num range: " + num);
149            }
150        } else {
151            numrange = new int[]{NUM_MIN, NUM_MAX};
152        }
153    }
154
155    /**
156     * Convert an action string to an integer actions mask.
157     *
158     * @param action the action string
159     * @return the action mask
160     */
161    private synchronized static int getMask(String action) {
162        int mask = NONE;
163
164        if (action == null) {
165            return mask;
166        }
167
168        StringTokenizer st = new StringTokenizer(action.toLowerCase(), ",");
169        while (st.hasMoreTokens()) {
170            String token = st.nextToken();
171            if (token.equals("watch")) {
172                mask |= WATCH;
173            } else if (token.equals("preview")) {
174                mask |= PREVIEW;
175            } else {
176                throw new IllegalArgumentException("invalid TV permission: " + token);
177            }
178        }
179        return mask;
180    }
181
182    @Override
183    public boolean implies(Permission p) {
184        if (!(p instanceof TVPermission)) {
185            return false;
186        }
187
188        if (this.wildcard) {
189            return true;
190        }
191
192        TVPermission that = (TVPermission) p;
193
194        if ((this.mask & that.mask) != that.mask) {
195            System.out.println("Masks are not ok this = "
196                    + this.mask + "THat = " + that.mask);
197            return false;
198        }
199
200        if ((this.numrange[0] > that.numrange[0])
201                || (this.numrange[1] < that.numrange[1])) {
202
203            System.out.println("This 0= " + this.numrange[0]
204                    + " 1 = " + this.numrange[1]);
205            System.out.println("That 0= " + that.numrange[0]
206                    + " 1 = " + that.numrange[1]);
207            return false;
208        }
209        return true;
210    }
211
212    /**
213     * Checks two TVPermission objects for equality.
214     * <p>
215     * @param obj the object we are testing for equality.
216     * @return true if obj is a TVPermission, and has the same channelname and
217     * action mask as this TVPermission object.
218     */
219    @Override
220    public boolean equals(Object obj) {
221        if (obj == this) {
222            return true;
223        }
224
225        if (!(obj instanceof TVPermission)) {
226            return false;
227        }
228
229        TVPermission that = (TVPermission) obj;
230
231        // check the mask first
232        if (this.mask != that.mask) {
233            return false;
234        }
235
236        // now check the num range...
237        if ((this.numrange[0] != that.numrange[0])
238                || (this.numrange[1] != that.numrange[1])) {
239            return false;
240        }
241
242        return this.getName().equals(that.getName());
243    }
244
245    /**
246     * Returns the hash code value for this object.
247     *
248     * @return a hash code value for this object.
249     */
250    @Override
251    public int hashCode() {
252        return this.getName().hashCode();
253    }
254
255    /**
256     * Return the canonical string representation of the actions. Always returns
257     * actions in the following order: watch,preview.
258     *
259     * @param mask a specific integer action mask to translate into a string
260     * @return the canonical string representation of the actions
261     */
262    private synchronized static String getActions(int mask) {
263        StringJoiner sj = new StringJoiner(",");
264        if ((mask & WATCH) == WATCH) {
265            sj.add("watch");
266        }
267        if ((mask & PREVIEW) == PREVIEW) {
268            sj.add("preview");
269        }
270        return sj.toString();
271    }
272
273    /**
274     * Return the canonical string representation of the actions. Always returns
275     * actions in the following order: watch,preview.
276     *
277     * @return the canonical string representation of the actions.
278     */
279    @Override
280    public String getActions() {
281        if (actions == null) {
282            actions = getActions(this.mask);
283        }
284
285        return actions;
286    }
287
288    @Override
289    public String toString() {
290        return super.toString() + "\n"
291                + "cname = " + cname + "\n"
292                + "wildcard = " + wildcard + "\n"
293                + "numrange = " + numrange[0] + "," + numrange[1] + "\n";
294
295    }
296
297    @Override
298    public PermissionCollection newPermissionCollection() {
299        return new TVPermissionCollection();
300    }
301}
302
303final class TVPermissionCollection extends PermissionCollection {
304
305    /**
306     * The TVPermissions for this set.
307     */
308    private final ArrayList<TVPermission> permissions = new ArrayList<>();
309
310    /**
311     * Adds a permission to the TVPermissions. The key for the hash is the name
312     * in the case of wildcards, or all the IP addresses.
313     *
314     * @param permission the Permission object to add.
315     */
316    @Override
317    public void add(Permission permission) {
318        if (!(permission instanceof TVPermission)) {
319            throw new IllegalArgumentException("invalid permission: " + permission);
320        }
321        permissions.add((TVPermission) permission);
322    }
323
324    /**
325     * Check and see if this collection of permissions implies the permissions
326     * expressed in "permission".
327     *
328     * @param p the Permission object to compare
329     *
330     * @return true if "permission" is a proper subset of a permission in the
331     * collection, false if not.
332     */
333    @Override
334    public boolean implies(Permission p) {
335        if (!(p instanceof TVPermission)) {
336            return false;
337        }
338
339        Iterator<TVPermission> i = permissions.iterator();
340        while (i.hasNext()) {
341            if (((TVPermission) i.next()).implies(p)) {
342                return true;
343            }
344        }
345        return false;
346    }
347
348    /**
349     * Returns an enumeration of all the TVPermission objects in the container.
350     *
351     * @return an enumeration of all the TVPermission objects.
352     */
353    @Override
354    public Enumeration elements() {
355        return Collections.enumeration(permissions);
356    }
357
358}
359