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 ExtensionLoader.Config; using OpenMetaverse; using OpenMetaverse.Capabilities; namespace Simian { public partial class Simian { public const string CONFIG_FILE = "Simian.ini"; // 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 IniConfigSource ConfigFile; 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 PersistentExtensions = new List(); /// All of the agents currently connected to this UDP server public Dictionary Agents = new Dictionary(); public Simian() { } public bool Start(int port, bool ssl) { HttpPort = port; UDPPort = port; List extensionList = null; try { // Load the extension list (and ordering) from our config file ConfigFile = new IniConfigSource(CONFIG_FILE); IConfig extensionConfig = ConfigFile.Configs["Extensions"]; extensionList = new List(extensionConfig.GetKeys()); } catch (Exception) { Logger.Log("Failed to load [Extensions] section from " + CONFIG_FILE, Helpers.LogLevel.Error); return false; } InitHttpServer(HttpPort, ssl); RegionHandle = Utils.UIntsToLong(REGION_X, REGION_Y); try { // Load all of the extensions List references = new List(); references.Add("OpenMetaverseTypes.dll"); references.Add("OpenMetaverse.dll"); references.Add("Simian.exe"); List assignables = ExtensionLoader.GetInterfaces(this); ExtensionLoader.LoadAllExtensions(Assembly.GetExecutingAssembly(), AppDomain.CurrentDomain.BaseDirectory, extensionList, 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.Extensions) { // Track all of the extensions with persistence if (extension is IPersistable) PersistentExtensions.Add((IPersistable)extension); } // Start all of the extensions foreach (IExtension extension in ExtensionLoader.Extensions) { Logger.Log("Starting extension " + extension.GetType().Name, Helpers.LogLevel.Info); extension.Start(this); } return true; } public void Stop() { foreach (IExtension extension in ExtensionLoader.Extensions) { // Stop persistence providers first if (extension is IPersistenceProvider) extension.Stop(); } foreach (IExtension extension in ExtensionLoader.Extensions) { // Stop all other extensions if (!(extension is IPersistenceProvider)) extension.Stop(); } HttpServer.Stop(); } void InitHttpServer(int port, bool ssl) { HttpServer = new HttpServer(port, ssl); // Login webpage HEAD request, used to check if the login webpage is alive HttpServer.AddHandler("head", null, "^/$", LoginWebpageHeadHandler); // Login webpage GET request, gets the login webpage data (purely aesthetic) HttpServer.AddHandler("get", null, @"^/(\?.*)?$", LoginWebpageGetHandler); // Client XML-RPC login HttpServer.AddHandler("post", "text/xml", "^/$", LoginXmlRpcPostHandler); // Client LLSD login HttpServer.AddHandler("post", "application/xml", "^/$", LoginLLSDPostHandler); HttpServer.Start(); } bool LoginWebpageHeadHandler(ref HttpListenerContext context) { context.Response.StatusCode = (int)HttpStatusCode.OK; context.Response.StatusDescription = "OK"; return true; } bool LoginWebpageGetHandler(ref HttpListenerContext context) { string pageContent = "Simian

Welcome to Simian

"; byte[] pageData = Encoding.UTF8.GetBytes(pageContent); context.Response.OutputStream.Write(pageData, 0, pageData.Length); context.Response.OutputStream.Close(); return true; } bool LoginXmlRpcPostHandler(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; case "platform": case "mac": case "id0": case "options": // Ignored values reader.ReadInnerXml(); break; default: if (reader.Name == "string") Console.WriteLine(String.Format("Ignore login xml value: name={0}", 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); if (responseData.Success) responseData.InventorySkeleton = Inventory.CreateInventorySkeleton(responseData.AgentID); XmlWriter writer = XmlWriter.Create(context.Response.OutputStream); responseData.ToXmlRpc(writer); writer.Close(); context.Response.Close(); } catch (Exception ex) { Logger.Log("XmlRpc login error: " + ex.Message, Helpers.LogLevel.Error, ex); } return true; } bool LoginLLSDPostHandler(ref HttpListenerContext context) { string body = String.Empty; using (StreamReader reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding)) { body = reader.ReadToEnd(); } Logger.DebugLog("LLSD login is not implemented:\n" + body); return true; } 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[addresses.AddressList.Length - 1] :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; Utils.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; } } }