using InABox.Client.IPC; using InABox.Clients; using InABox.Core; namespace InABox.IPC { internal static class LocalCache { public static string Password { get; set; } } public class IPCClient : BaseClient where TEntity : Entity, new() { private IPCClientTransport _pipe; public IPCClient(string pipeName) { _pipe = IPCClientFactory.GetClient(pipeName); Timeout = TimeSpan.FromSeconds(300); } public override bool IsConnected() => _pipe?.Disconnected == false; private static string[]? _types; public override string[] SupportedTypes() { _types ??= CoreUtils.Entities .Where(x => x.GetInterfaces().Contains(typeof(IPersistent))) .Select(x => x.EntityName().Replace(".", "_")) .ToArray(); return _types; } public override DatabaseInfo? Info() { try { var request = new InfoRequest(); PrepareRequest(request, false); var response = Send(IPCMessage.Info(request), 5000)?.GetResponse(); return response?.Info; } catch (Exception) { return new DatabaseInfo(); } } private void PrepareRequest(Request request, bool doCredentials = true) { if(request is not ValidateRequest && _pipe.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); } private IPCMessage Send(IPCMessage request, int? timeout = null) { return _pipe.Send(request, timeout ?? Convert.ToInt32(Timeout.TotalMilliseconds)); } protected override bool DoCheck2FA(string code, Guid? session) { var request = new Check2FARequest(code); PrepareRequest(request); var response = Send(IPCMessage.Check2FA(request))?.GetResponse(); if (response != null) { return response.Status switch { StatusCode.OK => response.Valid, StatusCode.Unauthenticated => false, _ => throw new IPCException(response.Messages), }; } return false; } protected override bool DoPing() { try { var request = new PingRequest(); PrepareRequest(request); var response = Send(IPCMessage.Ping(request), 10_000)?.GetResponse(); if (response != null) { return response.Status switch { StatusCode.Error or StatusCode.BadServer or StatusCode.Incomplete => throw new IPCException(response.Messages), _ => true }; } } catch (Exception) { } return false; } protected override void DoDelete(TEntity entity, string auditnote) { var request = new DeleteRequest(entity, auditnote); PrepareRequest(request); var response = Send(IPCMessage.Delete(request)).GetResponse>(); switch (response.Status) { case StatusCode.OK: break; case StatusCode.Unauthenticated: throw new IPCException("Client not authenticated"); default: throw new IPCException(response.Messages); } } protected override void DoDelete(IEnumerable entities, string auditnote) { var items = entities.AsArray(); var request = new MultiDeleteRequest(items, auditnote); PrepareRequest(request); var response = Send(IPCMessage.MultiDelete(request)).GetResponse>(); switch (response.Status) { case StatusCode.OK: break; case StatusCode.Unauthenticated: throw new IPCException("Client not authenticated"); default: throw new IPCException(response.Messages); } } protected override TEntity[] DoLoad(Filter? filter = null, SortOrder? sort = null, CoreRange? range = null) { var request = new QueryRequest(filter, null, sort, range); PrepareRequest(request); var response = Send(IPCMessage.Query(request)).GetResponse>(); if (response.Items != null) { return response.Items.ToArray(); } else { return []; } } protected override CoreTable DoQuery(Filter? filter, Columns? columns, SortOrder? sort = null, CoreRange? range = null) { var request = new QueryRequest(filter, columns, sort, range); PrepareRequest(request); var response = Send(IPCMessage.Query(request)).GetResponse>(); return response.Status switch { StatusCode.OK => response.Items, StatusCode.Unauthenticated => throw new IPCException("Client not authenticated", StatusCode.Unauthenticated), _ => throw new IPCException(response.Messages), }; } protected override Dictionary DoQueryMultiple(Dictionary queries) { var request = new MultiQueryRequest(); foreach (var item in queries) { request.AddQuery(item.Key, item.Value); } PrepareRequest(request); var response = Send(IPCMessage.QueryMultiple(request)).GetResponse(); if (response != null) { return response.Status switch { StatusCode.OK => response.Tables, StatusCode.Unauthenticated => throw new IPCException("Client not authenticated"), _ => throw new IPCException(response.Messages), }; } return null; } protected override void DoSave(TEntity entity, string auditnote) { var request = new SaveRequest(entity, auditnote) { ReturnOnlyChanged = true }; PrepareRequest(request); var response = Send(IPCMessage.Save(request)).GetResponse>(); 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(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); } } protected override void DoSave(IEnumerable entities, string auditnote) { var items = entities.ToArray(); var request = new MultiSaveRequest(items, auditnote) { ReturnOnlyChanged = true }; PrepareRequest(request); var response = Send(IPCMessage.MultiSave(request)).GetResponse>(); 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(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(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); } } protected override IValidationData DoValidate(Guid session) { return Validate( null, null, false, session); } protected override IValidationData DoValidate(string pin, Guid session) { return Validate( null, pin, true, session); } protected override IValidationData DoValidate(string userid, string password, Guid session) { return Validate( userid, password, false, session); } private IValidationData 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 { request.UserID = userid; request.Password = password; } request.Credentials.Platform = ClientFactory.Platform; request.Credentials.Version = ClientFactory.Version; PrepareRequest(request, false); if(session != Guid.Empty) { request.Credentials.Session = session; } var response = Send(IPCMessage.Validate(request), 10000).GetResponse(); if (response != null) if (response.Status.Equals(StatusCode.OK)) { LocalCache.Password = password; return new ValidationData( response.ValidationStatus, 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( ValidationStatus.INVALID, "", Guid.Empty, Guid.Empty, Guid.Empty, null, DateTime.MinValue ); } public override string Version() { try { var request = new VersionRequest(); PrepareRequest(request, false); var response = Send(IPCMessage.Version(request)).GetResponse(); return response.Version; } catch (Exception) { return ""; } } public override string ReleaseNotes() { try { var request = new ReleaseNotesRequest(); PrepareRequest(request, false); var response = Send(IPCMessage.ReleaseNotes(request)).GetResponse(); return response.ReleaseNotes; } catch (Exception) { return ""; } } public override byte[]? Installer() { try { var request = new InstallerRequest(); PrepareRequest(request, false); var response = Send(IPCMessage.Installer(request)).GetResponse(); return response.Installer; } catch (Exception) { return null; } } } }