| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 | using H.Pipes;using H.Pipes.AccessControl;using H.Pipes.Args;using InABox.API;using InABox.Clients;using InABox.Core;using InABox.Server;using System.IO.Pipes;using System.Reflection;using System.Security.Principal;namespace InABox.IPC{    public class IPCServer : IDisposable    {        PipeServer<IPCMessage> Server;        IPCPushState PushState = new();        public IPCServer(string name)        {            Server = new PipeServer<IPCMessage>(name);            #if WINDOWS            SetPipeSecurity();            #endif            Server.ClientConnected += Server_ClientConnected;            Server.ClientDisconnected += Server_ClientDisconnected;            Server.MessageReceived += Server_MessageReceived;            Server.ExceptionOccurred += Server_ExceptionOccurred;        }        private void SetPipeSecurity()        {            #pragma warning disable CA1416                        var pipeSecurity = new PipeSecurity();            pipeSecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.LocalSid, null), PipeAccessRights.ReadWrite,                System.Security.AccessControl.AccessControlType.Allow));            pipeSecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.LocalServiceSid, null), PipeAccessRights.ReadWrite,                System.Security.AccessControl.AccessControlType.Allow));            pipeSecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null), PipeAccessRights.ReadWrite,                System.Security.AccessControl.AccessControlType.Allow));            Server.SetPipeSecurity(pipeSecurity);                        #pragma warning restore CA1416        }        private void Server_ExceptionOccurred(object? sender, H.Pipes.Args.ExceptionEventArgs e)        {            Logger.Send(LogType.Error, "", $"Exception Occurred: {e.Exception.Message}");        }        public void Start()        {            Server.StartAsync().Wait();        }        private static List<Type>? _persistentRemotable;        private static Type? GetEntity(string entityName)        {            _persistentRemotable ??= CoreUtils.TypeList(                e => e.IsSubclassOf(typeof(Entity)) &&                     e.GetInterfaces().Contains(typeof(IRemotable)) &&                     e.GetInterfaces().Contains(typeof(IPersistent))).ToList();            return _persistentRemotable.FirstOrDefault(x => x.Name == entityName);        }        private static Type? GetResponseType(Method method, string? entityName)        {            if(entityName != null)            {                var entityType = GetEntity(entityName);                if(entityType != null)                {                    var response = method switch                    {                        Method.Query => typeof(QueryResponse<>).MakeGenericType(entityType),                        Method.Delete => typeof(DeleteResponse<>).MakeGenericType(entityType),                        Method.MultiDelete => typeof(MultiDeleteResponse<>).MakeGenericType(entityType),                        Method.Save => typeof(SaveResponse<>).MakeGenericType(entityType),                        Method.MultiSave => typeof(MultiSaveResponse<>).MakeGenericType(entityType),                        _ => null                    };                    if (response != null) return response;                }            }            return method switch            {                Method.QueryMultiple => typeof(MultiQueryResponse),                Method.Validate => typeof(ValidateResponse),                Method.Check2FA => typeof(Check2FAResponse),                Method.Version => typeof(VersionResponse),                Method.Installer => typeof(InstallerResponse),                Method.ReleaseNotes => typeof(ReleaseNotesResponse),                _ => null            };        }        private class RequestData        {            public ConnectionMessageEventArgs<IPCMessage?> e { get; }            public RequestData(ConnectionMessageEventArgs<IPCMessage?> e)            {                this.e = e;            }        }        private IPCMessage QueryMultiple(IPCMessage request, RequestData data)        {            var response = RestService.QueryMultiple(request.GetRequest<MultiQueryRequest>(), true);            return request.Respond(response);        }        private IPCMessage Validate(IPCMessage request, RequestData data)        {            var response = RestService.Validate(request.GetRequest<ValidateRequest>());            return request.Respond(response);        }        private IPCMessage Ping(IPCMessage request, RequestData data) => request.Respond(new PingResponse().Status(StatusCode.OK));        private IPCMessage Info(IPCMessage request, RequestData data)        {            var response = RestService.Info(request.GetRequest<InfoRequest>());            return request.Respond(response);        }                private IPCMessage Check2FA(IPCMessage request, RequestData data)        {            var response = RestService.Check2FA(request.GetRequest<Check2FARequest>());            return request.Respond(response);        }        private IPCMessage Query<T>(IPCMessage request, RequestData data) where T : Entity, new()        {            var response = RestService<T>.List(request.GetRequest<QueryRequest<T>>());            return request.Respond(response);        }        private IPCMessage Save<T>(IPCMessage request, RequestData data) where T : Entity, new()        {            var response = RestService<T>.Save(request.GetRequest<SaveRequest<T>>());            return request.Respond(response);        }        private IPCMessage MultiSave<T>(IPCMessage request, RequestData data) where T : Entity, new()        {            var response = RestService<T>.MultiSave(request.GetRequest<MultiSaveRequest<T>>());            return request.Respond(response);        }        private IPCMessage Delete<T>(IPCMessage request, RequestData data) where T : Entity, new()        {            var response = RestService<T>.Delete(request.GetRequest<DeleteRequest<T>>());            return request.Respond(response);        }        private IPCMessage MultiDelete<T>(IPCMessage request, RequestData data) where T : Entity, new()        {            var response = RestService<T>.MultiDelete(request.GetRequest<MultiDeleteRequest<T>>());            return request.Respond(response);        }        private IPCMessage Version(IPCMessage request, RequestData data) =>             request.Respond(new VersionResponse { Version = UpdateData.GetUpdateVersion() });        private IPCMessage ReleaseNotes(IPCMessage request, RequestData data) =>             request.Respond(new ReleaseNotesResponse { ReleaseNotes = UpdateData.GetReleaseNotes() });        private IPCMessage Installer(IPCMessage request, RequestData data) =>             request.Respond(new InstallerResponse { Installer = UpdateData.GetUpdateInstaller() });        private static readonly MethodInfo QueryMethod = GetMethod(nameof(Query));        private static readonly MethodInfo SaveMethod = GetMethod(nameof(Save));        private static readonly MethodInfo MultiSaveMethod = GetMethod(nameof(MultiSave));        private static readonly MethodInfo DeleteMethod = GetMethod(nameof(Delete));        private static readonly MethodInfo MultiDeleteMethod = GetMethod(nameof(MultiDelete));        private static readonly MethodInfo QueryMultipleMethod = GetMethod(nameof(QueryMultiple));        private static readonly MethodInfo ValidateMethod = GetMethod(nameof(Validate));        private static readonly MethodInfo Check2FAMethod = GetMethod(nameof(Check2FA));        private static readonly MethodInfo PingMethod = GetMethod(nameof(Ping));        private static readonly MethodInfo InfoMethod = GetMethod(nameof(Info));        private static readonly MethodInfo VersionMethod = GetMethod(nameof(Version));        private static readonly MethodInfo ReleaseNotesMethod = GetMethod(nameof(ReleaseNotes));        private static readonly MethodInfo InstallerMethod = GetMethod(nameof(Installer));        private static MethodInfo GetMethod(string name) =>            typeof(IPCServer).GetMethod(name, BindingFlags.NonPublic | BindingFlags.Instance)            ?? throw new Exception($"Invalid method '{name}'");        private void Server_MessageReceived(object? sender, H.Pipes.Args.ConnectionMessageEventArgs<IPCMessage?> e)        {            Task.Run(() =>            {                var start = DateTime.Now;                try                {                    if (e.Message == null) throw new Exception($"Invalid message");                    var method = e.Message.Method switch                    {                        Method.Query => QueryMethod,                        Method.QueryMultiple => QueryMultipleMethod,                        Method.Delete => DeleteMethod,                        Method.MultiDelete => MultiDeleteMethod,                        Method.Save => SaveMethod,                        Method.MultiSave => MultiSaveMethod,                        Method.Check2FA => Check2FAMethod,                        Method.Validate => ValidateMethod,                        Method.Ping => PingMethod,                        Method.Info => InfoMethod,                        Method.Version => VersionMethod,                        Method.ReleaseNotes => ReleaseNotesMethod,                        Method.Installer => InstallerMethod,                        Method.None or _ => throw new Exception($"Invalid method '{e.Message.Method}'")                    };                    if (e.Message.Type != null)                    {                        var entityType = GetEntity(e.Message.Type) ?? throw new Exception($"No entity '{e.Message.Type}'");                        method = method.MakeGenericMethod(entityType);                    }                    var response = method.Invoke(this, new object[] { e.Message, new RequestData(e) }) as IPCMessage;                    e.Connection.WriteAsync(response).ContinueWith(task =>                    {                        if (task.Exception != null)                        {                            Logger.Send(LogType.Error, "", $"Error in response: {CoreUtils.FormatException(task.Exception)}");                        }                    });                }                catch (Exception err)                {                    Logger.Send(LogType.Error, "", err.Message);                    if (e.Message != null)                    {                        var responseType = GetResponseType(e.Message.Method, e.Message.Type);                        if (responseType != null)                        {                            var response = (Activator.CreateInstance(responseType) as Response)!;                            response.Status = StatusCode.Error;                            response.Messages.Add(err.Message);                            e.Connection.WriteAsync(e.Message.Respond(response)).ContinueWith(task =>                            {                                if (task.Exception != null)                                {                                    Logger.Send(LogType.Error, "", $"Error in response: {CoreUtils.FormatException(task.Exception)}");                                }                            });                        }                    }                }            });        }        private void Server_ClientDisconnected(object? sender, H.Pipes.Args.ConnectionEventArgs<IPCMessage> e)        {            Logger.Send(LogType.Information, "", "Client Disconnected");            var sessionID = PushState.SessionMap.Where(x => x.Value.Connection == e.Connection).FirstOrDefault().Key;            PushState.SessionMap.TryRemove(sessionID, out var session);            e.Connection.DisposeAsync();        }        private void Server_ClientConnected(object? sender, H.Pipes.Args.ConnectionEventArgs<IPCMessage> e)        {            Logger.Send(LogType.Information, "", "Client Connected");        }        public void Dispose()        {            Server.DisposeAsync().AsTask().Wait();        }        ~IPCServer()        {            Dispose();        }    }}
 |