Changelog for WebFramework

2.0.0.1 (2024-03-18)

  • Added ToBase64PathSafe and FromBase64PathSafe and replaced the calls to ToBase64 and FromBase64 in BackupPartInfo and RestorePartInfo with those.

2.0 (2024-03-03)

  • Added string Parsers.ToBase64(string) and string FromBase64(string).
  • Added Server.Config.Backup with bool Enabled (default: false), string Directory, DayOfWeek FreshDay and TimeSpan Time.
  • Added Table.EnumerateOtherFiles and EnumerateOtherDirectories to enumerate other things that are associated with a given database entry while it is being backed up.
  • Changed Table.Backup to have parameters in order to be compatible with the new backup functionality.
  • Changed Table.Backup to print errors to the console instead of returning them as a dictionary.
  • Added void IPlugin.Backup, Plugin.Backup, ITable.Backup, Tables.BackupAll and PluginManager.Backup.
  • Added void IPlugin.Restore, Plugin.Restore, ITable.Restore, Tables.RestoreAll and PluginManager.Restore.
  • Added bool Server.BackupRunning and RestoreRunning.
  • Added backup functionality to the worker.
  • Added Server.BackupNow(forceFresh) and Restore(string id).
  • Added class BackupTree with Encode().
  • Added class BackupPartInfo with Load(id), Finish(), BackupFile(path) and BackupDirectory(path).
  • Added class RestorePartInfo with Load(id) and Restore().
  • Added delegate BackupHandler and RestoreHandler.
  • Added events BackupAlmostDone and RestoreAlmost done.
  • Added bool QueryManager.TryGetValue(string key, out T value) and T? QueryManager.TryGet(string key).
  • Added List IRequest.Domains and changed every reference of Parsers.Domains(string) to use the list from the current request object instead of calculating it again. Parsers.Domains(string) is being called in the "Init" layer.
  • Added LayerDelegate and LayerRequestData.
  • Added layers Init, LetsEncrypt, HttpsRedirect, Redirect, File, Auth and Handler.
  • Added Server.Config.Layers.
  • Changed the middleware to use the layers instead.
  • Optimized the layers' old code.
  • Changed request constructors to only pass on a LayerRequestData object.
  • Added string? FriendlyName and ReadOnlyCollection? LimitedToPaths to AuthTokenData and modified the methods that use it accordingly.
  • Added AccountManager.GenerateAuthTokenCookieOptions and changed AddAuthTokenCookie to use it.
  • Added AuthManager.AddNewLimited.
  • Added a constructor to BulletList and OrderedList that accepts items as IEnumerable.
  • Added status codes 301 and 302 to Server.Config.StatusMessages.
  • Added bool Flatten to Script and Style (settings this to true always exports the code directly to the page instead of potentially referencing it).
  • Changed PluginManager.GetPlugin to also return the domain.
  • Changed the title part of Page.Export to also include the title extension on error pages.
  • Added code to Page.Export to export .wfpg for status/error pages if present (e.g. placed in Public/example.com/status/404.wfpg, "any.wfpg" also works as a fallback and [STATUS] and [MESSAGE] insert the status code and associated message).
  • Updated to SmtpServer 10.0.1.
  • Replaced MailboxFilterResult MailManager.In.AcceptMail with bool MailboxExists.
  • Added bool MailManager.In.HasCertificate.
  • Added X509Certificate2? Server.GetCertificate(string domain).
  • Changed the mail server to use a certificate factory instead of needing a restart whenever a new certificate is received for the mail server domain.
  • Added bool MailManager.In.TryRestart().
  • Various optimizations.

