Skip to content

Commit a03fa9a

Browse files
ISQ-GTThierynomus
authored andcommitted
Added charset support, centralized UTF-8 usage (#305)
* Added charset support, centralized UTF-8 usage * Code style, buffer methods with charsets * assure remote charset isn't null
1 parent bcb15e6 commit a03fa9a

File tree

13 files changed

+120
-36
lines changed

13 files changed

+120
-36
lines changed

src/main/java/com/hierynomus/sshj/backport/Jdk7HttpProxySocket.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
import java.io.IOException;
1919
import java.io.InputStream;
2020
import java.net.*;
21-
import java.nio.charset.Charset;
21+
22+
import net.schmizz.sshj.common.IOUtils;
2223

2324
public class Jdk7HttpProxySocket extends Socket {
2425

@@ -48,7 +49,7 @@ private void connectHttpProxy(SocketAddress endpoint, int timeout) throws IOExce
4849
}
4950
InetSocketAddress isa = (InetSocketAddress) endpoint;
5051
String httpConnect = "CONNECT " + isa.getHostName() + ":" + isa.getPort() + " HTTP/1.0\n\n";
51-
getOutputStream().write(httpConnect.getBytes(Charset.forName("UTF-8")));
52+
getOutputStream().write(httpConnect.getBytes(IOUtils.UTF8));
5253
checkAndFlushProxyResponse();
5354
}
5455

@@ -61,7 +62,7 @@ private void checkAndFlushProxyResponse()throws IOException {
6162
throw new SocketException("Empty response from proxy");
6263
}
6364

64-
String proxyResponse = new String(tmpBuffer, 0, len, "UTF-8");
65+
String proxyResponse = new String(tmpBuffer, 0, len, IOUtils.UTF8);
6566

