diff --git a/media/img/client-v2-download-lib.png b/media/img/client-v2-download-lib.png new file mode 100644 index 0000000000..144cd558a3 Binary files /dev/null and b/media/img/client-v2-download-lib.png differ diff --git a/media/js/CryptoJS/components/lib-typedarrays-min.js b/media/js/CryptoJS/components/lib-typedarrays-min.js new file mode 100644 index 0000000000..7eee4b2ecb --- /dev/null +++ b/media/js/CryptoJS/components/lib-typedarrays-min.js @@ -0,0 +1,8 @@ +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){if("function"==typeof ArrayBuffer){var b=CryptoJS.lib.WordArray,e=b.init;(b.init=function(a){a instanceof ArrayBuffer&&(a=new Uint8Array(a));if(a instanceof Int8Array||a instanceof Uint8ClampedArray||a instanceof Int16Array||a instanceof Uint16Array||a instanceof Int32Array||a instanceof Uint32Array||a instanceof Float32Array||a instanceof Float64Array)a=new Uint8Array(a.buffer,a.byteOffset,a.byteLength);if(a instanceof Uint8Array){for(var b=a.byteLength,d=[],c=0;c>>2]|=a[c]<< +24-8*(c%4);e.call(this,d,b)}else e.apply(this,arguments)}).prototype=b}})(); diff --git a/media/js/CryptoJS/rollups/aes.js b/media/js/CryptoJS/rollups/aes.js new file mode 100644 index 0000000000..827503cbd6 --- /dev/null +++ b/media/js/CryptoJS/rollups/aes.js @@ -0,0 +1,35 @@ +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +var CryptoJS=CryptoJS||function(u,p){var d={},l=d.lib={},s=function(){},t=l.Base={extend:function(a){s.prototype=this;var c=new s;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},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)}}, +r=l.WordArray=t.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=p?c:4*a.length},toString:function(a){return(a||v).stringify(this)},concat:function(a){var c=this.words,e=a.words,j=this.sigBytes;a=a.sigBytes;this.clamp();if(j%4)for(var k=0;k>>2]|=(e[k>>>2]>>>24-8*(k%4)&255)<<24-8*((j+k)%4);else if(65535>>2]=e[k>>>2];else c.push.apply(c,e);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<< +32-8*(c%4);a.length=u.ceil(c/4)},clone:function(){var a=t.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],e=0;e>>2]>>>24-8*(j%4)&255;e.push((k>>>4).toString(16));e.push((k&15).toString(16))}return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j>>3]|=parseInt(a.substr(j, +2),16)<<24-4*(j%8);return new r.init(e,c/2)}},b=w.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j>>2]>>>24-8*(j%4)&255));return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j>>2]|=(a.charCodeAt(j)&255)<<24-8*(j%4);return new r.init(e,c)}},x=w.Utf8={stringify:function(a){try{return decodeURIComponent(escape(b.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return b.parse(unescape(encodeURIComponent(a)))}}, +q=l.BufferedBlockAlgorithm=t.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=x.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,e=c.words,j=c.sigBytes,k=this.blockSize,b=j/(4*k),b=a?u.ceil(b):u.max((b|0)-this._minBufferSize,0);a=b*k;j=u.min(4*a,j);if(a){for(var q=0;q>>2]>>>24-8*(r%4)&255)<<16|(l[r+1>>>2]>>>24-8*((r+1)%4)&255)<<8|l[r+2>>>2]>>>24-8*((r+2)%4)&255,v=0;4>v&&r+0.75*v>>6*(3-v)&63));if(l=t.charAt(64))for(;d.length%4;)d.push(l);return d.join("")},parse:function(d){var l=d.length,s=this._map,t=s.charAt(64);t&&(t=d.indexOf(t),-1!=t&&(l=t));for(var t=[],r=0,w=0;w< +l;w++)if(w%4){var v=s.indexOf(d.charAt(w-1))<<2*(w%4),b=s.indexOf(d.charAt(w))>>>6-2*(w%4);t[r>>>2]|=(v|b)<<24-8*(r%4);r++}return p.create(t,r)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})(); +(function(u){function p(b,n,a,c,e,j,k){b=b+(n&a|~n&c)+e+k;return(b<>>32-j)+n}function d(b,n,a,c,e,j,k){b=b+(n&c|a&~c)+e+k;return(b<>>32-j)+n}function l(b,n,a,c,e,j,k){b=b+(n^a^c)+e+k;return(b<>>32-j)+n}function s(b,n,a,c,e,j,k){b=b+(a^(n|~c))+e+k;return(b<>>32-j)+n}for(var t=CryptoJS,r=t.lib,w=r.WordArray,v=r.Hasher,r=t.algo,b=[],x=0;64>x;x++)b[x]=4294967296*u.abs(u.sin(x+1))|0;r=r.MD5=v.extend({_doReset:function(){this._hash=new w.init([1732584193,4023233417,2562383102,271733878])}, +_doProcessBlock:function(q,n){for(var a=0;16>a;a++){var c=n+a,e=q[c];q[c]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360}var a=this._hash.words,c=q[n+0],e=q[n+1],j=q[n+2],k=q[n+3],z=q[n+4],r=q[n+5],t=q[n+6],w=q[n+7],v=q[n+8],A=q[n+9],B=q[n+10],C=q[n+11],u=q[n+12],D=q[n+13],E=q[n+14],x=q[n+15],f=a[0],m=a[1],g=a[2],h=a[3],f=p(f,m,g,h,c,7,b[0]),h=p(h,f,m,g,e,12,b[1]),g=p(g,h,f,m,j,17,b[2]),m=p(m,g,h,f,k,22,b[3]),f=p(f,m,g,h,z,7,b[4]),h=p(h,f,m,g,r,12,b[5]),g=p(g,h,f,m,t,17,b[6]),m=p(m,g,h,f,w,22,b[7]), +f=p(f,m,g,h,v,7,b[8]),h=p(h,f,m,g,A,12,b[9]),g=p(g,h,f,m,B,17,b[10]),m=p(m,g,h,f,C,22,b[11]),f=p(f,m,g,h,u,7,b[12]),h=p(h,f,m,g,D,12,b[13]),g=p(g,h,f,m,E,17,b[14]),m=p(m,g,h,f,x,22,b[15]),f=d(f,m,g,h,e,5,b[16]),h=d(h,f,m,g,t,9,b[17]),g=d(g,h,f,m,C,14,b[18]),m=d(m,g,h,f,c,20,b[19]),f=d(f,m,g,h,r,5,b[20]),h=d(h,f,m,g,B,9,b[21]),g=d(g,h,f,m,x,14,b[22]),m=d(m,g,h,f,z,20,b[23]),f=d(f,m,g,h,A,5,b[24]),h=d(h,f,m,g,E,9,b[25]),g=d(g,h,f,m,k,14,b[26]),m=d(m,g,h,f,v,20,b[27]),f=d(f,m,g,h,D,5,b[28]),h=d(h,f, +m,g,j,9,b[29]),g=d(g,h,f,m,w,14,b[30]),m=d(m,g,h,f,u,20,b[31]),f=l(f,m,g,h,r,4,b[32]),h=l(h,f,m,g,v,11,b[33]),g=l(g,h,f,m,C,16,b[34]),m=l(m,g,h,f,E,23,b[35]),f=l(f,m,g,h,e,4,b[36]),h=l(h,f,m,g,z,11,b[37]),g=l(g,h,f,m,w,16,b[38]),m=l(m,g,h,f,B,23,b[39]),f=l(f,m,g,h,D,4,b[40]),h=l(h,f,m,g,c,11,b[41]),g=l(g,h,f,m,k,16,b[42]),m=l(m,g,h,f,t,23,b[43]),f=l(f,m,g,h,A,4,b[44]),h=l(h,f,m,g,u,11,b[45]),g=l(g,h,f,m,x,16,b[46]),m=l(m,g,h,f,j,23,b[47]),f=s(f,m,g,h,c,6,b[48]),h=s(h,f,m,g,w,10,b[49]),g=s(g,h,f,m, +E,15,b[50]),m=s(m,g,h,f,r,21,b[51]),f=s(f,m,g,h,u,6,b[52]),h=s(h,f,m,g,k,10,b[53]),g=s(g,h,f,m,B,15,b[54]),m=s(m,g,h,f,e,21,b[55]),f=s(f,m,g,h,v,6,b[56]),h=s(h,f,m,g,x,10,b[57]),g=s(g,h,f,m,t,15,b[58]),m=s(m,g,h,f,D,21,b[59]),f=s(f,m,g,h,z,6,b[60]),h=s(h,f,m,g,C,10,b[61]),g=s(g,h,f,m,j,15,b[62]),m=s(m,g,h,f,A,21,b[63]);a[0]=a[0]+f|0;a[1]=a[1]+m|0;a[2]=a[2]+g|0;a[3]=a[3]+h|0},_doFinalize:function(){var b=this._data,n=b.words,a=8*this._nDataBytes,c=8*b.sigBytes;n[c>>>5]|=128<<24-c%32;var e=u.floor(a/ +4294967296);n[(c+64>>>9<<4)+15]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360;n[(c+64>>>9<<4)+14]=(a<<8|a>>>24)&16711935|(a<<24|a>>>8)&4278255360;b.sigBytes=4*(n.length+1);this._process();b=this._hash;n=b.words;for(a=0;4>a;a++)c=n[a],n[a]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360;return b},clone:function(){var b=v.clone.call(this);b._hash=this._hash.clone();return b}});t.MD5=v._createHelper(r);t.HmacMD5=v._createHmacHelper(r)})(Math); +(function(){var u=CryptoJS,p=u.lib,d=p.Base,l=p.WordArray,p=u.algo,s=p.EvpKDF=d.extend({cfg:d.extend({keySize:4,hasher:p.MD5,iterations:1}),init:function(d){this.cfg=this.cfg.extend(d)},compute:function(d,r){for(var p=this.cfg,s=p.hasher.create(),b=l.create(),u=b.words,q=p.keySize,p=p.iterations;u.length>>2]&255}};d.BlockCipher=v.extend({cfg:v.cfg.extend({mode:b,padding:q}),reset:function(){v.reset.call(this);var a=this.cfg,b=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var c=a.createEncryptor;else c=a.createDecryptor,this._minBufferSize=1;this._mode=c.call(a, +this,b&&b.words)},_doProcessBlock:function(a,b){this._mode.processBlock(a,b)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var b=this._process(!0)}else b=this._process(!0),a.unpad(b);return b},blockSize:4});var n=d.CipherParams=l.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),b=(p.format={}).OpenSSL={stringify:function(a){var b=a.ciphertext;a=a.salt;return(a?s.create([1398893684, +1701076831]).concat(a).concat(b):b).toString(r)},parse:function(a){a=r.parse(a);var b=a.words;if(1398893684==b[0]&&1701076831==b[1]){var c=s.create(b.slice(2,4));b.splice(0,4);a.sigBytes-=16}return n.create({ciphertext:a,salt:c})}},a=d.SerializableCipher=l.extend({cfg:l.extend({format:b}),encrypt:function(a,b,c,d){d=this.cfg.extend(d);var l=a.createEncryptor(c,d);b=l.finalize(b);l=l.cfg;return n.create({ciphertext:b,key:c,iv:l.iv,algorithm:a,mode:l.mode,padding:l.padding,blockSize:a.blockSize,formatter:d.format})}, +decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);return a.createDecryptor(c,d).finalize(b.ciphertext)},_parse:function(a,b){return"string"==typeof a?b.parse(a,this):a}}),p=(p.kdf={}).OpenSSL={execute:function(a,b,c,d){d||(d=s.random(8));a=w.create({keySize:b+c}).compute(a,d);c=s.create(a.words.slice(b),4*c);a.sigBytes=4*b;return n.create({key:a,iv:c,salt:d})}},c=d.PasswordBasedCipher=a.extend({cfg:a.cfg.extend({kdf:p}),encrypt:function(b,c,d,l){l=this.cfg.extend(l);d=l.kdf.execute(d, +b.keySize,b.ivSize);l.iv=d.iv;b=a.encrypt.call(this,b,c,d.key,l);b.mixIn(d);return b},decrypt:function(b,c,d,l){l=this.cfg.extend(l);c=this._parse(c,l.format);d=l.kdf.execute(d,b.keySize,b.ivSize,c.salt);l.iv=d.iv;return a.decrypt.call(this,b,c,d.key,l)}})}(); +(function(){for(var u=CryptoJS,p=u.lib.BlockCipher,d=u.algo,l=[],s=[],t=[],r=[],w=[],v=[],b=[],x=[],q=[],n=[],a=[],c=0;256>c;c++)a[c]=128>c?c<<1:c<<1^283;for(var e=0,j=0,c=0;256>c;c++){var k=j^j<<1^j<<2^j<<3^j<<4,k=k>>>8^k&255^99;l[e]=k;s[k]=e;var z=a[e],F=a[z],G=a[F],y=257*a[k]^16843008*k;t[e]=y<<24|y>>>8;r[e]=y<<16|y>>>16;w[e]=y<<8|y>>>24;v[e]=y;y=16843009*G^65537*F^257*z^16843008*e;b[k]=y<<24|y>>>8;x[k]=y<<16|y>>>16;q[k]=y<<8|y>>>24;n[k]=y;e?(e=z^a[a[a[G^z]]],j^=a[a[j]]):e=j=1}var H=[0,1,2,4,8, +16,32,64,128,27,54],d=d.AES=p.extend({_doReset:function(){for(var a=this._key,c=a.words,d=a.sigBytes/4,a=4*((this._nRounds=d+6)+1),e=this._keySchedule=[],j=0;j>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255]):(k=k<<8|k>>>24,k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255],k^=H[j/d|0]<<24);e[j]=e[j-d]^k}c=this._invKeySchedule=[];for(d=0;dd||4>=j?k:b[l[k>>>24]]^x[l[k>>>16&255]]^q[l[k>>> +8&255]]^n[l[k&255]]},encryptBlock:function(a,b){this._doCryptBlock(a,b,this._keySchedule,t,r,w,v,l)},decryptBlock:function(a,c){var d=a[c+1];a[c+1]=a[c+3];a[c+3]=d;this._doCryptBlock(a,c,this._invKeySchedule,b,x,q,n,s);d=a[c+1];a[c+1]=a[c+3];a[c+3]=d},_doCryptBlock:function(a,b,c,d,e,j,l,f){for(var m=this._nRounds,g=a[b]^c[0],h=a[b+1]^c[1],k=a[b+2]^c[2],n=a[b+3]^c[3],p=4,r=1;r>>24]^e[h>>>16&255]^j[k>>>8&255]^l[n&255]^c[p++],s=d[h>>>24]^e[k>>>16&255]^j[n>>>8&255]^l[g&255]^c[p++],t= +d[k>>>24]^e[n>>>16&255]^j[g>>>8&255]^l[h&255]^c[p++],n=d[n>>>24]^e[g>>>16&255]^j[h>>>8&255]^l[k&255]^c[p++],g=q,h=s,k=t;q=(f[g>>>24]<<24|f[h>>>16&255]<<16|f[k>>>8&255]<<8|f[n&255])^c[p++];s=(f[h>>>24]<<24|f[k>>>16&255]<<16|f[n>>>8&255]<<8|f[g&255])^c[p++];t=(f[k>>>24]<<24|f[n>>>16&255]<<16|f[g>>>8&255]<<8|f[h&255])^c[p++];n=(f[n>>>24]<<24|f[g>>>16&255]<<16|f[h>>>8&255]<<8|f[k&255])^c[p++];a[b]=q;a[b+1]=s;a[b+2]=t;a[b+3]=n},keySize:8});u.AES=p._createHelper(d)})(); diff --git a/media/js/CryptoJS/rollups/sha1.js b/media/js/CryptoJS/rollups/sha1.js new file mode 100644 index 0000000000..d0d589f715 --- /dev/null +++ b/media/js/CryptoJS/rollups/sha1.js @@ -0,0 +1,15 @@ +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +var CryptoJS=CryptoJS||function(e,m){var p={},j=p.lib={},l=function(){},f=j.Base={extend:function(a){l.prototype=this;var c=new l;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},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)}}, +n=j.WordArray=f.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=m?c:4*a.length},toString:function(a){return(a||h).stringify(this)},concat:function(a){var c=this.words,q=a.words,d=this.sigBytes;a=a.sigBytes;this.clamp();if(d%4)for(var b=0;b>>2]|=(q[b>>>2]>>>24-8*(b%4)&255)<<24-8*((d+b)%4);else if(65535>>2]=q[b>>>2];else c.push.apply(c,q);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<< +32-8*(c%4);a.length=e.ceil(c/4)},clone:function(){var a=f.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],b=0;b>>2]>>>24-8*(d%4)&255;b.push((f>>>4).toString(16));b.push((f&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-4*(d%8);return new n.init(b,c/2)}},g=b.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],d=0;d>>2]>>>24-8*(d%4)&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],d=0;d>>2]|=(a.charCodeAt(d)&255)<<24-8*(d%4);return new n.init(b,c)}},r=b.Utf8={stringify:function(a){try{return decodeURIComponent(escape(g.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return g.parse(unescape(encodeURIComponent(a)))}}, +k=j.BufferedBlockAlgorithm=f.extend({reset:function(){this._data=new n.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=r.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,b=c.words,d=c.sigBytes,f=this.blockSize,h=d/(4*f),h=a?e.ceil(h):e.max((h|0)-this._minBufferSize,0);a=h*f;d=e.min(4*a,d);if(a){for(var g=0;ga;a++){if(16>a)l[a]=f[n+a]|0;else{var c=l[a-3]^l[a-8]^l[a-14]^l[a-16];l[a]=c<<1|c>>>31}c=(h<<5|h>>>27)+j+l[a];c=20>a?c+((g&e|~g&k)+1518500249):40>a?c+((g^e^k)+1859775393):60>a?c+((g&e|g&k|e&k)-1894007588):c+((g^e^ +k)-899497514);j=k;k=e;e=g<<30|g>>>2;g=h;h=c}b[0]=b[0]+h|0;b[1]=b[1]+g|0;b[2]=b[2]+e|0;b[3]=b[3]+k|0;b[4]=b[4]+j|0},_doFinalize:function(){var f=this._data,e=f.words,b=8*this._nDataBytes,h=8*f.sigBytes;e[h>>>5]|=128<<24-h%32;e[(h+64>>>9<<4)+14]=Math.floor(b/4294967296);e[(h+64>>>9<<4)+15]=b;f.sigBytes=4*e.length;this._process();return this._hash},clone:function(){var e=j.clone.call(this);e._hash=this._hash.clone();return e}});e.SHA1=j._createHelper(m);e.HmacSHA1=j._createHmacHelper(m)})(); diff --git a/media/js/base.js b/media/js/base.js index a9bcc6f136..3a2a2bb31b 100644 --- a/media/js/base.js +++ b/media/js/base.js @@ -54,6 +54,12 @@ $(document).click(function(e) { } }); +// clear repo enc info when log out +$('#logout').click(function() { + if ('localStorage' in window && window['localStorage'] !== null) { + localStorage.clear(); + } +}); if ($.browser.mozilla || $.browser.msie) { $('a').focus(function() { $(this).blur(); diff --git a/media/js/codecBytes.js b/media/js/codecBytes.js new file mode 100644 index 0000000000..cde2af3aec --- /dev/null +++ b/media/js/codecBytes.js @@ -0,0 +1,37 @@ +/** @fileOverview Bit array codec implementations. + * + * @author Emily Stark + * @author Mike Hamburg + * @author Dan Boneh + */ + +/** @namespace Arrays of bytes */ +sjcl.codec.bytes = { + /** Convert from a bitArray to an array of bytes. */ + fromBits: function (arr) { + var out = [], bl = sjcl.bitArray.bitLength(arr), i, tmp; + for (i=0; i>> 24); + tmp <<= 8; + } + return out; + }, + /** Convert from an array of bytes to a bitArray. */ + toBits: function (bytes) { + var out = [], i, tmp=0; + for (i=0; i>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^g[3][f[c& +255]]}; +sjcl.cipher.aes.prototype={encrypt:function(a){return y(this,a,0)},decrypt:function(a){return y(this,a,1)},j:[[[],[],[],[],[]],[[],[],[],[],[]]],D:function(){var a=this.j[0],b=this.j[1],c=a[4],d=b[4],e,f,g,h=[],l=[],k,n,m,p;for(e=0;0x100>e;e++)l[(h[e]=e<<1^283*(e>>7))^e]=e;for(f=g=0;!c[f];f^=k||1,g=l[g]||1){m=g^g<<1^g<<2^g<<3^g<<4;m=m>>8^m&255^99;c[f]=m;d[m]=f;n=h[e=h[k=h[f]]];p=0x1010101*n^0x10001*e^0x101*k^0x1010100*f;n=0x101*h[m]^0x1010100*m;for(e=0;4>e;e++)a[e][f]=n=n<<24^n>>>8,b[e][m]=p=p<<24^p>>>8}for(e= +0;5>e;e++)a[e]=a[e].slice(0),b[e]=b[e].slice(0)}}; +function y(a,b,c){4!==b.length&&q(new sjcl.exception.invalid("invalid aes block size"));var d=a.a[c],e=b[0]^d[0],f=b[c?3:1]^d[1],g=b[2]^d[2];b=b[c?1:3]^d[3];var h,l,k,n=d.length/4-2,m,p=4,s=[0,0,0,0];h=a.j[c];a=h[0];var r=h[1],v=h[2],w=h[3],x=h[4];for(m=0;m>>24]^r[f>>16&255]^v[g>>8&255]^w[b&255]^d[p],l=a[f>>>24]^r[g>>16&255]^v[b>>8&255]^w[e&255]^d[p+1],k=a[g>>>24]^r[b>>16&255]^v[e>>8&255]^w[f&255]^d[p+2],b=a[b>>>24]^r[e>>16&255]^v[f>>8&255]^w[g&255]^d[p+3],p+=4,e=h,f=l,g=k;for(m=0;4> +m;m++)s[c?3&-m:m]=x[e>>>24]<<24^x[f>>16&255]<<16^x[g>>8&255]<<8^x[b&255]^d[p++],h=e,e=f,f=g,g=b,b=h;return s} +sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.O(a.slice(b/32),32-(b&31)).slice(1);return c===t?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<>b-1,1));return a},partial:function(a,b,c){return 32===a?b:(c?b|0:b<<32-a)+0x10000000000*a},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return u;var c=0,d;for(d=0;d>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32>>24),e<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c>>e)>>>26),6>e?(g=a[c]<<6-e,e+=26,c++):(g<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,f=sjcl.codec.base64.I,g=0,h;b&&(f=f.substr(0,62)+"-_");for(d=0;dh&&q(new sjcl.exception.invalid("this isn't base64!")),26>>e),g=h<<32-e):(e+=6,g^=h<<32-e);e&56&&c.push(sjcl.bitArray.partial(e&56,g,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.a[0]||this.D();a?(this.q=a.q.slice(0),this.m=a.m.slice(0),this.g=a.g):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()}; +sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.q=this.M.slice(0);this.m=[];this.g=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.m=sjcl.bitArray.concat(this.m,a);b=this.g;a=this.g=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)z(this,c.splice(0,16));return this},finalize:function(){var a,b=this.m,c=this.q,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.g/ +4294967296));for(b.push(this.g|0);b.length;)z(this,b.splice(0,16));this.reset();return c},M:[],a:[],D:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}var b=0,c=2,d;a:for(;64>b;c++){for(d=2;d*d<=c;d++)if(0===c%d)continue a;8>b&&(this.M[b]=a(Math.pow(c,0.5)));this.a[b]=a(Math.pow(c,1/3));b++}}}; +function z(a,b){var c,d,e,f=b.slice(0),g=a.q,h=a.a,l=g[0],k=g[1],n=g[2],m=g[3],p=g[4],s=g[5],r=g[6],v=g[7];for(c=0;64>c;c++)16>c?d=f[c]:(d=f[c+1&15],e=f[c+14&15],d=f[c&15]=(d>>>7^d>>>18^d>>>3^d<<25^d<<14)+(e>>>17^e>>>19^e>>>10^e<<15^e<<13)+f[c&15]+f[c+9&15]|0),d=d+v+(p>>>6^p>>>11^p>>>25^p<<26^p<<21^p<<7)+(r^p&(s^r))+h[c],v=r,r=s,s=p,p=m+d|0,m=n,n=k,k=l,l=d+(k&n^m&(k^n))+(k>>>2^k>>>13^k>>>22^k<<30^k<<19^k<<10)|0;g[0]=g[0]+l|0;g[1]=g[1]+k|0;g[2]=g[2]+n|0;g[3]=g[3]+m|0;g[4]=g[4]+p|0;g[5]=g[5]+s|0;g[6]= +g[6]+r|0;g[7]=g[7]+v|0} +sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,l=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];7>l&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(f=2;4>f&&k>>>8*f;f++);f<15-l&&(f=15-l);c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.K(a,b,c,d,e,f);g=sjcl.mode.ccm.n(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),l=f.clamp(b,h-e),k=f.bitSlice(b, +h-e),h=(h-e)/8;7>g&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(b=2;4>b&&h>>>8*b;b++);b<15-g&&(b=15-g);c=f.clamp(c,8*(15-b));l=sjcl.mode.ccm.n(a,l,c,k,e,b);a=sjcl.mode.ccm.K(a,l.data,c,d,e,b);f.equal(l.tag,a)||q(new sjcl.exception.corrupt("ccm: tag doesn't match"));return l.data},K:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,l=h.k;e/=8;(e%2||4>e||16=c?g=[h.partial(16,c)]:0xffffffff>=c&&(g=h.concat([h.partial(16,65534)],[c]));g=h.concat(g,d);for(d=0;de.bitLength(c)&&(h=f(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));g=f(g,c);return a.encrypt(f(d(f(h, +d(h))),g))},G:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}}; +sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var f=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.n(!0,a,f,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var f=b.slice(0),g=sjcl.bitArray,h=g.bitLength(f);e=e||128;d=d||[];e<=h?(b=g.bitSlice(f,h-e),f=g.bitSlice(f,0,h-e)):(b=f,f=[]);a=sjcl.mode.gcm.n(u,a,f,d,c,e);g.equal(a.tag,b)||q(new sjcl.exception.corrupt("gcm: tag doesn't match"));return a.data},U:function(a,b){var c,d,e,f,g,h=sjcl.bitArray.k;e=[0,0,0,0];f=b.slice(0); +for(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,f));g=0!==(f[3]&1);for(d=3;0>>1|(f[d-1]&1)<<31;f[0]>>>=1;g&&(f[0]^=-0x1f000000)}return e},f:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;de&&(a=b.hash(a));for(d=0;dd||0>c)&&q(sjcl.exception.invalid("invalid params to pbkdf2"));"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,l,k=[],n=sjcl.bitArray;for(l=1;32*k.length<(d||1);l++){e=f=a.encrypt(n.concat(b,[l]));for(g=1;gg;g++)e.push(0x100000000*Math.random()|0);for(g=0;g=1<this.i&&(this.i=f);this.F++; +this.a=sjcl.hash.sha256.hash(this.a.concat(e));this.A=new sjcl.cipher.aes(this.a);for(d=0;4>d&&!(this.e[d]=this.e[d]+1|0,this.e[d]);d++);}for(d=0;d>>=1;this.b[g].update([d,this.C++,2,b,f,a.length].concat(a))}break;case "string":b===t&&(b=a.length);this.b[g].update([d,this.C++,3,b,f,a.length]);this.b[g].update(a); +break;default:l=1}l&&q(new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string"));this.h[g]+=b;this.c+=b;h===this.l&&(this.isReady()!==this.l&&C("seeded",Math.max(this.i,this.c)),C("progress",this.getProgress()))},isReady:function(a){a=this.H[a!==t?a:this.B];return this.i&&this.i>=a?this.h[0]>this.P&&(new Date).valueOf()>this.N?this.w|this.u:this.u:this.c>=a?this.w|this.l:this.l},getProgress:function(a){a=this.H[a?a:this.B];return this.i>=a?1:this.c>a?1:this.c/ +a},startCollectors:function(){this.p||(window.addEventListener?(window.addEventListener("load",this.r,u),window.addEventListener("mousemove",this.s,u)):document.attachEvent?(document.attachEvent("onload",this.r),document.attachEvent("onmousemove",this.s)):q(new sjcl.exception.bug("can't attach event")),this.p=!0)},stopCollectors:function(){this.p&&(window.removeEventListener?(window.removeEventListener("load",this.r,u),window.removeEventListener("mousemove",this.s,u)):window.detachEvent&&(window.detachEvent("onload", +this.r),window.detachEvent("onmousemove",this.s)),this.p=u)},addEventListener:function(a,b){this.z[a][this.S++]=b},removeEventListener:function(a,b){var c,d,e=this.z[a],f=[];for(d in e)e.hasOwnProperty(d)&&e[d]===b&&f.push(d);for(c=0;cb&&!(a.e[b]=a.e[b]+1|0,a.e[b]);b++);return a.A.encrypt(a.e)}sjcl.random=new sjcl.prng(6);try{var D=new Uint32Array(32);crypto.getRandomValues(D);sjcl.random.addEntropy(D,1024,"crypto['getRandomValues']")}catch(E){} +sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},encrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.d({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.d(f,c);c=f.adata;"string"===typeof f.salt&&(f.salt=sjcl.codec.base64.toBits(f.salt));"string"===typeof f.iv&&(f.iv=sjcl.codec.base64.toBits(f.iv));(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||"string"===typeof a&&100>=f.iter||64!==f.ts&&96!==f.ts&&128!==f.ts||128!==f.ks&&192!==f.ks&&0x100!==f.ks||2>f.iv.length|| +4=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4>> ((i & 0x03) << 3) & 0xff; + } + + return _rnds; + }; + } + + // Buffer class to use + var BufferClass = typeof(Buffer) == 'function' ? Buffer : Array; + + // Maps for number <-> hex string conversion + var _byteToHex = []; + var _hexToByte = {}; + for (var i = 0; i < 256; i++) { + _byteToHex[i] = (i + 0x100).toString(16).substr(1); + _hexToByte[_byteToHex[i]] = i; + } + + // **`parse()` - Parse a UUID into it's component bytes** + function parse(s, buf, offset) { + var i = (buf && offset) || 0, ii = 0; + + buf = buf || []; + s.toLowerCase().replace(/[0-9a-f]{2}/g, function(oct) { + if (ii < 16) { // Don't overflow! + buf[i + ii++] = _hexToByte[oct]; + } + }); + + // Zero out remaining bytes if string was short + while (ii < 16) { + buf[i + ii++] = 0; + } + + return buf; + } + + // **`unparse()` - Convert UUID byte array (ala parse()) into a string** + function unparse(buf, offset) { + var i = offset || 0, bth = _byteToHex; + return bth[buf[i++]] + bth[buf[i++]] + + bth[buf[i++]] + bth[buf[i++]] + '-' + + bth[buf[i++]] + bth[buf[i++]] + '-' + + bth[buf[i++]] + bth[buf[i++]] + '-' + + bth[buf[i++]] + bth[buf[i++]] + '-' + + bth[buf[i++]] + bth[buf[i++]] + + bth[buf[i++]] + bth[buf[i++]] + + bth[buf[i++]] + bth[buf[i++]]; + } + + // **`v1()` - Generate time-based UUID** + // + // Inspired by https://github.com/LiosK/UUID.js + // and http://docs.python.org/library/uuid.html + + // random #'s we need to init node and clockseq + var _seedBytes = _rng(); + + // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1) + var _nodeId = [ + _seedBytes[0] | 0x01, + _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5] + ]; + + // Per 4.2.2, randomize (14 bit) clockseq + var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff; + + // Previous uuid creation time + var _lastMSecs = 0, _lastNSecs = 0; + + // See https://github.com/broofa/node-uuid for API details + function v1(options, buf, offset) { + var i = buf && offset || 0; + var b = buf || []; + + options = options || {}; + + var clockseq = options.clockseq != null ? options.clockseq : _clockseq; + + // UUID timestamps are 100 nano-second units since the Gregorian epoch, + // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so + // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs' + // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00. + var msecs = options.msecs != null ? options.msecs : new Date().getTime(); + + // Per 4.2.1.2, use count of uuid's generated during the current clock + // cycle to simulate higher resolution clock + var nsecs = options.nsecs != null ? options.nsecs : _lastNSecs + 1; + + // Time since last uuid creation (in msecs) + var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000; + + // Per 4.2.1.2, Bump clockseq on clock regression + if (dt < 0 && options.clockseq == null) { + clockseq = clockseq + 1 & 0x3fff; + } + + // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new + // time interval + if ((dt < 0 || msecs > _lastMSecs) && options.nsecs == null) { + nsecs = 0; + } + + // Per 4.2.1.2 Throw error if too many uuids are requested + if (nsecs >= 10000) { + throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec'); + } + + _lastMSecs = msecs; + _lastNSecs = nsecs; + _clockseq = clockseq; + + // Per 4.1.4 - Convert from unix epoch to Gregorian epoch + msecs += 12219292800000; + + // `time_low` + var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; + b[i++] = tl >>> 24 & 0xff; + b[i++] = tl >>> 16 & 0xff; + b[i++] = tl >>> 8 & 0xff; + b[i++] = tl & 0xff; + + // `time_mid` + var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff; + b[i++] = tmh >>> 8 & 0xff; + b[i++] = tmh & 0xff; + + // `time_high_and_version` + b[i++] = tmh >>> 24 & 0xf | 0x10; // include version + b[i++] = tmh >>> 16 & 0xff; + + // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) + b[i++] = clockseq >>> 8 | 0x80; + + // `clock_seq_low` + b[i++] = clockseq & 0xff; + + // `node` + var node = options.node || _nodeId; + for (var n = 0; n < 6; n++) { + b[i + n] = node[n]; + } + + return buf ? buf : unparse(b); + } + + // **`v4()` - Generate random UUID** + + // See https://github.com/broofa/node-uuid for API details + function v4(options, buf, offset) { + // Deprecated - 'format' argument, as supported in v1.2 + var i = buf && offset || 0; + + if (typeof(options) == 'string') { + buf = options == 'binary' ? new BufferClass(16) : null; + options = null; + } + options = options || {}; + + var rnds = options.random || (options.rng || _rng)(); + + // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` + rnds[6] = (rnds[6] & 0x0f) | 0x40; + rnds[8] = (rnds[8] & 0x3f) | 0x80; + + // Copy bytes to buffer, if provided + if (buf) { + for (var ii = 0; ii < 16; ii++) { + buf[i + ii] = rnds[ii]; + } + } + + return buf || unparse(rnds); + } + + // Export public API + var uuid = v4; + uuid.v1 = v1; + uuid.v4 = v4; + uuid.parse = parse; + uuid.unparse = unparse; + uuid.BufferClass = BufferClass; + + if (typeof define === 'function' && define.amd) { + // Publish as AMD module + define(function() {return uuid;}); + } else if (typeof(module) != 'undefined' && module.exports) { + // Publish as node.js module + module.exports = uuid; + } else { + // Publish as global (in browsers) + var _previousRoot = _global.uuid; + + // **`noConflict()` - (browser only) to reset global 'uuid' var** + uuid.noConflict = function() { + _global.uuid = _previousRoot; + return uuid; + }; + + _global.uuid = uuid; + } +}).call(this); diff --git a/seahub/base/context_processors.py b/seahub/base/context_processors.py index 6ba3e2f8ee..74b35ff97a 100644 --- a/seahub/base/context_processors.py +++ b/seahub/base/context_processors.py @@ -7,7 +7,7 @@ These are referenced from the setting TEMPLATE_CONTEXT_PROCESSORS and used by RequestContext. """ from seahub.settings import SEAFILE_VERSION, SITE_TITLE, SITE_NAME, SITE_BASE, \ - ENABLE_SIGNUP, MAX_FILE_NAME, BRANDING_CSS, LOGO_PATH, LOGO_URL + ENABLE_SIGNUP, MAX_FILE_NAME, BRANDING_CSS, LOGO_PATH, LOGO_URL, KEEP_ENC_REPO_PASSWD try: from seahub.settings import BUSINESS_MODE except ImportError: @@ -54,5 +54,6 @@ def base(request): 'max_file_name': MAX_FILE_NAME, 'has_file_search': HAS_FILE_SEARCH, 'enable_pubfile': ENABLE_PUBFILE, + 'keep_enc_repo_passwd': KEEP_ENC_REPO_PASSWD, } diff --git a/seahub/base/decorators.py b/seahub/base/decorators.py index 89428817e9..057c9825dc 100644 --- a/seahub/base/decorators.py +++ b/seahub/base/decorators.py @@ -3,7 +3,9 @@ from django.shortcuts import render_to_response from django.template import RequestContext from seaserv import get_repo, is_passwd_set -from seahub.utils import check_and_get_org_by_repo, check_and_get_org_by_group +from seahub.utils import check_and_get_org_by_repo, check_and_get_org_by_group, render_error +from django.utils.translation import ugettext as _ +from seahub.settings import KEEP_ENC_REPO_PASSWD def sys_staff_required(func): """ @@ -62,12 +64,18 @@ def repo_passwd_set_required(func): if not repo: raise Http404 username = request.user.username - if repo.encrypted and not is_passwd_set(repo_id, username): - # Redirect uesr to decrypt repo page. - return render_to_response('decrypt_repo_form.html', { - 'repo': repo, - 'next': request.get_full_path(), - }, context_instance=RequestContext(request)) + if repo.encrypted: + if (repo.enc_version == 1 or (repo.enc_version == 2 and KEEP_ENC_REPO_PASSWD)) \ + and not is_passwd_set(repo_id, username): + # Redirect uesr to decrypt repo page. + return render_to_response('decrypt_repo_form.html', { + 'repo': repo, + 'next': request.get_full_path(), + }, context_instance=RequestContext(request)) + + if repo.enc_version == 2 and not KEEP_ENC_REPO_PASSWD: + return render_error(request, _(u'Files in this library can not be viewed online.')) + return func(request, *args, **kwargs) return _decorated diff --git a/seahub/forms.py b/seahub/forms.py index 45536dce74..e1b42c85b6 100644 --- a/seahub/forms.py +++ b/seahub/forms.py @@ -64,7 +64,9 @@ class RepoCreateForm(forms.Form): 'min_length': _(u'Password is too short (minimum is 3 characters)'), 'max_length': _(u'Password is too long (maximum is 30 characters)'), }) - + uuid = forms.CharField(required=False) + magic_str = forms.CharField(required=False) + random_key = forms.CharField(required=False) def clean_repo_name(self): repo_name = self.cleaned_data['repo_name'] if not is_valid_filename(repo_name): @@ -74,23 +76,24 @@ class RepoCreateForm(forms.Form): return repo_name def clean(self): - """ - Verifiy that the values entered into the two password fields - match. - """ - if 'passwd' in self.cleaned_data and 'passwd_again' in self.cleaned_data: - encryption = self.cleaned_data['encryption'] - if int(encryption) == 0: - # This prevents the case that form has passwords but the - # encryption checkbox is not selected. - self.cleaned_data['passwd'] = None - self.cleaned_data['passwd_again'] = None - return self.cleaned_data - else: - passwd = self.cleaned_data['passwd'] - passwd_again = self.cleaned_data['passwd_again'] - if passwd != passwd_again: - raise forms.ValidationError(_("Passwords don't match")) + encryption = self.cleaned_data['encryption'] + if int(encryption) == 0: + return self.cleaned_data + + if settings.KEEP_ENC_REPO_PASSWD: + passwd = self.cleaned_data['passwd'] + passwd_again = self.cleaned_data['passwd_again'] + if not (passwd and passwd_again): + raise forms.ValidationError(_("Password is required")) + if passwd != passwd_again: + raise forms.ValidationError(_("Passwords don't match")) + else: + uuid = self.cleaned_data['uuid'] + magic_str = self.cleaned_data['magic_str'] + random_key = self.cleaned_data['random_key'] + if not (uuid and magic_str and random_key): + raise forms.ValidationError(_("Argument missing")) + return self.cleaned_data class SharedRepoCreateForm(RepoCreateForm): diff --git a/seahub/group/templates/group/group_info.html b/seahub/group/templates/group/group_info.html index 0393ed2434..f1ee2859fd 100644 --- a/seahub/group/templates/group/group_info.html +++ b/seahub/group/templates/group/group_info.html @@ -169,6 +169,12 @@ {% endblock %} {% block extra_script %} +{% if not keep_enc_repo_passwd %} + + + + +{% endif %} +{% endblock %} diff --git a/seahub/templates/myhome.html b/seahub/templates/myhome.html index 65a5230654..5c8c1cd751 100644 --- a/seahub/templates/myhome.html +++ b/seahub/templates/myhome.html @@ -115,6 +115,12 @@ {% endblock %} {% block extra_script %} +{% if not keep_enc_repo_passwd %} + + + + +{% endif %} + + + +{% endif %} + + + +{% endif %} {% upload_js %} @@ -428,6 +512,95 @@ $('#mv-dirents, #cp-dirents').click(function() { }); }); +{% if repo.enc_version == 2 and not keep_enc_repo_passwd and repo.encrypted %} +function wordArray2ab(wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var u8 = new Uint8Array(sigBytes); + for (var i = 0; i < sigBytes; i++) { + var byte = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + u8[i] = byte; + } + + return u8; +} + +function encAndSubmitFile(file, prepare_fd, submit_url) { + function sliceFile(file) { + file.slice = file.mozSlice || file.webkitSlice || file.slice; // compatibility + var pos = 0; + var slices = []; + if (file.size == 0) { + return [file]; // It is required to be encrypted. + } + while(pos < file.size){ + slices.push(file.slice(pos, pos += 1024 * 1024)); + } + return slices; + } + var blocks = sliceFile(file), encrypted_blocks = []; + var workers = [], workersCount = 4, i, len; + for (i = 0; i < workersCount; i++) { + workers.push(new Worker('{{MEDIA_URL}}js/file_crypto.js')); + } + for (i = 0, len = workers.length; i < len ; i++) { + workers[i].addEventListener('message', onWorkerMessage, false); + } + for (i = 0, len = blocks.length; i < len; i++) { + workers[i % workers.length].postMessage({ + encrypt: true, + key: enc_key, + iv: enc_iv, + index: i, + block: blocks[i] + }); + } + + function onWorkerMessage(e) { + if (typeof e.data != 'object') { + // error + return; + } + encrypted_blocks.push({index: e.data.index, block: e.data.block, block_id: e.data.block_id}); + if (encrypted_blocks.length == blocks.length ) { + onFinish(); + } + } + + function onFinish() { + var ordered_enc_blocks = [], blk; + for (var i = 0, len = encrypted_blocks.length; i < len; i++) { + ordered_enc_blocks[encrypted_blocks[i].index] = {'id': encrypted_blocks[i].block_id, 'block': encrypted_blocks[i].block}; + } + var fd = prepare_fd(); + for (var i = 0, len = ordered_enc_blocks.length; i < len; i++) { + blk = new Blob([wordArray2ab(ordered_enc_blocks[i].block)], {type:''}); + fd.append('file', blk, ordered_enc_blocks[i].id); + } + $.ajax({ + url: submit_url, + type: "POST", + dataType: 'json', + data: fd, + processData: false, // tell jQuery not to process the data + contentType: false, // tell jQuery not to set contentType + beforeSend: prepareCSRFToken, + success: function(data) { + $.modal.close(); + reqDirData('{% url 'repo_dir_data' repo.id %}?p=' + e(cur_path)); + updateCmt(); + }, + error: function () { + $.modal.close(); + feedback('{% trans "Failed." %}', 'error'); + } + }); + } +} +{% endif %} // js on repo file list var no_file_op_popup = true; @@ -435,6 +608,35 @@ function dirOP() { $('.path .dir-link').click(dirlinkClick); +{% if repo.enc_version == 2 and not keep_enc_repo_passwd and repo.encrypted %} +$('#upload-file').click(function () { + $('#upload-file-dialog').modal(); + $('#simplemodal-container').css({'height':'auto'}); + + $('#upload-file-form [type="button"]').click(function() { + var form = $('#upload-file-form'); + var file_input = $('[type="file"]', form); + if (file_input[0].files.length == 0) { + $('.error', form).removeClass('hide'); + return false; + } + var file = file_input[0].files[0]; + + var prepare_fd = function() { + // the selected file will not be submitted + file_input.remove(); + // get the fields in the form (e.g. csrf...token) + var fd = new FormData(form[0]); + fd.append('parent_dir', cur_path); + fd.append('file_name', file.name); + fd.append('file_size', file.size); + return fd; + }; + var sb_url = form.attr('action'); + encAndSubmitFile(file, prepare_fd, sb_url); + }); +}); +{% else %} $('#upload-file').click(function () { var upload_success = false; $('#upload-file-dialog').modal({ @@ -450,8 +652,10 @@ $('#upload-file').click(function () { }); $('.simplemodal-wrap').css({'overflow':'auto'}); // for ie - var form = $('#upload-file-form'); - $('input[name="parent_dir"]', form).val(cur_path); + var form = $('#upload-file-form'), + parent_dir = $('input[name="parent_dir"]', form); + + parent_dir.val(cur_path); // Initialize the jQuery File Upload widget: form.fileupload({ @@ -471,6 +675,7 @@ $('#upload-file').click(function () { window.location.href.replace(/\/repo\/[-a-z0-9]{36}\/.*/, '/media/cors/result.html?%s') ); }); +{% endif %} $('#add-new-dir').click(function () { $('#add-new-dir-form').modal({appendTo:'#main'}); @@ -678,6 +883,133 @@ $('.checkbox-orig', context).unbind().click(function() { $('.dir-link', context).click(dirlinkClick); +{% if repo.enc_version == 2 and not keep_enc_repo_passwd and repo.encrypted %} + +$('.file-download').click(function() { + var file_name = $(this).parents('tr').attr('data-name'), + file_id = $(this).attr('data-fileid'); // data() will convert values, for example, '000000000...' is converted to 0 + + // if file size is 0, directly download it + if (file_id == '0000000000000000000000000000000000000000') { + var fileAsBlob = new Blob([], {type:''}); + var URL = window.URL || window.webkitURL; + var url = URL.createObjectURL(fileAsBlob); + var link = $("").attr("href", url).attr("download", file_name).html('Download').appendTo("body").hide(); + link[0].click(); + link.remove(); + return false; + } + + function showMsg(con, type) { + if ($('.messages')[0]) { + $('.messages').html('
  • ' + con + '
  • '); + } else { + var html = '
    • ' + con + '
    '; + $('#main').append(html); + } + $('.messages').css({'left':($(window).width() - $('.messages').width())/2, 'top':10}).removeClass('hide'); + } + + + showMsg("{% trans "Downloading..." %}", 'info'); + + $.ajax({ + url: '{{SITE_ROOT}}ajax/repo/{{repo.id}}/encrypted_file/' + file_id + '/download/', + dataType: 'json', + success: function(data) { + var block_id_list = data['blklist'], + block_get_url_root = data['url']; + + var blocks = [], decrypted_blocks = []; + + for (var i = 0, len = block_id_list.length; i < len; i++) { + reqBlock(i); + } + + var str2ab = function(str) { + var buf = new ArrayBuffer(str.length); + var bufView = new Uint8Array(buf); + for (var i = 0, strLen = str.length; i < strLen; i++) { + bufView[i] = str.charCodeAt(i); + } + return buf; + }; + + function reqBlock(index) { + $.ajax({ + url: block_get_url_root + block_id_list[index], + beforeSend:function(xhr) { + // let JQuery not process the returned binary data: return binary str + xhr.overrideMimeType("text/plain; charset=x-user-defined"); + }, + success: function(data, textStatus, xhr) { + blocks.push({index: index, block: str2ab(data)}); + var msg = "{% trans "Downloading, %(num)s% complete." %}"; + showMsg(msg.replace('%(num)s', (blocks.length / block_id_list.length * 100).toFixed(0)), 'info'); + if (blocks.length == block_id_list.length) { + // after receiving all blocks + decrypt(blocks); + } + }, + error: function() { + console.log('failed to get block ' + index); + } + }); + } + + var workers = [], workersCount = 4, i; + for (i = 0; i < workersCount; i++) { + workers.push(new Worker('{{MEDIA_URL}}js/file_crypto.js')); + } + for (i = 0; i < workers.length; i++){ + workers[i].addEventListener('message', onWorkerMessage, false); + } + function decrypt(blocks) { + showMsg("{% trans "Decrypting..." %}", 'info'); + for (i = 0; i < blocks.length; i++) { + workers[i % workers.length].postMessage({ + decrypt: true, + key: enc_key, + iv: enc_iv, + index: blocks[i].index, + block: blocks[i].block + }); + } + } + function onWorkerMessage(e) { + if (typeof e.data != 'object') { + // error + return; + } + decrypted_blocks.push({index: e.data.index, block: e.data.block}); + var msg = "{% trans "Decrypting, %(num)s% complete." %}"; + showMsg(msg.replace('%(num)s', (decrypted_blocks.length / block_id_list.length * 100).toFixed(0)), 'info'); + if (decrypted_blocks.length == block_id_list.length ) { + onFinish(); + } + } + + function onFinish() { + var ordered_decrypted_blocks = []; + for (var i = 0, len = decrypted_blocks.length; i < len; i++) { + ordered_decrypted_blocks[decrypted_blocks[i].index] = wordArray2ab(decrypted_blocks[i].block); + } + + var fileAsBlob = new Blob(ordered_decrypted_blocks, {type:''}); + var URL = window.URL || window.webkitURL; + var url = URL.createObjectURL(fileAsBlob); + var link = $("
    ").attr("href", url).attr("download", file_name).html('Download').appendTo("body").hide(); + link[0].click(); + showMsg("{% trans "Done!" %}", 'info'); + setTimeout(function() { $('.messages').addClass('hide'); }, 1500); + link.remove(); + } //onFinish ends + } + }); + return false; +}); +{% endif %} + var popup_tr = ''; // the tr which the shown popup belongs to $('#main-panel').removeClass('ovhd'); // to fix 'height becomes 0 after sort' for some versions of chrome/ff. @@ -840,8 +1172,41 @@ $('.file-cp, .file-mv, .dir-cp, .dir-mv', context).click(function () { return false; }); +{% if repo.enc_version == 2 and not keep_enc_repo_passwd and repo.encrypted %} $('.file-update', context).click(function() { - var file_name = $(this).parents('.file-item').data('name'); + $('#update-file-dialog').modal(); + $('#simplemodal-container').css({'width':'auto', 'height':'auto'}); + + var file_name = $(this).parents('.file-item').attr('data-name'); + var form = $('#update-file-form'); + var hd = $('#update-file-dialog .hd'); + hd.html(hd.html().replace('%(file_name)s', '' + file_name + '')); + + $('input[name="target_file"]', form).val(cur_path + file_name); + + $('[type="button"]', form).click(function() { + var file_input = $('[type="file"]', form); + if (file_input[0].files.length == 0) { + $('.error', form).removeClass('hide'); + return false; + } + var file = file_input[0].files[0]; + var prepare_fd = function() { + file_input.remove(); + var fd = new FormData(form[0]); + fd.append('file_size', file.size); + return fd; + }; + var sb_url = '{{ ajax_update_url }}?head=' + $('#repo-latest-commit .commit-msg').data('cmtid'); + + encAndSubmitFile(file, prepare_fd, sb_url); + }); + + return false; +}); +{% else %} +$('.file-update', context).click(function() { + var file_name = $(this).parents('.file-item').attr('data-name'); var form = $('#update-file-form'); var upload_success = false; $('#update-file-dialog').modal({ @@ -882,6 +1247,7 @@ $('.file-update', context).click(function() { return false; }); +{% endif %} $('.file-star', context).click(function() { var op = $(this), @@ -1010,12 +1376,14 @@ $('#add-new-file-form, #add-new-dir-form, #rename-form, #mv-form').submit(functi var name_link = $('.dirent-name a', op_obj); name_link.html(new_name).attr('href', name_link.attr('href').substr(0, name_link.attr('href').indexOf('?')) + '?p=' + e(path+new_name)); $('.dirent-update', op_obj).html("{% trans "Just now" %}"); - var dld_link = $('.download', op_obj), dld_href = dld_link.attr('href'); + var dld_link; if (op_obj.attr('class') == 'dir-item') { + dld_link = $('.dir-download', op_obj), dld_href = dld_link.attr('href'); dld_link.attr('href', dld_href.substr(0, dld_href.indexOf('?')) + '?p=' + e(path+new_name)); $('.dir-share', op_obj).data('link', '').data('token', ''); } else { $('.file-star', op_obj).attr('title', "{% trans "unstarred" %}").attr('class', 'icon-star-empty file-star').attr('data-status', 'unstarred'); + dld_link = $('.file-download', op_obj), dld_href = dld_link.attr('href'); dld_link.attr('href', dld_href.substr(0, dld_href.indexOf('?')) + '?file_name=' + e(new_name) + '&op=download'); $('.file-share', op_obj).data('link', '').data('token', ''); @@ -1236,7 +1604,6 @@ function updateCmt() { disable($(this)); }); {% endif %} - {% include "snippets/list_commit_detail.html" %} {% include "snippets/shared_link_js.html" %} {% include "snippets/bottom_bar.html" %} diff --git a/seahub/templates/seafile_access_check.html b/seahub/templates/seafile_access_check.html index 701becf5eb..b3983f4b1a 100644 --- a/seahub/templates/seafile_access_check.html +++ b/seahub/templates/seafile_access_check.html @@ -4,7 +4,11 @@ {% block main_panel %}

    {% trans 'Preparing, please wait...' %}

    -

    {% trans "No running Seafile client is detected. If you're sure Seafile client is running, click the following link to continue downloading:" %}
    {{ download_url|slice:"40" }}...

    +
    +

    {% trans "No running Seafile client version 1.x is detected." %}
    {% trans "If you're sure Seafile client is running, click the following link to continue downloading:" %}
    {{ download_url|slice:"40" }}...

    +

    {% trans "If you're using Seafile client version 2.0+, download the library from the client directly." %}

    + +

    {% trans "Your Seafile client is out of date, please upgrade to latest version." %}{% trans "Click to download." %}

    {% endblock %} diff --git a/seahub/templates/snippets/repo_create_js.html b/seahub/templates/snippets/repo_create_js.html index d57904ac9c..4eaadc07b4 100644 --- a/seahub/templates/snippets/repo_create_js.html +++ b/seahub/templates/snippets/repo_create_js.html @@ -13,78 +13,124 @@ $('#repo-create') } ); $('#encrypt-switch').click(function () { + var form = $('#repo-create-form'), + pwd_input = $('input[type="password"]', form); + if ($(this).attr('checked')) { - $('#repo-create-form input[type="password"]').attr('disabled', false).removeClass('input-disabled'); + pwd_input.attr('disabled', false).removeClass('input-disabled'); } else { - $('#repo-create-form input[type="password"]').attr('disabled', true).addClass('input-disabled'); + pwd_input.attr('disabled', true).addClass('input-disabled'); } }); $('#repo-create-form').submit(function() { - var passwd = $(this).find('input[name="passwd"]'), - passwd_again = $(this).find('input[name="passwd_again"]'); + var form = $(this), + form_id = form.attr('id'), + name = $('#repo-name').val(), + desc = $('#repo-desc').val(), + encrypted = $('#encrypt-switch').attr('checked'), + passwd, passwd_again; - var form = 'repo-create-form'; - if (!$.trim($('#repo-name').val())) { - apply_form_error(form, "{% trans "Name can't be empty" %}"); + if (!$.trim(name)) { + apply_form_error(form_id, "{% trans "Name is required" %}"); return false; } - if (!$.trim($('#repo-desc').val())) { - apply_form_error(form, "{% trans "Description can't be empty" %}"); + if (!$.trim(desc)) { + apply_form_error(form_id, "{% trans "Description is required" %}"); return false; } - if ($('#encrypt-switch').attr('checked')) { - if (!$.trim(passwd.val())) { - apply_form_error(form, "{% trans "Please enter password" %}"); + if (encrypted) { + passwd = $('input[name="passwd"]', form).val(); + passwd_again = $('input[name="passwd_again"]', form).val(); + + if (!$.trim(passwd)) { + apply_form_error(form_id, "{% trans "Please enter password" %}"); return false; } - if ($.trim(passwd.val()).length < 3) { - apply_form_error(form, "{% trans "Password is too short (minimum is 3 characters)" %}"); + if ($.trim(passwd).length < 3) { + apply_form_error(form_id, "{% trans "Password is too short (minimum is 3 characters)" %}"); return false; } - if ($.trim(passwd.val()).length > 30) { - apply_form_error(form, "{% trans "Password is too long (maximum is 30 characters)" %}"); + if ($.trim(passwd).length > 30) { + apply_form_error(form_id, "{% trans "Password is too long (maximum is 30 characters)" %}"); return false; } - if (!$.trim(passwd_again.val())) { - apply_form_error(form, "{% trans "Please enter the password again" %}"); + if (!$.trim(passwd_again)) { + apply_form_error(form_id, "{% trans "Please enter the password again" %}"); return false; } - if ($.trim(passwd.val()) != $.trim(passwd_again.val())) { - apply_form_error(form, "{% trans "Passwords don't match" %}"); + if ($.trim(passwd) != $.trim(passwd_again)) { + apply_form_error(form_id, "{% trans "Passwords don't match" %}"); return false; } + + {% if not keep_enc_repo_passwd %} + // don't send the password to the server + var salt = sjcl.codec.bytes.toBits([0xda, 0x90, 0x45, 0xc3, 0x06, 0xc7, 0xcc, 0x26]); + var uu_id = uuid.v4(); + var magic_array = sjcl.misc.pbkdf2(uu_id + passwd, salt, 1000, 32*8, null); // 32 bytes + var magic_str = sjcl.codec.hex.fromBits(magic_array); + + var secret_array; + if (window.crypto && window.crypto.getRandomValues) { + secret_array = new Uint32Array(8); + window.crypto.getRandomValues(secret_array); + } else { + secret_array = []; + for (var i = 0; i < 8; i++) { + secret_array.push(parseInt(Math.random() * Math.pow(2,32))); + } + } + + var key_array = sjcl.misc.pbkdf2(passwd, salt, 1000, 32*8, null); + var iv_array = sjcl.misc.pbkdf2(key_array, salt, 10, 32*8, null); + var key = sjcl.codec.hex.fromBits(key_array); + var iv = sjcl.codec.hex.fromBits(iv_array); + var random_key_obj = CryptoJS.AES.encrypt(CryptoJS.lib.WordArray.create(secret_array), CryptoJS.enc.Hex.parse(key), {iv: CryptoJS.enc.Hex.parse(iv)}); + var random_key = random_key_obj.ciphertext.toString(CryptoJS.enc.Hex); // convert to hex + {% endif %} } - var submit_btn = $(this).find('input[type="submit"]'); + var submit_btn = $('input[type="submit"]', form); disable(submit_btn); + + var post_data = { + 'repo_name': name, + 'repo_desc': desc, + 'encryption': encrypted ? 1 : 0 + }; + {% if create_shared_repo %} + $.extend(post_data, { + 'permission': $('select[name="permission"]', form).val() + }); + {% endif %} + + if (encrypted) { + {% if keep_enc_repo_passwd %} + $.extend(post_data, { + 'passwd': passwd, + 'passwd_again': passwd_again + }); + {% else %} + $.extend(post_data, { + 'uuid': uu_id, + 'magic_str': magic_str, + 'random_key': random_key + }); + {% endif %} + } + $.ajax({ url: '{{ post_url }}', type: 'POST', dataType: 'json', beforeSend: prepareCSRFToken, - data: { - 'repo_name': $('#repo-name').val(), - 'repo_desc': $('#repo-desc').val(), - {% if create_shared_repo %} - 'permission': $('#repo-create-form select[name="permission"]').val(), - {% endif %} - 'encryption': $('#encrypt-switch').attr('checked') ? 1 : 0, - 'passwd': passwd.val(), - 'passwd_again': passwd_again.val() - }, + data: post_data, success: function(data) { - if (data['success']) { - location.reload(true); - } else { - apply_form_error('repo-create-form', data['error']); - enable(submit_btn); - } + location.reload(true); }, - error: function(data, textStatus, jqXHR) { - var errors = $.parseJSON(data.responseText); - $.each(errors, function(index, value) { - apply_form_error('repo-create-form', value[0]); - }); + error: function(xhr, textStatus, errorThrown) { + var error = $.parseJSON(xhr.responseText).error; + apply_form_error(form_id, error); enable(submit_btn); } }); diff --git a/seahub/templates/snippets/repo_dirents.html b/seahub/templates/snippets/repo_dirents.html index f13dffe5ea..4fab36fe1e 100644 --- a/seahub/templates/snippets/repo_dirents.html +++ b/seahub/templates/snippets/repo_dirents.html @@ -21,7 +21,7 @@
    - {% trans 'Download' %} + {% trans 'Download' %} {% if not repo.encrypted %} {% trans "Share" %} {% endif %} @@ -67,7 +67,7 @@
    - {% trans "Download"%} + {% trans "Download"%} {% if user_perm == 'rw' %} {% trans "Update"%} {% endif %} diff --git a/seahub/templates/view_file_base.html b/seahub/templates/view_file_base.html index 794d329239..e28e0ddd54 100644 --- a/seahub/templates/view_file_base.html +++ b/seahub/templates/view_file_base.html @@ -67,10 +67,6 @@ {% endif %} {% if request.user.is_authenticated %} - {% block open_local %} - - {% endblock %} - {% block edit_file %} {% endblock %} {% endif %} @@ -89,17 +85,6 @@
    -
    -

    {% trans "Processing..."%}

    -

    -
    -

    {% trans "You have not downloaded " %}"{{ repo.props.name }}"{% trans "." %}

    - -
    -
    - - - {% include "snippets/file_share_popup.html" %} {% with attach_type='file' %} @@ -178,78 +163,10 @@ $('#star').click(function() { } ); -//open local -function send_open_local_file_request(path) { - var callback = 'xx'; - $.ajax({ - url: '{{ applet_root }}/open-local-file/?repo_id={{ repo.id }}&commit_id={{current_commit.id}}&path=' + e(path) + '&callback=' + callback, - dataType: 'jsonp', - jsonpCallback: callback, - crossDomain: true, - success: function(data) { - $('#open-local-handling-tip').addClass('hide'); - if (data['exists'] === false) { // repo doesn't exist on local machine - $('#open-local-error').addClass('hide'); - $('#redirect-download').removeClass('hide'); - - } else if (data['no_assoc'] === true) { // no application to open the file - $('#open-local-error').html('{% trans "Failed to find an application to open the file. Seafile will open the folder." %}'); - - } else if (data['outdated'] === true) { // local repo not in sync yet - if (data['auto-sync'] === false) { - $('#open-local-error').html('{% trans "The file is not synchronized, you can synchronize it manually." %}'); - } else { - $('#open-local-error').html('{% trans "The file is not synchronized, please retry later." %}'); - } - - } else if (data['error']) { // other error - $('#open-local-error').html('{% trans "An error occurred when open the local file." %}'); - - } else { // open file successfully - $('#open-local-handling-tip').removeClass('hide'); - setTimeout(function() { $.modal.close(); }, 1500); - } - } - }); -} $('#download-repo').click(function() { location.href = $(this).attr('data'); }); -$('#open-local').click(function () { - var path = $(this).attr('data'); - var local_applet_running = false; - $('#open-local-feedback').modal({appendTo: '#main', containerCss: { width: 300}}); - $('#simplemodal-container').css('height', 'auto'); - - $.ajax({ - url: '{{ applet_root }}/seafile_rpc_version/', - dataType: 'jsonp', - jsonpCallback: 'xx', - crossDomain: true, - success: function(version) { - local_applet_running = true; - if (version < 1) { - $('#open-local-handling-tip').addClass('hide'); - $('#open-local-error').html('{% trans 'You are running an old version of Seafile client, please upgrade it.' %}'); - $('#open-local-error a').attr('href', 'http://www.seafile.com/'); - } else { - send_open_local_file_request(path); - } - } - }); - - var url = '{{ applet_root }}/open-local-file/?repo_id={{ repo.id }}&commit_id={{current_commit.id}}&path=' + e(path) + '&callback=xx'; - var url_short = url.slice(0, 40) + '...'; - - setTimeout(function() { - if (!local_applet_running) { - $('#open-local-handling-tip').addClass('hide'); - $('#open-local-error').html("{% trans 'No running Seafile client is detected. If you are sure Seafile client is running, click the following link to open the local file:' %}" + '
    ' + url_short + ''); - } - }, 2000); -}); - $('#file-op .history').click(function () { location.href = $(this).data('url'); }); diff --git a/seahub/templates/view_file_sf.html b/seahub/templates/view_file_sf.html index ae54421dd9..34b9302d8e 100644 --- a/seahub/templates/view_file_sf.html +++ b/seahub/templates/view_file_sf.html @@ -1,8 +1,6 @@ {% extends 'view_file_base.html' %} {% load i18n %} -{% block open_local %}{% endblock %} - {% block edit_file %} {% if user_perm == 'rw' and not err%} diff --git a/seahub/urls.py b/seahub/urls.py index 7546852d37..d0394ed4cc 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -133,6 +133,8 @@ urlpatterns = patterns('', url(r'^ajax/repo/(?P[-0-9a-f]{36})/current_commit/$', get_current_commit, name='get_current_commit'), + url(r'^ajax/repo/(?P[-0-9a-f]{36})/encrypted_file/(?P[0-9a-f]{40})/download/$', download_enc_file, name='download_enc_file'), + ### Apps ### (r'^api2/', include('seahub.api2.urls')), (r'^avatar/', include('seahub.avatar.urls')), diff --git a/seahub/views/__init__.py b/seahub/views/__init__.py index 1cd4cf4be1..7fcced473a 100644 --- a/seahub/views/__init__.py +++ b/seahub/views/__init__.py @@ -89,7 +89,7 @@ if HAS_OFFICE_CONVERTER: import seahub.settings as settings from seahub.settings import FILE_PREVIEW_MAX_SIZE, INIT_PASSWD, USE_PDFJS, FILE_ENCODING_LIST, \ FILE_ENCODING_TRY_LIST, SEND_EMAIL_ON_ADDING_SYSTEM_MEMBER, SEND_EMAIL_ON_RESETTING_USER_PASSWD, \ - ENABLE_SUB_LIBRARY + ENABLE_SUB_LIBRARY, KEEP_ENC_REPO_PASSWD # Get an instance of a logger logger = logging.getLogger(__name__) @@ -609,7 +609,8 @@ def repo_history(request, repo_id): raise Http404 password_set = False - if repo.props.encrypted: + if repo.props.encrypted and \ + (repo.enc_version == 1 or (repo.enc_version == 2 and KEEP_ENC_REPO_PASSWD)): try: ret = seafserv_rpc.is_passwd_set(repo_id, request.user.username) if ret == 1: @@ -617,8 +618,8 @@ def repo_history(request, repo_id): except SearpcError, e: return render_error(request, e.msg) - if repo.props.encrypted and not password_set: - return HttpResponseRedirect(reverse('repo', args=[repo_id])) + if not password_set: + return HttpResponseRedirect(reverse('repo', args=[repo_id])) try: current_page = int(request.GET.get('page', '1')) @@ -665,7 +666,8 @@ def repo_view_snapshot(request, repo_id): raise Http404 password_set = False - if repo.props.encrypted: + if repo.props.encrypted and \ + (repo.enc_version == 1 or (repo.enc_version == 2 and KEEP_ENC_REPO_PASSWD)): try: ret = seafserv_rpc.is_passwd_set(repo_id, request.user.username) if ret == 1: @@ -673,8 +675,8 @@ def repo_view_snapshot(request, repo_id): except SearpcError, e: return render_error(request, e.msg) - if repo.props.encrypted and not password_set: - return HttpResponseRedirect(reverse('repo', args=[repo_id])) + if not password_set: + return HttpResponseRedirect(reverse('repo', args=[repo_id])) try: current_page = int(request.GET.get('page', '1')) @@ -718,7 +720,8 @@ def repo_history_revert(request, repo_id): return render_permission_error(request, _(u'You have no permission to restore library')) password_set = False - if repo.props.encrypted: + if repo.props.encrypted and \ + (repo.enc_version == 1 or (repo.enc_version == 2 and KEEP_ENC_REPO_PASSWD)): try: ret = seafserv_rpc.is_passwd_set(repo_id, request.user.username) if ret == 1: @@ -726,8 +729,8 @@ def repo_history_revert(request, repo_id): except SearpcError, e: return render_error(request, e.msg) - if repo.props.encrypted and not password_set: - return HttpResponseRedirect(reverse('repo', args=[repo_id])) + if not password_set: + return HttpResponseRedirect(reverse('repo', args=[repo_id])) commit_id = request.GET.get('commit_id', '') if not commit_id: @@ -784,6 +787,9 @@ def get_diff(repo_id, arg1, arg2): @login_required def repo_history_changes(request, repo_id): + if not request.is_ajax(): + return Http404 + changes = {} content_type = 'application/json; charset=utf-8' @@ -794,7 +800,9 @@ def repo_history_changes(request, repo_id): if not repo: return HttpResponse(json.dumps(changes), content_type=content_type) - if repo.encrypted and not is_passwd_set(repo_id, request.user.username): + if repo.encrypted and \ + (repo.enc_version == 1 or (repo.enc_version == 2 and KEEP_ENC_REPO_PASSWD)) \ + and not is_passwd_set(repo_id, request.user.username): return HttpResponse(json.dumps(changes), content_type=content_type) commit_id = request.GET.get('commit_id', '') @@ -1093,36 +1101,50 @@ def public_repo_create(request): content_type = 'application/json; charset=utf-8' form = SharedRepoCreateForm(request.POST) - if form.is_valid(): - repo_name = form.cleaned_data['repo_name'] - repo_desc = form.cleaned_data['repo_desc'] - permission = form.cleaned_data['permission'] - passwd = form.cleaned_data['passwd'] - user = request.user.username - - try: - # create a repo - repo_id = seafserv_threaded_rpc.create_repo(repo_name, repo_desc, - user, passwd) - # set this repo as inner pub - seafserv_threaded_rpc.set_inner_pub_repo(repo_id, permission) - except: - repo_id = None - if not repo_id: - result['error'] = _(u'Failed to create library') - else: - result['success'] = True - repo_created.send(sender=None, - org_id=-1, - creator=user, - repo_id=repo_id, - repo_name=repo_name) - - return HttpResponse(json.dumps(result), content_type=content_type) - else: - return HttpResponseBadRequest(json.dumps(form.errors), + if not form.is_valid(): + result['error'] = str(form.errors.values()[0]) + return HttpResponseBadRequest(json.dumps(result), content_type=content_type) + repo_name = form.cleaned_data['repo_name'] + repo_desc = form.cleaned_data['repo_desc'] + permission = form.cleaned_data['permission'] + encryption = int(form.cleaned_data['encryption']) + + passwd = form.cleaned_data['passwd'] + uuid = form.cleaned_data['uuid'] + magic_str = form.cleaned_data['magic_str'] + random_key = form.cleaned_data['random_key'] + user = request.user.username + + try: + if not encryption: + repo_id = seafile_api.create_repo(repo_name, repo_desc, user, None) + else: + if KEEP_ENC_REPO_PASSWD: + repo_id = seafile_api.create_repo(repo_name, repo_desc, user, passwd) + else: + repo_id = seafile_api.create_enc_repo(uuid, repo_name, repo_desc, user, magic_str, random_key, enc_version=2) + + # set this repo as inner pub + seafile_api.add_inner_pub_repo(repo_id, permission) + #seafserv_threaded_rpc.set_inner_pub_repo(repo_id, permission) + except SearpcError, e: + repo_id = None + + if not repo_id: + result['error'] = _(u'Internal Server Error') + return HttpResponse(json.dumps(result), status=500, + content_type=content_type) + else: + result['success'] = True + repo_created.send(sender=None, + org_id=-1, + creator=user, + repo_id=repo_id, + repo_name=repo_name) + return HttpResponse(json.dumps(result), content_type=content_type) + @login_required def unsetinnerpub(request, repo_id): repo = get_repo(repo_id) @@ -1267,7 +1289,9 @@ def get_repo_download_url(request, repo_id): url += "&email=%s&token=%s" % (email, token) url += "&repo_id=%s&repo_name=%s" % (repo_id, quote_repo_name) if enc: - url += "&encrypted=1&magic=%s" % repo.magic + url += "&encrypted=1&magic=%s&enc_ver=%s" % (repo.magic, repo.enc_version) + if repo.enc_version == 2 and repo.random_key: + url += "&key=%s" % repo.random_key return url, '' @@ -1355,31 +1379,43 @@ def repo_create(request): content_type = 'application/json; charset=utf-8' form = RepoCreateForm(request.POST) - if form.is_valid(): - repo_name = form.cleaned_data['repo_name'] - repo_desc = form.cleaned_data['repo_desc'] - passwd = form.cleaned_data['passwd'] - user = request.user.username - - try: - repo_id = seafserv_threaded_rpc.create_repo(repo_name, repo_desc, - user, passwd) - except: - repo_id = None - if not repo_id: - result['error'] = _(u"Failed to create library") - else: - result['success'] = True - repo_created.send(sender=None, - org_id=-1, - creator=user, - repo_id=repo_id, - repo_name=repo_name) - return HttpResponse(json.dumps(result), content_type=content_type) - else: - return HttpResponseBadRequest(json.dumps(form.errors), + if not form.is_valid(): + result['error'] = str(form.errors.values()[0]) + return HttpResponseBadRequest(json.dumps(result), content_type=content_type) + repo_name = form.cleaned_data['repo_name'] + repo_desc = form.cleaned_data['repo_desc'] + encryption = int(form.cleaned_data['encryption']) + + passwd = form.cleaned_data['passwd'] + uuid = form.cleaned_data['uuid'] + magic_str = form.cleaned_data['magic_str'] + random_key = form.cleaned_data['random_key'] + user = request.user.username + try: + if not encryption: + repo_id = seafile_api.create_repo(repo_name, repo_desc, user, None) + else: + if KEEP_ENC_REPO_PASSWD: + repo_id = seafile_api.create_repo(repo_name, repo_desc, user, passwd) + else: + repo_id = seafile_api.create_enc_repo(uuid, repo_name, repo_desc, user, magic_str, random_key, enc_version=2) + except SearpcError, e: + repo_id = None + + if not repo_id: + result['error'] = _(u"Internal Server Error") + return HttpResponse(json.dumps(result), status=500, content_type=content_type) + else: + result['success'] = True + repo_created.send(sender=None, + org_id=-1, + creator=user, + repo_id=repo_id, + repo_name=repo_name) + return HttpResponse(json.dumps(result), content_type=content_type) + def render_file_revisions (request, repo_id): """List all history versions of a file.""" path = request.GET.get('p', '/') diff --git a/seahub/views/ajax.py b/seahub/views/ajax.py index eb8fbbeb24..30a125c1e6 100644 --- a/seahub/views/ajax.py +++ b/seahub/views/ajax.py @@ -12,7 +12,7 @@ from django.utils.http import urlquote from django.utils.translation import ugettext as _ import seaserv -from seaserv import seafile_api +from seaserv import seafile_api, seafserv_rpc from pysearpc import SearpcError from seahub.auth.decorators import login_required @@ -23,7 +23,9 @@ from seahub.views.repo import get_nav_path, get_fileshare, get_dir_share_link import seahub.settings as settings from seahub.signals import repo_created from seahub.utils import check_filename_with_rename +from seahub.utils import check_filename_with_rename, EMPTY_SHA1, gen_block_get_url from seahub.utils.star import star_file, unstar_file +from seahub.settings import KEEP_ENC_REPO_PASSWD # Get an instance of a logger logger = logging.getLogger(__name__) @@ -181,7 +183,9 @@ def list_dir(request, repo_id): return HttpResponse(json.dumps({'error': err_msg}), status=403, content_type=content_type) - if repo.encrypted and not seafile_api.is_password_set(repo.id, username): + if repo.encrypted and \ + (repo.enc_version == 1 or (repo.enc_version == 2 and KEEP_ENC_REPO_PASSWD)) \ + and not seafile_api.is_password_set(repo.id, username): err_msg = _(u'Library is encrypted.') return HttpResponse(json.dumps({'error': err_msg}), status=403, content_type=content_type) @@ -245,7 +249,9 @@ def list_dir_more(request, repo_id): return HttpResponse(json.dumps({'error': err_msg}), status=403, content_type=content_type) - if repo.encrypted and not seafile_api.is_password_set(repo.id, username): + if repo.encrypted and \ + (repo.enc_version == 1 or (repo.enc_version == 2 and KEEP_ENC_REPO_PASSWD)) \ + and not seafile_api.is_password_set(repo.id, username): err_msg = _(u'Library is encrypted.') return HttpResponse(json.dumps({'error': err_msg}), status=403, content_type=content_type) @@ -858,7 +864,9 @@ def get_current_commit(request, repo_id): return HttpResponse(json.dumps({'error': err_msg}), status=403, content_type=content_type) - if repo.encrypted and not seafile_api.is_password_set(repo.id, username): + if repo.encrypted and \ + (repo.enc_version == 1 or (repo.enc_version == 2 and KEEP_ENC_REPO_PASSWD)) \ + and not seafile_api.is_password_set(repo.id, username): err_msg = _(u'Library is encrypted.') return HttpResponse(json.dumps({'error': err_msg}), status=403, content_type=content_type) @@ -918,3 +926,34 @@ def sub_repo(request, repo_id): return HttpResponse(json.dumps(result), status=500, content_type=content_type) return HttpResponse(json.dumps(result), content_type=content_type) + +def download_enc_file(request, repo_id, file_id): + if not request.is_ajax(): + raise Http404 + + content_type = 'application/json; charset=utf-8' + result = {} + + op = 'downloadblks' + blklist = [] + + if file_id == EMPTY_SHA1: + result = { 'blklist':blklist, 'url':None, } + return HttpResponse(json.dumps(result), content_type=content_type) + + try: + blks = seafile_api.list_file_by_file_id(file_id) + except SearpcError, e: + result['error'] = _(u'Failed to get file block list') + return HttpResponse(json.dumps(result), content_type=content_type) + + blklist = blks.split('\n') + blklist = [i for i in blklist if len(i) == 40] + token = seafserv_rpc.web_get_access_token(repo_id, file_id, + op, request.user.username) + url = gen_block_get_url(token, None) + result = { + 'blklist':blklist, + 'url':url, + } + return HttpResponse(json.dumps(result), content_type=content_type) diff --git a/seahub/views/repo.py b/seahub/views/repo.py index f468b9e5ce..600535cb2d 100644 --- a/seahub/views/repo.py +++ b/seahub/views/repo.py @@ -9,7 +9,7 @@ from django.template import RequestContext from django.template.loader import render_to_string import seaserv -from seaserv import seafile_api, MAX_UPLOAD_FILE_SIZE, get_personal_groups_by_user +from seaserv import seafserv_rpc, seafile_api, MAX_UPLOAD_FILE_SIZE, get_personal_groups_by_user from seahub.auth.decorators import login_required from seahub.contacts.models import Contact @@ -20,7 +20,7 @@ from seahub.views import gen_path_link, get_user_permission, get_repo_dirents, \ from seahub.utils import get_ccnetapplet_root, gen_file_upload_url, \ get_httpserver_root, gen_dir_share_link -from seahub.settings import ENABLE_SUB_LIBRARY +from seahub.settings import ENABLE_SUB_LIBRARY, KEEP_ENC_REPO_PASSWD # Get an instance of a logger logger = logging.getLogger(__name__) @@ -110,7 +110,35 @@ def get_ajax_upload_url(request, repo_id): """ api_upload_url = get_api_upload_url(request, repo_id) return api_upload_url.replace('api', 'aj') - + +def get_blks_upload_url(request, repo_id): + ''' + Get upload url for encrypted file (uploaded in blocks) + ''' + username = request.user.username + if get_user_permission(request, repo_id) == 'rw': + token = seafserv_rpc.web_get_access_token(repo_id, + 'dummy', + 'upload-blks', + request.user.username) + return gen_file_upload_url(token, 'upload-blks-api').replace('api', 'aj') + else: + return '' + +def get_blks_update_url(request, repo_id): + ''' + Get update url for encrypted file (uploaded in blocks) + ''' + username = request.user.username + if get_user_permission(request, repo_id) == 'rw': + token = seafserv_rpc.web_get_access_token(repo_id, + 'dummy', + 'update-blks', + request.user.username) + return gen_file_upload_url(token, 'update-blks-api').replace('api', 'aj') + else: + return '' + def get_api_update_url(request, repo_id): username = request.user.username if get_user_permission(request, repo_id) == 'rw': @@ -160,7 +188,9 @@ def render_repo(request, repo): 'repo': repo, }, context_instance=RequestContext(request)) - if repo.encrypted and not is_password_set(repo.id, username): + if repo.encrypted and \ + (repo.enc_version == 1 or (repo.enc_version == 2 and KEEP_ENC_REPO_PASSWD)) \ + and not is_password_set(repo.id, username): return render_to_response('decrypt_repo_form.html', { 'repo': repo, 'next': get_next_url_from_request(request) or \ @@ -199,8 +229,13 @@ def render_repo(request, repo): else: repo_group_str = '' upload_url = get_upload_url(request, repo.id) - ajax_upload_url = get_ajax_upload_url(request, repo.id) - ajax_update_url = get_ajax_update_url(request, repo.id) + + if repo.encrypted and repo.enc_version == 2 and not KEEP_ENC_REPO_PASSWD: + ajax_upload_url = get_blks_upload_url(request, repo.id) + ajax_update_url = get_blks_update_url(request, repo.id) + else: + ajax_upload_url = get_ajax_upload_url(request, repo.id) + ajax_update_url = get_ajax_update_url(request, repo.id) fileshare = get_fileshare(repo.id, username, path) dir_shared_link = get_dir_share_link(fileshare) @@ -280,7 +315,9 @@ def repo_history_view(request, repo_id): 'repo': repo, }, context_instance=RequestContext(request)) - if repo.encrypted and not is_password_set(repo.id, username): + if repo.encrypted and \ + (repo.enc_version == 1 or (repo.enc_version == 2 and KEEP_ENC_REPO_PASSWD)) \ + and not is_password_set(repo.id, username): return render_to_response('decrypt_repo_form.html', { 'repo': repo, 'next': get_next_url_from_request(request) or \