diff --git a/src/lib/MBS.Web/UI/WebControls/Button.cs b/src/lib/MBS.Web/UI/WebControls/Button.cs index a8dac49..a2ab5f3 100644 --- a/src/lib/MBS.Web/UI/WebControls/Button.cs +++ b/src/lib/MBS.Web/UI/WebControls/Button.cs @@ -18,7 +18,7 @@ public class Button : WebControlWithMnemonic { if (DropDown != null) { - return base.GetStyleClasses().Union(["uwt-button-dropdownbutton"]); + return base.GetStyleClasses().Union(new string[] { "uwt-button-dropdownbutton" }); } return base.GetStyleClasses(); } diff --git a/src/lib/MBS.Web/UI/WebControls/ContainerBase.cs b/src/lib/MBS.Web/UI/WebControls/ContainerBase.cs index 02a93c5..a0e1714 100644 --- a/src/lib/MBS.Web/UI/WebControls/ContainerBase.cs +++ b/src/lib/MBS.Web/UI/WebControls/ContainerBase.cs @@ -4,7 +4,7 @@ namespace MBS.Web.UI.WebControls; public abstract class ContainerBase : WebControl { - private IEnumerable _list = [ ]; + private IEnumerable _list = new Control[0]; protected virtual IEnumerable GetChildControls() { return _list; diff --git a/src/lib/MBS.Web/UI/WebControls/DockableContainer.cs b/src/lib/MBS.Web/UI/WebControls/DockableContainer.cs index 8829998..3e0bbff 100644 --- a/src/lib/MBS.Web/UI/WebControls/DockableContainer.cs +++ b/src/lib/MBS.Web/UI/WebControls/DockableContainer.cs @@ -47,7 +47,7 @@ public class DockableContainer : Container public CardinalDirection Position { get; set; } = CardinalDirection.Bottom; protected override IEnumerable GetStyleClasses() - { + { string uwtDockPosition = ""; switch (Position) { @@ -56,7 +56,7 @@ public class DockableContainer : Container case CardinalDirection.Right: uwtDockPosition = "uwt-dock-right"; break; case CardinalDirection.Bottom: uwtDockPosition = "uwt-dock-bottom"; break; } - return base.GetStyleClasses().Union([ "uwt-dockable", uwtDockPosition ]); + return base.GetStyleClasses().Union(new string[] { "uwt-dockable", uwtDockPosition }); } diff --git a/src/lib/MBS.Web/UI/WebControls/ListView.cs b/src/lib/MBS.Web/UI/WebControls/ListView.cs index 134d5e2..d38ff46 100644 --- a/src/lib/MBS.Web/UI/WebControls/ListView.cs +++ b/src/lib/MBS.Web/UI/WebControls/ListView.cs @@ -115,7 +115,7 @@ public class ListView : Container protected override IEnumerable GetStyleClasses() { - return [ "uwt-listview" ]; + return new string[] { "uwt-listview" }; } protected override IReadOnlyDictionary GetStyleProperties() { diff --git a/src/lib/MBS.Web/UI/WebControls/Menu.cs b/src/lib/MBS.Web/UI/WebControls/Menu.cs index 5fec8a0..93942e8 100644 --- a/src/lib/MBS.Web/UI/WebControls/Menu.cs +++ b/src/lib/MBS.Web/UI/WebControls/Menu.cs @@ -6,7 +6,7 @@ public class Menu : ContainerBase protected override string TagName => "ul"; protected override IEnumerable GetStyleClasses() { - return [ "uwt-menu" ]; + return new string[] { "uwt-menu" }; } private IEnumerable? _items = null; @@ -25,7 +25,7 @@ public class Menu : ContainerBase { return _items; } - return []; + return new Control[0]; } } diff --git a/src/lib/MBS.Web/UI/WebControls/MenuItem.cs b/src/lib/MBS.Web/UI/WebControls/MenuItem.cs index 654349c..28f7c54 100644 --- a/src/lib/MBS.Web/UI/WebControls/MenuItem.cs +++ b/src/lib/MBS.Web/UI/WebControls/MenuItem.cs @@ -27,14 +27,14 @@ public abstract class MenuItem : ContainerBase protected override IEnumerable GetStyleClasses() { - return base.GetStyleClasses().Union(["uwt-menu-item"]); + return base.GetStyleClasses().Union(new string[] { "uwt-menu-item" }); } } public class CommandMenuItem : MenuItem { protected override IEnumerable GetStyleClasses() { - return base.GetStyleClasses().Union(["uwt-menu-item-command"]); + return base.GetStyleClasses().Union(new string[] { "uwt-menu-item-command" }); } public string Text { get; set; } @@ -55,7 +55,7 @@ public class CommandMenuItem : MenuItem { a.TargetUrl = TargetUrl; } - return [a]; + return new Control[] { a }; } } \ No newline at end of file diff --git a/src/lib/MBS.Web/UI/WebPage.cs b/src/lib/MBS.Web/UI/WebPage.cs index df99926..6700641 100644 --- a/src/lib/MBS.Web/UI/WebPage.cs +++ b/src/lib/MBS.Web/UI/WebPage.cs @@ -39,7 +39,7 @@ public class WebPage : Control protected virtual IEnumerable GetHeaderControls() { - return [ ]; + return new Control[0]; } protected override string TagName => "html"; diff --git a/src/lib/MBS.Web/WebApplication.cs b/src/lib/MBS.Web/WebApplication.cs index 161ad60..76f1c9a 100644 --- a/src/lib/MBS.Web/WebApplication.cs +++ b/src/lib/MBS.Web/WebApplication.cs @@ -24,7 +24,7 @@ public abstract class WebApplication : Application ProcessRequest?.Invoke(this, e); } - private void server_OnProcessRequest(object sender, WebServerProcessRequestEventArgs e) + private void server_OnProcessRequest(object? sender, WebServerProcessRequestEventArgs e) { OnProcessRequest(e); } @@ -35,11 +35,16 @@ public abstract class WebApplication : Application ServerCreated?.Invoke(this, e); } + protected virtual WebServer CreateWebServer() + { + return new WebServer(new IPEndPoint(IPAddress.Any, DefaultPort)); + } + protected override void OnStartup(EventArgs e) { base.OnStartup(e); - WebServer server = new WebServer(new IPEndPoint(IPAddress.Any, DefaultPort)); + WebServer server = CreateWebServer(); Console.WriteLine("attempting to start server listening on port {0}", DefaultPort); OnServerCreated(new WebServerCreatedEventArgs(server)); diff --git a/src/lib/MBS.Web/WebRequest.cs b/src/lib/MBS.Web/WebRequest.cs index 96b9e9b..15a4444 100644 --- a/src/lib/MBS.Web/WebRequest.cs +++ b/src/lib/MBS.Web/WebRequest.cs @@ -1,6 +1,7 @@ using System.Collections.ObjectModel; using System.Data.SqlTypes; using System.Net; +using MBS.Core; namespace MBS.Web; @@ -31,12 +32,12 @@ public class WebRequest int queryStringIndex = path.IndexOf('?'); if (queryStringIndex > -1) { - Path = path.Substring(0, queryStringIndex); + Path = path.Substring(0, queryStringIndex).UrlDecode(); QueryString = path.Substring(queryStringIndex + 1); } else { - Path = path; + Path = path.UrlDecode(); } PathParts = Path.Split(new char[] { '/' }); @@ -44,17 +45,18 @@ public class WebRequest foreach (string queryStringPart in queryStringParts) { string[] nameValue = queryStringPart.Split(new char[] { '=' }); + string key = nameValue[0].UrlDecode(); if (nameValue.Length > 1) { - if (!Query.ContainsKey(nameValue[0])) + if (!Query.ContainsKey(key)) { - Query[nameValue[0]] = new List(); + Query[key] = new List(); } - Query[nameValue[0]].Add(nameValue[1]); + Query[key].Add(nameValue[1].UrlDecode()); } else { - Query[nameValue[0]] = new List(); + Query[key] = new List(); } } diff --git a/src/lib/MBS.Web/WebServer.cs b/src/lib/MBS.Web/WebServer.cs index cab5bcb..9958a9f 100644 --- a/src/lib/MBS.Web/WebServer.cs +++ b/src/lib/MBS.Web/WebServer.cs @@ -1,5 +1,7 @@ using System.Net; +using System.Net.Security; using System.Net.Sockets; +using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Text; using MBS.Core; @@ -160,6 +162,9 @@ public class WebServer private Dictionary> _sessions = new Dictionary>(); + public string? SSLCertificateFile = null; + public string? SSLCertificateKeyFile = null; + private void HandleClient(System.Net.Sockets.TcpClient client) { // WebServerProcessRequestEventArgs e = new WebServerProcessRequestEventArgs(); @@ -174,9 +179,40 @@ public class WebServer // client.ReceiveTimeout = 5000; // client.SendTimeout = 5000; - NetworkStream st = client.GetStream(); + Stream st = client.GetStream(); + + if (ServerCertificate != null) + { + SslStream ssl = new SslStream(client.GetStream(), false); + try + { + ssl.AuthenticateAsServer(ServerCertificate, false, SslProtocols.None, false); + } + catch (AuthenticationException ex) + { + Console.Error.WriteLine("AuthenticationException: {0}", ex.Message); + } + + st = ssl; + } + else + { + Console.WriteLine("MBS.Web.WebServer: !! NOT using SSL !!"); + } + StreamReader sr = new StreamReader(st); + /* + byte[] buffer = new byte[4096]; + int len = st.Read(buffer, 0, buffer.Length); + Array.Resize(ref buffer, len); + + string text = System.Text.Encoding.UTF8.GetString(buffer); + Console.WriteLine(text); + */ + string line = sr.ReadLine(); + Console.Error.WriteLine(line); + if (line == null) { Console.Error.WriteLine("unexpected NULL line from client"); @@ -186,7 +222,7 @@ public class WebServer string[] lineParts = line.Split(new char[] { ' ' }); if (lineParts.Length != 3) { - Console.Error.WriteLine("unexpected request from client:\n----> {0}", line); + Console.Error.WriteLine("unexpected request from client"); client.Close(); return; } @@ -224,18 +260,21 @@ public class WebServer if (contentLength != "") { int contentLengthInt = Int32.Parse(contentLength); + Console.Error.WriteLine("Content-Length: {0}", contentLengthInt); char[] buffer = new char[contentLengthInt]; sr.ReadBlock(buffer, 0, contentLengthInt); string content = new string(buffer); + Console.Error.WriteLine(content); + if (!String.IsNullOrEmpty(content)) { string[] kvps = content.Split(new char[] { '&' }); foreach (string kvp in kvps) { string[] pv = kvp.Split(new char[] { '=' }); - form[pv[0]] = pv[1].Replace('+', ' '); + form[pv[0].UrlDecode()] = pv[1].UrlDecode().Replace('+', ' '); } } } @@ -328,34 +367,39 @@ public class WebServer client.Close(); } - private void tClientThread_ParameterizedThreadStart(object parm) + private void tClientThread_ParameterizedThreadStart(object? parm) { - System.Net.Sockets.TcpClient client = (System.Net.Sockets.TcpClient)parm; - Console.WriteLine("Client connected"); + if (parm is System.Net.Sockets.TcpClient client) + { + Console.WriteLine("Client connected"); - try - { - HandleClient(client); + try + { + HandleClient(client); + } + catch (System.Net.Sockets.SocketException ex) + { + Console.Error.WriteLine("caught SocketException; ignoring"); + Console.Error.WriteLine(ex.Message); + } + catch (System.IO.IOException ex) + { + Console.Error.WriteLine("caught IOException; ignoring"); + Console.Error.WriteLine(ex.Message); + } + catch (Exception ex) + { + Console.Error.WriteLine("caught exception of type {0}", ex.GetType().FullName); + Console.Error.WriteLine(ex.Message); + Console.Error.WriteLine(ex.StackTrace); + } + + client.Close(); } - catch (System.Net.Sockets.SocketException ex) - { - Console.Error.WriteLine("caught SocketException; ignoring"); - Console.Error.WriteLine(ex.Message); - } - catch (System.IO.IOException ex) - { - Console.Error.WriteLine("caught IOException; ignoring"); - Console.Error.WriteLine(ex.Message); - } - catch (Exception ex) - { - Console.Error.WriteLine("caught exception of type {0}", ex.GetType().FullName); - Console.Error.WriteLine(ex.Message); - } - - client.Close(); } + public X509Certificate? ServerCertificate { get; private set; } = null; + private bool _Stopping = false; private Thread? tServer = null; public void Start() @@ -365,6 +409,31 @@ public class WebServer _Stopping = false; + + if (SSLCertificateFile != null && SSLCertificateKeyFile != null) + { + Console.WriteLine("MBS.Web.WebServer: using SSL :)"); + + if (!System.IO.File.Exists(SSLCertificateFile)) + { + Console.Error.WriteLine("SSLCertificateFile not found: {0}", SSLCertificateFile); + } + else if (!System.IO.File.Exists(SSLCertificateKeyFile)) + { + Console.Error.WriteLine("SSLCertificateKeyFile not found: {0}", SSLCertificateKeyFile); + } + else + { + // assuming an 509 certificate has been loaded before in an init method of some sort + // X509Certificate serverCertificate = X509Certificate2.CreateFromCertFile(SSLCertificateFile); + ServerCertificate = X509Certificate2.CreateFromPemFile(SSLCertificateFile, SSLCertificateKeyFile); + } + } + else + { + Console.WriteLine("MBS.Web.WebServer: !! NOT using SSL !!"); + } + tServer = new Thread(tServer_ThreadStart); tServer.Start(); }