WebMessage LoadSessionResult(XmlRequest request) { WebMessage message = request.Result.GetRow <WebMessage>("Result"); if (message.Error != WebError.None) { throw new XAuthException(message, request); } Trace.TraceInformation("{0}", message); Software = request.Result.GetRow <Software>("Software"); Session = request.Result.GetRow <SoftwareSession>("SoftwareSession"); return(message); }
/// <summary>Performs a leave group command.</summary> /// <param name="groupID">The group identifier.</param> /// <returns></returns> public WebMessage GroupLeave(long groupID) { lock (this) { var request = XmlRequest.Prepare(Server, "GroupLeave", $"groupID={groupID}"); request.Headers["Session"] = Session.ID.ToString(); WebMessage message = request.Post(); if (message.Error != WebError.None) { throw new XAuthException(message, request); } return(message); } }
/// <summary>Performs a create group command.</summary> /// <param name="groupName">Name of the group.</param> /// <returns></returns> public XmlDeserializer GroupCreate(string groupName) { lock (this) { var request = XmlRequest.Prepare(Server, "GroupCreate", $"groupName={groupName}"); request.Headers["Session"] = Session.ID.ToString(); WebMessage message = request.Post(); if (message.Error != WebError.None) { throw new XAuthException(message, request); } return(request.Result); } }
/// <summary>Performs a session check. A successful check extends the session.</summary> public void CheckSession() { Trace.TraceInformation("Checking session {0}", Session); var request = XmlRequest.Prepare(Server, "CheckSession"); request.Headers["Session"] = Session.ID.ToString(); WebMessage message = request.Post(); if (message.Error != WebError.None) { throw new XAuthException(message, request); } message = LoadSessionResult(request); OnSessionUpdated(new EventArgs()); }
/// <summary>Gets a transaction key.</summary> /// <param name="url">The URL.</param> /// <returns></returns> public TransactionKey GetTransactionKey(string url) { lock (this) { string hash = Base64.UrlChars.Encode(Hash.FromString(Hash.Type.SHA256, url)); var request = XmlRequest.Prepare(Server, "GetTransactionKey", $"urlHash={hash}"); request.Headers["Session"] = Session.ID.ToString(); WebMessage message = request.Post(); if (message.Error != WebError.None) { throw new XAuthException(message, request); } return(request.Result.GetRow <TransactionKey>("TransactionKey")); } }
/// <summary>Creates a new account.</summary> /// <param name="user">The user.</param> /// <param name="email">The email.</param> /// <param name="firstname">The firstname.</param> /// <param name="lastname">The lastname.</param> /// <param name="birthday">The birthday.</param> /// <param name="gender">The gender.</param> /// <returns></returns> public WebMessage CreateAccount(string user, string email, string firstname, string lastname, DateTime birthday, Gender gender) { lock (this) { var request = XmlRequest.Prepare(Server, "CreateAccount", $"user={user}", $"email={email}", $"firstname={firstname}", $"lastname={lastname}", $"gender={gender}", $"birthday={birthday.Date}"); WebMessage message = request.Post(); if (message.Error != WebError.None) { throw new XAuthException(message, request); } return(message); } }
/// <summary>Closes the current session.</summary> /// <returns></returns> public WebMessage CloseSession() { lock (this) { if (Session.IsValid()) { var request = XmlRequest.Prepare(Server, "CloseSession"); WebMessage message = request.Post(); if (message.Error != WebError.None) { throw new XAuthException(message, request); } return(message); } return(WebMessage.Create("CloseSession", "Session was already expired!", error: WebError.None)); } }
/// <summary>Creates a new session.</summary> /// <param name="user">The user.</param> /// <param name="pass">The pass.</param> /// <returns></returns> public WebMessage CreateSession(string user, string pass) { lock (this) { var request = XmlRequest.Prepare(Server, "CreateSession"); request.Credentials = new NetworkCredential(user, pass); WebMessage message = request.Post(); if (message.Error != WebError.None) { throw new XAuthException(message, request); } message = LoadSessionResult(request); timer?.Dispose(); timer = new System.Threading.Timer(CheckSession, null, 1000 * 10, 1000 * 10); return(message); } }
/// <summary>Sets a xml content.</summary> /// <param name="request">The request.</param> /// <param name="message">The message.</param> /// <param name="xdoc">The xdoc.</param> /// <param name="culture">The culture.</param> /// <returns>Returns a new RestContent instance.</returns> public static WebAnswer Xml(WebRequest request, WebMessage message, XDocument xdoc, CultureInfo culture = null) { byte[] data; using (var ms = new MemoryStream()) using (var sw = new StreamWriter(ms, Encoding.GetEncoding(xdoc.Declaration.Encoding))) { xdoc.Save(sw); data = ms.ToArray(); } return(new WebAnswer() { AllowCompression = request?.AllowCompression == true, Message = message, StatusCode = message.Code, ContentData = data, ContentType = "text/xml; charset=" + xdoc.Declaration.Encoding, ContentLanguage = (culture ?? Default).TwoLetterISOLanguageName, }); }
void GetStaticFileListing(WebData data) { string url = Uri.UnescapeDataString(data.Request.DecodedUrl).Trim('/'); string path = FileSystem.Combine(StaticFilesPath, url); var entries = new List <WebDirectoryEntry>(); string root = FileSystem.Combine(path, ".."); if (FileSystem.IsRelative(root, StaticFilesPath)) { FileSystemInfo info = new DirectoryInfo(root); var entry = new WebDirectoryEntry() { DateTime = info.LastWriteTime, Name = "..", Type = WebDirectoryEntryType.Directory, Link = "/" + url + "/..", }; entries.Add(entry); } if (FileSystem.IsRelative(path, StaticFilesPath)) { foreach (string dir in Directory.GetDirectories(path)) { FileSystemInfo info = new DirectoryInfo(dir); var entry = new WebDirectoryEntry() { DateTime = info.LastWriteTime, Name = info.Name, Type = WebDirectoryEntryType.Directory, Link = "/" + FileSystem.Combine('/', url, info.Name), }; entries.Add(entry); } foreach (string file in Directory.GetFiles(path)) { var info = new FileInfo(file); var entry = new WebDirectoryEntry() { DateTime = info.LastWriteTime, Size = info.Length, Name = info.Name, Type = WebDirectoryEntryType.File, Link = "/" + FileSystem.Combine('/', url, info.Name), }; entries.Add(entry); } } var pb = new HtmlPageBuilder(data.Request); pb.Content.CardOpenText($"File Listing:"); pb.Content.ParagraphText($"{entries.Count} entries"); pb.Content.TableOpen(new string[] { "Type", "Size", "Name" }, "table-striped table-responsive"); foreach (WebDirectoryEntry entry in entries) { pb.Content.TableRowOpen(); pb.Content.TableHtmlCell(Bootstrap4.GetBadge(entry.Type.ToString(), "badge-default")); pb.Content.TableCell(entry.Type == WebDirectoryEntryType.Directory ? string.Empty : entry.Size.FormatBinarySize()); pb.Content.TableHtmlCell(Bootstrap4.GetLink(entry.Name, entry.Link)); pb.Content.TableRowClose(); } pb.Content.TableClose(); pb.Content.CardClose(); pb.Content.AddHtml(" "); data.Answer = pb.ToAnswer(WebMessage.Create("FileListing", "File listing retrieved.")); }
/// <summary>Initializes a new instance of the <see cref="WebServerException"/> class.</summary> /// <param name="message">The message.</param> public WebServerException(WebMessage message) : base(message.Content) { Error = message.Error; Code = message.Code; }
/// <summary>Adds a result message.</summary> /// <param name="message">The message.</param> public void AddMessage(WebMessage message) { message.ID = ++messageCount; lastMessage = message; AddStruct(message); }
/// <summary>Handles a client stage1 (preparations).</summary> /// <remarks>Performs the firewall checks and enters stage2.</remarks> internal void HandleClient(WebServerClient client) { System.Globalization.CultureInfo threadCulture = Thread.CurrentThread.CurrentCulture; int threadId = Thread.CurrentThread.ManagedThreadId; WebResultBuilder result = null; try { // callback for connected client ClientConnected?.Invoke(this, new WebClientEventArgs(client)); // do request handling int requestNumber = 0; if (PerformanceChecks) { Trace.TraceInformation( $"HandleClient [{threadId}] <cyan>{client.RemoteEndPoint}<default> ready to receive request. " + $"Elapsed <cyan>{client.StopWatch.Elapsed.FormatTime()}<default>."); } while (client.IsConnected) { result = null; if (PerformanceChecks && requestNumber > 0) { Trace.TraceInformation( $"HandleClient [{threadId}] <cyan>{client.RemoteEndPoint}<default> request <green>{requestNumber}<default> handling completed. " + $"Elapsed <cyan>{client.StopWatch.Elapsed.FormatTime()}<default>."); } // read first request line string firstLine = client.Reader.ReadLine(); client.StopWatch.Reset(); if (PerformanceChecks) { Trace.TraceInformation( $"HandleClient [{threadId}] <cyan>{client.RemoteEndPoint}<default> start handling request <cyan>{++requestNumber}<default>. " + $"Elapsed <cyan>{client.StopWatch.Elapsed.FormatTime()}<default>."); } // load request var request = WebRequest.Load(this, firstLine, client); // prepare web data object var data = new WebData(request, client.StopWatch); result = data.Result; // update thread culture Thread.CurrentThread.CurrentCulture = data.Request.Culture; // handle request but change some default exceptions to web exceptions try { HandleRequest(client, data); } catch (ObjectDisposedException) { Trace.TraceInformation($"HandleClient [{threadId}] <red>{client.RemoteEndPoint}<default> Connection closed"); } catch (InvalidOperationException ex) { throw new WebServerException(ex, WebError.InvalidOperation, 0, ex.Message); } catch (ArgumentException ex) { throw new WebServerException(ex, WebError.InvalidParameters, 0, ex.Message); } } } catch (WebServerException ex) { Trace.TraceInformation(ex.ToString()); if (result == null) { result = new WebResultBuilder(this); } result.AddMessage(WebMessage.Create(ex)); if (ex.Error == WebError.AuthenticationRequired || ex.Error == WebError.InvalidTransactionKey) { result.Headers["WWW-Authenticate"] = $"Basic realm=\"{AssemblyVersionInfo.Program.Company} - {AssemblyVersionInfo.Program.Product}\""; } result.CloseAfterAnswer = true; client.SendAnswer(result.ToAnswer()); } catch (SocketException) { Trace.TraceInformation($"HandleClient [{threadId}] <red>{client.RemoteEndPoint}<default> Connection closed"); /*client closed connection*/ } catch (EndOfStreamException) { /*client closed connection*/ Trace.TraceInformation($"HandleClient [{threadId}] <red>{client.RemoteEndPoint}<default> Connection closed"); } catch (Exception ex) { if (ex.InnerException is SocketException) { Trace.TraceInformation($"HandleClient [{threadId}] <red>{client.RemoteEndPoint}<default> Connection closed"); return; } string supportCode = Base32.Safe.Encode(Environment.TickCount); Trace.TraceError("<red>Unhandled Internal Server Error<default> Code {1}\n{0}", ex.ToString(), supportCode); if (result == null) { result = new WebResultBuilder(this); } result.AddMessage(ex.Source, WebError.InternalServerError, $"Internal Server Error\nUnexpected result on request.\nPlease contact support!\nSupport Code = {supportCode}"); result.CloseAfterAnswer = true; client.SendAnswer(result.ToAnswer()); } finally { while (client.IsConnected && client.Reader.Available == 0) { Thread.Sleep(1); } client.Close(); if (client != null) { ClientDisconnected?.Invoke(this, new WebClientEventArgs(client)); } // reset thread culture if (Thread.CurrentThread.CurrentCulture != threadCulture) { Thread.CurrentThread.CurrentCulture = threadCulture; } } }
/// <summary>Adds a result message.</summary> /// <param name="method">The method.</param> /// <param name="message">The message.</param> /// <param name="args">The arguments.</param> public void AddMessage(WebServerMethod method, string message, params object[] args) { AddMessage(WebMessage.Create(method, string.Format(message, args))); }
/// <summary>Adds a result message.</summary> /// <param name="source">The source.</param> /// <param name="message">The message.</param> /// <param name="args">The arguments.</param> public void AddMessage(string source, string message, params object[] args) { AddMessage(WebMessage.Create(source, string.Format(message, args))); }
/// <summary>Adds a result message.</summary> /// <param name="source">The source.</param> /// <param name="error">The error.</param> /// <param name="code">The code.</param> /// <param name="message">The message.</param> /// <param name="args">The arguments.</param> public void AddMessage(string source, WebError error, HttpStatusCode code, string message, params object[] args) { AddMessage(WebMessage.Create(source, string.Format(message, args), error: error, code: code)); }
/// <summary>Adds a result message.</summary> /// <param name="method">The method.</param> /// <param name="error">The error.</param> /// <param name="code">The code.</param> /// <param name="message">The message.</param> /// <param name="args">The arguments.</param> public void AddMessage(WebServerMethod method, WebError error, HttpStatusCode code, string message, params object[] args) { AddMessage(WebMessage.Create(method, string.Format(message, args), error: error, code: code)); }
void ExplainFunction(WebData data, WebServerMethod function) { var html = new HtmlPageBuilder(data.Request); { string path = string.Empty; string[] parts = function.FullPaths.First().Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); int last = parts.Length - 1; for (int n = 0; n < parts.Length; n++) { path += "/" + parts[n]; html.Breadcrump.Add(new WebLink() { Text = parts[n], Link = (n != last) ? $"/Explain?functions={path}" : $"/Explain?function={path}" }); } } Bootstrap4 content = html.Content; WebServerAuthType authType = function.PageAttribute?.AuthType ?? WebServerAuthType.None; { string link = function.FullPaths.First(); var head = new Bootstrap4(); if (authType != WebServerAuthType.None) { // head.DivOpen(Bootstrap4.Item.float_right); head.DivOpen(Bootstrap4.Item.float_right); AddBadges(head, function.PageAttribute); head.DivClose(Bootstrap4.Item.float_right); // head.AddHtml("<br/>"); } head.AddHtml("<h2>"); head.AddHtml(function.Method.Name.SplitCamelCase().Join(" ")); if (function.Parameters.Length > 0) { head.AddHtml(" ("); head.AddHtml(function.ParameterString()); head.AddHtml(")"); } head.AddHtml("</h2>"); head.DivOpen(Bootstrap4.Item.float_right); head.Link("html", link + ".html", "btn btn-sm btn-outline-primary"); head.Link("json", link + ".json", "btn btn-sm btn-outline-primary"); head.Link("xml", link + ".xml", "btn btn-sm btn-outline-primary"); head.Link("plain", link + ".txt", "btn btn-sm btn-outline-primary"); head.DivClose(Bootstrap4.Item.float_right); head.AddHtml(function.Method.DeclaringType.AssemblyQualifiedName); content.CardOpen(head.ToString()); } XNetDocItem doc = documentation.GetMethod(function.Method); DocumentHtml(content, doc, function.IsAction ? "Generic action" : function.Method.ToString()); content.ListGroupOpen(); int i = 0; foreach (ParameterInfo parameter in function.Parameters) { if (parameter.ParameterType == typeof(WebData)) { continue; } ParameterHtml(content, i++, parameter, doc); } content.ListGroupClose(); content.CardClose(); content.AddHtml(" "); var message = WebMessage.Create("Explain " + function.Name, string.Format("Explain function {0}", function)); data.Answer = html.ToAnswer(message); }
void ExplainFunctionList(WebData data) { var html = new HtmlPageBuilder(data.Request); IEnumerable <KeyValuePair <string, WebServerMethod> > paths = data.Server.RegisteredPaths; if (data.Request.Parameters.TryGetValue("functions", out string functions)) { string path = string.Empty; string[] parts = functions.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); for (int n = 0; n < parts.Length; n++) { path += "/" + parts[n]; html.Breadcrump.Add(new WebLink() { Text = parts[n], Link = $"/Explain?functions={path}" }); } paths = paths.Where(p => p.Key.StartsWith(functions)); } Bootstrap4 content = html.Content; content.ListGroupOpen(); int i = 0; ILookup <WebServerMethod, string> lookup = paths.ToLookup(p => p.Value, p => p.Key); foreach (IGrouping <WebServerMethod, string> item in lookup) { WebServerMethod function = item.Key; // if (item.Key == "/") continue; content.ListGroupItemOpen(i++ % 2 == 0 ? " list-group-item-info" : null); XNetDocItem doc = documentation.GetMethod(function.Method); content.AddHtml("<div style=\"width:100%\">"); content.DivOpen(Bootstrap4.Item.row); WebServerAuthType authType = function.PageAttribute?.AuthType ?? WebServerAuthType.None; if (authType != WebServerAuthType.None) { content.DivOpen(Bootstrap4.Item.col, "col-12 col-sm-auto flex-sm-last"); AddBadges(content, function.PageAttribute); content.DivClose(Bootstrap4.Item.col); } content.DivOpen(Bootstrap4.Item.col); content.AddHtml("<h4>"); content.AddHtml(function.Method.Name.SplitCamelCase().Join(" ")); if (function.Parameters.Length > 0) { content.AddHtml(" ("); content.AddHtml(function.ParameterString()); content.AddHtml(")"); } content.AddHtml("</h4>"); content.DivClose(Bootstrap4.Item.col); content.DivClose(Bootstrap4.Item.row); foreach (string path in item) { content.DivOpen(Bootstrap4.Item.row); content.DivOpen(Bootstrap4.Item.col); content.Link(path, $"Explain?function={path}"); content.DivClose(Bootstrap4.Item.col); content.DivOpen(Bootstrap4.Item.col, "col-12 col-sm-auto"); content.Link("html", path + ".html", "btn btn-sm btn-outline-primary"); content.Link("json", path + ".json", "btn btn-sm btn-outline-primary"); content.Link("xml", path + ".xml", "btn btn-sm btn-outline-primary"); content.Link("plain", path + ".txt", "btn btn-sm btn-outline-primary"); content.DivClose(Bootstrap4.Item.col); content.DivClose(Bootstrap4.Item.row); } if (doc?.Summary != null) { content.DivOpen(Bootstrap4.Item.row); content.DivOpen(Bootstrap4.Item.col); content.AddHtml("<strong>Description:</strong>"); int cdata = doc.Summary.IndexOf("<![CDATA["); if (cdata > -1) { content.Text(doc.Summary.Substring(0, cdata)); content.AddHtml("<br/><code>"); string code = doc.Summary.Substring(cdata + 9); int cdataEnd = code.IndexOf("]]>"); content.AddHtml(code.Substring(0, cdata)); content.AddHtml("</code>"); content.Text(doc.Summary.Substring(cdata + cdataEnd + 9 + 3)); } else { content.Text(doc.Summary); } content.DivClose(Bootstrap4.Item.col); content.DivClose(Bootstrap4.Item.row); } content.AddHtml("</div>"); content.ListGroupItemClose(); } content.ListGroupClose(); content.AddHtml(" "); var message = WebMessage.Create("Explain", functions == null ? "Explain functions." : $"Explain {functions} functions."); data.Answer = html.ToAnswer(message); }
/// <summary>Builds the template.</summary> /// <param name="data">The data.</param> /// <returns>Returns true on success, false otherwise.</returns> public bool Render(WebData data) { if (data.Server != server) { throw new ArgumentOutOfRangeException(nameof(data.Server)); } { // need reload ? DateTime lastChanged = FileSystem.GetLastWriteTimeUtc(FileName); if (lastChanged != LastChanged) { Reload(); } } // do auth and load user session (if any) data.Result.SkipMainObject = true; data.Result.TransmitLayout = false; // call functions IDictionary <string, string> templateParameters = data.Request.Parameters; var tables = new Set <string>(); var parameterDescription = new List <WebTemplateParameter>(); for (int i = 0; i < functions.Length; i++) { Func function = functions[i]; parameterDescription.AddRange(function.ParameterDescriptions); if (function.NeededParameters.Count > 0) { // foreach neededparameters, any parameter is not at tmeplate parameters -> continue if (function.NeededParameters.Where(n => !templateParameters.ContainsKey(n)).Any()) { continue; } } var functionParameters = new Dictionary <string, string>(); foreach (System.Reflection.ParameterInfo methodParameter in function.Method.Parameters) { // lookup function parameter name from function section at template if (!function.Parameters.TryGetValue(methodParameter.Name, out string templateParameterName)) { continue; } // parameter name at template could be loaded if (!templateParameters.TryGetValue(templateParameterName, out string parameterValue)) { if (!methodParameter.IsOptional) { // no value given and is not optional throw new WebServerException(WebError.InvalidParameters, $"Template error: Missing {methodParameter.Name} is not for function {function} is not set. Define {templateParameterName} at template call!"); } continue; } functionParameters[methodParameter.Name] = parameterValue; } data.Request.Parameters = new ReadOnlyDictionary <string, string>(functionParameters); // invoke method data.Method = function.Method; data.Server.CallMethod(data); } Stopwatch renderWatch = server.PerformanceChecks ? Stopwatch.StartNew() : null; // replace content byte[] result = staticData ?? BuildStaticData(content); if (renderWatch != null) { Trace.TraceInformation("Template static data generation took {0}", renderWatch.Elapsed.FormatTime()); } // render data { data.Result.Type = WebResultType.Json; data.Result.AddStructs(parameterDescription); WebAnswer answer = data.Result.ToAnswer(); result = result.ReplaceFirst(Tag, ScriptStart, answer.ContentData, ScriptEnd); } // set result data.Result = null; WebMessage message; if (data.Method != null) { message = WebMessage.Create(data.Method, $"Template call <cyan>{data.Request}<default> succeeded."); } else { message = WebMessage.Create($"Static {data.Request.PlainUrl}", $"Template call <cyan>{data.Request}<default> succeeded."); } data.Answer = WebAnswer.Raw(data.Request, message, result, "text/html"); return(true); }