diff --git a/.gitignore b/.gitignore
index 3292aea..db8bee1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,6 @@ obj/
bin/
.vs
.vscode/*
+/cmpctircd.sln.DotSettings.user
+/cmpctircd.v3.ncrunchsolution
+/_NCrunch_cmpctircd
diff --git a/cmpctircd/App.config b/cmpctircd/App.config
deleted file mode 100644
index 5bd659d..0000000
--- a/cmpctircd/App.config
+++ /dev/null
@@ -1,91 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/cmpctircd/Client/Client.cs b/cmpctircd/Client/Client.cs
index 36a127a..d5bfbfe 100755
--- a/cmpctircd/Client/Client.cs
+++ b/cmpctircd/Client/Client.cs
@@ -8,6 +8,7 @@
using cmpctircd.Modes;
using System.Net;
using System.IO;
+using Microsoft.Extensions.Configuration;
namespace cmpctircd
{
@@ -43,7 +44,7 @@ public IDictionary Modes {
public string NickIfSet() => string.IsNullOrEmpty(Nick) ? "*" : Nick;
public Client(IRCd ircd, TcpClient tc, SocketListener sl, Stream stream, string UID = null, Server OriginServer = null, bool RemoteClient = false) : base(ircd, tc, sl, stream) {
- if(ircd.Config.Advanced.ResolveHostnames)
+ if(ircd.Config.Value.Advanced.ResolveHostnames)
ResolvingHost = true;
this.UID = UID;
@@ -94,7 +95,7 @@ public override void BeginTasks() {
// Check for PING/PONG events due
CheckTimeout();
- if(IRCd.Config.Advanced.ResolveHostnames)
+ if(IRCd.Config.Value.Advanced.ResolveHostnames)
Resolve();
} catch(Exception e) {
IRCd.Log.Debug($"Failed to access client: {e.ToString()}");
@@ -169,7 +170,7 @@ public void Resolve() {
IRCd.DNSCache[ip] = DNSHost;
}
- if (IRCd.Config.Advanced.ResolveHostnames) {
+ if (IRCd.Config.Value.Advanced.ResolveHostnames) {
// If the IRCd resolves all hostnames, then we will have
// delayed calling SendWelcome until DNS resolution was complete
SendWelcome();
@@ -252,7 +253,7 @@ public void SendLusers() {
users += list.Count();
// Count all of the users with the user mode +i (invisible)
invisible += list.Where(client => client.Modes["i"].Enabled).Count();
- // Count all of the users with usermode +o (IRC Operators)
+ // Count all of the users with usermode +o (IRC Opers)
ircops += list.Where(client => client.Modes["o"].Enabled).Count();
}
users -= invisible;
diff --git a/cmpctircd/Configuration/AdvancedElement.cs b/cmpctircd/Configuration/AdvancedElement.cs
deleted file mode 100644
index ef63a7d..0000000
--- a/cmpctircd/Configuration/AdvancedElement.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Configuration;
-
-namespace cmpctircd.Configuration {
- public class AdvancedElement : ConfigurationElement {
- [ConfigurationProperty("resolveHostnames", IsRequired = true)]
- public bool ResolveHostnames {
- get { return (bool) this["resolveHostnames"]; }
- set { this["resolveHostnames"] = value; }
- }
-
- [ConfigurationProperty("requirePongCookie", IsRequired = true)]
- public bool RequirePongCookie {
- get { return (bool) this["requirePongCookie"]; }
- set { this["requirePongCookie"] = value; }
- }
-
- [ConfigurationProperty("pingTimeout", IsRequired = true)]
- public int PingTimeout {
- get { return (int) this["pingTimeout"]; }
- set { this["pingTimeout"] = value; }
- }
-
- [ConfigurationProperty("maxTargets", IsRequired = true)]
- public int MaxTargets {
- get { return (int) this["maxTargets"]; }
- set { this["maxTargets"] = value; }
- }
-
- [ConfigurationProperty("cloak", IsRequired = true)]
- public CloakElement Cloak {
- get {
- return this["cloak"] as CloakElement;
- }
- }
- }
-}
diff --git a/cmpctircd/Configuration/CloakElement.cs b/cmpctircd/Configuration/CloakElement.cs
deleted file mode 100644
index 48b06e9..0000000
--- a/cmpctircd/Configuration/CloakElement.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System.Configuration;
-
-namespace cmpctircd.Configuration {
- public class CloakElement : ConfigurationElement {
- [ConfigurationProperty("key", IsRequired = true)]
- public string Key {
- get { return (string) this["key"]; }
- set { this["key"] = value; }
- }
-
- [ConfigurationProperty("prefix", IsRequired = true)]
- public string Prefix {
- get { return (string)this["prefix"]; }
- set { this["prefix"] = value; }
- }
-
- [ConfigurationProperty("domainParts", IsRequired = true)]
- public int DomainParts {
- get { return (int) this["domainParts"]; }
- set { this["domainParts"] = value; }
- }
-
- [ConfigurationProperty("full", IsRequired = true)]
- public bool Full {
- get { return (bool) this["full"]; }
- set { this["full"] = value; }
- }
- }
-}
diff --git a/cmpctircd/Configuration/CmpctConfigurationSection.cs b/cmpctircd/Configuration/CmpctConfigurationSection.cs
deleted file mode 100644
index 0e56e39..0000000
--- a/cmpctircd/Configuration/CmpctConfigurationSection.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-using System.Configuration;
-
-namespace cmpctircd.Configuration {
- public class CmpctConfigurationSection : ConfigurationSection {
- public static CmpctConfigurationSection GetConfiguration() {
- return (CmpctConfigurationSection) ConfigurationManager.GetSection("ircd") ?? new CmpctConfigurationSection();
- }
-
- [ConfigurationProperty("sid", DefaultValue = "auto", IsRequired = false)]
- public string SID {
- get { return (string) this["sid"]; }
- set { this["sid"] = value; }
- }
-
- [ConfigurationProperty("host", IsRequired = true)]
- public string Host {
- get { return (string) this["host"]; }
- set { this["host"] = value; }
- }
-
- [ConfigurationProperty("network", IsRequired = true)]
- public string Network {
- get { return (string) this["network"]; }
- set { this["network"] = value; }
- }
-
- [ConfigurationProperty("description", DefaultValue = "", IsRequired = false)]
- public string Description {
- get { return (string) this["description"]; }
- set { this["description"] = value; }
- }
-
- [ConfigurationProperty("sockets", IsRequired = true)]
- [ConfigurationCollection(typeof(SocketElement), AddItemName = "socket")]
- public SocketElementCollection Sockets {
- get {
- return this["sockets"] as SocketElementCollection;
- }
- }
-
- [ConfigurationProperty("tls", IsRequired = false, DefaultValue = null)]
- public TlsElement Tls {
- get {
- return this["tls"] as TlsElement;
- }
- }
-
- [ConfigurationProperty("log", IsRequired = true)]
- [ConfigurationCollection(typeof(LoggerElement), AddItemName = "logger")]
- public LoggerElementCollection Loggers {
- get {
- return this["log"] as LoggerElementCollection;
- }
- }
-
- [ConfigurationProperty("advanced", IsRequired = true)]
- public AdvancedElement Advanced {
- get {
- return this["advanced"] as AdvancedElement;
- }
- }
-
- [ConfigurationProperty("cmodes")]
- [ConfigurationCollection(typeof(ModeElement), AddItemName = "mode")]
- public ModeElementCollection AutomaticModes {
- get {
- return this["cmodes"] as ModeElementCollection;
- }
- }
-
- [ConfigurationProperty("umodes")]
- [ConfigurationCollection(typeof(ModeElement), AddItemName = "mode")]
- public ModeElementCollection AutomaticUserModes {
- get {
- return this["umodes"] as ModeElementCollection;
- }
- }
-
- [ConfigurationProperty("servers")]
- [ConfigurationCollection(typeof(ModeElement), AddItemName = "server")]
- public ServerElementCollection Servers {
- get {
- return this["servers"] as ServerElementCollection;
- }
- }
-
- [ConfigurationProperty("opers")]
- [ConfigurationCollection(typeof(ModeElement), AddItemName = "oper")]
- public OperatorElementCollection Operators {
- get {
- return this["opers"] as OperatorElementCollection;
- }
- }
- }
-}
diff --git a/cmpctircd/Configuration/ListConverter.cs b/cmpctircd/Configuration/ListConverter.cs
deleted file mode 100644
index cf4d89e..0000000
--- a/cmpctircd/Configuration/ListConverter.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Globalization;
-using System.Linq;
-
-namespace cmpctircd.Configuration {
- [TypeConverter(typeof(ListConverter))]
- class ListConverter : TypeConverter {
- public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
- return sourceType == typeof(string);
- }
-
- public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
- return value == null ? new List() : ((string) value).Split(' ').Where(s => !string.IsNullOrEmpty(s)).ToList();
- }
-
- public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
- return destinationType == typeof(string);
- }
-
- public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
- return string.Join(" ", (IEnumerable) value);
- }
- }
-}
diff --git a/cmpctircd/Configuration/LoggerElement.cs b/cmpctircd/Configuration/LoggerElement.cs
index 26dced2..b6dfbb8 100644
--- a/cmpctircd/Configuration/LoggerElement.cs
+++ b/cmpctircd/Configuration/LoggerElement.cs
@@ -1,36 +1,15 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
+using System.Collections.Generic;
using System.Configuration;
-
-namespace cmpctircd.Configuration {
- public class LoggerElement : ConfigurationElement {
- private readonly Dictionary _attributes = new Dictionary();
-
- // Guid, due to a lack of other unique properties for this element type.
- public Guid InstanceGuid {
- get;
- } = Guid.NewGuid();
-
- public IReadOnlyDictionary Attributes {
- get { return new ReadOnlyDictionary(_attributes); }
- }
-
- [ConfigurationProperty("type", IsRequired = true)]
- public LoggerType Type {
- get { return (LoggerType) this["type"]; }
- set { this["type"] = value; }
- }
-
- [ConfigurationProperty("level", IsRequired = true)]
- public LogType Level {
- get { return (LogType) this["level"]; }
- set { this["level"] = value; }
- }
-
- protected override bool OnDeserializeUnrecognizedAttribute(string name, string value) {
- _attributes.Add(name, value);
- return true;
- }
+using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace cmpctircd.Configuration
+{
+ public class LoggerElement
+ {
+ public LoggerType Type { get; set; }
+ public LogType Level { get; set; }
+ public Dictionary Attributes { get; set; } = new Dictionary();
}
-}
+}
\ No newline at end of file
diff --git a/cmpctircd/Configuration/LoggerElementCollection.cs b/cmpctircd/Configuration/LoggerElementCollection.cs
deleted file mode 100644
index 626c147..0000000
--- a/cmpctircd/Configuration/LoggerElementCollection.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.Configuration;
-
-namespace cmpctircd.Configuration {
- public class LoggerElementCollection : ConfigurationElementCollection {
- public LoggerElement this[int i] {
- get {
- return (LoggerElement)BaseGet(i);
- }
- }
-
- protected override ConfigurationElement CreateNewElement() {
- return new LoggerElement();
- }
-
- protected override object GetElementKey(ConfigurationElement element) {
- return ((LoggerElement) element).InstanceGuid;
- }
- }
-}
diff --git a/cmpctircd/Configuration/ModeElement.cs b/cmpctircd/Configuration/ModeElement.cs
index e36a6fe..899f8dc 100644
--- a/cmpctircd/Configuration/ModeElement.cs
+++ b/cmpctircd/Configuration/ModeElement.cs
@@ -1,17 +1,14 @@
-using System.Configuration;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
-namespace cmpctircd.Configuration {
- public class ModeElement : ConfigurationElement {
- [ConfigurationProperty("name", IsRequired = true)]
- public string Name {
- get { return (string) this["name"]; }
- set { this["name"] = value; }
- }
-
- [ConfigurationProperty("param", IsRequired = false, DefaultValue = "")]
- public string Param {
- get { return (string) this["param"]; }
- set { this["param"] = value; }
- }
+namespace cmpctircd.Configuration
+{
+ public class ModeElement
+ {
+ public string Name { get; set; }
+ public string Param { get; set; }
}
}
diff --git a/cmpctircd/Configuration/ModeElementCollection.cs b/cmpctircd/Configuration/ModeElementCollection.cs
deleted file mode 100644
index 0b49ccc..0000000
--- a/cmpctircd/Configuration/ModeElementCollection.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.Configuration;
-
-namespace cmpctircd.Configuration {
- public class ModeElementCollection : ConfigurationElementCollection {
- public ModeElement this[int i] {
- get {
- return (ModeElement) BaseGet(i);
- }
- }
-
- protected override ConfigurationElement CreateNewElement() {
- return new ModeElement();
- }
-
- protected override object GetElementKey(ConfigurationElement element) {
- return ((ModeElement) element).Name;
- }
- }
-}
diff --git a/cmpctircd/Configuration/OperatorElement.cs b/cmpctircd/Configuration/OperatorElement.cs
index f5656c5..85376e3 100644
--- a/cmpctircd/Configuration/OperatorElement.cs
+++ b/cmpctircd/Configuration/OperatorElement.cs
@@ -1,70 +1,31 @@
using System;
-using System.Configuration;
-using System.Xml;
using System.Collections.Generic;
-using System.Linq;
using System.ComponentModel;
+using System.Configuration;
using System.Globalization;
+using System.Linq;
namespace cmpctircd.Configuration {
public class OperatorElement : ConfigurationElement {
- [ConfigurationProperty("name", IsRequired = true, IsKey = true)]
- public string Name {
- get { return (string)this["name"]; }
- set { this["name"] = value; }
- }
+ public string Name { get; set; }
- [TypeConverter(typeof(HexadecimalConverter))]
- [ConfigurationProperty("password", IsRequired = true)]
- public byte[] Password {
- get { return (byte[]) this["password"]; }
- set { this["password"] = value; }
- }
+ public string Password { get; set; }
- [TypeConverter(typeof(TypeNameConverter))]
- [ConfigurationProperty("algorithm", IsRequired = true)]
- public Type Algorithm {
- get { return this["algorithm"] as Type; }
- set { this["algorithm"] = value; }
- }
+ public string Algorithm { get; set; }
- [ConfigurationProperty("tls", IsRequired = false, DefaultValue = false)]
- public bool Tls {
- get { return (bool) this["tls"]; }
- set { this["tls"] = value; }
- }
+ public bool Tls { get; set; }
+ public List Hosts { get; set; }
+ public Type AlgorithmType => Type.GetType($"{Algorithm}, System.Security.Cryptography.Algorithms");
+ public byte[] PasswordArray => ConvertPassword(Password);
- [TypeConverter(typeof(ListConverter))]
- [ConfigurationProperty("hosts", IsRequired = true)]
- public List Hosts {
- get { return (List) this["hosts"]; }
- set { this["hosts"] = value; }
- }
- }
-
- [TypeConverter(typeof(HexadecimalConverter))]
- class HexadecimalConverter : TypeConverter {
- public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
- return sourceType == typeof(string);
- }
-
- public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
+ public byte[] ConvertPassword(string value) {
// From hex string to byte[]
// https://stackoverflow.com/questions/321370/how-can-i-convert-a-hex-string-to-a-byte-array
- var hex = (string) value;
+ var hex = value;
return Enumerable.Range(0, hex.Length)
- .Where(x => x % 2 == 0)
- .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
- .ToArray();
- }
-
- public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
- return destinationType == typeof(string);
- }
-
- public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
- // From byte[] to hex string
- return String.Concat(Array.ConvertAll((byte[]) value, x => x.ToString("X2")));
+ .Where(x => x % 2 == 0)
+ .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
+ .ToArray();
}
}
-}
+}
\ No newline at end of file
diff --git a/cmpctircd/Configuration/OperatorElementCollection.cs b/cmpctircd/Configuration/OperatorElementCollection.cs
deleted file mode 100644
index eca4a59..0000000
--- a/cmpctircd/Configuration/OperatorElementCollection.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Configuration;
-
-namespace cmpctircd.Configuration {
- public class OperatorElementCollection : ConfigurationElementCollection {
- public OperatorElement this[int i] {
- get {
- return (OperatorElement) BaseGet(i);
- }
- }
-
- protected override ConfigurationElement CreateNewElement() {
- return new OperatorElement();
- }
-
- protected override object GetElementKey(ConfigurationElement element) {
- return ((OperatorElement) element).Name;
- }
-
- [TypeConverter(typeof(ListConverter))]
- [ConfigurationProperty("channels", IsRequired = false)]
- public IList Channels {
- get { return (List) this["channels"]; }
- set { this["channels"] = value; }
- }
- }
-}
diff --git a/cmpctircd/Configuration/Options/AdvancedOptions.cs b/cmpctircd/Configuration/Options/AdvancedOptions.cs
new file mode 100644
index 0000000..9f3f6cc
--- /dev/null
+++ b/cmpctircd/Configuration/Options/AdvancedOptions.cs
@@ -0,0 +1,16 @@
+namespace cmpctircd.Configuration.Options {
+ public class AdvancedOptions {
+ public bool ResolveHostnames { get; set; }
+ public bool RequirePongCookie { get; set; }
+ public int PingTimeout { get; set; }
+ public int MaxTargets { get; set; }
+ public CloakOptions Cloak { get; set; }
+ }
+
+ public class CloakOptions {
+ public string Key { get; set; }
+ public string Prefix { get; set; }
+ public int DomainParts { get; set; }
+ public bool Full { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/cmpctircd/Configuration/Options/CModeOptions.cs b/cmpctircd/Configuration/Options/CModeOptions.cs
new file mode 100644
index 0000000..7a5df44
--- /dev/null
+++ b/cmpctircd/Configuration/Options/CModeOptions.cs
@@ -0,0 +1,5 @@
+namespace cmpctircd.Configuration.Options {
+ public class CModeOptions {
+ public ModeElement[] CModes { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/cmpctircd/Configuration/Options/ConfigurationOptions.cs b/cmpctircd/Configuration/Options/ConfigurationOptions.cs
new file mode 100644
index 0000000..9d4ec21
--- /dev/null
+++ b/cmpctircd/Configuration/Options/ConfigurationOptions.cs
@@ -0,0 +1,17 @@
+namespace cmpctircd.Configuration.Options {
+ public class ConfigurationOptions {
+ public string Sid { get; set; }
+ public string Host { get; set; }
+ public string Network { get; set; }
+ public string Description { get; set; }
+ public LoggerElement[] Loggers { get; set; }
+ public ServerElement[] Servers { get; set; }
+ public SocketElement[] Sockets { get; set; }
+ public OperatorElement[] Opers { get; set; }
+ public ModeElement[] CModes { get; set; }
+ public ModeElement[] UModes { get; set; }
+ public TlsOptions Tls { get; set; }
+ public AdvancedOptions Advanced { get; set; }
+ public string[] OperChan { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/cmpctircd/Configuration/Options/LoggerOptions.cs b/cmpctircd/Configuration/Options/LoggerOptions.cs
new file mode 100644
index 0000000..d2774f8
--- /dev/null
+++ b/cmpctircd/Configuration/Options/LoggerOptions.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace cmpctircd.Configuration.Options
+{
+ public class LoggerOptions
+ {
+ public LoggerElement[] Loggers { get; set; }
+ }
+}
diff --git a/cmpctircd/Configuration/Options/OperatorOptions.cs b/cmpctircd/Configuration/Options/OperatorOptions.cs
new file mode 100644
index 0000000..60cb749
--- /dev/null
+++ b/cmpctircd/Configuration/Options/OperatorOptions.cs
@@ -0,0 +1,5 @@
+namespace cmpctircd.Configuration.Options {
+ public class OperatorOptions {
+ public OperatorElement[] Opers { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/cmpctircd/Configuration/Options/ServerOptions.cs b/cmpctircd/Configuration/Options/ServerOptions.cs
new file mode 100644
index 0000000..539f260
--- /dev/null
+++ b/cmpctircd/Configuration/Options/ServerOptions.cs
@@ -0,0 +1,5 @@
+namespace cmpctircd.Configuration.Options {
+ public class ServerOptions {
+ public ServerElement[] Servers { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/cmpctircd/Configuration/Options/SocketOptions.cs b/cmpctircd/Configuration/Options/SocketOptions.cs
new file mode 100644
index 0000000..5c87b70
--- /dev/null
+++ b/cmpctircd/Configuration/Options/SocketOptions.cs
@@ -0,0 +1,5 @@
+namespace cmpctircd.Configuration {
+ public class SocketOptions {
+ public SocketElement[] Sockets { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/cmpctircd/Configuration/Options/TlsOptions.cs b/cmpctircd/Configuration/Options/TlsOptions.cs
new file mode 100644
index 0000000..19de6b5
--- /dev/null
+++ b/cmpctircd/Configuration/Options/TlsOptions.cs
@@ -0,0 +1,6 @@
+namespace cmpctircd.Configuration.Options {
+ public class TlsOptions {
+ public string File { get; set; }
+ public string Password { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/cmpctircd/Configuration/Options/UMoodeOptions.cs b/cmpctircd/Configuration/Options/UMoodeOptions.cs
new file mode 100644
index 0000000..589af55
--- /dev/null
+++ b/cmpctircd/Configuration/Options/UMoodeOptions.cs
@@ -0,0 +1,5 @@
+namespace cmpctircd.Configuration.Options {
+ public class UModeOptions {
+ public ModeElement[] UModes { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/cmpctircd/Configuration/ServerElement.cs b/cmpctircd/Configuration/ServerElement.cs
index 450b64d..cfa851b 100644
--- a/cmpctircd/Configuration/ServerElement.cs
+++ b/cmpctircd/Configuration/ServerElement.cs
@@ -1,64 +1,17 @@
using System.Collections.Generic;
-using System.ComponentModel;
-using System.Configuration;
-namespace cmpctircd.Configuration {
- public class ServerElement : ConfigurationElement {
- [ConfigurationProperty("type", IsRequired = true)]
- public ServerType Type {
- get { return (ServerType) this["type"]; }
- set { this["type"] = value; }
- }
-
- [ConfigurationProperty("host", IsRequired = true)]
- public string Host {
- get { return (string) this["host"]; }
- set { this["host"] = value; }
- }
-
- [TypeConverter(typeof(ListConverter))]
- [ConfigurationProperty("masks", IsRequired = true)]
- public List Masks {
- get { return ((List) this["masks"]); }
- set { this["masks"] = value; }
- }
-
- [ConfigurationProperty("port", IsRequired = true)]
- [IntegerValidator(MinValue = 0, MaxValue = 65535, ExcludeRange = false)]
- public int Port {
- get { return (int) this["port"]; }
- set { this["port"] = value; }
- }
-
- [ConfigurationProperty("password", IsRequired = true)]
- public string Password {
- get { return (string) this["password"]; }
- set { this["password"] = value; }
- }
-
- [ConfigurationProperty("tls", IsRequired = false, DefaultValue = false)]
- public bool IsTls {
- get { return (bool) this["tls"]; }
- set { this["tls"] = value; }
- }
-
- // Outbound server
- [ConfigurationProperty("outbound", IsRequired = false, DefaultValue = false)]
- public bool IsOutbound {
- get { return (bool) this["outbound"]; }
- set { this["outbound"] = value; }
- }
-
- [ConfigurationProperty("destination", IsRequired = false)]
- public string Destination {
- get { return (string) this["destination"]; }
- set { this["destination"] = value; }
- }
-
- [ConfigurationProperty("verify", IsRequired = false, DefaultValue = true)]
- public bool VerifyTlsCert {
- get { return (bool) this["verify"]; }
- set { this["verify"] = value; }
- }
+namespace cmpctircd.Configuration
+{
+ public class ServerElement
+ {
+ public ServerType Type { get; set; }
+ public string Host { get; set; }
+ public List Masks { get; set; }
+ public int Port { get; set; }
+ public string Password { get; set; }
+ public bool Tls { get; set; }
+ public bool Outbound { get; set; }
+ public string Destination { get; set; }
+ public bool VerifyTlsCert { get; set; }
}
-}
+}
\ No newline at end of file
diff --git a/cmpctircd/Configuration/ServerElementCollection.cs b/cmpctircd/Configuration/ServerElementCollection.cs
deleted file mode 100644
index 6ff0ecf..0000000
--- a/cmpctircd/Configuration/ServerElementCollection.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System.Configuration;
-
-namespace cmpctircd.Configuration {
- public class ServerElementCollection : ConfigurationElementCollection {
- public ServerElement this[int i] {
- get { return (ServerElement) BaseGet(i); }
- }
-
- protected override ConfigurationElement CreateNewElement() {
- return new ServerElement();
- }
-
- protected override object GetElementKey(ConfigurationElement element) {
- ServerElement se = (ServerElement) element;
- return se.Host + ":" + se.Port;
- }
- }
-}
diff --git a/cmpctircd/Configuration/SocketElement.cs b/cmpctircd/Configuration/SocketElement.cs
index b86b16d..f8cd5ea 100644
--- a/cmpctircd/Configuration/SocketElement.cs
+++ b/cmpctircd/Configuration/SocketElement.cs
@@ -1,81 +1,38 @@
-using System;
-using System.ComponentModel;
-using System.Configuration;
-using System.Globalization;
+using System.ComponentModel;
using System.Net;
+using System.Text.Json.Serialization;
-namespace cmpctircd.Configuration {
- public class SocketElement : ConfigurationElement {
- [ConfigurationProperty("type", IsRequired = true)]
- public ListenerType Type {
- get { return (ListenerType) this["type"]; }
- set { this["type"] = value; }
- }
+namespace cmpctircd.Configuration
+{
+ public class SocketElement
+ {
+ public ListenerType Type { get; set; }
- [TypeConverter(typeof(IPAddressConverter))]
- [ConfigurationProperty("host", IsRequired = true)]
- public IPAddress Host {
- get { return (IPAddress) this["host"]; }
- set { this["host"] = value; }
- }
+ public string Host { get; set; }
- [ConfigurationProperty("port", IsRequired = true)]
- [IntegerValidator(MinValue = 0, MaxValue = 65535, ExcludeRange = false)]
- public int Port {
- get { return (int) this["port"]; }
- set { this["port"] = value; }
- }
+ public int Port { get; set; }
- public IPEndPoint EndPoint {
- get { return new IPEndPoint(Host, Port); }
- }
+ public bool Tls { get; set; }
- [ConfigurationProperty("tls", IsRequired = false, DefaultValue = false)]
- public bool IsTls {
- get { return (bool) this["tls"]; }
- set { this["tls"] = value; }
- }
+ public ServerType Protocol { get; set; }
- [ConfigurationProperty("protocol", IsRequired = false)]
- public ServerType Protocol {
- get { return (ServerType) this["protocol"]; }
- set { this["protocol"] = value; }
- }
+ public IPEndPoint EndPoint => new IPEndPoint(IPAddress.Parse(Host), Port);
- public static implicit operator SocketElement(ServerElement serverElement) {
+ public static implicit operator SocketElement(ServerElement serverElement)
+ {
var se = new SocketElement();
// Check if this is a DNS name
// If it is, resolve it
- if (!IPAddress.TryParse(serverElement.Destination, out var IP)) {
+ if (!IPAddress.TryParse(serverElement.Destination, out var IP))
IP = Dns.GetHostEntry(serverElement.Destination).AddressList[0];
- }
- se.Host = IP;
+ se.Host = IP.ToString();
se.Port = serverElement.Port;
- se.IsTls = serverElement.IsTls;
+ se.Tls = serverElement.Tls;
se.Type = ListenerType.Server;
return se;
}
}
-
- [TypeConverter(typeof(IPAddressConverter))]
- class IPAddressConverter : TypeConverter {
- public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
- return sourceType == typeof(string);
- }
-
- public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
- return IPAddress.Parse((string) value);
- }
-
- public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
- return destinationType == typeof(string);
- }
-
- public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
- return ((IPAddress) value).ToString();
- }
- }
-}
+}
\ No newline at end of file
diff --git a/cmpctircd/Configuration/SocketElementCollection.cs b/cmpctircd/Configuration/SocketElementCollection.cs
deleted file mode 100644
index 88fbbac..0000000
--- a/cmpctircd/Configuration/SocketElementCollection.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.Configuration;
-
-namespace cmpctircd.Configuration {
- public class SocketElementCollection : ConfigurationElementCollection {
- public SocketElement this[int i] {
- get {
- return (SocketElement) BaseGet(i);
- }
- }
-
- protected override ConfigurationElement CreateNewElement() {
- return new SocketElement();
- }
-
- protected override object GetElementKey(ConfigurationElement element) {
- return ((SocketElement) element).EndPoint;
- }
- }
-}
diff --git a/cmpctircd/Configuration/TlsElement.cs b/cmpctircd/Configuration/TlsElement.cs
deleted file mode 100644
index 72b8247..0000000
--- a/cmpctircd/Configuration/TlsElement.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System.Configuration;
-
-namespace cmpctircd.Configuration {
- public class TlsElement : ConfigurationElement {
- [ConfigurationProperty("file", IsRequired = true)]
- public string File {
- get { return (string) this["file"]; }
- set { this["file"] = value; }
- }
-
- [ConfigurationProperty("password", IsRequired = false, DefaultValue = "")]
- public string Password {
- get { return (string) this["password"]; }
- set { this["password"] = value; }
- }
- }
-}
diff --git a/cmpctircd/Controllers/OperController.cs b/cmpctircd/Controllers/OperController.cs
index 9cc404b..4454d80 100644
--- a/cmpctircd/Controllers/OperController.cs
+++ b/cmpctircd/Controllers/OperController.cs
@@ -49,12 +49,12 @@ public bool OperHandler(HandlerArgs args) {
}
// Instantiate the algorithm through reflection, if not already instantiated.
HashAlgorithm algorithm;
- if(!_algorithms.TryGetValue(ircop.Algorithm, out algorithm))
- algorithm = _algorithms[ircop.Algorithm] = (ircop.Algorithm.GetConstructor(Type.EmptyTypes)?.Invoke(new object[0]) as HashAlgorithm) ?? throw new IrcErrNoOperHostException(client);
+ if(!_algorithms.TryGetValue(ircop.AlgorithmType, out algorithm))
+ algorithm = _algorithms[ircop.AlgorithmType] = (ircop.AlgorithmType.GetConstructor(Type.EmptyTypes)?.Invoke(new object[0]) as HashAlgorithm) ?? throw new IrcErrNoOperHostException(client);
// Hash the user's input to match the stored hash password in the config
byte[] password = Encoding.UTF8.GetBytes(args.SpacedArgs[2]);
byte[] builtHash = algorithm.ComputeHash(password);
- if(builtHash.SequenceEqual(ircop.Password)) {
+ if(builtHash.SequenceEqual(ircop.PasswordArray)) {
Channel channel;
Topic topic;
client.Modes["o"].Grant("", true, true);
diff --git a/cmpctircd/IRCd.cs b/cmpctircd/IRCd.cs
index 41f2bd6..cd34d68 100644
--- a/cmpctircd/IRCd.cs
+++ b/cmpctircd/IRCd.cs
@@ -1,16 +1,62 @@
-namespace cmpctircd {
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Text.RegularExpressions;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Timers;
+using cmpctircd.Configuration;
+using cmpctircd.Configuration.Options;
+using cmpctircd.Modes;
+using cmpctircd.Validation;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Options;
+
+namespace cmpctircd
+{
+ public class IRCd
+ {
+ public const string Version = "0.2.1-dev";
+ public static char[] lastUID = { };
+ public readonly IList Connectors = new List();
+ private readonly IList Listeners = new List();
- using cmpctircd.Configuration;
- using cmpctircd.Modes;
+ public IRCd(Log log, IServiceProvider services, IOptions config)
+ {
+ Log = log;
+ Config = config;
+
+ // Interpret the ConfigData
+ SID = config.Value.Sid;
+ Host = config.Value.Host;
+ Desc = config.Value.Description;
+ Network = config.Value.Network;
+
+ if (SID == "auto") SID = GenerateSID(Host, Desc);
+
+ PingTimeout = config.Value.Advanced.PingTimeout;
+ RequirePong = config.Value.Advanced.RequirePongCookie;
+
+ Loggers = config.Value.Loggers;
+
+ MaxTargets = config.Value.Advanced.MaxTargets;
+ CloakKey = config.Value.Advanced.Cloak.Key;
+ CloakFull = config.Value.Advanced.Cloak.Full;
+ CloakPrefix = config.Value.Advanced.Cloak.Prefix;
+ CloakDomainParts = config.Value.Advanced.Cloak.DomainParts;
+ AutoModes = config.Value.CModes.ToDictionary(m => m.Name, m => m.Param);
+ AutoUModes = config.Value.UModes.ToDictionary(m => m.Name, m => m.Param);
+ Opers = config.Value.Opers;
+ OperChan = config.Value.OperChan;
+
+ PacketManager = new PacketManager(this, services);
+ ChannelManager = new ChannelManager(this);
+
+ // Create certificate refresh
+ if (config.Value.Tls != null)
+ Certificate =
+ new AutomaticCertificateCacheRefresh(new FileInfo(config.Value.Tls.File), password: config.Value.Tls.Password);
+ }
- public class IRCd {
- private readonly IList Listeners = new List();
- public readonly IList Connectors = new List();
public PacketManager PacketManager { get; }
public ChannelManager ChannelManager { get; }
public IList> ClientLists { get; } = new List>();
@@ -20,12 +66,12 @@ public class IRCd {
private IList UserModes { get; set; }
public Log Log { get; }
- public CmpctConfigurationSection Config { get; }
+ public IOptions Config { get; }
+ public IOptions SocketOptions { get; }
public string SID { get; }
public string Host { get; }
public string Desc { get; }
public string Network { get; }
- public const string Version = "0.2.1-dev";
public int MaxTargets { get; }
public int MaxSeen { get; set; } = 0;
public bool RequirePong { get; }
@@ -42,7 +88,6 @@ public class IRCd {
public IList Opers { get; }
public IList OperChan { get; }
public DateTime CreateTime { get; private set; }
- public static char[] lastUID = new char[] { };
public AutomaticFileCacheRefresh MOTD { get; } = new AutomaticFileCacheRefresh(new FileInfo("ircd.motd"));
public AutomaticFileCacheRefresh Rules { get; } = new AutomaticFileCacheRefresh(new FileInfo("ircd.rules"));
@@ -50,243 +95,246 @@ public class IRCd {
public List Clients => ClientLists.SelectMany(clientList => clientList).ToList();
public List Servers => ServerLists.SelectMany(serverList => serverList).ToList();
- public IRCd(Log log, CmpctConfigurationSection config, IServiceProvider services) {
- this.Log = log;
- this.Config = config;
- // Interpret the ConfigData
- SID = config.SID;
- Host = config.Host;
- Desc = config.Description;
- Network = config.Network;
+ public void Run()
+ {
+ Log.Info("==> Validating appsettings.json");
+ var configurationValidator = new ConfigurationOptionsValidator();
+ var validationResult = configurationValidator.Validate(Config);
- if (SID == "auto") {
- SID = IRCd.GenerateSID(Host, Desc);
+ if (!validationResult.IsValid) {
+ Log.Error($"==> {string.Join("\n", validationResult.Errors.Select(e => e.ErrorMessage))}");
+ return;
}
- PingTimeout = config.Advanced.PingTimeout;
- RequirePong = config.Advanced.RequirePongCookie;
-
- Loggers = config.Loggers.OfType().ToList();
-
- MaxTargets = config.Advanced.MaxTargets;
- CloakKey = config.Advanced.Cloak.Key;
- CloakFull = config.Advanced.Cloak.Full;
- CloakPrefix = config.Advanced.Cloak.Prefix;
- CloakDomainParts = config.Advanced.Cloak.DomainParts;
- AutoModes = config.AutomaticModes.OfType().ToDictionary(m => m.Name, m => m.Param);
- AutoUModes = config.AutomaticUserModes.OfType().ToDictionary(m => m.Name, m => m.Param);
- Opers = config.Operators.OfType().ToList();
- OperChan = config.Operators.Channels;
-
- PacketManager = new PacketManager(this, services);
- ChannelManager = new ChannelManager(this);
-
- // Create certificate refresh
- if(config.Tls != null)
- Certificate = new AutomaticCertificateCacheRefresh(new FileInfo(config.Tls.File), password: Config.Tls.Password);
- }
-
- public void Run() {
Log.Info($"==> Starting cmpctircd-{Version}");
- if(Version.Contains("-dev")) {
+ if (Version.Contains("-dev"))
+ {
Log.Info("===> You are running a development version of cmpctircd.NET.");
Log.Info("===> If you are having problems, consider reverting to a stable version.");
- Log.Info("===> Please report any bugs or feedback to the developers via the bugtracker at https://bugs.cmpct.info/");
+ Log.Info(
+ "===> Please report any bugs or feedback to the developers via the bugtracker at https://bugs.cmpct.info/");
}
+
Log.Info($"==> Host: {Host}");
PacketManager.Load();
- foreach(var listener in Config.Sockets.OfType()) {
- SocketListener sl = new SocketListener(this, listener);
- Log.Info($"==> Listening on: {listener.Host}:{listener.Port} ({listener.Type}) ({(listener.IsTls ? "TLS" : "Plain" )})");
+ var sockets = Config.Value.Sockets;
+ foreach (var listener in sockets)
+ {
+ var sl = new SocketListener(this, listener);
+ Log.Info(
+ $"==> Listening on: {listener.Host}:{listener.Port} ({listener.Type}) ({(listener.Tls ? "TLS" : "Plain")})");
Listeners.Add(sl);
sl.Bind();
}
- foreach (var server in Config.Servers.OfType()) {
- if (server.IsOutbound) {
+ foreach (var server in Config.Value.Servers)
+ if (server.Outbound)
+ {
// tag with outbound="true"
// We want to connect out to this server, not have them connect to us
var sc = new SocketConnector(this, server);
- Log.Info($"==> Connecting to: {server.Destination}:{server.Port} ({server.Host}) ({(server.IsTls ? "TLS" : "Plain" )})");
+ Log.Info(
+ $"==> Connecting to: {server.Destination}:{server.Port} ({server.Host}) ({(server.Tls ? "TLS" : "Plain")})");
Connectors.Add(sc);
sc.Connect();
}
- }
// Set create time
CreateTime = DateTime.UtcNow;
- try {
+ try
+ {
// HACK: You can't use await in async
Listeners.ForEach(listener => listener.ListenToClients());
- } catch {
+ }
+ catch
+ {
Log.Error("Got an exception: shutting down all listeners");
Listeners.ForEach(listener => listener.Stop());
}
- if(Log.ShouldLogLevel(LogType.Debug)) {
- System.Timers.Timer statTimer = new System.Timers.Timer();
+ if (Log.ShouldLogLevel(LogType.Debug))
+ {
+ var statTimer = new Timer();
// Run the timer every 5 minutes
statTimer.Interval = TimeSpan.FromMinutes(5).TotalMilliseconds;
- statTimer.Elapsed += delegate {
+ statTimer.Elapsed += delegate
+ {
// Create a report on how each of the listeners is preforming (ratio of authenticated clients)
// Should let us see if there's some problem with a specific listener - or in general with the handshake
Log.Debug($"Since {CreateTime}, the following activity has occurred:");
- foreach(var listener in this.Listeners) {
- if(listener.ClientCount == 0) continue;
+ foreach (var listener in Listeners)
+ {
+ if (listener.ClientCount == 0) continue;
- var authRatio = decimal.Round(((decimal) listener.AuthClientCount / (decimal) listener.ClientCount) * 100);
+ var authRatio = decimal.Round(listener.AuthClientCount / (decimal) listener.ClientCount * 100);
var unauthCount = listener.ClientCount - listener.AuthClientCount;
- var unauthRatio = decimal.Round(((decimal) unauthCount / (decimal) listener.ClientCount) * 100);
- var prefixLine = $"[{listener.Info.Host}:{listener.Info.Port} ({listener.Info.Type}) ({(listener.Info.IsTls ? "SSL/TLS" : "Plain" )})]";
+ var unauthRatio = decimal.Round(unauthCount / (decimal) listener.ClientCount * 100);
+ var prefixLine =
+ $"[{listener.Info.Host}:{listener.Info.Port} ({listener.Info.Type}) ({(listener.Info.Tls ? "SSL/TLS" : "Plain")})]";
- Log.Debug($"==> {prefixLine} Authed: {listener.AuthClientCount} ({authRatio}%). Unauthed: {unauthCount} ({unauthRatio}%). Total: {listener.ClientCount}.");
+ Log.Debug(
+ $"==> {prefixLine} Authed: {listener.AuthClientCount} ({authRatio}%). Unauthed: {unauthCount} ({unauthRatio}%). Total: {listener.ClientCount}.");
}
};
statTimer.Start();
}
}
- public void Stop() {
+ public void Stop()
+ {
// TODO: Do other things?
Listeners.ForEach(listener => listener.Stop());
}
- public void WriteToAllServers(string message, List except = null) {
- foreach(List servers in ServerLists) {
- foreach(Server server in servers) {
- if(except != null && except.Contains(server)) {
- // Skip a specified server
- continue;
- }
- server.Write(message + "\r\n");
- }
+ public void WriteToAllServers(string message, List except = null)
+ {
+ foreach (List servers in ServerLists)
+ foreach (var server in servers)
+ {
+ if (except != null && except.Contains(server)) // Skip a specified server
+ continue;
+ server.Write(message + "\r\n");
}
}
- public Client GetClientByNick(String nick) {
- foreach(var client in Clients) {
+ public Client GetClientByNick(string nick)
+ {
+ foreach (var client in Clients)
+ {
// User may not have a nick yet
- if (String.IsNullOrEmpty(client.Nick)) continue;
+ if (string.IsNullOrEmpty(client.Nick)) continue;
// Check if user has the nick we're looking for
- if (client.Nick.Equals(nick, StringComparison.OrdinalIgnoreCase)) {
- return client;
- }
+ if (client.Nick.Equals(nick, StringComparison.OrdinalIgnoreCase)) return client;
}
+
throw new InvalidOperationException("No such user exists");
}
- public Client GetClientByUUID(String UUID) {
- try {
+ public Client GetClientByUUID(string UUID)
+ {
+ try
+ {
return Clients.Single(client => client.UUID == UUID);
- } catch(InvalidOperationException) {
+ }
+ catch (InvalidOperationException)
+ {
throw new InvalidOperationException("No such user exists");
}
}
- public Server GetServerBySID(String SID) {
- foreach (var serverList in ServerLists) {
- try {
+ public Server GetServerBySID(string SID)
+ {
+ foreach (var serverList in ServerLists)
+ try
+ {
return serverList.Single(server => server.SID == SID);
- } catch (Exception) { }
- }
+ }
+ catch (Exception)
+ {
+ }
+
throw new InvalidOperationException("No such server exists");
}
- public IDictionary GetSupportedModes(bool requireSymbols) {
- if(ModeDict != null && ModeDict.Any()) {
- // Caching because this is still a relatively expensive operation to perform on each connection
+ public IDictionary GetSupportedModes(bool requireSymbols)
+ {
+ if (ModeDict != null && ModeDict.Any()
+ ) // Caching because this is still a relatively expensive operation to perform on each connection
// (GetSupportedModesByType() is likely far more expensive given it uses reflection)
// This is called by SendWelcome() to provide RPL_ISUPPORT
return ModeDict;
- }
ModeDict = new Dictionary();
var chan = new Channel(ChannelManager, this);
- foreach(var modeList in ModeTypes) {
- foreach(var mode in modeList.Value) {
- var modeObject = chan.Modes[mode];
-
- // TODO: Are two different caches needed?
- if(requireSymbols && String.IsNullOrEmpty(modeObject.Symbol)) continue;
- ModeDict.Add(modeObject.Character, modeObject.Symbol);
- }
+ foreach (var modeList in ModeTypes)
+ foreach (var mode in modeList.Value)
+ {
+ var modeObject = chan.Modes[mode];
+
+ // TODO: Are two different caches needed?
+ if (requireSymbols && string.IsNullOrEmpty(modeObject.Symbol)) continue;
+ ModeDict.Add(modeObject.Character, modeObject.Symbol);
}
- var modeCharacters = String.Join("", ModeDict.Select(p => p.Key));
- var modeSymbols = String.Join("", ModeDict.Select(p => p.Value));
+ var modeCharacters = string.Join("", ModeDict.Select(p => p.Key));
+ var modeSymbols = string.Join("", ModeDict.Select(p => p.Value));
ModeDict.Add("Characters", modeCharacters);
ModeDict.Add("Symbols", modeSymbols);
return ModeDict;
}
- public IList GetSupportedUModes(Client client) {
- if(UserModes != null && UserModes.Any()) {
- // Caching because reflection is an expensive operation to perform on each connection
+ public IList GetSupportedUModes(Client client)
+ {
+ if (UserModes != null && UserModes.Any()
+ ) // Caching because reflection is an expensive operation to perform on each connection
// This is called by SendWelcome() to provide RPL_MYINFO
return UserModes;
- }
UserModes = new List();
- string[] badClasses = { "Mode", "ModeType" };
+ string[] badClasses = {"Mode", "ModeType"};
var classes = AppDomain.CurrentDomain.GetAssemblies()
- .SelectMany(t => t.GetTypes())
- .Where(
- t => t.IsClass &&
- t.Namespace == "cmpctircd.Modes" &&
- t.BaseType.Equals(typeof(UserMode)) &&
- !badClasses.Contains(t.Name)
- );
-
- foreach(Type className in classes) {
- UserMode modeInstance = (UserMode) Activator.CreateInstance(Type.GetType(className.ToString()), client);
+ .SelectMany(t => t.GetTypes())
+ .Where(
+ t => t.IsClass &&
+ t.Namespace == "cmpctircd.Modes" &&
+ t.BaseType.Equals(typeof(UserMode)) &&
+ !badClasses.Contains(t.Name)
+ );
+
+ foreach (var className in classes)
+ {
+ var modeInstance = (UserMode) Activator.CreateInstance(Type.GetType(className.ToString()), client);
UserModes.Add(modeInstance.Character);
}
return UserModes;
}
- public IDictionary> GetSupportedModesByType() {
- if(ModeTypes != null && ModeTypes.Any()) {
- // Caching to only generate this list once - reflection is expensive
+ public IDictionary> GetSupportedModesByType()
+ {
+ if (ModeTypes != null && ModeTypes.Any()
+ ) // Caching to only generate this list once - reflection is expensive
return ModeTypes;
- }
ModeTypes = new Dictionary>();
// http://www.irc.org/tech_docs/005.html
- List typeA = new List();
- List typeB = new List();
- List typeC = new List();
- List typeD = new List();
- List typeNone = new List();
+ var typeA = new List();
+ var typeB = new List();
+ var typeC = new List();
+ var typeD = new List();
+ var typeNone = new List();
- string[] badClasses = { "Mode", "ModeType" };
+ string[] badClasses = {"Mode", "ModeType"};
var classes = AppDomain.CurrentDomain.GetAssemblies()
- .SelectMany(t => t.GetTypes())
- .Where(
- t => t.IsClass &&
- t.Namespace == "cmpctircd.Modes" &&
- t.BaseType.Equals(typeof(ChannelMode)) &&
- !badClasses.Contains(t.Name)
- );
-
- foreach(Type className in classes) {
- ChannelMode modeInstance = (ChannelMode) Activator.CreateInstance(Type.GetType(className.ToString()), new Channel(ChannelManager, this));
- ChannelModeType type = modeInstance.Type;
- string modeChar = modeInstance.Character;
-
- switch(type) {
+ .SelectMany(t => t.GetTypes())
+ .Where(
+ t => t.IsClass &&
+ t.Namespace == "cmpctircd.Modes" &&
+ t.BaseType.Equals(typeof(ChannelMode)) &&
+ !badClasses.Contains(t.Name)
+ );
+
+ foreach (var className in classes)
+ {
+ var modeInstance = (ChannelMode) Activator.CreateInstance(Type.GetType(className.ToString()),
+ new Channel(ChannelManager, this));
+ var type = modeInstance.Type;
+ var modeChar = modeInstance.Character;
+
+ switch (type)
+ {
case ChannelModeType.A:
typeA.Add(modeChar);
break;
@@ -316,68 +364,76 @@ public IDictionary> GetSupportedModesByType() {
// UID <-> Nick translation helpers
- public string GenerateUID() {
+ public string GenerateUID()
+ {
var UID = new char[6];
var highestUid = 6 * 90; // Sum of 6(Z)
var aCharacter = Convert.ToChar(65); // A
var zCharacter = Convert.ToChar(90); // Z
- if (new String(lastUID) == "" || UID.Sum(character => Convert.ToInt32(character)) == highestUid) {
- // We're at the start or we've hit the maximum possible ID (ZZZZZZ)
+ if (new string(lastUID) == "" || UID.Sum(character => Convert.ToInt32(character)) == highestUid
+ ) // We're at the start or we've hit the maximum possible ID (ZZZZZZ)
// Start (again)...
- UID = new char[] { 'A', 'A', 'A', 'A', 'A', 'A' };
- } else {
- for (int i = UID.Length - 1; i >= 0; i--) {
+ UID = new[] {'A', 'A', 'A', 'A', 'A', 'A'};
+ else
+ for (var i = UID.Length - 1; i >= 0; i--)
+ {
// We need to increment every index, starting at UID[5] (6) until it reaches Z
// Once it reaches Z (this will depend on subsequent UIDs), hop to the next column and repeat
// If this column of the old UID was a Z, don't increment it
// Copy it over and work on the next column
- if (lastUID[i] == zCharacter) {
+ if (lastUID[i] == zCharacter)
+ {
UID[i] = lastUID[i];
continue;
}
// Add one to the column if we're at the start
- if (i == UID.Length - 1) {
+ if (i == UID.Length - 1)
+ {
UID[i] = Convert.ToChar(lastUID[i] + 1);
- } else if(lastUID[i + 1] == zCharacter) {
+ }
+ else if (lastUID[i + 1] == zCharacter)
+ {
// Add one to the column if the previous column is Z
UID[i] = Convert.ToChar(lastUID[i] + 1);
// Once we've added one to THIS column, reset the one over to an A
- if (lastUID[i + 1] == zCharacter) {
- // If the next character over is a Z, change it to an A when we bump the next column
+ if (lastUID[i + 1] == zCharacter
+ ) // If the next character over is a Z, change it to an A when we bump the next column
// e.g. if lastUID is AAAAAZ, make next AAAABA
UID[i + 1] = Convert.ToChar(aCharacter);
- }
- } else {
+ }
+ else
+ {
// Otherwise just copy that value
// e.g. with AAAAAB -> AAAAAC, only the B -> C has changed, so rest can be copied
UID[i] = lastUID[i];
}
}
- }
// Don't allow this UID to be generated again...
lastUID = UID;
- var string_UID = new String(UID);
+ var string_UID = new string(UID);
Log.Debug($"Generated a UID: {string_UID}");
return string_UID;
-
}
- public bool IsUUID(string message) {
+ public bool IsUUID(string message)
+ {
return Regex.IsMatch(message, "^[0-9][A-Z0-9][A-Z0-9][A-Z][A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9]$");
}
// Works on nick or (U)UID
- public string ExtractIdentifierFromMessage(string message, bool split = false) {
+ public string ExtractIdentifierFromMessage(string message, bool split = false)
+ {
var identifier = message;
- if(split) {
- var message_split = message.Split(new string[] { " " }, StringSplitOptions.None);
+ if (split)
+ {
+ var message_split = message.Split(new[] {" "}, StringSplitOptions.None);
identifier = message_split[0];
}
@@ -389,11 +445,14 @@ public string ExtractIdentifierFromMessage(string message, bool split = false) {
}
// todo: UID -> UUID rename
- public string ReplaceUUIDWithNick(string message, int index = 0) {
- var split_message = message.Split(new string[] { " " }, StringSplitOptions.None);
- if(IsUUID(split_message[index].Replace(":", ""))) {
+ public string ReplaceUUIDWithNick(string message, int index = 0)
+ {
+ var split_message = message.Split(new[] {" "}, StringSplitOptions.None);
+ if (IsUUID(split_message[index].Replace(":", "")))
+ {
split_message[index] = ExtractIdentifierFromMessage(split_message[index]);
- if (split_message[index] != Host) {
+ if (split_message[index] != Host)
+ {
Log.Debug($"Looking for client with UUID (want their nick): {split_message[index]}");
var client = GetClientByUUID(split_message[index]);
@@ -401,14 +460,18 @@ public string ReplaceUUIDWithNick(string message, int index = 0) {
// TODO exception if non existent?
}
}
- return String.Join(" ", split_message);
+
+ return string.Join(" ", split_message);
}
- public string ReplaceNickWithUUID(string message, int index = 0) {
- var split_message = message.Split(new string[] { " " }, StringSplitOptions.None);
- if (!IsUUID(split_message[index])) {
+ public string ReplaceNickWithUUID(string message, int index = 0)
+ {
+ var split_message = message.Split(new[] {" "}, StringSplitOptions.None);
+ if (!IsUUID(split_message[index]))
+ {
split_message[index] = ExtractIdentifierFromMessage(split_message[index]);
- if (split_message[index] != Host) {
+ if (split_message[index] != Host)
+ {
Log.Debug($"Looking for client with nick (want their UUID): {split_message[index]}");
var client = GetClientByNick(split_message[index]);
@@ -416,25 +479,23 @@ public string ReplaceNickWithUUID(string message, int index = 0) {
// TODO exception if non existent?
}
}
- return String.Join(" ", split_message);
+
+ return string.Join(" ", split_message);
}
// SID
- public static string GenerateSID(string name, string description) {
+ public static string GenerateSID(string name, string description)
+ {
// http://www.inspircd.org/wiki/Modules/spanningtree/UUIDs.html
var SID = 0;
- for(int i = 0; i < name.Length; i++) {
- SID = (5 * SID) + Convert.ToInt32(name[i]);
- }
+ for (var i = 0; i < name.Length; i++) SID = 5 * SID + Convert.ToInt32(name[i]);
- for(int n = 0; n < description.Length; n++) {
- SID = (5 * SID) + Convert.ToInt32(description[n]);
- }
+ for (var n = 0; n < description.Length; n++) SID = 5 * SID + Convert.ToInt32(description[n]);
SID = SID % 999;
return SID.ToString("000");
}
}
-}
+}
\ No newline at end of file
diff --git a/cmpctircd/IrcApplicationLifecycle.cs b/cmpctircd/IrcApplicationLifecycle.cs
index b1387af..1fe1c44 100644
--- a/cmpctircd/IrcApplicationLifecycle.cs
+++ b/cmpctircd/IrcApplicationLifecycle.cs
@@ -1,4 +1,9 @@
-namespace cmpctircd {
+using System.Collections.Generic;
+using cmpctircd.Configuration.Options;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Options;
+
+namespace cmpctircd {
using System;
using System.Linq;
using System.Threading;
@@ -10,11 +15,13 @@
public class IrcApplicationLifecycle : IHostedService {
private readonly IRCd ircd;
private readonly Log log;
- private readonly CmpctConfigurationSection config;
+ private readonly IConfiguration config;
private readonly IHostApplicationLifetime appLifetime;
private QueuedSynchronizationContext synchronizationContext;
+ private IOptions _loggerOptions;
- public IrcApplicationLifecycle(IRCd ircd, Log log, CmpctConfigurationSection config, IHostApplicationLifetime appLifetime) {
+ public IrcApplicationLifecycle(IRCd ircd, Log log, IConfiguration config, IHostApplicationLifetime appLifetime, IOptions loggerOptions) {
+ _loggerOptions = loggerOptions;
this.ircd = ircd ?? throw new ArgumentNullException(nameof(ircd));
this.log = log ?? throw new ArgumentNullException(nameof(log));
this.config = config ?? throw new ArgumentNullException(nameof(config));
@@ -36,7 +43,7 @@ public Task StopAsync(CancellationToken cancellationToken) {
private void OnStarted() {
synchronizationContext = new QueuedSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(synchronizationContext);
- log.Initialise(ircd, config.Loggers.OfType().ToList());
+ log.Initialise(ircd, _loggerOptions.Value.Loggers.ToList());
ircd.Run();
synchronizationContext.Run();
}
diff --git a/cmpctircd/Log/Log.cs b/cmpctircd/Log/Log.cs
index a995af0..39a412a 100644
--- a/cmpctircd/Log/Log.cs
+++ b/cmpctircd/Log/Log.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
namespace cmpctircd {
public class Log {
diff --git a/cmpctircd/PacketManager.cs b/cmpctircd/PacketManager.cs
index 902e176..400348e 100644
--- a/cmpctircd/PacketManager.cs
+++ b/cmpctircd/PacketManager.cs
@@ -5,6 +5,7 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
+using Microsoft.Extensions.Configuration;
namespace cmpctircd {
public class PacketManager {
@@ -65,7 +66,7 @@ public bool Handle(String packet, IRCd ircd, object sender, HandlerArgs args, Li
try {
// Restrict the commands which non-registered (i.e. pre PONG, pre USER/NICK) users can execute
- if ((client.State.Equals(ClientState.PreAuth) || (ircd.Config.Advanced.ResolveHostnames && client.ResolvingHost)) && !registrationCommands.Contains(packet.ToUpper())) {
+ if ((client.State.Equals(ClientState.PreAuth) || (ircd.Config.Value.Advanced.ResolveHostnames && client.ResolvingHost)) && !registrationCommands.Contains(packet.ToUpper())) {
throw new IrcErrNotRegisteredException(client);
}
diff --git a/cmpctircd/Program.cs b/cmpctircd/Program.cs
index 5d9c0af..913391b 100644
--- a/cmpctircd/Program.cs
+++ b/cmpctircd/Program.cs
@@ -1,35 +1,51 @@
-namespace cmpctircd {
- using System;
- using System.Linq;
- using cmpctircd.Configuration;
- using cmpctircd.Controllers;
- using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.Hosting;
+using System;
+using System.IO;
+using System.Linq;
+using cmpctircd.Configuration;
+using cmpctircd.Configuration.Options;
+using cmpctircd.Controllers;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
- class Program {
- static void Main(string[] args) {
+namespace cmpctircd
+{
+ internal class Program
+ {
+ private static void Main(string[] args)
+ {
CreateHostBuilder(args)
.Build().Run();
}
- static IHostBuilder CreateHostBuilder(string[] args) {
+ private static IHostBuilder CreateHostBuilder(string[] args)
+ {
return Host.CreateDefaultBuilder(args)
- .ConfigureServices((hostContext, services) => {
+ .ConfigureServices((hostContext, services) =>
+ {
var log = new Log();
- var config = CmpctConfigurationSection.GetConfiguration();
+ var configuration = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetParent(AppContext.BaseDirectory).FullName)
+ .AddJsonFile("appsettings.json", false)
+ .Build();
- foreach (var controllerType in AppDomain.CurrentDomain.GetAssemblies().SelectMany(t => t.GetTypes()).Where(t => !t.IsAbstract && typeof(ControllerBase).IsAssignableFrom(t))) {
+ foreach (var controllerType in AppDomain.CurrentDomain.GetAssemblies().SelectMany(t => t.GetTypes())
+ .Where(t => !t.IsAbstract && typeof(ControllerBase).IsAssignableFrom(t)))
services.AddTransient(controllerType);
- }
- services.AddSingleton(config);
+ services.AddSingleton(configuration);
services.AddSingleton(log);
services.AddSingleton();
services.AddScoped();
services.AddScoped(sp => sp.GetRequiredService().Sender as Client);
services.AddScoped(sp => sp.GetRequiredService().Sender as Server);
services.AddHostedService();
+
+ services.AddOptions().Bind(configuration);
+ services.AddOptions().Bind(configuration);
+ services.AddOptions().Bind(configuration);
+ services.Configure(configuration);
});
}
}
- }
+}
\ No newline at end of file
diff --git a/cmpctircd/Server/Server.cs b/cmpctircd/Server/Server.cs
index 1480d6d..4d486d3 100644
--- a/cmpctircd/Server/Server.cs
+++ b/cmpctircd/Server/Server.cs
@@ -1,10 +1,12 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using cmpctircd.Configuration;
+using Microsoft.Extensions.Configuration;
namespace cmpctircd {
public class Server : SocketBase {
@@ -136,14 +138,14 @@ public bool FindServerConfig(string hostname, string password) {
link = ServerInfo;
if (link.Host == hostname && link.Port == Listener.Info.Port
- && link.IsTls == Listener.Info.IsTls && link.Password == password) {
+ && link.Tls == Listener.Info.Tls && link.Password == password) {
foundMatch = true;
}
} else {
// Find matching tag in config (or null)
- link = IRCd.Config.Servers.Cast().Where(s => s.Host == hostname
+ link = IRCd.Config.Value.Servers.Where(s => s.Host == hostname
&& s.Port == Listener.Info.Port
- && s.IsTls == Listener.Info.IsTls
+ && s.Tls == Listener.Info.Tls
&& s.Password == password).FirstOrDefault();
// IP address needed for the block
diff --git a/cmpctircd/SocketConnector.cs b/cmpctircd/SocketConnector.cs
index 7c252e9..e12df42 100644
--- a/cmpctircd/SocketConnector.cs
+++ b/cmpctircd/SocketConnector.cs
@@ -35,14 +35,14 @@ public async Task Connect() {
tc = new TcpClient();
try {
- await tc.ConnectAsync(Info.Host.ToString(), Info.Port);
+ await tc.ConnectAsync(Info.Host, Info.Port);
stream = tc.GetStream();
} catch (SocketException) {
- _ircd.Log.Warn($"Unable to connect to server {Info.Host.ToString()}:{Info.Port}");
+ _ircd.Log.Warn($"Unable to connect to server {Info.Host}:{Info.Port}");
return;
}
- if (ServerInfo.IsTls) {
+ if (ServerInfo.Tls) {
// If we're TLS, we need to handshake immediately
stream = await HandshakeTlsAsClient(tc, ServerInfo.Host, ServerInfo.VerifyTlsCert);
}
diff --git a/cmpctircd/SocketListener.cs b/cmpctircd/SocketListener.cs
index 19bdfa6..3ee6b70 100644
--- a/cmpctircd/SocketListener.cs
+++ b/cmpctircd/SocketListener.cs
@@ -4,6 +4,7 @@
using System.Threading.Tasks;
using System.IO;
+using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
@@ -28,7 +29,7 @@ public class SocketListener {
public SocketListener(IRCd ircd, SocketElement info) {
this._ircd = ircd;
this.Info = info;
- _listener = new TcpListener(info.Host, info.Port);
+ _listener = new TcpListener(info.EndPoint.Address, info.Port);
_ircd.ClientLists.Add(Clients);
_ircd.ServerLists.Add(_servers);
}
@@ -43,7 +44,7 @@ public virtual void Bind() {
}
public virtual void Stop() {
if (_started) {
- _ircd.Log.Debug($"Shutting down listener [IP: {Info.Host}, Port: {Info.Port}, TLS: {Info.IsTls}]");
+ _ircd.Log.Debug($"Shutting down listener [IP: {Info.Host}, Port: {Info.Port}, TLS: {Info.Tls}]");
_listener.Stop();
_started = false;
}
@@ -68,7 +69,7 @@ public async Task ListenToClients() {
protected async Task HandshakeIfNeededAsync(TcpClient tc, Stream stream) {
// Handshake with TLS if they're from a TLS port
- if (Info.IsTls) {
+ if (Info.Tls) {
try {
stream = await HandshakeTlsAsServerAsync(tc);
} catch (Exception e) {
diff --git a/cmpctircd/Validation/ConfigurationOptionsValidator.cs b/cmpctircd/Validation/ConfigurationOptionsValidator.cs
new file mode 100644
index 0000000..1cad4b0
--- /dev/null
+++ b/cmpctircd/Validation/ConfigurationOptionsValidator.cs
@@ -0,0 +1,37 @@
+using cmpctircd.Configuration.Options;
+using FluentValidation;
+using Microsoft.Extensions.Options;
+
+namespace cmpctircd.Validation {
+ public class ConfigurationOptionsValidator : AbstractValidator> {
+ public ConfigurationOptionsValidator() {
+ RuleFor(c => c.Value.Host).NotEmpty();
+ RuleFor(c => c.Value.Sid).NotEmpty();
+ RuleFor(c => c.Value.OperChan).NotEmpty();
+ RuleForEach(c => c.Value.OperChan).NotEmpty();
+ RuleFor(c => c.Value.Advanced).SetValidator(new AdvancedOptionsValidator());
+ RuleForEach(c => c.Value.Opers).NotEmpty().SetValidator(new OperatorElementValidator());
+ RuleForEach(c => c.Value.Sockets).NotEmpty().SetValidator(new SocketElementValidator());
+ RuleForEach(c => c.Value.Loggers).SetValidator(new LoggerElementValidator());
+ RuleForEach(c => c.Value.UModes).SetValidator(new ModeElementValidator());
+ RuleForEach(c => c.Value.CModes).SetValidator(new ModeElementValidator());
+ RuleForEach(c => c.Value.Servers).NotEmpty().SetValidator(new ServerElementValidator());
+ RuleFor(c => c.Value.Tls).SetValidator(new TlsOptionsValidator());
+ }
+ }
+
+ public class TlsOptionsValidator : AbstractValidator {
+ public TlsOptionsValidator() {
+ RuleFor(t => t.File).NotEmpty();
+ }
+ }
+
+ public class AdvancedOptionsValidator : AbstractValidator {
+ public AdvancedOptionsValidator() {
+ RuleFor(a => a.MaxTargets).NotEmpty();
+ RuleFor(a => a.PingTimeout).NotEmpty();
+ RuleFor(a => a.RequirePongCookie).NotNull();
+ RuleFor(a => a.ResolveHostnames).NotNull();
+ }
+ }
+}
\ No newline at end of file
diff --git a/cmpctircd/Validation/LoggerElementValidator.cs b/cmpctircd/Validation/LoggerElementValidator.cs
new file mode 100644
index 0000000..78eddb5
--- /dev/null
+++ b/cmpctircd/Validation/LoggerElementValidator.cs
@@ -0,0 +1,11 @@
+using cmpctircd.Configuration;
+using FluentValidation;
+
+namespace cmpctircd.Validation {
+ public class LoggerElementValidator : AbstractValidator {
+ public LoggerElementValidator() {
+ RuleFor(logger => logger.Type).NotEmpty();
+ RuleFor(logger => logger.Level).NotEmpty();
+ }
+ }
+}
\ No newline at end of file
diff --git a/cmpctircd/Validation/ModeElementValidator.cs b/cmpctircd/Validation/ModeElementValidator.cs
new file mode 100644
index 0000000..98bdabf
--- /dev/null
+++ b/cmpctircd/Validation/ModeElementValidator.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using cmpctircd.Configuration;
+using FluentValidation;
+
+namespace cmpctircd.Validation
+{
+ public class ModeElementValidator : AbstractValidator
+ {
+ public ModeElementValidator() {
+ RuleFor(m => m.Name).NotEmpty().MaximumLength(1);
+ }
+ }
+}
diff --git a/cmpctircd/Validation/OperatorElementValidator.cs b/cmpctircd/Validation/OperatorElementValidator.cs
new file mode 100644
index 0000000..4d645c0
--- /dev/null
+++ b/cmpctircd/Validation/OperatorElementValidator.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using cmpctircd.Configuration;
+using FluentValidation;
+
+namespace cmpctircd.Validation
+{
+ public class OperatorElementValidator : AbstractValidator
+ {
+ public OperatorElementValidator() {
+ RuleFor(o => o.Algorithm).NotEmpty();
+ RuleFor(o => o.Name).NotEmpty();
+ RuleFor(o => o.Password).NotEmpty();
+ RuleFor(o => o.Hosts).NotEmpty();
+ }
+ }
+}
diff --git a/cmpctircd/Validation/ServerElementValidator.cs b/cmpctircd/Validation/ServerElementValidator.cs
new file mode 100644
index 0000000..738b061
--- /dev/null
+++ b/cmpctircd/Validation/ServerElementValidator.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using cmpctircd.Configuration;
+using FluentValidation;
+
+namespace cmpctircd.Validation
+{
+ public class ServerElementValidator : AbstractValidator
+ {
+ public ServerElementValidator() {
+ RuleFor(s => s.Host).NotEmpty();
+ RuleFor(s => s.Type).NotEmpty();
+ RuleFor(s => s.Masks).NotEmpty();
+ RuleFor(s => s.Port).NotEmpty().ExclusiveBetween(0, 65535);
+ RuleFor(s => s.Password).NotEmpty();
+ }
+ }
+}
diff --git a/cmpctircd/Validation/SocketElementValidator.cs b/cmpctircd/Validation/SocketElementValidator.cs
new file mode 100644
index 0000000..d10982a
--- /dev/null
+++ b/cmpctircd/Validation/SocketElementValidator.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using cmpctircd.Configuration;
+using FluentValidation;
+
+namespace cmpctircd.Validation
+{
+ public class SocketElementValidator : AbstractValidator
+ {
+ public SocketElementValidator() {
+ RuleFor(s => s.Tls).NotNull();
+ RuleFor(s => s.Host).NotEmpty();
+ RuleFor(s => s.Port).NotEmpty();
+ RuleFor(s => s.Type).NotNull();
+ }
+ }
+}
diff --git a/cmpctircd/appsettings.json b/cmpctircd/appsettings.json
new file mode 100644
index 0000000..3bd9ed7
--- /dev/null
+++ b/cmpctircd/appsettings.json
@@ -0,0 +1,142 @@
+{
+ "startup": {
+ "supportedRuntime": {
+ "version": "v4.0",
+ "sku": ".NETFramework,Version=v4.6.1"
+ }
+ },
+ "sid": "auto",
+ "host": "irc.cmpct.info",
+ "network": "cmpct",
+ "description": "The C# IRC Server",
+ "sockets": [
+ {
+ "type": "Client",
+ "host": "127.0.0.1",
+ "port": 6667,
+ "tls": false
+ }
+ //{
+ // "type": "Client",
+ // "host": "127.0.0.1",
+ // "port": "6697",
+ // "tls": true
+ //},
+ //{
+ // "protocol": "InspIRCd20",
+ // "type": "Server",
+ // "host": "127.0.0.1",
+ // "port": 9000,
+ // "tls": false
+ //},
+ //{
+ // "protocol": "InspIRCd20",
+ // "type": "Server",
+ // "host": "127.0.0.1",
+ // "port": 9001,
+ // "tls": true
+ //}
+ ],
+ //
+ //
+ //
+ //
+ "tls": {
+ "file": "server.pfx",
+ "password": ""
+ },
+ //
+ //
+ "loggers": [
+ {
+ "type": "Stdout",
+ "level": "Debug"
+ },
+ {
+ "type": "File",
+ "level": "Warn",
+ "attributes": {
+ "path": "ircd.log"
+ }
+ }
+ //
+ //
+ //
+ //{
+ // "type": "IRC",
+ // "level": "Debug",
+ // "attributes": {
+ // "channel": "#debug",
+ // "modes": "+nz"
+ // }
+
+ //}
+ ],
+ "advanced": {
+ "resolveHostnames": true,
+ "requirePongCookie": true,
+ "pingTimeout": 120,
+ "maxTargets": 200,
+//
+ "cloak": {
+ "key": "cmpct",
+ "prefix": "cmpct",
+ "domainParts": 3,
+ "full": false
+ }
+ },
+ "cmodes": [
+ {
+ "name": "n",
+ "param": ""
+ },
+ {
+ "name": "t",
+ "param": ""
+ }
+ ],
+ "umodes": [
+ {
+ "name": "x",
+ "param": ""
+ },
+ {
+ "name": "i",
+ "param": ""
+ }
+ ],
+//
+//
+ "servers": [
+ {
+ "type": "InspIRCd20",
+ "host": "services.cmpct.info",
+ "masks": [ "*@127.0.0.1" ],
+ "port": 9000,
+ "password": "mypassword",
+ "tls": false
+ }
+ ],
+ //
+ //
+ "opers": [
+ {
+ "name": "josh",
+ "password": "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8",
+ "algorithm": "System.Security.Cryptography.SHA256Managed",
+ "tls": false,
+ "hosts": [ "*@127.0.0.1" ]
+ }
+ ],
+ "operChan": [
+ "#opers"
+ ]
+}
\ No newline at end of file
diff --git a/cmpctircd/cmpctircd.csproj b/cmpctircd/cmpctircd.csproj
index c2d2f05..4677ebb 100644
--- a/cmpctircd/cmpctircd.csproj
+++ b/cmpctircd/cmpctircd.csproj
@@ -19,6 +19,7 @@
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
@@ -27,6 +28,7 @@
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
@@ -47,7 +49,6 @@
-
PreserveNewest
@@ -57,4 +58,9 @@
PreserveNewest
+
+
+ Always
+
+