private void Listen() { while (!ShutdownServer) { try { // Check for anybody whose authentication has expired. Dictionary<string, DateTime>.Enumerator en = _authenticated.GetEnumerator(); while (en.MoveNext()) { if (en.Current.Value < DateTime.Now) { _authenticated.Remove(en.Current.Key); en = _authenticated.GetEnumerator(); } } if (Active) { Listener.Prefixes.Clear(); Listener.Prefixes.Add("http://*:" + Config.Settings.WebServerPort + "/"); Listener.Start(); while (!ShutdownServer && Active) { IAsyncResult result = Listener.BeginGetContext(ListenerCallback, Listener); while (!ShutdownServer && !result.IsCompleted) { Thread.Sleep(500); if (!Active || ShutdownServer) { result.AsyncWaitHandle.Close(); break; } } } if (!Disposed && Listener.IsListening) Listener.Stop(); } else Thread.Sleep(500); } catch (ObjectDisposedException) { // Just restart the listener. Listener = new HttpListener(); } catch (HttpListenerException ex) { var message = "The web server had an error occur: " + ex.Message; if (ex.ErrorCode == 5) message = "Unable to open the web server's port. You may need administrative rights to do so."; if (ex.ErrorCode == 183) message = "Port " + Config.Settings.WebServerPort + " is in use. Please choose another port before starting the server again, or ensure " + "the port is available."; if (WebStatusChanged != null) { var e = new WebStatusChangedEventArgs { Active = false, Error = true, Message = message }; WebStatusChanged(this, e); } else MessageBox.Show(message, "Unexpected Error", MessageBoxButtons.OK, MessageBoxIcon.Error); if (!Disposed && Listener.IsListening) Listener.Stop(); Active = false; } catch (Exception ex) { if (WebStatusChanged != null) { var e = new WebStatusChangedEventArgs { Active = false, Error = true, Message = ex.Message }; WebStatusChanged(this, e); } else MessageBox.Show("The web server had an error occur: " + ex.Message, "Unexpected Error", MessageBoxButtons.OK, MessageBoxIcon.Error); if (!Disposed && Listener.IsListening) Listener.Stop(); Active = false; } } }
private void ListenerCallback(IAsyncResult result) { // Short circuits the callback if we're disposing. Saves us a lot of hassle with error handling. if (Disposed) return; try { var myListener = (HttpListener) result.AsyncState; HttpListenerContext context = myListener.EndGetContext(result); HttpListenerRequest request = context.Request; string content; using (var sr = new StreamReader(request.InputStream)) content = sr.ReadToEnd(); string originalUrl = request.RawUrl; string url = originalUrl; if (url.Contains("?")) { content = url.Substring(url.IndexOf("?") + 1); url = url.Substring(0, url.IndexOf("?")); } if (request.RemoteEndPoint == null) return; var remoteIP = request.RemoteEndPoint.Address.ToString(); var isAuthenticated = Config.Settings.WebServerPassword.Length > 0 && _authenticated.ContainsKey(remoteIP); var parameters = new Dictionary<string, string>(); if (content.Contains("=")) { string[] values = content.Split(new[] {"&"}, StringSplitOptions.RemoveEmptyEntries); foreach (string value in values) { if (value.Contains("=")) { string key = value.Substring(0, value.IndexOf("=")); string innerValue = ""; if (key.Length + 1 != value.Length) innerValue = value.Substring(value.IndexOf("=") + 1); if (!parameters.ContainsKey(key)) parameters.Add(key, innerValue); } else if (!parameters.ContainsKey(value)) parameters.Add(value, ""); } } HttpListenerResponse response = context.Response; switch (url.ToLower()) { case "/Style.css": { string html = Resources.Style; byte[] buffer = Encoding.UTF8.GetBytes(html); response.ContentLength64 = buffer.Length; response.OutputStream.Write(buffer, 0, buffer.Length); } break; case "/login.htm": { string html; if (isAuthenticated) { html = Resources.Authenticated; html = html.Replace("<!-- url -->", parameters.ContainsKey("target") ? HttpUtility.UrlDecode(parameters["target"]) : "/"); } else if (parameters.ContainsKey("target")) { html = GetAuthenticationPage(parameters["target"]); } else if (parameters.ContainsKey("Password") && parameters["Password"] == Config.Settings.WebServerPassword) { _authenticated.Add(remoteIP, DateTime.Now.AddDays(1)); html = Resources.Authenticated; html = html.Replace("<!-- url -->", parameters.ContainsKey("Target") ? HttpUtility.UrlDecode(parameters["Target"]) : "/"); } else { html = Resources.RetryAuthenticate; html = html.Replace("<!-- url -->", parameters.ContainsKey("Target") ? parameters["Target"] : "/"); } byte[] buffer = Encoding.UTF8.GetBytes(html); response.ContentLength64 = buffer.Length; response.OutputStream.Write(buffer, 0, buffer.Length); } break; case "/favicon.ico": { response.Headers.Add("Content-Type", "image/x-icon"); Icon favIcon = Resources.LuciusIcon; var ms = new MemoryStream(); favIcon.Save(ms); response.ContentLength64 = ms.Length; ms.Seek(0, SeekOrigin.Begin); var buffer = new byte[ms.Length]; ms.Read(buffer, 0, buffer.Length); response.OutputStream.Write(buffer, 0, buffer.Length); } break; case "/apple-touch-icon.png": case "/apple-touch-icon-precomposed.png": { response.Headers.Add("Content-Type", "image/png"); Image appleIcon = Resources.AppleTouchIcon; var ms = new MemoryStream(); appleIcon.Save(ms, ImageFormat.Png); response.ContentLength64 = ms.Length; ms.Seek(0, SeekOrigin.Begin); var buffer = new byte[ms.Length]; ms.Read(buffer, 0, buffer.Length); response.OutputStream.Write(buffer, 0, buffer.Length); } break; case "/tournament.htm": { string name = ""; if (parameters.ContainsKey("name")) name = HttpUtility.UrlDecode(parameters["name"]); if (string.IsNullOrEmpty(name)) { url = "/"; break; } name = name.Replace("%20", " "); var tournament = Config.Settings.GetTournament(name); if (tournament == null) { url = "/"; break; } var html = Resources.ChooseRound; html = html.Replace("<!-- name -->", tournament.Name + " -- Round " + tournament.Rounds.Count + " of " + tournament.TotalRounds); var message = isAuthenticated ? "\t\tChoose the Round to manage from the list below:<br />\r\n" + "\t\t(Unscored rounds are listed first, followed by rounds with scores.)<br />" : "\t\tBelow is a list of the Matches this round and their end scores, if recorded.<br />"; html = html.Replace("<!-- message -->", message); var roundsScored = ""; var roundsOpen = ""; var id = 0; foreach (var match in tournament.Rounds[tournament.Rounds.Count - 1].Matches) { var player1 = Config.Settings.GetPlayer(match.Players[0]); if (match.Players.Count == 2) { var player2 = Config.Settings.GetPlayer(match.Players[1]); if (isAuthenticated) { var tag = "\t\t<a href=\"Round.htm?name=" + HttpUtility.UrlEncode(tournament.Name) + "&id=" + id + "\"><p class=\"roundbordered\"><br />Match #" + (id + 1) + "<br />" + player1.Name + " VS " + player2.Name + "<br />"; if (match.Results.Count > 0) roundsScored += tag + "SCORED -> " + match.Results[player1.ID].VictoryPoints + " VPs VS " + match.Results[player2.ID].VictoryPoints + " VPs <- SCORED<br /><br /></p></a>\r\n"; else roundsOpen += tag + "0 VPs VS 0 VPs<br /><br /></p></a>\r\n"; } else { roundsScored += "\t\t<p class=\"round\"><br />Match #" + (id + 1) + "<br />" + player1.Name + " VS " + player2.Name + "<br />"; if (match.Results.Count > 0) roundsScored += "SCORED -> " + match.Results[player1.ID].VictoryPoints + " VPs VS " + match.Results[player2.ID].VictoryPoints + " VPs <- SCORED<br /></p>\r\n"; else roundsScored += "0 VPs VS 0 VPs<br /></p>\r\n"; } } else roundsScored += "\t\t<p class=\"round\"><br />" + player1.Name + " -- Bye Round<br /><br /></p>\r\n"; id++; } if (isAuthenticated) { string scores = "<p class=\"round\">Rounds with Scores<br /></p><br />" + roundsScored; if (roundsOpen.Length > 0) scores = "<p class=\"round\">Open Rounds<br /></p><br />" + roundsOpen + "<br /><br />\r\n" + scores; html = html.Replace("<!-- entries -->", scores); } else { roundsScored += "\r\n\t\t<a href=\"/Login.htm?target=" + originalUrl + "\"><p class=\"name\"><br />Log In to Edit Rounds<br /><br /></p></a>\r\n"; html = html.Replace("<!-- entries -->", roundsScored); } byte[] buffer = Encoding.UTF8.GetBytes(html); response.ContentLength64 = buffer.Length; response.OutputStream.Write(buffer, 0, buffer.Length); } break; case "/round.htm": { string html; if (!isAuthenticated) html = GetAuthenticationPage(originalUrl); else { var name = ""; if (parameters.ContainsKey("name")) name = HttpUtility.UrlDecode(parameters["name"]); var id = -1; if (parameters.ContainsKey("id")) int.TryParse(parameters["id"], out id); if (string.IsNullOrEmpty(name) || id <= -1) { url = "/"; break; } var tournament = Config.Settings.GetTournament(name); if (tournament == null) { url = "/"; break; } if (id >= tournament.Rounds[tournament.Rounds.Count - 1].Matches.Count) { url = "/"; break; } var match = tournament.Rounds[tournament.Rounds.Count - 1].Matches[id]; var player1 = Config.Settings.GetPlayer(match.Players[0]); var player2 = Config.Settings.GetPlayer(match.Players[1]); html = Resources.EditRound; html = html.Replace("<!-- error -->", ""); html = html.Replace("<!-- player1 -->", player1.Name); html = html.Replace("<!-- player1VP -->", match.Results.ContainsKey(player1.ID) ? match.Results[player1.ID].VictoryPoints.ToString() : "0"); html = html.Replace("<!-- player2 -->", player2.Name); html = html.Replace("<!-- player2VP -->", match.Results.ContainsKey(player2.ID) ? match.Results[player2.ID].VictoryPoints.ToString() : "0"); html = html.Replace("<!-- name -->", HttpUtility.UrlEncode(tournament.Name)); html = html.Replace("<!-- player1ID -->", player1.ID); html = html.Replace("<!-- player2ID -->", player2.ID); html = html.Replace("<!-- ID -->", id.ToString()); } var buffer = Encoding.UTF8.GetBytes(html); response.ContentLength64 = buffer.Length; response.OutputStream.Write(buffer, 0, buffer.Length); } break; case "/submit.htm": { string mode = ""; if (parameters.ContainsKey("Mode")) mode = parameters["Mode"]; if (mode == "UpdateScores") { string html; if (!isAuthenticated) html = GetAuthenticationPage(originalUrl); else { var name = HttpUtility.UrlDecode(parameters["Name"]); name = name.Replace("+", " "); var player1ID = parameters["Player1ID"]; var player2ID = parameters["Player2ID"]; var player1Vp = parameters["Player1VP"]; var player2Vp = parameters["Player2VP"]; var e = new MatchUpdateFromWebEventArgs { TournamentName = name, Player1ID = player1ID, Player2ID = player2ID }; if (player1Vp.ToUpper() != "F") { int vp; if (!int.TryParse(player1Vp, out vp)) { url = "/"; break; } e.Player1Vp = vp; } else e.Player1Forfeit = true; if (player2Vp.ToUpper() != "F") { int vp; if (!int.TryParse(player2Vp, out vp)) { url = "/"; break; } e.Player2Vp = vp; } else e.Player2Forfeit = true; if (MatchUpdateFromWeb != null) MatchUpdateFromWeb(this, e); html = Resources.Submitted; html = html.Replace("<!-- url -->", "Tournament.htm?name=" + parameters["Name"]); const string message = "Scores have been successfully submitted. Redirecting back to " + "the tournament page in a few seconds..."; html = html.Replace("<!-- message -->", message); } var buffer = Encoding.UTF8.GetBytes(html); response.ContentLength64 = buffer.Length; response.OutputStream.Write(buffer, 0, buffer.Length); } else url = "/"; } break; default: url = "/"; break; } // We may fall through to here if we got a bad URL. if (url.ToLower() == "/") { var html = Resources.ChooseTournament; var tournamentHTML = ""; foreach (var tournament in Config.Settings.Tournaments) if (!tournament.Completed && tournament.Rounds.Count > 0) { var currentRound = tournament.Rounds.Count; if (tournament.Rounds[currentRound - 1].Completed) { tournamentHTML += "\t\t<a href=\"Tournament.htm?name=" + HttpUtility.UrlEncode(tournament.Name) + "\">" + "<p><br />" + tournament.Name + " - Round " + currentRound + " Completed<br /><br /></p>\r\n"; } else { tournamentHTML += "\t\t<a href=\"Tournament.htm?name=" + HttpUtility.UrlEncode(tournament.Name) + "\">" + "<p><br />" + tournament.Name + " - Round " + currentRound + " Active<br /><br /></p>\r\n"; } } if (tournamentHTML.Length == 0) tournamentHTML = "\t\tThere are no active tournaments to record scores for!<br />"; html = html.Replace("<!-- entries -->", tournamentHTML); var buffer = Encoding.UTF8.GetBytes(html); response.ContentLength64 = buffer.Length; response.OutputStream.Write(buffer, 0, buffer.Length); } response.OutputStream.Close(); response.Close(); } catch (Exception ex) { // Code 995 occurs when the HttpListener is stopped. We can ignore this error. if (ex is HttpListenerException && ((HttpListenerException) ex).ErrorCode == 995) return; if (WebStatusChanged == null) return; var e = new WebStatusChangedEventArgs { Active = true, Error = true, Message = ex.Message }; WebStatusChanged(this, e); } }
private void Server_WebStatusChanged(object sender, WebStatusChangedEventArgs e) { if (!e.Active) { if (!lblWebStatus.Text.StartsWith("Listening") && LogWindow != null) LogWindow.WriteLog("Web Server Stopped Listening."); string IP = LocalIPAddress(); if (IP == null) lblWebStatus.Text = "Offline -- No Network Connection"; else lblWebStatus.Text = "Offline"; } if (e.Message.Length > 0 && LogWindow != null) LogWindow.WriteLog("[WebServer] " + (e.Error ? "Error: " : "") + e.Message); else MessageBox.Show(e.Message, "Web Server Error", MessageBoxButtons.OK, MessageBoxIcon.Error); }