Browse Source

Add support for Deluge V2 RPC.

Fixes #502.
Unfortunatley there is no good way to detect the version, so added another choice.
pull/506/head
Adam Peck 5 years ago
parent
commit
da00a7c958
  1. 45
      app/src/main/java/org/transdroid/daemon/Daemon.java
  2. 16
      app/src/main/java/org/transdroid/daemon/Deluge/DelugeRpcAdapter.java
  3. 46
      app/src/main/java/org/transdroid/daemon/Deluge/DelugeRpcClient.java
  4. 2
      app/src/main/res/values/strings.xml

45
app/src/main/java/org/transdroid/daemon/Daemon.java

@ -69,7 +69,12 @@ public enum Daemon {
}, },
DelugeRpc { DelugeRpc {
public IDaemonAdapter createAdapter(DaemonSettings settings) { public IDaemonAdapter createAdapter(DaemonSettings settings) {
return new DelugeRpcAdapter(settings); return new DelugeRpcAdapter(settings, false);
}
},
Deluge2Rpc {
public IDaemonAdapter createAdapter(DaemonSettings settings) {
return new DelugeRpcAdapter(settings, true);
} }
}, },
Dummy { Dummy {
@ -157,6 +162,8 @@ public enum Daemon {
return "daemon_deluge"; return "daemon_deluge";
case DelugeRpc: case DelugeRpc:
return "daemon_deluge_rpc"; return "daemon_deluge_rpc";
case Deluge2Rpc:
return "daemon_deluge2_rpc";
case DLinkRouterBT: case DLinkRouterBT:
return "daemon_dlinkrouterbt"; return "daemon_dlinkrouterbt";
case Dummy: case Dummy:
@ -214,6 +221,9 @@ public enum Daemon {
if (daemonCode.equals("daemon_deluge_rpc")) { if (daemonCode.equals("daemon_deluge_rpc")) {
return DelugeRpc; return DelugeRpc;
} }
if (daemonCode.equals("daemon_deluge2_rpc")) {
return Deluge2Rpc;
}
if (daemonCode.equals("daemon_dlinkrouterbt")) { if (daemonCode.equals("daemon_dlinkrouterbt")) {
return DLinkRouterBT; return DLinkRouterBT;
} }
@ -275,6 +285,7 @@ public enum Daemon {
return 8112; return 8112;
case DelugeRpc: case DelugeRpc:
case Deluge2Rpc:
return DelugeRpcAdapter.DEFAULT_PORT; return DelugeRpcAdapter.DEFAULT_PORT;
case Synology: case Synology:
@ -308,13 +319,13 @@ public enum Daemon {
public static boolean supportsFileListing(Daemon type) { public static boolean supportsFileListing(Daemon type) {
return type == Synology || type == Transmission || type == uTorrent || type == BitTorrent || type == KTorrent || type == Deluge return type == Synology || type == Transmission || type == uTorrent || type == BitTorrent || type == KTorrent || type == Deluge
|| type == DelugeRpc || type == rTorrent || type == Vuze || type == DLinkRouterBT || type == Bitflu || type == qBittorrent || type == DelugeRpc || type == Deluge2Rpc || type == rTorrent || type == Vuze || type == DLinkRouterBT || type == Bitflu
|| type == BuffaloNas || type == BitComet || type == Aria2 || type == tTorrent || type == Dummy; || type == qBittorrent || type == BuffaloNas || type == BitComet || type == Aria2 || type == tTorrent || type == Dummy;
} }
public static boolean supportsFineDetails(Daemon type) { public static boolean supportsFineDetails(Daemon type) {
return type == uTorrent || type == BitTorrent || type == Daemon.Transmission || type == Deluge || type == DelugeRpc return type == uTorrent || type == BitTorrent || type == Daemon.Transmission || type == Deluge || type == DelugeRpc
|| type == rTorrent || type == qBittorrent || type == Aria2 || type == Dummy; || type == Deluge2Rpc || type == rTorrent || type == qBittorrent || type == Aria2 || type == Dummy;
} }
public static boolean needsManualPathSpecified(Daemon type) { public static boolean needsManualPathSpecified(Daemon type) {
@ -322,7 +333,7 @@ public enum Daemon {
} }
public static boolean supportsFilePaths(Daemon type) { public static boolean supportsFilePaths(Daemon type) {
return type == uTorrent || type == BitTorrent || type == Vuze || type == Deluge || type == DelugeRpc return type == uTorrent || type == BitTorrent || type == Vuze || type == Deluge || type == DelugeRpc || type == Deluge2Rpc
|| type == Transmission || type == rTorrent || type == KTorrent || type == BuffaloNas || type == Aria2 || type == Dummy; || type == Transmission || type == rTorrent || type == KTorrent || type == BuffaloNas || type == Aria2 || type == Dummy;
} }
@ -335,12 +346,12 @@ public enum Daemon {
} }
public static boolean supportsCustomFolder(Daemon type) { public static boolean supportsCustomFolder(Daemon type) {
return type == rTorrent || type == Tfb4rt || type == Bitflu || type == Deluge || type == DelugeRpc || type == Aria2 return type == rTorrent || type == Tfb4rt || type == Bitflu || type == Deluge || type == DelugeRpc || type == Deluge2Rpc || type == Aria2
|| type == Transmission || type == BitTorrent || type == uTorrent || type == qBittorrent || type == Dummy; || type == Transmission || type == BitTorrent || type == uTorrent || type == qBittorrent || type == Dummy;
} }
public static boolean supportsSetTransferRates(Daemon type) { public static boolean supportsSetTransferRates(Daemon type) {
return type == Deluge || type == DelugeRpc return type == Deluge || type == DelugeRpc || type == Deluge2Rpc
|| type == Transmission || type == uTorrent || type == BitTorrent || type == rTorrent || type == Vuze || type == BuffaloNas || type == Transmission || type == uTorrent || type == BitTorrent || type == rTorrent || type == Vuze || type == BuffaloNas
|| type == BitComet || type == Aria2 || type == qBittorrent || type == Dummy; || type == BitComet || type == Aria2 || type == qBittorrent || type == Dummy;
} }
@ -364,28 +375,28 @@ public enum Daemon {
public static boolean supportsFilePrioritySetting(Daemon type) { public static boolean supportsFilePrioritySetting(Daemon type) {
return type == BitTorrent || type == uTorrent || type == Transmission || type == KTorrent || type == rTorrent || type == Vuze return type == BitTorrent || type == uTorrent || type == Transmission || type == KTorrent || type == rTorrent || type == Vuze
|| type == Deluge || type == DelugeRpc || type == Deluge || type == DelugeRpc || type == Deluge2Rpc
|| type == qBittorrent || type == tTorrent || type == Dummy; || type == qBittorrent || type == tTorrent || type == Dummy;
} }
public static boolean supportsDateAdded(Daemon type) { public static boolean supportsDateAdded(Daemon type) {
return type == Vuze || type == Transmission || type == rTorrent || type == Bitflu || type == BitComet || type == uTorrent return type == Vuze || type == Transmission || type == rTorrent || type == Bitflu || type == BitComet || type == uTorrent
|| type == BitTorrent || type == Deluge || type == DelugeRpc || type == BitTorrent || type == Deluge || type == DelugeRpc || type == Deluge2Rpc
|| type == qBittorrent || type == Dummy; || type == qBittorrent || type == Dummy;
} }
public static boolean supportsLabels(Daemon type) { public static boolean supportsLabels(Daemon type) {
return type == uTorrent || type == BitTorrent || type == Deluge || type == DelugeRpc || type == BitComet || type == rTorrent return type == uTorrent || type == BitTorrent || type == Deluge || type == DelugeRpc || type == Deluge2Rpc || type == BitComet
|| type == qBittorrent || type == Dummy; || type == rTorrent || type == qBittorrent || type == Dummy;
} }
public static boolean supportsSetLabel(Daemon type) { public static boolean supportsSetLabel(Daemon type) {
return type == uTorrent || type == BitTorrent || type == rTorrent || type == Deluge || type == DelugeRpc return type == uTorrent || type == BitTorrent || type == rTorrent || type == Deluge || type == DelugeRpc || type == Deluge2Rpc
|| type == qBittorrent || type == Dummy; || type == qBittorrent || type == Dummy;
} }
public static boolean supportsSetDownloadLocation(Daemon type) { public static boolean supportsSetDownloadLocation(Daemon type) {
return type == Transmission || type == Deluge || type == DelugeRpc || type == qBittorrent || type == Dummy; return type == Transmission || type == Deluge || type == DelugeRpc || type == Deluge2Rpc|| type == qBittorrent || type == Dummy;
} }
public static boolean supportsSetAlternativeMode(Daemon type) { public static boolean supportsSetAlternativeMode(Daemon type) {
@ -393,12 +404,12 @@ public enum Daemon {
} }
public static boolean supportsSetTrackers(Daemon type) { public static boolean supportsSetTrackers(Daemon type) {
return type == uTorrent || type == BitTorrent || type == Deluge || type == DelugeRpc || type == Dummy; return type == uTorrent || type == BitTorrent || type == Deluge || type == DelugeRpc || type == Deluge2Rpc|| type == Dummy;
} }
public static boolean supportsForceRecheck(Daemon type) { public static boolean supportsForceRecheck(Daemon type) {
return type == uTorrent || type == BitTorrent || type == Deluge || type == DelugeRpc || type == rTorrent || type == Transmission return type == uTorrent || type == BitTorrent || type == Deluge || type == DelugeRpc || type == Deluge2Rpc || type == rTorrent
|| type == Dummy || type == qBittorrent; || type == Transmission || type == Dummy || type == qBittorrent;
} }
public static boolean supportsExtraPassword(Daemon type) { public static boolean supportsExtraPassword(Daemon type) {
@ -406,7 +417,7 @@ public enum Daemon {
} }
public static boolean supportsRemoteRssManagement(Daemon type) { public static boolean supportsRemoteRssManagement(Daemon type) {
return type == uTorrent || type == DelugeRpc; return type == uTorrent || type == DelugeRpc || type == Deluge2Rpc;
} }
} }

16
app/src/main/java/org/transdroid/daemon/Deluge/DelugeRpcAdapter.java

@ -49,16 +49,18 @@ public class DelugeRpcAdapter implements IDaemonAdapter, RemoteRssSupplier {
public static final int DEFAULT_PORT = 58846; public static final int DEFAULT_PORT = 58846;
private final DaemonSettings settings; private final DaemonSettings settings;
private final boolean isVersion2;
private int version = -1; private int version = -1;
public DelugeRpcAdapter(DaemonSettings settings) { public DelugeRpcAdapter(DaemonSettings settings, boolean isVersion2) {
this.settings = settings; this.settings = settings;
this.isVersion2 = isVersion2;
} }
@Override @Override
public DaemonTaskResult executeTask(Log log, DaemonTask task) { public DaemonTaskResult executeTask(Log log, DaemonTask task) {
final DelugeRpcClient client = new DelugeRpcClient(); final DelugeRpcClient client = new DelugeRpcClient(isVersion2);
try { try {
client.connect(settings); client.connect(settings);
switch (task.getMethod()) { switch (task.getMethod()) {
@ -109,7 +111,7 @@ public class DelugeRpcAdapter implements IDaemonAdapter, RemoteRssSupplier {
@Override @Override
public Daemon getType() { public Daemon getType() {
return Daemon.DelugeRpc; return isVersion2 ? Daemon.Deluge2Rpc : Daemon.DelugeRpc;
} }
@Override @Override
@ -120,7 +122,7 @@ public class DelugeRpcAdapter implements IDaemonAdapter, RemoteRssSupplier {
@Override @Override
public ArrayList<RemoteRssChannel> getRemoteRssChannels(Log log) throws DaemonException { public ArrayList<RemoteRssChannel> getRemoteRssChannels(Log log) throws DaemonException {
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
final DelugeRpcClient client = new DelugeRpcClient(); final DelugeRpcClient client = new DelugeRpcClient(isVersion2);
try { try {
client.connect(settings); client.connect(settings);
@ -197,7 +199,7 @@ public class DelugeRpcAdapter implements IDaemonAdapter, RemoteRssSupplier {
} else { } else {
label = null; label = null;
} }
final DelugeRpcClient client = new DelugeRpcClient(); final DelugeRpcClient client = new DelugeRpcClient(isVersion2);
try { try {
client.connect(settings); client.connect(settings);
@ -369,10 +371,10 @@ public class DelugeRpcAdapter implements IDaemonAdapter, RemoteRssSupplier {
final List<Torrent> torrents = new ArrayList<>(); final List<Torrent> torrents = new ArrayList<>();
int id = 0; int id = 0;
for (Map<String, Object> torrentMap : torrentMaps) { for (Map<String, Object> torrentMap : torrentMaps) {
final Object timeAdded = torrentMap.get(RPC_TIMEADDED); final Number timeAdded = (Number) torrentMap.get(RPC_TIMEADDED);
final Date timeAddedDate; final Date timeAddedDate;
if (timeAdded != null) { if (timeAdded != null) {
final long seconds = (long) (float) timeAdded; final long seconds = timeAdded.longValue();
timeAddedDate = new Date(seconds * 1000L); timeAddedDate = new Date(seconds * 1000L);
} else { } else {
timeAddedDate = null; timeAddedDate = null;

46
app/src/main/java/org/transdroid/daemon/Deluge/DelugeRpcClient.java

@ -27,8 +27,10 @@ import se.dimovski.rencode.Rencode;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.Socket; import java.net.Socket;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -36,6 +38,7 @@ import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream; import java.util.zip.InflaterInputStream;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_DAEMON_LOGIN; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_DAEMON_LOGIN;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_INFO;
/** /**
* A Deluge RPC API Client. * A Deluge RPC API Client.
@ -45,13 +48,23 @@ class DelugeRpcClient implements Closeable {
private static final int RESPONSE_TYPE_INDEX = 0; private static final int RESPONSE_TYPE_INDEX = 0;
private static final int RESPONSE_RETURN_VALUE_INDEX = 2; private static final int RESPONSE_RETURN_VALUE_INDEX = 2;
private static final int RPC_ERROR = 2; private static final int RPC_ERROR = 2;
private static final byte V2_PROTOCOL_VERSION = 1;
private static final int V2_HEADER_SIZE = 5;
private Socket socket; private Socket socket;
private final boolean isVersion2;
private static AtomicInteger requestId = new AtomicInteger(); private static AtomicInteger requestId = new AtomicInteger();
DelugeRpcClient(boolean isVersion2) {
this.isVersion2 = isVersion2;
}
void connect(DaemonSettings settings) throws DaemonException { void connect(DaemonSettings settings) throws DaemonException {
try { try {
socket = openSocket(settings); socket = openSocket(settings);
if (isVersion2) {
sendRequest(RPC_METHOD_INFO);
}
if (settings.shouldUseAuthentication()) { if (settings.shouldUseAuthentication()) {
sendRequest(RPC_METHOD_DAEMON_LOGIN, settings.getUsername(), settings.getPassword()); sendRequest(RPC_METHOD_DAEMON_LOGIN, settings.getUsername(), settings.getPassword());
} }
@ -75,12 +88,26 @@ class DelugeRpcClient implements Closeable {
Object sendRequest(String method, Object... args) throws DaemonException { Object sendRequest(String method, Object... args) throws DaemonException {
final byte[] requestBytes; final byte[] requestBytes;
try { try {
requestBytes = compress(Rencode.encode(new Object[]{new Object[]{requestId.getAndIncrement(), method, args, new HashMap<>()}})); HashMap<Object, Object> kwargs = new HashMap<>();
if (isVersion2 && RPC_METHOD_DAEMON_LOGIN.equals(method)) {
kwargs.put("client_version", "" + V2_PROTOCOL_VERSION);
}
requestBytes = compress(Rencode.encode(new Object[]{new Object[]{requestId.getAndIncrement(), method, args, kwargs}}));
} catch (IOException e) { } catch (IOException e) {
throw new DaemonException(ExceptionType.ConnectionError, "Failed to encode request: " + e.getMessage()); throw new DaemonException(ExceptionType.ConnectionError, "Failed to encode request: " + e.getMessage());
} }
try { try {
if (isVersion2) {
socket.getOutputStream().write(
ByteBuffer.allocate(V2_HEADER_SIZE + requestBytes.length)
.put(V2_PROTOCOL_VERSION)
.putInt(requestBytes.length)
.put(requestBytes)
.array()
);
} else {
socket.getOutputStream().write(requestBytes); socket.getOutputStream().write(requestBytes);
}
return readResponse(); return readResponse();
} catch (IOException e) { } catch (IOException e) {
throw new DaemonException(ExceptionType.ConnectionError, e.getMessage()); throw new DaemonException(ExceptionType.ConnectionError, e.getMessage());
@ -106,9 +133,22 @@ class DelugeRpcClient implements Closeable {
@NonNull @NonNull
private Object readResponse() throws DaemonException, IOException { private Object readResponse() throws DaemonException, IOException {
final InflaterInputStream inflater = new InflaterInputStream(socket.getInputStream()); final InputStream in = socket.getInputStream();
final InflaterInputStream inflater = new InflaterInputStream(in);
final ByteArrayOutputStream out = new ByteArrayOutputStream(); final ByteArrayOutputStream out = new ByteArrayOutputStream();
final byte[] buffer = new byte[1024];
final byte[] buffer;
if (isVersion2) {
final byte[] header = new byte[V2_HEADER_SIZE];
in.read(header, 0, V2_HEADER_SIZE);
if (header[0] != V2_PROTOCOL_VERSION) {
throw new DaemonException(ExceptionType.ConnectionError, "Unexpected protocol version: " + header[0]);
}
buffer = new byte[ByteBuffer.wrap(header).getInt(1)];
} else {
buffer = new byte[1024];
}
while (inflater.available() > 0) { while (inflater.available() > 0) {
final int n = inflater.read(buffer); final int n = inflater.read(buffer);
if (n > 0) { if (n > 0) {

2
app/src/main/res/values/strings.xml

@ -389,6 +389,7 @@
<item>Buffalo NAS -1.31</item> <item>Buffalo NAS -1.31</item>
<item>Deluge 1.2+</item> <item>Deluge 1.2+</item>
<item>Deluge RPC</item> <item>Deluge RPC</item>
<item>Deluge 2 RPC</item>
<item>DLink Router BT</item> <item>DLink Router BT</item>
<item>Ktorrent</item> <item>Ktorrent</item>
<item>qBittorrent</item> <item>qBittorrent</item>
@ -408,6 +409,7 @@
<item>daemon_buffalonas</item> <item>daemon_buffalonas</item>
<item>daemon_deluge</item> <item>daemon_deluge</item>
<item>daemon_deluge_rpc</item> <item>daemon_deluge_rpc</item>
<item>daemon_deluge2_rpc</item>
<item>daemon_dlinkrouterbt</item> <item>daemon_dlinkrouterbt</item>
<item>daemon_ktorrent</item> <item>daemon_ktorrent</item>
<item>daemon_qbittorrent</item> <item>daemon_qbittorrent</item>

Loading…
Cancel
Save