1/*
2 * Copyright (c) 1997, 2013, 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.net.www.protocol.jar;
27
28import java.io.InputStream;
29import java.io.IOException;
30import java.io.FileNotFoundException;
31import java.io.BufferedInputStream;
32import java.net.URL;
33import java.net.URLConnection;
34import java.net.MalformedURLException;
35import java.net.UnknownServiceException;
36import java.util.Enumeration;
37import java.util.Map;
38import java.util.List;
39import java.util.jar.JarEntry;
40import java.util.jar.JarFile;
41import java.util.jar.Manifest;
42import java.security.Permission;
43
44/**
45 * @author Benjamin Renaud
46 * @since 1.2
47 */
48public class JarURLConnection extends java.net.JarURLConnection {
49
50    private static final boolean debug = false;
51
52    /* the Jar file factory. It handles both retrieval and caching.
53     */
54    private static final JarFileFactory factory = JarFileFactory.getInstance();
55
56    /* the url for the Jar file */
57    private URL jarFileURL;
58
59    /* the permission to get this JAR file. This is the actual, ultimate,
60     * permission, returned by the jar file factory.
61     */
62    private Permission permission;
63
64    /* the url connection for the JAR file */
65    private URLConnection jarFileURLConnection;
66
67    /* the entry name, if any */
68    private String entryName;
69
70    /* the JarEntry */
71    private JarEntry jarEntry;
72
73    /* the jar file corresponding to this connection */
74    private JarFile jarFile;
75
76    /* the content type for this connection */
77    private String contentType;
78
79    public JarURLConnection(URL url, Handler handler)
80    throws MalformedURLException, IOException {
81        super(url);
82
83        jarFileURL = getJarFileURL();
84        jarFileURLConnection = jarFileURL.openConnection();
85        entryName = getEntryName();
86    }
87
88    public JarFile getJarFile() throws IOException {
89        connect();
90        return jarFile;
91    }
92
93    public JarEntry getJarEntry() throws IOException {
94        connect();
95        return jarEntry;
96    }
97
98    public Permission getPermission() throws IOException {
99        return jarFileURLConnection.getPermission();
100    }
101
102    class JarURLInputStream extends java.io.FilterInputStream {
103        JarURLInputStream (InputStream src) {
104            super (src);
105        }
106        public void close () throws IOException {
107            try {
108                super.close();
109            } finally {
110                if (!getUseCaches()) {
111                    jarFile.close();
112                }
113            }
114        }
115    }
116
117
118
119    public void connect() throws IOException {
120        if (!connected) {
121            /* the factory call will do the security checks */
122            jarFile = factory.get(getJarFileURL(), getUseCaches());
123
124            /* we also ask the factory the permission that was required
125             * to get the jarFile, and set it as our permission.
126             */
127            if (getUseCaches()) {
128                boolean oldUseCaches = jarFileURLConnection.getUseCaches();
129                jarFileURLConnection = factory.getConnection(jarFile);
130                jarFileURLConnection.setUseCaches(oldUseCaches);
131            }
132
133            if ((entryName != null)) {
134                jarEntry = (JarEntry)jarFile.getEntry(entryName);
135                if (jarEntry == null) {
136                    try {
137                        if (!getUseCaches()) {
138                            jarFile.close();
139                        }
140                    } catch (Exception e) {
141                    }
142                    throw new FileNotFoundException("JAR entry " + entryName +
143                                                    " not found in " +
144                                                    jarFile.getName());
145                }
146            }
147            connected = true;
148        }
149    }
150
151    public InputStream getInputStream() throws IOException {
152        connect();
153
154        InputStream result = null;
155
156        if (entryName == null) {
157            throw new IOException("no entry name specified");
158        } else {
159            if (jarEntry == null) {
160                throw new FileNotFoundException("JAR entry " + entryName +
161                                                " not found in " +
162                                                jarFile.getName());
163            }
164            result = new JarURLInputStream (jarFile.getInputStream(jarEntry));
165        }
166        return result;
167    }
168
169    public int getContentLength() {
170        long result = getContentLengthLong();
171        if (result > Integer.MAX_VALUE)
172            return -1;
173        return (int) result;
174    }
175
176    public long getContentLengthLong() {
177        long result = -1;
178        try {
179            connect();
180            if (jarEntry == null) {
181                /* if the URL referes to an archive */
182                result = jarFileURLConnection.getContentLengthLong();
183            } else {
184                /* if the URL referes to an archive entry */
185                result = getJarEntry().getSize();
186            }
187        } catch (IOException e) {
188        }
189        return result;
190    }
191
192    public Object getContent() throws IOException {
193        Object result = null;
194
195        connect();
196        if (entryName == null) {
197            result = jarFile;
198        } else {
199            result = super.getContent();
200        }
201        return result;
202    }
203
204    public String getContentType() {
205        if (contentType == null) {
206            if (entryName == null) {
207                contentType = "x-java/jar";
208            } else {
209                try {
210                    connect();
211                    InputStream in = jarFile.getInputStream(jarEntry);
212                    contentType = guessContentTypeFromStream(
213                                        new BufferedInputStream(in));
214                    in.close();
215                } catch (IOException e) {
216                    // don't do anything
217                }
218            }
219            if (contentType == null) {
220                contentType = guessContentTypeFromName(entryName);
221            }
222            if (contentType == null) {
223                contentType = "content/unknown";
224            }
225        }
226        return contentType;
227    }
228
229    public String getHeaderField(String name) {
230        return jarFileURLConnection.getHeaderField(name);
231    }
232
233    /**
234     * Sets the general request property.
235     *
236     * @param   key     the keyword by which the request is known
237     *                  (e.g., "<code>accept</code>").
238     * @param   value   the value associated with it.
239     */
240    public void setRequestProperty(String key, String value) {
241        jarFileURLConnection.setRequestProperty(key, value);
242    }
243
244    /**
245     * Returns the value of the named general request property for this
246     * connection.
247     *
248     * @return  the value of the named general request property for this
249     *           connection.
250     */
251    public String getRequestProperty(String key) {
252        return jarFileURLConnection.getRequestProperty(key);
253    }
254
255    /**
256     * Adds a general request property specified by a
257     * key-value pair.  This method will not overwrite
258     * existing values associated with the same key.
259     *
260     * @param   key     the keyword by which the request is known
261     *                  (e.g., "<code>accept</code>").
262     * @param   value   the value associated with it.
263     */
264    public void addRequestProperty(String key, String value) {
265        jarFileURLConnection.addRequestProperty(key, value);
266    }
267
268    /**
269     * Returns an unmodifiable Map of general request
270     * properties for this connection. The Map keys
271     * are Strings that represent the request-header
272     * field names. Each Map value is a unmodifiable List
273     * of Strings that represents the corresponding
274     * field values.
275     *
276     * @return  a Map of the general request properties for this connection.
277     */
278    public Map<String,List<String>> getRequestProperties() {
279        return jarFileURLConnection.getRequestProperties();
280    }
281
282    /**
283     * Set the value of the <code>allowUserInteraction</code> field of
284     * this <code>URLConnection</code>.
285     *
286     * @param   allowuserinteraction   the new value.
287     * @see     java.net.URLConnection#allowUserInteraction
288     */
289    public void setAllowUserInteraction(boolean allowuserinteraction) {
290        jarFileURLConnection.setAllowUserInteraction(allowuserinteraction);
291    }
292
293    /**
294     * Returns the value of the <code>allowUserInteraction</code> field for
295     * this object.
296     *
297     * @return  the value of the <code>allowUserInteraction</code> field for
298     *          this object.
299     * @see     java.net.URLConnection#allowUserInteraction
300     */
301    public boolean getAllowUserInteraction() {
302        return jarFileURLConnection.getAllowUserInteraction();
303    }
304
305    /*
306     * cache control
307     */
308
309    /**
310     * Sets the value of the <code>useCaches</code> field of this
311     * <code>URLConnection</code> to the specified value.
312     * <p>
313     * Some protocols do caching of documents.  Occasionally, it is important
314     * to be able to "tunnel through" and ignore the caches (e.g., the
315     * "reload" button in a browser).  If the UseCaches flag on a connection
316     * is true, the connection is allowed to use whatever caches it can.
317     *  If false, caches are to be ignored.
318     *  The default value comes from DefaultUseCaches, which defaults to
319     * true.
320     *
321     * @see     java.net.URLConnection#useCaches
322     */
323    public void setUseCaches(boolean usecaches) {
324        jarFileURLConnection.setUseCaches(usecaches);
325    }
326
327    /**
328     * Returns the value of this <code>URLConnection</code>'s
329     * <code>useCaches</code> field.
330     *
331     * @return  the value of this <code>URLConnection</code>'s
332     *          <code>useCaches</code> field.
333     * @see     java.net.URLConnection#useCaches
334     */
335    public boolean getUseCaches() {
336        return jarFileURLConnection.getUseCaches();
337    }
338
339    /**
340     * Sets the value of the <code>ifModifiedSince</code> field of
341     * this <code>URLConnection</code> to the specified value.
342     *
343     * @param   value   the new value.
344     * @see     java.net.URLConnection#ifModifiedSince
345     */
346    public void setIfModifiedSince(long ifmodifiedsince) {
347        jarFileURLConnection.setIfModifiedSince(ifmodifiedsince);
348    }
349
350   /**
351     * Sets the default value of the <code>useCaches</code> field to the
352     * specified value.
353     *
354     * @param   defaultusecaches   the new value.
355     * @see     java.net.URLConnection#useCaches
356     */
357    public void setDefaultUseCaches(boolean defaultusecaches) {
358        jarFileURLConnection.setDefaultUseCaches(defaultusecaches);
359    }
360
361   /**
362     * Returns the default value of a <code>URLConnection</code>'s
363     * <code>useCaches</code> flag.
364     * <p>
365     * Ths default is "sticky", being a part of the static state of all
366     * URLConnections.  This flag applies to the next, and all following
367     * URLConnections that are created.
368     *
369     * @return  the default value of a <code>URLConnection</code>'s
370     *          <code>useCaches</code> flag.
371     * @see     java.net.URLConnection#useCaches
372     */
373    public boolean getDefaultUseCaches() {
374        return jarFileURLConnection.getDefaultUseCaches();
375    }
376}
377