* Switched Simian over to ExtensionLoader git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@2258 52acb1d6-8a22-11de-b505-999d5b087335
400 lines
17 KiB
C#
400 lines
17 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Net;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Xml;
|
|
using System.Threading;
|
|
using System.Reflection;
|
|
using ExtensionLoader;
|
|
using OpenMetaverse;
|
|
using OpenMetaverse.Capabilities;
|
|
using OpenMetaverse.Packets;
|
|
|
|
namespace Simian
|
|
{
|
|
public partial class Simian
|
|
{
|
|
// TODO: Don't hard-code these
|
|
public const uint REGION_X = 256000;
|
|
public const uint REGION_Y = 256000;
|
|
|
|
public int UDPPort = 9000;
|
|
public int HttpPort = 9000;
|
|
public string DataDir = "SimianData/";
|
|
|
|
public HttpServer HttpServer;
|
|
public ulong RegionHandle;
|
|
|
|
// Interfaces
|
|
public IAuthenticationProvider Authentication;
|
|
public IAccountProvider Accounts;
|
|
public IUDPProvider UDP;
|
|
public ISceneProvider Scene;
|
|
public IAssetProvider Assets;
|
|
public IAvatarProvider Avatars;
|
|
public IInventoryProvider Inventory;
|
|
public IParcelProvider Parcels;
|
|
public IMeshingProvider Mesher;
|
|
|
|
// Persistent extensions
|
|
public List<IPersistable> PersistentExtensions = new List<IPersistable>();
|
|
|
|
/// <summary>All of the agents currently connected to this UDP server</summary>
|
|
public Dictionary<UUID, Agent> Agents = new Dictionary<UUID, Agent>();
|
|
|
|
public Simian()
|
|
{
|
|
}
|
|
|
|
public bool Start(int port, bool ssl)
|
|
{
|
|
HttpPort = port;
|
|
UDPPort = port;
|
|
|
|
InitHttpServer(HttpPort, ssl);
|
|
|
|
RegionHandle = Helpers.UIntsToLong(REGION_X, REGION_Y);
|
|
|
|
try
|
|
{
|
|
// Load all of the extensions
|
|
List<string> references = new List<string>();
|
|
references.Add("OpenMetaverseTypes.dll");
|
|
references.Add("OpenMetaverse.dll");
|
|
references.Add("Simian.exe");
|
|
|
|
Dictionary<Type, FieldInfo> assignables = GetInterfaces();
|
|
|
|
ExtensionLoader<Simian>.LoadAllExtensions(Assembly.GetExecutingAssembly(),
|
|
AppDomain.CurrentDomain.BaseDirectory, this, references,
|
|
"Simian.*.dll", "Simian.*.cs", this, assignables);
|
|
}
|
|
catch (ExtensionException ex)
|
|
{
|
|
Logger.Log("Interface loading failed, shutting down: " + ex.Message, Helpers.LogLevel.Error);
|
|
Stop();
|
|
return false;
|
|
}
|
|
|
|
foreach (IExtension extension in ExtensionLoader<Simian>.Extensions)
|
|
{
|
|
// Track all of the extensions with persistence
|
|
if (extension is IPersistable)
|
|
PersistentExtensions.Add((IPersistable)extension);
|
|
}
|
|
|
|
foreach (IExtension extension in ExtensionLoader<Simian>.Extensions)
|
|
{
|
|
// Start persistance providers after all other extensions
|
|
if (!(extension is IPersistenceProvider))
|
|
{
|
|
Logger.DebugLog("Loading extension " + extension.GetType().Name);
|
|
extension.Start();
|
|
}
|
|
}
|
|
|
|
foreach (IExtension extension in ExtensionLoader<Simian>.Extensions)
|
|
{
|
|
// Start the persistance provider(s)
|
|
if (extension is IPersistenceProvider)
|
|
{
|
|
Logger.DebugLog("Loading persistance provider " + extension.GetType().Name);
|
|
extension.Start();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
foreach (IExtension extension in ExtensionLoader<Simian>.Extensions)
|
|
{
|
|
// Stop persistance providers first
|
|
if (extension is IPersistenceProvider)
|
|
extension.Stop();
|
|
}
|
|
|
|
foreach (IExtension extension in ExtensionLoader<Simian>.Extensions)
|
|
{
|
|
// Stop all other extensions
|
|
if (!(extension is IPersistenceProvider))
|
|
extension.Stop();
|
|
}
|
|
|
|
HttpServer.Stop();
|
|
}
|
|
|
|
public void DisconnectClient(Agent agent)
|
|
{
|
|
// Remove the avatar from the scene
|
|
SimulationObject obj;
|
|
if (Scene.TryGetObject(agent.AgentID, out obj))
|
|
Scene.ObjectRemove(this, obj);
|
|
else
|
|
Logger.Log("Disconnecting an agent that is not in the scene", Helpers.LogLevel.Warning);
|
|
|
|
// Remove the UDP client
|
|
UDP.RemoveClient(agent);
|
|
|
|
// HACK: Notify everyone when someone disconnects
|
|
OfflineNotificationPacket offline = new OfflineNotificationPacket();
|
|
offline.AgentBlock = new OfflineNotificationPacket.AgentBlockBlock[1];
|
|
offline.AgentBlock[0] = new OfflineNotificationPacket.AgentBlockBlock();
|
|
offline.AgentBlock[0].AgentID = agent.AgentID;
|
|
UDP.BroadcastPacket(offline, PacketCategory.State);
|
|
}
|
|
|
|
Dictionary<Type, FieldInfo> GetInterfaces()
|
|
{
|
|
Dictionary<Type, FieldInfo> interfaces = new Dictionary<Type, FieldInfo>();
|
|
|
|
foreach (FieldInfo field in this.GetType().GetFields())
|
|
{
|
|
if (field.FieldType.IsInterface)
|
|
interfaces.Add(field.FieldType, field);
|
|
}
|
|
|
|
return interfaces;
|
|
}
|
|
|
|
void InitHttpServer(int port, bool ssl)
|
|
{
|
|
HttpServer = new HttpServer(HttpPort, ssl);
|
|
|
|
// Login webpage HEAD request, used to check if the login webpage is alive
|
|
HttpRequestSignature signature = new HttpRequestSignature();
|
|
signature.Method = "head";
|
|
signature.ContentType = String.Empty;
|
|
signature.Path = "/loginpage";
|
|
HttpServer.HttpRequestCallback callback = new HttpServer.HttpRequestCallback(LoginWebpageHeadHandler);
|
|
HttpServer.HttpRequestHandler handler = new HttpServer.HttpRequestHandler(signature, callback);
|
|
HttpServer.AddHandler(handler);
|
|
|
|
// Login webpage GET request, gets the login webpage data (purely aesthetic)
|
|
signature.Method = "get";
|
|
signature.ContentType = String.Empty;
|
|
signature.Path = "/loginpage";
|
|
callback = new HttpServer.HttpRequestCallback(LoginWebpageGetHandler);
|
|
handler.Signature = signature;
|
|
handler.Callback = callback;
|
|
HttpServer.AddHandler(handler);
|
|
|
|
// Client XML-RPC login
|
|
signature.Method = "post";
|
|
signature.ContentType = "text/xml";
|
|
signature.Path = "/login";
|
|
callback = new HttpServer.HttpRequestCallback(LoginXmlRpcPostHandler);
|
|
handler.Signature = signature;
|
|
handler.Callback = callback;
|
|
HttpServer.AddHandler(handler);
|
|
|
|
// Client LLSD login
|
|
signature.Method = "post";
|
|
signature.ContentType = "application/xml";
|
|
signature.Path = "/login";
|
|
callback = new HttpServer.HttpRequestCallback(LoginLLSDPostHandler);
|
|
handler.Signature = signature;
|
|
handler.Callback = callback;
|
|
HttpServer.AddHandler(handler);
|
|
|
|
HttpServer.Start();
|
|
}
|
|
|
|
void LoginWebpageHeadHandler(HttpRequestSignature signature, ref HttpListenerContext context)
|
|
{
|
|
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
|
context.Response.StatusDescription = "OK";
|
|
}
|
|
|
|
void LoginWebpageGetHandler(HttpRequestSignature signature, ref HttpListenerContext context)
|
|
{
|
|
string pageContent = "<html><head><title>Simian</title></head><body><br/><h1>Welcome to Simian</h1></body></html>";
|
|
byte[] pageData = Encoding.UTF8.GetBytes(pageContent);
|
|
context.Response.OutputStream.Write(pageData, 0, pageData.Length);
|
|
context.Response.Close();
|
|
}
|
|
|
|
void LoginXmlRpcPostHandler(HttpRequestSignature signature, ref HttpListenerContext context)
|
|
{
|
|
string
|
|
firstName = String.Empty,
|
|
lastName = String.Empty,
|
|
password = String.Empty,
|
|
start = String.Empty,
|
|
version = String.Empty,
|
|
channel = String.Empty;
|
|
|
|
try
|
|
{
|
|
// Parse the incoming XML
|
|
XmlReader reader = XmlReader.Create(context.Request.InputStream);
|
|
|
|
reader.ReadStartElement("methodCall");
|
|
{
|
|
string methodName = reader.ReadElementContentAsString("methodName", String.Empty);
|
|
|
|
if (methodName == "login_to_simulator")
|
|
{
|
|
reader.ReadStartElement("params");
|
|
reader.ReadStartElement("param");
|
|
reader.ReadStartElement("value");
|
|
reader.ReadStartElement("struct");
|
|
{
|
|
while (reader.Name == "member")
|
|
{
|
|
reader.ReadStartElement("member");
|
|
{
|
|
string name = reader.ReadElementContentAsString("name", String.Empty);
|
|
|
|
reader.ReadStartElement("value");
|
|
{
|
|
switch (name)
|
|
{
|
|
case "first":
|
|
firstName = reader.ReadElementContentAsString("string", String.Empty);
|
|
break;
|
|
case "last":
|
|
lastName = reader.ReadElementContentAsString("string", String.Empty);
|
|
break;
|
|
case "passwd":
|
|
password = reader.ReadElementContentAsString("string", String.Empty);
|
|
break;
|
|
case "start":
|
|
start = reader.ReadElementContentAsString("string", String.Empty);
|
|
break;
|
|
case "version":
|
|
version = reader.ReadElementContentAsString("string", String.Empty);
|
|
break;
|
|
case "channel":
|
|
channel = reader.ReadElementContentAsString("string", String.Empty);
|
|
break;
|
|
default:
|
|
if (reader.Name == "string")
|
|
Console.WriteLine(String.Format("Ignore login xml value: name={0}, value={1}", name, reader.ReadInnerXml()));
|
|
else
|
|
Console.WriteLine(String.Format("Unknown login xml: name={0}, value={1}", name, reader.ReadInnerXml()));
|
|
break;
|
|
}
|
|
}
|
|
reader.ReadEndElement();
|
|
}
|
|
reader.ReadEndElement();
|
|
}
|
|
}
|
|
reader.ReadEndElement();
|
|
reader.ReadEndElement();
|
|
reader.ReadEndElement();
|
|
reader.ReadEndElement();
|
|
}
|
|
}
|
|
reader.ReadEndElement();
|
|
reader.Close();
|
|
|
|
LoginResponseData responseData = HandleLogin(firstName, lastName, password, start, version, channel);
|
|
XmlWriter writer = XmlWriter.Create(context.Response.OutputStream);
|
|
responseData.ToXmlRpc(writer);
|
|
writer.Close();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine(e.ToString());
|
|
}
|
|
}
|
|
|
|
void LoginLLSDPostHandler(HttpRequestSignature signature, ref HttpListenerContext context)
|
|
{
|
|
string body = String.Empty;
|
|
|
|
using (StreamReader reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding))
|
|
{
|
|
body = reader.ReadToEnd();
|
|
}
|
|
|
|
Console.WriteLine(body);
|
|
}
|
|
|
|
LoginResponseData HandleLogin(string firstName, string lastName, string password, string start, string version, string channel)
|
|
{
|
|
LoginResponseData response = new LoginResponseData();
|
|
Agent agent;
|
|
|
|
UUID agentID = Authentication.Authenticate(firstName, lastName, password);
|
|
if (agentID != UUID.Zero)
|
|
{
|
|
// Authentication successful, create a login instance of this agent
|
|
agent = Accounts.CreateInstance(agentID);
|
|
|
|
if (agent != null)
|
|
{
|
|
// Assign a circuit code and insert the agent into the unassociatedAgents dictionary
|
|
agent.CircuitCode = UDP.CreateCircuit(agent);
|
|
|
|
agent.TickLastPacketReceived = Environment.TickCount;
|
|
agent.LastLoginTime = Utils.DateTimeToUnixTime(DateTime.Now);
|
|
|
|
// Get this machine's IP address
|
|
IPHostEntry addresses = Dns.GetHostByName(Dns.GetHostName());
|
|
IPAddress simIP = addresses.AddressList.Length > 0 ? addresses.AddressList[0] : IPAddress.Loopback;
|
|
|
|
response.AgentID = agent.AgentID;
|
|
response.SecureSessionID = agent.SecureSessionID;
|
|
response.SessionID = agent.SessionID;
|
|
response.CircuitCode = agent.CircuitCode;
|
|
response.AgentAccess = agent.AccessLevel;
|
|
response.BuddyList = null; // FIXME:
|
|
response.FirstName = agent.FirstName;
|
|
response.HomeLookAt = agent.HomeLookAt;
|
|
response.HomePosition = agent.HomePosition;
|
|
response.HomeRegion = agent.HomeRegionHandle;
|
|
response.InventoryRoot = agent.InventoryRoot;
|
|
response.InventorySkeleton = null; // FIXME:
|
|
response.LastName = agent.LastName;
|
|
response.LibraryOwner = agent.InventoryLibraryOwner;
|
|
response.LibraryRoot = agent.InventoryLibraryRoot;
|
|
response.LibrarySkeleton = null; // FIXME:
|
|
response.LookAt = agent.CurrentLookAt;
|
|
response.Message = "Welcome to Simian";
|
|
response.Reason = String.Empty;
|
|
|
|
uint regionX, regionY;
|
|
Helpers.LongToUInts(agent.CurrentRegionHandle, out regionX, out regionY);
|
|
response.RegionX = regionX;
|
|
response.RegionY = regionY;
|
|
|
|
response.SecondsSinceEpoch = DateTime.Now;
|
|
// FIXME: Actually generate a seed capability
|
|
response.SeedCapability = String.Format("http://{0}:{1}/seed_caps", simIP, HttpPort);
|
|
response.SimIP = simIP;
|
|
response.SimPort = (ushort)UDPPort;
|
|
response.StartLocation = "last"; // FIXME:
|
|
response.Success = true;
|
|
}
|
|
else
|
|
{
|
|
// Something went wrong creating an agent instance, return a fail response
|
|
response.AgentID = agentID;
|
|
response.FirstName = firstName;
|
|
response.LastName = lastName;
|
|
response.Message = "Failed to create an account instance";
|
|
response.Reason = "account";
|
|
response.Success = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Authentication failed, return a fail response
|
|
response.AgentID = agentID;
|
|
response.FirstName = firstName;
|
|
response.LastName = lastName;
|
|
response.Message = "Authentication failed";
|
|
response.Reason = "key";
|
|
response.Success = false;
|
|
}
|
|
|
|
return response;
|
|
}
|
|
}
|
|
}
|