6667
// Expecting HTTP/1.x 200 OK
6768
if (proxyResponse.contains("200")) {

src/main/java/net/schmizz/sshj/SSHClient.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package net.schmizz.sshj;
1717

1818
import net.schmizz.sshj.common.Factory;
19+
import net.schmizz.sshj.common.IOUtils;
1920
import net.schmizz.sshj.common.LoggerFactory;
2021
import net.schmizz.sshj.common.SSHException;
2122
import net.schmizz.sshj.common.SecurityUtils;
@@ -61,6 +62,7 @@
6162
import java.io.File;
6263
import java.io.IOException;
6364
import java.net.ServerSocket;
65+
import java.nio.charset.Charset;
6466
import java.security.KeyPair;
6567
import java.security.PublicKey;
6668
import java.util.*;
@@ -128,6 +130,9 @@ public class SSHClient
128130

129131
private final List<LocalPortForwarder> forwarders = new ArrayList<LocalPortForwarder>();
130132

133+
/** character set of the remote machine */
134+
protected Charset remoteCharset = IOUtils.UTF8;
135+
131136
/** Default constructor. Initializes this object using {@link DefaultConfig}. */
132137
public SSHClient() {
133138
this(new DefaultConfig());
@@ -440,6 +445,15 @@ public Connection getConnection() {
440445
return conn;
441446
}
442447

448+
/**
449+
* Returns the character set used to communicate with the remote machine for certain strings (like paths).
450+
*
451+
* @return remote character set
452+
*/
453+
public Charset getRemoteCharset() {
454+
return remoteCharset;
455+
}
456+
443457
/** @return a {@link RemotePortForwarder} that allows requesting remote forwarding over this connection. */
444458
public RemotePortForwarder getRemotePortForwarder() {
445459
synchronized (conn) {
@@ -708,12 +722,22 @@ public void rekey()
708722
doKex();
709723
}
710724

725+
/**
726+
* Sets the character set used to communicate with the remote machine for certain strings (like paths)
727+
*
728+
* @param remoteCharset
729+
* remote character set or {@code null} for default
730+
*/
731+
public void setRemoteCharset(Charset remoteCharset) {
732+
this.remoteCharset = remoteCharset != null ? remoteCharset : IOUtils.UTF8;
733+
}
734+
711735
@Override
712736
public Session startSession()
713737
throws ConnectionException, TransportException {
714738
checkConnected();
715739
checkAuthenticated();
716-
final SessionChannel sess = new SessionChannel(conn);
740+
final SessionChannel sess = new SessionChannel(conn, remoteCharset);
717741
sess.open();
718742
return sess;
719743
}

src/main/java/net/schmizz/sshj/common/Buffer.java

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
*/
1616
package net.schmizz.sshj.common;
1717

18-
import java.io.UnsupportedEncodingException;
1918
import java.math.BigInteger;
19+
import java.nio.charset.Charset;
2020
import java.security.GeneralSecurityException;
2121
import java.security.PublicKey;
2222
import java.util.Arrays;
@@ -361,24 +361,31 @@ public T putUInt64(long uint64) {
361361
/**
362362
* Reads an SSH string
363363
*
364+
* @param cs the charset to use for decoding
365+
*
364366
* @return the string as a Java {@code String}
365367
*/
366-
public String readString()
368+
public String readString(Charset cs)
367369
throws BufferException {
368370
int len = readUInt32AsInt();
369371
if (len < 0 || len > 32768)
370372
throw new BufferException("Bad item length: " + len);
371373
ensureAvailable(len);
372-
String s;
373-
try {
374-
s = new String(data, rpos, len, "UTF-8");
375-
} catch (UnsupportedEncodingException e) {
376-
throw new SSHRuntimeException(e);
377-
}
374+
String s = new String(data, rpos, len, cs);
378375
rpos += len;
379376
return s;
380377
}
381378

379+
/**
380+
* Reads an SSH string using {@code UTF8}
381+
*
382+
* @return the string as a Java {@code String}
383+
*/
384+
public String readString()
385+
throws BufferException {
386+
return readString(IOUtils.UTF8);
387+
}
388+
382389
/**
383390
* Reads an SSH string
384391
*
@@ -397,8 +404,12 @@ public T putString(byte[] str, int offset, int len) {
397404
return putBytes(str, offset, len);
398405
}
399406

407+
public T putString(String string, Charset cs) {
408+
return putString(string.getBytes(cs));
409+
}
410+
400411
public T putString(String string) {
401-
return putString(string.getBytes(IOUtils.UTF8));
412+
return putString(string, IOUtils.UTF8);
402413
}
403414

404415
/**

src/main/java/net/schmizz/sshj/connection/channel/AbstractChannel.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import java.io.InputStream;
2828
import java.io.OutputStream;
29+
import java.nio.charset.Charset;
2930
import java.util.LinkedList;
3031
import java.util.Queue;
3132
import java.util.concurrent.TimeUnit;
@@ -51,6 +52,8 @@ public abstract class AbstractChannel
5152
private final int id;
5253
/** Remote recipient ID */
5354
private int recipient;
55+
/** Remote character set */
56+
private final Charset remoteCharset;
5457

5558
private boolean eof = false;
5659

@@ -78,12 +81,16 @@ public abstract class AbstractChannel
7881
private volatile boolean autoExpand = false;
7982

8083
protected AbstractChannel(Connection conn, String type) {
84+
this(conn, type, null);
85+
}
86+
protected AbstractChannel(Connection conn, String type, Charset remoteCharset) {
8187
this.conn = conn;
8288
this.loggerFactory = conn.getTransport().getConfig().getLoggerFactory();
8389
this.type = type;
8490
this.log = loggerFactory.getLogger(getClass());
8591
this.trans = conn.getTransport();
8692

93+
this.remoteCharset = remoteCharset != null ? remoteCharset : IOUtils.UTF8;
8794
id = conn.nextID();
8895

8996
lwin = new Window.Local(conn.getWindowSize(), conn.getMaxPacketSize(), loggerFactory);
@@ -135,6 +142,11 @@ public int getRecipient() {
135142
return recipient;
136143
}
137144

145+
@Override
146+
public Charset getRemoteCharset() {
147+
return remoteCharset;
148+
}
149+
138150
@Override
139151
public int getRemoteMaxPacketSize() {
140152
return rwin.getMaxPacketSize();

src/main/java/net/schmizz/sshj/connection/channel/Channel.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.io.Closeable;
2525
import java.io.InputStream;
2626
import java.io.OutputStream;
27+
import java.nio.charset.Charset;
2728
import java.util.concurrent.TimeUnit;
2829

2930
/**
@@ -123,6 +124,9 @@ interface Forwarded extends Channel {
123124
*/
124125
int getRecipient();
125126

127+
/** @return the character set used to communicate with the remote machine for certain strings (like paths). */
128+
Charset getRemoteCharset();
129+
126130
/**
127131
* @return the maximum packet size as specified by the remote end.
128132
*/

src/main/java/net/schmizz/sshj/connection/channel/direct/AbstractDirectChannel.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import net.schmizz.sshj.connection.channel.OpenFailException;
2626
import net.schmizz.sshj.transport.TransportException;
2727

28+
import java.nio.charset.Charset;
2829
import java.util.concurrent.TimeUnit;
2930

3031
/** Base class for direct channels whose open is initated by the client. */
@@ -41,6 +42,15 @@ protected AbstractDirectChannel(Connection conn, String type) {
4142
conn.attach(this);
4243
}
4344

45+
protected AbstractDirectChannel(Connection conn, String type, Charset remoteCharset) {
46+
super(conn, type, remoteCharset);
47+
48+
/*
49+
* We expect to receive channel open confirmation/rejection and want to be able to next this packet.
50+
*/
51+
conn.attach(this);
52+
}
53+
4454
@Override
4555
public void open()
4656
throws ConnectionException, TransportException {

src/main/java/net/schmizz/sshj/connection/channel/direct/SessionChannel.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import net.schmizz.sshj.transport.TransportException;
2323

2424
import java.io.InputStream;
25+
import java.nio.charset.Charset;
2526
import java.util.Collections;
2627
import java.util.Map;
2728
import java.util.concurrent.TimeUnit;
@@ -47,6 +48,10 @@ public SessionChannel(Connection conn) {
4748
super(conn, "session");
4849
}
4950

51+
public SessionChannel(Connection conn, Charset remoteCharset) {
52+
super(conn, "session", remoteCharset);
53+
}
54+
5055
@Override
5156
public void allocateDefaultPTY()
5257
throws ConnectionException, TransportException {
@@ -93,7 +98,7 @@ public Command exec(String command)
9398
throws ConnectionException, TransportException {
9499
checkReuse();
95100
log.debug("Will request to exec `{}`", command);
96-
sendChannelRequest("exec", true, new Buffer.PlainBuffer().putString(command))
101+
sendChannelRequest("exec", true, new Buffer.PlainBuffer().putString(command, getRemoteCharset()))
97102
.await(conn.getTimeoutMs(), TimeUnit.MILLISECONDS);
98103
usedUp = true;
99104
return this;

src/main/java/net/schmizz/sshj/sftp/RemoteDirectory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public List<RemoteResourceInfo> scan(RemoteResourceFilter filter)
4242
case NAME:
4343
final int count = res.readUInt32AsInt();
4444
for (int i = 0; i < count; i++) {
45-
final String name = res.readString();
45+
final String name = res.readString(requester.sub.getRemoteCharset());
4646
res.readString(); // long name - IGNORED - shdve never been in the protocol
4747
final FileAttributes attrs = res.readFileAttributes();
4848
final PathComponents comps = requester.getPathHelper().getComponents(path, name);

0 commit comments

Comments
 (0)