Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unstoppabledomains/feature #875

Closed
12 changes: 10 additions & 2 deletions add-on/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,11 @@
"message": "Failed to stop IPFS node",
"description": "System notification title displayed when stopping an IPFS node fails (notify_stopIpfsNodeErrorTitle)"
},
"option_legend_readMore" : {
"option_legend_readMore": {
"message": "Read more",
"description": "A generic link in option description on the Preferences screen (option_legend_readMore)"
},
"option_header_nodeType" : {
"option_header_nodeType": {
"message": "IPFS Node",
"description": "A section header on the Preferences screen (option_header_nodeType)"
},
Expand Down Expand Up @@ -674,5 +674,13 @@
"page_landingWelcome_projects_title": {
"message": "Related Projects",
"description": "Projects section title (page_landingWelcome_projects_title)"
},
"option_supportUnstoppableDomains_title": {
"message": "Enable UnstoppableDomains (.crypto and .zil)",
"description": "An option title on the Preferences screen (option_supportUnstoppableDomains_title)"
},
"option_supportUnstoppableDomains_description": {
"message": "Allows visit decentralized ipfs sites with top level domain being .crypto or .zil",
"description": "An option description on the Preferences screen (option_supportUnstoppableDomains_description)"
}
}
8 changes: 8 additions & 0 deletions add-on/_locales/fi/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -674,5 +674,13 @@
"page_landingWelcome_projects_title": {
"message": "Related Projects",
"description": "Projects section title (page_landingWelcome_projects_title)"
},
"option_supportUnstoppableDomains_title": {
"message": "Enable UnstoppableDomains (.crypto and .zil)",
"description": "An option title on the Preferences screen (option_supportUnstoppableDomains_title)"
},
"option_supportUnstoppableDomains_description": {
"message": "Allows visit decentralized ipfs sites with top level domain being .crypto or .zil",
"description": "An option description on the Preferences screen (option_supportUnstoppableDomains_description)"
}
}
8 changes: 8 additions & 0 deletions add-on/_locales/ru/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -674,5 +674,13 @@
"page_landingWelcome_projects_title": {
"message": "Связанные проекты",
"description": "Projects section title (page_landingWelcome_projects_title)"
},
"option_supportUnstoppableDomains_title": {
"message": "Включить поддержку домейнов (.crypto и .zil)",
"description": "An option title on the Preferences screen (option_supportUnstoppableDomains_title)"
},
"option_supportUnstoppableDomains_description": {
"message": "Позволяет посещать сайты под домейнами .crypto и .zil",
"description": "An option description on the Preferences screen (option_supportUnstoppableDomains_description)"
}
}
Binary file added add-on/icons/png/ipfs-exclamation-point.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
91 changes: 91 additions & 0 deletions add-on/src/lib/blockchain-domains.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const { Resolution } = require('@unstoppabledomains/resolution')
const browser = require('webextension-polyfill')
// Main tool to resolve blockchain domains
const resolution = new Resolution({
blockchain: {
ens: false,
cns: { url: 'https://mainnet.infura.io/v3/7253dea473954208bede79ae26ae1225' }
}
})

