Index: binary-improvements/MapRendering/Web/Web.cs
===================================================================
--- binary-improvements/MapRendering/Web/Web.cs	(revision 238)
+++ binary-improvements/MapRendering/Web/Web.cs	(revision 244)
@@ -1,4 +1,5 @@
 using System;
 using System.Collections.Generic;
+using System.Collections.Specialized;
 using System.IO;
 using System.Net;
@@ -9,15 +10,23 @@
 using UnityEngine;
 
+using AllocsFixes.NetConnections.Servers.Web.Handlers;
+
 namespace AllocsFixes.NetConnections.Servers.Web
 {
 	public class Web : IConsoleServer {
+		private const int GUEST_PERMISSION_LEVEL = 2000;
 		private readonly HttpListener _listener = new HttpListener ();
 		private Dictionary<string, PathHandler> handlers = new Dictionary<string, PathHandler> ();
-		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;
+		private bool useStaticCache = false;
+
+		public bool isSslRedirected {
+			private set;
+			get;
+		}
+
+		public ConnectionHandler connectionHandler;
 
 		public Web () {
@@ -25,5 +34,5 @@
 				int webPort = GamePrefs.GetInt (EnumGamePrefs.ControlPanelPort);
 				if (webPort < 1 || webPort > 65533) {
-					Log.Out ("Webserver not started (ControlPanelPort not within 1-65534)");
+					Log.Out ("Webserver not started (ControlPanelPort not within 1-65533)");
 					return;
 				}
@@ -33,4 +42,8 @@
 				}
 
+				// TODO: Read from config
+				isSslRedirected = false;
+				useStaticCache = false;
+
 				dataFolder = Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location) + "/webserver";
 
@@ -47,11 +60,33 @@
 						new SimpleRedirectHandler ("/static/favicon.ico"));
 				handlers.Add (
-						"/static/",
-						new StaticHandler (
-								"/static/",
-								dataFolder,
-								new AllocsFixes.FileCache.DirectAccess (),
-								true)
-				); // TODO: Enable cache
+						"/session/",
+						new SessionHandler (
+									"/session/",
+									dataFolder,
+									this)
+				);
+				handlers.Add (
+						"/userstatus",
+						new UserStatusHandler ()
+				);
+				if (useStaticCache) {
+					handlers.Add (
+							"/static/",
+							new StaticHandler (
+									"/static/",
+									dataFolder,
+									new AllocsFixes.FileCache.SimpleCache (),
+									true)
+					);
+				} else {
+					handlers.Add (
+							"/static/",
+							new StaticHandler (
+									"/static/",
+									dataFolder,
+									new AllocsFixes.FileCache.DirectAccess (),
+									true)
+					);
+				}
 
 				handlers.Add (
@@ -68,16 +103,17 @@
 						GameUtils.GetSaveGameDir () + "/map",
 						MapRendering.MapRendering.GetTileCache (),
-						false)
-				);
-
-				handlers.Add ("/api/", new ApiHandler ("/api/"));
+						false,
+						"web.map")
+				);
+
+				handlers.Add (
+					"/api/",
+					new ApiHandler ("/api/")
+				);
+
+				connectionHandler = new ConnectionHandler (this);
 
 				_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);
@@ -85,5 +121,5 @@
 				_listener.BeginGetContext (new AsyncCallback (HandleRequest), _listener);
 
