1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/*
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  Licensed to the Apache Software Foundation (ASF) under one or more
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  contributor license agreements.  See the NOTICE file distributed with
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  this work for additional information regarding copyright ownership.
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  The ASF licenses this file to You under the Apache License, Version 2.0
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  (the "License"); you may not use this file except in compliance with
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  the License.  You may obtain a copy of the License at
8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *     http://www.apache.org/licenses/LICENSE-2.0
10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  Unless required by applicable law or agreed to in writing, software
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  distributed under the License is distributed on an "AS IS" BASIS,
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  See the License for the specific language governing permissions and
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  limitations under the License.
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
185aafac4db69e6d087c512cdfa5c7c0e2f1611681Jesse Wilsonpackage libcore.net.url;
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
20adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.BufferedInputStream;
2155392539fea537abfb6581b474918f9d611fba27Jesse Wilsonimport java.io.EOFException;
22adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.FileNotFoundException;
23adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.IOException;
24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.InputStream;
25adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.InterruptedIOException;
26adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.OutputStream;
27adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.net.InetSocketAddress;
28adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.net.Proxy;
29adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.net.ProxySelector;
30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.net.ServerSocket;
31adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.net.Socket;
32adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.net.SocketPermission;
33adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.net.URI;
34adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.net.URISyntaxException;
35adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.net.URL;
36adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.net.URLConnection;
37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.net.URLStreamHandler;
38e810d3b49631329b11440aa5b7a54db181d42ed1Elliott Hughesimport java.nio.charset.Charsets;
39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.security.Permission;
40adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.ArrayList;
41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.Iterator;
42adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.List;
43adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
44adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpublic class FtpURLConnection extends URLConnection {
45adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
46adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final int FTP_PORT = 21;
47adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
48adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    // FTP Reply Constants
49adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final int FTP_DATAOPEN = 125;
50adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
51adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final int FTP_OPENDATA = 150;
52adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
53adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final int FTP_OK = 200;
54adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
55adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final int FTP_USERREADY = 220;
56adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
57adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final int FTP_TRANSFEROK = 226;
58adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
59adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    // private static final int FTP_PASV = 227;
60adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
61adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final int FTP_LOGGEDIN = 230;
62adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
63adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final int FTP_FILEOK = 250;
64adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
65adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final int FTP_PASWD = 331;
66adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
67adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    // private static final int FTP_DATAERROR = 451;
68adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
69adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    // private static final int FTP_ERROR = 500;
70adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
71adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private static final int FTP_NOTFOUND = 550;
72adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
73adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private Socket controlSocket;
74adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
75adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private Socket dataSocket;
76adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
77adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private ServerSocket acceptSocket;
78adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
79adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private InputStream ctrlInput;
80adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
81adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private InputStream inputStream;
82adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
83adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private OutputStream ctrlOutput;
84adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
85adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private int dataPort;
86adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
87f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes    private String username = "anonymous";
88adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
89f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes    private String password = "";
90adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
91adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private String replyCode;
92adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
93adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private String hostName;
94adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
95adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private Proxy proxy;
96adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
97adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private Proxy currentProxy;
98adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
99adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private URI uri;
100adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
101adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
102adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * FtpURLConnection constructor comment.
103f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
104adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param url
105adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
106adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected FtpURLConnection(URL url) {
107adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        super(url);
108adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        hostName = url.getHost();
109adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String parse = url.getUserInfo();
110adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (parse != null) {
111adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            int split = parse.indexOf(':');
112adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (split >= 0) {
113adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                username = parse.substring(0, split);
114adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                password = parse.substring(split + 1);
115adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else {
116adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                username = parse;
117adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
118adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
119adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        uri = null;
120adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        try {
121adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            uri = url.toURI();
122adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } catch (URISyntaxException e) {
123adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            // do nothing.
124adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
125adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
126adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
127adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
128adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * FtpURLConnection constructor.
129f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
130adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param url
131adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param proxy
132adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
133adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    protected FtpURLConnection(URL url, Proxy proxy) {
134adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        this(url);
135adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        this.proxy = proxy;
136adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
137adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
138adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
139adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Change the server directory to that specified in the URL
140adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
141adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private void cd() throws IOException {
142adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int idx = url.getFile().lastIndexOf('/');
143adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
144adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (idx > 0) {
145adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String dir = url.getFile().substring(0, idx);
146f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            write("CWD " + dir + "\r\n");
147adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            int reply = getReply();
148adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (reply != FTP_FILEOK && dir.length() > 0 && dir.charAt(0) == '/') {
149f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                write("CWD " + dir.substring(1) + "\r\n");
150adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                reply = getReply();
151adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
152adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (reply != FTP_FILEOK) {
153b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes                throw new IOException("Unable to change directories");
154adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
155adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
156adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
157adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
158adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
159adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Establishes the connection to the resource specified by this
160adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * <code>URL</code>
161f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
162adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #connected
163adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see java.io.IOException
164adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see URLStreamHandler
165adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
166adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
167adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public void connect() throws IOException {
168adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // Use system-wide ProxySelect to select proxy list,
169adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        // then try to connect via elements in the proxy list.
170adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        List<Proxy> proxyList = null;
171b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        if (proxy != null) {
172adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            proxyList = new ArrayList<Proxy>(1);
173adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            proxyList.add(proxy);
174adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } else {
1750917c4a9d5d0115950450cdd0bb46e43a48da5dbElliott Hughes            ProxySelector selector = ProxySelector.getDefault();
1760917c4a9d5d0115950450cdd0bb46e43a48da5dbElliott Hughes            if (selector != null) {
1770917c4a9d5d0115950450cdd0bb46e43a48da5dbElliott Hughes                proxyList = selector.select(uri);
1780917c4a9d5d0115950450cdd0bb46e43a48da5dbElliott Hughes            }
179adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
180b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        if (proxyList == null) {
181adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            currentProxy = null;
182adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            connectInternal();
183adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } else {
184adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            ProxySelector selector = ProxySelector.getDefault();
185adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            Iterator<Proxy> iter = proxyList.iterator();
186adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            boolean connectOK = false;
187f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            String failureReason = "";
188adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            while (iter.hasNext() && !connectOK) {
189adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                currentProxy = iter.next();
190adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                try {
191adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    connectInternal();
192adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    connectOK = true;
193adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                } catch (IOException ioe) {
194f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                    failureReason = ioe.getLocalizedMessage();
195adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    // If connect failed, callback "connectFailed"
196adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    // should be invoked.
197b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes                    if (selector != null && Proxy.NO_PROXY != currentProxy) {
198f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson                        selector.connectFailed(uri, currentProxy.address(), ioe);
199adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    }
200adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
201adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
202adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (!connectOK) {
203b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes                throw new IOException("Unable to connect to server: " + failureReason);
204adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
205adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
206adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
207adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
208adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private void connectInternal() throws IOException {
209adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int port = url.getPort();
210adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int connectTimeout = getConnectTimeout();
211adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (port <= 0) {
212adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            port = FTP_PORT;
213adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
214b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        if (currentProxy == null || Proxy.Type.HTTP == currentProxy.type()) {
215adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            controlSocket = new Socket();
216adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } else {
217adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            controlSocket = new Socket(currentProxy);
218adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
219adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        InetSocketAddress addr = new InetSocketAddress(hostName, port);
220adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        controlSocket.connect(addr, connectTimeout);
221adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        connected = true;
222adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        ctrlOutput = controlSocket.getOutputStream();
223adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        ctrlInput = controlSocket.getInputStream();
224adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        login();
225adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        setType();
226adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (!getDoInput()) {
227adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            cd();
228adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
229adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
230adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        try {
231adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            acceptSocket = new ServerSocket(0);
232adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            dataPort = acceptSocket.getLocalPort();
233adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            /* Cannot set REUSEADDR so we need to send a PORT command */
234adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            port();
235adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (connectTimeout == 0) {
236adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                // set timeout rather than zero as before
237adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                connectTimeout = 3000;
238adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
239adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            acceptSocket.setSoTimeout(getConnectTimeout());
240adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (getDoInput()) {
241adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                getFile();
242adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else {
243adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                sendFile();
244adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
245adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            dataSocket = acceptSocket.accept();
246adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            dataSocket.setSoTimeout(getReadTimeout());
247adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            acceptSocket.close();
248adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } catch (InterruptedIOException e) {
249b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes            throw new IOException("Could not establish data connection");
250adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
251adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (getDoInput()) {
252adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            inputStream = new FtpURLInputStream(
253b748a9b827665a8b19d60af4b419503b45e74329Elliott Hughes                    new BufferedInputStream(dataSocket.getInputStream()), controlSocket);
254adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
255adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
256adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
257adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
258adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the content type of the resource. Just takes a guess based on the
259adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * name.
260adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
261adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
262adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getContentType() {
263adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String result = guessContentTypeFromName(url.getFile());
264adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (result == null) {
2657b3ebefcdf79a251cf2a5619dd2354952fc67665Elliott Hughes            return "content/unknown";
266adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
267adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return result;
268adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
269adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
270adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private void getFile() throws IOException {
271adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int reply;
272adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String file = url.getFile();
273f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        write("RETR " + file + "\r\n");
274adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        reply = getReply();
275adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (reply == FTP_NOTFOUND && file.length() > 0 && file.charAt(0) == '/') {
276f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            write("RETR " + file.substring(1) + "\r\n");
277adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            reply = getReply();
278adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
279adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (!(reply == FTP_OPENDATA || reply == FTP_TRANSFEROK)) {
280b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes            throw new FileNotFoundException("Unable to retrieve file: " + reply);
281adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
282adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
283adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
284adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
285adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Creates a input stream for writing to this URL Connection.
286f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
287adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return The input stream to write to
288adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
289adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             Cannot read from URL or error creating InputStream
290f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
291adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #getContent()
292adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #getOutputStream()
293adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see java.io.InputStream
294adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see java.io.IOException
295f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
296adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
297adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
298adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public InputStream getInputStream() throws IOException {
299adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (!connected) {
300adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            connect();
301adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
302adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return inputStream;
303adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
304adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
305adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
306adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the permission object (in this case, SocketPermission) with the
307adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * host and the port number as the target name and "resolve, connect" as the
308adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * action list.
309f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
310adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the permission object required for this connection
311adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
312adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             thrown when an IO exception occurs during the creation of the
313adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             permission object.
314adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
315adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
316adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Permission getPermission() throws IOException {
317adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int port = url.getPort();
318adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (port <= 0) {
319adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            port = FTP_PORT;
320adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
321f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        return new SocketPermission(hostName + ":" + port, "connect, resolve");
322adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
323adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
324adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
325adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Creates a output stream for writing to this URL Connection.
326f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
327adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return The output stream to write to
328adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IOException
329adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             when the OutputStream could not be created
330f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
331adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #getContent()
332adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #getInputStream()
333adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see java.io.IOException
334f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
335adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
336adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
337adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public OutputStream getOutputStream() throws IOException {
338adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (!connected) {
339adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            connect();
340adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
341adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return dataSocket.getOutputStream();
342adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
343adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
344adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private int getReply() throws IOException {
345adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        byte[] code = new byte[3];
34655392539fea537abfb6581b474918f9d611fba27Jesse Wilson        for (int i = 0; i < code.length; i++) {
34755392539fea537abfb6581b474918f9d611fba27Jesse Wilson            final int tmp = ctrlInput.read();
34855392539fea537abfb6581b474918f9d611fba27Jesse Wilson            if (tmp == -1) {
34955392539fea537abfb6581b474918f9d611fba27Jesse Wilson                throw new EOFException();
35055392539fea537abfb6581b474918f9d611fba27Jesse Wilson            }
35155392539fea537abfb6581b474918f9d611fba27Jesse Wilson            code[i] = (byte) tmp;
35255392539fea537abfb6581b474918f9d611fba27Jesse Wilson        }
353e810d3b49631329b11440aa5b7a54db181d42ed1Elliott Hughes        replyCode = new String(code, 0, code.length, Charsets.ISO_8859_1);
354f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
355adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        boolean multiline = false;
356adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (ctrlInput.read() == '-') {
357adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            multiline = true;
358adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
359adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        readLine(); /* Skip the rest of the first line */
360adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (multiline) {
361adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            while (readMultiLine()) {/* Read all of a multiline reply */
362adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
363adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
364f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
36555392539fea537abfb6581b474918f9d611fba27Jesse Wilson        try {
36655392539fea537abfb6581b474918f9d611fba27Jesse Wilson            return Integer.parseInt(replyCode);
36755392539fea537abfb6581b474918f9d611fba27Jesse Wilson        } catch (NumberFormatException e) {
36855392539fea537abfb6581b474918f9d611fba27Jesse Wilson            throw (IOException)(new IOException("reply code is invalid").initCause(e));
36955392539fea537abfb6581b474918f9d611fba27Jesse Wilson        }
370adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
371adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
372adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private void login() throws IOException {
373adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int reply;
374adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        reply = getReply();
375adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (reply == FTP_USERREADY) {
376adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } else {
377b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes            throw new IOException("Unable to connect to server: " + url.getHost());
378adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
379f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        write("USER " + username + "\r\n");
380adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        reply = getReply();
381adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (reply == FTP_PASWD || reply == FTP_LOGGEDIN) {
382adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        } else {
383b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes            throw new IOException("Unable to log in to server (USER): " + url.getHost());
384adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
385adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (reply == FTP_PASWD) {
386f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes            write("PASS " + password + "\r\n");
387adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            reply = getReply();
388adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (!(reply == FTP_OK || reply == FTP_USERREADY || reply == FTP_LOGGEDIN)) {
389b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes                throw new IOException("Unable to log in to server (PASS): " + url.getHost());
390adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
391adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
392adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
393adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
394adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private void port() throws IOException {
395f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        write("PORT "
396adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                + controlSocket.getLocalAddress().getHostAddress().replace('.',
397adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        ',') + ',' + (dataPort >> 8) + ','
398adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                + (dataPort & 255)
399f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                + "\r\n");
400adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (getReply() != FTP_OK) {
401b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes            throw new IOException("Unable to configure data port");
402adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
403adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
404adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
405adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
406adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Read a line of text and return it for possible parsing
407adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
408adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private String readLine() throws IOException {
409a389b4a499f40379b0b204d7ba1c2057663d95c0Jesse Wilson        StringBuilder sb = new StringBuilder();
410adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int c;
411adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        while ((c = ctrlInput.read()) != '\n') {
412adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            sb.append((char) c);
413adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
414adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return sb.toString();
415adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
416adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
417adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private boolean readMultiLine() throws IOException {
418adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String line = readLine();
419adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (line.length() < 4) {
420adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return true;
421adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
422adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (line.substring(0, 3).equals(replyCode)
423adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                && (line.charAt(3) == (char) 32)) {
424adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return false;
425adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
426adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return true;
427adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
428adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
429adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
430adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Issue the STOR command to the server with the file as the parameter
431adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
432adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private void sendFile() throws IOException {
433adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int reply;
434f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        write("STOR "
435adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                + url.getFile().substring(url.getFile().lastIndexOf('/') + 1,
436f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                        url.getFile().length()) + "\r\n");
437adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        reply = getReply();
438adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (!(reply == FTP_OPENDATA || reply == FTP_OK || reply == FTP_DATAOPEN)) {
439b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes            throw new IOException("Unable to store file");
440adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
441adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
442adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
443adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
444adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Set the flag if this <code>URLConnection</code> supports input (read).
445adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * It cannot be set after the connection is made. FtpURLConnections cannot
446adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * support both input and output
447f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
448adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param newValue *
449adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IllegalAccessError
450adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             when this method attempts to change the flag after connected
451f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
452adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #doInput
453adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #getDoInput()
454adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see java.lang.IllegalAccessError
455adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #setDoInput(boolean)
456adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
457adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
458adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public void setDoInput(boolean newValue) {
459adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (connected) {
460adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new IllegalAccessError();
461adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
462adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        this.doInput = newValue;
463adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        this.doOutput = !newValue;
464adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
465adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
466adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
467adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Set the flag if this <code>URLConnection</code> supports output(read).
468adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * It cannot be set after the connection is made.\ FtpURLConnections cannot
469adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * support both input and output.
470f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
471adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param newValue
472f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
473adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @throws IllegalAccessError
474adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *             when this method attempts to change the flag after connected
475f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes     *
476adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #doOutput
477adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see java.lang.IllegalAccessError
478adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @see #setDoOutput(boolean)
479adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
480adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
481adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public void setDoOutput(boolean newValue) {
482adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (connected) {
483adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new IllegalAccessError();
484adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
485adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        this.doOutput = newValue;
486adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        this.doInput = !newValue;
487adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
488adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
489adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
490adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Set the type of the file transfer. Only Image is supported
491adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
492adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private void setType() throws IOException {
493f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        write("TYPE I\r\n");
494adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (getReply() != FTP_OK) {
495b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes            throw new IOException("Unable to set transfer type");
496adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
497adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
498adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
499adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private void write(String command) throws IOException {
500e810d3b49631329b11440aa5b7a54db181d42ed1Elliott Hughes        ctrlOutput.write(command.getBytes(Charsets.ISO_8859_1));
501adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
502adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
503