1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-24 00:33:48 +02:00

[224799] Fix JSch encoding problems with arabic UTF-8 file names

This commit is contained in:
Martin Oberhuber 2008-04-02 21:00:03 +00:00
parent 94f38246c5
commit 69c14379e8
2 changed files with 441 additions and 419 deletions

View file

@ -78,8 +78,8 @@ public class SshConnectorService extends StandardConnectorService implements ISs
private static final String PROPERTY_KEY_TIMEOUT = "timeout(sec)"; //$NON-NLS-1$ private static final String PROPERTY_KEY_TIMEOUT = "timeout(sec)"; //$NON-NLS-1$
private static final String PROPERTY_KEY_KEEPALIVE = "keepalive(sec)"; //$NON-NLS-1$ private static final String PROPERTY_KEY_KEEPALIVE = "keepalive(sec)"; //$NON-NLS-1$
private Session session; private Session session;
private SessionLostHandler fSessionLostHandler; private SessionLostHandler fSessionLostHandler;
/** Indicates the default string encoding on this platform */ /** Indicates the default string encoding on this platform */
private static String _defaultEncoding = new java.io.InputStreamReader(new java.io.ByteArrayInputStream(new byte[0])).getEncoding(); private static String _defaultEncoding = new java.io.InputStreamReader(new java.io.ByteArrayInputStream(new byte[0])).getEncoding();
@ -100,46 +100,46 @@ public class SshConnectorService extends StandardConnectorService implements ISs
* the Jsch config (for instance, in order to switch off strict * the Jsch config (for instance, in order to switch off strict
* host key checking or in order to add specific ciphers). * host key checking or in order to add specific ciphers).
*/ */
protected Session createSession(String username, String password, String hostname, int port, UserInfo wrapperUI, IProgressMonitor monitor) throws JSchException { protected Session createSession(String username, String password, String hostname, int port, UserInfo wrapperUI, IProgressMonitor monitor) throws JSchException {
IJSchService service = Activator.getDefault().getJSchService(); IJSchService service = Activator.getDefault().getJSchService();
if (service == null) if (service == null)
return null; return null;
Session session = service.createSession(hostname, port, username); Session session = service.createSession(hostname, port, username);
IPropertySet propertySet = getPropertySet(); IPropertySet propertySet = getPropertySet();
String timeoutStr = propertySet.getPropertyValue(PROPERTY_KEY_TIMEOUT); String timeoutStr = propertySet.getPropertyValue(PROPERTY_KEY_TIMEOUT);
int timeout = 0; //default is never timeout int timeout = 0; //default is never timeout
try { try {
int value = Integer.parseInt(timeoutStr); int value = Integer.parseInt(timeoutStr);
if (value > 0) { if (value > 0) {
timeout = value * 1000; timeout = value * 1000;
} }
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
//wrong input - should never happen because property type is Integer //wrong input - should never happen because property type is Integer
} }
session.setTimeout(timeout); session.setTimeout(timeout);
int keepalive = 300000; //default is 5 minutes int keepalive = 300000; //default is 5 minutes
String keepaliveStr = propertySet.getPropertyValue(PROPERTY_KEY_KEEPALIVE); String keepaliveStr = propertySet.getPropertyValue(PROPERTY_KEY_KEEPALIVE);
try { try {
int value = Integer.parseInt(keepaliveStr); int value = Integer.parseInt(keepaliveStr);
if (value >= 0) { if (value >= 0) {
keepalive = value * 1000; keepalive = value * 1000;
} }
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
//wrong input - should never happen because property type is Integer //wrong input - should never happen because property type is Integer
} }
if (keepalive > 0) { if (keepalive > 0) {
session.setServerAliveInterval(keepalive); session.setServerAliveInterval(keepalive);
} }
session.setServerAliveCountMax(6); //give up after 6 tries (remote will be dead after 30 min) session.setServerAliveCountMax(6); //give up after 6 tries (remote will be dead after 30 min)
if (password != null) if (password != null)
session.setPassword(password); session.setPassword(password);
session.setUserInfo(wrapperUI); session.setUserInfo(wrapperUI);
return session; return session;
} }
static void shutdown() { static void shutdown() {
//TODO: Store all Jsch sessions in a pool and disconnect them on shutdown //TODO: Store all Jsch sessions in a pool and disconnect them on shutdown
@ -159,45 +159,45 @@ public class SshConnectorService extends StandardConnectorService implements ISs
} }
protected void internalConnect(IProgressMonitor monitor) throws Exception protected void internalConnect(IProgressMonitor monitor) throws Exception
{ {
// Fire comm event to signal state about to change // Fire comm event to signal state about to change
fireCommunicationsEvent(CommunicationsEvent.BEFORE_CONNECT); fireCommunicationsEvent(CommunicationsEvent.BEFORE_CONNECT);
String host = getHostName(); String host = getHostName();
String user = getUserId(); String user = getUserId();
String password=""; //$NON-NLS-1$ String password=""; //$NON-NLS-1$
SystemSignonInformation ssi = getSignonInformation(); SystemSignonInformation ssi = getSignonInformation();
if (ssi!=null) { if (ssi!=null) {
password = getSignonInformation().getPassword(); password = getSignonInformation().getPassword();
} }
MyUserInfo userInfo = new MyUserInfo(user, password); MyUserInfo userInfo = new MyUserInfo(user, password);
userInfo.aboutToConnect(); userInfo.aboutToConnect();
try { try {
session = createSession(user, password, host, getSshPort(), session = createSession(user, password, host, getSshPort(),
userInfo, monitor); userInfo, monitor);
//java.util.Hashtable config=new java.util.Hashtable(); //java.util.Hashtable config=new java.util.Hashtable();
//config.put("StrictHostKeyChecking", "no"); //config.put("StrictHostKeyChecking", "no");
//session.setConfig(config); //session.setConfig(config);
userInfo.aboutToConnect(); userInfo.aboutToConnect();
Activator.trace("SshConnectorService.connecting..."); //$NON-NLS-1$ Activator.trace("SshConnectorService.connecting..."); //$NON-NLS-1$
//wait for 60 sec maximum during connect //wait for 60 sec maximum during connect
session.connect(CONNECT_DEFAULT_TIMEOUT * 1000); session.connect(CONNECT_DEFAULT_TIMEOUT * 1000);
Activator.trace("SshConnectorService.connected"); //$NON-NLS-1$ Activator.trace("SshConnectorService.connected"); //$NON-NLS-1$
} catch (JSchException e) { } catch (JSchException e) {
Activator.trace("SshConnectorService.connect failed: "+e.toString()); //$NON-NLS-1$ Activator.trace("SshConnectorService.connect failed: "+e.toString()); //$NON-NLS-1$
sessionDisconnect(); sessionDisconnect();
if(e.toString().indexOf("Auth cancel")>=0) { //$NON-NLS-1$ if(e.toString().indexOf("Auth cancel")>=0) { //$NON-NLS-1$
throw new OperationCanceledException(); throw new OperationCanceledException();
} }
throw e; throw e;
} }
userInfo.connectionMade(); userInfo.connectionMade();
fSessionLostHandler = new SessionLostHandler(this); fSessionLostHandler = new SessionLostHandler(this);
notifyConnection(); notifyConnection();
} }
/** /**
* Disconnect the ssh session. * Disconnect the ssh session.
@ -205,21 +205,21 @@ public class SshConnectorService extends StandardConnectorService implements ISs
* quickly in succession. * quickly in succession.
*/ */
private synchronized void sessionDisconnect() { private synchronized void sessionDisconnect() {
Activator.trace("SshConnectorService.sessionDisconnect"); //$NON-NLS-1$ Activator.trace("SshConnectorService.sessionDisconnect"); //$NON-NLS-1$
try { try {
if (session.isConnected()) if (session.isConnected())
session.disconnect(); session.disconnect();
} catch(Exception e) { } catch(Exception e) {
//Bug 175328: NPE on disconnect shown in UI //Bug 175328: NPE on disconnect shown in UI
//This is a non-critical exception so print only in debug mode //This is a non-critical exception so print only in debug mode
if (Activator.isTracingOn()) e.printStackTrace(); if (Activator.isTracingOn()) e.printStackTrace();
} }
} }
protected void internalDisconnect(IProgressMonitor monitor) throws Exception protected void internalDisconnect(IProgressMonitor monitor) throws Exception
{ {
//TODO Will services like the sftp service be disconnected too? Or notified? //TODO Will services like the sftp service be disconnected too? Or notified?
Activator.trace("SshConnectorService.disconnect"); //$NON-NLS-1$ Activator.trace("SshConnectorService.disconnect"); //$NON-NLS-1$
try try
{ {
if (session != null) { if (session != null) {
@ -253,30 +253,29 @@ public class SshConnectorService extends StandardConnectorService implements ISs
} }
//TODO avoid having jsch type "Session" in the API. //TODO avoid having jsch type "Session" in the API.
//Could be done by instanciating SshShellService and SshFileService here, // Could be done by instantiating SshShellService and SshFileService here,
//and implementing IShellService getShellService() //and implementing IShellService getShellService()
//and IFileService getFileService(). //and IFileService getFileService().
public Session getSession() { public Session getSession() {
return session; return session;
} }
public String getControlEncoding() { public String getControlEncoding() {
//TODO this code should be in IHost //TODO this code should be in IHost
String encoding = getHost().getDefaultEncoding(false); String encoding = getHost().getDefaultEncoding(true);
if (encoding==null) encoding = getHost().getDefaultEncoding(true);
if (encoding==null) encoding = _defaultEncoding; if (encoding==null) encoding = _defaultEncoding;
//</code to be in IHost> //</code to be in IHost>
return encoding; 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.
* Most of this is extracted from dstore's ConnectionStatusListener. * Most of this is extracted from dstore's ConnectionStatusListener.
* *
* TODO should be refactored to make it generally available, and allow * TODO should be refactored to make it generally available, and allow
* dstore to derive from it. * dstore to derive from it.
*/ */
public static class SessionLostHandler implements Runnable, IRunnableWithProgress public static class SessionLostHandler implements Runnable, IRunnableWithProgress
{ {
private IConnectorService _connection; private IConnectorService _connection;
@ -334,23 +333,23 @@ public class SshConnectorService extends StandardConnectorService implements ISs
//But what about error messages? //But what about error messages?
IRunnableContext runnableContext = getRunnableContext(getShell()); IRunnableContext runnableContext = getRunnableContext(getShell());
// will do this.run(IProgressMonitor mon) // will do this.run(IProgressMonitor mon)
//runnableContext.run(false,true,this); // inthread, cancellable, IRunnableWithProgress //runnableContext.run(false,true,this); // inthread, cancellable, IRunnableWithProgress
runnableContext.run(true,true,this); // fork, cancellable, IRunnableWithProgress runnableContext.run(true,true,this); // fork, cancellable, IRunnableWithProgress
_connection.reset(); _connection.reset();
ISystemRegistry sr = RSECorePlugin.getTheSystemRegistry(); ISystemRegistry sr = RSECorePlugin.getTheSystemRegistry();
sr.connectedStatusChange(_connection.getPrimarySubSystem(), false, true, true); sr.connectedStatusChange(_connection.getPrimarySubSystem(), false, true, true);
}
catch (InterruptedException exc) // user cancelled
{
if (shell != null)
showDisconnectCancelledMessage(shell, _connection.getHostName(), _connection.getPort());
}
catch (java.lang.reflect.InvocationTargetException invokeExc) // unexpected error
{
Exception exc = (Exception)invokeExc.getTargetException();
if (shell != null)
showDisconnectErrorMessage(shell, _connection.getHostName(), _connection.getPort(), exc);
} }
catch (InterruptedException exc) // user cancelled
{
if (shell != null)
showDisconnectCancelledMessage(shell, _connection.getHostName(), _connection.getPort());
}
catch (java.lang.reflect.InvocationTargetException invokeExc) // unexpected error
{
Exception exc = (Exception)invokeExc.getTargetException();
if (shell != null)
showDisconnectErrorMessage(shell, _connection.getHostName(), _connection.getPort(), exc);
}
catch (Exception e) catch (Exception e)
{ {
SystemBasePlugin.logError(SshConnectorResources.SshConnectorService_ErrorDisconnecting, e); SystemBasePlugin.logError(SshConnectorResources.SshConnectorService_ErrorDisconnecting, e);
@ -358,7 +357,7 @@ public class SshConnectorService extends StandardConnectorService implements ISs
} }
public void run(IProgressMonitor monitor) public void run(IProgressMonitor monitor)
throws InvocationTargetException, InterruptedException throws InvocationTargetException, InterruptedException
{ {
String message = null; String message = null;
message = SubSystemConfiguration.getDisconnectingMessage( message = SubSystemConfiguration.getDisconnectingMessage(
@ -391,7 +390,7 @@ public class SshConnectorService extends StandardConnectorService implements ISs
} }
if (window == null) { if (window == null) {
IWorkbenchWindow[] windows = PlatformUI.getWorkbench() IWorkbenchWindow[] windows = PlatformUI.getWorkbench()
.getWorkbenchWindows(); .getWorkbenchWindows();
if (windows != null && windows.length > 0) { if (windows != null && windows.length > 0) {
return windows[0].getShell(); return windows[0].getShell();
} }
@ -402,7 +401,7 @@ public class SshConnectorService extends StandardConnectorService implements ISs
return null; return null;
} }
/** /**
* Get the progress monitor dialog for this operation. We try to use one * Get the progress monitor dialog for this operation. We try to use one
* for all phases of a single operation, such as connecting and * for all phases of a single operation, such as connecting and
* resolving. * resolving.
@ -413,11 +412,11 @@ public class SshConnectorService extends StandardConnectorService implements ISs
IWorkbenchWindow win = SystemBasePlugin.getActiveWorkbenchWindow(); IWorkbenchWindow win = SystemBasePlugin.getActiveWorkbenchWindow();
if (win != null) { if (win != null) {
Shell winShell = RSEUIPlugin.getDefault().getWorkbench() Shell winShell = RSEUIPlugin.getDefault().getWorkbench()
.getActiveWorkbenchWindow().getShell(); .getActiveWorkbenchWindow().getShell();
if (winShell != null && !winShell.isDisposed() if (winShell != null && !winShell.isDisposed()
&& winShell.isVisible()) { && winShell.isVisible()) {
SystemBasePlugin SystemBasePlugin
.logInfo("Using active workbench window as runnable context"); //$NON-NLS-1$ .logInfo("Using active workbench window as runnable context"); //$NON-NLS-1$
shell = winShell; shell = winShell;
return win; return win;
} else { } else {
@ -426,69 +425,69 @@ public class SshConnectorService extends StandardConnectorService implements ISs
} }
if (shell == null || shell.isDisposed() || !shell.isVisible()) { if (shell == null || shell.isDisposed() || !shell.isVisible()) {
SystemBasePlugin SystemBasePlugin
.logInfo("Using progress monitor dialog with given shell as parent"); //$NON-NLS-1$ .logInfo("Using progress monitor dialog with given shell as parent"); //$NON-NLS-1$
shell = rshell; shell = rshell;
} }
IRunnableContext dlg = new ProgressMonitorDialog(rshell); IRunnableContext dlg = new ProgressMonitorDialog(rshell);
return dlg; return dlg;
} }
/** /**
* Show an error message when the disconnection fails. Shows a common * Show an error message when the disconnection fails. Shows a common
* message by default. Overridable. * message by default. Overridable.
*/ */
protected void showDisconnectErrorMessage(Shell shell, String hostName, int port, Exception exc) protected void showDisconnectErrorMessage(Shell shell, String hostName, int port, Exception exc)
{ {
//SystemMessage.displayMessage(SystemMessage.MSGTYPE_ERROR,shell,RSEUIPlugin.getResourceBundle(), //SystemMessage.displayMessage(SystemMessage.MSGTYPE_ERROR,shell,RSEUIPlugin.getResourceBundle(),
// ISystemMessages.MSG_DISCONNECT_FAILED, // ISystemMessages.MSG_DISCONNECT_FAILED,
// hostName, exc.getMessage()); // hostName, exc.getMessage());
//RSEUIPlugin.logError("Disconnect failed",exc); // temporary //RSEUIPlugin.logError("Disconnect failed",exc); // temporary
SystemMessage msg = new SimpleSystemMessage(Activator.PLUGIN_ID, ICommonMessageIds.MSG_DISCONNECT_FAILED, IStatus.ERROR, NLS.bind(CommonMessages.MSG_DISCONNECT_FAILED, hostName), exc); SystemMessage msg = new SimpleSystemMessage(Activator.PLUGIN_ID, ICommonMessageIds.MSG_DISCONNECT_FAILED, IStatus.ERROR, NLS.bind(CommonMessages.MSG_DISCONNECT_FAILED, hostName), exc);
SystemMessageDialog msgDlg = new SystemMessageDialog(shell, msg); SystemMessageDialog msgDlg = new SystemMessageDialog(shell, msg);
msgDlg.setException(exc); msgDlg.setException(exc);
msgDlg.open(); msgDlg.open();
} }
/** /**
* Show an error message when the user cancels the disconnection. * Show an error message when the user cancels the disconnection.
* Shows a common message by default. * Shows a common message by default.
* Overridable. * Overridable.
*/ */
protected void showDisconnectCancelledMessage(Shell shell, String hostName, int port) protected void showDisconnectCancelledMessage(Shell shell, String hostName, int port)
{ {
//SystemMessage.displayMessage(SystemMessage.MSGTYPE_ERROR, shell, RSEUIPlugin.getResourceBundle(), //SystemMessage.displayMessage(SystemMessage.MSGTYPE_ERROR, shell, RSEUIPlugin.getResourceBundle(),
// ISystemMessages.MSG_DISCONNECT_CANCELED, hostName) // ISystemMessages.MSG_DISCONNECT_CANCELED, hostName)
SystemMessage msg = new SimpleSystemMessage(Activator.PLUGIN_ID, ICommonMessageIds.MSG_DISCONNECT_CANCELED, IStatus.CANCEL, NLS.bind(CommonMessages.MSG_DISCONNECT_CANCELED, hostName)); SystemMessage msg = new SimpleSystemMessage(Activator.PLUGIN_ID, ICommonMessageIds.MSG_DISCONNECT_CANCELED, IStatus.CANCEL, NLS.bind(CommonMessages.MSG_DISCONNECT_CANCELED, hostName));
SystemMessageDialog msgDlg = new SystemMessageDialog(shell,msg); SystemMessageDialog msgDlg = new SystemMessageDialog(shell,msg);
msgDlg.open(); msgDlg.open();
} }
} }
/** /**
* Notification from sub-services that our session was lost. * Notification from sub-services that our session was lost.
* Notify all subsystems properly. * Notify all subsystems properly.
* TODO allow user to try and reconnect? * TODO allow user to try and reconnect?
*/ */
public void handleSessionLost() { public void handleSessionLost() {
Activator.trace("SshConnectorService: handleSessionLost"); //$NON-NLS-1$ Activator.trace("SshConnectorService: handleSessionLost"); //$NON-NLS-1$
if (fSessionLostHandler!=null) { if (fSessionLostHandler!=null) {
fSessionLostHandler.sessionLost(); fSessionLostHandler.sessionLost();
} }
} }
protected static Display getStandardDisplay() { protected static Display getStandardDisplay() {
Display display = Display.getCurrent(); Display display = Display.getCurrent();
if( display==null ) { if( display==null ) {
display = Display.getDefault(); display = Display.getDefault();
} }
return display; return display;
} }
private static class MyUserInfo implements UserInfo, UIKeyboardInteractive { private static class MyUserInfo implements UserInfo, UIKeyboardInteractive {
private String fPassphrase; private String fPassphrase;
private String fPassword; private String fPassword;
private int fAttemptCount; private int fAttemptCount;
private final String fUser; private final String fUser;
public MyUserInfo(String user, String password) { public MyUserInfo(String user, String password) {
fUser = user; fUser = user;
@ -550,50 +549,50 @@ public class SshConnectorService extends StandardConnectorService implements ISs
final String name, final String instruction, final String name, final String instruction,
final String[] prompt, final boolean[] echo) final String[] prompt, final boolean[] echo)
{ {
if (prompt.length == 0) { if (prompt.length == 0) {
// No need to prompt, just return an empty String array // No need to prompt, just return an empty String array
return new String[0]; return new String[0];
} }
try{ try{
if (fAttemptCount == 0 && fPassword != null && prompt.length == 1 && prompt[0].trim().equalsIgnoreCase("password:")) { //$NON-NLS-1$ if (fAttemptCount == 0 && fPassword != null && prompt.length == 1 && prompt[0].trim().equalsIgnoreCase("password:")) { //$NON-NLS-1$
// Return the provided password the first time but always prompt on subsequent tries // Return the provided password the first time but always prompt on subsequent tries
fAttemptCount++; fAttemptCount++;
return new String[] { fPassword }; return new String[] { fPassword };
} }
final String[][] finResult = new String[1][]; final String[][] finResult = new String[1][];
getStandardDisplay().syncExec(new Runnable() { getStandardDisplay().syncExec(new Runnable() {
public void run() { public void run() {
KeyboardInteractiveDialog dialog = new KeyboardInteractiveDialog(null, KeyboardInteractiveDialog dialog = new KeyboardInteractiveDialog(null,
null, destination, name, instruction, prompt, echo); null, destination, name, instruction, prompt, echo);
dialog.open(); dialog.open();
finResult[0]=dialog.getResult(); finResult[0]=dialog.getResult();
} }
}); });
String[] result=finResult[0]; String[] result=finResult[0];
if (result == null) if (result == null)
return null; // canceled return null; // canceled
if (result.length == 1 && prompt.length == 1 && prompt[0].trim().equalsIgnoreCase("password:")) { //$NON-NLS-1$ if (result.length == 1 && prompt.length == 1 && prompt[0].trim().equalsIgnoreCase("password:")) { //$NON-NLS-1$
fPassword = result[0]; fPassword = result[0];
} }
fAttemptCount++; fAttemptCount++;
return result; return result;
} }
catch(OperationCanceledException e){ catch(OperationCanceledException e){
return null; return null;
} }
} }
/** /**
* Callback to indicate that a connection is about to be attempted * Callback to indicate that a connection is about to be attempted
*/ */
public void aboutToConnect() { public void aboutToConnect() {
fAttemptCount = 0; fAttemptCount = 0;
} }
/** /**
* Callback to indicate that a connection was made * Callback to indicate that a connection was made
*/ */
public void connectionMade() { public void connectionMade() {
fAttemptCount = 0; fAttemptCount = 0;
} }
} }

View file

@ -23,6 +23,7 @@
* David McKnight (IBM) - [209593] [api] add support for "file permissions" and "owner" properties for unix files * David McKnight (IBM) - [209593] [api] add support for "file permissions" and "owner" properties for unix files
* Martin Oberhuber (Wind River) - [216343] immediate link targets and canonical paths for Sftp * Martin Oberhuber (Wind River) - [216343] immediate link targets and canonical paths for Sftp
* David McKnight (IBM) - [216252] [api][nls] Resource Strings specific to subsystems should be moved from rse.ui into files.ui / shells.ui / processes.ui where possible * David McKnight (IBM) - [216252] [api][nls] Resource Strings specific to subsystems should be moved from rse.ui into files.ui / shells.ui / processes.ui where possible
* Martin Oberhuber (Wind River) - [224799] Fix JSch encoding problems with Arabic filenames
*******************************************************************************/ *******************************************************************************/
package org.eclipse.rse.internal.services.ssh.files; package org.eclipse.rse.internal.services.ssh.files;
@ -161,16 +162,34 @@ public class SftpFileService extends AbstractFileService implements IFileService
private String fControlEncoding = null; private String fControlEncoding = null;
/** Indicates the default string encoding on this platform */ /** Indicates the default string encoding on this platform */
private static String defaultEncoding = new java.io.InputStreamReader(new java.io.ByteArrayInputStream(new byte[0])).getEncoding(); private static String defaultEncoding = new java.io.InputStreamReader(new java.io.ByteArrayInputStream(new byte[0])).getEncoding();
/** Indicates the encoding that our JSch channel uses */
private String fJSchChannelEncoding = defaultEncoding;
// public SftpFileService(SshConnectorService conn) { // public SftpFileService(SshConnectorService conn) {
// fConnector = conn; // fConnector = conn;
// } // }
public SftpFileService(ISshSessionProvider sessionProvider) { public SftpFileService(ISshSessionProvider sessionProvider) {
fSessionProvider = sessionProvider; fSessionProvider = sessionProvider;
} }
public void setControlEncoding(String encoding) { public void setControlEncoding(String encoding) throws SystemMessageException {
try {
fChannelSftp.setFilenameEncoding(encoding);
fJSchChannelEncoding = encoding;
} catch (NoSuchMethodError e) {
// Fallback for JSch < 0.1.34: use recode() for encoding conversion
fControlEncoding = encoding;
fJSchChannelEncoding = defaultEncoding;
} catch (SftpException e) {
try {
fChannelSftp.setFilenameEncoding("UTF-8"); //$NON-NLS-1$
fJSchChannelEncoding = "UTF-8"; //$NON-NLS-1$
} catch (SftpException enest) {
// should not happen, are we not connected?
throw makeSystemMessageException(enest);
}
}
fControlEncoding = encoding; fControlEncoding = encoding;
} }
@ -183,12 +202,14 @@ public class SftpFileService extends AbstractFileService implements IFileService
protected String recode(String s) throws SystemMessageException { protected String recode(String s) throws SystemMessageException {
if (fControlEncoding==null) { if (fControlEncoding==null) {
return s; return s;
} else if (fControlEncoding.equals(defaultEncoding)) { } else if (fControlEncoding.equals(fJSchChannelEncoding)) {
return s; return s;
} }
try { try {
byte[] bytes = s.getBytes(fControlEncoding); //what we want on the wire 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 return new String(bytes, fJSchChannelEncoding); // what we need to
// tell Jsch to get
// this on the wire
} catch(UnsupportedEncodingException e) { } catch(UnsupportedEncodingException e) {
throw makeSystemMessageException(e); throw makeSystemMessageException(e);
} }
@ -206,8 +227,8 @@ public class SftpFileService extends AbstractFileService implements IFileService
protected String recodeSafe(String s) throws SystemMessageException { protected String recodeSafe(String s) throws SystemMessageException {
try { try {
String recoded = recode(s); String recoded = recode(s);
byte[] bytes = recoded.getBytes(defaultEncoding); byte[] bytes = recoded.getBytes(fJSchChannelEncoding);
String decoded = decode(new String(bytes)); String decoded = decode(new String(bytes, fJSchChannelEncoding));
if (!s.equals(decoded)) { if (!s.equals(decoded)) {
int i=0; int i=0;
int lmax = Math.min(s.length(), decoded.length()); int lmax = Math.min(s.length(), decoded.length());
@ -217,10 +238,10 @@ public class SftpFileService extends AbstractFileService implements IFileService
//String sbad=s.substring(Math.max(i-2,0), Math.min(i+2,lmax)); //String sbad=s.substring(Math.max(i-2,0), Math.min(i+2,lmax));
char sbad = s.charAt(i); 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$ 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)) { if (fControlEncoding == null || fControlEncoding.equals(fJSchChannelEncoding)) {
msg += "default encoding \""+defaultEncoding+"\". "; //$NON-NLS-1$ //$NON-NLS-2$ msg += "default encoding \"" + fJSchChannelEncoding + "\". "; //$NON-NLS-1$ //$NON-NLS-2$
} else { } else {
msg += "encoding \""+fControlEncoding+"\" over local default encoding \""+defaultEncoding+"\". "; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ msg += "encoding \"" + fControlEncoding + "\" over local default encoding \"" + fJSchChannelEncoding + "\". "; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} }
msg += "Please specify a different encoding in host properties."; //$NON-NLS-1$ msg += "Please specify a different encoding in host properties."; //$NON-NLS-1$
throw new UnsupportedEncodingException(msg); throw new UnsupportedEncodingException(msg);
@ -230,10 +251,10 @@ public class SftpFileService extends AbstractFileService implements IFileService
return quoteForJsch(recoded); return quoteForJsch(recoded);
} catch(UnsupportedEncodingException e) { } catch(UnsupportedEncodingException e) {
//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(),"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$ 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 RemoteFileIOException(new SystemMessageException(msg));
throw new SystemMessageException(msg); throw new SystemMessageException(msg);
} }
} }
@ -247,11 +268,13 @@ public class SftpFileService extends AbstractFileService implements IFileService
protected String decode(String s) throws SystemMessageException { protected String decode(String s) throws SystemMessageException {
if (fControlEncoding==null) { if (fControlEncoding==null) {
return s; return s;
} else if (fControlEncoding.equals(defaultEncoding)) { } else if (fControlEncoding.equals(fJSchChannelEncoding)) {
return s; return s;
} }
try { try {
byte[] bytes = s.getBytes(); //original bytes sent by SSH byte[] bytes = s.getBytes(fJSchChannelEncoding); // original
// bytes sent by
// SSH
return new String(bytes, fControlEncoding); return new String(bytes, fControlEncoding);
} catch(UnsupportedEncodingException e) { } catch(UnsupportedEncodingException e) {
throw makeSystemMessageException(e); throw makeSystemMessageException(e);
@ -272,7 +295,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
StringBuffer buf = new StringBuffer(s.length()+8); StringBuffer buf = new StringBuffer(s.length()+8);
for(int i=0; i<s.length(); i++) { for(int i=0; i<s.length(); i++) {
char c = s.charAt(i); char c = s.charAt(i);
// if(c=='?' || c=='*' || c=='\\') { // if(c=='?' || c=='*' || c=='\\') {
if(c=='?' || c=='*') { if(c=='?' || c=='*') {
buf.append('\\'); buf.append('\\');
} }
@ -295,10 +318,10 @@ public class SftpFileService extends AbstractFileService implements IFileService
Activator.trace("SftpFileService.connecting..."); //$NON-NLS-1$ Activator.trace("SftpFileService.connecting..."); //$NON-NLS-1$
try { try {
Session session = fSessionProvider.getSession(); Session session = fSessionProvider.getSession();
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;
setControlEncoding(fSessionProvider.getControlEncoding()); setControlEncoding(fSessionProvider.getControlEncoding());
fUserHome = decode(fChannelSftp.pwd()); fUserHome = decode(fChannelSftp.pwd());
Activator.trace("SftpFileService.connected"); //$NON-NLS-1$ Activator.trace("SftpFileService.connected"); //$NON-NLS-1$
} catch(Exception e) { } catch(Exception e) {
@ -466,33 +489,33 @@ 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)) {
boolean haveSubMonitor = false; boolean haveSubMonitor = false;
try { try {
Vector vv=getChannel("SftpFileService.internalFetch: "+parentPath).ls(recodeSafe(parentPath)); //$NON-NLS-1$ Vector vv=getChannel("SftpFileService.internalFetch: "+parentPath).ls(recodeSafe(parentPath)); //$NON-NLS-1$
progressTick(monitor, 40); progressTick(monitor, 40);
if (vv.size()>1 && monitor!=null) { if (vv.size()>1 && monitor!=null) {
monitor = new SubProgressMonitor(monitor, 40); monitor = new SubProgressMonitor(monitor, 40);
monitor.beginTask(null, vv.size()); monitor.beginTask(null, vv.size());
} }
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 = decode(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;
} }
if (filematcher.matches(fileName) || (lsEntry.getAttrs().isDir() && fileType!=IFileService.FILE_TYPE_FOLDERS)) { if (filematcher.matches(fileName) || (lsEntry.getAttrs().isDir() && fileType!=IFileService.FILE_TYPE_FOLDERS)) {
//get ALL directory names (unless looking for folders only) //get ALL directory names (unless looking for folders only)
SftpHostFile node = makeHostFile(parentPath, fileName, lsEntry.getAttrs()); SftpHostFile node = makeHostFile(parentPath, fileName, lsEntry.getAttrs());
progressTick(monitor, 1); progressTick(monitor, 1);
if (isRightType(fileType, node)) { if (isRightType(fileType, node)) {
results.add(node); results.add(node);
} }
} }
} }
} }
Activator.trace("SftpFileService.internalFetch <--"); //$NON-NLS-1$ Activator.trace("SftpFileService.internalFetch <--"); //$NON-NLS-1$
} catch(Exception e) { } catch(Exception e) {
//TODO throw new SystemMessageException. //TODO throw new SystemMessageException.
@ -541,7 +564,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
} }
if (linkTarget!=null && !linkTarget.equals(concat(parentPath, fileName))) { if (linkTarget!=null && !linkTarget.equals(concat(parentPath, fileName))) {
if (readlinkDone) { if (readlinkDone) {
//linkTarget may be a relative path name that needs to be resolved for stat() to work properly //linkTarget may be a relative path name that needs to be resolved for stat() to work properly
String curdir=decode(getChannel("makeHostFile.pwd").pwd()); //$NON-NLS-1$ String curdir=decode(getChannel("makeHostFile.pwd").pwd()); //$NON-NLS-1$
if (!parentPath.equals(curdir)) { if (!parentPath.equals(curdir)) {
getChannel("makeHostFile.chdir").cd(recode(parentPath)); //$NON-NLS-1$ getChannel("makeHostFile.chdir").cd(recode(parentPath)); //$NON-NLS-1$
@ -627,7 +650,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
dst = recodeSafe(dst); 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();
channel.put(localFile.getAbsolutePath(), dst, sftpMonitor, mode); channel.put(localFile.getAbsolutePath(), dst, sftpMonitor, mode);
Activator.trace("SftpFileService.upload "+remoteFile+ " ok"); //$NON-NLS-1$ //$NON-NLS-2$ Activator.trace("SftpFileService.upload "+remoteFile+ " ok"); //$NON-NLS-1$ //$NON-NLS-2$
if (monitor.isCanceled()) { if (monitor.isCanceled()) {
@ -659,56 +682,56 @@ public class SftpFileService extends AbstractFileService implements IFileService
return true; return true;
} }
public static class MyProgressMonitor implements SftpProgressMonitor public static class MyProgressMonitor implements SftpProgressMonitor
{ {
private IProgressMonitor fMonitor; private IProgressMonitor fMonitor;
private double fWorkPercentFactor; private double fWorkPercentFactor;
private Long fMaxWorkKB; private Long fMaxWorkKB;
private long fWorkToDate; private long fWorkToDate;
public MyProgressMonitor(IProgressMonitor monitor) { public MyProgressMonitor(IProgressMonitor monitor) {
fMonitor = monitor; fMonitor = monitor;
} }
public void init(int op, String src, String dest, long max){ public void init(int op, String src, String dest, long max){
fWorkPercentFactor = 1.0 / max; fWorkPercentFactor = 1.0 / max;
fMaxWorkKB = new Long(max / 1024L); fMaxWorkKB = new Long(max / 1024L);
fWorkToDate = 0; fWorkToDate = 0;
String srcFile = new Path(src).lastSegment(); String srcFile = new Path(src).lastSegment();
//String desc = ((op==SftpProgressMonitor.PUT)? //String desc = ((op==SftpProgressMonitor.PUT)?
// "Uploading " : "Downloading ")+srcFile; // "Uploading " : "Downloading ")+srcFile;
String desc = srcFile; String desc = srcFile;
if (Activator.isTracingOn()) { if (Activator.isTracingOn()) {
Activator.trace("Sftp-monitor: "+max+", "+desc); //$NON-NLS-1$ //$NON-NLS-2$ Activator.trace("Sftp-monitor: "+max+", "+desc); //$NON-NLS-1$ //$NON-NLS-2$
} }
//TODO avoid cast from long to int //TODO avoid cast from long to int
fMonitor.beginTask(desc, (int)max); fMonitor.beginTask(desc, (int)max);
} }
public boolean count(long count){ public boolean count(long count){
fWorkToDate += count; fWorkToDate += count;
Long workToDateKB = new Long(fWorkToDate / 1024L); Long workToDateKB = new Long(fWorkToDate / 1024L);
Double workPercent = new Double(fWorkPercentFactor * fWorkToDate); Double workPercent = new Double(fWorkPercentFactor * fWorkToDate);
String subDesc = MessageFormat.format( String subDesc = MessageFormat.format(
SshServiceResources.SftpFileService_Msg_Progress, SshServiceResources.SftpFileService_Msg_Progress,
new Object[] { new Object[] {
workToDateKB, fMaxWorkKB, workPercent workToDateKB, fMaxWorkKB, workPercent
}); });
if (Activator.isTracingOn()) { if (Activator.isTracingOn()) {
System.out.print('#'); System.out.print('#');
} }
fMonitor.subTask(subDesc); fMonitor.subTask(subDesc);
fMonitor.worked((int)count); fMonitor.worked((int)count);
return !(fMonitor.isCanceled()); return !(fMonitor.isCanceled());
} }
public void end(){ public void end(){
if (Activator.isTracingOn()) { if (Activator.isTracingOn()) {
System.out.println(); System.out.println();
System.out.println("Sftp-monitor <--"); //$NON-NLS-1$ System.out.println("Sftp-monitor <--"); //$NON-NLS-1$
System.out.flush(); System.out.flush();
} }
fMonitor.done(); fMonitor.done();
} }
} }
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
@ -721,14 +744,14 @@ public class SftpFileService extends AbstractFileService implements IFileService
FileOutputStream os = new FileOutputStream(tempFile); FileOutputStream os = new FileOutputStream(tempFile);
BufferedOutputStream bos = new BufferedOutputStream(os); BufferedOutputStream bos = new BufferedOutputStream(os);
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
int readCount; int readCount;
while( (readCount = bis.read(buffer)) > 0) while( (readCount = bis.read(buffer)) > 0)
{ {
bos.write(buffer, 0, readCount); bos.write(buffer, 0, readCount);
} }
bos.close(); bos.close();
upload(tempFile, remoteParent, remoteFile, isBinary, "", hostEncoding, monitor); //$NON-NLS-1$ upload(tempFile, remoteParent, remoteFile, isBinary, "", hostEncoding, monitor); //$NON-NLS-1$
} }
catch (Exception e) { catch (Exception e) {
throw makeSystemMessageException(e); throw makeSystemMessageException(e);
@ -755,7 +778,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
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$
channel=(ChannelSftp)fSessionProvider.getSession().openChannel("sftp"); //$NON-NLS-1$ channel=(ChannelSftp)fSessionProvider.getSession().openChannel("sftp"); //$NON-NLS-1$
channel.connect(); channel.connect();
channel.get(remotePathRecoded, localFile.getAbsolutePath(), sftpMonitor, mode); channel.get(remotePathRecoded, localFile.getAbsolutePath(), sftpMonitor, mode);
Activator.trace("SftpFileService.download "+remoteFile+ " ok"); //$NON-NLS-1$ //$NON-NLS-2$ Activator.trace("SftpFileService.download "+remoteFile+ " ok"); //$NON-NLS-1$ //$NON-NLS-2$
if (monitor.isCanceled()) { if (monitor.isCanceled()) {
@ -1066,7 +1089,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
public boolean setLastModified(String parent, String name, public boolean setLastModified(String parent, String name,
long timestamp, IProgressMonitor monitor) throws SystemMessageException long timestamp, IProgressMonitor monitor) throws SystemMessageException
{ {
boolean ok=false; boolean ok=false;
String path = concat(parent, name); String path = concat(parent, name);
if (fDirChannelMutex.waitForLock(monitor, fDirChannelTimeout)) { if (fDirChannelMutex.waitForLock(monitor, fDirChannelTimeout)) {
@ -1082,7 +1105,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
} }
} }
return ok; return ok;
} }
public boolean setReadOnly(String parent, String name, public boolean setReadOnly(String parent, String name,
boolean readOnly, IProgressMonitor monitor) throws SystemMessageException { boolean readOnly, IProgressMonitor monitor) throws SystemMessageException {
@ -1131,7 +1154,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
String remotePathRecoded = recode(remotePath); String remotePathRecoded = recode(remotePath);
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();
stream = new SftpBufferedInputStream(channel.get(remotePathRecoded), channel); stream = new SftpBufferedInputStream(channel.get(remotePathRecoded), channel);
Activator.trace("SftpFileService.getInputStream " + remoteFile + " ok"); //$NON-NLS-1$ //$NON-NLS-2$ Activator.trace("SftpFileService.getInputStream " + remoteFile + " ok"); //$NON-NLS-1$ //$NON-NLS-2$
} }
@ -1179,7 +1202,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(recodeSafe(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$
} }
@ -1211,7 +1234,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
*/ */
public void setFilePermissions(IHostFile file, public void setFilePermissions(IHostFile file,
IHostFilePermissions permissions, IProgressMonitor monitor) IHostFilePermissions permissions, IProgressMonitor monitor)
throws SystemMessageException { throws SystemMessageException {
return; return;
} }