Browse Source

Changed aXMLRPC library (for use with rTorrent) to use the Apache HttpClient to support digest authentification (and in general just reuse the library-wide method of creating a connection). Fixes #69.

pull/82/head
Eric Kok 11 years ago
parent
commit
8cd4275bca
  1. 55
      lib/src/de/timroes/axmlrpc/AuthenticationManager.java
  2. 95
      lib/src/de/timroes/axmlrpc/CookieManager.java
  3. 451
      lib/src/de/timroes/axmlrpc/XMLRPCClient.java
  4. 17
      lib/src/org/transdroid/daemon/Rtorrent/RtorrentAdapter.java
  5. 2
      lib/src/org/transdroid/daemon/util/HttpHelper.java

55
lib/src/de/timroes/axmlrpc/AuthenticationManager.java

@ -1,55 +0,0 @@
package de.timroes.axmlrpc;
import de.timroes.base64.Base64;
import java.net.HttpURLConnection;
/**
* The AuthenticationManager handle basic HTTP authentication.
*
* @author Tim Roes
*/
public class AuthenticationManager {
private String user;
private String pass;
/**
* Clear the username and password. No basic HTTP authentication will be used
* in the next calls.
*/
public void clearAuthData() {
this.user = null;
this.pass = null;
}
/**
* Set the username and password that should be used to perform basic
* http authentication.
*
* @param user Username
* @param pass Password
*/
public void setAuthData(String user, String pass) {
this.user = user;
this.pass = pass;
}
/**
* Set the authentication at the HttpURLConnection.
*
* @param http The HttpURLConnection to set authentication.
*/
public void setAuthentication(HttpURLConnection http) {
if(user == null || pass == null
|| user.length() <= 0 || pass.length() <= 0) {
return;
}
String base64login = Base64.encode(user + ":" + pass);
http.addRequestProperty("Authorization", "Basic " + base64login);
}
}

95
lib/src/de/timroes/axmlrpc/CookieManager.java

@ -1,95 +0,0 @@
package de.timroes.axmlrpc;
import java.net.HttpURLConnection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* The CookieManager handles cookies for the http requests.
* If the FLAGS_ENABLE_COOKIES has been set, it will save cookies
* and send it with every request.
*
* @author Tim Roes
*/
class CookieManager {
private static final String SET_COOKIE = "Set-Cookie";
private static final String COOKIE = "Cookie";
private int flags;
private Map<String,String> cookies = new ConcurrentHashMap<String, String>();
/**
* Create a new CookieManager with the given flags.
*
* @param flags A combination of flags to be set.
*/
public CookieManager(int flags) {
this.flags = flags;
}
/**
* Delete all cookies.
*/
public void clearCookies() {
cookies.clear();
}
/**
* Returns a {@link Map} of all cookies.
*
* @return All cookies
*/
public Map<String,String> getCookies() {
return cookies;
}
/**
* Read the cookies from an http response. It will look at every Set-Cookie
* header and put the cookie to the map of cookies.
*
* @param http A http connection.
*/
public void readCookies(HttpURLConnection http) {
// Only save cookies if FLAGS_ENABLE_COOKIES has been set.
if((flags & XMLRPCClient.FLAGS_ENABLE_COOKIES) == 0)
return;
String cookie, key;
String[] split;
// Extract every Set-Cookie field and put the cookie to the cookies map.
for(int i = 0; i < http.getHeaderFields().size(); i++) {
key = http.getHeaderFieldKey(i);
if(key != null && SET_COOKIE.toLowerCase().equals(key.toLowerCase())) {
cookie = http.getHeaderField(i).split(";")[0];
split = cookie.split("=");
if(split.length >= 2)
cookies.put(split[0], split[1]);
}
}
}
/**
* Write the cookies to a http connection. It will set the Cookie field
* to all currently set cookies in the map.
*
* @param http A http connection.
*/
public void setCookies(HttpURLConnection http) {
// Only save cookies if FLAGS_ENABLE_COOKIES has been set.
if((flags & XMLRPCClient.FLAGS_ENABLE_COOKIES) == 0)
return;
String concat = "";
for(Map.Entry<String,String> cookie : cookies.entrySet()) {
concat += cookie.getKey() + "=" + cookie.getValue() + "; ";
}
http.setRequestProperty(COOKIE, concat);
}
}