1.5.4 (2024-02-10)

  • Added Parsers.HtmlValueSafe as a method that escapes characters that might hurt the layout or exit the quotes for an HTML attribute value.
  • Changed Parsers.HtmlSafe to be more efficient and to escape \n as well.
  • Changed the following classes to be more efficient and HTML safe: Page, CustomPage, ButtonJS, ContainerElement, ContainerElementIsoTop, HeadingElement, Checkbox, FileSelector, FlexibleTextArea, Image, Paragraph, Selector, TextArea, TextBox.
  • Changed PageParser to use the next paragraph's text if the description command was used without a parameter.
  • Changed PageParser.ParseCommand to add abbreviations.
  • Fixed the "a" field for CheckSPF and IPv6 for Parsers.Domain(c).
  • Added Parsers.PluginHome(string) and DomainMain(string).
  • Added SameSite to auth cookies for wildcard domains as well.
  • Added bool Server.Config.Accounts.SameSiteStrict=false to indicate whether to use SameSite=Strict instead of Lax.
  • Added bool Server.Config.Accounts.HttpOnly=true to indicate whether authentication cookies should only be accessible for HTTP requests (not from JavaScript directly).
  • Changed AddAuthTokenCookie to be more efficient.
  • Added a case for plugin files without a timestamp to ScriptOrStyle.Export.
  • Removed Themes and ThemeName(...) from Presets and PresetsHandler.
  • Added _-+. to the allowed table key characters.
  • Added MailManager.DnsServersCloudflare as a possible option for MailManager.DnsServers.
  • Added MailManager.In.TryStart.
  • Changed the WFPG parser to append lines that start with
    but aren't only
    to the last paragraph if one exists, instead of creating a new paragraph.

1.5.3 (2023-12-29)

  • Optimization for AccountManager and the worker.
  • Added more information to the default value of Server.Config.Accounts.FailedAuth.BanMessage.
  • Added the default value of Server.Config.Accounts.FailedAuth.BanMessage to its documentation.
  • Added bool Server.Config.Accounts.FailedAuth.LogBans (default: false).
  • Added bool FileSelector.AllowMultiple.
  • Made User.Auth and User.Settings enumerable.
  • Added Parsers.Before and After with a string as the separator.
  • Made the value of TextBox, TextArea and FlexibleTextArea HTML-safe.
  • Changed the 2FA validation to fully renew the token instead of just changing its properties.
  • Changed the mail token validation to renew the token.
  • Applied the actual expiration times for temporary tokens to cookies.
  • Added string? OnChange to Checkbox, FileSelector and Selector.
  • Added constructors that accepts multiple strings as paragraphs to ContainerElement, ContainerElementIsoTop, HeadingElement, LargeContainerElement and LargeContainerElementIsoTop.
  • Changed AccountManager.AddAuthTokenCookie to use path "/".

1.5.2.1 (2023-11-18)

  • Corrected documentation for IRequest.User.
  • Added bool IRequest HasUser.

1.5.2 (2023-11-18)

  • Added string? MailAuth.CheckPTR(IPAddress).
  • Added string? PTR to MailAuth.FullResult.
  • Added IPageElement Server.Config.Accounts.FailedAuth.BanMessage and applied it to Page.
  • Added a case for LoginState.Banned to PresetsHandler.AccountButton.
  • Added method TryGetValue to AuthManager (User.Auth).
  • Optimized UserTable.Authenticate, TwoFactorData.Validate, DownloadRequest and SimpleResponseRequest.
  • Changed IRequest to make the public "User" non-nullable. Nullability can be checked using bool IRequest.LoggedIn.
  • Added Server.Config.Log to contain settings to control what types of log entries will appear in the console.
  • Moved Server.Config.ShowAspNetLogs to Server.Config.Log.AspNet.
  • Added Server.Log.Startup.
  • Added Server.Config.Log.AuthTokenExpired and AuthTokenRenewed.
  • Added bool Config.Database.WriteBackOnLoad to decide whether to write the new JSON to the disk if it doesn't match the old one while loading a table (default: true).
  • Moved AccountManager.Settings to Server.Config.Accounts.
  • Changed everything in Server.Config to be a property instead of a field.
  • Updated to MailKit 4.3.0.
  • Updated to .NET 8.

1.5.1.2 (2023-11-07)

  • Added parameter string potentialMessageId to Mail.BeforeSendDelegate.
  • Changed MailManager.Out.Send to generate and pass over potential message IDs to BeforeSend for internal mail sending.

1.5.1.1 (2023-11-07)

  • Added Dictionary Internal to MailSendResult.
  • Added parameter out string log to Mail.BeforeSendDelegate.
  • Changed MailManager.Out.Send to include logs from BeforeSend and check if there are any addresses left before attempting to send FromSelf.
  • Removed obsolete MailManager.Out methods and changed some methods to be private as they are not needed outside anymore.
  • Changed PresetHandler.WarningMail to use the new Mail.Out.Send method instead.

