001: package net.matuschek.http;
002:
003: import java.io.BufferedReader;
004: import java.io.FileInputStream;
005: import java.io.IOException;
006: import java.io.InputStream;
007: import java.io.InputStreamReader;
008: import java.util.StringTokenizer;
009: import java.util.Vector;
010:
011: /*********************************************
012: Copyright (c) 2001 by Daniel Matuschek
013: *********************************************/
014:
015: /**
016: * This download selector decides if a file should be downloaded
017: * based on the mime type and the size of the content (content-length
018: * header)
019: *
020: * @author Daniel Matuschek daniel@matuschek.net
021: * @version $Id: DownloadRuleSet.java,v 1.5 2003/02/27 18:46:58 oliver_schmidt Exp $
022: */
023: public class DownloadRuleSet {
024:
025: /**********************************************************************/
026: /* instance variables */
027: /**********************************************************************/
028:
029: /** a vector containing all rules **/
030: protected Vector<DownloadRule> rules;
031:
032: /** default behavior, if no rule matches (allow or deny)
033: true = allow, false = deny **/
034: protected boolean defaultBehavior = true;
035:
036: /**********************************************************************/
037: /* methods */
038: /**********************************************************************/
039:
040: /**
041: * initializes the DownloadRuleSet with an empty rule set
042: * if no other rules will be added, it will allow all downloads
043: */
044: public DownloadRuleSet() {
045: rules = new Vector<DownloadRule>();
046: defaultBehavior = true;
047: }
048:
049: /**
050: * initializes the DownloadRuleSet with a rule set
051: * read from a file
052: * @see #loadRuleFile(String)
053: */
054: public DownloadRuleSet(String filename) throws IOException {
055: this ();
056: loadRuleFile(filename);
057: }
058:
059: /**
060: * adds a set of rules that are defined in a rule file
061: * a rule file consists of lines in the format<br />
062: * allow|deny mimetype/subtype <xxxx >yyyyy
063: */
064: public void loadRuleFile(String filename) throws IOException {
065: InputStream is = new FileInputStream(filename);
066: BufferedReader reader = new BufferedReader(
067: new InputStreamReader(is));
068:
069: String line = "";
070: int lineno = 0;
071:
072: while (line != null) {
073: line = reader.readLine();
074: lineno++;
075:
076: if ((line != null) && (!line.trim().equals(""))
077: && (!line.startsWith("#"))) {
078: StringTokenizer st = new StringTokenizer(line);
079: // we need at least 2 tokens
080: if (st.countTokens() < 2) {
081: throw new IOException("line " + lineno
082: + " has less then 2 fields");
083: }
084:
085: String allowStr = st.nextToken();
086: boolean allow = true;
087: String mime = st.nextToken();
088:
089: // allow or deny ?
090: if (allowStr.equalsIgnoreCase("allow")) {
091: allow = true;
092: } else if (allowStr.equalsIgnoreCase("deny")) {
093: allow = false;
094: } else {
095: throw new IOException("first token in line "
096: + lineno + " has to be allow or deny");
097: }
098:
099: DownloadRule r = new DownloadRule();
100: r.setAllow(allow);
101: try {
102: r.setMimeType(mime);
103: } catch (IllegalArgumentException e) {
104: throw new IOException(e.getMessage());
105: }
106:
107: // parse < and > rules
108: while (st.hasMoreTokens()) {
109: boolean isMin = true;
110:
111: String descr = st.nextToken();
112:
113: if (descr.startsWith("<")) {
114: // it is a maximum value
115: isMin = false;
116: } else if (descr.startsWith(">")) {
117: isMin = true;
118: } else {
119: throw new IOException("can't understand "
120: + descr + " in line " + lineno);
121: }
122:
123: int size = 0;
124: try {
125: size = Integer.parseInt(descr.substring(1));
126: } catch (NumberFormatException e) {
127: throw new IOException("no numerical value "
128: + descr + " in line " + lineno);
129: }
130:
131: if (isMin) {
132: r.setMinSize(size);
133: } else {
134: r.setMaxSize(size);
135: }
136: }
137:
138: rules.add(r);
139: }
140: }
141: }
142:
143: /**
144: * sets the default behavior
145: * @param allow allow or deny download if no matching rule was
146: * found
147: */
148: public void setDefault(boolean allow) {
149: this .defaultBehavior = allow;
150: }
151:
152: /**
153: * gets the default behaviour
154: */
155: public boolean getDefault() {
156: return this .defaultBehavior;
157: }
158:
159: /**
160: * Get the value of downloadRules.
161: * @return Value of downloadRules as a Vector fo DownloadRule objects.
162: */
163: public Vector getDownloadRules() {
164: return this .rules;
165: }
166:
167: /**
168: * Set the value of downloadRules.
169: * @param v Value to assign to downloadRules. Must be a vector
170: * of DownloadRule objects
171: */
172: public void setDownloadRules(Vector<DownloadRule> downloadRules) {
173: this .rules = downloadRules;
174: }
175:
176: /**
177: * adds a download rule for the given mimetype/subtype
178: * @param mimeType basic mime type (the part before /)
179: * @param mimeSubtype mime sub type (part after /)
180: * @param minSize minimal size (in bytes)
181: * @param maxSize maximal size (in bytes)
182: * @param allow allow or deny this download ?
183: * wildchar "*" can be used as mimeType and mimeSubtype that means
184: * "all". there will be no pattern matching, that means "*" matches
185: * all types, but "t*" doesn't match all types that start with t
186: */
187: public void addRule(String mimeBaseType, String mimeSubtype,
188: int minSize, int maxSize, boolean allow) {
189: DownloadRule newrule = new DownloadRule();
190: newrule.setMimeBaseType(mimeBaseType);
191: newrule.setMimeSubType(mimeSubtype);
192: newrule.setMinSize(minSize);
193: newrule.setMaxSize(maxSize);
194: newrule.setAllow(allow);
195: rules.add(newrule);
196: }
197:
198: /**
199: * finds the first matching rule
200: * @param mimetype mimeType ("type/subtype")
201: * @return a rule or null if no rule was found
202: */
203: private DownloadRule findRule(String mimeType, int size) {
204: // is it a valid mime string
205: if (mimeType.indexOf("/") < 0) {
206: return null;
207: }
208:
209: // modified Dominic Betts 28/5/02
210: // mimetype like:
211: // Content-Type: text/html; Charset=iso-8859-1
212: if (mimeType.indexOf(";") > 0) {
213: StringTokenizer st = new StringTokenizer(mimeType, ";");
214: mimeType = st.nextToken();
215: }
216:
217: String basetype = null;
218: String subtype = null;
219: StringTokenizer st = new StringTokenizer(mimeType, "/");
220: basetype = st.nextToken();
221: subtype = st.nextToken();
222:
223: for (int i = 0; i < rules.size(); i++) {
224: DownloadRule rule = (DownloadRule) rules.elementAt(i);
225: if (rule.matches(basetype, subtype, size)) {
226: return rule;
227: }
228: }
229:
230: return null;
231: }
232:
233: /**
234: * gets the value of a httpHeader from a vector of httpHeaders
235: * @param httpHeaders a Vector of HttpHeader objects
236: * @param name name of the header (e.g. content-length) not case-sensitive
237: * @return the value of this header or null if this header doesn't
238: * exists
239: */
240: protected String getHeaderValue(Vector httpHeaders, String name) {
241: for (int i = 0; i < httpHeaders.size(); i++) {
242: HttpHeader h = (HttpHeader) httpHeaders.elementAt(i);
243: if (h.getName().equalsIgnoreCase(name)) {
244: return h.getValue();
245: }
246: }
247: return null;
248: }
249:
250: public boolean downloadAllowed(Vector httpHeaders) {
251: String mimeType = getHeaderValue(httpHeaders,
252: HttpHeader.CONTENT_TYPE);
253: String sizeStr = getHeaderValue(httpHeaders,
254: HttpHeader.CONTENT_LENGTH);
255:
256: // mimeType must exists in any HTTP response !!!
257: if (mimeType == null) {
258: return false;
259: }
260:
261: // size MAY exist, if not use -1 for unknown
262: int size = -1;
263: try {
264: size = Integer.parseInt(sizeStr);
265: } catch (NumberFormatException e) {
266: }
267:
268: DownloadRule r = findRule(mimeType, size);
269:
270: if (r == null) {
271: return defaultBehavior;
272: } else {
273: // System.err.println(size+" "+r);
274: return r.getAllow();
275: }
276: }
277:
278: public boolean processAllowed(Vector httpHeaders) {
279: String mimeType = getHeaderValue(httpHeaders,
280: HttpHeader.CONTENT_TYPE);
281: String sizeStr = getHeaderValue(httpHeaders,
282: HttpHeader.CONTENT_LENGTH);
283:
284: // mimeType must exists in any HTTP response !!!
285: if (mimeType == null) {
286: return false;
287: }
288:
289: // size MAY exist, if not use -1 for unknown
290: int size = -1;
291: try {
292: size = Integer.parseInt(sizeStr);
293: } catch (NumberFormatException e) {
294: }
295:
296: DownloadRule r = findRule(mimeType, size);
297:
298: if (r == null) {
299: return defaultBehavior;
300: } else {
301: // System.err.println(size+" "+r);
302: return r.getProcessAllowed();
303: }
304: }
305:
306: /**
307: * converts the object to a String represenation. the format may
308: * change without notice. Use it only for debugging and logging.
309: */
310: public String toString() {
311: StringBuffer sb = new StringBuffer();
312: sb.append("DownloadRule default=");
313: if (defaultBehavior) {
314: sb.append("true");
315: } else {
316: sb.append("false");
317: }
318: sb.append("\n");
319:
320: for (int i = 0; i < rules.size(); i++) {
321: sb.append(" ");
322: sb.append(((DownloadRule) rules.elementAt(i)).toString());
323: sb.append("\n");
324: }
325: return sb.toString();
326: }
327:
328: } // DownloadRuleSet
|