2020-08-06 23:56:29 +02:00
import events from 'events' ;
// LinkParser
//
// https://github.com/ravisorg/LinkParser
//
// Locate and extract almost any URL within a string. Handles protocol-less domains, IPv4 and
// IPv6, unrecognised TLDs, and more.
//
// This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
// http://creativecommons.org/licenses/by-sa/4.0/
( function ( ) {
// Original URL regex from the Android android.text.util.Linkify function, found here:
// http://stackoverflow.com/a/19696443
2019-01-10 15:39:37 +03:00
//
2020-08-06 23:56:29 +02:00
// However there were problems with it, most probably related to the fact it was
// written in 2007, and it's been highly modified.
2019-01-10 15:39:37 +03:00
//
2020-08-06 23:56:29 +02:00
// 1) I didn't like the fact that it was tied to specific TLDs, since new ones
// are being added all the time it wouldn't be reasonable to expect developer to
// be continually updating their regular expressions.
2019-01-10 15:39:37 +03:00
//
2020-08-06 23:56:29 +02:00
// 2) It didn't allow unicode characters in the domains which are now allowed in
// many languages, (including some IDN TLDs). Again these are constantly being
// added to and it doesn't seem reasonable to hard-code them. Note this ended up
// not being possible in standard JS due to the way it handles multibyte strings.
// It is possible using XRegExp, however a big performance hit results. Disabled
// for now.
//
// 3) It didn't allow for IPv6 hostnames
// IPv6 regex from http://stackoverflow.com/a/17871737
//
// 4) It was very poorly commented
//
// 5) It wasn't as smart as it could have been about what should be part of a
// URL and what should be part of human language.
const protocols = '(?:(?:http|https|rtsp|ftp):\\/\\/)' ;
const credentials = "(?:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,64}" // username (1-64 normal or url escaped characters)
+ "(?:\\:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,25})?" // followed by optional password (: + 1-25 normal or url escaped characters)
+ '\\@)' ;
// IPv6 Regex http://forums.intermapper.com/viewtopic.php?t=452
// by Dartware, LLC is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License
// http://intermapper.com/
const ipv6 = '('
+ '(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))'
+ '|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))'
+ '|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))'
+ '|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ '|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ '|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ '|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ '|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ ')(%.+)?' ;
const ipv4 = '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.'
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.'
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.'
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])' ;
// This would have been a lot cleaner if JS RegExp supported conditionals...
const linkRegExpString =
// begin match for protocol / username / password / host
'(?:'
// ============================
// If we have a recognized protocol at the beginning of the URL, we're
// more relaxed about what we accept, because we assume the user wants
// this to be a URL, and we're not accidentally matching human language
+ protocols + '?'
// optional username:password@
+ credentials + '?'
// IP address (both v4 and v6)
+ '(?:'
// IPv6
+ ipv6
// IPv4
+ '|' + ipv4
+ ')'
// end match for protocol / username / password / host
+ ')'
// optional port number
+ '(?:\\:\\d{1,5})?'
// plus optional path and query params (no unicode allowed here?)
+ '(?:'
+ '\\/(?:'
// some characters we'll accept because it's unlikely human language
// would use them after a URL unless they were part of the url
+ '(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])'
+ '|(?:\\%[a-f0-9]{2})'
// some characters are much more likely to be used AFTER a url and
// were not intended to be included in the url itself. Mostly end
// of sentence type things. It's also likely that the URL would
// still work if any of these characters were missing from the end
// because we parsed it incorrectly. For these characters to be accepted
// they must be followed by another character that we're reasonably
// sure is part of the url
+ "|(?:[\\;\\?\\:\\.\\!\\'\\(\\)\\,\\=]+(?=(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])|(?:\\%[a-f0-9]{2})))"
+ ')*'
+ '|\\b|\$'
+ ')' ;
// regex = XRegExp(regex,'gi');
const linkRegExp = RegExp ( linkRegExpString , 'gi' ) ;
const protocolRegExp = RegExp ( '^' + protocols , 'i' ) ;
// if url doesn't begin with a known protocol, add http by default
function ensureProtocol ( url ) {
if ( ! url . match ( protocolRegExp ) ) {
url = 'http://' + url ;
2019-01-10 15:39:37 +03:00
}
2020-08-06 23:56:29 +02:00
return url ;
}
2019-01-10 15:39:37 +03:00
2020-08-06 23:56:29 +02:00
// look for links in the text
const LinkParser = {
parse : function ( text ) {
const links = [ ] ;
let match ;
// eslint-disable-next-line no-cond-assign
while ( match = linkRegExp . exec ( text ) ) {
console . debug ( match ) ;
const txt = match [ 0 ] ;
const pos = match . index ;
const len = txt . length ;
const url = ensureProtocol ( text ) ;
links . push ( { 'pos' : pos , 'text' : txt , 'len' : len , 'url' : url } ) ;
2019-01-10 15:39:37 +03:00
}
2020-08-06 23:56:29 +02:00
return links ;
}
2019-01-10 15:39:37 +03:00
2020-08-06 23:56:29 +02:00
} ;
2019-01-10 15:39:37 +03:00
2020-08-06 23:56:29 +02:00
window . LinkParser = LinkParser ;
} ) ( ) ;
2018-10-23 01:05:09 +03:00
2020-08-06 23:56:29 +02:00
let cache = { } ;
2019-01-10 15:39:37 +03:00
2020-08-06 23:56:29 +02:00
// TODO: Replace with isIP (https://www.npmjs.com/package/is-ip)
function isValidIpAddress ( address ) {
const links = LinkParser . parse ( address ) ;
2018-10-23 01:05:09 +03:00
2020-08-06 23:56:29 +02:00
return links . length == 1 ;
}
2019-01-10 15:39:37 +03:00
2020-08-06 23:56:29 +02:00
// TODO: Add IPv6 support. Potentially replace with isLocalhost (https://www.npmjs.com/package/is-localhost-ip)
function isLocalIpAddress ( address ) {
address = address . toLowerCase ( ) ;
2019-01-10 15:39:37 +03:00
2020-08-06 23:56:29 +02:00
if ( address . includes ( '127.0.0.1' ) ) {
return true ;
}
if ( address . includes ( 'localhost' ) ) {
return true ;
2018-10-23 01:05:09 +03:00
}
2020-08-06 23:56:29 +02:00
return false ;
}
2019-01-10 15:39:37 +03:00
2020-08-16 16:12:33 +02:00
export function getServerAddress ( apiClient ) {
2020-08-06 23:56:29 +02:00
const serverAddress = apiClient . serverAddress ( ) ;
2019-01-10 15:39:37 +03:00
2020-08-06 23:56:29 +02:00
if ( isValidIpAddress ( serverAddress ) && ! isLocalIpAddress ( serverAddress ) ) {
return Promise . resolve ( serverAddress ) ;
2018-10-23 01:05:09 +03:00
}
2020-08-06 23:56:29 +02:00
const cachedValue = getCachedValue ( serverAddress ) ;
if ( cachedValue ) {
return Promise . resolve ( cachedValue ) ;
2018-10-23 01:05:09 +03:00
}
2020-08-06 23:56:29 +02:00
return apiClient . getEndpointInfo ( ) . then ( function ( endpoint ) {
if ( endpoint . IsInNetwork ) {
return apiClient . getPublicSystemInfo ( ) . then ( function ( info ) {
let localAddress = info . LocalAddress ;
if ( ! localAddress ) {
console . debug ( 'No valid local address returned, defaulting to external one' ) ;
localAddress = serverAddress ;
}
addToCache ( serverAddress , localAddress ) ;
return localAddress ;
} ) ;
} else {
addToCache ( serverAddress , serverAddress ) ;
return serverAddress ;
}
} ) ;
}
2018-10-23 01:05:09 +03:00
2020-08-06 23:56:29 +02:00
function clearCache ( ) {
cache = { } ;
}
2019-01-10 15:39:37 +03:00
2020-08-06 23:56:29 +02:00
function addToCache ( key , value ) {
cache [ key ] = {
value : value ,
time : new Date ( ) . getTime ( )
} ;
}
2019-01-10 15:39:37 +03:00
2020-08-06 23:56:29 +02:00
function getCachedValue ( key ) {
const obj = cache [ key ] ;
if ( obj && ( new Date ( ) . getTime ( ) - obj . time ) < 180000 ) {
return obj . value ;
2018-10-23 01:05:09 +03:00
}
2019-01-10 15:39:37 +03:00
2020-08-06 23:56:29 +02:00
return null ;
}
2019-01-10 15:39:37 +03:00
2020-08-30 06:06:47 +02:00
events . on ( window . connectionManager , 'localusersignedin' , clearCache ) ;
events . on ( window . connectionManager , 'localusersignedout' , clearCache ) ;
2020-08-06 23:56:29 +02:00
export default {
getServerAddress : getServerAddress
} ;