diff --git a/config.example.js b/config.example.js index 76bba6eae..1be17e510 100644 --- a/config.example.js +++ b/config.example.js @@ -116,6 +116,12 @@ module.exports = { 'contact', ], + /* Domain + * If you want to have enable payments on your CryptPad instance, it has to be able to tell + * our account server what is your domain + */ + // domain: 'https://cryptpad.fr', + /* You have the option of specifying an alternative storage adaptor. These status of these alternatives are specified in their READMEs, diff --git a/package.json b/package.json index 83653bd7b..75a4fbb11 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cryptpad", "description": "realtime collaborative visual editor with zero knowlege server", - "version": "1.6.0", + "version": "1.8.0", "dependencies": { "chainpad-server": "^1.0.1", "express": "~4.10.1", diff --git a/rpc.js b/rpc.js index ec6e516d0..e729e4802 100644 --- a/rpc.js +++ b/rpc.js @@ -7,11 +7,14 @@ var Nacl = require("tweetnacl"); var Fs = require("fs"); var Path = require("path"); +var Https = require("https"); var RPC = module.exports; var Store = require("./storage/file"); +var DEFAULT_LIMIT = 100; + var isValidChannel = function (chan) { return /^[a-fA-F0-9]/.test(chan) || [32, 48].indexOf(chan.length) !== -1; @@ -454,8 +457,64 @@ var isPrivilegedUser = function (publicKey, cb) { }); }; -var getLimit = function (cb) { - cb = cb; // TODO +// The limits object contains storage limits for all the publicKey that have paid +// To each key is associated an object containing the 'limit' value and a 'note' explaining that limit +var limits = {}; +var updateLimits = function (config, publicKey, cb) { + if (typeof cb !== "function") { cb = function () {}; } + + var body = JSON.stringify({ + domain: config.domain, + subdomain: config.subdomain + }); + var options = { + host: 'accounts.cryptpad.fr', + path: '/api/getauthorized', + method: 'POST', + headers: { + "Content-Type": "application/json", + "Content-Length": Buffer.byteLength(body) + } + }; + var req = Https.request(options, function (response) { + if (!('' + req.statusCode).match(/^2\d\d$/)) { + return void cb('SERVER ERROR ' + req.statusCode); + } + var str = ''; + + response.on('data', function (chunk) { + str += chunk; + }); + + response.on('end', function () { + try { + var json = JSON.parse(str); + limits = json; + var l; + if (publicKey) { + var limit = limits[publicKey]; + l = limit && typeof limit.limit === "number" ? limit.limit : DEFAULT_LIMIT; + } + cb(void 0, l); + } catch (e) { + cb(e); + } + }); + }); + + req.on('error', function (e) { + if (!config.domain) { return cb(); } + cb(e); + }); + + req.end(body); +}; + +var getLimit = function (publicKey, cb) { + var limit = limits[publicKey]; + + cb(void 0, limit && typeof(limit.limit) === "number"? + limit.limit : DEFAULT_LIMIT); }; var safeMkdir = function (path, cb) { @@ -714,10 +773,16 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) }); case 'GET_FILE_SIZE': return void getFileSize(ctx.store, msg[1], Respond); - case 'GET_LIMIT': // TODO implement this and cache it per-user - return void getLimit(function (e, limit) { + case 'UPDATE_LIMITS': + return void updateLimits(config, safeKey, function (e, limit) { + if (e) { return void Respond(e); } + Respond(void 0, limit); + }); + case 'GET_LIMIT': + return void getLimit(safeKey, function (e, limit) { + if (e) { return void Respond(e); } limit = limit; - Respond('NOT_IMPLEMENTED'); + Respond(void 0, limit); }); case 'GET_MULTIPLE_FILE_SIZE': return void getMultipleFileSize(ctx.store, msg[1], function (e, dict) { @@ -725,7 +790,6 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) Respond(void 0, dict); }); - // restricted to privileged users... case 'UPLOAD': if (!privileged) { return deny(); } @@ -775,6 +839,14 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function) handleMessage(session.privilege); }; + var updateLimitDaily = function () { + updateLimits(config, undefined, function (e) { + if (e) { console.error('Error updating the storage limits', e); } + }); + }; + updateLimitDaily(); + setInterval(updateLimitDaily, 24*3600*1000); + Store.create({ filePath: pinPath, }, function (s) { diff --git a/www/common/common-util.js b/www/common/common-util.js index f5ba9a61c..bbd648f7c 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -21,7 +21,7 @@ define([], function () { .replace(/ +$/, "") .split(" "); var byteString = String.fromCharCode.apply(null, hexArray); - return window.btoa(byteString).replace(/\//g, '-').slice(0,-2); + return window.btoa(byteString).replace(/\//g, '-').replace(/=+$/, ''); }; Util.base64ToHex = function (b64String) { @@ -90,11 +90,21 @@ define([], function () { }; Util.fetch = function (src, cb) { + var done = false; + var CB = function (err, res) { + if (done) { return; } + done = true; + cb(err, res); + }; + var xhr = new XMLHttpRequest(); xhr.open("GET", src, true); xhr.responseType = "arraybuffer"; xhr.onload = function () { - return void cb(void 0, new Uint8Array(xhr.response)); + return void CB(void 0, new Uint8Array(xhr.response)); + }; + xhr.onerror = function () { + CB('XHR_ERROR'); }; xhr.send(null); }; diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 457102032..ca8de5a08 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -746,8 +746,15 @@ define([ }); }; + common.updatePinLimit = function (cb) { + if (!pinsReady()) { return void cb('[RPC_NOT_READY]'); } + rpc.getFileListSize(cb); + }; + common.getPinLimit = function (cb) { - cb(void 0, typeof(AppConfig.pinLimit) === 'number'? AppConfig.pinLimit: 1000); + if (!pinsReady()) { return void cb('[RPC_NOT_READY]'); } + rpc.getFileListSize(cb); + //cb(void 0, typeof(AppConfig.pinLimit) === 'number'? AppConfig.pinLimit: 1000); }; common.isOverPinLimit = function (cb) { diff --git a/www/common/media-tag.js b/www/common/media-tag.js index d0953bcf6..0d18138ce 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -1 +1 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.MediaTag=t():e.MediaTag=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};return t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=82)}([function(e,t,n){"use strict";var r={IMAGE:"image",AUDIO:"audio",VIDEO:"video",PDF:"pdf",DASH:"dash",DOWNLOAD:"download",CRYPTO:"crypto",CLEAR_KEY:"clear-key",MEDIA_OBJECT:"media-object"};e.exports=r},function(e,t,n){"use strict";var r={MATCHER:"matcher",RENDERER:"renderer",FILTER:"filter",SANITIZER:"sanitizer"};e.exports=r},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=function(){function e(e,t){for(var n=0;n=e.STACK_LIMIT)throw console.error(e.snapshots[n]),new Error("Plugin stack size exceed");if(e.snapshots[n].length>=e.SNAPSHOT_LIMIT)throw console.error(e.snapshots[n]),new Error("Plugin snapshots size exceed");var r=0;if(e.stacks[n].forEach(function(e){e.type===u.RENDERER&&r++}),r<1&&e.stacks[n].unshift(e.defaultPlugin),r>1)throw new Error("More of one renderer in the stack")}},{key:"return",value:function(t){e.start(t)}},{key:"run",value:function(t){var n=t.getId(),r=e.stacks[n].length,o=e.stacks[n][r-1];if(!o)throw console.log(e.stacks),new Error("Impossible to run a undefined plugin");o.process(t)}}]),e}();f.stacks={},f.STACK_LIMIT=1e3,f.snapshots={},f.SNAPSHOT_LIMIT=1e3,f.defaultPlugin=new s,e.exports=f},function(e,t,n){"use strict";var r={EVERY:"every",ANY:"any",ONCE:"once"};e.exports=r},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var o=function(){function e(e,t){for(var n=0;n1;){if("number"!=typeof e[t])throw new Error("E_UNSAFE_TYPE");if(e[t]>255)throw new Error("E_OUT_OF_BOUNDS");if(255!==e[t])return void e[t]++;if(e[t]=0,0===t)throw new Error("E_NONCE_TOO_LARGE")}}},{key:"joinChunks",value:function(t){return new Uint8Array(t.reduce(function(t,n){return e.slice(t).concat(e.slice(n))},[]))}},{key:"padChunk",value:function(e){var t;if(131072===e.length)return e;if(e.length<131072)return t=new Array(131072-e.length).fill(32),e.concat(t);if(e.length>131072){var n=Math.ceil(e.length/131072);return t=new Array(131072*n-e.length).fill(32),e.concat(t)}}},{key:"slice",value:function(e){return Array.prototype.slice.call(e)}},{key:"getRandomKeyStr",value:function(){var t=e.Nacl,n=t.randomBytes(18);return t.util.encodeBase64(n)}},{key:"getKeyFromStr",value:function(t){return e.Nacl.util.decodeBase64(t)}},{key:"encrypt",value:function(t,n){var r=t,o=e.Nacl.randomBytes(24),i=e.Nacl.secretbox(r,o,n);if(i)return new Uint8Array(e.slice(o).concat(e.slice(i)));throw new Error}},{key:"decrypt",value:function(t,n){for(var r,o=e.Nacl,i=e.createNonce(),u=0,a=function(){var r=131088*u,a=r+131088;u++;var c=new Uint8Array(t.subarray(r,a)),s=o.secretbox.open(c,i,n);return e.increment(i),s},c="",s={metadata:void 0};!s.metadata&&131088*u1?t[0]:window.location.protocol}},{key:"hostname",value:function(e){var t=e.getAttribute("src").split("://");return t.length>1?t[1].split("/")[0]:window.location.hostname}},{key:"source",value:function(e){return e.getAttribute("src")}},{key:"schemes",value:function(e){return/\w+:/.exec(e.getAttribute("src"))}},{key:"parse",value:function(t){return{protocol:e.protocol(t),hostname:e.hostname(t),src:e.source(t),type:e.type(t),extension:e.extension(t),mime:e.mime(t)}}}]),e}();e.exports=i},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=function(){function e(e,t){for(var n=0;n=e.STACK_LIMIT)throw console.error(e.snapshots[n]),new Error("Plugin stack size exceed");if(e.snapshots[n].length>=e.SNAPSHOT_LIMIT)throw console.error(e.snapshots[n]),new Error("Plugin snapshots size exceed");var r=0;if(e.stacks[n].forEach(function(e){e.type===u.RENDERER&&r++}),r<1&&e.stacks[n].unshift(e.defaultPlugin),r>1)throw new Error("More of one renderer in the stack")}},{key:"return",value:function(t){e.start(t)}},{key:"run",value:function(t){var n=t.getId(),r=e.stacks[n].length,o=e.stacks[n][r-1];if(!o)throw console.log(e.stacks),new Error("Impossible to run a undefined plugin");o.process(t)}}]),e}();f.stacks={},f.STACK_LIMIT=1e3,f.snapshots={},f.SNAPSHOT_LIMIT=1e3,f.defaultPlugin=new s,e.exports=f},function(e,t,n){"use strict";var r={EVERY:"every",ANY:"any",ONCE:"once"};e.exports=r},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var o=function(){function e(e,t){for(var n=0;n1;){if(c){if("number"!=typeof e[t])throw new Error("E_UNSAFE_TYPE");if(e[t]>255)throw new Error("E_OUT_OF_BOUNDS")}if(255!==e[t])return void e[t]++;if(e[t]=0,0===t)throw new Error("E_NONCE_TOO_LARGE")}}},{key:"encodePrefix",value:function(e){return[65280,255].map(function(t,n){return(e&t)>>8*(1-n)})}},{key:"decodePrefix",value:function(e){return e[0]<<8|e[1]}},{key:"joinChunks",value:function(t){return new Uint8Array(t.reduce(function(t,n){return e.slice(t).concat(e.slice(n))},[]))}},{key:"slice",value:function(e){return Array.prototype.slice.call(e)}},{key:"getRandomKeyStr",value:function(){var t=e.Nacl,n=t.randomBytes(18);return t.util.encodeBase64(n)}},{key:"getKeyFromStr",value:function(t){return e.Nacl.util.decodeBase64(t)}},{key:"encrypt",value:function(t,n){var r=t,o=e.Nacl.randomBytes(24),i=e.Nacl.secretbox(r,o,n);if(i)return new Uint8Array(e.slice(o).concat(e.slice(i)));throw new Error}},{key:"decrypt",value:function(t,n){var r=e.Nacl,o=function(e){throw new Error(e||"DECRYPTION_ERROR")},i=new Uint8Array(new Array(24).fill(0)),u=0,a=t.subarray(0,2),c=e.decodePrefix(a),f={metadata:void 0},l=new Uint8Array(t.subarray(2,2+c)),p=r.secretbox.open(l,i,n);e.increment(i);try{f.metadata=JSON.parse(r.util.encodeUTF8(p))}catch(e){return o("E_METADATA_DECRYPTION")}f.metadata||o("NO_METADATA");for(var y,b=function(){var o=u*s+2+c,a=o+s;u++;var f=new Uint8Array(t.subarray(o,a)),l=r.secretbox.open(f,i,n);return e.increment(i),l},h=[];u*s1?t[0]:window.location.protocol}},{key:"hostname",value:function(e){var t=e.getAttribute("src").split("://");return t.length>1?t[1].split("/")[0]:window.location.hostname}},{key:"source",value:function(e){return e.getAttribute("src")}},{key:"schemes",value:function(e){return/\w+:/.exec(e.getAttribute("src"))}},{key:"parse",value:function(t){return{protocol:e.protocol(t),hostname:e.hostname(t),src:e.source(t),type:e.type(t),extension:e.extension(t),mime:e.mime(t)}}}]),e}();e.exports=i},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=function(){function e(e,t){for(var n=0;n diff --git a/www/media/main.js b/www/media/main.js index 9e8127a62..9e5276019 100644 --- a/www/media/main.js +++ b/www/media/main.js @@ -20,6 +20,11 @@ define([ Cryptpad.addLoadingScreen(); var andThen = function () { + $(window.document).on('decryption', function (e) { + var decrypted = e.originalEvent; + console.log(decrypted.blob, decrypted.metadata); + }); + var $bar = $iframe.find('.toolbar-container'); var secret = Cryptpad.getSecrets();