6893702: Overhaul of Ftp Client internal code

Major reorg of internal FTP client code

Reviewed-by: chegar
This commit is contained in:
Jean-Christophe Collet 2009-10-21 16:28:57 +02:00
parent 48792ee40c
commit 196a0c5cf8
11 changed files with 4026 additions and 802 deletions

View File

@ -45,8 +45,14 @@ FILES_java = \
sun/net/dns/ResolverConfiguration.java \ sun/net/dns/ResolverConfiguration.java \
sun/net/dns/ResolverConfigurationImpl.java \ sun/net/dns/ResolverConfigurationImpl.java \
sun/net/ftp/FtpClient.java \ sun/net/ftp/FtpClient.java \
sun/net/ftp/FtpClientProvider.java \
sun/net/ftp/FtpDirEntry.java \
sun/net/ftp/FtpReplyCode.java \
sun/net/ftp/FtpDirParser.java \
sun/net/ftp/FtpLoginException.java \ sun/net/ftp/FtpLoginException.java \
sun/net/ftp/FtpProtocolException.java \ sun/net/ftp/FtpProtocolException.java \
sun/net/ftp/impl/FtpClient.java \
sun/net/ftp/impl/DefaultFtpClientProvider.java \
sun/net/spi/DefaultProxySelector.java \ sun/net/spi/DefaultProxySelector.java \
sun/net/spi/nameservice/NameServiceDescriptor.java \ sun/net/spi/nameservice/NameServiceDescriptor.java \
sun/net/spi/nameservice/NameService.java \ sun/net/spi/nameservice/NameService.java \

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,158 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.net.ftp;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ServiceConfigurationError;
//import sun.misc.Service;
/**
* Service provider class for FtpClient.
* Sub-classes of FtpClientProvider provide an implementation of {@link FtpClient}
* and associated classes. Applications do not normally use this class directly.
* See {@link #provider() } for how providers are found and loaded.
*
* @since 1.7
*/
public abstract class FtpClientProvider {
/**
* Creates a FtpClient from this provider.
*
* @return The created {@link FtpClient}.
*/
public abstract FtpClient createFtpClient();
private static final Object lock = new Object();
private static FtpClientProvider provider = null;
/**
* Initializes a new instance of this class.
*
* @throws SecurityException if a security manager is installed and it denies
* {@link RuntimePermission}<tt>("ftpClientProvider")</tt>
*/
protected FtpClientProvider() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("ftpClientProvider"));
}
}
private static boolean loadProviderFromProperty() {
String cm = System.getProperty("sun.net.ftpClientProvider");
if (cm == null) {
return false;
}
try {
Class c = Class.forName(cm, true, null);
provider = (FtpClientProvider) c.newInstance();
return true;
} catch (ClassNotFoundException x) {
throw new ServiceConfigurationError(x.toString());
} catch (IllegalAccessException x) {
throw new ServiceConfigurationError(x.toString());
} catch (InstantiationException x) {
throw new ServiceConfigurationError(x.toString());
} catch (SecurityException x) {
throw new ServiceConfigurationError(x.toString());
}
}
private static boolean loadProviderAsService() {
// Iterator i = Service.providers(FtpClientProvider.class,
// ClassLoader.getSystemClassLoader());
// while (i.hasNext()) {
// try {
// provider = (FtpClientProvider) i.next();
// return true;
// } catch (ServiceConfigurationError sce) {
// if (sce.getCause() instanceof SecurityException) {
// // Ignore, try next provider, if any
// continue;
// }
// throw sce;
// }
// }
return false;
}
/**
* Returns the system wide default FtpClientProvider for this invocation of
* the Java virtual machine.
*
* <p> The first invocation of this method locates the default provider
* object as follows: </p>
*
* <ol>
*
* <li><p> If the system property
* <tt>java.net.FtpClientProvider</tt> is defined then it is
* taken to be the fully-qualified name of a concrete provider class.
* The class is loaded and instantiated; if this process fails then an
* unspecified unchecked error or exception is thrown. </p></li>
*
* <li><p> If a provider class has been installed in a jar file that is
* visible to the system class loader, and that jar file contains a
* provider-configuration file named
* <tt>java.net.FtpClientProvider</tt> in the resource
* directory <tt>META-INF/services</tt>, then the first class name
* specified in that file is taken. The class is loaded and
* instantiated; if this process fails then an unspecified unchecked error or exception is
* thrown. </p></li>
*
* <li><p> Finally, if no provider has been specified by any of the above
* means then the system-default provider class is instantiated and the
* result is returned. </p></li>
*
* </ol>
*
* <p> Subsequent invocations of this method return the provider that was
* returned by the first invocation. </p>
*
* @return The system-wide default FtpClientProvider
*/
public static FtpClientProvider provider() {
synchronized (lock) {
if (provider != null) {
return provider;
}
return (FtpClientProvider) AccessController.doPrivileged(
new PrivilegedAction<Object>() {
public Object run() {
if (loadProviderFromProperty()) {
return provider;
}
if (loadProviderAsService()) {
return provider;
}
provider = new sun.net.ftp.impl.DefaultFtpClientProvider();
return provider;
}
});
}
}
}

