diff --git a/conf/example/camo.toml b/conf/example/camo.toml index 4e81ca35..cd7e010c 100644 --- a/conf/example/camo.toml +++ b/conf/example/camo.toml @@ -11,3 +11,5 @@ whitelisted-domains = [ 'i.imgur.com', 'i.4cdn.org' ] +# Whether to use URL encoding ("url") or hex encoding ("hex") for the target URL. +encoding = 'url' diff --git a/package.json b/package.json index 26abf8b9..c27326cf 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Calvin Montgomery", "name": "CyTube", "description": "Online media synchronizer and chat", - "version": "3.39.3", + "version": "3.39.4", "repository": { "url": "http://github.com/calzoneman/sync" }, diff --git a/src/camo.js b/src/camo.js index cf2ec070..23b1cc59 100644 --- a/src/camo.js +++ b/src/camo.js @@ -24,8 +24,14 @@ export function camoify(camoConfig: CamoConfig, url: string): string { const hmac = crypto.createHmac('sha1', camoConfig.getKey()); hmac.update(url); const digest = hmac.digest('hex'); - const hexUrl = Buffer.from(url, 'utf8').toString('hex'); - return `${camoConfig.getServer()}/${digest}/${hexUrl}`; + // https://github.com/atmos/camo#url-formats + if (camoConfig.getEncoding() === 'hex') { + const hexUrl = Buffer.from(url, 'utf8').toString('hex'); + return `${camoConfig.getServer()}/${digest}/${hexUrl}`; + } else { + const encoded = encodeURIComponent(url); + return `${camoConfig.getServer()}/${digest}?url=${encoded}`; + } } export function transformImgTags(camoConfig: CamoConfig, tagName: string, attribs: Object) { diff --git a/src/configuration/camoconfig.js b/src/configuration/camoconfig.js index 4d4f0961..a33728fb 100644 --- a/src/configuration/camoconfig.js +++ b/src/configuration/camoconfig.js @@ -4,6 +4,14 @@ class CamoConfig { if (this.config.server) { this.config.server = this.config.server.replace(/\/+$/, ''); } + this.validate(); + } + + validate() { + if (this.config.encoding + && !~['url', 'hex'].indexOf(this.config.encoding)) { + throw new Error(`Value for key 'encoding' must be either 'url' or 'hex', not '${this.config.encoding}'`); + } } isEnabled() { @@ -21,6 +29,10 @@ class CamoConfig { getWhitelistedDomains() { return this.config['whitelisted-domains'] || []; } + + getEncoding() { + return this.config.encoding || 'url'; + } } export { CamoConfig }; diff --git a/test/camo.js b/test/camo.js index 5fc3ebfa..ee157551 100644 --- a/test/camo.js +++ b/test/camo.js @@ -7,7 +7,8 @@ describe('Camo', () => { camo: { server: 'http://localhost:8081', key: '9LKC7708ZHOVRCTLOLE3G2YJ0U1T8F96', - 'whitelisted-domains': ['def.xyz'] + 'whitelisted-domains': ['def.xyz'], + encoding: 'hex' } }); @@ -17,6 +18,20 @@ describe('Camo', () => { assert.strictEqual(result, 'http://localhost:8081/a9c295dd7d8dcbc8247dec97ac5d9b4ee8baeb31/687474703a2f2f6162632e78797a2f696d6167652e6a706567'); }); + it('constructs a camo url using url encoding', () => { + const config = new CamoConfig({ + camo: { + server: 'http://localhost:8081', + key: '9LKC7708ZHOVRCTLOLE3G2YJ0U1T8F96', + 'whitelisted-domains': ['def.xyz'], + encoding: 'url' + } + }); + + const result = Camo.camoify(config, 'http://abc.xyz/image.jpeg'); + assert.strictEqual(result, 'http://localhost:8081/a9c295dd7d8dcbc8247dec97ac5d9b4ee8baeb31?url=http%3A%2F%2Fabc.xyz%2Fimage.jpeg'); + }); + it('bypasses camo for whitelisted domains', () => { const result = Camo.camoify(config, 'http://def.xyz/image.jpeg'); assert.strictEqual(result, 'https://def.xyz/image.jpeg'); diff --git a/test/configuration/camoconfig.js b/test/configuration/camoconfig.js index 3f8c463b..acf62333 100644 --- a/test/configuration/camoconfig.js +++ b/test/configuration/camoconfig.js @@ -15,6 +15,17 @@ describe('CamoConfig', () => { it('defaults to enabled=false', () => { assert.strictEqual(new CamoConfig().isEnabled(), false); }); + + it('validates that encoding must be either url or hex', () => { + + assert.throws(() => { + new CamoConfig({ + camo: { + encoding: 'asdjfnasdf' + } + }); + }, /must be either 'url' or 'hex'/); + }); }); describe('#getWhitelistedDomains', () => { @@ -22,4 +33,10 @@ describe('CamoConfig', () => { assert.deepStrictEqual(new CamoConfig().getWhitelistedDomains(), []); }); }); + + describe('#getEncoding', () => { + it('defaults to url', () => { + assert.deepStrictEqual(new CamoConfig().getEncoding(), 'url'); + }); + }); });