|
@@ -1,372 +0,0 @@
|
|
|
-using H.Formatters;
|
|
|
-using H.Pipes;
|
|
|
-using H.Pipes.AccessControl;
|
|
|
-using H.Pipes.Args;
|
|
|
-using InABox.API;
|
|
|
-using InABox.Clients;
|
|
|
-using InABox.Core;
|
|
|
-using InABox.IPC.Shared;
|
|
|
-using InABox.Server.WebSocket;
|
|
|
-using System;
|
|
|
-using System.Collections.Concurrent;
|
|
|
-using System.Collections.Generic;
|
|
|
-using System.Diagnostics;
|
|
|
-using System.IO.Pipes;
|
|
|
-using System.Linq;
|
|
|
-using System.Reflection;
|
|
|
-using System.Security.Principal;
|
|
|
-using System.Text;
|
|
|
-using System.Threading.Tasks;
|
|
|
-
|
|
|
-namespace Piping
|
|
|
-{
|
|
|
- using PipeResponse = PipeRequest;
|
|
|
-
|
|
|
- delegate void PipePollEvent(PipeNotifyState.Session session);
|
|
|
-
|
|
|
- class PipeNotifyState
|
|
|
- {
|
|
|
- public class Session
|
|
|
- {
|
|
|
- public PipeConnection<PipeResponse?> Connection { get; }
|
|
|
-
|
|
|
- public Guid SessionID { get; }
|
|
|
-
|
|
|
- public Platform Platform { get; }
|
|
|
-
|
|
|
- public Session(PipeConnection<PipeResponse?> connection, Guid sessionID, Platform platform)
|
|
|
- {
|
|
|
- Connection = connection;
|
|
|
- SessionID = sessionID;
|
|
|
- Platform = platform;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public ConcurrentDictionary<Guid, Session> SessionMap = new();
|
|
|
-
|
|
|
- public event PipePollEvent? OnPoll;
|
|
|
-
|
|
|
- public void Poll(Session session)
|
|
|
- {
|
|
|
- OnPoll?.Invoke(session);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- class PipeIPCNotifier : Notifier
|
|
|
- {
|
|
|
- PipeNotifyState NotifyState { get; set; }
|
|
|
-
|
|
|
- public PipeIPCNotifier(PipeNotifyState notifyState)
|
|
|
- {
|
|
|
- NotifyState = notifyState;
|
|
|
- NotifyState.OnPoll += NotifyState_OnPoll;
|
|
|
- }
|
|
|
-
|
|
|
- private void NotifyState_OnPoll(PipeNotifyState.Session session)
|
|
|
- {
|
|
|
- Notify.Poll(session.SessionID);
|
|
|
- }
|
|
|
-
|
|
|
- protected override IEnumerable<Guid> GetSessions(Platform platform)
|
|
|
- {
|
|
|
- return NotifyState.SessionMap.Where(x => x.Value.Platform == platform).Select(x => x.Key);
|
|
|
- }
|
|
|
-
|
|
|
- protected override IEnumerable<Guid> GetUserSessions(Guid userID)
|
|
|
- {
|
|
|
- return CredentialsCache.GetUserSessions(userID);
|
|
|
- }
|
|
|
-
|
|
|
- protected override void NotifyAll<TNotification>(TNotification notification)
|
|
|
- {
|
|
|
- foreach(var session in NotifyState.SessionMap.Values)
|
|
|
- {
|
|
|
- session.Connection.WriteAsync(PipeRequest.Notification(notification)).ContinueWith(task =>
|
|
|
- {
|
|
|
- if(task.Exception != null)
|
|
|
- {
|
|
|
- Logger.Send(LogType.Error, "", $"Error in notification: {CoreUtils.FormatException(task.Exception)}");
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- protected override void NotifySession<TNotification>(Guid sessionID, TNotification notification)
|
|
|
- {
|
|
|
- if(NotifyState.SessionMap.TryGetValue(sessionID, out var session))
|
|
|
- {
|
|
|
- session.Connection.WriteAsync(PipeRequest.Notification(notification)).ContinueWith(task =>
|
|
|
- {
|
|
|
- if (task.Exception != null)
|
|
|
- {
|
|
|
- Logger.Send(LogType.Error, "", $"Error in notification: {CoreUtils.FormatException(task.Exception)}");
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- protected override void NotifySession(Guid sessionID, Type TNotification, BaseObject notification)
|
|
|
- {
|
|
|
- if (NotifyState.SessionMap.TryGetValue(sessionID, out var session))
|
|
|
- {
|
|
|
- session.Connection.WriteAsync(PipeRequest.Notification(TNotification, notification)).ContinueWith(task =>
|
|
|
- {
|
|
|
- if (task.Exception != null)
|
|
|
- {
|
|
|
- Logger.Send(LogType.Error, "", $"Error in notification: {CoreUtils.FormatException(task.Exception)}");
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public class PipeIPCServer : IDisposable
|
|
|
- {
|
|
|
- PipeServer<PipeRequest> Server;
|
|
|
-
|
|
|
- PipeNotifyState NotifyState = new();
|
|
|
-
|
|
|
- public PipeIPCServer(string name)
|
|
|
- {
|
|
|
- Server = new PipeServer<PipeRequest>(name);
|
|
|
-
|
|
|
- Notify.AddNotifier(new PipeIPCNotifier(NotifyState));
|
|
|
-
|
|
|
- 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);
|
|
|
-
|
|
|
- Server.ClientConnected += Server_ClientConnected;
|
|
|
- Server.ClientDisconnected += Server_ClientDisconnected;
|
|
|
- Server.MessageReceived += Server_MessageReceived;
|
|
|
- Server.ExceptionOccurred += Server_ExceptionOccurred;
|
|
|
- }
|
|
|
-
|
|
|
- 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),
|
|
|
- _ => null
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- private class RequestData
|
|
|
- {
|
|
|
- public ConnectionMessageEventArgs<PipeRequest?> e { get; }
|
|
|
-
|
|
|
- public RequestData(ConnectionMessageEventArgs<PipeResponse?> e)
|
|
|
- {
|
|
|
- this.e = e;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private PipeResponse QueryMultiple(PipeRequest request, RequestData data)
|
|
|
- {
|
|
|
- var response = RestService.QueryMultiple(request.GetRequest<MultiQueryRequest>(), true);
|
|
|
- return request.Respond(response);
|
|
|
- }
|
|
|
-
|
|
|
- private PipeResponse Validate(PipeRequest request, RequestData data)
|
|
|
- {
|
|
|
- var validateRequest = request.GetRequest<ValidateRequest>();
|
|
|
- var response = RestService.Validate(validateRequest);
|
|
|
-
|
|
|
- if(response.Session != Guid.Empty)
|
|
|
- {
|
|
|
- var newSession = new PipeNotifyState.Session(data.e.Connection, response.Session, validateRequest.Credentials.Platform);
|
|
|
- NotifyState.SessionMap[response.Session] = newSession;
|
|
|
- NotifyState.Poll(newSession);
|
|
|
- }
|
|
|
-
|
|
|
- return request.Respond(response);
|
|
|
- }
|
|
|
-
|
|
|
- private PipeResponse Ping(PipeRequest request, RequestData data) => request.Respond(new PingResponse().Status(StatusCode.OK));
|
|
|
-
|
|
|
- private PipeResponse Info(PipeRequest request, RequestData data)
|
|
|
- {
|
|
|
- var response = RestService.Info(request.GetRequest<InfoRequest>());
|
|
|
- return request.Respond(response);
|
|
|
- }
|
|
|
-
|
|
|
- private PipeResponse Check2FA(PipeRequest request, RequestData data)
|
|
|
- {
|
|
|
- var response = RestService.Check2FA(request.GetRequest<Check2FARequest>());
|
|
|
- return request.Respond(response);
|
|
|
- }
|
|
|
-
|
|
|
- private PipeResponse Query<T>(PipeRequest request, RequestData data) where T : Entity, new()
|
|
|
- {
|
|
|
- var response = RestService<T>.List(request.GetRequest<QueryRequest<T>>());
|
|
|
- return request.Respond(response);
|
|
|
- }
|
|
|
-
|
|
|
- private PipeResponse Save<T>(PipeRequest request, RequestData data) where T : Entity, new()
|
|
|
- {
|
|
|
- var response = RestService<T>.Save(request.GetRequest<SaveRequest<T>>());
|
|
|
- return request.Respond(response);
|
|
|
- }
|
|
|
- private PipeResponse MultiSave<T>(PipeRequest request, RequestData data) where T : Entity, new()
|
|
|
- {
|
|
|
- var response = RestService<T>.MultiSave(request.GetRequest<MultiSaveRequest<T>>());
|
|
|
- return request.Respond(response);
|
|
|
- }
|
|
|
-
|
|
|
- private PipeResponse Delete<T>(PipeRequest request, RequestData data) where T : Entity, new()
|
|
|
- {
|
|
|
- var response = RestService<T>.Delete(request.GetRequest<DeleteRequest<T>>());
|
|
|
- return request.Respond(response);
|
|
|
- }
|
|
|
- private PipeResponse MultiDelete<T>(PipeRequest request, RequestData data) where T : Entity, new()
|
|
|
- {
|
|
|
- var response = RestService<T>.MultiDelete(request.GetRequest<MultiDeleteRequest<T>>());
|
|
|
- return request.Respond(response);
|
|
|
- }
|
|
|
-
|
|
|
- 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 MethodInfo GetMethod(string name) =>
|
|
|
- typeof(PipeIPCServer).GetMethod(name, BindingFlags.NonPublic | BindingFlags.Instance)
|
|
|
- ?? throw new Exception($"Invalid method '{name}'");
|
|
|
-
|
|
|
- private void Server_MessageReceived(object? sender, ConnectionMessageEventArgs<PipeRequest?> 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.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 PipeResponse;
|
|
|
- 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<PipeRequest> e)
|
|
|
- {
|
|
|
- Logger.Send(LogType.Information, "", "Client Disconnected");
|
|
|
-
|
|
|
- var sessionID = NotifyState.SessionMap.Where(x => x.Value.Connection == e.Connection).FirstOrDefault().Key;
|
|
|
- NotifyState.SessionMap.TryRemove(sessionID, out var session);
|
|
|
-
|
|
|
- e.Connection.DisposeAsync();
|
|
|
- }
|
|
|
-
|
|
|
- private void Server_ClientConnected(object? sender, H.Pipes.Args.ConnectionEventArgs<PipeRequest> e)
|
|
|
- {
|
|
|
- Logger.Send(LogType.Information, "", "Client Connected");
|
|
|
- }
|
|
|
-
|
|
|
- public void Dispose()
|
|
|
- {
|
|
|
- Server.DisposeAsync().AsTask().Wait();
|
|
|
- }
|
|
|
- ~PipeIPCServer()
|
|
|
- {
|
|
|
- Dispose();
|
|
|
- }
|
|
|
- }
|
|
|
-}
|