using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Text; using System.Threading; using UnityEngine; namespace AllocsFixes.NetConnections.Servers.Web { public class Web : IConsoleServer { private readonly HttpListener _listener = new HttpListener (); private Dictionary handlers = new Dictionary (); private bool authEnabled = false; private string realm = "7dtd Admin Panel"; public static int handlingCount = 0; public static int currentHandlers = 0; private string dataFolder; private bool mapEnabled = false; public Web () { try { int webPort = GamePrefs.GetInt (EnumGamePrefs.ControlPanelPort); if (webPort < 1 || webPort > 65533) { Log.Out ("Webserver not started (ControlPanelPort not within 1-65534)"); return; } if (!Directory.Exists (Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location) + "/webserver")) { Log.Out ("Webserver not started (folder \"webserver\" not found in WebInterface mod folder)"); return; } dataFolder = Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location) + "/webserver"; if (!HttpListener.IsSupported) { Log.Out ("Webserver not started (needs Windows XP SP2, Server 2003 or later or Mono)"); return; } handlers.Add ( "/index.htm", new SimpleRedirectHandler ("/static/index.html")); handlers.Add ( "/favicon.ico", new SimpleRedirectHandler ("/static/favicon.ico")); handlers.Add ( "/static/", new StaticHandler ( "/static/", dataFolder, new AllocsFixes.FileCache.DirectAccess (), true) ); // TODO: Enable cache handlers.Add ( "/itemicons/", new ItemIconHandler ( "/itemicons/", true) ); handlers.Add ( "/map/", new StaticHandler ( "/map/", GameUtils.GetSaveGameDir () + "/map", MapRendering.MapRendering.GetTileCache (), false) ); handlers.Add ("/api/", new ApiHandler ("/api/")); _listener.Prefixes.Add (String.Format ("http://*:{0}/", webPort + 2)); authEnabled = File.Exists (dataFolder + "/protect"); if (authEnabled) { _listener.AuthenticationSchemes = AuthenticationSchemes.Basic; } _listener.Start (); _listener.Realm = realm; SdtdConsole.Instance.RegisterServer (this); _listener.BeginGetContext (new AsyncCallback (HandleRequest), _listener); Log.Out ("Started Webserver on " + (webPort + 2) + " (authentication " + (authEnabled ? "enabled" : "disabled") + ")"); } catch (Exception e) { Log.Out ("Error in Web.ctor: " + e); } } private void HandleRequest (IAsyncResult result) { if (_listener.IsListening) { Interlocked.Increment (ref handlingCount); Interlocked.Increment (ref currentHandlers); HttpListenerContext ctx = _listener.EndGetContext (result); _listener.BeginGetContext (new AsyncCallback (HandleRequest), _listener); try { ctx.Response.ProtocolVersion = new Version ("1.0"); HttpListenerBasicIdentity user = Authorize (ctx); if (!authEnabled || (user.Name.ToLower ().Equals ("admin") && user.Password.Equals (GamePrefs.GetString (EnumGamePrefs.ControlPanelPassword)))) { if (ctx.Request.Url.AbsolutePath.Length < 2) { handlers ["/index.htm"].HandleRequest (ctx.Request, ctx.Response, user); return; } else { foreach (KeyValuePair kvp in handlers) { if (ctx.Request.Url.AbsolutePath.StartsWith (kvp.Key)) { kvp.Value.HandleRequest (ctx.Request, ctx.Response, user); return; } } } Log.Out ("Error in Web.HandleRequest(): No handler found for path \"" + ctx.Request.Url.AbsolutePath + "\""); ctx.Response.StatusCode = (int)HttpStatusCode.NotFound; } else { ctx.Response.StatusCode = (int)HttpStatusCode.Unauthorized; ctx.Response.Headers ["WWW-Authenticate"] = "Basic realm=\"" + realm + "\""; } } catch (IOException e) { if (e.InnerException is SocketException) { Log.Out ("Error in Web.HandleRequest(): Remote host closed connection: " + e.InnerException.Message); } else { Log.Out ("Error (IO) in Web.HandleRequest(): " + e); } } catch (Exception e) { Log.Out ("Error in Web.HandleRequest(): " + e); } finally { if (ctx != null) { ctx.Response.OutputStream.Close (); } Interlocked.Decrement (ref currentHandlers); } } } private HttpListenerBasicIdentity Authorize (HttpListenerContext ctx) { try { return (HttpListenerBasicIdentity)ctx.User.Identity; } catch (NullReferenceException) { return null; } } public void Disconnect () { try { _listener.Stop (); _listener.Close (); } catch (Exception e) { Log.Out ("Error in Web.Disconnect: " + e); } } public void SendLine (string line) { try { //Log.Out ("NOT IMPLEMENTED: Web.WriteToClient"); } catch (Exception e) { Log.Out ("Error in Web.WriteToClient: " + e); } } public void SendLog (string text, string trace, UnityEngine.LogType type) { //throw new System.NotImplementedException (); } public static void SetResponseTextContent (HttpListenerResponse resp, string text) { byte[] buf = Encoding.UTF8.GetBytes (text); resp.ContentLength64 = buf.Length; resp.ContentType = "text/html"; resp.ContentEncoding = Encoding.UTF8; resp.OutputStream.Write (buf, 0, buf.Length); } } }