|
@@ -1,24 +1,20 @@
|
|
|
-using H.Pipes;
|
|
|
+using InABox.Client.IPC;
|
|
|
+using InABox.Clients;
|
|
|
using InABox.Core;
|
|
|
-using InABox.IPC.Shared;
|
|
|
-using System;
|
|
|
-using System.Collections.Concurrent;
|
|
|
-using System.Collections.Generic;
|
|
|
-using System.Linq;
|
|
|
-using System.Text;
|
|
|
-using System.Threading.Tasks;
|
|
|
-
|
|
|
-namespace InABox.Client.IPC
|
|
|
+
|
|
|
+namespace InABox.IPC
|
|
|
{
|
|
|
- public class IPCClient : IDisposable
|
|
|
+ internal static class LocalCache
|
|
|
{
|
|
|
- private PipeClient<PipeRequest> Client;
|
|
|
+ public static string Password { get; set; }
|
|
|
|
|
|
- private ConcurrentDictionary<Guid, ManualResetEventSlim> Events = new();
|
|
|
- private ConcurrentDictionary<Guid, PipeRequest> Responses = new();
|
|
|
+ }
|
|
|
|
|
|
- private const int DefaultRequestTimeout = 5 * 60 * 1000; // 5 minutes
|
|
|
+ public class IPCClient<TEntity> : BaseClient<TEntity> where TEntity : Entity, new()
|
|
|
+ {
|
|
|
+ private IPCClientTransport _clientTransport;
|
|
|
|
|
|
+<<<<<<< Updated upstream
|
|
|
public delegate void ConnectEvent();
|
|
|
public delegate void DisconnectEvent();
|
|
|
|
|
@@ -34,113 +30,396 @@ namespace InABox.Client.IPC
|
|
|
public event PushEvent? OnPush;
|
|
|
|
|
|
public IPCClient(string name)
|
|
|
+=======
|
|
|
+ public IPCClient(string pipeName)
|
|
|
+>>>>>>> Stashed changes
|
|
|
{
|
|
|
- Client = new PipeClient<PipeRequest>(name);
|
|
|
- Client.Connected += Client_Connected;
|
|
|
- Client.Disconnected += Client_Disconnected;
|
|
|
- Client.MessageReceived += Client_MessageReceived;
|
|
|
- Client.ExceptionOccurred += Client_ExceptionOccurred;
|
|
|
-
|
|
|
- Client.ConnectAsync();
|
|
|
+ _clientTransport = IPCClientFactory.GetClient(pipeName);
|
|
|
+ Timeout = TimeSpan.FromSeconds(300);
|
|
|
}
|
|
|
|
|
|
- private void Client_ExceptionOccurred(object? sender, H.Pipes.Args.ExceptionEventArgs e)
|
|
|
+ private static string[]? _types;
|
|
|
+ public override string[] SupportedTypes()
|
|
|
{
|
|
|
- Logger.Send(LogType.Error, "", $"Exception occured: {e.Exception.Message}");
|
|
|
+ _types ??= CoreUtils.Entities
|
|
|
+ .Where(x => x.GetInterfaces().Contains(typeof(IPersistent)))
|
|
|
+ .Select(x => x.EntityName().Replace(".", "_"))
|
|
|
+ .ToArray();
|
|
|
+ return _types;
|
|
|
}
|
|
|
|
|
|
- public PipeRequest Send(PipeRequest request, int timeout = DefaultRequestTimeout)
|
|
|
+ public override DatabaseInfo Info()
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var request = new InfoRequest();
|
|
|
+ PrepareRequest(request, false);
|
|
|
+ var response = Send(IPCRequest.Info(request)).GetResponse<InfoResponse>();
|
|
|
+ return response.Info;
|
|
|
+ }
|
|
|
+ catch (Exception)
|
|
|
+ {
|
|
|
+ return new DatabaseInfo();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void PrepareRequest(Request request, bool doCredentials = true)
|
|
|
{
|
|
|
- var start = DateTime.Now;
|
|
|
- var ev = Queue(request.RequestID);
|
|
|
- Client.WriteAsync(request);
|
|
|
- var result = GetResult(request.RequestID, ev, timeout);
|
|
|
- return result;
|
|
|
+ if(request is not ValidateRequest && _clientTransport.Disconnected)
|
|
|
+ {
|
|
|
+ ClientFactory.Validate(ClientFactory.UserID, LocalCache.Password);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (doCredentials)
|
|
|
+ {
|
|
|
+ request.Credentials.Platform = ClientFactory.Platform;
|
|
|
+ request.Credentials.Version = ClientFactory.Version;
|
|
|
+ request.Credentials.Session = ClientFactory.SessionID;
|
|
|
+ }
|
|
|
+
|
|
|
+ Request.BeforeRequest?.Invoke(request);
|
|
|
}
|
|
|
|
|
|
- public ManualResetEventSlim Queue(Guid id)
|
|
|
+ private IPCRequest Send(IPCRequest request, int? timeout = null)
|
|
|
{
|
|
|
- var ev = new ManualResetEventSlim();
|
|
|
- Events[id] = ev;
|
|
|
- return ev;
|
|
|
+ return _clientTransport.Send(request, timeout ?? Convert.ToInt32(Timeout.TotalMilliseconds));
|
|
|
}
|
|
|
|
|
|
- public PipeRequest GetResult(Guid id, ManualResetEventSlim ev, int timeout)
|
|
|
+ protected override bool DoCheck2FA(string code, Guid? session)
|
|
|
{
|
|
|
- if (Responses.TryGetValue(id, out var result))
|
|
|
+ var request = new Check2FARequest { Code = code };
|
|
|
+
|
|
|
+ PrepareRequest(request);
|
|
|
+
|
|
|
+ var response = Send(IPCRequest.Check2FA(request)).GetResponse<Check2FAResponse>();
|
|
|
+ if (response != null)
|
|
|
{
|
|
|
- Responses.Remove(id, out result);
|
|
|
- Events.Remove(id, out ev);
|
|
|
- return result;
|
|
|
+ return response.Status switch
|
|
|
+ {
|
|
|
+ StatusCode.OK => response.Valid,
|
|
|
+ StatusCode.Unauthenticated => false,
|
|
|
+ _ => throw new IPCException(response.Messages),
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override bool DoPing()
|
|
|
+ {
|
|
|
try
|
|
|
{
|
|
|
- if (!ev.Wait(timeout))
|
|
|
+ var request = new PingRequest();
|
|
|
+
|
|
|
+ PrepareRequest(request);
|
|
|
+
|
|
|
+ var response = Send(IPCRequest.Ping(request), 10_000).GetResponse<PingResponse>();
|
|
|
+ if (response != null)
|
|
|
{
|
|
|
- return PipeRequest.Error(RequestError.TIMEOUT);
|
|
|
+ return response.Status switch
|
|
|
+ {
|
|
|
+ StatusCode.Error or StatusCode.BadServer or StatusCode.Incomplete => throw new IPCException(response.Messages),
|
|
|
+ _ => true
|
|
|
+ };
|
|
|
}
|
|
|
}
|
|
|
- catch (Exception e)
|
|
|
+ catch (Exception) { }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override void DoDelete(TEntity entity, string auditnote)
|
|
|
+ {
|
|
|
+ var request = new DeleteRequest<TEntity> { Item = entity };
|
|
|
+ PrepareRequest(request);
|
|
|
+
|
|
|
+ var response = Send(IPCRequest.Delete(request)).GetResponse<DeleteResponse<TEntity>>();
|
|
|
+ switch (response.Status)
|
|
|
{
|
|
|
- Console.WriteLine(e);
|
|
|
- throw;
|
|
|
+ case StatusCode.OK:
|
|
|
+ break;
|
|
|
+ case StatusCode.Unauthenticated:
|
|
|
+ throw new IPCException("Client not authenticated");
|
|
|
+ default:
|
|
|
+ throw new IPCException(response.Messages);
|
|
|
}
|
|
|
-
|
|
|
- Responses.Remove(id, out result);
|
|
|
- Events.Remove(id, out ev);
|
|
|
- return result ?? PipeRequest.Error(RequestError.UNKNOWN);
|
|
|
}
|
|
|
|
|
|
- private void Client_MessageReceived(object? sender, H.Pipes.Args.ConnectionMessageEventArgs<PipeRequest?> e)
|
|
|
+ protected override void DoDelete(IList<TEntity> entities, string auditnote)
|
|
|
{
|
|
|
- if (Events.TryGetValue(e.Message.RequestID, out var ev))
|
|
|
+ var items = entities.ToArray();
|
|
|
+ var request = new MultiDeleteRequest<TEntity> { Items = items, AuditNote = auditnote };
|
|
|
+ PrepareRequest(request);
|
|
|
+
|
|
|
+ var response = Send(IPCRequest.MultiDelete(request)).GetResponse<MultiDeleteResponse<TEntity>>();
|
|
|
+ switch (response.Status)
|
|
|
{
|
|
|
- Responses[e.Message.RequestID] = e.Message;
|
|
|
- ev.Set();
|
|
|
+ case StatusCode.OK:
|
|
|
+ break;
|
|
|
+ case StatusCode.Unauthenticated:
|
|
|
+ throw new IPCException("Client not authenticated");
|
|
|
+ default:
|
|
|
+ throw new IPCException(response.Messages);
|
|
|
}
|
|
|
- else
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override TEntity[] DoLoad(Filter<TEntity> filter = null, SortOrder<TEntity> sort = null)
|
|
|
+ {
|
|
|
+ var request = new QueryRequest<TEntity>
|
|
|
{
|
|
|
- Task.Run(() =>
|
|
|
+ Filter = filter,
|
|
|
+ Sort = sort
|
|
|
+ };
|
|
|
+ PrepareRequest(request);
|
|
|
+
|
|
|
+ var result = new List<TEntity>();
|
|
|
+ var response = Send(IPCRequest.Query(request)).GetResponse<QueryResponse<TEntity>>();
|
|
|
+ if (response.Items != null)
|
|
|
+ foreach (var row in response.Items.Rows)
|
|
|
+ result.Add(row.ToObject<TEntity>());
|
|
|
+ return result.ToArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override CoreTable DoQuery(Filter<TEntity>? filter, Columns<TEntity>? columns, SortOrder<TEntity>? sort = null)
|
|
|
+ {
|
|
|
+ var request = new QueryRequest<TEntity>
|
|
|
+ {
|
|
|
+ Columns = columns,
|
|
|
+ Filter = filter,
|
|
|
+ Sort = sort
|
|
|
+ };
|
|
|
+ PrepareRequest(request);
|
|
|
+
|
|
|
+ var response = Send(IPCRequest.Query(request)).GetResponse<QueryResponse<TEntity>>();
|
|
|
+
|
|
|
+ if (response != null)
|
|
|
+ {
|
|
|
+ return response.Status switch
|
|
|
{
|
|
|
- OnPush?.Invoke(e.Message);
|
|
|
- }).ContinueWith(task =>
|
|
|
+ StatusCode.OK => response.Items,
|
|
|
+ StatusCode.Unauthenticated => throw new IPCException("Client not authenticated", StatusCode.Unauthenticated),
|
|
|
+ _ => throw new IPCException(response.Messages),
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override Dictionary<string, CoreTable> DoQueryMultiple(Dictionary<string, IQueryDef> queries)
|
|
|
+ {
|
|
|
+ var request = new MultiQueryRequest
|
|
|
+ {
|
|
|
+ TableTypes = new(),
|
|
|
+ Filters = new(),
|
|
|
+ Columns = new(),
|
|
|
+ Sorts = new()
|
|
|
+ };
|
|
|
+ foreach (var item in queries)
|
|
|
+ {
|
|
|
+ request.TableTypes[item.Key] = item.Value.Type.EntityName();
|
|
|
+ request.Filters[item.Key] = Serialization.Serialize(item.Value.Filter);
|
|
|
+ request.Columns[item.Key] = Serialization.Serialize(item.Value.Columns);
|
|
|
+ request.Sorts[item.Key] = Serialization.Serialize(item.Value.SortOrder);
|
|
|
+ }
|
|
|
+ PrepareRequest(request);
|
|
|
+
|
|
|
+ var response = Send(IPCRequest.QueryMultiple(request)).GetResponse<MultiQueryResponse>();
|
|
|
+ if (response != null)
|
|
|
+ {
|
|
|
+ return response.Status switch
|
|
|
{
|
|
|
- if (task.Exception != null)
|
|
|
- {
|
|
|
- Logger.Send(LogType.Error, "", $"Error in IPC Client Push: {CoreUtils.FormatException(task.Exception)}");
|
|
|
- }
|
|
|
- });
|
|
|
+ StatusCode.OK => response.Tables,
|
|
|
+ StatusCode.Unauthenticated => throw new IPCException("Client not authenticated"),
|
|
|
+ _ => throw new IPCException(response.Messages),
|
|
|
+ };
|
|
|
}
|
|
|
+
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
- private void Client_Connected(object? sender, H.Pipes.Args.ConnectionEventArgs<PipeRequest> e)
|
|
|
+ protected override void DoSave(TEntity entity, string auditnote)
|
|
|
{
|
|
|
- Logger.Send(LogType.Information, "", $"Connected to Pipe: {e.Connection.PipeName}");
|
|
|
- Disconnected = false;
|
|
|
- OnConnect?.Invoke();
|
|
|
+ var request = new SaveRequest<TEntity>
|
|
|
+ {
|
|
|
+ Item = entity,
|
|
|
+ AuditNote = auditnote,
|
|
|
+ ReturnOnlyChanged = true
|
|
|
+ };
|
|
|
+ PrepareRequest(request);
|
|
|
+
|
|
|
+ var response = Send(IPCRequest.Save(request)).GetResponse<SaveResponse<TEntity>>();
|
|
|
+ switch (response.Status)
|
|
|
+ {
|
|
|
+ case StatusCode.OK:
|
|
|
+ /*var props = CoreUtils.PropertyList(typeof(TEntity), x => true, true);
|
|
|
+ entity.SetObserving(false);
|
|
|
+ foreach (var prop in props.Keys)
|
|
|
+ {
|
|
|
+ var value = CoreUtils.GetPropertyValue(response.Item, prop);
|
|
|
+ CoreUtils.SetPropertyValue(entity, prop, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ entity.CommitChanges();
|
|
|
+ entity.SetObserving(true);*/
|
|
|
+
|
|
|
+ entity.SetObserving(false);
|
|
|
+ foreach (var (key, value) in response.ChangedValues)
|
|
|
+ {
|
|
|
+ if (CoreUtils.TryGetProperty<TEntity>(key, out var property))
|
|
|
+ {
|
|
|
+ CoreUtils.SetPropertyValue(entity, key, CoreUtils.ChangeType(value, property.PropertyType));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ entity.CommitChanges();
|
|
|
+ entity.SetObserving(true);
|
|
|
+
|
|
|
+ break;
|
|
|
+ case StatusCode.Unauthenticated:
|
|
|
+ throw new IPCException("Client not authenticated");
|
|
|
+ default:
|
|
|
+ throw new IPCException(response.Messages);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- private void Client_Disconnected(object? sender, H.Pipes.Args.ConnectionEventArgs<PipeRequest> e)
|
|
|
+ protected override void DoSave(IEnumerable<TEntity> entities, string auditnote)
|
|
|
{
|
|
|
- Logger.Send(LogType.Information, "", $"Disconnected from Pipe: {e.Connection.PipeName}");
|
|
|
- foreach (var ev in Events)
|
|
|
+ var items = entities.ToArray();
|
|
|
+ var request = new MultiSaveRequest<TEntity>
|
|
|
{
|
|
|
- Responses.TryAdd(ev.Key, PipeRequest.Error(RequestError.DISCONNECTED));
|
|
|
- ev.Value.Set();
|
|
|
+ Items = items,
|
|
|
+ AuditNote = auditnote,
|
|
|
+ ReturnOnlyChanged = true
|
|
|
+ };
|
|
|
+ PrepareRequest(request);
|
|
|
+
|
|
|
+ var response = Send(IPCRequest.MultiSave(request)).GetResponse<MultiSaveResponse<TEntity>>();
|
|
|
+ switch (response.Status)
|
|
|
+ {
|
|
|
+ case StatusCode.OK:
|
|
|
+ /*var props = CoreUtils.PropertyList(typeof(TEntity), x => true, true);
|
|
|
+ for (var i = 0; i < items.Length; i++)
|
|
|
+ {
|
|
|
+ items[i].SetObserving(false);
|
|
|
+ foreach (var prop in props.Keys)
|
|
|
+ {
|
|
|
+ var value = CoreUtils.GetPropertyValue(response.Items[i], prop);
|
|
|
+ CoreUtils.SetPropertyValue(items[i], prop, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ //CoreUtils.DeepClone<TEntity>(response.Items[i], items[i]);
|
|
|
+ items[i].CommitChanges();
|
|
|
+ items[i].SetObserving(true);
|
|
|
+ }*/
|
|
|
+ for (int i = 0; i < items.Length; ++i)
|
|
|
+ {
|
|
|
+ var entity = items[i];
|
|
|
+ var changedValues = response.ChangedValues[i];
|
|
|
+
|
|
|
+ entity.SetObserving(false);
|
|
|
+ foreach (var (key, value) in changedValues)
|
|
|
+ {
|
|
|
+ if (CoreUtils.TryGetProperty<TEntity>(key, out var property))
|
|
|
+ {
|
|
|
+ CoreUtils.SetPropertyValue(entity, key, CoreUtils.ChangeType(value, property.PropertyType));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ entity.CommitChanges();
|
|
|
+ entity.SetObserving(true);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case StatusCode.Unauthenticated:
|
|
|
+ throw new IPCException("Client not authenticated");
|
|
|
+ default:
|
|
|
+ throw new IPCException(response.Messages);
|
|
|
}
|
|
|
- Disconnected = true;
|
|
|
- OnDisconnect?.Invoke();
|
|
|
}
|
|
|
|
|
|
- public void Dispose()
|
|
|
+ protected override ValidationData DoValidate(Guid session)
|
|
|
{
|
|
|
- Client.DisposeAsync().AsTask().Wait();
|
|
|
+ return Validate(
|
|
|
+ null, null, false, session);
|
|
|
}
|
|
|
|
|
|
- ~IPCClient()
|
|
|
+ protected override ValidationData DoValidate(string pin, Guid session)
|
|
|
{
|
|
|
- Dispose();
|
|
|
+ return Validate(
|
|
|
+ null, pin, true, session);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override ValidationData DoValidate(string userid, string password, Guid session)
|
|
|
+ {
|
|
|
+ return Validate(
|
|
|
+ userid, password, false, session);
|
|
|
+ }
|
|
|
+
|
|
|
+ private ValidationData Validate(string? userid, string? password, bool usePin, Guid session = default)
|
|
|
+ {
|
|
|
+ var ticks = DateTime.Now.ToUniversalTime().Ticks.ToString();
|
|
|
+ var request = new ValidateRequest { UsePIN = usePin };
|
|
|
+
|
|
|
+ if (usePin)
|
|
|
+ {
|
|
|
+ request.UserID = Encryption.Encrypt(ticks, "wCq9rryEJEuHIifYrxRjxg", true);
|
|
|
+ request.Password = Encryption.Encrypt(ticks, "7mhvLnqMwkCAzN+zNGlyyg", true);
|
|
|
+ request.PIN = password;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+<<<<<<< Updated upstream
|
|
|
+ Task.Run(() =>
|
|
|
+ {
|
|
|
+ OnPush?.Invoke(e.Message);
|
|
|
+ }).ContinueWith(task =>
|
|
|
+ {
|
|
|
+ if (task.Exception != null)
|
|
|
+ {
|
|
|
+ Logger.Send(LogType.Error, "", $"Error in IPC Client Push: {CoreUtils.FormatException(task.Exception)}");
|
|
|
+ }
|
|
|
+ });
|
|
|
+=======
|
|
|
+ request.UserID = userid;
|
|
|
+ request.Password = password;
|
|
|
+>>>>>>> Stashed changes
|
|
|
+ }
|
|
|
+ request.Credentials.Platform = ClientFactory.Platform;
|
|
|
+ request.Credentials.Version = ClientFactory.Version;
|
|
|
+ PrepareRequest(request, false);
|
|
|
+ if(session != Guid.Empty)
|
|
|
+ {
|
|
|
+ request.Credentials.Session = session;
|
|
|
+ }
|
|
|
+
|
|
|
+ var response = Send(IPCRequest.Validate(request), 10000).GetResponse<ValidateResponse>();
|
|
|
+ if (response != null)
|
|
|
+ if (response.Status.Equals(StatusCode.OK))
|
|
|
+ {
|
|
|
+ LocalCache.Password = password;
|
|
|
+ return new ValidationData(
|
|
|
+ response.ValidationResult,
|
|
|
+ response.UserID,
|
|
|
+ response.UserGuid,
|
|
|
+ response.SecurityID,
|
|
|
+ response.Session,
|
|
|
+ response.Recipient2FA,
|
|
|
+ response.PasswordExpiration
|
|
|
+
|
|
|
+ );
|
|
|
+ }
|
|
|
+ else if (response.Status == StatusCode.BadServer)
|
|
|
+ {
|
|
|
+ throw new IPCException(response.Messages);
|
|
|
+ }
|
|
|
+
|
|
|
+ return new ValidationData(
|
|
|
+ ValidationResult.INVALID,
|
|
|
+ "",
|
|
|
+ Guid.Empty,
|
|
|
+ Guid.Empty,
|
|
|
+ Guid.Empty,
|
|
|
+ null,
|
|
|
+ DateTime.MinValue
|
|
|
+ );
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
}
|