Moved programs into the Programs folder

git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@1958 52acb1d6-8a22-11de-b505-999d5b087335
This commit is contained in:
John Hurliman
2008-07-22 23:00:32 +00:00
parent c1f3d6a3c2
commit 660db80eb5
82 changed files with 0 additions and 667 deletions

732
Programs/SLProxy/Analyst.cs Normal file
View File

@@ -0,0 +1,732 @@
/*
* Analyst.cs: proxy that makes packet inspection and modifcation interactive
* See the README for usage instructions.
*
* Copyright (c) 2006 Austin Jennings
* Modified by "qode" and "mcortez" on December 21st, 2006 to work with the new
* pregen
* All rights reserved.
*
* - Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Neither the name of the openmetaverse.org nor the names
* of its contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
using SLProxy;
using OpenMetaverse;
using Nwc.XmlRpc;
using OpenMetaverse.Packets;
using System.Reflection;
using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
public class Analyst : ProxyPlugin
{
private ProxyFrame frame;
private Proxy proxy;
private Hashtable loggedPackets = new Hashtable();
private string logGrep = null;
private Hashtable modifiedPackets = new Hashtable();
private Assembly openmvAssembly;
public Analyst(ProxyFrame frame)
{
this.frame = frame;
this.proxy = frame.proxy;
}
public override void Init()
{
openmvAssembly = Assembly.Load("OpenMetaverse");
if (openmvAssembly == null) throw new Exception("Assembly load exception");
// build the table of /command delegates
InitializeCommandDelegates();
// handle command line arguments
foreach (string arg in frame.Args)
if (arg == "--log-all")
LogAll();
Console.WriteLine("Analyst loaded");
}
// InitializeCommandDelegates: configure Analyst's commands
private void InitializeCommandDelegates()
{
frame.AddCommand("/log", new ProxyFrame.CommandDelegate(CmdLog));
frame.AddCommand("/-log", new ProxyFrame.CommandDelegate(CmdNoLog));
frame.AddCommand("/grep", new ProxyFrame.CommandDelegate(CmdGrep));
frame.AddCommand("/set", new ProxyFrame.CommandDelegate(CmdSet));
frame.AddCommand("/-set", new ProxyFrame.CommandDelegate(CmdNoSet));
frame.AddCommand("/inject", new ProxyFrame.CommandDelegate(CmdInject));
frame.AddCommand("/in", new ProxyFrame.CommandDelegate(CmdInject));
}
private static PacketType packetTypeFromName(string name)
{
Type packetTypeType = typeof(PacketType);
System.Reflection.FieldInfo f = packetTypeType.GetField(name);
if (f == null) throw new ArgumentException("Bad packet type");
return (PacketType)Enum.ToObject(packetTypeType, (int)f.GetValue(packetTypeType));
}
// CmdLog: handle a /log command
private void CmdLog(string[] words)
{
if (words.Length != 2)
SayToUser("Usage: /log <packet name>");
else if (words[1] == "*")
{
LogAll();
SayToUser("logging all packets");
}
else
{
PacketType pType;
try
{
pType = packetTypeFromName(words[1]);
}
catch (ArgumentException)
{
SayToUser("Bad packet name: " + words[1]);
return;
}
loggedPackets[pType] = null;
proxy.AddDelegate(pType, Direction.Incoming, new PacketDelegate(LogPacketIn));
proxy.AddDelegate(pType, Direction.Outgoing, new PacketDelegate(LogPacketOut));
SayToUser("logging " + words[1]);
}
}
// CmdNoLog: handle a /-log command
private void CmdNoLog(string[] words)
{
if (words.Length != 2)
SayToUser("Usage: /-log <packet name>");
else if (words[1] == "*")
{
NoLogAll();
SayToUser("stopped logging all packets");
}
else
{
PacketType pType = packetTypeFromName(words[1]);
loggedPackets.Remove(pType);
proxy.RemoveDelegate(pType, Direction.Incoming, new PacketDelegate(LogPacketIn));
proxy.RemoveDelegate(pType, Direction.Outgoing, new PacketDelegate(LogPacketOut));
SayToUser("stopped logging " + words[1]);
}
}
// CmdGrep: handle a /grep command
private void CmdGrep(string[] words) {
if (words.Length == 1) {
logGrep = null;
SayToUser("stopped filtering logs");
} else {
string[] regexArray = new string[words.Length - 1];
Array.Copy(words, 1, regexArray, 0, words.Length - 1);
logGrep = String.Join(" ", regexArray);
SayToUser("filtering log with " + logGrep);
}
}
// CmdSet: handle a /set command
private void CmdSet(string[] words)
{
if (words.Length < 5)
SayToUser("Usage: /set <packet name> <block> <field> <value>");
else
{
PacketType pType;
try
{
pType = packetTypeFromName(words[1]);
}
catch (ArgumentException)
{
SayToUser("Bad packet name: " + words[1]);
return;
}
string[] valueArray = new string[words.Length - 4];
Array.Copy(words, 4, valueArray, 0, words.Length - 4);
string valueString = String.Join(" ", valueArray);
object value;
try
{
value = MagicCast(words[1], words[2], words[3], valueString);
}
catch (Exception e)
{
SayToUser(e.Message);
return;
}
Hashtable fields;
if (modifiedPackets.Contains(pType))
fields = (Hashtable)modifiedPackets[pType];
else
fields = new Hashtable();
fields[new BlockField(words[2], words[3])] = value;
modifiedPackets[pType] = fields;
proxy.AddDelegate(pType, Direction.Incoming, new PacketDelegate(ModifyIn));
proxy.AddDelegate(pType, Direction.Outgoing, new PacketDelegate(ModifyOut));
SayToUser("setting " + words[1] + "." + words[2] + "." + words[3] + " = " + valueString);
}
}
// CmdNoSet: handle a /-set command
private void CmdNoSet(string[] words)
{
if (words.Length == 2 && words[1] == "*")
{
foreach (PacketType pType in modifiedPackets.Keys)
{
proxy.RemoveDelegate(pType, Direction.Incoming, new PacketDelegate(ModifyIn));
proxy.RemoveDelegate(pType, Direction.Outgoing, new PacketDelegate(ModifyOut));
}
modifiedPackets = new Hashtable();
SayToUser("stopped setting all fields");
}
else if (words.Length == 4)
{
PacketType pType;
try
{
pType = packetTypeFromName(words[1]);
}
catch (ArgumentException)
{
SayToUser("Bad packet name: " + words[1]);
return;
}
if (modifiedPackets.Contains(pType))
{
Hashtable fields = (Hashtable)modifiedPackets[pType];
fields.Remove(new BlockField(words[2], words[3]));
if (fields.Count == 0)
{
modifiedPackets.Remove(pType);
proxy.RemoveDelegate(pType, Direction.Incoming, new PacketDelegate(ModifyIn));
proxy.RemoveDelegate(pType, Direction.Outgoing, new PacketDelegate(ModifyOut));
}
}
SayToUser("stopped setting " + words[1] + "." + words[2] + "." + words[3]);
}
else
SayToUser("Usage: /-set <packet name> <block> <field>");
}
// CmdInject: handle an /inject command
private void CmdInject(string[] words)
{
if (words.Length < 2)
SayToUser("Usage: /inject <packet file> [value]");
else
{
string[] valueArray = new string[words.Length - 2];
Array.Copy(words, 2, valueArray, 0, words.Length - 2);
string value = String.Join(" ", valueArray);
FileStream fs = null;
StreamReader sr = null;
Direction direction = Direction.Incoming;
string name = null;
string block = null;
object blockObj = null;
Type packetClass = null;
Packet packet = null;
try
{
fs = File.OpenRead(words[1] + ".packet");
sr = new StreamReader(fs);
string line;
while ((line = sr.ReadLine()) != null)
{
Match match;
if (name == null)
{
match = (new Regex(@"^\s*(in|out)\s+(\w+)\s*$")).Match(line);
if (!match.Success)
{
SayToUser("expecting direction and packet name, got: " + line);
return;
}
string lineDir = match.Groups[1].Captures[0].ToString();
string lineName = match.Groups[2].Captures[0].ToString();
if (lineDir == "in")
direction = Direction.Incoming;
else if (lineDir == "out")
direction = Direction.Outgoing;
else
{
SayToUser("expecting 'in' or 'out', got: " + line);
return;
}
name = lineName;
packetClass = openmvAssembly.GetType("OpenMetaverse.Packets." + name + "Packet");
if (packetClass == null) throw new Exception("Couldn't get class " + name + "Packet");
ConstructorInfo ctr = packetClass.GetConstructor(new Type[] { });
if (ctr == null) throw new Exception("Couldn't get suitable constructor for " + name + "Packet");
packet = (Packet)ctr.Invoke(new object[] { });
//Console.WriteLine("Created new " + name + "Packet");
}
else
{
match = (new Regex(@"^\s*\[(\w+)\]\s*$")).Match(line);
if (match.Success)
{
block = match.Groups[1].Captures[0].ToString();
FieldInfo blockField = packetClass.GetField(block);
if (blockField == null) throw new Exception("Couldn't get " + name + "Packet." + block);
Type blockClass = blockField.FieldType;
if (blockClass.IsArray)
{
blockClass = blockClass.GetElementType();
ConstructorInfo ctr = blockClass.GetConstructor(new Type[] { });
if (ctr == null) throw new Exception("Couldn't get suitable constructor for " + blockClass.Name);
blockObj = ctr.Invoke(new object[] { });
object[] arr = (object[])blockField.GetValue(packet);
object[] narr = (object[])Array.CreateInstance(blockClass, arr.Length + 1);
Array.Copy(arr, narr, arr.Length);
narr[arr.Length] = blockObj;
blockField.SetValue(packet, narr);
//Console.WriteLine("Added block "+block);
}
else
{
blockObj = blockField.GetValue(packet);
}
if (blockObj == null) throw new Exception("Got " + name + "Packet." + block + " == null");
//Console.WriteLine("Got block " + name + "Packet." + block);
continue;
}
if (block == null)
{
SayToUser("expecting block name, got: " + line);
return;
}
match = (new Regex(@"^\s*(\w+)\s*=\s*(.*)$")).Match(line);
if (match.Success)
{
string lineField = match.Groups[1].Captures[0].ToString();
string lineValue = match.Groups[2].Captures[0].ToString();
object fval;
//FIXME: use of MagicCast inefficient
if (lineValue == "$Value")
fval = MagicCast(name, block, lineField, value);
else if (lineValue == "$UUID")
fval = LLUUID.Random();
else if (lineValue == "$AgentID")
fval = frame.AgentID;
else if (lineValue == "$SessionID")
fval = frame.SessionID;
else
fval = MagicCast(name, block, lineField, lineValue);
MagicSetField(blockObj, lineField, fval);
continue;
}
SayToUser("expecting block name or field, got: " + line);
return;
}
}
if (name == null)
{
SayToUser("expecting direction and packet name, got EOF");
return;
}
packet.Header.Flags |= Helpers.MSG_RELIABLE;
//if (protocolManager.Command(name).Encoded)
// packet.Header.Flags |= Helpers.MSG_ZEROCODED;
proxy.InjectPacket(packet, direction);
SayToUser("injected " + words[1]);
}
catch (Exception e)
{
SayToUser("failed to inject " + words[1] + ": " + e.Message);
Console.WriteLine("failed to inject " + words[1] + ": " + e.Message + "\n" + e.StackTrace);
}
finally
{
if (fs != null)
fs.Close();
if (sr != null)
sr.Close();
}
}
}
// SayToUser: send a message to the user as in-world chat
private void SayToUser(string message)
{
ChatFromSimulatorPacket packet = new ChatFromSimulatorPacket();
packet.ChatData.FromName = Helpers.StringToField("Analyst");
packet.ChatData.SourceID = LLUUID.Random();
packet.ChatData.OwnerID = frame.AgentID;
packet.ChatData.SourceType = (byte)2;
packet.ChatData.ChatType = (byte)1;
packet.ChatData.Audible = (byte)1;
packet.ChatData.Position = new LLVector3(0, 0, 0);
packet.ChatData.Message = Helpers.StringToField(message);
proxy.InjectPacket(packet, Direction.Incoming);
}
// BlockField: product type for a block name and field name
private struct BlockField
{
public string block;
public string field;
public BlockField(string block, string field)
{
this.block = block;
this.field = field;
}
}
private static void MagicSetField(object obj, string field, object val)
{
Type cls = obj.GetType();
FieldInfo fieldInf = cls.GetField(field);
if (fieldInf == null)
{
PropertyInfo prop = cls.GetProperty(field);
if (prop == null) throw new Exception("Couldn't find field " + cls.Name + "." + field);
prop.SetValue(obj, val, null);
//throw new Exception("FIXME: can't set properties");
}
else
{
fieldInf.SetValue(obj, val);
}
}
// MagicCast: given a packet/block/field name and a string, convert the string to a value of the appropriate type
private object MagicCast(string name, string block, string field, string value)
{
Type packetClass = openmvAssembly.GetType("OpenMetaverse.Packets." + name + "Packet");
if (packetClass == null) throw new Exception("Couldn't get class " + name + "Packet");
FieldInfo blockField = packetClass.GetField(block);
if (blockField == null) throw new Exception("Couldn't get " + name + "Packet." + block);
Type blockClass = blockField.FieldType;
if (blockClass.IsArray) blockClass = blockClass.GetElementType();
// Console.WriteLine("DEBUG: " + blockClass.Name);
FieldInfo fieldField = blockClass.GetField(field); PropertyInfo fieldProp = null;
Type fieldClass = null;
if (fieldField == null)
{
fieldProp = blockClass.GetProperty(field);
if (fieldProp == null) throw new Exception("Couldn't get " + name + "Packet." + block + "." + field);
fieldClass = fieldProp.PropertyType;
}
else
{
fieldClass = fieldField.FieldType;
}
try
{
if (fieldClass == typeof(byte))
{
return Convert.ToByte(value);
}
else if (fieldClass == typeof(ushort))
{
return Convert.ToUInt16(value);
}
else if (fieldClass == typeof(uint))
{
return Convert.ToUInt32(value);
}
else if (fieldClass == typeof(ulong))
{
return Convert.ToUInt64(value);
}
else if (fieldClass == typeof(sbyte))
{
return Convert.ToSByte(value);
}
else if (fieldClass == typeof(short))
{
return Convert.ToInt16(value);
}
else if (fieldClass == typeof(int))
{
return Convert.ToInt32(value);
}
else if (fieldClass == typeof(long))
{
return Convert.ToInt64(value);
}
else if (fieldClass == typeof(float))
{
return Convert.ToSingle(value);
}
else if (fieldClass == typeof(double))
{
return Convert.ToDouble(value);
}
else if (fieldClass == typeof(LLUUID))
{
return new LLUUID(value);
}
else if (fieldClass == typeof(bool))
{
if (value.ToLower() == "true")
return true;
else if (value.ToLower() == "false")
return false;
else
throw new Exception();
}
else if (fieldClass == typeof(byte[]))
{
return Helpers.StringToField(value);
}
else if (fieldClass == typeof(LLVector3))
{
LLVector3 result;
if(LLVector3.TryParse(value, out result))
return result;
else
throw new Exception();
}
else if (fieldClass == typeof(LLVector3d))
{
LLVector3d result;
if (LLVector3d.TryParse(value, out result))
return result;
else
throw new Exception();
}
else if (fieldClass == typeof(LLVector4))
{
LLVector4 result;
if (LLVector4.TryParse(value, out result))
return result;
else
throw new Exception();
}
else if (fieldClass == typeof(LLQuaternion))
{
LLQuaternion result;
if (LLQuaternion.TryParse(value, out result))
return result;
else
throw new Exception();
}
else
{
throw new Exception("unsupported field type " + fieldClass);
}
}
catch
{
throw new Exception("unable to interpret " + value + " as " + fieldClass);
}
}
// ModifyIn: modify an incoming packet
private Packet ModifyIn(Packet packet, IPEndPoint endPoint)
{
return Modify(packet, endPoint, Direction.Incoming);
}
// ModifyOut: modify an outgoing packet
private Packet ModifyOut(Packet packet, IPEndPoint endPoint)
{
return Modify(packet, endPoint, Direction.Outgoing);
}
// Modify: modify a packet
private Packet Modify(Packet packet, IPEndPoint endPoint, Direction direction)
{
if (modifiedPackets.Contains(packet.Type))
{
try
{
Hashtable changes = (Hashtable)modifiedPackets[packet.Type];
Type packetClass = packet.GetType();
foreach (BlockField bf in changes.Keys)
{
//FIXME: support variable blocks
FieldInfo blockField = packetClass.GetField(bf.block);
//Type blockClass = blockField.FieldType;
object blockObject = blockField.GetValue(packet);
MagicSetField(blockObject, bf.field, changes[blockField]);
}
}
catch (Exception e)
{
Console.WriteLine("failed to modify " + packet.Type + ": " + e.Message);
Console.WriteLine(e.StackTrace);
}
}
return packet;
}
// LogPacketIn: log an incoming packet
private Packet LogPacketIn(Packet packet, IPEndPoint endPoint)
{
LogPacket(packet, endPoint, Direction.Incoming);
return packet;
}
// LogPacketOut: log an outgoing packet
private Packet LogPacketOut(Packet packet, IPEndPoint endPoint)
{
LogPacket(packet, endPoint, Direction.Outgoing);
return packet;
}
// LogAll: register logging delegates for all packets
private void LogAll()
{
Type packetTypeType = typeof(PacketType);
System.Reflection.MemberInfo[] packetTypes = packetTypeType.GetMembers();
for (int i = 0; i < packetTypes.Length; i++)
{
if (packetTypes[i].MemberType == System.Reflection.MemberTypes.Field && packetTypes[i].DeclaringType == packetTypeType)
{
string name = packetTypes[i].Name;
PacketType pType;
try
{
pType = packetTypeFromName(name);
}
catch (Exception)
{
continue;
}
loggedPackets[pType] = null;
proxy.AddDelegate(pType, Direction.Incoming, new PacketDelegate(LogPacketIn));
proxy.AddDelegate(pType, Direction.Outgoing, new PacketDelegate(LogPacketOut));
}
}
}
// NoLogAll: unregister logging delegates for all packets
private void NoLogAll()
{
Type packetTypeType = typeof(PacketType);
System.Reflection.MemberInfo[] packetTypes = packetTypeType.GetMembers();
for (int i = 0; i < packetTypes.Length; i++)
{
if (packetTypes[i].MemberType == System.Reflection.MemberTypes.Field && packetTypes[i].DeclaringType == packetTypeType)
{
string name = packetTypes[i].Name;
PacketType pType;
try
{
pType = packetTypeFromName(name);
}
catch (Exception)
{
continue;
}
loggedPackets.Remove(pType);
proxy.RemoveDelegate(pType, Direction.Incoming, new PacketDelegate(LogPacketIn));
proxy.RemoveDelegate(pType, Direction.Outgoing, new PacketDelegate(LogPacketOut));
}
}
}
// LogPacket: dump a packet to the console
private void LogPacket(Packet packet, IPEndPoint endPoint, Direction direction)
{
string packetText = packet.ToString();
if (logGrep == null || (logGrep != null && Regex.IsMatch(packetText, logGrep)))
{
Console.WriteLine("{0} {1,21} {2,5} {3}{4}{5}"
, direction == Direction.Incoming ? "<--" : "-->"
, endPoint
, packet.Header.Sequence
, InterpretOptions(packet.Header.Flags)
, Environment.NewLine
, packetText
);
}
}
// InterpretOptions: produce a string representing a packet's header options
private static string InterpretOptions(byte options)
{
return "["
+ ((options & Helpers.MSG_APPENDED_ACKS) != 0 ? "Ack" : " ")
+ " "
+ ((options & Helpers.MSG_RESENT) != 0 ? "Res" : " ")
+ " "
+ ((options & Helpers.MSG_RELIABLE) != 0 ? "Rel" : " ")
+ " "
+ ((options & Helpers.MSG_ZEROCODED) != 0 ? "Zer" : " ")
+ "]"
;
}
}

View File

@@ -0,0 +1,107 @@
/*
* ChatConsole.cs: sample SLProxy appliation that writes chat to the console.
* Typing on the console will send chat to the grid.
*
* Copyright (c) 2006 Austin Jennings
* Modified by Andrew Ortman ("qode") on Decemeber 21, 2006 to work with the new pregen proxy.
* All rights reserved.
*
* - Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Neither the name of the openmetaverse.org nor the names
* of its contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
using SLProxy;
using OpenMetaverse;
using OpenMetaverse.Packets;
using Nwc.XmlRpc;
using System;
using System.Collections;
using System.Net;
using System.Threading;
public class ChatConsole {
private static Proxy proxy;
private static LLUUID agentID;
private static LLUUID sessionID;
public static void Main(string[] args) {
// configure the proxy
ProxyConfig proxyConfig = new ProxyConfig("ChatConsole V2", "Austin Jennings / Andrew Ortman", args);
proxy = new Proxy(proxyConfig);
// set a delegate for when the client logs in
proxy.SetLoginResponseDelegate(new XmlRpcResponseDelegate(Login));
// add a delegate for incoming chat
proxy.AddDelegate(PacketType.ChatFromSimulator, Direction.Incoming, new PacketDelegate(ChatFromSimulator));
// start the proxy
proxy.Start();
}
private static void Login(XmlRpcResponse response) {
Hashtable values = (Hashtable)response.Value;
if (values.Contains("agent_id") && values.Contains("session_id")) {
// remember our agentID and sessionID
agentID = new LLUUID((string)values["agent_id"]);
sessionID = new LLUUID((string)values["session_id"]);
// start a new thread that reads lines from the console
(new Thread(new ThreadStart(ReadFromConsole))).Start();
}
}
private static void ReadFromConsole() {
// send text from the console in an infinite loop
for (;;) {
// read a line from the console
string message = Console.ReadLine();
// construct a ChatFromViewer packet
ChatFromViewerPacket chat = new ChatFromViewerPacket();
chat.ChatData.Channel = 0;
chat.ChatData.Message = Helpers.StringToField(message);
chat.ChatData.Type = (byte)1;
chat.AgentData.AgentID = agentID;
chat.AgentData.SessionID = sessionID;
// inject the packet
proxy.InjectPacket((Packet)chat, Direction.Outgoing);
}
}
private static Packet ChatFromSimulator(Packet packet, IPEndPoint sim) {
// deconstruct the packet
ChatFromSimulatorPacket chat = (ChatFromSimulatorPacket)packet;
string message = Helpers.FieldToUTF8String(chat.ChatData.Message);
string name = Helpers.FieldToUTF8String(chat.ChatData.FromName);
byte audible = chat.ChatData.Audible;
byte type = chat.ChatData.ChatType;
// if this was a normal, audible message, write it to the console
if (audible != 0 && (type == 0 || type == 1 || type == 2))
Console.WriteLine(name + ": " + message);
// return the packet unmodified
return packet;
}
}

View File

@@ -0,0 +1,121 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectType>Local</ProjectType>
<ProductVersion>8.0.50727</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{D8ECCBE1-AC71-4054-AAA6-2D50E5629504}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ApplicationIcon>
</ApplicationIcon>
<AssemblyKeyContainerName>
</AssemblyKeyContainerName>
<AssemblyName>ChatConsole</AssemblyName>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
<DefaultClientScript>JScript</DefaultClientScript>
<DefaultHTMLPageLayout>Grid</DefaultHTMLPageLayout>
<DefaultTargetSchema>IE50</DefaultTargetSchema>
<DelaySign>false</DelaySign>
<OutputType>Exe</OutputType>
<RootNamespace>ChatConsole</RootNamespace>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<StartupObject>
</StartupObject>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>..\bin\</OutputPath>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>
</DocumentationFile>
<DebugSymbols>true</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
<NoWarn>
</NoWarn>
<Optimize>false</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>4</WarningLevel>
<DebugType>full</DebugType>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>..\..\bin\</OutputPath>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>TRACE</DefineConstants>
<DocumentationFile>
</DocumentationFile>
<DebugSymbols>false</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
<NoWarn>
</NoWarn>
<Optimize>true</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>4</WarningLevel>
<DebugType>none</DebugType>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release-docs|AnyCPU' ">
<OutputPath>bin\Release-docs\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<BaseAddress>285212672</BaseAddress>
<Optimize>true</Optimize>
<DebugType>
</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<CodeAnalysisRuleAssemblies>C:\Program Files\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules</CodeAnalysisRuleAssemblies>
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<Reference Include="System">
<Name>System</Name>
</Reference>
<Reference Include="System.Data">
<Name>System.Data</Name>
</Reference>
<Reference Include="System.Xml">
<Name>System.XML</Name>
</Reference>
<ProjectReference Include="..\libsecondlife\OpenMetaverse.csproj">
<Project>{D9CDEDFB-8169-4B03-B57F-0DF638F044EC}</Project>
<Name>OpenMetaverse</Name>
</ProjectReference>
<ProjectReference Include="SLProxy.csproj">
<Name>SLProxy</Name>
<Project>{E4115DC9-FC88-47D6-B3B6-2400AD19B80D}</Project>
<Package>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</Package>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="ChatConsole.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,809 @@
/*
* ClientAO.cs: SLProxy application that acts as a client side animation overrider.
* The application will start and stop animations corresponding to the movements
* of the avatar on screen.
*
* Copyright (c) 2007 Gilbert Roulot
* All rights reserved.
*
* - Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Neither the name of the Second Life Reverse Engineering Team nor the names
* of its contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
using SLProxy;
using libsecondlife;
using libsecondlife.Packets;
using libsecondlife.StructuredData;
using Nwc.XmlRpc;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.IO;
using System.Reflection;
public class ClientAO : ProxyPlugin
{
private ProxyFrame frame;
private Proxy proxy;
private LLUUID[] wetikonanims = {
Animations.WALK,
Animations.RUN,
Animations.CROUCHWALK,
Animations.FLY,
Animations.TURNLEFT,
Animations.TURNRIGHT,
Animations.JUMP,
Animations.HOVER_UP,
Animations.CROUCH,
Animations.HOVER_DOWN,
Animations.STAND,
Animations.STAND_1,
Animations.STAND_2,
Animations.STAND_3,
Animations.STAND_4,
Animations.HOVER,
Animations.SIT,
Animations.PRE_JUMP,
Animations.FALLDOWN,
Animations.LAND,
Animations.STANDUP,
Animations.FLYSLOW,
Animations.SIT_GROUND_staticRAINED,
LLUUID.Zero, //swimming doesnt exist
LLUUID.Zero,
LLUUID.Zero,
LLUUID.Zero
};
private string[] wetikonanimnames = {
"walk",
"run",
"crouch walk",
"fly",
"turn left",
"turn right",
"jump",
"hover up",
"crouch",
"hover down",
"stand",
"stand 2",
"stand 3",
"stand 4",
"stand 5",
"hover",
"sit",
"pre jump",
"fall down",
"land",
"stand up",
"fly slow",
"sit on ground",
"swim (ignored)", //swimming doesnt exist
"swim (ignored)",
"swim (ignored)",
"swim (ignored)"
};
private Dictionary<LLUUID, string> animuid2name;
//private Assembly libslAssembly;
#region Packet delegates members
private PacketDelegate _packetDelegate;
private PacketDelegate packetDelegate
{
get
{
if (_packetDelegate == null)
{
_packetDelegate = new PacketDelegate(AnimationPacketHandler);
}
return _packetDelegate;
}
}
private PacketDelegate _inventoryPacketDelegate;
private PacketDelegate inventoryPacketDelegate
{
get
{
if (_inventoryPacketDelegate == null)
{
_inventoryPacketDelegate = new PacketDelegate(InventoryDescendentsHandler);
}
return _inventoryPacketDelegate;
}
}
private PacketDelegate _transferPacketDelegate;
private PacketDelegate transferPacketDelegate
{
get
{
if (_transferPacketDelegate == null)
{
_transferPacketDelegate = new PacketDelegate(TransferPacketHandler);
}
return _transferPacketDelegate;
}
}
private PacketDelegate _transferInfoDelegate;
private PacketDelegate transferInfoDelegate
{
get
{
if (_transferInfoDelegate == null)
{
_transferInfoDelegate = new PacketDelegate(TransferInfoHandler);
}
return _transferInfoDelegate;
}
}
#endregion
//map of built in SL animations and their overrides
private Dictionary<LLUUID,LLUUID> overrides = new Dictionary<LLUUID,LLUUID>();
//list of animations currently running
private Dictionary<LLUUID, int> SignaledAnimations = new Dictionary<LLUUID, int>();
//playing status of animations'override animation
private Dictionary<LLUUID, bool> overrideanimationisplaying;
//Current inventory path search
string[] searchPath;
//Search level
int searchLevel;
//Current folder
LLUUID currentFolder;
// Number of directory descendents received
int nbdescendantsreceived;
//List of items in the current folder
Dictionary<string,InventoryItem> currentFolderItems;
//Asset download request ID
LLUUID assetdownloadID;
//Downloaded bytes so far
int downloadedbytes;
//size of download
int downloadsize;
//data buffer
byte[] buffer;
public ClientAO(ProxyFrame frame)
{
this.frame = frame;
this.proxy = frame.proxy;
}
//Initialise the plugin
public override void Init()
{
//libslAssembly = Assembly.Load("libsecondlife");
//if (libslAssembly == null) throw new Exception("Assembly load exception");
// build the table of /command delegates
InitializeCommandDelegates();
SayToUser("ClientAO loaded");
}
// InitializeCommandDelegates: configure ClientAO's commands
private void InitializeCommandDelegates()
{
//The ClientAO responds to command beginning with /ao
frame.AddCommand("/ao", new ProxyFrame.CommandDelegate(CmdAO));
}
//Process commands from the user
private void CmdAO(string[] words) {
if (words.Length < 2)
{
SayToUser("Usage: /ao on/off/notecard path");
}
else if (words[1] == "on")
{
//Turn AO on
AOOn();
SayToUser("AO started");
}
else if (words[1] == "off")
{
//Turn AO off
AOOff();
SayToUser("AO stopped");
}
else
{
//Load notecard from path
//exemple: /ao Objects/My AOs/wetikon/config.txt
string[] tmp = new string[words.Length - 1];
//join the arguments together with spaces, to
//take care of folder and item names with spaces in them
for (int i = 1; i < words.Length; i++)
{
tmp[i - 1] = words[i];
}
// add a delegate to monitor inventory infos
proxy.AddDelegate(PacketType.InventoryDescendents, Direction.Incoming, this.inventoryPacketDelegate);
RequestFindObjectByPath(frame.InventoryRoot, String.Join(" ", tmp));
}
}
private void AOOn()
{
// add a delegate to track agent movements
proxy.AddDelegate(PacketType.AvatarAnimation, Direction.Incoming, this.packetDelegate);
}
private void AOOff()
{
// remove the delegate to track agent movements
proxy.RemoveDelegate(PacketType.AvatarAnimation, Direction.Incoming, this.packetDelegate);
//Stop all override animations
foreach (LLUUID tmp in overrides.Values)
{
Animate(tmp, false);
}
}
// Inventory functions
//start requesting an item by its path
public void RequestFindObjectByPath(LLUUID baseFolder, string path)
{
if (path == null || path.Length == 0)
throw new ArgumentException("Empty path is not supported");
currentFolder = baseFolder;
//split path by '/'
searchPath = path.Split('/');
//search for first element in the path
searchLevel = 0;
// Start the search
RequestFolderContents(baseFolder,
true,
(searchPath.Length == 1) ? true : false,
InventorySortOrder.ByName);
}
//request a folder content
public void RequestFolderContents(LLUUID folder, bool folders, bool items,
InventorySortOrder order)
{
//empty the dictionnary containing current folder items by name
currentFolderItems = new Dictionary<string, InventoryItem>();
//reset the number of descendants received
nbdescendantsreceived = 0;
//build a packet to request the content
FetchInventoryDescendentsPacket fetch = new FetchInventoryDescendentsPacket();
fetch.AgentData.AgentID = frame.AgentID;
fetch.AgentData.SessionID = frame.SessionID;
fetch.InventoryData.FetchFolders = folders;
fetch.InventoryData.FetchItems = items;
fetch.InventoryData.FolderID = folder;
fetch.InventoryData.OwnerID = frame.AgentID; //is it correct?
fetch.InventoryData.SortOrder = (int)order;
//send packet to SL
proxy.InjectPacket(fetch, Direction.Outgoing);
}
//process the reply from SL
private Packet InventoryDescendentsHandler(Packet packet, IPEndPoint sim)
{
bool intercept = false;
InventoryDescendentsPacket reply = (InventoryDescendentsPacket)packet;
if (reply.AgentData.Descendents > 0
&& reply.AgentData.FolderID == currentFolder)
{
//SayToUser("nb descendents: " + reply.AgentData.Descendents);
//this packet concerns the folder we asked for
if (reply.FolderData[0].FolderID != LLUUID.Zero
&& searchLevel < searchPath.Length - 1)
{
nbdescendantsreceived += reply.FolderData.Length;
//SayToUser("nb received: " + nbdescendantsreceived);
//folders are present, and we are not at end of path.
//look at them
for (int i = 0; i < reply.FolderData.Length; i++)
{
//SayToUser("Folder: " + Helpers.FieldToUTF8String(reply.FolderData[i].Name));
if (searchPath[searchLevel] == Helpers.FieldToUTF8String(reply.FolderData[i].Name)) {
//We found the next folder in the path
currentFolder = reply.FolderData[i].FolderID;
if (searchLevel < searchPath.Length - 1)
{
// ask for next item in path
searchLevel++;
RequestFolderContents(currentFolder,
true,
(searchLevel < searchPath.Length - 1) ? false : true,
InventorySortOrder.ByName);
//Jump to end
goto End;
}
}
}
if (nbdescendantsreceived >= reply.AgentData.Descendents)
{
//We have not found the folder. The user probably mistyped it
SayToUser("Didn't find folder " + searchPath[searchLevel]);
//Stop looking at packets
proxy.RemoveDelegate(PacketType.InventoryDescendents, Direction.Incoming, this.inventoryPacketDelegate);
}
}
else if (searchLevel < searchPath.Length - 1)
{
//There are no folders in the packet ; but we are looking for one!
//We have not found the folder. The user probably mistyped it
SayToUser("Didn't find folder " + searchPath[searchLevel]);
//Stop looking at packets
proxy.RemoveDelegate(PacketType.InventoryDescendents, Direction.Incoming, this.inventoryPacketDelegate);
}
else
{
//There are folders in the packet. And we are at the end of
//the path, count their number in nbdescendantsreceived
nbdescendantsreceived += reply.FolderData.Length;
//SayToUser("nb received: " + nbdescendantsreceived);
}
if (reply.ItemData[0].ItemID != LLUUID.Zero
&& searchLevel == searchPath.Length - 1)
{
//there are items returned and we are looking for one
//(end of search path)
//count them
nbdescendantsreceived += reply.ItemData.Length;
//SayToUser("nb received: " + nbdescendantsreceived);
for (int i = 0; i < reply.ItemData.Length; i++)
{
//we are going to store info on all items. we'll need
//it to get the asset ID of animations refered to by the
//configuration notecard
if (reply.ItemData[i].ItemID != LLUUID.Zero)
{
InventoryItem item = CreateInventoryItem((InventoryType)reply.ItemData[i].InvType, reply.ItemData[i].ItemID);
item.ParentUUID = reply.ItemData[i].FolderID;
item.CreatorID = reply.ItemData[i].CreatorID;
item.AssetType = (AssetType)reply.ItemData[i].Type;
item.AssetUUID = reply.ItemData[i].AssetID;
item.CreationDate = Helpers.UnixTimeToDateTime((uint)reply.ItemData[i].CreationDate);
item.Description = Helpers.FieldToUTF8String(reply.ItemData[i].Description);
item.Flags = (uint)reply.ItemData[i].Flags;
item.Name = Helpers.FieldToUTF8String(reply.ItemData[i].Name);
item.GroupID = reply.ItemData[i].GroupID;
item.GroupOwned = reply.ItemData[i].GroupOwned;
item.Permissions = new Permissions(
reply.ItemData[i].BaseMask,
reply.ItemData[i].EveryoneMask,
reply.ItemData[i].GroupMask,
reply.ItemData[i].NextOwnerMask,
reply.ItemData[i].OwnerMask);
item.SalePrice = reply.ItemData[i].SalePrice;
item.SaleType = (SaleType)reply.ItemData[i].SaleType;
item.OwnerID = reply.AgentData.OwnerID;
//SayToUser("item in folder: " + item.Name);
//Add the item to the name -> item hash
currentFolderItems.Add(item.Name, item);
}
}
if (nbdescendantsreceived >= reply.AgentData.Descendents)
{
//We have received all the items in the last folder
//Let's look for the item we are looking for
if (currentFolderItems.ContainsKey(searchPath[searchLevel]))
{
//We found what we where looking for
//Stop looking at packets
proxy.RemoveDelegate(PacketType.InventoryDescendents, Direction.Incoming, this.inventoryPacketDelegate);
//Download the notecard
assetdownloadID = RequestInventoryAsset(currentFolderItems[searchPath[searchLevel]]);
}
else
{
//We didnt find the item, the user probably mistyped its name
SayToUser("Didn't find notecard " + searchPath[searchLevel]);
//TODO: keep looking for a moment, or else reply packets may still
//come in case of a very large inventory folder
//Stop looking at packets
proxy.RemoveDelegate(PacketType.InventoryDescendents, Direction.Incoming, this.inventoryPacketDelegate);
}
}
}
else if (searchLevel == searchPath.Length - 1 && nbdescendantsreceived >= reply.AgentData.Descendents)
{
//There are no items in the packet, but we are looking for one!
//We didnt find the item, the user probably mistyped its name
SayToUser("Didn't find notecard " + searchPath[searchLevel]);
//TODO: keep looking for a moment, or else reply packets may still
//come in case of a very large inventory folder
//Stop looking at packets
proxy.RemoveDelegate(PacketType.InventoryDescendents, Direction.Incoming, this.inventoryPacketDelegate);
}
//Intercept the packet, it was a reply to our request. No need
//to confuse the actual SL client
intercept = true;
}
End:
if (intercept)
{
//stop packet
return null;
}
else
{
//let packet go to client
return packet;
}
}
public static InventoryItem CreateInventoryItem(InventoryType type, LLUUID id)
{
switch (type)
{
case InventoryType.Texture: return new InventoryTexture(id);
case InventoryType.Sound: return new InventorySound(id);
case InventoryType.CallingCard: return new InventoryCallingCard(id);
case InventoryType.Landmark: return new InventoryLandmark(id);
case InventoryType.Object: return new InventoryObject(id);
case InventoryType.Notecard: return new InventoryNotecard(id);
case InventoryType.Category: return new InventoryCategory(id);
case InventoryType.LSL: return new InventoryLSL(id);
case InventoryType.Snapshot: return new InventorySnapshot(id);
case InventoryType.Attachment: return new InventoryAttachment(id);
case InventoryType.Wearable: return new InventoryWearable(id);
case InventoryType.Animation: return new InventoryAnimation(id);
case InventoryType.Gesture: return new InventoryGesture(id);
default: return new InventoryItem(type, id);
}
}
//Ask for download of an item
public LLUUID RequestInventoryAsset(InventoryItem item)
{
// Build the request packet and send it
TransferRequestPacket request = new TransferRequestPacket();
request.TransferInfo.ChannelType = (int)ChannelType.Asset;
request.TransferInfo.Priority = 101.0f;
request.TransferInfo.SourceType = (int)SourceType.SimInventoryItem;
LLUUID transferID = LLUUID.Random();
request.TransferInfo.TransferID = transferID;
byte[] paramField = new byte[100];
Buffer.BlockCopy(frame.AgentID.GetBytes(), 0, paramField, 0, 16);
Buffer.BlockCopy(frame.SessionID.GetBytes(), 0, paramField, 16, 16);
Buffer.BlockCopy(item.OwnerID.GetBytes(), 0, paramField, 32, 16);
Buffer.BlockCopy(LLUUID.Zero.GetBytes(), 0, paramField, 48, 16);
Buffer.BlockCopy(item.UUID.GetBytes(), 0, paramField, 64, 16);
Buffer.BlockCopy(item.AssetUUID.GetBytes(), 0, paramField, 80, 16);
Buffer.BlockCopy(Helpers.IntToBytes((int)item.AssetType), 0, paramField, 96, 4);
request.TransferInfo.Params = paramField;
// add a delegate to monitor configuration notecards download
proxy.AddDelegate(PacketType.TransferPacket, Direction.Incoming, this.transferPacketDelegate);
//send packet to SL
proxy.InjectPacket(request, Direction.Outgoing);
//so far we downloaded 0 bytes
downloadedbytes = 0;
//the total size of the download is yet unknown
downloadsize = 0;
//A 100K buffer should be enough for everyone
buffer = new byte[1024 * 100];
//Return the transfer ID
return transferID;
}
// SayToUser: send a message to the user as in-world chat
private void SayToUser(string message)
{
ChatFromSimulatorPacket packet = new ChatFromSimulatorPacket();
packet.ChatData.FromName = Helpers.StringToField("ClientAO");
packet.ChatData.SourceID = LLUUID.Random();
packet.ChatData.OwnerID = frame.AgentID;
packet.ChatData.SourceType = (byte)2;
packet.ChatData.ChatType = (byte)1;
packet.ChatData.Audible = (byte)1;
packet.ChatData.Position = new LLVector3(0, 0, 0);
packet.ChatData.Message = Helpers.StringToField(message);
proxy.InjectPacket(packet, Direction.Incoming);
}
//start or stop an animation
public void Animate(LLUUID animationuuid, bool run)
{
AgentAnimationPacket animate = new AgentAnimationPacket();
animate.Header.Reliable = true;
animate.AgentData.AgentID = frame.AgentID;
animate.AgentData.SessionID = frame.SessionID;
//We send one animation
animate.AnimationList = new AgentAnimationPacket.AnimationListBlock[1];
animate.AnimationList[0] = new AgentAnimationPacket.AnimationListBlock();
animate.AnimationList[0].AnimID = animationuuid;
animate.AnimationList[0].StartAnim = run;
//SayToUser("anim " + animname(animationuuid) + " " + run);
proxy.InjectPacket(animate, Direction.Outgoing);
}
//return the name of an animation by its UUID
private string animname(LLUUID arg)
{
return animuid2name[arg];
}
//handle animation packets from simulator
private Packet AnimationPacketHandler(Packet packet, IPEndPoint sim) {
AvatarAnimationPacket animation = (AvatarAnimationPacket)packet;
if (animation.Sender.ID == frame.AgentID)
{
//the received animation packet is about our Agent, handle it
lock (SignaledAnimations)
{
// Reset the signaled animation list
SignaledAnimations.Clear();
//fill it with the fresh list from simulator
for (int i = 0; i < animation.AnimationList.Length; i++)
{
LLUUID animID = animation.AnimationList[i].AnimID;
int sequenceID = animation.AnimationList[i].AnimSequenceID;
// Add this animation to the list of currently signaled animations
SignaledAnimations[animID] = sequenceID;
//SayToUser("Animation: " + animname(animID));
}
}
//we now have a list of currently running animations
//Start override animations if necessary
foreach (LLUUID key in overrides.Keys)
{
//For each overriden animation key, test if its override is running
if (SignaledAnimations.ContainsKey(key) && (!overrideanimationisplaying[key] ))
{
//An overriden animation is present and its override animation
//isnt currently playing
//Start the override animation
//SayToUser("animation " + animname(key) + " started, will override with " + animname(overrides[key]));
overrideanimationisplaying[key] = true;
Animate(overrides[key], true);
}
else if ((!SignaledAnimations.ContainsKey(key)) && overrideanimationisplaying[key])
{
//an override animation is currently playing, but it's overriden
//animation is not.
//stop the override animation
//SayToUser("animation " + animname(key) + " stopped, will override with " + animname(overrides[key]));
overrideanimationisplaying[key] = false;
Animate(overrides[key], false);
}
}
}
//Let the packet go to the client
return packet;
}
//handle packets that contain info about the notecard data transfer
private Packet TransferInfoHandler(Packet packet, IPEndPoint simulator)
{
TransferInfoPacket info = (TransferInfoPacket)packet;
if (info.TransferInfo.TransferID == assetdownloadID)
{
//this is our requested tranfer, handle it
downloadsize = info.TransferInfo.Size;
if ((StatusCode)info.TransferInfo.Status != StatusCode.OK)
{
SayToUser("Failed to read notecard");
}
if (downloadedbytes >= downloadsize)
{
//Download already completed!
downloadCompleted();
}
//intercept packet
return null;
}
return packet;
}
//handle packets which contain the notecard data
private Packet TransferPacketHandler(Packet packet, IPEndPoint simulator)
{
TransferPacketPacket asset = (TransferPacketPacket)packet;
if (asset.TransferData.TransferID == assetdownloadID) {
Buffer.BlockCopy(asset.TransferData.Data, 0, buffer, 1000 * asset.TransferData.Packet,
asset.TransferData.Data.Length);
downloadedbytes += asset.TransferData.Data.Length;
// Check if we downloaded the full asset
if (downloadedbytes >= downloadsize)
{
downloadCompleted();
}
//Intercept packet
return null;
}
return packet;
}
private void downloadCompleted()
{
//We have the notecard.
//Stop looking at transfer packets
proxy.RemoveDelegate(PacketType.TransferPacket, Direction.Incoming, this.transferPacketDelegate);
//crop the buffer size
byte[] tmp = new byte[downloadedbytes];
Buffer.BlockCopy(buffer, 0, tmp, 0, downloadedbytes);
buffer = tmp;
String notecardtext = getNotecardText(Helpers.FieldToUTF8String(buffer));
//Load config, wetikon format
loadWetIkon(notecardtext);
}
private void loadWetIkon(string config)
{
//Reinitialise override table
overrides = new Dictionary<LLUUID,LLUUID>();
overrideanimationisplaying = new Dictionary<LLUUID, bool>();
animuid2name = new Dictionary<LLUUID,string>();
foreach (LLUUID key in wetikonanims )
{
animuid2name[key] = wetikonanimnames[Array.IndexOf(wetikonanims, key)];
}
//list of animations in wetikon
//read every second line in the config
char[] sep = { '\n' };
string[] lines = config.Split(sep);
int length = lines.Length;
int i = 1;
while (i < length) {
//Read animation name and look it up
string animname = lines[i].Trim();
//SayToUser("anim: " + animname);
if (animname != "")
{
if (currentFolderItems.ContainsKey(animname))
{
LLUUID over = currentFolderItems[animname].AssetUUID;
LLUUID orig = wetikonanims[((i + 1) / 2) - 1];
//put it in overrides
animuid2name[over] = animname;
overrides[orig] = over;
overrideanimationisplaying[orig] = false;
//SayToUser(wetikonanimnames[((i + 1) / 2) - 1] + " overriden by " + animname + " ( " + over + ")");
}
else
{
//Not found
SayToUser(animname + " not found.");
}
}
i += 2;
}
SayToUser("Notecard read, " + overrides.Count + " animations found");
}
private string getNotecardText(string data)
{
// Version 1 format:
// Linden text version 1
// {
// <EmbeddedItemList chunk>
// Text length
// <ASCII text; 0x80 | index = embedded item>
// }
// Version 2 format: (NOTE: Imports identically to version 1)
// Linden text version 2
// {
// <EmbeddedItemList chunk>
// Text length
// <UTF8 text; FIRST_EMBEDDED_CHAR + index = embedded item>
// }
int i = 0;
char[] sep = { '\n' };
string[] lines = data.Split(sep);
int length = lines.Length;
string result = "";
//check format
if (!lines[i].StartsWith("Linden text version "))
{
SayToUser("error");
return "";
}
//{
i++;
if (lines[i] != "{")
{
SayToUser("error");
return "";
}
i++;
if (lines[i] != "LLEmbeddedItems version 1")
{
SayToUser("error");
return "";
}
//{
i++;
if (lines[i] != "{")
{
SayToUser("error");
return "";
}
//count ...
i++;
if (!lines[i].StartsWith("count "))
{
SayToUser("error");
return "";
}
//}
i++;
if (lines[i] != "}")
{
SayToUser("error");
return "";
}
//Text length ...
i++;
if (!lines[i].StartsWith("Text length "))
{
SayToUser("error");
return "";
}
i++;
while (i < length)
{
result += lines[i] + "\n";
i++;
}
result = result.Substring(0, result.Length - 3);
return result;
}
}

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.50727</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{173E70E4-44EE-440C-AD59-1CCD0BF03AC1}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ClientAO</RootNamespace>
<AssemblyName>ClientAO</AssemblyName>
<StartupObject>
</StartupObject>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\..\bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\..\bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<ItemGroup>
<Compile Include="ClientAO.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="libsecondlife, Version=0.9.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\bin\libsecondlife.dll</HintPath>
</Reference>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\SLProxy.csproj">
<Project>{E4115DC9-FC88-47D6-B3B6-2400AD19B80D}</Project>
<Name>SLProxy</Name>
</ProjectReference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,35 @@
ClientAO by Issarlk Chatnoir
What is it?
***********
ClientAO is an implementation of an animation overrider running as a plugin for SLProxy.
Since it runs on the local computer and not on SL's server, this AO has no impact on sim
performance and lag. Also, it reacts to animation changes instantly instead of having to
poll the avatar state every so often, like inworld AO.
Instead of using an object like a regular AO, you point it to a folder in your inventory
containing the animations and a configuration notecard.
The AO understands "wetikon" type notecards (see sample)
How to use
**********
- Run SLProxy and start your SecondLife client so that it connects through the Proxy
(ex: SecondLife.exe ...(usual options here)... -loginuri http://localhost:8080/ )
- Login like usual
- once logged in, load the ClientAO plugin: /load ClientAO.dll
- Point it to a folder where you have previously put animations and a configuration
notecard, ex: /ao Objects/MyAO/*Default Anims
The AO will load the notecard.
- start the AO: /ao on
The AO should now replace your animations.

View File

@@ -0,0 +1,54 @@
:: [ Walking (also Striding) ] ::
:: [ Running ] ::
:: [ CrouchWalking ] ::
:: [ Flying (also FlyingSlow) ] ::
:: [ Turning Left ] ::
:: [ Turning Right ] ::
:: [ Jumping ] ::
:: [ Hovering Up ] ::
:: [ Crouching ] ::
:: [ Fly Down ] ::
:: [ Standing 1 ] ::
:: [ Standing 2 ] ::
:: [ Standing 3 ] ::
:: [ Standing 4 ] ::
:: [ Standing 5 ] ::
:: [ Hovering ] ::
:: [ Sitting ] ::
:: [ PreJumping ] ::
:: [ Falling ] ::
:: [ Soft Landing/Landing ] ::
:: [ Standing Up (That anim you play after you fall when you stand up and brush yourself off) ] ::
:: [ FlyingSlow ] ::
:: [ Sitting on Ground ] ::
:: [ Floating (Hovering underwater) ] ::
:: [ Swimming Forward (Flying underwater) ] ::
:: [ Swimming Up (Hover Up underwater) ] ::
:: [ Swimming Down (Fly Down underwater) ] ::

View File

@@ -0,0 +1 @@
All information about SLProxy can be found on the libsecondlife wiki at http://www.libsecondlife.org/wiki/SLProxy

View File

@@ -0,0 +1,153 @@
<?xml version="1.0"?>
<project name="libsecondlife" default="build">
<!-- global framework settings -->
<property name="target.framework" value="${framework::get-target-framework()}" />
<property name="assembly.dir" value="${framework::get-assembly-directory(target.framework)}" />
<!-- global project settings -->
<xmlpeek
file="../libsecondlife.build"
xpath="/project/property[@name = 'project.version']/@value"
property="project.version" />
<property name="build.number"
value="${math::abs(math::floor(timespan::get-total-days(datetime::now()
- datetime::parse('01/01/2002'))))}" />
<property name="assembly" value="SLProxy"/>
<property name="bin_dir" value="../bin" />
<!-- default configuration -->
<property name="project.config" value="debug" /> <!-- debug|release -->
<!-- named configurations -->
<target name="init" description="Initializes build properties">
<call target="${project.config}" />
</target>
<target name="debug" description="configures a debug build">
<property name="build.debug" value="true" />
<property name="package.name"
value="${project::get-name()}-${project.version}-${project.config}" />
<property name="assembly.configuration"
value="${framework::get-target-framework()}.${platform::get-name()} [${project.config}]" />
</target>
<target name="release" description="configures a release build">
<property name="project.config" value="release" />
<property name="build.debug" value="false" />
<property name="package.name"
value="${project::get-name()}-${project.version}" />
<property name="assembly.configuration"
value="${framework::get-target-framework()}.${platform::get-name()}" />
</target>
<!-- build tasks -->
<!-- target name="build" depends="build-analyst build-chatconsole build-main"
description="build analyst and chatconsole"
FIXME: Analyst's build is broken and Analyst and SLProxyLoader have
circular dependencies. Skipping Analyst for now. -axial -->
<target name="build" depends="build-analyst build-slproxy build-chatconsole build-main"
description="build SLProxy and chatconsole">
</target>
<target name="build-slproxy" depends="init"
description="Builds the binaries for the current configuration">
<echo message="Build Directory is ${bin_dir}/" />
<mkdir dir="${bin_dir}" failonerror="false" />
<csc
target="library"
debug="${build.debug}"
output="${bin_dir}/SLProxy.dll">
<sources failonempty="true">
<include name="SLProxy.cs" />
<include name="XmlRpcCS/*.cs" />
<include name="SLProxyLoader.cs" />
<!-- <include name="legacy/*.cs" /> -->
</sources>
<references basedir="${bin_dir}/">
<include name="libsecondlife.dll"/>
</references>
</csc>
</target>
<target name="build-analyst" depends="init build-slproxy"
description="Builds the binaries for the current configuration">
<echo message="Build Directory is ${bin_dir}/" />
<mkdir dir="${bin_dir}" failonerror="false" />
<csc
target="library"
debug="${build.debug}"
output="${bin_dir}/Analyst.dll">
<sources failonempty="true">
<include name="Analyst.cs" />
<!-- <include name="legacy/*.cs" /> -->
</sources>
<references basedir="${bin_dir}/">
<include name="libsecondlife.dll"/>
<include name="SLProxy.dll"/>
</references>
</csc>
</target>
<!-- Note - please, please, PLEASE, refrain from calling the executable
SLProxy.exe, as the name conflict with SLProxy.dll confuses Mono -->
<target name="build-main"
depends="init build-slproxy"
description="build SLProxy main">
<echo message="Build Directory is ${bin_dir}/" />
<mkdir dir="${bin_dir}" failonerror="false" />
<csc
target="exe"
debug="${build.debug}"
output="${bin_dir}/SLProxyMain.exe">
<sources failonempty="true">
<include name="SLProxyMain.cs" />
</sources>
<references basedir="${bin_dir}/">
<include name="libsecondlife.dll"/>
<include name="SLProxy.dll"/>
<include name="Analyst.dll"/>
</references>
</csc>
</target>
<target name="build-chatconsole"
depends="init build-slproxy"
description="build SLProxy ChatConsole">
<echo message="Build Directory is ${bin_dir}/" />
<mkdir dir="${bin_dir}" failonerror="false" />
<csc
target="exe"
debug="${build.debug}"
output="${bin_dir}/ChatConsole.exe">
<sources failonempty="true">
<include name="ChatConsole.cs" />
</sources>
<references basedir="${bin_dir}/">
<include name="libsecondlife.dll"/>
<include name="SLProxy.dll"/>
</references>
</csc>
</target>
<target name="clean" depends="init"
description="Deletes the current configuration">
<delete failonerror="false">
<fileset basedir="${bin_dir}/">
<include name="SLProxy.dll" />
<include name="SLProxy.dll.mdb" />
<include name="ChatConsole.exe" />
<include name="ChatConsole.exe.mdb" />
<include name="Analyst.exe" />
<include name="Analyst.exe.mdb" />
</fileset>
</delete>
</target>
<target
name="*"
description="Handles unknown targets">
<echo message="skipping unknown target" />
</target>
</project>

2127
Programs/SLProxy/SLProxy.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,140 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectType>Local</ProjectType>
<ProductVersion>8.0.50727</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{E4115DC9-FC88-47D6-B3B6-2400AD19B80D}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ApplicationIcon>
</ApplicationIcon>
<AssemblyKeyContainerName>
</AssemblyKeyContainerName>
<AssemblyName>SLProxy</AssemblyName>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
<DefaultClientScript>JScript</DefaultClientScript>
<DefaultHTMLPageLayout>Grid</DefaultHTMLPageLayout>
<DefaultTargetSchema>IE50</DefaultTargetSchema>
<DelaySign>false</DelaySign>
<OutputType>Exe</OutputType>
<RootNamespace>SLProxy</RootNamespace>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<StartupObject>ProxyMain</StartupObject>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>..\bin\</OutputPath>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>
</DocumentationFile>
<DebugSymbols>true</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
<NoWarn>
</NoWarn>
<Optimize>false</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>4</WarningLevel>
<DebugType>full</DebugType>
<ErrorReport>prompt</ErrorReport>
<UseVSHostingProcess>false</UseVSHostingProcess>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>..\bin\</OutputPath>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>TRACE</DefineConstants>
<DocumentationFile>
</DocumentationFile>
<DebugSymbols>false</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
<NoWarn>
</NoWarn>
<Optimize>true</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>4</WarningLevel>
<DebugType>none</DebugType>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release-docs|AnyCPU' ">
<OutputPath>bin\Release-docs\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<BaseAddress>285212672</BaseAddress>
<Optimize>true</Optimize>
<DebugType>
</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<CodeAnalysisRuleAssemblies>C:\Program Files\Microsoft Visual Studio 8\Team Tools\Static Analysis Tools\FxCop\\rules</CodeAnalysisRuleAssemblies>
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<Reference Include="System">
<Name>System</Name>
</Reference>
<Reference Include="System.Data">
<Name>System.Data</Name>
</Reference>
<Reference Include="System.Xml">
<Name>System.XML</Name>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Analyst.cs" />
<Compile Include="SLProxy.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="SLProxyLoader.cs" />
<Compile Include="SLProxyMain.cs" />
<Compile Include="XmlRpcCS\Logger.cs" />
<Compile Include="XmlRpcCS\SimpleHttpRequest.cs" />
<Compile Include="XmlRpcCS\XmlRpcBoxcarRequest.cs" />
<Compile Include="XmlRpcCS\XmlRpcClientProxy.cs" />
<Compile Include="XmlRpcCS\XmlRpcDeserializer.cs" />
<Compile Include="XmlRpcCS\XmlRpcErrorCodes.cs" />
<Compile Include="XmlRpcCS\XmlRpcException.cs" />
<Compile Include="XmlRpcCS\XmlRpcExposedAttribute.cs" />
<Compile Include="XmlRpcCS\XmlRpcRequest.cs" />
<Compile Include="XmlRpcCS\XmlRpcRequestDeserializer.cs" />
<Compile Include="XmlRpcCS\XmlRpcRequestSerializer.cs" />
<Compile Include="XmlRpcCS\XmlRpcResponder.cs" />
<Compile Include="XmlRpcCS\XmlRpcResponse.cs" />
<Compile Include="XmlRpcCS\XmlRpcResponseDeserializer.cs" />
<Compile Include="XmlRpcCS\XmlRpcResponseSerializer.cs" />
<Compile Include="XmlRpcCS\XmlRpcSerializer.cs" />
<Compile Include="XmlRpcCS\XmlRpcServer.cs" />
<Compile Include="XmlRpcCS\XmlRpcSystemObject.cs" />
<Compile Include="XmlRpcCS\XmlRpcXmlTokens.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\libsecondlife\OpenMetaverse.csproj">
<Project>{D9CDEDFB-8169-4B03-B57F-0DF638F044EC}</Project>
<Name>OpenMetaverse</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,210 @@
using SLProxy;
using OpenMetaverse;
using Nwc.XmlRpc;
using OpenMetaverse.Packets;
using System.Reflection;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
namespace SLProxy
{
public class ProxyFrame
{
public Proxy proxy;
private Dictionary<string, CommandDelegate> commandDelegates = new Dictionary<string, CommandDelegate>();
private LLUUID agentID;
private LLUUID sessionID;
private LLUUID inventoryRoot;
private bool logLogin = false;
private string[] args;
public delegate void CommandDelegate(string[] words);
public string[] Args
{
get { return args; }
}
public LLUUID AgentID
{
get { return agentID; }
}
public LLUUID SessionID
{
get { return sessionID; }
}
public LLUUID InventoryRoot
{
get { return inventoryRoot; }
}
public void AddCommand(string cmd, CommandDelegate deleg)
{
commandDelegates[cmd] = deleg;
}
public ProxyFrame(string[] args)
{
//bool externalPlugin = false;
this.args = args;
ProxyConfig proxyConfig = new ProxyConfig("SLProxy", "Austin Jennings / Andrew Ortman", args);
proxy = new Proxy(proxyConfig);
// add delegates for login
proxy.SetLoginRequestDelegate(new XmlRpcRequestDelegate(LoginRequest));
proxy.SetLoginResponseDelegate(new XmlRpcResponseDelegate(LoginResponse));
// add a delegate for outgoing chat
proxy.AddDelegate(PacketType.ChatFromViewer, Direction.Outgoing, new PacketDelegate(ChatFromViewerOut));
// handle command line arguments
foreach (string arg in args)
if (arg == "--log-login")
logLogin = true;
else if (arg.Substring(0, 2) == "--")
{
int ipos = arg.IndexOf("=");
if (ipos != -1)
{
string sw = arg.Substring(0, ipos);
string val = arg.Substring(ipos + 1);
Console.WriteLine("arg '" + sw + "' val '" + val + "'");
if (sw == "--load")
{
//externalPlugin = true;
LoadPlugin(val);
}
}
}
commandDelegates["/load"] = new CommandDelegate(CmdLoad);
}
private void CmdLoad(string[] words)
{
if (words.Length != 2)
SayToUser("Usage: /load <plugin name>");
else
{
try
{
LoadPlugin(words[1]);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
public void LoadPlugin(string name)
{
Assembly assembly = Assembly.LoadFile(Path.GetFullPath(name));
foreach (Type t in assembly.GetTypes())
{
try
{
if (t.IsSubclassOf(typeof(ProxyPlugin)))
{
ConstructorInfo info = t.GetConstructor(new Type[] { typeof(ProxyFrame) });
ProxyPlugin plugin = (ProxyPlugin)info.Invoke(new object[] { this });
plugin.Init();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
// LoginRequest: dump a login request to the console
private void LoginRequest(XmlRpcRequest request)
{
if (logLogin)
{
Console.WriteLine("==> Login Request");
Console.WriteLine(request);
}
}
// Loginresponse: dump a login response to the console
private void LoginResponse(XmlRpcResponse response)
{
System.Collections.Hashtable values = (System.Collections.Hashtable)response.Value;
if (values.Contains("agent_id"))
agentID = new LLUUID((string)values["agent_id"]);
if (values.Contains("session_id"))
sessionID = new LLUUID((string)values["session_id"]);
if (values.Contains("inventory-root"))
{
inventoryRoot = new LLUUID(
(string)((System.Collections.Hashtable)(((System.Collections.ArrayList)values["inventory-root"])[0]))["folder_id"]
);
Console.WriteLine("inventory root: " + inventoryRoot);
}
if (logLogin)
{
Console.WriteLine("<== Login Response");
Console.WriteLine(response);
}
}
// ChatFromViewerOut: outgoing ChatFromViewer delegate; check for Analyst commands
private Packet ChatFromViewerOut(Packet packet, IPEndPoint sim)
{
// deconstruct the packet
ChatFromViewerPacket cpacket = (ChatFromViewerPacket)packet;
string message = System.Text.Encoding.UTF8.GetString(cpacket.ChatData.Message).Replace("\0", "");
if (message.Length > 1 && message[0] == '/')
{
string[] words = message.Split(' ');
if (commandDelegates.ContainsKey(words[0]))
{
// this is an Analyst command; act on it and drop the chat packet
((CommandDelegate)commandDelegates[words[0]])(words);
return null;
}
}
return packet;
}
// SayToUser: send a message to the user as in-world chat
public void SayToUser(string message)
{
ChatFromSimulatorPacket packet = new ChatFromSimulatorPacket();
packet.ChatData.FromName = Helpers.StringToField("SLProxy");
packet.ChatData.SourceID = LLUUID.Random();
packet.ChatData.OwnerID = agentID;
packet.ChatData.SourceType = (byte)2;
packet.ChatData.ChatType = (byte)1;
packet.ChatData.Audible = (byte)1;
packet.ChatData.Position = new LLVector3(0, 0, 0);
packet.ChatData.Message = Helpers.StringToField(message);
proxy.InjectPacket(packet, Direction.Incoming);
}
}
public abstract class ProxyPlugin : MarshalByRefObject
{
// public abstract ProxyPlugin(ProxyFrame main);
public abstract void Init();
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Reflection;
using SLProxy;
class ProxyMain
{
public static void Main(string[] args)
{
ProxyFrame p = new ProxyFrame(args);
ProxyPlugin analyst = new Analyst(p);
analyst.Init();
p.proxy.Start();
}
}

View File

@@ -0,0 +1,46 @@
namespace Nwc.XmlRpc
{
using System;
/// <summary>Define levels of logging.</summary><remarks> This duplicates
/// similar enumerations in System.Diagnostics.EventLogEntryType. The
/// duplication was merited because .NET Compact Framework lacked the EventLogEntryType enum.</remarks>
public enum LogLevel
{
/// <summary>Information level, log entry for informational reasons only.</summary>
Information,
/// <summary>Warning level, indicates a possible problem.</summary>
Warning,
/// <summary>Error level, implies a significant problem.</summary>
Error
}
///<summary>
///Logging singleton with swappable output delegate.
///</summary>
///<remarks>
///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.
///</remarks>
public class Logger
{
///<summary>Delegate definition for logging.</summary>
///<param name="message">The message <c>String</c> to log.</param>
///<param name="level">The <c>LogLevel</c> of your message.</param>
public delegate void LoggerDelegate(String message, LogLevel level);
///<summary>The LoggerDelegate that will recieve WriteEntry requests.</summary>
static public LoggerDelegate Delegate = null;
///<summary>
///Method logging events are sent to.
///</summary>
///<param name="message">The message <c>String</c> to log.</param>
///<param name="level">The <c>LogLevel</c> of your message.</param>
static public void WriteEntry(String message, LogLevel level)
{
if (Delegate != null)
Delegate(message, level);
}
}
}

View File

@@ -0,0 +1,204 @@
namespace Nwc.XmlRpc
{
using System;
using System.IO;
using System.Net.Sockets;
using System.Collections;
///<summary>Very basic HTTP request handler.</summary>
///<remarks>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.</remarks>
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;
/// <summary>A constructor which accepts the TcpClient.</summary>
/// <remarks>It creates the associated input and output streams, determines the request type,
/// and parses the remaining HTTP header.</remarks>
/// <param name="client">The <c>TcpClient</c> associated with the HTTP connection.</param>
public SimpleHttpRequest(TcpClient client)
{
_client = client;
_output = new StreamWriter(client.GetStream());
_input = new StreamReader(client.GetStream());
GetRequestMethod();
GetRequestHeaders();
}
/// <summary>The output <c>StreamWriter</c> associated with the request.</summary>
public StreamWriter Output
{
get { return _output; }
}
/// <summary>The input <c>StreamReader</c> associated with the request.</summary>
public StreamReader Input
{
get { return _input; }
}
/// <summary>The <c>TcpClient</c> with the request.</summary>
public TcpClient Client
{
get { return _client; }
}
private String _filePath
{
get { return __filePath; }
set
{
__filePath = value;
_filePathDir = null;
_filePathFile = null;
}
}
/// <summary>The type of HTTP request (i.e. PUT, GET, etc.).</summary>
public String HttpMethod
{
get { return _httpMethod; }
}
/// <summary>The level of the HTTP protocol.</summary>
public String Protocol
{
get { return _protocol; }
}
/// <summary>The "path" which is part of any HTTP request.</summary>
public String FilePath
{
get { return _filePath; }
}
/// <summary>The file portion of the "path" which is part of any HTTP request.</summary>
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;
}
}
/// <summary>The directory portion of the "path" which is part of any HTTP request.</summary>
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);
}
}
}
/// <summary>
/// Format the object contents into a useful string representation.
/// </summary>
///<returns><c>String</c> representation of the <c>SimpleHttpRequest</c> as the <i>HttpMethod FilePath Protocol</i>.</returns>
override public String ToString()
{
return HttpMethod + " " + FilePath + " " + Protocol;
}
/// <summary>
/// Close the <c>SimpleHttpRequest</c>. This flushes and closes all associated io streams.
/// </summary>
public void Close()
{
_output.Flush();
_output.Close();
_input.Close();
_client.Close();
}
}
}

View File

@@ -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;
/// <summary>Class that collects individual <c>XmlRpcRequest</c> objects and submits them as a <i>boxcarred</i> request.</summary>
/// <remarks>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.
///</remarks>
/// <seealso cref="XmlRpcRequest"/>
public class XmlRpcBoxcarRequest : XmlRpcRequest
{
/// <summary>ArrayList to collect the requests to boxcar.</summary>
public IList Requests = new ArrayList();
/// <summary>Basic constructor.</summary>
public XmlRpcBoxcarRequest()
{
}
/// <summary>Returns the <c>String</c> "system.multiCall" which is the server method that handles boxcars.</summary>
public override String MethodName
{
get { return "system.multiCall"; }
}
/// <summary>The <c>ArrayList</c> of boxcarred <paramref>Requests</paramref> as properly formed parameters.</summary>
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;
}
}
}
}

View File

@@ -0,0 +1,61 @@
namespace Nwc.XmlRpc
{
using System;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Messaging;
/// <summary>This class provides support for creating local proxies of XML-RPC remote objects</summary>
/// <remarks>
/// To create a local proxy you need to create a local C# interface and then, via <i>createProxy</i>
/// associate that interface with a remote object at a given URL.
/// </remarks>
public class XmlRpcClientProxy : RealProxy
{
private String _remoteObjectName;
private String _url;
private XmlRpcRequest _client = new XmlRpcRequest();
/// <summary>Factory method to create proxies.</summary>
/// <remarks>
/// 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 <c>createProxy</c> along with the object name and URL of the remote object and
/// cast the resulting object to the specifice interface.
/// </remarks>
/// <param name="remoteObjectName"><c>String</c> The name of the remote object.</param>
/// <param name="url"><c>String</c> The URL of the remote object.</param>
/// <param name="anInterface"><c>Type</c> The typeof() of a C# interface.</param>
/// <returns><c>Object</c> A proxy for your specified interface. Cast to appropriate type.</returns>
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;
}
/// <summary>The local method dispatcher - do not invoke.</summary>
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);
}
}
}
}

View File

@@ -0,0 +1,195 @@
namespace Nwc.XmlRpc
{
using System;
using System.Collections;
using System.IO;
using System.Xml;
using System.Globalization;
/// <summary>Parser context, we maintain contexts in a stack to avoiding recursion. </summary>
struct Context
{
public String Name;
public Object Container;
}
/// <summary>Basic XML-RPC data deserializer.</summary>
/// <remarks>Uses <c>XmlTextReader</c> 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.</remarks>
public class XmlRpcDeserializer : XmlRpcXmlTokens
{
private static DateTimeFormatInfo _dateFormat = new DateTimeFormatInfo();
private Object _container;
private Stack _containerStack;
/// <summary>Protected reference to last text.</summary>
protected String _text;
/// <summary>Protected reference to last deserialized value.</summary>
protected Object _value;
/// <summary>Protected reference to last name field.</summary>
protected String _name;
/// <summary>Basic constructor.</summary>
public XmlRpcDeserializer()
{
Reset();
_dateFormat.FullDateTimePattern = ISO_DATETIME;
}
/// <summary>Static method that parses XML data into a response using the Singleton.</summary>
/// <param name="xmlData"><c>StreamReader</c> containing an XML-RPC response.</param>
/// <returns><c>Object</c> object resulting from the deserialization.</returns>
virtual public Object Deserialize(TextReader xmlData)
{
return null;
}
/// <summary>Protected method to parse a node in an XML-RPC XML stream.</summary>
/// <remarks>Method deals with elements common to all XML-RPC data, subclasses of
/// this object deal with request/response spefic elements.</remarks>
/// <param name="reader"><c>XmlTextReader</c> of the in progress parsing data stream.</param>
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 <string> tag, they just do <value>
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;
}
}
/// <summary>Static method that parses XML in a <c>String</c> into a
/// request using the Singleton.</summary>
/// <param name="xmlData"><c>String</c> containing an XML-RPC request.</param>
/// <returns><c>XmlRpcRequest</c> object resulting from the parse.</returns>
public Object Deserialize(String xmlData)
{
StringReader sr = new StringReader(xmlData);
return Deserialize(sr);
}
/// <summary>Pop a Context of the stack, an Array or Struct has closed.</summary>
private void PopContext()
{
Context c = (Context)_containerStack.Pop();
_container = c.Container;
_name = c.Name;
}
/// <summary>Push a Context on the stack, an Array or Struct has opened.</summary>
private void PushContext()
{
Context context;
context.Container = _container;
context.Name = _name;
_containerStack.Push(context);
}
/// <summary>Reset the internal state of the deserializer.</summary>
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
}
}

View File

@@ -0,0 +1,51 @@
namespace Nwc.XmlRpc
{
using System;
/// <summary>Standard XML-RPC error codes.</summary>
public class XmlRpcErrorCodes
{
/// <summary></summary>
public const int PARSE_ERROR_MALFORMED = -32700;
/// <summary></summary>
public const String PARSE_ERROR_MALFORMED_MSG = "Parse Error, not well formed";
/// <summary></summary>
public const int PARSE_ERROR_ENCODING = -32701;
/// <summary></summary>
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.
//
/// <summary></summary>
public const int SERVER_ERROR_METHOD = -32601;
/// <summary></summary>
public const String SERVER_ERROR_METHOD_MSG = "Server Error, requested method not found";
/// <summary></summary>
public const int SERVER_ERROR_PARAMS = -32602;
/// <summary></summary>
public const String SERVER_ERROR_PARAMS_MSG = "Server Error, invalid method parameters";
//
// -32603 ---> server error. internal xml-rpc error
//
/// <summary></summary>
public const int APPLICATION_ERROR = -32500;
/// <summary></summary>
public const String APPLICATION_ERROR_MSG = "Application Error";
//
// -32400 ---> system error
//
/// <summary></summary>
public const int TRANSPORT_ERROR = -32300;
/// <summary></summary>
public const String TRANSPORT_ERROR_MSG = "Transport Layer Error";
}
}

View File

@@ -0,0 +1,39 @@
namespace Nwc.XmlRpc
{
using System;
/// <summary>An XML-RPC Exception.</summary>
/// <remarks>Maps a C# exception to an XML-RPC fault. Normal exceptions
/// include a message so this adds the code needed by XML-RPC.</remarks>
public class XmlRpcException : Exception
{
private int _code;
/// <summary>Instantiate an <c>XmlRpcException</c> with a code and message.</summary>
/// <param name="code"><c>Int</c> faultCode associated with this exception.</param>
/// <param name="message"><c>String</c> faultMessage associated with this exception.</param>
public XmlRpcException(int code, String message)
: base(message)
{
_code = code;
}
/// <summary>The value of the faults message, i.e. the faultString.</summary>
public String FaultString
{
get { return Message; }
}
/// <summary>The value of the faults code, i.e. the faultCode.</summary>
public int FaultCode
{
get { return _code; }
}
/// <summary>Format the message to include the code.</summary>
override public String ToString()
{
return "Code: " + FaultCode + " Message: " + base.ToString();
}
}
}

View File

@@ -0,0 +1,60 @@
namespace Nwc.XmlRpc
{
using System;
using System.Reflection;
/// <summary>
/// Simple tagging attribute to indicate participation is XML-RPC exposure.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
[AttributeUsage(
AttributeTargets.Class | AttributeTargets.Method,
AllowMultiple = false,
Inherited = true
)]
public class XmlRpcExposedAttribute : Attribute
{
/// <summary>Check if <paramref>obj</paramref> is an object utilizing the XML-RPC exposed Attribute.</summary>
/// <param name="obj"><c>Object</c> of a class or method to check for attribute.</param>
/// <returns><c>Boolean</c> true if attribute present.</returns>
public static Boolean ExposedObject(Object obj)
{
return IsExposed(obj.GetType());
}
/// <summary>Check if <paramref>obj</paramref>.<paramref>methodName</paramref> is an XML-RPC exposed method.</summary>
/// <remarks>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.</remarks>
/// <returns><c>Boolean</c> true if the method is exposed.</returns>
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);
}
/// <summary>Check if <paramref>mi</paramref> is XML-RPC exposed.</summary>
/// <param name="mi"><c>MemberInfo</c> of a class or method to check for attribute.</param>
/// <returns><c>Boolean</c> true if attribute present.</returns>
public static Boolean IsExposed(MemberInfo mi)
{
foreach (Attribute attr in mi.GetCustomAttributes(true))
{
if (attr is XmlRpcExposedAttribute)
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,150 @@
namespace Nwc.XmlRpc
{
using System;
using System.Collections;
using System.IO;
using System.Xml;
using System.Net;
using System.Text;
using System.Reflection;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
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;
}
}
/// <summary>Class supporting the request side of an XML-RPC transaction.</summary>
public class XmlRpcRequest
{
private String _methodName = null;
private Encoding _encoding = new ASCIIEncoding();
private XmlRpcRequestSerializer _serializer = new XmlRpcRequestSerializer();
private XmlRpcResponseDeserializer _deserializer = new XmlRpcResponseDeserializer();
/// <summary><c>ArrayList</c> containing the parameters.</summary>
protected IList _params = null;
/// <summary>Instantiate an <c>XmlRpcRequest</c></summary>
public XmlRpcRequest()
{
_params = new ArrayList();
}
/// <summary>Instantiate an <c>XmlRpcRequest</c> for a specified method and parameters.</summary>
/// <param name="methodName"><c>String</c> designating the <i>object.method</i> on the server the request
/// should be directed to.</param>
/// <param name="parameters"><c>ArrayList</c> of XML-RPC type parameters to invoke the request with.</param>
public XmlRpcRequest(String methodName, IList parameters)
{
MethodName = methodName;
_params = parameters;
}
/// <summary><c>ArrayList</c> conntaining the parameters for the request.</summary>
public virtual IList Params
{
get { return _params; }
}
/// <summary><c>String</c> conntaining the method name, both object and method, that the request will be sent to.</summary>
public virtual String MethodName
{
get { return _methodName; }
set { _methodName = value; }
}
/// <summary><c>String</c> object name portion of the method name.</summary>
public String MethodNameObject
{
get
{
int index = MethodName.IndexOf(".");
if (index == -1)
return MethodName;
return MethodName.Substring(0, index);
}
}
/// <summary><c>String</c> method name portion of the object.method name.</summary>
public String MethodNameMethod
{
get
{
int index = MethodName.IndexOf(".");
if (index == -1)
return MethodName;
return MethodName.Substring(index + 1, MethodName.Length - index - 1);
}
}
/// <summary>Invoke this request on the server.</summary>
/// <param name="url"><c>String</c> The url of the XML-RPC server.</param>
/// <returns><c>Object</c> The value returned from the method invocation on the server.</returns>
/// <exception cref="XmlRpcException">If an exception generated on the server side.</exception>
public Object Invoke(String url)
{
XmlRpcResponse res = Send(url, 10000);
if (res.IsFault)
throw new XmlRpcException(res.FaultCode, res.FaultString);
return res.Value;
}
/// <summary>Send the request to the server.</summary>
/// <param name="url"><c>String</c> The url of the XML-RPC server.</param>
/// <param name="timeout">Milliseconds before the connection times out.</param>
/// <returns><c>XmlRpcResponse</c> The response generated.</returns>
public XmlRpcResponse Send(String url, int timeout)
{
// 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.Timeout = timeout;
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;
}
/// <summary>Produce <c>String</c> representation of the object.</summary>
/// <returns><c>String</c> representation of the object.</returns>
override public String ToString()
{
return _serializer.Serialize(this);
}
}
}

View File

@@ -0,0 +1,64 @@
namespace Nwc.XmlRpc
{
using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Xml;
/// <summary>Class to deserialize XML data representing a request.</summary>
public class XmlRpcRequestDeserializer : XmlRpcDeserializer
{
static private XmlRpcRequestDeserializer _singleton;
/// <summary>A static singleton instance of this deserializer.</summary>
[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;
}
}
/// <summary>Static method that parses XML data into a request using the Singleton.</summary>
/// <param name="xmlData"><c>StreamReader</c> containing an XML-RPC request.</param>
/// <returns><c>XmlRpcRequest</c> object resulting from the parse.</returns>
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;
}
}
}

View File

@@ -0,0 +1,51 @@
namespace Nwc.XmlRpc
{
using System;
using System.Collections;
using System.Xml;
using System.IO;
/// <summary>Class responsible for serializing an XML-RPC request.</summary>
/// <remarks>This class handles the request envelope, depending on <c>XmlRpcSerializer</c>
/// to serialize the payload.</remarks>
/// <seealso cref="XmlRpcSerializer"/>
public class XmlRpcRequestSerializer : XmlRpcSerializer
{
static private XmlRpcRequestSerializer _singleton;
/// <summary>A static singleton instance of this deserializer.</summary>
static public XmlRpcRequestSerializer Singleton
{
get
{
if (_singleton == null)
_singleton = new XmlRpcRequestSerializer();
return _singleton;
}
}
/// <summary>Serialize the <c>XmlRpcRequest</c> to the output stream.</summary>
/// <param name="output">An <c>XmlTextWriter</c> stream to write data to.</param>
/// <param name="obj">An <c>XmlRpcRequest</c> to serialize.</param>
/// <seealso cref="XmlRpcRequest"/>
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();
}
}
}

View File

@@ -0,0 +1,98 @@
namespace Nwc.XmlRpc
{
using System;
using System.Xml;
using System.Net.Sockets;
/// <summary>The class is a container of the context of an XML-RPC dialog on the server side.</summary>
/// <remarks>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. </remarks>
public class XmlRpcResponder
{
private XmlRpcRequestDeserializer _deserializer = new XmlRpcRequestDeserializer();
private XmlRpcResponseSerializer _serializer = new XmlRpcResponseSerializer();
private XmlRpcServer _server;
private TcpClient _client;
private SimpleHttpRequest _httpReq;
/// <summary>The SimpleHttpRequest based on the TcpClient.</summary>
public SimpleHttpRequest HttpReq
{
get { return _httpReq; }
}
/// <summary>Basic constructor.</summary>
/// <param name="server">XmlRpcServer that this XmlRpcResponder services.</param>
/// <param name="client">TcpClient with the connection.</param>
public XmlRpcResponder(XmlRpcServer server, TcpClient client)
{
_server = server;
_client = client;
_httpReq = new SimpleHttpRequest(_client);
}
/// <summary>Call close to insure proper shutdown.</summary>
~XmlRpcResponder()
{
Close();
}
///<summary>Respond using this responders HttpReq.</summary>
public void Respond()
{
Respond(HttpReq);
}
/// <summary>Handle an HTTP request containing an XML-RPC request.</summary>
/// <remarks>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.
/// </remarks>
/// <param name="httpReq"><c>SimpleHttpRequest</c> containing the request.</param>
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();
}
///<summary>Close all contained resources, both the HttpReq and client.</summary>
public void Close()
{
if (_httpReq != null)
{
_httpReq.Close();
_httpReq = null;
}
if (_client != null)
{
_client.Close();
_client = null;
}
}
}
}

View File

@@ -0,0 +1,85 @@
namespace Nwc.XmlRpc
{
using System;
using System.Collections;
using System.IO;
using System.Xml;
/// <summary>Class designed to represent an XML-RPC response.</summary>
public class XmlRpcResponse
{
private Object _value;
/// <summary><c>bool</c> indicating if this response represents a fault.</summary>
public bool IsFault;
/// <summary>Basic constructor</summary>
public XmlRpcResponse()
{
Value = null;
IsFault = false;
}
/// <summary>Constructor for a fault.</summary>
/// <param name="code"><c>int</c> the numeric faultCode value.</param>
/// <param name="message"><c>String</c> the faultString value.</param>
public XmlRpcResponse(int code, String message)
: this()
{
SetFault(code, message);
}
/// <summary>The data value of the response, may be fault data.</summary>
public Object Value
{
get { return _value; }
set
{
IsFault = false;
_value = value;
}
}
/// <summary>The faultCode if this is a fault.</summary>
public int FaultCode
{
get
{
if (!IsFault)
return 0;
else
return (int)((Hashtable)_value)[XmlRpcXmlTokens.FAULT_CODE];
}
}
/// <summary>The faultString if this is a fault.</summary>
public String FaultString
{
get
{
if (!IsFault)
return "";
else
return (String)((Hashtable)_value)[XmlRpcXmlTokens.FAULT_STRING];
}
}
/// <summary>Set this response to be a fault.</summary>
/// <param name="code"><c>int</c> the numeric faultCode value.</param>
/// <param name="message"><c>String</c> the faultString value.</param>
public void SetFault(int code, String message)
{
Hashtable fault = new Hashtable();
fault.Add("faultCode", code);
fault.Add("faultString", message);
Value = fault;
IsFault = true;
}
/// <summary>Form a useful string representation of the object, in this case the XML response.</summary>
/// <returns><c>String</c> The XML serialized XML-RPC response.</returns>
override public String ToString()
{
return XmlRpcResponseSerializer.Singleton.Serialize(this);
}
}
}

View File

@@ -0,0 +1,65 @@
namespace Nwc.XmlRpc
{
using System;
using System.Collections;
using System.IO;
using System.Xml;
/// <summary>Class to deserialize XML data representing a response.</summary>
public class XmlRpcResponseDeserializer : XmlRpcDeserializer
{
static private XmlRpcResponseDeserializer _singleton;
/// <summary>A static singleton instance of this deserializer.</summary>
[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;
}
}
/// <summary>Static method that parses XML data into a response using the Singleton.</summary>
/// <param name="xmlData"><c>StreamReader</c> containing an XML-RPC response.</param>
/// <returns><c>XmlRpcResponse</c> object resulting from the parse.</returns>
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;
}
}
}

View File

@@ -0,0 +1,57 @@
namespace Nwc.XmlRpc
{
using System;
using System.Collections;
using System.Xml;
/// <summary>Class responsible for serializing an XML-RPC response.</summary>
/// <remarks>This class handles the response envelope, depending on XmlRpcSerializer
/// to serialize the payload.</remarks>
/// <seealso cref="XmlRpcSerializer"/>
public class XmlRpcResponseSerializer : XmlRpcSerializer
{
static private XmlRpcResponseSerializer _singleton;
/// <summary>A static singleton instance of this deserializer.</summary>
static public XmlRpcResponseSerializer Singleton
{
get
{
if (_singleton == null)
_singleton = new XmlRpcResponseSerializer();
return _singleton;
}
}
/// <summary>Serialize the <c>XmlRpcResponse</c> to the output stream.</summary>
/// <param name="output">An <c>XmlTextWriter</c> stream to write data to.</param>
/// <param name="obj">An <c>Object</c> to serialize.</param>
/// <seealso cref="XmlRpcResponse"/>
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();
}
}
}

View File

@@ -0,0 +1,109 @@
namespace Nwc.XmlRpc
{
using System;
using System.Collections;
using System.IO;
using System.Xml;
/// <summary>Base class of classes serializing data to XML-RPC's XML format.</summary>
/// <remarks>This class handles the basic type conversions like Integer to &lt;i4&gt;. </remarks>
/// <seealso cref="XmlRpcXmlTokens"/>
public class XmlRpcSerializer : XmlRpcXmlTokens
{
/// <summary>Serialize the <c>XmlRpcRequest</c> to the output stream.</summary>
/// <param name="output">An <c>XmlTextWriter</c> stream to write data to.</param>
/// <param name="obj">An <c>Object</c> to serialize.</param>
/// <seealso cref="XmlRpcRequest"/>
virtual public void Serialize(XmlTextWriter output, Object obj)
{
}
/// <summary>Serialize the <c>XmlRpcRequest</c> to a String.</summary>
/// <remarks>Note this may represent a real memory hog for a large request.</remarks>
/// <param name="obj">An <c>Object</c> to serialize.</param>
/// <returns><c>String</c> containing XML-RPC representation of the request.</returns>
/// <seealso cref="XmlRpcRequest"/>
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;
}
/// <remarks>Serialize the object to the output stream.</remarks>
/// <param name="output">An <c>XmlTextWriter</c> stream to write data to.</param>
/// <param name="obj">An <c>Object</c> to serialize.</param>
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();
}
}
}
}

View File

@@ -0,0 +1,239 @@
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;
/// <summary>A restricted HTTP server for use with XML-RPC.</summary>
/// <remarks>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.
/// </remarks>
public class XmlRpcServer : IEnumerable
{
#pragma warning disable 0414 // disable "private field assigned but not used"
const int RESPONDER_COUNT = 10;
private TcpListener _myListener;
private int _port;
private IPAddress _address;
private IDictionary _handlers;
private XmlRpcSystemObject _system;
private WaitCallback _wc;
#pragma warning restore 0414
///<summary>Constructor with port and address.</summary>
///<remarks>This constructor sets up a TcpListener listening on the
///given port and address. It also calls a Thread on the method StartListen().</remarks>
///<param name="address"><c>IPAddress</c> value of the address to listen on.</param>
///<param name="port"><c>Int</c> value of the port to listen on.</param>
public XmlRpcServer(IPAddress address, int port)
{
_port = port;
_address = address;
_handlers = new Hashtable();
_system = new XmlRpcSystemObject(this);
_wc = new WaitCallback(WaitCallback);
}
///<summary>Basic constructor.</summary>
///<remarks>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.</remarks>
///<param name="port"><c>Int</c> value of the port to listen on.</param>
public XmlRpcServer(int port) : this(IPAddress.Any, port) { }
/// <summary>Start the server.</summary>
public void Start()
{
try
{
Stop();
//start listing on the given port
// IPAddress addr = IPAddress.Parse("127.0.0.1");
lock (this)
{
_myListener = new TcpListener(IPAddress.Any, _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);
}
}
/// <summary>Stop the server.</summary>
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);
}
}
/// <summary>Get an enumeration of my XML-RPC handlers.</summary>
/// <returns><c>IEnumerable</c> the handler enumeration.</returns>
public IEnumerator GetEnumerator()
{
return _handlers.GetEnumerator();
}
/// <summary>Retrieve a handler by name.</summary>
/// <param name="name"><c>String</c> naming a handler</param>
/// <returns><c>Object</c> that is the handler.</returns>
public Object this[String name]
{
get { return _handlers[name]; }
}
///<summary>
///This method Accepts new connections and dispatches them when appropriate.
///</summary>
public void StartListen()
{
while (true && _myListener != null)
{
//Accept a new connection
XmlRpcResponder responder = new XmlRpcResponder(this, _myListener.AcceptTcpClient());
ThreadPool.QueueUserWorkItem(_wc, responder);
}
}
///<summary>
///Add an XML-RPC handler object by name.
///</summary>
///<param name="name"><c>String</c> XML-RPC dispatch name of this object.</param>
///<param name="obj"><c>Object</c> The object that is the XML-RPC handler.</param>
public void Add(String name, Object obj)
{
_handlers.Add(name, obj);
}
///<summary>Return a C# object.method name for and XML-RPC object.method name pair.</summary>
///<param name="methodName">The XML-RPC object.method.</param>
///<returns><c>String</c> of form object.method for the underlying C# method.</returns>
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);
}
///<summary>Invoke a method described in a request.</summary>
///<param name="req"><c>XmlRpcRequest</c> containing a method descriptions.</param>
/// <seealso cref="XmlRpcSystemObject.Invoke"/>
/// <seealso cref="XmlRpcServer.Invoke(String,String,IList)"/>
public Object Invoke(XmlRpcRequest req)
{
return Invoke(req.MethodNameObject, req.MethodNameMethod, req.Params);
}
///<summary>Invoke a method on a named handler.</summary>
///<param name="objectName"><c>String</c> The name of the handler.</param>
///<param name="methodName"><c>String</c> The name of the method to invoke on the handler.</param>
///<param name="parameters"><c>IList</c> The parameters to invoke the method with.</param>
/// <seealso cref="XmlRpcSystemObject.Invoke"/>
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);
}
/// <summary>The method the thread pool invokes when a thread is available to handle an HTTP request.</summary>
/// <param name="responder">TcpClient from the socket accept.</param>
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();
}
/// <summary>
/// This function send the Header Information to the client (Browser)
/// </summary>
/// <param name="sHttpVersion">HTTP Version</param>
/// <param name="sMIMEHeader">Mime Type</param>
/// <param name="iTotBytes">Total Bytes to be sent in the body</param>
/// <param name="sStatusCode"></param>
/// <param name="output">Socket reference</param>
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);
}
}
}

View File

@@ -0,0 +1,252 @@
namespace Nwc.XmlRpc
{
using System;
using System.Collections;
using System.Reflection;
/// <summary> XML-RPC System object implementation of extended specifications.</summary>
[XmlRpcExposed]
public class XmlRpcSystemObject
{
private XmlRpcServer _server;
static private IDictionary _methodHelp = new Hashtable();
/// <summary>Static <c>IDictionary</c> to hold mappings of method name to associated documentation String</summary>
static public IDictionary MethodHelp
{
get { return _methodHelp; }
}
/// <summary>Constructor.</summary>
/// <param name="server"><c>XmlRpcServer</c> server to be the system object for.</param>
public XmlRpcSystemObject(XmlRpcServer server)
{
_server = server;
server.Add("system", this);
_methodHelp.Add(this.GetType().FullName + ".methodHelp", "Return a string description.");
}
/// <summary>Invoke a method on a given object.</summary>
/// <remarks>Using reflection, and respecting the <c>XmlRpcExposed</c> attribute,
/// invoke the <paramref>methodName</paramref> method on the <paramref>target</paramref>
/// instance with the <paramref>parameters</paramref> provided. All this packages other <c>Invoke</c> methods
/// end up calling this.</remarks>
/// <returns><c>Object</c> the value the invoked method returns.</returns>
/// <exception cref="XmlRpcException">If method does not exist, is not exposed, parameters invalid, or invocation
/// results in an exception. Note, the <c>XmlRpcException.Code</c> will indicate cause.</exception>
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);
}
}
/// <summary>List methods available on all handlers of this server.</summary>
/// <returns><c>IList</c> An array of <c>Strings</c>, each <c>String</c> will have form "object.method".</returns>
[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;
}
/// <summary>Given a method name return the possible signatures for it.</summary>
/// <param name="name"><c>String</c> The object.method name to look up.</param>
/// <returns><c>IList</c> Of arrays of signatures.</returns>
[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;
}
/// <summary>Help for given method signature. Not implemented yet.</summary>
/// <param name="name"><c>String</c> The object.method name to look up.</param>
/// <returns><c>String</c> help text. Rich HTML text.</returns>
[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;
}
/// <summary>Boxcarring support method.</summary>
/// <param name="calls"><c>IList</c> of calls</param>
/// <returns><c>ArrayList</c> of results/faults.</returns>
[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;
}
}
}

View File

@@ -0,0 +1,76 @@
namespace Nwc.XmlRpc
{
using System;
/// <summary>Class collecting <c>String</c> tokens that are part of XML-RPC files.</summary>
public class XmlRpcXmlTokens
{
/// <summary>C# formatting string to describe an ISO 8601 date.</summary>
public const String ISO_DATETIME = "yyyyMMdd\\THH\\:mm\\:ss";
/// <summary>Base64 field indicator.</summary>
/// <remarks>Corresponds to the &lt;base64&gt; tag.</remarks>
public const String BASE64 = "base64";
/// <summary>String field indicator.</summary>
/// <remarks>Corresponds to the &lt;string&gt; tag.</remarks>
public const String STRING = "string";
/// <summary>Integer field integer.</summary>
/// <remarks>Corresponds to the &lt;i4&gt; tag.</remarks>
public const String INT = "i4";
/// <summary>Alternate integer field indicator.</summary>
/// <remarks>Corresponds to the &lt;int&gt; tag.</remarks>
public const String ALT_INT = "int";
/// <summary>Date field indicator.</summary>
/// <remarks>Corresponds to the &lt;dateTime.iso8601&gt; tag.</remarks>
public const String DATETIME = "dateTime.iso8601";
/// <summary>Boolean field indicator.</summary>
/// <remarks>Corresponds to the &lt;boolean&gt; tag.</remarks>
public const String BOOLEAN = "boolean";
/// <summary>Value token.</summary>
/// <remarks>Corresponds to the &lt;value&gt; tag.</remarks>
public const String VALUE = "value";
/// <summary>Name token.</summary>
/// <remarks>Corresponds to the &lt;name&gt; tag.</remarks>
public const String NAME = "name";
/// <summary>Array field indicator..</summary>
/// <remarks>Corresponds to the &lt;array&gt; tag.</remarks>
public const String ARRAY = "array";
/// <summary>Data token.</summary>
/// <remarks>Corresponds to the &lt;data&gt; tag.</remarks>
public const String DATA = "data";
/// <summary>Member token.</summary>
/// <remarks>Corresponds to the &lt;member&gt; tag.</remarks>
public const String MEMBER = "member";
/// <summary>Stuct field indicator.</summary>
/// <remarks>Corresponds to the &lt;struct&gt; tag.</remarks>
public const String STRUCT = "struct";
/// <summary>Double field indicator.</summary>
/// <remarks>Corresponds to the &lt;double&gt; tag.</remarks>
public const String DOUBLE = "double";
/// <summary>Param token.</summary>
/// <remarks>Corresponds to the &lt;param&gt; tag.</remarks>
public const String PARAM = "param";
/// <summary>Params token.</summary>
/// <remarks>Corresponds to the &lt;params&gt; tag.</remarks>
public const String PARAMS = "params";
/// <summary>MethodCall token.</summary>
/// <remarks>Corresponds to the &lt;methodCall&gt; tag.</remarks>
public const String METHOD_CALL = "methodCall";
/// <summary>MethodName token.</summary>
/// <remarks>Corresponds to the &lt;methodName&gt; tag.</remarks>
public const String METHOD_NAME = "methodName";
/// <summary>MethodResponse token</summary>
/// <remarks>Corresponds to the &lt;methodResponse&gt; tag.</remarks>
public const String METHOD_RESPONSE = "methodResponse";
/// <summary>Fault response token.</summary>
/// <remarks>Corresponds to the &lt;fault&gt; tag.</remarks>
public const String FAULT = "fault";
/// <summary>FaultCode token.</summary>
/// <remarks>Corresponds to the &lt;faultCode&gt; tag.</remarks>
public const String FAULT_CODE = "faultCode";
/// <summary>FaultString token.</summary>
/// <remarks>Corresponds to the &lt;faultString&gt; tag.</remarks>
public const String FAULT_STRING = "faultString";
}
}