import * as http from "http" ; import * as url from "url" ; import * as fs from "fs" ; import * as path from "path" ; import mime = require ( 'mime' ) ; import { format , promisify } from "util" ; import * as JSON5 from "json5" ; import * as send from "send" ; import * as WebSocket from "ws" ; import { Stats , appendFileSync } from "fs" ; import { gzip , createGzip } from "zlib" ; import { Writable , Stream } from "stream" ; import * as https from "https" ; import { networkInterfaces , NetworkInterfaceInfo } from "os" ; import * as ipcalc from "./ipcalc" ; import { NewTreeOptions , NewTreePathOptions_Auth , NewTreePathOptions_Index , ServerConfig , ServerConfigBase , ServerConfigSchema , ServerConfig_AccessOptions , ServerConfig_BindInfo , normalizeSettings , ConvertSettings , NewTreeOptionsObject , Config , OptionsConfig , } from "./server-config" ; import { JsonError , keys } from "./utils-functions" ; import { checkServerConfigSchema } from "./interface-checker" ; export { Config , NewTreeOptions , NewTreePathOptions_Auth , NewTreePathOptions_Index , ServerConfig , ServerConfigBase , ServerConfigSchema , ServerConfig_AccessOptions , normalizeSettings , ConvertSettings , } ; declare const __non_webpack_require__ ; const nodeRequire = typeof __non_webpack_require__ !== "undefined" ? __non_webpack_require__ : require ; type PromiseType < T > = T extends Promise < infer R > ? R : any ; type PromiseReturnType < T extends ( ... args : any ) => any > = ReturnType < T > extends Promise < infer R > ? R : any ; interface Async < T > extends Promise < T > { readonly type : T ; } export function as < T > ( obj ) { return obj ; } type DebugFunc = ( level : number , str : string | NodeJS . ErrnoException , ... args : any [ ] ) => any ; interface ServerEventsListener < THIS > { ( event , listener ) : THIS ; ( event , listener ) : THIS ; ( event , listener ) : THIS ; ( event , listener ) : THIS ; ( event , listener ) : THIS ; ( event , listener ) : THIS ; ( event , listener ) : THIS ; } export type ServerEvents = { "websocket-connection" : readonly [ RequestEvent ] ; "settingsChanged" : readonly [ ( keyof ServerConfig ) [ ] ] ; "settings" : readonly [ ServerConfig ] ; "stateError" : readonly [ StateObject ] ; "stateDebug" : readonly [ StateObject ] ; "serverOpen" : readonly [ { servers : import ( "./server" ) . Listener [ ] ; hosts : string [ ] ; https : boolean ; dryRun : boolean ; } ] ; "serverClose" : readonly [ string ] ; } ; export type ServerEventEmitter = EventEmitter < ServerEvents > ; export const TAGS = [ "B" , "KB" , "MB" , "GB" , "TB" , "PB" ] as const ; export function getHumanSize ( size ) { let power = 0 ; while ( size >= 1024 ) { size /= 1024 ; power ++ ; } return size . toFixed ( 1 ) + TAGS [ power ] ; } export type Hashmap < T > = { [ K ] : T ; } ; export type FolderEntryType = "folder" | "datafolder" | "htmlfile" | "other" | "error" ; export interface DirectoryEntry { name : string ; type : string ; path : string ; size : string ; } export interface Directory { path : string ; entries : DirectoryEntry [ ] ; type : string ; } export function isError ( obj ) { return ! ! obj && obj . constructor === Error ; } export function isErrnoException ( obj ) { return isError ( obj ) ; } export function sanitizeJSON ( key , value ) { if ( ! key ) { return value ; } else if ( key . substring ( 0 , 1 ) === "$" ) return ; else if ( key . substring ( 0 , 1 ) === "_" ) return ; else return value ; } export interface ServeStaticResult { status : number ; headers : { } ; message : string ; } export function serveFile ( state , file , root ) { promisify ( fs . stat ) ( root ? path . join ( root , file ) : file ) . then ( ( stat ) : any => { state . send ( { root , filepath : file , error : err => { state . log ( 2 , "%s %s" , err . status , err . message ) . throw ( 500 ) ; } , } ) ; } , err => { state . throw < StateObject > ( 404 ) ; } ) ; } export function serveFolder ( state , mount , root , serveIndex ? ) { const pathname = state . url . pathname ; if ( state . url . pathname . slice ( 0 , mount . length ) !== mount ) { state . log ( 2 , "URL is different than the mount point %s" , mount ) . throw ( 500 ) ; } else { state . send ( { root , filepath : pathname . slice ( mount . length ) , error : err => { state . log ( - 1 , "%s %s" , err . status , err . message ) . throw ( 404 ) ; } , directory : filepath => { if ( serveIndex ) { serveIndex ( state , filepath ) ; } else { state . throw ( 403 ) ; } } , } ) ; } } export function serveFolderIndex ( options ) { async function readFolder ( folder ) { let files = await promisify ( fs . readdir ) ( folder ) ; let res = { directory : [ ] , file : [ ] } ; await Promise . all ( files . map ( file => promisify ( fs . stat ) ( path . join ( folder , file ) ) . then ( stat => { let itemtype = stat . isDirectory ( ) ? "directory" : stat . isFile ( ) ? "file" : "other" ; res [ itemtype ] . push ( file ) ; } , x => undefined ) ) ) ; return res ; } if ( options . type === "json" ) { return function ( state , folder ) { readFolder ( folder ) . then ( item => { sendResponse ( state , JSON . stringify ( item ) , { contentType : "application/json" , doGzip : canAcceptGzip ( state . req ) , } ) ; } ) ; } ; } } export function canAcceptGzip ( header ) { if ( ( ( a ) : a is { headers : http . IncomingHttpHeaders ; } => typeof a === "object" ) ( header ) ) { header = header . headers [ "accept-encoding" ] as string ; } var gzip = header . split ( "," ) . map ( e => e . split ( ";" ) ) . filter ( e => e [ 0 ] === "gzip" ) [ 0 ] ; var can = ! ! gzip && ! ! gzip [ 1 ] && parseFloat ( gzip [ 1 ] . split ( "=" ) [ 1 ] ) > 0 ; return can ; } export function sendResponse ( state , body , options = { } ) { body = ! Buffer . isBuffer ( body ) ? Buffer . from ( body , "utf8" ) : body ; if ( options . doGzip ) gzip ( body , ( err , gzBody ) => { if ( err ) _send ( body , false ) ; else _send ( gzBody , true ) ; } ) ; else _send ( body , false ) ; function _send ( body , isGzip ) { state . setHeaders ( { "Content-Length" : Buffer . isBuffer ( body ) ? body . length . toString ( ) : Buffer . byteLength ( body , "utf8" ) . toString ( ) , "Content-Type" : options . contentType || "text/plain; charset=utf-8" , } ) ; if ( isGzip ) state . setHeaders ( { "Content-Encoding" : "gzip" } ) ; state . respond ( 200 ) . buffer ( body ) ; } } import { generateDirectoryListing } from "./generate-directory-listing" ; import { generateDirectoryRSS } from "./generate-directory-rss" ; import { RequestEvent } from "./request-event" ; export type DirectoryIndexData = { keys : string [ ] ; paths : ( string | true ) [ ] ; dirpath : string ; type : "group" | "folder" ; } ; export type DirectoryIndexListing = { path : string ; entries : { name : string ; path : string ; icon : string ; type : "error" | "folder" | "datafolder" | "file" | "group" ; size : string ; mime : string ; modified : number ; } [ ] ; type : "group" | "folder" | 403 | 404 ; } ; export type DirectoryIndexOptions = { upload : boolean ; mkdir : boolean ; format : "json" | "html" | "rss" ; mixFolders : boolean ; isLoggedIn : string | false ; extIcons : { [ ext_mime ] : string ; } ; sort : string [ ] ; } ; export type DirectoryIndexEntry = DirectoryIndexListing [ "entries" ] [ number ] ; export const DirectoryIndexKeys = keys < { [ K in keyof DirectoryIndexEntry ] : undefined ; } > ( { name : undefined , size : undefined , icon : undefined , mime : undefined , modified : undefined , path : undefined , type : undefined } ) ; export async function sendDirectoryIndex ( _r , options ) { let { keys , paths , dirpath , type } = _r ; let entries = await Promise . all ( keys . map ( async ( key , i ) => { let statpath = paths [ i ] ; let stat = statpath === true ? undefined : await statPath ( statpath , false ) ; const nameparts = key . indexOf ( "." ) !== - 1 ? key . split ( "." ) . pop ( ) : "" ; const list = { name : key , path : key + ( ! stat || stat . itemtype === "folder" ? "/" : "" ) , type : ! stat ? "group" : stat . itemtype , icon : ! stat ? "group.png" : ( stat . itemtype === "file" ) ? ( nameparts && options . extIcons [ nameparts ] || "other.png" ) : ( stat . itemtype as string + ".png" ) , size : stat && stat . stat ? getHumanSize ( stat . stat . size ) : "" , mime : ( stat ?. itemtype === "file" ) ? mime . lookup ( statpath , "" ) : "" , modified : stat ?. stat ?. mtimeMs || 0 , } ; return list ; } ) ) ; sortDirectoryEntries ( entries , options ) ; if ( options . format === "json" ) { return JSON . stringify ( { path : dirpath , entries , type , options } , null , 2 ) ; } else if ( options . format === "rss" ) { let def = { path : dirpath , entries , type } ; return generateDirectoryRSS ( def , options ) ; } else { let def = { path : dirpath , entries , type } ; return generateDirectoryListing ( def , options ) ; } } export const directorySorters = { name : ( a , b , opts ) => ( opts . mixFolders ? 0 : directorySorters . type ( a , b , opts ) ) || a . name . localeCompare ( b . name ) , size : ( a , b , opts ) => TAGS . findIndex ( e => a . size . endsWith ( e ) ) - TAGS . findIndex ( e => b . size . endsWith ( e ) ) || + a . size - + b . size , modified : ( a , b , opts ) => b . modified - a . modified , path : ( a , b , opts ) => a . path . localeCompare ( b . path ) , icon : ( a , b , opts ) => a . icon . localeCompare ( b . icon ) , mime : ( a , b , opts ) => a . mime . localeCompare ( b . mime ) , type : ( a , b , opts ) => + ( a . type === "file" ) - + ( b . type === "file" ) , } ; /** sort directory entries in place according to sort array*/ export function sortDirectoryEntries ( entries , opts ) { var { sort } = opts ; return entries . sort ( ( a , b ) => { for ( var i = 0 , diff = 0 , e = sort [ i ] , reverse = false ; i < sort . length && ! diff ; ( i ++ , e = sort [ i ] ) ) { reverse = e . startsWith ( "-" ) ; if ( reverse ) e = e . substr ( 1 ) ; diff = directorySorters [ e ] ( a , b , opts ) ; } if ( reverse ) return - diff ; else return diff ; } ) ; } /**
 * If the path
 */ export async function statWalkPath ( test ) { if ( ! Config . isPath ( test . item ) ) { console . log ( test . item ) ; throw "property item must be a TreePath" ; } let noDataFolder = Config . isPath ( test . item ) ? ! ! test . item . noDataFolder : false ; let n = { statpath : "" , index : - 1 , endStat : false } ; let stats = [ test . item . path , ... test . filepathPortion ] . map ( e => { return ( n = { statpath : path . join ( n . statpath , e ) , index : n . index + 1 , endStat : false , } ) ; } ) ; while ( true ) { let s = stats . shift ( ) ; if ( ! s ) throw new Error ( "PROGRAMMER ERROR" ) ; let res = await statPath ( s , noDataFolder ) ; if ( res . endStat || stats . length === 0 ) return res ; } } export function statsafe ( p ) { return promisify ( fs . stat ) ( p ) . catch ( x => undefined ) ; } param { ( { statpath : string , index : number , endStat : boolean } | string ) } s returns export async function statPath ( s , noDataFolder ) { if ( typeof s === "string" ) s = { statpath : s , index : 0 } ; const { statpath , index } = s ; let stat = await statsafe ( statpath ) ; let endStat = ! stat || ! stat . isDirectory ( ) ; let infostat = undefined ; if ( ! endStat && ! noDataFolder ) { infostat = await statsafe ( path . join ( statpath , "tiddlywiki.info" ) ) ; endStat = ! ! infostat && infostat . isFile ( ) ; } return { stat , statpath , index , endStat , itemtype : getItemType ( stat , ! noDataFolder && infostat ) , infostat : noDataFolder && infostat && infostat . isFile ( ) ? infostat : undefined , } as StatPathResult ; } function getItemType ( stat , infostat ) { let itemtype ; if ( ! stat ) itemtype = "error" ; else if ( stat . isDirectory ( ) ) itemtype = ! ! infostat ? "datafolder" : "folder" ; else if ( stat . isFile ( ) || stat . isSymbolicLink ( ) ) itemtype = "file" ; else itemtype = "error" ; return itemtype ; } export function treeWalker ( tree , reqpath ) { function getAncesterEntry ( a ) { return Object . assign ( { } , a , { $children : undefined } ) ; } var item = tree ; var ancestry = [ ] ; var folderPathFound = Config . isPath ( item ) ; for ( var end = 0 ; end < reqpath . length ; end ++ ) { if ( Config . isPath ( item ) ) { folderPathFound = true ; break ; } let t = item . $children . find ( ( e ) : e is Config . GroupElement | Config . FolderElement => ( Config . isGroup ( e ) || Config . isPath ( e ) ) && e . key === reqpath [ end ] ) ; if ( t ) { ancestry . push ( item ) ; item = t ; } else { break ; } } return { item , end , folderPathFound , ancestry } as TreePathResult ; } export function resolvePath ( state , tree ) { var reqpath ; if ( Array . isArray ( state ) ) { reqpath = state ; } else { reqpath = state . path ; } reqpath = decodeURI ( reqpath . slice ( ) . filter ( a => a ) . join ( "/" ) ) . split ( "/" ) . filter ( a => a ) ; if ( ! reqpath . every ( a => a !== ".." && a !== "." ) ) return ; var result = treeWalker ( tree , reqpath ) ; if ( reqpath . length > result . end && ! result . folderPathFound ) return ; let filepathPortion = reqpath . slice ( result . end ) . map ( a => a . trim ( ) ) ; const fullfilepath = Config . isPath ( result . item ) ? path . join ( result . item . path , ... filepathPortion ) : "" ; return { item : result . item , ancestry : result . ancestry , treepathPortion : reqpath . slice ( 0 , result . end ) , filepathPortion , reqpath , fullfilepath , } ; } type NodeCallback < T , S > = [ NodeJS . ErrnoException , T , S ] ; export function fs_move ( oldPath , newPath , callback ) { fs . rename ( oldPath , newPath , function ( err ) { if ( err ) { if ( err . code === "EXDEV" ) { copy ( ) ; } else { callback ( err ) ; } return ; } callback ( ) ; } ) ; function copy ( ) { var readStream = fs . createReadStream ( oldPath ) ; var writeStream = fs . createWriteStream ( newPath ) ; readStream . on ( "error" , callback ) ; writeStream . on ( "error" , callback ) ; readStream . on ( "close" , function ( ) { fs . unlink ( oldPath , callback ) ; } ) ; readStream . pipe ( writeStream ) ; } } export class StateError extends Error { state : StateObject ; constructor ( state , message ) { super ( message ) ; this . state = state ; } } export interface IStatPathResult < IT , ST , IFST , END > { stat : ST ; infostat : IFST ; index : number ; endStat : END ; itemtype : IT ; statpath : string ; } export type StatPathResult = IStatPathResult < "error" , fs . Stats | undefined , undefined , true > | IStatPathResult < "folder" , fs . Stats , undefined , false > | IStatPathResult < "datafolder" , fs . Stats , fs . Stats , true > | IStatPathResult < "file" , fs . Stats , undefined , true > ; export type getStatPathResult < T extends StatPathResult [ "itemtype" ] > = Extract < StatPathResult , { itemtype : T ; } > ; export class URLSearchParams { constructor ( str ) { } } export interface StateObjectUrl { path : string ; pathname : string ; query : Hashmap < string [ ] | string | undefined > ; search : string ; href : string ; } type StandardResponseHeaderValue = number | string | string [ ] | undefined ; export interface StandardResponseHeaders { /** Specifying which web sites can participate in cross-origin resource sharing */ "Access-Control-Allow-Origin" ? : string ; /** Specifying which web sites can participate in cross-origin resource sharing */ "Access-Control-Allow-Credentials" ? : string ; /** Specifying which web sites can participate in cross-origin resource sharing */ "Access-Control-Expose-Headers" ? : string ; /** Specifying which web sites can participate in cross-origin resource sharing */ "Access-Control-Max-Age" ? : string ; /** Specifying which web sites can participate in cross-origin resource sharing */ "Access-Control-Allow-Methods" ? : string ; /** Specifying which web sites can participate in cross-origin resource sharing */ "Access-Control-Allow-Headers" ? : string ; /** Specifies which patch document formats this server supports */ "Accept-Patch" ? : string ; /** What partial content range types this server supports via byte serving */ "Accept-Ranges" ? : string ; /** The age the object has been in a proxy cachein seconds */ Age ? : string ; /** Valid methods for a specified resource. To be used for a 405 Method not allowed */ Allow ? : string ; /**
     * A server uses "Alt-Svc" header (meaning Alternative Services) to indicate that its resources can also be accessed at a different
     * When using HTTP/2, servers should instead send an ALTSVC frame. [45]
     */ "Alt-Svc" ? : string ; /** Tells all caching mechanisms from server to client whether they may cache this object. It is measured in seconds */ "Cache-Control" ? : string ; /** Control options for the current connection and list of hop-by-hop response fields.[12] Must not be used with HTTP/2.[13] */ Connection ? : string ; /** An opportunity to raise a "File Download" dialogue box for a known MIME type with binary format or suggest a filename for dynami */ "Content-Disposition" ? : string ; /** The type of encoding used on the data. See HTTP compression. */ "Content-Encoding" ? : string ; /** The natural language or languages of the intended audience for the enclosed content[47] */ "Content-Language" ? : string ; /** The length of the response body in octets (8-bit bytes) */ "Content-Length" ? : string ; /** An alternate location for the returned data */ "Content-Location" ? : string ; /** A Base64-encoded binary MD5 sum of the content of the response */ "Content-MD5" ? : string ; /** Where in a full body message this partial message belongs */ "Content-Range" ? : string ; /** The MIME type of this content */ "Content-Type" ? : string ; /** The date and time that the message was sent (in "HTTP-date" format as defined by RFC 7231) [48] */ Date ? : string ; /** Specifies the delta-encoding entity tag of the response[10]. */ "Delta-Base" ? : string ; /** An identifier for a specific version of a resource, often a message digest */ ETag ? : string ; /** Gives the date/time after which the response is considered stale (in "HTTP-date" format as defined by RFC 7231) */ Expires ? : string ; /** Instance-manipulations applied to the response[10]. */ IM ? : string ; /** The last modified date for the requested object (in "HTTP-date" format as defined by RFC 7231) */ "Last-Modified" ? : string ; /** Used to express a typed relationship with another resource, where the relation type is defined by RFC 5988 */ Link ? : string ; /** Used in redirection, or when a new resource has been created. */ Location ? : string ; /** This field is supposed to set P3P policy, in the form of P3P:CP="your_compact_policy". However, P3P did not take off,[50] most b*/ P3P ? : string ; /** Implementation-specific fields that may have various effects anywhere along the request-response chain. */ Pragma ? : string ; /** Request authentication to access the proxy. */ "Proxy-Authenticate" ? : string ; /** HTTP Public Key Pinning, announces hash of website's authentic TLS certificate */ "Public-Key-Pins" ? : string ; /** If an entity is temporarily unavailable, this instructs the client to try again later. Value could be a specified period of time*/ "Retry-After" ? : string ; /** A name for the server */ Server ? : string ; /** An HTTP cookie */ "Set-Cookie" ? : string [ ] ; /** A HSTS Policy informing the HTTP client how long to cache the HTTPS only policy and whether this applies to subdomains. */ "Strict-Transport-Security" ? : string ; /** The Trailer general field value indicates that the given set of header fields is present in the trailer of a message encoded wit */ Trailer ? : string ; /** The form of encoding used to safely transfer the entity to the user. Currently defined methods are: chunked, compress, deflate, */ "Transfer-Encoding" ? : string ; /** Tracking Status header, value suggested to be sent in response to a DNT(do-not-track), possible values: */ Tk ? : string ; /** Ask the client to upgrade to another protocol. */ Upgrade ? : string ; /** Tells downstream proxies how to match future request headers to decide whether the cached response can be used rather than reque */ Vary ? : string ; /** Informs the client of proxies through which the response was sent. */ Via ? : string ; /** A general warning about possible problems with the entity body. */ Warning ? : string ; /** Indicates the authentication scheme that should be used to access the requested entity. */ "WWW-Authenticate" ? : string ; /** Clickjacking protection: deny - no rendering within a frame, sameorigin - no rendering if origin mismatch, allow-from - allow fr */ "X-Frame-Options" ? : string ; "x-api-access-type" ? : string ; dav ? : string ; etag ? : string ; } import { StateObject } from "./state-object" ; import { EventEmitter } from "./event-emitter-types" ; export class ER extends Error { constructor ( public reason , message ) { super ( message ) ; } } /** to be used with concatMap, mergeMap, etc. */ export function recieveBody ( state , parseJSON , sendError ? ) { return state . recieveBody ( parseJSON , sendError ) ; } export interface ThrowFunc < T > { throw ( statusCode , reason ? , str ? , ... args ) ; } export const TestConfig = { } as any ; export interface AccessPathResult < T > { isFullpath : boolean ; type : string | NodeJS . ErrnoException ; tag : T ; end : number ; statItem : fs . Stats ; statTW ? : fs . Stats ; } export interface AccessPathTag { state : StateObject ; item : string | { } ; treepath : string ; filepath : string ; } export interface PathResolverResult { item : Config . MountElement ; ancestry : Config . MountElement [ ] ; reqpath : string [ ] ; treepathPortion : string [ ] ; filepathPortion : string [ ] ; fullfilepath : string ; } export class Resolved implements PathResolverResult { static resolve ( pathname , root ) { let res = resolvePath ( pathname , root ) ; if ( ! res ) return false ; else return new Resolved ( res ) ; } public item : Config . MountElement ; public ancestry : Config . MountElement [ ] ; public reqpath : string [ ] ; public treepathPortion : string [ ] ; public filepathPortion : string [ ] ; public fullfilepath : string ; constructor ( res ) { this . item = res . item ; this . ancestry = res . ancestry ; this . reqpath = res . reqpath ; this . treepathPortion = res . treepathPortion ; this . filepathPortion = res . filepathPortion ; this . fullfilepath = res . fullfilepath ; } } export type TreeObject = { [ K ] : string | TreeObject ; } ; export type TreePathResultObject < T , U , V > = { item : T ; end : U ; folderPathFound : V ; /** The array of mount items in the path. Redundant, but easy to iterate quickly. */ ancestry : T [ ] ; } ; export type TreePathResult = TreePathResultObject < Config . GroupElement , number , false > | TreePathResultObject < Config . FolderElement , number , true > ; export function createHashmapString < T > ( keys , values ) { if ( keys . length !== values . length ) throw "keys and values must be the same length" ; var obj = { } ; keys . forEach ( ( e , i ) => { obj [ e ] = values [ i ] ; } ) ; return obj ; } export function createHashmapNumber < T > ( keys , values ) { if ( keys . length !== values . length ) throw "keys and values must be the same length" ; var obj = { } ; keys . forEach ( ( e , i ) => { obj [ e ] = values [ i ] ; } ) ; return obj ; } export function obsTruthy < T > ( a ) { return ! ! a ; } const ERRORS = { PROGRAMMER_EXCEPTION : "A programmer exception occurred: %s" , } ; export function getError ( code ) ; export function getError ( code ) ; export function getError ( code , keyList ) ; export function getError ( code , table , id ) ; export function getError ( code , message ) ; export function getError ( code , ... args ) { if ( ERRORS [ code ] ) args . unshift ( ERRORS [ code ] ) ; return { code : code , message : format ( code , ... args ) } ; } param { string } ip param { string } range param { number } netmask export function testAddress ( ip , range , netmask ) { let netmaskBinStr = ipcalc . IPv4_bitsNM_to_binstrNM ( netmask ) ; let addressBinStr = ipcalc . IPv4_intA_to_binstrA ( ipcalc . IPv4_dotquadA_to_intA ( ip ) ) ; let netaddrBinStr = ipcalc . IPv4_intA_to_binstrA ( ipcalc . IPv4_dotquadA_to_intA ( range ) ) ; let netaddrBinStrMasked = ipcalc . IPv4_Calc_netaddrBinStr ( netaddrBinStr , netmaskBinStr ) ; let addressBinStrMasked = ipcalc . IPv4_Calc_netaddrBinStr ( addressBinStr , netmaskBinStr ) ; return netaddrBinStrMasked === addressBinStrMasked ; } let hostIPv4reg = /^(\-?)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,2})$/i ; export function parseHostList ( hosts ) { let hostTests = hosts . map ( e => hostIPv4reg . exec ( e ) || e ) ; return ( addr ) => { let usable = false ; let lastMatch = - 1 ; hostTests . forEach ( ( test , i ) => { if ( Array . isArray ( test ) ) { let allow = ! test [ 1 ] ; let ip = test [ 2 ] ; let netmask = + test [ 3 ] ; if ( netmask < 0 || netmask > 32 ) console . log ( "Host %s has an invalid netmask" , test [ 0 ] ) ; if ( testAddress ( addr , ip , netmask ) ) { usable = allow ; lastMatch = i ; } } else { let ip = test . startsWith ( "-" ) ? test . slice ( 1 ) : test ; let deny = test . startsWith ( "-" ) ; if ( ip === addr ) { usable = ! deny ; lastMatch = i ; } } } ) ; return { usable , lastMatch } ; } ; } export function getUsableAddresses ( hosts ) { let reg = /^(\-?)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,2})$/i ; let hostTests = hosts . map ( e => reg . exec ( e ) || e ) ; var ifaces = networkInterfaces ( ) ; let addresses = Object . keys ( ifaces ) . reduce ( ( n , k ) => n . concat ( ifaces [ k ] . filter ( e => e . family === "IPv4" ) ) , [ ] as NetworkInterfaceInfo [ ] ) ; let usableArray = addresses . filter ( addr => { let usable = false ; hostTests . forEach ( test => { if ( Array . isArray ( test ) ) { if ( addr . family === "IPv6" ) return ; let allow = ! test [ 1 ] ; let ip = test [ 2 ] ; let netmask = + test [ 3 ] ; if ( netmask < 0 || netmask > 32 ) console . log ( "Host %s has an invalid netmask" , test [ 0 ] ) ; if ( testAddress ( addr . address , ip , netmask ) ) usable = allow ; } else { let ip = test . startsWith ( "-" ) ? test . slice ( 1 ) : test ; let deny = test . startsWith ( "-" ) ; if ( ip === addr . address ) usable = ! deny ; } } ) ; return usable ; } ) ; return usableArray ; } export function NodePromise < T > ( body ) { return new Promise < T > ( ( resolve , reject ) => { body ( ( err , data ) => ( err ? reject ( err ) : resolve ( data ) ) ) ; } ) ; }