-				Log.Out ("Started Webserver on " + (webPort + 2) + " (authentication " + (authEnabled ? "enabled" : "disabled") + ")");
+				Log.Out ("Started Webserver on " + (webPort + 2));
 			} catch (Exception e) {
 				Log.Out ("Error in Web.ctor: " + e);
@@ -98,27 +134,48 @@
 				_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<string, PathHandler> kvp in handlers) {
-								if (ctx.Request.Url.AbsolutePath.StartsWith (kvp.Key)) {
-									kvp.Value.HandleRequest (ctx.Request, ctx.Response, user);
-									return;
+					HttpListenerRequest request = ctx.Request;
+					HttpListenerResponse response = ctx.Response;
+
+					response.ProtocolVersion = new Version ("1.1");
+
+					WebConnection conn;
+					int permissionLevel = DoAuthentication (request, out conn);
+
+
+					//Log.Out ("Login status: conn!=null: {0}, permissionlevel: {1}", conn != null, permissionLevel);
+
+
+					if (conn != null) {
+						Cookie cookie = new Cookie ("sid", conn.SessionID, "/");
+						cookie.Expired = false;
+						cookie.Expires = new DateTime (2020, 1, 1);
+						cookie.HttpOnly = true;
+						cookie.Secure = false;
+						response.AppendCookie (cookie);
+					}
+
+					if (request.Url.AbsolutePath.Length < 2) {
+						handlers ["/index.htm"].HandleRequest (request, response, conn, permissionLevel);
+						return;
+					} else {
+						foreach (KeyValuePair<string, PathHandler> kvp in handlers) {
+							if (request.Url.AbsolutePath.StartsWith (kvp.Key)) {
+								if (!kvp.Value.IsAuthorizedForHandler (conn, permissionLevel)) {
+									response.StatusCode = (int)HttpStatusCode.Forbidden;
+									if (conn != null) {
+										Log.Out ("Web.HandleRequest: user '{0}' not allowed to access '{1}'", conn.SteamID, kvp.Value.ModuleName);
+									} else {
+										Log.Out ("Web.HandleRequest: unidentified user from '{0}' not allowed to access '{1}'", request.RemoteEndPoint.Address, kvp.Value.ModuleName);
+									}
+								} else {
+									kvp.Value.HandleRequest (request, response, conn, permissionLevel);
 								}
+								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 + "\"";
-					}
+					}
+
+					Log.Out ("Error in Web.HandleRequest(): No handler found for path \"" + request.Url.AbsolutePath + "\"");
+					response.StatusCode = (int)HttpStatusCode.NotFound;
 				} catch (IOException e) {
 					if (e.InnerException is SocketException) {
@@ -131,5 +188,5 @@
 				} finally {
 					if (ctx != null) {
-						ctx.Response.OutputStream.Close ();
+						ctx.Response.Close ();
 					}
 					Interlocked.Decrement (ref currentHandlers);
@@ -138,10 +195,42 @@
 		}
 
-		private HttpListenerBasicIdentity Authorize (HttpListenerContext ctx) {
-			try {
-				return (HttpListenerBasicIdentity)ctx.User.Identity;
-			} catch (NullReferenceException) {
-				return null;
-			}
+		private int DoAuthentication (HttpListenerRequest _req, out WebConnection _con) {
+			_con = null;
+
+			string sessionId = null;
+			if (_req.Cookies ["sid"] != null) {
+				sessionId = _req.Cookies ["sid"].Value;
+			}
+
+			if (!string.IsNullOrEmpty (sessionId)) {
+				WebConnection con = connectionHandler.IsLoggedIn (sessionId, _req.RemoteEndPoint.Address.ToString ());
+				if (con != null) {
+					_con = con;
+					return GameManager.Instance.adminTools.GetAdminToolsClientInfo (_con.SteamID.ToString ()).PermissionLevel;
+				}
+			}
+
+			if (_req.QueryString ["adminuser"] != null && _req.QueryString ["admintoken"] != null) {
+				WebPermissions.AdminToken admin = WebPermissions.Instance.GetWebAdmin (_req.QueryString ["adminuser"], _req.QueryString ["admintoken"]);
+				if (admin != null) {
+					return admin.permissionLevel;
+				} else {
+					Log.Warning ("Invalid Admintoken used from " + _req.RemoteEndPoint.ToString ());
+				}
+			}
+
+			if (_req.Url.AbsolutePath.StartsWith ("/session/verify")) {
+				ulong id = OpenID.Validate (_req);
+				if (id > 0) {
+					WebConnection con = connectionHandler.LogIn (id, _req.RemoteEndPoint.Address.ToString ());
+					_con = con;
+					//Log.Out ("Logged in with session id: {0}", con.SessionID);
+					return GameManager.Instance.adminTools.GetAdminToolsClientInfo (id.ToString ()).PermissionLevel;
+				} else {
+					Log.Out ("Steam OpenID login failed from {0}", _req.RemoteEndPoint.ToString ());
+				}
+			}
+
+			return GUEST_PERMISSION_LEVEL;
 		}
 
@@ -156,13 +245,9 @@
 
 		public void SendLine (string line) {
-			try {
-				//Log.Out ("NOT IMPLEMENTED: Web.WriteToClient");
-			} catch (Exception e) {
-				Log.Out ("Error in Web.WriteToClient: " + e);
-			}
+			connectionHandler.SendLine (line);
 		}
 
 		public void SendLog (string text, string trace, UnityEngine.LogType type) {
-			//throw new System.NotImplementedException ();
+			connectionHandler.SendLog (text, trace, type);
 		}
 
