https://en.wikipedia.org/w/index.php?action=history&feed=atom&title=Module%3AURLutil Module:URLutil - Revision history 2025-05-25T04:08:25Z Revision history for this page on the wiki MediaWiki 1.45.0-wmf.2 https://en.wikipedia.org/w/index.php?title=Module:URLutil&diff=1126494969&oldid=prev ExE Boss: CopiedĀ from de:Modul:URLutil 2022-12-09T17:12:27Z <p>CopiedĀ from <a href="https://de.wikipedia.org/wiki/Modul:URLutil" class="extiw" title="de:Modul:URLutil">de:Modul:URLutil</a></p> <p><b>New page</b></p><div>local URLutil = { suite = &quot;URLutil&quot;,<br /> serial = &quot;2022-04-05&quot;,<br /> item = 10859193 }<br /> --[=[<br /> Utilities for URL etc. on www.<br /> * decode()<br /> * encode()<br /> * getAuthority()<br /> * getFragment()<br /> * getHost()<br /> * getLocation()<br /> * getNormalized()<br /> * getPath()<br /> * getPort()<br /> * getQuery()<br /> * getQueryTable()<br /> * getRelativePath()<br /> * getScheme()<br /> * getSortkey()<br /> * getTLD()<br /> * getTop2domain()<br /> * getTop3domain()<br /> * isAuthority()<br /> * isDomain()<br /> * isDomainExample()<br /> * isDomainInt()<br /> * isHost()<br /> * isHostPathResource()<br /> * isIP()<br /> * isIPlocal()<br /> * isIPv4()<br /> * isIPv6()<br /> * isMailAddress()<br /> * isMailLink()<br /> * isProtocolDialog()<br /> * isProtocolWiki()<br /> * isResourceURL()<br /> * isSuspiciousURL()<br /> * isUnescapedURL()<br /> * isWebURL()<br /> * wikiEscapeURL()<br /> * failsafe()<br /> Only [[dotted decimal]] notation for IPv4 expected.<br /> Does not support dotted hexadecimal, dotted octal, or single-number formats.<br /> IPv6 URL (bracketed) not yet implemented; might need Wikintax escaping anyway.<br /> ]=]<br /> local Failsafe = URLutil<br /> <br /> <br /> <br /> local decodeComponentProtect = { F = &quot;\&quot;#%&lt;&gt;[\]^`{|}&quot;,<br /> P = &quot;\&quot;#%&lt;&gt;[\]^`{|}/?&quot;,<br /> Q = &quot;\&quot;#%&lt;&gt;[\]^`{|}&amp;=+;,&quot;,<br /> X = &quot;\&quot;#%&lt;&gt;[\]^`{|}&amp;=+;,/?&quot; }<br /> <br /> <br /> <br /> local decodeComponentEscape = function ( averse, adapt )<br /> return adapt == 20 or adapt == 127 or<br /> decodeComponentProtect[ averse ]:find( string.char( adapt ),<br /> 1,<br /> true )<br /> end -- decodeComponentEscape()<br /> <br /> <br /> <br /> local decodeComponentML = function ( ask )<br /> local i = 1<br /> local j, n, s<br /> while ( i ) do<br /> i = ask:find( &quot;&amp;#[xX]%x%x+;&quot;, i )<br /> if i then<br /> j = ask:find( &quot;;&quot;, i + 3, true )<br /> s = ask:sub( i + 2, j - 1 ):upper()<br /> n = s:byte( 1, 1 )<br /> if n == 88 then<br /> n = tonumber( s:sub( 2 ), 16 )<br /> elseif s:match( &quot;^%d+$&quot; ) then<br /> n = tonumber( s )<br /> else<br /> n = false<br /> end<br /> if n then<br /> if n &gt;= 128 then<br /> s = string.format( &quot;&amp;#%d;&quot;, n )<br /> elseif decodeComponentEscape( &quot;X&quot;, n ) then<br /> s = string.format( &quot;%%%02X&quot;, n )<br /> else<br /> s = string.format( &quot;%c&quot;, n )<br /> end<br /> j = j + 1<br /> if i == 1 then<br /> ask = s .. ask:sub( j )<br /> else<br /> ask = string.format( &quot;%s%s%s&quot;,<br /> ask:sub( 1, i - 1 ),<br /> s,<br /> ask:sub( j ) )<br /> end<br /> end<br /> i = i + 1<br /> end<br /> end -- while i<br /> return ask<br /> end -- decodeComponentML()<br /> <br /> <br /> <br /> local decodeComponentPercent = function ( ask, averse )<br /> local i = 1<br /> local j, k, m, n<br /> while ( i ) do<br /> i = ask:find( &quot;%%[2-7]%x&quot;, i )<br /> if i then<br /> j = i + 1<br /> k = j + 1<br /> n = ask:byte( k, k )<br /> k = k + 1<br /> m = ( n &gt; 96 )<br /> if m then<br /> n = n - 32<br /> m = n<br /> end<br /> if n &gt; 57 then<br /> n = n - 55<br /> else<br /> n = n - 48<br /> end<br /> n = ( ask:byte( j, j ) - 48 ) * 16 + n<br /> if n == 39 and<br /> ask:sub( i + 3, i + 5 ) == &quot;%27&quot; then<br /> j = i + 6<br /> while ( ask:sub( j, j + 2 ) == &quot;%27&quot; ) do<br /> j = j + 3<br /> end -- while &quot;%27&quot;<br /> elseif decodeComponentEscape( averse, n ) then<br /> if m then<br /> ask = string.format( &quot;%s%c%s&quot;,<br /> ask:sub( 1, j ),<br /> m,<br /> ask:sub( k ) )<br /> end<br /> elseif i == 1 then<br /> ask = string.format( &quot;%c%s&quot;, n, ask:sub( k ) )<br /> else<br /> ask = string.format( &quot;%s%c%s&quot;,<br /> ask:sub( 1, i - 1 ),<br /> n,<br /> ask:sub( k ) )<br /> end<br /> i = j<br /> end<br /> end -- while i<br /> return ask<br /> end -- decodeComponentPercent()<br /> <br /> <br /> <br /> local getTopDomain = function ( url, mode )<br /> local r = URLutil.getHost( url )<br /> if r then<br /> local pattern = &quot;[%w%%%-]+%.%a[%w%-]*%a)$&quot;<br /> if mode == 3 then<br /> pattern = &quot;[%w%%%-]+%.&quot; .. pattern<br /> end<br /> r = mw.ustring.match( &quot;.&quot; .. r, &quot;%.(&quot; .. pattern )<br /> if not r then<br /> r = false<br /> end<br /> else<br /> r = false<br /> end<br /> return r<br /> end -- getTopDomain()<br /> <br /> <br /> <br /> local getHash = function ( url )<br /> local r = url:find( &quot;#&quot;, 1, true )<br /> if r then<br /> local i = url:find( &quot;&amp;#&quot;, 1, true )<br /> if i then<br /> local s<br /> while ( i ) do<br /> s = url:sub( i + 2 )<br /> if s:match( &quot;^%d+;&quot; ) or s:match( &quot;^x%x+;&quot; ) then<br /> r = url:find( &quot;#&quot;, i + 4, true )<br /> if r then<br /> i = url:find( &quot;&amp;#&quot;, i + 4, true )<br /> else<br /> i = false<br /> end<br /> else<br /> r = i + 1<br /> i = false<br /> end<br /> end -- while i<br /> end<br /> end<br /> return r<br /> end -- getHash()<br /> <br /> <br /> <br /> URLutil.decode = function ( url, enctype )<br /> local r, s<br /> if type( enctype ) == &quot;string&quot; then<br /> s = mw.text.trim( enctype )<br /> if s == &quot;&quot; then<br /> s = false<br /> else<br /> s = s:upper()<br /> end<br /> end<br /> r = mw.text.encode( mw.uri.decode( url, s ) )<br /> if r:find( &quot;[%[|%]]&quot; ) then<br /> local k<br /> r, k = r:gsub( &quot;%[&quot;, &quot;&amp;#91;&quot; )<br /> :gsub( &quot;|&quot;, &quot;&amp;#124;&quot; )<br /> :gsub( &quot;%]&quot;, &quot;&amp;#93;&quot; )<br /> end<br /> return r<br /> end -- URLutil.decode()<br /> <br /> <br /> <br /> URLutil.encode = function ( url, enctype )<br /> local k, r, s<br /> if type( enctype ) == &quot;string&quot; then<br /> s = mw.text.trim( enctype )<br /> if s == &quot;&quot; then<br /> s = false<br /> else<br /> s = s:upper()<br /> end<br /> end<br /> r = mw.uri.encode( url, s )<br /> k = r:byte( 1, 1 )<br /> if -- k == 35 or -- #<br /> k == 42 or -- *<br /> k == 58 or -- :<br /> k == 59 then -- ;<br /> r = string.format( &quot;%%%X%s&quot;, k, r:sub( 2 ) )<br /> end<br /> if r:find( &quot;[%[|%]]&quot; ) then<br /> r, k = r:gsub( &quot;%[&quot;, &quot;%5B&quot; )<br /> :gsub( &quot;|&quot;, &quot;%7C&quot; )<br /> :gsub( &quot;%]&quot;, &quot;%5D&quot; )<br /> end<br /> return r<br /> end -- URLutil.encode()<br /> <br /> <br /> <br /> URLutil.getAuthority = function ( url )<br /> local r<br /> if type( url ) == &quot;string&quot; then<br /> local colon, host, port<br /> local pattern = &quot;^%s*%w*:?//([%w%.%%_-]+)(:?)([%d]*)/&quot;<br /> local s = mw.text.decode( url )<br /> local i = s:find( &quot;#&quot;, 6, true )<br /> if i then<br /> s = s:sub( 1, i - 1 ) .. &quot;/&quot;<br /> else<br /> s = s .. &quot;/&quot;<br /> end<br /> host, colon, port = mw.ustring.match( s, pattern )<br /> if URLutil.isHost( host ) then<br /> host = mw.ustring.lower( host )<br /> if colon == &quot;:&quot; then<br /> if port:find( &quot;^[1-9]&quot; ) then<br /> r = ( host .. &quot;:&quot; .. port )<br /> end<br /> elseif #port == 0 then<br /> r = host<br /> end<br /> end<br /> else<br /> r = false<br /> end<br /> return r<br /> end -- URLutil.getAuthority()<br /> <br /> <br /> <br /> URLutil.getFragment = function ( url, decode )<br /> local r<br /> if type( url ) == &quot;string&quot; then<br /> local i = getHash( url )<br /> if i then<br /> r = mw.text.trim( url:sub( i ) ):sub( 2 )<br /> if type( decode ) == &quot;string&quot; then<br /> local encoding = mw.text.trim( decode )<br /> local launch<br /> if encoding == &quot;%&quot; then<br /> launch = true<br /> elseif encoding == &quot;WIKI&quot; then<br /> r = r:gsub( &quot;%.(%x%x)&quot;, &quot;%%%1&quot; )<br /> :gsub( &quot;_&quot;, &quot; &quot; )<br /> launch = true<br /> end<br /> if launch then<br /> r = mw.uri.decode( r, &quot;PATH&quot; )<br /> end<br /> end<br /> else<br /> r = false<br /> end<br /> else<br /> r = nil<br /> end<br /> return r<br /> end -- URLutil.getFragment()<br /> <br /> <br /> <br /> URLutil.getHost = function ( url )<br /> local r = URLutil.getAuthority( url )<br /> if r then<br /> r = mw.ustring.match( r, &quot;^([%w%.%%_%-]+):?[%d]*$&quot; )<br /> end<br /> return r<br /> end -- URLutil.getHost()<br /> <br /> <br /> <br /> URLutil.getLocation = function ( url )<br /> local r<br /> if type( url ) == &quot;string&quot; then<br /> r = mw.text.trim( url )<br /> if r == &quot;&quot; then<br /> r = false<br /> else<br /> local i<br /> i = getHash( r )<br /> if i then<br /> if i == 1 then<br /> r = false<br /> else<br /> r = r:sub( 1, i - 1 )<br /> end<br /> end<br /> end<br /> else<br /> r = nil<br /> end<br /> return r<br /> end -- URLutil.getLocation()<br /> <br /> <br /> <br /> URLutil.getNormalized = function ( url )<br /> local r<br /> if type( url ) == &quot;string&quot; then<br /> r = mw.text.trim( url )<br /> if r == &quot;&quot; then<br /> r = false<br /> else<br /> r = decodeComponentML( r )<br /> end<br /> else<br /> r = false<br /> end<br /> if r then<br /> local k = r:find( &quot;//&quot;, 1, true )<br /> if k then<br /> local j = r:find( &quot;/&quot;, k + 2, true )<br /> local sF, sP, sQ<br /> if r:find( &quot;%%[2-7]%x&quot; ) then<br /> local i = getHash( r )<br /> if i then<br /> sF = r:sub( i + 1 )<br /> r = r:sub( 1, i - 1 )<br /> if sF == &quot;&quot; then<br /> sF = false<br /> else<br /> sF = decodeComponentPercent( sF, &quot;F&quot; )<br /> end<br /> end<br /> i = r:find( &quot;?&quot;, 1, true )<br /> if i then<br /> sQ = r:sub( i )<br /> r = r:sub( 1, i - 1 )<br /> sQ = decodeComponentPercent( sQ, &quot;Q&quot; )<br /> end<br /> if j then<br /> if #r &gt; j then<br /> sP = r:sub( j + 1 )<br /> sP = decodeComponentPercent( sP, &quot;P&quot; )<br /> end<br /> r = r:sub( 1, j - 1 )<br /> end<br /> elseif j then<br /> local n = #r<br /> if r:byte( n, n ) == 35 then -- &#039;#&#039;<br /> n = n - 1<br /> r = r:sub( 1, n )<br /> end<br /> if n &gt; j then<br /> sP = r:sub( j + 1 )<br /> end<br /> r = r:sub( 1, j - 1 )<br /> end<br /> r = mw.ustring.lower( r ) .. &quot;/&quot;<br /> if sP then<br /> r = r .. sP<br /> end<br /> if sQ then<br /> r = r .. sQ<br /> end<br /> if sF then<br /> r = string.format( &quot;%s#%s&quot;, r, sF )<br /> end<br /> end<br /> r = r:gsub( &quot; &quot;, &quot;%%20&quot; )<br /> :gsub( &quot;%[&quot;, &quot;%%5B&quot; )<br /> :gsub( &quot;|&quot;, &quot;%%7C&quot; )<br /> :gsub( &quot;%]&quot;, &quot;%%5D&quot; )<br /> :gsub( &quot;%&lt;&quot;, &quot;%%3C&quot; )<br /> :gsub( &quot;%&gt;&quot;, &quot;%%3E&quot; )<br /> end<br /> return r<br /> end -- URLutil.getNormalized()<br /> <br /> <br /> <br /> URLutil.getPath = function ( url )<br /> local r = URLutil.getRelativePath( url )<br /> if r then<br /> local s = r:match( &quot;^([^%?]*)%?&quot; )<br /> if s then<br /> r = s<br /> end<br /> s = r:match( &quot;^([^#]*)#&quot; )<br /> if s then<br /> r = s<br /> end<br /> end<br /> return r<br /> end -- URLutil.getPath()<br /> <br /> <br /> <br /> URLutil.getPort = function ( url )<br /> local r = URLutil.getAuthority( url )<br /> if r then<br /> r = r:match( &quot;:([1-9][0-9]*)$&quot; )<br /> if r then<br /> r = tonumber( r )<br /> else<br /> r = false<br /> end<br /> end<br /> return r<br /> end -- URLutil.getPort()<br /> <br /> <br /> <br /> URLutil.getQuery = function ( url, key, separator )<br /> local r = URLutil.getLocation( url )<br /> if r then<br /> r = r:match( &quot;^[^%?]*%?(.+)$&quot; )<br /> if r then<br /> if type( key ) == &quot;string&quot; then<br /> local single = mw.text.trim( key )<br /> local sep = &quot;&amp;&quot;<br /> local s, scan<br /> if type( separator ) == &quot;string&quot; then<br /> s = mw.text.trim( separator )<br /> if s:match( &quot;^[&amp;;,/]$&quot; ) then<br /> sep = s<br /> end<br /> end<br /> s = string.format( &quot;%s%s%s&quot;, sep, r, sep )<br /> scan = string.format( &quot;%s%s=([^%s]*)%s&quot;,<br /> sep, key, sep, sep )<br /> r = s:match( scan )<br /> end<br /> end<br /> if not r then<br /> r = false<br /> end<br /> end<br /> return r<br /> end -- URLutil.getQuery()<br /> <br /> <br /> <br /> URLutil.getQueryTable = function ( url, separator )<br /> local r = URLutil.getQuery( url )<br /> if r then<br /> local sep = &quot;&amp;&quot;<br /> local n, pairs, s, set<br /> if type( separator ) == &quot;string&quot; then<br /> s = mw.text.trim( separator )<br /> if s:match( &quot;^[&amp;;,/]$&quot; ) then<br /> sep = s<br /> end<br /> end<br /> pairs = mw.text.split( r, sep, true )<br /> n = #pairs<br /> r = { }<br /> for i = 1, n do<br /> s = pairs[ i ]<br /> if s:find( &quot;=&quot;, 2, true ) then<br /> s, set = s:match( &quot;^([^=]+)=(.*)$&quot; )<br /> if s then<br /> r[ s ] = set<br /> end<br /> else<br /> r[ s ] = false<br /> end<br /> end -- for i<br /> end<br /> return r<br /> end -- URLutil.getQueryTable()<br /> <br /> <br /> <br /> URLutil.getRelativePath = function ( url )<br /> local r<br /> if type( url ) == &quot;string&quot; then<br /> local s = url:match( &quot;^%s*[a-zA-Z]*://(.*)$&quot; )<br /> if s then<br /> s = s:match( &quot;[^/]+(/.*)$&quot; )<br /> else<br /> local x<br /> x, s = url:match( &quot;^%s*(/?)(/.*)$&quot; )<br /> if x == &quot;/&quot; then<br /> s = s:match( &quot;/[^/]+(/.*)$&quot; )<br /> end<br /> end<br /> if s then<br /> r = mw.text.trim( s )<br /> elseif URLutil.isResourceURL( url ) then<br /> r = &quot;/&quot;<br /> else<br /> r = false<br /> end<br /> else<br /> r = nil<br /> end<br /> return r<br /> end -- URLutil.getRelativePath()<br /> <br /> <br /> <br /> URLutil.getScheme = function ( url )<br /> local r<br /> if type( url ) == &quot;string&quot; then<br /> local pattern = &quot;^%s*([a-zA-Z]*)(:?)(//)&quot;<br /> local prot, colon, slashes = url:match( pattern )<br /> r = false<br /> if slashes == &quot;//&quot; then<br /> if colon == &quot;:&quot; then<br /> if #prot &gt; 2 then<br /> r = prot:lower() .. &quot;://&quot;<br /> end<br /> elseif #prot == 0 then<br /> r = &quot;//&quot;<br /> end<br /> end<br /> else<br /> r = nil<br /> end<br /> return r<br /> end -- URLutil.getScheme()<br /> <br /> <br /> <br /> URLutil.getSortkey = function ( url )<br /> local r = url<br /> if type( url ) == &quot;string&quot; then<br /> local i = url:find( &quot;//&quot; )<br /> if i then<br /> local scheme<br /> if i == 0 then<br /> scheme = &quot;&quot;<br /> else<br /> scheme = url:match( &quot;^%s*([a-zA-Z]*)://&quot; )<br /> end<br /> if scheme then<br /> local s = url:sub( i + 2 )<br /> local comps, site, m, suffix<br /> scheme = scheme:lower()<br /> i = s:find( &quot;/&quot; )<br /> if i and i &gt; 1 then<br /> suffix = s:sub( i + 1 ) -- mw.uri.encode()<br /> s = s:sub( 1, i - 1 )<br /> suffix = suffix:gsub( &quot;#&quot;, &quot; &quot; )<br /> else<br /> suffix = &quot;&quot;<br /> end<br /> site, m = s:match( &quot;^(.+)(:%d+)$&quot; )<br /> if not m then<br /> site = s<br /> m = 0<br /> end<br /> comps = mw.text.split( site:lower(), &quot;.&quot;, true )<br /> r = &quot;///&quot;<br /> for i = #comps, 2, -1 do<br /> r = string.format( &quot;%s%s.&quot;, r, comps[ i ] )<br /> end -- for --i<br /> r = string.format( &quot;%s%s %d %s: %s&quot;,<br /> r, comps[ 1 ], m, scheme, suffix )<br /> end<br /> end<br /> end<br /> return r<br /> end -- URLutil.getSortkey()<br /> <br /> <br /> <br /> URLutil.getTLD = function ( url )<br /> local r = URLutil.getHost( url )<br /> if r then<br /> r = mw.ustring.match( r, &quot;%w+%.(%a[%w%-]*%a)$&quot; )<br /> if not r then<br /> r = false<br /> end<br /> end<br /> return r<br /> end -- URLutil.getTLD()<br /> <br /> <br /> <br /> URLutil.getTop2domain = function ( url )<br /> return getTopDomain( url, 2 )<br /> end -- URLutil.getTop2domain()<br /> <br /> <br /> <br /> URLutil.getTop3domain = function ( url )<br /> return getTopDomain( url, 3 )<br /> end -- URLutil.getTop3domain()<br /> <br /> <br /> <br /> URLutil.isAuthority = function ( s )<br /> local r<br /> if type( s ) == &quot;string&quot; then<br /> local pattern = &quot;^%s*([%w%.%%_-]+)(:?)(%d*)%s*$&quot;<br /> local host, colon, port = mw.ustring.match( s, pattern )<br /> if colon == &quot;:&quot; then<br /> port = port:match( &quot;^[1-9][0-9]*$&quot; )<br /> if type( port ) ~= &quot;string&quot; then<br /> r = false<br /> end<br /> elseif port ~= &quot;&quot; then<br /> r = false<br /> end<br /> r = URLutil.isHost( host )<br /> else<br /> r = nil<br /> end<br /> return r<br /> end -- URLutil.isAuthority()<br /> <br /> <br /> <br /> URLutil.isDomain = function ( s )<br /> local r<br /> if type( s ) == &quot;string&quot; then<br /> local scan = &quot;^%s*([%w%.%%_-]*%w)%.(%a[%w-]*%a)%s*$&quot;<br /> local scope<br /> s, scope = mw.ustring.match( s, scan )<br /> if type( s ) == &quot;string&quot; then<br /> if mw.ustring.find( s, &quot;^%w&quot; ) then<br /> if mw.ustring.find( s, &quot;..&quot;, 1, true ) then<br /> r = false<br /> else<br /> r = true<br /> end<br /> end<br /> end<br /> else<br /> r = nil<br /> end<br /> return r<br /> end -- URLutil.isDomain()<br /> <br /> <br /> <br /> URLutil.isDomainExample = function ( url )<br /> -- RFC 2606: example.com example.net example.org example.edu<br /> local r = getTopDomain( url, 2 )<br /> if r then<br /> local s = r:lower():match( &quot;^example%.([a-z][a-z][a-z])$&quot; )<br /> if s then<br /> r = ( s == &quot;com&quot; or<br /> s == &quot;edu&quot; or<br /> s == &quot;net&quot; or<br /> s == &quot;org&quot; )<br /> else<br /> r = false<br /> end<br /> end<br /> return r<br /> end -- URLutil.isDomainExample()<br /> <br /> <br /> <br /> URLutil.isDomainInt = function ( url )<br /> -- Internationalized Domain Name (Punycode)<br /> local r = URLutil.getHost( url )<br /> if r then<br /> if r:match( &quot;^[!-~]+$&quot; ) then<br /> local s = &quot;.&quot; .. r<br /> if s:find( &quot;.xn--&quot;, 1, true ) then<br /> r = true<br /> else<br /> r = false<br /> end<br /> else<br /> r = true<br /> end<br /> end<br /> return r<br /> end -- URLutil.isDomainInt()<br /> <br /> <br /> <br /> URLutil.isHost = function ( s )<br /> return URLutil.isDomain( s ) or URLutil.isIP( s )<br /> end -- URLutil.isHost()<br /> <br /> <br /> <br /> URLutil.isHostPathResource = function ( s )<br /> local r = URLutil.isResourceURL( s )<br /> if not r and s then<br /> r = URLutil.isResourceURL( &quot;//&quot; .. mw.text.trim( s ) )<br /> end<br /> return r<br /> end -- URLutil.isHostPathResource()<br /> <br /> <br /> <br /> URLutil.isIP = function ( s )<br /> return URLutil.isIPv4( s ) and 4 or URLutil.isIPv6( s ) and 6<br /> end -- URLutil.isIP()<br /> <br /> <br /> <br /> URLutil.isIPlocal = function ( s )<br /> -- IPv4 according to RFC 1918, RFC 1122; even any 0.0.0.0 (RFC 5735)<br /> local r = false<br /> local num = s:match( &quot;^ *([01][0-9]*)%.&quot; )<br /> if num then<br /> num = tonumber( num )<br /> if num == 0 then<br /> r = s:match( &quot;^ *0+%.[0-9]+%.[0-9]+%.[0-9]+ *$&quot; )<br /> elseif num == 10 or num == 127 then<br /> -- loopback; private/local host: 127.0.0.1<br /> r = URLutil.isIPv4( s )<br /> elseif num == 169 then<br /> -- 169.254.*.*<br /> elseif num == 172 then<br /> -- 172.(16...31).*.*<br /> num = s:match( &quot;^ *0*172%.([0-9]+)%.&quot; )<br /> if num then<br /> num = tonumber( num )<br /> if num &gt;= 16 and num &lt;= 31 then<br /> r = URLutil.isIPv4( s )<br /> end<br /> end<br /> elseif beg == 192 then<br /> -- 192.168.*.*<br /> num = s:match( &quot;^ *0*192%.([0-9]+)%.&quot; )<br /> if num then<br /> num = tonumber( num )<br /> if num == 168 then<br /> r = URLutil.isIPv4( s )<br /> end<br /> end<br /> end<br /> end<br /> if r then<br /> r = true<br /> end<br /> return r<br /> end -- URLutil.isIPlocal()<br /> <br /> <br /> <br /> URLutil.isIPv4 = function ( s )<br /> local function legal( n )<br /> return ( tonumber( n ) &lt; 256 )<br /> end<br /> local r = false<br /> if type( s ) == &quot;string&quot; then<br /> local p1, p2, p3, p4 = s:match( &quot;^%s*([1-9][0-9]?[0-9]?)%.([12]?[0-9]?[0-9])%.([12]?[0-9]?[0-9])%.([12]?[0-9]?[0-9])%s*$&quot; )<br /> if p1 and p2 and p3 and p4 then<br /> r = legal( p1 ) and legal( p2 ) and legal( p3 ) and legal( p4 )<br /> end<br /> end<br /> return r<br /> end -- URLutil.isIPv4()<br /> <br /> <br /> <br /> URLutil.isIPv6 = function ( s )<br /> local dcolon, groups<br /> if type( s ) ~= &quot;string&quot;<br /> or s:len() == 0<br /> or s:find( &quot;[^:%x]&quot; ) -- only colon and hex digits are legal chars<br /> or s:find( &quot;^:[^:]&quot; ) -- can begin or end with :: but not with single :<br /> or s:find( &quot;[^:]:$&quot; )<br /> or s:find( &quot;:::&quot; )<br /> then<br /> return false<br /> end<br /> s = mw.text.trim( s )<br /> s, dcolon = s:gsub( &quot;::&quot;, &quot;:&quot; )<br /> if dcolon &gt; 1 then<br /> return false<br /> end -- at most one ::<br /> s = s:gsub( &quot;^:?&quot;, &quot;:&quot; ) -- prepend : if needed, upper<br /> s, groups = s:gsub( &quot;:%x%x?%x?%x?&quot;, &quot;&quot; ) -- remove valid groups, and count them<br /> return ( ( dcolon == 1 and groups &lt; 8 ) or<br /> ( dcolon == 0 and groups == 8 ) )<br /> and ( s:len() == 0 or ( dcolon == 1 and s == &quot;:&quot; ) ) -- might be one dangling : if original ended with ::<br /> end -- URLutil.isIPv6()<br /> <br /> <br /> <br /> URLutil.isMailAddress = function ( s )<br /> if type( s ) == &quot;string&quot; then<br /> s = mw.ustring.match( s, &quot;^%s*[%w%.%%_-]+@([%w%.%%-]+)%s*$&quot; )<br /> return URLutil.isDomain( s )<br /> end<br /> return false<br /> end -- URLutil.isMailAddress()<br /> <br /> <br /> <br /> URLutil.isMailLink = function ( s )<br /> if type( s ) == &quot;string&quot; then<br /> local addr<br /> s, addr = mw.ustring.match( s, &quot;^%s*([Mm][Aa][Ii][Ll][Tt][Oo]):(%S[%w%.%%_-]*@[%w%.%%-]+)%s*$&quot; )<br /> if type( s ) == &quot;string&quot; then<br /> if s:lower() == &quot;mailto&quot; then<br /> return URLutil.isMailAddress( addr )<br /> end<br /> end<br /> end<br /> return false<br /> end -- URLutil.isMailLink()<br /> <br /> <br /> <br /> local function isProtocolAccepted( prot, supplied )<br /> if type( prot ) == &quot;string&quot; then<br /> local scheme, colon, slashes = mw.ustring.match( prot, &quot;^%s*([a-zA-Z]*)(:?)(/?/?)%s*$&quot; )<br /> if slashes ~= &quot;/&quot; then<br /> if scheme == &quot;&quot; then<br /> if colon ~= &quot;:&quot; and slashes == &quot;//&quot; then<br /> return true<br /> end<br /> elseif colon == &quot;:&quot; or slashes == &quot;&quot; then<br /> local s = supplied:match( &quot; &quot; .. scheme:lower() .. &quot; &quot; )<br /> if type( s ) == &quot;string&quot; then<br /> return true<br /> end<br /> end<br /> end<br /> end<br /> return false<br /> end -- isProtocolAccepted()<br /> <br /> <br /> <br /> URLutil.isProtocolDialog = function ( prot )<br /> return isProtocolAccepted( prot, &quot; mailto irc ircs ssh telnet &quot; )<br /> end -- URLutil.isProtocolDialog()<br /> <br /> <br /> <br /> URLutil.isProtocolWiki = function ( prot )<br /> return isProtocolAccepted( prot,<br /> &quot; ftp ftps git http https nntp sftp svn worldwind &quot; )<br /> end -- URLutil.isProtocolWiki()<br /> <br /> <br /> <br /> URLutil.isResourceURL = function ( url )<br /> local scheme = URLutil.getScheme( url )<br /> if scheme then<br /> local s = &quot; // http:// https:// ftp:// sftp:// &quot;<br /> s = s:find( string.format( &quot; %s &quot;, scheme ) )<br /> if s then<br /> if URLutil.getAuthority( url ) then<br /> if not url:match( &quot;%S%s+%S&quot; ) then<br /> local s1, s2 = url:match( &quot;^([^#]+)(#.*)$&quot; )<br /> if s2 then<br /> if url:match( &quot;^%s*[a-zA-Z]*:?//(.+)/&quot; ) then<br /> return true<br /> end<br /> else<br /> return true<br /> end<br /> end<br /> end<br /> end<br /> end<br /> return false<br /> end -- URLutil.isResourceURL()<br /> <br /> <br /> <br /> URLutil.isSuspiciousURL = function ( url )<br /> if URLutil.isResourceURL( url ) then<br /> local s = URLutil.getAuthority( url )<br /> local pat = &quot;[%[|%]&quot; ..<br /> mw.ustring.char( 34,<br /> 8201, 45, 8207,<br /> 8234, 45, 8239,<br /> 8288 )<br /> .. &quot;]&quot;<br /> if s:find( &quot;@&quot; )<br /> or url:find( &quot;&#039;&#039;&quot; )<br /> or url:find( pat )<br /> or url:find( &quot;[%.,]$&quot; ) then<br /> return true<br /> end<br /> -- TODO zero width character ??<br /> return false<br /> end<br /> return true<br /> end -- URLutil.isSuspiciousURL()<br /> <br /> <br /> <br /> URLutil.isUnescapedURL = function ( url, trailing )<br /> if type( trailing ) ~= &quot;string&quot; then<br /> if URLutil.isWebURL( url ) then<br /> if url:match( &quot;[%[|%]]&quot; ) then<br /> return true<br /> end<br /> end<br /> end<br /> return false<br /> end -- URLutil.isUnescapedURL()<br /> <br /> <br /> <br /> URLutil.isWebURL = function ( url )<br /> if URLutil.getScheme( url ) and URLutil.getAuthority( url ) then<br /> if not url:find( &quot;%S%s+%S&quot; ) and<br /> not url:find( &quot;&#039;&#039;&quot;, 1, true ) then<br /> return true<br /> end<br /> end<br /> return false<br /> end -- URLutil.isWebURL()<br /> <br /> <br /> <br /> URLutil.wikiEscapeURL = function ( url )<br /> if url:find( &quot;[%[|%]]&quot; ) then<br /> local n<br /> url, n = url:gsub( &quot;%[&quot;, &quot;&amp;#91;&quot; )<br /> :gsub( &quot;|&quot;, &quot;&amp;#124;&quot; )<br /> :gsub( &quot;%]&quot;, &quot;&amp;#93;&quot; )<br /> end<br /> return url<br /> end -- URLutil.wikiEscapeURL()<br /> <br /> <br /> <br /> Failsafe.failsafe = function ( atleast )<br /> -- Retrieve versioning and check for compliance<br /> -- Precondition:<br /> -- atleast -- string, with required version<br /> -- or wikidata|item|~|@ or false<br /> -- Postcondition:<br /> -- Returns string -- with queried version/item, also if problem<br /> -- false -- if appropriate<br /> -- 2020-08-17<br /> local since = atleast<br /> local last = ( since == &quot;~&quot; )<br /> local linked = ( since == &quot;@&quot; )<br /> local link = ( since == &quot;item&quot; )<br /> local r<br /> if last or link or linked or since == &quot;wikidata&quot; then<br /> local item = Failsafe.item<br /> since = false<br /> if type( item ) == &quot;number&quot; and item &gt; 0 then<br /> local suited = string.format( &quot;Q%d&quot;, item )<br /> if link then<br /> r = suited<br /> else<br /> local entity = mw.wikibase.getEntity( suited )<br /> if type( entity ) == &quot;table&quot; then<br /> local seek = Failsafe.serialProperty or &quot;P348&quot;<br /> local vsn = entity:formatPropertyValues( seek )<br /> if type( vsn ) == &quot;table&quot; and<br /> type( vsn.value ) == &quot;string&quot; and<br /> vsn.value ~= &quot;&quot; then<br /> if last and vsn.value == Failsafe.serial then<br /> r = false<br /> elseif linked then<br /> if mw.title.getCurrentTitle().prefixedText<br /> == mw.wikibase.getSitelink( suited ) then<br /> r = false<br /> else<br /> r = suited<br /> end<br /> else<br /> r = vsn.value<br /> end<br /> end<br /> end<br /> end<br /> end<br /> end<br /> if type( r ) == &quot;nil&quot; then<br /> if not since or since &lt;= Failsafe.serial then<br /> r = Failsafe.serial<br /> else<br /> r = false<br /> end<br /> end<br /> return r<br /> end -- Failsafe.failsafe()<br /> <br /> <br /> <br /> local function Template( frame, action, amount )<br /> -- Run actual code from template transclusion<br /> -- Precondition:<br /> -- frame -- object<br /> -- action -- string, with function name<br /> -- amount -- number, of args if &gt; 1<br /> -- Postcondition:<br /> -- Return string or not<br /> local n = amount or 1<br /> local v = { }<br /> local r, s<br /> for i = 1, n do<br /> s = frame.args[ i ]<br /> if s then<br /> s = mw.text.trim( s )<br /> if s ~= &quot;&quot; then<br /> v[ i ] = s<br /> end<br /> end<br /> end -- for i<br /> if v[ 1 ] then<br /> r = URLutil[ action ]( v[ 1 ], v[ 2 ], v[ 3 ] )<br /> end<br /> return r<br /> end -- Template()<br /> <br /> <br /> <br /> local p = {}<br /> <br /> function p.decode( frame )<br /> return Template( frame, &quot;decode&quot;, 2 ) or &quot;&quot;<br /> end<br /> function p.encode( frame )<br /> return Template( frame, &quot;encode&quot;, 2 ) or &quot;&quot;<br /> end<br /> function p.getAuthority( frame )<br /> return Template( frame, &quot;getAuthority&quot; ) or &quot;&quot;<br /> end<br /> function p.getFragment( frame )<br /> local r = Template( frame, &quot;getFragment&quot;, 2 )<br /> if r then<br /> r = &quot;#&quot; .. r<br /> else<br /> r = &quot;&quot;<br /> end<br /> return r<br /> end<br /> function p.getHost( frame )<br /> return Template( frame, &quot;getHost&quot; ) or &quot;&quot;<br /> end<br /> function p.getLocation( frame )<br /> return Template( frame, &quot;getLocation&quot; ) or &quot;&quot;<br /> end<br /> function p.getNormalized( frame )<br /> return Template( frame, &quot;getNormalized&quot; ) or &quot;&quot;<br /> end<br /> function p.getPath( frame )<br /> return Template( frame, &quot;getPath&quot; ) or &quot;&quot;<br /> end<br /> function p.getPort( frame )<br /> return Template( frame, &quot;getPort&quot; ) or &quot;&quot;<br /> end<br /> function p.getQuery( frame )<br /> local r = Template( frame, &quot;getQuery&quot;, 3 )<br /> if r then<br /> local key = frame.args[ 2 ]<br /> if key then<br /> key = mw.text.trim( key )<br /> if key == &quot;&quot; then<br /> key = nil<br /> end<br /> end<br /> if not key then<br /> r = &quot;?&quot; .. r<br /> end<br /> else<br /> r = &quot;&quot;<br /> end<br /> return r<br /> end<br /> function p.getRelativePath( frame )<br /> return Template( frame, &quot;getRelativePath&quot; ) or &quot;&quot;<br /> end<br /> function p.getScheme( frame )<br /> return Template( frame, &quot;getScheme&quot; ) or &quot;&quot;<br /> end<br /> function p.getSortkey( frame )<br /> return Template( frame, &quot;getSortkey&quot; ) or &quot;&quot;<br /> end<br /> function p.getTLD( frame )<br /> return Template( frame, &quot;getTLD&quot; ) or &quot;&quot;<br /> end<br /> function p.getTop2domain( frame )<br /> return Template( frame, &quot;getTop2domain&quot; ) or &quot;&quot;<br /> end<br /> function p.getTop3domain( frame )<br /> return Template( frame, &quot;getTop3domain&quot; ) or &quot;&quot;<br /> end<br /> function p.isAuthority( frame )<br /> return Template( frame, &quot;isAuthority&quot; ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.isDomain( frame )<br /> return Template( frame, &quot;isDomain&quot; ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.isDomainExample( frame )<br /> return Template( frame, &quot;isDomainExample&quot; ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.isDomainInt( frame )<br /> return Template( frame, &quot;isDomainInt&quot; ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.isHost( frame )<br /> return Template( frame, &quot;isHost&quot; ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.isHostPathResource( frame )<br /> return Template( frame, &quot;isHostPathResource&quot; ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.isIP( frame )<br /> return Template( frame, &quot;isIP&quot; ) or &quot;&quot;<br /> end<br /> function p.isIPlocal( frame )<br /> return Template( frame, &quot;isIPlocal&quot; ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.isIPv4( frame )<br /> return Template( frame, &quot;isIPv4&quot; ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.isIPv6( frame )<br /> return Template( frame, &quot;isIPv6&quot; ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.isMailAddress( frame )<br /> return Template( frame, &quot;isMailAddress&quot; ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.isMailLink( frame )<br /> return Template( frame, &quot;isMailLink&quot; ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.isProtocolDialog( frame )<br /> return Template( frame, &quot;isProtocolDialog&quot; ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.isProtocolWiki( frame )<br /> return Template( frame, &quot;isProtocolWiki&quot; ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.isResourceURL( frame )<br /> return Template( frame, &quot;isResourceURL&quot; ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.isSuspiciousURL( frame )<br /> return Template( frame, &quot;isSuspiciousURL&quot; ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.isUnescapedURL( frame )<br /> return Template( frame, &quot;isUnescapedURL&quot;, 2 ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.isWebURL( frame )<br /> return Template( frame, &quot;isWebURL&quot; ) and &quot;1&quot; or &quot;&quot;<br /> end<br /> function p.wikiEscapeURL( frame )<br /> return Template( frame, &quot;wikiEscapeURL&quot; )<br /> end<br /> p.failsafe = function ( frame )<br /> local s = type( frame )<br /> local since<br /> if s == &quot;table&quot; then<br /> since = frame.args[ 1 ]<br /> elseif s == &quot;string&quot; then<br /> since = frame<br /> end<br /> if since then<br /> since = mw.text.trim( since )<br /> if since == &quot;&quot; then<br /> since = false<br /> end<br /> end<br /> return Failsafe.failsafe( since ) or &quot;&quot;<br /> end<br /> function p.URLutil()<br /> return URLutil<br /> end<br /> <br /> return p</div> ExE Boss