summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaindropsSys <contact@minteck.org>2023-07-09 17:18:50 +0200
committerRaindropsSys <contact@minteck.org>2023-07-09 17:18:50 +0200
commit13d6e5b53b656c1db79d962d63cc047de8befa3b (patch)
treec8b2f59e07a80a2b04068ca67255f74b59500e57
parent2dd43d2822b6a4b0645e70afa0b7b3bad6321e15 (diff)
downloadbridleshare-13d6e5b53b656c1db79d962d63cc047de8befa3b.tar.gz
bridleshare-13d6e5b53b656c1db79d962d63cc047de8befa3b.tar.bz2
bridleshare-13d6e5b53b656c1db79d962d63cc047de8befa3b.zip
Updated 5 files and added 3 files (automated)
-rw-r--r--assets/qrcode.js1
-rw-r--r--includes/functions.php12
-rw-r--r--includes/header.php1
-rw-r--r--includes/ws/index.js24
-rw-r--r--includes/ws/upstream.js7
-rw-r--r--includes/ws/upstream_run.js120
-rw-r--r--index.php505
-rw-r--r--plans/index.php48
8 files changed, 543 insertions, 175 deletions
diff --git a/assets/qrcode.js b/assets/qrcode.js
new file mode 100644
index 0000000..993e88f
--- /dev/null
+++ b/assets/qrcode.js
@@ -0,0 +1 @@
+var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this.parsedData=[];for(var b=[],d=0,e=this.data.length;e>d;d++){var f=this.data.charCodeAt(d);f>65536?(b[0]=240|(1835008&f)>>>18,b[1]=128|(258048&f)>>>12,b[2]=128|(4032&f)>>>6,b[3]=128|63&f):f>2048?(b[0]=224|(61440&f)>>>12,b[1]=128|(4032&f)>>>6,b[2]=128|63&f):f>128?(b[0]=192|(1984&f)>>>6,b[1]=128|63&f):b[0]=f,this.parsedData=this.parsedData.concat(b)}this.parsedData.length!=this.data.length&&(this.parsedData.unshift(191),this.parsedData.unshift(187),this.parsedData.unshift(239))}function b(a,b){this.typeNumber=a,this.errorCorrectLevel=b,this.modules=null,this.moduleCount=0,this.dataCache=null,this.dataList=[]}function i(a,b){if(void 0==a.length)throw new Error(a.length+"/"+b);for(var c=0;c<a.length&&0==a[c];)c++;this.num=new Array(a.length-c+b);for(var d=0;d<a.length-c;d++)this.num[d]=a[d+c]}function j(a,b){this.totalCount=a,this.dataCount=b}function k(){this.buffer=[],this.length=0}function m(){return"undefined"!=typeof CanvasRenderingContext2D}function n(){var a=!1,b=navigator.userAgent;return/android/i.test(b)&&(a=!0,aMat=b.toString().match(/android ([0-9]\.[0-9])/i),aMat&&aMat[1]&&(a=parseFloat(aMat[1]))),a}function r(a,b){for(var c=1,e=s(a),f=0,g=l.length;g>=f;f++){var h=0;switch(b){case d.L:h=l[f][0];break;case d.M:h=l[f][1];break;case d.Q:h=l[f][2];break;case d.H:h=l[f][3]}if(h>=e)break;c++}if(c>l.length)throw new Error("Too long data");return c}function s(a){var b=encodeURI(a).toString().replace(/\%[0-9a-fA-F]{2}/g,"a");return b.length+(b.length!=a?3:0)}a.prototype={getLength:function(){return this.parsedData.length},write:function(a){for(var b=0,c=this.parsedData.length;c>b;b++)a.put(this.parsedData[b],8)}},b.prototype={addData:function(b){var c=new a(b);this.dataList.push(c),this.dataCache=null},isDark:function(a,b){if(0>a||this.moduleCount<=a||0>b||this.moduleCount<=b)throw new Error(a+","+b);return this.modules[a][b]},getModuleCount:function(){return this.moduleCount},make:function(){this.makeImpl(!1,this.getBestMaskPattern())},makeImpl:function(a,c){this.moduleCount=4*this.typeNumber+17,this.modules=new Array(this.moduleCount);for(var d=0;d<this.moduleCount;d++){this.modules[d]=new Array(this.moduleCount);for(var e=0;e<this.moduleCount;e++)this.modules[d][e]=null}this.setupPositionProbePattern(0,0),this.setupPositionProbePattern(this.moduleCount-7,0),this.setupPositionProbePattern(0,this.moduleCount-7),this.setupPositionAdjustPattern(),this.setupTimingPattern(),this.setupTypeInfo(a,c),this.typeNumber>=7&&this.setupTypeNumber(a),null==this.dataCache&&(this.dataCache=b.createData(this.typeNumber,this.errorCorrectLevel,this.dataList)),this.mapData(this.dataCache,c)},setupPositionProbePattern:function(a,b){for(var c=-1;7>=c;c++)if(!(-1>=a+c||this.moduleCount<=a+c))for(var d=-1;7>=d;d++)-1>=b+d||this.moduleCount<=b+d||(this.modules[a+c][b+d]=c>=0&&6>=c&&(0==d||6==d)||d>=0&&6>=d&&(0==c||6==c)||c>=2&&4>=c&&d>=2&&4>=d?!0:!1)},getBestMaskPattern:function(){for(var a=0,b=0,c=0;8>c;c++){this.makeImpl(!0,c);var d=f.getLostPoint(this);(0==c||a>d)&&(a=d,b=c)}return b},createMovieClip:function(a,b,c){var d=a.createEmptyMovieClip(b,c),e=1;this.make();for(var f=0;f<this.modules.length;f++)for(var g=f*e,h=0;h<this.modules[f].length;h++){var i=h*e,j=this.modules[f][h];j&&(d.beginFill(0,100),d.moveTo(i,g),d.lineTo(i+e,g),d.lineTo(i+e,g+e),d.lineTo(i,g+e),d.endFill())}return d},setupTimingPattern:function(){for(var a=8;a<this.moduleCount-8;a++)null==this.modules[a][6]&&(this.modules[a][6]=0==a%2);for(var b=8;b<this.moduleCount-8;b++)null==this.modules[6][b]&&(this.modules[6][b]=0==b%2)},setupPositionAdjustPattern:function(){for(var a=f.getPatternPosition(this.typeNumber),b=0;b<a.length;b++)for(var c=0;c<a.length;c++){var d=a[b],e=a[c];if(null==this.modules[d][e])for(var g=-2;2>=g;g++)for(var h=-2;2>=h;h++)this.modules[d+g][e+h]=-2==g||2==g||-2==h||2==h||0==g&&0==h?!0:!1}},setupTypeNumber:function(a){for(var b=f.getBCHTypeNumber(this.typeNumber),c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[Math.floor(c/3)][c%3+this.moduleCount-8-3]=d}for(var c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[c%3+this.moduleCount-8-3][Math.floor(c/3)]=d}},setupTypeInfo:function(a,b){for(var c=this.errorCorrectLevel<<3|b,d=f.getBCHTypeInfo(c),e=0;15>e;e++){var g=!a&&1==(1&d>>e);6>e?this.modules[e][8]=g:8>e?this.modules[e+1][8]=g:this.modules[this.moduleCount-15+e][8]=g}for(var e=0;15>e;e++){var g=!a&&1==(1&d>>e);8>e?this.modules[8][this.moduleCount-e-1]=g:9>e?this.modules[8][15-e-1+1]=g:this.modules[8][15-e-1]=g}this.modules[this.moduleCount-8][8]=!a},mapData:function(a,b){for(var c=-1,d=this.moduleCount-1,e=7,g=0,h=this.moduleCount-1;h>0;h-=2)for(6==h&&h--;;){for(var i=0;2>i;i++)if(null==this.modules[d][h-i]){var j=!1;g<a.length&&(j=1==(1&a[g]>>>e));var k=f.getMask(b,d,h-i);k&&(j=!j),this.modules[d][h-i]=j,e--,-1==e&&(g++,e=7)}if(d+=c,0>d||this.moduleCount<=d){d-=c,c=-c;break}}}},b.PAD0=236,b.PAD1=17,b.createData=function(a,c,d){for(var e=j.getRSBlocks(a,c),g=new k,h=0;h<d.length;h++){var i=d[h];g.put(i.mode,4),g.put(i.getLength(),f.getLengthInBits(i.mode,a)),i.write(g)}for(var l=0,h=0;h<e.length;h++)l+=e[h].dataCount;if(g.getLengthInBits()>8*l)throw new Error("code length overflow. ("+g.getLengthInBits()+">"+8*l+")");for(g.getLengthInBits()+4<=8*l&&g.put(0,4);0!=g.getLengthInBits()%8;)g.putBit(!1);for(;;){if(g.getLengthInBits()>=8*l)break;if(g.put(b.PAD0,8),g.getLengthInBits()>=8*l)break;g.put(b.PAD1,8)}return b.createBytes(g,e)},b.createBytes=function(a,b){for(var c=0,d=0,e=0,g=new Array(b.length),h=new Array(b.length),j=0;j<b.length;j++){var k=b[j].dataCount,l=b[j].totalCount-k;d=Math.max(d,k),e=Math.max(e,l),g[j]=new Array(k);for(var m=0;m<g[j].length;m++)g[j][m]=255&a.buffer[m+c];c+=k;var n=f.getErrorCorrectPolynomial(l),o=new i(g[j],n.getLength()-1),p=o.mod(n);h[j]=new Array(n.getLength()-1);for(var m=0;m<h[j].length;m++){var q=m+p.getLength()-h[j].length;h[j][m]=q>=0?p.get(q):0}}for(var r=0,m=0;m<b.length;m++)r+=b[m].totalCount;for(var s=new Array(r),t=0,m=0;d>m;m++)for(var j=0;j<b.length;j++)m<g[j].length&&(s[t++]=g[j][m]);for(var m=0;e>m;m++)for(var j=0;j<b.length;j++)m<h[j].length&&(s[t++]=h[j][m]);return s};for(var c={MODE_NUMBER:1,MODE_ALPHA_NUM:2,MODE_8BIT_BYTE:4,MODE_KANJI:8},d={L:1,M:0,Q:3,H:2},e={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7},f={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:1335,G18:7973,G15_MASK:21522,getBCHTypeInfo:function(a){for(var b=a<<10;f.getBCHDigit(b)-f.getBCHDigit(f.G15)>=0;)b^=f.G15<<f.getBCHDigit(b)-f.getBCHDigit(f.G15);return(a<<10|b)^f.G15_MASK},getBCHTypeNumber:function(a){for(var b=a<<12;f.getBCHDigit(b)-f.getBCHDigit(f.G18)>=0;)b^=f.G18<<f.getBCHDigit(b)-f.getBCHDigit(f.G18);return a<<12|b},getBCHDigit:function(a){for(var b=0;0!=a;)b++,a>>>=1;return b},getPatternPosition:function(a){return f.PATTERN_POSITION_TABLE[a-1]},getMask:function(a,b,c){switch(a){case e.PATTERN000:return 0==(b+c)%2;case e.PATTERN001:return 0==b%2;case e.PATTERN010:return 0==c%3;case e.PATTERN011:return 0==(b+c)%3;case e.PATTERN100:return 0==(Math.floor(b/2)+Math.floor(c/3))%2;case e.PATTERN101:return 0==b*c%2+b*c%3;case e.PATTERN110:return 0==(b*c%2+b*c%3)%2;case e.PATTERN111:return 0==(b*c%3+(b+c)%2)%2;default:throw new Error("bad maskPattern:"+a)}},getErrorCorrectPolynomial:function(a){for(var b=new i([1],0),c=0;a>c;c++)b=b.multiply(new i([1,g.gexp(c)],0));return b},getLengthInBits:function(a,b){if(b>=1&&10>b)switch(a){case c.MODE_NUMBER:return 10;case c.MODE_ALPHA_NUM:return 9;case c.MODE_8BIT_BYTE:return 8;case c.MODE_KANJI:return 8;default:throw new Error("mode:"+a)}else if(27>b)switch(a){case c.MODE_NUMBER:return 12;case c.MODE_ALPHA_NUM:return 11;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 10;default:throw new Error("mode:"+a)}else{if(!(41>b))throw new Error("type:"+b);switch(a){case c.MODE_NUMBER:return 14;case c.MODE_ALPHA_NUM:return 13;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 12;default:throw new Error("mode:"+a)}}},getLostPoint:function(a){for(var b=a.getModuleCount(),c=0,d=0;b>d;d++)for(var e=0;b>e;e++){for(var f=0,g=a.isDark(d,e),h=-1;1>=h;h++)if(!(0>d+h||d+h>=b))for(var i=-1;1>=i;i++)0>e+i||e+i>=b||(0!=h||0!=i)&&g==a.isDark(d+h,e+i)&&f++;f>5&&(c+=3+f-5)}for(var d=0;b-1>d;d++)for(var e=0;b-1>e;e++){var j=0;a.isDark(d,e)&&j++,a.isDark(d+1,e)&&j++,a.isDark(d,e+1)&&j++,a.isDark(d+1,e+1)&&j++,(0==j||4==j)&&(c+=3)}for(var d=0;b>d;d++)for(var e=0;b-6>e;e++)a.isDark(d,e)&&!a.isDark(d,e+1)&&a.isDark(d,e+2)&&a.isDark(d,e+3)&&a.isDark(d,e+4)&&!a.isDark(d,e+5)&&a.isDark(d,e+6)&&(c+=40);for(var e=0;b>e;e++)for(var d=0;b-6>d;d++)a.isDark(d,e)&&!a.isDark(d+1,e)&&a.isDark(d+2,e)&&a.isDark(d+3,e)&&a.isDark(d+4,e)&&!a.isDark(d+5,e)&&a.isDark(d+6,e)&&(c+=40);for(var k=0,e=0;b>e;e++)for(var d=0;b>d;d++)a.isDark(d,e)&&k++;var l=Math.abs(100*k/b/b-50)/5;return c+=10*l}},g={glog:function(a){if(1>a)throw new Error("glog("+a+")");return g.LOG_TABLE[a]},gexp:function(a){for(;0>a;)a+=255;for(;a>=256;)a-=255;return g.EXP_TABLE[a]},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)},h=0;8>h;h++)g.EXP_TABLE[h]=1<<h;for(var h=8;256>h;h++)g.EXP_TABLE[h]=g.EXP_TABLE[h-4]^g.EXP_TABLE[h-5]^g.EXP_TABLE[h-6]^g.EXP_TABLE[h-8];for(var h=0;255>h;h++)g.LOG_TABLE[g.EXP_TABLE[h]]=h;i.prototype={get:function(a){return this.num[a]},getLength:function(){return this.num.length},multiply:function(a){for(var b=new Array(this.getLength()+a.getLength()-1),c=0;c<this.getLength();c++)for(var d=0;d<a.getLength();d++)b[c+d]^=g.gexp(g.glog(this.get(c))+g.glog(a.get(d)));return new i(b,0)},mod:function(a){if(this.getLength()-a.getLength()<0)return this;for(var b=g.glog(this.get(0))-g.glog(a.get(0)),c=new Array(this.getLength()),d=0;d<this.getLength();d++)c[d]=this.get(d);for(var d=0;d<a.getLength();d++)c[d]^=g.gexp(g.glog(a.get(d))+b);return new i(c,0).mod(a)}},j.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],j.getRSBlocks=function(a,b){var c=j.getRsBlockTable(a,b);if(void 0==c)throw new Error("bad rs block @ typeNumber:"+a+"/errorCorrectLevel:"+b);for(var d=c.length/3,e=[],f=0;d>f;f++)for(var g=c[3*f+0],h=c[3*f+1],i=c[3*f+2],k=0;g>k;k++)e.push(new j(h,i));return e},j.getRsBlockTable=function(a,b){switch(b){case d.L:return j.RS_BLOCK_TABLE[4*(a-1)+0];case d.M:return j.RS_BLOCK_TABLE[4*(a-1)+1];case d.Q:return j.RS_BLOCK_TABLE[4*(a-1)+2];case d.H:return j.RS_BLOCK_TABLE[4*(a-1)+3];default:return void 0}},k.prototype={get:function(a){var b=Math.floor(a/8);return 1==(1&this.buffer[b]>>>7-a%8)},put:function(a,b){for(var c=0;b>c;c++)this.putBit(1==(1&a>>>b-c-1))},getLengthInBits:function(){return this.length},putBit:function(a){var b=Math.floor(this.length/8);this.buffer.length<=b&&this.buffer.push(0),a&&(this.buffer[b]|=128>>>this.length%8),this.length++}};var l=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]],o=function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){function g(a,b){var c=document.createElementNS("http://www.w3.org/2000/svg",a);for(var d in b)b.hasOwnProperty(d)&&c.setAttribute(d,b[d]);return c}var b=this._htOption,c=this._el,d=a.getModuleCount();Math.floor(b.width/d),Math.floor(b.height/d),this.clear();var h=g("svg",{viewBox:"0 0 "+String(d)+" "+String(d),width:"100%",height:"100%",fill:b.colorLight});h.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xlink","http://www.w3.org/1999/xlink"),c.appendChild(h),h.appendChild(g("rect",{fill:b.colorDark,width:"1",height:"1",id:"template"}));for(var i=0;d>i;i++)for(var j=0;d>j;j++)if(a.isDark(i,j)){var k=g("use",{x:String(i),y:String(j)});k.setAttributeNS("http://www.w3.org/1999/xlink","href","#template"),h.appendChild(k)}},a.prototype.clear=function(){for(;this._el.hasChildNodes();)this._el.removeChild(this._el.lastChild)},a}(),p="svg"===document.documentElement.tagName.toLowerCase(),q=p?o:m()?function(){function a(){this._elImage.src=this._elCanvas.toDataURL("image/png"),this._elImage.style.display="block",this._elCanvas.style.display="none"}function d(a,b){var c=this;if(c._fFail=b,c._fSuccess=a,null===c._bSupportDataURI){var d=document.createElement("img"),e=function(){c._bSupportDataURI=!1,c._fFail&&_fFail.call(c)},f=function(){c._bSupportDataURI=!0,c._fSuccess&&c._fSuccess.call(c)};return d.onabort=e,d.onerror=e,d.onload=f,d.src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",void 0}c._bSupportDataURI===!0&&c._fSuccess?c._fSuccess.call(c):c._bSupportDataURI===!1&&c._fFail&&c._fFail.call(c)}if(this._android&&this._android<=2.1){var b=1/window.devicePixelRatio,c=CanvasRenderingContext2D.prototype.drawImage;CanvasRenderingContext2D.prototype.drawImage=function(a,d,e,f,g,h,i,j){if("nodeName"in a&&/img/i.test(a.nodeName))for(var l=arguments.length-1;l>=1;l--)arguments[l]=arguments[l]*b;else"undefined"==typeof j&&(arguments[1]*=b,arguments[2]*=b,arguments[3]*=b,arguments[4]*=b);c.apply(this,arguments)}}var e=function(a,b){this._bIsPainted=!1,this._android=n(),this._htOption=b,this._elCanvas=document.createElement("canvas"),this._elCanvas.width=b.width,this._elCanvas.height=b.height,a.appendChild(this._elCanvas),this._el=a,this._oContext=this._elCanvas.getContext("2d"),this._bIsPainted=!1,this._elImage=document.createElement("img"),this._elImage.style.display="none",this._el.appendChild(this._elImage),this._bSupportDataURI=null};return e.prototype.draw=function(a){var b=this._elImage,c=this._oContext,d=this._htOption,e=a.getModuleCount(),f=d.width/e,g=d.height/e,h=Math.round(f),i=Math.round(g);b.style.display="none",this.clear();for(var j=0;e>j;j++)for(var k=0;e>k;k++){var l=a.isDark(j,k),m=k*f,n=j*g;c.strokeStyle=l?d.colorDark:d.colorLight,c.lineWidth=1,c.fillStyle=l?d.colorDark:d.colorLight,c.fillRect(m,n,f,g),c.strokeRect(Math.floor(m)+.5,Math.floor(n)+.5,h,i),c.strokeRect(Math.ceil(m)-.5,Math.ceil(n)-.5,h,i)}this._bIsPainted=!0},e.prototype.makeImage=function(){this._bIsPainted&&d.call(this,a)},e.prototype.isPainted=function(){return this._bIsPainted},e.prototype.clear=function(){this._oContext.clearRect(0,0,this._elCanvas.width,this._elCanvas.height),this._bIsPainted=!1},e.prototype.round=function(a){return a?Math.floor(1e3*a)/1e3:a},e}():function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){for(var b=this._htOption,c=this._el,d=a.getModuleCount(),e=Math.floor(b.width/d),f=Math.floor(b.height/d),g=['<table style="border:0;border-collapse:collapse;">'],h=0;d>h;h++){g.push("<tr>");for(var i=0;d>i;i++)g.push('<td style="border:0;border-collapse:collapse;padding:0;margin:0;width:'+e+"px;height:"+f+"px;background-color:"+(a.isDark(h,i)?b.colorDark:b.colorLight)+';"></td>');g.push("</tr>")}g.push("</table>"),c.innerHTML=g.join("");var j=c.childNodes[0],k=(b.width-j.offsetWidth)/2,l=(b.height-j.offsetHeight)/2;k>0&&l>0&&(j.style.margin=l+"px "+k+"px")},a.prototype.clear=function(){this._el.innerHTML=""},a}();QRCode=function(a,b){if(this._htOption={width:256,height:256,typeNumber:4,colorDark:"#000000",colorLight:"#ffffff",correctLevel:d.H},"string"==typeof b&&(b={text:b}),b)for(var c in b)this._htOption[c]=b[c];"string"==typeof a&&(a=document.getElementById(a)),this._android=n(),this._el=a,this._oQRCode=null,this._oDrawing=new q(this._el,this._htOption),this._htOption.text&&this.makeCode(this._htOption.text)},QRCode.prototype.makeCode=function(a){this._oQRCode=new b(r(a,this._htOption.correctLevel),this._htOption.correctLevel),this._oQRCode.addData(a),this._oQRCode.make(),this._el.title=a,this._oDrawing.draw(this._oQRCode),this.makeImage()},QRCode.prototype.makeImage=function(){"function"==typeof this._oDrawing.makeImage&&(!this._android||this._android>=3)&&this._oDrawing.makeImage()},QRCode.prototype.clear=function(){this._oDrawing.clear()},QRCode.CorrectLevel=d}(); \ No newline at end of file
diff --git a/includes/functions.php b/includes/functions.php
index 82dfea4..42c4ec1 100644
--- a/includes/functions.php
+++ b/includes/functions.php
@@ -54,24 +54,20 @@ function getLimits(): array {
if (!isLoggedIn()) {
return [
- 'speed' => 100*1024,
- 'size' => 50*1024**2
+ 'speed' => 500*1024
];
} else {
if (in_array(getUserID(), $app["high2"])) {
return [
- 'speed' => 5*1024**2,
- 'size' => 20480*1024**2
+ 'speed' => 10*1024**2
];
} elseif (in_array(getUserID(), $app["high1"])) {
return [
- 'speed' => 1024**2,
- 'size' => 1024*1024**2
+ 'speed' => 3*1024**2
];
} else {
return [
- 'speed' => 500*1024,
- 'size' => 100*1024**2
+ 'speed' => 1024**2
];
}
}
diff --git a/includes/header.php b/includes/header.php
index 0a4627c..b168dae 100644
--- a/includes/header.php
+++ b/includes/header.php
@@ -12,6 +12,7 @@
<script src="/assets/bootstrap.bundle.min.js"></script>
<script src="/assets/baseex.js"></script>
<script src="/assets/libcrypt.js"></script>
+ <script src="/assets/qrcode.js"></script>
<style>
@media (max-width: 900px) {
#box {
diff --git a/includes/ws/index.js b/includes/ws/index.js
index ef4073d..f01ac7c 100644
--- a/includes/ws/index.js
+++ b/includes/ws/index.js
@@ -29,7 +29,6 @@ wss.on('connection', function connection(ws, req) {
let token = ws.token = parseCookies(req)['BS_SESSION_TOKEN'] ?? null;
let user = ws.user = token && !token.includes("/") && token.startsWith("bs") && fs.existsSync("../tokens/" + token) ? JSON.parse(fs.readFileSync("../tokens/" + token).toString()) : null;
let id = ws.id = crypto.randomBytes(96).toString("hex").match(/.{1,4}/g).map(i => parseInt(i, 16).toString(36)).join("").substring(0, 96);
- let transmittedData = 0;
users[id] = ws;
@@ -40,23 +39,19 @@ wss.on('connection', function connection(ws, req) {
if (user?.id && privilegeList['high2'].includes(user?.id)) {
limits = ws.limits = {
- speed: 5*1024**2,
- size: 20480*1024**2
+ speed: 5*1024**2
}
} else if (user?.id && privilegeList['high1'].includes(user?.id)) {
limits = ws.limits = {
- speed: 1024**2,
- size: 1024*1024**2
+ speed: 1024**2
}
} else if (user?.id) {
limits = ws.limits = {
- speed: 500*1024,
- size: 100*1024**2
+ speed: 500*1024
}
} else {
limits = ws.limits = {
- speed: 100*1024,
- size: 50*1024**2
+ speed: 100*1024
}
}
@@ -69,16 +64,6 @@ wss.on('connection', function connection(ws, req) {
if (data.type === "encrypted" || data.type === "encryptedData") {
if (ws.peer && data.message) {
- transmittedData += JSON.stringify(data.message).length;
-
- /*if (transmittedData >= limits.size * 1.02) {
- ws.send(JSON.stringify({
- type: "error",
- error: "SIZE_LIMIT_EXCEEDED"
- }));
- ws.close();
- }*/
-
let delay = new Date().getTime() - ws.lastMessage > 0 ? Math.round((JSON.stringify(data.message).length / limits.speed) * 1000) : 0;
setTimeout(() => {
@@ -86,6 +71,7 @@ wss.on('connection', function connection(ws, req) {
ws.peer.send(JSON.stringify({
type: data.type,
+ chunk: data.chunk ?? null,
message: data.message,
delay
}));
diff --git a/includes/ws/upstream.js b/includes/ws/upstream.js
new file mode 100644
index 0000000..9b1bb82
--- /dev/null
+++ b/includes/ws/upstream.js
@@ -0,0 +1,7 @@
+const child_process = require('child_process');
+
+child_process.execFile("node", [ "upstream_run.js", "19202" ], { stdio: "inherit" });
+child_process.execFile("node", [ "upstream_run.js", "19203" ], { stdio: "inherit" });
+child_process.execFile("node", [ "upstream_run.js", "19204" ], { stdio: "inherit" });
+child_process.execFile("node", [ "upstream_run.js", "19205" ], { stdio: "inherit" });
+child_process.execFile("node", [ "upstream_run.js", "19206" ], { stdio: "inherit" }); \ No newline at end of file
diff --git a/includes/ws/upstream_run.js b/includes/ws/upstream_run.js
new file mode 100644
index 0000000..193a7e2
--- /dev/null
+++ b/includes/ws/upstream_run.js
@@ -0,0 +1,120 @@
+const { WebSocketServer } = require('ws');
+const fs = require('fs');
+const crypto = require('crypto');
+
+const tokens = {};
+
+function parseCookies (request) {
+ const list = {};
+ const cookieHeader = request.headers?.cookie;
+ if (!cookieHeader) return list;
+
+ cookieHeader.split(`;`).forEach(function(cookie) {
+ let [ name, ...rest] = cookie.split(`=`);
+ name = name?.trim();
+ if (!name) return;
+ const value = rest.join(`=`).trim();
+ if (!value) return;
+ list[name] = decodeURIComponent(value);
+ });
+
+ return list;
+}
+
+function connectionHandler(ws, req, port) {
+ let privilegeList = JSON.parse(fs.readFileSync("../app.json").toString());
+
+ let token = ws.token = parseCookies(req)['BS_SESSION_TOKEN'] ?? null;
+ let user = ws.user = token && !token.includes("/") && token.startsWith("bs") && fs.existsSync("../tokens/" + token) ? JSON.parse(fs.readFileSync("../tokens/" + token).toString()) : null;
+
+ let limits;
+
+ ws.token = null;
+ ws.peer = null;
+ ws.lastMessage = 0;
+
+ if (user?.id && privilegeList['high2'].includes(user?.id)) {
+ limits = ws.limits = {
+ speed: 2*1024**2
+ }
+ } else if (user?.id && privilegeList['high1'].includes(user?.id)) {
+ limits = ws.limits = {
+ speed: 1024**2
+ }
+ } else if (user?.id) {
+ limits = ws.limits = {
+ speed: 500*1024
+ }
+ }
+
+ ws.on('error', console.error);
+
+ ws.on('message', function message(_data) {
+ try {
+ let data = JSON.parse(_data);
+ if (!data.type || (data.type && data.type !== "keepalive" && data.type !== "encryptedData" && data.type !== "encrypted")) console.log(data);
+
+ if (data.type === "encryptedData") {
+ if (ws.peer && data.message) {
+ let delay = new Date().getTime() - ws.lastMessage > 0 ? Math.round((JSON.stringify(data.message).length / limits.speed) * 1000) : 0;
+
+ setTimeout(() => {
+ ws.lastMessage = new Date().getTime();
+
+ ws.peer.send(JSON.stringify({
+ type: data.type,
+ chunk: data.chunk ?? null,
+ message: data.message,
+ delay
+ }));
+ }, delay);
+ }
+ }
+
+ if (data.type === "token") {
+ if (tokens[data.token]) {
+ tokens[data.token][1] = ws;
+ ws.peer = tokens[data.token][0];
+ ws.token = data.token;
+ tokens[data.token][0].peer = ws;
+ } else {
+ if (!user?.id || (port > 19204 && !(user?.id && privilegeList['high2'].includes(user?.id))) || (port > 19203 && !(user?.id && privilegeList['high1'].includes(user?.id)) && !(user?.id && privilegeList['high2'].includes(user?.id)))) ws.close();
+ tokens[data.token] = [ ws, null ];
+ }
+ }
+ } catch (e) {
+ console.error(e);
+ }
+ });
+
+ ws.on('close', () => {
+ if (ws.peer) {
+ ws.peer.close();
+ if (ws.token) delete tokens[ws.token];
+ }
+ })
+
+ ws.send(JSON.stringify({
+ type: 'init',
+ limits,
+ code: ws.id
+ }));
+}
+
+const { createServer } = require('https');
+
+function startServer(port) {
+ const server = createServer({
+ cert: fs.readFileSync('/etc/letsencrypt/live/minteck.org/fullchain.pem'),
+ key: fs.readFileSync('/etc/letsencrypt/live/minteck.org/privkey.pem')
+ });
+
+ const wss = new WebSocketServer({ server });
+ wss.on('connection', (ws, req) => {
+ connectionHandler(ws, req, port);
+ });
+
+ server.listen(port);
+}
+
+startServer(parseInt(process.argv[2])); \ No newline at end of file
diff --git a/index.php b/index.php
index 6dad5eb..f79ca51 100644
--- a/index.php
+++ b/index.php
@@ -11,12 +11,12 @@
<div>
<img src="/assets/icons/add.svg" style="width: 64px; margin-bottom: 10px;"><br>
<div style="font-weight: bold; font-size: 1.2em;">Drop and drop a file</div>
- or click to share up to <?= sizeToString(getLimits()['size']) ?>
+ or click at <?= sizeToString(getLimits()['speed']) ?>/s
<div style="margin-top: 20px;">
<span class="btn btn-primary">Select a file to share</span><br>
<div style="margin-top: 10px;">
<?php if (!isLoggedIn()): ?>
- <a class="small" href="/auth/init/" id="login-cta">Sign in to send up to 100 MiB</a>
+ <a class="small" href="/auth/init/" id="login-cta">Sign in to share at 1 MiB/s</a>
<?php else: ?>
<a class="small" href="/plan/" id="login-cta">Contact us to share larger files</a>
<?php endif; ?>
@@ -49,6 +49,16 @@
}
}
+ window.onunload = () => {
+ if (connected) {
+ try {
+ sendEncrypted({
+ type: "leave"
+ });
+ } catch (e) {}
+ }
+ }
+
document.getElementById("drop-zone").ondrop = (ev) => {
console.log("File(s) dropped");
ev.preventDefault();
@@ -87,13 +97,18 @@
<div class="progress-bar progress-bar-striped progress-bar-animated bg-progress" style="width: 100%;" id="share-progress"></div>
</div>
- <div id="file-status">
- Waiting for the other user to connect...
+ <div>
+ <span id="file-status">Waiting for the other user to connect...</span>
</div>
</div>
</div>
<hr>
- <div id="share-link">Share the following link with the other user:<br><a id="link" style="word-break: break-all;" href="#">https://bridleshare.equestria.dev/?/{code}</a></div>
+ <div id="share-link">Share the following link with the other user:
+ <div style="margin-top: 10px; display: grid; grid-template-columns: 2fr 1fr; grid-gap: 10px;">
+ <a id="link" style="word-break: break-all;" href="#">https://bridleshare.equestria.dev/?/{code}</a>
+ <div id="qr"></div>
+ </div>
+ </div>
<script>
document.getElementById("link").onclick = (e) => {
e.preventDefault();
@@ -124,8 +139,8 @@
<div class="progress-bar progress-bar-striped progress-bar-animated bg-progress" style="width: 100%;" id="receive-progress"></div>
</div>
- <div id="rfile-status">
- Waiting for the other user to connect...
+ <div>
+ <span id="rfile-status">Waiting for the other user to connect...</span>
</div>
</div>
</div>
@@ -159,28 +174,29 @@
}
window.currentID = null;
- window.currentSpeed = 0;
window.maxSpeed = <?= getLimits()['speed'] ?> / 2;
window.file = null;
window.keyChain = null;
window.forceQuit = false;
+ window.downloadStarted = false;
window.infoInTitle = false;
window.oldTitle = document.title;
+ window.receivedChunks = [];
- function formatSize(size) {
+ function formatSize(size, small) {
if (size > 1024) {
if (size > 1024**2) {
if (size > 1024**3) {
if (size > 1024**4) {
- return (size / 1024**4).toFixed(1) + " TiB";
+ return (size / 1024**4).toFixed(1) + (small ? "T" : " TiB");
} else {
- return (size / 1024**3).toFixed(1) + " GiB";
+ return (size / 1024**3).toFixed(1) + (small ? "G" : " GiB");
}
} else {
- return (size / 1024**2).toFixed(1) + " MiB";
+ return (size / 1024**2).toFixed(1) + (small ? "M" : " MiB");
}
} else {
- return (size / 1024).toFixed(1) + " KiB";
+ return (size / 1024).toFixed(1) + (small ? "K" : " KiB");
}
} else {
return size + " B";
@@ -193,12 +209,6 @@
document.getElementById("file-name").innerText = file.name;
document.getElementById("file-size").innerText = formatSize(file.size);
- if (file.size > <?= getLimits()["size"] ?>) {
- alert("This file is too large. The maximum allowed size with your plan is <?= sizeToString(getLimits()["size"]) ?>. Please try a smaller file.");
- window.file = null;
- return;
- }
-
document.title = window.oldTitle = file.name + " | " + document.title;
document.getElementById("main").style.display = "none";
@@ -218,10 +228,14 @@
let canResume = false;
let allowCrash = true;
let totalReceived = 0;
- let blob = new Blob();
window.fileSize = 0;
window.pos = 0;
+ window.availableUpstreamServers = [];
+
+ function generateToken() {
+ return [...crypto.getRandomValues(new Uint8Array(128))].map(x => x.toString(16).padStart(2, '0')).join("");
+ }
setInterval(() => {
if (connected) {
@@ -231,49 +245,163 @@
}
}, 2000);
- function connect() {
- connecting = true;
- window.ws = new WebSocket("wss://bridleshare.equestria.dev/ws");
- ws.onopen = (event) => {
- console.log(event);
+ setInterval(() => {
+ for (let server of availableUpstreamServers) {
+ server.send(JSON.stringify({
+ type: "keepalive"
+ }));
}
+ }, 5000);
+
+ function sendEncrypted(message, cb) {
+ return new Promise((res) => {
+ let waiter = setInterval(async () => {
+ if (keyChain.chain) {
+ clearInterval(waiter);
+ ws.send(JSON.stringify({
+ type: "encrypted",
+ message: await BridleshareCrypt.encrypt(JSON.stringify(message), keyChain.chain)
+ }), cb ?? function () {});
+ res();
+ }
+ });
+ });
+ }
- async function sendEncrypted(message, cb) {
- ws.send(JSON.stringify({
- type: "encrypted",
- message: await BridleshareCrypt.encrypt(JSON.stringify(message), keyChain.chain)
- }), cb ?? function () {});
+ function handleEncryptedData(data, upstream) {
+ let waiter = setInterval(() => {
+ if (keyChain.chain) {
+ clearInterval(waiter);
+ BridleshareCrypt.decryptBuffer(data.message, keyChain.chain).then((ab) => {
+ window.receivedChunks[data.chunk] = ab;
+
+ totalReceived = totalReceived + ab.byteLength;
+ let total = window.fileSize;
+ let percentage = (totalReceived / total) * 100;
+
+ document.getElementById("receive-progress").className = "progress-bar bg-primary";
+ document.getElementById("receive-error").style.display = document.getElementById("share-error").style.display = "none";
+ document.getElementById("receive-progress").style.width = percentage + "%";
+ document.getElementById("rfile-status").innerText = generateStatus(false, totalReceived, total);
+
+ if (connected) {
+ pos++;
+
+ sendEncrypted({
+ type: "next",
+ chunks: [
+ {
+ chunk: pos,
+ upstream: upstream
+ }
+ ]
+ });
+ }
+ });
+ }
+ });
+ }
+
+ let lastStatusUpdate = 0;
+ let lastStatus;
+ let lastDone = 0;
+ let currentSpeed = 0;
+ let speeds = [];
+ let newDone;
+
+ setInterval(() => {
+ if (newDone) {
+ speeds.unshift(newDone - lastDone);
+ speeds = speeds.slice(0, 15);
+ currentSpeed = speeds.reduce((a, b) => a + b) / speeds.length;
+ lastDone = newDone;
}
+ }, 1000);
- function generateStatus(up, done, total) {
- let eta = (1 / currentSpeed) * (total - done);
- let etaString;
+ function generateStatus(up, done, total, small) {
+ newDone = done;
+ let servers = availableUpstreamServers.filter(i => i._token);
+ if (new Date().getTime() - lastStatusUpdate < 1000) return lastStatus;
- if (isFinite(eta) && !isNaN(eta)) {
- if (eta > 3600) {
- etaString = Math.round(eta / 3600) + " hour" + (Math.round(eta / 3600) > 1 ? "s" : "");
- } else if (eta > 60) {
- etaString = Math.round(eta / 60) + " minute" + (Math.round(eta / 60) > 1 ? "s" : "");
- } else {
- etaString = Math.round(eta) + " second" + (Math.round(eta) > 1 ? "s" : "");
- }
+ let eta = (1 / currentSpeed) * (total - done);
+ let etaString;
+
+ if (isFinite(eta) && !isNaN(eta)) {
+ if (eta > 3600) {
+ etaString = Math.round(eta / 3600) + (small ? "hr" : " hour") + (Math.round(eta / 3600) > 1 ? "s" : "");
+ } else if (eta > 60) {
+ etaString = Math.round(eta / 60) + (small ? "mn" : " minute") + (Math.round(eta / 60) > 1 ? "s" : "");
+ } else {
+ etaString = Math.round(eta) + (small ? "sec" : " second") + (Math.round(eta) > 1 ? "s" : "");
}
+ }
+
+ let info = (up ? "↑ " : "↓ ") + formatSize(currentSpeed) + "/s · " + formatSize(done, small) + "/" + formatSize(total, small) + (etaString ? (" · " + etaString) : "");
- let info = (up ? "↑ " : "↓ ") + formatSize(currentSpeed) + "/s · " + formatSize(done) + "/" + formatSize(total) + (etaString ? (" · " + etaString) : "");
+ if (!small && servers.length > 0) {
+ info = (up ? "↑ " : "↓ ") + formatSize(currentSpeed) + "/s (" + servers.length + " connection" + (servers.length > 1 ? "s" : "") + ") · " + formatSize(done, small) + "/" + formatSize(total, small) + (etaString ? (" · " + etaString) : "");
+ } else if (servers.length > 0) {
+ info = (up ? "↑" : "↓") + formatSize(currentSpeed, small) + "/s(" + servers.length + ") · " + formatSize(done, small) + "/" + formatSize(total, small) + (etaString ? (" · " + etaString) : "");
+ } else if (small) {
+ info = (up ? "↑" : "↓") + formatSize(currentSpeed, small) + "/s · " + formatSize(done, small) + "/" + formatSize(total, small) + (etaString ? (" · " + etaString) : "");
+ }
+ if (!small) {
if (window.infoInTitle) {
- document.title = info;
+ document.title = generateStatus(up, done, total, true);
} else {
document.title = window.oldTitle;
}
+ }
+
+ lastStatusUpdate = new Date().getTime();
+ lastStatus = info;
+ return info;
+ }
+
+ function connect() {
+ connecting = true;
+ window.ws = new WebSocket("wss://bridleshare.equestria.dev/ws");
+
+ window.ws_data1 = new WebSocket("wss://bridleshare.equestria.dev:19202");
+ window.ws_data2 = new WebSocket("wss://bridleshare.equestria.dev:19203");
+ window.ws_data3 = new WebSocket("wss://bridleshare.equestria.dev:19204");
+ window.ws_data4 = new WebSocket("wss://bridleshare.equestria.dev:19205");
+ window.ws_data5 = new WebSocket("wss://bridleshare.equestria.dev:19206");
+
+ ws.onopen = (event) => {
+ console.log(event);
+ }
+
+ ws_data1.onopen = ws_data2.onopen = ws_data3.onopen = ws_data4.onopen = ws_data5.onopen = (event) => {
+ console.log(event);
+ availableUpstreamServers.push(event.target);
- return info;
+ if (!window.fileID) {
+ event.target._token = generateToken();
+ event.target.send(JSON.stringify({
+ type: "token",
+ token: event.target._token
+ }));
+ }
}
- let lastChunk;
+ ws_data1.onmessage = ws_data2.onmessage = ws_data3.onmessage = ws_data4.onmessage = ws_data5.onmessage = (event) => {
+ try {
+ let data = JSON.parse(event.data);
+ if (data.type !== "encrypted" && data.type !== "encryptedData") console.log(data);
+
+ if (data.type === "encryptedData") {
+ handleEncryptedData(data, event.target.url);
+ }
+ } catch (e) {
+ console.error(e);
+ console.log(event);
+ }
+ }
- async function nextChunk(pos) {
+ async function nextChunk(pos, upstream) {
let chunkSize = maxSpeed / 2;
let chunks = Math.ceil(file.size / chunkSize);
@@ -293,10 +421,19 @@
let ab = await readArrayBuffer(file, pos * chunkSize, chunkSize);
if (ab.byteLength > 0) {
- ws.send(JSON.stringify({
- type: "encryptedData",
- message: await BridleshareCrypt.encryptBuffer(ab, keyChain.chain)
- }));
+ if (availableUpstreamServers.filter(i => i.url === upstream).length > 0) {
+ availableUpstreamServers.filter(i => i.url === upstream)[0].send(JSON.stringify({
+ type: "encryptedData",
+ chunk: pos,
+ message: await BridleshareCrypt.encryptBuffer(ab, keyChain.chain)
+ }));
+ } else {
+ ws.send(JSON.stringify({
+ type: "encryptedData",
+ chunk: pos,
+ message: await BridleshareCrypt.encryptBuffer(ab, keyChain.chain)
+ }));
+ }
} else {
sendEncrypted({
type: "end"
@@ -311,13 +448,6 @@
if (done > total) done = total;
let percentage = (done / total) * 100;
- if (lastChunk) {
- currentSpeed = ab.byteLength / ((new Date().getTime() / 1000) - lastChunk);
- lastChunk = new Date().getTime() / 1000;
- } else {
- lastChunk = new Date().getTime() / 1000;
- }
-
document.getElementById("share-progress").className = "progress-bar bg-primary";
document.getElementById("receive-error").style.display = document.getElementById("share-error").style.display = "none";
document.getElementById("share-progress").style.width = percentage + "%";
@@ -330,84 +460,171 @@
if (data.type !== "encrypted" && data.type !== "encryptedData") console.log(data);
if (data.type === "encryptedData") {
+ handleEncryptedData(data, null);
+ }
+
+ if (data.type === "encrypted") {
let waiter = setInterval(() => {
if (keyChain.chain) {
clearInterval(waiter);
- BridleshareCrypt.decryptBuffer(data.message, keyChain.chain).then((ab) => {
- blob = new Blob([blob, ab], {
- type: window.fileType
- });
-
- totalReceived = totalReceived + ab.byteLength;
- let total = window.fileSize;
- let percentage = (totalReceived / total) * 100;
-
- if (lastChunk) {
- currentSpeed = ab.byteLength / ((new Date().getTime() / 1000) - lastChunk);
- lastChunk = new Date().getTime() / 1000;
- } else {
- lastChunk = new Date().getTime() / 1000;
- }
+ BridleshareCrypt.decrypt(data.message, keyChain.chain).then((msg) => {
+ let data = JSON.parse(msg);
+ console.log(data);
- document.getElementById("receive-progress").className = "progress-bar bg-primary";
- document.getElementById("receive-error").style.display = document.getElementById("share-error").style.display = "none";
- document.getElementById("receive-progress").style.width = percentage + "%";
- document.getElementById("rfile-status").innerText = generateStatus(false, totalReceived, total);
-
- if (connected) {
- pos++;
+ if (data.type === "leave") {
+ forceQuit = true;
+ location.href = "/";
+ }
- sendEncrypted({
- type: "next",
- chunk: pos
+ if (data.type === "end") {
+ let waiter = setInterval(() => {
+ if (receivedChunks.filter(i => i === null).length === 0) {
+ clearInterval(waiter);
+ document.getElementById("file-status").innerText = document.getElementById("rfile-status").innerText = "Transfer completed successfully";
+ document.getElementById("receive-progress").className = document.getElementById("share-progress").className = "progress-bar bg-success";
+
+ if (window.fileID) {
+ sendEncrypted({
+ type: "end"
+ });
+
+ let blob = new Blob(receivedChunks, {
+ type: window.fileType
+ });
+
+ if (window.downloadStarted) return;
+
+ let dl = document.createElement("a");
+ dl.href = URL.createObjectURL(blob);
+ dl.download = document.getElementById("rfile-name").innerText.trim();
+ dl.click();
+ window.downloadStarted = true;
+ }
+ }
});
}
- });
- }
- });
- }
- if (data.type === "encrypted") {
- BridleshareCrypt.decrypt(data.message, keyChain.chain).then((msg) => {
- let data = JSON.parse(msg);
- console.log(data);
-
- if (data.type === "end") {
- document.getElementById("file-status").innerText = document.getElementById("rfile-status").innerText = "Transfer completed successfully";
- document.getElementById("receive-progress").className = document.getElementById("share-progress").className = "progress-bar bg-success";
-
- if (window.fileID) {
- sendEncrypted({
- type: "end"
- });
-
- console.log(blob);
-
- let dl = document.createElement("a");
- dl.href = URL.createObjectURL(blob);
- dl.download = document.getElementById("rfile-name").innerText.trim();
- dl.click();
- }
- }
+ if (data.type === "next") {
+ for (let chunk of data.chunks) {
+ nextChunk(chunk.chunk, chunk.upstream ?? null);
+ }
+ }
- if (data.type === "next") {
- nextChunk(data.chunk);
- }
+ if (data.type === "upstreams") {
+ let completed = 0;
+ let requested = data.servers.filter(i => i.url === ws_data1.url || i.url === ws_data2.url || i.url === ws_data3.url || i.url === ws_data4.url || i.url === ws_data5.url).length;
+
+ for (let server of data.servers) {
+ if (ws_data1.url === server['url']) {
+ let waiter = setInterval(() => {
+ if (ws_data1.readyState === 1) {
+ clearInterval(waiter);
+ ws_data1._token = server['token'];
+ ws_data1.send(JSON.stringify({
+ type: "token",
+ token: server['token']
+ }));
+ completed++;
+ }
+ });
+ } else if (ws_data2.url === server['url']) {
+ let waiter = setInterval(() => {
+ if (ws_data2.readyState === 1) {
+ clearInterval(waiter);
+ ws_data2._token = server['token'];
+ ws_data2.send(JSON.stringify({
+ type: "token",
+ token: server['token']
+ }));
+ completed++;
+ }
+ });
+ } else if (ws_data3.url === server['url']) {
+ let waiter = setInterval(() => {
+ if (ws_data3.readyState === 1) {
+ clearInterval(waiter);
+ ws_data3._token = server['token'];
+ ws_data3.send(JSON.stringify({
+ type: "token",
+ token: server['token']
+ }));
+ completed++;
+ }
+ });
+ } else if (ws_data4.url === server['url']) {
+ let waiter = setInterval(() => {
+ if (ws_data4.readyState === 1) {
+ clearInterval(waiter);
+ ws_data4._token = server['token'];
+ ws_data4.send(JSON.stringify({
+ type: "token",
+ token: server['token']
+ }));
+ completed++;
+ }
+ });
+ } else if (ws_data5.url === server['url']) {
+ let waiter = setInterval(() => {
+ if (ws_data5.readyState === 1) {
+ clearInterval(waiter);
+ ws_data5._token = server['token'];
+ ws_data5.send(JSON.stringify({
+ type: "token",
+ token: server['token']
+ }));
+ completed++;
+ }
+ });
+ }
+ }
+
+ if (availableUpstreamServers.length > 0) {
+ let waiter = setInterval(() => {
+ if (completed >= requested) {
+ clearInterval(waiter);
+ for (let server of availableUpstreamServers) {
+ sendEncrypted({
+ type: "next",
+ chunks: [
+ {
+ chunk: pos,
+ upstream: server.url
+ }
+ ]
+ });
+
+ pos++;
+ }
+
+ pos--;
+ }
+ });
+ } else {
+ sendEncrypted({
+ type: "next",
+ chunks: [
+ {
+ chunk: pos,
+ upstream: null
+ }
+ ]
+ });
+ }
+ }
- if (data.type === "metadata") {
- document.getElementById("receive-load").style.display = "none";
- document.getElementById("receive-info").style.display = "";
- document.getElementById("rfile-name").innerText = data.name;
- document.getElementById("rfile-size").innerText = formatSize(data.size);
+ if (data.type === "metadata") {
+ document.getElementById("receive-load").style.display = "none";
+ document.getElementById("receive-info").style.display = "";
+ document.getElementById("rfile-name").innerText = data.name;
+ document.getElementById("rfile-size").innerText = formatSize(data.size);
- window.fileType = data.mime;
- window.fileSize = data.size;
+ window.fileType = data.mime;
+ window.fileSize = data.size;
- document.getElementById("file-status").innerText = document.getElementById("rfile-status").innerText = "Waiting for transfer to start...";
+ window.receivedChunks = Array(data.chunks).fill(null);
- sendEncrypted({
- type: "next",
- chunk: pos
+ document.getElementById("file-status").innerText = document.getElementById("rfile-status").innerText = "Waiting for transfer to start...";
+ }
});
}
});
@@ -431,7 +648,22 @@
type: "metadata",
name: window.file.name,
size: window.file.size,
- mime: window.file.type
+ mime: window.file.type,
+ chunks: Math.ceil(window.file.size / (maxSpeed / 2))
+ });
+
+ window.receivedChunks = Array(Math.ceil(window.file.size / (maxSpeed / 2))).fill(null);
+
+ document.getElementById("file-status").innerText = document.getElementById("rfile-status").innerText = "Sending upstream servers information...";
+
+ sendEncrypted({
+ type: "upstreams",
+ servers: availableUpstreamServers.filter(i => i._token).map(i => {
+ return {
+ url: i.url,
+ token: i._token
+ }
+ })
});
document.getElementById("file-status").innerText = document.getElementById("rfile-status").innerText = "Waiting for receiver to be ready...";
@@ -449,6 +681,7 @@
if (data.type === "init") {
document.getElementById("link").innerText = document.getElementById("link").href = document.getElementById("link").innerText.replace("{code}", data.code);
+ new QRCode(document.getElementById("qr"), document.getElementById("link").href);
if (!window.currentID) window.currentID = data.code;
BridleshareCrypt.generateKey().then((key) => {
@@ -463,6 +696,7 @@
if (window.fileID) {
document.getElementById("file-status").innerText = document.getElementById("rfile-status").innerText = "Connecting to sender...";
+
canResume = true;
document.getElementById("receive").style.display = "";
} else {
@@ -485,13 +719,36 @@
document.getElementById("receive-progress").className = document.getElementById("share-progress").className = "progress-bar bg-danger";
document.getElementById("receive-error").style.display = document.getElementById("share-error").style.display = "";
+ try { ws_data1.close(); } catch (e) {}
+ try { ws_data2.close(); } catch (e) {}
+ try { ws_data3.close(); } catch (e) {}
+ try { ws_data4.close(); } catch (e) {}
+ try { ws_data5.close(); } catch (e) {}
+
setTimeout(() => {
connect();
}, 1000);
}
}
- ws.onerror = (event) => {
+ ws_data1.onclose = ws_data2.onclose = ws_data3.onclose = ws_data4.onclose = ws_data5.onclose = (event) => {
+ console.log(event);
+
+ if (availableUpstreamServers.includes(event.target)) {
+ availableUpstreamServers = availableUpstreamServers.filter(i => i !== event.target);
+ }
+
+ if (canResume) {
+ try { ws_data1.close(); } catch (e) {}
+ try { ws_data2.close(); } catch (e) {}
+ try { ws_data3.close(); } catch (e) {}
+ try { ws_data4.close(); } catch (e) {}
+ try { ws_data5.close(); } catch (e) {}
+ try { ws.close(); } catch (e) {}
+ }
+ }
+
+ ws.onerror = ws_data1.onerror = ws_data2.onerror = ws_data3.onerror = ws_data4.onerror = ws_data5.onerror = (event) => {
console.log(event);
}
}
diff --git a/plans/index.php b/plans/index.php
index d106ad0..b68768b 100644
--- a/plans/index.php
+++ b/plans/index.php
@@ -2,7 +2,7 @@
<style>
@font-face {
- src: url("https://git.equestria.dev/equestria.dev/maretimesans/src/branch/mane/export.otf");
+ src: url("https://git.equestria.dev/equestria.dev/maretimesans/raw/branch/mane/export.otf");
font-family: "MLPMaretimeSans";
font-style: normal;
font-weight: normal;
@@ -26,22 +26,22 @@
<b>Upload speed</b>
</div>
<div class="progress" style="margin-bottom: 5px;">
- <div class="progress-bar bg-primary" style="width: 1.95%;"></div>
+ <div class="progress-bar bg-primary" style="width: 5%;"></div>
</div>
<div>
- 100 KiB/s
+ 500 KiB/s
</div>
</div>
- <div style="margin: 20px 0;">
+ <div style="margin: 10px 0;">
<div style="margin-bottom: 5px;">
- <b>Storage space</b>
+ <b>Parallel connections</b>
</div>
<div class="progress" style="margin-bottom: 5px;">
- <div class="progress-bar bg-primary" style="width: 2.44%;"></div>
+ <div class="progress-bar bg-primary" style="width: 20%;"></div>
</div>
<div>
- 50 MiB
+ 1 connection
</div>
</div>
</div>
@@ -63,22 +63,22 @@
<b>Upload speed</b>
</div>
<div class="progress" style="margin-bottom: 5px;">
- <div class="progress-bar bg-primary" style="width: 9.77%;"></div>
+ <div class="progress-bar bg-primary" style="width: 10%;"></div>
</div>
<div>
- 500 KiB/s
+ 1 MiB/s
</div>
</div>
- <div style="margin: 20px 0;">
+ <div style="margin: 10px 0;">
<div style="margin-bottom: 5px;">
- <b>Storage space</b>
+ <b>Parallel connections</b>
</div>
<div class="progress" style="margin-bottom: 5px;">
- <div class="progress-bar bg-primary" style="width: 4.88%;"></div>
+ <div class="progress-bar bg-primary" style="width: 40%;"></div>
</div>
<div>
- 100 MiB
+ 2 connections at 500 KiB/s each
</div>
</div>
</div>
@@ -100,22 +100,22 @@
<b>Upload speed</b>
</div>
<div class="progress" style="margin-bottom: 5px;">
- <div class="progress-bar bg-primary" style="width: 20%;"></div>
+ <div class="progress-bar bg-primary" style="width: 30%;"></div>
</div>
<div>
- 1 MiB/s
+ 3 MiB/s
</div>
</div>
- <div style="margin: 20px 0;">
+ <div style="margin: 10px 0;">
<div style="margin-bottom: 5px;">
- <b>Storage space</b>
+ <b>Parallel connections</b>
</div>
<div class="progress" style="margin-bottom: 5px;">
- <div class="progress-bar bg-primary" style="width: 50%;"></div>
+ <div class="progress-bar bg-primary" style="width: 60%;"></div>
</div>
<div>
- 1 GiB
+ 3 connections at 1 MiB/s each
</div>
</div>
</div>
@@ -140,26 +140,26 @@
<div class="progress-bar bg-primary" style="width: 100%;"></div>
</div>
<div>
- 5 MiB/s
+ 10 MiB/s
</div>
</div>
- <div style="margin: 20px 0;">
+ <div style="margin: 10px 0;">
<div style="margin-bottom: 5px;">
- <b>Storage space</b>
+ <b>Parallel connections</b>
</div>
<div class="progress" style="margin-bottom: 5px;">
<div class="progress-bar bg-primary" style="width: 100%;"></div>
</div>
<div>
- 2 GiB or more
+ 5 connections at 2 MiB/s each
</div>
</div>
</div>
</div>
</div>
- <div class="small text-muted" style="margin-top: 20px;">The actual upload speed depends on server load and will almost never be the announced speed. These limitations only apply to proxied traffic and will not apply to peer-to-peer traffic once it is made available globally.</div>
+ <div class="small text-muted" style="margin-top: 20px;">The actual upload speed depends on various criteria (server load, available parallel connections, network speed, ...) and will almost never be the announced speed. Parallel connections only work on supported browsers, and might not be available entirely or at all at any point in time.</div>
</div>
<?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/footer.php"; ?> \ No newline at end of file