2022-08-30 15:00:58 +00:00
|
|
|
|
using Hamakaze;
|
2022-08-30 15:05:29 +00:00
|
|
|
|
using SharpChat.Configuration;
|
|
|
|
|
using SharpChat.Database;
|
|
|
|
|
using SharpChat.Database.Null;
|
|
|
|
|
using SharpChat.DataProvider;
|
|
|
|
|
using SharpChat.DataProvider.Null;
|
|
|
|
|
using SharpChat.Protocol;
|
|
|
|
|
using SharpChat.Protocol.IRC;
|
|
|
|
|
using SharpChat.Protocol.Null;
|
|
|
|
|
using SharpChat.Protocol.SockChat;
|
|
|
|
|
using SharpChat.Reflection;
|
2022-08-30 15:00:58 +00:00
|
|
|
|
using System;
|
2022-08-30 15:05:29 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Text;
|
2022-08-30 15:00:58 +00:00
|
|
|
|
using System.Threading;
|
|
|
|
|
|
|
|
|
|
namespace SharpChat {
|
|
|
|
|
public class Program {
|
2022-08-30 15:05:29 +00:00
|
|
|
|
public const string CONFIG = @"sharpchat.cfg";
|
|
|
|
|
public const ushort DEFAULT_PORT = 6770;
|
|
|
|
|
|
|
|
|
|
private static string GetFlagArgument(string[] args, string flag) {
|
|
|
|
|
int offset = Array.IndexOf(args, flag) + 1;
|
|
|
|
|
return offset < 1 ? null : args.ElementAtOrDefault(offset);
|
|
|
|
|
}
|
2022-08-30 15:00:58 +00:00
|
|
|
|
|
|
|
|
|
public static void Main(string[] args) {
|
|
|
|
|
Console.WriteLine(@" _____ __ ________ __ ");
|
|
|
|
|
Console.WriteLine(@" / ___// /_ ____ __________ / ____/ /_ ____ _/ /_");
|
|
|
|
|
Console.WriteLine(@" \__ \/ __ \/ __ `/ ___/ __ \/ / / __ \/ __ `/ __/");
|
|
|
|
|
Console.WriteLine(@" ___/ / / / / /_/ / / / /_/ / /___/ / / / /_/ / /_ ");
|
|
|
|
|
Console.WriteLine(@"/____/_/ /_/\__,_/_/ / .___/\____/_/ /_/\__,_/\__/ ");
|
2022-08-30 15:05:29 +00:00
|
|
|
|
/**/Console.Write(@" / _/");
|
|
|
|
|
if(SharpInfo.IsDebugBuild) {
|
|
|
|
|
Console.WriteLine();
|
|
|
|
|
Console.Write(@"== ");
|
|
|
|
|
Console.Write(SharpInfo.VersionString);
|
|
|
|
|
Console.WriteLine(@" == DBG ==");
|
|
|
|
|
} else
|
|
|
|
|
Console.WriteLine(SharpInfo.VersionStringShort.PadLeft(28, ' '));
|
|
|
|
|
|
|
|
|
|
string configFile = GetFlagArgument(args, @"--cfg") ?? CONFIG;
|
|
|
|
|
|
|
|
|
|
// If the config file doesn't exist and we're using the default path, run the converter
|
|
|
|
|
if(!File.Exists(configFile) && configFile == CONFIG)
|
|
|
|
|
ConvertConfiguration();
|
|
|
|
|
|
|
|
|
|
using IConfig config = new StreamConfig(configFile);
|
|
|
|
|
|
|
|
|
|
// Load database and data provider libraries
|
|
|
|
|
ReflectionUtilities.LoadAssemblies(@"SharpChat.Database.*.dll");
|
|
|
|
|
ReflectionUtilities.LoadAssemblies(@"SharpChat.DataProvider.*.dll");
|
|
|
|
|
ReflectionUtilities.LoadAssemblies(@"SharpChat.Protocol.*.dll");
|
|
|
|
|
|
|
|
|
|
// Allow forcing a sqlite database through console flags
|
|
|
|
|
string sqliteDbPath = GetFlagArgument(args, @"--dbpath");
|
|
|
|
|
string databaseBackendName;
|
|
|
|
|
object databaseArgument;
|
|
|
|
|
if(!string.IsNullOrEmpty(sqliteDbPath)) {
|
|
|
|
|
Logger.Write($@"Forcing SQLite: {sqliteDbPath}");
|
|
|
|
|
databaseBackendName = @"sqlite";
|
|
|
|
|
databaseArgument = sqliteDbPath;
|
|
|
|
|
} else {
|
|
|
|
|
databaseBackendName = GetFlagArgument(args, @"--dbb") ?? config.ReadValue(@"db");
|
|
|
|
|
databaseArgument = config.ScopeTo($@"db:{databaseBackendName}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IDatabaseBackend databaseBackend = new ObjectConstructor<IDatabaseBackend, DatabaseBackendAttribute, NullDatabaseBackend>()
|
|
|
|
|
.Construct(databaseBackendName, databaseArgument);
|
|
|
|
|
|
|
|
|
|
using HttpClient httpClient = new() {
|
|
|
|
|
DefaultUserAgent = @"SharpChat/1.0",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
string dataProviderName = GetFlagArgument(args, @"--dpn") ?? config.ReadValue(@"dp");
|
|
|
|
|
IDataProvider dataProvider = new ObjectConstructor<IDataProvider, DataProviderAttribute, NullDataProvider>()
|
|
|
|
|
.Construct(dataProviderName, config.ScopeTo($@"dp:{dataProviderName}"), httpClient);
|
|
|
|
|
|
|
|
|
|
string portArg = GetFlagArgument(args, @"--port") ?? config.ReadValue(@"chat:port");
|
|
|
|
|
if(string.IsNullOrEmpty(portArg) || !ushort.TryParse(portArg, out ushort port))
|
|
|
|
|
port = DEFAULT_PORT;
|
|
|
|
|
|
|
|
|
|
Win32.IncreaseThreadPrecision();
|
|
|
|
|
|
|
|
|
|
ObjectConstructor<IServer, ServerAttribute, NullServer> serverConstructor = new();
|
|
|
|
|
|
|
|
|
|
Logger.Write(@"Creating context...");
|
|
|
|
|
using(Context ctx = new(config.ScopeTo(@"chat"), databaseBackend, dataProvider)) {
|
|
|
|
|
List<IServer> servers = new();
|
|
|
|
|
|
|
|
|
|
// Crusty temporary solution, just want to have the variable constructor arguments for servers in place already
|
|
|
|
|
|
|
|
|
|
string[] serverNames = new[] {
|
|
|
|
|
@"sockchat",
|
2022-08-30 15:00:58 +00:00
|
|
|
|
#if DEBUG
|
2022-08-30 15:05:29 +00:00
|
|
|
|
@"irc",
|
2022-08-30 15:00:58 +00:00
|
|
|
|
#endif
|
2022-08-30 15:05:29 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
foreach(string serverName in serverNames) {
|
|
|
|
|
Logger.Write($@"Starting {serverName} server...");
|
|
|
|
|
IServer server = serverConstructor.Construct(serverName, ctx, config.ScopeTo(serverName));
|
|
|
|
|
servers.Add(server);
|
|
|
|
|
|
|
|
|
|
if(server is SockChatServer)
|
|
|
|
|
server.Listen(new IPEndPoint(IPAddress.Any, port));
|
|
|
|
|
else if(server is IRCServer)
|
|
|
|
|
server.Listen(new IPEndPoint(IPAddress.Any, 6667));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using ManualResetEvent mre = new(false);
|
|
|
|
|
Console.CancelKeyPress += (s, e) => { e.Cancel = true; mre.Set(); };
|
|
|
|
|
mre.WaitOne();
|
|
|
|
|
|
|
|
|
|
foreach(IServer server in servers)
|
|
|
|
|
server.Dispose();
|
|
|
|
|
|
|
|
|
|
if(dataProvider is IDisposable dpd)
|
|
|
|
|
dpd.Dispose();
|
|
|
|
|
if(databaseBackend is IDisposable dbd)
|
|
|
|
|
dbd.Dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Win32.RestoreThreadPrecision();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void ConvertConfiguration() {
|
|
|
|
|
using Stream s = new FileStream(CONFIG, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
|
|
|
|
|
s.SetLength(0);
|
|
|
|
|
s.Flush();
|
|
|
|
|
|
|
|
|
|
using StreamWriter sw = new(s, new UTF8Encoding(false));
|
|
|
|
|
|
|
|
|
|
sw.WriteLine(@"# and ; can be used at the start of a line for comments.");
|
|
|
|
|
sw.WriteLine();
|
|
|
|
|
|
|
|
|
|
sw.WriteLine(@"# General Configuration");
|
|
|
|
|
sw.WriteLine($@"#chat:port {DEFAULT_PORT}");
|
|
|
|
|
sw.WriteLine($@"#chat:messages:maxLength {Messages.MessageManager.DEFAULT_LENGTH_MAX}");
|
|
|
|
|
sw.WriteLine($@"#chat:sessions:timeOut {Sessions.SessionManager.DEFAULT_TIMEOUT}");
|
|
|
|
|
sw.WriteLine($@"#chat:sessions:maxCount {Sessions.SessionManager.DEFAULT_MAX_COUNT}");
|
|
|
|
|
sw.WriteLine();
|
|
|
|
|
|
|
|
|
|
sw.WriteLine(@"# Rate Limiter Configuration");
|
|
|
|
|
sw.WriteLine($@"#chat:rateLimit:userSize {RateLimiting.RateLimitManager.DEFAULT_USER_SIZE}");
|
|
|
|
|
sw.WriteLine($@"#chat:rateLimit:userWarnSize {RateLimiting.RateLimitManager.DEFAULT_USER_WARN_SIZE}");
|
|
|
|
|
sw.WriteLine($@"#chat:rateLimit:connSize {RateLimiting.RateLimitManager.DEFAULT_CONN_SIZE}");
|
|
|
|
|
sw.WriteLine($@"#chat:rateLimit:minDelay {RateLimiting.RateLimitManager.DEFAULT_MINIMUM_DELAY}");
|
|
|
|
|
sw.WriteLine($@"#chat:rateLimit:kickLength {RateLimiting.RateLimitManager.DEFAULT_KICK_LENGTH}");
|
|
|
|
|
sw.WriteLine($@"#chat:rateLimit:kickMultiplier {RateLimiting.RateLimitManager.DEFAULT_KICK_MULTIPLIER}");
|
|
|
|
|
sw.WriteLine();
|
|
|
|
|
|
|
|
|
|
sw.WriteLine(@"# Channels");
|
|
|
|
|
sw.WriteLine(@"chat:channels lounge staff");
|
|
|
|
|
sw.WriteLine();
|
|
|
|
|
|
|
|
|
|
sw.WriteLine(@"# Lounge channel settings");
|
|
|
|
|
sw.WriteLine(@"chat:channels:lounge:autoJoin true");
|
|
|
|
|
sw.WriteLine();
|
|
|
|
|
|
|
|
|
|
sw.WriteLine(@"# Staff channel settings");
|
|
|
|
|
sw.WriteLine(@"chat:channels:staff:minRank 5");
|
|
|
|
|
sw.WriteLine();
|
|
|
|
|
|
|
|
|
|
const string msz_config = @"login_key.txt";
|
|
|
|
|
|
|
|
|
|
sw.WriteLine(@"# Selected DataProvider (misuzu, null)");
|
|
|
|
|
if(!File.Exists(msz_config))
|
|
|
|
|
sw.WriteLine(@"dp null");
|
|
|
|
|
else {
|
|
|
|
|
sw.WriteLine(@"dp misuzu");
|
|
|
|
|
sw.WriteLine();
|
|
|
|
|
sw.WriteLine(@"# Misuzu DataProvider settings");
|
|
|
|
|
sw.WriteLine(@"#db:misuzu:userId 61");
|
|
|
|
|
sw.Write(@"dp:misuzu:secret ");
|
|
|
|
|
sw.Write(File.ReadAllText(msz_config).Trim());
|
|
|
|
|
sw.WriteLine();
|
|
|
|
|
sw.Write(@"dp:misuzu:endpoint ");
|
|
|
|
|
#if DEBUG
|
|
|
|
|
sw.Write(@"https://misuzu.misaka.nl/_sockchat");
|
|
|
|
|
#else
|
|
|
|
|
sw.Write(@"https://flashii.net/_sockchat");
|
|
|
|
|
#endif
|
|
|
|
|
sw.WriteLine();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sw.WriteLine();
|
|
|
|
|
|
|
|
|
|
const string sql_config = @"sqlite.txt";
|
|
|
|
|
const string mdb_config = @"mariadb.txt";
|
|
|
|
|
|
|
|
|
|
bool hasMDB = File.Exists(mdb_config),
|
|
|
|
|
hasSQL = File.Exists(sql_config);
|
|
|
|
|
|
|
|
|
|
sw.WriteLine(@"# Selected Database Backend (mariadb, sqlite, null)");
|
|
|
|
|
if(hasMDB)
|
|
|
|
|
sw.WriteLine(@"db mariadb");
|
|
|
|
|
else if(hasSQL)
|
|
|
|
|
sw.WriteLine(@"db sqlite");
|
|
|
|
|
else
|
|
|
|
|
sw.WriteLine(@"db null");
|
|
|
|
|
sw.WriteLine();
|
2022-08-30 15:00:58 +00:00
|
|
|
|
|
2022-08-30 15:05:29 +00:00
|
|
|
|
if(hasMDB) {
|
|
|
|
|
string[] mdbCfg = File.ReadAllLines(mdb_config);
|
|
|
|
|
sw.WriteLine(@"# MariaDB configuration");
|
|
|
|
|
sw.WriteLine($@"db:mariadb:host {mdbCfg[0]}");
|
|
|
|
|
if(mdbCfg.Length > 1)
|
|
|
|
|
sw.WriteLine($@"db:mariadb:user {mdbCfg[1]}");
|
|
|
|
|
else
|
|
|
|
|
sw.WriteLine($@"#db:mariadb:user <username>");
|
|
|
|
|
if(mdbCfg.Length > 2)
|
|
|
|
|
sw.WriteLine($@"db:mariadb:pass {mdbCfg[2]}");
|
|
|
|
|
else
|
|
|
|
|
sw.WriteLine($@"#db:mariadb:pass <password>");
|
|
|
|
|
if(mdbCfg.Length > 3)
|
|
|
|
|
sw.WriteLine($@"db:mariadb:db {mdbCfg[3]}");
|
|
|
|
|
else
|
|
|
|
|
sw.WriteLine($@"#db:mariadb:db <database>");
|
|
|
|
|
sw.WriteLine();
|
|
|
|
|
}
|
2022-08-30 15:00:58 +00:00
|
|
|
|
|
2022-08-30 15:05:29 +00:00
|
|
|
|
if(hasSQL) {
|
|
|
|
|
string[] sqlCfg = File.ReadAllLines(sql_config);
|
|
|
|
|
sw.WriteLine(@"# SQLite configuration");
|
|
|
|
|
sw.WriteLine($@"db:sqlite:path {sqlCfg[0]}");
|
|
|
|
|
}
|
2022-08-30 15:00:58 +00:00
|
|
|
|
|
2022-08-30 15:05:29 +00:00
|
|
|
|
sw.Flush();
|
2022-08-30 15:00:58 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|