1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-14 12:35:22 +02:00

[203500] Support encodings for SSH Sftp and FTP paths

This commit is contained in:
Martin Oberhuber 2007-09-26 22:50:53 +00:00
parent 48a344793d
commit a46672968f
5 changed files with 274 additions and 39 deletions

View file

@ -13,6 +13,7 @@
* Martin Oberhuber (Wind River) - [186773] split ISystemRegistryUI from ISystemRegistry * Martin Oberhuber (Wind River) - [186773] split ISystemRegistryUI from ISystemRegistry
* Martin Oberhuber (Wind River) - [186761] make the port setting configurable * Martin Oberhuber (Wind River) - [186761] make the port setting configurable
* Martin Oberhuber (Wind River) - [198790] make SSH createSession() protected * Martin Oberhuber (Wind River) - [198790] make SSH createSession() protected
* Martin Oberhuber (Wind River) - [203500] Support encodings for SSH Sftp paths
*******************************************************************************/ *******************************************************************************/
package org.eclipse.rse.internal.connectorservice.ssh; package org.eclipse.rse.internal.connectorservice.ssh;
@ -61,6 +62,8 @@ public class SshConnectorService extends StandardConnectorService implements ISs
private static final int CONNECT_DEFAULT_TIMEOUT = 60; //seconds private static final int CONNECT_DEFAULT_TIMEOUT = 60; //seconds
private Session session; private Session session;
private SessionLostHandler fSessionLostHandler; private SessionLostHandler fSessionLostHandler;
/** Indicates the default string encoding on this platform */
private static String _defaultEncoding = new java.io.InputStreamReader(new java.io.ByteArrayInputStream(new byte[0])).getEncoding();
public SshConnectorService(IHost host) { public SshConnectorService(IHost host) {
super(SshConnectorResources.SshConnectorService_Name, SshConnectorResources.SshConnectorService_Description, host, SSH_DEFAULT_PORT); super(SshConnectorResources.SshConnectorService_Name, SshConnectorResources.SshConnectorService_Description, host, SSH_DEFAULT_PORT);
@ -210,6 +213,15 @@ public class SshConnectorService extends StandardConnectorService implements ISs
return session; return session;
} }
public String getControlEncoding() {
//TODO this code should be in IHost
String encoding = getHost().getDefaultEncoding(false);
if (encoding==null) encoding = getHost().getDefaultEncoding(true);
if (encoding==null) encoding = _defaultEncoding;
//</code to be in IHost>
return encoding;
}
/** /**
* Handle session-lost events. * Handle session-lost events.
* This is generic for any sort of connector service. * This is generic for any sort of connector service.

View file

@ -37,7 +37,7 @@
* Javier Montalvo Orus (Symbian) - Fixing 168120 - [ftp] root filter resolves to home dir * Javier Montalvo Orus (Symbian) - Fixing 168120 - [ftp] root filter resolves to home dir
* Javier Montalvo Orus (Symbian) - Fixing 169680 - [ftp] FTP files subsystem and service should use passive mode * Javier Montalvo Orus (Symbian) - Fixing 169680 - [ftp] FTP files subsystem and service should use passive mode
* Javier Montalvo Orus (Symbian) - Fixing 174828 - [ftp] Folders are attempted to be removed as files * Javier Montalvo Orus (Symbian) - Fixing 174828 - [ftp] Folders are attempted to be removed as files
* Javier Montalvo Orus (Symbian) - Fixing 176216 - [api] FTP sould provide API to allow clients register their own FTPListingParser * Javier Montalvo Orus (Symbian) - Fixing 176216 - [api] FTP should provide API to allow clients register their own FTPListingParser
* Martin Oberhuber (Wind River) - [186128] Move IProgressMonitor last in all API * Martin Oberhuber (Wind River) - [186128] Move IProgressMonitor last in all API
* Javier Montalvo Orus (Symbian) - improved autodetection of FTPListingParser * Javier Montalvo Orus (Symbian) - improved autodetection of FTPListingParser
* Javier Montalvo Orus (Symbian) - [187096] Drag&Drop + Copy&Paste shows error message on FTP connection * Javier Montalvo Orus (Symbian) - [187096] Drag&Drop + Copy&Paste shows error message on FTP connection
@ -61,6 +61,7 @@
* Martin Oberhuber (Wind River) - [203306] Fix Deadlock comparing two files on FTP * Martin Oberhuber (Wind River) - [203306] Fix Deadlock comparing two files on FTP
* Martin Oberhuber (Wind River) - [204669] Fix ftp path concatenation on systems using backslash separator * Martin Oberhuber (Wind River) - [204669] Fix ftp path concatenation on systems using backslash separator
* Martin Oberhuber (Wind River) - [203490] Fix NPE in FTPService.getUserHome() * Martin Oberhuber (Wind River) - [203490] Fix NPE in FTPService.getUserHome()
* Martin Oberhuber (Wind River) - [203500] Support encodings for FTP paths
********************************************************************************/ ********************************************************************************/
package org.eclipse.rse.internal.services.files.ftp; package org.eclipse.rse.internal.services.files.ftp;
@ -74,6 +75,7 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -96,6 +98,8 @@ import org.eclipse.rse.services.clientserver.FileTypeMatcher;
import org.eclipse.rse.services.clientserver.IMatcher; import org.eclipse.rse.services.clientserver.IMatcher;
import org.eclipse.rse.services.clientserver.NamePatternMatcher; import org.eclipse.rse.services.clientserver.NamePatternMatcher;
import org.eclipse.rse.services.clientserver.PathUtility; import org.eclipse.rse.services.clientserver.PathUtility;
import org.eclipse.rse.services.clientserver.messages.IndicatorException;
import org.eclipse.rse.services.clientserver.messages.SystemMessage;
import org.eclipse.rse.services.clientserver.messages.SystemMessageException; import org.eclipse.rse.services.clientserver.messages.SystemMessageException;
import org.eclipse.rse.services.files.AbstractFileService; import org.eclipse.rse.services.files.AbstractFileService;
import org.eclipse.rse.services.files.IFileService; import org.eclipse.rse.services.files.IFileService;
@ -117,6 +121,7 @@ public class FTPService extends AbstractFileService implements IFileService, IFT
private transient String _userId; private transient String _userId;
private transient String _password; private transient String _password;
private transient int _portNumber; private transient int _portNumber;
private transient String _controlEncoding; //Encoding to be used for file and path names
private OutputStream _ftpLoggingOutputStream; private OutputStream _ftpLoggingOutputStream;
private IPropertySet _ftpPropertySet; private IPropertySet _ftpPropertySet;
@ -274,12 +279,68 @@ public class FTPService extends AbstractFileService implements IFileService, IFT
_entryParserFactory = entryParserFactory; _entryParserFactory = entryParserFactory;
} }
/**
* Set the character encoding to be used on the FTP command channel.
* The encoding must be compatible with ASCII since FTP commands will
* be sent with the same encoding. Therefore, wide
* (16-bit) encodings are not supported.
* @param encoding Encoding to set
*/
public void setControlEncoding(String encoding)
{
_controlEncoding = encoding;
}
/**
* Check whether the given Unicode String can be properly represented with the
* specified control encoding. Throw a SystemMessageException if it turns out
* that information would be lost.
* @param s String to check
* @return the original String or a quoted or re-coded version if possible
* @throws SystemMessageException if information is lost
*/
protected String checkEncoding(String s) throws SystemMessageException {
String encoding = _controlEncoding!=null ? _controlEncoding : getFTPClient().getControlEncoding();
try {
byte[] bytes = s.getBytes(encoding);
String decoded = new String(bytes, encoding);
if (!s.equals(decoded)) {
int i=0;
int lmax = Math.min(s.length(), decoded.length());
while( (i<lmax) && (s.charAt(i)==decoded.charAt(i))) {
i++;
}
//String sbad=s.substring(Math.max(i-2,0), Math.min(i+2,lmax));
char sbad = s.charAt(i);
//FIXME Need to externalize this message in 3.0
String msg = "Cannot express character \'"+sbad+"\'(0x"+Integer.toHexString(sbad) +") with " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ "encoding \""+encoding+"\". "; //$NON-NLS-1$ //$NON-NLS-2$
msg += "Please specify a different encoding in host properties."; //$NON-NLS-1$
throw new UnsupportedEncodingException(msg);
}
return s;
} catch(UnsupportedEncodingException e) {
try {
//SystemMessage msg = new SystemMessage("RSE","F","9999",'E',e.getMessage(),"Please specify a different encoding in host properties."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
SystemMessage msg = new SystemMessage("RSE","F","9999",'E',e.getMessage(),""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
//throw new RemoteFileIOException(new SystemMessageException(msg));
throw new SystemMessageException(msg);
} catch(IndicatorException ind) {
throw new RemoteFileIOException(e);
}
}
}
public void connect() throws RemoteFileSecurityException,IOException public void connect() throws RemoteFileSecurityException,IOException
{ {
if (_ftpClient == null) if (_ftpClient == null)
{ {
_ftpClient = new FTPClient(); _ftpClient = new FTPClient();
// Encoding of control connection
if(_controlEncoding!=null) {
_ftpClient.setControlEncoding(_controlEncoding);
}
} }
if(_ftpLoggingOutputStream!=null) if(_ftpLoggingOutputStream!=null)
@ -403,6 +464,10 @@ public class FTPService extends AbstractFileService implements IFileService, IFT
if (_ftpClient == null) if (_ftpClient == null)
{ {
_ftpClient = new FTPClient(); _ftpClient = new FTPClient();
// Encoding of control connection
if(_controlEncoding!=null) {
_ftpClient.setControlEncoding(_controlEncoding);
}
} }
if(_hostName!=null) if(_hostName!=null)
@ -433,6 +498,7 @@ public class FTPService extends AbstractFileService implements IFileService, IFT
FTPClient ftpClient = new FTPClient(); FTPClient ftpClient = new FTPClient();
boolean ok=false; boolean ok=false;
try { try {
ftpClient.setControlEncoding(_ftpClient.getControlEncoding());
ftpClient.connect(_ftpClient.getRemoteAddress()); ftpClient.connect(_ftpClient.getRemoteAddress());
ftpClient.login(_userId,_password); ftpClient.login(_userId,_password);
@ -485,6 +551,8 @@ public class FTPService extends AbstractFileService implements IFileService, IFT
*/ */
protected FTPHostFile getFileInternal(String remoteParent, String fileName, IProgressMonitor monitor) throws SystemMessageException protected FTPHostFile getFileInternal(String remoteParent, String fileName, IProgressMonitor monitor) throws SystemMessageException
{ {
remoteParent = checkEncoding(remoteParent);
fileName = checkEncoding(fileName);
if (monitor!=null){ if (monitor!=null){
if (monitor.isCanceled()) { if (monitor.isCanceled()) {
throw new RemoteFileCancelledException(); throw new RemoteFileCancelledException();
@ -584,6 +652,7 @@ public class FTPService extends AbstractFileService implements IFileService, IFT
*/ */
protected IHostFile[] internalFetch(String parentPath, String fileFilter, int fileType, IProgressMonitor monitor) throws SystemMessageException protected IHostFile[] internalFetch(String parentPath, String fileFilter, int fileType, IProgressMonitor monitor) throws SystemMessageException
{ {
parentPath = checkEncoding(parentPath);
if (monitor!=null){ if (monitor!=null){
if (monitor.isCanceled()) { if (monitor.isCanceled()) {
throw new RemoteFileCancelledException(); throw new RemoteFileCancelledException();
@ -676,6 +745,8 @@ public class FTPService extends AbstractFileService implements IFileService, IFT
public boolean upload(File localFile, String remoteParent, String remoteFile, boolean isBinary, String srcEncoding, String hostEncoding, IProgressMonitor monitor) throws SystemMessageException public boolean upload(File localFile, String remoteParent, String remoteFile, boolean isBinary, String srcEncoding, String hostEncoding, IProgressMonitor monitor) throws SystemMessageException
{ {
boolean retValue = true; boolean retValue = true;
remoteParent = checkEncoding(remoteParent);
remoteFile = checkEncoding(remoteFile);
if (monitor!=null){ if (monitor!=null){
if (monitor.isCanceled()) { if (monitor.isCanceled()) {
@ -758,6 +829,8 @@ public class FTPService extends AbstractFileService implements IFileService, IFT
public boolean upload(InputStream stream, String remoteParent, String remoteFile, boolean isBinary, String hostEncoding, IProgressMonitor monitor) throws SystemMessageException public boolean upload(InputStream stream, String remoteParent, String remoteFile, boolean isBinary, String hostEncoding, IProgressMonitor monitor) throws SystemMessageException
{ {
boolean retValue = true; boolean retValue = true;
remoteParent = checkEncoding(remoteParent);
remoteFile = checkEncoding(remoteFile);
try try
{ {
@ -897,7 +970,6 @@ public class FTPService extends AbstractFileService implements IFileService, IFT
return new FTPHostFile("",_userHome,true,true,0,0,true); //$NON-NLS-1$ return new FTPHostFile("",_userHome,true,true,0,0,true); //$NON-NLS-1$
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* @see org.eclipse.rse.services.files.IFileService#getRoots(org.eclipse.core.runtime.IProgressMonitor) * @see org.eclipse.rse.services.files.IFileService#getRoots(org.eclipse.core.runtime.IProgressMonitor)
@ -924,6 +996,8 @@ public class FTPService extends AbstractFileService implements IFileService, IFT
*/ */
public boolean delete(String remoteParent, String fileName, IProgressMonitor monitor) throws SystemMessageException { public boolean delete(String remoteParent, String fileName, IProgressMonitor monitor) throws SystemMessageException {
boolean hasSucceeded = false; boolean hasSucceeded = false;
remoteParent = checkEncoding(remoteParent);
fileName = checkEncoding(fileName);
MyProgressMonitor progressMonitor = new MyProgressMonitor(monitor); MyProgressMonitor progressMonitor = new MyProgressMonitor(monitor);
progressMonitor.init(FTPServiceResources.FTP_File_Service_Deleting_Task+fileName, 1); progressMonitor.init(FTPServiceResources.FTP_File_Service_Deleting_Task+fileName, 1);
@ -980,8 +1054,10 @@ public class FTPService extends AbstractFileService implements IFileService, IFT
* @see org.eclipse.rse.services.files.IFileService#rename(org.eclipse.core.runtime.IProgressMonitor, java.lang.String, java.lang.String, java.lang.String) * @see org.eclipse.rse.services.files.IFileService#rename(org.eclipse.core.runtime.IProgressMonitor, java.lang.String, java.lang.String, java.lang.String)
*/ */
public boolean rename(String remoteParent, String oldName, String newName, IProgressMonitor monitor) throws SystemMessageException { public boolean rename(String remoteParent, String oldName, String newName, IProgressMonitor monitor) throws SystemMessageException {
boolean success = false; boolean success = false;
remoteParent = checkEncoding(remoteParent);
oldName = checkEncoding(oldName);
newName = checkEncoding(newName);
if(_commandMutex.waitForLock(monitor, Long.MAX_VALUE)) if(_commandMutex.waitForLock(monitor, Long.MAX_VALUE))
{ {
@ -1026,8 +1102,11 @@ public class FTPService extends AbstractFileService implements IFileService, IFT
* @see org.eclipse.rse.services.files.IFileService#move(org.eclipse.core.runtime.IProgressMonitor, java.lang.String, java.lang.String, java.lang.String, java.lang.String) * @see org.eclipse.rse.services.files.IFileService#move(org.eclipse.core.runtime.IProgressMonitor, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/ */
public boolean move(String srcParent, String srcName, String tgtParent, String tgtName, IProgressMonitor monitor) throws SystemMessageException{ public boolean move(String srcParent, String srcName, String tgtParent, String tgtName, IProgressMonitor monitor) throws SystemMessageException{
boolean success = false; boolean success = false;
srcParent = checkEncoding(srcParent);
srcName = checkEncoding(srcName);
tgtParent = checkEncoding(tgtParent);
tgtName = checkEncoding(tgtName);
if(_commandMutex.waitForLock(monitor, Long.MAX_VALUE)) if(_commandMutex.waitForLock(monitor, Long.MAX_VALUE))
{ {
@ -1061,6 +1140,8 @@ public class FTPService extends AbstractFileService implements IFileService, IFT
*/ */
public IHostFile createFolder(String remoteParent, String folderName, IProgressMonitor monitor) throws SystemMessageException public IHostFile createFolder(String remoteParent, String folderName, IProgressMonitor monitor) throws SystemMessageException
{ {
remoteParent = checkEncoding(remoteParent);
folderName = checkEncoding(folderName);
if(_commandMutex.waitForLock(monitor, Long.MAX_VALUE)) if(_commandMutex.waitForLock(monitor, Long.MAX_VALUE))
{ {
try try
@ -1093,7 +1174,8 @@ public class FTPService extends AbstractFileService implements IFileService, IFT
* @see org.eclipse.rse.services.files.IFileService#createFile(org.eclipse.core.runtime.IProgressMonitor, java.lang.String, java.lang.String) * @see org.eclipse.rse.services.files.IFileService#createFile(org.eclipse.core.runtime.IProgressMonitor, java.lang.String, java.lang.String)
*/ */
public IHostFile createFile(String remoteParent, String fileName, IProgressMonitor monitor) throws SystemMessageException{ public IHostFile createFile(String remoteParent, String fileName, IProgressMonitor monitor) throws SystemMessageException{
remoteParent = checkEncoding(remoteParent);
fileName = checkEncoding(fileName);
try { try {
File tempFile = File.createTempFile("ftp", "temp"); //$NON-NLS-1$ //$NON-NLS-2$ File tempFile = File.createTempFile("ftp", "temp"); //$NON-NLS-1$ //$NON-NLS-2$
tempFile.deleteOnExit(); tempFile.deleteOnExit();
@ -1385,6 +1467,8 @@ public class FTPService extends AbstractFileService implements IFileService, IFT
* @see org.eclipse.rse.services.files.AbstractFileService#getOutputStream(java.lang.String, java.lang.String, boolean, org.eclipse.core.runtime.IProgressMonitor) * @see org.eclipse.rse.services.files.AbstractFileService#getOutputStream(java.lang.String, java.lang.String, boolean, org.eclipse.core.runtime.IProgressMonitor)
*/ */
public OutputStream getOutputStream(String remoteParent, String remoteFile, boolean isBinary, IProgressMonitor monitor) throws SystemMessageException { public OutputStream getOutputStream(String remoteParent, String remoteFile, boolean isBinary, IProgressMonitor monitor) throws SystemMessageException {
remoteParent = checkEncoding(remoteParent);
remoteFile = checkEncoding(remoteFile);
if (monitor != null && monitor.isCanceled()){ if (monitor != null && monitor.isCanceled()){
throw new RemoteFileCancelledException(); throw new RemoteFileCancelledException();

View file

@ -12,7 +12,7 @@
* Emily Bruner, Mazen Faraj, Adrian Storisteanu, Li Ding, and Kent Hawley. * Emily Bruner, Mazen Faraj, Adrian Storisteanu, Li Ding, and Kent Hawley.
* *
* Contributors: * Contributors:
* {Name} (company) - description of contribution. * Martin Oberhuber (Wind River) - [203500] Support encodings for SSH Sftp paths
*******************************************************************************/ *******************************************************************************/
package org.eclipse.rse.internal.services.ssh; package org.eclipse.rse.internal.services.ssh;
@ -26,4 +26,7 @@ public interface ISshSessionProvider
/* Inform the connectorService that a session has been lost. */ /* Inform the connectorService that a session has been lost. */
public void handleSessionLost(); public void handleSessionLost();
/* Return the encoding to be used for file and directory names */
public String getControlEncoding();
} }

View file

@ -14,6 +14,7 @@
* Martin Oberhuber (Wind River) - [199548] Avoid touching files on setReadOnly() if unnecessary * Martin Oberhuber (Wind River) - [199548] Avoid touching files on setReadOnly() if unnecessary
* Benjamin Muskalla (b.muskalla@gmx.net) - [174690][ssh] cannot delete symbolic links on remote systems * Benjamin Muskalla (b.muskalla@gmx.net) - [174690][ssh] cannot delete symbolic links on remote systems
* Martin Oberhuber (Wind River) - [203490] Fix NPE in SftpService.getUserHome() * Martin Oberhuber (Wind River) - [203490] Fix NPE in SftpService.getUserHome()
* Martin Oberhuber (Wind River) - [203500] Support encodings for SSH Sftp paths
*******************************************************************************/ *******************************************************************************/
package org.eclipse.rse.internal.services.ssh.files; package org.eclipse.rse.internal.services.ssh.files;
@ -26,10 +27,12 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Vector; import java.util.Vector;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor;
@ -53,6 +56,8 @@ import org.eclipse.rse.services.clientserver.FileTypeMatcher;
import org.eclipse.rse.services.clientserver.IMatcher; import org.eclipse.rse.services.clientserver.IMatcher;
import org.eclipse.rse.services.clientserver.NamePatternMatcher; import org.eclipse.rse.services.clientserver.NamePatternMatcher;
import org.eclipse.rse.services.clientserver.PathUtility; import org.eclipse.rse.services.clientserver.PathUtility;
import org.eclipse.rse.services.clientserver.messages.IndicatorException;
import org.eclipse.rse.services.clientserver.messages.SystemMessage;
import org.eclipse.rse.services.clientserver.messages.SystemMessageException; import org.eclipse.rse.services.clientserver.messages.SystemMessageException;
import org.eclipse.rse.services.files.AbstractFileService; import org.eclipse.rse.services.files.AbstractFileService;
import org.eclipse.rse.services.files.IFileService; import org.eclipse.rse.services.files.IFileService;
@ -140,6 +145,10 @@ public class SftpFileService extends AbstractFileService implements IFileService
private String fUserHome; private String fUserHome;
private Mutex fDirChannelMutex = new Mutex(); private Mutex fDirChannelMutex = new Mutex();
private long fDirChannelTimeout = 5000; //max.5 seconds to obtain dir channel private long fDirChannelTimeout = 5000; //max.5 seconds to obtain dir channel
/** Client-desired encoding for file and path names */
private String fControlEncoding = null;
/** Indicates the default string encoding on this platform */
private static String defaultEncoding = new java.io.InputStreamReader(new java.io.ByteArrayInputStream(new byte[0])).getEncoding();
// public SftpFileService(SshConnectorService conn) { // public SftpFileService(SshConnectorService conn) {
// fConnector = conn; // fConnector = conn;
@ -149,6 +158,121 @@ public class SftpFileService extends AbstractFileService implements IFileService
fSessionProvider = sessionProvider; fSessionProvider = sessionProvider;
} }
public void setControlEncoding(String encoding) {
fControlEncoding = encoding;
}
/**
* Encode String with requested user encoding, in case it differs from Platform default encoding.
* @param s String to encode
* @return encoded String
* @throws SystemMessageException
*/
protected String recode(String s) throws SystemMessageException {
if (fControlEncoding==null) {
return s;
} else if (fControlEncoding.equals(defaultEncoding)) {
return s;
}
try {
byte[] bytes = s.getBytes(fControlEncoding); //what we want on the wire
return new String(bytes); //what we need to tell Jsch to get this on the wire
} catch(UnsupportedEncodingException e) {
throw makeSystemMessageException(e);
}
}
/**
* Recode String, and check that no information is lost.
* Throw an exception in case the desired Unicode String can not be expressed
* by the current encodings. Also enquotes result characters '?' and '*' for
* Jsch if necessary.
* @param s String to recode
* @return recoded String
* @throws SystemMessageException if information is lost
*/
protected String recodeSafe(String s) throws SystemMessageException {
try {
String recoded = recode(s);
byte[] bytes = recoded.getBytes(defaultEncoding);
String decoded = decode(new String(bytes));
if (!s.equals(decoded)) {
int i=0;
int lmax = Math.min(s.length(), decoded.length());
while( (i<lmax) && (s.charAt(i)==decoded.charAt(i))) {
i++;
}
//String sbad=s.substring(Math.max(i-2,0), Math.min(i+2,lmax));
char sbad = s.charAt(i);
String msg = "Cannot express character \'"+sbad+"\'(0x"+Integer.toHexString(sbad) +") with "; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
if (fControlEncoding==null || fControlEncoding.equals(defaultEncoding)) {
msg += "default encoding \""+defaultEncoding+"\". "; //$NON-NLS-1$ //$NON-NLS-2$
} else {
msg += "encoding \""+fControlEncoding+"\" over local default encoding \""+defaultEncoding+"\". "; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
msg += "Please specify a different encoding in host properties."; //$NON-NLS-1$
throw new UnsupportedEncodingException(msg);
}
//Quote ? and * characters for Jsch
//FIXME bug 204705: this does not work properly for commands like ls(), due to a Jsch bug
return quoteForJsch(recoded);
} catch(UnsupportedEncodingException e) {
try {
//SystemMessage msg = new SystemMessage("RSE","F","9999",'E',e.getMessage(),"Please specify a different encoding in host properties."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
SystemMessage msg = new SystemMessage("RSE","F","9999",'E',e.getMessage(),""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
//throw new RemoteFileIOException(new SystemMessageException(msg));
throw new SystemMessageException(msg);
} catch(IndicatorException ind) {
throw makeSystemMessageException(e);
}
}
}
/**
* Decode String (sftp result) with requested user encoding, in case it differs from Platform default encoding.
* @param s String to decode
* @return decoded String
* @throws SystemMessageException
*/
protected String decode(String s) throws SystemMessageException {
if (fControlEncoding==null) {
return s;
} else if (fControlEncoding.equals(defaultEncoding)) {
return s;
}
try {
byte[] bytes = s.getBytes(); //original bytes sent by SSH
return new String(bytes, fControlEncoding);
} catch(UnsupportedEncodingException e) {
throw makeSystemMessageException(e);
}
}
/** Regular expression pattern to know when Jsch needs quoting. */
private static Pattern quoteForJschPattern = Pattern.compile("[*?\\\\]"); //$NON-NLS-1$
/**
* Quote characters '?' and '*' for Jsch because it would otherwise
* use them as patterns for globbing.
* @param s String to enquote
* @return String with '?' and '*' quoted.
*/
protected String quoteForJsch(String s) {
if(quoteForJschPattern.matcher(s).find()) {
StringBuffer buf = new StringBuffer(s.length()+8);
for(int i=0; i<s.length(); i++) {
char c = s.charAt(i);
// if(c=='?' || c=='*' || c=='\\') {
if(c=='?' || c=='*') {
buf.append('\\');
}
buf.append(c);
}
s = buf.toString();
}
return s;
}
public String getName() { public String getName() {
return SshServiceResources.SftpFileService_Name; return SshServiceResources.SftpFileService_Name;
} }
@ -164,7 +288,8 @@ public class SftpFileService extends AbstractFileService implements IFileService
Channel channel=session.openChannel("sftp"); //$NON-NLS-1$ Channel channel=session.openChannel("sftp"); //$NON-NLS-1$
channel.connect(); channel.connect();
fChannelSftp=(ChannelSftp)channel; fChannelSftp=(ChannelSftp)channel;
fUserHome = fChannelSftp.pwd(); setControlEncoding(fSessionProvider.getControlEncoding());
fUserHome = decode(fChannelSftp.pwd());
Activator.trace("SftpFileService.connected"); //$NON-NLS-1$ Activator.trace("SftpFileService.connected"); //$NON-NLS-1$
} catch(Exception e) { } catch(Exception e) {
Activator.trace("SftpFileService.connecting failed: "+e.toString()); //$NON-NLS-1$ Activator.trace("SftpFileService.connecting failed: "+e.toString()); //$NON-NLS-1$
@ -256,7 +381,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
SftpATTRS attrs = null; SftpATTRS attrs = null;
if (fDirChannelMutex.waitForLock(monitor, fDirChannelTimeout)) { if (fDirChannelMutex.waitForLock(monitor, fDirChannelTimeout)) {
try { try {
attrs = getChannel("SftpFileService.getFile: "+fileName).stat(remoteParent+'/'+fileName); //$NON-NLS-1$ attrs = getChannel("SftpFileService.getFile: "+fileName).stat(recodeSafe(remoteParent+'/'+fileName)); //$NON-NLS-1$
Activator.trace("SftpFileService.getFile <--"); //$NON-NLS-1$ Activator.trace("SftpFileService.getFile <--"); //$NON-NLS-1$
node = makeHostFile(remoteParent, fileName, attrs); node = makeHostFile(remoteParent, fileName, attrs);
} catch(Exception e) { } catch(Exception e) {
@ -304,12 +429,12 @@ public class SftpFileService extends AbstractFileService implements IFileService
List results = new ArrayList(); List results = new ArrayList();
if (fDirChannelMutex.waitForLock(monitor, fDirChannelTimeout)) { if (fDirChannelMutex.waitForLock(monitor, fDirChannelTimeout)) {
try { try {
Vector vv=getChannel("SftpFileService.internalFetch: "+parentPath).ls(parentPath); //$NON-NLS-1$ Vector vv=getChannel("SftpFileService.internalFetch: "+parentPath).ls(recodeSafe(parentPath)); //$NON-NLS-1$
for(int ii=0; ii<vv.size(); ii++) { for(int ii=0; ii<vv.size(); ii++) {
Object obj=vv.elementAt(ii); Object obj=vv.elementAt(ii);
if(obj instanceof ChannelSftp.LsEntry){ if(obj instanceof ChannelSftp.LsEntry){
ChannelSftp.LsEntry lsEntry = (ChannelSftp.LsEntry)obj; ChannelSftp.LsEntry lsEntry = (ChannelSftp.LsEntry)obj;
String fileName = lsEntry.getFilename(); String fileName = decode(lsEntry.getFilename());
if (".".equals(fileName) || "..".equals(fileName)) { //$NON-NLS-1$ //$NON-NLS-2$ if (".".equals(fileName) || "..".equals(fileName)) { //$NON-NLS-1$ //$NON-NLS-2$
//don't show the trivial names //don't show the trivial names
continue; continue;
@ -353,15 +478,15 @@ public class SftpFileService extends AbstractFileService implements IFileService
// try { // try {
// //Note: readlink() is supported only with jsch-0.1.29 or higher. // //Note: readlink() is supported only with jsch-0.1.29 or higher.
// //By catching the exception we remain backward compatible. // //By catching the exception we remain backward compatible.
// linkTarget=getChannel("makeHostFile.readlink").readlink(node.getAbsolutePath()); //$NON-NLS-1$ // linkTarget=getChannel("makeHostFile.readlink").readlink(recode(node.getAbsolutePath())); //$NON-NLS-1$
// //TODO: Classify the type of resource linked to as file, folder or broken link // //TODO: Classify the type of resource linked to as file, folder or broken link
// } catch(Exception e) {} // } catch(Exception e) {}
//check if the link points to a directory //check if the link points to a directory
try { try {
getChannel("makeHostFile.chdir").cd(parentPath+'/'+fileName); //$NON-NLS-1$ getChannel("makeHostFile.chdir").cd(recode(parentPath+'/'+fileName)); //$NON-NLS-1$
linkTarget=getChannel("makeHostFile.chdir").pwd(); //$NON-NLS-1$ linkTarget=decode(getChannel("makeHostFile.chdir").pwd()); //$NON-NLS-1$
if (linkTarget!=null && !linkTarget.equals(parentPath+'/'+fileName)) { if (linkTarget!=null && !linkTarget.equals(parentPath+'/'+fileName)) {
attrsTarget = getChannel("SftpFileService.getFile").stat(linkTarget); //$NON-NLS-1$ attrsTarget = getChannel("SftpFileService.getFile").stat(recode(linkTarget)); //$NON-NLS-1$
} else { } else {
linkTarget=null; linkTarget=null;
} }
@ -421,6 +546,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
} }
dst += remoteFile; dst += remoteFile;
} }
dst = recodeSafe(dst);
getChannel("SftpFileService.upload "+remoteFile); //check the session is healthy //$NON-NLS-1$ getChannel("SftpFileService.upload "+remoteFile); //check the session is healthy //$NON-NLS-1$
channel=(ChannelSftp)fSessionProvider.getSession().openChannel("sftp"); //$NON-NLS-1$ channel=(ChannelSftp)fSessionProvider.getSession().openChannel("sftp"); //$NON-NLS-1$
channel.connect(); channel.connect();
@ -439,7 +565,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
if (attr.getSize() != localFile.length()) { if (attr.getSize() != localFile.length()) {
//Error: file truncated? - Inform the user!! //Error: file truncated? - Inform the user!!
//TODO test if this works //TODO test if this works
throw makeSystemMessageException(new IOException(NLS.bind(SshServiceResources.SftpFileService_Error_upload_size,dst))); throw makeSystemMessageException(new IOException(NLS.bind(SshServiceResources.SftpFileService_Error_upload_size,remoteFile)));
//return false; //return false;
} }
} }
@ -532,7 +658,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
//localFile.createNewFile(); //localFile.createNewFile();
} }
//TODO Ascii/binary? //TODO Ascii/binary?
String remotePath = remoteParent+'/'+remoteFile; String remotePath = recode(remoteParent+'/'+remoteFile);
int mode=ChannelSftp.OVERWRITE; int mode=ChannelSftp.OVERWRITE;
MyProgressMonitor sftpMonitor = new MyProgressMonitor(monitor); MyProgressMonitor sftpMonitor = new MyProgressMonitor(monitor);
getChannel("SftpFileService.download "+remoteFile); //check the session is healthy //$NON-NLS-1$ getChannel("SftpFileService.download "+remoteFile); //check the session is healthy //$NON-NLS-1$
@ -550,7 +676,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
if (attr.getSize() != localFile.length()) { if (attr.getSize() != localFile.length()) {
//Error: file truncated? - Inform the user!! //Error: file truncated? - Inform the user!!
//TODO test if this works //TODO test if this works
throw makeSystemMessageException(new IOException(NLS.bind(SshServiceResources.SftpFileService_Error_download_size,remotePath))); throw makeSystemMessageException(new IOException(NLS.bind(SshServiceResources.SftpFileService_Error_download_size,remoteFile)));
//return false; //return false;
} }
} }
@ -603,7 +729,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
IHostFile result = null; IHostFile result = null;
if (fDirChannelMutex.waitForLock(monitor, fDirChannelTimeout)) { if (fDirChannelMutex.waitForLock(monitor, fDirChannelTimeout)) {
try { try {
String fullPath = remoteParent + '/' + fileName; String fullPath = recodeSafe(remoteParent + '/' + fileName);
OutputStream os = getChannel("SftpFileService.createFile").put(fullPath); //$NON-NLS-1$ OutputStream os = getChannel("SftpFileService.createFile").put(fullPath); //$NON-NLS-1$
//TODO workaround bug 153118: write a single space //TODO workaround bug 153118: write a single space
//since jsch hangs when trying to close the stream without writing //since jsch hangs when trying to close the stream without writing
@ -629,7 +755,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
IHostFile result = null; IHostFile result = null;
if (fDirChannelMutex.waitForLock(monitor, fDirChannelTimeout)) { if (fDirChannelMutex.waitForLock(monitor, fDirChannelTimeout)) {
try { try {
String fullPath = remoteParent + '/' + folderName; String fullPath = recodeSafe(remoteParent + '/' + folderName);
getChannel("SftpFileService.createFolder").mkdir(fullPath); //$NON-NLS-1$ getChannel("SftpFileService.createFolder").mkdir(fullPath); //$NON-NLS-1$
SftpATTRS attrs = getChannel("SftpFileService.createFolder.stat").stat(fullPath); //$NON-NLS-1$ SftpATTRS attrs = getChannel("SftpFileService.createFolder.stat").stat(fullPath); //$NON-NLS-1$
result = makeHostFile(remoteParent, folderName, attrs); result = makeHostFile(remoteParent, folderName, attrs);
@ -652,7 +778,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
Activator.trace("SftpFileService.delete.waitForLock"); //$NON-NLS-1$ Activator.trace("SftpFileService.delete.waitForLock"); //$NON-NLS-1$
if (fDirChannelMutex.waitForLock(monitor, fDirChannelTimeout)) { if (fDirChannelMutex.waitForLock(monitor, fDirChannelTimeout)) {
try { try {
String fullPath = remoteParent + '/' + fileName; String fullPath = recodeSafe(remoteParent + '/' + fileName);
SftpATTRS attrs = null; SftpATTRS attrs = null;
try { try {
attrs = getChannel("SftpFileService.delete").lstat(fullPath); //$NON-NLS-1$ attrs = getChannel("SftpFileService.delete").lstat(fullPath); //$NON-NLS-1$
@ -705,7 +831,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
try { try {
String fullPathOld = remoteParent + '/' + oldName; String fullPathOld = remoteParent + '/' + oldName;
String fullPathNew = remoteParent + '/' + newName; String fullPathNew = remoteParent + '/' + newName;
getChannel("SftpFileService.rename").rename(fullPathOld, fullPathNew); //$NON-NLS-1$ getChannel("SftpFileService.rename").rename(recode(fullPathOld), recodeSafe(fullPathNew)); //$NON-NLS-1$
ok=true; ok=true;
Activator.trace("SftpFileService.rename ok"); //$NON-NLS-1$ Activator.trace("SftpFileService.rename ok"); //$NON-NLS-1$
} catch (Exception e) { } catch (Exception e) {
@ -794,8 +920,8 @@ public class SftpFileService extends AbstractFileService implements IFileService
// TODO Interpret some error messages like "command not found" (use ren instead of mv on windows) // TODO Interpret some error messages like "command not found" (use ren instead of mv on windows)
// TODO mimic by copy if the remote does not support copying between file systems? // TODO mimic by copy if the remote does not support copying between file systems?
Activator.trace("SftpFileService.move "+srcName); //$NON-NLS-1$ Activator.trace("SftpFileService.move "+srcName); //$NON-NLS-1$
String fullPathOld = PathUtility.enQuoteUnix(srcParent + '/' + srcName); String fullPathOld = PathUtility.enQuoteUnix(recode(srcParent + '/' + srcName));
String fullPathNew = PathUtility.enQuoteUnix(tgtParent + '/' + tgtName); String fullPathNew = PathUtility.enQuoteUnix(recodeSafe(tgtParent + '/' + tgtName));
int rv = runCommand("mv "+fullPathOld+' '+fullPathNew, monitor); //$NON-NLS-1$ int rv = runCommand("mv "+fullPathOld+' '+fullPathNew, monitor); //$NON-NLS-1$
return (rv==0); return (rv==0);
} }
@ -805,8 +931,8 @@ public class SftpFileService extends AbstractFileService implements IFileService
// TODO check if newer versions of sftp support copy directly // TODO check if newer versions of sftp support copy directly
// TODO Interpret some error messages like "command not found" (use (x)copy instead of cp on windows) // TODO Interpret some error messages like "command not found" (use (x)copy instead of cp on windows)
Activator.trace("SftpFileService.copy "+srcName); //$NON-NLS-1$ Activator.trace("SftpFileService.copy "+srcName); //$NON-NLS-1$
String fullPathOld = PathUtility.enQuoteUnix(srcParent + '/' + srcName); String fullPathOld = PathUtility.enQuoteUnix(recode(srcParent + '/' + srcName));
String fullPathNew = PathUtility.enQuoteUnix(tgtParent + '/' + tgtName); String fullPathNew = PathUtility.enQuoteUnix(recodeSafe(tgtParent + '/' + tgtName));
int rv = runCommand("cp -Rp "+fullPathOld+' '+fullPathNew, monitor); //$NON-NLS-1$ int rv = runCommand("cp -Rp "+fullPathOld+' '+fullPathNew, monitor); //$NON-NLS-1$
return (rv==0); return (rv==0);
} }
@ -852,7 +978,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
if (fDirChannelMutex.waitForLock(monitor, fDirChannelTimeout)) { if (fDirChannelMutex.waitForLock(monitor, fDirChannelTimeout)) {
try { try {
String path = parent + '/' + name; String path = parent + '/' + name;
getChannel("SftpFileService.setLastModified").setMtime(path, (int)(timestamp/1000)); //$NON-NLS-1$ getChannel("SftpFileService.setLastModified").setMtime(recode(path), (int)(timestamp/1000)); //$NON-NLS-1$
ok=true; ok=true;
Activator.trace("SftpFileService.setLastModified ok"); //$NON-NLS-1$ Activator.trace("SftpFileService.setLastModified ok"); //$NON-NLS-1$
} catch (Exception e) { } catch (Exception e) {
@ -871,7 +997,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
if (fDirChannelMutex.waitForLock(monitor, fDirChannelTimeout)) { if (fDirChannelMutex.waitForLock(monitor, fDirChannelTimeout)) {
try { try {
String path = parent + '/' + name; String path = parent + '/' + name;
SftpATTRS attr = getChannel("SftpFileService.setReadOnly").stat(path); //$NON-NLS-1$ SftpATTRS attr = getChannel("SftpFileService.setReadOnly").stat(recode(path)); //$NON-NLS-1$
int permOld = attr.getPermissions(); int permOld = attr.getPermissions();
int permNew = permOld; int permNew = permOld;
if (readOnly) { if (readOnly) {
@ -882,7 +1008,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
if (permNew != permOld) { if (permNew != permOld) {
//getChannel("SftpFileService.setReadOnly").chmod(permNew, path); //$NON-NLS-1$ //getChannel("SftpFileService.setReadOnly").chmod(permNew, path); //$NON-NLS-1$
attr.setPERMISSIONS(permNew); attr.setPERMISSIONS(permNew);
getChannel("SftpFileService.setReadOnly").setStat(path, attr); //$NON-NLS-1$ getChannel("SftpFileService.setReadOnly").setStat(recode(path), attr); //$NON-NLS-1$
ok=true; ok=true;
Activator.trace("SftpFileService.setReadOnly ok"); //$NON-NLS-1$ Activator.trace("SftpFileService.setReadOnly ok"); //$NON-NLS-1$
} else { } else {
@ -909,7 +1035,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
InputStream stream = null; InputStream stream = null;
try { try {
String remotePath = remoteParent + '/' + remoteFile; String remotePath = recode(remoteParent + '/' + remoteFile);
getChannel("SftpFileService.getInputStream " + remoteFile); //check the session is healthy //$NON-NLS-1$ getChannel("SftpFileService.getInputStream " + remoteFile); //check the session is healthy //$NON-NLS-1$
ChannelSftp channel = (ChannelSftp)fSessionProvider.getSession().openChannel("sftp"); //$NON-NLS-1$ ChannelSftp channel = (ChannelSftp)fSessionProvider.getSession().openChannel("sftp"); //$NON-NLS-1$
channel.connect(); channel.connect();
@ -954,7 +1080,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
getChannel("SftpFileService.getOutputStream " + remoteFile); //check the session is healthy //$NON-NLS-1$ getChannel("SftpFileService.getOutputStream " + remoteFile); //check the session is healthy //$NON-NLS-1$
ChannelSftp channel = (ChannelSftp)fSessionProvider.getSession().openChannel("sftp"); //$NON-NLS-1$ ChannelSftp channel = (ChannelSftp)fSessionProvider.getSession().openChannel("sftp"); //$NON-NLS-1$
channel.connect(); channel.connect();
stream = new SftpBufferedOutputStream(channel.put(dst, sftpMonitor, mode), channel); stream = new SftpBufferedOutputStream(channel.put(recodeSafe(dst), sftpMonitor, mode), channel);
Activator.trace("SftpFileService.getOutputStream " + remoteFile + " ok"); //$NON-NLS-1$ //$NON-NLS-2$ Activator.trace("SftpFileService.getOutputStream " + remoteFile + " ok"); //$NON-NLS-1$ //$NON-NLS-2$
} }
catch (Exception e) { catch (Exception e) {

View file

@ -21,6 +21,7 @@
* David Dykstal (IBM) - added RESID_FTP_SETTINGS_LABEL * David Dykstal (IBM) - added RESID_FTP_SETTINGS_LABEL
* David McKnight (IBM) - [196632] [ftp] Passive mode setting does not work * David McKnight (IBM) - [196632] [ftp] Passive mode setting does not work
* Martin Oberhuber (Wind River) - [204669] Fix ftp path concatenation on systems using backslash separator * Martin Oberhuber (Wind River) - [204669] Fix ftp path concatenation on systems using backslash separator
* Martin Oberhuber (Wind River) - [203500] Support encodings for FTP paths
********************************************************************************/ ********************************************************************************/
package org.eclipse.rse.internal.subsystems.files.ftp.connectorservice; package org.eclipse.rse.internal.subsystems.files.ftp.connectorservice;
@ -52,6 +53,9 @@ public class FTPConnectorService extends StandardConnectorService
{ {
protected FTPService _ftpService; protected FTPService _ftpService;
/** Indicates the default string encoding on this platform */
private static String _defaultEncoding = new java.io.InputStreamReader(new java.io.ByteArrayInputStream(new byte[0])).getEncoding();
public FTPConnectorService(IHost host, int port) public FTPConnectorService(IHost host, int port)
{ {
super(FTPSubsystemResources.RESID_FTP_CONNECTORSERVICE_NAME,FTPSubsystemResources.RESID_FTP_CONNECTORSERVICE_DESCRIPTION, host, port); super(FTPSubsystemResources.RESID_FTP_CONNECTORSERVICE_NAME,FTPSubsystemResources.RESID_FTP_CONNECTORSERVICE_DESCRIPTION, host, port);
@ -105,11 +109,17 @@ public class FTPConnectorService extends StandardConnectorService
_ftpService.setLoggingStream(getLoggingStream(info.getHostname(),getPort())); _ftpService.setLoggingStream(getLoggingStream(info.getHostname(),getPort()));
_ftpService.setPropertySet(propertySet); _ftpService.setPropertySet(propertySet);
_ftpService.setFTPClientConfigFactory(FTPClientConfigFactory.getParserFactory()); _ftpService.setFTPClientConfigFactory(FTPClientConfigFactory.getParserFactory());
//TODO this code should be in IHost
String encoding = getHost().getDefaultEncoding(false);
if (encoding==null) encoding = getHost().getDefaultEncoding(true);
//TODO Here, we set the FTP default encoding same as the local encoding.
//Another alternative would be to set ISO-8859-1, which is the
//default-default internal to FTP, or keep it "null".
if (encoding==null) encoding = _defaultEncoding;
//</code to be in IHost>
_ftpService.setControlEncoding(encoding);
_ftpService.connect(); _ftpService.connect();
} }
/* (non-Javadoc) /* (non-Javadoc)