diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2e95f96 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Dart", + "program": "bin/main.dart", + "request": "launch", + "type": "dart", + "" + } + ] +} \ No newline at end of file diff --git a/bin/Core/AsyncBag.dart b/bin/Core/AsyncBag.dart new file mode 100644 index 0000000..c806fd4 --- /dev/null +++ b/bin/Core/AsyncBag.dart @@ -0,0 +1,58 @@ +import 'AsyncReply.dart'; + +class AsyncBag extends AsyncReply> +{ + + List> _replies = new List>(); + List _results = new List(); + + int _count = 0; + bool _sealedBag = false; + + seal() + { + if (_sealedBag) + return; + + _sealedBag = true; + + if (_results.length == 0) + trigger(new List()); + + for (var i = 0; i < _results.length; i++) + { + var k = _replies[i]; + var index = i; + + k.then((r) + { + _results[index] = r; + _count++; + if (_count == _results.length) + trigger(_results); + }); + } + } + + add(AsyncReply reply) + { + if (!_sealedBag) + { + _results.add(null); + _replies.add(reply); + } + } + + addBag(AsyncBag bag) + { + bag._replies.forEach((r) { + add(r); + }); + } + + AsyncBag() + { + + } + +} diff --git a/bin/Core/AsyncException.dart b/bin/Core/AsyncException.dart new file mode 100644 index 0000000..ec04af3 --- /dev/null +++ b/bin/Core/AsyncException.dart @@ -0,0 +1,33 @@ + +import 'ExceptionCode.dart'; +import 'ErrorType.dart'; + +class AsyncException implements Exception +{ + final ErrorType type; + final int code; + final String message; + + AsyncException(this.type, this.code, this.message) + { + + } + + static toAsyncException(Exception ex) + { + return ex is AsyncException ? ex + : new AsyncException(ErrorType.Exception, 0, ex.toString()); + } + + String errMsg() { + if (type == ErrorType.Management) + return ExceptionCode.values.elementAt(code).toString() + ": " + (message ?? ""); + else + return code.toString() + ": " + message; + } + + @override + String toString() { + return errMsg(); + } +} diff --git a/bin/Core/AsyncQueue.dart b/bin/Core/AsyncQueue.dart new file mode 100644 index 0000000..c1339d7 --- /dev/null +++ b/bin/Core/AsyncQueue.dart @@ -0,0 +1,51 @@ +library esiur; + +import 'AsyncReply.dart'; + +class AsyncQueue extends AsyncReply +{ + List> _list = new List>(); + +// object queueLock = new object(); + + add(AsyncReply reply) + { + //lock (queueLock) + _list.add(reply); + + //super._resultReady = false; + super.setResultReady(false); + + reply.then(processQueue); + } + + remove(AsyncReply reply) + { + //lock (queueLock) + _list.remove(reply); + processQueue(null); + } + + void processQueue(T o) + { + //lock (queueLock) + for (var i = 0; i < _list.length; i++) + if (_list[i].ready) + { + super.trigger(_list[i].result); + _list.removeAt(i); + i--; + } + else + break; + + + //super._resultReady = (_list.length == 0); + super.setResultReady(_list.length == 0); + } + + AsyncQueue() + { + + } + } diff --git a/bin/Core/AsyncReply.dart b/bin/Core/AsyncReply.dart new file mode 100644 index 0000000..2be9080 --- /dev/null +++ b/bin/Core/AsyncReply.dart @@ -0,0 +1,220 @@ +/* + +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 'dart:async'; +import 'dart:core'; +import 'AsyncException.dart'; +import 'ProgressType.dart'; + +class AsyncReply implements Future +{ + + List _callbacks = new List(); + + T _result; + + List _errorCallbacks = new List(); + + List _progressCallbacks = new List(); + + List _chunkCallbacks = new List(); + + + + bool _resultReady = false; + AsyncException _exception; + + + bool get ready + { + return _resultReady; + } + + T get result + { + return _result; + } + + setResultReady(bool val) + { + _resultReady = val; + } + + AsyncReply then(FutureOr onValue(T value), {Function onError}) + { + _callbacks.add(onValue); + if (onError != null) + { + if (onError is Function(dynamic, dynamic)) + { + _errorCallbacks.add((ex)=>onError(ex, null)); + } + else if (onError is Function(dynamic)) + { + _errorCallbacks.add(onError); + } + else if (onError is Function()) + { + _errorCallbacks.add((ex)=>onError()); + } + } + + + if (_resultReady) + onValue(result); + + return this as AsyncReply; + + } + + AsyncReply whenComplete(FutureOr action()) + { + return this; + //_callbacks.add(action); + } + + Stream asStream() + { + return null; + } + + AsyncReply catchError(Function onError, {bool test(Object error)}) + { + return this.error(onError); + } + + AsyncReply timeout(Duration timeLimit, {FutureOr onTimeout()}) + { + return this; + } + + AsyncReply _then_old(Function(T) callback) + { + _callbacks.add(callback); + + if (_resultReady) + callback(result); + + return this; + } + + + AsyncReply error(Function(dynamic) callback) + { + _errorCallbacks.add(callback); + + if (_exception != null) + callback(_exception); + + return this; + } + + AsyncReply progress(Function(ProgressType, int, int) callback) + { + _progressCallbacks.add(callback); + return this; + } + + + AsyncReply chunk(Function(T) callback) + { + _chunkCallbacks.add(callback); + return this; + } + + void trigger(T result) + { + +// lock (callbacksLock) +// { + if (_resultReady) + return; + + _result = result; + _resultReady = true; + + _callbacks.forEach((x) { + x(result); + }); + +// } + + } + + triggerError(Exception exception) + { + if (_resultReady) + return; + + _exception = AsyncException.toAsyncException(exception); + + ///lock (callbacksLock) + //{ + _errorCallbacks.forEach((x) { + x(_exception); + }); + //} + + } + + triggerProgress(ProgressType type, int value, int max) + { + if (_resultReady) + return; + + //lock (callbacksLock) + //{ + _progressCallbacks.forEach((x) { + x(type, value, max); + }); + //} + } + + + triggerChunk(T value) + { + if (_resultReady) + return; + + //lock (callbacksLock) + //{ + _chunkCallbacks.forEach((x) { + x(value); + }); + + //} + } + + + AsyncReply.ready(T result) + { + _resultReady = true; + _result = result; + } + + AsyncReply() + { + + } + +} \ No newline at end of file diff --git a/bin/Core/ErrorType.dart b/bin/Core/ErrorType.dart new file mode 100644 index 0000000..171b6ee --- /dev/null +++ b/bin/Core/ErrorType.dart @@ -0,0 +1,5 @@ +enum ErrorType +{ + Management, + Exception +} diff --git a/bin/Core/ExceptionCode.dart b/bin/Core/ExceptionCode.dart new file mode 100644 index 0000000..09f8bd2 --- /dev/null +++ b/bin/Core/ExceptionCode.dart @@ -0,0 +1,31 @@ + +enum ExceptionCode +{ + HostNotReachable, + AccessDenied, + ResourceNotFound, + AttachDenied, + InvalidMethod, + InvokeDenied, + CreateDenied, + AddParentDenied, + AddChildDenied, + ViewAttributeDenied, + UpdateAttributeDenied, + StoreNotFound, + ParentNotFound, + ChildNotFound, + ResourceIsNotStore, + DeleteDenied, + DeleteFailed, + UpdateAttributeFailed, + GetAttributesFailed, + ClearAttributesFailed, + TemplateNotFound, + RenameDenied, + ClassNotFound, + MethodNotFound, + PropertyNotFound, + SetPropertyDenied, + ReadOnlyProperty +} \ No newline at end of file diff --git a/bin/Core/IDestructible.dart b/bin/Core/IDestructible.dart new file mode 100644 index 0000000..0f47012 --- /dev/null +++ b/bin/Core/IDestructible.dart @@ -0,0 +1,10 @@ +library esiur; + +import 'IEventHandler.dart'; + +typedef DestroyedEvent(sender); + +abstract class IDestructible extends IEventHandler +{ + void destroy(); +} \ No newline at end of file diff --git a/bin/Core/IEventHandler.dart b/bin/Core/IEventHandler.dart new file mode 100644 index 0000000..1d81101 --- /dev/null +++ b/bin/Core/IEventHandler.dart @@ -0,0 +1,48 @@ +class IEventHandler +{ + Map> _events; + + register(String event) + { + _events[event.toLowerCase()] = []; + } + + IEventHandler() + { + _events = {}; + } + + emitArgs(String event, List arguments) + { + event = event.toLowerCase(); + if (_events.containsKey(event)) + for(var i = 0; i < _events[event].length; i++) + if (Function.apply(_events[event][i], arguments) != null) + return true; + + return false; + } + + on(String event, Function callback) + { + event = event.toLowerCase(); + // add + if (!_events.containsKey(event)) + register(event); + + _events[event].add(callback); + return this; + } + + off(event, callback) + { + event = event.toString(); + if (_events.containsKey(event)) + { + if (callback != null) + _events[event].remove(callback); + else + this._events[event] = []; + } + } +} diff --git a/bin/Core/ProgressType.dart b/bin/Core/ProgressType.dart new file mode 100644 index 0000000..76e17b6 --- /dev/null +++ b/bin/Core/ProgressType.dart @@ -0,0 +1,5 @@ + enum ProgressType + { + Execution, + Network, + } \ No newline at end of file diff --git a/bin/Data/AutoList.dart b/bin/Data/AutoList.dart new file mode 100644 index 0000000..246a9be --- /dev/null +++ b/bin/Data/AutoList.dart @@ -0,0 +1,200 @@ +import '../Core/IDestructible.dart'; +import 'Codec.dart'; +import 'dart:collection'; + +class AutoList extends IDestructible with IterableMixin +{ + + List _list = new List(); + + + ST _state; + bool _removableList; + + sort(Function comparer) + { + _list.sort(comparer); + } + + Iterator get iterator => _list.iterator; + + + /// + /// Convert AutoList to array + /// + /// Array + //List toList() + //{ + // list.OrderBy() + // return _list; + //} + + + /// Create a new instance of AutoList + /// State object to be included when an event is raised. + AutoList([ST state, List values]) + { + this._state = state; + this._removableList = Codec.implementsInterface(); + + if (values != null) + addRange(values); + + register("modified"); + register("added"); + register("removed"); + register("cleared"); + } + + + + /// + /// Synchronization lock of the list + /// + //public object SyncRoot + //{ + // get + // { + // return syncRoot; + // } + //} + + /// + /// First item in the list + /// + //T first() + //{ + // return _list.first; + //} + + operator [](index) + { + return _list[index]; + } + + operator []=(index, value) + { + var oldValue = _list[index]; + + if (_removableList) + { + if (oldValue != null) + (oldValue as IDestructible).off("destroy", _itemDestroyed); + if (value != null) + value.on("destroy", _itemDestroyed); + } + + //lock (syncRoot) + _list[index] = value; + + emitArgs("modified", [_state, index, oldValue, value]); + } + + /// + /// Add item to the list + /// + add(T value) + { + if (_removableList) + if (value != null) + (value as IDestructible).on("destroy", _itemDestroyed); + + // lock (syncRoot) + _list.add(value); + + emitArgs("add",[_state, value]); + } + + /// + /// Add an array of items to the list + /// + addRange(List values) + { + values.forEach((x)=>add(x)); + } + + _itemDestroyed(T sender) + { + remove(sender); + } + + /// + /// Clear the list + /// + clear() + { + if (_removableList) + _list.forEach((x)=>(x as IDestructible)?.off("destroy", _itemDestroyed)); + + +// lock (syncRoot) + _list.clear(); + + emitArgs("cleared", [_state]); + } + + /// + /// Remove an item from the list + /// Item to remove + /// + remove(T value) + { + if (!_list.contains(value)) + return; + + if (_removableList) + if (value != null) + (value as IDestructible).off("destroy", _itemDestroyed); + + //lock (syncRoot) + _list.remove(value); + + emitArgs("removed", [_state, value]); + } + + /// + /// Number of items in the list + /// + get count => _list.length; + get length => _list.length; + + + /// + /// Check if an item exists in the list + /// + /// Item to check if exists + //contains(T value) => _list.contains(value); + + /// + /// Check if any item of the given array is in the list + /// + /// Array of items + containsAny(values) + { + + if (values is List) + { + for(var v in values) + { + if (_list.contains(v)) + return true; + } + } + else if (values is AutoList) + { + + for(var v in values._list) + { + if (_list.contains(v)) + return true; + } + } + + return false; + } + + @override + void destroy() { + clear(); + } +} diff --git a/bin/Data/BinaryList.dart b/bin/Data/BinaryList.dart new file mode 100644 index 0000000..5cf7f3d --- /dev/null +++ b/bin/Data/BinaryList.dart @@ -0,0 +1,550 @@ +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 26/07/2019. + */ + +import '../Core/AsyncReply.dart'; +import 'dart:typed_data'; +import 'DC.dart'; +import 'DataType.dart'; +import 'Guid.dart'; + +class BinaryList +{ + var _list = new List(); + + int get length => _list.length; + + BinaryList addDateTime(DateTime value) + { + _list.addAll(DC.dateTimeToBytes(value)); + return this; + } + + BinaryList insertDateTime(int position, DateTime value) + { + _list.insertAll(position, DC.dateTimeToBytes(value)); + return this; + } + + + BinaryList addDateTimeArray(List value) + { + _list.addAll(DC.dateTimeArrayToBytes(value)); + return this; + } + + BinaryList insertDateTimeArray(int position, List value) + { + _list.insertAll(position, DC.dateTimeArrayToBytes(value)); + return this; + } + + BinaryList addGuid(Guid value) + { + _list.addAll(DC.guidToBytes(value)); + return this; + } + + BinaryList insertGuid(int position, Guid value) + { + _list.insertAll(position, DC.guidToBytes(value)); + return this; + } + + BinaryList addGuidArray(List value) + { + _list.addAll(DC.guidArrayToBytes(value)); + return this; + } + + BinaryList insertGuidArray(int position, List value) + { + _list.insertAll(position, DC.guidArrayToBytes(value)); + return this; + } + + + BinaryList addUint8Array(Uint8List value) + { + _list.addAll(value); + return this; + } + + BinaryList addDC(DC value) + { + _list.addAll(value.toArray()); + return this; + } + + BinaryList insertUint8Array(int position, Uint8List value) + { + _list.insertAll(position, value); + return this; + } + + + /* + BinaryList addHex(String value) + { + return this.addUint8Array(DC.fromHex(value, null)); + } + + BinaryList insertHex(int position, String value) + { + return this.insertUint8Array(position, DC.fromHex(value, null)); + } + */ + + + BinaryList addString(String value) + { + _list.addAll(DC.stringToBytes(value)); + return this; + } + + BinaryList insertString(int position, String value) + { + _list.insertAll(position, DC.stringToBytes(value)); + return this; + } + + BinaryList addStringArray(List value) + { + _list.addAll(DC.stringArrayToBytes(value)); + return this; + } + + BinaryList insertStringArray(int position, List value) + { + _list.insertAll(position, DC.stringArrayToBytes(value)); + return this; + } + + BinaryList insertUint8(int position, int value) + { + _list.insert(position, value); + return this; + } + + BinaryList addUint8(int value) + { + _list.add(value); + return this; + } + + BinaryList addInt8(int value) + { + _list.add(value); + return this; + } + + BinaryList insertInt8(int position, int value) + { + _list.insert(position, value); + return this; + } + + BinaryList addInt8Array(Int8List value) + { + _list.addAll(DC.int8ArrayToBytes(value)); + return this; + } + + BinaryList insertInt8Array(int position, Int8List value) + { + _list.insertAll(position, DC.int8ArrayToBytes(value)); + return this; + } + + + BinaryList addChar(int value) + { + _list.addAll(DC.charToBytes(value)); + return this; + } + + BinaryList InsertChar(int position, int value) + { + _list.insertAll(position, DC.charToBytes(value)); + return this; + } + + BinaryList addCharArray(Uint16List value) + { + _list.addAll(DC.charArrayToBytes(value)); + return this; + } + + BinaryList InsertCharArray(int position, Uint16List value) + { + _list.insertAll(position, DC.charArrayToBytes(value)); + return this; + } + + + BinaryList addBoolean(bool value) + { + _list.addAll(DC.boolToBytes(value)); + return this; + } + + BinaryList insertBoolean(int position, bool value) + { + _list.insertAll(position, DC.boolToBytes(value)); + return this; + } + + BinaryList addBooleanArray(List value) + { + _list.addAll(DC.boolToBytes(value)); + return this; + } + + BinaryList insertBooleanArray(int position, List value) + { + _list.insertAll(position, DC.boolToBytes(value)); + return this; + } + + BinaryList addUint16(int value) + { + _list.addAll(DC.uint16ToBytes(value)); + return this; + } + + BinaryList insertUint16(int position, int value) + { + _list.insertAll(position, DC.uint16ToBytes(value)); + return this; + } + + BinaryList addUint16Array(Uint16List value) + { + _list.addAll(DC.uint16ArrayToBytes(value)); + return this; + } + + BinaryList insertUint16Array(int position, Uint16List value) + { + _list.insertAll(position, DC.uint16ArrayToBytes(value)); + return this; + } + + + BinaryList addInt16(int value) + { + _list.addAll(DC.int16ToBytes(value)); + return this; + } + + BinaryList insertInt16(int position, int value) + { + _list.insertAll(position, DC.int16ToBytes(value)); + return this; + } + + + BinaryList addInt16Array(Int16List value) + { + _list.addAll(DC.int16ArrayToBytes(value)); + return this; + } + + BinaryList insertInt16Array(int position, Int16List value) + { + _list.insertAll(position, DC.int16ArrayToBytes(value)); + return this; + } + + BinaryList addUint32(int value) + { + _list.addAll(DC.uint32ToBytes(value)); + return this; + } + + BinaryList insertUint32(int position, int value) + { + _list.insertAll(position, DC.uint32ToBytes(value)); + return this; + } + + BinaryList addUint32Array(Uint32List value) + { + _list.addAll(DC.uint32ArrayToBytes(value)); + return this; + } + BinaryList InsertUint32Array(int position, Uint32List value) + { + _list.insertAll(position, DC.uint32ArrayToBytes(value)); + return this; + } + + + BinaryList addInt32(int value) + { + _list.addAll(DC.int32ToBytes(value)); + return this; + } + + BinaryList insertInt32(int position, int value) + { + _list.insertAll(position, DC.int32ToBytes(value)); + return this; + } + + BinaryList addInt32Array(Int32List value) + { + _list.addAll(DC.int32ArrayToBytes(value)); + return this; + } + + BinaryList insertInt32Array(int position, Int32List value) + { + _list.insertAll(position, DC.int32ArrayToBytes(value)); + return this; + } + + + BinaryList addUint64(int value) + { + _list.addAll(DC.uint64ToBytes(value)); + return this; + } + + BinaryList insertUint64(int position, int value) + { + _list.insertAll(position, DC.uint64ToBytes(value)); + return this; + } + + BinaryList addUint64Array(Uint64List value) + { + _list.addAll(DC.uint64ArrayToBytes(value)); + return this; + } + + BinaryList InsertUint64Array(int position, Uint64List value) + { + _list.insertAll(position, DC.uint64ArrayToBytes(value)); + return this; + } + + BinaryList addInt64(int value) + { + _list.addAll(DC.int64ToBytes(value)); + return this; + } + + BinaryList insertInt64(int position, int value) + { + _list.insertAll(position, DC.int64ToBytes(value)); + return this; + } + + BinaryList addInt64Array(Int64List value) + { + _list.addAll(DC.int64ArrayToBytes(value)); + return this; + } + + BinaryList insertInt64Array(int position, Int64List value) + { + _list.insertAll(position, DC.int64ArrayToBytes(value)); + return this; + } + + BinaryList addFloat32(double value) + { + _list.addAll(DC.float32ToBytes(value)); + return this; + } + + BinaryList insertFloat32(int position, double value) + { + _list.insertAll(position, DC.float32ToBytes(value)); + return this; + } + + BinaryList addFloat32Array(Float32List value) + { + _list.addAll(DC.float32ArrayToBytes(value)); + return this; + } + + BinaryList insertFloat32Array(int position, Float32List value) + { + _list.insertAll(position, DC.float32ArrayToBytes(value)); + return this; + } + + + BinaryList addFloat64(double value) + { + _list.addAll(DC.float64ToBytes(value)); + return this; + } + + BinaryList insertFloat64(int position, double value) + { + _list.insertAll(position, DC.float64ToBytes(value)); + return this; + } + + BinaryList addFloat64Array(Float64List value) + { + _list.addAll(DC.float64ArrayToBytes(value)); + return this; + } + + BinaryList insertFloat64Array(int position, Float64List value) + { + _list.insertAll(position, DC.float64ArrayToBytes(value)); + return this; + } + + + + BinaryList add(type, value) + { + switch (type) + { + case DataType.Bool: + addBoolean(value); + return this; + case DataType.BoolArray: + addBooleanArray(value); + return this; + case DataType.UInt8: + addUint8(value); + return this; + case DataType.UInt8Array: + addUint8Array(value); + return this; + case DataType.Int8: + addInt8(value); + return this; + case DataType.Int8Array: + addInt8Array(value); + return this; + case DataType.Char: + addChar(value); + return this; + case DataType.CharArray: + addCharArray(value); + return this; + case DataType.UInt16: + addUint16(value); + return this; + case DataType.UInt16Array: + addUint16Array(value); + return this; + case DataType.Int16: + addInt16(value); + return this; + case DataType.Int16Array: + addInt16Array(value); + return this; + case DataType.UInt32: + addUint32(value); + return this; + case DataType.UInt32Array: + addUint32Array(value); + return this; + case DataType.Int32: + addInt32(value); + return this; + case DataType.Int32Array: + addInt32Array(value); + return this; + case DataType.UInt64: + addUint64(value); + return this; + case DataType.UInt64Array: + addUint64Array(value); + return this; + case DataType.Int64: + addInt64(value); + return this; + case DataType.Int64Array: + addInt64Array(value); + return this; + + case DataType.Float32: + addFloat32(value); + return this; + case DataType.Float32Array: + addFloat32Array(value); + return this; + + case DataType.Float64: + addFloat64(value); + return this; + case DataType.Float64Array: + addFloat64Array(value); + return this; + + case DataType.String: + addString(value); + return this; + case DataType.StringArray: + addStringArray(value); + return this; + + case DataType.DateTime: + addDateTime(value); + return this; + case DataType.DateTimeArray: + addDateTimeArray(value); + return this; + + default: + throw new Exception("Not Implemented " + type.ToString()); + //return this; + } + } + /// + /// Convert the _list to an array of bytes + /// + /// Bytes array + Uint8List toArray() + { + return Uint8List.fromList(_list); + } + + + DC toDC() + { + return new DC.fromUint8Array(toArray()); + } + + + AsyncReply done() + { + return null; + // + } + + +} \ No newline at end of file diff --git a/bin/Data/Codec.dart b/bin/Data/Codec.dart new file mode 100644 index 0000000..255a2b0 --- /dev/null +++ b/bin/Data/Codec.dart @@ -0,0 +1,1074 @@ +/* + +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 'DataType.dart'; +import 'StructureComparisonResult.dart'; +import 'dart:typed_data'; +import 'Structure.dart'; +import 'StructureMetadata.dart'; +import '../Core/AsyncBag.dart'; +import '../Core/AsyncReply.dart'; +import 'DC.dart'; +import 'BinaryList.dart'; +import 'SizeObject.dart'; +import 'NotModified.dart'; +import 'ResourceComparisonResult.dart'; +import 'PropertyValue.dart'; +import 'KeyList.dart'; +import '../Net/IIP/DistributedConnection.dart'; +import '../Net/IIP/DistributedResource.dart'; +import '../Resource/Warehouse.dart'; +import '../Resource/IResource.dart'; +import '../Resource/Template/PropertyTemplate.dart'; +import '../Net/IIP/DistributedPropertyContext.dart'; + +class Codec +{ + /// + /// Check if a DataType is an array + /// + /// DataType to check + /// True if DataType is an array, otherwise false + static bool isArray(int type) + { + return ((type & 0x80) == 0x80) && (type != DataType.NotModified); + } + + /// + /// Get the element DataType + /// + /// + /// Passing UInt8Array will return UInt8 + /// + /// DataType to get its element DataType + static int getElementType(int type) + { + return type & 0x7F; + } + + /// + /// Get DataType array of a given Structure + /// + /// Structure to get its DataTypes + /// Distributed connection is required in case a type is at the other end + static List getStructureDateTypes(Structure structure, DistributedConnection connection) + { + var keys = structure.getKeys(); + var types = new List(keys.length); + + for (var i = 0; i < keys.length; i++) + types[i] = Codec.getDataType(structure[keys[i]], connection); + return types; + } + + /// + /// Compare two structures + /// + /// Initial structure to compare with + /// Next structure to compare with the initial + /// DistributedConnection is required in case a structure holds items at the other end + static int compareStructures(Structure initial, Structure next, DistributedConnection connection) + { + if (next == null) + return StructureComparisonResult.Null; + + if (initial == null) + return StructureComparisonResult.Structure; + + if (next == initial) + return StructureComparisonResult.Same; + + if (initial.length != next.length) + return StructureComparisonResult.Structure; + + var previousKeys = initial.getKeys(); + var nextKeys = next.getKeys(); + + for (var i = 0; i < previousKeys.length; i++) + if (previousKeys[i] != nextKeys[i]) + return StructureComparisonResult.Structure; + + var previousTypes = getStructureDateTypes(initial, connection); + var nextTypes = getStructureDateTypes(next, connection); + + for (var i = 0; i < previousTypes.length; i++) + if (previousTypes[i] != nextTypes[i]) + return StructureComparisonResult.StructureSameKeys; + + return StructureComparisonResult.StructureSameTypes; + } + + /// + /// Compose an array of structures into an array of bytes + /// + /// Array of Structure to compose + /// DistributedConnection is required in case a structure in the array holds items at the other end + /// If true, prepend the length as UInt32 at the beginning of the returned bytes array + /// Array of bytes in the network byte order + static DC composeStructureArray(List structures, DistributedConnection connection, [bool prependLength = false]) + { + if (structures == null || structures?.length == 0) + return prependLength ? new DC(4): new DC(0); + + var rt = new BinaryList(); + var comparsion = StructureComparisonResult.Structure; + + rt.addUint8(comparsion) + .addDC(composeStructure(structures[0], connection, true, true, true)); + + for (var i = 1; i < structures.length; i++) + { + comparsion = compareStructures(structures[i - 1], structures[i], connection); + rt.addUint8(comparsion); + + if (comparsion == StructureComparisonResult.Structure) + rt.addDC(composeStructure(structures[i], connection, true, true, true)); + else if (comparsion == StructureComparisonResult.StructureSameKeys) + rt.addDC(composeStructure(structures[i], connection, false, true, true)); + else if (comparsion == StructureComparisonResult.StructureSameTypes) + rt.addDC(composeStructure(structures[i], connection, false, false, true)); + } + + if (prependLength) + rt.insertInt32(0, rt.length); + + return rt.toDC(); + } + + /// + /// Parse an array of structures + /// + /// Bytes array + /// Zero-indexed offset + /// Number of bytes to parse + /// DistributedConnection is required in case a structure in the array holds items at the other end + /// Array of structures + static AsyncBag parseStructureArray(DC data, int offset, int length, DistributedConnection connection) + { + var reply = new AsyncBag(); + if (length == 0) + { + reply.seal(); + return reply; + } + + var end = offset + length; + + var result = data[offset++]; + + AsyncReply previous = null; + // string[] previousKeys = null; + // DataType[] previousTypes = null; + + StructureMetadata metadata = new StructureMetadata(); + + + if (result == StructureComparisonResult.Null) + previous = new AsyncReply.ready(null); + else if (result == StructureComparisonResult.Structure) + { + int cs = data.getUint32(offset); + offset += 4; + previous = parseStructure(data, offset, cs, connection, metadata); + offset += cs; + } + + reply.add(previous); + + + while (offset < end) + { + result = data[offset++]; + + if (result == StructureComparisonResult.Null) + previous = new AsyncReply.ready(null); + else if (result == StructureComparisonResult.Structure) + { + int cs = data.getUint32(offset); + offset += 4; + previous = parseStructure(data, offset, cs, connection, metadata);// out previousKeys, out previousTypes); + offset += cs; + } + else if (result == StructureComparisonResult.StructureSameKeys) + { + int cs = data.getUint32(offset); + offset += 4; + previous = parseStructure(data, offset, cs, connection, metadata, metadata.keys); + offset += cs; + } + else if (result == StructureComparisonResult.StructureSameTypes) + { + int cs = data.getUint32(offset); + offset += 4; + previous = parseStructure(data, offset, cs, connection, metadata, metadata.keys, metadata.types); + offset += cs; + } + + reply.add(previous); + } + + reply.seal(); + return reply; + } + + /// + /// Compose a structure into an array of bytes + /// + /// Structure to compose + /// DistributedConnection is required in case an item in the structure is at the other end + /// Whether to include the structure keys + /// Whether to include each item DataType + /// If true, prepend the length as UInt32 at the beginning of the returned bytes array + /// Array of bytes in the network byte order + static DC composeStructure(Structure value, DistributedConnection connection, [bool includeKeys = true, bool includeTypes = true, bool prependLength = false]) + { + var rt = new BinaryList(); + + if (includeKeys) + { + for (var k in value.keys) + { + var key = DC.stringToBytes(k); + rt.addUint8(key.length) + .addDC(key) + .addDC(compose(value[k], connection)); + } + } + else + { + for (var k in value.keys) + rt.addDC(compose(value[k], connection, includeTypes)); + } + + if (prependLength) + rt.insertInt32(0, rt.length); + + return rt.toDC(); //.toArray(); + } + + /// + /// Parse a structure + /// + /// Bytes array + /// Zero-indexed offset. + /// Number of bytes to parse. + /// DistributedConnection is required in case a structure in the array holds items at the other end. + /// Array to store keys in. + /// Array to store DataTypes in. + /// Array of keys, in case the data doesn't include keys + /// Array of DataTypes, in case the data doesn't include DataTypes + /// Structure + static AsyncReply parseStructure(DC data, int offset, int length, DistributedConnection connection, [StructureMetadata metadata = null, List keys = null, List types = null])// out string[] parsedKeys, out DataType[] parsedTypes, string[] keys = null, DataType[] types = null) + { + var reply = new AsyncReply(); + var bag = new AsyncBag(); + var keylist = new List(); + var typelist = new List(); + var sizeObject = new SizeObject(); + + if (keys == null) + { + while (length > 0) + { + var len = data[offset++]; + keylist.add(data.getString(offset, len)); + offset += len; + + typelist.add(data[offset]); + + bag.add(Codec.parse(data, offset, connection, sizeObject)); + length -= sizeObject.size + len + 1; + offset += sizeObject.size; + } + } + else if (types == null) + { + keylist.addAll(keys); + + while (length > 0) + { + typelist.add(data[offset]); + + bag.add(Codec.parse(data, offset, connection, sizeObject)); + length -= sizeObject.size; + offset += sizeObject.size; + } + } + else + { + keylist.addAll(keys); + typelist.addAll(types); + + var i = 0; + while (length > 0) + { + bag.add(parse(data, offset, connection, sizeObject, types[i])); + length -= sizeObject.size; + offset += sizeObject.size; + i++; + } + } + + bag.seal(); + + bag.then((res) + { + // compose the list + var s = new Structure(); + for (var i = 0; i < keylist.length; i++) + s[keylist[i]] = res[i]; + reply.trigger(s); + }); + + if (metadata != null) + { + metadata.keys = keylist; + metadata.types = typelist; + } + + return reply; + } + + + + /// + /// Parse a value + /// + /// Bytes array + /// Zero-indexed offset. + /// Output the number of bytes parsed + /// DistributedConnection is required in case a structure in the array holds items at the other end. + /// DataType, in case the data is not prepended with DataType + /// Value + static AsyncReply parse(DC data, int offset, DistributedConnection connection, [SizeObject sizeObject, int dataType = DataType.Unspecified]) + { + + bool isArray; + int t; + + + if (dataType == DataType.Unspecified) + { + sizeObject?.size = 1; + dataType = data[offset++]; + } + else + sizeObject?.size = 0; + + t = dataType & 0x7F; + + isArray = (dataType & 0x80) == 0x80; + + var payloadSize = DataType.size(dataType); + + + int contentLength = 0; + + // check if we have the enough data + if (payloadSize == -1) + { + contentLength = data.getUint32(offset); + offset += 4; + sizeObject?.size += 4 + contentLength; + } + else + sizeObject?.size += payloadSize; + + if (isArray) + { + switch (t) + { + // VarArray ? + case DataType.Void: + return parseVarArray(data, offset, contentLength, connection); + + case DataType.Bool: + return new AsyncReply>.ready(data.getBooleanArray(offset, contentLength)); + + case DataType.UInt8: + return new AsyncReply.ready(data.getUint8Array(offset, contentLength)); + + case DataType.Int8: + return new AsyncReply.ready(data.getInt8Array(offset, contentLength)); + + case DataType.Char: + return new AsyncReply>.ready(data.getCharArray(offset, contentLength)); + + case DataType.Int16: + return new AsyncReply.ready(data.getInt16Array(offset, contentLength)); + + case DataType.UInt16: + return new AsyncReply.ready(data.getUint16Array(offset, contentLength)); + + case DataType.Int32: + return new AsyncReply.ready(data.getInt32Array(offset, contentLength)); + + case DataType.UInt32: + return new AsyncReply.ready(data.getUint32Array(offset, contentLength)); + + case DataType.Int64: + return new AsyncReply.ready(data.getInt64Array(offset, contentLength)); + + case DataType.UInt64: + return new AsyncReply.ready(data.getUint64Array(offset, contentLength)); + + case DataType.Float32: + return new AsyncReply.ready(data.getFloat32Array(offset, contentLength)); + + case DataType.Float64: + return new AsyncReply.ready(data.getFloat64Array(offset, contentLength)); + + case DataType.String: + return new AsyncReply>.ready(data.getStringArray(offset, contentLength)); + + case DataType.Resource: + case DataType.DistributedResource: + return parseResourceArray(data, offset, contentLength, connection); + + case DataType.DateTime: + return new AsyncReply>.ready(data.getDateTimeArray(offset, contentLength)); + + case DataType.Structure: + return parseStructureArray(data, offset, contentLength, connection); + } + } + else + { + switch (t) + { + case DataType.NotModified: + return new AsyncReply.ready(new NotModified()); + + case DataType.Void: + return new AsyncReply.ready(null); + + case DataType.Bool: + return new AsyncReply.ready(data.getBoolean(offset)); + + case DataType.UInt8: + return new AsyncReply.ready(data[offset]); + + case DataType.Int8: + return new AsyncReply.ready(data[offset]); + + case DataType.Char: + return new AsyncReply.ready(data.getChar(offset)); + + case DataType.Int16: + return new AsyncReply.ready(data.getInt16(offset)); + + case DataType.UInt16: + return new AsyncReply.ready(data.getUint16(offset)); + + case DataType.Int32: + return new AsyncReply.ready(data.getInt32(offset)); + + case DataType.UInt32: + return new AsyncReply.ready(data.getUint32(offset)); + + case DataType.Int64: + return new AsyncReply.ready(data.getInt64(offset)); + + case DataType.UInt64: + return new AsyncReply.ready(data.getUint64(offset)); + + case DataType.Float32: + return new AsyncReply.ready(data.getFloat32(offset)); + + case DataType.Float64: + return new AsyncReply.ready(data.getFloat64(offset)); + + case DataType.String: + return new AsyncReply.ready(data.getString(offset, contentLength)); + + case DataType.Resource: + return parseResource(data, offset); + + case DataType.DistributedResource: + return parseDistributedResource(data, offset, connection); + + case DataType.DateTime: + return new AsyncReply.ready(data.getDateTime(offset)); + + case DataType.Structure: + return parseStructure(data, offset, contentLength, connection); + } + } + + + return null; + } + + /// + /// Parse a resource + /// + /// Bytes array + /// Zero-indexed offset. + /// Resource + static AsyncReply parseResource(DC data, int offset) + { + return Warehouse.get(data.getUint32(offset)); + } + + /// + /// Parse a DistributedResource + /// + /// Bytes array + /// Zero-indexed offset. + /// DistributedConnection is required. + /// DistributedResource + static AsyncReply parseDistributedResource(DC data, int offset, DistributedConnection connection) + { + //var g = data.GetGuid(offset); + //offset += 16; + + // find the object + var iid = data.getUint32(offset); + + return connection.fetch(iid);// Warehouse.Get(iid); + } + + /// + /// Check if a resource is local to a given connection. + /// + /// Resource to check. + /// DistributedConnection to check if the resource is local to it. + /// True, if the resource owner is the given connection, otherwise False. + static bool isLocalResource(IResource resource, DistributedConnection connection) + { + if (resource is DistributedResource) + if ((resource as DistributedResource).connection == connection) + return true; + + return false; + } + + /// + /// Compare two resources + /// + /// Initial resource to make comparison with. + /// Next resource to compare with the initial. + /// DistributedConnection is required to check locality. + /// Null, same, local, distributed or same class distributed. + + static int compareResources(IResource initial, IResource next, DistributedConnection connection) + { + if (next == null) + return ResourceComparisonResult.Null; + else if (next == initial) + return ResourceComparisonResult.Same; + else if (isLocalResource(next, connection)) + return ResourceComparisonResult.Local; + else + return ResourceComparisonResult.Distributed; + } + + /// + /// Compose a resource + /// + /// Resource to compose. + /// DistributedConnection is required to check locality. + /// Array of bytes in the network byte order. + static DC composeResource(IResource resource, DistributedConnection connection) + { + if (isLocalResource(resource, connection)) + return DC.uint32ToBytes((resource as DistributedResource).id); + else + { + return new BinaryList().addGuid(resource.instance.template.classId).addUint32(resource.instance.id).toDC(); + //return BinaryList.ToBytes(resource.Instance.Template.ClassId, resource.Instance.Id); + } + } + + /// + /// Compose an array of resources + /// + /// Array of resources. + /// DistributedConnection is required to check locality. + /// If True, prepend the length of the output at the beginning. + /// Array of bytes in the network byte order. + + static DC composeResourceArray(List resources, DistributedConnection connection, [bool prependLength = false]) + { + if (resources == null || resources?.length == 0) + return prependLength ? new DC(4) : new DC(0); + + var rt = new BinaryList(); + var comparsion = compareResources(null, resources[0], connection); + + rt.addUint8(comparsion); + + if (comparsion == ResourceComparisonResult.Local) + rt.addUint32((resources[0] as DistributedResource).id); + else if (comparsion == ResourceComparisonResult.Distributed) + rt.addUint32(resources[0].instance.id); + + for (var i = 1; i < resources.length; i++) + { + comparsion = compareResources(resources[i - 1], resources[i], connection); + rt.addUint8(comparsion); + if (comparsion == ResourceComparisonResult.Local) + rt.addUint32((resources[i] as DistributedResource).id); + else if (comparsion == ResourceComparisonResult.Distributed) + rt.addUint32(resources[i].instance.id); + } + + if (prependLength) + rt.insertInt32(0, rt.length); + + return rt.toDC(); + } + + /// + /// Parse an array of bytes into array of resources + /// + /// Array of bytes. + /// Number of bytes to parse. + /// Zero-indexed offset. + /// DistributedConnection is required to fetch resources. + /// Array of resources. + static AsyncBag parseResourceArray(DC data, int offset, int length, DistributedConnection connection) + { + var reply = new AsyncBag(); + if (length == 0) + { + reply.seal(); + return reply; + } + + var end = offset + length; + + // + var result = data[offset++]; + + AsyncReply previous = null; + + if (result == ResourceComparisonResult.Null) + previous = new AsyncReply.ready(null); + else if (result == ResourceComparisonResult.Local) + { + previous = Warehouse.get(data.getUint32(offset)); + offset += 4; + } + else if (result == ResourceComparisonResult.Distributed) + { + previous = connection.fetch(data.getUint32(offset)); + offset += 4; + } + + reply.add(previous); + + + while (offset < end) + { + result = data[offset++]; + + AsyncReply current = null; + + if (result == ResourceComparisonResult.Null) + { + current = new AsyncReply.ready(null); + } + else if (result == ResourceComparisonResult.Same) + { + current = previous; + } + else if (result == ResourceComparisonResult.Local) + { + current = Warehouse.get(data.getUint32(offset)); + offset += 4; + } + else if (result == ResourceComparisonResult.Distributed) + { + current = connection.fetch(data.getUint32(offset)); + offset += 4; + } + + reply.add(current); + + previous = current; + } + + reply.seal(); + return reply; + } + + /// + /// Compose an array of variables + /// + /// Variables. + /// DistributedConnection is required to check locality. + /// If True, prepend the length as UInt32 at the beginning of the output. + /// Array of bytes in the network byte order. + static DC composeVarArray(List array, DistributedConnection connection, [bool prependLength = false]) + { + var rt = new BinaryList(); + + for (var i = 0; i < array.length; i++) + rt.addDC(compose(array[i], connection)); + + if (prependLength) + rt.insertUint32(0, rt.length); + + return rt.toDC(); + } + + + /// + /// Parse an array of bytes into an array of varialbes. + /// + /// Array of bytes. + /// Zero-indexed offset. + /// Number of bytes to parse. + /// DistributedConnection is required to fetch resources. + /// Array of variables. + static AsyncBag parseVarArray(DC data, int offset, int length, DistributedConnection connection) + { + var rt = new AsyncBag(); + var sizeObject = new SizeObject(); + + while (length > 0) + { + rt.add(parse(data, offset, connection, sizeObject)); + + if (sizeObject.size > 0) + { + offset += sizeObject.size; + length -= sizeObject.size; + } + else + throw new Exception("Error while parsing structured data"); + + } + + rt.seal(); + return rt; + } + + /// + /// Compose an array of property values. + /// + /// PropertyValue array. + /// DistributedConnection is required to check locality. + /// If True, prepend the length as UInt32 at the beginning of the output. + /// Array of bytes in the network byte order. + /// //, bool includeAge = true + static DC composePropertyValueArray(List array, DistributedConnection connection, [bool prependLength = false]) + { + var rt = new BinaryList(); + + for (var i = 0; i < array.length; i++) + rt.addDC(composePropertyValue(array[i], connection)); + if (prependLength) + rt.insertUint32(0, rt.length); + + return rt.toDC(); + } + + /// + /// Compose a property value. + /// + /// Property value + /// DistributedConnection is required to check locality. + /// Array of bytes in the network byte order. + static DC composePropertyValue(PropertyValue propertyValue, DistributedConnection connection)//, bool includeAge = true) + { + + return new BinaryList() + .addUint64(propertyValue.age) + .addDateTime(propertyValue.date) + .addDC(compose(propertyValue.value, connection)) + .toDC(); + } + + + /// + /// Parse property value. + /// + /// Array of bytes. + /// Zero-indexed offset. + /// DistributedConnection is required to fetch resources. + /// Output content size. + /// PropertyValue. + static AsyncReply parsePropertyValue(DC data, int offset, SizeObject sizeObject, DistributedConnection connection) + { + var reply = new AsyncReply(); + + var age = data.getUint64(offset); + offset += 8; + + DateTime date = data.getDateTime(offset); + offset += 8; + + parse(data, offset, connection, sizeObject).then((value) + { + reply.trigger(new PropertyValue(value, age, date)); + }); + + sizeObject.size += 16; + + return reply; + } + + + /// + /// Parse resource history + /// + /// Array of bytes. + /// Zero-indexed offset. + /// Number of bytes to parse. + /// Resource + /// Starting age. + /// Ending age. + /// DistributedConnection is required to fetch resources. + /// + static AsyncReply>> parseHistory(DC data, int offset, int length, IResource resource, DistributedConnection connection) + { + + var list = new KeyList>(); + + var reply = new AsyncReply>>(); + + var bagOfBags = new AsyncBag>(); + + var ends = offset + length; + + //var sizeObject = new SizeObject(); + + while (offset < ends) + { + var index = data[offset++]; + var pt = resource.instance.template.getPropertyTemplateByIndex(index); + list.add(pt, null); + var cs = data.getUint32(offset); + offset += 4; + bagOfBags.add(parsePropertyValueArray(data, offset, cs, connection)); + offset += cs; + } + + bagOfBags.seal(); + + bagOfBags.then((x) + { + for(var i = 0; i < list.length; i++) + list[list.keys.elementAt(i)] = x[i]; + + reply.trigger(list); + }); + + return reply; + + } + + /// + /// Compose resource history + /// + /// History + /// DistributedConnection is required to fetch resources. + /// + static DC composeHistory(KeyList> history, + DistributedConnection connection, [bool prependLength = false]) + { + var rt = new BinaryList(); + + for (var i = 0; i < history.length; i++) + rt.addUint8(history.keys.elementAt(i).index) + .addDC(composePropertyValueArray(history.values.elementAt(i), connection, true)); + + if (prependLength) + rt.insertInt32(0, rt.length); + + return rt.toDC(); + } + + /// + /// Parse an array of PropertyValue. + /// + /// Array of bytes. + /// Zero-indexed offset. + /// Number of bytes to parse. + /// DistributedConnection is required to fetch resources. + /// Whether property age is represented in the data. + /// + static AsyncBag parsePropertyValueArray(DC data, int offset, int length, DistributedConnection connection)//, bool ageIncluded = true) + { + var rt = new AsyncBag(); + + var sizeObject = new SizeObject(); + + while (length > 0) + { + + rt.add(parsePropertyValue(data, offset, sizeObject, connection)); + + if (sizeObject.size > 0) + { + offset += sizeObject.size; + length -= sizeObject.size; + } + else + throw new Exception("Error while parsing ValueInfo structured data"); + } + + rt.seal(); + return rt; + } + + /// + /// Compose a variable + /// + /// Value to compose. + /// DistributedConnection is required to check locality. + /// If True, prepend the DataType at the beginning of the output. + /// Array of bytes in the network byte order. + static DC compose(dynamic value, DistributedConnection connection, [bool prependType = true]) + { + + if (value is Function(DistributedConnection)) + value = Function.apply(value, [connection]); + else if (value is DistributedPropertyContext) + value = (value as DistributedPropertyContext).method(connection); + + var type = getDataType(value, connection); + var rt = new BinaryList(); + + switch (type) + { + + case DataType.Void: + // nothing to do; + break; + + case DataType.String: + var st = DC.stringToBytes(value); + rt.addInt32(st.length).addDC(st); + break; + + case DataType.Resource: + rt.addUint32((value as DistributedResource).id); + break; + + case DataType.DistributedResource: + rt.addUint32((value as IResource).instance.id); + break; + + case DataType.Structure: + rt.addDC(composeStructure(value, connection, true, true, true)); + break; + + case DataType.VarArray: + rt.addDC(composeVarArray(value, connection, true)); + break; + + case DataType.ResourceArray: + rt.addDC(composeResourceArray(value, connection, true)); + break; + + case DataType.StructureArray: + rt.addDC(composeStructureArray(value, connection, true)); + break; + + default: + rt.add(type, value); + if (DataType.isArray(type)) + rt.insertInt32(0, rt.length); + break; + } + + if (prependType) + rt.insertUint8(0, type); + + return rt.toDC(); + } + + /// + /// Get the DataType of a given value. + /// This function is needed to compose a value. + /// + /// Value to find its DataType. + /// DistributedConnection is required to check locality of resources. + /// DataType. + static int getDataType(value, DistributedConnection connection) + { + if (value == null) + return DataType.Void; + + if (value is bool) + return DataType.Bool; + else if (value is List) + return DataType.BoolArray; + else if (value is int) + return DataType.Int64; + else if (value is List || value is Int64List) + return DataType.Int64Array; + else if (value is double) + return DataType.Float64; + else if (value is List) + return DataType.Float64Array; + else if (value is String) + return DataType.String; + else if (value is List) + return DataType.StringArray; + else if (value is Uint8List) + return DataType.UInt8Array; + else if (value is Int8List) + return DataType.Int8Array; + else if (value is Uint16List) + return DataType.UInt16Array; + else if (value is Int16List) + return DataType.Int16Array; + else if (value is Uint32List) + return DataType.UInt32Array; + else if (value is Int32List) + return DataType.Int32Array; + else if (value is Uint64List) + return DataType.Int64Array; + else if (value is DateTime) + return DataType.DateTime; + else if (value is List) + return DataType.DateTimeArray; + else if (value is IResource) + return isLocalResource(value, connection) ? DataType.Resource : DataType.DistributedResource; + else if (value is List) + return DataType.ResourceArray; + else if (value is Structure) + return DataType.Structure; + else if (value is List) + return DataType.StructureArray; + else if (value is List) + return DataType.VarArray; + else + return DataType.Void; + } + + /// + /// Check if a type implements an interface + /// + /// Sub-class type. + /// Super-interface type. + /// True, if implements . + static bool implementsInterface() => _DummyClass() is _DummyClass; +} + +// related to implementsInterface +class _DummyClass { } \ No newline at end of file diff --git a/bin/Data/DC.dart b/bin/Data/DC.dart new file mode 100644 index 0000000..2839792 --- /dev/null +++ b/bin/Data/DC.dart @@ -0,0 +1,643 @@ +/* +* 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 'dart:typed_data'; +import 'dart:convert'; +import 'BinaryList.dart'; +import 'dart:collection'; +import 'Guid.dart'; + +/** + * Created by Ahmed Zamil on 6/10/2019. + */ + + +const UNIX_EPOCH = 621355968000000000; +const TWO_PWR_32 = (1 << 16) * (1 << 16); + +class DC with IterableMixin +{ + + Uint8List _data; + ByteData _dv; + + DC(int length) + { + _data = new Uint8List(length); + _dv = ByteData.view(_data.buffer); + } + + DC.fromUint8Array(Uint8List array) + { + _data = array; + _dv = ByteData.view(_data.buffer); + } + + DC.fromList(List list) + { + _data = Uint8List.fromList(list); + _dv = ByteData.view(_data.buffer); + } + + operator [](index) => _data[index]; + operator []=(index,value) => _data[index] = value; + int get length => _data.length; + + Iterator get iterator => _data.iterator; + + static DC hexToBytes(String value) + { + // convert hex to Uint8Array + var rt = new DC(value.length~/2); + for(var i = 0; i < rt.length; i++) + rt[i] = int.parse(value.substring(i*2, 2), radix: 16); + return rt; + } + + static DC boolToBytes(value) + { + var rt = new DC(1); + rt.setBoolean(0, value); + return rt; + } + + + static DC guidToBytes(Guid value) + { + var rt = new DC(16); + rt.setGuid(0, value); + return rt; + } + + static DC guidArrayToBytes(List value) + { + var rt = new DC(value.length * 16); + for(var i = 0; i < value.length; i++) + rt.setGuid(i * 16, value[i]); + return rt; + } + + static DC boolArrayToBytes(List value) + { + var rt = new DC(value.length); + for(var i = 0; i < value.length; i++) + rt[i] = value[i] ? 1 : 0; + return rt; + } + + static DC int8ToBytes(value) + { + var rt = new DC(1); + rt.setInt8(0, value); + return rt; + } + + static DC int8ArrayToBytes(Int8List value) + { + var rt = new DC(value.length); + for(var i = 0; i < value.length; i++) + rt.setInt8(i, value[i]); + return rt; + } + + + + static DC uint8ToBytes(value) + { + var rt = new DC(1); + rt.setUint8(0, value); + return rt; + } + + static DC uint8ArrayToBytes(Uint8List value) + { + var rt = new DC(value.length); + for(var i = 0; i < value.length; i++) + rt.setUint8(i, value[i]); + return rt; + } + + static DC charToBytes(int value) + { + var rt = new DC(2); + rt.setChar(0, value); + return rt; + } + + static DC charArrayToBytes(Uint16List value) + { + var rt = new DC(value.length * 2); + for(var i = 0; i < value.length; i++) + rt.setChar(i*2, value[i]); + + return rt; + } + + static DC int16ToBytes(int value) + { + var rt = new DC(2); + rt.setInt16(0, value); + return rt; + } + + static DC int16ArrayToBytes(List value) + { + var rt = new DC(value.length * 2); + for(var i = 0; i < value.length; i++) + rt.setInt16(i*2, value[i]); + return rt; + } + + static DC uint16ToBytes(int value) + { + var rt = new DC(2); + rt.setUint16(0, value); + return rt; + } + + static DC uint16ArrayToBytes(Uint16List value) + { + var rt = new DC(value.length * 2); + for(var i = 0; i < value.length; i++) + rt.setUint16(i*2, value[i]); + return rt; + } + + static DC int32ToBytes(int value) + { + var rt = new DC(4); + rt.setInt32(0, value); + return rt; + } + + static DC int32ArrayToBytes(Int32List value) + { + var rt = new DC(value.length * 4); + for(var i = 0; i < value.length; i++) + rt.setInt32(i*4, value[i]); + return rt; + } + + static DC uint32ToBytes(int value) + { + var rt = new DC(4); + rt.setUint32(0, value); + return rt; + } + + static DC uint32ArrayToBytes(Uint32List value) + { + var rt = new DC(value.length * 4); + for(var i = 0; i < value.length; i++) + rt.setUint32(i*4, value[i]); + return rt; + } + + static DC float32ToBytes(double value) + { + var rt = new DC(4); + rt.setFloat32(0, value); + return rt; + } + + static DC float32ArrayToBytes(Float32List value) + { + var rt = new DC(value.length * 4); + for(var i = 0; i < value.length; i++) + rt.setFloat32(i*4, value[i]); + return rt; + } + + static DC int64ToBytes(int value) + { + var rt = new DC(8); + rt.setInt64(0, value); + return rt; + } + + static DC int64ArrayToBytes(Int64List value) + { + var rt = new DC(value.length * 8); + for(var i = 0; i < value.length; i++) + rt.setInt64(i*8, value[i]); + return rt; + } + + static DC uint64ToBytes(int value) + { + var rt = new DC(8); + rt.setUint64(0, value); + return rt; + } + + static DC uint64ArrayToBytes(Uint64List value) + { + var rt = new DC(value.length * 8); + for(var i = 0; i < value.length; i++) + rt.setUint64(i*8, value[i]); + return rt; + } + + static DC float64ToBytes(double value) + { + var rt = new DC(8); + rt.setFloat64(0, value); + return rt; + } + + static DC float64ArrayToBytes(Float64List value) + { + var rt = new DC(value.length * 8); + for(var i = 0; i < value.length; i++) + rt.setFloat64(i*8, value[i]); + return rt; + } + + static DC dateTimeToBytes(DateTime value) + { + var rt = new DC(8); + rt.setDateTime(0, value); + return rt; + } + + + + static DC dateTimeArrayToBytes(List value) + { + var rt = new DC(value.length * 8); + for(var i = 0; i < value.length; i++) + rt.setDateTime(i*8, value[i]); + return rt; + } + + + static DC stringToBytes(String value) + { + var bytes = utf8.encode(value); + var rt = new DC.fromList(bytes); + return rt; + } + + static DC stringArrayToBytes(List value) + { + var list = new BinaryList(); + for(var i = 0; i < value.length; i++) + { + var s = DC.stringToBytes(value[i]); + list.addUint32(s.length).addUint8Array(s.toArray()); + } + + return list.toDC(); + } + + DC append(DC src, int offset, int length) + { + //if (!(src is DC)) + // src = new DC(src); + + var appendix = src.clip(offset, length); + var rt = new DC(this.length + appendix.length); + rt.set(this, 0); + rt.set(appendix, this.length); + + this._data = rt._data; + this._dv = rt._dv; + + return this; + } + + set(DC dc, int offset) + { + _data.setRange(offset, offset + dc.length, dc._data); + } + + static combine(a, aOffset, aLength, b, bOffset, bLength) + { + if (!(a is DC)) + a = new DC(a); + if (!(b is DC)) + b = new DC(b); + + a = a.clip(aOffset, aLength); + b = b.clip(bOffset, bLength); + + var rt = new DC(a.length + b.length); + + + rt.set(a, 0); + rt.set(b, a.length); + return rt; + } + + DC clip(offset, length) + { + return DC.fromUint8Array(Uint8List.fromList(_data.getRange(offset, offset + length).toList())); + } + + getInt8(int offset) + { + return _dv.getInt8(offset); + } + + getUint8(int offset) + { + return _data[offset];// this.dv.getUint8(offset); + } + + getInt16(int offset) + { + return _dv.getInt16(offset); + } + + getUint16(int offset) + { + return _dv.getUint16(offset); + } + + getInt32(int offset) + { + return _dv.getInt32(offset); + } + + getUint32(int offset) + { + return _dv.getUint32(offset); + } + + getFloat32(int offset) + { + return _dv.getFloat32(offset); + } + + getFloat64(int offset) + { + return _dv.getFloat64(offset); + } + + setInt8(int offset, int value) + { + return _dv.setInt8(offset, value); + } + + setUint8(int offset, int value) + { + return _dv.setUint8(offset, value); + } + + setInt16(int offset, int value) + { + return _dv.setInt16(offset, value); + } + + setUint16(int offset, int value) + { + return _dv.setUint16(offset, value); + } + + setInt32(int offset, int value) + { + return _dv.setInt32(offset, value); + } + + setUint32(int offset, int value) + { + return _dv.setUint32(offset, value); + } + + setFloat32(int offset, double value) + { + return _dv.setFloat32(offset, value); + } + + setFloat64(int offset, double value) + { + return _dv.setFloat64(offset, value); + } + + Int8List getInt8Array(int offset, int length) + { + return _data.buffer.asInt8List(offset, length); + } + + Uint8List getUint8Array(int offset, int length) + { + return _data.buffer.asUint8List(offset, length); + } + + Int16List getInt16Array(int offset, int length) + { + return _data.buffer.asInt16List(offset, length); + } + + Uint16List getUint16Array(int offset, int length) + { + return _data.buffer.asUint16List(offset, length); + } + + Int32List getInt32Array(int offset, int length) + { + return _data.buffer.asInt32List(offset, length); + } + + Uint32List getUint32Array(int offset, int length) + { + return _data.buffer.asUint32List(offset, length); + } + + Float32List getFloat32Array(int offset, int length) + { + return _data.buffer.asFloat32List(offset, length); + } + + Float64List getFloat64Array(int offset, int length) + { + return _data.buffer.asFloat64List(offset, length); + } + + Int64List getInt64Array(int offset, int length) + { + return _data.buffer.asInt64List(offset, length); + } + + Uint64List getUint64Array(int offset, int length) + { + return _data.buffer.asUint64List(offset, length); + } + + bool getBoolean(int offset) + { + return this.getUint8(offset) > 0; + } + + setBoolean(int offset, bool value) + { + this.setUint8(offset, value ? 1: 0); + } + + List getBooleanArray(int offset, int length) + { + var rt = new List(); + for(var i = 0; i < length; i++) + rt.add(this.getBoolean(offset+i)); + return rt; + } + + String getChar(int offset) + { + return String.fromCharCode(this.getUint16(offset)); + } + + setChar(int offset, int value) + { + this.setUint16(offset, value); //value.codeUnitAt(0)); + } + + List getCharArray(int offset, int length) + { + var rt = new List(); + for(var i = 0; i < length; i+=2) + rt.add(this.getChar(offset+i)); + return rt; + } + + String getHex(offset, length) + { + var rt = ""; + for(var i = offset; i < offset + length; i++) { + var h = this[i].toString(16); + rt += h.length == 1 ? "0" + h : h; + } + + return rt; + } + + /* + List toList(offset, length) + { + var rt = new List(); + for(var i = 0; i < length; i++) + rt[i] = _data[offset+i] as T; + return rt; + }*/ + + + Uint8List toArray() => _data; + + + String getString(offset, length) + { + var bytes = clip(offset, length)._data;// toList(offset, length); + var str = utf8.decode(bytes); + return str; + } + + List getStringArray(offset, length) + { + var rt = List(); + var i = 0; + + while (i < length) + { + var cl = this.getUint32(offset + i); + i += 4; + rt.add(this.getString(offset + i, cl)); + i += cl; + } + + return rt; + } + + getInt64(offset) + { + return _dv.getUint64(offset); + } + + getUint64(offset) + { + return _dv.getInt64(offset); + } + + void setInt64(offset, value) + { + _dv.setInt64(offset, value); + } + + void setUint64(offset, value) + { + + _dv.setUint64(offset, value); + } + + setDateTime(offset, value) + { + // Unix Epoch + var ticks = 621355968000000000 + (value.getTime() * 10000); + this.setUint64(offset, ticks); + } + + DateTime getDateTime(int offset) + { + var ticks = this.getUint64(offset); + // there are 10,000 ticks in a millisecond + return DateTime.fromMillisecondsSinceEpoch((ticks-UNIX_EPOCH)~/10000); + } + + List getDateTimeArray(int offset, int length) + { + var rt = new List(); + for(var i = 0; i < length; i+=8) + rt.add(this.getDateTime(offset+i)); + return rt; + } + + Guid getGuid(int offset) + { + return new Guid(this.clip(offset, 16)); + } + + setGuid(int offset, Guid guid) + { + set(guid.value, offset); + } + + List getGuidArray(int offset, int length) + { + var rt = []; + for(var i = 0; i < length; i+=16) + rt.add(this.getGuid(offset+i)); + return rt; + } + + bool sequenceEqual(ar) + { + if (ar.length != this.length) + return false; + else + { + for(var i = 0; i < this.length; i++) + if (ar[i] != this[i]) + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/bin/Data/DataType.dart b/bin/Data/DataType.dart new file mode 100644 index 0000000..91d5dae --- /dev/null +++ b/bin/Data/DataType.dart @@ -0,0 +1,115 @@ +/* + +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. + +*/ + +class DataType +{ + static const int Void = 0x0, + //Variant, + Bool = 1, + Int8 = 2, + UInt8 = 3, + Char = 4, + Int16 = 5, + UInt16 = 6, + Int32 = 7, + UInt32 = 8, + Int64 = 9, + UInt64 = 0xA, + Float32 = 0xB, + Float64 = 0xC, + Decimal = 0xD, + DateTime = 0xE, + Resource = 0xF, + DistributedResource = 0x10, + ResourceLink = 0x11, + String = 0x12, + Structure = 0x13, + //Stream, + //Array = 0x80, + VarArray = 0x80, + BoolArray = 0x81, + UInt8Array = 0x82, + Int8Array = 0x83, + CharArray = 0x84, + Int16Array = 0x85, + UInt16Array = 0x86, + Int32Array = 0x87, + UInt32Array = 0x88, + Int64Array = 0x89, + UInt64Array = 0x8A, + Float32Array = 0x8B, + Float64Array = 0x8C, + DecimalArray = 0x8D, + DateTimeArray = 0x8E, + ResourceArray = 0x8F, + DistributedResourceArray = 0x90, + ResourceLinkArray = 0x91, + StringArray = 0x92, + StructureArray = 0x93, + NotModified = 0x7F, + Unspecified = 0xFF; + + static bool isArray(int type) + { + return ((type & 0x80) == 0x80) && (type != NotModified); + } + + static int getElementType(int type) + { + return type & 0x7F; + } + + static int size(int type) + { + switch (type) + { + case DataType.Void: + case DataType.NotModified: + return 0; + case DataType.Bool: + case DataType.UInt8: + case DataType.Int8: + return 1; + case DataType.Char: + case DataType.UInt16: + case DataType.Int16: + return 2; + case DataType.Int32: + case DataType.UInt32: + case DataType.Float32: + case DataType.Resource: + return 4; + case DataType.Int64: + case DataType.UInt64: + case DataType.Float64: + case DataType.DateTime: + return 8; + case DataType.DistributedResource: + return 4; + + default: + return -1; + } + } +} diff --git a/bin/Data/Guid.dart b/bin/Data/Guid.dart new file mode 100644 index 0000000..84c0c43 --- /dev/null +++ b/bin/Data/Guid.dart @@ -0,0 +1,18 @@ +import 'DC.dart'; + +class Guid +{ + DC _data; + + Guid(DC data) + { + _data = data; + } + + DC get value => _data; + + @override + String toString() { + return _data.getString(0, _data.length); + } +} \ No newline at end of file diff --git a/bin/Data/KeyList.dart b/bin/Data/KeyList.dart new file mode 100644 index 0000000..0720361 --- /dev/null +++ b/bin/Data/KeyList.dart @@ -0,0 +1,156 @@ +/* + +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 '../Core/IEventHandler.dart'; +import '../Core/IDestructible.dart'; + +import 'dart:collection'; +import 'Codec.dart'; + +class KeyList extends IEventHandler with MapMixin +{ + + dynamic owner; + + Map _map = new Map(); + + Iterator get iterator => _map.keys.iterator; + + Iterable get keys => _map.keys; + Iterable get values => _map.values; + + + operator[](index) => _map[index]; + + operator []= (index, value) => add(index, value); + + + at(int index) => _map.values.elementAt(index); + + + bool _removableList; + + + T take(KT key) + { + if (_map.containsKey(key)) + { + var v = _map[key]; + remove(key); + return v; + } + else + return null; + } + + + List toArray() => _map.values.toList(); + + void add(KT key, T value) + { + if (_removableList) + if (value != null) + (value as IDestructible).on("destroy", _itemDestroyed); + + if (_map.containsKey(key)) + { + var oldValue = _map[key]; + if (_removableList) + if (oldValue != null) + (oldValue as IDestructible).off("destroy", _itemDestroyed); + + _map[key] = value; + + emitArgs("modified", [key, oldValue, value, this]); + } + else + { + _map[key] = value; + + emitArgs("add", [value, this]); + + } + + } + + _itemDestroyed(T sender) + { + removeValue(sender); + } + + removeValue(T value) + { + var toRemove = new List(); + for (var k in _map.keys) + if (_map[k] == value) + toRemove.add(k); + + for (var k in toRemove) + remove(k); + } + + clear() + { + if (_removableList) + for (var v in _map.values) + (v as IDestructible)?.off("destroy", _itemDestroyed); + + _map.clear(); + + emitArgs("cleared", [this]); + + } + + + + T remove(key) + { + if (!_map.containsKey(key)) + return null; + + var value = _map[key]; + + if (_removableList) + (value as IDestructible)?.off("destroy", _itemDestroyed); + + + _map.remove(key); + + emitArgs("removed", [key, value, this]); + + return value; + } + + + + int get count => _map.length; + + bool contains(KT key) => _map.containsKey(key); + + + KeyList([owner = null]) + { + _removableList = Codec.implementsInterface(); + this.owner = owner; + } +} \ No newline at end of file diff --git a/bin/Data/NotModified.dart b/bin/Data/NotModified.dart new file mode 100644 index 0000000..d805ffc --- /dev/null +++ b/bin/Data/NotModified.dart @@ -0,0 +1,4 @@ +class NotModified +{ + +} diff --git a/bin/Data/PropertyValue.dart b/bin/Data/PropertyValue.dart new file mode 100644 index 0000000..1a2c329 --- /dev/null +++ b/bin/Data/PropertyValue.dart @@ -0,0 +1,23 @@ +class PropertyValue +{ + /// + /// Get or set the value. + /// + dynamic value; + /// + /// Get or set date of modification or occurrence. + /// + DateTime date; + /// + /// Get or set property age. + /// + int age; + + /// + /// Create an instance of PropertyValue. + /// + /// Value. + /// Age. + /// Date. + PropertyValue(this.value, this.age, this.date); +} diff --git a/bin/Data/ResourceComparisonResult.dart b/bin/Data/ResourceComparisonResult.dart new file mode 100644 index 0000000..e0864b3 --- /dev/null +++ b/bin/Data/ResourceComparisonResult.dart @@ -0,0 +1,7 @@ +class ResourceComparisonResult +{ + static const Null = 0; + static const Distributed = 1; + static const Local = 2; + static const Same = 3; +} \ No newline at end of file diff --git a/bin/Data/SizeObject.dart b/bin/Data/SizeObject.dart new file mode 100644 index 0000000..fb2f2d8 --- /dev/null +++ b/bin/Data/SizeObject.dart @@ -0,0 +1,4 @@ +class SizeObject +{ + int size; +} \ No newline at end of file diff --git a/bin/Data/Structure.dart b/bin/Data/Structure.dart new file mode 100644 index 0000000..e69d6a2 --- /dev/null +++ b/bin/Data/Structure.dart @@ -0,0 +1,59 @@ +/* + +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 'dart:collection'; + +class Structure with MapMixin//, IterableMixin +{ + + Map _map = new Map(); + + Iterator get iterator => _map.keys.iterator; + + Iterable get keys => _map.keys; + + operator[](index) => _map[index]; + + operator []= (index, value) => _map[index] = value; + + remove(key) => _map.remove(key); + + clear() => _map.clear(); + + + at(int index) => _map.values.elementAt(index); + + List getKeys() => _map.keys.toList(); + + + Structure.fromMap(Map map) + { + for(var i in map.keys) + _map[i.toString()] = map[i]; + } + + Structure() + { + // do nothing + } +} diff --git a/bin/Data/StructureComparisonResult.dart b/bin/Data/StructureComparisonResult.dart new file mode 100644 index 0000000..083d783 --- /dev/null +++ b/bin/Data/StructureComparisonResult.dart @@ -0,0 +1,8 @@ +class StructureComparisonResult +{ + static const int Null = 0; + static const int Structure = 1; + static const int StructureSameKeys = 2; + static const int StructureSameTypes = 3; + static const int Same = 4; +} \ No newline at end of file diff --git a/bin/Data/StructureMetadata.dart b/bin/Data/StructureMetadata.dart new file mode 100644 index 0000000..5305f20 --- /dev/null +++ b/bin/Data/StructureMetadata.dart @@ -0,0 +1,8 @@ +class StructureMetadata +{ + + List keys; + List types; + + //const StructureMetadata(this.keys, this.types); +} \ No newline at end of file diff --git a/bin/Data/ValueObject.dart b/bin/Data/ValueObject.dart new file mode 100644 index 0000000..448d1fd --- /dev/null +++ b/bin/Data/ValueObject.dart @@ -0,0 +1,4 @@ +class ValueObject +{ + dynamic value; +} \ No newline at end of file diff --git a/bin/Net/IIP/DistributedConnection.dart b/bin/Net/IIP/DistributedConnection.dart new file mode 100644 index 0000000..11bbda5 --- /dev/null +++ b/bin/Net/IIP/DistributedConnection.dart @@ -0,0 +1,2466 @@ +/* + +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(); + } +} diff --git a/bin/Net/IIP/DistributedPropertyContext.dart b/bin/Net/IIP/DistributedPropertyContext.dart new file mode 100644 index 0000000..2f7109e --- /dev/null +++ b/bin/Net/IIP/DistributedPropertyContext.dart @@ -0,0 +1,18 @@ +import 'DistributedConnection.dart'; +class DistributedPropertyContext +{ + dynamic value; + DistributedConnection connection; + dynamic Function(DistributedConnection) method; + + + DistributedPropertyContext(this.method) + { + + } + + DistributedPropertyContext.setter(this.value, this.connection) + { + + } +} diff --git a/bin/Net/IIP/DistributedResource.dart b/bin/Net/IIP/DistributedResource.dart new file mode 100644 index 0000000..dfbbd23 --- /dev/null +++ b/bin/Net/IIP/DistributedResource.dart @@ -0,0 +1,300 @@ +/* + +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 '../../Resource/IResource.dart'; +import '../../Core/AsyncReply.dart'; +import '../../Data/PropertyValue.dart'; +import '../../Data/Structure.dart'; +import '../../Data/Codec.dart'; +import './DistributedConnection.dart'; +import '../Packets/IIPPacketAction.dart'; + +class DistributedResource extends IResource +{ + + int _instanceId; + DistributedConnection _connection; + + + bool _isAttached = false; + bool _isReady = false; + + + + String _link; + List _properties; + + + bool _destroyed = false; + + /// + /// Connection responsible for the distributed resource. + /// + DistributedConnection get connection => _connection; + + /// + /// Resource link + /// + String get link => _link; + + /// + /// Instance Id given by the other end. + /// + int get id => _instanceId; + + /// + /// IDestructible interface. + /// + void destroy() + { + _destroyed = true; + emitArgs("destroy", [this]); + } + + /// + /// Resource is ready when all its properties are attached. + /// + bool get isReady => _isReady; + + /// + /// Resource is attached when all its properties are received. + /// + bool get IsAttached => _isAttached; + + + // public DistributedResourceStack Stack + //{ + // get { return stack; } + //} + + /// + /// Create a new distributed resource. + /// + /// Connection responsible for the distributed resource. + /// Resource template. + /// Instance Id given by the other end. + /// Resource age. + DistributedResource(DistributedConnection connection, int instanceId, int age, String link) + { + this._link = link; + this._connection = connection; + this._instanceId = instanceId; + } + + void _ready() + { + _isReady = true; + } + + /// + /// Export all properties with ResourceProperty attributed as bytes array. + /// + /// + List serialize() + { + + var props = new List(_properties.length); + + + for (var i = 0; i < _properties.length; i++) + props[i] = new PropertyValue(_properties[i], instance.getAge(i), instance.getModificationDate(i)); + + return props; + } + + bool attached(List properties) + { + if (_isAttached) + return false; + else + { + _properties = new List(properties.length);// object[properties.Length]; + + //_events = new DistributedResourceEvent[Instance.Template.Events.Length]; + + for (var i = 0; i < properties.length; i++) + { + instance.setAge(i, properties[i].age); + instance.setModificationDate(i, properties[i].date); + _properties[i] = properties[i].value; + } + + // trigger holded events/property updates. + //foreach (var r in afterAttachmentTriggers) + // r.Key.Trigger(r.Value); + + //afterAttachmentTriggers.Clear(); + + _isAttached = true; + + } + return true; + } + + void emitEventByIndex(int index, List args) + { + var et = instance.template.getEventTemplateByIndex(index); + //events[index]?.Invoke(this, args); + emitArgs(et.name, args); + + instance.emitResourceEvent(null, null, et.name, args); + } + + AsyncReply invokeByNamedArguments(int index, Structure namedArgs) + { + if (_destroyed) + throw new Exception("Trying to access destroyed object"); + + if (index >= instance.template.functions.length) + throw new Exception("Function index is incorrect"); + + + return connection.sendInvokeByNamedArguments(_instanceId, index, namedArgs); + } + + AsyncReply invokeByArrayArguments(int index, List args) + { + if (_destroyed) + throw new Exception("Trying to access destroyed object"); + + if (index >= instance.template.functions.length) + throw new Exception("Function index is incorrect"); + + + return connection.sendInvokeByArrayArguments(_instanceId, index, args); + } + + + String _getMemberName(Symbol symbol) + { + var memberName = symbol.toString(); + if (memberName.endsWith("=\")")) + return memberName.substring(8, memberName.length - 3); + else + return memberName.substring(8, memberName.length - 2); + } + + @override //overring noSuchMethod + noSuchMethod(Invocation invocation) + { + var memberName = _getMemberName(invocation.memberName); + + if (invocation.isMethod) + { + var ft = instance.template.getFunctionTemplateByName(memberName); + + if (_isAttached && ft!=null) + { + if (invocation.namedArguments.length > 0) + { + var namedArgs = new Structure(); + for(var p in invocation.namedArguments.keys) + namedArgs[_getMemberName(p)] = invocation.namedArguments[p]; + + return invokeByNamedArguments(ft.index, namedArgs); + } + else + { + return invokeByArrayArguments(ft.index, invocation.positionalArguments); + } + } + } + else if (invocation.isSetter) + { + var pt = instance.template.getPropertyTemplateByName(memberName); + + if (pt != null) + { + set(pt.index, invocation.positionalArguments[0]); + return true; + } + } + else if (invocation.isGetter) + { + + var pt = instance.template.getPropertyTemplateByName(memberName); + + if (pt != null) + { + return get(pt.index); + } + } + + return null; + } + + + + /// + /// Get a property value. + /// + /// Zero-based property index. + /// Value + get(int index) + { + if (index >= _properties.length) + return null; + return _properties[index]; + } + + + void updatePropertyByIndex(int index, dynamic value) + { + var pt = instance.template.getPropertyTemplateByIndex(index); + _properties[index] = value; + instance.emitModification(pt, value); + } + + /// + /// Set property value. + /// + /// Zero-based property index. + /// Value + /// Indicator when the property is set. + AsyncReply set(int index, dynamic value) + { + if (index >= _properties.length) + return null; + + var reply = new AsyncReply(); + + var parameters = Codec.compose(value, connection); + connection.sendRequest(IIPPacketAction.SetProperty) + .addUint32(_instanceId) + .addUint8(index) + .addDC(parameters) + .done() + .then((res) + { + // not really needed, server will always send property modified, + // this only happens if the programmer forgot to emit in property setter + _properties[index] = value; + reply.trigger(null); + }); + + return reply; + } + + +} \ No newline at end of file diff --git a/bin/Net/IIP/DistributedResourceQueueItem.dart b/bin/Net/IIP/DistributedResourceQueueItem.dart new file mode 100644 index 0000000..6777812 --- /dev/null +++ b/bin/Net/IIP/DistributedResourceQueueItem.dart @@ -0,0 +1,39 @@ +/* + +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 'DistributedResourceQueueItemType.dart'; +import 'DistributedResource.dart'; + +class DistributedResourceQueueItem +{ + final DistributedResourceQueueItemType type; + final int index; + final dynamic value; + final DistributedResource resource; + + DistributedResourceQueueItem(this.resource, this.type, this.value, this.index) + { + + } +} diff --git a/bin/Net/IIP/DistributedResourceQueueItemType.dart b/bin/Net/IIP/DistributedResourceQueueItemType.dart new file mode 100644 index 0000000..563a71a --- /dev/null +++ b/bin/Net/IIP/DistributedResourceQueueItemType.dart @@ -0,0 +1,5 @@ + enum DistributedResourceQueueItemType + { + Propery, + Event + } \ No newline at end of file diff --git a/bin/Net/NetworkBuffer.dart b/bin/Net/NetworkBuffer.dart new file mode 100644 index 0000000..8e53f83 --- /dev/null +++ b/bin/Net/NetworkBuffer.dart @@ -0,0 +1,134 @@ +/* + +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 '../Data/DC.dart'; +import 'dart:core'; + +class NetworkBuffer +{ + DC _data; + + int _neededDataLength = 0; + + NetworkBuffer() + { + _data = new DC(0); + } + + bool get protected => _neededDataLength > _data.length; + + int get available => _data.length; + + + + void holdForNextWrite(DC src, int offset, int size) + { + holdFor(src, offset, size, size + 1); + } + + + void holdFor(DC src, int offset, int size, int needed) + { + //lock (syncLock) + //{ + if (size >= needed) + throw new Exception("Size >= Needed !"); + + //trim = true; + _data = DC.combine(src, offset, size, _data, 0, _data.length); + _neededDataLength = needed; + + //} + } + + void holdForNeeded(DC src, int needed) + { + holdFor(src, 0, src.length, needed); + } + + bool protect(DC data, int offset, int needed) + { + int dataLength = _data.length - offset; + + // protection + if (dataLength < needed) + { + holdFor(data, offset, dataLength, needed); + return true; + } + else + return false; + } + + + + void write(DC src, int offset, int length) + { + //lock(syncLock) + _data.append(src, offset, length); + } + + bool get canRead + { + if (_data.length == 0) + return false; + if (_data.length < _neededDataLength) + return false; + + return true; + } + + DC read() + { + //lock (syncLock) + //{ + if (_data.length == 0) + return null; + + DC rt = null; + + if (_neededDataLength == 0) + { + rt = _data; + _data = new DC(0); + } + else + { + if (_data.length >= _neededDataLength) + { + rt = _data; + _data = new DC(0); + _neededDataLength = 0; + return rt; + } + else + { + return null; + } + } + //} + + return rt; + } +} \ No newline at end of file diff --git a/bin/Net/NetworkConnection.dart b/bin/Net/NetworkConnection.dart new file mode 100644 index 0000000..e2c4014 --- /dev/null +++ b/bin/Net/NetworkConnection.dart @@ -0,0 +1,198 @@ +/* + +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 '../Core/IDestructible.dart'; +import 'Sockets/ISocket.dart'; +import 'Sockets/SocketState.dart'; +import 'NetworkBuffer.dart'; +import '../Data/DC.dart'; +import 'Sockets/IPEndPoint.dart'; + +class NetworkConnection extends IDestructible +{ + ISocket _sock; + + DateTime _lastAction; + + //public delegate void DataReceivedEvent(NetworkConnection sender, NetworkBuffer data); + //public delegate void ConnectionClosedEvent(NetworkConnection sender); + //public delegate void ConnectionEstablishedEvent(NetworkConnection sender); + + //public event ConnectionEstablishedEvent OnConnect; + //public event DataReceivedEvent OnDataReceived; + //public event ConnectionClosedEvent OnClose; + //public event DestroyedEvent OnDestroy; + //object receivingLock = new object(); + + bool _processing = false; + + + void destroy() + { + // if (connected) + close(); + emitArgs("close", [this]); + //OnDestroy?.Invoke(this); + } + + NetworkConnection() + { + + } + + + + ISocket get socket => _sock; + + void assign(ISocket socket) + { + _lastAction = DateTime.now(); + _sock = socket; + + socket.on("receive", socket_OnReceive); + socket.on("close", socket_OnClose); + socket.on("connect", socket_OnConnect); + + } + + + void socket_OnConnect() + { + emitArgs("connect", [this]); + } + + void socket_OnClose() + { + emitArgs("close", [this]); + } + + void socket_OnReceive(NetworkBuffer buffer) + { + try + { + // Unassigned ? + if (_sock == null) + return; + + // Closed ? + if (_sock.state == SocketState.Closed || _sock.state == SocketState.Terminated) // || !connected) + return; + + _lastAction = DateTime.now(); + + if (!_processing) + { + _processing = true; + + try + { + while (buffer.available > 0 && !buffer.protected) + dataReceived(buffer); + } + catch(ex) + { + + } + + _processing = false; + } + + } + catch (ex) + { + //Global.Log("NetworkConnection", LogType.Warning, ex.ToString()); + } + } + + ISocket unassign() + { + if (_sock != null) + { + // connected = false; + _sock.off("close", socket_OnClose); + _sock.off("connect", socket_OnConnect); + _sock.off("receive", socket_OnReceive); + + var rt = _sock; + _sock = null; + + return rt; + } + else + return null; + } + + void dataReceived(NetworkBuffer data) + { + emitArgs("dataReceived", [data]); + } + + void close() + { + + try + { + if (_sock != null) + _sock.close(); + } + catch(ex) + { + //Global.Log("NetworkConenction:Close", LogType.Error, ex.ToString()); + + } + } + + DateTime get lastAction => _lastAction; + + + IPEndPoint get remoteEndPoint => _sock?.remoteEndPoint; + + IPEndPoint get localEndPoint => _sock?.localEndPoint; + + bool get connected => _sock.state == SocketState.Established; + + + void send(DC msg) + { + + try + { + + if (_sock != null) + { + _lastAction = DateTime.now(); + _sock.send(msg); + } + } + catch (ex) + { + //Console.WriteLine(ex.ToString()); + } + } + + void sendString(String data) + { + send(DC.stringToBytes(data)); + } +} \ No newline at end of file diff --git a/bin/Net/Packets/IIPAuthPacket.dart b/bin/Net/Packets/IIPAuthPacket.dart new file mode 100644 index 0000000..07f80d9 --- /dev/null +++ b/bin/Net/Packets/IIPAuthPacket.dart @@ -0,0 +1,278 @@ +/* + +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 '../../Data/DC.dart'; +import 'IIPAuthPacketAction.dart'; +import 'IIPAuthPacketCommand.dart'; +import 'IIPAuthPacketMethod.dart'; + +class IIPAuthPacket +{ + + int command; + int action; + + int errorCode; + String errorMessage; + + int localMethod; + + DC sourceInfo; + + DC hash; + + DC sessionId; + + int remoteMethod; + + String domain; + + int certificateId; + + String localUsername; + + String remoteUsername; + + DC localPassword; + + DC remotePassword; + + DC localToken; + + DC remoteToken; + + DC asymetricEncryptionKey; + + DC localNonce; + + DC remoteNonce; + + int _dataLengthNeeded; + + bool _notEnough(int offset, int ends, int needed) + { + if (offset + needed > ends) + { + _dataLengthNeeded = needed - (ends - offset); + return true; + } + else + return false; + } + + toString() + { + return command.toString() + " " + action.toString(); + } + + int parse(DC data, int offset, int ends) + { + var oOffset = offset; + + if (_notEnough(offset, ends, 1)) + return -_dataLengthNeeded; + + command = (data[offset] >> 6); + + if (command == IIPAuthPacketCommand.Action) + { + action = (data[offset++] & 0x3f); + + if (action == IIPAuthPacketAction.AuthenticateHash) + { + if (_notEnough(offset, ends, 32)) + return -_dataLengthNeeded; + + hash = data.clip(offset, 32); + + //var hash = new byte[32]; + //Buffer.BlockCopy(data, (int)offset, hash, 0, 32); + //Hash = hash; + + offset += 32; + } + else if (action == IIPAuthPacketAction.NewConnection) + { + if (_notEnough(offset, ends, 2)) + return -_dataLengthNeeded; + + var length = data.getUint16(offset); + + offset += 2; + + if (_notEnough(offset, ends, length)) + return -_dataLengthNeeded; + + sourceInfo = data.clip(offset, length); + + //var sourceInfo = new byte[length]; + //Buffer.BlockCopy(data, (int)offset, sourceInfo, 0, length); + //SourceInfo = sourceInfo; + + offset += 32; + } + else if (action == IIPAuthPacketAction.ResumeConnection + || action == IIPAuthPacketAction.ConnectionEstablished) + { + //var sessionId = new byte[32]; + + if (_notEnough(offset, ends, 32)) + return -_dataLengthNeeded; + + sessionId = data.clip(offset, 32); + + //Buffer.BlockCopy(data, (int)offset, sessionId, 0, 32); + //SessionId = sessionId; + + offset += 32; + } + } + else if (command == IIPAuthPacketCommand.Declare) + { + remoteMethod = ((data[offset] >> 4) & 0x3); + localMethod = ((data[offset] >> 2) & 0x3); + var encrypt = ((data[offset++] & 0x2) == 0x2); + + + if (_notEnough(offset, ends, 1)) + return -_dataLengthNeeded; + + var domainLength = data[offset++]; + if (_notEnough(offset, ends, domainLength)) + return -_dataLengthNeeded; + + var domain = data.getString(offset, domainLength); + + this.domain = domain; + + offset += domainLength; + + + if (remoteMethod == IIPAuthPacketMethod.Credentials) + { + if (localMethod == IIPAuthPacketMethod.None) + { + if (_notEnough(offset, ends, 33)) + return -_dataLengthNeeded; + + + remoteNonce = data.clip(offset, 32); + + offset += 32; + + var length = data[offset++]; + + if (_notEnough(offset, ends, length)) + return -_dataLengthNeeded; + + remoteUsername = data.getString(offset, length); + + + offset += length; + } + } + + if (encrypt) + { + if (_notEnough(offset, ends, 2)) + return -_dataLengthNeeded; + + var keyLength = data.getUint16(offset); + + offset += 2; + + if (_notEnough(offset, ends, keyLength)) + return -_dataLengthNeeded; + + + asymetricEncryptionKey = data.clip(offset, keyLength); + + offset += keyLength; + } + } + else if (command == IIPAuthPacketCommand.Acknowledge) + { + remoteMethod = ((data[offset] >> 4) & 0x3); + localMethod = ((data[offset] >> 2) & 0x3); + var encrypt = ((data[offset++] & 0x2) == 0x2); + + if (_notEnough(offset, ends, 1)) + return -_dataLengthNeeded; + + + if (remoteMethod == IIPAuthPacketMethod.Credentials) + { + if (localMethod == IIPAuthPacketMethod.None) + { + if (_notEnough(offset, ends, 32)) + return -_dataLengthNeeded; + + remoteNonce = data.clip(offset, 32); + offset += 32; + + } + } + + if (encrypt) + { + if (_notEnough(offset, ends, 2)) + return -_dataLengthNeeded; + + var keyLength = data.getUint16(offset); + + offset += 2; + + if (_notEnough(offset, ends, keyLength)) + return -_dataLengthNeeded; + + asymetricEncryptionKey = data.clip(offset, keyLength); + + offset += keyLength; + } + } + else if (command == IIPAuthPacketCommand.Error) + { + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + offset++; + errorCode = data[offset++]; + + + var cl = data.getUint16(offset); + offset += 2; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + errorMessage = data.getString(offset, cl); + offset += cl; + + } + + + return offset - oOffset; + + } + +} \ No newline at end of file diff --git a/bin/Net/Packets/IIPAuthPacketAction.dart b/bin/Net/Packets/IIPAuthPacketAction.dart new file mode 100644 index 0000000..182df4e --- /dev/null +++ b/bin/Net/Packets/IIPAuthPacketAction.dart @@ -0,0 +1,16 @@ +class IIPAuthPacketAction +{ + // Authenticate + static const int AuthenticateHash = 0; + + + //Challenge, + //CertificateRequest, + //CertificateReply, + //EstablishRequest, + //EstablishReply + + static const int NewConnection = 0x20; + static const int ResumeConnection = 0x21; + static const int ConnectionEstablished = 0x28; +} \ No newline at end of file diff --git a/bin/Net/Packets/IIPAuthPacketCommand.dart b/bin/Net/Packets/IIPAuthPacketCommand.dart new file mode 100644 index 0000000..f8dde7b --- /dev/null +++ b/bin/Net/Packets/IIPAuthPacketCommand.dart @@ -0,0 +1,7 @@ +class IIPAuthPacketCommand +{ + static const int Action = 0; + static const int Declare = 1; + static const int Acknowledge = 2; + static const int Error = 3; +} \ No newline at end of file diff --git a/bin/Net/Packets/IIPAuthPacketMethod.dart b/bin/Net/Packets/IIPAuthPacketMethod.dart new file mode 100644 index 0000000..34d4eb3 --- /dev/null +++ b/bin/Net/Packets/IIPAuthPacketMethod.dart @@ -0,0 +1,8 @@ + + class IIPAuthPacketMethod + { + static const int None = 0; + static const int Certificate = 1; + static const int Credentials = 2; + static const int Token = 3; + } \ No newline at end of file diff --git a/bin/Net/Packets/IIPPacket.dart b/bin/Net/Packets/IIPPacket.dart new file mode 100644 index 0000000..1beefcb --- /dev/null +++ b/bin/Net/Packets/IIPPacket.dart @@ -0,0 +1,727 @@ +/* + +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 '../../Data/DC.dart'; +import '../../Data/Guid.dart'; + +import 'IIPPacketAction.dart'; +import 'IIPPacketCommand.dart'; +import 'IIPPacketEvent.dart'; +import 'IIPPacketReport.dart'; +import '../../Data/Codec.dart'; +import '../../Data/DataType.dart'; + +class IIPPacket +{ + + + + + + + int report; + + int command; + + int action; + + int event; + + int previousCommand; + + + int previousAction; + + int previousEvent; + + + int resourceId; + int newResourceId; + + int childId; + int storeId; + + int resourceAge; + DC content; + int errorCode; + String errorMessage; + String className ; + String resourceLink ; + Guid classId ; + int methodIndex ; + String methodName; + int callbackId ; + int progressValue; + int progressMax ; + DateTime fromDate ; + DateTime toDate; + int fromAge; + int toAge; + + int _dataLengthNeeded; + int _originalOffset; + + + bool _notEnough(int offset, int ends, int needed) + { + if (offset + needed > ends) + { + //dataLengthNeeded = needed - (ends - offset); + _dataLengthNeeded = needed - (ends - _originalOffset); + + return true; + } + else + return false; + } + + int parse(DC data, int offset, int ends) + { + _originalOffset = offset; + + if (_notEnough(offset, ends, 1)) + return -_dataLengthNeeded; + + previousCommand = command; + + command = (data[offset] >> 6); + + if (command == IIPPacketCommand.Event) + { + event = (data[offset++] & 0x3f); + + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + resourceId = data.getUint32(offset); + offset += 4; + } + else if (command == IIPPacketCommand.Report) + { + report = (data[offset++] & 0x3f); + + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + callbackId = data.getUint32(offset); + offset += 4; + } + else + { + previousAction = action; + action = (data[offset++] & 0x3f); + + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + callbackId = data.getUint32(offset); + offset += 4; + } + + if (command == IIPPacketCommand.Event) + { + if (event == IIPPacketEvent.ResourceReassigned) + { + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + newResourceId = data.getUint32( offset); + offset += 4; + + } + else if (event == IIPPacketEvent.ResourceDestroyed) + { + // nothing to parse + } + else if (event == IIPPacketEvent.ChildAdded + || event == IIPPacketEvent.ChildRemoved) + { + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + childId = data.getUint32(offset); + offset += 4; + } + else if(event == IIPPacketEvent.Renamed) + { + if (_notEnough(offset, ends, 2)) + return -_dataLengthNeeded; + + var cl = data.getUint16(offset); + offset += 2; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + content = data.clip(offset, cl); + + offset += cl; + } + else if (event == IIPPacketEvent.PropertyUpdated) + { + if (_notEnough(offset, ends, 2)) + return -_dataLengthNeeded; + + methodIndex = data[offset++]; + + var dt = data[offset++]; + var size = DataType.size(dt); + + if (size < 0) + { + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + var cl = data.getUint32( offset); + offset += 4; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + content = data.clip( offset - 5, cl + 5); + offset += cl; + } + else + { + if (_notEnough(offset, ends, size)) + return -_dataLengthNeeded; + + content = data.clip(offset - 1, size + 1); + offset += size; + } + } + else if (event == IIPPacketEvent.EventOccurred) + { + if (_notEnough(offset, ends, 5)) + return -_dataLengthNeeded; + + methodIndex = data[offset++]; + + var cl = data.getUint32( offset); + offset += 4; + + content = data.clip(offset, cl); + offset += cl; + + } + // Attribute + else if (event == IIPPacketEvent.AttributesUpdated) + { + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + var cl = data.getUint32(offset); + offset += 4; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + content = data.clip(offset, cl); + + offset += cl; + } + } + else if (command == IIPPacketCommand.Request) + { + if (action == IIPPacketAction.AttachResource) + { + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + resourceId = data.getUint32(offset); + offset += 4; + } + else if (action == IIPPacketAction.ReattachResource) + { + if (_notEnough(offset, ends, 12)) + return -_dataLengthNeeded; + + resourceId = data.getUint32(offset); + offset += 4; + + resourceAge = data.getUint64(offset); + offset += 8; + + } + else if (action == IIPPacketAction.DetachResource) + { + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + resourceId = data.getUint32(offset); + offset += 4; + + } + else if (action == IIPPacketAction.CreateResource) + { + if (_notEnough(offset, ends, 12)) + return -_dataLengthNeeded; + + storeId = data.getUint32(offset); + offset += 4; + resourceId = data.getUint32(offset); + offset += 4; + + var cl = data.getUint32(offset); + offset += 4; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + content = data.clip(offset, cl); + } + else if (action == IIPPacketAction.DeleteResource) + { + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + resourceId = data.getUint32(offset); + offset += 4; + + } + else if (action == IIPPacketAction.AddChild + || action == IIPPacketAction.RemoveChild) + { + if (_notEnough(offset, ends, 8)) + return -_dataLengthNeeded; + + resourceId = data.getUint32(offset); + offset += 4; + childId = data.getUint32(offset); + offset += 4; + } + else if (action == IIPPacketAction.RenameResource) + { + if (_notEnough(offset, ends, 6)) + return -_dataLengthNeeded; + + resourceId = data.getUint32(offset); + offset += 4; + var cl = data.getUint16(offset); + offset += 2; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + content = data.clip(offset, cl); + offset += cl; + } + else if (action == IIPPacketAction.TemplateFromClassName) + { + if (_notEnough(offset, ends, 1)) + return -_dataLengthNeeded; + + var cl = data[offset++]; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + className = data.getString(offset, cl); + offset += cl; + + } + else if (action == IIPPacketAction.TemplateFromClassId) + { + if (_notEnough(offset, ends, 16)) + return -_dataLengthNeeded; + + classId = data.getGuid(offset); + offset += 16; + + } + else if (action == IIPPacketAction.TemplateFromResourceId) + { + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + resourceId = data.getUint32(offset); + offset += 4; + } + else if (action == IIPPacketAction.QueryLink) + { + if (_notEnough(offset, ends, 2)) + return -_dataLengthNeeded; + + var cl = data.getUint16(offset); + offset += 2; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + resourceLink = data.getString(offset, cl); + offset += cl; + } + else if (action == IIPPacketAction.ResourceChildren + || action == IIPPacketAction.ResourceParents) + { + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + resourceId = data.getUint32(offset); + offset += 4; + } + else if (action == IIPPacketAction.ResourceHistory) + { + if (_notEnough(offset, ends, 20)) + return -_dataLengthNeeded; + + resourceId = data.getUint32(offset); + offset += 4; + + fromDate = data.getDateTime(offset); + offset += 8; + + toDate = data.getDateTime(offset); + offset += 8; + + } + else if (action == IIPPacketAction.InvokeFunctionArrayArguments + || action == IIPPacketAction.InvokeFunctionNamedArguments) + { + if (_notEnough(offset, ends, 9)) + return -_dataLengthNeeded; + + resourceId = data.getUint32(offset); + offset += 4; + + methodIndex = data[offset++]; + + var cl = data.getUint32(offset); + offset += 4; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + content = data.clip(offset, cl); + offset += cl; + + } + else if (action == IIPPacketAction.GetProperty) + { + if (_notEnough(offset, ends, 5)) + return -_dataLengthNeeded; + + resourceId = data.getUint32(offset); + offset += 4; + + methodIndex = data[offset++]; + + } + else if (action == IIPPacketAction.GetPropertyIfModified) + { + if (_notEnough(offset, ends, 9)) + return -_dataLengthNeeded; + + resourceId = data.getUint32(offset); + offset += 4; + + methodIndex = data[offset++]; + + resourceAge = data.getUint64(offset); + offset += 8; + + } + else if (action == IIPPacketAction.SetProperty) + { + if (_notEnough(offset, ends, 6)) + return -_dataLengthNeeded; + + resourceId = data.getUint32(offset); + offset += 4; + + methodIndex = data[offset++]; + + + var dt = data[offset++]; + var size = DataType.size(dt); + + if (size < 0) + { + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + var cl = data.getUint32(offset); + offset += 4; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + content = data.clip(offset-5, cl + 5); + offset += cl; + } + else + { + if (_notEnough(offset, ends, size)) + return -_dataLengthNeeded; + + content = data.clip(offset-1, size + 1); + offset += size; + } + } + // Attributes + else if (action == IIPPacketAction.UpdateAllAttributes + || action == IIPPacketAction.GetAttributes + || action == IIPPacketAction.UpdateAttributes + || action == IIPPacketAction.ClearAttributes) + { + if (_notEnough(offset, ends, 8)) + return -_dataLengthNeeded; + + resourceId = data.getUint32(offset); + offset += 4; + var cl = data.getUint32(offset); + offset += 4; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + content = data.clip(offset, cl); + offset += cl; + } + } + else if (command == IIPPacketCommand.Reply) + { + if (action == IIPPacketAction.AttachResource + || action == IIPPacketAction.ReattachResource) + { + + if (_notEnough(offset, ends, 26)) + return -_dataLengthNeeded; + + classId = data.getGuid(offset); + offset += 16; + + resourceAge = data.getUint64(offset); + offset += 8; + + var cl = data.getUint16(offset); + offset += 2; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + resourceLink = data.getString(offset, cl); + offset += cl; + + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + cl = data.getUint32(offset); + offset += 4; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + content = data.clip(offset, cl); + offset += cl; + } + else if (action == IIPPacketAction.DetachResource) + { + // nothing to do + } + else if (action == IIPPacketAction.CreateResource) + { + if (_notEnough(offset, ends, 20)) + return -_dataLengthNeeded; + + //ClassId = data.GetGuid(offset); + //offset += 16; + + resourceId = data.getUint32(offset); + offset += 4; + + } + else if (action == IIPPacketAction.DetachResource) + { + // nothing to do + } + // Inquire + else if (action == IIPPacketAction.TemplateFromClassName + || action == IIPPacketAction.TemplateFromClassId + || action == IIPPacketAction.TemplateFromResourceId + || action == IIPPacketAction.QueryLink + || action == IIPPacketAction.ResourceChildren + || action == IIPPacketAction.ResourceParents + || action == IIPPacketAction.ResourceHistory + // Attribute + || action == IIPPacketAction.GetAllAttributes + || action == IIPPacketAction.GetAttributes) + { + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + var cl = data.getUint32(offset); + offset += 4; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + content = data.clip(offset, cl); + offset += cl; + } + else if (action == IIPPacketAction.InvokeFunctionArrayArguments + || action == IIPPacketAction.InvokeFunctionNamedArguments + || action == IIPPacketAction.GetProperty + || action == IIPPacketAction.GetPropertyIfModified) + { + if (_notEnough(offset, ends, 1)) + return -_dataLengthNeeded; + + var dt = data[offset++]; + var size = DataType.size(dt); + + if (size < 0) + { + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + var cl = data.getUint32(offset); + offset += 4; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + content = data.clip(offset - 5, cl + 5); + offset += cl; + } + else + { + if (_notEnough(offset, ends, size)) + return -_dataLengthNeeded; + + content = data.clip(offset - 1, size + 1); + offset += size; + } + } + else if (action == IIPPacketAction.SetProperty) + { + // nothing to do + } + } + else if (command == IIPPacketCommand.Report) + { + if (report == IIPPacketReport.ManagementError) + { + if (_notEnough(offset, ends, 2)) + return -_dataLengthNeeded; + + errorCode = data.getUint16(offset); + offset += 2; + } + else if (report == IIPPacketReport.ExecutionError) + { + if (_notEnough(offset, ends, 2)) + return -_dataLengthNeeded; + + errorCode = data.getUint16(offset); + offset += 2; + + if (_notEnough(offset, ends, 2)) + return -_dataLengthNeeded; + + var cl = data.getUint16(offset); + offset += 2; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + errorMessage = data.getString(offset, cl); + offset += cl; + } + else if (report == IIPPacketReport.ProgressReport) + { + if (_notEnough(offset, ends, 8)) + return -_dataLengthNeeded; + + progressValue = data.getInt32(offset); + offset += 4; + progressMax = data.getInt32(offset); + offset += 4; + } + else if (report == IIPPacketReport.ChunkStream) + { + if (_notEnough(offset, ends, 1)) + return -_dataLengthNeeded; + + var dt = data[offset++]; + var size = DataType.size(dt); + + if (size < 0) + { + if (_notEnough(offset, ends, 4)) + return -_dataLengthNeeded; + + var cl = data.getUint32(offset); + offset += 4; + + if (_notEnough(offset, ends, cl)) + return -_dataLengthNeeded; + + content = data.clip(offset - 5, cl + 5); + offset += cl; + } + else + { + if (_notEnough(offset, ends, size)) + return -_dataLengthNeeded; + + content = data.clip(offset - 1, size + 1); + offset += size; + } + } + } + + return offset - _originalOffset; + } + + + + + + + + toString() + { + var rt = command.toString(); + + if (command == IIPPacketCommand.Event) + { + rt += " " + event.toString(); + } + else if (command == IIPPacketCommand.Request) + { + rt += " " + action.toString(); + if (action == IIPPacketAction.AttachResource) + { + rt += " CID: " + callbackId.toString() + " RID: " + resourceId.toString(); + } + } + else if (command == IIPPacketCommand.Reply) + rt += " " + action.toString(); + else if (command == IIPPacketCommand.Report) + rt += " " + report.toString(); + + return rt; + } + +} diff --git a/bin/Net/Packets/IIPPacketAction.dart b/bin/Net/Packets/IIPPacketAction.dart new file mode 100644 index 0000000..14c9524 --- /dev/null +++ b/bin/Net/Packets/IIPPacketAction.dart @@ -0,0 +1,36 @@ +class IIPPacketAction +{ + // Request Manage + static const int AttachResource = 0x0; + static const int ReattachResource = 0x1; + static const int DetachResource = 0x2; + static const int CreateResource = 0x3; + static const int DeleteResource = 0x4; + static const int AddChild = 0x5; + static const int RemoveChild = 0x6; + static const int RenameResource = 0x7; + + // Request Inquire + static const int TemplateFromClassName = 0x8; + static const int TemplateFromClassId = 0x9; + static const int TemplateFromResourceId = 0xA; + static const int QueryLink = 0xB; + static const int ResourceHistory = 0xC; + static const int ResourceChildren = 0xD; + static const int ResourceParents = 0xE; + + // Request Invoke + static const int InvokeFunctionArrayArguments = 0x10; + static const int GetProperty = 0x11; + static const int GetPropertyIfModified = 0x12; + static const int SetProperty = 0x13; + static const int InvokeFunctionNamedArguments = 0x14; + + // Request Attribute + static const int GetAllAttributes = 0x18; + static const int UpdateAllAttributes = 0x19; + static const int ClearAllAttributes = 0x1A; + static const int GetAttributes = 0x1B; + static const int UpdateAttributes = 0x1C; + static const int ClearAttributes = 0x1D; +} \ No newline at end of file diff --git a/bin/Net/Packets/IIPPacketCommand.dart b/bin/Net/Packets/IIPPacketCommand.dart new file mode 100644 index 0000000..9dac072 --- /dev/null +++ b/bin/Net/Packets/IIPPacketCommand.dart @@ -0,0 +1,7 @@ +class IIPPacketCommand +{ + static const int Event = 0; + static const int Request = 1; + static const int Reply = 2; + static const int Report = 3; +} \ No newline at end of file diff --git a/bin/Net/Packets/IIPPacketEvent.dart b/bin/Net/Packets/IIPPacketEvent.dart new file mode 100644 index 0000000..c3fbebb --- /dev/null +++ b/bin/Net/Packets/IIPPacketEvent.dart @@ -0,0 +1,15 @@ +class IIPPacketEvent +{ + // Event Manage + static const int ResourceReassigned = 0; + static const int ResourceDestroyed = 1; + static const int ChildAdded = 2; + static const int ChildRemoved = 3; + static const int Renamed = 4; + // Event Invoke + static const int PropertyUpdated = 0x10; + static const int EventOccurred = 0x11; + + // Attribute + static const int AttributesUpdated = 0x18; +} diff --git a/bin/Net/Packets/IIPPacketReport.dart b/bin/Net/Packets/IIPPacketReport.dart new file mode 100644 index 0000000..ddd9b37 --- /dev/null +++ b/bin/Net/Packets/IIPPacketReport.dart @@ -0,0 +1,7 @@ +class IIPPacketReport +{ + static const int ManagementError = 0; + static const int ExecutionError = 1; + static const int ProgressReport = 0x8; + static const int ChunkStream = 0x9; +} \ No newline at end of file diff --git a/bin/Net/SendList.dart b/bin/Net/SendList.dart new file mode 100644 index 0000000..8ea6b98 --- /dev/null +++ b/bin/Net/SendList.dart @@ -0,0 +1,22 @@ +import '../Data/BinaryList.dart'; +import '../Core/AsyncReply.dart'; +import 'NetworkConnection.dart'; + +class SendList extends BinaryList +{ + NetworkConnection connection; + AsyncReply> reply; + + SendList(NetworkConnection connection, AsyncReply> reply) + { + this.reply = reply; + this.connection = connection; + } + + @override + AsyncReply> done() + { + connection.send(super.toDC()); + return reply; + } +} diff --git a/bin/Net/Sockets/IPEndPoint.dart b/bin/Net/Sockets/IPEndPoint.dart new file mode 100644 index 0000000..7bd7d85 --- /dev/null +++ b/bin/Net/Sockets/IPEndPoint.dart @@ -0,0 +1,25 @@ +class IPEndPoint +{ + List ip; + int port; + + + String getIP() + { + return ip.join("."); + //return "${(ip >> 24) & 0xFF}.${(ip >> 16) & 0xFF}.${(ip >> 8) & 0xFF}.${ip & 0xFF}"; + } + + String get address => getIP(); + + @override + String toString() { + return "${getIP()}:${port}"; + } + + + IPEndPoint(this.ip, this.port) + { + + } +} \ No newline at end of file diff --git a/bin/Net/Sockets/ISocket.dart b/bin/Net/Sockets/ISocket.dart new file mode 100644 index 0000000..c08d033 --- /dev/null +++ b/bin/Net/Sockets/ISocket.dart @@ -0,0 +1,47 @@ +/* + +Copyright (c) 2017 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 '../../Core/IDestructible.dart'; +import '../../Data/DC.dart'; +import 'IPEndPoint.dart'; +import '../../Core/AsyncReply.dart'; +import 'SocketState.dart'; + +abstract class ISocket extends IDestructible +{ + SocketState get state ; //{ get; } + + //event ISocketReceiveEvent OnReceive; + //event ISocketConnectEvent OnConnect; + //event ISocketCloseEvent OnClose; + + //void send(DC message); + void send(DC message, [int offset, int size]); + void close(); + AsyncReply connect(String hostname, int port); + bool begin(); + + AsyncReply accept(); + IPEndPoint remoteEndPoint; + IPEndPoint localEndPoint; +} diff --git a/bin/Net/Sockets/SocketState.dart b/bin/Net/Sockets/SocketState.dart new file mode 100644 index 0000000..f08a73f --- /dev/null +++ b/bin/Net/Sockets/SocketState.dart @@ -0,0 +1,9 @@ + enum SocketState + { + Initial, + Listening, + Connecting, + Established, + Closed, + Terminated + } \ No newline at end of file diff --git a/bin/Net/Sockets/TCPSocket.dart b/bin/Net/Sockets/TCPSocket.dart new file mode 100644 index 0000000..69477be --- /dev/null +++ b/bin/Net/Sockets/TCPSocket.dart @@ -0,0 +1,212 @@ +/* + +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 'dart:io'; +import 'ISocket.dart'; +import '../../Data/DC.dart'; +import '../NetworkBuffer.dart'; +import 'SocketState.dart'; +import 'IPEndPoint.dart'; +import '../../Core/AsyncReply.dart'; + +class TCPSocket extends ISocket +{ + Socket sock; + NetworkBuffer receiveNetworkBuffer = new NetworkBuffer(); + + //bool asyncSending; + bool began = false; + + SocketState _state = SocketState.Initial; + + //public event ISocketReceiveEvent OnReceive; + //public event ISocketConnectEvent OnConnect; + //public event ISocketCloseEvent OnClose; + //public event DestroyedEvent OnDestroy; + + //SocketAsyncEventArgs socketArgs = new SocketAsyncEventArgs(); + + /* + void connected(Task t) + { + state = SocketState.Established; + OnConnect?.Invoke(); + Begin(); + } + */ + + IPEndPoint _localEP, _remoteEP; + + bool begin() + { + if (began) + return false; + + began = true; + + _localEP = IPEndPoint(sock.address.rawAddress, sock.port); + _remoteEP = IPEndPoint(sock.remoteAddress.rawAddress, sock.remotePort); + return true; + } + + void dataHandler(List data){ + //print(new String.fromCharCodes(data).trim()); + + + try + { + if (_state == SocketState.Closed || _state == SocketState.Terminated) + return; + + var dc = new DC.fromList(data); + receiveNetworkBuffer.write(dc, 0, dc.length); + emitArgs("receive", [receiveNetworkBuffer]); + + } + catch (ex) + { + if (_state != SocketState.Closed)// && !sock.connected) + { + _state = SocketState.Terminated; + close(); + } + + } + + } + + void errorHandler(error, StackTrace trace){ + print(error); + } + + void doneHandler(){ + sock.destroy(); + } + + AsyncReply connect(String hostname, int port) + { + var rt = new AsyncReply(); + + try + { + _state = SocketState.Connecting; + + Socket.connect(hostname, port).then((s){ + sock = s; + s.listen(dataHandler, + onError: errorHandler, + onDone: doneHandler, + cancelOnError: false); + _state = SocketState.Established; + emitArgs("connect", []); + begin(); + rt.trigger(true); + + }).catchError((ex){ + close(); + rt.triggerError(ex); + }); + + + } + catch(ex) + { + rt.triggerError(ex); + } + + return rt; + } + + + + + IPEndPoint get localEndPoint => _localEP; + IPEndPoint get remoteEndPoint => _remoteEP; + + + SocketState get state => _state; + + + TCPSocket.fromSocket(Socket socket) + { + sock = socket; + //if (socket.) + // _state = SocketState.Established; + } + + TCPSocket() + { + // default constructor + } + + void close() + { + if (state != SocketState.Closed && state != SocketState.Terminated) + _state = SocketState.Closed; + + sock?.close(); + + emitArgs("close", []); + } + + + void send(DC message, [int offset, int size]) + { + if (state == SocketState.Established) + sock.add(message.toList()); + } + + + + void destroy() + { + close(); + emitArgs("destroy", [this]); + } + + AsyncReply accept() + { + + + var reply = new AsyncReply(); + return reply; + +/* + ServerSocket.bind(InternetAddress.ANY_IP_V4, 4567).then( + (ServerSocket server) { + server.listen(handleClient); + } + ); + + void handleClient(Socket client){ + print('Connection from ' + '${client.remoteAddress.address}:${client.remotePort}'); + + client.write("Hello from simple server!\n"); + client.close(); +} + +*/ + } +} \ No newline at end of file diff --git a/bin/Resource/IResource.dart b/bin/Resource/IResource.dart new file mode 100644 index 0000000..cca8f9e --- /dev/null +++ b/bin/Resource/IResource.dart @@ -0,0 +1,46 @@ +/* + +Copyright (c) 2017 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 '../Core/IDestructible.dart'; +import 'ResourceTrigger.dart'; +import '../Core/AsyncReply.dart'; +import 'Instance.dart'; + +abstract class IResource extends IDestructible +{ + + AsyncReply trigger(ResourceTrigger trigger); + /* + { + // do nothing + return new AsyncReply.ready(true); + } + + destroy() + { + // Destroyed + } + */ + Instance instance; +} \ No newline at end of file diff --git a/bin/Resource/IStore.dart b/bin/Resource/IStore.dart new file mode 100644 index 0000000..37d0f34 --- /dev/null +++ b/bin/Resource/IStore.dart @@ -0,0 +1,43 @@ +/* + +Copyright (c) 2017 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 './IResource.dart'; +import '../Core/AsyncReply.dart'; +import '../Data/KeyList.dart'; +import './Template/PropertyTemplate.dart'; +import '../Data/PropertyValue.dart'; + +abstract class IStore extends IResource +{ + AsyncReply get(String path); + AsyncReply retrieve(int iid); + bool put(IResource resource); + String link(IResource resource); + bool record(IResource resource, String propertyName, dynamic value, int age, DateTime dateTime); + bool modify(IResource resource, String propertyName, dynamic value, int age, DateTime dateTime); + bool remove(IResource resource); + + + AsyncReply>> getRecord(IResource resource, DateTime fromDate, DateTime toDate); +} diff --git a/bin/Resource/Instance.dart b/bin/Resource/Instance.dart new file mode 100644 index 0000000..285ec19 --- /dev/null +++ b/bin/Resource/Instance.dart @@ -0,0 +1,680 @@ +import 'dart:core'; +import '../Data/DC.dart'; +import '../Data/Structure.dart'; +import '../Data/AutoList.dart'; +import './IStore.dart'; +import './IResource.dart'; +import '../Data/KeyList.dart'; +import './StorageMode.dart'; +import '../Data/ValueObject.dart'; +import '../Core/IEventHandler.dart'; +import '../Security/Permissions/Ruling.dart'; +import '../Security/Permissions/IPermissionsManager.dart'; +import '../Security/Permissions/ActionType.dart'; +import './Template/ResourceTemplate.dart'; +import './Template/PropertyTemplate.dart'; +import './Template/FunctionTemplate.dart'; +import './Template/EventTemplate.dart'; + +import '../Security/Authority/Session.dart'; +import './Template/MemberTemplate.dart'; +import '../Data/PropertyValue.dart'; +import 'Warehouse.dart'; + +class Instance extends IEventHandler +{ + String _name; + + AutoList _children; + IResource _resource; + IStore _store; + AutoList _parents; + //bool inherit; + ResourceTemplate _template; + + AutoList _managers; + + + KeyList _attributes; + + List _ages = new List(); + List _modificationDates = new List(); + int _instanceAge; + DateTime _instanceModificationDate; + + int _id; + + + /// + /// Instance attributes are custom properties associated with the instance, a place to store information by IStore. + /// + KeyList get attributes => _attributes; + + @override + String toString() => _name + " (" + link + ")"; + + + bool removeAttributes([List attributes = null]) + { + if (attributes == null) + this._attributes.clear(); + else + { + for (var attr in attributes) + this.attributes.remove(attr); + } + + return true; + } + + Structure getAttributes([List attributes = null]) + { + var st = new Structure(); + + if (attributes == null) + { + var clone = this.attributes.keys.toList(); + clone.add("managers"); + attributes = clone.toList(); + } + + for(var attr in attributes) + { + if (attr == "name") + st["name"] = _name; + else if (attr == "managers") + { + var mngrs = new List(); + + for (var i = 0; i < _managers.length; i++) + { + var mst = new Structure(); + mst["type"] = _managers[i].runtimeType; + mst["settings"] = _managers[i].settings; + + mngrs.add(mst); + } + + st["managers"] = mngrs; + } + else if (attr == "parents") + { + st["parents"] = _parents.toList(); + } + else if (attr == "children") + { + st["children"] = _children.toList(); + } + else if (attr == "childrenCount") + { + st["childrenCount"] = _children.count; + } + else if (attr == "type") + { + st["type"] = resource.runtimeType; + } + else + st[attr] = _attributes[attr]; + } + + return st; + } + + bool setAttributes(Structure attributes, [bool clearAttributes = false]) + { + try + { + + if (clearAttributes) + _attributes.clear(); + + for (var attrKey in attributes.keys) + if (attrKey == "name") + _name = attributes[attrKey]; + else if (attrKey == "managers") + { + _managers.clear(); + + var mngrs = attributes[attrKey] as List; + // this is not implemented now, Flutter doesn't support mirrors, needs a workaround @ Warehouse.registerManager + /* + for (var mngr in mngrs) + { + var m = mngr as Structure; + var type = Type.GetType(m["type"] as string); + if (Codec.implementsInterface(); + + foreach (var manager in managers) + { + var mngr = new Structure(); + mngr["settings"] = manager.Settings; + mngr["type"] = manager.GetType().FullName; + mngrs.Add(mngr); + } + + st["managers"] = mngrs; + + return st; + }*/ + + /// + /// Get the age of a given property index. + /// + /// Zero-based property index. + /// Age. + int getAge(int index) + { + if (index < _ages.length) + return _ages[index]; + else + return 0; + } + + /// + /// Set the age of a property. + /// + /// Zero-based property index. + /// Age. + void setAge(int index, int value) + { + if (index < _ages.length) + { + _ages[index] = value; + if (value > _instanceAge) + _instanceAge = value; + } + } + + /// + /// Set the modification date of a property. + /// + /// Zero-based property index. + /// Modification date. + void setModificationDate(int index, DateTime value) + { + if (index < _modificationDates.length) + { + _modificationDates[index] = value; + if (_instanceModificationDate == null || value.millisecondsSinceEpoch > _instanceModificationDate.millisecondsSinceEpoch) + _instanceModificationDate = value; + } + } + + /// + /// Get modification date of a specific property. + /// + /// Zero-based property index + /// Modification date. + DateTime getModificationDate(int index) + { + if (index < _modificationDates.length) + return _modificationDates[index]; + else + return new DateTime(0); + } + + + /// + /// Load property value (used by stores) + /// + /// Property name + /// Property age + /// Property value + /// + bool loadProperty(String name, int age, DateTime modificationDate, dynamic value) + { + /* + var pt = _template.getPropertyTemplate(name); + + if (pt == null) + return false; + + if (pt.info.propertyType == typeof(DistributedPropertyContext)) + return false; + + try + { + if (pt.into.canWrite) + pt.info.setValue(resource, DC.CastConvert(value, pt.Info.PropertyType)); + } + catch(ex) + { + // + } + + setAge(pt.index, age); + setModificationDate(pt.index, modificationDate); + + */ + return true; + + } + + /// + /// Age of the instance, incremented by 1 in every modification. + /// + int get age => _instanceAge; + // this must be internal + set age (value) => _instanceAge = value; + + /// + /// Last modification date. + /// + DateTime get modificationDate => _instanceModificationDate; + + /// + /// Instance Id. + /// + int get id => _id; + + /// + /// Import properties from bytes array. + /// + /// + /// + bool deserialize(List properties) + { + for (var i = 0; i < properties.length; i++) + { + var pt = _template.getPropertyTemplateByIndex(i); + if (pt != null) + { + var pv = properties[i]; + loadProperty(pt.name, pv.age, pv.date, pv.value); + } + } + + return true; + } + + /// + /// Export all properties with ResourceProperty attributed as bytes array. + /// + /// + List serialize() + { + List props = new List(); + + for (var pt in _template.properties) + { + // var rt = pt.info.getValue(resource, null); + // props.add(new PropertyValue(rt, _ages[pt.index], _modificationDates[pt.index])); + } + + return props; + } + /* + public bool Deserialize(byte[] data, uint offset, uint length) + { + + var props = Codec.ParseValues(data, offset, length); + Deserialize(props); + return true; + } + */ + /* + public byte[] Serialize(bool includeLength = false, DistributedConnection sender = null) + { + + //var bl = new BinaryList(); + List props = new List(); + + foreach (var pt in template.Properties) + { + + var pi = resource.GetType().GetProperty(pt.Name); + + var rt = pi.GetValue(resource, null); + + // this is a cool hack to let the property know the sender + if (rt is Func) + rt = (rt as Func)(sender); + + props.Add(rt); + + } + + if (includeLength) + { + return Codec.Compose(props.ToArray(), false); + } + else + { + var rt = Codec.Compose(props.ToArray(), false); + return DC.Clip(rt, 4, (uint)(rt.Length - 4)); + } + } + + public byte[] StorageSerialize() + { + + var props = new List(); + + foreach(var pt in template.Properties) + { + if (!pt.Storable) + continue; + + var pi = resource.GetType().GetProperty(pt.Name); + + if (!pi.CanWrite) + continue; + + var rt = pi.GetValue(resource, null); + + props.Add(rt); + + } + + return Codec.Compose(props.ToArray(), false); + } + */ + + /// + /// If True, the instance can be stored to disk. + /// + /// + bool isStorable() + { + return false; + } + + + void emitModification(PropertyTemplate pt, dynamic value) + { + _instanceAge++; + var now = DateTime.now().toUtc(); + + _ages[pt.index] = _instanceAge; + _modificationDates[pt.index] = now; + + if (pt.storage == StorageMode.NonVolatile) + { + _store.modify(_resource, pt.name, value, _ages[pt.index], now); + } + else if (pt.storage == StorageMode.Recordable) + { + _store.record(_resource, pt.name, value, _ages[pt.index], now); + } + + emitArgs("resourceModified", [_resource, pt.name, value]); + } + + /// + /// Notify listeners that a property was modified. + /// + /// + /// + /// + modified(String propertyName) + { + var valueObject = new ValueObject(); + if (getPropertyValue(propertyName, valueObject)) + { + var pt = _template.getPropertyTemplateByName(propertyName); + emitModification(pt, valueObject.value); + } + } + + emitResourceEvent(issuer, List receivers, String name, List args) + { + emitArgs("resourceEventOccurred", [_resource, issuer, receivers, name, args]); + } + + /// + /// Get the value of a given property by name. + /// + /// Property name + /// Output value + /// True, if the resource has the property. + bool getPropertyValue(String name, ValueObject valueObject) + { + var pt = _template.getPropertyTemplateByName(name); + + /* + if (pt != null && pt.info != null) + { + valueObject.value = pt.info.getValue(_resource, null); + return true; + + }*/ + + valueObject.value = null; + return false; + } + + + /* + public bool Inherit + { + get { return inherit; } + }*/ + + /// + /// List of parents. + /// + AutoList get parents => _parents; + + + /// + /// Store responsible for creating and keeping the resource. + /// + IStore get store => _store; + + /// + /// List of children. + /// + AutoList get children => _children; + + /// + /// The unique and permanent link to the resource. + /// + String get link + { + if (_store != null) + return _store.link(_resource); + else + { + var l = new List(); + + var p = _resource; + + while (true) + { + l.insert(0, p.instance.name); + + if (p.instance.parents.count == 0) + break; + + p = p.instance.parents.first(); + } + + return l.join("/"); + } + + } + + /// + /// Instance name. + /// + String get name => _name; + set name(value) => name = value; + + + /// + /// Resource managed by this instance. + /// + IResource get resource => _resource; + + + /// + /// Resource template describes the properties, functions and events of the resource. + /// + ResourceTemplate get template => _template; + + /// + /// Check for permission. + /// + /// Caller sessions. + /// Action type + /// Function, property or event to check for permission. + /// Permission inquirer. + /// Ruling. + Ruling applicable(Session session, ActionType action, MemberTemplate member, [dynamic inquirer = null]) + { + + for(var i = 0; i < _managers.length; i++) + { + var r = _managers[i].applicable(this.resource, session, action, member, inquirer); + if (r != Ruling.DontCare) + return r; + } + + return Ruling.DontCare; + + } + + /// + /// Execution managers. + /// + AutoList get managers => _managers; + + /// + /// Create new instance. + /// + /// Instance Id. + /// Name of the instance. + /// Resource to manage. + /// Store responsible for the resource. + Instance(int id, String name, IResource resource, IStore store, [ResourceTemplate customTemplate = null, int age = 0]) + { + _store = store; + _resource = resource; + _id = id; + _name = name; + _instanceAge = age; + + _attributes = new KeyList(this); + _children = new AutoList(this); + _parents = new AutoList(this); + _managers = new AutoList(this); + + _children.on("add", children_OnAdd); + _children.on("remove", children_OnRemoved); + _parents.on("add", parents_OnAdd); + _parents.on("remove", parents_OnRemoved); + + resource.on("destroy", resource_OnDestroy); + + if (customTemplate != null) + _template = customTemplate; + else + _template = Warehouse.getTemplateByType(resource.runtimeType); + + // set ages + for (int i = 0; i < _template.properties.length; i++) + { + _ages.add(0); + _modificationDates.add(new DateTime(0));//DateTime.MinValue); + } + + /* + // connect events + Type t = resource.runtimeType; + + var events = t.GetTypeInfo().GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); + + foreach (var evt in events) + { + //if (evt.EventHandlerType != typeof(ResourceEventHanlder)) + // continue; + + + if (evt.EventHandlerType == typeof(ResourceEventHanlder)) + { + var ca = (ResourceEvent[])evt.GetCustomAttributes(typeof(ResourceEvent), true); + if (ca.Length == 0) + continue; + + ResourceEventHanlder proxyDelegate = (args) => EmitResourceEvent(null, null, evt.Name, args); + evt.AddEventHandler(resource, proxyDelegate); + + } + else if (evt.EventHandlerType == typeof(CustomResourceEventHanlder)) + { + var ca = (ResourceEvent[])evt.GetCustomAttributes(typeof(ResourceEvent), true); + if (ca.Length == 0) + continue; + + CustomResourceEventHanlder proxyDelegate = (issuer, receivers, args) => EmitResourceEvent(issuer, receivers, evt.Name, args); + evt.AddEventHandler(resource, proxyDelegate); + } + + + } + */ + } + + void children_OnRemoved(Instance parent, IResource value) + { + value.instance.parents.remove(_resource); + } + + void children_OnAdd(Instance parent, IResource value) + { + if (!value.instance.parents.contains(_resource)) + value.instance.parents.add(_resource); + } + + void parents_OnRemoved(Instance parent, IResource value) + { + value.instance.children.remove(_resource); + } + + void parents_OnAdd(Instance parent, IResource value) + { + if (!value.instance.children.contains(_resource)) + value.instance.children.add(_resource); + } + + + void resource_OnDestroy(sender) + { + emitArgs("resourceDestroyed", [sender]); + } +} diff --git a/bin/Resource/ResourceTrigger.dart b/bin/Resource/ResourceTrigger.dart new file mode 100644 index 0000000..d2b1387 --- /dev/null +++ b/bin/Resource/ResourceTrigger.dart @@ -0,0 +1,34 @@ +/* + +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. + +*/ + +enum ResourceTrigger +{ + Open, + Initialize, + Terminate, + Configure, + SystemInitialized, + SystemTerminated, + SystemReload, +} \ No newline at end of file diff --git a/bin/Resource/StorageMode.dart b/bin/Resource/StorageMode.dart new file mode 100644 index 0000000..1e0eb91 --- /dev/null +++ b/bin/Resource/StorageMode.dart @@ -0,0 +1,6 @@ +class StorageMode +{ + static const NonVolatile = 0; + static const Volatile = 1; + static const Recordable = 2; +} \ No newline at end of file diff --git a/bin/Resource/Template/EventTemplate.dart b/bin/Resource/Template/EventTemplate.dart new file mode 100644 index 0000000..c56710b --- /dev/null +++ b/bin/Resource/Template/EventTemplate.dart @@ -0,0 +1,45 @@ + +import 'MemberTemplate.dart'; +import '../../Data/DC.dart'; +import '../../Data/BinaryList.dart'; +import 'ResourceTemplate.dart'; +import 'MemberType.dart'; + +class EventTemplate extends MemberTemplate +{ + + String expansion; + + + DC compose() + { + var name = super.compose(); + + if (expansion != null) + { + var exp = DC.stringToBytes(expansion); + return new BinaryList() + .addUint8(0x50) + .addInt32(exp.length) + .addDC(exp) + .addUint8(name.length) + .addDC(name) + .toDC(); + } + else + { + return new BinaryList() + .addUint8(0x40) + .addUint8(name.length) + .addDC(name) + .toDC(); + } + } + + + EventTemplate(ResourceTemplate template, int index, String name, String expansion) + : super(template, MemberType.Property, index, name) + { + this.expansion = expansion; + } +} diff --git a/bin/Resource/Template/FunctionTemplate.dart b/bin/Resource/Template/FunctionTemplate.dart new file mode 100644 index 0000000..967023b --- /dev/null +++ b/bin/Resource/Template/FunctionTemplate.dart @@ -0,0 +1,42 @@ +import 'MemberTemplate.dart'; +import '../../Data/DC.dart'; +import '../../Data/BinaryList.dart'; +import 'ResourceTemplate.dart'; +import 'MemberType.dart'; + +class FunctionTemplate extends MemberTemplate +{ + + String expansion; + bool isVoid; + + + DC compose() + { + var name = super.compose(); + + if (expansion != null) + { + var exp = DC.stringToBytes(expansion); + return new BinaryList().addUint8((0x10 | (isVoid ? 0x8 : 0x0))) + .addUint8(name.length) + .addDC(name) + .addInt32(exp.length) + .addDC(exp) + .toDC(); + } + else + return new BinaryList().addUint8((isVoid ? 0x8 : 0x0)) + .addUint8(name.length) + .addDC(name) + .toDC(); + } + + + FunctionTemplate(ResourceTemplate template, int index, String name, bool isVoid, String expansion) + :super(template, MemberType.Property, index, name) + { + this.isVoid = isVoid; + this.expansion = expansion; + } +} diff --git a/bin/Resource/Template/MemberTemplate.dart b/bin/Resource/Template/MemberTemplate.dart new file mode 100644 index 0000000..1a21d3a --- /dev/null +++ b/bin/Resource/Template/MemberTemplate.dart @@ -0,0 +1,34 @@ + +import 'MemberType.dart'; +import '../../Data/DC.dart'; +import './ResourceTemplate.dart'; + +class MemberTemplate +{ + + int get index => _index; + String get name => _name; + MemberType get type => _type; + + ResourceTemplate _template; + String _name; + MemberType _type; + int _index; + + ResourceTemplate get template => _template; + + MemberTemplate(ResourceTemplate template, MemberType type, int index, String name) + { + this._template = template; + this._type = type; + this._index = index; + this._name = name; + } + + String get fullname => _template.className + "." + _name; + + DC compose() + { + // return DC.ToBytes(Name); + } +} diff --git a/bin/Resource/Template/MemberType.dart b/bin/Resource/Template/MemberType.dart new file mode 100644 index 0000000..b8f306c --- /dev/null +++ b/bin/Resource/Template/MemberType.dart @@ -0,0 +1,6 @@ +enum MemberType +{ + Function,// = 0, + Property,// = 1, + Event// = 2, +} \ No newline at end of file diff --git a/bin/Resource/Template/PropertyPermission.dart b/bin/Resource/Template/PropertyPermission.dart new file mode 100644 index 0000000..64307d8 --- /dev/null +++ b/bin/Resource/Template/PropertyPermission.dart @@ -0,0 +1,6 @@ +class PropertyPermission +{ + static const int Read = 1; + static const int Write = 2; + static const int ReadWrite = 3; +} \ No newline at end of file diff --git a/bin/Resource/Template/PropertyTemplate.dart b/bin/Resource/Template/PropertyTemplate.dart new file mode 100644 index 0000000..c5b3dcf --- /dev/null +++ b/bin/Resource/Template/PropertyTemplate.dart @@ -0,0 +1,80 @@ + +import 'MemberTemplate.dart'; +import '../../Data/DC.dart'; +import '../../Data/BinaryList.dart'; +import 'ResourceTemplate.dart'; +import 'MemberType.dart'; +import 'PropertyPermission.dart'; +import '../StorageMode.dart'; + +class PropertyTemplate extends MemberTemplate +{ + + + int permission; + + + int storage; + + String readExpansion; + + String writeExpansion; + + DC compose() + { + var name = super.compose(); + var pv = ((permission) << 1) | (storage == StorageMode.Recordable ? 1 : 0); + + if (writeExpansion != null && readExpansion != null) + { + var rexp = DC.stringToBytes(readExpansion); + var wexp = DC.stringToBytes(writeExpansion); + return new BinaryList() + .addUint8(0x38 | pv) + .addUint8(name.length) + .addDC(name) + .addInt32(wexp.length) + .addDC(wexp) + .addInt32(rexp.length) + .addDC(rexp) + .toDC(); + } + else if (writeExpansion != null) + { + var wexp = DC.stringToBytes(writeExpansion); + return new BinaryList() + .addUint8(0x30 | pv) + .addUint8(name.length) + .addDC(name) + .addInt32(wexp.length) + .addDC(wexp) + .toDC(); + } + else if (readExpansion != null) + { + var rexp = DC.stringToBytes(readExpansion); + return new BinaryList() + .addUint8(0x28 | pv) + .addUint8(name.length) + .addDC(name) + .addInt32(rexp.length) + .addDC(rexp) + .toDC(); + } + else + return new BinaryList() + .addUint8(0x20 | pv) + .addUint8(name.length) + .addDC(name) + .toDC(); + } + + PropertyTemplate(ResourceTemplate template, int index, String name, String read, String write, int storage) + :super(template, MemberType.Property, index, name) + { + //this.Recordable = recordable; + this.storage = storage; + this.readExpansion = read; + this.writeExpansion = write; + } +} \ No newline at end of file diff --git a/bin/Resource/Template/ResourceTemplate.dart b/bin/Resource/Template/ResourceTemplate.dart new file mode 100644 index 0000000..e72705c --- /dev/null +++ b/bin/Resource/Template/ResourceTemplate.dart @@ -0,0 +1,330 @@ + +import './MemberTemplate.dart'; +import '../../Data/Guid.dart'; +import '../../Data/DC.dart'; +import './EventTemplate.dart'; +import './PropertyTemplate.dart'; +import './FunctionTemplate.dart'; +import '../StorageMode.dart'; + +class ResourceTemplate +{ + Guid _classId; + String _className; + List _members = new List(); + List _functions = new List(); + List _events = new List(); + List _properties = new List(); + int _version; + //bool isReady; + + DC _content; + + DC get content => _content; + +/* + MemberTemplate getMemberTemplate(MemberInfo member) + { + if (member is MethodInfo) + return getFunctionTemplate(member.Name); + else if (member is EventInfo) + return getEventTemplate(member.Name); + else if (member is PropertyInfo) + return getPropertyTemplate(member.Name); + else + return null; + } + */ + + EventTemplate getEventTemplateByName(String eventName) + { + for (var i in _events) + if (i.name == eventName) + return i; + return null; + } + + EventTemplate getEventTemplateByIndex(int index) + { + for (var i in _events) + if (i.index == index) + return i; + return null; + } + + FunctionTemplate getFunctionTemplateByName(String functionName) + { + for (var i in _functions) + if (i.name == functionName) + return i; + return null; + } + + FunctionTemplate getFunctionTemplateByIndex(int index) + { + for (var i in _functions) + if (i.index == index) + return i; + return null; + } + + PropertyTemplate getPropertyTemplateByIndex(int index) + { + for (var i in _properties) + if (i.index == index) + return i; + return null; + } + + PropertyTemplate getPropertyTemplateByName(String propertyName) + { + for (var i in _properties) + if (i.name == propertyName) + return i; + return null; + } + + Guid get classId => _classId; + + String get className => _className; + + List get methods => _members; + + List get functions => _functions; + + List get events => _events; + + List get properties => _properties; + + ResourceTemplate() + { + + } + + + ResourceTemplate.fromType(Type type) + { + + } + +/* + ResourceTemplate(Type type) + { + + type = ResourceProxy.GetBaseType(type); + + // set guid + + var typeName = Encoding.UTF8.GetBytes(type.FullName); + var hash = SHA256.Create().ComputeHash(typeName).Clip(0, 16); + + classId = new Guid(hash); + className = type.FullName; + + +#if NETSTANDARD1_5 + PropertyInfo[] propsInfo = type.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); + EventInfo[] eventsInfo = type.GetTypeInfo().GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); + MethodInfo[] methodsInfo = type.GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); + +#else + PropertyInfo[] propsInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); + EventInfo[] eventsInfo = type.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); + MethodInfo[] methodsInfo = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); +#endif + + //byte currentIndex = 0; + + byte i = 0; + + foreach (var pi in propsInfo) + { + var ps = (ResourceProperty[])pi.GetCustomAttributes(typeof(ResourceProperty), true); + if (ps.Length > 0) + { + var pt = new PropertyTemplate(this, i++, pi.Name, ps[0].ReadExpansion, ps[0].WriteExpansion, ps[0].Storage); + pt.Info = pi; + properties.Add(pt); + } + } + + i = 0; + + foreach (var ei in eventsInfo) + { + var es = (ResourceEvent[])ei.GetCustomAttributes(typeof(ResourceEvent), true); + if (es.Length > 0) + { + var et = new EventTemplate(this, i++, ei.Name, es[0].Expansion); + events.Add(et); + } + } + + i = 0; + foreach (MethodInfo mi in methodsInfo) + { + var fs = (ResourceFunction[])mi.GetCustomAttributes(typeof(ResourceFunction), true); + if (fs.Length > 0) + { + var ft = new FunctionTemplate(this, i++, mi.Name, mi.ReturnType == typeof(void), fs[0].Expansion); + functions.Add(ft); + } + } + + // append signals + for (i = 0; i < events.Count; i++) + members.Add(events[i]); + // append slots + for (i = 0; i < functions.Count; i++) + members.Add(functions[i]); + // append properties + for (i = 0; i < properties.Count; i++) + members.Add(properties[i]); + + // bake it binarily + var b = new BinaryList(); + b.AddGuid(classId) + .AddUInt8((byte)className.Length) + .AddString(className) + .AddInt32(version) + .AddUInt16((ushort)members.Count); + + + foreach (var ft in functions) + b.AddUInt8Array(ft.Compose()); + foreach (var pt in properties) + b.AddUInt8Array(pt.Compose()); + foreach (var et in events) + b.AddUInt8Array(et.Compose()); + + content = b.ToArray(); + } + +*/ + + + ResourceTemplate.parse(DC data, [int offset = 0, int contentLength]) + { + + // cool Dart feature + contentLength ??= data.length; + + + int ends = offset + contentLength; + + int oOffset = offset; + + // start parsing... + + //var od = new ResourceTemplate(); + _content = data.clip(offset, contentLength); + + _classId = data.getGuid(offset); + offset += 16; + _className = data.getString(offset + 1, data[offset]); + offset += data[offset] + 1; + + _version = data.getInt32(offset); + offset += 4; + + var methodsCount = data.getUint16(offset); + offset += 2; + + var functionIndex = 0; + var propertyIndex = 0; + var eventIndex = 0; + + for (int i = 0; i < methodsCount; i++) + { + var type = data[offset] >> 5; + + if (type == 0) // function + { + String expansion = null; + var hasExpansion = ((data[offset] & 0x10) == 0x10); + var isVoid = ((data[offset++] & 0x08) == 0x08); + var name = data.getString(offset + 1, data[offset]); + offset += data[offset] + 1; + + if (hasExpansion) // expansion ? + { + var cs = data.getUint32(offset); + offset += 4; + expansion = data.getString(offset, cs); + offset += cs; + } + + var ft = new FunctionTemplate(this, functionIndex++, name, isVoid, expansion); + + _functions.add(ft); + } + else if (type == 1) // property + { + + String readExpansion = null, writeExpansion = null; + + var hasReadExpansion = ((data[offset] & 0x8) == 0x8); + var hasWriteExpansion = ((data[offset] & 0x10) == 0x10); + var recordable = ((data[offset] & 1) == 1); + var permission = (data[offset++] >> 1) & 0x3; + var name = data.getString(offset + 1, data[offset]); + + offset += data[offset] + 1; + + if (hasReadExpansion) // expansion ? + { + var cs = data.getUint32(offset); + offset += 4; + readExpansion = data.getString(offset, cs); + offset += cs; + } + + if (hasWriteExpansion) // expansion ? + { + var cs = data.getUint32(offset); + offset += 4; + writeExpansion = data.getString(offset, cs); + offset += cs; + } + + var pt = new PropertyTemplate(this, propertyIndex++, name, readExpansion, writeExpansion, recordable ? StorageMode.Recordable : StorageMode.Volatile); + + _properties.add(pt); + } + else if (type == 2) // Event + { + + String expansion = null; + var hasExpansion = ((data[offset++] & 0x10) == 0x10); + + var name = data.getString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, (int)data[offset]); + offset += data[offset] + 1; + + if (hasExpansion) // expansion ? + { + var cs = data.getUint32(offset); + offset += 4; + expansion = data.getString(offset, cs); + offset += cs; + } + + var et = new EventTemplate(this, eventIndex++, name, expansion); + + _events.add(et); + + } + } + + // append signals + for (int i = 0; i < _events.length; i++) + _members.add(_events[i]); + // append slots + for (int i = 0; i < _functions.length; i++) + _members.add(_functions[i]); + // append properties + for (int i = 0; i < _properties.length; i++) + _members.add(_properties[i]); + + } +} + diff --git a/bin/Resource/Warehouse.dart b/bin/Resource/Warehouse.dart new file mode 100644 index 0000000..c9544ee --- /dev/null +++ b/bin/Resource/Warehouse.dart @@ -0,0 +1,458 @@ +/* + +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 '../Data/AutoList.dart'; +import './Template/ResourceTemplate.dart'; +import '../Data/Guid.dart'; +import '../Data/KeyList.dart'; +import '../Data/Structure.dart'; +import '../Security/Permissions/IPermissionsManager.dart'; +import 'IResource.dart'; +import 'Instance.dart'; +import 'IStore.dart'; +import '../Core/AsyncReply.dart'; +import '../Core/AsyncBag.dart'; +import 'ResourceTrigger.dart'; + +import '../Net/IIP/DistributedConnection.dart'; + +// Centeral Resource Issuer +class Warehouse +{ + static AutoList _stores = new AutoList(null); + static Map _resources = new Map(); + static int resourceCounter = 0; + + static KeyList _templates = new KeyList(); + + static bool storeIsOpen = false; + + //public delegate void StoreConnectedEvent(IStore store, string name); + //public delegate void StoreDisconnectedEvent(IStore store); + + //public static event StoreConnectedEvent StoreConnected; + ///public static event StoreDisconnectedEvent StoreDisconnected; + + static KeyList protocols = getProtocols(); + + /// + /// Get a store by its name. + /// + /// Store instance name + /// + static IStore getStore(String name) + { + for(var s in _stores) + if (s.instance.name == name) + return s; + return null; + } + + /// + /// Get a resource by instance Id. + /// + /// Instance Id + /// + static AsyncReply getById(int id) + { + if (_resources.containsKey(id)) + return new AsyncReply.ready(_resources[id]); + else + return new AsyncReply.ready(null); + } + + /// + /// Open the warehouse. + /// This function issues the initialize trigger to all stores and resources. + /// + /// True, if no problem occurred. + static AsyncReply open() + { + var bag = new AsyncBag(); + + + for(var s in _stores) + bag.add(s.trigger(ResourceTrigger.Initialize)); + + + + bag.seal(); + + var rt = new AsyncReply(); + bag.then((x) + { + for (var b in x) + if (!b) + { + rt.trigger(false); + return; + } + + var rBag = new AsyncBag(); + for (var rk in _resources.keys) + rBag.add(_resources[rk].trigger(ResourceTrigger.SystemInitialized)); + + rBag.seal(); + + rBag.then((y) + { + for (var b in y) + if (!b) + { + rt.trigger(false); + return; + } + + rt.trigger(true); + storeIsOpen = true; + }); + + }); + + return rt; + } + + /// + /// Close the warehouse. + /// This function issues terminate trigger to all resources and stores. + /// + /// True, if no problem occurred. + static AsyncReply close() + { + + var bag = new AsyncBag(); + + for (var resource in _resources.values) + if (!(resource is IStore)) + bag.add(resource.trigger(ResourceTrigger.Terminate)); + + for (var s in _stores) + bag.add(s.trigger(ResourceTrigger.Terminate)); + + for (var resource in _resources.values) + if (!(resource is IStore)) + bag.add(resource.trigger(ResourceTrigger.SystemTerminated)); + + for (var store in _stores) + bag.add(store.trigger(ResourceTrigger.SystemTerminated)); + + bag.seal(); + + var rt = new AsyncReply(); + bag.then((x) + { + for (var b in x) + if (!b) + { + rt.trigger(false); + return; + } + + rt.trigger(true); + }); + + return rt; + } + + + static List qureyIn(List path, int index, AutoList resources) + { + var rt = new List(); + + if (index == path.length - 1) + { + if (path[index] == "") + for (var child in resources) + rt.add(child); + else + for (var child in resources) + if (child.instance.name == path[index]) + rt.add(child); + } + else + for (var child in resources) + if (child.instance.name == path[index]) + rt.addAll(qureyIn(path, index+1, child.instance.children)); + + return rt; + } + + static AsyncReply> query(String path) + { + + + if (path == null || path == "") + { + var roots = _stores.where((s) => s.instance.parents.length == 0).toList(); + return new AsyncReply>.ready(roots); + } + else + { + var rt = new AsyncReply>(); + get(path).then((x) + { + var p = path.split('/'); + + if (x == null) + { + rt.trigger(qureyIn(p, 0, _stores)); + } + else + { + var ar = qureyIn(p, 0, _stores).where((r) => r != x).toList(); + ar.insert(0, x); + rt.trigger(ar); + } + }); + + return rt; + + } + + } + + /// + /// Get a resource by its path. + /// Resource path is sperated by '/' character, e.g. "system/http". + /// + /// + /// Resource instance. + static AsyncReply get(String path, [attributes = null, IResource parent = null, IPermissionsManager manager = null]) + { + + var p = path.split('/'); + IResource res; + + for(IStore d in _stores) + if (p[0] == d.instance.name) + { + var i = 1; + res = d; + while(p.length > i) + { + var si = i; + + for (IResource r in res.instance.children) + if (r.instance.name == p[i]) + { + i++; + res = r; + break; + } + + if (si == i) + // not found, ask the store + return d.get(path.substring(p[0].length + 1)); + } + + return new AsyncReply.ready(res); + } + + // Should we create a new store ? + if (path.contains("://")) + { + var url = path.split("://"); + var hostname = url[1].split('/')[0]; + var pathname = url[1].split('/').skip(1).join("/"); + + var rt = new AsyncReply(); + + if (protocols.containsKey(url[0])) + { + var handler = protocols[url[0]]; + + var store = handler(); + put(store, url[0] + "://" + hostname, null, parent, null, 0, manager, attributes); + + + store.trigger(ResourceTrigger.Open).then((x){ + if (pathname.length > 0 && pathname != "") + store.get(pathname).then((r) => rt.trigger(r) + ).error((e) => + + rt.triggerError(e) + + ); + else + rt.trigger(store); + }).error((e) { + rt.triggerError(e); + Warehouse.remove(store); + }); + } + + return rt; + } + + + return new AsyncReply.ready(null); + } + + /// + /// Put a resource in the warehouse. + /// + /// Resource instance. + /// Resource name. + /// IStore that manages the resource. Can be null if the resource is a store. + /// Parent resource. if not presented the store becomes the parent for the resource. + static void put(IResource resource, String name, [IStore store = null, IResource parent = null, ResourceTemplate customTemplate = null, int age = 0, IPermissionsManager manager = null, attributes = null]) + { + resource.instance = new Instance(resourceCounter++, name, resource, store, customTemplate, age); + + if (attributes != null) + resource.instance.setAttributes(Structure.fromMap(attributes)); + + if (manager != null) + resource.instance.managers.add(manager); + + if (store == parent) + parent = null; + + if (parent == null) + { + if (!(resource is IStore)) + store.instance.children.add(resource); + } + else + parent.instance.children.add(resource); + + + if (resource is IStore) + { + _stores.add(resource); + //StoreConnected?.Invoke(resource as IStore, name); + } + else + store.put(resource); + + _resources[resource.instance.id] = resource; + + //if (!storeIsOpen) + // resource.trigger(ResourceTrigger.Initialize); + + } + + static T New(String name, [IStore store = null, IResource parent = null, IPermissionsManager manager = null, Structure attributes = null]) + { + /* + var type = ResourceProxy.GetProxy(); + var res = Activator.CreateInstance(type) as IResource; + put(res, name, store, parent, null, 0, manager, attributes); + return (T)res; + */ + } + + /// + /// Put a resource template in the templates warehouse. + /// + /// Resource template. + static void putTemplate(ResourceTemplate template) + { + if (!_templates.containsKey(template.classId)) + _templates.add(template.classId, template); + } + + + /// + /// Get a template by type from the templates warehouse. If not in the warehouse, a new ResourceTemplate is created and added to the warehouse. + /// + /// .Net type. + /// Resource template. + static ResourceTemplate getTemplateByType(Type type) + { + // loaded ? + for (var t in _templates.values) + if (t.className == type.toString()) + return t; + + var template = new ResourceTemplate.fromType(type); + _templates.add(template.classId, template); + + return template; + } + + /// + /// Get a template by class Id from the templates warehouse. If not in the warehouse, a new ResourceTemplate is created and added to the warehouse. + /// + /// Class Id. + /// Resource template. + static AsyncReply getTemplateByClassId(Guid classId) + { + if (_templates.containsKey(classId)) + return new AsyncReply.ready(_templates[classId]); + return null; + } + + /// + /// Get a template by class name from the templates warehouse. If not in the warehouse, a new ResourceTemplate is created and added to the warehouse. + /// + /// Class name. + /// Resource template. + static AsyncReply getTemplateByClassName(String className) + { + for (var t in _templates.values) + if (t.className == className) + return new AsyncReply.ready(t); + + return null; + } + + static bool remove(IResource resource) + { + + if (resource.instance == null) + return false; + + if (_resources.containsKey(resource.instance.id)) + _resources.remove(resource.instance.id); + else + return false; + + if (resource is IStore) + { + _stores.remove(resource); + + // remove all objects associated with the store + var toBeRemoved = _resources.values.where((x) => x.instance.store == resource); + for (var o in toBeRemoved) + remove(o); + + // StoreDisconnected?.Invoke(resource as IStore); + } + + if (resource.instance.store != null) + resource.instance.store.remove(resource); + + resource.destroy(); + + return true; + } +} + +KeyList getProtocols() +{ + var rt = new KeyList(); + rt.add("iip", () => new DistributedConnection()); + return rt; +} \ No newline at end of file diff --git a/bin/Security/Authority/Authentication.dart b/bin/Security/Authority/Authentication.dart new file mode 100644 index 0000000..5e66256 --- /dev/null +++ b/bin/Security/Authority/Authentication.dart @@ -0,0 +1,48 @@ +/* + +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 'AuthenticationType.dart'; +import 'Source.dart'; + +class Authentication +{ + + String username; + //Certificate certificate; + String domain; + + String get fullName => username + "@" + domain; + int state; + + Source source = new Source(); + + + final AuthenticationType type; + + Authentication(this.type) + { + + } +} + diff --git a/bin/Security/Authority/AuthenticationState.dart b/bin/Security/Authority/AuthenticationState.dart new file mode 100644 index 0000000..733501d --- /dev/null +++ b/bin/Security/Authority/AuthenticationState.dart @@ -0,0 +1,34 @@ +/* + +Copyright (c) 2017 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. + +*/ + + +class AuthenticationState +{ + static const int Denied = 0x1; + static const int Succeeded = 0x2; + static const int Blocked = 0x4; + static const int Rejected = 0x8; + static const int NeedsUpdate = 0x10; + static const int NotFound = 0x20; +} \ No newline at end of file diff --git a/bin/Security/Authority/AuthenticationType.dart b/bin/Security/Authority/AuthenticationType.dart new file mode 100644 index 0000000..b3f33a2 --- /dev/null +++ b/bin/Security/Authority/AuthenticationType.dart @@ -0,0 +1,31 @@ +/* + +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. + +*/ + +enum AuthenticationType +{ + Host, + CoHost, + Client, + Alien +} diff --git a/bin/Security/Authority/ClientAuthentication.dart b/bin/Security/Authority/ClientAuthentication.dart new file mode 100644 index 0000000..6525ad0 --- /dev/null +++ b/bin/Security/Authority/ClientAuthentication.dart @@ -0,0 +1,34 @@ +/* + +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 'Authentication.dart'; +import 'AuthenticationType.dart'; + +class ClientAuthentication extends Authentication +{ + ClientAuthentication() + :super(AuthenticationType.Client) + { + + } +} diff --git a/bin/Security/Authority/CoHostAuthentication.dart b/bin/Security/Authority/CoHostAuthentication.dart new file mode 100644 index 0000000..fa19545 --- /dev/null +++ b/bin/Security/Authority/CoHostAuthentication.dart @@ -0,0 +1,34 @@ +/* + +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 'Authentication.dart'; +import 'AuthenticationType.dart'; + +class CoHostAuthentication extends Authentication +{ + CoHostAuthentication() + :super(AuthenticationType.Client) + { + + } +} diff --git a/bin/Security/Authority/HostAuthentication.dart b/bin/Security/Authority/HostAuthentication.dart new file mode 100644 index 0000000..d0f16b7 --- /dev/null +++ b/bin/Security/Authority/HostAuthentication.dart @@ -0,0 +1,34 @@ +/* + +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 'Authentication.dart'; +import 'AuthenticationType.dart'; + +class HostAuthentication extends Authentication +{ + HostAuthentication() + :super(AuthenticationType.Client) + { + + } +} diff --git a/bin/Security/Authority/Session.dart b/bin/Security/Authority/Session.dart new file mode 100644 index 0000000..52aa0a4 --- /dev/null +++ b/bin/Security/Authority/Session.dart @@ -0,0 +1,54 @@ +/* + +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 '../../Data/DC.dart'; +import 'Authentication.dart'; +import '../../Data/KeyList.dart'; + +class Session +{ + Authentication get localAuthentication => _localAuth; + Authentication get remoteAuthentication => _remoteAuth; + + // public Source Source { get; } + DC id; + + //DateTime get creation => _creation; + + //public DateTime Modification { get; } + final KeyList variables = new KeyList(); + + //KeyList Variables { get; } + //IStore Store { get; } + + //string id; + Authentication _localAuth, _remoteAuth; + + + Session(Authentication localAuthentication, Authentication remoteAuthentication) + { + + this._localAuth = localAuthentication; + this._remoteAuth = remoteAuthentication; + } +} diff --git a/bin/Security/Authority/Source.dart b/bin/Security/Authority/Source.dart new file mode 100644 index 0000000..53e0871 --- /dev/null +++ b/bin/Security/Authority/Source.dart @@ -0,0 +1,46 @@ +/* + +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 '../../Data/KeyList.dart'; +import 'SourceAttributeType.dart'; + +class Source +{ + + //string id; + String id; + + KeyList attributes; + + Source.from(this.id, this.attributes) + { + + } + + Source() + { + attributes = new KeyList(); + } + +} diff --git a/bin/Security/Authority/SourceAttributeType.dart b/bin/Security/Authority/SourceAttributeType.dart new file mode 100644 index 0000000..2f3928f --- /dev/null +++ b/bin/Security/Authority/SourceAttributeType.dart @@ -0,0 +1,42 @@ +/* + +Copyright (c) 2017 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. + +*/ + + +enum SourceAttributeType +{ + Mobility, // Stationary/Mobile + CPU, // Arc, Speed, Cores + IPv4, // IPv4, IPv6 Address + IPv6, // IPv4, IPv6 Address + Port, // TCP or UDP port + Proxy, // Proxy + Route, // Trace Root + Location, // Lon, Lat, Alt, Accuracy + OS, // OS name, version, distro, kernel + Application, // lib version, app version + Network, // Bandwidth, MAC, IP, Route + Display, // Screen WxH + Media, // AudioIn, AudioOut, VideoIn, + Identity, // IMEI, IMSI, Manufacture +} diff --git a/bin/Security/Integrity/SHA256.dart b/bin/Security/Integrity/SHA256.dart new file mode 100644 index 0000000..e596ff1 --- /dev/null +++ b/bin/Security/Integrity/SHA256.dart @@ -0,0 +1,160 @@ +import '../../Data/DC.dart'; +import '../../Data/BinaryList.dart'; +import 'dart:typed_data'; + +class SHA256 +{ + + static int RROT(int n, int d) + { + return ZSHIFT(n, d) | (n << (32 - d)); + } + + // Zero-fill right shift + static int ZSHIFT(int n, int d) + { + return (n & 0xFFFFFFFF) >> d; + } + + static DC compute(DC msg) + { + /* + Note 1: All variables are 32 bit unsigned integers and addition is calculated modulo 2^32 + Note 2: For each round, there is one round constant k[i] and one entry in the message schedule array w[i], 0 ≤ i ≤ 63 + Note 3: The compression function uses 8 working variables, a through h + Note 4: Big-endian convention is used when expressing the constants in this pseudocode, + and when parsing message block data from bytes to words, for example, + the first word of the input message "abc" after padding is 0x61626380 + */ + + // Initialize hash values: + // (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19): + + var hash = new Uint32List.fromList([0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, + 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ]); + + // Initialize array of round constants: + // (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311): + var k = new Uint32List.fromList([ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 ]); + + + + // Pre-processing: + // begin with the original message of length L bits + int L = msg.length * 8; + + // append a single '1' bit + // append K '0' bits, where K is the minimum number >= 0 such that L + 1 + K + 64 is a multiple of 512 + + var K = 512 - ((L + 1 + 64) % 512); + + if (K == 512) + K = 0; + + var paddingLength = (K + 1) ~/ 8; + var paddingBytes = new DC(paddingLength); + paddingBytes[0] = 0x80; + + var data = new BinaryList().addDC(msg).addDC(paddingBytes).addUint64(L).toDC(); + + + + // append L as a 64-bit big-endian integer, making the total post-processed length a multiple of 512 bits + + // Process the message in successive 512-bit chunks: + // break message into 512-bit chunks + // for each chunk + + for (var chunk = 0; chunk < data.length; chunk += 64) + { + // create a 64-entry message schedule array w[0..63] of 32-bit words + // (The initial values in w[0..63] don't matter, so many implementations zero them here) + // copy chunk into first 16 words w[0..15] of the message schedule array + + var w = new Uint64List(64);// uint[64]; + for (var i = 0; i < 16; i++) + w[i] = data.getUint32(chunk + (i * 4)); + + //for(var i = 16; i < 64; i++) + // w[i] = 0; + + // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array: + // for i from 16 to 63 + // s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3) + // s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10) + // w[i] := w[i-16] + s0 + w[i-7] + s1 + + for (var i = 16; i < 64; i++) + { + var s0 = SHA256.RROT(w[i - 15], 7) ^ SHA256.RROT(w[i - 15], 18) ^ ZSHIFT(w[i - 15], 3); + var s1 = SHA256.RROT(w[i - 2], 17) ^ SHA256.RROT(w[i - 2], 19) ^ ZSHIFT(w[i - 2], 10); + w[i] = w[i - 16] + s0 + w[i - 7] + s1; + } + + // Initialize working variables to current hash value: + var a = hash[0]; + var b = hash[1]; + var c = hash[2]; + var d = hash[3]; + var e = hash[4]; + var f = hash[5]; + var g = hash[6]; + var h = hash[7]; + + + // Compression function main loop: + for (var i = 0; i < 64; i++) + { + var S1 = SHA256.RROT(e, 6) ^ SHA256.RROT(e, 11) ^ SHA256.RROT(e, 25); + var ch = (e & f) ^ ((~e) & g); + var temp1 = h + S1 + ch + k[i] + w[i]; + var S0 = SHA256.RROT(a, 2) ^ SHA256.RROT(a, 13) ^ SHA256.RROT(a, 22); + var maj = (a & b) ^ (a & c) ^ (b & c); + int temp2 = S0 + maj; + + h = g; + g = f; + f = e; + e = ZSHIFT(d + temp1, 0); + d = c; + c = b; + b = a; + a = ZSHIFT(temp1 + temp2, 0); + } + + // Add the compressed chunk to the current hash value: + + hash[0] = ZSHIFT(hash[0] + a, 0); + hash[1] = ZSHIFT(hash[1] + b , 0); + hash[2] = ZSHIFT(hash[2] + c , 0); + hash[3] = ZSHIFT(hash[3] + d , 0); + hash[4] = ZSHIFT(hash[4] + e , 0); + hash[5] = ZSHIFT(hash[5] + f , 0); + hash[6] = ZSHIFT(hash[6] + g , 0); + hash[7] = ZSHIFT(hash[7] + h, 0); + + + } + + + + + // Produce the final hash value (big-endian): + //digest := hash := h0 append h1 append h2 append h3 append h4 append h5 append h6 append h7 + + var results = new BinaryList(); + for (var i = 0; i < 8; i++) + results.addUint32(hash[i]); + + + return results.toDC(); + } +} diff --git a/bin/Security/Permissions/ActionType.dart b/bin/Security/Permissions/ActionType.dart new file mode 100644 index 0000000..6b27c40 --- /dev/null +++ b/bin/Security/Permissions/ActionType.dart @@ -0,0 +1,40 @@ +/* + +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. + +*/ +enum ActionType +{ + Attach, + Delete, + Execute, + GetProperty, + SetProperty, + CreateResource, + UpdateAttributes, + InquireAttributes, + AddParent, + RemoveParent, + AddChild, + RemoveChild, + Rename, + ReceiveEvent +} \ No newline at end of file diff --git a/bin/Security/Permissions/IPermissionsManager.dart b/bin/Security/Permissions/IPermissionsManager.dart new file mode 100644 index 0000000..e273d93 --- /dev/null +++ b/bin/Security/Permissions/IPermissionsManager.dart @@ -0,0 +1,48 @@ +/* + +Copyright (c) 2017 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 'Ruling.dart'; +import 'ActionType.dart'; +import '../../Data/Structure.dart'; +import '../../Resource/IResource.dart'; +import '../Authority/Session.dart'; +import '../../Resource/Template/MemberTemplate.dart'; + +abstract class IPermissionsManager +{ + /// + /// Check for permission. + /// + /// IResource. + /// Caller sessions. + /// Action type + /// Function, property or event to check for permission. + /// Permission inquirer object. + /// Allowed or denined. + Ruling applicable(IResource resource, Session session, ActionType action, MemberTemplate member, [dynamic inquirer = null]); + + bool initialize(Structure settings, IResource resource); + + Structure get settings; +} diff --git a/bin/Security/Permissions/Ruling.dart b/bin/Security/Permissions/Ruling.dart new file mode 100644 index 0000000..068f5b5 --- /dev/null +++ b/bin/Security/Permissions/Ruling.dart @@ -0,0 +1,6 @@ +enum Ruling +{ + Denied, + Allowed, + DontCare +} diff --git a/bin/esiur.dart b/bin/esiur.dart new file mode 100644 index 0000000..f4c70d3 --- /dev/null +++ b/bin/esiur.dart @@ -0,0 +1,2 @@ +export 'Resource/Warehouse.dart'; + diff --git a/bin/main.dart b/bin/main.dart new file mode 100644 index 0000000..0f5c45a --- /dev/null +++ b/bin/main.dart @@ -0,0 +1,24 @@ +//import 'Net/IIP/DistributedConnection.dart'; +//import 'Resource/Warehouse.dart'; + +import 'esiur.dart'; + +main() async +{ + var x = await Warehouse.get("iip://localhost:5000/db/my", {"username": "demo", "password": "1234"});//.then((x){ + + // get property + print(x.Level); + // listen to event + x.on("LevelUp", (v,y,z)=>print("Level up ${v} ${y}${z}")); + // use await + print("Added successfully ${await x.Add(40)}"); + // use named arguments + await x.Add(value: 20); + // test chunks + x.Stream(10).chunk((c)=>print(c)); + x.Level += 900; + + print("Done"); + +}