View File

@ -0,0 +1,331 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.net.ftp;
import java.util.Date;
import java.util.HashMap;
/**
* A {@code FtpDirEntry} is a class agregating all the information that the FTP client
* can gather from the server by doing a {@code LST} (or {@code NLST}) command and
* parsing the output. It will typically contain the name, type, size, last modification
* time, owner and group of the file, although some of these could be unavailable
* due to specific FTP server limitations.
*
* @see sun.net.ftp.FtpDirParser
* @since 1.7
*/
public class FtpDirEntry {
public enum Type {
FILE, DIR, PDIR, CDIR, LINK
};
public enum Permission {
USER(0), GROUP(1), OTHERS(2);
int value;
Permission(int v) {
value = v;
}
};
private final String name;
private String user = null;
private String group = null;
private long size = -1;
private java.util.Date created = null;
private java.util.Date lastModified = null;
private Type type = Type.FILE;
private boolean[][] permissions = null;
private HashMap<String, String> facts = new HashMap<String, String>();
private FtpDirEntry() {
name = null;
}
/**
* Creates an FtpDirEntry instance with only the name being set.
*
* @param name The name of the file
*/
public FtpDirEntry(String name) {
this.name = name;
}
/**
* Returns the name of the remote file.
*
* @return a {@code String} containing the name of the remote file.
*/
public String getName() {
return name;
}
/**
* Returns the user name of the owner of the file as returned by the FTP
* server, if provided. This could be a name or a user id (number).
*
* @return a {@code String} containing the user name or
* {@code null} if that information is not available.
*/
public String getUser() {
return user;
}
/**
* Sets the user name of the owner of the file. Intended mostly to be
* used from inside a {@link java.net.FtpDirParser} implementation.
*
* @param user The user name of the owner of the file, or {@code null}
* if that information is not available.
* @return this FtpDirEntry
*/
public FtpDirEntry setUser(String user) {
this.user = user;
return this;
}
/**
* Returns the group name of the file as returned by the FTP
* server, if provided. This could be a name or a group id (number).
*
* @return a {@code String} containing the group name or
* {@code null} if that information is not available.
*/
public String getGroup() {
return group;
}
/**
* Sets the name of the group to which the file belong. Intended mostly to be
* used from inside a {@link java.net.FtpDirParser} implementation.
*
* @param group The name of the group to which the file belong, or {@code null}
* if that information is not available.
* @return this FtpDirEntry
*/
public FtpDirEntry setGroup(String group) {
this.group = group;
return this;
}
/**
* Returns the size of the remote file as it was returned by the FTP
* server, if provided.
*
* @return the size of the file or -1 if that information is not available.
*/
public long getSize() {
return size;
}
/**
* Sets the size of that file. Intended mostly to be used from inside an
* {@link java.net.FtpDirParser} implementation.
*
* @param size The size, in bytes, of that file. or -1 if unknown.
* @return this FtpDirEntry
*/
public FtpDirEntry setSize(long size) {
this.size = size;
return this;
}
/**
* Returns the type of the remote file as it was returned by the FTP
* server, if provided.
* It returns a FtpDirEntry.Type enum and the values can be:
* - FtpDirEntry.Type.FILE for a normal file
* - FtpDirEntry.Type.DIR for a directory
* - FtpDirEntry.Type.LINK for a symbolic link
*
* @return a {@code FtpDirEntry.Type} describing the type of the file
* or {@code null} if that information is not available.
*/
public Type getType() {
return type;
}
/**
* Sets the type of the file. Intended mostly to be used from inside an
* {@link java.net.FtpDirParser} implementation.
*
* @param type the type of this file or {@code null} if that information
* is not available.
* @return this FtpDirEntry
*/
public FtpDirEntry setType(Type type) {
this.type = type;
return this;
}
/**
* Returns the last modification time of the remote file as it was returned
* by the FTP server, if provided, {@code null} otherwise.
*
* @return a <code>Date</code> representing the last time the file was
* modified on the server, or {@code null} if that
* information is not available.
*/
public java.util.Date getLastModified() {
return this.lastModified;
}
/**
* Sets the last modification time of the file. Intended mostly to be used
* from inside an {@link java.net.FtpDirParser} implementation.
*
* @param lastModified The Date representing the last modification time, or
* {@code null} if that information is not available.
* @return this FtpDirEntry
*/
public FtpDirEntry setLastModified(Date lastModified) {
this.lastModified = lastModified;
return this;
}
/**
* Returns whether read access is granted for a specific permission.
*
* @param p the Permission (user, group, others) to check.
* @return {@code true} if read access is granted.
*/
public boolean canRead(Permission p) {
if (permissions != null) {
return permissions[p.value][0];
}
return false;
}
/**
* Returns whether write access is granted for a specific permission.
*
* @param p the Permission (user, group, others) to check.
* @return {@code true} if write access is granted.
*/
public boolean canWrite(Permission p) {
if (permissions != null) {
return permissions[p.value][1];
}
return false;
}
/**
* Returns whether execute access is granted for a specific permission.
*
* @param p the Permission (user, group, others) to check.
* @return {@code true} if execute access is granted.
*/
public boolean canExexcute(Permission p) {
if (permissions != null) {
return permissions[p.value][2];
}
return false;
}
/**
* Sets the permissions for that file. Intended mostly to be used
* from inside an {@link java.net.FtpDirParser} implementation.
* The permissions array is a 3x3 {@code boolean} array, the first index being
* the User, group or owner (0, 1 and 2 respectively) while the second
* index is read, write or execute (0, 1 and 2 respectively again).
* <p>E.G.: {@code permissions[1][2]} is the group/execute permission.</p>
*
* @param permissions a 3x3 {@code boolean} array
* @return this {@code FtpDirEntry}
*/
public FtpDirEntry setPermissions(boolean[][] permissions) {
this.permissions = permissions;
return this;
}
/**
* Adds a 'fact', as defined in RFC 3659, to the list of facts of this file.
* Intended mostly to be used from inside a {@link java.net.FtpDirParser}
* implementation.
*
* @param fact the name of the fact (e.g. "Media-Type"). It is not case-sensitive.
* @param value the value associated with this fact.
* @return this {@code FtpDirEntry}
*/
public FtpDirEntry addFact(String fact, String value) {
facts.put(fact.toLowerCase(), value);
return this;
}
/**
* Returns the requested 'fact', as defined in RFC 3659, if available.
*
* @param fact The name of the fact *e.g. "Media-Type"). It is not case sensitive.
* @return The value of the fact or, {@code null} if that fact wasn't
* provided by the server.
*/
public String getFact(String fact) {
return facts.get(fact.toLowerCase());
}
/**
* Returns the creation time of the file, when provided by the server.
*
* @return The Date representing the creation time, or {@code null}
* if the server didn't provide that information.
*/
public Date getCreated() {
return created;
}
/**
* Sets the creation time for that file. Intended mostly to be used from
* inside a {@link java.net.FtpDirParser} implementation.
*
* @param created the Date representing the creation time for that file, or
* {@code null} if that information is not available.
* @return this FtpDirEntry
*/
public FtpDirEntry setCreated(Date created) {
this.created = created;
return this;
}
/**
* Returns a string representation of the object.
* The {@code toString} method for class {@code FtpDirEntry}
* returns a string consisting of the name of the file, followed by its
* type between brackets, followed by the user and group between
* parenthesis, then size between '{', and, finally, the lastModified of last
* modification if it's available.
*
* @return a string representation of the object.
*/
@Override
public String toString() {
if (lastModified == null) {
return name + " [" + type + "] (" + user + " / " + group + ") " + size;
}
return name + " [" + type + "] (" + user + " / " + group + ") {" + size + "} " + java.text.DateFormat.getDateInstance().format(lastModified);
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.net.ftp;
/**
* This interface describes a parser for the FtpClient class. Such a parser is
* used when listing a remote directory to transform text lines like:
* drwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog
* into FtpDirEntry instances.
*
* @see java.net.FtpClient#setFileParser(FtpDirParser)
* @since 1.7
*/
public interface FtpDirParser {
/**
* Takes one line from a directory listing and returns an FtpDirEntry instance
* based on the information contained.
*
* @param line a <code>String</code>, a line sent by the FTP server as a
* result of the LST command.
* @return an <code>FtpDirEntry</code> instance.
* @see java.net.FtpDirEntry
*/
public FtpDirEntry parseLine(String line);
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright 1994-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -25,7 +25,7 @@
package sun.net.ftp; package sun.net.ftp;
import java.io.*; import java.io.IOException;
/** /**
* This exception is thrown when an error is encountered during an * This exception is thrown when an error is encountered during an
@ -33,10 +33,10 @@ import java.io.*;
* *
* @author Jonathan Payne * @author Jonathan Payne
*/ */
public class FtpLoginException extends FtpProtocolException { public class FtpLoginException extends IOException {
private static final long serialVersionUID = 2218162403237941536L; private static final long serialVersionUID = 2218162403237941536L;
FtpLoginException(String s) { public FtpLoginException(String s) {
super(s); super(s);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright 1994-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -22,21 +22,49 @@
* CA 95054 USA or visit www.sun.com if you need additional information or * CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions. * have any questions.
*/ */
package sun.net.ftp; package sun.net.ftp;
import java.io.*;
/** /**
* This exeception is thrown when unexpected results are returned during * Thrown to indicate that the FTP server reported an error.
* an FTP session. * For instance that the requested file doesn't exist or
* * that a command isn't supported.
* <p>The specific error code can be retreived with {@link #getReplyCode() }.</p>
* @author Jonathan Payne * @author Jonathan Payne
*/ */
public class FtpProtocolException extends IOException { public class FtpProtocolException extends Exception {
private static final long serialVersionUID = 5978077070276545054L; private static final long serialVersionUID = 5978077070276545054L;
private final FtpReplyCode code;
FtpProtocolException(String s) { /**
super(s); * Constructs a new {@code FtpProtocolException} from the
* specified detail message. The reply code is set to unknow error.
*
* @param detail the detail message.
*/
public FtpProtocolException(String detail) {
super(detail);
code = FtpReplyCode.UNKNOWN_ERROR;
}
/**
* Constructs a new {@code FtpProtocolException} from the
* specified response code and exception detail message
*
* @param detail the detail message.
* @param code The {@code FtpRelyCode} received from server.
*/
public FtpProtocolException(String detail, FtpReplyCode code) {
super(detail);
this.code = code;
}
/**
* Gets the reply code sent by the server that led to this exception
* being thrown.
*
* @return The {@link FtpReplyCode} associated with that exception.
*/
public FtpReplyCode getReplyCode() {
return code;
} }
} }

View File

@ -0,0 +1,248 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.net.ftp;
/**
* This class describes a FTP protocol reply code and associates a meaning
* to the numerical value according to the various RFCs (RFC 959 in
* particular).
*
*/
public enum FtpReplyCode {
RESTART_MARKER(110),
SERVICE_READY_IN(120),
DATA_CONNECTION_ALREADY_OPEN(125),
FILE_STATUS_OK(150),
COMMAND_OK(200),
NOT_IMPLEMENTED(202),
SYSTEM_STATUS(211),
DIRECTORY_STATUS(212),
FILE_STATUS(213),
HELP_MESSAGE(214),
NAME_SYSTEM_TYPE(215),
SERVICE_READY(220),
SERVICE_CLOSING(221),
DATA_CONNECTION_OPEN(225),
CLOSING_DATA_CONNECTION(226),
ENTERING_PASSIVE_MODE(227),
ENTERING_EXT_PASSIVE_MODE(229),
LOGGED_IN(230),
SECURELY_LOGGED_IN(232),
SECURITY_EXCHANGE_OK(234),
SECURITY_EXCHANGE_COMPLETE(235),
FILE_ACTION_OK(250),
PATHNAME_CREATED(257),
NEED_PASSWORD(331),
NEED_ACCOUNT(332),
NEED_ADAT(334),
NEED_MORE_ADAT(335),
FILE_ACTION_PENDING(350),
SERVICE_NOT_AVAILABLE(421),
CANT_OPEN_DATA_CONNECTION(425),
CONNECTION_CLOSED(426),
NEED_SECURITY_RESOURCE(431),
FILE_ACTION_NOT_TAKEN(450),
ACTION_ABORTED(451),
INSUFFICIENT_STORAGE(452),
COMMAND_UNRECOGNIZED(500),
INVALID_PARAMETER(501),
BAD_SEQUENCE(503),
NOT_IMPLEMENTED_FOR_PARAMETER(504),
NOT_LOGGED_IN(530),
NEED_ACCOUNT_FOR_STORING(532),
PROT_LEVEL_DENIED(533),
REQUEST_DENIED(534),
FAILED_SECURITY_CHECK(535),
UNSUPPORTED_PROT_LEVEL(536),
PROT_LEVEL_NOT_SUPPORTED_BY_SECURITY(537),
FILE_UNAVAILABLE(550),
PAGE_TYPE_UNKNOWN(551),
EXCEEDED_STORAGE(552),
FILE_NAME_NOT_ALLOWED(553),
PROTECTED_REPLY(631),
UNKNOWN_ERROR(999);
private final int value;
FtpReplyCode(int val) {
this.value = val;
}
/**
* Returns the numerical value of the code.
*
* @return the numerical value.
*/
public int getValue() {
return value;
}
/**
* Determines if the code is a Positive Preliminary response.
* This means beginning with a 1 (which means a value between 100 and 199)
*
* @return <code>true</code> if the reply code is a positive preliminary
* response.
*/
public boolean isPositivePreliminary() {
return value >= 100 && value < 200;
}
/**
* Determines if the code is a Positive Completion response.
* This means beginning with a 2 (which means a value between 200 and 299)
*
* @return <code>true</code> if the reply code is a positive completion
* response.
*/
public boolean isPositiveCompletion() {
return value >= 200 && value < 300;
}
/**
* Determines if the code is a positive internediate response.
* This means beginning with a 3 (which means a value between 300 and 399)
*
* @return <code>true</code> if the reply code is a positive intermediate
* response.
*/
public boolean isPositiveIntermediate() {
return value >= 300 && value < 400;
}
/**
* Determines if the code is a transient negative response.
* This means beginning with a 4 (which means a value between 400 and 499)
*
* @return <code>true</code> if the reply code is a transient negative
* response.
*/
public boolean isTransientNegative() {
return value >= 400 && value < 500;
}
/**
* Determines if the code is a permanent negative response.
* This means beginning with a 5 (which means a value between 500 and 599)
*
* @return <code>true</code> if the reply code is a permanent negative
* response.
*/
public boolean isPermanentNegative() {
return value >= 500 && value < 600;
}
/**
* Determines if the code is a protected reply response.
* This means beginning with a 6 (which means a value between 600 and 699)
*
* @return <code>true</code> if the reply code is a protected reply
* response.
*/
public boolean isProtectedReply() {
return value >= 600 && value < 700;
}
/**
* Determines if the code is a syntax related response.
* This means the second digit is a 0.
*
* @return <code>true</code> if the reply code is a syntax related
* response.
*/
public boolean isSyntax() {
return ((value / 10) - ((value / 100) * 10)) == 0;
}
/**
* Determines if the code is an information related response.
* This means the second digit is a 1.
*
* @return <code>true</code> if the reply code is an information related
* response.
*/
public boolean isInformation() {
return ((value / 10) - ((value / 100) * 10)) == 1;
}
/**
* Determines if the code is a connection related response.
* This means the second digit is a 2.
*
* @return <code>true</code> if the reply code is a connection related
* response.
*/
public boolean isConnection() {
return ((value / 10) - ((value / 100) * 10)) == 2;
}
/**
* Determines if the code is an authentication related response.
* This means the second digit is a 3.
*
* @return <code>true</code> if the reply code is an authentication related
* response.
*/
public boolean isAuthentication() {
return ((value / 10) - ((value / 100) * 10)) == 3;
}
/**
* Determines if the code is an unspecified type of response.
* This means the second digit is a 4.
*
* @return <code>true</code> if the reply code is an unspecified type of
* response.
*/
public boolean isUnspecified() {
return ((value / 10) - ((value / 100) * 10)) == 4;
}
/**
* Determines if the code is a file system related response.
* This means the second digit is a 5.
*
* @return <code>true</code> if the reply code is a file system related
* response.
*/
public boolean isFileSystem() {
return ((value / 10) - ((value / 100) * 10)) == 5;
}
/**
* Static utility method to convert a value into a FtpReplyCode.
*
* @param v the value to convert
* @return the <code>FtpReplyCode</code> associated with the value.
*/
public static FtpReplyCode find(int v) {
for (FtpReplyCode code : FtpReplyCode.values()) {
if (code.getValue() == v) {
return code;
}
}
return UNKNOWN_ERROR;
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.net.ftp.impl;
/**
* Default FtpClientProvider.
* Uses sun.net.ftp.FtpCLient.
*/
public class DefaultFtpClientProvider extends sun.net.ftp.FtpClientProvider {
@Override
public sun.net.ftp.FtpClient createFtpClient() {
return sun.net.ftp.impl.FtpClient.create();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright 1994-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -37,10 +37,8 @@ import java.io.FilterInputStream;
import java.io.FilterOutputStream; import java.io.FilterOutputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.net.URL; import java.net.URL;
import java.net.URLStreamHandler;
import java.net.SocketPermission; import java.net.SocketPermission;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.net.MalformedURLException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.URI; import java.net.URI;
import java.net.Proxy; import java.net.Proxy;
@ -84,7 +82,6 @@ public class FtpURLConnection extends URLConnection {
// In case we have to use proxies, we use HttpURLConnection // In case we have to use proxies, we use HttpURLConnection
HttpURLConnection http = null; HttpURLConnection http = null;
private Proxy instProxy; private Proxy instProxy;
Proxy proxy = null;
InputStream is = null; InputStream is = null;
OutputStream os = null; OutputStream os = null;
@ -125,12 +122,11 @@ public class FtpURLConnection extends URLConnection {
ftp = cl; ftp = cl;
} }
@Override
public void close() throws IOException { public void close() throws IOException {
super.close(); super.close();
try { if (ftp != null) {
if (ftp != null) ftp.close();
ftp.closeServer();
} catch (IOException ex) {
} }
} }
} }
@ -149,12 +145,11 @@ public class FtpURLConnection extends URLConnection {
ftp = cl; ftp = cl;
} }
@Override
public void close() throws IOException { public void close() throws IOException {
super.close(); super.close();
try { if (ftp != null) {
if (ftp != null) ftp.close();
ftp.closeServer();
} catch (IOException ex) {
} }
} }
} }
@ -192,12 +187,14 @@ public class FtpURLConnection extends URLConnection {
private void setTimeouts() { private void setTimeouts() {
if (ftp != null) { if (ftp != null) {
if (connectTimeout >= 0) if (connectTimeout >= 0) {
ftp.setConnectTimeout(connectTimeout); ftp.setConnectTimeout(connectTimeout);
if (readTimeout >= 0) }
if (readTimeout >= 0) {
ftp.setReadTimeout(readTimeout); ftp.setReadTimeout(readTimeout);
} }
} }
}
/** /**
* Connects to the FTP server and logs in. * Connects to the FTP server and logs in.
@ -229,8 +226,9 @@ public class FtpURLConnection extends URLConnection {
while (it.hasNext()) { while (it.hasNext()) {
p = it.next(); p = it.next();
if (p == null || p == Proxy.NO_PROXY || if (p == null || p == Proxy.NO_PROXY ||
p.type() == Proxy.Type.SOCKS) p.type() == Proxy.Type.SOCKS) {
break; break;
}
if (p.type() != Proxy.Type.HTTP || if (p.type() != Proxy.Type.HTTP ||
!(p.address() instanceof InetSocketAddress)) { !(p.address() instanceof InetSocketAddress)) {
sel.connectFailed(uri, p.address(), new IOException("Wrong proxy type")); sel.connectFailed(uri, p.address(), new IOException("Wrong proxy type"));
@ -240,10 +238,14 @@ public class FtpURLConnection extends URLConnection {
InetSocketAddress paddr = (InetSocketAddress) p.address(); InetSocketAddress paddr = (InetSocketAddress) p.address();
try { try {
http = new HttpURLConnection(url, p); http = new HttpURLConnection(url, p);
if (connectTimeout >= 0) http.setDoInput(getDoInput());
http.setDoOutput(getDoOutput());
if (connectTimeout >= 0) {
http.setConnectTimeout(connectTimeout); http.setConnectTimeout(connectTimeout);
if (readTimeout >= 0) }
if (readTimeout >= 0) {
http.setReadTimeout(readTimeout); http.setReadTimeout(readTimeout);
}
http.connect(); http.connect();
connected = true; connected = true;
return; return;
@ -257,10 +259,14 @@ public class FtpURLConnection extends URLConnection {
p = instProxy; p = instProxy;
if (p.type() == Proxy.Type.HTTP) { if (p.type() == Proxy.Type.HTTP) {
http = new HttpURLConnection(url, instProxy); http = new HttpURLConnection(url, instProxy);
if (connectTimeout >= 0) http.setDoInput(getDoInput());
http.setDoOutput(getDoOutput());
if (connectTimeout >= 0) {
http.setConnectTimeout(connectTimeout); http.setConnectTimeout(connectTimeout);
if (readTimeout >= 0) }
if (readTimeout >= 0) {
http.setReadTimeout(readTimeout); http.setReadTimeout(readTimeout);
}
http.connect(); http.connect();
connected = true; connected = true;
return; return;
@ -276,25 +282,29 @@ public class FtpURLConnection extends URLConnection {
"Java" + vers + "@")); "Java" + vers + "@"));
} }
try { try {
if (p != null) ftp = FtpClient.create();
ftp = new FtpClient(p); if (p != null) {
else ftp.setProxy(p);
ftp = new FtpClient(); }
setTimeouts(); setTimeouts();
if (port != -1) if (port != -1) {
ftp.openServer(host, port); ftp.connect(new InetSocketAddress(host, port));
else } else {
ftp.openServer(host); ftp.connect(new InetSocketAddress(host, FtpClient.defaultPort()));
}
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
// Maybe do something smart here, like use a proxy like iftp. // Maybe do something smart here, like use a proxy like iftp.
// Just keep throwing for now. // Just keep throwing for now.
throw e; throw e;
} catch (FtpProtocolException fe) {
throw new IOException(fe);
} }
try { try {
ftp.login(user, password); ftp.login(user, password.toCharArray());
} catch (sun.net.ftp.FtpLoginException e) { } catch (sun.net.ftp.FtpProtocolException e) {
ftp.closeServer(); ftp.close();
throw e; // Backward compatibility
throw new sun.net.ftp.FtpLoginException("Invalid username/password");
} }
connected = true; connected = true;
} }
@ -307,19 +317,24 @@ public class FtpURLConnection extends URLConnection {
int i = path.indexOf(";type="); int i = path.indexOf(";type=");
if (i >= 0) { if (i >= 0) {
String s1 = path.substring(i + 6, path.length()); String s1 = path.substring(i + 6, path.length());
if ("i".equalsIgnoreCase(s1)) if ("i".equalsIgnoreCase(s1)) {
type = BIN; type = BIN;
if ("a".equalsIgnoreCase(s1)) }
if ("a".equalsIgnoreCase(s1)) {
type = ASCII; type = ASCII;
if ("d".equalsIgnoreCase(s1)) }
if ("d".equalsIgnoreCase(s1)) {
type = DIR; type = DIR;
}
path = path.substring(0, i); path = path.substring(0, i);
} }
if (path != null && path.length() > 1 && if (path != null && path.length() > 1 &&
path.charAt(0) == '/') path.charAt(0) == '/') {
path = path.substring(1); path = path.substring(1);
if (path == null || path.length() == 0) }
if (path == null || path.length() == 0) {
path = "./"; path = "./";
}
if (!path.endsWith("/")) { if (!path.endsWith("/")) {
i = path.lastIndexOf('/'); i = path.lastIndexOf('/');
if (i > 0) { if (i > 0) {
@ -334,11 +349,12 @@ public class FtpURLConnection extends URLConnection {
pathname = path.substring(0, path.length() - 1); pathname = path.substring(0, path.length() - 1);
filename = null; filename = null;
} }
if (pathname != null) if (pathname != null) {
fullpath = pathname + "/" + (filename != null ? filename : ""); fullpath = pathname + "/" + (filename != null ? filename : "");
else } else {
fullpath = filename; fullpath = filename;
} }
}
/* /*
* As part of RFC-1738 it is specified that the path should be * As part of RFC-1738 it is specified that the path should be
@ -346,18 +362,19 @@ public class FtpURLConnection extends URLConnection {
* This is because, '/' is not necessarly the directory delimiter * This is because, '/' is not necessarly the directory delimiter
* on every systems. * on every systems.
*/ */
private void cd(String path) throws FtpProtocolException, IOException {
private void cd(String path) throws IOException { if (path == null || path.isEmpty()) {
if (path == null || "".equals(path))
return; return;
}
if (path.indexOf('/') == -1) { if (path.indexOf('/') == -1) {
ftp.cd(ParseUtil.decode(path)); ftp.changeDirectory(ParseUtil.decode(path));
return; return;
} }
StringTokenizer token = new StringTokenizer(path, "/"); StringTokenizer token = new StringTokenizer(path, "/");
while (token.hasMoreTokens()) while (token.hasMoreTokens()) {
ftp.cd(ParseUtil.decode(token.nextToken())); ftp.changeDirectory(ParseUtil.decode(token.nextToken()));
}
} }
/** /**
@ -369,16 +386,19 @@ public class FtpURLConnection extends URLConnection {
* @throws IOException if already opened for output * @throws IOException if already opened for output
* @throws FtpProtocolException if errors occur during the transfert. * @throws FtpProtocolException if errors occur during the transfert.
*/ */
@Override
public InputStream getInputStream() throws IOException { public InputStream getInputStream() throws IOException {
if (!connected) { if (!connected) {
connect(); connect();
} }
if (http != null) if (http != null) {
return http.getInputStream(); return http.getInputStream();
}
if (os != null) if (os != null) {
throw new IOException("Already opened for output"); throw new IOException("Already opened for output");
}
if (is != null) { if (is != null) {
return is; return is;
@ -386,38 +406,31 @@ public class FtpURLConnection extends URLConnection {
MessageHeader msgh = new MessageHeader(); MessageHeader msgh = new MessageHeader();
boolean isAdir = false;
try { try {
decodePath(url.getPath()); decodePath(url.getPath());
if (filename == null || type == DIR) { if (filename == null || type == DIR) {
ftp.ascii(); ftp.setAsciiType();
cd(pathname); cd(pathname);
if (filename == null) if (filename == null) {
is = new FtpInputStream(ftp, ftp.list()); is = new FtpInputStream(ftp, ftp.list(null));
else
is = new FtpInputStream(ftp, ftp.nameList(filename));
} else { } else {
if (type == ASCII) is = new FtpInputStream(ftp, ftp.nameList(filename));
ftp.ascii(); }
else } else {
ftp.binary(); if (type == ASCII) {
ftp.setAsciiType();
} else {
ftp.setBinaryType();
}
cd(pathname); cd(pathname);
is = new FtpInputStream(ftp, ftp.get(filename)); is = new FtpInputStream(ftp, ftp.getFileStream(filename));
} }
/* Try to get the size of the file in bytes. If that is /* Try to get the size of the file in bytes. If that is
successful, then create a MeteredStream. */ successful, then create a MeteredStream. */
try { try {
String response = ftp.getResponseString(); long l = ftp.getLastTransferSize();
int offset;
if ((offset = response.indexOf(" bytes)")) != -1) {
int i = offset;
int c;
while (--i >= 0 && ((c = response.charAt(i)) >= '0'
&& c <= '9'))
;
long l = Long.parseLong(response.substring(i + 1, offset));
msgh.add("content-length", Long.toString(l)); msgh.add("content-length", Long.toString(l));
if (l > 0) { if (l > 0) {
@ -435,19 +448,24 @@ public class FtpURLConnection extends URLConnection {
is = new MeteredStream(is, pi, l); is = new MeteredStream(is, pi, l);
} }
}
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
/* do nothing, since all we were doing was trying to /* do nothing, since all we were doing was trying to
get the size in bytes of the file */ get the size in bytes of the file */
} }
String type = guessContentTypeFromName(fullpath); if (isAdir) {
if (type == null && is.markSupported()) { msgh.add("content-type", "text/plain");
type = guessContentTypeFromStream(is); msgh.add("access-type", "directory");
} else {
msgh.add("access-type", "file");
String ftype = guessContentTypeFromName(fullpath);
if (ftype == null && is.markSupported()) {
ftype = guessContentTypeFromStream(is);
}
if (ftype != null) {
msgh.add("content-type", ftype);
} }
if (type != null) {
msgh.add("content-type", type);
} }
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
try { try {
@ -455,13 +473,18 @@ public class FtpURLConnection extends URLConnection {
/* if that worked, then make a directory listing /* if that worked, then make a directory listing
and build an html stream with all the files in and build an html stream with all the files in
the directory */ the directory */
ftp.ascii(); ftp.setAsciiType();
is = new FtpInputStream(ftp, ftp.list()); is = new FtpInputStream(ftp, ftp.list(null));
msgh.add("content-type", "text/plain"); msgh.add("content-type", "text/plain");
msgh.add("access-type", "directory");
} catch (IOException ex) { } catch (IOException ex) {
throw new FileNotFoundException(fullpath); throw new FileNotFoundException(fullpath);
} catch (FtpProtocolException ex2) {
throw new FileNotFoundException(fullpath);
} }
} catch (FtpProtocolException ftpe) {
throw new IOException(ftpe);
} }
setProperties(msgh); setProperties(msgh);
return is; return is;
@ -477,31 +500,45 @@ public class FtpURLConnection extends URLConnection {
* points to a directory * points to a directory
* @throws FtpProtocolException if errors occur during the transfert. * @throws FtpProtocolException if errors occur during the transfert.
*/ */
@Override
public OutputStream getOutputStream() throws IOException { public OutputStream getOutputStream() throws IOException {
if (!connected) { if (!connected) {
connect(); connect();
} }
if (http != null) if (http != null) {
return http.getOutputStream(); OutputStream out = http.getOutputStream();
// getInputStream() is neccessary to force a writeRequests()
// on the http client.
http.getInputStream();
return out;
}
if (is != null) if (is != null) {
throw new IOException("Already opened for input"); throw new IOException("Already opened for input");
}
if (os != null) { if (os != null) {
return os; return os;
} }
decodePath(url.getPath()); decodePath(url.getPath());
if (filename == null || filename.length() == 0) if (filename == null || filename.length() == 0) {
throw new IOException("illegal filename for a PUT"); throw new IOException("illegal filename for a PUT");
if (pathname != null) }
try {
if (pathname != null) {
cd(pathname); cd(pathname);
if (type == ASCII) }
ftp.ascii(); if (type == ASCII) {
else ftp.setAsciiType();
ftp.binary(); } else {
os = new FtpOutputStream(ftp, ftp.put(filename)); ftp.setBinaryType();
}
os = new FtpOutputStream(ftp, ftp.putFileStream(filename, false));
} catch (FtpProtocolException e) {
throw new IOException(e);
}
return os; return os;
} }
@ -514,12 +551,13 @@ public class FtpURLConnection extends URLConnection {
* *
* @return The <code>Permission</code> object. * @return The <code>Permission</code> object.
*/ */
@Override
public Permission getPermission() { public Permission getPermission() {
if (permission == null) { if (permission == null) {
int port = url.getPort(); int urlport = url.getPort();
port = port < 0 ? FtpClient.FTP_PORT : port; urlport = urlport < 0 ? FtpClient.defaultPort() : urlport;
String host = this.host + ":" + port; String urlhost = this.host + ":" + urlport;
permission = new SocketPermission(host, "connect"); permission = new SocketPermission(urlhost, "connect");
} }
return permission; return permission;
} }
@ -534,23 +572,24 @@ public class FtpURLConnection extends URLConnection {
* @throws IllegalStateException if already connected * @throws IllegalStateException if already connected
* @see #getRequestProperty(java.lang.String) * @see #getRequestProperty(java.lang.String)
*/ */
@Override
public void setRequestProperty(String key, String value) { public void setRequestProperty(String key, String value) {
super.setRequestProperty(key, value); super.setRequestProperty(key, value);
if ("type".equals(key)) { if ("type".equals(key)) {
if ("i".equalsIgnoreCase(value)) if ("i".equalsIgnoreCase(value)) {
type = BIN; type = BIN;
else if ("a".equalsIgnoreCase(value)) } else if ("a".equalsIgnoreCase(value)) {
type = ASCII; type = ASCII;
else } else if ("d".equalsIgnoreCase(value)) {
if ("d".equalsIgnoreCase(value))
type = DIR; type = DIR;
else } else {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Value of '" + key + "Value of '" + key +
"' request property was '" + value + "' request property was '" + value +
"' when it must be either 'i', 'a' or 'd'"); "' when it must be either 'i', 'a' or 'd'");
} }
} }
}
/** /**
* Returns the value of the named general request property for this * Returns the value of the named general request property for this
@ -562,33 +601,41 @@ public class FtpURLConnection extends URLConnection {
* @throws IllegalStateException if already connected * @throws IllegalStateException if already connected
* @see #setRequestProperty(java.lang.String, java.lang.String) * @see #setRequestProperty(java.lang.String, java.lang.String)
*/ */
@Override
public String getRequestProperty(String key) { public String getRequestProperty(String key) {
String value = super.getRequestProperty(key); String value = super.getRequestProperty(key);
if (value == null) { if (value == null) {
if ("type".equals (key)) if ("type".equals(key)) {
value = (type == ASCII ? "a" : type == DIR ? "d" : "i"); value = (type == ASCII ? "a" : type == DIR ? "d" : "i");
} }
}
return value; return value;
} }
@Override
public void setConnectTimeout(int timeout) { public void setConnectTimeout(int timeout) {
if (timeout < 0) if (timeout < 0) {
throw new IllegalArgumentException("timeouts can't be negative"); throw new IllegalArgumentException("timeouts can't be negative");
}
connectTimeout = timeout; connectTimeout = timeout;
} }
@Override
public int getConnectTimeout() { public int getConnectTimeout() {
return (connectTimeout < 0 ? 0 : connectTimeout); return (connectTimeout < 0 ? 0 : connectTimeout);
} }
@Override
public void setReadTimeout(int timeout) { public void setReadTimeout(int timeout) {
if (timeout < 0) if (timeout < 0) {
throw new IllegalArgumentException("timeouts can't be negative"); throw new IllegalArgumentException("timeouts can't be negative");
}
readTimeout = timeout; readTimeout = timeout;
} }
@Override
public int getReadTimeout() { public int getReadTimeout() {
return readTimeout < 0 ? 0 : readTimeout; return readTimeout < 0 ? 0 : readTimeout;
} }