diff --git a/build.bat b/build.bat index c0c88c1..9c9be2c 100644 --- a/build.bat +++ b/build.bat @@ -3,9 +3,27 @@ del "build\esiur-debug.js" del "build\esiur.js" -type "src\IEventHandler.js" > build\esiur-debug.js +type "src\CustomResourceEvent.js" >> build\esiur-debug.js echo. >> build\esiur-debug.js -type "external\sha256.min.js" >> build\esiur-debug.js +type "src\NetworkBuffer.js" >> build\esiur-debug.js +echo. >> build\esiur-debug.js +type "src\AsyncException.js" >> build\esiur-debug.js +echo. >> build\esiur-debug.js +type "src\Authentication.js" >> build\esiur-debug.js +echo. >> build\esiur-debug.js +type "src\Session.js" >> build\esiur-debug.js +echo. >> build\esiur-debug.js +type "src\DistributedPropertyContext.js" >> build\esiur-debug.js +echo. >> build\esiur-debug.js +type "src\IPermissionsManager.js" >> build\esiur-debug.js +echo. >> build\esiur-debug.js +type "src\KeyList.js" >> build\esiur-debug.js +echo. >> build\esiur-debug.js +type "src\PropertyValue.js" >> build\esiur-debug.js +echo. >> build\esiur-debug.js +type "src\IEventHandler.js" >> build\esiur-debug.js +echo. >> build\esiur-debug.js +type "src\SHA256.js" >> build\esiur-debug.js echo. >> build\esiur-debug.js type "src\IDestructible.js" >> build\esiur-debug.js echo. >> build\esiur-debug.js @@ -55,8 +73,7 @@ type "src\IIPPacket.js" >> build\esiur-debug.js echo. >> build\esiur-debug.js type "src\Instance.js" >> build\esiur-debug.js echo. >> build\esiur-debug.js -type "src\NetworkBuffer.js" >> build\esiur-debug.js -echo. >> build\esiur-debug.js + type "src\NotModified.js" >> build\esiur-debug.js echo. >> build\esiur-debug.js type "src\PropertyTemplate.js" >> build\esiur-debug.js @@ -68,5 +85,9 @@ echo. >> build\esiur-debug.js type "src\Warehouse.js" >> build\esiur-debug.js echo. >> build\esiur-debug.js +copy build\esiur-debug.js build\esiur-debug-node.js +type "module.js" >> build\esiur-debug-node.js +echo. >> build\esiur-debug-node.js + REM minify build/esiur-debug.js -o build/esiur.js uglifyjs build/esiur-debug.js -c -o build/esiur.js diff --git a/build/esiur-debug-node.js b/build/esiur-debug-node.js new file mode 100644 index 0000000..eb666af --- /dev/null +++ b/build/esiur-debug-node.js @@ -0,0 +1,7652 @@ +/* +* Copyright (c) 2017-2018 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 10/11/2018. + */ + +"use strict"; + +class CustomResourceEvent +{ + constructor(issuer, receivers, params) + { + this.issuer = issuer; + this.receivers = receivers; + this.params = params; + } +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 01/09/2017. + */ + +"use strict"; + +class NetworkBuffer { + constructor() { + this.neededDataLength = 0; + this.data = new DC(0); + } + + get protected() { + return this.neededDataLength > this.data.length; + } + + get available() { + + return this.data.length; + } + + holdAllForNextWrite(src) { + this.holdFor(src, src.length + 1); + } + + holdForNextWrite(src, offset, size) { + this.holdFor(src, offset, size, size + 1); + } + + + holdFor(src, offset, size, needed) { + if (size >= needed) + throw new Exception("Size >= Needed !"); + + this.data = DC.combine(src, offset, size, this.data, 0, this.data.length); + this.neededDataLength = needed; + } + + holdAllFor(src, needed) { + this.holdFor(src, 0, src.length, needed); + } + + protect(data, offset, needed) { + var dataLength = data.length - offset; + + // protection + if (dataLength < needed) { + this.holdFor(data, offset, dataLength, needed); + return true; + } + else + return false; + } + + writeAll(src) { + this.write(src, 0, src.length ? src.length : src.byteLength); + } + + write(src, offset, length) { + this.data = this.data.append(src, offset, length); + } + + get canRead() { + if (this.data.length == 0) + return false; + else if (this.data.length < this.neededDataLength) + return false; + return true; + } + + read() { + if (this.data.length == 0) + return null; + + var rt = null; + + if (this.neededDataLength == 0) { + rt = this.data; + this.data = new DC(0); + } + else { + if (this.data.length >= this.neededDataLength) { + rt = this.data; + this.data = new DC(0); + this.neededDataLength = 0; + return rt; + } + else { + return null; + } + } + + return rt; + } +} + +/* +* 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. +*/ +/** + * Created by Ahmed Zamil on 18/11/2017. + */ +"use strict"; + +const ExceptionCode = +{ + HostNotReachable: 0, + AccessDenied: 1, + ResourceNotFound: 2, + AttachDenied: 3, + InvalidMethod: 4, + InvokeDenied: 5, + CreateDenied: 6, + AddParentDenied: 7, + AddChildDenied: 8, + ViewAttributeDenied: 9, + UpdateAttributeDenied: 10, + StoreNotFound: 11, + ParentNotFound: 12, + ChildNotFound: 13, + ResourceIsNotStore: 14, + DeleteDenied: 15, + DeleteFailed: 16, + UpdateAttributeFailed: 17, + GetAttributesFailed: 18, + ClearAttributesFailed: 19, + TemplateNotFound: 20, + RenameDenied: 21, + ClassNotFound: 22, + MethodNotFound: 23, + PropertyNotFound: 24, + SetPropertyDenied: 25, + ReadOnlyProperty: 26 +}; + +class AsyncException extends Error + { + constructor() + { + super(); + this.raised = false; + } + + raise(type, code, message) + { + this.type = (type == 0 ? "Management" : "Execusion"); + this.code = code; + + if (type == 0) + for(var i in ExceptionCode) + if (ExceptionCode[i] == code) + { + this.message = i; + break; + } + else + this.message = message; + + this.raised = true; + } + + toString() + { + return this.type + " " + this.code + " " + this.message; + } + } +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 16/11/2017. + */ + +"use strict"; + + const AuthenticationType = { + Host: 0, + CoHost: 1, + Client: 2, + Alien: 3 + }; + + class Authentication +{ + constructor(type) + { + this.type = type; + this.state = 0; + this.domain = null; + this.username = null; + } + + get fullName() + { + return this.domain + "@" + this.username; + } +} + + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 16/11/2017. + */ + +"use strict"; + +class Session +{ + constructor(localAuthentication, remoteAuthentication) + { + + this.localAuthentication = localAuthentication; + this.remoteAuthentication = remoteAuthentication; + this.id = null; + this.creation = null; + this.modification = null; + } +} + +/* +* Copyright (c) 2017-2018 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 27/10/2018. + */ + +"use strict"; + +class DistributedPropertyContext +{ + constructor(p1, p2) + { + if(arguments.length == 1) + { + this.method = p1; + } + else if (arguments.length == 2) + { + this.connection = p1; + this.value = p2; + } + } +} + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 16/11/2017. + */ + +"use strict"; + +const ActionType = +{ + Attach: 0, + Delete: 1, + Execute: 2, + GetProperty: 3, + SetProperty: 4, + CreateResource: 5, + UpdateAttributes: 6, + InquireAttributes: 7, + AddParent: 8, + RemoveParent: 9, + AddChild: 10, + RemoveChild: 11, + Rename: 12, + ReceiveEvent: 13 +}; + +const Ruling = { + Denied: 0, + Allowed: 1, + DontCare: 2, +}; + +class IPermissionsManager +{ + /// + /// Check for permission. + /// + /// IResource. + /// Caller sessions. + /// Action type + /// Function or property to check for permission. + /// Allowed or denined. + applicable(resource, session, action, member, inquirer) + { + + } + + initialize(settings, resource) + { + + } + + get settings() + { + + } +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 06/11/2017. + */ + +"use strict"; + +class KeyList + { + constructor() + { + this.keys = []; + this.values = []; + } + + at(index) + { + return this.values[index]; + } + + item(key) + { + for(var i = 0; i < this.keys.length; i++) + if (this.keys[i] == key) + return this.values[i]; + } + + get(key) + { + for(var i = 0; i < this.keys.length; i++) + if (this.keys[i] == key) + return this.values[i]; + } + + _item_destroyed(sender) + { + for(var i = 0; i < this.values.length; i++) + if (sender == this.values[i]) + { + this.removeAt(i); + break; + } + } + + add(key, value) + { + this.remove(key); + + if (value instanceof IDestructible) + value.on("destroy", this._item_destroyed, this); + + this.keys.push(key); + this.values.push(value); + } + + contains(key) + { + for(var i = 0; i < this.keys.length; i++) + if (this.keys[i] == key) + return true; + + return false; + } + + set(key, value) + { + this.remove(key); + this.add(key, value); + } + + remove(key) + { + for(var i = 0; i < this.keys.length; i++) + if (key == this.keys[i]) + { + this.removeAt(i); + break; + } + } + + removeAt(index) + { + if (this.values[index] instanceof IDestructible) + this.values[index].off("destroy", this._item_destroyed); + + this.keys.splice(index, 1); + this.values.splice(index, 1); + } + + get length() + { + return this.keys.length; + } + } +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 06/11/2017. + */ + +"use strict"; + +class PropertyValue + { + constructor(value, age, date) + { + this.value = value; + this.age = age; + this.date = date; + } + } +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 30/08/2017. + */ +"use strict"; + +class IEventHandler +{ + _register(event) + { + this._events[event] = []; + } + + constructor() + { + this._events = {}; + } + + _emit(event) + { + event = event.toLowerCase(); + var args = Array.prototype.slice.call(arguments, 1); + if (this._events[event]) + for(var i = 0; i < this._events[event].length; i++) + if (this._events[event][i].f.apply(this._events[event][i].i, args)) + return true; + + return false; + } + + _emitArgs(event, args) + { + event = event.toLowerCase(); + if (this._events[event]) + for(var i = 0; i < this._events[event].length; i++) + if (this._events[event][i].f.apply(this._events[event][i].i, args)) + return true; + return this; + } + + on(event, fn, issuer) + { + if (!(fn instanceof Function)) + return this; + + event = event.toLowerCase(); + // add + if (!this._events[event]) + this._events[event] = []; + this._events[event].push({f: fn, i: issuer == null ? this: issuer}); + return this; + } + + off(event, fn) + { + event = event.toLocaleString(); + if (this._events[event]) + { + if (fn) + { + for(var i = 0; i < this._events[event].length; i++) + if (this._events[event][i].f == fn) + this._events[event].splice(i--, 1); + + //var index = this._events[event].indexOf(fn); + //if (index > -1) + //this._events[event].splice(index, 1); + } + else + { + this._events[event] = []; + } + } + } +} + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 25/12/2017. + * Ref: https://en.wikipedia.org/wiki/SHA-2 + */ + +class SHA256 +{ + + static RROT(n, d) + { + return (n >>> d)|(n << (32 - d)); + } + + static compute(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): + + const hash = new Uint32Array([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): + const k = new Uint32Array([ + 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 + var 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 Uint8Array(paddingLength); + paddingBytes[0] = 0x80; + + var data = new DC(BL().addUint8Array(msg).addUint8Array(paddingBytes).addUint64(L).toArray()); + + + + // 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 Uint32Array(64); + for(var i = 0; i < 16; i++) + w[i] = data.getInt32(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) ^ (w[i-15] >>> 3); + var s1 = SHA256.RROT(w[i-2], 17) ^ SHA256.RROT(w[i-2], 19) ^ (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); + var temp2 = S0 + maj; + + h = g; + g = f; + f = e; + e = (d + temp1) >>> 0; + d = c; + c = b; + b = a; + a = (temp1 + temp2) >>> 0; + } + + // Add the compressed chunk to the current hash value: + + hash[0] = (hash[0] + a) >>> 0; + hash[1] = (hash[1] + b) >>> 0; + hash[2] = (hash[2] + c) >>> 0; + hash[3] = (hash[3] + d) >>> 0; + hash[4] = (hash[4] + e) >>> 0; + hash[5] = (hash[5] + f) >>> 0; + hash[6] = (hash[6] + g) >>> 0; + hash[7] = (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(); + } +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 31/08/2017. + */ + +"use strict"; + +class IDestructible extends IEventHandler +{ + destroy() + { + this._emit("destroy", this); + } + + constructor() + { + super(); + } +} + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 05/09/2017. + */ + +"use strict"; + +class AutoList extends IEventHandler +{ + constructor() + { + super(); + this.list = []; + } + + get length() + { + return this.list.length; + } + + add(value) + { + if (value instanceof IDestructible) + value.on("destroy", this._item_destroyed, this); + + this.list.push(value); + + this._emit("add", value); + } + + set(index, value) + { + if (index >= this.list.length || index < 0) + return; + + if (value instanceof IDestructible) + value.on("destroy", this._item_destroyed, this); + + if (this.list[index] instanceof IDestructible) + this.list[index].off("destroy", this._item_destroyed); + + this.list[index] = value; + } + + at(index) + { + return this.list[index]; + } + + item(index) + { + return this.list[index]; + } + + remove(value) + { + this.removeAt(this.list.indexOf(value)); + } + + contains(value) + { + return this.list.indexOf(value) > -1; + } + + toArray() + { + return this.list.slice(0); + } + + removeAt(index) + { + if (index >= this.list.length || index < 0) + return; + + var item = this.list[index]; + + if (item instanceof IDestructible) + item.off("destroy", this._item_destroyed); + + this.list.splice(index, 1); + + this._emit("remove", item); + } + + _item_destroyed(sender) + { + this.remove(sender); + } +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 25/07/2017. + */ + +"use strict"; + +const ResourceTrigger = +{ + Loaded : 0, + Initialize: 1, + Terminate: 2, + Configure: 3, + SystemInitialized: 4, + SystemTerminated: 5, + SystemReload: 6 +}; + +class IResource extends IDestructible +{ + trigger(trigger) + { + + } + + constructor() + { + super(); + } + + static getTemplate() + { + return { + namespace: "Esiur", + properties: [], + functions: [], + events: [] + } + } +} + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 25/07/2017. + */ + +"use strict"; + +class IStore extends IResource { + get(path) { + + } + + retrieve(iid) { + + } + + put(resource) { + + } + + record(resource, propertyName, value, age, dateTime) + { + + } + + getRecord(resource, fromDate, toDate) + { + + } + + remove(resource) + { + + } + + constructor() + { + super(); + } +} + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 26/08/2017. + */ + +"use strict"; + +class Structure +{ + getKeys() { + var rt = []; + for (var i in this) + if (!(this[i] instanceof Function)) + rt.push(i); + + return rt; + } +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 06/09/2017. + */ + +"use strict"; + +class StructureArray extends Array +{ + push(value) + { + if (value instanceof Structure) + super.push(value); + else + return; + } +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 26/08/2017. + */ + +"use strict"; + +class ResourceArray extends Array +{ + push(value) + { + if (value instanceof IResource) + super.push(value); + else + return; + } +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 24/08/2017. + */ + +"use strict"; + +const MemberType = { + Function: 0, + Property: 1, + Event: 2 +}; + +class MemberTemplate { + compose() { + return DC.stringToBytes(this.name); + } +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 25/07/2017. + */ + +"use strict"; + +const ErrorType = { + Management: 0, + Exception: 1 +}; + +const ProgressType = { + Execution: 0, + Network: 1 +}; + +class AsyncReply +{ + then(callback) + { + this.callbacks.push(callback); + + if (this.ready) + { + callback(this.result, this); + + if (!this.taskExpired) + { + this.taskExpired = true; + this.resolveTask(this.result); + } + } + + return this; + } + + catch(callback) + { + return error(callback); + } + + error(callback) + { + this.errorCallbacks.push(callback); + + if (this.exception.raised) + { + callback(this.exception); + + if (!this.taskExpired) + { + this.taskExpired = true; + this.rejectTask(this.exception); + } + } + + return this; + } + + progress(callback) + { + this.progressCallbacks.push(callback); + return this; + } + + chunk(callback) + { + this.chunkCallbacks.push(callback); + return this; + } + + trigger(result) + { + this.result = result; + this.ready = true; + + for(var i = 0; i < this.callbacks.length; i++) + this.callbacks[i](result, this); + + + if (!this.taskExpired) + { + this.taskExpired = true; + this.resolveTask(this.result); + } + } + + + triggerError(type, code, message)//exception) + { + if (this.ready) + return; + + this.exception.raise(type, code, message);// = exception; + + for(var i = 0; i < this.errorCallbacks.length; i++) + this.errorCallbacks[i](this.exception, this); + + + if (!this.taskExpired) + { + this.taskExpired = true; + this.rejectTask(this.exception); + } + } + + triggerProgress(type, value, max) + { + if (this.ready) + return; + + for(var i = 0; i < this.progressCallbacks.length; i++) + this.progressCallbacks[i](type, value, max, this); + } + + triggerChunk(value) + { + if (this.ready) + return; + + for(var i = 0; i < this.chunkCallbacks.length; i++) + this.chunkCallbacks[i](value, this); + } + + constructor(result) + { + this.callbacks = []; + this.errorCallbacks = []; + this.progressCallbacks = []; + this.chunkCallbacks = []; + this.exception = new AsyncException();// null; + + var self = this; + + this.task = new Promise(function(resolve, reject){ + self.resolveTask = resolve; + self.rejectTask = reject; + }); + + + if (result !== undefined) { + this.result = result; + this.ready = true; + this.taskExpired = true; + this.resolveTask(result); + } + else + { + this.taskExpired = false; + this.ready = false; + this.result = null; + } + } +} +/* +* 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. +*/ +/** + * Created by Ahmed Zamil on 25/07/2017. + */ + +"use strict"; + +class AsyncBag extends AsyncReply +{ + constructor() { + super(); + this.replies = []; + this.results = []; + this.count = 0; + this.sealedBag = false; + } + + seal() + { + this.sealedBag = true; + + if (this.results.length == 0) + this.trigger([]); + + var self = this; + + var singleTaskCompleted = function(taskIndex) + { + return function(results, reply){ + self.results[taskIndex] = results; + self.count++; + if (self.count == self.results.length) + self.trigger(self.results); + }; + }; + + for(var i = 0; i < this.results.length; i++) + this.replies[i].then(singleTaskCompleted(i)); + + /* + this.replies[i].then(function(r, reply){ + self.results[self.replies.indexOf(reply)] = r; + self.count++; + if (self.count == self.results.length) + self.trigger(self.results); + }); + */ + } + + add(reply) + { + if (!this.sealedBag) { + this.replies.push(reply); + this.results.push(null); + } + } +} +/* +* 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. +*/ +/** + * Created by Ahmed Zamil on 25/07/2017. + */ + +"use strict"; + +class AsyncQueue extends AsyncReply +{ + + constructor() + { + super(); + this.list = []; + + var self = this; + + this.processQueue = function () + { + for (var i = 0; i < self.list.length; i++) + if (self.list[i].ready) + { + self.trigger(self.list[i].result); + self.list.splice(i, 1); + i--; + } + else + break; + + self.ready = (self.list.length == 0); + } + } + + add(reply) + { + this.list.push(reply); + this.ready = false; + reply.then(this.processQueue); + } + + remove(reply) + { + this.list.splice(this.list.indexOf(reply), 1); + this.processQueue(); + } + + + +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 25/08/2017. + */ + +"use strict"; + +function BL(){ + return new BinaryList(); +}; + +class BinaryList +{ + + constructor() + { + this.list = []; + } + + + addRange(bl) + { + for(var i = 0; i < bl.list.length; i++) + this.list.push(bl.list[i]); + + return this; + } + + add(typedValue, position) + { + if (position !== undefined) + this.list.splice(position, 0, typedValue); + else + this.list.push(typedValue); + return this; + } + + get length() + { + return this.toArray().length; + } + + toArray() + { + var ars = []; + // calculate length + for(var i = 0; i < this.list.length; i++) + { + switch (this.list[i].type) + { + case DataType.Bool: + ars.push(DC.boolToBytes(this.list[i].value)); + break; + case DataType.UInt8: + ars.push(DC.uint8ToBytes(this.list[i].value)); + break; + case DataType.Int8: + ars.push(DC.int8ToBytes(this.list[i].value)); + break; + case DataType.Char: + ars.push(DC.charToBytes(this.list[i].value)); + break; + case DataType.UInt16: + ars.push(DC.uint16ToBytes(this.list[i].value)); + break; + case DataType.Int16: + ars.push(DC.int16ToBytes(this.list[i].value)); + break; + case DataType.UInt32: + ars.push(DC.uint32ToBytes(this.list[i].value)); + break; + case DataType.Int32: + ars.push(DC.int32ToBytes(this.list[i].value)); + break; + case DataType.UInt64: + ars.push(DC.uint64ToBytes(this.list[i].value)); + break; + case DataType.Int64: + ars.push(DC.int64ToBytes(this.list[i].value)); + break; + case DataType.Float32: + ars.push(DC.float32ToBytes(this.list[i].value)); + break; + case DataType.Float64: + ars.push(DC.float64ToBytes(this.list[i].value)); + break; + case DataType.String: + ars.push(DC.stringToBytes(this.list[i].value)); + break; + case DataType.DateTime: + ars.push(DC.dateTimeToBytes(this.list[i].value)); + break; + case DataType.UInt8Array: + ars.push(this.list[i].value); + + //case DataType.Resource: + // ars.push(DC.uint32ToBytes(this.list[i].value.instance.id)); + // break; + //case DataType.DistributedResource: + // ars.push(DC.int8ToBytes(this.list[i].value)); + // break; + + + + } + } + + var length = 0; + ars.forEach(function(a){ + length += a.length; + }); + + var rt = new Uint8Array(length); + + var offset = 0; + for(var i = 0; i < ars.length; i++) { + rt.set(ars[i], offset); + offset+=ars[i].length; + } + + return rt; + } + + toDC() + { + return new DC(this.toArray()); + } + + addDateTime(value, position) + { + return this.add({type: DataType.DateTime, value: value}, position); + } + + addUint8Array(value, position) + { + return this.add({type: DataType.UInt8Array, value: value}, position); + } + + + addHex(value, position) + { + return this.addUint8Array(DC.hexToBytes(value), position); + } + + addString(value, position) + { + return this.add({type: DataType.String, value: value}, position); + } + + addUint8(value, position) + { + return this.add({type: DataType.UInt8, value: value}, position); + } + + addInt8(value, position) + { + return this.add({type: DataType.Int8, value: value}, position); + } + + addChar(value, position) + { + return this.add({type: DataType.Char, value: value}, position); + } + + addUint16(value, position) + { + return this.add({type: DataType.UInt16, value: value}, position); + } + + addInt16(value, position) + { + return this.add({type: DataType.Int16, value: value}, position); + } + + addUint32(value, position) + { + return this.add({type: DataType.UInt32, value: value}, position); + } + + addInt32(value, position) + { + return this.add({type: DataType.Int32, value: value}, position); + } + + addUint64(value, position) + { + return this.add({type: DataType.UInt64, value: value}, position); + } + + addInt64(value, position) + { + return this.add({type: DataType.Int64, value: value}, position); + } + + addFloat32(value, position) + { + return this.add({type: DataType.Float32, value: value}, position); + } + + addFloat64(value, position) + { + return this.add({type: DataType.Float64, value: value}, position); + } + +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 25/07/2017. + */ + +"use strict"; + +const ResourceComparisonResult = + { + Null: 0, + Distributed: 1, + Local: 2, + Same: 3 + }; + +const StructureComparisonResult = + { + Null: 0, + Structure: 1, + StructureSameKeys: 2, + StructureSameTypes: 3, + Same: 4 + }; + +class Codec { + + + static parse(data, offset, sizeObject, connection, dataType = DataType.Unspecified) { + + var size; + + var reply = new AsyncReply(); + + var isArray; + var t; + + if (dataType == DataType.Unspecified) { + size = 1; + dataType = data[offset++]; + } + else + size = 0; + + t = dataType & 0x7F; + + isArray = (dataType & 0x80) == 0x80; + + var payloadSize = DataType.sizeOf(dataType); + + var contentLength = 0; + + // check if we have the enough data + if (payloadSize == -1) { + contentLength = data.getUint32(offset); + offset += 4; + size += 4 + contentLength; + } + else + size += payloadSize; + + + sizeObject.size = size; + + if (isArray) { + switch (t) { + // VarArray ? + case DataType.Void: + return Codec.parseVarArray(data, offset, contentLength, connection); + + case DataType.Bool: + return new AsyncReply(data.getBooleanArray(offset, contentLength)); + + case DataType.UInt8: + return new AsyncReply(data.getUint8Array(offset, contentLength)); + + case DataType.Int8: + return new AsyncReply(data.getInt8Array(offset, contentLength)); + + case DataType.Char: + return new AsyncReply(data.getCharArray(offset, contentLength)); + + case DataType.Int16: + return new AsyncReply(data.getInt16Array(offset, contentLength)); + + case DataType.UInt16: + return new AsyncReply(data.getUint16Array(offset, contentLength)); + + case DataType.Int32: + return new AsyncReply(data.getInt32Array(offset, contentLength)); + + case DataType.UInt32: + return new AsyncReply(data.getUint32Array(offset, contentLength)); + + case DataType.Int64: + return new AsyncReply(data.getInt64Array(offset, contentLength)); + + case DataType.UInt64: + return new AsyncReply(data.getUint64Array(offset, contentLength)); + + case DataType.Float32: + return new AsyncReply(data.getFloat32Array(offset, contentLength)); + + case DataType.Float64: + return new AsyncReply(data.getFloat64Array(offset, contentLength)); + + case DataType.String: + return new AsyncReply(data.getStringArray(offset, contentLength)); + + case DataType.Resource: + case DataType.DistributedResource: + return Codec.parseResourceArray(data, offset, contentLength, connection); + + case DataType.DateTime: + return new AsyncReply(data.getDateTimeArray(offset, contentLength)); + + case DataType.Structure: + return Codec.parseStructureArray(data, offset, contentLength, connection); + } + } + else { + switch (t) { + case DataType.NotModified: + return new AsyncReply(new NotModified()); + + case DataType.Void: + return new AsyncReply(null); + + case DataType.Bool: + return new AsyncReply(data.getBoolean(offset)); + + case DataType.UInt8: + return new AsyncReply(data[offset]); + + case DataType.Int8: + return new AsyncReply(data.getInt8(offset)); + + case DataType.Char: + return new AsyncReply(data.getChar(offset)); + + case DataType.Int16: + return new AsyncReply(data.getInt16(offset)); + + case DataType.UInt16: + return new AsyncReply(data.getUint16(offset)); + + case DataType.Int32: + return new AsyncReply(data.getInt32(offset)); + + case DataType.UInt32: + return new AsyncReply(data.getUint32(offset)); + + case DataType.Int64: + return new AsyncReply(data.getInt64(offset)); + + case DataType.UInt64: + return new AsyncReply(data.getUint64(offset)); + + case DataType.Float32: + return new AsyncReply(data.getFloat32(offset)); + + case DataType.Float64: + return new AsyncReply(data.getFloat64(offset)); + + case DataType.String: + return new AsyncReply(data.getString(offset, contentLength)); + + case DataType.Resource: + return Codec.parseResource(data, offset); + + case DataType.DistributedResource: + return Codec.parseDistributedResource(data, offset, connection); + + case DataType.DateTime: + return new AsyncReply(data.getDateTime(offset)); + + case DataType.Structure: + return Codec.parseStructure(data, offset, contentLength, connection); + } + } + + + return null; + } + + static parseResource(data, offset) { + return Warehouse.get(data.getUint32(offset)); + } + + static parseDistributedResource(data, offset, connection) { + //var g = data.getGuid(offset); + //offset += 16; + + // find the object + var iid = data.getUint32(offset); + + return connection.fetch(iid);// Warehouse.Get(iid); + } + + /// + /// 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 parseResourceArray(data, offset, length, connection) + { + var reply = new AsyncBag(); + if (length == 0) + { + reply.seal(); + return reply; + } + + var end = offset + length; + + // + var result = data[offset++]; + + var previous = null; + + if (result == ResourceComparisonResult.Null) + previous = new AsyncReply(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++]; + + var current = null; + + if (result == ResourceComparisonResult.Null) + { + current = new AsyncReply(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 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. + + static composePropertyValueArray(array, connection, prependLength = false) + { + var rt = BL(); + for (var i = 0; i < array.Length; i++) + rt.addUint8Array(Codec.composePropertyValue(array[i], connection)); + if (prependLength) + rt.addUint32(rt.length, 0); + return rt.toArray(); + } + + /// + /// Compose a property value. + /// + /// Property value + /// DistributedConnection is required to check locality. + /// Array of bytes in the network byte order. + static composePropertyValue(propertyValue, connection) + { + // age, date, value + return BL().addUint64(propertyValue.age) + .addDateTime(propertyValue.date) + .addUint8Array(Codec.compose(propertyValue.value, connection)) + .toArray(); + } + + + /// + /// Parse property value. + /// + /// Array of bytes. + /// Zero-indexed offset. + /// DistributedConnection is required to fetch resources. + /// Output content size. + /// PropertyValue. + static parsePropertyValue(data, offset, sizeObject, connection) + { + var reply = new AsyncReply(); + + var age = data.getUint64(offset); + offset += 8; + + var date = data.getDateTime(offset); + offset += 8; + + var cs = {}; + + Codec.parse(data, offset, cs, connection).then(function(value) + { + reply.trigger(new PropertyValue(value, age, date)); + }); + + sizeObject.size = 16 + cs.size; + 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 parseHistory(data, offset, length, resource, connection) + { + var list = new KeyList(); + + var reply = new AsyncReply(); + + var bagOfBags = new AsyncBag(); + + var ends = offset + length; + 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(Codec.parsePropertyValueArray(data, offset, cs, connection)); + offset += cs; + } + + bagOfBags.seal(); + + bagOfBags.then(x => + { + for(var i = 0; i < list.length; i++) + list.values[i] = x[i]; + + reply.trigger(list); + }); + + return reply; + + } + + /// + /// Compose resource history + /// + /// History + /// DistributedConnection is required to fetch resources. + /// + static composeHistory(history, connection, prependLength = false) + { + var rt = new BinaryList(); + + for (var i = 0; i < history.length; i++) + rt.addUint8(history.keys[i].index).addUint8Array(Codec.composePropertyValueArray(history.values[i], connection, true)); + + if (prependLength) + rt.addUint32(rt.length, 0); + + return rt.toArray(); + } + + /// + /// Parse an array of ProperyValue. + /// + /// Array of bytes. + /// Zero-indexed offset. + /// Number of bytes to parse. + /// DistributedConnection is required to fetch resources. + /// + static parsePropertyValueArray(data, offset, length, connection) + { + var rt = new AsyncBag(); + + while (length > 0) + { + var cs = {}; + + rt.add(Codec.parsePropertyValue(data, offset, cs, connection)); + + if (cs.size > 0) + { + offset += cs.size; + length -= cs.size; + } + else + throw new Exception("Error while parsing ValueInfo structured data"); + } + + rt.seal(); + return rt; + } + + static parseStructure(data, offset, contentLength, connection, metadata = null, keys = null, types = null) + { + var reply = new AsyncReply(); + var bag = new AsyncBag(); + + + var keylist = []; + var typelist = []; + + + if (keys == null) { + while (contentLength > 0) { + var len = data[offset++]; + keylist.push(data.getString(offset, len)); + offset += len; + + typelist.push(data[offset]); + + var rt = {}; + bag.add(Codec.parse(data, offset, rt, connection)); + contentLength -= rt.size + len + 1; + offset += rt.size; + } + } + else if (types == null) { + for (var i = 0; i < keys.length; i++) + keylist.push(keys[i]); + + while (contentLength > 0) { + typelist.push(data[offset]); + + var rt = {}; + bag.add(Codec.parse(data, offset, rt, connection)); + contentLength -= rt.size; + offset += rt.size; + } + } + else { + + for (var i = 0; i < keys.length; i++) { + keylist.push(keys[i]); + typelist.push(types[i]); + } + + var i = 0; + while (contentLength > 0) { + var rt = {}; + bag.add(Codec.parse(data, offset, rt, connection, types[i])); + contentLength -= rt.size; + offset += rt.size; + i++; + } + } + + bag.seal(); + + bag.then(function (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; + } + + + static parseVarArray(data, offset, contentLength, connection) { + var rt = new AsyncBag(); + + while (contentLength > 0) { + var cs = {}; + + rt.add(Codec.parse(data, offset, cs, connection)); + + if (cs.size > 0) { + offset += cs.size; + contentLength -= cs.size; + } + else + throw new Exception("Error while parsing structured data"); + + } + + rt.seal(); + return rt; + } + + + static compose(value, connection, prependType = true) { + + if (value instanceof Function) + value = value(connection); + else if (value instanceof DistributedPropertyContext) + value = value.method(this); + + var type = Codec.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.addUint32(st.length).addUint8Array(st); + break; + + case DataType.Resource: + rt.addUint32(value._p.instanceId); + break; + + case DataType.DistributedResource: +// rt.addUint8Array(DC.stringToBytes(value.instance.template.classId)).addUint32(value.instance.id); + rt.addUint32(value.instance.id); + break; + + case DataType.Structure: + rt.addUint8Array(Codec.composeStructure(value, connection, true, true, true)); + break; + + case DataType.VarArray: + rt.addUint8Array(Codec.composeVarArray(value, connection, true)); + break; + + case DataType.ResourceArray: + rt.addUint8Array(Codec.composeResourceArray(value, connection, true)); + break; + + case DataType.StructureArray: + rt.addUint8Array(Codec.composeStructureArray(value, connection, true)); + break; + + default: + rt.add({type: type, value: value}); + if (DataType.isArray(type)) + rt.addUint32(rt.length, 0); + + break; + } + + if (prependType) + rt.addUint8(type, 0); + + return rt.toArray(); + } + + static composeVarArray(array, connection, prependLength = false) { + var rt = new BinaryList(); + + for (var i = 0; i < array.length; i++) + rt.addUint8Array(Codec.compose(array[i], connection)); + + if (prependLength) + rt.addUint32(rt.length, 0); + return rt.toArray(); + } + + static composeStructure(value, connection, includeKeys = true, includeTypes = true, prependLength = false) { + var rt = new BinaryList(); + + var keys = value.getKeys(); + + if (includeKeys) { + for (var i = 0; i < keys.length; i++) { + var key = DC.stringToBytes(keys[i]); + rt.addUint8(key.length).addString(key).addUint8Array(DC.compose(value[i], connection)); + } + } + else { + for (var i = 0; i < keys.length; i++) + rt.addUint8Array(DC.compose(value[keys[i]], connection, includeTypes)); + } + + if (prependLength) + rt.addUint32(rt.length, 0); + + return rt.toArray(); + } + + static composeStructureArray(structures, connection, prependLength = false) { + if (structures == null || structures.length == 0 || !(structures instanceof StructureArray)) + return new DC(0); + + var rt = new BinaryList(); + var comparision = StructureComparisonResult.Structure; + + rt.addUint8(comparision); + rt.addUint8Array(Codec.composeStructure(structures[0], connection)); + + for (var i = 1; i < structures.Length; i++) { + comparision = Codec.compareStructure(structures[i - 1], structures[i], connection); + rt.addUint8(comparision); + + if (comparision == StructureComparisonResult.Structure) + rt.addUint8Array(Codec.composeStructure(structures[i], connection)); + else if (comparision == StructureComparisonResult.StructureSameKeys) + rt.addUint8Array(Codec.composeStructure(structures[i], connection, false)); + else if (comparision == StructureComparisonResult.StructureSameTypes) + rt.addUint8Array(Codec.composeStructure(structures[i], connection, false, false)); + } + + if (prependLength) + rt.addUint32(rt.length, 0); + + return rt.toArray(); + } + + static compareStructure(previous, next, connection) { + if (next == null) + return StructureComparisonResult.Null; + + if (previous == null) + return StructureComparisonResult.Structure; + + if (next == previous) + return StructureComparisonResult.Same; + + if (previous.length != next.length) + return StructureComparisonResult.Structure; + + var previousKeys = previous.getKeys(); + var nextKeys = next.getKeys(); + + for (var i = 0; i < previousKeys.length; i++) + if (previousKeys[i] != nextKeys[i]) + return StructureComparisonResult.Structure; + + var previousTypes = Codec.getStructureDateTypes(previous, connection); + var nextTypes = Codec.getStructureDateTypes(next, connection); + + for (var i = 0; i < previousTypes.length; i++) + if (previousTypes[i] != nextTypes[i]) + return StructureComparisonResult.StructureSameKeys; + + return StructureComparisonResult.StructureSameTypes; + } + + static getStructureDateTypes(structure, connection) { + var keys = structure.getKeys(); + var types = []; + + for (var i = 0; i < keys.length; i++) + types.push(Codec.getDataType(structure[keys[i]], connection)); + return types; + } + +static isLocalResource(resource, connection) { + if (resource instanceof DistributedResource) + if (resource._p.connection == connection) + return true; + + return false; +} + + static composeResource(resource, connection) { + if (Codec.isLocalResource(resource, connection)) + return BL().addUint32(resource.id); + else { + return BL().addUint8Array(resource.instance.template.classId.value).addUint32(resource.instance.id); + } + } + + static compareResource(previous, next, connection) { + + if (next == null) + return ResourceComparisonResult.Null; + else if (next == previous) + return ResourceComparisonResult.Same; + else if (Codec.isLocalResource(next, connection)) + return ResourceComparisonResult.Local; + else + return ResourceComparisonResult.Distributed; + } + + static composeResourceArray(resources, connection, prependLength = false) { + + if (resources == null || resources.length == 0)// || !(resources instanceof ResourceArray)) + return prependLength ? new DC(4) : new DC(0); + + var rt = new BinaryList(); + var comparsion = Codec.compareResource(null, resources[0], connection); + + rt.addUint8(comparsion); + + if (comparsion == ResourceComparisonResult.Local) + rt.addUint32(resources[0]._p.instanceId); + else if (comparsion == ResourceComparisonResult.Distributed) + rt.addUint32(resources[0].instance.id); + + for (var i = 1; i < resources.Length; i++) + { + comparsion = Codec.compareResource(resources[i - 1], resources[i], connection); + rt.addUint8(comparsion); + if (comparsion == ResourceComparisonResult.Local) + rt.addUint32(resources[i]._p.instanceId); + else if (comparsion == ResourceComparisonResult.Distributed) + rt.addUint32(resources[i].instance.id); + } + + if (prependLength) + rt.addUint32(rt.length, 0); + + + return rt.toArray(); + } + + + +static getDataType(value) { + switch (typeof value) { + case "number": + // float or ? + if (Math.floor(value) == value) { + if (value > 0) { + // larger than byte ? + if (value > 0xFF) { + // larger than short ? + if (value > 0xFFFF) { + // larger than int ? + if (value > 0xFFFFFFFF) { + return DataType.UInt64; + } + else { + return DataType.UInt32; + } + } + else { + return DataType.UInt16; + } + } + else { + return DataType.UInt8; + } + } + else { + if (value < -128) { + if (value < -32768) { + if (value < -2147483648) { + return DataType.Int64; + } + else { + return DataType.Int32; + } + } + else { + return DataType.Int16; + } + } + else { + return DataType.Int8; + } + } + } + else { + // float or double + return DataType.Float64; + } + break; + + case "string": + return DataType.String; + case "boolean": + return DataType.Bool; + case "object": + if (value instanceof Array) { + return DataType.VarArray; + } + else if (value instanceof IResource) { + return Codec.isLocalResource(value, connection) ? DataType.Resource : DataType.DistributedResource; + } + else if (value instanceof Date) { + return DataType.DateTime; + } + else if (value instanceof Uint8Array + || value instanceof ArrayBuffer) { + return DataType.UInt8Array; + } + else if (value instanceof Number) { + // JS numbers are always 64-bit float + return DataType.Float64; + } + else if (value instanceof Structure) { + return DataType.Structure; + } + else { + return DataType.Void + } + + break; + + default: + return DataType.Void; + } + } + + + /// + /// 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 parseStructureArray(data, offset, length, connection) + { + var reply = new AsyncBag(); + if (length == 0) + { + reply.seal(); + return reply; + } + + var end = offset + length; + + var result = data[offset++]; + + var previous = null; + //var previousKeys = []; + //var previousTypes = []; + + var metadata = {keys: null, types: null}; + + + if (result == StructureComparisonResult.Null) + previous = new AsyncReply(null); + else if (result == StructureComparisonResult.Structure) + { + var cs = data.getUint32(offset); + offset += 4; + previous = this.parseStructure(data, offset, cs, connection, metadata); + offset += cs; + } + + reply.add(previous); + + + while (offset < end) + { + result = data[offset++]; + + if (result == StructureComparisonResult.Null) + previous = new AsyncReply(null); + else if (result == StructureComparisonResult.Structure) + { + var cs = data.getUint32(offset); + offset += 4; + previous = this.parseStructure(data, offset, cs, connection, metadata); + offset += cs; + } + else if (result == StructureComparisonResult.StructureSameKeys) + { + var cs = data.getUint32(offset); + offset += 4; + previous = this.parseStructure(data, offset, cs, connection, metadata, metadata.keys); + offset += cs; + } + else if (result == StructureComparisonResult.StructureSameTypes) + { + var cs = data.getUint32(offset); + offset += 4; + previous = this.parseStructure(data, offset, cs, connection, metadata, metadata.keys, metadata.types); + offset += cs; + } + + reply.add(previous); + } + + reply.seal(); + return reply; + } + + +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 25/07/2017. + */ + +"use strict"; + +const UNIX_EPOCH = 621355968000000000; +const TWO_PWR_32 = (1 << 16) * (1 << 16); + +class DC extends Uint8Array// extends DataView // Data Converter +{ + constructor(bufferOrSize) { + super(bufferOrSize); + + //if (bufferOrSize instanceof ArrayBuffer) { + // this.buffer = bufferOrSize; + //} + //else + //{ + // this.buffer = new Uint8Array(bufferOrSize); + //} + + this.dv = new DataView(this.buffer); + } + + + static boolToBytes(value) + { + var rt = new DC(1); + rt.setBoolean(0, value); + return rt; + } + + static int8ToBytes(value) + { + var rt = new DC(1); + rt.setInt8(0, value); + return rt; + } + + static hexToBytes(value) + { + // convert hex to Uint8Array + var rt = new DC(value.length/2); + for(var i = 0; i < ar.length; i++) + rt[i] = parseInt(value.substr(i*2, 2), 16); + return rt; + } + + static uint8ToBytes(value) + { + var rt = new DC(1); + rt.setUint8(0, value); + return rt; + } + + static charToBytes(value) + { + var rt = new DC(2); + rt.setChar(0, value); + return rt; + } + + static int16ToBytes(value) + { + var rt = new DC(2); + rt.setInt16(0, value); + return rt; + } + + static uint16ToBytes(value) + { + var rt = new DC(2); + rt.setUint16(0, value); + return rt; + } + + static int32ToBytes(value) + { + var rt = new DC(4); + rt.setInt32(0, value); + return rt; + } + + static uint32ToBytes(value) + { + var rt = new DC(4); + rt.setUint32(0, value); + return rt; + } + + static float32ToBytes(value) + { + var rt = new DC(4); + rt.setFloat32(0, value); + return rt; + } + + static int64ToBytes(value) + { + var rt = new DC(8); + rt.setInt64(0, value); + return rt; + } + + static uint64ToBytes(value) + { + var rt = new DC(8); + rt.setUint64(0, value); + return rt; + } + + static float64ToBytes(value) + { + var rt = new DC(8); + rt.setFloat64(0, value); + return rt; + } + + static dateTimeToBytes(value) + { + var rt = new DC(8); + rt.setDateTime(0, value); + return rt; + } + + static stringToBytes(value) + { + var utf8 = unescape(encodeURIComponent(value)); + var rt = []; + + for (var i = 0; i < utf8.length; i++) + rt.push(utf8.charCodeAt(i)); + + return new DC(rt); + } + + static stringArrayToBytes(values) + { + var list = new BinaryList(); + for(var i = 0; i < values.length; i++) + { + var s = DC.stringToBytes(values[i]); + list.addUint32(s.length).addUint8Array(s); + } + + return list.toArray(); + } + + append(src, offset, length) + { + if (!(src instanceof 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); + return rt; + } + + static combine(a, aOffset, aLength, b, bOffset, bLength) + { + if (!(a instanceof DC)) + a = new DC(a); + if (!(b instanceof 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; + } + + clip(offset, length) + { + return this.slice(offset, offset+length); + } + + getInt8(offset) + { + return this.dv.getUint8(offset); + } + + getUint8(offset) + { + return this[offset];// this.dv.getUint8(offset); + } + + getInt16(offset) + { + return this.dv.getInt16(offset); + } + + getUint16(offset) + { + return this.dv.getUint16(offset); + } + + getInt32(offset) + { + return this.dv.getInt32(offset); + } + + getUint32(offset) + { + return this.dv.getUint32(offset); + } + + getFloat32(offset) + { + return this.dv.getFloat32(offset); + } + + getFloat64(offset) + { + return this.dv.getFloat64(offset); + } + + setInt8(offset, value) + { + return this.dv.setInt8(offset, value); + } + + setUint8(offset, value) + { + return this.dv.setUint8(offset, value); + } + + setInt16(offset, value) + { + return this.dv.setInt16(offset, value); + } + + setUint16(offset, value) + { + return this.dv.setUint16(offset, value); + } + + setInt32(offset, value) + { + return this.dv.setInt32(offset, value); + } + + setUint32(offset, value) + { + return this.dv.setUint32(offset, value); + } + + setFloat32(offset, value) + { + return this.dv.setFloat32(offset, value); + } + + setFloat64(offset, value) + { + return this.dv.setFloat64(offset, value); + } + + getInt8Array(offset, length) + { + return new Int8Array(this.buffer, offset, length); + } + + getUint8Array(offset, length) + { + return new Uint8Array(this.buffer, offset, length); + } + + getInt16Array(offset, length) + { + return new Int16Array(this.buffer, offset, length); + } + + getUint16Array(offset, length) + { + return new Uint16Array(this.buffer, offset, length); + } + + getInt32Array(offset, length) + { + return new Int32Array(this.buffer, offset, length); + } + + getUint32Array(offset, length) + { + return new Uint32Array(this.buffer, offset, length); + } + + getFloat32Array(offset, length) + { + return new Float32Array(this.buffer, offset, length); + } + + getFloat64Array(offset, length) + { + return new Float64Array(this.buffer, offset, length); + } + + getInt64Array(offset, length) + { + return new Int64Array(this.buffer, offset, length); + } + + + getUint64Array(offset, length) + { + return new Uint64Array(this.buffer, offset, length); + } + + getBoolean(offset) + { + return this.getUint8(offset) > 0; + } + + setBoolean(offset, value) + { + this.setUint8(offset, value ? 1: 0); + } + + getBooleanArray(offset, length) + { + var rt = []; + for(var i = 0; i < length; i++) + rt.push(this.getBoolean(offset+i)); + return rt; + } + + getChar(offset) + { + return String.fromCharCode(this.getUint16(offset)); + } + + setChar(offset, value) + { + this.setUint16(offset, value.charCodeAt(0)); + } + + getCharArray(offset, length) + { + var rt = []; + for(var i = 0; i < length; i+=2) + rt.push(this.getChar(offset+i)); + return rt; + } + + 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; + } + + getString(offset, length) + { + if (typeof StringView != "undefined") + return new StringView(this.buffer, "UTF-8", offset, length); + else + { + var bytes = this.getUint8Array(offset, length); + var encodedString = String.fromCharCode.apply(null, bytes), + decodedString = decodeURIComponent(escape(encodedString)); + return decodedString; + } + } + + getStringArray(offset, length) + { + var rt = []; + var i = 0; + + while (i < length) + { + var cl = this.getUint32(offset + i); + i += 4; + rt.push(this.getString(offset + i, cl)); + i += cl; + } + + return rt; + } + + getInt64(offset) + { + var h = this.getInt32(offset); + var l = this.getInt32(offset + 4); + + return h * TWO_PWR_32 + ((l >= 0) ? l : TWO_PWR_32 + l); + } + + getUint64(offset) + { + var h = this.getUint32(offset); + var l = this.getUint32(offset + 4); + return h * TWO_PWR_32 + ((l >= 0) ? l : TWO_PWR_32 + l); + } + + setInt64(offset, value) + { + var l = (value % TWO_PWR_32) | 0; + var h = (value / TWO_PWR_32) | 0; + this.setInt32(offset, h); + this.setInt32(offset + 4, l); + } + + setUint64(offset, value) + { + var l = (value % TWO_PWR_32) | 0; + var h = (value / TWO_PWR_32) | 0; + this.setInt32(offset, h); + this.setInt32(offset + 4, l); + } + + setDateTime(offset, value) + { + // Unix Epoch + var ticks = 621355968000000000 + (value.getTime() * 10000); + this.setUint64(offset, ticks); + } + + getDateTime(offset) + { + var ticks = this.getUint64(offset); + return new Date(Math.round((ticks-UNIX_EPOCH)/10000)); + } + + getDateTimeArray(offset) + { + var rt = []; + for(var i = 0; i < length; i+=8) + rt.push(this.getDateTime(offset+i)); + return rt; + } + + getGuid(offset) + { + return new Guid(this.clip(offset, 16)); + + /* + var d = this.getUint8Array(offset, 16); + var rt = ""; + for (var i = 0; i < 16; i++) { + rt += String.fromCharCode(d[i]); + } + + return btoa(rt); + */ + } + + getGuidArray(offset, length) + { + var rt = []; + for(var i = 0; i < length; i+=16) + rt.push(this.getGuid(offset+i)); + return rt; + } + + 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; + } +} + + + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 25/07/2017. + */ + +"use strict"; + +const DataType = +{ + Void: 0x0, + //Variant, + Bool: 1, + Int8: 2, + UInt8: 3, + Char: 4, + Int16: 5, + UInt16: 6, + Int32: 7, + UInt32: 8, + Int64: 9, + UInt64: 10, + Float32: 11, + Float64: 12, + Decimal: 13, + DateTime: 14, + Resource: 15, + DistributedResource: 16, + ResourceLink: 17, + String: 18, + Structure: 19, + //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, + + isArray: function(type) + { + return ((type & 0x80) == 0x80) && (type != DataType.NotModified); + }, + + sizeOf: function(type) + { + switch (type) + { + case DataType.Void: + case DataType.NotModified: + return 0; + case DataType.Bool: + case DataType.Int8: + case DataType.UInt8: + return 1; + case DataType.Char: + case DataType.Int16: + case DataType.UInt16: + 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; + } + } +}; + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 25/07/2017. + */ + +"use strict"; + +class DistributedConnection extends IStore { + + send(data) { + console.log("Send", data.length); + this.socket.send(data.buffer); + } + + sendParams(doneReply) { + return new SendList(this, doneReply); + } + + generateNonce(length) + { + var rt = new Uint8Array(length); + for(var i = 0; i < length; i++) + rt[i] = Math.random() * 255; + + return rt; + } + + constructor(url, domain, username, password, checkInterval = 30, connectionTimeout = 600, revivingTime = 120) { + + super(); + + //Instance.Name = Global.GenerateCode(12); + + //this.hostType = AuthenticationType.Client; + //this.domain = domain; + //this.localUsername = username; + + this._register("ready"); + this._register("error"); + this._register("close"); + + this.session = new Session(new Authentication(AuthenticationType.Client), new Authentication(AuthenticationType.Host)); + + this.session.localAuthentication.domain = domain; + this.session.localAuthentication.username = username; + + this.localPassword = DC.stringToBytes(password); + + this.socket = new WebSocket(url, "iip"); + this.socket.binaryType = "arraybuffer"; + this.socket.connection = this; + this.socket.networkBuffer = new NetworkBuffer(); + + this.debug = false; + this.totalReceived = 0; + this.totalSent = 0; + + this.checkInterval = checkInterval * 1000; // check every 30 seconds + this.connectionTimeout = connectionTimeout * 1000; // 10 minutes (4 pings failed) + this.revivingTime = revivingTime * 1000; // 2 minutes + this.lastAction = Date.now(); + + this.packet = new IIPPacket(); + this.authPacket = new IIPAuthPacket(); + + this.resources = {}; + this.templates = new KeyList(); + this.requests = {}; + this.pathRequests = {}; + this.templateRequests = new KeyList(); + this.resourceRequests = {}; + this.callbackCounter = 0; + + this.queue = new AsyncQueue(); + + this.queue.then(function (x) { + if (x.type == DistributedResourceQueueItemType.Event) { + x.resource._emitEventByIndex(x.index, x.value); + } + else { + x.resource._updatePropertyByIndex(x.index, x.value); + } + }); + + this.localNonce = this.generateNonce(32);// new Uint8Array(32); + //window.crypto.getRandomValues(this.localNonce); + + // declare (Credentials -> No Auth, No Enctypt) + var un = DC.stringToBytes(username); + var dmn = DC.stringToBytes(domain); + var self = this; + + this.socket.onopen = function () { + var bl = new BinaryList(); + bl.addUint8(0x60).addUint8(dmn.length).addUint8Array(dmn).addUint8Array(self.localNonce).addUint8(un.length).addUint8Array(un); + self.send(bl.toArray()); + }; + + this.socket.onmessage = function (msg) { + + console.log("Rec", msg.data.byteLength); + + this.networkBuffer.writeAll(msg.data); + + self.lastAction = new Date(); + + while (this.networkBuffer.available > 0 && !this.networkBuffer.protected) + self.receive(this.networkBuffer); + + + }; + + this.socket.onclose = function(event) + { + self.close(event); + }; + + //this.socket.onerror = function(event) + //{ + // self.close(event); + //}; + } + + + processPacket(msg, offset, ends, data) + { + + + var authPacket = this.authPacket; + + if (this.ready) { + var packet = new IIPPacket(); + + var rt = packet.parse(msg, offset, ends); + if (rt <= 0) { + data.holdFor(msg, offset, ends - offset, -rt); + return ends; + } + else { + offset += rt; + + if (packet.command == IIPPacketCommand.Event) { + switch (packet.event) { + case IIPPacketEvent.ResourceReassigned: + this.IIPEventResourceReassigned(packet.resourceId, packet.newResourceId); + break; + case IIPPacketEvent.ResourceDestroyed: + this.IIPEventResourceDestroyed(packet.resourceId); + break; + case IIPPacketEvent.PropertyUpdated: + this.IIPEventPropertyUpdated(packet.resourceId, packet.methodIndex, packet.content); + break; + case IIPPacketEvent.EventOccurred: + this.IIPEventEventOccurred(packet.resourceId, packet.methodIndex, packet.content); + break; + + case IIPPacketEvent.ChildAdded: + this.IIPEventChildAdded(packet.resourceId, packet.childId); + break; + case IIPPacketEvent.ChildRemoved: + this.IIPEventChildRemoved(packet.resourceId, packet.childId); + break; + case IIPPacketEvent.Renamed: + this.IIPEventRenamed(packet.resourceId, packet.content); + break; + case IIPPacketEvent.AttributesUpdated: + this.IIPEventAttributesUpdated(packet.resourceId, packet.content); + break; + + } + } + else if (packet.command == IIPPacketCommand.Request) { + switch (packet.action) { + + // Manage + case IIPPacketAction.AttachResource: + this.IIPRequestAttachResource(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.ReattachResource: + this.IIPRequestReattachResource(packet.callbackId, packet.resourceId, packet.resourceAge); + break; + case IIPPacketAction.DetachResource: + this.IIPRequestDetachResource(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.CreateResource: + this.IIPRequestCreateResource(packet.callbackId, packet.storeId, packet.resourceId, packet.content); + break; + case IIPPacketAction.DeleteResource: + this.IIPRequestDeleteResource(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.AddChild: + this.IIPRequestAddChild(packet.callbackId, packet.resourceId, packet.childId); + break; + case IIPPacketAction.RemoveChild: + this.IIPRequestRemoveChild(packet.callbackId, packet.resourceId, packet.childId); + break; + case IIPPacketAction.RenameResource: + this.IIPRequestRenameResource(packet.callbackId, packet.resourceId, packet.content); + break; + + // Inquire + case IIPPacketAction.TemplateFromClassName: + this.IIPRequestTemplateFromClassName(packet.callbackId, packet.className); + break; + case IIPPacketAction.TemplateFromClassId: + this.IIPRequestTemplateFromClassId(packet.callbackId, packet.classId); + break; + case IIPPacketAction.TemplateFromResourceId: + this.IIPRequestTemplateFromResourceId(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.QueryLink: + this.IIPRequestQueryResources(packet.callbackId, packet.resourceLink); + break; + case IIPPacketAction.ResourceChildren: + this.IIPRequestResourceChildren(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.ResourceParents: + this.IIPRequestResourceParents(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.ResourceHistory: + this.IIPRequestInquireResourceHistory(packet.callbackId, packet.resourceId, packet.fromDate, packet.toDate); + break; + + // Invoke + case IIPPacketAction.InvokeFunction: + this.IIPRequestInvokeFunction(packet.callbackId, packet.resourceId, packet.methodIndex, packet.content); + break; + case IIPPacketAction.GetProperty: + this.IIPRequestGetProperty(packet.callbackId, packet.resourceId, packet.methodIndex); + break; + case IIPPacketAction.GetPropertyIfModified: + this.IIPRequestGetPropertyIfModifiedSince(packet.callbackId, packet.resourceId, packet.methodIndex, packet.resourceAge); + break; + case IIPPacketAction.SetProperty: + this.IIPRequestSetProperty(packet.callbackId, packet.resourceId, packet.methodIndex, packet.content); + break; + case IIPPacketAction.ResourceHistory: + this.IIPRequestInquireResourceHistory(packet.callbackId, packet.resourceId, packet.fromDate, packet.toDate); + break; + case IIPPacketAction.QueryLink: + this.IIPRequestQueryResources(packet.callbackId, packet.resourceLink); + break; + + // Attribute + case IIPPacketAction.GetAllAttributes: + this.IIPRequestGetAttributes(packet.callbackId, packet.resourceId, packet.content, true); + break; + case IIPPacketAction.UpdateAllAttributes: + this.IIPRequestUpdateAttributes(packet.callbackId, packet.resourceId, packet.content, true); + break; + case IIPPacketAction.ClearAllAttributes: + this.IIPRequestClearAttributes(packet.callbackId, packet.resourceId, packet.content, true); + break; + case IIPPacketAction.GetAttributes: + this.IIPRequestGetAttributes(packet.callbackId, packet.resourceId, packet.content, false); + break; + case IIPPacketAction.UpdateAttributes: + this.IIPRequestUpdateAttributes(packet.callbackId, packet.resourceId, packet.content, false); + break; + case IIPPacketAction.ClearAttributes: + this.IIPRequestClearAttributes(packet.callbackId, packet.resourceId, packet.content, false); + break; + + } + } + else if (packet.command == IIPPacketCommand.Reply) { + switch (packet.action) { + case IIPPacketAction.AttachResource: + this.IIPReply(packet.callbackId, packet.classId, packet.resourceAge, packet.resourceLink, packet.content); + break; + case IIPPacketAction.ReattachResource: + this.IIPReply(packet.callbackId, packet.resourceAge, packet.content); + break; + case IIPPacketAction.DetachResource: + this.IIPReply(packet.callbackId); + break; + case IIPPacketAction.CreateResource: + this.IIPReply(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.DeleteResource: + case IIPPacketAction.AddChild: + case IIPPacketAction.RemoveChild: + case IIPPacketAction.RenameResource: + this.IIPReply(packet.callbackId); + break; + case IIPPacketAction.TemplateFromClassName: + case IIPPacketAction.TemplateFromClassId: + case IIPPacketAction.TemplateFromResourceId: + this.IIPReply(packet.callbackId, ResourceTemplate.parse(packet.content)); + break; + + case IIPPacketAction.QueryLink: + case IIPPacketAction.ResourceChildren: + case IIPPacketAction.ResourceParents: + case IIPPacketAction.ResourceHistory: + this.IIPReply(packet.callbackId, packet.content); + break; + + case IIPPacketAction.InvokeFunction: + this.IIPReplyInvoke(packet.callbackId, packet.content); + break; + case IIPPacketAction.GetProperty: + this.IIPReply(packet.callbackId, packet.content); + break; + case IIPPacketAction.GetPropertyIfModified: + this.IIPReply(packet.callbackId, packet.content); + break; + case IIPPacketAction.SetProperty: + this.IIPReply(packet.callbackId); + break; + + // Attribute + case IIPPacketAction.GetAllAttributes: + case IIPPacketAction.GetAttributes: + this.IIPReply(packet.callbackId, packet.content); + break; + + case IIPPacketAction.UpdateAllAttributes: + case IIPPacketAction.UpdateAttributes: + case IIPPacketAction.ClearAllAttributes: + case IIPPacketAction.ClearAttributes: + this.IIPReply(packet.callbackId); + break; + + } + + } + else if (packet.command == IIPPacketCommand.Report) + { + switch (packet.report) + { + case IIPPacketReport.ManagementError: + this.IIPReportError(packet.callbackId, ErrorType.Management, packet.errorCode, null); + break; + case IIPPacketReport.ExecutionError: + this.IIPReportError(packet.callbackId, ErrorType.Exception, packet.errorCode, packet.errorMessage); + break; + case IIPPacketReport.ProgressReport: + this.IIPReportProgress(packet.callbackId, ProgressType.Execution, packet.progressValue, packet.progressMax); + break; + case IIPPacketReport.ChunkStream: + this.IIPReportChunk(packet.callbackId, packet.content); + + break; + } + } + + } + } + + else { + var rt = authPacket.parse(msg, offset, ends); + + + if (rt <= 0) { + data.holdAllFor(msg, ends - rt); + return ends; + } + else { + offset += rt; + + if (this.session.localAuthentication.type == AuthenticationType.Host) { + if (authPacket.command == IIPAuthPacketCommand.Declare) { + if (authPacket.remoteMethod == IIPAuthPacketMethod.credentials + && authPacket.localMethod == IIPAuthPacketMethod.None) { + this.session.remoteAuthentication.username = authPacket.remoteUsername; + this.remoteNonce = authPacket.remoteNonce; + this.domain = authPacket.domain; + this.sendParams().addUint8(0xa0).addUint8Array(this.localNonce).done(); + } + } + else if (authPacket.command == IIPAuthPacketCommand.Action) { + if (authPacket.action == IIPAuthPacketAction.AuthenticateHash) { + var remoteHash = authPacket.hash; + + this.server.membership.getPassword(this.session.remoteAuthentication.username, this.domain).then(function (pw) { + if (pw != null) { + + //var hash = new DC(sha256.arrayBuffer(BL().addString(pw).addUint8Array(remoteNonce).addUint8Array(this.localNonce).toArray())); + var hash = SHA256.compute(BL().addString(pw).addUint8Array(remoteNonce).addUint8Array(this.localNonce).toDC()); + + + if (hash.sequenceEqual(remoteHash)) { + // send our hash + //var localHash = new DC(sha256.arrayBuffer((new BinaryList()).addUint8Array(this.localNonce).addUint8Array(remoteNonce).addUint8Array(pw).toArray())); + var localHash = SHA256.compute(BL().addUint8Array(this.localNonce).addUint8Array(remoteNonce).addUint8Array(pw).toDC()); + this.sendParams().addUint8(0).addUint8Array(localHash).done(); + + this.readyToEstablish = true; + } + else { + // incorrect password + this.sendParams().addUint8(0xc0).addInt32(1).addUint16(5).addString("Error").done(); + } + } + }); + } + else if (authPacket.action == IIPAuthPacketAction.NewConnection) { + if (readyToEstablish) { + this.session.id = this.generateNonce(32);// new DC(32); + //window.crypto.getRandomValues(this.session.id); + + this.sendParams().addUint8(0x28).addUint8Array(this.session.id).done(); + this.ready = true; + this._emit("ready", this); + } + } + } + } + else if (this.session.localAuthentication.type == AuthenticationType.Client) { + if (authPacket.command == IIPAuthPacketCommand.Acknowledge) { + this.remoteNonce = authPacket.remoteNonce; + + // send our hash + + //var localHash = new DC(sha256.arrayBuffer(BL().addUint8Array(this.localPassword) + // .addUint8Array(this.localNonce) + // .addUint8Array(this.remoteNonce).toArray())); + + var localHash = SHA256.compute(BL().addUint8Array(this.localPassword) + .addUint8Array(this.localNonce) + .addUint8Array(this.remoteNonce).toDC()); + + this.sendParams().addUint8(0).addUint8Array(localHash).done(); + } + else if (authPacket.command == IIPAuthPacketCommand.Action) { + if (authPacket.action == IIPAuthPacketAction.AuthenticateHash) { + // check if the server knows my password + //var remoteHash = new DC(sha256.arrayBuffer(BL().addUint8Array(this.remoteNonce) + // .addUint8Array(this.localNonce) + // .addUint8Array(this.localPassword).toArray() + //)); + + var remoteHash = SHA256.compute(BL().addUint8Array(this.remoteNonce) + .addUint8Array(this.localNonce) + .addUint8Array(this.localPassword).toDC()); + + + if (remoteHash.sequenceEqual(authPacket.hash)) { + // send establish request + this.sendParams().addUint8(0x20).addUint16(0).done(); + } + else { + this.sendParams().addUint8(0xc0).addUint32(1).addUint16(5).addString("Error").done(); + } + } + else if (authPacket.action == IIPAuthPacketAction.ConnectionEstablished) { + this.session.id = authPacket.sessionId; + this.ready = true; + this._emit("ready", this); + } + } + else if (authPacket.command == IIPAuthPacketCommand.Error) + { + this._emit("error", this, authPacket.errorCode, authPacket.errorMessage); + this.close(); + } + } + } + } + + return offset; + + //if (offset < ends) + // this.processPacket(msg, offset, ends, data); + } + + receive(data) { + var msg = data.read(); + var offset = 0; + var ends = msg.length; + var packet = this.packet; + + //console.log("Data"); + + while (offset < ends) { + offset = this.processPacket(msg, offset, ends, data); + } + + + } + + close(event) + { + this._emit("close", event); + + Warehouse.remove(this); + + if (this.socket.readyState != this.socket.CLOSED) + { + this.socket.close(); + } + } + + trigger(trigger) { + return true; + } + + put(resource) { + this.resources[parseInt(resource.instance.name)] = resource; + return true; + } + + remove(resource) + { + // nothing to do (IStore interface) + } + + // Protocol Implementation + + sendRequest2(action, binaryList) { + var reply = new AsyncReply(); + this.callbackCounter++; + this.sendParams().addUint8(0x40 | action).addUint32(this.callbackCounter).addRange(binaryList).done(); + this.requests[this.callbackCounter] = reply; + return reply; + } + + sendRequest(action) { + var reply = new AsyncReply(); + this.callbackCounter++; + this.requests[this.callbackCounter] = reply; + return this.sendParams(reply).addUint8(0x40 | action).addUint32(this.callbackCounter); + } + + sendInvoke(instanceId, index, parameters) + { + var reply = new AsyncReply(); + + var pb = Codec.composeVarArray(parameters, this, true); + + this.callbackCounter++; + this.sendParams() + .addUint8(0x40 | IIPPacketAction.InvokeFunction) + .addUint32(this.callbackCounter) + .addUint32(instanceId) + .addUint8(index) + .addUint8Array(pb) + .done(); + + this.requests[this.callbackCounter] = reply; + + return reply; + } + + sendError(type, callbackId, errorCode, errorMessage = "") + { + var msg = DC.stringToBytes(errorMessage); + if (type == ErrorType.Management) + this.sendParams() + .addUint8(0xC0 | IIPPacketReport.ManagementError) + .addUint32(callbackId) + .addUint16(errorCode) + .done(); + else if (type == ErrorType.Exception) + this.sendParams() + .addUint8(0xC0 | IIPPacketReport.ExecutionError) + .addUint32(callbackId) + .addUint16(errorCode) + .addUint16(msg.length) + .addUint8Array(msg) + .done(); + } + + sendProgress(callbackId, value, max) + { + this.sendParams() + .addUint8(0xC0 | IIPPacketReport.ProgressReport) + .addUint32(callbackId) + .addInt32(value) + .addInt32(max) + .done(); + } + + sendChunk(callbackId, chunk) + { + var c = Codec.compose(chunk, this, true); + this.sendParams() + .addUint8(0xC0 | IIPPacketReport.ChunkStream) + .addUint32(callbackId) + .addUint8Array(c) + .done(); + } + + IIPReply(callbackId) { + + var results = Array.prototype.slice.call(arguments, 1); + var req = this.requests[callbackId]; + + //console.log("Reply " + callbackId, req); + + delete this.requests[callbackId]; + req.trigger(results); + } + + IIPReplyInvoke(callbackId, result) + { + var req = this.requests[callbackId]; + delete this.requests[callbackId]; + + Codec.parse(result, 0, {}, this).then(function(rt) + { + req.trigger(rt); + }); + } + + IIPReportError(callbackId, errorType, errorCode, errorMessage) + { + var req = this.requests[callbackId]; + delete this.requests[callbackId]; + req.triggerError(errorType, errorCode, errorMessage); + } + + IIPReportProgress(callbackId, type, value, max) + { + var req = this.requests[callbackId]; + req.triggerProgress(type, value, max); + } + + IIPReportChunk(callbackId, data) + { + if (this.requests[callbackId]) + { + var req = this.requests[callbackId]; + Codec.parse(data, 0, {}, this).then(function(x) + { + req.triggerChunk(x); + }); + } + } + + IIPEventResourceReassigned(resourceId, newResourceId) { + + } + + IIPEventResourceDestroyed(resourceId) { + if (this.resources[resourceId]) { + var r = this.resources[resourceId]; + delete this.resources[resourceId]; + r.destroy(); + } + } + + IIPEventPropertyUpdated(resourceId, index, content) { + + var self = this; + + this.fetch(resourceId).then(function(r){ + // push to the queue to gaurantee serialization + var item = new AsyncReply(); + self.queue.add(item); + + Codec.parse(content, 0, {}, self).then(function (args) { + var pt = r.instance.template.getPropertyTemplateByIndex(index); + if (pt != null) { + item.trigger(new DistributedResourceQueueItem(r, DistributedResourceQueueItemType.Propery, args, index)); + } + else { // ft found, fi not found, this should never happen + self.queue.remove(item); + } + }); + }); + } + + + IIPEventEventOccurred(resourceId, index, content) { + var self = this; + + this.fetch(resourceId).then(function(r){ + // push to the queue to guarantee serialization + var item = new AsyncReply(); + var r = self.resources[resourceId]; + + self.queue.add(item); + + Codec.parseVarArray(content, 0, content.length, self).then(function (args) { + var et = r.instance.template.getEventTemplateByIndex(index); + if (et != null) { + item.trigger(new DistributedResourceQueueItem(r, DistributedResourceQueueItemType.Event, args, index)); + } + else { // ft found, fi not found, this should never happen + self.queue.remove(item); + } + }); + }); + } + + IIPEventChildAdded(resourceId, childId) + { + var self = this; + + this.fetch(resourceId).then(function(parent) + { + self.fetch(childId).then(function(child) + { + parent.instance.children.add(child); + }); + }); + } + + IIPEventChildRemoved(resourceId, childId) + { + var self = this; + + this.fetch(resourceId).then(function(parent) + { + self.fetch(childId).then(function(child) + { + parent.instance.children.remove(child); + }); + }); + } + + IIPEventRenamed(resourceId, name) + { + this.fetch(resourceId).then(function(resource) + { + resource.instance.attributes.set("name", name.getString(0, name.length)); + }); + } + + + IIPEventAttributesUpdated(resourceId, attributes) + { + var self = this; + + this.fetch(resourceId).then(function(resource) + { + var attrs = attributes.getStringArray(0, attributes.length); + + self.getAttributes(resource, attrs).then(function(s) + { + resource.instance.setAttributes(s); + }); + }); + } + + sendReply(action, callbackId) + { + return this.sendParams().addUint8(0x80 | action).addUint32(callbackId); + } + + sendEvent(evt) + { + return this.sendParams().addUint8(evt); + } + + IIPRequestAttachResource(callback, resourceId) { + + //var sl = this.sendParams(); + var self = this; + + + Warehouse.get(resourceId).then(function (r) { + if (r != null) { + + if (r.instance.applicable(self.session, ActionType.Attach, null) == Ruling.Denied) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AttachDenied); + return; + } + + r.instance.on("ResourceEventOccurred", self.instance_eventOccurred, self); + r.instance.on("ResourceModified", self.instance_propertyModified, self); + r.instance.on("ResourceDestroyed", self.instance_resourceDestroyed, self); + // reply ok + + var link = DC.stringToBytes(r.instance.link); + + if (r instanceof DistributedResource) + self.sendReply(IIPPacketAction.AttachResource, callback) + .addUint8Array(r.instance.template.classId.value) + .addUint64(r.instance.age) + .addUint16(link.length) + .addUint8Array(link) + .addUint8Array(Codec.composePropertyValueArray(r._serialize(), self, true)) + .done(); + else + self.sendReply(IIPPacketAction.AttachResource, callback) + .addUint8Array(r.instance.template.classId.value) + .addUint64(r.instance.age) + .addUint16(link.length) + .addUint8Array(link) + .addUint8Array(Codec.composePropertyValueArray(r.instance.serialize(), self, true)) + .done(); + } + else { + // reply failed + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + } + }); + } + + IIPRequestReattachResource(callback, resourceId, resourceAge) { + var self = this; + + Warehouse.get(resourceId).then(function (r) { + if (res != null) { + r.instance.on("ResourceEventOccurred", self.instance_eventOccurred, self); + r.instance.on("ResourceModified", self.instance_propertyModified, self); + r.instance.on("ResourceDestroyed", self.instance_resourceDestroyed, self); + // reply ok + self.sendReply(IIPPacketAction.ReattachResource, callback) + .addUint64(r.instance.age) + .addUint8Array(Codec.composePropertyValueArray(r.instance.serialize(), self, true)) + .done(); + } + else { + // reply failed + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + } + }); + } + + IIPRequestDetachResource(callback, resourceId) { + var self = this; + + Warehouse.get(resourceId).then(function (r) { + if (r != null) { + r.instance.off("ResourceEventOccurred", self.instance_eventOccurred); + r.instance.off("ResourceModified", self.instance_propertyModified); + r.instance.off("ResourceDestroyed", self.instance_resourceDestroyed); + + // reply ok + self.sendReply(IIPPacketAction.DetachResource, callback).done(); + } + else { + // reply failed + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + } + }); + } + + IIPRequestCreateResource(callback, storeId, parentId, content) { + var self = this; + Warehouse.get(storeId).then(function(store) + { + if (store == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.StoreNotFound); + return; + } + + if (!(store instanceof IStore)) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceIsNotStore); + return; + } + + // check security + if (store.instance.applicable(self.session, ActionType.CreateResource, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.CreateDenied); + return; + } + + Warehouse.get(parentId).then(function(parent) + { + + // check security + + if (parent != null) + if (parent.instance.applicable(self.session, ActionType.AddChild, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AddChildDenied); + return; + } + + var 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 = window[className]; + + if (type == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ClassNotFound); + return; + } + + Codec.parseVarArray(content, offset, cl, self).then(function(parameters) + { + offset += cl; + cl = content.getUint32(offset); + Codec.parseStructure(content, offset, cl, self).then(function(attributes) + { + offset += cl; + cl = content.length - offset; + + Codec.parseStructure(content, offset, cl, self).then(function(values) + { + + + var resource = new (Function.prototype.bind.apply(type, values)); + + Warehouse.put(resource, name, store, parent); + + + self.sendReply(IIPPacketAction.CreateResource, callback) + .addUint32(resource.Instance.Id) + .done(); + + }); + }); + }); + }); + }); + } + + IIPRequestDeleteResource(callback, resourceId) { + var self = this; + Warehouse.get(resourceId).then(function(r) + { + if (r == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (r.instance.store.instance.applicable(session, ActionType.Delete, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.DeleteDenied); + return; + } + + if (Warehouse.remove(r)) + self.sendReply(IIPPacketAction.DeleteResource, callback).done(); + else + self.sendError(ErrorType.Management, callback, ExceptionCode.DeleteFailed); + }); + } + + IIPRequestTemplateFromClassName(callback, className) { + + var self = this; + + Warehouse.getTemplateByClassName(className).then(function (t) { + if (t != null) + self.sendReply(IIPPacketAction.TemplateFromClassName, callback) + .addUint32(t.content.length) + .addUint8Array(t.content) + .done(); + else { + // reply failed + self.sendError(ErrorType.Management, callback, ExceptionCode.TemplateNotFound); + } + }); + } + + IIPRequestTemplateFromClassId(callback, classId) { + var self = this; + Warehouse.getTemplateByClassId(classId).then(function (t) { + if (t != null) + self.sendReply(IIPPacketAction.TemplateFromClassId, callback) + .addUint32(t.content.length) + .addUint8Array(t.content) + .done(); + else { + // reply failed + self.sendError(ErrorType.Management, callback, ExceptionCode.TemplateNotFound); + } + }); + } + + IIPRequestTemplateFromResourceId(callback, resourceId) { + + var self = this; + + Warehouse.get(resourceId).then(function (r) { + if (r != null) + self.sendReply(IIPPacketAction.TemplateFromResourceId, callback) + .addUint32(r.instance.template.content.length) + .addUint8Array(r.instance.template.content) + .done(); + else { + // reply failed + self.sendError(ErrorType.Management, callback, ExceptionCode.TemplateNotFound); + } + }); + } + + IIPRequestInvokeFunction(callback, resourceId, index, content) { + + var self = this; + + Warehouse.get(resourceId).then(function (r) { + if (r != null) { + Codec.parseVarArray(content, 0, content.length, self).then(function (args) { + var ft = r.instance.template.getFunctionTemplateByIndex(index); + if (ft != null) { + if (r instanceof DistributedResource) { + var rt = r._invoke(index, args); + if (rt != null) { + rt.then(function (res) { + self.sendReply(IIPPacketAction.InvokeFunction, callback) + .addUint8Array(Codec.compose(res, self)) + .done(); + }); + } + else { + // function not found on a distributed object + } + } + else { + + var fi = r[ft.name]; + + if (r.instance.applicable(self.session, ActionType.Execute, ft) == Ruling.Denied) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.InvokeDenied); + return; + } + + if (fi instanceof Function) { + args.push(self); + + var rt = fi.apply(r, args); + + + if (rt instanceof AsyncReply) { + rt.then(function (res) { + self.sendReply(IIPPacketAction.InvokeFunction, callback) + .addUint8Array(Codec.compose(res, self)) + .done(); + }); + } + else { + self.sendReply(IIPPacketAction.InvokeFunction, callback) + .addUint8Array(Codec.compose(rt, self)) + .done(); + } + } + else { + // ft found, fi not found, this should never happen + } + } + } + else { + // no function at this index + } + }); + } + else { + // no resource with this id + } + }); + } + + IIPRequestGetProperty(callback, resourceId, index) { + + var self = this; + + Warehouse.get(resourceId).then(function (r) { + if (r != null) { + var pt = r.instance.template.getFunctionTemplateByIndex(index); + if (pt != null) { + if (r instanceof DistributedResource) { + self.sendReply(IIPPacketAction.GetProperty, callback) + .addUint8Array(Codec.compose(r._get(pt.index), self)) + .done(); + } + else { + var pv = r[pt.name]; + self.sendReply(IIPPacketAction.GetProperty) + .addUint8Array(Codec.compose(pv, self)) + .done(); + } + } + else { + // pt not found + } + } + else { + // resource not found + } + }); + } + + IIPRequestGetPropertyIfModifiedSince(callback, resourceId, index, age) { + + var self = this; + + Warehouse.get(resourceId).then(function (r) { + if (r != null) { + var pt = r.instance.template.getFunctionTemplateByIndex(index); + if (pt != null) { + if (r.instance.getAge(index) > age) { + var pv = r[pt.name]; + self.sendReply(IIPPacketAction.GetPropertyIfModified, callback) + .addUint8Array(Codec.compose(pv, self)) + .done(); + } + else + { + self.sendReply(IIPPacketAction.GetPropertyIfModified, callback) + .addUint8(DataType.NotModified) + .done(); + } + } + else { + // pt not found + } + } + else { + // resource not found + } + }); + } + + IIPRequestSetProperty(callback, resourceId, index, content) { + + var self = this; + + Warehouse.get(resourceId).then(function (r) { + if (r != null) { + + + var pt = r.instance.template.getPropertyTemplateByIndex(index); + if (pt != null) { + Codec.parse(content, 0, {}, this).then(function (value) { + if (r instanceof DistributedResource) { + // propagation + r._set(index, value).then(function (x) { + self.sendReply(IIPPacketAction.SetProperty, callback) + .done(); + }).error(function(x){ + self.sendError(x.type, callback, x.code, x.message) + .done(); + }); + } + else + { + if (r.instance.applicable(self.session, ActionType.SetProperty, pt) == Ruling.Denied) + { + self.sendError(AsyncReply.ErrorType.Exception, callback, ExceptionCode.SetPropertyDenied); + return; + } + + try + { + if (r[pt.name] instanceof DistributedPropertyContext) + value = new DistributedPropertyContext(this, value); + + r[pt.name] = value; + self.sendReply(IIPPacketAction.SetProperty, callback).done(); + } + catch(ex) + { + self.sendError(AsyncReply.ErrorType.Exception, callback, 0, ex.toString()).done(); + } + } + + }); + } + else { + // property not found + self.sendError(AsyncReply.ErrorType.Management, callback, ExceptionCode.PropertyNotFound).done(); + } + } + else { + // resource not found + self.sendError(AsyncReply.ErrorType.Management, callback, ExceptionCode.PropertyNotFound).done(); + } + }); + } + + IIPRequestInquireResourceHistory(callback, resourceId, fromDate, toDate) + { + var self = this; + Warehouse.get(resourceId).then(function(r) + { + if (r != null) + { + r.instance.store.getRecord(r, fromDate, toDate).then(function(results) + { + var history = Codec.composeHistory(results, self, true); + self.sendReply(IIPPacketAction.ResourceHistory, callback) + .addUint8Array(history) + .done(); + }); + } + }); + } + + IIPRequestQueryResources(callback, resourceLink) + { + var self = this; + + Warehouse.query(resourceLink).then(function(resources) + { + + var list = resources.filter(function(r){return r.instance.applicable(self.session, ActionType.Attach, null) != Ruling.Denied}); + + if (list.length == 0) + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + else + self.sendReply(IIPPacketAction.QueryLink, callback) + .addUint8Array(Codec.composeResourceArray(list, self, true)) + .done(); + }); + } + + create(store, parent, className, parameters, attributes, values) + { + var reply = new AsyncReply(); + var sb = DC.stringToBytes(className); + + var pkt = BL().addUint32(store.instance.id) + .addUint32(parent.instance.id) + .addUint32(sb.length) + .addUint8Array(sb) + .addUint8Array(Codec.composeVarArray(parameters, this, true)) + .addUint8Array(Codec.composeStructure(attributes, this, true, true, true)) + .addUint8Array(Codec.composeStructure(values, this)); + + pkt.addUint32(pkt.length, 8); + + this.sendRequest(IIPPacket.IIPPacketAction.CreateResource).addUint8Array(pkt.ToArray()).done().then(function(args) + { + var rid = args[0]; + + self.fetch(rid).then(function(r) + { + reply.trigger(r); + }); + + }); + + return reply; + } + + query(resourceLink) + { + var reply = new AsyncReply(); + var self = this; + + var sb = DC.stringToBytes(resourceLink); + + this.sendRequest(IIPPacketAction.QueryLink) + .addUint16(sb.length) + .addUint8Array(sb) + .done() + .then(function(args) + { + Codec.parseResourceArray(args[0], 0, args[0].length, self).then(function(resources) { + reply.trigger(resources); + }); + }).error(function(ex){ + reply.triggerError(ex); + }); + + return reply; + } + + getTemplate(classId) { + if (this.templates.contains(classId)) + return new AsyncReply(this.templates.item(classId)); + else if (this.templateRequests.contains(classId)) + return this.templateRequests.item(classId); + + var reply = new AsyncReply(); + this.templateRequests.add(classId.valueOf(), reply); + + var self = this; + + this.sendRequest(IIPPacketAction.TemplateFromClassId) + .addUint8Array(classId.value) + .done() + .then(function (rt) { + self.templateRequests.remove(classId); + self.templates.add(rt[0].classId.valueOf(), rt[0]); + Warehouse.putTemplate(rt[0]); + reply.trigger(rt[0]); + }); + + return reply; + } + +// IStore interface + get(path) { + + var rt = new AsyncReply(); + + this.query(path).then(function(ar) + { + if (ar != null && ar.length > 0) + rt.trigger(ar[0]); + else + rt.trigger(null); + }).error(function(ex) {rt.triggerError(ex);}); + + return rt; + + /* + if (this.pathRequests[path]) + return this.pathRequests[path]; + + var reply = new AsyncReply(); + this.pathRequests[path] = reply; + + var bl = new BinaryList(); + bl.addString(path); + bl.addUint16(bl.length, 0); + + var link = data.get + var self = this; + + this.sendRequest(IIPPacketAction.ResourceIdFromResourceLink) + .addUint16(.then(function (rt) { + delete self.pathRequests[path]; + + self.fetch(rt[1]).then(function (r) { + reply.trigger(r); + }); + }); + + + return reply; + */ + } + + retrieve(iid) { + for (var r in this.resources) + if (this.resources[r].instance.id == iid) + return new AsyncReply(r); + return new AsyncReply(null); + } + +// Get a resource from the other end + fetch(id) { + if (this.resourceRequests[id] && this.resources[id]) { + // dig for dead locks + // or not + return new AsyncReply(this.resources[id]); + //return this.resourceRequests[id]; + } + else if (this.resourceRequests[id]) + return this.resourceRequests[id]; + else if (this.resources[id]) + return new AsyncReply(this.resources[id]); + + var reply = new AsyncReply(); + + this.resourceRequests[id] = reply; + + var self = this; + + this.sendRequest(IIPPacketAction.AttachResource) + .addUint32(id) + .done() + .then(function (rt) { + var dr = new DistributedResource(self, id, rt[1], rt[2]); + //var dr = new DistributedResource(self, tmp, id, rt[1], rt[2]); + + self.getTemplate(rt[0]).then(function (tmp) { + + // ClassId, ResourceAge, ResourceLink, Content + Warehouse.put(dr, id.toString(), self, null, tmp); + + + Codec.parsePropertyValueArray(rt[3], 0, rt[3].length, self).then(function (ar) { + dr._attached(ar); + delete self.resourceRequests[id]; + reply.trigger(dr); + }); + }); + }); + + return reply; + } + + getRecord(resource, fromDate, toDate) + { + if (resource instanceof DistributedResource) + { + + if (resource._p.connection != this) + return new AsyncReply(null); + + var reply = new AsyncReply(); + + var self = this; + + this.sendRequest(IIPPacketAction.ResourceHistory) + .addUint32(resource._p.instanceId) + .addDateTime(fromDate).addDateTime(toDate) + .done() + .then(function(rt) + { + Codec.parseHistory(rt[0], 0, rt[0].length, resource, self).then(function(history) + { + reply.trigger(history); + }); + }); + + return reply; + } + else + return new AsyncReply(null); + } + + instance_resourceDestroyed(resource) { + // compose the packet + this.sendEvent(IIPPacketEvent.ResourceDestroyed) + .addUint32(resource.instance.id) + .done(); + } + + instance_propertyModified(resource, name, newValue) { + var pt = resource.instance.template.getPropertyTemplateByName(name); + + if (pt == null) + return; + + this.sendEvent(IIPPacketEvent.PropertyUpdated) + .addUint32(resource.instance.id) + .addUint8(pt.index) + .addUint8Array(Codec.compose(newValue, this)) + .done(); + } + + instance_eventOccurred(resource, issuer, receivers, name, args) { + var et = resource.instance.template.getEventTemplateByName(name); + + if (et == null) + return; + + if (receivers != null) + if (receivers.indexOf(this.session) < 0) + return; + + if (resource.instance.applicable(this.session, ActionType.ReceiveEvent, et, issuer) == Ruling.Denied) + return; + + // compose the packet + this.sendEvent(IIPPacketEvent.EventOccurred) + .addUint32(resource.instance.id) + .addUint8(et.index) + .addUint8Array(Codec.composeVarArray(args, this, true)) + .done(); + + } + + + + IIPRequestAddChild(callback, parentId, childId) + { + var self = this; + Warehouse.get(parentId).then(function(parent) + { + if (parent == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + Warehouse.get(childId).then(function(child) + { + if (child == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (parent.instance.applicable(self.session, ActionType.AddChild, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AddChildDenied); + return; + } + + if (child.instance.applicable(self.session, ActionType.AddParent, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AddParentDenied); + return; + } + + parent.instance.children.add(child); + + self.sendReply(IIPPacketAction.AddChild, callback) + .done(); + //child.Instance.Parents + }); + + }); + } + + IIPRequestRemoveChild(callback, parentId, childId) + { + var self = this; + + Warehouse.get(parentId).then(function(parent) + { + if (parent == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + Warehouse.get(childId).then(function(child) + { + if (child == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (parent.instance.applicable(self.session, ActionType.RemoveChild, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AddChildDenied); + return; + } + + if (child.instance.applicable(self.session, ActionType.RemoveParent, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AddParentDenied); + return; + } + + parent.instance.children.remove(child); + + self.sendReply(IIPPacketAction.RemoveChild, callback) + .done(); + //child.Instance.Parents + }); + + }); + } + + IIPRequestRenameResource(callback, resourceId, name) + { + var self = this; + Warehouse.get(resourceId).then(function(resource) + { + if (resource == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (resource.instance.applicable(self.session, ActionType.Rename, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.RenameDenied); + return; + } + + resource.instance.name = name.getString(0, name.length); + self.sendReply(IIPPacketAction.RenameResource, callback) + .done(); + }); + } + + IIPRequestResourceChildren(callback, resourceId) + { + var self = this; + Warehouse.get(resourceId).then(function(resource) + { + if (resource == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + self.sendReply(IIPPacketAction.ResourceChildren, callback) + .addUint8Array(Codec.composeResourceArray(resource.instance.children.toArray(), this, true)) + .done(); + + }); + } + + IIPRequestResourceParents(callback, resourceId) + { + var self = this; + + Warehouse.get(resourceId).then(function(resource) + { + if (resource == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + self.sendReply(IIPPacketAction.ResourceParents, callback) + .addUint8Array(Codec.composeResourceArray(resource.instance.parents.toArray(), this, true)) + .done(); + }); + } + + IIPRequestClearAttributes(callback, resourceId, attributes, all = false) + { + Warehouse.get(resourceId).then(function(r) + { + if (r == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (r.instance.store.instance.applicable(self.session, ActionType.UpdateAttributes, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.UpdateAttributeDenied); + return; + } + + var attrs = []; + + if (!all) + attrs = attributes.getStringArray(0, attributes.length); + + if (r.instance.removeAttributes(attrs)) + self.sendReply(all ? IIPPacketAction.ClearAllAttributes : IIPPacketAction.ClearAttributes, callback) + .done(); + else + self.sendError(AsyncReply.ErrorType.Management, callback, ExceptionCode.UpdateAttributeFailed); + + }); + } + + IIPRequestUpdateAttributes(callback, resourceId, attributes, clearAttributes = false) + { + var self = this; + + Warehouse.get(resourceId).then(function(r) + { + if (r == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (r.instance.store.instance.applicable(self.session, ActionType.UpdateAttributes, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.UpdateAttributeDenied); + return; + } + + Codec.parseStructure(attributes, 0, attributes.Length, this).then(function(attrs) { + if (r.instance.setAttributes(attrs, clearAttributes)) + self.sendReply(clearAttributes ? IIPPacketAction.ClearAllAttributes : IIPPacketAction.ClearAttributes, + callback) + .done(); + else + self.sendError(ErrorType.Management, callback, ExceptionCode.UpdateAttributeFailed); + }); + + }); + + } + + + + getChildren(resource) + { + if (resource._p.connection != this) + return new AsyncReply(null); + + var rt = new AsyncReply(); + var self = this; + + this.sendRequest(IIPPacketAction.ResourceChildren) + .addUint32(resource._p.instanceId) + .done() + .then(function(d) + { + + Codec.parseResourceArray(d, 0, d.length, self).then(function(resources) + { + rt.trigger(resources); + }).error(function(ex) { rt.triggerError(ex); }); + }); + + return rt; + } + + getParents(resource) + { + if (resource._p.connection != this) + return new AsyncReply(null); + + var rt = new AsyncReply(); + var self = this; + + this.sendRequest(IIPPacketAction.ResourceParents) + .addUint32(resource._p.instanceId) + .done() + .then(function(d) + { + Codec.parseResourceArray(d, 0, d.length, this).then(function(resources) + { + rt.trigger(resources); + }).error(function(ex) { rt.triggerError(ex);}); + }); + + return rt; + } + + removeAttributes(resource, attributes = null) + { + if (resource._p.connection != this) + return new AsyncReply(null); + + var rt = new AsyncReply(); + + if (attributes == null) + this.sendRequest(IIPPacketAction.ClearAllAttributes) + .addUint32(resource._p.instanceId) + .done() + .then(function(ar) + { + rt.trigger(true); + }).error(function(ex) { rt.triggerError(ex); }); + else + { + var attrs = DC.stringArrayToBytes(attributes); + this.sendRequest(IIPPacketAction.ClearAttributes) + .addUint32(resource.instance.id) + .addUint32(attrs.length) + .addUint8Array(attrs) + .done() + .then(function(ar) + { + rt.trigger(true); + }).error(function(ex) { rt.triggerChunk(ex); }); + } + + return rt; + } + + setAttributes(resource, attributes, clearAttributes = false) + { + if (resource._p.connection != this) + return new AsyncReply(null); + + var rt = new AsyncReply(); + + this.sendRequest(clearAttributes ? IIPPacketAction.UpdateAllAttributes : IIPPacketAction.UpdateAttributes) + .addUint32(resource._p.instanceId) + .addUint8Array(Codec.composeStructure(attributes, this, true, true, true)) + .done() + .then(function(ar) + { + rt.trigger(true); + }).error(function(ex) {rt.triggerError(ex);}); + + return rt; + } + + getAttributes(resource, attributes = null) + { + if (resource._p.connection != this) + return new AsyncReply(null); + + var rt = new AsyncReply(); + var self = this; + + if (attributes == null) + { + this.sendRequest(IIPPacketAction.GetAllAttributes) + .addUint32(resource._p.instanceId) + .done() + .then(function(ar) + { + Codec.parseStructure(ar[0], 0, ar[0].length, this).then(function(st) + { + for (var a in st) + resource.instance.attributes.set(a, st[a]); + rt.trigger(st); + }).error(function(ex) { rt.triggerError(ex); }); + }); + } + else + { + var attrs = DC.stringArrayToBytes(attributes); + this.sendRequest(IIPPacketAction.GetAttributes) + .addUint32(resource._p.instanceId) + .addUint32(attrs.length) + .addUint8Array(attrs) + .done() + .then(function(ar) + { + Codec.parseStructure(ar[0], 0, ar[0].length, self).then(function(st) + { + for (var a in st) + resource.instance.attributes.set(a, st[a]); + rt.trigger(st); + }).error(function(ex) { rt.triggerError(ex); }); + }); + } + + return rt; + } + +} + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 25/07/2017. + */ + +"use strict"; + +class DistributedResource extends IResource +{ + destroy() + { + this.destroyed = true; + this._emit("destroy", this); + } + + constructor(connection, instanceId, age, link) + { + super(); + + this._p = { + isAttached: false, + connection: connection, + instanceId: instanceId, + age: age, + link: link, + properties: [] + }; + } + + _serialize() + { + var props = []; + + for (var i = 0; i < this._p.properties.length; i++) + props.push(new PropertyValue(this._p.properties[i], + this.instance.getAge(i), + this.instance.getModificationDate(i))); + + return props; + } + + _attached(properties) + { + + if (this._isAttached) + return false; + else + { + for(var i = 0; i < properties.length; i++) + { + this.instance.setAge(i, properties[i].age); + this.instance.setModificationDate(i, properties[i].date); + this._p.properties.push(properties[i].value); + } + + + this._p.isAttached = true; + + var self = this; + + var makeFunc = function(index) + { + return function () { + return self._invoke(index, arguments); + }; + }; + + var makeGetter = function(index) + { + return function () { + return self._get(index); + }; + }; + + var makeSetter = function(index) + { + return function (value) { + self._set(index, value); + }; + }; + + for(var i = 0; i < this.instance.template.functions.length; i++) + { + var ft = this.instance.template.functions[i]; + this[ft.name] = makeFunc(ft.index); + } + + for(var i = 0; i < this.instance.template.properties.length; i++) + { + var pt = this.instance.template.properties[i]; + + Object.defineProperty(this, pt.name, { + get: makeGetter(pt.index), + set: makeSetter(pt.index), + enumerable: true, + configurable: true + }); + + } + } + return true; + } + + _emitEventByIndex(index, args) + { + var et = this.instance.template.getEventTemplateByIndex(index); + this._emitArgs(et.name, args); + this.instance._emitResourceEvent(null, null, et.name, args); + } + + _invoke(index, args) { + if (this.destroyed) + throw new Exception("Trying to access destroyed object"); + + if (index >= this.instance.template.functions.length) + throw new Exception("Function index is incorrect"); + + return this._p.connection.sendInvoke(this._p.instanceId, index, args); + + /* + var reply = new AsyncReply(); + + var parameters = Codec.composeVarArray(args, this._p.connection, true); + + var self = this; + + this._p.connection.sendRequest(IIPPacketAction.InvokeFunction, + BL().addUint32(self._p.instanceId).addUint8(index).addUint8Array(parameters)) + .then(function (res) { + Codec.parse(res[0], 0, self._p.connection).then(function (rt) { + reply.trigger(rt); + }); + }); + + + return reply; + */ + } + + + _get(index) + { + if (index >= this._p.properties.length) + return null; + return this._p.properties[index]; + } + + + _updatePropertyByIndex(index, value) + { + var pt = this.instance.template.getPropertyTemplateByIndex(index); + this._p.properties[index] = value; + this.instance.emitModification(pt, value); + } + + _set(index, value) + { + if (index >= this._p.properties.length) + return null; + + var reply = new AsyncReply(); + + var parameters = Codec.compose(value, this._p.connection); + var self = this; + + this._p.connection.sendRequest(IIPPacketAction.SetProperty) + .addUint32(self._p.instanceId).addUint8(index).addUint8Array(parameters) + .done() + .then(function(res) + { + // not really needed, server will always send property modified, this only happens if the programmer forgot to emit in property setter + self._p.properties[index] = value; + reply.trigger(null); + }); + + return reply; + } +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 25/07/2017. + */ + +"use strict"; + +const DistributedResourceQueueItemType = + { + Propery: 0, + Event: 1 + }; + +class DistributedResourceQueueItem { + constructor(resource, type, value, index) { + this.resource = resource; + this.index = index; + this.type = type; + this.value = value; + } +} + + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 24/08/2017. + */ + +"use strict"; + +class EventTemplate extends MemberTemplate +{ + + constructor() + { + super(); + this.type = MemberType.Event; + } + + compose() + { + var rt = new BinaryList(); + + var name = super.compose(); + if (this.expansion != null) { + var exp = DC.stringToBytes(this.expansion); + return rt.addUint8(0x50).addUint32(exp.length).addUint8Array(exp).addUint8(name.length).addUint8Array(name).toArray(); + } + else + return rt.addUint8(0x40).addUint32(name.length).addUint8Array(name).toArray(); + } + +} + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 27/08/2017. + */ + +"use strict"; + +class FunctionTemplate extends MemberTemplate { + compose() { + var name = super.compose(); + var rt = new BinaryList(); + + if (this.expansion != null) { + var exp = DC.stringToBytes(this.expansion); + + return rt.addUint8(0x10 | (this.isVoid ? 0x8 : 0x0)) + .addUint32(exp.length).addUint8Array(exp) + .addUint8(name.length).addUint8Array(name).toArray(); + } + else + return rt.addUint8(this.isVoid ? 0x8 : 0x0).addUint8(name.length).addUint8Array(name).toArray(); + } + + + constructor() { + super(); + this.type = MemberType.Function; + } +} + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 02/09/2017. + */ + +"use strict"; + +class Guid +{ + constructor(dc) + { + this.value = dc; + } + + valueOf() + { + return this.value.getHex(0, 16); + } +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 25/07/2017. + */ + +"use strict"; + +const IIPAuthPacketCommand = +{ + Action: 0, + Declare: 1, + Acknowledge: 2, + Error: 3 +}; + +const IIPAuthPacketAction = +{ + // Authenticate + AuthenticateHash: 0, + NewConnection: 0x20, + ResumeConnection: 0x21, + ConnectionEstablished: 0x28 +}; + + +const IIPAuthPacketMethod = +{ + None: 0, + Certificate: 1, + Credentials: 2, + Token: 3 +}; + +class IIPAuthPacket +{ + constructor() + { + this.command = 0; + this.action = 0; + this.errorCode = 0; + this.errorMessage = ""; + this.localMethod = 0; + this.sourceInfo = ""; + this.hash = ""; + this.sessionId = ""; + this.remoteMethod = 0; + this.domain = ""; + this.CertificateId = 0; + this.localUsername = ""; + this.remoteUsername = ""; + this.localPassword = ""; + this.remotePassword = ""; + this.localToken = []; + this.reemoteToken = []; + this.asymetricEncryptionKey = []; + this.localNonce = []; + this.remoteNonce = []; + this.dataLengthNeeded = 0; + } + + notEnough(offset, ends, needed) + { + if (offset + needed > ends) + { + this.dataLengthNeeded = needed - (ends - offset); + return true; + } + else + return false; + } + + parse(data, offset, ends) + { + var oOffset = offset; + + if (this.notEnough(offset, ends, 1)) + return -this.dataLengthNeeded; + + this.command = data.getUint8(offset) >> 6; + + if (this.command == IIPAuthPacketCommand.Action) + { + this.action = data[offset++] & 0x3f; + + if (this.action == IIPAuthPacketAction.AuthenticateHash) + { + if (this.notEnough(offset, ends, 32)) + return -this.dataLengthNeeded; + + this.hash = data.getUint8Array(offset, 32); + + offset += 32; + } + else if (this.action == IIPAuthPacketAction.NewConnection) + { + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; + + var length = data.getUint16(offset); + + offset += 2; + + if (this.notEnough(offset, ends, length)) + return -this.dataLengthNeeded; + + this.sourceInfo = data.clip(offset, length); + + offset += 32; + } + else if (this.action == IIPAuthPacketAction.ResumeConnection + || this.action == IIPAuthPacketAction.ConnectionEstablished) + { + if (this.notEnough(offset, ends, 32)) + return -this.dataLengthNeeded; + + this.sessionId = data.clip(offset, 32); + + offset += 32; + } + } + else if (this.command == IIPAuthPacketCommand.Declare) + { + this.remoteMethod = ((data.getUint8(offset) >> 4) & 0x3); + this.localMethod = ((data.getUint8(offset) >> 2) & 0x3); + + var encrypt = ((data.getUint8(offset++) & 0x2) == 0x2); + + + if (this.notEnough(offset, ends, 1)) + return -this.dataLengthNeeded; + + var domainLength = data.getUint8(offset++); + if (this.notEnough(offset, ends, domainLength)) + return -this.dataLengthNeeded; + + this.domain = data.getString(offset, domainLength); + + offset += domainLength; + + + if (this.remoteMethod == IIPAuthPacketMethod.Credentials) + { + if (this.localMethod == IIPAuthPacketMethod.None) + { + if (this.notEnough(offset, ends, 33)) + return -this.dataLengthNeeded; + + this.remoteNonce = data.clip(offset, 32); + + offset += 32; + + var length = data.getUint8(offset++); + + if (this.notEnough(offset, ends, length)) + return -this.dataLengthNeeded; + + this.remoteUsername = data.getString(offset, length); + + + offset += length; + } + } + + if (encrypt) + { + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; + + var keyLength = data.getUint16(offset); + + offset += 2; + + if (this.notEnough(offset, ends, keyLength)) + return -this.dataLengthNeeded; + + this.asymetricEncryptionKey = data.clip(offset, keyLength); + + offset += keyLength; + } + } + else if (this.command == IIPAuthPacketCommand.Acknowledge) + { + this.remoteMethod = (data.getUint8(offset) >> 4) & 0x3; + this.localMethod = (data.getUint8(offset) >> 2) & 0x3; + var encrypt = ((data.getUint8(offset++) & 0x2) == 0x2); + + if (this.notEnough(offset, ends, 1)) + return -this.dataLengthNeeded; + + + if (this.remoteMethod == IIPAuthPacketMethod.Credentials) + { + if (this.localMethod == IIPAuthPacketMethod.None) + { + if (this.notEnough(offset, ends, 32)) + return -this.dataLengthNeeded; + + this.remoteNonce = data.clip(offset, 32); + + offset += 32; + + } + } + + if (encrypt) + { + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; + + var keyLength = data.getUint16(offset); + + offset += 2; + + if (this.notEnough(offset, ends, keyLength)) + return -this.dataLengthNeeded; + + this.asymetricEncryptionKey = data.clip(offset, keyLength); + + offset += keyLength; + } + } + else if (this.command == IIPAuthPacketCommand.Error) + { + if (this.notEnough(offset, ends, 5)) + return -this.dataLengthNeeded; + + offset++; + this.errorCode = data.getUint8(offset++); + + + var cl = data.getUint16(offset); + offset += 2; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.errorMessage = data.getString(offset, cl); + offset += cl; + + } + + + return offset - oOffset; + + } +} + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 25/07/2017. + */ + +"use strict"; + +const IIPPacketCommand = +{ + Event: 0, + Request: 1, + Reply: 2, + Report: 3 +}; + +const IIPPacketReport = +{ + ManagementError: 0, + ExecutionError: 1, + ProgressReport: 0x8, + ChunkStream: 0x9 +}; + +const IIPPacketEvent = +{ + // Event Manage + ResourceReassigned : 0, + ResourceDestroyed: 1, + ChildAdded: 2, + ChildRemoved: 3, + Renamed: 4, + + // Event Invoke + PropertyUpdated : 0x10, + EventOccurred: 0x11, + + // Attribute + AttributesUpdated: 0x18 + +}; + +const IIPPacketAction = +{ + // Request Manage + AttachResource: 0, + ReattachResource: 1, + DetachResource: 2, + CreateResource: 3, + DeleteResource: 4, + AddChild: 5, + RemoveChild: 6, + RenameResource: 7, + + // Request Inquire + TemplateFromClassName: 8, + TemplateFromClassId: 9, + TemplateFromResourceId: 10, + QueryLink: 11, + ResourceHistory: 12, + ResourceChildren: 13, + ResourceParents: 14, + + // Request Invoke + InvokeFunction: 16, + GetProperty: 17, + GetPropertyIfModified: 18, + SetProperty: 19, + + // Request Attribute + GetAllAttributes: 24, + UpdateAllAttributes: 25, + ClearAllAttributes: 26, + GetAttributes: 27, + UpdateAttributes: 28, + ClearAttributes: 29 +}; + + +class IIPPacket +{ + constructor() + { + this.command = 0; + this.action = 0; + this.event = 0; + this.resourceId = 0; + this.newResourceId = 0; + this.resourceAge = 0; + this.content = []; + this.errorCode = 0; + this.errorMessage = ""; + this.className = ""; + this.resourceLink = ""; + this.classId = ""; + this.methodIndex = ""; + this.methodName = ""; + this.callbackId = 0; + this.dataLengthNeeded = 0; + this.originalOffset = 0; + } + + notEnough(offset, ends, needed) + { + if (offset + needed > ends) + { + this.dataLengthNeeded = needed - (ends - this.originalOffset); + return true; + } + else + return false; + } + + parse(data, offset, ends) + { + this.originalOffset = offset; + + if (this.notEnough(offset, ends, 1)) + return -this.dataLengthNeeded; + + this.command = (data.getUint8(offset) >> 6); + + if (this.command == IIPPacketCommand.Event) + { + this.event = (data.getUint8(offset++) & 0x3f); + + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + } + else if (this.command == IIPPacketCommand.Report) + { + this.report = (data.getUint8(offset++) & 0x3f); + + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + this.callbackId = data.getUint32(offset); + offset += 4; + } + else + { + this.action = (data.getUint8(offset++) & 0x3f); + + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + this.callbackId = data.getUint32(offset); + offset += 4; + } + + if (this.command == IIPPacketCommand.Event) + { + if (this.event == IIPPacketEvent.ResourceReassigned) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + this.newResourceId = data.getUint32( offset); + offset += 4; + + } + else if (this.event == IIPPacketEvent.ResourceDestroyed) + { + // nothing to parse + } + else if (this.event == IIPPacketEvent.ChildAdded + || this.event == IIPPacketEvent.ChildRemoved) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + this.childId = data.getUint32(offset); + offset += 4; + } + else if(this.event == IIPPacketEvent.Renamed) + { + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; + + var cl = data.getUint16(offset); + offset += 2; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset, cl); + + offset += cl; + } + else if (this.event == IIPPacketEvent.PropertyUpdated) + { + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; + + this.methodIndex = data[offset++]; + + var dt = data.getUint8(offset++); + var size = DataType.sizeOf(dt); + + if (size < 0) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + var cl = data.getUint32(offset); + offset += 4; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset - 5, cl + 5); + offset += cl; + } + else + { + if (this.notEnough(offset, ends, size)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset - 1, size + 1); + offset += size; + } + } + else if (this.event == IIPPacketEvent.EventOccurred) + { + if (this.notEnough(offset, ends, 5)) + return -this.dataLengthNeeded; + + this.methodIndex = data.getUint8(offset++); + + var cl = data.getUint32(offset); + offset += 4; + + this.content = data.clip(offset, cl); + offset += cl; + } + // Attribute + else if (this.event == IIPPacketEvent.AttributesUpdated) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + var cl = data.getUint32(offset); + offset += 4; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset, cl); + + offset += cl; + } + } + else if (this.command == IIPPacketCommand.Request) + { + if (this.action == IIPPacketAction.AttachResource) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + } + else if (this.action == IIPPacketAction.ReattachResource) + { + if (this.notEnough(offset, ends, 12)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + this.resourceAge = data.getUint64(offset); + offset += 8; + } + else if (this.action == IIPPacketAction.DetachResource) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + } + else if (this.action == IIPPacketAction.CreateResource) + { + if (this.notEnough(offset, ends, 12)) + return -dataLengthNeeded; + + this.storeId = data.getUint32(offset); + offset += 4; + this.resourceId = data.getUint32(offset); + offset += 4; + + var cl = data.getUint32(offset); + offset += 4; + + if (this.notEnough(offset, ends, cl)) + return -dataLengthNeeded; + + this.content = data.clip(offset, cl); + } + else if (this.action == IIPPacketAction.DeleteResource) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + } + else if (this.action == IIPPacketAction.AddChild + || this.action == IIPPacketAction.RemoveChild) + { + if (this.notEnough(offset, ends, 8)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + this.childId = data.getUint32(offset); + offset += 4; + + } + else if (this.action == IIPPacketAction.RenameResource) + { + if (this.notEnough(offset, ends, 6)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + var cl = data.getUint16(offset); + offset += 2; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset, cl); + offset += cl; + + } + else if (this.action == IIPPacketAction.TemplateFromClassName) + { + if (this.notEnough(offset, ends, 1)) + return -this.dataLengthNeeded; + + var cl = data.getUint8(offset++); + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.className = data.getString(offset, cl); + offset += cl; + + } + else if (this.action == IIPPacketAction.TemplateFromClassId) + { + if (this.notEnough(offset, ends, 16)) + return -this.dataLengthNeeded; + + this.classId = data.getGuid(offset); + offset += 16; + } + else if (this.action == IIPPacketAction.TemplateFromResourceId) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + } + else if (this.action == IIPPacketAction.QueryLink) + { + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; + + var cl = data.getUint16(offset); + offset += 2; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.resourceLink = data.getString(offset, cl); + offset += cl; + } + else if (this.action == IIPPacketAction.ResourceChildren + || this.action == IIPPacketAction.ResourceParents) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + } + else if (this.action == IIPPacketAction.ResourceHistory) + { + if (this.notEnough(offset, ends, 20)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + this.fromDate = data.getDateTime(offset); + offset += 8; + + this.toDate = data.getDateTime(offset); + offset += 8; + + } + else if (this.action == IIPPacketAction.InvokeFunction) + { + if (this.notEnough(offset, ends, 9)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + this.methodIndex = data.getUint8(offset++); + + var cl = data.getUint32(offset); + offset += 4; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset, cl); + offset += cl; + + } + else if (this.action == IIPPacketAction.GetProperty) + { + if (this.notEnough(offset, ends, 5)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + this.methodIndex = data.getUint8(offset++); + + } + else if (this.action == IIPPacketAction.GetPropertyIfModified) + { + if (this.notEnough(offset, ends, 9)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + this.methodIndex = data[offset++]; + + this.resourceAge = data.getUint64(offset); + offset += 8; + + } + else if (this.action == IIPPacketAction.SetProperty) + { + if (this.notEnough(offset, ends, 6)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + this.methodIndex = data[offset++]; + + + var dt = data.getUint8(offset++); + var size = DataType.sizeOf(dt); + + if (size < 0) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + var cl = data.getUint32(offset); + offset += 4; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset-5, cl + 5); + offset += cl; + } + else + { + if (this.notEnough(offset, ends, size)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset-1, size + 1); + offset += size; + } + } + + // Attribute + else if (this.action == IIPPacketAction.UpdateAllAttributes + || this.action == IIPPacketAction.GetAttributes + || this.action == IIPPacketAction.UpdateAttributes + || this.action == IIPPacketAction.ClearAttributes) + { + if (this.notEnough(offset, ends, 8)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + var cl = data.getUint32(offset); + offset += 4; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset, cl); + offset += cl; + } + + } + else if (this.command == IIPPacketCommand.Reply) + { + if (this.action == IIPPacketAction.AttachResource + || this.action == IIPPacketAction.ReattachResource) + { + if (this.notEnough(offset, ends, 26)) + return -this.dataLengthNeeded; + + this.classId = data.getGuid(offset); + offset += 16; + + this.resourceAge = data.getUint64(offset); + offset += 8; + + var cl = data.getUint16(offset); + offset+=2; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.resourceLink = data.getString(offset, cl); + offset += cl; + + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + cl = data.getUint32(offset); + offset += 4; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset, cl); + offset += cl; + } + else if (this.action == IIPPacketAction.DetachResource) + { + // nothing to do + } + else if (this.action == IIPPacketAction.CreateResource) + { + if (this.notEnough(offset, ends, 20)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + } + else if (this.action == IIPPacketAction.DetachResource) + { + // nothing to do + } + else if (this.action == IIPPacketAction.TemplateFromClassName + || this.action == IIPPacketAction.TemplateFromClassId + || this.action == IIPPacketAction.TemplateFromResourceId + || this.action == IIPPacketAction.QueryLink + || this.action == IIPPacketAction.ResourceChildren + || this.action == IIPPacketAction.ResourceParents + || this.action == IIPPacketAction.ResourceHistory + // Attribute + || this.action == IIPPacketAction.GetAllAttributes + || this.action == IIPPacketAction.GetAttributes) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + var cl = data.getUint32(offset); + offset += 4; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset, cl); + offset += cl; + } + else if (this.action == IIPPacketAction.InvokeFunction + || this.action == IIPPacketAction.GetProperty + || this.action == IIPPacketAction.GetPropertyIfModified) + { + if (this.notEnough(offset, ends, 1)) + return -this.dataLengthNeeded; + + var dt = data.getUint8(offset++); + var size = DataType.sizeOf(dt); + + if (size < 0) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + var cl = data.getUint32(offset); + offset += 4; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset - 5, cl + 5); + offset += cl; + } + else + { + if (this.notEnough(offset, ends, size)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset - 1, size + 1); + offset += size; + } + } + else if (this.action == IIPPacketAction.SetProperty) + { + // nothing to do + } + } + else if (this.command == IIPPacketCommand.Report) + { + if (this.report == IIPPacketReport.ManagementError) + { + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; + + this.errorCode = data.getUint16(offset); + offset += 2; + } + else if (this.report == IIPPacketReport.ExecutionError) + { + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; + + this.errorCode = data.getUint16(offset); + offset += 2; + + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; + + var cl = data.getUint16(offset); + offset += 2; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.errorMessage = data.getString(offset, cl); + offset += cl; + } + else if (this.report == IIPPacketReport.ProgressReport) + { + if (this.notEnough(offset, ends, 8)) + return -this.dataLengthNeeded; + + this.progressValue = data.getInt32(offset); + offset += 4; + this.progressMax = data.getInt32(offset); + offset += 4; + } + else if (this.report == IIPPacketReport.ChunkStream) + { + var dt = data.getUint8(offset++); + var size = DataType.sizeOf(dt); + + if (size < 0) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + var cl = data.getUint32(offset); + offset += 4; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset - 5, cl + 5); + offset += cl; + } + else + { + if (this.notEnough(offset, ends, size)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset - 1, size + 1); + offset += size; + } + } + } + + return offset - this.originalOffset; + } +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 29/08/2017. + */ + +"use strict"; + +class Instance extends IEventHandler +{ + + getAge(index) + { + if (index < this.ages.length) + return this.ages[index]; + else + return 0; + } + + setAge(index, value) + { + if (index < this.ages.length) + { + this.ages[index] = value; + if (value > this.instanceAge) + this.instanceAge = value; + } + } + + getModificationDate(index) + { + if (index < this.modificationDates.length) + return this.modificationDates[index]; + else + return new Date(0); + } + + setModificationDate(index, value) + { + if (index < this.modificationDates.length) + { + this.modificationDates[index] = value; + + if (value > this.instanceModificationDate) + this.instanceModificationDate = value; + } + } + + loadProperty(name, age, modificationDate, value) + { + var pt = this.template.getPropertyTemplateByName(name); + + if (pt == null) + return false; + + this.resource[name] = value; + + this.setAge(pt.index, age); + this.setModificationDate(pt.index, modificationDate); + + return true; + } + + deserialize(properties) + { + + for (var i = 0; i < properties.length; i++) + { + var pt = this.template.GetPropertyTemplateByIndex(i); + if (pt != null) + { + var pv = properties[i]; + this.loadProperty(pt.name, pv.age, pv.date, pv.value); + } + } + + return true; + } + + serialize() + { + var props = []; + + for (var i = 0; i < this.template.properties.length; i++) + props.push(new PropertyValue(this.resource[this.template.properties[i].name], + this.ages[this.template.properties[i].index], + this.modificationDates[this.template.properties[i].index])); + + return props; + } + + isStorable() + { + return resource instanceof Storable; + } + + emitModification(pt, value) + { + this.instanceAge++; + + var now = new Date(); + + this.ages[pt.index] = this.instanceAge; + this.modificationDates[pt.index] = now; + + if (pt.recordable) + this.store.record(this.resource, pt.name, value, this.ages[pt.index], now); + + super._emit("ResourceModified", this.resource, pt.name, value); + this.resource._emit("modified", pt.name, value); + } + + modified(propertyName = null) + { + if (propertyName == null) + propertyName = modified.caller.name; + + var val = {}; + if (this.getPropertyValue(propertyName, val)) + { + var pt = this.template.getPropertyTemplateByName(propertyName); + this.emitModification(pt, val.value) + } + } + + _emitResourceEvent(issuer, receivers, name, args) + { + super._emit("ResourceEventOccurred", this.resource, issuer, receivers, name, args); + } + + getPropertyValue(name, resultObject) + { + for (var i = 0; i < this.template.properties.length; i++) + if (this.template.properties[i].name == name) + { + resultObject.value = this.resource[name]; + return true; + } + + return false; + } + + + + constructor(id, name, resource, store, customTemplate = null, age = 0) + { + super(); + + this.store = store; + this.resource = resource; + this.id = id; + this.name = name; + + this.instanceAge = age; + this.instanceModificationDate = new Date(0); + + this.children = new AutoList(); + this.parents = new AutoList(); + this.managers = new AutoList(); + + this.attributes = new KeyList(); + + var self = this; + + this.children.on("add", function(value){ + value.instance.parents.add(self.resource); + }); + + this.children.on("remove", function(value){ + value.instance.parents.remove(self.resource); + }); + + + this.resource.on("Destroy", function(sender){ + self._emit("ResourceDestroyed", sender); + }); + + if (customTemplate != null) + this.template = customTemplate; + else + this.template = Warehouse.getTemplateByType(this.resource.constructor); + + // set ages + this.ages = []; + this.modificationDates = []; + + for(var i = 0; i < this.template.properties.length; i++) + { + this.ages.push(0); + this.modificationDates.push(new Date(0)); + } + + // connect events + for (var i = 0; i < this.template.events.length; i++) + this.resource.on(this.template.events[i].name, this._makeHandler(this.template.events[i].name)); + + } + + _makeHandler(name) + { + var self = this; + return function(args) + { + if (args instanceof CustomResourceEvent) + self._emitResourceEvent(args.issuer, args.receivers, name, args.params); + else + self._emitResourceEvent(null, null, name, args); + }; + } + + + /// + /// Check for permission. + /// + /// Caller sessions. + /// Action type + /// Function or property to check for permission. + /// Ruling. + applicable(session, action, member, inquirer) + { + for (var i = 0; i < this.managers.length; i++) + { + var r = this.managers.item(i).applicable(this.resource, session, action, member, inquirer); + if (r != Ruling.DontCare) + return r; + } + + return Ruling.DontCare; + } + + + + removeAttributes(attributes = null) + { + if (attributes == null) + this.attributes.clear(); + else + { + for(var i = 0; i < attributes.length; i++) + this.attributes.remove(attributes[i]); + } + + return true; + } + + getAttributes(attributes = null) + { + var st = new Structure(); + + if (attributes == null) + { + attributes = this.attributes.keys.slice(0); + attributes.push("managers"); + } + + for(var i = 0; i < attributes.length; i++) + { + var attr = attributes[i]; + + if (attr == "name") + st["name"] = this.name; + + else if (attr == "managers") + { + var mngrs = new StructureArray(); + + for(var j = 0; j < this.managers.length; j++) + { + var manager = this.managers.item(j); + var sm = new Structure(); + sm["type"] = manager.constructor.name; + sm["settings"] = manager.settings; + + mngrs.push(sm); + } + + st["managers"] = mngrs; + + } + else + st[attr] = this.attributes.item(attr); + } + + return st; + } + + + setAttributes(attributes, clearAttributes = false) + { + + if (clearAttributes) + this.attributes.clear(); + + + for (var attr in attributes) + if (attr == "name") + this.name = attributes[attr]; + else if (attr == "managers") + { + this.managers.clear(); + + var mngrs = attributes[attr]; + + for (var i = 0; i < mngrs.length; i++) + { + var mngr = mngrs[i]; + + var type = window[mngr]; + + var settings = mngr["settings"]; + + var manager = new (Function.prototype.bind.apply(type)); + + if (manager instanceof IPermissionsManager) + { + manager.initialize(settings, this.resource); + this.managers.add(manager); + } + else + return false; + } + } + else + { + this.attributes.set(attr, attributes[attr]); + } + + + return true; + } +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 26/08/2017. + */ + +"use strict"; + +class NotModified +{ + +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 27/08/2017. + */ + +"use strict"; + +const PropertyPermission = { + Read: 1, + Write: 2, + ReadWrite: 3 +}; + +class PropertyTemplate extends MemberTemplate +{ + + constructor() + { + super(); + this.type = MemberType.Property; + } + + compose() + { + var name = super.compose(); + var rt = new BinaryList(); + var pv = (this.permission >> 1) | (this.recordable ? 1 : 0); + + if (this.writeExpansion != null && this.readExpansion != null) + { + var rexp = DC.stringToBytes(this.readExpansion); + var wexp = DC.stringToBytes(this.writeExpansion); + return rt.addUint8(0x38 | pv) + .addUint32(wexp.length) + .addUint8Array(wexp) + .addUint32(rexp.length) + .addUint8Array(rexp) + .addUint8(name.length) + .addUint8Array(name).toArray(); + } + else if (this.writeExpansion != null) + { + var wexp = DC.stringToBytes(this.writeExpansion); + return rt.addUint8(0x30 | pv) + .addUint32(wexp.length) + .addUint8Array(wexp) + .addUint8(name.length) + .addUint8Array(name).toArray(); + } + else if (this.readExpansion != null) + { + var rexp = DC.stringToBytes(this.readExpansion); + return rt.addUint8(0x28 | pv) + .addUint32(rexp.length) + .addUint8Array(rexp) + .addUint8(name.length) + .addUint8Array(name).toArray(); + } + else + return rt.addUint8(0x20 | pv) + .addUint32(name.length) + .addUint8Array(name).toArray(); + } +} + +/* +* 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. +*/ + +"use strict"; + +class ResourceTemplate { + + getEventTemplateByName(eventName) { + for (var i = 0; i < this.events.length; i++) + if (this.events[i].name == eventName) + return this.events[i]; + return null; + } + + getEventTemplateByIndex(index) { + for (var i = 0; i < this.events.length; i++) + if (this.events[i].index == index) + return this.events[i]; + return null; + } + + getFunctionTemplateByName(functionName) { + for (var i = 0; i < this.functions.length; i++) + if (this.functions[i].name == functionName) + return this.functions[i]; + return null; + } + + getFunctionTemplateByIndex(index) { + for (var i = 0; i < this.functions.length; i++) + if (this.functions[i].index == index) + return this.functions[i]; + return null; + } + + getPropertyTemplateByName(propertyName) { + for (var i = 0; i < this.properties.length; i++) + if (this.properties[i].name == propertyName) + return this.properties[i]; + return null; + } + + getPropertyTemplateByIndex(index) { + for (var i = 0; i < this.properties.length; i++) + if (this.properties[i].index == index) + return this.properties[i]; + return null; + } + + + /* + + template: { + properties: [ + {name: 'size', read: null, write: null} + ], + functions: [ + + ], + events: [ + + ] + } + */ + + constructor(type) { + + + this.properties = []; + this.events = []; + this.functions = []; + this.members = []; + + if (type === undefined) + return; + + var template = type.getTemplate(); + + // set guid + this.className = template.namespace + "." + type.prototype.constructor.name; + + this.classId = SHA256.compute(DC.stringToBytes(this.className)).getGuid(0); + + //byte currentIndex = 0; + + for (var i = 0; i < template.properties.length; i++) { + var pt = new PropertyTemplate(); + pt.name = template.properties[i].name; + pt.index = i; + pt.readExpansion = template.properties[i].read; + pt.writeExpansion = template.properties[i].write; + pt.recordable = template.properties[i].recordable; + this.properties.push(pt); + } + + for (var i = 0; i < template.events.length; i++) { + var et = new EventTemplate(); + et.name = template.events[i].name; + et.index = i; + et.expansion = template.events[i].expansion; + this.events.push(et); + } + + for (var i = 0; i < template.functions.length; i++) { + var ft = new FunctionTemplate(); + ft.name = template.functions[i].name; + ft.index = i; + ft.isVoid = template.functions[i].void; + ft.expansion = template.functions[i].expansion; + this.functions.push(ft); + } + + + // append signals + for (var i = 0; i < this.events.length; i++) + this.members.push(this.events[i]); + // append slots + for (var i = 0; i < this.functions.length; i++) + this.members.push(this.functions[i]); + // append properties + for (var i = 0; i < this.properties.length; i++) + this.members.push(this.properties[i]); + + // bake it binarily + var b = new BinaryList(); + var cls = DC.stringToBytes(this.className); + b.addUint8Array(this.classId.value) + .addUint8(cls.length).addUint8Array(cls).addUint32(template.version).addUint16(this.members.length); + + for (var i = 0; i < this.functions.length; i++) + b.addUint8Array(this.functions[i].compose()); + + for (var i = 0; i < this.properties.length; i++) + b.addUint8Array(this.properties[i].compose()); + + for (var i = 0; i < this.events.length; i++) + b.addUint8Array(this.events[i].compose()); + + this.content = b.toArray(); + } + + static parse(data, offset = 0, contentLength = -1) { + + if (contentLength == -1) + contentLength = data.length; + + var ends = offset + contentLength; + + var oOffset = offset; + + // start parsing... + + var od = new ResourceTemplate(); + od.content = data.clip(offset, contentLength); + + od.classId = data.getGuid(offset); + offset += 16; + od.className = data.getString(offset + 1, data.getUint8(offset)); + offset += data.getUint8(offset) + 1; + + od.version = data.getInt32(offset); + offset += 4; + + var methodsCount = data.getUint16(offset); + offset += 2; + + var functionIndex = 0; + var propertyIndex = 0; + var eventIndex = 0; + + for (var i = 0; i < methodsCount; i++) { + var type = data.getUint8(offset) >> 5; + + if (type == 0) // function + { + var ft = new FunctionTemplate(); + ft.index = functionIndex++; + var expansion = ((data.getUint8(offset) & 0x10) == 0x10); + ft.isVoid = ((data.getUint8(offset++) & 0x08) == 0x08); + ft.name = data.getString(offset + 1, data.getUint8(offset));// Encoding.ASCII.getString(data, (int)offset + 1, data.getUint8(offset)); + offset += data.getUint8(offset) + 1; + + if (expansion) // expansion ? + { + var cs = data.getUint32(offset); + offset += 4; + ft.expansion = data.getString(offset, cs); + offset += cs; + } + + od.functions.push(ft); + } + else if (type == 1) // property + { + + var pt = new PropertyTemplate(); + pt.index = propertyIndex++; + var readExpansion = ((data.getUint8(offset) & 0x8) == 0x8); + var writeExpansion = ((data.getUint8(offset) & 0x10) == 0x10); + pt.recordable = ((data.getUint8(offset) & 1) == 1); + pt.permission = ((data.getUint8(offset++) >> 1) & 0x3); + pt.name = data.getString(offset + 1, data.getUint8(offset));// Encoding.ASCII.getString(data, (int)offset + 1, data.getUint8(offset)); + offset += data.getUint8(offset) + 1; + + if (readExpansion) // expansion ? + { + var cs = data.getUint32(offset); + offset += 4; + pt.readExpansion = data.getString(offset, cs); + offset += cs; + } + + if (writeExpansion) // expansion ? + { + var cs = data.getUint32(offset); + offset += 4; + pt.writeExpansion = data.getString(offset, cs); + offset += cs; + } + + od.properties.push(pt); + } + else if (type == 2) // Event + { + var et = new EventTemplate(); + et.index = eventIndex++; + var expansion = ((data.getUint8(offset++) & 0x10) == 0x10); + + et.name = data.getString(offset + 1, data.getUint8(offset));// Encoding.ASCII.getString(data, (int)offset + 1, (int)data.getUint8(offset)); + offset += data.getUint8(offset) + 1; + + if (expansion) // expansion ? + { + var cs = data.getUint32(offset); + offset += 4; + et.expansion = data.getString(offset, cs); + offset += cs; + } + + od.events.push(et); + + } + } + + // append signals + for (var i = 0; i < od.events.length; i++) + od.members.push(od.events[i]); + // append slots + for (var i = 0; i < od.functions.length; i++) + od.members.push(od.functions[i]); + // append properties + for (var i = 0; i < od.properties.length; i++) + od.members.push(od.properties[i]); + + + return od; + } +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 02/09/2017. + */ + +"use strict"; + +class SendList extends BinaryList +{ + constructor(connection, doneReply) + { + super(); + this.connection = connection; + this.reply = doneReply; + } + + done() + { + this.connection.send(this.toArray()); + return this.reply; + } +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 25/07/2017. + */ + +"use strict"; + + +class Warehouse +{ + static new(type, name, store = null, parent = null, manager = null) + { + var res = type(); + Warehouse.put(res, name, store, parent, null, 0, manager); + return res; + } + + static get(id) + { + if (Number.isInteger(id)) + { + //if (Warehouse.resources.contains(id)) + return new AsyncReply(Warehouse.resources.item(id)); + //else + // return null; + } + else + { + var p = id.split('/'); + var res = null; + + for(var s = 0; s < this.stores.length; s++) + { + var d = this.stores.at(s); + if (p[0] == d.instance.name) + { + var i = 1; + res = d; + while(p.length > i) + { + var si = i; + + for (var r = 0; r < res.instance.children.length; r++) + if (res.instance.children.item(r).instance.name == p[i]) + { + i++; + res = res.instance.children.item(r); + break; + } + + if (si == i) + // not found, ask the store + return d.get(id.substring(p[0].length + 1)); + } + + return new AsyncReply(res); + } + } + + return new AsyncReply(null); + } + } + + + static remove(resource) + { + + if (Warehouse.resources.contains(resource.instance.id)) + Warehouse.resources.remove(resource.instance.id); + else + return false; + + if (resource instanceof IStore) + { + Warehouse.stores.remove(resource); + + // remove all objects associated with the store + var toBeRemoved = null; + + for (var i = 0; i < Warehouse.resources.length; i++) + { + var o = Warehouse.resources.at(i); + if (o.instance.store == resource) + { + if (toBeRemoved == null) + toBeRemoved = []; + toBeRemoved.push(o); + } + } + + if (toBeRemoved != null) + for(var i = 0; i < toBeRemoved.length; i++) + Warehouse.remove(toBeRemoved[i]); + } + + if (resource.instance.store != null) + resource.instance.store.remove(resource); + resource.destroy(); + + return true; + } + + static put(resource, name, store, parent, customTemplate = null, age = 0, manager = null){ + resource.instance = new Instance(Warehouse.resourceCounter++, name, resource, store, customTemplate, age); + //resource.instance.children.on("add", Warehouse._onChildrenAdd).on("remove", Warehouse._onChildrenRemove); + //resource.instance.parents.on("add", Warehouse._onParentsAdd).on("remove", Warehouse._onParentsRemove); + + if (manager != null) + resource.instance.managers.add(manager); + + if (parent) + { + parent.instance.children.add(resource); + } + else + { + if (!(resource instanceof IStore)) + store.instance.children.add(resource); + } + + if (resource instanceof IStore) + Warehouse.stores.add(resource); + else + store.put(resource); + + Warehouse.resources.add(resource.instance.id, resource); + } + + static _onParentsRemove(value) + { + if (value.instance.children.contains(value)) + value.instance.children.remove(value); + } + + static _onParentsAdd(value) + { + if (!value.instance.children.contains(value)) + value.instance.children.add(value); + } + + static _onChildrenRemove(value) + { + if (value.instance.parents.contains(value)) + value.instance.parents.remove(value); + } + + static _onChildrenAdd(value) + { + if (!value.instance.parents.contains(value)) + value.instance.parents.add(value); + } + + static putTemplate(template) + { + Warehouse.templates.add(template.classId.valueOf(), template); + } + + static getTemplateByType(type) + { + // loaded ? + for (var i = 0; i < Warehouse.templates.length; i++) + if (Warehouse.templates.at(i).className == typeof(type)) + return Warehouse.templates.at(i); + + var template = new ResourceTemplate(type); + Warehouse.templates.add(template.classId.valueOf(), template); + + return template; + } + + static getTemplateByClassId(classId) + { + var template = Warehouse.templates.item(classId); + return new AsyncReply(template); + } + + static getTemplateByClassName(className) + { + for(var i = 0; i < Warehouse.templates.length; i++) + if (Warehouse.templates.at(i).className == className) + return new AsyncReply(Warehouse.templates.at(i)); + + return new AsyncReply(null); + } + + static _qureyIn(path, index, resources) + { + var rt = []; + + if (index == path.length - 1) + { + if (path[index] == "") + for(var i = 0; i < resources.length; i++) + rt.push(resources.at(i)); + else + for(var i = 0; i < resources.length; i++) + if (resources.at(i).instance.name == path[index]) + rt.push(resources.at(i)); + } + else + for(var i = 0; i < resources.length; i++) + if (resources.at(i).instance.name == path[index]) + rt = rt.concat(Warehouse._qureyIn(path, index+1, resources.at(i).instance.children)); + + return rt; + } + + static query(path) + { + var p = path.split('/'); + return new AsyncReply(Warehouse._qureyIn(p, 0, Warehouse.stores)); + } +} + +// Initialize +Warehouse.stores = new AutoList(); +Warehouse.resources = new KeyList(); +Warehouse.resourceCounter = 0; +Warehouse.templates = new KeyList(); + +exports.printMsg = function() { + console.log("Esiur 1.1"); +} + +module.exports = { Warehouse, DistributedConnection}; + + +var WebSocket = require('ws') + diff --git a/build/esiur-debug.js b/build/esiur-debug.js index 74705ed..dcd66a3 100644 --- a/build/esiur-debug.js +++ b/build/esiur-debug.js @@ -1,4 +1,613 @@ /* +* Copyright (c) 2017-2018 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 10/11/2018. + */ + +"use strict"; + +class CustomResourceEvent +{ + constructor(issuer, receivers, params) + { + this.issuer = issuer; + this.receivers = receivers; + this.params = params; + } +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 01/09/2017. + */ + +"use strict"; + +class NetworkBuffer { + constructor() { + this.neededDataLength = 0; + this.data = new DC(0); + } + + get protected() { + return this.neededDataLength > this.data.length; + } + + get available() { + + return this.data.length; + } + + holdAllForNextWrite(src) { + this.holdFor(src, src.length + 1); + } + + holdForNextWrite(src, offset, size) { + this.holdFor(src, offset, size, size + 1); + } + + + holdFor(src, offset, size, needed) { + if (size >= needed) + throw new Exception("Size >= Needed !"); + + this.data = DC.combine(src, offset, size, this.data, 0, this.data.length); + this.neededDataLength = needed; + } + + holdAllFor(src, needed) { + this.holdFor(src, 0, src.length, needed); + } + + protect(data, offset, needed) { + var dataLength = data.length - offset; + + // protection + if (dataLength < needed) { + this.holdFor(data, offset, dataLength, needed); + return true; + } + else + return false; + } + + writeAll(src) { + this.write(src, 0, src.length ? src.length : src.byteLength); + } + + write(src, offset, length) { + this.data = this.data.append(src, offset, length); + } + + get canRead() { + if (this.data.length == 0) + return false; + else if (this.data.length < this.neededDataLength) + return false; + return true; + } + + read() { + if (this.data.length == 0) + return null; + + var rt = null; + + if (this.neededDataLength == 0) { + rt = this.data; + this.data = new DC(0); + } + else { + if (this.data.length >= this.neededDataLength) { + rt = this.data; + this.data = new DC(0); + this.neededDataLength = 0; + return rt; + } + else { + return null; + } + } + + return rt; + } +} + +/* +* 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. +*/ +/** + * Created by Ahmed Zamil on 18/11/2017. + */ +"use strict"; + +const ExceptionCode = +{ + HostNotReachable: 0, + AccessDenied: 1, + ResourceNotFound: 2, + AttachDenied: 3, + InvalidMethod: 4, + InvokeDenied: 5, + CreateDenied: 6, + AddParentDenied: 7, + AddChildDenied: 8, + ViewAttributeDenied: 9, + UpdateAttributeDenied: 10, + StoreNotFound: 11, + ParentNotFound: 12, + ChildNotFound: 13, + ResourceIsNotStore: 14, + DeleteDenied: 15, + DeleteFailed: 16, + UpdateAttributeFailed: 17, + GetAttributesFailed: 18, + ClearAttributesFailed: 19, + TemplateNotFound: 20, + RenameDenied: 21, + ClassNotFound: 22, + MethodNotFound: 23, + PropertyNotFound: 24, + SetPropertyDenied: 25, + ReadOnlyProperty: 26 +}; + +class AsyncException extends Error + { + constructor() + { + super(); + this.raised = false; + } + + raise(type, code, message) + { + this.type = (type == 0 ? "Management" : "Execusion"); + this.code = code; + + if (type == 0) + for(var i in ExceptionCode) + if (ExceptionCode[i] == code) + { + this.message = i; + break; + } + else + this.message = message; + + this.raised = true; + } + + toString() + { + return this.type + " " + this.code + " " + this.message; + } + } +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 16/11/2017. + */ + +"use strict"; + + const AuthenticationType = { + Host: 0, + CoHost: 1, + Client: 2, + Alien: 3 + }; + + class Authentication +{ + constructor(type) + { + this.type = type; + this.state = 0; + this.domain = null; + this.username = null; + } + + get fullName() + { + return this.domain + "@" + this.username; + } +} + + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 16/11/2017. + */ + +"use strict"; + +class Session +{ + constructor(localAuthentication, remoteAuthentication) + { + + this.localAuthentication = localAuthentication; + this.remoteAuthentication = remoteAuthentication; + this.id = null; + this.creation = null; + this.modification = null; + } +} + +/* +* Copyright (c) 2017-2018 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 27/10/2018. + */ + +"use strict"; + +class DistributedPropertyContext +{ + constructor(p1, p2) + { + if(arguments.length == 1) + { + this.method = p1; + } + else if (arguments.length == 2) + { + this.connection = p1; + this.value = p2; + } + } +} + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 16/11/2017. + */ + +"use strict"; + +const ActionType = +{ + Attach: 0, + Delete: 1, + Execute: 2, + GetProperty: 3, + SetProperty: 4, + CreateResource: 5, + UpdateAttributes: 6, + InquireAttributes: 7, + AddParent: 8, + RemoveParent: 9, + AddChild: 10, + RemoveChild: 11, + Rename: 12, + ReceiveEvent: 13 +}; + +const Ruling = { + Denied: 0, + Allowed: 1, + DontCare: 2, +}; + +class IPermissionsManager +{ + /// + /// Check for permission. + /// + /// IResource. + /// Caller sessions. + /// Action type + /// Function or property to check for permission. + /// Allowed or denined. + applicable(resource, session, action, member, inquirer) + { + + } + + initialize(settings, resource) + { + + } + + get settings() + { + + } +} +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 06/11/2017. + */ + +"use strict"; + +class KeyList + { + constructor() + { + this.keys = []; + this.values = []; + } + + at(index) + { + return this.values[index]; + } + + item(key) + { + for(var i = 0; i < this.keys.length; i++) + if (this.keys[i] == key) + return this.values[i]; + } + + get(key) + { + for(var i = 0; i < this.keys.length; i++) + if (this.keys[i] == key) + return this.values[i]; + } + + _item_destroyed(sender) + { + for(var i = 0; i < this.values.length; i++) + if (sender == this.values[i]) + { + this.removeAt(i); + break; + } + } + + add(key, value) + { + this.remove(key); + + if (value instanceof IDestructible) + value.on("destroy", this._item_destroyed, this); + + this.keys.push(key); + this.values.push(value); + } + + contains(key) + { + for(var i = 0; i < this.keys.length; i++) + if (this.keys[i] == key) + return true; + + return false; + } + + set(key, value) + { + this.remove(key); + this.add(key, value); + } + + remove(key) + { + for(var i = 0; i < this.keys.length; i++) + if (key == this.keys[i]) + { + this.removeAt(i); + break; + } + } + + removeAt(index) + { + if (this.values[index] instanceof IDestructible) + this.values[index].off("destroy", this._item_destroyed); + + this.keys.splice(index, 1); + this.values.splice(index, 1); + } + + get length() + { + return this.keys.length; + } + } +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 06/11/2017. + */ + +"use strict"; + +class PropertyValue + { + constructor(value, age, date) + { + this.value = value; + this.age = age; + this.date = date; + } + } +/* * Copyright (c) 2017 Ahmed Kh. Zamil * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -23,6 +632,7 @@ /** * Created by Ahmed Zamil on 30/08/2017. */ +"use strict"; class IEventHandler { @@ -42,19 +652,32 @@ class IEventHandler var args = Array.prototype.slice.call(arguments, 1); if (this._events[event]) for(var i = 0; i < this._events[event].length; i++) - if (this._events[event][i].apply(this, args)) + if (this._events[event][i].f.apply(this._events[event][i].i, args)) return true; return false; } - on(event, fn) + _emitArgs(event, args) { + event = event.toLowerCase(); + if (this._events[event]) + for(var i = 0; i < this._events[event].length; i++) + if (this._events[event][i].f.apply(this._events[event][i].i, args)) + return true; + return this; + } + + on(event, fn, issuer) + { + if (!(fn instanceof Function)) + return this; + event = event.toLowerCase(); // add if (!this._events[event]) this._events[event] = []; - this._events[event].push(fn); + this._events[event].push({f: fn, i: issuer == null ? this: issuer}); return this; } @@ -65,9 +688,13 @@ class IEventHandler { if (fn) { - var index = this._events[event].indexOf(fn); - if (index > -1) - this._events[event].splice(index, 1); + for(var i = 0; i < this._events[event].length; i++) + if (this._events[event][i].f == fn) + this._events[event].splice(i--, 1); + + //var index = this._events[event].indexOf(fn); + //if (index > -1) + //this._events[event].splice(index, 1); } else { @@ -76,15 +703,183 @@ class IEventHandler } } } -/** - * [js-sha256]{@link https://github.com/emn178/js-sha256} - * - * @version 0.6.0 - * @author Chen, Yi-Cyuan [emn178@gmail.com] - * @copyright Chen, Yi-Cyuan 2014-2017 - * @license MIT + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 25/12/2017. + * Ref: https://en.wikipedia.org/wiki/SHA-2 */ -!function(){"use strict";function t(t,i){i?(p[0]=p[16]=p[1]=p[2]=p[3]=p[4]=p[5]=p[6]=p[7]=p[8]=p[9]=p[10]=p[11]=p[12]=p[13]=p[14]=p[15]=0,this.blocks=p):this.blocks=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],t?(this.h0=3238371032,this.h1=914150663,this.h2=812702999,this.h3=4144912697,this.h4=4290775857,this.h5=1750603025,this.h6=1694076839,this.h7=3204075428):(this.h0=1779033703,this.h1=3144134277,this.h2=1013904242,this.h3=2773480762,this.h4=1359893119,this.h5=2600822924,this.h6=528734635,this.h7=1541459225),this.block=this.start=this.bytes=0,this.finalized=this.hashed=!1,this.first=!0,this.is224=t}function i(i,r,e){var n="string"!=typeof i;if(n){if(null===i||void 0===i)throw h;i.constructor===s.ArrayBuffer&&(i=new Uint8Array(i))}var o=i.length;if(n){if("number"!=typeof o||!Array.isArray(i)&&(!a||!ArrayBuffer.isView(i)))throw h}else{for(var f,u=[],o=i.length,c=0,y=0;o>y;++y)f=i.charCodeAt(y),128>f?u[c++]=f:2048>f?(u[c++]=192|f>>6,u[c++]=128|63&f):55296>f||f>=57344?(u[c++]=224|f>>12,u[c++]=128|f>>6&63,u[c++]=128|63&f):(f=65536+((1023&f)<<10|1023&i.charCodeAt(++y)),u[c++]=240|f>>18,u[c++]=128|f>>12&63,u[c++]=128|f>>6&63,u[c++]=128|63&f);i=u}i.length>64&&(i=new t(r,!0).update(i).array());for(var p=[],l=[],y=0;64>y;++y){var d=i[y]||0;p[y]=92^d,l[y]=54^d}t.call(this,r,e),this.update(l),this.oKeyPad=p,this.inner=!0,this.sharedMemory=e}var h="input is invalid type",s="object"==typeof window?window:{},r=!s.JS_SHA256_NO_NODE_JS&&"object"==typeof process&&process.versions&&process.versions.node;r&&(s=global);var e=!s.JS_SHA256_NO_COMMON_JS&&"object"==typeof module&&module.exports,n="function"==typeof define&&define.amd,a="undefined"!=typeof ArrayBuffer,o="0123456789abcdef".split(""),f=[-2147483648,8388608,32768,128],u=[24,16,8,0],c=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],y=["hex","array","digest","arrayBuffer"],p=[];(s.JS_SHA256_NO_NODE_JS||!Array.isArray)&&(Array.isArray=function(t){return"[object Array]"===Object.prototype.toString.call(t)});var l=function(i,h){return function(s){return new t(h,!0).update(s)[i]()}},d=function(i){var h=l("hex",i);r&&(h=v(h,i)),h.create=function(){return new t(i)},h.update=function(t){return h.create().update(t)};for(var s=0;so;){if(this.hashed&&(this.hashed=!1,f[0]=this.block,f[16]=f[1]=f[2]=f[3]=f[4]=f[5]=f[6]=f[7]=f[8]=f[9]=f[10]=f[11]=f[12]=f[13]=f[14]=f[15]=0),i)for(n=this.start;r>o&&64>n;++o)f[n>>2]|=t[o]<o&&64>n;++o)e=t.charCodeAt(o),128>e?f[n>>2]|=e<e?(f[n>>2]|=(192|e>>6)<>2]|=(128|63&e)<e||e>=57344?(f[n>>2]|=(224|e>>12)<>2]|=(128|e>>6&63)<>2]|=(128|63&e)<>2]|=(240|e>>18)<>2]|=(128|e>>12&63)<>2]|=(128|e>>6&63)<>2]|=(128|63&e)<=64?(this.block=f[16],this.start=n-64,this.hash(),this.hashed=!0):this.start=n}return this}},t.prototype.finalize=function(){if(!this.finalized){this.finalized=!0;var t=this.blocks,i=this.lastByteIndex;t[16]=this.block,t[i>>2]|=f[3&i],this.block=t[16],i>=56&&(this.hashed||this.hash(),t[0]=this.block,t[16]=t[1]=t[2]=t[3]=t[4]=t[5]=t[6]=t[7]=t[8]=t[9]=t[10]=t[11]=t[12]=t[13]=t[14]=t[15]=0),t[15]=this.bytes<<3,this.hash()}},t.prototype.hash=function(){var t,i,h,s,r,e,n,a,o,f,u,y=this.h0,p=this.h1,l=this.h2,d=this.h3,v=this.h4,A=this.h5,w=this.h6,b=this.h7,g=this.blocks;for(t=16;64>t;++t)r=g[t-15],i=(r>>>7|r<<25)^(r>>>18|r<<14)^r>>>3,r=g[t-2],h=(r>>>17|r<<15)^(r>>>19|r<<13)^r>>>10,g[t]=g[t-16]+i+g[t-7]+h<<0;for(u=p&l,t=0;64>t;t+=4)this.first?(this.is224?(a=300032,r=g[0]-1413257819,b=r-150054599<<0,d=r+24177077<<0):(a=704751109,r=g[0]-210244248,b=r-1521486534<<0,d=r+143694565<<0),this.first=!1):(i=(y>>>2|y<<30)^(y>>>13|y<<19)^(y>>>22|y<<10),h=(v>>>6|v<<26)^(v>>>11|v<<21)^(v>>>25|v<<7),a=y&p,s=a^y&l^u,n=v&A^~v&w,r=b+h+n+c[t]+g[t],e=i+s,b=d+r<<0,d=r+e<<0),i=(d>>>2|d<<30)^(d>>>13|d<<19)^(d>>>22|d<<10),h=(b>>>6|b<<26)^(b>>>11|b<<21)^(b>>>25|b<<7),o=d&y,s=o^d&p^a,n=b&v^~b&A,r=w+h+n+c[t+1]+g[t+1],e=i+s,w=l+r<<0,l=r+e<<0,i=(l>>>2|l<<30)^(l>>>13|l<<19)^(l>>>22|l<<10),h=(w>>>6|w<<26)^(w>>>11|w<<21)^(w>>>25|w<<7),f=l&d,s=f^l&y^o,n=w&b^~w&v,r=A+h+n+c[t+2]+g[t+2],e=i+s,A=p+r<<0,p=r+e<<0,i=(p>>>2|p<<30)^(p>>>13|p<<19)^(p>>>22|p<<10),h=(A>>>6|A<<26)^(A>>>11|A<<21)^(A>>>25|A<<7),u=p&l,s=u^p&d^f,n=A&w^~A&b,r=v+h+n+c[t+3]+g[t+3],e=i+s,v=y+r<<0,y=r+e<<0;this.h0=this.h0+y<<0,this.h1=this.h1+p<<0,this.h2=this.h2+l<<0,this.h3=this.h3+d<<0,this.h4=this.h4+v<<0,this.h5=this.h5+A<<0,this.h6=this.h6+w<<0,this.h7=this.h7+b<<0},t.prototype.hex=function(){this.finalize();var t=this.h0,i=this.h1,h=this.h2,s=this.h3,r=this.h4,e=this.h5,n=this.h6,a=this.h7,f=o[t>>28&15]+o[t>>24&15]+o[t>>20&15]+o[t>>16&15]+o[t>>12&15]+o[t>>8&15]+o[t>>4&15]+o[15&t]+o[i>>28&15]+o[i>>24&15]+o[i>>20&15]+o[i>>16&15]+o[i>>12&15]+o[i>>8&15]+o[i>>4&15]+o[15&i]+o[h>>28&15]+o[h>>24&15]+o[h>>20&15]+o[h>>16&15]+o[h>>12&15]+o[h>>8&15]+o[h>>4&15]+o[15&h]+o[s>>28&15]+o[s>>24&15]+o[s>>20&15]+o[s>>16&15]+o[s>>12&15]+o[s>>8&15]+o[s>>4&15]+o[15&s]+o[r>>28&15]+o[r>>24&15]+o[r>>20&15]+o[r>>16&15]+o[r>>12&15]+o[r>>8&15]+o[r>>4&15]+o[15&r]+o[e>>28&15]+o[e>>24&15]+o[e>>20&15]+o[e>>16&15]+o[e>>12&15]+o[e>>8&15]+o[e>>4&15]+o[15&e]+o[n>>28&15]+o[n>>24&15]+o[n>>20&15]+o[n>>16&15]+o[n>>12&15]+o[n>>8&15]+o[n>>4&15]+o[15&n];return this.is224||(f+=o[a>>28&15]+o[a>>24&15]+o[a>>20&15]+o[a>>16&15]+o[a>>12&15]+o[a>>8&15]+o[a>>4&15]+o[15&a]),f},t.prototype.toString=t.prototype.hex,t.prototype.digest=function(){this.finalize();var t=this.h0,i=this.h1,h=this.h2,s=this.h3,r=this.h4,e=this.h5,n=this.h6,a=this.h7,o=[t>>24&255,t>>16&255,t>>8&255,255&t,i>>24&255,i>>16&255,i>>8&255,255&i,h>>24&255,h>>16&255,h>>8&255,255&h,s>>24&255,s>>16&255,s>>8&255,255&s,r>>24&255,r>>16&255,r>>8&255,255&r,e>>24&255,e>>16&255,e>>8&255,255&e,n>>24&255,n>>16&255,n>>8&255,255&n];return this.is224||o.push(a>>24&255,a>>16&255,a>>8&255,255&a),o},t.prototype.array=t.prototype.digest,t.prototype.arrayBuffer=function(){this.finalize();var t=new ArrayBuffer(this.is224?28:32),i=new DataView(t);return i.setUint32(0,this.h0),i.setUint32(4,this.h1),i.setUint32(8,this.h2),i.setUint32(12,this.h3),i.setUint32(16,this.h4),i.setUint32(20,this.h5),i.setUint32(24,this.h6),this.is224||i.setUint32(28,this.h7),t},i.prototype=new t,i.prototype.finalize=function(){if(t.prototype.finalize.call(this),this.inner){this.inner=!1;var i=this.array();t.call(this,this.is224,this.sharedMemory),this.update(this.oKeyPad),this.update(i),t.prototype.finalize.call(this)}};var b=d();b.sha256=b,b.sha224=d(!0),b.sha256.hmac=w(),b.sha224.hmac=w(!0),e?module.exports=b:(s.sha256=b.sha256,s.sha224=b.sha224,n&&define(function(){return b}))}(); + +class SHA256 +{ + + static RROT(n, d) + { + return (n >>> d)|(n << (32 - d)); + } + + static compute(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): + + const hash = new Uint32Array([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): + const k = new Uint32Array([ + 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 + var 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 Uint8Array(paddingLength); + paddingBytes[0] = 0x80; + + var data = new DC(BL().addUint8Array(msg).addUint8Array(paddingBytes).addUint64(L).toArray()); + + + + // 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 Uint32Array(64); + for(var i = 0; i < 16; i++) + w[i] = data.getInt32(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) ^ (w[i-15] >>> 3); + var s1 = SHA256.RROT(w[i-2], 17) ^ SHA256.RROT(w[i-2], 19) ^ (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); + var temp2 = S0 + maj; + + h = g; + g = f; + f = e; + e = (d + temp1) >>> 0; + d = c; + c = b; + b = a; + a = (temp1 + temp2) >>> 0; + } + + // Add the compressed chunk to the current hash value: + + hash[0] = (hash[0] + a) >>> 0; + hash[1] = (hash[1] + b) >>> 0; + hash[2] = (hash[2] + c) >>> 0; + hash[3] = (hash[3] + d) >>> 0; + hash[4] = (hash[4] + e) >>> 0; + hash[5] = (hash[5] + f) >>> 0; + hash[6] = (hash[6] + g) >>> 0; + hash[7] = (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(); + } +} /* * Copyright (c) 2017 Ahmed Kh. Zamil * @@ -110,6 +905,9 @@ class IEventHandler /** * Created by Ahmed Zamil on 31/08/2017. */ + +"use strict"; + class IDestructible extends IEventHandler { destroy() @@ -149,6 +947,8 @@ class IDestructible extends IEventHandler * Created by Ahmed Zamil on 05/09/2017. */ +"use strict"; + class AutoList extends IEventHandler { constructor() @@ -157,10 +957,15 @@ class AutoList extends IEventHandler this.list = []; } + get length() + { + return this.list.length; + } + add(value) { if (value instanceof IDestructible) - value.on("destroy", this._item_destroyed); + value.on("destroy", this._item_destroyed, this); this.list.push(value); @@ -173,7 +978,7 @@ class AutoList extends IEventHandler return; if (value instanceof IDestructible) - value.on("destroy", this._item_destroyed); + value.on("destroy", this._item_destroyed, this); if (this.list[index] instanceof IDestructible) this.list[index].off("destroy", this._item_destroyed); @@ -181,6 +986,16 @@ class AutoList extends IEventHandler this.list[index] = value; } + at(index) + { + return this.list[index]; + } + + item(index) + { + return this.list[index]; + } + remove(value) { this.removeAt(this.list.indexOf(value)); @@ -191,6 +1006,11 @@ class AutoList extends IEventHandler return this.list.indexOf(value) > -1; } + toArray() + { + return this.list.slice(0); + } + removeAt(index) { if (index >= this.list.length || index < 0) @@ -237,7 +1057,9 @@ class AutoList extends IEventHandler * Created by Ahmed Zamil on 25/07/2017. */ -var ResourceTrigger = +"use strict"; + +const ResourceTrigger = { Loaded : 0, Initialize: 1, @@ -297,6 +1119,8 @@ class IResource extends IDestructible * Created by Ahmed Zamil on 25/07/2017. */ +"use strict"; + class IStore extends IResource { get(path) { @@ -310,6 +1134,21 @@ class IStore extends IResource { } + record(resource, propertyName, value, age, dateTime) + { + + } + + getRecord(resource, fromDate, toDate) + { + + } + + remove(resource) + { + + } + constructor() { super(); @@ -342,6 +1181,8 @@ class IStore extends IResource { * Created by Ahmed Zamil on 26/08/2017. */ +"use strict"; + class Structure { getKeys() { @@ -379,6 +1220,8 @@ class Structure * Created by Ahmed Zamil on 06/09/2017. */ +"use strict"; + class StructureArray extends Array { push(value) @@ -415,6 +1258,8 @@ class StructureArray extends Array * Created by Ahmed Zamil on 26/08/2017. */ +"use strict"; + class ResourceArray extends Array { push(value) @@ -451,8 +1296,9 @@ class ResourceArray extends Array * Created by Ahmed Zamil on 24/08/2017. */ +"use strict"; -var MemberType = { +const MemberType = { Function: 0, Property: 1, Event: 2 @@ -488,13 +1334,72 @@ class MemberTemplate { /** * Created by Ahmed Zamil on 25/07/2017. */ + +"use strict"; + +const ErrorType = { + Management: 0, + Exception: 1 +}; + +const ProgressType = { + Execution: 0, + Network: 1 +}; + class AsyncReply { then(callback) { this.callbacks.push(callback); + if (this.ready) + { callback(this.result, this); + + if (!this.taskExpired) + { + this.taskExpired = true; + this.resolveTask(this.result); + } + } + + return this; + } + + catch(callback) + { + return error(callback); + } + + error(callback) + { + this.errorCallbacks.push(callback); + + if (this.exception.raised) + { + callback(this.exception); + + if (!this.taskExpired) + { + this.taskExpired = true; + this.rejectTask(this.exception); + } + } + + return this; + } + + progress(callback) + { + this.progressCallbacks.push(callback); + return this; + } + + chunk(callback) + { + this.chunkCallbacks.push(callback); + return this; } trigger(result) @@ -504,15 +1409,79 @@ class AsyncReply for(var i = 0; i < this.callbacks.length; i++) this.callbacks[i](result, this); + + + if (!this.taskExpired) + { + this.taskExpired = true; + this.resolveTask(this.result); + } + } + + + triggerError(type, code, message)//exception) + { + if (this.ready) + return; + + this.exception.raise(type, code, message);// = exception; + + for(var i = 0; i < this.errorCallbacks.length; i++) + this.errorCallbacks[i](this.exception, this); + + + if (!this.taskExpired) + { + this.taskExpired = true; + this.rejectTask(this.exception); + } + } + + triggerProgress(type, value, max) + { + if (this.ready) + return; + + for(var i = 0; i < this.progressCallbacks.length; i++) + this.progressCallbacks[i](type, value, max, this); + } + + triggerChunk(value) + { + if (this.ready) + return; + + for(var i = 0; i < this.chunkCallbacks.length; i++) + this.chunkCallbacks[i](value, this); } constructor(result) { this.callbacks = []; + this.errorCallbacks = []; + this.progressCallbacks = []; + this.chunkCallbacks = []; + this.exception = new AsyncException();// null; - if (result) { + var self = this; + + this.task = new Promise(function(resolve, reject){ + self.resolveTask = resolve; + self.rejectTask = reject; + }); + + + if (result !== undefined) { this.result = result; this.ready = true; + this.taskExpired = true; + this.resolveTask(result); + } + else + { + this.taskExpired = false; + this.ready = false; + this.result = null; } } } @@ -541,6 +1510,8 @@ class AsyncReply * Created by Ahmed Zamil on 25/07/2017. */ +"use strict"; + class AsyncBag extends AsyncReply { constructor() { @@ -560,13 +1531,27 @@ class AsyncBag extends AsyncReply var self = this; + var singleTaskCompleted = function(taskIndex) + { + return function(results, reply){ + self.results[taskIndex] = results; + self.count++; + if (self.count == self.results.length) + self.trigger(self.results); + }; + }; + for(var i = 0; i < this.results.length; i++) + this.replies[i].then(singleTaskCompleted(i)); + + /* this.replies[i].then(function(r, reply){ self.results[self.replies.indexOf(reply)] = r; self.count++; if (self.count == self.results.length) self.trigger(self.results); }); + */ } add(reply) @@ -601,6 +1586,9 @@ class AsyncBag extends AsyncReply /** * Created by Ahmed Zamil on 25/07/2017. */ + +"use strict"; + class AsyncQueue extends AsyncReply { @@ -669,7 +1657,9 @@ class AsyncQueue extends AsyncReply * Created by Ahmed Zamil on 25/08/2017. */ -var BL = function(){ +"use strict"; + +function BL(){ return new BinaryList(); }; @@ -785,6 +1775,16 @@ class BinaryList return rt; } + toDC() + { + return new DC(this.toArray()); + } + + addDateTime(value, position) + { + return this.add({type: DataType.DateTime, value: value}, position); + } + addUint8Array(value, position) { return this.add({type: DataType.UInt8Array, value: value}, position); @@ -883,16 +1883,17 @@ class BinaryList * Created by Ahmed Zamil on 25/07/2017. */ -var ResourceComparisonResult = +"use strict"; + +const ResourceComparisonResult = { Null: 0, Distributed: 1, - DistributedSameClass: 2, - Local: 3, - Same: 4 + Local: 2, + Same: 3 }; -var StructureComparisonResult = +const StructureComparisonResult = { Null: 0, Structure: 1, @@ -903,6 +1904,7 @@ var StructureComparisonResult = class Codec { + static parse(data, offset, sizeObject, connection, dataType = DataType.Unspecified) { var size; @@ -1074,16 +2076,251 @@ class Codec { return connection.fetch(iid);// Warehouse.Get(iid); } + /// + /// 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 parseResourceArray(data, offset, length, connection) + { + var reply = new AsyncBag(); + if (length == 0) + { + reply.seal(); + return reply; + } - static parseStructure(data, offset, contentLength, connection, keylist = null, typelist = null, keys = null, types = null) { - var reply = new AsyncReply(); - var bag = new AsyncBag(); + var end = offset + length; + + // + var result = data[offset++]; + + var previous = null; + + if (result == ResourceComparisonResult.Null) + previous = new AsyncReply(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); - if (keylist == null) - keylist = []; - if (typelist == null) - typelist = []; + while (offset < end) + { + result = data[offset++]; + + var current = null; + + if (result == ResourceComparisonResult.Null) + { + current = new AsyncReply(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 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. + + static composePropertyValueArray(array, connection, prependLength = false) + { + var rt = BL(); + for (var i = 0; i < array.Length; i++) + rt.addUint8Array(Codec.composePropertyValue(array[i], connection)); + if (prependLength) + rt.addUint32(rt.length, 0); + return rt.toArray(); + } + + /// + /// Compose a property value. + /// + /// Property value + /// DistributedConnection is required to check locality. + /// Array of bytes in the network byte order. + static composePropertyValue(propertyValue, connection) + { + // age, date, value + return BL().addUint64(propertyValue.age) + .addDateTime(propertyValue.date) + .addUint8Array(Codec.compose(propertyValue.value, connection)) + .toArray(); + } + + + /// + /// Parse property value. + /// + /// Array of bytes. + /// Zero-indexed offset. + /// DistributedConnection is required to fetch resources. + /// Output content size. + /// PropertyValue. + static parsePropertyValue(data, offset, sizeObject, connection) + { + var reply = new AsyncReply(); + + var age = data.getUint64(offset); + offset += 8; + + var date = data.getDateTime(offset); + offset += 8; + + var cs = {}; + + Codec.parse(data, offset, cs, connection).then(function(value) + { + reply.trigger(new PropertyValue(value, age, date)); + }); + + sizeObject.size = 16 + cs.size; + 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 parseHistory(data, offset, length, resource, connection) + { + var list = new KeyList(); + + var reply = new AsyncReply(); + + var bagOfBags = new AsyncBag(); + + var ends = offset + length; + 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(Codec.parsePropertyValueArray(data, offset, cs, connection)); + offset += cs; + } + + bagOfBags.seal(); + + bagOfBags.then(x => + { + for(var i = 0; i < list.length; i++) + list.values[i] = x[i]; + + reply.trigger(list); + }); + + return reply; + + } + + /// + /// Compose resource history + /// + /// History + /// DistributedConnection is required to fetch resources. + /// + static composeHistory(history, connection, prependLength = false) + { + var rt = new BinaryList(); + + for (var i = 0; i < history.length; i++) + rt.addUint8(history.keys[i].index).addUint8Array(Codec.composePropertyValueArray(history.values[i], connection, true)); + + if (prependLength) + rt.addUint32(rt.length, 0); + + return rt.toArray(); + } + + /// + /// Parse an array of ProperyValue. + /// + /// Array of bytes. + /// Zero-indexed offset. + /// Number of bytes to parse. + /// DistributedConnection is required to fetch resources. + /// + static parsePropertyValueArray(data, offset, length, connection) + { + var rt = new AsyncBag(); + + while (length > 0) + { + var cs = {}; + + rt.add(Codec.parsePropertyValue(data, offset, cs, connection)); + + if (cs.size > 0) + { + offset += cs.size; + length -= cs.size; + } + else + throw new Exception("Error while parsing ValueInfo structured data"); + } + + rt.seal(); + return rt; + } + + static parseStructure(data, offset, contentLength, connection, metadata = null, keys = null, types = null) + { + var reply = new AsyncReply(); + var bag = new AsyncBag(); + + + var keylist = []; + var typelist = []; + if (keys == null) { while (contentLength > 0) { @@ -1108,11 +2345,12 @@ class Codec { var rt = {}; bag.add(Codec.parse(data, offset, rt, connection)); - contentLength -= rt.size + 1; - offset += rt.size + 1; + contentLength -= rt.size; + offset += rt.size; } } else { + for (var i = 0; i < keys.length; i++) { keylist.push(keys[i]); typelist.push(types[i]); @@ -1138,7 +2376,12 @@ class Codec { reply.trigger(s); }); - + if (metadata != null) + { + metadata.keys = keylist; + metadata.types = typelist; + } + return reply; } @@ -1167,6 +2410,11 @@ class Codec { static compose(value, connection, prependType = true) { + if (value instanceof Function) + value = value(connection); + else if (value instanceof DistributedPropertyContext) + value = value.method(this); + var type = Codec.getDataType(value, connection); var rt = new BinaryList(); @@ -1181,7 +2429,7 @@ class Codec { break; case DataType.Resource: - rt.addUint32(value.instance.id); + rt.addUint32(value._p.instanceId); break; case DataType.DistributedResource: @@ -1321,7 +2569,7 @@ class Codec { static isLocalResource(resource, connection) { if (resource instanceof DistributedResource) - if (resource.connection == connection) + if (resource._p.connection == connection) return true; return false; @@ -1336,58 +2584,47 @@ static isLocalResource(resource, connection) { } static compareResource(previous, next, connection) { + if (next == null) return ResourceComparisonResult.Null; - - if (next == previous) + else if (next == previous) return ResourceComparisonResult.Same; - - if (Codec.isLocalResource(next, connection)) + else if (Codec.isLocalResource(next, connection)) return ResourceComparisonResult.Local; - - if (previous == null) + else return ResourceComparisonResult.Distributed; - - if (previous.instance.template.classId.valueOf() == next.instance.template.classId.valueOf()) - return ResourceComparisonResult.DistributedSameClass; - - return ResourceComparisonResult.Distributed; } static composeResourceArray(resources, connection, prependLength = false) { - if (resources == null || resources.length == 0 || !(resources instanceof ResourceArray)) - return new DC(0); - var rt = new BinaryList(); - var comparsion = Codec.compareResource(null, resources[0], connection); + if (resources == null || resources.length == 0)// || !(resources instanceof ResourceArray)) + return prependLength ? new DC(4) : new DC(0); - rt.addUint8(comparsion); + var rt = new BinaryList(); + var comparsion = Codec.compareResource(null, resources[0], connection); - if (comparsion == ResourceComparisonResult.Local) - rt.addUint32(resources[0].id); - else if (comparsion == ResourceComparisonResult.Distributed) { - rt.addUint8Array(resources[0].instance.template.classId.value); - rt.addUint32(resources[0].instance.id); - } + rt.addUint8(comparsion); - for (var i = 1; i < resources.length; i++) { - comparsion = Codec.compareResource(resources[i - 1], resources[i], connection); - rt.addUint8(comparsion); - if (comparsion == ResourceComparisonResult.Local) - rt.addUint32(resources[0].id); - else if (comparsion == ResourceComparisonResult.Distributed) { - rt.addUint8Array(resources[0].instance.template.classId.value); - rt.addUint32(resources[0].instance.id); - } - else if (comparsion == ResourceComparisonResult.DistributedSameClass) { - rt.addUint32(resources[0].instance.id); - } - } + if (comparsion == ResourceComparisonResult.Local) + rt.addUint32(resources[0]._p.instanceId); + else if (comparsion == ResourceComparisonResult.Distributed) + rt.addUint32(resources[0].instance.id); - if (prependLength) - rt.addUint32(0, rt.length); + for (var i = 1; i < resources.Length; i++) + { + comparsion = Codec.compareResource(resources[i - 1], resources[i], connection); + rt.addUint8(comparsion); + if (comparsion == ResourceComparisonResult.Local) + rt.addUint32(resources[i]._p.instanceId); + else if (comparsion == ResourceComparisonResult.Distributed) + rt.addUint32(resources[i].instance.id); + } - return rt.toArray(); + if (prependLength) + rt.addUint32(rt.length, 0); + + + return rt.toArray(); } @@ -1478,6 +2715,85 @@ static getDataType(value) { return DataType.Void; } } + + + /// + /// 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 parseStructureArray(data, offset, length, connection) + { + var reply = new AsyncBag(); + if (length == 0) + { + reply.seal(); + return reply; + } + + var end = offset + length; + + var result = data[offset++]; + + var previous = null; + //var previousKeys = []; + //var previousTypes = []; + + var metadata = {keys: null, types: null}; + + + if (result == StructureComparisonResult.Null) + previous = new AsyncReply(null); + else if (result == StructureComparisonResult.Structure) + { + var cs = data.getUint32(offset); + offset += 4; + previous = this.parseStructure(data, offset, cs, connection, metadata); + offset += cs; + } + + reply.add(previous); + + + while (offset < end) + { + result = data[offset++]; + + if (result == StructureComparisonResult.Null) + previous = new AsyncReply(null); + else if (result == StructureComparisonResult.Structure) + { + var cs = data.getUint32(offset); + offset += 4; + previous = this.parseStructure(data, offset, cs, connection, metadata); + offset += cs; + } + else if (result == StructureComparisonResult.StructureSameKeys) + { + var cs = data.getUint32(offset); + offset += 4; + previous = this.parseStructure(data, offset, cs, connection, metadata, metadata.keys); + offset += cs; + } + else if (result == StructureComparisonResult.StructureSameTypes) + { + var cs = data.getUint32(offset); + offset += 4; + previous = this.parseStructure(data, offset, cs, connection, metadata, metadata.keys, metadata.types); + offset += cs; + } + + reply.add(previous); + } + + reply.seal(); + return reply; + } + + } /* * Copyright (c) 2017 Ahmed Kh. Zamil @@ -1505,8 +2821,10 @@ static getDataType(value) { * Created by Ahmed Zamil on 25/07/2017. */ -var UNIX_EPOCH = 621355968000000000; -var TWO_PWR_32 = (1 << 16) * (1 << 16); +"use strict"; + +const UNIX_EPOCH = 621355968000000000; +const TWO_PWR_32 = (1 << 16) * (1 << 16); class DC extends Uint8Array// extends DataView // Data Converter { @@ -1636,7 +2954,17 @@ class DC extends Uint8Array// extends DataView // Data Converter return new DC(rt); } + static stringArrayToBytes(values) + { + var list = new BinaryList(); + for(var i = 0; i < values.length; i++) + { + var s = DC.stringToBytes(values[i]); + list.addUint32(s.length).addUint8Array(s); + } + return list.toArray(); + } append(src, offset, length) { @@ -1919,7 +3247,7 @@ class DC extends Uint8Array// extends DataView // Data Converter getDateTime(offset) { var ticks = this.getUint64(offset); - return new Date(Math.round((ticks-DCStatic.UNIX_EPOCH)/10000)); + return new Date(Math.round((ticks-UNIX_EPOCH)/10000)); } getDateTimeArray(offset) @@ -1932,7 +3260,7 @@ class DC extends Uint8Array// extends DataView // Data Converter getGuid(offset) { - return new Guid(this.getUint8Array(offset, 16)); + return new Guid(this.clip(offset, 16)); /* var d = this.getUint8Array(offset, 16); @@ -1995,7 +3323,11 @@ class DC extends Uint8Array// extends DataView // Data Converter /** * Created by Ahmed Zamil on 25/07/2017. */ -DataType = { + +"use strict"; + +const DataType = +{ Void: 0x0, //Variant, Bool: 1, @@ -2107,22 +3439,26 @@ DataType = { * Created by Ahmed Zamil on 25/07/2017. */ -var AuthenticationType = -{ - Host: 0, - CoHost: 1, - Client: 2, - Alien: 3 -}; +"use strict"; class DistributedConnection extends IStore { send(data) { + console.log("Send", data.length); this.socket.send(data.buffer); } - sendParams() { - return new SendList(this); + sendParams(doneReply) { + return new SendList(this, doneReply); + } + + generateNonce(length) + { + var rt = new Uint8Array(length); + for(var i = 0; i < length; i++) + rt[i] = Math.random() * 255; + + return rt; } constructor(url, domain, username, password, checkInterval = 30, connectionTimeout = 600, revivingTime = 120) { @@ -2130,9 +3466,20 @@ class DistributedConnection extends IStore { super(); //Instance.Name = Global.GenerateCode(12); - this.hostType = AuthenticationType.Client; - this.domain = domain; - this.localUsername = username; + + //this.hostType = AuthenticationType.Client; + //this.domain = domain; + //this.localUsername = username; + + this._register("ready"); + this._register("error"); + this._register("close"); + + this.session = new Session(new Authentication(AuthenticationType.Client), new Authentication(AuthenticationType.Host)); + + this.session.localAuthentication.domain = domain; + this.session.localAuthentication.username = username; + this.localPassword = DC.stringToBytes(password); this.socket = new WebSocket(url, "iip"); @@ -2153,10 +3500,10 @@ class DistributedConnection extends IStore { this.authPacket = new IIPAuthPacket(); this.resources = {}; - this.templates = {}; + this.templates = new KeyList(); this.requests = {}; this.pathRequests = {}; - this.templateRequests = {}; + this.templateRequests = new KeyList(); this.resourceRequests = {}; this.callbackCounter = 0; @@ -2171,8 +3518,8 @@ class DistributedConnection extends IStore { } }); - this.localNonce = new Uint8Array(32); - window.crypto.getRandomValues(this.localNonce); + this.localNonce = this.generateNonce(32);// new Uint8Array(32); + //window.crypto.getRandomValues(this.localNonce); // declare (Credentials -> No Auth, No Enctypt) var un = DC.stringToBytes(username); @@ -2187,7 +3534,7 @@ class DistributedConnection extends IStore { this.socket.onmessage = function (msg) { - //console.log(msg); + console.log("Rec", msg.data.byteLength); this.networkBuffer.writeAll(msg.data); @@ -2198,247 +3545,387 @@ class DistributedConnection extends IStore { }; + + this.socket.onclose = function(event) + { + self.close(event); + }; + + //this.socket.onerror = function(event) + //{ + // self.close(event); + //}; } + processPacket(msg, offset, ends, data) + { + + + var authPacket = this.authPacket; + + if (this.ready) { + var packet = new IIPPacket(); + + var rt = packet.parse(msg, offset, ends); + if (rt <= 0) { + data.holdFor(msg, offset, ends - offset, -rt); + return ends; + } + else { + offset += rt; + + if (packet.command == IIPPacketCommand.Event) { + switch (packet.event) { + case IIPPacketEvent.ResourceReassigned: + this.IIPEventResourceReassigned(packet.resourceId, packet.newResourceId); + break; + case IIPPacketEvent.ResourceDestroyed: + this.IIPEventResourceDestroyed(packet.resourceId); + break; + case IIPPacketEvent.PropertyUpdated: + this.IIPEventPropertyUpdated(packet.resourceId, packet.methodIndex, packet.content); + break; + case IIPPacketEvent.EventOccurred: + this.IIPEventEventOccurred(packet.resourceId, packet.methodIndex, packet.content); + break; + + case IIPPacketEvent.ChildAdded: + this.IIPEventChildAdded(packet.resourceId, packet.childId); + break; + case IIPPacketEvent.ChildRemoved: + this.IIPEventChildRemoved(packet.resourceId, packet.childId); + break; + case IIPPacketEvent.Renamed: + this.IIPEventRenamed(packet.resourceId, packet.content); + break; + case IIPPacketEvent.AttributesUpdated: + this.IIPEventAttributesUpdated(packet.resourceId, packet.content); + break; + + } + } + else if (packet.command == IIPPacketCommand.Request) { + switch (packet.action) { + + // Manage + case IIPPacketAction.AttachResource: + this.IIPRequestAttachResource(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.ReattachResource: + this.IIPRequestReattachResource(packet.callbackId, packet.resourceId, packet.resourceAge); + break; + case IIPPacketAction.DetachResource: + this.IIPRequestDetachResource(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.CreateResource: + this.IIPRequestCreateResource(packet.callbackId, packet.storeId, packet.resourceId, packet.content); + break; + case IIPPacketAction.DeleteResource: + this.IIPRequestDeleteResource(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.AddChild: + this.IIPRequestAddChild(packet.callbackId, packet.resourceId, packet.childId); + break; + case IIPPacketAction.RemoveChild: + this.IIPRequestRemoveChild(packet.callbackId, packet.resourceId, packet.childId); + break; + case IIPPacketAction.RenameResource: + this.IIPRequestRenameResource(packet.callbackId, packet.resourceId, packet.content); + break; + + // Inquire + case IIPPacketAction.TemplateFromClassName: + this.IIPRequestTemplateFromClassName(packet.callbackId, packet.className); + break; + case IIPPacketAction.TemplateFromClassId: + this.IIPRequestTemplateFromClassId(packet.callbackId, packet.classId); + break; + case IIPPacketAction.TemplateFromResourceId: + this.IIPRequestTemplateFromResourceId(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.QueryLink: + this.IIPRequestQueryResources(packet.callbackId, packet.resourceLink); + break; + case IIPPacketAction.ResourceChildren: + this.IIPRequestResourceChildren(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.ResourceParents: + this.IIPRequestResourceParents(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.ResourceHistory: + this.IIPRequestInquireResourceHistory(packet.callbackId, packet.resourceId, packet.fromDate, packet.toDate); + break; + + // Invoke + case IIPPacketAction.InvokeFunction: + this.IIPRequestInvokeFunction(packet.callbackId, packet.resourceId, packet.methodIndex, packet.content); + break; + case IIPPacketAction.GetProperty: + this.IIPRequestGetProperty(packet.callbackId, packet.resourceId, packet.methodIndex); + break; + case IIPPacketAction.GetPropertyIfModified: + this.IIPRequestGetPropertyIfModifiedSince(packet.callbackId, packet.resourceId, packet.methodIndex, packet.resourceAge); + break; + case IIPPacketAction.SetProperty: + this.IIPRequestSetProperty(packet.callbackId, packet.resourceId, packet.methodIndex, packet.content); + break; + case IIPPacketAction.ResourceHistory: + this.IIPRequestInquireResourceHistory(packet.callbackId, packet.resourceId, packet.fromDate, packet.toDate); + break; + case IIPPacketAction.QueryLink: + this.IIPRequestQueryResources(packet.callbackId, packet.resourceLink); + break; + + // Attribute + case IIPPacketAction.GetAllAttributes: + this.IIPRequestGetAttributes(packet.callbackId, packet.resourceId, packet.content, true); + break; + case IIPPacketAction.UpdateAllAttributes: + this.IIPRequestUpdateAttributes(packet.callbackId, packet.resourceId, packet.content, true); + break; + case IIPPacketAction.ClearAllAttributes: + this.IIPRequestClearAttributes(packet.callbackId, packet.resourceId, packet.content, true); + break; + case IIPPacketAction.GetAttributes: + this.IIPRequestGetAttributes(packet.callbackId, packet.resourceId, packet.content, false); + break; + case IIPPacketAction.UpdateAttributes: + this.IIPRequestUpdateAttributes(packet.callbackId, packet.resourceId, packet.content, false); + break; + case IIPPacketAction.ClearAttributes: + this.IIPRequestClearAttributes(packet.callbackId, packet.resourceId, packet.content, false); + break; + + } + } + else if (packet.command == IIPPacketCommand.Reply) { + switch (packet.action) { + case IIPPacketAction.AttachResource: + this.IIPReply(packet.callbackId, packet.classId, packet.resourceAge, packet.resourceLink, packet.content); + break; + case IIPPacketAction.ReattachResource: + this.IIPReply(packet.callbackId, packet.resourceAge, packet.content); + break; + case IIPPacketAction.DetachResource: + this.IIPReply(packet.callbackId); + break; + case IIPPacketAction.CreateResource: + this.IIPReply(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.DeleteResource: + case IIPPacketAction.AddChild: + case IIPPacketAction.RemoveChild: + case IIPPacketAction.RenameResource: + this.IIPReply(packet.callbackId); + break; + case IIPPacketAction.TemplateFromClassName: + case IIPPacketAction.TemplateFromClassId: + case IIPPacketAction.TemplateFromResourceId: + this.IIPReply(packet.callbackId, ResourceTemplate.parse(packet.content)); + break; + + case IIPPacketAction.QueryLink: + case IIPPacketAction.ResourceChildren: + case IIPPacketAction.ResourceParents: + case IIPPacketAction.ResourceHistory: + this.IIPReply(packet.callbackId, packet.content); + break; + + case IIPPacketAction.InvokeFunction: + this.IIPReplyInvoke(packet.callbackId, packet.content); + break; + case IIPPacketAction.GetProperty: + this.IIPReply(packet.callbackId, packet.content); + break; + case IIPPacketAction.GetPropertyIfModified: + this.IIPReply(packet.callbackId, packet.content); + break; + case IIPPacketAction.SetProperty: + this.IIPReply(packet.callbackId); + break; + + // Attribute + case IIPPacketAction.GetAllAttributes: + case IIPPacketAction.GetAttributes: + this.IIPReply(packet.callbackId, packet.content); + break; + + case IIPPacketAction.UpdateAllAttributes: + case IIPPacketAction.UpdateAttributes: + case IIPPacketAction.ClearAllAttributes: + case IIPPacketAction.ClearAttributes: + this.IIPReply(packet.callbackId); + break; + + } + + } + else if (packet.command == IIPPacketCommand.Report) + { + switch (packet.report) + { + case IIPPacketReport.ManagementError: + this.IIPReportError(packet.callbackId, ErrorType.Management, packet.errorCode, null); + break; + case IIPPacketReport.ExecutionError: + this.IIPReportError(packet.callbackId, ErrorType.Exception, packet.errorCode, packet.errorMessage); + break; + case IIPPacketReport.ProgressReport: + this.IIPReportProgress(packet.callbackId, ProgressType.Execution, packet.progressValue, packet.progressMax); + break; + case IIPPacketReport.ChunkStream: + this.IIPReportChunk(packet.callbackId, packet.content); + + break; + } + } + + } + } + + else { + var rt = authPacket.parse(msg, offset, ends); + + + if (rt <= 0) { + data.holdAllFor(msg, ends - rt); + return ends; + } + else { + offset += rt; + + if (this.session.localAuthentication.type == AuthenticationType.Host) { + if (authPacket.command == IIPAuthPacketCommand.Declare) { + if (authPacket.remoteMethod == IIPAuthPacketMethod.credentials + && authPacket.localMethod == IIPAuthPacketMethod.None) { + this.session.remoteAuthentication.username = authPacket.remoteUsername; + this.remoteNonce = authPacket.remoteNonce; + this.domain = authPacket.domain; + this.sendParams().addUint8(0xa0).addUint8Array(this.localNonce).done(); + } + } + else if (authPacket.command == IIPAuthPacketCommand.Action) { + if (authPacket.action == IIPAuthPacketAction.AuthenticateHash) { + var remoteHash = authPacket.hash; + + this.server.membership.getPassword(this.session.remoteAuthentication.username, this.domain).then(function (pw) { + if (pw != null) { + + //var hash = new DC(sha256.arrayBuffer(BL().addString(pw).addUint8Array(remoteNonce).addUint8Array(this.localNonce).toArray())); + var hash = SHA256.compute(BL().addString(pw).addUint8Array(remoteNonce).addUint8Array(this.localNonce).toDC()); + + + if (hash.sequenceEqual(remoteHash)) { + // send our hash + //var localHash = new DC(sha256.arrayBuffer((new BinaryList()).addUint8Array(this.localNonce).addUint8Array(remoteNonce).addUint8Array(pw).toArray())); + var localHash = SHA256.compute(BL().addUint8Array(this.localNonce).addUint8Array(remoteNonce).addUint8Array(pw).toDC()); + this.sendParams().addUint8(0).addUint8Array(localHash).done(); + + this.readyToEstablish = true; + } + else { + // incorrect password + this.sendParams().addUint8(0xc0).addInt32(1).addUint16(5).addString("Error").done(); + } + } + }); + } + else if (authPacket.action == IIPAuthPacketAction.NewConnection) { + if (readyToEstablish) { + this.session.id = this.generateNonce(32);// new DC(32); + //window.crypto.getRandomValues(this.session.id); + + this.sendParams().addUint8(0x28).addUint8Array(this.session.id).done(); + this.ready = true; + this._emit("ready", this); + } + } + } + } + else if (this.session.localAuthentication.type == AuthenticationType.Client) { + if (authPacket.command == IIPAuthPacketCommand.Acknowledge) { + this.remoteNonce = authPacket.remoteNonce; + + // send our hash + + //var localHash = new DC(sha256.arrayBuffer(BL().addUint8Array(this.localPassword) + // .addUint8Array(this.localNonce) + // .addUint8Array(this.remoteNonce).toArray())); + + var localHash = SHA256.compute(BL().addUint8Array(this.localPassword) + .addUint8Array(this.localNonce) + .addUint8Array(this.remoteNonce).toDC()); + + this.sendParams().addUint8(0).addUint8Array(localHash).done(); + } + else if (authPacket.command == IIPAuthPacketCommand.Action) { + if (authPacket.action == IIPAuthPacketAction.AuthenticateHash) { + // check if the server knows my password + //var remoteHash = new DC(sha256.arrayBuffer(BL().addUint8Array(this.remoteNonce) + // .addUint8Array(this.localNonce) + // .addUint8Array(this.localPassword).toArray() + //)); + + var remoteHash = SHA256.compute(BL().addUint8Array(this.remoteNonce) + .addUint8Array(this.localNonce) + .addUint8Array(this.localPassword).toDC()); + + + if (remoteHash.sequenceEqual(authPacket.hash)) { + // send establish request + this.sendParams().addUint8(0x20).addUint16(0).done(); + } + else { + this.sendParams().addUint8(0xc0).addUint32(1).addUint16(5).addString("Error").done(); + } + } + else if (authPacket.action == IIPAuthPacketAction.ConnectionEstablished) { + this.session.id = authPacket.sessionId; + this.ready = true; + this._emit("ready", this); + } + } + else if (authPacket.command == IIPAuthPacketCommand.Error) + { + this._emit("error", this, authPacket.errorCode, authPacket.errorMessage); + this.close(); + } + } + } + } + + return offset; + + //if (offset < ends) + // this.processPacket(msg, offset, ends, data); + } + receive(data) { var msg = data.read(); var offset = 0; var ends = msg.length; var packet = this.packet; - var authPacket = this.authPacket; //console.log("Data"); while (offset < ends) { - - if (this.ready) { - var rt = packet.parse(msg, offset, ends); - if (rt <= 0) { - data.holdFor(msg, offset, ends - offset, -rt); - return; - } - else { - offset += rt; - - if (packet.command == IIPPacketCommand.Event) { - switch (packet.event) { - case IIPPacketEvent.ResourceReassigned: - this.IIPEventResourceReassigned(packet.resourceId, packet.newResourceId); - break; - case IIPPacketEvent.ResourceDestroyed: - this.IIPEventResourceDestroyed(packet.resourceId); - break; - case IIPPacketEvent.PropertyUpdated: - this.IIPEventPropertyUpdated(packet.resourceId, packet.methodIndex, packet.content); - break; - case IIPPacketEvent.EventOccured: - this.IIPEventEventOccured(packet.resourceId, packet.methodIndex, packet.content); - break; - } - } - else if (packet.command == IIPPacketCommand.Request) { - switch (packet.action) { - case IIPPacketAction.AttachResource: - this.IIPRequestAttachResource(packet.callbackId, packet.resourceId); - break; - case IIPPacketAction.ReattachResource: - this.IIPRequestReattachResource(packet.callbackId, packet.resourceId, packet.resourceAge); - break; - case IIPPacketAction.DetachResource: - this.IIPRequestDetachResource(packet.callbackId, packet.resourceId); - break; - case IIPPacketAction.CreateResource: - this.IIPRequestCreateResource(packet.callbackId, packet.className); - break; - case IIPPacketAction.DeleteResource: - this.IIPRequestDeleteResource(packet.callbackId, packet.resourceId); - break; - case IIPPacketAction.TemplateFromClassName: - this.IIPRequestTemplateFromClassName(packet.callbackId, packet.className); - break; - case IIPPacketAction.TemplateFromClassId: - this.IIPRequestTemplateFromClassId(packet.callbackId, packet.classId); - break; - case IIPPacketAction.TemplateFromResourceLink: - this.IIPRequestTemplateFromResourceLink(packet.callbackId, packet.resourceLink); - break; - case IIPPacketAction.TemplateFromResourceId: - this.IIPRequestTemplateFromResourceId(packet.callbackId, packet.resourceId); - break; - case IIPPacketAction.ResourceIdFromResourceLink: - this.IIPRequestResourceIdFromResourceLink(packet.callbackId, packet.resourceLink); - break; - case IIPPacketAction.InvokeFunction: - this.IIPRequestInvokeFunction(packet.callbackId, packet.resourceId, packet.methodIndex, packet.content); - break; - case IIPPacketAction.GetProperty: - this.IIPRequestGetProperty(packet.callbackId, packet.resourceId, packet.methodIndex); - break; - case IIPPacketAction.GetPropertyIfModified: - this.IIPRequestGetPropertyIfModifiedSince(packet.callbackId, packet.resourceId, packet.methodIndex, packet.resourceAge); - break; - case IIPPacketAction.SetProperty: - this.IIPRequestSetProperty(packet.callbackId, packet.resourceId, packet.methodIndex, packet.content); - break; - } - } - else if (packet.command == IIPPacketCommand.Reply) { - switch (packet.action) { - case IIPPacketAction.AttachResource: - this.IIPReply(packet.callbackId, packet.classId, packet.resourceAge, packet.resourceLink, packet.content); - break; - case IIPPacketAction.ReattachResource: - this.IIPReply(packet.callbackId, packet.resourceAge, packet.content); - break; - case IIPPacketAction.DetachResource: - this.IIPReply(packet.callbackId); - break; - case IIPPacketAction.CreateResource: - this.IIPReply(packet.callbackId, packet.classId, packet.resourceId); - break; - case IIPPacketAction.DeleteResource: - this.IIPReply(packet.callbackId); - break; - case IIPPacketAction.TemplateFromClassName: - this.IIPReply(packet.callbackId, ResourceTemplate.parse(packet.content)); - break; - case IIPPacketAction.TemplateFromClassId: - this.IIPReply(packet.callbackId, ResourceTemplate.parse(packet.content)); - break; - case IIPPacketAction.TemplateFromResourceLink: - this.IIPReply(packet.callbackId, ResourceTemplate.parse(packet.content)); - break; - case IIPPacketAction.TemplateFromResourceId: - this.IIPReply(packet.callbackId, ResourceTemplate.parse(packet.content)); - break; - case IIPPacketAction.ResourceIdFromResourceLink: - this.IIPReply(packet.callbackId, packet.classId, packet.resourceId, packet.resourceAge); - break; - case IIPPacketAction.InvokeFunction: - this.IIPReply(packet.callbackId, packet.content); - break; - case IIPPacketAction.GetProperty: - this.IIPReply(packet.callbackId, packet.content); - break; - case IIPPacketAction.GetPropertyIfModified: - this.IIPReply(packet.callbackId, packet.content); - break; - case IIPPacketAction.SetProperty: - this.IIPReply(packet.callbackId); - break; - } - - } - - } - } - - else { - var rt = authPacket.parse(msg, offset, ends); - - - if (rt <= 0) { - data.holdAllFor(msg, ends - rt); - return; - } - else { - offset += rt; - - if (this.hostType == AuthenticationType.Host) { - if (authPacket.command == IIPAuthPacketCommand.Declare) { - if (authPacket.remoteMethod == IIPAuthPacketMethod.credentials - && authPacket.localMethod == IIPAuthPacketMethod.None) { - this.remoteUsername = authPacket.remoteUsername; - this.remoteNonce = authPacket.remoteNonce; - this.domain = authPacket.domain; - this.sendParams().addUint8(0xa0).addUint8Array(this.localNonce).done(); - } - } - else if (authPacket.command == IIPAuthPacketCommand.Action) { - if (authPacket.action == IIPAuthPacketAction.AuthenticateHash) { - var remoteHash = authPacket.hash; - - this.server.membership.getPassword(this.remoteUsername, this.domain).then(function (pw) { - if (pw != null) { - - var hash = new DC(sha256.arrayBuffer(BL().addString(pw).addUint8Array(remoteNonce).addUint8Array(this.localNonce).toArray())); - - - if (hash.sequenceEqual(remoteHash)) { - // send our hash - var localHash = new DC(sha256.arrayBuffer((new BinaryList()).addUint8Array(this.localNonce).addUint8Array(remoteNonce).addUint8Array(pw).toArray())); - this.sendParams().addUint8(0).addUint8Array(localHash).done(); - - this.readyToEstablish = true; - } - else { - // incorrect password - this.sendParams().addUint8(0xc0).addInt32(1).addUint16(5).addString("Error").done(); - } - } - }); - } - else if (authPacket.action == IIPAuthPacketAction.NewConnection) { - if (readyToEstablish) { - this.sessionId = new DC(32); - window.crypto.getRandomValues(this.sessionId); - - this.sendParams().addUint8(0x28).addUint8Array(this.sessionId).done(); - this.ready = true; - this._emit("ready", this); - } - } - } - } - else if (this.hostType == AuthenticationType.Client) { - if (authPacket.command == IIPAuthPacketCommand.Acknowledge) { - this.remoteNonce = authPacket.remoteNonce; - - // send our hash - - var localHash = new DC(sha256.arrayBuffer(BL().addUint8Array(this.localPassword) - .addUint8Array(this.localNonce) - .addUint8Array(this.remoteNonce).toArray())); - this.sendParams().addUint8(0).addUint8Array(localHash).done(); - } - else if (authPacket.command == IIPAuthPacketCommand.Action) { - if (authPacket.action == IIPAuthPacketAction.AuthenticateHash) { - // check if the server knows my password - var remoteHash = new DC(sha256.arrayBuffer(BL().addUint8Array(this.remoteNonce) - .addUint8Array(this.localNonce) - .addUint8Array(this.localPassword).toArray() - )); - - if (remoteHash.sequenceEqual(authPacket.hash)) { - // send establish request - this.sendParams().addUint8(0x20).addUint16(0).done(); - } - else { - this.sendParams().addUint8(0xc0).addUint32(1).addUint16(5).addString("Error").done(); - } - } - else if (authPacket.action == IIPAuthPacketAction.ConnectionEstablished) { - this.sessionId = authPacket.sessionId; - this.ready = true; - this._emit("ready", this); - } - } - else if (authPacket.command == IIPAuthPacketCommand.Error) - { - this._emit("error", this, authPacket.errorCode, authPacket.errorMessage); - this.close(); - } - } - } - } + offset = this.processPacket(msg, offset, ends, data); } + } - close() + close(event) { - this.socket.close(); + this._emit("close", event); + + Warehouse.remove(this); + + if (this.socket.readyState != this.socket.CLOSED) + { + this.socket.close(); + } } trigger(trigger) { @@ -2450,10 +3937,14 @@ class DistributedConnection extends IStore { return true; } + remove(resource) + { + // nothing to do (IStore interface) + } // Protocol Implementation - sendRequest(action, binaryList) { + sendRequest2(action, binaryList) { var reply = new AsyncReply(); this.callbackCounter++; this.sendParams().addUint8(0x40 | action).addUint32(this.callbackCounter).addRange(binaryList).done(); @@ -2461,13 +3952,119 @@ class DistributedConnection extends IStore { return reply; } + sendRequest(action) { + var reply = new AsyncReply(); + this.callbackCounter++; + this.requests[this.callbackCounter] = reply; + return this.sendParams(reply).addUint8(0x40 | action).addUint32(this.callbackCounter); + } + + sendInvoke(instanceId, index, parameters) + { + var reply = new AsyncReply(); + + var pb = Codec.composeVarArray(parameters, this, true); + + this.callbackCounter++; + this.sendParams() + .addUint8(0x40 | IIPPacketAction.InvokeFunction) + .addUint32(this.callbackCounter) + .addUint32(instanceId) + .addUint8(index) + .addUint8Array(pb) + .done(); + + this.requests[this.callbackCounter] = reply; + + return reply; + } + + sendError(type, callbackId, errorCode, errorMessage = "") + { + var msg = DC.stringToBytes(errorMessage); + if (type == ErrorType.Management) + this.sendParams() + .addUint8(0xC0 | IIPPacketReport.ManagementError) + .addUint32(callbackId) + .addUint16(errorCode) + .done(); + else if (type == ErrorType.Exception) + this.sendParams() + .addUint8(0xC0 | IIPPacketReport.ExecutionError) + .addUint32(callbackId) + .addUint16(errorCode) + .addUint16(msg.length) + .addUint8Array(msg) + .done(); + } + + sendProgress(callbackId, value, max) + { + this.sendParams() + .addUint8(0xC0 | IIPPacketReport.ProgressReport) + .addUint32(callbackId) + .addInt32(value) + .addInt32(max) + .done(); + } + + sendChunk(callbackId, chunk) + { + var c = Codec.compose(chunk, this, true); + this.sendParams() + .addUint8(0xC0 | IIPPacketReport.ChunkStream) + .addUint32(callbackId) + .addUint8Array(c) + .done(); + } + IIPReply(callbackId) { + var results = Array.prototype.slice.call(arguments, 1); var req = this.requests[callbackId]; + + //console.log("Reply " + callbackId, req); + delete this.requests[callbackId]; req.trigger(results); } + IIPReplyInvoke(callbackId, result) + { + var req = this.requests[callbackId]; + delete this.requests[callbackId]; + + Codec.parse(result, 0, {}, this).then(function(rt) + { + req.trigger(rt); + }); + } + + IIPReportError(callbackId, errorType, errorCode, errorMessage) + { + var req = this.requests[callbackId]; + delete this.requests[callbackId]; + req.triggerError(errorType, errorCode, errorMessage); + } + + IIPReportProgress(callbackId, type, value, max) + { + var req = this.requests[callbackId]; + req.triggerProgress(type, value, max); + } + + IIPReportChunk(callbackId, data) + { + if (this.requests[callbackId]) + { + var req = this.requests[callbackId]; + Codec.parse(data, 0, {}, this).then(function(x) + { + req.triggerChunk(x); + }); + } + } + IIPEventResourceReassigned(resourceId, newResourceId) { } @@ -2481,203 +4078,367 @@ class DistributedConnection extends IStore { } IIPEventPropertyUpdated(resourceId, index, content) { - if (this.resources[resourceId]) { - // push to the queue to gaurantee serialization - var reply = new AsyncReply(); - this.queue.add(reply); - var r = this.resources[resourceId]; - Codec.parse(content, 0, this).then(function (args) { - var pt = r._p.template.getPropertyTemplateByIndex(index); - if (pt != null) { - reply.trigger(new DistributedResourceQueueItem(r, DistributedResourceQueueItemType.Propery, args, index)); - } - else { // ft found, fi not found, this should never happen - this.queue.remove(reply); - } - }); - } + var self = this; + + this.fetch(resourceId).then(function(r){ + // push to the queue to gaurantee serialization + var item = new AsyncReply(); + self.queue.add(item); + + Codec.parse(content, 0, {}, self).then(function (args) { + var pt = r.instance.template.getPropertyTemplateByIndex(index); + if (pt != null) { + item.trigger(new DistributedResourceQueueItem(r, DistributedResourceQueueItemType.Propery, args, index)); + } + else { // ft found, fi not found, this should never happen + self.queue.remove(item); + } + }); + }); } - IIPEventEventOccured(resourceId, index, content) { - if (this.resources[resourceId]) { + IIPEventEventOccurred(resourceId, index, content) { + var self = this; + + this.fetch(resourceId).then(function(r){ // push to the queue to guarantee serialization - var reply = new AsyncReply(); - var r = this.resources[resourceId]; + var item = new AsyncReply(); + var r = self.resources[resourceId]; - this.queue.add(reply); + self.queue.add(item); - Codec.parseVarArray(content, 0, content.length, this).then(function (args) { - var et = r._p.template.getEventTemplateByIndex(index); + Codec.parseVarArray(content, 0, content.length, self).then(function (args) { + var et = r.instance.template.getEventTemplateByIndex(index); if (et != null) { - reply.trigger(new DistributedResourceQueueItem(r, DistributedResourceQueueItemType.Event, args, index)); + item.trigger(new DistributedResourceQueueItem(r, DistributedResourceQueueItemType.Event, args, index)); } else { // ft found, fi not found, this should never happen - this.queue.remove(reply); + self.queue.remove(item); } }); - } + }); + } + + IIPEventChildAdded(resourceId, childId) + { + var self = this; + + this.fetch(resourceId).then(function(parent) + { + self.fetch(childId).then(function(child) + { + parent.instance.children.add(child); + }); + }); + } + + IIPEventChildRemoved(resourceId, childId) + { + var self = this; + + this.fetch(resourceId).then(function(parent) + { + self.fetch(childId).then(function(child) + { + parent.instance.children.remove(child); + }); + }); + } + + IIPEventRenamed(resourceId, name) + { + this.fetch(resourceId).then(function(resource) + { + resource.instance.attributes.set("name", name.getString(0, name.length)); + }); + } + + + IIPEventAttributesUpdated(resourceId, attributes) + { + var self = this; + + this.fetch(resourceId).then(function(resource) + { + var attrs = attributes.getStringArray(0, attributes.length); + + self.getAttributes(resource, attrs).then(function(s) + { + resource.instance.setAttributes(s); + }); + }); + } + + sendReply(action, callbackId) + { + return this.sendParams().addUint8(0x80 | action).addUint32(callbackId); + } + + sendEvent(evt) + { + return this.sendParams().addUint8(evt); } IIPRequestAttachResource(callback, resourceId) { - var sl = this.sendParams(); + //var sl = this.sendParams(); + var self = this; + Warehouse.get(resourceId).then(function (r) { if (r != null) { - r.instance.on("ResourceEventOccured", this.instance_eventOccured); - r.instance.on("ResourceModified", this.instance_propertyModified); - r.instance.on("ResourceDestroyed", this.instance_resourceDestroyed); + + if (r.instance.applicable(self.session, ActionType.Attach, null) == Ruling.Denied) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AttachDenied); + return; + } + + r.instance.on("ResourceEventOccurred", self.instance_eventOccurred, self); + r.instance.on("ResourceModified", self.instance_propertyModified, self); + r.instance.on("ResourceDestroyed", self.instance_resourceDestroyed, self); // reply ok var link = DC.stringToBytes(r.instance.link); - sl.addUint8(0x80) - .addUint32(callback) - .addUint8Array(r.instance.template.classId.value) - .addUint32(r.instance.age) - .addUint16(link.length) - .addUint8Array(link) - .addUint8Array(Codec.composeVarArray(r.instance.serialize(), this, true)) - .done(); + if (r instanceof DistributedResource) + self.sendReply(IIPPacketAction.AttachResource, callback) + .addUint8Array(r.instance.template.classId.value) + .addUint64(r.instance.age) + .addUint16(link.length) + .addUint8Array(link) + .addUint8Array(Codec.composePropertyValueArray(r._serialize(), self, true)) + .done(); + else + self.sendReply(IIPPacketAction.AttachResource, callback) + .addUint8Array(r.instance.template.classId.value) + .addUint64(r.instance.age) + .addUint16(link.length) + .addUint8Array(link) + .addUint8Array(Codec.composePropertyValueArray(r.instance.serialize(), self, true)) + .done(); } else { // reply failed - //SendParams(0x80, r.Instance.Id, r.Instance.Age, r.Instance.Serialize(false, this)); + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); } }); } IIPRequestReattachResource(callback, resourceId, resourceAge) { - var sl = this.sendParams(); + var self = this; Warehouse.get(resourceId).then(function (r) { if (res != null) { - r.instance.on("ResourceEventOccured", this.instance_eventOccured); - r.instance.on("ResourceModified", this.instance_propertyModified); - r.instance.on("ResourceDestroyed", this.instance_resourceDestroyed); + r.instance.on("ResourceEventOccurred", self.instance_eventOccurred, self); + r.instance.on("ResourceModified", self.instance_propertyModified, self); + r.instance.on("ResourceDestroyed", self.instance_resourceDestroyed, self); // reply ok - sl.addUint8(0x81) - .addUint32(callback) - .addUint32(r.instance.age) - .addUint8Array(Codec.composeVarArray(r.instance.serialize(), this, true)) - .done(); + self.sendReply(IIPPacketAction.ReattachResource, callback) + .addUint64(r.instance.age) + .addUint8Array(Codec.composePropertyValueArray(r.instance.serialize(), self, true)) + .done(); } else { // reply failed + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); } }); } IIPRequestDetachResource(callback, resourceId) { - var sl = this.sendParams(); + var self = this; Warehouse.get(resourceId).then(function (r) { if (r != null) { - r.instance.off("ResourceEventOccured", this.instance_eventOccured); - r.instance.off("ResourceModified", this.instance_propertyModified); - r.instance.off("ResourceDestroyed", this.instance_resourceDestroyed); + r.instance.off("ResourceEventOccurred", self.instance_eventOccurred); + r.instance.off("ResourceModified", self.instance_propertyModified); + r.instance.off("ResourceDestroyed", self.instance_resourceDestroyed); // reply ok - sl.addUint8(0x82).addUint32(callback).done(); + self.sendReply(IIPPacketAction.DetachResource, callback).done(); } else { // reply failed + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); } }); } - IIPRequestCreateResource(callback, className) { - // not implemented + IIPRequestCreateResource(callback, storeId, parentId, content) { + var self = this; + Warehouse.get(storeId).then(function(store) + { + if (store == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.StoreNotFound); + return; + } + + if (!(store instanceof IStore)) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceIsNotStore); + return; + } + + // check security + if (store.instance.applicable(self.session, ActionType.CreateResource, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.CreateDenied); + return; + } + + Warehouse.get(parentId).then(function(parent) + { + + // check security + + if (parent != null) + if (parent.instance.applicable(self.session, ActionType.AddChild, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AddChildDenied); + return; + } + + var 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 = window[className]; + + if (type == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ClassNotFound); + return; + } + + Codec.parseVarArray(content, offset, cl, self).then(function(parameters) + { + offset += cl; + cl = content.getUint32(offset); + Codec.parseStructure(content, offset, cl, self).then(function(attributes) + { + offset += cl; + cl = content.length - offset; + + Codec.parseStructure(content, offset, cl, self).then(function(values) + { + + + var resource = new (Function.prototype.bind.apply(type, values)); + + Warehouse.put(resource, name, store, parent); + + + self.sendReply(IIPPacketAction.CreateResource, callback) + .addUint32(resource.Instance.Id) + .done(); + + }); + }); + }); + }); + }); } IIPRequestDeleteResource(callback, resourceId) { - // not implemented + var self = this; + Warehouse.get(resourceId).then(function(r) + { + if (r == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (r.instance.store.instance.applicable(session, ActionType.Delete, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.DeleteDenied); + return; + } + + if (Warehouse.remove(r)) + self.sendReply(IIPPacketAction.DeleteResource, callback).done(); + else + self.sendError(ErrorType.Management, callback, ExceptionCode.DeleteFailed); + }); } IIPRequestTemplateFromClassName(callback, className) { - var sl = this.sendParams(); + + var self = this; Warehouse.getTemplateByClassName(className).then(function (t) { if (t != null) - sl.addUint8(0x88).addUint32(callback).addUint8Array(t.content).done(); + self.sendReply(IIPPacketAction.TemplateFromClassName, callback) + .addUint32(t.content.length) + .addUint8Array(t.content) + .done(); else { // reply failed + self.sendError(ErrorType.Management, callback, ExceptionCode.TemplateNotFound); } }); } IIPRequestTemplateFromClassId(callback, classId) { - var sl = this.sendParams(); - + var self = this; Warehouse.getTemplateByClassId(classId).then(function (t) { if (t != null) - sl.addUint8(0x89) - .addUint32(callback) - .addUint32(t.content.length) - .addUint8Array(t.content) - .done(); - else { - // reply failed - } - }); - } - - IIPRequestTemplateFromResourceLink(callback, resourceLink) { - var sl = this.sendParams(); - - Warehouse.getTemplate(resourceLink).then(function (t) { - if (t != null) - sl.addUint8(0x8a).addUint32(callback).addUint8Array(t.content).done(); + self.sendReply(IIPPacketAction.TemplateFromClassId, callback) + .addUint32(t.content.length) + .addUint8Array(t.content) + .done(); else { // reply failed + self.sendError(ErrorType.Management, callback, ExceptionCode.TemplateNotFound); } }); } IIPRequestTemplateFromResourceId(callback, resourceId) { - var sl = this.sendParams(); + + var self = this; Warehouse.get(resourceId).then(function (r) { if (r != null) - sl.addUint8(0x8b).addUint32(callback).addUint8Array(r.instance.template.content).done(); - else { - // reply failed - } - }); - } - - IIPRequestResourceIdFromResourceLink(callback, resourceLink) { - - var sl = this.sendParams(); - - Warehouse.get(resourceLink).then(function (r) { - if (r != null) - sl.addUint8(0x8c) - .addUint32(callback) - .addUint8Array(r.instance.template.classId.value) - .addUint32(r.instance.id) - .addUint32(r.instance.age).done(); + self.sendReply(IIPPacketAction.TemplateFromResourceId, callback) + .addUint32(r.instance.template.content.length) + .addUint8Array(r.instance.template.content) + .done(); else { // reply failed + self.sendError(ErrorType.Management, callback, ExceptionCode.TemplateNotFound); } }); } IIPRequestInvokeFunction(callback, resourceId, index, content) { - var sl = this.sendParams(); + + var self = this; Warehouse.get(resourceId).then(function (r) { if (r != null) { - Codec.parseVarArray(content, 0, content.length, sl.connection).then(function (args) { + Codec.parseVarArray(content, 0, content.length, self).then(function (args) { var ft = r.instance.template.getFunctionTemplateByIndex(index); if (ft != null) { if (r instanceof DistributedResource) { var rt = r._invoke(index, args); if (rt != null) { rt.then(function (res) { - sl.addUint8(0x90).addUint32(callback).addUint8Array(Codec.compose(res, sl.connection)).done(); + self.sendReply(IIPPacketAction.InvokeFunction, callback) + .addUint8Array(Codec.compose(res, self)) + .done(); }); } else { @@ -2687,19 +4448,30 @@ class DistributedConnection extends IStore { else { var fi = r[ft.name]; + + if (r.instance.applicable(self.session, ActionType.Execute, ft) == Ruling.Denied) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.InvokeDenied); + return; + } + if (fi instanceof Function) { - args.push(sl.connection); + args.push(self); var rt = fi.apply(r, args); if (rt instanceof AsyncReply) { rt.then(function (res) { - sl.addUint8(0x90).addUint32(callback).addUint8Array(Codec.compose(res, sl.connection)).done(); + self.sendReply(IIPPacketAction.InvokeFunction, callback) + .addUint8Array(Codec.compose(res, self)) + .done(); }); } else { - sl.addUint8(0x90).addUint32(callback).addUint8Array(Codec.compose(rt, sl.connection)).done(); + self.sendReply(IIPPacketAction.InvokeFunction, callback) + .addUint8Array(Codec.compose(rt, self)) + .done(); } } else { @@ -2719,18 +4491,23 @@ class DistributedConnection extends IStore { } IIPRequestGetProperty(callback, resourceId, index) { - var sl = this.sendParams(); + + var self = this; Warehouse.get(resourceId).then(function (r) { if (r != null) { var pt = r.instance.template.getFunctionTemplateByIndex(index); if (pt != null) { if (r instanceof DistributedResource) { - sl.addUint8(0x91).addUint32(callback).addUint8Array(Codec.compose(r._get(pt.index), sl.connection)).done(); + self.sendReply(IIPPacketAction.GetProperty, callback) + .addUint8Array(Codec.compose(r._get(pt.index), self)) + .done(); } else { var pv = r[pt.name]; - sl.addUint8(0x91).addUint32(callback).addUint8Array(Codec.compose(pv, sl.connection)).done(); + self.sendReply(IIPPacketAction.GetProperty) + .addUint8Array(Codec.compose(pv, self)) + .done(); } } else { @@ -2744,7 +4521,8 @@ class DistributedConnection extends IStore { } IIPRequestGetPropertyIfModifiedSince(callback, resourceId, index, age) { - var sl = this.sendParams(); + + var self = this; Warehouse.get(resourceId).then(function (r) { if (r != null) { @@ -2752,10 +4530,15 @@ class DistributedConnection extends IStore { if (pt != null) { if (r.instance.getAge(index) > age) { var pv = r[pt.name]; - sl.addUint8(0x92).addUint32(callback).addUint8Array(Codec.compose(pv, sl.connection)).done(); + self.sendReply(IIPPacketAction.GetPropertyIfModified, callback) + .addUint8Array(Codec.compose(pv, self)) + .done(); } - else { - sl.addUint8(0x92).addUint32(callback).addUint8(DataType.NotModified).done(); + else + { + self.sendReply(IIPPacketAction.GetPropertyIfModified, callback) + .addUint8(DataType.NotModified) + .done(); } } else { @@ -2769,7 +4552,8 @@ class DistributedConnection extends IStore { } IIPRequestSetProperty(callback, resourceId, index, content) { - var sl = this.sendParams(); + + var self = this; Warehouse.get(resourceId).then(function (r) { if (r != null) { @@ -2777,53 +4561,181 @@ class DistributedConnection extends IStore { var pt = r.instance.template.getPropertyTemplateByIndex(index); if (pt != null) { - Codec.parse(content, 0, this).then(function (value) { + Codec.parse(content, 0, {}, this).then(function (value) { if (r instanceof DistributedResource) { // propagation r._set(index, value).then(function (x) { - sl.addUint8(0x93).addUint32(callback).done(); + self.sendReply(IIPPacketAction.SetProperty, callback) + .done(); + }).error(function(x){ + self.sendError(x.type, callback, x.code, x.message) + .done(); }); } - else { - r[pt.name] = value; - sl.addUint8(0x93).addUint32(callback).done(); + else + { + if (r.instance.applicable(self.session, ActionType.SetProperty, pt) == Ruling.Denied) + { + self.sendError(AsyncReply.ErrorType.Exception, callback, ExceptionCode.SetPropertyDenied); + return; + } + + try + { + if (r[pt.name] instanceof DistributedPropertyContext) + value = new DistributedPropertyContext(this, value); + + r[pt.name] = value; + self.sendReply(IIPPacketAction.SetProperty, callback).done(); + } + catch(ex) + { + self.sendError(AsyncReply.ErrorType.Exception, callback, 0, ex.toString()).done(); + } } }); } else { // property not found + self.sendError(AsyncReply.ErrorType.Management, callback, ExceptionCode.PropertyNotFound).done(); } } else { // resource not found + self.sendError(AsyncReply.ErrorType.Management, callback, ExceptionCode.PropertyNotFound).done(); } }); } + IIPRequestInquireResourceHistory(callback, resourceId, fromDate, toDate) + { + var self = this; + Warehouse.get(resourceId).then(function(r) + { + if (r != null) + { + r.instance.store.getRecord(r, fromDate, toDate).then(function(results) + { + var history = Codec.composeHistory(results, self, true); + self.sendReply(IIPPacketAction.ResourceHistory, callback) + .addUint8Array(history) + .done(); + }); + } + }); + } + + IIPRequestQueryResources(callback, resourceLink) + { + var self = this; + + Warehouse.query(resourceLink).then(function(resources) + { + + var list = resources.filter(function(r){return r.instance.applicable(self.session, ActionType.Attach, null) != Ruling.Denied}); + + if (list.length == 0) + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + else + self.sendReply(IIPPacketAction.QueryLink, callback) + .addUint8Array(Codec.composeResourceArray(list, self, true)) + .done(); + }); + } + + create(store, parent, className, parameters, attributes, values) + { + var reply = new AsyncReply(); + var sb = DC.stringToBytes(className); + + var pkt = BL().addUint32(store.instance.id) + .addUint32(parent.instance.id) + .addUint32(sb.length) + .addUint8Array(sb) + .addUint8Array(Codec.composeVarArray(parameters, this, true)) + .addUint8Array(Codec.composeStructure(attributes, this, true, true, true)) + .addUint8Array(Codec.composeStructure(values, this)); + + pkt.addUint32(pkt.length, 8); + + this.sendRequest(IIPPacket.IIPPacketAction.CreateResource).addUint8Array(pkt.ToArray()).done().then(function(args) + { + var rid = args[0]; + + self.fetch(rid).then(function(r) + { + reply.trigger(r); + }); + + }); + + return reply; + } + + query(resourceLink) + { + var reply = new AsyncReply(); + var self = this; + + var sb = DC.stringToBytes(resourceLink); + + this.sendRequest(IIPPacketAction.QueryLink) + .addUint16(sb.length) + .addUint8Array(sb) + .done() + .then(function(args) + { + Codec.parseResourceArray(args[0], 0, args[0].length, self).then(function(resources) { + reply.trigger(resources); + }); + }).error(function(ex){ + reply.triggerError(ex); + }); + + return reply; + } getTemplate(classId) { - if (this.templates[classId]) - return new AsyncReply(this.templates[classId]); - else if (this.templateRequests[classId]) - return this.templateRequests[classId]; + if (this.templates.contains(classId)) + return new AsyncReply(this.templates.item(classId)); + else if (this.templateRequests.contains(classId)) + return this.templateRequests.item(classId); var reply = new AsyncReply(); - this.templateRequests[classId] = reply; + this.templateRequests.add(classId.valueOf(), reply); var self = this; - this.sendRequest(IIPPacketAction.TemplateFromClassId, BL().addUint8Array(classId.value)).then(function (rt) { - delete self.templateRequests[classId]; - self.templates[rt[0].classId] = rt[0]; - reply.trigger(rt[0]); - }); + this.sendRequest(IIPPacketAction.TemplateFromClassId) + .addUint8Array(classId.value) + .done() + .then(function (rt) { + self.templateRequests.remove(classId); + self.templates.add(rt[0].classId.valueOf(), rt[0]); + Warehouse.putTemplate(rt[0]); + reply.trigger(rt[0]); + }); return reply; } // IStore interface get(path) { + + var rt = new AsyncReply(); + + this.query(path).then(function(ar) + { + if (ar != null && ar.length > 0) + rt.trigger(ar[0]); + else + rt.trigger(null); + }).error(function(ex) {rt.triggerError(ex);}); + + return rt; + + /* if (this.pathRequests[path]) return this.pathRequests[path]; @@ -2834,9 +4746,11 @@ class DistributedConnection extends IStore { bl.addString(path); bl.addUint16(bl.length, 0); + var link = data.get var self = this; - this.sendRequest(IIPPacketAction.ResourceIdFromResourceLink, bl).then(function (rt) { + this.sendRequest(IIPPacketAction.ResourceIdFromResourceLink) + .addUint16(.then(function (rt) { delete self.pathRequests[path]; self.fetch(rt[1]).then(function (r) { @@ -2846,6 +4760,7 @@ class DistributedConnection extends IStore { return reply; + */ } retrieve(iid) { @@ -2859,7 +4774,9 @@ class DistributedConnection extends IStore { fetch(id) { if (this.resourceRequests[id] && this.resources[id]) { // dig for dead locks - return this.resourceRequests[id]; + // or not + return new AsyncReply(this.resources[id]); + //return this.resourceRequests[id]; } else if (this.resourceRequests[id]) return this.resourceRequests[id]; @@ -2868,78 +4785,454 @@ class DistributedConnection extends IStore { var reply = new AsyncReply(); + this.resourceRequests[id] = reply; + var self = this; - this.sendRequest(IIPPacketAction.AttachResource, BL().addUint32(id)).then(function (rt) { - self.getTemplate(rt[0]).then(function (tmp) { + this.sendRequest(IIPPacketAction.AttachResource) + .addUint32(id) + .done() + .then(function (rt) { + var dr = new DistributedResource(self, id, rt[1], rt[2]); + //var dr = new DistributedResource(self, tmp, id, rt[1], rt[2]); - var dr = new DistributedResource(self, tmp, id, rt[1], rt[2]); - Warehouse.put(dr, id.toString(), self); + self.getTemplate(rt[0]).then(function (tmp) { - Codec.parseVarArray(rt[3], 0, rt[3].length, self).then(function (ar) { - dr._attached(ar); - delete self.resourceRequests[id]; - reply.trigger(dr); - }); - }); - }); + // ClassId, ResourceAge, ResourceLink, Content + Warehouse.put(dr, id.toString(), self, null, tmp); + + + Codec.parsePropertyValueArray(rt[3], 0, rt[3].length, self).then(function (ar) { + dr._attached(ar); + delete self.resourceRequests[id]; + reply.trigger(dr); + }); + }); + }); return reply; } - instance_resourceDestroyed(resource) { - // compose the packet - this.sendParams().addUint8(0x1).addUint32(resource.instance.id).done(); + getRecord(resource, fromDate, toDate) + { + if (resource instanceof DistributedResource) + { + + if (resource._p.connection != this) + return new AsyncReply(null); + + var reply = new AsyncReply(); + + var self = this; + + this.sendRequest(IIPPacketAction.ResourceHistory) + .addUint32(resource._p.instanceId) + .addDateTime(fromDate).addDateTime(toDate) + .done() + .then(function(rt) + { + Codec.parseHistory(rt[0], 0, rt[0].length, resource, self).then(function(history) + { + reply.trigger(history); + }); + }); + + return reply; + } + else + return new AsyncReply(null); } - instance_propertyModified(resource, name, newValue, oldValue) { + instance_resourceDestroyed(resource) { + // compose the packet + this.sendEvent(IIPPacketEvent.ResourceDestroyed) + .addUint32(resource.instance.id) + .done(); + } + + instance_propertyModified(resource, name, newValue) { var pt = resource.instance.template.getPropertyTemplateByName(name); if (pt == null) return; - // compose the packet - if (newValue instanceof Function) - sendParams().addUint8(0x10) - .addUint32(resource.instance.id) - .addUint8(pt.index) - .addUint8Array(Codec.compose(newValue(this), this)) - .done(); - else - sendParams().addUint8(0x10) - .addUint32(resource.instance.id) - .addUint8(pt.index) - .addUint8Array(Codec.compose(newValue, this)) - .done(); + this.sendEvent(IIPPacketEvent.PropertyUpdated) + .addUint32(resource.instance.id) + .addUint8(pt.index) + .addUint8Array(Codec.compose(newValue, this)) + .done(); } - instance_eventOccured(resource, name, receivers, args) { + instance_eventOccurred(resource, issuer, receivers, name, args) { var et = resource.instance.template.getEventTemplateByName(name); if (et == null) return; if (receivers != null) - if (receivers.indexOf(this.remoteUsername) < 0) + if (receivers.indexOf(this.session) < 0) return; - var clientArgs = [];//new object[args.Length]; - for (var i = 0; i < args.Length; i++) - if (args[i] instanceof Function) - clientArgs[i] = args[i](this); - else - clientArgs[i] = args[i]; - + if (resource.instance.applicable(this.session, ActionType.ReceiveEvent, et, issuer) == Ruling.Denied) + return; // compose the packet - sendParams().addUint8(0x11) - .addUint32(resource.instance.id) - .addUint8(et.index) - .addUint8Array(Codec.composeVarArray(args, this, true)) - .done(); + this.sendEvent(IIPPacketEvent.EventOccurred) + .addUint32(resource.instance.id) + .addUint8(et.index) + .addUint8Array(Codec.composeVarArray(args, this, true)) + .done(); } + + + IIPRequestAddChild(callback, parentId, childId) + { + var self = this; + Warehouse.get(parentId).then(function(parent) + { + if (parent == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + Warehouse.get(childId).then(function(child) + { + if (child == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (parent.instance.applicable(self.session, ActionType.AddChild, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AddChildDenied); + return; + } + + if (child.instance.applicable(self.session, ActionType.AddParent, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AddParentDenied); + return; + } + + parent.instance.children.add(child); + + self.sendReply(IIPPacketAction.AddChild, callback) + .done(); + //child.Instance.Parents + }); + + }); + } + + IIPRequestRemoveChild(callback, parentId, childId) + { + var self = this; + + Warehouse.get(parentId).then(function(parent) + { + if (parent == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + Warehouse.get(childId).then(function(child) + { + if (child == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (parent.instance.applicable(self.session, ActionType.RemoveChild, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AddChildDenied); + return; + } + + if (child.instance.applicable(self.session, ActionType.RemoveParent, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AddParentDenied); + return; + } + + parent.instance.children.remove(child); + + self.sendReply(IIPPacketAction.RemoveChild, callback) + .done(); + //child.Instance.Parents + }); + + }); + } + + IIPRequestRenameResource(callback, resourceId, name) + { + var self = this; + Warehouse.get(resourceId).then(function(resource) + { + if (resource == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (resource.instance.applicable(self.session, ActionType.Rename, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.RenameDenied); + return; + } + + resource.instance.name = name.getString(0, name.length); + self.sendReply(IIPPacketAction.RenameResource, callback) + .done(); + }); + } + + IIPRequestResourceChildren(callback, resourceId) + { + var self = this; + Warehouse.get(resourceId).then(function(resource) + { + if (resource == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + self.sendReply(IIPPacketAction.ResourceChildren, callback) + .addUint8Array(Codec.composeResourceArray(resource.instance.children.toArray(), this, true)) + .done(); + + }); + } + + IIPRequestResourceParents(callback, resourceId) + { + var self = this; + + Warehouse.get(resourceId).then(function(resource) + { + if (resource == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + self.sendReply(IIPPacketAction.ResourceParents, callback) + .addUint8Array(Codec.composeResourceArray(resource.instance.parents.toArray(), this, true)) + .done(); + }); + } + + IIPRequestClearAttributes(callback, resourceId, attributes, all = false) + { + Warehouse.get(resourceId).then(function(r) + { + if (r == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (r.instance.store.instance.applicable(self.session, ActionType.UpdateAttributes, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.UpdateAttributeDenied); + return; + } + + var attrs = []; + + if (!all) + attrs = attributes.getStringArray(0, attributes.length); + + if (r.instance.removeAttributes(attrs)) + self.sendReply(all ? IIPPacketAction.ClearAllAttributes : IIPPacketAction.ClearAttributes, callback) + .done(); + else + self.sendError(AsyncReply.ErrorType.Management, callback, ExceptionCode.UpdateAttributeFailed); + + }); + } + + IIPRequestUpdateAttributes(callback, resourceId, attributes, clearAttributes = false) + { + var self = this; + + Warehouse.get(resourceId).then(function(r) + { + if (r == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (r.instance.store.instance.applicable(self.session, ActionType.UpdateAttributes, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.UpdateAttributeDenied); + return; + } + + Codec.parseStructure(attributes, 0, attributes.Length, this).then(function(attrs) { + if (r.instance.setAttributes(attrs, clearAttributes)) + self.sendReply(clearAttributes ? IIPPacketAction.ClearAllAttributes : IIPPacketAction.ClearAttributes, + callback) + .done(); + else + self.sendError(ErrorType.Management, callback, ExceptionCode.UpdateAttributeFailed); + }); + + }); + + } + + + + getChildren(resource) + { + if (resource._p.connection != this) + return new AsyncReply(null); + + var rt = new AsyncReply(); + var self = this; + + this.sendRequest(IIPPacketAction.ResourceChildren) + .addUint32(resource._p.instanceId) + .done() + .then(function(d) + { + + Codec.parseResourceArray(d, 0, d.length, self).then(function(resources) + { + rt.trigger(resources); + }).error(function(ex) { rt.triggerError(ex); }); + }); + + return rt; + } + + getParents(resource) + { + if (resource._p.connection != this) + return new AsyncReply(null); + + var rt = new AsyncReply(); + var self = this; + + this.sendRequest(IIPPacketAction.ResourceParents) + .addUint32(resource._p.instanceId) + .done() + .then(function(d) + { + Codec.parseResourceArray(d, 0, d.length, this).then(function(resources) + { + rt.trigger(resources); + }).error(function(ex) { rt.triggerError(ex);}); + }); + + return rt; + } + + removeAttributes(resource, attributes = null) + { + if (resource._p.connection != this) + return new AsyncReply(null); + + var rt = new AsyncReply(); + + if (attributes == null) + this.sendRequest(IIPPacketAction.ClearAllAttributes) + .addUint32(resource._p.instanceId) + .done() + .then(function(ar) + { + rt.trigger(true); + }).error(function(ex) { rt.triggerError(ex); }); + else + { + var attrs = DC.stringArrayToBytes(attributes); + this.sendRequest(IIPPacketAction.ClearAttributes) + .addUint32(resource.instance.id) + .addUint32(attrs.length) + .addUint8Array(attrs) + .done() + .then(function(ar) + { + rt.trigger(true); + }).error(function(ex) { rt.triggerChunk(ex); }); + } + + return rt; + } + + setAttributes(resource, attributes, clearAttributes = false) + { + if (resource._p.connection != this) + return new AsyncReply(null); + + var rt = new AsyncReply(); + + this.sendRequest(clearAttributes ? IIPPacketAction.UpdateAllAttributes : IIPPacketAction.UpdateAttributes) + .addUint32(resource._p.instanceId) + .addUint8Array(Codec.composeStructure(attributes, this, true, true, true)) + .done() + .then(function(ar) + { + rt.trigger(true); + }).error(function(ex) {rt.triggerError(ex);}); + + return rt; + } + + getAttributes(resource, attributes = null) + { + if (resource._p.connection != this) + return new AsyncReply(null); + + var rt = new AsyncReply(); + var self = this; + + if (attributes == null) + { + this.sendRequest(IIPPacketAction.GetAllAttributes) + .addUint32(resource._p.instanceId) + .done() + .then(function(ar) + { + Codec.parseStructure(ar[0], 0, ar[0].length, this).then(function(st) + { + for (var a in st) + resource.instance.attributes.set(a, st[a]); + rt.trigger(st); + }).error(function(ex) { rt.triggerError(ex); }); + }); + } + else + { + var attrs = DC.stringArrayToBytes(attributes); + this.sendRequest(IIPPacketAction.GetAttributes) + .addUint32(resource._p.instanceId) + .addUint32(attrs.length) + .addUint8Array(attrs) + .done() + .then(function(ar) + { + Codec.parseStructure(ar[0], 0, ar[0].length, self).then(function(st) + { + for (var a in st) + resource.instance.attributes.set(a, st[a]); + rt.trigger(st); + }).error(function(ex) { rt.triggerError(ex); }); + }); + } + + return rt; + } + } /* @@ -2968,18 +5261,17 @@ class DistributedConnection extends IStore { * Created by Ahmed Zamil on 25/07/2017. */ +"use strict"; class DistributedResource extends IResource { destroy() { this.destroyed = true; - this._emit("destroy"); + this._emit("destroy", this); } - - - constructor(connection, template, instanceId, age) + constructor(connection, instanceId, age, link) { super(); @@ -2988,20 +5280,38 @@ class DistributedResource extends IResource connection: connection, instanceId: instanceId, age: age, - template: template + link: link, + properties: [] }; } + _serialize() + { + var props = []; + + for (var i = 0; i < this._p.properties.length; i++) + props.push(new PropertyValue(this._p.properties[i], + this.instance.getAge(i), + this.instance.getModificationDate(i))); + + return props; + } + _attached(properties) { if (this._isAttached) return false; else - { - this._p.properties = properties; - this._p.ages = new Uint32Array(properties.length); - //this.events = [];//new [this.template.events.length]; + { + for(var i = 0; i < properties.length; i++) + { + this.instance.setAge(i, properties[i].age); + this.instance.setModificationDate(i, properties[i].date); + this._p.properties.push(properties[i].value); + } + + this._p.isAttached = true; var self = this; @@ -3027,15 +5337,15 @@ class DistributedResource extends IResource }; }; - for(var i = 0; i < this._p.template.functions.length; i++) + for(var i = 0; i < this.instance.template.functions.length; i++) { - var ft = this._p.template.functions[i]; + var ft = this.instance.template.functions[i]; this[ft.name] = makeFunc(ft.index); } - for(var i = 0; i < this._p.template.properties.length; i++) + for(var i = 0; i < this.instance.template.properties.length; i++) { - var pt = this._p.template.properties[i]; + var pt = this.instance.template.properties[i]; Object.defineProperty(this, pt.name, { get: makeGetter(pt.index), @@ -3051,18 +5361,21 @@ class DistributedResource extends IResource _emitEventByIndex(index, args) { - var et = this._p.template.getEventTemplateByIndex(index); - this._emit(et.name, args); - this.instance.emitResourceEvent(et.name, null, args); + var et = this.instance.template.getEventTemplateByIndex(index); + this._emitArgs(et.name, args); + this.instance._emitResourceEvent(null, null, et.name, args); } _invoke(index, args) { if (this.destroyed) throw new Exception("Trying to access destroyed object"); - if (index >= this._p.template.functions.length) + if (index >= this.instance.template.functions.length) throw new Exception("Function index is incorrect"); + return this._p.connection.sendInvoke(this._p.instanceId, index, args); + + /* var reply = new AsyncReply(); var parameters = Codec.composeVarArray(args, this._p.connection, true); @@ -3079,7 +5392,7 @@ class DistributedResource extends IResource return reply; - + */ } @@ -3091,12 +5404,11 @@ class DistributedResource extends IResource } - _updatePropertyByIndex(index, value) { - var pt = this._p.template.getPropertyTemplateByIndex(index); + var pt = this.instance.template.getPropertyTemplateByIndex(index); this._p.properties[index] = value; - this.instance.modified(pt.name, value); + this.instance.emitModification(pt, value); } _set(index, value) @@ -3109,15 +5421,15 @@ class DistributedResource extends IResource var parameters = Codec.compose(value, this._p.connection); var self = this; - this._p.connection.sendRequest(IIPPacketAction.SetProperty, - BL().addUint32(self._p.instanceId).addUint8(index).addUint8Array(parameters)) + this._p.connection.sendRequest(IIPPacketAction.SetProperty) + .addUint32(self._p.instanceId).addUint8(index).addUint8Array(parameters) + .done() .then(function(res) { // not really needed, server will always send property modified, this only happens if the programmer forgot to emit in property setter - //Update(index, value); + self._p.properties[index] = value; reply.trigger(null); - // nothing to do here - }); + }); return reply; } @@ -3147,7 +5459,10 @@ class DistributedResource extends IResource /** * Created by Ahmed Zamil on 25/07/2017. */ -var DistributedResourceQueueItemType = + +"use strict"; + +const DistributedResourceQueueItemType = { Propery: 0, Event: 1 @@ -3189,6 +5504,8 @@ class DistributedResourceQueueItem { * Created by Ahmed Zamil on 24/08/2017. */ +"use strict"; + class EventTemplate extends MemberTemplate { @@ -3239,6 +5556,8 @@ class EventTemplate extends MemberTemplate * Created by Ahmed Zamil on 27/08/2017. */ +"use strict"; + class FunctionTemplate extends MemberTemplate { compose() { var name = super.compose(); @@ -3247,12 +5566,12 @@ class FunctionTemplate extends MemberTemplate { if (this.expansion != null) { var exp = DC.stringToBytes(this.expansion); - return rt.addUint8(0x10 | (IsVoid ? 0x8 : 0x0)) + return rt.addUint8(0x10 | (this.isVoid ? 0x8 : 0x0)) .addUint32(exp.length).addUint8Array(exp) .addUint8(name.length).addUint8Array(name).toArray(); } else - return rt.addUint8(IsVoid ? 0x8 : 0x0).addUint8(name.length).addUint8Array(name).toArray(); + return rt.addUint8(this.isVoid ? 0x8 : 0x0).addUint8(name.length).addUint8Array(name).toArray(); } @@ -3287,6 +5606,9 @@ class FunctionTemplate extends MemberTemplate { /** * Created by Ahmed Zamil on 02/09/2017. */ + +"use strict"; + class Guid { constructor(dc) @@ -3324,7 +5646,10 @@ class Guid /** * Created by Ahmed Zamil on 25/07/2017. */ -var IIPAuthPacketCommand = + +"use strict"; + +const IIPAuthPacketCommand = { Action: 0, Declare: 1, @@ -3332,7 +5657,7 @@ var IIPAuthPacketCommand = Error: 3 }; -var IIPAuthPacketAction = +const IIPAuthPacketAction = { // Authenticate AuthenticateHash: 0, @@ -3342,7 +5667,7 @@ var IIPAuthPacketAction = }; -var IIPAuthPacketMethod = +const IIPAuthPacketMethod = { None: 0, Certificate: 1, @@ -3590,27 +5915,43 @@ class IIPAuthPacket * Created by Ahmed Zamil on 25/07/2017. */ +"use strict"; -var IIPPacketCommand = +const IIPPacketCommand = { Event: 0, Request: 1, Reply: 2, - Error: 3 + Report: 3 }; -var IIPPacketEvent = +const IIPPacketReport = +{ + ManagementError: 0, + ExecutionError: 1, + ProgressReport: 0x8, + ChunkStream: 0x9 +}; + +const IIPPacketEvent = { // Event Manage ResourceReassigned : 0, ResourceDestroyed: 1, + ChildAdded: 2, + ChildRemoved: 3, + Renamed: 4, // Event Invoke PropertyUpdated : 0x10, - EventOccured: 0x11 + EventOccurred: 0x11, + + // Attribute + AttributesUpdated: 0x18 + }; -var IIPPacketAction = +const IIPPacketAction = { // Request Manage AttachResource: 0, @@ -3618,25 +5959,35 @@ var IIPPacketAction = DetachResource: 2, CreateResource: 3, DeleteResource: 4, + AddChild: 5, + RemoveChild: 6, + RenameResource: 7, // Request Inquire - TemplateFromClassName: 0x8, - TemplateFromClassId: 0x9, - TemplateFromResourceLink: 0xA, - TemplateFromResourceId: 0xB, - ResourceIdFromResourceLink: 0xC, + TemplateFromClassName: 8, + TemplateFromClassId: 9, + TemplateFromResourceId: 10, + QueryLink: 11, + ResourceHistory: 12, + ResourceChildren: 13, + ResourceParents: 14, // Request Invoke - InvokeFunction: 0x10, - GetProperty: 0x11, - GetPropertyIfModified: 0x12, - SetProperty: 0x13 + InvokeFunction: 16, + GetProperty: 17, + GetPropertyIfModified: 18, + SetProperty: 19, + + // Request Attribute + GetAllAttributes: 24, + UpdateAllAttributes: 25, + ClearAllAttributes: 26, + GetAttributes: 27, + UpdateAttributes: 28, + ClearAttributes: 29 }; - - - class IIPPacket { constructor() @@ -3657,13 +6008,14 @@ class IIPPacket this.methodName = ""; this.callbackId = 0; this.dataLengthNeeded = 0; + this.originalOffset = 0; } notEnough(offset, ends, needed) { if (offset + needed > ends) { - this.dataLengthNeeded = needed - (ends - offset); + this.dataLengthNeeded = needed - (ends - this.originalOffset); return true; } else @@ -3672,7 +6024,7 @@ class IIPPacket parse(data, offset, ends) { - var oOffset = offset; + this.originalOffset = offset; if (this.notEnough(offset, ends, 1)) return -this.dataLengthNeeded; @@ -3689,6 +6041,16 @@ class IIPPacket this.resourceId = data.getUint32(offset); offset += 4; } + else if (this.command == IIPPacketCommand.Report) + { + this.report = (data.getUint8(offset++) & 0x3f); + + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + this.callbackId = data.getUint32(offset); + offset += 4; + } else { this.action = (data.getUint8(offset++) & 0x3f); @@ -3715,6 +6077,30 @@ class IIPPacket { // nothing to parse } + else if (this.event == IIPPacketEvent.ChildAdded + || this.event == IIPPacketEvent.ChildRemoved) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + this.childId = data.getUint32(offset); + offset += 4; + } + else if(this.event == IIPPacketEvent.Renamed) + { + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; + + var cl = data.getUint16(offset); + offset += 2; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset, cl); + + offset += cl; + } else if (this.event == IIPPacketEvent.PropertyUpdated) { if (this.notEnough(offset, ends, 2)) @@ -3748,7 +6134,7 @@ class IIPPacket offset += size; } } - else if (this.event == IIPPacketEvent.EventOccured) + else if (this.event == IIPPacketEvent.EventOccurred) { if (this.notEnough(offset, ends, 5)) return -this.dataLengthNeeded; @@ -3761,6 +6147,22 @@ class IIPPacket this.content = data.clip(offset, cl); offset += cl; } + // Attribute + else if (this.event == IIPPacketEvent.AttributesUpdated) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + var cl = data.getUint32(offset); + offset += 4; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset, cl); + + offset += cl; + } } else if (this.command == IIPPacketCommand.Request) { @@ -3774,14 +6176,14 @@ class IIPPacket } else if (this.action == IIPPacketAction.ReattachResource) { - if (this.notEnough(offset, ends, 8)) + if (this.notEnough(offset, ends, 12)) return -this.dataLengthNeeded; this.resourceId = data.getUint32(offset); offset += 4; - this.resourceAge = data.getUint32(offset); - offset += 4; + this.resourceAge = data.getUint64(offset); + offset += 8; } else if (this.action == IIPPacketAction.DetachResource) { @@ -3794,16 +6196,21 @@ class IIPPacket } else if (this.action == IIPPacketAction.CreateResource) { - if (this.notEnough(offset, ends, 1)) - return -this.dataLengthNeeded; + if (this.notEnough(offset, ends, 12)) + return -dataLengthNeeded; - var cl = data.getUint8(offset++); + this.storeId = data.getUint32(offset); + offset += 4; + this.resourceId = data.getUint32(offset); + offset += 4; + + var cl = data.getUint32(offset); + offset += 4; if (this.notEnough(offset, ends, cl)) - return -this.dataLengthNeeded; + return -dataLengthNeeded; - this.className = data.getString(offset, cl); - offset += cl; + this.content = data.clip(offset, cl); } else if (this.action == IIPPacketAction.DeleteResource) { @@ -3813,6 +6220,37 @@ class IIPPacket this.resourceId = data.getUint32(offset); offset += 4; + } + else if (this.action == IIPPacketAction.AddChild + || this.action == IIPPacketAction.RemoveChild) + { + if (this.notEnough(offset, ends, 8)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + this.childId = data.getUint32(offset); + offset += 4; + + } + else if (this.action == IIPPacketAction.RenameResource) + { + if (this.notEnough(offset, ends, 6)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + var cl = data.getUint16(offset); + offset += 2; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset, cl); + offset += cl; + } else if (this.action == IIPPacketAction.TemplateFromClassName) { @@ -3836,20 +6274,6 @@ class IIPPacket this.classId = data.getGuid(offset); offset += 16; } - else if (this.action == IIPPacketAction.TemplateFromResourceLink) - { - if (this.notEnough(offset, ends, 2)) - return -this.dataLengthNeeded; - - var cl = data.getUint16(offset); - offset += 2; - - if (this.notEnough(offset, ends, cl)) - return -this.dataLengthNeeded; - - this.resourceLink = data.getString(offset, cl); - offset += cl; - } else if (this.action == IIPPacketAction.TemplateFromResourceId) { if (this.notEnough(offset, ends, 4)) @@ -3858,7 +6282,7 @@ class IIPPacket this.resourceId = data.getUint32(offset); offset += 4; } - else if (this.action == IIPPacketAction.ResourceIdFromResourceLink) + else if (this.action == IIPPacketAction.QueryLink) { if (this.notEnough(offset, ends, 2)) return -this.dataLengthNeeded; @@ -3872,6 +6296,30 @@ class IIPPacket this.resourceLink = data.getString(offset, cl); offset += cl; } + else if (this.action == IIPPacketAction.ResourceChildren + || this.action == IIPPacketAction.ResourceParents) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + } + else if (this.action == IIPPacketAction.ResourceHistory) + { + if (this.notEnough(offset, ends, 20)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + this.fromDate = data.getDateTime(offset); + offset += 8; + + this.toDate = data.getDateTime(offset); + offset += 8; + + } else if (this.action == IIPPacketAction.InvokeFunction) { if (this.notEnough(offset, ends, 9)) @@ -3913,8 +6361,8 @@ class IIPPacket this.methodIndex = data[offset++]; - this.resourceAge = data.getUint32(offset); - offset += 4; + this.resourceAge = data.getUint64(offset); + offset += 8; } else if (this.action == IIPPacketAction.SetProperty) @@ -3954,6 +6402,28 @@ class IIPPacket offset += size; } } + + // Attribute + else if (this.action == IIPPacketAction.UpdateAllAttributes + || this.action == IIPPacketAction.GetAttributes + || this.action == IIPPacketAction.UpdateAttributes + || this.action == IIPPacketAction.ClearAttributes) + { + if (this.notEnough(offset, ends, 8)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + var cl = data.getUint32(offset); + offset += 4; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset, cl); + offset += cl; + } + } else if (this.command == IIPPacketCommand.Reply) { @@ -3966,8 +6436,8 @@ class IIPPacket this.classId = data.getGuid(offset); offset += 16; - this.resourceAge = data.getUint32(offset); - offset += 4; + this.resourceAge = data.getUint64(offset); + offset += 8; var cl = data.getUint16(offset); offset+=2; @@ -3999,9 +6469,6 @@ class IIPPacket if (this.notEnough(offset, ends, 20)) return -this.dataLengthNeeded; - this.classId = data.GetGuid(offset); - offset += 16; - this.resourceId = data.getUint32(offset); offset += 4; @@ -4012,8 +6479,14 @@ class IIPPacket } else if (this.action == IIPPacketAction.TemplateFromClassName || this.action == IIPPacketAction.TemplateFromClassId - || this.action == IIPPacketAction.TemplateFromResourceLink - || this.action == IIPPacketAction.TemplateFromResourceId) + || this.action == IIPPacketAction.TemplateFromResourceId + || this.action == IIPPacketAction.QueryLink + || this.action == IIPPacketAction.ResourceChildren + || this.action == IIPPacketAction.ResourceParents + || this.action == IIPPacketAction.ResourceHistory + // Attribute + || this.action == IIPPacketAction.GetAllAttributes + || this.action == IIPPacketAction.GetAttributes) { if (this.notEnough(offset, ends, 4)) return -this.dataLengthNeeded; @@ -4027,20 +6500,6 @@ class IIPPacket this.content = data.clip(offset, cl); offset += cl; } - else if (this.action == IIPPacketAction.ResourceIdFromResourceLink) - { - if (this.notEnough(offset, ends, 24)) - return -this.dataLengthNeeded; - - this.classId = data.getGuid(offset); - offset += 16; - - this.resourceId = data.getUint32(offset); - offset += 4; - - this.resourceAge = data.getUint32(offset); - offset += 4; - } else if (this.action == IIPPacketAction.InvokeFunction || this.action == IIPPacketAction.GetProperty || this.action == IIPPacketAction.GetPropertyIfModified) @@ -4079,33 +6538,77 @@ class IIPPacket // nothing to do } } - else if (this.command == IIPPacketCommand.Error) + else if (this.command == IIPPacketCommand.Report) { - // Error - if (this.notEnough(offset, ends, 4)) - return -this.dataLengthNeeded; + if (this.report == IIPPacketReport.ManagementError) + { + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; - this.callbackId = data.getUint32(offset); + this.errorCode = data.getUint16(offset); + offset += 2; + } + else if (this.report == IIPPacketReport.ExecutionError) + { + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; - if (this.notEnough(offset, ends, 1)) - return -this.dataLengthNeeded; + this.errorCode = data.getUint16(offset); + offset += 2; - this.errorCode = data.getUint8(offset++); + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; - if (this.notEnough(offset, ends, 4)) - return -this.dataLengthNeeded; + var cl = data.getUint16(offset); + offset += 2; - var cl = data.getUint32(offset); - offset += 4; + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; - if (this.notEnough(offset, ends, cl)) - return -this.dataLengthNeeded; + this.errorMessage = data.getString(offset, cl); + offset += cl; + } + else if (this.report == IIPPacketReport.ProgressReport) + { + if (this.notEnough(offset, ends, 8)) + return -this.dataLengthNeeded; - this.errorMessage = data.getString(offset, cl); - offset += cl; + this.progressValue = data.getInt32(offset); + offset += 4; + this.progressMax = data.getInt32(offset); + offset += 4; + } + else if (this.report == IIPPacketReport.ChunkStream) + { + var dt = data.getUint8(offset++); + var size = DataType.sizeOf(dt); + + if (size < 0) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + var cl = data.getUint32(offset); + offset += 4; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset - 5, cl + 5); + offset += cl; + } + else + { + if (this.notEnough(offset, ends, size)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset - 1, size + 1); + offset += size; + } + } } - return offset - oOffset; + return offset - this.originalOffset; } } /* @@ -4133,22 +6636,77 @@ class IIPPacket /** * Created by Ahmed Zamil on 29/08/2017. */ + +"use strict"; + class Instance extends IEventHandler { getAge(index) { - if (index < this.ages.Count) + if (index < this.ages.length) return this.ages[index]; else return 0; } + setAge(index, value) + { + if (index < this.ages.length) + { + this.ages[index] = value; + if (value > this.instanceAge) + this.instanceAge = value; + } + } + + getModificationDate(index) + { + if (index < this.modificationDates.length) + return this.modificationDates[index]; + else + return new Date(0); + } + + setModificationDate(index, value) + { + if (index < this.modificationDates.length) + { + this.modificationDates[index] = value; + + if (value > this.instanceModificationDate) + this.instanceModificationDate = value; + } + } + + loadProperty(name, age, modificationDate, value) + { + var pt = this.template.getPropertyTemplateByName(name); + + if (pt == null) + return false; + + this.resource[name] = value; + + this.setAge(pt.index, age); + this.setModificationDate(pt.index, modificationDate); + + return true; + } deserialize(properties) { - for(var i = 0; i < this.template.properties.length; i++) - this.resource[this.template.properties[i].name] = properties[i]; + + for (var i = 0; i < properties.length; i++) + { + var pt = this.template.GetPropertyTemplateByIndex(i); + if (pt != null) + { + var pv = properties[i]; + this.loadProperty(pt.name, pv.age, pv.date, pv.value); + } + } + return true; } @@ -4157,8 +6715,10 @@ class Instance extends IEventHandler var props = []; for (var i = 0; i < this.template.properties.length; i++) - props.push(this.resource[this.template.properties[i].name]); - + props.push(new PropertyValue(this.resource[this.template.properties[i].name], + this.ages[this.template.properties[i].index], + this.modificationDates[this.template.properties[i].index])); + return props; } @@ -4167,25 +6727,38 @@ class Instance extends IEventHandler return resource instanceof Storable; } + emitModification(pt, value) + { + this.instanceAge++; - modified(propertyName = null, newValue = null, oldValue = null) + var now = new Date(); + + this.ages[pt.index] = this.instanceAge; + this.modificationDates[pt.index] = now; + + if (pt.recordable) + this.store.record(this.resource, pt.name, value, this.ages[pt.index], now); + + super._emit("ResourceModified", this.resource, pt.name, value); + this.resource._emit("modified", pt.name, value); + } + + modified(propertyName = null) { if (propertyName == null) propertyName = modified.caller.name; - if (newValue == null) + var val = {}; + if (this.getPropertyValue(propertyName, val)) { - var val = {}; - if (this.getPropertyValue(propertyName, val)) - super._emit("ResourceModified", this.resource, propertyName, val.value, oldValue); + var pt = this.template.getPropertyTemplateByName(propertyName); + this.emitModification(pt, val.value) } - else - super._emit("ResourceModified", this.resource, propertyName, newValue, oldValue); } - emitResourceEvent(name, receivers, args) + _emitResourceEvent(issuer, receivers, name, args) { - super._emit("ResourceEventOccured", this.resource, name, receivers, args); + super._emit("ResourceEventOccurred", this.resource, issuer, receivers, name, args); } getPropertyValue(name, resultObject) @@ -4202,7 +6775,7 @@ class Instance extends IEventHandler - constructor(id, name, resource, store) + constructor(id, name, resource, store, customTemplate = null, age = 0) { super(); @@ -4211,8 +6784,14 @@ class Instance extends IEventHandler this.id = id; this.name = name; + this.instanceAge = age; + this.instanceModificationDate = new Date(0); + this.children = new AutoList(); this.parents = new AutoList(); + this.managers = new AutoList(); + + this.attributes = new KeyList(); var self = this; @@ -4229,144 +6808,160 @@ class Instance extends IEventHandler self._emit("ResourceDestroyed", sender); }); - this.template = Warehouse.getTemplateByType(this.resource.constructor); + if (customTemplate != null) + this.template = customTemplate; + else + this.template = Warehouse.getTemplateByType(this.resource.constructor); // set ages - this.ages = new Uint32Array(this.template.properties.length); + this.ages = []; + this.modificationDates = []; + for(var i = 0; i < this.template.properties.length; i++) + { + this.ages.push(0); + this.modificationDates.push(new Date(0)); + } // connect events - var makeHandler = function(name, receivers, args) - { - return new function(receivers, args) - { - self.emitResourceEvent(name, receivers, args); - }; - }; - for (var i = 0; i < this.template.events.length; i++) - this.resource.on(this.template.events[i].name, makeHandler(this.template.events[i].name)); - + this.resource.on(this.template.events[i].name, this._makeHandler(this.template.events[i].name)); + } -} - -/* -* 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. -*/ - -/** - * Created by Ahmed Zamil on 01/09/2017. - */ - -class NetworkBuffer { - constructor() { - this.neededDataLength = 0; - this.data = new DC(0); - } - - get protected() { - return this.neededDataLength > this.data.length; - } - - get available() { - - return this.data.length; - } - - holdAllForNextWrite(src) { - this.holdFor(src, src.length + 1); - } - - holdForNextWrite(src, offset, size) { - this.holdFor(src, offset, size, size + 1); + _makeHandler(name) + { + var self = this; + return function(args) + { + if (args instanceof CustomResourceEvent) + self._emitResourceEvent(args.issuer, args.receivers, name, args.params); + else + self._emitResourceEvent(null, null, name, args); + }; } - holdFor(src, offset, size, needed) { - if (size >= needed) - throw new Exception("Size >= Needed !"); - - this.data = DC.combine(src, offset, size, this.data, 0, this.data.length); - this.neededDataLength = needed; - } - - holdAllFor(src, needed) { - this.holdFor(src, 0, src.length, needed); - } - - protect(data, offset, needed) { - var dataLength = data.length - offset; - - // protection - if (dataLength < needed) { - this.holdFor(data, offset, dataLength, needed); - return true; + /// + /// Check for permission. + /// + /// Caller sessions. + /// Action type + /// Function or property to check for permission. + /// Ruling. + applicable(session, action, member, inquirer) + { + for (var i = 0; i < this.managers.length; i++) + { + var r = this.managers.item(i).applicable(this.resource, session, action, member, inquirer); + if (r != Ruling.DontCare) + return r; } + + return Ruling.DontCare; + } + + + + removeAttributes(attributes = null) + { + if (attributes == null) + this.attributes.clear(); else - return false; - } + { + for(var i = 0; i < attributes.length; i++) + this.attributes.remove(attributes[i]); + } - writeAll(src) { - this.write(src, 0, src.length ? src.length : src.byteLength); - } - - write(src, offset, length) { - this.data = this.data.append(src, offset, length); - } - - get canRead() { - if (this.data.length == 0) - return false; - else if (this.data.length < this.neededDataLength) - return false; return true; } - read() { - if (this.data.length == 0) - return null; + getAttributes(attributes = null) + { + var st = new Structure(); - var rt = null; - - if (this.neededDataLength == 0) { - rt = this.data; - this.data = new DC(0); - } - else { - if (this.data.length >= this.neededDataLength) { - rt = this.data; - this.data = new DC(0); - this.neededDataLength = 0; - return rt; - } - else { - return null; - } + if (attributes == null) + { + attributes = this.attributes.keys.slice(0); + attributes.push("managers"); } - return rt; + for(var i = 0; i < attributes.length; i++) + { + var attr = attributes[i]; + + if (attr == "name") + st["name"] = this.name; + + else if (attr == "managers") + { + var mngrs = new StructureArray(); + + for(var j = 0; j < this.managers.length; j++) + { + var manager = this.managers.item(j); + var sm = new Structure(); + sm["type"] = manager.constructor.name; + sm["settings"] = manager.settings; + + mngrs.push(sm); + } + + st["managers"] = mngrs; + + } + else + st[attr] = this.attributes.item(attr); + } + + return st; } -} - + + + setAttributes(attributes, clearAttributes = false) + { + + if (clearAttributes) + this.attributes.clear(); + + + for (var attr in attributes) + if (attr == "name") + this.name = attributes[attr]; + else if (attr == "managers") + { + this.managers.clear(); + + var mngrs = attributes[attr]; + + for (var i = 0; i < mngrs.length; i++) + { + var mngr = mngrs[i]; + + var type = window[mngr]; + + var settings = mngr["settings"]; + + var manager = new (Function.prototype.bind.apply(type)); + + if (manager instanceof IPermissionsManager) + { + manager.initialize(settings, this.resource); + this.managers.add(manager); + } + else + return false; + } + } + else + { + this.attributes.set(attr, attributes[attr]); + } + + + return true; + } +} /* * Copyright (c) 2017 Ahmed Kh. Zamil * @@ -4393,6 +6988,8 @@ class NetworkBuffer { * Created by Ahmed Zamil on 26/08/2017. */ +"use strict"; + class NotModified { @@ -4423,13 +7020,14 @@ class NotModified * Created by Ahmed Zamil on 27/08/2017. */ -var PropertyPermission = { +"use strict"; + +const PropertyPermission = { Read: 1, Write: 2, ReadWrite: 3 }; - class PropertyTemplate extends MemberTemplate { @@ -4443,12 +7041,13 @@ class PropertyTemplate extends MemberTemplate { var name = super.compose(); var rt = new BinaryList(); + var pv = (this.permission >> 1) | (this.recordable ? 1 : 0); if (this.writeExpansion != null && this.readExpansion != null) { var rexp = DC.stringToBytes(this.readExpansion); var wexp = DC.stringToBytes(this.writeExpansion); - return rt.addUint8(0x38 | this.permission) + return rt.addUint8(0x38 | pv) .addUint32(wexp.length) .addUint8Array(wexp) .addUint32(rexp.length) @@ -4459,7 +7058,7 @@ class PropertyTemplate extends MemberTemplate else if (this.writeExpansion != null) { var wexp = DC.stringToBytes(this.writeExpansion); - return rt.addUint8(0x30 | this.permission) + return rt.addUint8(0x30 | pv) .addUint32(wexp.length) .addUint8Array(wexp) .addUint8(name.length) @@ -4468,14 +7067,14 @@ class PropertyTemplate extends MemberTemplate else if (this.readExpansion != null) { var rexp = DC.stringToBytes(this.readExpansion); - return rt.addUint8(0x28 | this.permission) + return rt.addUint8(0x28 | pv) .addUint32(rexp.length) .addUint8Array(rexp) .addUint8(name.length) .addUint8Array(name).toArray(); } else - return rt.addUint8(0x20 | this.permission) + return rt.addUint8(0x20 | pv) .addUint32(name.length) .addUint8Array(name).toArray(); } @@ -4503,18 +7102,9 @@ class PropertyTemplate extends MemberTemplate * SOFTWARE. */ -class ResourceTemplate { +"use strict"; - getMemberTemplate(member) { - if (member instanceof MethodInfo) - return this.getFunctionTemplate(member.name); - else if (member instanceof EventInfo) - return this.getEventTemplate(member.name); - else if (member instanceof PropertyInfo) - return this.getPropertyTemplate(member.name); - else - return null; - } +class ResourceTemplate { getEventTemplateByName(eventName) { for (var i = 0; i < this.events.length; i++) @@ -4590,7 +7180,7 @@ class ResourceTemplate { // set guid this.className = template.namespace + "." + type.prototype.constructor.name; - this.classId = (new DC(sha256.arrayBuffer(this.className))).getGuid(0); + this.classId = SHA256.compute(DC.stringToBytes(this.className)).getGuid(0); //byte currentIndex = 0; @@ -4600,6 +7190,7 @@ class ResourceTemplate { pt.index = i; pt.readExpansion = template.properties[i].read; pt.writeExpansion = template.properties[i].write; + pt.recordable = template.properties[i].recordable; this.properties.push(pt); } @@ -4707,6 +7298,7 @@ class ResourceTemplate { pt.index = propertyIndex++; var readExpansion = ((data.getUint8(offset) & 0x8) == 0x8); var writeExpansion = ((data.getUint8(offset) & 0x10) == 0x10); + pt.recordable = ((data.getUint8(offset) & 1) == 1); pt.permission = ((data.getUint8(offset++) >> 1) & 0x3); pt.name = data.getString(offset + 1, data.getUint8(offset));// Encoding.ASCII.getString(data, (int)offset + 1, data.getUint8(offset)); offset += data.getUint8(offset) + 1; @@ -4791,17 +7383,21 @@ class ResourceTemplate { * Created by Ahmed Zamil on 02/09/2017. */ +"use strict"; + class SendList extends BinaryList { - constructor(connection) + constructor(connection, doneReply) { super(); this.connection = connection; + this.reply = doneReply; } done() { this.connection.send(this.toArray()); + return this.reply; } } /* @@ -4829,33 +7425,112 @@ class SendList extends BinaryList /** * Created by Ahmed Zamil on 25/07/2017. */ -var Warehouse = { - stores: [], - resources: {}, - resourceCounter: 0, - templates: {}, +"use strict"; - new(type, name, store = null, parent = null) + +class Warehouse +{ + static new(type, name, store = null, parent = null, manager = null) { var res = type(); - Warehouse.put(res, name, store, parent); + Warehouse.put(res, name, store, parent, null, 0, manager); return res; - }, + } -get: function(id) + static get(id) { - if (Warehouse.resources[id]) - return new AsyncReply(Warehouse.resources[id]); + if (Number.isInteger(id)) + { + //if (Warehouse.resources.contains(id)) + return new AsyncReply(Warehouse.resources.item(id)); + //else + // return null; + } else - return null; - }, + { + var p = id.split('/'); + var res = null; - put: function(resource, name, store, parent){ - resource.instance = new Instance(Warehouse.resourceCounter++, name, resource, store); + for(var s = 0; s < this.stores.length; s++) + { + var d = this.stores.at(s); + if (p[0] == d.instance.name) + { + var i = 1; + res = d; + while(p.length > i) + { + var si = i; + + for (var r = 0; r < res.instance.children.length; r++) + if (res.instance.children.item(r).instance.name == p[i]) + { + i++; + res = res.instance.children.item(r); + break; + } + + if (si == i) + // not found, ask the store + return d.get(id.substring(p[0].length + 1)); + } + + return new AsyncReply(res); + } + } + + return new AsyncReply(null); + } + } + + + static remove(resource) + { + + if (Warehouse.resources.contains(resource.instance.id)) + Warehouse.resources.remove(resource.instance.id); + else + return false; + + if (resource instanceof IStore) + { + Warehouse.stores.remove(resource); + + // remove all objects associated with the store + var toBeRemoved = null; + + for (var i = 0; i < Warehouse.resources.length; i++) + { + var o = Warehouse.resources.at(i); + if (o.instance.store == resource) + { + if (toBeRemoved == null) + toBeRemoved = []; + toBeRemoved.push(o); + } + } + + if (toBeRemoved != null) + for(var i = 0; i < toBeRemoved.length; i++) + Warehouse.remove(toBeRemoved[i]); + } + + if (resource.instance.store != null) + resource.instance.store.remove(resource); + resource.destroy(); + + return true; + } + + static put(resource, name, store, parent, customTemplate = null, age = 0, manager = null){ + resource.instance = new Instance(Warehouse.resourceCounter++, name, resource, store, customTemplate, age); //resource.instance.children.on("add", Warehouse._onChildrenAdd).on("remove", Warehouse._onChildrenRemove); //resource.instance.parents.on("add", Warehouse._onParentsAdd).on("remove", Warehouse._onParentsRemove); + if (manager != null) + resource.instance.managers.add(manager); + if (parent) { parent.instance.children.add(resource); @@ -4867,69 +7542,102 @@ get: function(id) } if (resource instanceof IStore) - Warehouse.stores.push(resource); + Warehouse.stores.add(resource); else store.put(resource); - Warehouse.resources[resource.instance.id] = resource; - }, + Warehouse.resources.add(resource.instance.id, resource); + } - _onParentsRemove: function(value) + static _onParentsRemove(value) { if (value.instance.children.contains(value)) value.instance.children.remove(value); - }, + } - _onParentsAdd: function(value) + static _onParentsAdd(value) { if (!value.instance.children.contains(value)) value.instance.children.add(value); - }, + } - _onChildrenRemove: function(value) + static _onChildrenRemove(value) { if (value.instance.parents.contains(value)) value.instance.parents.remove(value); - }, + } - _onChildrenAdd: function(value) + static _onChildrenAdd(value) { if (!value.instance.parents.contains(value)) value.instance.parents.add(value); - }, + } - putTemplate: function(template) + static putTemplate(template) { - if (Warehouse.templates[template.classId]) - Warehouse.templates[template.classId] = template; - }, + Warehouse.templates.add(template.classId.valueOf(), template); + } - getTemplateByType: function(type) + static getTemplateByType(type) { // loaded ? - for (var t in Warehouse.templates) - if (Warehouse.templates[t].className == typeof(type)) - return t; + for (var i = 0; i < Warehouse.templates.length; i++) + if (Warehouse.templates.at(i).className == typeof(type)) + return Warehouse.templates.at(i); var template = new ResourceTemplate(type); - Warehouse.templates[template.classId] = template; - + Warehouse.templates.add(template.classId.valueOf(), template); + return template; - }, - - getTemplateByClassId: function(classId) - { - if (Warehouse.templates[classId]) - return new AsyncReply(Warehouse.templates[classId]); - return null; - }, - - getTemplateByClassName: function(className) - { - for(var t in Warehouse.templates) - if (Warehouse.templates[t].className == className) - return new AsyncReply(t); - - return null; } -}; + + static getTemplateByClassId(classId) + { + var template = Warehouse.templates.item(classId); + return new AsyncReply(template); + } + + static getTemplateByClassName(className) + { + for(var i = 0; i < Warehouse.templates.length; i++) + if (Warehouse.templates.at(i).className == className) + return new AsyncReply(Warehouse.templates.at(i)); + + return new AsyncReply(null); + } + + static _qureyIn(path, index, resources) + { + var rt = []; + + if (index == path.length - 1) + { + if (path[index] == "") + for(var i = 0; i < resources.length; i++) + rt.push(resources.at(i)); + else + for(var i = 0; i < resources.length; i++) + if (resources.at(i).instance.name == path[index]) + rt.push(resources.at(i)); + } + else + for(var i = 0; i < resources.length; i++) + if (resources.at(i).instance.name == path[index]) + rt = rt.concat(Warehouse._qureyIn(path, index+1, resources.at(i).instance.children)); + + return rt; + } + + static query(path) + { + var p = path.split('/'); + return new AsyncReply(Warehouse._qureyIn(p, 0, Warehouse.stores)); + } +} + +// Initialize +Warehouse.stores = new AutoList(); +Warehouse.resources = new KeyList(); +Warehouse.resourceCounter = 0; +Warehouse.templates = new KeyList(); + diff --git a/build/esiur.js b/build/esiur.js deleted file mode 100644 index a48965e..0000000 --- a/build/esiur.js +++ /dev/null @@ -1 +0,0 @@ -class IEventHandler{_register(event){this._events[event]=[]}constructor(){this._events={}}_emit(event){event=event.toLowerCase();var args=Array.prototype.slice.call(arguments,1);if(this._events[event])for(var i=0;i-1&&this._events[event].splice(index,1)}else this._events[event]=[]}}!function(){"use strict";function t(t,i){i?(p[0]=p[16]=p[1]=p[2]=p[3]=p[4]=p[5]=p[6]=p[7]=p[8]=p[9]=p[10]=p[11]=p[12]=p[13]=p[14]=p[15]=0,this.blocks=p):this.blocks=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],t?(this.h0=3238371032,this.h1=914150663,this.h2=812702999,this.h3=4144912697,this.h4=4290775857,this.h5=1750603025,this.h6=1694076839,this.h7=3204075428):(this.h0=1779033703,this.h1=3144134277,this.h2=1013904242,this.h3=2773480762,this.h4=1359893119,this.h5=2600822924,this.h6=528734635,this.h7=1541459225),this.block=this.start=this.bytes=0,this.finalized=this.hashed=!1,this.first=!0,this.is224=t}function i(i,r,e){var n="string"!=typeof i;if(n){if(null===i||void 0===i)throw h;i.constructor===s.ArrayBuffer&&(i=new Uint8Array(i))}o=i.length;if(n){if("number"!=typeof o||!Array.isArray(i)&&(!a||!ArrayBuffer.isView(i)))throw h}else{for(var f,u=[],o=i.length,c=0,y=0;o>y;++y)128>(f=i.charCodeAt(y))?u[c++]=f:2048>f?(u[c++]=192|f>>6,u[c++]=128|63&f):55296>f||f>=57344?(u[c++]=224|f>>12,u[c++]=128|f>>6&63,u[c++]=128|63&f):(f=65536+((1023&f)<<10|1023&i.charCodeAt(++y)),u[c++]=240|f>>18,u[c++]=128|f>>12&63,u[c++]=128|f>>6&63,u[c++]=128|63&f);i=u}i.length>64&&(i=new t(r,!0).update(i).array());for(var p=[],l=[],y=0;64>y;++y){var d=i[y]||0;p[y]=92^d,l[y]=54^d}t.call(this,r,e),this.update(l),this.oKeyPad=p,this.inner=!0,this.sharedMemory=e}var h="input is invalid type",s="object"==typeof window?window:{},r=!s.JS_SHA256_NO_NODE_JS&&"object"==typeof process&&process.versions&&process.versions.node;r&&(s=global);var e=!s.JS_SHA256_NO_COMMON_JS&&"object"==typeof module&&module.exports,n="function"==typeof define&&define.amd,a="undefined"!=typeof ArrayBuffer,o="0123456789abcdef".split(""),f=[-2147483648,8388608,32768,128],u=[24,16,8,0],c=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],y=["hex","array","digest","arrayBuffer"],p=[];(s.JS_SHA256_NO_NODE_JS||!Array.isArray)&&(Array.isArray=function(t){return"[object Array]"===Object.prototype.toString.call(t)});var l=function(i,h){return function(s){return new t(h,!0).update(s)[i]()}},d=function(i){var h=l("hex",i);r&&(h=v(h,i)),h.create=function(){return new t(i)},h.update=function(t){return h.create().update(t)};for(var s=0;so;){if(this.hashed&&(this.hashed=!1,f[0]=this.block,f[16]=f[1]=f[2]=f[3]=f[4]=f[5]=f[6]=f[7]=f[8]=f[9]=f[10]=f[11]=f[12]=f[13]=f[14]=f[15]=0),i)for(n=this.start;r>o&&64>n;++o)f[n>>2]|=t[o]<o&&64>n;++o)128>(e=t.charCodeAt(o))?f[n>>2]|=e<e?(f[n>>2]|=(192|e>>6)<>2]|=(128|63&e)<e||e>=57344?(f[n>>2]|=(224|e>>12)<>2]|=(128|e>>6&63)<>2]|=(128|63&e)<>2]|=(240|e>>18)<>2]|=(128|e>>12&63)<>2]|=(128|e>>6&63)<>2]|=(128|63&e)<=64?(this.block=f[16],this.start=n-64,this.hash(),this.hashed=!0):this.start=n}return this}},t.prototype.finalize=function(){if(!this.finalized){this.finalized=!0;var t=this.blocks,i=this.lastByteIndex;t[16]=this.block,t[i>>2]|=f[3&i],this.block=t[16],i>=56&&(this.hashed||this.hash(),t[0]=this.block,t[16]=t[1]=t[2]=t[3]=t[4]=t[5]=t[6]=t[7]=t[8]=t[9]=t[10]=t[11]=t[12]=t[13]=t[14]=t[15]=0),t[15]=this.bytes<<3,this.hash()}},t.prototype.hash=function(){var t,i,h,s,r,e,n,a,o,f,u,y=this.h0,p=this.h1,l=this.h2,d=this.h3,v=this.h4,A=this.h5,w=this.h6,b=this.h7,g=this.blocks;for(t=16;64>t;++t)r=g[t-15],i=(r>>>7|r<<25)^(r>>>18|r<<14)^r>>>3,r=g[t-2],h=(r>>>17|r<<15)^(r>>>19|r<<13)^r>>>10,g[t]=g[t-16]+i+g[t-7]+h<<0;for(u=p&l,t=0;64>t;t+=4)this.first?(this.is224?(a=300032,r=g[0]-1413257819,b=r-150054599<<0,d=r+24177077<<0):(a=704751109,r=g[0]-210244248,b=r-1521486534<<0,d=r+143694565<<0),this.first=!1):(i=(y>>>2|y<<30)^(y>>>13|y<<19)^(y>>>22|y<<10),h=(v>>>6|v<<26)^(v>>>11|v<<21)^(v>>>25|v<<7),a=y&p,s=a^y&l^u,n=v&A^~v&w,r=b+h+n+c[t]+g[t],e=i+s,b=d+r<<0,d=r+e<<0),i=(d>>>2|d<<30)^(d>>>13|d<<19)^(d>>>22|d<<10),h=(b>>>6|b<<26)^(b>>>11|b<<21)^(b>>>25|b<<7),o=d&y,s=o^d&p^a,n=b&v^~b&A,r=w+h+n+c[t+1]+g[t+1],e=i+s,w=l+r<<0,l=r+e<<0,i=(l>>>2|l<<30)^(l>>>13|l<<19)^(l>>>22|l<<10),h=(w>>>6|w<<26)^(w>>>11|w<<21)^(w>>>25|w<<7),f=l&d,s=f^l&y^o,n=w&b^~w&v,r=A+h+n+c[t+2]+g[t+2],e=i+s,A=p+r<<0,p=r+e<<0,i=(p>>>2|p<<30)^(p>>>13|p<<19)^(p>>>22|p<<10),h=(A>>>6|A<<26)^(A>>>11|A<<21)^(A>>>25|A<<7),u=p&l,s=u^p&d^f,n=A&w^~A&b,r=v+h+n+c[t+3]+g[t+3],e=i+s,v=y+r<<0,y=r+e<<0;this.h0=this.h0+y<<0,this.h1=this.h1+p<<0,this.h2=this.h2+l<<0,this.h3=this.h3+d<<0,this.h4=this.h4+v<<0,this.h5=this.h5+A<<0,this.h6=this.h6+w<<0,this.h7=this.h7+b<<0},t.prototype.hex=function(){this.finalize();var t=this.h0,i=this.h1,h=this.h2,s=this.h3,r=this.h4,e=this.h5,n=this.h6,a=this.h7,f=o[t>>28&15]+o[t>>24&15]+o[t>>20&15]+o[t>>16&15]+o[t>>12&15]+o[t>>8&15]+o[t>>4&15]+o[15&t]+o[i>>28&15]+o[i>>24&15]+o[i>>20&15]+o[i>>16&15]+o[i>>12&15]+o[i>>8&15]+o[i>>4&15]+o[15&i]+o[h>>28&15]+o[h>>24&15]+o[h>>20&15]+o[h>>16&15]+o[h>>12&15]+o[h>>8&15]+o[h>>4&15]+o[15&h]+o[s>>28&15]+o[s>>24&15]+o[s>>20&15]+o[s>>16&15]+o[s>>12&15]+o[s>>8&15]+o[s>>4&15]+o[15&s]+o[r>>28&15]+o[r>>24&15]+o[r>>20&15]+o[r>>16&15]+o[r>>12&15]+o[r>>8&15]+o[r>>4&15]+o[15&r]+o[e>>28&15]+o[e>>24&15]+o[e>>20&15]+o[e>>16&15]+o[e>>12&15]+o[e>>8&15]+o[e>>4&15]+o[15&e]+o[n>>28&15]+o[n>>24&15]+o[n>>20&15]+o[n>>16&15]+o[n>>12&15]+o[n>>8&15]+o[n>>4&15]+o[15&n];return this.is224||(f+=o[a>>28&15]+o[a>>24&15]+o[a>>20&15]+o[a>>16&15]+o[a>>12&15]+o[a>>8&15]+o[a>>4&15]+o[15&a]),f},t.prototype.toString=t.prototype.hex,t.prototype.digest=function(){this.finalize();var t=this.h0,i=this.h1,h=this.h2,s=this.h3,r=this.h4,e=this.h5,n=this.h6,a=this.h7,o=[t>>24&255,t>>16&255,t>>8&255,255&t,i>>24&255,i>>16&255,i>>8&255,255&i,h>>24&255,h>>16&255,h>>8&255,255&h,s>>24&255,s>>16&255,s>>8&255,255&s,r>>24&255,r>>16&255,r>>8&255,255&r,e>>24&255,e>>16&255,e>>8&255,255&e,n>>24&255,n>>16&255,n>>8&255,255&n];return this.is224||o.push(a>>24&255,a>>16&255,a>>8&255,255&a),o},t.prototype.array=t.prototype.digest,t.prototype.arrayBuffer=function(){this.finalize();var t=new ArrayBuffer(this.is224?28:32),i=new DataView(t);return i.setUint32(0,this.h0),i.setUint32(4,this.h1),i.setUint32(8,this.h2),i.setUint32(12,this.h3),i.setUint32(16,this.h4),i.setUint32(20,this.h5),i.setUint32(24,this.h6),this.is224||i.setUint32(28,this.h7),t},(i.prototype=new t).finalize=function(){if(t.prototype.finalize.call(this),this.inner){this.inner=!1;var i=this.array();t.call(this,this.is224,this.sharedMemory),this.update(this.oKeyPad),this.update(i),t.prototype.finalize.call(this)}};var b=d();b.sha256=b,b.sha224=d(!0),b.sha256.hmac=w(),b.sha224.hmac=w(!0),e?module.exports=b:(s.sha256=b.sha256,s.sha224=b.sha224,n&&define(function(){return b}))}();class IDestructible extends IEventHandler{destroy(){this._emit("destroy",this)}constructor(){super()}}class AutoList extends IEventHandler{constructor(){super(),this.list=[]}add(value){value instanceof IDestructible&&value.on("destroy",this._item_destroyed),this.list.push(value),this._emit("add",value)}set(index,value){index>=this.list.length||index<0||(value instanceof IDestructible&&value.on("destroy",this._item_destroyed),this.list[index]instanceof IDestructible&&this.list[index].off("destroy",this._item_destroyed),this.list[index]=value)}remove(value){this.removeAt(this.list.indexOf(value))}contains(value){return this.list.indexOf(value)>-1}removeAt(index){if(!(index>=this.list.length||index<0)){var item=this.list[index];item instanceof IDestructible&&item.off("destroy",this._item_destroyed),this.list.splice(index,1),this._emit("remove",item)}}_item_destroyed(sender){this.remove(sender)}}var ResourceTrigger={Loaded:0,Initialize:1,Terminate:2,Configure:3,SystemInitialized:4,SystemTerminated:5,SystemReload:6};class IResource extends IDestructible{trigger(trigger){}constructor(){super()}static getTemplate(){return{namespace:"Esiur",properties:[],functions:[],events:[]}}}class IStore extends IResource{get(path){}retrieve(iid){}put(resource){}constructor(){super()}}class Structure{getKeys(){var rt=[];for(var i in this)this[i]instanceof Function||rt.push(i);return rt}}class StructureArray extends Array{push(value){value instanceof Structure&&super.push(value)}}class ResourceArray extends Array{push(value){value instanceof IResource&&super.push(value)}}var MemberType={Function:0,Property:1,Event:2};class MemberTemplate{compose(){return DC.stringToBytes(this.name)}}class AsyncReply{then(callback){this.callbacks.push(callback),this.ready&&callback(this.result,this)}trigger(result){this.result=result,this.ready=!0;for(var i=0;i0;){var len=data[offset++];keylist.push(data.getString(offset,len)),offset+=len,typelist.push(data[offset]);rt={};bag.add(Codec.parse(data,offset,rt,connection)),contentLength-=rt.size+len+1,offset+=rt.size}else if(null==types){for(i=0;i0;){typelist.push(data[offset]);rt={};bag.add(Codec.parse(data,offset,rt,connection)),contentLength-=rt.size+1,offset+=rt.size+1}}else{for(i=0;i0;){var rt={};bag.add(Codec.parse(data,offset,rt,connection,types[i])),contentLength-=rt.size,offset+=rt.size,i++}}return bag.seal(),bag.then(function(res){for(var s=new Structure,i=0;i0;){var cs={};if(rt.add(Codec.parse(data,offset,cs,connection)),!(cs.size>0))throw new Exception("Error while parsing structured data");offset+=cs.size,contentLength-=cs.size}return rt.seal(),rt}static compose(value,connection,prependType=!0){var type=Codec.getDataType(value,connection),rt=new BinaryList;switch(type){case DataType.Void:break;case DataType.String:var st=DC.stringToBytes(value);rt.addUint32(st.length).addUint8Array(st);break;case DataType.Resource:case DataType.DistributedResource:rt.addUint32(value.instance.id);break;case DataType.Structure:rt.addUint8Array(Codec.composeStructure(value,connection,!0,!0,!0));break;case DataType.VarArray:rt.addUint8Array(Codec.composeVarArray(value,connection,!0));break;case DataType.ResourceArray:rt.addUint8Array(Codec.composeResourceArray(value,connection,!0));break;case DataType.StructureArray:rt.addUint8Array(Codec.composeStructureArray(value,connection,!0));break;default:rt.add({type:type,value:value}),DataType.isArray(type)&&rt.addUint32(rt.length,0)}return prependType&&rt.addUint8(type,0),rt.toArray()}static composeVarArray(array,connection,prependLength=!1){for(var rt=new BinaryList,i=0;i0?value>255?value>65535?value>4294967295?DataType.UInt64:DataType.UInt32:DataType.UInt16:DataType.UInt8:value<-128?value<-32768?value<-2147483648?DataType.Int64:DataType.Int32:DataType.Int16:DataType.Int8:DataType.Float64;case"string":return DataType.String;case"boolean":return DataType.Bool;case"object":return value instanceof Array?DataType.VarArray:value instanceof IResource?Codec.isLocalResource(value,connection)?DataType.Resource:DataType.DistributedResource:value instanceof Date?DataType.DateTime:value instanceof Uint8Array||value instanceof ArrayBuffer?DataType.UInt8Array:value instanceof Number?DataType.Float64:value instanceof Structure?DataType.Structure:DataType.Void;default:return DataType.Void}}}var UNIX_EPOCH=621355968e9,TWO_PWR_32=4294967296;class DC extends Uint8Array{constructor(bufferOrSize){super(bufferOrSize),this.dv=new DataView(this.buffer)}static boolToBytes(value){var rt=new DC(1);return rt.setBoolean(0,value),rt}static int8ToBytes(value){var rt=new DC(1);return rt.setInt8(0,value),rt}static hexToBytes(value){for(var rt=new DC(value.length/2),i=0;i0}setBoolean(offset,value){this.setUint8(offset,value?1:0)}getBooleanArray(offset,length){for(var rt=[],i=0;i=0?l:TWO_PWR_32+l)}getUint64(offset){var h=this.getUint32(offset),l=this.getUint32(offset+4);return h*TWO_PWR_32+(l>=0?l:TWO_PWR_32+l)}setInt64(offset,value){var l=value%TWO_PWR_32|0,h=value/TWO_PWR_32|0;this.setInt32(offset,h),this.setInt32(offset+4,l)}setUint64(offset,value){var l=value%TWO_PWR_32|0,h=value/TWO_PWR_32|0;this.setInt32(offset,h),this.setInt32(offset+4,l)}setDateTime(offset,value){var ticks=621355968e9+1e4*value.getTime();this.setUint64(offset,ticks)}getDateTime(offset){var ticks=this.getUint64(offset);return new Date(Math.round((ticks-DCStatic.UNIX_EPOCH)/1e4))}getDateTimeArray(offset){for(var rt=[],i=0;i0&&!this.networkBuffer.protected;)self.receive(this.networkBuffer)}}receive(data){for(var msg=data.read(),offset=0,ends=msg.length,packet=this.packet,authPacket=this.authPacket;offsetage){var pv=r[pt.name];sl.addUint8(146).addUint32(callback).addUint8Array(Codec.compose(pv,sl.connection)).done()}else sl.addUint8(146).addUint32(callback).addUint8(DataType.NotModified).done()}})}IIPRequestSetProperty(callback,resourceId,index,content){var sl=this.sendParams();Warehouse.get(resourceId).then(function(r){if(null!=r){var pt=r.instance.template.getPropertyTemplateByIndex(index);null!=pt&&Codec.parse(content,0,this).then(function(value){r instanceof DistributedResource?r._set(index,value).then(function(x){sl.addUint8(147).addUint32(callback).done()}):(r[pt.name]=value,sl.addUint8(147).addUint32(callback).done())})}})}getTemplate(classId){if(this.templates[classId])return new AsyncReply(this.templates[classId]);if(this.templateRequests[classId])return this.templateRequests[classId];var reply=new AsyncReply;this.templateRequests[classId]=reply;var self=this;return this.sendRequest(IIPPacketAction.TemplateFromClassId,BL().addUint8Array(classId.value)).then(function(rt){delete self.templateRequests[classId],self.templates[rt[0].classId]=rt[0],reply.trigger(rt[0])}),reply}get(path){if(this.pathRequests[path])return this.pathRequests[path];var reply=new AsyncReply;this.pathRequests[path]=reply;var bl=new BinaryList;bl.addString(path),bl.addUint16(bl.length,0);var self=this;return this.sendRequest(IIPPacketAction.ResourceIdFromResourceLink,bl).then(function(rt){delete self.pathRequests[path],self.fetch(rt[1]).then(function(r){reply.trigger(r)})}),reply}retrieve(iid){for(var r in this.resources)if(this.resources[r].instance.id==iid)return new AsyncReply(r);return new AsyncReply(null)}fetch(id){if(this.resourceRequests[id]&&this.resources[id])return this.resourceRequests[id];if(this.resourceRequests[id])return this.resourceRequests[id];if(this.resources[id])return new AsyncReply(this.resources[id]);var reply=new AsyncReply,self=this;return this.sendRequest(IIPPacketAction.AttachResource,BL().addUint32(id)).then(function(rt){self.getTemplate(rt[0]).then(function(tmp){var dr=new DistributedResource(self,tmp,id,rt[1],rt[2]);Warehouse.put(dr,id.toString(),self),Codec.parseVarArray(rt[3],0,rt[3].length,self).then(function(ar){dr._attached(ar),delete self.resourceRequests[id],reply.trigger(dr)})})}),reply}instance_resourceDestroyed(resource){this.sendParams().addUint8(1).addUint32(resource.instance.id).done()}instance_propertyModified(resource,name,newValue,oldValue){var pt=resource.instance.template.getPropertyTemplateByName(name);null!=pt&&(newValue instanceof Function?sendParams().addUint8(16).addUint32(resource.instance.id).addUint8(pt.index).addUint8Array(Codec.compose(newValue(this),this)).done():sendParams().addUint8(16).addUint32(resource.instance.id).addUint8(pt.index).addUint8Array(Codec.compose(newValue,this)).done())}instance_eventOccured(resource,name,receivers,args){var et=resource.instance.template.getEventTemplateByName(name);if(null!=et&&!(null!=receivers&&receivers.indexOf(this.remoteUsername)<0)){for(var clientArgs=[],i=0;i=this._p.template.functions.length)throw new Exception("Function index is incorrect");var reply=new AsyncReply,parameters=Codec.composeVarArray(args,this._p.connection,!0),self=this;return this._p.connection.sendRequest(IIPPacketAction.InvokeFunction,BL().addUint32(self._p.instanceId).addUint8(index).addUint8Array(parameters)).then(function(res){Codec.parse(res[0],0,self._p.connection).then(function(rt){reply.trigger(rt)})}),reply}_get(index){return index>=this._p.properties.length?null:this._p.properties[index]}_updatePropertyByIndex(index,value){var pt=this._p.template.getPropertyTemplateByIndex(index);this._p.properties[index]=value,this.instance.modified(pt.name,value)}_set(index,value){if(index>=this._p.properties.length)return null;var reply=new AsyncReply,parameters=Codec.compose(value,this._p.connection),self=this;return this._p.connection.sendRequest(IIPPacketAction.SetProperty,BL().addUint32(self._p.instanceId).addUint8(index).addUint8Array(parameters)).then(function(res){reply.trigger(null)}),reply}}var DistributedResourceQueueItemType={Propery:0,Event:1};class DistributedResourceQueueItem{constructor(resource,type,value,index){this.resource=resource,this.index=index,this.type=type,this.value=value}}class EventTemplate extends MemberTemplate{constructor(){super(),this.type=MemberType.Event}compose(){var rt=new BinaryList,name=super.compose();if(null!=this.expansion){var exp=DC.stringToBytes(this.expansion);return rt.addUint8(80).addUint32(exp.length).addUint8Array(exp).addUint8(name.length).addUint8Array(name).toArray()}return rt.addUint8(64).addUint32(name.length).addUint8Array(name).toArray()}}class FunctionTemplate extends MemberTemplate{compose(){var name=super.compose(),rt=new BinaryList;if(null!=this.expansion){var exp=DC.stringToBytes(this.expansion);return rt.addUint8(16|(IsVoid?8:0)).addUint32(exp.length).addUint8Array(exp).addUint8(name.length).addUint8Array(name).toArray()}return rt.addUint8(IsVoid?8:0).addUint8(name.length).addUint8Array(name).toArray()}constructor(){super(),this.type=MemberType.Function}}class Guid{constructor(dc){this.value=dc}valueOf(){return this.value.getHex(0,16)}}var IIPAuthPacketCommand={Action:0,Declare:1,Acknowledge:2,Error:3},IIPAuthPacketAction={AuthenticateHash:0,NewConnection:32,ResumeConnection:33,ConnectionEstablished:40},IIPAuthPacketMethod={None:0,Certificate:1,Credentials:2,Token:3};class IIPAuthPacket{constructor(){this.command=0,this.action=0,this.errorCode=0,this.errorMessage="",this.localMethod=0,this.sourceInfo="",this.hash="",this.sessionId="",this.remoteMethod=0,this.domain="",this.CertificateId=0,this.localUsername="",this.remoteUsername="",this.localPassword="",this.remotePassword="",this.localToken=[],this.reemoteToken=[],this.asymetricEncryptionKey=[],this.localNonce=[],this.remoteNonce=[],this.dataLengthNeeded=0}notEnough(offset,ends,needed){return offset+needed>ends&&(this.dataLengthNeeded=needed-(ends-offset),!0)}parse(data,offset,ends){var oOffset=offset;if(this.notEnough(offset,ends,1))return-this.dataLengthNeeded;if(this.command=data.getUint8(offset)>>6,this.command==IIPAuthPacketCommand.Action){if(this.action=63&data[offset++],this.action==IIPAuthPacketAction.AuthenticateHash){if(this.notEnough(offset,ends,32))return-this.dataLengthNeeded;this.hash=data.getUint8Array(offset,32),offset+=32}else if(this.action==IIPAuthPacketAction.NewConnection){if(this.notEnough(offset,ends,2))return-this.dataLengthNeeded;length=data.getUint16(offset);if(offset+=2,this.notEnough(offset,ends,length))return-this.dataLengthNeeded;this.sourceInfo=data.clip(offset,length),offset+=32}else if(this.action==IIPAuthPacketAction.ResumeConnection||this.action==IIPAuthPacketAction.ConnectionEstablished){if(this.notEnough(offset,ends,32))return-this.dataLengthNeeded;this.sessionId=data.clip(offset,32),offset+=32}}else if(this.command==IIPAuthPacketCommand.Declare){this.remoteMethod=data.getUint8(offset)>>4&3,this.localMethod=data.getUint8(offset)>>2&3;encrypt=2==(2&data.getUint8(offset++));if(this.notEnough(offset,ends,1))return-this.dataLengthNeeded;var domainLength=data.getUint8(offset++);if(this.notEnough(offset,ends,domainLength))return-this.dataLengthNeeded;if(this.domain=data.getString(offset,domainLength),offset+=domainLength,this.remoteMethod==IIPAuthPacketMethod.Credentials&&this.localMethod==IIPAuthPacketMethod.None){if(this.notEnough(offset,ends,33))return-this.dataLengthNeeded;this.remoteNonce=data.clip(offset,32),offset+=32;var length=data.getUint8(offset++);if(this.notEnough(offset,ends,length))return-this.dataLengthNeeded;this.remoteUsername=data.getString(offset,length),offset+=length}if(encrypt){if(this.notEnough(offset,ends,2))return-this.dataLengthNeeded;keyLength=data.getUint16(offset);if(offset+=2,this.notEnough(offset,ends,keyLength))return-this.dataLengthNeeded;this.asymetricEncryptionKey=data.clip(offset,keyLength),offset+=keyLength}}else if(this.command==IIPAuthPacketCommand.Acknowledge){this.remoteMethod=data.getUint8(offset)>>4&3,this.localMethod=data.getUint8(offset)>>2&3;var encrypt=2==(2&data.getUint8(offset++));if(this.notEnough(offset,ends,1))return-this.dataLengthNeeded;if(this.remoteMethod==IIPAuthPacketMethod.Credentials&&this.localMethod==IIPAuthPacketMethod.None){if(this.notEnough(offset,ends,32))return-this.dataLengthNeeded;this.remoteNonce=data.clip(offset,32),offset+=32}if(encrypt){if(this.notEnough(offset,ends,2))return-this.dataLengthNeeded;var keyLength=data.getUint16(offset);if(offset+=2,this.notEnough(offset,ends,keyLength))return-this.dataLengthNeeded;this.asymetricEncryptionKey=data.clip(offset,keyLength),offset+=keyLength}}else if(this.command==IIPAuthPacketCommand.Error){if(this.notEnough(offset,ends,5))return-this.dataLengthNeeded;offset++,this.errorCode=data.getUint8(offset++);var cl=data.getUint16(offset);if(offset+=2,this.notEnough(offset,ends,cl))return-this.dataLengthNeeded;this.errorMessage=data.getString(offset,cl),offset+=cl}return offset-oOffset}}var IIPPacketCommand={Event:0,Request:1,Reply:2,Error:3},IIPPacketEvent={ResourceReassigned:0,ResourceDestroyed:1,PropertyUpdated:16,EventOccured:17},IIPPacketAction={AttachResource:0,ReattachResource:1,DetachResource:2,CreateResource:3,DeleteResource:4,TemplateFromClassName:8,TemplateFromClassId:9,TemplateFromResourceLink:10,TemplateFromResourceId:11,ResourceIdFromResourceLink:12,InvokeFunction:16,GetProperty:17,GetPropertyIfModified:18,SetProperty:19};class IIPPacket{constructor(){this.command=0,this.action=0,this.event=0,this.resourceId=0,this.newResourceId=0,this.resourceAge=0,this.content=[],this.errorCode=0,this.errorMessage="",this.className="",this.resourceLink="",this.classId="",this.methodIndex="",this.methodName="",this.callbackId=0,this.dataLengthNeeded=0}notEnough(offset,ends,needed){return offset+needed>ends&&(this.dataLengthNeeded=needed-(ends-offset),!0)}parse(data,offset,ends){var oOffset=offset;if(this.notEnough(offset,ends,1))return-this.dataLengthNeeded;if(this.command=data.getUint8(offset)>>6,this.command==IIPPacketCommand.Event){if(this.event=63&data.getUint8(offset++),this.notEnough(offset,ends,4))return-this.dataLengthNeeded;this.resourceId=data.getUint32(offset),offset+=4}else{if(this.action=63&data.getUint8(offset++),this.notEnough(offset,ends,4))return-this.dataLengthNeeded;this.callbackId=data.getUint32(offset),offset+=4}if(this.command==IIPPacketCommand.Event){if(this.event==IIPPacketEvent.ResourceReassigned){if(this.notEnough(offset,ends,4))return-this.dataLengthNeeded;this.newResourceId=data.getUint32(offset),offset+=4}else if(this.event==IIPPacketEvent.ResourceDestroyed);else if(this.event==IIPPacketEvent.PropertyUpdated){if(this.notEnough(offset,ends,2))return-this.dataLengthNeeded;this.methodIndex=data[offset++];dt=data.getUint8(offset++);if((size=DataType.sizeOf(dt))<0){if(this.notEnough(offset,ends,4))return-this.dataLengthNeeded;cl=data.getUint32(offset);if(offset+=4,this.notEnough(offset,ends,cl))return-this.dataLengthNeeded;this.content=data.clip(offset-5,cl+5),offset+=cl}else{if(this.notEnough(offset,ends,size))return-this.dataLengthNeeded;this.content=data.clip(offset-1,size+1),offset+=size}}else if(this.event==IIPPacketEvent.EventOccured){if(this.notEnough(offset,ends,5))return-this.dataLengthNeeded;this.methodIndex=data.getUint8(offset++);cl=data.getUint32(offset);offset+=4,this.content=data.clip(offset,cl),offset+=cl}}else if(this.command==IIPPacketCommand.Request){if(this.action==IIPPacketAction.AttachResource){if(this.notEnough(offset,ends,4))return-this.dataLengthNeeded;this.resourceId=data.getUint32(offset),offset+=4}else if(this.action==IIPPacketAction.ReattachResource){if(this.notEnough(offset,ends,8))return-this.dataLengthNeeded;this.resourceId=data.getUint32(offset),offset+=4,this.resourceAge=data.getUint32(offset),offset+=4}else if(this.action==IIPPacketAction.DetachResource){if(this.notEnough(offset,ends,4))return-this.dataLengthNeeded;this.resourceId=data.getUint32(offset),offset+=4}else if(this.action==IIPPacketAction.CreateResource){if(this.notEnough(offset,ends,1))return-this.dataLengthNeeded;cl=data.getUint8(offset++);if(this.notEnough(offset,ends,cl))return-this.dataLengthNeeded;this.className=data.getString(offset,cl),offset+=cl}else if(this.action==IIPPacketAction.DeleteResource){if(this.notEnough(offset,ends,4))return-this.dataLengthNeeded;this.resourceId=data.getUint32(offset),offset+=4}else if(this.action==IIPPacketAction.TemplateFromClassName){if(this.notEnough(offset,ends,1))return-this.dataLengthNeeded;cl=data.getUint8(offset++);if(this.notEnough(offset,ends,cl))return-this.dataLengthNeeded;this.className=data.getString(offset,cl),offset+=cl}else if(this.action==IIPPacketAction.TemplateFromClassId){if(this.notEnough(offset,ends,16))return-this.dataLengthNeeded;this.classId=data.getGuid(offset),offset+=16}else if(this.action==IIPPacketAction.TemplateFromResourceLink){if(this.notEnough(offset,ends,2))return-this.dataLengthNeeded;cl=data.getUint16(offset);if(offset+=2,this.notEnough(offset,ends,cl))return-this.dataLengthNeeded;this.resourceLink=data.getString(offset,cl),offset+=cl}else if(this.action==IIPPacketAction.TemplateFromResourceId){if(this.notEnough(offset,ends,4))return-this.dataLengthNeeded;this.resourceId=data.getUint32(offset),offset+=4}else if(this.action==IIPPacketAction.ResourceIdFromResourceLink){if(this.notEnough(offset,ends,2))return-this.dataLengthNeeded;cl=data.getUint16(offset);if(offset+=2,this.notEnough(offset,ends,cl))return-this.dataLengthNeeded;this.resourceLink=data.getString(offset,cl),offset+=cl}else if(this.action==IIPPacketAction.InvokeFunction){if(this.notEnough(offset,ends,9))return-this.dataLengthNeeded;this.resourceId=data.getUint32(offset),offset+=4,this.methodIndex=data.getUint8(offset++);cl=data.getUint32(offset);if(offset+=4,this.notEnough(offset,ends,cl))return-this.dataLengthNeeded;this.content=data.clip(offset,cl),offset+=cl}else if(this.action==IIPPacketAction.GetProperty){if(this.notEnough(offset,ends,5))return-this.dataLengthNeeded;this.resourceId=data.getUint32(offset),offset+=4,this.methodIndex=data.getUint8(offset++)}else if(this.action==IIPPacketAction.GetPropertyIfModified){if(this.notEnough(offset,ends,9))return-this.dataLengthNeeded;this.resourceId=data.getUint32(offset),offset+=4,this.methodIndex=data[offset++],this.resourceAge=data.getUint32(offset),offset+=4}else if(this.action==IIPPacketAction.SetProperty){if(this.notEnough(offset,ends,6))return-this.dataLengthNeeded;this.resourceId=data.getUint32(offset),offset+=4,this.methodIndex=data[offset++];dt=data.getUint8(offset++);if((size=DataType.sizeOf(dt))<0){if(this.notEnough(offset,ends,4))return-this.dataLengthNeeded;cl=data.getUint32(offset);if(offset+=4,this.notEnough(offset,ends,cl))return-this.dataLengthNeeded;this.content=data.clip(offset-5,cl+5),offset+=cl}else{if(this.notEnough(offset,ends,size))return-this.dataLengthNeeded;this.content=data.clip(offset-1,size+1),offset+=size}}}else if(this.command==IIPPacketCommand.Reply)if(this.action==IIPPacketAction.AttachResource||this.action==IIPPacketAction.ReattachResource){if(this.notEnough(offset,ends,26))return-this.dataLengthNeeded;this.classId=data.getGuid(offset),offset+=16,this.resourceAge=data.getUint32(offset),offset+=4;cl=data.getUint16(offset);if(offset+=2,this.notEnough(offset,ends,cl))return-this.dataLengthNeeded;if(this.resourceLink=data.getString(offset,cl),offset+=cl,this.notEnough(offset,ends,4))return-this.dataLengthNeeded;if(cl=data.getUint32(offset),offset+=4,this.notEnough(offset,ends,cl))return-this.dataLengthNeeded;this.content=data.clip(offset,cl),offset+=cl}else if(this.action==IIPPacketAction.DetachResource);else if(this.action==IIPPacketAction.CreateResource){if(this.notEnough(offset,ends,20))return-this.dataLengthNeeded;this.classId=data.GetGuid(offset),offset+=16,this.resourceId=data.getUint32(offset),offset+=4}else if(this.action==IIPPacketAction.DetachResource);else if(this.action==IIPPacketAction.TemplateFromClassName||this.action==IIPPacketAction.TemplateFromClassId||this.action==IIPPacketAction.TemplateFromResourceLink||this.action==IIPPacketAction.TemplateFromResourceId){if(this.notEnough(offset,ends,4))return-this.dataLengthNeeded;cl=data.getUint32(offset);if(offset+=4,this.notEnough(offset,ends,cl))return-this.dataLengthNeeded;this.content=data.clip(offset,cl),offset+=cl}else if(this.action==IIPPacketAction.ResourceIdFromResourceLink){if(this.notEnough(offset,ends,24))return-this.dataLengthNeeded;this.classId=data.getGuid(offset),offset+=16,this.resourceId=data.getUint32(offset),offset+=4,this.resourceAge=data.getUint32(offset),offset+=4}else if(this.action==IIPPacketAction.InvokeFunction||this.action==IIPPacketAction.GetProperty||this.action==IIPPacketAction.GetPropertyIfModified){if(this.notEnough(offset,ends,1))return-this.dataLengthNeeded;var dt=data.getUint8(offset++),size=DataType.sizeOf(dt);if(size<0){if(this.notEnough(offset,ends,4))return-this.dataLengthNeeded;cl=data.getUint32(offset);if(offset+=4,this.notEnough(offset,ends,cl))return-this.dataLengthNeeded;this.content=data.clip(offset-5,cl+5),offset+=cl}else{if(this.notEnough(offset,ends,size))return-this.dataLengthNeeded;this.content=data.clip(offset-1,size+1),offset+=size}}else this.action,IIPPacketAction.SetProperty;else if(this.command==IIPPacketCommand.Error){if(this.notEnough(offset,ends,4))return-this.dataLengthNeeded;if(this.callbackId=data.getUint32(offset),this.notEnough(offset,ends,1))return-this.dataLengthNeeded;if(this.errorCode=data.getUint8(offset++),this.notEnough(offset,ends,4))return-this.dataLengthNeeded;var cl=data.getUint32(offset);if(offset+=4,this.notEnough(offset,ends,cl))return-this.dataLengthNeeded;this.errorMessage=data.getString(offset,cl),offset+=cl}return offset-oOffset}}class Instance extends IEventHandler{getAge(index){return indexthis.data.length}get available(){return this.data.length}holdAllForNextWrite(src){this.holdFor(src,src.length+1)}holdForNextWrite(src,offset,size){this.holdFor(src,offset,size,size+1)}holdFor(src,offset,size,needed){if(size>=needed)throw new Exception("Size >= Needed !");this.data=DC.combine(src,offset,size,this.data,0,this.data.length),this.neededDataLength=needed}holdAllFor(src,needed){this.holdFor(src,0,src.length,needed)}protect(data,offset,needed){var dataLength=data.length-offset;return dataLength=this.neededDataLength?(rt=this.data,this.data=new DC(0),this.neededDataLength=0,rt):null:(rt=this.data,this.data=new DC(0),rt)}}class NotModified{}var PropertyPermission={Read:1,Write:2,ReadWrite:3};class PropertyTemplate extends MemberTemplate{constructor(){super(),this.type=MemberType.Property}compose(){var name=super.compose(),rt=new BinaryList;if(null!=this.writeExpansion&&null!=this.readExpansion){var rexp=DC.stringToBytes(this.readExpansion),wexp=DC.stringToBytes(this.writeExpansion);return rt.addUint8(56|this.permission).addUint32(wexp.length).addUint8Array(wexp).addUint32(rexp.length).addUint8Array(rexp).addUint8(name.length).addUint8Array(name).toArray()}if(null!=this.writeExpansion){wexp=DC.stringToBytes(this.writeExpansion);return rt.addUint8(48|this.permission).addUint32(wexp.length).addUint8Array(wexp).addUint8(name.length).addUint8Array(name).toArray()}if(null!=this.readExpansion){rexp=DC.stringToBytes(this.readExpansion);return rt.addUint8(40|this.permission).addUint32(rexp.length).addUint8Array(rexp).addUint8(name.length).addUint8Array(name).toArray()}return rt.addUint8(32|this.permission).addUint32(name.length).addUint8Array(name).toArray()}}class ResourceTemplate{getMemberTemplate(member){return member instanceof MethodInfo?this.getFunctionTemplate(member.name):member instanceof EventInfo?this.getEventTemplate(member.name):member instanceof PropertyInfo?this.getPropertyTemplate(member.name):null}getEventTemplateByName(eventName){for(var i=0;i>5;if(0==type){var ft=new FunctionTemplate;ft.index=functionIndex++;expansion=16==(16&data.getUint8(offset));if(ft.isVoid=8==(8&data.getUint8(offset++)),ft.name=data.getString(offset+1,data.getUint8(offset)),offset+=data.getUint8(offset)+1,expansion){cs=data.getUint32(offset);offset+=4,ft.expansion=data.getString(offset,cs),offset+=cs}od.functions.push(ft)}else if(1==type){var pt=new PropertyTemplate;pt.index=propertyIndex++;var readExpansion=8==(8&data.getUint8(offset)),writeExpansion=16==(16&data.getUint8(offset));if(pt.permission=data.getUint8(offset++)>>1&3,pt.name=data.getString(offset+1,data.getUint8(offset)),offset+=data.getUint8(offset)+1,readExpansion){cs=data.getUint32(offset);offset+=4,pt.readExpansion=data.getString(offset,cs),offset+=cs}if(writeExpansion){cs=data.getUint32(offset);offset+=4,pt.writeExpansion=data.getString(offset,cs),offset+=cs}od.properties.push(pt)}else if(2==type){var et=new EventTemplate;et.index=eventIndex++;var expansion=16==(16&data.getUint8(offset++));if(et.name=data.getString(offset+1,data.getUint8(offset)),offset+=data.getUint8(offset)+1,expansion){var cs=data.getUint32(offset);offset+=4,et.expansion=data.getString(offset,cs),offset+=cs}od.events.push(et)}}for(i=0;iy;++y)f=i.charCodeAt(y),128>f?u[c++]=f:2048>f?(u[c++]=192|f>>6,u[c++]=128|63&f):55296>f||f>=57344?(u[c++]=224|f>>12,u[c++]=128|f>>6&63,u[c++]=128|63&f):(f=65536+((1023&f)<<10|1023&i.charCodeAt(++y)),u[c++]=240|f>>18,u[c++]=128|f>>12&63,u[c++]=128|f>>6&63,u[c++]=128|63&f);i=u}i.length>64&&(i=new t(r,!0).update(i).array());for(var p=[],l=[],y=0;64>y;++y){var d=i[y]||0;p[y]=92^d,l[y]=54^d}t.call(this,r,e),this.update(l),this.oKeyPad=p,this.inner=!0,this.sharedMemory=e}var h="input is invalid type",s="object"==typeof window?window:{},r=!s.JS_SHA256_NO_NODE_JS&&"object"==typeof process&&process.versions&&process.versions.node;r&&(s=global);var e=!s.JS_SHA256_NO_COMMON_JS&&"object"==typeof module&&module.exports,n="function"==typeof define&&define.amd,a="undefined"!=typeof ArrayBuffer,o="0123456789abcdef".split(""),f=[-2147483648,8388608,32768,128],u=[24,16,8,0],c=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],y=["hex","array","digest","arrayBuffer"],p=[];(s.JS_SHA256_NO_NODE_JS||!Array.isArray)&&(Array.isArray=function(t){return"[object Array]"===Object.prototype.toString.call(t)});var l=function(i,h){return function(s){return new t(h,!0).update(s)[i]()}},d=function(i){var h=l("hex",i);r&&(h=v(h,i)),h.create=function(){return new t(i)},h.update=function(t){return h.create().update(t)};for(var s=0;so;){if(this.hashed&&(this.hashed=!1,f[0]=this.block,f[16]=f[1]=f[2]=f[3]=f[4]=f[5]=f[6]=f[7]=f[8]=f[9]=f[10]=f[11]=f[12]=f[13]=f[14]=f[15]=0),i)for(n=this.start;r>o&&64>n;++o)f[n>>2]|=t[o]<o&&64>n;++o)e=t.charCodeAt(o),128>e?f[n>>2]|=e<e?(f[n>>2]|=(192|e>>6)<>2]|=(128|63&e)<e||e>=57344?(f[n>>2]|=(224|e>>12)<>2]|=(128|e>>6&63)<>2]|=(128|63&e)<>2]|=(240|e>>18)<>2]|=(128|e>>12&63)<>2]|=(128|e>>6&63)<>2]|=(128|63&e)<=64?(this.block=f[16],this.start=n-64,this.hash(),this.hashed=!0):this.start=n}return this}},t.prototype.finalize=function(){if(!this.finalized){this.finalized=!0;var t=this.blocks,i=this.lastByteIndex;t[16]=this.block,t[i>>2]|=f[3&i],this.block=t[16],i>=56&&(this.hashed||this.hash(),t[0]=this.block,t[16]=t[1]=t[2]=t[3]=t[4]=t[5]=t[6]=t[7]=t[8]=t[9]=t[10]=t[11]=t[12]=t[13]=t[14]=t[15]=0),t[15]=this.bytes<<3,this.hash()}},t.prototype.hash=function(){var t,i,h,s,r,e,n,a,o,f,u,y=this.h0,p=this.h1,l=this.h2,d=this.h3,v=this.h4,A=this.h5,w=this.h6,b=this.h7,g=this.blocks;for(t=16;64>t;++t)r=g[t-15],i=(r>>>7|r<<25)^(r>>>18|r<<14)^r>>>3,r=g[t-2],h=(r>>>17|r<<15)^(r>>>19|r<<13)^r>>>10,g[t]=g[t-16]+i+g[t-7]+h<<0;for(u=p&l,t=0;64>t;t+=4)this.first?(this.is224?(a=300032,r=g[0]-1413257819,b=r-150054599<<0,d=r+24177077<<0):(a=704751109,r=g[0]-210244248,b=r-1521486534<<0,d=r+143694565<<0),this.first=!1):(i=(y>>>2|y<<30)^(y>>>13|y<<19)^(y>>>22|y<<10),h=(v>>>6|v<<26)^(v>>>11|v<<21)^(v>>>25|v<<7),a=y&p,s=a^y&l^u,n=v&A^~v&w,r=b+h+n+c[t]+g[t],e=i+s,b=d+r<<0,d=r+e<<0),i=(d>>>2|d<<30)^(d>>>13|d<<19)^(d>>>22|d<<10),h=(b>>>6|b<<26)^(b>>>11|b<<21)^(b>>>25|b<<7),o=d&y,s=o^d&p^a,n=b&v^~b&A,r=w+h+n+c[t+1]+g[t+1],e=i+s,w=l+r<<0,l=r+e<<0,i=(l>>>2|l<<30)^(l>>>13|l<<19)^(l>>>22|l<<10),h=(w>>>6|w<<26)^(w>>>11|w<<21)^(w>>>25|w<<7),f=l&d,s=f^l&y^o,n=w&b^~w&v,r=A+h+n+c[t+2]+g[t+2],e=i+s,A=p+r<<0,p=r+e<<0,i=(p>>>2|p<<30)^(p>>>13|p<<19)^(p>>>22|p<<10),h=(A>>>6|A<<26)^(A>>>11|A<<21)^(A>>>25|A<<7),u=p&l,s=u^p&d^f,n=A&w^~A&b,r=v+h+n+c[t+3]+g[t+3],e=i+s,v=y+r<<0,y=r+e<<0;this.h0=this.h0+y<<0,this.h1=this.h1+p<<0,this.h2=this.h2+l<<0,this.h3=this.h3+d<<0,this.h4=this.h4+v<<0,this.h5=this.h5+A<<0,this.h6=this.h6+w<<0,this.h7=this.h7+b<<0},t.prototype.hex=function(){this.finalize();var t=this.h0,i=this.h1,h=this.h2,s=this.h3,r=this.h4,e=this.h5,n=this.h6,a=this.h7,f=o[t>>28&15]+o[t>>24&15]+o[t>>20&15]+o[t>>16&15]+o[t>>12&15]+o[t>>8&15]+o[t>>4&15]+o[15&t]+o[i>>28&15]+o[i>>24&15]+o[i>>20&15]+o[i>>16&15]+o[i>>12&15]+o[i>>8&15]+o[i>>4&15]+o[15&i]+o[h>>28&15]+o[h>>24&15]+o[h>>20&15]+o[h>>16&15]+o[h>>12&15]+o[h>>8&15]+o[h>>4&15]+o[15&h]+o[s>>28&15]+o[s>>24&15]+o[s>>20&15]+o[s>>16&15]+o[s>>12&15]+o[s>>8&15]+o[s>>4&15]+o[15&s]+o[r>>28&15]+o[r>>24&15]+o[r>>20&15]+o[r>>16&15]+o[r>>12&15]+o[r>>8&15]+o[r>>4&15]+o[15&r]+o[e>>28&15]+o[e>>24&15]+o[e>>20&15]+o[e>>16&15]+o[e>>12&15]+o[e>>8&15]+o[e>>4&15]+o[15&e]+o[n>>28&15]+o[n>>24&15]+o[n>>20&15]+o[n>>16&15]+o[n>>12&15]+o[n>>8&15]+o[n>>4&15]+o[15&n];return this.is224||(f+=o[a>>28&15]+o[a>>24&15]+o[a>>20&15]+o[a>>16&15]+o[a>>12&15]+o[a>>8&15]+o[a>>4&15]+o[15&a]),f},t.prototype.toString=t.prototype.hex,t.prototype.digest=function(){this.finalize();var t=this.h0,i=this.h1,h=this.h2,s=this.h3,r=this.h4,e=this.h5,n=this.h6,a=this.h7,o=[t>>24&255,t>>16&255,t>>8&255,255&t,i>>24&255,i>>16&255,i>>8&255,255&i,h>>24&255,h>>16&255,h>>8&255,255&h,s>>24&255,s>>16&255,s>>8&255,255&s,r>>24&255,r>>16&255,r>>8&255,255&r,e>>24&255,e>>16&255,e>>8&255,255&e,n>>24&255,n>>16&255,n>>8&255,255&n];return this.is224||o.push(a>>24&255,a>>16&255,a>>8&255,255&a),o},t.prototype.array=t.prototype.digest,t.prototype.arrayBuffer=function(){this.finalize();var t=new ArrayBuffer(this.is224?28:32),i=new DataView(t);return i.setUint32(0,this.h0),i.setUint32(4,this.h1),i.setUint32(8,this.h2),i.setUint32(12,this.h3),i.setUint32(16,this.h4),i.setUint32(20,this.h5),i.setUint32(24,this.h6),this.is224||i.setUint32(28,this.h7),t},i.prototype=new t,i.prototype.finalize=function(){if(t.prototype.finalize.call(this),this.inner){this.inner=!1;var i=this.array();t.call(this,this.is224,this.sharedMemory),this.update(this.oKeyPad),this.update(i),t.prototype.finalize.call(this)}};var b=d();b.sha256=b,b.sha224=d(!0),b.sha256.hmac=w(),b.sha224.hmac=w(!0),e?module.exports=b:(s.sha256=b.sha256,s.sha224=b.sha224,n&&define(function(){return b}))}(); \ No newline at end of file diff --git a/module.js b/module.js new file mode 100644 index 0000000..4cf078b --- /dev/null +++ b/module.js @@ -0,0 +1,8 @@ +exports.printMsg = function() { + console.log("Esiur 1.1"); +} + +module.exports = { Warehouse, DistributedConnection}; + + +var WebSocket = require('ws') diff --git a/node_modules/async-limiter/.travis.yml b/node_modules/async-limiter/.travis.yml new file mode 100644 index 0000000..6cf4a7a --- /dev/null +++ b/node_modules/async-limiter/.travis.yml @@ -0,0 +1,7 @@ +language: node_js +node_js: + - "6" + - "node" +script: npm run travis +cache: + yarn: true diff --git a/node_modules/async-limiter/LICENSE b/node_modules/async-limiter/LICENSE new file mode 100644 index 0000000..9c91fb2 --- /dev/null +++ b/node_modules/async-limiter/LICENSE @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2017 Samuel Reed + +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. diff --git a/node_modules/async-limiter/coverage/coverage.json b/node_modules/async-limiter/coverage/coverage.json new file mode 100644 index 0000000..5b4a358 --- /dev/null +++ b/node_modules/async-limiter/coverage/coverage.json @@ -0,0 +1 @@ +{"/Users/samuelreed/git/forks/async-throttle/index.js":{"path":"/Users/samuelreed/git/forks/async-throttle/index.js","s":{"1":1,"2":7,"3":1,"4":6,"5":6,"6":6,"7":6,"8":6,"9":6,"10":1,"11":1,"12":3,"13":13,"14":13,"15":13,"16":1,"17":19,"18":1,"19":45,"20":6,"21":39,"22":13,"23":13,"24":13,"25":13,"26":39,"27":18,"28":6,"29":6,"30":1,"31":6,"32":6,"33":6,"34":1,"35":13,"36":13,"37":1},"b":{"1":[1,6],"2":[6,5],"3":[6,5],"4":[6,39],"5":[13,26],"6":[18,21],"7":[6,0]},"f":{"1":7,"2":3,"3":13,"4":19,"5":45,"6":6,"7":13},"fnMap":{"1":{"name":"Queue","line":3,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":24}}},"2":{"name":"(anonymous_2)","line":22,"loc":{"start":{"line":22,"column":24},"end":{"line":22,"column":41}}},"3":{"name":"(anonymous_3)","line":23,"loc":{"start":{"line":23,"column":28},"end":{"line":23,"column":39}}},"4":{"name":"(anonymous_4)","line":31,"loc":{"start":{"line":31,"column":7},"end":{"line":31,"column":18}}},"5":{"name":"(anonymous_5)","line":36,"loc":{"start":{"line":36,"column":23},"end":{"line":36,"column":34}}},"6":{"name":"(anonymous_6)","line":55,"loc":{"start":{"line":55,"column":25},"end":{"line":55,"column":38}}},"7":{"name":"done","line":62,"loc":{"start":{"line":62,"column":0},"end":{"line":62,"column":16}}}},"statementMap":{"1":{"start":{"line":3,"column":0},"end":{"line":14,"column":1}},"2":{"start":{"line":4,"column":2},"end":{"line":6,"column":3}},"3":{"start":{"line":5,"column":4},"end":{"line":5,"column":30}},"4":{"start":{"line":8,"column":2},"end":{"line":8,"column":26}},"5":{"start":{"line":9,"column":2},"end":{"line":9,"column":53}},"6":{"start":{"line":10,"column":2},"end":{"line":10,"column":19}},"7":{"start":{"line":11,"column":2},"end":{"line":11,"column":17}},"8":{"start":{"line":12,"column":2},"end":{"line":12,"column":16}},"9":{"start":{"line":13,"column":2},"end":{"line":13,"column":31}},"10":{"start":{"line":16,"column":0},"end":{"line":20,"column":2}},"11":{"start":{"line":22,"column":0},"end":{"line":28,"column":3}},"12":{"start":{"line":23,"column":2},"end":{"line":27,"column":4}},"13":{"start":{"line":24,"column":4},"end":{"line":24,"column":75}},"14":{"start":{"line":25,"column":4},"end":{"line":25,"column":16}},"15":{"start":{"line":26,"column":4},"end":{"line":26,"column":24}},"16":{"start":{"line":30,"column":0},"end":{"line":34,"column":3}},"17":{"start":{"line":32,"column":4},"end":{"line":32,"column":43}},"18":{"start":{"line":36,"column":0},"end":{"line":53,"column":2}},"19":{"start":{"line":37,"column":2},"end":{"line":39,"column":3}},"20":{"start":{"line":38,"column":4},"end":{"line":38,"column":11}},"21":{"start":{"line":40,"column":2},"end":{"line":45,"column":3}},"22":{"start":{"line":41,"column":4},"end":{"line":41,"column":32}},"23":{"start":{"line":42,"column":4},"end":{"line":42,"column":19}},"24":{"start":{"line":43,"column":4},"end":{"line":43,"column":20}},"25":{"start":{"line":44,"column":4},"end":{"line":44,"column":16}},"26":{"start":{"line":47,"column":2},"end":{"line":52,"column":3}},"27":{"start":{"line":48,"column":4},"end":{"line":51,"column":5}},"28":{"start":{"line":49,"column":6},"end":{"line":49,"column":30}},"29":{"start":{"line":50,"column":6},"end":{"line":50,"column":27}},"30":{"start":{"line":55,"column":0},"end":{"line":60,"column":2}},"31":{"start":{"line":56,"column":2},"end":{"line":59,"column":3}},"32":{"start":{"line":57,"column":4},"end":{"line":57,"column":22}},"33":{"start":{"line":58,"column":4},"end":{"line":58,"column":16}},"34":{"start":{"line":62,"column":0},"end":{"line":65,"column":1}},"35":{"start":{"line":63,"column":2},"end":{"line":63,"column":17}},"36":{"start":{"line":64,"column":2},"end":{"line":64,"column":14}},"37":{"start":{"line":67,"column":0},"end":{"line":67,"column":23}}},"branchMap":{"1":{"line":4,"type":"if","locations":[{"start":{"line":4,"column":2},"end":{"line":4,"column":2}},{"start":{"line":4,"column":2},"end":{"line":4,"column":2}}]},"2":{"line":8,"type":"binary-expr","locations":[{"start":{"line":8,"column":12},"end":{"line":8,"column":19}},{"start":{"line":8,"column":23},"end":{"line":8,"column":25}}]},"3":{"line":9,"type":"binary-expr","locations":[{"start":{"line":9,"column":21},"end":{"line":9,"column":40}},{"start":{"line":9,"column":44},"end":{"line":9,"column":52}}]},"4":{"line":37,"type":"if","locations":[{"start":{"line":37,"column":2},"end":{"line":37,"column":2}},{"start":{"line":37,"column":2},"end":{"line":37,"column":2}}]},"5":{"line":40,"type":"if","locations":[{"start":{"line":40,"column":2},"end":{"line":40,"column":2}},{"start":{"line":40,"column":2},"end":{"line":40,"column":2}}]},"6":{"line":47,"type":"if","locations":[{"start":{"line":47,"column":2},"end":{"line":47,"column":2}},{"start":{"line":47,"column":2},"end":{"line":47,"column":2}}]},"7":{"line":56,"type":"if","locations":[{"start":{"line":56,"column":2},"end":{"line":56,"column":2}},{"start":{"line":56,"column":2},"end":{"line":56,"column":2}}]}}}} \ No newline at end of file diff --git a/node_modules/async-limiter/coverage/lcov-report/async-throttle/index.html b/node_modules/async-limiter/coverage/lcov-report/async-throttle/index.html new file mode 100644 index 0000000..198882b --- /dev/null +++ b/node_modules/async-limiter/coverage/lcov-report/async-throttle/index.html @@ -0,0 +1,73 @@ + + + + Code coverage report for async-throttle/ + + + + + + +
+

Code coverage report for async-throttle/

+

+ Statements: 100% (37 / 37)      + Branches: 92.86% (13 / 14)      + Functions: 100% (7 / 7)      + Lines: 100% (37 / 37)      + Ignored: none      +

+
All files » async-throttle/
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
index.js100%(37 / 37)92.86%(13 / 14)100%(7 / 7)100%(37 / 37)
+
+
+ + + + + + diff --git a/node_modules/async-limiter/coverage/lcov-report/async-throttle/index.js.html b/node_modules/async-limiter/coverage/lcov-report/async-throttle/index.js.html new file mode 100644 index 0000000..adc030f --- /dev/null +++ b/node_modules/async-limiter/coverage/lcov-report/async-throttle/index.js.html @@ -0,0 +1,246 @@ + + + + Code coverage report for async-throttle/index.js + + + + + + +
+

Code coverage report for async-throttle/index.js

+

+ Statements: 100% (37 / 37)      + Branches: 92.86% (13 / 14)      + Functions: 100% (7 / 7)      + Lines: 100% (37 / 37)      + Ignored: none      +

+
All files » async-throttle/ » index.js
+
+
+

+
+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68  +  +1 +7 +1 +  +  +6 +6 +6 +6 +6 +6 +  +  +1 +  +  +  +  +  +1 +3 +13 +13 +13 +  +  +  +1 +  +19 +  +  +  +1 +45 +6 +  +39 +13 +13 +13 +13 +  +  +39 +18 +6 +6 +  +  +  +  +1 +6 +6 +6 +  +  +  +1 +13 +13 +  +  +1 + 
'use strict';
+ 
+function Queue(options) {
+  if (!(this instanceof Queue)) {
+    return new Queue(options);
+  }
+ 
+  options = options || {};
+  this.concurrency = options.concurrency || Infinity;
+  this.pending = 0;
+  this.jobs = [];
+  this.cbs = [];
+  this._done = done.bind(this);
+}
+ 
+var arrayAddMethods = [
+  'push',
+  'unshift',
+  'splice'
+];
+ 
+arrayAddMethods.forEach(function(method) {
+  Queue.prototype[method] = function() {
+    var methodResult = Array.prototype[method].apply(this.jobs, arguments);
+    this._run();
+    return methodResult;
+  };
+});
+ 
+Object.defineProperty(Queue.prototype, 'length', {
+  get: function() {
+    return this.pending + this.jobs.length;
+  }
+});
+ 
+Queue.prototype._run = function() {
+  if (this.pending === this.concurrency) {
+    return;
+  }
+  if (this.jobs.length) {
+    var job = this.jobs.shift();
+    this.pending++;
+    job(this._done);
+    this._run();
+  }
+ 
+  if (this.pending === 0) {
+    while (this.cbs.length !== 0) {
+      var cb = this.cbs.pop();
+      process.nextTick(cb);
+    }
+  }
+};
+ 
+Queue.prototype.onDone = function(cb) {
+  Eif (typeof cb === 'function') {
+    this.cbs.push(cb);
+    this._run();
+  }
+};
+ 
+function done() {
+  this.pending--;
+  this._run();
+}
+ 
+module.exports = Queue;
+ 
+ +
+ + + + + + diff --git a/node_modules/async-limiter/coverage/lcov-report/base.css b/node_modules/async-limiter/coverage/lcov-report/base.css new file mode 100644 index 0000000..a6a2f32 --- /dev/null +++ b/node_modules/async-limiter/coverage/lcov-report/base.css @@ -0,0 +1,182 @@ +body, html { + margin:0; padding: 0; +} +body { + font-family: Helvetica Neue, Helvetica,Arial; + font-size: 10pt; +} +div.header, div.footer { + background: #eee; + padding: 1em; +} +div.header { + z-index: 100; + position: fixed; + top: 0; + border-bottom: 1px solid #666; + width: 100%; +} +div.footer { + border-top: 1px solid #666; +} +div.body { + margin-top: 10em; +} +div.meta { + font-size: 90%; + text-align: center; +} +h1, h2, h3 { + font-weight: normal; +} +h1 { + font-size: 12pt; +} +h2 { + font-size: 10pt; +} +pre { + font-family: Consolas, Menlo, Monaco, monospace; + margin: 0; + padding: 0; + line-height: 1.3; + font-size: 14px; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} + +div.path { font-size: 110%; } +div.path a:link, div.path a:visited { color: #000; } +table.coverage { border-collapse: collapse; margin:0; padding: 0 } + +table.coverage td { + margin: 0; + padding: 0; + color: #111; + vertical-align: top; +} +table.coverage td.line-count { + width: 50px; + text-align: right; + padding-right: 5px; +} +table.coverage td.line-coverage { + color: #777 !important; + text-align: right; + border-left: 1px solid #666; + border-right: 1px solid #666; +} + +table.coverage td.text { +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 40px; +} +table.coverage td span.cline-neutral { + background: #eee; +} +table.coverage td span.cline-yes { + background: #b5d592; + color: #999; +} +table.coverage td span.cline-no { + background: #fc8c84; +} + +.cstat-yes { color: #111; } +.cstat-no { background: #fc8c84; color: #111; } +.fstat-no { background: #ffc520; color: #111 !important; } +.cbranch-no { background: yellow !important; color: #111; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +.missing-if-branch { + display: inline-block; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: black; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} + +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} + +.entity, .metric { font-weight: bold; } +.metric { display: inline-block; border: 1px solid #333; padding: 0.3em; background: white; } +.metric small { font-size: 80%; font-weight: normal; color: #666; } + +div.coverage-summary table { border-collapse: collapse; margin: 3em; font-size: 110%; } +div.coverage-summary td, div.coverage-summary table th { margin: 0; padding: 0.25em 1em; border-top: 1px solid #666; border-bottom: 1px solid #666; } +div.coverage-summary th { text-align: left; border: 1px solid #666; background: #eee; font-weight: normal; } +div.coverage-summary th.file { border-right: none !important; } +div.coverage-summary th.pic { border-left: none !important; text-align: right; } +div.coverage-summary th.pct { border-right: none !important; } +div.coverage-summary th.abs { border-left: none !important; text-align: right; } +div.coverage-summary td.pct { text-align: right; border-left: 1px solid #666; } +div.coverage-summary td.abs { text-align: right; font-size: 90%; color: #444; border-right: 1px solid #666; } +div.coverage-summary td.file { border-left: 1px solid #666; white-space: nowrap; } +div.coverage-summary td.pic { min-width: 120px !important; } +div.coverage-summary a:link { text-decoration: none; color: #000; } +div.coverage-summary a:visited { text-decoration: none; color: #777; } +div.coverage-summary a:hover { text-decoration: underline; } +div.coverage-summary tfoot td { border-top: 1px solid #666; } + +div.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +div.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +div.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} + +.high { background: #b5d592 !important; } +.medium { background: #ffe87c !important; } +.low { background: #fc8c84 !important; } + +span.cover-fill, span.cover-empty { + display:inline-block; + border:1px solid #444; + background: white; + height: 12px; +} +span.cover-fill { + background: #ccc; + border-right: 1px solid #444; +} +span.cover-empty { + background: white; + border-left: none; +} +span.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } diff --git a/node_modules/async-limiter/coverage/lcov-report/index.html b/node_modules/async-limiter/coverage/lcov-report/index.html new file mode 100644 index 0000000..782a1cf --- /dev/null +++ b/node_modules/async-limiter/coverage/lcov-report/index.html @@ -0,0 +1,73 @@ + + + + Code coverage report for All files + + + + + + +
+

Code coverage report for All files

+

+ Statements: 100% (37 / 37)      + Branches: 92.86% (13 / 14)      + Functions: 100% (7 / 7)      + Lines: 100% (37 / 37)      + Ignored: none      +

+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
async-throttle/100%(37 / 37)92.86%(13 / 14)100%(7 / 7)100%(37 / 37)
+
+
+ + + + + + diff --git a/node_modules/async-limiter/coverage/lcov-report/prettify.css b/node_modules/async-limiter/coverage/lcov-report/prettify.css new file mode 100644 index 0000000..b317a7c --- /dev/null +++ b/node_modules/async-limiter/coverage/lcov-report/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/node_modules/async-limiter/coverage/lcov-report/prettify.js b/node_modules/async-limiter/coverage/lcov-report/prettify.js new file mode 100644 index 0000000..ef51e03 --- /dev/null +++ b/node_modules/async-limiter/coverage/lcov-report/prettify.js @@ -0,0 +1 @@ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/node_modules/async-limiter/coverage/lcov-report/sort-arrow-sprite.png b/node_modules/async-limiter/coverage/lcov-report/sort-arrow-sprite.png new file mode 100644 index 0000000..03f704a Binary files /dev/null and b/node_modules/async-limiter/coverage/lcov-report/sort-arrow-sprite.png differ diff --git a/node_modules/async-limiter/coverage/lcov-report/sorter.js b/node_modules/async-limiter/coverage/lcov-report/sorter.js new file mode 100644 index 0000000..6afb736 --- /dev/null +++ b/node_modules/async-limiter/coverage/lcov-report/sorter.js @@ -0,0 +1,156 @@ +var addSorting = (function () { + "use strict"; + var cols, + currentSort = { + index: 0, + desc: false + }; + + // returns the summary table element + function getTable() { return document.querySelector('.coverage-summary table'); } + // returns the thead element of the summary table + function getTableHeader() { return getTable().querySelector('thead tr'); } + // returns the tbody element of the summary table + function getTableBody() { return getTable().querySelector('tbody'); } + // returns the th element for nth column + function getNthColumn(n) { return getTableHeader().querySelectorAll('th')[n]; } + + // loads all columns + function loadColumns() { + var colNodes = getTableHeader().querySelectorAll('th'), + colNode, + cols = [], + col, + i; + + for (i = 0; i < colNodes.length; i += 1) { + colNode = colNodes[i]; + col = { + key: colNode.getAttribute('data-col'), + sortable: !colNode.getAttribute('data-nosort'), + type: colNode.getAttribute('data-type') || 'string' + }; + cols.push(col); + if (col.sortable) { + col.defaultDescSort = col.type === 'number'; + colNode.innerHTML = colNode.innerHTML + ''; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function (a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function (a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function () { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i =0 ; i < cols.length; i += 1) { + if (cols[i].sortable) { + el = getNthColumn(i).querySelector('.sorter'); + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function () { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(cols); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/node_modules/async-limiter/coverage/lcov.info b/node_modules/async-limiter/coverage/lcov.info new file mode 100644 index 0000000..fbf36aa --- /dev/null +++ b/node_modules/async-limiter/coverage/lcov.info @@ -0,0 +1,74 @@ +TN: +SF:/Users/samuelreed/git/forks/async-throttle/index.js +FN:3,Queue +FN:22,(anonymous_2) +FN:23,(anonymous_3) +FN:31,(anonymous_4) +FN:36,(anonymous_5) +FN:55,(anonymous_6) +FN:62,done +FNF:7 +FNH:7 +FNDA:7,Queue +FNDA:3,(anonymous_2) +FNDA:13,(anonymous_3) +FNDA:19,(anonymous_4) +FNDA:45,(anonymous_5) +FNDA:6,(anonymous_6) +FNDA:13,done +DA:3,1 +DA:4,7 +DA:5,1 +DA:8,6 +DA:9,6 +DA:10,6 +DA:11,6 +DA:12,6 +DA:13,6 +DA:16,1 +DA:22,1 +DA:23,3 +DA:24,13 +DA:25,13 +DA:26,13 +DA:30,1 +DA:32,19 +DA:36,1 +DA:37,45 +DA:38,6 +DA:40,39 +DA:41,13 +DA:42,13 +DA:43,13 +DA:44,13 +DA:47,39 +DA:48,18 +DA:49,6 +DA:50,6 +DA:55,1 +DA:56,6 +DA:57,6 +DA:58,6 +DA:62,1 +DA:63,13 +DA:64,13 +DA:67,1 +LF:37 +LH:37 +BRDA:4,1,0,1 +BRDA:4,1,1,6 +BRDA:8,2,0,6 +BRDA:8,2,1,5 +BRDA:9,3,0,6 +BRDA:9,3,1,5 +BRDA:37,4,0,6 +BRDA:37,4,1,39 +BRDA:40,5,0,13 +BRDA:40,5,1,26 +BRDA:47,6,0,18 +BRDA:47,6,1,21 +BRDA:56,7,0,6 +BRDA:56,7,1,0 +BRF:14 +BRH:13 +end_of_record diff --git a/node_modules/async-limiter/index.js b/node_modules/async-limiter/index.js new file mode 100644 index 0000000..c9bd2f9 --- /dev/null +++ b/node_modules/async-limiter/index.js @@ -0,0 +1,67 @@ +'use strict'; + +function Queue(options) { + if (!(this instanceof Queue)) { + return new Queue(options); + } + + options = options || {}; + this.concurrency = options.concurrency || Infinity; + this.pending = 0; + this.jobs = []; + this.cbs = []; + this._done = done.bind(this); +} + +var arrayAddMethods = [ + 'push', + 'unshift', + 'splice' +]; + +arrayAddMethods.forEach(function(method) { + Queue.prototype[method] = function() { + var methodResult = Array.prototype[method].apply(this.jobs, arguments); + this._run(); + return methodResult; + }; +}); + +Object.defineProperty(Queue.prototype, 'length', { + get: function() { + return this.pending + this.jobs.length; + } +}); + +Queue.prototype._run = function() { + if (this.pending === this.concurrency) { + return; + } + if (this.jobs.length) { + var job = this.jobs.shift(); + this.pending++; + job(this._done); + this._run(); + } + + if (this.pending === 0) { + while (this.cbs.length !== 0) { + var cb = this.cbs.pop(); + process.nextTick(cb); + } + } +}; + +Queue.prototype.onDone = function(cb) { + if (typeof cb === 'function') { + this.cbs.push(cb); + this._run(); + } +}; + +function done() { + this.pending--; + this._run(); +} + +module.exports = Queue; diff --git a/node_modules/async-limiter/package.json b/node_modules/async-limiter/package.json new file mode 100644 index 0000000..d6cdef1 --- /dev/null +++ b/node_modules/async-limiter/package.json @@ -0,0 +1,69 @@ +{ + "_from": "async-limiter@~1.0.0", + "_id": "async-limiter@1.0.0", + "_inBundle": false, + "_integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "_location": "/async-limiter", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "async-limiter@~1.0.0", + "name": "async-limiter", + "escapedName": "async-limiter", + "rawSpec": "~1.0.0", + "saveSpec": null, + "fetchSpec": "~1.0.0" + }, + "_requiredBy": [ + "/ws" + ], + "_resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "_shasum": "78faed8c3d074ab81f22b4e985d79e8738f720f8", + "_spec": "async-limiter@~1.0.0", + "_where": "C:\\www\\esiur-js\\node_modules\\ws", + "author": { + "name": "Samuel Reed" + }, + "bugs": { + "url": "https://github.com/strml/async-limiter/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "asynchronous function queue with adjustable concurrency", + "devDependencies": { + "coveralls": "^2.11.2", + "eslint": "^4.6.1", + "eslint-plugin-mocha": "^4.11.0", + "intelli-espower-loader": "^1.0.1", + "istanbul": "^0.3.2", + "mocha": "^3.5.2", + "power-assert": "^1.4.4" + }, + "homepage": "https://github.com/strml/async-limiter#readme", + "keywords": [ + "throttle", + "async", + "limiter", + "asynchronous", + "job", + "task", + "concurrency", + "concurrent" + ], + "license": "MIT", + "name": "async-limiter", + "repository": { + "type": "git", + "url": "git+https://github.com/strml/async-limiter.git" + }, + "scripts": { + "coverage": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | coveralls", + "example": "node example", + "lint": "eslint .", + "test": "mocha --R intelli-espower-loader test/", + "travis": "npm run lint && npm run coverage" + }, + "version": "1.0.0" +} diff --git a/node_modules/async-limiter/readme.md b/node_modules/async-limiter/readme.md new file mode 100644 index 0000000..dcf4932 --- /dev/null +++ b/node_modules/async-limiter/readme.md @@ -0,0 +1,132 @@ +# Async-Limiter + +A module for limiting concurrent asynchronous actions in flight. Forked from [queue](https://github.com/jessetane/queue). + +[![npm](http://img.shields.io/npm/v/async-limiter.svg?style=flat-square)](http://www.npmjs.org/async-limiter) +[![tests](https://img.shields.io/travis/STRML/async-limiter.svg?style=flat-square&branch=master)](https://travis-ci.org/STRML/async-limiter) +[![coverage](https://img.shields.io/coveralls/STRML/async-limiter.svg?style=flat-square&branch=master)](https://coveralls.io/r/STRML/async-limiter) + +This module exports a class `Limiter` that implements some of the `Array` API. +Pass async functions (ones that accept a callback or return a promise) to an instance's additive array methods. + +## Motivation + +Certain functions, like `zlib`, have [undesirable behavior](https://github.com/nodejs/node/issues/8871#issuecomment-250915913) when +run at infinite concurrency. + +In this case, it is actually faster, and takes far less memory, to limit concurrency. + +This module should do the absolute minimum work necessary to queue up functions. PRs are welcome that would +make this module faster or lighter, but new functionality is not desired. + +Style should confirm to nodejs/node style. + +## Example + +``` javascript +var Limiter = require('async-limiter') + +var t = new Limiter({concurrency: 2}); +var results = [] + +// add jobs using the familiar Array API +t.push(function (cb) { + results.push('two') + cb() +}) + +t.push( + function (cb) { + results.push('four') + cb() + }, + function (cb) { + results.push('five') + cb() + } +) + +t.unshift(function (cb) { + results.push('one') + cb() +}) + +t.splice(2, 0, function (cb) { + results.push('three') + cb() +}) + +// Jobs run automatically. If you want a callback when all are done, +// call 'onDone()'. +t.onDone(function () { + console.log('all done:', results) +}) +``` + +## Zlib Example + +```js +const zlib = require('zlib'); +const Limiter = require('async-limiter'); + +const message = {some: "data"}; +const payload = new Buffer(JSON.stringify(message)); + +// Try with different concurrency values to see how this actually +// slows significantly with higher concurrency! +// +// 5: 1398.607ms +// 10: 1375.668ms +// Infinity: 4423.300ms +// +const t = new Limiter({concurrency: 5}); +function deflate(payload, cb) { + t.push(function(done) { + zlib.deflate(payload, function(err, buffer) { + done(); + cb(err, buffer); + }); + }); +} + +console.time('deflate'); +for(let i = 0; i < 30000; ++i) { + deflate(payload, function (err, buffer) {}); +} +q.onDone(function() { + console.timeEnd('deflate'); +}); +``` + +## Install + +`npm install async-limiter` + +## Test + +`npm test` + +## API + +### `var t = new Limiter([opts])` +Constructor. `opts` may contain inital values for: +* `q.concurrency` + +## Instance methods + +### `q.onDone(fn)` +`fn` will be called once and only once, when the queue is empty. + +## Instance methods mixed in from `Array` +Mozilla has docs on how these methods work [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array). +### `q.push(element1, ..., elementN)` +### `q.unshift(element1, ..., elementN)` +### `q.splice(index , howMany[, element1[, ...[, elementN]]])` + +## Properties +### `q.concurrency` +Max number of jobs the queue should process concurrently, defaults to `Infinity`. + +### `q.length` +Jobs pending + jobs to process (readonly). + diff --git a/node_modules/ws/LICENSE b/node_modules/ws/LICENSE new file mode 100644 index 0000000..a145cd1 --- /dev/null +++ b/node_modules/ws/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2011 Einar Otto Stangvik + +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. diff --git a/node_modules/ws/README.md b/node_modules/ws/README.md new file mode 100644 index 0000000..2b5c611 --- /dev/null +++ b/node_modules/ws/README.md @@ -0,0 +1,449 @@ +# ws: a Node.js WebSocket library + +[![Version npm](https://img.shields.io/npm/v/ws.svg?logo=npm)](https://www.npmjs.com/package/ws) +[![Linux Build](https://img.shields.io/travis/websockets/ws/master.svg?logo=travis)](https://travis-ci.org/websockets/ws) +[![Windows Build](https://img.shields.io/appveyor/ci/lpinca/ws/master.svg?logo=appveyor)](https://ci.appveyor.com/project/lpinca/ws) +[![Coverage Status](https://img.shields.io/coveralls/websockets/ws/master.svg)](https://coveralls.io/github/websockets/ws) + +ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and +server implementation. + +Passes the quite extensive Autobahn test suite: [server][server-report], +[client][client-report]. + +**Note**: This module does not work in the browser. The client in the docs is a +reference to a back end with the role of a client in the WebSocket +communication. Browser clients must use the native +[`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) +object. To make the same code work seamlessly on Node.js and the browser, you +can use one of the many wrappers available on npm, like +[isomorphic-ws](https://github.com/heineiuo/isomorphic-ws). + +## Table of Contents + +- [Protocol support](#protocol-support) +- [Installing](#installing) + - [Opt-in for performance and spec compliance](#opt-in-for-performance-and-spec-compliance) +- [API docs](#api-docs) +- [WebSocket compression](#websocket-compression) +- [Usage examples](#usage-examples) + - [Sending and receiving text data](#sending-and-receiving-text-data) + - [Sending binary data](#sending-binary-data) + - [Simple server](#simple-server) + - [External HTTP/S server](#external-https-server) + - [Multiple servers sharing a single HTTP/S server](#multiple-servers-sharing-a-single-https-server) + - [Server broadcast](#server-broadcast) + - [echo.websocket.org demo](#echowebsocketorg-demo) + - [Other examples](#other-examples) +- [Error handling best practices](#error-handling-best-practices) +- [FAQ](#faq) + - [How to get the IP address of the client?](#how-to-get-the-ip-address-of-the-client) + - [How to detect and close broken connections?](#how-to-detect-and-close-broken-connections) + - [How to connect via a proxy?](#how-to-connect-via-a-proxy) +- [Changelog](#changelog) +- [License](#license) + +## Protocol support + +- **HyBi drafts 07-12** (Use the option `protocolVersion: 8`) +- **HyBi drafts 13-17** (Current default, alternatively option + `protocolVersion: 13`) + +## Installing + +``` +npm install ws +``` + +### Opt-in for performance and spec compliance + +There are 2 optional modules that can be installed along side with the ws +module. These modules are binary addons which improve certain operations. +Prebuilt binaries are available for the most popular platforms so you don't +necessarily need to have a C++ compiler installed on your machine. + +- `npm install --save-optional bufferutil`: Allows to efficiently perform + operations such as masking and unmasking the data payload of the WebSocket + frames. +- `npm install --save-optional utf-8-validate`: Allows to efficiently check if a + message contains valid UTF-8 as required by the spec. + +## API docs + +See [`/doc/ws.md`](./doc/ws.md) for Node.js-like docs for the ws classes. + +## WebSocket compression + +ws supports the [permessage-deflate extension][permessage-deflate] which enables +the client and server to negotiate a compression algorithm and its parameters, +and then selectively apply it to the data payloads of each WebSocket message. + +The extension is disabled by default on the server and enabled by default on the +client. It adds a significant overhead in terms of performance and memory +consumption so we suggest to enable it only if it is really needed. + +Note that Node.js has a variety of issues with high-performance compression, +where increased concurrency, especially on Linux, can lead to [catastrophic +memory fragmentation][node-zlib-bug] and slow performance. If you intend to use +permessage-deflate in production, it is worthwhile to set up a test +representative of your workload and ensure Node.js/zlib will handle it with +acceptable performance and memory usage. + +Tuning of permessage-deflate can be done via the options defined below. You can +also use `zlibDeflateOptions` and `zlibInflateOptions`, which is passed directly +into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs]. + +See [the docs][ws-server-options] for more options. + +```js +const WebSocket = require('ws'); + +const wss = new WebSocket.Server({ + port: 8080, + perMessageDeflate: { + zlibDeflateOptions: { + // See zlib defaults. + chunkSize: 1024, + memLevel: 7, + level: 3 + }, + zlibInflateOptions: { + chunkSize: 10 * 1024 + }, + // Other options settable: + clientNoContextTakeover: true, // Defaults to negotiated value. + serverNoContextTakeover: true, // Defaults to negotiated value. + serverMaxWindowBits: 10, // Defaults to negotiated value. + // Below options specified as default values. + concurrencyLimit: 10, // Limits zlib concurrency for perf. + threshold: 1024 // Size (in bytes) below which messages + // should not be compressed. + } +}); +``` + +The client will only use the extension if it is supported and enabled on the +server. To always disable the extension on the client set the +`perMessageDeflate` option to `false`. + +```js +const WebSocket = require('ws'); + +const ws = new WebSocket('ws://www.host.com/path', { + perMessageDeflate: false +}); +``` + +## Usage examples + +### Sending and receiving text data + +```js +const WebSocket = require('ws'); + +const ws = new WebSocket('ws://www.host.com/path'); + +ws.on('open', function open() { + ws.send('something'); +}); + +ws.on('message', function incoming(data) { + console.log(data); +}); +``` + +### Sending binary data + +```js +const WebSocket = require('ws'); + +const ws = new WebSocket('ws://www.host.com/path'); + +ws.on('open', function open() { + const array = new Float32Array(5); + + for (var i = 0; i < array.length; ++i) { + array[i] = i / 2; + } + + ws.send(array); +}); +``` + +### Simple server + +```js +const WebSocket = require('ws'); + +const wss = new WebSocket.Server({ port: 8080 }); + +wss.on('connection', function connection(ws) { + ws.on('message', function incoming(message) { + console.log('received: %s', message); + }); + + ws.send('something'); +}); +``` + +### External HTTP/S server + +```js +const fs = require('fs'); +const https = require('https'); +const WebSocket = require('ws'); + +const server = new https.createServer({ + cert: fs.readFileSync('/path/to/cert.pem'), + key: fs.readFileSync('/path/to/key.pem') +}); +const wss = new WebSocket.Server({ server }); + +wss.on('connection', function connection(ws) { + ws.on('message', function incoming(message) { + console.log('received: %s', message); + }); + + ws.send('something'); +}); + +server.listen(8080); +``` + +### Multiple servers sharing a single HTTP/S server + +```js +const http = require('http'); +const WebSocket = require('ws'); + +const server = http.createServer(); +const wss1 = new WebSocket.Server({ noServer: true }); +const wss2 = new WebSocket.Server({ noServer: true }); + +wss1.on('connection', function connection(ws) { + // ... +}); + +wss2.on('connection', function connection(ws) { + // ... +}); + +server.on('upgrade', function upgrade(request, socket, head) { + const pathname = url.parse(request.url).pathname; + + if (pathname === '/foo') { + wss1.handleUpgrade(request, socket, head, function done(ws) { + wss1.emit('connection', ws, request); + }); + } else if (pathname === '/bar') { + wss2.handleUpgrade(request, socket, head, function done(ws) { + wss2.emit('connection', ws, request); + }); + } else { + socket.destroy(); + } +}); + +server.listen(8080); +``` + +### Server broadcast + +```js +const WebSocket = require('ws'); + +const wss = new WebSocket.Server({ port: 8080 }); + +// Broadcast to all. +wss.broadcast = function broadcast(data) { + wss.clients.forEach(function each(client) { + if (client.readyState === WebSocket.OPEN) { + client.send(data); + } + }); +}; + +wss.on('connection', function connection(ws) { + ws.on('message', function incoming(data) { + // Broadcast to everyone else. + wss.clients.forEach(function each(client) { + if (client !== ws && client.readyState === WebSocket.OPEN) { + client.send(data); + } + }); + }); +}); +``` + +### echo.websocket.org demo + +```js +const WebSocket = require('ws'); + +const ws = new WebSocket('wss://echo.websocket.org/', { + origin: 'https://websocket.org' +}); + +ws.on('open', function open() { + console.log('connected'); + ws.send(Date.now()); +}); + +ws.on('close', function close() { + console.log('disconnected'); +}); + +ws.on('message', function incoming(data) { + console.log(`Roundtrip time: ${Date.now() - data} ms`); + + setTimeout(function timeout() { + ws.send(Date.now()); + }, 500); +}); +``` + +### Other examples + +For a full example with a browser client communicating with a ws server, see the +examples folder. + +Otherwise, see the test cases. + +## Error handling best practices + +```js +// If the WebSocket is closed before the following send is attempted +ws.send('something'); + +// Errors (both immediate and async write errors) can be detected in an optional +// callback. The callback is also the only way of being notified that data has +// actually been sent. +ws.send('something', function ack(error) { + // If error is not defined, the send has been completed, otherwise the error + // object will indicate what failed. +}); + +// Immediate errors can also be handled with `try...catch`, but **note** that +// since sends are inherently asynchronous, socket write failures will *not* be +// captured when this technique is used. +try { + ws.send('something'); +} catch (e) { + /* handle error */ +} +``` + +## FAQ + +### How to get the IP address of the client? + +The remote IP address can be obtained from the raw socket. + +```js +const WebSocket = require('ws'); + +const wss = new WebSocket.Server({ port: 8080 }); + +wss.on('connection', function connection(ws, req) { + const ip = req.connection.remoteAddress; +}); +``` + +When the server runs behind a proxy like NGINX, the de-facto standard is to use +the `X-Forwarded-For` header. + +```js +wss.on('connection', function connection(ws, req) { + const ip = req.headers['x-forwarded-for'].split(/\s*,\s*/)[0]; +}); +``` + +### How to detect and close broken connections? + +Sometimes the link between the server and the client can be interrupted in a way +that keeps both the server and the client unaware of the broken state of the +connection (e.g. when pulling the cord). + +In these cases ping messages can be used as a means to verify that the remote +endpoint is still responsive. + +```js +const WebSocket = require('ws'); + +function noop() {} + +function heartbeat() { + this.isAlive = true; +} + +const wss = new WebSocket.Server({ port: 8080 }); + +wss.on('connection', function connection(ws) { + ws.isAlive = true; + ws.on('pong', heartbeat); +}); + +const interval = setInterval(function ping() { + wss.clients.forEach(function each(ws) { + if (ws.isAlive === false) return ws.terminate(); + + ws.isAlive = false; + ws.ping(noop); + }); +}, 30000); +``` + +Pong messages are automatically sent in response to ping messages as required by +the spec. + +Just like the server example above your clients might as well lose connection +without knowing it. You might want to add a ping listener on your clients to +prevent that. A simple implementation would be: + +```js +const WebSocket = require('ws'); + +function heartbeat() { + clearTimeout(this.pingTimeout); + + // Use `WebSocket#terminate()` and not `WebSocket#close()`. Delay should be + // equal to the interval at which your server sends out pings plus a + // conservative assumption of the latency. + this.pingTimeout = setTimeout(() => { + this.terminate(); + }, 30000 + 1000); +} + +const client = new WebSocket('wss://echo.websocket.org/'); + +client.on('open', heartbeat); +client.on('ping', heartbeat); +client.on('close', function clear() { + clearTimeout(this.pingTimeout); +}); +``` + +### How to connect via a proxy? + +Use a custom `http.Agent` implementation like [https-proxy-agent][] or +[socks-proxy-agent][]. + +## Changelog + +We're using the GitHub [releases][changelog] for changelog entries. + +## License + +[MIT](LICENSE) + +[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent +[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent +[client-report]: http://websockets.github.io/ws/autobahn/clients/ +[server-report]: http://websockets.github.io/ws/autobahn/servers/ +[permessage-deflate]: https://tools.ietf.org/html/rfc7692 +[changelog]: https://github.com/websockets/ws/releases +[node-zlib-bug]: https://github.com/nodejs/node/issues/8871 +[node-zlib-deflaterawdocs]: + https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options +[ws-server-options]: + https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback diff --git a/node_modules/ws/browser.js b/node_modules/ws/browser.js new file mode 100644 index 0000000..7820779 --- /dev/null +++ b/node_modules/ws/browser.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = function() { + throw new Error( + 'ws does not work in the browser. Browser clients must use the native ' + + 'WebSocket object' + ); +}; diff --git a/node_modules/ws/index.js b/node_modules/ws/index.js new file mode 100644 index 0000000..b8d6be1 --- /dev/null +++ b/node_modules/ws/index.js @@ -0,0 +1,9 @@ +'use strict'; + +const WebSocket = require('./lib/websocket'); + +WebSocket.Server = require('./lib/websocket-server'); +WebSocket.Receiver = require('./lib/receiver'); +WebSocket.Sender = require('./lib/sender'); + +module.exports = WebSocket; diff --git a/node_modules/ws/lib/buffer-util.js b/node_modules/ws/lib/buffer-util.js new file mode 100644 index 0000000..8fcb88f --- /dev/null +++ b/node_modules/ws/lib/buffer-util.js @@ -0,0 +1,144 @@ +'use strict'; + +const { EMPTY_BUFFER } = require('./constants'); + +/** + * Merges an array of buffers into a new buffer. + * + * @param {Buffer[]} list The array of buffers to concat + * @param {Number} totalLength The total length of buffers in the list + * @return {Buffer} The resulting buffer + * @public + */ +function concat(list, totalLength) { + if (list.length === 0) return EMPTY_BUFFER; + if (list.length === 1) return list[0]; + + const target = Buffer.allocUnsafe(totalLength); + var offset = 0; + + for (var i = 0; i < list.length; i++) { + const buf = list[i]; + buf.copy(target, offset); + offset += buf.length; + } + + return target; +} + +/** + * Masks a buffer using the given mask. + * + * @param {Buffer} source The buffer to mask + * @param {Buffer} mask The mask to use + * @param {Buffer} output The buffer where to store the result + * @param {Number} offset The offset at which to start writing + * @param {Number} length The number of bytes to mask. + * @public + */ +function _mask(source, mask, output, offset, length) { + for (var i = 0; i < length; i++) { + output[offset + i] = source[i] ^ mask[i & 3]; + } +} + +/** + * Unmasks a buffer using the given mask. + * + * @param {Buffer} buffer The buffer to unmask + * @param {Buffer} mask The mask to use + * @public + */ +function _unmask(buffer, mask) { + // Required until https://github.com/nodejs/node/issues/9006 is resolved. + const length = buffer.length; + for (var i = 0; i < length; i++) { + buffer[i] ^= mask[i & 3]; + } +} + +/** + * Converts a buffer to an `ArrayBuffer`. + * + * @param {Buffer} buf The buffer to convert + * @return {ArrayBuffer} Converted buffer + * @public + */ +function toArrayBuffer(buf) { + if (buf.byteLength === buf.buffer.byteLength) { + return buf.buffer; + } + + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); +} + +/** + * Converts `data` to a `Buffer`. + * + * @param {*} data The data to convert + * @return {Buffer} The buffer + * @throws {TypeError} + * @public + */ +function toBuffer(data) { + toBuffer.readOnly = true; + + if (Buffer.isBuffer(data)) return data; + + var buf; + + if (data instanceof ArrayBuffer) { + buf = Buffer.from(data); + } else if (ArrayBuffer.isView(data)) { + buf = viewToBuffer(data); + } else { + buf = Buffer.from(data); + toBuffer.readOnly = false; + } + + return buf; +} + +/** + * Converts an `ArrayBuffer` view into a buffer. + * + * @param {(DataView|TypedArray)} view The view to convert + * @return {Buffer} Converted view + * @private + */ +function viewToBuffer(view) { + const buf = Buffer.from(view.buffer); + + if (view.byteLength !== view.buffer.byteLength) { + return buf.slice(view.byteOffset, view.byteOffset + view.byteLength); + } + + return buf; +} + +try { + const bufferUtil = require('bufferutil'); + const bu = bufferUtil.BufferUtil || bufferUtil; + + module.exports = { + concat, + mask(source, mask, output, offset, length) { + if (length < 48) _mask(source, mask, output, offset, length); + else bu.mask(source, mask, output, offset, length); + }, + toArrayBuffer, + toBuffer, + unmask(buffer, mask) { + if (buffer.length < 32) _unmask(buffer, mask); + else bu.unmask(buffer, mask); + } + }; +} catch (e) /* istanbul ignore next */ { + module.exports = { + concat, + mask: _mask, + toArrayBuffer, + toBuffer, + unmask: _unmask + }; +} diff --git a/node_modules/ws/lib/constants.js b/node_modules/ws/lib/constants.js new file mode 100644 index 0000000..4082981 --- /dev/null +++ b/node_modules/ws/lib/constants.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = { + BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'], + GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', + kStatusCode: Symbol('status-code'), + kWebSocket: Symbol('websocket'), + EMPTY_BUFFER: Buffer.alloc(0), + NOOP: () => {} +}; diff --git a/node_modules/ws/lib/event-target.js b/node_modules/ws/lib/event-target.js new file mode 100644 index 0000000..44c81d9 --- /dev/null +++ b/node_modules/ws/lib/event-target.js @@ -0,0 +1,170 @@ +'use strict'; + +/** + * Class representing an event. + * + * @private + */ +class Event { + /** + * Create a new `Event`. + * + * @param {String} type The name of the event + * @param {Object} target A reference to the target to which the event was dispatched + */ + constructor(type, target) { + this.target = target; + this.type = type; + } +} + +/** + * Class representing a message event. + * + * @extends Event + * @private + */ +class MessageEvent extends Event { + /** + * Create a new `MessageEvent`. + * + * @param {(String|Buffer|ArrayBuffer|Buffer[])} data The received data + * @param {WebSocket} target A reference to the target to which the event was dispatched + */ + constructor(data, target) { + super('message', target); + + this.data = data; + } +} + +/** + * Class representing a close event. + * + * @extends Event + * @private + */ +class CloseEvent extends Event { + /** + * Create a new `CloseEvent`. + * + * @param {Number} code The status code explaining why the connection is being closed + * @param {String} reason A human-readable string explaining why the connection is closing + * @param {WebSocket} target A reference to the target to which the event was dispatched + */ + constructor(code, reason, target) { + super('close', target); + + this.wasClean = target._closeFrameReceived && target._closeFrameSent; + this.reason = reason; + this.code = code; + } +} + +/** + * Class representing an open event. + * + * @extends Event + * @private + */ +class OpenEvent extends Event { + /** + * Create a new `OpenEvent`. + * + * @param {WebSocket} target A reference to the target to which the event was dispatched + */ + constructor(target) { + super('open', target); + } +} + +/** + * Class representing an error event. + * + * @extends Event + * @private + */ +class ErrorEvent extends Event { + /** + * Create a new `ErrorEvent`. + * + * @param {Object} error The error that generated this event + * @param {WebSocket} target A reference to the target to which the event was dispatched + */ + constructor(error, target) { + super('error', target); + + this.message = error.message; + this.error = error; + } +} + +/** + * This provides methods for emulating the `EventTarget` interface. It's not + * meant to be used directly. + * + * @mixin + */ +const EventTarget = { + /** + * Register an event listener. + * + * @param {String} method A string representing the event type to listen for + * @param {Function} listener The listener to add + * @public + */ + addEventListener(method, listener) { + if (typeof listener !== 'function') return; + + function onMessage(data) { + listener.call(this, new MessageEvent(data, this)); + } + + function onClose(code, message) { + listener.call(this, new CloseEvent(code, message, this)); + } + + function onError(error) { + listener.call(this, new ErrorEvent(error, this)); + } + + function onOpen() { + listener.call(this, new OpenEvent(this)); + } + + if (method === 'message') { + onMessage._listener = listener; + this.on(method, onMessage); + } else if (method === 'close') { + onClose._listener = listener; + this.on(method, onClose); + } else if (method === 'error') { + onError._listener = listener; + this.on(method, onError); + } else if (method === 'open') { + onOpen._listener = listener; + this.on(method, onOpen); + } else { + this.on(method, listener); + } + }, + + /** + * Remove an event listener. + * + * @param {String} method A string representing the event type to remove + * @param {Function} listener The listener to remove + * @public + */ + removeEventListener(method, listener) { + const listeners = this.listeners(method); + + for (var i = 0; i < listeners.length; i++) { + if (listeners[i] === listener || listeners[i]._listener === listener) { + this.removeListener(method, listeners[i]); + } + } + } +}; + +module.exports = EventTarget; diff --git a/node_modules/ws/lib/extension.js b/node_modules/ws/lib/extension.js new file mode 100644 index 0000000..47096b9 --- /dev/null +++ b/node_modules/ws/lib/extension.js @@ -0,0 +1,222 @@ +'use strict'; + +// +// Allowed token characters: +// +// '!', '#', '$', '%', '&', ''', '*', '+', '-', +// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~' +// +// tokenChars[32] === 0 // ' ' +// tokenChars[33] === 1 // '!' +// tokenChars[34] === 0 // '"' +// ... +// +// prettier-ignore +const tokenChars = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 + 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127 +]; + +/** + * Adds an offer to the map of extension offers or a parameter to the map of + * parameters. + * + * @param {Object} dest The map of extension offers or parameters + * @param {String} name The extension or parameter name + * @param {(Object|Boolean|String)} elem The extension parameters or the + * parameter value + * @private + */ +function push(dest, name, elem) { + if (Object.prototype.hasOwnProperty.call(dest, name)) dest[name].push(elem); + else dest[name] = [elem]; +} + +/** + * Parses the `Sec-WebSocket-Extensions` header into an object. + * + * @param {String} header The field value of the header + * @return {Object} The parsed object + * @public + */ +function parse(header) { + const offers = {}; + + if (header === undefined || header === '') return offers; + + var params = {}; + var mustUnescape = false; + var isEscaping = false; + var inQuotes = false; + var extensionName; + var paramName; + var start = -1; + var end = -1; + + for (var i = 0; i < header.length; i++) { + const code = header.charCodeAt(i); + + if (extensionName === undefined) { + if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (code === 0x20 /* ' ' */ || code === 0x09 /* '\t' */) { + if (end === -1 && start !== -1) end = i; + } else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + + if (end === -1) end = i; + const name = header.slice(start, end); + if (code === 0x2c) { + push(offers, name, params); + params = {}; + } else { + extensionName = name; + } + + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else if (paramName === undefined) { + if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (code === 0x20 || code === 0x09) { + if (end === -1 && start !== -1) end = i; + } else if (code === 0x3b || code === 0x2c) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + + if (end === -1) end = i; + push(params, header.slice(start, end), true); + if (code === 0x2c) { + push(offers, extensionName, params); + params = {}; + extensionName = undefined; + } + + start = end = -1; + } else if (code === 0x3d /* '=' */ && start !== -1 && end === -1) { + paramName = header.slice(start, i); + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else { + // + // The value of a quoted-string after unescaping must conform to the + // token ABNF, so only token characters are valid. + // Ref: https://tools.ietf.org/html/rfc6455#section-9.1 + // + if (isEscaping) { + if (tokenChars[code] !== 1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + if (start === -1) start = i; + else if (!mustUnescape) mustUnescape = true; + isEscaping = false; + } else if (inQuotes) { + if (tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (code === 0x22 /* '"' */ && start !== -1) { + inQuotes = false; + end = i; + } else if (code === 0x5c /* '\' */) { + isEscaping = true; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) { + inQuotes = true; + } else if (end === -1 && tokenChars[code] === 1) { + if (start === -1) start = i; + } else if (start !== -1 && (code === 0x20 || code === 0x09)) { + if (end === -1) end = i; + } else if (code === 0x3b || code === 0x2c) { + if (start === -1) { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + + if (end === -1) end = i; + var value = header.slice(start, end); + if (mustUnescape) { + value = value.replace(/\\/g, ''); + mustUnescape = false; + } + push(params, paramName, value); + if (code === 0x2c) { + push(offers, extensionName, params); + params = {}; + extensionName = undefined; + } + + paramName = undefined; + start = end = -1; + } else { + throw new SyntaxError(`Unexpected character at index ${i}`); + } + } + } + + if (start === -1 || inQuotes) { + throw new SyntaxError('Unexpected end of input'); + } + + if (end === -1) end = i; + const token = header.slice(start, end); + if (extensionName === undefined) { + push(offers, token, {}); + } else { + if (paramName === undefined) { + push(params, token, true); + } else if (mustUnescape) { + push(params, paramName, token.replace(/\\/g, '')); + } else { + push(params, paramName, token); + } + push(offers, extensionName, params); + } + + return offers; +} + +/** + * Builds the `Sec-WebSocket-Extensions` header field value. + * + * @param {Object} extensions The map of extensions and parameters to format + * @return {String} A string representing the given object + * @public + */ +function format(extensions) { + return Object.keys(extensions) + .map((extension) => { + var configurations = extensions[extension]; + if (!Array.isArray(configurations)) configurations = [configurations]; + return configurations + .map((params) => { + return [extension] + .concat( + Object.keys(params).map((k) => { + var values = params[k]; + if (!Array.isArray(values)) values = [values]; + return values + .map((v) => (v === true ? k : `${k}=${v}`)) + .join('; '); + }) + ) + .join('; '); + }) + .join(', '); + }) + .join(', '); +} + +module.exports = { format, parse }; diff --git a/node_modules/ws/lib/permessage-deflate.js b/node_modules/ws/lib/permessage-deflate.js new file mode 100644 index 0000000..9c88764 --- /dev/null +++ b/node_modules/ws/lib/permessage-deflate.js @@ -0,0 +1,502 @@ +'use strict'; + +const Limiter = require('async-limiter'); +const zlib = require('zlib'); + +const bufferUtil = require('./buffer-util'); +const { kStatusCode, NOOP } = require('./constants'); + +const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]); +const EMPTY_BLOCK = Buffer.from([0x00]); + +const kPerMessageDeflate = Symbol('permessage-deflate'); +const kTotalLength = Symbol('total-length'); +const kCallback = Symbol('callback'); +const kBuffers = Symbol('buffers'); +const kError = Symbol('error'); + +// +// We limit zlib concurrency, which prevents severe memory fragmentation +// as documented in https://github.com/nodejs/node/issues/8871#issuecomment-250915913 +// and https://github.com/websockets/ws/issues/1202 +// +// Intentionally global; it's the global thread pool that's an issue. +// +let zlibLimiter; + +/** + * permessage-deflate implementation. + */ +class PerMessageDeflate { + /** + * Creates a PerMessageDeflate instance. + * + * @param {Object} options Configuration options + * @param {Boolean} options.serverNoContextTakeover Request/accept disabling + * of server context takeover + * @param {Boolean} options.clientNoContextTakeover Advertise/acknowledge + * disabling of client context takeover + * @param {(Boolean|Number)} options.serverMaxWindowBits Request/confirm the + * use of a custom server window size + * @param {(Boolean|Number)} options.clientMaxWindowBits Advertise support + * for, or request, a custom client window size + * @param {Object} options.zlibDeflateOptions Options to pass to zlib on deflate + * @param {Object} options.zlibInflateOptions Options to pass to zlib on inflate + * @param {Number} options.threshold Size (in bytes) below which messages + * should not be compressed + * @param {Number} options.concurrencyLimit The number of concurrent calls to + * zlib + * @param {Boolean} isServer Create the instance in either server or client + * mode + * @param {Number} maxPayload The maximum allowed message length + */ + constructor(options, isServer, maxPayload) { + this._maxPayload = maxPayload | 0; + this._options = options || {}; + this._threshold = + this._options.threshold !== undefined ? this._options.threshold : 1024; + this._isServer = !!isServer; + this._deflate = null; + this._inflate = null; + + this.params = null; + + if (!zlibLimiter) { + const concurrency = + this._options.concurrencyLimit !== undefined + ? this._options.concurrencyLimit + : 10; + zlibLimiter = new Limiter({ concurrency }); + } + } + + /** + * @type {String} + */ + static get extensionName() { + return 'permessage-deflate'; + } + + /** + * Create an extension negotiation offer. + * + * @return {Object} Extension parameters + * @public + */ + offer() { + const params = {}; + + if (this._options.serverNoContextTakeover) { + params.server_no_context_takeover = true; + } + if (this._options.clientNoContextTakeover) { + params.client_no_context_takeover = true; + } + if (this._options.serverMaxWindowBits) { + params.server_max_window_bits = this._options.serverMaxWindowBits; + } + if (this._options.clientMaxWindowBits) { + params.client_max_window_bits = this._options.clientMaxWindowBits; + } else if (this._options.clientMaxWindowBits == null) { + params.client_max_window_bits = true; + } + + return params; + } + + /** + * Accept an extension negotiation offer/response. + * + * @param {Array} configurations The extension negotiation offers/reponse + * @return {Object} Accepted configuration + * @public + */ + accept(configurations) { + configurations = this.normalizeParams(configurations); + + this.params = this._isServer + ? this.acceptAsServer(configurations) + : this.acceptAsClient(configurations); + + return this.params; + } + + /** + * Releases all resources used by the extension. + * + * @public + */ + cleanup() { + if (this._inflate) { + this._inflate.close(); + this._inflate = null; + } + + if (this._deflate) { + this._deflate.close(); + this._deflate = null; + } + } + + /** + * Accept an extension negotiation offer. + * + * @param {Array} offers The extension negotiation offers + * @return {Object} Accepted configuration + * @private + */ + acceptAsServer(offers) { + const opts = this._options; + const accepted = offers.find((params) => { + if ( + (opts.serverNoContextTakeover === false && + params.server_no_context_takeover) || + (params.server_max_window_bits && + (opts.serverMaxWindowBits === false || + (typeof opts.serverMaxWindowBits === 'number' && + opts.serverMaxWindowBits > params.server_max_window_bits))) || + (typeof opts.clientMaxWindowBits === 'number' && + !params.client_max_window_bits) + ) { + return false; + } + + return true; + }); + + if (!accepted) { + throw new Error('None of the extension offers can be accepted'); + } + + if (opts.serverNoContextTakeover) { + accepted.server_no_context_takeover = true; + } + if (opts.clientNoContextTakeover) { + accepted.client_no_context_takeover = true; + } + if (typeof opts.serverMaxWindowBits === 'number') { + accepted.server_max_window_bits = opts.serverMaxWindowBits; + } + if (typeof opts.clientMaxWindowBits === 'number') { + accepted.client_max_window_bits = opts.clientMaxWindowBits; + } else if ( + accepted.client_max_window_bits === true || + opts.clientMaxWindowBits === false + ) { + delete accepted.client_max_window_bits; + } + + return accepted; + } + + /** + * Accept the extension negotiation response. + * + * @param {Array} response The extension negotiation response + * @return {Object} Accepted configuration + * @private + */ + acceptAsClient(response) { + const params = response[0]; + + if ( + this._options.clientNoContextTakeover === false && + params.client_no_context_takeover + ) { + throw new Error('Unexpected parameter "client_no_context_takeover"'); + } + + if (!params.client_max_window_bits) { + if (typeof this._options.clientMaxWindowBits === 'number') { + params.client_max_window_bits = this._options.clientMaxWindowBits; + } + } else if ( + this._options.clientMaxWindowBits === false || + (typeof this._options.clientMaxWindowBits === 'number' && + params.client_max_window_bits > this._options.clientMaxWindowBits) + ) { + throw new Error( + 'Unexpected or invalid parameter "client_max_window_bits"' + ); + } + + return params; + } + + /** + * Normalize parameters. + * + * @param {Array} configurations The extension negotiation offers/reponse + * @return {Array} The offers/response with normalized parameters + * @private + */ + normalizeParams(configurations) { + configurations.forEach((params) => { + Object.keys(params).forEach((key) => { + var value = params[key]; + + if (value.length > 1) { + throw new Error(`Parameter "${key}" must have only a single value`); + } + + value = value[0]; + + if (key === 'client_max_window_bits') { + if (value !== true) { + const num = +value; + if (!Number.isInteger(num) || num < 8 || num > 15) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + value = num; + } else if (!this._isServer) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + } else if (key === 'server_max_window_bits') { + const num = +value; + if (!Number.isInteger(num) || num < 8 || num > 15) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + value = num; + } else if ( + key === 'client_no_context_takeover' || + key === 'server_no_context_takeover' + ) { + if (value !== true) { + throw new TypeError( + `Invalid value for parameter "${key}": ${value}` + ); + } + } else { + throw new Error(`Unknown parameter "${key}"`); + } + + params[key] = value; + }); + }); + + return configurations; + } + + /** + * Decompress data. Concurrency limited by async-limiter. + * + * @param {Buffer} data Compressed data + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @public + */ + decompress(data, fin, callback) { + zlibLimiter.push((done) => { + this._decompress(data, fin, (err, result) => { + done(); + callback(err, result); + }); + }); + } + + /** + * Compress data. Concurrency limited by async-limiter. + * + * @param {Buffer} data Data to compress + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @public + */ + compress(data, fin, callback) { + zlibLimiter.push((done) => { + this._compress(data, fin, (err, result) => { + done(); + callback(err, result); + }); + }); + } + + /** + * Decompress data. + * + * @param {Buffer} data Compressed data + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @private + */ + _decompress(data, fin, callback) { + const endpoint = this._isServer ? 'client' : 'server'; + + if (!this._inflate) { + const key = `${endpoint}_max_window_bits`; + const windowBits = + typeof this.params[key] !== 'number' + ? zlib.Z_DEFAULT_WINDOWBITS + : this.params[key]; + + this._inflate = zlib.createInflateRaw( + Object.assign({}, this._options.zlibInflateOptions, { windowBits }) + ); + this._inflate[kPerMessageDeflate] = this; + this._inflate[kTotalLength] = 0; + this._inflate[kBuffers] = []; + this._inflate.on('error', inflateOnError); + this._inflate.on('data', inflateOnData); + } + + this._inflate[kCallback] = callback; + + this._inflate.write(data); + if (fin) this._inflate.write(TRAILER); + + this._inflate.flush(() => { + const err = this._inflate[kError]; + + if (err) { + this._inflate.close(); + this._inflate = null; + callback(err); + return; + } + + const data = bufferUtil.concat( + this._inflate[kBuffers], + this._inflate[kTotalLength] + ); + + if (fin && this.params[`${endpoint}_no_context_takeover`]) { + this._inflate.close(); + this._inflate = null; + } else { + this._inflate[kTotalLength] = 0; + this._inflate[kBuffers] = []; + } + + callback(null, data); + }); + } + + /** + * Compress data. + * + * @param {Buffer} data Data to compress + * @param {Boolean} fin Specifies whether or not this is the last fragment + * @param {Function} callback Callback + * @private + */ + _compress(data, fin, callback) { + if (!data || data.length === 0) { + process.nextTick(callback, null, EMPTY_BLOCK); + return; + } + + const endpoint = this._isServer ? 'server' : 'client'; + + if (!this._deflate) { + const key = `${endpoint}_max_window_bits`; + const windowBits = + typeof this.params[key] !== 'number' + ? zlib.Z_DEFAULT_WINDOWBITS + : this.params[key]; + + this._deflate = zlib.createDeflateRaw( + Object.assign({}, this._options.zlibDeflateOptions, { windowBits }) + ); + + this._deflate[kTotalLength] = 0; + this._deflate[kBuffers] = []; + + // + // An `'error'` event is emitted, only on Node.js < 10.0.0, if the + // `zlib.DeflateRaw` instance is closed while data is being processed. + // This can happen if `PerMessageDeflate#cleanup()` is called at the wrong + // time due to an abnormal WebSocket closure. + // + this._deflate.on('error', NOOP); + this._deflate.on('data', deflateOnData); + } + + this._deflate.write(data); + this._deflate.flush(zlib.Z_SYNC_FLUSH, () => { + if (!this._deflate) { + // + // This `if` statement is only needed for Node.js < 10.0.0 because as of + // commit https://github.com/nodejs/node/commit/5e3f5164, the flush + // callback is no longer called if the deflate stream is closed while + // data is being processed. + // + return; + } + + var data = bufferUtil.concat( + this._deflate[kBuffers], + this._deflate[kTotalLength] + ); + + if (fin) data = data.slice(0, data.length - 4); + + if (fin && this.params[`${endpoint}_no_context_takeover`]) { + this._deflate.close(); + this._deflate = null; + } else { + this._deflate[kTotalLength] = 0; + this._deflate[kBuffers] = []; + } + + callback(null, data); + }); + } +} + +module.exports = PerMessageDeflate; + +/** + * The listener of the `zlib.DeflateRaw` stream `'data'` event. + * + * @param {Buffer} chunk A chunk of data + * @private + */ +function deflateOnData(chunk) { + this[kBuffers].push(chunk); + this[kTotalLength] += chunk.length; +} + +/** + * The listener of the `zlib.InflateRaw` stream `'data'` event. + * + * @param {Buffer} chunk A chunk of data + * @private + */ +function inflateOnData(chunk) { + this[kTotalLength] += chunk.length; + + if ( + this[kPerMessageDeflate]._maxPayload < 1 || + this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload + ) { + this[kBuffers].push(chunk); + return; + } + + this[kError] = new RangeError('Max payload size exceeded'); + this[kError][kStatusCode] = 1009; + this.removeListener('data', inflateOnData); + this.reset(); +} + +/** + * The listener of the `zlib.InflateRaw` stream `'error'` event. + * + * @param {Error} err The emitted error + * @private + */ +function inflateOnError(err) { + // + // There is no need to call `Zlib#close()` as the handle is automatically + // closed when an error is emitted. + // + this[kPerMessageDeflate]._inflate = null; + err[kStatusCode] = 1007; + this[kCallback](err); +} diff --git a/node_modules/ws/lib/receiver.js b/node_modules/ws/lib/receiver.js new file mode 100644 index 0000000..0a8d76d --- /dev/null +++ b/node_modules/ws/lib/receiver.js @@ -0,0 +1,492 @@ +'use strict'; + +const { Writable } = require('stream'); + +const PerMessageDeflate = require('./permessage-deflate'); +const { + BINARY_TYPES, + EMPTY_BUFFER, + kStatusCode, + kWebSocket +} = require('./constants'); +const { concat, toArrayBuffer, unmask } = require('./buffer-util'); +const { isValidStatusCode, isValidUTF8 } = require('./validation'); + +const GET_INFO = 0; +const GET_PAYLOAD_LENGTH_16 = 1; +const GET_PAYLOAD_LENGTH_64 = 2; +const GET_MASK = 3; +const GET_DATA = 4; +const INFLATING = 5; + +/** + * HyBi Receiver implementation. + * + * @extends stream.Writable + */ +class Receiver extends Writable { + /** + * Creates a Receiver instance. + * + * @param {String} binaryType The type for binary data + * @param {Object} extensions An object containing the negotiated extensions + * @param {Number} maxPayload The maximum allowed message length + */ + constructor(binaryType, extensions, maxPayload) { + super(); + + this._binaryType = binaryType || BINARY_TYPES[0]; + this[kWebSocket] = undefined; + this._extensions = extensions || {}; + this._maxPayload = maxPayload | 0; + + this._bufferedBytes = 0; + this._buffers = []; + + this._compressed = false; + this._payloadLength = 0; + this._mask = undefined; + this._fragmented = 0; + this._masked = false; + this._fin = false; + this._opcode = 0; + + this._totalPayloadLength = 0; + this._messageLength = 0; + this._fragments = []; + + this._state = GET_INFO; + this._loop = false; + } + + /** + * Implements `Writable.prototype._write()`. + * + * @param {Buffer} chunk The chunk of data to write + * @param {String} encoding The character encoding of `chunk` + * @param {Function} cb Callback + */ + _write(chunk, encoding, cb) { + if (this._opcode === 0x08 && this._state == GET_INFO) return cb(); + + this._bufferedBytes += chunk.length; + this._buffers.push(chunk); + this.startLoop(cb); + } + + /** + * Consumes `n` bytes from the buffered data. + * + * @param {Number} n The number of bytes to consume + * @return {Buffer} The consumed bytes + * @private + */ + consume(n) { + this._bufferedBytes -= n; + + if (n === this._buffers[0].length) return this._buffers.shift(); + + if (n < this._buffers[0].length) { + const buf = this._buffers[0]; + this._buffers[0] = buf.slice(n); + return buf.slice(0, n); + } + + const dst = Buffer.allocUnsafe(n); + + do { + const buf = this._buffers[0]; + + if (n >= buf.length) { + this._buffers.shift().copy(dst, dst.length - n); + } else { + buf.copy(dst, dst.length - n, 0, n); + this._buffers[0] = buf.slice(n); + } + + n -= buf.length; + } while (n > 0); + + return dst; + } + + /** + * Starts the parsing loop. + * + * @param {Function} cb Callback + * @private + */ + startLoop(cb) { + var err; + this._loop = true; + + do { + switch (this._state) { + case GET_INFO: + err = this.getInfo(); + break; + case GET_PAYLOAD_LENGTH_16: + err = this.getPayloadLength16(); + break; + case GET_PAYLOAD_LENGTH_64: + err = this.getPayloadLength64(); + break; + case GET_MASK: + this.getMask(); + break; + case GET_DATA: + err = this.getData(cb); + break; + default: + // `INFLATING` + this._loop = false; + return; + } + } while (this._loop); + + cb(err); + } + + /** + * Reads the first two bytes of a frame. + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + getInfo() { + if (this._bufferedBytes < 2) { + this._loop = false; + return; + } + + const buf = this.consume(2); + + if ((buf[0] & 0x30) !== 0x00) { + this._loop = false; + return error(RangeError, 'RSV2 and RSV3 must be clear', true, 1002); + } + + const compressed = (buf[0] & 0x40) === 0x40; + + if (compressed && !this._extensions[PerMessageDeflate.extensionName]) { + this._loop = false; + return error(RangeError, 'RSV1 must be clear', true, 1002); + } + + this._fin = (buf[0] & 0x80) === 0x80; + this._opcode = buf[0] & 0x0f; + this._payloadLength = buf[1] & 0x7f; + + if (this._opcode === 0x00) { + if (compressed) { + this._loop = false; + return error(RangeError, 'RSV1 must be clear', true, 1002); + } + + if (!this._fragmented) { + this._loop = false; + return error(RangeError, 'invalid opcode 0', true, 1002); + } + + this._opcode = this._fragmented; + } else if (this._opcode === 0x01 || this._opcode === 0x02) { + if (this._fragmented) { + this._loop = false; + return error(RangeError, `invalid opcode ${this._opcode}`, true, 1002); + } + + this._compressed = compressed; + } else if (this._opcode > 0x07 && this._opcode < 0x0b) { + if (!this._fin) { + this._loop = false; + return error(RangeError, 'FIN must be set', true, 1002); + } + + if (compressed) { + this._loop = false; + return error(RangeError, 'RSV1 must be clear', true, 1002); + } + + if (this._payloadLength > 0x7d) { + this._loop = false; + return error( + RangeError, + `invalid payload length ${this._payloadLength}`, + true, + 1002 + ); + } + } else { + this._loop = false; + return error(RangeError, `invalid opcode ${this._opcode}`, true, 1002); + } + + if (!this._fin && !this._fragmented) this._fragmented = this._opcode; + this._masked = (buf[1] & 0x80) === 0x80; + + if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16; + else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64; + else return this.haveLength(); + } + + /** + * Gets extended payload length (7+16). + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + getPayloadLength16() { + if (this._bufferedBytes < 2) { + this._loop = false; + return; + } + + this._payloadLength = this.consume(2).readUInt16BE(0); + return this.haveLength(); + } + + /** + * Gets extended payload length (7+64). + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + getPayloadLength64() { + if (this._bufferedBytes < 8) { + this._loop = false; + return; + } + + const buf = this.consume(8); + const num = buf.readUInt32BE(0); + + // + // The maximum safe integer in JavaScript is 2^53 - 1. An error is returned + // if payload length is greater than this number. + // + if (num > Math.pow(2, 53 - 32) - 1) { + this._loop = false; + return error( + RangeError, + 'Unsupported WebSocket frame: payload length > 2^53 - 1', + false, + 1009 + ); + } + + this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4); + return this.haveLength(); + } + + /** + * Payload length has been read. + * + * @return {(RangeError|undefined)} A possible error + * @private + */ + haveLength() { + if (this._payloadLength && this._opcode < 0x08) { + this._totalPayloadLength += this._payloadLength; + if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) { + this._loop = false; + return error(RangeError, 'Max payload size exceeded', false, 1009); + } + } + + if (this._masked) this._state = GET_MASK; + else this._state = GET_DATA; + } + + /** + * Reads mask bytes. + * + * @private + */ + getMask() { + if (this._bufferedBytes < 4) { + this._loop = false; + return; + } + + this._mask = this.consume(4); + this._state = GET_DATA; + } + + /** + * Reads data bytes. + * + * @param {Function} cb Callback + * @return {(Error|RangeError|undefined)} A possible error + * @private + */ + getData(cb) { + var data = EMPTY_BUFFER; + + if (this._payloadLength) { + if (this._bufferedBytes < this._payloadLength) { + this._loop = false; + return; + } + + data = this.consume(this._payloadLength); + if (this._masked) unmask(data, this._mask); + } + + if (this._opcode > 0x07) return this.controlMessage(data); + + if (this._compressed) { + this._state = INFLATING; + this.decompress(data, cb); + return; + } + + if (data.length) { + // + // This message is not compressed so its lenght is the sum of the payload + // length of all fragments. + // + this._messageLength = this._totalPayloadLength; + this._fragments.push(data); + } + + return this.dataMessage(); + } + + /** + * Decompresses data. + * + * @param {Buffer} data Compressed data + * @param {Function} cb Callback + * @private + */ + decompress(data, cb) { + const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; + + perMessageDeflate.decompress(data, this._fin, (err, buf) => { + if (err) return cb(err); + + if (buf.length) { + this._messageLength += buf.length; + if (this._messageLength > this._maxPayload && this._maxPayload > 0) { + return cb( + error(RangeError, 'Max payload size exceeded', false, 1009) + ); + } + + this._fragments.push(buf); + } + + const er = this.dataMessage(); + if (er) return cb(er); + + this.startLoop(cb); + }); + } + + /** + * Handles a data message. + * + * @return {(Error|undefined)} A possible error + * @private + */ + dataMessage() { + if (this._fin) { + const messageLength = this._messageLength; + const fragments = this._fragments; + + this._totalPayloadLength = 0; + this._messageLength = 0; + this._fragmented = 0; + this._fragments = []; + + if (this._opcode === 2) { + var data; + + if (this._binaryType === 'nodebuffer') { + data = concat(fragments, messageLength); + } else if (this._binaryType === 'arraybuffer') { + data = toArrayBuffer(concat(fragments, messageLength)); + } else { + data = fragments; + } + + this.emit('message', data); + } else { + const buf = concat(fragments, messageLength); + + if (!isValidUTF8(buf)) { + this._loop = false; + return error(Error, 'invalid UTF-8 sequence', true, 1007); + } + + this.emit('message', buf.toString()); + } + } + + this._state = GET_INFO; + } + + /** + * Handles a control message. + * + * @param {Buffer} data Data to handle + * @return {(Error|RangeError|undefined)} A possible error + * @private + */ + controlMessage(data) { + if (this._opcode === 0x08) { + this._loop = false; + + if (data.length === 0) { + this.emit('conclude', 1005, ''); + this.end(); + } else if (data.length === 1) { + return error(RangeError, 'invalid payload length 1', true, 1002); + } else { + const code = data.readUInt16BE(0); + + if (!isValidStatusCode(code)) { + return error(RangeError, `invalid status code ${code}`, true, 1002); + } + + const buf = data.slice(2); + + if (!isValidUTF8(buf)) { + return error(Error, 'invalid UTF-8 sequence', true, 1007); + } + + this.emit('conclude', code, buf.toString()); + this.end(); + } + } else if (this._opcode === 0x09) { + this.emit('ping', data); + } else { + this.emit('pong', data); + } + + this._state = GET_INFO; + } +} + +module.exports = Receiver; + +/** + * Builds an error object. + * + * @param {(Error|RangeError)} ErrorCtor The error constructor + * @param {String} message The error message + * @param {Boolean} prefix Specifies whether or not to add a default prefix to + * `message` + * @param {Number} statusCode The status code + * @return {(Error|RangeError)} The error + * @private + */ +function error(ErrorCtor, message, prefix, statusCode) { + const err = new ErrorCtor( + prefix ? `Invalid WebSocket frame: ${message}` : message + ); + + Error.captureStackTrace(err, error); + err[kStatusCode] = statusCode; + return err; +} diff --git a/node_modules/ws/lib/sender.js b/node_modules/ws/lib/sender.js new file mode 100644 index 0000000..8e9e5db --- /dev/null +++ b/node_modules/ws/lib/sender.js @@ -0,0 +1,362 @@ +'use strict'; + +const { randomBytes } = require('crypto'); + +const PerMessageDeflate = require('./permessage-deflate'); +const { EMPTY_BUFFER } = require('./constants'); +const { isValidStatusCode } = require('./validation'); +const { mask: applyMask, toBuffer } = require('./buffer-util'); + +/** + * HyBi Sender implementation. + */ +class Sender { + /** + * Creates a Sender instance. + * + * @param {net.Socket} socket The connection socket + * @param {Object} extensions An object containing the negotiated extensions + */ + constructor(socket, extensions) { + this._extensions = extensions || {}; + this._socket = socket; + + this._firstFragment = true; + this._compress = false; + + this._bufferedBytes = 0; + this._deflating = false; + this._queue = []; + } + + /** + * Frames a piece of data according to the HyBi WebSocket protocol. + * + * @param {Buffer} data The data to frame + * @param {Object} options Options object + * @param {Number} options.opcode The opcode + * @param {Boolean} options.readOnly Specifies whether `data` can be modified + * @param {Boolean} options.fin Specifies whether or not to set the FIN bit + * @param {Boolean} options.mask Specifies whether or not to mask `data` + * @param {Boolean} options.rsv1 Specifies whether or not to set the RSV1 bit + * @return {Buffer[]} The framed data as a list of `Buffer` instances + * @public + */ + static frame(data, options) { + const merge = data.length < 1024 || (options.mask && options.readOnly); + var offset = options.mask ? 6 : 2; + var payloadLength = data.length; + + if (data.length >= 65536) { + offset += 8; + payloadLength = 127; + } else if (data.length > 125) { + offset += 2; + payloadLength = 126; + } + + const target = Buffer.allocUnsafe(merge ? data.length + offset : offset); + + target[0] = options.fin ? options.opcode | 0x80 : options.opcode; + if (options.rsv1) target[0] |= 0x40; + + if (payloadLength === 126) { + target.writeUInt16BE(data.length, 2); + } else if (payloadLength === 127) { + target.writeUInt32BE(0, 2); + target.writeUInt32BE(data.length, 6); + } + + if (!options.mask) { + target[1] = payloadLength; + if (merge) { + data.copy(target, offset); + return [target]; + } + + return [target, data]; + } + + const mask = randomBytes(4); + + target[1] = payloadLength | 0x80; + target[offset - 4] = mask[0]; + target[offset - 3] = mask[1]; + target[offset - 2] = mask[2]; + target[offset - 1] = mask[3]; + + if (merge) { + applyMask(data, mask, target, offset, data.length); + return [target]; + } + + applyMask(data, mask, data, 0, data.length); + return [target, data]; + } + + /** + * Sends a close message to the other peer. + * + * @param {(Number|undefined)} code The status code component of the body + * @param {String} data The message component of the body + * @param {Boolean} mask Specifies whether or not to mask the message + * @param {Function} cb Callback + * @public + */ + close(code, data, mask, cb) { + var buf; + + if (code === undefined) { + buf = EMPTY_BUFFER; + } else if (typeof code !== 'number' || !isValidStatusCode(code)) { + throw new TypeError('First argument must be a valid error code number'); + } else if (data === undefined || data === '') { + buf = Buffer.allocUnsafe(2); + buf.writeUInt16BE(code, 0); + } else { + buf = Buffer.allocUnsafe(2 + Buffer.byteLength(data)); + buf.writeUInt16BE(code, 0); + buf.write(data, 2); + } + + if (this._deflating) { + this.enqueue([this.doClose, buf, mask, cb]); + } else { + this.doClose(buf, mask, cb); + } + } + + /** + * Frames and sends a close message. + * + * @param {Buffer} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Function} cb Callback + * @private + */ + doClose(data, mask, cb) { + this.sendFrame( + Sender.frame(data, { + fin: true, + rsv1: false, + opcode: 0x08, + mask, + readOnly: false + }), + cb + ); + } + + /** + * Sends a ping message to the other peer. + * + * @param {*} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Function} cb Callback + * @public + */ + ping(data, mask, cb) { + const buf = toBuffer(data); + + if (this._deflating) { + this.enqueue([this.doPing, buf, mask, toBuffer.readOnly, cb]); + } else { + this.doPing(buf, mask, toBuffer.readOnly, cb); + } + } + + /** + * Frames and sends a ping message. + * + * @param {*} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Boolean} readOnly Specifies whether `data` can be modified + * @param {Function} cb Callback + * @private + */ + doPing(data, mask, readOnly, cb) { + this.sendFrame( + Sender.frame(data, { + fin: true, + rsv1: false, + opcode: 0x09, + mask, + readOnly + }), + cb + ); + } + + /** + * Sends a pong message to the other peer. + * + * @param {*} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Function} cb Callback + * @public + */ + pong(data, mask, cb) { + const buf = toBuffer(data); + + if (this._deflating) { + this.enqueue([this.doPong, buf, mask, toBuffer.readOnly, cb]); + } else { + this.doPong(buf, mask, toBuffer.readOnly, cb); + } + } + + /** + * Frames and sends a pong message. + * + * @param {*} data The message to send + * @param {Boolean} mask Specifies whether or not to mask `data` + * @param {Boolean} readOnly Specifies whether `data` can be modified + * @param {Function} cb Callback + * @private + */ + doPong(data, mask, readOnly, cb) { + this.sendFrame( + Sender.frame(data, { + fin: true, + rsv1: false, + opcode: 0x0a, + mask, + readOnly + }), + cb + ); + } + + /** + * Sends a data message to the other peer. + * + * @param {*} data The message to send + * @param {Object} options Options object + * @param {Boolean} options.compress Specifies whether or not to compress `data` + * @param {Boolean} options.binary Specifies whether `data` is binary or text + * @param {Boolean} options.fin Specifies whether the fragment is the last one + * @param {Boolean} options.mask Specifies whether or not to mask `data` + * @param {Function} cb Callback + * @public + */ + send(data, options, cb) { + const buf = toBuffer(data); + const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; + var opcode = options.binary ? 2 : 1; + var rsv1 = options.compress; + + if (this._firstFragment) { + this._firstFragment = false; + if (rsv1 && perMessageDeflate) { + rsv1 = buf.length >= perMessageDeflate._threshold; + } + this._compress = rsv1; + } else { + rsv1 = false; + opcode = 0; + } + + if (options.fin) this._firstFragment = true; + + if (perMessageDeflate) { + const opts = { + fin: options.fin, + rsv1, + opcode, + mask: options.mask, + readOnly: toBuffer.readOnly + }; + + if (this._deflating) { + this.enqueue([this.dispatch, buf, this._compress, opts, cb]); + } else { + this.dispatch(buf, this._compress, opts, cb); + } + } else { + this.sendFrame( + Sender.frame(buf, { + fin: options.fin, + rsv1: false, + opcode, + mask: options.mask, + readOnly: toBuffer.readOnly + }), + cb + ); + } + } + + /** + * Dispatches a data message. + * + * @param {Buffer} data The message to send + * @param {Boolean} compress Specifies whether or not to compress `data` + * @param {Object} options Options object + * @param {Number} options.opcode The opcode + * @param {Boolean} options.readOnly Specifies whether `data` can be modified + * @param {Boolean} options.fin Specifies whether or not to set the FIN bit + * @param {Boolean} options.mask Specifies whether or not to mask `data` + * @param {Boolean} options.rsv1 Specifies whether or not to set the RSV1 bit + * @param {Function} cb Callback + * @private + */ + dispatch(data, compress, options, cb) { + if (!compress) { + this.sendFrame(Sender.frame(data, options), cb); + return; + } + + const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; + + this._deflating = true; + perMessageDeflate.compress(data, options.fin, (_, buf) => { + this._deflating = false; + options.readOnly = false; + this.sendFrame(Sender.frame(buf, options), cb); + this.dequeue(); + }); + } + + /** + * Executes queued send operations. + * + * @private + */ + dequeue() { + while (!this._deflating && this._queue.length) { + const params = this._queue.shift(); + + this._bufferedBytes -= params[1].length; + params[0].apply(this, params.slice(1)); + } + } + + /** + * Enqueues a send operation. + * + * @param {Array} params Send operation parameters. + * @private + */ + enqueue(params) { + this._bufferedBytes += params[1].length; + this._queue.push(params); + } + + /** + * Sends a frame. + * + * @param {Buffer[]} list The frame to send + * @param {Function} cb Callback + * @private + */ + sendFrame(list, cb) { + if (list.length === 2) { + this._socket.write(list[0]); + this._socket.write(list[1], cb); + } else { + this._socket.write(list[0], cb); + } + } +} + +module.exports = Sender; diff --git a/node_modules/ws/lib/validation.js b/node_modules/ws/lib/validation.js new file mode 100644 index 0000000..479a7db --- /dev/null +++ b/node_modules/ws/lib/validation.js @@ -0,0 +1,30 @@ +'use strict'; + +try { + const isValidUTF8 = require('utf-8-validate'); + + exports.isValidUTF8 = + typeof isValidUTF8 === 'object' + ? isValidUTF8.Validation.isValidUTF8 // utf-8-validate@<3.0.0 + : isValidUTF8; +} catch (e) /* istanbul ignore next */ { + exports.isValidUTF8 = () => true; +} + +/** + * Checks if a status code is allowed in a close frame. + * + * @param {Number} code The status code + * @return {Boolean} `true` if the status code is valid, else `false` + * @public + */ +exports.isValidStatusCode = (code) => { + return ( + (code >= 1000 && + code <= 1013 && + code !== 1004 && + code !== 1005 && + code !== 1006) || + (code >= 3000 && code <= 4999) + ); +}; diff --git a/node_modules/ws/lib/websocket-server.js b/node_modules/ws/lib/websocket-server.js new file mode 100644 index 0000000..991b509 --- /dev/null +++ b/node_modules/ws/lib/websocket-server.js @@ -0,0 +1,397 @@ +'use strict'; + +const EventEmitter = require('events'); +const crypto = require('crypto'); +const http = require('http'); + +const PerMessageDeflate = require('./permessage-deflate'); +const extension = require('./extension'); +const WebSocket = require('./websocket'); +const { GUID } = require('./constants'); + +const keyRegex = /^[+/0-9A-Za-z]{22}==$/; + +/** + * Class representing a WebSocket server. + * + * @extends EventEmitter + */ +class WebSocketServer extends EventEmitter { + /** + * Create a `WebSocketServer` instance. + * + * @param {Object} options Configuration options + * @param {String} options.host The hostname where to bind the server + * @param {Number} options.port The port where to bind the server + * @param {http.Server} options.server A pre-created HTTP/S server to use + * @param {Function} options.verifyClient An hook to reject connections + * @param {Function} options.handleProtocols An hook to handle protocols + * @param {String} options.path Accept only connections matching this path + * @param {Boolean} options.noServer Enable no server mode + * @param {Boolean} options.clientTracking Specifies whether or not to track clients + * @param {(Boolean|Object)} options.perMessageDeflate Enable/disable permessage-deflate + * @param {Number} options.maxPayload The maximum allowed message size + * @param {Function} callback A listener for the `listening` event + */ + constructor(options, callback) { + super(); + + options = Object.assign( + { + maxPayload: 100 * 1024 * 1024, + perMessageDeflate: false, + handleProtocols: null, + clientTracking: true, + verifyClient: null, + noServer: false, + backlog: null, // use default (511 as implemented in net.js) + server: null, + host: null, + path: null, + port: null + }, + options + ); + + if (options.port == null && !options.server && !options.noServer) { + throw new TypeError( + 'One of the "port", "server", or "noServer" options must be specified' + ); + } + + if (options.port != null) { + this._server = http.createServer((req, res) => { + const body = http.STATUS_CODES[426]; + + res.writeHead(426, { + 'Content-Length': body.length, + 'Content-Type': 'text/plain' + }); + res.end(body); + }); + this._server.listen( + options.port, + options.host, + options.backlog, + callback + ); + } else if (options.server) { + this._server = options.server; + } + + if (this._server) { + this._removeListeners = addListeners(this._server, { + listening: this.emit.bind(this, 'listening'), + error: this.emit.bind(this, 'error'), + upgrade: (req, socket, head) => { + this.handleUpgrade(req, socket, head, (ws) => { + this.emit('connection', ws, req); + }); + } + }); + } + + if (options.perMessageDeflate === true) options.perMessageDeflate = {}; + if (options.clientTracking) this.clients = new Set(); + this.options = options; + } + + /** + * Returns the bound address, the address family name, and port of the server + * as reported by the operating system if listening on an IP socket. + * If the server is listening on a pipe or UNIX domain socket, the name is + * returned as a string. + * + * @return {(Object|String|null)} The address of the server + * @public + */ + address() { + if (this.options.noServer) { + throw new Error('The server is operating in "noServer" mode'); + } + + if (!this._server) return null; + return this._server.address(); + } + + /** + * Close the server. + * + * @param {Function} cb Callback + * @public + */ + close(cb) { + if (cb) this.once('close', cb); + + // + // Terminate all associated clients. + // + if (this.clients) { + for (const client of this.clients) client.terminate(); + } + + const server = this._server; + + if (server) { + this._removeListeners(); + this._removeListeners = this._server = null; + + // + // Close the http server if it was internally created. + // + if (this.options.port != null) { + server.close(() => this.emit('close')); + return; + } + } + + process.nextTick(emitClose, this); + } + + /** + * See if a given request should be handled by this server instance. + * + * @param {http.IncomingMessage} req Request object to inspect + * @return {Boolean} `true` if the request is valid, else `false` + * @public + */ + shouldHandle(req) { + if (this.options.path) { + const index = req.url.indexOf('?'); + const pathname = index !== -1 ? req.url.slice(0, index) : req.url; + + if (pathname !== this.options.path) return false; + } + + return true; + } + + /** + * Handle a HTTP Upgrade request. + * + * @param {http.IncomingMessage} req The request object + * @param {net.Socket} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Function} cb Callback + * @public + */ + handleUpgrade(req, socket, head, cb) { + socket.on('error', socketOnError); + + const key = + req.headers['sec-websocket-key'] !== undefined + ? req.headers['sec-websocket-key'].trim() + : false; + const version = +req.headers['sec-websocket-version']; + const extensions = {}; + + if ( + req.method !== 'GET' || + req.headers.upgrade.toLowerCase() !== 'websocket' || + !key || + !keyRegex.test(key) || + (version !== 8 && version !== 13) || + !this.shouldHandle(req) + ) { + return abortHandshake(socket, 400); + } + + if (this.options.perMessageDeflate) { + const perMessageDeflate = new PerMessageDeflate( + this.options.perMessageDeflate, + true, + this.options.maxPayload + ); + + try { + const offers = extension.parse(req.headers['sec-websocket-extensions']); + + if (offers[PerMessageDeflate.extensionName]) { + perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]); + extensions[PerMessageDeflate.extensionName] = perMessageDeflate; + } + } catch (err) { + return abortHandshake(socket, 400); + } + } + + // + // Optionally call external client verification handler. + // + if (this.options.verifyClient) { + const info = { + origin: + req.headers[`${version === 8 ? 'sec-websocket-origin' : 'origin'}`], + secure: !!(req.connection.authorized || req.connection.encrypted), + req + }; + + if (this.options.verifyClient.length === 2) { + this.options.verifyClient(info, (verified, code, message, headers) => { + if (!verified) { + return abortHandshake(socket, code || 401, message, headers); + } + + this.completeUpgrade(key, extensions, req, socket, head, cb); + }); + return; + } + + if (!this.options.verifyClient(info)) return abortHandshake(socket, 401); + } + + this.completeUpgrade(key, extensions, req, socket, head, cb); + } + + /** + * Upgrade the connection to WebSocket. + * + * @param {String} key The value of the `Sec-WebSocket-Key` header + * @param {Object} extensions The accepted extensions + * @param {http.IncomingMessage} req The request object + * @param {net.Socket} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Function} cb Callback + * @private + */ + completeUpgrade(key, extensions, req, socket, head, cb) { + // + // Destroy the socket if the client has already sent a FIN packet. + // + if (!socket.readable || !socket.writable) return socket.destroy(); + + const digest = crypto + .createHash('sha1') + .update(key + GUID) + .digest('base64'); + + const headers = [ + 'HTTP/1.1 101 Switching Protocols', + 'Upgrade: websocket', + 'Connection: Upgrade', + `Sec-WebSocket-Accept: ${digest}` + ]; + + const ws = new WebSocket(null); + var protocol = req.headers['sec-websocket-protocol']; + + if (protocol) { + protocol = protocol.trim().split(/ *, */); + + // + // Optionally call external protocol selection handler. + // + if (this.options.handleProtocols) { + protocol = this.options.handleProtocols(protocol, req); + } else { + protocol = protocol[0]; + } + + if (protocol) { + headers.push(`Sec-WebSocket-Protocol: ${protocol}`); + ws.protocol = protocol; + } + } + + if (extensions[PerMessageDeflate.extensionName]) { + const params = extensions[PerMessageDeflate.extensionName].params; + const value = extension.format({ + [PerMessageDeflate.extensionName]: [params] + }); + headers.push(`Sec-WebSocket-Extensions: ${value}`); + ws._extensions = extensions; + } + + // + // Allow external modification/inspection of handshake headers. + // + this.emit('headers', headers, req); + + socket.write(headers.concat('\r\n').join('\r\n')); + socket.removeListener('error', socketOnError); + + ws.setSocket(socket, head, this.options.maxPayload); + + if (this.clients) { + this.clients.add(ws); + ws.on('close', () => this.clients.delete(ws)); + } + + cb(ws); + } +} + +module.exports = WebSocketServer; + +/** + * Add event listeners on an `EventEmitter` using a map of + * pairs. + * + * @param {EventEmitter} server The event emitter + * @param {Object.} map The listeners to add + * @return {Function} A function that will remove the added listeners when called + * @private + */ +function addListeners(server, map) { + for (const event of Object.keys(map)) server.on(event, map[event]); + + return function removeListeners() { + for (const event of Object.keys(map)) { + server.removeListener(event, map[event]); + } + }; +} + +/** + * Emit a `'close'` event on an `EventEmitter`. + * + * @param {EventEmitter} server The event emitter + * @private + */ +function emitClose(server) { + server.emit('close'); +} + +/** + * Handle premature socket errors. + * + * @private + */ +function socketOnError() { + this.destroy(); +} + +/** + * Close the connection when preconditions are not fulfilled. + * + * @param {net.Socket} socket The socket of the upgrade request + * @param {Number} code The HTTP response status code + * @param {String} [message] The HTTP response body + * @param {Object} [headers] Additional HTTP response headers + * @private + */ +function abortHandshake(socket, code, message, headers) { + if (socket.writable) { + message = message || http.STATUS_CODES[code]; + headers = Object.assign( + { + Connection: 'close', + 'Content-type': 'text/html', + 'Content-Length': Buffer.byteLength(message) + }, + headers + ); + + socket.write( + `HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` + + Object.keys(headers) + .map((h) => `${h}: ${headers[h]}`) + .join('\r\n') + + '\r\n\r\n' + + message + ); + } + + socket.removeListener('error', socketOnError); + socket.destroy(); +} diff --git a/node_modules/ws/lib/websocket.js b/node_modules/ws/lib/websocket.js new file mode 100644 index 0000000..0dd5fb2 --- /dev/null +++ b/node_modules/ws/lib/websocket.js @@ -0,0 +1,900 @@ +'use strict'; + +const EventEmitter = require('events'); +const crypto = require('crypto'); +const https = require('https'); +const http = require('http'); +const net = require('net'); +const tls = require('tls'); +const url = require('url'); + +const PerMessageDeflate = require('./permessage-deflate'); +const EventTarget = require('./event-target'); +const extension = require('./extension'); +const Receiver = require('./receiver'); +const Sender = require('./sender'); +const { + BINARY_TYPES, + EMPTY_BUFFER, + GUID, + kStatusCode, + kWebSocket, + NOOP +} = require('./constants'); + +const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED']; +const protocolVersions = [8, 13]; +const closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly. + +/** + * Class representing a WebSocket. + * + * @extends EventEmitter + */ +class WebSocket extends EventEmitter { + /** + * Create a new `WebSocket`. + * + * @param {(String|url.Url|url.URL)} address The URL to which to connect + * @param {(String|String[])} protocols The subprotocols + * @param {Object} options Connection options + */ + constructor(address, protocols, options) { + super(); + + this.readyState = WebSocket.CONNECTING; + this.protocol = ''; + + this._binaryType = BINARY_TYPES[0]; + this._closeFrameReceived = false; + this._closeFrameSent = false; + this._closeMessage = ''; + this._closeTimer = null; + this._closeCode = 1006; + this._extensions = {}; + this._receiver = null; + this._sender = null; + this._socket = null; + + if (address !== null) { + this._isServer = false; + this._redirects = 0; + + if (Array.isArray(protocols)) { + protocols = protocols.join(', '); + } else if (typeof protocols === 'object' && protocols !== null) { + options = protocols; + protocols = undefined; + } + + initAsClient(this, address, protocols, options); + } else { + this._isServer = true; + } + } + + get CONNECTING() { + return WebSocket.CONNECTING; + } + get CLOSING() { + return WebSocket.CLOSING; + } + get CLOSED() { + return WebSocket.CLOSED; + } + get OPEN() { + return WebSocket.OPEN; + } + + /** + * This deviates from the WHATWG interface since ws doesn't support the required + * default "blob" type (instead we define a custom "nodebuffer" type). + * + * @type {String} + */ + get binaryType() { + return this._binaryType; + } + + set binaryType(type) { + if (!BINARY_TYPES.includes(type)) return; + + this._binaryType = type; + + // + // Allow to change `binaryType` on the fly. + // + if (this._receiver) this._receiver._binaryType = type; + } + + /** + * @type {Number} + */ + get bufferedAmount() { + if (!this._socket) return 0; + + // + // `socket.bufferSize` is `undefined` if the socket is closed. + // + return (this._socket.bufferSize || 0) + this._sender._bufferedBytes; + } + + /** + * @type {String} + */ + get extensions() { + return Object.keys(this._extensions).join(); + } + + /** + * Set up the socket and the internal resources. + * + * @param {net.Socket} socket The network socket between the server and client + * @param {Buffer} head The first packet of the upgraded stream + * @param {Number} maxPayload The maximum allowed message size + * @private + */ + setSocket(socket, head, maxPayload) { + const receiver = new Receiver( + this._binaryType, + this._extensions, + maxPayload + ); + + this._sender = new Sender(socket, this._extensions); + this._receiver = receiver; + this._socket = socket; + + receiver[kWebSocket] = this; + socket[kWebSocket] = this; + + receiver.on('conclude', receiverOnConclude); + receiver.on('drain', receiverOnDrain); + receiver.on('error', receiverOnError); + receiver.on('message', receiverOnMessage); + receiver.on('ping', receiverOnPing); + receiver.on('pong', receiverOnPong); + + socket.setTimeout(0); + socket.setNoDelay(); + + if (head.length > 0) socket.unshift(head); + + socket.on('close', socketOnClose); + socket.on('data', socketOnData); + socket.on('end', socketOnEnd); + socket.on('error', socketOnError); + + this.readyState = WebSocket.OPEN; + this.emit('open'); + } + + /** + * Emit the `'close'` event. + * + * @private + */ + emitClose() { + this.readyState = WebSocket.CLOSED; + + if (!this._socket) { + this.emit('close', this._closeCode, this._closeMessage); + return; + } + + if (this._extensions[PerMessageDeflate.extensionName]) { + this._extensions[PerMessageDeflate.extensionName].cleanup(); + } + + this._receiver.removeAllListeners(); + this.emit('close', this._closeCode, this._closeMessage); + } + + /** + * Start a closing handshake. + * + * +----------+ +-----------+ +----------+ + * - - -|ws.close()|-->|close frame|-->|ws.close()|- - - + * | +----------+ +-----------+ +----------+ | + * +----------+ +-----------+ | + * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING + * +----------+ +-----------+ | + * | | | +---+ | + * +------------------------+-->|fin| - - - - + * | +---+ | +---+ + * - - - - -|fin|<---------------------+ + * +---+ + * + * @param {Number} code Status code explaining why the connection is closing + * @param {String} data A string explaining why the connection is closing + * @public + */ + close(code, data) { + if (this.readyState === WebSocket.CLOSED) return; + if (this.readyState === WebSocket.CONNECTING) { + const msg = 'WebSocket was closed before the connection was established'; + return abortHandshake(this, this._req, msg); + } + + if (this.readyState === WebSocket.CLOSING) { + if (this._closeFrameSent && this._closeFrameReceived) this._socket.end(); + return; + } + + this.readyState = WebSocket.CLOSING; + this._sender.close(code, data, !this._isServer, (err) => { + // + // This error is handled by the `'error'` listener on the socket. We only + // want to know if the close frame has been sent here. + // + if (err) return; + + this._closeFrameSent = true; + + if (this._socket.writable) { + if (this._closeFrameReceived) this._socket.end(); + + // + // Ensure that the connection is closed even if the closing handshake + // fails. + // + this._closeTimer = setTimeout( + this._socket.destroy.bind(this._socket), + closeTimeout + ); + } + }); + } + + /** + * Send a ping. + * + * @param {*} data The data to send + * @param {Boolean} mask Indicates whether or not to mask `data` + * @param {Function} cb Callback which is executed when the ping is sent + * @public + */ + ping(data, mask, cb) { + if (typeof data === 'function') { + cb = data; + data = mask = undefined; + } else if (typeof mask === 'function') { + cb = mask; + mask = undefined; + } + + if (this.readyState !== WebSocket.OPEN) { + const err = new Error( + `WebSocket is not open: readyState ${this.readyState} ` + + `(${readyStates[this.readyState]})` + ); + + if (cb) return cb(err); + throw err; + } + + if (typeof data === 'number') data = data.toString(); + if (mask === undefined) mask = !this._isServer; + this._sender.ping(data || EMPTY_BUFFER, mask, cb); + } + + /** + * Send a pong. + * + * @param {*} data The data to send + * @param {Boolean} mask Indicates whether or not to mask `data` + * @param {Function} cb Callback which is executed when the pong is sent + * @public + */ + pong(data, mask, cb) { + if (typeof data === 'function') { + cb = data; + data = mask = undefined; + } else if (typeof mask === 'function') { + cb = mask; + mask = undefined; + } + + if (this.readyState !== WebSocket.OPEN) { + const err = new Error( + `WebSocket is not open: readyState ${this.readyState} ` + + `(${readyStates[this.readyState]})` + ); + + if (cb) return cb(err); + throw err; + } + + if (typeof data === 'number') data = data.toString(); + if (mask === undefined) mask = !this._isServer; + this._sender.pong(data || EMPTY_BUFFER, mask, cb); + } + + /** + * Send a data message. + * + * @param {*} data The message to send + * @param {Object} options Options object + * @param {Boolean} options.compress Specifies whether or not to compress `data` + * @param {Boolean} options.binary Specifies whether `data` is binary or text + * @param {Boolean} options.fin Specifies whether the fragment is the last one + * @param {Boolean} options.mask Specifies whether or not to mask `data` + * @param {Function} cb Callback which is executed when data is written out + * @public + */ + send(data, options, cb) { + if (typeof options === 'function') { + cb = options; + options = {}; + } + + if (this.readyState !== WebSocket.OPEN) { + const err = new Error( + `WebSocket is not open: readyState ${this.readyState} ` + + `(${readyStates[this.readyState]})` + ); + + if (cb) return cb(err); + throw err; + } + + if (typeof data === 'number') data = data.toString(); + + const opts = Object.assign( + { + binary: typeof data !== 'string', + mask: !this._isServer, + compress: true, + fin: true + }, + options + ); + + if (!this._extensions[PerMessageDeflate.extensionName]) { + opts.compress = false; + } + + this._sender.send(data || EMPTY_BUFFER, opts, cb); + } + + /** + * Forcibly close the connection. + * + * @public + */ + terminate() { + if (this.readyState === WebSocket.CLOSED) return; + if (this.readyState === WebSocket.CONNECTING) { + const msg = 'WebSocket was closed before the connection was established'; + return abortHandshake(this, this._req, msg); + } + + if (this._socket) { + this.readyState = WebSocket.CLOSING; + this._socket.destroy(); + } + } +} + +readyStates.forEach((readyState, i) => { + WebSocket[readyState] = i; +}); + +// +// Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes. +// See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface +// +['open', 'error', 'close', 'message'].forEach((method) => { + Object.defineProperty(WebSocket.prototype, `on${method}`, { + /** + * Return the listener of the event. + * + * @return {(Function|undefined)} The event listener or `undefined` + * @public + */ + get() { + const listeners = this.listeners(method); + for (var i = 0; i < listeners.length; i++) { + if (listeners[i]._listener) return listeners[i]._listener; + } + + return undefined; + }, + /** + * Add a listener for the event. + * + * @param {Function} listener The listener to add + * @public + */ + set(listener) { + const listeners = this.listeners(method); + for (var i = 0; i < listeners.length; i++) { + // + // Remove only the listeners added via `addEventListener`. + // + if (listeners[i]._listener) this.removeListener(method, listeners[i]); + } + this.addEventListener(method, listener); + } + }); +}); + +WebSocket.prototype.addEventListener = EventTarget.addEventListener; +WebSocket.prototype.removeEventListener = EventTarget.removeEventListener; + +module.exports = WebSocket; + +/** + * Initialize a WebSocket client. + * + * @param {WebSocket} websocket The client to initialize + * @param {(String|url.Url|url.URL)} address The URL to which to connect + * @param {String} protocols The subprotocols + * @param {Object} options Connection options + * @param {(Boolean|Object)} options.perMessageDeflate Enable/disable + * permessage-deflate + * @param {Number} options.handshakeTimeout Timeout in milliseconds for the + * handshake request + * @param {Number} options.protocolVersion Value of the `Sec-WebSocket-Version` + * header + * @param {String} options.origin Value of the `Origin` or + * `Sec-WebSocket-Origin` header + * @param {Number} options.maxPayload The maximum allowed message size + * @param {Boolean} options.followRedirects Whether or not to follow redirects + * @param {Number} options.maxRedirects The maximum number of redirects allowed + * @private + */ +function initAsClient(websocket, address, protocols, options) { + const opts = Object.assign( + { + protocolVersion: protocolVersions[1], + maxPayload: 100 * 1024 * 1024, + perMessageDeflate: true, + followRedirects: false, + maxRedirects: 10 + }, + options, + { + createConnection: undefined, + socketPath: undefined, + hostname: undefined, + protocol: undefined, + timeout: undefined, + method: undefined, + auth: undefined, + host: undefined, + path: undefined, + port: undefined + } + ); + + if (!protocolVersions.includes(opts.protocolVersion)) { + throw new RangeError( + `Unsupported protocol version: ${opts.protocolVersion} ` + + `(supported versions: ${protocolVersions.join(', ')})` + ); + } + + var parsedUrl; + + if (typeof address === 'object' && address.href !== undefined) { + parsedUrl = address; + websocket.url = address.href; + } else { + // + // The WHATWG URL constructor is not available on Node.js < 6.13.0 + // + parsedUrl = url.URL ? new url.URL(address) : url.parse(address); + websocket.url = address; + } + + const isUnixSocket = parsedUrl.protocol === 'ws+unix:'; + + if (!parsedUrl.host && (!isUnixSocket || !parsedUrl.pathname)) { + throw new Error(`Invalid URL: ${websocket.url}`); + } + + const isSecure = + parsedUrl.protocol === 'wss:' || parsedUrl.protocol === 'https:'; + const defaultPort = isSecure ? 443 : 80; + const key = crypto.randomBytes(16).toString('base64'); + const get = isSecure ? https.get : http.get; + const path = parsedUrl.search + ? `${parsedUrl.pathname || '/'}${parsedUrl.search}` + : parsedUrl.pathname || '/'; + var perMessageDeflate; + + opts.createConnection = isSecure ? tlsConnect : netConnect; + opts.defaultPort = opts.defaultPort || defaultPort; + opts.port = parsedUrl.port || defaultPort; + opts.host = parsedUrl.hostname.startsWith('[') + ? parsedUrl.hostname.slice(1, -1) + : parsedUrl.hostname; + opts.headers = Object.assign( + { + 'Sec-WebSocket-Version': opts.protocolVersion, + 'Sec-WebSocket-Key': key, + Connection: 'Upgrade', + Upgrade: 'websocket' + }, + opts.headers + ); + opts.path = path; + opts.timeout = opts.handshakeTimeout; + + if (opts.perMessageDeflate) { + perMessageDeflate = new PerMessageDeflate( + opts.perMessageDeflate !== true ? opts.perMessageDeflate : {}, + false, + opts.maxPayload + ); + opts.headers['Sec-WebSocket-Extensions'] = extension.format({ + [PerMessageDeflate.extensionName]: perMessageDeflate.offer() + }); + } + if (protocols) { + opts.headers['Sec-WebSocket-Protocol'] = protocols; + } + if (opts.origin) { + if (opts.protocolVersion < 13) { + opts.headers['Sec-WebSocket-Origin'] = opts.origin; + } else { + opts.headers.Origin = opts.origin; + } + } + if (parsedUrl.auth) { + opts.auth = parsedUrl.auth; + } else if (parsedUrl.username || parsedUrl.password) { + opts.auth = `${parsedUrl.username}:${parsedUrl.password}`; + } + + if (isUnixSocket) { + const parts = path.split(':'); + + opts.socketPath = parts[0]; + opts.path = parts[1]; + } + + var req = (websocket._req = get(opts)); + + if (opts.timeout) { + req.on('timeout', () => { + abortHandshake(websocket, req, 'Opening handshake has timed out'); + }); + } + + req.on('error', (err) => { + if (websocket._req.aborted) return; + + req = websocket._req = null; + websocket.readyState = WebSocket.CLOSING; + websocket.emit('error', err); + websocket.emitClose(); + }); + + req.on('response', (res) => { + const location = res.headers.location; + const statusCode = res.statusCode; + + if ( + location && + opts.followRedirects && + statusCode >= 300 && + statusCode < 400 + ) { + if (++websocket._redirects > opts.maxRedirects) { + abortHandshake(websocket, req, 'Maximum redirects exceeded'); + return; + } + + req.abort(); + + const addr = url.URL + ? new url.URL(location, address) + : url.resolve(address, location); + + initAsClient(websocket, addr, protocols, options); + } else if (!websocket.emit('unexpected-response', req, res)) { + abortHandshake( + websocket, + req, + `Unexpected server response: ${res.statusCode}` + ); + } + }); + + req.on('upgrade', (res, socket, head) => { + websocket.emit('upgrade', res); + + // + // The user may have closed the connection from a listener of the `upgrade` + // event. + // + if (websocket.readyState !== WebSocket.CONNECTING) return; + + req = websocket._req = null; + + const digest = crypto + .createHash('sha1') + .update(key + GUID) + .digest('base64'); + + if (res.headers['sec-websocket-accept'] !== digest) { + abortHandshake(websocket, socket, 'Invalid Sec-WebSocket-Accept header'); + return; + } + + const serverProt = res.headers['sec-websocket-protocol']; + const protList = (protocols || '').split(/, */); + var protError; + + if (!protocols && serverProt) { + protError = 'Server sent a subprotocol but none was requested'; + } else if (protocols && !serverProt) { + protError = 'Server sent no subprotocol'; + } else if (serverProt && !protList.includes(serverProt)) { + protError = 'Server sent an invalid subprotocol'; + } + + if (protError) { + abortHandshake(websocket, socket, protError); + return; + } + + if (serverProt) websocket.protocol = serverProt; + + if (perMessageDeflate) { + try { + const extensions = extension.parse( + res.headers['sec-websocket-extensions'] + ); + + if (extensions[PerMessageDeflate.extensionName]) { + perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]); + websocket._extensions[ + PerMessageDeflate.extensionName + ] = perMessageDeflate; + } + } catch (err) { + abortHandshake( + websocket, + socket, + 'Invalid Sec-WebSocket-Extensions header' + ); + return; + } + } + + websocket.setSocket(socket, head, opts.maxPayload); + }); +} + +/** + * Create a `net.Socket` and initiate a connection. + * + * @param {Object} options Connection options + * @return {net.Socket} The newly created socket used to start the connection + * @private + */ +function netConnect(options) { + // + // Override `options.path` only if `options` is a copy of the original options + // object. This is always true on Node.js >= 8 but not on Node.js 6 where + // `options.socketPath` might be `undefined` even if the `socketPath` option + // was originally set. + // + if (options.protocolVersion) options.path = options.socketPath; + return net.connect(options); +} + +/** + * Create a `tls.TLSSocket` and initiate a connection. + * + * @param {Object} options Connection options + * @return {tls.TLSSocket} The newly created socket used to start the connection + * @private + */ +function tlsConnect(options) { + options.path = undefined; + options.servername = options.servername || options.host; + return tls.connect(options); +} + +/** + * Abort the handshake and emit an error. + * + * @param {WebSocket} websocket The WebSocket instance + * @param {(http.ClientRequest|net.Socket)} stream The request to abort or the + * socket to destroy + * @param {String} message The error message + * @private + */ +function abortHandshake(websocket, stream, message) { + websocket.readyState = WebSocket.CLOSING; + + const err = new Error(message); + Error.captureStackTrace(err, abortHandshake); + + if (stream.setHeader) { + stream.abort(); + stream.once('abort', websocket.emitClose.bind(websocket)); + websocket.emit('error', err); + } else { + stream.destroy(err); + stream.once('error', websocket.emit.bind(websocket, 'error')); + stream.once('close', websocket.emitClose.bind(websocket)); + } +} + +/** + * The listener of the `Receiver` `'conclude'` event. + * + * @param {Number} code The status code + * @param {String} reason The reason for closing + * @private + */ +function receiverOnConclude(code, reason) { + const websocket = this[kWebSocket]; + + websocket._socket.removeListener('data', socketOnData); + websocket._socket.resume(); + + websocket._closeFrameReceived = true; + websocket._closeMessage = reason; + websocket._closeCode = code; + + if (code === 1005) websocket.close(); + else websocket.close(code, reason); +} + +/** + * The listener of the `Receiver` `'drain'` event. + * + * @private + */ +function receiverOnDrain() { + this[kWebSocket]._socket.resume(); +} + +/** + * The listener of the `Receiver` `'error'` event. + * + * @param {(RangeError|Error)} err The emitted error + * @private + */ +function receiverOnError(err) { + const websocket = this[kWebSocket]; + + websocket._socket.removeListener('data', socketOnData); + + websocket.readyState = WebSocket.CLOSING; + websocket._closeCode = err[kStatusCode]; + websocket.emit('error', err); + websocket._socket.destroy(); +} + +/** + * The listener of the `Receiver` `'finish'` event. + * + * @private + */ +function receiverOnFinish() { + this[kWebSocket].emitClose(); +} + +/** + * The listener of the `Receiver` `'message'` event. + * + * @param {(String|Buffer|ArrayBuffer|Buffer[])} data The message + * @private + */ +function receiverOnMessage(data) { + this[kWebSocket].emit('message', data); +} + +/** + * The listener of the `Receiver` `'ping'` event. + * + * @param {Buffer} data The data included in the ping frame + * @private + */ +function receiverOnPing(data) { + const websocket = this[kWebSocket]; + + websocket.pong(data, !websocket._isServer, NOOP); + websocket.emit('ping', data); +} + +/** + * The listener of the `Receiver` `'pong'` event. + * + * @param {Buffer} data The data included in the pong frame + * @private + */ +function receiverOnPong(data) { + this[kWebSocket].emit('pong', data); +} + +/** + * The listener of the `net.Socket` `'close'` event. + * + * @private + */ +function socketOnClose() { + const websocket = this[kWebSocket]; + + this.removeListener('close', socketOnClose); + this.removeListener('end', socketOnEnd); + + websocket.readyState = WebSocket.CLOSING; + + // + // The close frame might not have been received or the `'end'` event emitted, + // for example, if the socket was destroyed due to an error. Ensure that the + // `receiver` stream is closed after writing any remaining buffered data to + // it. If the readable side of the socket is in flowing mode then there is no + // buffered data as everything has been already written and `readable.read()` + // will return `null`. If instead, the socket is paused, any possible buffered + // data will be read as a single chunk and emitted synchronously in a single + // `'data'` event. + // + websocket._socket.read(); + websocket._receiver.end(); + + this.removeListener('data', socketOnData); + this[kWebSocket] = undefined; + + clearTimeout(websocket._closeTimer); + + if ( + websocket._receiver._writableState.finished || + websocket._receiver._writableState.errorEmitted + ) { + websocket.emitClose(); + } else { + websocket._receiver.on('error', receiverOnFinish); + websocket._receiver.on('finish', receiverOnFinish); + } +} + +/** + * The listener of the `net.Socket` `'data'` event. + * + * @param {Buffer} chunk A chunk of data + * @private + */ +function socketOnData(chunk) { + if (!this[kWebSocket]._receiver.write(chunk)) { + this.pause(); + } +} + +/** + * The listener of the `net.Socket` `'end'` event. + * + * @private + */ +function socketOnEnd() { + const websocket = this[kWebSocket]; + + websocket.readyState = WebSocket.CLOSING; + websocket._receiver.end(); + this.end(); +} + +/** + * The listener of the `net.Socket` `'error'` event. + * + * @private + */ +function socketOnError() { + const websocket = this[kWebSocket]; + + this.removeListener('error', socketOnError); + this.on('error', NOOP); + + if (websocket) { + websocket.readyState = WebSocket.CLOSING; + this.destroy(); + } +} diff --git a/node_modules/ws/package.json b/node_modules/ws/package.json new file mode 100644 index 0000000..d2f0cdd --- /dev/null +++ b/node_modules/ws/package.json @@ -0,0 +1,80 @@ +{ + "_from": "ws@^6.2.0", + "_id": "ws@6.2.0", + "_inBundle": false, + "_integrity": "sha512-deZYUNlt2O4buFCa3t5bKLf8A7FPP/TVjwOeVNpw818Ma5nk4MLXls2eoEGS39o8119QIYxTrTDoPQ5B/gTD6w==", + "_location": "/ws", + "_phantomChildren": {}, + "_requested": { + "type": "range", + "registry": true, + "raw": "ws@^6.2.0", + "name": "ws", + "escapedName": "ws", + "rawSpec": "^6.2.0", + "saveSpec": null, + "fetchSpec": "^6.2.0" + }, + "_requiredBy": [ + "#USER", + "/" + ], + "_resolved": "https://registry.npmjs.org/ws/-/ws-6.2.0.tgz", + "_shasum": "13806d9913b2a5f3cbb9ba47b563c002cbc7c526", + "_spec": "ws@^6.2.0", + "_where": "C:\\www\\esiur-js", + "author": { + "name": "Einar Otto Stangvik", + "email": "einaros@gmail.com", + "url": "http://2x.io" + }, + "browser": "browser.js", + "bugs": { + "url": "https://github.com/websockets/ws/issues" + }, + "bundleDependencies": false, + "dependencies": { + "async-limiter": "~1.0.0" + }, + "deprecated": false, + "description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js", + "devDependencies": { + "benchmark": "~2.1.4", + "bufferutil": "~4.0.0", + "coveralls": "~3.0.3", + "eslint": "~5.15.0", + "eslint-config-prettier": "~4.1.0", + "eslint-plugin-prettier": "~3.0.0", + "mocha": "~6.0.0", + "nyc": "~13.3.0", + "prettier": "~1.16.1", + "utf-8-validate": "~5.0.0" + }, + "files": [ + "browser.js", + "index.js", + "lib/*.js" + ], + "homepage": "https://github.com/websockets/ws", + "keywords": [ + "HyBi", + "Push", + "RFC-6455", + "WebSocket", + "WebSockets", + "real-time" + ], + "license": "MIT", + "main": "index.js", + "name": "ws", + "repository": { + "type": "git", + "url": "git+https://github.com/websockets/ws.git" + }, + "scripts": { + "integration": "npm run lint && mocha test/*.integration.js", + "lint": "eslint --ignore-path .gitignore . && prettier --check --ignore-path .gitignore \"**/*.{json,md,yml}\"", + "test": "npm run lint && nyc --reporter=html --reporter=text mocha test/*.test.js" + }, + "version": "6.2.0" +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..9272a2a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,21 @@ +{ + "name": "esiur", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, + "ws": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.0.tgz", + "integrity": "sha512-deZYUNlt2O4buFCa3t5bKLf8A7FPP/TVjwOeVNpw818Ma5nk4MLXls2eoEGS39o8119QIYxTrTDoPQ5B/gTD6w==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d3afdc1 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "esiur", + "version": "1.0.0", + "description": "Distributed Object Framework", + "main": "build/esiur-debug-node.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/esiur/esiur-js.git" + }, + "author": "Ahmed Zamil", + "license": "MIT", + "bugs": { + "url": "https://github.com/esiur/esiur-js/issues" + }, + "homepage": "https://github.com/esiur/esiur-js#readme", + "dependencies": { + "ws": "^6.2.0" + } +} diff --git a/src/AsyncBag.js b/src/AsyncBag.js index 3df9d38..469634b 100644 --- a/src/AsyncBag.js +++ b/src/AsyncBag.js @@ -23,6 +23,8 @@ * Created by Ahmed Zamil on 25/07/2017. */ +"use strict"; + class AsyncBag extends AsyncReply { constructor() { @@ -42,13 +44,27 @@ class AsyncBag extends AsyncReply var self = this; + var singleTaskCompleted = function(taskIndex) + { + return function(results, reply){ + self.results[taskIndex] = results; + self.count++; + if (self.count == self.results.length) + self.trigger(self.results); + }; + }; + for(var i = 0; i < this.results.length; i++) + this.replies[i].then(singleTaskCompleted(i)); + + /* this.replies[i].then(function(r, reply){ self.results[self.replies.indexOf(reply)] = r; self.count++; if (self.count == self.results.length) self.trigger(self.results); }); + */ } add(reply) diff --git a/src/AsyncException.js b/src/AsyncException.js new file mode 100644 index 0000000..ce5f81d --- /dev/null +++ b/src/AsyncException.js @@ -0,0 +1,88 @@ +/* +* 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. +*/ +/** + * Created by Ahmed Zamil on 18/11/2017. + */ +"use strict"; + +const ExceptionCode = +{ + HostNotReachable: 0, + AccessDenied: 1, + ResourceNotFound: 2, + AttachDenied: 3, + InvalidMethod: 4, + InvokeDenied: 5, + CreateDenied: 6, + AddParentDenied: 7, + AddChildDenied: 8, + ViewAttributeDenied: 9, + UpdateAttributeDenied: 10, + StoreNotFound: 11, + ParentNotFound: 12, + ChildNotFound: 13, + ResourceIsNotStore: 14, + DeleteDenied: 15, + DeleteFailed: 16, + UpdateAttributeFailed: 17, + GetAttributesFailed: 18, + ClearAttributesFailed: 19, + TemplateNotFound: 20, + RenameDenied: 21, + ClassNotFound: 22, + MethodNotFound: 23, + PropertyNotFound: 24, + SetPropertyDenied: 25, + ReadOnlyProperty: 26 +}; + +class AsyncException extends Error + { + constructor() + { + super(); + this.raised = false; + } + + raise(type, code, message) + { + this.type = (type == 0 ? "Management" : "Execusion"); + this.code = code; + + if (type == 0) + for(var i in ExceptionCode) + if (ExceptionCode[i] == code) + { + this.message = i; + break; + } + else + this.message = message; + + this.raised = true; + } + + toString() + { + return this.type + " " + this.code + " " + this.message; + } + } \ No newline at end of file diff --git a/src/AsyncQueue.js b/src/AsyncQueue.js index 4bc8bfa..c9fd53e 100644 --- a/src/AsyncQueue.js +++ b/src/AsyncQueue.js @@ -22,6 +22,9 @@ /** * Created by Ahmed Zamil on 25/07/2017. */ + +"use strict"; + class AsyncQueue extends AsyncReply { diff --git a/src/AsyncReply.js b/src/AsyncReply.js index 2487b17..61215f2 100644 --- a/src/AsyncReply.js +++ b/src/AsyncReply.js @@ -23,13 +23,72 @@ /** * Created by Ahmed Zamil on 25/07/2017. */ + +"use strict"; + +const ErrorType = { + Management: 0, + Exception: 1 +}; + +const ProgressType = { + Execution: 0, + Network: 1 +}; + class AsyncReply { then(callback) { this.callbacks.push(callback); + if (this.ready) + { callback(this.result, this); + + if (!this.taskExpired) + { + this.taskExpired = true; + this.resolveTask(this.result); + } + } + + return this; + } + + catch(callback) + { + return error(callback); + } + + error(callback) + { + this.errorCallbacks.push(callback); + + if (this.exception.raised) + { + callback(this.exception); + + if (!this.taskExpired) + { + this.taskExpired = true; + this.rejectTask(this.exception); + } + } + + return this; + } + + progress(callback) + { + this.progressCallbacks.push(callback); + return this; + } + + chunk(callback) + { + this.chunkCallbacks.push(callback); + return this; } trigger(result) @@ -39,15 +98,79 @@ class AsyncReply for(var i = 0; i < this.callbacks.length; i++) this.callbacks[i](result, this); + + + if (!this.taskExpired) + { + this.taskExpired = true; + this.resolveTask(this.result); + } + } + + + triggerError(type, code, message)//exception) + { + if (this.ready) + return; + + this.exception.raise(type, code, message);// = exception; + + for(var i = 0; i < this.errorCallbacks.length; i++) + this.errorCallbacks[i](this.exception, this); + + + if (!this.taskExpired) + { + this.taskExpired = true; + this.rejectTask(this.exception); + } + } + + triggerProgress(type, value, max) + { + if (this.ready) + return; + + for(var i = 0; i < this.progressCallbacks.length; i++) + this.progressCallbacks[i](type, value, max, this); + } + + triggerChunk(value) + { + if (this.ready) + return; + + for(var i = 0; i < this.chunkCallbacks.length; i++) + this.chunkCallbacks[i](value, this); } constructor(result) { this.callbacks = []; + this.errorCallbacks = []; + this.progressCallbacks = []; + this.chunkCallbacks = []; + this.exception = new AsyncException();// null; - if (result) { + var self = this; + + this.task = new Promise(function(resolve, reject){ + self.resolveTask = resolve; + self.rejectTask = reject; + }); + + + if (result !== undefined) { this.result = result; this.ready = true; + this.taskExpired = true; + this.resolveTask(result); + } + else + { + this.taskExpired = false; + this.ready = false; + this.result = null; } } } \ No newline at end of file diff --git a/src/Authentication.js b/src/Authentication.js new file mode 100644 index 0000000..b93f46b --- /dev/null +++ b/src/Authentication.js @@ -0,0 +1,50 @@ +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 16/11/2017. + */ + +"use strict"; + + const AuthenticationType = { + Host: 0, + CoHost: 1, + Client: 2, + Alien: 3 + }; + + class Authentication +{ + constructor(type) + { + this.type = type; + this.state = 0; + this.domain = null; + this.username = null; + } + + get fullName() + { + return this.domain + "@" + this.username; + } +} diff --git a/src/AutoList.js b/src/AutoList.js index d7d1976..2f79068 100644 --- a/src/AutoList.js +++ b/src/AutoList.js @@ -24,6 +24,8 @@ * Created by Ahmed Zamil on 05/09/2017. */ +"use strict"; + class AutoList extends IEventHandler { constructor() @@ -32,10 +34,15 @@ class AutoList extends IEventHandler this.list = []; } + get length() + { + return this.list.length; + } + add(value) { if (value instanceof IDestructible) - value.on("destroy", this._item_destroyed); + value.on("destroy", this._item_destroyed, this); this.list.push(value); @@ -48,7 +55,7 @@ class AutoList extends IEventHandler return; if (value instanceof IDestructible) - value.on("destroy", this._item_destroyed); + value.on("destroy", this._item_destroyed, this); if (this.list[index] instanceof IDestructible) this.list[index].off("destroy", this._item_destroyed); @@ -56,6 +63,16 @@ class AutoList extends IEventHandler this.list[index] = value; } + at(index) + { + return this.list[index]; + } + + item(index) + { + return this.list[index]; + } + remove(value) { this.removeAt(this.list.indexOf(value)); @@ -66,6 +83,11 @@ class AutoList extends IEventHandler return this.list.indexOf(value) > -1; } + toArray() + { + return this.list.slice(0); + } + removeAt(index) { if (index >= this.list.length || index < 0) diff --git a/src/AutoMap.js b/src/AutoMap.js index 7871ed6..bc6881e 100644 --- a/src/AutoMap.js +++ b/src/AutoMap.js @@ -24,6 +24,8 @@ * Created by Ahmed Zamil on 05/09/2017. */ +"use strict"; + class AutoMap extends IEventHandler { constructor() diff --git a/src/BinaryList.js b/src/BinaryList.js index 4bb5594..c11ab0c 100644 --- a/src/BinaryList.js +++ b/src/BinaryList.js @@ -24,7 +24,9 @@ * Created by Ahmed Zamil on 25/08/2017. */ -var BL = function(){ +"use strict"; + +function BL(){ return new BinaryList(); }; @@ -140,6 +142,16 @@ class BinaryList return rt; } + toDC() + { + return new DC(this.toArray()); + } + + addDateTime(value, position) + { + return this.add({type: DataType.DateTime, value: value}, position); + } + addUint8Array(value, position) { return this.add({type: DataType.UInt8Array, value: value}, position); diff --git a/src/Codec.js b/src/Codec.js index 9d99ce6..9b5cfdd 100644 --- a/src/Codec.js +++ b/src/Codec.js @@ -24,16 +24,17 @@ * Created by Ahmed Zamil on 25/07/2017. */ -var ResourceComparisonResult = +"use strict"; + +const ResourceComparisonResult = { Null: 0, Distributed: 1, - DistributedSameClass: 2, - Local: 3, - Same: 4 + Local: 2, + Same: 3 }; -var StructureComparisonResult = +const StructureComparisonResult = { Null: 0, Structure: 1, @@ -44,6 +45,7 @@ var StructureComparisonResult = class Codec { + static parse(data, offset, sizeObject, connection, dataType = DataType.Unspecified) { var size; @@ -215,16 +217,251 @@ class Codec { return connection.fetch(iid);// Warehouse.Get(iid); } + /// + /// 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 parseResourceArray(data, offset, length, connection) + { + var reply = new AsyncBag(); + if (length == 0) + { + reply.seal(); + return reply; + } - static parseStructure(data, offset, contentLength, connection, keylist = null, typelist = null, keys = null, types = null) { - var reply = new AsyncReply(); - var bag = new AsyncBag(); + var end = offset + length; + + // + var result = data[offset++]; + + var previous = null; + + if (result == ResourceComparisonResult.Null) + previous = new AsyncReply(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); - if (keylist == null) - keylist = []; - if (typelist == null) - typelist = []; + while (offset < end) + { + result = data[offset++]; + + var current = null; + + if (result == ResourceComparisonResult.Null) + { + current = new AsyncReply(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 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. + + static composePropertyValueArray(array, connection, prependLength = false) + { + var rt = BL(); + for (var i = 0; i < array.Length; i++) + rt.addUint8Array(Codec.composePropertyValue(array[i], connection)); + if (prependLength) + rt.addUint32(rt.length, 0); + return rt.toArray(); + } + + /// + /// Compose a property value. + /// + /// Property value + /// DistributedConnection is required to check locality. + /// Array of bytes in the network byte order. + static composePropertyValue(propertyValue, connection) + { + // age, date, value + return BL().addUint64(propertyValue.age) + .addDateTime(propertyValue.date) + .addUint8Array(Codec.compose(propertyValue.value, connection)) + .toArray(); + } + + + /// + /// Parse property value. + /// + /// Array of bytes. + /// Zero-indexed offset. + /// DistributedConnection is required to fetch resources. + /// Output content size. + /// PropertyValue. + static parsePropertyValue(data, offset, sizeObject, connection) + { + var reply = new AsyncReply(); + + var age = data.getUint64(offset); + offset += 8; + + var date = data.getDateTime(offset); + offset += 8; + + var cs = {}; + + Codec.parse(data, offset, cs, connection).then(function(value) + { + reply.trigger(new PropertyValue(value, age, date)); + }); + + sizeObject.size = 16 + cs.size; + 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 parseHistory(data, offset, length, resource, connection) + { + var list = new KeyList(); + + var reply = new AsyncReply(); + + var bagOfBags = new AsyncBag(); + + var ends = offset + length; + 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(Codec.parsePropertyValueArray(data, offset, cs, connection)); + offset += cs; + } + + bagOfBags.seal(); + + bagOfBags.then(x => + { + for(var i = 0; i < list.length; i++) + list.values[i] = x[i]; + + reply.trigger(list); + }); + + return reply; + + } + + /// + /// Compose resource history + /// + /// History + /// DistributedConnection is required to fetch resources. + /// + static composeHistory(history, connection, prependLength = false) + { + var rt = new BinaryList(); + + for (var i = 0; i < history.length; i++) + rt.addUint8(history.keys[i].index).addUint8Array(Codec.composePropertyValueArray(history.values[i], connection, true)); + + if (prependLength) + rt.addUint32(rt.length, 0); + + return rt.toArray(); + } + + /// + /// Parse an array of ProperyValue. + /// + /// Array of bytes. + /// Zero-indexed offset. + /// Number of bytes to parse. + /// DistributedConnection is required to fetch resources. + /// + static parsePropertyValueArray(data, offset, length, connection) + { + var rt = new AsyncBag(); + + while (length > 0) + { + var cs = {}; + + rt.add(Codec.parsePropertyValue(data, offset, cs, connection)); + + if (cs.size > 0) + { + offset += cs.size; + length -= cs.size; + } + else + throw new Exception("Error while parsing ValueInfo structured data"); + } + + rt.seal(); + return rt; + } + + static parseStructure(data, offset, contentLength, connection, metadata = null, keys = null, types = null) + { + var reply = new AsyncReply(); + var bag = new AsyncBag(); + + + var keylist = []; + var typelist = []; + if (keys == null) { while (contentLength > 0) { @@ -249,11 +486,12 @@ class Codec { var rt = {}; bag.add(Codec.parse(data, offset, rt, connection)); - contentLength -= rt.size + 1; - offset += rt.size + 1; + contentLength -= rt.size; + offset += rt.size; } } else { + for (var i = 0; i < keys.length; i++) { keylist.push(keys[i]); typelist.push(types[i]); @@ -279,7 +517,12 @@ class Codec { reply.trigger(s); }); - + if (metadata != null) + { + metadata.keys = keylist; + metadata.types = typelist; + } + return reply; } @@ -308,6 +551,11 @@ class Codec { static compose(value, connection, prependType = true) { + if (value instanceof Function) + value = value(connection); + else if (value instanceof DistributedPropertyContext) + value = value.method(this); + var type = Codec.getDataType(value, connection); var rt = new BinaryList(); @@ -322,7 +570,7 @@ class Codec { break; case DataType.Resource: - rt.addUint32(value.instance.id); + rt.addUint32(value._p.instanceId); break; case DataType.DistributedResource: @@ -462,7 +710,7 @@ class Codec { static isLocalResource(resource, connection) { if (resource instanceof DistributedResource) - if (resource.connection == connection) + if (resource._p.connection == connection) return true; return false; @@ -477,58 +725,47 @@ static isLocalResource(resource, connection) { } static compareResource(previous, next, connection) { + if (next == null) return ResourceComparisonResult.Null; - - if (next == previous) + else if (next == previous) return ResourceComparisonResult.Same; - - if (Codec.isLocalResource(next, connection)) + else if (Codec.isLocalResource(next, connection)) return ResourceComparisonResult.Local; - - if (previous == null) + else return ResourceComparisonResult.Distributed; - - if (previous.instance.template.classId.valueOf() == next.instance.template.classId.valueOf()) - return ResourceComparisonResult.DistributedSameClass; - - return ResourceComparisonResult.Distributed; } static composeResourceArray(resources, connection, prependLength = false) { - if (resources == null || resources.length == 0 || !(resources instanceof ResourceArray)) - return new DC(0); - var rt = new BinaryList(); - var comparsion = Codec.compareResource(null, resources[0], connection); + if (resources == null || resources.length == 0)// || !(resources instanceof ResourceArray)) + return prependLength ? new DC(4) : new DC(0); - rt.addUint8(comparsion); + var rt = new BinaryList(); + var comparsion = Codec.compareResource(null, resources[0], connection); - if (comparsion == ResourceComparisonResult.Local) - rt.addUint32(resources[0].id); - else if (comparsion == ResourceComparisonResult.Distributed) { - rt.addUint8Array(resources[0].instance.template.classId.value); - rt.addUint32(resources[0].instance.id); - } + rt.addUint8(comparsion); - for (var i = 1; i < resources.length; i++) { - comparsion = Codec.compareResource(resources[i - 1], resources[i], connection); - rt.addUint8(comparsion); - if (comparsion == ResourceComparisonResult.Local) - rt.addUint32(resources[0].id); - else if (comparsion == ResourceComparisonResult.Distributed) { - rt.addUint8Array(resources[0].instance.template.classId.value); - rt.addUint32(resources[0].instance.id); - } - else if (comparsion == ResourceComparisonResult.DistributedSameClass) { - rt.addUint32(resources[0].instance.id); - } - } + if (comparsion == ResourceComparisonResult.Local) + rt.addUint32(resources[0]._p.instanceId); + else if (comparsion == ResourceComparisonResult.Distributed) + rt.addUint32(resources[0].instance.id); - if (prependLength) - rt.addUint32(0, rt.length); + for (var i = 1; i < resources.Length; i++) + { + comparsion = Codec.compareResource(resources[i - 1], resources[i], connection); + rt.addUint8(comparsion); + if (comparsion == ResourceComparisonResult.Local) + rt.addUint32(resources[i]._p.instanceId); + else if (comparsion == ResourceComparisonResult.Distributed) + rt.addUint32(resources[i].instance.id); + } - return rt.toArray(); + if (prependLength) + rt.addUint32(rt.length, 0); + + + return rt.toArray(); } @@ -619,4 +856,83 @@ static getDataType(value) { return DataType.Void; } } + + + /// + /// 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 parseStructureArray(data, offset, length, connection) + { + var reply = new AsyncBag(); + if (length == 0) + { + reply.seal(); + return reply; + } + + var end = offset + length; + + var result = data[offset++]; + + var previous = null; + //var previousKeys = []; + //var previousTypes = []; + + var metadata = {keys: null, types: null}; + + + if (result == StructureComparisonResult.Null) + previous = new AsyncReply(null); + else if (result == StructureComparisonResult.Structure) + { + var cs = data.getUint32(offset); + offset += 4; + previous = this.parseStructure(data, offset, cs, connection, metadata); + offset += cs; + } + + reply.add(previous); + + + while (offset < end) + { + result = data[offset++]; + + if (result == StructureComparisonResult.Null) + previous = new AsyncReply(null); + else if (result == StructureComparisonResult.Structure) + { + var cs = data.getUint32(offset); + offset += 4; + previous = this.parseStructure(data, offset, cs, connection, metadata); + offset += cs; + } + else if (result == StructureComparisonResult.StructureSameKeys) + { + var cs = data.getUint32(offset); + offset += 4; + previous = this.parseStructure(data, offset, cs, connection, metadata, metadata.keys); + offset += cs; + } + else if (result == StructureComparisonResult.StructureSameTypes) + { + var cs = data.getUint32(offset); + offset += 4; + previous = this.parseStructure(data, offset, cs, connection, metadata, metadata.keys, metadata.types); + offset += cs; + } + + reply.add(previous); + } + + reply.seal(); + return reply; + } + + } \ No newline at end of file diff --git a/src/CustomResourceEvent.js b/src/CustomResourceEvent.js new file mode 100644 index 0000000..18965f9 --- /dev/null +++ b/src/CustomResourceEvent.js @@ -0,0 +1,37 @@ +/* +* Copyright (c) 2017-2018 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 10/11/2018. + */ + +"use strict"; + +class CustomResourceEvent +{ + constructor(issuer, receivers, params) + { + this.issuer = issuer; + this.receivers = receivers; + this.params = params; + } +} \ No newline at end of file diff --git a/src/DataConverter.js b/src/DataConverter.js index a884a0b..db6e7b0 100644 --- a/src/DataConverter.js +++ b/src/DataConverter.js @@ -24,8 +24,10 @@ * Created by Ahmed Zamil on 25/07/2017. */ -var UNIX_EPOCH = 621355968000000000; -var TWO_PWR_32 = (1 << 16) * (1 << 16); +"use strict"; + +const UNIX_EPOCH = 621355968000000000; +const TWO_PWR_32 = (1 << 16) * (1 << 16); class DC extends Uint8Array// extends DataView // Data Converter { @@ -155,7 +157,17 @@ class DC extends Uint8Array// extends DataView // Data Converter return new DC(rt); } + static stringArrayToBytes(values) + { + var list = new BinaryList(); + for(var i = 0; i < values.length; i++) + { + var s = DC.stringToBytes(values[i]); + list.addUint32(s.length).addUint8Array(s); + } + return list.toArray(); + } append(src, offset, length) { @@ -438,7 +450,7 @@ class DC extends Uint8Array// extends DataView // Data Converter getDateTime(offset) { var ticks = this.getUint64(offset); - return new Date(Math.round((ticks-DCStatic.UNIX_EPOCH)/10000)); + return new Date(Math.round((ticks-UNIX_EPOCH)/10000)); } getDateTimeArray(offset) @@ -451,7 +463,7 @@ class DC extends Uint8Array// extends DataView // Data Converter getGuid(offset) { - return new Guid(this.getUint8Array(offset, 16)); + return new Guid(this.clip(offset, 16)); /* var d = this.getUint8Array(offset, 16); diff --git a/src/DataType.js b/src/DataType.js index 28c51ba..307d416 100644 --- a/src/DataType.js +++ b/src/DataType.js @@ -23,7 +23,11 @@ /** * Created by Ahmed Zamil on 25/07/2017. */ -DataType = { + +"use strict"; + +const DataType = +{ Void: 0x0, //Variant, Bool: 1, diff --git a/src/DistributedConnection.js b/src/DistributedConnection.js index 954dae2..43ded9b 100644 --- a/src/DistributedConnection.js +++ b/src/DistributedConnection.js @@ -24,22 +24,26 @@ * Created by Ahmed Zamil on 25/07/2017. */ -var AuthenticationType = -{ - Host: 0, - CoHost: 1, - Client: 2, - Alien: 3 -}; +"use strict"; class DistributedConnection extends IStore { send(data) { + console.log("Send", data.length); this.socket.send(data.buffer); } - sendParams() { - return new SendList(this); + sendParams(doneReply) { + return new SendList(this, doneReply); + } + + generateNonce(length) + { + var rt = new Uint8Array(length); + for(var i = 0; i < length; i++) + rt[i] = Math.random() * 255; + + return rt; } constructor(url, domain, username, password, checkInterval = 30, connectionTimeout = 600, revivingTime = 120) { @@ -47,9 +51,20 @@ class DistributedConnection extends IStore { super(); //Instance.Name = Global.GenerateCode(12); - this.hostType = AuthenticationType.Client; - this.domain = domain; - this.localUsername = username; + + //this.hostType = AuthenticationType.Client; + //this.domain = domain; + //this.localUsername = username; + + this._register("ready"); + this._register("error"); + this._register("close"); + + this.session = new Session(new Authentication(AuthenticationType.Client), new Authentication(AuthenticationType.Host)); + + this.session.localAuthentication.domain = domain; + this.session.localAuthentication.username = username; + this.localPassword = DC.stringToBytes(password); this.socket = new WebSocket(url, "iip"); @@ -70,10 +85,10 @@ class DistributedConnection extends IStore { this.authPacket = new IIPAuthPacket(); this.resources = {}; - this.templates = {}; + this.templates = new KeyList(); this.requests = {}; this.pathRequests = {}; - this.templateRequests = {}; + this.templateRequests = new KeyList(); this.resourceRequests = {}; this.callbackCounter = 0; @@ -88,8 +103,8 @@ class DistributedConnection extends IStore { } }); - this.localNonce = new Uint8Array(32); - window.crypto.getRandomValues(this.localNonce); + this.localNonce = this.generateNonce(32);// new Uint8Array(32); + //window.crypto.getRandomValues(this.localNonce); // declare (Credentials -> No Auth, No Enctypt) var un = DC.stringToBytes(username); @@ -104,7 +119,7 @@ class DistributedConnection extends IStore { this.socket.onmessage = function (msg) { - //console.log(msg); + console.log("Rec", msg.data.byteLength); this.networkBuffer.writeAll(msg.data); @@ -115,247 +130,387 @@ class DistributedConnection extends IStore { }; + + this.socket.onclose = function(event) + { + self.close(event); + }; + + //this.socket.onerror = function(event) + //{ + // self.close(event); + //}; } + processPacket(msg, offset, ends, data) + { + + + var authPacket = this.authPacket; + + if (this.ready) { + var packet = new IIPPacket(); + + var rt = packet.parse(msg, offset, ends); + if (rt <= 0) { + data.holdFor(msg, offset, ends - offset, -rt); + return ends; + } + else { + offset += rt; + + if (packet.command == IIPPacketCommand.Event) { + switch (packet.event) { + case IIPPacketEvent.ResourceReassigned: + this.IIPEventResourceReassigned(packet.resourceId, packet.newResourceId); + break; + case IIPPacketEvent.ResourceDestroyed: + this.IIPEventResourceDestroyed(packet.resourceId); + break; + case IIPPacketEvent.PropertyUpdated: + this.IIPEventPropertyUpdated(packet.resourceId, packet.methodIndex, packet.content); + break; + case IIPPacketEvent.EventOccurred: + this.IIPEventEventOccurred(packet.resourceId, packet.methodIndex, packet.content); + break; + + case IIPPacketEvent.ChildAdded: + this.IIPEventChildAdded(packet.resourceId, packet.childId); + break; + case IIPPacketEvent.ChildRemoved: + this.IIPEventChildRemoved(packet.resourceId, packet.childId); + break; + case IIPPacketEvent.Renamed: + this.IIPEventRenamed(packet.resourceId, packet.content); + break; + case IIPPacketEvent.AttributesUpdated: + this.IIPEventAttributesUpdated(packet.resourceId, packet.content); + break; + + } + } + else if (packet.command == IIPPacketCommand.Request) { + switch (packet.action) { + + // Manage + case IIPPacketAction.AttachResource: + this.IIPRequestAttachResource(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.ReattachResource: + this.IIPRequestReattachResource(packet.callbackId, packet.resourceId, packet.resourceAge); + break; + case IIPPacketAction.DetachResource: + this.IIPRequestDetachResource(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.CreateResource: + this.IIPRequestCreateResource(packet.callbackId, packet.storeId, packet.resourceId, packet.content); + break; + case IIPPacketAction.DeleteResource: + this.IIPRequestDeleteResource(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.AddChild: + this.IIPRequestAddChild(packet.callbackId, packet.resourceId, packet.childId); + break; + case IIPPacketAction.RemoveChild: + this.IIPRequestRemoveChild(packet.callbackId, packet.resourceId, packet.childId); + break; + case IIPPacketAction.RenameResource: + this.IIPRequestRenameResource(packet.callbackId, packet.resourceId, packet.content); + break; + + // Inquire + case IIPPacketAction.TemplateFromClassName: + this.IIPRequestTemplateFromClassName(packet.callbackId, packet.className); + break; + case IIPPacketAction.TemplateFromClassId: + this.IIPRequestTemplateFromClassId(packet.callbackId, packet.classId); + break; + case IIPPacketAction.TemplateFromResourceId: + this.IIPRequestTemplateFromResourceId(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.QueryLink: + this.IIPRequestQueryResources(packet.callbackId, packet.resourceLink); + break; + case IIPPacketAction.ResourceChildren: + this.IIPRequestResourceChildren(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.ResourceParents: + this.IIPRequestResourceParents(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.ResourceHistory: + this.IIPRequestInquireResourceHistory(packet.callbackId, packet.resourceId, packet.fromDate, packet.toDate); + break; + + // Invoke + case IIPPacketAction.InvokeFunction: + this.IIPRequestInvokeFunction(packet.callbackId, packet.resourceId, packet.methodIndex, packet.content); + break; + case IIPPacketAction.GetProperty: + this.IIPRequestGetProperty(packet.callbackId, packet.resourceId, packet.methodIndex); + break; + case IIPPacketAction.GetPropertyIfModified: + this.IIPRequestGetPropertyIfModifiedSince(packet.callbackId, packet.resourceId, packet.methodIndex, packet.resourceAge); + break; + case IIPPacketAction.SetProperty: + this.IIPRequestSetProperty(packet.callbackId, packet.resourceId, packet.methodIndex, packet.content); + break; + case IIPPacketAction.ResourceHistory: + this.IIPRequestInquireResourceHistory(packet.callbackId, packet.resourceId, packet.fromDate, packet.toDate); + break; + case IIPPacketAction.QueryLink: + this.IIPRequestQueryResources(packet.callbackId, packet.resourceLink); + break; + + // Attribute + case IIPPacketAction.GetAllAttributes: + this.IIPRequestGetAttributes(packet.callbackId, packet.resourceId, packet.content, true); + break; + case IIPPacketAction.UpdateAllAttributes: + this.IIPRequestUpdateAttributes(packet.callbackId, packet.resourceId, packet.content, true); + break; + case IIPPacketAction.ClearAllAttributes: + this.IIPRequestClearAttributes(packet.callbackId, packet.resourceId, packet.content, true); + break; + case IIPPacketAction.GetAttributes: + this.IIPRequestGetAttributes(packet.callbackId, packet.resourceId, packet.content, false); + break; + case IIPPacketAction.UpdateAttributes: + this.IIPRequestUpdateAttributes(packet.callbackId, packet.resourceId, packet.content, false); + break; + case IIPPacketAction.ClearAttributes: + this.IIPRequestClearAttributes(packet.callbackId, packet.resourceId, packet.content, false); + break; + + } + } + else if (packet.command == IIPPacketCommand.Reply) { + switch (packet.action) { + case IIPPacketAction.AttachResource: + this.IIPReply(packet.callbackId, packet.classId, packet.resourceAge, packet.resourceLink, packet.content); + break; + case IIPPacketAction.ReattachResource: + this.IIPReply(packet.callbackId, packet.resourceAge, packet.content); + break; + case IIPPacketAction.DetachResource: + this.IIPReply(packet.callbackId); + break; + case IIPPacketAction.CreateResource: + this.IIPReply(packet.callbackId, packet.resourceId); + break; + case IIPPacketAction.DeleteResource: + case IIPPacketAction.AddChild: + case IIPPacketAction.RemoveChild: + case IIPPacketAction.RenameResource: + this.IIPReply(packet.callbackId); + break; + case IIPPacketAction.TemplateFromClassName: + case IIPPacketAction.TemplateFromClassId: + case IIPPacketAction.TemplateFromResourceId: + this.IIPReply(packet.callbackId, ResourceTemplate.parse(packet.content)); + break; + + case IIPPacketAction.QueryLink: + case IIPPacketAction.ResourceChildren: + case IIPPacketAction.ResourceParents: + case IIPPacketAction.ResourceHistory: + this.IIPReply(packet.callbackId, packet.content); + break; + + case IIPPacketAction.InvokeFunction: + this.IIPReplyInvoke(packet.callbackId, packet.content); + break; + case IIPPacketAction.GetProperty: + this.IIPReply(packet.callbackId, packet.content); + break; + case IIPPacketAction.GetPropertyIfModified: + this.IIPReply(packet.callbackId, packet.content); + break; + case IIPPacketAction.SetProperty: + this.IIPReply(packet.callbackId); + break; + + // Attribute + case IIPPacketAction.GetAllAttributes: + case IIPPacketAction.GetAttributes: + this.IIPReply(packet.callbackId, packet.content); + break; + + case IIPPacketAction.UpdateAllAttributes: + case IIPPacketAction.UpdateAttributes: + case IIPPacketAction.ClearAllAttributes: + case IIPPacketAction.ClearAttributes: + this.IIPReply(packet.callbackId); + break; + + } + + } + else if (packet.command == IIPPacketCommand.Report) + { + switch (packet.report) + { + case IIPPacketReport.ManagementError: + this.IIPReportError(packet.callbackId, ErrorType.Management, packet.errorCode, null); + break; + case IIPPacketReport.ExecutionError: + this.IIPReportError(packet.callbackId, ErrorType.Exception, packet.errorCode, packet.errorMessage); + break; + case IIPPacketReport.ProgressReport: + this.IIPReportProgress(packet.callbackId, ProgressType.Execution, packet.progressValue, packet.progressMax); + break; + case IIPPacketReport.ChunkStream: + this.IIPReportChunk(packet.callbackId, packet.content); + + break; + } + } + + } + } + + else { + var rt = authPacket.parse(msg, offset, ends); + + + if (rt <= 0) { + data.holdAllFor(msg, ends - rt); + return ends; + } + else { + offset += rt; + + if (this.session.localAuthentication.type == AuthenticationType.Host) { + if (authPacket.command == IIPAuthPacketCommand.Declare) { + if (authPacket.remoteMethod == IIPAuthPacketMethod.credentials + && authPacket.localMethod == IIPAuthPacketMethod.None) { + this.session.remoteAuthentication.username = authPacket.remoteUsername; + this.remoteNonce = authPacket.remoteNonce; + this.domain = authPacket.domain; + this.sendParams().addUint8(0xa0).addUint8Array(this.localNonce).done(); + } + } + else if (authPacket.command == IIPAuthPacketCommand.Action) { + if (authPacket.action == IIPAuthPacketAction.AuthenticateHash) { + var remoteHash = authPacket.hash; + + this.server.membership.getPassword(this.session.remoteAuthentication.username, this.domain).then(function (pw) { + if (pw != null) { + + //var hash = new DC(sha256.arrayBuffer(BL().addString(pw).addUint8Array(remoteNonce).addUint8Array(this.localNonce).toArray())); + var hash = SHA256.compute(BL().addString(pw).addUint8Array(remoteNonce).addUint8Array(this.localNonce).toDC()); + + + if (hash.sequenceEqual(remoteHash)) { + // send our hash + //var localHash = new DC(sha256.arrayBuffer((new BinaryList()).addUint8Array(this.localNonce).addUint8Array(remoteNonce).addUint8Array(pw).toArray())); + var localHash = SHA256.compute(BL().addUint8Array(this.localNonce).addUint8Array(remoteNonce).addUint8Array(pw).toDC()); + this.sendParams().addUint8(0).addUint8Array(localHash).done(); + + this.readyToEstablish = true; + } + else { + // incorrect password + this.sendParams().addUint8(0xc0).addInt32(1).addUint16(5).addString("Error").done(); + } + } + }); + } + else if (authPacket.action == IIPAuthPacketAction.NewConnection) { + if (readyToEstablish) { + this.session.id = this.generateNonce(32);// new DC(32); + //window.crypto.getRandomValues(this.session.id); + + this.sendParams().addUint8(0x28).addUint8Array(this.session.id).done(); + this.ready = true; + this._emit("ready", this); + } + } + } + } + else if (this.session.localAuthentication.type == AuthenticationType.Client) { + if (authPacket.command == IIPAuthPacketCommand.Acknowledge) { + this.remoteNonce = authPacket.remoteNonce; + + // send our hash + + //var localHash = new DC(sha256.arrayBuffer(BL().addUint8Array(this.localPassword) + // .addUint8Array(this.localNonce) + // .addUint8Array(this.remoteNonce).toArray())); + + var localHash = SHA256.compute(BL().addUint8Array(this.localPassword) + .addUint8Array(this.localNonce) + .addUint8Array(this.remoteNonce).toDC()); + + this.sendParams().addUint8(0).addUint8Array(localHash).done(); + } + else if (authPacket.command == IIPAuthPacketCommand.Action) { + if (authPacket.action == IIPAuthPacketAction.AuthenticateHash) { + // check if the server knows my password + //var remoteHash = new DC(sha256.arrayBuffer(BL().addUint8Array(this.remoteNonce) + // .addUint8Array(this.localNonce) + // .addUint8Array(this.localPassword).toArray() + //)); + + var remoteHash = SHA256.compute(BL().addUint8Array(this.remoteNonce) + .addUint8Array(this.localNonce) + .addUint8Array(this.localPassword).toDC()); + + + if (remoteHash.sequenceEqual(authPacket.hash)) { + // send establish request + this.sendParams().addUint8(0x20).addUint16(0).done(); + } + else { + this.sendParams().addUint8(0xc0).addUint32(1).addUint16(5).addString("Error").done(); + } + } + else if (authPacket.action == IIPAuthPacketAction.ConnectionEstablished) { + this.session.id = authPacket.sessionId; + this.ready = true; + this._emit("ready", this); + } + } + else if (authPacket.command == IIPAuthPacketCommand.Error) + { + this._emit("error", this, authPacket.errorCode, authPacket.errorMessage); + this.close(); + } + } + } + } + + return offset; + + //if (offset < ends) + // this.processPacket(msg, offset, ends, data); + } + receive(data) { var msg = data.read(); var offset = 0; var ends = msg.length; var packet = this.packet; - var authPacket = this.authPacket; //console.log("Data"); while (offset < ends) { - - if (this.ready) { - var rt = packet.parse(msg, offset, ends); - if (rt <= 0) { - data.holdFor(msg, offset, ends - offset, -rt); - return; - } - else { - offset += rt; - - if (packet.command == IIPPacketCommand.Event) { - switch (packet.event) { - case IIPPacketEvent.ResourceReassigned: - this.IIPEventResourceReassigned(packet.resourceId, packet.newResourceId); - break; - case IIPPacketEvent.ResourceDestroyed: - this.IIPEventResourceDestroyed(packet.resourceId); - break; - case IIPPacketEvent.PropertyUpdated: - this.IIPEventPropertyUpdated(packet.resourceId, packet.methodIndex, packet.content); - break; - case IIPPacketEvent.EventOccured: - this.IIPEventEventOccured(packet.resourceId, packet.methodIndex, packet.content); - break; - } - } - else if (packet.command == IIPPacketCommand.Request) { - switch (packet.action) { - case IIPPacketAction.AttachResource: - this.IIPRequestAttachResource(packet.callbackId, packet.resourceId); - break; - case IIPPacketAction.ReattachResource: - this.IIPRequestReattachResource(packet.callbackId, packet.resourceId, packet.resourceAge); - break; - case IIPPacketAction.DetachResource: - this.IIPRequestDetachResource(packet.callbackId, packet.resourceId); - break; - case IIPPacketAction.CreateResource: - this.IIPRequestCreateResource(packet.callbackId, packet.className); - break; - case IIPPacketAction.DeleteResource: - this.IIPRequestDeleteResource(packet.callbackId, packet.resourceId); - break; - case IIPPacketAction.TemplateFromClassName: - this.IIPRequestTemplateFromClassName(packet.callbackId, packet.className); - break; - case IIPPacketAction.TemplateFromClassId: - this.IIPRequestTemplateFromClassId(packet.callbackId, packet.classId); - break; - case IIPPacketAction.TemplateFromResourceLink: - this.IIPRequestTemplateFromResourceLink(packet.callbackId, packet.resourceLink); - break; - case IIPPacketAction.TemplateFromResourceId: - this.IIPRequestTemplateFromResourceId(packet.callbackId, packet.resourceId); - break; - case IIPPacketAction.ResourceIdFromResourceLink: - this.IIPRequestResourceIdFromResourceLink(packet.callbackId, packet.resourceLink); - break; - case IIPPacketAction.InvokeFunction: - this.IIPRequestInvokeFunction(packet.callbackId, packet.resourceId, packet.methodIndex, packet.content); - break; - case IIPPacketAction.GetProperty: - this.IIPRequestGetProperty(packet.callbackId, packet.resourceId, packet.methodIndex); - break; - case IIPPacketAction.GetPropertyIfModified: - this.IIPRequestGetPropertyIfModifiedSince(packet.callbackId, packet.resourceId, packet.methodIndex, packet.resourceAge); - break; - case IIPPacketAction.SetProperty: - this.IIPRequestSetProperty(packet.callbackId, packet.resourceId, packet.methodIndex, packet.content); - break; - } - } - else if (packet.command == IIPPacketCommand.Reply) { - switch (packet.action) { - case IIPPacketAction.AttachResource: - this.IIPReply(packet.callbackId, packet.classId, packet.resourceAge, packet.resourceLink, packet.content); - break; - case IIPPacketAction.ReattachResource: - this.IIPReply(packet.callbackId, packet.resourceAge, packet.content); - break; - case IIPPacketAction.DetachResource: - this.IIPReply(packet.callbackId); - break; - case IIPPacketAction.CreateResource: - this.IIPReply(packet.callbackId, packet.classId, packet.resourceId); - break; - case IIPPacketAction.DeleteResource: - this.IIPReply(packet.callbackId); - break; - case IIPPacketAction.TemplateFromClassName: - this.IIPReply(packet.callbackId, ResourceTemplate.parse(packet.content)); - break; - case IIPPacketAction.TemplateFromClassId: - this.IIPReply(packet.callbackId, ResourceTemplate.parse(packet.content)); - break; - case IIPPacketAction.TemplateFromResourceLink: - this.IIPReply(packet.callbackId, ResourceTemplate.parse(packet.content)); - break; - case IIPPacketAction.TemplateFromResourceId: - this.IIPReply(packet.callbackId, ResourceTemplate.parse(packet.content)); - break; - case IIPPacketAction.ResourceIdFromResourceLink: - this.IIPReply(packet.callbackId, packet.classId, packet.resourceId, packet.resourceAge); - break; - case IIPPacketAction.InvokeFunction: - this.IIPReply(packet.callbackId, packet.content); - break; - case IIPPacketAction.GetProperty: - this.IIPReply(packet.callbackId, packet.content); - break; - case IIPPacketAction.GetPropertyIfModified: - this.IIPReply(packet.callbackId, packet.content); - break; - case IIPPacketAction.SetProperty: - this.IIPReply(packet.callbackId); - break; - } - - } - - } - } - - else { - var rt = authPacket.parse(msg, offset, ends); - - - if (rt <= 0) { - data.holdAllFor(msg, ends - rt); - return; - } - else { - offset += rt; - - if (this.hostType == AuthenticationType.Host) { - if (authPacket.command == IIPAuthPacketCommand.Declare) { - if (authPacket.remoteMethod == IIPAuthPacketMethod.credentials - && authPacket.localMethod == IIPAuthPacketMethod.None) { - this.remoteUsername = authPacket.remoteUsername; - this.remoteNonce = authPacket.remoteNonce; - this.domain = authPacket.domain; - this.sendParams().addUint8(0xa0).addUint8Array(this.localNonce).done(); - } - } - else if (authPacket.command == IIPAuthPacketCommand.Action) { - if (authPacket.action == IIPAuthPacketAction.AuthenticateHash) { - var remoteHash = authPacket.hash; - - this.server.membership.getPassword(this.remoteUsername, this.domain).then(function (pw) { - if (pw != null) { - - var hash = new DC(sha256.arrayBuffer(BL().addString(pw).addUint8Array(remoteNonce).addUint8Array(this.localNonce).toArray())); - - - if (hash.sequenceEqual(remoteHash)) { - // send our hash - var localHash = new DC(sha256.arrayBuffer((new BinaryList()).addUint8Array(this.localNonce).addUint8Array(remoteNonce).addUint8Array(pw).toArray())); - this.sendParams().addUint8(0).addUint8Array(localHash).done(); - - this.readyToEstablish = true; - } - else { - // incorrect password - this.sendParams().addUint8(0xc0).addInt32(1).addUint16(5).addString("Error").done(); - } - } - }); - } - else if (authPacket.action == IIPAuthPacketAction.NewConnection) { - if (readyToEstablish) { - this.sessionId = new DC(32); - window.crypto.getRandomValues(this.sessionId); - - this.sendParams().addUint8(0x28).addUint8Array(this.sessionId).done(); - this.ready = true; - this._emit("ready", this); - } - } - } - } - else if (this.hostType == AuthenticationType.Client) { - if (authPacket.command == IIPAuthPacketCommand.Acknowledge) { - this.remoteNonce = authPacket.remoteNonce; - - // send our hash - - var localHash = new DC(sha256.arrayBuffer(BL().addUint8Array(this.localPassword) - .addUint8Array(this.localNonce) - .addUint8Array(this.remoteNonce).toArray())); - this.sendParams().addUint8(0).addUint8Array(localHash).done(); - } - else if (authPacket.command == IIPAuthPacketCommand.Action) { - if (authPacket.action == IIPAuthPacketAction.AuthenticateHash) { - // check if the server knows my password - var remoteHash = new DC(sha256.arrayBuffer(BL().addUint8Array(this.remoteNonce) - .addUint8Array(this.localNonce) - .addUint8Array(this.localPassword).toArray() - )); - - if (remoteHash.sequenceEqual(authPacket.hash)) { - // send establish request - this.sendParams().addUint8(0x20).addUint16(0).done(); - } - else { - this.sendParams().addUint8(0xc0).addUint32(1).addUint16(5).addString("Error").done(); - } - } - else if (authPacket.action == IIPAuthPacketAction.ConnectionEstablished) { - this.sessionId = authPacket.sessionId; - this.ready = true; - this._emit("ready", this); - } - } - else if (authPacket.command == IIPAuthPacketCommand.Error) - { - this._emit("error", this, authPacket.errorCode, authPacket.errorMessage); - this.close(); - } - } - } - } + offset = this.processPacket(msg, offset, ends, data); } + } - close() + close(event) { - this.socket.close(); + this._emit("close", event); + + Warehouse.remove(this); + + if (this.socket.readyState != this.socket.CLOSED) + { + this.socket.close(); + } } trigger(trigger) { @@ -367,10 +522,14 @@ class DistributedConnection extends IStore { return true; } + remove(resource) + { + // nothing to do (IStore interface) + } // Protocol Implementation - sendRequest(action, binaryList) { + sendRequest2(action, binaryList) { var reply = new AsyncReply(); this.callbackCounter++; this.sendParams().addUint8(0x40 | action).addUint32(this.callbackCounter).addRange(binaryList).done(); @@ -378,13 +537,119 @@ class DistributedConnection extends IStore { return reply; } + sendRequest(action) { + var reply = new AsyncReply(); + this.callbackCounter++; + this.requests[this.callbackCounter] = reply; + return this.sendParams(reply).addUint8(0x40 | action).addUint32(this.callbackCounter); + } + + sendInvoke(instanceId, index, parameters) + { + var reply = new AsyncReply(); + + var pb = Codec.composeVarArray(parameters, this, true); + + this.callbackCounter++; + this.sendParams() + .addUint8(0x40 | IIPPacketAction.InvokeFunction) + .addUint32(this.callbackCounter) + .addUint32(instanceId) + .addUint8(index) + .addUint8Array(pb) + .done(); + + this.requests[this.callbackCounter] = reply; + + return reply; + } + + sendError(type, callbackId, errorCode, errorMessage = "") + { + var msg = DC.stringToBytes(errorMessage); + if (type == ErrorType.Management) + this.sendParams() + .addUint8(0xC0 | IIPPacketReport.ManagementError) + .addUint32(callbackId) + .addUint16(errorCode) + .done(); + else if (type == ErrorType.Exception) + this.sendParams() + .addUint8(0xC0 | IIPPacketReport.ExecutionError) + .addUint32(callbackId) + .addUint16(errorCode) + .addUint16(msg.length) + .addUint8Array(msg) + .done(); + } + + sendProgress(callbackId, value, max) + { + this.sendParams() + .addUint8(0xC0 | IIPPacketReport.ProgressReport) + .addUint32(callbackId) + .addInt32(value) + .addInt32(max) + .done(); + } + + sendChunk(callbackId, chunk) + { + var c = Codec.compose(chunk, this, true); + this.sendParams() + .addUint8(0xC0 | IIPPacketReport.ChunkStream) + .addUint32(callbackId) + .addUint8Array(c) + .done(); + } + IIPReply(callbackId) { + var results = Array.prototype.slice.call(arguments, 1); var req = this.requests[callbackId]; + + //console.log("Reply " + callbackId, req); + delete this.requests[callbackId]; req.trigger(results); } + IIPReplyInvoke(callbackId, result) + { + var req = this.requests[callbackId]; + delete this.requests[callbackId]; + + Codec.parse(result, 0, {}, this).then(function(rt) + { + req.trigger(rt); + }); + } + + IIPReportError(callbackId, errorType, errorCode, errorMessage) + { + var req = this.requests[callbackId]; + delete this.requests[callbackId]; + req.triggerError(errorType, errorCode, errorMessage); + } + + IIPReportProgress(callbackId, type, value, max) + { + var req = this.requests[callbackId]; + req.triggerProgress(type, value, max); + } + + IIPReportChunk(callbackId, data) + { + if (this.requests[callbackId]) + { + var req = this.requests[callbackId]; + Codec.parse(data, 0, {}, this).then(function(x) + { + req.triggerChunk(x); + }); + } + } + IIPEventResourceReassigned(resourceId, newResourceId) { } @@ -398,203 +663,367 @@ class DistributedConnection extends IStore { } IIPEventPropertyUpdated(resourceId, index, content) { - if (this.resources[resourceId]) { - // push to the queue to gaurantee serialization - var reply = new AsyncReply(); - this.queue.add(reply); - var r = this.resources[resourceId]; - Codec.parse(content, 0, this).then(function (args) { - var pt = r._p.template.getPropertyTemplateByIndex(index); - if (pt != null) { - reply.trigger(new DistributedResourceQueueItem(r, DistributedResourceQueueItemType.Propery, args, index)); - } - else { // ft found, fi not found, this should never happen - this.queue.remove(reply); - } - }); - } + var self = this; + + this.fetch(resourceId).then(function(r){ + // push to the queue to gaurantee serialization + var item = new AsyncReply(); + self.queue.add(item); + + Codec.parse(content, 0, {}, self).then(function (args) { + var pt = r.instance.template.getPropertyTemplateByIndex(index); + if (pt != null) { + item.trigger(new DistributedResourceQueueItem(r, DistributedResourceQueueItemType.Propery, args, index)); + } + else { // ft found, fi not found, this should never happen + self.queue.remove(item); + } + }); + }); } - IIPEventEventOccured(resourceId, index, content) { - if (this.resources[resourceId]) { + IIPEventEventOccurred(resourceId, index, content) { + var self = this; + + this.fetch(resourceId).then(function(r){ // push to the queue to guarantee serialization - var reply = new AsyncReply(); - var r = this.resources[resourceId]; + var item = new AsyncReply(); + var r = self.resources[resourceId]; - this.queue.add(reply); + self.queue.add(item); - Codec.parseVarArray(content, 0, content.length, this).then(function (args) { - var et = r._p.template.getEventTemplateByIndex(index); + Codec.parseVarArray(content, 0, content.length, self).then(function (args) { + var et = r.instance.template.getEventTemplateByIndex(index); if (et != null) { - reply.trigger(new DistributedResourceQueueItem(r, DistributedResourceQueueItemType.Event, args, index)); + item.trigger(new DistributedResourceQueueItem(r, DistributedResourceQueueItemType.Event, args, index)); } else { // ft found, fi not found, this should never happen - this.queue.remove(reply); + self.queue.remove(item); } }); - } + }); + } + + IIPEventChildAdded(resourceId, childId) + { + var self = this; + + this.fetch(resourceId).then(function(parent) + { + self.fetch(childId).then(function(child) + { + parent.instance.children.add(child); + }); + }); + } + + IIPEventChildRemoved(resourceId, childId) + { + var self = this; + + this.fetch(resourceId).then(function(parent) + { + self.fetch(childId).then(function(child) + { + parent.instance.children.remove(child); + }); + }); + } + + IIPEventRenamed(resourceId, name) + { + this.fetch(resourceId).then(function(resource) + { + resource.instance.attributes.set("name", name.getString(0, name.length)); + }); + } + + + IIPEventAttributesUpdated(resourceId, attributes) + { + var self = this; + + this.fetch(resourceId).then(function(resource) + { + var attrs = attributes.getStringArray(0, attributes.length); + + self.getAttributes(resource, attrs).then(function(s) + { + resource.instance.setAttributes(s); + }); + }); + } + + sendReply(action, callbackId) + { + return this.sendParams().addUint8(0x80 | action).addUint32(callbackId); + } + + sendEvent(evt) + { + return this.sendParams().addUint8(evt); } IIPRequestAttachResource(callback, resourceId) { - var sl = this.sendParams(); + //var sl = this.sendParams(); + var self = this; + Warehouse.get(resourceId).then(function (r) { if (r != null) { - r.instance.on("ResourceEventOccured", this.instance_eventOccured); - r.instance.on("ResourceModified", this.instance_propertyModified); - r.instance.on("ResourceDestroyed", this.instance_resourceDestroyed); + + if (r.instance.applicable(self.session, ActionType.Attach, null) == Ruling.Denied) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AttachDenied); + return; + } + + r.instance.on("ResourceEventOccurred", self.instance_eventOccurred, self); + r.instance.on("ResourceModified", self.instance_propertyModified, self); + r.instance.on("ResourceDestroyed", self.instance_resourceDestroyed, self); // reply ok var link = DC.stringToBytes(r.instance.link); - sl.addUint8(0x80) - .addUint32(callback) - .addUint8Array(r.instance.template.classId.value) - .addUint32(r.instance.age) - .addUint16(link.length) - .addUint8Array(link) - .addUint8Array(Codec.composeVarArray(r.instance.serialize(), this, true)) - .done(); + if (r instanceof DistributedResource) + self.sendReply(IIPPacketAction.AttachResource, callback) + .addUint8Array(r.instance.template.classId.value) + .addUint64(r.instance.age) + .addUint16(link.length) + .addUint8Array(link) + .addUint8Array(Codec.composePropertyValueArray(r._serialize(), self, true)) + .done(); + else + self.sendReply(IIPPacketAction.AttachResource, callback) + .addUint8Array(r.instance.template.classId.value) + .addUint64(r.instance.age) + .addUint16(link.length) + .addUint8Array(link) + .addUint8Array(Codec.composePropertyValueArray(r.instance.serialize(), self, true)) + .done(); } else { // reply failed - //SendParams(0x80, r.Instance.Id, r.Instance.Age, r.Instance.Serialize(false, this)); + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); } }); } IIPRequestReattachResource(callback, resourceId, resourceAge) { - var sl = this.sendParams(); + var self = this; Warehouse.get(resourceId).then(function (r) { if (res != null) { - r.instance.on("ResourceEventOccured", this.instance_eventOccured); - r.instance.on("ResourceModified", this.instance_propertyModified); - r.instance.on("ResourceDestroyed", this.instance_resourceDestroyed); + r.instance.on("ResourceEventOccurred", self.instance_eventOccurred, self); + r.instance.on("ResourceModified", self.instance_propertyModified, self); + r.instance.on("ResourceDestroyed", self.instance_resourceDestroyed, self); // reply ok - sl.addUint8(0x81) - .addUint32(callback) - .addUint32(r.instance.age) - .addUint8Array(Codec.composeVarArray(r.instance.serialize(), this, true)) - .done(); + self.sendReply(IIPPacketAction.ReattachResource, callback) + .addUint64(r.instance.age) + .addUint8Array(Codec.composePropertyValueArray(r.instance.serialize(), self, true)) + .done(); } else { // reply failed + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); } }); } IIPRequestDetachResource(callback, resourceId) { - var sl = this.sendParams(); + var self = this; Warehouse.get(resourceId).then(function (r) { if (r != null) { - r.instance.off("ResourceEventOccured", this.instance_eventOccured); - r.instance.off("ResourceModified", this.instance_propertyModified); - r.instance.off("ResourceDestroyed", this.instance_resourceDestroyed); + r.instance.off("ResourceEventOccurred", self.instance_eventOccurred); + r.instance.off("ResourceModified", self.instance_propertyModified); + r.instance.off("ResourceDestroyed", self.instance_resourceDestroyed); // reply ok - sl.addUint8(0x82).addUint32(callback).done(); + self.sendReply(IIPPacketAction.DetachResource, callback).done(); } else { // reply failed + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); } }); } - IIPRequestCreateResource(callback, className) { - // not implemented + IIPRequestCreateResource(callback, storeId, parentId, content) { + var self = this; + Warehouse.get(storeId).then(function(store) + { + if (store == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.StoreNotFound); + return; + } + + if (!(store instanceof IStore)) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceIsNotStore); + return; + } + + // check security + if (store.instance.applicable(self.session, ActionType.CreateResource, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.CreateDenied); + return; + } + + Warehouse.get(parentId).then(function(parent) + { + + // check security + + if (parent != null) + if (parent.instance.applicable(self.session, ActionType.AddChild, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AddChildDenied); + return; + } + + var 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 = window[className]; + + if (type == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ClassNotFound); + return; + } + + Codec.parseVarArray(content, offset, cl, self).then(function(parameters) + { + offset += cl; + cl = content.getUint32(offset); + Codec.parseStructure(content, offset, cl, self).then(function(attributes) + { + offset += cl; + cl = content.length - offset; + + Codec.parseStructure(content, offset, cl, self).then(function(values) + { + + + var resource = new (Function.prototype.bind.apply(type, values)); + + Warehouse.put(resource, name, store, parent); + + + self.sendReply(IIPPacketAction.CreateResource, callback) + .addUint32(resource.Instance.Id) + .done(); + + }); + }); + }); + }); + }); } IIPRequestDeleteResource(callback, resourceId) { - // not implemented + var self = this; + Warehouse.get(resourceId).then(function(r) + { + if (r == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (r.instance.store.instance.applicable(session, ActionType.Delete, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.DeleteDenied); + return; + } + + if (Warehouse.remove(r)) + self.sendReply(IIPPacketAction.DeleteResource, callback).done(); + else + self.sendError(ErrorType.Management, callback, ExceptionCode.DeleteFailed); + }); } IIPRequestTemplateFromClassName(callback, className) { - var sl = this.sendParams(); + + var self = this; Warehouse.getTemplateByClassName(className).then(function (t) { if (t != null) - sl.addUint8(0x88).addUint32(callback).addUint8Array(t.content).done(); + self.sendReply(IIPPacketAction.TemplateFromClassName, callback) + .addUint32(t.content.length) + .addUint8Array(t.content) + .done(); else { // reply failed + self.sendError(ErrorType.Management, callback, ExceptionCode.TemplateNotFound); } }); } IIPRequestTemplateFromClassId(callback, classId) { - var sl = this.sendParams(); - + var self = this; Warehouse.getTemplateByClassId(classId).then(function (t) { if (t != null) - sl.addUint8(0x89) - .addUint32(callback) - .addUint32(t.content.length) - .addUint8Array(t.content) - .done(); - else { - // reply failed - } - }); - } - - IIPRequestTemplateFromResourceLink(callback, resourceLink) { - var sl = this.sendParams(); - - Warehouse.getTemplate(resourceLink).then(function (t) { - if (t != null) - sl.addUint8(0x8a).addUint32(callback).addUint8Array(t.content).done(); + self.sendReply(IIPPacketAction.TemplateFromClassId, callback) + .addUint32(t.content.length) + .addUint8Array(t.content) + .done(); else { // reply failed + self.sendError(ErrorType.Management, callback, ExceptionCode.TemplateNotFound); } }); } IIPRequestTemplateFromResourceId(callback, resourceId) { - var sl = this.sendParams(); + + var self = this; Warehouse.get(resourceId).then(function (r) { if (r != null) - sl.addUint8(0x8b).addUint32(callback).addUint8Array(r.instance.template.content).done(); - else { - // reply failed - } - }); - } - - IIPRequestResourceIdFromResourceLink(callback, resourceLink) { - - var sl = this.sendParams(); - - Warehouse.get(resourceLink).then(function (r) { - if (r != null) - sl.addUint8(0x8c) - .addUint32(callback) - .addUint8Array(r.instance.template.classId.value) - .addUint32(r.instance.id) - .addUint32(r.instance.age).done(); + self.sendReply(IIPPacketAction.TemplateFromResourceId, callback) + .addUint32(r.instance.template.content.length) + .addUint8Array(r.instance.template.content) + .done(); else { // reply failed + self.sendError(ErrorType.Management, callback, ExceptionCode.TemplateNotFound); } }); } IIPRequestInvokeFunction(callback, resourceId, index, content) { - var sl = this.sendParams(); + + var self = this; Warehouse.get(resourceId).then(function (r) { if (r != null) { - Codec.parseVarArray(content, 0, content.length, sl.connection).then(function (args) { + Codec.parseVarArray(content, 0, content.length, self).then(function (args) { var ft = r.instance.template.getFunctionTemplateByIndex(index); if (ft != null) { if (r instanceof DistributedResource) { var rt = r._invoke(index, args); if (rt != null) { rt.then(function (res) { - sl.addUint8(0x90).addUint32(callback).addUint8Array(Codec.compose(res, sl.connection)).done(); + self.sendReply(IIPPacketAction.InvokeFunction, callback) + .addUint8Array(Codec.compose(res, self)) + .done(); }); } else { @@ -604,19 +1033,30 @@ class DistributedConnection extends IStore { else { var fi = r[ft.name]; + + if (r.instance.applicable(self.session, ActionType.Execute, ft) == Ruling.Denied) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.InvokeDenied); + return; + } + if (fi instanceof Function) { - args.push(sl.connection); + args.push(self); var rt = fi.apply(r, args); if (rt instanceof AsyncReply) { rt.then(function (res) { - sl.addUint8(0x90).addUint32(callback).addUint8Array(Codec.compose(res, sl.connection)).done(); + self.sendReply(IIPPacketAction.InvokeFunction, callback) + .addUint8Array(Codec.compose(res, self)) + .done(); }); } else { - sl.addUint8(0x90).addUint32(callback).addUint8Array(Codec.compose(rt, sl.connection)).done(); + self.sendReply(IIPPacketAction.InvokeFunction, callback) + .addUint8Array(Codec.compose(rt, self)) + .done(); } } else { @@ -636,18 +1076,23 @@ class DistributedConnection extends IStore { } IIPRequestGetProperty(callback, resourceId, index) { - var sl = this.sendParams(); + + var self = this; Warehouse.get(resourceId).then(function (r) { if (r != null) { var pt = r.instance.template.getFunctionTemplateByIndex(index); if (pt != null) { if (r instanceof DistributedResource) { - sl.addUint8(0x91).addUint32(callback).addUint8Array(Codec.compose(r._get(pt.index), sl.connection)).done(); + self.sendReply(IIPPacketAction.GetProperty, callback) + .addUint8Array(Codec.compose(r._get(pt.index), self)) + .done(); } else { var pv = r[pt.name]; - sl.addUint8(0x91).addUint32(callback).addUint8Array(Codec.compose(pv, sl.connection)).done(); + self.sendReply(IIPPacketAction.GetProperty) + .addUint8Array(Codec.compose(pv, self)) + .done(); } } else { @@ -661,7 +1106,8 @@ class DistributedConnection extends IStore { } IIPRequestGetPropertyIfModifiedSince(callback, resourceId, index, age) { - var sl = this.sendParams(); + + var self = this; Warehouse.get(resourceId).then(function (r) { if (r != null) { @@ -669,10 +1115,15 @@ class DistributedConnection extends IStore { if (pt != null) { if (r.instance.getAge(index) > age) { var pv = r[pt.name]; - sl.addUint8(0x92).addUint32(callback).addUint8Array(Codec.compose(pv, sl.connection)).done(); + self.sendReply(IIPPacketAction.GetPropertyIfModified, callback) + .addUint8Array(Codec.compose(pv, self)) + .done(); } - else { - sl.addUint8(0x92).addUint32(callback).addUint8(DataType.NotModified).done(); + else + { + self.sendReply(IIPPacketAction.GetPropertyIfModified, callback) + .addUint8(DataType.NotModified) + .done(); } } else { @@ -686,7 +1137,8 @@ class DistributedConnection extends IStore { } IIPRequestSetProperty(callback, resourceId, index, content) { - var sl = this.sendParams(); + + var self = this; Warehouse.get(resourceId).then(function (r) { if (r != null) { @@ -694,53 +1146,181 @@ class DistributedConnection extends IStore { var pt = r.instance.template.getPropertyTemplateByIndex(index); if (pt != null) { - Codec.parse(content, 0, this).then(function (value) { + Codec.parse(content, 0, {}, this).then(function (value) { if (r instanceof DistributedResource) { // propagation r._set(index, value).then(function (x) { - sl.addUint8(0x93).addUint32(callback).done(); + self.sendReply(IIPPacketAction.SetProperty, callback) + .done(); + }).error(function(x){ + self.sendError(x.type, callback, x.code, x.message) + .done(); }); } - else { - r[pt.name] = value; - sl.addUint8(0x93).addUint32(callback).done(); + else + { + if (r.instance.applicable(self.session, ActionType.SetProperty, pt) == Ruling.Denied) + { + self.sendError(AsyncReply.ErrorType.Exception, callback, ExceptionCode.SetPropertyDenied); + return; + } + + try + { + if (r[pt.name] instanceof DistributedPropertyContext) + value = new DistributedPropertyContext(this, value); + + r[pt.name] = value; + self.sendReply(IIPPacketAction.SetProperty, callback).done(); + } + catch(ex) + { + self.sendError(AsyncReply.ErrorType.Exception, callback, 0, ex.toString()).done(); + } } }); } else { // property not found + self.sendError(AsyncReply.ErrorType.Management, callback, ExceptionCode.PropertyNotFound).done(); } } else { // resource not found + self.sendError(AsyncReply.ErrorType.Management, callback, ExceptionCode.PropertyNotFound).done(); } }); } + IIPRequestInquireResourceHistory(callback, resourceId, fromDate, toDate) + { + var self = this; + Warehouse.get(resourceId).then(function(r) + { + if (r != null) + { + r.instance.store.getRecord(r, fromDate, toDate).then(function(results) + { + var history = Codec.composeHistory(results, self, true); + self.sendReply(IIPPacketAction.ResourceHistory, callback) + .addUint8Array(history) + .done(); + }); + } + }); + } + + IIPRequestQueryResources(callback, resourceLink) + { + var self = this; + + Warehouse.query(resourceLink).then(function(resources) + { + + var list = resources.filter(function(r){return r.instance.applicable(self.session, ActionType.Attach, null) != Ruling.Denied}); + + if (list.length == 0) + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + else + self.sendReply(IIPPacketAction.QueryLink, callback) + .addUint8Array(Codec.composeResourceArray(list, self, true)) + .done(); + }); + } + + create(store, parent, className, parameters, attributes, values) + { + var reply = new AsyncReply(); + var sb = DC.stringToBytes(className); + + var pkt = BL().addUint32(store.instance.id) + .addUint32(parent.instance.id) + .addUint32(sb.length) + .addUint8Array(sb) + .addUint8Array(Codec.composeVarArray(parameters, this, true)) + .addUint8Array(Codec.composeStructure(attributes, this, true, true, true)) + .addUint8Array(Codec.composeStructure(values, this)); + + pkt.addUint32(pkt.length, 8); + + this.sendRequest(IIPPacket.IIPPacketAction.CreateResource).addUint8Array(pkt.ToArray()).done().then(function(args) + { + var rid = args[0]; + + self.fetch(rid).then(function(r) + { + reply.trigger(r); + }); + + }); + + return reply; + } + + query(resourceLink) + { + var reply = new AsyncReply(); + var self = this; + + var sb = DC.stringToBytes(resourceLink); + + this.sendRequest(IIPPacketAction.QueryLink) + .addUint16(sb.length) + .addUint8Array(sb) + .done() + .then(function(args) + { + Codec.parseResourceArray(args[0], 0, args[0].length, self).then(function(resources) { + reply.trigger(resources); + }); + }).error(function(ex){ + reply.triggerError(ex); + }); + + return reply; + } getTemplate(classId) { - if (this.templates[classId]) - return new AsyncReply(this.templates[classId]); - else if (this.templateRequests[classId]) - return this.templateRequests[classId]; + if (this.templates.contains(classId)) + return new AsyncReply(this.templates.item(classId)); + else if (this.templateRequests.contains(classId)) + return this.templateRequests.item(classId); var reply = new AsyncReply(); - this.templateRequests[classId] = reply; + this.templateRequests.add(classId.valueOf(), reply); var self = this; - this.sendRequest(IIPPacketAction.TemplateFromClassId, BL().addUint8Array(classId.value)).then(function (rt) { - delete self.templateRequests[classId]; - self.templates[rt[0].classId] = rt[0]; - reply.trigger(rt[0]); - }); + this.sendRequest(IIPPacketAction.TemplateFromClassId) + .addUint8Array(classId.value) + .done() + .then(function (rt) { + self.templateRequests.remove(classId); + self.templates.add(rt[0].classId.valueOf(), rt[0]); + Warehouse.putTemplate(rt[0]); + reply.trigger(rt[0]); + }); return reply; } // IStore interface get(path) { + + var rt = new AsyncReply(); + + this.query(path).then(function(ar) + { + if (ar != null && ar.length > 0) + rt.trigger(ar[0]); + else + rt.trigger(null); + }).error(function(ex) {rt.triggerError(ex);}); + + return rt; + + /* if (this.pathRequests[path]) return this.pathRequests[path]; @@ -751,9 +1331,11 @@ class DistributedConnection extends IStore { bl.addString(path); bl.addUint16(bl.length, 0); + var link = data.get var self = this; - this.sendRequest(IIPPacketAction.ResourceIdFromResourceLink, bl).then(function (rt) { + this.sendRequest(IIPPacketAction.ResourceIdFromResourceLink) + .addUint16(.then(function (rt) { delete self.pathRequests[path]; self.fetch(rt[1]).then(function (r) { @@ -763,6 +1345,7 @@ class DistributedConnection extends IStore { return reply; + */ } retrieve(iid) { @@ -776,7 +1359,9 @@ class DistributedConnection extends IStore { fetch(id) { if (this.resourceRequests[id] && this.resources[id]) { // dig for dead locks - return this.resourceRequests[id]; + // or not + return new AsyncReply(this.resources[id]); + //return this.resourceRequests[id]; } else if (this.resourceRequests[id]) return this.resourceRequests[id]; @@ -785,76 +1370,452 @@ class DistributedConnection extends IStore { var reply = new AsyncReply(); + this.resourceRequests[id] = reply; + var self = this; - this.sendRequest(IIPPacketAction.AttachResource, BL().addUint32(id)).then(function (rt) { - self.getTemplate(rt[0]).then(function (tmp) { + this.sendRequest(IIPPacketAction.AttachResource) + .addUint32(id) + .done() + .then(function (rt) { + var dr = new DistributedResource(self, id, rt[1], rt[2]); + //var dr = new DistributedResource(self, tmp, id, rt[1], rt[2]); - var dr = new DistributedResource(self, tmp, id, rt[1], rt[2]); - Warehouse.put(dr, id.toString(), self); + self.getTemplate(rt[0]).then(function (tmp) { - Codec.parseVarArray(rt[3], 0, rt[3].length, self).then(function (ar) { - dr._attached(ar); - delete self.resourceRequests[id]; - reply.trigger(dr); - }); - }); - }); + // ClassId, ResourceAge, ResourceLink, Content + Warehouse.put(dr, id.toString(), self, null, tmp); + + + Codec.parsePropertyValueArray(rt[3], 0, rt[3].length, self).then(function (ar) { + dr._attached(ar); + delete self.resourceRequests[id]; + reply.trigger(dr); + }); + }); + }); return reply; } - instance_resourceDestroyed(resource) { - // compose the packet - this.sendParams().addUint8(0x1).addUint32(resource.instance.id).done(); + getRecord(resource, fromDate, toDate) + { + if (resource instanceof DistributedResource) + { + + if (resource._p.connection != this) + return new AsyncReply(null); + + var reply = new AsyncReply(); + + var self = this; + + this.sendRequest(IIPPacketAction.ResourceHistory) + .addUint32(resource._p.instanceId) + .addDateTime(fromDate).addDateTime(toDate) + .done() + .then(function(rt) + { + Codec.parseHistory(rt[0], 0, rt[0].length, resource, self).then(function(history) + { + reply.trigger(history); + }); + }); + + return reply; + } + else + return new AsyncReply(null); } - instance_propertyModified(resource, name, newValue, oldValue) { + instance_resourceDestroyed(resource) { + // compose the packet + this.sendEvent(IIPPacketEvent.ResourceDestroyed) + .addUint32(resource.instance.id) + .done(); + } + + instance_propertyModified(resource, name, newValue) { var pt = resource.instance.template.getPropertyTemplateByName(name); if (pt == null) return; - // compose the packet - if (newValue instanceof Function) - sendParams().addUint8(0x10) - .addUint32(resource.instance.id) - .addUint8(pt.index) - .addUint8Array(Codec.compose(newValue(this), this)) - .done(); - else - sendParams().addUint8(0x10) - .addUint32(resource.instance.id) - .addUint8(pt.index) - .addUint8Array(Codec.compose(newValue, this)) - .done(); + this.sendEvent(IIPPacketEvent.PropertyUpdated) + .addUint32(resource.instance.id) + .addUint8(pt.index) + .addUint8Array(Codec.compose(newValue, this)) + .done(); } - instance_eventOccured(resource, name, receivers, args) { + instance_eventOccurred(resource, issuer, receivers, name, args) { var et = resource.instance.template.getEventTemplateByName(name); if (et == null) return; if (receivers != null) - if (receivers.indexOf(this.remoteUsername) < 0) + if (receivers.indexOf(this.session) < 0) return; - var clientArgs = [];//new object[args.Length]; - for (var i = 0; i < args.Length; i++) - if (args[i] instanceof Function) - clientArgs[i] = args[i](this); - else - clientArgs[i] = args[i]; - + if (resource.instance.applicable(this.session, ActionType.ReceiveEvent, et, issuer) == Ruling.Denied) + return; // compose the packet - sendParams().addUint8(0x11) - .addUint32(resource.instance.id) - .addUint8(et.index) - .addUint8Array(Codec.composeVarArray(args, this, true)) - .done(); + this.sendEvent(IIPPacketEvent.EventOccurred) + .addUint32(resource.instance.id) + .addUint8(et.index) + .addUint8Array(Codec.composeVarArray(args, this, true)) + .done(); } + + + IIPRequestAddChild(callback, parentId, childId) + { + var self = this; + Warehouse.get(parentId).then(function(parent) + { + if (parent == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + Warehouse.get(childId).then(function(child) + { + if (child == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (parent.instance.applicable(self.session, ActionType.AddChild, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AddChildDenied); + return; + } + + if (child.instance.applicable(self.session, ActionType.AddParent, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AddParentDenied); + return; + } + + parent.instance.children.add(child); + + self.sendReply(IIPPacketAction.AddChild, callback) + .done(); + //child.Instance.Parents + }); + + }); + } + + IIPRequestRemoveChild(callback, parentId, childId) + { + var self = this; + + Warehouse.get(parentId).then(function(parent) + { + if (parent == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + Warehouse.get(childId).then(function(child) + { + if (child == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (parent.instance.applicable(self.session, ActionType.RemoveChild, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AddChildDenied); + return; + } + + if (child.instance.applicable(self.session, ActionType.RemoveParent, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.AddParentDenied); + return; + } + + parent.instance.children.remove(child); + + self.sendReply(IIPPacketAction.RemoveChild, callback) + .done(); + //child.Instance.Parents + }); + + }); + } + + IIPRequestRenameResource(callback, resourceId, name) + { + var self = this; + Warehouse.get(resourceId).then(function(resource) + { + if (resource == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (resource.instance.applicable(self.session, ActionType.Rename, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.RenameDenied); + return; + } + + resource.instance.name = name.getString(0, name.length); + self.sendReply(IIPPacketAction.RenameResource, callback) + .done(); + }); + } + + IIPRequestResourceChildren(callback, resourceId) + { + var self = this; + Warehouse.get(resourceId).then(function(resource) + { + if (resource == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + self.sendReply(IIPPacketAction.ResourceChildren, callback) + .addUint8Array(Codec.composeResourceArray(resource.instance.children.toArray(), this, true)) + .done(); + + }); + } + + IIPRequestResourceParents(callback, resourceId) + { + var self = this; + + Warehouse.get(resourceId).then(function(resource) + { + if (resource == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + self.sendReply(IIPPacketAction.ResourceParents, callback) + .addUint8Array(Codec.composeResourceArray(resource.instance.parents.toArray(), this, true)) + .done(); + }); + } + + IIPRequestClearAttributes(callback, resourceId, attributes, all = false) + { + Warehouse.get(resourceId).then(function(r) + { + if (r == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (r.instance.store.instance.applicable(self.session, ActionType.UpdateAttributes, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.UpdateAttributeDenied); + return; + } + + var attrs = []; + + if (!all) + attrs = attributes.getStringArray(0, attributes.length); + + if (r.instance.removeAttributes(attrs)) + self.sendReply(all ? IIPPacketAction.ClearAllAttributes : IIPPacketAction.ClearAttributes, callback) + .done(); + else + self.sendError(AsyncReply.ErrorType.Management, callback, ExceptionCode.UpdateAttributeFailed); + + }); + } + + IIPRequestUpdateAttributes(callback, resourceId, attributes, clearAttributes = false) + { + var self = this; + + Warehouse.get(resourceId).then(function(r) + { + if (r == null) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.ResourceNotFound); + return; + } + + if (r.instance.store.instance.applicable(self.session, ActionType.UpdateAttributes, null) != Ruling.Allowed) + { + self.sendError(ErrorType.Management, callback, ExceptionCode.UpdateAttributeDenied); + return; + } + + Codec.parseStructure(attributes, 0, attributes.Length, this).then(function(attrs) { + if (r.instance.setAttributes(attrs, clearAttributes)) + self.sendReply(clearAttributes ? IIPPacketAction.ClearAllAttributes : IIPPacketAction.ClearAttributes, + callback) + .done(); + else + self.sendError(ErrorType.Management, callback, ExceptionCode.UpdateAttributeFailed); + }); + + }); + + } + + + + getChildren(resource) + { + if (resource._p.connection != this) + return new AsyncReply(null); + + var rt = new AsyncReply(); + var self = this; + + this.sendRequest(IIPPacketAction.ResourceChildren) + .addUint32(resource._p.instanceId) + .done() + .then(function(d) + { + + Codec.parseResourceArray(d, 0, d.length, self).then(function(resources) + { + rt.trigger(resources); + }).error(function(ex) { rt.triggerError(ex); }); + }); + + return rt; + } + + getParents(resource) + { + if (resource._p.connection != this) + return new AsyncReply(null); + + var rt = new AsyncReply(); + var self = this; + + this.sendRequest(IIPPacketAction.ResourceParents) + .addUint32(resource._p.instanceId) + .done() + .then(function(d) + { + Codec.parseResourceArray(d, 0, d.length, this).then(function(resources) + { + rt.trigger(resources); + }).error(function(ex) { rt.triggerError(ex);}); + }); + + return rt; + } + + removeAttributes(resource, attributes = null) + { + if (resource._p.connection != this) + return new AsyncReply(null); + + var rt = new AsyncReply(); + + if (attributes == null) + this.sendRequest(IIPPacketAction.ClearAllAttributes) + .addUint32(resource._p.instanceId) + .done() + .then(function(ar) + { + rt.trigger(true); + }).error(function(ex) { rt.triggerError(ex); }); + else + { + var attrs = DC.stringArrayToBytes(attributes); + this.sendRequest(IIPPacketAction.ClearAttributes) + .addUint32(resource.instance.id) + .addUint32(attrs.length) + .addUint8Array(attrs) + .done() + .then(function(ar) + { + rt.trigger(true); + }).error(function(ex) { rt.triggerChunk(ex); }); + } + + return rt; + } + + setAttributes(resource, attributes, clearAttributes = false) + { + if (resource._p.connection != this) + return new AsyncReply(null); + + var rt = new AsyncReply(); + + this.sendRequest(clearAttributes ? IIPPacketAction.UpdateAllAttributes : IIPPacketAction.UpdateAttributes) + .addUint32(resource._p.instanceId) + .addUint8Array(Codec.composeStructure(attributes, this, true, true, true)) + .done() + .then(function(ar) + { + rt.trigger(true); + }).error(function(ex) {rt.triggerError(ex);}); + + return rt; + } + + getAttributes(resource, attributes = null) + { + if (resource._p.connection != this) + return new AsyncReply(null); + + var rt = new AsyncReply(); + var self = this; + + if (attributes == null) + { + this.sendRequest(IIPPacketAction.GetAllAttributes) + .addUint32(resource._p.instanceId) + .done() + .then(function(ar) + { + Codec.parseStructure(ar[0], 0, ar[0].length, this).then(function(st) + { + for (var a in st) + resource.instance.attributes.set(a, st[a]); + rt.trigger(st); + }).error(function(ex) { rt.triggerError(ex); }); + }); + } + else + { + var attrs = DC.stringArrayToBytes(attributes); + this.sendRequest(IIPPacketAction.GetAttributes) + .addUint32(resource._p.instanceId) + .addUint32(attrs.length) + .addUint8Array(attrs) + .done() + .then(function(ar) + { + Codec.parseStructure(ar[0], 0, ar[0].length, self).then(function(st) + { + for (var a in st) + resource.instance.attributes.set(a, st[a]); + rt.trigger(st); + }).error(function(ex) { rt.triggerError(ex); }); + }); + } + + return rt; + } + } diff --git a/src/DistributedPropertyContext.js b/src/DistributedPropertyContext.js new file mode 100644 index 0000000..c72ceca --- /dev/null +++ b/src/DistributedPropertyContext.js @@ -0,0 +1,43 @@ +/* +* Copyright (c) 2017-2018 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 27/10/2018. + */ + +"use strict"; + +class DistributedPropertyContext +{ + constructor(p1, p2) + { + if(arguments.length == 1) + { + this.method = p1; + } + else if (arguments.length == 2) + { + this.connection = p1; + this.value = p2; + } + } +} diff --git a/src/DistributedResource.js b/src/DistributedResource.js index e85668e..fd07429 100644 --- a/src/DistributedResource.js +++ b/src/DistributedResource.js @@ -24,18 +24,17 @@ * Created by Ahmed Zamil on 25/07/2017. */ +"use strict"; class DistributedResource extends IResource { destroy() { this.destroyed = true; - this._emit("destroy"); + this._emit("destroy", this); } - - - constructor(connection, template, instanceId, age) + constructor(connection, instanceId, age, link) { super(); @@ -44,20 +43,38 @@ class DistributedResource extends IResource connection: connection, instanceId: instanceId, age: age, - template: template + link: link, + properties: [] }; } + _serialize() + { + var props = []; + + for (var i = 0; i < this._p.properties.length; i++) + props.push(new PropertyValue(this._p.properties[i], + this.instance.getAge(i), + this.instance.getModificationDate(i))); + + return props; + } + _attached(properties) { if (this._isAttached) return false; else - { - this._p.properties = properties; - this._p.ages = new Uint32Array(properties.length); - //this.events = [];//new [this.template.events.length]; + { + for(var i = 0; i < properties.length; i++) + { + this.instance.setAge(i, properties[i].age); + this.instance.setModificationDate(i, properties[i].date); + this._p.properties.push(properties[i].value); + } + + this._p.isAttached = true; var self = this; @@ -83,15 +100,15 @@ class DistributedResource extends IResource }; }; - for(var i = 0; i < this._p.template.functions.length; i++) + for(var i = 0; i < this.instance.template.functions.length; i++) { - var ft = this._p.template.functions[i]; + var ft = this.instance.template.functions[i]; this[ft.name] = makeFunc(ft.index); } - for(var i = 0; i < this._p.template.properties.length; i++) + for(var i = 0; i < this.instance.template.properties.length; i++) { - var pt = this._p.template.properties[i]; + var pt = this.instance.template.properties[i]; Object.defineProperty(this, pt.name, { get: makeGetter(pt.index), @@ -107,18 +124,21 @@ class DistributedResource extends IResource _emitEventByIndex(index, args) { - var et = this._p.template.getEventTemplateByIndex(index); - this._emit(et.name, args); - this.instance.emitResourceEvent(et.name, null, args); + var et = this.instance.template.getEventTemplateByIndex(index); + this._emitArgs(et.name, args); + this.instance._emitResourceEvent(null, null, et.name, args); } _invoke(index, args) { if (this.destroyed) throw new Exception("Trying to access destroyed object"); - if (index >= this._p.template.functions.length) + if (index >= this.instance.template.functions.length) throw new Exception("Function index is incorrect"); + return this._p.connection.sendInvoke(this._p.instanceId, index, args); + + /* var reply = new AsyncReply(); var parameters = Codec.composeVarArray(args, this._p.connection, true); @@ -135,7 +155,7 @@ class DistributedResource extends IResource return reply; - + */ } @@ -147,12 +167,11 @@ class DistributedResource extends IResource } - _updatePropertyByIndex(index, value) { - var pt = this._p.template.getPropertyTemplateByIndex(index); + var pt = this.instance.template.getPropertyTemplateByIndex(index); this._p.properties[index] = value; - this.instance.modified(pt.name, value); + this.instance.emitModification(pt, value); } _set(index, value) @@ -165,15 +184,15 @@ class DistributedResource extends IResource var parameters = Codec.compose(value, this._p.connection); var self = this; - this._p.connection.sendRequest(IIPPacketAction.SetProperty, - BL().addUint32(self._p.instanceId).addUint8(index).addUint8Array(parameters)) + this._p.connection.sendRequest(IIPPacketAction.SetProperty) + .addUint32(self._p.instanceId).addUint8(index).addUint8Array(parameters) + .done() .then(function(res) { // not really needed, server will always send property modified, this only happens if the programmer forgot to emit in property setter - //Update(index, value); + self._p.properties[index] = value; reply.trigger(null); - // nothing to do here - }); + }); return reply; } diff --git a/src/DistributedResourceQueueItem.js b/src/DistributedResourceQueueItem.js index 5d7b922..90fe38e 100644 --- a/src/DistributedResourceQueueItem.js +++ b/src/DistributedResourceQueueItem.js @@ -23,7 +23,10 @@ /** * Created by Ahmed Zamil on 25/07/2017. */ -var DistributedResourceQueueItemType = + +"use strict"; + +const DistributedResourceQueueItemType = { Propery: 0, Event: 1 diff --git a/src/EventTemplate.js b/src/EventTemplate.js index c31c3dc..55873c7 100644 --- a/src/EventTemplate.js +++ b/src/EventTemplate.js @@ -24,6 +24,8 @@ * Created by Ahmed Zamil on 24/08/2017. */ +"use strict"; + class EventTemplate extends MemberTemplate { diff --git a/src/FunctionTemplate.js b/src/FunctionTemplate.js index 124203f..341715b 100644 --- a/src/FunctionTemplate.js +++ b/src/FunctionTemplate.js @@ -24,6 +24,8 @@ * Created by Ahmed Zamil on 27/08/2017. */ +"use strict"; + class FunctionTemplate extends MemberTemplate { compose() { var name = super.compose(); @@ -32,12 +34,12 @@ class FunctionTemplate extends MemberTemplate { if (this.expansion != null) { var exp = DC.stringToBytes(this.expansion); - return rt.addUint8(0x10 | (IsVoid ? 0x8 : 0x0)) + return rt.addUint8(0x10 | (this.isVoid ? 0x8 : 0x0)) .addUint32(exp.length).addUint8Array(exp) .addUint8(name.length).addUint8Array(name).toArray(); } else - return rt.addUint8(IsVoid ? 0x8 : 0x0).addUint8(name.length).addUint8Array(name).toArray(); + return rt.addUint8(this.isVoid ? 0x8 : 0x0).addUint8(name.length).addUint8Array(name).toArray(); } diff --git a/src/Guid.js b/src/Guid.js index 5104fac..9f76df8 100644 --- a/src/Guid.js +++ b/src/Guid.js @@ -23,6 +23,9 @@ /** * Created by Ahmed Zamil on 02/09/2017. */ + +"use strict"; + class Guid { constructor(dc) diff --git a/src/IDestructible.js b/src/IDestructible.js index 58a46da..54aad09 100644 --- a/src/IDestructible.js +++ b/src/IDestructible.js @@ -23,6 +23,9 @@ /** * Created by Ahmed Zamil on 31/08/2017. */ + +"use strict"; + class IDestructible extends IEventHandler { destroy() diff --git a/src/IEventHandler.js b/src/IEventHandler.js index 2379caf..06ce833 100644 --- a/src/IEventHandler.js +++ b/src/IEventHandler.js @@ -23,6 +23,7 @@ /** * Created by Ahmed Zamil on 30/08/2017. */ +"use strict"; class IEventHandler { @@ -42,19 +43,32 @@ class IEventHandler var args = Array.prototype.slice.call(arguments, 1); if (this._events[event]) for(var i = 0; i < this._events[event].length; i++) - if (this._events[event][i].apply(this, args)) + if (this._events[event][i].f.apply(this._events[event][i].i, args)) return true; return false; } - on(event, fn) + _emitArgs(event, args) { + event = event.toLowerCase(); + if (this._events[event]) + for(var i = 0; i < this._events[event].length; i++) + if (this._events[event][i].f.apply(this._events[event][i].i, args)) + return true; + return this; + } + + on(event, fn, issuer) + { + if (!(fn instanceof Function)) + return this; + event = event.toLowerCase(); // add if (!this._events[event]) this._events[event] = []; - this._events[event].push(fn); + this._events[event].push({f: fn, i: issuer == null ? this: issuer}); return this; } @@ -65,9 +79,13 @@ class IEventHandler { if (fn) { - var index = this._events[event].indexOf(fn); - if (index > -1) - this._events[event].splice(index, 1); + for(var i = 0; i < this._events[event].length; i++) + if (this._events[event][i].f == fn) + this._events[event].splice(i--, 1); + + //var index = this._events[event].indexOf(fn); + //if (index > -1) + //this._events[event].splice(index, 1); } else { diff --git a/src/IIPAuthPacket.js b/src/IIPAuthPacket.js index 8e2d7e0..cc0b164 100644 --- a/src/IIPAuthPacket.js +++ b/src/IIPAuthPacket.js @@ -23,7 +23,10 @@ /** * Created by Ahmed Zamil on 25/07/2017. */ -var IIPAuthPacketCommand = + +"use strict"; + +const IIPAuthPacketCommand = { Action: 0, Declare: 1, @@ -31,7 +34,7 @@ var IIPAuthPacketCommand = Error: 3 }; -var IIPAuthPacketAction = +const IIPAuthPacketAction = { // Authenticate AuthenticateHash: 0, @@ -41,7 +44,7 @@ var IIPAuthPacketAction = }; -var IIPAuthPacketMethod = +const IIPAuthPacketMethod = { None: 0, Certificate: 1, diff --git a/src/IIPPacket.js b/src/IIPPacket.js index 40a6a67..399fb9e 100644 --- a/src/IIPPacket.js +++ b/src/IIPPacket.js @@ -24,27 +24,43 @@ * Created by Ahmed Zamil on 25/07/2017. */ +"use strict"; -var IIPPacketCommand = +const IIPPacketCommand = { Event: 0, Request: 1, Reply: 2, - Error: 3 + Report: 3 }; -var IIPPacketEvent = +const IIPPacketReport = +{ + ManagementError: 0, + ExecutionError: 1, + ProgressReport: 0x8, + ChunkStream: 0x9 +}; + +const IIPPacketEvent = { // Event Manage ResourceReassigned : 0, ResourceDestroyed: 1, + ChildAdded: 2, + ChildRemoved: 3, + Renamed: 4, // Event Invoke PropertyUpdated : 0x10, - EventOccured: 0x11 + EventOccurred: 0x11, + + // Attribute + AttributesUpdated: 0x18 + }; -var IIPPacketAction = +const IIPPacketAction = { // Request Manage AttachResource: 0, @@ -52,25 +68,35 @@ var IIPPacketAction = DetachResource: 2, CreateResource: 3, DeleteResource: 4, + AddChild: 5, + RemoveChild: 6, + RenameResource: 7, // Request Inquire - TemplateFromClassName: 0x8, - TemplateFromClassId: 0x9, - TemplateFromResourceLink: 0xA, - TemplateFromResourceId: 0xB, - ResourceIdFromResourceLink: 0xC, + TemplateFromClassName: 8, + TemplateFromClassId: 9, + TemplateFromResourceId: 10, + QueryLink: 11, + ResourceHistory: 12, + ResourceChildren: 13, + ResourceParents: 14, // Request Invoke - InvokeFunction: 0x10, - GetProperty: 0x11, - GetPropertyIfModified: 0x12, - SetProperty: 0x13 + InvokeFunction: 16, + GetProperty: 17, + GetPropertyIfModified: 18, + SetProperty: 19, + + // Request Attribute + GetAllAttributes: 24, + UpdateAllAttributes: 25, + ClearAllAttributes: 26, + GetAttributes: 27, + UpdateAttributes: 28, + ClearAttributes: 29 }; - - - class IIPPacket { constructor() @@ -91,13 +117,14 @@ class IIPPacket this.methodName = ""; this.callbackId = 0; this.dataLengthNeeded = 0; + this.originalOffset = 0; } notEnough(offset, ends, needed) { if (offset + needed > ends) { - this.dataLengthNeeded = needed - (ends - offset); + this.dataLengthNeeded = needed - (ends - this.originalOffset); return true; } else @@ -106,7 +133,7 @@ class IIPPacket parse(data, offset, ends) { - var oOffset = offset; + this.originalOffset = offset; if (this.notEnough(offset, ends, 1)) return -this.dataLengthNeeded; @@ -123,6 +150,16 @@ class IIPPacket this.resourceId = data.getUint32(offset); offset += 4; } + else if (this.command == IIPPacketCommand.Report) + { + this.report = (data.getUint8(offset++) & 0x3f); + + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + this.callbackId = data.getUint32(offset); + offset += 4; + } else { this.action = (data.getUint8(offset++) & 0x3f); @@ -149,6 +186,30 @@ class IIPPacket { // nothing to parse } + else if (this.event == IIPPacketEvent.ChildAdded + || this.event == IIPPacketEvent.ChildRemoved) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + this.childId = data.getUint32(offset); + offset += 4; + } + else if(this.event == IIPPacketEvent.Renamed) + { + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; + + var cl = data.getUint16(offset); + offset += 2; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset, cl); + + offset += cl; + } else if (this.event == IIPPacketEvent.PropertyUpdated) { if (this.notEnough(offset, ends, 2)) @@ -182,7 +243,7 @@ class IIPPacket offset += size; } } - else if (this.event == IIPPacketEvent.EventOccured) + else if (this.event == IIPPacketEvent.EventOccurred) { if (this.notEnough(offset, ends, 5)) return -this.dataLengthNeeded; @@ -195,6 +256,22 @@ class IIPPacket this.content = data.clip(offset, cl); offset += cl; } + // Attribute + else if (this.event == IIPPacketEvent.AttributesUpdated) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + var cl = data.getUint32(offset); + offset += 4; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset, cl); + + offset += cl; + } } else if (this.command == IIPPacketCommand.Request) { @@ -208,14 +285,14 @@ class IIPPacket } else if (this.action == IIPPacketAction.ReattachResource) { - if (this.notEnough(offset, ends, 8)) + if (this.notEnough(offset, ends, 12)) return -this.dataLengthNeeded; this.resourceId = data.getUint32(offset); offset += 4; - this.resourceAge = data.getUint32(offset); - offset += 4; + this.resourceAge = data.getUint64(offset); + offset += 8; } else if (this.action == IIPPacketAction.DetachResource) { @@ -228,16 +305,21 @@ class IIPPacket } else if (this.action == IIPPacketAction.CreateResource) { - if (this.notEnough(offset, ends, 1)) - return -this.dataLengthNeeded; + if (this.notEnough(offset, ends, 12)) + return -dataLengthNeeded; - var cl = data.getUint8(offset++); + this.storeId = data.getUint32(offset); + offset += 4; + this.resourceId = data.getUint32(offset); + offset += 4; + + var cl = data.getUint32(offset); + offset += 4; if (this.notEnough(offset, ends, cl)) - return -this.dataLengthNeeded; + return -dataLengthNeeded; - this.className = data.getString(offset, cl); - offset += cl; + this.content = data.clip(offset, cl); } else if (this.action == IIPPacketAction.DeleteResource) { @@ -247,6 +329,37 @@ class IIPPacket this.resourceId = data.getUint32(offset); offset += 4; + } + else if (this.action == IIPPacketAction.AddChild + || this.action == IIPPacketAction.RemoveChild) + { + if (this.notEnough(offset, ends, 8)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + this.childId = data.getUint32(offset); + offset += 4; + + } + else if (this.action == IIPPacketAction.RenameResource) + { + if (this.notEnough(offset, ends, 6)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + var cl = data.getUint16(offset); + offset += 2; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset, cl); + offset += cl; + } else if (this.action == IIPPacketAction.TemplateFromClassName) { @@ -270,20 +383,6 @@ class IIPPacket this.classId = data.getGuid(offset); offset += 16; } - else if (this.action == IIPPacketAction.TemplateFromResourceLink) - { - if (this.notEnough(offset, ends, 2)) - return -this.dataLengthNeeded; - - var cl = data.getUint16(offset); - offset += 2; - - if (this.notEnough(offset, ends, cl)) - return -this.dataLengthNeeded; - - this.resourceLink = data.getString(offset, cl); - offset += cl; - } else if (this.action == IIPPacketAction.TemplateFromResourceId) { if (this.notEnough(offset, ends, 4)) @@ -292,7 +391,7 @@ class IIPPacket this.resourceId = data.getUint32(offset); offset += 4; } - else if (this.action == IIPPacketAction.ResourceIdFromResourceLink) + else if (this.action == IIPPacketAction.QueryLink) { if (this.notEnough(offset, ends, 2)) return -this.dataLengthNeeded; @@ -306,6 +405,30 @@ class IIPPacket this.resourceLink = data.getString(offset, cl); offset += cl; } + else if (this.action == IIPPacketAction.ResourceChildren + || this.action == IIPPacketAction.ResourceParents) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + } + else if (this.action == IIPPacketAction.ResourceHistory) + { + if (this.notEnough(offset, ends, 20)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + + this.fromDate = data.getDateTime(offset); + offset += 8; + + this.toDate = data.getDateTime(offset); + offset += 8; + + } else if (this.action == IIPPacketAction.InvokeFunction) { if (this.notEnough(offset, ends, 9)) @@ -347,8 +470,8 @@ class IIPPacket this.methodIndex = data[offset++]; - this.resourceAge = data.getUint32(offset); - offset += 4; + this.resourceAge = data.getUint64(offset); + offset += 8; } else if (this.action == IIPPacketAction.SetProperty) @@ -388,6 +511,28 @@ class IIPPacket offset += size; } } + + // Attribute + else if (this.action == IIPPacketAction.UpdateAllAttributes + || this.action == IIPPacketAction.GetAttributes + || this.action == IIPPacketAction.UpdateAttributes + || this.action == IIPPacketAction.ClearAttributes) + { + if (this.notEnough(offset, ends, 8)) + return -this.dataLengthNeeded; + + this.resourceId = data.getUint32(offset); + offset += 4; + var cl = data.getUint32(offset); + offset += 4; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset, cl); + offset += cl; + } + } else if (this.command == IIPPacketCommand.Reply) { @@ -400,8 +545,8 @@ class IIPPacket this.classId = data.getGuid(offset); offset += 16; - this.resourceAge = data.getUint32(offset); - offset += 4; + this.resourceAge = data.getUint64(offset); + offset += 8; var cl = data.getUint16(offset); offset+=2; @@ -433,9 +578,6 @@ class IIPPacket if (this.notEnough(offset, ends, 20)) return -this.dataLengthNeeded; - this.classId = data.GetGuid(offset); - offset += 16; - this.resourceId = data.getUint32(offset); offset += 4; @@ -446,8 +588,14 @@ class IIPPacket } else if (this.action == IIPPacketAction.TemplateFromClassName || this.action == IIPPacketAction.TemplateFromClassId - || this.action == IIPPacketAction.TemplateFromResourceLink - || this.action == IIPPacketAction.TemplateFromResourceId) + || this.action == IIPPacketAction.TemplateFromResourceId + || this.action == IIPPacketAction.QueryLink + || this.action == IIPPacketAction.ResourceChildren + || this.action == IIPPacketAction.ResourceParents + || this.action == IIPPacketAction.ResourceHistory + // Attribute + || this.action == IIPPacketAction.GetAllAttributes + || this.action == IIPPacketAction.GetAttributes) { if (this.notEnough(offset, ends, 4)) return -this.dataLengthNeeded; @@ -461,20 +609,6 @@ class IIPPacket this.content = data.clip(offset, cl); offset += cl; } - else if (this.action == IIPPacketAction.ResourceIdFromResourceLink) - { - if (this.notEnough(offset, ends, 24)) - return -this.dataLengthNeeded; - - this.classId = data.getGuid(offset); - offset += 16; - - this.resourceId = data.getUint32(offset); - offset += 4; - - this.resourceAge = data.getUint32(offset); - offset += 4; - } else if (this.action == IIPPacketAction.InvokeFunction || this.action == IIPPacketAction.GetProperty || this.action == IIPPacketAction.GetPropertyIfModified) @@ -513,32 +647,76 @@ class IIPPacket // nothing to do } } - else if (this.command == IIPPacketCommand.Error) + else if (this.command == IIPPacketCommand.Report) { - // Error - if (this.notEnough(offset, ends, 4)) - return -this.dataLengthNeeded; + if (this.report == IIPPacketReport.ManagementError) + { + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; - this.callbackId = data.getUint32(offset); + this.errorCode = data.getUint16(offset); + offset += 2; + } + else if (this.report == IIPPacketReport.ExecutionError) + { + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; - if (this.notEnough(offset, ends, 1)) - return -this.dataLengthNeeded; + this.errorCode = data.getUint16(offset); + offset += 2; - this.errorCode = data.getUint8(offset++); + if (this.notEnough(offset, ends, 2)) + return -this.dataLengthNeeded; - if (this.notEnough(offset, ends, 4)) - return -this.dataLengthNeeded; + var cl = data.getUint16(offset); + offset += 2; - var cl = data.getUint32(offset); - offset += 4; + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; - if (this.notEnough(offset, ends, cl)) - return -this.dataLengthNeeded; + this.errorMessage = data.getString(offset, cl); + offset += cl; + } + else if (this.report == IIPPacketReport.ProgressReport) + { + if (this.notEnough(offset, ends, 8)) + return -this.dataLengthNeeded; - this.errorMessage = data.getString(offset, cl); - offset += cl; + this.progressValue = data.getInt32(offset); + offset += 4; + this.progressMax = data.getInt32(offset); + offset += 4; + } + else if (this.report == IIPPacketReport.ChunkStream) + { + var dt = data.getUint8(offset++); + var size = DataType.sizeOf(dt); + + if (size < 0) + { + if (this.notEnough(offset, ends, 4)) + return -this.dataLengthNeeded; + + var cl = data.getUint32(offset); + offset += 4; + + if (this.notEnough(offset, ends, cl)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset - 5, cl + 5); + offset += cl; + } + else + { + if (this.notEnough(offset, ends, size)) + return -this.dataLengthNeeded; + + this.content = data.clip(offset - 1, size + 1); + offset += size; + } + } } - return offset - oOffset; + return offset - this.originalOffset; } } \ No newline at end of file diff --git a/src/IPermissionsManager.js b/src/IPermissionsManager.js new file mode 100644 index 0000000..8097811 --- /dev/null +++ b/src/IPermissionsManager.js @@ -0,0 +1,77 @@ +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 16/11/2017. + */ + +"use strict"; + +const ActionType = +{ + Attach: 0, + Delete: 1, + Execute: 2, + GetProperty: 3, + SetProperty: 4, + CreateResource: 5, + UpdateAttributes: 6, + InquireAttributes: 7, + AddParent: 8, + RemoveParent: 9, + AddChild: 10, + RemoveChild: 11, + Rename: 12, + ReceiveEvent: 13 +}; + +const Ruling = { + Denied: 0, + Allowed: 1, + DontCare: 2, +}; + +class IPermissionsManager +{ + /// + /// Check for permission. + /// + /// IResource. + /// Caller sessions. + /// Action type + /// Function or property to check for permission. + /// Allowed or denined. + applicable(resource, session, action, member, inquirer) + { + + } + + initialize(settings, resource) + { + + } + + get settings() + { + + } +} \ No newline at end of file diff --git a/src/IResource.js b/src/IResource.js index bd009b0..820fbf1 100644 --- a/src/IResource.js +++ b/src/IResource.js @@ -24,7 +24,9 @@ * Created by Ahmed Zamil on 25/07/2017. */ -var ResourceTrigger = +"use strict"; + +const ResourceTrigger = { Loaded : 0, Initialize: 1, diff --git a/src/IStore.js b/src/IStore.js index 2b45765..0dc869c 100644 --- a/src/IStore.js +++ b/src/IStore.js @@ -24,6 +24,8 @@ * Created by Ahmed Zamil on 25/07/2017. */ +"use strict"; + class IStore extends IResource { get(path) { @@ -37,6 +39,21 @@ class IStore extends IResource { } + record(resource, propertyName, value, age, dateTime) + { + + } + + getRecord(resource, fromDate, toDate) + { + + } + + remove(resource) + { + + } + constructor() { super(); diff --git a/src/Instance.js b/src/Instance.js index 5233d85..40c3ab7 100644 --- a/src/Instance.js +++ b/src/Instance.js @@ -23,22 +23,77 @@ /** * Created by Ahmed Zamil on 29/08/2017. */ + +"use strict"; + class Instance extends IEventHandler { getAge(index) { - if (index < this.ages.Count) + if (index < this.ages.length) return this.ages[index]; else return 0; } + setAge(index, value) + { + if (index < this.ages.length) + { + this.ages[index] = value; + if (value > this.instanceAge) + this.instanceAge = value; + } + } + + getModificationDate(index) + { + if (index < this.modificationDates.length) + return this.modificationDates[index]; + else + return new Date(0); + } + + setModificationDate(index, value) + { + if (index < this.modificationDates.length) + { + this.modificationDates[index] = value; + + if (value > this.instanceModificationDate) + this.instanceModificationDate = value; + } + } + + loadProperty(name, age, modificationDate, value) + { + var pt = this.template.getPropertyTemplateByName(name); + + if (pt == null) + return false; + + this.resource[name] = value; + + this.setAge(pt.index, age); + this.setModificationDate(pt.index, modificationDate); + + return true; + } deserialize(properties) { - for(var i = 0; i < this.template.properties.length; i++) - this.resource[this.template.properties[i].name] = properties[i]; + + for (var i = 0; i < properties.length; i++) + { + var pt = this.template.GetPropertyTemplateByIndex(i); + if (pt != null) + { + var pv = properties[i]; + this.loadProperty(pt.name, pv.age, pv.date, pv.value); + } + } + return true; } @@ -47,8 +102,10 @@ class Instance extends IEventHandler var props = []; for (var i = 0; i < this.template.properties.length; i++) - props.push(this.resource[this.template.properties[i].name]); - + props.push(new PropertyValue(this.resource[this.template.properties[i].name], + this.ages[this.template.properties[i].index], + this.modificationDates[this.template.properties[i].index])); + return props; } @@ -57,25 +114,38 @@ class Instance extends IEventHandler return resource instanceof Storable; } + emitModification(pt, value) + { + this.instanceAge++; - modified(propertyName = null, newValue = null, oldValue = null) + var now = new Date(); + + this.ages[pt.index] = this.instanceAge; + this.modificationDates[pt.index] = now; + + if (pt.recordable) + this.store.record(this.resource, pt.name, value, this.ages[pt.index], now); + + super._emit("ResourceModified", this.resource, pt.name, value); + this.resource._emit("modified", pt.name, value); + } + + modified(propertyName = null) { if (propertyName == null) propertyName = modified.caller.name; - if (newValue == null) + var val = {}; + if (this.getPropertyValue(propertyName, val)) { - var val = {}; - if (this.getPropertyValue(propertyName, val)) - super._emit("ResourceModified", this.resource, propertyName, val.value, oldValue); + var pt = this.template.getPropertyTemplateByName(propertyName); + this.emitModification(pt, val.value) } - else - super._emit("ResourceModified", this.resource, propertyName, newValue, oldValue); } - emitResourceEvent(name, receivers, args) + _emitResourceEvent(issuer, receivers, name, args) { - super._emit("ResourceEventOccured", this.resource, name, receivers, args); + super._emit("ResourceEventOccurred", this.resource, issuer, receivers, name, args); } getPropertyValue(name, resultObject) @@ -92,7 +162,7 @@ class Instance extends IEventHandler - constructor(id, name, resource, store) + constructor(id, name, resource, store, customTemplate = null, age = 0) { super(); @@ -101,8 +171,14 @@ class Instance extends IEventHandler this.id = id; this.name = name; + this.instanceAge = age; + this.instanceModificationDate = new Date(0); + this.children = new AutoList(); this.parents = new AutoList(); + this.managers = new AutoList(); + + this.attributes = new KeyList(); var self = this; @@ -119,24 +195,157 @@ class Instance extends IEventHandler self._emit("ResourceDestroyed", sender); }); - this.template = Warehouse.getTemplateByType(this.resource.constructor); + if (customTemplate != null) + this.template = customTemplate; + else + this.template = Warehouse.getTemplateByType(this.resource.constructor); // set ages - this.ages = new Uint32Array(this.template.properties.length); + this.ages = []; + this.modificationDates = []; + for(var i = 0; i < this.template.properties.length; i++) + { + this.ages.push(0); + this.modificationDates.push(new Date(0)); + } // connect events - var makeHandler = function(name, receivers, args) - { - return new function(receivers, args) - { - self.emitResourceEvent(name, receivers, args); - }; - }; - for (var i = 0; i < this.template.events.length; i++) - this.resource.on(this.template.events[i].name, makeHandler(this.template.events[i].name)); - + this.resource.on(this.template.events[i].name, this._makeHandler(this.template.events[i].name)); + } -} + _makeHandler(name) + { + var self = this; + return function(args) + { + if (args instanceof CustomResourceEvent) + self._emitResourceEvent(args.issuer, args.receivers, name, args.params); + else + self._emitResourceEvent(null, null, name, args); + }; + } + + + /// + /// Check for permission. + /// + /// Caller sessions. + /// Action type + /// Function or property to check for permission. + /// Ruling. + applicable(session, action, member, inquirer) + { + for (var i = 0; i < this.managers.length; i++) + { + var r = this.managers.item(i).applicable(this.resource, session, action, member, inquirer); + if (r != Ruling.DontCare) + return r; + } + + return Ruling.DontCare; + } + + + + removeAttributes(attributes = null) + { + if (attributes == null) + this.attributes.clear(); + else + { + for(var i = 0; i < attributes.length; i++) + this.attributes.remove(attributes[i]); + } + + return true; + } + + getAttributes(attributes = null) + { + var st = new Structure(); + + if (attributes == null) + { + attributes = this.attributes.keys.slice(0); + attributes.push("managers"); + } + + for(var i = 0; i < attributes.length; i++) + { + var attr = attributes[i]; + + if (attr == "name") + st["name"] = this.name; + + else if (attr == "managers") + { + var mngrs = new StructureArray(); + + for(var j = 0; j < this.managers.length; j++) + { + var manager = this.managers.item(j); + var sm = new Structure(); + sm["type"] = manager.constructor.name; + sm["settings"] = manager.settings; + + mngrs.push(sm); + } + + st["managers"] = mngrs; + + } + else + st[attr] = this.attributes.item(attr); + } + + return st; + } + + + setAttributes(attributes, clearAttributes = false) + { + + if (clearAttributes) + this.attributes.clear(); + + + for (var attr in attributes) + if (attr == "name") + this.name = attributes[attr]; + else if (attr == "managers") + { + this.managers.clear(); + + var mngrs = attributes[attr]; + + for (var i = 0; i < mngrs.length; i++) + { + var mngr = mngrs[i]; + + var type = window[mngr]; + + var settings = mngr["settings"]; + + var manager = new (Function.prototype.bind.apply(type)); + + if (manager instanceof IPermissionsManager) + { + manager.initialize(settings, this.resource); + this.managers.add(manager); + } + else + return false; + } + } + else + { + this.attributes.set(attr, attributes[attr]); + } + + + return true; + } +} \ No newline at end of file diff --git a/src/KeyList.js b/src/KeyList.js new file mode 100644 index 0000000..688f037 --- /dev/null +++ b/src/KeyList.js @@ -0,0 +1,115 @@ +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 06/11/2017. + */ + +"use strict"; + +class KeyList + { + constructor() + { + this.keys = []; + this.values = []; + } + + at(index) + { + return this.values[index]; + } + + item(key) + { + for(var i = 0; i < this.keys.length; i++) + if (this.keys[i] == key) + return this.values[i]; + } + + get(key) + { + for(var i = 0; i < this.keys.length; i++) + if (this.keys[i] == key) + return this.values[i]; + } + + _item_destroyed(sender) + { + for(var i = 0; i < this.values.length; i++) + if (sender == this.values[i]) + { + this.removeAt(i); + break; + } + } + + add(key, value) + { + this.remove(key); + + if (value instanceof IDestructible) + value.on("destroy", this._item_destroyed, this); + + this.keys.push(key); + this.values.push(value); + } + + contains(key) + { + for(var i = 0; i < this.keys.length; i++) + if (this.keys[i] == key) + return true; + + return false; + } + + set(key, value) + { + this.remove(key); + this.add(key, value); + } + + remove(key) + { + for(var i = 0; i < this.keys.length; i++) + if (key == this.keys[i]) + { + this.removeAt(i); + break; + } + } + + removeAt(index) + { + if (this.values[index] instanceof IDestructible) + this.values[index].off("destroy", this._item_destroyed); + + this.keys.splice(index, 1); + this.values.splice(index, 1); + } + + get length() + { + return this.keys.length; + } + } \ No newline at end of file diff --git a/src/MemberTemplate.js b/src/MemberTemplate.js index e94bc21..9e030ba 100644 --- a/src/MemberTemplate.js +++ b/src/MemberTemplate.js @@ -24,8 +24,9 @@ * Created by Ahmed Zamil on 24/08/2017. */ +"use strict"; -var MemberType = { +const MemberType = { Function: 0, Property: 1, Event: 2 diff --git a/src/MemoryStore.js b/src/MemoryStore.js new file mode 100644 index 0000000..5f491a3 --- /dev/null +++ b/src/MemoryStore.js @@ -0,0 +1,75 @@ +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 12/11/2017. + */ + +"use strict"; + +class MemoryStore extends IStore + { + constructor() + { + super(); + this.resources = []; + } + + put(resource) + { + this.resources[resource.instance.id] = resource; + } + + retrive(id) + { + if (this.resources[resource.instance.id]) + return new AsyncReply(this.resources[resource.instance.id]); + else + return new AsyncReply(null); + } + + get(resource) + { + return new AsyncReply(null); + } + + link(resource) + { + if (resource.instance.store == this) + return this.instance.name + "/" + resource.instance.id; + } + + trigger(trigger) + { + return new AsyncReply(true); + } + + record(resource, propertyName, value, age, dateTime) + { + + } + + getRecord(resource, fromDate, toDate) + { + + } + } \ No newline at end of file diff --git a/src/NetworkBuffer.js b/src/NetworkBuffer.js index 18e75dd..b674110 100644 --- a/src/NetworkBuffer.js +++ b/src/NetworkBuffer.js @@ -24,6 +24,8 @@ * Created by Ahmed Zamil on 01/09/2017. */ +"use strict"; + class NetworkBuffer { constructor() { this.neededDataLength = 0; diff --git a/src/NotModified.js b/src/NotModified.js index 76bad0d..9599618 100644 --- a/src/NotModified.js +++ b/src/NotModified.js @@ -24,6 +24,8 @@ * Created by Ahmed Zamil on 26/08/2017. */ +"use strict"; + class NotModified { diff --git a/src/PropertyTemplate.js b/src/PropertyTemplate.js index b7cc79c..f63c4ce 100644 --- a/src/PropertyTemplate.js +++ b/src/PropertyTemplate.js @@ -24,13 +24,14 @@ * Created by Ahmed Zamil on 27/08/2017. */ -var PropertyPermission = { +"use strict"; + +const PropertyPermission = { Read: 1, Write: 2, ReadWrite: 3 }; - class PropertyTemplate extends MemberTemplate { @@ -44,12 +45,13 @@ class PropertyTemplate extends MemberTemplate { var name = super.compose(); var rt = new BinaryList(); + var pv = (this.permission >> 1) | (this.recordable ? 1 : 0); if (this.writeExpansion != null && this.readExpansion != null) { var rexp = DC.stringToBytes(this.readExpansion); var wexp = DC.stringToBytes(this.writeExpansion); - return rt.addUint8(0x38 | this.permission) + return rt.addUint8(0x38 | pv) .addUint32(wexp.length) .addUint8Array(wexp) .addUint32(rexp.length) @@ -60,7 +62,7 @@ class PropertyTemplate extends MemberTemplate else if (this.writeExpansion != null) { var wexp = DC.stringToBytes(this.writeExpansion); - return rt.addUint8(0x30 | this.permission) + return rt.addUint8(0x30 | pv) .addUint32(wexp.length) .addUint8Array(wexp) .addUint8(name.length) @@ -69,14 +71,14 @@ class PropertyTemplate extends MemberTemplate else if (this.readExpansion != null) { var rexp = DC.stringToBytes(this.readExpansion); - return rt.addUint8(0x28 | this.permission) + return rt.addUint8(0x28 | pv) .addUint32(rexp.length) .addUint8Array(rexp) .addUint8(name.length) .addUint8Array(name).toArray(); } else - return rt.addUint8(0x20 | this.permission) + return rt.addUint8(0x20 | pv) .addUint32(name.length) .addUint8Array(name).toArray(); } diff --git a/src/PropertyValue.js b/src/PropertyValue.js new file mode 100644 index 0000000..ffc0bb9 --- /dev/null +++ b/src/PropertyValue.js @@ -0,0 +1,37 @@ +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 06/11/2017. + */ + +"use strict"; + +class PropertyValue + { + constructor(value, age, date) + { + this.value = value; + this.age = age; + this.date = date; + } + } \ No newline at end of file diff --git a/src/ResourceArray.js b/src/ResourceArray.js index fc358a9..79818f8 100644 --- a/src/ResourceArray.js +++ b/src/ResourceArray.js @@ -24,6 +24,8 @@ * Created by Ahmed Zamil on 26/08/2017. */ +"use strict"; + class ResourceArray extends Array { push(value) diff --git a/src/ResourceTemplate.js b/src/ResourceTemplate.js index c3e1344..fb13a06 100644 --- a/src/ResourceTemplate.js +++ b/src/ResourceTemplate.js @@ -20,18 +20,9 @@ * SOFTWARE. */ -class ResourceTemplate { +"use strict"; - getMemberTemplate(member) { - if (member instanceof MethodInfo) - return this.getFunctionTemplate(member.name); - else if (member instanceof EventInfo) - return this.getEventTemplate(member.name); - else if (member instanceof PropertyInfo) - return this.getPropertyTemplate(member.name); - else - return null; - } +class ResourceTemplate { getEventTemplateByName(eventName) { for (var i = 0; i < this.events.length; i++) @@ -107,7 +98,7 @@ class ResourceTemplate { // set guid this.className = template.namespace + "." + type.prototype.constructor.name; - this.classId = (new DC(sha256.arrayBuffer(this.className))).getGuid(0); + this.classId = SHA256.compute(DC.stringToBytes(this.className)).getGuid(0); //byte currentIndex = 0; @@ -117,6 +108,7 @@ class ResourceTemplate { pt.index = i; pt.readExpansion = template.properties[i].read; pt.writeExpansion = template.properties[i].write; + pt.recordable = template.properties[i].recordable; this.properties.push(pt); } @@ -224,6 +216,7 @@ class ResourceTemplate { pt.index = propertyIndex++; var readExpansion = ((data.getUint8(offset) & 0x8) == 0x8); var writeExpansion = ((data.getUint8(offset) & 0x10) == 0x10); + pt.recordable = ((data.getUint8(offset) & 1) == 1); pt.permission = ((data.getUint8(offset++) >> 1) & 0x3); pt.name = data.getString(offset + 1, data.getUint8(offset));// Encoding.ASCII.getString(data, (int)offset + 1, data.getUint8(offset)); offset += data.getUint8(offset) + 1; diff --git a/src/SHA256.js b/src/SHA256.js new file mode 100644 index 0000000..8f2182c --- /dev/null +++ b/src/SHA256.js @@ -0,0 +1,177 @@ + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 25/12/2017. + * Ref: https://en.wikipedia.org/wiki/SHA-2 + */ + +class SHA256 +{ + + static RROT(n, d) + { + return (n >>> d)|(n << (32 - d)); + } + + static compute(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): + + const hash = new Uint32Array([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): + const k = new Uint32Array([ + 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 + var 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 Uint8Array(paddingLength); + paddingBytes[0] = 0x80; + + var data = new DC(BL().addUint8Array(msg).addUint8Array(paddingBytes).addUint64(L).toArray()); + + + + // 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 Uint32Array(64); + for(var i = 0; i < 16; i++) + w[i] = data.getInt32(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) ^ (w[i-15] >>> 3); + var s1 = SHA256.RROT(w[i-2], 17) ^ SHA256.RROT(w[i-2], 19) ^ (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); + var temp2 = S0 + maj; + + h = g; + g = f; + f = e; + e = (d + temp1) >>> 0; + d = c; + c = b; + b = a; + a = (temp1 + temp2) >>> 0; + } + + // Add the compressed chunk to the current hash value: + + hash[0] = (hash[0] + a) >>> 0; + hash[1] = (hash[1] + b) >>> 0; + hash[2] = (hash[2] + c) >>> 0; + hash[3] = (hash[3] + d) >>> 0; + hash[4] = (hash[4] + e) >>> 0; + hash[5] = (hash[5] + f) >>> 0; + hash[6] = (hash[6] + g) >>> 0; + hash[7] = (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(); + } +} \ No newline at end of file diff --git a/src/SendList.js b/src/SendList.js index 33a42ef..8e03c67 100644 --- a/src/SendList.js +++ b/src/SendList.js @@ -24,16 +24,20 @@ * Created by Ahmed Zamil on 02/09/2017. */ +"use strict"; + class SendList extends BinaryList { - constructor(connection) + constructor(connection, doneReply) { super(); this.connection = connection; + this.reply = doneReply; } done() { this.connection.send(this.toArray()); + return this.reply; } } \ No newline at end of file diff --git a/src/Session.js b/src/Session.js new file mode 100644 index 0000000..e204bdd --- /dev/null +++ b/src/Session.js @@ -0,0 +1,41 @@ + +/* +* 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. +*/ + +/** + * Created by Ahmed Zamil on 16/11/2017. + */ + +"use strict"; + +class Session +{ + constructor(localAuthentication, remoteAuthentication) + { + + this.localAuthentication = localAuthentication; + this.remoteAuthentication = remoteAuthentication; + this.id = null; + this.creation = null; + this.modification = null; + } +} diff --git a/src/Structure.js b/src/Structure.js index c059623..c8fddd4 100644 --- a/src/Structure.js +++ b/src/Structure.js @@ -24,6 +24,8 @@ * Created by Ahmed Zamil on 26/08/2017. */ +"use strict"; + class Structure { getKeys() { diff --git a/src/StructureArray.js b/src/StructureArray.js index 73e1c91..cc42145 100644 --- a/src/StructureArray.js +++ b/src/StructureArray.js @@ -24,6 +24,8 @@ * Created by Ahmed Zamil on 06/09/2017. */ +"use strict"; + class StructureArray extends Array { push(value) diff --git a/src/Warehouse.js b/src/Warehouse.js index e9532f1..2bb7b47 100644 --- a/src/Warehouse.js +++ b/src/Warehouse.js @@ -23,33 +23,112 @@ /** * Created by Ahmed Zamil on 25/07/2017. */ -var Warehouse = { - stores: [], - resources: {}, - resourceCounter: 0, - templates: {}, +"use strict"; - new(type, name, store = null, parent = null) + +class Warehouse +{ + static new(type, name, store = null, parent = null, manager = null) { var res = type(); - Warehouse.put(res, name, store, parent); + Warehouse.put(res, name, store, parent, null, 0, manager); return res; - }, + } -get: function(id) + static get(id) { - if (Warehouse.resources[id]) - return new AsyncReply(Warehouse.resources[id]); + if (Number.isInteger(id)) + { + //if (Warehouse.resources.contains(id)) + return new AsyncReply(Warehouse.resources.item(id)); + //else + // return null; + } else - return null; - }, + { + var p = id.split('/'); + var res = null; - put: function(resource, name, store, parent){ - resource.instance = new Instance(Warehouse.resourceCounter++, name, resource, store); + for(var s = 0; s < this.stores.length; s++) + { + var d = this.stores.at(s); + if (p[0] == d.instance.name) + { + var i = 1; + res = d; + while(p.length > i) + { + var si = i; + + for (var r = 0; r < res.instance.children.length; r++) + if (res.instance.children.item(r).instance.name == p[i]) + { + i++; + res = res.instance.children.item(r); + break; + } + + if (si == i) + // not found, ask the store + return d.get(id.substring(p[0].length + 1)); + } + + return new AsyncReply(res); + } + } + + return new AsyncReply(null); + } + } + + + static remove(resource) + { + + if (Warehouse.resources.contains(resource.instance.id)) + Warehouse.resources.remove(resource.instance.id); + else + return false; + + if (resource instanceof IStore) + { + Warehouse.stores.remove(resource); + + // remove all objects associated with the store + var toBeRemoved = null; + + for (var i = 0; i < Warehouse.resources.length; i++) + { + var o = Warehouse.resources.at(i); + if (o.instance.store == resource) + { + if (toBeRemoved == null) + toBeRemoved = []; + toBeRemoved.push(o); + } + } + + if (toBeRemoved != null) + for(var i = 0; i < toBeRemoved.length; i++) + Warehouse.remove(toBeRemoved[i]); + } + + if (resource.instance.store != null) + resource.instance.store.remove(resource); + resource.destroy(); + + return true; + } + + static put(resource, name, store, parent, customTemplate = null, age = 0, manager = null){ + resource.instance = new Instance(Warehouse.resourceCounter++, name, resource, store, customTemplate, age); //resource.instance.children.on("add", Warehouse._onChildrenAdd).on("remove", Warehouse._onChildrenRemove); //resource.instance.parents.on("add", Warehouse._onParentsAdd).on("remove", Warehouse._onParentsRemove); + if (manager != null) + resource.instance.managers.add(manager); + if (parent) { parent.instance.children.add(resource); @@ -61,69 +140,101 @@ get: function(id) } if (resource instanceof IStore) - Warehouse.stores.push(resource); + Warehouse.stores.add(resource); else store.put(resource); - Warehouse.resources[resource.instance.id] = resource; - }, + Warehouse.resources.add(resource.instance.id, resource); + } - _onParentsRemove: function(value) + static _onParentsRemove(value) { if (value.instance.children.contains(value)) value.instance.children.remove(value); - }, + } - _onParentsAdd: function(value) + static _onParentsAdd(value) { if (!value.instance.children.contains(value)) value.instance.children.add(value); - }, + } - _onChildrenRemove: function(value) + static _onChildrenRemove(value) { if (value.instance.parents.contains(value)) value.instance.parents.remove(value); - }, + } - _onChildrenAdd: function(value) + static _onChildrenAdd(value) { if (!value.instance.parents.contains(value)) value.instance.parents.add(value); - }, + } - putTemplate: function(template) + static putTemplate(template) { - if (Warehouse.templates[template.classId]) - Warehouse.templates[template.classId] = template; - }, + Warehouse.templates.add(template.classId.valueOf(), template); + } - getTemplateByType: function(type) + static getTemplateByType(type) { // loaded ? - for (var t in Warehouse.templates) - if (Warehouse.templates[t].className == typeof(type)) - return t; + for (var i = 0; i < Warehouse.templates.length; i++) + if (Warehouse.templates.at(i).className == typeof(type)) + return Warehouse.templates.at(i); var template = new ResourceTemplate(type); - Warehouse.templates[template.classId] = template; - + Warehouse.templates.add(template.classId.valueOf(), template); + return template; - }, - - getTemplateByClassId: function(classId) - { - if (Warehouse.templates[classId]) - return new AsyncReply(Warehouse.templates[classId]); - return null; - }, - - getTemplateByClassName: function(className) - { - for(var t in Warehouse.templates) - if (Warehouse.templates[t].className == className) - return new AsyncReply(t); - - return null; } -}; \ No newline at end of file + + static getTemplateByClassId(classId) + { + var template = Warehouse.templates.item(classId); + return new AsyncReply(template); + } + + static getTemplateByClassName(className) + { + for(var i = 0; i < Warehouse.templates.length; i++) + if (Warehouse.templates.at(i).className == className) + return new AsyncReply(Warehouse.templates.at(i)); + + return new AsyncReply(null); + } + + static _qureyIn(path, index, resources) + { + var rt = []; + + if (index == path.length - 1) + { + if (path[index] == "") + for(var i = 0; i < resources.length; i++) + rt.push(resources.at(i)); + else + for(var i = 0; i < resources.length; i++) + if (resources.at(i).instance.name == path[index]) + rt.push(resources.at(i)); + } + else + for(var i = 0; i < resources.length; i++) + if (resources.at(i).instance.name == path[index]) + rt = rt.concat(Warehouse._qureyIn(path, index+1, resources.at(i).instance.children)); + + return rt; + } + + static query(path) + { + var p = path.split('/'); + return new AsyncReply(Warehouse._qureyIn(p, 0, Warehouse.stores)); + } +} + +// Initialize +Warehouse.stores = new AutoList(); +Warehouse.resources = new KeyList(); +Warehouse.resourceCounter = 0; +Warehouse.templates = new KeyList(); diff --git a/test.js b/test.js new file mode 100644 index 0000000..e481471 --- /dev/null +++ b/test.js @@ -0,0 +1,30 @@ +const http = require('http'); + +const esiur = require('./build/esiur-debug-node.js'); + +var IIP_LINK = "ws://media.delta.iq:5001/iip/system/iip"; + +var connection = new esiur.DistributedConnection(IIP_LINK, "sawadland", "guest", "guest"); +esiur.Warehouse.put(connection, "remote"); + +connection.on("ready", function (d) { + console.log(d); + connection.get("vod/home").then(function(rt){ + + console.log("Home"); + rt.getMyList().then(x => x.map(y=>console.log(y.name))); +})}); + + +const hostname = '127.0.0.1'; +const port = 3000; + +const server = http.createServer((req, res) => { + res.statusCode = 200; + res.setHeader('Content-Type', 'text/plain'); + res.end('Hello World\n'); +}); + +server.listen(port, hostname, () => { + console.log(`Server running at http://${hostname}:${port}/`); +}); diff --git a/test/app.js b/test/app.js new file mode 100644 index 0000000..b08fcf3 --- /dev/null +++ b/test/app.js @@ -0,0 +1,77 @@ +"use strict"; + +var demo = null; + +class JSResource extends IResource +{ + static getTemplate() + { + return { + namespace: "JS", + properties: [{name: "message", recordable: true}], + functions: [{name: "send", void: true, expansion: null}], + events: [{name: "published", expansion: null}] + } + } + + constructor() + { + super(); + this.message = "hi"; + } + + send(message) + { + console.log(message); + } +} + + +function init() { + + + var local = new MemoryStore(); + + var con = new DistributedConnection("ws://localhost:5001/iip/system/iip", "localhost", "demo", "1234"); + Warehouse.put(con, "remote"); + Warehouse.put(local, "local"); + + Warehouse.put(new JSResource(), "js", local); + + + con.on("ready", async function (d) { + + // var list = await con.query("").task; + + // console.log(list); + // debugger; + + Warehouse.get("remote/db/my").then(async function(rt){ + console.log("Object received."); + demo = rt; + rt.on("LevelUp", function(v){ + console.log("LevelUp", v); + }); + + con.getRecord(rt, new Date(new Date() - 60000000), new Date()).then(function(h){ + console.log(h); + }); + + var l = await rt.Subtract(3).task; + console.log("Await", l); + + //rt.Stream(10).then(x=> console.log("finished S")) + // .chunk(x => console.log ("chunk", x)) + // .progress((x,y,z) => console.log("progress", x, y, z)); + + //rt.Enum(4).then(x=> console.log("Enum finished")) + // .chunk(x => console.log ("Enum chunk", x)) + // .progress((x,y,z) => console.log("progress", x, y, z)); + + + }); + }).on("error", function(sender, code, msg){ + console.log(sender, code, msg); + }); + +} \ No newline at end of file diff --git a/test/import.js b/test/import.js new file mode 100644 index 0000000..1ab2b54 --- /dev/null +++ b/test/import.js @@ -0,0 +1,5 @@ +"use strict"; + +export * from "../src/IEventHandler.js"; +export * from "../src/IDestructible.js"; +export * from "../src/IResource.js"; \ No newline at end of file diff --git a/examples/test-debug.html b/test/test-debug.html similarity index 100% rename from examples/test-debug.html rename to test/test-debug.html diff --git a/examples/test-plain.html b/test/test-plain.html similarity index 66% rename from examples/test-plain.html rename to test/test-plain.html index 8200ab9..b6c6a49 100644 --- a/examples/test-plain.html +++ b/test/test-plain.html @@ -1,10 +1,11 @@ - - + + + @@ -14,12 +15,19 @@ - + + + + + + + + @@ -37,34 +45,12 @@ - + + + + diff --git a/examples/test.html b/test/test.html similarity index 100% rename from examples/test.html rename to test/test.html diff --git a/tools/manager/css/font-awesome.css b/tools/manager/css/font-awesome.css new file mode 100644 index 0000000..ee906a8 --- /dev/null +++ b/tools/manager/css/font-awesome.css @@ -0,0 +1,2337 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */ +/* FONT PATH + * -------------------------- */ +@font-face { + font-family: 'FontAwesome'; + src: url('../fonts/fontawesome-webfont.eot?v=4.7.0'); + src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg'); + font-weight: normal; + font-style: normal; +} +.fa { + display: inline-block; + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +/* makes the font 33% larger relative to the icon container */ +.fa-lg { + font-size: 1.33333333em; + line-height: 0.75em; + vertical-align: -15%; +} +.fa-2x { + font-size: 2em; +} +.fa-3x { + font-size: 3em; +} +.fa-4x { + font-size: 4em; +} +.fa-5x { + font-size: 5em; +} +.fa-fw { + width: 1.28571429em; + text-align: center; +} +.fa-ul { + padding-left: 0; + margin-left: 2.14285714em; + list-style-type: none; +} +.fa-ul > li { + position: relative; +} +.fa-li { + position: absolute; + left: -2.14285714em; + width: 2.14285714em; + top: 0.14285714em; + text-align: center; +} +.fa-li.fa-lg { + left: -1.85714286em; +} +.fa-border { + padding: .2em .25em .15em; + border: solid 0.08em #eeeeee; + border-radius: .1em; +} +.fa-pull-left { + float: left; +} +.fa-pull-right { + float: right; +} +.fa.fa-pull-left { + margin-right: .3em; +} +.fa.fa-pull-right { + margin-left: .3em; +} +/* Deprecated as of 4.4.0 */ +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.fa.pull-left { + margin-right: .3em; +} +.fa.pull-right { + margin-left: .3em; +} +.fa-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear; +} +.fa-pulse { + -webkit-animation: fa-spin 1s infinite steps(8); + animation: fa-spin 1s infinite steps(8); +} +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +.fa-rotate-90 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)"; + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} +.fa-rotate-180 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)"; + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} +.fa-rotate-270 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)"; + -webkit-transform: rotate(270deg); + -ms-transform: rotate(270deg); + transform: rotate(270deg); +} +.fa-flip-horizontal { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)"; + -webkit-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.fa-flip-vertical { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"; + -webkit-transform: scale(1, -1); + -ms-transform: scale(1, -1); + transform: scale(1, -1); +} +:root .fa-rotate-90, +:root .fa-rotate-180, +:root .fa-rotate-270, +:root .fa-flip-horizontal, +:root .fa-flip-vertical { + filter: none; +} +.fa-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; +} +.fa-stack-1x, +.fa-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; +} +.fa-stack-1x { + line-height: inherit; +} +.fa-stack-2x { + font-size: 2em; +} +.fa-inverse { + color: #ffffff; +} +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ +.fa-glass:before { + content: "\f000"; +} +.fa-music:before { + content: "\f001"; +} +.fa-search:before { + content: "\f002"; +} +.fa-envelope-o:before { + content: "\f003"; +} +.fa-heart:before { + content: "\f004"; +} +.fa-star:before { + content: "\f005"; +} +.fa-star-o:before { + content: "\f006"; +} +.fa-user:before { + content: "\f007"; +} +.fa-film:before { + content: "\f008"; +} +.fa-th-large:before { + content: "\f009"; +} +.fa-th:before { + content: "\f00a"; +} +.fa-th-list:before { + content: "\f00b"; +} +.fa-check:before { + content: "\f00c"; +} +.fa-remove:before, +.fa-close:before, +.fa-times:before { + content: "\f00d"; +} +.fa-search-plus:before { + content: "\f00e"; +} +.fa-search-minus:before { + content: "\f010"; +} +.fa-power-off:before { + content: "\f011"; +} +.fa-signal:before { + content: "\f012"; +} +.fa-gear:before, +.fa-cog:before { + content: "\f013"; +} +.fa-trash-o:before { + content: "\f014"; +} +.fa-home:before { + content: "\f015"; +} +.fa-file-o:before { + content: "\f016"; +} +.fa-clock-o:before { + content: "\f017"; +} +.fa-road:before { + content: "\f018"; +} +.fa-download:before { + content: "\f019"; +} +.fa-arrow-circle-o-down:before { + content: "\f01a"; +} +.fa-arrow-circle-o-up:before { + content: "\f01b"; +} +.fa-inbox:before { + content: "\f01c"; +} +.fa-play-circle-o:before { + content: "\f01d"; +} +.fa-rotate-right:before, +.fa-repeat:before { + content: "\f01e"; +} +.fa-refresh:before { + content: "\f021"; +} +.fa-list-alt:before { + content: "\f022"; +} +.fa-lock:before { + content: "\f023"; +} +.fa-flag:before { + content: "\f024"; +} +.fa-headphones:before { + content: "\f025"; +} +.fa-volume-off:before { + content: "\f026"; +} +.fa-volume-down:before { + content: "\f027"; +} +.fa-volume-up:before { + content: "\f028"; +} +.fa-qrcode:before { + content: "\f029"; +} +.fa-barcode:before { + content: "\f02a"; +} +.fa-tag:before { + content: "\f02b"; +} +.fa-tags:before { + content: "\f02c"; +} +.fa-book:before { + content: "\f02d"; +} +.fa-bookmark:before { + content: "\f02e"; +} +.fa-print:before { + content: "\f02f"; +} +.fa-camera:before { + content: "\f030"; +} +.fa-font:before { + content: "\f031"; +} +.fa-bold:before { + content: "\f032"; +} +.fa-italic:before { + content: "\f033"; +} +.fa-text-height:before { + content: "\f034"; +} +.fa-text-width:before { + content: "\f035"; +} +.fa-align-left:before { + content: "\f036"; +} +.fa-align-center:before { + content: "\f037"; +} +.fa-align-right:before { + content: "\f038"; +} +.fa-align-justify:before { + content: "\f039"; +} +.fa-list:before { + content: "\f03a"; +} +.fa-dedent:before, +.fa-outdent:before { + content: "\f03b"; +} +.fa-indent:before { + content: "\f03c"; +} +.fa-video-camera:before { + content: "\f03d"; +} +.fa-photo:before, +.fa-image:before, +.fa-picture-o:before { + content: "\f03e"; +} +.fa-pencil:before { + content: "\f040"; +} +.fa-map-marker:before { + content: "\f041"; +} +.fa-adjust:before { + content: "\f042"; +} +.fa-tint:before { + content: "\f043"; +} +.fa-edit:before, +.fa-pencil-square-o:before { + content: "\f044"; +} +.fa-share-square-o:before { + content: "\f045"; +} +.fa-check-square-o:before { + content: "\f046"; +} +.fa-arrows:before { + content: "\f047"; +} +.fa-step-backward:before { + content: "\f048"; +} +.fa-fast-backward:before { + content: "\f049"; +} +.fa-backward:before { + content: "\f04a"; +} +.fa-play:before { + content: "\f04b"; +} +.fa-pause:before { + content: "\f04c"; +} +.fa-stop:before { + content: "\f04d"; +} +.fa-forward:before { + content: "\f04e"; +} +.fa-fast-forward:before { + content: "\f050"; +} +.fa-step-forward:before { + content: "\f051"; +} +.fa-eject:before { + content: "\f052"; +} +.fa-chevron-left:before { + content: "\f053"; +} +.fa-chevron-right:before { + content: "\f054"; +} +.fa-plus-circle:before { + content: "\f055"; +} +.fa-minus-circle:before { + content: "\f056"; +} +.fa-times-circle:before { + content: "\f057"; +} +.fa-check-circle:before { + content: "\f058"; +} +.fa-question-circle:before { + content: "\f059"; +} +.fa-info-circle:before { + content: "\f05a"; +} +.fa-crosshairs:before { + content: "\f05b"; +} +.fa-times-circle-o:before { + content: "\f05c"; +} +.fa-check-circle-o:before { + content: "\f05d"; +} +.fa-ban:before { + content: "\f05e"; +} +.fa-arrow-left:before { + content: "\f060"; +} +.fa-arrow-right:before { + content: "\f061"; +} +.fa-arrow-up:before { + content: "\f062"; +} +.fa-arrow-down:before { + content: "\f063"; +} +.fa-mail-forward:before, +.fa-share:before { + content: "\f064"; +} +.fa-expand:before { + content: "\f065"; +} +.fa-compress:before { + content: "\f066"; +} +.fa-plus:before { + content: "\f067"; +} +.fa-minus:before { + content: "\f068"; +} +.fa-asterisk:before { + content: "\f069"; +} +.fa-exclamation-circle:before { + content: "\f06a"; +} +.fa-gift:before { + content: "\f06b"; +} +.fa-leaf:before { + content: "\f06c"; +} +.fa-fire:before { + content: "\f06d"; +} +.fa-eye:before { + content: "\f06e"; +} +.fa-eye-slash:before { + content: "\f070"; +} +.fa-warning:before, +.fa-exclamation-triangle:before { + content: "\f071"; +} +.fa-plane:before { + content: "\f072"; +} +.fa-calendar:before { + content: "\f073"; +} +.fa-random:before { + content: "\f074"; +} +.fa-comment:before { + content: "\f075"; +} +.fa-magnet:before { + content: "\f076"; +} +.fa-chevron-up:before { + content: "\f077"; +} +.fa-chevron-down:before { + content: "\f078"; +} +.fa-retweet:before { + content: "\f079"; +} +.fa-shopping-cart:before { + content: "\f07a"; +} +.fa-folder:before { + content: "\f07b"; +} +.fa-folder-open:before { + content: "\f07c"; +} +.fa-arrows-v:before { + content: "\f07d"; +} +.fa-arrows-h:before { + content: "\f07e"; +} +.fa-bar-chart-o:before, +.fa-bar-chart:before { + content: "\f080"; +} +.fa-twitter-square:before { + content: "\f081"; +} +.fa-facebook-square:before { + content: "\f082"; +} +.fa-camera-retro:before { + content: "\f083"; +} +.fa-key:before { + content: "\f084"; +} +.fa-gears:before, +.fa-cogs:before { + content: "\f085"; +} +.fa-comments:before { + content: "\f086"; +} +.fa-thumbs-o-up:before { + content: "\f087"; +} +.fa-thumbs-o-down:before { + content: "\f088"; +} +.fa-star-half:before { + content: "\f089"; +} +.fa-heart-o:before { + content: "\f08a"; +} +.fa-sign-out:before { + content: "\f08b"; +} +.fa-linkedin-square:before { + content: "\f08c"; +} +.fa-thumb-tack:before { + content: "\f08d"; +} +.fa-external-link:before { + content: "\f08e"; +} +.fa-sign-in:before { + content: "\f090"; +} +.fa-trophy:before { + content: "\f091"; +} +.fa-github-square:before { + content: "\f092"; +} +.fa-upload:before { + content: "\f093"; +} +.fa-lemon-o:before { + content: "\f094"; +} +.fa-phone:before { + content: "\f095"; +} +.fa-square-o:before { + content: "\f096"; +} +.fa-bookmark-o:before { + content: "\f097"; +} +.fa-phone-square:before { + content: "\f098"; +} +.fa-twitter:before { + content: "\f099"; +} +.fa-facebook-f:before, +.fa-facebook:before { + content: "\f09a"; +} +.fa-github:before { + content: "\f09b"; +} +.fa-unlock:before { + content: "\f09c"; +} +.fa-credit-card:before { + content: "\f09d"; +} +.fa-feed:before, +.fa-rss:before { + content: "\f09e"; +} +.fa-hdd-o:before { + content: "\f0a0"; +} +.fa-bullhorn:before { + content: "\f0a1"; +} +.fa-bell:before { + content: "\f0f3"; +} +.fa-certificate:before { + content: "\f0a3"; +} +.fa-hand-o-right:before { + content: "\f0a4"; +} +.fa-hand-o-left:before { + content: "\f0a5"; +} +.fa-hand-o-up:before { + content: "\f0a6"; +} +.fa-hand-o-down:before { + content: "\f0a7"; +} +.fa-arrow-circle-left:before { + content: "\f0a8"; +} +.fa-arrow-circle-right:before { + content: "\f0a9"; +} +.fa-arrow-circle-up:before { + content: "\f0aa"; +} +.fa-arrow-circle-down:before { + content: "\f0ab"; +} +.fa-globe:before { + content: "\f0ac"; +} +.fa-wrench:before { + content: "\f0ad"; +} +.fa-tasks:before { + content: "\f0ae"; +} +.fa-filter:before { + content: "\f0b0"; +} +.fa-briefcase:before { + content: "\f0b1"; +} +.fa-arrows-alt:before { + content: "\f0b2"; +} +.fa-group:before, +.fa-users:before { + content: "\f0c0"; +} +.fa-chain:before, +.fa-link:before { + content: "\f0c1"; +} +.fa-cloud:before { + content: "\f0c2"; +} +.fa-flask:before { + content: "\f0c3"; +} +.fa-cut:before, +.fa-scissors:before { + content: "\f0c4"; +} +.fa-copy:before, +.fa-files-o:before { + content: "\f0c5"; +} +.fa-paperclip:before { + content: "\f0c6"; +} +.fa-save:before, +.fa-floppy-o:before { + content: "\f0c7"; +} +.fa-square:before { + content: "\f0c8"; +} +.fa-navicon:before, +.fa-reorder:before, +.fa-bars:before { + content: "\f0c9"; +} +.fa-list-ul:before { + content: "\f0ca"; +} +.fa-list-ol:before { + content: "\f0cb"; +} +.fa-strikethrough:before { + content: "\f0cc"; +} +.fa-underline:before { + content: "\f0cd"; +} +.fa-table:before { + content: "\f0ce"; +} +.fa-magic:before { + content: "\f0d0"; +} +.fa-truck:before { + content: "\f0d1"; +} +.fa-pinterest:before { + content: "\f0d2"; +} +.fa-pinterest-square:before { + content: "\f0d3"; +} +.fa-google-plus-square:before { + content: "\f0d4"; +} +.fa-google-plus:before { + content: "\f0d5"; +} +.fa-money:before { + content: "\f0d6"; +} +.fa-caret-down:before { + content: "\f0d7"; +} +.fa-caret-up:before { + content: "\f0d8"; +} +.fa-caret-left:before { + content: "\f0d9"; +} +.fa-caret-right:before { + content: "\f0da"; +} +.fa-columns:before { + content: "\f0db"; +} +.fa-unsorted:before, +.fa-sort:before { + content: "\f0dc"; +} +.fa-sort-down:before, +.fa-sort-desc:before { + content: "\f0dd"; +} +.fa-sort-up:before, +.fa-sort-asc:before { + content: "\f0de"; +} +.fa-envelope:before { + content: "\f0e0"; +} +.fa-linkedin:before { + content: "\f0e1"; +} +.fa-rotate-left:before, +.fa-undo:before { + content: "\f0e2"; +} +.fa-legal:before, +.fa-gavel:before { + content: "\f0e3"; +} +.fa-dashboard:before, +.fa-tachometer:before { + content: "\f0e4"; +} +.fa-comment-o:before { + content: "\f0e5"; +} +.fa-comments-o:before { + content: "\f0e6"; +} +.fa-flash:before, +.fa-bolt:before { + content: "\f0e7"; +} +.fa-sitemap:before { + content: "\f0e8"; +} +.fa-umbrella:before { + content: "\f0e9"; +} +.fa-paste:before, +.fa-clipboard:before { + content: "\f0ea"; +} +.fa-lightbulb-o:before { + content: "\f0eb"; +} +.fa-exchange:before { + content: "\f0ec"; +} +.fa-cloud-download:before { + content: "\f0ed"; +} +.fa-cloud-upload:before { + content: "\f0ee"; +} +.fa-user-md:before { + content: "\f0f0"; +} +.fa-stethoscope:before { + content: "\f0f1"; +} +.fa-suitcase:before { + content: "\f0f2"; +} +.fa-bell-o:before { + content: "\f0a2"; +} +.fa-coffee:before { + content: "\f0f4"; +} +.fa-cutlery:before { + content: "\f0f5"; +} +.fa-file-text-o:before { + content: "\f0f6"; +} +.fa-building-o:before { + content: "\f0f7"; +} +.fa-hospital-o:before { + content: "\f0f8"; +} +.fa-ambulance:before { + content: "\f0f9"; +} +.fa-medkit:before { + content: "\f0fa"; +} +.fa-fighter-jet:before { + content: "\f0fb"; +} +.fa-beer:before { + content: "\f0fc"; +} +.fa-h-square:before { + content: "\f0fd"; +} +.fa-plus-square:before { + content: "\f0fe"; +} +.fa-angle-double-left:before { + content: "\f100"; +} +.fa-angle-double-right:before { + content: "\f101"; +} +.fa-angle-double-up:before { + content: "\f102"; +} +.fa-angle-double-down:before { + content: "\f103"; +} +.fa-angle-left:before { + content: "\f104"; +} +.fa-angle-right:before { + content: "\f105"; +} +.fa-angle-up:before { + content: "\f106"; +} +.fa-angle-down:before { + content: "\f107"; +} +.fa-desktop:before { + content: "\f108"; +} +.fa-laptop:before { + content: "\f109"; +} +.fa-tablet:before { + content: "\f10a"; +} +.fa-mobile-phone:before, +.fa-mobile:before { + content: "\f10b"; +} +.fa-circle-o:before { + content: "\f10c"; +} +.fa-quote-left:before { + content: "\f10d"; +} +.fa-quote-right:before { + content: "\f10e"; +} +.fa-spinner:before { + content: "\f110"; +} +.fa-circle:before { + content: "\f111"; +} +.fa-mail-reply:before, +.fa-reply:before { + content: "\f112"; +} +.fa-github-alt:before { + content: "\f113"; +} +.fa-folder-o:before { + content: "\f114"; +} +.fa-folder-open-o:before { + content: "\f115"; +} +.fa-smile-o:before { + content: "\f118"; +} +.fa-frown-o:before { + content: "\f119"; +} +.fa-meh-o:before { + content: "\f11a"; +} +.fa-gamepad:before { + content: "\f11b"; +} +.fa-keyboard-o:before { + content: "\f11c"; +} +.fa-flag-o:before { + content: "\f11d"; +} +.fa-flag-checkered:before { + content: "\f11e"; +} +.fa-terminal:before { + content: "\f120"; +} +.fa-code:before { + content: "\f121"; +} +.fa-mail-reply-all:before, +.fa-reply-all:before { + content: "\f122"; +} +.fa-star-half-empty:before, +.fa-star-half-full:before, +.fa-star-half-o:before { + content: "\f123"; +} +.fa-location-arrow:before { + content: "\f124"; +} +.fa-crop:before { + content: "\f125"; +} +.fa-code-fork:before { + content: "\f126"; +} +.fa-unlink:before, +.fa-chain-broken:before { + content: "\f127"; +} +.fa-question:before { + content: "\f128"; +} +.fa-info:before { + content: "\f129"; +} +.fa-exclamation:before { + content: "\f12a"; +} +.fa-superscript:before { + content: "\f12b"; +} +.fa-subscript:before { + content: "\f12c"; +} +.fa-eraser:before { + content: "\f12d"; +} +.fa-puzzle-piece:before { + content: "\f12e"; +} +.fa-microphone:before { + content: "\f130"; +} +.fa-microphone-slash:before { + content: "\f131"; +} +.fa-shield:before { + content: "\f132"; +} +.fa-calendar-o:before { + content: "\f133"; +} +.fa-fire-extinguisher:before { + content: "\f134"; +} +.fa-rocket:before { + content: "\f135"; +} +.fa-maxcdn:before { + content: "\f136"; +} +.fa-chevron-circle-left:before { + content: "\f137"; +} +.fa-chevron-circle-right:before { + content: "\f138"; +} +.fa-chevron-circle-up:before { + content: "\f139"; +} +.fa-chevron-circle-down:before { + content: "\f13a"; +} +.fa-html5:before { + content: "\f13b"; +} +.fa-css3:before { + content: "\f13c"; +} +.fa-anchor:before { + content: "\f13d"; +} +.fa-unlock-alt:before { + content: "\f13e"; +} +.fa-bullseye:before { + content: "\f140"; +} +.fa-ellipsis-h:before { + content: "\f141"; +} +.fa-ellipsis-v:before { + content: "\f142"; +} +.fa-rss-square:before { + content: "\f143"; +} +.fa-play-circle:before { + content: "\f144"; +} +.fa-ticket:before { + content: "\f145"; +} +.fa-minus-square:before { + content: "\f146"; +} +.fa-minus-square-o:before { + content: "\f147"; +} +.fa-level-up:before { + content: "\f148"; +} +.fa-level-down:before { + content: "\f149"; +} +.fa-check-square:before { + content: "\f14a"; +} +.fa-pencil-square:before { + content: "\f14b"; +} +.fa-external-link-square:before { + content: "\f14c"; +} +.fa-share-square:before { + content: "\f14d"; +} +.fa-compass:before { + content: "\f14e"; +} +.fa-toggle-down:before, +.fa-caret-square-o-down:before { + content: "\f150"; +} +.fa-toggle-up:before, +.fa-caret-square-o-up:before { + content: "\f151"; +} +.fa-toggle-right:before, +.fa-caret-square-o-right:before { + content: "\f152"; +} +.fa-euro:before, +.fa-eur:before { + content: "\f153"; +} +.fa-gbp:before { + content: "\f154"; +} +.fa-dollar:before, +.fa-usd:before { + content: "\f155"; +} +.fa-rupee:before, +.fa-inr:before { + content: "\f156"; +} +.fa-cny:before, +.fa-rmb:before, +.fa-yen:before, +.fa-jpy:before { + content: "\f157"; +} +.fa-ruble:before, +.fa-rouble:before, +.fa-rub:before { + content: "\f158"; +} +.fa-won:before, +.fa-krw:before { + content: "\f159"; +} +.fa-bitcoin:before, +.fa-btc:before { + content: "\f15a"; +} +.fa-file:before { + content: "\f15b"; +} +.fa-file-text:before { + content: "\f15c"; +} +.fa-sort-alpha-asc:before { + content: "\f15d"; +} +.fa-sort-alpha-desc:before { + content: "\f15e"; +} +.fa-sort-amount-asc:before { + content: "\f160"; +} +.fa-sort-amount-desc:before { + content: "\f161"; +} +.fa-sort-numeric-asc:before { + content: "\f162"; +} +.fa-sort-numeric-desc:before { + content: "\f163"; +} +.fa-thumbs-up:before { + content: "\f164"; +} +.fa-thumbs-down:before { + content: "\f165"; +} +.fa-youtube-square:before { + content: "\f166"; +} +.fa-youtube:before { + content: "\f167"; +} +.fa-xing:before { + content: "\f168"; +} +.fa-xing-square:before { + content: "\f169"; +} +.fa-youtube-play:before { + content: "\f16a"; +} +.fa-dropbox:before { + content: "\f16b"; +} +.fa-stack-overflow:before { + content: "\f16c"; +} +.fa-instagram:before { + content: "\f16d"; +} +.fa-flickr:before { + content: "\f16e"; +} +.fa-adn:before { + content: "\f170"; +} +.fa-bitbucket:before { + content: "\f171"; +} +.fa-bitbucket-square:before { + content: "\f172"; +} +.fa-tumblr:before { + content: "\f173"; +} +.fa-tumblr-square:before { + content: "\f174"; +} +.fa-long-arrow-down:before { + content: "\f175"; +} +.fa-long-arrow-up:before { + content: "\f176"; +} +.fa-long-arrow-left:before { + content: "\f177"; +} +.fa-long-arrow-right:before { + content: "\f178"; +} +.fa-apple:before { + content: "\f179"; +} +.fa-windows:before { + content: "\f17a"; +} +.fa-android:before { + content: "\f17b"; +} +.fa-linux:before { + content: "\f17c"; +} +.fa-dribbble:before { + content: "\f17d"; +} +.fa-skype:before { + content: "\f17e"; +} +.fa-foursquare:before { + content: "\f180"; +} +.fa-trello:before { + content: "\f181"; +} +.fa-female:before { + content: "\f182"; +} +.fa-male:before { + content: "\f183"; +} +.fa-gittip:before, +.fa-gratipay:before { + content: "\f184"; +} +.fa-sun-o:before { + content: "\f185"; +} +.fa-moon-o:before { + content: "\f186"; +} +.fa-archive:before { + content: "\f187"; +} +.fa-bug:before { + content: "\f188"; +} +.fa-vk:before { + content: "\f189"; +} +.fa-weibo:before { + content: "\f18a"; +} +.fa-renren:before { + content: "\f18b"; +} +.fa-pagelines:before { + content: "\f18c"; +} +.fa-stack-exchange:before { + content: "\f18d"; +} +.fa-arrow-circle-o-right:before { + content: "\f18e"; +} +.fa-arrow-circle-o-left:before { + content: "\f190"; +} +.fa-toggle-left:before, +.fa-caret-square-o-left:before { + content: "\f191"; +} +.fa-dot-circle-o:before { + content: "\f192"; +} +.fa-wheelchair:before { + content: "\f193"; +} +.fa-vimeo-square:before { + content: "\f194"; +} +.fa-turkish-lira:before, +.fa-try:before { + content: "\f195"; +} +.fa-plus-square-o:before { + content: "\f196"; +} +.fa-space-shuttle:before { + content: "\f197"; +} +.fa-slack:before { + content: "\f198"; +} +.fa-envelope-square:before { + content: "\f199"; +} +.fa-wordpress:before { + content: "\f19a"; +} +.fa-openid:before { + content: "\f19b"; +} +.fa-institution:before, +.fa-bank:before, +.fa-university:before { + content: "\f19c"; +} +.fa-mortar-board:before, +.fa-graduation-cap:before { + content: "\f19d"; +} +.fa-yahoo:before { + content: "\f19e"; +} +.fa-google:before { + content: "\f1a0"; +} +.fa-reddit:before { + content: "\f1a1"; +} +.fa-reddit-square:before { + content: "\f1a2"; +} +.fa-stumbleupon-circle:before { + content: "\f1a3"; +} +.fa-stumbleupon:before { + content: "\f1a4"; +} +.fa-delicious:before { + content: "\f1a5"; +} +.fa-digg:before { + content: "\f1a6"; +} +.fa-pied-piper-pp:before { + content: "\f1a7"; +} +.fa-pied-piper-alt:before { + content: "\f1a8"; +} +.fa-drupal:before { + content: "\f1a9"; +} +.fa-joomla:before { + content: "\f1aa"; +} +.fa-language:before { + content: "\f1ab"; +} +.fa-fax:before { + content: "\f1ac"; +} +.fa-building:before { + content: "\f1ad"; +} +.fa-child:before { + content: "\f1ae"; +} +.fa-paw:before { + content: "\f1b0"; +} +.fa-spoon:before { + content: "\f1b1"; +} +.fa-cube:before { + content: "\f1b2"; +} +.fa-cubes:before { + content: "\f1b3"; +} +.fa-behance:before { + content: "\f1b4"; +} +.fa-behance-square:before { + content: "\f1b5"; +} +.fa-steam:before { + content: "\f1b6"; +} +.fa-steam-square:before { + content: "\f1b7"; +} +.fa-recycle:before { + content: "\f1b8"; +} +.fa-automobile:before, +.fa-car:before { + content: "\f1b9"; +} +.fa-cab:before, +.fa-taxi:before { + content: "\f1ba"; +} +.fa-tree:before { + content: "\f1bb"; +} +.fa-spotify:before { + content: "\f1bc"; +} +.fa-deviantart:before { + content: "\f1bd"; +} +.fa-soundcloud:before { + content: "\f1be"; +} +.fa-database:before { + content: "\f1c0"; +} +.fa-file-pdf-o:before { + content: "\f1c1"; +} +.fa-file-word-o:before { + content: "\f1c2"; +} +.fa-file-excel-o:before { + content: "\f1c3"; +} +.fa-file-powerpoint-o:before { + content: "\f1c4"; +} +.fa-file-photo-o:before, +.fa-file-picture-o:before, +.fa-file-image-o:before { + content: "\f1c5"; +} +.fa-file-zip-o:before, +.fa-file-archive-o:before { + content: "\f1c6"; +} +.fa-file-sound-o:before, +.fa-file-audio-o:before { + content: "\f1c7"; +} +.fa-file-movie-o:before, +.fa-file-video-o:before { + content: "\f1c8"; +} +.fa-file-code-o:before { + content: "\f1c9"; +} +.fa-vine:before { + content: "\f1ca"; +} +.fa-codepen:before { + content: "\f1cb"; +} +.fa-jsfiddle:before { + content: "\f1cc"; +} +.fa-life-bouy:before, +.fa-life-buoy:before, +.fa-life-saver:before, +.fa-support:before, +.fa-life-ring:before { + content: "\f1cd"; +} +.fa-circle-o-notch:before { + content: "\f1ce"; +} +.fa-ra:before, +.fa-resistance:before, +.fa-rebel:before { + content: "\f1d0"; +} +.fa-ge:before, +.fa-empire:before { + content: "\f1d1"; +} +.fa-git-square:before { + content: "\f1d2"; +} +.fa-git:before { + content: "\f1d3"; +} +.fa-y-combinator-square:before, +.fa-yc-square:before, +.fa-hacker-news:before { + content: "\f1d4"; +} +.fa-tencent-weibo:before { + content: "\f1d5"; +} +.fa-qq:before { + content: "\f1d6"; +} +.fa-wechat:before, +.fa-weixin:before { + content: "\f1d7"; +} +.fa-send:before, +.fa-paper-plane:before { + content: "\f1d8"; +} +.fa-send-o:before, +.fa-paper-plane-o:before { + content: "\f1d9"; +} +.fa-history:before { + content: "\f1da"; +} +.fa-circle-thin:before { + content: "\f1db"; +} +.fa-header:before { + content: "\f1dc"; +} +.fa-paragraph:before { + content: "\f1dd"; +} +.fa-sliders:before { + content: "\f1de"; +} +.fa-share-alt:before { + content: "\f1e0"; +} +.fa-share-alt-square:before { + content: "\f1e1"; +} +.fa-bomb:before { + content: "\f1e2"; +} +.fa-soccer-ball-o:before, +.fa-futbol-o:before { + content: "\f1e3"; +} +.fa-tty:before { + content: "\f1e4"; +} +.fa-binoculars:before { + content: "\f1e5"; +} +.fa-plug:before { + content: "\f1e6"; +} +.fa-slideshare:before { + content: "\f1e7"; +} +.fa-twitch:before { + content: "\f1e8"; +} +.fa-yelp:before { + content: "\f1e9"; +} +.fa-newspaper-o:before { + content: "\f1ea"; +} +.fa-wifi:before { + content: "\f1eb"; +} +.fa-calculator:before { + content: "\f1ec"; +} +.fa-paypal:before { + content: "\f1ed"; +} +.fa-google-wallet:before { + content: "\f1ee"; +} +.fa-cc-visa:before { + content: "\f1f0"; +} +.fa-cc-mastercard:before { + content: "\f1f1"; +} +.fa-cc-discover:before { + content: "\f1f2"; +} +.fa-cc-amex:before { + content: "\f1f3"; +} +.fa-cc-paypal:before { + content: "\f1f4"; +} +.fa-cc-stripe:before { + content: "\f1f5"; +} +.fa-bell-slash:before { + content: "\f1f6"; +} +.fa-bell-slash-o:before { + content: "\f1f7"; +} +.fa-trash:before { + content: "\f1f8"; +} +.fa-copyright:before { + content: "\f1f9"; +} +.fa-at:before { + content: "\f1fa"; +} +.fa-eyedropper:before { + content: "\f1fb"; +} +.fa-paint-brush:before { + content: "\f1fc"; +} +.fa-birthday-cake:before { + content: "\f1fd"; +} +.fa-area-chart:before { + content: "\f1fe"; +} +.fa-pie-chart:before { + content: "\f200"; +} +.fa-line-chart:before { + content: "\f201"; +} +.fa-lastfm:before { + content: "\f202"; +} +.fa-lastfm-square:before { + content: "\f203"; +} +.fa-toggle-off:before { + content: "\f204"; +} +.fa-toggle-on:before { + content: "\f205"; +} +.fa-bicycle:before { + content: "\f206"; +} +.fa-bus:before { + content: "\f207"; +} +.fa-ioxhost:before { + content: "\f208"; +} +.fa-angellist:before { + content: "\f209"; +} +.fa-cc:before { + content: "\f20a"; +} +.fa-shekel:before, +.fa-sheqel:before, +.fa-ils:before { + content: "\f20b"; +} +.fa-meanpath:before { + content: "\f20c"; +} +.fa-buysellads:before { + content: "\f20d"; +} +.fa-connectdevelop:before { + content: "\f20e"; +} +.fa-dashcube:before { + content: "\f210"; +} +.fa-forumbee:before { + content: "\f211"; +} +.fa-leanpub:before { + content: "\f212"; +} +.fa-sellsy:before { + content: "\f213"; +} +.fa-shirtsinbulk:before { + content: "\f214"; +} +.fa-simplybuilt:before { + content: "\f215"; +} +.fa-skyatlas:before { + content: "\f216"; +} +.fa-cart-plus:before { + content: "\f217"; +} +.fa-cart-arrow-down:before { + content: "\f218"; +} +.fa-diamond:before { + content: "\f219"; +} +.fa-ship:before { + content: "\f21a"; +} +.fa-user-secret:before { + content: "\f21b"; +} +.fa-motorcycle:before { + content: "\f21c"; +} +.fa-street-view:before { + content: "\f21d"; +} +.fa-heartbeat:before { + content: "\f21e"; +} +.fa-venus:before { + content: "\f221"; +} +.fa-mars:before { + content: "\f222"; +} +.fa-mercury:before { + content: "\f223"; +} +.fa-intersex:before, +.fa-transgender:before { + content: "\f224"; +} +.fa-transgender-alt:before { + content: "\f225"; +} +.fa-venus-double:before { + content: "\f226"; +} +.fa-mars-double:before { + content: "\f227"; +} +.fa-venus-mars:before { + content: "\f228"; +} +.fa-mars-stroke:before { + content: "\f229"; +} +.fa-mars-stroke-v:before { + content: "\f22a"; +} +.fa-mars-stroke-h:before { + content: "\f22b"; +} +.fa-neuter:before { + content: "\f22c"; +} +.fa-genderless:before { + content: "\f22d"; +} +.fa-facebook-official:before { + content: "\f230"; +} +.fa-pinterest-p:before { + content: "\f231"; +} +.fa-whatsapp:before { + content: "\f232"; +} +.fa-server:before { + content: "\f233"; +} +.fa-user-plus:before { + content: "\f234"; +} +.fa-user-times:before { + content: "\f235"; +} +.fa-hotel:before, +.fa-bed:before { + content: "\f236"; +} +.fa-viacoin:before { + content: "\f237"; +} +.fa-train:before { + content: "\f238"; +} +.fa-subway:before { + content: "\f239"; +} +.fa-medium:before { + content: "\f23a"; +} +.fa-yc:before, +.fa-y-combinator:before { + content: "\f23b"; +} +.fa-optin-monster:before { + content: "\f23c"; +} +.fa-opencart:before { + content: "\f23d"; +} +.fa-expeditedssl:before { + content: "\f23e"; +} +.fa-battery-4:before, +.fa-battery:before, +.fa-battery-full:before { + content: "\f240"; +} +.fa-battery-3:before, +.fa-battery-three-quarters:before { + content: "\f241"; +} +.fa-battery-2:before, +.fa-battery-half:before { + content: "\f242"; +} +.fa-battery-1:before, +.fa-battery-quarter:before { + content: "\f243"; +} +.fa-battery-0:before, +.fa-battery-empty:before { + content: "\f244"; +} +.fa-mouse-pointer:before { + content: "\f245"; +} +.fa-i-cursor:before { + content: "\f246"; +} +.fa-object-group:before { + content: "\f247"; +} +.fa-object-ungroup:before { + content: "\f248"; +} +.fa-sticky-note:before { + content: "\f249"; +} +.fa-sticky-note-o:before { + content: "\f24a"; +} +.fa-cc-jcb:before { + content: "\f24b"; +} +.fa-cc-diners-club:before { + content: "\f24c"; +} +.fa-clone:before { + content: "\f24d"; +} +.fa-balance-scale:before { + content: "\f24e"; +} +.fa-hourglass-o:before { + content: "\f250"; +} +.fa-hourglass-1:before, +.fa-hourglass-start:before { + content: "\f251"; +} +.fa-hourglass-2:before, +.fa-hourglass-half:before { + content: "\f252"; +} +.fa-hourglass-3:before, +.fa-hourglass-end:before { + content: "\f253"; +} +.fa-hourglass:before { + content: "\f254"; +} +.fa-hand-grab-o:before, +.fa-hand-rock-o:before { + content: "\f255"; +} +.fa-hand-stop-o:before, +.fa-hand-paper-o:before { + content: "\f256"; +} +.fa-hand-scissors-o:before { + content: "\f257"; +} +.fa-hand-lizard-o:before { + content: "\f258"; +} +.fa-hand-spock-o:before { + content: "\f259"; +} +.fa-hand-pointer-o:before { + content: "\f25a"; +} +.fa-hand-peace-o:before { + content: "\f25b"; +} +.fa-trademark:before { + content: "\f25c"; +} +.fa-registered:before { + content: "\f25d"; +} +.fa-creative-commons:before { + content: "\f25e"; +} +.fa-gg:before { + content: "\f260"; +} +.fa-gg-circle:before { + content: "\f261"; +} +.fa-tripadvisor:before { + content: "\f262"; +} +.fa-odnoklassniki:before { + content: "\f263"; +} +.fa-odnoklassniki-square:before { + content: "\f264"; +} +.fa-get-pocket:before { + content: "\f265"; +} +.fa-wikipedia-w:before { + content: "\f266"; +} +.fa-safari:before { + content: "\f267"; +} +.fa-chrome:before { + content: "\f268"; +} +.fa-firefox:before { + content: "\f269"; +} +.fa-opera:before { + content: "\f26a"; +} +.fa-internet-explorer:before { + content: "\f26b"; +} +.fa-tv:before, +.fa-television:before { + content: "\f26c"; +} +.fa-contao:before { + content: "\f26d"; +} +.fa-500px:before { + content: "\f26e"; +} +.fa-amazon:before { + content: "\f270"; +} +.fa-calendar-plus-o:before { + content: "\f271"; +} +.fa-calendar-minus-o:before { + content: "\f272"; +} +.fa-calendar-times-o:before { + content: "\f273"; +} +.fa-calendar-check-o:before { + content: "\f274"; +} +.fa-industry:before { + content: "\f275"; +} +.fa-map-pin:before { + content: "\f276"; +} +.fa-map-signs:before { + content: "\f277"; +} +.fa-map-o:before { + content: "\f278"; +} +.fa-map:before { + content: "\f279"; +} +.fa-commenting:before { + content: "\f27a"; +} +.fa-commenting-o:before { + content: "\f27b"; +} +.fa-houzz:before { + content: "\f27c"; +} +.fa-vimeo:before { + content: "\f27d"; +} +.fa-black-tie:before { + content: "\f27e"; +} +.fa-fonticons:before { + content: "\f280"; +} +.fa-reddit-alien:before { + content: "\f281"; +} +.fa-edge:before { + content: "\f282"; +} +.fa-credit-card-alt:before { + content: "\f283"; +} +.fa-codiepie:before { + content: "\f284"; +} +.fa-modx:before { + content: "\f285"; +} +.fa-fort-awesome:before { + content: "\f286"; +} +.fa-usb:before { + content: "\f287"; +} +.fa-product-hunt:before { + content: "\f288"; +} +.fa-mixcloud:before { + content: "\f289"; +} +.fa-scribd:before { + content: "\f28a"; +} +.fa-pause-circle:before { + content: "\f28b"; +} +.fa-pause-circle-o:before { + content: "\f28c"; +} +.fa-stop-circle:before { + content: "\f28d"; +} +.fa-stop-circle-o:before { + content: "\f28e"; +} +.fa-shopping-bag:before { + content: "\f290"; +} +.fa-shopping-basket:before { + content: "\f291"; +} +.fa-hashtag:before { + content: "\f292"; +} +.fa-bluetooth:before { + content: "\f293"; +} +.fa-bluetooth-b:before { + content: "\f294"; +} +.fa-percent:before { + content: "\f295"; +} +.fa-gitlab:before { + content: "\f296"; +} +.fa-wpbeginner:before { + content: "\f297"; +} +.fa-wpforms:before { + content: "\f298"; +} +.fa-envira:before { + content: "\f299"; +} +.fa-universal-access:before { + content: "\f29a"; +} +.fa-wheelchair-alt:before { + content: "\f29b"; +} +.fa-question-circle-o:before { + content: "\f29c"; +} +.fa-blind:before { + content: "\f29d"; +} +.fa-audio-description:before { + content: "\f29e"; +} +.fa-volume-control-phone:before { + content: "\f2a0"; +} +.fa-braille:before { + content: "\f2a1"; +} +.fa-assistive-listening-systems:before { + content: "\f2a2"; +} +.fa-asl-interpreting:before, +.fa-american-sign-language-interpreting:before { + content: "\f2a3"; +} +.fa-deafness:before, +.fa-hard-of-hearing:before, +.fa-deaf:before { + content: "\f2a4"; +} +.fa-glide:before { + content: "\f2a5"; +} +.fa-glide-g:before { + content: "\f2a6"; +} +.fa-signing:before, +.fa-sign-language:before { + content: "\f2a7"; +} +.fa-low-vision:before { + content: "\f2a8"; +} +.fa-viadeo:before { + content: "\f2a9"; +} +.fa-viadeo-square:before { + content: "\f2aa"; +} +.fa-snapchat:before { + content: "\f2ab"; +} +.fa-snapchat-ghost:before { + content: "\f2ac"; +} +.fa-snapchat-square:before { + content: "\f2ad"; +} +.fa-pied-piper:before { + content: "\f2ae"; +} +.fa-first-order:before { + content: "\f2b0"; +} +.fa-yoast:before { + content: "\f2b1"; +} +.fa-themeisle:before { + content: "\f2b2"; +} +.fa-google-plus-circle:before, +.fa-google-plus-official:before { + content: "\f2b3"; +} +.fa-fa:before, +.fa-font-awesome:before { + content: "\f2b4"; +} +.fa-handshake-o:before { + content: "\f2b5"; +} +.fa-envelope-open:before { + content: "\f2b6"; +} +.fa-envelope-open-o:before { + content: "\f2b7"; +} +.fa-linode:before { + content: "\f2b8"; +} +.fa-address-book:before { + content: "\f2b9"; +} +.fa-address-book-o:before { + content: "\f2ba"; +} +.fa-vcard:before, +.fa-address-card:before { + content: "\f2bb"; +} +.fa-vcard-o:before, +.fa-address-card-o:before { + content: "\f2bc"; +} +.fa-user-circle:before { + content: "\f2bd"; +} +.fa-user-circle-o:before { + content: "\f2be"; +} +.fa-user-o:before { + content: "\f2c0"; +} +.fa-id-badge:before { + content: "\f2c1"; +} +.fa-drivers-license:before, +.fa-id-card:before { + content: "\f2c2"; +} +.fa-drivers-license-o:before, +.fa-id-card-o:before { + content: "\f2c3"; +} +.fa-quora:before { + content: "\f2c4"; +} +.fa-free-code-camp:before { + content: "\f2c5"; +} +.fa-telegram:before { + content: "\f2c6"; +} +.fa-thermometer-4:before, +.fa-thermometer:before, +.fa-thermometer-full:before { + content: "\f2c7"; +} +.fa-thermometer-3:before, +.fa-thermometer-three-quarters:before { + content: "\f2c8"; +} +.fa-thermometer-2:before, +.fa-thermometer-half:before { + content: "\f2c9"; +} +.fa-thermometer-1:before, +.fa-thermometer-quarter:before { + content: "\f2ca"; +} +.fa-thermometer-0:before, +.fa-thermometer-empty:before { + content: "\f2cb"; +} +.fa-shower:before { + content: "\f2cc"; +} +.fa-bathtub:before, +.fa-s15:before, +.fa-bath:before { + content: "\f2cd"; +} +.fa-podcast:before { + content: "\f2ce"; +} +.fa-window-maximize:before { + content: "\f2d0"; +} +.fa-window-minimize:before { + content: "\f2d1"; +} +.fa-window-restore:before { + content: "\f2d2"; +} +.fa-times-rectangle:before, +.fa-window-close:before { + content: "\f2d3"; +} +.fa-times-rectangle-o:before, +.fa-window-close-o:before { + content: "\f2d4"; +} +.fa-bandcamp:before { + content: "\f2d5"; +} +.fa-grav:before { + content: "\f2d6"; +} +.fa-etsy:before { + content: "\f2d7"; +} +.fa-imdb:before { + content: "\f2d8"; +} +.fa-ravelry:before { + content: "\f2d9"; +} +.fa-eercast:before { + content: "\f2da"; +} +.fa-microchip:before { + content: "\f2db"; +} +.fa-snowflake-o:before { + content: "\f2dc"; +} +.fa-superpowers:before { + content: "\f2dd"; +} +.fa-wpexplorer:before { + content: "\f2de"; +} +.fa-meetup:before { + content: "\f2e0"; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} diff --git a/tools/manager/css/font-awesome.min.css b/tools/manager/css/font-awesome.min.css new file mode 100644 index 0000000..540440c --- /dev/null +++ b/tools/manager/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} diff --git a/tools/manager/css/style.css b/tools/manager/css/style.css new file mode 100644 index 0000000..37e8bc5 --- /dev/null +++ b/tools/manager/css/style.css @@ -0,0 +1,67 @@ + +.app +{ + display: flex; + flex-direction: column; + height: 100%; + width: 100%; +} + +.app-tool-bar +{ + display: flex; + width: 100%; + height: 60px; + background-color: var(--default-background-color); +} + +.app-center +{ + flex: 1; + width: 100%; + display: flex; + background-color: yellow; +} + +.app-content +{ + flex: 1; +} + +.app-menu +{ + min-width: 300px; + background-color: red; +} + +.app-status-bar +{ + height: 60px; + background-color: blue; + +} + +.browser +{ + display: flex; + flex-wrap: wrap; +} + +.browser > .bar +{ + width: 100%; + flex-grow:2; + height: 40px; +} + +.browser > .table:nth-child(2) +{ + width: 200px; + height: calc(100% - 40px); +} + +.browser > .table:nth-child(3) +{ + width: calc(100% - 200px); + height: calc(100% - 40px); +} diff --git a/tools/manager/fonts/FontAwesome.otf b/tools/manager/fonts/FontAwesome.otf new file mode 100644 index 0000000..401ec0f Binary files /dev/null and b/tools/manager/fonts/FontAwesome.otf differ diff --git a/tools/manager/fonts/fontawesome-webfont.eot b/tools/manager/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000..e9f60ca Binary files /dev/null and b/tools/manager/fonts/fontawesome-webfont.eot differ diff --git a/tools/manager/fonts/fontawesome-webfont.svg b/tools/manager/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000..855c845 --- /dev/null +++ b/tools/manager/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/manager/fonts/fontawesome-webfont.ttf b/tools/manager/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..35acda2 Binary files /dev/null and b/tools/manager/fonts/fontawesome-webfont.ttf differ diff --git a/tools/manager/fonts/fontawesome-webfont.woff b/tools/manager/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..400014a Binary files /dev/null and b/tools/manager/fonts/fontawesome-webfont.woff differ diff --git a/tools/manager/fonts/fontawesome-webfont.woff2 b/tools/manager/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000..4d13fc6 Binary files /dev/null and b/tools/manager/fonts/fontawesome-webfont.woff2 differ diff --git a/tools/manager/index.html b/tools/manager/index.html new file mode 100644 index 0000000..9a1ea61 --- /dev/null +++ b/tools/manager/index.html @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ +
+ Resource Manager +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + +
Server URL:
Domain:
Username:
Password:
+ + + +
+ + + \ No newline at end of file diff --git a/tools/manager/js/IUIView.js b/tools/manager/js/IUIView.js new file mode 100644 index 0000000..06bb947 --- /dev/null +++ b/tools/manager/js/IUIView.js @@ -0,0 +1,4 @@ +class IUIView extends IUIWidget +{ + +} \ No newline at end of file diff --git a/tools/manager/js/app.js b/tools/manager/js/app.js new file mode 100644 index 0000000..5e74565 --- /dev/null +++ b/tools/manager/js/app.js @@ -0,0 +1,30 @@ + +function init() +{ + iui("dlgLogin").dialog().show(); + + var local = new MemoryStore(); + Warehouse.put(local, "local"); + + iui("divContent").browser(); +} + +function connect() +{ + var url = document.getElementById("txtURL").value; + var domain = document.getElementById("txtDomain").value; + var username = document.getElementById("txtUsername").value; + var password = document.getElementById("txtPassword").value; + + iui("dlgLogin").setLoading(true); + + var con = new DistributedConnection(url, domain, username, password); + Warehouse.put(con, "remote"); + + con.on("ready", function (d) { + iui("divContent").setConnection(con); + iui("dlgLogin").hide(); + }).on("error", function(sender, code, msg){ + console.log(sender, code, msg); + }); +} \ No newline at end of file diff --git a/tools/manager/js/browser.js b/tools/manager/js/browser.js new file mode 100644 index 0000000..a632726 --- /dev/null +++ b/tools/manager/js/browser.js @@ -0,0 +1,132 @@ +class ResourceBrowser extends IUIWidget +{ + + loadClass(className) + { + var rt = new AsyncReply(); + + if (this.scripts[className] != null) + { + rt.trigger(true); + } + else + { + var script = document.createElement('script'); + var self = this; + script.onload = function () { + self.scripts[classsName] = script; + rt.trigger(true); + }; + + script.src = className + ".js"; + document.head.appendChild(script); + } + } + + constructor(el, properties) + { + super(el, IUI.extend(properties, + { + customClass: "browser", + visible: false + }) + ); + + var treeLayout = [ + {width: '200px', field: "instance", title: "Name", formatter: function(d, v){ + + if(v.instance.attributes.item("name") == null) + return v.instance.name; + else + return v.instance.attributes.item("name"); + }}, + ]; + + /* + var contentLayout = [ + {field: "instance", title: "Name", formatter: function(d, v){ + return v.instance.attributes.item("name"); + }}, + {field: "instance", title: "Type", formatter: function(d, v){ + return v.instance.template.name; + }} + ]; +*/ + + + this.tree = iui(document.createElement("div")).table({tree: true, layout: treeLayout}); +// this.content = iui(document.createElement("div")).table({layout: contentLayout}); + this.bar = iui(document.createElement("div")).bar(); + this.el.classList.add(this.customClass); + this.el.appendChild(this.bar.el); + this.el.appendChild(this.tree.el); + // this.el.appendChild(this.content.el); + + var self = this; + this.tree.on("dblclick", function(item){ + + self.tree.clear(); + self.connection.getAttributes(item, ["name", "parents", "type"]).then(function(attrs){ + self.tree.add(item); + // self.content.add(item); + }); + + }).on("expand", function(e){ + self.connection.getAttributes(e.item, ["children"]).then(function(attrs){ + + attrs.children.forEach(function(child){ + self.connection.getAttributes(child, ["name", "parents", "childrenCount", "type"]).then(function(cAttars){ + self.tree.add(child, cAttars.childrenCount > 0); + e.success(); + }); + }); + + + }); + }); + } + + setConnection(connection, query = "") + { + var self = this; + this.connection = connection; + this.connection.query(query).then(function(rt) + { + rt.forEach(function(item){ + connection.getAttributes(item, ["name", "childrenCount", "type"]).then(function(attrs){ + + //connection.getAttributes(item, ["name", "children", "type"]).then(function(attrs){ + self.tree.add(item, attrs.childrenCount > 0); + + /* + var children = attrs.children; + + for(var i = 0; i < children.length; i++) + { + + if (!children[i].instance.attributes.contains("parents")) + children[i].instance.attributes.add("parents", []); + + children[i].instance.attributes.item("parents").push(item); + + self.tree.add(children[i]); + } + */ + }); + }); + + /* + for(var i = 0; i < rt.length; i++) + { + var item = rt[i]; + connection.getAttributes(rt[i]).then(function(attrs){ + self.tree.add(rt[i]); + self.content.add(rt[i]); + }); + } + */ + }); + } +} + +IUI.module("browser", ResourceBrowser); \ No newline at end of file diff --git a/tools/manager/viewers/Esiur.Stores.MemoryStore.js b/tools/manager/viewers/Esiur.Stores.MemoryStore.js new file mode 100644 index 0000000..a3f3e0b --- /dev/null +++ b/tools/manager/viewers/Esiur.Stores.MemoryStore.js @@ -0,0 +1,9 @@ +class Esiur_Stores_MemoryStore extends IUIWidget +{ + constuctor() + { + + } +} + +IUI.module("Esiur.Stores.MemoryStore", Esiur_Stores_MemoryStore); \ No newline at end of file