diff --git a/src/constants.ts b/src/constants.ts
index 1051dd9..e04ec12 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -1,3 +1,4 @@
export const META_FILE_NAME = '.swarmgatewaymeta.json'
export const PREVIEW_FILE_NAME = '.swarmgatewaypreview.jpeg'
export const PREVIEW_DIMENSIONS = { maxWidth: 250, maxHeight: 175 }
+export const BZZ_LINK_DOMAIN = process.env.REACT_APP_BZZ_LINK_DOMAIN || 'bzz.link'
diff --git a/src/pages/files/Download.tsx b/src/pages/files/Download.tsx
index 0ae913b..71718bd 100644
--- a/src/pages/files/Download.tsx
+++ b/src/pages/files/Download.tsx
@@ -8,7 +8,7 @@ import { History } from '../../components/History'
import { Context, defaultUploadOrigin } from '../../providers/File'
import { Context as SettingsContext } from '../../providers/Settings'
import { ROUTES } from '../../routes'
-import { extractSwarmHash } from '../../utils'
+import { recognizeSwarmHash } from '../../utils'
import { determineHistoryName, HISTORY_KEYS, putHistory } from '../../utils/local-storage'
import { FileNavigation } from './FileNavigation'
@@ -71,20 +71,6 @@ export function Download(): ReactElement {
}
}
- function recognizeSwarmHash(value: string) {
- if (value.length < 64) {
- return value
- }
-
- const hash = extractSwarmHash(value)
-
- if (hash) {
- return hash
- }
-
- return value
- }
-
return (
<>
diff --git a/src/utils/index.test.ts b/src/utils/index.test.ts
new file mode 100644
index 0000000..24db426
--- /dev/null
+++ b/src/utils/index.test.ts
@@ -0,0 +1,138 @@
+import { extractSwarmHash, extractSwarmCid, recognizeSwarmHash } from './index'
+
+interface TestObject {
+ input: string
+ expectedOutput: string | undefined
+}
+
+const correctHashes: TestObject[] = [
+ // non-encrypted
+ {
+ input: 'b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f',
+ expectedOutput: 'b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f',
+ },
+ {
+ input: 'http://gateway.org/bzz/b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f',
+ expectedOutput: 'b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f',
+ },
+ {
+ input: 'https://gateway.org/b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f',
+ expectedOutput: 'b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f',
+ },
+ {
+ input: 'http://gateway.org/bzz/b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f/',
+ expectedOutput: 'b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f',
+ },
+ {
+ input: 'https://gateway.org/b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f/',
+ expectedOutput: 'b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f',
+ },
+ // encrypted
+ {
+ input:
+ 'b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3fb7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f',
+ expectedOutput:
+ 'b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3fb7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f',
+ },
+ {
+ input:
+ 'http://gateway.org/bzz/b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3fb7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f',
+ expectedOutput:
+ 'b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3fb7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f',
+ },
+ {
+ input:
+ 'https://gateway.org/b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3fb7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f',
+ expectedOutput:
+ 'b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3fb7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f',
+ },
+ {
+ input:
+ 'http://gateway.org/bzz/b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3fb7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f/',
+ expectedOutput:
+ 'b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3fb7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f',
+ },
+ {
+ input:
+ 'https://gateway.org/b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3fb7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f/',
+ expectedOutput:
+ 'b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3fb7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3f',
+ },
+]
+
+const wrongHashes: string[] = [
+ // one character too long
+ 'b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3fa',
+ 'http://gateway.org/bzz/b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3fa',
+ 'https://gateway.org/b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3fa',
+ 'http://gateway.org/bzz/b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3fa/',
+ 'https://gateway.org/b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d3fa/',
+
+ // a bit shorter
+ 'b7e53783114e4555384d7fd7154eb8c2e3f7c749c176dcb8f4015b08161b3d',
+]
+
+describe('extractSwarmHash', () => {
+ test('should correctly extract hash', () => {
+ correctHashes.forEach(({ input, expectedOutput }) => {
+ const hash = extractSwarmHash(input)
+ expect(hash).toBe(expectedOutput)
+ })
+ })
+ test('should not extract hash from incorrect inputs', () => {
+ wrongHashes.forEach(input => {
+ const hash = extractSwarmHash(input)
+ expect(hash).toBe(undefined)
+ })
+ })
+})
+
+const correctCids: TestObject[] = [
+ {
+ input: 'https://bah5acgza5afd34lfvo7sowxfjahj4ujeduxgg2ge5u3zo4kcjlzjzi23fhka.bzz.link',
+ expectedOutput: 'e80a3df165abbf275ae5480e9e51241d2e6368c4ed379771424af29ca35b29d4',
+ },
+ {
+ input: 'https://bah5acgza2lzgtqfztvn3xtnzhv7qvbmblljd7bi52l5jiueretcad55vookq.bzz.link',
+ expectedOutput: 'd2f269c0b99d5bbbcdb93d7f0a85815ad23f851dd2fa94509124c401f7b57395',
+ },
+]
+const wrongCids: string[] = [
+ 'https://bah5acgza5afd34lfvo7sowxfjahj4ujeduxgg2ge5u3zo4kcjlzjzi23fhka.another.domain',
+ 'http://bah5acgza2lzgtqfztvn3xtnzhv7qvbmblljd7bi52l5jiueretcad55vookq.bzz.link',
+ 'https://not_cid.bzz.link',
+ 'https://bah5acgza5afd34lfvo7sowxfjahj4ujeduxgg2ge5u3zo4kcjlzjzi23fhka.subdomain.bzz.link',
+ 'https://bah5acgza5afd34lfvo7sowxfjahj4ujeduxgg2ge5u3zo4kcjlzjzi23fhka.subdomain.bzz.link',
+ 'https://bah5acgza2lzgtqfztvn3xtnzhv7qvbmblljd7bi52l5jiueretcad55vook.bzz.link',
+ 'https://aah5acgza2lzgtqfztvn3xtnzhv7qvbmblljd7bi52l5jiueretcad55vookq.bzz.link',
+]
+
+describe('extractSwarmCid', () => {
+ test('should correctly extract hash', () => {
+ correctCids.forEach(({ input, expectedOutput }) => {
+ const hash = extractSwarmCid(input)
+ expect(hash).toBe(expectedOutput)
+ })
+ })
+ test('should not extract cid from incorrect urls', () => {
+ wrongCids.forEach(url => {
+ const hash = extractSwarmCid(url)
+ expect(hash).toBe(undefined)
+ })
+ })
+})
+
+describe('recognizeSwarmHash', () => {
+ test('should correctly extract hash', () => {
+ ;[...correctHashes, ...correctCids].forEach(({ input, expectedOutput }) => {
+ const hash = recognizeSwarmHash(input)
+ expect(hash).toBe(expectedOutput)
+ })
+ })
+ test('should not extract hash from incorrect inputs but instead return them', () => {
+ ;[...wrongHashes, ...wrongCids].forEach(url => {
+ const hash = recognizeSwarmHash(url)
+ expect(hash).toBe(url)
+ })
+ })
+})
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 2d8e766..ded258f 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -1,5 +1,7 @@
import { BigNumber } from 'bignumber.js'
import { Token } from '../models/Token'
+import { decodeCid } from '@ethersphere/swarm-cid'
+import { BZZ_LINK_DOMAIN } from '../constants'
/**
* Test if value is an integer
@@ -108,10 +110,41 @@ export function makeRetriablePromise(fn: () => Promise, maxRetries = 3, de
})
}
-export function extractSwarmHash(string: string): string | null {
- const matches = string.match(/[a-fA-F0-9]{64,128}/)
+// Matches exactly 64 or 128 caracters alphanumeric characters that are surrounded by non-alpha num characters
+const regexpMatchHash = /(?:^|[^a-f0-9]+)([a-f0-9]{64}|[a-f0-9]{128})(?:$|[^a-f0-9]+)/i
- return (matches && matches[0]) || null
+export function extractSwarmHash(string: string): string | undefined {
+ const matches = string.match(regexpMatchHash)
+
+ return (matches && matches[1]) || undefined
+}
+
+// Matches the CID from bzz-link subdomain
+const regexpMatchCID = new RegExp(`https://(bah5acgza[a-z0-9]{52})\\.${BZZ_LINK_DOMAIN}`, 'i')
+
+export function extractSwarmCid(s: string): string | undefined {
+ const matches = s.match(regexpMatchCID)
+
+ if (!matches || !matches[1]) {
+ return
+ }
+
+ const cid = matches[1]
+ try {
+ const decodeResult = decodeCid(cid)
+
+ if (!decodeResult.type) {
+ return
+ }
+
+ return decodeResult.reference
+ } catch (e) {
+ return
+ }
+}
+
+export function recognizeSwarmHash(value: string): string {
+ return extractSwarmHash(value) || extractSwarmCid(value) || value
}
export function uuidV4(): string {