diff --git a/OSJ Forum/src/org/xmlrpc/android/Base64Coder.java b/OSJ Forum/src/org/xmlrpc/android/Base64Coder.java new file mode 100644 index 0000000..f9a4807 --- /dev/null +++ b/OSJ Forum/src/org/xmlrpc/android/Base64Coder.java @@ -0,0 +1,199 @@ +package org.xmlrpc.android; + +/** + * A Base64 Encoder/Decoder. + * + *

+ * This class is used to encode and decode data in Base64 format as described in + * RFC 1521. + * + *

+ * This is "Open Source" software and released under the GNU/LGPL license.
+ * It is provided "as is" without warranty of any kind.
+ * Copyright 2003: Christian d'Heureuse, Inventec Informatik AG, Switzerland.
+ * Home page: www.source-code.biz
+ * + *

+ * Version history:
+ * 2003-07-22 Christian d'Heureuse (chdh): Module created.
+ * 2005-08-11 chdh: Lincense changed from GPL to LGPL.
+ * 2006-11-21 chdh:
+ *   Method encode(String) renamed to encodeString(String).
+ *   Method decode(String) renamed to decodeString(String).
+ *   New method encode(byte[],int) added.
+ *   New method decode(String) added.
+ */ + +class Base64Coder { + + // Mapping table from 6-bit nibbles to Base64 characters. + private static char[] map1 = new char[64]; + static { + int i = 0; + for (char c = 'A'; c <= 'Z'; c++) { + map1[i++] = c; + } + for (char c = 'a'; c <= 'z'; c++) { + map1[i++] = c; + } + for (char c = '0'; c <= '9'; c++) { + map1[i++] = c; + } + map1[i++] = '+'; + map1[i++] = '/'; + } + + // Mapping table from Base64 characters to 6-bit nibbles. + private static byte[] map2 = new byte[128]; + static { + for (int i = 0; i < map2.length; i++) { + map2[i] = -1; + } + for (int i = 0; i < 64; i++) { + map2[map1[i]] = (byte) i; + } + } + + /** + * Encodes a string into Base64 format. No blanks or line breaks are + * inserted. + * + * @param s + * a String to be encoded. + * @return A String with the Base64 encoded data. + */ + static String encodeString(String s) { + return new String(encode(s.getBytes())); + } + + /** + * Encodes a byte array into Base64 format. No blanks or line breaks are + * inserted. + * + * @param in + * an array containing the data bytes to be encoded. + * @return A character array with the Base64 encoded data. + */ + static char[] encode(byte[] in) { + return encode(in, in.length); + } + + /** + * Encodes a byte array into Base64 format. No blanks or line breaks are + * inserted. + * + * @param in + * an array containing the data bytes to be encoded. + * @param iLen + * number of bytes to process in in. + * @return A character array with the Base64 encoded data. + */ + static char[] encode(byte[] in, int iLen) { + int oDataLen = (iLen * 4 + 2) / 3; // output length without padding + int oLen = ((iLen + 2) / 3) * 4; // output length including padding + char[] out = new char[oLen]; + int ip = 0; + int op = 0; + while (ip < iLen) { + int i0 = in[ip++] & 0xff; + int i1 = ip < iLen ? in[ip++] & 0xff : 0; + int i2 = ip < iLen ? in[ip++] & 0xff : 0; + int o0 = i0 >>> 2; + int o1 = ((i0 & 3) << 4) | (i1 >>> 4); + int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); + int o3 = i2 & 0x3F; + out[op++] = map1[o0]; + out[op++] = map1[o1]; + out[op] = op < oDataLen ? map1[o2] : '='; + op++; + out[op] = op < oDataLen ? map1[o3] : '='; + op++; + } + return out; + } + + /** + * Decodes a string from Base64 format. + * + * @param s + * a Base64 String to be decoded. + * @return A String containing the decoded data. + * @throws IllegalArgumentException + * if the input is not valid Base64 encoded data. + */ + static String decodeString(String s) { + return new String(decode(s)); + } + + /** + * Decodes a byte array from Base64 format. + * + * @param s + * a Base64 String to be decoded. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException + * if the input is not valid Base64 encoded data. + */ + static byte[] decode(String s) { + return decode(s.toCharArray()); + } + + /** + * Decodes a byte array from Base64 format. No blanks or line breaks are + * allowed within the Base64 encoded data. + * + * @param in + * a character array containing the Base64 encoded data. + * @return An array containing the decoded data bytes. + * @throws IllegalArgumentException + * if the input is not valid Base64 encoded data. + */ + static byte[] decode(char[] in) { + int iLen = in.length; + if (iLen % 4 != 0) { + throw new IllegalArgumentException( + "Length of Base64 encoded input string is not a multiple of 4."); + } + while (iLen > 0 && in[iLen - 1] == '=') { + iLen--; + } + int oLen = (iLen * 3) / 4; + byte[] out = new byte[oLen]; + int ip = 0; + int op = 0; + while (ip < iLen) { + int i0 = in[ip++]; + int i1 = in[ip++]; + int i2 = ip < iLen ? in[ip++] : 'A'; + int i3 = ip < iLen ? in[ip++] : 'A'; + if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) { + throw new IllegalArgumentException( + "Illegal character in Base64 encoded data."); + } + int b0 = map2[i0]; + int b1 = map2[i1]; + int b2 = map2[i2]; + int b3 = map2[i3]; + if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) { + throw new IllegalArgumentException( + "Illegal character in Base64 encoded data."); + } + int o0 = (b0 << 2) | (b1 >>> 4); + int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2); + int o2 = ((b2 & 3) << 6) | b3; + out[op++] = (byte) o0; + if (op < oLen) { + out[op++] = (byte) o1; + } + if (op < oLen) { + out[op++] = (byte) o2; + } + } + return out; + } + + // Dummy constructor. + private Base64Coder() { + } +} diff --git a/OSJ Forum/src/org/xmlrpc/android/IXMLRPCSerializer.java b/OSJ Forum/src/org/xmlrpc/android/IXMLRPCSerializer.java new file mode 100644 index 0000000..f651e46 --- /dev/null +++ b/OSJ Forum/src/org/xmlrpc/android/IXMLRPCSerializer.java @@ -0,0 +1,32 @@ +package org.xmlrpc.android; + +import java.io.IOException; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +public interface IXMLRPCSerializer { + String TAG_NAME = "name"; + String TAG_MEMBER = "member"; + String TAG_VALUE = "value"; + String TAG_DATA = "data"; + + String TYPE_INT = "int"; + String TYPE_I4 = "i4"; + String TYPE_I8 = "i8"; + String TYPE_DOUBLE = "double"; + String TYPE_BOOLEAN = "boolean"; + String TYPE_STRING = "string"; + String TYPE_DATE_TIME_ISO8601 = "dateTime.iso8601"; + String TYPE_BASE64 = "base64"; + String TYPE_ARRAY = "array"; + String TYPE_STRUCT = "struct"; + // This added by mattias.ellback as part of issue #19 + String TYPE_NULL = "nil"; + + String DATETIME_FORMAT = "yyyyMMdd'T'HH:mm:ss"; + + void serialize(XmlSerializer serializer, Object object) throws IOException; + Object deserialize(XmlPullParser parser) throws XmlPullParserException, IOException; +} diff --git a/OSJ Forum/src/org/xmlrpc/android/MethodCall.java b/OSJ Forum/src/org/xmlrpc/android/MethodCall.java new file mode 100644 index 0000000..d573d5c --- /dev/null +++ b/OSJ Forum/src/org/xmlrpc/android/MethodCall.java @@ -0,0 +1,20 @@ +package org.xmlrpc.android; + +import java.util.ArrayList; + +public class MethodCall { + + private static final int TOPIC = 1; + String methodName; + ArrayList params = new ArrayList(); + + public String getMethodName() { return methodName; } + void setMethodName(String methodName) { this.methodName = methodName; } + + public ArrayList getParams() { return params; } + void setParams(ArrayList params) { this.params = params; } + + public String getTopic() { + return (String)params.get(TOPIC); + } +} diff --git a/OSJ Forum/src/org/xmlrpc/android/Tag.java b/OSJ Forum/src/org/xmlrpc/android/Tag.java new file mode 100644 index 0000000..0405843 --- /dev/null +++ b/OSJ Forum/src/org/xmlrpc/android/Tag.java @@ -0,0 +1,13 @@ +package org.xmlrpc.android; + +public class Tag { + static final String LOG = "XMLRPC"; + static final String METHOD_CALL = "methodCall"; + static final String METHOD_NAME = "methodName"; + static final String METHOD_RESPONSE = "methodResponse"; + static final String PARAMS = "params"; + static final String PARAM = "param"; + static final String FAULT = "fault"; + static final String FAULT_CODE = "faultCode"; + static final String FAULT_STRING = "faultString"; +} \ No newline at end of file diff --git a/OSJ Forum/src/org/xmlrpc/android/XMLRPCClient.java b/OSJ Forum/src/org/xmlrpc/android/XMLRPCClient.java new file mode 100644 index 0000000..f18670b --- /dev/null +++ b/OSJ Forum/src/org/xmlrpc/android/XMLRPCClient.java @@ -0,0 +1,423 @@ +package org.xmlrpc.android; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringWriter; +import java.net.URI; +import java.net.URL; +import java.util.Map; +import java.util.Vector; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.scheme.PlainSocketFactory; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; +import org.apache.http.params.HttpParams; +import org.apache.http.params.HttpProtocolParams; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserFactory; + +/** + * XMLRPCClient allows to call remote XMLRPC method. + * + *

+ * The following table shows how XML-RPC types are mapped to java call parameters/response values. + *

+ * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
XML-RPC TypeCall ParametersCall Response
int, i4byte
Byte
short
Short
int
Integer
int
Integer
i8long
Long
long
Long
doublefloat
Float
double
Double
double
Double
stringStringString
booleanboolean
Boolean
boolean
Boolean
dateTime.iso8601java.util.Date
java.util.Calendar
java.util.Date
base64byte[]byte[]
arrayjava.util.List<Object>
Object[]
Object[]
structjava.util.Map<String, Object>java.util.Map<String, Object>
+ *

+ *

+ * You can also pass as a parameter any object implementing XMLRPCSerializable interface. In this + * case your object overrides getSerializable() telling how to serialize to XMLRPC protocol + *

+ */ + +public class XMLRPCClient extends XMLRPCCommon { + private HttpClient client; + private HttpPost postMethod; + private HttpParams httpParams; + // These variables used in the code inspired by erickok in issue #6 + private boolean httpPreAuth = false; + private String username = ""; + private String password = ""; + + /** + * XMLRPCClient constructor. Creates new instance based on server URI + * (Code contributed by sgayda2 from issue #17, and by erickok from ticket #10) + * + * @param XMLRPC server URI + */ + public XMLRPCClient(URI uri) { + SchemeRegistry registry = new SchemeRegistry(); + registry.register(new Scheme("http", new PlainSocketFactory(), 80)); + registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443)); + + 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 = new DefaultHttpClient(new ThreadSafeClientConnManager(httpParams, registry), httpParams); + } + + /** + * XMLRPCClient constructor. Creates new instance based on server URI + * (Code contributed by sgayda2 from issue #17) + * + * @param XMLRPC server URI + * @param HttpClient to use + */ + + public XMLRPCClient(URI uri, HttpClient client) { + 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; + } + + /** + * Amends user agent + * (Code contributed by mortenholdflod from issue #28) + * + * @param userAgent defining the new User Agent string + */ + public void setUserAgent(String userAgent) { + postMethod.removeHeaders("User-Agent"); + postMethod.addHeader("User-Agent", userAgent); + } + + /** + * Convenience constructor. Creates new instance based on server String address + * @param XMLRPC server address + */ + public XMLRPCClient(String url) { + this(URI.create(url)); + } + + /** + * Convenience constructor. Creates new instance based on server String address + * @param XMLRPC server address + * @param HttpClient to use + */ + public XMLRPCClient(String url, HttpClient client) { + this(URI.create(url), client); + } + + + /** + * Convenience XMLRPCClient constructor. Creates new instance based on server URL + * @param XMLRPC server URL + */ + public XMLRPCClient(URL url) { + this(URI.create(url.toExternalForm())); + } + + /** + * Convenience XMLRPCClient constructor. Creates new instance based on server URL + * @param XMLRPC server URL + * @param HttpClient to use + */ + public XMLRPCClient(URL url, HttpClient client) { + this(URI.create(url.toExternalForm()), client); + } + + /** + * Convenience constructor. Creates new instance based on server String address + * @param XMLRPC server address + * @param HTTP Server - Basic Authentication - Username + * @param HTTP Server - Basic Authentication - Password + */ + public XMLRPCClient(URI uri, String username, String password) { + this(uri); + + ((DefaultHttpClient) client).getCredentialsProvider().setCredentials( + new AuthScope(uri.getHost(), uri.getPort(),AuthScope.ANY_REALM), + new UsernamePasswordCredentials(username, password)); + } + + /** + * Convenience constructor. Creates new instance based on server String address + * @param XMLRPC server address + * @param HTTP Server - Basic Authentication - Username + * @param HTTP Server - Basic Authentication - Password + * @param HttpClient to use + */ + public XMLRPCClient(URI uri, String username, String password, HttpClient client) { + this(uri, client); + + ((DefaultHttpClient) this.client).getCredentialsProvider().setCredentials( + new AuthScope(uri.getHost(), uri.getPort(),AuthScope.ANY_REALM), + new UsernamePasswordCredentials(username, password)); + } + + /** + * Convenience constructor. Creates new instance based on server String address + * @param XMLRPC server address + * @param HTTP Server - Basic Authentication - Username + * @param HTTP Server - Basic Authentication - Password + */ + public XMLRPCClient(String url, String username, String password) { + this(URI.create(url), username, password); + } + + /** + * Convenience constructor. Creates new instance based on server String address + * @param XMLRPC server address + * @param HTTP Server - Basic Authentication - Username + * @param HTTP Server - Basic Authentication - Password + * @param HttpClient to use + */ + public XMLRPCClient(String url, String username, String password, HttpClient client) { + this(URI.create(url), username, password, client); + } + + /** + * Convenience constructor. Creates new instance based on server String address + * @param XMLRPC server url + * @param HTTP Server - Basic Authentication - Username + * @param HTTP Server - Basic Authentication - Password + */ + public XMLRPCClient(URL url, String username, String password) { + this(URI.create(url.toExternalForm()), username, password); + } + + /** + * Convenience constructor. Creates new instance based on server String address + * @param XMLRPC server url + * @param HTTP Server - Basic Authentication - Username + * @param HTTP Server - Basic Authentication - Password + * @param HttpClient to use + */ + public XMLRPCClient(URL url, String username, String password, HttpClient client) { + this(URI.create(url.toExternalForm()), username, password, client); + } + + /** + * Sets basic authentication on web request using plain credentials + * @param username The plain text username + * @param password The plain text password + * @param doPreemptiveAuth Select here whether to authenticate without it being requested first by the server. + */ + public void setBasicAuthentication(String username, String password, boolean doPreemptiveAuth) { + // This code required to trigger the patch created by erickok in issue #6 + if(doPreemptiveAuth = true) { + this.httpPreAuth = doPreemptiveAuth; + this.username = username; + this.password = password; + } else { + ((DefaultHttpClient) client).getCredentialsProvider().setCredentials(new AuthScope(postMethod.getURI().getHost(), postMethod.getURI().getPort(), AuthScope.ANY_REALM), new UsernamePasswordCredentials(username, password)); + } + } + + /** + * Convenience Constructor: Sets basic authentication on web request using plain credentials + * @param username The plain text username + * @param password The plain text password + */ + public void setBasicAuthentication(String username, String password) { + setBasicAuthentication(username, password, false); + } + + /** + * 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 + */ + @SuppressWarnings("unchecked") + public Object callEx(String method, Object[] params) throws XMLRPCException { + try { + // prepare POST body + String body = methodCall(method, params); + + // set POST body + HttpEntity entity = new StringEntity(body); + postMethod.setEntity(entity); + + // This code slightly tweaked from the code by erickok in issue #6 + // Force preemptive authentication + // This makes sure there is an 'Authentication: ' header being send before trying and failing and retrying + // by the basic authentication mechanism of DefaultHttpClient + if(this.httpPreAuth == true) { + String auth = this.username + ":" + this.password; + postMethod.addHeader("Authorization", "Basic " + Base64Coder.encode(auth.getBytes()).toString()); + } + + //Log.d(Tag.LOG, "ros HTTP POST"); + // execute HTTP POST request + HttpResponse response = client.execute(postMethod); + //Log.d(Tag.LOG, "ros HTTP POSTed"); + + // check status code + int statusCode = response.getStatusLine().getStatusCode(); + //Log.d(Tag.LOG, "ros status code:" + statusCode); + if (statusCode != HttpStatus.SC_OK) { + throw new XMLRPCException("HTTP status code: " + statusCode + " != " + HttpStatus.SC_OK, statusCode); + } + + // parse response stuff + // + // setup pull parser + XmlPullParser pullParser = XmlPullParserFactory.newInstance().newPullParser(); + entity = response.getEntity(); + Reader reader = new InputStreamReader(new BufferedInputStream(entity.getContent())); +// for testing purposes only +// reader = new StringReader("\n\n\n"); + pullParser.setInput(reader); + + // lets start pulling... + pullParser.nextTag(); + pullParser.require(XmlPullParser.START_TAG, null, Tag.METHOD_RESPONSE); + + pullParser.nextTag(); // either Tag.PARAMS () 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 = iXMLRPCSerializer.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) iXMLRPCSerializer.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) { + e.printStackTrace(); + // wrap any other Exception(s) around XMLRPCException + throw new XMLRPCException(e); + } + } + + private String methodCall(String method, Object[] params) + throws IllegalArgumentException, IllegalStateException, IOException { + 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); + + serializeParams(params); + + serializer.endTag(null, Tag.METHOD_CALL); + serializer.endDocument(); + + return bodyWriter.toString(); + } + + /** + * 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 callEx(method, null); + } + + /** + * Convenience method call with a vectorized parameter + * (Code contributed by jahbromo from issue #14) + * @param method name of method to call + * @param paramsv vector of method's parameter + * @return deserialized method return value + * @throws XMLRPCException + */ + + public Object call(String method, Vector paramsv) throws XMLRPCException { + Object[] params = new Object [paramsv.size()]; + for (int i=0; i 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 + if (object instanceof XMLRPCSerializable) { + XMLRPCSerializable serializable = (XMLRPCSerializable) object; + serialize(serializer, serializable.getSerializable()); + } else { + throw new IOException("Cannot serialize " + object); + } + } + + public Object deserialize(XmlPullParser parser) throws XmlPullParserException, IOException { + parser.require(XmlPullParser.START_TAG, null, TAG_VALUE); + + if (parser.isEmptyElementTag()) { + // degenerated , return empty string + return ""; + } + + Object obj; + boolean hasType = true; + String typeNodeName = null; + try { + parser.nextTag(); + typeNodeName = parser.getName(); + if (typeNodeName.equals(TAG_VALUE) && parser.getEventType() == XmlPullParser.END_TAG) { + // empty , return empty string + return ""; + } + + + } catch (XmlPullParserException e) { + hasType = false; + } + if (hasType) { + // This code submitted by mattias.ellback in issue #19 + if (typeNodeName.equals(TYPE_NULL)){ + parser.nextTag(); + obj = null; + } + else + 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()); + } + } else { + // TYPE_STRING () is not required + obj = parser.getText(); + } + parser.nextTag(); // TAG_VALUE () + parser.require(XmlPullParser.END_TAG, null, TAG_VALUE); + return obj; + } +} diff --git a/OSJ Forum/src/org/xmlrpc/android/XMLRPCServer.java b/OSJ Forum/src/org/xmlrpc/android/XMLRPCServer.java new file mode 100644 index 0000000..7db8c49 --- /dev/null +++ b/OSJ Forum/src/org/xmlrpc/android/XMLRPCServer.java @@ -0,0 +1,103 @@ +package org.xmlrpc.android; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringWriter; +import java.net.Socket; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import android.util.Log; + +public class XMLRPCServer extends XMLRPCCommon { + + private static final String CRLF = "\r\n"; + private static final String RESPONSE = + "HTTP/1.1 200 OK" + CRLF + + "Connection: close" + CRLF + + "Content-Type: text/xml" + CRLF + + "Content-Length: "; + private static final String NEWLINES = CRLF + CRLF; + private XMLRPCSerializer iXMLRPCSerializer; + + public XMLRPCServer() { + iXMLRPCSerializer = new XMLRPCSerializer(); + } + + public MethodCall readMethodCall(Socket socket) throws IOException, XmlPullParserException + { + MethodCall methodCall = new MethodCall(); + InputStream inputStream = socket.getInputStream(); + + XmlPullParser pullParser = xmlPullParserFromSocket(inputStream); + + pullParser.nextTag(); + pullParser.require(XmlPullParser.START_TAG, null, Tag.METHOD_CALL); + pullParser.nextTag(); + pullParser.require(XmlPullParser.START_TAG, null, Tag.METHOD_NAME); + + methodCall.setMethodName(pullParser.nextText()); + + pullParser.nextTag(); + pullParser.require(XmlPullParser.START_TAG, null, Tag.PARAMS); + pullParser.nextTag(); // possible optional or + + while (pullParser.getName().equals(Tag.PARAM)) { // + //Log.d(Tag.LOG, "type=" + pullParser.getEventType() + ", tag=" + pullParser.getName()); + pullParser.require(XmlPullParser.START_TAG, null, Tag.PARAM); + pullParser.nextTag(); // + + Object param = iXMLRPCSerializer.deserialize(pullParser); + methodCall.params.add(param); // add to return value + + pullParser.nextTag(); + pullParser.require(XmlPullParser.END_TAG, null, Tag.PARAM); + pullParser.nextTag(); // or + } + + return methodCall; + } + + XmlPullParser xmlPullParserFromSocket(InputStream socketInputStream) throws IOException, XmlPullParserException { + String line; + BufferedReader br = new BufferedReader(new InputStreamReader(socketInputStream)); + while ((line = br.readLine()) != null && line.length() > 0); // eat the HTTP POST headers + + XmlPullParser pullParser = XmlPullParserFactory.newInstance().newPullParser(); + pullParser.setInput(br); + return pullParser; + } + + public void respond(Socket socket, Object value) throws IOException { + + String content = methodResponse(value); + String response = RESPONSE + (content.length()) + NEWLINES + content; + OutputStream outputStream = socket.getOutputStream(); + outputStream.write(response.getBytes()); + outputStream.flush(); + outputStream.close(); + socket.close(); + Log.d(Tag.LOG, "response:" + response); + } + + private String methodResponse(Object value) throws IllegalArgumentException, IllegalStateException, IOException { + StringWriter bodyWriter = new StringWriter(); + serializer.setOutput(bodyWriter); + serializer.startDocument(null, null); + serializer.startTag(null, Tag.METHOD_RESPONSE); + + serializeParams(value); + + serializer.endTag(null, Tag.METHOD_RESPONSE); + serializer.endDocument(); + + return bodyWriter.toString(); + } +} \ No newline at end of file