Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Xml;
- using System.IO;
- using System.Text;
- using System.Net;
- using UnityEngine;
- public class RPCmin_XML
- {
- public class RPCFault
- { // a simple struct with a code and a string. All errors will be given to you as one of these
- public int FaultCode = 400; // if there's a better "generic fault", let me know
- public string FaultString;
- public RPCFault(string faultString = "") { FaultString = faultString; }
- };
- public string URL;
- public RPCmin_XML(string _URL = null)
- { // hang on to the URL. If we make several calls, it'll likely be to the same URL
- URL = _URL;
- }
- // the following calls are Unity-specific. If you're using generic C#, use the blocking calls or roll your own threaded version
- public static MonoBehaviour MBHook { protected get; set; } = null; // coroutines require a monobehavior, so we'll hook into an existing one. Set this to a monobehavior (preferably a singleton) for the coroutine
- public bool Call(MonoBehaviour mBHook, string method, object[] parms = null, Action<object> fnSuccess = null, Action<RPCFault> fnFault = null)
- {
- MBHook = mBHook;
- return Call(method, parms, fnSuccess, fnFault);
- }
- public bool Call(string method, object[] parms = null, Action<object> fnSuccess = null, Action<RPCFault> fnFault = null)
- { // non-blocking call that returns immediately and returns success or fault as actions. Returns true if call was sent
- bool RetVal = false;
- if ((URL != null) && (MBHook != null))
- { // build the request
- var Form = new WWWForm();
- var Headers = Form.headers;
- Headers["Content-Type"] = "text/xml;charset=UTF-8";
- string Request = EncodeXMLRPCRequest(method, parms);
- // Debug.Log(Request);
- var wwwRequest = new WWW(URL, Encoding.UTF8.GetBytes(Request), Headers);
- MBHook.StartCoroutine(WaitForRequest(wwwRequest, fnSuccess, fnFault)); // Start a coroutine which will wait until our servers comes back
- RetVal = true;
- }
- return RetVal;
- }
- static public bool RPCXMLCall(string _URL, MonoBehaviour mBHook, string method, object[] parms = null, Action<object> fnSuccess = null, Action<RPCFault> fnFault = null)
- { // if you're just making one call, do this. It'll make the call, return from whence it came, and then disappear
- return new RPCmin_XML(_URL).Call(mBHook, method, parms, fnSuccess, fnFault);
- }
- static public bool RPCXMLCall(string _URL, string method, object[] parms = null, Action<object> fnSuccess = null, Action<RPCFault> fnFault = null)
- { // if you're just making one call, do this. It'll make the call, return from whence it came, and then disappear
- return new RPCmin_XML(_URL).Call(method, parms, fnSuccess, fnFault);
- }
- protected IEnumerator WaitForRequest(WWW wwwResult, Action<object> fnSuccess, Action<RPCFault> fnFault)
- { // coroutine that yields until a result appears. Might want to add a timeout here eventually.
- yield return wwwResult;
- if (String.IsNullOrEmpty(wwwResult.error))
- { // parse the XML and see if there's a fault
- string HTMLResult = Encoding.UTF8.GetString(wwwResult.bytes);
- object Ob = DecodeXMLRPCResult(HTMLResult);
- if (Ob == null)
- fnFault.Invoke(new RPCFault("Could not parse " + HTMLResult)); // not something parse-able
- else if (Ob.GetType().ToString().Contains("RPCFault"))
- fnFault.Invoke((RPCFault)Ob); // RPC didn't run, most likely passed it bogus info
- else
- fnSuccess.Invoke(Ob); // returned a non-fault object, which is good
- }
- else
- { // we got some error text from the http server
- RPCFault Fault = new RPCFault(wwwResult.error);
- if ((wwwResult.responseHeaders != null) && wwwResult.responseHeaders.ContainsKey("STATUS"))
- { // getting the numeric fault code isn't easy
- string[] StatusStrs = wwwResult.responseHeaders["STATUS"].Split(' ');
- int StatusInt;
- if ((StatusStrs.Length >= 3) && int.TryParse(StatusStrs[1], out StatusInt))
- Fault.FaultCode = StatusInt; // the second value was a number, so assumme it's the fault code
- }
- fnFault.Invoke(Fault);
- }
- }
- // End of the Unity-specific stuff
- public static RPCFault Fault(object Ob)
- { // pass the result of the Call() below here, it will return a RPCFault if it's a fault, or null if not
- return Ob.GetType().Equals(typeof(RPCFault)) ? (RPCFault)Ob : null;
- }
- public object Call(string method, object[] parms = null)
- { // returns an object that you can cast to the results you expect, null if there is no URL, or a RPCmin_XML.RPCFault if there is a fault
- object RetVal = null;
- if (URL != null)
- { // build the request
- HttpWebRequest HTTPRequest = (HttpWebRequest)WebRequest.Create(URL);
- HTTPRequest.Method = "POST";
- HTTPRequest.ContentType = "text/xml";
- // Add the request stream.
- Stream RequestStream = HTTPRequest.GetRequestStream();
- string Request = EncodeXMLRPCRequest(method, parms);
- // Debug.Log(Request);
- byte[] Bytes = Encoding.UTF8.GetBytes(Request);
- RequestStream.Write(Bytes, 0, Bytes.Length);
- RequestStream.Close();
- HttpWebResponse HTTPResponse = (HttpWebResponse)HTTPRequest.GetResponse(); // Now send it off
- // And we're (hopefully) back. Get the response as a stream with the required encoding format.
- StreamReader ReadStream = new StreamReader(HTTPResponse.GetResponseStream(), Encoding.UTF8);
- RetVal = DecodeXMLRPCResult(ReadStream.ReadToEnd()); // now parse the received XML back into an object
- ReadStream.Close();
- HTTPResponse.Close();
- }
- return RetVal;
- }
- // End of the public interface
- protected const string XMLRPC_STRING = "string"; // string
- protected const string XMLRPC_INT = "int"; // int
- protected const string XMLRPC_I4 = "i4"; // int
- protected const string XMLRPC_DOUBLE = "double"; // double, float
- protected const string XMLRPC_BOOLEAN = "boolean"; // bool
- protected const string XMLRPC_ARRAY = "array"; // Array
- protected const string XMLRPC_STRUCT = "struct"; // Hashtable
- protected const string XMLRPC_DATE = "dateTime.iso8601"; // DateTimeOffset (preferred) or DateTime
- protected const string XMLRPC_BASE64 = "base64"; // byte[]
- protected string XMLRPCTypeOf(object obValue)
- { // converts a C# type to its equivalent in XMLRPC
- Type ObType = obValue.GetType();
- if (ObType.Equals(typeof(int)) || ObType.Equals(typeof(uint)) || ObType.Equals(typeof(short)) || ObType.Equals(typeof(ushort)))
- return XMLRPC_INT; // you can send all kinds of ints, but only an standard 32-bit signed int comes back
- else if (ObType.Equals(typeof(double)) || ObType.Equals(typeof(float)))
- return XMLRPC_DOUBLE; // you can send floats or doubles, but only a double comes back
- else if (ObType.Equals(typeof(bool)))
- return XMLRPC_BOOLEAN;
- else if (ObType.Equals(typeof(Hashtable)))
- return XMLRPC_STRUCT;
- else if (ObType.Equals(typeof(DateTime)) || ObType.Equals(typeof(DateTimeOffset)))
- return XMLRPC_DATE; // you can pass a DateTime or a DateTimeOffset. A DateTimeOffset comes back
- else if (ObType.Equals(typeof(byte[])))
- return XMLRPC_BASE64; // It converts to Base64 and back, so you'll get a byte[] back
- else if (ObType.IsArray)
- return XMLRPC_ARRAY;
- else // everything else is a string, per the spec
- return XMLRPC_STRING;
- }
- protected string EncodeXMLRPCValue(object obValue)
- { // figures out what an object is, converts it to a string, and returns it bracketed with <value><type>
- String RetVal = null;
- string TypeStr = XMLRPCTypeOf(obValue);
- switch (TypeStr)
- {
- case XMLRPC_INT:
- RetVal = ((int)obValue).ToString();
- break;
- case XMLRPC_DOUBLE:
- RetVal = ((double)obValue).ToString();
- break;
- case XMLRPC_BOOLEAN:
- RetVal = (((bool)obValue) ? "1" : "0");
- break;
- case XMLRPC_ARRAY:
- {
- StringBuilder S = new StringBuilder();
- foreach (object Ob in (Array)obValue)
- {
- string Node = EncodeXMLRPCValue(Ob);
- if (Node != null)
- S.AppendLine(Node);
- }
- if (S.Length > 0) // we encoded something
- RetVal = "<data>" + S.ToString() + "</data>";
- }
- break;
- case XMLRPC_STRUCT:
- {
- StringBuilder S = new StringBuilder();
- foreach (string Key in ((Hashtable)obValue).Keys)
- {
- string Node = EncodeXMLRPCValue(((Hashtable)obValue)[Key]);
- if (Node != null)
- { // this was a good value, so save the key and value
- S.AppendLine("<member>");
- S.AppendLine("<name>" + Key + "</name>");
- S.AppendLine(Node);
- S.AppendLine("</member>");
- }
- }
- if (S.Length > 0) // we encoded something
- RetVal = S.ToString();
- }
- break;
- case XMLRPC_DATE:
- {
- DateTimeOffset D;
- if (obValue.GetType().ToString() == "System.DateTime")
- D = new DateTimeOffset((DateTime)obValue);
- else
- D = (DateTimeOffset)obValue;
- RetVal = D.ToString("o");
- }
- break;
- case XMLRPC_BASE64:
- RetVal = Convert.ToBase64String((byte[])obValue);
- break;
- default: // assume string
- TypeStr = XMLRPC_STRING;
- RetVal = ((string)obValue).ToString();
- break;
- }
- return String.IsNullOrEmpty(RetVal) ? null : "<value><" + TypeStr + ">" + RetVal.ToString() + "</" + TypeStr + "></value>";
- }
- protected string EncodeXMLRPCRequest(string method, object[] parms)
- { // returns an XMLRPC-formatted object to send to a server
- StringBuilder XMLRPC = new StringBuilder();
- XMLRPC.AppendLine("<?xml version = \"1.0\" ?>");
- XMLRPC.AppendLine("<methodCall>");
- XMLRPC.AppendLine("<methodName>" + method + "</methodName>");
- if (parms != null)
- {
- StringBuilder S = new StringBuilder();
- foreach (object Item in parms)
- {
- string ObEncoded = EncodeXMLRPCValue(Item);
- if (ObEncoded != null)
- S.AppendLine("<param>" + ObEncoded + "</param>");
- }
- if (S.Length > 0)
- XMLRPC.AppendLine("<params>" + S.ToString() + "</params>");
- }
- XMLRPC.AppendLine("</methodCall>");
- return XMLRPC.ToString();
- }
- protected static readonly string[] Format8601 = new string[] { "s", "u", "o" }; // permissive date parsing, as we don't know what the server thinks is standard
- protected object DecodeXMLRPCValue(XmlNode Node)
- { // converts a single XML <type> string to an object
- object RetVal = null;
- XmlNode ValueNode = Node.FirstChild;
- switch (ValueNode.LocalName)
- {
- case XMLRPC_INT:
- case XMLRPC_I4:
- case XMLRPC_BOOLEAN:
- {
- int Val = 0;
- int.TryParse(ValueNode.InnerText, out Val);
- if (ValueNode.LocalName == XMLRPC_BOOLEAN)
- RetVal = (Val == 0) ? false : true;
- else
- RetVal = Val;
- }
- break;
- case XMLRPC_STRING:
- RetVal = ValueNode.InnerText;
- break;
- case XMLRPC_DOUBLE:
- {
- double Val = 0.0;
- double.TryParse(ValueNode.InnerText, out Val);
- RetVal = Val;
- }
- break;
- case XMLRPC_ARRAY:
- {
- List<object> ObArr = new List<object>();
- foreach (XmlNode ArrayNode in ValueNode.FirstChild) // there's a single <data> member, so get around it
- {
- object ObArrayElement = DecodeXMLRPCValue(ArrayNode);
- if (ObArrayElement != null)
- ObArr.Add(ObArrayElement);
- }
- if (ObArr.Count > 0)
- RetVal = ObArr.ToArray(); // there's an array with items, so return it
- }
- break;
- case XMLRPC_STRUCT:
- {
- Hashtable H = new Hashtable();
- foreach (XmlNode StructNode in ValueNode) // loop through the <member> members
- {
- string Key = null;
- object Value = null;
- XmlNode StructNameNode = StructNode.SelectSingleNode("name");
- if (StructNameNode != null)
- Key = StructNameNode.InnerText;
- XmlNode StructValueNode = StructNode.SelectSingleNode("value");
- if (StructValueNode != null)
- Value = DecodeXMLRPCValue(StructValueNode);
- if ((Key != null) && (Value != null))
- H[Key] = Value;
- }
- if (H.Count > 0) // there was something in the struct
- RetVal = H;
- }
- break;
- case XMLRPC_DATE:
- DateTimeOffset Result;
- if (DateTimeOffset.TryParseExact(ValueNode.InnerText, Format8601, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AllowWhiteSpaces | System.Globalization.DateTimeStyles.AdjustToUniversal, out Result))
- RetVal = Result;
- break;
- case XMLRPC_BASE64:
- RetVal = Convert.FromBase64String(ValueNode.InnerText);
- break;
- }
- return RetVal;
- }
- protected object DecodeXMLRPCResult(string XMLResultString)
- {
- // Debug.Log(XMLResultString);
- object RetVal = null;
- XmlDocument XML = new XmlDocument();
- XML.LoadXml(XMLResultString);
- XmlNode XMLMethodResponse = XML.SelectSingleNode("/methodResponse");
- if (XMLMethodResponse != null)
- { // we have a response
- XmlNode XMLFault = XMLMethodResponse.SelectSingleNode("fault");
- if (XMLFault != null)
- { // we have a <fault> tag, so create a fault
- RetVal = new RPCFault();
- foreach (XmlNode XMLFaultMember in XMLFault.SelectNodes("value/struct/member"))
- { // get the value
- object XMLFaultMemberValue = DecodeXMLRPCValue(XMLFaultMember.SelectSingleNode("value"));
- switch (XMLRPCTypeOf(XMLFaultMemberValue))
- { // only concerned about string and int values, as those are the code and the error
- case XMLRPC_STRING:
- ((RPCFault)RetVal).FaultString = (string)XMLFaultMemberValue;
- break;
- case XMLRPC_INT:
- ((RPCFault)RetVal).FaultCode = (int)XMLFaultMemberValue;
- break;
- }
- }
- }
- else // no fault, so parse the value
- RetVal = DecodeXMLRPCValue(XMLMethodResponse.SelectNodes("params/param/value").Item(0)); // return only has one param and value, so assume the first
- }
- return RetVal;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment