0001: /*
0002: * @(#)URLConnection.java 1.98 03/08/14
0003: *
0004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License version
0009: * 2 only, as published by the Free Software Foundation.
0010: *
0011: * This program is distributed in the hope that it will be useful, but
0012: * WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * General Public License version 2 for more details (a copy is
0015: * included at /legal/license.txt).
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * version 2 along with this work; if not, write to the Free Software
0019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA
0021: *
0022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023: * Clara, CA 95054 or visit www.sun.com if you need additional
0024: * information or have any questions.
0025: *
0026: */
0027:
0028: package java.net;
0029:
0030: import java.io.IOException;
0031: import java.io.InputStream;
0032: import java.io.OutputStream;
0033: import java.util.Hashtable;
0034: import java.text.DateFormat;
0035: import java.util.StringTokenizer;
0036: import java.util.Collections;
0037: import java.util.Map;
0038: import java.security.Permission;
0039: import java.security.AccessController;
0040: import sun.security.util.SecurityConstants;
0041:
0042: /**
0043: * The abstract class <code>URLConnection</code> is the superclass
0044: * of all classes that represent a communications link between the
0045: * application and a URL. Instances of this class can be used both to
0046: * read from and to write to the resource referenced by the URL. In
0047: * general, creating a connection to a URL is a multistep process:
0048: * <p>
0049: * <center><table border=2 summary="Describes the process of creating a connection to a URL: openConnection() and connect() over time.">
0050: * <tr><th><code>openConnection()</code></th>
0051: * <th><code>connect()</code></th></tr>
0052: * <tr><td>Manipulate parameters that affect the connection to the remote
0053: * resource.</td>
0054: * <td>Interact with the resource; query header fields and
0055: * contents.</td></tr>
0056: * </table>
0057: * ---------------------------->
0058: * <br>time</center>
0059: *
0060: * <ol>
0061: * <li>The connection object is created by invoking the
0062: * <code>openConnection</code> method on a URL.
0063: * <li>The setup parameters and general request properties are manipulated.
0064: * <li>The actual connection to the remote object is made, using the
0065: * <code>connect</code> method.
0066: * <li>The remote object becomes available. The header fields and the contents
0067: * of the remote object can be accessed.
0068: * </ol>
0069: * <p>
0070: * The setup parameters are modified using the following methods:
0071: * <ul>
0072: * <li><code>setAllowUserInteraction</code>
0073: * <li><code>setDoInput</code>
0074: * <li><code>setDoOutput</code>
0075: * <li><code>setIfModifiedSince</code>
0076: * <li><code>setUseCaches</code>
0077: * </ul>
0078: * <p>
0079: * and the general request properties are modified using the method:
0080: * <ul>
0081: * <li><code>setRequestProperty</code>
0082: * </ul>
0083: * <p>
0084: * Default values for the <code>AllowUserInteraction</code> and
0085: * <code>UseCaches</code> parameters can be set using the methods
0086: * <code>setDefaultAllowUserInteraction</code> and
0087: * <code>setDefaultUseCaches</code>.
0088: * <p>
0089: * Each of the above <code>set</code> methods has a corresponding
0090: * <code>get</code> method to retrieve the value of the parameter or
0091: * general request property. The specific parameters and general
0092: * request properties that are applicable are protocol specific.
0093: * <p>
0094: * The following methods are used to access the header fields and
0095: * the contents after the connection is made to the remote object:
0096: * <ul>
0097: * <li><code>getContent</code>
0098: * <li><code>getHeaderField</code>
0099: * <li><code>getInputStream</code>
0100: * <li><code>getOutputStream</code>
0101: * </ul>
0102: * <p>
0103: * Certain header fields are accessed frequently. The methods:
0104: * <ul>
0105: * <li><code>getContentEncoding</code>
0106: * <li><code>getContentLength</code>
0107: * <li><code>getContentType</code>
0108: * <li><code>getDate</code>
0109: * <li><code>getExpiration</code>
0110: * <li><code>getLastModifed</code>
0111: * </ul>
0112: * <p>
0113: * provide convenient access to these fields. The
0114: * <code>getContentType</code> method is used by the
0115: * <code>getContent</code> method to determine the type of the remote
0116: * object; subclasses may find it convenient to override the
0117: * <code>getContentType</code> method.
0118: * <p>
0119: * In the common case, all of the pre-connection parameters and
0120: * general request properties can be ignored: the pre-connection
0121: * parameters and request properties default to sensible values. For
0122: * most clients of this interface, there are only two interesting
0123: * methods: <code>getInputStream</code> and <code>getContent</code>,
0124: * which are mirrored in the <code>URL</code> class by convenience methods.
0125: * <p>
0126: * More information on the request properties and header fields of
0127: * an <code>http</code> connection can be found at:
0128: * <blockquote><pre>
0129: * <a href="http://www.ietf.org/rfc/rfc2068.txt">http://www.ietf.org/rfc/rfc2068.txt</a>
0130: * </pre></blockquote>
0131: *
0132: * Note about <code>fileNameMap</code>: In versions prior to JDK 1.1.6,
0133: * field <code>fileNameMap</code> of <code>URLConnection</code> was public.
0134: * In JDK 1.1.6 and later, <code>fileNameMap</code> is private; accessor
0135: * and mutator methods {@link #getFileNameMap() getFileNameMap} and
0136: * {@link #setFileNameMap(java.net.FileNameMap) setFileNameMap} are added
0137: * to access it. This change is also described on the <a href=
0138: * "http://java.sun.com/products/jdk/1.2/compatibility.html#incompatibilities1.2">
0139: * Compatibility</a> page.
0140: *
0141: * Calling the <tt>close()</tt> methods on the <tt>InputStream</tt> or <tt>OutputStream</tt> of an
0142: * <tt>URLConnection</tt> after a request may free network resources associated with this
0143: * instance, unless particular protocol specifications specify different behaviours
0144: * for it.
0145: *
0146: * @author James Gosling
0147: * @version 1.75, 05/03/00
0148: * @see java.net.URL#openConnection()
0149: * @see java.net.URLConnection#connect()
0150: * @see java.net.URLConnection#getContent()
0151: * @see java.net.URLConnection#getContentEncoding()
0152: * @see java.net.URLConnection#getContentLength()
0153: * @see java.net.URLConnection#getContentType()
0154: * @see java.net.URLConnection#getDate()
0155: * @see java.net.URLConnection#getExpiration()
0156: * @see java.net.URLConnection#getHeaderField(int)
0157: * @see java.net.URLConnection#getHeaderField(java.lang.String)
0158: * @see java.net.URLConnection#getInputStream()
0159: * @see java.net.URLConnection#getLastModified()
0160: * @see java.net.URLConnection#getOutputStream()
0161: * @see java.net.URLConnection#setAllowUserInteraction(boolean)
0162: * @see java.net.URLConnection#setDefaultUseCaches(boolean)
0163: * @see java.net.URLConnection#setDoInput(boolean)
0164: * @see java.net.URLConnection#setDoOutput(boolean)
0165: * @see java.net.URLConnection#setIfModifiedSince(long)
0166: * @see java.net.URLConnection#setRequestProperty(java.lang.String, java.lang.String)
0167: * @see java.net.URLConnection#setUseCaches(boolean)
0168: * @since JDK1.0
0169: */
0170: public abstract class URLConnection {
0171:
0172: /**
0173: * The URL represents the remote object on the World Wide Web to
0174: * which this connection is opened.
0175: * <p>
0176: * The value of this field can be accessed by the
0177: * <code>getURL</code> method.
0178: * <p>
0179: * The default value of this variable is the value of the URL
0180: * argument in the <code>URLConnection</code> constructor.
0181: *
0182: * @see java.net.URLConnection#getURL()
0183: * @see java.net.URLConnection#url
0184: */
0185: protected URL url;
0186:
0187: /**
0188: * This variable is set by the <code>setDoInput</code> method. Its
0189: * value is returned by the <code>getDoInput</code> method.
0190: * <p>
0191: * A URL connection can be used for input and/or output. Setting the
0192: * <code>doInput</code> flag to <code>true</code> indicates that
0193: * the application intends to read data from the URL connection.
0194: * <p>
0195: * The default value of this field is <code>true</code>.
0196: *
0197: * @see java.net.URLConnection#getDoInput()
0198: * @see java.net.URLConnection#setDoInput(boolean)
0199: */
0200: protected boolean doInput = true;
0201:
0202: /**
0203: * This variable is set by the <code>setDoOutput</code> method. Its
0204: * value is returned by the <code>getDoOutput</code> method.
0205: * <p>
0206: * A URL connection can be used for input and/or output. Setting the
0207: * <code>doOutput</code> flag to <code>true</code> indicates
0208: * that the application intends to write data to the URL connection.
0209: * <p>
0210: * The default value of this field is <code>false</code>.
0211: *
0212: * @see java.net.URLConnection#getDoOutput()
0213: * @see java.net.URLConnection#setDoOutput(boolean)
0214: */
0215: protected boolean doOutput = false;
0216:
0217: private static boolean defaultAllowUserInteraction = false;
0218:
0219: /**
0220: * If <code>true</code>, this <code>URL</code> is being examined in
0221: * a context in which it makes sense to allow user interactions such
0222: * as popping up an authentication dialog. If <code>false</code>,
0223: * then no user interaction is allowed.
0224: * <p>
0225: * The value of this field can be set by the
0226: * <code>setAllowUserInteraction</code> method.
0227: * Its value is returned by the
0228: * <code>getAllowUserInteraction</code> method.
0229: * Its default value is the value of the argument in the last invocation
0230: * of the <code>setDefaultAllowUserInteraction</code> method.
0231: *
0232: * @see java.net.URLConnection#getAllowUserInteraction()
0233: * @see java.net.URLConnection#setAllowUserInteraction(boolean)
0234: * @see java.net.URLConnection#setDefaultAllowUserInteraction(boolean)
0235: */
0236: protected boolean allowUserInteraction = defaultAllowUserInteraction;
0237:
0238: private static boolean defaultUseCaches = true;
0239:
0240: /**
0241: * If <code>true</code>, the protocol is allowed to use caching
0242: * whenever it can. If <code>false</code>, the protocol must always
0243: * try to get a fresh copy of the object.
0244: * <p>
0245: * This field is set by the <code>setUseCaches</code> method. Its
0246: * value is returned by the <code>getUseCaches</code> method.
0247: * <p>
0248: * Its default value is the value given in the last invocation of the
0249: * <code>setDefaultUseCaches</code> method.
0250: *
0251: * @see java.net.URLConnection#setUseCaches(boolean)
0252: * @see java.net.URLConnection#getUseCaches()
0253: * @see java.net.URLConnection#setDefaultUseCaches(boolean)
0254: */
0255: protected boolean useCaches = defaultUseCaches;
0256:
0257: /**
0258: * Some protocols support skipping the fetching of the object unless
0259: * the object has been modified more recently than a certain time.
0260: * <p>
0261: * A nonzero value gives a time as the number of milliseconds since
0262: * January 1, 1970, GMT. The object is fetched only if it has been
0263: * modified more recently than that time.
0264: * <p>
0265: * This variable is set by the <code>setIfModifiedSince</code>
0266: * method. Its value is returned by the
0267: * <code>getIfModifiedSince</code> method.
0268: * <p>
0269: * The default value of this field is <code>0</code>, indicating
0270: * that the fetching must always occur.
0271: *
0272: * @see java.net.URLConnection#getIfModifiedSince()
0273: * @see java.net.URLConnection#setIfModifiedSince(long)
0274: */
0275: protected long ifModifiedSince = 0;
0276:
0277: /**
0278: * If <code>false</code>, this connection object has not created a
0279: * communications link to the specified URL. If <code>true</code>,
0280: * the communications link has been established.
0281: */
0282: protected boolean connected = false;
0283:
0284: /**
0285: * @since JDK1.1
0286: */
0287: private static FileNameMap fileNameMap;
0288:
0289: /**
0290: * @since 1.2.2
0291: */
0292: private static boolean fileNameMapLoaded = false;
0293:
0294: /**
0295: * Loads filename map (a mimetable) from a data file. It will
0296: * first try to load the user-specific table, defined
0297: * by "content.types.user.table" property. If that fails,
0298: * it tries to load the default built-in table at
0299: * lib/content-types.properties under java home.
0300: *
0301: * @return the FileNameMap
0302: * @since 1.2
0303: * @see #setFileNameMap(java.net.FileNameMap)
0304: */
0305: public static synchronized FileNameMap getFileNameMap() {
0306: if ((fileNameMap == null) && !fileNameMapLoaded) {
0307: fileNameMap = sun.net.www.MimeTable.loadTable();
0308: fileNameMapLoaded = true;
0309: }
0310:
0311: return new FileNameMap() {
0312: private FileNameMap map = fileNameMap;
0313:
0314: public String getContentTypeFor(String fileName) {
0315: return map.getContentTypeFor(fileName);
0316: }
0317: };
0318: }
0319:
0320: /**
0321: * Sets the FileNameMap.
0322: * <p>
0323: * If there is a security manager, this method first calls
0324: * the security manager's <code>checkSetFactory</code> method
0325: * to ensure the operation is allowed.
0326: * This could result in a SecurityException.
0327: *
0328: * @param map the FileNameMap to be set
0329: * @exception SecurityException if a security manager exists and its
0330: * <code>checkSetFactory</code> method doesn't allow the operation.
0331: * @see SecurityManager#checkSetFactory
0332: * @see #getFileNameMap()
0333: * @since 1.2
0334: */
0335: public static void setFileNameMap(FileNameMap map) {
0336: SecurityManager sm = System.getSecurityManager();
0337: if (sm != null)
0338: sm.checkSetFactory();
0339: fileNameMap = map;
0340: }
0341:
0342: /**
0343: * Opens a communications link to the resource referenced by this
0344: * URL, if such a connection has not already been established.
0345: * <p>
0346: * If the <code>connect</code> method is called when the connection
0347: * has already been opened (indicated by the <code>connected</code>
0348: * field having the value <code>true</code>), the call is ignored.
0349: * <p>
0350: * URLConnection objects go through two phases: first they are
0351: * created, then they are connected. After being created, and
0352: * before being connected, various options can be specified
0353: * (e.g., doInput and UseCaches). After connecting, it is an
0354: * error to try to set them. Operations that depend on being
0355: * connected, like getContentLength, will implicitly perform the
0356: * connection, if necessary.
0357: *
0358: * @exception IOException if an I/O error occurs while opening the
0359: * connection.
0360: * @see java.net.URLConnection#connected */
0361: abstract public void connect() throws IOException;
0362:
0363: /**
0364: * Constructs a URL connection to the specified URL. A connection to
0365: * the object referenced by the URL is not created.
0366: *
0367: * @param url the specified URL.
0368: */
0369: protected URLConnection(URL url) {
0370: this .url = url;
0371: }
0372:
0373: /**
0374: * Returns the value of this <code>URLConnection</code>'s <code>URL</code>
0375: * field.
0376: *
0377: * @return the value of this <code>URLConnection</code>'s <code>URL</code>
0378: * field.
0379: * @see java.net.URLConnection#url
0380: */
0381: public URL getURL() {
0382: return url;
0383: }
0384:
0385: /**
0386: * Returns the value of the <code>content-length</code> header field.
0387: *
0388: * @return the content length of the resource that this connection's URL
0389: * references, or <code>-1</code> if the content length is
0390: * not known.
0391: */
0392: public int getContentLength() {
0393: return getHeaderFieldInt("content-length", -1);
0394: }
0395:
0396: /**
0397: * Returns the value of the <code>content-type</code> header field.
0398: *
0399: * @return the content type of the resource that the URL references,
0400: * or <code>null</code> if not known.
0401: * @see java.net.URLConnection#getHeaderField(java.lang.String)
0402: */
0403: public String getContentType() {
0404: return getHeaderField("content-type");
0405: }
0406:
0407: /**
0408: * Returns the value of the <code>content-encoding</code> header field.
0409: *
0410: * @return the content encoding of the resource that the URL references,
0411: * or <code>null</code> if not known.
0412: * @see java.net.URLConnection#getHeaderField(java.lang.String)
0413: */
0414: public String getContentEncoding() {
0415: return getHeaderField("content-encoding");
0416: }
0417:
0418: /**
0419: * Returns the value of the <code>expires</code> header field.
0420: *
0421: * @return the expiration date of the resource that this URL references,
0422: * or 0 if not known. The value is the number of milliseconds since
0423: * January 1, 1970 GMT.
0424: * @see java.net.URLConnection#getHeaderField(java.lang.String)
0425: */
0426: public long getExpiration() {
0427: return getHeaderFieldDate("expires", 0);
0428: }
0429:
0430: /**
0431: * Returns the value of the <code>date</code> header field.
0432: *
0433: * @return the sending date of the resource that the URL references,
0434: * or <code>0</code> if not known. The value returned is the
0435: * number of milliseconds since January 1, 1970 GMT.
0436: * @see java.net.URLConnection#getHeaderField(java.lang.String)
0437: */
0438: public long getDate() {
0439: return getHeaderFieldDate("date", 0);
0440: }
0441:
0442: /**
0443: * Returns the value of the <code>last-modified</code> header field.
0444: * The result is the number of milliseconds since January 1, 1970 GMT.
0445: *
0446: * @return the date the resource referenced by this
0447: * <code>URLConnection</code> was last modified, or 0 if not known.
0448: * @see java.net.URLConnection#getHeaderField(java.lang.String)
0449: */
0450: public long getLastModified() {
0451: return getHeaderFieldDate("last-modified", 0);
0452: }
0453:
0454: /**
0455: * Returns the value of the named header field.
0456: * <p>
0457: * If called on a connection that sets the same header multiple times
0458: * with possibly different values, only the last value is returned.
0459: *
0460: *
0461: * @param name the name of a header field.
0462: * @return the value of the named header field, or <code>null</code>
0463: * if there is no such field in the header.
0464: */
0465: public String getHeaderField(String name) {
0466: return null;
0467: }
0468:
0469: /**
0470: * Returns an unmodifiable Map of the header fields.
0471: * The Map keys are Strings that represent the
0472: * response-header field names. Each Map value is an
0473: * unmodifiable List of Strings that represents
0474: * the corresponding field values.
0475: *
0476: * @return a Map of header fields
0477: * @since 1.4
0478: */
0479: public Map getHeaderFields() {
0480: return Collections.EMPTY_MAP;
0481: }
0482:
0483: /**
0484: * Returns the value of the named field parsed as a number.
0485: * <p>
0486: * This form of <code>getHeaderField</code> exists because some
0487: * connection types (e.g., <code>http-ng</code>) have pre-parsed
0488: * headers. Classes for that connection type can override this method
0489: * and short-circuit the parsing.
0490: *
0491: * @param name the name of the header field.
0492: * @param Default the default value.
0493: * @return the value of the named field, parsed as an integer. The
0494: * <code>Default</code> value is returned if the field is
0495: * missing or malformed.
0496: */
0497: public int getHeaderFieldInt(String name, int Default) {
0498: String value = getHeaderField(name);
0499: try {
0500: return Integer.parseInt(value);
0501: } catch (Exception e) {
0502: }
0503: return Default;
0504: }
0505:
0506: /**
0507: * Returns the value of the named field parsed as date.
0508: * The result is the number of milliseconds since January 1, 1970 GMT
0509: * represented by the named field.
0510: * <p>
0511: * This form of <code>getHeaderField</code> exists because some
0512: * connection types (e.g., <code>http-ng</code>) have pre-parsed
0513: * headers. Classes for that connection type can override this method
0514: * and short-circuit the parsing.
0515: *
0516: * @param name the name of the header field.
0517: * @param Default a default value.
0518: * @return the value of the field, parsed as a date. The value of the
0519: * <code>Default</code> argument is returned if the field is
0520: * missing or malformed.
0521: */
0522: public long getHeaderFieldDate(String name, long Default) {
0523: try {
0524: DateFormat df = DateFormat.getDateInstance();
0525:
0526: return df.parse(getHeaderField(name)).getTime();
0527: } catch (Throwable t) {
0528: }
0529: return Default;
0530: }
0531:
0532: /**
0533: * Returns the key for the <code>n</code><sup>th</sup> header field.
0534: * It returns <code>null</code> if there are fewer than <code>n+1</code> fields.
0535: *
0536: * @param n an index, where n>=0
0537: * @return the key for the <code>n</code><sup>th</sup> header field,
0538: * or <code>null</code> if there are fewer than <code>n+1</code>
0539: * fields.
0540: */
0541: public String getHeaderFieldKey(int n) {
0542: return null;
0543: }
0544:
0545: /**
0546: * Returns the value for the <code>n</code><sup>th</sup> header field.
0547: * It returns <code>null</code> if there are fewer than
0548: * <code>n+1</code>fields.
0549: * <p>
0550: * This method can be used in conjunction with the
0551: * {@link #getHeaderFieldKey(int) getHeaderFieldKey} method to iterate through all
0552: * the headers in the message.
0553: *
0554: * @param n an index, where n>=0
0555: * @return the value of the <code>n</code><sup>th</sup> header field
0556: * or <code>null</code> if there are fewer than <code>n+1</code> fields
0557: * @see java.net.URLConnection#getHeaderFieldKey(int)
0558: */
0559: public String getHeaderField(int n) {
0560: return null;
0561: }
0562:
0563: /**
0564: * Retrieves the contents of this URL connection.
0565: * <p>
0566: * This method first determines the content type of the object by
0567: * calling the <code>getContentType</code> method. If this is
0568: * the first time that the application has seen that specific content
0569: * type, a content handler for that content type is created:
0570: * <ol>
0571: * <li>If the application has set up a content handler factory instance
0572: * using the <code>setContentHandlerFactory</code> method, the
0573: * <code>createContentHandler</code> method of that instance is called
0574: * with the content type as an argument; the result is a content
0575: * handler for that content type.
0576: * <li>If no content handler factory has yet been set up, or if the
0577: * factory's <code>createContentHandler</code> method returns
0578: * <code>null</code>, then the application loads the class named:
0579: * <blockquote><pre>
0580: * sun.net.www.content.<<i>contentType</i>>
0581: * </pre></blockquote>
0582: * where <<i>contentType</i>> is formed by taking the
0583: * content-type string, replacing all slash characters with a
0584: * <code>period</code> ('.'), and all other non-alphanumeric characters
0585: * with the underscore character '<code>_</code>'. The alphanumeric
0586: * characters are specifically the 26 uppercase ASCII letters
0587: * '<code>A</code>' through '<code>Z</code>', the 26 lowercase ASCII
0588: * letters '<code>a</code>' through '<code>z</code>', and the 10 ASCII
0589: * digits '<code>0</code>' through '<code>9</code>'. If the specified
0590: * class does not exist, or is not a subclass of
0591: * <code>ContentHandler</code>, then an
0592: * <code>UnknownServiceException</code> is thrown.
0593: * </ol>
0594: *
0595: * @return the object fetched. The <code>instanceof</code> operator
0596: * should be used to determine the specific kind of object
0597: * returned.
0598: * @exception IOException if an I/O error occurs while
0599: * getting the content.
0600: * @exception UnknownServiceException if the protocol does not support
0601: * the content type.
0602: * @see java.net.ContentHandlerFactory#createContentHandler(java.lang.String)
0603: * @see java.net.URLConnection#getContentType()
0604: * @see java.net.URLConnection#setContentHandlerFactory(java.net.ContentHandlerFactory)
0605: */
0606: public Object getContent() throws IOException {
0607: // Must call getInputStream before GetHeaderField gets called
0608: // so that FileNotFoundException has a chance to be thrown up
0609: // from here without being caught.
0610: getInputStream();
0611: return getContentHandler().getContent(this );
0612: }
0613:
0614: /**
0615: * Retrieves the contents of this URL connection.
0616: *
0617: * @param classes the <code>Class</code> array
0618: * indicating the requested types
0619: * @return the object fetched that is the first match of the type
0620: * specified in the classes array. null if none of
0621: * the requested types are supported.
0622: * The <code>instanceof</code> operator should be used to
0623: * determine the specific kind of object returned.
0624: * @exception IOException if an I/O error occurs while
0625: * getting the content.
0626: * @exception UnknownServiceException if the protocol does not support
0627: * the content type.
0628: * @see java.net.URLConnection#getContent()
0629: * @see java.net.ContentHandlerFactory#createContentHandler(java.lang.String)
0630: * @see java.net.URLConnection#getContent(java.lang.Class[])
0631: * @see java.net.URLConnection#setContentHandlerFactory(java.net.ContentHandlerFactory)
0632: */
0633: public Object getContent(Class[] classes) throws IOException {
0634: // Must call getInputStream before GetHeaderField gets called
0635: // so that FileNotFoundException has a chance to be thrown up
0636: // from here without being caught.
0637: getInputStream();
0638: return getContentHandler().getContent(this , classes);
0639: }
0640:
0641: /**
0642: * Returns a permission object representing the permission
0643: * necessary to make the connection represented by this
0644: * object. This method returns null if no permission is
0645: * required to make the connection. By default, this method
0646: * returns <code>java.security.AllPermission</code>. Subclasses
0647: * should override this method and return the permission
0648: * that best represents the permission required to make a
0649: * a connection to the URL. For example, a <code>URLConnection</code>
0650: * representing a <code>file:</code> URL would return a
0651: * <code>java.io.FilePermission</code> object.
0652: *
0653: * <p>The permission returned may dependent upon the state of the
0654: * connection. For example, the permission before connecting may be
0655: * different from that after connecting. For example, an HTTP
0656: * sever, say foo.com, may redirect the connection to a different
0657: * host, say bar.com. Before connecting the permission returned by
0658: * the connection will represent the permission needed to connect
0659: * to foo.com, while the permission returned after connecting will
0660: * be to bar.com.
0661: *
0662: * <p>Permissions are generally used for two purposes: to protect
0663: * caches of objects obtained through URLConnections, and to check
0664: * the right of a recipient to learn about a particular URL. In
0665: * the first case, the permission should be obtained
0666: * <em>after</em> the object has been obtained. For example, in an
0667: * HTTP connection, this will represent the permission to connect
0668: * to the host from which the data was ultimately fetched. In the
0669: * second case, the permission should be obtained and tested
0670: * <em>before</em> connecting.
0671: *
0672: * @return the permission object representing the permission
0673: * necessary to make the connection represented by this
0674: * URLConnection.
0675: *
0676: * @exception IOException if the computation of the permission
0677: * requires network or file I/O and an exception occurs while
0678: * computing it.
0679: */
0680: public Permission getPermission() throws IOException {
0681: return SecurityConstants.ALL_PERMISSION;
0682: }
0683:
0684: /**
0685: * Returns an input stream that reads from this open connection.
0686: *
0687: * @return an input stream that reads from this open connection.
0688: * @exception IOException if an I/O error occurs while
0689: * creating the input stream.
0690: * @exception UnknownServiceException if the protocol does not support
0691: * input.
0692: */
0693: public InputStream getInputStream() throws IOException {
0694: throw new UnknownServiceException(
0695: "protocol doesn't support input");
0696: }
0697:
0698: /**
0699: * Returns an output stream that writes to this connection.
0700: *
0701: * @return an output stream that writes to this connection.
0702: * @exception IOException if an I/O error occurs while
0703: * creating the output stream.
0704: * @exception UnknownServiceException if the protocol does not support
0705: * output.
0706: */
0707: public OutputStream getOutputStream() throws IOException {
0708: throw new UnknownServiceException(
0709: "protocol doesn't support output");
0710: }
0711:
0712: /**
0713: * Returns a <code>String</code> representation of this URL connection.
0714: *
0715: * @return a string representation of this <code>URLConnection</code>.
0716: */
0717: public String toString() {
0718: return this .getClass().getName() + ":" + url;
0719: }
0720:
0721: /**
0722: * Sets the value of the <code>doInput</code> field for this
0723: * <code>URLConnection</code> to the specified value.
0724: * <p>
0725: * A URL connection can be used for input and/or output. Set the DoInput
0726: * flag to true if you intend to use the URL connection for input,
0727: * false if not. The default is true.
0728: *
0729: * @param doinput the new value.
0730: * @throws IllegalStateException if already connected
0731: * @see java.net.URLConnection#doInput
0732: * @see #getDoInput()
0733: */
0734: public void setDoInput(boolean doinput) {
0735: if (connected)
0736: throw new IllegalStateException("Already connected");
0737: doInput = doinput;
0738: }
0739:
0740: /**
0741: * Returns the value of this <code>URLConnection</code>'s
0742: * <code>doInput</code> flag.
0743: *
0744: * @return the value of this <code>URLConnection</code>'s
0745: * <code>doInput</code> flag.
0746: * @see #setDoInput(boolean)
0747: */
0748: public boolean getDoInput() {
0749: return doInput;
0750: }
0751:
0752: /**
0753: * Sets the value of the <code>doOutput</code> field for this
0754: * <code>URLConnection</code> to the specified value.
0755: * <p>
0756: * A URL connection can be used for input and/or output. Set the DoOutput
0757: * flag to true if you intend to use the URL connection for output,
0758: * false if not. The default is false.
0759: *
0760: * @param dooutput the new value.
0761: * @throws IllegalStateException if already connected
0762: * @see #getDoOutput()
0763: */
0764: public void setDoOutput(boolean dooutput) {
0765: if (connected)
0766: throw new IllegalStateException("Already connected");
0767: doOutput = dooutput;
0768: }
0769:
0770: /**
0771: * Returns the value of this <code>URLConnection</code>'s
0772: * <code>doOutput</code> flag.
0773: *
0774: * @return the value of this <code>URLConnection</code>'s
0775: * <code>doOutput</code> flag.
0776: * @see #setDoOutput(boolean)
0777: */
0778: public boolean getDoOutput() {
0779: return doOutput;
0780: }
0781:
0782: /**
0783: * Set the value of the <code>allowUserInteraction</code> field of
0784: * this <code>URLConnection</code>.
0785: *
0786: * @param allowuserinteraction the new value.
0787: * @throws IllegalStateException if already connected
0788: * @see #getAllowUserInteraction()
0789: */
0790: public void setAllowUserInteraction(boolean allowuserinteraction) {
0791: if (connected)
0792: throw new IllegalStateException("Already connected");
0793: allowUserInteraction = allowuserinteraction;
0794: }
0795:
0796: /**
0797: * Returns the value of the <code>allowUserInteraction</code> field for
0798: * this object.
0799: *
0800: * @return the value of the <code>allowUserInteraction</code> field for
0801: * this object.
0802: * @see #setAllowUserInteraction(boolean)
0803: */
0804: public boolean getAllowUserInteraction() {
0805: return allowUserInteraction;
0806: }
0807:
0808: /**
0809: * Sets the default value of the
0810: * <code>allowUserInteraction</code> field for all future
0811: * <code>URLConnection</code> objects to the specified value.
0812: *
0813: * @param defaultallowuserinteraction the new value.
0814: * @see #getDefaultAllowUserInteraction()
0815: */
0816: public static void setDefaultAllowUserInteraction(
0817: boolean defaultallowuserinteraction) {
0818: defaultAllowUserInteraction = defaultallowuserinteraction;
0819: }
0820:
0821: /**
0822: * Returns the default value of the <code>allowUserInteraction</code>
0823: * field.
0824: * <p>
0825: * Ths default is "sticky", being a part of the static state of all
0826: * URLConnections. This flag applies to the next, and all following
0827: * URLConnections that are created.
0828: *
0829: * @return the default value of the <code>allowUserInteraction</code>
0830: * field.
0831: * @see #setDefaultAllowUserInteraction(boolean)
0832: */
0833: public static boolean getDefaultAllowUserInteraction() {
0834: return defaultAllowUserInteraction;
0835: }
0836:
0837: /**
0838: * Sets the value of the <code>useCaches</code> field of this
0839: * <code>URLConnection</code> to the specified value.
0840: * <p>
0841: * Some protocols do caching of documents. Occasionally, it is important
0842: * to be able to "tunnel through" and ignore the caches (e.g., the
0843: * "reload" button in a browser). If the UseCaches flag on a connection
0844: * is true, the connection is allowed to use whatever caches it can.
0845: * If false, caches are to be ignored.
0846: * The default value comes from DefaultUseCaches, which defaults to
0847: * true.
0848: *
0849: * @param usecaches a <code>boolean</code> indicating whether
0850: * or not to allow caching
0851: * @throws IllegalStateException if already connected
0852: * @see #getUseCaches()
0853: */
0854: public void setUseCaches(boolean usecaches) {
0855: if (connected)
0856: throw new IllegalStateException("Already connected");
0857: useCaches = usecaches;
0858: }
0859:
0860: /**
0861: * Returns the value of this <code>URLConnection</code>'s
0862: * <code>useCaches</code> field.
0863: *
0864: * @return the value of this <code>URLConnection</code>'s
0865: * <code>useCaches</code> field.
0866: * @see #setUseCaches(boolean)
0867: */
0868: public boolean getUseCaches() {
0869: return useCaches;
0870: }
0871:
0872: /**
0873: * Sets the value of the <code>ifModifiedSince</code> field of
0874: * this <code>URLConnection</code> to the specified value.
0875: *
0876: * @param ifmodifiedsince the new value.
0877: * @throws IllegalStateException if already connected
0878: * @see #getIfModifiedSince()
0879: */
0880: public void setIfModifiedSince(long ifmodifiedsince) {
0881: if (connected)
0882: throw new IllegalStateException("Already connected");
0883: ifModifiedSince = ifmodifiedsince;
0884: }
0885:
0886: /**
0887: * Returns the value of this object's <code>ifModifiedSince</code> field.
0888: *
0889: * @return the value of this object's <code>ifModifiedSince</code> field.
0890: * @see #setIfModifiedSince(long)
0891: */
0892: public long getIfModifiedSince() {
0893: return ifModifiedSince;
0894: }
0895:
0896: /**
0897: * Returns the default value of a <code>URLConnection</code>'s
0898: * <code>useCaches</code> flag.
0899: * <p>
0900: * Ths default is "sticky", being a part of the static state of all
0901: * URLConnections. This flag applies to the next, and all following
0902: * URLConnections that are created.
0903: *
0904: * @return the default value of a <code>URLConnection</code>'s
0905: * <code>useCaches</code> flag.
0906: * @see #setDefaultUseCaches(boolean)
0907: */
0908: public boolean getDefaultUseCaches() {
0909: return defaultUseCaches;
0910: }
0911:
0912: /**
0913: * Sets the default value of the <code>useCaches</code> field to the
0914: * specified value.
0915: *
0916: * @param defaultusecaches the new value.
0917: * @see #getDefaultUseCaches()
0918: */
0919: public void setDefaultUseCaches(boolean defaultusecaches) {
0920: defaultUseCaches = defaultusecaches;
0921: }
0922:
0923: /**
0924: * Sets the general request property. If a property with the key already
0925: * exists, overwrite its value with the new value.
0926: *
0927: * <p> NOTE: HTTP requires all request properties which can
0928: * legally have multiple instances with the same key
0929: * to use a comma-seperated list syntax which enables multiple
0930: * properties to be appended into a single property.
0931: *
0932: * @param key the keyword by which the request is known
0933: * (e.g., "<code>accept</code>").
0934: * @param value the value associated with it.
0935: * @throws IllegalStateException if already connected
0936: * @throws NullPointerException if key is <CODE>null</CODE>
0937: * @see #getRequestProperty(java.lang.String)
0938: */
0939: public void setRequestProperty(String key, String value) {
0940: if (connected)
0941: throw new IllegalStateException("Already connected");
0942: if (key == null)
0943: throw new NullPointerException("key is null");
0944: }
0945:
0946: /**
0947: * Adds a general request property specified by a
0948: * key-value pair. This method will not overwrite
0949: * existing values associated with the same key.
0950: *
0951: * @param key the keyword by which the request is known
0952: * (e.g., "<code>accept</code>").
0953: * @param value the value associated with it.
0954: * @throws IllegalStateException if already connected
0955: * @throws NullPointerException if key is null
0956: * @see #getRequestProperties(java.lang.String)
0957: * @since 1.4
0958: */
0959: public void addRequestProperty(String key, String value) {
0960: if (connected)
0961: throw new IllegalStateException("Already connected");
0962: if (key == null)
0963: throw new NullPointerException("key is null");
0964: }
0965:
0966: /**
0967: * Returns the value of the named general request property for this
0968: * connection.
0969: *
0970: * @param key the keyword by which the request is known (e.g., "accept").
0971: * @return the value of the named general request property for this
0972: * connection. If key is null, then null is returned.
0973: * @throws IllegalStateException if already connected
0974: * @see #setRequestProperty(java.lang.String, java.lang.String)
0975: */
0976: public String getRequestProperty(String key) {
0977: if (connected)
0978: throw new IllegalStateException("Already connected");
0979: return null;
0980: }
0981:
0982: /**
0983: * Returns an unmodifiable Map of general request
0984: * properties for this connection. The Map keys
0985: * are Strings that represent the request-header
0986: * field names. Each Map value is a unmodifiable List
0987: * of Strings that represents the corresponding
0988: * field values.
0989: *
0990: * @return a Map of the general request properties for this connection.
0991: * @throws IllegalStateException if already connected
0992: * @since 1.4
0993: */
0994: public Map getRequestProperties() {
0995: if (connected)
0996: throw new IllegalStateException("Already connected");
0997: return Collections.EMPTY_MAP;
0998: }
0999:
1000: /**
1001: * Sets the default value of a general request property. When a
1002: * <code>URLConnection</code> is created, it is initialized with
1003: * these properties.
1004: *
1005: * param key the keyword by which the request is known
1006: * (e.g., "<code>accept</code>").
1007: * param value the value associated with the key.
1008: *
1009: * see java.net.URLConnection#setRequestProperty(java.lang.String,java.lang.String)
1010: *
1011: * deprecated The instance specific setRequestProperty method
1012: * should be used after an appropriate instance of URLConnection
1013: * is obtained.
1014: *
1015: * see #getDefaultRequestProperty(java.lang.String)
1016: *
1017: public static void setDefaultRequestProperty(String key, String value) {
1018: }
1019: */
1020:
1021: /**
1022: * Returns the value of the default request property. Default request
1023: * properties are set for every connection.
1024: *
1025: * param key the keyword by which the request is known (e.g., "accept").
1026: * return the value of the default request property
1027: * for the specified key.
1028: *
1029: * see java.net.URLConnection#getRequestProperty(java.lang.String)
1030: *
1031: * deprecated The instance specific getRequestProperty method
1032: * should be used after an appropriate instance of URLConnection
1033: * is obtained.
1034: *
1035: * see #setDefaultRequestProperty(java.lang.String, java.lang.String)
1036: *
1037: public static String getDefaultRequestProperty(String key) {
1038: return null;
1039: }
1040: */
1041:
1042: /**
1043: * The ContentHandler factory.
1044: */
1045: static ContentHandlerFactory factory;
1046:
1047: /**
1048: * Sets the <code>ContentHandlerFactory</code> of an
1049: * application. It can be called at most once by an application.
1050: * <p>
1051: * The <code>ContentHandlerFactory</code> instance is used to
1052: * construct a content handler from a content type
1053: * <p>
1054: * If there is a security manager, this method first calls
1055: * the security manager's <code>checkSetFactory</code> method
1056: * to ensure the operation is allowed.
1057: * This could result in a SecurityException.
1058: *
1059: * @param fac the desired factory.
1060: * @exception Error if the factory has already been defined.
1061: * @exception SecurityException if a security manager exists and its
1062: * <code>checkSetFactory</code> method doesn't allow the operation.
1063: * @see java.net.ContentHandlerFactory
1064: * @see java.net.URLConnection#getContent()
1065: * @see SecurityManager#checkSetFactory
1066: */
1067: public static synchronized void setContentHandlerFactory(
1068: ContentHandlerFactory fac) {
1069: if (factory != null) {
1070: throw new Error("factory already defined");
1071: }
1072: SecurityManager security = System.getSecurityManager();
1073: if (security != null) {
1074: security.checkSetFactory();
1075: }
1076: factory = fac;
1077: }
1078:
1079: private static Hashtable handlers = new Hashtable();
1080: private static final ContentHandler UnknownContentHandlerP = new UnknownContentHandler();
1081:
1082: /**
1083: * Gets the Content Handler appropriate for this connection.
1084: * @param connection the connection to use.
1085: */
1086: synchronized ContentHandler getContentHandler()
1087: throws UnknownServiceException {
1088: String contentType = stripOffParameters(getContentType());
1089: ContentHandler handler = null;
1090: if (contentType == null)
1091: throw new UnknownServiceException("no content-type");
1092: try {
1093: handler = (ContentHandler) handlers.get(contentType);
1094: if (handler != null)
1095: return handler;
1096: } catch (Exception e) {
1097: }
1098:
1099: if (factory != null)
1100: handler = factory.createContentHandler(contentType);
1101: if (handler == null) {
1102: try {
1103: handler = lookupContentHandlerClassFor(contentType);
1104: } catch (Exception e) {
1105: e.printStackTrace();
1106: handler = UnknownContentHandlerP;
1107: }
1108: handlers.put(contentType, handler);
1109: }
1110: return handler;
1111: }
1112:
1113: /*
1114: * Media types are in the format: type/subtype*(; parameter).
1115: * For looking up the content handler, we should ignore those
1116: * parameters.
1117: */
1118: private String stripOffParameters(String contentType) {
1119: if (contentType == null)
1120: return null;
1121: int index = contentType.indexOf(';');
1122:
1123: if (index > 0)
1124: return contentType.substring(0, index);
1125: else
1126: return contentType;
1127: }
1128:
1129: private static final String contentClassPrefix = "sun.net.www.content";
1130: private static final String contentPathProp = "java.content.handler.pkgs";
1131:
1132: /**
1133: * Looks for a content handler in a user-defineable set of places.
1134: * By default it looks in sun.net.www.content, but users can define a
1135: * vertical-bar delimited set of class prefixes to search through in
1136: * addition by defining the java.content.handler.pkgs property.
1137: * The class name must be of the form:
1138: * <pre>
1139: * {package-prefix}.{major}.{minor}
1140: * e.g.
1141: * YoyoDyne.experimental.text.plain
1142: * </pre>
1143: */
1144: private ContentHandler lookupContentHandlerClassFor(
1145: String contentType) throws InstantiationException,
1146: IllegalAccessException, ClassNotFoundException {
1147: String contentHandlerClassName = typeToPackageName(contentType);
1148:
1149: String contentHandlerPkgPrefixes = getContentHandlerPkgPrefixes();
1150:
1151: StringTokenizer packagePrefixIter = new StringTokenizer(
1152: contentHandlerPkgPrefixes, "|");
1153:
1154: while (packagePrefixIter.hasMoreTokens()) {
1155: String packagePrefix = packagePrefixIter.nextToken().trim();
1156:
1157: try {
1158: String clsName = packagePrefix + "."
1159: + contentHandlerClassName;
1160: Class cls = null;
1161: try {
1162: cls = Class.forName(clsName);
1163: } catch (ClassNotFoundException e) {
1164: ClassLoader cl = ClassLoader.getSystemClassLoader();
1165: if (cl != null) {
1166: cls = cl.loadClass(clsName);
1167: }
1168: }
1169: if (cls != null) {
1170: ContentHandler handler = (ContentHandler) cls
1171: .newInstance();
1172: return handler;
1173: }
1174: } catch (Exception e) {
1175: }
1176: }
1177:
1178: return UnknownContentHandlerP;
1179: }
1180:
1181: /**
1182: * Utility function to map a MIME content type into an equivalent
1183: * pair of class name components. For example: "text/html" would
1184: * be returned as "text.html"
1185: */
1186: private String typeToPackageName(String contentType) {
1187: // make sure we canonicalize the class name: all lower case
1188: contentType = contentType.toLowerCase();
1189: int len = contentType.length();
1190: char nm[] = new char[len];
1191: contentType.getChars(0, len, nm, 0);
1192: for (int i = 0; i < len; i++) {
1193: char c = nm[i];
1194: if (c == '/') {
1195: nm[i] = '.';
1196: } else if (!('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c
1197: && c <= '9')) {
1198: nm[i] = '_';
1199: }
1200: }
1201: return new String(nm);
1202: }
1203:
1204: /**
1205: * Returns a vertical bar separated list of package prefixes for potential
1206: * content handlers. Tries to get the java.content.handler.pkgs property
1207: * to use as a set of package prefixes to search. Whether or not
1208: * that property has been defined, the sun.net.www.content is always
1209: * the last one on the returned package list.
1210: */
1211: private String getContentHandlerPkgPrefixes() {
1212: String packagePrefixList = (String) AccessController
1213: .doPrivileged(new sun.security.action.GetPropertyAction(
1214: contentPathProp, ""));
1215:
1216: if (packagePrefixList != "") {
1217: packagePrefixList += "|";
1218: }
1219:
1220: return packagePrefixList + contentClassPrefix;
1221: }
1222:
1223: /**
1224: * Tries to determine the content type of an object, based
1225: * on the specified "file" component of a URL.
1226: * This is a convenience method that can be used by
1227: * subclasses that override the <code>getContentType</code> method.
1228: *
1229: * @param fname a filename.
1230: * @return a guess as to what the content type of the object is,
1231: * based upon its file name.
1232: * @see java.net.URLConnection#getContentType()
1233: */
1234: public static String guessContentTypeFromName(String fname) {
1235: return getFileNameMap().getContentTypeFor(fname);
1236: }
1237:
1238: /**
1239: * Tries to determine the type of an input stream based on the
1240: * characters at the beginning of the input stream. This method can
1241: * be used by subclasses that override the
1242: * <code>getContentType</code> method.
1243: * <p>
1244: * Ideally, this routine would not be needed. But many
1245: * <code>http</code> servers return the incorrect content type; in
1246: * addition, there are many nonstandard extensions. Direct inspection
1247: * of the bytes to determine the content type is often more accurate
1248: * than believing the content type claimed by the <code>http</code> server.
1249: *
1250: * @param is an input stream that supports marks.
1251: * @return a guess at the content type, or <code>null</code> if none
1252: * can be determined.
1253: * @exception IOException if an I/O error occurs while reading the
1254: * input stream.
1255: * @see java.io.InputStream#mark(int)
1256: * @see java.io.InputStream#markSupported()
1257: * @see java.net.URLConnection#getContentType()
1258: */
1259: static public String guessContentTypeFromStream(InputStream is)
1260: throws IOException {
1261: // If we can't read ahead safely, just give up on guessing
1262: if (!is.markSupported())
1263: return null;
1264:
1265: is.mark(12);
1266: int c1 = is.read();
1267: int c2 = is.read();
1268: int c3 = is.read();
1269: int c4 = is.read();
1270: int c5 = is.read();
1271: int c6 = is.read();
1272: int c7 = is.read();
1273: int c8 = is.read();
1274: int c9 = is.read();
1275: int c10 = is.read();
1276: int c11 = is.read();
1277: is.reset();
1278:
1279: if (c1 == 0xCA && c2 == 0xFE && c3 == 0xBA && c4 == 0xBE) {
1280: return "application/java-vm";
1281: }
1282:
1283: if (c1 == 0xAC && c2 == 0xED) {
1284: // next two bytes are version number, currently 0x00 0x05
1285: return "application/x-java-serialized-object";
1286: }
1287:
1288: if (c1 == '<') {
1289: if (c2 == '!'
1290: || ((c2 == 'h'
1291: && (c3 == 't' && c4 == 'm' && c5 == 'l' || c3 == 'e'
1292: && c4 == 'a' && c5 == 'd') || (c2 == 'b'
1293: && c3 == 'o' && c4 == 'd' && c5 == 'y')))
1294: || ((c2 == 'H'
1295: && (c3 == 'T' && c4 == 'M' && c5 == 'L' || c3 == 'E'
1296: && c4 == 'A' && c5 == 'D') || (c2 == 'B'
1297: && c3 == 'O' && c4 == 'D' && c5 == 'Y')))) {
1298: return "text/html";
1299: }
1300:
1301: if (c2 == '?' && c3 == 'x' && c4 == 'm' && c5 == 'l'
1302: && c6 == ' ') {
1303: return "application/xml";
1304: }
1305: }
1306:
1307: // big and little endian UTF-16 encodings, with byte order mark
1308: if (c1 == 0xfe && c2 == 0xff) {
1309: if (c3 == 0 && c4 == '<' && c5 == 0 && c6 == '?' && c7 == 0
1310: && c8 == 'x') {
1311: return "application/xml";
1312: }
1313: }
1314:
1315: if (c1 == 0xff && c2 == 0xfe) {
1316: if (c3 == '<' && c4 == 0 && c5 == '?' && c6 == 0
1317: && c7 == 'x' && c8 == 0) {
1318: return "application/xml";
1319: }
1320: }
1321:
1322: if (c1 == 'G' && c2 == 'I' && c3 == 'F' && c4 == '8') {
1323: return "image/gif";
1324: }
1325:
1326: if (c1 == '#' && c2 == 'd' && c3 == 'e' && c4 == 'f') {
1327: return "image/x-bitmap";
1328: }
1329:
1330: if (c1 == '!' && c2 == ' ' && c3 == 'X' && c4 == 'P'
1331: && c5 == 'M' && c6 == '2') {
1332: return "image/x-pixmap";
1333: }
1334:
1335: if (c1 == 137 && c2 == 80 && c3 == 78 && c4 == 71 && c5 == 13
1336: && c6 == 10 && c7 == 26 && c8 == 10) {
1337: return "image/png";
1338: }
1339:
1340: if (c1 == 0xFF && c2 == 0xD8 && c3 == 0xFF) {
1341: if (c4 == 0xE0) {
1342: return "image/jpeg";
1343: }
1344:
1345: /**
1346: * File format used by digital cameras to store images.
1347: * Exif Format can be read by any application supporting
1348: * JPEG. Exif Spec can be found at:
1349: * http://www.pima.net/standards/it10/PIMA15740/Exif_2-1.PDF
1350: */
1351: if ((c4 == 0xE1)
1352: && (c7 == 'E' && c8 == 'x' && c9 == 'i'
1353: && c10 == 'f' && c11 == 0)) {
1354: return "image/jpeg";
1355: }
1356:
1357: if (c4 == 0xEE) {
1358: return "image/jpg";
1359: }
1360: }
1361:
1362: if (c1 == 0xD0 && c2 == 0xCF && c3 == 0x11 && c4 == 0xE0
1363: && c5 == 0xA1 && c6 == 0xB1 && c7 == 0x1A && c8 == 0xE1) {
1364:
1365: /* Above is signature of Microsoft Structured Storage.
1366: * Below this, could have tests for various SS entities.
1367: * For now, just test for FlashPix.
1368: */
1369: if (checkfpx(is)) {
1370: return "image/vnd.fpx";
1371: }
1372: }
1373:
1374: if (c1 == 0x2E && c2 == 0x73 && c3 == 0x6E && c4 == 0x64) {
1375: return "audio/basic"; // .au format, big endian
1376: }
1377:
1378: if (c1 == 0x64 && c2 == 0x6E && c3 == 0x73 && c4 == 0x2E) {
1379: return "audio/basic"; // .au format, little endian
1380: }
1381:
1382: if (c1 == 'R' && c2 == 'I' && c3 == 'F' && c4 == 'F') {
1383: /* I don't know if this is official but evidence
1384: * suggests that .wav files start with "RIFF" - brown
1385: */
1386: return "audio/x-wav";
1387: }
1388: return null;
1389: }
1390:
1391: /**
1392: * Check for FlashPix image data in InputStream is. Return true if
1393: * the stream has FlashPix data, false otherwise. Before calling this
1394: * method, the stream should have already been checked to be sure it
1395: * contains Microsoft Structured Storage data.
1396: */
1397: static private boolean checkfpx(InputStream is) throws IOException {
1398:
1399: /* Test for FlashPix image data in Microsoft Structured Storage format.
1400: * In general, should do this with calls to an SS implementation.
1401: * Lacking that, need to dig via offsets to get to the FlashPix
1402: * ClassID. Details:
1403: *
1404: * Offset to Fpx ClsID from beginning of stream should be:
1405: *
1406: * FpxClsidOffset = rootEntryOffset + clsidOffset
1407: *
1408: * where: clsidOffset = 0x50.
1409: * rootEntryOffset = headerSize + sectorSize*sectDirStart
1410: * + 128*rootEntryDirectory
1411: *
1412: * where: headerSize = 0x200 (always)
1413: * sectorSize = 2 raised to power of uSectorShift,
1414: * which is found in the header at
1415: * offset 0x1E.
1416: * sectDirStart = found in the header at offset 0x30.
1417: * rootEntryDirectory = in general, should search for
1418: * directory labelled as root.
1419: * We will assume value of 0 (i.e.,
1420: * rootEntry is in first directory)
1421: */
1422:
1423: // Mark the stream so we can reset it. 0x100 is enough for the first
1424: // few reads, but the mark will have to be reset and set again once
1425: // the offset to the root directory entry is computed. That offset
1426: // can be very large and isn't know until the stream has been read from
1427: is.mark(0x100);
1428:
1429: // Get the byte ordering located at 0x1E. 0xFE is little endian,
1430: // 0xFF is big endian.
1431: long toSkip = (long) 0x1C;
1432: long posn;
1433:
1434: if ((posn = skipForward(is, toSkip)) < toSkip) {
1435: is.reset();
1436: return false;
1437: }
1438:
1439: int c[] = new int[16];
1440: if (readBytes(c, 2, is) < 0) {
1441: is.reset();
1442: return false;
1443: }
1444:
1445: int byteOrder = c[0];
1446:
1447: posn += 2;
1448: int uSectorShift;
1449: if (readBytes(c, 2, is) < 0) {
1450: is.reset();
1451: return false;
1452: }
1453:
1454: if (byteOrder == 0xFE) {
1455: uSectorShift = c[0];
1456: uSectorShift += c[1] << 8;
1457: } else {
1458: uSectorShift = c[0] << 8;
1459: uSectorShift += c[1];
1460: }
1461:
1462: posn += 2;
1463: toSkip = (long) 0x30 - posn;
1464: long skipped = 0;
1465: if ((skipped = skipForward(is, toSkip)) < toSkip) {
1466: is.reset();
1467: return false;
1468: }
1469: posn += skipped;
1470:
1471: if (readBytes(c, 4, is) < 0) {
1472: is.reset();
1473: return false;
1474: }
1475:
1476: int sectDirStart;
1477: if (byteOrder == 0xFE) {
1478: sectDirStart = c[0];
1479: sectDirStart += c[1] << 8;
1480: sectDirStart += c[2] << 16;
1481: sectDirStart += c[3] << 24;
1482: } else {
1483: sectDirStart = c[0] << 24;
1484: sectDirStart += c[1] << 16;
1485: sectDirStart += c[2] << 8;
1486: sectDirStart += c[3];
1487: }
1488: posn += 4;
1489: is.reset(); // Reset back to the beginning
1490:
1491: toSkip = (long) 0x200 + (long) ((int) 1 << uSectorShift)
1492: * sectDirStart + (long) 0x50;
1493:
1494: // Sanity check!
1495: if (toSkip < 0) {
1496: return false;
1497: }
1498:
1499: /*
1500: * How far can we skip? Is there any performance problem here?
1501: * This skip can be fairly long, at least 0x4c650 in at least
1502: * one case. Have to assume that the skip will fit in an int.
1503: * Leave room to read whole root dir
1504: */
1505: is.mark((int) toSkip + 0x30);
1506:
1507: if ((skipForward(is, toSkip)) < toSkip) {
1508: is.reset();
1509: return false;
1510: }
1511:
1512: /* should be at beginning of ClassID, which is as follows
1513: * (in little-endian byte order):
1514: * 00 67 61 56 54 C1 CE 11 85 53 00 AA 00 A1 F9 5B
1515: *
1516: * This is stored from Windows as long,short,short,char[8]
1517: * so for byte order changes, the order only changes for
1518: * the first 8 bytes in the ClassID.
1519: *
1520: * Test against this, ignoring second byte (little-endian) since
1521: * this could change depending on part of Fpx file we have.
1522: */
1523:
1524: if (readBytes(c, 16, is) < 0) {
1525: is.reset();
1526: return false;
1527: }
1528:
1529: // little-endian byte order
1530: if (byteOrder == 0xFE && c[0] == 0x00 && c[2] == 0x61
1531: && c[3] == 0x56 && c[4] == 0x54 && c[5] == 0xC1
1532: && c[6] == 0xCE && c[7] == 0x11 && c[8] == 0x85
1533: && c[9] == 0x53 && c[10] == 0x00 && c[11] == 0xAA
1534: && c[12] == 0x00 && c[13] == 0xA1 && c[14] == 0xF9
1535: && c[15] == 0x5B) {
1536: is.reset();
1537: return true;
1538: }
1539:
1540: // big-endian byte order
1541: else if (c[3] == 0x00 && c[1] == 0x61 && c[0] == 0x56
1542: && c[5] == 0x54 && c[4] == 0xC1 && c[7] == 0xCE
1543: && c[6] == 0x11 && c[8] == 0x85 && c[9] == 0x53
1544: && c[10] == 0x00 && c[11] == 0xAA && c[12] == 0x00
1545: && c[13] == 0xA1 && c[14] == 0xF9 && c[15] == 0x5B) {
1546: is.reset();
1547: return true;
1548: }
1549: is.reset();
1550: return false;
1551: }
1552:
1553: /**
1554: * Tries to read the specified number of bytes from the stream
1555: * Returns -1, If EOF is reached before len bytes are read, returns 0
1556: * otherwise
1557: */
1558: static private int readBytes(int c[], int len, InputStream is)
1559: throws IOException {
1560:
1561: byte buf[] = new byte[len];
1562: if (is.read(buf, 0, len) < len) {
1563: return -1;
1564: }
1565:
1566: // fill the passed in int array
1567: for (int i = 0; i < len; i++) {
1568: c[i] = buf[i] & 0xff;
1569: }
1570: return 0;
1571: }
1572:
1573: /**
1574: * Skips through the specified number of bytes from the stream
1575: * until either EOF is reached, or the specified
1576: * number of bytes have been skipped
1577: */
1578: static private long skipForward(InputStream is, long toSkip)
1579: throws IOException {
1580:
1581: long eachSkip = 0;
1582: long skipped = 0;
1583:
1584: while (skipped != toSkip) {
1585: eachSkip = is.skip(toSkip - skipped);
1586:
1587: // check if EOF is reached
1588: if (eachSkip <= 0) {
1589: if (is.read() == -1) {
1590: return skipped;
1591: } else {
1592: skipped++;
1593: }
1594: }
1595: skipped += eachSkip;
1596: }
1597: return skipped;
1598: }
1599:
1600: }
1601:
1602: class UnknownContentHandler extends ContentHandler {
1603: public Object getContent(URLConnection uc) throws IOException {
1604: return uc.getInputStream();
1605: }
1606: }
|