aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2022-04-19 22:18:26 +0700
committerShulhan <ms@kilabit.info>2022-04-19 22:18:26 +0700
commit4f5896b2cfd316cb388d39e3772439f48b215fda (patch)
treeba516a4bd7c94d3429af6cb67066c205dd2c1daa
parent0156deaaaa0edbb93cfbf60a8cac99fc17d63a60 (diff)
downloadrescached-4f5896b2cfd316cb388d39e3772439f48b215fda.tar.xz
all: refactoring the hosts blocks format and location
This changes add new directory called "block.d" under rescached. This directory contains hosts file fetched from URL defined in hosts block configuration. In this way, we did not mixed between user created hosts file and external hosts file. In the configuration file, we changes the hosts block format from single line hosts_block = <URL> into subsection with dynamic name, [block.d "<name>"] name = <name> url = <URL>
-rw-r--r--Makefile2
-rw-r--r--_www/hosts_blocks/index.html14
-rw-r--r--cmd/rescached/rescached.cfg19
-rw-r--r--cmd/rescached/rescached.cfg.test23
-rw-r--r--environment.go84
-rw-r--r--environment_test.go74
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--hosts_block.go153
-rw-r--r--hosts_block_test.go104
-rw-r--r--httpd.go52
-rw-r--r--memfs_generate.go8
-rw-r--r--rescached.go28
-rw-r--r--testdata/rescached.cfg.test.out37
14 files changed, 419 insertions, 185 deletions
diff --git a/Makefile b/Makefile
index eed784e..02fcb9a 100644
--- a/Makefile
+++ b/Makefile
@@ -100,8 +100,10 @@ serve-doc:
install-common:
mkdir -p $(PREFIX)/etc/rescached
+ mkdir -p $(PREFIX)/etc/rescached/block.d
mkdir -p $(PREFIX)/etc/rescached/hosts.d
mkdir -p $(PREFIX)/etc/rescached/zone.d
+
cp $(RESCACHED_CFG) $(PREFIX)/etc/rescached/
cp testdata/localhost.cert.pem $(PREFIX)/etc/rescached/
cp testdata/localhost.key.pem $(PREFIX)/etc/rescached/
diff --git a/_www/hosts_blocks/index.html b/_www/hosts_blocks/index.html
index 331ec2b..79b79de 100644
--- a/_www/hosts_blocks/index.html
+++ b/_www/hosts_blocks/index.html
@@ -90,8 +90,8 @@
renderHostsBlocks(env.HostsBlocks)
}
- function onCheckHostblock(idx, val) {
- resc.env.HostsBlocks[idx].IsEnabled = val
+ function onCheckHostblock(key, val) {
+ resc.env.HostsBlocks[key].IsEnabled = val
}
async function updateHostsBlocks() {
@@ -109,8 +109,12 @@
let parent = document.getElementById("HostsBlocks")
parent.innerHTML = ""
- for (let x = 0; x < hostsBlocks.length; x++) {
- let hostsBlock = hostsBlocks[x]
+ for (var k in hostsBlocks) {
+ if (!hostsBlocks.hasOwnProperty(k)) {
+ continue;
+ }
+
+ let hostsBlock = hostsBlocks[k]
let item = document.createElement("div")
item.classList.add("item")
item.innerHTML = `
@@ -118,7 +122,7 @@
<input
type="checkbox"
${hostsBlock.IsEnabled ? "checked" : ""}
- oninput="onCheckHostblock('${x}', this.checked)"
+ oninput="onCheckHostblock('${k}', this.checked)"
/>
</span>
<span class="info">
diff --git a/cmd/rescached/rescached.cfg b/cmd/rescached/rescached.cfg
index bbca672..fb60b6c 100644
--- a/cmd/rescached/rescached.cfg
+++ b/cmd/rescached/rescached.cfg
@@ -12,10 +12,21 @@ file.resolvconf=
debug=0
wui.listen = 127.0.0.1:5380
-hosts_block = http://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&startdate[day]=&startdate[month]=&startdate[year]=&mimetype=plaintext
-hosts_block = http://www.malwaredomainlist.com/hostslist/hosts.txt
-hosts_block = http://winhelp2002.mvps.org/hosts.txt
-hosts_block = http://someonewhocares.org/hosts/hosts
+[block.d "pgl.yoyo.org"]
+name = pgl.yoyo.org
+url = http://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&startdate[day]=&startdate[month]=&startdate[year]=&mimetype=plaintext
+
+[block.d "someonewhocares.org"]
+name = someonewhocares.org
+url = http://someonewhocares.org/hosts/hosts
+
+[block.d "winhelp2002.mvps.org"]
+name = winhelp2002.mvps.org
+url = http://winhelp2002.mvps.org/hosts.txt
+
+[block.d "www.malwaredomainlist.com"]
+name = www.malwaredomainlist.com
+url = http://www.malwaredomainlist.com/hostslist/hosts.txt
[dns "server"]
parent=udp://1.1.1.1
diff --git a/cmd/rescached/rescached.cfg.test b/cmd/rescached/rescached.cfg.test
index a99ae95..d1f37ba 100644
--- a/cmd/rescached/rescached.cfg.test
+++ b/cmd/rescached/rescached.cfg.test
@@ -12,16 +12,27 @@ file.resolvconf=
debug=2
wui.listen = 127.0.0.1:5381
-hosts_block = http://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&startdate[day]=&startdate[month]=&startdate[year]=&mimetype=plaintext
-hosts_block = http://www.malwaredomainlist.com/hostslist/hosts.txt
-hosts_block = http://winhelp2002.mvps.org/hosts.txt
-hosts_block = http://someonewhocares.org/hosts/hosts
+[block.d "pgl.yoyo.org"]
+name = pgl.yoyo.org
+url = http://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&startdate[day]=&startdate[month]=&startdate[year]=&mimetype=plaintext
+
+[block.d "www.malwaredomainlist.com"]
+name = www.malwaredomainlist.com
+url = http://www.malwaredomainlist.com/hostslist/hosts.txt
+
+[block.d "winhelp2002.mvps.org"]
+name = winhelp2002.mvps.org
+url = http://winhelp2002.mvps.org/hosts.txt
+
+[block.d "someonewhocares.org"]
+name = someonewhocares.org
+url = http://someonewhocares.org/hosts/hosts
[dns "server"]
-#parent=udp://62.171.181.13
+parent=udp://10.8.0.1
#parent=tcp://62.171.181.13
## DNS over TLS
-parent=https://62.171.181.13
+#parent=https://10.8.0.1
## DNS over HTTPS
#parent=https://kilabit.info/dns-query
diff --git a/environment.go b/environment.go
index a4d7923..aac538e 100644
--- a/environment.go
+++ b/environment.go
@@ -23,6 +23,7 @@ const (
)
const (
+ sectionNameBlockd = "block.d"
sectionNameDNS = "dns"
sectionNameRescached = "rescached"
@@ -30,11 +31,12 @@ const (
keyDebug = "debug"
keyFileResolvConf = "file.resolvconf"
+ keyName = "name"
+ keyUrl = "url"
keyCachePruneDelay = "cache.prune_delay"
keyCachePruneThreshold = "cache.prune_threshold"
keyDohBehindProxy = "doh.behind_proxy"
- keyHostsBlock = "hosts_block"
keyHTTPPort = "http.port"
keyListen = "listen"
keyParent = "parent"
@@ -44,6 +46,7 @@ const (
keyTLSPort = "tls.port"
keyTLSPrivateKey = "tls.private_key"
+ dirBlock = "/etc/rescached/block.d"
dirHosts = "/etc/rescached/hosts.d"
dirZone = "/etc/rescached/zone.d"
)
@@ -52,9 +55,7 @@ var (
mfsWww *memfs.MemFS
)
-//
// Environment for running rescached.
-//
type Environment struct {
HostsFiles map[string]*dns.HostsFile
Zones map[string]*dns.Zone
@@ -63,8 +64,8 @@ type Environment struct {
FileResolvConf string `ini:"rescached::file.resolvconf"`
WUIListen string `ini:"rescached::wui.listen"`
- HostsBlocksRaw []string `ini:"rescached::hosts_block" json:"-"`
- HostsBlocks []*hostsBlock
+ HostsBlocks map[string]*hostsBlock `ini:"block.d"`
+ hostsBlocksFile map[string]*dns.HostsFile
// The options for WUI HTTP server.
HttpdOptions *libhttp.ServerOptions `json:"-"`
@@ -74,9 +75,7 @@ type Environment struct {
Debug int `ini:"rescached::debug"`
}
-//
// LoadEnvironment initialize environment from configuration file.
-//
func LoadEnvironment(fileConfig string) (env *Environment, err error) {
var (
logp = "LoadEnvironment"
@@ -104,11 +103,10 @@ func LoadEnvironment(fileConfig string) (env *Environment, err error) {
return env, nil
}
-//
// newEnvironment create and initialize options with default values.
-//
func newEnvironment() *Environment {
return &Environment{
+ hostsBlocksFile: make(map[string]*dns.HostsFile),
HttpdOptions: &libhttp.ServerOptions{
Memfs: mfsWww,
Address: defWuiAddress,
@@ -119,9 +117,7 @@ func newEnvironment() *Environment {
}
}
-//
// init check and initialize the environment instance with default values.
-//
func (env *Environment) init() (err error) {
if len(env.WUIListen) == 0 {
env.WUIListen = defWuiAddress
@@ -172,14 +168,13 @@ func (env *Environment) init() (err error) {
}
func (env *Environment) initHostsBlock() {
- env.HostsBlocks = hostsBlockSources
-
- for x, v := range env.HostsBlocksRaw {
- env.HostsBlocksRaw[x] = strings.ToLower(v)
- }
+ var (
+ dirBase = ""
- for _, hb := range env.HostsBlocks {
- hb.init(env.HostsBlocksRaw)
+ hb *hostsBlock
+ )
+ for _, hb = range env.HostsBlocks {
+ hb.init(dirBase)
}
}
@@ -216,8 +211,8 @@ func (env *Environment) save(file string) (in *ini.Ini, err error) {
var (
logp = "save"
- hb *hostsBlock
- ns string
+ hb *hostsBlock
+ vstr string
)
if len(file) == 0 {
@@ -233,38 +228,28 @@ func (env *Environment) save(file string) (in *ini.Ini, err error) {
in.Set(sectionNameRescached, "", keyDebug, strconv.Itoa(env.Debug))
in.Set(sectionNameRescached, "", keyWUIListen, strings.TrimSpace(env.WUIListen))
- in.UnsetAll(sectionNameRescached, "", keyHostsBlock)
-
for _, hb = range env.HostsBlocks {
- if hb.IsEnabled {
- in.Add(sectionNameRescached, "", keyHostsBlock, hb.URL)
- }
+ in.Set(sectionNameBlockd, hb.Name, keyName, hb.Name)
+ in.Set(sectionNameBlockd, hb.Name, keyUrl, hb.URL)
}
in.UnsetAll(sectionNameDNS, subNameServer, keyParent)
- for _, ns = range env.NameServers {
- in.Add(sectionNameDNS, subNameServer, keyParent, ns)
+ for _, vstr = range env.NameServers {
+ in.Add(sectionNameDNS, subNameServer, keyParent, vstr)
}
- in.Set(sectionNameDNS, subNameServer, keyListen,
- env.ServerOptions.ListenAddress)
+ in.Set(sectionNameDNS, subNameServer, keyListen, env.ListenAddress)
- in.Set(sectionNameDNS, subNameServer, keyHTTPPort,
- strconv.Itoa(int(env.ServerOptions.HTTPPort)))
+ in.Set(sectionNameDNS, subNameServer, keyHTTPPort, strconv.Itoa(int(env.HTTPPort)))
- in.Set(sectionNameDNS, subNameServer, keyTLSPort,
- strconv.Itoa(int(env.ServerOptions.TLSPort)))
- in.Set(sectionNameDNS, subNameServer, keyTLSCertificate,
- env.ServerOptions.TLSCertFile)
- in.Set(sectionNameDNS, subNameServer, keyTLSPrivateKey,
- env.ServerOptions.TLSPrivateKey)
- in.Set(sectionNameDNS, subNameServer, keyTLSAllowInsecure,
- fmt.Sprintf("%t", env.ServerOptions.TLSAllowInsecure))
- in.Set(sectionNameDNS, subNameServer, keyDohBehindProxy,
- fmt.Sprintf("%t", env.ServerOptions.DoHBehindProxy))
+ in.Set(sectionNameDNS, subNameServer, keyTLSPort, strconv.Itoa(int(env.TLSPort)))
+ in.Set(sectionNameDNS, subNameServer, keyTLSCertificate, env.TLSCertFile)
+ in.Set(sectionNameDNS, subNameServer, keyTLSPrivateKey, env.TLSPrivateKey)
+ in.Set(sectionNameDNS, subNameServer, keyTLSAllowInsecure, fmt.Sprintf("%t", env.TLSAllowInsecure))
+ in.Set(sectionNameDNS, subNameServer, keyDohBehindProxy, fmt.Sprintf("%t", env.DoHBehindProxy))
- in.Set(sectionNameDNS, subNameServer, keyCachePruneDelay, env.ServerOptions.PruneDelay.String())
- in.Set(sectionNameDNS, subNameServer, keyCachePruneThreshold, env.ServerOptions.PruneThreshold.String())
+ in.Set(sectionNameDNS, subNameServer, keyCachePruneDelay, env.PruneDelay.String())
+ in.Set(sectionNameDNS, subNameServer, keyCachePruneThreshold, env.PruneThreshold.String())
return in, nil
}
@@ -277,20 +262,23 @@ func (env *Environment) Write(w io.Writer) (err error) {
var (
logp = "Environment.Write"
- in *ini.Ini
+ outb []byte
)
- in, err = env.save(env.fileConfig)
+ outb, err = ini.Marshal(env)
if err != nil {
return fmt.Errorf("%s: %w", logp, err)
}
- return in.Write(w)
+ _, err = w.Write(outb)
+ if err != nil {
+ return fmt.Errorf("%s: %w", logp, err)
+ }
+
+ return nil
}
-//
// write the options values back to file.
-//
func (env *Environment) write(file string) (err error) {
var (
in *ini.Ini
diff --git a/environment_test.go b/environment_test.go
index e104ece..39b497e 100644
--- a/environment_test.go
+++ b/environment_test.go
@@ -4,9 +4,12 @@
package rescached
import (
+ "bytes"
+ "os"
"testing"
"github.com/shuLhan/share/lib/dns"
+ libhttp "github.com/shuLhan/share/lib/http"
"github.com/shuLhan/share/lib/ini"
"github.com/shuLhan/share/lib/test"
)
@@ -54,3 +57,74 @@ parent = https://kilabit.info/dns-query
test.Assert(t, "environment", c.exp, got)
}
}
+
+func TestLoadEnvironment(t *testing.T) {
+ var (
+ expEnv = &Environment{
+ fileConfig: "cmd/rescached/rescached.cfg.test",
+
+ WUIListen: "127.0.0.1:5381",
+ HostsBlocks: map[string]*hostsBlock{
+ "pgl.yoyo.org": &hostsBlock{
+ Name: "pgl.yoyo.org",
+ URL: `http://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&startdate[day]=&startdate[month]=&startdate[year]=&mimetype=plaintext`,
+ },
+ "www.malwaredomainlist.com": &hostsBlock{
+ Name: "www.malwaredomainlist.com",
+ URL: `http://www.malwaredomainlist.com/hostslist/hosts.txt`,
+ },
+ "winhelp2002.mvps.org": &hostsBlock{
+ Name: "winhelp2002.mvps.org",
+ URL: `http://winhelp2002.mvps.org/hosts.txt`,
+ },
+ "someonewhocares.org": &hostsBlock{
+ Name: "someonewhocares.org",
+ URL: `http://someonewhocares.org/hosts/hosts`,
+ },
+ },
+ HttpdOptions: &libhttp.ServerOptions{
+ Address: defWuiAddress,
+ },
+ ServerOptions: dns.ServerOptions{
+ ListenAddress: "127.0.0.1:5350",
+ NameServers: []string{
+ "udp://10.8.0.1",
+ },
+ TLSAllowInsecure: true,
+ },
+
+ Debug: 2,
+ }
+ expBuffer []byte
+
+ gotEnv *Environment
+ gotBuffer bytes.Buffer
+ err error
+ )
+
+ expBuffer, err = os.ReadFile("testdata/rescached.cfg.test.out")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ gotEnv, err = LoadEnvironment("cmd/rescached/rescached.cfg.test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ gotEnv.HttpdOptions.Memfs = nil
+
+ test.Assert(t, "LoadEnvironment", expEnv, gotEnv)
+
+ gotEnv.HostsBlocks["test"] = &hostsBlock{
+ Name: "test",
+ URL: "http://someurl",
+ }
+
+ err = gotEnv.Write(&gotBuffer)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ test.Assert(t, "Write", expBuffer, gotBuffer.Bytes())
+}
diff --git a/go.mod b/go.mod
index 4822aa2..054bdf7 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,6 @@ module github.com/shuLhan/rescached-go/v4
go 1.16
-require github.com/shuLhan/share v0.36.1-0.20220414192731-ceba0704c6fc
+require github.com/shuLhan/share v0.36.1-0.20220418175009-e5c85cdbfb18
//replace github.com/shuLhan/share => ../share
diff --git a/go.sum b/go.sum
index 1ce3cad..c6d95c1 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-github.com/shuLhan/share v0.36.1-0.20220414192731-ceba0704c6fc h1:PRCQ/x8ZzOv+dlSNl3NVZgkq3rpQd4Wwz33Q73T6P2k=
-github.com/shuLhan/share v0.36.1-0.20220414192731-ceba0704c6fc/go.mod h1:laKGR1DNboj8+INRIC9VFYRiVEu/IIjrLurUmTHXkw0=
+github.com/shuLhan/share v0.36.1-0.20220418175009-e5c85cdbfb18 h1:kC1DQeauBOs830pBOx0oD7u4WWdV74dpy1/QR3kPNqE=
+github.com/shuLhan/share v0.36.1-0.20220418175009-e5c85cdbfb18/go.mod h1:laKGR1DNboj8+INRIC9VFYRiVEu/IIjrLurUmTHXkw0=
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
diff --git a/hosts_block.go b/hosts_block.go
index 4a829be..d4c6722 100644
--- a/hosts_block.go
+++ b/hosts_block.go
@@ -14,43 +14,84 @@ import (
"time"
)
-//
-// List of blocked hosts sources.
-//
-var hostsBlockSources = []*hostsBlock{{
- Name: "pgl.yoyo.org",
- URL: `http://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&startdate[day]=&startdate[month]=&startdate[year]=&mimetype=plaintext`,
-}, {
- Name: "www.malwaredomainlist.com",
- URL: `http://www.malwaredomainlist.com/hostslist/hosts.txt`,
-}, {
- Name: "winhelp2002.mvps.org",
- URL: `http://winhelp2002.mvps.org/hosts.txt`,
-}, {
- Name: "someonewhocares.org",
- URL: `http://someonewhocares.org/hosts/hosts`,
-}}
+const (
+ lastUpdatedFormat = "2006-01-02 15:04:05 MST"
+)
type hostsBlock struct {
lastUpdated time.Time
- Name string // Derived from hostname in URL.
- URL string
- LastUpdated string
- file string
+ Name string `ini:"::name"` // Derived from hostname in URL.
+ URL string `ini:"::url"`
+
+ file string
+ fileDisabled string
+ LastUpdated string
+
+ IsEnabled bool // True if the hosts file un-hidden in block.d directory.
+ isFileExist bool // True if the file exist and enabled or disabled.
+}
- IsEnabled bool
+// disable the hosts block by prefixing the file name with single dot.
+func (hb *hostsBlock) disable() (err error) {
+ err = os.Rename(hb.file, hb.fileDisabled)
+ if err != nil {
+ return fmt.Errorf("disable: %w", err)
+ }
+ hb.IsEnabled = false
+ return nil
}
-func (hb *hostsBlock) init(sources []string) {
- for _, src := range sources {
- if hb.URL == src {
- hb.IsEnabled = true
- break
+// enable the hosts block file by removing the dot prefix from file name.
+func (hb *hostsBlock) enable() (err error) {
+ if hb.isFileExist {
+ err = os.Rename(hb.fileDisabled, hb.file)
+ } else {
+ err = os.WriteFile(hb.file, []byte(""), 0600)
+ }
+ if err != nil {
+ return fmt.Errorf("enable: %w", err)
+ }
+ hb.IsEnabled = true
+ hb.isFileExist = true
+ return nil
+}
+
+func (hb *hostsBlock) init(dirBase string) {
+ var (
+ fi os.FileInfo
+ err error
+ )
+
+ hb.file = filepath.Join(dirBase, dirBlock, hb.Name)
+ hb.fileDisabled = filepath.Join(dirBase, dirBlock, "."+hb.Name)
+
+ fi, err = os.Stat(hb.file)
+ if err != nil {
+ hb.IsEnabled = false
+
+ fi, err = os.Stat(hb.fileDisabled)
+ if err != nil {
+ return
}
+
+ hb.isFileExist = true
+ } else {
+ hb.IsEnabled = true
+ hb.isFileExist = true
}
- hb.initLastUpdated()
+ hb.lastUpdated = fi.ModTime()
+ hb.LastUpdated = hb.lastUpdated.Format(lastUpdatedFormat)
+}
+
+// isOld will return true if the host file has not been updated since seven
+// days.
+func (hb *hostsBlock) isOld() bool {
+ oneWeek := 7 * 24 * time.Hour
+ lastWeek := time.Now().Add(-1 * oneWeek)
+
+ return hb.lastUpdated.Before(lastWeek)
}
func (hb *hostsBlock) update() (err error) {
@@ -86,61 +127,17 @@ func (hb *hostsBlock) update() (err error) {
body = bytes.ReplaceAll(body, []byte("\r\n"), []byte("\n"))
- err = os.WriteFile(hb.file, body, 0644)
- if err != nil {
- return fmt.Errorf("%s %q: %w", logp, hb.Name, err)
+ if hb.IsEnabled {
+ err = os.WriteFile(hb.file, body, 0644)
+ } else {
+ err = os.WriteFile(hb.fileDisabled, body, 0644)
}
-
- hb.initLastUpdated()
-
- return nil
-}
-
-func (hb *hostsBlock) hide() (err error) {
- oldFileName := filepath.Join(dirHosts, hb.Name)
- newFileName := filepath.Join(dirHosts, "."+hb.Name)
- err = os.Rename(oldFileName, newFileName)
if err != nil {
- return err
- }
-
- hb.file = newFileName
-
- return nil
-}
-
-func (hb *hostsBlock) isOld() bool {
- oneWeek := 7 * 24 * time.Hour
- lastWeek := time.Now().Add(-1 * oneWeek)
-
- return hb.lastUpdated.Before(lastWeek)
-}
-
-//
-// unhide the hosts block file.
-//
-func (hb *hostsBlock) unhide() (err error) {
- oldFileName := filepath.Join(dirHosts, "."+hb.Name)
- newFileName := filepath.Join(dirHosts, hb.Name)
- err = os.Rename(oldFileName, newFileName)
- if err != nil {
- return err
+ return fmt.Errorf("%s %q: %w", logp, hb.Name, err)
}
- hb.file = newFileName
- hb.initLastUpdated()
+ hb.lastUpdated = time.Now()
+ hb.LastUpdated = hb.lastUpdated.Format(lastUpdatedFormat)
return nil
}
-
-func (hb *hostsBlock) initLastUpdated() {
- hb.file = filepath.Join(dirHosts, hb.Name)
- fi, err := os.Stat(hb.file)
- if err != nil {
- hb.IsEnabled = false
- return
- }
-
- hb.lastUpdated = fi.ModTime()
- hb.LastUpdated = hb.lastUpdated.Format("2006-01-02 15:04:05 MST")
-}
diff --git a/hosts_block_test.go b/hosts_block_test.go
new file mode 100644
index 0000000..456fb63
--- /dev/null
+++ b/hosts_block_test.go
@@ -0,0 +1,104 @@
+// SPDX-FileCopyrightText: 2020 M. Shulhan <ms@kilabit.info>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package rescached
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/shuLhan/share/lib/test"
+)
+
+func TestHostsBlock_init(t *testing.T) {
+ type testCase struct {
+ desc string
+ hb hostsBlock
+ exp hostsBlock
+ }
+
+ var (
+ testDirBase = t.TempDir()
+ testDirBlock = filepath.Join(testDirBase, dirBlock)
+ fileEnabled = "fileEnabled"
+ fileDisabled = "fileDisabled"
+ fileNotExist = "fileNotExist"
+ hostsFileEnabled = filepath.Join(testDirBlock, fileEnabled)
+ hostsFileDisabled = filepath.Join(testDirBlock, "."+fileDisabled)
+
+ fiEnabled os.FileInfo
+ fiDisabled os.FileInfo
+ cases []testCase
+ c testCase
+ err error
+ )
+
+ err = os.MkdirAll(testDirBlock, 0700)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = os.WriteFile(hostsFileEnabled, []byte("127.0.0.2 localhost"), 0600)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = os.WriteFile(hostsFileDisabled, []byte("127.0.0.2 localhost"), 0600)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ fiEnabled, err = os.Stat(hostsFileEnabled)
+ if err != nil {
+ t.Fatal(err)
+ }
+ fiDisabled, err = os.Stat(hostsFileDisabled)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cases = []testCase{{
+ desc: "With hosts block file enabled",
+ hb: hostsBlock{
+ Name: fileEnabled,
+ },
+ exp: hostsBlock{
+ Name: fileEnabled,
+ lastUpdated: fiEnabled.ModTime(),
+ LastUpdated: fiEnabled.ModTime().Format(lastUpdatedFormat),
+ file: hostsFileEnabled,
+ fileDisabled: filepath.Join(testDirBlock, "."+fileEnabled),
+ IsEnabled: true,
+ isFileExist: true,
+ },
+ }, {
+ desc: "With hosts block file disabled",
+ hb: hostsBlock{
+ Name: fileDisabled,
+ },
+ exp: hostsBlock{
+ Name: fileDisabled,
+ lastUpdated: fiDisabled.ModTime(),
+ LastUpdated: fiDisabled.ModTime().Format(lastUpdatedFormat),
+ file: filepath.Join(testDirBlock, fileDisabled),
+ fileDisabled: hostsFileDisabled,
+ isFileExist: true,
+ },
+ }, {
+ desc: "With hosts block file not exist",
+ hb: hostsBlock{
+ Name: fileNotExist,
+ },
+ exp: hostsBlock{
+ Name: fileNotExist,
+ file: filepath.Join(testDirBlock, fileNotExist),
+ fileDisabled: filepath.Join(testDirBlock, "."+fileNotExist),
+ },
+ }}
+
+ for _, c = range cases {
+ c.hb.init(testDirBase)
+
+ test.Assert(t, c.desc, c.exp, c.hb)
+ }
+}
diff --git a/httpd.go b/httpd.go
index 6e51ddf..17a8442 100644
--- a/httpd.go
+++ b/httpd.go
@@ -5,11 +5,9 @@ package rescached
import (
"encoding/json"
- "errors"
"fmt"
"log"
"net/http"
- "os"
"path/filepath"
"regexp"
"strconv"
@@ -376,19 +374,17 @@ func (srv *Server) httpApiEnvironmentUpdate(epr *libhttp.EndpointRequest) (resBo
return json.Marshal(&res)
}
-//
// apiHostsBlockUpdate set the HostsBlock to be enabled or disabled.
//
// If its status changes to enabled, unhide the hosts block file, populate the
-// hosts back to caches, and add it to list of HostsFiles.
+// hosts back to caches, and add it to list of hostsBlocksFile.
//
// If its status changes to disabled, remove the hosts from caches, hide it,
-// and remove it from list of HostsFiles.
-//
+// and remove it from list of hostsBlocksFile.
func (srv *Server) apiHostsBlockUpdate(epr *libhttp.EndpointRequest) (resBody []byte, err error) {
var (
res = libhttp.EndpointResponse{}
- hostsBlocks = make([]*hostsBlock, 0)
+ hostsBlocks = make(map[string]*hostsBlock, 0)
hbx *hostsBlock
hby *hostsBlock
@@ -444,39 +440,32 @@ func (srv *Server) apiHostsBlockUpdate(epr *libhttp.EndpointRequest) (resBody []
func (srv *Server) hostsBlockEnable(hb *hostsBlock) (err error) {
var (
+ logp = "hostsBlockEnable"
+
hfile *dns.HostsFile
)
- hb.IsEnabled = true
-
- err = hb.unhide()
+ err = hb.enable()
if err != nil {
- if !errors.Is(err, os.ErrNotExist) {
- return err
- }
- // File not exist, fetch new from server.
- err = hb.update()
- if err != nil {
- return err
- }
+ return fmt.Errorf("%s: %w", logp, err)
}
- hfile, err = dns.ParseHostsFile(filepath.Join(dirHosts, hb.Name))
+ err = hb.update()
if err != nil {
- return err
+ return fmt.Errorf("%s: %w", logp, err)
}
- err = srv.dns.PopulateCachesByRR(hfile.Records, hfile.Path)
+ hfile, err = dns.ParseHostsFile(hb.file)
if err != nil {
- return err
+ return fmt.Errorf("%s: %w", logp, err)
}
- err = hb.update()
+ err = srv.dns.PopulateCachesByRR(hfile.Records, hfile.Path)
if err != nil {
- return err
+ return fmt.Errorf("%s: %w", logp, err)
}
- srv.env.HostsFiles[hfile.Name] = hfile
+ srv.env.hostsBlocksFile[hfile.Name] = hfile
return nil
}
@@ -488,18 +477,19 @@ func (srv *Server) hostsBlockDisable(hb *hostsBlock) (err error) {
hfile *dns.HostsFile
)
- hfile = srv.env.HostsFiles[hb.Name]
+ hfile = srv.env.hostsBlocksFile[hb.Name]
if hfile == nil {
return fmt.Errorf("%s: unknown hosts block: %q", logp, hb.Name)
}
srv.dns.RemoveLocalCachesByNames(hfile.Names())
- err = hb.hide()
+ err = hb.disable()
if err != nil {
return fmt.Errorf("%s: %w", logp, err)
}
- delete(srv.env.HostsFiles, hb.Name)
+
+ delete(srv.env.hostsBlocksFile, hfile.Name)
return nil
}
@@ -602,9 +592,7 @@ func (srv *Server) apiHostsFileDelete(epr *libhttp.EndpointRequest) (resbody []b
return json.Marshal(&res)
}
-//
// apiHostsFileRRCreate create new record and save it to the hosts file.
-//
func (srv *Server) apiHostsFileRRCreate(epr *libhttp.EndpointRequest) (resbody []byte, err error) {
var (
res = libhttp.EndpointResponse{}
@@ -806,9 +794,7 @@ func (srv *Server) apiZoneDelete(epr *libhttp.EndpointRequest) (resbody []byte,
return json.Marshal(&res)
}
-//
// apiZoneRRCreate create new RR for the zone file.
-//
func (srv *Server) apiZoneRRCreate(epr *libhttp.EndpointRequest) (resbody []byte, err error) {
var (
res = libhttp.EndpointResponse{}
@@ -908,9 +894,7 @@ func (srv *Server) apiZoneRRCreate(epr *libhttp.EndpointRequest) (resbody []byte
return json.Marshal(&res)
}
-//
// apiZoneRRDelete delete RR from the zone file.
-//
func (srv *Server) apiZoneRRDelete(epr *libhttp.EndpointRequest) (resbody []byte, err error) {
var (
res = libhttp.EndpointResponse{}
diff --git a/memfs_generate.go b/memfs_generate.go
index 9f1aad1..b9652ff 100644
--- a/memfs_generate.go
+++ b/memfs_generate.go
@@ -127,12 +127,12 @@ func generate__www_hosts_blocks_index_html() *memfs.Node {
Path: "/hosts_blocks/index.html",
ContentType: "text/html; charset=utf-8",
GenFuncName: "generate__www_hosts_blocks_index_html",
- Content: []byte("\x3C\x21\x44\x4F\x43\x54\x59\x50\x45\x20\x68\x74\x6D\x6C\x3E\x0A\x3C\x21\x2D\x2D\x20\x53\x50\x44\x58\x2D\x46\x69\x6C\x65\x43\x6F\x70\x79\x72\x69\x67\x68\x74\x54\x65\x78\x74\x3A\x20\x32\x30\x32\x31\x20\x4D\x2E\x20\x53\x68\x75\x6C\x68\x61\x6E\x20\x3C\x6D\x73\x40\x6B\x69\x6C\x61\x62\x69\x74\x2E\x69\x6E\x66\x6F\x20\x2D\x2D\x3E\x0A\x3C\x21\x2D\x2D\x20\x53\x50\x44\x58\x2D\x4C\x69\x63\x65\x6E\x73\x65\x2D\x49\x64\x65\x6E\x74\x69\x66\x69\x65\x72\x3A\x20\x47\x50\x4C\x2D\x33\x2E\x30\x2D\x6F\x72\x2D\x6C\x61\x74\x65\x72\x20\x2D\x2D\x3E\x0A\x3C\x68\x74\x6D\x6C\x20\x6C\x61\x6E\x67\x3D\x22\x65\x6E\x22\x3E\x0A\x09\x3C\x68\x65\x61\x64\x3E\x0A\x09\x09\x3C\x6D\x65\x74\x61\x20\x63\x68\x61\x72\x73\x65\x74\x3D\x22\x75\x74\x66\x2D\x38\x22\x20\x2F\x3E\x0A\x09\x09\x3C\x6D\x65\x74\x61\x20\x6E\x61\x6D\x65\x3D\x22\x76\x69\x65\x77\x70\x6F\x72\x74\x22\x20\x63\x6F\x6E\x74\x65\x6E\x74\x3D\x22\x77\x69\x64\x74\x68\x3D\x64\x65\x76\x69\x63\x65\x2D\x77\x69\x64\x74\x68\x2C\x20\x69\x6E\x69\x74\x69\x61\x6C\x2D\x73\x63\x61\x6C\x65\x3D\x31\x2C\x20\x73\x68\x72\x69\x6E\x6B\x2D\x74\x6F\x2D\x66\x69\x74\x3D\x6E\x6F\x22\x20\x2F\x3E\x0A\x09\x09\x3C\x6C\x69\x6E\x6B\x20\x72\x65\x6C\x3D\x22\x69\x63\x6F\x6E\x22\x20\x74\x79\x70\x65\x3D\x22\x69\x6D\x61\x67\x65\x2F\x70\x6E\x67\x22\x20\x68\x72\x65\x66\x3D\x22\x2F\x66\x61\x76\x69\x63\x6F\x6E\x2E\x70\x6E\x67\x22\x20\x2F\x3E\x0A\x09\x09\x3C\x6C\x69\x6E\x6B\x20\x72\x65\x6C\x3D\x22\x73\x74\x79\x6C\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3D\x22\x2F\x69\x6E\x64\x65\x78\x2E\x63\x73\x73\x22\x20\x2F\x3E\x0A\x09\x09\x3C\x74\x69\x74\x6C\x65\x3E\x72\x65\x73\x63\x61\x63\x68\x65\x64\x20\x7C\x20\x68\x6F\x73\x74\x73\x20\x62\x6C\x6F\x63\x6B\x73\x3C\x2F\x74\x69\x74\x6C\x65\x3E\x0A\x0A\x09\x09\x3C\x73\x74\x79\x6C\x65\x3E\x0A\x09\x09\x09\x2E\x62\x6C\x6F\x63\x6B\x5F\x73\x6F\x75\x72\x63\x65\x20\x7B\x0A\x09\x09\x09\x09\x77\x69\x64\x74\x68\x3A\x20\x63\x61\x6C\x63\x28\x31\x30\x30\x25\x20\x2D\x20\x32\x65\x6D\x29\x3B\x0A\x09\x09\x09\x09\x6F\x76\x65\x72\x66\x6C\x6F\x77\x3A\x20\x61\x75\x74\x6F\x3B\x0A\x09\x09\x09\x7D\x0A\x09\x09\x09\x2E\x62\x6C\x6F\x63\x6B\x5F\x73\x6F\x75\x72\x63\x65\x20\x69\x6E\x70\x75\x74\x3A\x64\x69\x73\x61\x62\x6C\x65\x64\x20\x7B\x0A\x09\x09\x09\x09\x63\x6F\x6C\x6F\x72\x3A\x20\x62\x6C\x61\x63\x6B\x3B\x0A\x09\x09\x09\x7D\x0A\x09\x09\x09\x2E\x69\x74\x65\x6D\x20\x7B\x0A\x09\x09\x09\x09\x77\x69\x64\x74\x68\x3A\x20\x31\x30\x30\x25\x3B\x0A\x09\x09\x09\x09\x6D\x61\x72\x67\x69\x6E\x2D\x62\x6F\x74\x74\x6F\x6D\x3A\x20\x31\x65\x6D\x3B\x0A\x09\x09\x09\x7D\x0A\x09\x09\x09\x2E\x69\x74\x65\x6D\x2E\x68\x65\x61\x64\x65\x72\x20\x7B\x0A\x09\x09\x09\x09\x66\x6F\x6E\x74\x2D\x77\x65\x69\x67\x68\x74\x3A\x20\x62\x6F\x6C\x64\x3B\x0A\x09\x09\x09\x09\x6D\x61\x72\x67\x69\x6E\x2D\x62\x6F\x74\x74\x6F\x6D\x3A\x20\x31\x65\x6D\x3B\x0A\x09\x09\x09\x09\x62\x6F\x72\x64\x65\x72\x2D\x62\x6F\x74\x74\x6F\x6D\x3A\x20\x31\x70\x78\x20\x73\x6F\x6C\x69\x64\x20\x73\x69\x6C\x76\x65\x72\x3B\x0A\x09\x09\x09\x7D\x0A\x09\x09\x09\x2E\x69\x74\x65\x6D\x20\x2E\x69\x73\x2D\x65\x6E\x61\x62\x6C\x65\x64\x20\x7B\x0A\x09\x09\x09\x09\x64\x69\x73\x70\x6C\x61\x79\x3A\x20\x69\x6E\x6C\x69\x6E\x65\x2D\x62\x6C\x6F\x63\x6B\x3B\x0A\x09\x09\x09\x09\x77\x69\x64\x74\x68\x3A\x20\x34\x65\x6D\x3B\x0A\x09\x09\x09\x09\x76\x65\x72\x74\x69\x63\x61\x6C\x2D\x61\x6C\x69\x67\x6E\x3A\x20\x74\x6F\x70\x3B\x0A\x09\x09\x09\x7D\x0A\x09\x09\x09\x2E\x69\x74\x65\x6D\x20\x2E\x69\x6E\x66\x6F\x20\x7B\x0A\x09\x09\x09\x09\x64\x69\x73\x70\x6C\x61\x79\x3A\x20\x69\x6E\x6C\x69\x6E\x65\x2D\x62\x6C\x6F\x63\x6B\x3B\x0A\x09\x09\x09\x09\x77\x69\x64\x74\x68\x3A\x20\x63\x61\x6C\x63\x28\x31\x30\x30\x25\x20\x2D\x20\x36\x65\x6D\x29\x3B\x0A\x09\x09\x09\x7D\x0A\x09\x09\x09\x2E\x69\x74\x65\x6D\x20\x2E\x69\x6E\x66\x6F\x20\x69\x6E\x70\x75\x74\x20\x7B\x0A\x09\x09\x09\x09\x77\x69\x64\x74\x68\x3A\x20\x63\x61\x6C\x63\x28\x31\x30\x30\x25\x20\x2D\x20\x34\x65\x6D\x29\x3B\x0A\x09\x09\x09\x7D\x0A\x09\x09\x3C\x2F\x73\x74\x79\x6C\x65\x3E\x0A\x09\x3C\x2F\x68\x65\x61\x64\x3E\x0A\x09\x3C\x62\x6F\x64\x79\x20\x6F\x6E\x6C\x6F\x61\x64\x3D\x22\x6F\x6E\x4C\x6F\x61\x64\x28\x29\x22\x3E\x0A\x09\x09\x3C\x6E\x61\x76\x20\x63\x6C\x61\x73\x73\x3D\x22\x6D\x65\x6E\x75\x22\x3E\x0A\x09\x09\x09\x3C\x61\x20\x68\x72\x65\x66\x3D\x22\x2F\x22\x3E\x20\x72\x65\x73\x63\x61\x63\x68\x65\x64\x20\x3C\x2F\x61\x3E\x0A\x09\x09\x09\x2F\x0A\x09\x09\x09\x3C\x61\x20\x68\x72\x65\x66\x3D\x22\x2F\x65\x6E\x76\x69\x72\x6F\x6E\x6D\x65\x6E\x74\x2F\x22\x3E\x20\x45\x6E\x76\x69\x72\x6F\x6E\x6D\x65\x6E\x74\x20\x3C\x2F\x61\x3E\x0A\x09\x09\x09\x2F\x0A\x09\x09\x09\x3C\x61\x20\x68\x72\x65\x66\x3D\x22\x2F\x68\x6F\x73\x74\x73\x5F\x62\x6C\x6F\x63\x6B\x73\x2F\x22\x20\x63\x6C\x61\x73\x73\x3D\x22\x61\x63\x74\x69\x76\x65\x22\x3E\x20\x48\x6F\x73\x74\x73\x20\x62\x6C\x6F\x63\x6B\x73\x20\x3C\x2F\x61\x3E\x0A\x09\x09\x09\x2F\x0A\x09\x09\x09\x3C\x61\x20\x68\x72\x65\x66\x3D\x22\x2F\x68\x6F\x73\x74\x73\x2E\x64\x2F\x22\x3E\x20\x68\x6F\x73\x74\x73\x2E\x64\x20\x3C\x2F\x61\x3E\x0A\x09\x09\x09\x2F\x0A\x09\x09\x09\x3C\x61\x20\x68\x72\x65\x66\x3D\x22\x2F\x7A\x6F\x6E\x65\x2E\x64\x2F\x22\x3E\x20\x7A\x6F\x6E\x65\x2E\x64\x20\x3C\x2F\x61\x3E\x0A\x09\x09\x3C\x2F\x6E\x61\x76\x3E\x0A\x0A\x09\x09\x3C\x64\x69\x76\x20\x69\x64\x3D\x22\x6E\x6F\x74\x69\x66\x22\x3E\x3C\x2F\x64\x69\x76\x3E\x0A\x0A\x09\x09\x3C\x70\x3E\x43\x6F\x6E\x66\x69\x67\x75\x72\x65\x20\x74\x68\x65\x20\x73\x6F\x75\x72\x63\x65\x20\x6F\x66\x20\x62\x6C\x6F\x63\x6B\x65\x64\x20\x68\x6F\x73\x74\x73\x20\x66\x69\x6C\x65\x2E\x3C\x2F\x70\x3E\x0A\x0A\x09\x09\x3C\x64\x69\x76\x20\x63\x6C\x61\x73\x73\x3D\x22\x62\x6C\x6F\x63\x6B\x5F\x73\x6F\x75\x72\x63\x65\x22\x3E\x0A\x09\x09\x09\x3C\x64\x69\x76\x20\x63\x6C\x61\x73\x73\x3D\x22\x69\x74\x65\x6D\x20\x68\x65\x61\x64\x65\x72\x22\x3E\x0A\x09\x09\x09\x09\x3C\x73\x70\x61\x6E\x20\x63\x6C\x61\x73\x73\x3D\x22\x69\x73\x2D\x65\x6E\x61\x62\x6C\x65\x64\x22\x3E\x20\x45\x6E\x61\x62\x6C\x65\x64\x20\x3C\x2F\x73\x70\x61\x6E\x3E\x0A\x09\x09\x09\x09\x3C\x73\x70\x61\x6E\x20\x63\x6C\x61\x73\x73\x3D\x22\x69\x6E\x66\x6F\x22\x3E\x20\x4E\x61\x6D\x65\x20\x3C\x2F\x73\x70\x61\x6E\x3E\x0A\x09\x09\x09\x3C\x2F\x64\x69\x76\x3E\x0A\x09\x09\x3C\x2F\x64\x69\x76\x3E\x0A\x09\x09\x3C\x64\x69\x76\x20\x69\x64\x3D\x22\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x22\x20\x63\x6C\x61\x73\x73\x3D\x22\x62\x6C\x6F\x63\x6B\x5F\x73\x6F\x75\x72\x63\x65\x22\x3E\x3C\x2F\x64\x69\x76\x3E\x0A\x0A\x09\x09\x3C\x64\x69\x76\x3E\x0A\x09\x09\x09\x3C\x62\x75\x74\x74\x6F\x6E\x20\x6F\x6E\x63\x6C\x69\x63\x6B\x3D\x22\x75\x70\x64\x61\x74\x65\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x28\x29\x22\x3E\x53\x61\x76\x65\x3C\x2F\x62\x75\x74\x74\x6F\x6E\x3E\x0A\x09\x09\x3C\x2F\x64\x69\x76\x3E\x0A\x0A\x09\x09\x3C\x73\x63\x72\x69\x70\x74\x20\x73\x72\x63\x3D\x22\x2F\x69\x6E\x64\x65\x78\x2E\x6A\x73\x22\x3E\x3C\x2F\x73\x63\x72\x69\x70\x74\x3E\x0A\x09\x09\x3C\x73\x63\x72\x69\x70\x74\x20\x73\x72\x63\x3D\x22\x2F\x72\x65\x73\x63\x61\x63\x68\x65\x64\x2E\x6A\x73\x22\x3E\x3C\x2F\x73\x63\x72\x69\x70\x74\x3E\x0A\x09\x09\x3C\x73\x63\x72\x69\x70\x74\x3E\x0A\x09\x09\x09\x6C\x65\x74\x20\x72\x65\x73\x63\x20\x3D\x20\x6E\x75\x6C\x6C\x0A\x0A\x09\x09\x09\x66\x75\x6E\x63\x74\x69\x6F\x6E\x20\x6F\x6E\x4C\x6F\x61\x64\x28\x29\x20\x7B\x0A\x09\x09\x09\x09\x72\x65\x73\x63\x20\x3D\x20\x6E\x65\x77\x20\x52\x65\x73\x63\x61\x63\x68\x65\x64\x28\x22\x22\x29\x0A\x09\x09\x09\x09\x67\x65\x74\x45\x6E\x76\x69\x72\x6F\x6E\x6D\x65\x6E\x74\x28\x29\x0A\x09\x09\x09\x7D\x0A\x0A\x09\x09\x09\x61\x73\x79\x6E\x63\x20\x66\x75\x6E\x63\x74\x69\x6F\x6E\x20\x67\x65\x74\x45\x6E\x76\x69\x72\x6F\x6E\x6D\x65\x6E\x74\x28\x29\x20\x7B\x0A\x09\x09\x09\x09\x63\x6F\x6E\x73\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x72\x65\x73\x63\x2E\x67\x65\x74\x45\x6E\x76\x69\x72\x6F\x6E\x6D\x65\x6E\x74\x28\x29\x0A\x09\x09\x09\x09\x69\x66\x20\x28\x72\x65\x73\x2E\x63\x6F\x64\x65\x20\x21\x3D\x20\x32\x30\x30\x29\x20\x7B\x0A\x09\x09\x09\x09\x09\x6E\x6F\x74\x69\x66\x45\x72\x72\x6F\x72\x28\x72\x65\x73\x2E\x6D\x65\x73\x73\x61\x67\x65\x29\x0A\x09\x09\x09\x09\x09\x72\x65\x74\x75\x72\x6E\x0A\x09\x09\x09\x09\x7D\x0A\x0A\x09\x09\x09\x09\x6C\x65\x74\x20\x65\x6E\x76\x20\x3D\x20\x72\x65\x73\x2E\x64\x61\x74\x61\x0A\x09\x09\x09\x09\x72\x65\x6E\x64\x65\x72\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x28\x65\x6E\x76\x2E\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x29\x0A\x09\x09\x09\x7D\x0A\x0A\x09\x09\x09\x66\x75\x6E\x63\x74\x69\x6F\x6E\x20\x6F\x6E\x43\x68\x65\x63\x6B\x48\x6F\x73\x74\x62\x6C\x6F\x63\x6B\x28\x69\x64\x78\x2C\x20\x76\x61\x6C\x29\x20\x7B\x0A\x09\x09\x09\x09\x72\x65\x73\x63\x2E\x65\x6E\x76\x2E\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x5B\x69\x64\x78\x5D\x2E\x49\x73\x45\x6E\x61\x62\x6C\x65\x64\x20\x3D\x20\x76\x61\x6C\x0A\x09\x09\x09\x7D\x0A\x0A\x09\x09\x09\x61\x73\x79\x6E\x63\x20\x66\x75\x6E\x63\x74\x69\x6F\x6E\x20\x75\x70\x64\x61\x74\x65\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x28\x29\x20\x7B\x0A\x09\x09\x09\x09\x63\x6F\x6E\x73\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x72\x65\x73\x63\x2E\x75\x70\x64\x61\x74\x65\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x28\x72\x65\x73\x63\x2E\x65\x6E\x76\x2E\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x29\x0A\x09\x09\x09\x09\x69\x66\x20\x28\x72\x65\x73\x2E\x63\x6F\x64\x65\x20\x21\x3D\x20\x32\x30\x30\x29\x20\x7B\x0A\x09\x09\x09\x09\x09\x6E\x6F\x74\x69\x66\x45\x72\x72\x6F\x72\x28\x22\x75\x70\x64\x61\x74\x65\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x3A\x20\x22\x2C\x20\x72\x65\x73\x2E\x6D\x65\x73\x73\x61\x67\x65\x29\x0A\x09\x09\x09\x09\x09\x72\x65\x74\x75\x72\x6E\x0A\x09\x09\x09\x09\x7D\x0A\x0A\x09\x09\x09\x09\x72\x65\x6E\x64\x65\x72\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x28\x72\x65\x73\x2E\x64\x61\x74\x61\x29\x0A\x09\x09\x09\x09\x6E\x6F\x74\x69\x66\x49\x6E\x66\x6F\x28\x22\x54\x68\x65\x20\x68\x6F\x73\x74\x73\x20\x62\x6C\x6F\x63\x6B\x73\x20\x68\x61\x73\x20\x62\x65\x65\x6E\x20\x75\x70\x64\x61\x74\x65\x64\x2E\x22\x29\x0A\x09\x09\x09\x7D\x0A\x0A\x09\x09\x09\x66\x75\x6E\x63\x74\x69\x6F\x6E\x20\x72\x65\x6E\x64\x65\x72\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x28\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x29\x20\x7B\x0A\x09\x09\x09\x09\x6C\x65\x74\x20\x70\x61\x72\x65\x6E\x74\x20\x3D\x20\x64\x6F\x63\x75\x6D\x65\x6E\x74\x2E\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x42\x79\x49\x64\x28\x22\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x22\x29\x0A\x09\x09\x09\x09\x70\x61\x72\x65\x6E\x74\x2E\x69\x6E\x6E\x65\x72\x48\x54\x4D\x4C\x20\x3D\x20\x22\x22\x0A\x0A\x09\x09\x09\x09\x66\x6F\x72\x20\x28\x6C\x65\x74\x20\x78\x20\x3D\x20\x30\x3B\x20\x78\x20\x3C\x20\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x2E\x6C\x65\x6E\x67\x74\x68\x3B\x20\x78\x2B\x2B\x29\x20\x7B\x0A\x09\x09\x09\x09\x09\x6C\x65\x74\x20\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x20\x3D\x20\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x5B\x78\x5D\x0A\x09\x09\x09\x09\x09\x6C\x65\x74\x20\x69\x74\x65\x6D\x20\x3D\x20\x64\x6F\x63\x75\x6D\x65\x6E\x74\x2E\x63\x72\x65\x61\x74\x65\x45\x6C\x65\x6D\x65\x6E\x74\x28\x22\x64\x69\x76\x22\x29\x0A\x09\x09\x09\x09\x09\x69\x74\x65\x6D\x2E\x63\x6C\x61\x73\x73\x4C\x69\x73\x74\x2E\x61\x64\x64\x28\x22\x69\x74\x65\x6D\x22\x29\x0A\x09\x09\x09\x09\x09\x69\x74\x65\x6D\x2E\x69\x6E\x6E\x65\x72\x48\x54\x4D\x4C\x20\x3D\x20\x60\x0A\x09\x09\x09\x09\x09\x09\x3C\x73\x70\x61\x6E\x20\x63\x6C\x61\x73\x73\x3D\x22\x69\x73\x2D\x65\x6E\x61\x62\x6C\x65\x64\x22\x3E\x0A\x09\x09\x09\x09\x09\x09\x09\x3C\x69\x6E\x70\x75\x74\x0A\x09\x09\x09\x09\x09\x09\x09\x09\x74\x79\x70\x65\x3D\x22\x63\x68\x65\x63\x6B\x62\x6F\x78\x22\x0A\x09\x09\x09\x09\x09\x09\x09\x09\x24\x7B\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x2E\x49\x73\x45\x6E\x61\x62\x6C\x65\x64\x20\x3F\x20\x22\x63\x68\x65\x63\x6B\x65\x64\x22\x20\x3A\x20\x22\x22\x7D\x0A\x09\x09\x09\x09\x09\x09\x09\x09\x6F\x6E\x69\x6E\x70\x75\x74\x3D\x22\x6F\x6E\x43\x68\x65\x63\x6B\x48\x6F\x73\x74\x62\x6C\x6F\x63\x6B\x28\x27\x24\x7B\x78\x7D\x27\x2C\x20\x74\x68\x69\x73\x2E\x63\x68\x65\x63\x6B\x65\x64\x29\x22\x0A\x09\x09\x09\x09\x09\x09\x09\x2F\x3E\x0A\x09\x09\x09\x09\x09\x09\x3C\x2F\x73\x70\x61\x6E\x3E\x0A\x09\x09\x09\x09\x09\x09\x3C\x73\x70\x61\x6E\x20\x63\x6C\x61\x73\x73\x3D\x22\x69\x6E\x66\x6F\x22\x3E\x0A\x09\x09\x09\x09\x09\x09\x09\x3C\x64\x69\x76\x3E\x24\x7B\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x2E\x4E\x61\x6D\x65\x7D\x3C\x2F\x64\x69\x76\x3E\x0A\x09\x09\x09\x09\x09\x09\x09\x3C\x64\x69\x76\x3E\x20\x55\x52\x4C\x3A\x20\x3C\x69\x6E\x70\x75\x74\x20\x76\x61\x6C\x75\x65\x3D\x22\x24\x7B\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x2E\x55\x52\x4C\x7D\x22\x20\x64\x69\x73\x61\x62\x6C\x65\x64\x20\x2F\x3E\x20\x3C\x2F\x64\x69\x76\x3E\x0A\x09\x09\x09\x09\x09\x09\x09\x3C\x64\x69\x76\x3E\x20\x4C\x61\x73\x74\x20\x75\x70\x64\x61\x74\x65\x64\x20\x61\x74\x20\x24\x7B\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x2E\x4C\x61\x73\x74\x55\x70\x64\x61\x74\x65\x64\x7D\x20\x3C\x2F\x64\x69\x76\x3E\x0A\x09\x09\x09\x09\x09\x09\x3C\x2F\x73\x70\x61\x6E\x3E\x60\x0A\x0A\x09\x09\x09\x09\x09\x70\x61\x72\x65\x6E\x74\x2E\x61\x70\x70\x65\x6E\x64\x43\x68\x69\x6C\x64\x28\x69\x74\x65\x6D\x29\x0A\x09\x09\x09\x09\x7D\x0A\x09\x09\x09\x7D\x0A\x09\x09\x3C\x2F\x73\x63\x72\x69\x70\x74\x3E\x0A\x09\x3C\x2F\x62\x6F\x64\x79\x3E\x0A\x3C\x2F\x68\x74\x6D\x6C\x3E\x0A"),
+ Content: []byte("\x3C\x21\x44\x4F\x43\x54\x59\x50\x45\x20\x68\x74\x6D\x6C\x3E\x0A\x3C\x21\x2D\x2D\x20\x53\x50\x44\x58\x2D\x46\x69\x6C\x65\x43\x6F\x70\x79\x72\x69\x67\x68\x74\x54\x65\x78\x74\x3A\x20\x32\x30\x32\x31\x20\x4D\x2E\x20\x53\x68\x75\x6C\x68\x61\x6E\x20\x3C\x6D\x73\x40\x6B\x69\x6C\x61\x62\x69\x74\x2E\x69\x6E\x66\x6F\x20\x2D\x2D\x3E\x0A\x3C\x21\x2D\x2D\x20\x53\x50\x44\x58\x2D\x4C\x69\x63\x65\x6E\x73\x65\x2D\x49\x64\x65\x6E\x74\x69\x66\x69\x65\x72\x3A\x20\x47\x50\x4C\x2D\x33\x2E\x30\x2D\x6F\x72\x2D\x6C\x61\x74\x65\x72\x20\x2D\x2D\x3E\x0A\x3C\x68\x74\x6D\x6C\x20\x6C\x61\x6E\x67\x3D\x22\x65\x6E\x22\x3E\x0A\x09\x3C\x68\x65\x61\x64\x3E\x0A\x09\x09\x3C\x6D\x65\x74\x61\x20\x63\x68\x61\x72\x73\x65\x74\x3D\x22\x75\x74\x66\x2D\x38\x22\x20\x2F\x3E\x0A\x09\x09\x3C\x6D\x65\x74\x61\x20\x6E\x61\x6D\x65\x3D\x22\x76\x69\x65\x77\x70\x6F\x72\x74\x22\x20\x63\x6F\x6E\x74\x65\x6E\x74\x3D\x22\x77\x69\x64\x74\x68\x3D\x64\x65\x76\x69\x63\x65\x2D\x77\x69\x64\x74\x68\x2C\x20\x69\x6E\x69\x74\x69\x61\x6C\x2D\x73\x63\x61\x6C\x65\x3D\x31\x2C\x20\x73\x68\x72\x69\x6E\x6B\x2D\x74\x6F\x2D\x66\x69\x74\x3D\x6E\x6F\x22\x20\x2F\x3E\x0A\x09\x09\x3C\x6C\x69\x6E\x6B\x20\x72\x65\x6C\x3D\x22\x69\x63\x6F\x6E\x22\x20\x74\x79\x70\x65\x3D\x22\x69\x6D\x61\x67\x65\x2F\x70\x6E\x67\x22\x20\x68\x72\x65\x66\x3D\x22\x2F\x66\x61\x76\x69\x63\x6F\x6E\x2E\x70\x6E\x67\x22\x20\x2F\x3E\x0A\x09\x09\x3C\x6C\x69\x6E\x6B\x20\x72\x65\x6C\x3D\x22\x73\x74\x79\x6C\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3D\x22\x2F\x69\x6E\x64\x65\x78\x2E\x63\x73\x73\x22\x20\x2F\x3E\x0A\x09\x09\x3C\x74\x69\x74\x6C\x65\x3E\x72\x65\x73\x63\x61\x63\x68\x65\x64\x20\x7C\x20\x68\x6F\x73\x74\x73\x20\x62\x6C\x6F\x63\x6B\x73\x3C\x2F\x74\x69\x74\x6C\x65\x3E\x0A\x0A\x09\x09\x3C\x73\x74\x79\x6C\x65\x3E\x0A\x09\x09\x09\x2E\x62\x6C\x6F\x63\x6B\x5F\x73\x6F\x75\x72\x63\x65\x20\x7B\x0A\x09\x09\x09\x09\x77\x69\x64\x74\x68\x3A\x20\x63\x61\x6C\x63\x28\x31\x30\x30\x25\x20\x2D\x20\x32\x65\x6D\x29\x3B\x0A\x09\x09\x09\x09\x6F\x76\x65\x72\x66\x6C\x6F\x77\x3A\x20\x61\x75\x74\x6F\x3B\x0A\x09\x09\x09\x7D\x0A\x09\x09\x09\x2E\x62\x6C\x6F\x63\x6B\x5F\x73\x6F\x75\x72\x63\x65\x20\x69\x6E\x70\x75\x74\x3A\x64\x69\x73\x61\x62\x6C\x65\x64\x20\x7B\x0A\x09\x09\x09\x09\x63\x6F\x6C\x6F\x72\x3A\x20\x62\x6C\x61\x63\x6B\x3B\x0A\x09\x09\x09\x7D\x0A\x09\x09\x09\x2E\x69\x74\x65\x6D\x20\x7B\x0A\x09\x09\x09\x09\x77\x69\x64\x74\x68\x3A\x20\x31\x30\x30\x25\x3B\x0A\x09\x09\x09\x09\x6D\x61\x72\x67\x69\x6E\x2D\x62\x6F\x74\x74\x6F\x6D\x3A\x20\x31\x65\x6D\x3B\x0A\x09\x09\x09\x7D\x0A\x09\x09\x09\x2E\x69\x74\x65\x6D\x2E\x68\x65\x61\x64\x65\x72\x20\x7B\x0A\x09\x09\x09\x09\x66\x6F\x6E\x74\x2D\x77\x65\x69\x67\x68\x74\x3A\x20\x62\x6F\x6C\x64\x3B\x0A\x09\x09\x09\x09\x6D\x61\x72\x67\x69\x6E\x2D\x62\x6F\x74\x74\x6F\x6D\x3A\x20\x31\x65\x6D\x3B\x0A\x09\x09\x09\x09\x62\x6F\x72\x64\x65\x72\x2D\x62\x6F\x74\x74\x6F\x6D\x3A\x20\x31\x70\x78\x20\x73\x6F\x6C\x69\x64\x20\x73\x69\x6C\x76\x65\x72\x3B\x0A\x09\x09\x09\x7D\x0A\x09\x09\x09\x2E\x69\x74\x65\x6D\x20\x2E\x69\x73\x2D\x65\x6E\x61\x62\x6C\x65\x64\x20\x7B\x0A\x09\x09\x09\x09\x64\x69\x73\x70\x6C\x61\x79\x3A\x20\x69\x6E\x6C\x69\x6E\x65\x2D\x62\x6C\x6F\x63\x6B\x3B\x0A\x09\x09\x09\x09\x77\x69\x64\x74\x68\x3A\x20\x34\x65\x6D\x3B\x0A\x09\x09\x09\x09\x76\x65\x72\x74\x69\x63\x61\x6C\x2D\x61\x6C\x69\x67\x6E\x3A\x20\x74\x6F\x70\x3B\x0A\x09\x09\x09\x7D\x0A\x09\x09\x09\x2E\x69\x74\x65\x6D\x20\x2E\x69\x6E\x66\x6F\x20\x7B\x0A\x09\x09\x09\x09\x64\x69\x73\x70\x6C\x61\x79\x3A\x20\x69\x6E\x6C\x69\x6E\x65\x2D\x62\x6C\x6F\x63\x6B\x3B\x0A\x09\x09\x09\x09\x77\x69\x64\x74\x68\x3A\x20\x63\x61\x6C\x63\x28\x31\x30\x30\x25\x20\x2D\x20\x36\x65\x6D\x29\x3B\x0A\x09\x09\x09\x7D\x0A\x09\x09\x09\x2E\x69\x74\x65\x6D\x20\x2E\x69\x6E\x66\x6F\x20\x69\x6E\x70\x75\x74\x20\x7B\x0A\x09\x09\x09\x09\x77\x69\x64\x74\x68\x3A\x20\x63\x61\x6C\x63\x28\x31\x30\x30\x25\x20\x2D\x20\x34\x65\x6D\x29\x3B\x0A\x09\x09\x09\x7D\x0A\x09\x09\x3C\x2F\x73\x74\x79\x6C\x65\x3E\x0A\x09\x3C\x2F\x68\x65\x61\x64\x3E\x0A\x09\x3C\x62\x6F\x64\x79\x20\x6F\x6E\x6C\x6F\x61\x64\x3D\x22\x6F\x6E\x4C\x6F\x61\x64\x28\x29\x22\x3E\x0A\x09\x09\x3C\x6E\x61\x76\x20\x63\x6C\x61\x73\x73\x3D\x22\x6D\x65\x6E\x75\x22\x3E\x0A\x09\x09\x09\x3C\x61\x20\x68\x72\x65\x66\x3D\x22\x2F\x22\x3E\x20\x72\x65\x73\x63\x61\x63\x68\x65\x64\x20\x3C\x2F\x61\x3E\x0A\x09\x09\x09\x2F\x0A\x09\x09\x09\x3C\x61\x20\x68\x72\x65\x66\x3D\x22\x2F\x65\x6E\x76\x69\x72\x6F\x6E\x6D\x65\x6E\x74\x2F\x22\x3E\x20\x45\x6E\x76\x69\x72\x6F\x6E\x6D\x65\x6E\x74\x20\x3C\x2F\x61\x3E\x0A\x09\x09\x09\x2F\x0A\x09\x09\x09\x3C\x61\x20\x68\x72\x65\x66\x3D\x22\x2F\x68\x6F\x73\x74\x73\x5F\x62\x6C\x6F\x63\x6B\x73\x2F\x22\x20\x63\x6C\x61\x73\x73\x3D\x22\x61\x63\x74\x69\x76\x65\x22\x3E\x20\x48\x6F\x73\x74\x73\x20\x62\x6C\x6F\x63\x6B\x73\x20\x3C\x2F\x61\x3E\x0A\x09\x09\x09\x2F\x0A\x09\x09\x09\x3C\x61\x20\x68\x72\x65\x66\x3D\x22\x2F\x68\x6F\x73\x74\x73\x2E\x64\x2F\x22\x3E\x20\x68\x6F\x73\x74\x73\x2E\x64\x20\x3C\x2F\x61\x3E\x0A\x09\x09\x09\x2F\x0A\x09\x09\x09\x3C\x61\x20\x68\x72\x65\x66\x3D\x22\x2F\x7A\x6F\x6E\x65\x2E\x64\x2F\x22\x3E\x20\x7A\x6F\x6E\x65\x2E\x64\x20\x3C\x2F\x61\x3E\x0A\x09\x09\x3C\x2F\x6E\x61\x76\x3E\x0A\x0A\x09\x09\x3C\x64\x69\x76\x20\x69\x64\x3D\x22\x6E\x6F\x74\x69\x66\x22\x3E\x3C\x2F\x64\x69\x76\x3E\x0A\x0A\x09\x09\x3C\x70\x3E\x43\x6F\x6E\x66\x69\x67\x75\x72\x65\x20\x74\x68\x65\x20\x73\x6F\x75\x72\x63\x65\x20\x6F\x66\x20\x62\x6C\x6F\x63\x6B\x65\x64\x20\x68\x6F\x73\x74\x73\x20\x66\x69\x6C\x65\x2E\x3C\x2F\x70\x3E\x0A\x0A\x09\x09\x3C\x64\x69\x76\x20\x63\x6C\x61\x73\x73\x3D\x22\x62\x6C\x6F\x63\x6B\x5F\x73\x6F\x75\x72\x63\x65\x22\x3E\x0A\x09\x09\x09\x3C\x64\x69\x76\x20\x63\x6C\x61\x73\x73\x3D\x22\x69\x74\x65\x6D\x20\x68\x65\x61\x64\x65\x72\x22\x3E\x0A\x09\x09\x09\x09\x3C\x73\x70\x61\x6E\x20\x63\x6C\x61\x73\x73\x3D\x22\x69\x73\x2D\x65\x6E\x61\x62\x6C\x65\x64\x22\x3E\x20\x45\x6E\x61\x62\x6C\x65\x64\x20\x3C\x2F\x73\x70\x61\x6E\x3E\x0A\x09\x09\x09\x09\x3C\x73\x70\x61\x6E\x20\x63\x6C\x61\x73\x73\x3D\x22\x69\x6E\x66\x6F\x22\x3E\x20\x4E\x61\x6D\x65\x20\x3C\x2F\x73\x70\x61\x6E\x3E\x0A\x09\x09\x09\x3C\x2F\x64\x69\x76\x3E\x0A\x09\x09\x3C\x2F\x64\x69\x76\x3E\x0A\x09\x09\x3C\x64\x69\x76\x20\x69\x64\x3D\x22\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x22\x20\x63\x6C\x61\x73\x73\x3D\x22\x62\x6C\x6F\x63\x6B\x5F\x73\x6F\x75\x72\x63\x65\x22\x3E\x3C\x2F\x64\x69\x76\x3E\x0A\x0A\x09\x09\x3C\x64\x69\x76\x3E\x0A\x09\x09\x09\x3C\x62\x75\x74\x74\x6F\x6E\x20\x6F\x6E\x63\x6C\x69\x63\x6B\x3D\x22\x75\x70\x64\x61\x74\x65\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x28\x29\x22\x3E\x53\x61\x76\x65\x3C\x2F\x62\x75\x74\x74\x6F\x6E\x3E\x0A\x09\x09\x3C\x2F\x64\x69\x76\x3E\x0A\x0A\x09\x09\x3C\x73\x63\x72\x69\x70\x74\x20\x73\x72\x63\x3D\x22\x2F\x69\x6E\x64\x65\x78\x2E\x6A\x73\x22\x3E\x3C\x2F\x73\x63\x72\x69\x70\x74\x3E\x0A\x09\x09\x3C\x73\x63\x72\x69\x70\x74\x20\x73\x72\x63\x3D\x22\x2F\x72\x65\x73\x63\x61\x63\x68\x65\x64\x2E\x6A\x73\x22\x3E\x3C\x2F\x73\x63\x72\x69\x70\x74\x3E\x0A\x09\x09\x3C\x73\x63\x72\x69\x70\x74\x3E\x0A\x09\x09\x09\x6C\x65\x74\x20\x72\x65\x73\x63\x20\x3D\x20\x6E\x75\x6C\x6C\x0A\x0A\x09\x09\x09\x66\x75\x6E\x63\x74\x69\x6F\x6E\x20\x6F\x6E\x4C\x6F\x61\x64\x28\x29\x20\x7B\x0A\x09\x09\x09\x09\x72\x65\x73\x63\x20\x3D\x20\x6E\x65\x77\x20\x52\x65\x73\x63\x61\x63\x68\x65\x64\x28\x22\x22\x29\x0A\x09\x09\x09\x09\x67\x65\x74\x45\x6E\x76\x69\x72\x6F\x6E\x6D\x65\x6E\x74\x28\x29\x0A\x09\x09\x09\x7D\x0A\x0A\x09\x09\x09\x61\x73\x79\x6E\x63\x20\x66\x75\x6E\x63\x74\x69\x6F\x6E\x20\x67\x65\x74\x45\x6E\x76\x69\x72\x6F\x6E\x6D\x65\x6E\x74\x28\x29\x20\x7B\x0A\x09\x09\x09\x09\x63\x6F\x6E\x73\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x72\x65\x73\x63\x2E\x67\x65\x74\x45\x6E\x76\x69\x72\x6F\x6E\x6D\x65\x6E\x74\x28\x29\x0A\x09\x09\x09\x09\x69\x66\x20\x28\x72\x65\x73\x2E\x63\x6F\x64\x65\x20\x21\x3D\x20\x32\x30\x30\x29\x20\x7B\x0A\x09\x09\x09\x09\x09\x6E\x6F\x74\x69\x66\x45\x72\x72\x6F\x72\x28\x72\x65\x73\x2E\x6D\x65\x73\x73\x61\x67\x65\x29\x0A\x09\x09\x09\x09\x09\x72\x65\x74\x75\x72\x6E\x0A\x09\x09\x09\x09\x7D\x0A\x0A\x09\x09\x09\x09\x6C\x65\x74\x20\x65\x6E\x76\x20\x3D\x20\x72\x65\x73\x2E\x64\x61\x74\x61\x0A\x09\x09\x09\x09\x72\x65\x6E\x64\x65\x72\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x28\x65\x6E\x76\x2E\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x29\x0A\x09\x09\x09\x7D\x0A\x0A\x09\x09\x09\x66\x75\x6E\x63\x74\x69\x6F\x6E\x20\x6F\x6E\x43\x68\x65\x63\x6B\x48\x6F\x73\x74\x62\x6C\x6F\x63\x6B\x28\x6B\x65\x79\x2C\x20\x76\x61\x6C\x29\x20\x7B\x0A\x09\x09\x09\x09\x72\x65\x73\x63\x2E\x65\x6E\x76\x2E\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x5B\x6B\x65\x79\x5D\x2E\x49\x73\x45\x6E\x61\x62\x6C\x65\x64\x20\x3D\x20\x76\x61\x6C\x0A\x09\x09\x09\x7D\x0A\x0A\x09\x09\x09\x61\x73\x79\x6E\x63\x20\x66\x75\x6E\x63\x74\x69\x6F\x6E\x20\x75\x70\x64\x61\x74\x65\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x28\x29\x20\x7B\x0A\x09\x09\x09\x09\x63\x6F\x6E\x73\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x72\x65\x73\x63\x2E\x75\x70\x64\x61\x74\x65\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x28\x72\x65\x73\x63\x2E\x65\x6E\x76\x2E\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x29\x0A\x09\x09\x09\x09\x69\x66\x20\x28\x72\x65\x73\x2E\x63\x6F\x64\x65\x20\x21\x3D\x20\x32\x30\x30\x29\x20\x7B\x0A\x09\x09\x09\x09\x09\x6E\x6F\x74\x69\x66\x45\x72\x72\x6F\x72\x28\x22\x75\x70\x64\x61\x74\x65\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x3A\x20\x22\x2C\x20\x72\x65\x73\x2E\x6D\x65\x73\x73\x61\x67\x65\x29\x0A\x09\x09\x09\x09\x09\x72\x65\x74\x75\x72\x6E\x0A\x09\x09\x09\x09\x7D\x0A\x0A\x09\x09\x09\x09\x72\x65\x6E\x64\x65\x72\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x28\x72\x65\x73\x2E\x64\x61\x74\x61\x29\x0A\x09\x09\x09\x09\x6E\x6F\x74\x69\x66\x49\x6E\x66\x6F\x28\x22\x54\x68\x65\x20\x68\x6F\x73\x74\x73\x20\x62\x6C\x6F\x63\x6B\x73\x20\x68\x61\x73\x20\x62\x65\x65\x6E\x20\x75\x70\x64\x61\x74\x65\x64\x2E\x22\x29\x0A\x09\x09\x09\x7D\x0A\x0A\x09\x09\x09\x66\x75\x6E\x63\x74\x69\x6F\x6E\x20\x72\x65\x6E\x64\x65\x72\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x28\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x29\x20\x7B\x0A\x09\x09\x09\x09\x6C\x65\x74\x20\x70\x61\x72\x65\x6E\x74\x20\x3D\x20\x64\x6F\x63\x75\x6D\x65\x6E\x74\x2E\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x42\x79\x49\x64\x28\x22\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x22\x29\x0A\x09\x09\x09\x09\x70\x61\x72\x65\x6E\x74\x2E\x69\x6E\x6E\x65\x72\x48\x54\x4D\x4C\x20\x3D\x20\x22\x22\x0A\x0A\x09\x09\x09\x09\x66\x6F\x72\x20\x28\x76\x61\x72\x20\x6B\x20\x69\x6E\x20\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x29\x20\x7B\x0A\x09\x09\x09\x09\x09\x69\x66\x20\x28\x21\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x2E\x68\x61\x73\x4F\x77\x6E\x50\x72\x6F\x70\x65\x72\x74\x79\x28\x6B\x29\x29\x20\x7B\x0A\x09\x09\x09\x09\x09\x09\x63\x6F\x6E\x74\x69\x6E\x75\x65\x3B\x0A\x09\x09\x09\x09\x09\x7D\x0A\x0A\x09\x09\x09\x09\x09\x6C\x65\x74\x20\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x20\x3D\x20\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x5B\x6B\x5D\x0A\x09\x09\x09\x09\x09\x6C\x65\x74\x20\x69\x74\x65\x6D\x20\x3D\x20\x64\x6F\x63\x75\x6D\x65\x6E\x74\x2E\x63\x72\x65\x61\x74\x65\x45\x6C\x65\x6D\x65\x6E\x74\x28\x22\x64\x69\x76\x22\x29\x0A\x09\x09\x09\x09\x09\x69\x74\x65\x6D\x2E\x63\x6C\x61\x73\x73\x4C\x69\x73\x74\x2E\x61\x64\x64\x28\x22\x69\x74\x65\x6D\x22\x29\x0A\x09\x09\x09\x09\x09\x69\x74\x65\x6D\x2E\x69\x6E\x6E\x65\x72\x48\x54\x4D\x4C\x20\x3D\x20\x60\x0A\x09\x09\x09\x09\x09\x09\x3C\x73\x70\x61\x6E\x20\x63\x6C\x61\x73\x73\x3D\x22\x69\x73\x2D\x65\x6E\x61\x62\x6C\x65\x64\x22\x3E\x0A\x09\x09\x09\x09\x09\x09\x09\x3C\x69\x6E\x70\x75\x74\x0A\x09\x09\x09\x09\x09\x09\x09\x09\x74\x79\x70\x65\x3D\x22\x63\x68\x65\x63\x6B\x62\x6F\x78\x22\x0A\x09\x09\x09\x09\x09\x09\x09\x09\x24\x7B\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x2E\x49\x73\x45\x6E\x61\x62\x6C\x65\x64\x20\x3F\x20\x22\x63\x68\x65\x63\x6B\x65\x64\x22\x20\x3A\x20\x22\x22\x7D\x0A\x09\x09\x09\x09\x09\x09\x09\x09\x6F\x6E\x69\x6E\x70\x75\x74\x3D\x22\x6F\x6E\x43\x68\x65\x63\x6B\x48\x6F\x73\x74\x62\x6C\x6F\x63\x6B\x28\x27\x24\x7B\x6B\x7D\x27\x2C\x20\x74\x68\x69\x73\x2E\x63\x68\x65\x63\x6B\x65\x64\x29\x22\x0A\x09\x09\x09\x09\x09\x09\x09\x2F\x3E\x0A\x09\x09\x09\x09\x09\x09\x3C\x2F\x73\x70\x61\x6E\x3E\x0A\x09\x09\x09\x09\x09\x09\x3C\x73\x70\x61\x6E\x20\x63\x6C\x61\x73\x73\x3D\x22\x69\x6E\x66\x6F\x22\x3E\x0A\x09\x09\x09\x09\x09\x09\x09\x3C\x64\x69\x76\x3E\x24\x7B\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x2E\x4E\x61\x6D\x65\x7D\x3C\x2F\x64\x69\x76\x3E\x0A\x09\x09\x09\x09\x09\x09\x09\x3C\x64\x69\x76\x3E\x20\x55\x52\x4C\x3A\x20\x3C\x69\x6E\x70\x75\x74\x20\x76\x61\x6C\x75\x65\x3D\x22\x24\x7B\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x2E\x55\x52\x4C\x7D\x22\x20\x64\x69\x73\x61\x62\x6C\x65\x64\x20\x2F\x3E\x20\x3C\x2F\x64\x69\x76\x3E\x0A\x09\x09\x09\x09\x09\x09\x09\x3C\x64\x69\x76\x3E\x20\x4C\x61\x73\x74\x20\x75\x70\x64\x61\x74\x65\x64\x20\x61\x74\x20\x24\x7B\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x2E\x4C\x61\x73\x74\x55\x70\x64\x61\x74\x65\x64\x7D\x20\x3C\x2F\x64\x69\x76\x3E\x0A\x09\x09\x09\x09\x09\x09\x3C\x2F\x73\x70\x61\x6E\x3E\x60\x0A\x0A\x09\x09\x09\x09\x09\x70\x61\x72\x65\x6E\x74\x2E\x61\x70\x70\x65\x6E\x64\x43\x68\x69\x6C\x64\x28\x69\x74\x65\x6D\x29\x0A\x09\x09\x09\x09\x7D\x0A\x09\x09\x09\x7D\x0A\x09\x09\x3C\x2F\x73\x63\x72\x69\x70\x74\x3E\x0A\x09\x3C\x2F\x62\x6F\x64\x79\x3E\x0A\x3C\x2F\x68\x74\x6D\x6C\x3E\x0A"),
}
node.SetMode(420)
- node.SetModTimeUnix(1647276210, 803014611)
+ node.SetModTimeUnix(1650380258, 721203491)
node.SetName("index.html")
- node.SetSize(3229)
+ node.SetSize(3278)
return node
}
@@ -190,7 +190,7 @@ func generate__www_rescached_js() *memfs.Node {
Content: []byte("\x2F\x2F\x20\x53\x50\x44\x58\x2D\x46\x69\x6C\x65\x43\x6F\x70\x79\x72\x69\x67\x68\x74\x54\x65\x78\x74\x3A\x20\x32\x30\x32\x31\x20\x4D\x2E\x20\x53\x68\x75\x6C\x68\x61\x6E\x20\x3C\x6D\x73\x40\x6B\x69\x6C\x61\x62\x69\x74\x2E\x69\x6E\x66\x6F\x3E\x0A\x2F\x2F\x20\x53\x50\x44\x58\x2D\x4C\x69\x63\x65\x6E\x73\x65\x2D\x49\x64\x65\x6E\x74\x69\x66\x69\x65\x72\x3A\x20\x47\x50\x4C\x2D\x33\x2E\x30\x2D\x6F\x72\x2D\x6C\x61\x74\x65\x72\x0A\x0A\x63\x6F\x6E\x73\x74\x20\x52\x52\x54\x79\x70\x65\x73\x20\x3D\x20\x7B\x0A\x09\x31\x3A\x20\x22\x41\x22\x2C\x0A\x09\x32\x3A\x20\x22\x4E\x53\x22\x2C\x0A\x09\x33\x3A\x20\x22\x4D\x44\x22\x2C\x0A\x09\x34\x3A\x20\x22\x4D\x46\x22\x2C\x0A\x09\x35\x3A\x20\x22\x43\x4E\x41\x4D\x45\x22\x2C\x0A\x09\x36\x3A\x20\x22\x53\x4F\x41\x22\x2C\x0A\x09\x37\x3A\x20\x22\x4D\x42\x22\x2C\x0A\x09\x38\x3A\x20\x22\x4D\x47\x22\x2C\x0A\x09\x39\x3A\x20\x22\x4D\x52\x22\x2C\x0A\x09\x31\x30\x3A\x20\x22\x4E\x55\x4C\x4C\x22\x2C\x0A\x09\x31\x31\x3A\x20\x22\x57\x4B\x53\x22\x2C\x0A\x09\x31\x32\x3A\x20\x22\x50\x54\x52\x22\x2C\x0A\x09\x31\x33\x3A\x20\x22\x48\x49\x4E\x46\x4F\x22\x2C\x0A\x09\x31\x34\x3A\x20\x22\x4D\x49\x4E\x46\x4F\x22\x2C\x0A\x09\x31\x35\x3A\x20\x22\x4D\x58\x22\x2C\x0A\x09\x31\x36\x3A\x20\x22\x54\x58\x54\x22\x2C\x0A\x09\x32\x38\x3A\x20\x22\x41\x41\x41\x41\x22\x2C\x0A\x09\x33\x33\x3A\x20\x22\x53\x52\x56\x22\x2C\x0A\x09\x34\x31\x3A\x20\x22\x4F\x50\x54\x22\x2C\x0A\x7D\x0A\x0A\x66\x75\x6E\x63\x74\x69\x6F\x6E\x20\x67\x65\x74\x52\x52\x54\x79\x70\x65\x4E\x61\x6D\x65\x28\x6B\x29\x20\x7B\x0A\x09\x6C\x65\x74\x20\x76\x20\x3D\x20\x52\x52\x54\x79\x70\x65\x73\x5B\x6B\x5D\x0A\x09\x69\x66\x20\x28\x76\x20\x3D\x3D\x3D\x20\x22\x22\x29\x20\x7B\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x6B\x0A\x09\x7D\x0A\x09\x72\x65\x74\x75\x72\x6E\x20\x76\x0A\x7D\x0A\x0A\x63\x6C\x61\x73\x73\x20\x52\x65\x73\x63\x61\x63\x68\x65\x64\x20\x7B\x0A\x09\x73\x74\x61\x74\x69\x63\x20\x6E\x61\x6E\x6F\x53\x65\x63\x6F\x6E\x64\x73\x20\x3D\x20\x31\x30\x30\x30\x30\x30\x30\x30\x30\x30\x0A\x09\x73\x74\x61\x74\x69\x63\x20\x61\x70\x69\x43\x61\x63\x68\x65\x73\x20\x3D\x20\x22\x2F\x61\x70\x69\x2F\x63\x61\x63\x68\x65\x73\x22\x0A\x09\x73\x74\x61\x74\x69\x63\x20\x61\x70\x69\x43\x61\x63\x68\x65\x73\x53\x65\x61\x72\x63\x68\x20\x3D\x20\x22\x2F\x61\x70\x69\x2F\x63\x61\x63\x68\x65\x73\x2F\x73\x65\x61\x72\x63\x68\x22\x0A\x09\x73\x74\x61\x74\x69\x63\x20\x61\x70\x69\x48\x6F\x73\x74\x73\x64\x20\x3D\x20\x22\x2F\x61\x70\x69\x2F\x68\x6F\x73\x74\x73\x2E\x64\x2F\x22\x0A\x09\x73\x74\x61\x74\x69\x63\x20\x61\x70\x69\x5A\x6F\x6E\x65\x64\x20\x3D\x20\x22\x2F\x61\x70\x69\x2F\x7A\x6F\x6E\x65\x2E\x64\x2F\x22\x0A\x0A\x09\x63\x6F\x6E\x73\x74\x72\x75\x63\x74\x6F\x72\x28\x73\x65\x72\x76\x65\x72\x29\x20\x7B\x0A\x09\x09\x74\x68\x69\x73\x2E\x73\x65\x72\x76\x65\x72\x20\x3D\x20\x73\x65\x72\x76\x65\x72\x0A\x09\x09\x74\x68\x69\x73\x2E\x65\x6E\x76\x20\x3D\x20\x7B\x7D\x0A\x09\x7D\x0A\x0A\x09\x61\x73\x79\x6E\x63\x20\x43\x61\x63\x68\x65\x73\x28\x29\x20\x7B\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x66\x65\x74\x63\x68\x28\x74\x68\x69\x73\x2E\x73\x65\x72\x76\x65\x72\x20\x2B\x20\x52\x65\x73\x63\x61\x63\x68\x65\x64\x2E\x61\x70\x69\x43\x61\x63\x68\x65\x73\x2C\x20\x7B\x0A\x09\x09\x09\x68\x65\x61\x64\x65\x72\x73\x3A\x20\x7B\x0A\x09\x09\x09\x09\x43\x6F\x6E\x6E\x65\x63\x74\x69\x6F\x6E\x3A\x20\x22\x6B\x65\x65\x70\x2D\x61\x6C\x69\x76\x65\x22\x2C\x0A\x09\x09\x09\x7D\x2C\x0A\x09\x09\x7D\x29\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x61\x77\x61\x69\x74\x20\x72\x65\x73\x2E\x6A\x73\x6F\x6E\x28\x29\x0A\x09\x7D\x0A\x0A\x09\x61\x73\x79\x6E\x63\x20\x43\x61\x63\x68\x65\x52\x65\x6D\x6F\x76\x65\x28\x71\x6E\x61\x6D\x65\x29\x20\x7B\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x66\x65\x74\x63\x68\x28\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x73\x65\x72\x76\x65\x72\x20\x2B\x20\x52\x65\x73\x63\x61\x63\x68\x65\x64\x2E\x61\x70\x69\x43\x61\x63\x68\x65\x73\x20\x2B\x20\x22\x3F\x6E\x61\x6D\x65\x3D\x22\x20\x2B\x20\x71\x6E\x61\x6D\x65\x2C\x0A\x09\x09\x09\x7B\x0A\x09\x09\x09\x09\x6D\x65\x74\x68\x6F\x64\x3A\x20\x22\x44\x45\x4C\x45\x54\x45\x22\x2C\x0A\x09\x09\x09\x7D\x2C\x0A\x09\x09\x29\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x61\x77\x61\x69\x74\x20\x72\x65\x73\x2E\x6A\x73\x6F\x6E\x28\x29\x0A\x09\x7D\x0A\x0A\x09\x61\x73\x79\x6E\x63\x20\x53\x65\x61\x72\x63\x68\x28\x71\x75\x65\x72\x79\x29\x20\x7B\x0A\x09\x09\x63\x6F\x6E\x73\x6F\x6C\x65\x2E\x6C\x6F\x67\x28\x22\x53\x65\x61\x72\x63\x68\x3A\x20\x22\x2C\x20\x71\x75\x65\x72\x79\x29\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x66\x65\x74\x63\x68\x28\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x73\x65\x72\x76\x65\x72\x20\x2B\x0A\x09\x09\x09\x09\x52\x65\x73\x63\x61\x63\x68\x65\x64\x2E\x61\x70\x69\x43\x61\x63\x68\x65\x73\x53\x65\x61\x72\x63\x68\x20\x2B\x0A\x09\x09\x09\x09\x22\x3F\x71\x75\x65\x72\x79\x3D\x22\x20\x2B\x0A\x09\x09\x09\x09\x71\x75\x65\x72\x79\x2C\x0A\x09\x09\x29\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x61\x77\x61\x69\x74\x20\x72\x65\x73\x2E\x6A\x73\x6F\x6E\x28\x29\x0A\x09\x7D\x0A\x0A\x09\x61\x73\x79\x6E\x63\x20\x48\x6F\x73\x74\x73\x46\x69\x6C\x65\x43\x72\x65\x61\x74\x65\x28\x6E\x61\x6D\x65\x29\x20\x7B\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x68\x74\x74\x70\x52\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x66\x65\x74\x63\x68\x28\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x73\x65\x72\x76\x65\x72\x20\x2B\x20\x52\x65\x73\x63\x61\x63\x68\x65\x64\x2E\x61\x70\x69\x48\x6F\x73\x74\x73\x64\x20\x2B\x20\x6E\x61\x6D\x65\x2C\x0A\x09\x09\x09\x7B\x0A\x09\x09\x09\x09\x6D\x65\x74\x68\x6F\x64\x3A\x20\x22\x50\x55\x54\x22\x2C\x0A\x09\x09\x09\x7D\x2C\x0A\x09\x09\x29\x0A\x09\x09\x6C\x65\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x68\x74\x74\x70\x52\x65\x73\x2E\x6A\x73\x6F\x6E\x28\x29\x0A\x09\x09\x69\x66\x20\x28\x72\x65\x73\x2E\x63\x6F\x64\x65\x20\x3D\x3D\x3D\x20\x32\x30\x30\x29\x20\x7B\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x65\x6E\x76\x2E\x48\x6F\x73\x74\x73\x46\x69\x6C\x65\x73\x5B\x6E\x61\x6D\x65\x5D\x20\x3D\x20\x7B\x0A\x09\x09\x09\x09\x4E\x61\x6D\x65\x3A\x20\x6E\x61\x6D\x65\x2C\x0A\x09\x09\x09\x09\x52\x65\x63\x6F\x72\x64\x73\x3A\x20\x5B\x5D\x2C\x0A\x09\x09\x09\x7D\x0A\x09\x09\x7D\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x72\x65\x73\x0A\x09\x7D\x0A\x0A\x09\x61\x73\x79\x6E\x63\x20\x67\x65\x74\x45\x6E\x76\x69\x72\x6F\x6E\x6D\x65\x6E\x74\x28\x29\x20\x7B\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x68\x74\x74\x70\x52\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x66\x65\x74\x63\x68\x28\x74\x68\x69\x73\x2E\x73\x65\x72\x76\x65\x72\x20\x2B\x20\x22\x2F\x61\x70\x69\x2F\x65\x6E\x76\x69\x72\x6F\x6E\x6D\x65\x6E\x74\x22\x29\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x68\x74\x74\x70\x52\x65\x73\x2E\x6A\x73\x6F\x6E\x28\x29\x0A\x0A\x09\x09\x69\x66\x20\x28\x68\x74\x74\x70\x52\x65\x73\x2E\x73\x74\x61\x74\x75\x73\x20\x3D\x3D\x3D\x20\x32\x30\x30\x29\x20\x7B\x0A\x09\x09\x09\x72\x65\x73\x2E\x64\x61\x74\x61\x2E\x50\x72\x75\x6E\x65\x44\x65\x6C\x61\x79\x20\x3D\x0A\x09\x09\x09\x09\x72\x65\x73\x2E\x64\x61\x74\x61\x2E\x50\x72\x75\x6E\x65\x44\x65\x6C\x61\x79\x20\x2F\x20\x52\x65\x73\x63\x61\x63\x68\x65\x64\x2E\x6E\x61\x6E\x6F\x53\x65\x63\x6F\x6E\x64\x73\x0A\x09\x09\x09\x72\x65\x73\x2E\x64\x61\x74\x61\x2E\x50\x72\x75\x6E\x65\x54\x68\x72\x65\x73\x68\x6F\x6C\x64\x20\x3D\x0A\x09\x09\x09\x09\x72\x65\x73\x2E\x64\x61\x74\x61\x2E\x50\x72\x75\x6E\x65\x54\x68\x72\x65\x73\x68\x6F\x6C\x64\x20\x2F\x0A\x09\x09\x09\x09\x52\x65\x73\x63\x61\x63\x68\x65\x64\x2E\x6E\x61\x6E\x6F\x53\x65\x63\x6F\x6E\x64\x73\x0A\x0A\x09\x09\x09\x66\x6F\x72\x20\x28\x6C\x65\x74\x20\x6B\x20\x69\x6E\x20\x72\x65\x73\x2E\x64\x61\x74\x61\x2E\x48\x6F\x73\x74\x73\x46\x69\x6C\x65\x73\x29\x20\x7B\x0A\x09\x09\x09\x09\x69\x66\x20\x28\x21\x72\x65\x73\x2E\x64\x61\x74\x61\x2E\x48\x6F\x73\x74\x73\x46\x69\x6C\x65\x73\x2E\x68\x61\x73\x4F\x77\x6E\x50\x72\x6F\x70\x65\x72\x74\x79\x28\x6B\x29\x29\x20\x7B\x0A\x09\x09\x09\x09\x09\x63\x6F\x6E\x74\x69\x6E\x75\x65\x0A\x09\x09\x09\x09\x7D\x0A\x09\x09\x09\x09\x6C\x65\x74\x20\x68\x66\x20\x3D\x20\x72\x65\x73\x2E\x64\x61\x74\x61\x2E\x48\x6F\x73\x74\x73\x46\x69\x6C\x65\x73\x5B\x6B\x5D\x0A\x09\x09\x09\x09\x69\x66\x20\x28\x74\x79\x70\x65\x6F\x66\x20\x68\x66\x2E\x52\x65\x63\x6F\x72\x64\x73\x20\x3D\x3D\x3D\x20\x22\x75\x6E\x64\x65\x66\x69\x6E\x65\x64\x22\x29\x20\x7B\x0A\x09\x09\x09\x09\x09\x68\x66\x2E\x52\x65\x63\x6F\x72\x64\x73\x20\x3D\x20\x5B\x5D\x0A\x09\x09\x09\x09\x7D\x0A\x09\x09\x09\x7D\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x65\x6E\x76\x20\x3D\x20\x72\x65\x73\x2E\x64\x61\x74\x61\x0A\x09\x09\x7D\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x72\x65\x73\x0A\x09\x7D\x0A\x0A\x09\x47\x65\x74\x52\x52\x54\x79\x70\x65\x4E\x61\x6D\x65\x28\x6B\x29\x20\x7B\x0A\x09\x09\x6C\x65\x74\x20\x76\x20\x3D\x20\x52\x52\x54\x79\x70\x65\x73\x5B\x6B\x5D\x0A\x09\x09\x69\x66\x20\x28\x76\x20\x3D\x3D\x3D\x20\x22\x22\x29\x20\x7B\x0A\x09\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x6B\x0A\x09\x09\x7D\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x76\x0A\x09\x7D\x0A\x0A\x09\x61\x73\x79\x6E\x63\x20\x48\x6F\x73\x74\x73\x46\x69\x6C\x65\x44\x65\x6C\x65\x74\x65\x28\x6E\x61\x6D\x65\x29\x20\x7B\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x68\x74\x74\x70\x52\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x66\x65\x74\x63\x68\x28\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x73\x65\x72\x76\x65\x72\x20\x2B\x20\x52\x65\x73\x63\x61\x63\x68\x65\x64\x2E\x61\x70\x69\x48\x6F\x73\x74\x73\x64\x20\x2B\x20\x6E\x61\x6D\x65\x2C\x0A\x09\x09\x09\x7B\x0A\x09\x09\x09\x09\x6D\x65\x74\x68\x6F\x64\x3A\x20\x22\x44\x45\x4C\x45\x54\x45\x22\x2C\x0A\x09\x09\x09\x7D\x2C\x0A\x09\x09\x29\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x68\x74\x74\x70\x52\x65\x73\x2E\x6A\x73\x6F\x6E\x28\x29\x0A\x09\x09\x69\x66\x20\x28\x68\x74\x74\x70\x52\x65\x73\x2E\x73\x74\x61\x74\x75\x73\x20\x3D\x3D\x3D\x20\x32\x30\x30\x29\x20\x7B\x0A\x09\x09\x09\x64\x65\x6C\x65\x74\x65\x20\x74\x68\x69\x73\x2E\x65\x6E\x76\x2E\x48\x6F\x73\x74\x73\x46\x69\x6C\x65\x73\x5B\x6E\x61\x6D\x65\x5D\x0A\x09\x09\x7D\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x72\x65\x73\x0A\x09\x7D\x0A\x0A\x09\x61\x73\x79\x6E\x63\x20\x48\x6F\x73\x74\x73\x46\x69\x6C\x65\x47\x65\x74\x28\x6E\x61\x6D\x65\x29\x20\x7B\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x68\x74\x74\x70\x52\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x66\x65\x74\x63\x68\x28\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x73\x65\x72\x76\x65\x72\x20\x2B\x20\x52\x65\x73\x63\x61\x63\x68\x65\x64\x2E\x61\x70\x69\x48\x6F\x73\x74\x73\x64\x20\x2B\x20\x6E\x61\x6D\x65\x2C\x0A\x09\x09\x29\x0A\x09\x09\x6C\x65\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x68\x74\x74\x70\x52\x65\x73\x2E\x6A\x73\x6F\x6E\x28\x29\x0A\x09\x09\x69\x66\x20\x28\x68\x74\x74\x70\x52\x65\x73\x2E\x53\x74\x61\x74\x75\x73\x20\x3D\x3D\x3D\x20\x32\x30\x30\x29\x20\x7B\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x65\x6E\x76\x2E\x48\x6F\x73\x74\x73\x46\x69\x6C\x65\x73\x5B\x6E\x61\x6D\x65\x5D\x20\x3D\x20\x7B\x0A\x09\x09\x09\x09\x4E\x61\x6D\x65\x3A\x20\x6E\x61\x6D\x65\x2C\x0A\x09\x09\x09\x09\x52\x65\x63\x6F\x72\x64\x73\x3A\x20\x72\x65\x73\x2E\x64\x61\x74\x61\x2C\x0A\x09\x09\x09\x7D\x0A\x09\x09\x7D\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x72\x65\x73\x0A\x09\x7D\x0A\x0A\x09\x61\x73\x79\x6E\x63\x20\x48\x6F\x73\x74\x73\x46\x69\x6C\x65\x52\x65\x63\x6F\x72\x64\x41\x64\x64\x28\x68\x6F\x73\x74\x73\x46\x69\x6C\x65\x2C\x20\x64\x6F\x6D\x61\x69\x6E\x2C\x20\x76\x61\x6C\x75\x65\x29\x20\x7B\x0A\x09\x09\x6C\x65\x74\x20\x70\x61\x72\x61\x6D\x73\x20\x3D\x20\x6E\x65\x77\x20\x55\x52\x4C\x53\x65\x61\x72\x63\x68\x50\x61\x72\x61\x6D\x73\x28\x29\x0A\x09\x09\x70\x61\x72\x61\x6D\x73\x2E\x73\x65\x74\x28\x22\x64\x6F\x6D\x61\x69\x6E\x22\x2C\x20\x64\x6F\x6D\x61\x69\x6E\x29\x0A\x09\x09\x70\x61\x72\x61\x6D\x73\x2E\x73\x65\x74\x28\x22\x76\x61\x6C\x75\x65\x22\x2C\x20\x76\x61\x6C\x75\x65\x29\x0A\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x61\x70\x69\x20\x3D\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x73\x65\x72\x76\x65\x72\x20\x2B\x0A\x09\x09\x09\x52\x65\x73\x63\x61\x63\x68\x65\x64\x2E\x61\x70\x69\x48\x6F\x73\x74\x73\x64\x20\x2B\x0A\x09\x09\x09\x68\x6F\x73\x74\x73\x46\x69\x6C\x65\x20\x2B\x0A\x09\x09\x09\x22\x2F\x72\x72\x22\x20\x2B\x0A\x09\x09\x09\x22\x3F\x22\x20\x2B\x0A\x09\x09\x09\x70\x61\x72\x61\x6D\x73\x2E\x74\x6F\x53\x74\x72\x69\x6E\x67\x28\x29\x0A\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x68\x74\x74\x70\x52\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x66\x65\x74\x63\x68\x28\x61\x70\x69\x2C\x20\x7B\x0A\x09\x09\x09\x6D\x65\x74\x68\x6F\x64\x3A\x20\x22\x50\x4F\x53\x54\x22\x2C\x0A\x09\x09\x7D\x29\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x68\x74\x74\x70\x52\x65\x73\x2E\x6A\x73\x6F\x6E\x28\x29\x0A\x09\x09\x69\x66\x20\x28\x68\x74\x74\x70\x52\x65\x73\x2E\x53\x74\x61\x74\x75\x73\x20\x3D\x3D\x3D\x20\x32\x30\x30\x29\x20\x7B\x0A\x09\x09\x09\x6C\x65\x74\x20\x68\x66\x20\x3D\x20\x74\x68\x69\x73\x2E\x65\x6E\x76\x2E\x48\x6F\x73\x74\x73\x46\x69\x6C\x65\x73\x5B\x68\x6F\x73\x74\x73\x46\x69\x6C\x65\x5D\x0A\x09\x09\x09\x68\x66\x2E\x52\x65\x63\x6F\x72\x64\x73\x2E\x70\x75\x73\x68\x28\x72\x65\x73\x2E\x64\x61\x74\x61\x29\x0A\x09\x09\x7D\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x72\x65\x73\x0A\x09\x7D\x0A\x0A\x09\x61\x73\x79\x6E\x63\x20\x48\x6F\x73\x74\x73\x46\x69\x6C\x65\x52\x65\x63\x6F\x72\x64\x44\x65\x6C\x65\x74\x65\x28\x68\x6F\x73\x74\x73\x46\x69\x6C\x65\x2C\x20\x64\x6F\x6D\x61\x69\x6E\x29\x20\x7B\x0A\x09\x09\x6C\x65\x74\x20\x70\x61\x72\x61\x6D\x73\x20\x3D\x20\x6E\x65\x77\x20\x55\x52\x4C\x53\x65\x61\x72\x63\x68\x50\x61\x72\x61\x6D\x73\x28\x29\x0A\x09\x09\x70\x61\x72\x61\x6D\x73\x2E\x73\x65\x74\x28\x22\x64\x6F\x6D\x61\x69\x6E\x22\x2C\x20\x64\x6F\x6D\x61\x69\x6E\x29\x0A\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x61\x70\x69\x20\x3D\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x73\x65\x72\x76\x65\x72\x20\x2B\x0A\x09\x09\x09\x52\x65\x73\x63\x61\x63\x68\x65\x64\x2E\x61\x70\x69\x48\x6F\x73\x74\x73\x64\x20\x2B\x0A\x09\x09\x09\x68\x6F\x73\x74\x73\x46\x69\x6C\x65\x20\x2B\x0A\x09\x09\x09\x22\x2F\x72\x72\x22\x20\x2B\x0A\x09\x09\x09\x22\x3F\x22\x20\x2B\x0A\x09\x09\x09\x70\x61\x72\x61\x6D\x73\x2E\x74\x6F\x53\x74\x72\x69\x6E\x67\x28\x29\x0A\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x68\x74\x74\x70\x52\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x66\x65\x74\x63\x68\x28\x61\x70\x69\x2C\x20\x7B\x0A\x09\x09\x09\x6D\x65\x74\x68\x6F\x64\x3A\x20\x22\x44\x45\x4C\x45\x54\x45\x22\x2C\x0A\x09\x09\x7D\x29\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x68\x74\x74\x70\x52\x65\x73\x2E\x6A\x73\x6F\x6E\x28\x29\x0A\x09\x09\x69\x66\x20\x28\x68\x74\x74\x70\x52\x65\x73\x2E\x53\x74\x61\x74\x75\x73\x20\x3D\x3D\x3D\x20\x32\x30\x30\x29\x20\x7B\x0A\x09\x09\x09\x6C\x65\x74\x20\x68\x66\x20\x3D\x20\x74\x68\x69\x73\x2E\x65\x6E\x76\x2E\x48\x6F\x73\x74\x73\x46\x69\x6C\x65\x73\x5B\x68\x6F\x73\x74\x73\x46\x69\x6C\x65\x5D\x0A\x09\x09\x09\x66\x6F\x72\x20\x28\x6C\x65\x74\x20\x78\x20\x3D\x20\x30\x3B\x20\x78\x20\x3C\x20\x68\x66\x2E\x52\x65\x63\x6F\x72\x64\x73\x2E\x6C\x65\x6E\x67\x74\x68\x3B\x20\x78\x2B\x2B\x29\x20\x7B\x0A\x09\x09\x09\x09\x69\x66\x20\x28\x68\x66\x2E\x52\x65\x63\x6F\x72\x64\x73\x5B\x78\x5D\x2E\x4E\x61\x6D\x65\x20\x3D\x3D\x3D\x20\x64\x6F\x6D\x61\x69\x6E\x29\x20\x7B\x0A\x09\x09\x09\x09\x09\x68\x66\x2E\x52\x65\x63\x6F\x72\x64\x73\x2E\x73\x70\x6C\x69\x63\x65\x28\x78\x2C\x20\x31\x29\x0A\x09\x09\x09\x09\x7D\x0A\x09\x09\x09\x7D\x0A\x09\x09\x7D\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x72\x65\x73\x0A\x09\x7D\x0A\x0A\x09\x61\x73\x79\x6E\x63\x20\x75\x70\x64\x61\x74\x65\x45\x6E\x76\x69\x72\x6F\x6E\x6D\x65\x6E\x74\x28\x29\x20\x7B\x0A\x09\x09\x6C\x65\x74\x20\x67\x6F\x74\x20\x3D\x20\x7B\x7D\x0A\x0A\x09\x09\x4F\x62\x6A\x65\x63\x74\x2E\x61\x73\x73\x69\x67\x6E\x28\x67\x6F\x74\x2C\x20\x74\x68\x69\x73\x2E\x65\x6E\x76\x29\x0A\x0A\x09\x09\x67\x6F\x74\x2E\x50\x72\x75\x6E\x65\x44\x65\x6C\x61\x79\x20\x3D\x20\x67\x6F\x74\x2E\x50\x72\x75\x6E\x65\x44\x65\x6C\x61\x79\x20\x2A\x20\x74\x68\x69\x73\x2E\x6E\x61\x6E\x6F\x53\x65\x63\x6F\x6E\x64\x73\x0A\x09\x09\x67\x6F\x74\x2E\x50\x72\x75\x6E\x65\x54\x68\x72\x65\x73\x68\x6F\x6C\x64\x20\x3D\x20\x67\x6F\x74\x2E\x50\x72\x75\x6E\x65\x54\x68\x72\x65\x73\x68\x6F\x6C\x64\x20\x2A\x20\x74\x68\x69\x73\x2E\x6E\x61\x6E\x6F\x53\x65\x63\x6F\x6E\x64\x73\x0A\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x68\x74\x74\x70\x52\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x66\x65\x74\x63\x68\x28\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x73\x65\x72\x76\x65\x72\x20\x2B\x20\x22\x2F\x61\x70\x69\x2F\x65\x6E\x76\x69\x72\x6F\x6E\x6D\x65\x6E\x74\x22\x2C\x0A\x09\x09\x09\x7B\x0A\x09\x09\x09\x09\x6D\x65\x74\x68\x6F\x64\x3A\x20\x22\x50\x4F\x53\x54\x22\x2C\x0A\x09\x09\x09\x09\x68\x65\x61\x64\x65\x72\x73\x3A\x20\x7B\x0A\x09\x09\x09\x09\x09\x22\x43\x6F\x6E\x74\x65\x6E\x74\x2D\x54\x79\x70\x65\x22\x3A\x20\x22\x61\x70\x70\x6C\x69\x63\x61\x74\x69\x6F\x6E\x2F\x6A\x73\x6F\x6E\x22\x2C\x0A\x09\x09\x09\x09\x7D\x2C\x0A\x09\x09\x09\x09\x62\x6F\x64\x79\x3A\x20\x4A\x53\x4F\x4E\x2E\x73\x74\x72\x69\x6E\x67\x69\x66\x79\x28\x67\x6F\x74\x29\x2C\x0A\x09\x09\x09\x7D\x2C\x0A\x09\x09\x29\x0A\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x61\x77\x61\x69\x74\x20\x68\x74\x74\x70\x52\x65\x73\x2E\x6A\x73\x6F\x6E\x28\x29\x0A\x09\x7D\x0A\x0A\x09\x61\x73\x79\x6E\x63\x20\x75\x70\x64\x61\x74\x65\x48\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x28\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x29\x20\x7B\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x68\x74\x74\x70\x52\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x66\x65\x74\x63\x68\x28\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x73\x65\x72\x76\x65\x72\x20\x2B\x20\x22\x2F\x61\x70\x69\x2F\x68\x6F\x73\x74\x73\x5F\x62\x6C\x6F\x63\x6B\x22\x2C\x0A\x09\x09\x09\x7B\x0A\x09\x09\x09\x09\x6D\x65\x74\x68\x6F\x64\x3A\x20\x22\x50\x4F\x53\x54\x22\x2C\x0A\x09\x09\x09\x09\x68\x65\x61\x64\x65\x72\x73\x3A\x20\x7B\x0A\x09\x09\x09\x09\x09\x22\x43\x6F\x6E\x74\x65\x6E\x74\x2D\x54\x79\x70\x65\x22\x3A\x20\x22\x61\x70\x70\x6C\x69\x63\x61\x74\x69\x6F\x6E\x2F\x6A\x73\x6F\x6E\x22\x2C\x0A\x09\x09\x09\x09\x7D\x2C\x0A\x09\x09\x09\x09\x62\x6F\x64\x79\x3A\x20\x4A\x53\x4F\x4E\x2E\x73\x74\x72\x69\x6E\x67\x69\x66\x79\x28\x68\x6F\x73\x74\x73\x42\x6C\x6F\x63\x6B\x73\x29\x2C\x0A\x09\x09\x09\x7D\x2C\x0A\x09\x09\x29\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x61\x77\x61\x69\x74\x20\x68\x74\x74\x70\x52\x65\x73\x2E\x6A\x73\x6F\x6E\x28\x29\x0A\x09\x7D\x0A\x0A\x09\x61\x73\x79\x6E\x63\x20\x5A\x6F\x6E\x65\x46\x69\x6C\x65\x43\x72\x65\x61\x74\x65\x28\x6E\x61\x6D\x65\x29\x20\x7B\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x68\x74\x74\x70\x52\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x66\x65\x74\x63\x68\x28\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x73\x65\x72\x76\x65\x72\x20\x2B\x20\x52\x65\x73\x63\x61\x63\x68\x65\x64\x2E\x61\x70\x69\x5A\x6F\x6E\x65\x64\x20\x2B\x20\x6E\x61\x6D\x65\x2C\x0A\x09\x09\x09\x7B\x0A\x09\x09\x09\x09\x6D\x65\x74\x68\x6F\x64\x3A\x20\x22\x50\x55\x54\x22\x2C\x0A\x09\x09\x09\x7D\x2C\x0A\x09\x09\x29\x0A\x09\x09\x6C\x65\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x68\x74\x74\x70\x52\x65\x73\x2E\x6A\x73\x6F\x6E\x28\x29\x0A\x09\x09\x69\x66\x20\x28\x72\x65\x73\x2E\x63\x6F\x64\x65\x20\x3D\x3D\x20\x32\x30\x30\x29\x20\x7B\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x65\x6E\x76\x2E\x5A\x6F\x6E\x65\x46\x69\x6C\x65\x73\x5B\x6E\x61\x6D\x65\x5D\x20\x3D\x20\x72\x65\x73\x2E\x64\x61\x74\x61\x0A\x09\x09\x7D\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x72\x65\x73\x0A\x09\x7D\x0A\x0A\x09\x61\x73\x79\x6E\x63\x20\x5A\x6F\x6E\x65\x46\x69\x6C\x65\x44\x65\x6C\x65\x74\x65\x28\x6E\x61\x6D\x65\x29\x20\x7B\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x68\x74\x74\x70\x52\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x66\x65\x74\x63\x68\x28\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x73\x65\x72\x76\x65\x72\x20\x2B\x20\x52\x65\x73\x63\x61\x63\x68\x65\x64\x2E\x61\x70\x69\x5A\x6F\x6E\x65\x64\x20\x2B\x20\x6E\x61\x6D\x65\x2C\x0A\x09\x09\x09\x7B\x0A\x09\x09\x09\x09\x6D\x65\x74\x68\x6F\x64\x3A\x20\x22\x44\x45\x4C\x45\x54\x45\x22\x2C\x0A\x09\x09\x09\x7D\x2C\x0A\x09\x09\x29\x0A\x09\x09\x6C\x65\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x68\x74\x74\x70\x52\x65\x73\x2E\x6A\x73\x6F\x6E\x28\x29\x0A\x09\x09\x69\x66\x20\x28\x72\x65\x73\x2E\x63\x6F\x64\x65\x20\x3D\x3D\x20\x32\x30\x30\x29\x20\x7B\x0A\x09\x09\x09\x64\x65\x6C\x65\x74\x65\x20\x74\x68\x69\x73\x2E\x65\x6E\x76\x2E\x5A\x6F\x6E\x65\x46\x69\x6C\x65\x73\x5B\x6E\x61\x6D\x65\x5D\x0A\x09\x09\x7D\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x72\x65\x73\x0A\x09\x7D\x0A\x0A\x09\x61\x73\x79\x6E\x63\x20\x5A\x6F\x6E\x65\x46\x69\x6C\x65\x52\x65\x63\x6F\x72\x64\x43\x72\x65\x61\x74\x65\x28\x6E\x61\x6D\x65\x2C\x20\x72\x72\x29\x20\x7B\x0A\x09\x09\x6C\x65\x74\x20\x61\x70\x69\x20\x3D\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x73\x65\x72\x76\x65\x72\x20\x2B\x0A\x09\x09\x09\x52\x65\x73\x63\x61\x63\x68\x65\x64\x2E\x61\x70\x69\x5A\x6F\x6E\x65\x64\x20\x2B\x0A\x09\x09\x09\x6E\x61\x6D\x65\x20\x2B\x0A\x09\x09\x09\x22\x2F\x72\x72\x2F\x22\x20\x2B\x0A\x09\x09\x09\x72\x72\x2E\x54\x79\x70\x65\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x68\x74\x74\x70\x52\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x66\x65\x74\x63\x68\x28\x61\x70\x69\x2C\x20\x7B\x0A\x09\x09\x09\x6D\x65\x74\x68\x6F\x64\x3A\x20\x22\x50\x4F\x53\x54\x22\x2C\x0A\x09\x09\x09\x68\x65\x61\x64\x65\x72\x73\x3A\x20\x7B\x0A\x09\x09\x09\x09\x22\x43\x6F\x6E\x74\x65\x6E\x74\x2D\x54\x79\x70\x65\x22\x3A\x20\x22\x61\x70\x70\x6C\x69\x63\x61\x74\x69\x6F\x6E\x2F\x6A\x73\x6F\x6E\x22\x2C\x0A\x09\x09\x09\x7D\x2C\x0A\x09\x09\x09\x62\x6F\x64\x79\x3A\x20\x4A\x53\x4F\x4E\x2E\x73\x74\x72\x69\x6E\x67\x69\x66\x79\x28\x72\x72\x29\x2C\x0A\x09\x09\x7D\x29\x0A\x09\x09\x6C\x65\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x68\x74\x74\x70\x52\x65\x73\x2E\x6A\x73\x6F\x6E\x28\x29\x0A\x09\x09\x69\x66\x20\x28\x68\x74\x74\x70\x52\x65\x73\x2E\x73\x74\x61\x74\x75\x73\x20\x3D\x3D\x3D\x20\x32\x30\x30\x29\x20\x7B\x0A\x09\x09\x09\x6C\x65\x74\x20\x7A\x66\x20\x3D\x20\x74\x68\x69\x73\x2E\x65\x6E\x76\x2E\x5A\x6F\x6E\x65\x46\x69\x6C\x65\x73\x5B\x6E\x61\x6D\x65\x5D\x0A\x09\x09\x09\x69\x66\x20\x28\x72\x72\x2E\x54\x79\x70\x65\x20\x3D\x3D\x20\x36\x29\x20\x7B\x0A\x09\x09\x09\x09\x2F\x2F\x20\x53\x4F\x41\x2E\x0A\x09\x09\x09\x09\x7A\x66\x2E\x53\x4F\x41\x20\x3D\x20\x72\x65\x73\x2E\x64\x61\x74\x61\x0A\x09\x09\x09\x7D\x20\x65\x6C\x73\x65\x20\x7B\x0A\x09\x09\x09\x09\x7A\x66\x2E\x52\x65\x63\x6F\x72\x64\x73\x20\x3D\x20\x72\x65\x73\x2E\x64\x61\x74\x61\x0A\x09\x09\x09\x7D\x0A\x09\x09\x7D\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x72\x65\x73\x0A\x09\x7D\x0A\x0A\x09\x61\x73\x79\x6E\x63\x20\x5A\x6F\x6E\x65\x46\x69\x6C\x65\x52\x65\x63\x6F\x72\x64\x44\x65\x6C\x65\x74\x65\x28\x6E\x61\x6D\x65\x2C\x20\x72\x72\x29\x20\x7B\x0A\x09\x09\x6C\x65\x74\x20\x61\x70\x69\x20\x3D\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x73\x65\x72\x76\x65\x72\x20\x2B\x0A\x09\x09\x09\x52\x65\x73\x63\x61\x63\x68\x65\x64\x2E\x61\x70\x69\x5A\x6F\x6E\x65\x64\x20\x2B\x0A\x09\x09\x09\x6E\x61\x6D\x65\x20\x2B\x0A\x09\x09\x09\x22\x2F\x72\x72\x2F\x22\x20\x2B\x0A\x09\x09\x09\x72\x72\x2E\x54\x79\x70\x65\x0A\x09\x09\x63\x6F\x6E\x73\x74\x20\x68\x74\x74\x70\x52\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x66\x65\x74\x63\x68\x28\x61\x70\x69\x2C\x20\x7B\x0A\x09\x09\x09\x6D\x65\x74\x68\x6F\x64\x3A\x20\x22\x44\x45\x4C\x45\x54\x45\x22\x2C\x0A\x09\x09\x09\x68\x65\x61\x64\x65\x72\x73\x3A\x20\x7B\x0A\x09\x09\x09\x09\x22\x43\x6F\x6E\x74\x65\x6E\x74\x2D\x54\x79\x70\x65\x22\x3A\x20\x22\x61\x70\x70\x6C\x69\x63\x61\x74\x69\x6F\x6E\x2F\x6A\x73\x6F\x6E\x22\x2C\x0A\x09\x09\x09\x7D\x2C\x0A\x09\x09\x09\x62\x6F\x64\x79\x3A\x20\x4A\x53\x4F\x4E\x2E\x73\x74\x72\x69\x6E\x67\x69\x66\x79\x28\x72\x72\x29\x2C\x0A\x09\x09\x7D\x29\x0A\x09\x09\x6C\x65\x74\x20\x72\x65\x73\x20\x3D\x20\x61\x77\x61\x69\x74\x20\x68\x74\x74\x70\x52\x65\x73\x2E\x6A\x73\x6F\x6E\x28\x29\x0A\x09\x09\x69\x66\x20\x28\x68\x74\x74\x70\x52\x65\x73\x2E\x73\x74\x61\x74\x75\x73\x20\x3D\x3D\x3D\x20\x32\x30\x30\x29\x20\x7B\x0A\x09\x09\x09\x74\x68\x69\x73\x2E\x65\x6E\x76\x2E\x5A\x6F\x6E\x65\x46\x69\x6C\x65\x73\x5B\x6E\x61\x6D\x65\x5D\x2E\x52\x65\x63\x6F\x72\x64\x73\x20\x3D\x20\x72\x65\x73\x2E\x64\x61\x74\x61\x0A\x09\x09\x7D\x0A\x09\x09\x72\x65\x74\x75\x72\x6E\x20\x72\x65\x73\x0A\x09\x7D\x0A\x7D\x0A"),
}
node.SetMode(420)
- node.SetModTimeUnix(1647276264, 46986583)
+ node.SetModTimeUnix(1650380257, 104529981)
node.SetName("rescached.js")
node.SetSize(5919)
return node
diff --git a/rescached.go b/rescached.go
index 232d340..5b8951b 100644
--- a/rescached.go
+++ b/rescached.go
@@ -65,7 +65,12 @@ func New(env *Environment) (srv *Server, err error) {
// it.
//
func (srv *Server) Start() (err error) {
- logp := "Start"
+ var (
+ logp = "Start"
+
+ hb *hostsBlock
+ hfile *dns.HostsFile
+ )
srv.dns, err = dns.NewServer(&srv.env.ServerOptions)
if err != nil {
@@ -94,12 +99,29 @@ func (srv *Server) Start() (err error) {
if err != nil {
return err
}
- err = srv.dns.PopulateCachesByRR(systemHostsFile.Records,
- systemHostsFile.Path)
+ err = srv.dns.PopulateCachesByRR(systemHostsFile.Records, systemHostsFile.Path)
if err != nil {
return err
}
+ for _, hb = range srv.env.HostsBlocks {
+ if !hb.IsEnabled {
+ continue
+ }
+
+ hfile, err = dns.ParseHostsFile(hb.file)
+ if err != nil {
+ return fmt.Errorf("%s: %w", logp, err)
+ }
+
+ err = srv.dns.PopulateCachesByRR(hfile.Records, hfile.Path)
+ if err != nil {
+ return fmt.Errorf("%s: %w", logp, err)
+ }
+
+ srv.env.hostsBlocksFile[hfile.Name] = hfile
+ }
+
srv.env.HostsFiles, err = dns.LoadHostsDir(dirHosts)
if err != nil {
return err
diff --git a/testdata/rescached.cfg.test.out b/testdata/rescached.cfg.test.out
new file mode 100644
index 0000000..adfe7c5
--- /dev/null
+++ b/testdata/rescached.cfg.test.out
@@ -0,0 +1,37 @@
+[rescached]
+file.resolvconf =
+wui.listen = 127.0.0.1:5381
+debug = 2
+
+[block.d "pgl.yoyo.org"]
+name = pgl.yoyo.org
+url = http://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&startdate[day]=&startdate[month]=&startdate[year]=&mimetype=plaintext
+
+[block.d "someonewhocares.org"]
+name = someonewhocares.org
+url = http://someonewhocares.org/hosts/hosts
+
+[block.d "test"]
+name = test
+url = http://someurl
+
+[block.d "winhelp2002.mvps.org"]
+name = winhelp2002.mvps.org
+url = http://winhelp2002.mvps.org/hosts.txt
+
+[block.d "www.malwaredomainlist.com"]
+name = www.malwaredomainlist.com
+url = http://www.malwaredomainlist.com/hostslist/hosts.txt
+
+[dns "server"]
+listen = 127.0.0.1:5350
+tls.certificate =
+tls.private_key =
+parent = udp://10.8.0.1
+http.idle_timeout = 0s
+cache.prune_delay = 0s
+cache.prune_threshold = 0s
+http.port = 0
+tls.port = 0
+tls.allow_insecure = true
+doh.behind_proxy = false