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:
732
Programs/SLProxy/Analyst.cs
Normal file
732
Programs/SLProxy/Analyst.cs
Normal 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" : " ")
|
||||
+ "]"
|
||||
;
|
||||
}
|
||||
}
|
||||
107
Programs/SLProxy/ChatConsole.cs
Normal file
107
Programs/SLProxy/ChatConsole.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
121
Programs/SLProxy/ChatConsole.csproj
Normal file
121
Programs/SLProxy/ChatConsole.csproj
Normal 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>
|
||||
809
Programs/SLProxy/ClientAO/ClientAO/ClientAO.cs
Normal file
809
Programs/SLProxy/ClientAO/ClientAO/ClientAO.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
57
Programs/SLProxy/ClientAO/ClientAO/ClientAO.csproj
Normal file
57
Programs/SLProxy/ClientAO/ClientAO/ClientAO.csproj
Normal 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>
|
||||
35
Programs/SLProxy/ClientAO/README.txt
Normal file
35
Programs/SLProxy/ClientAO/README.txt
Normal 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.
|
||||
54
Programs/SLProxy/ClientAO/configuration sample.txt
Normal file
54
Programs/SLProxy/ClientAO/configuration sample.txt
Normal 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) ] ::
|
||||
|
||||
1
Programs/SLProxy/README.txt
Normal file
1
Programs/SLProxy/README.txt
Normal file
@@ -0,0 +1 @@
|
||||
All information about SLProxy can be found on the libsecondlife wiki at http://www.libsecondlife.org/wiki/SLProxy
|
||||
153
Programs/SLProxy/SLProxy.build
Normal file
153
Programs/SLProxy/SLProxy.build
Normal 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
2127
Programs/SLProxy/SLProxy.cs
Normal file
File diff suppressed because it is too large
Load Diff
140
Programs/SLProxy/SLProxy.csproj
Normal file
140
Programs/SLProxy/SLProxy.csproj
Normal 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>
|
||||
210
Programs/SLProxy/SLProxyLoader.cs
Normal file
210
Programs/SLProxy/SLProxyLoader.cs
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
14
Programs/SLProxy/SLProxyMain.cs
Normal file
14
Programs/SLProxy/SLProxyMain.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
46
Programs/SLProxy/XmlRpcCS/Logger.cs
Normal file
46
Programs/SLProxy/XmlRpcCS/Logger.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
204
Programs/SLProxy/XmlRpcCS/SimpleHttpRequest.cs
Normal file
204
Programs/SLProxy/XmlRpcCS/SimpleHttpRequest.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
51
Programs/SLProxy/XmlRpcCS/XmlRpcBoxcarRequest.cs
Normal file
51
Programs/SLProxy/XmlRpcCS/XmlRpcBoxcarRequest.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
61
Programs/SLProxy/XmlRpcCS/XmlRpcClientProxy.cs
Normal file
61
Programs/SLProxy/XmlRpcCS/XmlRpcClientProxy.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
195
Programs/SLProxy/XmlRpcCS/XmlRpcDeserializer.cs
Normal file
195
Programs/SLProxy/XmlRpcCS/XmlRpcDeserializer.cs
Normal 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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
51
Programs/SLProxy/XmlRpcCS/XmlRpcErrorCodes.cs
Normal file
51
Programs/SLProxy/XmlRpcCS/XmlRpcErrorCodes.cs
Normal 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";
|
||||
}
|
||||
}
|
||||
39
Programs/SLProxy/XmlRpcCS/XmlRpcException.cs
Normal file
39
Programs/SLProxy/XmlRpcCS/XmlRpcException.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
60
Programs/SLProxy/XmlRpcCS/XmlRpcExposedAttribute.cs
Normal file
60
Programs/SLProxy/XmlRpcCS/XmlRpcExposedAttribute.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
150
Programs/SLProxy/XmlRpcCS/XmlRpcRequest.cs
Normal file
150
Programs/SLProxy/XmlRpcCS/XmlRpcRequest.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
64
Programs/SLProxy/XmlRpcCS/XmlRpcRequestDeserializer.cs
Normal file
64
Programs/SLProxy/XmlRpcCS/XmlRpcRequestDeserializer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
51
Programs/SLProxy/XmlRpcCS/XmlRpcRequestSerializer.cs
Normal file
51
Programs/SLProxy/XmlRpcCS/XmlRpcRequestSerializer.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
98
Programs/SLProxy/XmlRpcCS/XmlRpcResponder.cs
Normal file
98
Programs/SLProxy/XmlRpcCS/XmlRpcResponder.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
85
Programs/SLProxy/XmlRpcCS/XmlRpcResponse.cs
Normal file
85
Programs/SLProxy/XmlRpcCS/XmlRpcResponse.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
Programs/SLProxy/XmlRpcCS/XmlRpcResponseDeserializer.cs
Normal file
65
Programs/SLProxy/XmlRpcCS/XmlRpcResponseDeserializer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
57
Programs/SLProxy/XmlRpcCS/XmlRpcResponseSerializer.cs
Normal file
57
Programs/SLProxy/XmlRpcCS/XmlRpcResponseSerializer.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
109
Programs/SLProxy/XmlRpcCS/XmlRpcSerializer.cs
Normal file
109
Programs/SLProxy/XmlRpcCS/XmlRpcSerializer.cs
Normal 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 <i4>. </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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
239
Programs/SLProxy/XmlRpcCS/XmlRpcServer.cs
Normal file
239
Programs/SLProxy/XmlRpcCS/XmlRpcServer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
252
Programs/SLProxy/XmlRpcCS/XmlRpcSystemObject.cs
Normal file
252
Programs/SLProxy/XmlRpcCS/XmlRpcSystemObject.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
76
Programs/SLProxy/XmlRpcCS/XmlRpcXmlTokens.cs
Normal file
76
Programs/SLProxy/XmlRpcCS/XmlRpcXmlTokens.cs
Normal 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 <base64> tag.</remarks>
|
||||
public const String BASE64 = "base64";
|
||||
/// <summary>String field indicator.</summary>
|
||||
/// <remarks>Corresponds to the <string> tag.</remarks>
|
||||
public const String STRING = "string";
|
||||
/// <summary>Integer field integer.</summary>
|
||||
/// <remarks>Corresponds to the <i4> tag.</remarks>
|
||||
public const String INT = "i4";
|
||||
/// <summary>Alternate integer field indicator.</summary>
|
||||
/// <remarks>Corresponds to the <int> tag.</remarks>
|
||||
public const String ALT_INT = "int";
|
||||
/// <summary>Date field indicator.</summary>
|
||||
/// <remarks>Corresponds to the <dateTime.iso8601> tag.</remarks>
|
||||
public const String DATETIME = "dateTime.iso8601";
|
||||
/// <summary>Boolean field indicator.</summary>
|
||||
/// <remarks>Corresponds to the <boolean> tag.</remarks>
|
||||
public const String BOOLEAN = "boolean";
|
||||
/// <summary>Value token.</summary>
|
||||
/// <remarks>Corresponds to the <value> tag.</remarks>
|
||||
public const String VALUE = "value";
|
||||
/// <summary>Name token.</summary>
|
||||
/// <remarks>Corresponds to the <name> tag.</remarks>
|
||||
public const String NAME = "name";
|
||||
/// <summary>Array field indicator..</summary>
|
||||
/// <remarks>Corresponds to the <array> tag.</remarks>
|
||||
public const String ARRAY = "array";
|
||||
/// <summary>Data token.</summary>
|
||||
/// <remarks>Corresponds to the <data> tag.</remarks>
|
||||
public const String DATA = "data";
|
||||
/// <summary>Member token.</summary>
|
||||
/// <remarks>Corresponds to the <member> tag.</remarks>
|
||||
public const String MEMBER = "member";
|
||||
/// <summary>Stuct field indicator.</summary>
|
||||
/// <remarks>Corresponds to the <struct> tag.</remarks>
|
||||
public const String STRUCT = "struct";
|
||||
/// <summary>Double field indicator.</summary>
|
||||
/// <remarks>Corresponds to the <double> tag.</remarks>
|
||||
public const String DOUBLE = "double";
|
||||
/// <summary>Param token.</summary>
|
||||
/// <remarks>Corresponds to the <param> tag.</remarks>
|
||||
public const String PARAM = "param";
|
||||
/// <summary>Params token.</summary>
|
||||
/// <remarks>Corresponds to the <params> tag.</remarks>
|
||||
public const String PARAMS = "params";
|
||||
/// <summary>MethodCall token.</summary>
|
||||
/// <remarks>Corresponds to the <methodCall> tag.</remarks>
|
||||
public const String METHOD_CALL = "methodCall";
|
||||
/// <summary>MethodName token.</summary>
|
||||
/// <remarks>Corresponds to the <methodName> tag.</remarks>
|
||||
public const String METHOD_NAME = "methodName";
|
||||
/// <summary>MethodResponse token</summary>
|
||||
/// <remarks>Corresponds to the <methodResponse> tag.</remarks>
|
||||
public const String METHOD_RESPONSE = "methodResponse";
|
||||
/// <summary>Fault response token.</summary>
|
||||
/// <remarks>Corresponds to the <fault> tag.</remarks>
|
||||
public const String FAULT = "fault";
|
||||
/// <summary>FaultCode token.</summary>
|
||||
/// <remarks>Corresponds to the <faultCode> tag.</remarks>
|
||||
public const String FAULT_CODE = "faultCode";
|
||||
/// <summary>FaultString token.</summary>
|
||||
/// <remarks>Corresponds to the <faultString> tag.</remarks>
|
||||
public const String FAULT_STRING = "faultString";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user