Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openanolis
dragonwell8_jdk
提交
f4fd5266
D
dragonwell8_jdk
项目概览
openanolis
/
dragonwell8_jdk
通知
4
Star
2
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
dragonwell8_jdk
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
f4fd5266
编写于
10月 21, 2009
作者:
J
jccollet
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
6893702: Overhaul of Ftp Client internal code
Summary: Major reorg of internal FTP client code Reviewed-by: chegar
上级
dc16d53b
变更
11
隐藏空白更改
内联
并排
Showing
11 changed file
with
4020 addition
and
796 deletion
+4020
-796
make/sun/net/FILES_java.gmk
make/sun/net/FILES_java.gmk
+6
-0
src/share/classes/sun/net/ftp/FtpClient.java
src/share/classes/sun/net/ftp/FtpClient.java
+774
-646
src/share/classes/sun/net/ftp/FtpClientProvider.java
src/share/classes/sun/net/ftp/FtpClientProvider.java
+158
-0
src/share/classes/sun/net/ftp/FtpDirEntry.java
src/share/classes/sun/net/ftp/FtpDirEntry.java
+331
-0
src/share/classes/sun/net/ftp/FtpDirParser.java
src/share/classes/sun/net/ftp/FtpDirParser.java
+49
-0
src/share/classes/sun/net/ftp/FtpLoginException.java
src/share/classes/sun/net/ftp/FtpLoginException.java
+4
-4
src/share/classes/sun/net/ftp/FtpProtocolException.java
src/share/classes/sun/net/ftp/FtpProtocolException.java
+38
-10
src/share/classes/sun/net/ftp/FtpReplyCode.java
src/share/classes/sun/net/ftp/FtpReplyCode.java
+248
-0
src/share/classes/sun/net/ftp/impl/DefaultFtpClientProvider.java
...re/classes/sun/net/ftp/impl/DefaultFtpClientProvider.java
+38
-0
src/share/classes/sun/net/ftp/impl/FtpClient.java
src/share/classes/sun/net/ftp/impl/FtpClient.java
+2191
-0
src/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java
...re/classes/sun/net/www/protocol/ftp/FtpURLConnection.java
+183
-136
未找到文件。
make/sun/net/FILES_java.gmk
浏览文件 @
f4fd5266
...
...
@@ -45,8 +45,14 @@ FILES_java = \
sun/net/dns/ResolverConfiguration.java \
sun/net/dns/ResolverConfigurationImpl.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/FtpProtocolException.java \
sun/net/ftp/impl/FtpClient.java \
sun/net/ftp/impl/DefaultFtpClientProvider.java \
sun/net/spi/DefaultProxySelector.java \
sun/net/spi/nameservice/NameServiceDescriptor.java \
sun/net/spi/nameservice/NameService.java \
...
...
src/share/classes/sun/net/ftp/FtpClient.java
浏览文件 @
f4fd5266
/*
* Copyright
1994-2008
Sun Microsystems, Inc. All Rights Reserved.
* 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
...
...
@@ -22,794 +22,922 @@
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package
sun.net.ftp
;
import
java.util.StringTokenizer
;
import
java.util.regex.*
;
import
java.io.*
;
import
java.net.*
;
import
sun.net.TransferProtocolClient
;
import
sun.net.TelnetInputStream
;
import
sun.net.TelnetOutputStream
;
import
sun.misc.RegexpPool
;
import
java.security.AccessController
;
import
java.security.PrivilegedAction
;
import
java.io.*
;
import
java.util.Date
;
import
java.util.List
;
import
java.util.Iterator
;
/**
* This class implements the FTP client.
* A class that implements the FTP protocol according to
* RFCs <A href="http://www.ietf.org/rfc/rfc0959.txt">959</A>,
* <A href="http://www.ietf.org/rfc/rfc2228.txt">2228</A>,
* <A href="http://www.ietf.org/rfc/rfc2389.txt">2389</A>,
* <A href="http://www.ietf.org/rfc/rfc2428.txt">2428</A>,
* <A href="http://www.ietf.org/rfc/rfc3659.txt">3659</A>,
* <A href="http://www.ietf.org/rfc/rfc4217.txt">4217</A>.
* Which includes support for FTP over SSL/TLS (aka ftps).
*
* @author Jonathan Payne
* {@code FtpClient} provides all the functionalities of a typical FTP
* client, like storing or retrieving files, listing or creating directories.
* A typical usage would consist of connecting the client to the server,
* log in, issue a few commands then logout.
* Here is a code example:
* <pre>
* FtpClient cl = FtpClient.create();
* cl.connect("ftp.gnu.org").login("anonymous", "john.doe@mydomain.com".toCharArray())).changeDirectory("pub/gnu");
* Iterator<FtpDirEntry> dir = cl.listFiles();
* while (dir.hasNext()) {
* FtpDirEntry f = dir.next();
* System.err.println(f.getName());
* }
* cl.close();
* }
* </pre>
* <p><b>Error reporting:</b> There are, mostly, two families of errors that
* can occur during an FTP session. The first kind are the network related issues
* like a connection reset, and they are usually fatal to the session, meaning,
* in all likelyhood the connection to the server has been lost and the session
* should be restarted from scratch. These errors are reported by throwing an
* {@link IOException}. The second kind are the errors reported by the FTP server,
* like when trying to download a non-existing file for example. These errors
* are usually non fatal to the session, meaning more commands can be sent to the
* server. In these cases, a {@link FtpProtocolException} is thrown.</p>
* <p>
* It should be noted that this is not a thread-safe API, as it wouldn't make
* too much sense, due to the very sequential nature of FTP, to provide a
* client able to be manipulated from multiple threads.
*
* @since 1.7
*/
public
abstract
class
FtpClient
implements
java
.
io
.
Closeable
{
public
class
FtpClient
extends
TransferProtocolClient
{
public
static
final
int
FTP_PORT
=
21
;
static
int
FTP_SUCCESS
=
1
;
static
int
FTP_TRY_AGAIN
=
2
;
static
int
FTP_ERROR
=
3
;
private
static
final
int
FTP_PORT
=
21
;
/** remember the ftp server name because we may need it */
private
String
serverName
=
null
;
public
static
enum
TransferType
{
/** socket for data transfer */
private
boolean
replyPending
=
false
;
private
boolean
binaryMode
=
false
;
private
boolean
loggedIn
=
false
;
ASCII
,
BINARY
,
EBCDIC
};
/** regexp pool of hosts for which we should connect directly, not Proxy
* these are intialized from a property.
/**
* Returns the default FTP port number.
*
* @return the port number.
*/
private
static
RegexpPool
nonProxyHostsPool
=
null
;
public
static
final
int
defaultPort
()
{
return
FTP_PORT
;
}
/** The string soucre of nonProxyHostsPool
/**
* Creates an instance of FtpClient. The client is not connected to any
* server yet.
*
*/
private
static
String
nonProxyHostsSource
=
null
;
/** last command issued */
String
command
;
/** The last reply code from the ftp daemon. */
int
lastReplyCode
;
/** Welcome message from the server, if any. */
public
String
welcomeMsg
;
protected
FtpClient
()
{
}
/* these methods are used to determine whether ftp urls are sent to */
/* an http server instead of using a direct connection to the */
/* host. They aren't used directly here. */
/**
* @return if the networking layer should send ftp connections through
* a proxy
* Creates an instance of {@code FtpClient}. The client is not connected to any
* server yet.
*
* @return the created {@code FtpClient}
*/
public
static
boolean
getUseFtpProxy
()
{
// if the ftp.proxyHost is set, use it!
return
(
getFtpProxyHost
()
!=
null
);
public
static
FtpClient
create
()
{
FtpClientProvider
provider
=
FtpClientProvider
.
provider
();
return
provider
.
createFtpClient
(
);
}
/**
* @return the host to use, or null if none has been specified
*/
public
static
String
getFtpProxyHost
()
{
return
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
<
String
>()
{
public
String
run
()
{
String
result
=
System
.
getProperty
(
"ftp.proxyHost"
);
if
(
result
==
null
)
{
result
=
System
.
getProperty
(
"ftpProxyHost"
);
}
if
(
result
==
null
)
{
// as a last resort we use the general one if ftp.useProxy
// is true
if
(
Boolean
.
getBoolean
(
"ftp.useProxy"
))
{
result
=
System
.
getProperty
(
"proxyHost"
);
}
}
return
result
;
}
});
* Creates an instance of FtpClient and connects it to the specified
* address.
*
* @param dest the {@code InetSocketAddress} to connect to.
* @return The created {@code FtpClient}
* @throws IOException if the connection fails
* @see #connect(java.net.SocketAddress)
*/
public
static
FtpClient
create
(
InetSocketAddress
dest
)
throws
FtpProtocolException
,
IOException
{
FtpClient
client
=
create
();
if
(
dest
!=
null
)
{
client
.
connect
(
dest
);
}
return
client
;
}
/**
* @return the proxy port to use. Will default reasonably if not set.
*/
public
static
int
getFtpProxyPort
()
{
final
int
result
[]
=
{
80
};
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
<
Void
>()
{
public
Void
run
()
{
String
tmp
=
System
.
getProperty
(
"ftp.proxyPort"
);
if
(
tmp
==
null
)
{
// for compatibility with 1.0.2
tmp
=
System
.
getProperty
(
"ftpProxyPort"
);
}
if
(
tmp
==
null
)
{
// as a last resort we use the general one if ftp.useProxy
// is true
if
(
Boolean
.
getBoolean
(
"ftp.useProxy"
))
{
tmp
=
System
.
getProperty
(
"proxyPort"
);
}
}
if
(
tmp
!=
null
)
{
result
[
0
]
=
Integer
.
parseInt
(
tmp
);
}
return
null
;
}
});
return
result
[
0
];
* Creates an instance of {@code FtpClient} and connects it to the
* specified host on the default FTP port.
*
* @param dest the {@code String} containing the name of the host
* to connect to.
* @return The created {@code FtpClient}
* @throws IOException if the connection fails.
* @throws FtpProtocolException if the server rejected the connection
*/
public
static
FtpClient
create
(
String
dest
)
throws
FtpProtocolException
,
IOException
{
return
create
(
new
InetSocketAddress
(
dest
,
FTP_PORT
));
}
public
static
boolean
matchNonProxyHosts
(
String
host
)
{
synchronized
(
FtpClient
.
class
)
{
String
rawList
=
java
.
security
.
AccessController
.
doPrivileged
(
new
sun
.
security
.
action
.
GetPropertyAction
(
"ftp.nonProxyHosts"
));
if
(
rawList
==
null
)
{
nonProxyHostsPool
=
null
;
}
else
{
if
(!
rawList
.
equals
(
nonProxyHostsSource
))
{
RegexpPool
pool
=
new
RegexpPool
();
StringTokenizer
st
=
new
StringTokenizer
(
rawList
,
"|"
,
false
);
try
{
while
(
st
.
hasMoreTokens
())
{
pool
.
add
(
st
.
nextToken
().
toLowerCase
(),
Boolean
.
TRUE
);
}
}
catch
(
sun
.
misc
.
REException
ex
)
{
System
.
err
.
println
(
"Error in http.nonProxyHosts system property: "
+
ex
);
}
nonProxyHostsPool
=
pool
;
}
}
nonProxyHostsSource
=
rawList
;
}
if
(
nonProxyHostsPool
==
null
)
{
return
false
;
}
if
(
nonProxyHostsPool
.
match
(
host
)
!=
null
)
{
return
true
;
}
else
{
return
false
;
}
}
/**
* Enables, or disables, the use of the <I>passive</I> mode. In that mode,
* data connections are established by having the client connect to the server.
* This is the recommended default mode as it will work best through
* firewalls and NATs. If set to {@code false} the mode is said to be
* <I>active</I> which means the server will connect back to the client
* after a PORT command to establish a data connection.
*
* <p><b>Note:</b> Since the passive mode might not be supported by all
* FTP servers, enabling it means the client will try to use it. If the
* server rejects it, then the client will attempt to fall back to using
* the <I>active</I> mode by issuing a {@code PORT} command instead.</p>
*
* @param passive {@code true} to force passive mode.
* @return This FtpClient
* @see #isPassiveModeEnabled()
*/
public
abstract
FtpClient
enablePassiveMode
(
boolean
passive
);
/**
*
issue the QUIT command to the FTP server and close the connection
.
*
Tests whether passive mode is enabled
.
*
* @exception FtpProtocolException if an error occured
* @return {@code true} if the passive mode has been enabled.
* @see #enablePassiveMode(boolean)
*/
public
void
closeServer
()
throws
IOException
{
if
(
serverIsOpen
())
{
issueCommand
(
"QUIT"
);
super
.
closeServer
();
}
}
public
abstract
boolean
isPassiveModeEnabled
();
/**
* Se
nd a command to the FTP server.
* Se
ts the default timeout value to use when connecting to the server,
*
* @param
cmd String containing the command
*
@return reply code
* @param
timeout the timeout value, in milliseconds, to use for the connect
*
operation. A value of zero or less, means use the default timeout.
*
* @
exception FtpProtocolException if an error occured
* @
return This FtpClient
*/
protected
int
issueCommand
(
String
cmd
)
throws
IOException
{
command
=
cmd
;
int
reply
;
while
(
replyPending
)
{
replyPending
=
false
;
if
(
readReply
()
==
FTP_ERROR
)
throw
new
FtpProtocolException
(
"Error reading FTP pending reply\n"
);
}
do
{
sendServer
(
cmd
+
"\r\n"
);
reply
=
readReply
();
}
while
(
reply
==
FTP_TRY_AGAIN
);
return
reply
;
}
public
abstract
FtpClient
setConnectTimeout
(
int
timeout
);
/**
*
Send a command to the FTP server and check for success
.
*
Returns the current default connection timeout value
.
*
* @param cmd String containing the command
* @return the value, in milliseconds, of the current connect timeout.
* @see #setConnectTimeout(int)
*/
public
abstract
int
getConnectTimeout
();
/**
* Sets the timeout value to use when reading from the server,
*
* @exception FtpProtocolException if an error occured
* @param timeout the timeout value, in milliseconds, to use for the read
* operation. A value of zero or less, means use the default timeout.
* @return This FtpClient
*/
protected
void
issueCommandCheck
(
String
cmd
)
throws
IOException
{
if
(
issueCommand
(
cmd
)
!=
FTP_SUCCESS
)
throw
new
FtpProtocolException
(
cmd
+
":"
+
getResponseString
());
}
public
abstract
FtpClient
setReadTimeout
(
int
timeout
);
/**
* Re
ad the reply from the FTP server
.
* Re
turns the current read timeout value
.
*
* @return
FTP_SUCCESS or FTP_ERROR depending on success
* @
exception FtpProtocolException if an error occured
* @return
the value, in milliseconds, of the current read timeout.
* @
see #setReadTimeout(int)
*/
protected
int
readReply
()
throws
IOException
{
lastReplyCode
=
readServerResponse
();
public
abstract
int
getReadTimeout
();
switch
(
lastReplyCode
/
100
)
{
case
1
:
replyPending
=
true
;
/* falls into ... */
/**
* Set the {@code Proxy} to be used for the next connection.
* If the client is already connected, it doesn't affect the current
* connection. However it is not recommended to change this during a session.
*
* @param p the {@code Proxy} to use, or {@code null} for no proxy.
* @return This FtpClient
*/
public
abstract
FtpClient
setProxy
(
Proxy
p
);
case
2
:
case
3
:
return
FTP_SUCCESS
;
/**
* Get the proxy of this FtpClient
*
* @return the {@code Proxy}, this client is using, or {@code null}
* if none is used.
* @see #setProxy(Proxy)
*/
public
abstract
Proxy
getProxy
();
case
5
:
if
(
lastReplyCode
==
530
)
{
if
(!
loggedIn
)
{
throw
new
FtpLoginException
(
"Not logged in"
);
}
return
FTP_ERROR
;
}
if
(
lastReplyCode
==
550
)
{
throw
new
FileNotFoundException
(
command
+
": "
+
getResponseString
());
}
}
/**
* Tests whether this client is connected or not to a server.
*
* @return {@code true} if the client is connected.
*/
public
abstract
boolean
isConnected
();
/* this statement is not reached */
return
FTP_ERROR
;
}
/**
* Connects the {@code FtpClient} to the specified destination server.
*
* @param dest the address of the destination server
* @return this FtpClient
* @throws IOException if connection failed.
* @throws SecurityException if there is a SecurityManager installed and it
* denied the authorization to connect to the destination.
* @throws FtpProtocolException
*/
public
abstract
FtpClient
connect
(
SocketAddress
dest
)
throws
FtpProtocolException
,
IOException
;
/**
* Tries to open a Data Connection in "PASSIVE" mode by issuing a EPSV or
* PASV command then opening a Socket to the specified address & port
*
* @return the opened socket
* @exception FtpProtocolException if an error occurs when issuing the
* PASV command to the ftp server.
*/
protected
Socket
openPassiveDataConnection
()
throws
IOException
{
String
serverAnswer
;
int
port
;
InetSocketAddress
dest
=
null
;
/**
* Here is the idea:
*
* - First we want to try the new (and IPv6 compatible) EPSV command
* But since we want to be nice with NAT software, we'll issue the
* EPSV ALL cmd first.
* EPSV is documented in RFC2428
* - If EPSV fails, then we fall back to the older, yet OK PASV command
* - If PASV fails as well, then we throw an exception and the calling method
* will have to try the EPRT or PORT command
*/
if
(
issueCommand
(
"EPSV ALL"
)
==
FTP_SUCCESS
)
{
// We can safely use EPSV commands
if
(
issueCommand
(
"EPSV"
)
==
FTP_ERROR
)
throw
new
FtpProtocolException
(
"EPSV Failed: "
+
getResponseString
());
serverAnswer
=
getResponseString
();
// The response string from a EPSV command will contain the port number
// the format will be :
// 229 Entering Extended Passive Mode (|||58210|)
//
// So we'll use the regular expresions package to parse the output.
Pattern
p
=
Pattern
.
compile
(
"^229 .* \\(\\|\\|\\|(\\d+)\\|\\)"
);
Matcher
m
=
p
.
matcher
(
serverAnswer
);
if
(!
m
.
find
())
throw
new
FtpProtocolException
(
"EPSV failed : "
+
serverAnswer
);
// Yay! Let's extract the port number
String
s
=
m
.
group
(
1
);
port
=
Integer
.
parseInt
(
s
);
InetAddress
add
=
serverSocket
.
getInetAddress
();
if
(
add
!=
null
)
{
dest
=
new
InetSocketAddress
(
add
,
port
);
}
else
{
// This means we used an Unresolved address to connect in
// the first place. Most likely because the proxy is doing
// the name resolution for us, so let's keep using unresolved
// address.
dest
=
InetSocketAddress
.
createUnresolved
(
serverName
,
port
);
}
}
else
{
// EPSV ALL failed, so Let's try the regular PASV cmd
if
(
issueCommand
(
"PASV"
)
==
FTP_ERROR
)
throw
new
FtpProtocolException
(
"PASV failed: "
+
getResponseString
());
serverAnswer
=
getResponseString
();
// Let's parse the response String to get the IP & port to connect to
// the String should be in the following format :
//
// 227 Entering Passive Mode (A1,A2,A3,A4,p1,p2)
//
// Note that the two parenthesis are optional
//
// The IP address is A1.A2.A3.A4 and the port is p1 * 256 + p2
//
// The regular expression is a bit more complex this time, because the
// parenthesis are optionals and we have to use 3 groups.
Pattern
p
=
Pattern
.
compile
(
"227 .* \\(?(\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3}),(\\d{1,3}),(\\d{1,3})\\)?"
);
Matcher
m
=
p
.
matcher
(
serverAnswer
);
if
(!
m
.
find
())
throw
new
FtpProtocolException
(
"PASV failed : "
+
serverAnswer
);
// Get port number out of group 2 & 3
port
=
Integer
.
parseInt
(
m
.
group
(
3
))
+
(
Integer
.
parseInt
(
m
.
group
(
2
))
<<
8
);
// IP address is simple
String
s
=
m
.
group
(
1
).
replace
(
','
,
'.'
);
dest
=
new
InetSocketAddress
(
s
,
port
);
}
// Got everything, let's open the socket!
Socket
s
;
if
(
proxy
!=
null
)
{
if
(
proxy
.
type
()
==
Proxy
.
Type
.
SOCKS
)
{
s
=
AccessController
.
doPrivileged
(
new
PrivilegedAction
<
Socket
>()
{
public
Socket
run
()
{
return
new
Socket
(
proxy
);
}});
}
else
s
=
new
Socket
(
Proxy
.
NO_PROXY
);
}
else
s
=
new
Socket
();
// Bind the socket to the same address as the control channel. This
// is needed in case of multi-homed systems.
s
.
bind
(
new
InetSocketAddress
(
serverSocket
.
getLocalAddress
(),
0
));
if
(
connectTimeout
>=
0
)
{
s
.
connect
(
dest
,
connectTimeout
);
}
else
{
if
(
defaultConnectTimeout
>
0
)
{
s
.
connect
(
dest
,
defaultConnectTimeout
);
}
else
{
s
.
connect
(
dest
);
}
}
if
(
readTimeout
>=
0
)
s
.
setSoTimeout
(
readTimeout
);
else
if
(
defaultSoTimeout
>
0
)
{
s
.
setSoTimeout
(
defaultSoTimeout
);
}
return
s
;
}
* Connects the FtpClient to the specified destination server.
*
* @param dest the address of the destination server
* @param timeout the value, in milliseconds, to use as a connection timeout
* @return this FtpClient
* @throws IOException if connection failed.
* @throws SecurityException if there is a SecurityManager installed and it
* denied the authorization to connect to the destination.
* @throws FtpProtocolException
*/
public
abstract
FtpClient
connect
(
SocketAddress
dest
,
int
timeout
)
throws
FtpProtocolException
,
IOException
;
/**
* Tries to open a Data Connection with the server. It will first try a passive
* mode connection, then, if it fails, a more traditional PORT command
* Retrieves the address of the FTP server this client is connected to.
*
* @param cmd the command to execute (RETR, STOR, etc...)
* @return the opened socket
*
* @exception FtpProtocolException if an error occurs when issuing the
* PORT command to the ftp server.
* @return the {@link SocketAddress} of the server, or {@code null} if this
* client is not connected yet.
*/
protected
Socket
openDataConnection
(
String
cmd
)
throws
IOException
{
ServerSocket
portSocket
;
Socket
clientSocket
=
null
;
String
portCmd
;
InetAddress
myAddress
;
IOException
e
;
// Let's try passive mode first
try
{
clientSocket
=
openPassiveDataConnection
();
}
catch
(
IOException
ex
)
{
clientSocket
=
null
;
}
if
(
clientSocket
!=
null
)
{
// We did get a clientSocket, so the passive mode worked
// Let's issue the command (GET, DIR, ...)
try
{
if
(
issueCommand
(
cmd
)
==
FTP_ERROR
)
{
clientSocket
.
close
();
throw
new
FtpProtocolException
(
getResponseString
());
}
else
return
clientSocket
;
}
catch
(
IOException
ioe
)
{
clientSocket
.
close
();
throw
ioe
;
}
}
public
abstract
SocketAddress
getServerAddress
();
assert
(
clientSocket
==
null
);
/**
* Attempts to log on the server with the specified user name and password.
*
* @param user The user name
* @param password The password for that user
* @return this FtpClient
* @throws IOException if an error occured during the transmission
* @throws FtpProtocolException if the login was refused by the server
*/
public
abstract
FtpClient
login
(
String
user
,
char
[]
password
)
throws
FtpProtocolException
,
IOException
;
// Passive mode failed, let's fall back to the good old "PORT"
/**
* Attempts to log on the server with the specified user name, password and
* account name.
*
* @param user The user name
* @param password The password for that user.
* @param account The account name for that user.
* @return this FtpClient
* @throws IOException if an error occurs during the transmission.
* @throws FtpProtocolException if the login was refused by the server
*/
public
abstract
FtpClient
login
(
String
user
,
char
[]
password
,
String
account
)
throws
FtpProtocolException
,
IOException
;
if
(
proxy
!=
null
&&
proxy
.
type
()
==
Proxy
.
Type
.
SOCKS
)
{
// We're behind a firewall and the passive mode fail,
// since we can't accept a connection through SOCKS (yet)
// throw an exception
throw
new
FtpProtocolException
(
"Passive mode failed"
);
}
// Bind the ServerSocket to the same address as the control channel
// This is needed for multi-homed systems
portSocket
=
new
ServerSocket
(
0
,
1
,
serverSocket
.
getLocalAddress
());
try
{
myAddress
=
portSocket
.
getInetAddress
();
if
(
myAddress
.
isAnyLocalAddress
())
myAddress
=
getLocalAddress
();
// Let's try the new, IPv6 compatible EPRT command
// See RFC2428 for specifics
// Some FTP servers (like the one on Solaris) are bugged, they
// will accept the EPRT command but then, the subsequent command
// (e.g. RETR) will fail, so we have to check BOTH results (the
// EPRT cmd then the actual command) to decide wether we should
// fall back on the older PORT command.
portCmd
=
"EPRT |"
+
((
myAddress
instanceof
Inet6Address
)
?
"2"
:
"1"
)
+
"|"
+
myAddress
.
getHostAddress
()
+
"|"
+
portSocket
.
getLocalPort
()+
"|"
;
if
(
issueCommand
(
portCmd
)
==
FTP_ERROR
||
issueCommand
(
cmd
)
==
FTP_ERROR
)
{
// The EPRT command failed, let's fall back to good old PORT
portCmd
=
"PORT "
;
byte
[]
addr
=
myAddress
.
getAddress
();
/* append host addr */
for
(
int
i
=
0
;
i
<
addr
.
length
;
i
++)
{
portCmd
=
portCmd
+
(
addr
[
i
]
&
0xFF
)
+
","
;
}
/* append port number */
portCmd
=
portCmd
+
((
portSocket
.
getLocalPort
()
>>>
8
)
&
0xff
)
+
","
+
(
portSocket
.
getLocalPort
()
&
0xff
);
if
(
issueCommand
(
portCmd
)
==
FTP_ERROR
)
{
e
=
new
FtpProtocolException
(
"PORT :"
+
getResponseString
());
throw
e
;
}
if
(
issueCommand
(
cmd
)
==
FTP_ERROR
)
{
e
=
new
FtpProtocolException
(
cmd
+
":"
+
getResponseString
());
throw
e
;
}
}
// Either the EPRT or the PORT command was successful
// Let's create the client socket
if
(
connectTimeout
>=
0
)
{
portSocket
.
setSoTimeout
(
connectTimeout
);
}
else
{
if
(
defaultConnectTimeout
>
0
)
portSocket
.
setSoTimeout
(
defaultConnectTimeout
);
}
clientSocket
=
portSocket
.
accept
();
if
(
readTimeout
>=
0
)
clientSocket
.
setSoTimeout
(
readTimeout
);
else
{
if
(
defaultSoTimeout
>
0
)
clientSocket
.
setSoTimeout
(
defaultSoTimeout
);
}
}
finally
{
portSocket
.
close
();
}
/**
* Closes the current connection. Logs out the current user, if any, by
* issuing the QUIT command to the server.
* This is in effect terminates the current
* session and the connection to the server will be closed.
* <p>After a close, the client can then be connected to another server
* to start an entirely different session.</P>
*
* @throws IOException if an error occurs during transmission
*/
public
abstract
void
close
()
throws
IOException
;
return
clientSocket
;
}
/**
* Checks whether the client is logged in to the server or not.
*
* @return {@code true} if the client has already completed a login.
*/
public
abstract
boolean
isLoggedIn
();
/* public methods */
/**
* Changes to a specific directory on a remote FTP server
*
* @param remoteDirectory path of the directory to CD to.
* @return this FtpClient
* @throws IOException if an error occurs during the transmission.
* @throws FtpProtocolException if the command was refused by the server
*/
public
abstract
FtpClient
changeDirectory
(
String
remoteDirectory
)
throws
FtpProtocolException
,
IOException
;
/**
*
Open a FTP connection to host <i>host</i>
.
*
Changes to the parent directory, sending the CDUP command to the server
.
*
* @param host The hostname of the ftp server
* @return this FtpClient
* @throws IOException if an error occurs during the transmission.
* @throws FtpProtocolException if the command was refused by the server
*/
public
abstract
FtpClient
changeToParentDirectory
()
throws
FtpProtocolException
,
IOException
;
/**
* Retrieve the server current working directory using the PWD command.
*
* @exception FtpProtocolException if connection fails
* @return a {@code String} containing the current working directory
* @throws IOException if an error occurs during transmission
* @throws FtpProtocolException if the command was refused by the server,
*/
public
void
openServer
(
String
host
)
throws
IOException
{
openServer
(
host
,
FTP_PORT
);
}
public
abstract
String
getWorkingDirectory
()
throws
FtpProtocolException
,
IOException
;
/**
* Open a FTP connection to host <i>host</i> on port <i>port</i>.
* Sets the restart offset to the specified value. That value will be
* sent through a {@code REST} command to server before the next file
* transfer and has the effect of resuming a file transfer from the
* specified point. After the transfer the restart offset is set back to
* zero.
*
* @param offset the offset in the remote file at which to start the next
* transfer. This must be a value greater than or equal to zero.
* @return this FtpClient
* @throws IllegalArgumentException if the offset is negative.
*/
public
abstract
FtpClient
setRestartOffset
(
long
offset
);
/**
* Retrieves a file from the ftp server and writes its content to the specified
* {@code OutputStream}.
* <p>If the restart offset was set, then a {@code REST} command will be
* sent before the {@code RETR} in order to restart the tranfer from the specified
* offset.</p>
* <p>The {@code OutputStream} is not closed by this method at the end
* of the transfer. </p>
* <p>This method will block until the transfer is complete or an exception
* is thrown.</p>
*
* @param name a {@code String} containing the name of the file to
* retreive from the server.
* @param local the {@code OutputStream} the file should be written to.
* @return this FtpClient
* @throws IOException if the transfer fails.
* @throws FtpProtocolException if the command was refused by the server
* @see #setRestartOffset(long)
*/
public
abstract
FtpClient
getFile
(
String
name
,
OutputStream
local
)
throws
FtpProtocolException
,
IOException
;
/**
* Retrieves a file from the ftp server, using the {@code RETR} command, and
* returns the InputStream from the established data connection.
* {@link #completePending()} <b>has</b> to be called once the application
* is done reading from the returned stream.
* <p>If the restart offset was set, then a {@code REST} command will be
* sent before the {@code RETR} in order to restart the tranfer from the specified
* offset.</p>
*
* @param name the name of the remote file
* @return the {@link java.io.InputStream} from the data connection
* @throws IOException if an error occured during the transmission.
* @throws FtpProtocolException if the command was refused by the server
* @see #setRestartOffset(long)
*/
public
abstract
InputStream
getFileStream
(
String
name
)
throws
FtpProtocolException
,
IOException
;
/**
* Transfers a file from the client to the server (aka a <I>put</I>)
* by sending the STOR command, and returns the {@code OutputStream}
* from the established data connection.
*
* A new file is created at the server site if the file specified does
* not already exist.
*
*
@param host the hostname of the ftp server
*
@param port the port to connect to (usually 21)
*
{@link #completePending()} <b>has</b> to be called once the application
*
is finished writing to the returned stream.
*
* @exception FtpProtocolException if connection fails
* @param name the name of the remote file to write.
* @return the {@link java.io.OutputStream} from the data connection or
* {@code null} if the command was unsuccessful.
* @throws IOException if an error occured during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
void
openServer
(
String
host
,
int
port
)
throws
IOException
{
this
.
serverName
=
host
;
super
.
openServer
(
host
,
port
);
if
(
readReply
()
==
FTP_ERROR
)
throw
new
FtpProtocolException
(
"Welcome message: "
+
getResponseString
());
public
OutputStream
putFileStream
(
String
name
)
throws
FtpProtocolException
,
IOException
{
return
putFileStream
(
name
,
false
);
}
/**
* Transfers a file from the client to the server (aka a <I>put</I>)
* by sending the STOR or STOU command, depending on the
* {@code unique} argument, and returns the {@code OutputStream}
* from the established data connection.
* {@link #completePending()} <b>has</b> to be called once the application
* is finished writing to the stream.
*
* A new file is created at the server site if the file specified does
* not already exist.
*
* If {@code unique} is set to {@code true}, the resultant file
* is to be created under a name unique to that directory, meaning
* it will not overwrite an existing file, instead the server will
* generate a new, unique, file name.
* The name of the remote file can be retrieved, after completion of the
* transfer, by calling {@link #getLastFileName()}.
*
* @param name the name of the remote file to write.
* @param unique {@code true} if the remote files should be unique,
* in which case the STOU command will be used.
* @return the {@link java.io.OutputStream} from the data connection.
* @throws IOException if an error occured during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
abstract
OutputStream
putFileStream
(
String
name
,
boolean
unique
)
throws
FtpProtocolException
,
IOException
;
/**
* login user to a host with username <i>user</i> and password
* <i>password</i>
*
* @param user Username to use at login
* @param password Password to use at login or null of none is needed
*
* @exception FtpLoginException if login is unsuccesful
*/
public
void
login
(
String
user
,
String
password
)
throws
IOException
{
if
(!
serverIsOpen
())
throw
new
FtpLoginException
(
"not connected to host"
);
if
(
user
==
null
||
user
.
length
()
==
0
)
return
;
if
(
issueCommand
(
"USER "
+
user
)
==
FTP_ERROR
)
throw
new
FtpLoginException
(
"user "
+
user
+
" : "
+
getResponseString
());
/*
* Checks for "331 User name okay, need password." answer
*/
if
(
lastReplyCode
==
331
)
if
((
password
==
null
)
||
(
password
.
length
()
==
0
)
||
(
issueCommand
(
"PASS "
+
password
)
==
FTP_ERROR
))
throw
new
FtpLoginException
(
"password: "
+
getResponseString
());
// keep the welcome message around so we can
// put it in the resulting HTML page.
String
l
;
StringBuffer
sb
=
new
StringBuffer
();
for
(
int
i
=
0
;
i
<
serverResponse
.
size
();
i
++)
{
l
=
(
String
)
serverResponse
.
elementAt
(
i
);
if
(
l
!=
null
)
{
if
(
l
.
length
()
>=
4
&&
l
.
startsWith
(
"230"
))
{
// get rid of the "230-" prefix
l
=
l
.
substring
(
4
);
}
sb
.
append
(
l
);
}
}
welcomeMsg
=
sb
.
toString
();
loggedIn
=
true
;
* Transfers a file from the client to the server (aka a <I>put</I>)
* by sending the STOR or STOU command, depending on the
* {@code unique} argument. The content of the {@code InputStream}
* passed in argument is written into the remote file, overwriting any
* existing data.
*
* A new file is created at the server site if the file specified does
* not already exist.
*
* If {@code unique} is set to {@code true}, the resultant file
* is to be created under a name unique to that directory, meaning
* it will not overwrite an existing file, instead the server will
* generate a new, unique, file name.
* The name of the remote file can be retrieved, after completion of the
* transfer, by calling {@link #getLastFileName()}.
*
* <p>This method will block until the transfer is complete or an exception
* is thrown.</p>
*
* @param name the name of the remote file to write.
* @param local the {@code InputStream} that points to the data to
* transfer.
* @return this FtpClient
* @throws IOException if an error occured during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
FtpClient
putFile
(
String
name
,
InputStream
local
)
throws
FtpProtocolException
,
IOException
{
return
putFile
(
name
,
local
,
false
);
}
/**
* GET a file from the FTP server
*
* @param filename name of the file to retrieve
* @return the <code>InputStream</code> to read the file from
*
* @exception FileNotFoundException if the file can't be opened
*/
public
TelnetInputStream
get
(
String
filename
)
throws
IOException
{
Socket
s
;
try
{
s
=
openDataConnection
(
"RETR "
+
filename
);
}
catch
(
FileNotFoundException
fileException
)
{
/* Well, "/" might not be the file delimitor for this
particular ftp server, so let's try a series of
"cd" commands to get to the right place. */
/* But don't try this if there are no '/' in the path */
if
(
filename
.
indexOf
(
'/'
)
==
-
1
)
throw
fileException
;
StringTokenizer
t
=
new
StringTokenizer
(
filename
,
"/"
);
String
pathElement
=
null
;
while
(
t
.
hasMoreElements
())
{
pathElement
=
t
.
nextToken
();
if
(!
t
.
hasMoreElements
())
{
/* This is the file component. Look it up now. */
break
;
}
try
{
cd
(
pathElement
);
}
catch
(
FtpProtocolException
e
)
{
/* Giving up. */
throw
fileException
;
}
}
if
(
pathElement
!=
null
)
{
s
=
openDataConnection
(
"RETR "
+
pathElement
);
}
else
{
throw
fileException
;
}
}
* Transfers a file from the client to the server (aka a <I>put</I>)
* by sending the STOR command. The content of the {@code InputStream}
* passed in argument is written into the remote file, overwriting any
* existing data.
*
* A new file is created at the server site if the file specified does
* not already exist.
*
* <p>This method will block until the transfer is complete or an exception
* is thrown.</p>
*
* @param name the name of the remote file to write.
* @param local the {@code InputStream} that points to the data to
* transfer.
* @param unique {@code true} if the remote file should be unique
* (i.e. not already existing), {@code false} otherwise.
* @return this FtpClient
* @throws IOException if an error occured during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
* @see #getLastFileName()
*/
public
abstract
FtpClient
putFile
(
String
name
,
InputStream
local
,
boolean
unique
)
throws
FtpProtocolException
,
IOException
;
return
new
TelnetInputStream
(
s
.
getInputStream
(),
binaryMode
);
}
/**
* Sends the APPE command to the server in order to transfer a data stream
* passed in argument and append it to the content of the specified remote
* file.
*
* <p>This method will block until the transfer is complete or an exception
* is thrown.</p>
*
* @param name A {@code String} containing the name of the remote file
* to append to.
* @param local The {@code InputStream} providing access to the data
* to be appended.
* @return this FtpClient
* @throws IOException if an error occured during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
abstract
FtpClient
appendFile
(
String
name
,
InputStream
local
)
throws
FtpProtocolException
,
IOException
;
/**
* PUT a file to the FTP server
*
* @param filename name of the file to store
* @return the <code>OutputStream</code> to write the file to
* Renames a file on the server.
*
* @param from the name of the file being renamed
* @param to the new name for the file
* @return this FtpClient
* @throws IOException if an error occured during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
TelnetOutputStream
put
(
String
filename
)
throws
IOException
{
Socket
s
=
openDataConnection
(
"STOR "
+
filename
);
TelnetOutputStream
out
=
new
TelnetOutputStream
(
s
.
getOutputStream
(),
binaryMode
);
if
(!
binaryMode
)
out
.
setStickyCRLF
(
true
);
return
out
;
}
public
abstract
FtpClient
rename
(
String
from
,
String
to
)
throws
FtpProtocolException
,
IOException
;
/**
* Append to a file on the FTP server
*
* @param filename name of the file to append to
* @return the <code>OutputStream</code> to write the file to
* Deletes a file on the server.
*
* @param name a {@code String} containing the name of the file
* to delete.
* @return this FtpClient
* @throws IOException if an error occured during the exchange
* @throws FtpProtocolException if the command was rejected by the server
*/
public
TelnetOutputStream
append
(
String
filename
)
throws
IOException
{
Socket
s
=
openDataConnection
(
"APPE "
+
filename
);
TelnetOutputStream
out
=
new
TelnetOutputStream
(
s
.
getOutputStream
(),
binaryMode
);
if
(!
binaryMode
)
out
.
setStickyCRLF
(
true
);
public
abstract
FtpClient
deleteFile
(
String
name
)
throws
FtpProtocolException
,
IOException
;
return
out
;
}
/**
* Creates a new directory on the server.
*
* @param name a {@code String} containing the name of the directory
* to create.
* @return this FtpClient
* @throws IOException if an error occured during the exchange
* @throws FtpProtocolException if the command was rejected by the server
*/
public
abstract
FtpClient
makeDirectory
(
String
name
)
throws
FtpProtocolException
,
IOException
;
/**
*
LIST files in the current directory on a remote FTP server
*
Removes a directory on the server.
*
* @return the <code>InputStream</code> to read the list from
* @param name a {@code String} containing the name of the directory
* to remove.
*
* @return this FtpClient
* @throws IOException if an error occured during the exchange.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
TelnetInputStream
list
()
throws
IOException
{
Socket
s
=
openDataConnection
(
"LIST"
);
return
new
TelnetInputStream
(
s
.
getInputStream
(),
binaryMode
);
}
public
abstract
FtpClient
removeDirectory
(
String
name
)
throws
FtpProtocolException
,
IOException
;
/**
* List (NLST) file names on a remote FTP server
* Sends a No-operation command. It's useful for testing the connection
* status or as a <I>keep alive</I> mechanism.
*
* @param path pathname to the directory to list, null for current
* directory
* @return the <code>InputStream</code> to read the list from
* @exception <code>FtpProtocolException</code>
* @return this FtpClient
* @throws IOException if an error occured during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
TelnetInputStream
nameList
(
String
path
)
throws
IOException
{
Socket
s
;
public
abstract
FtpClient
noop
()
throws
FtpProtocolException
,
IOException
;
if
(
path
!=
null
)
s
=
openDataConnection
(
"NLST "
+
path
);
else
s
=
openDataConnection
(
"NLST"
);
return
new
TelnetInputStream
(
s
.
getInputStream
(),
binaryMode
);
}
/**
* Sends the {@code STAT} command to the server.
* This can be used while a data connection is open to get a status
* on the current transfer, in that case the parameter should be
* {@code null}.
* If used between file transfers, it may have a pathname as argument
* in which case it will work as the LIST command except no data
* connection will be created.
*
* @param name an optional {@code String} containing the pathname
* the STAT command should apply to.
* @return the response from the server
* @throws IOException if an error occured during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
abstract
String
getStatus
(
String
name
)
throws
FtpProtocolException
,
IOException
;
/**
* CD to a specific directory on a remote FTP server
* Sends the {@code FEAT} command to the server and returns the list of supported
* features in the form of strings.
*
* @param remoteDirectory path of the directory to CD to
* The features are the supported commands, like AUTH TLS, PROT or PASV.
* See the RFCs for a complete list.
*
* @exception <code>FtpProtocolException</code>
* Note that not all FTP servers support that command, in which case
* a {@link FtpProtocolException} will be thrown.
*
* @return a {@code List} of {@code Strings} describing the
* supported additional features
* @throws IOException if an error occurs during the transmission.
* @throws FtpProtocolException if the command is rejected by the server
*/
public
void
cd
(
String
remoteDirectory
)
throws
IOException
{
if
(
remoteDirectory
==
null
||
""
.
equals
(
remoteDirectory
))
return
;
issueCommandCheck
(
"CWD "
+
remoteDirectory
);
}
public
abstract
List
<
String
>
getFeatures
()
throws
FtpProtocolException
,
IOException
;
/**
* Sends the {@code ABOR} command to the server.
* <p>It tells the server to stop the previous command or transfer. No action
* will be taken if the previous command has already been completed.</p>
* <p>This doesn't abort the current session, more commands can be issued
* after an abort.</p>
*
* @return this FtpClient
* @throws IOException if an error occured during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
abstract
FtpClient
abort
()
throws
FtpProtocolException
,
IOException
;
/**
* CD to the parent directory on a remote FTP server
* Some methods do not wait until completion before returning, so this
* method can be called to wait until completion. This is typically the case
* with commands that trigger a transfer like {@link #getFileStream(String)}.
* So this method should be called before accessing information related to
* such a command.
* <p>This method will actually block reading on the command channel for a
* notification from the server that the command is finished. Such a
* notification often carries extra information concerning the completion
* of the pending action (e.g. number of bytes transfered).</p>
* <p>Note that this will return immediately if no command or action
* is pending</p>
* <p>It should be also noted that most methods issuing commands to the ftp
* server will call this method if a previous command is pending.
* <p>Example of use:
* <pre>
* InputStream in = cl.getFileStream("file");
* ...
* cl.completePending();
* long size = cl.getLastTransferSize();
* </pre>
* On the other hand, it's not necessary in a case like:
* <pre>
* InputStream in = cl.getFileStream("file");
* // read content
* ...
* cl.close();
* </pre>
* <p>Since {@link #close()} will call completePending() if necessary.</p>
* @return this FtpClient
* @throws IOException if an error occured during the transfer
* @throws FtpProtocolException if the command didn't complete successfully
*/
public
abstract
FtpClient
completePending
()
throws
FtpProtocolException
,
IOException
;
/**
* Reinitializes the USER parameters on the FTP server
*
* @return this FtpClient
* @throws IOException if an error occurs during transmission
* @throws FtpProtocolException if the command fails
*/
public
void
cdUp
()
throws
IOException
{
issueCommandCheck
(
"CDUP"
);
}
public
abstract
FtpClient
reInit
()
throws
FtpProtocolException
,
IOException
;
/**
* Print working directory of remote FTP server
* Changes the transfer type (binary, ascii, ebcdic) and issue the
* proper command (e.g. TYPE A) to the server.
*
* @exception FtpProtocolException if the command fails
* @param type the {@code TransferType} to use.
* @return This FtpClient
* @throws IOException if an error occurs during transmission.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
String
pwd
()
throws
IOException
{
String
answ
;
public
abstract
FtpClient
setType
(
TransferType
type
)
throws
FtpProtocolException
,
IOException
;
issueCommandCheck
(
"PWD"
);
/*
* answer will be of the following format :
*
* 257 "/" is current directory.
*/
answ
=
getResponseString
();
if
(!
answ
.
startsWith
(
"257"
))
throw
new
FtpProtocolException
(
"PWD failed. "
+
answ
);
return
answ
.
substring
(
5
,
answ
.
lastIndexOf
(
'"'
));
/**
* Changes the current transfer type to binary.
* This is a convenience method that is equivalent to
* {@code setType(TransferType.BINARY)}
*
* @return This FtpClient
* @throws IOException if an error occurs during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
* @see #setType(TransferType)
*/
public
FtpClient
setBinaryType
()
throws
FtpProtocolException
,
IOException
{
setType
(
TransferType
.
BINARY
);
return
this
;
}
/**
* Set transfer type to 'I'
*
* @exception FtpProtocolException if the command fails
* Changes the current transfer type to ascii.
* This is a convenience method that is equivalent to
* {@code setType(TransferType.ASCII)}
*
* @return This FtpClient
* @throws IOException if an error occurs during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
* @see #setType(TransferType)
*/
public
void
binary
()
throws
IOException
{
issueCommandCheck
(
"TYPE I"
);
binaryMode
=
true
;
public
FtpClient
setAsciiType
()
throws
FtpProtocolException
,
IOException
{
setType
(
TransferType
.
ASCII
);
return
this
;
}
/**
* Set transfer type to 'A'
*
* @exception FtpProtocolException if the command fails
* Issues a {@code LIST} command to the server to get the current directory
* listing, and returns the InputStream from the data connection.
*
* <p>{@link #completePending()} <b>has</b> to be called once the application
* is finished reading from the stream.</p>
*
* @param path the pathname of the directory to list, or {@code null}
* for the current working directory.
* @return the {@code InputStream} from the resulting data connection
* @throws IOException if an error occurs during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
* @see #changeDirectory(String)
* @see #listFiles(String)
*/
public
void
ascii
()
throws
IOException
{
issueCommandCheck
(
"TYPE A"
);
binaryMode
=
false
;
}
public
abstract
InputStream
list
(
String
path
)
throws
FtpProtocolException
,
IOException
;
/**
* Rename a file on the ftp server
*
* @exception FtpProtocolException if the command fails
* Issues a {@code NLST path} command to server to get the specified directory
* content. It differs from {@link #list(String)} method by the fact that
* it will only list the file names which would make the parsing of the
* somewhat easier.
*
* <p>{@link #completePending()} <b>has</b> to be called once the application
* is finished reading from the stream.</p>
*
* @param path a {@code String} containing the pathname of the
* directory to list or {@code null} for the current working directory.
* @return the {@code InputStream} from the resulting data connection
* @throws IOException if an error occurs during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
void
rename
(
String
from
,
String
to
)
throws
IOException
{
issueCommandCheck
(
"RNFR "
+
from
);
issueCommandCheck
(
"RNTO "
+
to
);
}
public
abstract
InputStream
nameList
(
String
path
)
throws
FtpProtocolException
,
IOException
;
/**
* Get the "System string" from the FTP server
*
* @exception FtpProtocolException if it fails
* Issues the {@code SIZE [path]} command to the server to get the size of a
* specific file on the server.
* Note that this command may not be supported by the server. In which
* case -1 will be returned.
*
* @param path a {@code String} containing the pathname of the
* file.
* @return a {@code long} containing the size of the file or -1 if
* the server returned an error, which can be checked with
* {@link #getLastReplyCode()}.
* @throws IOException if an error occurs during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
String
system
()
throws
IOException
{
String
answ
;
issueCommandCheck
(
"SYST"
);
answ
=
getResponseString
();
if
(!
answ
.
startsWith
(
"215"
))
throw
new
FtpProtocolException
(
"SYST failed."
+
answ
);
return
answ
.
substring
(
4
);
// Skip "215 "
}
public
abstract
long
getSize
(
String
path
)
throws
FtpProtocolException
,
IOException
;
/**
* Issues the {@code MDTM [path]} command to the server to get the modification
* time of a specific file on the server.
* Note that this command may not be supported by the server, in which
* case {@code null} will be returned.
*
* @param path a {@code String} containing the pathname of the file.
* @return a {@code Date} representing the last modification time
* or {@code null} if the server returned an error, which
* can be checked with {@link #getLastReplyCode()}.
* @throws IOException if an error occurs during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
abstract
Date
getLastModified
(
String
path
)
throws
FtpProtocolException
,
IOException
;
/**
* Send a No-operation command. It's usefull for testing the connection status
* Sets the parser used to handle the directory output to the specified
* one. By default the parser is set to one that can handle most FTP
* servers output (Unix base mostly). However it may be necessary for
* and application to provide its own parser due to some uncommon
* output format.
*
* @param p The {@code FtpDirParser} to use.
* @return this FtpClient
* @see #listFiles(String)
*/
public
abstract
FtpClient
setDirParser
(
FtpDirParser
p
);
/**
* Issues a {@code MLSD} command to the server to get the specified directory
* listing and applies the internal parser to create an Iterator of
* {@link java.net.FtpDirEntry}. Note that the Iterator returned is also a
* {@link java.io.Closeable}.
* <p>If the server doesn't support the MLSD command, the LIST command is used
* instead and the parser set by {@link #setDirParser(java.net.FtpDirParser) }
* is used instead.</p>
*
* {@link #completePending()} <b>has</b> to be called once the application
* is finished iterating through the files.
*
* @param path the pathname of the directory to list or {@code null}
* for the current working directoty.
* @return a {@code Iterator} of files or {@code null} if the
* command failed.
* @throws IOException if an error occured during the transmission
* @see #setDirParser(FtpDirParser)
* @see #changeDirectory(String)
* @throws FtpProtocolException if the command was rejected by the server
*/
public
abstract
Iterator
<
FtpDirEntry
>
listFiles
(
String
path
)
throws
FtpProtocolException
,
IOException
;
/**
* Attempts to use Kerberos GSSAPI as an authentication mechanism with the
* ftp server. This will issue an {@code AUTH GSSAPI} command, and if
* it is accepted by the server, will followup with {@code ADAT}
* command to exchange the various tokens until authentication is
* successful. This conforms to Appendix I of RFC 2228.
*
* @return this FtpClient
* @throws IOException if an error occurs during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
abstract
FtpClient
useKerberos
()
throws
FtpProtocolException
,
IOException
;
/**
* Returns the Welcome string the server sent during initial connection.
*
* @exception FtpProtocolException if the command fails
* @return a {@code String} containing the message the server
* returned during connection or {@code null}.
*/
public
void
noop
()
throws
IOException
{
issueCommandCheck
(
"NOOP"
);
}
public
abstract
String
getWelcomeMsg
();
/**
* Re
initialize the USER parameters on the FTp server
* Re
turns the last reply code sent by the server.
*
* @
exception FtpProtocolException if the command fails
* @
return the lastReplyCode or {@code null} if none were received yet.
*/
public
void
reInit
()
throws
IOException
{
issueCommandCheck
(
"REIN"
);
loggedIn
=
false
;
}
public
abstract
FtpReplyCode
getLastReplyCode
();
/**
*
New FTP client connected to host <i>host</i>
.
*
Returns the last response string sent by the server
.
*
* @param host Hostname of the FTP server
* @return the message string, which can be quite long, last returned
* by the server, or {@code null} if no response were received yet.
*/
public
abstract
String
getLastResponseString
();
/**
* Returns, when available, the size of the latest started transfer.
* This is retreived by parsing the response string received as an initial
* response to a {@code RETR} or similar request.
*
* @exception FtpProtocolException if the connection fails
* @return the size of the latest transfer or -1 if either there was no
* transfer or the information was unavailable.
*/
public
FtpClient
(
String
host
)
throws
IOException
{
super
();
openServer
(
host
,
FTP_PORT
);
}
public
abstract
long
getLastTransferSize
();
/**
* New FTP client connected to host <i>host</i>, port <i>port</i>.
* Returns, when available, the remote name of the last transfered file.
* This is mainly useful for "put" operation when the unique flag was
* set since it allows to recover the unique file name created on the
* server which may be different from the one submitted with the command.
*
* @param host Hostname of the FTP server
* @param port port number to connect to (usually 21)
* @return the name the latest transfered file remote name, or
* {@code null} if that information is unavailable.
*/
public
abstract
String
getLastFileName
();
/**
* Attempts to switch to a secure, encrypted connection. This is done by
* sending the {@code AUTH TLS} command.
* <p>See <a href="http://www.ietf.org/rfc/rfc4217.txt">RFC 4217</a></p>
* If successful this will establish a secure command channel with the
* server, it will also make it so that all other transfers (e.g. a RETR
* command) will be done over an encrypted channel as well unless a
* {@link #reInit()} command or a {@link #endSecureSession()} command is issued.
* <p>This method should be called after a successful {@link #connect(java.net.InetSocketAddress) }
* but before calling {@link #login(java.lang.String, char[]) }.</p>
*
* @return this FtpCLient
* @throws IOException if an error occured during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
* @see #endSecureSession()
*/
public
abstract
FtpClient
startSecureSession
()
throws
FtpProtocolException
,
IOException
;
/**
* Sends a {@code CCC} command followed by a {@code PROT C}
* command to the server terminating an encrypted session and reverting
* back to a non encrypted transmission.
*
* @return this FtpClient
* @throws IOException if an error occured during transmission.
* @throws FtpProtocolException if the command was rejected by the server
* @see #startSecureSession()
*/
public
abstract
FtpClient
endSecureSession
()
throws
FtpProtocolException
,
IOException
;
/**
* Sends the "Allocate" ({@code ALLO}) command to the server telling it to
* pre-allocate the specified number of bytes for the next transfer.
*
* @exception FtpProtocolException if the connection fails
* @param size The number of bytes to allocate.
* @return this FtpClient
* @throws IOException if an error occured during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
FtpClient
(
String
host
,
int
port
)
throws
IOException
{
super
();
openServer
(
host
,
port
);
}
public
abstract
FtpClient
allocate
(
long
size
)
throws
FtpProtocolException
,
IOException
;
/** Create an uninitialized FTP client. */
public
FtpClient
()
{}
/**
* Sends the "Structure Mount" ({@code SMNT}) command to the server. This let the
* user mount a different file system data structure without altering his
* login or accounting information.
*
* @param struct a {@code String} containing the name of the
* structure to mount.
* @return this FtpClient
* @throws IOException if an error occured during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
abstract
FtpClient
structureMount
(
String
struct
)
throws
FtpProtocolException
,
IOException
;
public
FtpClient
(
Proxy
p
)
{
proxy
=
p
;
}
/**
* Sends a System ({@code SYST}) command to the server and returns the String
* sent back by the server describing the operating system at the
* server.
*
* @return a {@code String} describing the OS, or {@code null}
* if the operation was not successful.
* @throws IOException if an error occured during the transmission.
* @throws FtpProtocolException if the command was rejected by the server
*/
public
abstract
String
getSystem
()
throws
FtpProtocolException
,
IOException
;
protected
void
finalize
()
throws
IOException
{
/**
* Do not call the "normal" closeServer() as we want finalization
* to be as efficient as possible
*/
if
(
serverIsOpen
())
super
.
closeServer
();
}
/**
* Sends the {@code HELP} command to the server, with an optional command, like
* SITE, and returns the text sent back by the server.
*
* @param cmd the command for which the help is requested or
* {@code null} for the general help
* @return a {@code String} containing the text sent back by the
* server, or {@code null} if the command failed.
* @throws IOException if an error occured during transmission
* @throws FtpProtocolException if the command was rejected by the server
*/
public
abstract
String
getHelp
(
String
cmd
)
throws
FtpProtocolException
,
IOException
;
/**
* Sends the {@code SITE} command to the server. This is used by the server
* to provide services specific to his system that are essential
* to file transfer.
*
* @param cmd the command to be sent.
* @return this FtpClient
* @throws IOException if an error occured during transmission
* @throws FtpProtocolException if the command was rejected by the server
*/
public
abstract
FtpClient
siteCmd
(
String
cmd
)
throws
FtpProtocolException
,
IOException
;
}
src/share/classes/sun/net/ftp/FtpClientProvider.java
0 → 100644
浏览文件 @
f4fd5266
/*
* 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
;
}
});
}
}
}
src/share/classes/sun/net/ftp/FtpDirEntry.java
0 → 100644
浏览文件 @
f4fd5266
/*
* 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
);
}
}
src/share/classes/sun/net/ftp/FtpDirParser.java
0 → 100644
浏览文件 @
f4fd5266
/*
* 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
);
}
src/share/classes/sun/net/ftp/FtpLoginException.java
浏览文件 @
f4fd5266
/*
* Copyright 1994-200
8
Sun Microsystems, Inc. All Rights Reserved.
* Copyright 1994-200
9
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
...
...
@@ -25,7 +25,7 @@
package
sun.net.ftp
;
import
java.io.
*
;
import
java.io.
IOException
;
/**
* This exception is thrown when an error is encountered during an
...
...
@@ -33,10 +33,10 @@ import java.io.*;
*
* @author Jonathan Payne
*/
public
class
FtpLoginException
extends
FtpProtocol
Exception
{
public
class
FtpLoginException
extends
IO
Exception
{
private
static
final
long
serialVersionUID
=
2218162403237941536L
;
FtpLoginException
(
String
s
)
{
public
FtpLoginException
(
String
s
)
{
super
(
s
);
}
}
src/share/classes/sun/net/ftp/FtpProtocolException.java
浏览文件 @
f4fd5266
/*
* Copyright 1994-200
8
Sun Microsystems, Inc. All Rights Reserved.
* Copyright 1994-200
9
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
...
...
@@ -22,21 +22,49 @@
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package
sun.net.ftp
;
import
java.io.*
;
/**
* This exeception is thrown when unexpected results are returned during
* an FTP session.
*
* Thrown to indicate that the FTP server reported an error.
* 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
*/
public
class
FtpProtocolException
extends
IO
Exception
{
public
class
FtpProtocolException
extends
Exception
{
private
static
final
long
serialVersionUID
=
5978077070276545054L
;
private
final
FtpReplyCode
code
;
/**
* 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
;
}
FtpProtocolException
(
String
s
)
{
super
(
s
);
/**
* 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
;
}
}
src/share/classes/sun/net/ftp/FtpReplyCode.java
0 → 100644
浏览文件 @
f4fd5266
/*
* 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
;
}
}
src/share/classes/sun/net/ftp/impl/DefaultFtpClientProvider.java
0 → 100644
浏览文件 @
f4fd5266
/*
* 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
();
}
}
src/share/classes/sun/net/ftp/impl/FtpClient.java
0 → 100644
浏览文件 @
f4fd5266
/*
* 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
;
import
java.net.*
;
import
java.io.*
;
import
java.security.AccessController
;
import
java.security.PrivilegedAction
;
import
java.text.DateFormat
;
import
java.text.ParseException
;
import
java.text.SimpleDateFormat
;
import
java.util.ArrayList
;
import
java.util.Calendar
;
import
java.util.Date
;
import
java.util.Iterator
;
import
java.util.List
;
import
java.util.TimeZone
;
import
java.util.Vector
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
javax.net.ssl.SSLSocket
;
import
javax.net.ssl.SSLSocketFactory
;
import
sun.misc.BASE64Decoder
;
import
sun.misc.BASE64Encoder
;
import
sun.net.ftp.*
;
import
sun.util.logging.PlatformLogger
;
public
class
FtpClient
extends
sun
.
net
.
ftp
.
FtpClient
{
private
static
int
defaultSoTimeout
;
private
static
int
defaultConnectTimeout
;
private
static
final
PlatformLogger
logger
=
PlatformLogger
.
getLogger
(
"sun.net.ftp.FtpClient"
);
private
Proxy
proxy
;
private
Socket
server
;
private
PrintStream
out
;
private
InputStream
in
;
private
int
readTimeout
=
-
1
;
private
int
connectTimeout
=
-
1
;
/* Name of encoding to use for output */
private
static
String
encoding
=
"ISO8859_1"
;
/** remember the ftp server name because we may need it */
private
InetSocketAddress
serverAddr
;
private
boolean
replyPending
=
false
;
private
boolean
loggedIn
=
false
;
private
boolean
useCrypto
=
false
;
private
SSLSocketFactory
sslFact
;
private
Socket
oldSocket
;
/** Array of strings (usually 1 entry) for the last reply from the server. */
private
Vector
<
String
>
serverResponse
=
new
Vector
<
String
>(
1
);
/** The last reply code from the ftp daemon. */
private
FtpReplyCode
lastReplyCode
=
null
;
/** Welcome message from the server, if any. */
private
String
welcomeMsg
;
private
boolean
passiveMode
=
true
;
private
TransferType
type
=
TransferType
.
BINARY
;
private
long
restartOffset
=
0
;
private
long
lastTransSize
=
-
1
;
// -1 means 'unknown size'
private
String
lastFileName
;
/**
* Static members used by the parser
*/
private
static
String
[]
patStrings
=
{
// drwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog
"([\\-ld](?:[r\\-][w\\-][x\\-]){3})\\s*\\d+ (\\w+)\\s*(\\w+)\\s*(\\d+)\\s*([A-Z][a-z][a-z]\\s*\\d+)\\s*(\\d\\d:\\d\\d)\\s*(\\p{Print}*)"
,
// drwxr-xr-x 1 user01 ftp 512 Jan 29 1997 prog
"([\\-ld](?:[r\\-][w\\-][x\\-]){3})\\s*\\d+ (\\w+)\\s*(\\w+)\\s*(\\d+)\\s*([A-Z][a-z][a-z]\\s*\\d+)\\s*(\\d{4})\\s*(\\p{Print}*)"
,
// 04/28/2006 09:12a 3,563 genBuffer.sh
"(\\d{2}/\\d{2}/\\d{4})\\s*(\\d{2}:\\d{2}[ap])\\s*((?:[0-9,]+)|(?:<DIR>))\\s*(\\p{Graph}*)"
,
// 01-29-97 11:32PM <DIR> prog
"(\\d{2}-\\d{2}-\\d{2})\\s*(\\d{2}:\\d{2}[AP]M)\\s*((?:[0-9,]+)|(?:<DIR>))\\s*(\\p{Graph}*)"
};
private
static
int
[][]
patternGroups
=
{
// 0 - file, 1 - size, 2 - date, 3 - time, 4 - year, 5 - permissions,
// 6 - user, 7 - group
{
7
,
4
,
5
,
6
,
0
,
1
,
2
,
3
},
{
7
,
4
,
5
,
0
,
6
,
1
,
2
,
3
},
{
4
,
3
,
1
,
2
,
0
,
0
,
0
,
0
},
{
4
,
3
,
1
,
2
,
0
,
0
,
0
,
0
}};
private
static
Pattern
[]
patterns
;
private
static
Pattern
linkp
=
Pattern
.
compile
(
"(\\p{Print}+) \\-\\> (\\p{Print}+)$"
);
private
DateFormat
df
=
DateFormat
.
getDateInstance
(
DateFormat
.
MEDIUM
,
java
.
util
.
Locale
.
US
);
static
{
final
int
vals
[]
=
{
0
,
0
};
final
String
encs
[]
=
{
null
};
AccessController
.
doPrivileged
(
new
PrivilegedAction
<
Object
>()
{
public
Object
run
()
{
vals
[
0
]
=
Integer
.
getInteger
(
"sun.net.client.defaultReadTimeout"
,
0
).
intValue
();
vals
[
1
]
=
Integer
.
getInteger
(
"sun.net.client.defaultConnectTimeout"
,
0
).
intValue
();
encs
[
0
]
=
System
.
getProperty
(
"file.encoding"
,
"ISO8859_1"
);
return
null
;
}
});
if
(
vals
[
0
]
==
0
)
{
defaultSoTimeout
=
-
1
;
}
else
{
defaultSoTimeout
=
vals
[
0
];
}
if
(
vals
[
1
]
==
0
)
{
defaultConnectTimeout
=
-
1
;
}
else
{
defaultConnectTimeout
=
vals
[
1
];
}
encoding
=
encs
[
0
];
try
{
if
(!
isASCIISuperset
(
encoding
))
{
encoding
=
"ISO8859_1"
;
}
}
catch
(
Exception
e
)
{
encoding
=
"ISO8859_1"
;
}
patterns
=
new
Pattern
[
patStrings
.
length
];
for
(
int
i
=
0
;
i
<
patStrings
.
length
;
i
++)
{
patterns
[
i
]
=
Pattern
.
compile
(
patStrings
[
i
]);
}
}
/**
* Test the named character encoding to verify that it converts ASCII
* characters correctly. We have to use an ASCII based encoding, or else
* the NetworkClients will not work correctly in EBCDIC based systems.
* However, we cannot just use ASCII or ISO8859_1 universally, because in
* Asian locales, non-ASCII characters may be embedded in otherwise
* ASCII based protocols (eg. HTTP). The specifications (RFC2616, 2398)
* are a little ambiguous in this matter. For instance, RFC2398 [part 2.1]
* says that the HTTP request URI should be escaped using a defined
* mechanism, but there is no way to specify in the escaped string what
* the original character set is. It is not correct to assume that
* UTF-8 is always used (as in URLs in HTML 4.0). For this reason,
* until the specifications are updated to deal with this issue more
* comprehensively, and more importantly, HTTP servers are known to
* support these mechanisms, we will maintain the current behavior
* where it is possible to send non-ASCII characters in their original
* unescaped form.
*/
private
static
boolean
isASCIISuperset
(
String
encoding
)
throws
Exception
{
String
chkS
=
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
"abcdefghijklmnopqrstuvwxyz-_.!~*'();/?:@&=+$,"
;
// Expected byte sequence for string above
byte
[]
chkB
=
{
48
,
49
,
50
,
51
,
52
,
53
,
54
,
55
,
56
,
57
,
65
,
66
,
67
,
68
,
69
,
70
,
71
,
72
,
73
,
74
,
75
,
76
,
77
,
78
,
79
,
80
,
81
,
82
,
83
,
84
,
85
,
86
,
87
,
88
,
89
,
90
,
97
,
98
,
99
,
100
,
101
,
102
,
103
,
104
,
105
,
106
,
107
,
108
,
109
,
110
,
111
,
112
,
113
,
114
,
115
,
116
,
117
,
118
,
119
,
120
,
121
,
122
,
45
,
95
,
46
,
33
,
126
,
42
,
39
,
40
,
41
,
59
,
47
,
63
,
58
,
64
,
38
,
61
,
43
,
36
,
44
};
byte
[]
b
=
chkS
.
getBytes
(
encoding
);
return
java
.
util
.
Arrays
.
equals
(
b
,
chkB
);
}
private
class
DefaultParser
implements
FtpDirParser
{
/**
* Possible patterns:
*
* drwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog
* drwxr-xr-x 1 user01 ftp 512 Jan 29 1997 prog
* drwxr-xr-x 1 1 1 512 Jan 29 23:32 prog
* lrwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog -> prog2000
* drwxr-xr-x 1 username ftp 512 Jan 29 23:32 prog
* -rw-r--r-- 1 jcc staff 105009 Feb 3 15:05 test.1
*
* 01-29-97 11:32PM <DIR> prog
* 04/28/2006 09:12a 3,563 genBuffer.sh
*
* drwxr-xr-x folder 0 Jan 29 23:32 prog
*
* 0 DIR 01-29-97 23:32 PROG
*/
private
DefaultParser
()
{
}
public
FtpDirEntry
parseLine
(
String
line
)
{
String
fdate
=
null
;
String
fsize
=
null
;
String
time
=
null
;
String
filename
=
null
;
String
permstring
=
null
;
String
username
=
null
;
String
groupname
=
null
;
boolean
dir
=
false
;
Calendar
now
=
Calendar
.
getInstance
();
int
year
=
now
.
get
(
Calendar
.
YEAR
);
Matcher
m
=
null
;
for
(
int
j
=
0
;
j
<
patterns
.
length
;
j
++)
{
m
=
patterns
[
j
].
matcher
(
line
);
if
(
m
.
find
())
{
// 0 - file, 1 - size, 2 - date, 3 - time, 4 - year,
// 5 - permissions, 6 - user, 7 - group
filename
=
m
.
group
(
patternGroups
[
j
][
0
]);
fsize
=
m
.
group
(
patternGroups
[
j
][
1
]);
fdate
=
m
.
group
(
patternGroups
[
j
][
2
]);
if
(
patternGroups
[
j
][
4
]
>
0
)
{
fdate
+=
(
", "
+
m
.
group
(
patternGroups
[
j
][
4
]));
}
else
if
(
patternGroups
[
j
][
3
]
>
0
)
{
fdate
+=
(
", "
+
String
.
valueOf
(
year
));
}
if
(
patternGroups
[
j
][
3
]
>
0
)
{
time
=
m
.
group
(
patternGroups
[
j
][
3
]);
}
if
(
patternGroups
[
j
][
5
]
>
0
)
{
permstring
=
m
.
group
(
patternGroups
[
j
][
5
]);
dir
=
permstring
.
startsWith
(
"d"
);
}
if
(
patternGroups
[
j
][
6
]
>
0
)
{
username
=
m
.
group
(
patternGroups
[
j
][
6
]);
}
if
(
patternGroups
[
j
][
7
]
>
0
)
{
groupname
=
m
.
group
(
patternGroups
[
j
][
7
]);
}
// Old DOS format
if
(
"<DIR>"
.
equals
(
fsize
))
{
dir
=
true
;
fsize
=
null
;
}
}
}
if
(
filename
!=
null
)
{
Date
d
;
try
{
d
=
df
.
parse
(
fdate
);
}
catch
(
Exception
e
)
{
d
=
null
;
}
if
(
d
!=
null
&&
time
!=
null
)
{
int
c
=
time
.
indexOf
(
":"
);
now
.
setTime
(
d
);
now
.
set
(
Calendar
.
HOUR
,
Integer
.
parseInt
(
time
.
substring
(
0
,
c
)));
now
.
set
(
Calendar
.
MINUTE
,
Integer
.
parseInt
(
time
.
substring
(
c
+
1
)));
d
=
now
.
getTime
();
}
// see if it's a symbolic link, i.e. the name if followed
// by a -> and a path
Matcher
m2
=
linkp
.
matcher
(
filename
);
if
(
m2
.
find
())
{
// Keep only the name then
filename
=
m2
.
group
(
1
);
}
boolean
[][]
perms
=
new
boolean
[
3
][
3
];
for
(
int
i
=
0
;
i
<
3
;
i
++)
{
for
(
int
j
=
0
;
j
<
3
;
j
++)
{
perms
[
i
][
j
]
=
(
permstring
.
charAt
((
i
*
3
)
+
j
)
!=
'-'
);
}
}
FtpDirEntry
file
=
new
FtpDirEntry
(
filename
);
file
.
setUser
(
username
).
setGroup
(
groupname
);
file
.
setSize
(
Long
.
parseLong
(
fsize
)).
setLastModified
(
d
);
file
.
setPermissions
(
perms
);
file
.
setType
(
dir
?
FtpDirEntry
.
Type
.
DIR
:
(
line
.
charAt
(
0
)
==
'l'
?
FtpDirEntry
.
Type
.
LINK
:
FtpDirEntry
.
Type
.
FILE
));
return
file
;
}
return
null
;
}
}
private
class
MLSxParser
implements
FtpDirParser
{
private
SimpleDateFormat
df
=
new
SimpleDateFormat
(
"yyyyMMddhhmmss"
);
public
FtpDirEntry
parseLine
(
String
line
)
{
String
name
=
null
;
int
i
=
line
.
lastIndexOf
(
";"
);
if
(
i
>
0
)
{
name
=
line
.
substring
(
i
+
1
).
trim
();
line
=
line
.
substring
(
0
,
i
);
}
else
{
name
=
line
.
trim
();
line
=
""
;
}
FtpDirEntry
file
=
new
FtpDirEntry
(
name
);
while
(!
line
.
isEmpty
())
{
String
s
;
i
=
line
.
indexOf
(
";"
);
if
(
i
>
0
)
{
s
=
line
.
substring
(
0
,
i
);
line
=
line
.
substring
(
i
+
1
);
}
else
{
s
=
line
;
line
=
""
;
}
i
=
s
.
indexOf
(
"="
);
if
(
i
>
0
)
{
String
fact
=
s
.
substring
(
0
,
i
);
String
value
=
s
.
substring
(
i
+
1
);
file
.
addFact
(
fact
,
value
);
}
}
String
s
=
file
.
getFact
(
"Size"
);
if
(
s
!=
null
)
{
file
.
setSize
(
Long
.
parseLong
(
s
));
}
s
=
file
.
getFact
(
"Modify"
);
if
(
s
!=
null
)
{
Date
d
=
null
;
try
{
d
=
df
.
parse
(
s
);
}
catch
(
ParseException
ex
)
{
}
if
(
d
!=
null
)
{
file
.
setLastModified
(
d
);
}
}
s
=
file
.
getFact
(
"Create"
);
if
(
s
!=
null
)
{
Date
d
=
null
;
try
{
d
=
df
.
parse
(
s
);
}
catch
(
ParseException
ex
)
{
}
if
(
d
!=
null
)
{
file
.
setCreated
(
d
);
}
}
s
=
file
.
getFact
(
"Type"
);
if
(
s
!=
null
)
{
if
(
s
.
equalsIgnoreCase
(
"file"
))
{
file
.
setType
(
FtpDirEntry
.
Type
.
FILE
);
}
if
(
s
.
equalsIgnoreCase
(
"dir"
))
{
file
.
setType
(
FtpDirEntry
.
Type
.
DIR
);
}
if
(
s
.
equalsIgnoreCase
(
"cdir"
))
{
file
.
setType
(
FtpDirEntry
.
Type
.
CDIR
);
}
if
(
s
.
equalsIgnoreCase
(
"pdir"
))
{
file
.
setType
(
FtpDirEntry
.
Type
.
PDIR
);
}
}
return
file
;
}
};
private
FtpDirParser
parser
=
new
DefaultParser
();
private
FtpDirParser
mlsxParser
=
new
MLSxParser
();
private
static
Pattern
transPat
=
null
;
private
void
getTransferSize
()
{
lastTransSize
=
-
1
;
/**
* If it's a start of data transfer response, let's try to extract
* the size from the response string. Usually it looks like that:
*
* 150 Opening BINARY mode data connection for foo (6701 bytes).
*/
String
response
=
getLastResponseString
();
if
(
transPat
==
null
)
{
transPat
=
Pattern
.
compile
(
"150 Opening .*\\((\\d+) bytes\\)."
);
}
Matcher
m
=
transPat
.
matcher
(
response
);
if
(
m
.
find
())
{
String
s
=
m
.
group
(
1
);
lastTransSize
=
Long
.
parseLong
(
s
);
}
}
/**
* extract the created file name from the response string:
* 226 Transfer complete (unique file name:toto.txt.1).
* Usually happens when a STOU (store unique) command had been issued.
*/
private
void
getTransferName
()
{
lastFileName
=
null
;
String
response
=
getLastResponseString
();
int
i
=
response
.
indexOf
(
"unique file name:"
);
int
e
=
response
.
lastIndexOf
(
')'
);
if
(
i
>=
0
)
{
i
+=
17
;
// Length of "unique file name:"
lastFileName
=
response
.
substring
(
i
,
e
);
}
}
/**
* Pulls the response from the server and returns the code as a
* number. Returns -1 on failure.
*/
private
int
readServerResponse
()
throws
IOException
{
StringBuffer
replyBuf
=
new
StringBuffer
(
32
);
int
c
;
int
continuingCode
=
-
1
;
int
code
;
String
response
;
serverResponse
.
setSize
(
0
);
while
(
true
)
{
while
((
c
=
in
.
read
())
!=
-
1
)
{
if
(
c
==
'\r'
)
{
if
((
c
=
in
.
read
())
!=
'\n'
)
{
replyBuf
.
append
(
'\r'
);
}
}
replyBuf
.
append
((
char
)
c
);
if
(
c
==
'\n'
)
{
break
;
}
}
response
=
replyBuf
.
toString
();
replyBuf
.
setLength
(
0
);
if
(
logger
.
isLoggable
(
PlatformLogger
.
FINEST
))
{
logger
.
finest
(
"Server ["
+
serverAddr
+
"] --> "
+
response
);
}
if
(
response
.
length
()
==
0
)
{
code
=
-
1
;
}
else
{
try
{
code
=
Integer
.
parseInt
(
response
.
substring
(
0
,
3
));
}
catch
(
NumberFormatException
e
)
{
code
=
-
1
;
}
catch
(
StringIndexOutOfBoundsException
e
)
{
/* this line doesn't contain a response code, so
we just completely ignore it */
continue
;
}
}
serverResponse
.
addElement
(
response
);
if
(
continuingCode
!=
-
1
)
{
/* we've seen a ###- sequence */
if
(
code
!=
continuingCode
||
(
response
.
length
()
>=
4
&&
response
.
charAt
(
3
)
==
'-'
))
{
continue
;
}
else
{
/* seen the end of code sequence */
continuingCode
=
-
1
;
break
;
}
}
else
if
(
response
.
length
()
>=
4
&&
response
.
charAt
(
3
)
==
'-'
)
{
continuingCode
=
code
;
continue
;
}
else
{
break
;
}
}
return
code
;
}
/** Sends command <i>cmd</i> to the server. */
private
void
sendServer
(
String
cmd
)
{
out
.
print
(
cmd
);
if
(
logger
.
isLoggable
(
PlatformLogger
.
FINEST
))
{
logger
.
finest
(
"Server ["
+
serverAddr
+
"] <-- "
+
cmd
);
}
}
/** converts the server response into a string. */
private
String
getResponseString
()
{
return
serverResponse
.
elementAt
(
0
);
}
/** Returns all server response strings. */
private
Vector
<
String
>
getResponseStrings
()
{
return
serverResponse
;
}
/**
* Read the reply from the FTP server.
*
* @return <code>true</code> if the command was successful
* @throws IOException if an error occured
*/
private
boolean
readReply
()
throws
IOException
{
lastReplyCode
=
FtpReplyCode
.
find
(
readServerResponse
());
if
(
lastReplyCode
.
isPositivePreliminary
())
{
replyPending
=
true
;
return
true
;
}
if
(
lastReplyCode
.
isPositiveCompletion
()
||
lastReplyCode
.
isPositiveIntermediate
())
{
if
(
lastReplyCode
==
FtpReplyCode
.
CLOSING_DATA_CONNECTION
)
{
getTransferName
();
}
return
true
;
}
return
false
;
}
/**
* Sends a command to the FTP server and returns the error code
* (which can be a "success") sent by the server.
*
* @param cmd
* @return <code>true</code> if the command was successful
* @throws IOException
*/
private
boolean
issueCommand
(
String
cmd
)
throws
IOException
{
if
(!
isConnected
())
{
throw
new
IllegalStateException
(
"Not connected"
);
}
if
(
replyPending
)
{
try
{
completePending
();
}
catch
(
sun
.
net
.
ftp
.
FtpProtocolException
e
)
{
// ignore...
}
}
sendServer
(
cmd
+
"\r\n"
);
return
readReply
();
}
/**
* Send a command to the FTP server and check for success.
*
* @param cmd String containing the command
*
* @throws FtpProtocolException if an error occured
*/
private
void
issueCommandCheck
(
String
cmd
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
if
(!
issueCommand
(
cmd
))
{
throw
new
sun
.
net
.
ftp
.
FtpProtocolException
(
cmd
+
":"
+
getResponseString
(),
getLastReplyCode
());
}
}
private
static
Pattern
epsvPat
=
null
;
private
static
Pattern
pasvPat
=
null
;
/**
* Opens a "PASSIVE" connection with the server and returns the connected
* <code>Socket</code>.
*
* @return the connected <code>Socket</code>
* @throws IOException if the connection was unsuccessful.
*/
private
Socket
openPassiveDataConnection
(
String
cmd
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
String
serverAnswer
;
int
port
;
InetSocketAddress
dest
=
null
;
/**
* Here is the idea:
*
* - First we want to try the new (and IPv6 compatible) EPSV command
* But since we want to be nice with NAT software, we'll issue the
* EPSV ALL command first.
* EPSV is documented in RFC2428
* - If EPSV fails, then we fall back to the older, yet ok, PASV
* - If PASV fails as well, then we throw an exception and the calling
* method will have to try the EPRT or PORT command
*/
if
(
issueCommand
(
"EPSV ALL"
))
{
// We can safely use EPSV commands
issueCommandCheck
(
"EPSV"
);
serverAnswer
=
getResponseString
();
// The response string from a EPSV command will contain the port number
// the format will be :
// 229 Entering Extended PASSIVE Mode (|||58210|)
//
// So we'll use the regular expresions package to parse the output.
if
(
epsvPat
==
null
)
{
epsvPat
=
Pattern
.
compile
(
"^229 .* \\(\\|\\|\\|(\\d+)\\|\\)"
);
}
Matcher
m
=
epsvPat
.
matcher
(
serverAnswer
);
if
(!
m
.
find
())
{
throw
new
sun
.
net
.
ftp
.
FtpProtocolException
(
"EPSV failed : "
+
serverAnswer
);
}
// Yay! Let's extract the port number
String
s
=
m
.
group
(
1
);
port
=
Integer
.
parseInt
(
s
);
InetAddress
add
=
server
.
getInetAddress
();
if
(
add
!=
null
)
{
dest
=
new
InetSocketAddress
(
add
,
port
);
}
else
{
// This means we used an Unresolved address to connect in
// the first place. Most likely because the proxy is doing
// the name resolution for us, so let's keep using unresolved
// address.
dest
=
InetSocketAddress
.
createUnresolved
(
serverAddr
.
getHostName
(),
port
);
}
}
else
{
// EPSV ALL failed, so Let's try the regular PASV cmd
issueCommandCheck
(
"PASV"
);
serverAnswer
=
getResponseString
();
// Let's parse the response String to get the IP & port to connect
// to. The String should be in the following format :
//
// 227 Entering PASSIVE Mode (A1,A2,A3,A4,p1,p2)
//
// Note that the two parenthesis are optional
//
// The IP address is A1.A2.A3.A4 and the port is p1 * 256 + p2
//
// The regular expression is a bit more complex this time, because
// the parenthesis are optionals and we have to use 3 groups.
if
(
pasvPat
==
null
)
{
pasvPat
=
Pattern
.
compile
(
"227 .* \\(?(\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3}),(\\d{1,3}),(\\d{1,3})\\)?"
);
}
Matcher
m
=
pasvPat
.
matcher
(
serverAnswer
);
if
(!
m
.
find
())
{
throw
new
sun
.
net
.
ftp
.
FtpProtocolException
(
"PASV failed : "
+
serverAnswer
);
}
// Get port number out of group 2 & 3
port
=
Integer
.
parseInt
(
m
.
group
(
3
))
+
(
Integer
.
parseInt
(
m
.
group
(
2
))
<<
8
);
// IP address is simple
String
s
=
m
.
group
(
1
).
replace
(
','
,
'.'
);
dest
=
new
InetSocketAddress
(
s
,
port
);
}
// Got everything, let's open the socket!
Socket
s
;
if
(
proxy
!=
null
)
{
if
(
proxy
.
type
()
==
Proxy
.
Type
.
SOCKS
)
{
s
=
AccessController
.
doPrivileged
(
new
PrivilegedAction
<
Socket
>()
{
public
Socket
run
()
{
return
new
Socket
(
proxy
);
}
});
}
else
{
s
=
new
Socket
(
Proxy
.
NO_PROXY
);
}
}
else
{
s
=
new
Socket
();
}
// Bind the socket to the same address as the control channel. This
// is needed in case of multi-homed systems.
s
.
bind
(
new
InetSocketAddress
(
server
.
getLocalAddress
(),
0
));
if
(
connectTimeout
>=
0
)
{
s
.
connect
(
dest
,
connectTimeout
);
}
else
{
if
(
defaultConnectTimeout
>
0
)
{
s
.
connect
(
dest
,
defaultConnectTimeout
);
}
else
{
s
.
connect
(
dest
);
}
}
if
(
readTimeout
>=
0
)
{
s
.
setSoTimeout
(
readTimeout
);
}
else
if
(
defaultSoTimeout
>
0
)
{
s
.
setSoTimeout
(
defaultSoTimeout
);
}
if
(
useCrypto
)
{
try
{
s
=
sslFact
.
createSocket
(
s
,
dest
.
getHostName
(),
dest
.
getPort
(),
true
);
}
catch
(
Exception
e
)
{
throw
new
sun
.
net
.
ftp
.
FtpProtocolException
(
"Can't open secure data channel: "
+
e
);
}
}
if
(!
issueCommand
(
cmd
))
{
s
.
close
();
throw
new
sun
.
net
.
ftp
.
FtpProtocolException
(
cmd
+
":"
+
getResponseString
(),
getLastReplyCode
());
}
return
s
;
}
/**
* Opens a data connection with the server according to the set mode
* (ACTIVE or PASSIVE) then send the command passed as an argument.
*
* @param cmd the <code>String</code> containing the command to execute
* @return the connected <code>Socket</code>
* @throws IOException if the connection or command failed
*/
private
Socket
openDataConnection
(
String
cmd
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
Socket
clientSocket
;
if
(
passiveMode
)
{
return
openPassiveDataConnection
(
cmd
);
}
ServerSocket
portSocket
;
InetAddress
myAddress
;
String
portCmd
;
if
(
proxy
!=
null
&&
proxy
.
type
()
==
Proxy
.
Type
.
SOCKS
)
{
// We're behind a firewall and the passive mode fail,
// since we can't accept a connection through SOCKS (yet)
// throw an exception
throw
new
sun
.
net
.
ftp
.
FtpProtocolException
(
"Passive mode failed"
);
}
// Bind the ServerSocket to the same address as the control channel
// This is needed for multi-homed systems
portSocket
=
new
ServerSocket
(
0
,
1
,
server
.
getLocalAddress
());
try
{
myAddress
=
portSocket
.
getInetAddress
();
if
(
myAddress
.
isAnyLocalAddress
())
{
myAddress
=
server
.
getLocalAddress
();
}
// Let's try the new, IPv6 compatible EPRT command
// See RFC2428 for specifics
// Some FTP servers (like the one on Solaris) are bugged, they
// will accept the EPRT command but then, the subsequent command
// (e.g. RETR) will fail, so we have to check BOTH results (the
// EPRT cmd then the actual command) to decide wether we should
// fall back on the older PORT command.
portCmd
=
"EPRT |"
+
((
myAddress
instanceof
Inet6Address
)
?
"2"
:
"1"
)
+
"|"
+
myAddress
.
getHostAddress
()
+
"|"
+
portSocket
.
getLocalPort
()
+
"|"
;
if
(!
issueCommand
(
portCmd
)
||
!
issueCommand
(
cmd
))
{
// The EPRT command failed, let's fall back to good old PORT
portCmd
=
"PORT "
;
byte
[]
addr
=
myAddress
.
getAddress
();
/* append host addr */
for
(
int
i
=
0
;
i
<
addr
.
length
;
i
++)
{
portCmd
=
portCmd
+
(
addr
[
i
]
&
0xFF
)
+
","
;
}
/* append port number */
portCmd
=
portCmd
+
((
portSocket
.
getLocalPort
()
>>>
8
)
&
0xff
)
+
","
+
(
portSocket
.
getLocalPort
()
&
0xff
);
issueCommandCheck
(
portCmd
);
issueCommandCheck
(
cmd
);
}
// Either the EPRT or the PORT command was successful
// Let's create the client socket
if
(
connectTimeout
>=
0
)
{
portSocket
.
setSoTimeout
(
connectTimeout
);
}
else
{
if
(
defaultConnectTimeout
>
0
)
{
portSocket
.
setSoTimeout
(
defaultConnectTimeout
);
}
}
clientSocket
=
portSocket
.
accept
();
if
(
readTimeout
>=
0
)
{
clientSocket
.
setSoTimeout
(
readTimeout
);
}
else
{
if
(
defaultSoTimeout
>
0
)
{
clientSocket
.
setSoTimeout
(
defaultSoTimeout
);
}
}
}
finally
{
portSocket
.
close
();
}
if
(
useCrypto
)
{
try
{
clientSocket
=
sslFact
.
createSocket
(
clientSocket
,
serverAddr
.
getHostName
(),
serverAddr
.
getPort
(),
true
);
}
catch
(
Exception
ex
)
{
throw
new
IOException
(
ex
.
getLocalizedMessage
());
}
}
return
clientSocket
;
}
private
InputStream
createInputStream
(
InputStream
in
)
{
if
(
type
==
TransferType
.
ASCII
)
{
return
new
sun
.
net
.
TelnetInputStream
(
in
,
false
);
}
return
in
;
}
private
OutputStream
createOutputStream
(
OutputStream
out
)
{
if
(
type
==
TransferType
.
ASCII
)
{
return
new
sun
.
net
.
TelnetOutputStream
(
out
,
false
);
}
return
out
;
}
/**
* Creates an instance of FtpClient. The client is not connected to any
* server yet.
*
*/
protected
FtpClient
()
{
}
/**
* Creates an instance of FtpClient. The client is not connected to any
* server yet.
*
*/
public
static
sun
.
net
.
ftp
.
FtpClient
create
()
{
return
new
FtpClient
();
}
/**
* Set the transfer mode to <I>passive</I>. In that mode, data connections
* are established by having the client connect to the server.
* This is the recommended default mode as it will work best through
* firewalls and NATs.
*
* @return This FtpClient
* @see #setActiveMode()
*/
public
sun
.
net
.
ftp
.
FtpClient
enablePassiveMode
(
boolean
passive
)
{
passiveMode
=
passive
;
return
this
;
}
/**
* Gets the current transfer mode.
*
* @return the current <code>FtpTransferMode</code>
*/
public
boolean
isPassiveModeEnabled
()
{
return
passiveMode
;
}
/**
* Sets the timeout value to use when connecting to the server,
*
* @param timeout the timeout value, in milliseconds, to use for the connect
* operation. A value of zero or less, means use the default timeout.
*
* @return This FtpClient
*/
public
sun
.
net
.
ftp
.
FtpClient
setConnectTimeout
(
int
timeout
)
{
connectTimeout
=
timeout
;
return
this
;
}
/**
* Returns the current connection timeout value.
*
* @return the value, in milliseconds, of the current connect timeout.
* @see #setConnectTimeout(int)
*/
public
int
getConnectTimeout
()
{
return
connectTimeout
;
}
/**
* Sets the timeout value to use when reading from the server,
*
* @param timeout the timeout value, in milliseconds, to use for the read
* operation. A value of zero or less, means use the default timeout.
* @return This FtpClient
*/
public
sun
.
net
.
ftp
.
FtpClient
setReadTimeout
(
int
timeout
)
{
readTimeout
=
timeout
;
return
this
;
}
/**
* Returns the current read timeout value.
*
* @return the value, in milliseconds, of the current read timeout.
* @see #setReadTimeout(int)
*/
public
int
getReadTimeout
()
{
return
readTimeout
;
}
public
sun
.
net
.
ftp
.
FtpClient
setProxy
(
Proxy
p
)
{
proxy
=
p
;
return
this
;
}
/**
* Get the proxy of this FtpClient
*
* @return the <code>Proxy</code>, this client is using, or <code>null</code>
* if none is used.
* @see #setProxy(Proxy)
*/
public
Proxy
getProxy
()
{
return
proxy
;
}
/**
* Connects to the specified destination.
*
* @param dest the <code>InetSocketAddress</code> to connect to.
* @throws IOException if the connection fails.
*/
private
void
tryConnect
(
InetSocketAddress
dest
,
int
timeout
)
throws
IOException
{
if
(
isConnected
())
{
disconnect
();
}
server
=
doConnect
(
dest
,
timeout
);
try
{
out
=
new
PrintStream
(
new
BufferedOutputStream
(
server
.
getOutputStream
()),
true
,
encoding
);
}
catch
(
UnsupportedEncodingException
e
)
{
throw
new
InternalError
(
encoding
+
"encoding not found"
);
}
in
=
new
BufferedInputStream
(
server
.
getInputStream
());
}
private
Socket
doConnect
(
InetSocketAddress
dest
,
int
timeout
)
throws
IOException
{
Socket
s
;
if
(
proxy
!=
null
)
{
if
(
proxy
.
type
()
==
Proxy
.
Type
.
SOCKS
)
{
s
=
AccessController
.
doPrivileged
(
new
PrivilegedAction
<
Socket
>()
{
public
Socket
run
()
{
return
new
Socket
(
proxy
);
}
});
}
else
{
s
=
new
Socket
(
Proxy
.
NO_PROXY
);
}
}
else
{
s
=
new
Socket
();
}
// Instance specific timeouts do have priority, that means
// connectTimeout & readTimeout (-1 means not set)
// Then global default timeouts
// Then no timeout.
if
(
timeout
>=
0
)
{
s
.
connect
(
dest
,
timeout
);
}
else
{
if
(
connectTimeout
>=
0
)
{
s
.
connect
(
dest
,
connectTimeout
);
}
else
{
if
(
defaultConnectTimeout
>
0
)
{
s
.
connect
(
dest
,
defaultConnectTimeout
);
}
else
{
s
.
connect
(
dest
);
}
}
}
if
(
readTimeout
>=
0
)
{
s
.
setSoTimeout
(
readTimeout
);
}
else
if
(
defaultSoTimeout
>
0
)
{
s
.
setSoTimeout
(
defaultSoTimeout
);
}
return
s
;
}
private
void
disconnect
()
throws
IOException
{
if
(
isConnected
())
{
server
.
close
();
}
server
=
null
;
in
=
null
;
out
=
null
;
lastTransSize
=
-
1
;
lastFileName
=
null
;
restartOffset
=
0
;
welcomeMsg
=
null
;
lastReplyCode
=
null
;
serverResponse
.
setSize
(
0
);
}
/**
* Tests whether this client is connected or not to a server.
*
* @return <code>true</code> if the client is connected.
*/
public
boolean
isConnected
()
{
return
server
!=
null
;
}
public
SocketAddress
getServerAddress
()
{
return
server
==
null
?
null
:
server
.
getRemoteSocketAddress
();
}
public
sun
.
net
.
ftp
.
FtpClient
connect
(
SocketAddress
dest
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
return
connect
(
dest
,
-
1
);
}
/**
* Connects the FtpClient to the specified destination.
*
* @param dest the address of the destination server
* @throws IOException if connection failed.
*/
public
sun
.
net
.
ftp
.
FtpClient
connect
(
SocketAddress
dest
,
int
timeout
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
if
(!(
dest
instanceof
InetSocketAddress
))
{
throw
new
IllegalArgumentException
(
"Wrong address type"
);
}
serverAddr
=
(
InetSocketAddress
)
dest
;
tryConnect
(
serverAddr
,
timeout
);
if
(!
readReply
())
{
throw
new
sun
.
net
.
ftp
.
FtpProtocolException
(
"Welcome message: "
+
getResponseString
(),
lastReplyCode
);
}
welcomeMsg
=
getResponseString
().
substring
(
4
);
return
this
;
}
private
void
tryLogin
(
String
user
,
char
[]
password
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
(
"USER "
+
user
);
/*
* Checks for "331 User name okay, need password." answer
*/
if
(
lastReplyCode
==
FtpReplyCode
.
NEED_PASSWORD
)
{
if
((
password
!=
null
)
&&
(
password
.
length
>
0
))
{
issueCommandCheck
(
"PASS "
+
String
.
valueOf
(
password
));
}
}
}
/**
* Attempts to log on the server with the specified user name and password.
*
* @param user The user name
* @param password The password for that user
* @return <code>true</code> if the login was successful.
* @throws IOException if an error occured during the transmission
*/
public
sun
.
net
.
ftp
.
FtpClient
login
(
String
user
,
char
[]
password
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
if
(!
isConnected
())
{
throw
new
sun
.
net
.
ftp
.
FtpProtocolException
(
"Not connected yet"
,
FtpReplyCode
.
BAD_SEQUENCE
);
}
if
(
user
==
null
||
user
.
length
()
==
0
)
{
throw
new
IllegalArgumentException
(
"User name can't be null or empty"
);
}
tryLogin
(
user
,
password
);
// keep the welcome message around so we can
// put it in the resulting HTML page.
String
l
;
StringBuffer
sb
=
new
StringBuffer
();
for
(
int
i
=
0
;
i
<
serverResponse
.
size
();
i
++)
{
l
=
serverResponse
.
elementAt
(
i
);
if
(
l
!=
null
)
{
if
(
l
.
length
()
>=
4
&&
l
.
startsWith
(
"230"
))
{
// get rid of the "230-" prefix
l
=
l
.
substring
(
4
);
}
sb
.
append
(
l
);
}
}
welcomeMsg
=
sb
.
toString
();
loggedIn
=
true
;
return
this
;
}
/**
* Attempts to log on the server with the specified user name, password and
* account name.
*
* @param user The user name
* @param password The password for that user.
* @param account The account name for that user.
* @return <code>true</code> if the login was successful.
* @throws IOException if an error occurs during the transmission.
*/
public
sun
.
net
.
ftp
.
FtpClient
login
(
String
user
,
char
[]
password
,
String
account
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
if
(!
isConnected
())
{
throw
new
sun
.
net
.
ftp
.
FtpProtocolException
(
"Not connected yet"
,
FtpReplyCode
.
BAD_SEQUENCE
);
}
if
(
user
==
null
||
user
.
length
()
==
0
)
{
throw
new
IllegalArgumentException
(
"User name can't be null or empty"
);
}
tryLogin
(
user
,
password
);
/*
* Checks for "332 Need account for login." answer
*/
if
(
lastReplyCode
==
FtpReplyCode
.
NEED_ACCOUNT
)
{
issueCommandCheck
(
"ACCT "
+
account
);
}
// keep the welcome message around so we can
// put it in the resulting HTML page.
StringBuffer
sb
=
new
StringBuffer
();
if
(
serverResponse
!=
null
)
{
for
(
String
l
:
serverResponse
)
{
if
(
l
!=
null
)
{
if
(
l
.
length
()
>=
4
&&
l
.
startsWith
(
"230"
))
{
// get rid of the "230-" prefix
l
=
l
.
substring
(
4
);
}
sb
.
append
(
l
);
}
}
}
welcomeMsg
=
sb
.
toString
();
loggedIn
=
true
;
return
this
;
}
/**
* Logs out the current user. This is in effect terminates the current
* session and the connection to the server will be closed.
*
*/
public
void
close
()
throws
IOException
{
if
(
isConnected
())
{
issueCommand
(
"QUIT"
);
loggedIn
=
false
;
}
disconnect
();
}
/**
* Checks whether the client is logged in to the server or not.
*
* @return <code>true</code> if the client has already completed a login.
*/
public
boolean
isLoggedIn
()
{
return
loggedIn
;
}
/**
* Changes to a specific directory on a remote FTP server
*
* @param remoteDirectory path of the directory to CD to.
* @return <code>true</code> if the operation was successful.
* @exception <code>FtpProtocolException</code>
*/
public
sun
.
net
.
ftp
.
FtpClient
changeDirectory
(
String
remoteDirectory
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
if
(
remoteDirectory
==
null
||
""
.
equals
(
remoteDirectory
))
{
throw
new
IllegalArgumentException
(
"directory can't be null or empty"
);
}
issueCommandCheck
(
"CWD "
+
remoteDirectory
);
return
this
;
}
/**
* Changes to the parent directory, sending the CDUP command to the server.
*
* @return <code>true</code> if the command was successful.
* @throws IOException
*/
public
sun
.
net
.
ftp
.
FtpClient
changeToParentDirectory
()
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
(
"CDUP"
);
return
this
;
}
/**
* Returns the server current working directory, or <code>null</code> if
* the PWD command failed.
*
* @return a <code>String</code> containing the current working directory,
* or <code>null</code>
* @throws IOException
*/
public
String
getWorkingDirectory
()
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
(
"PWD"
);
/*
* answer will be of the following format :
*
* 257 "/" is current directory.
*/
String
answ
=
getResponseString
();
if
(!
answ
.
startsWith
(
"257"
))
{
return
null
;
}
return
answ
.
substring
(
5
,
answ
.
lastIndexOf
(
'"'
));
}
/**
* Sets the restart offset to the specified value. That value will be
* sent through a <code>REST</code> command to server before a file
* transfer and has the effect of resuming a file transfer from the
* specified point. After a transfer the restart offset is set back to
* zero.
*
* @param offset the offset in the remote file at which to start the next
* transfer. This must be a value greater than or equal to zero.
* @throws IllegalArgumentException if the offset is negative.
*/
public
sun
.
net
.
ftp
.
FtpClient
setRestartOffset
(
long
offset
)
{
if
(
offset
<
0
)
{
throw
new
IllegalArgumentException
(
"offset can't be negative"
);
}
restartOffset
=
offset
;
return
this
;
}
/**
* Retrieves a file from the ftp server and writes it to the specified
* <code>OutputStream</code>.
* If the restart offset was set, then a <code>REST</code> command will be
* sent before the RETR in order to restart the tranfer from the specified
* offset.
* The <code>OutputStream</code> is not closed by this method at the end
* of the transfer.
*
* @param name a <code>String<code> containing the name of the file to
* retreive from the server.
* @param local the <code>OutputStream</code> the file should be written to.
* @throws IOException if the transfer fails.
*/
public
sun
.
net
.
ftp
.
FtpClient
getFile
(
String
name
,
OutputStream
local
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
int
mtu
=
1500
;
if
(
restartOffset
>
0
)
{
Socket
s
;
try
{
s
=
openDataConnection
(
"REST "
+
restartOffset
);
}
finally
{
restartOffset
=
0
;
}
issueCommandCheck
(
"RETR "
+
name
);
getTransferSize
();
InputStream
remote
=
createInputStream
(
s
.
getInputStream
());
byte
[]
buf
=
new
byte
[
mtu
*
10
];
int
l
;
while
((
l
=
remote
.
read
(
buf
))
>=
0
)
{
if
(
l
>
0
)
{
local
.
write
(
buf
,
0
,
l
);
}
}
remote
.
close
();
}
else
{
Socket
s
=
openDataConnection
(
"RETR "
+
name
);
getTransferSize
();
InputStream
remote
=
createInputStream
(
s
.
getInputStream
());
byte
[]
buf
=
new
byte
[
mtu
*
10
];
int
l
;
while
((
l
=
remote
.
read
(
buf
))
>=
0
)
{
if
(
l
>
0
)
{
local
.
write
(
buf
,
0
,
l
);
}
}
remote
.
close
();
}
return
completePending
();
}
/**
* Retrieves a file from the ftp server, using the RETR command, and
* returns the InputStream from* the established data connection.
* {@link #completePending()} <b>has</b> to be called once the application
* is done reading from the returned stream.
*
* @param name the name of the remote file
* @return the {@link java.io.InputStream} from the data connection, or
* <code>null</code> if the command was unsuccessful.
* @throws IOException if an error occured during the transmission.
*/
public
InputStream
getFileStream
(
String
name
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
Socket
s
;
if
(
restartOffset
>
0
)
{
try
{
s
=
openDataConnection
(
"REST "
+
restartOffset
);
}
finally
{
restartOffset
=
0
;
}
if
(
s
==
null
)
{
return
null
;
}
issueCommandCheck
(
"RETR "
+
name
);
getTransferSize
();
return
createInputStream
(
s
.
getInputStream
());
}
s
=
openDataConnection
(
"RETR "
+
name
);
if
(
s
==
null
)
{
return
null
;
}
getTransferSize
();
return
createInputStream
(
s
.
getInputStream
());
}
/**
* Transfers a file from the client to the server (aka a <I>put</I>)
* by sending the STOR or STOU command, depending on the
* <code>unique</code> argument, and returns the <code>OutputStream</code>
* from the established data connection.
* {@link #completePending()} <b>has</b> to be called once the application
* is finished writing to the stream.
*
* A new file is created at the server site if the file specified does
* not already exist.
*
* If <code>unique</code> is set to <code>true</code>, the resultant file
* is to be created under a name unique to that directory, meaning
* it will not overwrite an existing file, instead the server will
* generate a new, unique, file name.
* The name of the remote file can be retrieved, after completion of the
* transfer, by calling {@link #getLastFileName()}.
*
* @param name the name of the remote file to write.
* @param unique <code>true</code> if the remote files should be unique,
* in which case the STOU command will be used.
* @return the {@link java.io.OutputStream} from the data connection or
* <code>null</code> if the command was unsuccessful.
* @throws IOException if an error occured during the transmission.
*/
public
OutputStream
putFileStream
(
String
name
,
boolean
unique
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
String
cmd
=
unique
?
"STOU "
:
"STOR "
;
Socket
s
=
openDataConnection
(
cmd
+
name
);
if
(
s
==
null
)
{
return
null
;
}
if
(
type
==
TransferType
.
BINARY
)
{
return
s
.
getOutputStream
();
}
return
new
sun
.
net
.
TelnetOutputStream
(
s
.
getOutputStream
(),
false
);
}
/**
* Transfers a file from the client to the server (aka a <I>put</I>)
* by sending the STOR command. The content of the <code>InputStream</code>
* passed in argument is written into the remote file, overwriting any
* existing data.
*
* A new file is created at the server site if the file specified does
* not already exist.
*
* @param name the name of the remote file to write.
* @param local the <code>InputStream</code> that points to the data to
* transfer.
* @param unique <code>true</code> if the remote file should be unique
* (i.e. not already existing), <code>false</code> otherwise.
* @return <code>true</code> if the transfer was successful.
* @throws IOException if an error occured during the transmission.
* @see #getLastFileName()
*/
public
sun
.
net
.
ftp
.
FtpClient
putFile
(
String
name
,
InputStream
local
,
boolean
unique
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
String
cmd
=
unique
?
"STOU "
:
"STOR "
;
int
mtu
=
1500
;
if
(
type
==
TransferType
.
BINARY
)
{
Socket
s
=
openDataConnection
(
cmd
+
name
);
OutputStream
remote
=
createOutputStream
(
s
.
getOutputStream
());
byte
[]
buf
=
new
byte
[
mtu
*
10
];
int
l
;
while
((
l
=
local
.
read
(
buf
))
>=
0
)
{
if
(
l
>
0
)
{
remote
.
write
(
buf
,
0
,
l
);
}
}
remote
.
close
();
}
return
completePending
();
}
/**
* Sends the APPE command to the server in order to transfer a data stream
* passed in argument and append it to the content of the specified remote
* file.
*
* @param name A <code>String</code> containing the name of the remote file
* to append to.
* @param local The <code>InputStream</code> providing access to the data
* to be appended.
* @return <code>true</code> if the transfer was successful.
* @throws IOException if an error occured during the transmission.
*/
public
sun
.
net
.
ftp
.
FtpClient
appendFile
(
String
name
,
InputStream
local
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
int
mtu
=
1500
;
Socket
s
=
openDataConnection
(
"APPE "
+
name
);
OutputStream
remote
=
createOutputStream
(
s
.
getOutputStream
());
byte
[]
buf
=
new
byte
[
mtu
*
10
];
int
l
;
while
((
l
=
local
.
read
(
buf
))
>=
0
)
{
if
(
l
>
0
)
{
remote
.
write
(
buf
,
0
,
l
);
}
}
remote
.
close
();
return
completePending
();
}
/**
* Renames a file on the server.
*
* @param from the name of the file being renamed
* @param to the new name for the file
* @throws IOException if the command fails
*/
public
sun
.
net
.
ftp
.
FtpClient
rename
(
String
from
,
String
to
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
(
"RNFR "
+
from
);
issueCommandCheck
(
"RNTO "
+
to
);
return
this
;
}
/**
* Deletes a file on the server.
*
* @param name a <code>String</code> containing the name of the file
* to delete.
* @return <code>true</code> if the command was successful
* @throws IOException if an error occured during the exchange
*/
public
sun
.
net
.
ftp
.
FtpClient
deleteFile
(
String
name
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
(
"DELE "
+
name
);
return
this
;
}
/**
* Creates a new directory on the server.
*
* @param name a <code>String</code> containing the name of the directory
* to create.
* @return <code>true</code> if the operation was successful.
* @throws IOException if an error occured during the exchange
*/
public
sun
.
net
.
ftp
.
FtpClient
makeDirectory
(
String
name
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
(
"MKD "
+
name
);
return
this
;
}
/**
* Removes a directory on the server.
*
* @param name a <code>String</code> containing the name of the directory
* to remove.
*
* @return <code>true</code> if the operation was successful.
* @throws IOException if an error occured during the exchange.
*/
public
sun
.
net
.
ftp
.
FtpClient
removeDirectory
(
String
name
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
(
"RMD "
+
name
);
return
this
;
}
/**
* Sends a No-operation command. It's useful for testing the connection
* status or as a <I>keep alive</I> mechanism.
*
* @throws FtpProtocolException if the command fails
*/
public
sun
.
net
.
ftp
.
FtpClient
noop
()
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
(
"NOOP"
);
return
this
;
}
/**
* Sends the STAT command to the server.
* This can be used while a data connection is open to get a status
* on the current transfer, in that case the parameter should be
* <code>null</code>.
* If used between file transfers, it may have a pathname as argument
* in which case it will work as the LIST command except no data
* connection will be created.
*
* @param name an optional <code>String</code> containing the pathname
* the STAT command should apply to.
* @return the response from the server or <code>null</code> if the
* command failed.
* @throws IOException if an error occured during the transmission.
*/
public
String
getStatus
(
String
name
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
((
name
==
null
?
"STAT"
:
"STAT "
+
name
));
/*
* A typical response will be:
* 213-status of t32.gif:
* -rw-r--r-- 1 jcc staff 247445 Feb 17 1998 t32.gif
* 213 End of Status
*
* or
*
* 211-jsn FTP server status:
* Version wu-2.6.2+Sun
* Connected to localhost (::1)
* Logged in as jccollet
* TYPE: ASCII, FORM: Nonprint; STRUcture: File; transfer MODE: Stream
* No data connection
* 0 data bytes received in 0 files
* 0 data bytes transmitted in 0 files
* 0 data bytes total in 0 files
* 53 traffic bytes received in 0 transfers
* 485 traffic bytes transmitted in 0 transfers
* 587 traffic bytes total in 0 transfers
* 211 End of status
*
* So we need to remove the 1st and last line
*/
Vector
<
String
>
resp
=
getResponseStrings
();
StringBuffer
sb
=
new
StringBuffer
();
for
(
int
i
=
1
;
i
<
resp
.
size
()
-
1
;
i
++)
{
sb
.
append
(
resp
.
get
(
i
));
}
return
sb
.
toString
();
}
/**
* Sends the FEAT command to the server and returns the list of supported
* features in the form of strings.
*
* The features are the supported commands, like AUTH TLS, PROT or PASV.
* See the RFCs for a complete list.
*
* Note that not all FTP servers support that command, in which case
* the method will return <code>null</code>
*
* @return a <code>List</code> of <code>Strings</code> describing the
* supported additional features, or <code>null</code>
* if the command is not supported.
* @throws IOException if an error occurs during the transmission.
*/
public
List
<
String
>
getFeatures
()
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
/*
* The FEAT command, when implemented will return something like:
*
* 211-Features:
* AUTH TLS
* PBSZ
* PROT
* EPSV
* EPRT
* PASV
* REST STREAM
* 211 END
*/
ArrayList
<
String
>
features
=
new
ArrayList
<
String
>();
issueCommandCheck
(
"FEAT"
);
Vector
<
String
>
resp
=
getResponseStrings
();
// Note that we start at index 1 to skip the 1st line (211-...)
// and we stop before the last line.
for
(
int
i
=
1
;
i
<
resp
.
size
()
-
1
;
i
++)
{
String
s
=
resp
.
get
(
i
);
// Get rid of leading space and trailing newline
features
.
add
(
s
.
substring
(
1
,
s
.
length
()
-
1
));
}
return
features
;
}
/**
* sends the ABOR command to the server.
* It tells the server to stop the previous command or transfer.
*
* @return <code>true</code> if the command was successful.
* @throws IOException if an error occured during the transmission.
*/
public
sun
.
net
.
ftp
.
FtpClient
abort
()
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
(
"ABOR"
);
// TODO: Must check the ReplyCode:
/*
* From the RFC:
* There are two cases for the server upon receipt of this
* command: (1) the FTP service command was already completed,
* or (2) the FTP service command is still in progress.
* In the first case, the server closes the data connection
* (if it is open) and responds with a 226 reply, indicating
* that the abort command was successfully processed.
* In the second case, the server aborts the FTP service in
* progress and closes the data connection, returning a 426
* reply to indicate that the service request terminated
* abnormally. The server then sends a 226 reply,
* indicating that the abort command was successfully
* processed.
*/
return
this
;
}
/**
* Some methods do not wait until completion before returning, so this
* method can be called to wait until completion. This is typically the case
* with commands that trigger a transfer like {@link #getFileStream(String)}.
* So this method should be called before accessing information related to
* such a command.
* <p>This method will actually block reading on the command channel for a
* notification from the server that the command is finished. Such a
* notification often carries extra information concerning the completion
* of the pending action (e.g. number of bytes transfered).</p>
* <p>Note that this will return true immediately if no command or action
* is pending</p>
* <p>It should be also noted that most methods issuing commands to the ftp
* server will call this method if a previous command is pending.
* <p>Example of use:
* <pre>
* InputStream in = cl.getFileStream("file");
* ...
* cl.completePending();
* long size = cl.getLastTransferSize();
* </pre>
* On the other hand, it's not necessary in a case like:
* <pre>
* InputStream in = cl.getFileStream("file");
* // read content
* ...
* cl.logout();
* </pre>
* <p>Since {@link #logout()} will call completePending() if necessary.</p>
* @return <code>true</code> if the completion was successful or if no
* action was pending.
* @throws IOException
*/
public
sun
.
net
.
ftp
.
FtpClient
completePending
()
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
while
(
replyPending
)
{
replyPending
=
false
;
if
(!
readReply
())
{
throw
new
sun
.
net
.
ftp
.
FtpProtocolException
(
getLastResponseString
(),
lastReplyCode
);
}
}
return
this
;
}
/**
* Reinitializes the USER parameters on the FTP server
*
* @throws FtpProtocolException if the command fails
*/
public
sun
.
net
.
ftp
.
FtpClient
reInit
()
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
(
"REIN"
);
loggedIn
=
false
;
if
(
useCrypto
)
{
if
(
server
instanceof
SSLSocket
)
{
javax
.
net
.
ssl
.
SSLSession
session
=
((
SSLSocket
)
server
).
getSession
();
session
.
invalidate
();
// Restore previous socket and streams
server
=
oldSocket
;
oldSocket
=
null
;
try
{
out
=
new
PrintStream
(
new
BufferedOutputStream
(
server
.
getOutputStream
()),
true
,
encoding
);
}
catch
(
UnsupportedEncodingException
e
)
{
throw
new
InternalError
(
encoding
+
"encoding not found"
);
}
in
=
new
BufferedInputStream
(
server
.
getInputStream
());
}
}
useCrypto
=
false
;
return
this
;
}
/**
* Changes the transfer type (binary, ascii, ebcdic) and issue the
* proper command (e.g. TYPE A) to the server.
*
* @param type the <code>FtpTransferType</code> to use.
* @return This FtpClient
* @throws IOException if an error occurs during transmission.
*/
public
sun
.
net
.
ftp
.
FtpClient
setType
(
TransferType
type
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
String
cmd
=
"NOOP"
;
this
.
type
=
type
;
if
(
type
==
TransferType
.
ASCII
)
{
cmd
=
"TYPE A"
;
}
if
(
type
==
TransferType
.
BINARY
)
{
cmd
=
"TYPE I"
;
}
if
(
type
==
TransferType
.
EBCDIC
)
{
cmd
=
"TYPE E"
;
}
issueCommandCheck
(
cmd
);
return
this
;
}
/**
* Issues a LIST command to the server to get the current directory
* listing, and returns the InputStream from the data connection.
* {@link #completePending()} <b>has</b> to be called once the application
* is finished writing to the stream.
*
* @param path the pathname of the directory to list, or <code>null</code>
* for the current working directory.
* @return the <code>InputStream</code> from the resulting data connection
* @throws IOException if an error occurs during the transmission.
* @see #changeDirectory(String)
* @see #listFiles(String)
*/
public
InputStream
list
(
String
path
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
Socket
s
;
s
=
openDataConnection
(
path
==
null
?
"LIST"
:
"LIST "
+
path
);
if
(
s
!=
null
)
{
return
createInputStream
(
s
.
getInputStream
());
}
return
null
;
}
/**
* Issues a NLST path command to server to get the specified directory
* content. It differs from {@link #list(String)} method by the fact that
* it will only list the file names which would make the parsing of the
* somewhat easier.
*
* {@link #completePending()} <b>has</b> to be called once the application
* is finished writing to the stream.
*
* @param path a <code>String</code> containing the pathname of the
* directory to list or <code>null</code> for the current working
* directory.
* @return the <code>InputStream</code> from the resulting data connection
* @throws IOException if an error occurs during the transmission.
*/
public
InputStream
nameList
(
String
path
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
Socket
s
;
s
=
openDataConnection
(
"NLST "
+
path
);
if
(
s
!=
null
)
{
return
createInputStream
(
s
.
getInputStream
());
}
return
null
;
}
/**
* Issues the SIZE [path] command to the server to get the size of a
* specific file on the server.
* Note that this command may not be supported by the server. In which
* case -1 will be returned.
*
* @param path a <code>String</code> containing the pathname of the
* file.
* @return a <code>long</code> containing the size of the file or -1 if
* the server returned an error, which can be checked with
* {@link #getLastReplyCode()}.
* @throws IOException if an error occurs during the transmission.
*/
public
long
getSize
(
String
path
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
if
(
path
==
null
||
path
.
length
()
==
0
)
{
throw
new
IllegalArgumentException
(
"path can't be null or empty"
);
}
issueCommandCheck
(
"SIZE "
+
path
);
if
(
lastReplyCode
==
FtpReplyCode
.
FILE_STATUS
)
{
String
s
=
getResponseString
();
s
=
s
.
substring
(
4
,
s
.
length
()
-
1
);
return
Long
.
parseLong
(
s
);
}
return
-
1
;
}
private
static
String
[]
MDTMformats
=
{
"yyyyMMddHHmmss.SSS"
,
"yyyyMMddHHmmss"
};
private
static
SimpleDateFormat
[]
dateFormats
=
new
SimpleDateFormat
[
MDTMformats
.
length
];
static
{
for
(
int
i
=
0
;
i
<
MDTMformats
.
length
;
i
++)
{
dateFormats
[
i
]
=
new
SimpleDateFormat
(
MDTMformats
[
i
]);
dateFormats
[
i
].
setTimeZone
(
TimeZone
.
getTimeZone
(
"GMT"
));
}
}
/**
* Issues the MDTM [path] command to the server to get the modification
* time of a specific file on the server.
* Note that this command may not be supported by the server, in which
* case <code>null</code> will be returned.
*
* @param path a <code>String</code> containing the pathname of the file.
* @return a <code>Date</code> representing the last modification time
* or <code>null</code> if the server returned an error, which
* can be checked with {@link #getLastReplyCode()}.
* @throws IOException if an error occurs during the transmission.
*/
public
Date
getLastModified
(
String
path
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
(
"MDTM "
+
path
);
if
(
lastReplyCode
==
FtpReplyCode
.
FILE_STATUS
)
{
String
s
=
getResponseString
().
substring
(
4
);
Date
d
=
null
;
for
(
SimpleDateFormat
dateFormat
:
dateFormats
)
{
try
{
d
=
dateFormat
.
parse
(
s
);
}
catch
(
ParseException
ex
)
{
}
if
(
d
!=
null
)
{
return
d
;
}
}
}
return
null
;
}
/**
* Sets the parser used to handle the directory output to the specified
* one. By default the parser is set to one that can handle most FTP
* servers output (Unix base mostly). However it may be necessary for
* and application to provide its own parser due to some uncommon
* output format.
*
* @param p The <code>FtpDirParser</code> to use.
* @see #listFiles(String)
*/
public
sun
.
net
.
ftp
.
FtpClient
setDirParser
(
FtpDirParser
p
)
{
parser
=
p
;
return
this
;
}
private
class
FtpFileIterator
implements
Iterator
<
FtpDirEntry
>,
Closeable
{
private
BufferedReader
in
=
null
;
private
FtpDirEntry
nextFile
=
null
;
private
FtpDirParser
fparser
=
null
;
private
boolean
eof
=
false
;
public
FtpFileIterator
(
FtpDirParser
p
,
BufferedReader
in
)
{
this
.
in
=
in
;
this
.
fparser
=
p
;
readNext
();
}
private
void
readNext
()
{
nextFile
=
null
;
if
(
eof
)
{
return
;
}
String
line
=
null
;
try
{
do
{
line
=
in
.
readLine
();
if
(
line
!=
null
)
{
nextFile
=
fparser
.
parseLine
(
line
);
if
(
nextFile
!=
null
)
{
return
;
}
}
}
while
(
line
!=
null
);
in
.
close
();
}
catch
(
IOException
iOException
)
{
}
eof
=
true
;
}
public
boolean
hasNext
()
{
return
nextFile
!=
null
;
}
public
FtpDirEntry
next
()
{
FtpDirEntry
ret
=
nextFile
;
readNext
();
return
ret
;
}
public
void
remove
()
{
throw
new
UnsupportedOperationException
(
"Not supported yet."
);
}
public
void
close
()
throws
IOException
{
if
(
in
!=
null
&&
!
eof
)
{
in
.
close
();
}
eof
=
true
;
nextFile
=
null
;
}
}
/**
* Issues a MLSD command to the server to get the specified directory
* listing and applies the current parser to create an Iterator of
* {@link java.net.ftp.FtpDirEntry}. Note that the Iterator returned is also a
* {@link java.io.Closeable}.
* If the server doesn't support the MLSD command, the LIST command is used
* instead.
*
* {@link #completePending()} <b>has</b> to be called once the application
* is finished iterating through the files.
*
* @param path the pathname of the directory to list or <code>null</code>
* for the current working directoty.
* @return a <code>Iterator</code> of files or <code>null</code> if the
* command failed.
* @throws IOException if an error occured during the transmission
* @see #setDirParser(FtpDirParser)
* @see #changeDirectory(String)
*/
public
Iterator
<
FtpDirEntry
>
listFiles
(
String
path
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
Socket
s
=
null
;
BufferedReader
sin
=
null
;
try
{
s
=
openDataConnection
(
path
==
null
?
"MLSD"
:
"MLSD "
+
path
);
}
catch
(
sun
.
net
.
ftp
.
FtpProtocolException
FtpException
)
{
// The server doesn't understand new MLSD command, ignore and fall
// back to LIST
}
if
(
s
!=
null
)
{
sin
=
new
BufferedReader
(
new
InputStreamReader
(
s
.
getInputStream
()));
return
new
FtpFileIterator
(
mlsxParser
,
sin
);
}
else
{
s
=
openDataConnection
(
path
==
null
?
"LIST"
:
"LIST "
+
path
);
if
(
s
!=
null
)
{
sin
=
new
BufferedReader
(
new
InputStreamReader
(
s
.
getInputStream
()));
return
new
FtpFileIterator
(
parser
,
sin
);
}
}
return
null
;
}
private
boolean
sendSecurityData
(
byte
[]
buf
)
throws
IOException
{
BASE64Encoder
encoder
=
new
BASE64Encoder
();
String
s
=
encoder
.
encode
(
buf
);
return
issueCommand
(
"ADAT "
+
s
);
}
private
byte
[]
getSecurityData
()
{
String
s
=
getLastResponseString
();
if
(
s
.
substring
(
4
,
9
).
equalsIgnoreCase
(
"ADAT="
))
{
BASE64Decoder
decoder
=
new
BASE64Decoder
();
try
{
// Need to get rid of the leading '315 ADAT='
// and the trailing newline
return
decoder
.
decodeBuffer
(
s
.
substring
(
9
,
s
.
length
()
-
1
));
}
catch
(
IOException
e
)
{
//
}
}
return
null
;
}
/**
* Attempts to use Kerberos GSSAPI as an authentication mechanism with the
* ftp server. This will issue an <code>AUTH GSSAPI</code> command, and if
* it is accepted by the server, will followup with <code>ADAT</code>
* command to exchange the various tokens until authentification is
* successful. This conforms to Appendix I of RFC 2228.
*
* @return <code>true</code> if authentication was successful.
* @throws IOException if an error occurs during the transmission.
*/
public
sun
.
net
.
ftp
.
FtpClient
useKerberos
()
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
/*
* Comment out for the moment since it's not in use and would create
* needless cross-package links.
*
issueCommandCheck("AUTH GSSAPI");
if (lastReplyCode != FtpReplyCode.NEED_ADAT)
throw new sun.net.ftp.FtpProtocolException("Unexpected reply from server");
try {
GSSManager manager = GSSManager.getInstance();
GSSName name = manager.createName("SERVICE:ftp@"+
serverAddr.getHostName(), null);
GSSContext context = manager.createContext(name, null, null,
GSSContext.DEFAULT_LIFETIME);
context.requestMutualAuth(true);
context.requestReplayDet(true);
context.requestSequenceDet(true);
context.requestCredDeleg(true);
byte []inToken = new byte[0];
while (!context.isEstablished()) {
byte[] outToken
= context.initSecContext(inToken, 0, inToken.length);
// send the output token if generated
if (outToken != null) {
if (sendSecurityData(outToken)) {
inToken = getSecurityData();
}
}
}
loggedIn = true;
} catch (GSSException e) {
}
*/
return
this
;
}
/**
* Returns the Welcome string the server sent during initial connection.
*
* @return a <code>String</code> containing the message the server
* returned during connection or <code>null</code>.
*/
public
String
getWelcomeMsg
()
{
return
welcomeMsg
;
}
/**
* Returns the last reply code sent by the server.
*
* @return the lastReplyCode
*/
public
FtpReplyCode
getLastReplyCode
()
{
return
lastReplyCode
;
}
/**
* Returns the last response string sent by the server.
*
* @return the message string, which can be quite long, last returned
* by the server.
*/
public
String
getLastResponseString
()
{
StringBuffer
sb
=
new
StringBuffer
();
if
(
serverResponse
!=
null
)
{
for
(
String
l
:
serverResponse
)
{
if
(
l
!=
null
)
{
sb
.
append
(
l
);
}
}
}
return
sb
.
toString
();
}
/**
* Returns, when available, the size of the latest started transfer.
* This is retreived by parsing the response string received as an initial
* response to a RETR or similar request.
*
* @return the size of the latest transfer or -1 if either there was no
* transfer or the information was unavailable.
*/
public
long
getLastTransferSize
()
{
return
lastTransSize
;
}
/**
* Returns, when available, the remote name of the last transfered file.
* This is mainly useful for "put" operation when the unique flag was
* set since it allows to recover the unique file name created on the
* server which may be different from the one submitted with the command.
*
* @return the name the latest transfered file remote name, or
* <code>null</code> if that information is unavailable.
*/
public
String
getLastFileName
()
{
return
lastFileName
;
}
/**
* Attempts to switch to a secure, encrypted connection. This is done by
* sending the "AUTH TLS" command.
* <p>See <a href="http://www.ietf.org/rfc/rfc4217.txt">RFC 4217</a></p>
* If successful this will establish a secure command channel with the
* server, it will also make it so that all other transfers (e.g. a RETR
* command) will be done over an encrypted channel as well unless a
* {@link #reInit()} command or a {@link #endSecureSession()} command is issued.
*
* @return <code>true</code> if the operation was successful.
* @throws IOException if an error occured during the transmission.
* @see #endSecureSession()
*/
public
sun
.
net
.
ftp
.
FtpClient
startSecureSession
()
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
if
(!
isConnected
())
{
throw
new
sun
.
net
.
ftp
.
FtpProtocolException
(
"Not connected yet"
,
FtpReplyCode
.
BAD_SEQUENCE
);
}
if
(
sslFact
==
null
)
{
try
{
sslFact
=
(
SSLSocketFactory
)
SSLSocketFactory
.
getDefault
();
}
catch
(
Exception
e
)
{
throw
new
IOException
(
e
.
getLocalizedMessage
());
}
}
issueCommandCheck
(
"AUTH TLS"
);
Socket
s
=
null
;
try
{
s
=
sslFact
.
createSocket
(
server
,
serverAddr
.
getHostName
(),
serverAddr
.
getPort
(),
true
);
}
catch
(
javax
.
net
.
ssl
.
SSLException
ssle
)
{
try
{
disconnect
();
}
catch
(
Exception
e
)
{
}
throw
ssle
;
}
// Remember underlying socket so we can restore it later
oldSocket
=
server
;
server
=
s
;
try
{
out
=
new
PrintStream
(
new
BufferedOutputStream
(
server
.
getOutputStream
()),
true
,
encoding
);
}
catch
(
UnsupportedEncodingException
e
)
{
throw
new
InternalError
(
encoding
+
"encoding not found"
);
}
in
=
new
BufferedInputStream
(
server
.
getInputStream
());
issueCommandCheck
(
"PBSZ 0"
);
issueCommandCheck
(
"PROT P"
);
useCrypto
=
true
;
return
this
;
}
/**
* Sends a <code>CCC</code> command followed by a <code>PROT C</code>
* command to the server terminating an encrypted session and reverting
* back to a non crypted transmission.
*
* @return <code>true</code> if the operation was successful.
* @throws IOException if an error occured during transmission.
* @see #startSecureSession()
*/
public
sun
.
net
.
ftp
.
FtpClient
endSecureSession
()
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
if
(!
useCrypto
)
{
return
this
;
}
issueCommandCheck
(
"CCC"
);
issueCommandCheck
(
"PROT C"
);
useCrypto
=
false
;
// Restore previous socket and streams
server
=
oldSocket
;
oldSocket
=
null
;
try
{
out
=
new
PrintStream
(
new
BufferedOutputStream
(
server
.
getOutputStream
()),
true
,
encoding
);
}
catch
(
UnsupportedEncodingException
e
)
{
throw
new
InternalError
(
encoding
+
"encoding not found"
);
}
in
=
new
BufferedInputStream
(
server
.
getInputStream
());
return
this
;
}
/**
* Sends the "Allocate" (ALLO) command to the server telling it to
* pre-allocate the specified number of bytes for the next transfer.
*
* @param size The number of bytes to allocate.
* @return <code>true</code> if the operation was successful.
* @throws IOException if an error occured during the transmission.
*/
public
sun
.
net
.
ftp
.
FtpClient
allocate
(
long
size
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
(
"ALLO "
+
size
);
return
this
;
}
/**
* Sends the "Structure Mount" (SMNT) command to the server. This let the
* user mount a different file system data structure without altering his
* login or accounting information.
*
* @param struct a <code>String</code> containing the name of the
* structure to mount.
* @return <code>true</code> if the operation was successful.
* @throws IOException if an error occured during the transmission.
*/
public
sun
.
net
.
ftp
.
FtpClient
structureMount
(
String
struct
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
(
"SMNT "
+
struct
);
return
this
;
}
/**
* Sends a SYST (System) command to the server and returns the String
* sent back by the server describing the operating system at the
* server.
*
* @return a <code>String</code> describing the OS, or <code>null</code>
* if the operation was not successful.
* @throws IOException if an error occured during the transmission.
*/
public
String
getSystem
()
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
(
"SYST"
);
/*
* 215 UNIX Type: L8 Version: SUNOS
*/
String
resp
=
getResponseString
();
// Get rid of the leading code and blank
return
resp
.
substring
(
4
);
}
/**
* Sends the HELP command to the server, with an optional command, like
* SITE, and returns the text sent back by the server.
*
* @param cmd the command for which the help is requested or
* <code>null</code> for the general help
* @return a <code>String</code> containing the text sent back by the
* server, or <code>null</code> if the command failed.
* @throws IOException if an error occured during transmission
*/
public
String
getHelp
(
String
cmd
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
(
"HELP "
+
cmd
);
/**
*
* HELP
* 214-The following commands are implemented.
* USER EPRT STRU ALLO DELE SYST RMD MDTM ADAT
* PASS EPSV MODE REST CWD STAT PWD PROT
* QUIT LPRT RETR RNFR LIST HELP CDUP PBSZ
* PORT LPSV STOR RNTO NLST NOOP STOU AUTH
* PASV TYPE APPE ABOR SITE MKD SIZE CCC
* 214 Direct comments to ftp-bugs@jsn.
*
* HELP SITE
* 214-The following SITE commands are implemented.
* UMASK HELP GROUPS
* IDLE ALIAS CHECKMETHOD
* CHMOD CDPATH CHECKSUM
* 214 Direct comments to ftp-bugs@jsn.
*/
Vector
<
String
>
resp
=
getResponseStrings
();
if
(
resp
.
size
()
==
1
)
{
// Single line response
return
resp
.
get
(
0
).
substring
(
4
);
}
// on multiple lines answers, like the ones above, remove 1st and last
// line, concat the the others.
StringBuffer
sb
=
new
StringBuffer
();
for
(
int
i
=
1
;
i
<
resp
.
size
()
-
1
;
i
++)
{
sb
.
append
(
resp
.
get
(
i
).
substring
(
3
));
}
return
sb
.
toString
();
}
/**
* Sends the SITE command to the server. This is used by the server
* to provide services specific to his system that are essential
* to file transfer.
*
* @param cmd the command to be sent.
* @return <code>true</code> if the command was successful.
* @throws IOException if an error occured during transmission
*/
public
sun
.
net
.
ftp
.
FtpClient
siteCmd
(
String
cmd
)
throws
sun
.
net
.
ftp
.
FtpProtocolException
,
IOException
{
issueCommandCheck
(
"SITE "
+
cmd
);
return
this
;
}
}
src/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java
浏览文件 @
f4fd5266
/*
* Copyright 1994-200
8
Sun Microsystems, Inc. All Rights Reserved.
* Copyright 1994-200
9
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
...
...
@@ -37,10 +37,8 @@ import java.io.FilterInputStream;
import
java.io.FilterOutputStream
;
import
java.io.FileNotFoundException
;
import
java.net.URL
;
import
java.net.URLStreamHandler
;
import
java.net.SocketPermission
;
import
java.net.UnknownHostException
;
import
java.net.MalformedURLException
;
import
java.net.InetSocketAddress
;
import
java.net.URI
;
import
java.net.Proxy
;
...
...
@@ -84,7 +82,6 @@ public class FtpURLConnection extends URLConnection {
// In case we have to use proxies, we use HttpURLConnection
HttpURLConnection
http
=
null
;
private
Proxy
instProxy
;
Proxy
proxy
=
null
;
InputStream
is
=
null
;
OutputStream
os
=
null
;
...
...
@@ -125,12 +122,11 @@ public class FtpURLConnection extends URLConnection {
ftp
=
cl
;
}
@Override
public
void
close
()
throws
IOException
{
super
.
close
();
try
{
if
(
ftp
!=
null
)
ftp
.
closeServer
();
}
catch
(
IOException
ex
)
{
if
(
ftp
!=
null
)
{
ftp
.
close
();
}
}
}
...
...
@@ -149,12 +145,11 @@ public class FtpURLConnection extends URLConnection {
ftp
=
cl
;
}
@Override
public
void
close
()
throws
IOException
{
super
.
close
();
try
{
if
(
ftp
!=
null
)
ftp
.
closeServer
();
}
catch
(
IOException
ex
)
{
if
(
ftp
!=
null
)
{
ftp
.
close
();
}
}
}
...
...
@@ -192,10 +187,12 @@ public class FtpURLConnection extends URLConnection {
private
void
setTimeouts
()
{
if
(
ftp
!=
null
)
{
if
(
connectTimeout
>=
0
)
if
(
connectTimeout
>=
0
)
{
ftp
.
setConnectTimeout
(
connectTimeout
);
if
(
readTimeout
>=
0
)
}
if
(
readTimeout
>=
0
)
{
ftp
.
setReadTimeout
(
readTimeout
);
}
}
}
...
...
@@ -218,21 +215,22 @@ public class FtpURLConnection extends URLConnection {
* Do we have to use a proxy?
*/
ProxySelector
sel
=
java
.
security
.
AccessController
.
doPrivileged
(
new
java
.
security
.
PrivilegedAction
<
ProxySelector
>()
{
public
ProxySelector
run
()
{
return
ProxySelector
.
getDefault
();
}
});
new
java
.
security
.
PrivilegedAction
<
ProxySelector
>()
{
public
ProxySelector
run
()
{
return
ProxySelector
.
getDefault
();
}
});
if
(
sel
!=
null
)
{
URI
uri
=
sun
.
net
.
www
.
ParseUtil
.
toURI
(
url
);
Iterator
<
Proxy
>
it
=
sel
.
select
(
uri
).
iterator
();
while
(
it
.
hasNext
())
{
p
=
it
.
next
();
if
(
p
==
null
||
p
==
Proxy
.
NO_PROXY
||
p
.
type
()
==
Proxy
.
Type
.
SOCKS
)
p
.
type
()
==
Proxy
.
Type
.
SOCKS
)
{
break
;
}
if
(
p
.
type
()
!=
Proxy
.
Type
.
HTTP
||
!(
p
.
address
()
instanceof
InetSocketAddress
))
{
!(
p
.
address
()
instanceof
InetSocketAddress
))
{
sel
.
connectFailed
(
uri
,
p
.
address
(),
new
IOException
(
"Wrong proxy type"
));
continue
;
}
...
...
@@ -240,10 +238,14 @@ public class FtpURLConnection extends URLConnection {
InetSocketAddress
paddr
=
(
InetSocketAddress
)
p
.
address
();
try
{
http
=
new
HttpURLConnection
(
url
,
p
);
if
(
connectTimeout
>=
0
)
http
.
setDoInput
(
getDoInput
());
http
.
setDoOutput
(
getDoOutput
());
if
(
connectTimeout
>=
0
)
{
http
.
setConnectTimeout
(
connectTimeout
);
if
(
readTimeout
>=
0
)
}
if
(
readTimeout
>=
0
)
{
http
.
setReadTimeout
(
readTimeout
);
}
http
.
connect
();
connected
=
true
;
return
;
...
...
@@ -257,10 +259,14 @@ public class FtpURLConnection extends URLConnection {
p
=
instProxy
;
if
(
p
.
type
()
==
Proxy
.
Type
.
HTTP
)
{
http
=
new
HttpURLConnection
(
url
,
instProxy
);
if
(
connectTimeout
>=
0
)
http
.
setDoInput
(
getDoInput
());
http
.
setDoOutput
(
getDoOutput
());
if
(
connectTimeout
>=
0
)
{
http
.
setConnectTimeout
(
connectTimeout
);
if
(
readTimeout
>=
0
)
}
if
(
readTimeout
>=
0
)
{
http
.
setReadTimeout
(
readTimeout
);
}
http
.
connect
();
connected
=
true
;
return
;
...
...
@@ -270,31 +276,35 @@ public class FtpURLConnection extends URLConnection {
if
(
user
==
null
)
{
user
=
"anonymous"
;
String
vers
=
java
.
security
.
AccessController
.
doPrivileged
(
new
GetPropertyAction
(
"java.version"
));
new
GetPropertyAction
(
"java.version"
));
password
=
java
.
security
.
AccessController
.
doPrivileged
(
new
GetPropertyAction
(
"ftp.protocol.user"
,
"Java"
+
vers
+
"@"
));
new
GetPropertyAction
(
"ftp.protocol.user"
,
"Java"
+
vers
+
"@"
));
}
try
{
if
(
p
!=
null
)
ftp
=
new
FtpClient
(
p
);
else
ftp
=
new
FtpClient
();
ftp
=
FtpClient
.
create
();
if
(
p
!=
null
)
{
ftp
.
setProxy
(
p
);
}
setTimeouts
();
if
(
port
!=
-
1
)
ftp
.
openServer
(
host
,
port
);
else
ftp
.
openServer
(
host
);
if
(
port
!=
-
1
)
{
ftp
.
connect
(
new
InetSocketAddress
(
host
,
port
));
}
else
{
ftp
.
connect
(
new
InetSocketAddress
(
host
,
FtpClient
.
defaultPort
()));
}
}
catch
(
UnknownHostException
e
)
{
// Maybe do something smart here, like use a proxy like iftp.
// Just keep throwing for now.
throw
e
;
}
catch
(
FtpProtocolException
fe
)
{
throw
new
IOException
(
fe
);
}
try
{
ftp
.
login
(
user
,
password
);
}
catch
(
sun
.
net
.
ftp
.
FtpLoginException
e
)
{
ftp
.
closeServer
();
throw
e
;
ftp
.
login
(
user
,
password
.
toCharArray
());
}
catch
(
sun
.
net
.
ftp
.
FtpProtocolException
e
)
{
ftp
.
close
();
// Backward compatibility
throw
new
sun
.
net
.
ftp
.
FtpLoginException
(
"Invalid username/password"
);
}
connected
=
true
;
}
...
...
@@ -306,24 +316,29 @@ public class FtpURLConnection extends URLConnection {
private
void
decodePath
(
String
path
)
{
int
i
=
path
.
indexOf
(
";type="
);
if
(
i
>=
0
)
{
String
s1
=
path
.
substring
(
i
+
6
,
path
.
length
());
if
(
"i"
.
equalsIgnoreCase
(
s1
))
String
s1
=
path
.
substring
(
i
+
6
,
path
.
length
());
if
(
"i"
.
equalsIgnoreCase
(
s1
))
{
type
=
BIN
;
if
(
"a"
.
equalsIgnoreCase
(
s1
))
}
if
(
"a"
.
equalsIgnoreCase
(
s1
))
{
type
=
ASCII
;
if
(
"d"
.
equalsIgnoreCase
(
s1
))
}
if
(
"d"
.
equalsIgnoreCase
(
s1
))
{
type
=
DIR
;
}
path
=
path
.
substring
(
0
,
i
);
}
if
(
path
!=
null
&&
path
.
length
()
>
1
&&
path
.
charAt
(
0
)
==
'/'
)
path
.
charAt
(
0
)
==
'/'
)
{
path
=
path
.
substring
(
1
);
if
(
path
==
null
||
path
.
length
()
==
0
)
}
if
(
path
==
null
||
path
.
length
()
==
0
)
{
path
=
"./"
;
}
if
(!
path
.
endsWith
(
"/"
))
{
i
=
path
.
lastIndexOf
(
'/'
);
if
(
i
>
0
)
{
filename
=
path
.
substring
(
i
+
1
,
path
.
length
());
filename
=
path
.
substring
(
i
+
1
,
path
.
length
());
filename
=
ParseUtil
.
decode
(
filename
);
pathname
=
path
.
substring
(
0
,
i
);
}
else
{
...
...
@@ -334,10 +349,11 @@ public class FtpURLConnection extends URLConnection {
pathname
=
path
.
substring
(
0
,
path
.
length
()
-
1
);
filename
=
null
;
}
if
(
pathname
!=
null
)
if
(
pathname
!=
null
)
{
fullpath
=
pathname
+
"/"
+
(
filename
!=
null
?
filename
:
""
);
else
}
else
{
fullpath
=
filename
;
}
}
/*
...
...
@@ -346,18 +362,19 @@ public class FtpURLConnection extends URLConnection {
* This is because, '/' is not necessarly the directory delimiter
* on every systems.
*/
private
void
cd
(
String
path
)
throws
IOException
{
if
(
path
==
null
||
""
.
equals
(
path
))
private
void
cd
(
String
path
)
throws
FtpProtocolException
,
IOException
{
if
(
path
==
null
||
path
.
isEmpty
())
{
return
;
}
if
(
path
.
indexOf
(
'/'
)
==
-
1
)
{
ftp
.
c
d
(
ParseUtil
.
decode
(
path
));
ftp
.
c
hangeDirectory
(
ParseUtil
.
decode
(
path
));
return
;
}
StringTokenizer
token
=
new
StringTokenizer
(
path
,
"/"
);
while
(
token
.
hasMoreTokens
())
ftp
.
cd
(
ParseUtil
.
decode
(
token
.
nextToken
()));
StringTokenizer
token
=
new
StringTokenizer
(
path
,
"/"
);
while
(
token
.
hasMoreTokens
())
{
ftp
.
changeDirectory
(
ParseUtil
.
decode
(
token
.
nextToken
()));
}
}
/**
...
...
@@ -369,16 +386,19 @@ public class FtpURLConnection extends URLConnection {
* @throws IOException if already opened for output
* @throws FtpProtocolException if errors occur during the transfert.
*/
@Override
public
InputStream
getInputStream
()
throws
IOException
{
if
(!
connected
)
{
connect
();
}
if
(
http
!=
null
)
if
(
http
!=
null
)
{
return
http
.
getInputStream
();
}
if
(
os
!=
null
)
if
(
os
!=
null
)
{
throw
new
IOException
(
"Already opened for output"
);
}
if
(
is
!=
null
)
{
return
is
;
...
...
@@ -386,82 +406,85 @@ public class FtpURLConnection extends URLConnection {
MessageHeader
msgh
=
new
MessageHeader
();
boolean
isAdir
=
false
;
try
{
decodePath
(
url
.
getPath
());
if
(
filename
==
null
||
type
==
DIR
)
{
ftp
.
ascii
();
ftp
.
setAsciiType
();
cd
(
pathname
);
if
(
filename
==
null
)
is
=
new
FtpInputStream
(
ftp
,
ftp
.
list
());
else
if
(
filename
==
null
)
{
is
=
new
FtpInputStream
(
ftp
,
ftp
.
list
(
null
));
}
else
{
is
=
new
FtpInputStream
(
ftp
,
ftp
.
nameList
(
filename
));
}
}
else
{
if
(
type
==
ASCII
)
ftp
.
ascii
();
else
ftp
.
binary
();
if
(
type
==
ASCII
)
{
ftp
.
setAsciiType
();
}
else
{
ftp
.
setBinaryType
();
}
cd
(
pathname
);
is
=
new
FtpInputStream
(
ftp
,
ftp
.
get
(
filename
));
is
=
new
FtpInputStream
(
ftp
,
ftp
.
get
FileStream
(
filename
));
}
/* Try to get the size of the file in bytes. If that is
successful, then create a MeteredStream. */
successful, then create a MeteredStream. */
try
{
String
response
=
ftp
.
getResponseString
();
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
));
if
(
l
>
0
)
{
// Wrap input stream with MeteredStream to ensure read() will always return -1
// at expected length.
// Check if URL should be metered
boolean
meteredInput
=
ProgressMonitor
.
getDefault
().
shouldMeterInput
(
url
,
"GET"
);
ProgressSource
pi
=
null
;
if
(
meteredInput
)
{
pi
=
new
ProgressSource
(
url
,
"GET"
,
l
);
pi
.
beginTracking
();
}
long
l
=
ftp
.
getLastTransferSize
();
msgh
.
add
(
"content-length"
,
Long
.
toString
(
l
));
if
(
l
>
0
)
{
// Wrap input stream with MeteredStream to ensure read() will always return -1
// at expected length.
is
=
new
MeteredStream
(
is
,
pi
,
l
);
// Check if URL should be metered
boolean
meteredInput
=
ProgressMonitor
.
getDefault
().
shouldMeterInput
(
url
,
"GET"
);
ProgressSource
pi
=
null
;
if
(
meteredInput
)
{
pi
=
new
ProgressSource
(
url
,
"GET"
,
l
);
pi
.
beginTracking
();
}
is
=
new
MeteredStream
(
is
,
pi
,
l
);
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
/* do nothing, since all we were doing was trying to
get the size in bytes of the file */
/* do nothing, since all we were doing was trying to
get the size in bytes of the file */
}
String
type
=
guessContentTypeFromName
(
fullpath
);
if
(
type
==
null
&&
is
.
markSupported
())
{
type
=
guessContentTypeFromStream
(
is
);
}
if
(
type
!=
null
)
{
msgh
.
add
(
"content-type"
,
type
);
if
(
isAdir
)
{
msgh
.
add
(
"content-type"
,
"text/plain"
);
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
);
}
}
}
catch
(
FileNotFoundException
e
)
{
try
{
cd
(
fullpath
);
/* if that worked, then make a directory listing
and build an html stream with all the files in
the directory */
ftp
.
ascii
();
and build an html stream with all the files in
the directory */
ftp
.
setAsciiType
();
is
=
new
FtpInputStream
(
ftp
,
ftp
.
list
());
is
=
new
FtpInputStream
(
ftp
,
ftp
.
list
(
null
));
msgh
.
add
(
"content-type"
,
"text/plain"
);
msgh
.
add
(
"access-type"
,
"directory"
);
}
catch
(
IOException
ex
)
{
throw
new
FileNotFoundException
(
fullpath
);
}
catch
(
FtpProtocolException
ex2
)
{
throw
new
FileNotFoundException
(
fullpath
);
}
}
catch
(
FtpProtocolException
ftpe
)
{
throw
new
IOException
(
ftpe
);
}
setProperties
(
msgh
);
return
is
;
...
...
@@ -477,31 +500,45 @@ public class FtpURLConnection extends URLConnection {
* points to a directory
* @throws FtpProtocolException if errors occur during the transfert.
*/
@Override
public
OutputStream
getOutputStream
()
throws
IOException
{
if
(!
connected
)
{
connect
();
}
if
(
http
!=
null
)
return
http
.
getOutputStream
();
if
(
http
!=
null
)
{
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"
);
}
if
(
os
!=
null
)
{
return
os
;
}
decodePath
(
url
.
getPath
());
if
(
filename
==
null
||
filename
.
length
()
==
0
)
if
(
filename
==
null
||
filename
.
length
()
==
0
)
{
throw
new
IOException
(
"illegal filename for a PUT"
);
if
(
pathname
!=
null
)
cd
(
pathname
);
if
(
type
==
ASCII
)
ftp
.
ascii
();
else
ftp
.
binary
();
os
=
new
FtpOutputStream
(
ftp
,
ftp
.
put
(
filename
));
}
try
{
if
(
pathname
!=
null
)
{
cd
(
pathname
);
}
if
(
type
==
ASCII
)
{
ftp
.
setAsciiType
();
}
else
{
ftp
.
setBinaryType
();
}
os
=
new
FtpOutputStream
(
ftp
,
ftp
.
putFileStream
(
filename
,
false
));
}
catch
(
FtpProtocolException
e
)
{
throw
new
IOException
(
e
);
}
return
os
;
}
...
...
@@ -514,12 +551,13 @@ public class FtpURLConnection extends URLConnection {
*
* @return The <code>Permission</code> object.
*/
@Override
public
Permission
getPermission
()
{
if
(
permission
==
null
)
{
int
port
=
url
.
getPort
();
port
=
port
<
0
?
FtpClient
.
FTP_PORT
:
port
;
String
host
=
this
.
host
+
":"
+
port
;
permission
=
new
SocketPermission
(
host
,
"connect"
);
int
url
port
=
url
.
getPort
();
urlport
=
urlport
<
0
?
FtpClient
.
defaultPort
()
:
url
port
;
String
urlhost
=
this
.
host
+
":"
+
url
port
;
permission
=
new
SocketPermission
(
url
host
,
"connect"
);
}
return
permission
;
}
...
...
@@ -534,21 +572,22 @@ public class FtpURLConnection extends URLConnection {
* @throws IllegalStateException if already connected
* @see #getRequestProperty(java.lang.String)
*/
@Override
public
void
setRequestProperty
(
String
key
,
String
value
)
{
super
.
setRequestProperty
(
key
,
value
);
if
(
"type"
.
equals
(
key
))
{
if
(
"i"
.
equalsIgnoreCase
(
value
))
super
.
setRequestProperty
(
key
,
value
);
if
(
"type"
.
equals
(
key
))
{
if
(
"i"
.
equalsIgnoreCase
(
value
))
{
type
=
BIN
;
else
if
(
"a"
.
equalsIgnoreCase
(
value
))
}
else
if
(
"a"
.
equalsIgnoreCase
(
value
))
{
type
=
ASCII
;
else
if
(
"d"
.
equalsIgnoreCase
(
value
))
type
=
DIR
;
else
}
else
if
(
"d"
.
equalsIgnoreCase
(
value
))
{
type
=
DIR
;
}
else
{
throw
new
IllegalArgumentException
(
"Value of '"
+
key
+
"' request property was '"
+
value
+
"' when it must be either 'i', 'a' or 'd'"
);
"Value of '"
+
key
+
"' request property was '"
+
value
+
"' when it must be either 'i', 'a' or 'd'"
);
}
}
}
...
...
@@ -562,33 +601,41 @@ public class FtpURLConnection extends URLConnection {
* @throws IllegalStateException if already connected
* @see #setRequestProperty(java.lang.String, java.lang.String)
*/
@Override
public
String
getRequestProperty
(
String
key
)
{
String
value
=
super
.
getRequestProperty
(
key
);
String
value
=
super
.
getRequestProperty
(
key
);
if
(
value
==
null
)
{
if
(
"type"
.
equals
(
key
))
if
(
"type"
.
equals
(
key
))
{
value
=
(
type
==
ASCII
?
"a"
:
type
==
DIR
?
"d"
:
"i"
);
}
}
return
value
;
}
@Override
public
void
setConnectTimeout
(
int
timeout
)
{
if
(
timeout
<
0
)
if
(
timeout
<
0
)
{
throw
new
IllegalArgumentException
(
"timeouts can't be negative"
);
}
connectTimeout
=
timeout
;
}
@Override
public
int
getConnectTimeout
()
{
return
(
connectTimeout
<
0
?
0
:
connectTimeout
);
}
@Override
public
void
setReadTimeout
(
int
timeout
)
{
if
(
timeout
<
0
)
if
(
timeout
<
0
)
{
throw
new
IllegalArgumentException
(
"timeouts can't be negative"
);
}
readTimeout
=
timeout
;
}
@Override
public
int
getReadTimeout
()
{
return
readTimeout
<
0
?
0
:
readTimeout
;
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录