| [224] | 1 | using System; | 
|---|
|  | 2 | using System.Collections.Generic; | 
|---|
|  | 3 | using System.IO; | 
|---|
|  | 4 | using System.Net; | 
|---|
|  | 5 | using System.Net.Sockets; | 
|---|
|  | 6 | using System.Reflection; | 
|---|
|  | 7 | using System.Text; | 
|---|
|  | 8 | using System.Threading; | 
|---|
|  | 9 | using UnityEngine; | 
|---|
|  | 10 |  | 
|---|
|  | 11 | namespace AllocsFixes.NetConnections.Servers.Web | 
|---|
|  | 12 | { | 
|---|
|  | 13 | public class Web : IServer { | 
|---|
|  | 14 | private readonly HttpListener _listener = new HttpListener (); | 
|---|
|  | 15 | private Dictionary<string, PathHandler> handlers = new Dictionary<string, PathHandler> (); | 
|---|
|  | 16 | private bool authEnabled = false; | 
|---|
|  | 17 | private string realm = "7dtd Admin Panel"; | 
|---|
|  | 18 | public static int handlingCount = 0; | 
|---|
|  | 19 | public static int currentHandlers = 0; | 
|---|
|  | 20 | private string dataFolder; | 
|---|
|  | 21 | private bool mapEnabled = false; | 
|---|
|  | 22 |  | 
|---|
|  | 23 | public Web () { | 
|---|
|  | 24 | try { | 
|---|
|  | 25 | int webPort = GamePrefs.GetInt (EnumGamePrefs.ControlPanelPort); | 
|---|
|  | 26 | if (webPort < 1 || webPort > 65533) { | 
|---|
|  | 27 | Log.Out ("Webserver not started (ControlPanelPort not within 1-65534)"); | 
|---|
|  | 28 | return; | 
|---|
|  | 29 | } | 
|---|
|  | 30 | if (!Directory.Exists (Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location) + "/webserver")) { | 
|---|
|  | 31 | Log.Out ("Webserver not started (folder \"webserver\" not found in WebInterface mod folder)"); | 
|---|
|  | 32 | return; | 
|---|
|  | 33 | } | 
|---|
|  | 34 |  | 
|---|
|  | 35 | dataFolder = Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location) + "/webserver"; | 
|---|
|  | 36 |  | 
|---|
|  | 37 | if (!HttpListener.IsSupported) { | 
|---|
|  | 38 | Log.Out ("Webserver not started (needs Windows XP SP2, Server 2003 or later or Mono)"); | 
|---|
|  | 39 | return; | 
|---|
|  | 40 | } | 
|---|
|  | 41 |  | 
|---|
|  | 42 | handlers.Add ( | 
|---|
|  | 43 | "/index.htm", | 
|---|
|  | 44 | new SimpleRedirectHandler ("/static/index.html")); | 
|---|
|  | 45 | handlers.Add ( | 
|---|
|  | 46 | "/favicon.ico", | 
|---|
|  | 47 | new SimpleRedirectHandler ("/static/favicon.ico")); | 
|---|
|  | 48 | handlers.Add ( | 
|---|
|  | 49 | "/static/", | 
|---|
|  | 50 | new StaticHandler ( | 
|---|
|  | 51 | "/static/", | 
|---|
|  | 52 | dataFolder, | 
|---|
|  | 53 | new AllocsFixes.FileCache.DirectAccess (), | 
|---|
|  | 54 | true) | 
|---|
|  | 55 | ); // TODO: Enable cache | 
|---|
|  | 56 |  | 
|---|
|  | 57 | { | 
|---|
|  | 58 | if (AllocsFixes.Mods.ModLoaded ("AllocsMapRendering")) { | 
|---|
|  | 59 | Assembly renderAssembly = AllocsFixes.Mods.GetModAssembly ("AllocsMapRendering"); | 
|---|
|  | 60 | foreach (Type t in renderAssembly.GetTypes ()) { | 
|---|
|  | 61 | foreach (MethodInfo mi in t.GetMethods ()) { | 
|---|
|  | 62 | if (mi.Name == "GetTileCache") { | 
|---|
|  | 63 | handlers.Add ( | 
|---|
|  | 64 | "/map/", | 
|---|
|  | 65 | new StaticHandler ( | 
|---|
|  | 66 | "/map/", | 
|---|
|  | 67 | StaticDirectories.GetSaveGameDir () + "/map", | 
|---|
|  | 68 | mi.Invoke (null, new object[0]) as AllocsFixes.FileCache.AbstractCache, | 
|---|
|  | 69 | false) | 
|---|
|  | 70 | ); | 
|---|
|  | 71 | mapEnabled = true; | 
|---|
|  | 72 | } | 
|---|
|  | 73 | } | 
|---|
|  | 74 | } | 
|---|
|  | 75 | } | 
|---|
|  | 76 | } | 
|---|
|  | 77 | handlers.Add ("/api/", new ApiHandler ("/api/")); | 
|---|
|  | 78 |  | 
|---|
|  | 79 | _listener.Prefixes.Add (String.Format ("http://*:{0}/", webPort + 2)); | 
|---|
|  | 80 | authEnabled = File.Exists (dataFolder + "/protect"); | 
|---|
|  | 81 | if (authEnabled) { | 
|---|
|  | 82 | _listener.AuthenticationSchemes = AuthenticationSchemes.Basic; | 
|---|
|  | 83 | } | 
|---|
|  | 84 | _listener.Start (); | 
|---|
|  | 85 | _listener.Realm = realm; | 
|---|
|  | 86 |  | 
|---|
|  | 87 | NetTelnetServer.RegisterServer (this); | 
|---|
|  | 88 |  | 
|---|
|  | 89 | _listener.BeginGetContext (new AsyncCallback (HandleRequest), _listener); | 
|---|
|  | 90 |  | 
|---|
|  | 91 | Log.Out ("Started Webserver on " + (webPort + 2) + " (authentication " + (authEnabled ? "enabled" : "disabled") + ")"); | 
|---|
|  | 92 | } catch (Exception e) { | 
|---|
|  | 93 | Log.Out ("Error in Web.ctor: " + e); | 
|---|
|  | 94 | } | 
|---|
|  | 95 | } | 
|---|
|  | 96 |  | 
|---|
|  | 97 | private void HandleRequest (IAsyncResult result) { | 
|---|
|  | 98 | if (_listener.IsListening) { | 
|---|
|  | 99 | Interlocked.Increment (ref handlingCount); | 
|---|
|  | 100 | Interlocked.Increment (ref currentHandlers); | 
|---|
|  | 101 | HttpListenerContext ctx = _listener.EndGetContext (result); | 
|---|
|  | 102 | _listener.BeginGetContext (new AsyncCallback (HandleRequest), _listener); | 
|---|
|  | 103 | try { | 
|---|
|  | 104 | ctx.Response.ProtocolVersion = new Version ("1.0"); | 
|---|
|  | 105 |  | 
|---|
|  | 106 | HttpListenerBasicIdentity user = Authorize (ctx); | 
|---|
|  | 107 |  | 
|---|
|  | 108 | if (!authEnabled || (user.Name.ToLower ().Equals ("admin") && user.Password.Equals (GamePrefs.GetString (EnumGamePrefs.ControlPanelPassword)))) { | 
|---|
|  | 109 | if (ctx.Request.Url.AbsolutePath.Length < 2) { | 
|---|
|  | 110 | handlers ["/index.htm"].HandleRequest (ctx.Request, ctx.Response, user); | 
|---|
|  | 111 | return; | 
|---|
|  | 112 | } else { | 
|---|
|  | 113 | foreach (KeyValuePair<string, PathHandler> kvp in handlers) { | 
|---|
|  | 114 | if (ctx.Request.Url.AbsolutePath.StartsWith (kvp.Key)) { | 
|---|
|  | 115 | kvp.Value.HandleRequest (ctx.Request, ctx.Response, user); | 
|---|
|  | 116 | return; | 
|---|
|  | 117 | } | 
|---|
|  | 118 | } | 
|---|
|  | 119 | } | 
|---|
|  | 120 |  | 
|---|
|  | 121 | Log.Out ("Error in Web.HandleRequest(): No handler found for path \"" + ctx.Request.Url.AbsolutePath + "\""); | 
|---|
|  | 122 | ctx.Response.StatusCode = (int)HttpStatusCode.NotFound; | 
|---|
|  | 123 | } else { | 
|---|
|  | 124 | ctx.Response.StatusCode = (int)HttpStatusCode.Unauthorized; | 
|---|
|  | 125 | ctx.Response.Headers ["WWW-Authenticate"] = "Basic realm=\"" + realm + "\""; | 
|---|
|  | 126 | } | 
|---|
|  | 127 | } catch (IOException e) { | 
|---|
|  | 128 | if (e.InnerException is SocketException) { | 
|---|
|  | 129 | Log.Out ("Error in Web.HandleRequest(): Remote host closed connection: " + e.InnerException.Message); | 
|---|
|  | 130 | } else { | 
|---|
|  | 131 | Log.Out ("Error (IO) in Web.HandleRequest(): " + e); | 
|---|
|  | 132 | } | 
|---|
|  | 133 | } catch (Exception e) { | 
|---|
|  | 134 | Log.Out ("Error in Web.HandleRequest(): " + e); | 
|---|
|  | 135 | } finally { | 
|---|
|  | 136 | if (ctx != null) { | 
|---|
|  | 137 | ctx.Response.OutputStream.Close (); | 
|---|
|  | 138 | } | 
|---|
|  | 139 | Interlocked.Decrement (ref currentHandlers); | 
|---|
|  | 140 | } | 
|---|
|  | 141 | } | 
|---|
|  | 142 | } | 
|---|
|  | 143 |  | 
|---|
|  | 144 | private HttpListenerBasicIdentity Authorize (HttpListenerContext ctx) { | 
|---|
|  | 145 | try { | 
|---|
|  | 146 | return (HttpListenerBasicIdentity)ctx.User.Identity; | 
|---|
|  | 147 | } catch (NullReferenceException) { | 
|---|
|  | 148 | return null; | 
|---|
|  | 149 | } | 
|---|
|  | 150 | } | 
|---|
|  | 151 |  | 
|---|
|  | 152 | public void Disconnect () { | 
|---|
|  | 153 | try { | 
|---|
|  | 154 | _listener.Stop (); | 
|---|
|  | 155 | _listener.Close (); | 
|---|
|  | 156 | } catch (Exception e) { | 
|---|
|  | 157 | Log.Out ("Error in Web.Disconnect: " + e); | 
|---|
|  | 158 | } | 
|---|
|  | 159 | } | 
|---|
|  | 160 |  | 
|---|
|  | 161 | public void SendLine (string line) { | 
|---|
|  | 162 | try { | 
|---|
|  | 163 | //Log.Out ("NOT IMPLEMENTED: Web.WriteToClient"); | 
|---|
|  | 164 | } catch (Exception e) { | 
|---|
|  | 165 | Log.Out ("Error in Web.WriteToClient: " + e); | 
|---|
|  | 166 | } | 
|---|
|  | 167 | } | 
|---|
|  | 168 |  | 
|---|
|  | 169 | public void SendLog (string text, string trace, UnityEngine.LogType type) { | 
|---|
|  | 170 | throw new System.NotImplementedException (); | 
|---|
|  | 171 | } | 
|---|
|  | 172 |  | 
|---|
|  | 173 | public static void SetResponseTextContent (HttpListenerResponse resp, string text) { | 
|---|
|  | 174 | byte[] buf = Encoding.UTF8.GetBytes (text); | 
|---|
|  | 175 | resp.ContentLength64 = buf.Length; | 
|---|
|  | 176 | resp.ContentType = "text/html"; | 
|---|
|  | 177 | resp.ContentEncoding = Encoding.UTF8; | 
|---|
|  | 178 | resp.OutputStream.Write (buf, 0, buf.Length); | 
|---|
|  | 179 | } | 
|---|
|  | 180 |  | 
|---|
|  | 181 | } | 
|---|
|  | 182 | } | 
|---|