001: /*
002: * @(#)FtpClient.java 1.55 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.net.ftp;
029:
030: import java.util.StringTokenizer;
031: import java.io.*;
032: import java.net.*;
033: import sun.net.TransferProtocolClient;
034: import sun.net.TelnetInputStream;
035: import sun.net.TelnetOutputStream;
036:
037: /**
038: * This class implements the FTP client.
039: *
040: * @version 1.49, 08/19/02
041: * @author Jonathan Payne
042: */
043:
044: public class FtpClient extends TransferProtocolClient {
045: public static final int FTP_PORT = 21;
046: static int FTP_SUCCESS = 1;
047: static int FTP_TRY_AGAIN = 2;
048: static int FTP_ERROR = 3;
049: /** socket for data transfer */
050: private Socket dataSocket = null;
051: private boolean replyPending = false;
052: private boolean binaryMode = false;
053: /** user name for login */
054: String user = null;
055: /** password for login */
056: String password = null;
057: /** last command issued */
058: String command;
059: /** The last reply code from the ftp daemon. */
060: int lastReplyCode;
061: /** Welcome message from the server, if any. */
062: public String welcomeMsg;
063: /* The following three data members are left in for binary */
064:
065: /* backwards-compatibility. Unfortunately, HotJava sets them directly */
066:
067: /* when it wants to change the settings. The new design has us not */
068:
069: /* cache these, so this is unnecessary, but eliminating the data members */
070:
071: /* would break HJB 1.1 under JDK 1.2. */
072:
073: /* */
074:
075: /* These data members are not used, and their values are meaningless. */
076:
077: /* TODO: Take them out for JDK 2.0! */
078:
079: /**
080: * @deprecated
081: */
082: public static boolean useFtpProxy;
083: /**
084: * @deprecated
085: */
086: public static String ftpProxyHost;
087: /**
088: * @deprecated
089: */
090: public static int ftpProxyPort;
091:
092: /* these methods are used to determine whether ftp urls are sent to */
093:
094: /* an http server instead of using a direct connection to the */
095:
096: /* host. They aren't used directly here. */
097:
098: /**
099: * @return if the networking layer should send ftp connections through
100: * a proxy
101: */
102: public static boolean getUseFtpProxy() {
103: // if the ftp.proxyHost is set, use it!
104: return (getFtpProxyHost() != null);
105: }
106:
107: /**
108: * @return the host to use, or null if none has been specified
109: */
110: public static String getFtpProxyHost() {
111: return (String) java.security.AccessController
112: .doPrivileged(new java.security.PrivilegedAction() {
113: public Object run() {
114: String result = System
115: .getProperty("ftp.proxyHost");
116: if (result == null) {
117: result = System.getProperty("ftpProxyHost");
118: }
119: if (result == null) {
120: // as a last resort we use the general one if ftp.useProxy
121: // is true
122: if (Boolean.getBoolean("ftp.useProxy")) {
123: result = System
124: .getProperty("proxyHost");
125: }
126: }
127: return result;
128: }
129: });
130: }
131:
132: /**
133: * @return the proxy port to use. Will default reasonably if not set.
134: */
135: public static int getFtpProxyPort() {
136: final int result[] = { 80 };
137: java.security.AccessController
138: .doPrivileged(new java.security.PrivilegedAction() {
139: public Object run() {
140: String tmp = System
141: .getProperty("ftp.proxyPort");
142: if (tmp == null) {
143: // for compatibility with 1.0.2
144: tmp = System.getProperty("ftpProxyPort");
145: }
146: if (tmp == null) {
147: // as a last resort we use the general one if ftp.useProxy
148: // is true
149: if (Boolean.getBoolean("ftp.useProxy")) {
150: tmp = System.getProperty("proxyPort");
151: }
152: }
153: if (tmp != null) {
154: result[0] = Integer.parseInt(tmp);
155: }
156: return null;
157: }
158: });
159: return result[0];
160: }
161:
162: /**
163: * issue the QUIT command to the FTP server and close the connection.
164: */
165: public void closeServer() throws IOException {
166: if (serverIsOpen()) {
167: issueCommand("QUIT");
168: super .closeServer();
169: }
170: }
171:
172: protected int issueCommand(String cmd) throws IOException {
173: command = cmd;
174: int reply;
175: if (replyPending) {
176: if (readReply() == FTP_ERROR)
177: System.out.print("Error reading FTP pending reply\n");
178: }
179: replyPending = false;
180: do {
181: sendServer(cmd + "\r\n");
182: reply = readReply();
183: } while (reply == FTP_TRY_AGAIN);
184: return reply;
185: }
186:
187: protected void issueCommandCheck(String cmd) throws IOException {
188: if (issueCommand(cmd) != FTP_SUCCESS)
189: throw new FtpProtocolException(cmd);
190: }
191:
192: protected int readReply() throws IOException {
193: lastReplyCode = readServerResponse();
194: switch (lastReplyCode / 100) {
195: case 1:
196: replyPending = true;
197:
198: /* falls into ... */
199:
200: case 2:
201: case 3:
202: return FTP_SUCCESS;
203:
204: case 5:
205: if (lastReplyCode == 530) {
206: if (user == null) {
207: throw new FtpLoginException("Not logged in");
208: }
209: return FTP_ERROR;
210: }
211: if (lastReplyCode == 550) {
212: throw new FileNotFoundException(command + ": "
213: + getResponseString());
214: }
215: }
216: /* this statement is not reached */
217: return FTP_ERROR;
218: }
219:
220: protected Socket openDataConnection(String cmd) throws IOException {
221: ServerSocket portSocket;
222: String portCmd;
223: InetAddress myAddress = InetAddress.getLocalHost();
224: byte addr[] = myAddress.getAddress();
225: int shift;
226: IOException e;
227: portSocket = new ServerSocket(0, 1);
228: portCmd = "PORT ";
229: /* append host addr */
230: for (int i = 0; i < addr.length; i++) {
231: portCmd = portCmd + (addr[i] & 0xFF) + ",";
232: }
233: /* append port number */
234: portCmd = portCmd + ((portSocket.getLocalPort() >>> 8) & 0xff)
235: + "," + (portSocket.getLocalPort() & 0xff);
236: if (issueCommand(portCmd) == FTP_ERROR) {
237: e = new FtpProtocolException("PORT");
238: portSocket.close();
239: throw e;
240: }
241: if (issueCommand(cmd) == FTP_ERROR) {
242: e = new FtpProtocolException(cmd);
243: portSocket.close();
244: throw e;
245: }
246: dataSocket = portSocket.accept();
247: portSocket.close();
248: return dataSocket;
249: }
250:
251: /* public methods */
252:
253: /** open a FTP connection to host <i>host</i>. */
254: public void openServer(String host) throws IOException {
255: int port = FTP_PORT;
256: /*
257: String source = Firewall.verifyAccess(host, port);
258:
259: if (source != null) {
260: Firewall.securityError("Applet at " +
261: source +
262: " tried to open FTP connection to "
263: + host + ":" + port);
264: return;
265: }
266: */
267: openServer(host, port);
268: }
269:
270: /** open a FTP connection to host <i>host</i> on port <i>port</i>. */
271: public void openServer(String host, int port) throws IOException {
272: /*
273: String source = Firewall.verifyAccess(host, port);
274:
275: if (source != null) {
276: Firewall.securityError("Applet at " +
277: source +
278: " tried to open FTP connection to "
279: + host + ":" + port);
280: return;
281: }
282: */
283: super .openServer(host, port);
284: if (readReply() == FTP_ERROR)
285: throw new FtpProtocolException("Welcome message");
286: }
287:
288: /**
289: * login user to a host with username <i>user</i> and password
290: * <i>password</i>
291: */
292: public void login(String user, String password) throws IOException {
293: /* It shouldn't send a password unless it
294: needs to. */
295:
296: if (!serverIsOpen())
297: throw new FtpLoginException("not connected to host");
298: this .user = user;
299: this .password = password;
300: if (issueCommand("USER " + user) == FTP_ERROR)
301: throw new FtpLoginException("user");
302: if (password != null
303: && issueCommand("PASS " + password) == FTP_ERROR)
304: throw new FtpLoginException("password");
305: // keep the welcome message around so we can
306: // put it in the resulting HTML page.
307: String l;
308: for (int i = 0; i < serverResponse.size(); i++) {
309: l = (String) serverResponse.elementAt(i);
310: if (l != null) {
311: if (l.charAt(3) != '-') {
312: break;
313: }
314: // get rid of the "230-" prefix
315: l = l.substring(4);
316: if (welcomeMsg == null) {
317: welcomeMsg = l;
318: } else {
319: welcomeMsg += l;
320: }
321: }
322: }
323: }
324:
325: /** GET a file from the FTP server */
326: public TelnetInputStream get(String filename) throws IOException {
327: Socket s;
328: try {
329: s = openDataConnection("RETR " + filename);
330: } catch (FileNotFoundException fileException) {
331: /* Well, "/" might not be the file delimitor for this
332: particular ftp server, so let's try a series of
333: "cd" commands to get to the right place. */
334: StringTokenizer t = new StringTokenizer(filename, "/");
335: String pathElement = null;
336: while (t.hasMoreElements()) {
337: pathElement = t.nextToken();
338: if (!t.hasMoreElements()) {
339: /* This is the file component. Look it up now. */
340: break;
341: }
342: try {
343: cd(pathElement);
344: } catch (FtpProtocolException e) {
345: /* Giving up. */
346: throw fileException;
347: }
348: }
349: if (pathElement != null) {
350: s = openDataConnection("RETR " + pathElement);
351: } else {
352: throw fileException;
353: }
354: }
355: return new FtpInputStream(this , s.getInputStream(), binaryMode);
356: }
357:
358: /** PUT a file to the FTP server */
359: public TelnetOutputStream put(String filename) throws IOException {
360: Socket s = openDataConnection("STOR " + filename);
361: return new TelnetOutputStream(s.getOutputStream(), binaryMode);
362: }
363:
364: /** LIST files on a remote FTP server */
365: public TelnetInputStream list() throws IOException {
366: Socket s = openDataConnection("LIST");
367: return new TelnetInputStream(s.getInputStream(), binaryMode);
368: }
369:
370: /** CD to a specific directory on a remote FTP server */
371: public void cd(String remoteDirectory) throws IOException {
372: issueCommandCheck("CWD " + remoteDirectory);
373: }
374:
375: /** Set transfer type to 'I' */
376: public void binary() throws IOException {
377: issueCommandCheck("TYPE I");
378: binaryMode = true;
379: }
380:
381: /** Set transfer type to 'A' */
382: public void ascii() throws IOException {
383: issueCommandCheck("TYPE A");
384: binaryMode = false;
385: }
386:
387: /** New FTP client connected to host <i>host</i>. */
388: public FtpClient(String host) throws IOException {
389: super ();
390: openServer(host, FTP_PORT);
391: }
392:
393: /** New FTP client connected to host <i>host</i>, port <i>port</i>. */
394: public FtpClient(String host, int port) throws IOException {
395: super ();
396: openServer(host, port);
397: }
398:
399: /** Create an uninitialized FTP client. */
400: public FtpClient() {
401: }
402: }
|