/* Copyright (c) 2019 Ahmed Kh. Zamil Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import '../Sockets/TCPSocket.dart'; import 'DistributedPropertyContext.dart'; import '../../Data/PropertyValue.dart'; import '../../Resource/Template/PropertyTemplate.dart'; import '../../Core/AsyncException.dart'; import '../NetworkBuffer.dart'; import '../Sockets/ISocket.dart'; import '../../Core/AsyncQueue.dart'; import '../../Core/ExceptionCode.dart'; import '../../Core/ErrorType.dart'; import '../../Resource/Warehouse.dart'; import '../Sockets/SocketState.dart'; import 'dart:math'; import '../../Resource/IStore.dart'; import '../../Resource/IResource.dart'; import '../Packets/IIPPacket.dart'; import '../Packets/IIPAuthPacket.dart'; import '../../Security/Authority/Session.dart'; import '../../Data/DC.dart'; import '../../Data/KeyList.dart'; import '../../Core/AsyncReply.dart'; import '../SendList.dart'; import '../../Security/Authority/SourceAttributeType.dart'; import '../../Resource/Instance.dart'; import '../../Security/Authority/AuthenticationType.dart'; import '../../Security/Authority/ClientAuthentication.dart'; import '../../Security/Authority/HostAuthentication.dart'; import 'DistributedResource.dart'; import 'DistributedResourceQueueItem.dart'; import 'DistributedResourceQueueItemType.dart'; import '../Packets/IIPAuthPacketAction.dart'; import '../Packets/IIPAuthPacketCommand.dart'; import '../Packets/IIPPacketAction.dart'; import '../Packets/IIPPacketCommand.dart'; import '../Packets/IIPPacketEvent.dart'; import '../Packets/IIPPacketReport.dart'; import '../Packets/IIPAuthPacketMethod.dart'; import '../../Data/BinaryList.dart'; import '../NetworkConnection.dart'; import '../../Data/Guid.dart'; import '../../Resource/Template/ResourceTemplate.dart'; import '../../Security/Permissions/Ruling.dart'; import '../../Security/Permissions/ActionType.dart'; import '../../Data/Codec.dart'; import '../../Data/DataType.dart'; import '../../Data/Structure.dart'; import '../../Core/ProgressType.dart'; import '../../Security/Integrity/SHA256.dart'; import '../../Resource/ResourceTrigger.dart'; class DistributedConnection extends NetworkConnection with IStore { //public delegate void ReadyEvent(DistributedConnection sender); //public delegate void ErrorEvent(DistributedConnection sender, byte errorCode, string errorMessage); /// /// Ready event is raised when the connection is fully established. /// //public event ReadyEvent OnReady; /// /// Error event /// //public event ErrorEvent OnError; AsyncReply _openReply; IIPPacket _packet = new IIPPacket(); IIPAuthPacket _authPacket = new IIPAuthPacket(); Session _session; DC _localPassword; DC _localNonce, _remoteNonce; bool _ready = false, _readyToEstablish = false; DateTime _loginDate; KeyList _resources = new KeyList(); KeyList> _resourceRequests = new KeyList>(); KeyList> _templateRequests = new KeyList>(); KeyList> _pathRequests = new KeyList>(); Map _templates = new Map(); KeyList> _requests = new KeyList>(); int _callbackCounter = 0; AsyncQueue _queue = new AsyncQueue(); /// /// Local username to authenticate ourselves. /// String get localUsername => _session.localAuthentication.username; /// /// Peer's username. /// String get remoteUsername => _session.remoteAuthentication.username;// { get; set; } /// /// Working domain. /// //public string Domain { get { return domain; } } /// /// The session related to this connection. /// Session get session => _session; /// /// Distributed server responsible for this connection, usually for incoming connections. /// //public DistributedServer Server bool remove(IResource resource) { // nothing to do return true; } /// /// Send data to the other end as parameters /// /// Values will be converted to bytes then sent. SendList sendParams([AsyncReply> reply = null]) { return new SendList(this, reply); } /// /// Send raw data through the connection. /// /// Data to send. void send(DC data) { //Console.WriteLine("Client: {0}", Data.length); //Global.Counters["IIP Sent Packets"]++; super.send(data); } AsyncReply trigger(ResourceTrigger trigger) { if (trigger == ResourceTrigger.Open) { if (instance.attributes.containsKey("username") && instance.attributes.containsKey("password")) { var hostname = instance.name.split ("://").skip(1).join("://").split("/")[0]; // assign domain from hostname if not provided var address = hostname.split(":")[0]; var port = int.parse(hostname.split(":")[1]); var username = instance.attributes["username"].toString(); var domain = instance.attributes.containsKey("domain") ? instance.attributes["domain"] : address; _session = new Session(new ClientAuthentication() , new HostAuthentication()); _session.localAuthentication.domain = domain; _session.localAuthentication.username = username; _localPassword = DC.stringToBytes(instance.attributes["password"].toString()); _openReply = new AsyncReply(); var sock = new TCPSocket(); sock.connect(domain, port).then((x){ assign(sock); //rt.trigger(true); }).error((x)=>_openReply.triggerError(x)); return _openReply; } } return new AsyncReply.ready(true); } /// /// KeyList to store user variables related to this connection. /// final KeyList variables = new KeyList(); /// /// IResource interface. /// Instance instance; /// /// Assign a socket to the connection. /// /// Any socket that implements ISocket. assign(ISocket socket) { super.assign(socket); session.remoteAuthentication.source.attributes.add(SourceAttributeType.IPv4, socket.remoteEndPoint.address); session.remoteAuthentication.source.attributes.add(SourceAttributeType.Port, socket.remoteEndPoint.port); session.localAuthentication.source.attributes.add(SourceAttributeType.IPv4, socket.localEndPoint.address); session.localAuthentication.source.attributes.add(SourceAttributeType.Port, socket.localEndPoint.port); if (session.localAuthentication.type == AuthenticationType.Client) { // declare (Credentials -> No Auth, No Enctypt) var un = DC.stringToBytes(session.localAuthentication.username); var dmn = DC.stringToBytes(session.localAuthentication.domain);// domain); if (socket.state == SocketState.Established) { sendParams() .addUint8(0x60) .addUint8(dmn.length) .addDC(dmn) .addDC(_localNonce) .addUint8(un.length) .addDC(un) .done(); } else { socket.on("connect", () { // declare (Credentials -> No Auth, No Enctypt) sendParams() .addUint8(0x60) .addUint8(dmn.length) .addDC(dmn) .addDC(_localNonce) .addUint8(un.length) .addDC(un) .done(); }); } } } /// /// Create a new distributed connection. /// /// Socket to transfer data through. /// Working domain. /// Username. /// Password. DistributedConnection.connect(ISocket socket, String domain, String username, String password) { _session = new Session(new ClientAuthentication() , new HostAuthentication()); _session.localAuthentication.domain = domain; _session.localAuthentication.username = username; _localPassword = DC.stringToBytes(password); init(); assign(socket); } /// /// Create a new instance of a distributed connection /// DistributedConnection() { //myId = Global.GenerateCode(12); // localParams.Host = DistributedParameters.HostType.Host; _session = new Session(new HostAuthentication(), new ClientAuthentication()); init(); } String link(IResource resource) { if (resource is DistributedResource) { var r = resource as DistributedResource; if (r.instance.store == this) return this.instance.name + "/" + r.id.toString(); } return null; } void init() { _queue.then((x) { if (x.type == DistributedResourceQueueItemType.Event) x.resource.emitEventByIndex(x.index, x.value); else x.resource.updatePropertyByIndex(x.index, x.value); }); var r = new Random(); _localNonce = new DC(32); for(var i = 0; i < 32; i++) _localNonce[i] = r.nextInt(255); } int processPacket(DC msg, int offset, int ends, NetworkBuffer data, int chunkId) { var packet = new IIPPacket(); if (_ready) { var rt = packet.parse(msg, offset, ends); if (rt <= 0) { var size = ends - offset; data.holdFor(msg, offset, size, size - rt); return ends; } else { offset += rt; if (packet.command == IIPPacketCommand.Event) { switch (packet.event) { case IIPPacketEvent.ResourceReassigned: iipEventResourceReassigned(packet.resourceId, packet.newResourceId); break; case IIPPacketEvent.ResourceDestroyed: iipEventResourceDestroyed(packet.resourceId); break; case IIPPacketEvent.PropertyUpdated: iipEventPropertyUpdated(packet.resourceId, packet.methodIndex, packet.content); break; case IIPPacketEvent.EventOccurred: iipEventEventOccurred(packet.resourceId, packet.methodIndex, packet.content); break; case IIPPacketEvent.ChildAdded: iipEventChildAdded(packet.resourceId, packet.childId); break; case IIPPacketEvent.ChildRemoved: iipEventChildRemoved(packet.resourceId, packet.childId); break; case IIPPacketEvent.Renamed: iipEventRenamed(packet.resourceId, packet.content); break; case IIPPacketEvent.AttributesUpdated: iipEventAttributesUpdated(packet.resourceId, packet.content); break; } } else if (packet.command == IIPPacketCommand.Request) { switch (packet.action) { // Manage case IIPPacketAction.AttachResource: iipRequestAttachResource(packet.callbackId, packet.resourceId); break; case IIPPacketAction.ReattachResource: iipRequestReattachResource(packet.callbackId, packet.resourceId, packet.resourceAge); break; case IIPPacketAction.DetachResource: iipRequestDetachResource(packet.callbackId, packet.resourceId); break; case IIPPacketAction.CreateResource: iipRequestCreateResource(packet.callbackId, packet.storeId, packet.resourceId, packet.content); break; case IIPPacketAction.DeleteResource: iipRequestDeleteResource(packet.callbackId, packet.resourceId); break; case IIPPacketAction.AddChild: iipRequestAddChild(packet.callbackId, packet.resourceId, packet.childId); break; case IIPPacketAction.RemoveChild: iipRequestRemoveChild(packet.callbackId, packet.resourceId, packet.childId); break; case IIPPacketAction.RenameResource: iipRequestRenameResource(packet.callbackId, packet.resourceId, packet.content); break; // Inquire case IIPPacketAction.TemplateFromClassName: iipRequestTemplateFromClassName(packet.callbackId, packet.className); break; case IIPPacketAction.TemplateFromClassId: iipRequestTemplateFromClassId(packet.callbackId, packet.classId); break; case IIPPacketAction.TemplateFromResourceId: iipRequestTemplateFromResourceId(packet.callbackId, packet.resourceId); break; case IIPPacketAction.QueryLink: iipRequestQueryResources(packet.callbackId, packet.resourceLink); break; case IIPPacketAction.ResourceChildren: iipRequestResourceChildren(packet.callbackId, packet.resourceId); break; case IIPPacketAction.ResourceParents: iipRequestResourceParents(packet.callbackId, packet.resourceId); break; case IIPPacketAction.ResourceHistory: iipRequestInquireResourceHistory(packet.callbackId, packet.resourceId, packet.fromDate, packet.toDate); break; // Invoke case IIPPacketAction.InvokeFunctionArrayArguments: iipRequestInvokeFunctionArrayArguments(packet.callbackId, packet.resourceId, packet.methodIndex, packet.content); break; case IIPPacketAction.InvokeFunctionNamedArguments: iipRequestInvokeFunctionNamedArguments(packet.callbackId, packet.resourceId, packet.methodIndex, packet.content); break; case IIPPacketAction.GetProperty: iipRequestGetProperty(packet.callbackId, packet.resourceId, packet.methodIndex); break; case IIPPacketAction.GetPropertyIfModified: iipRequestGetPropertyIfModifiedSince(packet.callbackId, packet.resourceId, packet.methodIndex, packet.resourceAge); break; case IIPPacketAction.SetProperty: iipRequestSetProperty(packet.callbackId, packet.resourceId, packet.methodIndex, packet.content); break; // Attribute case IIPPacketAction.GetAllAttributes: iipRequestGetAttributes(packet.callbackId, packet.resourceId, packet.content, true); break; case IIPPacketAction.UpdateAllAttributes: iipRequestUpdateAttributes(packet.callbackId, packet.resourceId, packet.content, true); break; case IIPPacketAction.ClearAllAttributes: iipRequestClearAttributes(packet.callbackId, packet.resourceId, packet.content, true); break; case IIPPacketAction.GetAttributes: iipRequestGetAttributes(packet.callbackId, packet.resourceId, packet.content, false); break; case IIPPacketAction.UpdateAttributes: iipRequestUpdateAttributes(packet.callbackId, packet.resourceId, packet.content, false); break; case IIPPacketAction.ClearAttributes: iipRequestClearAttributes(packet.callbackId, packet.resourceId, packet.content, false); break; } } else if (packet.command == IIPPacketCommand.Reply) { switch (packet.action) { // Manage case IIPPacketAction.AttachResource: iipReply(packet.callbackId, [packet.classId, packet.resourceAge, packet.resourceLink, packet.content]); break; case IIPPacketAction.ReattachResource: iipReply(packet.callbackId, [packet.resourceAge, packet.content]); break; case IIPPacketAction.DetachResource: iipReply (packet.callbackId); break; case IIPPacketAction.CreateResource: iipReply(packet.callbackId, [packet.resourceId]); break; case IIPPacketAction.DeleteResource: case IIPPacketAction.AddChild: case IIPPacketAction.RemoveChild: case IIPPacketAction.RenameResource: iipReply(packet.callbackId); break; // Inquire case IIPPacketAction.TemplateFromClassName: case IIPPacketAction.TemplateFromClassId: case IIPPacketAction.TemplateFromResourceId: iipReply(packet.callbackId, [ResourceTemplate.parse(packet.content)]); break; case IIPPacketAction.QueryLink: case IIPPacketAction.ResourceChildren: case IIPPacketAction.ResourceParents: case IIPPacketAction.ResourceHistory: iipReply(packet.callbackId, [packet.content]); break; // Invoke case IIPPacketAction.InvokeFunctionArrayArguments: case IIPPacketAction.InvokeFunctionNamedArguments: iipReplyInvoke(packet.callbackId, packet.content); break; case IIPPacketAction.GetProperty: iipReply(packet.callbackId, [packet.content]); break; case IIPPacketAction.GetPropertyIfModified: iipReply(packet.callbackId, [packet.content]); break; case IIPPacketAction.SetProperty: iipReply(packet.callbackId); break; // Attribute case IIPPacketAction.GetAllAttributes: case IIPPacketAction.GetAttributes: iipReply(packet.callbackId, [packet.content]); break; case IIPPacketAction.UpdateAllAttributes: case IIPPacketAction.UpdateAttributes: case IIPPacketAction.ClearAllAttributes: case IIPPacketAction.ClearAttributes: iipReply(packet.callbackId); break; } } else if (packet.command == IIPPacketCommand.Report) { switch (packet.report) { case IIPPacketReport.ManagementError: iipReportError(packet.callbackId, ErrorType.Management, packet.errorCode, null); break; case IIPPacketReport.ExecutionError: iipReportError(packet.callbackId, ErrorType.Exception, packet.errorCode, packet.errorMessage); break; case IIPPacketReport.ProgressReport: iipReportProgress(packet.callbackId, ProgressType.Execution, packet.progressValue, packet.progressMax); break; case IIPPacketReport.ChunkStream: iipReportChunk(packet.callbackId, packet.content); break; } } } } else { var rt = _authPacket.parse(msg, offset, ends); if (rt <= 0) { data.holdForNeeded(msg, ends -rt); return ends; } else { offset += rt; if (session.localAuthentication.type == AuthenticationType.Host) { if (_authPacket.command == IIPAuthPacketCommand.Declare) { if (_authPacket.remoteMethod == IIPAuthPacketMethod.Credentials && _authPacket.localMethod == IIPAuthPacketMethod.None) { /* server.membership.userExists(_authPacket.remoteUsername, _authPacket.domain).then((x) { if (x) { _session.remoteAuthentication.username = _authPacket.remoteUsername; _remoteNonce = _authPacket.remoteNonce; _session.remoteAuthentication.domain = _authPacket.domain; sendParams() .addUint8(0xa0) .addDC(_localNonce) .done(); } else { sendParams().addUint8(0xc0).addUint8(1).addUint16(14).addString("User not found").done(); } }); */ } } else if (_authPacket.command == IIPAuthPacketCommand.Action) { if (_authPacket.action == IIPAuthPacketAction.AuthenticateHash) { var remoteHash = _authPacket.hash; /* server.membership.getPassword(_session.remoteAuthentication.username, _session.remoteAuthentication.domain).then((pw) { if (pw != null) { //var hash = hashFunc.ComputeHash(BinaryList.ToBytes(pw, remoteNonce, localNonce)); var hash = SHA256.compute((new BinaryList()) .addDC(pw) .addDC(_remoteNonce) .addDC(_localNonce) .toDC()); if (hash.sequenceEqual(remoteHash)) { // send our hash //var localHash = hashFunc.ComputeHash(BinaryList.ToBytes(localNonce, remoteNonce, pw)); //SendParams((byte)0, localHash); var localHash = SHA256.compute ((new BinaryList()).addDC(_localNonce).addDC(_remoteNonce).addDC(pw).toDC()); sendParams().addUint8(0).addDC(localHash).done(); _readyToEstablish = true; } else { sendParams().addUint8(0xc0).addUint8(1).addUint16(5).addString("Error").done(); } } }); */ } else if (_authPacket.action == IIPAuthPacketAction.NewConnection) { if (_readyToEstablish) { var r = new Random(); session.id = new DC(32); for(var i = 0; i < 32; i++) session.id[i] = r.nextInt(255); sendParams() .addUint8(0x28) .addDC(session.id) .done(); _ready = true; _openReply.trigger(true); emitArgs("ready", []); //OnReady?.Invoke(this); // server.membership.login(session); } } } } else if (_session.localAuthentication.type == AuthenticationType.Client) { if (_authPacket.command == IIPAuthPacketCommand.Acknowledge) { _remoteNonce = _authPacket.remoteNonce; // send our hash var localHash = SHA256.compute(new BinaryList() .addDC(_localPassword) .addDC(_localNonce) .addDC(_remoteNonce) .toDC()); sendParams() .addUint8(0) .addDC(localHash) .done(); //SendParams((byte)0, localHash); } else if (_authPacket.command == IIPAuthPacketCommand.Action) { if (_authPacket.action == IIPAuthPacketAction.AuthenticateHash) { // check if the server knows my password var remoteHash = SHA256.compute(new BinaryList() .addDC(_remoteNonce) .addDC(_localNonce) .addDC(_localPassword) .toDC()); if (remoteHash.sequenceEqual(_authPacket.hash)) { // send establish request sendParams() .addUint8(0x20) .addUint16(0) .done(); } else { sendParams() .addUint8(0xc0) .addUint8(1) .addUint16(5) .addString("Error") .done(); //SendParams((byte)0xc0, 1, 5, DC.ToBytes("Error")); } } else if (_authPacket.action == IIPAuthPacketAction.ConnectionEstablished) { session.id = _authPacket.sessionId; _ready = true; _openReply.trigger(true); emitArgs("ready", []); //OnReady?.Invoke(this); } } else if (_authPacket.command == IIPAuthPacketCommand.Error) { var ex = AsyncException(ErrorType.Management, _authPacket.errorCode, _authPacket.errorMessage); _openReply.triggerError(ex); emitArgs("error", [ex]); //OnError?.Invoke(this, authPacket.ErrorCode, authPacket.ErrorMessage); close(); } } } } return offset; //if (offset < ends) // processPacket(msg, offset, ends, data, chunkId); } @override void dataReceived(NetworkBuffer data) { // Console.WriteLine("DR " + hostType + " " + data.Available + " " + RemoteEndPoint.ToString()); var msg = data.read(); int offset = 0; int ends = msg.length; var packs = new List(); var chunkId = (new Random()).nextInt(1000000); while (offset < ends) { offset = processPacket(msg, offset, ends, data, chunkId); } } /// /// Resource interface /// /// Resource trigger. /// //AsyncReply trigger(ResourceTrigger trigger) //{ // return new AsyncReply(); //} /// /// Store interface. /// /// Resource. /// bool put(IResource resource) { if (Codec.isLocalResource(resource, this)) _resources.add((resource as DistributedResource).id, resource); // else .. put it in the server.... return true; } bool record(IResource resource, String propertyName, value, int age, DateTime dateTime) { // nothing to do return true; } bool modify(IResource resource, String propertyName, value, int age, DateTime dateTime) { // nothing to do return true; } /// /// Send IIP request. /// /// Packet action. /// Arguments to send. /// SendList sendRequest(int action) { var reply = new AsyncReply>(); var c = _callbackCounter++; // avoid thread racing _requests.add(c, reply); return sendParams(reply).addUint8(0x40 | action).addUint32(c); } //int _maxcallerid = 0; SendList sendReply(int action, int callbackId) { return sendParams().addUint8((0x80 | action)).addUint32(callbackId); } SendList sendEvent(int evt) { return sendParams().addUint8((evt)); } AsyncReply sendInvokeByArrayArguments(int instanceId, int index, List parameters) { var pb = Codec.composeVarArray(parameters, this, true); var reply = new AsyncReply(); var c = _callbackCounter++; _requests.add(c, reply); sendParams().addUint8(0x40 | IIPPacketAction.InvokeFunctionArrayArguments) .addUint32(c) .addUint32(instanceId) .addUint8(index) .addDC(pb) .done(); return reply; } AsyncReply sendInvokeByNamedArguments(int instanceId, int index, Structure parameters) { var pb = Codec.composeStructure(parameters, this, true, true, true); var reply = new AsyncReply(); var c = _callbackCounter++; _requests.add(c, reply); sendParams().addUint8(0x40 | IIPPacketAction.InvokeFunctionNamedArguments) .addUint32(c) .addUint32(instanceId) .addUint8(index) .addDC(pb) .done(); return reply; } void sendError(ErrorType type, int callbackId, int errorCode, [String errorMessage = ""]) { var msg = DC.stringToBytes(errorMessage); if (type == ErrorType.Management) sendParams() .addUint8(0xC0 | IIPPacketReport.ManagementError) .addUint32(callbackId) .addUint16(errorCode) .done(); else if (type == ErrorType.Exception) sendParams() .addUint8(0xC0 | IIPPacketReport.ExecutionError) .addUint32(callbackId) .addUint16(errorCode) .addUint16(msg.length) .addDC(msg) .done(); } void sendProgress(int callbackId, int value, int max) { sendParams() .addUint8(0xC0 | IIPPacketReport.ProgressReport) .addUint32(callbackId) .addInt32(value) .addInt32(max) .done(); //SendParams(, callbackId, value, max); } void sendChunk(int callbackId, dynamic chunk) { var c = Codec.compose(chunk, this, true); sendParams() .addUint8(0xC0 | IIPPacketReport.ChunkStream) .addUint32(callbackId) .addDC(c) .done(); } void iipReply(int callbackId, [List results = null]) { var req = _requests.take(callbackId); req?.trigger(results); } void iipReplyInvoke(int callbackId, DC result) { var req = _requests.take(callbackId); Codec.parse(result, 0, this).then((rt) { req?.trigger(rt); }); } void iipReportError(int callbackId, ErrorType errorType, int errorCode, String errorMessage) { var req = _requests.take(callbackId); req?.triggerError(new AsyncException(errorType, errorCode, errorMessage)); } void iipReportProgress(int callbackId, ProgressType type, int value, int max) { var req = _requests[callbackId]; req?.triggerProgress(type, value, max); } void iipReportChunk(int callbackId, DC data) { if (_requests.containsKey(callbackId)) { var req = _requests[callbackId]; Codec.parse(data, 0, this).then((x) { req.triggerChunk(x); }); } } void iipEventResourceReassigned(int resourceId, int newResourceId) { } void iipEventResourceDestroyed(int resourceId) { if (_resources.contains(resourceId)) { var r = _resources[resourceId]; _resources.remove(resourceId); r.destroy(); } } void iipEventPropertyUpdated(int resourceId, int index, DC content) { fetch(resourceId).then((r) { var item = new AsyncReply(); _queue.add(item); Codec.parse(content, 0, this).then((arguments) { var pt = r.instance.template.getPropertyTemplateByIndex(index); if (pt != null) { item.trigger(new DistributedResourceQueueItem(r as DistributedResource, DistributedResourceQueueItemType.Propery, arguments, index)); } else { // ft found, fi not found, this should never happen _queue.remove(item); } }); }); /* if (resources.Contains(resourceId)) { // push to the queue to gaurantee serialization var reply = new AsyncReply(); queue.Add(reply); var r = resources[resourceId]; Codec.parse(content, 0, this).then((arguments) => { if (!r.IsAttached) { // property updated before the template is received r.AddAfterAttachement(reply, new DistributedResourceQueueItem((DistributedResource)r, DistributedResourceQueueItem.DistributedResourceQueueItemType.Propery, arguments, index)); } else { var pt = r.instance.template.GetPropertyTemplate(index); if (pt != null) { reply.trigger(new DistributedResourceQueueItem((DistributedResource)r, DistributedResourceQueueItem.DistributedResourceQueueItemType.Propery, arguments, index)); } else { // ft found, fi not found, this should never happen queue.Remove(reply); } } }); } */ } void iipEventEventOccurred(int resourceId, int index, DC content) { fetch(resourceId).then((r) { // push to the queue to gaurantee serialization var item = new AsyncReply(); _queue.add(item); Codec.parseVarArray(content, 0, content.length, this).then((arguments) { var et = r.instance.template.getEventTemplateByIndex(index); if (et != null) { item.trigger(new DistributedResourceQueueItem(r, DistributedResourceQueueItemType.Event, arguments, index)); } else { // ft found, fi not found, this should never happen _queue.remove(item); } }); }); /* if (resources.Contains(resourceId)) { // push to the queue to gaurantee serialization var reply = new AsyncReply(); var r = resources[resourceId]; queue.Add(reply); Codec.parseVarArray(content, this).then((arguments) => { if (!r.IsAttached) { // event occurred before the template is received r.AddAfterAttachement(reply, new DistributedResourceQueueItem((DistributedResource)r, DistributedResourceQueueItem.DistributedResourceQueueItemType.Event, arguments, index)); } else { var et = r.instance.template.GetEventTemplate(index); if (et != null) { reply.trigger(new DistributedResourceQueueItem((DistributedResource)r, DistributedResourceQueueItem.DistributedResourceQueueItemType.Event, arguments, index)); } else { // ft found, fi not found, this should never happen queue.Remove(reply); } } }); } */ } void iipEventChildAdded(int resourceId, int childId) { fetch(resourceId).then((parent) { fetch(childId).then((child) { parent.instance.children.add(child); }); }); } void iipEventChildRemoved(int resourceId, int childId) { fetch(resourceId).then((parent) { fetch(childId).then((child) { parent.instance.children.remove(child); }); }); } void iipEventRenamed(int resourceId, DC name) { fetch(resourceId).then((resource) { resource.instance.attributes["name"] = name.getString(0, name.length); }); } void iipEventAttributesUpdated(int resourceId, DC attributes) { fetch(resourceId).then((resource) { var attrs = attributes.getStringArray(0, attributes.length); getAttributes(resource, attrs).then((s) { resource.instance.setAttributes(s); }); }); } void iipRequestAttachResource(int callback, int resourceId) { Warehouse.getById(resourceId).then((res) { if (res != null) { if (res.instance.applicable(session, ActionType.Attach, null) == Ruling.Denied) { sendError(ErrorType.Management, callback, 6); return; } var r = res as IResource; r.instance.on("resourceEventOccurred", _instance_EventOccurred); r.instance.on("resourceModified", _instance_PropertyModified); r.instance.on("resourceDestroyed", _instance_ResourceDestroyed); r.instance.children.on("add", _children_OnAdd); r.instance.children.on("removed", _children_OnRemoved); r.instance.attributes.on("modified", _attributes_OnModified); var link = DC.stringToBytes(r.instance.link); if (r is DistributedResource) { // reply ok sendReply(IIPPacketAction.AttachResource, callback) .addGuid(r.instance.template.classId) .addUint64(r.instance.age) .addUint16(link.length) .addDC(link) .addDC(Codec.composePropertyValueArray((r as DistributedResource).serialize(), this, true)) .done(); } else { // reply ok sendReply(IIPPacketAction.AttachResource, callback) .addGuid(r.instance.template.classId) .addUint64(r.instance.age) .addUint16(link.length) .addDC(link) .addDC(Codec.composePropertyValueArray(r.instance.serialize(), this, true)) .done(); } } else { // reply failed //SendParams(0x80, r.instance.id, r.instance.Age, r.instance.serialize(false, this)); sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound.index); } }); } void _attributes_OnModified(String key, oldValue, newValue, KeyList sender) { if (key == "name") { var instance = (sender.owner as Instance); var name = DC.stringToBytes(newValue.toString()); sendEvent(IIPPacketEvent.ChildRemoved) .addUint32(instance.id) .addUint16(name.length) .addDC(name) .done(); } } void _children_OnRemoved(Instance sender, IResource value) { sendEvent(IIPPacketEvent.ChildRemoved) .addUint32(sender.id) .addUint32(value.instance.id) .done(); } void _children_OnAdd(Instance sender, IResource value) { //if (sender.applicable(sender.Resource, this.session, ActionType.)) sendEvent(IIPPacketEvent.ChildAdded) .addUint32(sender.id) .addUint32(value.instance.id) .done(); } void iipRequestReattachResource(int callback, int resourceId, int resourceAge) { Warehouse.getById(resourceId).then((res) { if (res != null) { var r = res as IResource; r.instance.on("resourceEventOccurred", _instance_EventOccurred); r.instance.on("resourceModified", _instance_PropertyModified); r.instance.on("resourceDestroyed", _instance_ResourceDestroyed); // reply ok sendReply(IIPPacketAction.ReattachResource, callback) .addUint64(r.instance.age) .addDC(Codec.composePropertyValueArray(r.instance.serialize(), this, true)) .done(); } else { // reply failed sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound.index); } }); } void iipRequestDetachResource(int callback, int resourceId) { Warehouse.getById(resourceId).then((res) { if (res != null) { var r = res as IResource; r.instance.off("resourceEventOccurred", _instance_EventOccurred); r.instance.off("resourceModified", _instance_PropertyModified); r.instance.off("resourceDestroyed", _instance_ResourceDestroyed); // reply ok sendReply(IIPPacketAction.DetachResource, callback).done(); } else { // reply failed sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound.index); } }); } void iipRequestCreateResource(int callback, int storeId, int parentId, DC content) { Warehouse.getById(storeId).then((store) { if (store == null) { sendError(ErrorType.Management, callback, ExceptionCode.StoreNotFound.index); return; } if (!(store is IStore)) { sendError(ErrorType.Management, callback, ExceptionCode.ResourceIsNotStore.index); return; } // check security if (store.instance.applicable(session, ActionType.CreateResource, null) != Ruling.Allowed) { sendError(ErrorType.Management, callback, ExceptionCode.CreateDenied.index); return; } Warehouse.getById(parentId).then((parent) { // check security if (parent != null) if (parent.instance.applicable(session, ActionType.AddChild, null) != Ruling.Allowed) { sendError(ErrorType.Management, callback, ExceptionCode.AddChildDenied.index); return; } int offset = 0; var className = content.getString(offset + 1, content[0]); offset += 1 + content[0]; var nameLength = content.getUint16(offset); offset += 2; var name = content.getString(offset, nameLength); var cl = content.getUint32(offset); offset += 4; var type = null;//Type.getType(className); if (type == null) { sendError(ErrorType.Management, callback, ExceptionCode.ClassNotFound.index); return; } Codec.parseVarArray(content, offset, cl, this).then((parameters) { offset += cl; cl = content.getUint32(offset); Codec.parseStructure(content, offset, cl, this).then((attributes) { offset += cl; cl = content.length - offset; Codec.parseStructure(content, offset, cl, this).then((values) { var constructors = [];//Type.GetType(className).GetTypeInfo().GetConstructors(); var matching = constructors.where((x) { var ps = x.GetParameters(); // if (ps.length > 0 && ps.length == parameters.length + 1) // if (ps.Last().ParameterType == typeof(DistributedConnection)) // return true; return ps.length == parameters.length; } ).toList(); var pi = matching[0].getParameters(); // cast arguments List args = null; if (pi.length > 0) { int argsCount = pi.length; args = new List(pi.length); if (pi[pi.length - 1].parameterType.runtimeType == DistributedConnection) { args[--argsCount] = this; } if (parameters != null) { for (int i = 0; i < argsCount && i < parameters.length; i++) { //args[i] = DC.CastConvert(parameters[i], pi[i].ParameterType); } } } // create the resource var resource = null; //Activator.CreateInstance(type, args) as IResource; Warehouse.put(resource, name, store as IStore, parent); sendReply(IIPPacketAction.CreateResource, callback) .addUint32(resource.instance.id) .done(); }); }); }); }); }); } void iipRequestDeleteResource(int callback, int resourceId) { Warehouse.getById(resourceId).then((r) { if (r == null) { sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound.index); return; } if (r.instance.store.instance.applicable(session, ActionType.Delete, null) != Ruling.Allowed) { sendError(ErrorType.Management, callback, ExceptionCode.DeleteDenied.index); return; } if (Warehouse.remove(r)) sendReply(IIPPacketAction.DeleteResource, callback).done(); //SendParams((byte)0x84, callback); else sendError(ErrorType.Management, callback, ExceptionCode.DeleteFailed.index); }); } void iipRequestGetAttributes(int callback, int resourceId, DC attributes, [bool all = false]) { Warehouse.getById(resourceId).then((r) { if (r == null) { sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound.index); return; } // if (!r.instance.store.instance.applicable(r, session, ActionType.InquireAttributes, null)) if (r.instance.applicable(session, ActionType.InquireAttributes, null) != Ruling.Allowed) { sendError(ErrorType.Management, callback, ExceptionCode.ViewAttributeDenied.index); return; } List attrs = null; if (!all) attrs = attributes.getStringArray(0, attributes.length); var st = r.instance.getAttributes(attrs); if (st != null) sendReply(all ? IIPPacketAction.GetAllAttributes : IIPPacketAction.GetAttributes, callback) .addDC(Codec.composeStructure(st, this, true, true, true)) .done(); else sendError(ErrorType.Management, callback, ExceptionCode.GetAttributesFailed.index); }); } void iipRequestAddChild(int callback, int parentId, int childId) { Warehouse.getById(parentId).then((parent) { if (parent == null) { sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound.index); return; } Warehouse.getById(childId).then((child) { if (child == null) { sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound.index); return; } if (parent.instance.applicable(this.session, ActionType.AddChild, null) != Ruling.Allowed) { sendError(ErrorType.Management, callback, ExceptionCode.AddChildDenied.index); return; } if (child.instance.applicable(this.session, ActionType.AddParent, null) != Ruling.Allowed) { sendError(ErrorType.Management, callback, ExceptionCode.AddParentDenied.index); return; } parent.instance.children.add(child); sendReply(IIPPacketAction.AddChild, callback).done(); //child.instance.Parents }); }); } void iipRequestRemoveChild(int callback, int parentId, int childId) { Warehouse.getById(parentId).then((parent) { if (parent == null) { sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound.index); return; } Warehouse.getById(childId).then((child) { if (child == null) { sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound.index); return; } if (parent.instance.applicable(this.session, ActionType.RemoveChild, null) != Ruling.Allowed) { sendError(ErrorType.Management, callback, ExceptionCode.AddChildDenied.index); return; } if (child.instance.applicable(this.session, ActionType.RemoveParent, null) != Ruling.Allowed) { sendError(ErrorType.Management, callback, ExceptionCode.AddParentDenied.index); return; } parent.instance.children.remove(child); sendReply(IIPPacketAction.RemoveChild, callback).done(); //child.instance.Parents }); }); } void iipRequestRenameResource(int callback, int resourceId, DC name) { Warehouse.getById(resourceId).then((resource) { if (resource == null) { sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound.index); return; } if (resource.instance.applicable(this.session, ActionType.Rename, null) != Ruling.Allowed) { sendError(ErrorType.Management, callback, ExceptionCode.RenameDenied.index); return; } resource.instance.name = name.getString(0, name.length); sendReply(IIPPacketAction.RenameResource, callback).done(); }); } void iipRequestResourceChildren(int callback, int resourceId) { Warehouse.getById(resourceId).then((resource) { if (resource == null) { sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound.index); return; } sendReply(IIPPacketAction.ResourceChildren, callback) .addDC(Codec.composeResourceArray(resource.instance.children.toList(), this, true)) .done(); }); } void iipRequestResourceParents(int callback, int resourceId) { Warehouse.getById(resourceId).then((resource) { if (resource == null) { sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound.index); return; } sendReply(IIPPacketAction.ResourceParents, callback) .addDC(Codec.composeResourceArray(resource.instance.parents.toList(), this, true)) .done(); }); } void iipRequestClearAttributes(int callback, int resourceId, DC attributes, [bool all = false]) { Warehouse.getById(resourceId).then((r) { if (r == null) { sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound.index); return; } if (r.instance.store.instance.applicable(session, ActionType.UpdateAttributes, null) != Ruling.Allowed) { sendError(ErrorType.Management, callback, ExceptionCode.UpdateAttributeDenied.index); return; } List attrs = null; if (!all) attrs = attributes.getStringArray(0, attributes.length); if (r.instance.removeAttributes(attrs)) sendReply(all ? IIPPacketAction.ClearAllAttributes : IIPPacketAction.ClearAttributes, callback).done(); else sendError(ErrorType.Management, callback, ExceptionCode.UpdateAttributeFailed.index); }); } void iipRequestUpdateAttributes(int callback, int resourceId, DC attributes, [bool clearAttributes = false]) { Warehouse.getById(resourceId).then((r) { if (r == null) { sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound.index); return; } if (r.instance.store.instance.applicable(session, ActionType.UpdateAttributes, null) != Ruling.Allowed) { sendError(ErrorType.Management, callback, ExceptionCode.UpdateAttributeDenied.index); return; } Codec.parseStructure(attributes, 0, attributes.length, this).then((attrs) { if (r.instance.setAttributes(attrs, clearAttributes)) sendReply(clearAttributes ? IIPPacketAction.ClearAllAttributes : IIPPacketAction.ClearAttributes, callback).done(); else sendError(ErrorType.Management, callback, ExceptionCode.UpdateAttributeFailed.index); }); }); } void iipRequestTemplateFromClassName(int callback, String className) { Warehouse.getTemplateByClassName(className).then((t) { if (t != null) sendReply(IIPPacketAction.TemplateFromClassName, callback) .addInt32(t.content.length) .addDC(t.content) .done(); else { // reply failed sendError(ErrorType.Management, callback, ExceptionCode.TemplateNotFound.index); } }); } void iipRequestTemplateFromClassId(int callback, Guid classId) { Warehouse.getTemplateByClassId(classId).then((t) { if (t != null) sendReply(IIPPacketAction.TemplateFromClassId, callback) .addInt32(t.content.length) .addDC(t.content) .done(); else { // reply failed sendError(ErrorType.Management, callback, ExceptionCode.TemplateNotFound.index); } }); } void iipRequestTemplateFromResourceId(int callback, int resourceId) { Warehouse.getById(resourceId).then((r) { if (r != null) sendReply(IIPPacketAction.TemplateFromResourceId, callback) .addInt32(r.instance.template.content.length) .addDC(r.instance.template.content) .done(); else { // reply failed sendError(ErrorType.Management, callback, ExceptionCode.TemplateNotFound.index); } }); } void iipRequestQueryResources(int callback, String resourceLink) { Warehouse.query(resourceLink).then((r) { //if (r != null) //{ var list = r.where((x) => x.instance.applicable(session, ActionType.Attach, null) != Ruling.Denied).toList(); if (list.length == 0) sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound.index); else sendReply(IIPPacketAction.QueryLink, callback) .addDC(Codec.composeResourceArray(list, this, true)) .done(); }); } void IIPRequestResourceAttribute(int callback, int resourceId) { } void iipRequestInvokeFunctionArrayArguments(int callback, int resourceId, int index, DC content) { Warehouse.getById(resourceId).then((r) { if (r != null) { Codec.parseVarArray(content, 0, content.length, this).then((arguments) { var ft = r.instance.template.getFunctionTemplateByIndex(index); if (ft != null) { if (r is DistributedResource) { var rt = (r as DistributedResource).invokeByArrayArguments(index, arguments); if (rt != null) { rt.then((res) { sendReply(IIPPacketAction.InvokeFunctionArrayArguments, callback) .addDC(Codec.compose(res, this)) .done(); }); } else { // function not found on a distributed object } } else { var fi = null ;//r.GetType().GetTypeInfo().GetMethod(ft.name); if (fi != null) { } else { // ft found, fi not found, this should never happen } } } else { // no function at this index } }); } else { // no resource with this id } }); } void iipRequestInvokeFunctionNamedArguments(int callback, int resourceId, int index, DC content) { Warehouse.getById(resourceId).then((r) { if (r != null) { Codec.parseStructure(content, 0, content.length, this).then((namedArgs) { var ft = r.instance.template.getFunctionTemplateByIndex(index); if (ft != null) { if (r is DistributedResource) { var rt = (r as DistributedResource).invokeByNamedArguments(index, namedArgs); if (rt != null) { rt.then((res) { sendReply(IIPPacketAction.InvokeFunctionNamedArguments, callback) .addDC(Codec.compose(res, this)) .done(); }); } else { // function not found on a distributed object } } else { var fi = null; if (fi != null) { } else { // ft found, fi not found, this should never happen } } } else { // no function at this index } }); } else { // no resource with this id } }); } void iipRequestGetProperty(int callback, int resourceId, int index) { Warehouse.getById(resourceId).then((r) { if (r != null) { var pt = r.instance.template.getFunctionTemplateByIndex(index); if (pt != null) { if (r is DistributedResource) { sendReply(IIPPacketAction.GetProperty, callback) .addDC(Codec.compose((r as DistributedResource).get(pt.index), this)) .done(); } else { var pi = null; //r.GetType().GetTypeInfo().GetProperty(pt.Name); if (pi != null) { sendReply(IIPPacketAction.GetProperty, callback) .addDC(Codec.compose(pi.GetValue(r), this)) .done(); } else { // pt found, pi not found, this should never happen } } } else { // pt not found } } else { // resource not found } }); } void iipRequestInquireResourceHistory(int callback, int resourceId, DateTime fromDate, DateTime toDate) { Warehouse.getById(resourceId).then((r) { if (r != null) { r.instance.store.getRecord(r, fromDate, toDate).then((results) { var history = Codec.composeHistory(results, this, true); /* ulong fromAge = 0; ulong toAge = 0; if (results.Count > 0) { var firstProp = results.Values.First(); //var lastProp = results.Values.Last(); if (firstProp.length > 0) { fromAge = firstProp[0].Age; toAge = firstProp.Last().Age; } }*/ sendReply(IIPPacketAction.ResourceHistory, callback) .addDC(history) .done(); }); } }); } void iipRequestGetPropertyIfModifiedSince(int callback, int resourceId,int index, int age) { Warehouse.getById(resourceId).then((r) { if (r != null) { var pt = r.instance.template.getFunctionTemplateByIndex(index); if (pt != null) { if (r.instance.getAge(index) > age) { var pi = null; //r.GetType().GetProperty(pt.Name); if (pi != null) { sendReply(IIPPacketAction.GetPropertyIfModified, callback) .addDC(Codec.compose(pi.GetValue(r), this)) .done(); } else { // pt found, pi not found, this should never happen } } else { sendReply(IIPPacketAction.GetPropertyIfModified, callback) .addUint8(DataType.NotModified) .done(); } } else { // pt not found } } else { // resource not found } }); } void iipRequestSetProperty(int callback, int resourceId, int index, DC content) { Warehouse.getById(resourceId).then((r) { if (r != null) { var pt = r.instance.template.getPropertyTemplateByIndex(index); if (pt != null) { Codec.parse(content, 0, this).then((value) { if (r is DistributedResource) { // propagation (r as DistributedResource).set(index, value).then((x) { sendReply(IIPPacketAction.SetProperty, callback).done(); }).error((x) { sendError(x.type, callback, x.code, x.message); }); } else { /* #if NETSTANDARD1_5 var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name); #else var pi = r.GetType().GetProperty(pt.Name); #endif*/ var pi = null;// pt.Info; if (pi != null) { if (r.instance.applicable(session, ActionType.SetProperty, pt, this) == Ruling.Denied) { sendError(ErrorType.Exception, callback, ExceptionCode.SetPropertyDenied.index); return; } if (!pi.CanWrite) { sendError(ErrorType.Management, callback, ExceptionCode.ReadOnlyProperty.index); return; } if (pi.propertyType.runtimeType == DistributedPropertyContext) { value = new DistributedPropertyContext.setter(this, value); } else { // cast new value type to property type // value = DC.castConvert(value, pi.PropertyType); } try { pi.setValue(r, value); sendReply(IIPPacketAction.SetProperty, callback).done(); } catch (ex) { sendError(ErrorType.Exception, callback, 0, ex.message); } } else { // pt found, pi not found, this should never happen sendError(ErrorType.Management, callback, ExceptionCode.PropertyNotFound.index); } } }); } else { // property not found sendError(ErrorType.Management, callback, ExceptionCode.PropertyNotFound.index); } } else { // resource not found sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound.index); } }); } /// /// Get the ResourceTemplate for a given class Id. /// /// Class GUID. /// ResourceTemplate. AsyncReply getTemplate(Guid classId) { if (_templates.containsKey(classId)) return new AsyncReply.ready(_templates[classId]); else if (_templateRequests.containsKey(classId)) return _templateRequests[classId]; var reply = new AsyncReply(); _templateRequests.add(classId, reply); sendRequest(IIPPacketAction.TemplateFromClassId) .addGuid(classId) .done() .then((rt) { _templateRequests.remove(classId); _templates[(rt[0] as ResourceTemplate).classId] = rt[0] as ResourceTemplate; Warehouse.putTemplate(rt[0] as ResourceTemplate); reply.trigger(rt[0]); }).error((ex) { reply.triggerError(ex); }); return reply; } // IStore interface /// /// Get a resource by its path. /// /// Path to the resource. /// Resource AsyncReply get(String path) { var rt = new AsyncReply(); query(path).then((ar) { if (ar?.length > 0) rt.trigger(ar[0]); else rt.trigger(null); }).error((ex) => rt.triggerError(ex)); return rt; } /// /// Retrive a resource by its instance Id. /// /// Instance Id /// Resource AsyncReply retrieve(int iid) { for (var r in _resources.values) if (r.instance.id == iid) return new AsyncReply.ready(r); return new AsyncReply.ready(null); } /// /// Fetch a resource from the other end /// /// Class GUID /// Resource IdGuid classId /// DistributedResource AsyncReply fetch(int id) { if (_resourceRequests.containsKey(id) && _resources.containsKey(id)) { //Console.WriteLine("DEAD LOCK " + id); return new AsyncReply.ready(_resources[id]); // dig for dead locks //return resourceRequests[id]; } else if (_resourceRequests.containsKey(id)) return _resourceRequests[id]; else if (_resources.containsKey(id)) return new AsyncReply.ready(_resources[id]); var reply = new AsyncReply(); _resourceRequests.add(id, reply); sendRequest(IIPPacketAction.AttachResource) .addUint32(id) .done() .then((rt) { var dr = new DistributedResource(this, id, rt[1], rt[2]); getTemplate(rt[0] as Guid).then((tmp) { // ClassId, ResourceAge, ResourceLink, Content Warehouse.put(dr, id.toString(), this, null, tmp); var d = rt[3] as DC; Codec.parsePropertyValueArray(d, 0, d.length, this).then((ar) { dr.attached(ar); _resourceRequests.remove(id); reply.trigger(dr); }); }).error((ex) { reply.triggerError(ex); }); }).error((ex) { reply.triggerError(ex); }); return reply; } AsyncReply> getChildren(IResource resource) { var rt = new AsyncReply>(); sendRequest(IIPPacketAction.ResourceChildren) .addUint32(resource.instance.id) .done() .then((ar) { var d = ar[0] as DC; Codec.parseResourceArray(d, 0, d.length, this).then((resources) { rt.trigger(resources); }).error((ex) => rt.triggerError(ex)); }); return rt; } AsyncReply> getParents(IResource resource) { var rt = new AsyncReply>(); sendRequest(IIPPacketAction.ResourceParents) .addUint32(resource.instance.id) .done() .then((ar) { var d = ar[0] as DC; Codec.parseResourceArray(d, 0, d.length, this).then((resources) { rt.trigger(resources); }).error((ex) => rt.triggerError(ex)); }); return rt; } AsyncReply removeAttributes(IResource resource, [List attributes = null]) { var rt = new AsyncReply(); if (attributes == null) sendRequest(IIPPacketAction.ClearAllAttributes) .addUint32(resource.instance.id) .done() .then((ar) => rt.trigger(true)) .error((ex) => rt.triggerError(ex)); else { var attrs = DC.stringArrayToBytes(attributes); sendRequest(IIPPacketAction.ClearAttributes) .addUint32(resource.instance.id) .addInt32(attrs.length) .addDC(attrs) .done() .then((ar) => rt.trigger(true)) .error((ex) => rt.triggerError(ex)); } return rt; } AsyncReply setAttributes(IResource resource, Structure attributes, [ bool clearAttributes = false ]) { var rt = new AsyncReply(); sendRequest(clearAttributes ? IIPPacketAction.UpdateAllAttributes : IIPPacketAction.UpdateAttributes) .addUint32(resource.instance.id) .addDC(Codec.composeStructure(attributes, this, true, true, true)) .done() .then((ar) => rt.trigger(true)) .error((ex) => rt.triggerError(ex)); return rt; } AsyncReply getAttributes(IResource resource, [List attributes = null]) { var rt = new AsyncReply(); if (attributes == null) { sendRequest(IIPPacketAction.GetAllAttributes) .addUint32(resource.instance.id) .done() .then((ar) { var d = ar[0] as DC; Codec.parseStructure(d, 0, d.length, this).then((st) { resource.instance.setAttributes(st); rt.trigger(st); }).error((ex) => rt.triggerError(ex)); }); } else { var attrs = DC.stringArrayToBytes(attributes); sendRequest(IIPPacketAction.GetAttributes) .addUint32(resource.instance.id) .addInt32(attrs.length) .addDC(attrs) .done() .then((ar) { var d = ar[0] as DC; Codec.parseStructure(d, 0, d.length, this).then((st) { resource.instance.setAttributes(st); rt.trigger(st); }).error((ex) => rt.triggerError(ex)); }); } return rt; } /// /// Get resource history. /// /// IResource. /// From date. /// To date. /// AsyncReply>> getRecord(IResource resource, DateTime fromDate, DateTime toDate) { if (resource is DistributedResource) { var dr = resource as DistributedResource; if (dr.connection != this) return new AsyncReply>>.ready(null); var reply = new AsyncReply>>(); sendRequest(IIPPacketAction.ResourceHistory) .addUint32(dr.id) .addDateTime(fromDate) .addDateTime(toDate) .done() .then((rt) { var content = rt[0] as DC; Codec.parseHistory(content, 0, content.length, resource, this) .then((history) => reply.trigger(history)); }).error((ex) => reply.triggerError(ex)); return reply; } else return new AsyncReply>>.ready(null); } /// /// Query resources at specific link. /// /// Link path. /// AsyncReply> query(String path) { var str = DC.stringToBytes(path); var reply = new AsyncReply>(); sendRequest(IIPPacketAction.QueryLink) .addUint16(str.length) .addDC(str) .done() .then((args) { var content = args[0] as DC; Codec.parseResourceArray(content, 0, content.length, this) .then((resources) => reply.trigger(resources)); }).error((ex)=>reply.triggerError(ex)); return reply; } /// /// Create a new resource. /// /// The store in which the resource is saved. /// Class full name. /// Constructor parameters. /// Resource attributeds. /// Values for the resource properties. /// New resource instance AsyncReply create(IStore store, IResource parent, String className, List parameters, Structure attributes, Structure values) { var reply = new AsyncReply(); var pkt = new BinaryList() .addUint32(store.instance.id) .addUint32(parent.instance.id) .addUint8(className.length) .addString(className) .addDC(Codec.composeVarArray(parameters, this, true)) .addDC(Codec.composeStructure(attributes, this, true, true, true)) .addDC(Codec.composeStructure(values, this)); pkt.insertInt32(8, pkt.length); sendRequest(IIPPacketAction.CreateResource) .addDC(pkt.toDC()) .done() .then((args) { var rid = args[0]; fetch(rid).then((r) { reply.trigger(r); }); }); return reply; } _instance_ResourceDestroyed(IResource resource) { // compose the packet sendEvent(IIPPacketEvent.ResourceDestroyed) .addUint32(resource.instance.id) .done(); } void _instance_PropertyModified(IResource resource, String name, newValue) { var pt = resource.instance.template.getPropertyTemplateByName(name); if (pt == null) return; sendEvent(IIPPacketEvent.PropertyUpdated) .addUint32(resource.instance.id) .addUint8(pt.index) .addDC(Codec.compose(newValue, this)) .done(); } // private void Instance_EventOccurred(IResource resource, string name, string[] users, DistributedConnection[] connections, object[] args) void _instance_EventOccurred(IResource resource, issuer, List receivers, String name, List args) { var et = resource.instance.template.getEventTemplateByName(name); if (et == null) return; if (receivers != null) if (!receivers.contains(this.session)) return; if (resource.instance.applicable(this.session, ActionType.ReceiveEvent, et, issuer) == Ruling.Denied) return; // compose the packet sendEvent(IIPPacketEvent.EventOccurred) .addUint32(resource.instance.id) .addUint8(et.index) .addDC(Codec.composeVarArray(args, this, true)) .done(); } }