diff --git a/core/libs/transdroid-connect.jar b/core/libs/transdroid-connect.jar
index 1fdbd664..868d4aa1 100644
Binary files a/core/libs/transdroid-connect.jar and b/core/libs/transdroid-connect.jar differ
diff --git a/lib/src/de/timroes/axmlrpc/AuthenticationManager.java b/lib/src/de/timroes/axmlrpc/AuthenticationManager.java
new file mode 100644
index 00000000..2a603010
--- /dev/null
+++ b/lib/src/de/timroes/axmlrpc/AuthenticationManager.java
@@ -0,0 +1,55 @@
+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);
+
+ }
+
+}
\ No newline at end of file
diff --git a/lib/src/de/timroes/axmlrpc/Call.java b/lib/src/de/timroes/axmlrpc/Call.java
new file mode 100644
index 00000000..de393ec5
--- /dev/null
+++ b/lib/src/de/timroes/axmlrpc/Call.java
@@ -0,0 +1,86 @@
+package de.timroes.axmlrpc;
+
+import de.timroes.axmlrpc.serializer.SerializerHandler;
+import de.timroes.axmlrpc.xmlcreator.SimpleXMLCreator;
+import de.timroes.axmlrpc.xmlcreator.XmlElement;
+
+/**
+ * A Call object represents a call of a remote methode.
+ * It contains the name of the method to be called and the parameters to use
+ * in this remote procedure call. To send it over the network the method getXML
+ * returns an xml representation according to the XML-RPC specification as a String.
+ *
+ * @author Tim Roes
+ */
+public class Call {
+
+ private String method;
+ private Object[] params;
+
+ /**
+ * Create a new method call with the given name and no parameters.
+ * @param method The method to be called.
+ */
+ public Call(String method) {
+ this(method, null);
+ }
+
+ /**
+ * Create a new method call with the given name and parameters.
+ * @param method The method to be called.
+ * @param params An array of parameters for the method.
+ */
+ public Call(String method, Object[] params) {
+ this.method = method;
+ this.params = params;
+ }
+
+ /**
+ * Return an xml representation of the method call as specified in
+ * http://www.xmlrpc.com/spec. If flags have been set in the XMLRPCClient
+ * the returning xml does not comply strict to the standard.
+ *
+ * @return The string of the xml representing this call.
+ * @throws XMLRPCException Will be thrown whenever the xml representation cannot
+ * be build without errors.
+ * @see XMLRPCClient
+ */
+ public String getXML() throws XMLRPCException {
+
+ SimpleXMLCreator creator = new SimpleXMLCreator();
+
+ XmlElement methodCall = new XmlElement(XMLRPCClient.METHOD_CALL);
+ creator.setRootElement(methodCall);
+
+ XmlElement methodName = new XmlElement(XMLRPCClient.METHOD_NAME);
+ methodName.setContent(method);
+ methodCall.addChildren(methodName);
+
+ if(params != null && params.length > 0) {
+ XmlElement params = new XmlElement(XMLRPCClient.PARAMS);
+ methodCall.addChildren(params);
+
+ for(Object o : this.params) {
+ params.addChildren(getXMLParam(o));
+ }
+ }
+
+ return creator.toString();
+ }
+
+ /**
+ * Generates the param xml tag for a specific parameter object.
+ *
+ * @param o The parameter object.
+ * @return The object serialized into an xml tag.
+ * @throws XMLRPCException Will be thrown if the serialization failed.
+ */
+ private XmlElement getXMLParam(Object o) throws XMLRPCException {
+ XmlElement param = new XmlElement(XMLRPCClient.PARAM);
+ XmlElement value = new XmlElement(XMLRPCClient.VALUE);
+ param.addChildren(value);
+ value.addChildren(SerializerHandler.getDefault().serialize(o));
+ return param;
+ }
+
+}
diff --git a/lib/src/de/timroes/axmlrpc/CookieManager.java b/lib/src/de/timroes/axmlrpc/CookieManager.java
new file mode 100644
index 00000000..8f6ae475
--- /dev/null
+++ b/lib/src/de/timroes/axmlrpc/CookieManager.java
@@ -0,0 +1,95 @@
+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 cookies = new ConcurrentHashMap();
+
+ /**
+ * 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 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 cookie : cookies.entrySet()) {
+ concat += cookie.getKey() + "=" + cookie.getValue() + "; ";
+ }
+ http.setRequestProperty(COOKIE, concat);
+
+ }
+
+}
\ No newline at end of file
diff --git a/lib/src/de/timroes/axmlrpc/ResponseParser.java b/lib/src/de/timroes/axmlrpc/ResponseParser.java
new file mode 100644
index 00000000..b0968963
--- /dev/null
+++ b/lib/src/de/timroes/axmlrpc/ResponseParser.java
@@ -0,0 +1,96 @@
+package de.timroes.axmlrpc;
+
+import de.timroes.axmlrpc.serializer.SerializerHandler;
+import java.io.InputStream;
+import java.util.Map;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * The ResponseParser parses the response of an XMLRPC server to an object.
+ *
+ * @author Tim Roes
+ */
+class ResponseParser {
+
+ private static final String FAULT_CODE = "faultCode";
+ private static final String FAULT_STRING = "faultString";
+
+ /**
+ * The given InputStream must contain the xml response from an xmlrpc server.
+ * This method extract the content of it as an object.
+ *
+ * @param response The InputStream of the server response.
+ * @return The returned object.
+ * @throws XMLRPCException Will be thrown whenever something fails.
+ * @throws XMLRPCServerException Will be thrown, if the server returns an error.
+ */
+ public Object parse(InputStream response) throws XMLRPCException {
+
+ try {
+
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document dom = builder.parse(response);
+ Element e = dom.getDocumentElement();
+
+ // Check for root tag
+ if(!e.getNodeName().equals(XMLRPCClient.METHOD_RESPONSE)) {
+ throw new XMLRPCException("MethodResponse root tag is missing.");
+ }
+
+ e = XMLUtil.getOnlyChildElement(e.getChildNodes());
+
+ if(e.getNodeName().equals(XMLRPCClient.PARAMS)) {
+
+ e = XMLUtil.getOnlyChildElement(e.getChildNodes());
+
+ if(!e.getNodeName().equals(XMLRPCClient.PARAM)) {
+ throw new XMLRPCException("The params tag must contain a param tag.");
+ }
+
+ return getReturnValueFromElement(e);
+
+ } else if(e.getNodeName().equals(XMLRPCClient.FAULT)) {
+
+ @SuppressWarnings("unchecked")
+ Map o = (Map)getReturnValueFromElement(e);
+
+ throw new XMLRPCServerException((String)o.get(FAULT_STRING), (Integer)o.get(FAULT_CODE));
+
+ }
+
+ throw new XMLRPCException("The methodResponse tag must contain a fault or params tag.");
+
+ } catch (Exception ex) {
+
+ if(ex instanceof XMLRPCServerException)
+ throw (XMLRPCServerException)ex;
+ else
+ throw new XMLRPCException("Error getting result from server.", ex);
+
+ }
+
+ }
+
+ /**
+ * This method takes an element (must be a param or fault element) and
+ * returns the deserialized object of this param tag.
+ *
+ * @param element An param element.
+ * @return The deserialized object within the given param element.
+ * @throws XMLRPCException Will be thrown when the structure of the document
+ * doesn't match the XML-RPC specification.
+ */
+ private Object getReturnValueFromElement(Element element) throws XMLRPCException {
+
+ element = XMLUtil.getOnlyChildElement(element.getChildNodes());
+
+ return SerializerHandler.getDefault().deserialize(element);
+
+ }
+
+}
\ No newline at end of file
diff --git a/lib/src/de/timroes/axmlrpc/XMLRPCCallback.java b/lib/src/de/timroes/axmlrpc/XMLRPCCallback.java
new file mode 100644
index 00000000..9e4c0497
--- /dev/null
+++ b/lib/src/de/timroes/axmlrpc/XMLRPCCallback.java
@@ -0,0 +1,36 @@
+package de.timroes.axmlrpc;
+
+/**
+ * The XMLRPCCallback interface must be implemented by a listener for an
+ * asynchronous call to a server method.
+ * When the server responds, the corresponding method on the listener is called.
+ *
+ * @author Tim Roes
+ */
+public interface XMLRPCCallback {
+
+ /**
+ * This callback is called whenever the server successfully responds.
+ *
+ * @param id The id as returned by the XMLRPCClient.asyncCall(..) method for this request.
+ * @param result The Object returned from the server.
+ */
+ public void onResponse(long id, Object result);
+
+ /**
+ * This callback is called whenever an error occurs during the method call.
+ *
+ * @param id The id as returned by the XMLRPCClient.asyncCall(..) method for this request.
+ * @param error The error occured.
+ */
+ public void onError(long id, XMLRPCException error);
+
+ /**
+ * This callback is called whenever the server returns an error.
+ *
+ * @param id The id as returned by the XMLRPCClient.asyncCall(..) method for this request.
+ * @param error The error returned from the server.
+ */
+ public void onServerError(long id, XMLRPCServerException error);
+
+}
diff --git a/lib/src/de/timroes/axmlrpc/XMLRPCClient.java b/lib/src/de/timroes/axmlrpc/XMLRPCClient.java
new file mode 100644
index 00000000..d07332a9
--- /dev/null
+++ b/lib/src/de/timroes/axmlrpc/XMLRPCClient.java
@@ -0,0 +1,812 @@
+package de.timroes.axmlrpc;
+
+import de.timroes.axmlrpc.serializer.SerializerHandler;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.net.*;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.net.ssl.*;
+
+/**
+ * An XMLRPCClient is a client used to make XML-RPC (Extensible Markup Language
+ * Remote Procedure Calls).
+ * The specification of XMLRPC can be found at http://www.xmlrpc.com/spec.
+ * You can use flags to extend the functionality of the client to some extras.
+ * Further information on the flags can be found in the documentation of these.
+ * For a documentation on how to use this class see also the README file delivered
+ * with the source of this library.
+ *
+ * @author Tim Roes
+ */
+public class XMLRPCClient {
+
+ private static final String DEFAULT_USER_AGENT = "aXMLRPC";
+
+ /**
+ * Constants from the http protocol.
+ */
+ static final String USER_AGENT = "User-Agent";
+ static final String CONTENT_TYPE = "Content-Type";
+ static final String TYPE_XML = "text/xml; charset=utf-8";
+ static final String HOST = "Host";
+ static final String CONTENT_LENGTH = "Content-Length";
+ static final String HTTP_POST = "POST";
+
+ /**
+ * XML elements to be used.
+ */
+ static final String METHOD_RESPONSE = "methodResponse";
+ static final String PARAMS = "params";
+ static final String PARAM = "param";
+ public static final String VALUE = "value";
+ static final String FAULT = "fault";
+ static final String METHOD_CALL = "methodCall";
+ static final String METHOD_NAME = "methodName";
+ static final String STRUCT_MEMBER = "member";
+
+ /**
+ * No flags should be set.
+ */
+ public static final int FLAGS_NONE = 0x0;
+
+ /**
+ * The client should parse responses strict to specification.
+ * It will check if the given content-type is right.
+ * The method name in a call must only contain of A-Z, a-z, 0-9, _, ., :, /
+ * Normally this is not needed.
+ */
+ public static final int FLAGS_STRICT = 0x01;
+
+ /**
+ * The client will be able to handle 8 byte integer values (longs).
+ * The xml type tag <i8> will be used. This is not in the specification
+ * but some libraries and servers support this behaviour.
+ * If this isn't enabled you cannot recieve 8 byte integers and if you try to
+ * send a long the value must be within the 4byte integer range.
+ */
+ 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
+ * as . This extension is described under: http://ontosys.com/xml-rpc/extensions.php
+ */
+ public static final int FLAGS_NIL = 0x08;
+
+ /**
+ * With this flag enabled, the XML-RPC client will ignore the HTTP status
+ * code of the response from the server. According to specification the
+ * status code must be 200. This flag is only needed for the use with
+ * not standard compliant servers.
+ */
+ public static final int FLAGS_IGNORE_STATUSCODE = 0x10;
+
+ /**
+ * With this flag enabled, the client will forward the request, if
+ * the 301 or 302 HTTP status code has been received. If this flag has not
+ * been set, the client will throw an exception on these HTTP status codes.
+ */
+ 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
+ * as a string element. This is just for incoming messages. Outgoing messages
+ * will still be generated according to specification.
+ */
+ public static final int FLAGS_DEFAULT_TYPE_STRING = 0x100;
+
+ /**
+ * With this flag enabled, the {@link XMLRPCClient} ignores all namespaces
+ * used within the response from the server.
+ */
+ public static final int FLAGS_IGNORE_NAMESPACES = 0x200;
+
+ /**
+ * With this flag enabled, the {@link XMLRPCClient} will use the system http
+ * proxy to connect to the XML-RPC server.
+ */
+ public static final int FLAGS_USE_SYSTEM_PROXY = 0x400;
+
+ /**
+ * This prevents the decoding of incoming strings, meaning & and <
+ * won't be decoded to the & sign and the "less then" sign. See
+ * {@link #FLAGS_NO_STRING_ENCODE} for the counterpart.
+ */
+ public static final int FLAGS_NO_STRING_DECODE = 0x800;
+
+ /**
+ * By default outgoing string values will be encoded according to specification.
+ * Meaning the & sign will be encoded to & and the "less then" sign to <.
+ * If you set this flag, the encoding won't be done for outgoing string values.
+ * See {@link #FLAGS_NO_STRING_ENCODE} for the counterpart.
+ */
+ 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 will set some flags, so that the not standard conform behavior
+ * of the server will be ignored.
+ * This will enable the following flags: FLAGS_IGNORE_NAMESPACES, FLAGS_NIL,
+ * FLAGS_DEFAULT_TYPE_STRING
+ */
+ public static final int FLAGS_APACHE_WS = FLAGS_IGNORE_NAMESPACES | FLAGS_NIL
+ | FLAGS_DEFAULT_TYPE_STRING;
+
+ private final int flags;
+
+ private URL url;
+ private Map httpParameters = new ConcurrentHashMap();
+
+ private Map backgroundCalls = new ConcurrentHashMap();
+
+ 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.
+ *
+ * @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.
+ */
+ public XMLRPCClient(URL url, String userAgent, int flags) {
+
+ SerializerHandler.initialize(flags);
+
+ this.url = url;
+
+ this.flags = flags;
+ // Create a parser for the http responses.
+ 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.
+ * No flags will be used.
+ * The default user agent string will be used.
+ *
+ * @param url The url to send the requests to.
+ */
+ public XMLRPCClient(URL url) {
+ this(url, DEFAULT_USER_AGENT, 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 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
+ * a method name. If the method requires parameters, this must be set.
+ * The type of the return object depends on the server. You should consult
+ * the server documentation and then cast the return value according to that.
+ * This method will block until the server returned a result (or an error occurred).
+ * Read the README file delivered with the source code of this library for more
+ * information.
+ *
+ * @param method A method name to call.
+ * @param params An array of parameters for the method.
+ * @return The result of the server.
+ * @throws XMLRPCException Will be thrown if an error occurred during the call.
+ */
+ public Object call(String method, Object... params) throws XMLRPCException {
+ return new Caller().call(method, params);
+ }
+
+ /**
+ * Asynchronously 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. When the server returns a response the onResponse method is called
+ * on the listener. If the server returns an error the onServerError method
+ * is called on the listener. The onError method is called whenever something
+ * fails. This method returns immediately and returns an identifier for the
+ * request. All listener methods get this id as a parameter to distinguish between
+ * multiple requests.
+ *
+ * @param listener A listener, which will be notified about the server response or errors.
+ * @param methodName A method name to call on the server.
+ * @param params An array of parameters for the method.
+ * @return The id of the current request.
+ */
+ public long callAsync(XMLRPCCallback listener, String methodName, Object... params) {
+ long id = System.currentTimeMillis();
+ new Caller(listener, id, methodName, params).start();
+ return id;
+ }
+
+ /**
+ * Cancel a specific asynchronous call.
+ *
+ * @param id The id of the call as returned by the callAsync method.
+ */
+ public void cancel(long id) {
+
+ // Lookup the background call for the given id.
+ Caller cancel = backgroundCalls.get(id);
+ if(cancel == null) {
+ return;
+ }
+
+ // Cancel the thread
+ cancel.cancel();
+
+ try {
+ // Wait for the thread
+ cancel.join();
+ } catch (InterruptedException ex) {
+ // Ignore this
+ }
+
+ }
+
+ /**
+ * Create a call object from a given method string and parameters.
+ *
+ * @param method The method that should be called.
+ * @param params An array of parameters or null if no parameters needed.
+ * @return A call object.
+ */
+ private Call createCall(String method, Object[] params) {
+
+ if(isFlagSet(FLAGS_STRICT) && !method.matches("^[A-Za-z0-9\\._:/]*$")) {
+ throw new XMLRPCRuntimeException("Method name must only contain A-Z a-z . : _ / ");
+ }
+
+ return new Call(method, params);
+
+ }
+
+ /**
+ * Checks whether a specific flag has been set.
+ *
+ * @param flag The flag to check for.
+ * @return Whether the flag has been set.
+ */
+ private boolean isFlagSet(int flag) {
+ return (this.flags & flag) != 0;
+ }
+
+ /**
+ * The Caller class is used to make asynchronous calls to the server.
+ * For synchronous calls the Thread function of this class isn't used.
+ */
+ private class Caller extends Thread {
+
+ private XMLRPCCallback listener;
+ private long threadId;
+ private String methodName;
+ private Object[] params;
+
+ private volatile boolean canceled;
+ private HttpURLConnection http;
+
+ /**
+ * Create a new Caller for asynchronous use.
+ *
+ * @param listener The listener to notice about the response or an error.
+ * @param threadId An id that will be send to the listener.
+ * @param methodName The method name to call.
+ * @param params The parameters of the call or null.
+ */
+ public Caller(XMLRPCCallback listener, long threadId, String methodName, Object[] params) {
+ this.listener = listener;
+ this.threadId = threadId;
+ this.methodName = methodName;
+ this.params = params;
+ }
+
+ /**
+ * Create a new Caller for synchronous use.
+ * If the caller has been created with this constructor you cannot use the
+ * start method to start it as a thread. But you can call the call method
+ * on it for synchronous use.
+ */
+ public Caller() { }
+
+ /**
+ * The run method is invoked when the thread gets started.
+ * This will only work, if the Caller has been created with parameters.
+ * It execute the call method and notify the listener about the result.
+ */
+ @Override
+ public void run() {
+
+ if(listener == null)
+ return;
+
+ try {
+ backgroundCalls.put(threadId, this);
+ Object o = this.call(methodName, params);
+ listener.onResponse(threadId, o);
+ } catch(CancelException ex) {
+ // Don't notify the listener, if the call has been canceled.
+ } catch(XMLRPCServerException ex) {
+ listener.onServerError(threadId, ex);
+ } catch (XMLRPCException ex) {
+ listener.onError(threadId, ex);
+ } finally {
+ backgroundCalls.remove(threadId);
+ }
+
+ }
+
+ /**
+ * Cancel this call. This will abort the network communication.
+ */
+ public void cancel() {
+ // Set the flag, that this thread has been canceled
+ canceled = true;
+ // Disconnect the connection to the server
+ http.disconnect();
+ }
+
+ /**
+ * 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.
+ * The type of the return object depends on the server. You should consult
+ * the server documentation and then cast the return value according to that.
+ * This method will block until the server returned a result (or an error occurred).
+ * Read the README file delivered with the source code of this library for more
+ * information.
+ *
+ * @param method A method name to call.
+ * @param params An array of parameters for the method.
+ * @return The result of the server.
+ * @throws XMLRPCException Will be thrown if an error occurred during the call.
+ */
+ public Object call(String methodName, Object[] params) throws XMLRPCException {
+
+ try {
+
+ Call c = createCall(methodName, params);
+
+ // If proxy is available, use it
+ URLConnection conn;
+ if(proxy != null)
+ conn = url.openConnection(proxy);
+ else
+ conn = url.openConnection();
+
+ 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
+ for(Map.Entry param : httpParameters.entrySet()) {
+ 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;
+
+ // If status code was 401 or 403 throw exception or if appropriate
+ // flag is set, ignore error code.
+ if(statusCode == HttpURLConnection.HTTP_FORBIDDEN
+ || statusCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
+
+ if(isFlagSet(FLAGS_IGNORE_STATUSCODE)) {
+ // getInputStream will fail if server returned above
+ // error code, use getErrorStream instead
+ istream = http.getErrorStream();
+ } else {
+ throw new XMLRPCException("Invalid status code '"
+ + statusCode + "' returned from server.");
+ }
+
+ } else {
+ istream = http.getInputStream();
+ }
+
+ // If status code is 301 Moved Permanently or 302 Found ...
+ if(statusCode == HttpURLConnection.HTTP_MOVED_PERM
+ || statusCode == HttpURLConnection.HTTP_MOVED_TEMP) {
+ // ... do either a foward
+ if(isFlagSet(FLAGS_FORWARD)) {
+ boolean temporaryForward = (statusCode == HttpURLConnection.HTTP_MOVED_TEMP);
+
+ // Get new location from header field.
+ String newLocation = http.getHeaderField("Location");
+ // Try getting header in lower case, if no header has been found
+ if(newLocation == null || newLocation.length() <= 0)
+ newLocation = http.getHeaderField("location");
+
+ // Set new location, disconnect current connection and request to new location.
+ URL oldURL = url;
+ url = new URL(newLocation);
+ http.disconnect();
+ Object forwardedResult = call(methodName, params);
+
+ // In case of temporary forward, restore original URL again for next call.
+ if(temporaryForward) {
+ url = oldURL;
+ }
+
+ return forwardedResult;
+
+ } else {
+ // ... or throw an exception
+ throw new XMLRPCException("The server responded with a http 301 or 302 status "
+ + "code, but forwarding has not been enabled (FLAGS_FORWARD).");
+
+ }
+ }
+
+ if(!isFlagSet(FLAGS_IGNORE_STATUSCODE)
+ && statusCode != HttpURLConnection.HTTP_OK) {
+ throw new XMLRPCException("The status code of the http response must be 200.");
+ }
+
+ // Check for strict parameters
+ if(isFlagSet(FLAGS_STRICT)) {
+ if(!http.getContentType().startsWith(TYPE_XML)) {
+ throw new XMLRPCException("The Content-Type of the response must be text/xml.");
+ }
+ }
+
+ cookieManager.readCookies(http);
+
+ return responseParser.parse(istream);
+
+ } catch(SocketTimeoutException ex) {
+ throw new XMLRPCTimeoutException("The XMLRPC call timed out.");
+ } catch (IOException ex) {
+ // If the thread has been canceled this exception will be thrown.
+ // So only throw an exception if the thread hasnt been canceled
+ // or if the thred has not been started in background.
+ if(!canceled || threadId <= 0) {
+ throw new XMLRPCException(ex);
+ } else {
+ throw new CancelException();
+ }
+ }
+
+ }
+
+ /**
+ * 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;
+
+ }
+
+ }
+
+ private class CancelException extends RuntimeException { }
+
+}
diff --git a/lib/src/de/timroes/axmlrpc/XMLRPCException.java b/lib/src/de/timroes/axmlrpc/XMLRPCException.java
new file mode 100644
index 00000000..96c8572d
--- /dev/null
+++ b/lib/src/de/timroes/axmlrpc/XMLRPCException.java
@@ -0,0 +1,26 @@
+package de.timroes.axmlrpc;
+
+/**
+ * The exception is thrown whenever the remote procedure call fails in some point.
+ *
+ * @author Tim Roes
+ */
+public class XMLRPCException extends Exception {
+
+ public XMLRPCException() {
+ super();
+ }
+
+ public XMLRPCException(Exception ex) {
+ super(ex);
+ }
+
+ public XMLRPCException(String ex) {
+ super(ex);
+ }
+
+ public XMLRPCException(String msg, Exception ex) {
+ super(msg, ex);
+ }
+
+}
diff --git a/lib/src/de/timroes/axmlrpc/XMLRPCRuntimeException.java b/lib/src/de/timroes/axmlrpc/XMLRPCRuntimeException.java
new file mode 100644
index 00000000..f2dfcebb
--- /dev/null
+++ b/lib/src/de/timroes/axmlrpc/XMLRPCRuntimeException.java
@@ -0,0 +1,17 @@
+package de.timroes.axmlrpc;
+
+/**
+ *
+ * @author Tim Roes
+ */
+public class XMLRPCRuntimeException extends RuntimeException {
+
+ public XMLRPCRuntimeException(String ex) {
+ super(ex);
+ }
+
+ public XMLRPCRuntimeException(Exception ex) {
+ super(ex);
+ }
+
+}
\ No newline at end of file
diff --git a/lib/src/de/timroes/axmlrpc/XMLRPCServerException.java b/lib/src/de/timroes/axmlrpc/XMLRPCServerException.java
new file mode 100644
index 00000000..0b403e93
--- /dev/null
+++ b/lib/src/de/timroes/axmlrpc/XMLRPCServerException.java
@@ -0,0 +1,38 @@
+package de.timroes.axmlrpc;
+
+/**
+ * This exception will be thrown if the server returns an error. It contains the
+ * message and the error number returned from the server.
+ *
+ * @author Tim Roes
+ */
+public class XMLRPCServerException extends XMLRPCException {
+
+ private int errornr;
+
+ public XMLRPCServerException(String ex, int errnr) {
+ super(ex);
+ this.errornr = errnr;
+ }
+
+ /**
+ * Returns the detail message string of this throwable.
+ * It will have the server error number at the end.
+ *
+ * @return The detail message string of this error.
+ */
+ @Override
+ public String getMessage() {
+ return super.getMessage() + " [" + errornr + "]";
+ }
+
+ /**
+ * Return the error number.
+ *
+ * @return The error number.
+ */
+ public int getErrorNr() {
+ return errornr;
+ }
+
+}
diff --git a/lib/src/de/timroes/axmlrpc/XMLRPCTimeoutException.java b/lib/src/de/timroes/axmlrpc/XMLRPCTimeoutException.java
new file mode 100644
index 00000000..6c24ff94
--- /dev/null
+++ b/lib/src/de/timroes/axmlrpc/XMLRPCTimeoutException.java
@@ -0,0 +1,15 @@
+package de.timroes.axmlrpc;
+
+/**
+ * Will be thrown when a call to the server times out. The timeout can be
+ * set via {@link XMLRPCClient#setTimeout(int)}.
+ *
+ * @author Tim Roes
+ */
+public class XMLRPCTimeoutException extends XMLRPCException {
+
+ XMLRPCTimeoutException(String ex) {
+ super(ex);
+ }
+
+}
\ No newline at end of file
diff --git a/lib/src/de/timroes/axmlrpc/XMLUtil.java b/lib/src/de/timroes/axmlrpc/XMLUtil.java
new file mode 100644
index 00000000..fb3b2cc6
--- /dev/null
+++ b/lib/src/de/timroes/axmlrpc/XMLUtil.java
@@ -0,0 +1,124 @@
+package de.timroes.axmlrpc;
+
+import de.timroes.axmlrpc.xmlcreator.XmlElement;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * This class provides some utility methods for the use with the Java DOM parser.
+ *
+ * @author Tim Roes
+ */
+public class XMLUtil {
+
+ /**
+ * Returns the only child element in a given NodeList.
+ * Will throw an error if there is more then one child element or any other
+ * child that is not an element or an empty text string (whitespace are normal).
+ *
+ * @param list A NodeList of children nodes.
+ * @return The only child element in the given node list.
+ * @throws XMLRPCException Will be thrown if there is more then one child element
+ * except empty text nodes.
+ */
+ public static Element getOnlyChildElement(NodeList list) throws XMLRPCException {
+
+ Element e = null;
+ Node n;
+ for(int i = 0; i < list.getLength(); i++) {
+ n = list.item(i);
+ // Strip only whitespace text elements and comments
+ if((n.getNodeType() == Node.TEXT_NODE
+ && n.getNodeValue().trim().length() <= 0)
+ || n.getNodeType() == Node.COMMENT_NODE)
+ continue;
+
+ // Check if there is anything else than an element node.
+ if(n.getNodeType() != Node.ELEMENT_NODE) {
+ throw new XMLRPCException("Only element nodes allowed.");
+ }
+
+ // If there was already an element, throw exception.
+ if(e != null) {
+ throw new XMLRPCException("Element has more than one children.");
+ }
+
+ e = (Element)n;
+
+ }
+
+ return e;
+
+ }
+
+ /**
+ * Returns the text node from a given NodeList. If the list contains
+ * more then just text nodes, an exception will be thrown.
+ *
+ * @param list The given list of nodes.
+ * @return The text of the given node list.
+ * @throws XMLRPCException Will be thrown if there is more than just one
+ * text node within the list.
+ */
+ public static String getOnlyTextContent(NodeList list) throws XMLRPCException {
+
+ StringBuilder builder = new StringBuilder();
+ Node n;
+
+ for(int i = 0; i < list.getLength(); i++) {
+ n = list.item(i);
+
+ // Skip comments inside text tag.
+ if(n.getNodeType() == Node.COMMENT_NODE) {
+ continue;
+ }
+
+ if(n.getNodeType() != Node.TEXT_NODE) {
+ throw new XMLRPCException("Element must contain only text elements.");
+ }
+
+ builder.append(n.getNodeValue());
+
+ }
+
+ return builder.toString();
+
+ }
+
+ /**
+ * Checks if the given {@link NodeList} contains a child element.
+ *
+ * @param list The {@link NodeList} to check.
+ * @return Whether the {@link NodeList} contains children.
+ */
+ public static boolean hasChildElement(NodeList list) {
+
+ Node n;
+
+ for(int i = 0; i < list.getLength(); i++) {
+ n = list.item(i);
+
+ if(n.getNodeType() == Node.ELEMENT_NODE) {
+ return true;
+ }
+ }
+
+ return false;
+
+ }
+
+ /**
+ * Creates an xml tag with a given type and content.
+ *
+ * @param type The type of the xml tag. What will be filled in the <..>.
+ * @param content The content of the tag.
+ * @return The xml tag with its content as a string.
+ */
+ public static XmlElement makeXmlTag(String type, String content) {
+ XmlElement xml = new XmlElement(type);
+ xml.setContent(content);
+ return xml;
+ }
+
+}
\ No newline at end of file
diff --git a/lib/src/de/timroes/axmlrpc/serializer/ArraySerializer.java b/lib/src/de/timroes/axmlrpc/serializer/ArraySerializer.java
new file mode 100644
index 00000000..5850f806
--- /dev/null
+++ b/lib/src/de/timroes/axmlrpc/serializer/ArraySerializer.java
@@ -0,0 +1,78 @@
+package de.timroes.axmlrpc.serializer;
+
+import de.timroes.axmlrpc.XMLRPCException;
+import de.timroes.axmlrpc.XMLRPCRuntimeException;
+import de.timroes.axmlrpc.XMLUtil;
+import de.timroes.axmlrpc.xmlcreator.XmlElement;
+import java.util.ArrayList;
+import java.util.List;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ *
+ * @author Tim Roes
+ */
+public class ArraySerializer implements Serializer {
+
+ private static final String ARRAY_DATA = "data";
+ private static final String ARRAY_VALUE = "value";
+
+ public Object deserialize(Element content) throws XMLRPCException {
+
+ List
- */
-
-public class XMLRPCClient {
- private static final String TAG_METHOD_CALL = "methodCall";
- private static final String TAG_METHOD_NAME = "methodName";
- private static final String TAG_METHOD_RESPONSE = "methodResponse";
- private static final String TAG_PARAMS = "params";
- private static final String TAG_PARAM = "param";
- private static final String TAG_FAULT = "fault";
- private static final String TAG_FAULT_CODE = "faultCode";
- private static final String TAG_FAULT_STRING = "faultString";
-
- private HttpClient client;
- private HttpPost postMethod;
- private XmlSerializer serializer;
- private HttpParams httpParams;
-
- /**
- * XMLRPCClient constructor. Creates new instance based on server URI
- * @param XMLRPC server URI
- */
- public XMLRPCClient(HttpClient client, URI uri) {
- postMethod = new HttpPost(uri);
- postMethod.addHeader("Content-Type", "text/xml");
-
- // WARNING
- // I had to disable "Expect: 100-Continue" header since I had
- // two second delay between sending http POST request and POST body
- httpParams = postMethod.getParams();
- HttpProtocolParams.setUseExpectContinue(httpParams, false);
-
- this.client = client;
- serializer = Xml.newSerializer();
- }
-
- /**
- * Convenience constructor. Creates new instance based on server String address
- * @param XMLRPC server address
- */
- public XMLRPCClient(HttpClient client, String url) {
- this(client, URI.create(url));
- }
-
- /**
- * Call method with optional parameters. This is general method.
- * If you want to call your method with 0-8 parameters, you can use more
- * convenience call methods
- *
- * @param method name of method to call
- * @param params parameters to pass to method (may be null if method has no parameters)
- * @return deserialized method return value
- * @throws XMLRPCException
- */
- public Object call(String method, Object[] params) throws XMLRPCException {
- return callXMLRPC(method, params);
- }
-
- /**
- * Convenience method call with no parameters
- *
- * @param method name of method to call
- * @return deserialized method return value
- * @throws XMLRPCException
- */
- public Object call(String method) throws XMLRPCException {
- return callXMLRPC(method, null);
- }
-
- /**
- * Convenience method call with one parameter
- *
- * @param method name of method to call
- * @param p0 method's parameter
- * @return deserialized method return value
- * @throws XMLRPCException
- */
- public Object call(String method, Object p0) throws XMLRPCException {
- Object[] params = {
- p0,
- };
- return callXMLRPC(method, params);
- }
-
- /**
- * Convenience method call with two parameters
- *
- * @param method name of method to call
- * @param p0 method's 1st parameter
- * @param p1 method's 2nd parameter
- * @return deserialized method return value
- * @throws XMLRPCException
- */
- public Object call(String method, Object p0, Object p1) throws XMLRPCException {
- Object[] params = {
- p0, p1,
- };
- return callXMLRPC(method, params);
- }
-
- /**
- * Convenience method call with three parameters
- *
- * @param method name of method to call
- * @param p0 method's 1st parameter
- * @param p1 method's 2nd parameter
- * @param p2 method's 3rd parameter
- * @return deserialized method return value
- * @throws XMLRPCException
- */
- public Object call(String method, Object p0, Object p1, Object p2) throws XMLRPCException {
- Object[] params = {
- p0, p1, p2,
- };
- return callXMLRPC(method, params);
- }
-
- /**
- * Convenience method call with four parameters
- *
- * @param method name of method to call
- * @param p0 method's 1st parameter
- * @param p1 method's 2nd parameter
- * @param p2 method's 3rd parameter
- * @param p3 method's 4th parameter
- * @return deserialized method return value
- * @throws XMLRPCException
- */
- public Object call(String method, Object p0, Object p1, Object p2, Object p3) throws XMLRPCException {
- Object[] params = {
- p0, p1, p2, p3,
- };
- return callXMLRPC(method, params);
- }
-
- /**
- * Convenience method call with five parameters
- *
- * @param method name of method to call
- * @param p0 method's 1st parameter
- * @param p1 method's 2nd parameter
- * @param p2 method's 3rd parameter
- * @param p3 method's 4th parameter
- * @param p4 method's 5th parameter
- * @return deserialized method return value
- * @throws XMLRPCException
- */
- public Object call(String method, Object p0, Object p1, Object p2, Object p3, Object p4) throws XMLRPCException {
- Object[] params = {
- p0, p1, p2, p3, p4,
- };
- return callXMLRPC(method, params);
- }
-
- /**
- * Convenience method call with six parameters
- *
- * @param method name of method to call
- * @param p0 method's 1st parameter
- * @param p1 method's 2nd parameter
- * @param p2 method's 3rd parameter
- * @param p3 method's 4th parameter
- * @param p4 method's 5th parameter
- * @param p5 method's 6th parameter
- * @return deserialized method return value
- * @throws XMLRPCException
- */
- public Object call(String method, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5) throws XMLRPCException {
- Object[] params = {
- p0, p1, p2, p3, p4, p5,
- };
- return callXMLRPC(method, params);
- }
-
- /**
- * Convenience method call with seven parameters
- *
- * @param method name of method to call
- * @param p0 method's 1st parameter
- * @param p1 method's 2nd parameter
- * @param p2 method's 3rd parameter
- * @param p3 method's 4th parameter
- * @param p4 method's 5th parameter
- * @param p5 method's 6th parameter
- * @param p6 method's 7th parameter
- * @return deserialized method return value
- * @throws XMLRPCException
- */
- public Object call(String method, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6) throws XMLRPCException {
- Object[] params = {
- p0, p1, p2, p3, p4, p5, p6,
- };
- return callXMLRPC(method, params);
- }
-
- /**
- * Convenience method call with eight parameters
- *
- * @param method name of method to call
- * @param p0 method's 1st parameter
- * @param p1 method's 2nd parameter
- * @param p2 method's 3rd parameter
- * @param p3 method's 4th parameter
- * @param p4 method's 5th parameter
- * @param p5 method's 6th parameter
- * @param p6 method's 7th parameter
- * @param p7 method's 8th parameter
- * @return deserialized method return value
- * @throws XMLRPCException
- */
- public Object call(String method, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7) throws XMLRPCException {
- Object[] params = {
- p0, p1, p2, p3, p4, p5, p6, p7,
- };
- return callXMLRPC(method, params);
- }
-
- /**
- * Call method with optional parameters
- *
- * @param method name of method to call
- * @param params parameters to pass to method (may be null if method has no parameters)
- * @return deserialized method return value
- * @throws XMLRPCException
- */
- @SuppressWarnings("unchecked")
- protected Object callXMLRPC(String method, Object[] params) throws XMLRPCException {
- try {
- // prepare POST body
- StringWriter bodyWriter = new StringWriter();
- serializer.setOutput(bodyWriter);
- serializer.startDocument(null, null);
- serializer.startTag(null, TAG_METHOD_CALL);
- // set method name
- serializer.startTag(null, TAG_METHOD_NAME).text(method).endTag(null, TAG_METHOD_NAME);
- if (params != null && params.length != 0) {
- // set method params
- serializer.startTag(null, TAG_PARAMS);
- for (int i=0; i) or TAG_FAULT ()
- String tag = pullParser.getName();
- if (tag.equals(TAG_PARAMS)) {
- // normal response
- pullParser.nextTag(); // TAG_PARAM ()
- pullParser.require(XmlPullParser.START_TAG, null, TAG_PARAM);
- pullParser.nextTag(); // TAG_VALUE ()
- // no parser.require() here since its called in XMLRPCSerializer.deserialize() below
-
- // deserialize result
- Object obj = XMLRPCSerializer.deserialize(pullParser);
- entity.consumeContent();
- return obj;
- } else
- if (tag.equals(TAG_FAULT)) {
- // fault response
- pullParser.nextTag(); // TAG_VALUE ()
- // no parser.require() here since its called in XMLRPCSerializer.deserialize() below
-
- // deserialize fault result
- Map map = (Map) XMLRPCSerializer.deserialize(pullParser);
- String faultString = (String) map.get(TAG_FAULT_STRING);
- int faultCode = (Integer) map.get(TAG_FAULT_CODE);
- entity.consumeContent();
- throw new XMLRPCFault(faultString, faultCode);
- } else {
- entity.consumeContent();
- throw new XMLRPCException("Bad tag <" + tag + "> in XMLRPC response - neither nor ");
- }
- } catch (XMLRPCException e) {
- // catch & propagate XMLRPCException/XMLRPCFault
- throw e;
- } catch (Exception e) {
- // wrap any other Exception(s) around XMLRPCException
- throw new XMLRPCException(e);
- }
- }
-}
diff --git a/lib/src/org/xmlrpc/android/XMLRPCException.java b/lib/src/org/xmlrpc/android/XMLRPCException.java
deleted file mode 100644
index 0bfd4e50..00000000
--- a/lib/src/org/xmlrpc/android/XMLRPCException.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.xmlrpc.android;
-
-public class XMLRPCException extends Exception {
-
- /**
- *
- */
- private static final long serialVersionUID = 7499675036625522379L;
-
- public XMLRPCException(Exception e) {
- super(e);
- }
-
- public XMLRPCException(String string) {
- super(string);
- }
-}
diff --git a/lib/src/org/xmlrpc/android/XMLRPCFault.java b/lib/src/org/xmlrpc/android/XMLRPCFault.java
deleted file mode 100644
index 9f371d07..00000000
--- a/lib/src/org/xmlrpc/android/XMLRPCFault.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.xmlrpc.android;
-
-public class XMLRPCFault extends XMLRPCException {
- /**
- *
- */
- private static final long serialVersionUID = 5676562456612956519L;
- private String faultString;
- private int faultCode;
-
- public XMLRPCFault(String faultString, int faultCode) {
- super("XMLRPC Fault: " + faultString + " [code " + faultCode + "]");
- this.faultString = faultString;
- this.faultCode = faultCode;
- }
-
- public String getFaultString() {
- return faultString;
- }
-
- public int getFaultCode() {
- return faultCode;
- }
-}
diff --git a/lib/src/org/xmlrpc/android/XMLRPCSerializer.java b/lib/src/org/xmlrpc/android/XMLRPCSerializer.java
deleted file mode 100644
index 4f488516..00000000
--- a/lib/src/org/xmlrpc/android/XMLRPCSerializer.java
+++ /dev/null
@@ -1,205 +0,0 @@
-package org.xmlrpc.android;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringReader;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-class XMLRPCSerializer {
- static final String TAG_NAME = "name";
- static final String TAG_MEMBER = "member";
- static final String TAG_VALUE = "value";
- static final String TAG_DATA = "data";
-
- static final String TYPE_INT = "int";
- static final String TYPE_I4 = "i4";
- static final String TYPE_I8 = "i8";
- static final String TYPE_DOUBLE = "double";
- static final String TYPE_BOOLEAN = "boolean";
- static final String TYPE_STRING = "string";
- static final String TYPE_DATE_TIME_ISO8601 = "dateTime.iso8601";
- static final String TYPE_BASE64 = "base64";
- static final String TYPE_ARRAY = "array";
- static final String TYPE_STRUCT = "struct";
-
- static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
-
- @SuppressWarnings("unchecked")
- static void serialize(XmlSerializer serializer, Object object ) throws IOException {
- // check for scalar types:
- if (object instanceof Integer || object instanceof Short || object instanceof Byte) {
- serializer.startTag(null, TYPE_I4).text(object.toString()).endTag(null, TYPE_I4);
- } else
- if (object instanceof Long) {
- serializer.startTag(null, TYPE_I8).text(object.toString()).endTag(null, TYPE_I8);
- } else
- if (object instanceof Double || object instanceof Float) {
- serializer.startTag(null, TYPE_DOUBLE).text(object.toString()).endTag(null, TYPE_DOUBLE);
- } else
- if (object instanceof Boolean) {
- Boolean bool = (Boolean) object;
- String boolStr = bool.booleanValue() ? "1" : "0";
- serializer.startTag(null, TYPE_BOOLEAN).text(boolStr).endTag(null, TYPE_BOOLEAN);
- } else
- if (object instanceof String) {
- serializer.startTag(null, TYPE_STRING).text(object.toString()).endTag(null, TYPE_STRING);
- } else
- if (object instanceof Date || object instanceof Calendar) {
- String dateStr = dateFormat.format(object);
- serializer.startTag(null, TYPE_DATE_TIME_ISO8601).text(dateStr).endTag(null, TYPE_DATE_TIME_ISO8601);
- } else
- if (object instanceof byte[] ){
- String value = new String(Base64Coder.encode((byte[])object));
- serializer.startTag(null, TYPE_BASE64).text(value).endTag(null, TYPE_BASE64);
- } else
- if (object instanceof List) {
- serializer.startTag(null, TYPE_ARRAY).startTag(null, TAG_DATA);
- List list = (List) object;
- Iterator iter = list.iterator();
- while (iter.hasNext()) {
- Object o = iter.next();
- serializer.startTag(null, TAG_VALUE);
- serialize(serializer, o);
- serializer.endTag(null, TAG_VALUE);
- }
- serializer.endTag(null, TAG_DATA).endTag(null, TYPE_ARRAY);
- } else
- if (object instanceof Object[]) {
- serializer.startTag(null, TYPE_ARRAY).startTag(null, TAG_DATA);
- Object[] objects = (Object[]) object;
- for (int i=0; i map = (Map) object;
- Iterator> iter = map.entrySet().iterator();
- while (iter.hasNext()) {
- Entry entry = iter.next();
- String key = entry.getKey();
- Object value = entry.getValue();
-
- serializer.startTag(null, TAG_MEMBER);
- serializer.startTag(null, TAG_NAME).text(key).endTag(null, TAG_NAME);
- serializer.startTag(null, TAG_VALUE);
- serialize(serializer, value);
- serializer.endTag(null, TAG_VALUE);
- serializer.endTag(null, TAG_MEMBER);
- }
- serializer.endTag(null, TYPE_STRUCT);
- } else {
- throw new IOException("Cannot serialize " + object);
- }
- }
-
- static Object deserialize(XmlPullParser parser) throws XmlPullParserException, IOException {
- parser.require(XmlPullParser.START_TAG, null, TAG_VALUE);
-
- parser.nextTag();
- String typeNodeName = parser.getName();
-
- Object obj;
- if (typeNodeName.equals(TYPE_INT) || typeNodeName.equals(TYPE_I4)) {
- String value = parser.nextText();
- obj = Integer.parseInt(value);
- } else
- if (typeNodeName.equals(TYPE_I8)) {
- String value = parser.nextText();
- obj = Long.parseLong(value);
- } else
- if (typeNodeName.equals(TYPE_DOUBLE)) {
- String value = parser.nextText();
- obj = Double.parseDouble(value);
- } else
- if (typeNodeName.equals(TYPE_BOOLEAN)) {
- String value = parser.nextText();
- obj = value.equals("1") ? Boolean.TRUE : Boolean.FALSE;
- } else
- if (typeNodeName.equals(TYPE_STRING)) {
- obj = parser.nextText();
- } else
- if (typeNodeName.equals(TYPE_DATE_TIME_ISO8601)) {
- String value = parser.nextText();
- try {
- obj = dateFormat.parseObject(value);
- } catch (ParseException e) {
- throw new IOException("Cannot deserialize dateTime " + value);
- }
- } else
- if (typeNodeName.equals(TYPE_BASE64)) {
- String value = parser.nextText();
- BufferedReader reader = new BufferedReader(new StringReader(value));
- String line;
- StringBuffer sb = new StringBuffer();
- while ((line = reader.readLine()) != null) {
- sb.append(line);
- }
- obj = Base64Coder.decode(sb.toString());
- } else
- if (typeNodeName.equals(TYPE_ARRAY)) {
- parser.nextTag(); // TAG_DATA ()
- parser.require(XmlPullParser.START_TAG, null, TAG_DATA);
-
- parser.nextTag();
- List list = new ArrayList();
- while (parser.getName().equals(TAG_VALUE)) {
- list.add(deserialize(parser));
- parser.nextTag();
- }
- parser.require(XmlPullParser.END_TAG, null, TAG_DATA);
- parser.nextTag(); // TAG_ARRAY ()
- parser.require(XmlPullParser.END_TAG, null, TYPE_ARRAY);
- obj = list.toArray();
- } else
- if (typeNodeName.equals(TYPE_STRUCT)) {
- parser.nextTag();
- Map map = new HashMap();
- while (parser.getName().equals(TAG_MEMBER)) {
- String memberName = null;
- Object memberValue = null;
- while (true) {
- parser.nextTag();
- String name = parser.getName();
- if (name.equals(TAG_NAME)) {
- memberName = parser.nextText();
- } else
- if (name.equals(TAG_VALUE)) {
- memberValue = deserialize(parser);
- } else {
- break;
- }
- }
- if (memberName != null && memberValue != null) {
- map.put(memberName, memberValue);
- }
- parser.require(XmlPullParser.END_TAG, null, TAG_MEMBER);
- parser.nextTag();
- }
- parser.require(XmlPullParser.END_TAG, null, TYPE_STRUCT);
- obj = map;
- } else {
- throw new IOException("Cannot deserialize " + parser.getName());
- }
- parser.nextTag(); // TAG_VALUE ()
- parser.require(XmlPullParser.END_TAG, null, TAG_VALUE);
- return obj;
- }
-}