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