
org.globus.ftp.GridFTPClient Maven / Gradle / Ivy
The newest version!
/*
* Copyright 1999-2006 University of Chicago
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.globus.ftp;
import java.io.IOException;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.Vector;
import java.net.UnknownHostException;
import org.globus.ftp.exception.ClientException;
import org.globus.ftp.exception.ServerException;
import org.globus.ftp.exception.FTPReplyParseException;
import org.globus.ftp.exception.UnexpectedReplyCodeException;
import org.globus.ftp.vanilla.Command;
import org.globus.ftp.vanilla.Reply;
import org.globus.ftp.vanilla.TransferState;
import org.globus.ftp.vanilla.FTPControlChannel;
import org.globus.ftp.extended.GridFTPServerFacade;
import org.globus.ftp.extended.GridFTPControlChannel;
import org.globus.gsi.gssapi.auth.Authorization;
import org.globus.ftp.MultipleTransferComplete;
import org.globus.ftp.MultipleTransferCompleteListener;
import org.ietf.jgss.GSSCredential;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.globus.common.Version;
import java.io.ByteArrayOutputStream;
import java.text.DecimalFormat;
import org.globus.ftp.exception.FTPException;
/**
* This is the main user interface for GridFTP operations.
* Use this class for client - server or third party transfers
* with mode E, parallelism, markers, striping or GSI authentication.
* Consult the manual for general usage.
*
Note: If using with GridFTP servers operations like
* {@link #setMode(int) setMode()}, {@link #setType(int) setType()},
* {@link #setDataChannelProtection(int) setDataChannelProtection()},
* and {@link #setDataChannelAuthentication(DataChannelAuthentication)
* setDataChannelAuthentication()} that affect data channel settings
* must be called before passive or active data channel mode is set.
**/
public class GridFTPClient extends FTPClient {
private static Log logger =
LogFactory.getLog(GridFTPClient.class.getName());
//utility alias to session and localServer
protected GridFTPSession gSession;
protected GridFTPServerFacade gLocalServer;
protected String usageString;
/**
* Constructs client and connects it to the remote server.
*
* @param host remote server host
* @param port remote server port
*/
public GridFTPClient(String host, int port)
throws IOException, ServerException {
gSession = new GridFTPSession();
session = gSession;
controlChannel = new GridFTPControlChannel(host, port);
controlChannel.open();
gLocalServer =
new GridFTPServerFacade((GridFTPControlChannel)controlChannel);
localServer = gLocalServer;
gLocalServer.authorize();
this.useAllo = true;
setUsageInformation("CoG", Version.getVersion());
}
/**
* Performs authentication with specified user credentials.
*
* @param credential user credentials to use.
* @throws IOException on i/o error
* @throws ServerException on server refusal or faulty server behavior
*/
public void authenticate(GSSCredential credential)
throws IOException, ServerException {
authenticate(credential, null);
}
public void setUsageInformation(
String appName,
String appVer)
{
usageString = new String(
"CLIENTINFO appname=" + appName +";appver=" + appVer +
";schema=gsiftp;");
}
/**
* Performs authentication with specified user credentials and
* a specific username (assuming the user dn maps to the passed username).
*
* @param credential user credentials to use.
* @param username specific username to authenticate as.
* @throws IOException on i/o error
* @throws ServerException on server refusal or faulty server behavior
*/
public void authenticate(GSSCredential credential,
String username)
throws IOException, ServerException {
((GridFTPControlChannel)controlChannel).authenticate(credential,
username);
gLocalServer.setCredential(credential);
gSession.authorized = true;
this.username = username;
// quietly send version information to the server.
// ignore errors
try
{
String version = Version.getVersion();
this.site(usageString);
}
catch (Exception ex)
{
}
}
/**
* Performs remote directory listing like
* {@link FTPClient#list(String,String) FTPClient.list()}.
* Note: This method cannot be used
* in conjunction with parallelism or striping; set parallelism to
* 1 before calling it. Otherwise, use
* {@link FTPClient#list(String,String,DataSink) FTPClient.list()}.
* Unlike in vanilla FTP, here IMAGE mode is allowed.
* For more documentation, look at FTPClient.
*/
public Vector list(String filter, String modifier)
throws ServerException, ClientException, IOException {
if (gSession.parallel > 1) {
throw new ClientException(
ClientException.BAD_MODE,
"list cannot be called with parallelism");
}
return super.list(filter, modifier);
}
/**
* Performs remote directory listing like
* {@link FTPClient#nlist(String) FTPClient.nlist()}.
* Note: This method cannot be used
* in conjunction with parallelism or striping; set parallelism to
* 1 before calling it. Otherwise, use
* {@link FTPClient#nlist(String,DataSink) FTPClient.nlist()}.
* Unlike in vanilla FTP, here IMAGE mode is allowed.
* For more documentation, look at FTPClient.
*/
public Vector nlist(String path)
throws ServerException, ClientException, IOException {
if (gSession.parallel > 1) {
throw new ClientException(
ClientException.BAD_MODE,
"nlist cannot be called with parallelism");
}
return super.nlist(path);
}
/**
* Performs remote directory listing like
* {@link FTPClient#mlsd(String) FTPClient.mlsd()}.
* Note: This method cannot be used
* in conjunction with parallelism or striping; set parallelism to
* 1 before calling it. Otherwise, use
* {@link FTPClient#mlsd(String,DataSink) FTPClient.mlsd()}.
* Unlike in vanilla FTP, here IMAGE mode is allowed.
* For more documentation, look at FTPClient.
*/
public Vector mlsd(String filter)
throws ServerException, ClientException, IOException {
if (gSession.parallel > 1) {
throw new ClientException(
ClientException.BAD_MODE,
"mlsd cannot be called with parallelism");
}
return super.mlsd(filter);
}
protected void listCheck() throws ClientException {
// do nothing
}
protected void checkTransferParamsGet()
throws ServerException, IOException, ClientException {
Session localSession = localServer.getSession();
session.matches(localSession);
// if transfer modes have not been defined,
// set this (dest) as active
if (session.serverMode == Session.SERVER_DEFAULT) {
HostPort hp = setLocalPassive();
setActive(hp);
}
}
protected String getModeStr(int mode)
{
switch (mode) {
case Session.MODE_STREAM:
return "S";
case Session.MODE_BLOCK:
return "B";
case GridFTPSession.MODE_EBLOCK:
return "E";
default:
throw new IllegalArgumentException("Bad mode: " + mode);
}
}
/**
* Sets remote server TCP buffer size, in the following way:
* First see if server supports "SBUF" and if so, use it.
* If not, try the following commands until success:
* "SITE RETRBUFSIZE", "SITE RBUFSZ", "SITE RBUFSIZ",
* "SITE STORBUFSIZE", "SITE SBUFSZ", "SITE SBUFSIZ",
* "SITE BUFSIZE".
* Returns normally if the server confirms successfull setting of the
* remote buffer size, both for sending and for receiving data.
* Otherwise, throws ServerException.
**/
public void setTCPBufferSize(int size)
throws IOException, ServerException {
if (size <= 0) {
throw new IllegalArgumentException("size <= 0");
}
try {
boolean succeeded = false;
String sizeString = Integer.toString(size);
FeatureList feat = getFeatureList();
if (feat.contains(FeatureList.SBUF)) {
succeeded = tryExecutingCommand( new Command("SBUF", sizeString));
}
if (!succeeded) {
succeeded = tryExecutingCommand( new Command("SITE BUFSIZE", sizeString));
}
if (!succeeded) {
succeeded = tryExecutingTwoCommands(new Command("SITE RETRBUFSIZE", sizeString),
new Command("SITE STORBUFSIZE", sizeString));
}
if (!succeeded) {
succeeded = tryExecutingTwoCommands(new Command("SITE RBUFSZ", sizeString),
new Command("SITE SBUFSZ", sizeString));
}
if (!succeeded) {
succeeded = tryExecutingTwoCommands(new Command("SITE RBUFSIZ", sizeString),
new Command("SITE SBUFSIZ", sizeString));
}
if (succeeded) {
this.gSession.TCPBufferSize = size;
} else {
throw new ServerException(ServerException.SERVER_REFUSED,
"Server refused setting TCP buffer size with any of the known commands.");
}
} catch (FTPReplyParseException rpe) {
throw ServerException.embedFTPReplyParseException(rpe);
}
}
private boolean tryExecutingTwoCommands(Command cmd1, Command cmd2)
throws IOException,
FTPReplyParseException,
ServerException {
boolean result = tryExecutingCommand(cmd1);
if (result) {
result = tryExecutingCommand(cmd2);
}
return result;
}
/*
* This is like controlChannel.executeCommand, only that negative reply it
* returns "false" rather than throwing exception
*/
private boolean tryExecutingCommand(Command cmd)
throws IOException,
FTPReplyParseException,
ServerException {
Reply reply = controlChannel.exchange(cmd);
return Reply.isPositiveCompletion(reply);
}
/**
* Sets local TCP buffer size (for both receiving and sending).
**/
public void setLocalTCPBufferSize(int size)
throws ClientException {
if (size <=0 ) {
throw new IllegalArgumentException("size <= 0");
}
gLocalServer.setTCPBufferSize(size);
}
/**
* Sets remote server to striped passive server mode (SPAS).
**/
public HostPortList setStripedPassive()
throws IOException,
ServerException {
Command cmd = new Command("SPAS",
(controlChannel.isIPv6()) ? "2" : null);
Reply reply = null;
try {
reply = controlChannel.execute(cmd);
} catch (UnexpectedReplyCodeException urce) {
throw ServerException.embedUnexpectedReplyCodeException(urce);
} catch(FTPReplyParseException rpe) {
throw ServerException.embedFTPReplyParseException(rpe);
}
this.gSession.serverMode = GridFTPSession.SERVER_EPAS;
if (controlChannel.isIPv6()) {
gSession.serverAddressList =
HostPortList.parseIPv6Format(reply.getMessage());
int size = gSession.serverAddressList.size();
for (int i=0;i 1) {
throw new ClientException(
ClientException.BAD_MODE,
"mlsr cannot be called with parallelism");
}
Command cmd = (path == null) ?
new Command("MLSR") :
new Command("MLSR", path);
MlsxParserDataSink sink = new MlsxParserDataSink(writer);
performTransfer(cmd, sink);
}
private class MlsxParserDataSink implements DataSink {
private MlsxEntryWriter writer;
private byte[] buf = new byte[4096];
private int pos = 0;
public MlsxParserDataSink(MlsxEntryWriter w) {
writer = w;
}
public void write(Buffer buffer) throws IOException {
byte[] data = buffer.getBuffer();
int len = buffer.getLength();
int i = 0;
while (i < len && pos < buf.length) {
if (data[i] == '\r' || data[i] == '\n') {
if (pos > 0) {
try {
writer.write(new MlsxEntry(new String(buf, 0, pos)));
} catch (FTPException ex) {
throw new IOException();
}
}
pos = 0;
while (i < len && data[i] < ' ') ++i;
} else {
buf[pos++] = data[i++];
}
}
}
public void close() throws IOException {
writer.close();
}
}
/**
* Change the Unix group membership of a file.
*
* @param group the name or ID of the group
* @param file the file whose group membership should be changed
* @exception ServerException if an error occurred.
*/
public void changeGroup(String group, String file)
throws IOException, ServerException {
String arguments = group + " " + file;
Command cmd = new Command("SITE CHGRP", arguments);
try {
controlChannel.execute(cmd);
} catch (UnexpectedReplyCodeException urce) {
throw ServerException.embedUnexpectedReplyCodeException(urce);
} catch (FTPReplyParseException rpe) {
throw ServerException.embedFTPReplyParseException(rpe);
}
}
/**
* Change the modification time of a file.
*
* @param year Modifcation year
* @param month Modification month (1-12)
* @param day Modification day (1-31)
* @param hour Modification hour (0-23)
* @param min Modification minutes (0-59)
* @param sec Modification seconds (0-59)
* @param file file whose modification time should be changed
* @throws IOException
* @throws ServerException if an error occurred.
*/
public void changeModificationTime(int year, int month, int day, int hour, int min, int sec, String file)
throws IOException, ServerException {
DecimalFormat df2 = new DecimalFormat("00");
DecimalFormat df4 = new DecimalFormat("0000");
String arguments = df4.format(year) + df2.format(month) + df2.format(day) +
df2.format(hour) + df2.format(min) + df2.format(sec) + " " + file;
Command cmd = new Command("SITE UTIME", arguments);
try {
controlChannel.execute(cmd);
}catch (UnexpectedReplyCodeException urce) {
throw ServerException.embedUnexpectedReplyCodeException(urce);
} catch (FTPReplyParseException rpe) {
throw ServerException.embedFTPReplyParseException(rpe);
}
}
/**
* Create a symbolic link on the FTP server.
*
* @param link_target the path to which the symbolic link should point
* @param link_name the path of the symbolic link to create
* @throws IOException
* @throws ServerException if an error occurred.
*/
public void createSymbolicLink(String link_target, String link_name)
throws IOException, ServerException {
String arguments = link_target.replaceAll(" ", "%20") + " " + link_name;
Command cmd = new Command("SITE SYMLINK", arguments);
try {
controlChannel.execute(cmd);
}catch (UnexpectedReplyCodeException urce) {
throw ServerException.embedUnexpectedReplyCodeException(urce);
} catch (FTPReplyParseException rpe) {
throw ServerException.embedFTPReplyParseException(rpe);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy