diff --git a/Sunfish/Sunfish/Extensions.cs b/Sunfish/Sunfish/Extensions.cs index bacac4e..ed3592b 100644 --- a/Sunfish/Sunfish/Extensions.cs +++ b/Sunfish/Sunfish/Extensions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Windows.Forms; @@ -21,5 +22,13 @@ s.Write(buf, 0, readed); } } + + public static T GetValue(this Dictionary dict,K key, T def) + { + T value; + if (dict.TryGetValue(key, out value)) + return value; + return def; + } } } diff --git a/Sunfish/Sunfish/Middleware/HttpServer.cs b/Sunfish/Sunfish/Middleware/HttpServer.cs index dd3b283..4f2040a 100644 --- a/Sunfish/Sunfish/Middleware/HttpServer.cs +++ b/Sunfish/Sunfish/Middleware/HttpServer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.IO; using System.Net; using System.Security.Principal; @@ -96,6 +97,7 @@ } finally { + call.Response.StatusCode = 500; call.Close(); } } @@ -185,9 +187,12 @@ if (encoding == null) encoding = new UTF8Encoding(false); if (string.IsNullOrEmpty(contentType) || string.IsNullOrWhiteSpace(contentType)) + contentType = Response.ContentType; + if (string.IsNullOrEmpty(contentType) || string.IsNullOrWhiteSpace(contentType)) contentType = "text/html"; bs = new BufferedStream(Response.OutputStream); swout = new StreamWriter(bs, encoding); + Response.ContentType = contentType; Response.Headers[HttpResponseHeader.ContentType] = contentType; Response.Headers[HttpResponseHeader.ContentEncoding] = encoding.WebName; } @@ -412,6 +417,22 @@ GetOut().BaseStream.Write(data, offset, count); } + public void Write(Stream s) + { + GetOut().Flush(); + GetOut().BaseStream.TransferFrom(s); + } + + public void Write(Icon image) + { + using (MemoryStream ms = new MemoryStream()) + { + image.Save(ms); + ms.Position = 0; + Write(ms); + } + } + public void Close() { if (swout == null) @@ -432,6 +453,8 @@ public HttpListenerResponse Response { get; } public IPrincipal User { get; } public StreamWriter Out => GetOut(); + + public Dictionary Parameters => parameters; } public class LogError diff --git a/Sunfish/Sunfish/Middleware/Templater.cs b/Sunfish/Sunfish/Middleware/Templater.cs new file mode 100644 index 0000000..ca50c7e --- /dev/null +++ b/Sunfish/Sunfish/Middleware/Templater.cs @@ -0,0 +1,105 @@ +using Microsoft.SqlServer.Server; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +namespace DolphinWebXplorer2.Middleware +{ + public class Templater + { + private List parts = new List(); + + public Templater(string template) + { + Tokenize(template); + } + + private void Tokenize(string templ) //Can be optimized with indexOf?? + { + parts.Clear(); + StringBuilder sb = new StringBuilder(); + bool key = false; + for (int i = 0; i < templ.Length; i++) + { + char c = templ[i]; + if (!key && c == '{') + { + key = true; + parts.Add(sb.ToString()); + sb.Clear(); + } + else if (key && c == '}') + { + key = false; + parts.Add(sb.ToString()); + sb.Clear(); + } + else + sb.Append(c); + } + if (sb.Length > 0) + parts.Add(sb.ToString()); + } + + private string GetValue(string key, object[] data) + { + foreach (object o in data) + { + if (o == null) + continue; + if (o is Dictionary) + { + Dictionary d = (Dictionary)o; + object v; + if (d.TryGetValue(key, out v)) + if (v == null) + return null; + else + return v.ToString(); + } + else + { + Type t = o.GetType(); + try + { + PropertyInfo p = t.GetProperty(key); + if (p != null) + { + object v = p.GetValue(o); + return v?.ToString(); + } + } + catch { }; + } + } + return null; + } + + public string Process(params object[] data) + { + StringBuilder sb = new StringBuilder(); + bool key = false; + foreach (string s in parts) + { + if (key) + { + key = false; + string v = GetValue(s, data); + if (!string.IsNullOrEmpty(v)) + sb.Append(v); + } + else + { + key = true; + sb.Append(s); + } + } + return sb.ToString(); + } + + } +} diff --git a/Sunfish/Sunfish/Middleware/WebUI.cs b/Sunfish/Sunfish/Middleware/WebUI.cs new file mode 100644 index 0000000..cabe9d5 --- /dev/null +++ b/Sunfish/Sunfish/Middleware/WebUI.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DolphinWebXplorer2.Middleware +{ + class WebUI + { + static WebUI() + { + // InitResources(); + } + + #region Frontend Resources + + private static string rpath = @"C:\Users\XWolf\Source\Repos\Sunfish\Sunfish\ShareWeb\$sunfish"; + private const string SECBEGIN = ""; + private static Dictionary BaseData = new Dictionary() + { + ["AppName"] = "Sunfish", + ["AppVersion"] = Program.VERSION, + }; + + public static void InitResources() + { + Templs.Clear(); + string template = File.ReadAllText(Path.Combine(rpath, "$index.html")); + string tname = ""; + string tval; + int i = template.IndexOf(SECBEGIN); + while (i >= 0) + { + tval = template.Substring(0, i); + if (!string.IsNullOrEmpty(tval)) + Templs[tname] = new Templater(tval); + template = template.Substring(i + SECBEGIN.Length); + i = template.IndexOf(SECTAGEND); + tname = template.Substring(0, i); + template = template.Substring(i + SECTAGEND.Length); + i = template.IndexOf(SECBEGIN); + } + if (!string.IsNullOrEmpty(template)) + Templs[tname] = new Templater(template); + } + + private static Dictionary Templs = new Dictionary(); + + #endregion + + public static void WriteHeader(HttpCall call) + { + call.Write(Templs["head-a"].Process(BaseData)); + //call.Write(Templs["head-location-item"].Process(BaseData)); + call.Write(Templs["head-b"].Process(BaseData)); + //call.Write(Templs["head-toolbar-item"].Process(BaseData)); + call.Write(Templs["head-c"].Process(BaseData)); + } + + public static void WriteFooter(HttpCall call) + { + call.Write(Templs["footer"].Process(BaseData)); + } + + public static void WriteItem(WebUIListItem item, HttpCall call) + { + call.Write(Templs["item"].Process(item, BaseData)); + } + + public static void WriteResource(string path, HttpCall call) + { + call.Write(File.ReadAllBytes(Path.Combine(rpath, path))); + } + + + } + + public struct WebUIListItem + { + public string Name { get; set; } + public string Description { get; set; } + public string Link { get; set; } + + public string Styles { get; set; } + } +} diff --git a/Sunfish/Sunfish/Properties/Resources.Designer.cs b/Sunfish/Sunfish/Properties/Resources.Designer.cs index 37b7480..5cc81cb 100644 --- a/Sunfish/Sunfish/Properties/Resources.Designer.cs +++ b/Sunfish/Sunfish/Properties/Resources.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// Este código fue generado por una herramienta. -// Versión de runtime:4.0.30319.42000 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// Los cambios en este archivo podrían causar un comportamiento incorrecto y se perderán si -// se vuelve a generar el código. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -13,12 +13,12 @@ /// - /// Clase de recurso fuertemente tipado, para buscar cadenas traducidas, etc. + /// A strongly-typed resource class, for looking up localized strings, etc. /// - // StronglyTypedResourceBuilder generó automáticamente esta clase - // a través de una herramienta como ResGen o Visual Studio. - // Para agregar o quitar un miembro, edite el archivo .ResX y, a continuación, vuelva a ejecutar ResGen - // con la opción /str o recompile su proyecto de VS. + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] @@ -33,7 +33,7 @@ } /// - /// Devuelve la instancia de ResourceManager almacenada en caché utilizada por esta clase. + /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { @@ -47,8 +47,8 @@ } /// - /// Reemplaza la propiedad CurrentUICulture del subproceso actual para todas las - /// búsquedas de recursos mediante esta clase de recurso fuertemente tipado. + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { @@ -61,7 +61,7 @@ } /// - /// Busca un recurso adaptado de tipo System.Drawing.Bitmap. + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap foldericon { get { @@ -71,7 +71,7 @@ } /// - /// Busca un recurso adaptado de tipo System.Drawing.Bitmap. + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap foldericond { get { @@ -81,7 +81,7 @@ } /// - /// Busca un recurso adaptado de tipo System.Drawing.Bitmap. + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap messagebox_info { get { @@ -91,7 +91,7 @@ } /// - /// Busca un recurso adaptado de tipo System.Drawing.Bitmap. + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap osubf { get { @@ -101,7 +101,7 @@ } /// - /// Busca un recurso adaptado de tipo System.Drawing.Bitmap. + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap ouplo { get { @@ -111,7 +111,7 @@ } /// - /// Busca un recurso adaptado de tipo System.Drawing.Bitmap. + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap rename { get { @@ -121,7 +121,7 @@ } /// - /// Busca un recurso adaptado de tipo System.Drawing.Bitmap. + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap run { get { @@ -131,7 +131,7 @@ } /// - /// Busca un recurso adaptado de tipo System.Drawing.Bitmap. + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap screen { get { @@ -141,117 +141,7 @@ } /// - /// Busca una cadena traducida similar a var shs = new (function () { - /// var xmlhttp = new XMLHttpRequest(); - /// var byId = function (name) { - /// return document.getElementById(name); - /// } - /// var sfifo = []; - /// var sending = false; - /// var send = function (code, cmd, important) { - /// if (sending) { - /// if (important) - /// sfifo.push(cmd); - /// return; - /// } - /// sending = true; - /// if (cmd == null) { - /// cmd = sfifo.shift(); - /// } - /// xmlhttp.open("GET", "$screencmd? [resto de la cadena truncado]";. - /// - internal static string ShScreen { - get { - return ResourceManager.GetString("ShScreen", resourceCulture); - } - } - - /// - /// Busca una cadena traducida similar a html{ - /// background:#BBBBBB; - ///} - /// - ///body{ - /// margin:0 auto 0 auto; - /// max-width: 900px; - /// background:white; - /// box-shadow: 0 0 5px; - /// padding:2px; - /// font-family:verdana; - /// font-size:12px; - /// padding:0; - ///} - /// - ///#head{ - /// height:68px; - /// background:#F5F6F7 url(/·logo) 20px 20px no-repeat; - /// padding:10px 10px 10px 80px; - /// font-family: verdana; - /// font-size:25px; - /// border-bottom: solid 1px #DADBDC; - /// box-shadow: 0 3px 2px -2px gray; - /// position:relative; - ///} - ///#head a{ - /// color:black; - /// text-decoration:none; - ///} - ///#head a:hover [resto de la cadena truncado]";. - /// - internal static string site { - get { - return ResourceManager.GetString("site", resourceCulture); - } - } - - /// - /// Busca una cadena traducida similar a html{ - /// background: #222; - /// height: 100%; - ///} - /// - ///body{ - /// display: table; - /// margin: 0 auto; - /// height: 100%; - ///} - /// - ///div#ctt { - /// display: table-cell; - /// vertical-align: middle; - /// height: 100%; - ///} - /// - ///div#main { - /// background: #FFF; - /// box-shadow: 0 2px 7px #999; - /// border: 0px; - /// border-radius:4px; - /// font-family:verdana; - /// font-size:12px; - /// padding:0; - /// overflow:hidden; - ///} - /// - ///div#frm { - /// padding:10px; - ///} - /// - ///img#scr { - ////* - /// max-width:100%; - /// max-height:100%; - ///*/ - ///}. - /// - internal static string siteblack { - get { - return ResourceManager.GetString("siteblack", resourceCulture); - } - } - - /// - /// Busca un recurso adaptado de tipo System.Drawing.Icon similar a (Icono). + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). /// internal static System.Drawing.Icon sunfishWebServer { get { @@ -261,7 +151,7 @@ } /// - /// Busca un recurso adaptado de tipo System.Drawing.Bitmap. + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap sws { get { @@ -271,7 +161,7 @@ } /// - /// Busca un recurso adaptado de tipo System.Drawing.Bitmap. + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap t_delete { get { @@ -281,7 +171,7 @@ } /// - /// Busca un recurso adaptado de tipo System.Drawing.Bitmap. + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap t_execute { get { @@ -291,7 +181,7 @@ } /// - /// Busca un recurso adaptado de tipo System.Drawing.Bitmap. + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap upload { get { diff --git a/Sunfish/Sunfish/Properties/Resources.resx b/Sunfish/Sunfish/Properties/Resources.resx index 94b3f21..c110d58 100644 --- a/Sunfish/Sunfish/Properties/Resources.resx +++ b/Sunfish/Sunfish/Properties/Resources.resx @@ -112,15 +112,12 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ..\resources\site.css;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;iso-8859-1 - + ..\Resources\sunfishWebServer.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -157,12 +154,6 @@ ..\Resources\share.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - ..\Resources\site1.css;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 - - - ..\Resources\ShScreen.js;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 - ..\Resources\upload.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/Sunfish/Sunfish/Resources/ShScreen.js b/Sunfish/Sunfish/Resources/ShScreen.js deleted file mode 100644 index 4c0da1a..0000000 --- a/Sunfish/Sunfish/Resources/ShScreen.js +++ /dev/null @@ -1,87 +0,0 @@ -var shs = new (function () { - var xmlhttp = new XMLHttpRequest(); - var byId = function (name) { - return document.getElementById(name); - } - var sfifo = []; - var sending = false; - var send = function (code, cmd, important) { - if (sending) { - if (important) - sfifo.push(cmd); - return; - } - sending = true; - if (cmd == null) { - cmd = sfifo.shift(); - } - xmlhttp.open("GET", "$screencmd?code=" + code + "&cmd=" + cmd, true); - var unlock = function () { - sending = false; - if (sfifo.length > 0) - send(code, null); - } - xmlhttp.onreadystatechange = function (evt) { - if (xmlhttp.readyState == 4) - unlock(); - } - xmlhttp.onerror = function () { - unlock(); - } - xmlhttp.send(); - } - this.start = function () { - var frm = byId("frm"); - var code = byId("scpwd").value; - var img = byId("scr"); - frm.style.display = "none"; - img.onload = function () { - img.onload = function () { - img.src = "/$screencap?code=" + code + "&time=" + new Date().getTime(); - } - img.onerror = function () { - location.href = "/"; - } - img.onload(); - } - img.onerror = function () { - img.style.display = "none"; - frm.style.display = ""; - } - img.onmousemove = function (evt) { - send(code, "MV" + evt.offsetX + ";" + evt.offsetY, false); - } - img.onmousedown = function (evt) { - var buttons = ["LD", "MD", "RD"]; - send(code, buttons[evt.button] + evt.offsetX + ";" + evt.offsetY, true); - return false; - } - img.onmouseup = function (evt) { - var buttons = ["LU", "MU", "RU"]; - send(code, buttons[evt.button] + evt.offsetX + ";" + evt.offsetY, true); - return false; - } - img.onmousewheel = function (evt) { - send(code, "WH" + evt.offsetX + ";" + evt.offsetY + ";" + evt.wheelDelta, false); - return false; - } - img.onclick = function (evt) { - evt.preventDefault(); - } - img.oncontextmenu = function () { - return false; - } - document.onkeydown = function (evt) { - send(code, "KD" + evt.which, true); - return false; - } - document.onkeyup = function (evt) { - send(code, "KU" + evt.which, true); - return false; - } - document.onkeypress = function (evt) { - return false; - } - img.src = "/$screencap?code=" + code; - } -})(); \ No newline at end of file diff --git a/Sunfish/Sunfish/Resources/site.css b/Sunfish/Sunfish/Resources/site.css deleted file mode 100644 index ac1732a..0000000 --- a/Sunfish/Sunfish/Resources/site.css +++ /dev/null @@ -1,173 +0,0 @@ -html{ - background:#BBBBBB; -} - -body{ - margin:0 auto 0 auto; - max-width: 900px; - background:white; - box-shadow: 0 0 5px; - padding:2px; - font-family:verdana; - font-size:12px; - padding:0; -} - -#head{ - height:68px; - background:#F5F6F7 url(/�logo) 20px 20px no-repeat; - padding:10px 10px 10px 80px; - font-family: verdana; - font-size:25px; - border-bottom: solid 1px #DADBDC; - box-shadow: 0 3px 2px -2px gray; - position:relative; -} -#head a{ - color:black; - text-decoration:none; -} -#head a:hover{ - color:#0077FF; -} -#head a:active{ - color:black; -} -#head a:visited{ - color:black; -} -#breadcrum{ - font-size:12px; - position:absolute; - bottom:0px; - padding:0 0 5px 5px; -} -#breadcrum a,#breadcrum a:visited{ - color:#333344; -} -#breadcrum a:hover{ - color:#EEEEEE; - text-shadow: 0 0 2px gray; -} - -#breadcrum a:active{ - color:#EEEEFF; -} - -#headtoolbox { - position:absolute; - top:55px; - right:20px; - width:150px; - height:24px; - text-align:right; -} - -#main{ - padding:12px; -} - -.item{ - cursor:pointer; cursor: hand; - padding:4px; - margin:5px 5px 9px 5px; - width:420px; - position: relative; - display:inline-block; - border-bottom:1px solid #EEEEEE; -} -.item:hover{ - border:1px solid #66A7E8; - background-color:#EEF4FF; - margin:4px 4px 9px 4px; - border-radius:2px; -} -.item a{ - color:black; - text-decoration:none; -} -.item:hover a{ - color:#0077FF; -} -.item a:active{ - color:white; -} -.item:hover .itool{ - //display:inherit; - opacity:1; -} -.iname{ - position:absolute; - top:2px; - left:43px; - width:380px; - text-overflow: ellipsis; - overflow:hidden; - white-space: nowrap; -} -.iinfo{ - position:absolute; - top:20px; - left:45px; - color: #555; -} -.itool{ - position:absolute; - bottom: 2px; - right: 2px; - //display:none; - opacity:0.1; -} -.itool img{ - margin-left:10px; -} -.itool img:hover{ - -webkit-filter: contrast(3); - filter: contrast(3); -} -.activity{ - background-color:#F5F6F7; - padding:10px; - border-radius:4px; - box-shadow:1px 2px 4px gray; - margin: 0 auto 0 auto; - width:70%; -} -.activity input{ - width:100%; -} -.activity input[type='submit']{ - width:initial; - float:right; - margin-top: 5px; - margin-left: 5px; -} -.activity input[type='button']{ - width:initial; - float:right; - margin-top: 5px; - margin-left: 5px; -} -.activity::after{ - content:'.'; - display:block; - height: 0; - clear: both; - visibility: hidden; -} -.activity form{ - margin:0; - padding:0; -} -.error{ - border:1px solid #E8A766; - background-color:#FFF4EE; - padding:5px; - border-radius:3px; -} -.htool { - width:24px; - height:24px; - padding:0; - margin-left:7px; -} \ No newline at end of file diff --git a/Sunfish/Sunfish/Resources/site1.css b/Sunfish/Sunfish/Resources/site1.css deleted file mode 100644 index 5ea94b7..0000000 --- a/Sunfish/Sunfish/Resources/site1.css +++ /dev/null @@ -1,38 +0,0 @@ -html{ - background: #222; - height: 100%; -} - -body{ - display: table; - margin: 0 auto; - height: 100%; -} - -div#ctt { - display: table-cell; - vertical-align: middle; - height: 100%; -} - -div#main { - background: #FFF; - box-shadow: 0 2px 7px #999; - border: 0px; - border-radius:4px; - font-family:verdana; - font-size:12px; - padding:0; - overflow:hidden; -} - -div#frm { - padding:10px; -} - -img#scr { -/* - max-width:100%; - max-height:100%; -*/ -} \ No newline at end of file diff --git a/Sunfish/Sunfish/Services/RootService.cs b/Sunfish/Sunfish/Services/RootService.cs new file mode 100644 index 0000000..13ca342 --- /dev/null +++ b/Sunfish/Sunfish/Services/RootService.cs @@ -0,0 +1,83 @@ +using DolphinWebXplorer2.Middleware; +using System; +using System.IO; +using System.Reflection; + +namespace DolphinWebXplorer2.Services +{ + [DefineConfigurator(typeof(RootServiceConfigurator))] + class RootService : SunfishService + { + public const string DIR_COMMON = "/$sunfish/"; + public const string DIR_API = "api/"; + public RootService() : base(new SunfishServiceConfiguration() + { + Enabled = true, + Name = "ROOT", + Location = "/" + }) + { + + } + + public override void Process(string path, HttpCall call) + { + WebUI.InitResources(); + if (path == "/") + { + // Root page + if (ShowMenu) + { + WebUI.WriteHeader(call); + foreach (SunfishService s in Sunfish.Services) + { + if (!s.Enabled) + continue; + WebUI.WriteItem(new WebUIListItem() + { + Name = s.Configuration.Name, + Description = s.Configuration.Location, + Link = s.Configuration.Location, + }, call); + } + WebUI.WriteFooter(call); + } + else + call.Response.StatusCode = 404; + } + else if (path.StartsWith(DIR_COMMON)) + { + path = path.Substring(DIR_COMMON.Length); + if (path.StartsWith(DIR_API)) + { + // API + path = path.Substring(DIR_API.Length); + } + else if (path == "Sunfish.exe") + { + // Self copy + call.Response.ContentType = "application/x-msdownload"; + call.Write(File.ReadAllBytes(Assembly.GetExecutingAssembly().Location)); + } + else + { + // Internal Resources + WebUI.WriteResource(path, call); //TODO: Change to internal resources ZIP + } + } + else + call.NotFound(); + } + + protected override void Start() + { + } + + protected override void Stop() + { + } + + public override string Description => "Root page service and API"; + public bool ShowMenu { get; set; } = true; + } +} diff --git a/Sunfish/Sunfish/Services/RootServiceConfigurator.cs b/Sunfish/Sunfish/Services/RootServiceConfigurator.cs new file mode 100644 index 0000000..bf2d172 --- /dev/null +++ b/Sunfish/Sunfish/Services/RootServiceConfigurator.cs @@ -0,0 +1,23 @@ +using DolphinWebXplorer2.Configurator; + +namespace DolphinWebXplorer2.Services +{ + class RootServiceConfigurator : SunfishServiceConfigurator + { + internal const string CFG_MENU = "rootMenu"; + + internal protected override ConfigurationScreen GetConfigurationScreen() + { + return new ConfigurationScreen() + { + Elements = new ConfigurationElement[] + { + new ConfigurationBool(CFG_MENU,"Show service menu") + { + Tooltip="Show a list of available services on the root page", + } + } + }; + } + } +} diff --git a/Sunfish/Sunfish/Services/WebService.cs b/Sunfish/Sunfish/Services/WebService.cs index 079cc50..92608fa 100644 --- a/Sunfish/Sunfish/Services/WebService.cs +++ b/Sunfish/Sunfish/Services/WebService.cs @@ -2,6 +2,7 @@ using Json.Net; using System.Collections.Generic; using System.IO; +using System.Runtime.CompilerServices; namespace DolphinWebXplorer2.Services { @@ -24,13 +25,9 @@ #region ChildClasses class WebServiceData { - public List Files { get; set; } - public string AppTitle => "Sunfish "+Program.VERSION; - } - - class WebServiceFile - { - + public string[] Files { get; set; } + public string ServicePath { get; set; } + public string AppTitle => "Sunfish " + Program.VERSION; } #endregion @@ -71,18 +68,39 @@ } } + private object GetWebServiceData(string path) + { + WebServiceData wsd = new WebServiceData(); + if (path != null) + { + List fileList = new List(); + VFSItem itm = vfs.GetItem(path); + if (itm != null) + { + foreach (string d in vfs.ListDirectories(path)) + fileList.Add(d + "/"); + fileList.Sort(); + List lfls = new List(vfs.ListFiles(path)); + lfls.Sort(); + fileList.AddRange(lfls); + wsd.Files = fileList.ToArray(); + } + } + wsd.ServicePath = Configuration.Location; + if (!wsd.ServicePath.EndsWith("/")) + wsd.ServicePath = wsd.ServicePath + "/"; + return wsd; + } + public override void Process(string path, HttpCall call) { - if (path.StartsWith("/|") && allowNavigation) + if (allowNavigation && path.Contains("/|")) { - path = path.Substring(2); + path = path.Substring(path.IndexOf("/|") + 2); switch (path) { case "data": - call.Write(JsonNet.Serialize(new WebServiceData() - { - Files = new List() - })); + call.Write(JsonNet.Serialize(GetWebServiceData(call.Parameters.GetValue("path", null)))); break; } } @@ -92,12 +110,16 @@ //Directory entry, go for index file or navigation if (index != null) { - if (idx != null) + if (idx != null && !idx.Folder) + { DownloadAt(idx, call); + return; + } } if (allowNavigation) { - if (idx != null && idx.Folder) + idx = vfs.GetItem(path); + if (idx != null && idx.Folder)// WHY? DownloadAt("/$sunfish/index.html", call); else call.NotFound(); @@ -111,7 +133,7 @@ if (idx != null) { if (idx.Folder) - call.Redirect(path + "/"); + call.Redirect(call.Request.Url.LocalPath + "/"); else DownloadAt(idx, call); } diff --git a/Sunfish/Sunfish/Sunfish.cs b/Sunfish/Sunfish/Sunfish.cs index 406acf7..67988c2 100644 --- a/Sunfish/Sunfish/Sunfish.cs +++ b/Sunfish/Sunfish/Sunfish.cs @@ -21,6 +21,7 @@ public const string DEFAULT_CONF_FILE = "sunfish2"; private static SunfishConfiguration conf = new SunfishConfiguration(); private static List srvs = new List(); + private static RootService sroot = new RootService(); private static HttpServer server; #region Configuration & Initialization @@ -33,10 +34,12 @@ { if (!File.Exists(DEFAULT_CONF_FILE)) return; + string json = File.ReadAllText(DEFAULT_CONF_FILE); conf = JsonNet.Deserialize(json); if (conf.Services == null) conf.Services = new List(); + sroot.ShowMenu = conf.SunfishRoot; foreach (SunfishServiceConfiguration ssc in conf.Services) { srvs.Add(SunfishService.Instance(ssc)); @@ -89,10 +92,15 @@ { var path = call.Request.Url.LocalPath; SunfishService s = GetServiceForPath(ref path); - if (s == null || !s.Enabled) - ErrorService.Process404(call); + if (string.IsNullOrWhiteSpace(path)) + call.Redirect(call.Request.Url.LocalPath + "/"); else - s.Process(path,call); + { + if (s == null || !s.Enabled) + ErrorService.Process404(call); + else + s.Process(path, call); + } call.Close(); } @@ -140,18 +148,24 @@ public static SunfishService GetServiceForPath(ref string path) { - string candidateRelativePath=""; + if (string.IsNullOrEmpty(path) || path == "/" || path.StartsWith("/$sunfish")) + return sroot; + string candidateRelativePath = ""; SunfishService candidate = null; foreach (SunfishService s in srvs) - if (path.StartsWith(s.Configuration.Location) && + { + string plb = s.Configuration.Location; + if (!plb.EndsWith("/")) + plb = plb + "/"; + if ((path == s.Configuration.Location || path.StartsWith(plb)) && (candidate == null || (candidate.Configuration.Location.Length < s.Configuration.Location.Length))) { candidate = s; candidateRelativePath = path.Substring(s.Configuration.Location.Length); } - path = candidateRelativePath; - if (string.IsNullOrWhiteSpace(path)) - path = "/"; + } + if (candidate != null) + path = candidateRelativePath; return candidate; } diff --git a/Sunfish/Sunfish/Sunfish.csproj b/Sunfish/Sunfish/Sunfish.csproj index 473dd61..58a309a 100644 --- a/Sunfish/Sunfish/Sunfish.csproj +++ b/Sunfish/Sunfish/Sunfish.csproj @@ -106,10 +106,14 @@ + + + + @@ -140,6 +144,8 @@ True + + @@ -173,11 +179,8 @@ - - - diff --git a/Sunfish/Sunfish/SunfishService.cs b/Sunfish/Sunfish/SunfishService.cs index c628303..c257ce5 100644 --- a/Sunfish/Sunfish/SunfishService.cs +++ b/Sunfish/Sunfish/SunfishService.cs @@ -30,7 +30,7 @@ Type tme = typeof(SunfishService); foreach (Type t in a.GetTypes()) { - if (tme.IsAssignableFrom(t) && !t.IsAbstract && t != typeof(ErrorService)) + if (tme.IsAssignableFrom(t) && !t.IsAbstract && t != typeof(ErrorService) && t != typeof(RootService)) st.Add(t); } }