diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..635891d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +*.eps -crlf +*.jpg -crlf +*.png -crlf \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e77e935 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.user +*.pro.user* +*~ +*.orig \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..0e3ad74 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,8 @@ +before_script: + - git submodule update --init +types: + - build + +build_app: + type: build + script: echo Building! \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9b2c22e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "core"] + path = core + url = git@github.com:qflow/core.git +[submodule "qbs"] + path = qbs + url = git@github.com:qflow/qbs.git diff --git a/LICENSE.LGPLv3 b/LICENSE.LGPLv3 new file mode 100644 index 0000000..bde60ce --- /dev/null +++ b/LICENSE.LGPLv3 @@ -0,0 +1,165 @@ +GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..fcac856 --- /dev/null +++ b/README.md @@ -0,0 +1,118 @@ +# Introduction +[Router](src/client/README.md) +# Get sources + git clone --recursive git@gitlab.com:qflow/wamp.git + +# Build + qbs --recursive wamp + make + +# Client +## QML +```qml +import QFlow.Core 1.0 +import QFlow.Networking 1.0 +import QFlow.Wamp 1.0 + +... +QtObject //example object that will be registered as "object" + { + id: root + property int testInt: 5 //will be registered as "object.testInt" + function testFunc1() //will be registerd as "object.testFunc" + { + + } + property list objList: [ //will be registered as "object.ojList" + QtObject{property int p: 4}, //will be registered as "object.objList.0" + QtObject{property string s: "hello"}] ////will be registered as "object.objList.1" + property QtObject child: QtObject{ //will be registered as "object.child" + id: child + function substract(a,b) { //will be registered as "object.child.substract" + return a-b; + } + + } + } + +WampConnection + { + id: wampConnection + url: "ws://localhost:8080" + realm: "realm1" + user: WampCraUser + { + name: "name" + secret: "secret" + } + onConnected: + { + wampConnection.registerProcedure("test.add", wampConnection.add); //register procedure + wampConnection.subscribe("com.myapp.hello", function(param1){ //subscribe to event + console.log(param1); //event received + }); + wampConnection.registerObject("object", root); //register object with properties, functions, recursively registering children + + var future = wampConnection.call("test.add", [1,2]); //call remote procedure + future.then(function(){ + console.log(future.result()); //display result + }); + wampConnection.publish("com.myapp.hello", ["hello"]); //publish event + } + function add(a,b) + { + return a + b; + } + onError: + { + console.log(error); + } + } + +``` + +## C++ +```cpp +include +using namespace QFlow; + +... +//initialize wamp connection +WampConnection* con = new WampConnection(); +con->setRealm("realm1"); +con->setUser(new WampCraUser("name", "secret", con)); +con->setUrl(QUrl("ws://localhost:8080")); +con->connect(); + +//use connection + +//register lambda +con->registerProcedure("test.add", [](int a, int b){ + return a + b; +}); + +//register QObject +con->registerObject("object", obj); + +//register method of object 'obj' +con->registerMethod("object.testFunc1", obj, "testFunc1()"); + +//register integer property 'testInt'. The property getter will be at "object.testInt" and the setter at "object.testInt.set" +con->registerProperty("object.testInt", obj, "testInt") + +//subscribe and specify lambda callback +con->subscribe("com.myapp.hello", [](QString param1){ + qDebug() << param1; //event captured +}); + +//call remote procedure +Future f = con->call("test.add", {1, 2}); +f.then([this](const Future& future){ + qDebug() << future.result(); //result received +}); + +// publish event +con->publish("com.myapp.hello", {"hello"}); + + +``` \ No newline at end of file diff --git a/src/autobahn.js b/src/autobahn.js new file mode 100644 index 0000000..2fcb601 --- /dev/null +++ b/src/autobahn.js @@ -0,0 +1,344 @@ +/* + + Counter block mode compatible with Dr Brian Gladman fileenc.c + derived from CryptoJS.mode.CTR + Jan Hruby jhruby.web@gmail.com + + (c) 2012 by C?dric Mesnil. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + MIT License (c) copyright 2013-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors MIT License (c) copyright 2010-2014 original author or authors */ +(function(ma){"object"===typeof exports&&"undefined"!==typeof module?module.exports=ma():"function"===typeof define&&define.amd?define([],ma):("undefined"!==typeof window?window:"undefined"!==typeof global?global:"undefined"!==typeof self?self:this).autobahn=ma()})(function(){return function h(n,l,b){function a(c,f){if(!l[c]){if(!n[c]){var q="function"==typeof require&&require;if(!f&&q)return q(c,!0);if(g)return g(c,!0);q=Error("Cannot find module '"+c+"'");throw q.code="MODULE_NOT_FOUND",q;}q=l[c]= +{exports:{}};n[c][0].call(q.exports,function(b){var r=n[c][1][b];return a(r?r:b)},q,q.exports,h,n,l,b)}return l[c].exports}for(var g="function"==typeof require&&require,c=0;cthis._max_retry_delay&&(this._retry_delay=this._max_retry_delay);this._retry_count+=1;var a;a=this._retry&&(-1===this._max_retries|| +this._retry_count<=this._max_retries)?{count:this._retry_count,delay:this._retry_delay,will_retry:!0}:{count:null,delay:null,will_retry:!1};this._retry_delay_growth&&(this._retry_delay*=this._retry_delay_growth);return a};q.prototype.open=function(){function a(){c._transport=c._create_transport();if(c._transport)c._session=new g.Session(c._transport,c._defer,c._options.onchallenge),c._session_close_reason=null,c._session_close_message=null,c._transport.onopen=function(){c._autoreconnect_reset();c._connect_successes+= +1;c._session.join(c._options.realm,c._options.authmethods,c._options.authid)},c._session.onjoin=function(a){if(c.onopen)try{c.onopen(c._session,a)}catch(b){e.debug("Exception raised from app code while firing Connection.onopen()",b)}},c._session.onleave=function(a,b){c._session_close_reason=a;c._session_close_message=b.message||"";c._retry=!1;c._transport.close(1E3)},c._transport.onclose=function(b){c._autoreconnect_reset_timer();var f=c._transport=null;0===c._connect_successes?(f="unreachable",c._retry_if_unreachable|| +(c._retry=!1)):f=b.wasClean?"closed":"lost";b=c._autoreconnect_advance();if(c.onclose){var g={reason:c._session_close_reason,message:c._session_close_message,retry_delay:b.delay,retry_count:b.count,will_retry:b.will_retry};try{var q=c.onclose(f,g)}catch(v){e.debug("Exception raised from app code while firing Connection.onclose()",v)}}c._session&&(c._session._id=null,c._session=null,c._session_close_reason=null,c._session_close_message=null);c._retry&&!q&&(b.will_retry?(c._is_retrying=!0,e.debug("retrying in "+ +b.delay+" s"),c._retry_timer=setTimeout(a,1E3*b.delay)):e.debug("giving up trying to reconnect"))};else if(c._retry=!1,c.onclose)c.onclose("unsupported",{reason:null,message:null,retry_delay:null,retry_count:null,will_retry:!1})}var c=this;if(c._transport)throw"connection already open (or opening)";c._autoreconnect_reset();c._retry=!0;a()};q.prototype.close=function(a,c){if(!this._transport&&!this._is_retrying)throw"connection already closed";this._retry=!1;this._session&&this._session.isOpen?this._session.leave(a, +c):this._transport&&this._transport.close(1E3)};Object.defineProperty(q.prototype,"defer",{get:function(){return this._defer}});Object.defineProperty(q.prototype,"session",{get:function(){return this._session}});Object.defineProperty(q.prototype,"isOpen",{get:function(){return this._session&&this._session.isOpen?!0:!1}});Object.defineProperty(q.prototype,"isConnected",{get:function(){return this._transport?!0:!1}});Object.defineProperty(q.prototype,"transport",{get:function(){return this._transport? +this._transport:{info:{type:"none",url:null,protocol:null}}}});Object.defineProperty(q.prototype,"isRetrying",{get:function(){return this._is_retrying}});l.Connection=q}).call(this,"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{"./autobahn.js":3,"./log.js":6,"./session.js":15,"./util.js":19,when:78}],6:[function(h,n,l){(function(b){var a=function(){};"AUTOBAHN_DEBUG"in b&&AUTOBAHN_DEBUG&&"console"in b&&(a=function(){console.log.apply(console, +arguments)});l.debug=a}).call(this,"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{}],7:[function(h,n,l){h("./polyfill/object");h("./polyfill/array");h("./polyfill/string");h("./polyfill/function");h("./polyfill/console");h("./polyfill/typedarray");h("./polyfill/json")},{"./polyfill/array":8,"./polyfill/console":9,"./polyfill/function":10,"./polyfill/json":11,"./polyfill/object":12,"./polyfill/string":13,"./polyfill/typedarray":14}],8:[function(h, +n,l){"function"!==typeof Array.prototype.reduce&&(Array.prototype.reduce=function(b){var a,g,c,e;if(null===this||"undefined"===typeof this)throw new TypeError("Array.prototype.reduce called on null or undefined");if("function"!==typeof b)throw new TypeError(b+" is not a function");g=Object(this);a=g.length>>>0;e=0;if(2<=arguments.length)c=arguments[1];else{for(;e=a)throw new TypeError("Reduce of empty array with no initial value");c=g[e++]}for(;ea&&(a+=this.length);0>a&&(a=0);for(var g=this.length;aa&&(a+=this.length);a>this.length-1&&(a=this.length-1);for(a++;0>>0)-1,c;if(2<=arguments.length)c=arguments[1];else{for(;0<=g&&!g in a;)g--;if(0>g)throw new TypeError("Reduce of empty array with no initial value");c=a[g--]}for(;0<=g;g--)g in a&&(c=b(c,a[g],g,a));return c})},{}],9:[function(h,n,l){(function(b){(function(a){a||(a=window.console={log:function(a,c,b,f,q){},info:function(a,c,b,f,q){},warn:function(a,c,b,f,q){},error:function(a,c,b,f,q){},assert:function(a,c){}});"object"===typeof a.log&&(a.log=Function.prototype.call.bind(a.log,a),a.info= +Function.prototype.call.bind(a.info,a),a.warn=Function.prototype.call.bind(a.warn,a),a.error=Function.prototype.call.bind(a.error,a),a.debug=Function.prototype.call.bind(a.info,a));"group"in a||(a.group=function(b){a.info("\n--- "+b+" ---\n")});"groupEnd"in a||(a.groupEnd=function(){a.log("\n")});"assert"in a||(a.assert=function(a,c){if(!a)try{throw Error("assertion failed: "+c);}catch(b){setTimeout(function(){throw b;},0)}});"time"in a||function(){var b={};a.time=function(a){b[a]=(new Date).getTime()}; +a.timeEnd=function(c){var e=(new Date).getTime();a.info(c+": "+(c in b?e-b[c]:0)+"ms")}}()})(b.console)}).call(this,"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{}],10:[function(h,n,l){Function.prototype.bind||(Function.prototype.bind=function(b){var a=this,g=Array.prototype.slice.call(arguments,1);return function(){return a.apply(b,Array.prototype.concat.apply(g,arguments))}})},{}],11:[function(h,n,l){"object"!==typeof JSON&&(JSON={}); +(function(){function b(a){return 10>a?"0"+a:a}function a(a){e.lastIndex=0;return e.test(a)?'"'+a.replace(e,function(a){var c=k[a];return"string"===typeof c?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function g(c,b){var e,k,v,D,Q=f,C,m=b[c];m&&"object"===typeof m&&"function"===typeof m.toJSON&&(m=m.toJSON(c));"function"===typeof r&&(m=r.call(b,c,m));switch(typeof m){case "string":return a(m);case "number":return isFinite(m)?String(m):"null";case "boolean":case "null":return String(m); +case "object":if(!m)return"null";f+=q;C=[];if("[object Array]"===Object.prototype.toString.apply(m)){D=m.length;for(e=0;e>b}function d(a,c){var b=32-c;return a<>>b}function z(a){return[a&255]}function G(a){return r(a[0],8)}function h(a){return[a&255]}function v(a){return d(a[0],8)}function D(a){a=W(Number(a));return[0>a?0:255>8&255,a&255]}function C(a){return r(a[0]<<8|a[1],16)}function m(a){return[a>>8&255,a&255]}function H(a){return d(a[0]<<8|a[1],16)}function A(a){return[a>>24&255,a>>16&255,a>>8&255, +a&255]}function l(a){return r(a[0]<<24|a[1]<<16|a[2]<<8|a[3],32)}function n(a){return[a>>24&255,a>>16&255,a>>8&255,a&255]}function E(a){return d(a[0]<<24|a[1]<<16|a[2]<<8|a[3],32)}function L(a,c,b){function d(a){var c=t(a);a-=c;return.5>a?c:.5a?1:0):0===a?(k=f=0,p=-Infinity===1/a?1:0):(p=0>a,a=w(a),a>=K(2,1-e)?(f=P(t(x(a)/u),1023),k=d(a/K(2,f)*K(2,b)),2<=k/K(2,b)&&(f+=1,k=1),f> +e?(f=(1<>=1;d.reverse();p=d.join("");a=(1<d?-0:0}function y(a){return N(a,11,52)}function R(a){return L(a,11,52)}function p(a){return N(a,8,23)}function I(a){return L(a,8,23)}var u=Math.LN2,w=Math.abs,t=Math.floor,x=Math.log,S=Math.max,P=Math.min,K=Math.pow,W=Math.round;(function(){var a=Object.defineProperty,c;try{c=Object.defineProperty({},"x",{})}catch(b){c=!1}a&&c||(Object.defineProperty=function(c,b,d){if(a)try{return a(c,b,d)}catch(p){}if(c!== +Object(c))throw TypeError("Object.defineProperty called on non-object");Object.prototype.__defineGetter__&&"get"in d&&Object.prototype.__defineGetter__.call(c,b,d.get);Object.prototype.__defineSetter__&&"set"in d&&Object.prototype.__defineSetter__.call(c,b,d.set);"value"in d&&(c[b]=d.value);return c})})();(function(){function d(a){a>>=0;if(0>a)throw RangeError("ArrayBuffer size is not a small enough positive integer.");Object.defineProperty(this,"byteLength",{value:a});Object.defineProperty(this, +"_bytes",{value:Array(a)});for(var c=0;c>=0;if(0>a)throw RangeError("length is not a small enough positive integer.");Object.defineProperty(this,"length",{value:a});Object.defineProperty(this,"byteLength",{value:a*this.BYTES_PER_ELEMENT});Object.defineProperty(this,"buffer",{value:new d(this.byteLength)});Object.defineProperty(this,"byteOffset",{value:0})}.apply(this,arguments);if(1<=arguments.length&& +"object"===c(arguments[0])&&arguments[0]instanceof r)return function(a){if(this.constructor!==a.constructor)throw TypeError();var c=a.length*this.BYTES_PER_ELEMENT;Object.defineProperty(this,"buffer",{value:new d(c)});Object.defineProperty(this,"byteLength",{value:c});Object.defineProperty(this,"byteOffset",{value:0});Object.defineProperty(this,"length",{value:a.length});for(c=0;c>>=0;if(c>a.byteLength)throw RangeError("byteOffset out of range");if(c%this.BYTES_PER_ELEMENT)throw RangeError("buffer length minus the byteOffset is not a multiple of the element size.");if(void 0===b){var d=a.byteLength-c;if(d%this.BYTES_PER_ELEMENT)throw RangeError("length of buffer minus byteOffset not a multiple of the element size");b=d/this.BYTES_PER_ELEMENT}else b>>>=0,d=b*this.BYTES_PER_ELEMENT;if(c+d>a.byteLength)throw RangeError("byteOffset and length reference an area beyond the end of the buffer"); +Object.defineProperty(this,"buffer",{value:a});Object.defineProperty(this,"byteLength",{value:d});Object.defineProperty(this,"byteOffset",{value:c});Object.defineProperty(this,"length",{value:b})}.apply(this,arguments);throw TypeError();}function u(a,c,b){var d=function(){Object.defineProperty(this,"constructor",{value:d});r.apply(this,arguments);k(this)};"__proto__"in d?d.__proto__=r:(d.from=r.from,d.of=r.of);d.BYTES_PER_ELEMENT=a;var p=function(){};p.prototype=x;d.prototype=new p;Object.defineProperty(d.prototype, +"BYTES_PER_ELEMENT",{value:a});Object.defineProperty(d.prototype,"_pack",{value:c});Object.defineProperty(d.prototype,"_unpack",{value:b});return d}a.ArrayBuffer=a.ArrayBuffer||d;Object.defineProperty(r,"from",{value:function(a){return new this(a)}});Object.defineProperty(r,"of",{value:function(){return new this(arguments)}});var x={};r.prototype=x;Object.defineProperty(r.prototype,"_getter",{value:function(a){if(1>arguments.length)throw SyntaxError("Not enough arguments");a>>>=0;if(!(a>=this.length)){var c= +[],b,d;b=0;for(d=this.byteOffset+a*this.BYTES_PER_ELEMENT;barguments.length)throw SyntaxError("Not enough arguments");a>>>=0;if(!(a>=this.length)){var b=this._pack(c),d,p;d=0;for(p=this.byteOffset+a*this.BYTES_PER_ELEMENT;d>>0,p=S(p,0);a>>=0;a=0>a?S(p+a,0):P(a,p);c>>=0;c=0>c?S(p+c,0):P(c,p);b=void 0===b?p:b>>0;b=0>b?S(p+b,0):P(b,p);p=P(b-c,p-a);from>>0;if(!f(a))throw TypeError();for(var p=0;p>>0,p=S(p,0);c>>=0;c=0>c?S(p+c,0):P(c,p);b=void 0===b?p:b>>0;for(p=0>b?S(p+b,0):P(b,p);c>>0;if(!f(a))throw TypeError(); +for(var p=[],e=0;e>>0;if(!f(a))throw TypeError();for(var d=1>>0;if(!f(a))throw TypeError();for(var d=1>>0;if(!f(a))throw TypeError();for(var p=0;p>>0;if(0===b)return-1;var d=0,p;0=b)return-1;for(d=0<=d?d:S(b-w(d),0);d>>0,d=Array(b),p=0;p>>0;if(0===b)return-1;var d=b;1>>0;if(!f(a))throw TypeError();var p=[];p.length=d;for(var e=0;e>>0;if(!f(a))throw TypeError();if(0===b&&1===arguments.length)throw TypeError();var d=0,p;for(p=2<=arguments.length?arguments[1]:c._getter(d++);d>>0;if(!f(a))throw TypeError(); +if(0===b&&1===arguments.length)throw TypeError();var b=b-1,d;for(d=2<=arguments.length?arguments[1]:c._getter(b--);0<=b;)d=a.call(void 0,d,c._getter(b),b,c),b--;return d}});Object.defineProperty(r.prototype,"reverse",{value:function(){if(void 0===this||null===this)throw TypeError();for(var a=Object(this),c=a.length>>>0,b=t(c/2),d=0,c=c-1;darguments.length)throw SyntaxError("Not enough arguments"); +var b,d,p,e,f,k;if("object"===typeof arguments[0]&&arguments[0].constructor===this.constructor){b=arguments[0];d=arguments[1]>>>0;if(d+b.length>this.length)throw RangeError("Offset plus length of array is out of range");k=this.byteOffset+d*this.BYTES_PER_ELEMENT;d=b.length*this.BYTES_PER_ELEMENT;if(b.buffer===this.buffer){p=[];e=0;for(f=b.byteOffset;e>>0;d=arguments[1]>>>0;if(d+p>this.length)throw RangeError("Offset plus length of array is out of range");for(e=0;e>>0,p=a>>0,p=0>p?S(d+p,0):P(p,d),e=void 0===c?d:c>>0,d=0>e?S(d+e,0): +P(e,d),e=new b.constructor(d-p),f=0;p>>0;if(!f(a))throw TypeError();for(var p=0;p>>0,d=Array(b),p=0;p< +b;++p)d[p]=c._getter(p);a?d.sort(a):d.sort();for(p=0;p>=0;c>>=0;1>arguments.length&&(a=0);2>arguments.length&&(c=this.length);0>a&&(a=this.length+a);0>c&&(c=this.length+c);var b=this.length;a=0>a?0:a>b?b:a;b=this.length;b=(0>c?0:c>b?b:c)-a;0>b&&(b=0);return new this.constructor(this.buffer,this.byteOffset+a*this.BYTES_PER_ELEMENT,b)}});var da=u(1,z,G),T=u(1,h,v),N=u(1,D,v),L=u(2,Q,C),K=u(2,m,H), +ia=u(4,A,l),fa=u(4,n,E),W=u(4,I,p),ga=u(8,R,y);a.Int8Array=b.Int8Array=a.Int8Array||da;a.Uint8Array=b.Uint8Array=a.Uint8Array||T;a.Uint8ClampedArray=b.Uint8ClampedArray=a.Uint8ClampedArray||N;a.Int16Array=b.Int16Array=a.Int16Array||L;a.Uint16Array=b.Uint16Array=a.Uint16Array||K;a.Int32Array=b.Int32Array=a.Int32Array||ia;a.Uint32Array=b.Uint32Array=a.Uint32Array||fa;a.Float32Array=b.Float32Array=a.Float32Array||W;a.Float64Array=b.Float64Array=a.Float64Array||ga})();(function(){function c(a,b){return f(a.get)? +a.get(b):a[b]}function b(a,c,d){if(!(a instanceof ArrayBuffer||"ArrayBuffer"===e(a)))throw TypeError();c>>>=0;if(c>a.byteLength)throw RangeError("byteOffset out of range");d=void 0===d?a.byteLength-c:d>>>0;if(c+d>a.byteLength)throw RangeError("byteOffset and length reference an area beyond the end of the buffer");Object.defineProperty(this,"buffer",{value:a});Object.defineProperty(this,"byteLength",{value:d});Object.defineProperty(this,"byteOffset",{value:c})}function d(b){return function(d,p){d>>>= +0;if(d+b.BYTES_PER_ELEMENT>this.byteLength)throw RangeError("Array index out of range");d+=this.byteOffset;for(var e=new a.Uint8Array(this.buffer,d,b.BYTES_PER_ELEMENT),f=[],r=0;r>>=0;if(d+b.BYTES_PER_ELEMENT>this.byteLength)throw RangeError("Array index out of range");p=new b([p]);p=new a.Uint8Array(p.buffer);var f=[],r;for(r=0;r must be a string"); +e.assert(!c||Array.isArray(c),"Session.join: must be an array []");e.assert(!b||"string"===typeof b,"Session.join: must be a string");if(this.isOpen)throw"session already open";this._goodbye_sent=!1;this._realm=a;var d={};d.roles=f;c&&(d.authmethods=c);b&&(d.authid=b);this._send_wamp([1,a,d])};v.prototype.leave=function(a,c){e.assert(!a||"string"===typeof a,"Session.leave: must be a string");e.assert(!c||"string"===typeof c,"Session.leave: must be a string"); +if(!this.isOpen)throw"session not open";a||(a="wamp.close.normal");var b={};c&&(b.message=c);this._send_wamp([6,b,a]);this._goodbye_sent=!0};v.prototype.call=function(c,b,d,f){e.assert("string"===typeof c,"Session.call: must be a string");e.assert(!b||Array.isArray(b),"Session.call: must be an array []");e.assert(!d||d instanceof Object,"Session.call: must be an object {}");e.assert(!f||f instanceof Object,"Session.call: must be an object {}");if(!this.isOpen)throw"session not open"; +f=f||{};void 0===f.disclose_me&&this._caller_disclose_me&&(f.disclose_me=!0);var k=this._defer(),r=a();this._call_reqs[r]=[k,f];c=[48,r,f,this.resolve(c)];b?(c.push(b),d&&c.push(d)):d&&(c.push([]),c.push(d));this._send_wamp(c);return k.promise.then?k.promise:k};v.prototype.publish=function(c,b,d,f){e.assert("string"===typeof c,"Session.publish: must be a string");e.assert(!b||Array.isArray(b),"Session.publish: must be an array []");e.assert(!d||d instanceof Object,"Session.publish: must be an object {}"); +e.assert(!f||f instanceof Object,"Session.publish: must be an object {}");if(!this.isOpen)throw"session not open";f=f||{};void 0===f.disclose_me&&this._publisher_disclose_me&&(f.disclose_me=!0);var k=null,r=a();f.acknowledge&&(k=this._defer(),this._publish_reqs[r]=[k,f]);c=[16,r,f,this.resolve(c)];b?(c.push(b),d&&c.push(d)):d&&(c.push([]),c.push(d));this._send_wamp(c);if(k)return k.promise.then?k.promise:k};v.prototype.subscribe=function(c,b,d){e.assert("string"===typeof c,"Session.subscribe: must be a string"); +e.assert("function"===typeof b,"Session.subscribe: must be a function");e.assert(!d||d instanceof Object,"Session.subscribe: must be an object {}");if(!this.isOpen)throw"session not open";var f=a(),k=this._defer();this._subscribe_reqs[f]=[k,c,b,d];b=[32,f];d?b.push(d):b.push({});b.push(this.resolve(c));this._send_wamp(b);return k.promise.then?k.promise:k};v.prototype.register=function(c,b,d){e.assert("string"===typeof c,"Session.register: must be a string");e.assert("function"=== +typeof b,"Session.register: must be a function");e.assert(!d||d instanceof Object,"Session.register: must be an object {}");if(!this.isOpen)throw"session not open";var f=a(),k=this._defer();this._register_reqs[f]=[k,c,b,d];b=[64,f];d?b.push(d):b.push({});b.push(this.resolve(c));this._send_wamp(b);return k.promise.then?k.promise:k};v.prototype.unsubscribe=function(c){e.assert(c instanceof z,"Session.unsubscribe: must be an instance of class autobahn.Subscription"); +if(!this.isOpen)throw"session not open";if(!(c.active&&c.id in this._subscriptions))throw"subscription not active";var b=this._subscriptions[c.id],d=b.indexOf(c);if(-1===d)throw"subscription not active";b.splice(d,1);c.active=!1;d=this._defer();b.length?d.resolve(!1):(b=a(),this._unsubscribe_reqs[b]=[d,c.id],this._send_wamp([34,b,c.id]));return d.promise.then?d.promise:d};v.prototype.unregister=function(c){e.assert(c instanceof G,"Session.unregister: must be an instance of class autobahn.Registration"); +if(!this.isOpen)throw"session not open";if(!(c.active&&c.id in this._registrations))throw"registration not active";var b=a(),d=this._defer();this._unregister_reqs[b]=[d,c];this._send_wamp([66,b,c.id]);return d.promise.then?d.promise:d};v.prototype.prefix=function(a,c){e.assert("string"===typeof a,"Session.prefix: must be a string");e.assert(!c||"string"===typeof c,"Session.prefix: must be a string or falsy");c?this._prefixes[a]=c:a in this._prefixes&&delete this._prefixes[a]};v.prototype.resolve= +function(a){e.assert("string"===typeof a,"Session.resolve: must be a string");var c=a.indexOf(":");if(0<=c){var b=a.substring(0,c);if(b in this._prefixes)return this._prefixes[b]+"."+a.substring(c+1);throw"cannot resolve CURIE prefix '"+b+"'";}return a};l.Session=v;l.Invocation=q;l.Event=k;l.Result=r;l.Error=d;l.Subscription=z;l.Registration=G;l.Publication=B}).call(this,"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{"./log.js":6, +"./util.js":19,when:78,"when/function":54}],16:[function(h,n,l){function b(c){a.assert(void 0!==c.url,"options.url missing");a.assert("string"===typeof c.url,"options.url must be a string");this._options=c}var a=h("../util.js"),g=h("../log.js");h("when");b.prototype.type="longpoll";b.prototype.create=function(){var c=this;g.debug("longpoll.Factory.create");var b={protocol:void 0,send:void 0,close:void 0,onmessage:function(){},onopen:function(){},onclose:function(){},info:{type:"longpoll",url:null, +protocol:"wamp.2.json"},_run:function(){var f=null,q=!1,k=c._options.request_timeout||12E3;a.http_post(c._options.url+"/open",JSON.stringify({protocols:["wamp.2.json"]}),k).then(function(r){function d(){g.debug("longpoll.Transport: polling for message ...");a.http_post(z+"/receive",null,k).then(function(a){a&&(a=JSON.parse(a),g.debug("longpoll.Transport: message received",a),b.onmessage(a));q||d()},function(a){g.debug("longpoll.Transport: could not receive message",a.code,a.text);q=!0;b.onclose({code:1001, +reason:"transport receive failure (HTTP/POST status "+a.code+" - '"+a.text+"')",wasClean:!1})})}f=JSON.parse(r);var z=c._options.url+"/"+f.transport;b.info.url=z;g.debug("longpoll.Transport: open",f);b.close=function(c,d){if(q)throw"transport is already closing";q=!0;a.http_post(z+"/close",null,k).then(function(){g.debug("longpoll.Transport: transport closed");b.onclose({code:1E3,reason:"transport closed",wasClean:!0})},function(a){g.debug("longpoll.Transport: could not close transport",a.code,a.text)})}; +b.send=function(c){if(q)throw"transport is closing or closed already";g.debug("longpoll.Transport: sending message ...",c);c=JSON.stringify(c);a.http_post(z+"/send",c,k).then(function(){g.debug("longpoll.Transport: message sent")},function(a){g.debug("longpoll.Transport: could not send message",a.code,a.text);q=!0;b.onclose({code:1001,reason:"transport send failure (HTTP/POST status "+a.code+" - '"+a.text+"')",wasClean:!1})})};d();b.onopen()},function(a){g.debug("longpoll.Transport: could not open transport", +a.code,a.text);q=!0;b.onclose({code:1001,reason:"transport open failure (HTTP/POST status "+a.code+" - '"+a.text+"')",wasClean:!1})})}};b._run();return b};l.Factory=b},{"../log.js":6,"../util.js":19,when:78}],17:[function(h,n,l){(function(b,a){function g(a){a.protocols?e.assert(Array.isArray(a.protocols),"options.protocols must be an array"):a.protocols=["wamp.2.json"];a.rawsocket_max_len_exp=a.rawsocket_max_len_exp||24;this._options=a}function c(c,b){this._options={_peer_serializer:null,_peer_max_len_exp:0}; +this._options=e.defaults(this._options,b,this.DEFAULT_OPTIONS);e.assert(this._options.serializer in this.SERIALIZERS,"Unsupported serializer: "+this._options.serializer);e.assert(9<=this._options.max_len_exp&&36>=this._options.max_len_exp,"Message length out of bounds [9, 36]: "+this._options.max_len_exp);e.assert(!this._options.autoping||Number.isInteger(this._options.autoping)&&0<=this._options.autoping,"Autoping interval must be positive");e.assert(!this._options.ping_timeout||Number.isInteger(this._options.ping_timeout)&& +0<=this._options.ping_timeout,"Ping timeout duration must be positive");e.assert(!this._options.packet_timeout||Number.isInteger(this._options.packet_timeout)&&0<=this._options.packet_timeout,"Packet timeout duration must be positive");e.assert(!this._options.autoping||!this._options.ping_timeout||this._options.autoping>this._options.ping_timeout,"Autoping interval ("+this._options.autoping+") must be lower than ping timeout ("+this._options.ping_timeout+")");this._ping_interval=this._ping_payload= +this._ping_timeout=null;this._status=this.STATUS.UNINITIATED;this._stream=c;this._emitter=new q;this._buffer=new a(4);this._msgLen=this._bufferLen=0;var f=this;this._stream.on("data",function(a){f._read(a)});this._stream.on("connect",function(){f._handshake()});["close","drain","end","error","timeout"].forEach(function(a){f._stream.on(a,function(c){f._emitter.emit(a,c)})})}var e=h("../util.js"),f=h("../log.js"),q=h("events").EventEmitter;g.prototype.type="rawsocket";g.prototype.create=function(){var a= +this,d={protocol:void 0,send:void 0,close:void 0,onmessage:function(){},onopen:function(){},onclose:function(){},info:{type:"rawsocket",url:null,protocol:"wamp.2.json"}};if(b.process&&b.process.versions.node)(function(){var b=h("net"),e;if(a._options.path)connectionOptions={path:a._options.path,allowHalfOpen:!0};else if(a._options.port)connectionOptions={port:a._options.port||8E3,host:a._options.host||"localhost",allowHalfOpen:!0};else throw"You must specify a host/port combination or a unix socket path to connect to"; +b=b.connect(connectionOptions);e=new c(b,{serializer:"json",max_len_exp:a._options.rawsocket_max_len_exp});e.on("connect",function(a){f.debug("RawSocket transport negociated");d.onopen(a)});e.on("data",function(a){f.debug("RawSocket transport received",a);d.onmessage(a)});e.on("close",function(a){f.debug("RawSocket transport closed");d.onclose({code:999,reason:"",wasClean:!a})});e.on("error",function(a){f.debug("RawSocket transport error",a)});d.close=function(a,c){f.debug("RawSocket transport closing", +a,c);e.close()};d.send=function(a){f.debug("RawSocket transport sending",a);e.write(a)}})();else throw"No RawSocket support in browser";return d};c.prototype._MAGIC_BYTE=127;c.prototype.SERIALIZERS={json:1};c.prototype.STATUS={CLOSED:-1,UNINITIATED:0,NEGOCIATING:1,NEGOCIATED:2,RXHEAD:3,RXDATA:4,RXPING:5,RXPONG:6};c.prototype.ERRORS={0:"illegal (must not be used)",1:"serializer unsupported",2:"maximum message length unacceptable",3:"use of reserved bits (unsupported feature)",4:"maximum connection count reached"}; +c.prototype.MSGTYPES={WAMP:0,PING:1,PONG:2};c.prototype.DEFAULT_OPTIONS={fail_on_ping_timeout:!0,strict_pong:!0,ping_timeout:2E3,autoping:0,max_len_exp:24,serializer:"json",packet_timeout:2E3};c.prototype.close=function(){this._status=this.STATUS.CLOSED;this._stream.close();return this.STATUS.CLOSED};c.prototype.write=function(c,b,e){b=void 0===b?0:b;b===this.MSGTYPES.WAMP&&(c=JSON.stringify(c));var f=a.byteLength(c,"utf8");if(f>Math.pow(2,this._options._peer_max_len_exp))this._emitter.emit("error", +new k("Frame too big"));else{var g=new a(f+4);g.writeUInt8(b,0);g.writeUIntBE(f,1,3);g.write(c,4);this._stream.write(g,e)}};c.prototype.ping=function(a){a=a||255;if(Number.isInteger(a))for(var c=Math.max(1,a),b=0;b".charAt(92*Math.random()|0);this._ping_payload=a;return this.write(a,this.MSGTYPES.PING,this._setupPingTimeout.bind(this))};c.prototype._setupPingTimeout=function(){this._options.ping_timeout&& +(this._ping_timeout=setTimeout(this._onPingTimeout.bind(this),this._options.ping_timeout))};c.prototype._clearPingTimeout=function(){this._ping_timeout&&(clearTimeout(this._ping_timeout),this._ping_timeout=null)};c.prototype._setupAutoPing=function(){this._clearAutoPing();this._options.autoping&&(this._autoping_interval=setInterval(this.ping.bind(this),this._options.autoping))};c.prototype._clearAutoPing=function(){this._autoping_interval&&(clearInterval(this._autoping_interval),this._autoping_interval= +null)};c.prototype._onPingTimeout=function(){this._emitter.emit("error",new k("PING timeout"));this._options.fail_on_ping_timeout&&this.close()};c.prototype._read=function(a){var c,b;switch(this._status){case this.STATUS.CLOSED:case this.STATUS.UNINITIATED:this._emitter.emit("error",k("Unexpected packet"));break;case this.STATUS.NEGOCIATING:c=this._handleHandshake;b=4;break;case this.STATUS.NEGOCIATED:case this.STATUS.RXHEAD:this._status=this.STATUS.RXHEAD;c=this._handleHeaderPacket;b=4;break;case this.STATUS.RXDATA:c= +this._handleDataPacket;b=this._msgLen;break;case this.STATUS.RXPING:c=this._handlePingPacket;b=this._msgLen;break;case this.STATUS.RXPONG:c=this._handlePongPacket,b=this._msgLen}if(a=this._splitBytes(a,b))this._status=c.call(this,a[0]),0>4,this._emitter.emit("error",new k("Peer failed handshake: "+(this.ERRORS[a]||"0x"+a.toString(16)))),this.close();this._options._peer_max_len_exp=(a[1]>>4)+9;this._options._peer_serializer=a[1]&15;if(this._options._peer_serializer!==this.SERIALIZERS.json)return this._emitter.emit("error",new k("Unsupported serializer: 0x"+this._options._peer_serializer.toString(16))),this.close();this._emitter.emit("connect"); +this._setupAutoPing();return this.STATUS.NEGOCIATED};c.prototype._handleHeaderPacket=function(a){var c=a[0]&15;this._msgLen=a.readUIntBE(1,3);switch(c){case this.MSGTYPES.WAMP:return this.STATUS.RXDATA;case this.MSGTYPES.PING:return this.STATUS.RXPING;case this.MSGTYPES.PONG:return this.STATUS.RXPONG;default:return this._emitter.emit("error",new k("Invalid frame type: 0x"+status.toString(16))),this.close()}};c.prototype._handleDataPacket=function(a){var c;try{c=JSON.parse(a.toString("utf8"))}catch(b){return this._emitter.emit("error", +new k("Invalid JSON frame")),this.STATUS.RXHEAD}this._emitter.emit("data",c);return this.STATUS.RXHEAD};c.prototype._handlePingPacket=function(a){this.write(a.toString("utf8"),this.MSGTYPES.PONG);return this.STATUS.RXHEAD};c.prototype._handlePongPacket=function(a){this._clearPingTimeout();return this._options.strict_pong&&this._ping_payload!==a.toString("utf8")?(this._emitter.emit("error",new k("PONG response payload doesn't match PING.")),this.close()):this.STATUS.RXHEAD};c.prototype.on=function(a, +c){return this._emitter.on(a,c)};c.prototype.once=function(a,c){return this._emitter.once(a,c)};c.prototype.removeListener=function(a,c){return this._emitter.removeListener(a,c)};var k=l.ProtocolError=function(a){Error.apply(this,Array.prototype.splice.call(arguments));Error.captureStackTrace(this,this.constructor);this.message=a;this.name="ProtocolError"};k.prototype=Object.create(Error.prototype);l.Factory=g;l.Protocol=c}).call(this,"undefined"!==typeof global?global:"undefined"!==typeof self?self: +"undefined"!==typeof window?window:{},h("buffer").Buffer)},{"../log.js":6,"../util.js":19,buffer:82,events:86,net:81}],18:[function(h,n,l){(function(b){function a(a){g.assert(void 0!==a.url,"options.url missing");g.assert("string"===typeof a.url,"options.url must be a string");a.protocols?g.assert(Array.isArray(a.protocols),"options.protocols must be an array"):a.protocols=["wamp.2.json"];this._options=a}var g=h("../util.js"),c=h("../log.js");a.prototype.type="websocket";a.prototype.create=function(){var a= +this,f={protocol:void 0,send:void 0,close:void 0,onmessage:function(){},onopen:function(){},onclose:function(){},info:{type:"websocket",url:null,protocol:"wamp.2.json"}};b.process&&b.process.versions.node?function(){var c=h("ws"),b,g;a._options.protocols?(g=a._options.protocols,Array.isArray(g)&&(g=g.join(",")),b=new c(a._options.url,{protocol:g})):b=new c(a._options.url);f.send=function(a){a=JSON.stringify(a);b.send(a,{binary:!1})};f.close=function(a,c){b.close()};b.on("open",function(){f.onopen()}); +b.on("message",function(a,c){if(!c.binary){var b=JSON.parse(a);f.onmessage(b)}});b.on("close",function(a,c){f.onclose({code:a,reason:c,wasClean:1E3===a})});b.on("error",function(a){f.onclose({code:1006,reason:"",wasClean:!1})})}():function(){var g;if("WebSocket"in b)g=a._options.protocols?new b.WebSocket(a._options.url,a._options.protocols):new b.WebSocket(a._options.url);else if("MozWebSocket"in b)g=a._options.protocols?new b.MozWebSocket(a._options.url,a._options.protocols):new b.MozWebSocket(a._options.url); +else throw"browser does not support WebSocket or WebSocket in Web workers";g.onmessage=function(a){c.debug("WebSocket transport receive",a.data);a=JSON.parse(a.data);f.onmessage(a)};g.onopen=function(){f.info.url=a._options.url;f.onopen()};g.onclose=function(a){f.onclose({code:a.code,reason:a.message,wasClean:a.wasClean})};f.send=function(a){a=JSON.stringify(a);c.debug("WebSocket transport send",a);g.send(a)};f.close=function(a,c){g.close(a,c)}}();return f};l.Factory=a}).call(this,"undefined"!==typeof global? +global:"undefined"!==typeof self?self:"undefined"!==typeof window?window:{})},{"../log.js":6,"../util.js":19,ws:79}],19:[function(h,n,l){(function(b){var a=h("./log.js"),g=h("when"),c=function(a,e){if(!a){if(c.useDebugger||"AUTOBAHN_DEBUG"in b&&AUTOBAHN_DEBUG)debugger;throw Error(e||"Assertion failed!");}},e=function(){if(0===arguments.length)return{};var a=arguments[0],c=!1,b=arguments.length;"boolean"===typeof arguments[b-1]&&(c=arguments[b-1],--b);var g=function(b){var d=z[b];b in a?c&&"object"=== +typeof d&&"object"===typeof a[b]&&e(a[b],d):a[b]=d},d=1;for(;db;b++)a[b]=128>b?b<<1:b<<1^283;for(var g=0,m=0,b=0;256>b;b++){var v=m^m<<1^m<<2^m<<3^m<<4,v=v>>>8^v&255^99;c[g]=v; +e[v]=g;var l=a[g],n=a[l],F=a[n],E=257*a[v]^16843008*v;f[g]=E<<24|E>>>8;q[g]=E<<16|E>>>16;k[g]=E<<8|E>>>24;r[g]=E;E=16843009*F^65537*n^257*l^16843008*g;d[v]=E<<24|E>>>8;z[v]=E<<16|E>>>16;h[v]=E<<8|E>>>24;B[v]=E;g?(g=l^a[a[a[F^l]]],m^=a[a[m]]):g=m=1}})();var v=[0,1,2,4,8,16,32,64,128,27,54],g=g.AES=a.extend({_doReset:function(){for(var a=this._key,b=a.words,e=a.sigBytes/4,a=4*((this._nRounds=e+6)+1),f=this._keySchedule=[],k=0;k>>24]<< +24|c[g>>>16&255]<<16|c[g>>>8&255]<<8|c[g&255]):(g=g<<8|g>>>24,g=c[g>>>24]<<24|c[g>>>16&255]<<16|c[g>>>8&255]<<8|c[g&255],g^=v[k/e|0]<<24);f[k]=f[k-e]^g}b=this._invKeySchedule=[];for(e=0;ee||4>=k?g:d[c[g>>>24]]^z[c[g>>>16&255]]^h[c[g>>>8&255]]^B[c[g&255]]},encryptBlock:function(a,b){this._doCryptBlock(a,b,this._keySchedule,f,q,k,r,c)},decryptBlock:function(a,c){var b=a[c+1];a[c+1]=a[c+3];a[c+3]=b;this._doCryptBlock(a,c,this._invKeySchedule,d,z,h,B,e);b=a[c+1]; +a[c+1]=a[c+3];a[c+3]=b},_doCryptBlock:function(a,c,b,d,e,f,k,g){for(var r=this._nRounds,z=a[c]^b[0],q=a[c+1]^b[1],v=a[c+2]^b[2],h=a[c+3]^b[3],p=4,I=1;I>>24]^e[q>>>16&255]^f[v>>>8&255]^k[h&255]^b[p++],w=d[q>>>24]^e[v>>>16&255]^f[h>>>8&255]^k[z&255]^b[p++],t=d[v>>>24]^e[h>>>16&255]^f[z>>>8&255]^k[q&255]^b[p++],h=d[h>>>24]^e[z>>>16&255]^f[q>>>8&255]^k[v&255]^b[p++],z=u,q=w,v=t;u=(g[z>>>24]<<24|g[q>>>16&255]<<16|g[v>>>8&255]<<8|g[h&255])^b[p++];w=(g[q>>>24]<<24|g[v>>>16&255]<<16|g[h>>> +8&255]<<8|g[z&255])^b[p++];t=(g[v>>>24]<<24|g[h>>>16&255]<<16|g[z>>>8&255]<<8|g[q&255])^b[p++];h=(g[h>>>24]<<24|g[z>>>16&255]<<16|g[q>>>8&255]<<8|g[v&255])^b[p++];a[c]=u;a[c+1]=w;a[c+2]=t;a[c+3]=h},keySize:8});b.AES=a._createHelper(g)})();return b.AES})},{"./cipher-core":21,"./core":22,"./enc-base64":23,"./evpkdf":25,"./md5":30}],21:[function(h,n,l){(function(b,a){"object"===typeof l?n.exports=l=a(h("./core")):a(b.CryptoJS)})(this,function(b){b.lib.Cipher||function(a){var g=b.lib,c=g.Base,e=g.WordArray, +f=g.BufferedBlockAlgorithm,q=b.enc.Base64,k=b.algo.EvpKDF,r=g.Cipher=f.extend({cfg:c.extend(),createEncryptor:function(a,c){return this.create(this._ENC_XFORM_MODE,a,c)},createDecryptor:function(a,c){return this.create(this._DEC_XFORM_MODE,a,c)},init:function(a,c,b){this.cfg=this.cfg.extend(b);this._xformMode=a;this._key=c;this.reset()},reset:function(){f.reset.call(this);this._doReset()},process:function(a){this._append(a);return this._process()},finalize:function(a){a&&this._append(a);return this._doFinalize()}, +keySize:4,ivSize:4,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:function(){return function(a){return{encrypt:function(c,b,d){return("string"==typeof b?D:v).encrypt(a,c,b,d)},decrypt:function(c,b,d){return("string"==typeof b?D:v).decrypt(a,c,b,d)}}}}()});g.StreamCipher=r.extend({_doFinalize:function(){return this._process(!0)},blockSize:1});var d=b.mode={},z=g.BlockCipherMode=c.extend({createEncryptor:function(a,c){return this.Encryptor.create(a,c)},createDecryptor:function(a,c){return this.Decryptor.create(a, +c)},init:function(a,c){this._cipher=a;this._iv=c}}),d=d.CBC=function(){function c(b,d,e){var f=this._iv;f?this._iv=a:f=this._prevBlock;for(var k=0;k>>2]&255}};g.BlockCipher=r.extend({cfg:r.cfg.extend({mode:d,padding:h}),reset:function(){r.reset.call(this);var a=this.cfg,c=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var b=a.createEncryptor;else b=a.createDecryptor,this._minBufferSize=1;this._mode=b.call(a,this,c&&c.words)},_doProcessBlock:function(a, +c){this._mode.processBlock(a,c)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var c=this._process(!0)}else c=this._process(!0),a.unpad(c);return c},blockSize:4});var B=g.CipherParams=c.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),d=(b.format={}).OpenSSL={stringify:function(a){var c=a.ciphertext;a=a.salt;return(a?e.create([1398893684,1701076831]).concat(a).concat(c): +c).toString(q)},parse:function(a){a=q.parse(a);var c=a.words;if(1398893684==c[0]&&1701076831==c[1]){var b=e.create(c.slice(2,4));c.splice(0,4);a.sigBytes-=16}return B.create({ciphertext:a,salt:b})}},v=g.SerializableCipher=c.extend({cfg:c.extend({format:d}),encrypt:function(a,c,b,d){d=this.cfg.extend(d);var e=a.createEncryptor(b,d);c=e.finalize(c);e=e.cfg;return B.create({ciphertext:c,key:b,iv:e.iv,algorithm:a,mode:e.mode,padding:e.padding,blockSize:a.blockSize,formatter:d.format})},decrypt:function(a, +c,b,d){d=this.cfg.extend(d);c=this._parse(c,d.format);return a.createDecryptor(b,d).finalize(c.ciphertext)},_parse:function(a,c){return"string"==typeof a?c.parse(a,this):a}}),c=(b.kdf={}).OpenSSL={execute:function(a,c,b,d){d||(d=e.random(8));a=k.create({keySize:c+b}).compute(a,d);b=e.create(a.words.slice(c),4*b);a.sigBytes=4*c;return B.create({key:a,iv:b,salt:d})}},D=g.PasswordBasedCipher=v.extend({cfg:v.cfg.extend({kdf:c}),encrypt:function(a,c,b,d){d=this.cfg.extend(d);b=d.kdf.execute(b,a.keySize, +a.ivSize);d.iv=b.iv;a=v.encrypt.call(this,a,c,b.key,d);a.mixIn(b);return a},decrypt:function(a,c,b,d){d=this.cfg.extend(d);c=this._parse(c,d.format);b=d.kdf.execute(b,a.keySize,a.ivSize,c.salt);d.iv=b.iv;return v.decrypt.call(this,a,c,b.key,d)}})}()})},{"./core":22}],22:[function(h,n,l){(function(b,a){"object"===typeof l?n.exports=l=a():b.CryptoJS=a()})(this,function(){var b=b||function(a,b){var c={},e=c.lib={},f=e.Base=function(){function a(){}return{extend:function(c){a.prototype=this;var b=new a; +c&&b.mixIn(c);b.hasOwnProperty("init")||(b.init=function(){b.$super.init.apply(this,arguments)});b.init.prototype=b;b.$super=this;return b},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}}}(),q=e.WordArray=f.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=b? +c:4*a.length},toString:function(a){return(a||r).stringify(this)},concat:function(a){var c=this.words,b=a.words,d=this.sigBytes;a=a.sigBytes;this.clamp();if(d%4)for(var e=0;e>>2]|=(b[e>>>2]>>>24-e%4*8&255)<<24-(d+e)%4*8;else for(e=0;e>>2]=b[e>>>2];this.sigBytes+=a;return this},clamp:function(){var c=this.words,b=this.sigBytes;c[b>>>2]&=4294967295<<32-b%4*8;c.length=a.ceil(b/4)},clone:function(){var a=f.clone.call(this);a.words=this.words.slice(0);return a},random:function(c){for(var b= +[],d=function(c){var b=987654321;return function(){b=36969*(b&65535)+(b>>16)&4294967295;c=18E3*(c&65535)+(c>>16)&4294967295;var d=(b<<16)+c&4294967295,d=d/4294967296+.5;return d*(.5>>2]>>>24-d%4*8&255;b.push((e>>>4).toString(16));b.push((e&15).toString(16))}return b.join("")}, +parse:function(a){for(var c=a.length,b=[],d=0;d>>3]|=parseInt(a.substr(d,2),16)<<24-d%8*4;return new q.init(b,c/2)}},d=k.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],d=0;d>>2]>>>24-d%4*8&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],d=0;d>>2]|=(a.charCodeAt(d)&255)<<24-d%4*8;return new q.init(b,c)}},z=k.Utf8={stringify:function(a){try{return decodeURIComponent(escape(d.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data"); +}},parse:function(a){return d.parse(unescape(encodeURIComponent(a)))}},h=e.BufferedBlockAlgorithm=f.extend({reset:function(){this._data=new q.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=z.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(c){var b=this._data,d=b.words,e=b.sigBytes,f=this.blockSize,k=e/(4*f),k=c?a.ceil(k):a.max((k|0)-this._minBufferSize,0);c=k*f;e=a.min(4*c,e);if(c){for(var g=0;g>>2]>>>24-q%4*8&255)<<16|(c[q+1>>>2]>>>24-(q+1)%4*8&255)<<8|c[q+2>>>2]>>>24-(q+2)%4*8&255,r=0;4>r&&q+.75*r>> +6*(3-r)&63));if(c=f.charAt(64))for(;a.length%4;)a.push(c);return a.join("")},parse:function(b){var c=b.length,e=this._map,f=e.charAt(64);f&&(f=b.indexOf(f),-1!=f&&(c=f));for(var f=[],q=0,k=0;k>>6-k%4*2;f[q>>>2]|=(r|d)<<24-q%4*8;q++}return a.create(f,q)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();return b.enc.Base64})},{"./core":22}],24:[function(h,n,l){(function(b,a){"object"===typeof l? +n.exports=l=a(h("./core")):a(b.CryptoJS)})(this,function(b){(function(){function a(a){return a<<8&4278255360|a>>>8&16711935}var g=b.lib.WordArray,c=b.enc;c.Utf16=c.Utf16BE={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],k=0;k>>2]>>>16-k%4*8&65535));return b.join("")},parse:function(a){for(var c=a.length,b=[],k=0;k>>1]|=a.charCodeAt(k)<<16-k%2*16;return g.create(b,2*c)}};c.Utf16LE={stringify:function(c){var b=c.words;c=c.sigBytes;for(var g= +[],k=0;k>>2]>>>16-k%4*8&65535);g.push(String.fromCharCode(r))}return g.join("")},parse:function(c){for(var b=c.length,q=[],k=0;k>>1]|=a(c.charCodeAt(k)<<16-k%2*16);return g.create(q,2*b)}}})();return b.enc.Utf16})},{"./core":22}],25:[function(h,n,l){(function(b,a,g){"object"===typeof l?n.exports=l=a(h("./core"),h("./sha1"),h("./hmac")):a(b.CryptoJS)})(this,function(b){(function(){var a=b.lib,g=a.Base,c=a.WordArray,a=b.algo,e=a.EvpKDF=g.extend({cfg:g.extend({keySize:4, +hasher:a.MD5,iterations:1}),init:function(a){this.cfg=this.cfg.extend(a)},compute:function(a,b){for(var e=this.cfg,g=e.hasher.create(),d=c.create(),z=d.words,h=e.keySize,e=e.iterations;z.lengthf&&(c=b.finalize(c));c.clamp();for(var q=this._oKey=c.clone(),k=this._iKey=c.clone(),r=q.words,d=k.words,z=0;z>>2]|=a[q]<<24-q%4*8;g.call(this,f,b)}else g.apply(this,arguments)}).prototype=a}})();return b.lib.WordArray})},{"./core":22}],30:[function(h,n,l){(function(b,a){"object"===typeof l?n.exports= +l=a(h("./core")):a(b.CryptoJS)})(this,function(b){(function(a){function g(a,c,b,d,e,f,k){a=a+(c&b|~c&d)+e+k;return(a<>>32-f)+c}function c(a,c,b,d,e,f,k){a=a+(c&d|b&~d)+e+k;return(a<>>32-f)+c}function e(a,c,b,d,e,f,k){a=a+(c^b^d)+e+k;return(a<>>32-f)+c}function f(a,c,b,d,e,f,k){a=a+(b^(c|~d))+e+k;return(a<>>32-f)+c}var q=b.lib,k=q.WordArray,r=q.Hasher,q=b.algo,d=[];(function(){for(var c=0;64>c;c++)d[c]=4294967296*a.abs(a.sin(c+1))|0})();q=q.MD5=r.extend({_doReset:function(){this._hash= +new k.init([1732584193,4023233417,2562383102,271733878])},_doProcessBlock:function(a,b){for(var k=0;16>k;k++){var r=b+k,q=a[r];a[r]=(q<<8|q>>>24)&16711935|(q<<24|q>>>8)&4278255360}var k=this._hash.words,r=a[b+0],q=a[b+1],h=a[b+2],l=a[b+3],m=a[b+4],n=a[b+5],A=a[b+6],M=a[b+7],F=a[b+8],E=a[b+9],L=a[b+10],N=a[b+11],y=a[b+12],R=a[b+13],p=a[b+14],I=a[b+15],u=k[0],w=k[1],t=k[2],x=k[3],u=g(u,w,t,x,r,7,d[0]),x=g(x,u,w,t,q,12,d[1]),t=g(t,x,u,w,h,17,d[2]),w=g(w,t,x,u,l,22,d[3]),u=g(u,w,t,x,m,7,d[4]),x=g(x,u, +w,t,n,12,d[5]),t=g(t,x,u,w,A,17,d[6]),w=g(w,t,x,u,M,22,d[7]),u=g(u,w,t,x,F,7,d[8]),x=g(x,u,w,t,E,12,d[9]),t=g(t,x,u,w,L,17,d[10]),w=g(w,t,x,u,N,22,d[11]),u=g(u,w,t,x,y,7,d[12]),x=g(x,u,w,t,R,12,d[13]),t=g(t,x,u,w,p,17,d[14]),w=g(w,t,x,u,I,22,d[15]),u=c(u,w,t,x,q,5,d[16]),x=c(x,u,w,t,A,9,d[17]),t=c(t,x,u,w,N,14,d[18]),w=c(w,t,x,u,r,20,d[19]),u=c(u,w,t,x,n,5,d[20]),x=c(x,u,w,t,L,9,d[21]),t=c(t,x,u,w,I,14,d[22]),w=c(w,t,x,u,m,20,d[23]),u=c(u,w,t,x,E,5,d[24]),x=c(x,u,w,t,p,9,d[25]),t=c(t,x,u,w,l,14,d[26]), +w=c(w,t,x,u,F,20,d[27]),u=c(u,w,t,x,R,5,d[28]),x=c(x,u,w,t,h,9,d[29]),t=c(t,x,u,w,M,14,d[30]),w=c(w,t,x,u,y,20,d[31]),u=e(u,w,t,x,n,4,d[32]),x=e(x,u,w,t,F,11,d[33]),t=e(t,x,u,w,N,16,d[34]),w=e(w,t,x,u,p,23,d[35]),u=e(u,w,t,x,q,4,d[36]),x=e(x,u,w,t,m,11,d[37]),t=e(t,x,u,w,M,16,d[38]),w=e(w,t,x,u,L,23,d[39]),u=e(u,w,t,x,R,4,d[40]),x=e(x,u,w,t,r,11,d[41]),t=e(t,x,u,w,l,16,d[42]),w=e(w,t,x,u,A,23,d[43]),u=e(u,w,t,x,E,4,d[44]),x=e(x,u,w,t,y,11,d[45]),t=e(t,x,u,w,I,16,d[46]),w=e(w,t,x,u,h,23,d[47]),u=f(u, +w,t,x,r,6,d[48]),x=f(x,u,w,t,M,10,d[49]),t=f(t,x,u,w,p,15,d[50]),w=f(w,t,x,u,n,21,d[51]),u=f(u,w,t,x,y,6,d[52]),x=f(x,u,w,t,l,10,d[53]),t=f(t,x,u,w,L,15,d[54]),w=f(w,t,x,u,q,21,d[55]),u=f(u,w,t,x,F,6,d[56]),x=f(x,u,w,t,I,10,d[57]),t=f(t,x,u,w,A,15,d[58]),w=f(w,t,x,u,R,21,d[59]),u=f(u,w,t,x,m,6,d[60]),x=f(x,u,w,t,N,10,d[61]),t=f(t,x,u,w,h,15,d[62]),w=f(w,t,x,u,E,21,d[63]);k[0]=k[0]+u|0;k[1]=k[1]+w|0;k[2]=k[2]+t|0;k[3]=k[3]+x|0},_doFinalize:function(){var c=this._data,b=c.words,d=8*this._nDataBytes, +e=8*c.sigBytes;b[e>>>5]|=128<<24-e%32;var f=a.floor(d/4294967296);b[(e+64>>>9<<4)+15]=(f<<8|f>>>24)&16711935|(f<<24|f>>>8)&4278255360;b[(e+64>>>9<<4)+14]=(d<<8|d>>>24)&16711935|(d<<24|d>>>8)&4278255360;c.sigBytes=4*(b.length+1);this._process();c=this._hash;b=c.words;for(d=0;4>d;d++)e=b[d],b[d]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360;return c},clone:function(){var a=r.clone.call(this);a._hash=this._hash.clone();return a}});b.MD5=r._createHelper(q);b.HmacMD5=r._createHmacHelper(q)})(Math);return b.MD5})}, +{"./core":22}],31:[function(h,n,l){(function(b,a,g){"object"===typeof l?n.exports=l=a(h("./core"),h("./cipher-core")):a(b.CryptoJS)})(this,function(b){b.mode.CFB=function(){function a(a,b,f,g){var k=this._iv;k?(k=k.slice(0),this._iv=void 0):k=this._prevBlock;g.encryptBlock(k,0);for(g=0;g>24&255)){var b=a>>16&255,c=a>>8&255,k=a&255;255===b?(b=0,255===c?(c=0,255===k?k=0:++k):++c):++b;a=0+(b<<16)+(c<<8);a+=k}else a+=16777216;return a}var g= +b.lib.BlockCipherMode.extend(),c=g.Encryptor=g.extend({processBlock:function(b,c){var g=this._cipher,k=g.blockSize,r=this._iv,d=this._counter;r&&(d=this._counter=r.slice(0),this._iv=void 0);r=d;0===(r[0]=a(r[0]))&&(r[1]=a(r[1]));d=d.slice(0);g.encryptBlock(d,0);for(g=0;g>>2]|=e<<24-c%4*8;a.sigBytes+=e},unpad:function(a){a.sigBytes-=a.words[a.sigBytes-1>>>2]&255}};return b.pad.Ansix923})},{"./cipher-core":21,"./core":22}],37:[function(h,n,l){(function(b,a,g){"object"===typeof l?n.exports=l=a(h("./core"),h("./cipher-core")):a(b.CryptoJS)})(this,function(b){b.pad.Iso10126={pad:function(a,g){var c=4*g,c=c-a.sigBytes%c;a.concat(b.lib.WordArray.random(c-1)).concat(b.lib.WordArray.create([c<<24],1))},unpad:function(a){a.sigBytes-=a.words[a.sigBytes- +1>>>2]&255}};return b.pad.Iso10126})},{"./cipher-core":21,"./core":22}],38:[function(h,n,l){(function(b,a,g){"object"===typeof l?n.exports=l=a(h("./core"),h("./cipher-core")):a(b.CryptoJS)})(this,function(b){b.pad.Iso97971={pad:function(a,g){a.concat(b.lib.WordArray.create([2147483648],1));b.pad.ZeroPadding.pad(a,g)},unpad:function(a){b.pad.ZeroPadding.unpad(a);a.sigBytes--}};return b.pad.Iso97971})},{"./cipher-core":21,"./core":22}],39:[function(h,n,l){(function(b,a,g){"object"===typeof l?n.exports= +l=a(h("./core"),h("./cipher-core")):a(b.CryptoJS)})(this,function(b){b.pad.NoPadding={pad:function(){},unpad:function(){}};return b.pad.NoPadding})},{"./cipher-core":21,"./core":22}],40:[function(h,n,l){(function(b,a,g){"object"===typeof l?n.exports=l=a(h("./core"),h("./cipher-core")):a(b.CryptoJS)})(this,function(b){b.pad.ZeroPadding={pad:function(a,b){var c=4*b;a.clamp();a.sigBytes+=c-(a.sigBytes%c||c)},unpad:function(a){for(var b=a.words,c=a.sigBytes-1;!(b[c>>>2]>>>24-c%4*8&255);)c--;a.sigBytes= +c+1}};return b.pad.ZeroPadding})},{"./cipher-core":21,"./core":22}],41:[function(h,n,l){(function(b,a,g){"object"===typeof l?n.exports=l=a(h("./core"),h("./sha1"),h("./hmac")):a(b.CryptoJS)})(this,function(b){(function(){var a=b.lib,g=a.Base,c=a.WordArray,a=b.algo,e=a.HMAC,f=a.PBKDF2=g.extend({cfg:g.extend({keySize:4,hasher:a.SHA1,iterations:1}),init:function(a){this.cfg=this.cfg.extend(a)},compute:function(a,b){for(var f=this.cfg,d=e.create(f.hasher,a),g=c.create(),h=c.create([1]),l=g.words,v=h.words, +D=f.keySize,f=f.iterations;l.lengthc;c++)e[c]=b[c];b[0]=b[0]+1295307597+this._b|0;b[1]=b[1]+3545052371+(b[0]>>>0>>0?1:0)|0;b[2]=b[2]+886263092+(b[1]>>>0>>0?1:0)|0;b[3]=b[3]+1295307597+(b[2]>>>0>>0?1:0)|0;b[4]=b[4]+3545052371+(b[3]>>>0>>0?1:0)|0;b[5]=b[5]+886263092+(b[4]>>>0>>0?1:0)|0;b[6]=b[6]+1295307597+(b[5]>>>0>>0?1:0)|0;b[7]=b[7]+3545052371+(b[6]>>>0>>0?1:0)|0;this._b=b[7]>>>0>>0?1:0;for(c= +0;8>c;c++){var g=a[c]+b[c],h=g&65535,q=g>>>16;f[c]=((h*h>>>17)+h*q>>>15)+q*q^((g&4294901760)*g|0)+((g&65535)*g|0)}a[0]=f[0]+(f[7]<<16|f[7]>>>16)+(f[6]<<16|f[6]>>>16)|0;a[1]=f[1]+(f[0]<<8|f[0]>>>24)+f[7]|0;a[2]=f[2]+(f[1]<<16|f[1]>>>16)+(f[0]<<16|f[0]>>>16)|0;a[3]=f[3]+(f[2]<<8|f[2]>>>24)+f[1]|0;a[4]=f[4]+(f[3]<<16|f[3]>>>16)+(f[2]<<16|f[2]>>>16)|0;a[5]=f[5]+(f[4]<<8|f[4]>>>24)+f[3]|0;a[6]=f[6]+(f[5]<<16|f[5]>>>16)+(f[4]<<16|f[4]>>>16)|0;a[7]=f[7]+(f[6]<<8|f[6]>>>24)+f[5]|0}var g=b.lib.StreamCipher, +c=[],e=[],f=[],h=b.algo.RabbitLegacy=g.extend({_doReset:function(){for(var b=this._key.words,c=this.cfg.iv,d=this._X=[b[0],b[3]<<16|b[2]>>>16,b[1],b[0]<<16|b[3]>>>16,b[2],b[1]<<16|b[0]>>>16,b[3],b[2]<<16|b[1]>>>16],b=this._C=[b[2]<<16|b[2]>>>16,b[0]&4294901760|b[1]&65535,b[3]<<16|b[3]>>>16,b[1]&4294901760|b[2]&65535,b[0]<<16|b[0]>>>16,b[2]&4294901760|b[3]&65535,b[1]<<16|b[1]>>>16,b[3]&4294901760|b[0]&65535],e=this._b=0;4>e;e++)a.call(this);for(e=0;8>e;e++)b[e]^=d[e+4&7];if(c){var d=c.words,c=d[0], +d=d[1],c=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360,d=(d<<8|d>>>24)&16711935|(d<<24|d>>>8)&4278255360,e=c>>>16|d&4294901760,f=d<<16|c&65535;b[0]^=c;b[1]^=e;b[2]^=d;b[3]^=f;b[4]^=c;b[5]^=e;b[6]^=d;b[7]^=f;for(e=0;4>e;e++)a.call(this)}},_doProcessBlock:function(b,e){var d=this._X;a.call(this);c[0]=d[0]^d[5]>>>16^d[3]<<16;c[1]=d[2]^d[7]>>>16^d[5]<<16;c[2]=d[4]^d[1]>>>16^d[7]<<16;c[3]=d[6]^d[3]>>>16^d[1]<<16;for(d=0;4>d;d++)c[d]=(c[d]<<8|c[d]>>>24)&16711935|(c[d]<<24|c[d]>>>8)&4278255360,b[e+d]^= +c[d]},blockSize:4,ivSize:2});b.RabbitLegacy=g._createHelper(h)})();return b.RabbitLegacy})},{"./cipher-core":21,"./core":22,"./enc-base64":23,"./evpkdf":25,"./md5":30}],43:[function(h,n,l){(function(b,a,g){"object"===typeof l?n.exports=l=a(h("./core"),h("./enc-base64"),h("./md5"),h("./evpkdf"),h("./cipher-core")):a(b.CryptoJS)})(this,function(b){(function(){function a(){for(var a=this._X,b=this._C,c=0;8>c;c++)e[c]=b[c];b[0]=b[0]+1295307597+this._b|0;b[1]=b[1]+3545052371+(b[0]>>>0>>0?1:0)|0; +b[2]=b[2]+886263092+(b[1]>>>0>>0?1:0)|0;b[3]=b[3]+1295307597+(b[2]>>>0>>0?1:0)|0;b[4]=b[4]+3545052371+(b[3]>>>0>>0?1:0)|0;b[5]=b[5]+886263092+(b[4]>>>0>>0?1:0)|0;b[6]=b[6]+1295307597+(b[5]>>>0>>0?1:0)|0;b[7]=b[7]+3545052371+(b[6]>>>0>>0?1:0)|0;this._b=b[7]>>>0>>0?1:0;for(c=0;8>c;c++){var g=a[c]+b[c],h=g&65535,q=g>>>16;f[c]=((h*h>>>17)+h*q>>>15)+q*q^((g&4294901760)*g|0)+((g&65535)*g|0)}a[0]=f[0]+(f[7]<<16|f[7]>>>16)+(f[6]<<16|f[6]>>>16)|0;a[1]=f[1]+(f[0]<< +8|f[0]>>>24)+f[7]|0;a[2]=f[2]+(f[1]<<16|f[1]>>>16)+(f[0]<<16|f[0]>>>16)|0;a[3]=f[3]+(f[2]<<8|f[2]>>>24)+f[1]|0;a[4]=f[4]+(f[3]<<16|f[3]>>>16)+(f[2]<<16|f[2]>>>16)|0;a[5]=f[5]+(f[4]<<8|f[4]>>>24)+f[3]|0;a[6]=f[6]+(f[5]<<16|f[5]>>>16)+(f[4]<<16|f[4]>>>16)|0;a[7]=f[7]+(f[6]<<8|f[6]>>>24)+f[5]|0}var g=b.lib.StreamCipher,c=[],e=[],f=[],h=b.algo.Rabbit=g.extend({_doReset:function(){for(var b=this._key.words,c=this.cfg.iv,d=0;4>d;d++)b[d]=(b[d]<<8|b[d]>>>24)&16711935|(b[d]<<24|b[d]>>>8)&4278255360;for(var e= +this._X=[b[0],b[3]<<16|b[2]>>>16,b[1],b[0]<<16|b[3]>>>16,b[2],b[1]<<16|b[0]>>>16,b[3],b[2]<<16|b[1]>>>16],b=this._C=[b[2]<<16|b[2]>>>16,b[0]&4294901760|b[1]&65535,b[3]<<16|b[3]>>>16,b[1]&4294901760|b[2]&65535,b[0]<<16|b[0]>>>16,b[2]&4294901760|b[3]&65535,b[1]<<16|b[1]>>>16,b[3]&4294901760|b[0]&65535],d=this._b=0;4>d;d++)a.call(this);for(d=0;8>d;d++)b[d]^=e[d+4&7];if(c){var d=c.words,c=d[0],d=d[1],c=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360,d=(d<<8|d>>>24)&16711935|(d<<24|d>>>8)&4278255360,e= +c>>>16|d&4294901760,f=d<<16|c&65535;b[0]^=c;b[1]^=e;b[2]^=d;b[3]^=f;b[4]^=c;b[5]^=e;b[6]^=d;b[7]^=f;for(d=0;4>d;d++)a.call(this)}},_doProcessBlock:function(b,e){var d=this._X;a.call(this);c[0]=d[0]^d[5]>>>16^d[3]<<16;c[1]=d[2]^d[7]>>>16^d[5]<<16;c[2]=d[4]^d[1]>>>16^d[7]<<16;c[3]=d[6]^d[3]>>>16^d[1]<<16;for(d=0;4>d;d++)c[d]=(c[d]<<8|c[d]>>>24)&16711935|(c[d]<<24|c[d]>>>8)&4278255360,b[e+d]^=c[d]},blockSize:4,ivSize:2});b.Rabbit=g._createHelper(h)})();return b.Rabbit})},{"./cipher-core":21,"./core":22, +"./enc-base64":23,"./evpkdf":25,"./md5":30}],44:[function(h,n,l){(function(b,a,g){"object"===typeof l?n.exports=l=a(h("./core"),h("./enc-base64"),h("./md5"),h("./evpkdf"),h("./cipher-core")):a(b.CryptoJS)})(this,function(b){(function(){function a(){for(var a=this._S,b=this._i,c=this._j,e=0,d=0;4>d;d++){var b=(b+1)%256,c=(c+a[b])%256,g=a[b];a[b]=a[c];a[c]=g;e|=a[(a[b]+a[c])%256]<<24-8*d}this._i=b;this._j=c;return e}var g=b.lib.StreamCipher,c=b.algo,e=c.RC4=g.extend({_doReset:function(){for(var a=this._key, +b=a.words,a=a.sigBytes,c=this._S=[],e=0;256>e;e++)c[e]=e;for(var d=e=0;256>e;e++){var g=e%a,d=(d+c[e]+(b[g>>>2]>>>24-g%4*8&255))%256,g=c[e];c[e]=c[d];c[d]=g}this._i=this._j=0},_doProcessBlock:function(b,c){b[c]^=a.call(this)},keySize:8,ivSize:0});b.RC4=g._createHelper(e);c=c.RC4Drop=e.extend({cfg:e.cfg.extend({drop:192}),_doReset:function(){e._doReset.call(this);for(var b=this.cfg.drop;0>>32-b}a=b.lib;var c=a.WordArray,e=a.Hasher;a=b.algo;var f=c.create([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8,3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2,4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13]),h=c.create([5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12,6,11, +3,7,0,13,5,10,14,15,8,12,4,9,1,2,15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13,8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11]),k=c.create([11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8,7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12,11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12,9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6]),r=c.create([8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6,9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5,15,5,8, +11,14,14,6,14,6,9,12,9,12,5,15,8,8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11]),d=c.create([0,1518500249,1859775393,2400959708,2840853838]),z=c.create([1352829926,1548603684,1836072691,2053994217,0]);a=a.RIPEMD160=e.extend({_doReset:function(){this._hash=c.create([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(a,b){for(var c=0;16>c;c++){var e=b+c,l=a[e];a[e]=(l<<8|l>>>24)&16711935|(l<<24|l>>>8)&4278255360}var e=this._hash.words,l=d.words,n=z.words,m=f.words,H=h.words, +A=k.words,M=r.words,F,E,L,N,y,R,p,I,u,w;R=F=e[0];p=E=e[1];I=L=e[2];u=N=e[3];w=y=e[4];for(var t,c=0;80>c;c+=1)t=F+a[b+m[c]]|0,t=16>c?t+((E^L^N)+l[0]):32>c?t+((E&L|~E&N)+l[1]):48>c?t+(((E|~L)^N)+l[2]):64>c?t+((E&N|L&~N)+l[3]):t+((E^(L|~N))+l[4]),t|=0,t=g(t,A[c]),t=t+y|0,F=y,y=N,N=g(L,10),L=E,E=t,t=R+a[b+H[c]]|0,t=16>c?t+((p^(I|~u))+n[0]):32>c?t+((p&u|I&~u)+n[1]):48>c?t+(((p|~I)^u)+n[2]):64>c?t+((p&I|~p&u)+n[3]):t+((p^I^u)+n[4]),t|=0,t=g(t,M[c]),t=t+w|0,R=w,w=u,u=g(I,10),I=p,p=t;t=e[1]+L+u|0;e[1]=e[2]+ +N+w|0;e[2]=e[3]+y+R|0;e[3]=e[4]+F+p|0;e[4]=e[0]+E+I|0;e[0]=t},_doFinalize:function(){var a=this._data,b=a.words,c=8*this._nDataBytes,d=8*a.sigBytes;b[d>>>5]|=128<<24-d%32;b[(d+64>>>9<<4)+14]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360;a.sigBytes=4*(b.length+1);this._process();a=this._hash;b=a.words;for(c=0;5>c;c++)d=b[c],b[c]=(d<<8|d>>>24)&16711935|(d<<24|d>>>8)&4278255360;return a},clone:function(){var a=e.clone.call(this);a._hash=this._hash.clone();return a}});b.RIPEMD160=e._createHelper(a); +b.HmacRIPEMD160=e._createHmacHelper(a)})(Math);return b.RIPEMD160})},{"./core":22}],46:[function(h,n,l){(function(b,a){"object"===typeof l?n.exports=l=a(h("./core")):a(b.CryptoJS)})(this,function(b){(function(){var a=b.lib,g=a.WordArray,c=a.Hasher,e=[],a=b.algo.SHA1=c.extend({_doReset:function(){this._hash=new g.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(a,b){for(var c=this._hash.words,g=c[0],d=c[1],h=c[2],l=c[3],B=c[4],v=0;80>v;v++){if(16>v)e[v]=a[b+v]| +0;else{var D=e[v-3]^e[v-8]^e[v-14]^e[v-16];e[v]=D<<1|D>>>31}D=(g<<5|g>>>27)+B+e[v];D=20>v?D+((d&h|~d&l)+1518500249):40>v?D+((d^h^l)+1859775393):60>v?D+((d&h|d&l|h&l)-1894007588):D+((d^h^l)-899497514);B=l;l=h;h=d<<30|d>>>2;d=g;g=D}c[0]=c[0]+g|0;c[1]=c[1]+d|0;c[2]=c[2]+h|0;c[3]=c[3]+l|0;c[4]=c[4]+B|0},_doFinalize:function(){var a=this._data,b=a.words,c=8*this._nDataBytes,e=8*a.sigBytes;b[e>>>5]|=128<<24-e%32;b[(e+64>>>9<<4)+14]=Math.floor(c/4294967296);b[(e+64>>>9<<4)+15]=c;a.sigBytes=4*b.length;this._process(); +return this._hash},clone:function(){var a=c.clone.call(this);a._hash=this._hash.clone();return a}});b.SHA1=c._createHelper(a);b.HmacSHA1=c._createHmacHelper(a)})();return b.SHA1})},{"./core":22}],47:[function(h,n,l){(function(b,a,g){"object"===typeof l?n.exports=l=a(h("./core"),h("./sha256")):a(b.CryptoJS)})(this,function(b){(function(){var a=b.lib.WordArray,g=b.algo,c=g.SHA256,g=g.SHA224=c.extend({_doReset:function(){this._hash=new a.init([3238371032,914150663,812702999,4144912697,4290775857,1750603025, +1694076839,3204075428])},_doFinalize:function(){var a=c._doFinalize.call(this);a.sigBytes-=4;return a}});b.SHA224=c._createHelper(g);b.HmacSHA224=c._createHmacHelper(g)})();return b.SHA224})},{"./core":22,"./sha256":48}],48:[function(h,n,l){(function(b,a){"object"===typeof l?n.exports=l=a(h("./core")):a(b.CryptoJS)})(this,function(b){(function(a){var g=b.lib,c=g.WordArray,e=g.Hasher,g=b.algo,f=[],h=[];(function(){function b(c){for(var d=a.sqrt(c),e=2;e<=d;e++)if(!(c%e))return!1;return!0}function c(a){return 4294967296* +(a-(a|0))|0}for(var e=2,g=0;64>g;)b(e)&&(8>g&&(f[g]=c(a.pow(e,.5))),h[g]=c(a.pow(e,1/3)),g++),e++})();var k=[],g=g.SHA256=e.extend({_doReset:function(){this._hash=new c.init(f.slice(0))},_doProcessBlock:function(a,b){for(var c=this._hash.words,e=c[0],f=c[1],g=c[2],l=c[3],n=c[4],C=c[5],m=c[6],H=c[7],A=0;64>A;A++){if(16>A)k[A]=a[b+A]|0;else{var M=k[A-15],F=k[A-2];k[A]=((M<<25|M>>>7)^(M<<14|M>>>18)^M>>>3)+k[A-7]+((F<<15|F>>>17)^(F<<13|F>>>19)^F>>>10)+k[A-16]}M=H+((n<<26|n>>>6)^(n<<21|n>>>11)^(n<<7|n>>> +25))+(n&C^~n&m)+h[A]+k[A];F=((e<<30|e>>>2)^(e<<19|e>>>13)^(e<<10|e>>>22))+(e&f^e&g^f&g);H=m;m=C;C=n;n=l+M|0;l=g;g=f;f=e;e=M+F|0}c[0]=c[0]+e|0;c[1]=c[1]+f|0;c[2]=c[2]+g|0;c[3]=c[3]+l|0;c[4]=c[4]+n|0;c[5]=c[5]+C|0;c[6]=c[6]+m|0;c[7]=c[7]+H|0},_doFinalize:function(){var b=this._data,c=b.words,e=8*this._nDataBytes,f=8*b.sigBytes;c[f>>>5]|=128<<24-f%32;c[(f+64>>>9<<4)+14]=a.floor(e/4294967296);c[(f+64>>>9<<4)+15]=e;b.sigBytes=4*c.length;this._process();return this._hash},clone:function(){var a=e.clone.call(this); +a._hash=this._hash.clone();return a}});b.SHA256=e._createHelper(g);b.HmacSHA256=e._createHmacHelper(g)})(Math);return b.SHA256})},{"./core":22}],49:[function(h,n,l){(function(b,a,g){"object"===typeof l?n.exports=l=a(h("./core"),h("./x64-core")):a(b.CryptoJS)})(this,function(b){(function(a){var g=b.lib,c=g.WordArray,e=g.Hasher,f=b.x64.Word,g=b.algo,h=[],k=[],r=[];(function(){for(var a=1,b=0,c=0;24>c;c++){h[a+5*b]=(c+1)*(c+2)/2%64;var d=(2*a+3*b)%5,a=b%5,b=d}for(a=0;5>a;a++)for(b=0;5>b;b++)k[a+5*b]= +b+(2*a+3*b)%5*5;a=1;for(b=0;24>b;b++){for(var e=d=c=0;7>e;e++){if(a&1){var g=(1<g?d^=1<a;a++)d[a]=f.create()})();g=g.SHA3=e.extend({cfg:e.cfg.extend({outputLength:512}),_doReset:function(){for(var a=this._state=[],b=0;25>b;b++)a[b]=new f.init;this.blockSize=(1600-2*this.cfg.outputLength)/32},_doProcessBlock:function(a,b){for(var c=this._state,e=this.blockSize/2,f=0;f>>24)&16711935|(g<<24|g>>>8)&4278255360,l=(l<<8|l>>>24)&16711935|(l<<24|l>>>8)&4278255360,m=c[f];m.high^=l;m.low^=g}for(e=0;24>e;e++){for(f=0;5>f;f++){for(var n=g=0,A=0;5>A;A++)m=c[f+5*A],g^=m.high,n^=m.low;m=d[f];m.high=g;m.low=n}for(f=0;5>f;f++)for(m=d[(f+4)%5],g=d[(f+1)%5],l=g.high,A=g.low,g=m.high^(l<<1|A>>>31),n=m.low^(A<<1|l>>>31),A=0;5>A;A++)m=c[f+5*A],m.high^=g,m.low^=n;for(l=1;25>l;l++)m=c[l],f=m.high,m=m.low,A=h[l],32>A?(g=f<>>32-A,n=m<>>32-A):(g=m<>>64- +A,n=f<>>64-A),m=d[k[l]],m.high=g,m.low=n;m=d[0];f=c[0];m.high=f.high;m.low=f.low;for(f=0;5>f;f++)for(A=0;5>A;A++)l=f+5*A,m=c[l],g=d[l],l=d[(f+1)%5+5*A],n=d[(f+2)%5+5*A],m.high=g.high^~l.high&n.high,m.low=g.low^~l.low&n.low;m=c[0];f=r[e];m.high^=f.high;m.low^=f.low}},_doFinalize:function(){var b=this._data,d=b.words,e=8*b.sigBytes,f=32*this.blockSize;d[e>>>5]|=1<<24-e%32;d[(a.ceil((e+1)/f)*f>>>5)-1]|=128;b.sigBytes=4*d.length;this._process();for(var b=this._state,d=this.cfg.outputLength/8, +e=d/8,f=[],g=0;g>>24)&16711935|(h<<24|h>>>8)&4278255360,k=(k<<8|k>>>24)&16711935|(k<<24|k>>>8)&4278255360;f.push(k);f.push(h)}return new c.init(f,d)},clone:function(){for(var a=e.clone.call(this),b=a._state=this._state.slice(0),c=0;25>c;c++)b[c]=b[c].clone();return a}});b.SHA3=e._createHelper(g);b.HmacSHA3=e._createHmacHelper(g)})(Math);return b.SHA3})},{"./core":22,"./x64-core":53}],50:[function(h,n,l){(function(b,a,g){"object"===typeof l?n.exports=l= +a(h("./core"),h("./x64-core"),h("./sha512")):a(b.CryptoJS)})(this,function(b){(function(){var a=b.x64,g=a.Word,c=a.WordArray,a=b.algo,e=a.SHA512,a=a.SHA384=e.extend({_doReset:function(){this._hash=new c.init([new g.init(3418070365,3238371032),new g.init(1654270250,914150663),new g.init(2438529370,812702999),new g.init(355462360,4144912697),new g.init(1731405415,4290775857),new g.init(2394180231,1750603025),new g.init(3675008525,1694076839),new g.init(1203062813,3204075428)])},_doFinalize:function(){var a= +e._doFinalize.call(this);a.sigBytes-=16;return a}});b.SHA384=e._createHelper(a);b.HmacSHA384=e._createHmacHelper(a)})();return b.SHA384})},{"./core":22,"./sha512":51,"./x64-core":53}],51:[function(h,n,l){(function(b,a,g){"object"===typeof l?n.exports=l=a(h("./core"),h("./x64-core")):a(b.CryptoJS)})(this,function(b){(function(){function a(){return e.create.apply(e,arguments)}var g=b.lib.Hasher,c=b.x64,e=c.Word,f=c.WordArray,c=b.algo,h=[a(1116352408,3609767458),a(1899447441,602891725),a(3049323471, +3964484399),a(3921009573,2173295548),a(961987163,4081628472),a(1508970993,3053834265),a(2453635748,2937671579),a(2870763221,3664609560),a(3624381080,2734883394),a(310598401,1164996542),a(607225278,1323610764),a(1426881987,3590304994),a(1925078388,4068182383),a(2162078206,991336113),a(2614888103,633803317),a(3248222580,3479774868),a(3835390401,2666613458),a(4022224774,944711139),a(264347078,2341262773),a(604807628,2007800933),a(770255983,1495990901),a(1249150122,1856431235),a(1555081692,3175218132), +a(1996064986,2198950837),a(2554220882,3999719339),a(2821834349,766784016),a(2952996808,2566594879),a(3210313671,3203337956),a(3336571891,1034457026),a(3584528711,2466948901),a(113926993,3758326383),a(338241895,168717936),a(666307205,1188179964),a(773529912,1546045734),a(1294757372,1522805485),a(1396182291,2643833823),a(1695183700,2343527390),a(1986661051,1014477480),a(2177026350,1206759142),a(2456956037,344077627),a(2730485921,1290863460),a(2820302411,3158454273),a(3259730800,3505952657),a(3345764771, +106217008),a(3516065817,3606008344),a(3600352804,1432725776),a(4094571909,1467031594),a(275423344,851169720),a(430227734,3100823752),a(506948616,1363258195),a(659060556,3750685593),a(883997877,3785050280),a(958139571,3318307427),a(1322822218,3812723403),a(1537002063,2003034995),a(1747873779,3602036899),a(1955562222,1575990012),a(2024104815,1125592928),a(2227730452,2716904306),a(2361852424,442776044),a(2428436474,593698344),a(2756734187,3733110249),a(3204031479,2999351573),a(3329325298,3815920427), +a(3391569614,3928383900),a(3515267271,566280711),a(3940187606,3454069534),a(4118630271,4000239992),a(116418474,1914138554),a(174292421,2731055270),a(289380356,3203993006),a(460393269,320620315),a(685471733,587496836),a(852142971,1086792851),a(1017036298,365543100),a(1126000580,2618297676),a(1288033470,3409855158),a(1501505948,4234509866),a(1607167915,987167468),a(1816402316,1246189591)],k=[];(function(){for(var b=0;80>b;b++)k[b]=a()})();c=c.SHA512=g.extend({_doReset:function(){this._hash=new f.init([new e.init(1779033703, +4089235720),new e.init(3144134277,2227873595),new e.init(1013904242,4271175723),new e.init(2773480762,1595750129),new e.init(1359893119,2917565137),new e.init(2600822924,725511199),new e.init(528734635,4215389547),new e.init(1541459225,327033209)])},_doProcessBlock:function(a,b){for(var c=this._hash.words,e=c[0],f=c[1],g=c[2],l=c[3],n=c[4],C=c[5],m=c[6],c=c[7],H=e.high,A=e.low,M=f.high,F=f.low,E=g.high,L=g.low,N=l.high,y=l.low,R=n.high,p=n.low,I=C.high,u=C.low,w=m.high,t=m.low,x=c.high,S=c.low,P= +H,K=A,W=M,aa=F,ja=E,ha=L,na=N,da=y,T=R,U=p,la=I,ka=u,ia=w,fa=t,pa=x,ga=S,X=0;80>X;X++){var ba=k[X];if(16>X)var V=ba.high=a[b+2*X]|0,J=ba.low=a[b+2*X+1]|0;else{var V=k[X-15],J=V.high,Y=V.low,V=(J>>>1|Y<<31)^(J>>>8|Y<<24)^J>>>7,Y=(Y>>>1|J<<31)^(Y>>>8|J<<24)^(Y>>>7|J<<25),ea=k[X-2],J=ea.high,O=ea.low,ea=(J>>>19|O<<13)^(J<<3|O>>>29)^J>>>6,O=(O>>>19|J<<13)^(O<<3|J>>>29)^(O>>>6|J<<26),J=k[X-7],oa=J.high,ca=k[X-16],Z=ca.high,ca=ca.low,J=Y+J.low,V=V+oa+(J>>>0>>0?1:0),J=J+O,V=V+ea+(J>>>0>>0?1:0),J=J+ +ca,V=V+Z+(J>>>0>>0?1:0);ba.high=V;ba.low=J}var oa=T&la^~T&ia,ca=U&ka^~U&fa,ba=P&W^P&ja^W&ja,ra=K&aa^K&ha^aa&ha,Y=(P>>>28|K<<4)^(P<<30|K>>>2)^(P<<25|K>>>7),ea=(K>>>28|P<<4)^(K<<30|P>>>2)^(K<<25|P>>>7),O=h[X],sa=O.high,qa=O.low,O=ga+((U>>>14|T<<18)^(U>>>18|T<<14)^(U<<23|T>>>9)),Z=pa+((T>>>14|U<<18)^(T>>>18|U<<14)^(T<<23|U>>>9))+(O>>>0>>0?1:0),O=O+ca,Z=Z+oa+(O>>>0>>0?1:0),O=O+qa,Z=Z+sa+(O>>>0>>0?1:0),O=O+J,Z=Z+V+(O>>>0>>0?1:0),J=ea+ra,ba=Y+ba+(J>>>0>>0?1:0),pa=ia,ga=fa,ia=la,fa= +ka,la=T,ka=U,U=da+O|0,T=na+Z+(U>>>0>>0?1:0)|0,na=ja,da=ha,ja=W,ha=aa,W=P,aa=K,K=O+J|0,P=Z+ba+(K>>>0>>0?1:0)|0}A=e.low=A+K;e.high=H+P+(A>>>0>>0?1:0);F=f.low=F+aa;f.high=M+W+(F>>>0>>0?1:0);L=g.low=L+ha;g.high=E+ja+(L>>>0>>0?1:0);y=l.low=y+da;l.high=N+na+(y>>>0>>0?1:0);p=n.low=p+U;n.high=R+T+(p>>>0>>0?1:0);u=C.low=u+ka;C.high=I+la+(u>>>0>>0?1:0);t=m.low=t+fa;m.high=w+ia+(t>>>0>>0?1:0);S=c.low=S+ga;c.high=x+pa+(S>>>0>>0?1:0)},_doFinalize:function(){var a=this._data, +b=a.words,c=8*this._nDataBytes,e=8*a.sigBytes;b[e>>>5]|=128<<24-e%32;b[(e+128>>>10<<5)+30]=Math.floor(c/4294967296);b[(e+128>>>10<<5)+31]=c;a.sigBytes=4*b.length;this._process();return this._hash.toX32()},clone:function(){var a=g.clone.call(this);a._hash=this._hash.clone();return a},blockSize:32});b.SHA512=g._createHelper(c);b.HmacSHA512=g._createHmacHelper(c)})();return b.SHA512})},{"./core":22,"./x64-core":53}],52:[function(h,n,l){(function(b,a,g){"object"===typeof l?n.exports=l=a(h("./core"),h("./enc-base64"), +h("./md5"),h("./evpkdf"),h("./cipher-core")):a(b.CryptoJS)})(this,function(b){(function(){function a(a,b){var c=(this._lBlock>>>a^this._rBlock)&b;this._rBlock^=c;this._lBlock^=c<>>a^this._lBlock)&b;this._lBlock^=c;this._rBlock^=c<c;c++){var d=h[c]-1;b[c]=a[d>>>5]>>>31-d%32&1}a=this._subKeys=[];for(d=0;16>d;d++){for(var e=a[d]=[],f=l[d],c=0;24>c;c++)e[c/6|0]|=b[(k[c]-1+f)%28]<<31-c%6,e[4+(c/6|0)]|=b[28+(k[c+24]- +1+f)%28]<<31-c%6;e[0]=e[0]<<1|e[0]>>>31;for(c=1;7>c;c++)e[c]>>>=4*(c-1)+3;e[7]=e[7]<<5|e[7]>>>27}b=this._invSubKeys=[];for(c=0;16>c;c++)b[c]=a[15-c]},encryptBlock:function(a,b){this._doCryptBlock(a,b,this._subKeys)},decryptBlock:function(a,b){this._doCryptBlock(a,b,this._invSubKeys)},_doCryptBlock:function(b,c,e){this._lBlock=b[c];this._rBlock=b[c+1];a.call(this,4,252645135);a.call(this,16,65535);g.call(this,2,858993459);g.call(this,8,16711935);a.call(this,1,1431655765);for(var f=0;16>f;f++){for(var k= +e[f],h=this._lBlock,q=this._rBlock,l=0,r=0;8>r;r++)l|=d[r][((q^k[r])&n[r])>>>0];this._lBlock=q;this._rBlock=h^l}e=this._lBlock;this._lBlock=this._rBlock;this._rBlock=e;a.call(this,1,1431655765);g.call(this,8,16711935);g.call(this,2,858993459);a.call(this,16,65535);a.call(this,4,252645135);b[c]=this._lBlock;b[c+1]=this._rBlock},keySize:2,ivSize:2,blockSize:2});b.DES=c._createHelper(G);f=f.TripleDES=c.extend({_doReset:function(){var a=this._key.words;this._des1=G.createEncryptor(e.create(a.slice(0, +2)));this._des2=G.createEncryptor(e.create(a.slice(2,4)));this._des3=G.createEncryptor(e.create(a.slice(4,6)))},encryptBlock:function(a,b){this._des1.encryptBlock(a,b);this._des2.decryptBlock(a,b);this._des3.encryptBlock(a,b)},decryptBlock:function(a,b){this._des3.decryptBlock(a,b);this._des2.encryptBlock(a,b);this._des1.decryptBlock(a,b)},keySize:6,ivSize:2,blockSize:2});b.TripleDES=c._createHelper(f)})();return b.TripleDES})},{"./cipher-core":21,"./core":22,"./enc-base64":23,"./evpkdf":25,"./md5":30}], +53:[function(h,n,l){(function(b,a){"object"===typeof l?n.exports=l=a(h("./core")):a(b.CryptoJS)})(this,function(b){(function(a){var g=b.lib,c=g.Base,e=g.WordArray,g=b.x64={};g.Word=c.extend({init:function(a,b){this.high=a;this.low=b}});g.WordArray=c.extend({init:function(b,c){b=this.words=b||[];this.sigBytes=c!=a?c:8*b.length},toX32:function(){for(var a=this.words,b=a.length,c=[],g=0;gb.i)return e(b.f,b.thisArg,b.params,f);a._handler(b.args[b.i]).fold(h,b,void 0,f)}function h(a,b,c){a.params[a.i]=b;--a.i;f(a,c)}2>arguments.length&&(e=b);return function(b,g,d){var h=a._defer(),l=d.length;f({f:b,thisArg:g,args:d,params:Array(l),i:l-1,call:e},h._handler);return h}}function b(a,e,f,g){try{g.resolve(a.apply(e,f))}catch(k){g.reject(k)}}a.tryCatchResolve=b;return a})})(function(b){n.exports= +b()})},{}],59:[function(h,n,l){(function(b){b(function(a){var b=a("../state"),c=a("../apply");return function(a){function f(c){var d=a._handler(c);if(0===d.state())return l(c).then(b.fulfilled,b.rejected);d._unreport();return b.inspect(d)}function h(a){return function(b,c,d){return k(a,void 0,[b,c,d])}}var k=c(a),l=a.resolve,d=a.all,n=Array.prototype.reduce,G=Array.prototype.reduceRight,B=Array.prototype.slice;a.any=function(b){function c(a){l=null;this.resolve(a)}function d(a){this.resolved||(l.push(a), +0===--h&&this.reject(l))}for(var f=a._defer(),g=f._handler,k=b.length>>>0,h=k,l=[],q,r=0;r>>0,r=0,n,z,G;for(G=0;Gr?k.reject(new RangeError("some(): array must contain at least "+c+" item(s), but had "+r)):0===r&&k.resolve(h);for(G=0;Garguments.length?d.call(this,b):"function"!==typeof b?this.ensure(a):d.call(this,e(arguments[1],b))};c.prototype["finally"]=c.prototype.ensure=function(a){return"function"!==typeof a?this:this.then(function(c){return f(a,this,b,c)},function(b){return f(a,this,l,b)})};c.prototype["else"]=c.prototype.orElse=function(a){return this.then(void 0,function(){return a})};c.prototype.yield=function(a){return this.then(function(){return a})};c.prototype.tap=function(a){return this.then(a).yield(this)}; +return c}})})(function(b){n.exports=b()})},{}],61:[function(h,n,l){(function(b){b(function(){return function(a){a.prototype.fold=function(b,c){var e=this._beget();this._handler.fold(function(c,e,k){a._handler(c).fold(function(a,c,e){e.resolve(b.call(this,c,a))},e,this,k)},c,e._handler.receiver,e._handler);return e};return a}})})(function(b){n.exports=b()})},{}],62:[function(h,n,l){(function(b){b(function(a){var b=a("../state").inspect;return function(a){a.prototype.inspect=function(){return b(a._handler(this))}; +return a}})})(function(b){n.exports=b(h)})},{"../state":72}],63:[function(h,n,l){(function(b){b(function(){return function(a){function b(a,f,h,k){function l(d,k){return c(h(d)).then(function(){return b(a,f,h,k)})}return c(k).then(function(b){return c(f(b)).then(function(f){return f?b:c(a(b)).spread(l)})})}var c=a.resolve;a.iterate=function(a,c,h,k){return b(function(b){return[b,a(b)]},c,h,k)};a.unfold=b;return a}})})(function(b){n.exports=b()})},{}],64:[function(h,n,l){(function(b){b(function(){return function(a){a.prototype.progress= +function(a){return this.then(void 0,void 0,a)};return a}})})(function(b){n.exports=b()})},{}],65:[function(h,n,l){(function(b){b(function(a){function b(a,e,g,h){return c.setTimer(function(){a(g,h,e)},e)}var c=a("../env"),e=a("../TimeoutError");return function(a){function h(a,c,e){b(k,a,c,e)}function k(a,b){b.resolve(a)}function l(a,b,c){a="undefined"===typeof a?new e("timed out after "+c+"ms"):a;b.reject(a)}a.prototype.delay=function(a){var b=this._beget();this._handler.fold(h,a,void 0,b._handler); +return b};a.prototype.timeout=function(a,e){var f=this._beget(),k=f._handler,h=b(l,a,e,f._handler);this._handler.visit(k,function(a){c.clearTimer(h);this.resolve(a)},function(a){c.clearTimer(h);this.reject(a)},k.notify);return f};return a}})})(function(b){n.exports=b(h)})},{"../TimeoutError":57,"../env":68}],66:[function(h,n,l){(function(b){b(function(a){function b(a){throw a;}function c(){}var e=a("../env").setTimer,f=a("../format");return function(a){function k(a){a.handled||(Q.push(a),n("Potentially unhandled rejection ["+ +a.id+"] "+f.formatError(a.value)))}function h(a){var b=Q.indexOf(a);0<=b&&(Q.splice(b,1),B("Handled previous rejection ["+a.id+"] "+f.formatObject(a.value)))}function d(a,b){D.push(a,b);null===C&&(C=e(l,0))}function l(){for(C=null;0>>0,u=Array(I),h=0,k;he&&d._unreport()}}}function n(a){return a instanceof c?a._handler.join():R(a)?G(a):new m(a)}function G(a){try{var b=a.then;return"function"===typeof b?new C(b,a):new m(a)}catch(c){return new H(c)}}function B(){}function v(){}function D(a,b){c.createContext(this,b);this.consumers=void 0;this.receiver=a;this.handler=void 0;this.resolved=!1}function Q(a){this.handler=a}function C(a,b){D.call(this);x.enqueue(new L(a,b,this))}function m(a){c.createContext(this);this.value=a}function H(a){c.createContext(this); +this.id=++W;this.value=a;this.reported=this.handled=!1;this._report()}function A(a,b){this.rejection=a;this.context=b}function M(a){this.rejection=a}function F(a,b){this.continuation=a;this.handler=b}function E(a,b){this.handler=b;this.value=a}function L(a,b,c){this._then=a;this.thenable=b;this.resolver=c}function N(a,b,c,d,e){try{a.call(b,c,d,e)}catch(p){d(p)}}function y(a,b,c,d){this.f=a;this.z=b;this.c=c;this.to=d;this.resolver=K;this.receiver=this}function R(a){return("object"===typeof a||"function"=== +typeof a)&&null!==a}function p(a,b,d,e){if("function"!==typeof a)return e.become(b);c.enterContext(b);try{e.become(n(a.call(d,b.value)))}catch(p){e.become(new H(p))}c.exitContext()}function I(a,b,c){try{return a(b,c)}catch(d){return h(d)}}function u(a,b){b.prototype=P(a.prototype);b.prototype.constructor=b}function w(a,b){return b}function t(){}var x=a.scheduler,S=function(){return"undefined"!==typeof b&&null!==b&&"function"===typeof b.emit?function(a,c){return"unhandledRejection"===a?b.emit(a,c.value, +c):b.emit(a,c)}:"undefined"!==typeof self&&"function"===typeof CustomEvent?function(a,b,c){var d=!1;try{d=new c("unhandledRejection")instanceof c}catch(e){}return d?function(a,d){var e=new c(a,{detail:{reason:d.value,key:d},bubbles:!1,cancelable:!0});return!b.dispatchEvent(e)}:a}(t,self,CustomEvent):t}(),P=Object.create||function(a){function b(){}b.prototype=a;return new b};c.resolve=f;c.reject=h;c.never=function(){return aa};c._defer=function(){return new c(B,new D)};c._handler=n;c.prototype.then= +function(a,b,c){var d=this._handler,e=d.join().state();if("function"!==typeof a&&0e)return new this.constructor(B,d);e=this._beget();d.chain(e._handler,d.receiver,a,b,c);return e};c.prototype["catch"]=function(a){return this.then(void 0,a)};c.prototype._beget=function(){var a=this._handler,b=this.constructor,a=new D(a.receiver,a.join().context);return new b(B,a)};c.all=function(a){return k(w,null,a)};c.race=function(a){if("object"!==typeof a||null===a)return h(new TypeError("non-iterable passed to race()")); +if(0===a.length)a=aa;else if(1===a.length)a=f(a[0]);else{var b=new D,e,p;for(e=0;earguments.length?f:f.then(c, +d,e)}function c(a){return function(){for(var b=0,c=arguments.length,d=Array(c);b= 3.1.5",when:">= 3.7.3",ws:">= 0.8.0"},devDependencies:{browserify:">= 11.0.1",nodeunit:">= 0.9.1"},repository:{type:"git",url:"git://github.com/crossbario/autobahn-js.git"},keywords:["WAMP", +"WebSocket","RPC","PubSub"],author:"Tavendo GmbH",license:"MIT"}},{}],81:[function(h,n,l){},{}],82:[function(h,n,l){function b(d){if(!(this instanceof b))return 1d?0:e(d)|0);if(!b.TYPED_ARRAY_SUPPORT)for(var g=0;g>>1&&(a.parent=L);return a}function e(a){if(a>=(b.TYPED_ARRAY_SUPPORT?2147483647:1073741823))throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+ +(b.TYPED_ARRAY_SUPPORT?2147483647:1073741823).toString(16)+" bytes");return a|0}function f(a,c){if(!(this instanceof f))return new f(a,c);var d=new b(a,c);delete d.parent;return d}function q(a,b){"string"!==typeof a&&(a=""+a);var c=a.length;if(0===c)return 0;for(var d=!1;;)switch(b){case "ascii":case "binary":case "raw":case "raws":return c;case "utf8":case "utf-8":return m(a).length;case "ucs2":case "ucs-2":case "utf16le":case "utf-16le":return 2*c;case "hex":return c>>>1;case "base64":return M.toByteArray(C(a)).length; +default:if(d)return m(a).length;b=(""+b).toLowerCase();d=!0}}function k(a,b,c){var d=!1;b|=0;c=void 0===c||Infinity===c?this.length:c|0;a||(a="utf8");0>b&&(b=0);c>this.length&&(c=this.length);if(c<=b)return"";for(;;)switch(a){case "hex":a=b;b=c;c=this.length;if(!a||0>a)a=0;if(!b||0>b||b>c)b=c;d="";for(c=a;cd?"0"+d.toString(16):d.toString(16),d=a+d;return d;case "utf8":case "utf-8":return r(this,b,c);case "ascii":a="";for(c=Math.min(this.length,c);be&&(f=e);break;case 2:h=a[b+1];128===(h&192)&&(e=(e&31)<<6|h&63,127e||57343e&&(f=e))}}null===f?(f=65533,g=1):65535>> +10&1023|55296),f=56320|f&1023);d.push(f);b+=g}a=d.length;if(a<=N)d=String.fromCharCode.apply(String,d);else{c="";for(b=0;ba)throw new RangeError("offset is not uint");if(a+b>c)throw new RangeError("Trying to access beyond buffer length");}function z(a,c,d,e,f,g){if(!b.isBuffer(a))throw new TypeError("buffer must be a Buffer instance");if(c>f||ca.length)throw new RangeError("index out of range");}function G(a,b,c,d){0>b&&(b=65535+b+1);for(var e=0,f=Math.min(a.length-c,2);e>>8*(d?e:1-e)}function B(a,b,c,d){0>b&&(b=4294967295+b+1);for(var e=0,f=Math.min(a.length-c,4);e>>8*(d?e:3-e)&255}function v(a,b,c,d,e,f){if(b>e||ba.length)throw new RangeError("index out of range");if(0>c)throw new RangeError("index out of range");}function D(a, +b,c,d,e){e||v(a,b,c,4,3.4028234663852886E38,-3.4028234663852886E38);F.write(a,b,c,d,23,4);return c+4}function Q(a,b,c,d,e){e||v(a,b,c,8,1.7976931348623157E308,-1.7976931348623157E308);F.write(a,b,c,d,52,8);return c+8}function C(a){a=a.trim?a.trim():a.replace(/^\s+|\s+$/g,"");a=a.replace(R,"");if(2>a.length)return"";for(;0!==a.length%4;)a+="=";return a}function m(a,b){b=b||Infinity;for(var c,d=a.length,e=null,f=[],g=0;gc){if(!e){if(56319c){-1<(b-=3)&&f.push(239,191,189);e=c;continue}c=e-55296<<10|c-56320|65536}else e&&-1<(b-=3)&&f.push(239,191,189);e=null;if(128>c){if(0>--b)break;f.push(c)}else if(2048>c){if(0>(b-=2))break;f.push(c>>6|192,c&63|128)}else if(65536>c){if(0>(b-=3))break;f.push(c>>12|224,c>>6&63|128,c&63|128)}else if(1114112>c){if(0>(b-=4))break;f.push(c>>18|240,c>>12&63|128,c>>6&63|128,c&63|128)}else throw Error("Invalid code point"); +}return f}function H(a){for(var b=[],c=0;c=b.length||e>=a.length);e++)b[e+c]=a[e];return e}var M=h("base64-js"),F=h("ieee754"),E=h("is-array");l.Buffer=b;l.SlowBuffer=f;l.INSPECT_MAX_BYTES=50;b.poolSize=8192;var L={};b.TYPED_ARRAY_SUPPORT=function(){function a(){}try{var b=new Uint8Array(1);b.foo=function(){return 42};b.constructor=a;return 42===b.foo()&&b.constructor===a&&"function"===typeof b.subarray&& +0===b.subarray(1,1).byteLength}catch(c){return!1}}();b.isBuffer=function(a){return!(null==a||!a._isBuffer)};b.compare=function(a,c){if(!b.isBuffer(a)||!b.isBuffer(c))throw new TypeError("Arguments must be Buffers");if(a===c)return 0;for(var d=a.length,e=c.length,f=0,g=Math.min(d,e);fb&&(a+=" ... "));return""};b.prototype.compare=function(a){if(!b.isBuffer(a))throw new TypeError("Argument must be a Buffer");return this===a?0:b.compare(this,a)};b.prototype.indexOf=function(a,c){function d(a,b,c){for(var e= +-1,f=0;c+fc&&(c=-2147483648);c>>=0;if(0===this.length||c>=this.length)return-1;0>c&&(c=Math.max(this.length+c,0));if("string"===typeof a)return 0===a.length?-1:String.prototype.indexOf.call(this,a,c);if(b.isBuffer(a))return d(this,a,c);if("number"===typeof a)return b.TYPED_ARRAY_SUPPORT&&"function"===Uint8Array.prototype.indexOf?Uint8Array.prototype.indexOf.call(this, +a,c):d(this,[a],c);throw new TypeError("val must be string, number or Buffer");};b.prototype.get=function(a){console.log(".get() is deprecated. Access using array indexes instead.");return this.readUInt8(a)};b.prototype.set=function(a,b){console.log(".set() is deprecated. Access using array indexes instead.");return this.writeUInt8(a,b)};b.prototype.write=function(a,b,c,d){if(void 0===b)d="utf8",c=this.length,b=0;else if(void 0===c&&"string"===typeof b)d=b,c=this.length,b=0;else if(isFinite(b))b|= +0,isFinite(c)?(c|=0,void 0===d&&(d="utf8")):(d=c,c=void 0);else{var e=d;d=b;b=c|0;c=e}e=this.length-b;if(void 0===c||c>e)c=e;if(0c||0>b)||b>this.length)throw new RangeError("attempt to write outside buffer bounds");d||(d="utf8");for(e=!1;;)switch(d){case "hex":b=Number(b)||0;d=this.length-b;c?(c=Number(c),c>d&&(c=d)):c=d;d=a.length;if(0!==d%2)throw Error("Invalid hex string");c>d/2&&(c=d/2);for(d=0;d(d-=2));h++)f=a.charCodeAt(h),e=f>>8,f%=256,g.push(f),g.push(e);return A(g,this,b,c);default:if(e)throw new TypeError("Unknown encoding: "+d);d=(""+d).toLowerCase(); +e=!0}};b.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var N=4096;b.prototype.slice=function(a,c){var d=this.length;a=~~a;c=void 0===c?d:~~c;0>a?(a+=d,0>a&&(a=0)):a>d&&(a=d);0>c?(c+=d,0>c&&(c=0)):c>d&&(c=d);c=128*e&&(c-=Math.pow(2,8*b));return c};b.prototype.readIntBE=function(a,b,c){a|=0;b|=0;c||d(a,b,this.length);c=b;for(var e=1,f=this[a+--c];0=128*e&&(f-=Math.pow(2,8*b));return f};b.prototype.readInt8=function(a,b){b||d(a,1,this.length);return this[a]&128?-1*(255-this[a]+1):this[a]};b.prototype.readInt16LE=function(a,b){b||d(a,2,this.length);var c=this[a]|this[a+1]<<8;return c&32768?c|4294901760:c};b.prototype.readInt16BE=function(a,b){b||d(a,2,this.length);var c=this[a+1]|this[a]<<8;return c&32768?c|4294901760:c};b.prototype.readInt32LE=function(a,b){b||d(a,4,this.length);return this[a]|this[a+1]<<8|this[a+2]<< +16|this[a+3]<<24};b.prototype.readInt32BE=function(a,b){b||d(a,4,this.length);return this[a]<<24|this[a+1]<<16|this[a+2]<<8|this[a+3]};b.prototype.readFloatLE=function(a,b){b||d(a,4,this.length);return F.read(this,a,!0,23,4)};b.prototype.readFloatBE=function(a,b){b||d(a,4,this.length);return F.read(this,a,!1,23,4)};b.prototype.readDoubleLE=function(a,b){b||d(a,8,this.length);return F.read(this,a,!0,52,8)};b.prototype.readDoubleBE=function(a,b){b||d(a,8,this.length);return F.read(this,a,!1,52,8)}; +b.prototype.writeUIntLE=function(a,b,c,d){a=+a;b|=0;c|=0;d||z(this,a,b,c,Math.pow(2,8*c),0);d=1;var e=0;for(this[b]=a&255;++e>>8):G(this,a,c,!0);return c+2};b.prototype.writeUInt16BE=function(a,c,d){a=+a;c|=0;d||z(this,a,c,2,65535,0);b.TYPED_ARRAY_SUPPORT?(this[c]=a>>>8,this[c+1]=a):G(this,a,c,!1);return c+2};b.prototype.writeUInt32LE=function(a,c,d){a=+a;c|=0;d||z(this,a,c,4,4294967295,0);b.TYPED_ARRAY_SUPPORT?(this[c+3]=a>>>24,this[c+2]=a>>>16,this[c+1]=a>>>8,this[c]=a):B(this,a,c,!0);return c+4};b.prototype.writeUInt32BE= +function(a,c,d){a=+a;c|=0;d||z(this,a,c,4,4294967295,0);b.TYPED_ARRAY_SUPPORT?(this[c]=a>>>24,this[c+1]=a>>>16,this[c+2]=a>>>8,this[c+3]=a):B(this,a,c,!1);return c+4};b.prototype.writeIntLE=function(a,b,c,d){a=+a;b|=0;d||(d=Math.pow(2,8*c-1),z(this,a,b,c,d-1,-d));d=0;var e=1,f=0>a?1:0;for(this[b]=a&255;++d>0)-f&255;return b+c};b.prototype.writeIntBE=function(a,b,c,d){a=+a;b|=0;d||(d=Math.pow(2,8*c-1),z(this,a,b,c,d-1,-d));d=c-1;var e=1,f=0>a?1:0;for(this[b+d]=a&255;0<= +--d&&(e*=256);)this[b+d]=(a/e>>0)-f&255;return b+c};b.prototype.writeInt8=function(a,c,d){a=+a;c|=0;d||z(this,a,c,1,127,-128);b.TYPED_ARRAY_SUPPORT||(a=Math.floor(a));0>a&&(a=255+a+1);this[c]=a;return c+1};b.prototype.writeInt16LE=function(a,c,d){a=+a;c|=0;d||z(this,a,c,2,32767,-32768);b.TYPED_ARRAY_SUPPORT?(this[c]=a,this[c+1]=a>>>8):G(this,a,c,!0);return c+2};b.prototype.writeInt16BE=function(a,c,d){a=+a;c|=0;d||z(this,a,c,2,32767,-32768);b.TYPED_ARRAY_SUPPORT?(this[c]=a>>>8,this[c+1]=a):G(this, +a,c,!1);return c+2};b.prototype.writeInt32LE=function(a,c,d){a=+a;c|=0;d||z(this,a,c,4,2147483647,-2147483648);b.TYPED_ARRAY_SUPPORT?(this[c]=a,this[c+1]=a>>>8,this[c+2]=a>>>16,this[c+3]=a>>>24):B(this,a,c,!0);return c+4};b.prototype.writeInt32BE=function(a,c,d){a=+a;c|=0;d||z(this,a,c,4,2147483647,-2147483648);0>a&&(a=4294967295+a+1);b.TYPED_ARRAY_SUPPORT?(this[c]=a>>>24,this[c+1]=a>>>16,this[c+2]=a>>>8,this[c+3]=a):B(this,a,c,!1);return c+4};b.prototype.writeFloatLE=function(a,b,c){return D(this, +a,b,!0,c)};b.prototype.writeFloatBE=function(a,b,c){return D(this,a,b,!1,c)};b.prototype.writeDoubleLE=function(a,b,c){return Q(this,a,b,!0,c)};b.prototype.writeDoubleBE=function(a,b,c){return Q(this,a,b,!1,c)};b.prototype.copy=function(a,c,d,e){d||(d=0);e||0===e||(e=this.length);c>=a.length&&(c=a.length);c||(c=0);0c)throw new RangeError("targetStart out of bounds");if(0>d||d>=this.length)throw new RangeError("sourceStart out of bounds"); +if(0>e)throw new RangeError("sourceEnd out of bounds");e>this.length&&(e=this.length);a.length-cf||!b.TYPED_ARRAY_SUPPORT)for(e=0;eb||b>=this.length)throw new RangeError("start out of bounds"); +if(0>c||c>this.length)throw new RangeError("end out of bounds");if("number"===typeof a)for(;ba)return-1;if(58>a)return a-48+52;if(91>a)return a-65;if(123>a)return a-97+26}var g="undefined"!==typeof Uint8Array?Uint8Array:Array;b.toByteArray=function(b){function e(a){d[n++]=a}var f,h,k,l,d;if(0>16),e((k&65280)>>8),e(k&255);2===l?(k=a(b.charAt(f))<<2|a(b.charAt(f+1))>>4,e(k&255)):1===l&&(k=a(b.charAt(f))<<10|a(b.charAt(f+1))<<4|a(b.charAt(f+2))>>2,e(k>>8&255),e(k&255));return d};b.fromByteArray=function(a){var b,f=a.length%3,g="",h,l;b=0;for(l=a.length-f;b>18&63)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(h>> +12&63)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(h>>6&63)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(h&63),g+=h;switch(f){case 1:h=a[a.length-1];g+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(h>>2);g+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(h<<4&63);g+="==";break;case 2:h=(a[a.length-2]<<8)+a[a.length-1],g+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(h>> +10),g+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(h>>4&63),g+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(h<<2&63),g+="="}return g}})("undefined"===typeof l?this.base64js={}:l)},{}],84:[function(h,n,l){l.read=function(b,a,g,c,e){var f;f=8*e-c-1;var h=(1<>1,l=-7;e=g?e-1:0;var d=g?-1:1,n=b[a+e];e+=d;g=n&(1<<-l)-1;n>>=-l;for(l+=f;0>=-l;for(l+=c;0>1,n=23===e?Math.pow(2,-24)-Math.pow(2,-77):0;f=c?0:f-1;var G=c?1:-1,B=0>a||0===a&&0>1/a?1:0;a=Math.abs(a);isNaN(a)||Infinity===a?(a=isNaN(a)?1:0,c=l):(c=Math.floor(Math.log(a)/Math.LN2),1>a*(h=Math.pow(2,-c))&&(c--,h*=2),a=1<=c+d?a+n/h:a+n*Math.pow(2,1-d),2<=a*h&&(c++,h/=2),c+d>=l?(a=0,c=l):1<=c+d?(a=(a*h-1)*Math.pow(2,e),c+=d):(a=a* +Math.pow(2,d-1)*Math.pow(2,e),c=0));for(;8<=e;b[g+f]=a&255,f+=G,a/=256,e-=8);c=c<a||isNaN(a))throw TypeError("n must be a positive number");this._maxListeners=a;return this};b.prototype.emit=function(b){var e,f,h,k;this._events||(this._events={});if("error"===b&&(!this._events.error||g(this._events.error)&&!this._events.error.length)){e=arguments[1];if(e instanceof Error)throw e;throw TypeError('Uncaught, unspecified "error" event.'); +}f=this._events[b];if(void 0===f)return!1;if(a(f))switch(arguments.length){case 1:f.call(this);break;case 2:f.call(this,arguments[1]);break;case 3:f.call(this,arguments[1],arguments[2]);break;default:e=arguments.length;h=Array(e-1);for(k=1;kf&&(this._events[c].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.", +this._events[c].length),"function"===typeof console.trace&&console.trace());return this};b.prototype.on=b.prototype.addListener;b.prototype.once=function(b,e){function f(){this.removeListener(b,f);g||(g=!0,e.apply(this,arguments))}if(!a(e))throw TypeError("listener must be a function");var g=!1;f.listener=e;this.on(b,f);return this};b.prototype.removeListener=function(b,e){var f,h,k;if(!a(e))throw TypeError("listener must be a function");if(!this._events||!this._events[b])return this;f=this._events[b]; +k=f.length;h=-1;if(f===e||a(f.listener)&&f.listener===e)delete this._events[b],this._events.removeListener&&this.emit("removeListener",b,e);else if(g(f)){for(;0h)return this;1===f.length?(f.length=0,delete this._events[b]):f.splice(h,1);this._events.removeListener&&this.emit("removeListener",b,e)}return this};b.prototype.removeAllListeners=function(b){var e;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length? +this._events={}:this._events[b]&&delete this._events[b],this;if(0===arguments.length){for(e in this._events)"removeListener"!==e&&this.removeAllListeners(e);this.removeAllListeners("removeListener");this._events={};return this}e=this._events[b];if(a(e))this.removeListener(b,e);else for(;e.length;)this.removeListener(b,e[e.length-1]);delete this._events[b];return this};b.prototype.listeners=function(b){return this._events&&this._events[b]?a(this._events[b])?[this._events[b]]:this._events[b].slice(): +[]};b.listenerCount=function(b,e){return b._events&&b._events[e]?a(b._events[e])?1:b._events[e].length:0}},{}],87:[function(h,n,l){function b(){f=!1;q.length?e=q.concat(e):k=-1;e.length&&a()}function a(){if(!f){var a=setTimeout(b);f=!0;for(var c=e.length;c;){q=e;for(e=[];++kexecute({res}); + } + _promise.set_value(res); +} +void Call::resultReady(QVariant res) +{ + if(parent()) + { + QMetaObject::invokeMethod(this, "resultReadyInternal", Qt::BlockingQueuedConnection, Q_ARG(QVariant, res)); + } + else resultReadyInternal(res); +} +Future Call::getFuture() +{ + std::shared_future stdFuture(_promise.get_future()); + return Future(stdFuture); +} +} diff --git a/src/client/call.h b/src/client/call.h new file mode 100644 index 0000000..c4005a0 --- /dev/null +++ b/src/client/call.h @@ -0,0 +1,34 @@ +#ifndef CALL_H +#define CALL_H + +#include "future.h" +#include + +namespace QFlow{ + +class Impl; + +class Call : public QObject +{ + Q_OBJECT +public: + Call(Impl* callback, QObject* parent = NULL); + ~Call(); + void resultReady(QVariant res); + Future getFuture(); +public Q_SLOTS: + void resultReadyInternal(QVariant res); +Q_SIGNALS: + void result(QVariant res); +private: + QScopedPointer _callback; + std::promise _promise; +}; +struct CallDeleter { + void operator()(Call* c) const { + c->deleteLater(); + } +}; +typedef QSharedPointer CallPointer; +} +#endif // CALL_H diff --git a/src/client/registration_p.h b/src/client/registration_p.h new file mode 100644 index 0000000..b202273 --- /dev/null +++ b/src/client/registration_p.h @@ -0,0 +1,287 @@ +#ifndef REGISTRATION_P_H +#define REGISTRATION_P_H + +#include "functor.h" +#include "helper.h" +#include "future.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace QFlow{ + +class WampResult +{ + Q_GADGET + Q_PROPERTY(QVariant resultData READ resultData) + Q_PROPERTY(QString errorUri READ errorUri) +private: + QVariant _resultData; + QString _errorUri; +public: + WampResult(QVariant resultData = QVariant(), QString errorUri = QString()) : _resultData(resultData), + _errorUri(errorUri) + { + + } + WampResult(QString errorUri) : _errorUri(errorUri) + { + + } + + ~WampResult() + { + + } + WampResult(const WampResult& other) : _resultData(other._resultData), _errorUri(other._errorUri) + { + + } + + QVariant resultData() const + { + return _resultData; + } + bool isError() const + { + return !(_errorUri.isEmpty() || _errorUri.isNull()); + } + QString errorUri() const + { + return _errorUri; + } +}; +class Impl +{ +public: + virtual WampResult execute(const QVariantList& args) = 0; + virtual ~Impl() + { + + } +}; +class Registration : public Impl +{ +protected: + qulonglong _registrationId; + QString _uri; + QScopedPointer _impl; +public: + QString uri() const + { + return _uri; + } + qulonglong registrationId() const + { + return _registrationId; + } + void setRegistrationId(qulonglong registrationId) + { + _registrationId = registrationId; + } + + Registration(QString uri, Impl* impl) : _uri(uri), _impl(impl) + { + + } + virtual ~Registration() + { + + } + WampResult execute(const QVariantList& args) override + { + return _impl->execute(args); + } +}; +typedef QSharedPointer RegistrationPointer; + +class JSImpl : public Impl +{ + QJSValue _callback; +public: + JSImpl(QJSValue val) : _callback(val) + { + + } + + WampResult execute(const QVariantList& args) override + { + QJSValueList params; + Q_FOREACH (QVariant val, args) { + //QJSValue param = Helper::variantToJS(val, _callback.engine()); + QJSValue param = _callback.engine()->toScriptValue(val); + params.append(param); + } + QJSValue jsResult = _callback.call(params); + QVariant result = jsResult.toVariant(); + return WampResult(result); + } + + virtual ~JSImpl() + { + + } +}; +class MethodImpl : public Impl +{ + QObject* _obj; + QMetaMethod _metaMethod; +public: + MethodImpl(QObject* obj, QMetaMethod metaMethod) : _obj(obj), + _metaMethod(metaMethod) + { + + } + MethodImpl(QObject* obj, QString method) : _obj(obj) + { + int methodIndex = obj->metaObject()->indexOfMethod(method.toLatin1()); + _metaMethod = obj->metaObject()->method(methodIndex); + } + + virtual ~MethodImpl() + { + + } + + WampResult execute(const QVariantList& args) override + { + QGenericArgument genArgs[10]; + QVariantList translatedArgs = args; + for(int i=0; ithread()) connectionType = Qt::DirectConnection; + _metaMethod.invoke(_obj, connectionType, retArg, genArgs[0], genArgs[1], genArgs[2], genArgs[3], + genArgs[4], genArgs[5], genArgs[6], genArgs[7], genArgs[8], genArgs[9]); + + if(retVar.type() == (QVariant::Type)QMetaType::QVariant) + { + QVariant inner = retVar.value(); + retVar = inner; + } + return WampResult(retVar); + + } +}; +class PropertyGetterImpl : public Impl +{ + QObject* _obj; + QMetaProperty _metaProperty; +public: + PropertyGetterImpl(QObject* obj, QMetaProperty metaProperty) : _obj(obj), + _metaProperty(metaProperty) + { + + } + virtual ~PropertyGetterImpl() + { + + } + + WampResult execute(const QVariantList& /*args*/) override + { + QVariant resultVar = _metaProperty.read(_obj); + return WampResult(resultVar); + } +}; +class QmlListCountImpl : public Impl +{ + QQmlListReference _ref; +public: + QmlListCountImpl(QQmlListReference ref) : _ref(ref) + { + + } + virtual ~QmlListCountImpl() + { + + } + + WampResult execute(const QVariantList& /*args*/) override + { + QVariant resultVar(_ref.count()); + return WampResult(resultVar); + } +}; +class PropertySetterImpl : public Impl +{ + QObject* _obj; + QMetaProperty _metaProperty; +public: + PropertySetterImpl(QObject* obj, QMetaProperty metaProperty) : _obj(obj), + _metaProperty(metaProperty) + { + + } + virtual ~PropertySetterImpl() + { + + } + + WampResult execute(const QVariantList& args) override + { + _metaProperty.write(_obj, args[0]); + return WampResult(); + } +}; +class FunctorImpl : public Impl +{ + FunctorBase* _functor; +public: + FunctorImpl(FunctorBase* functor) : _functor(functor) + { + + } + virtual ~FunctorImpl() + { + + } + WampResult execute(const QVariantList& args) override + { + QVariant resultVar = _functor->call(args); + return WampResult(resultVar); + } + +}; +typedef std::function SimpleCallback; +class SimpleImpl : public Impl +{ + SimpleCallback _callback; +public: + SimpleImpl(SimpleCallback callback) : _callback(callback) + { + + } + virtual ~SimpleImpl() + { + + } + WampResult execute(const QVariantList& args) override + { + return _callback(args); + } +}; +} +#endif // REGISTRATION_P_H + diff --git a/src/client/subscription_p.h b/src/client/subscription_p.h new file mode 100644 index 0000000..37b0fc4 --- /dev/null +++ b/src/client/subscription_p.h @@ -0,0 +1,119 @@ +#ifndef SUBSCRIPTION_P_H +#define SUBSCRIPTION_P_H + +#include "functor.h" +#include "helper.h" +#include +#include +#include +#include +#include +#include +#include + +namespace QFlow{ + +class Subscription +{ +protected: + qulonglong _subscriptionId; + QString _uri; +public: + Subscription() : _subscriptionId(-1) + { + + } + QString uri() const + { + return _uri; + } + void setSubscriptionId(qulonglong subscriptionId) + { + _subscriptionId = subscriptionId; + } + + Subscription(QString uri) : _uri(uri) + { + + } + qulonglong subscriptionId() const + { + return _subscriptionId; + } + virtual ~Subscription() + { + + } + virtual void handle(const QVariantList& args) = 0; +}; +typedef QSharedPointer SubscriptionPointer; +class JSSubscription : public Subscription +{ + QJSValue _callback; +public: + JSSubscription(QString uri, QJSValue val) : Subscription(uri), _callback(val) + { + Q_ASSERT(val.isCallable()); + } + void handle(const QVariantList& args) override + { + QJSValueList params; + Q_FOREACH (QVariant val, args) { + QByteArray arr = val.toByteArray(); + //QJSValue param = Helper::variantToJS(val, _callback.engine()); + QJSValue param = _callback.engine()->toScriptValue(val); + params.append(param); + } + _callback.call(params); + } + + ~JSSubscription() + { + + } +}; +class MethodSubscription : public Subscription +{ + QObject* _obj; + QString _method; + QMetaMethod _metaMethod; +public: + MethodSubscription(QString uri, QObject* obj, QString method) : Subscription(uri), _obj(obj), _method(method) + { + int index = _obj->metaObject()->indexOfSlot(method.toLatin1()); + _metaMethod = _obj->metaObject()->method(index); + } + void handle(const QVariantList& args) override + { + QGenericArgument genArgs[10]; + for(int i=0; icall(args); + } + +}; +} +#endif // SUBSCRIPTION_P_H + diff --git a/src/client/wampconnection.cpp b/src/client/wampconnection.cpp new file mode 100644 index 0000000..29edf57 --- /dev/null +++ b/src/client/wampconnection.cpp @@ -0,0 +1,337 @@ +#include "wampconnection.h" +#include "wampconnection_p.h" +#include "wampattached.h" +#include "future.h" +#include "wampworker.h" +#include "random.h" +#include "wampinvocation.h" +#include "signalobserver.h" +#include "wampmessageserializer.h" +#include "websocketconnection.h" +#include "call.h" +#include +#include +#include +#include +#include +#include + +namespace QFlow{ + +WampConnectionPrivate::WampConnectionPrivate(WampConnection* parent) : QObject(), q_ptr(parent) +{ + _worker = new WampWorker(); + _worker->_socketPrivate = this; + connect(&_workerThread, &QThread::finished, _worker, &QObject::deleteLater); + _workerThread.start(); + _worker->moveToThread(&_workerThread); +} +WampConnectionPrivate::~WampConnectionPrivate() +{ + _workerThread.quit(); + _workerThread.wait(); +} +QByteArray WampConnectionPrivate::IntToOctet(int i) +{ + QByteArray octet; + octet[0] = (char)((uint)i >> 24); + octet[1] = (char)((uint)i >> 16); + octet[2] = (char)((uint)i >> 8); + octet[3] = (char)i; + return octet; +} + +QByteArray WampConnectionPrivate::PBKDF2(QString password, QString salt, int iterations) +{ + QList U; + QByteArray newSalt = salt.toLatin1().append(IntToOctet(1)); + QMessageAuthenticationCode hash(QCryptographicHash::Sha256); + for(int i=0;iregistration->execute(invocation->args); + QVariant val = result.resultData(); + QVariantList resultArr{val}; + QVariantList arr{(int)WampMsgCode::YIELD, invocation->requestId, QVariantMap()}; + if(val.isValid()) arr.append(QVariant(resultArr)); + sendWampMessage(arr); +} +void WampConnectionPrivate::sendWampMessage(const QVariantList &arr) +{ + QByteArray message = _serializer->serialize(arr); + if(_serializer->isBinary()) + { + _worker->sendBinaryMessage(message); + } + else + { + _worker->sendTextMessage(message); + } +} + +void WampConnectionPrivate::handleEvent(const Event& event) +{ + event.subscription->handle(event.args); +} +void WampConnectionPrivate::onConnected() +{ + Q_Q(WampConnection);; + + q->subscribe(KEY_SUBSCRIPTION_ON_CREATE, std::function{ + [q, this](double, QVariantMap info){ + QString topicUri = info["uri"].toString(); + Q_EMIT q->subscriptionCreated(topicUri); + if(!_topicObserver.contains(topicUri)) return; + SignalObserver* so = _topicObserver[topicUri]; + so->setEnabled(true); + }}); + q->subscribe(KEY_SUBSCRIPTION_ON_DELETE, std::function{ + [q, this](double, double, QString topic){ + Q_EMIT q->subscriptionDeleted(topic); + if(!_topicObserver.contains(topic)) return; + SignalObserver* so = _topicObserver[topic]; + so->setEnabled(false); + }}); + Q_EMIT q->connected(); +} + +WampConnection::WampConnection(QObject* parent) : WampBase(parent), d_ptr(new WampConnectionPrivate(this)) +{ + +} +WampConnection::~WampConnection() +{ + +} +QUrl WampConnection::url() const +{ + Q_D(const WampConnection); + return d->_url; +} +void WampConnection::setUrl(QUrl value) +{ + Q_D(WampConnection); + d->_url = value; + Q_EMIT urlChanged(); +} +QString WampConnection::realm() const +{ + Q_D(const WampConnection); + return d->_realm; +} +void WampConnection::setRealm(QString realm) +{ + Q_D(WampConnection); + d->_realm = realm; + Q_EMIT realmChanged(); +} +User* WampConnection::user() const +{ + Q_D(const WampConnection); + return d->_user; +} +void WampConnection::setUser(User* value) +{ + Q_D(WampConnection); + d->_user = value; + Q_EMIT userChanged(); +} + +void WampConnection::connect() +{ + Q_D(WampConnection); + QMetaObject::invokeMethod(d->_worker, "connect", Qt::QueuedConnection); +} +void WampConnection::disconnect() +{ + Q_D(WampConnection); + QMetaObject::invokeMethod(d->_socket.data(), "close", Qt::QueuedConnection); +} + +void WampConnectionPrivate::addRegistration(RegistrationPointer reg) +{ + qulonglong requestId = Random::generate(); + QVariantList arr{(int)WampMsgCode::REGISTER, requestId, QVariantMap(), reg->uri()}; + _pendingRegistrations[requestId] = reg; + sendWampMessage(arr); +} +void WampConnectionPrivate::addSubscription(SubscriptionPointer sub) +{ + qulonglong requestId = Random::generate(); + QVariantList arr{(int)WampMsgCode::SUBSCRIBE, requestId, QVariantMap(), sub->uri()}; + _pendingSubscriptions[requestId] = sub; + sendWampMessage(arr); +} +void WampConnection::addRegistration(RegistrationPointer reg) +{ + Q_D(WampConnection); + d->addRegistration(reg); +} +void WampConnection::addSubscription(SubscriptionPointer sub) +{ + Q_D(WampConnection); + d->addSubscription(sub); +} +void WampConnection::unregister(qulonglong registrationId) +{ + Q_D(WampConnection); + qulonglong requestId = Random::generate(); + QVariantList arr{(int)WampMsgCode::UNREGISTER, requestId, registrationId}; + + RegistrationPointer reg = d->_registrations.take(registrationId); + d->_pendingUnregistrations[requestId] = reg; + d->sendWampMessage(arr); +} +void WampConnection::unsubscribe(qulonglong subscriptionId) +{ + Q_D(WampConnection); + qulonglong requestId = Random::generate(); + QVariantList arr{(int)WampMsgCode::UNSUBSCRIBE, requestId, subscriptionId}; + + SubscriptionPointer sub = d->_subscriptions.take(subscriptionId); + d->_pendingUnsubscriptions[requestId] = sub; + d->sendWampMessage(arr); +} +void WampConnection::subscribe(QString uri, QJSValue callback) +{ + Q_D(WampConnection); + SubscriptionPointer sub(new JSSubscription(uri, callback)); + d->addSubscription(sub); +} +void WampConnection::subscribe(QString uri, QObject *obj, QString method) +{ + Q_D(WampConnection); + SubscriptionPointer sub(new MethodSubscription(uri, obj, method)); + d->addSubscription(sub); +} +void WampConnectionPrivate::call(QString uri, const QVariantList &args, CallPointer call) +{ + qulonglong requestId = Random::generate(); + QVariantList arr{(int)WampMsgCode::CALL, requestId, QVariantMap(), uri, args}; + _pendingCalls[requestId] = call; + sendWampMessage(arr); +} + +Future WampConnection::call(QString uri, const QVariantList& args, const QJSValue& callback) +{ + Q_D(WampConnection); + Impl* impl = NULL; + if(callback.isCallable()) impl = new JSImpl(callback); + CallPointer call(new Call(impl, this), CallDeleter()); + d->call(uri, args, call); + return call->getFuture(); +} +Future WampConnection::call(QString uri, const QVariantList &args, QObject *callbackObj, QString callbackMethod) +{ + Q_D(WampConnection); + Impl* impl = new MethodImpl(callbackObj, callbackMethod); + CallPointer call(new Call(impl), CallDeleter()); + d->call(uri, args, call); + return call->getFuture(); +} + +Future WampConnection::call2(QString uri, const QVariantList& args, ResultCallback callback) +{ + Q_D(WampConnection); + Impl* impl = NULL; + if(callback) + { + Functor* functor = new Functor(callback); + impl = new FunctorImpl(functor); + } + CallPointer call(new Call(impl), CallDeleter()); + d->call(uri, args, call); + return call->getFuture(); +} + +void WampConnection::publish(QString uri, const QVariantList& args) +{ + Q_D(WampConnection); + qulonglong requestId = Random::generate(); + QVariantList arr{(int)WampMsgCode::PUBLISH, requestId, QVariantMap(), uri, args}; + d->sendWampMessage(arr); +} +void WampConnection::define(QString uri, QString definition) +{ + call2(KEY_DEFINE_SCHEMA, {uri, definition}); +} +Future WampConnection::describe(QString uri) +{ + return call2(KEY_DESCRIBE_SCHEMA, {uri}); +} +Future WampConnection::lookupRegistration(QString uri) +{ + return call2(KEY_LOOKUP_REGISTRATION, {uri}); +} +Future WampConnection::listRegistrations() +{ + QVariantMap match; + match["match"] = "prefix"; + return call2("wamp.registration.list", QVariantList()); +} +Future WampConnection::getSubscription(qulonglong subscriptionId) +{ + return call2(KEY_GET_SUBSCRIPTION, {subscriptionId}); +} +Future WampConnection::subscribersCount(QString topicUri, ResultCallback callback) +{ + return call2(KEY_COUNT_SUBSCRIBERS, {topicUri}, callback); +} + +void WampConnection::unregister(QString uri) +{ + Q_D(WampConnection); + if(!d->_uriRegistration.contains(uri)) + { + qWarning() << QString("Cannot unregister not existing registration %1").arg(uri); + return; + } + qulonglong registrationId = d->_uriRegistration[uri]->registrationId(); + unregister(registrationId); +} +void WampConnection::unsubscribe(QString uri) +{ + Q_D(WampConnection); + if(!d->_uriSubscription.contains(uri)) + { + qWarning() << QString("Cannot unsubscribe from not existing subscription %1").arg(uri); + return; + } + qulonglong subscriptionId = d->_uriSubscription[uri]->subscriptionId(); + unsubscribe(subscriptionId); +} +void WampConnection::addSignalObserver(QString uri, SignalObserver *observer) +{ + Q_D(WampConnection); + d->_topicObserver[uri] = observer; + subscribersCount(uri, [observer](const QVariant& result){ + int count = result.toInt(); + observer->setEnabled(count > 0); + }); + QObject::connect(observer, &SignalObserver::signalEmitted, [this, uri](QVariantList args){ + publish(uri, args); + }); +} +} diff --git a/src/client/wampconnection.h b/src/client/wampconnection.h new file mode 100644 index 0000000..5e530d1 --- /dev/null +++ b/src/client/wampconnection.h @@ -0,0 +1,83 @@ +#ifndef WAMPCONNECTION_H +#define WAMPCONNECTION_H + +#include "wamp_global.h" +#include "wampbase.h" +#include "subscription_p.h" +#include "wamperror.h" +#include "future.h" +#include "user.h" +#include +#include +#include + + +namespace QFlow{ + +typedef std::function ResultCallback; + +class WampAttached; +class WampConnectionPrivate; +class WAMP_EXPORT WampConnection : public WampBase +{ + Q_OBJECT + Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged) + Q_PROPERTY(QString realm READ realm WRITE setRealm NOTIFY realmChanged) + Q_PROPERTY(User* user READ user WRITE setUser NOTIFY userChanged) + + friend class WampRouterPrivate; +public: + WampConnection(QObject* parent = NULL); + ~WampConnection(); + QUrl url() const; + void setUrl(QUrl value); + QString realm() const; + void setRealm(QString realm); + User* user() const; + void setUser(User* value); + template + void subscribe(QString uri, std::function f) + { + Functor* functor = new Functor(f); + SubscriptionPointer sub(new FunctorSubscription(uri, functor)); + addSubscription(sub); + } + void unregister(qulonglong registrationId); + void unsubscribe(qulonglong subscriptionId); + Future call2(QString uri, const QVariantList& args, ResultCallback callback = nullptr); +public Q_SLOTS: + void connect(); + void subscribe(QString uri, QJSValue callback); + void subscribe(QString uri, QObject* obj, QString method); + void unregister(QString uri); + void unsubscribe(QString uri); + Future lookupRegistration(QString uri); + Future listRegistrations(); + Future getSubscription(qulonglong subscriptionId); + Future subscribersCount(QString topicUri, ResultCallback callback = nullptr); + void publish(QString uri, const QVariantList& args); + Future call(QString uri, const QVariantList& args, const QJSValue& callback = QJSValue()); + Future call(QString uri, const QVariantList& args, QObject* callbackObj, QString callbackMethod); + void define(QString uri, QString definition); + Future describe(QString uri); + void disconnect(); +Q_SIGNALS: + void urlChanged(); + void realmChanged(); + void connected(); + void disconnected(); + void error(const WampError& error); + void userChanged(); + void textMessageReceived(const QString &message); + void subscriptionCreated(const QString &topicUri); + void subscriptionDeleted(const QString &topicUrl); +private: + void addRegistration(RegistrationPointer reg); + void addSubscription(SubscriptionPointer sub); + void addSignalObserver(QString uri, SignalObserver* observer); + const QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(WampConnection) +}; +typedef QSharedPointer WampConnectionPointer; +} +#endif // WAMPCONNECTION_H diff --git a/src/client/wampconnection_p.h b/src/client/wampconnection_p.h new file mode 100644 index 0000000..9092d87 --- /dev/null +++ b/src/client/wampconnection_p.h @@ -0,0 +1,86 @@ +#ifndef WAMPCONNECTION_P_H +#define WAMPCONNECTION_P_H + +#include "jsonhelper.h" +#include "registration_p.h" +#include "subscription_p.h" +#include "call.h" +#include +#include + +namespace QFlow{ + +class WebSocketConnection; +class WampWorker; +class WampInvocation; +typedef QSharedPointer WampInvocationPointer; + +class Event +{ +public: + SubscriptionPointer subscription; + QVariantList args; + qulonglong publicationId; + Event() : subscription(NULL) + { + + } + ~Event() + { + + } + Event(const Event& other) : subscription(other.subscription), args(other.args), publicationId(other.publicationId) + { + + } +}; + +class User; +class WampConnection; +class SignalObserver; +class WampMessageSerializer; +class WampConnectionPrivate : public QObject +{ + Q_OBJECT + friend class WampWorker; +public: + + WampConnectionPrivate(WampConnection* parent); + ~WampConnectionPrivate(); + QUrl _url; + QString _realm; + User* _user; + QPointer _socket; + QThread _workerThread; + WampWorker* _worker; + QScopedPointer _serializer; + QHash _pendingRegistrations; + QHash _pendingUnregistrations; + QHash _registrations; + QHash _uriRegistration; + QHash _pendingCalls; + QHash _topicObserver; + + QHash _pendingSubscriptions; + QHash _pendingUnsubscriptions; + QHash _subscriptions; + QHash _uriSubscription; + + void addRegistration(RegistrationPointer reg); + void addSubscription(SubscriptionPointer sub); + + static QByteArray PBKDF2(QString password, QString salt, int iterations); + static QByteArray IntToOctet(int i); + void onConnected(); +public Q_SLOTS: + void handleInvocation(WampInvocationPointer invocation); + void handleEvent(const Event& event); + void sendWampMessage(const QVariantList& arr); + void call(QString uri, const QVariantList& args, CallPointer call); +private: + WampConnection* q_ptr; + Q_DECLARE_PUBLIC(WampConnection) +}; +} +#endif // WAMPCONNECTION_P_H + diff --git a/src/client/wampworker.cpp b/src/client/wampworker.cpp new file mode 100644 index 0000000..e461ddd --- /dev/null +++ b/src/client/wampworker.cpp @@ -0,0 +1,214 @@ +#include "wampworker.h" +#include "wampconnection_p.h" +#include "wampconnection.h" +#include "credentialstore.h" +#include "wampinvocation.h" +#include "wampcrauser.h" +#include "helper.h" +#include "wampmessageserializer.h" +#include "websocketconnection.h" +#include "call.h" +#include +#include + +namespace QFlow{ + +WampWorker::WampWorker() : QObject(), _timer(new QTimer(this)) +{ + _timer->setInterval(5000); + QObject::connect(_timer, SIGNAL(timeout()), this, SLOT(reconnect())); +} +WampWorker::~WampWorker() +{ + _timer->stop(); + if(_socketPrivate->_socket) + { + delete _socketPrivate->_socket; + } +} + +void WampWorker::reconnect() +{ + _socketPrivate->_socket->connect(); +} + +void WampWorker::connect() +{ + _socketPrivate->_socket = new WebSocketConnection(); + _socketPrivate->_socket->setUri(_socketPrivate->_url.url()); + _socketPrivate->_socket->setRequestedSubprotocols({KEY_WAMP_JSON_SUB, KEY_WAMP_MSGPACK_SUB}); + QObject::connect(_socketPrivate->_socket.data(), SIGNAL(opened()), this, SLOT(opened())); + QObject::connect(_socketPrivate->_socket.data(), SIGNAL(closed()), this, SLOT(closed())); + QObject::connect(_socketPrivate->_socket.data(), SIGNAL(messageReceived(QByteArray)), this, SLOT(messageReceived(QByteArray))); + _socketPrivate->_socket->connect(); +} + +void WampWorker::sendTextMessage(const QString &message) +{ + _socketPrivate->_socket->sendText(message); +} +void WampWorker::sendBinaryMessage(const QByteArray &message) +{ + return _socketPrivate->_socket->sendBinary(message); +} +void WampWorker::closed() +{ + qDebug() << "WampConnection: WebSocket closed"; + Q_EMIT _socketPrivate->q_ptr->disconnected(); + _timer->start(); +} + +void WampWorker::opened() +{ + _timer->stop(); + QString sub = _socketPrivate->_socket->subprotocol(); + _socketPrivate->_serializer.reset(WampMessageSerializer::create(sub)); + QVariantMap options; + if(!_socketPrivate->_user) + { + CredentialStore store; + _socketPrivate->_user = new WampCraUser(_socketPrivate->q_ptr); + _socketPrivate->_user->setName(store.readUsername(_socketPrivate->_url)); + } + if(_socketPrivate->_user) + { + options["authid"] = _socketPrivate->_user->name(); + } + QVariantList authMethods{_socketPrivate->_user->authMethod()}; + options["authmethods"] = authMethods; + QVariantMap roles{{"publisher", QVariantMap()}, {"subscriber", QVariantMap()}, {"caller", QVariantMap()}, {"callee", QVariantMap()}}; + options["roles"] = roles; + QVariantList arr{WampMsgCode::HELLO, _socketPrivate->_realm, options}; + _socketPrivate->sendWampMessage(arr); +} +void WampWorker::messageReceived(const QByteArray &message) +{ + QVariantList arr = _socketPrivate->_serializer->deserialize(message); + WampMsgCode code = (WampMsgCode)arr[0].toInt(); + if(code == WampMsgCode::ERROR) + { + WampMsgCode subCode = (WampMsgCode)arr[1].toInt(); + qulonglong requestId = arr[2].toULongLong(); + QUrl uri = arr[4].toString(); + if(subCode == WampMsgCode::CALL) + { + CallPointer call = _socketPrivate->_pendingCalls.take(requestId); + call->resultReady(QVariant()); + } + QVariantMap details = arr[3].toMap(); + QVariantList args; + if(arr.count() > 5) + { + args = arr[5].toList(); + } + WampError wampError((int)subCode, requestId, details, uri, args); + Q_EMIT _socketPrivate->q_ptr->error(wampError); + + } + if(code == WampMsgCode::ABORT) + { + QString uri = arr[2].toString(); + QVariantMap details = arr[1].toMap(); + /*WampError wampError((int)WampMsgCode::ABORT, 0, details, uri, QVariantList()); + Q_EMIT _socketPrivate->q_ptr->error(wampError);*/ + qDebug() << uri; + } + if(code == WampMsgCode::WELCOME) + { + _socketPrivate->onConnected(); + } + else if(code == WampMsgCode::REGISTERED) + { + qulonglong regId = arr[2].toULongLong(); + qulonglong requestId = arr[1].toULongLong(); + RegistrationPointer reg = _socketPrivate->_pendingRegistrations.take(requestId); + reg->setRegistrationId(regId); + _socketPrivate->_registrations[regId] = reg; + _socketPrivate->_uriRegistration[reg->uri()] = reg; + } + else if(code == WampMsgCode::UNREGISTERED){ + qulonglong requestId = arr[1].toULongLong(); + RegistrationPointer reg = _socketPrivate->_pendingUnregistrations.take(requestId); + _socketPrivate->_registrations.remove(reg->registrationId()); + _socketPrivate->_uriRegistration.remove(reg->uri()); + + } + else if(code == WampMsgCode::SUBSCRIBED) + { + qulonglong subId = arr[2].toULongLong(); + qulonglong requestId = arr[1].toULongLong(); + SubscriptionPointer sub = _socketPrivate->_pendingSubscriptions.take(requestId); + sub->setSubscriptionId(subId); + _socketPrivate->_subscriptions[subId] = sub; + _socketPrivate->_uriSubscription[sub->uri()] = sub; + } + else if(code == WampMsgCode::UNSUBSCRIBED){ + qulonglong requestId = arr[1].toULongLong(); + SubscriptionPointer sub = _socketPrivate->_pendingUnsubscriptions.take(requestId); + _socketPrivate->_subscriptions.remove(sub->subscriptionId()); + _socketPrivate->_uriSubscription.remove(sub->uri()); + } + else if(code == WampMsgCode::INVOCATION) + { + qulonglong regId = arr[2].toULongLong(); + RegistrationPointer reg = _socketPrivate->_registrations[regId]; + QVariantList args; + if(arr.count() > 4 && (QMetaType::Type)arr[4].type() == QMetaType::QVariantList) + { + args = arr[4].toList(); + } + + WampInvocationPointer inv(new WampInvocation()); + inv->registration = reg; + inv->args = args; + inv->requestId = arr[1].toULongLong(); + QMetaObject::invokeMethod(_socketPrivate, "handleInvocation", Qt::QueuedConnection, Q_ARG(WampInvocationPointer, inv)); + } + else if(code == WampMsgCode::EVENT) + { + qulonglong subId = arr[1].toULongLong(); + if(!_socketPrivate->_subscriptions.contains(subId)) + { + qDebug() << "Event received for non existing subscritption " << subId; + return; + } + SubscriptionPointer sub = _socketPrivate->_subscriptions[subId]; + QVariantList args; + if(arr.count() > 4 && (QMetaType::Type)arr[4].type() == QMetaType::QVariantList) + { + args = arr[4].toList(); + } + Event event; + event.subscription = sub; + event.args = args; + event.publicationId = arr[2].toULongLong(); + QMetaObject::invokeMethod(_socketPrivate, "handleEvent", Qt::QueuedConnection, Q_ARG(Event, event)); + + } + else if(code == WampMsgCode::RESULT) + { + qulonglong requestId = arr[1].toULongLong(); + CallPointer call = _socketPrivate->_pendingCalls.take(requestId); + QVariant result; + if(arr.count() > 3) + { + QVariantList resultArray = arr[3].toList(); + result = resultArray[0]; + } + call->resultReady(result); + } + else if(code == WampMsgCode::PUBLISHED) + { + } + else if(code == WampMsgCode::CHALLENGE) + { + QVariantMap extra = arr[2].toMap(); + QByteArray challenge = extra["challenge"].toByteArray(); + QByteArray result = _socketPrivate->_user->response(challenge); + + QVariantList resArr{WampMsgCode::AUTHENTICATE, result, QVariantMap()}; + _socketPrivate->sendWampMessage(resArr); + } + Q_EMIT _socketPrivate->q_ptr->textMessageReceived(message); +} +} diff --git a/src/client/wampworker.h b/src/client/wampworker.h new file mode 100644 index 0000000..d0cfa9a --- /dev/null +++ b/src/client/wampworker.h @@ -0,0 +1,28 @@ +#ifndef WAMPWORKER_H +#define WAMPWORKER_H + +#include +#include + +namespace QFlow{ + +class WampConnectionPrivate; +class WampWorker : public QObject +{ + Q_OBJECT +public: + WampWorker(); + ~WampWorker(); + WampConnectionPrivate* _socketPrivate; + QTimer* _timer; +public Q_SLOTS: + void connect(); + void messageReceived(const QByteArray & message); + void opened(); + void closed(); + void sendTextMessage(const QString& message); + void sendBinaryMessage(const QByteArray& message); + void reconnect(); +}; +} +#endif // WAMPWORKER_H diff --git a/src/credentialstore.cpp b/src/credentialstore.cpp new file mode 100644 index 0000000..09a066b --- /dev/null +++ b/src/credentialstore.cpp @@ -0,0 +1,86 @@ +#include "credentialstore.h" + +#ifdef Q_OS_WIN +#include +#include +#endif + +#ifdef Q_OS_LINUX +#define SECRET_API_SUBJECT_TO_CHANGE +#include +#endif + +namespace QFlow{ + + +CredentialStore::CredentialStore(QObject *parent) : QObject(parent) +{ +} +CredentialStore::~CredentialStore() +{ + +} + +#ifdef Q_OS_LINUX +GList* getItems(QString credential) +{ + GError *error = NULL; + GHashTable* attrs = g_hash_table_new(NULL, NULL); + g_hash_table_insert(attrs, (gpointer)"url", g_strdup(credential.toUtf8().constData())); + + GList* items = secret_service_search_sync(NULL, // default secret service + NULL, attrs, + SECRET_SEARCH_NONE, + NULL, // no cancellable ojbect + &error); + return items; +} +#endif + + +QString CredentialStore::readPassword(QUrl resource) +{ +#ifdef Q_OS_WIN + PCREDENTIALW pcred; + BOOL ok = CredReadW((LPCWSTR)resource.url().data(), CRED_TYPE_GENERIC, 0, &pcred); + if(ok) return QString((const QChar*)pcred->CredentialBlob); + return QString(); +#endif +#ifdef Q_OS_LINUX + GList *items = getItems(resource.url()); + if(!items) return QString(); + GList* first = g_list_first (items); + SecretItem *item = (SecretItem*)first->data; + GError *error = NULL; + secret_item_load_secret_sync (item, NULL, &error); + SecretValue* secret = secret_item_get_secret (item); + const gchar *at; + gsize length; + at = secret_value_get (secret, &length); + QByteArray arr(at, length); + g_list_free_full (items, g_object_unref); + return QString(arr); +#endif +} +QString CredentialStore::readUsername(QUrl resource) +{ +#ifdef Q_OS_WIN + PCREDENTIALW pcred; + BOOL ok = CredReadW((LPCWSTR)resource.url().data(), CRED_TYPE_GENERIC, 0, &pcred); + if(ok) return QString((const QChar*)pcred->UserName); + return QString(); +#endif +#ifdef Q_OS_LINUX + GList *items = getItems(resource.url()); + if(!items) return QString(); + GList* first = g_list_first (items); + SecretItem *item = (SecretItem*)first->data; + GHashTable* attributes = secret_item_get_attributes (item); + const char* userC = (const char*)g_hash_table_lookup(attributes, (gpointer)"user"); + QString user(userC); + g_hash_table_unref (attributes); + g_list_free_full (items, g_object_unref); + return user; +#endif +} +} diff --git a/src/credentialstore.h b/src/credentialstore.h new file mode 100644 index 0000000..c7252ce --- /dev/null +++ b/src/credentialstore.h @@ -0,0 +1,22 @@ +#ifndef CREDENTIALSTORE_H +#define CREDENTIALSTORE_H + +#include +#include + +namespace QFlow{ + +class CredentialStore : public QObject +{ + Q_OBJECT +public: + explicit CredentialStore(QObject *parent = 0); + ~CredentialStore(); + QString readPassword(QUrl resource); + QString readUsername(QUrl resource); +Q_SIGNALS: + +public Q_SLOTS: +}; +} +#endif // CREDENTIALSTORE_H diff --git a/src/gssapiauthenticator.cpp b/src/gssapiauthenticator.cpp new file mode 100644 index 0000000..a106b43 --- /dev/null +++ b/src/gssapiauthenticator.cpp @@ -0,0 +1,180 @@ +#include "gssapiauthenticator.h" +#include "sid.h" +#include + +#ifdef Q_OS_WIN +#include +#define SECURITY_WIN32 +#include +#endif + +namespace QFlow{ + +class GSSAPIAuthenticatorPrivate +{ +public: + QString _authMethod; + GSSAPIAuthenticatorPrivate() + { + + } + ~GSSAPIAuthenticatorPrivate() + { + + } +}; +class GSSAPISession: public AuthSession +{ +public: + QString _authMethod; + bool _first; +#ifdef Q_OS_WIN + CredHandle _serverHandle; + SecBufferDesc _outSecBufDesc; + SecBuffer _outSecBuf; + SecBufferDesc _inSecBufDesc; + SecBuffer _inSecBuf; + PSecPkgInfo _info; + CtxtHandle _context; + ULONG _attrs; + TimeStamp _serviceLifetime; + HANDLE _secToken; + SecPkgContext_Names _names; +#endif + GSSAPISession(QString authMethod) : _authMethod(authMethod), _first(true) + { +#ifdef Q_OS_WIN + SECURITY_STATUS status; + + LPWSTR methodName = new WCHAR[_authMethod.length()+1]; + _authMethod.toWCharArray(methodName); + methodName[_authMethod.length()] = 0; + status = QuerySecurityPackageInfo(methodName, &_info); + TimeStamp serverLifetime; + status = AcquireCredentialsHandle(NULL, methodName, SECPKG_CRED_BOTH, NULL, NULL, NULL, NULL, &_serverHandle, &serverLifetime); + + _outSecBufDesc.ulVersion = 0; + _outSecBufDesc.cBuffers = 1; + _outSecBufDesc.pBuffers = &_outSecBuf; + _outSecBuf.cbBuffer = _info->cbMaxToken; + _outSecBuf.BufferType = SECBUFFER_TOKEN; + _outSecBuf.pvBuffer = (PBYTE) malloc (_info->cbMaxToken); + _inSecBufDesc.ulVersion = 0; + _inSecBufDesc.cBuffers = 1; + _inSecBufDesc.pBuffers = &_inSecBuf; + _inSecBuf.BufferType = SECBUFFER_TOKEN; + delete methodName; +#endif + } + ~GSSAPISession() + { +#ifdef Q_OS_WIN + free(_outSecBuf.pvBuffer); +#endif + } + QVariant securityToken() const + { +#ifdef Q_OS_WIN + return QVariant::fromValue(_secToken); +#endif +#ifdef Q_OS_UNIX + throw "not implemented"; + return QVariant(); +#endif + } + + AUTH_RESULT authenticate() + { +#ifdef Q_OS_WIN + SECURITY_STATUS status; + _inSecBuf.cbBuffer = inBuffer.length(); + _inSecBuf.BufferType = SECBUFFER_TOKEN; + _inSecBuf.pvBuffer = inBuffer.data(); + _outSecBuf.cbBuffer = _info->cbMaxToken; + + CtxtHandle* contextPtr = &_context; + if(_first) + { + contextPtr = NULL; + _first = false; + } + status = AcceptSecurityContext ( + &_serverHandle, + contextPtr, + &_inSecBufDesc, + 0, + SECURITY_NATIVE_DREP, + &_context, + &_outSecBufDesc, + &_attrs, + &_serviceLifetime + ); + + + outBuffer = QByteArray((const char *)_outSecBuf.pvBuffer, _outSecBuf.cbBuffer); + if(status == SEC_I_CONTINUE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) return AUTH_RESULT::CONTINUE; + if(status == 0) + { + + status = QuerySecurityContextToken(&_context, &_secToken); + status = QueryContextAttributes(&_context, SECPKG_ATTR_NAMES, &_names); + return AUTH_RESULT::ACCEPTED; + } + + return AUTH_RESULT::REJECTED; +#endif +#ifdef Q_OS_LINUX + throw "not implemented"; + return AUTH_RESULT::REJECTED; +#endif + } + +}; + +GSSAPIAuthenticator::GSSAPIAuthenticator(QObject *parent) : Authenticator(parent), d_ptr(new GSSAPIAuthenticatorPrivate()) +{ + +} +GSSAPIAuthenticator::~GSSAPIAuthenticator() +{ + +} + +QString GSSAPIAuthenticator::authMethod() const +{ + Q_D(const GSSAPIAuthenticator); + return d->_authMethod; +} +void GSSAPIAuthenticator::setAuthMethod(const QString &value) +{ + Q_D(GSSAPIAuthenticator); + d->_authMethod = value; +} +QVariantMap GSSAPIAuthenticator::generateChallenge(qulonglong sessionId, QString authId) +{ + Q_UNUSED(sessionId); + Q_UNUSED(authId); + return QVariantMap(); +} +AuthSession* GSSAPIAuthenticator::createSession() const +{ + Q_D(const GSSAPIAuthenticator); + return new GSSAPISession(d->_authMethod); +} +User* GSSAPIAuthenticator::getUser(AuthSession *session) +{ + GSSAPISession* gssSession = static_cast(session); + if(!gssSession) return NULL; + for(User* user: _users.values()) + { + QFlow::SIDUser* sid = static_cast(user); + if(!sid) continue; + bool isMember = sid->checkTokenMembership(gssSession->securityToken()); + if(isMember) + { + return user; + } + } + return NULL; +} +} diff --git a/src/gssapiauthenticator.h b/src/gssapiauthenticator.h new file mode 100644 index 0000000..e585b6d --- /dev/null +++ b/src/gssapiauthenticator.h @@ -0,0 +1,30 @@ +#ifndef GSSAPIAUTHENTICATOR_H +#define GSSAPIAUTHENTICATOR_H + +#include "authenticator.h" +#include + + +namespace QFlow{ +class GSSAPIAuthenticatorPrivate; +class GSSAPIAuthenticator : public Authenticator +{ + Q_OBJECT + Q_PROPERTY(QString authMethod READ authMethod WRITE setAuthMethod) +public: + explicit GSSAPIAuthenticator(QObject *parent = 0); + ~GSSAPIAuthenticator(); + QString authMethod() const; + void setAuthMethod(const QString& value); + QVariantMap generateChallenge(qulonglong sessionId, QString authId); + AuthSession* createSession() const; + User* getUser(AuthSession* session); +Q_SIGNALS: + +public Q_SLOTS: +private: + const QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(GSSAPIAuthenticator) +}; +} +#endif // GSSAPIAUTHENTICATOR_H diff --git a/src/gssapiuser.cpp b/src/gssapiuser.cpp new file mode 100644 index 0000000..df30400 --- /dev/null +++ b/src/gssapiuser.cpp @@ -0,0 +1,127 @@ +#include "gssapiuser.h" +#include + +#ifdef Q_OS_WIN +#include +#define SECURITY_WIN32 +#include +#endif + +namespace QFlow{ + +class GSSAPIUserPrivate +{ +public: + QString _authMethod; + bool _first; +#ifdef Q_OS_WIN + CredHandle _handle; + CtxtHandle _context; + SecBufferDesc _outSecBufDesc; + SecBuffer _outSecBuf; + SecBufferDesc _inSecBufDesc; + SecBuffer _inSecBuf; + PSecPkgInfo _info; + ULONG _attrs; + TimeStamp _serviceLifetime; +#endif + GSSAPIUserPrivate() : _first(true) + { + + } + ~GSSAPIUserPrivate() + { + + } +}; + +GSSAPIUser::GSSAPIUser(QObject *parent) : User(parent), d_ptr(new GSSAPIUserPrivate()) +{ + +} +GSSAPIUser::~GSSAPIUser() +{ + +} +QString GSSAPIUser::authMethod() const +{ + Q_D(const GSSAPIUser); + return d->_authMethod; +} +void GSSAPIUser::setAuthMethod(const QString &value) +{ + Q_D(GSSAPIUser); + d->_authMethod = value; + +#ifdef Q_OS_WIN + SECURITY_STATUS status; + + LPWSTR methodName = new WCHAR[d->_authMethod.length()+1]; + d->_authMethod.toWCharArray(methodName); + methodName[d->_authMethod.length()] = 0; + status = QuerySecurityPackageInfo(methodName, &d->_info); + TimeStamp lifetime; + status = AcquireCredentialsHandle(NULL, methodName, SECPKG_CRED_OUTBOUND, NULL, NULL, NULL, NULL, &d->_handle, &lifetime); + + + d->_outSecBufDesc.ulVersion = 0; + d->_outSecBufDesc.cBuffers = 1; + d->_outSecBufDesc.pBuffers = &d->_outSecBuf; + d->_outSecBuf.cbBuffer = d->_info->cbMaxToken; + d->_outSecBuf.BufferType = SECBUFFER_TOKEN; + d->_outSecBuf.pvBuffer = (PBYTE) malloc (d->_info->cbMaxToken); + + d->_inSecBufDesc.ulVersion = 0; + d->_inSecBufDesc.cBuffers = 1; + d->_inSecBufDesc.pBuffers = &d->_inSecBuf; + d->_inSecBuf.BufferType = SECBUFFER_TOKEN; + + SecPkgCredentials_Names names; + status = QueryCredentialsAttributes(&d->_handle, SECPKG_CRED_ATTR_NAMES, &names); + QString username((const QChar*)names.sUserName); + setName(username); +#endif +} + +QByteArray GSSAPIUser::response(QByteArray challenge) +{ +#ifdef Q_OS_WIN + Q_D(GSSAPIUser); + SECURITY_STATUS status; + d->_inSecBuf.cbBuffer = challenge.length(); + d->_inSecBuf.pvBuffer = challenge.data(); + d->_outSecBuf.cbBuffer = d->_info->cbMaxToken; + CtxtHandle* ctxPointer = &d->_context; + SecBufferDesc* inBufferPointer = &d->_inSecBufDesc; + if(d->_first) + { + ctxPointer = NULL; + inBufferPointer = NULL; + d->_first = false; + } + + status = InitializeSecurityContext ( + &d->_handle, + ctxPointer, + NULL, + // context requirements + 0, + 0, // reserved1 + SECURITY_NATIVE_DREP, + inBufferPointer, + 0, + &d->_context, + &d->_outSecBufDesc, + &d->_attrs, + &d->_serviceLifetime + ); + QByteArray token((const char *)d->_outSecBuf.pvBuffer, d->_outSecBuf.cbBuffer); + return token; +#endif +#ifdef Q_OS_LINUX + Q_UNUSED(challenge); + throw "not implemented"; + return QByteArray(); +#endif +} +} diff --git a/src/gssapiuser.h b/src/gssapiuser.h new file mode 100644 index 0000000..31cd54e --- /dev/null +++ b/src/gssapiuser.h @@ -0,0 +1,28 @@ +#ifndef GSSAPIUSER_H +#define GSSAPIUSER_H + +#include "user.h" +#include + +namespace QFlow{ + +class GSSAPIUserPrivate; +class GSSAPIUser : public User +{ + Q_OBJECT + Q_PROPERTY(QString authMethod READ authMethod WRITE setAuthMethod) +public: + explicit GSSAPIUser(QObject *parent = 0); + ~GSSAPIUser(); + QByteArray response(QByteArray challenge); + QString authMethod() const; + void setAuthMethod(const QString& value); +Q_SIGNALS: + +public Q_SLOTS: +private: + const QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(GSSAPIUser) +}; +} +#endif // GSSAPIUSER_H diff --git a/src/helper.cpp b/src/helper.cpp new file mode 100644 index 0000000..ba3caca --- /dev/null +++ b/src/helper.cpp @@ -0,0 +1,33 @@ +#include "helper.h" +#include +#include +#include + +namespace QFlow{ +Helper::Helper() +{ + +} +QJSValue Helper::variantToJS(const QVariant& var, QJSEngine* engine) +{ + if(var.isNull() || !var.isValid()) return QJSValue(QJSValue::NullValue); + if((QMetaType::Type)var.type() == QMetaType::QByteArray) + { + QByteArray ba = var.toByteArray(); + QJSValue v = engine->toScriptValue(var); + return v; + /*QJSValue arr = engine->newArray(ba.length()); + for(quint32 i=0; i()) return QJSValue(var.toInt()); + if(var.canConvert()) return QJSValue(var.toUInt()); + if(var.canConvert()) return QJSValue(var.toDouble()); + if(var.canConvert()) return QJSValue(var.toBool()); + if(var.canConvert()) return QJSValue(var.toString()); + return QJSValue(); +} +} diff --git a/src/helper.h b/src/helper.h new file mode 100644 index 0000000..2ca4cdb --- /dev/null +++ b/src/helper.h @@ -0,0 +1,17 @@ +#ifndef HELPER_H +#define HELPER_H + +#include +#include + +namespace QFlow{ +class Helper +{ +public: + Helper(); + static QJSValue variantToJS(const QVariant& var, QJSEngine* engine); +private: + static QJSEngine _engine; +}; +} +#endif // HELPER_H diff --git a/src/qmldir b/src/qmldir new file mode 100644 index 0000000..d7f1fde --- /dev/null +++ b/src/qmldir @@ -0,0 +1,2 @@ +module QFlow.Wamp +plugin wamp diff --git a/src/router/authenticator.cpp b/src/router/authenticator.cpp new file mode 100644 index 0000000..950b4ee --- /dev/null +++ b/src/router/authenticator.cpp @@ -0,0 +1,61 @@ +#include "authenticator.h" +#include "user.h" + + +namespace QFlow{ + +AuthSession::AuthSession() : user(NULL) +{ + +} +AuthSession::AuthSession(const AuthSession &other) : challenge(other.challenge), authenticator(other.authenticator), + user(other.user), inBuffer(other.inBuffer) +{ + +} + +AuthSession::~AuthSession() +{ + +} +Authenticator::Authenticator(QObject *parent) : QObject(parent) +{ + +} +void Authenticator::users_append(QQmlListProperty *prop, User *item) +{ + Authenticator* auth = static_cast(prop->object); + auth->_users.insert(item->name(), item); +} + +int Authenticator::users_count(QQmlListProperty *prop) +{ + Authenticator* auth = static_cast(prop->object); + return auth->_users.count(); +} + +User *Authenticator::users_at(QQmlListProperty *prop, int index) +{ + Authenticator* auth = static_cast(prop->object); + return auth->_users.values().at(index); +} + +void Authenticator::users_clear(QQmlListProperty *prop) +{ + Authenticator* auth = static_cast(prop->object); + auth->_users.clear(); +} +QQmlListProperty Authenticator::users() +{ + return QQmlListProperty(this, NULL, users_append, users_count, users_at, users_clear); +} + +bool Authenticator::containsUser(QString userName) const +{ + return _users.contains(userName); +} +User* Authenticator::user(QString userName) const +{ + return _users[userName]; +} +} diff --git a/src/router/authenticator.h b/src/router/authenticator.h new file mode 100644 index 0000000..aa51932 --- /dev/null +++ b/src/router/authenticator.h @@ -0,0 +1,60 @@ +#ifndef AUTHENTICATOR +#define AUTHENTICATOR + +#include "wamp_global.h" +#include "symbols.h" +#include "role.h" +#include +#include +#include +#include +#include + +namespace QFlow{ + +class Authenticator; + +class WampRouterSession; +typedef QSharedPointer WampRouterSessionPointer; +class User; + +enum class AUTH_RESULT {ACCEPTED = 1, REJECTED = 0, CONTINUE = 2}; +class AuthSession +{ +public: + QVariantMap challenge; + QPointer authenticator; + User* user; + QByteArray inBuffer; + QByteArray outBuffer; + AuthSession(); + AuthSession(const AuthSession &other); + virtual ~AuthSession(); + virtual AUTH_RESULT authenticate() = 0; +}; + +class WAMP_EXPORT Authenticator : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty users READ users) + + static void users_append(QQmlListProperty *prop, User *item); + static int users_count(QQmlListProperty *prop); + static User *users_at(QQmlListProperty *prop, int index); + static void users_clear(QQmlListProperty *prop); +public: + Authenticator(QObject* parent = NULL); + virtual QVariantMap generateChallenge(qulonglong sessionId, QString authId) = 0; + virtual QString authMethod() const = 0; + QQmlListProperty users(); + bool containsUser(QString userName) const; + User* user(QString userName) const; + virtual AuthSession* createSession() const = 0; + virtual User* getUser(AuthSession* session) = 0; +protected: + QHash _users; +}; +} + +#endif // AUTHENTICATOR + diff --git a/src/router/authorizer.cpp b/src/router/authorizer.cpp new file mode 100644 index 0000000..5208817 --- /dev/null +++ b/src/router/authorizer.cpp @@ -0,0 +1,8 @@ +#include "authorizer.h" + +namespace QFlow{ +Authorizer::Authorizer(QObject* parent) : QObject(parent) +{ + +} +} diff --git a/src/router/authorizer.h b/src/router/authorizer.h new file mode 100644 index 0000000..6c06fed --- /dev/null +++ b/src/router/authorizer.h @@ -0,0 +1,21 @@ +#ifndef AUTHORIZER +#define AUTHORIZER + +#include "wamp_global.h" +#include "wamp_symbols.h" +#include +#include + +namespace QFlow{ + +class WAMP_EXPORT Authorizer : public QObject +{ + Q_OBJECT +public: + Authorizer(QObject* parent = NULL); + virtual bool authorize(QString authId, QString uri, WampMsgCode action) = 0; +}; +} + +#endif // AUTHORIZER + diff --git a/src/router/defaultauthorizer.cpp b/src/router/defaultauthorizer.cpp new file mode 100644 index 0000000..0727d50 --- /dev/null +++ b/src/router/defaultauthorizer.cpp @@ -0,0 +1,119 @@ +#include "defaultauthorizer.h" +#include "symbols.h" +#include +#include + +namespace QFlow{ + +class DefaultAuthorizerPrivate +{ +public: + bool _allowCall; + bool _allowRegister; + bool _allowSubscribe; + bool _allowPublish; + QRegExp _regExp; + DefaultAuthorizerPrivate() : _allowCall(true), _allowRegister(true), _allowSubscribe(true), + _allowPublish(true) + { + _regExp.setPatternSyntax(QRegExp::Wildcard); + } + ~DefaultAuthorizerPrivate() + { + + } +}; + + +DefaultAuthorizer::DefaultAuthorizer(QObject *parent) : Authorizer(parent), d_ptr(new DefaultAuthorizerPrivate()) +{ + +} +DefaultAuthorizer::~DefaultAuthorizer() +{ + +} +bool DefaultAuthorizer::allowCall() const +{ + Q_D(const DefaultAuthorizer); + return d->_allowCall; +} +void DefaultAuthorizer::setAllowCall(bool value) +{ + Q_D(DefaultAuthorizer); + d->_allowCall = value; + Q_EMIT allowCallChanged(); +} +bool DefaultAuthorizer::allowRegister() const +{ + Q_D(const DefaultAuthorizer); + return d->_allowRegister; +} +void DefaultAuthorizer::setAllowRegister(bool value) +{ + Q_D(DefaultAuthorizer); + d->_allowRegister = value; + Q_EMIT allowRegisterChanged(); +} +bool DefaultAuthorizer::allowSubscribe() const +{ + Q_D(const DefaultAuthorizer); + return d->_allowSubscribe; +} +void DefaultAuthorizer::setAllowSubscribe(bool value) +{ + Q_D(DefaultAuthorizer); + d->_allowSubscribe = value; + Q_EMIT allowSubscribeChanged(); +} +bool DefaultAuthorizer::allowPublish() const +{ + Q_D(const DefaultAuthorizer); + return d->_allowPublish; +} +void DefaultAuthorizer::setAllowPublish(bool value) +{ + Q_D(DefaultAuthorizer); + d->_allowPublish = value; + Q_EMIT allowPublishChanged(); +} +QString DefaultAuthorizer::uriPattern() const +{ + Q_D(const DefaultAuthorizer); + return d->_regExp.pattern(); +} +void DefaultAuthorizer::setUriPattern(QString value) +{ + Q_D(DefaultAuthorizer); + d->_regExp.setPattern(value); +} + +bool DefaultAuthorizer::authorize(QString /*authId*/, QString uri, WampMsgCode action) +{ + Q_D(DefaultAuthorizer); + bool match = true; + if(!d->_regExp.isEmpty()) + { + match = d->_regExp.exactMatch(uri); + } + if(action == WampMsgCode::CALL) + { + if(!d->_allowCall) return false; + return match; + } + else if(action == WampMsgCode::PUBLISH) + { + return d->_allowPublish; + } + else if(action == WampMsgCode::SUBSCRIBE) + { + if(!d->_allowSubscribe) return false; + return match; + } + else if(action == WampMsgCode::REGISTER) + { + return d->_allowRegister; + } + return false; +} +} diff --git a/src/router/defaultauthorizer.h b/src/router/defaultauthorizer.h new file mode 100644 index 0000000..4a75e5b --- /dev/null +++ b/src/router/defaultauthorizer.h @@ -0,0 +1,46 @@ +#ifndef DEFAULTAUTHORIZER_H +#define DEFAULTAUTHORIZER_H + +#include "wamp_global.h" +#include "authorizer.h" +#include + +namespace QFlow{ + +class DefaultAuthorizerPrivate; +class WAMP_EXPORT DefaultAuthorizer : public Authorizer +{ + Q_OBJECT + Q_PROPERTY(bool allowCall READ allowCall WRITE setAllowCall NOTIFY allowCallChanged) + Q_PROPERTY(bool allowRegister READ allowRegister WRITE setAllowRegister NOTIFY allowRegisterChanged) + Q_PROPERTY(bool allowSubscribe READ allowSubscribe WRITE setAllowSubscribe NOTIFY allowSubscribeChanged) + Q_PROPERTY(bool allowPublish READ allowPublish WRITE setAllowPublish NOTIFY allowPublishChanged) + Q_PROPERTY(QString uriPattern READ uriPattern WRITE setUriPattern NOTIFY uriPatternChanged) +public: + explicit DefaultAuthorizer(QObject *parent = 0); + ~DefaultAuthorizer(); + bool allowCall() const; + void setAllowCall(bool value); + bool allowRegister() const; + void setAllowRegister(bool value); + bool allowSubscribe() const; + void setAllowSubscribe(bool value); + bool allowPublish() const; + void setAllowPublish(bool value); + QString uriPattern() const; + void setUriPattern(QString value); + bool authorize(QString authId, QString uri, WampMsgCode action); +Q_SIGNALS: + void allowCallChanged(); + void allowRegisterChanged(); + void allowSubscribeChanged(); + void allowPublishChanged(); + void uriPatternChanged(); +public Q_SLOTS: + +private: + const QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(DefaultAuthorizer) +}; +} +#endif // DEFAULTAUTHORIZER_H diff --git a/src/router/realm.cpp b/src/router/realm.cpp new file mode 100644 index 0000000..3276eba --- /dev/null +++ b/src/router/realm.cpp @@ -0,0 +1,204 @@ +#include "realm.h" +#include "realm_p.h" +#include "wamprouter_p.h" +#include "authenticator.h" +#include "random.h" + +namespace QFlow{ + +RealmPrivate::RealmPrivate() +{ + +} +RealmPrivate::~RealmPrivate() +{ + +} + +Realm::Realm(QObject *parent) : WampBase(parent), d_ptr(new RealmPrivate()) +{ + registerMethod(KEY_LIST_REGISTRATION_URIS, this, "registeredUris()"); + registerMethod("wamp.registration.list", this, "registrationIds()"); + registerMethod("wamp.registration.list_internal_uris", this, "registeredInternalUris()"); + + registerMethod("wamp.subscription.count_subscribers", this, "subscribersCount(QString)"); + registerProcedure(KEY_GET_SUBSCRIPTION, [this](QVariantList args){//register some object + qulonglong subscriptionId = (qulonglong)args[0].toDouble(); + if(!this->d_ptr->_subscriptions.contains(subscriptionId)) + { + return WampResult(QVariant("wamp.error.no_such_subscription")); + } + WampRouterSubscriptionPointer subscription = this->d_ptr->_subscriptions[subscriptionId]; + QVariantList resultArr; + QVariantMap details; + details["id"] = (double)subscription->subscriptionId(); + details["created"] = subscription->created().toString("yyyy-mm-ddThh:mm:zzzZ"); + details["uri"] = subscription->topic(); + resultArr.append(details); + return WampResult(QVariant(resultArr)); + }); +} +Realm::~Realm() +{ + +} +QString Realm::name() const +{ + Q_D(const Realm); + return d->_name; +} +void Realm::setName(QString value) +{ + Q_D(Realm); + d->_name = value; +} +QQmlListProperty Realm::roles() +{ + Q_D(Realm); + return QQmlListProperty(this, d->_roles); +} +QQmlListProperty Realm::authenticators() +{ + Q_D(Realm); + return QQmlListProperty(this, d->_authenticators); +} +Authenticator* Realm::findAuthenticatorByAuthMethod(QString authMethod) const +{ + Q_D(const Realm); + Q_FOREACH(Authenticator* auth, d->_authenticators) + { + if(auth->authMethod() == authMethod) return auth; + } + return NULL; +} +bool Realm::containsRegistration(QString uri) +{ + Q_D(Realm); + QMutexLocker lock(&d->_mutex); + return d->_uriRegistartion.contains(uri); +} +QStringList Realm::registeredUris() +{ + Q_D(Realm); + QMutexLocker lock(&d->_mutex); + return d->_uriRegistartion.keys(); +} +QStringList Realm::registeredInternalUris() +{ + Q_D(Realm); + QMutexLocker lock(&d->_mutex); + return d->_internalRegistrations.keys(); +} +QVariantList Realm::registrationIds() +{ + Q_D(Realm); + QVariantList res; + QMutexLocker lock(&d->_mutex); + Q_FOREACH (qulonglong id, d->_idRegistartion.keys()) + { + res << id; + } + return res; +} +void Realm::addRegistration(RegistrationPointer reg) +{ + Q_D(Realm); + d->_internalRegistrations.insert(reg->uri(), reg); +} +void Realm::addSignalObserver(QString /*uri*/, SignalObserver* /*observer*/) +{ + +} + +qulonglong RealmPrivate::publish(QString topic, const QVariantList& args) +{ + qulonglong publicationId = Random::generate(); + if(_uriSubscriptions.contains(topic)) + { + QList subscriptions = _uriSubscriptions.values(topic); + Q_FOREACH (WampRouterSubscriptionPointer subscription, subscriptions) + { + subscription->event(publicationId, args); + } + } + return publicationId; +} +qulonglong Realm::publish(QString topic, const QVariantList &args) +{ + Q_D(Realm); + return d->publish(topic, args); +} + +int Realm::subscribersCount(QString topicUri) +{ + Q_D(Realm); + QMutexLocker lock(&d->_mutex); + return d->_uriSubscriptions.count(topicUri); +} +void RealmPrivate::insertRegistration(QString uri, WampRouterRegistrationPointer registration) +{ + QMutexLocker lock(&_mutex); + _uriRegistartion[uri] = registration; + _idRegistartion.insert(registration->registrationId(), registration); +} +WampRouterRegistrationPointer RealmPrivate::getRegistration(qulonglong registrationId) +{ + QMutexLocker lock(&_mutex); + return _idRegistartion[registrationId]; +} +WampRouterRegistrationPointer RealmPrivate::getRegistration(QString uri) +{ + QMutexLocker lock(&_mutex); + return _uriRegistartion[uri]; +} + +void RealmPrivate::removeRegistration(WampRouterRegistrationPointer reg) +{ + QMutexLocker lock(&_mutex); + _uriRegistartion.remove(reg->uri()); + _idRegistartion.remove(reg->registrationId()); +} +void RealmPrivate::insertPendingInvocation(qulonglong requestId, WampRouterSession *session) +{ + QMutexLocker lock(&_mutex); + _pendingInvocations.insert(requestId, session); +} +bool RealmPrivate::containsInternalRegistration(QString uri) +{ + QMutexLocker lock(&_mutex); + return _internalRegistrations.contains(uri); +} +RegistrationPointer RealmPrivate::getInternalRegistration(QString uri) +{ + QMutexLocker lock(&_mutex); + return _internalRegistrations[uri]; +} +WampRouterSession* RealmPrivate::takePendingInvocation(qulonglong requestId) +{ + QMutexLocker lock(&_mutex); + return _pendingInvocations.take(requestId); +} +void RealmPrivate::insertSubscription(qulonglong subscriptionId, QString topic, WampRouterSubscriptionPointer subscription) +{ + QMutexLocker lock(&_mutex); + _subscriptions.insert(subscriptionId, subscription); + _uriSubscriptions.insertMulti(topic, subscription); +} +bool RealmPrivate::containsSubscription(QString topic) +{ + QMutexLocker lock(&_mutex); + return _uriSubscriptions.contains(topic); +} +WampRouterSubscriptionPointer RealmPrivate::takeSubscription(qulonglong subscriptionId) +{ + QMutexLocker lock(&_mutex); + WampRouterSubscriptionPointer sub = _subscriptions.take(subscriptionId); + _uriSubscriptions.remove(sub->topic(), sub); + if(!_uriSubscriptions.contains(sub->topic())) + { + publish(KEY_SUBSCRIPTION_ON_DELETE, {sub->subscriber()->sessionId(), sub->subscriptionId(), sub->topic()}); + } + publish(KEY_SUBSCRIPTION_ON_UNSUBSCRIBE, {sub->subscriber()->sessionId(), sub->subscriptionId(), sub->topic()}); + return sub; +} +} diff --git a/src/router/realm.h b/src/router/realm.h new file mode 100644 index 0000000..cc55340 --- /dev/null +++ b/src/router/realm.h @@ -0,0 +1,48 @@ +#ifndef REALM_H +#define REALM_H + +#include "wamp_global.h" +#include "wampbase.h" +#include +#include +#include +#include + +namespace QFlow{ + +class Authenticator; +class Role; +class RealmPrivate; +class WAMP_EXPORT Realm : public WampBase +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(QQmlListProperty roles READ roles) + Q_PROPERTY(QQmlListProperty authenticators READ authenticators) + friend class WampRouterWorker; + friend class WampRouterSessionPrivate; +public: + explicit Realm(QObject* parent = NULL); + QString name() const; + void setName(QString value); + QQmlListProperty roles(); + QQmlListProperty authenticators(); + Authenticator* findAuthenticatorByAuthMethod(QString authMethod) const; + ~Realm(); +public Q_SLOTS: + bool containsRegistration(QString uri); + QStringList registeredUris(); + QStringList registeredInternalUris(); + QVariantList registrationIds(); + qulonglong publish(QString topic, const QVariantList& args); + int subscribersCount(QString topicUri); +protected: + void addRegistration(RegistrationPointer reg); + void addSignalObserver(QString uri, SignalObserver* observer); +private: + const QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(Realm) +}; +} + +#endif // REALM_H diff --git a/src/router/realm_p.h b/src/router/realm_p.h new file mode 100644 index 0000000..a16c26d --- /dev/null +++ b/src/router/realm_p.h @@ -0,0 +1,57 @@ +#ifndef REALM_P_H +#define REALM_P_H + +#include +#include +#include +#include + +class QWebSocket; + +namespace QFlow{ + +class WampRouterSession; +typedef QSharedPointer WampRouterSessionPointer; +class Authenticator; +class Role; +class WampRouterRegistration; +typedef QSharedPointer WampRouterRegistrationPointer; +class WampRouterSubscription; +typedef QSharedPointer WampRouterSubscriptionPointer; +class Registration; +typedef QSharedPointer RegistrationPointer; + +class RealmPrivate +{ +public: + QMutex _mutex; + QString _name; + QHash _uriRegistartion; + QHash _idRegistartion; + QHash _pendingInvocations; + void insertPendingInvocation(qulonglong requestId, WampRouterSession* session); + QList _roles; + QList _authenticators; + QHash _internalRegistrations; + void insertRegistration(QString uri, WampRouterRegistrationPointer registration); + WampRouterRegistrationPointer getRegistration(qulonglong registrationId); + WampRouterRegistrationPointer getRegistration(QString uri); + void removeRegistration(WampRouterRegistrationPointer reg); + bool containsInternalRegistration(QString uri); + RegistrationPointer getInternalRegistration(QString uri); + WampRouterSession* takePendingInvocation(qulonglong requestId); + + QHash _subscriptions; + WampRouterSubscriptionPointer takeSubscription(qulonglong subscriptionId); + void insertSubscription(qulonglong subscriptionId, QString topic, WampRouterSubscriptionPointer subscription); + QMultiHash _uriSubscriptions; + bool containsSubscription(QString topic); + qulonglong publish(QString topic, const QVariantList& args); + + RealmPrivate(); + ~RealmPrivate(); +}; +} + +#endif // REALM_P_H + diff --git a/src/router/role.cpp b/src/router/role.cpp new file mode 100644 index 0000000..95043db --- /dev/null +++ b/src/router/role.cpp @@ -0,0 +1,48 @@ +#include "role.h" + +namespace QFlow{ + +class RolePrivate +{ +public: + QString _name; + Authorizer* _authorizer; + RolePrivate() + { + + } + ~RolePrivate() + { + + } +}; + +Role::Role(QObject *parent) : QObject(parent), d_ptr(new RolePrivate()) +{ + +} +Role::~Role() +{ + +} +QString Role::name() const +{ + Q_D(const Role); + return d->_name; +} +void Role::setName(QString value) +{ + Q_D(Role); + d->_name = value; +} +Authorizer* Role::authorizer() const +{ + Q_D(const Role); + return d->_authorizer; +} +void Role::setAuthorizer(Authorizer *value) +{ + Q_D(Role); + d->_authorizer = value; +} +} diff --git a/src/router/role.h b/src/router/role.h new file mode 100644 index 0000000..381a649 --- /dev/null +++ b/src/router/role.h @@ -0,0 +1,31 @@ +#ifndef ROLE_H +#define ROLE_H + +#include "wamp_global.h" +#include "authorizer.h" +#include + +namespace QFlow{ + +class RolePrivate; +class WAMP_EXPORT Role : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(Authorizer* authorizer READ authorizer WRITE setAuthorizer) +public: + explicit Role(QObject *parent = 0); + ~Role(); + QString name() const; + void setName(QString value); + Authorizer* authorizer() const; + void setAuthorizer(Authorizer* value); +Q_SIGNALS: + +public Q_SLOTS: +private: + const QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(Role) +}; +} +#endif // ROLE_H diff --git a/src/router/wampcraauthenticator.cpp b/src/router/wampcraauthenticator.cpp new file mode 100644 index 0000000..422983b --- /dev/null +++ b/src/router/wampcraauthenticator.cpp @@ -0,0 +1,90 @@ +#include "wampcraauthenticator.h" +#include "random.h" +#include "wamproutersession.h" +#include +#include +#include + +namespace QFlow{ + +class WampCraAuthenticatorPrivate +{ +public: + WampCraAuthenticatorPrivate() + { + + } + ~WampCraAuthenticatorPrivate() + { + + } +}; + +class WampCraSession: public AuthSession +{ +public: + WampCraSession() : AuthSession() + { + + } + ~WampCraSession() + { + + } + AUTH_RESULT authenticate() override + { + QMessageAuthenticationCode hash(QCryptographicHash::Sha256); + WampCraUser* crauser = (WampCraUser*)user; + hash.setKey(crauser->secret().toLatin1()); + QString challengeStr = challenge["challenge"].toString(); + hash.addData(challengeStr.toLatin1()); + + QByteArray hashResult = hash.result(); + QByteArray finalSig = hashResult.toBase64(); + if(finalSig == inBuffer) return AUTH_RESULT::ACCEPTED; + return AUTH_RESULT::REJECTED; + } +}; + +WampCraAuthenticator::WampCraAuthenticator(QObject *parent) : Authenticator(parent), + d_ptr(new WampCraAuthenticatorPrivate()) +{ + +} +WampCraAuthenticator::~WampCraAuthenticator() +{ + +} +QString WampCraAuthenticator::authMethod() const +{ + return "wampcra"; +} +QVariantMap WampCraAuthenticator::generateChallenge(qulonglong sessionId, QString authId) +{ + QVariantMap retObj; + QJsonObject challenge; + challenge["authid"] = authId; + challenge["authprovider"] = "wampcra"; + challenge["authmethod"] = "wampcra"; + qulonglong nonce = Random::generate(); + QString nonceStr = QString("%1").arg(nonce); + challenge["nonce"] = nonceStr; + QString timestampStr = QDateTime::currentDateTimeUtc().toString("yyyy-mm-ddThh:mm:zzzZ"); + challenge["timestamp"] = timestampStr; + challenge["session"] = (double)sessionId; + QJsonDocument challengeDoc(challenge); + QString challengeStr = challengeDoc.toJson(); + retObj["challenge"] = challengeStr; + return retObj; +} +AuthSession* WampCraAuthenticator::createSession() const +{ + return new WampCraSession(); +} +User* WampCraAuthenticator::getUser(AuthSession *session) +{ + Q_UNUSED(session); + throw "not implemented"; + return NULL; +} +} diff --git a/src/router/wampcraauthenticator.h b/src/router/wampcraauthenticator.h new file mode 100644 index 0000000..21d0977 --- /dev/null +++ b/src/router/wampcraauthenticator.h @@ -0,0 +1,31 @@ +#ifndef WAMPCRAAUTHENTICATOR_H +#define WAMPCRAAUTHENTICATOR_H + +#include "wamp_global.h" +#include "authenticator.h" +#include "wampcrauser.h" +#include +#include + +namespace QFlow{ + +class WampCraAuthenticatorPrivate; +class WAMP_EXPORT WampCraAuthenticator : public Authenticator +{ + Q_OBJECT +public: + explicit WampCraAuthenticator(QObject *parent = 0); + ~WampCraAuthenticator(); + QVariantMap generateChallenge(qulonglong sessionId, QString authId); + QString authMethod() const; + AuthSession* createSession() const override; + User* getUser(AuthSession* session); +Q_SIGNALS: + +public Q_SLOTS: +private: + const QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(WampCraAuthenticator) +}; +} +#endif // WAMPCRAAUTHENTICATOR_H diff --git a/src/router/wamprouter.cpp b/src/router/wamprouter.cpp new file mode 100644 index 0000000..b158431 --- /dev/null +++ b/src/router/wamprouter.cpp @@ -0,0 +1,87 @@ +#include "wamprouter.h" +#include "wamprouter_p.h" +#include "wamprouterworker.h" +#include "wampconnection.h" +#include "wampconnection_p.h" +#include "wampworker.h" +#include + +namespace QFlow{ + +WampRouterPrivate::WampRouterPrivate(WampRouter* parent) : QObject(), _port(8080), q_ptr(parent) +{ + _worker = new WampRouterWorker(); + _workerThread = new QThread(); + _worker->_router = this; + connect(_workerThread, &QThread::finished, _workerThread, &QObject::deleteLater); + _workerThread->start(); + _worker->moveToThread(_workerThread); + connect(_workerThread, &QThread::finished, _worker, &QObject::deleteLater); +} +WampRouterPrivate::~WampRouterPrivate() +{ +} + +WampRouter::WampRouter(QObject *parent) : QObject(parent), d_ptr(new WampRouterPrivate(this)) +{ + +} +WampRouter::~WampRouter() +{ +} +QString WampRouter::host() const +{ + Q_D(const WampRouter); + return d->_host; +} +void WampRouter::setHost(QString value) +{ + Q_D(WampRouter); + d->_host = value; + Q_EMIT hostChanged(); +} +int WampRouter::port() const +{ + Q_D(const WampRouter); + return d->_port; +} +void WampRouter::setPort(int value) +{ + Q_D(WampRouter); + d->_port = value; + Q_EMIT portChanged(); +} +ErrorInfo WampRouter::init() +{ + Q_D(WampRouter); + + Q_FOREACH (Realm* realm, d->_worker->_realms) { + realm->setParent(NULL); + realm->moveToThread(d->_workerThread); + } + QMetaObject::invokeMethod(d->_worker, "startServer", Qt::QueuedConnection); + return ErrorInfo(); +} +ErrorInfo WampRouter::deinit() +{ + return ErrorInfo(); +} +QQmlListProperty WampRouter::realms() +{ + Q_D(WampRouter); + return QQmlListProperty(d->_worker, d->_worker->_realms); +} +void WampRouterSubscription::event(qulonglong publicationId, const QVariantList& args) +{ + QVariantList resArr{(int)WampMsgCode::EVENT, _subscriptionId, publicationId, QVariantMap()}; + if(!args.isEmpty()) resArr.append(QVariant(args)); + _subscriber->sendWampMessage(resArr); +} +void WampRouterRegistration::handleRemote(qulonglong requestId, WampRouterSession* /*caller*/, QVariantList params) +{ + QVariantList invArr{(int)WampMsgCode::INVOCATION, requestId, registrationId(), QVariantMap()}; + if(!params.isEmpty()) invArr.append(QVariant(params)); + //if(arr.count()>5) invArr.append(arr[5]); + _session->sendWampMessage(invArr); +} +} diff --git a/src/router/wamprouter.h b/src/router/wamprouter.h new file mode 100644 index 0000000..497e591 --- /dev/null +++ b/src/router/wamprouter.h @@ -0,0 +1,40 @@ +#ifndef WAMPROUTER_H +#define WAMPROUTER_H + +#include "wamp_global.h" +#include "initiable.h" +#include "realm.h" +#include +#include + +namespace QFlow{ + +class WampRouterPrivate; +class WAMP_EXPORT WampRouter : public QObject, public QmlInitiable +{ + Q_OBJECT + Q_PROPERTY(QString host READ host WRITE setHost NOTIFY hostChanged) + Q_PROPERTY(int port READ port WRITE setPort NOTIFY portChanged) + Q_PROPERTY(QQmlListProperty realms READ realms) + Q_CLASSINFO("DefaultProperty", "realms") +public: + explicit WampRouter(QObject *parent = 0); + ~WampRouter(); + QString host() const; + void setHost(QString value); + int port() const; + void setPort(int value); + Q_INVOKABLE ErrorInfo init(); + Q_INVOKABLE ErrorInfo deinit(); + QQmlListProperty realms(); +Q_SIGNALS: + void hostChanged(); + void portChanged(); +public Q_SLOTS: + +private: + const QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(WampRouter) +}; +} +#endif // WAMPROUTER_H diff --git a/src/router/wamprouter_p.h b/src/router/wamprouter_p.h new file mode 100644 index 0000000..e97bbd4 --- /dev/null +++ b/src/router/wamprouter_p.h @@ -0,0 +1,106 @@ +#ifndef WAMPROUTER_P_H +#define WAMPROUTER_P_H + +#include "radixtreenode.h" +#include "wamproutersession.h" +#include "registration_p.h" +#include "subscription_p.h" +#include +#include +#include +#include + +namespace QFlow{ + +class WampRouterWorker; +class WampRouterSession; +class WampRouterRegistration +{ +public: + WampRouterRegistration(qulonglong regId, QString regUri, WampRouterSession* sessionPtr) : + _registrationId(regId), _uri(regUri), _session(sessionPtr), _created(QDateTime::currentDateTime()) + { + } + ~WampRouterRegistration() + { + + } + qulonglong registrationId() const + { + return _registrationId; + } + QString uri() const + { + return _uri; + } + void handleRemote(qulonglong requestId, WampRouterSession* caller, QVariantList params); + QDateTime created() const + { + return _created; + } +private: + qulonglong _registrationId; + QString _uri; + WampRouterSession* _session; + QDateTime _created; +}; +typedef QSharedPointer WampRouterRegistrationPointer; +class WampRouterSubscription +{ +public: + WampRouterSubscription(QString topic, qulonglong subscriptionId, WampRouterSession* subscriber) : _topic(topic), _subscriptionId(subscriptionId), + _subscriber(subscriber), _created(QDateTime::currentDateTime()) + { + } + ~WampRouterSubscription() + { + + } + + WampRouterSession* subscriber() const + { + return _subscriber; + } + QString topic() const + { + return _topic; + } + qulonglong subscriptionId() const + { + return _subscriptionId; + } + void event(qulonglong publicationId, const QVariantList& args); + QDateTime created() const + { + return _created; + } + +private: + QString _topic; + qulonglong _subscriptionId; + WampRouterSession* _subscriber; + QDateTime _created; +}; +typedef QSharedPointer WampRouterSubscriptionPointer; + +class Realm; +class WampRouter; +class WampRouterPrivate : public QObject +{ + Q_OBJECT +public: + QString _host; + int _port; + QThread* _workerThread; + WampRouterWorker* _worker; + + WampRouterPrivate(WampRouter* parent); + ~WampRouterPrivate(); +public Q_SLOTS: +private: + WampRouter* q_ptr; + Q_DECLARE_PUBLIC(WampRouter) +}; +} +#endif // WAMPROUTER_P_H + diff --git a/src/router/wamproutersession.cpp b/src/router/wamproutersession.cpp new file mode 100644 index 0000000..51953da --- /dev/null +++ b/src/router/wamproutersession.cpp @@ -0,0 +1,361 @@ +#include "wamproutersession.h" +#include "wamproutersession_p.h" +#include "realm.h" +#include "realm_p.h" +#include "wamprouter_p.h" +#include "authenticator.h" +#include "user.h" +#include "wamprouterworker.h" +#include "wampmessageserializer.h" +#include "websocketconnection.h" +#include "random.h" +#include +#include +#include +#include +#include + +namespace QFlow{ + +WampRouterSessionPrivate::WampRouterSessionPrivate(WampRouterSession* parent) : QObject(), q_ptr(parent) +{ +} +WampRouterSessionPrivate::~WampRouterSessionPrivate() +{ + _socket->deleteLater(); +} +void WampRouterSessionPrivate::sendWampMessage(const QVariantList& arr) +{ + QByteArray message = _serializer->serialize(arr); + if(_serializer->isBinary()) + { + _socket->sendBinary(message); + } + else + { + _socket->sendText(message); + } +} +WampRouterSession::WampRouterSession(WebSocketConnection *socket, QString subprotocol, QObject *parent) : QThread(parent), d_ptr(new WampRouterSessionPrivate(this)) +{ + Q_D(WampRouterSession); + d->moveToThread(this); + d->_sessionId = Random::generate(); + d->_serializer.reset(WampMessageSerializer::create(subprotocol)); + d->_socket = socket; + QObject::connect(socket, SIGNAL(messageReceived(QByteArray)), d, SLOT(onMessageReceived(QByteArray))); + QObject::connect(socket, SIGNAL(closed()), d, SLOT(closed())); + d->_router = (WampRouterWorker*)parent; + start(); +} +void WampRouterSessionPrivate::onMessageReceived(const QByteArray &message) +{ + Q_Q(WampRouterSession); + QVariantList arr = _serializer->deserialize(message); + WampMsgCode code = (WampMsgCode)arr[0].toInt(); + if(code == WampMsgCode::HELLO) + { + QString realmName = arr[1].toString(); + Realm* realmFound = NULL; + Q_FOREACH (Realm* r, _router->_realms) { + if(realmName == r->name()) + { + realmFound = r; + break; + } + } + if(!realmFound) + { + abort(KEY_ERR_NO_SUCH_REALM, "The realm does not exist."); + return; + } + _realm = realmFound; + QVariantMap details = arr[2].toMap(); + if(!realmFound->d_ptr->_authenticators.isEmpty()) + { + QVariantList authMethods2 = details["authmethods"].toList(); + Authenticator* foundAuth = NULL; + Q_FOREACH(QVariant authMethod, authMethods2) + { + foundAuth = realmFound->findAuthenticatorByAuthMethod(authMethod.toString()); + if(foundAuth) break; + } + if(foundAuth) + { + QString authId = details["authid"].toString(); + QVariantMap challenge = foundAuth->generateChallenge(_sessionId, authId); + QVariantList authArr{WampMsgCode::CHALLENGE, foundAuth->authMethod(), challenge}; + _authSession.reset(foundAuth->createSession()); + _authSession->challenge = challenge; + _authSession->authenticator = foundAuth; + _authSession->user = foundAuth->user(authId); + _user = foundAuth->user(authId); + sendWampMessage(authArr); + return; + } + + } + } + else if(code == WampMsgCode::AUTHENTICATE) + { + _authSession->inBuffer = arr[1].toByteArray(); + AUTH_RESULT authResult = _authSession->authenticate(); + if(authResult == AUTH_RESULT::ACCEPTED) + { + if(!_authSession->user) _authSession->user = _authSession->authenticator->getUser(_authSession.data()); + if(_authSession->user) + { + welcome(); + } + else abort(KEY_ERR_NOT_AUTHENTICATED); + } + if(authResult == AUTH_RESULT::REJECTED) + { + abort(KEY_ERR_NOT_AUTHENTICATED); + } + if(authResult == AUTH_RESULT::CONTINUE) + { + QVariantMap obj; + obj["challenge"] = _authSession->outBuffer; + QVariantList authArr{WampMsgCode::CHALLENGE, _authSession->authenticator->authMethod(), obj}; + sendWampMessage(authArr); + } + } + else if(code == WampMsgCode::REGISTER) + { + qulonglong requestId = arr[1].toULongLong(); + QString uri = arr[3].toString(); + bool authorized = authorize(uri, WampMsgCode::REGISTER, requestId); + if(!authorized) return; + WampRouterRegistrationPointer registration(new WampRouterRegistration(Random::generate(), uri, q)); + _realm->d_ptr->insertRegistration(uri, registration); + _registrations.append(registration); + QVariantList onCreateArgs{_sessionId}; + QVariantMap details; + details["id"] = registration->registrationId(); + details["created"] = registration->created().toString("yyyy-mm-ddThh:mm:zzzZ"); + details["uri"] = uri; + onCreateArgs.append(details); + _realm->publish(KEY_REGISTRATION_ON_CREATE, onCreateArgs); + QVariantList resArr{WampMsgCode::REGISTERED, requestId, registration->registrationId()}; + sendWampMessage(resArr); + } + else if(code == WampMsgCode::UNREGISTER) + { + qulonglong requestId = arr[1].toULongLong(); + qulonglong registrationId = arr[2].toULongLong(); + if(_realm->d_ptr->_idRegistartion.contains(registrationId)) + { + WampRouterRegistrationPointer reg = _realm->d_ptr->getRegistration(registrationId); + bool authorized = authorize(reg->uri(), WampMsgCode::REGISTER, requestId); + if(!authorized) return; + _realm->d_ptr->removeRegistration(reg); + QVariantList resArr{WampMsgCode::UNREGISTERED, requestId}; + sendWampMessage(resArr); + } + else + { + error(WampMsgCode::UNREGISTER, KEY_ERR_NO_SUCH_REGISTRATION, requestId); + } + } + else if(code == WampMsgCode::CALL) + { + qulonglong requestId = arr[1].toULongLong(); + QString uri = arr[3].toString(); + QVariantList params; + if(arr.count()>4) params = arr[4].toList(); + bool authorized = authorize(uri, WampMsgCode::CALL, requestId); + if(!authorized) return; + if(_realm->containsRegistration(uri)) + { + WampRouterRegistrationPointer reg = _realm->d_ptr->getRegistration(uri); + _realm->d_ptr->insertPendingInvocation(requestId, q); + reg->handleRemote(requestId, q, params); + } + else if(_realm->d_ptr->containsInternalRegistration(uri)) + { + RegistrationPointer impl = _realm->d_ptr->getInternalRegistration(uri); + WampResult res = impl->execute(params); + if(res.isError()) error(WampMsgCode::CALL, res.errorUri(), requestId); + else result(requestId, res.resultData()); + } + else + { + error(WampMsgCode::CALL, KEY_ERR_NO_SUCH_PROCEDURE, requestId, {{"procedureUri", uri}}); + } + } + else if(code == WampMsgCode::YIELD) + { + qulonglong requstId = arr[1].toULongLong(); + WampRouterSession* caller = _realm->d_ptr->takePendingInvocation(requstId); + QVariantList resultArr; + if(arr.count()>3) resultArr = arr[3].toList(); + caller->result(requstId, resultArr); + } + else if(code == WampMsgCode::SUBSCRIBE) + { + qulonglong requestId = arr[1].toULongLong(); + QString topic = arr[3].toString(); + bool authorized = authorize(topic, WampMsgCode::SUBSCRIBE, requestId); + if(!authorized) return; + + qulonglong subscriptionId = Random::generate(); + WampRouterSubscriptionPointer subscription(new WampRouterSubscription(topic, subscriptionId, q)); + if(!_realm->d_ptr->containsSubscription(topic)) + { + QVariantList onCreateArgs{_sessionId}; + QVariantMap details; + details["id"] = subscriptionId; + details["created"] = subscription->created().toString("yyyy-mm-ddThh:mm:zzzZ"); + details["uri"] = topic; + onCreateArgs.append(details); + _realm->publish(KEY_SUBSCRIPTION_ON_CREATE, onCreateArgs); + } + _realm->publish(KEY_SUBSCRIPTION_ON_SUBSCRIBE, {_sessionId, subscriptionId, topic}); + + _realm->d_ptr->insertSubscription(subscriptionId, topic, subscription); + _subscriptions.append(subscription); + + QVariantList resArr{WampMsgCode::SUBSCRIBED, requestId, subscriptionId}; + sendWampMessage(resArr); + } + else if(code == WampMsgCode::UNSUBSCRIBE) + { + qulonglong requestId = arr[1].toULongLong(); + qulonglong subscriptionId = arr[2].toULongLong(); + WampRouterSubscriptionPointer sub = _realm->d_ptr->takeSubscription(subscriptionId); + _subscriptions.removeAll(sub); + QVariantList resArr{WampMsgCode::UNSUBSCRIBED, requestId}; + sendWampMessage(resArr); + } + else if(code == WampMsgCode::PUBLISH) + { + qulonglong requestId = arr[1].toULongLong(); + QString topic = arr[3].toString(); + QVariantList args; + if(arr.count() > 4) args = arr[4].toList(); + bool authorized = authorize(topic, WampMsgCode::PUBLISH, requestId); + if(!authorized) return; + + qulonglong publicationId = _realm->publish(topic, args); + + QVariantList resArr{WampMsgCode::PUBLISHED, requestId, publicationId}; + sendWampMessage(resArr); + } +} + +void WampRouterSession::run() +{ + exec(); +} +WampRouterSession::~WampRouterSession() +{ + Q_D(WampRouterSession); + QMetaObject::invokeMethod(d, "deleteLater", Qt::BlockingQueuedConnection); + exit(); + wait(); +} +qulonglong WampRouterSession::sessionId() const +{ + Q_D(const WampRouterSession); + return d->_sessionId; +} +void WampRouterSession::sendWampMessage(const QVariantList &arr) +{ + Q_D(WampRouterSession); + QMetaObject::invokeMethod(d, "sendWampMessage", Qt::QueuedConnection, Q_ARG(QVariantList, arr)); +} + +void WampRouterSessionPrivate::error(WampMsgCode code, QString uri, qulonglong requestId, QVariantMap details) +{ + QVariantList errArr{WampMsgCode::ERROR, (int)code, requestId, details, uri}; + sendWampMessage(errArr); +} +void WampRouterSessionPrivate::welcome() +{ + QVariantMap roles{{"broker", QVariantMap()}, {"dealer", QVariantMap()}}; + QVariantMap details{{"roles", roles}}; + QVariantList resArr{WampMsgCode::WELCOME, _sessionId, details}; + sendWampMessage(resArr); +} +void WampRouterSessionPrivate::abort(QString uri, QString message) +{ + QVariantMap details; + if(!message.isEmpty()) details["message"] = message; + QVariantList abortArr{WampMsgCode::ABORT, details, uri}; + sendWampMessage(abortArr); +} + +void WampRouterSessionPrivate::result(qulonglong requestId, QVariant result) +{ + QVariantList responseArr{WampMsgCode::RESULT, requestId, QVariantMap()}; + + if(result.isValid()) + { + QVariantList resultArr; + if((QMetaType::Type)result.type() == QMetaType::QVariantList) + { + resultArr = result.toList(); + } + else + { + resultArr.append(result); + } + if(!resultArr.isEmpty()) responseArr.append(QVariant(resultArr)); + } + + //if(arr.count()>4) resArr.append(arr[4]); + sendWampMessage(responseArr); +} +void WampRouterSession::result(qulonglong requestId, QVariant result) +{ + Q_D(WampRouterSession); + QMetaObject::invokeMethod(d, "result", Qt::QueuedConnection, Q_ARG(qulonglong, requestId), Q_ARG(QVariant, result)); +} + +Realm* WampRouterSession::realm() const +{ + Q_D(const WampRouterSession); + return d->_realm; +} +User* WampRouterSession::user() const +{ + Q_D(const WampRouterSession); + return d->_user; +} +void WampRouterSession::setUser(User* value) +{ + Q_D(WampRouterSession); + d->_user = value; +} +bool WampRouterSessionPrivate::authorize(QString uri, WampMsgCode action, qulonglong requestId) +{ + if(_user) + { + bool authorized = _user->authorize(uri, action); + if(!authorized) + { + error(action, KEY_ERR_NOT_AUTHORIZED, requestId); + } + return authorized; + } + return false; +} +QString WampRouterSession::peerAddress() const +{ + Q_D(const WampRouterSession); + return d->_socket->peerAddress().toString(); +} +void WampRouterSessionPrivate::closed() +{ + Q_Q(WampRouterSession); + for (auto reg: _registrations) { + _realm->d_ptr->removeRegistration(reg); + } + for (auto sub: _subscriptions) { + _realm->d_ptr->takeSubscription(sub->subscriptionId()); + } + Q_EMIT q->closed(); +} + +} diff --git a/src/router/wamproutersession.h b/src/router/wamproutersession.h new file mode 100644 index 0000000..47bdd15 --- /dev/null +++ b/src/router/wamproutersession.h @@ -0,0 +1,41 @@ +#ifndef WAMPROUTERSESSION_H +#define WAMPROUTERSESSION_H + +#include "wamp_symbols.h" +#include +#include +#include +#include + +namespace QFlow{ + +class Realm; +class User; +class WebSocketConnection; + +class WampRouterSessionPrivate; +class WampRouterSession : public QThread +{ + Q_OBJECT +public: + WampRouterSession(WebSocketConnection* socket, QString subprotocol, QObject* parent); + qulonglong sessionId() const; + ~WampRouterSession(); + Realm* realm() const; + void invoke(qulonglong requestId, QString uri); + User* user() const; + void setUser(User* value); + void sendWampMessage(const QVariantList& arr); + void result(qulonglong requestId, QVariant result); +public Q_SLOTS: + QString peerAddress() const; +Q_SIGNALS: + void closed(); +private: + void run(); + WampRouterSessionPrivate* d_ptr; + Q_DECLARE_PRIVATE(WampRouterSession) +}; +} + +#endif // WAMPROUTERSESSION_H diff --git a/src/router/wamproutersession_p.h b/src/router/wamproutersession_p.h new file mode 100644 index 0000000..bac5a05 --- /dev/null +++ b/src/router/wamproutersession_p.h @@ -0,0 +1,50 @@ +#ifndef WAMPROUTERSESSION_P_H +#define WAMPROUTERSESSION_P_H + +#include "authenticator.h" +#include +#include + +namespace QFlow{ + +class WampRouterWorker; +class Realm; +class WebSocketConnection; +class User; +class WampMessageSerializer; +class WampRouterRegistration; +class WampRouterSubscription; +typedef QSharedPointer WampRouterRegistrationPointer; +typedef QSharedPointer WampRouterSubscriptionPointer; + +class WampRouterSessionPrivate : public QObject +{ + Q_OBJECT +public: + WampRouterSessionPrivate(WampRouterSession* parent); + ~WampRouterSessionPrivate(); + qulonglong _sessionId; + QPointer _realm; + QPointer _socket; + QPointer _user; + QScopedPointer _serializer; + QList _registrations; + QList _subscriptions; + WampRouterWorker* _router; + QScopedPointer _authSession; +public Q_SLOTS: + void sendWampMessage(const QVariantList& arr); + void onMessageReceived(const QByteArray &message); + void abort(QString uri, QString message = QString()); + void welcome(); + bool authorize(QString uri, WampMsgCode action, qulonglong requestId); + void error(WampMsgCode code, QString uri, qulonglong requestId, QVariantMap details = QVariantMap()); + void result(qulonglong requestId, QVariant result = QVariant()); + void closed(); +private: + WampRouterSession* q_ptr; + Q_DECLARE_PUBLIC(WampRouterSession) + +}; +} +#endif // WAMPROUTERSESSION_P_H diff --git a/src/router/wamprouterworker.cpp b/src/router/wamprouterworker.cpp new file mode 100644 index 0000000..e3a85a3 --- /dev/null +++ b/src/router/wamprouterworker.cpp @@ -0,0 +1,64 @@ +#include "wamprouterworker.h" +#include "wamprouter_p.h" +#include "wamprouter.h" +#include "wampconnection_p.h" +#include "random.h" +#include "realm_p.h" +#include "wamproutersession.h" +#include "authenticator.h" +#include "credentialstore.h" +#include "wampinvocation.h" +#include "wampmessageserializer.h" + +namespace QFlow{ + +WampRouterWorker::WampRouterWorker(QObject *parent) : QObject(parent) +{ +} + +WampRouterWorker::~WampRouterWorker() +{ + +} +void WampRouterWorker::startServer() +{ + Q_FOREACH (Realm* realm, _realms) { + realm->setParent(this); + } + _server.reset(new WebSocketServer()); + _server->setHost(_router->_host); + _server->setPort(_router->_port); + QObject::connect(_server.data(), SIGNAL(newConnection(WebSocketConnection*)), this, SLOT(onNewConnection(WebSocketConnection*))); + _server->init(); + qDebug() << QString("WampRouter started on port %1").arg(_router->_port); +} + +void WampRouterWorker::onNewConnection(WebSocketConnection *con) +{ + QStringList subprotocols = con->requestedSubprotocols(); + QString selectedSub; + Q_FOREACH(QString sub, subprotocols) + { + if(sub == KEY_WAMP_JSON_SUB) selectedSub = KEY_WAMP_JSON_SUB; + else if(sub == KEY_WAMP_MSGPACK_SUB) + { + selectedSub = KEY_WAMP_MSGPACK_SUB; + break; + } + } + if(selectedSub.isEmpty()) con->accept(false); + con->selectSubprotocol(selectedSub); + con->accept(true); + WampRouterSessionPointer newSession(new WampRouterSession(con, selectedSub, this)); + QObject::connect(newSession.data(), SIGNAL(closed()), this, SLOT(sessionClosed())); + _sessions.insert(newSession->sessionId(), newSession); +} +void WampRouterWorker::sessionClosed() +{ + WampRouterSession* session = (WampRouterSession*)sender(); + if(_sessions.contains(session->sessionId())) + { + _sessions.remove(session->sessionId()); + } +} +} diff --git a/src/router/wamprouterworker.h b/src/router/wamprouterworker.h new file mode 100644 index 0000000..2ceabf5 --- /dev/null +++ b/src/router/wamprouterworker.h @@ -0,0 +1,37 @@ +#ifndef WAMPROUTERWORKER_H +#define WAMPROUTERWORKER_H + +#include "websocketserver.h" +#include +#include + +namespace QFlow{ + +class AuthSession; +class Realm; +class WampRouterSession; +typedef QSharedPointer WampRouterSessionPointer; +class WampRouterPrivate; +class WampMessageSerializer; +class WebSocketConnection; +typedef QSharedPointer WampMessageSerializerPointer; + +class WampRouterWorker : public QObject +{ + Q_OBJECT +public: + explicit WampRouterWorker(QObject *parent = 0); + ~WampRouterWorker(); + WampRouterPrivate* _router; + QScopedPointer _server; + QHash _sessions; + QList _realms; +Q_SIGNALS: + +public Q_SLOTS: + void startServer(); + void onNewConnection(WebSocketConnection* con); + void sessionClosed(); +}; +} +#endif // WAMPROUTERWORKER_H diff --git a/src/sid.cpp b/src/sid.cpp new file mode 100644 index 0000000..5d9698a --- /dev/null +++ b/src/sid.cpp @@ -0,0 +1,82 @@ +#include "sid.h" +#include "user_p.h" + +#ifdef Q_OS_WIN +#include +#define SECURITY_WIN32 +#include +#endif + +namespace QFlow{ + +class SIDUserPrivate : public UserPrivate +{ +public: + bool _initialized; +#ifdef Q_OS_WIN + SID _sid; +#endif + SIDUserPrivate() : UserPrivate(), _initialized(false) + { + + } + ~SIDUserPrivate() + { + + } +}; + +SIDUser::SIDUser(QObject *parent) : User(*new SIDUserPrivate, parent) +{ + +} +SIDUser::SIDUser(SIDUserPrivate &dd, QObject *parent) : User(dd, parent) +{ + +} + +SIDUser::~SIDUser() +{ + +} +QByteArray SIDUser::response(QByteArray) +{ + return QByteArray(); +} +QString SIDUser::authMethod() const +{ + return QString(); +} +bool SIDUser::checkTokenMembership(QVariant handle) +{ +#ifdef Q_OS_WIN + Q_D(SIDUser); + BOOL b; + if(!d->_initialized) + { + DWORD cbSid = sizeof(d->_sid); + DWORD cchReferencedDomainName = 0; + SID_NAME_USE sidNameUse; + LPCTSTR s = (LPCTSTR)name().utf16(); + LPTSTR refDomain = NULL; + b = LookupAccountName(NULL, + s, + &d->_sid, &cbSid, refDomain, &cchReferencedDomainName, &sidNameUse); + SecureZeroMemory(&d->_sid, cbSid); + refDomain = new TCHAR[cchReferencedDomainName]; + b = LookupAccountName(NULL, + s, + &d->_sid, &cbSid, refDomain, &cchReferencedDomainName, &sidNameUse); + } + HANDLE accessHandle = handle.value(); + BOOL isMember; + b = CheckTokenMembership(accessHandle, &d->_sid, &isMember); + return isMember; +#endif +#ifdef Q_OS_LINUX + Q_UNUSED(handle); + throw "not implemented"; + return false; +#endif +} +} diff --git a/src/sid.h b/src/sid.h new file mode 100644 index 0000000..ff001d9 --- /dev/null +++ b/src/sid.h @@ -0,0 +1,26 @@ +#ifndef SID_H +#define SID_H + +#include "user.h" +#include +#include + +namespace QFlow{ + +class SIDUserPrivate; +class WAMP_EXPORT SIDUser : public User +{ + Q_OBJECT +public: + SIDUser(QObject* parent = NULL); + ~SIDUser(); + QByteArray response(QByteArray challenge); + QString authMethod() const; + bool checkTokenMembership(QVariant handle); +protected: + SIDUser(SIDUserPrivate &dd, QObject *parent); +private: + Q_DECLARE_PRIVATE(SIDUser) +}; +} +#endif // SID_H diff --git a/src/src.qbs b/src/src.qbs new file mode 100644 index 0000000..d4eba84 --- /dev/null +++ b/src/src.qbs @@ -0,0 +1,105 @@ +import qbs +import qbs.Probes as Probes + +QmlPlugin +{ + name: "wamp" + cpp.cxxLanguageVersion: "c++11" + files: [ + "client/call.cpp", + "client/call.h", + "gssapiauthenticator.cpp", + "gssapiauthenticator.h", + "gssapiuser.cpp", + "gssapiuser.h", + "helper.cpp", + "helper.h", + "router/authenticator.cpp", + "router/authenticator.h", + "router/authorizer.cpp", + "router/authorizer.h", + "router/defaultauthorizer.cpp", + "router/defaultauthorizer.h", + "router/realm.cpp", + "router/realm.h", + "router/realm_p.h", + "router/role.cpp", + "router/role.h", + "sid.cpp", + "sid.h", + "user.cpp", + "user.h", + "user_p.h", + "wamp.h", + "wamp_global.h", + "wamp_symbols.h", + "wampattached.h", + "wampbase.cpp", + "wampbase.h", + "client/wampconnection.h", + "router/wampcraauthenticator.cpp", + "router/wampcraauthenticator.h", + "wampcrauser.cpp", + "wampcrauser.h", + "wamperror.h", + "wampinvocation.h", + "router/wamprouter.cpp", + "router/wamprouter.h", + "router/wamprouter_p.h", + "router/wamproutersession.cpp", + "router/wamproutersession.h", + "router/wamprouterworker.cpp", + "router/wamprouterworker.h", + "client/wampworker.h", + "credentialstore.h", + "client/wampconnection_p.h", + "client/registration_p.h", + "client/subscription_p.h", + "wamp.cpp", + "credentialstore.cpp", + "wampattached.cpp", + "client/wampconnection.cpp", + "client/wampworker.cpp", + "wamperror.cpp", + "treemodel.cpp", + "treemodel.h", + "wampmessageserializer.cpp", + "wampmessageserializer.h", + "router/wamproutersession_p.h", + ] + + pluginNamespace: "QFlow.Wamp" + pluginRootPath: "bin/plugins" + cpp.includePaths: [".", "router", "client"] + + cpp.dynamicLibraries: + { + var libs = []; + if(qbs.targetOS.contains("windows")) + { + libs.push("Advapi32", "Secur32"); + } + + return libs; + } + + Group + { + name: "libraries" + qbs.install: true + qbs.installDir: "bin" + files: { + } + fileTags: "libraries" + } + Depends{ + name: "libsecret" + condition: qbs.targetOS.contains("linux") + } + Depends{name: "msgpack"} + Depends{name: "websockets"} + + + Depends{name: "Qt"; submodules: ["network"]} + Depends{name: "core"} +} diff --git a/src/treemodel.cpp b/src/treemodel.cpp new file mode 100644 index 0000000..a7aa395 --- /dev/null +++ b/src/treemodel.cpp @@ -0,0 +1,137 @@ +#include "treemodel.h" +#include "treeitem.h" + +namespace QFlow{ +TreeModel::TreeModel(QObject *parent) : QAbstractItemModel(parent) +{ + _rootItem = new TreeItem(QVariantList({"Title", "Uri"})); +} +TreeModel::~TreeModel() +{ + delete _rootItem; +} +void TreeModel::setData(WampConnection* connection) +{ + _connection = connection; + connection->call2(KEY_LIST_REGISTRATION_URIS, {}, + [this](const QVariant& result){ + beginResetModel(); + QStringList uriList = result.toStringList(); + Q_FOREACH (QString uri, uriList) { + _rootItem->add(uri); + } + endResetModel(); + std::function handler = std::bind(&TreeModel::registrationCreated, this, + std::placeholders::_1, + std::placeholders::_2); + _connection->subscribe(KEY_REGISTRATION_ON_CREATE, handler); + }); +} +void TreeModel::registrationCreated(double /*registrationId*/, QVariantMap args) +{ + QList itemsAdded = _rootItem->add(args["uri"].toString()); + Q_FOREACH (TreeItem* item, itemsAdded) { + TreeItem* parent = item->parent(); + QModelIndex parentIndex = createIndex(parent->index(), 0, parent); + int row = item->index(); + beginInsertRows(parentIndex, row, row); + endInsertRows(); + } +} + +QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) + const +{ + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + TreeItem *parentItem; + + if (!parent.isValid()) + parentItem = _rootItem; + else + parentItem = static_cast(parent.internalPointer()); + + TreeItem *childItem = parentItem->child(row); + if (childItem) + return createIndex(row, column, childItem); + else + return QModelIndex(); +} +QModelIndex TreeModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + TreeItem *childItem = static_cast(index.internalPointer()); + TreeItem *parentItem = childItem->parent(); + + if (parentItem == _rootItem) + return QModelIndex(); + + return createIndex(parentItem->index(), 0, parentItem); +} +int TreeModel::rowCount(const QModelIndex &parent) const +{ + TreeItem *parentItem; + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + parentItem = _rootItem; + else + parentItem = static_cast(parent.internalPointer()); + + int count = parentItem->childCount(); + return count; +} +int TreeModel::columnCount(const QModelIndex &parent) const +{ + TreeItem *parentItem; + if (parent.isValid()) + parentItem = static_cast(parent.internalPointer()); + else + parentItem = _rootItem; + int count = parentItem->columnCount(); + return count; +} +QVariant TreeModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + TreeItem *item = static_cast(index.internalPointer()); + if(!item) return QVariant(); + + switch (role) { + case TitleRole: + return item->dataAt(0); + case UriRole: + return item->dataAt(1); + default: + return QVariant(); + } +} +Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return 0; + + return QAbstractItemModel::flags(index); +} +QVariant TreeModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + return _rootItem->dataAt(section); + + return QVariant(); +} +QHash TreeModel::roleNames() const +{ + QHash roles; + roles[TreeRoles::TitleRole] = "Title"; + roles[TreeRoles::UriRole] = "Uri"; + return roles; +} +} diff --git a/src/treemodel.h b/src/treemodel.h new file mode 100644 index 0000000..c1ea5d7 --- /dev/null +++ b/src/treemodel.h @@ -0,0 +1,41 @@ +#ifndef TREEMODEL_H +#define TREEMODEL_H + +#include "wampconnection.h" +#include + +namespace QFlow{ + +class WampConnection; +class TreeItem; +class TreeModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + enum TreeRoles { + TitleRole = Qt::UserRole + 10, + UriRole = Qt::UserRole + 11 + }; + Q_ENUM(TreeRoles) + explicit TreeModel(QObject *parent = NULL); + ~TreeModel(); + QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; + Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE; + int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + QHash roleNames() const Q_DECL_OVERRIDE; +public Q_SLOTS: + void setData(WampConnection* connection); +private: + void registrationCreated(double subscriptionId, QVariantMap args); + WampConnection* _connection; + TreeItem* _rootItem; +}; +} +#endif // TREEMODEL_H diff --git a/src/user.cpp b/src/user.cpp new file mode 100644 index 0000000..1f3a326 --- /dev/null +++ b/src/user.cpp @@ -0,0 +1,56 @@ +#include "user.h" +#include "user_p.h" + +namespace QFlow{ + +UserPrivate::UserPrivate() +{ +} +UserPrivate::~UserPrivate() +{ + +} + +User::User(QObject* parent) : QObject(parent), d_ptr(new UserPrivate()) +{ + +} +User::User(UserPrivate &dd, QObject *parent) : QObject(parent), d_ptr(&dd) +{ + +} + +User::~User() +{ + +} + +QString User::name() const +{ + Q_D(const User); + return d->_name; +} +void User::setName(QString value) +{ + Q_D(User); + d->_name = value; +} + +Role* User::role() const +{ + Q_D(const User); + return d->_role; +} +void User::setRole(Role* role) +{ + Q_D(User); + d->_role = role; +} + +bool User::authorize(QString uri, WampMsgCode action) +{ + Q_D(User); + return d->_role->authorizer()->authorize(d->_name, uri, action); +} +} + diff --git a/src/user.h b/src/user.h new file mode 100644 index 0000000..fa86ee8 --- /dev/null +++ b/src/user.h @@ -0,0 +1,34 @@ +#ifndef USER_H +#define USER_H + +#include "wamp_global.h" +#include "role.h" +#include + +namespace QFlow{ + +class UserPrivate; +class WAMP_EXPORT User : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(Role* role READ role WRITE setRole) + +public: + User(QObject* parent = NULL); + virtual ~User(); + virtual QString name() const; + void setName(QString value); + Role* role() const; + void setRole(Role* role); + bool authorize(QString uri, WampMsgCode action); + virtual QByteArray response(QByteArray challenge) = 0; + virtual QString authMethod() const = 0; +protected: + const QScopedPointer d_ptr; + User(UserPrivate &dd, QObject *parent); +private: + Q_DECLARE_PRIVATE(User) +}; +} +#endif // USER_H diff --git a/src/user_p.h b/src/user_p.h new file mode 100644 index 0000000..3abd0ba --- /dev/null +++ b/src/user_p.h @@ -0,0 +1,18 @@ +#ifndef USER_P_H +#define USER_P_H + +#include + +namespace QFlow{ +class Role; +class UserPrivate +{ +public: + UserPrivate(); + ~UserPrivate(); + QString _name; + Role* _role; +}; +} + +#endif // USER_P_H diff --git a/src/wamp.cpp b/src/wamp.cpp new file mode 100644 index 0000000..6811469 --- /dev/null +++ b/src/wamp.cpp @@ -0,0 +1,46 @@ +#include "wamp.h" +#include "qqml.h" +#include "wampconnection.h" +#include "wampconnection_p.h" +#include "wamperror.h" +#include "wampattached.h" +#include "wamprouter.h" +#include "role.h" +#include "authorizer.h" +#include "authenticator.h" +#include "defaultauthorizer.h" +#include "wampcraauthenticator.h" +#include "gssapiauthenticator.h" +#include "wampinvocation.h" +#include "treemodel.h" +#include "gssapiuser.h" +#include "sid.h" + +namespace QFlow{ + +void Wamp::registerTypes(const char *uri) +{ + // @uri QFlow.Wamp + qmlRegisterType(uri, 1, 0, "WampConnection"); + qmlRegisterType(uri, 1, 0, "WampRouter"); + qmlRegisterType(uri, 1, 0, "Realm"); + qmlRegisterType(uri, 1, 0, "Role"); + qmlRegisterUncreatableType(uri, 1, 0, "Authorizer", "Cannot instatiate Authorizer base type"); + qmlRegisterType(uri, 1, 0, "DefaultAuthorizer"); + qmlRegisterType(uri, 1, 0, "WampCraAuthenticator"); + qmlRegisterType(uri, 1, 0, "GSSAPIAuthenticator"); + qmlRegisterUncreatableType(uri, 1, 0, "User", "Cannot instatiate User base type"); + qmlRegisterType(uri, 1, 0, "WampCraUser"); + qmlRegisterType(uri, 1, 0, "GSSAPIUser"); + qmlRegisterType(uri, 1, 0, "SID"); + qmlRegisterUncreatableType(uri, 1, 0, "Authenticator", "Cannot instatiate Authenticator base type"); + qmlRegisterType(uri, 1, 0, "UriTreeModel"); + + qRegisterMetaType("WampInvocationPointer"); + qRegisterMetaType("Event"); + qRegisterMetaType("WampError"); + qmlRegisterUncreatableType(uri, 1, 0, "WampError", "WampError cannot be created"); + qmlRegisterUncreatableType(uri, 1, 0, "WampBase", "Cannot instatiate WampBase base type"); + QMetaType::registerConverter(&WampError::toString); +} +} diff --git a/src/wamp.h b/src/wamp.h new file mode 100644 index 0000000..dd37bcb --- /dev/null +++ b/src/wamp.h @@ -0,0 +1,19 @@ +#ifndef WAMP_H +#define WAMP_H + +#include +#include +#include + +namespace QFlow{ + +class Wamp: public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "Wamp" FILE "wamp_metadata.json") +public: + void registerTypes(const char *uri); +}; +} + +#endif // WAMP_H diff --git a/src/wamp_global.h b/src/wamp_global.h new file mode 100644 index 0000000..e295b04 --- /dev/null +++ b/src/wamp_global.h @@ -0,0 +1,9 @@ +#include + +#if defined(WAMP_LIBRARY) +# define WAMP_EXPORT Q_DECL_EXPORT +#else +# define WAMP_EXPORT Q_DECL_IMPORT +#endif + + diff --git a/src/wamp_metadata.json b/src/wamp_metadata.json new file mode 100644 index 0000000..94ce011 --- /dev/null +++ b/src/wamp_metadata.json @@ -0,0 +1,5 @@ +{ +"category": "QFlow_plugin", +"description": "Collection of modules for wamp", +"version": "1.2" +} diff --git a/src/wamp_symbols.h b/src/wamp_symbols.h new file mode 100644 index 0000000..b5f147b --- /dev/null +++ b/src/wamp_symbols.h @@ -0,0 +1,56 @@ +#ifndef WAMP_SYMBOLS_H +#define WAMP_SYMBOLS_H + +#include + +namespace QFlow{ +const QString KEY_SUBSCRIPTION_ON_DELETE = QStringLiteral("wamp.subscription.on_delete"); +const QString KEY_SUBSCRIPTION_ON_CREATE = QStringLiteral("wamp.subscription.on_create"); +const QString KEY_SUBSCRIPTION_ON_SUBSCRIBE = QStringLiteral("wamp.subscription.on_subscribe"); +const QString KEY_SUBSCRIPTION_ON_UNSUBSCRIBE = QStringLiteral("wamp.subscription.on_unsubscribe"); +const QString KEY_REGISTRATION_ON_DELETE = QStringLiteral("wamp.registration.on_delete"); +const QString KEY_REGISTRATION_ON_CREATE = QStringLiteral("wamp.registration.on_create"); +const QString KEY_GET_SUBSCRIPTION = QStringLiteral("wamp.subscription.get"); +const QString KEY_COUNT_SUBSCRIBERS = QStringLiteral("wamp.subscription.count_subscribers"); +const QString KEY_DEFINE_SCHEMA = QStringLiteral("wamp.schema.define"); +const QString KEY_DESCRIBE_SCHEMA = QStringLiteral("wamp.schema.describe"); +const QString KEY_LOOKUP_REGISTRATION = QStringLiteral("wamp.registration.lookup"); +const QString KEY_LIST_REGISTRATION_URIS = QStringLiteral("wamp.registration.list_uris"); +const QString KEY_ERR_NOT_AUTHORIZED = QStringLiteral("wamp.error.not_authorized"); +const QString KEY_ERR_NOT_AUTHENTICATED = QStringLiteral("wamp.error.not_authenticated"); +const QString KEY_ERR_NO_SUCH_PROCEDURE = QStringLiteral("wamp.error.no_such_procedure"); +const QString KEY_ERR_NO_SUCH_REGISTRATION = QStringLiteral("wamp.error.no_such_registration"); +const QString KEY_ERR_NO_SUCH_REALM = QStringLiteral("wamp.error.no_such_realm"); +const QString KEY_WAMP_JSON_SUB = QStringLiteral("wamp.2.json"); +const QString KEY_WAMP_MSGPACK_SUB = QStringLiteral("wamp.2.msgpack"); + +enum WampMsgCode : int { + HELLO = 1, + WELCOME = 2, + ABORT = 3, + CHALLENGE = 4, + AUTHENTICATE = 5, + GOODBYE = 6, + HEARTBEAT = 7, + ERROR = 8, + PUBLISH = 16, + PUBLISHED = 17, + SUBSCRIBE = 32, + SUBSCRIBED = 33, + UNSUBSCRIBE = 34, + UNSUBSCRIBED = 35, + EVENT = 36, + CALL = 48, + CANCEL = 49, + RESULT = 50, + REGISTER = 64, + REGISTERED = 65, + UNREGISTER = 66, + UNREGISTERED = 67, + INVOCATION = 68, + INTERRUPT = 69, + YIELD = 70 +}; +} + +#endif // WAMP_SYMBOLS_H diff --git a/src/wampattached.cpp b/src/wampattached.cpp new file mode 100644 index 0000000..b792166 --- /dev/null +++ b/src/wampattached.cpp @@ -0,0 +1,20 @@ +#include "wampattached.h" + +namespace QFlow{ +WampAttached::WampAttached(QObject *parent) : QObject(parent), _isRemote(true) +{ + +} +WampAttached::~WampAttached() +{ + +} +bool WampAttached::isRemote() const +{ + return _isRemote; +} +void WampAttached::setIsRemote(bool value) +{ + _isRemote = value; +} +} diff --git a/src/wampattached.h b/src/wampattached.h new file mode 100644 index 0000000..a2bc2cf --- /dev/null +++ b/src/wampattached.h @@ -0,0 +1,24 @@ +#ifndef WAMPATTACHED_H +#define WAMPATTACHED_H + +#include + +namespace QFlow{ + + +class WampAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool isRemote READ isRemote WRITE setIsRemote NOTIFY isRemoteChanged) +public: + explicit WampAttached(QObject *parent = 0); + virtual ~WampAttached(); + bool isRemote() const; + void setIsRemote(bool value); +Q_SIGNALS: + void isRemoteChanged(); +private: + bool _isRemote; +}; +} +#endif // WAMPATTACHED_H diff --git a/src/wampbase.cpp b/src/wampbase.cpp new file mode 100644 index 0000000..ff6a29b --- /dev/null +++ b/src/wampbase.cpp @@ -0,0 +1,126 @@ +#include "wampbase.h" +#include "signalobserver.h" +#include +#include + +namespace QFlow{ + +WampBase::WampBase(QObject* parent) : QObject(parent) +{ + +} +void WampBase::registerPropertyGetter(QString uri, QObject *obj, QMetaProperty prop) +{ + Impl* impl = new PropertyGetterImpl(obj, prop); + RegistrationPointer reg(new Registration(uri, impl)); + addRegistration(reg); +} +void WampBase::registerPropertySetter(QString uri, QObject *obj, QMetaProperty prop) +{ + Impl* impl = new PropertySetterImpl(obj, prop); + RegistrationPointer reg(new Registration(uri, impl)); + addRegistration(reg); +} +void WampBase::registerList(QString uri, QQmlListReference list) +{ + for(int i=0;i()) + { + registerObject(uri, value.value()); + return; + } + QQmlProperty qmlProp(obj, prop.name()); + if(qmlProp.propertyTypeCategory() == QQmlProperty::List) + { + QQmlListReference ref(obj, prop.name()); + registerList(uri, ref); + return; + } + + if(prop.isReadable()) + registerPropertyGetter(uri, obj, prop); + if(prop.isWritable()) + { + QString setUri(uri + ".set"); + registerPropertySetter(setUri, obj, prop); + } +} +void WampBase::registerProperty(QString uri, QObject *obj, QString propName) +{ + int index = obj->metaObject()->indexOfProperty(propName.toLatin1()); + QMetaProperty prop = obj->metaObject()->property(index); + registerProperty(uri, obj, prop); +} + +void WampBase::registerMethod(QString uri, QObject *obj, QString methodName) +{ + int index = obj->metaObject()->indexOfMethod(methodName.toLatin1()); + QMetaMethod metaMethod = obj->metaObject()->method(index); + registerMethod(uri, obj, metaMethod); +} +void WampBase::registerMethod(QString uri, QObject* obj, QMetaMethod method) +{ + Impl* impl = new MethodImpl(obj, method); + RegistrationPointer reg(new Registration(uri, impl)); + addRegistration(reg); +} +void WampBase::registerProcedure(QString uri, QJSValue callback) +{ + Impl* impl = new JSImpl(callback); + RegistrationPointer reg(new Registration(uri, impl)); + addRegistration(reg); +} +void WampBase::registerSignal(QString uri, QObject *obj, QString signalSignature, bool enabled) +{ + SignalObserver* observer = new SignalObserver(obj, signalSignature.toUtf8(), enabled); + addSignalObserver(uri, observer); +} + +void WampBase::registerObject(QString uri, QObject *obj) +{ + const WampAttached *const attached = qobject_cast( + qmlAttachedPropertiesObject(obj, false)); + if(attached && !attached->isRemote()) return; + const QMetaObject* meta = obj->metaObject(); + for(int i=0;imethodCount();i++) + { + QMetaMethod method = meta->method(i); + if(method.access() != QMetaMethod::Public || + method.name() == "deleteLater") continue; + QString methodName = method.name(); + QString methodUri(QString("%1.%2").arg(uri).arg(methodName)); + if(method.methodType() == QMetaMethod::Slot) registerMethod(methodUri, obj, method); + else if(method.methodType() == QMetaMethod::Signal) + { + QString sig = method.methodSignature(); + registerSignal(methodUri, obj, sig, false); + } + } + for(int i=0;ipropertyCount();i++) + { + QMetaProperty prop = meta->property(i); + QString propName = prop.name(); + QString propUri(QString("%1.%2").arg(uri).arg(propName)); + registerProperty(propUri, obj, propName); + } +} +WampAttached *WampBase::qmlAttachedProperties(QObject *obj) +{ + return new WampAttached(obj); +} +} diff --git a/src/wampbase.h b/src/wampbase.h new file mode 100644 index 0000000..da22f23 --- /dev/null +++ b/src/wampbase.h @@ -0,0 +1,55 @@ +#ifndef WAMPBASE_H +#define WAMPBASE_H + +#include "functor.h" +#include "registration_p.h" +#include "wampattached.h" +#include +#include +#include + +namespace QFlow{ + +class SignalObserver; +class WampBase : public QObject +{ + Q_OBJECT +public: + WampBase(QObject* parent = NULL); + void registerList(QString uri, QQmlListReference list); + void registerMethod(QString uri, QObject* obj, QMetaMethod method); + void registerPropertyGetter(QString uri, QObject* obj, QMetaProperty prop); + void registerPropertySetter(QString uri, QObject* obj, QMetaProperty prop); + void registerProperty(QString uri, QObject* obj, QMetaProperty prop); + template + void registerProcedure(QString uri, std::function f) + { + Functor* functor = new Functor(f); + Impl* impl = new FunctorImpl(functor); + RegistrationPointer reg(new Registration(uri, impl)); + addRegistration(reg); + } + void registerProcedure(QString uri, SimpleCallback callback) + { + Impl* impl = new SimpleImpl(callback); + RegistrationPointer reg(new Registration(uri, impl)); + addRegistration(reg); + } + static WampAttached *qmlAttachedProperties(QObject *obj); + static QVariant deserializeMessage(const QString& message); + static QString serializeMessage(const QVariant& var); + static QJSValue variantToJS(const QVariant& var); +public Q_SLOTS: + void registerProcedure(QString uri, QJSValue callback); + void registerMethod(QString uri, QObject* obj, QString methodName); + void registerProperty(QString uri, QObject* obj, QString propName); + void registerObject(QString uri, QObject* obj); + void registerSignal(QString uri, QObject* obj, QString signalSignature, bool enabled = true); +protected: + virtual void addRegistration(RegistrationPointer reg) = 0; + virtual void addSignalObserver(QString uri, SignalObserver* observer) = 0; +}; +} +//QML_DECLARE_TYPE( QFlow::WampBase ) +QML_DECLARE_TYPEINFO( QFlow::WampBase, QML_HAS_ATTACHED_PROPERTIES ) +#endif // WAMPBASE_H diff --git a/src/wampcrauser.cpp b/src/wampcrauser.cpp new file mode 100644 index 0000000..1547b57 --- /dev/null +++ b/src/wampcrauser.cpp @@ -0,0 +1,78 @@ +#include "wampcrauser.h" +#include "credentialstore.h" +#include "wampconnection.h" +#include "user_p.h" +#include + +namespace QFlow{ + +class WampCraUserPrivate : public UserPrivate +{ +public: + QString _secret; + WampCraUserPrivate() : UserPrivate() + { + + } + ~WampCraUserPrivate() + { + + } +}; +WampCraUser::WampCraUser(QObject *parent) : User(*new WampCraUserPrivate, parent) +{ + +} +WampCraUser::WampCraUser(WampCraUserPrivate &dd, QObject *parent) : User(dd, parent) +{ + +} + +WampCraUser::WampCraUser(QString name, QString secret, QObject* parent) : User(*new WampCraUserPrivate, parent) +{ + Q_D(WampCraUser); + d->_secret = secret; + setName(name); +} + +WampCraUser::~WampCraUser() +{ + +} +QString WampCraUser::secret() const +{ + Q_D(const WampCraUser); + return d->_secret; +} +void WampCraUser::setSecret(QString value) +{ + Q_D(WampCraUser); + d->_secret = value; +} +QString WampCraUser::authMethod() const +{ + return "wampcra"; +} +QByteArray WampCraUser::response(QByteArray challenge) +{ + Q_D(WampCraUser); + QMessageAuthenticationCode hash(QCryptographicHash::Sha256); + CredentialStore store; + QString secret = d->_secret; + if(secret.isEmpty()) + { + WampConnection* parent = (WampConnection*)this->parent(); + secret = store.readPassword(parent->url()).toUtf8(); + } + /*if(extra.contains("salt") && !_socketPrivate->_wampCraIsSalted) + { + QByteArray salted = WampConnectionPrivate::PBKDF2(secret, + extra["salt"].toString(), extra["iterations"].toInt()); + secret = salted.toBase64(); + }*/ + hash.setKey(secret.toLatin1()); + hash.addData(challenge); + QByteArray hashResult = hash.result().toBase64(); + return hashResult; +} +} diff --git a/src/wampcrauser.h b/src/wampcrauser.h new file mode 100644 index 0000000..ce38a4a --- /dev/null +++ b/src/wampcrauser.h @@ -0,0 +1,31 @@ +#ifndef WAMPCRAUSER_H +#define WAMPCRAUSER_H + +#include "wamp_global.h" +#include "authenticator.h" +#include "role.h" +#include "user.h" +#include + +namespace QFlow{ + +class WampCraUserPrivate; +class WAMP_EXPORT WampCraUser : public User +{ + Q_OBJECT + Q_PROPERTY(QString secret READ secret WRITE setSecret) +public: + WampCraUser(QObject* parent = NULL); + WampCraUser(QString name, QString secret, QObject* parent = NULL); + ~WampCraUser(); + QString secret() const; + void setSecret(QString value); + QByteArray response(QByteArray challenge); + QString authMethod() const; +protected: + WampCraUser(WampCraUserPrivate &dd, QObject *parent); +private: + Q_DECLARE_PRIVATE(WampCraUser) +}; +} +#endif // WAMPCRAUSER_H diff --git a/src/wamperror.cpp b/src/wamperror.cpp new file mode 100644 index 0000000..9a08d69 --- /dev/null +++ b/src/wamperror.cpp @@ -0,0 +1,92 @@ +#include "wamperror.h" +#include +#include +#include + +namespace QFlow{ + +class WampErrorPrivate : public QSharedData +{ +public: + WampErrorPrivate() : QSharedData() + { + + } + WampErrorPrivate(const WampErrorPrivate& other) : QSharedData(other), + _requestType(other._requestType), _requestId(other._requestId), _details(other._details), _uri(other._uri), + _args(other._args) + { + + } + ~WampErrorPrivate() + { + + } + int _requestType; + qulonglong _requestId; + QVariantMap _details; + QUrl _uri; + QVariantList _args; +}; + +WampError::WampError(int requestType, qulonglong requestId, QVariantMap details, QUrl uri, QVariantList args) : d(new WampErrorPrivate()) +{ + d->_requestType = requestType; + d->_requestId = requestId; + d->_details = details; + d->_uri = uri; + d->_args = args; +} +WampError::WampError() : d(new WampErrorPrivate()) +{ + +} +WampError::~WampError() +{ + +} + +WampError::WampError(const WampError &other) : d(other.d) +{ + +} +WampError & WampError::operator=(const WampError &other) +{ + if (this == &other) + return *this; + d = other.d; + return *this; +} +int WampError::requestType() const +{ + return d->_requestType; +} +qulonglong WampError::requestId() const +{ + return d->_requestId; +} +QVariantMap WampError::details() const +{ + return d->_details; +} +QUrl WampError::uri() const +{ + return d->_uri; +} +QVariantList WampError::args() const +{ + return d->_args; +} +QString WampError::toString() const +{ + QJsonObject detailsJson = QJsonObject::fromVariantMap(d->_details); + QJsonDocument detailsDoc(detailsJson); + + QJsonArray argsJson = QJsonArray::fromVariantList(d->_args); + QJsonDocument argsDoc(argsJson); + + QString result = QString("RequestType: %1\nRequestId: %2\nDetails: %3ErrorUri: %4\nArgs: %5").arg(d->_requestType).arg(d->_requestId) + .arg(QString(detailsDoc.toJson())).arg(d->_uri.url()).arg(QString(argsDoc.toJson())); + return result; +} +} diff --git a/src/wamperror.h b/src/wamperror.h new file mode 100644 index 0000000..99a9bd2 --- /dev/null +++ b/src/wamperror.h @@ -0,0 +1,37 @@ +#ifndef WAMPERROR_H +#define WAMPERROR_H + +#include "wamp_global.h" +#include +#include +#include +#include + +namespace QFlow{ + +class WampErrorPrivate; +class WAMP_EXPORT WampError +{ + Q_GADGET + Q_PROPERTY(int requestType READ requestType) + Q_PROPERTY(qulonglong requestId READ requestId) + Q_PROPERTY(QVariantMap details READ details) + Q_PROPERTY(QUrl uri READ uri) + Q_PROPERTY(QVariantList args READ args) +public: + WampError(int requestType, qulonglong requestId, QVariantMap details, QUrl uri, QVariantList args); + WampError(); + WampError(const WampError& other); + WampError &operator=(const WampError &other); + ~WampError(); + int requestType() const; + qulonglong requestId() const; + QVariantMap details() const; + QUrl uri() const; + QVariantList args() const; + Q_INVOKABLE QString toString() const; +private: + QExplicitlySharedDataPointer d; +}; +} +#endif // WAMPERROR_H diff --git a/src/wampinvocation.h b/src/wampinvocation.h new file mode 100644 index 0000000..450c615 --- /dev/null +++ b/src/wampinvocation.h @@ -0,0 +1,30 @@ +#ifndef WAMPINVOCATION +#define WAMPINVOCATION + +#include +#include + +namespace QFlow{ + +class Registration; +typedef QSharedPointer RegistrationPointer; + +class WampInvocation +{ +public: + RegistrationPointer registration; + QVariantList args; + qulonglong requestId; + WampInvocation() : registration(NULL) + { + + } + ~WampInvocation() + { + + } +}; +typedef QSharedPointer WampInvocationPointer; +} +#endif // WAMPINVOCATION + diff --git a/src/wampmessageserializer.cpp b/src/wampmessageserializer.cpp new file mode 100644 index 0000000..15e7627 --- /dev/null +++ b/src/wampmessageserializer.cpp @@ -0,0 +1,284 @@ +#include "wampmessageserializer.h" +#include "wamp_symbols.h" +#include +#include +#include "msgpack.hpp" +#include +#include +#include + +namespace msgpack { +MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) { +namespace adaptor { + +template<> +struct pack { + template + packer& operator()(msgpack::packer& o, QVariant const& v) const { + if((QMetaType::Type)v.type() == QMetaType::Bool) + { + o.pack(v.toBool()); + } + else if((QMetaType::Type)v.type() == QMetaType::QString) + { + o.pack(v.toString().toStdString()); + } + else if((QMetaType::Type)v.type() == QMetaType::Int) + { + o.pack_int(v.toInt()); + } + else if((QMetaType::Type)v.type() == QMetaType::ULongLong) + { + o.pack_unsigned_long_long(v.toULongLong()); + } + else if((QMetaType::Type)v.type() == QMetaType::Float) + { + o.pack_float(v.toFloat()); + } + else if((QMetaType::Type)v.type() == QMetaType::Double) + { + o.pack_double(v.toDouble()); + } + else if((QMetaType::Type)v.type() == QMetaType::QByteArray) + { + QByteArray arr = v.toByteArray(); + o.pack_bin(arr.length()); + o.pack_bin_body(arr.data(), arr.length()); + } + else if((QMetaType::Type)v.type() == QMetaType::QVariantList || + (QMetaType::Type)v.type() == QMetaType::QStringList) + { + o.pack(v.toList()); + } + else if((QMetaType::Type)v.type() == QMetaType::QVariantMap) + { + o.pack(v.toMap()); + } + else if((QMetaType::Type)v.type() == QMetaType::QDateTime) + { + QString utcStr = v.toDateTime().toUTC().toString(Qt::ISODate); + o.pack(utcStr.toStdString()); + } + else if(v.canConvert()) + { + o.pack_int(v.toInt()); + } + else + { + qWarning() << "Could not serialize " << v.typeName(); + } + return o; + } +}; +template<> +struct pack { + template + packer& operator()(msgpack::packer& o, QVariantList const& arr) const { + o.pack_array(arr.length()); + Q_FOREACH(QVariant var, arr) + { + o.pack(var); + } + return o; + } +}; +template<> +struct pack { + template + packer& operator()(msgpack::packer& o, QVariantMap const& map) const { + o.pack_map(map.count()); + Q_FOREACH(QString key, map.keys()) + { + o.pack(key.toStdString()); + o.pack(map[key]); + } + return o; + } +}; +template<> +struct convert { + msgpack::object const& operator()(msgpack::object const& o, QVariant& v) const { + if(o.type == msgpack::type::BOOLEAN) + { + v = QVariant(o.as()); + } + else if(o.type == msgpack::type::STR) + { + v = QVariant(o.as()); + } + else if(o.type == msgpack::type::POSITIVE_INTEGER) + { + v = QVariant((quint64)o.via.u64); + } + else if(o.type == msgpack::type::NEGATIVE_INTEGER) + { + v = QVariant((qint64)o.via.i64); + } + else if(o.type == msgpack::type::FLOAT) + { + v = QVariant(o.via.f64); + } + else if(o.type == msgpack::type::BIN) + { + QByteArray ba; + ba.append(o.via.bin.ptr, o.via.bin.size); + v = QVariant(ba); + } + else if(o.type == msgpack::type::ARRAY) + { + v = QVariant(o.as()); + } + else if(o.type == msgpack::type::MAP) + { + v = QVariant(o.as()); + } + else + { + qWarning() << "Could not deserialize " << o.type; + } + return o; + } +}; +template<> +struct convert { + msgpack::object const& operator()(msgpack::object const& o, QVariantList& v) const { + QVariantList list; + if (o.type != msgpack::type::ARRAY) throw msgpack::type_error(); + for(uint i=0; i(); + list.append(var); + } + v = list; + return o; + } +}; +template<> +struct convert { + msgpack::object const& operator()(msgpack::object const& o, QString& v) const { + if (o.type != msgpack::type::STR) throw msgpack::type_error(); + QString str = QString::fromStdString(o.as()); + v = str; + return o; + } +}; +template<> +struct convert { + msgpack::object const& operator()(msgpack::object const& o, QVariantMap& v) const { + QVariantMap map; + if (o.type != msgpack::type::MAP) throw msgpack::type_error(); + for(uint i=0; i(); + QString key = pair.key.as(); + map[key] = value; + } + v = map; + return o; + } +}; +} +} +} + +QVariant toBase64(const QVariant& var) +{ + if((QMetaType::Type)var.type() == QMetaType::QByteArray) + { + QByteArray ba = var.toByteArray(); + return QVariant(ba.toBase64()); + } + else if((QMetaType::Type)var.type() == QMetaType::QVariantList) + { + QVariantList list = var.toList(); + for(int i=0;i(); + return arr; +} +QByteArray MsgpackMessageSerializer::serialize(const QVariantList &arr) +{ + std::stringstream buffer; + msgpack::pack(buffer, arr); + QByteArray message = QByteArray::fromStdString(buffer.str()); + return message; +} +bool MsgpackMessageSerializer::isBinary() const +{ + return true; +} +} diff --git a/src/wampmessageserializer.h b/src/wampmessageserializer.h new file mode 100644 index 0000000..ebef621 --- /dev/null +++ b/src/wampmessageserializer.h @@ -0,0 +1,39 @@ +#ifndef WAMPMESSAGESERIALIZER_H +#define WAMPMESSAGESERIALIZER_H + +#include +#include +#include + +namespace QFlow{ + +class WampMessageSerializer : public QObject +{ +public: + explicit WampMessageSerializer(QObject* parent = NULL); + virtual ~WampMessageSerializer(); + virtual QByteArray serialize(const QVariantList& arr) = 0; + virtual QVariantList deserialize(const QByteArray& message) = 0; + virtual bool isBinary() const; + static WampMessageSerializer* create(const QString& name); +}; +class JsonMessageSerializer : public WampMessageSerializer +{ +public: + explicit JsonMessageSerializer(QObject* parent = NULL); + virtual ~JsonMessageSerializer(); + QByteArray serialize(const QVariantList& arr); + QVariantList deserialize(const QByteArray& message); +}; + +class MsgpackMessageSerializer : public WampMessageSerializer +{ +public: + explicit MsgpackMessageSerializer(QObject* parent = NULL); + virtual ~MsgpackMessageSerializer(); + QByteArray serialize(const QVariantList& arr); + QVariantList deserialize(const QByteArray& message); + bool isBinary() const Q_DECL_OVERRIDE; +}; +} +#endif // WAMPMESSAGESERIALIZER_H diff --git a/wamp.qbs b/wamp.qbs new file mode 100644 index 0000000..182b7b3 --- /dev/null +++ b/wamp.qbs @@ -0,0 +1,11 @@ +import qbs +import qbs.Probes as Probes + +Project +{ + qbsSearchPaths: "qbs" + references: [ + "core/core.qbs", + "src/src.qbs" + ] +}