451
lib/src/de/timroes/axmlrpc/XMLRPCClient.java

@ -1,17 +1,19 @@
package de.timroes.axmlrpc; package de.timroes.axmlrpc;
import de.timroes.axmlrpc.serializer.SerializerHandler;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStreamWriter; import java.net.HttpURLConnection;
import java.net.*; import java.net.SocketTimeoutException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import javax.net.ssl.*;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HTTP;
import de.timroes.axmlrpc.serializer.SerializerHandler;
/** /**
* An XMLRPCClient is a client used to make XML-RPC (Extensible Markup Language * An XMLRPCClient is a client used to make XML-RPC (Extensible Markup Language
@ -26,12 +28,9 @@ import javax.net.ssl.*;
*/ */
public class XMLRPCClient { public class XMLRPCClient {
private static final String DEFAULT_USER_AGENT = "aXMLRPC";
/** /**
* Constants from the http protocol. * Constants from the http protocol.
*/ */
static final String USER_AGENT = "User-Agent";
static final String CONTENT_TYPE = "Content-Type"; static final String CONTENT_TYPE = "Content-Type";
static final String TYPE_XML = "text/xml; charset=utf-8"; static final String TYPE_XML = "text/xml; charset=utf-8";
static final String HOST = "Host"; static final String HOST = "Host";
@ -72,13 +71,6 @@ public class XMLRPCClient {
*/ */
public static final int FLAGS_8BYTE_INT = 0x02; public static final int FLAGS_8BYTE_INT = 0x02;
/**
* With this flag, the client will be able to handle cookies, meaning saving cookies
* from the server and sending it with every other request again. This is needed
* for some XML-RPC interfaces that support login.
*/
public static final int FLAGS_ENABLE_COOKIES = 0x04;
/** /**
* The client will be able to send null values. A null value will be send * The client will be able to send null values. A null value will be send
* as <nil/>. This extension is described under: http://ontosys.com/xml-rpc/extensions.php * as <nil/>. This extension is described under: http://ontosys.com/xml-rpc/extensions.php
@ -100,23 +92,6 @@ public class XMLRPCClient {
*/ */
public static final int FLAGS_FORWARD = 0x20; public static final int FLAGS_FORWARD = 0x20;
/**
* With this flag enabled, the client will ignore, if the URL doesn't match
* the SSL Certificate. This should be used with caution. Normally the URL
* should always match the URL in the SSL certificate, even with self signed
* certificates.
*/
public static final int FLAGS_SSL_IGNORE_INVALID_HOST = 0x40;
/**
* With this flag enabled, the client will ignore all unverified SSL/TLS
* certificates. This must be used, if you use self-signed certificates
* or certificated from unknown (or untrusted) authorities. If this flag is
* used, calls to {@link #installCustomTrustManager(javax.net.ssl.TrustManager)}
* won't have any effect.
*/
public static final int FLAGS_SSL_IGNORE_INVALID_CERT = 0x80;
/** /**
* With this flag enabled, a value with a missing type tag, will be parsed * With this flag enabled, a value with a missing type tag, will be parsed
* as a string element. This is just for incoming messages. Outgoing messages * as a string element. This is just for incoming messages. Outgoing messages
@ -151,14 +126,6 @@ public class XMLRPCClient {
*/ */
public static final int FLAGS_NO_STRING_ENCODE = 0x1000; public static final int FLAGS_NO_STRING_ENCODE = 0x1000;
/**
* This flag disables all SSL warnings. It is an alternative to use
* FLAGS_SSL_IGNORE_INVALID_CERT | FLAGS_SSL_IGNORE_INVALID_HOST. There
* is no functional difference.
*/
public static final int FLAGS_SSL_IGNORE_ERRORS =
FLAGS_SSL_IGNORE_INVALID_CERT | FLAGS_SSL_IGNORE_INVALID_HOST;
/** /**
* This flag should be used if the server is an apache ws xmlrpc server. * This flag should be used if the server is an apache ws xmlrpc server.
* This will set some flags, so that the not standard conform behavior * This will set some flags, so that the not standard conform behavior
@ -171,272 +138,45 @@ public class XMLRPCClient {
private final int flags; private final int flags;
private URL url; private DefaultHttpClient httpclient;
private Map<String,String> httpParameters = new ConcurrentHashMap<String, String>();
private String url;
private Map<Long,Caller> backgroundCalls = new ConcurrentHashMap<Long, Caller>(); private Map<Long,Caller> backgroundCalls = new ConcurrentHashMap<Long, Caller>();
private ResponseParser responseParser; private ResponseParser responseParser;
private CookieManager cookieManager;
private AuthenticationManager authManager;
private TrustManager[] trustManagers;
private KeyManager[] keyManagers;
private Proxy proxy;
private int timeout;
/** /**
* Create a new XMLRPC client for the given URL. * Create a new XMLRPC client for the given URL.
* *
* @param httpclient The already-initialized Apache HttpClient to use for connection.
* @param url The URL to send the requests to. * @param url The URL to send the requests to.
* @param userAgent A user agent string to use in the HTTP requests.
* @param flags A combination of flags to be set. * @param flags A combination of flags to be set.
*/ */
public XMLRPCClient(URL url, String userAgent, int flags) { public XMLRPCClient(DefaultHttpClient httpclient, String url, int flags) {
SerializerHandler.initialize(flags); SerializerHandler.initialize(flags);
this.httpclient = httpclient;
this.url = url; this.url = url;
this.flags = flags; this.flags = flags;
// Create a parser for the http responses. // Create a parser for the http responses.
responseParser = new ResponseParser(); responseParser = new ResponseParser();
cookieManager = new CookieManager(flags);
authManager = new AuthenticationManager();
httpParameters.put(CONTENT_TYPE, TYPE_XML);
httpParameters.put(USER_AGENT, userAgent);
// If invalid ssl certs are ignored, instantiate an all trusting TrustManager
if(isFlagSet(FLAGS_SSL_IGNORE_INVALID_CERT)) {
trustManagers = new TrustManager[] {
new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] xcs, String string)
throws CertificateException { }
public void checkServerTrusted(X509Certificate[] xcs, String string)
throws CertificateException { }
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
}
if(isFlagSet(FLAGS_USE_SYSTEM_PROXY)) {
// Read system proxy settings and generate a proxy from that
Properties prop = System.getProperties();
String proxyHost = prop.getProperty("http.proxyHost");
int proxyPort = Integer.parseInt(prop.getProperty("http.proxyPort", "0"));
if(proxyPort > 0 && proxyHost.length() > 0 && !proxyHost.equals("null")) {
proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
}
}
}
/**
* Create a new XMLRPC client for the given URL.
* The default user agent string will be used.
*
* @param url The URL to send the requests to.
* @param flags A combination of flags to be set.
*/
public XMLRPCClient(URL url, int flags) {
this(url, DEFAULT_USER_AGENT, flags);
}
/**
* Create a new XMLRPC client for the given url.
* No flags will be set.
*
* @param url The url to send the requests to.
* @param userAgent A user agent string to use in the http request.
*/
public XMLRPCClient(URL url, String userAgent) {
this(url, userAgent, FLAGS_NONE);
} }
/** /**
* Create a new XMLRPC client for the given url. * Create a new XMLRPC client for the given url.
* No flags will be used. * No flags will be used.
* The default user agent string will be used.
* *
* @param httpclient The already-initialized Apache HttpClient to use for connection.
* @param url The url to send the requests to. * @param url The url to send the requests to.
*/ */
public XMLRPCClient(URL url) { public XMLRPCClient(DefaultHttpClient httpclient, String url) {
this(url, DEFAULT_USER_AGENT, FLAGS_NONE); this(httpclient, url, FLAGS_NONE);
}
/**
* Returns the URL this XMLRPCClient is connected to. If that URL permanently forwards
* to another URL, this method will return the forwarded URL, as soon as
* the first call has been made.
*
* @return Returns the URL for this XMLRPCClient.
*/
public URL getURL() {
return url;
}
/**
* Sets the time in seconds after which a call should timeout.
* If {@code timeout} will be zero or less the connection will never timeout.
* In case the connection times out and {@link XMLRPCTimeoutException} will
* be thrown for calls made by {@link #call(java.lang.String, java.lang.Object[])}.
* For calls made by {@link #callAsync(de.timroes.axmlrpc.XMLRPCCallback, java.lang.String, java.lang.Object[])}
* the {@link XMLRPCCallback#onError(long, de.timroes.axmlrpc.XMLRPCException)} method
* of the callback will be called. By default connections won't timeout.
*
* @param timeout The timeout for connections in seconds.
*/
public void setTimeout(int timeout) {
this.timeout = timeout;
}
/**
* Sets the user agent string.
* If this method is never called the default
* user agent 'aXMLRPC' will be used.
*
* @param userAgent The new user agent string.
*/
public void setUserAgentString(String userAgent) {
httpParameters.put(USER_AGENT, userAgent);
}
/**
* Sets a proxy to use for this client. If you want to use the system proxy,
* use {@link #FLAGS_adbUSE_SYSTEM_PROXY} instead. If combined with
* {@code FLAGS_USE_SYSTEM_PROXY}, this proxy will be used instead of the
* system proxy.
*
* @param proxy A proxy to use for the connection.
*/
public void setProxy(Proxy proxy) {
this.proxy = proxy;
} }
/**
* Set a HTTP header field to a custom value.
* You cannot modify the Host or Content-Type field that way.
* If the field already exists, the old value is overwritten.
*
* @param headerName The name of the header field.
* @param headerValue The new value of the header field.
*/
public void setCustomHttpHeader(String headerName, String headerValue) {
if(CONTENT_TYPE.equals(headerName) || HOST.equals(headerName)
|| CONTENT_LENGTH.equals(headerName)) {
throw new XMLRPCRuntimeException("You cannot modify the Host, Content-Type or Content-Length header.");
}
httpParameters.put(headerName, headerValue);
}
/**
* Set the username and password that should be used to perform basic
* http authentication.
*
* @param user Username
* @param pass Password
*/
public void setLoginData(String user, String pass) {
authManager.setAuthData(user, pass);
}
/**
* Clear the username and password. No basic HTTP authentication will be used
* in the next calls.
*/
public void clearLoginData() {
authManager.clearAuthData();
}
/**
* Returns a {@link Map} of all cookies. It contains each cookie key as a map
* key and its value as a map value. Cookies will only be used if {@link #FLAGS_ENABLE_COOKIES}
* has been set for the client. This map will also be available (and empty)
* when this flag hasn't been said, but has no effect on the HTTP connection.
*
* @return A {@code Map} of all cookies.
*/
public Map<String,String> getCookies() {
return cookieManager.getCookies();
}
/**
* Delete all cookies currently used by the client.
* This method has only an effect, as long as the FLAGS_ENABLE_COOKIES has
* been set on this client.
*/
public void clearCookies() {
cookieManager.clearCookies();
}
/**
* Installs a custom {@link TrustManager} to handle SSL/TLS certificate verification.
* This will replace any previously installed {@code TrustManager}s.
* If {@link #FLAGS_SSL_IGNORE_INVALID_CERT} is set, this won't do anything.
*
* @param trustManager {@link TrustManager} to install.
*
* @see #installCustomTrustManagers(javax.net.ssl.TrustManager[])
*/
public void installCustomTrustManager(TrustManager trustManager) {
if(!isFlagSet(FLAGS_SSL_IGNORE_INVALID_CERT)) {
trustManagers = new TrustManager[] { trustManager };
}
}
/**
* Installs custom {@link TrustManager TrustManagers} to handle SSL/TLS certificate
* verification. This will replace any previously installed {@code TrustManagers}s.
* If {@link #FLAGS_SSL_IGNORE_INVALID_CERT} is set, this won't do anything.
*
* @param trustManagers {@link TrustManager TrustManagers} to install.
*
* @see #installCustomTrustManager(javax.net.ssl.TrustManager)
*/
public void installCustomTrustManagers(TrustManager[] trustManagers) {
if(!isFlagSet(FLAGS_SSL_IGNORE_INVALID_CERT)) {
this.trustManagers = trustManagers.clone();
}
}
/**
* Installs a custom {@link KeyManager} to handle SSL/TLS certificate verification.
* This will replace any previously installed {@code KeyManager}s.
* If {@link #FLAGS_SSL_IGNORE_INVALID_CERT} is set, this won't do anything.
*
* @param keyManager {@link KeyManager} to install.
*
* @see #installCustomKeyManagers(javax.net.ssl.KeyManager[])
*/
public void installCustomKeyManager(KeyManager keyManager) {
if(!isFlagSet(FLAGS_SSL_IGNORE_INVALID_CERT)) {
keyManagers = new KeyManager[] { keyManager };
}
}
/**
* Installs custom {@link KeyManager KeyManagers} to handle SSL/TLS certificate
* verification. This will replace any previously installed {@code KeyManagers}s.
* If {@link #FLAGS_SSL_IGNORE_INVALID_CERT} is set, this won't do anything.
*
* @param keyManagers {@link KeyManager KeyManagers} to install.
*
* @see #installCustomKeyManager(javax.net.ssl.KeyManager)
*/
public void installCustomKeyManagers(KeyManager[] keyManagers) {
if(!isFlagSet(FLAGS_SSL_IGNORE_INVALID_CERT)) {
this.keyManagers = keyManagers.clone();
}
}
/** /**
* Call a remote procedure on the server. The method must be described by * Call a remote procedure on the server. The method must be described by
* a method name. If the method requires parameters, this must be set. * a method name. If the method requires parameters, this must be set.
@ -452,7 +192,12 @@ public class XMLRPCClient {
* @throws XMLRPCException Will be thrown if an error occurred during the call. * @throws XMLRPCException Will be thrown if an error occurred during the call.
*/ */
public Object call(String method, Object... params) throws XMLRPCException { public Object call(String method, Object... params) throws XMLRPCException {
return new Caller().call(method, params); try {
return new Caller().call(method, params);
} catch (CancelException e) {
// Should not happen as this is not an async call
throw new XMLRPCException("Background thread was explicitly cancelled, but not started asynchronously.");
}
} }
/** /**
@ -539,8 +284,8 @@ public class XMLRPCClient {
private String methodName; private String methodName;
private Object[] params; private Object[] params;
HttpPost post = null;
private volatile boolean canceled; private volatile boolean canceled;
private HttpURLConnection http;
/** /**
* Create a new Caller for asynchronous use. * Create a new Caller for asynchronous use.
@ -580,12 +325,11 @@ public class XMLRPCClient {
backgroundCalls.put(threadId, this); backgroundCalls.put(threadId, this);
Object o = this.call(methodName, params); Object o = this.call(methodName, params);
listener.onResponse(threadId, o); listener.onResponse(threadId, o);
} catch(CancelException ex) {
// Don't notify the listener, if the call has been canceled.
} catch(XMLRPCServerException ex) { } catch(XMLRPCServerException ex) {
listener.onServerError(threadId, ex); listener.onServerError(threadId, ex);
} catch (XMLRPCException ex) { } catch (XMLRPCException ex) {
listener.onError(threadId, ex); listener.onError(threadId, ex);
} catch (CancelException e) {
} finally { } finally {
backgroundCalls.remove(threadId); backgroundCalls.remove(threadId);
} }
@ -599,7 +343,8 @@ public class XMLRPCClient {
// Set the flag, that this thread has been canceled // Set the flag, that this thread has been canceled
canceled = true; canceled = true;
// Disconnect the connection to the server // Disconnect the connection to the server
http.disconnect(); if (post != null)
post.abort();
} }
/** /**
@ -615,57 +360,24 @@ public class XMLRPCClient {
* @param params An array of parameters for the method. * @param params An array of parameters for the method.
* @return The result of the server. * @return The result of the server.
* @throws XMLRPCException Will be thrown if an error occurred during the call. * @throws XMLRPCException Will be thrown if an error occurred during the call.
* @throws CancelException WIll be thrown if the async execution is explicitly cancelled.
*/ */
public Object call(String methodName, Object[] params) throws XMLRPCException { public Object call(String methodName, Object[] params) throws XMLRPCException, CancelException {
try { try {
Call c = createCall(methodName, params); Call c = createCall(methodName, params);
// If proxy is available, use it // Prepare POST request
URLConnection conn; HttpPost post = new HttpPost(url);
if(proxy != null) post.getParams().setParameter("http.protocol.handle-redirects", false);
conn = url.openConnection(proxy); post.setHeader(CONTENT_TYPE, TYPE_XML);
else StringEntity entity = new StringEntity(c.getXML(), HTTP.UTF_8);
conn = url.openConnection(); entity.setContentType(TYPE_XML);
post.setEntity(entity);
http = verifyConnection(conn);
http.setInstanceFollowRedirects(false);
http.setRequestMethod(HTTP_POST);
http.setDoOutput(true);
http.setDoInput(true);
// Set timeout
if(timeout > 0) {
http.setConnectTimeout(timeout * 1000);
http.setReadTimeout(timeout * 1000);
}
// Set the request parameters HttpResponse response = httpclient.execute(post);
for(Map.Entry<String,String> param : httpParameters.entrySet()) { int statusCode = response.getStatusLine().getStatusCode();
http.setRequestProperty(param.getKey(), param.getValue());
}
authManager.setAuthentication(http);
cookieManager.setCookies(http);
OutputStreamWriter stream = new OutputStreamWriter(http.getOutputStream());
stream.write(c.getXML());
stream.flush();
stream.close();
// Try to get the status code from the connection
int statusCode;
try {
statusCode = http.getResponseCode();
} catch(IOException ex) {
// Due to a bug on android, the getResponseCode()-method will
// fail the first time, with a IOException, when 401 or 403 has been returned.
// The second time it should success. If it fail the second time again
// the normal exceptipon handling can take care of this, since
// it is a real error.
statusCode = http.getResponseCode();
}
InputStream istream; InputStream istream;
@ -677,14 +389,14 @@ public class XMLRPCClient {
if(isFlagSet(FLAGS_IGNORE_STATUSCODE)) { if(isFlagSet(FLAGS_IGNORE_STATUSCODE)) {
// getInputStream will fail if server returned above // getInputStream will fail if server returned above
// error code, use getErrorStream instead // error code, use getErrorStream instead
istream = http.getErrorStream(); istream = response.getEntity().getContent();
} else { } else {
throw new XMLRPCException("Invalid status code '" throw new XMLRPCException("Invalid status code '"
+ statusCode + "' returned from server."); + statusCode + "' returned from server.", new UnauthorizdException(statusCode));
} }
} else { } else {
istream = http.getInputStream(); istream = response.getEntity().getContent();
} }
// If status code is 301 Moved Permanently or 302 Found ... // If status code is 301 Moved Permanently or 302 Found ...
@ -695,15 +407,14 @@ public class XMLRPCClient {
boolean temporaryForward = (statusCode == HttpURLConnection.HTTP_MOVED_TEMP); boolean temporaryForward = (statusCode == HttpURLConnection.HTTP_MOVED_TEMP);
// Get new location from header field. // Get new location from header field.
String newLocation = http.getHeaderField("Location"); String newLocation = response.getFirstHeader("Location").getValue();
// Try getting header in lower case, if no header has been found // Try getting header in lower case, if no header has been found
if(newLocation == null || newLocation.length() <= 0) if(newLocation == null || newLocation.length() <= 0)
newLocation = http.getHeaderField("location"); newLocation = response.getFirstHeader("location").getValue();
// Set new location, disconnect current connection and request to new location. // Set new location, disconnect current connection and request to new location.
URL oldURL = url; String oldURL = url;
url = new URL(newLocation); url = newLocation;
http.disconnect();
Object forwardedResult = call(methodName, params); Object forwardedResult = call(methodName, params);
// In case of temporary forward, restore original URL again for next call. // In case of temporary forward, restore original URL again for next call.
@ -728,13 +439,11 @@ public class XMLRPCClient {
// Check for strict parameters // Check for strict parameters
if(isFlagSet(FLAGS_STRICT)) { if(isFlagSet(FLAGS_STRICT)) {
if(!http.getContentType().startsWith(TYPE_XML)) { if(!response.getFirstHeader("Content-Type").getValue().startsWith(TYPE_XML)) {
throw new XMLRPCException("The Content-Type of the response must be text/xml."); throw new XMLRPCException("The Content-Type of the response must be text/xml.");
} }
} }
cookieManager.readCookies(http);
return responseParser.parse(istream); return responseParser.parse(istream);
} catch(SocketTimeoutException ex) { } catch(SocketTimeoutException ex) {
@ -752,61 +461,17 @@ public class XMLRPCClient {
} }
/** }
* Verifies the given URLConnection to be a valid HTTP or HTTPS connection.
* If the SSL ignoring flags are set, the method will ignore SSL warnings.
*
* @param conn The URLConnection to validate.
* @return The verified HttpURLConnection.
* @throws XMLRPCException Will be thrown if an error occurred.
*/
private HttpURLConnection verifyConnection(URLConnection conn) throws XMLRPCException {
if(!(conn instanceof HttpURLConnection)) {
throw new IllegalArgumentException("The URL is not valid for a http connection.");
}
// Validate the connection if its an SSL connection
if(conn instanceof HttpsURLConnection) {
HttpsURLConnection h = (HttpsURLConnection)conn;
// Don't check, that URL matches the certificate.
if(isFlagSet(FLAGS_SSL_IGNORE_INVALID_HOST)) {
h.setHostnameVerifier(new HostnameVerifier() {
public boolean verify(String host, SSLSession ssl) {
return true;
}
});
}
// Associate the TrustManager with TLS and SSL connections, if present.
if(trustManagers != null) {
try {
String[] sslContexts = new String[]{ "TLS", "SSL" };
for(String ctx : sslContexts) {
SSLContext sc = SSLContext.getInstance(ctx);
sc.init(keyManagers, trustManagers, new SecureRandom());
h.setSSLSocketFactory(sc.getSocketFactory());
}
} catch(Exception ex) {
throw new XMLRPCException(ex);
}
}
return h;
}
return (HttpURLConnection)conn;
}
public static class CancelException extends Exception {
private static final long serialVersionUID = 9125122307255855136L;
} }
private class CancelException extends RuntimeException { } public static class UnauthorizdException extends Exception {
private static final long serialVersionUID = -3331056540713825039L;
private int statusCode;
public UnauthorizdException(int statusCode) { this.statusCode = statusCode; }
public int getStatusCode() { return statusCode; }
}
} }

17
lib/src/org/transdroid/daemon/Rtorrent/RtorrentAdapter.java

@ -25,7 +25,6 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URL;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
@ -64,10 +63,11 @@ import org.transdroid.daemon.task.SetFilePriorityTask;
import org.transdroid.daemon.task.SetLabelTask; import org.transdroid.daemon.task.SetLabelTask;
import org.transdroid.daemon.task.SetTransferRatesTask; import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.util.DLog; import org.transdroid.daemon.util.DLog;
import org.transdroid.daemon.util.FakeTrustManager; import org.transdroid.daemon.util.HttpHelper;
import de.timroes.axmlrpc.XMLRPCClient; import de.timroes.axmlrpc.XMLRPCClient;
import de.timroes.axmlrpc.XMLRPCException; import de.timroes.axmlrpc.XMLRPCException;
import de.timroes.axmlrpc.XMLRPCClient.UnauthorizdException;
/** /**
* An adapter that allows for easy access to rTorrent torrent data. Communication * An adapter that allows for easy access to rTorrent torrent data. Communication
@ -257,6 +257,8 @@ public class RtorrentAdapter implements IDaemonAdapter {
return rpcclient.call(serverMethod, arguments); return rpcclient.call(serverMethod, arguments);
} catch (XMLRPCException e) { } catch (XMLRPCException e) {
DLog.d(LOG_NAME, e.toString()); DLog.d(LOG_NAME, e.toString());
if (e.getCause() instanceof UnauthorizdException)
throw new DaemonException(ExceptionType.AuthenticationFailure, e.toString());
if (e.getCause() instanceof DaemonException) if (e.getCause() instanceof DaemonException)
throw (DaemonException) e.getCause(); throw (DaemonException) e.getCause();
throw new DaemonException(ExceptionType.ConnectionError, "Error making call to " + serverMethod + " with params [" + (params.length() > 100? params.substring(0, 100) + "...": params) + " ]: " + e.toString()); throw new DaemonException(ExceptionType.ConnectionError, "Error making call to " + serverMethod + " with params [" + (params.length() > 100? params.substring(0, 100) + "...": params) + " ]: " + e.toString());
@ -271,15 +273,8 @@ public class RtorrentAdapter implements IDaemonAdapter {
*/ */
private void initialise() throws DaemonException, MalformedURLException { private void initialise() throws DaemonException, MalformedURLException {
//this.rpcclient = new XMLRPCClient(HttpHelper.createStandardHttpClient(settings, true), buildWebUIUrl().trim()); int flags = XMLRPCClient.FLAGS_8BYTE_INT;
int flags = XMLRPCClient.FLAGS_8BYTE_INT | XMLRPCClient.FLAGS_ENABLE_COOKIES; this.rpcclient = new XMLRPCClient(HttpHelper.createStandardHttpClient(settings, true), buildWebUIUrl(), flags);
if (settings.getSsl() && settings.getSslTrustAll())
flags = XMLRPCClient.FLAGS_8BYTE_INT | XMLRPCClient.FLAGS_ENABLE_COOKIES | XMLRPCClient.FLAGS_SSL_IGNORE_INVALID_CERT;
this.rpcclient = new XMLRPCClient(new URL(buildWebUIUrl().trim()), flags);
if (settings.getSsl() && settings.getSslTrustKey() != null && !settings.getSslTrustKey().isEmpty())
this.rpcclient.installCustomTrustManager(new FakeTrustManager(settings.getSslTrustKey()));
this.rpcclient.setTimeout(settings.getTimeoutInMilliseconds() / 1000);
this.rpcclient.setLoginData(settings.getUsername(), settings.getPassword());
} }

2
lib/src/org/transdroid/daemon/util/HttpHelper.java

@ -71,7 +71,7 @@ public class HttpHelper {
/** /**
* The 'User-Agent' name to send to the server * The 'User-Agent' name to send to the server
*/ */
public static String userAgent = null; public static String userAgent = "Transdroid Torrent Connect";
/** /**
* Creates a standard Apache HttpClient that is thread safe, supports different SSL auth methods and basic * Creates a standard Apache HttpClient that is thread safe, supports different SSL auth methods and basic

Loading…
Cancel
Save