1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package org.apache.harmony.luni.internal.net.www.protocol.file;
19
20import java.io.BufferedInputStream;
21import java.io.ByteArrayInputStream;
22import java.io.ByteArrayOutputStream;
23import java.io.File;
24import java.io.FileInputStream;
25import java.io.FilePermission;
26import java.io.IOException;
27import java.io.InputStream;
28import java.io.PrintStream;
29import java.net.URL;
30import java.net.URLConnection;
31
32import org.apache.harmony.luni.internal.net.www.MimeTable;
33import org.apache.harmony.luni.util.Util;
34
35/**
36 * This subclass extends <code>URLConnection</code>.
37 * <p>
38 * This class is responsible for connecting, getting content and input stream of
39 * the file.
40 */
41public class FileURLConnection extends URLConnection {
42
43    String fileName;
44
45    private InputStream is;
46
47    private int length = -1;
48
49    private boolean isDir;
50
51    private FilePermission permission;
52
53    /**
54     * Creates an instance of <code>FileURLConnection</code> for establishing
55     * a connection to the file pointed by this <code>URL<code>
56     *
57     * @param url The URL this connection is connected to
58     */
59    public FileURLConnection(URL url) {
60        super(url);
61        fileName = url.getFile();
62        if (fileName == null) {
63            fileName = ""; //$NON-NLS-1$
64        }
65        fileName = Util.decode(fileName, false);
66    }
67
68    /**
69     * This methods will attempt to obtain the input stream of the file pointed
70     * by this <code>URL</code>. If the file is a directory, it will return
71     * that directory listing as an input stream.
72     *
73     * @throws IOException
74     *             if an IO error occurs while connecting
75     */
76    @Override
77    public void connect() throws IOException {
78        File f = new File(fileName);
79        if (f.isDirectory()) {
80            isDir = true;
81            is = getDirectoryListing(f);
82            // use -1 for the contentLength
83        } else {
84            // BEGIN android-modified
85            is = new BufferedInputStream(new FileInputStream(f), 8192);
86            // END android-modified
87            length = is.available();
88        }
89        connected = true;
90    }
91
92    /**
93     * Returns the length of the file in bytes.
94     *
95     * @return the length of the file
96     *
97     * @see #getContentType()
98     */
99    @Override
100    public int getContentLength() {
101        try {
102            if (!connected) {
103                connect();
104            }
105        } catch (IOException e) {
106            // default is -1
107        }
108        return length;
109    }
110
111    /**
112     * Returns the content type of the resource. Just takes a guess based on the
113     * name.
114     *
115     * @return the content type
116     */
117    @Override
118    public String getContentType() {
119        try {
120            if (!connected) {
121                connect();
122            }
123        } catch (IOException e) {
124            return MimeTable.UNKNOWN;
125        }
126        if (isDir) {
127            return "text/plain"; //$NON-NLS-1$
128        }
129        String result = guessContentTypeFromName(url.getFile());
130        if (result != null) {
131            return result;
132        }
133
134        try {
135            result = guessContentTypeFromStream(is);
136        } catch (IOException e) {
137            // Ignore
138        }
139        if (result != null) {
140            return result;
141        }
142
143        return MimeTable.UNKNOWN;
144    }
145
146    /**
147     * Returns the directory listing of the file component as an input stream.
148     *
149     * @return the input stream of the directory listing
150     */
151    private InputStream getDirectoryListing(File f) {
152        String fileList[] = f.list();
153        ByteArrayOutputStream bytes = new java.io.ByteArrayOutputStream();
154        PrintStream out = new PrintStream(bytes);
155        out.print("<title>Directory Listing</title>\n"); //$NON-NLS-1$
156        out.print("<base href=\"file:"); //$NON-NLS-1$
157        out.print(f.getPath().replace('\\', '/') + "/\"><h1>" + f.getPath() //$NON-NLS-1$
158                + "</h1>\n<hr>\n"); //$NON-NLS-1$
159        int i;
160        for (i = 0; i < fileList.length; i++) {
161            out.print(fileList[i] + "<br>\n"); //$NON-NLS-1$
162        }
163        out.close();
164        return new ByteArrayInputStream(bytes.toByteArray());
165    }
166
167    /**
168     * Returns the input stream of the object referred to by this
169     * <code>URLConnection</code>
170     *
171     * File Sample : "/ZIP211/+/harmony/tools/javac/resources/javac.properties"
172     * Invalid File Sample:
173     * "/ZIP/+/harmony/tools/javac/resources/javac.properties"
174     * "ZIP211/+/harmony/tools/javac/resources/javac.properties"
175     *
176     * @return input stream of the object
177     *
178     * @throws IOException
179     *             if an IO error occurs
180     */
181    @Override
182    public InputStream getInputStream() throws IOException {
183        if (!connected) {
184            connect();
185        }
186        return is;
187    }
188
189    /**
190     * Returns the permission, in this case the subclass, FilePermission object
191     * which represents the permission necessary for this URLConnection to
192     * establish the connection.
193     *
194     * @return the permission required for this URLConnection.
195     *
196     * @throws IOException
197     *             if an IO exception occurs while creating the permission.
198     */
199    @Override
200    public java.security.Permission getPermission() throws IOException {
201        if (permission == null) {
202            String path = fileName;
203            if (File.separatorChar != '/') {
204                path = path.replace('/', File.separatorChar);
205            }
206            permission = new FilePermission(path, "read"); //$NON-NLS-1$
207        }
208        return permission;
209    }
210}
211