From 82161a8518b5afebfd8d9d70e69ade9c76b98d82 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 14 Jun 2006 20:50:09 +0000 Subject: [PATCH] * Using XmlRpcCS for login now * Properly handle the Fixed field type in Packet.Blocks() git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@25 52acb1d6-8a22-11de-b505-999d5b087335 --- libsecondlife-cs/NetworkManager.cs | 200 ++++++++------ libsecondlife-cs/Packet.cs | 22 ++ libsecondlife-cs/Parcel.cs | 4 +- libsecondlife-cs/ProtocolManager.cs | 1 + libsecondlife-cs/Types.cs | 18 +- libsecondlife-cs/XmlRpcCS/AssemblyInfo.cs | 8 + libsecondlife-cs/XmlRpcCS/Logger.cs | 46 ++++ .../XmlRpcCS/SimpleHttpRequest.cs | 204 ++++++++++++++ .../XmlRpcCS/XmlRpcBoxcarRequest.cs | 51 ++++ libsecondlife-cs/XmlRpcCS/XmlRpcCS.csproj | 185 +++++++++++++ .../XmlRpcCS/XmlRpcClientProxy.cs | 61 +++++ .../XmlRpcCS/XmlRpcDeserializer.cs | 195 ++++++++++++++ libsecondlife-cs/XmlRpcCS/XmlRpcErrorCodes.cs | 53 ++++ libsecondlife-cs/XmlRpcCS/XmlRpcException.cs | 38 +++ .../XmlRpcCS/XmlRpcExposedAttribute.cs | 60 +++++ libsecondlife-cs/XmlRpcCS/XmlRpcRequest.cs | 146 ++++++++++ .../XmlRpcCS/XmlRpcRequestDeserializer.cs | 64 +++++ .../XmlRpcCS/XmlRpcRequestSerializer.cs | 51 ++++ libsecondlife-cs/XmlRpcCS/XmlRpcResponder.cs | 98 +++++++ libsecondlife-cs/XmlRpcCS/XmlRpcResponse.cs | 81 ++++++ .../XmlRpcCS/XmlRpcResponseDeserializer.cs | 65 +++++ .../XmlRpcCS/XmlRpcResponseSerializer.cs | 57 ++++ libsecondlife-cs/XmlRpcCS/XmlRpcSerializer.cs | 109 ++++++++ libsecondlife-cs/XmlRpcCS/XmlRpcServer.cs | 236 ++++++++++++++++ .../XmlRpcCS/XmlRpcSystemObject.cs | 251 ++++++++++++++++++ libsecondlife-cs/XmlRpcCS/XmlRpcXmlTokens.cs | 76 ++++++ libsecondlife-cs/libsecondlife.csproj | 5 + libsecondlife-cs/libsecondlife.sln | 8 + 28 files changed, 2303 insertions(+), 90 deletions(-) create mode 100644 libsecondlife-cs/XmlRpcCS/AssemblyInfo.cs create mode 100644 libsecondlife-cs/XmlRpcCS/Logger.cs create mode 100644 libsecondlife-cs/XmlRpcCS/SimpleHttpRequest.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcBoxcarRequest.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcCS.csproj create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcClientProxy.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcDeserializer.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcErrorCodes.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcException.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcExposedAttribute.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcRequest.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcRequestDeserializer.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcRequestSerializer.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcResponder.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcResponse.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcResponseDeserializer.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcResponseSerializer.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcSerializer.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcServer.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcSystemObject.cs create mode 100644 libsecondlife-cs/XmlRpcCS/XmlRpcXmlTokens.cs diff --git a/libsecondlife-cs/NetworkManager.cs b/libsecondlife-cs/NetworkManager.cs index f8731883..620f770e 100644 --- a/libsecondlife-cs/NetworkManager.cs +++ b/libsecondlife-cs/NetworkManager.cs @@ -32,6 +32,7 @@ using System.Net; using System.Net.Sockets; using System.Threading; using System.Security.Cryptography; +using Nwc.XmlRpc; namespace libsecondlife { @@ -62,8 +63,6 @@ namespace libsecondlife private EndPoint endPoint; private ProtocolManager Protocol; private NetworkManager Network; - private Hashtable UserCallbacks; - private Hashtable InternalCallbacks; private byte[] Buffer; private Socket Connection; private AsyncCallback ReceivedData; @@ -76,13 +75,10 @@ namespace libsecondlife private Mutex NeedAckMutex; private int ResendTick; - public Circuit(ProtocolManager protocol, NetworkManager network, Hashtable userCallbacks, - Hashtable internalCallbacks, uint circuitCode) + public Circuit(ProtocolManager protocol, NetworkManager network, uint circuitCode) { Protocol = protocol; Network = network; - UserCallbacks = userCallbacks; - InternalCallbacks = internalCallbacks; CircuitCode = circuitCode; Sequence = 0; Buffer = new byte[4096]; @@ -139,8 +135,8 @@ namespace libsecondlife ACKTimer.Start(); // Send the UseCircuitCode packet to initiate the connection - Packet packet = PacketBuilder.UseCircuitCode(Protocol, Network.LoginValues.AgentID, - Network.LoginValues.SessionID, CircuitCode); + Packet packet = PacketBuilder.UseCircuitCode(Protocol, Network.AgentID, + Network.SessionID, CircuitCode); // Send the initial packet out SendPacket(packet, true); @@ -456,7 +452,7 @@ namespace libsecondlife } // Fire any internal callbacks registered with this packet type - PacketCallback callback = (PacketCallback)InternalCallbacks[packet.Layout.Name]; + PacketCallback callback = (PacketCallback)Network.InternalCallbacks[packet.Layout.Name]; if (callback != null) { @@ -464,7 +460,7 @@ namespace libsecondlife } // Fire any user callbacks registered with this packet type - callback = (PacketCallback)UserCallbacks[packet.Layout.Name]; + callback = (PacketCallback)Network.UserCallbacks[packet.Layout.Name]; if (callback != null) { @@ -473,7 +469,7 @@ namespace libsecondlife else { // Attempt to fire a default user callback - callback = (PacketCallback)UserCallbacks["Default"]; + callback = (PacketCallback)Network.UserCallbacks["Default"]; if (callback != null) { @@ -556,28 +552,10 @@ namespace libsecondlife } } - public struct LoginReply - { - public LLUUID SessionID; - public LLUUID SecureSessionID; - public string StartLocation; - public string FirstName; - public string LastName; - public int RegionX; - public int RegionY; - public string Home; - public string Message; - public uint CircuitCode; - public int Port; - public string IP; - public string LookAt; - public LLUUID AgentID; - public uint SecondsSinceEpoch; - } - public class NetworkManager { - public LoginReply LoginValues; + public LLUUID AgentID; + public LLUUID SessionID; public string LoginError; public Hashtable UserCallbacks; public Hashtable InternalCallbacks; @@ -619,6 +597,114 @@ namespace libsecondlife circuit.SendPacket(packet, true); } + public static Hashtable DefaultLoginValues(string firstName, string lastName, string password, string mac, + string startLocation, int major, int minor, int patch, int build, string platform, string viewerDigest, + string userAgent, string author) + { + Hashtable values = new Hashtable(); + + // Generate an MD5 hash of the password + MD5 md5 = new MD5CryptoServiceProvider(); + byte[] hash = md5.ComputeHash(Encoding.ASCII.GetBytes(password)); + StringBuilder passwordDigest = new StringBuilder(); + // Convert the hash to a hex string + foreach(byte b in hash) + { + passwordDigest.AppendFormat("{0:x2}", b); + } + + values["first"] = firstName; + values["last"] = lastName; + values["passwd"] = "$1$" + passwordDigest; + values["start"] = startLocation; + values["major"] = major; + values["minor"] = minor; + values["patch"] = patch; + values["build"] = build; + values["platform"] = platform; + values["mac"] = mac; + values["viewer_digest"] = viewerDigest; + values["user-agent"] = userAgent + " (" + Helpers.VERSION + ")"; + values["author"] = author; + + return values; + } + + public bool Login(Hashtable loginParams, out Hashtable values) + { + XmlRpcResponse result; + + XmlRpcRequest xmlrpc = new XmlRpcRequest(); + xmlrpc.MethodName = "login_to_simulator"; + xmlrpc.Params.Clear(); + xmlrpc.Params.Add(loginParams); + + try + { + result = (XmlRpcResponse)xmlrpc.Send("https://login.agni.lindenlab.com/cgi-bin/login.cgi"); + } + catch (Exception e) + { + Helpers.Log(e.ToString(), Helpers.LogLevel.Error); + LoginError = e.Message; + values = null; + return false; + } + + if (result.IsFault) + { + Helpers.Log("Fault " + result.FaultCode + ": " + result.FaultString, Helpers.LogLevel.Error); + LoginError = "Fault " + result.FaultCode + ": " + result.FaultString; + values = null; + return false; + } + + values = (Hashtable)result.Value; + + if ((string)values["login"] == "false") + { + LoginError = values["reason"] + ": " + values["message"]; + return false; + } + + AgentID = new LLUUID((string)values["agent_id"]); + SessionID = new LLUUID((string)values["session_id"]); + uint circuitCode = (uint)(int)values["circuit_code"]; + + /*LoginValues.SessionID = RpcGetString(LoginBuffer.ToString(), "session_id"); + LoginValues.SecureSessionID = RpcGetString(LoginBuffer.ToString(), "secure_session_id"); + LoginValues.StartLocation = RpcGetString(LoginBuffer.ToString(), "start_location"); + LoginValues.FirstName = RpcGetString(LoginBuffer.ToString(), "first_name"); + LoginValues.LastName = RpcGetString(LoginBuffer.ToString(), "last_name"); + LoginValues.RegionX = RpcGetInt(LoginBuffer.ToString(), "region_x"); + LoginValues.RegionY = RpcGetInt(LoginBuffer.ToString(), "region_y"); + LoginValues.Home = RpcGetString(LoginBuffer.ToString(), "home"); + LoginValues.Message = RpcGetString(LoginBuffer.ToString(), "message").Replace("\r\n", ""); + LoginValues.CircuitCode = (uint)RpcGetInt(LoginBuffer.ToString(), "circuit_code"); + LoginValues.Port = RpcGetInt(LoginBuffer.ToString(), "sim_port"); + LoginValues.IP = RpcGetString(LoginBuffer.ToString(), "sim_ip"); + LoginValues.LookAt = RpcGetString(LoginBuffer.ToString(), "look_at"); + LoginValues.AgentID = RpcGetString(LoginBuffer.ToString(), "agent_id"); + LoginValues.SecondsSinceEpoch = (uint)RpcGetInt(LoginBuffer.ToString(), "seconds_since_epoch");*/ + + // Connect to the sim given in the login reply + Circuit circuit = new Circuit(Protocol, this, circuitCode); + if (!circuit.Open((string)values["sim_ip"], (int)values["sim_port"])) + { + return false; + } + + // Circuit was successfully opened, add it to the list and set it as default + Circuits.Add(circuit); + CurrentCircuit = circuit; + + // Move our agent in to the sim to complete the connection + Packet packet = PacketBuilder.CompleteAgentMovement(Protocol, AgentID, SessionID, circuitCode); + SendPacket(packet); + + return true; + } + public bool Login(string firstName, string lastName, string password, string mac, int major, int minor, int patch, int build, string platform, string viewerDigest, string userAgent, string author) @@ -703,7 +789,7 @@ namespace libsecondlife } // Parse the login reply and put the returned variables in to a struct - if (!ParseLoginReply()) + /*if (!ParseLoginReply()) { return false; } @@ -724,7 +810,9 @@ namespace libsecondlife LoginValues.CircuitCode); SendPacket(packet); - return true; + return true;*/ + + return false; } public void Logout() @@ -734,7 +822,7 @@ namespace libsecondlife // Halt all timers on the current circuit CurrentCircuit.Stop(); - Packet packet = PacketBuilder.LogoutRequest(Protocol, LoginValues.AgentID, LoginValues.SessionID); + Packet packet = PacketBuilder.LogoutRequest(Protocol, AgentID, SessionID); SendPacket(packet); // TODO: We should probably check if the server actually received the logout request @@ -742,50 +830,6 @@ namespace libsecondlife System.Threading.Thread.Sleep(1000); } - private bool ParseLoginReply() - { - string msg; - - if (LoginBuffer == null) - { - LoginError = "Error connecting to the login server"; - return false; - } - - msg = RpcGetString(LoginBuffer, "reason"); - if (msg.Length != 0) - { - LoginError = RpcGetString(LoginBuffer, "message"); - return false; - } - - msg = RpcGetString(LoginBuffer, "logintrue"); - if (msg.Length == 0) - { - LoginError = "Unknown login error"; - return false; - } - - // Grab the login parameters - LoginValues.SessionID = RpcGetString(LoginBuffer.ToString(), "session_id"); - LoginValues.SecureSessionID = RpcGetString(LoginBuffer.ToString(), "secure_session_id"); - LoginValues.StartLocation = RpcGetString(LoginBuffer.ToString(), "start_location"); - LoginValues.FirstName = RpcGetString(LoginBuffer.ToString(), "first_name"); - LoginValues.LastName = RpcGetString(LoginBuffer.ToString(), "last_name"); - LoginValues.RegionX = RpcGetInt(LoginBuffer.ToString(), "region_x"); - LoginValues.RegionY = RpcGetInt(LoginBuffer.ToString(), "region_y"); - LoginValues.Home = RpcGetString(LoginBuffer.ToString(), "home"); - LoginValues.Message = RpcGetString(LoginBuffer.ToString(), "message").Replace("\r\n", ""); - LoginValues.CircuitCode = (uint)RpcGetInt(LoginBuffer.ToString(), "circuit_code"); - LoginValues.Port = RpcGetInt(LoginBuffer.ToString(), "sim_port"); - LoginValues.IP = RpcGetString(LoginBuffer.ToString(), "sim_ip"); - LoginValues.LookAt = RpcGetString(LoginBuffer.ToString(), "look_at"); - LoginValues.AgentID = RpcGetString(LoginBuffer.ToString(), "agent_id"); - LoginValues.SecondsSinceEpoch = (uint)RpcGetInt(LoginBuffer.ToString(), "seconds_since_epoch"); - - return true; - } - string RpcGetString(string rpc, string name) { int pos = rpc.IndexOf(name); diff --git a/libsecondlife-cs/Packet.cs b/libsecondlife-cs/Packet.cs index 30d316ac..2a143611 100644 --- a/libsecondlife-cs/Packet.cs +++ b/libsecondlife-cs/Packet.cs @@ -259,6 +259,27 @@ namespace libsecondlife } } } + else if (fieldMap.Type == FieldType.Fixed) + { + fieldSize = fieldMap.Count; + + if (pos + fieldSize <= Data.Length) + { + // Create a new field to add to the fields for this block + field = new Field(); + field.Data = GetField(Data, pos, fieldMap.Type, fieldSize); + field.Layout = fieldMap; + + block.Fields.Add(field); + + pos += fieldSize; + } + else + { + Helpers.Log("getBlocks(): goto 4 reached", Helpers.LogLevel.Warning); + goto BlockDone; + } + } else { for (int j = 0; j < fieldMap.Count; ++j) @@ -355,6 +376,7 @@ namespace libsecondlife case FieldType.IPPORT: return BitConverter.ToUInt16(byteArray, pos); case FieldType.Variable: + case FieldType.Fixed: byte[] bytes = new byte[fieldSize]; for (int i = 0; i < fieldSize; ++i) diff --git a/libsecondlife-cs/Parcel.cs b/libsecondlife-cs/Parcel.cs index b762bbd6..1db5f7b0 100644 --- a/libsecondlife-cs/Parcel.cs +++ b/libsecondlife-cs/Parcel.cs @@ -90,7 +90,7 @@ namespace libsecondlife if (parcel != null) { Packet parcelInfoPacket = PacketBuilder.ParcelInfoRequest(Client.Protocol, parcel.ID, - Client.Network.LoginValues.AgentID, Client.Network.LoginValues.SessionID); + Client.Network.AgentID, Client.Network.SessionID); Client.Network.SendPacket(parcelInfoPacket); // Rate limiting @@ -235,7 +235,7 @@ namespace libsecondlife LLUUID queryID = new LLUUID(); Packet landQuery = PacketBuilder.DirLandQuery(Client.Protocol, ReservedNewbie, ForSale, queryID, - Auction, 0, Client.Network.LoginValues.AgentID, Client.Network.LoginValues.SessionID); + Auction, 0, Client.Network.AgentID, Client.Network.SessionID); Client.Network.SendPacket(landQuery); while (!DirLandTimeout) diff --git a/libsecondlife-cs/ProtocolManager.cs b/libsecondlife-cs/ProtocolManager.cs index 27b72127..56dd2627 100644 --- a/libsecondlife-cs/ProtocolManager.cs +++ b/libsecondlife-cs/ProtocolManager.cs @@ -142,6 +142,7 @@ namespace libsecondlife TypeSizes.Add(FieldType.IPADDR, 4); TypeSizes.Add(FieldType.IPPORT, 2); TypeSizes.Add(FieldType.Variable, -1); + TypeSizes.Add(FieldType.Fixed, -2); LoadKeywordFile(keywordFile); LoadMapFile(mapFile); diff --git a/libsecondlife-cs/Types.cs b/libsecondlife-cs/Types.cs index 51d21d8e..bc228d8f 100644 --- a/libsecondlife-cs/Types.cs +++ b/libsecondlife-cs/Types.cs @@ -64,6 +64,11 @@ namespace libsecondlife Array.Copy(byteArray, pos, Data, 0, 16); } + public override int GetHashCode() + { + return BitConverter.ToInt32(Data, 0); + } + public override bool Equals(object o) { if (!(o is LLUUID)) @@ -76,20 +81,13 @@ namespace libsecondlife public static bool operator==(LLUUID lhs, LLUUID rhs) { - if (lhs is LLUUID && rhs is LLUUID) + for (int i = 0; i < 16; ++i) { - for (int i = 0; i < 16; ++i) + if (lhs.Data[i] != rhs.Data[i]) { - if (lhs.Data[i] != rhs.Data[i]) - { - return false; - } + return false; } } - else - { - return false; - } return true; } diff --git a/libsecondlife-cs/XmlRpcCS/AssemblyInfo.cs b/libsecondlife-cs/XmlRpcCS/AssemblyInfo.cs new file mode 100644 index 00000000..05f089d3 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/AssemblyInfo.cs @@ -0,0 +1,8 @@ +using System.Reflection; + +[assembly: AssemblyVersion("1.10.*")] +[assembly: AssemblyTitle("XmlRpcCS")] +[assembly: AssemblyCompany("Ronin Consulting Inc")] +[assembly: AssemblyDescription("XML RPC client and server")] + + diff --git a/libsecondlife-cs/XmlRpcCS/Logger.cs b/libsecondlife-cs/XmlRpcCS/Logger.cs new file mode 100644 index 00000000..2c4379d3 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/Logger.cs @@ -0,0 +1,46 @@ +namespace Nwc.XmlRpc +{ + using System; + + /// Define levels of logging. This duplicates + /// similar enumerations in System.Diagnostics.EventLogEntryType. The + /// duplication was merited because .NET Compact Framework lacked the EventLogEntryType enum. + public enum LogLevel + { + /// Information level, log entry for informational reasons only. + Information, + /// Warning level, indicates a possible problem. + Warning, + /// Error level, implies a significant problem. + Error + } + + /// + ///Logging singleton with swappable output delegate. + /// + /// + ///This singleton provides a centralized log. The actual WriteEntry calls are passed + ///off to a delegate however. Having a delegate do the actual logginh allows you to + ///implement different logging mechanism and have them take effect throughout the system. + /// + public class Logger + { + ///Delegate definition for logging. + ///The message String to log. + ///The LogLevel of your message. + public delegate void LoggerDelegate(String message, LogLevel level); + ///The LoggerDelegate that will recieve WriteEntry requests. + static public LoggerDelegate Delegate = null; + + /// + ///Method logging events are sent to. + /// + ///The message String to log. + ///The LogLevel of your message. + static public void WriteEntry(String message, LogLevel level) + { + if (Delegate != null) + Delegate(message, level); + } + } +} diff --git a/libsecondlife-cs/XmlRpcCS/SimpleHttpRequest.cs b/libsecondlife-cs/XmlRpcCS/SimpleHttpRequest.cs new file mode 100644 index 00000000..4b408de5 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/SimpleHttpRequest.cs @@ -0,0 +1,204 @@ +namespace Nwc.XmlRpc +{ + using System; + using System.IO; + using System.Net.Sockets; + using System.Collections; + + ///Very basic HTTP request handler. + ///This class is designed to accept a TcpClient and treat it as an HTTP request. + /// It will do some basic header parsing and manage the input and output streams associated + /// with the request. + public class SimpleHttpRequest + { + private String _httpMethod = null; + private String _protocol; + private String _filePathFile = null; + private String _filePathDir = null; + private String __filePath; + private TcpClient _client; + private StreamReader _input; + private StreamWriter _output; + private Hashtable _headers; + + /// A constructor which accepts the TcpClient. + /// It creates the associated input and output streams, determines the request type, + /// and parses the remaining HTTP header. + /// The TcpClient associated with the HTTP connection. + public SimpleHttpRequest(TcpClient client) + { + _client = client; + _output = new StreamWriter(client.GetStream()); + _input = new StreamReader(client.GetStream()); + GetRequestMethod(); + GetRequestHeaders(); + } + + /// The output StreamWriter associated with the request. + public StreamWriter Output + { + get { return _output; } + } + + /// The input StreamReader associated with the request. + public StreamReader Input + { + get { return _input; } + } + + /// The TcpClient with the request. + public TcpClient Client + { + get { return _client; } + } + + private String _filePath + { + get { return __filePath; } + set + { + __filePath = value; + _filePathDir = null; + _filePathFile = null; + } + } + + /// The type of HTTP request (i.e. PUT, GET, etc.). + public String HttpMethod + { + get { return _httpMethod; } + } + + /// The level of the HTTP protocol. + public String Protocol + { + get { return _protocol; } + } + + /// The "path" which is part of any HTTP request. + public String FilePath + { + get { return _filePath; } + } + + /// The file portion of the "path" which is part of any HTTP request. + public String FilePathFile + { + get + { + if (_filePathFile != null) + return _filePathFile; + + int i = FilePath.LastIndexOf("/"); + + if (i == -1) + return ""; + + i++; + _filePathFile = FilePath.Substring(i, FilePath.Length - i); + return _filePathFile; + } + } + + /// The directory portion of the "path" which is part of any HTTP request. + public String FilePathDir + { + get + { + if (_filePathDir != null) + return _filePathDir; + + int i = FilePath.LastIndexOf("/"); + + if (i == -1) + return ""; + + i++; + _filePathDir = FilePath.Substring(0, i); + return _filePathDir; + } + } + + private void GetRequestMethod() + { + string req = _input.ReadLine(); + if (req == null) + throw new ApplicationException("Void request."); + + if (0 == String.Compare("GET ", req.Substring (0, 4), true)) + _httpMethod = "GET"; + else if (0 == String.Compare("POST ", req.Substring (0, 5), true)) + _httpMethod = "POST"; + else + throw new InvalidOperationException("Unrecognized method in query: " + req); + + req = req.TrimEnd (); + int idx = req.IndexOf(' ') + 1; + if (idx >= req.Length) + throw new ApplicationException ("What do you want?"); + + string page_protocol = req.Substring(idx); + int idx2 = page_protocol.IndexOf(' '); + if (idx2 == -1) + idx2 = page_protocol.Length; + + _filePath = page_protocol.Substring(0, idx2).Trim(); + _protocol = page_protocol.Substring(idx2).Trim(); + } + + private void GetRequestHeaders() + { + String line; + int idx; + + _headers = new Hashtable(); + + while ((line = _input.ReadLine ()) != "") + { + if (line == null) + { + break; + } + + idx = line.IndexOf (':'); + if (idx == -1 || idx == line.Length - 1) + { + Logger.WriteEntry("Malformed header line: " + line, LogLevel.Information); + continue; + } + + String key = line.Substring (0, idx); + String value = line.Substring (idx + 1); + + try + { + _headers.Add(key, value); + } + catch (Exception) + { + Logger.WriteEntry("Duplicate header key in line: " + line, LogLevel.Information); + } + } + } + + /// + /// Format the object contents into a useful string representation. + /// + ///String representation of the SimpleHttpRequest as the HttpMethod FilePath Protocol. + override public String ToString() + { + return HttpMethod + " " + FilePath + " " + Protocol; + } + + /// + /// Close the SimpleHttpRequest. This flushes and closes all associated io streams. + /// + public void Close() + { + _output.Flush(); + _output.Close(); + _input.Close(); + _client.Close(); + } + } +} diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcBoxcarRequest.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcBoxcarRequest.cs new file mode 100644 index 00000000..f87f7a50 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcBoxcarRequest.cs @@ -0,0 +1,51 @@ +namespace Nwc.XmlRpc +{ + using System; + using System.Collections; + using System.IO; + using System.Xml; + using System.Net; + using System.Text; + using System.Reflection; + + /// Class that collects individual XmlRpcRequest objects and submits them as a boxcarred request. + /// A boxcared request is when a number of request are collected before being sent via XML-RPC, and then are sent via + /// a single HTTP connection. This results in a speed up from reduced connection time. The results are then retuned collectively + /// as well. + /// + /// + public class XmlRpcBoxcarRequest : XmlRpcRequest + { + /// ArrayList to collect the requests to boxcar. + public IList Requests = new ArrayList(); + + /// Basic constructor. + public XmlRpcBoxcarRequest() + { + } + + /// Returns the String "system.multiCall" which is the server method that handles boxcars. + public override String MethodName + { + get { return "system.multiCall"; } + } + + /// The ArrayList of boxcarred Requests as properly formed parameters. + public override IList Params + { + get { + _params.Clear(); + ArrayList reqArray = new ArrayList(); + foreach (XmlRpcRequest request in Requests) + { + Hashtable requestEntry = new Hashtable(); + requestEntry.Add(XmlRpcXmlTokens.METHOD_NAME, request.MethodName); + requestEntry.Add(XmlRpcXmlTokens.PARAMS, request.Params); + reqArray.Add(requestEntry); + } + _params.Add(reqArray); + return _params; + } + } + } +} diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcCS.csproj b/libsecondlife-cs/XmlRpcCS/XmlRpcCS.csproj new file mode 100644 index 00000000..83befd85 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcCS.csproj @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcClientProxy.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcClientProxy.cs new file mode 100644 index 00000000..f52273a7 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcClientProxy.cs @@ -0,0 +1,61 @@ +namespace Nwc.XmlRpc +{ + using System; + using System.Runtime.Remoting.Proxies; + using System.Runtime.Remoting.Messaging; + + /// This class provides support for creating local proxies of XML-RPC remote objects + /// + /// To create a local proxy you need to create a local C# interface and then, via createProxy + /// associate that interface with a remote object at a given URL. + /// +public class XmlRpcClientProxy : RealProxy +{ + private String _remoteObjectName; + private String _url; + private XmlRpcRequest _client = new XmlRpcRequest(); + + /// Factory method to create proxies. + /// + /// To create a local proxy you need to create a local C# interface with methods that mirror those of the server object. + /// Next, pass that interface into createProxy along with the object name and URL of the remote object and + /// cast the resulting object to the specifice interface. + /// + /// String The name of the remote object. + /// String The URL of the remote object. + /// Type The typeof() of a C# interface. + /// Object A proxy for your specified interface. Cast to appropriate type. + public static Object createProxy(String remoteObjectName, String url, Type anInterface) + { + return new XmlRpcClientProxy(remoteObjectName, url, anInterface).GetTransparentProxy(); + } + + private XmlRpcClientProxy(String remoteObjectName, String url, Type t) : base(t) + { + _remoteObjectName = remoteObjectName; + _url = url; + } + + /// The local method dispatcher - do not invoke. + override public IMessage Invoke(IMessage msg) + { + IMethodCallMessage methodMessage = (IMethodCallMessage)msg; + + _client.MethodName = _remoteObjectName + "." + methodMessage.MethodName; + _client.Params.Clear(); + foreach (Object o in methodMessage.Args) + _client.Params.Add(o); + + try + { + Object ret = _client.Invoke(_url); + return new ReturnMessage(ret,null,0, + methodMessage.LogicalCallContext, methodMessage); + } + catch (Exception e) + { + return new ReturnMessage(e, methodMessage); + } + } +} +} diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcDeserializer.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcDeserializer.cs new file mode 100644 index 00000000..36a398c1 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcDeserializer.cs @@ -0,0 +1,195 @@ +namespace Nwc.XmlRpc +{ + using System; + using System.Collections; + using System.IO; + using System.Xml; + using System.Globalization; + + /// Parser context, we maintain contexts in a stack to avoiding recursion. + struct Context + { + public String Name; + public Object Container; + } + + /// Basic XML-RPC data deserializer. + /// Uses XmlTextReader to parse the XML data. This level of the class + /// only handles the tokens common to both Requests and Responses. This class is not useful in and of itself + /// but is designed to be subclassed. + public class XmlRpcDeserializer : XmlRpcXmlTokens + { + private static DateTimeFormatInfo _dateFormat = new DateTimeFormatInfo(); + + private Object _container; + private Stack _containerStack; + + /// Protected reference to last text. + protected String _text; + /// Protected reference to last deserialized value. + protected Object _value; + /// Protected reference to last name field. + protected String _name; + + + /// Basic constructor. + public XmlRpcDeserializer() + { + Reset(); + _dateFormat.FullDateTimePattern = ISO_DATETIME; + } + + /// Static method that parses XML data into a response using the Singleton. + /// StreamReader containing an XML-RPC response. + /// Object object resulting from the deserialization. + virtual public Object Deserialize(TextReader xmlData) + { + return null; + } + + /// Protected method to parse a node in an XML-RPC XML stream. + /// Method deals with elements common to all XML-RPC data, subclasses of + /// this object deal with request/response spefic elements. + /// XmlTextReader of the in progress parsing data stream. + protected void DeserializeNode(XmlTextReader reader) + { + switch (reader.NodeType) + { + case XmlNodeType.Element: + if (Logger.Delegate != null) + Logger.WriteEntry("START " + reader.Name, LogLevel.Information); + switch (reader.Name) + { + case VALUE: + _value = null; + _text = null; + break; + case STRUCT: + PushContext(); + _container = new Hashtable(); + break; + case ARRAY: + PushContext(); + _container = new ArrayList(); + break; + } + break; + case XmlNodeType.EndElement: + if (Logger.Delegate != null) + Logger.WriteEntry("END " + reader.Name, LogLevel.Information); + switch (reader.Name) + { + case BASE64: + _value = Convert.FromBase64String(_text); + break; + case BOOLEAN: + int val = Int16.Parse(_text); + if (val == 0) + _value = false; + else if (val == 1) + _value = true; + break; + case STRING: + _value = _text; + break; + case DOUBLE: + _value = Double.Parse(_text); + break; + case INT: + case ALT_INT: + _value = Int32.Parse(_text); + break; + case DATETIME: +#if __MONO__ + _value = DateParse(_text); +#else + _value = DateTime.ParseExact(_text, "F", _dateFormat); +#endif + break; + case NAME: + _name = _text; + break; + case VALUE: + if (_value == null) + _value = _text; // some kits don't use tag, they just do + + if ((_container != null) && (_container is IList)) // in an array? If so add value to it. + ((IList)_container).Add(_value); + break; + case MEMBER: + if ((_container != null) && (_container is IDictionary)) // in an struct? If so add value to it. + ((IDictionary)_container).Add(_name, _value); + break; + case ARRAY: + case STRUCT: + _value = _container; + PopContext(); + break; + } + break; + case XmlNodeType.Text: + if (Logger.Delegate != null) + Logger.WriteEntry("Text " + reader.Value, LogLevel.Information); + _text = reader.Value; + break; + default: + break; + } + } + + /// Static method that parses XML in a String into a + /// request using the Singleton. + /// String containing an XML-RPC request. + /// XmlRpcRequest object resulting from the parse. + public Object Deserialize(String xmlData) + { + StringReader sr = new StringReader(xmlData); + return Deserialize(sr); + } + + /// Pop a Context of the stack, an Array or Struct has closed. + private void PopContext() + { + Context c = (Context)_containerStack.Pop(); + _container = c.Container; + _name = c.Name; + } + + /// Push a Context on the stack, an Array or Struct has opened. + private void PushContext() + { + Context context; + + context.Container = _container; + context.Name = _name; + + _containerStack.Push(context); + } + + /// Reset the internal state of the deserializer. + protected void Reset() + { + _text = null; + _value = null; + _name = null; + _container = null; + _containerStack = new Stack(); + } + +#if __MONO__ + private DateTime DateParse(String str) + { + int year = Int32.Parse(str.Substring(0,4)); + int month = Int32.Parse(str.Substring(4,2)); + int day = Int32.Parse(str.Substring(6,2)); + int hour = Int32.Parse(str.Substring(9,2)); + int min = Int32.Parse(str.Substring(12,2)); + int sec = Int32.Parse(str.Substring(15,2)); + return new DateTime(year,month,day,hour,min,sec); + } +#endif + + } +} + + diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcErrorCodes.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcErrorCodes.cs new file mode 100644 index 00000000..29f2dceb --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcErrorCodes.cs @@ -0,0 +1,53 @@ +namespace Nwc.XmlRpc +{ + using System; + + /// Standard XML-RPC error codes. + public class XmlRpcErrorCodes + { + /// + public const int PARSE_ERROR_MALFORMED = -32700; + /// + public const String PARSE_ERROR_MALFORMED_MSG = "Parse Error, not well formed"; + + /// + public const int PARSE_ERROR_ENCODING = -32701; + /// + public const String PARSE_ERROR_ENCODING_MSG = "Parse Error, unsupported encoding"; + + /** + -32702 ---> parse error. invalid character for encoding + -32600 ---> server error. invalid xml-rpc. not conforming to spec. + **/ + + /// + public const int SERVER_ERROR_METHOD = -32601; + /// + public const String SERVER_ERROR_METHOD_MSG = "Server Error, requested method not found"; + + /// + public const int SERVER_ERROR_PARAMS = -32602; + /// + public const String SERVER_ERROR_PARAMS_MSG = "Server Error, invalid method parameters"; + + /** + -32603 ---> server error. internal xml-rpc error + **/ + + /// + public const int APPLICATION_ERROR = -32500; + /// + public const String APPLICATION_ERROR_MSG = "Application Error"; + + /** + -32400 ---> system error + **/ + + /// + public const int TRANSPORT_ERROR = -32300; + /// + public const String TRANSPORT_ERROR_MSG = "Transport Layer Error"; + } +} + + diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcException.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcException.cs new file mode 100644 index 00000000..96bff8d1 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcException.cs @@ -0,0 +1,38 @@ +namespace Nwc.XmlRpc +{ + using System; + + /// An XML-RPC Exception. + /// Maps a C# exception to an XML-RPC fault. Normal exceptions + /// include a message so this adds the code needed by XML-RPC. + public class XmlRpcException : Exception + { + private int _code; + + /// Instantiate an XmlRpcException with a code and message. + /// Int faultCode associated with this exception. + /// String faultMessage associated with this exception. + public XmlRpcException(int code, String message) : base(message) + { + _code = code; + } + + /// The value of the faults message, i.e. the faultString. + public String FaultString + { + get { return Message; } + } + + /// The value of the faults code, i.e. the faultCode. + public int FaultCode + { + get { return _code; } + } + + /// Format the message to include the code. + override public String ToString() + { + return "Code: " + FaultCode + " Message: " + base.ToString(); + } + } +} diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcExposedAttribute.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcExposedAttribute.cs new file mode 100644 index 00000000..c10edceb --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcExposedAttribute.cs @@ -0,0 +1,60 @@ +namespace Nwc.XmlRpc +{ + using System; + using System.Reflection; + + /// + /// Simple tagging attribute to indicate participation is XML-RPC exposure. + /// + /// + /// If present at the class level it indicates that this class does explicitly + /// expose methods. If present at the method level it denotes that the method + /// is exposed. + /// + [AttributeUsage( + AttributeTargets.Class | AttributeTargets.Method, + AllowMultiple=false, + Inherited=true + )] + public class XmlRpcExposedAttribute : Attribute + { + /// Check if obj is an object utilizing the XML-RPC exposed Attribute. + /// Object of a class or method to check for attribute. + /// Boolean true if attribute present. + public static Boolean ExposedObject(Object obj) + { + return IsExposed(obj.GetType()); + } + + /// Check if obj.methodName is an XML-RPC exposed method. + /// A method is considered to be exposed if it exists and, either, the object does not use the XmlRpcExposed attribute, + /// or the object does use the XmlRpcExposed attribute and the method has the XmlRpcExposed attribute as well. + /// Boolean true if the method is exposed. + public static Boolean ExposedMethod(Object obj, String methodName) + { + Type type = obj.GetType(); + MethodInfo method = type.GetMethod(methodName); + + if (method == null) + throw new MissingMethodException("Method " + methodName + " not found."); + + if (!IsExposed(type)) + return true; + + return IsExposed(method); + } + + /// Check if mi is XML-RPC exposed. + /// MemberInfo of a class or method to check for attribute. + /// Boolean true if attribute present. + public static Boolean IsExposed(MemberInfo mi) + { + foreach (Attribute attr in mi.GetCustomAttributes(true)) + { + if (attr is XmlRpcExposedAttribute) + return true; + } + return false; + } + } +} diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcRequest.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcRequest.cs new file mode 100644 index 00000000..af0660b0 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcRequest.cs @@ -0,0 +1,146 @@ +namespace Nwc.XmlRpc +{ + using System; + using System.Collections; + using System.IO; + using System.Xml; + using System.Net; + using System.Text; + using System.Reflection; + + internal class AcceptAllCertificatePolicy : ICertificatePolicy + { + public AcceptAllCertificatePolicy() + { + } + + public bool CheckValidationResult(ServicePoint sPoint, + System.Security.Cryptography.X509Certificates.X509Certificate cert, + WebRequest wRequest,int certProb) + { + // Always accept + return true; + } + } + + /// Class supporting the request side of an XML-RPC transaction. + public class XmlRpcRequest + { + private String _methodName = null; + private Encoding _encoding = new ASCIIEncoding(); + private XmlRpcRequestSerializer _serializer = new XmlRpcRequestSerializer(); + private XmlRpcResponseDeserializer _deserializer = new XmlRpcResponseDeserializer(); + + /// ArrayList containing the parameters. + protected IList _params = null; + + /// Instantiate an XmlRpcRequest + public XmlRpcRequest() + { + _params = new ArrayList(); + } + + /// Instantiate an XmlRpcRequest for a specified method and parameters. + /// String designating the object.method on the server the request + /// should be directed to. + /// ArrayList of XML-RPC type parameters to invoke the request with. + public XmlRpcRequest(String methodName, IList parameters) + { + MethodName = methodName; + _params = parameters; + } + + /// ArrayList conntaining the parameters for the request. + public virtual IList Params + { + get { return _params; } + } + + /// String conntaining the method name, both object and method, that the request will be sent to. + public virtual String MethodName + { + get { return _methodName; } + set { _methodName = value; } + } + + /// String object name portion of the method name. + public String MethodNameObject + { + get { + int index = MethodName.IndexOf("."); + + if (index == -1) + return MethodName; + + return MethodName.Substring(0,index); + } + } + + /// String method name portion of the object.method name. + public String MethodNameMethod + { + get { + int index = MethodName.IndexOf("."); + + if (index == -1) + return MethodName; + + return MethodName.Substring(index + 1, MethodName.Length - index - 1); + } + } + + /// Invoke this request on the server. + /// String The url of the XML-RPC server. + /// Object The value returned from the method invocation on the server. + /// If an exception generated on the server side. + public Object Invoke(String url) + { + XmlRpcResponse res = Send(url); + + if (res.IsFault) + throw new XmlRpcException(res.FaultCode, res.FaultString); + + return res.Value; + } + + /// Send the request to the server. + /// String The url of the XML-RPC server. + /// XmlRpcResponse The response generated. + public XmlRpcResponse Send(String url) + { + // Override SSL authentication mechanisms + ServicePointManager.CertificatePolicy = new AcceptAllCertificatePolicy(); + + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + if (request == null) + throw new XmlRpcException(XmlRpcErrorCodes.TRANSPORT_ERROR, + XmlRpcErrorCodes.TRANSPORT_ERROR_MSG +": Could not create request with " + url); + request.Method = "POST"; + request.ContentType = "text/xml"; + request.AllowWriteStreamBuffering = true; + request.KeepAlive = false; + request.Timeout = 8000; // miliseconds adjust as you see fit + + Stream stream = request.GetRequestStream(); + XmlTextWriter xml = new XmlTextWriter(stream, _encoding); + _serializer.Serialize(xml, this); + xml.Flush(); + xml.Close(); + + HttpWebResponse response = (HttpWebResponse)request.GetResponse(); + StreamReader input = new StreamReader(response.GetResponseStream()); + + XmlRpcResponse resp = (XmlRpcResponse)_deserializer.Deserialize(input); + input.Close(); + response.Close(); + return resp; + } + + /// Produce String representation of the object. + /// String representation of the object. + override public String ToString() + { + return _serializer.Serialize(this); + } + } +} diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcRequestDeserializer.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcRequestDeserializer.cs new file mode 100644 index 00000000..5975a152 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcRequestDeserializer.cs @@ -0,0 +1,64 @@ +namespace Nwc.XmlRpc +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.IO; + using System.Xml; + + /// Class to deserialize XML data representing a request. + public class XmlRpcRequestDeserializer : XmlRpcDeserializer + { + static private XmlRpcRequestDeserializer _singleton; + /// A static singleton instance of this deserializer. + [Obsolete("This object is now thread safe, just use an instance.",false)] + static public XmlRpcRequestDeserializer Singleton + { + get + { + if (_singleton == null) + _singleton = new XmlRpcRequestDeserializer(); + + return _singleton; + } + } + + /// Static method that parses XML data into a request using the Singleton. + /// StreamReader containing an XML-RPC request. + /// XmlRpcRequest object resulting from the parse. + override public Object Deserialize(TextReader xmlData) + { + XmlTextReader reader = new XmlTextReader(xmlData); + XmlRpcRequest request = new XmlRpcRequest(); + bool done = false; + + lock(this) + { + Reset(); + while (!done && reader.Read()) + { + DeserializeNode(reader); // Parent parse... + switch (reader.NodeType) + { + case XmlNodeType.EndElement: + switch (reader.Name) + { + case METHOD_NAME: + request.MethodName = _text; + break; + case METHOD_CALL: + done = true; + break; + case PARAM: + request.Params.Add(_value); + _text = null; + break; + } + break; + } + } + } + return request; + } + } +} diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcRequestSerializer.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcRequestSerializer.cs new file mode 100644 index 00000000..200493a7 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcRequestSerializer.cs @@ -0,0 +1,51 @@ +namespace Nwc.XmlRpc +{ + using System; + using System.Collections; + using System.Xml; + using System.IO; + + /// Class responsible for serializing an XML-RPC request. + /// This class handles the request envelope, depending on XmlRpcSerializer + /// to serialize the payload. + /// + public class XmlRpcRequestSerializer : XmlRpcSerializer + { + static private XmlRpcRequestSerializer _singleton; + /// A static singleton instance of this deserializer. + static public XmlRpcRequestSerializer Singleton + { + get + { + if (_singleton == null) + _singleton = new XmlRpcRequestSerializer(); + + return _singleton; + } + } + + /// Serialize the XmlRpcRequest to the output stream. + /// An XmlTextWriter stream to write data to. + /// An XmlRpcRequest to serialize. + /// + override public void Serialize(XmlTextWriter output, Object obj) + { + XmlRpcRequest request = (XmlRpcRequest)obj; + output.WriteStartDocument(); + output.WriteStartElement(METHOD_CALL); + output.WriteElementString(METHOD_NAME,request.MethodName); + output.WriteStartElement(PARAMS); + foreach (Object param in request.Params) + { + output.WriteStartElement(PARAM); + output.WriteStartElement(VALUE); + SerializeObject(output, param); + output.WriteEndElement(); + output.WriteEndElement(); + } + + output.WriteEndElement(); + output.WriteEndElement(); + } + } +} diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcResponder.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcResponder.cs new file mode 100644 index 00000000..736bd2de --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcResponder.cs @@ -0,0 +1,98 @@ +namespace Nwc.XmlRpc +{ + using System; + using System.Xml; + using System.Net.Sockets; + + /// The class is a container of the context of an XML-RPC dialog on the server side. + /// Instances of this class maintain the context for an individual XML-RPC server + /// side dialog. Namely they manage an inbound deserializer and an outbound serializer. + public class XmlRpcResponder + { + private XmlRpcRequestDeserializer _deserializer = new XmlRpcRequestDeserializer(); + private XmlRpcResponseSerializer _serializer = new XmlRpcResponseSerializer(); + private XmlRpcServer _server; + private TcpClient _client; + private SimpleHttpRequest _httpReq; + + /// The SimpleHttpRequest based on the TcpClient. + public SimpleHttpRequest HttpReq + { + get { return _httpReq; } + } + + /// Basic constructor. + /// XmlRpcServer that this XmlRpcResponder services. + /// TcpClient with the connection. + public XmlRpcResponder(XmlRpcServer server, TcpClient client) + { + _server = server; + _client = client; + _httpReq = new SimpleHttpRequest(_client); + } + + /// Call close to insure proper shutdown. + ~XmlRpcResponder() + { + Close(); + } + + ///Respond using this responders HttpReq. + public void Respond() + { + Respond(HttpReq); + } + + /// Handle an HTTP request containing an XML-RPC request. + /// This method deserializes the XML-RPC request, invokes the + /// described method, serializes the response (or fault) and sends the XML-RPC response + /// back as a valid HTTP page. + /// + /// SimpleHttpRequest containing the request. + public void Respond(SimpleHttpRequest httpReq) + { + XmlRpcRequest xmlRpcReq = (XmlRpcRequest)_deserializer.Deserialize(httpReq.Input); + XmlRpcResponse xmlRpcResp = new XmlRpcResponse(); + + try + { + xmlRpcResp.Value = _server.Invoke(xmlRpcReq); + } + catch (XmlRpcException e) + { + xmlRpcResp.SetFault(e.FaultCode, e.FaultString); + } + catch (Exception e2) + { + xmlRpcResp.SetFault(XmlRpcErrorCodes.APPLICATION_ERROR, + XmlRpcErrorCodes.APPLICATION_ERROR_MSG + ": " + e2.Message); + } + + if (Logger.Delegate != null) + Logger.WriteEntry(xmlRpcResp.ToString(), LogLevel.Information); + + XmlRpcServer.HttpHeader(httpReq.Protocol, "text/xml", 0, " 200 OK", httpReq.Output); + httpReq.Output.Flush(); + XmlTextWriter xml = new XmlTextWriter(httpReq.Output); + _serializer.Serialize(xml, xmlRpcResp); + xml.Flush(); + httpReq.Output.Flush(); + } + + ///Close all contained resources, both the HttpReq and client. + public void Close() + { + if (_httpReq != null) + { + _httpReq.Close(); + _httpReq = null; + } + + if (_client != null) + { + _client.Close(); + _client = null; + } + } + } +} diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcResponse.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcResponse.cs new file mode 100644 index 00000000..d0a14026 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcResponse.cs @@ -0,0 +1,81 @@ +namespace Nwc.XmlRpc +{ + using System; + using System.Collections; + using System.IO; + using System.Xml; + + /// Class designed to represent an XML-RPC response. + public class XmlRpcResponse + { + private Object _value; + /// bool indicating if this response represents a fault. + public bool IsFault; + + /// Basic constructor + public XmlRpcResponse() + { + Value = null; + IsFault = false; + } + + /// Constructor for a fault. + /// int the numeric faultCode value. + /// String the faultString value. + public XmlRpcResponse(int code, String message) : this() + { + SetFault(code,message); + } + + /// The data value of the response, may be fault data. + public Object Value + { + get { return _value; } + set { + IsFault = false; + _value = value; + } + } + + /// The faultCode if this is a fault. + public int FaultCode + { + get { + if (!IsFault) + return 0; + else + return (int)((Hashtable)_value)[XmlRpcXmlTokens.FAULT_CODE]; + } + } + + /// The faultString if this is a fault. + public String FaultString + { + get { + if (!IsFault) + return ""; + else + return (String)((Hashtable)_value)[XmlRpcXmlTokens.FAULT_STRING]; + } + } + + /// Set this response to be a fault. + /// int the numeric faultCode value. + /// String the faultString value. + public void SetFault(int code, String message) + { + Hashtable fault = new Hashtable(); + fault.Add("faultCode", code); + fault.Add("faultString", message); + Value = fault; + IsFault = true; + } + + /// Form a useful string representation of the object, in this case the XML response. + /// String The XML serialized XML-RPC response. + override public String ToString() + { + return XmlRpcResponseSerializer.Singleton.Serialize(this); + } + } +} diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcResponseDeserializer.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcResponseDeserializer.cs new file mode 100644 index 00000000..eb17f0d7 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcResponseDeserializer.cs @@ -0,0 +1,65 @@ +namespace Nwc.XmlRpc +{ + using System; + using System.Collections; + using System.IO; + using System.Xml; + + /// Class to deserialize XML data representing a response. + public class XmlRpcResponseDeserializer : XmlRpcDeserializer + { + static private XmlRpcResponseDeserializer _singleton; + /// A static singleton instance of this deserializer. + [Obsolete("This object is now thread safe, just use an instance.",false)] + static public XmlRpcResponseDeserializer Singleton + { + get + { + if (_singleton == null) + _singleton = new XmlRpcResponseDeserializer(); + + return _singleton; + } + } + + /// Static method that parses XML data into a response using the Singleton. + /// StreamReader containing an XML-RPC response. + /// XmlRpcResponse object resulting from the parse. + override public Object Deserialize(TextReader xmlData) + { + XmlTextReader reader = new XmlTextReader(xmlData); + XmlRpcResponse response = new XmlRpcResponse(); + bool done = false; + + lock(this) + { + Reset(); + + while (!done && reader.Read()) + { + DeserializeNode(reader); // Parent parse... + switch (reader.NodeType) + { + case XmlNodeType.EndElement: + switch (reader.Name) + { + case FAULT: + response.Value = _value; + response.IsFault = true; + break; + case PARAM: + response.Value = _value; + _value = null; + _text = null; + break; + } + break; + default: + break; + } + } + } + return response; + } + } +} diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcResponseSerializer.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcResponseSerializer.cs new file mode 100644 index 00000000..b2df7cc0 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcResponseSerializer.cs @@ -0,0 +1,57 @@ +namespace Nwc.XmlRpc +{ + using System; + using System.Collections; + using System.Xml; + + /// Class responsible for serializing an XML-RPC response. + /// This class handles the response envelope, depending on XmlRpcSerializer + /// to serialize the payload. + /// + public class XmlRpcResponseSerializer : XmlRpcSerializer + { + static private XmlRpcResponseSerializer _singleton; + /// A static singleton instance of this deserializer. + static public XmlRpcResponseSerializer Singleton + { + get + { + if (_singleton == null) + _singleton = new XmlRpcResponseSerializer(); + + return _singleton; + } + } + + /// Serialize the XmlRpcResponse to the output stream. + /// An XmlTextWriter stream to write data to. + /// An Object to serialize. + /// + override public void Serialize(XmlTextWriter output, Object obj) + { + XmlRpcResponse response = (XmlRpcResponse) obj; + + output.WriteStartDocument(); + output.WriteStartElement(METHOD_RESPONSE); + + if (response.IsFault) + output.WriteStartElement(FAULT); + else + { + output.WriteStartElement(PARAMS); + output.WriteStartElement(PARAM); + } + + output.WriteStartElement(VALUE); + + SerializeObject(output,response.Value); + + output.WriteEndElement(); + + output.WriteEndElement(); + if (!response.IsFault) + output.WriteEndElement(); + output.WriteEndElement(); + } + } +} diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcSerializer.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcSerializer.cs new file mode 100644 index 00000000..459bce15 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcSerializer.cs @@ -0,0 +1,109 @@ +namespace Nwc.XmlRpc +{ + using System; + using System.Collections; + using System.IO; + using System.Xml; + + /// Base class of classes serializing data to XML-RPC's XML format. + /// This class handles the basic type conversions like Integer to <i4>. + /// + public class XmlRpcSerializer : XmlRpcXmlTokens + { + + /// Serialize the XmlRpcRequest to the output stream. + /// An XmlTextWriter stream to write data to. + /// An Object to serialize. + /// + virtual public void Serialize(XmlTextWriter output, Object obj) + { + } + + /// Serialize the XmlRpcRequest to a String. + /// Note this may represent a real memory hog for a large request. + /// An Object to serialize. + /// String containing XML-RPC representation of the request. + /// + public String Serialize(Object obj) + { + StringWriter strBuf = new StringWriter(); + XmlTextWriter xml = new XmlTextWriter(strBuf); + xml.Formatting = Formatting.Indented; + xml.Indentation = 4; + Serialize(xml, obj); + xml.Flush(); + String returns = strBuf.ToString(); + xml.Close(); + return returns; + } + + /// Serialize the object to the output stream. + /// An XmlTextWriter stream to write data to. + /// An Object to serialize. + public void SerializeObject(XmlTextWriter output, Object obj) + { + if (obj == null) + return; + + if (obj is byte[]) + { + byte[] ba = (byte[])obj; + output.WriteStartElement(BASE64); + output.WriteBase64(ba,0,ba.Length); + output.WriteEndElement(); + } + else if (obj is String) + { + output.WriteElementString(STRING,obj.ToString()); + } + else if (obj is Int32) + { + output.WriteElementString(INT,obj.ToString()); + } + else if (obj is DateTime) + { + output.WriteElementString(DATETIME,((DateTime)obj).ToString(ISO_DATETIME)); + } + else if (obj is Double) + { + output.WriteElementString(DOUBLE,obj.ToString()); + } + else if (obj is Boolean) + { + output.WriteElementString(BOOLEAN, ((((Boolean)obj) == true)?"1":"0")); + } + else if (obj is IList) + { + output.WriteStartElement(ARRAY); + output.WriteStartElement(DATA); + if (((ArrayList)obj).Count > 0) + { + foreach (Object member in ((IList)obj)) + { + output.WriteStartElement(VALUE); + SerializeObject(output,member); + output.WriteEndElement(); + } + } + output.WriteEndElement(); + output.WriteEndElement(); + } + else if (obj is IDictionary) + { + IDictionary h = (IDictionary)obj; + output.WriteStartElement(STRUCT); + foreach (String key in h.Keys) + { + output.WriteStartElement(MEMBER); + output.WriteElementString(NAME,key); + output.WriteStartElement(VALUE); + SerializeObject(output,h[key]); + output.WriteEndElement(); + output.WriteEndElement(); + } + output.WriteEndElement(); + } + + } + } +} diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcServer.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcServer.cs new file mode 100644 index 00000000..0ceef7b4 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcServer.cs @@ -0,0 +1,236 @@ +namespace Nwc.XmlRpc +{ + using System; + using System.Collections; + using System.IO; + using System.Net; + using System.Net.Sockets; + using System.Text; + using System.Threading; + using System.Xml; + + /// A restricted HTTP server for use with XML-RPC. + /// It only handles POST requests, and only POSTs representing XML-RPC calls. + /// In addition to dispatching requests it also provides a registry for request handlers. + /// +public class XmlRpcServer : IEnumerable + { + const int RESPONDER_COUNT = 10; + private TcpListener _myListener; + private int _port; + private IPAddress _address; + private IDictionary _handlers; + private XmlRpcSystemObject _system; + private WaitCallback _wc; + + ///Constructor with port and address. + ///This constructor sets up a TcpListener listening on the + ///given port and address. It also calls a Thread on the method StartListen(). + ///IPAddress value of the address to listen on. + ///Int value of the port to listen on. + public XmlRpcServer(IPAddress address, int port) + { + _port = port; + _address = address; + _handlers = new Hashtable(); + _system = new XmlRpcSystemObject(this); + _wc = new WaitCallback(WaitCallback); + } + + ///Basic constructor. + ///This constructor sets up a TcpListener listening on the + ///given port. It also calls a Thread on the method StartListen(). IPAddress.Any + ///is assumed as the address here. + ///Int value of the port to listen on. + public XmlRpcServer(int port) : this(IPAddress.Any, port) {} + + /// Start the server. + public void Start() + { + try + { + Stop(); + //start listing on the given port + // IPAddress addr = IPAddress.Parse("127.0.0.1"); + lock (this) + { + _myListener = new TcpListener(_port); + _myListener.Start(); + //start the thread which calls the method 'StartListen' + Thread th = new Thread(new ThreadStart(StartListen)); + th.Start(); + } + } + catch(Exception e) + { + Logger.WriteEntry("An Exception Occurred while Listening :" +e.ToString(), LogLevel.Error); + } + } + + /// Stop the server. + public void Stop() + { + try + { + if (_myListener != null) + { + lock (this) + { + _myListener.Stop(); + _myListener = null; + } + } + } catch(Exception e) + { + Logger.WriteEntry("An Exception Occurred while stopping :" + + e.ToString(), LogLevel.Error); + } + } + + /// Get an enumeration of my XML-RPC handlers. + /// IEnumerable the handler enumeration. + public IEnumerator GetEnumerator() + { + return _handlers.GetEnumerator(); + } + + /// Retrieve a handler by name. + /// String naming a handler + /// Object that is the handler. + public Object this [String name] + { + get { return _handlers[name]; } + } + + /// + ///This method Accepts new connections and dispatches them when appropriate. + /// + public void StartListen() + { + while(true && _myListener != null) + { + //Accept a new connection + XmlRpcResponder responder = new XmlRpcResponder(this, _myListener.AcceptTcpClient()); + ThreadPool.QueueUserWorkItem(_wc,responder); + } + } + + + /// + ///Add an XML-RPC handler object by name. + /// + ///String XML-RPC dispatch name of this object. + ///Object The object that is the XML-RPC handler. + public void Add(String name, Object obj) + { + _handlers.Add(name,obj); + } + + ///Return a C# object.method name for and XML-RPC object.method name pair. + ///The XML-RPC object.method. + ///String of form object.method for the underlying C# method. + public String MethodName(String methodName) + { + int dotAt = methodName.LastIndexOf('.'); + + if (dotAt == -1) + { + throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, + XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Bad method name " + methodName); + } + + String objectName = methodName.Substring(0,dotAt); + Object target = _handlers[objectName]; + + if (target == null) + { + throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, + XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Object " + objectName + " not found"); + } + + return target.GetType().FullName + "." + methodName.Substring(dotAt + 1); + } + + ///Invoke a method described in a request. + ///XmlRpcRequest containing a method descriptions. + /// + /// + public Object Invoke(XmlRpcRequest req) + { + return Invoke(req.MethodNameObject, req.MethodNameMethod, req.Params); + } + + ///Invoke a method on a named handler. + ///String The name of the handler. + ///String The name of the method to invoke on the handler. + ///IList The parameters to invoke the method with. + /// + public Object Invoke(String objectName, String methodName, IList parameters) + { + Object target = _handlers[objectName]; + + if (target == null) + { + throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, + XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Object " + objectName + " not found"); + } + + return XmlRpcSystemObject.Invoke(target, methodName, parameters); + } + + /// The method the thread pool invokes when a thread is available to handle an HTTP request. + /// TcpClient from the socket accept. + public void WaitCallback(object responder) + { + XmlRpcResponder resp = (XmlRpcResponder)responder; + + if (resp.HttpReq.HttpMethod == "POST") + { + try + { + resp.Respond(); + } + catch (Exception e) + { + Logger.WriteEntry("Failed on post: " + e, LogLevel.Error); + } + } + else + { + Logger.WriteEntry("Only POST methods are supported: " + resp.HttpReq.HttpMethod + + " ignored", LogLevel.Error); + } + + resp.Close(); + } + + /// + /// This function send the Header Information to the client (Browser) + /// + /// HTTP Version + /// Mime Type + /// Total Bytes to be sent in the body + /// + /// Socket reference + static public void HttpHeader(string sHttpVersion, string sMIMEHeader, long iTotBytes, string sStatusCode, TextWriter output) + { + String sBuffer = ""; + + // if Mime type is not provided set default to text/html + if (sMIMEHeader.Length == 0 ) + { + sMIMEHeader = "text/html"; // Default Mime Type is text/html + } + + sBuffer += sHttpVersion + sStatusCode + "\r\n"; + sBuffer += "Connection: close\r\n"; + if (iTotBytes > 0) + sBuffer += "Content-Length: " + iTotBytes + "\r\n"; + sBuffer += "Server: XmlRpcServer \r\n"; + sBuffer += "Content-Type: " + sMIMEHeader + "\r\n"; + sBuffer += "\r\n"; + + output.Write(sBuffer); + } + } +} diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcSystemObject.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcSystemObject.cs new file mode 100644 index 00000000..f6cf0957 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcSystemObject.cs @@ -0,0 +1,251 @@ +namespace Nwc.XmlRpc +{ + using System; + using System.Collections; + using System.Reflection; + + /// XML-RPC System object implementation of extended specifications. + [XmlRpcExposed] + public class XmlRpcSystemObject + { + private XmlRpcServer _server; + static private IDictionary _methodHelp = new Hashtable(); + + /// Static IDictionary to hold mappings of method name to associated documentation String + static public IDictionary MethodHelp { + get { return _methodHelp; } + } + + /// Constructor. + /// XmlRpcServer server to be the system object for. + public XmlRpcSystemObject(XmlRpcServer server) + { + _server = server; + server.Add("system",this); + _methodHelp.Add(this.GetType().FullName + ".methodHelp", "Return a string description."); + } + + /// Invoke a method on a given object. + /// Using reflection, and respecting the XmlRpcExposed attribute, + /// invoke the methodName method on the target + /// instance with the parameters provided. All this packages other Invoke methods + /// end up calling this. + /// Object the value the invoked method returns. + /// If method does not exist, is not exposed, parameters invalid, or invocation + /// results in an exception. Note, the XmlRpcException.Code will indicate cause. + static public Object Invoke(Object target, String methodName, IList parameters) + { + if (target == null) + throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, + XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Invalid target object."); + + Type type = target.GetType(); + MethodInfo method = type.GetMethod(methodName); + + try + { + if (!XmlRpcExposedAttribute.ExposedMethod(target,methodName)) + throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, + XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": Method " + methodName + " is not exposed."); + } + catch (MissingMethodException me) + { + throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_METHOD, + XmlRpcErrorCodes.SERVER_ERROR_METHOD_MSG + ": " + me.Message); + } + + Object[] args = new Object[parameters.Count]; + + int index = 0; + foreach (Object arg in parameters) + { + args[index] = arg; + index++; + } + + try + { + Object retValue = method.Invoke(target, args); + if (retValue == null) + throw new XmlRpcException(XmlRpcErrorCodes.APPLICATION_ERROR, + XmlRpcErrorCodes.APPLICATION_ERROR_MSG + ": Method returned NULL."); + return retValue; + } + catch (XmlRpcException e) + { + throw e; + } + catch (ArgumentException ae) + { + Logger.WriteEntry(XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": " + ae.Message, + LogLevel.Information); + String call = methodName + "( "; + foreach (Object o in args) + { + call += o.GetType().Name; + call += " "; + } + call += ")"; + throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_PARAMS, + XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": Arguement type mismatch invoking " + call); + } + catch (TargetParameterCountException tpce) + { + Logger.WriteEntry(XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": " + tpce.Message, + LogLevel.Information); + throw new XmlRpcException(XmlRpcErrorCodes.SERVER_ERROR_PARAMS, + XmlRpcErrorCodes.SERVER_ERROR_PARAMS_MSG + ": Arguement count mismatch invoking " + methodName); + } + catch (TargetInvocationException tie) + { + throw new XmlRpcException(XmlRpcErrorCodes.APPLICATION_ERROR, + XmlRpcErrorCodes.APPLICATION_ERROR_MSG + " Invoked method " + methodName + ": " + tie.Message); + } + } + + /// List methods available on all handlers of this server. + /// IList An array of Strings, each String will have form "object.method". + [XmlRpcExposed] + public IList listMethods() + { + IList methods = new ArrayList(); + Boolean considerExposure; + + foreach (DictionaryEntry handlerEntry in _server) + { + considerExposure = XmlRpcExposedAttribute.IsExposed(handlerEntry.Value.GetType()); + + foreach (MemberInfo mi in handlerEntry.Value.GetType().GetMembers()) + { + if (mi.MemberType != MemberTypes.Method) + continue; + + if(!((MethodInfo)mi).IsPublic) + continue; + + if (considerExposure && !XmlRpcExposedAttribute.IsExposed(mi)) + continue; + + methods.Add(handlerEntry.Key + "." + mi.Name); + } + } + + return methods; + } + + /// Given a method name return the possible signatures for it. + /// String The object.method name to look up. + /// IList Of arrays of signatures. + [XmlRpcExposed] + public IList methodSignature(String name) + { + IList signatures = new ArrayList(); + int index = name.IndexOf('.'); + + if (index < 0) + return signatures; + + String oName = name.Substring(0,index); + Object obj = _server[oName]; + + if (obj == null) + return signatures; + + MemberInfo[] mi = obj.GetType().GetMember(name.Substring(index + 1)); + + if (mi == null || mi.Length != 1) // for now we want a single signature + return signatures; + + MethodInfo method; + + try + { + method = (MethodInfo)mi[0]; + } + catch (Exception e) + { + Logger.WriteEntry("Attempted methodSignature call on " + mi[0] + " caused: " + e, + LogLevel.Information); + return signatures; + } + + if (!method.IsPublic) + return signatures; + + IList signature = new ArrayList(); + signature.Add(method.ReturnType.Name); + + foreach (ParameterInfo param in method.GetParameters()) + { + signature.Add(param.ParameterType.Name); + } + + + signatures.Add(signature); + + return signatures; + } + + /// Help for given method signature. Not implemented yet. + /// String The object.method name to look up. + /// String help text. Rich HTML text. + [XmlRpcExposed] + public String methodHelp(String name) + { + String help = null; + + try + { + help = (String)_methodHelp[_server.MethodName(name)]; + } + catch (XmlRpcException e) + { + throw e; + } + catch (Exception) { /* ignored */ }; + + if (help == null) + help = "No help available for: " + name; + + return help; + } + + /// Boxcarring support method. + /// IList of calls + /// ArrayList of results/faults. + [XmlRpcExposed] + public IList multiCall(IList calls) + { + IList responses = new ArrayList(); + XmlRpcResponse fault = new XmlRpcResponse(); + + foreach (IDictionary call in calls) + { + try + { + XmlRpcRequest req = new XmlRpcRequest((String)call[XmlRpcXmlTokens.METHOD_NAME], + (ArrayList)call[XmlRpcXmlTokens.PARAMS]); + Object results = _server.Invoke(req); + IList response = new ArrayList(); + response.Add(results); + responses.Add(response); + } + catch (XmlRpcException e) + { + fault.SetFault(e.FaultCode, e.FaultString); + responses.Add(fault.Value); + } + catch (Exception e2) + { + fault.SetFault(XmlRpcErrorCodes.APPLICATION_ERROR, + XmlRpcErrorCodes.APPLICATION_ERROR_MSG + ": " + e2.Message); + responses.Add(fault.Value); + } + } + + return responses; + } + + } +} + diff --git a/libsecondlife-cs/XmlRpcCS/XmlRpcXmlTokens.cs b/libsecondlife-cs/XmlRpcCS/XmlRpcXmlTokens.cs new file mode 100644 index 00000000..5203bca2 --- /dev/null +++ b/libsecondlife-cs/XmlRpcCS/XmlRpcXmlTokens.cs @@ -0,0 +1,76 @@ +namespace Nwc.XmlRpc +{ + using System; + + /// Class collecting String tokens that are part of XML-RPC files. + public class XmlRpcXmlTokens + { + /// C# formatting string to describe an ISO 8601 date. + public const String ISO_DATETIME = "yyyyMMdd\\THH\\:mm\\:ss"; + /// Base64 field indicator. + /// Corresponds to the <base64> tag. + public const String BASE64 = "base64"; + /// String field indicator. + /// Corresponds to the <string> tag. + public const String STRING = "string"; + /// Integer field integer. + /// Corresponds to the <i4> tag. + public const String INT = "i4"; + /// Alternate integer field indicator. + /// Corresponds to the <int> tag. + public const String ALT_INT = "int"; + /// Date field indicator. + /// Corresponds to the <dateTime.iso8601> tag. + public const String DATETIME = "dateTime.iso8601"; + /// Boolean field indicator. + /// Corresponds to the <boolean> tag. + public const String BOOLEAN = "boolean"; + /// Value token. + /// Corresponds to the <value> tag. + public const String VALUE = "value"; + /// Name token. + /// Corresponds to the <name> tag. + public const String NAME = "name"; + /// Array field indicator.. + /// Corresponds to the <array> tag. + public const String ARRAY = "array"; + /// Data token. + /// Corresponds to the <data> tag. + public const String DATA = "data"; + /// Member token. + /// Corresponds to the <member> tag. + public const String MEMBER = "member"; + /// Stuct field indicator. + /// Corresponds to the <struct> tag. + public const String STRUCT = "struct"; + /// Double field indicator. + /// Corresponds to the <double> tag. + public const String DOUBLE = "double"; + /// Param token. + /// Corresponds to the <param> tag. + public const String PARAM = "param"; + /// Params token. + /// Corresponds to the <params> tag. + public const String PARAMS = "params"; + /// MethodCall token. + /// Corresponds to the <methodCall> tag. + public const String METHOD_CALL = "methodCall"; + /// MethodName token. + /// Corresponds to the <methodName> tag. + public const String METHOD_NAME = "methodName"; + /// MethodResponse token + /// Corresponds to the <methodResponse> tag. + public const String METHOD_RESPONSE = "methodResponse"; + /// Fault response token. + /// Corresponds to the <fault> tag. + public const String FAULT = "fault"; + /// FaultCode token. + /// Corresponds to the <faultCode> tag. + public const String FAULT_CODE = "faultCode"; + /// FaultString token. + /// Corresponds to the <faultString> tag. + public const String FAULT_STRING = "faultString"; + } +} + + diff --git a/libsecondlife-cs/libsecondlife.csproj b/libsecondlife-cs/libsecondlife.csproj index ffb792d5..92d7c64a 100644 --- a/libsecondlife-cs/libsecondlife.csproj +++ b/libsecondlife-cs/libsecondlife.csproj @@ -79,6 +79,11 @@ AssemblyName = "System.Xml" HintPath = "..\..\WINDOWS\Microsoft.NET\Framework\v1.1.4322\System.XML.dll" /> + diff --git a/libsecondlife-cs/libsecondlife.sln b/libsecondlife-cs/libsecondlife.sln index 423a02f9..612e1ec1 100644 --- a/libsecondlife-cs/libsecondlife.sln +++ b/libsecondlife-cs/libsecondlife.sln @@ -11,6 +11,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "name2key", "examples\name2k ProjectSection(ProjectDependencies) = postProject EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XmlRpcCS", "XmlRpcCS\XmlRpcCS.csproj", "{410F8877-F1E1-4696-ABC7-4339189EECC3}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject Global GlobalSection(SolutionConfiguration) = preSolution Debug = Debug @@ -29,6 +33,10 @@ Global {66FFD34E-652C-4EF5-81FE-06AD011169D2}.Debug.Build.0 = Debug|.NET {66FFD34E-652C-4EF5-81FE-06AD011169D2}.Release.ActiveCfg = Release|.NET {66FFD34E-652C-4EF5-81FE-06AD011169D2}.Release.Build.0 = Release|.NET + {410F8877-F1E1-4696-ABC7-4339189EECC3}.Debug.ActiveCfg = Debug|.NET + {410F8877-F1E1-4696-ABC7-4339189EECC3}.Debug.Build.0 = Debug|.NET + {410F8877-F1E1-4696-ABC7-4339189EECC3}.Release.ActiveCfg = Release|.NET + {410F8877-F1E1-4696-ABC7-4339189EECC3}.Release.Build.0 = Release|.NET EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution EndGlobalSection