001    /*
002     * ftp4j - A pure Java FTP client library
003     * 
004     * Copyright (C) 2008 Carlo Pelliccia (www.sauronsoftware.it)
005     * 
006     * This program is free software: you can redistribute it and/or modify
007     * it under the terms of the GNU General Public License as published by
008     * the Free Software Foundation, either version 3 of the License, or
009     * (at your option) any later version.
010     *
011     * This program is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014     * GNU General Public License for more details.
015     *
016     * You should have received a copy of the GNU General Public License
017     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
018     */
019    package it.sauronsoftware.ftp4j.connectors;
020    
021    import it.sauronsoftware.ftp4j.FTPCommunicationChannel;
022    import it.sauronsoftware.ftp4j.FTPConnection;
023    import it.sauronsoftware.ftp4j.FTPConnector;
024    import it.sauronsoftware.ftp4j.FTPIllegalReplyException;
025    import it.sauronsoftware.ftp4j.FTPReply;
026    
027    import java.io.IOException;
028    import java.net.Socket;
029    
030    /**
031     * This one connects a remote host via a FTP proxy which supports the SITE or
032     * the OPEN proxy method.
033     * 
034     * @author Carlo Pelliccia
035     */
036    public class FTPProxyConnector implements FTPConnector {
037    
038            /**
039             * Requires the connection to the remote host through a SITE command after
040             * proxy authentication. Default one.
041             */
042            public static int STYLE_SITE_COMMAND = 0;
043    
044            /**
045             * Requires the connection to the remote host through a OPEN command without
046             * proxy authentication.
047             */
048            public static int STYLE_OPEN_COMMAND = 1;
049    
050            /**
051             * The proxy host name.
052             */
053            private String proxyHost;
054    
055            /**
056             * The proxy port.
057             */
058            private int proxyPort;
059    
060            /**
061             * The proxyUser for proxy authentication.
062             */
063            private String proxyUser;
064    
065            /**
066             * The proxyPass for proxy authentication.
067             */
068            private String proxyPass;
069    
070            /**
071             * The style used by the proxy.
072             */
073            public int style = STYLE_SITE_COMMAND;
074    
075            /**
076             * Builds the connector.
077             * 
078             * Default value for the style is STYLE_SITE_COMMAND.
079             * 
080             * @param proxyHost
081             *            The proxy host name.
082             * @param proxyPort
083             *            The proxy port.
084             * @param proxyUser
085             *            The username for proxy authentication.
086             * @param proxyPass
087             *            The password for proxy authentication.
088             */
089            public FTPProxyConnector(String proxyHost, int proxyPort, String proxyUser,
090                            String proxyPass) {
091                    this.proxyHost = proxyHost;
092                    this.proxyPort = proxyPort;
093                    this.proxyUser = proxyUser;
094                    this.proxyPass = proxyPass;
095            }
096    
097            /**
098             * Builds the connector.
099             * 
100             * Default value for the style is STYLE_SITE_COMMAND.
101             * 
102             * @param proxyHost
103             *            The proxy host name.
104             * @param proxyPort
105             *            The proxy port.
106             */
107            public FTPProxyConnector(String proxyHost, int proxyPort) {
108                    this(proxyHost, proxyPort, "anonymous", "ftp4j");
109            }
110    
111            /**
112             * Sets the style used by the proxy.
113             * 
114             * {@link FTPProxyConnector#STYLE_SITE_COMMAND} - Requires the connection to
115             * the remote host through a SITE command after proxy authentication.
116             * 
117             * {@link FTPProxyConnector#STYLE_OPEN_COMMAND} - Requires the connection to
118             * the remote host through a OPEN command without proxy authentication.
119             * 
120             * Default value for the style is STYLE_SITE_COMMAND.
121             * 
122             * @param style
123             *            The style.
124             * @see FTPProxyConnector#STYLE_SITE_COMMAND
125             * @see FTPProxyConnector#STYLE_OPEN_COMMAND
126             */
127            public void setStyle(int style) {
128                    if (style != STYLE_OPEN_COMMAND && style != STYLE_SITE_COMMAND) {
129                            throw new IllegalArgumentException("Invalid style");
130                    }
131                    this.style = style;
132            }
133    
134            public FTPConnection connectForCommunicationChannel(String host, int port)
135                            throws IOException {
136                    Socket socket = new Socket(proxyHost, proxyPort);
137                    SocketConnection socketConnection = new SocketConnection(socket);
138                    FTPCommunicationChannel communication = new FTPCommunicationChannel(
139                                    socketConnection, "ASCII");
140                    // Welcome message.
141                    FTPReply r;
142                    try {
143                            r = communication.readFTPReply();
144                    } catch (FTPIllegalReplyException e) {
145                            throw new IOException("Invalid proxy response");
146                    }
147                    // Does this reply mean "ok"?
148                    if (r.getCode() != 220) {
149                            // Mmmmm... it seems no!
150                            throw new IOException("Invalid proxy response");
151                    }
152                    if (style == STYLE_SITE_COMMAND) {
153                            // Usefull flags.
154                            boolean passwordRequired;
155                            // Send the user and read the reply.
156                            communication.sendFTPCommand("USER " + proxyUser);
157                            try {
158                                    r = communication.readFTPReply();
159                            } catch (FTPIllegalReplyException e) {
160                                    throw new IOException("Invalid proxy response");
161                            }
162                            switch (r.getCode()) {
163                            case 230:
164                                    // Password isn't required.
165                                    passwordRequired = false;
166                                    break;
167                            case 331:
168                                    // Password is required.
169                                    passwordRequired = true;
170                                    break;
171                            default:
172                                    // User validation failed.
173                                    throw new IOException("Proxy authentication failed");
174                            }
175                            // Password.
176                            if (passwordRequired) {
177                                    // Send the password.
178                                    communication.sendFTPCommand("PASS " + proxyPass);
179                                    try {
180                                            r = communication.readFTPReply();
181                                    } catch (FTPIllegalReplyException e) {
182                                            throw new IOException("Invalid proxy response");
183                                    }
184                                    if (r.getCode() != 230) {
185                                            // Authentication failed.
186                                            throw new IOException("Proxy authentication failed");
187                                    }
188                            }
189                            communication.sendFTPCommand("SITE " + host + ":" + port);
190                    } else if (style == STYLE_OPEN_COMMAND) {
191                            communication.sendFTPCommand("OPEN " + host + ":" + port);
192                    }
193                    return socketConnection;
194            }
195    
196            public FTPConnection connectForDataTransferChannel(String host, int port)
197                            throws IOException {
198                    Socket socket = new Socket(host, port);
199                    return new SocketConnection(socket);
200            }
201    
202    }