1.5.1 (2023-11-07)

  • Added event Mail.Out.BeforeSend to determine whether to send a mail message externally to a specific recipient. This allows for internal mail sending and filtering.

1.5.0.2 (2023-11-05)

  • Added a check for Context.RequestAborted to EventRequest.KeepAlive.
  • Changed CheckDMARC to properly comply with the standard, it was too strict before.
  • Added bool Server.Config.Domains.UseCanonicalForHandling. If it is set to true (default), the canonical origin domain of the requested domain and of "any" may be used to serve files and plugins if no better option was found.
  • Added a warning about using the old value object to UnlockRestore in ITableEntry, ITableValue and TableEntry.

1.5.0.1 (2023-10-31)

  • Added a missing space to the Image exporter.
  • Fixed the documentation for BulletList and OrderedList.
  • Added documentation for all of the MailAuth stuff.

1.5 (2023-10-30)

  • Added EventRequest for server-sent events to /event/... with Send(string) and KeepAlive(), as well the event and the method for plugins.
  • Added Checkbox (IContent) with string Text and bool Checked.
  • Changed the constructors for each container elements without any contents to accept individual contents as parameters.
  • Added a lot of stuff for mail authentication (static class MailAuth) to validate SPF, DKIM and DMARC.
  • Renamed UList to BulletList.
  • Added OrderedList (IContent).

1.4.2 (2023-10-06)

  • Added clear divs to elements with buttons so they no longer overflow if overflow is not set to hidden.
  • Added List AccountManager.Settings.WildcardDomains to allow for auth cookies to be shared across a domain and its subdomains.
  • Added string Domain(this HttpContext c) to get the host without the port and replaced Host(c) with it in some places.
  • Added TryGet and TryGetValue to CookieManager.
  • Added class MailGenAttachment for MailGen so attachments can be sent along with emails.
  • Replaced MailGen.Text and IsHtml with TextBody and HtmlBody and added a list of attachments to MailGen.
  • Added method User.Auth.Renew(...) to replace a given auth token with a new one.
  • Changed UserTable.Authenticate to renew auth tokens instead of extending their validity.
  • Changed IRequest.RedirectUrl to accept URLs starting with http:// or https:// if the domain is the same or the domains share auth cookies according to the wildcard setting.
  • Changed IRequest.Domain to use the domain without the port (if present) instead of the full host string.
  • Added PluginManager.GetDomains().
  • Added plugin domains to the domains for AutoCertificate.
  • Added an option to pre-select an item in Selector.
  • Changed MailManager.DnsLookup to have a public getter.
  • Renamed MailAuthResult to MailConnectionData.

1.4.1 (2023-09-26)

  • Fixed that POST requests without forms cause an internal error that isn't being handled.
  • Changed the default email size limit to 64MB (instead of 16MB).
  • Moved text/byte/file writing and finish of ApiRequest and CorsDomain to a new abstract class SimpleResponseRequest.
  • Changed ApiRequest, PostRequest and UploadRequest to inherit SimpleResponseRequest. This way, POST and upload requests can now respond.
  • Added PostRequest.HasForm.
  • Added PostRequest.GetBodyText().