function createUnstoppableDomainsController (getState) {
const loadingPageURL = browser.extension.getURL('dist/pages/unstoppableDomains/loading.html')
const enableOptionPageURL = browser.extension.getURL('dist/pages/unstoppableDomains/enableUnstoppableOption.html')

const rules = {
segmentMinLength: 2,
labelLength: 63,
domainLength: 253,
domainSegment: /^[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/,
tldSegment: /^[a-zA-Z](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/
}

function isValidDNSHostname (hostname) {
if (!hostname || hostname.length > rules.domainLength) return false
const labels = hostname.split('.')
if (labels.length < rules.segmentMinLength) {
return false
}
return labels.every((label, i) => {
if (i < labels.length - 1) {
return rules.domainSegment.test(label) && label.length <= rules.labelLength
}
return rules.tldSegment.test(label)
})
}

return {
isSupportedDomain (request) {
const url = new URL(request.url)
const domain = url.hostname
return resolution.isSupportedDomain(domain)
},

domainResolution (request, ipfsPathValidator) {
var state = getState()
if (!state.supportUnstoppableDomains) {
browser.tabs.update({ url: enableOptionPageURL })
return { cancel: true }
}
const url = new URL(request.url)
const domain = url.hostname
console.log('trying to redirect to loading while hash is generating?', loadingPageURL)
browser.tabs.update({ url: loadingPageURL })
console.log('oinside hte redirect...')
return resolution.ipfsHash(domain).then(hash => {
const redirectUrl = ipfsPathValidator.resolveToPublicUrl(`/ipfs/${hash}${url.pathname}`)
browser.tabs.update({ url: redirectUrl })
return { cancel: true }
}).catch(err => {
console.log('capture error!', err.code)
const errorPageURL = loadingPageURL + `?error=${err.code}&domain=${domain}`
browser.tabs.update({ url: errorPageURL })
})
},
isSearchEngine (request) {
const url = new URL(request.url)
console.log(url)
const params = url.searchParams.get('q')
console.log(params)
if (params) return true
return false
},
parseSearchEngine (requestDetails) {
const url = new URL(requestDetails.url)
const params = url.searchParams.get('q').trim().toLowerCase()
const q = new URL(url.protocol + '//' + params)
if (
!q.hostname ||
!isValidDNSHostname(q.hostname) ||
!resolution.isSupportedDomain(q.hostname) ||
url.pathname !== '/search'
) {
return
}
browser.tabs.update({ url: q.toString() })
return { cancel: true }
}
}
}

exports.createUnstoppableDomainsController = createUnstoppableDomainsController
2 changes: 1 addition & 1 deletion add-on/src/lib/ipfs-companion.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ const { createContextMenus, findValueForContext, contextMenuCopyAddressAtPublicG
const createIpfsProxy = require('./ipfs-proxy')
const { registerSubdomainProxy } = require('./http-proxy')
const { showPendingLandingPages } = require('./on-installed')

// init happens on addon load in background/background.js
module.exports = async function init () {
// INIT
Expand Down Expand Up @@ -687,6 +686,7 @@ module.exports = async function init () {
case 'preloadAtPublicGateway':
case 'openViaWebUI':
case 'noIntegrationsHostnames':
case 'supportUnstoppableDomains':
case 'dnslinkRedirect':
state[key] = change.newValue
break
Expand Down
13 changes: 11 additions & 2 deletions add-on/src/lib/ipfs-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const isIPFS = require('is-ipfs')
const isFQDN = require('is-fqdn')
const { pathAtHttpGateway, sameGateway } = require('./ipfs-path')
const { safeURL } = require('./options')

const { createUnstoppableDomainsController } = require('./blockchain-domains')
const redirectOptOutHint = 'x-ipfs-companion-no-redirect'
const recoverableNetworkErrors = new Set([
// Firefox
Expand Down Expand Up @@ -114,6 +114,8 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru
return isIgnored(request.requestId)
}

const unstoppableDomainsController = createUnstoppableDomainsController(getState)

// Build RequestModifier
return {
// browser.webRequest.onBeforeRequest
Expand Down Expand Up @@ -147,6 +149,14 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru
return fix
}
}
if (unstoppableDomainsController.isSearchEngine(request)) {
console.log('found search Engine')
return unstoppableDomainsController.parseSearchEngine(request)
}
if (unstoppableDomainsController.isSupportedDomain(request)) {
console.log('catched', request.url)
return unstoppableDomainsController.domainResolution(request, ipfsPathValidator)
}
// handler for protocol_handlers from manifest.json
if (redirectingProtocolRequest(request)) {
// fix path passed via custom protocol
Expand All @@ -170,7 +180,6 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru
if (state.dnslinkRedirect) {
const redirectUrl = dnslinkResolver.dnslinkAtGateway(request.url)
if (redirectUrl && isSafeToRedirect(request, runtime)) {
// console.log('onBeforeRequest.dnslinkRedirect', dnslinkRedirect)
return { redirectUrl }
}
} else if (state.dnslinkDataPreload) {
Expand Down
3 changes: 2 additions & 1 deletion add-on/src/lib/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ exports.optionDefaults = Object.freeze({
ipfsProxy: true, // window.ipfs
logNamespaces: 'jsipfs*,ipfs*,libp2p:mdns*,libp2p-delegated*,-*:ipns*,-ipfs:preload*,-ipfs-http-client:request*,-ipfs:http-api*',
importDir: '/ipfs-companion-imports/%Y-%M-%D_%h%m%s/',
openViaWebUI: true
openViaWebUI: true,
supportUnstoppableDomains: false
})

function buildCustomGatewayUrl () {
Expand Down
11 changes: 11 additions & 0 deletions add-on/src/options/forms/experiments-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const html = require('choo/html')
const switchToggle = require('../../pages/components/switch-toggle')

function experimentsForm ({
supportUnstoppableDomains,
displayNotifications,
catchUnhandledProtocols,
linkify,
Expand All @@ -22,6 +23,7 @@ function experimentsForm ({
const onrecoverFailedHttpRequestsChange = onOptionChange('recoverFailedHttpRequests')
const onDetectIpfsPathHeaderChange = onOptionChange('detectIpfsPathHeader')
const onIpfsProxyChange = onOptionChange('ipfsProxy')
const onSupportUnstoppableDomainsChange = onOptionChange('supportUnstoppableDomains')

return html`
<form>
Expand All @@ -37,6 +39,15 @@ function experimentsForm ({
</label>
<div>${switchToggle({ id: 'displayNotifications', checked: displayNotifications, onchange: onDisplayNotificationsChange })}</div>
</div>
<div>
<label for="supportUnstoppableDomains">
<dl>
<dt>${browser.i18n.getMessage('option_supportUnstoppableDomains_title')}</dt>
<dd>${browser.i18n.getMessage('option_supportUnstoppableDomains_description')}</dd>
</dl>
</label>
<div>${switchToggle({ id: 'supportUnstoppableDomains', checked: supportUnstoppableDomains, onchange: onSupportUnstoppableDomainsChange })} </div>
</div>
<div>
<label for="catchUnhandledProtocols">
<dl>
Expand Down
1 change: 1 addition & 0 deletions add-on/src/options/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ module.exports = function optionsPage (state, emit) {
onOptionChange
})}
${experimentsForm({
supportUnstoppableDomains: state.options.supportUnstoppableDomains,
displayNotifications: state.options.displayNotifications,
catchUnhandledProtocols: state.options.catchUnhandledProtocols,
linkify: state.options.linkify,
Expand Down
20 changes: 20 additions & 0 deletions add-on/src/pages/unstoppableDomains/enableUnstoppableOption.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="./style.css" />
<link rel="stylesheet" type="text/css" href="/ui-kit/tachyons.css" />
<link rel="stylesheet" type="text/css" href="/ui-kit/ipfs.css" />
</head>
<body class="bg-navy">
<div class="window">
<div class="icon">
<img class="warning snow" src="glyph_info.svg" />
</div>
<div class="message moon-gray montserrat fw2 f3 ma0 pt0 pb2 tc">
<h1 class="">Ipfs-companion prevent this page from loading</h3>
<p>please opt-in support for unstoppable domains in your preferences</p>
</div>
</div>
</body>
</html>
1 change: 1 addition & 0 deletions add-on/src/pages/unstoppableDomains/glyph_info.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions add-on/src/pages/unstoppableDomains/loading.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="./style.css" />
<link rel="stylesheet" type="text/css" href="./spinner.css" />
<link rel="stylesheet" type="text/css" href="/ui-kit/tachyons.css" />
<link rel="stylesheet" type="text/css" href="/ui-kit/ipfs.css" />
</head>
<body class="bg-navy">
<div id="root">
</div>
<script src="parseError.js"></script>
</body>
</html>
64 changes: 64 additions & 0 deletions add-on/src/pages/unstoppableDomains/parseError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
console.log('script launched')

var root = document.getElementById('root')

const loadingHTML = () => `<div class="loader-wrapper">
<span class="loader"><span class="loader-inner"></span></span>
</div>
<div class="moon-gray montserrat fw2 f3 ma0 pt0 pb2 tc">
<h2>Resolving Unstoppable domain</h2>
</div>`

const errorHTML = (error) => ` <div class="moon-gray montserrat fw2 f3 ma0 pt0 pb2 tc">
<h2>${error}</h2>
</div>`

const urlParams = getAllUrlParams(window.location.href)
if (urlParams.error) {
const errorMessage = parseResolutionError(urlParams.error, urlParams.domain)
root.innerHTML += errorHTML(errorMessage)
} else {
root.innerHTML += loadingHTML()
}

function parseResolutionError (errorcode, domain) {
console.log({ errorcode, domain })
switch (errorcode) {
case 'RecordNotFound':
return `No ipfs record found for ${domain}`
case 'UnregisteredDomain':
return `Domain ${domain} is not registered`
default:
return `Something went wrong ${errorcode}`
}
}

function getAllUrlParams (url) {
// get query string from url (optional) or window
var queryString = url ? url.split('?')[1] : window.location.search.slice(1)

// we'll store the parameters here
var obj = {}

if (queryString) {
queryString = queryString.split('#')[0]
var arr = queryString.split('&')

for (var i = 0; i < arr.length; i++) {
// separate the keys and the values
var a = arr[i].split('=')
var paramName = a[0]
var paramValue = typeof (a[1]) === 'undefined' ? true : a[1]

if (!obj[paramName]) {
obj[paramName] = paramValue
} else if (obj[paramName] && typeof obj[paramName] === 'string') {
obj[paramName] = [obj[paramName]]
obj[paramName].push(paramValue)
} else {
obj[paramName].push(paramValue)
}
}
}
return obj
}
Loading