1.4 (2023-09-24)

  • Added class Password3 with PBKDF2 to replace Password2 in User.
  • Added new classes for 2FA to allow for multiple types of 2FA (and at the same time) in the future and replaced the 2FA stuff in User with it.
  • Used TOTP (2FA) time steps are now persistent.
  • Added Argon2 password hashing to Password3 (new default).
  • Created class User_Old2 with all properties of User except Password2 instead of Password3, TwoFactor instead of TwoFactorData and no methods, and added constructor User(User_Old2 old). This was done so old User objects can still be deserialized and upgraded.
  • Replaced User._Username with Username, removing the field that wrapped around _Username before.
  • Added method Serialization.DeserializeUser to specifically deserialize User objects and upgrade them if necessary. Deserialize(...) should no longer be used for User objects and a type check has been implemented for it.
  • Updated UserTable.Reload to be more specific for users, to allow object upgrading.
  • Added bool AccountManager.Settings.AutoUpgradePasswordHashes to set whether to automatically upgrade password hashes to the set default hash settings when users log in and their hash settings doesn't match the default settings.
  • Server.CurrentHttpContext will now return null instead of throwing an exception if no context was found.
  • Added the canonical origin of "any" to the Parsers.Domains result.
  • Database table entries will now be locked before trying to check and fix them, there is an option to avoid this.
  • Removed 'Response' from MailSendResult.Attempt as that should be written to the log instead (there can be multiple responses, after all).
  • Replaced MailSendResult.Attempt.Success (bool) with ResultType (Success, Mixed, Fail).
  • Added string Parsers.EnumerationText(this IEnumerable values) to return something like this: "[0], [1], [2] and [3]".
  • Added method MailManager.Out.Send that optionally accepts IEnumerable as TO (to allow for multiple recipients) and generates, signs and sends the mail in one step.
  • Created a class MailGen that contains all of the information needed to generate a mail message. This can be used to send messages instead of passing all of the parts as individual parameters.

1.3.4.1 (2023-09-18)

  • Added a HEAD request handler that simply returns code 200 for path / and 404 otherwise. This allows the server to be seen in server discovery using HEAD requests.

1.3.4 (2023-09-17)

  • Added parameter "string domain" to IPlugin.GetFile(...).

1.3.3.3 (2023-09-16)

  • Made Tables.Dictionary public so plugins and custom handlers can create custom tables.
  • Made UserTable.Delete slightly more efficient.

1.3.3.2 (2023-09-15)

  • Plugin files can now be accessed even if mail verification or 2FA confirmation are currently required.

1.3.3.1 (2023-09-15)

  • The constructor of User will no longer attempt to lock the table entry for it that doesn't exist yet.

1.3.3 (2023-09-15)

  • Added a missing else to ScriptOrStyle.

1.3.2 (2023-09-15)

  • NuGet package published :)
  • ScriptOrStyle now also works as intended if the URL contains a query string.

1.3.1 (2023-09-09)

  • Replaced all of the generated code parts for all pages, elements, scripts and styles with enumerators so page generation is now way more efficient and has greatly improved runtime characteristics in general.
  • CustomPage: Removed automatic favicon adding, renamed Head and Body to HeadLines and BodyLines, added title extension appending like in Page, added a title parameter to the constructor.
  • Changed the default favicon URL of Page to null.
  • Renamed Preload.Link to Url.
  • Moved Title, Scripts and Styles from IPage to Page and CustomPage and made IPage an interface instead of an abstract class.
  • Added a copy of Parsers.TryGetValueAny that allows for IEnumerable instead of params KEY[] so Page and CustomPage don't need to convert the domain list to an array.
  • Replaced Presets.ErrorScript and RedirectScript with direct source code instead of a link so library users don't have to add those scripts themselves.
  • Replaced PresetHandler.AccountText with AccountButton so the preset decides everything about the navbar account button.
  • Replaced Cache.AppendTextLinesTo and GetTextLines with EnumerateTextLines.
  • Removed the second div layer from ContainerElement and created a new class (Large)ContainerElementIsoTop for that purpose.
  • Changed a few types and names of the presets and script/styles to make more sense.
  • Documented all remaining classes (pages, elements, container contents, presets, scripts and styles).

1.3.0 (2023-09-08)

  • The code is now available on GitHub (that's why most stuff has been documented and stuff was moved to make more sense).
  • Removed the delays to receive mail as they're not actually being used.
  • Added documentation for everything that was left except for pages, elements, styles and scripts.
  • Moved AutoCertificate settings to Server.Config.AutoCertificate.
  • Renamed CacheEntry.AppendTextLines to AppendTextLinesTo.
  • Added method CacheEntry.GetTextLines().
  • The worker will now run when the program starts even if the interval is set to 0. Setting the interval to -1 prevents this.
  • Various optimizations
  • Added various methods to handle .wfpg files.
  • Added various methods to Parsers.
  • Added Image.Title (tooltip).
  • The mail server now also gracefully shuts down when exiting the program using Server.Exit.
  • Added a sidebar to Page and changed the layout a little bit.
  • Added method Page.PopulateSidebar to fill the sidebar with anchor buttons for every element with an ID.
  • Added methods GetFile and GetFileVersion to IPlugin and Plugin.
  • Added parameter string pluginPath to plugin handler methods so they know where they are currently mapped (not static for each plugin because one plugin can be mapped to multiple paths).
  • Plugin requests' relative path will no longer be made "/" if it is "". It will be made "" if the full path (either absolute or after /api or /dl) is "/".
  • Files can now be placed directly into ../Private (as immediate children without a subdirectory, not ../Public), .wfpg files, scripts and styles can be loaded like this for now.
  • Directories and files in ../Public and ../Private (only immediate children - doesn't apply to subdirectories) that start with a dot will no longer be loaded into the cache.
  • The middleware will now check if the mapped plugin (if present) has a file at the requested path before attempting to handle the request as an AppRequest.
  • Added method Parsers.Extension(this string path) to get the file extension of a given path.
  • Styles, scripts and favicons as plugin-served files are now detected.
  • When an AppRequest finishes with an error code and no page has been set, a new one will be created from the preset (CreatePage and Navigation).
  • There is now a page "Redirecting..." that is shown when a redirecting AppRequest results in another app being opened.

1.2.1 (2023-08-10)

  • Added an optional contructor argument for Search to allow for a custom first relevance value and changed the default to 1000.
  • Changed the type of the relevance values and settings in Search to uint.
  • Table names are now being checked for compliance with Tables.KeyChars as well.
  • Added documentation to more properties and methods.

1.2.0 (2023-08-09)

  • Renamed various configuration properties to make more sense.
  • Added documentation to various properties and methods.
  • Added options to set after how long auth tokens expire and get renewed on usage.
  • Changed the type of AccountManager.Settings.FailedAttempts.BanDuration to TimeSpan instead of int for hours.
  • Renamed the password hash "Method" to "Algorithm"
  • Database objects can now be locked multiple times by the same context.
  • Tokens without 2FA or mail verification expire after 10 minutes and don't get renewed until their login process is fully completed. Note that restoring and saving during an inner call applies to changes outside of it as well.

1.1.4 (2023-05-25)

  • Replaced ITableValue.ReportChange() with Lock and UnlockSave/UnlockDiscard.
  • Replaced the locking mechanism for table TableEntry with a custom solution.
  • Added field Server.CurrentHttpContext for the object locking mechanism so no request objects have to be passed around for it.

1.1.3 (2023-05-23)

  • Added methods RedirectToLogin and RedirectToQuery to AppRequest
  • Added lots of methods in the static class Presets
  • Custom presets can now be created and saved for shared use by inheriting PresetsHandler and setting Presets.Handler to that

1.1.2 (2023-05-20)

  • The server domain is now being used in the HELO message when sending mail as well.
  • Scripts and Styles by URL may only have URLs ending with .js or .css now.
  • Changed the "Powered by" text and HTTP server header from uwap.dev/wf to uwap.org/wf.

1.1.1 (2023-05-06)

  • Replaced the password stuff in User with a new class Password2 to increase password security and allow to future security improvements without having to migrate the data, as long as .NET supports the new method for PBKDF2.
  • Added static settings in uwap.WebFramework.Accounts.Password2: DefaultMethod=SHA512, DefaultPassCount=1048576, DefaultSaltLength=16, DefaultHashLength=64. These settings are very secure, but not very fast (for good reason).
  • Note: You shouldn't have to touch those password settings, as the default values will be updated once new algorithms become popular. Accounts will start using the new settings when a new password is set for them.
  • Another note: If logins are too slow for your taste after this update, consider decreasing the DefaultPassCount setting to 1048576 or even lower (remember to set a new password to apply it to your account). This reduces security a bit.
  • Added method Password2.WasteTime(string password) to spend about the same amount of time as Check(string password) would take. This is useful if the username wasn't found and that fact should be a secret (already used by UserTable.Login).

1.1.0 (2023-05-02)

  • Added the option to map plugins to a domain-path-combination with fallbacks, also considering the domain set as the canonical origin. Plugins can be created inheriting interface uwap.WebFramework.IPlugin or class Plugin.
  • Added methods for handling app/api/download/upload/post requests to plugins.
  • Added a method to be called by the worker to plugins.
  • The origin domains of the requested domain will also be considered for Page stuff now. (title, canonical link, favicon, copyright name)
  • The origin domains of the requested domain will also be considered for file serving now.
  • Added Dictionary Server.Config.Domains.Redirect to automatically redirect from the key domain to the value domain, like from www.uwap.dev to uwap.dev or the other way around.
  • Added classes to send a receive mail. Refer to the manual for details.
  • AutoCertificate now requests a certificate for MailManager.ServerDomain as well, if present.
  • AutoCertificate no longer attempts to get certificates for ../Public folders without dots or with just one dot at the start.

1.0.6 (2023-04-28)

  • Replaced RawHtmlElement with CustomElement, this supports multiple lines as a List as well now.
  • Added CustomStyle, similar to CustomScript
  • Added {501,"Created."} to the default status messages

1.0.5 (2023-04-20)

  • Added string? Page.Favicon to set the path for favicons. The default value is "/favicon.ico", null disables it.
  • Added List Page.Head to allow for adding custom elements to .
  • Added JSON and SVG to the default MIME types and cache types.

1.0.4 (2023-04-16)

  • Made Table enumerable with KeyValuePair so foreach and other stuff can be used.
  • Added List AutoCertificate.Domains in addition to the Public folder. This is meant for domains that don't have any public files beyond 'any'.
  • Added string? OnInput to TextBox, TextArea and FlexibleTextArea to do something in JS whenever the value changes.

1.0.3 (2023-04-15)

  • Added a canonical property to Page (generated on export), use the dictionary (see below) to control this behavior. Set the value for key="any" to null to disable this entirely. This is mainly for SEO purposes.
  • Moved setting Server.Config.AutoCertificateEmail to AutoCertificate.Email (AutoCertificate is now its own class).
  • Changed the content type for ACME challenge responses (used for automatic certificates) to text/plain;charset=utf-8 instead of text/plain, just in case they ever want special characters and don't default to UTF-8.
  • The server now checks if it is reachable over a domain before even trying to request a certificate for it and throws an exception if it is unreachable. To disable these errors, set the new bool AutoCertificate.MuteUnreachableErrors to true.
  • Added Server.Config.Domains with three Dictionary (details below). Set key "any" as a fallback in case a domain can't be found.
  • Added dictionary TitleExtensions to assign an extension to the title like "Title | Extension" to a domain (null means no extension, missing means domain as extension).
  • Added dictionary CopyrightNames to assign a name for the copyright part in the footer for a domain (null means no copyright in the footer, missing means domain as copyright name).
  • Added dictionary CanonicalDomains to set an origin domain for a domain (null means domain as canonical domain, missing without "any" means no canonical property).
  • Added string? Page.Description for SEO and website previews.
  • Added bool TryGetValueAny(this Dictionary dictionary, out TValue result, params TKey[] keys) extension method to Parsers. This will use the first found key's value as the result or return false if none were found.

1.0.2 (2023-04-09)

  • Added CustomScript to provide the code for scripts dynamically, mainly for short scripts that don't need a new file.
  • Added a constructor for HeadingElement that only takes one string as its parameter to allow for simple headings without giving an empty string.
  • Added methods CallApi() or CallDownload() to AppRequest to change the request to an API or download request, this locks the present AppRequest and calls the corresponding event with a new request object.
  • ContentType is now being set to text/plain when writing a status message, this fixes the displaying of status messages in Firefox.

1.0.1 (2023-03-24)

  • Added calming messages after fixing UserTable accelerators so admins know the issue has automatically been fixed.
  • Broken Table entries will now be automatically fixed if possible (the before mentioned calming messages have been added here too).
  • Table.Delete(string) and User.Settings.Delete(string) will now return true if successful and false if the key wasn't found instead of throwing an exception.

1.0.0 (2023-03-22)

  • First stable and official build for this complete reimagination + rewrite of the web framework! :)
  • Added 'any' as a domain wildcard for the user table selector.
  • Added method 'bool TryGetValue(string, out T)' to Table and User.Settings.