From 04806b614e64619828abc6e37b83659304323e7e Mon Sep 17 00:00:00 2001 From: Unknwon Date: Sat, 31 Oct 2015 23:22:28 -0400 Subject: more on #1705 --- models/repo.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'models/repo.go') diff --git a/models/repo.go b/models/repo.go index 197415c6..5f19159f 100644 --- a/models/repo.go +++ b/models/repo.go @@ -23,6 +23,7 @@ import ( "github.com/Unknwon/cae/zip" "github.com/Unknwon/com" "github.com/go-xorm/xorm" + "gopkg.in/ini.v1" "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/bindata" @@ -537,6 +538,17 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) { return repo, fmt.Errorf("create update hook: %v", err) } + // Clean up mirror info which prevents "push --all". + configPath := filepath.Join(repoPath, "/config") + cfg, err := ini.Load(configPath) + if err != nil { + return repo, fmt.Errorf("open config file: %v", err) + } + cfg.DeleteSection("remote \"origin\"") + if err = cfg.SaveToIndent(configPath, "\t"); err != nil { + return repo, fmt.Errorf("save config file: %v", err) + } + // Check if repository is empty. _, stderr, err = com.ExecCmdDir(repoPath, "git", "log", "-1") if err != nil { -- cgit v1.2.3 From 8411b50f5d4e3b30d7d601612ee2aa5e4921c968 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Tue, 3 Nov 2015 13:00:04 -0500 Subject: work on #1882 --- .travis.yml | 3 +-- models/repo.go | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'models/repo.go') diff --git a/.travis.yml b/.travis.yml index 371ce51b..837d9a1c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ go: - 1.3 - 1.4 - 1.5 - - tip before_install: - sudo apt-get update -qq @@ -12,7 +11,7 @@ before_install: - go get github.com/msteinert/pam install: - - go get -t ./... + - go get -t -v ./... script: go build -v -tags "pam" diff --git a/models/repo.go b/models/repo.go index 5f19159f..32841efc 100644 --- a/models/repo.go +++ b/models/repo.go @@ -910,9 +910,9 @@ func TransferOwnership(u *User, newOwnerName string, repo *Repository) error { } // Remove redundant collaborators. - collaborators, err := repo.GetCollaborators() + collaborators, err := repo.getCollaborators(sess) if err != nil { - return fmt.Errorf("GetCollaborators: %v", err) + return fmt.Errorf("getCollaborators: %v", err) } // Dummy object. @@ -948,9 +948,9 @@ func TransferOwnership(u *User, newOwnerName string, repo *Repository) error { } if newOwner.IsOrganization() { - t, err := newOwner.GetOwnerTeam() + t, err := newOwner.getOwnerTeam(sess) if err != nil { - return fmt.Errorf("GetOwnerTeam: %v", err) + return fmt.Errorf("getOwnerTeam: %v", err) } else if err = t.addRepository(sess, repo); err != nil { return fmt.Errorf("add to owner team: %v", err) } -- cgit v1.2.3 From 6f929dcd9e950a6aba14fe24cd79765016ece372 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Fri, 6 Nov 2015 11:14:58 -0500 Subject: #1895 Case sensitive clone URL --- models/repo.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'models/repo.go') diff --git a/models/repo.go b/models/repo.go index 32841efc..22ad486f 100644 --- a/models/repo.go +++ b/models/repo.go @@ -382,9 +382,9 @@ func (repo *Repository) CloneLink() (cl CloneLink, err error) { if setting.SSHPort != 22 { cl.SSH = fmt.Sprintf("ssh://%s@%s:%d/%s/%s.git", setting.RunUser, setting.SSHDomain, setting.SSHPort, repo.Owner.LowerName, repo.LowerName) } else { - cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.SSHDomain, repo.Owner.LowerName, repo.LowerName) + cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.SSHDomain, repo.Owner.Name, repo.Name) } - cl.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, repo.Owner.LowerName, repo.LowerName) + cl.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, repo.Owner.Name, repo.Name) return cl, nil } -- cgit v1.2.3 From d85a1d478ebba9b2f1f44431389507ae8d799dec Mon Sep 17 00:00:00 2001 From: Unknwon Date: Sat, 7 Nov 2015 20:20:51 -0500 Subject: admin op with notice --- README.md | 10 +++++----- models/repo.go | 10 +++++++--- 2 files changed, 12 insertions(+), 8 deletions(-) (limited to 'models/repo.go') diff --git a/README.md b/README.md index 3e300519..95bd6f44 100644 --- a/README.md +++ b/README.md @@ -29,12 +29,12 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra - Due to testing purpose, data of [try.gogs.io](https://try.gogs.io) has been reset in **Jan 28, 2015** and will reset multiple times after. Please do **NOT** put your important data on the site. - The demo site [try.gogs.io](https://try.gogs.io) is running under `develop` branch. -- :exclamation::exclamation::exclamation:You **MUST** read [CONTRIBUTING.md](CONTRIBUTING.md) before you start filing an issue or making a Pull Request, and **MUST** discuss with us on [Gitter](https://gitter.im/gogits/gogs) for UI changes and feature Pull Requests, otherwise it's high possibilities that we are not going to merge it.:exclamation::exclamation::exclamation: +- :bangbang:You **MUST** read [CONTRIBUTING.md](CONTRIBUTING.md) before you start filing an issue or making a Pull Request, and **MUST** discuss with us on [Gitter](https://gitter.im/gogits/gogs) for UI changes and feature Pull Requests, otherwise it's high possibilities that we are not going to merge it.:bangbang: - If you think there are vulnerabilities in the project, please talk privately to **u@gogs.io**. Thanks! +- If you're interested in using APIs, we have experimental support with [documentation](https://github.com/gogits/go-gogs-client/wiki). +- If your team/company is using Gogs and would like to put your logo on [our website](http://gogs.io), contact us by any means. -#### Other language version - -- [简体中文](README_ZH.md) +[简体中文](README_ZH.md) ## Purpose @@ -69,7 +69,7 @@ The goal of this project is to make the easiest, fastest, and most painless way ## System Requirements - A cheap Raspberry Pi is powerful enough for basic functionality. -- At least 2 CPU cores and 1GB RAM would be the baseline for teamwork. +- 2 CPU cores and 1GB RAM would be the baseline for teamwork. ## Browser Support diff --git a/models/repo.go b/models/repo.go index 22ad486f..c173b15f 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1330,10 +1330,14 @@ func RewriteRepositoryUpdateHook() error { return x.Where("id > 0").Iterate(new(Repository), func(idx int, bean interface{}) error { repo := bean.(*Repository) - if err := repo.GetOwner(); err != nil { - return err + repoPath, err := repo.RepoPath() + if err != nil { + if err2 := CreateRepositoryNotice(fmt.Sprintf("RewriteRepositoryUpdateHook[%d]: %v", repo.ID, err)); err2 != nil { + log.Error(4, "CreateRepositoryNotice: %v", err2) + } + return nil } - return createUpdateHook(RepoPath(repo.Owner.Name, repo.Name)) + return createUpdateHook(repoPath) }) } -- cgit v1.2.3 From 18c841050b4c843ef723e788d4c0a4c29c67ffe3 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Sun, 8 Nov 2015 16:59:56 -0500 Subject: fix 1540 and experimental SSH server support --- cmd/serve.go | 4 +- conf/app.ini | 2 + models/publickey.go | 40 +- models/repo.go | 4 +- modules/base/base.go | 19 + modules/bindata/bindata.go | 34 +- modules/crypto/ssh/agent/client.go | 615 ------------------ modules/crypto/ssh/agent/client_test.go | 287 --------- modules/crypto/ssh/agent/forward.go | 103 ---- modules/crypto/ssh/agent/keyring.go | 184 ------ modules/crypto/ssh/agent/server.go | 209 ------- modules/crypto/ssh/agent/server_test.go | 77 --- modules/crypto/ssh/agent/testdata_test.go | 64 -- modules/crypto/ssh/benchmark_test.go | 122 ---- modules/crypto/ssh/buffer.go | 98 --- modules/crypto/ssh/buffer_test.go | 87 --- modules/crypto/ssh/certs.go | 501 --------------- modules/crypto/ssh/certs_test.go | 216 ------- modules/crypto/ssh/channel.go | 631 ------------------- modules/crypto/ssh/cipher.go | 549 ----------------- modules/crypto/ssh/cipher_test.go | 127 ---- modules/crypto/ssh/client.go | 213 ------- modules/crypto/ssh/client_auth.go | 441 ------------- modules/crypto/ssh/client_auth_test.go | 393 ------------ modules/crypto/ssh/client_test.go | 39 -- modules/crypto/ssh/common.go | 354 ----------- modules/crypto/ssh/connection.go | 144 ----- modules/crypto/ssh/doc.go | 18 - modules/crypto/ssh/example_test.go | 211 ------- modules/crypto/ssh/handshake.go | 412 ------------- modules/crypto/ssh/handshake_test.go | 415 ------------- modules/crypto/ssh/kex.go | 526 ---------------- modules/crypto/ssh/kex_test.go | 50 -- modules/crypto/ssh/keys.go | 628 ------------------- modules/crypto/ssh/keys_test.go | 306 --------- modules/crypto/ssh/mac.go | 57 -- modules/crypto/ssh/mempipe_test.go | 110 ---- modules/crypto/ssh/messages.go | 725 ---------------------- modules/crypto/ssh/messages_test.go | 254 -------- modules/crypto/ssh/mux.go | 356 ----------- modules/crypto/ssh/mux_test.go | 525 ---------------- modules/crypto/ssh/server.go | 493 --------------- modules/crypto/ssh/session.go | 605 ------------------ modules/crypto/ssh/session_test.go | 774 ----------------------- modules/crypto/ssh/tcpip.go | 407 ------------ modules/crypto/ssh/tcpip_test.go | 20 - modules/crypto/ssh/terminal/terminal.go | 892 --------------------------- modules/crypto/ssh/terminal/terminal_test.go | 269 -------- modules/crypto/ssh/terminal/util.go | 128 ---- modules/crypto/ssh/terminal/util_bsd.go | 12 - modules/crypto/ssh/terminal/util_linux.go | 11 - modules/crypto/ssh/terminal/util_windows.go | 174 ------ modules/crypto/ssh/test/agent_unix_test.go | 59 -- modules/crypto/ssh/test/cert_test.go | 47 -- modules/crypto/ssh/test/doc.go | 7 - modules/crypto/ssh/test/forward_unix_test.go | 160 ----- modules/crypto/ssh/test/session_test.go | 340 ---------- modules/crypto/ssh/test/tcpip_test.go | 46 -- modules/crypto/ssh/test/test_unix_test.go | 261 -------- modules/crypto/ssh/test/testdata_test.go | 64 -- modules/crypto/ssh/testdata/doc.go | 8 - modules/crypto/ssh/testdata/keys.go | 43 -- modules/crypto/ssh/testdata_test.go | 63 -- modules/crypto/ssh/transport.go | 332 ---------- modules/crypto/ssh/transport_test.go | 109 ---- modules/setting/setting.go | 49 +- modules/ssh/ssh.go | 114 ++-- modules/ssh/ssh_1.3.go | 7 + routers/install.go | 6 + 69 files changed, 171 insertions(+), 15479 deletions(-) delete mode 100755 modules/crypto/ssh/agent/client.go delete mode 100755 modules/crypto/ssh/agent/client_test.go delete mode 100755 modules/crypto/ssh/agent/forward.go delete mode 100755 modules/crypto/ssh/agent/keyring.go delete mode 100755 modules/crypto/ssh/agent/server.go delete mode 100755 modules/crypto/ssh/agent/server_test.go delete mode 100755 modules/crypto/ssh/agent/testdata_test.go delete mode 100755 modules/crypto/ssh/benchmark_test.go delete mode 100755 modules/crypto/ssh/buffer.go delete mode 100755 modules/crypto/ssh/buffer_test.go delete mode 100755 modules/crypto/ssh/certs.go delete mode 100755 modules/crypto/ssh/certs_test.go delete mode 100755 modules/crypto/ssh/channel.go delete mode 100755 modules/crypto/ssh/cipher.go delete mode 100755 modules/crypto/ssh/cipher_test.go delete mode 100755 modules/crypto/ssh/client.go delete mode 100755 modules/crypto/ssh/client_auth.go delete mode 100755 modules/crypto/ssh/client_auth_test.go delete mode 100755 modules/crypto/ssh/client_test.go delete mode 100755 modules/crypto/ssh/common.go delete mode 100755 modules/crypto/ssh/connection.go delete mode 100755 modules/crypto/ssh/doc.go delete mode 100755 modules/crypto/ssh/example_test.go delete mode 100755 modules/crypto/ssh/handshake.go delete mode 100755 modules/crypto/ssh/handshake_test.go delete mode 100755 modules/crypto/ssh/kex.go delete mode 100755 modules/crypto/ssh/kex_test.go delete mode 100755 modules/crypto/ssh/keys.go delete mode 100755 modules/crypto/ssh/keys_test.go delete mode 100755 modules/crypto/ssh/mac.go delete mode 100755 modules/crypto/ssh/mempipe_test.go delete mode 100755 modules/crypto/ssh/messages.go delete mode 100755 modules/crypto/ssh/messages_test.go delete mode 100755 modules/crypto/ssh/mux.go delete mode 100755 modules/crypto/ssh/mux_test.go delete mode 100755 modules/crypto/ssh/server.go delete mode 100755 modules/crypto/ssh/session.go delete mode 100755 modules/crypto/ssh/session_test.go delete mode 100755 modules/crypto/ssh/tcpip.go delete mode 100755 modules/crypto/ssh/tcpip_test.go delete mode 100755 modules/crypto/ssh/terminal/terminal.go delete mode 100755 modules/crypto/ssh/terminal/terminal_test.go delete mode 100755 modules/crypto/ssh/terminal/util.go delete mode 100755 modules/crypto/ssh/terminal/util_bsd.go delete mode 100755 modules/crypto/ssh/terminal/util_linux.go delete mode 100755 modules/crypto/ssh/terminal/util_windows.go delete mode 100755 modules/crypto/ssh/test/agent_unix_test.go delete mode 100755 modules/crypto/ssh/test/cert_test.go delete mode 100755 modules/crypto/ssh/test/doc.go delete mode 100755 modules/crypto/ssh/test/forward_unix_test.go delete mode 100755 modules/crypto/ssh/test/session_test.go delete mode 100755 modules/crypto/ssh/test/tcpip_test.go delete mode 100755 modules/crypto/ssh/test/test_unix_test.go delete mode 100755 modules/crypto/ssh/test/testdata_test.go delete mode 100755 modules/crypto/ssh/testdata/doc.go delete mode 100755 modules/crypto/ssh/testdata/keys.go delete mode 100755 modules/crypto/ssh/testdata_test.go delete mode 100755 modules/crypto/ssh/transport.go delete mode 100755 modules/crypto/ssh/transport_test.go create mode 100644 modules/ssh/ssh_1.3.go (limited to 'models/repo.go') diff --git a/cmd/serve.go b/cmd/serve.go index 301a0c74..5c527dff 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -181,12 +181,12 @@ func runServ(c *cli.Context) { if requestedMode == models.ACCESS_MODE_WRITE || repo.IsPrivate { keys := strings.Split(c.Args()[0], "-") if len(keys) != 2 { - fail("Key ID format error", "Invalid key ID: %s", c.Args()[0]) + fail("Key ID format error", "Invalid key argument: %s", c.Args()[0]) } key, err := models.GetPublicKeyByID(com.StrTo(keys[1]).MustInt64()) if err != nil { - fail("Key ID format error", "Invalid key ID[%s]: %v", c.Args()[0], err) + fail("Invalid key ID", "Invalid key ID[%s]: %v", c.Args()[0], err) } keyID = key.ID diff --git a/conf/app.ini b/conf/app.ini index 3a8233f5..5c7ce698 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -48,6 +48,8 @@ HTTP_ADDR = HTTP_PORT = 3000 ; Disable SSH feature when not available DISABLE_SSH = false +; Whether use builtin SSH server or not. +START_SSH_SERVER = false SSH_PORT = 22 ; Disable CDN even in "prod" mode OFFLINE_MODE = false diff --git a/models/publickey.go b/models/publickey.go index 59a9f13e..0f041c09 100644 --- a/models/publickey.go +++ b/models/publickey.go @@ -13,7 +13,6 @@ import ( "io" "io/ioutil" "os" - "os/exec" "path" "path/filepath" "strings" @@ -38,20 +37,7 @@ var ( ) var sshOpLocker = sync.Mutex{} - -var ( - SSHPath string // SSH directory. - appPath string // Execution(binary) path. -) - -// exePath returns the executable path. -func exePath() (string, error) { - file, err := exec.LookPath(os.Args[0]) - if err != nil { - return "", err - } - return filepath.Abs(file) -} +var SSHPath string // SSH directory. // homeDir returns the home directory of current user. func homeDir() string { @@ -63,16 +49,9 @@ func homeDir() string { } func init() { - var err error - - if appPath, err = exePath(); err != nil { - log.Fatal(4, "fail to get app path: %v\n", err) - } - appPath = strings.Replace(appPath, "\\", "/", -1) - // Determine and create .ssh path. SSHPath = filepath.Join(homeDir(), ".ssh") - if err = os.MkdirAll(SSHPath, 0700); err != nil { + if err := os.MkdirAll(SSHPath, 0700); err != nil { log.Fatal(4, "fail to create '%s': %v", SSHPath, err) } } @@ -114,7 +93,7 @@ func (k *PublicKey) OmitEmail() string { // GetAuthorizedString generates and returns formatted public key string for authorized_keys file. func (key *PublicKey) GetAuthorizedString() string { - return fmt.Sprintf(_TPL_PUBLICK_KEY, appPath, key.ID, setting.CustomConf, key.Content) + return fmt.Sprintf(_TPL_PUBLICK_KEY, setting.AppPath, key.ID, setting.CustomConf, key.Content) } func extractTypeFromBase64Key(key string) (string, error) { @@ -373,6 +352,19 @@ func GetPublicKeyByID(keyID int64) (*PublicKey, error) { return key, nil } +// SearchPublicKeyByContent searches content as prefix (leak e-mail part) +// and returns public key found. +func SearchPublicKeyByContent(content string) (*PublicKey, error) { + key := new(PublicKey) + has, err := x.Where("content like ?", content+"%").Get(key) + if err != nil { + return nil, err + } else if !has { + return nil, ErrKeyNotExist{} + } + return key, nil +} + // ListPublicKeys returns a list of public keys belongs to given user. func ListPublicKeys(uid int64) ([]*PublicKey, error) { keys := make([]*PublicKey, 0, 5) diff --git a/models/repo.go b/models/repo.go index c173b15f..fab463b9 100644 --- a/models/repo.go +++ b/models/repo.go @@ -380,7 +380,7 @@ func (repo *Repository) CloneLink() (cl CloneLink, err error) { } if setting.SSHPort != 22 { - cl.SSH = fmt.Sprintf("ssh://%s@%s:%d/%s/%s.git", setting.RunUser, setting.SSHDomain, setting.SSHPort, repo.Owner.LowerName, repo.LowerName) + cl.SSH = fmt.Sprintf("ssh://%s@%s:%d/%s/%s.git", setting.RunUser, setting.SSHDomain, setting.SSHPort, repo.Owner.Name, repo.Name) } else { cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.SSHDomain, repo.Owner.Name, repo.Name) } @@ -599,7 +599,7 @@ func createUpdateHook(repoPath string) error { hookPath := path.Join(repoPath, "hooks/update") os.MkdirAll(path.Dir(hookPath), os.ModePerm) return ioutil.WriteFile(hookPath, - []byte(fmt.Sprintf(_TPL_UPDATE_HOOK, setting.ScriptType, "\""+appPath+"\"", setting.CustomConf)), 0777) + []byte(fmt.Sprintf(_TPL_UPDATE_HOOK, setting.ScriptType, "\""+setting.AppPath+"\"", setting.CustomConf)), 0777) } type CreateRepoOptions struct { diff --git a/modules/base/base.go b/modules/base/base.go index 864ede05..c9875fb5 100644 --- a/modules/base/base.go +++ b/modules/base/base.go @@ -4,6 +4,12 @@ package base +import ( + "os" + "os/exec" + "path/filepath" +) + const DOC_URL = "https://github.com/gogits/go-gogs-client/wiki" type ( @@ -11,3 +17,16 @@ type ( ) var GoGetMetas = make(map[string]bool) + +// ExecPath returns the executable path. +func ExecPath() (string, error) { + file, err := exec.LookPath(os.Args[0]) + if err != nil { + return "", err + } + p, err := filepath.Abs(file) + if err != nil { + return "", err + } + return p, nil +} diff --git a/modules/bindata/bindata.go b/modules/bindata/bindata.go index fef8e3b2..73fbc485 100644 --- a/modules/bindata/bindata.go +++ b/modules/bindata/bindata.go @@ -284,7 +284,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _confAppIni = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xb4\x7a\x5d\x8f\xe3\x46\x76\xf6\x3d\x7f\x45\x59\x7e\xfd\x7a\x1c\x48\xea\x2f\xf7\xcc\xb8\xed\x4e\xac\x96\x28\x35\x77\xf4\x65\x52\x9a\xf1\x78\xd0\xe0\xb0\xc9\x92\x44\x37\x45\x6a\x58\x64\x77\xcb\xc8\xc5\x1a\xb9\x08\x90\xdb\x04\xc9\x4d\x10\x24\x17\x41\x80\xcd\x07\xb2\xc8\xcd\x6e\x82\x5c\x19\xb9\x9f\xf9\x0f\x0b\x6f\xf2\x2f\xf2\x9c\x53\xa4\x44\xf5\xb4\x1b\x59\x20\xd9\x35\x46\x64\xb1\xea\x54\xd5\x73\xbe\x9e\x53\xd5\x1f\x8a\xa1\xf9\xdc\xb4\x05\xff\x33\x18\x75\xac\xee\x4b\x31\x39\xb7\x1c\xd1\xb5\xfa\xa6\xf1\xa1\x18\xf7\xcd\x96\x63\x8a\x41\xeb\x99\x29\xda\xe7\xad\x61\xcf\x74\xc4\x68\x28\xda\x23\xdb\x36\x9d\xf1\x68\xd8\xb1\x86\x3d\xd1\x9e\x3a\x93\xd1\x00\x8d\xc3\xae\xd5\xd3\x23\x8d\xcf\x45\x6b\xb5\x12\xb1\xb7\x94\x22\x5b\x78\x99\x50\x8b\xe4\x46\x89\x24\x16\xf2\x5a\xa6\x6b\xb1\xf2\xe6\xf8\x10\x66\x91\x34\x5a\xe3\xb1\x3b\x6c\x0d\x4c\x71\x2a\x7a\xc9\x5c\x9d\xe0\x5f\xd1\x0b\x33\xe1\xc8\xf4\x3a\xf4\x25\x24\xb5\x17\x5e\x8c\xee\x68\x0b\x67\x62\x9d\xe4\x22\xcd\x63\x11\x25\xbe\x17\x45\x6b\xc3\x9e\x0e\xdd\xa9\x83\xd5\x9f\x8a\x79\x98\xa1\xb7\x19\x66\x0b\x99\x8a\x5a\x20\xaf\x6b\x75\x51\x5b\xa5\x49\x50\x13\x09\x1a\x32\xa9\x32\xb4\x04\x72\xe6\xe5\x11\x64\x29\xdd\x87\x25\x60\xeb\xb4\x00\xbc\x1b\xc6\xab\x54\xae\x12\x15\x66\x49\xba\xbe\x30\xec\xd1\x68\x22\x4e\x0d\xa7\x6d\x5b\xe3\x89\x3b\x79\x39\xa6\x6e\x97\x9e\x5a\x60\xa6\x4e\x21\xa9\x35\x74\x2c\xe1\x2f\xbc\x54\xc9\xcc\xa0\x17\x17\x50\xd9\x8e\x89\x81\x02\xdd\xba\x49\xea\xcb\x62\xdf\xb1\xbc\x11\x5b\xf1\x22\x4b\xc4\xa5\x14\xab\x34\xbc\xf6\x32\x69\x74\x47\x76\xdb\x74\xc7\xb6\xf5\xbc\x35\xa1\x69\x66\x5e\xa4\x68\xff\x63\x2f\xf3\x17\x82\x96\x2f\xde\xe4\x32\x97\x22\x92\xf1\x3c\x5b\xd4\xc5\xd2\xbb\x62\x58\x3c\x25\x22\x2f\x05\x44\x78\x80\x6c\x15\x5e\x02\xd7\xf1\xb4\xdf\x77\x6d\xf3\xab\xa9\xe9\x4c\x5c\xfc\x3b\x35\xdd\xbe\x39\xec\x4d\xce\x21\xf9\x60\x1f\xff\xc3\x4e\xf3\xf0\x02\xf2\x87\xf9\xf2\x12\x88\x25\xb3\xed\xd2\x42\xa9\xb4\xde\xbc\x54\xb2\xee\x64\x20\xc2\x18\xfa\xc3\x46\x6e\x57\x51\x82\x56\x52\xa1\x61\x7e\x3d\xee\x8f\x6c\x2c\xba\xd5\x83\x25\xb8\xc3\xe9\x00\xd2\x0f\xf7\x77\x84\x86\x4a\xe5\x3f\x2d\x8e\xc5\x58\x8e\x33\xbd\x23\xe4\x60\x57\xc8\xd2\xbb\x0d\x97\xf9\x52\xf8\xc9\x72\x19\x66\xea\x8e\x10\xcf\xcf\xc2\xeb\x30\x5b\x8b\x99\x94\x81\xd1\x35\xcd\x8e\x3b\x68\x7d\xed\xb6\x47\x83\x81\x35\x29\x04\x1e\xf3\x7e\x9b\x5e\xb0\x0c\xe3\xdd\x5d\xe7\x4a\xa6\x0f\xaf\x8f\x0c\x6c\x77\x79\xc7\xfb\xef\x03\xf7\xa0\x08\xdb\x1c\x8f\x1e\x14\x11\x27\x19\xcc\xfd\x61\x21\xc3\xd1\xc4\x6a\x9b\x0f\x8a\x49\xd2\xb9\x17\x87\xdf\x79\x59\x08\x67\x7b\x48\xd6\xc8\xee\xbd\x27\xc8\x78\xb5\xf4\xd2\xab\x20\xb9\x61\x84\xcc\xd8\x83\x21\x09\x18\x76\x20\xa2\x10\xe3\x2e\x53\xe9\x5d\xc1\x00\x32\x19\x2b\x88\x37\xcc\x61\xeb\xac\x6f\xba\x30\xf6\x8e\xdb\xb7\x86\xa6\x7b\x66\x9b\xad\x67\x1b\xcb\x35\x5e\x01\x58\x98\xfd\x85\x31\xb6\x47\x93\x51\x7b\xd4\xc7\xa7\x45\x96\xad\x8c\xce\x68\xd0\xb2\x86\x78\x63\x2f\x5e\x24\x2a\x63\x47\x73\xa7\x36\x75\xf9\xe8\x51\xd9\xff\x13\x75\xb2\xb7\xf7\xd1\x23\xdd\x1d\x2f\x1f\x3d\x3a\x9f\x4c\xc6\xee\x78\x64\x4f\x3e\x51\x7b\x06\xbf\xb4\x3a\x1d\x38\xbf\xb1\xf9\x00\x01\x47\x64\xdd\xf0\xcf\x50\xf1\x06\x1c\xe7\x1c\x86\xe1\x65\x39\x80\xb8\x59\xc8\x98\xa0\x16\xde\xb5\x17\x46\xf4\xd9\xe8\x58\x0e\x6f\x83\xba\x95\x4b\xc7\x73\x29\xec\xf0\xb0\x22\xaa\xdd\x19\x92\x27\xc7\x84\x64\x11\x5a\x96\x49\x00\x30\xbb\x5d\x06\xa0\x88\x23\x5a\x48\x29\xd8\x1e\x4d\x27\xb0\x9f\xfe\xa8\x57\x71\xea\x9e\x8c\x65\x0a\xaf\x17\x2a\x93\x2b\x75\x82\x96\xff\x27\xfc\x00\xaa\xc9\x16\x7b\x59\xb2\x37\x47\x28\xdc\xf3\x73\x95\x25\xcb\x3d\x82\x4c\x71\x87\x26\xb7\x0b\x5f\xa6\x99\x68\xf8\xde\x69\x96\x22\x1a\x34\x82\x3c\x65\x75\x9f\x3e\x7d\xf2\x78\x7f\xb1\xbf\xdc\x57\xa2\x41\x98\x9e\x2e\xd7\xf4\xd3\x94\xb7\xde\x72\x15\xc9\x26\x7c\xc7\xf8\x1c\x72\x46\xa9\x98\xa5\xc9\x52\x78\xa2\xb9\x9a\xdd\x8a\x59\x18\xb1\x57\x27\x69\x06\x13\xe1\x2f\x88\xa0\xe2\x45\x18\x07\x14\xb3\x69\xb2\x70\x16\xfa\x7a\xad\xe4\xf9\x8f\x82\x04\x52\x08\xc4\x19\xac\x4d\x66\x14\xc6\xf4\x78\x1e\x58\x84\x33\x71\x25\xd7\x9f\xe8\x7d\x25\x2b\x18\x8c\x8a\xc4\xea\xca\x57\x07\x87\xa2\x01\xf0\x48\x2a\xcf\xde\x48\xf2\xac\x78\x93\x4b\xd1\x88\x13\x0c\x53\xff\xb3\x51\xe8\x59\x0e\xa2\x0f\x8a\x1e\x02\xa9\x8c\xb6\x69\x4f\x5c\x4a\x43\x80\xbb\x0a\xe1\x5e\x39\x8d\xf1\xcc\x7c\x79\x6f\x87\x42\x22\xa6\x9f\xae\x56\xf0\xa7\x08\xba\x8e\xc8\xab\x32\x09\x04\x69\x53\x5e\x1c\x00\x05\xc0\xed\x6b\xdc\x48\x5f\xe8\x5e\x49\x2a\x0c\x01\x5a\xc9\xd4\x00\x16\xe5\x34\x6a\x96\xb7\xd2\xcf\x01\xb0\xe1\x4c\x5a\x70\x62\x97\xed\x7d\xdc\xa2\x70\xac\x93\x65\x44\x10\x93\xd3\xea\x49\x7b\xdf\x58\x63\xa1\xf2\x15\xc1\x5a\x3a\x1a\xb7\x6d\x4d\xa8\x8f\xc5\x84\xf1\x5c\x27\x53\xa8\x02\x2a\x89\x1b\x51\x32\x9f\x43\x8d\x1c\xd3\xea\xc2\xf7\x62\xca\x30\xb5\x45\xb2\x94\x3a\x0b\x16\xe1\xbb\x66\xf4\x5b\x9c\xbd\x29\x06\x10\x0e\xd4\x03\x1e\x1b\x78\x99\x87\xf4\x26\x2f\x2a\x99\x74\xb9\x56\x6f\x22\xce\xa5\xb0\xa6\x79\x2a\x95\x96\x84\xc6\x30\x93\x47\xf8\x10\x66\x1f\x2b\x4a\xcc\x29\xb2\x60\x42\x39\xbb\x73\x56\xa6\x4a\x1e\x6b\x9c\x8f\x1c\x72\xa5\x83\xc3\x27\xcd\x7d\xfc\xff\xe0\xe4\xe8\x68\xff\xb1\x51\x64\x7d\x32\x69\xa3\x48\xe1\x69\x92\x64\xc6\xb8\xe5\x38\x2f\x3a\x8c\x4b\x97\x26\xaa\x4c\x1b\x47\xeb\xba\x90\x65\x86\xd7\x4e\x49\x2b\x4b\xe5\x9b\x3c\x4c\x8b\x2d\x22\xe4\x84\xb3\x75\x63\x96\x47\x51\x0d\x9e\xdc\xdf\x64\x77\xdd\xbf\x14\x5b\xae\x9f\x75\x5a\xcb\xc2\xe0\xb2\x66\x68\x85\x08\x42\x81\x5d\xad\x19\x5c\x02\x94\x22\x67\x50\x3c\xf3\xf3\x14\x79\xe6\xc2\xb0\x86\xd0\x23\x12\x6c\x7f\xd4\x7e\x56\x51\xc9\x07\x1f\x68\x96\xa4\x49\xd4\x64\x24\x9e\x99\xe6\x58\xbc\x1c\x4d\x6d\xc1\x3b\xec\xb4\x26\x2d\xe1\xb4\xba\xe6\x07\x1f\x18\x8e\xd9\xb6\xcd\x89\x0b\x5b\x84\x80\x0f\x3e\xfc\xb2\xdb\x31\x5f\xd8\xf8\xef\xff\xff\xde\x23\xb2\x88\x3c\x4b\x48\x99\xb0\xfa\x54\x2e\x25\x87\xf7\xc0\x83\x6b\x20\x8c\x58\x43\x24\xf6\x81\x39\x38\x43\x54\xe9\xb4\x5e\x3a\x18\xff\xc4\x68\x8f\x46\xcf\x2c\x93\xb9\x50\x05\x58\xd7\xbb\x91\x8a\x54\x5b\x7c\xde\x8c\xab\xf6\x09\x63\x3f\x95\x41\xa8\xb1\xb1\x89\xa9\x28\x72\xe3\xe4\x76\x2d\xbc\x1c\x58\xc7\x59\x69\x9b\x0b\xe9\x05\x58\x08\xf3\xba\x22\x73\xf2\x0b\xd2\x1b\x18\xa4\x43\xec\x65\xf4\xf5\x4b\xb7\x35\x9d\x9c\x9b\x43\x98\x39\x4c\x7d\xb4\xe1\x67\x5f\x37\x5e\x98\x67\xf4\xa9\x41\x0d\x45\x7a\x80\xb9\x5c\x18\xad\xf6\xc4\x7a\x6e\x22\x5f\x77\x40\x52\xe8\x69\x60\x0d\x11\x33\x69\x63\x07\x4f\xf7\x21\x1c\x64\xca\xd5\x66\xf1\x93\x9d\xe0\xb3\xbc\x1a\x30\x00\x0a\x48\x7e\x12\xcf\xc2\x74\x29\x64\x63\x89\x40\xcf\xee\x91\xca\x79\xa8\x32\x1d\x2b\x21\xb3\x67\x39\x14\x96\x4d\xe4\x96\xbe\xcb\xe4\xd5\x1e\x54\x54\xd9\x49\x90\x8c\x39\x53\x44\x51\x72\x53\x0c\xc6\x04\x64\x2d\x6c\x10\x02\xa0\x71\x48\xf0\xfd\x24\x8f\x33\x36\xce\x6d\xcc\x67\xf1\x36\xef\xbf\x22\x94\x97\xb8\x44\xc8\x11\x2a\x9c\x73\x16\xc1\x52\xaf\x43\xb0\x42\x2f\x5e\x67\x0b\x78\x73\xd3\x20\xce\x66\x81\x53\x39\x56\x6f\x08\x4d\x3f\xb7\xcc\x17\x15\x09\x6d\xcf\x47\x80\x41\xf6\xca\xbc\x94\xf9\x5e\xe8\x53\x62\x2b\x43\x44\xbb\xd5\x3e\x37\xdd\x16\x18\x64\xcb\xae\x8c\x1a\x10\x06\xc4\x30\x66\x85\x26\xcb\xfe\xc4\x28\xba\x2f\x5d\xc2\xa0\xda\x9d\xc2\x7c\x20\x33\x8c\x3a\xe1\x8c\x4d\x79\x18\xf4\x7a\x91\x5f\x52\x16\x21\xd7\x00\x09\xd3\x49\x4a\xd3\xbb\xbd\x83\xc7\xc7\xa5\xcc\x87\x6c\x61\x33\xc9\x4f\xf5\x1d\xfd\x14\x74\x9d\x84\xb5\x81\xdd\xfb\x57\x02\xf0\x33\x1d\x44\xc0\x06\x92\xdf\x21\xaf\x63\x71\xd0\x79\x8a\x30\xb1\x4a\x74\x58\xcc\xd6\xab\x6d\x0e\x86\xad\x58\x83\xe9\x80\xbc\x0d\xc0\x7e\x03\xa0\xce\xcd\x1d\xcf\x2d\xc8\x8e\xef\xad\x40\xb6\x3d\x71\xed\x45\x61\xa0\x6d\xfe\x3d\xd3\xd9\x40\x3d\x9e\xc0\xdb\x21\x83\xd2\x30\x95\x3c\x70\x07\xb6\x3d\x64\x06\xb2\x14\xca\x64\xc8\x02\x21\xc8\x3b\x71\xb0\x2c\x49\x88\x87\x21\x9c\x97\x86\xdf\x2c\xb6\xe1\xa2\xa7\x4b\xdb\x50\x17\x86\xd9\x39\x3c\x3e\x3e\xf8\x8c\x88\xc7\xf1\x63\xc3\x6c\x77\x9c\x96\x10\xc5\xdb\x70\x62\x4f\x05\xbf\x1d\xec\x3f\x7d\x62\x0c\xda\xa6\x28\x5e\x9f\xec\x1f\x1a\x03\x7f\xe7\xd5\xe6\x81\xba\xf3\xe1\xa7\x46\x67\xf7\xd5\x78\x75\x23\x2f\x17\x49\x72\x45\x81\xfe\x1c\xbf\x22\xf3\xd4\xd5\x4e\x7d\x61\xdc\x53\x34\x70\xd5\x13\x85\x88\x10\xa8\xde\x96\x92\xb2\x30\x6c\x18\x61\x11\x98\x2b\xa3\x63\x92\x5f\xda\xee\xc4\x1a\x98\xe0\x3c\x4c\xba\x11\xc9\xd8\x7f\xc2\x98\x83\xa7\xac\xf0\x09\xc2\xd2\x79\x66\x8d\xdd\x49\xdf\x71\x31\x8e\x8a\xcf\xad\x42\xb6\x94\x76\x11\x2a\xae\x96\xc2\x18\xaa\x58\x6a\xa5\x60\x56\x09\x4f\xd0\x54\xf6\x6e\xe1\x40\x34\x16\xc9\x19\xc4\x53\xab\xaa\x53\x11\x7b\x96\xcf\x66\x9c\xd9\x69\x8b\x24\x1d\xda\x8e\x63\x19\xd5\xa1\x2e\xb9\x2a\xaa\xa9\x90\x33\x79\x51\x6d\x06\x49\xfc\x31\xc8\x46\x8c\x4d\xdc\x10\x9f\xe6\x8f\x4d\x84\xef\x61\xc7\x3d\x9b\x76\xbb\x44\xed\xcc\xa1\x06\x88\xd6\x4d\xb1\x11\xa9\x06\x7c\x61\xad\x29\x37\x07\x20\x5d\xec\x3a\xd3\xb3\x9f\x99\xed\x09\x93\xdc\xb2\xf0\xfd\x44\x95\x0e\xaa\xe9\x32\x91\xc3\x25\x7b\x9e\x5a\x66\xab\xe6\x9c\x9e\xc9\xeb\x4e\x8e\xa1\xf3\xcf\xc5\x57\x5f\x15\x1f\xde\xbc\xe1\xd6\x43\xc2\x78\x98\x64\xb2\x4e\x0b\x66\xf6\x41\x4c\x4c\x42\x21\xda\x2b\x6a\x9f\x3e\x3e\x46\x8e\x74\x06\x93\xb1\x83\x96\x28\x22\x46\x40\xa6\xda\x44\x38\x22\x47\x41\x26\xb3\x27\xd0\x01\x95\xe7\x3c\x16\x13\xd1\xfe\x53\x49\xd5\x16\x04\x61\x1b\xc4\x86\xec\x6e\x5b\x3c\xfe\x74\xff\xb3\xa6\xb0\xf4\x44\x7a\xbd\x25\x4b\x51\x5b\x41\x80\x88\x27\xf2\xa2\x1b\xa4\xac\xcd\x7c\x25\x0f\xa8\x10\xea\x73\xb3\x3f\x22\xa6\xa7\x5d\x4b\xd3\x73\x22\xad\x9c\x61\xc8\x6b\x82\x90\xf4\x85\x14\xd4\xdc\xf8\x32\x8f\x61\x29\x6d\x26\x6f\xdb\x01\xe4\xaa\xbb\x12\x77\xea\x7d\xe6\xb6\x6a\x8d\x30\xbe\xc4\x5a\xd0\xcf\xa5\x05\x15\x99\x70\x1b\x62\x34\x7f\xe0\x1d\x56\xc9\x6f\x52\xdd\x74\x53\x8c\x10\xee\x69\x5b\x68\x24\xd1\x98\x59\xc9\x68\xd6\xa0\xb8\x0e\xbc\x2a\x03\x95\x36\xf2\x8d\x81\xeb\x34\x20\xfc\x28\xc4\xae\xaa\x1d\x89\x04\xb9\x44\x5e\xad\x2e\x45\xcb\x6d\x21\x71\x0f\xa1\xd5\x06\xfe\x10\xa3\x2d\x7a\x6c\x29\x2d\x9b\x98\x26\xfe\x41\x80\x38\x09\x7a\x48\x1a\x3d\x3e\x3a\x3c\x6c\x8a\x09\x6d\xa2\x60\x8b\xdf\x52\x7e\xc2\xa3\x64\xc3\xdd\x74\xc6\x0e\x69\xff\xaf\x6b\x64\xe1\x35\xf1\x05\x7f\xfe\xb2\x52\x5c\xfc\xfe\x6b\xa1\x1d\x54\x18\x5d\x7b\x34\x60\x02\x37\xe0\x55\x6c\x89\x02\xa7\xcf\x95\xa7\xd4\x4d\x92\x06\x05\xeb\xdb\x12\x3e\xe3\x95\x4f\xe9\x6d\x87\x7c\xca\x25\x7c\x5f\x73\x3c\x78\x55\x8d\xd7\x41\xad\xdc\xf3\xce\x79\x4e\xd1\xd9\x68\x75\x10\x9b\x99\x73\xe8\x96\x92\xf2\x15\xdf\x0b\x1e\xd9\x6b\xc3\x3b\x11\xa8\x11\xeb\x2b\x51\x6c\x47\xe2\xe3\x7d\x30\x3d\x48\x7a\xde\xa2\xf4\xf8\x78\xbf\x14\xa4\xd7\xa2\x99\x63\x65\x2d\x10\x10\x4b\x5f\x33\xa5\x84\x40\xd4\xd8\x61\x14\x0f\x38\x01\x3b\xc9\xb0\xf1\xab\xd3\xcc\x5f\xd5\xe9\xe3\xe9\xc9\xe3\xa3\x27\x9f\xd5\x4b\x40\x4e\x97\x9e\xef\xa5\xb0\xda\xe0\xf2\x74\xbf\xbe\x4a\x92\x88\xd3\xc2\x29\x22\x4b\x3d\x0c\x22\xe9\x16\x41\xf7\x54\x13\x9e\x72\xe6\x13\xf1\x7a\x4b\xad\x0f\x0e\x0e\x0f\x0e\x5e\x17\xae\xc6\x24\x4b\x51\xb1\x7e\x3f\xa6\x54\xc3\x6c\xb1\xd5\xd0\x16\x6c\xff\x3e\x5c\x91\xa5\x9f\x5b\x9d\x5d\x60\xc7\x69\x72\x1d\x12\x29\x64\xc6\x35\x87\xeb\xd1\xfe\x95\x5e\x1e\xba\x9c\xb0\x4f\x2d\xbc\x6b\xd2\xfd\xba\xec\xb5\x96\x74\x56\x47\xd3\x23\x9a\xe9\x15\x6e\x0b\x2a\x50\xfc\xe6\xbc\x29\x5e\x33\x0d\x2f\xbe\xaa\xd7\xff\x67\x28\xd2\x86\x4f\xc0\x84\x1b\xf8\x6d\x04\x29\x65\xb7\x3d\x6e\x14\x81\x8a\xcb\x05\x23\xfb\x23\x56\x96\x2b\xa3\x3a\xe5\xa4\x9c\xef\xcb\x72\x8d\x6e\x46\x31\xed\xf5\x06\x26\xb7\x38\x12\x2d\x0a\x8a\x72\x27\x98\xd3\x29\xb6\xec\x23\xf3\x86\x52\x53\xe8\x82\xa1\x17\xe1\x28\x74\xa3\xf0\x4a\xba\x9a\x69\x61\x84\xa5\x93\x11\x05\x9c\x12\x2f\xd8\x2c\x73\xb3\xc2\x9c\xab\x81\x4e\x87\x0d\x2d\x10\x75\xc6\xd4\x36\xdf\xa7\x3a\x0a\x95\xbb\x9e\x7f\x67\x2c\x93\x99\x82\xe2\x10\xed\xd6\x52\x4a\x96\xb3\x5d\x3a\xbc\x87\x70\xdc\xb8\xd0\x8e\x90\xa7\xc8\x13\xfb\x46\xaf\xed\x96\xde\xc3\x9c\x00\x42\xf4\x87\xad\x94\x28\x9c\x49\x96\x73\xcf\x70\xc7\x74\x1c\x2a\x1f\xfa\x56\xd7\xdc\x1d\x6f\xbc\x2a\x68\x2f\x59\xf5\x84\x52\x5e\xe4\xf9\xc4\xb0\x4a\x3a\xcc\x80\x6f\x2b\x45\x1d\xb3\xb5\x7d\xbf\x01\xe7\xca\xef\xd8\x77\xf1\x1d\x33\xda\xcf\xad\x36\xcd\x53\xa4\x62\x4d\xa4\xdd\xe9\xb8\x3f\x6a\x75\xdc\x6a\x75\xa8\x19\xb8\xe2\xe3\xe9\x30\x96\x4a\x16\xc7\x88\x14\x43\x51\x05\x27\x68\xa8\x05\x79\xa2\x16\x79\x52\x43\x27\xcc\xec\x15\x91\xb9\x24\xef\x0a\x05\xb3\x8f\x7d\x93\x9e\x35\xcb\x06\xc9\xf6\xe3\xe6\x3c\xd5\x1d\x98\x69\xeb\xc7\x3d\xa3\x67\x17\x4b\x71\x50\x4b\xf2\x0a\xcb\x6e\x9b\xb4\x58\x76\xa9\x9c\xbf\x79\x59\x86\xf8\x80\x14\x9e\x11\x50\x2f\x16\x92\xe1\xd8\xb6\x2a\x4e\xb1\x92\xed\x01\x74\xa0\x38\xd0\x56\x04\xe4\x6b\x52\xf7\xeb\xc2\x10\xb6\xda\x1f\xd3\xc9\x06\x25\xbb\x8a\x90\x3b\x03\x35\x3c\xdb\xcf\xaf\x77\xaa\xea\xca\x07\x3a\x8a\x8a\x25\x41\xb3\xa4\x7a\x83\xeb\x2c\x22\xd0\xe0\xed\xaa\x70\xb4\x70\x09\x7e\xb7\xf7\xed\x4a\xce\xff\x50\x3f\xae\xe2\xb9\x81\xba\x7b\xf4\xc2\xec\xf0\x11\x03\x55\x7f\xf7\x76\xa2\xd4\x73\xab\x6b\x03\x24\x6e\xe6\x8a\x14\x5f\x76\xd7\x7a\x74\x38\x38\x33\xe8\xa4\x98\x4a\x02\x48\xfa\xb4\x18\x16\x6f\xb8\x27\x8d\x51\xcc\x7e\xf2\x55\x94\x78\x77\x40\x02\xd7\xa4\xd1\x94\x78\x9d\xe2\x88\x99\x6c\x99\xc0\x76\x56\xd2\x47\x5e\x97\xfa\x40\xa8\xc8\x8b\x04\x1c\x1d\x4b\xac\x05\xc2\xcf\x8a\x8e\x83\x08\x14\x79\x07\x41\x64\x65\x04\xf1\xa3\x52\x08\xb2\x53\xc1\xb0\xd0\x1d\x8e\x46\x87\xe9\xa4\x36\xba\x69\x68\xd7\xc5\x34\x0e\x6f\x3b\x1e\xd1\x3f\x3b\xbf\x5c\x17\x4f\xdd\xf6\xd3\xc3\xc3\xf2\xf7\x1b\xfd\x70\xbc\x5f\x2f\x45\x6f\x1e\xf4\xa7\xa3\xa3\xa3\xcf\x36\x0f\x43\x2f\x4e\xea\xe2\x59\x88\x32\x48\x82\x3e\x39\x19\xf2\x7b\xf1\x33\x00\xa7\x0b\x37\xcf\x7e\x9a\x70\x02\xe4\x57\x1a\x55\x24\x47\x56\x66\x95\xab\x7b\x97\x54\x27\x54\x60\x50\x52\x96\xf6\x3e\x4f\x22\x0f\x45\x6f\x92\xce\xf7\x56\x57\xf3\x3d\x42\x6f\xef\x43\x3c\x35\x10\x76\x55\xe6\x91\x95\x74\x47\xf6\xa0\xa5\x73\x59\x94\xcc\xf5\x95\xcc\xf6\xe4\xac\xcc\x69\xd4\x3f\xd1\xc9\xac\x4c\x6a\x94\x8d\xe9\x97\xd8\xb2\xf6\xfd\xf2\x74\xeb\x8e\xfb\x97\x63\x4b\x66\x06\xd6\xeb\x91\x22\x94\x5c\x79\x7c\x46\xbb\x44\xcf\x10\x2c\x87\x0f\x7b\x4b\xdb\x2c\x87\xd5\xd9\x48\x6a\x46\x71\xca\x54\xb4\xfe\x6f\x96\x1a\x77\xab\x0c\x8e\xa0\xe5\xc6\x27\x29\x42\x1f\x6d\xb3\x23\x2f\xf3\x39\x3d\x58\xc0\x9e\x7e\x5f\x78\x29\xef\xdf\x4c\xd3\x24\xa5\x87\x76\x1a\xd2\x49\xce\xdd\xec\xae\x25\x18\x7d\x94\xe2\xc4\x72\xf8\xd5\x28\x99\x4e\x89\x0d\x6f\x5d\x9f\x71\x90\x1a\x9a\x45\xfb\x45\x39\x6c\x33\x80\xc1\xb8\xdb\x9b\x1a\xb7\x5d\x3f\xd7\x74\x53\xc7\x1d\x45\x67\x4c\x09\xcc\x02\xd6\x8d\xae\x22\x4d\x32\x3c\x3f\x52\x37\x64\x81\xec\x82\x09\x05\x06\x2a\x54\x0a\x6a\xf1\xc9\xfb\xf9\xaa\x3f\xea\xb9\xf6\x68\xa2\x49\x73\x11\xaa\xc8\x91\xf9\xda\x62\xeb\xcd\x54\xee\x40\x8b\xb4\x9a\x1d\x19\x8c\xe9\xbe\x76\x66\x3a\xc7\x77\x4a\x9c\x19\xe9\x4d\x20\x51\x8b\x70\x96\x3d\x24\xe7\xf0\x29\x48\x8f\x17\x43\xa0\xf8\xe2\x0b\xbc\xd5\xa9\x6c\xaf\x84\x18\xd7\x39\xb7\xba\x7c\xa9\xf0\x94\x73\xe0\x9c\xe2\x20\xef\x3a\x00\x4f\x5e\xbf\xbf\xaf\x4e\xcb\xea\xbf\x7c\x6f\x67\xe6\xed\x2a\x4c\x39\x76\xa0\xb8\xc2\x72\x48\x00\xad\xe5\x51\x20\x23\x49\x27\x52\x33\x3a\x7e\x58\x62\xd9\xd4\x63\x17\xae\x27\xbc\x98\xcd\xa9\x61\x45\xcd\xf1\x7d\x3a\x8e\xab\x5a\xb3\x65\x41\x70\x35\xbb\xa5\x68\xa6\xaf\x35\x0b\x3c\x96\x48\xea\x88\xbf\xf7\x50\x11\xdb\x04\x15\x1a\xa2\xf2\x75\x91\xcf\x07\x4e\xf5\x22\x64\xa2\xef\x41\xd3\x8d\x6c\xae\x01\x2b\x4c\x1a\x42\x22\x4c\xf7\x90\xd4\x2a\xb9\x29\xdc\x02\xdc\x90\x4c\x3e\x47\x74\xd4\xbe\x9f\x07\xab\x3b\x76\x4f\x5d\xaa\x57\x53\x78\xe7\xc3\x90\x0a\x71\x2f\x2e\x97\x36\x47\xc6\x1c\x49\xee\xa0\x44\x8d\x55\x94\x1e\x3a\x00\xd8\x5d\x40\x27\xf4\xe6\x31\xa6\x0b\xfd\x12\xba\xa2\x44\x25\xf2\x51\xab\x1c\x16\x3c\xd8\xf1\xce\xe9\x41\x41\xfc\x7f\xd7\xd2\x8b\xb5\x2b\x89\xfb\x6e\xaf\x0d\x92\x6d\x76\x2e\x62\xde\xab\xda\x41\xb5\xe2\xab\xd5\x6b\x87\x3b\xef\x17\xa4\x13\x93\x0e\x81\x9c\x0a\x6c\x9b\xb0\x7b\x17\xba\xed\x6d\xc3\x16\xbe\xdd\x5b\x07\xb1\x73\x01\x60\x74\x6c\x92\xcd\xfd\xce\x3c\x3a\x6d\x83\x7d\xdc\x22\xa9\xe8\xe5\x9d\xf0\xfd\xc1\x09\xfd\xf3\xe5\xe6\x66\x91\x4f\x29\xff\xa0\xb8\x9e\x3f\xcd\xb3\xd9\x53\x83\xac\x46\x57\x9b\x69\x52\xbd\xe9\x4c\xf3\x38\xa6\x38\x43\xcd\x7c\x1e\xc6\x99\x3f\x4c\x82\x90\xff\xd6\xa0\x59\x39\x4e\x2a\x3c\xd1\xce\xe3\x6a\x6f\x36\x5d\xbe\xf1\x41\xee\x4a\xc1\x8c\xf8\x8f\x0b\x5a\x13\x97\x4f\x46\xb6\xc4\x8c\xee\x97\x02\x4e\x2c\x21\xc5\x66\xa5\x57\xd2\xcc\xb9\xd1\x2d\x1a\x2f\x0c\xa7\x7d\x6e\x76\xa6\x4c\xbf\xbe\xd4\x8e\x76\xb0\x30\x58\x53\x9b\xbf\x20\x58\x48\x2f\xa2\x93\x4f\x3a\x15\x2d\xa4\xd0\x5d\xb4\xab\xdb\x5d\x6e\xbf\x4f\xd0\xe1\xa7\x74\x6d\xd5\x4a\xe7\xb9\xe6\x81\xe4\xcb\x9c\xf7\x60\x23\x1f\xa3\xe4\x10\x33\xe5\x5f\x7d\x5c\xc2\x5a\x6b\x34\xf2\x38\x25\x12\xc5\x38\x35\x1a\x99\x37\x57\x94\x2e\x29\x93\x73\xbe\x4f\xe2\x4d\x46\x0f\xb3\x86\xf2\x97\xcc\x5e\x83\xc4\x57\xdc\x40\xd2\xf6\x0e\x9a\x4f\x9a\xc7\x46\xcb\xee\x51\xe8\x31\x98\x39\xd3\x59\x6e\xe5\xef\x21\xf8\x66\x8d\xcc\xbc\x44\x84\xd7\xef\xf2\x8e\xe8\x1b\x30\xb9\x03\x28\xeb\xe1\xfe\xed\x19\xaf\x30\xf3\x05\x87\xbb\x9e\x35\x71\x3b\x56\xb7\xbb\x1b\xdc\x1f\x06\x60\xee\x57\xb7\xef\xcd\xc9\x00\x15\xfc\x03\xbb\xa7\x84\xf5\xbb\xec\x7e\xee\x17\x7b\x47\x41\xb4\xd9\xfe\xab\xf0\xe0\x29\x45\xd7\xd6\x90\x1b\x64\xdc\x98\x3a\xf5\xef\x16\x8d\xf6\x90\xfe\x3d\x7f\x56\x0f\x64\xa3\x63\xd6\x67\x69\xa3\x6b\xd7\xe3\xa8\x31\xec\xd7\xa3\xeb\x46\xff\x79\x3d\xcd\x1b\xf6\xb4\xfe\xad\xd7\xf8\xd9\xb8\x2e\x55\xc3\x74\xea\xab\xac\x71\x66\xd7\x57\x51\x63\xdc\xaf\x5f\xce\x1b\x67\xbd\x3a\x26\xb5\x26\x7c\xc1\x46\xb2\x4d\x44\xe7\x50\x2d\xea\xbf\xfd\xa7\x9f\xff\xe6\xdf\xff\xf4\x37\xbf\xfa\xc7\x1f\xff\xfc\x8f\xeb\xbf\xfd\xf5\xf7\xff\xf5\xf7\x7f\x56\xbc\x74\x64\x9e\x29\x7f\x51\xef\xa6\x5e\xfc\xc3\xdf\x79\xa1\xaa\x0f\x25\x6a\x7a\x70\xb3\x40\xd5\xfb\x5e\x76\x1d\xca\xff\xf8\x9b\xbc\xfe\xf6\xaf\xdf\xfd\xd1\xbb\xef\xdf\x7d\xff\xf6\x5f\xdf\xfe\xea\xed\xaf\xeb\x3f\xfe\xc5\xdf\xfe\xf8\x97\xff\xf0\x9f\xbf\xf8\xab\xba\xa9\x56\xde\x0f\xbf\x4c\xa2\xfa\x18\x34\x35\x9f\xe7\x3f\xfc\x42\x81\xcc\x88\xb3\xd4\x53\x21\x35\x46\xea\x2a\xac\xbf\xfd\xe5\xbb\x3f\x79\xfb\x6f\x6f\xff\xe5\xed\x3f\xbf\xfb\xb9\x96\x51\xb7\x32\x2f\x0a\x89\x3a\x6a\xea\x15\xb0\x1a\xc8\x09\x88\x08\xa2\x96\xbb\x42\x40\x63\xa0\x28\x54\x48\xa2\x8a\x17\x06\x23\xc5\x88\x19\x0c\x17\x1e\xbf\x5b\x18\x8c\x19\x3f\x36\x26\x2f\x0c\xc6\x8e\xff\xa0\xc7\x60\x00\xc9\xf5\x52\x83\x51\xc4\x63\x1c\x19\x0c\x25\xfd\x01\xc2\xb5\xc1\x78\xd2\xdd\x63\x6e\x30\xa8\x78\xfc\xd6\x33\x18\x59\x9a\x45\x19\x0c\x2f\x1e\xf9\xd7\x60\x98\xe9\x2d\x32\x18\x6b\xfa\x7b\xa0\xb9\xc1\x80\x53\x2d\x92\x41\xb3\x09\x45\x30\x78\xdd\xf9\xe8\x85\xdb\x05\x5b\x05\x77\x3b\xb3\xf5\x75\xeb\x26\x06\xfc\x77\x00\x00\x00\xff\xff\x69\xe4\xd5\x4b\x7a\x25\x00\x00") +var _confAppIni = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xb4\x7a\x5d\x8f\xe3\x46\x76\xf6\x3d\x7f\x45\x59\x7e\xfd\x7a\x1c\x48\xea\x2f\xf7\xcc\xb8\xed\x4e\xac\x96\x28\x35\x77\xf4\x65\x52\x9a\xf1\x78\xd0\xe0\xb0\xc9\x92\x44\x37\x45\x6a\x58\x64\x77\xcb\xc8\xc5\x1a\xb9\x08\x90\xdb\x04\xc9\x4d\x10\x24\x17\x41\x80\xcd\x07\xb2\xc8\xcd\x6e\x82\x5c\x19\xb9\x9f\xf9\x0f\x0b\x6f\xf2\x2f\xf2\x9c\x53\xa4\x44\xf5\xb4\x1b\x59\x20\xd9\x35\x46\x64\xb1\xea\x54\xd5\x73\xbe\x9e\x53\xd5\x1f\x8a\xa1\xf9\xdc\xb4\x05\xff\x33\x18\x75\xac\xee\x4b\x31\x39\xb7\x1c\xd1\xb5\xfa\xa6\xf1\xa1\x18\xf7\xcd\x96\x63\x8a\x41\xeb\x99\x29\xda\xe7\xad\x61\xcf\x74\xc4\x68\x28\xda\x23\xdb\x36\x9d\xf1\x68\xd8\xb1\x86\x3d\xd1\x9e\x3a\x93\xd1\x00\x8d\xc3\xae\xd5\xd3\x23\x8d\xcf\x45\x6b\xb5\x12\xb1\xb7\x94\x22\x5b\x78\x99\x50\x8b\xe4\x46\x89\x24\x16\xf2\x5a\xa6\x6b\xb1\xf2\xe6\xf8\x10\x66\x91\x34\x5a\xe3\xb1\x3b\x6c\x0d\x4c\x71\x2a\x7a\xc9\x5c\x9d\xe0\x5f\xd1\x0b\x33\xe1\xc8\xf4\x3a\xf4\x25\x24\xb5\x17\x5e\x8c\xee\x68\x0b\x67\x62\x9d\xe4\x22\xcd\x63\x11\x25\xbe\x17\x45\x6b\xc3\x9e\x0e\xdd\xa9\x83\xd5\x9f\x8a\x79\x98\xa1\xb7\x19\x66\x0b\x99\x8a\x5a\x20\xaf\x6b\x75\x51\x5b\xa5\x49\x50\x13\x09\x1a\x32\xa9\x32\xb4\x04\x72\xe6\xe5\x11\x64\x29\xdd\x87\x25\x60\xeb\xb4\x00\xbc\x1b\xc6\xab\x54\xae\x12\x15\x66\x49\xba\xbe\x30\xec\xd1\x68\x22\x4e\x0d\xa7\x6d\x5b\xe3\x89\x3b\x79\x39\xa6\x6e\x97\x9e\x5a\x60\xa6\x4e\x21\xa9\x35\x74\x2c\xe1\x2f\xbc\x54\xc9\xcc\xa0\x17\x17\x50\xd9\x8e\x89\x81\x02\xdd\xba\x49\xea\xcb\x62\xdf\xb1\xbc\x11\x5b\xf1\x22\x4b\xc4\xa5\x14\xab\x34\xbc\xf6\x32\x69\x74\x47\x76\xdb\x74\xc7\xb6\xf5\xbc\x35\xa1\x69\x66\x5e\xa4\x68\xff\x63\x2f\xf3\x17\x82\x96\x2f\xde\xe4\x32\x97\x22\x92\xf1\x3c\x5b\xd4\xc5\xd2\xbb\x62\x58\x3c\x25\x22\x2f\x05\x44\x78\x80\x6c\x15\x5e\x02\xd7\xf1\xb4\xdf\x77\x6d\xf3\xab\xa9\xe9\x4c\x5c\xfc\x3b\x35\xdd\xbe\x39\xec\x4d\xce\x21\xf9\x60\x1f\xff\xc3\x4e\xf3\xf0\x02\xf2\x87\xf9\xf2\x12\x88\x25\xb3\xed\xd2\x42\xa9\xb4\xde\xbc\x54\xb2\xee\x64\x20\xc2\x18\xfa\xc3\x46\x6e\x57\x51\x82\x56\x52\xa1\x61\x7e\x3d\xee\x8f\x6c\x2c\xba\xd5\x83\x25\xb8\xc3\xe9\x00\xd2\x0f\xf7\x77\x84\x86\x4a\xe5\x3f\x2d\x8e\xc5\x58\x8e\x33\xbd\x23\xe4\x60\x57\xc8\xd2\xbb\x0d\x97\xf9\x52\xf8\xc9\x72\x19\x66\xea\x8e\x10\xcf\xcf\xc2\xeb\x30\x5b\x8b\x99\x94\x81\xd1\x35\xcd\x8e\x3b\x68\x7d\xed\xb6\x47\x83\x81\x35\x29\x04\x1e\xf3\x7e\x9b\x5e\xb0\x0c\xe3\xdd\x5d\xe7\x4a\xa6\x0f\xaf\x8f\x0c\x6c\x77\x79\xc7\xfb\xef\x03\xf7\xa0\x08\xdb\x1c\x8f\x1e\x14\x11\x27\x19\xcc\xfd\x61\x21\xc3\xd1\xc4\x6a\x9b\x0f\x8a\x49\xd2\xb9\x17\x87\xdf\x79\x59\x08\x67\x7b\x48\xd6\xc8\xee\xbd\x27\xc8\x78\xb5\xf4\xd2\xab\x20\xb9\x61\x84\xcc\xd8\x83\x21\x09\x18\x76\x20\xa2\x10\xe3\x2e\x53\xe9\x5d\xc1\x00\x32\x19\x2b\x88\x37\xcc\x61\xeb\xac\x6f\xba\x30\xf6\x8e\xdb\xb7\x86\xa6\x7b\x66\x9b\xad\x67\x1b\xcb\x35\x5e\x01\x58\x98\xfd\x85\x31\xb6\x47\x93\x51\x7b\xd4\xc7\xa7\x45\x96\xad\x8c\xce\x68\xd0\xb2\x86\x78\x63\x2f\x5e\x24\x2a\x63\x47\x73\xa7\x36\x75\xf9\xe8\x51\xd9\xff\x13\x75\xb2\xb7\xf7\xd1\x23\xdd\x1d\x2f\x1f\x3d\x3a\x9f\x4c\xc6\xee\x78\x64\x4f\x3e\x51\x7b\x06\xbf\xb4\x3a\x1d\x38\xbf\xb1\xf9\x00\x01\x47\x64\xdd\xf0\xcf\x50\xf1\x06\x1c\xe7\x1c\x86\xe1\x65\x39\x80\xb8\x59\xc8\x98\xa0\x16\xde\xb5\x17\x46\xf4\xd9\xe8\x58\x0e\x6f\x83\xba\x6d\x9d\xee\xc5\x42\x72\x1c\x81\x71\x88\xcb\x3c\x8c\x32\x60\x47\x3d\xf4\x96\x28\xa0\x40\x4a\xd3\x70\x26\x2d\x7b\x42\x43\x5d\x98\xc8\x73\x0e\x43\x5a\x02\x35\x15\xcb\x39\x3c\xac\x2c\xa6\xdd\x19\x52\x2c\x88\x49\x17\x45\x70\x5a\x26\x01\xd4\xd1\xed\x32\x84\x45\x24\xd2\x42\xca\xa5\xd9\xa3\xe9\x04\x16\xd8\x1f\xf5\x2a\x2b\xec\xc9\x58\xa6\x88\x1b\x42\x65\x72\xa5\x4e\xd0\xf2\xff\x84\x1f\x40\xb9\xd9\x62\x2f\x4b\xf6\xe6\x08\xa6\x7b\x7e\xae\xb2\x64\xb9\x47\xa0\x2b\xee\xd0\xe4\x76\xe1\xcb\x34\x13\x0d\xdf\x3b\xcd\x52\xc4\x93\x46\x90\xa7\x6c\x30\xa7\x4f\x9f\x3c\xde\x5f\xec\x2f\xf7\x95\x68\x90\x56\x4e\x97\x6b\xfa\x69\xca\x5b\x6f\xb9\x8a\x64\x13\xde\x67\x7c\x0e\x39\xa3\x54\xcc\xd2\x64\x29\x3c\xd1\x5c\xcd\x6e\xc5\x2c\x8c\x38\x2e\x24\x69\x06\x23\xe3\x2f\xc0\x4e\xbc\x08\xe3\x80\xa2\x3e\x4d\x16\xce\x42\x5f\xaf\x95\x62\xc7\xa3\x20\x81\x14\x52\xc3\x0c\xf6\x2a\x33\x0a\x84\x7a\x3c\x0f\x2c\x02\xa2\xb8\x92\xeb\x4f\xf4\xbe\x92\x15\x4c\x4e\x45\x62\x75\xe5\xab\x83\x43\xd1\x00\x78\x24\x95\x67\x6f\x24\x79\x56\xbc\xc9\xa5\x68\xc4\x09\x86\xa9\xff\xd9\x28\xf4\x2c\x07\xd1\x07\x45\x0f\x81\x54\x46\xdb\x84\x52\x29\x91\x01\xee\x2a\x84\x7b\xe5\x34\xc6\x33\xf3\xe5\xbd\x1d\x0a\x89\x98\x7e\xba\x5a\xc1\x4e\x22\xe8\x3a\x22\xbf\xcc\x24\x10\xa4\x4d\x79\x71\x00\x14\x00\xb7\xaf\x71\x23\x7d\xa1\x7b\x25\x2d\x31\x04\x68\x25\x63\x05\x58\x94\x15\xa9\x59\xde\x4a\x3f\x07\xc0\x64\x73\x08\x03\x2e\x7b\xcc\xb8\x45\x01\x5d\xa7\xdb\x88\x20\x26\xb7\xd7\x93\xf6\xbe\xb1\xc6\x42\xe5\x2b\x82\xb5\x74\x55\x6e\xdb\x9a\x50\x1f\x8b\x09\xe3\xb9\x4e\xc7\x33\xb6\xe9\xb8\x11\x25\xf3\x39\xd4\xc8\x51\xb1\x2e\x7c\x2f\xa6\x1c\x55\x5b\x24\x4b\xa9\xf3\x68\x91\x00\x6a\x46\xbf\xc5\xf9\x9f\xa2\x08\xe1\x40\x3d\xe0\xf3\x81\x97\x79\x48\x90\xf2\xa2\x92\x8b\x97\x6b\xf5\x26\xe2\x6c\x0c\x6b\x9a\xa7\x52\x69\x49\x68\x0c\x33\x79\x84\x0f\x61\xf6\xb1\xa2\xd4\x9e\x22\x8f\x26\x94\xf5\x3b\x67\x65\xb2\xe5\xb1\xc6\xf9\xc8\x21\x57\x3a\x38\x7c\xd2\xdc\xc7\xff\x0f\x4e\x8e\x8e\xf6\x1f\x1b\x05\x6f\x20\x93\x36\x0a\x12\x90\x26\x49\x66\x8c\x5b\x8e\xf3\xa2\xc3\xb8\x74\x69\xa2\xca\xb4\x71\xb4\xae\x0b\x59\x72\x04\xed\x94\xb4\xb2\x54\xbe\xc9\xc3\xb4\xd8\x22\x3c\x3c\x9c\xad\x1b\xb3\x3c\x8a\x6a\xf0\xe4\xfe\x86\x1f\xe8\xfe\xa5\xd8\x72\xfd\xac\xd3\x5a\x16\x06\x97\x35\x43\x2b\x44\x10\x0a\xec\x6a\xcd\xe0\x12\xa0\x14\x59\x87\x22\xa2\x9f\xa7\xc8\x54\x17\x86\x35\x84\x1e\x91\xa2\xfb\xa3\xf6\xb3\x8a\x4a\x3e\xf8\x40\xf3\x2c\x4d\xc3\x26\x23\xf1\xcc\x34\xc7\xe2\xe5\x68\x6a\x0b\xde\x61\xa7\x35\x69\x09\xa7\xd5\x35\x3f\xf8\xc0\x70\xcc\xb6\x6d\x4e\x5c\xd8\x22\x04\x7c\xf0\xe1\x97\xdd\x8e\xf9\xc2\xc6\x7f\xff\xff\xf7\x1e\x91\x45\xe4\x59\x42\xca\x84\xd5\xa7\x72\x29\x39\x41\x04\x1e\x5c\x03\x61\xc4\x1a\x82\x1a\x0c\xcc\xc1\x19\xa2\x4a\xa7\xf5\xd2\xc1\xf8\x27\x46\x7b\x34\x7a\x66\x99\xcc\xa6\x2a\xc0\xba\xde\x8d\x54\xa4\xda\xe2\xf3\x66\x5c\xb5\x4f\x18\xfb\xa9\x0c\x42\x8d\x8d\x4d\x5c\x47\x91\x1b\x27\xb7\x6b\xe1\xe5\xc0\x3a\xce\x4a\xdb\x5c\x48\x2f\xc0\x42\x98\x19\x16\xb9\x97\x5f\x90\x20\x11\x3e\x1d\xe2\x3f\xa3\xaf\x5f\xba\xad\xe9\xe4\xdc\x1c\xc2\xcc\x61\xea\xa3\x0d\xc3\xfb\xba\xf1\xc2\x3c\xa3\x4f\x0d\x6a\x28\x12\x0c\xcc\xe5\xc2\x68\xb5\x27\xd6\x73\x13\x19\xbf\x03\x9a\x43\x4f\x03\x6b\x88\x98\x49\x1b\x3b\x78\xba\x0f\xe1\xa0\x63\xae\x36\x8b\x9f\xec\x04\x9f\xe5\xd5\x80\x43\x50\x40\xf2\x93\x78\x16\xa6\x4b\x21\x1b\x4b\xa4\x0a\x76\x8f\x54\xce\x43\x95\xe9\x58\x09\x99\x3d\xcb\xa1\xb0\x6c\x22\x3b\xf5\x5d\xa6\xbf\xf6\xa0\xa2\xca\x4e\x82\x74\xce\xb9\x26\x8a\x92\x9b\x62\x30\x26\x20\x6b\x61\x83\x10\x00\x8d\x43\x82\xef\x27\x79\x9c\xb1\x71\x6e\x63\x3e\x8b\xb7\x79\xff\x15\xa1\xbc\xc4\x25\x42\x8e\x50\xe1\x9c\xb3\x08\x96\x7a\x1d\x82\x57\x7a\xf1\x3a\x5b\xc0\x9b\x9b\x06\xb1\x3e\x0b\xac\xcc\xb1\x7a\x43\x68\xfa\xb9\x65\xbe\xa8\x48\x68\x7b\x3e\x02\x0c\xf2\x5f\xe6\xa5\xcc\x18\x43\x9f\x52\x63\x19\x22\xda\xad\xf6\xb9\xe9\xb6\xc0\x41\x5b\x76\x65\xd4\x80\x30\x20\x8e\x32\x2b\x34\x59\xf6\x27\x4e\xd2\x7d\xe9\x12\x06\xd5\xee\x14\xe6\x03\x99\x61\xd4\x09\xe7\x7c\xca\xe4\x20\xe8\x8b\xfc\x92\xb2\x08\xb9\x06\x68\x9c\x4e\x52\x9a\x20\xee\x1d\x3c\x3e\x2e\x65\x3e\x64\x0b\x9b\x49\x7e\xaa\xef\xe8\xa7\xa0\xeb\x24\xac\x0d\xec\xde\xbf\x12\x80\x9f\x09\x25\x02\x36\x90\xfc\x0e\xcc\x00\x8b\x83\xce\x53\x84\x89\x55\xa2\xc3\x62\xb6\x5e\x6d\x73\x30\x6c\xc5\x1a\x4c\x07\xe4\x6d\x00\xf6\x1b\x00\x75\x6e\xee\x78\x6e\x41\x97\x7c\x6f\x05\xba\xee\x89\x6b\x2f\x0a\x03\x6d\xf3\xef\x99\xce\x06\xea\xf1\x04\xde\x0e\x19\x94\x86\xa9\x68\x82\x3b\xb0\xed\x21\x33\x90\xa5\x50\x26\x43\x16\x08\x41\xff\x89\xc5\x65\x49\x42\x4c\x0e\xe1\xbc\x34\xfc\x66\xb1\x0d\x17\x3d\x5d\xda\x86\xba\x30\xcc\xce\xe1\xf1\xf1\xc1\x67\x44\x3c\x8e\x1f\x1b\x66\xbb\xe3\xb4\x84\x28\xde\x86\x13\x7b\x2a\xf8\xed\x60\xff\xe9\x13\x63\xd0\x36\x45\xf1\xfa\x64\xff\xd0\x18\xf8\x3b\xaf\x36\x0f\xd4\x9d\x0f\x3f\x35\x3a\xbb\xaf\xc6\xab\x1b\x79\xb9\x48\x92\x2b\x0a\xf4\xe7\xf8\x15\x99\xa7\xae\x76\x2a\x14\xe3\x9e\xb2\x83\xeb\xa6\x28\x24\x0e\x95\x85\x4b\x49\x59\x18\x36\x8c\xb0\x08\xcc\x95\xd1\x31\xc9\x2f\x6d\x77\x62\x0d\x4c\x70\x1e\xa6\xed\x88\x64\xec\x3f\x61\xcc\xc1\x53\x56\xf8\x04\x61\xe9\x3c\xb3\xc6\xee\xa4\xef\xb8\x18\x47\xe5\xeb\x56\x21\x5b\x52\xbc\x08\x15\xd7\x5b\x61\x0c\x55\x2c\xb5\x52\x30\xab\x84\x27\x68\x32\x7c\xb7\xf4\x20\x22\x8c\xe4\x0c\xea\xaa\x55\xd5\xa9\x88\x3d\xcb\x67\x33\xce\xec\xb4\x45\x92\x0e\x6d\xc7\xb1\x8c\xea\x50\x97\x5c\x15\xf5\x58\xc8\x99\xbc\xa8\x57\x83\x24\xfe\x18\x64\x23\xc6\x26\x6e\x88\x91\xf3\x47\x70\x47\x73\xd8\x71\xcf\xa6\xdd\x2e\x51\x3b\x73\xa8\x01\xa2\x75\x53\x6c\x44\xaa\x01\x5f\x58\x6b\xd2\xce\x01\x48\x97\xcb\xce\xf4\xec\x67\x66\x7b\xc2\x34\xb9\x2c\x9d\x3f\x51\xa5\x83\x6a\x76\x4a\xe4\x70\xc9\x9e\xa7\x96\xd9\xaa\x39\xa7\x67\xf2\xba\x93\x63\xe8\xfc\x73\xf1\xd5\x57\xc5\x87\x37\x6f\xb8\xf5\x90\x30\x1e\x26\x99\xac\xd3\x82\x99\x7d\x10\x13\x93\x50\x88\xf6\x8a\xda\xa7\x8f\x8f\x91\x23\x9d\xc1\x64\xec\xa0\x25\x8a\x88\x11\x90\xa9\x36\x11\x8e\xc8\x51\x98\x05\x43\x07\x54\xe0\xf3\x58\x4c\x44\xfb\x4f\x25\xd5\x6b\x10\x84\x6d\x10\x1b\xb2\xbb\x6d\xf1\xf8\xd3\xfd\xcf\x9a\xc2\xd2\x13\x15\x6c\xba\x60\x29\x6a\x2b\x08\x10\xf1\x44\x5e\x74\x83\x94\xb5\x99\xaf\xe4\x01\x15\x42\x7d\x6e\xf6\x47\xc4\xf4\xb4\x6b\x69\x82\x4f\xa4\x95\x33\x0c\x79\x4d\x10\x92\xbe\x90\x82\x9a\x1b\x5f\xe6\x31\x2c\xa5\xcd\xe4\x6d\x3b\x80\x5c\x75\x57\xe2\xce\x89\x01\x73\x5b\xb5\x46\x18\x5f\x62\x2d\xe8\xe7\xd2\x82\x8a\x4c\xb8\x0d\x31\x9a\x3f\xf0\x0e\xab\xe4\x37\xa9\x6e\xba\x29\x46\x08\xf7\x5c\x5f\x20\x6c\x2b\x9e\x59\xc9\x68\xd6\xa0\xb8\x0e\xbc\x2a\x03\x95\x36\xf2\x8d\x81\xeb\x34\x20\xfc\x28\xc4\xae\xaa\x1d\x89\x04\xb9\x44\x5e\xad\x2e\x45\xcb\x6d\x21\x71\x0f\xa1\xd5\x06\xfe\x10\xa3\x2d\x7a\x6c\x29\x2d\x9b\x98\x26\xfe\x41\x80\x38\x09\x7a\x48\x1a\x3d\x3e\x3a\x3c\x6c\x8a\x09\x6d\xa2\x60\x8b\xdf\x52\x7e\xc2\xa3\x64\xc3\xdd\x74\xc6\x0e\x69\xff\xaf\x6b\x64\xe1\x35\xf1\x05\x7f\xfe\xb2\x52\x5c\xfc\xfe\x6b\xa1\x1d\x54\x18\x5d\x7b\x34\x60\x02\x37\xe0\x55\x6c\x89\x02\xa7\xcf\x95\xa7\xd4\x4d\x92\x06\x05\xeb\xdb\x12\x3e\xe3\x95\x4f\xe9\x6d\x87\x7c\xca\x25\x7c\x5f\x73\x3c\x78\x55\x8d\xd7\x41\xad\xdc\xf3\xce\x89\x50\xd1\xd9\x68\x75\x10\x9b\x99\x73\xe8\x96\x92\xf2\x15\xdf\x0b\x1e\xd9\x6b\xc3\x3b\x11\xa8\x11\xeb\x2b\x51\x6c\x47\xe2\xe3\x7d\x30\x3d\x48\x7a\xde\xa2\xf4\xf8\x78\xbf\x14\xa4\xd7\xa2\x99\x63\x65\x2d\x10\x10\x4b\x5f\x33\xa5\x84\x40\xd4\xd8\x61\x14\x0f\x38\x01\x3b\xc9\xb0\xf1\xab\xd3\xcc\x5f\xd5\xe9\xe3\xe9\xc9\xe3\xa3\x27\x9f\xd5\x4b\x40\x4e\x97\x9e\xef\xa5\xb0\xda\xe0\xf2\x74\xbf\xbe\x4a\x92\x88\xd3\xc2\x29\x22\x4b\x3d\x0c\x22\xe9\x16\x41\xf7\x54\x13\x9e\x72\xe6\x13\xf1\x7a\x4b\xad\x0f\x0e\x0e\x0f\x0e\x5e\x17\xae\xc6\x24\x4b\x51\xb9\x7f\x3f\xa6\x54\xc3\x6c\xb1\xd5\xd0\x16\x6c\xff\x3e\x5c\x91\xa5\x9f\x5b\x9d\x5d\x60\xc7\x69\x72\x1d\x12\x29\x64\xc6\x35\x87\xeb\xd1\xfe\x95\x5e\x1e\xba\x9c\xb0\x4f\x2d\xbc\x6b\xd2\xfd\xba\xec\xb5\x96\x74\xda\x47\xd3\x23\x9a\xe9\x15\x6e\x0b\x2a\x50\xfc\xe6\xbc\x29\x5e\x33\x0d\x2f\xbe\xaa\xd7\xff\x67\x28\xd2\x86\x4f\xc0\x84\x1b\xf8\x6d\x04\x29\x65\xb7\x3d\x6e\x14\x81\x8a\xcb\x05\x23\xfb\x23\x56\x96\x2b\xa3\x3a\xe5\xa4\x9c\xef\xcb\x72\x8d\x6e\x46\x31\xed\xf5\x06\x26\xb7\x38\x54\x2d\x0a\x8a\x72\x27\x98\xd3\x29\xb6\xec\x23\xf3\x86\x52\x53\xe8\x82\xa1\x17\xe1\x28\x74\xa3\xf0\x4a\xba\x9a\x69\x61\x84\xa5\x93\x11\x05\x9c\x12\x2f\xd8\x2c\x73\xb3\xc2\x9c\xab\x81\x4e\x87\x0d\x2d\x10\x75\xc6\xd4\x36\xdf\xa7\x3a\x0a\x95\xbb\x9e\x7f\x67\x2c\x93\x99\x82\xe2\x10\xed\xd6\x52\x4a\x96\xb3\x5d\x3a\xbc\x87\x70\xdc\xb8\xd0\x8e\x90\xa7\xc8\x13\xfb\x46\xaf\xed\x96\xde\xc3\x9c\x00\x42\xf4\x87\xad\x94\x28\x9c\x49\x96\x73\xcf\x70\xc7\x74\x1c\x2a\x1f\xfa\x56\xd7\xdc\x1d\x6f\xbc\x2a\x68\x2f\x59\xf5\x84\x52\x5e\xe4\xf9\xc4\xb0\x4a\x3a\xcc\x80\x6f\x2b\x45\x1d\xb3\xb5\x7d\xbf\x01\xe7\xca\xef\xd8\x77\xf1\xdd\xa0\x53\x20\xab\x4d\xf3\x14\xa9\x58\x13\x69\x77\x3a\xee\x8f\x5a\x1d\xb7\x5a\x1d\x6a\x06\xae\xf8\x80\x3b\x8c\xa5\x92\xc5\x41\x24\xc5\x50\x54\xc1\x09\x1a\x6a\x41\x9e\xa8\x45\x9e\xd4\xd0\x09\x33\x7b\x45\x64\x2e\xc9\xbb\x42\xc1\xec\x63\xdf\xa4\x67\xcd\xb2\x41\xb2\xfd\xb8\x39\x4f\x75\x07\x66\xda\xfa\x71\xcf\xe8\xd9\xc5\x52\x1c\xd4\x92\xbc\xc2\xb2\xdb\x26\x2d\x96\x5d\x2a\x27\x78\x5e\x96\x21\x3e\x20\x85\x67\x17\x95\x43\xb1\x6d\xab\xe2\x14\x2b\xd9\x1e\x40\x07\x8a\x23\x71\x45\x40\xbe\x26\x75\xbf\x2e\x0c\x61\xab\xfd\x31\x9d\x6c\x50\xb2\xab\x08\xb9\x33\x50\xc3\xb3\xfd\xfc\x7a\xa7\xaa\xae\x7c\xa0\xa3\xa8\x58\x12\x34\x4b\xaa\x37\xb8\xce\x22\x02\x0d\xde\xae\x0a\x47\x0b\x97\xe0\x77\x7b\xdf\xae\xe4\xfc\x0f\xf5\xe3\x2a\x9e\x1b\xa8\xbb\x47\x2f\xcc\x0e\x1f\x31\x50\xf5\x77\x6f\x27\x4a\x3d\xb7\xba\x36\x40\xe2\x66\xae\x48\xf1\x65\x77\xad\x47\x87\x83\x33\x83\xce\x9a\xa9\x24\x80\xa4\x4f\x8b\x61\xf1\x86\x7b\xd2\x18\xc5\xec\x27\x5f\x45\x89\x77\x07\x24\x70\x4d\x1a\x4d\x89\xd7\x29\x0e\xa9\xc9\x96\x09\x6c\x67\x25\x7d\xe4\x75\xa9\x0f\x84\x8a\xbc\x48\xc0\xd1\xb1\xc4\x5a\x20\xfc\xac\xe8\x38\x88\x40\x91\x77\x10\x44\x56\x46\x10\x3f\x2a\x85\x20\x3b\x15\x0c\x0b\xdd\xe1\x68\x74\x1c\x4f\x6a\xa3\xbb\x8a\x76\x5d\x4c\xe3\xf0\xb6\xe3\x11\xfd\xb3\xf3\xcb\x75\xf1\xd4\x6d\x3f\x3d\x3c\x2c\x7f\xbf\xd1\x0f\xc7\xfb\xf5\x52\xf4\xe6\x41\x7f\x3a\x3a\x3a\xfa\x6c\xf3\x30\xf4\xe2\xa4\x2e\x9e\x85\x28\x83\x24\xe8\x93\x93\x21\xbf\x17\x3f\x03\x70\xba\x70\xf3\xec\xa7\x09\x27\x40\x7e\xa5\x51\x45\x72\x64\x65\x56\xb9\xba\x77\x49\x75\x42\x05\x06\x25\x65\x69\xef\xf3\x24\xf2\x50\xf4\x26\xe9\x7c\x6f\x75\x35\xdf\x23\xf4\xf6\x3e\xc4\x53\x03\x61\x57\x65\x1e\x59\x49\x77\x64\x0f\x5a\x3a\x97\x45\xc9\x5c\x5f\xea\x6c\x4f\xce\xca\x9c\x46\xfd\x13\x9d\xcc\xca\xa4\x46\xd9\x98\x7e\x89\x2d\x6b\xdf\x2f\x4f\xb7\xee\xb8\x7f\x39\xb6\x64\x66\x60\xbd\x1e\x29\x42\xc9\x95\xc7\x67\xb4\x4b\xf4\x0c\xc1\x72\xf8\xb0\xb7\xb4\xcd\x72\x58\x9d\x8d\xa4\x66\x14\xa7\x4c\x45\xeb\xff\x66\xa9\x71\xb7\xca\xe0\x08\x5a\x6e\x7c\x92\x22\xf4\xd1\x36\x3b\xf2\x32\x9f\xd3\x83\x05\xec\xe9\xf7\x85\x97\xf2\xfe\xcd\x34\x4d\x52\x7a\x68\xa7\x21\x9d\xe4\xdc\xcd\xee\x5a\x82\xd1\x47\x29\x4e\x2c\x87\x5f\x8d\x92\xe9\x94\xd8\xf0\xd6\xf5\x19\x07\xa9\xa1\x59\xb4\x5f\x94\xc3\x36\x03\x18\x8c\xbb\xbd\xa9\x71\xdb\xf5\x73\x4d\x37\x75\xdc\x51\x74\xc6\x94\xc0\x2c\x60\xdd\xe8\x2a\xd2\x24\xc3\xf3\x23\x75\x43\x16\xc8\x2e\x98\x50\x60\xa0\x42\xa5\xa0\x16\x9f\xbc\x9f\xaf\xfa\xa3\x9e\x6b\x8f\x26\x9a\x34\x17\xa1\x8a\x1c\x99\x2f\x3e\xb6\xde\x4c\xe5\x0e\xb4\x48\xab\xd9\x91\xc1\x98\xee\x6b\x67\xa6\x73\x7c\xa7\xc4\x99\x91\xde\x04\x12\xb5\x08\x67\xd9\x43\x72\x0e\x9f\x82\xf4\x78\x31\x04\x8a\x2f\xbe\xc0\x5b\x9d\xca\xf6\x4a\x88\x71\x9d\x73\xab\xcb\x97\x0a\x4f\x39\x07\xce\x29\x0e\xf2\xae\x03\xf0\xe4\xf5\xfb\xfb\xea\xb4\xac\xfe\xcb\xf7\x76\x66\xde\xae\xc2\x94\x63\x07\x8a\x2b\x2c\x87\x04\xd0\x5a\x1e\x05\x32\x92\x74\x22\x35\xa3\xe3\x87\x25\x96\x4d\x3d\x76\xe1\x7a\xc2\x8b\xd9\x9c\x1a\x56\xd4\x1c\xdf\xa7\xe3\xb8\xaa\x35\x5b\x16\x04\x57\xb3\x5b\x8a\x66\xfa\x62\xb4\xc0\x63\x89\xa4\x8e\xf8\x7b\x0f\x15\xb1\x4d\x50\xa1\x21\x2a\x5f\x17\xf9\x7c\xe0\x54\x2f\x42\x26\xfa\x26\x35\xdd\xc8\xe6\x1a\xb0\xc2\xa4\x21\x24\xc2\x74\x0f\x49\xad\x92\x9b\xc2\x2d\xc0\x0d\xc9\xe4\x73\x44\x47\xed\xfb\x79\xb0\xba\x63\xf7\xd4\xa5\x7a\xb9\x85\x77\x3e\x0c\xa9\x10\xf7\xe2\x7a\x6a\x73\x64\xcc\x91\xe4\x0e\x4a\xd4\x58\x45\xe9\xa1\x03\x80\xdd\x05\x74\x42\x6f\x1e\x63\xba\xd0\x2f\xa1\x2b\x4a\x54\x22\x1f\xb5\xca\x61\xc1\x83\x1d\xef\x9c\x1e\x14\xc4\xff\x77\x2d\xbd\x58\xbb\x92\xb8\xef\xf6\xda\x20\xd9\x66\xe7\x22\xe6\xbd\xaa\x1d\x54\x2b\xbe\x5a\xbd\x76\xb8\xf3\x7e\x41\x3a\x31\xe9\x10\xc8\xa9\xc0\xb6\x09\xbb\x77\xa1\xdb\xde\x36\x6c\xe1\xdb\xbd\x75\x10\x3b\x17\x00\x46\xc7\xb6\xf8\x9a\x8e\xa2\xab\x47\xa7\x6d\xb0\x8f\x5b\x24\x15\xbd\xbc\x13\xbe\x3f\x38\xa1\x7f\xbe\xdc\xdc\x4d\xf2\x29\xe5\x1f\x14\x17\xfc\xa7\x79\x36\x7b\x6a\x90\xd5\xe8\x6a\x33\x4d\xaa\x77\xa5\x69\x1e\xc7\x14\x67\xa8\x99\xcf\xc3\x38\xf3\x87\x49\x10\xf2\x5f\x2b\x34\x2b\xc7\x49\x85\x27\xda\x79\x5c\xed\xcd\xa6\xcb\x37\x3e\xc8\x5d\x29\x98\x11\xff\x79\x42\x6b\xe2\xf2\xc9\xc8\x96\x98\xd1\xfd\x52\xc0\x89\x25\xa4\xd8\xac\xf4\x4a\x9a\x39\x37\xba\x45\xe3\x85\xe1\xb4\xcf\xcd\xce\x94\xe9\xd7\x97\xda\xd1\x0e\x16\x06\x6b\x6a\xf3\x37\x08\x0b\xe9\x45\x74\xf2\x49\xa7\xa2\x85\x14\xba\xcd\x76\x75\xbb\xcb\xed\xf7\x09\x3a\xfc\x94\xae\xad\x5a\xe9\x3c\xd7\x3c\x90\x7c\x99\xf3\x1e\x6c\xe4\x63\x94\x1c\x62\xa6\xfc\xab\x8f\x4b\x58\x6b\x8d\x46\x1e\xa7\x44\xa2\x18\xa7\x46\x23\xf3\xe6\x8a\xd2\x25\x65\x72\xce\xf7\x49\xbc\xc9\xe8\x61\xd6\x50\xfe\x92\xd9\x6b\x90\xf8\x8a\x1b\x48\xda\xde\x41\xf3\x49\xf3\xd8\x68\xd9\x3d\x0a\x3d\x06\x33\x67\x3a\xcb\xad\xfc\x45\x05\xdf\xac\x91\x99\x97\x88\xf0\xfa\x5d\xde\x11\x7d\x03\x26\x77\x00\x65\x3d\xdc\xbf\x3d\xe3\x15\x66\xbe\xe0\x70\xd7\xb3\x26\x6e\xc7\xea\x76\x77\x83\xfb\xc3\x00\xcc\xfd\xea\xf6\xbd\x39\x19\xa0\x82\x7f\x60\xf7\x94\xb0\x7e\x97\xdd\xcf\xfd\x62\xef\x28\x88\x36\xdb\x7f\x15\x1e\x3c\xa5\xe8\xda\x1a\x72\x83\x8c\x1b\x53\xa7\xfe\xdd\xa2\xd1\x1e\xd2\xbf\xe7\xcf\xea\x81\x6c\x74\xcc\xfa\x2c\x6d\x74\xed\x7a\x1c\x35\x86\xfd\x7a\x74\xdd\xe8\x3f\xaf\xa7\x79\xc3\x9e\xd6\xbf\xf5\x1a\x3f\x1b\xd7\xa5\x6a\x98\x4e\x7d\x95\x35\xce\xec\xfa\x2a\x6a\x8c\xfb\xf5\xcb\x79\xe3\xac\x57\xc7\xa4\xd6\x84\x2f\xd8\x48\xb6\x89\xe8\x1c\xaa\x45\xfd\xb7\xff\xf4\xf3\xdf\xfc\xfb\x9f\xfe\xe6\x57\xff\xf8\xe3\x9f\xff\x71\xfd\xb7\xbf\xfe\xfe\xbf\xfe\xfe\xcf\x8a\x97\x8e\xcc\x33\xe5\x2f\xea\xdd\xd4\x8b\x7f\xf8\x3b\x2f\x54\xf5\xa1\x44\x4d\x0f\x6e\x16\xa8\x7a\xdf\xcb\xae\x43\xf9\x1f\x7f\x93\xd7\xdf\xfe\xf5\xbb\x3f\x7a\xf7\xfd\xbb\xef\xdf\xfe\xeb\xdb\x5f\xbd\xfd\x75\xfd\xc7\xbf\xf8\xdb\x1f\xff\xf2\x1f\xfe\xf3\x17\x7f\x55\x37\xd5\xca\xfb\xe1\x97\x49\x54\x1f\x83\xa6\xe6\xf3\xfc\x87\x5f\x28\x90\x19\x71\x96\x7a\x2a\xa4\xc6\x48\x5d\x85\xf5\xb7\xbf\x7c\xf7\x27\x6f\xff\xed\xed\xbf\xbc\xfd\xe7\x77\x3f\xd7\x32\xea\x56\xe6\x45\x21\x51\x47\x4d\xbd\x02\x56\x03\x39\x01\x11\x41\xd4\x72\x57\x08\x68\x0c\x14\x85\x0a\x49\x54\xf1\xc2\x60\xa4\x18\x31\x83\xe1\xc2\xe3\x77\x0b\x83\x31\xe3\xc7\xc6\xe4\x85\xc1\xd8\xf1\x9f\x04\x19\x0c\x20\xb9\x5e\x6a\x30\x8a\x78\x8c\x23\x83\xa1\xa4\x3f\x61\xb8\x36\x18\x4f\xba\x7b\xcc\x0d\x06\x15\x8f\xdf\x7a\x06\x23\x4b\xb3\x28\x83\xe1\xc5\x23\xff\x1a\x0c\x33\xbd\x45\x06\x63\x4d\x7f\x51\x34\x37\x18\x70\xaa\x45\x32\x68\x36\xa1\x08\x06\xaf\x3b\x1f\xbd\x70\xbb\x60\xab\xe0\x6e\x67\xb6\xbe\x6e\xdd\xc4\x80\xff\x0e\x00\x00\xff\xff\x0f\x76\x47\x0b\xbc\x25\x00\x00") func confAppIniBytes() ([]byte, error) { return bindataRead( @@ -299,7 +299,7 @@ func confAppIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/app.ini", size: 9594, mode: os.FileMode(420), modTime: time.Unix(1446399557, 0)} + info := bindataFileInfo{name: "conf/app.ini", size: 9660, mode: os.FileMode(420), modTime: time.Unix(1447018851, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4319,7 +4319,7 @@ func confLocaleTranslators() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/TRANSLATORS", size: 1298, mode: os.FileMode(420), modTime: time.Unix(1446433092, 0)} + info := bindataFileInfo{name: "conf/locale/TRANSLATORS", size: 1298, mode: os.FileMode(420), modTime: time.Unix(1447011494, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4339,7 +4339,7 @@ func confLocaleLocale_bgBgIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_bg-BG.ini", size: 69171, mode: os.FileMode(493), modTime: time.Unix(1446892612, 0)} + info := bindataFileInfo{name: "conf/locale/locale_bg-BG.ini", size: 69171, mode: os.FileMode(493), modTime: time.Unix(1447011494, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4359,7 +4359,7 @@ func confLocaleLocale_deDeIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_de-DE.ini", size: 48284, mode: os.FileMode(493), modTime: time.Unix(1446892612, 0)} + info := bindataFileInfo{name: "conf/locale/locale_de-DE.ini", size: 48284, mode: os.FileMode(493), modTime: time.Unix(1447011494, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4379,7 +4379,7 @@ func confLocaleLocale_enUsIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_en-US.ini", size: 44670, mode: os.FileMode(420), modTime: time.Unix(1446593396, 0)} + info := bindataFileInfo{name: "conf/locale/locale_en-US.ini", size: 44670, mode: os.FileMode(420), modTime: time.Unix(1447011494, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4399,7 +4399,7 @@ func confLocaleLocale_esEsIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_es-ES.ini", size: 49309, mode: os.FileMode(493), modTime: time.Unix(1446892610, 0)} + info := bindataFileInfo{name: "conf/locale/locale_es-ES.ini", size: 49309, mode: os.FileMode(493), modTime: time.Unix(1447011494, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4419,7 +4419,7 @@ func confLocaleLocale_frFrIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_fr-FR.ini", size: 49356, mode: os.FileMode(493), modTime: time.Unix(1446892610, 0)} + info := bindataFileInfo{name: "conf/locale/locale_fr-FR.ini", size: 49356, mode: os.FileMode(493), modTime: time.Unix(1447011494, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4439,7 +4439,7 @@ func confLocaleLocale_itItIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_it-IT.ini", size: 46771, mode: os.FileMode(493), modTime: time.Unix(1446892612, 0)} + info := bindataFileInfo{name: "conf/locale/locale_it-IT.ini", size: 46771, mode: os.FileMode(493), modTime: time.Unix(1447011494, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4459,7 +4459,7 @@ func confLocaleLocale_jaJpIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_ja-JP.ini", size: 54372, mode: os.FileMode(493), modTime: time.Unix(1446892612, 0)} + info := bindataFileInfo{name: "conf/locale/locale_ja-JP.ini", size: 54372, mode: os.FileMode(493), modTime: time.Unix(1447011494, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4479,7 +4479,7 @@ func confLocaleLocale_lvLvIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_lv-LV.ini", size: 49613, mode: os.FileMode(493), modTime: time.Unix(1446892618, 0)} + info := bindataFileInfo{name: "conf/locale/locale_lv-LV.ini", size: 49613, mode: os.FileMode(493), modTime: time.Unix(1447011494, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4499,7 +4499,7 @@ func confLocaleLocale_nlNlIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_nl-NL.ini", size: 46178, mode: os.FileMode(493), modTime: time.Unix(1446892612, 0)} + info := bindataFileInfo{name: "conf/locale/locale_nl-NL.ini", size: 46178, mode: os.FileMode(493), modTime: time.Unix(1447011494, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4519,7 +4519,7 @@ func confLocaleLocale_plPlIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_pl-PL.ini", size: 46875, mode: os.FileMode(493), modTime: time.Unix(1446892612, 0)} + info := bindataFileInfo{name: "conf/locale/locale_pl-PL.ini", size: 46875, mode: os.FileMode(493), modTime: time.Unix(1447011494, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4539,7 +4539,7 @@ func confLocaleLocale_ptBrIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_pt-BR.ini", size: 47920, mode: os.FileMode(493), modTime: time.Unix(1446892616, 0)} + info := bindataFileInfo{name: "conf/locale/locale_pt-BR.ini", size: 47920, mode: os.FileMode(493), modTime: time.Unix(1447011494, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4559,7 +4559,7 @@ func confLocaleLocale_ruRuIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_ru-RU.ini", size: 69779, mode: os.FileMode(493), modTime: time.Unix(1446892614, 0)} + info := bindataFileInfo{name: "conf/locale/locale_ru-RU.ini", size: 69779, mode: os.FileMode(493), modTime: time.Unix(1447011494, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4579,7 +4579,7 @@ func confLocaleLocale_zhCnIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_zh-CN.ini", size: 43800, mode: os.FileMode(493), modTime: time.Unix(1446892614, 0)} + info := bindataFileInfo{name: "conf/locale/locale_zh-CN.ini", size: 43800, mode: os.FileMode(493), modTime: time.Unix(1447011494, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4599,7 +4599,7 @@ func confLocaleLocale_zhHkIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_zh-HK.ini", size: 44166, mode: os.FileMode(493), modTime: time.Unix(1446892614, 0)} + info := bindataFileInfo{name: "conf/locale/locale_zh-HK.ini", size: 44166, mode: os.FileMode(493), modTime: time.Unix(1447011494, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/modules/crypto/ssh/agent/client.go b/modules/crypto/ssh/agent/client.go deleted file mode 100755 index 99e30990..00000000 --- a/modules/crypto/ssh/agent/client.go +++ /dev/null @@ -1,615 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* - Package agent implements a client to an ssh-agent daemon. - -References: - [PROTOCOL.agent]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent?rev=HEAD -*/ -package agent - -import ( - "bytes" - "crypto/dsa" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rsa" - "encoding/base64" - "encoding/binary" - "errors" - "fmt" - "io" - "math/big" - "sync" - - "github.com/gogits/gogs/modules/crypto/ssh" -) - -// Agent represents the capabilities of an ssh-agent. -type Agent interface { - // List returns the identities known to the agent. - List() ([]*Key, error) - - // Sign has the agent sign the data using a protocol 2 key as defined - // in [PROTOCOL.agent] section 2.6.2. - Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) - - // Add adds a private key to the agent. - Add(key AddedKey) error - - // Remove removes all identities with the given public key. - Remove(key ssh.PublicKey) error - - // RemoveAll removes all identities. - RemoveAll() error - - // Lock locks the agent. Sign and Remove will fail, and List will empty an empty list. - Lock(passphrase []byte) error - - // Unlock undoes the effect of Lock - Unlock(passphrase []byte) error - - // Signers returns signers for all the known keys. - Signers() ([]ssh.Signer, error) -} - -// AddedKey describes an SSH key to be added to an Agent. -type AddedKey struct { - // PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey or - // *ecdsa.PrivateKey, which will be inserted into the agent. - PrivateKey interface{} - // Certificate, if not nil, is communicated to the agent and will be - // stored with the key. - Certificate *ssh.Certificate - // Comment is an optional, free-form string. - Comment string - // LifetimeSecs, if not zero, is the number of seconds that the - // agent will store the key for. - LifetimeSecs uint32 - // ConfirmBeforeUse, if true, requests that the agent confirm with the - // user before each use of this key. - ConfirmBeforeUse bool -} - -// See [PROTOCOL.agent], section 3. -const ( - agentRequestV1Identities = 1 - - // 3.2 Requests from client to agent for protocol 2 key operations - agentAddIdentity = 17 - agentRemoveIdentity = 18 - agentRemoveAllIdentities = 19 - agentAddIdConstrained = 25 - - // 3.3 Key-type independent requests from client to agent - agentAddSmartcardKey = 20 - agentRemoveSmartcardKey = 21 - agentLock = 22 - agentUnlock = 23 - agentAddSmartcardKeyConstrained = 26 - - // 3.7 Key constraint identifiers - agentConstrainLifetime = 1 - agentConstrainConfirm = 2 -) - -// maxAgentResponseBytes is the maximum agent reply size that is accepted. This -// is a sanity check, not a limit in the spec. -const maxAgentResponseBytes = 16 << 20 - -// Agent messages: -// These structures mirror the wire format of the corresponding ssh agent -// messages found in [PROTOCOL.agent]. - -// 3.4 Generic replies from agent to client -const agentFailure = 5 - -type failureAgentMsg struct{} - -const agentSuccess = 6 - -type successAgentMsg struct{} - -// See [PROTOCOL.agent], section 2.5.2. -const agentRequestIdentities = 11 - -type requestIdentitiesAgentMsg struct{} - -// See [PROTOCOL.agent], section 2.5.2. -const agentIdentitiesAnswer = 12 - -type identitiesAnswerAgentMsg struct { - NumKeys uint32 `sshtype:"12"` - Keys []byte `ssh:"rest"` -} - -// See [PROTOCOL.agent], section 2.6.2. -const agentSignRequest = 13 - -type signRequestAgentMsg struct { - KeyBlob []byte `sshtype:"13"` - Data []byte - Flags uint32 -} - -// See [PROTOCOL.agent], section 2.6.2. - -// 3.6 Replies from agent to client for protocol 2 key operations -const agentSignResponse = 14 - -type signResponseAgentMsg struct { - SigBlob []byte `sshtype:"14"` -} - -type publicKey struct { - Format string - Rest []byte `ssh:"rest"` -} - -// Key represents a protocol 2 public key as defined in -// [PROTOCOL.agent], section 2.5.2. -type Key struct { - Format string - Blob []byte - Comment string -} - -func clientErr(err error) error { - return fmt.Errorf("agent: client error: %v", err) -} - -// String returns the storage form of an agent key with the format, base64 -// encoded serialized key, and the comment if it is not empty. -func (k *Key) String() string { - s := string(k.Format) + " " + base64.StdEncoding.EncodeToString(k.Blob) - - if k.Comment != "" { - s += " " + k.Comment - } - - return s -} - -// Type returns the public key type. -func (k *Key) Type() string { - return k.Format -} - -// Marshal returns key blob to satisfy the ssh.PublicKey interface. -func (k *Key) Marshal() []byte { - return k.Blob -} - -// Verify satisfies the ssh.PublicKey interface, but is not -// implemented for agent keys. -func (k *Key) Verify(data []byte, sig *ssh.Signature) error { - return errors.New("agent: agent key does not know how to verify") -} - -type wireKey struct { - Format string - Rest []byte `ssh:"rest"` -} - -func parseKey(in []byte) (out *Key, rest []byte, err error) { - var record struct { - Blob []byte - Comment string - Rest []byte `ssh:"rest"` - } - - if err := ssh.Unmarshal(in, &record); err != nil { - return nil, nil, err - } - - var wk wireKey - if err := ssh.Unmarshal(record.Blob, &wk); err != nil { - return nil, nil, err - } - - return &Key{ - Format: wk.Format, - Blob: record.Blob, - Comment: record.Comment, - }, record.Rest, nil -} - -// client is a client for an ssh-agent process. -type client struct { - // conn is typically a *net.UnixConn - conn io.ReadWriter - // mu is used to prevent concurrent access to the agent - mu sync.Mutex -} - -// NewClient returns an Agent that talks to an ssh-agent process over -// the given connection. -func NewClient(rw io.ReadWriter) Agent { - return &client{conn: rw} -} - -// call sends an RPC to the agent. On success, the reply is -// unmarshaled into reply and replyType is set to the first byte of -// the reply, which contains the type of the message. -func (c *client) call(req []byte) (reply interface{}, err error) { - c.mu.Lock() - defer c.mu.Unlock() - - msg := make([]byte, 4+len(req)) - binary.BigEndian.PutUint32(msg, uint32(len(req))) - copy(msg[4:], req) - if _, err = c.conn.Write(msg); err != nil { - return nil, clientErr(err) - } - - var respSizeBuf [4]byte - if _, err = io.ReadFull(c.conn, respSizeBuf[:]); err != nil { - return nil, clientErr(err) - } - respSize := binary.BigEndian.Uint32(respSizeBuf[:]) - if respSize > maxAgentResponseBytes { - return nil, clientErr(err) - } - - buf := make([]byte, respSize) - if _, err = io.ReadFull(c.conn, buf); err != nil { - return nil, clientErr(err) - } - reply, err = unmarshal(buf) - if err != nil { - return nil, clientErr(err) - } - return reply, err -} - -func (c *client) simpleCall(req []byte) error { - resp, err := c.call(req) - if err != nil { - return err - } - if _, ok := resp.(*successAgentMsg); ok { - return nil - } - return errors.New("agent: failure") -} - -func (c *client) RemoveAll() error { - return c.simpleCall([]byte{agentRemoveAllIdentities}) -} - -func (c *client) Remove(key ssh.PublicKey) error { - req := ssh.Marshal(&agentRemoveIdentityMsg{ - KeyBlob: key.Marshal(), - }) - return c.simpleCall(req) -} - -func (c *client) Lock(passphrase []byte) error { - req := ssh.Marshal(&agentLockMsg{ - Passphrase: passphrase, - }) - return c.simpleCall(req) -} - -func (c *client) Unlock(passphrase []byte) error { - req := ssh.Marshal(&agentUnlockMsg{ - Passphrase: passphrase, - }) - return c.simpleCall(req) -} - -// List returns the identities known to the agent. -func (c *client) List() ([]*Key, error) { - // see [PROTOCOL.agent] section 2.5.2. - req := []byte{agentRequestIdentities} - - msg, err := c.call(req) - if err != nil { - return nil, err - } - - switch msg := msg.(type) { - case *identitiesAnswerAgentMsg: - if msg.NumKeys > maxAgentResponseBytes/8 { - return nil, errors.New("agent: too many keys in agent reply") - } - keys := make([]*Key, msg.NumKeys) - data := msg.Keys - for i := uint32(0); i < msg.NumKeys; i++ { - var key *Key - var err error - if key, data, err = parseKey(data); err != nil { - return nil, err - } - keys[i] = key - } - return keys, nil - case *failureAgentMsg: - return nil, errors.New("agent: failed to list keys") - } - panic("unreachable") -} - -// Sign has the agent sign the data using a protocol 2 key as defined -// in [PROTOCOL.agent] section 2.6.2. -func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { - req := ssh.Marshal(signRequestAgentMsg{ - KeyBlob: key.Marshal(), - Data: data, - }) - - msg, err := c.call(req) - if err != nil { - return nil, err - } - - switch msg := msg.(type) { - case *signResponseAgentMsg: - var sig ssh.Signature - if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil { - return nil, err - } - - return &sig, nil - case *failureAgentMsg: - return nil, errors.New("agent: failed to sign challenge") - } - panic("unreachable") -} - -// unmarshal parses an agent message in packet, returning the parsed -// form and the message type of packet. -func unmarshal(packet []byte) (interface{}, error) { - if len(packet) < 1 { - return nil, errors.New("agent: empty packet") - } - var msg interface{} - switch packet[0] { - case agentFailure: - return new(failureAgentMsg), nil - case agentSuccess: - return new(successAgentMsg), nil - case agentIdentitiesAnswer: - msg = new(identitiesAnswerAgentMsg) - case agentSignResponse: - msg = new(signResponseAgentMsg) - default: - return nil, fmt.Errorf("agent: unknown type tag %d", packet[0]) - } - if err := ssh.Unmarshal(packet, msg); err != nil { - return nil, err - } - return msg, nil -} - -type rsaKeyMsg struct { - Type string `sshtype:"17"` - N *big.Int - E *big.Int - D *big.Int - Iqmp *big.Int // IQMP = Inverse Q Mod P - P *big.Int - Q *big.Int - Comments string - Constraints []byte `ssh:"rest"` -} - -type dsaKeyMsg struct { - Type string `sshtype:"17"` - P *big.Int - Q *big.Int - G *big.Int - Y *big.Int - X *big.Int - Comments string - Constraints []byte `ssh:"rest"` -} - -type ecdsaKeyMsg struct { - Type string `sshtype:"17"` - Curve string - KeyBytes []byte - D *big.Int - Comments string - Constraints []byte `ssh:"rest"` -} - -// Insert adds a private key to the agent. -func (c *client) insertKey(s interface{}, comment string, constraints []byte) error { - var req []byte - switch k := s.(type) { - case *rsa.PrivateKey: - if len(k.Primes) != 2 { - return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes)) - } - k.Precompute() - req = ssh.Marshal(rsaKeyMsg{ - Type: ssh.KeyAlgoRSA, - N: k.N, - E: big.NewInt(int64(k.E)), - D: k.D, - Iqmp: k.Precomputed.Qinv, - P: k.Primes[0], - Q: k.Primes[1], - Comments: comment, - Constraints: constraints, - }) - case *dsa.PrivateKey: - req = ssh.Marshal(dsaKeyMsg{ - Type: ssh.KeyAlgoDSA, - P: k.P, - Q: k.Q, - G: k.G, - Y: k.Y, - X: k.X, - Comments: comment, - Constraints: constraints, - }) - case *ecdsa.PrivateKey: - nistID := fmt.Sprintf("nistp%d", k.Params().BitSize) - req = ssh.Marshal(ecdsaKeyMsg{ - Type: "ecdsa-sha2-" + nistID, - Curve: nistID, - KeyBytes: elliptic.Marshal(k.Curve, k.X, k.Y), - D: k.D, - Comments: comment, - Constraints: constraints, - }) - default: - return fmt.Errorf("agent: unsupported key type %T", s) - } - - // if constraints are present then the message type needs to be changed. - if len(constraints) != 0 { - req[0] = agentAddIdConstrained - } - - resp, err := c.call(req) - if err != nil { - return err - } - if _, ok := resp.(*successAgentMsg); ok { - return nil - } - return errors.New("agent: failure") -} - -type rsaCertMsg struct { - Type string `sshtype:"17"` - CertBytes []byte - D *big.Int - Iqmp *big.Int // IQMP = Inverse Q Mod P - P *big.Int - Q *big.Int - Comments string - Constraints []byte `ssh:"rest"` -} - -type dsaCertMsg struct { - Type string `sshtype:"17"` - CertBytes []byte - X *big.Int - Comments string - Constraints []byte `ssh:"rest"` -} - -type ecdsaCertMsg struct { - Type string `sshtype:"17"` - CertBytes []byte - D *big.Int - Comments string - Constraints []byte `ssh:"rest"` -} - -// Insert adds a private key to the agent. If a certificate is given, -// that certificate is added instead as public key. -func (c *client) Add(key AddedKey) error { - var constraints []byte - - if secs := key.LifetimeSecs; secs != 0 { - constraints = append(constraints, agentConstrainLifetime) - - var secsBytes [4]byte - binary.BigEndian.PutUint32(secsBytes[:], secs) - constraints = append(constraints, secsBytes[:]...) - } - - if key.ConfirmBeforeUse { - constraints = append(constraints, agentConstrainConfirm) - } - - if cert := key.Certificate; cert == nil { - return c.insertKey(key.PrivateKey, key.Comment, constraints) - } else { - return c.insertCert(key.PrivateKey, cert, key.Comment, constraints) - } -} - -func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error { - var req []byte - switch k := s.(type) { - case *rsa.PrivateKey: - if len(k.Primes) != 2 { - return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes)) - } - k.Precompute() - req = ssh.Marshal(rsaCertMsg{ - Type: cert.Type(), - CertBytes: cert.Marshal(), - D: k.D, - Iqmp: k.Precomputed.Qinv, - P: k.Primes[0], - Q: k.Primes[1], - Comments: comment, - Constraints: constraints, - }) - case *dsa.PrivateKey: - req = ssh.Marshal(dsaCertMsg{ - Type: cert.Type(), - CertBytes: cert.Marshal(), - X: k.X, - Comments: comment, - }) - case *ecdsa.PrivateKey: - req = ssh.Marshal(ecdsaCertMsg{ - Type: cert.Type(), - CertBytes: cert.Marshal(), - D: k.D, - Comments: comment, - }) - default: - return fmt.Errorf("agent: unsupported key type %T", s) - } - - // if constraints are present then the message type needs to be changed. - if len(constraints) != 0 { - req[0] = agentAddIdConstrained - } - - signer, err := ssh.NewSignerFromKey(s) - if err != nil { - return err - } - if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { - return errors.New("agent: signer and cert have different public key") - } - - resp, err := c.call(req) - if err != nil { - return err - } - if _, ok := resp.(*successAgentMsg); ok { - return nil - } - return errors.New("agent: failure") -} - -// Signers provides a callback for client authentication. -func (c *client) Signers() ([]ssh.Signer, error) { - keys, err := c.List() - if err != nil { - return nil, err - } - - var result []ssh.Signer - for _, k := range keys { - result = append(result, &agentKeyringSigner{c, k}) - } - return result, nil -} - -type agentKeyringSigner struct { - agent *client - pub ssh.PublicKey -} - -func (s *agentKeyringSigner) PublicKey() ssh.PublicKey { - return s.pub -} - -func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) { - // The agent has its own entropy source, so the rand argument is ignored. - return s.agent.Sign(s.pub, data) -} diff --git a/modules/crypto/ssh/agent/client_test.go b/modules/crypto/ssh/agent/client_test.go deleted file mode 100755 index 82b63515..00000000 --- a/modules/crypto/ssh/agent/client_test.go +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package agent - -import ( - "bytes" - "crypto/rand" - "errors" - "net" - "os" - "os/exec" - "path/filepath" - "strconv" - "testing" - - "github.com/gogits/gogs/modules/crypto/ssh" -) - -// startAgent executes ssh-agent, and returns a Agent interface to it. -func startAgent(t *testing.T) (client Agent, socket string, cleanup func()) { - if testing.Short() { - // ssh-agent is not always available, and the key - // types supported vary by platform. - t.Skip("skipping test due to -short") - } - - bin, err := exec.LookPath("ssh-agent") - if err != nil { - t.Skip("could not find ssh-agent") - } - - cmd := exec.Command(bin, "-s") - out, err := cmd.Output() - if err != nil { - t.Fatalf("cmd.Output: %v", err) - } - - /* Output looks like: - - SSH_AUTH_SOCK=/tmp/ssh-P65gpcqArqvH/agent.15541; export SSH_AUTH_SOCK; - SSH_AGENT_PID=15542; export SSH_AGENT_PID; - echo Agent pid 15542; - */ - fields := bytes.Split(out, []byte(";")) - line := bytes.SplitN(fields[0], []byte("="), 2) - line[0] = bytes.TrimLeft(line[0], "\n") - if string(line[0]) != "SSH_AUTH_SOCK" { - t.Fatalf("could not find key SSH_AUTH_SOCK in %q", fields[0]) - } - socket = string(line[1]) - - line = bytes.SplitN(fields[2], []byte("="), 2) - line[0] = bytes.TrimLeft(line[0], "\n") - if string(line[0]) != "SSH_AGENT_PID" { - t.Fatalf("could not find key SSH_AGENT_PID in %q", fields[2]) - } - pidStr := line[1] - pid, err := strconv.Atoi(string(pidStr)) - if err != nil { - t.Fatalf("Atoi(%q): %v", pidStr, err) - } - - conn, err := net.Dial("unix", string(socket)) - if err != nil { - t.Fatalf("net.Dial: %v", err) - } - - ac := NewClient(conn) - return ac, socket, func() { - proc, _ := os.FindProcess(pid) - if proc != nil { - proc.Kill() - } - conn.Close() - os.RemoveAll(filepath.Dir(socket)) - } -} - -func testAgent(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) { - agent, _, cleanup := startAgent(t) - defer cleanup() - - testAgentInterface(t, agent, key, cert, lifetimeSecs) -} - -func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) { - signer, err := ssh.NewSignerFromKey(key) - if err != nil { - t.Fatalf("NewSignerFromKey(%T): %v", key, err) - } - // The agent should start up empty. - if keys, err := agent.List(); err != nil { - t.Fatalf("RequestIdentities: %v", err) - } else if len(keys) > 0 { - t.Fatalf("got %d keys, want 0: %v", len(keys), keys) - } - - // Attempt to insert the key, with certificate if specified. - var pubKey ssh.PublicKey - if cert != nil { - err = agent.Add(AddedKey{ - PrivateKey: key, - Certificate: cert, - Comment: "comment", - LifetimeSecs: lifetimeSecs, - }) - pubKey = cert - } else { - err = agent.Add(AddedKey{PrivateKey: key, Comment: "comment", LifetimeSecs: lifetimeSecs}) - pubKey = signer.PublicKey() - } - if err != nil { - t.Fatalf("insert(%T): %v", key, err) - } - - // Did the key get inserted successfully? - if keys, err := agent.List(); err != nil { - t.Fatalf("List: %v", err) - } else if len(keys) != 1 { - t.Fatalf("got %v, want 1 key", keys) - } else if keys[0].Comment != "comment" { - t.Fatalf("key comment: got %v, want %v", keys[0].Comment, "comment") - } else if !bytes.Equal(keys[0].Blob, pubKey.Marshal()) { - t.Fatalf("key mismatch") - } - - // Can the agent make a valid signature? - data := []byte("hello") - sig, err := agent.Sign(pubKey, data) - if err != nil { - t.Fatalf("Sign(%s): %v", pubKey.Type(), err) - } - - if err := pubKey.Verify(data, sig); err != nil { - t.Fatalf("Verify(%s): %v", pubKey.Type(), err) - } -} - -func TestAgent(t *testing.T) { - for _, keyType := range []string{"rsa", "dsa", "ecdsa"} { - testAgent(t, testPrivateKeys[keyType], nil, 0) - } -} - -func TestCert(t *testing.T) { - cert := &ssh.Certificate{ - Key: testPublicKeys["rsa"], - ValidBefore: ssh.CertTimeInfinity, - CertType: ssh.UserCert, - } - cert.SignCert(rand.Reader, testSigners["ecdsa"]) - - testAgent(t, testPrivateKeys["rsa"], cert, 0) -} - -func TestConstraints(t *testing.T) { - testAgent(t, testPrivateKeys["rsa"], nil, 3600 /* lifetime in seconds */) -} - -// netPipe is analogous to net.Pipe, but it uses a real net.Conn, and -// therefore is buffered (net.Pipe deadlocks if both sides start with -// a write.) -func netPipe() (net.Conn, net.Conn, error) { - listener, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - return nil, nil, err - } - defer listener.Close() - c1, err := net.Dial("tcp", listener.Addr().String()) - if err != nil { - return nil, nil, err - } - - c2, err := listener.Accept() - if err != nil { - c1.Close() - return nil, nil, err - } - - return c1, c2, nil -} - -func TestAuth(t *testing.T) { - a, b, err := netPipe() - if err != nil { - t.Fatalf("netPipe: %v", err) - } - - defer a.Close() - defer b.Close() - - agent, _, cleanup := startAgent(t) - defer cleanup() - - if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment"}); err != nil { - t.Errorf("Add: %v", err) - } - - serverConf := ssh.ServerConfig{} - serverConf.AddHostKey(testSigners["rsa"]) - serverConf.PublicKeyCallback = func(c ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { - if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) { - return nil, nil - } - - return nil, errors.New("pubkey rejected") - } - - go func() { - conn, _, _, err := ssh.NewServerConn(a, &serverConf) - if err != nil { - t.Fatalf("Server: %v", err) - } - conn.Close() - }() - - conf := ssh.ClientConfig{} - conf.Auth = append(conf.Auth, ssh.PublicKeysCallback(agent.Signers)) - conn, _, _, err := ssh.NewClientConn(b, "", &conf) - if err != nil { - t.Fatalf("NewClientConn: %v", err) - } - conn.Close() -} - -func TestLockClient(t *testing.T) { - agent, _, cleanup := startAgent(t) - defer cleanup() - testLockAgent(agent, t) -} - -func testLockAgent(agent Agent, t *testing.T) { - if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment 1"}); err != nil { - t.Errorf("Add: %v", err) - } - if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["dsa"], Comment: "comment dsa"}); err != nil { - t.Errorf("Add: %v", err) - } - if keys, err := agent.List(); err != nil { - t.Errorf("List: %v", err) - } else if len(keys) != 2 { - t.Errorf("Want 2 keys, got %v", keys) - } - - passphrase := []byte("secret") - if err := agent.Lock(passphrase); err != nil { - t.Errorf("Lock: %v", err) - } - - if keys, err := agent.List(); err != nil { - t.Errorf("List: %v", err) - } else if len(keys) != 0 { - t.Errorf("Want 0 keys, got %v", keys) - } - - signer, _ := ssh.NewSignerFromKey(testPrivateKeys["rsa"]) - if _, err := agent.Sign(signer.PublicKey(), []byte("hello")); err == nil { - t.Fatalf("Sign did not fail") - } - - if err := agent.Remove(signer.PublicKey()); err == nil { - t.Fatalf("Remove did not fail") - } - - if err := agent.RemoveAll(); err == nil { - t.Fatalf("RemoveAll did not fail") - } - - if err := agent.Unlock(nil); err == nil { - t.Errorf("Unlock with wrong passphrase succeeded") - } - if err := agent.Unlock(passphrase); err != nil { - t.Errorf("Unlock: %v", err) - } - - if err := agent.Remove(signer.PublicKey()); err != nil { - t.Fatalf("Remove: %v", err) - } - - if keys, err := agent.List(); err != nil { - t.Errorf("List: %v", err) - } else if len(keys) != 1 { - t.Errorf("Want 1 keys, got %v", keys) - } -} diff --git a/modules/crypto/ssh/agent/forward.go b/modules/crypto/ssh/agent/forward.go deleted file mode 100755 index 8b54acb0..00000000 --- a/modules/crypto/ssh/agent/forward.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package agent - -import ( - "errors" - "io" - "net" - "sync" - - "github.com/gogits/gogs/modules/crypto/ssh" -) - -// RequestAgentForwarding sets up agent forwarding for the session. -// ForwardToAgent or ForwardToRemote should be called to route -// the authentication requests. -func RequestAgentForwarding(session *ssh.Session) error { - ok, err := session.SendRequest("auth-agent-req@openssh.com", true, nil) - if err != nil { - return err - } - if !ok { - return errors.New("forwarding request denied") - } - return nil -} - -// ForwardToAgent routes authentication requests to the given keyring. -func ForwardToAgent(client *ssh.Client, keyring Agent) error { - channels := client.HandleChannelOpen(channelType) - if channels == nil { - return errors.New("agent: already have handler for " + channelType) - } - - go func() { - for ch := range channels { - channel, reqs, err := ch.Accept() - if err != nil { - continue - } - go ssh.DiscardRequests(reqs) - go func() { - ServeAgent(keyring, channel) - channel.Close() - }() - } - }() - return nil -} - -const channelType = "auth-agent@openssh.com" - -// ForwardToRemote routes authentication requests to the ssh-agent -// process serving on the given unix socket. -func ForwardToRemote(client *ssh.Client, addr string) error { - channels := client.HandleChannelOpen(channelType) - if channels == nil { - return errors.New("agent: already have handler for " + channelType) - } - conn, err := net.Dial("unix", addr) - if err != nil { - return err - } - conn.Close() - - go func() { - for ch := range channels { - channel, reqs, err := ch.Accept() - if err != nil { - continue - } - go ssh.DiscardRequests(reqs) - go forwardUnixSocket(channel, addr) - } - }() - return nil -} - -func forwardUnixSocket(channel ssh.Channel, addr string) { - conn, err := net.Dial("unix", addr) - if err != nil { - return - } - - var wg sync.WaitGroup - wg.Add(2) - go func() { - io.Copy(conn, channel) - conn.(*net.UnixConn).CloseWrite() - wg.Done() - }() - go func() { - io.Copy(channel, conn) - channel.CloseWrite() - wg.Done() - }() - - wg.Wait() - conn.Close() - channel.Close() -} diff --git a/modules/crypto/ssh/agent/keyring.go b/modules/crypto/ssh/agent/keyring.go deleted file mode 100755 index e27c2c94..00000000 --- a/modules/crypto/ssh/agent/keyring.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package agent - -import ( - "bytes" - "crypto/rand" - "crypto/subtle" - "errors" - "fmt" - "sync" - - "github.com/gogits/gogs/modules/crypto/ssh" -) - -type privKey struct { - signer ssh.Signer - comment string -} - -type keyring struct { - mu sync.Mutex - keys []privKey - - locked bool - passphrase []byte -} - -var errLocked = errors.New("agent: locked") - -// NewKeyring returns an Agent that holds keys in memory. It is safe -// for concurrent use by multiple goroutines. -func NewKeyring() Agent { - return &keyring{} -} - -// RemoveAll removes all identities. -func (r *keyring) RemoveAll() error { - r.mu.Lock() - defer r.mu.Unlock() - if r.locked { - return errLocked - } - - r.keys = nil - return nil -} - -// Remove removes all identities with the given public key. -func (r *keyring) Remove(key ssh.PublicKey) error { - r.mu.Lock() - defer r.mu.Unlock() - if r.locked { - return errLocked - } - - want := key.Marshal() - found := false - for i := 0; i < len(r.keys); { - if bytes.Equal(r.keys[i].signer.PublicKey().Marshal(), want) { - found = true - r.keys[i] = r.keys[len(r.keys)-1] - r.keys = r.keys[len(r.keys)-1:] - continue - } else { - i++ - } - } - - if !found { - return errors.New("agent: key not found") - } - return nil -} - -// Lock locks the agent. Sign and Remove will fail, and List will empty an empty list. -func (r *keyring) Lock(passphrase []byte) error { - r.mu.Lock() - defer r.mu.Unlock() - if r.locked { - return errLocked - } - - r.locked = true - r.passphrase = passphrase - return nil -} - -// Unlock undoes the effect of Lock -func (r *keyring) Unlock(passphrase []byte) error { - r.mu.Lock() - defer r.mu.Unlock() - if !r.locked { - return errors.New("agent: not locked") - } - if len(passphrase) != len(r.passphrase) || 1 != subtle.ConstantTimeCompare(passphrase, r.passphrase) { - return fmt.Errorf("agent: incorrect passphrase") - } - - r.locked = false - r.passphrase = nil - return nil -} - -// List returns the identities known to the agent. -func (r *keyring) List() ([]*Key, error) { - r.mu.Lock() - defer r.mu.Unlock() - if r.locked { - // section 2.7: locked agents return empty. - return nil, nil - } - - var ids []*Key - for _, k := range r.keys { - pub := k.signer.PublicKey() - ids = append(ids, &Key{ - Format: pub.Type(), - Blob: pub.Marshal(), - Comment: k.comment}) - } - return ids, nil -} - -// Insert adds a private key to the keyring. If a certificate -// is given, that certificate is added as public key. Note that -// any constraints given are ignored. -func (r *keyring) Add(key AddedKey) error { - r.mu.Lock() - defer r.mu.Unlock() - if r.locked { - return errLocked - } - signer, err := ssh.NewSignerFromKey(key.PrivateKey) - - if err != nil { - return err - } - - if cert := key.Certificate; cert != nil { - signer, err = ssh.NewCertSigner(cert, signer) - if err != nil { - return err - } - } - - r.keys = append(r.keys, privKey{signer, key.Comment}) - - return nil -} - -// Sign returns a signature for the data. -func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { - r.mu.Lock() - defer r.mu.Unlock() - if r.locked { - return nil, errLocked - } - - wanted := key.Marshal() - for _, k := range r.keys { - if bytes.Equal(k.signer.PublicKey().Marshal(), wanted) { - return k.signer.Sign(rand.Reader, data) - } - } - return nil, errors.New("not found") -} - -// Signers returns signers for all the known keys. -func (r *keyring) Signers() ([]ssh.Signer, error) { - r.mu.Lock() - defer r.mu.Unlock() - if r.locked { - return nil, errLocked - } - - s := make([]ssh.Signer, 0, len(r.keys)) - for _, k := range r.keys { - s = append(s, k.signer) - } - return s, nil -} diff --git a/modules/crypto/ssh/agent/server.go b/modules/crypto/ssh/agent/server.go deleted file mode 100755 index 71ec3bc0..00000000 --- a/modules/crypto/ssh/agent/server.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package agent - -import ( - "crypto/rsa" - "encoding/binary" - "fmt" - "io" - "log" - "math/big" - - "github.com/gogits/gogs/modules/crypto/ssh" -) - -// Server wraps an Agent and uses it to implement the agent side of -// the SSH-agent, wire protocol. -type server struct { - agent Agent -} - -func (s *server) processRequestBytes(reqData []byte) []byte { - rep, err := s.processRequest(reqData) - if err != nil { - if err != errLocked { - // TODO(hanwen): provide better logging interface? - log.Printf("agent %d: %v", reqData[0], err) - } - return []byte{agentFailure} - } - - if err == nil && rep == nil { - return []byte{agentSuccess} - } - - return ssh.Marshal(rep) -} - -func marshalKey(k *Key) []byte { - var record struct { - Blob []byte - Comment string - } - record.Blob = k.Marshal() - record.Comment = k.Comment - - return ssh.Marshal(&record) -} - -type agentV1IdentityMsg struct { - Numkeys uint32 `sshtype:"2"` -} - -type agentRemoveIdentityMsg struct { - KeyBlob []byte `sshtype:"18"` -} - -type agentLockMsg struct { - Passphrase []byte `sshtype:"22"` -} - -type agentUnlockMsg struct { - Passphrase []byte `sshtype:"23"` -} - -func (s *server) processRequest(data []byte) (interface{}, error) { - switch data[0] { - case agentRequestV1Identities: - return &agentV1IdentityMsg{0}, nil - case agentRemoveIdentity: - var req agentRemoveIdentityMsg - if err := ssh.Unmarshal(data, &req); err != nil { - return nil, err - } - - var wk wireKey - if err := ssh.Unmarshal(req.KeyBlob, &wk); err != nil { - return nil, err - } - - return nil, s.agent.Remove(&Key{Format: wk.Format, Blob: req.KeyBlob}) - - case agentRemoveAllIdentities: - return nil, s.agent.RemoveAll() - - case agentLock: - var req agentLockMsg - if err := ssh.Unmarshal(data, &req); err != nil { - return nil, err - } - - return nil, s.agent.Lock(req.Passphrase) - - case agentUnlock: - var req agentLockMsg - if err := ssh.Unmarshal(data, &req); err != nil { - return nil, err - } - return nil, s.agent.Unlock(req.Passphrase) - - case agentSignRequest: - var req signRequestAgentMsg - if err := ssh.Unmarshal(data, &req); err != nil { - return nil, err - } - - var wk wireKey - if err := ssh.Unmarshal(req.KeyBlob, &wk); err != nil { - return nil, err - } - - k := &Key{ - Format: wk.Format, - Blob: req.KeyBlob, - } - - sig, err := s.agent.Sign(k, req.Data) // TODO(hanwen): flags. - if err != nil { - return nil, err - } - return &signResponseAgentMsg{SigBlob: ssh.Marshal(sig)}, nil - case agentRequestIdentities: - keys, err := s.agent.List() - if err != nil { - return nil, err - } - - rep := identitiesAnswerAgentMsg{ - NumKeys: uint32(len(keys)), - } - for _, k := range keys { - rep.Keys = append(rep.Keys, marshalKey(k)...) - } - return rep, nil - case agentAddIdentity: - return nil, s.insertIdentity(data) - } - - return nil, fmt.Errorf("unknown opcode %d", data[0]) -} - -func (s *server) insertIdentity(req []byte) error { - var record struct { - Type string `sshtype:"17"` - Rest []byte `ssh:"rest"` - } - if err := ssh.Unmarshal(req, &record); err != nil { - return err - } - - switch record.Type { - case ssh.KeyAlgoRSA: - var k rsaKeyMsg - if err := ssh.Unmarshal(req, &k); err != nil { - return err - } - - priv := rsa.PrivateKey{ - PublicKey: rsa.PublicKey{ - E: int(k.E.Int64()), - N: k.N, - }, - D: k.D, - Primes: []*big.Int{k.P, k.Q}, - } - priv.Precompute() - - return s.agent.Add(AddedKey{PrivateKey: &priv, Comment: k.Comments}) - } - return fmt.Errorf("not implemented: %s", record.Type) -} - -// ServeAgent serves the agent protocol on the given connection. It -// returns when an I/O error occurs. -func ServeAgent(agent Agent, c io.ReadWriter) error { - s := &server{agent} - - var length [4]byte - for { - if _, err := io.ReadFull(c, length[:]); err != nil { - return err - } - l := binary.BigEndian.Uint32(length[:]) - if l > maxAgentResponseBytes { - // We also cap requests. - return fmt.Errorf("agent: request too large: %d", l) - } - - req := make([]byte, l) - if _, err := io.ReadFull(c, req); err != nil { - return err - } - - repData := s.processRequestBytes(req) - if len(repData) > maxAgentResponseBytes { - return fmt.Errorf("agent: reply too large: %d bytes", len(repData)) - } - - binary.BigEndian.PutUint32(length[:], uint32(len(repData))) - if _, err := c.Write(length[:]); err != nil { - return err - } - if _, err := c.Write(repData); err != nil { - return err - } - } -} diff --git a/modules/crypto/ssh/agent/server_test.go b/modules/crypto/ssh/agent/server_test.go deleted file mode 100755 index 7f257bcf..00000000 --- a/modules/crypto/ssh/agent/server_test.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package agent - -import ( - "testing" - - "github.com/gogits/gogs/modules/crypto/ssh" -) - -func TestServer(t *testing.T) { - c1, c2, err := netPipe() - if err != nil { - t.Fatalf("netPipe: %v", err) - } - defer c1.Close() - defer c2.Close() - client := NewClient(c1) - - go ServeAgent(NewKeyring(), c2) - - testAgentInterface(t, client, testPrivateKeys["rsa"], nil, 0) -} - -func TestLockServer(t *testing.T) { - testLockAgent(NewKeyring(), t) -} - -func TestSetupForwardAgent(t *testing.T) { - a, b, err := netPipe() - if err != nil { - t.Fatalf("netPipe: %v", err) - } - - defer a.Close() - defer b.Close() - - _, socket, cleanup := startAgent(t) - defer cleanup() - - serverConf := ssh.ServerConfig{ - NoClientAuth: true, - } - serverConf.AddHostKey(testSigners["rsa"]) - incoming := make(chan *ssh.ServerConn, 1) - go func() { - conn, _, _, err := ssh.NewServerConn(a, &serverConf) - if err != nil { - t.Fatalf("Server: %v", err) - } - incoming <- conn - }() - - conf := ssh.ClientConfig{} - conn, chans, reqs, err := ssh.NewClientConn(b, "", &conf) - if err != nil { - t.Fatalf("NewClientConn: %v", err) - } - client := ssh.NewClient(conn, chans, reqs) - - if err := ForwardToRemote(client, socket); err != nil { - t.Fatalf("SetupForwardAgent: %v", err) - } - - server := <-incoming - ch, reqs, err := server.OpenChannel(channelType, nil) - if err != nil { - t.Fatalf("OpenChannel(%q): %v", channelType, err) - } - go ssh.DiscardRequests(reqs) - - agentClient := NewClient(ch) - testAgentInterface(t, agentClient, testPrivateKeys["rsa"], nil, 0) - conn.Close() -} diff --git a/modules/crypto/ssh/agent/testdata_test.go b/modules/crypto/ssh/agent/testdata_test.go deleted file mode 100755 index 18dcde29..00000000 --- a/modules/crypto/ssh/agent/testdata_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// IMPLEMENTOR NOTE: To avoid a package loop, this file is in three places: -// ssh/, ssh/agent, and ssh/test/. It should be kept in sync across all three -// instances. - -package agent - -import ( - "crypto/rand" - "fmt" - - "github.com/gogits/gogs/modules/crypto/ssh" - "github.com/gogits/gogs/modules/crypto/ssh/testdata" -) - -var ( - testPrivateKeys map[string]interface{} - testSigners map[string]ssh.Signer - testPublicKeys map[string]ssh.PublicKey -) - -func init() { - var err error - - n := len(testdata.PEMBytes) - testPrivateKeys = make(map[string]interface{}, n) - testSigners = make(map[string]ssh.Signer, n) - testPublicKeys = make(map[string]ssh.PublicKey, n) - for t, k := range testdata.PEMBytes { - testPrivateKeys[t], err = ssh.ParseRawPrivateKey(k) - if err != nil { - panic(fmt.Sprintf("Unable to parse test key %s: %v", t, err)) - } - testSigners[t], err = ssh.NewSignerFromKey(testPrivateKeys[t]) - if err != nil { - panic(fmt.Sprintf("Unable to create signer for test key %s: %v", t, err)) - } - testPublicKeys[t] = testSigners[t].PublicKey() - } - - // Create a cert and sign it for use in tests. - testCert := &ssh.Certificate{ - Nonce: []byte{}, // To pass reflect.DeepEqual after marshal & parse, this must be non-nil - ValidPrincipals: []string{"gopher1", "gopher2"}, // increases test coverage - ValidAfter: 0, // unix epoch - ValidBefore: ssh.CertTimeInfinity, // The end of currently representable time. - Reserved: []byte{}, // To pass reflect.DeepEqual after marshal & parse, this must be non-nil - Key: testPublicKeys["ecdsa"], - SignatureKey: testPublicKeys["rsa"], - Permissions: ssh.Permissions{ - CriticalOptions: map[string]string{}, - Extensions: map[string]string{}, - }, - } - testCert.SignCert(rand.Reader, testSigners["rsa"]) - testPrivateKeys["cert"] = testPrivateKeys["ecdsa"] - testSigners["cert"], err = ssh.NewCertSigner(testCert, testSigners["ecdsa"]) - if err != nil { - panic(fmt.Sprintf("Unable to create certificate signer: %v", err)) - } -} diff --git a/modules/crypto/ssh/benchmark_test.go b/modules/crypto/ssh/benchmark_test.go deleted file mode 100755 index d9f7eb9b..00000000 --- a/modules/crypto/ssh/benchmark_test.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "errors" - "io" - "net" - "testing" -) - -type server struct { - *ServerConn - chans <-chan NewChannel -} - -func newServer(c net.Conn, conf *ServerConfig) (*server, error) { - sconn, chans, reqs, err := NewServerConn(c, conf) - if err != nil { - return nil, err - } - go DiscardRequests(reqs) - return &server{sconn, chans}, nil -} - -func (s *server) Accept() (NewChannel, error) { - n, ok := <-s.chans - if !ok { - return nil, io.EOF - } - return n, nil -} - -func sshPipe() (Conn, *server, error) { - c1, c2, err := netPipe() - if err != nil { - return nil, nil, err - } - - clientConf := ClientConfig{ - User: "user", - } - serverConf := ServerConfig{ - NoClientAuth: true, - } - serverConf.AddHostKey(testSigners["ecdsa"]) - done := make(chan *server, 1) - go func() { - server, err := newServer(c2, &serverConf) - if err != nil { - done <- nil - } - done <- server - }() - - client, _, reqs, err := NewClientConn(c1, "", &clientConf) - if err != nil { - return nil, nil, err - } - - server := <-done - if server == nil { - return nil, nil, errors.New("server handshake failed.") - } - go DiscardRequests(reqs) - - return client, server, nil -} - -func BenchmarkEndToEnd(b *testing.B) { - b.StopTimer() - - client, server, err := sshPipe() - if err != nil { - b.Fatalf("sshPipe: %v", err) - } - - defer client.Close() - defer server.Close() - - size := (1 << 20) - input := make([]byte, size) - output := make([]byte, size) - b.SetBytes(int64(size)) - done := make(chan int, 1) - - go func() { - newCh, err := server.Accept() - if err != nil { - b.Fatalf("Client: %v", err) - } - ch, incoming, err := newCh.Accept() - go DiscardRequests(incoming) - for i := 0; i < b.N; i++ { - if _, err := io.ReadFull(ch, output); err != nil { - b.Fatalf("ReadFull: %v", err) - } - } - ch.Close() - done <- 1 - }() - - ch, in, err := client.OpenChannel("speed", nil) - if err != nil { - b.Fatalf("OpenChannel: %v", err) - } - go DiscardRequests(in) - - b.ResetTimer() - b.StartTimer() - for i := 0; i < b.N; i++ { - if _, err := ch.Write(input); err != nil { - b.Fatalf("WriteFull: %v", err) - } - } - ch.Close() - b.StopTimer() - - <-done -} diff --git a/modules/crypto/ssh/buffer.go b/modules/crypto/ssh/buffer.go deleted file mode 100755 index 6931b511..00000000 --- a/modules/crypto/ssh/buffer.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "io" - "sync" -) - -// buffer provides a linked list buffer for data exchange -// between producer and consumer. Theoretically the buffer is -// of unlimited capacity as it does no allocation of its own. -type buffer struct { - // protects concurrent access to head, tail and closed - *sync.Cond - - head *element // the buffer that will be read first - tail *element // the buffer that will be read last - - closed bool -} - -// An element represents a single link in a linked list. -type element struct { - buf []byte - next *element -} - -// newBuffer returns an empty buffer that is not closed. -func newBuffer() *buffer { - e := new(element) - b := &buffer{ - Cond: newCond(), - head: e, - tail: e, - } - return b -} - -// write makes buf available for Read to receive. -// buf must not be modified after the call to write. -func (b *buffer) write(buf []byte) { - b.Cond.L.Lock() - e := &element{buf: buf} - b.tail.next = e - b.tail = e - b.Cond.Signal() - b.Cond.L.Unlock() -} - -// eof closes the buffer. Reads from the buffer once all -// the data has been consumed will receive os.EOF. -func (b *buffer) eof() error { - b.Cond.L.Lock() - b.closed = true - b.Cond.Signal() - b.Cond.L.Unlock() - return nil -} - -// Read reads data from the internal buffer in buf. Reads will block -// if no data is available, or until the buffer is closed. -func (b *buffer) Read(buf []byte) (n int, err error) { - b.Cond.L.Lock() - defer b.Cond.L.Unlock() - - for len(buf) > 0 { - // if there is data in b.head, copy it - if len(b.head.buf) > 0 { - r := copy(buf, b.head.buf) - buf, b.head.buf = buf[r:], b.head.buf[r:] - n += r - continue - } - // if there is a next buffer, make it the head - if len(b.head.buf) == 0 && b.head != b.tail { - b.head = b.head.next - continue - } - - // if at least one byte has been copied, return - if n > 0 { - break - } - - // if nothing was read, and there is nothing outstanding - // check to see if the buffer is closed. - if b.closed { - err = io.EOF - break - } - // out of buffers, wait for producer - b.Cond.Wait() - } - return -} diff --git a/modules/crypto/ssh/buffer_test.go b/modules/crypto/ssh/buffer_test.go deleted file mode 100755 index d5781cb3..00000000 --- a/modules/crypto/ssh/buffer_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "io" - "testing" -) - -var alphabet = []byte("abcdefghijklmnopqrstuvwxyz") - -func TestBufferReadwrite(t *testing.T) { - b := newBuffer() - b.write(alphabet[:10]) - r, _ := b.Read(make([]byte, 10)) - if r != 10 { - t.Fatalf("Expected written == read == 10, written: 10, read %d", r) - } - - b = newBuffer() - b.write(alphabet[:5]) - r, _ = b.Read(make([]byte, 10)) - if r != 5 { - t.Fatalf("Expected written == read == 5, written: 5, read %d", r) - } - - b = newBuffer() - b.write(alphabet[:10]) - r, _ = b.Read(make([]byte, 5)) - if r != 5 { - t.Fatalf("Expected written == 10, read == 5, written: 10, read %d", r) - } - - b = newBuffer() - b.write(alphabet[:5]) - b.write(alphabet[5:15]) - r, _ = b.Read(make([]byte, 10)) - r2, _ := b.Read(make([]byte, 10)) - if r != 10 || r2 != 5 || 15 != r+r2 { - t.Fatal("Expected written == read == 15") - } -} - -func TestBufferClose(t *testing.T) { - b := newBuffer() - b.write(alphabet[:10]) - b.eof() - _, err := b.Read(make([]byte, 5)) - if err != nil { - t.Fatal("expected read of 5 to not return EOF") - } - b = newBuffer() - b.write(alphabet[:10]) - b.eof() - r, err := b.Read(make([]byte, 5)) - r2, err2 := b.Read(make([]byte, 10)) - if r != 5 || r2 != 5 || err != nil || err2 != nil { - t.Fatal("expected reads of 5 and 5") - } - - b = newBuffer() - b.write(alphabet[:10]) - b.eof() - r, err = b.Read(make([]byte, 5)) - r2, err2 = b.Read(make([]byte, 10)) - r3, err3 := b.Read(make([]byte, 10)) - if r != 5 || r2 != 5 || r3 != 0 || err != nil || err2 != nil || err3 != io.EOF { - t.Fatal("expected reads of 5 and 5 and 0, with EOF") - } - - b = newBuffer() - b.write(make([]byte, 5)) - b.write(make([]byte, 10)) - b.eof() - r, err = b.Read(make([]byte, 9)) - r2, err2 = b.Read(make([]byte, 3)) - r3, err3 = b.Read(make([]byte, 3)) - r4, err4 := b.Read(make([]byte, 10)) - if err != nil || err2 != nil || err3 != nil || err4 != io.EOF { - t.Fatalf("Expected EOF on forth read only, err=%v, err2=%v, err3=%v, err4=%v", err, err2, err3, err4) - } - if r != 9 || r2 != 3 || r3 != 3 || r4 != 0 { - t.Fatal("Expected written == read == 15", r, r2, r3, r4) - } -} diff --git a/modules/crypto/ssh/certs.go b/modules/crypto/ssh/certs.go deleted file mode 100755 index 38577003..00000000 --- a/modules/crypto/ssh/certs.go +++ /dev/null @@ -1,501 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bytes" - "errors" - "fmt" - "io" - "net" - "sort" - "time" -) - -// These constants from [PROTOCOL.certkeys] represent the algorithm names -// for certificate types supported by this package. -const ( - CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com" - CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com" - CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com" - CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com" - CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com" -) - -// Certificate types distinguish between host and user -// certificates. The values can be set in the CertType field of -// Certificate. -const ( - UserCert = 1 - HostCert = 2 -) - -// Signature represents a cryptographic signature. -type Signature struct { - Format string - Blob []byte -} - -// CertTimeInfinity can be used for OpenSSHCertV01.ValidBefore to indicate that -// a certificate does not expire. -const CertTimeInfinity = 1<<64 - 1 - -// An Certificate represents an OpenSSH certificate as defined in -// [PROTOCOL.certkeys]?rev=1.8. -type Certificate struct { - Nonce []byte - Key PublicKey - Serial uint64 - CertType uint32 - KeyId string - ValidPrincipals []string - ValidAfter uint64 - ValidBefore uint64 - Permissions - Reserved []byte - SignatureKey PublicKey - Signature *Signature -} - -// genericCertData holds the key-independent part of the certificate data. -// Overall, certificates contain an nonce, public key fields and -// key-independent fields. -type genericCertData struct { - Serial uint64 - CertType uint32 - KeyId string - ValidPrincipals []byte - ValidAfter uint64 - ValidBefore uint64 - CriticalOptions []byte - Extensions []byte - Reserved []byte - SignatureKey []byte - Signature []byte -} - -func marshalStringList(namelist []string) []byte { - var to []byte - for _, name := range namelist { - s := struct{ N string }{name} - to = append(to, Marshal(&s)...) - } - return to -} - -type optionsTuple struct { - Key string - Value []byte -} - -type optionsTupleValue struct { - Value string -} - -// serialize a map of critical options or extensions -// issue #10569 - per [PROTOCOL.certkeys] and SSH implementation, -// we need two length prefixes for a non-empty string value -func marshalTuples(tups map[string]string) []byte { - keys := make([]string, 0, len(tups)) - for key := range tups { - keys = append(keys, key) - } - sort.Strings(keys) - - var ret []byte - for _, key := range keys { - s := optionsTuple{Key: key} - if value := tups[key]; len(value) > 0 { - s.Value = Marshal(&optionsTupleValue{value}) - } - ret = append(ret, Marshal(&s)...) - } - return ret -} - -// issue #10569 - per [PROTOCOL.certkeys] and SSH implementation, -// we need two length prefixes for a non-empty option value -func parseTuples(in []byte) (map[string]string, error) { - tups := map[string]string{} - var lastKey string - var haveLastKey bool - - for len(in) > 0 { - var key, val, extra []byte - var ok bool - - if key, in, ok = parseString(in); !ok { - return nil, errShortRead - } - keyStr := string(key) - // according to [PROTOCOL.certkeys], the names must be in - // lexical order. - if haveLastKey && keyStr <= lastKey { - return nil, fmt.Errorf("ssh: certificate options are not in lexical order") - } - lastKey, haveLastKey = keyStr, true - // the next field is a data field, which if non-empty has a string embedded - if val, in, ok = parseString(in); !ok { - return nil, errShortRead - } - if len(val) > 0 { - val, extra, ok = parseString(val) - if !ok { - return nil, errShortRead - } - if len(extra) > 0 { - return nil, fmt.Errorf("ssh: unexpected trailing data after certificate option value") - } - tups[keyStr] = string(val) - } else { - tups[keyStr] = "" - } - } - return tups, nil -} - -func parseCert(in []byte, privAlgo string) (*Certificate, error) { - nonce, rest, ok := parseString(in) - if !ok { - return nil, errShortRead - } - - key, rest, err := parsePubKey(rest, privAlgo) - if err != nil { - return nil, err - } - - var g genericCertData - if err := Unmarshal(rest, &g); err != nil { - return nil, err - } - - c := &Certificate{ - Nonce: nonce, - Key: key, - Serial: g.Serial, - CertType: g.CertType, - KeyId: g.KeyId, - ValidAfter: g.ValidAfter, - ValidBefore: g.ValidBefore, - } - - for principals := g.ValidPrincipals; len(principals) > 0; { - principal, rest, ok := parseString(principals) - if !ok { - return nil, errShortRead - } - c.ValidPrincipals = append(c.ValidPrincipals, string(principal)) - principals = rest - } - - c.CriticalOptions, err = parseTuples(g.CriticalOptions) - if err != nil { - return nil, err - } - c.Extensions, err = parseTuples(g.Extensions) - if err != nil { - return nil, err - } - c.Reserved = g.Reserved - k, err := ParsePublicKey(g.SignatureKey) - if err != nil { - return nil, err - } - - c.SignatureKey = k - c.Signature, rest, ok = parseSignatureBody(g.Signature) - if !ok || len(rest) > 0 { - return nil, errors.New("ssh: signature parse error") - } - - return c, nil -} - -type openSSHCertSigner struct { - pub *Certificate - signer Signer -} - -// NewCertSigner returns a Signer that signs with the given Certificate, whose -// private key is held by signer. It returns an error if the public key in cert -// doesn't match the key used by signer. -func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) { - if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { - return nil, errors.New("ssh: signer and cert have different public key") - } - - return &openSSHCertSigner{cert, signer}, nil -} - -func (s *openSSHCertSigner) Sign(rand io.Reader, data []byte) (*Signature, error) { - return s.signer.Sign(rand, data) -} - -func (s *openSSHCertSigner) PublicKey() PublicKey { - return s.pub -} - -const sourceAddressCriticalOption = "source-address" - -// CertChecker does the work of verifying a certificate. Its methods -// can be plugged into ClientConfig.HostKeyCallback and -// ServerConfig.PublicKeyCallback. For the CertChecker to work, -// minimally, the IsAuthority callback should be set. -type CertChecker struct { - // SupportedCriticalOptions lists the CriticalOptions that the - // server application layer understands. These are only used - // for user certificates. - SupportedCriticalOptions []string - - // IsAuthority should return true if the key is recognized as - // an authority. This allows for certificates to be signed by other - // certificates. - IsAuthority func(auth PublicKey) bool - - // Clock is used for verifying time stamps. If nil, time.Now - // is used. - Clock func() time.Time - - // UserKeyFallback is called when CertChecker.Authenticate encounters a - // public key that is not a certificate. It must implement validation - // of user keys or else, if nil, all such keys are rejected. - UserKeyFallback func(conn ConnMetadata, key PublicKey) (*Permissions, error) - - // HostKeyFallback is called when CertChecker.CheckHostKey encounters a - // public key that is not a certificate. It must implement host key - // validation or else, if nil, all such keys are rejected. - HostKeyFallback func(addr string, remote net.Addr, key PublicKey) error - - // IsRevoked is called for each certificate so that revocation checking - // can be implemented. It should return true if the given certificate - // is revoked and false otherwise. If nil, no certificates are - // considered to have been revoked. - IsRevoked func(cert *Certificate) bool -} - -// CheckHostKey checks a host key certificate. This method can be -// plugged into ClientConfig.HostKeyCallback. -func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey) error { - cert, ok := key.(*Certificate) - if !ok { - if c.HostKeyFallback != nil { - return c.HostKeyFallback(addr, remote, key) - } - return errors.New("ssh: non-certificate host key") - } - if cert.CertType != HostCert { - return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType) - } - - return c.CheckCert(addr, cert) -} - -// Authenticate checks a user certificate. Authenticate can be used as -// a value for ServerConfig.PublicKeyCallback. -func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permissions, error) { - cert, ok := pubKey.(*Certificate) - if !ok { - if c.UserKeyFallback != nil { - return c.UserKeyFallback(conn, pubKey) - } - return nil, errors.New("ssh: normal key pairs not accepted") - } - - if cert.CertType != UserCert { - return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType) - } - - if err := c.CheckCert(conn.User(), cert); err != nil { - return nil, err - } - - return &cert.Permissions, nil -} - -// CheckCert checks CriticalOptions, ValidPrincipals, revocation, timestamp and -// the signature of the certificate. -func (c *CertChecker) CheckCert(principal string, cert *Certificate) error { - if c.IsRevoked != nil && c.IsRevoked(cert) { - return fmt.Errorf("ssh: certicate serial %d revoked", cert.Serial) - } - - for opt, _ := range cert.CriticalOptions { - // sourceAddressCriticalOption will be enforced by - // serverAuthenticate - if opt == sourceAddressCriticalOption { - continue - } - - found := false - for _, supp := range c.SupportedCriticalOptions { - if supp == opt { - found = true - break - } - } - if !found { - return fmt.Errorf("ssh: unsupported critical option %q in certificate", opt) - } - } - - if len(cert.ValidPrincipals) > 0 { - // By default, certs are valid for all users/hosts. - found := false - for _, p := range cert.ValidPrincipals { - if p == principal { - found = true - break - } - } - if !found { - return fmt.Errorf("ssh: principal %q not in the set of valid principals for given certificate: %q", principal, cert.ValidPrincipals) - } - } - - if !c.IsAuthority(cert.SignatureKey) { - return fmt.Errorf("ssh: certificate signed by unrecognized authority") - } - - clock := c.Clock - if clock == nil { - clock = time.Now - } - - unixNow := clock().Unix() - if after := int64(cert.ValidAfter); after < 0 || unixNow < int64(cert.ValidAfter) { - return fmt.Errorf("ssh: cert is not yet valid") - } - if before := int64(cert.ValidBefore); cert.ValidBefore != uint64(CertTimeInfinity) && (unixNow >= before || before < 0) { - return fmt.Errorf("ssh: cert has expired") - } - if err := cert.SignatureKey.Verify(cert.bytesForSigning(), cert.Signature); err != nil { - return fmt.Errorf("ssh: certificate signature does not verify") - } - - return nil -} - -// SignCert sets c.SignatureKey to the authority's public key and stores a -// Signature, by authority, in the certificate. -func (c *Certificate) SignCert(rand io.Reader, authority Signer) error { - c.Nonce = make([]byte, 32) - if _, err := io.ReadFull(rand, c.Nonce); err != nil { - return err - } - c.SignatureKey = authority.PublicKey() - - sig, err := authority.Sign(rand, c.bytesForSigning()) - if err != nil { - return err - } - c.Signature = sig - return nil -} - -var certAlgoNames = map[string]string{ - KeyAlgoRSA: CertAlgoRSAv01, - KeyAlgoDSA: CertAlgoDSAv01, - KeyAlgoECDSA256: CertAlgoECDSA256v01, - KeyAlgoECDSA384: CertAlgoECDSA384v01, - KeyAlgoECDSA521: CertAlgoECDSA521v01, -} - -// certToPrivAlgo returns the underlying algorithm for a certificate algorithm. -// Panics if a non-certificate algorithm is passed. -func certToPrivAlgo(algo string) string { - for privAlgo, pubAlgo := range certAlgoNames { - if pubAlgo == algo { - return privAlgo - } - } - panic("unknown cert algorithm") -} - -func (cert *Certificate) bytesForSigning() []byte { - c2 := *cert - c2.Signature = nil - out := c2.Marshal() - // Drop trailing signature length. - return out[:len(out)-4] -} - -// Marshal serializes c into OpenSSH's wire format. It is part of the -// PublicKey interface. -func (c *Certificate) Marshal() []byte { - generic := genericCertData{ - Serial: c.Serial, - CertType: c.CertType, - KeyId: c.KeyId, - ValidPrincipals: marshalStringList(c.ValidPrincipals), - ValidAfter: uint64(c.ValidAfter), - ValidBefore: uint64(c.ValidBefore), - CriticalOptions: marshalTuples(c.CriticalOptions), - Extensions: marshalTuples(c.Extensions), - Reserved: c.Reserved, - SignatureKey: c.SignatureKey.Marshal(), - } - if c.Signature != nil { - generic.Signature = Marshal(c.Signature) - } - genericBytes := Marshal(&generic) - keyBytes := c.Key.Marshal() - _, keyBytes, _ = parseString(keyBytes) - prefix := Marshal(&struct { - Name string - Nonce []byte - Key []byte `ssh:"rest"` - }{c.Type(), c.Nonce, keyBytes}) - - result := make([]byte, 0, len(prefix)+len(genericBytes)) - result = append(result, prefix...) - result = append(result, genericBytes...) - return result -} - -// Type returns the key name. It is part of the PublicKey interface. -func (c *Certificate) Type() string { - algo, ok := certAlgoNames[c.Key.Type()] - if !ok { - panic("unknown cert key type") - } - return algo -} - -// Verify verifies a signature against the certificate's public -// key. It is part of the PublicKey interface. -func (c *Certificate) Verify(data []byte, sig *Signature) error { - return c.Key.Verify(data, sig) -} - -func parseSignatureBody(in []byte) (out *Signature, rest []byte, ok bool) { - format, in, ok := parseString(in) - if !ok { - return - } - - out = &Signature{ - Format: string(format), - } - - if out.Blob, in, ok = parseString(in); !ok { - return - } - - return out, in, ok -} - -func parseSignature(in []byte) (out *Signature, rest []byte, ok bool) { - sigBytes, rest, ok := parseString(in) - if !ok { - return - } - - out, trailing, ok := parseSignatureBody(sigBytes) - if !ok || len(trailing) > 0 { - return nil, nil, false - } - return -} diff --git a/modules/crypto/ssh/certs_test.go b/modules/crypto/ssh/certs_test.go deleted file mode 100755 index c5f2e533..00000000 --- a/modules/crypto/ssh/certs_test.go +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bytes" - "crypto/rand" - "reflect" - "testing" - "time" -) - -// Cert generated by ssh-keygen 6.0p1 Debian-4. -// % ssh-keygen -s ca-key -I test user-key -const exampleSSHCert = `ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgb1srW/W3ZDjYAO45xLYAwzHBDLsJ4Ux6ICFIkTjb1LEAAAADAQABAAAAYQCkoR51poH0wE8w72cqSB8Sszx+vAhzcMdCO0wqHTj7UNENHWEXGrU0E0UQekD7U+yhkhtoyjbPOVIP7hNa6aRk/ezdh/iUnCIt4Jt1v3Z1h1P+hA4QuYFMHNB+rmjPwAcAAAAAAAAAAAAAAAEAAAAEdGVzdAAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAAHcAAAAHc3NoLXJzYQAAAAMBAAEAAABhANFS2kaktpSGc+CcmEKPyw9mJC4nZKxHKTgLVZeaGbFZOvJTNzBspQHdy7Q1uKSfktxpgjZnksiu/tFF9ngyY2KFoc+U88ya95IZUycBGCUbBQ8+bhDtw/icdDGQD5WnUwAAAG8AAAAHc3NoLXJzYQAAAGC8Y9Z2LQKhIhxf52773XaWrXdxP0t3GBVo4A10vUWiYoAGepr6rQIoGGXFxT4B9Gp+nEBJjOwKDXPrAevow0T9ca8gZN+0ykbhSrXLE5Ao48rqr3zP4O1/9P7e6gp0gw8=` - -func TestParseCert(t *testing.T) { - authKeyBytes := []byte(exampleSSHCert) - - key, _, _, rest, err := ParseAuthorizedKey(authKeyBytes) - if err != nil { - t.Fatalf("ParseAuthorizedKey: %v", err) - } - if len(rest) > 0 { - t.Errorf("rest: got %q, want empty", rest) - } - - if _, ok := key.(*Certificate); !ok { - t.Fatalf("got %v (%T), want *Certificate", key, key) - } - - marshaled := MarshalAuthorizedKey(key) - // Before comparison, remove the trailing newline that - // MarshalAuthorizedKey adds. - marshaled = marshaled[:len(marshaled)-1] - if !bytes.Equal(authKeyBytes, marshaled) { - t.Errorf("marshaled certificate does not match original: got %q, want %q", marshaled, authKeyBytes) - } -} - -// Cert generated by ssh-keygen OpenSSH_6.8p1 OS X 10.10.3 -// % ssh-keygen -s ca -I testcert -O source-address=192.168.1.0/24 -O force-command=/bin/sleep user.pub -// user.pub key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDACh1rt2DXfV3hk6fszSQcQ/rueMId0kVD9U7nl8cfEnFxqOCrNT92g4laQIGl2mn8lsGZfTLg8ksHq3gkvgO3oo/0wHy4v32JeBOHTsN5AL4gfHNEhWeWb50ev47hnTsRIt9P4dxogeUo/hTu7j9+s9lLpEQXCvq6xocXQt0j8MV9qZBBXFLXVT3cWIkSqOdwt/5ZBg+1GSrc7WfCXVWgTk4a20uPMuJPxU4RQwZW6X3+O8Pqo8C3cW0OzZRFP6gUYUKUsTI5WntlS+LAxgw1mZNsozFGdbiOPRnEryE3SRldh9vjDR3tin1fGpA5P7+CEB/bqaXtG3V+F2OkqaMN -// Critical Options: -// force-command /bin/sleep -// source-address 192.168.1.0/24 -// Extensions: -// permit-X11-forwarding -// permit-agent-forwarding -// permit-port-forwarding -// permit-pty -// permit-user-rc -const exampleSSHCertWithOptions = `ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgDyysCJY0XrO1n03EeRRoITnTPdjENFmWDs9X58PP3VUAAAADAQABAAABAQDACh1rt2DXfV3hk6fszSQcQ/rueMId0kVD9U7nl8cfEnFxqOCrNT92g4laQIGl2mn8lsGZfTLg8ksHq3gkvgO3oo/0wHy4v32JeBOHTsN5AL4gfHNEhWeWb50ev47hnTsRIt9P4dxogeUo/hTu7j9+s9lLpEQXCvq6xocXQt0j8MV9qZBBXFLXVT3cWIkSqOdwt/5ZBg+1GSrc7WfCXVWgTk4a20uPMuJPxU4RQwZW6X3+O8Pqo8C3cW0OzZRFP6gUYUKUsTI5WntlS+LAxgw1mZNsozFGdbiOPRnEryE3SRldh9vjDR3tin1fGpA5P7+CEB/bqaXtG3V+F2OkqaMNAAAAAAAAAAAAAAABAAAACHRlc3RjZXJ0AAAAAAAAAAAAAAAA//////////8AAABLAAAADWZvcmNlLWNvbW1hbmQAAAAOAAAACi9iaW4vc2xlZXAAAAAOc291cmNlLWFkZHJlc3MAAAASAAAADjE5Mi4xNjguMS4wLzI0AAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAABFwAAAAdzc2gtcnNhAAAAAwEAAQAAAQEAwU+c5ui5A8+J/CFpjW8wCa52bEODA808WWQDCSuTG/eMXNf59v9Y8Pk0F1E9dGCosSNyVcB/hacUrc6He+i97+HJCyKavBsE6GDxrjRyxYqAlfcOXi/IVmaUGiO8OQ39d4GHrjToInKvExSUeleQyH4Y4/e27T/pILAqPFL3fyrvMLT5qU9QyIt6zIpa7GBP5+urouNavMprV3zsfIqNBbWypinOQAw823a5wN+zwXnhZrgQiHZ/USG09Y6k98y1dTVz8YHlQVR4D3lpTAsKDKJ5hCH9WU4fdf+lU8OyNGaJ/vz0XNqxcToe1l4numLTnaoSuH89pHryjqurB7lJKwAAAQ8AAAAHc3NoLXJzYQAAAQCaHvUIoPL1zWUHIXLvu96/HU1s/i4CAW2IIEuGgxCUCiFj6vyTyYtgxQxcmbfZf6eaITlS6XJZa7Qq4iaFZh75C1DXTX8labXhRSD4E2t//AIP9MC1rtQC5xo6FmbQ+BoKcDskr+mNACcbRSxs3IL3bwCfWDnIw2WbVox9ZdcthJKk4UoCW4ix4QwdHw7zlddlz++fGEEVhmTbll1SUkycGApPFBsAYRTMupUJcYPIeReBI/m8XfkoMk99bV8ZJQTAd7OekHY2/48Ff53jLmyDjP7kNw1F8OaPtkFs6dGJXta4krmaekPy87j+35In5hFj7yoOqvSbmYUkeX70/GGQ` - -func TestParseCertWithOptions(t *testing.T) { - opts := map[string]string{ - "source-address": "192.168.1.0/24", - "force-command": "/bin/sleep", - } - exts := map[string]string{ - "permit-X11-forwarding": "", - "permit-agent-forwarding": "", - "permit-port-forwarding": "", - "permit-pty": "", - "permit-user-rc": "", - } - authKeyBytes := []byte(exampleSSHCertWithOptions) - - key, _, _, rest, err := ParseAuthorizedKey(authKeyBytes) - if err != nil { - t.Fatalf("ParseAuthorizedKey: %v", err) - } - if len(rest) > 0 { - t.Errorf("rest: got %q, want empty", rest) - } - cert, ok := key.(*Certificate) - if !ok { - t.Fatalf("got %v (%T), want *Certificate", key, key) - } - if !reflect.DeepEqual(cert.CriticalOptions, opts) { - t.Errorf("unexpected critical options - got %v, want %v", cert.CriticalOptions, opts) - } - if !reflect.DeepEqual(cert.Extensions, exts) { - t.Errorf("unexpected Extensions - got %v, want %v", cert.Extensions, exts) - } - marshaled := MarshalAuthorizedKey(key) - // Before comparison, remove the trailing newline that - // MarshalAuthorizedKey adds. - marshaled = marshaled[:len(marshaled)-1] - if !bytes.Equal(authKeyBytes, marshaled) { - t.Errorf("marshaled certificate does not match original: got %q, want %q", marshaled, authKeyBytes) - } -} - -func TestValidateCert(t *testing.T) { - key, _, _, _, err := ParseAuthorizedKey([]byte(exampleSSHCert)) - if err != nil { - t.Fatalf("ParseAuthorizedKey: %v", err) - } - validCert, ok := key.(*Certificate) - if !ok { - t.Fatalf("got %v (%T), want *Certificate", key, key) - } - checker := CertChecker{} - checker.IsAuthority = func(k PublicKey) bool { - return bytes.Equal(k.Marshal(), validCert.SignatureKey.Marshal()) - } - - if err := checker.CheckCert("user", validCert); err != nil { - t.Errorf("Unable to validate certificate: %v", err) - } - invalidCert := &Certificate{ - Key: testPublicKeys["rsa"], - SignatureKey: testPublicKeys["ecdsa"], - ValidBefore: CertTimeInfinity, - Signature: &Signature{}, - } - if err := checker.CheckCert("user", invalidCert); err == nil { - t.Error("Invalid cert signature passed validation") - } -} - -func TestValidateCertTime(t *testing.T) { - cert := Certificate{ - ValidPrincipals: []string{"user"}, - Key: testPublicKeys["rsa"], - ValidAfter: 50, - ValidBefore: 100, - } - - cert.SignCert(rand.Reader, testSigners["ecdsa"]) - - for ts, ok := range map[int64]bool{ - 25: false, - 50: true, - 99: true, - 100: false, - 125: false, - } { - checker := CertChecker{ - Clock: func() time.Time { return time.Unix(ts, 0) }, - } - checker.IsAuthority = func(k PublicKey) bool { - return bytes.Equal(k.Marshal(), - testPublicKeys["ecdsa"].Marshal()) - } - - if v := checker.CheckCert("user", &cert); (v == nil) != ok { - t.Errorf("Authenticate(%d): %v", ts, v) - } - } -} - -// TODO(hanwen): tests for -// -// host keys: -// * fallbacks - -func TestHostKeyCert(t *testing.T) { - cert := &Certificate{ - ValidPrincipals: []string{"hostname", "hostname.domain"}, - Key: testPublicKeys["rsa"], - ValidBefore: CertTimeInfinity, - CertType: HostCert, - } - cert.SignCert(rand.Reader, testSigners["ecdsa"]) - - checker := &CertChecker{ - IsAuthority: func(p PublicKey) bool { - return bytes.Equal(testPublicKeys["ecdsa"].Marshal(), p.Marshal()) - }, - } - - certSigner, err := NewCertSigner(cert, testSigners["rsa"]) - if err != nil { - t.Errorf("NewCertSigner: %v", err) - } - - for _, name := range []string{"hostname", "otherhost"} { - c1, c2, err := netPipe() - if err != nil { - t.Fatalf("netPipe: %v", err) - } - defer c1.Close() - defer c2.Close() - - errc := make(chan error) - - go func() { - conf := ServerConfig{ - NoClientAuth: true, - } - conf.AddHostKey(certSigner) - _, _, _, err := NewServerConn(c1, &conf) - errc <- err - }() - - config := &ClientConfig{ - User: "user", - HostKeyCallback: checker.CheckHostKey, - } - _, _, _, err = NewClientConn(c2, name, config) - - succeed := name == "hostname" - if (err == nil) != succeed { - t.Fatalf("NewClientConn(%q): %v", name, err) - } - - err = <-errc - if (err == nil) != succeed { - t.Fatalf("NewServerConn(%q): %v", name, err) - } - } -} diff --git a/modules/crypto/ssh/channel.go b/modules/crypto/ssh/channel.go deleted file mode 100755 index 5403c7e4..00000000 --- a/modules/crypto/ssh/channel.go +++ /dev/null @@ -1,631 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "encoding/binary" - "errors" - "fmt" - "io" - "log" - "sync" -) - -const ( - minPacketLength = 9 - // channelMaxPacket contains the maximum number of bytes that will be - // sent in a single packet. As per RFC 4253, section 6.1, 32k is also - // the minimum. - channelMaxPacket = 1 << 15 - // We follow OpenSSH here. - channelWindowSize = 64 * channelMaxPacket -) - -// NewChannel represents an incoming request to a channel. It must either be -// accepted for use by calling Accept, or rejected by calling Reject. -type NewChannel interface { - // Accept accepts the channel creation request. It returns the Channel - // and a Go channel containing SSH requests. The Go channel must be - // serviced otherwise the Channel will hang. - Accept() (Channel, <-chan *Request, error) - - // Reject rejects the channel creation request. After calling - // this, no other methods on the Channel may be called. - Reject(reason RejectionReason, message string) error - - // ChannelType returns the type of the channel, as supplied by the - // client. - ChannelType() string - - // ExtraData returns the arbitrary payload for this channel, as supplied - // by the client. This data is specific to the channel type. - ExtraData() []byte -} - -// A Channel is an ordered, reliable, flow-controlled, duplex stream -// that is multiplexed over an SSH connection. -type Channel interface { - // Read reads up to len(data) bytes from the channel. - Read(data []byte) (int, error) - - // Write writes len(data) bytes to the channel. - Write(data []byte) (int, error) - - // Close signals end of channel use. No data may be sent after this - // call. - Close() error - - // CloseWrite signals the end of sending in-band - // data. Requests may still be sent, and the other side may - // still send data - CloseWrite() error - - // SendRequest sends a channel request. If wantReply is true, - // it will wait for a reply and return the result as a - // boolean, otherwise the return value will be false. Channel - // requests are out-of-band messages so they may be sent even - // if the data stream is closed or blocked by flow control. - SendRequest(name string, wantReply bool, payload []byte) (bool, error) - - // Stderr returns an io.ReadWriter that writes to this channel - // with the extended data type set to stderr. Stderr may - // safely be read and written from a different goroutine than - // Read and Write respectively. - Stderr() io.ReadWriter -} - -// Request is a request sent outside of the normal stream of -// data. Requests can either be specific to an SSH channel, or they -// can be global. -type Request struct { - Type string - WantReply bool - Payload []byte - - ch *channel - mux *mux -} - -// Reply sends a response to a request. It must be called for all requests -// where WantReply is true and is a no-op otherwise. The payload argument is -// ignored for replies to channel-specific requests. -func (r *Request) Reply(ok bool, payload []byte) error { - if !r.WantReply { - return nil - } - - if r.ch == nil { - return r.mux.ackRequest(ok, payload) - } - - return r.ch.ackRequest(ok) -} - -// RejectionReason is an enumeration used when rejecting channel creation -// requests. See RFC 4254, section 5.1. -type RejectionReason uint32 - -const ( - Prohibited RejectionReason = iota + 1 - ConnectionFailed - UnknownChannelType - ResourceShortage -) - -// String converts the rejection reason to human readable form. -func (r RejectionReason) String() string { - switch r { - case Prohibited: - return "administratively prohibited" - case ConnectionFailed: - return "connect failed" - case UnknownChannelType: - return "unknown channel type" - case ResourceShortage: - return "resource shortage" - } - return fmt.Sprintf("unknown reason %d", int(r)) -} - -func min(a uint32, b int) uint32 { - if a < uint32(b) { - return a - } - return uint32(b) -} - -type channelDirection uint8 - -const ( - channelInbound channelDirection = iota - channelOutbound -) - -// channel is an implementation of the Channel interface that works -// with the mux class. -type channel struct { - // R/O after creation - chanType string - extraData []byte - localId, remoteId uint32 - - // maxIncomingPayload and maxRemotePayload are the maximum - // payload sizes of normal and extended data packets for - // receiving and sending, respectively. The wire packet will - // be 9 or 13 bytes larger (excluding encryption overhead). - maxIncomingPayload uint32 - maxRemotePayload uint32 - - mux *mux - - // decided is set to true if an accept or reject message has been sent - // (for outbound channels) or received (for inbound channels). - decided bool - - // direction contains either channelOutbound, for channels created - // locally, or channelInbound, for channels created by the peer. - direction channelDirection - - // Pending internal channel messages. - msg chan interface{} - - // Since requests have no ID, there can be only one request - // with WantReply=true outstanding. This lock is held by a - // goroutine that has such an outgoing request pending. - sentRequestMu sync.Mutex - - incomingRequests chan *Request - - sentEOF bool - - // thread-safe data - remoteWin window - pending *buffer - extPending *buffer - - // windowMu protects myWindow, the flow-control window. - windowMu sync.Mutex - myWindow uint32 - - // writeMu serializes calls to mux.conn.writePacket() and - // protects sentClose and packetPool. This mutex must be - // different from windowMu, as writePacket can block if there - // is a key exchange pending. - writeMu sync.Mutex - sentClose bool - - // packetPool has a buffer for each extended channel ID to - // save allocations during writes. - packetPool map[uint32][]byte -} - -// writePacket sends a packet. If the packet is a channel close, it updates -// sentClose. This method takes the lock c.writeMu. -func (c *channel) writePacket(packet []byte) error { - c.writeMu.Lock() - if c.sentClose { - c.writeMu.Unlock() - return io.EOF - } - c.sentClose = (packet[0] == msgChannelClose) - err := c.mux.conn.writePacket(packet) - c.writeMu.Unlock() - return err -} - -func (c *channel) sendMessage(msg interface{}) error { - if debugMux { - log.Printf("send %d: %#v", c.mux.chanList.offset, msg) - } - - p := Marshal(msg) - binary.BigEndian.PutUint32(p[1:], c.remoteId) - return c.writePacket(p) -} - -// WriteExtended writes data to a specific extended stream. These streams are -// used, for example, for stderr. -func (c *channel) WriteExtended(data []byte, extendedCode uint32) (n int, err error) { - if c.sentEOF { - return 0, io.EOF - } - // 1 byte message type, 4 bytes remoteId, 4 bytes data length - opCode := byte(msgChannelData) - headerLength := uint32(9) - if extendedCode > 0 { - headerLength += 4 - opCode = msgChannelExtendedData - } - - c.writeMu.Lock() - packet := c.packetPool[extendedCode] - // We don't remove the buffer from packetPool, so - // WriteExtended calls from different goroutines will be - // flagged as errors by the race detector. - c.writeMu.Unlock() - - for len(data) > 0 { - space := min(c.maxRemotePayload, len(data)) - if space, err = c.remoteWin.reserve(space); err != nil { - return n, err - } - if want := headerLength + space; uint32(cap(packet)) < want { - packet = make([]byte, want) - } else { - packet = packet[:want] - } - - todo := data[:space] - - packet[0] = opCode - binary.BigEndian.PutUint32(packet[1:], c.remoteId) - if extendedCode > 0 { - binary.BigEndian.PutUint32(packet[5:], uint32(extendedCode)) - } - binary.BigEndian.PutUint32(packet[headerLength-4:], uint32(len(todo))) - copy(packet[headerLength:], todo) - if err = c.writePacket(packet); err != nil { - return n, err - } - - n += len(todo) - data = data[len(todo):] - } - - c.writeMu.Lock() - c.packetPool[extendedCode] = packet - c.writeMu.Unlock() - - return n, err -} - -func (c *channel) handleData(packet []byte) error { - headerLen := 9 - isExtendedData := packet[0] == msgChannelExtendedData - if isExtendedData { - headerLen = 13 - } - if len(packet) < headerLen { - // malformed data packet - return parseError(packet[0]) - } - - var extended uint32 - if isExtendedData { - extended = binary.BigEndian.Uint32(packet[5:]) - } - - length := binary.BigEndian.Uint32(packet[headerLen-4 : headerLen]) - if length == 0 { - return nil - } - if length > c.maxIncomingPayload { - // TODO(hanwen): should send Disconnect? - return errors.New("ssh: incoming packet exceeds maximum payload size") - } - - data := packet[headerLen:] - if length != uint32(len(data)) { - return errors.New("ssh: wrong packet length") - } - - c.windowMu.Lock() - if c.myWindow < length { - c.windowMu.Unlock() - // TODO(hanwen): should send Disconnect with reason? - return errors.New("ssh: remote side wrote too much") - } - c.myWindow -= length - c.windowMu.Unlock() - - if extended == 1 { - c.extPending.write(data) - } else if extended > 0 { - // discard other extended data. - } else { - c.pending.write(data) - } - return nil -} - -func (c *channel) adjustWindow(n uint32) error { - c.windowMu.Lock() - // Since myWindow is managed on our side, and can never exceed - // the initial window setting, we don't worry about overflow. - c.myWindow += uint32(n) - c.windowMu.Unlock() - return c.sendMessage(windowAdjustMsg{ - AdditionalBytes: uint32(n), - }) -} - -func (c *channel) ReadExtended(data []byte, extended uint32) (n int, err error) { - switch extended { - case 1: - n, err = c.extPending.Read(data) - case 0: - n, err = c.pending.Read(data) - default: - return 0, fmt.Errorf("ssh: extended code %d unimplemented", extended) - } - - if n > 0 { - err = c.adjustWindow(uint32(n)) - // sendWindowAdjust can return io.EOF if the remote - // peer has closed the connection, however we want to - // defer forwarding io.EOF to the caller of Read until - // the buffer has been drained. - if n > 0 && err == io.EOF { - err = nil - } - } - - return n, err -} - -func (c *channel) close() { - c.pending.eof() - c.extPending.eof() - close(c.msg) - close(c.incomingRequests) - c.writeMu.Lock() - // This is not necesary for a normal channel teardown, but if - // there was another error, it is. - c.sentClose = true - c.writeMu.Unlock() - // Unblock writers. - c.remoteWin.close() -} - -// responseMessageReceived is called when a success or failure message is -// received on a channel to check that such a message is reasonable for the -// given channel. -func (c *channel) responseMessageReceived() error { - if c.direction == channelInbound { - return errors.New("ssh: channel response message received on inbound channel") - } - if c.decided { - return errors.New("ssh: duplicate response received for channel") - } - c.decided = true - return nil -} - -func (c *channel) handlePacket(packet []byte) error { - switch packet[0] { - case msgChannelData, msgChannelExtendedData: - return c.handleData(packet) - case msgChannelClose: - c.sendMessage(channelCloseMsg{PeersId: c.remoteId}) - c.mux.chanList.remove(c.localId) - c.close() - return nil - case msgChannelEOF: - // RFC 4254 is mute on how EOF affects dataExt messages but - // it is logical to signal EOF at the same time. - c.extPending.eof() - c.pending.eof() - return nil - } - - decoded, err := decode(packet) - if err != nil { - return err - } - - switch msg := decoded.(type) { - case *channelOpenFailureMsg: - if err := c.responseMessageReceived(); err != nil { - return err - } - c.mux.chanList.remove(msg.PeersId) - c.msg <- msg - case *channelOpenConfirmMsg: - if err := c.responseMessageReceived(); err != nil { - return err - } - if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 { - return fmt.Errorf("ssh: invalid MaxPacketSize %d from peer", msg.MaxPacketSize) - } - c.remoteId = msg.MyId - c.maxRemotePayload = msg.MaxPacketSize - c.remoteWin.add(msg.MyWindow) - c.msg <- msg - case *windowAdjustMsg: - if !c.remoteWin.add(msg.AdditionalBytes) { - return fmt.Errorf("ssh: invalid window update for %d bytes", msg.AdditionalBytes) - } - case *channelRequestMsg: - req := Request{ - Type: msg.Request, - WantReply: msg.WantReply, - Payload: msg.RequestSpecificData, - ch: c, - } - - c.incomingRequests <- &req - default: - c.msg <- msg - } - return nil -} - -func (m *mux) newChannel(chanType string, direction channelDirection, extraData []byte) *channel { - ch := &channel{ - remoteWin: window{Cond: newCond()}, - myWindow: channelWindowSize, - pending: newBuffer(), - extPending: newBuffer(), - direction: direction, - incomingRequests: make(chan *Request, 16), - msg: make(chan interface{}, 16), - chanType: chanType, - extraData: extraData, - mux: m, - packetPool: make(map[uint32][]byte), - } - ch.localId = m.chanList.add(ch) - return ch -} - -var errUndecided = errors.New("ssh: must Accept or Reject channel") -var errDecidedAlready = errors.New("ssh: can call Accept or Reject only once") - -type extChannel struct { - code uint32 - ch *channel -} - -func (e *extChannel) Write(data []byte) (n int, err error) { - return e.ch.WriteExtended(data, e.code) -} - -func (e *extChannel) Read(data []byte) (n int, err error) { - return e.ch.ReadExtended(data, e.code) -} - -func (c *channel) Accept() (Channel, <-chan *Request, error) { - if c.decided { - return nil, nil, errDecidedAlready - } - c.maxIncomingPayload = channelMaxPacket - confirm := channelOpenConfirmMsg{ - PeersId: c.remoteId, - MyId: c.localId, - MyWindow: c.myWindow, - MaxPacketSize: c.maxIncomingPayload, - } - c.decided = true - if err := c.sendMessage(confirm); err != nil { - return nil, nil, err - } - - return c, c.incomingRequests, nil -} - -func (ch *channel) Reject(reason RejectionReason, message string) error { - if ch.decided { - return errDecidedAlready - } - reject := channelOpenFailureMsg{ - PeersId: ch.remoteId, - Reason: reason, - Message: message, - Language: "en", - } - ch.decided = true - return ch.sendMessage(reject) -} - -func (ch *channel) Read(data []byte) (int, error) { - if !ch.decided { - return 0, errUndecided - } - return ch.ReadExtended(data, 0) -} - -func (ch *channel) Write(data []byte) (int, error) { - if !ch.decided { - return 0, errUndecided - } - return ch.WriteExtended(data, 0) -} - -func (ch *channel) CloseWrite() error { - if !ch.decided { - return errUndecided - } - ch.sentEOF = true - return ch.sendMessage(channelEOFMsg{ - PeersId: ch.remoteId}) -} - -func (ch *channel) Close() error { - if !ch.decided { - return errUndecided - } - - return ch.sendMessage(channelCloseMsg{ - PeersId: ch.remoteId}) -} - -// Extended returns an io.ReadWriter that sends and receives data on the given, -// SSH extended stream. Such streams are used, for example, for stderr. -func (ch *channel) Extended(code uint32) io.ReadWriter { - if !ch.decided { - return nil - } - return &extChannel{code, ch} -} - -func (ch *channel) Stderr() io.ReadWriter { - return ch.Extended(1) -} - -func (ch *channel) SendRequest(name string, wantReply bool, payload []byte) (bool, error) { - if !ch.decided { - return false, errUndecided - } - - if wantReply { - ch.sentRequestMu.Lock() - defer ch.sentRequestMu.Unlock() - } - - msg := channelRequestMsg{ - PeersId: ch.remoteId, - Request: name, - WantReply: wantReply, - RequestSpecificData: payload, - } - - if err := ch.sendMessage(msg); err != nil { - return false, err - } - - if wantReply { - m, ok := (<-ch.msg) - if !ok { - return false, io.EOF - } - switch m.(type) { - case *channelRequestFailureMsg: - return false, nil - case *channelRequestSuccessMsg: - return true, nil - default: - return false, fmt.Errorf("ssh: unexpected response to channel request: %#v", m) - } - } - - return false, nil -} - -// ackRequest either sends an ack or nack to the channel request. -func (ch *channel) ackRequest(ok bool) error { - if !ch.decided { - return errUndecided - } - - var msg interface{} - if !ok { - msg = channelRequestFailureMsg{ - PeersId: ch.remoteId, - } - } else { - msg = channelRequestSuccessMsg{ - PeersId: ch.remoteId, - } - } - return ch.sendMessage(msg) -} - -func (ch *channel) ChannelType() string { - return ch.chanType -} - -func (ch *channel) ExtraData() []byte { - return ch.extraData -} diff --git a/modules/crypto/ssh/cipher.go b/modules/crypto/ssh/cipher.go deleted file mode 100755 index 3e06da0d..00000000 --- a/modules/crypto/ssh/cipher.go +++ /dev/null @@ -1,549 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/rc4" - "crypto/subtle" - "encoding/binary" - "errors" - "fmt" - "hash" - "io" - "io/ioutil" -) - -const ( - packetSizeMultiple = 16 // TODO(huin) this should be determined by the cipher. - - // RFC 4253 section 6.1 defines a minimum packet size of 32768 that implementations - // MUST be able to process (plus a few more kilobytes for padding and mac). The RFC - // indicates implementations SHOULD be able to handle larger packet sizes, but then - // waffles on about reasonable limits. - // - // OpenSSH caps their maxPacket at 256kB so we choose to do - // the same. maxPacket is also used to ensure that uint32 - // length fields do not overflow, so it should remain well - // below 4G. - maxPacket = 256 * 1024 -) - -// noneCipher implements cipher.Stream and provides no encryption. It is used -// by the transport before the first key-exchange. -type noneCipher struct{} - -func (c noneCipher) XORKeyStream(dst, src []byte) { - copy(dst, src) -} - -func newAESCTR(key, iv []byte) (cipher.Stream, error) { - c, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - return cipher.NewCTR(c, iv), nil -} - -func newRC4(key, iv []byte) (cipher.Stream, error) { - return rc4.NewCipher(key) -} - -type streamCipherMode struct { - keySize int - ivSize int - skip int - createFunc func(key, iv []byte) (cipher.Stream, error) -} - -func (c *streamCipherMode) createStream(key, iv []byte) (cipher.Stream, error) { - if len(key) < c.keySize { - panic("ssh: key length too small for cipher") - } - if len(iv) < c.ivSize { - panic("ssh: iv too small for cipher") - } - - stream, err := c.createFunc(key[:c.keySize], iv[:c.ivSize]) - if err != nil { - return nil, err - } - - var streamDump []byte - if c.skip > 0 { - streamDump = make([]byte, 512) - } - - for remainingToDump := c.skip; remainingToDump > 0; { - dumpThisTime := remainingToDump - if dumpThisTime > len(streamDump) { - dumpThisTime = len(streamDump) - } - stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime]) - remainingToDump -= dumpThisTime - } - - return stream, nil -} - -// cipherModes documents properties of supported ciphers. Ciphers not included -// are not supported and will not be negotiated, even if explicitly requested in -// ClientConfig.Crypto.Ciphers. -var cipherModes = map[string]*streamCipherMode{ - // Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms - // are defined in the order specified in the RFC. - "aes128-ctr": {16, aes.BlockSize, 0, newAESCTR}, - "aes192-ctr": {24, aes.BlockSize, 0, newAESCTR}, - "aes256-ctr": {32, aes.BlockSize, 0, newAESCTR}, - - // Ciphers from RFC4345, which introduces security-improved arcfour ciphers. - // They are defined in the order specified in the RFC. - "arcfour128": {16, 0, 1536, newRC4}, - "arcfour256": {32, 0, 1536, newRC4}, - - // Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol. - // Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and - // RC4) has problems with weak keys, and should be used with caution." - // RFC4345 introduces improved versions of Arcfour. - "arcfour": {16, 0, 0, newRC4}, - - // AES-GCM is not a stream cipher, so it is constructed with a - // special case. If we add any more non-stream ciphers, we - // should invest a cleaner way to do this. - gcmCipherID: {16, 12, 0, nil}, - - // insecure cipher, see http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf - // uncomment below to enable it. - // aes128cbcID: {16, aes.BlockSize, 0, nil}, -} - -// prefixLen is the length of the packet prefix that contains the packet length -// and number of padding bytes. -const prefixLen = 5 - -// streamPacketCipher is a packetCipher using a stream cipher. -type streamPacketCipher struct { - mac hash.Hash - cipher cipher.Stream - - // The following members are to avoid per-packet allocations. - prefix [prefixLen]byte - seqNumBytes [4]byte - padding [2 * packetSizeMultiple]byte - packetData []byte - macResult []byte -} - -// readPacket reads and decrypt a single packet from the reader argument. -func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { - if _, err := io.ReadFull(r, s.prefix[:]); err != nil { - return nil, err - } - - s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) - length := binary.BigEndian.Uint32(s.prefix[0:4]) - paddingLength := uint32(s.prefix[4]) - - var macSize uint32 - if s.mac != nil { - s.mac.Reset() - binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) - s.mac.Write(s.seqNumBytes[:]) - s.mac.Write(s.prefix[:]) - macSize = uint32(s.mac.Size()) - } - - if length <= paddingLength+1 { - return nil, errors.New("ssh: invalid packet length, packet too small") - } - - if length > maxPacket { - return nil, errors.New("ssh: invalid packet length, packet too large") - } - - // the maxPacket check above ensures that length-1+macSize - // does not overflow. - if uint32(cap(s.packetData)) < length-1+macSize { - s.packetData = make([]byte, length-1+macSize) - } else { - s.packetData = s.packetData[:length-1+macSize] - } - - if _, err := io.ReadFull(r, s.packetData); err != nil { - return nil, err - } - mac := s.packetData[length-1:] - data := s.packetData[:length-1] - s.cipher.XORKeyStream(data, data) - - if s.mac != nil { - s.mac.Write(data) - s.macResult = s.mac.Sum(s.macResult[:0]) - if subtle.ConstantTimeCompare(s.macResult, mac) != 1 { - return nil, errors.New("ssh: MAC failure") - } - } - - return s.packetData[:length-paddingLength-1], nil -} - -// writePacket encrypts and sends a packet of data to the writer argument -func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { - if len(packet) > maxPacket { - return errors.New("ssh: packet too large") - } - - paddingLength := packetSizeMultiple - (prefixLen+len(packet))%packetSizeMultiple - if paddingLength < 4 { - paddingLength += packetSizeMultiple - } - - length := len(packet) + 1 + paddingLength - binary.BigEndian.PutUint32(s.prefix[:], uint32(length)) - s.prefix[4] = byte(paddingLength) - padding := s.padding[:paddingLength] - if _, err := io.ReadFull(rand, padding); err != nil { - return err - } - - if s.mac != nil { - s.mac.Reset() - binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) - s.mac.Write(s.seqNumBytes[:]) - s.mac.Write(s.prefix[:]) - s.mac.Write(packet) - s.mac.Write(padding) - } - - s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) - s.cipher.XORKeyStream(packet, packet) - s.cipher.XORKeyStream(padding, padding) - - if _, err := w.Write(s.prefix[:]); err != nil { - return err - } - if _, err := w.Write(packet); err != nil { - return err - } - if _, err := w.Write(padding); err != nil { - return err - } - - if s.mac != nil { - s.macResult = s.mac.Sum(s.macResult[:0]) - if _, err := w.Write(s.macResult); err != nil { - return err - } - } - - return nil -} - -type gcmCipher struct { - aead cipher.AEAD - prefix [4]byte - iv []byte - buf []byte -} - -func newGCMCipher(iv, key, macKey []byte) (packetCipher, error) { - c, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - aead, err := cipher.NewGCM(c) - if err != nil { - return nil, err - } - - return &gcmCipher{ - aead: aead, - iv: iv, - }, nil -} - -const gcmTagSize = 16 - -func (c *gcmCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { - // Pad out to multiple of 16 bytes. This is different from the - // stream cipher because that encrypts the length too. - padding := byte(packetSizeMultiple - (1+len(packet))%packetSizeMultiple) - if padding < 4 { - padding += packetSizeMultiple - } - - length := uint32(len(packet) + int(padding) + 1) - binary.BigEndian.PutUint32(c.prefix[:], length) - if _, err := w.Write(c.prefix[:]); err != nil { - return err - } - - if cap(c.buf) < int(length) { - c.buf = make([]byte, length) - } else { - c.buf = c.buf[:length] - } - - c.buf[0] = padding - copy(c.buf[1:], packet) - if _, err := io.ReadFull(rand, c.buf[1+len(packet):]); err != nil { - return err - } - c.buf = c.aead.Seal(c.buf[:0], c.iv, c.buf, c.prefix[:]) - if _, err := w.Write(c.buf); err != nil { - return err - } - c.incIV() - - return nil -} - -func (c *gcmCipher) incIV() { - for i := 4 + 7; i >= 4; i-- { - c.iv[i]++ - if c.iv[i] != 0 { - break - } - } -} - -func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { - if _, err := io.ReadFull(r, c.prefix[:]); err != nil { - return nil, err - } - length := binary.BigEndian.Uint32(c.prefix[:]) - if length > maxPacket { - return nil, errors.New("ssh: max packet length exceeded.") - } - - if cap(c.buf) < int(length+gcmTagSize) { - c.buf = make([]byte, length+gcmTagSize) - } else { - c.buf = c.buf[:length+gcmTagSize] - } - - if _, err := io.ReadFull(r, c.buf); err != nil { - return nil, err - } - - plain, err := c.aead.Open(c.buf[:0], c.iv, c.buf, c.prefix[:]) - if err != nil { - return nil, err - } - c.incIV() - - padding := plain[0] - if padding < 4 || padding >= 20 { - return nil, fmt.Errorf("ssh: illegal padding %d", padding) - } - - if int(padding+1) >= len(plain) { - return nil, fmt.Errorf("ssh: padding %d too large", padding) - } - plain = plain[1 : length-uint32(padding)] - return plain, nil -} - -// cbcCipher implements aes128-cbc cipher defined in RFC 4253 section 6.1 -type cbcCipher struct { - mac hash.Hash - macSize uint32 - decrypter cipher.BlockMode - encrypter cipher.BlockMode - - // The following members are to avoid per-packet allocations. - seqNumBytes [4]byte - packetData []byte - macResult []byte - - // Amount of data we should still read to hide which - // verification error triggered. - oracleCamouflage uint32 -} - -func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) { - c, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - cbc := &cbcCipher{ - mac: macModes[algs.MAC].new(macKey), - decrypter: cipher.NewCBCDecrypter(c, iv), - encrypter: cipher.NewCBCEncrypter(c, iv), - packetData: make([]byte, 1024), - } - if cbc.mac != nil { - cbc.macSize = uint32(cbc.mac.Size()) - } - - return cbc, nil -} - -func maxUInt32(a, b int) uint32 { - if a > b { - return uint32(a) - } - return uint32(b) -} - -const ( - cbcMinPacketSizeMultiple = 8 - cbcMinPacketSize = 16 - cbcMinPaddingSize = 4 -) - -// cbcError represents a verification error that may leak information. -type cbcError string - -func (e cbcError) Error() string { return string(e) } - -func (c *cbcCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { - p, err := c.readPacketLeaky(seqNum, r) - if err != nil { - if _, ok := err.(cbcError); ok { - // Verification error: read a fixed amount of - // data, to make distinguishing between - // failing MAC and failing length check more - // difficult. - io.CopyN(ioutil.Discard, r, int64(c.oracleCamouflage)) - } - } - return p, err -} - -func (c *cbcCipher) readPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) { - blockSize := c.decrypter.BlockSize() - - // Read the header, which will include some of the subsequent data in the - // case of block ciphers - this is copied back to the payload later. - // How many bytes of payload/padding will be read with this first read. - firstBlockLength := uint32((prefixLen + blockSize - 1) / blockSize * blockSize) - firstBlock := c.packetData[:firstBlockLength] - if _, err := io.ReadFull(r, firstBlock); err != nil { - return nil, err - } - - c.oracleCamouflage = maxPacket + 4 + c.macSize - firstBlockLength - - c.decrypter.CryptBlocks(firstBlock, firstBlock) - length := binary.BigEndian.Uint32(firstBlock[:4]) - if length > maxPacket { - return nil, cbcError("ssh: packet too large") - } - if length+4 < maxUInt32(cbcMinPacketSize, blockSize) { - // The minimum size of a packet is 16 (or the cipher block size, whichever - // is larger) bytes. - return nil, cbcError("ssh: packet too small") - } - // The length of the packet (including the length field but not the MAC) must - // be a multiple of the block size or 8, whichever is larger. - if (length+4)%maxUInt32(cbcMinPacketSizeMultiple, blockSize) != 0 { - return nil, cbcError("ssh: invalid packet length multiple") - } - - paddingLength := uint32(firstBlock[4]) - if paddingLength < cbcMinPaddingSize || length <= paddingLength+1 { - return nil, cbcError("ssh: invalid packet length") - } - - // Positions within the c.packetData buffer: - macStart := 4 + length - paddingStart := macStart - paddingLength - - // Entire packet size, starting before length, ending at end of mac. - entirePacketSize := macStart + c.macSize - - // Ensure c.packetData is large enough for the entire packet data. - if uint32(cap(c.packetData)) < entirePacketSize { - // Still need to upsize and copy, but this should be rare at runtime, only - // on upsizing the packetData buffer. - c.packetData = make([]byte, entirePacketSize) - copy(c.packetData, firstBlock) - } else { - c.packetData = c.packetData[:entirePacketSize] - } - - if n, err := io.ReadFull(r, c.packetData[firstBlockLength:]); err != nil { - return nil, err - } else { - c.oracleCamouflage -= uint32(n) - } - - remainingCrypted := c.packetData[firstBlockLength:macStart] - c.decrypter.CryptBlocks(remainingCrypted, remainingCrypted) - - mac := c.packetData[macStart:] - if c.mac != nil { - c.mac.Reset() - binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum) - c.mac.Write(c.seqNumBytes[:]) - c.mac.Write(c.packetData[:macStart]) - c.macResult = c.mac.Sum(c.macResult[:0]) - if subtle.ConstantTimeCompare(c.macResult, mac) != 1 { - return nil, cbcError("ssh: MAC failure") - } - } - - return c.packetData[prefixLen:paddingStart], nil -} - -func (c *cbcCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { - effectiveBlockSize := maxUInt32(cbcMinPacketSizeMultiple, c.encrypter.BlockSize()) - - // Length of encrypted portion of the packet (header, payload, padding). - // Enforce minimum padding and packet size. - encLength := maxUInt32(prefixLen+len(packet)+cbcMinPaddingSize, cbcMinPaddingSize) - // Enforce block size. - encLength = (encLength + effectiveBlockSize - 1) / effectiveBlockSize * effectiveBlockSize - - length := encLength - 4 - paddingLength := int(length) - (1 + len(packet)) - - // Overall buffer contains: header, payload, padding, mac. - // Space for the MAC is reserved in the capacity but not the slice length. - bufferSize := encLength + c.macSize - if uint32(cap(c.packetData)) < bufferSize { - c.packetData = make([]byte, encLength, bufferSize) - } else { - c.packetData = c.packetData[:encLength] - } - - p := c.packetData - - // Packet header. - binary.BigEndian.PutUint32(p, length) - p = p[4:] - p[0] = byte(paddingLength) - - // Payload. - p = p[1:] - copy(p, packet) - - // Padding. - p = p[len(packet):] - if _, err := io.ReadFull(rand, p); err != nil { - return err - } - - if c.mac != nil { - c.mac.Reset() - binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum) - c.mac.Write(c.seqNumBytes[:]) - c.mac.Write(c.packetData) - // The MAC is now appended into the capacity reserved for it earlier. - c.packetData = c.mac.Sum(c.packetData) - } - - c.encrypter.CryptBlocks(c.packetData[:encLength], c.packetData[:encLength]) - - if _, err := w.Write(c.packetData); err != nil { - return err - } - - return nil -} diff --git a/modules/crypto/ssh/cipher_test.go b/modules/crypto/ssh/cipher_test.go deleted file mode 100755 index 54b92b6e..00000000 --- a/modules/crypto/ssh/cipher_test.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bytes" - "crypto" - "crypto/aes" - "crypto/rand" - "testing" -) - -func TestDefaultCiphersExist(t *testing.T) { - for _, cipherAlgo := range supportedCiphers { - if _, ok := cipherModes[cipherAlgo]; !ok { - t.Errorf("default cipher %q is unknown", cipherAlgo) - } - } -} - -func TestPacketCiphers(t *testing.T) { - // Still test aes128cbc cipher althought it's commented out. - cipherModes[aes128cbcID] = &streamCipherMode{16, aes.BlockSize, 0, nil} - defer delete(cipherModes, aes128cbcID) - - for cipher := range cipherModes { - kr := &kexResult{Hash: crypto.SHA1} - algs := directionAlgorithms{ - Cipher: cipher, - MAC: "hmac-sha1", - Compression: "none", - } - client, err := newPacketCipher(clientKeys, algs, kr) - if err != nil { - t.Errorf("newPacketCipher(client, %q): %v", cipher, err) - continue - } - server, err := newPacketCipher(clientKeys, algs, kr) - if err != nil { - t.Errorf("newPacketCipher(client, %q): %v", cipher, err) - continue - } - - want := "bla bla" - input := []byte(want) - buf := &bytes.Buffer{} - if err := client.writePacket(0, buf, rand.Reader, input); err != nil { - t.Errorf("writePacket(%q): %v", cipher, err) - continue - } - - packet, err := server.readPacket(0, buf) - if err != nil { - t.Errorf("readPacket(%q): %v", cipher, err) - continue - } - - if string(packet) != want { - t.Errorf("roundtrip(%q): got %q, want %q", cipher, packet, want) - } - } -} - -func TestCBCOracleCounterMeasure(t *testing.T) { - cipherModes[aes128cbcID] = &streamCipherMode{16, aes.BlockSize, 0, nil} - defer delete(cipherModes, aes128cbcID) - - kr := &kexResult{Hash: crypto.SHA1} - algs := directionAlgorithms{ - Cipher: aes128cbcID, - MAC: "hmac-sha1", - Compression: "none", - } - client, err := newPacketCipher(clientKeys, algs, kr) - if err != nil { - t.Fatalf("newPacketCipher(client): %v", err) - } - - want := "bla bla" - input := []byte(want) - buf := &bytes.Buffer{} - if err := client.writePacket(0, buf, rand.Reader, input); err != nil { - t.Errorf("writePacket: %v", err) - } - - packetSize := buf.Len() - buf.Write(make([]byte, 2*maxPacket)) - - // We corrupt each byte, but this usually will only test the - // 'packet too large' or 'MAC failure' cases. - lastRead := -1 - for i := 0; i < packetSize; i++ { - server, err := newPacketCipher(clientKeys, algs, kr) - if err != nil { - t.Fatalf("newPacketCipher(client): %v", err) - } - - fresh := &bytes.Buffer{} - fresh.Write(buf.Bytes()) - fresh.Bytes()[i] ^= 0x01 - - before := fresh.Len() - _, err = server.readPacket(0, fresh) - if err == nil { - t.Errorf("corrupt byte %d: readPacket succeeded ", i) - continue - } - if _, ok := err.(cbcError); !ok { - t.Errorf("corrupt byte %d: got %v (%T), want cbcError", i, err, err) - continue - } - - after := fresh.Len() - bytesRead := before - after - if bytesRead < maxPacket { - t.Errorf("corrupt byte %d: read %d bytes, want more than %d", i, bytesRead, maxPacket) - continue - } - - if i > 0 && bytesRead != lastRead { - t.Errorf("corrupt byte %d: read %d bytes, want %d bytes read", i, bytesRead, lastRead) - } - lastRead = bytesRead - } -} diff --git a/modules/crypto/ssh/client.go b/modules/crypto/ssh/client.go deleted file mode 100755 index 0b9fbe50..00000000 --- a/modules/crypto/ssh/client.go +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "errors" - "fmt" - "net" - "sync" -) - -// Client implements a traditional SSH client that supports shells, -// subprocesses, port forwarding and tunneled dialing. -type Client struct { - Conn - - forwards forwardList // forwarded tcpip connections from the remote side - mu sync.Mutex - channelHandlers map[string]chan NewChannel -} - -// HandleChannelOpen returns a channel on which NewChannel requests -// for the given type are sent. If the type already is being handled, -// nil is returned. The channel is closed when the connection is closed. -func (c *Client) HandleChannelOpen(channelType string) <-chan NewChannel { - c.mu.Lock() - defer c.mu.Unlock() - if c.channelHandlers == nil { - // The SSH channel has been closed. - c := make(chan NewChannel) - close(c) - return c - } - - ch := c.channelHandlers[channelType] - if ch != nil { - return nil - } - - ch = make(chan NewChannel, 16) - c.channelHandlers[channelType] = ch - return ch -} - -// NewClient creates a Client on top of the given connection. -func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client { - conn := &Client{ - Conn: c, - channelHandlers: make(map[string]chan NewChannel, 1), - } - - go conn.handleGlobalRequests(reqs) - go conn.handleChannelOpens(chans) - go func() { - conn.Wait() - conn.forwards.closeAll() - }() - go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-tcpip")) - return conn -} - -// NewClientConn establishes an authenticated SSH connection using c -// as the underlying transport. The Request and NewChannel channels -// must be serviced or the connection will hang. -func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan NewChannel, <-chan *Request, error) { - fullConf := *config - fullConf.SetDefaults() - conn := &connection{ - sshConn: sshConn{conn: c}, - } - - if err := conn.clientHandshake(addr, &fullConf); err != nil { - c.Close() - return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %v", err) - } - conn.mux = newMux(conn.transport) - return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil -} - -// clientHandshake performs the client side key exchange. See RFC 4253 Section -// 7. -func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) error { - if config.ClientVersion != "" { - c.clientVersion = []byte(config.ClientVersion) - } else { - c.clientVersion = []byte(packageVersion) - } - var err error - c.serverVersion, err = exchangeVersions(c.sshConn.conn, c.clientVersion) - if err != nil { - return err - } - - c.transport = newClientTransport( - newTransport(c.sshConn.conn, config.Rand, true /* is client */), - c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr()) - if err := c.transport.requestKeyChange(); err != nil { - return err - } - - if packet, err := c.transport.readPacket(); err != nil { - return err - } else if packet[0] != msgNewKeys { - return unexpectedMessageError(msgNewKeys, packet[0]) - } - - // We just did the key change, so the session ID is established. - c.sessionID = c.transport.getSessionID() - - return c.clientAuthenticate(config) -} - -// verifyHostKeySignature verifies the host key obtained in the key -// exchange. -func verifyHostKeySignature(hostKey PublicKey, result *kexResult) error { - sig, rest, ok := parseSignatureBody(result.Signature) - if len(rest) > 0 || !ok { - return errors.New("ssh: signature parse error") - } - - return hostKey.Verify(result.H, sig) -} - -// NewSession opens a new Session for this client. (A session is a remote -// execution of a program.) -func (c *Client) NewSession() (*Session, error) { - ch, in, err := c.OpenChannel("session", nil) - if err != nil { - return nil, err - } - return newSession(ch, in) -} - -func (c *Client) handleGlobalRequests(incoming <-chan *Request) { - for r := range incoming { - // This handles keepalive messages and matches - // the behaviour of OpenSSH. - r.Reply(false, nil) - } -} - -// handleChannelOpens channel open messages from the remote side. -func (c *Client) handleChannelOpens(in <-chan NewChannel) { - for ch := range in { - c.mu.Lock() - handler := c.channelHandlers[ch.ChannelType()] - c.mu.Unlock() - - if handler != nil { - handler <- ch - } else { - ch.Reject(UnknownChannelType, fmt.Sprintf("unknown channel type: %v", ch.ChannelType())) - } - } - - c.mu.Lock() - for _, ch := range c.channelHandlers { - close(ch) - } - c.channelHandlers = nil - c.mu.Unlock() -} - -// Dial starts a client connection to the given SSH server. It is a -// convenience function that connects to the given network address, -// initiates the SSH handshake, and then sets up a Client. For access -// to incoming channels and requests, use net.Dial with NewClientConn -// instead. -func Dial(network, addr string, config *ClientConfig) (*Client, error) { - conn, err := net.Dial(network, addr) - if err != nil { - return nil, err - } - c, chans, reqs, err := NewClientConn(conn, addr, config) - if err != nil { - return nil, err - } - return NewClient(c, chans, reqs), nil -} - -// A ClientConfig structure is used to configure a Client. It must not be -// modified after having been passed to an SSH function. -type ClientConfig struct { - // Config contains configuration that is shared between clients and - // servers. - Config - - // User contains the username to authenticate as. - User string - - // Auth contains possible authentication methods to use with the - // server. Only the first instance of a particular RFC 4252 method will - // be used during authentication. - Auth []AuthMethod - - // HostKeyCallback, if not nil, is called during the cryptographic - // handshake to validate the server's host key. A nil HostKeyCallback - // implies that all host keys are accepted. - HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error - - // ClientVersion contains the version identification string that will - // be used for the connection. If empty, a reasonable default is used. - ClientVersion string - - // HostKeyAlgorithms lists the key types that the client will - // accept from the server as host key, in order of - // preference. If empty, a reasonable default is used. Any - // string returned from PublicKey.Type method may be used, or - // any of the CertAlgoXxxx and KeyAlgoXxxx constants. - HostKeyAlgorithms []string -} diff --git a/modules/crypto/ssh/client_auth.go b/modules/crypto/ssh/client_auth.go deleted file mode 100755 index e15be3ef..00000000 --- a/modules/crypto/ssh/client_auth.go +++ /dev/null @@ -1,441 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bytes" - "errors" - "fmt" - "io" -) - -// clientAuthenticate authenticates with the remote server. See RFC 4252. -func (c *connection) clientAuthenticate(config *ClientConfig) error { - // initiate user auth session - if err := c.transport.writePacket(Marshal(&serviceRequestMsg{serviceUserAuth})); err != nil { - return err - } - packet, err := c.transport.readPacket() - if err != nil { - return err - } - var serviceAccept serviceAcceptMsg - if err := Unmarshal(packet, &serviceAccept); err != nil { - return err - } - - // during the authentication phase the client first attempts the "none" method - // then any untried methods suggested by the server. - tried := make(map[string]bool) - var lastMethods []string - for auth := AuthMethod(new(noneAuth)); auth != nil; { - ok, methods, err := auth.auth(c.transport.getSessionID(), config.User, c.transport, config.Rand) - if err != nil { - return err - } - if ok { - // success - return nil - } - tried[auth.method()] = true - if methods == nil { - methods = lastMethods - } - lastMethods = methods - - auth = nil - - findNext: - for _, a := range config.Auth { - candidateMethod := a.method() - if tried[candidateMethod] { - continue - } - for _, meth := range methods { - if meth == candidateMethod { - auth = a - break findNext - } - } - } - } - return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", keys(tried)) -} - -func keys(m map[string]bool) []string { - s := make([]string, 0, len(m)) - - for key := range m { - s = append(s, key) - } - return s -} - -// An AuthMethod represents an instance of an RFC 4252 authentication method. -type AuthMethod interface { - // auth authenticates user over transport t. - // Returns true if authentication is successful. - // If authentication is not successful, a []string of alternative - // method names is returned. If the slice is nil, it will be ignored - // and the previous set of possible methods will be reused. - auth(session []byte, user string, p packetConn, rand io.Reader) (bool, []string, error) - - // method returns the RFC 4252 method name. - method() string -} - -// "none" authentication, RFC 4252 section 5.2. -type noneAuth int - -func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { - if err := c.writePacket(Marshal(&userAuthRequestMsg{ - User: user, - Service: serviceSSH, - Method: "none", - })); err != nil { - return false, nil, err - } - - return handleAuthResponse(c) -} - -func (n *noneAuth) method() string { - return "none" -} - -// passwordCallback is an AuthMethod that fetches the password through -// a function call, e.g. by prompting the user. -type passwordCallback func() (password string, err error) - -func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { - type passwordAuthMsg struct { - User string `sshtype:"50"` - Service string - Method string - Reply bool - Password string - } - - pw, err := cb() - // REVIEW NOTE: is there a need to support skipping a password attempt? - // The program may only find out that the user doesn't have a password - // when prompting. - if err != nil { - return false, nil, err - } - - if err := c.writePacket(Marshal(&passwordAuthMsg{ - User: user, - Service: serviceSSH, - Method: cb.method(), - Reply: false, - Password: pw, - })); err != nil { - return false, nil, err - } - - return handleAuthResponse(c) -} - -func (cb passwordCallback) method() string { - return "password" -} - -// Password returns an AuthMethod using the given password. -func Password(secret string) AuthMethod { - return passwordCallback(func() (string, error) { return secret, nil }) -} - -// PasswordCallback returns an AuthMethod that uses a callback for -// fetching a password. -func PasswordCallback(prompt func() (secret string, err error)) AuthMethod { - return passwordCallback(prompt) -} - -type publickeyAuthMsg struct { - User string `sshtype:"50"` - Service string - Method string - // HasSig indicates to the receiver packet that the auth request is signed and - // should be used for authentication of the request. - HasSig bool - Algoname string - PubKey []byte - // Sig is tagged with "rest" so Marshal will exclude it during - // validateKey - Sig []byte `ssh:"rest"` -} - -// publicKeyCallback is an AuthMethod that uses a set of key -// pairs for authentication. -type publicKeyCallback func() ([]Signer, error) - -func (cb publicKeyCallback) method() string { - return "publickey" -} - -func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { - // Authentication is performed in two stages. The first stage sends an - // enquiry to test if each key is acceptable to the remote. The second - // stage attempts to authenticate with the valid keys obtained in the - // first stage. - - signers, err := cb() - if err != nil { - return false, nil, err - } - var validKeys []Signer - for _, signer := range signers { - if ok, err := validateKey(signer.PublicKey(), user, c); ok { - validKeys = append(validKeys, signer) - } else { - if err != nil { - return false, nil, err - } - } - } - - // methods that may continue if this auth is not successful. - var methods []string - for _, signer := range validKeys { - pub := signer.PublicKey() - - pubKey := pub.Marshal() - sign, err := signer.Sign(rand, buildDataSignedForAuth(session, userAuthRequestMsg{ - User: user, - Service: serviceSSH, - Method: cb.method(), - }, []byte(pub.Type()), pubKey)) - if err != nil { - return false, nil, err - } - - // manually wrap the serialized signature in a string - s := Marshal(sign) - sig := make([]byte, stringLength(len(s))) - marshalString(sig, s) - msg := publickeyAuthMsg{ - User: user, - Service: serviceSSH, - Method: cb.method(), - HasSig: true, - Algoname: pub.Type(), - PubKey: pubKey, - Sig: sig, - } - p := Marshal(&msg) - if err := c.writePacket(p); err != nil { - return false, nil, err - } - var success bool - success, methods, err = handleAuthResponse(c) - if err != nil { - return false, nil, err - } - if success { - return success, methods, err - } - } - return false, methods, nil -} - -// validateKey validates the key provided is acceptable to the server. -func validateKey(key PublicKey, user string, c packetConn) (bool, error) { - pubKey := key.Marshal() - msg := publickeyAuthMsg{ - User: user, - Service: serviceSSH, - Method: "publickey", - HasSig: false, - Algoname: key.Type(), - PubKey: pubKey, - } - if err := c.writePacket(Marshal(&msg)); err != nil { - return false, err - } - - return confirmKeyAck(key, c) -} - -func confirmKeyAck(key PublicKey, c packetConn) (bool, error) { - pubKey := key.Marshal() - algoname := key.Type() - - for { - packet, err := c.readPacket() - if err != nil { - return false, err - } - switch packet[0] { - case msgUserAuthBanner: - // TODO(gpaul): add callback to present the banner to the user - case msgUserAuthPubKeyOk: - var msg userAuthPubKeyOkMsg - if err := Unmarshal(packet, &msg); err != nil { - return false, err - } - if msg.Algo != algoname || !bytes.Equal(msg.PubKey, pubKey) { - return false, nil - } - return true, nil - case msgUserAuthFailure: - return false, nil - default: - return false, unexpectedMessageError(msgUserAuthSuccess, packet[0]) - } - } -} - -// PublicKeys returns an AuthMethod that uses the given key -// pairs. -func PublicKeys(signers ...Signer) AuthMethod { - return publicKeyCallback(func() ([]Signer, error) { return signers, nil }) -} - -// PublicKeysCallback returns an AuthMethod that runs the given -// function to obtain a list of key pairs. -func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMethod { - return publicKeyCallback(getSigners) -} - -// handleAuthResponse returns whether the preceding authentication request succeeded -// along with a list of remaining authentication methods to try next and -// an error if an unexpected response was received. -func handleAuthResponse(c packetConn) (bool, []string, error) { - for { - packet, err := c.readPacket() - if err != nil { - return false, nil, err - } - - switch packet[0] { - case msgUserAuthBanner: - // TODO: add callback to present the banner to the user - case msgUserAuthFailure: - var msg userAuthFailureMsg - if err := Unmarshal(packet, &msg); err != nil { - return false, nil, err - } - return false, msg.Methods, nil - case msgUserAuthSuccess: - return true, nil, nil - case msgDisconnect: - return false, nil, io.EOF - default: - return false, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0]) - } - } -} - -// KeyboardInteractiveChallenge should print questions, optionally -// disabling echoing (e.g. for passwords), and return all the answers. -// Challenge may be called multiple times in a single session. After -// successful authentication, the server may send a challenge with no -// questions, for which the user and instruction messages should be -// printed. RFC 4256 section 3.3 details how the UI should behave for -// both CLI and GUI environments. -type KeyboardInteractiveChallenge func(user, instruction string, questions []string, echos []bool) (answers []string, err error) - -// KeyboardInteractive returns a AuthMethod using a prompt/response -// sequence controlled by the server. -func KeyboardInteractive(challenge KeyboardInteractiveChallenge) AuthMethod { - return challenge -} - -func (cb KeyboardInteractiveChallenge) method() string { - return "keyboard-interactive" -} - -func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { - type initiateMsg struct { - User string `sshtype:"50"` - Service string - Method string - Language string - Submethods string - } - - if err := c.writePacket(Marshal(&initiateMsg{ - User: user, - Service: serviceSSH, - Method: "keyboard-interactive", - })); err != nil { - return false, nil, err - } - - for { - packet, err := c.readPacket() - if err != nil { - return false, nil, err - } - - // like handleAuthResponse, but with less options. - switch packet[0] { - case msgUserAuthBanner: - // TODO: Print banners during userauth. - continue - case msgUserAuthInfoRequest: - // OK - case msgUserAuthFailure: - var msg userAuthFailureMsg - if err := Unmarshal(packet, &msg); err != nil { - return false, nil, err - } - return false, msg.Methods, nil - case msgUserAuthSuccess: - return true, nil, nil - default: - return false, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0]) - } - - var msg userAuthInfoRequestMsg - if err := Unmarshal(packet, &msg); err != nil { - return false, nil, err - } - - // Manually unpack the prompt/echo pairs. - rest := msg.Prompts - var prompts []string - var echos []bool - for i := 0; i < int(msg.NumPrompts); i++ { - prompt, r, ok := parseString(rest) - if !ok || len(r) == 0 { - return false, nil, errors.New("ssh: prompt format error") - } - prompts = append(prompts, string(prompt)) - echos = append(echos, r[0] != 0) - rest = r[1:] - } - - if len(rest) != 0 { - return false, nil, errors.New("ssh: extra data following keyboard-interactive pairs") - } - - answers, err := cb(msg.User, msg.Instruction, prompts, echos) - if err != nil { - return false, nil, err - } - - if len(answers) != len(prompts) { - return false, nil, errors.New("ssh: not enough answers from keyboard-interactive callback") - } - responseLength := 1 + 4 - for _, a := range answers { - responseLength += stringLength(len(a)) - } - serialized := make([]byte, responseLength) - p := serialized - p[0] = msgUserAuthInfoResponse - p = p[1:] - p = marshalUint32(p, uint32(len(answers))) - for _, a := range answers { - p = marshalString(p, []byte(a)) - } - - if err := c.writePacket(serialized); err != nil { - return false, nil, err - } - } -} diff --git a/modules/crypto/ssh/client_auth_test.go b/modules/crypto/ssh/client_auth_test.go deleted file mode 100755 index 2ea44624..00000000 --- a/modules/crypto/ssh/client_auth_test.go +++ /dev/null @@ -1,393 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bytes" - "crypto/rand" - "errors" - "fmt" - "strings" - "testing" -) - -type keyboardInteractive map[string]string - -func (cr keyboardInteractive) Challenge(user string, instruction string, questions []string, echos []bool) ([]string, error) { - var answers []string - for _, q := range questions { - answers = append(answers, cr[q]) - } - return answers, nil -} - -// reused internally by tests -var clientPassword = "tiger" - -// tryAuth runs a handshake with a given config against an SSH server -// with config serverConfig -func tryAuth(t *testing.T, config *ClientConfig) error { - c1, c2, err := netPipe() - if err != nil { - t.Fatalf("netPipe: %v", err) - } - defer c1.Close() - defer c2.Close() - - certChecker := CertChecker{ - IsAuthority: func(k PublicKey) bool { - return bytes.Equal(k.Marshal(), testPublicKeys["ecdsa"].Marshal()) - }, - UserKeyFallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) { - if conn.User() == "testuser" && bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) { - return nil, nil - } - - return nil, fmt.Errorf("pubkey for %q not acceptable", conn.User()) - }, - IsRevoked: func(c *Certificate) bool { - return c.Serial == 666 - }, - } - - serverConfig := &ServerConfig{ - PasswordCallback: func(conn ConnMetadata, pass []byte) (*Permissions, error) { - if conn.User() == "testuser" && string(pass) == clientPassword { - return nil, nil - } - return nil, errors.New("password auth failed") - }, - PublicKeyCallback: certChecker.Authenticate, - KeyboardInteractiveCallback: func(conn ConnMetadata, challenge KeyboardInteractiveChallenge) (*Permissions, error) { - ans, err := challenge("user", - "instruction", - []string{"question1", "question2"}, - []bool{true, true}) - if err != nil { - return nil, err - } - ok := conn.User() == "testuser" && ans[0] == "answer1" && ans[1] == "answer2" - if ok { - challenge("user", "motd", nil, nil) - return nil, nil - } - return nil, errors.New("keyboard-interactive failed") - }, - AuthLogCallback: func(conn ConnMetadata, method string, err error) { - t.Logf("user %q, method %q: %v", conn.User(), method, err) - }, - } - serverConfig.AddHostKey(testSigners["rsa"]) - - go newServer(c1, serverConfig) - _, _, _, err = NewClientConn(c2, "", config) - return err -} - -func TestClientAuthPublicKey(t *testing.T) { - config := &ClientConfig{ - User: "testuser", - Auth: []AuthMethod{ - PublicKeys(testSigners["rsa"]), - }, - } - if err := tryAuth(t, config); err != nil { - t.Fatalf("unable to dial remote side: %s", err) - } -} - -func TestAuthMethodPassword(t *testing.T) { - config := &ClientConfig{ - User: "testuser", - Auth: []AuthMethod{ - Password(clientPassword), - }, - } - - if err := tryAuth(t, config); err != nil { - t.Fatalf("unable to dial remote side: %s", err) - } -} - -func TestAuthMethodFallback(t *testing.T) { - var passwordCalled bool - config := &ClientConfig{ - User: "testuser", - Auth: []AuthMethod{ - PublicKeys(testSigners["rsa"]), - PasswordCallback( - func() (string, error) { - passwordCalled = true - return "WRONG", nil - }), - }, - } - - if err := tryAuth(t, config); err != nil { - t.Fatalf("unable to dial remote side: %s", err) - } - - if passwordCalled { - t.Errorf("password auth tried before public-key auth.") - } -} - -func TestAuthMethodWrongPassword(t *testing.T) { - config := &ClientConfig{ - User: "testuser", - Auth: []AuthMethod{ - Password("wrong"), - PublicKeys(testSigners["rsa"]), - }, - } - - if err := tryAuth(t, config); err != nil { - t.Fatalf("unable to dial remote side: %s", err) - } -} - -func TestAuthMethodKeyboardInteractive(t *testing.T) { - answers := keyboardInteractive(map[string]string{ - "question1": "answer1", - "question2": "answer2", - }) - config := &ClientConfig{ - User: "testuser", - Auth: []AuthMethod{ - KeyboardInteractive(answers.Challenge), - }, - } - - if err := tryAuth(t, config); err != nil { - t.Fatalf("unable to dial remote side: %s", err) - } -} - -func TestAuthMethodWrongKeyboardInteractive(t *testing.T) { - answers := keyboardInteractive(map[string]string{ - "question1": "answer1", - "question2": "WRONG", - }) - config := &ClientConfig{ - User: "testuser", - Auth: []AuthMethod{ - KeyboardInteractive(answers.Challenge), - }, - } - - if err := tryAuth(t, config); err == nil { - t.Fatalf("wrong answers should not have authenticated with KeyboardInteractive") - } -} - -// the mock server will only authenticate ssh-rsa keys -func TestAuthMethodInvalidPublicKey(t *testing.T) { - config := &ClientConfig{ - User: "testuser", - Auth: []AuthMethod{ - PublicKeys(testSigners["dsa"]), - }, - } - - if err := tryAuth(t, config); err == nil { - t.Fatalf("dsa private key should not have authenticated with rsa public key") - } -} - -// the client should authenticate with the second key -func TestAuthMethodRSAandDSA(t *testing.T) { - config := &ClientConfig{ - User: "testuser", - Auth: []AuthMethod{ - PublicKeys(testSigners["dsa"], testSigners["rsa"]), - }, - } - if err := tryAuth(t, config); err != nil { - t.Fatalf("client could not authenticate with rsa key: %v", err) - } -} - -func TestClientHMAC(t *testing.T) { - for _, mac := range supportedMACs { - config := &ClientConfig{ - User: "testuser", - Auth: []AuthMethod{ - PublicKeys(testSigners["rsa"]), - }, - Config: Config{ - MACs: []string{mac}, - }, - } - if err := tryAuth(t, config); err != nil { - t.Fatalf("client could not authenticate with mac algo %s: %v", mac, err) - } - } -} - -// issue 4285. -func TestClientUnsupportedCipher(t *testing.T) { - config := &ClientConfig{ - User: "testuser", - Auth: []AuthMethod{ - PublicKeys(), - }, - Config: Config{ - Ciphers: []string{"aes128-cbc"}, // not currently supported - }, - } - if err := tryAuth(t, config); err == nil { - t.Errorf("expected no ciphers in common") - } -} - -func TestClientUnsupportedKex(t *testing.T) { - config := &ClientConfig{ - User: "testuser", - Auth: []AuthMethod{ - PublicKeys(), - }, - Config: Config{ - KeyExchanges: []string{"diffie-hellman-group-exchange-sha256"}, // not currently supported - }, - } - if err := tryAuth(t, config); err == nil || !strings.Contains(err.Error(), "common algorithm") { - t.Errorf("got %v, expected 'common algorithm'", err) - } -} - -func TestClientLoginCert(t *testing.T) { - cert := &Certificate{ - Key: testPublicKeys["rsa"], - ValidBefore: CertTimeInfinity, - CertType: UserCert, - } - cert.SignCert(rand.Reader, testSigners["ecdsa"]) - certSigner, err := NewCertSigner(cert, testSigners["rsa"]) - if err != nil { - t.Fatalf("NewCertSigner: %v", err) - } - - clientConfig := &ClientConfig{ - User: "user", - } - clientConfig.Auth = append(clientConfig.Auth, PublicKeys(certSigner)) - - t.Log("should succeed") - if err := tryAuth(t, clientConfig); err != nil { - t.Errorf("cert login failed: %v", err) - } - - t.Log("corrupted signature") - cert.Signature.Blob[0]++ - if err := tryAuth(t, clientConfig); err == nil { - t.Errorf("cert login passed with corrupted sig") - } - - t.Log("revoked") - cert.Serial = 666 - cert.SignCert(rand.Reader, testSigners["ecdsa"]) - if err := tryAuth(t, clientConfig); err == nil { - t.Errorf("revoked cert login succeeded") - } - cert.Serial = 1 - - t.Log("sign with wrong key") - cert.SignCert(rand.Reader, testSigners["dsa"]) - if err := tryAuth(t, clientConfig); err == nil { - t.Errorf("cert login passed with non-authoritive key") - } - - t.Log("host cert") - cert.CertType = HostCert - cert.SignCert(rand.Reader, testSigners["ecdsa"]) - if err := tryAuth(t, clientConfig); err == nil { - t.Errorf("cert login passed with wrong type") - } - cert.CertType = UserCert - - t.Log("principal specified") - cert.ValidPrincipals = []string{"user"} - cert.SignCert(rand.Reader, testSigners["ecdsa"]) - if err := tryAuth(t, clientConfig); err != nil { - t.Errorf("cert login failed: %v", err) - } - - t.Log("wrong principal specified") - cert.ValidPrincipals = []string{"fred"} - cert.SignCert(rand.Reader, testSigners["ecdsa"]) - if err := tryAuth(t, clientConfig); err == nil { - t.Errorf("cert login passed with wrong principal") - } - cert.ValidPrincipals = nil - - t.Log("added critical option") - cert.CriticalOptions = map[string]string{"root-access": "yes"} - cert.SignCert(rand.Reader, testSigners["ecdsa"]) - if err := tryAuth(t, clientConfig); err == nil { - t.Errorf("cert login passed with unrecognized critical option") - } - - t.Log("allowed source address") - cert.CriticalOptions = map[string]string{"source-address": "127.0.0.42/24"} - cert.SignCert(rand.Reader, testSigners["ecdsa"]) - if err := tryAuth(t, clientConfig); err != nil { - t.Errorf("cert login with source-address failed: %v", err) - } - - t.Log("disallowed source address") - cert.CriticalOptions = map[string]string{"source-address": "127.0.0.42"} - cert.SignCert(rand.Reader, testSigners["ecdsa"]) - if err := tryAuth(t, clientConfig); err == nil { - t.Errorf("cert login with source-address succeeded") - } -} - -func testPermissionsPassing(withPermissions bool, t *testing.T) { - serverConfig := &ServerConfig{ - PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) { - if conn.User() == "nopermissions" { - return nil, nil - } else { - return &Permissions{}, nil - } - }, - } - serverConfig.AddHostKey(testSigners["rsa"]) - - clientConfig := &ClientConfig{ - Auth: []AuthMethod{ - PublicKeys(testSigners["rsa"]), - }, - } - if withPermissions { - clientConfig.User = "permissions" - } else { - clientConfig.User = "nopermissions" - } - - c1, c2, err := netPipe() - if err != nil { - t.Fatalf("netPipe: %v", err) - } - defer c1.Close() - defer c2.Close() - - go NewClientConn(c2, "", clientConfig) - serverConn, err := newServer(c1, serverConfig) - if err != nil { - t.Fatal(err) - } - if p := serverConn.Permissions; (p != nil) != withPermissions { - t.Fatalf("withPermissions is %t, but Permissions object is %#v", withPermissions, p) - } -} - -func TestPermissionsPassing(t *testing.T) { - testPermissionsPassing(true, t) -} - -func TestNoPermissionsPassing(t *testing.T) { - testPermissionsPassing(false, t) -} diff --git a/modules/crypto/ssh/client_test.go b/modules/crypto/ssh/client_test.go deleted file mode 100755 index 1fe790cb..00000000 --- a/modules/crypto/ssh/client_test.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "net" - "testing" -) - -func testClientVersion(t *testing.T, config *ClientConfig, expected string) { - clientConn, serverConn := net.Pipe() - defer clientConn.Close() - receivedVersion := make(chan string, 1) - go func() { - version, err := readVersion(serverConn) - if err != nil { - receivedVersion <- "" - } else { - receivedVersion <- string(version) - } - serverConn.Close() - }() - NewClientConn(clientConn, "", config) - actual := <-receivedVersion - if actual != expected { - t.Fatalf("got %s; want %s", actual, expected) - } -} - -func TestCustomClientVersion(t *testing.T) { - version := "Test-Client-Version-0.0" - testClientVersion(t, &ClientConfig{ClientVersion: version}, version) -} - -func TestDefaultClientVersion(t *testing.T) { - testClientVersion(t, &ClientConfig{}, packageVersion) -} diff --git a/modules/crypto/ssh/common.go b/modules/crypto/ssh/common.go deleted file mode 100755 index 9fc739e1..00000000 --- a/modules/crypto/ssh/common.go +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "crypto" - "crypto/rand" - "fmt" - "io" - "sync" - - _ "crypto/sha1" - _ "crypto/sha256" - _ "crypto/sha512" -) - -// These are string constants in the SSH protocol. -const ( - compressionNone = "none" - serviceUserAuth = "ssh-userauth" - serviceSSH = "ssh-connection" -) - -// supportedCiphers specifies the supported ciphers in preference order. -var supportedCiphers = []string{ - "aes128-ctr", "aes192-ctr", "aes256-ctr", - "aes128-gcm@openssh.com", - "arcfour256", "arcfour128", -} - -// supportedKexAlgos specifies the supported key-exchange algorithms in -// preference order. -var supportedKexAlgos = []string{ - kexAlgoCurve25519SHA256, - // P384 and P521 are not constant-time yet, but since we don't - // reuse ephemeral keys, using them for ECDH should be OK. - kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, - kexAlgoDH14SHA1, kexAlgoDH1SHA1, -} - -// supportedKexAlgos specifies the supported host-key algorithms (i.e. methods -// of authenticating servers) in preference order. -var supportedHostKeyAlgos = []string{ - CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, - CertAlgoECDSA384v01, CertAlgoECDSA521v01, - - KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, - KeyAlgoRSA, KeyAlgoDSA, -} - -// supportedMACs specifies a default set of MAC algorithms in preference order. -// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed -// because they have reached the end of their useful life. -var supportedMACs = []string{ - "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96", -} - -var supportedCompressions = []string{compressionNone} - -// hashFuncs keeps the mapping of supported algorithms to their respective -// hashes needed for signature verification. -var hashFuncs = map[string]crypto.Hash{ - KeyAlgoRSA: crypto.SHA1, - KeyAlgoDSA: crypto.SHA1, - KeyAlgoECDSA256: crypto.SHA256, - KeyAlgoECDSA384: crypto.SHA384, - KeyAlgoECDSA521: crypto.SHA512, - CertAlgoRSAv01: crypto.SHA1, - CertAlgoDSAv01: crypto.SHA1, - CertAlgoECDSA256v01: crypto.SHA256, - CertAlgoECDSA384v01: crypto.SHA384, - CertAlgoECDSA521v01: crypto.SHA512, -} - -// unexpectedMessageError results when the SSH message that we received didn't -// match what we wanted. -func unexpectedMessageError(expected, got uint8) error { - return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected) -} - -// parseError results from a malformed SSH message. -func parseError(tag uint8) error { - return fmt.Errorf("ssh: parse error in message type %d", tag) -} - -func findCommon(what string, client []string, server []string) (common string, err error) { - for _, c := range client { - for _, s := range server { - if c == s { - return c, nil - } - } - } - return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server) -} - -type directionAlgorithms struct { - Cipher string - MAC string - Compression string -} - -type algorithms struct { - kex string - hostKey string - w directionAlgorithms - r directionAlgorithms -} - -func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) { - result := &algorithms{} - - result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos) - if err != nil { - return - } - - result.hostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos) - if err != nil { - return - } - - result.w.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer) - if err != nil { - return - } - - result.r.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient) - if err != nil { - return - } - - result.w.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) - if err != nil { - return - } - - result.r.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) - if err != nil { - return - } - - result.w.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) - if err != nil { - return - } - - result.r.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient) - if err != nil { - return - } - - return result, nil -} - -// If rekeythreshold is too small, we can't make any progress sending -// stuff. -const minRekeyThreshold uint64 = 256 - -// Config contains configuration data common to both ServerConfig and -// ClientConfig. -type Config struct { - // Rand provides the source of entropy for cryptographic - // primitives. If Rand is nil, the cryptographic random reader - // in package crypto/rand will be used. - Rand io.Reader - - // The maximum number of bytes sent or received after which a - // new key is negotiated. It must be at least 256. If - // unspecified, 1 gigabyte is used. - RekeyThreshold uint64 - - // The allowed key exchanges algorithms. If unspecified then a - // default set of algorithms is used. - KeyExchanges []string - - // The allowed cipher algorithms. If unspecified then a sensible - // default is used. - Ciphers []string - - // The allowed MAC algorithms. If unspecified then a sensible default - // is used. - MACs []string -} - -// SetDefaults sets sensible values for unset fields in config. This is -// exported for testing: Configs passed to SSH functions are copied and have -// default values set automatically. -func (c *Config) SetDefaults() { - if c.Rand == nil { - c.Rand = rand.Reader - } - if c.Ciphers == nil { - c.Ciphers = supportedCiphers - } - var ciphers []string - for _, c := range c.Ciphers { - if cipherModes[c] != nil { - // reject the cipher if we have no cipherModes definition - ciphers = append(ciphers, c) - } - } - c.Ciphers = ciphers - - if c.KeyExchanges == nil { - c.KeyExchanges = supportedKexAlgos - } - - if c.MACs == nil { - c.MACs = supportedMACs - } - - if c.RekeyThreshold == 0 { - // RFC 4253, section 9 suggests rekeying after 1G. - c.RekeyThreshold = 1 << 30 - } - if c.RekeyThreshold < minRekeyThreshold { - c.RekeyThreshold = minRekeyThreshold - } -} - -// buildDataSignedForAuth returns the data that is signed in order to prove -// possession of a private key. See RFC 4252, section 7. -func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { - data := struct { - Session []byte - Type byte - User string - Service string - Method string - Sign bool - Algo []byte - PubKey []byte - }{ - sessionId, - msgUserAuthRequest, - req.User, - req.Service, - req.Method, - true, - algo, - pubKey, - } - return Marshal(data) -} - -func appendU16(buf []byte, n uint16) []byte { - return append(buf, byte(n>>8), byte(n)) -} - -func appendU32(buf []byte, n uint32) []byte { - return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) -} - -func appendU64(buf []byte, n uint64) []byte { - return append(buf, - byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32), - byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) -} - -func appendInt(buf []byte, n int) []byte { - return appendU32(buf, uint32(n)) -} - -func appendString(buf []byte, s string) []byte { - buf = appendU32(buf, uint32(len(s))) - buf = append(buf, s...) - return buf -} - -func appendBool(buf []byte, b bool) []byte { - if b { - return append(buf, 1) - } - return append(buf, 0) -} - -// newCond is a helper to hide the fact that there is no usable zero -// value for sync.Cond. -func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) } - -// window represents the buffer available to clients -// wishing to write to a channel. -type window struct { - *sync.Cond - win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1 - writeWaiters int - closed bool -} - -// add adds win to the amount of window available -// for consumers. -func (w *window) add(win uint32) bool { - // a zero sized window adjust is a noop. - if win == 0 { - return true - } - w.L.Lock() - if w.win+win < win { - w.L.Unlock() - return false - } - w.win += win - // It is unusual that multiple goroutines would be attempting to reserve - // window space, but not guaranteed. Use broadcast to notify all waiters - // that additional window is available. - w.Broadcast() - w.L.Unlock() - return true -} - -// close sets the window to closed, so all reservations fail -// immediately. -func (w *window) close() { - w.L.Lock() - w.closed = true - w.Broadcast() - w.L.Unlock() -} - -// reserve reserves win from the available window capacity. -// If no capacity remains, reserve will block. reserve may -// return less than requested. -func (w *window) reserve(win uint32) (uint32, error) { - var err error - w.L.Lock() - w.writeWaiters++ - w.Broadcast() - for w.win == 0 && !w.closed { - w.Wait() - } - w.writeWaiters-- - if w.win < win { - win = w.win - } - w.win -= win - if w.closed { - err = io.EOF - } - w.L.Unlock() - return win, err -} - -// waitWriterBlocked waits until some goroutine is blocked for further -// writes. It is used in tests only. -func (w *window) waitWriterBlocked() { - w.Cond.L.Lock() - for w.writeWaiters == 0 { - w.Cond.Wait() - } - w.Cond.L.Unlock() -} diff --git a/modules/crypto/ssh/connection.go b/modules/crypto/ssh/connection.go deleted file mode 100755 index 979d919e..00000000 --- a/modules/crypto/ssh/connection.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "fmt" - "net" -) - -// OpenChannelError is returned if the other side rejects an -// OpenChannel request. -type OpenChannelError struct { - Reason RejectionReason - Message string -} - -func (e *OpenChannelError) Error() string { - return fmt.Sprintf("ssh: rejected: %s (%s)", e.Reason, e.Message) -} - -// ConnMetadata holds metadata for the connection. -type ConnMetadata interface { - // User returns the user ID for this connection. - // It is empty if no authentication is used. - User() string - - // SessionID returns the sesson hash, also denoted by H. - SessionID() []byte - - // ClientVersion returns the client's version string as hashed - // into the session ID. - ClientVersion() []byte - - // ServerVersion returns the server's version string as hashed - // into the session ID. - ServerVersion() []byte - - // RemoteAddr returns the remote address for this connection. - RemoteAddr() net.Addr - - // LocalAddr returns the local address for this connection. - LocalAddr() net.Addr -} - -// Conn represents an SSH connection for both server and client roles. -// Conn is the basis for implementing an application layer, such -// as ClientConn, which implements the traditional shell access for -// clients. -type Conn interface { - ConnMetadata - - // SendRequest sends a global request, and returns the - // reply. If wantReply is true, it returns the response status - // and payload. See also RFC4254, section 4. - SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error) - - // OpenChannel tries to open an channel. If the request is - // rejected, it returns *OpenChannelError. On success it returns - // the SSH Channel and a Go channel for incoming, out-of-band - // requests. The Go channel must be serviced, or the - // connection will hang. - OpenChannel(name string, data []byte) (Channel, <-chan *Request, error) - - // Close closes the underlying network connection - Close() error - - // Wait blocks until the connection has shut down, and returns the - // error causing the shutdown. - Wait() error - - // TODO(hanwen): consider exposing: - // RequestKeyChange - // Disconnect -} - -// DiscardRequests consumes and rejects all requests from the -// passed-in channel. -func DiscardRequests(in <-chan *Request) { - for req := range in { - if req.WantReply { - req.Reply(false, nil) - } - } -} - -// A connection represents an incoming connection. -type connection struct { - transport *handshakeTransport - sshConn - - // The connection protocol. - *mux -} - -func (c *connection) Close() error { - return c.sshConn.conn.Close() -} - -// sshconn provides net.Conn metadata, but disallows direct reads and -// writes. -type sshConn struct { - conn net.Conn - - user string - sessionID []byte - clientVersion []byte - serverVersion []byte -} - -func dup(src []byte) []byte { - dst := make([]byte, len(src)) - copy(dst, src) - return dst -} - -func (c *sshConn) User() string { - return c.user -} - -func (c *sshConn) RemoteAddr() net.Addr { - return c.conn.RemoteAddr() -} - -func (c *sshConn) Close() error { - return c.conn.Close() -} - -func (c *sshConn) LocalAddr() net.Addr { - return c.conn.LocalAddr() -} - -func (c *sshConn) SessionID() []byte { - return dup(c.sessionID) -} - -func (c *sshConn) ClientVersion() []byte { - return dup(c.clientVersion) -} - -func (c *sshConn) ServerVersion() []byte { - return dup(c.serverVersion) -} diff --git a/modules/crypto/ssh/doc.go b/modules/crypto/ssh/doc.go deleted file mode 100755 index a5ff8af6..00000000 --- a/modules/crypto/ssh/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -Package ssh implements an SSH client and server. - -SSH is a transport security protocol, an authentication protocol and a -family of application protocols. The most typical application level -protocol is a remote shell and this is specifically implemented. However, -the multiplexed nature of SSH is exposed to users that wish to support -others. - -References: - [PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD - [SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 -*/ -package ssh diff --git a/modules/crypto/ssh/example_test.go b/modules/crypto/ssh/example_test.go deleted file mode 100755 index 3b1327f4..00000000 --- a/modules/crypto/ssh/example_test.go +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh_test - -import ( - "bytes" - "fmt" - "io/ioutil" - "log" - "net" - "net/http" - - "github.com/gogits/gogs/modules/crypto/ssh" - "github.com/gogits/gogs/modules/crypto/ssh/terminal" -) - -func ExampleNewServerConn() { - // An SSH server is represented by a ServerConfig, which holds - // certificate details and handles authentication of ServerConns. - config := &ssh.ServerConfig{ - PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { - // Should use constant-time compare (or better, salt+hash) in - // a production setting. - if c.User() == "testuser" && string(pass) == "tiger" { - return nil, nil - } - return nil, fmt.Errorf("password rejected for %q", c.User()) - }, - } - - privateBytes, err := ioutil.ReadFile("id_rsa") - if err != nil { - panic("Failed to load private key") - } - - private, err := ssh.ParsePrivateKey(privateBytes) - if err != nil { - panic("Failed to parse private key") - } - - config.AddHostKey(private) - - // Once a ServerConfig has been configured, connections can be - // accepted. - listener, err := net.Listen("tcp", "0.0.0.0:2022") - if err != nil { - panic("failed to listen for connection") - } - nConn, err := listener.Accept() - if err != nil { - panic("failed to accept incoming connection") - } - - // Before use, a handshake must be performed on the incoming - // net.Conn. - _, chans, reqs, err := ssh.NewServerConn(nConn, config) - if err != nil { - panic("failed to handshake") - } - // The incoming Request channel must be serviced. - go ssh.DiscardRequests(reqs) - - // Service the incoming Channel channel. - for newChannel := range chans { - // Channels have a type, depending on the application level - // protocol intended. In the case of a shell, the type is - // "session" and ServerShell may be used to present a simple - // terminal interface. - if newChannel.ChannelType() != "session" { - newChannel.Reject(ssh.UnknownChannelType, "unknown channel type") - continue - } - channel, requests, err := newChannel.Accept() - if err != nil { - panic("could not accept channel.") - } - - // Sessions have out-of-band requests such as "shell", - // "pty-req" and "env". Here we handle only the - // "shell" request. - go func(in <-chan *ssh.Request) { - for req := range in { - ok := false - switch req.Type { - case "shell": - ok = true - if len(req.Payload) > 0 { - // We don't accept any - // commands, only the - // default shell. - ok = false - } - } - req.Reply(ok, nil) - } - }(requests) - - term := terminal.NewTerminal(channel, "> ") - - go func() { - defer channel.Close() - for { - line, err := term.ReadLine() - if err != nil { - break - } - fmt.Println(line) - } - }() - } -} - -func ExampleDial() { - // An SSH client is represented with a ClientConn. Currently only - // the "password" authentication method is supported. - // - // To authenticate with the remote server you must pass at least one - // implementation of AuthMethod via the Auth field in ClientConfig. - config := &ssh.ClientConfig{ - User: "username", - Auth: []ssh.AuthMethod{ - ssh.Password("yourpassword"), - }, - } - client, err := ssh.Dial("tcp", "yourserver.com:22", config) - if err != nil { - panic("Failed to dial: " + err.Error()) - } - - // Each ClientConn can support multiple interactive sessions, - // represented by a Session. - session, err := client.NewSession() - if err != nil { - panic("Failed to create session: " + err.Error()) - } - defer session.Close() - - // Once a Session is created, you can execute a single command on - // the remote side using the Run method. - var b bytes.Buffer - session.Stdout = &b - if err := session.Run("/usr/bin/whoami"); err != nil { - panic("Failed to run: " + err.Error()) - } - fmt.Println(b.String()) -} - -func ExampleClient_Listen() { - config := &ssh.ClientConfig{ - User: "username", - Auth: []ssh.AuthMethod{ - ssh.Password("password"), - }, - } - // Dial your ssh server. - conn, err := ssh.Dial("tcp", "localhost:22", config) - if err != nil { - log.Fatalf("unable to connect: %s", err) - } - defer conn.Close() - - // Request the remote side to open port 8080 on all interfaces. - l, err := conn.Listen("tcp", "0.0.0.0:8080") - if err != nil { - log.Fatalf("unable to register tcp forward: %v", err) - } - defer l.Close() - - // Serve HTTP with your SSH server acting as a reverse proxy. - http.Serve(l, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - fmt.Fprintf(resp, "Hello world!\n") - })) -} - -func ExampleSession_RequestPty() { - // Create client config - config := &ssh.ClientConfig{ - User: "username", - Auth: []ssh.AuthMethod{ - ssh.Password("password"), - }, - } - // Connect to ssh server - conn, err := ssh.Dial("tcp", "localhost:22", config) - if err != nil { - log.Fatalf("unable to connect: %s", err) - } - defer conn.Close() - // Create a session - session, err := conn.NewSession() - if err != nil { - log.Fatalf("unable to create session: %s", err) - } - defer session.Close() - // Set up terminal modes - modes := ssh.TerminalModes{ - ssh.ECHO: 0, // disable echoing - ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud - ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud - } - // Request pseudo terminal - if err := session.RequestPty("xterm", 80, 40, modes); err != nil { - log.Fatalf("request for pseudo terminal failed: %s", err) - } - // Start remote shell - if err := session.Shell(); err != nil { - log.Fatalf("failed to start shell: %s", err) - } -} diff --git a/modules/crypto/ssh/handshake.go b/modules/crypto/ssh/handshake.go deleted file mode 100755 index 1c54f758..00000000 --- a/modules/crypto/ssh/handshake.go +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "crypto/rand" - "errors" - "fmt" - "io" - "log" - "net" - "sync" -) - -// debugHandshake, if set, prints messages sent and received. Key -// exchange messages are printed as if DH were used, so the debug -// messages are wrong when using ECDH. -const debugHandshake = false - -// keyingTransport is a packet based transport that supports key -// changes. It need not be thread-safe. It should pass through -// msgNewKeys in both directions. -type keyingTransport interface { - packetConn - - // prepareKeyChange sets up a key change. The key change for a - // direction will be effected if a msgNewKeys message is sent - // or received. - prepareKeyChange(*algorithms, *kexResult) error - - // getSessionID returns the session ID. prepareKeyChange must - // have been called once. - getSessionID() []byte -} - -// rekeyingTransport is the interface of handshakeTransport that we -// (internally) expose to ClientConn and ServerConn. -type rekeyingTransport interface { - packetConn - - // requestKeyChange asks the remote side to change keys. All - // writes are blocked until the key change succeeds, which is - // signaled by reading a msgNewKeys. - requestKeyChange() error - - // getSessionID returns the session ID. This is only valid - // after the first key change has completed. - getSessionID() []byte -} - -// handshakeTransport implements rekeying on top of a keyingTransport -// and offers a thread-safe writePacket() interface. -type handshakeTransport struct { - conn keyingTransport - config *Config - - serverVersion []byte - clientVersion []byte - - // hostKeys is non-empty if we are the server. In that case, - // it contains all host keys that can be used to sign the - // connection. - hostKeys []Signer - - // hostKeyAlgorithms is non-empty if we are the client. In that case, - // we accept these key types from the server as host key. - hostKeyAlgorithms []string - - // On read error, incoming is closed, and readError is set. - incoming chan []byte - readError error - - // data for host key checking - hostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error - dialAddress string - remoteAddr net.Addr - - readSinceKex uint64 - - // Protects the writing side of the connection - mu sync.Mutex - cond *sync.Cond - sentInitPacket []byte - sentInitMsg *kexInitMsg - writtenSinceKex uint64 - writeError error -} - -func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport { - t := &handshakeTransport{ - conn: conn, - serverVersion: serverVersion, - clientVersion: clientVersion, - incoming: make(chan []byte, 16), - config: config, - } - t.cond = sync.NewCond(&t.mu) - return t -} - -func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ClientConfig, dialAddr string, addr net.Addr) *handshakeTransport { - t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion) - t.dialAddress = dialAddr - t.remoteAddr = addr - t.hostKeyCallback = config.HostKeyCallback - if config.HostKeyAlgorithms != nil { - t.hostKeyAlgorithms = config.HostKeyAlgorithms - } else { - t.hostKeyAlgorithms = supportedHostKeyAlgos - } - go t.readLoop() - return t -} - -func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ServerConfig) *handshakeTransport { - t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion) - t.hostKeys = config.hostKeys - go t.readLoop() - return t -} - -func (t *handshakeTransport) getSessionID() []byte { - return t.conn.getSessionID() -} - -func (t *handshakeTransport) id() string { - if len(t.hostKeys) > 0 { - return "server" - } - return "client" -} - -func (t *handshakeTransport) readPacket() ([]byte, error) { - p, ok := <-t.incoming - if !ok { - return nil, t.readError - } - return p, nil -} - -func (t *handshakeTransport) readLoop() { - for { - p, err := t.readOnePacket() - if err != nil { - t.readError = err - close(t.incoming) - break - } - if p[0] == msgIgnore || p[0] == msgDebug { - continue - } - t.incoming <- p - } - - // If we can't read, declare the writing part dead too. - t.mu.Lock() - defer t.mu.Unlock() - if t.writeError == nil { - t.writeError = t.readError - } - t.cond.Broadcast() -} - -func (t *handshakeTransport) readOnePacket() ([]byte, error) { - if t.readSinceKex > t.config.RekeyThreshold { - if err := t.requestKeyChange(); err != nil { - return nil, err - } - } - - p, err := t.conn.readPacket() - if err != nil { - return nil, err - } - - t.readSinceKex += uint64(len(p)) - if debugHandshake { - msg, err := decode(p) - log.Printf("%s got %T %v (%v)", t.id(), msg, msg, err) - } - if p[0] != msgKexInit { - return p, nil - } - err = t.enterKeyExchange(p) - - t.mu.Lock() - if err != nil { - // drop connection - t.conn.Close() - t.writeError = err - } - - if debugHandshake { - log.Printf("%s exited key exchange, err %v", t.id(), err) - } - - // Unblock writers. - t.sentInitMsg = nil - t.sentInitPacket = nil - t.cond.Broadcast() - t.writtenSinceKex = 0 - t.mu.Unlock() - - if err != nil { - return nil, err - } - - t.readSinceKex = 0 - return []byte{msgNewKeys}, nil -} - -// sendKexInit sends a key change message, and returns the message -// that was sent. After initiating the key change, all writes will be -// blocked until the change is done, and a failed key change will -// close the underlying transport. This function is safe for -// concurrent use by multiple goroutines. -func (t *handshakeTransport) sendKexInit() (*kexInitMsg, []byte, error) { - t.mu.Lock() - defer t.mu.Unlock() - return t.sendKexInitLocked() -} - -func (t *handshakeTransport) requestKeyChange() error { - _, _, err := t.sendKexInit() - return err -} - -// sendKexInitLocked sends a key change message. t.mu must be locked -// while this happens. -func (t *handshakeTransport) sendKexInitLocked() (*kexInitMsg, []byte, error) { - // kexInits may be sent either in response to the other side, - // or because our side wants to initiate a key change, so we - // may have already sent a kexInit. In that case, don't send a - // second kexInit. - if t.sentInitMsg != nil { - return t.sentInitMsg, t.sentInitPacket, nil - } - msg := &kexInitMsg{ - KexAlgos: t.config.KeyExchanges, - CiphersClientServer: t.config.Ciphers, - CiphersServerClient: t.config.Ciphers, - MACsClientServer: t.config.MACs, - MACsServerClient: t.config.MACs, - CompressionClientServer: supportedCompressions, - CompressionServerClient: supportedCompressions, - } - io.ReadFull(rand.Reader, msg.Cookie[:]) - - if len(t.hostKeys) > 0 { - for _, k := range t.hostKeys { - msg.ServerHostKeyAlgos = append( - msg.ServerHostKeyAlgos, k.PublicKey().Type()) - } - } else { - msg.ServerHostKeyAlgos = t.hostKeyAlgorithms - } - packet := Marshal(msg) - - // writePacket destroys the contents, so save a copy. - packetCopy := make([]byte, len(packet)) - copy(packetCopy, packet) - - if err := t.conn.writePacket(packetCopy); err != nil { - return nil, nil, err - } - - t.sentInitMsg = msg - t.sentInitPacket = packet - return msg, packet, nil -} - -func (t *handshakeTransport) writePacket(p []byte) error { - t.mu.Lock() - defer t.mu.Unlock() - - if t.writtenSinceKex > t.config.RekeyThreshold { - t.sendKexInitLocked() - } - for t.sentInitMsg != nil && t.writeError == nil { - t.cond.Wait() - } - if t.writeError != nil { - return t.writeError - } - t.writtenSinceKex += uint64(len(p)) - - switch p[0] { - case msgKexInit: - return errors.New("ssh: only handshakeTransport can send kexInit") - case msgNewKeys: - return errors.New("ssh: only handshakeTransport can send newKeys") - default: - return t.conn.writePacket(p) - } -} - -func (t *handshakeTransport) Close() error { - return t.conn.Close() -} - -// enterKeyExchange runs the key exchange. -func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { - if debugHandshake { - log.Printf("%s entered key exchange", t.id()) - } - myInit, myInitPacket, err := t.sendKexInit() - if err != nil { - return err - } - - otherInit := &kexInitMsg{} - if err := Unmarshal(otherInitPacket, otherInit); err != nil { - return err - } - - magics := handshakeMagics{ - clientVersion: t.clientVersion, - serverVersion: t.serverVersion, - clientKexInit: otherInitPacket, - serverKexInit: myInitPacket, - } - - clientInit := otherInit - serverInit := myInit - if len(t.hostKeys) == 0 { - clientInit = myInit - serverInit = otherInit - - magics.clientKexInit = myInitPacket - magics.serverKexInit = otherInitPacket - } - - algs, err := findAgreedAlgorithms(clientInit, serverInit) - if err != nil { - return err - } - - // We don't send FirstKexFollows, but we handle receiving it. - if otherInit.FirstKexFollows && algs.kex != otherInit.KexAlgos[0] { - // other side sent a kex message for the wrong algorithm, - // which we have to ignore. - if _, err := t.conn.readPacket(); err != nil { - return err - } - } - - kex, ok := kexAlgoMap[algs.kex] - if !ok { - return fmt.Errorf("ssh: unexpected key exchange algorithm %v", algs.kex) - } - - var result *kexResult - if len(t.hostKeys) > 0 { - result, err = t.server(kex, algs, &magics) - } else { - result, err = t.client(kex, algs, &magics) - } - - if err != nil { - return err - } - - t.conn.prepareKeyChange(algs, result) - if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil { - return err - } - if packet, err := t.conn.readPacket(); err != nil { - return err - } else if packet[0] != msgNewKeys { - return unexpectedMessageError(msgNewKeys, packet[0]) - } - return nil -} - -func (t *handshakeTransport) server(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) { - var hostKey Signer - for _, k := range t.hostKeys { - if algs.hostKey == k.PublicKey().Type() { - hostKey = k - } - } - - r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey) - return r, err -} - -func (t *handshakeTransport) client(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) { - result, err := kex.Client(t.conn, t.config.Rand, magics) - if err != nil { - return nil, err - } - - hostKey, err := ParsePublicKey(result.HostKey) - if err != nil { - return nil, err - } - - if err := verifyHostKeySignature(hostKey, result); err != nil { - return nil, err - } - - if t.hostKeyCallback != nil { - err = t.hostKeyCallback(t.dialAddress, t.remoteAddr, hostKey) - if err != nil { - return nil, err - } - } - - return result, nil -} diff --git a/modules/crypto/ssh/handshake_test.go b/modules/crypto/ssh/handshake_test.go deleted file mode 100755 index b86d369c..00000000 --- a/modules/crypto/ssh/handshake_test.go +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bytes" - "crypto/rand" - "errors" - "fmt" - "net" - "runtime" - "strings" - "sync" - "testing" -) - -type testChecker struct { - calls []string -} - -func (t *testChecker) Check(dialAddr string, addr net.Addr, key PublicKey) error { - if dialAddr == "bad" { - return fmt.Errorf("dialAddr is bad") - } - - if tcpAddr, ok := addr.(*net.TCPAddr); !ok || tcpAddr == nil { - return fmt.Errorf("testChecker: got %T want *net.TCPAddr", addr) - } - - t.calls = append(t.calls, fmt.Sprintf("%s %v %s %x", dialAddr, addr, key.Type(), key.Marshal())) - - return nil -} - -// netPipe is analogous to net.Pipe, but it uses a real net.Conn, and -// therefore is buffered (net.Pipe deadlocks if both sides start with -// a write.) -func netPipe() (net.Conn, net.Conn, error) { - listener, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - return nil, nil, err - } - defer listener.Close() - c1, err := net.Dial("tcp", listener.Addr().String()) - if err != nil { - return nil, nil, err - } - - c2, err := listener.Accept() - if err != nil { - c1.Close() - return nil, nil, err - } - - return c1, c2, nil -} - -func handshakePair(clientConf *ClientConfig, addr string) (client *handshakeTransport, server *handshakeTransport, err error) { - a, b, err := netPipe() - if err != nil { - return nil, nil, err - } - - trC := newTransport(a, rand.Reader, true) - trS := newTransport(b, rand.Reader, false) - clientConf.SetDefaults() - - v := []byte("version") - client = newClientTransport(trC, v, v, clientConf, addr, a.RemoteAddr()) - - serverConf := &ServerConfig{} - serverConf.AddHostKey(testSigners["ecdsa"]) - serverConf.AddHostKey(testSigners["rsa"]) - serverConf.SetDefaults() - server = newServerTransport(trS, v, v, serverConf) - - return client, server, nil -} - -func TestHandshakeBasic(t *testing.T) { - if runtime.GOOS == "plan9" { - t.Skip("see golang.org/issue/7237") - } - checker := &testChecker{} - trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr") - if err != nil { - t.Fatalf("handshakePair: %v", err) - } - - defer trC.Close() - defer trS.Close() - - go func() { - // Client writes a bunch of stuff, and does a key - // change in the middle. This should not confuse the - // handshake in progress - for i := 0; i < 10; i++ { - p := []byte{msgRequestSuccess, byte(i)} - if err := trC.writePacket(p); err != nil { - t.Fatalf("sendPacket: %v", err) - } - if i == 5 { - // halfway through, we request a key change. - _, _, err := trC.sendKexInit() - if err != nil { - t.Fatalf("sendKexInit: %v", err) - } - } - } - trC.Close() - }() - - // Server checks that client messages come in cleanly - i := 0 - for { - p, err := trS.readPacket() - if err != nil { - break - } - if p[0] == msgNewKeys { - continue - } - want := []byte{msgRequestSuccess, byte(i)} - if bytes.Compare(p, want) != 0 { - t.Errorf("message %d: got %q, want %q", i, p, want) - } - i++ - } - if i != 10 { - t.Errorf("received %d messages, want 10.", i) - } - - // If all went well, we registered exactly 1 key change. - if len(checker.calls) != 1 { - t.Fatalf("got %d host key checks, want 1", len(checker.calls)) - } - - pub := testSigners["ecdsa"].PublicKey() - want := fmt.Sprintf("%s %v %s %x", "addr", trC.remoteAddr, pub.Type(), pub.Marshal()) - if want != checker.calls[0] { - t.Errorf("got %q want %q for host key check", checker.calls[0], want) - } -} - -func TestHandshakeError(t *testing.T) { - checker := &testChecker{} - trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "bad") - if err != nil { - t.Fatalf("handshakePair: %v", err) - } - defer trC.Close() - defer trS.Close() - - // send a packet - packet := []byte{msgRequestSuccess, 42} - if err := trC.writePacket(packet); err != nil { - t.Errorf("writePacket: %v", err) - } - - // Now request a key change. - _, _, err = trC.sendKexInit() - if err != nil { - t.Errorf("sendKexInit: %v", err) - } - - // the key change will fail, and afterwards we can't write. - if err := trC.writePacket([]byte{msgRequestSuccess, 43}); err == nil { - t.Errorf("writePacket after botched rekey succeeded.") - } - - readback, err := trS.readPacket() - if err != nil { - t.Fatalf("server closed too soon: %v", err) - } - if bytes.Compare(readback, packet) != 0 { - t.Errorf("got %q want %q", readback, packet) - } - readback, err = trS.readPacket() - if err == nil { - t.Errorf("got a message %q after failed key change", readback) - } -} - -func TestHandshakeTwice(t *testing.T) { - checker := &testChecker{} - trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr") - if err != nil { - t.Fatalf("handshakePair: %v", err) - } - - defer trC.Close() - defer trS.Close() - - // send a packet - packet := make([]byte, 5) - packet[0] = msgRequestSuccess - if err := trC.writePacket(packet); err != nil { - t.Errorf("writePacket: %v", err) - } - - // Now request a key change. - _, _, err = trC.sendKexInit() - if err != nil { - t.Errorf("sendKexInit: %v", err) - } - - // Send another packet. Use a fresh one, since writePacket destroys. - packet = make([]byte, 5) - packet[0] = msgRequestSuccess - if err := trC.writePacket(packet); err != nil { - t.Errorf("writePacket: %v", err) - } - - // 2nd key change. - _, _, err = trC.sendKexInit() - if err != nil { - t.Errorf("sendKexInit: %v", err) - } - - packet = make([]byte, 5) - packet[0] = msgRequestSuccess - if err := trC.writePacket(packet); err != nil { - t.Errorf("writePacket: %v", err) - } - - packet = make([]byte, 5) - packet[0] = msgRequestSuccess - for i := 0; i < 5; i++ { - msg, err := trS.readPacket() - if err != nil { - t.Fatalf("server closed too soon: %v", err) - } - if msg[0] == msgNewKeys { - continue - } - - if bytes.Compare(msg, packet) != 0 { - t.Errorf("packet %d: got %q want %q", i, msg, packet) - } - } - if len(checker.calls) != 2 { - t.Errorf("got %d key changes, want 2", len(checker.calls)) - } -} - -func TestHandshakeAutoRekeyWrite(t *testing.T) { - checker := &testChecker{} - clientConf := &ClientConfig{HostKeyCallback: checker.Check} - clientConf.RekeyThreshold = 500 - trC, trS, err := handshakePair(clientConf, "addr") - if err != nil { - t.Fatalf("handshakePair: %v", err) - } - defer trC.Close() - defer trS.Close() - - for i := 0; i < 5; i++ { - packet := make([]byte, 251) - packet[0] = msgRequestSuccess - if err := trC.writePacket(packet); err != nil { - t.Errorf("writePacket: %v", err) - } - } - - j := 0 - for ; j < 5; j++ { - _, err := trS.readPacket() - if err != nil { - break - } - } - - if j != 5 { - t.Errorf("got %d, want 5 messages", j) - } - - if len(checker.calls) != 2 { - t.Errorf("got %d key changes, wanted 2", len(checker.calls)) - } -} - -type syncChecker struct { - called chan int -} - -func (t *syncChecker) Check(dialAddr string, addr net.Addr, key PublicKey) error { - t.called <- 1 - return nil -} - -func TestHandshakeAutoRekeyRead(t *testing.T) { - sync := &syncChecker{make(chan int, 2)} - clientConf := &ClientConfig{ - HostKeyCallback: sync.Check, - } - clientConf.RekeyThreshold = 500 - - trC, trS, err := handshakePair(clientConf, "addr") - if err != nil { - t.Fatalf("handshakePair: %v", err) - } - defer trC.Close() - defer trS.Close() - - packet := make([]byte, 501) - packet[0] = msgRequestSuccess - if err := trS.writePacket(packet); err != nil { - t.Fatalf("writePacket: %v", err) - } - // While we read out the packet, a key change will be - // initiated. - if _, err := trC.readPacket(); err != nil { - t.Fatalf("readPacket(client): %v", err) - } - - <-sync.called -} - -// errorKeyingTransport generates errors after a given number of -// read/write operations. -type errorKeyingTransport struct { - packetConn - readLeft, writeLeft int -} - -func (n *errorKeyingTransport) prepareKeyChange(*algorithms, *kexResult) error { - return nil -} -func (n *errorKeyingTransport) getSessionID() []byte { - return nil -} - -func (n *errorKeyingTransport) writePacket(packet []byte) error { - if n.writeLeft == 0 { - n.Close() - return errors.New("barf") - } - - n.writeLeft-- - return n.packetConn.writePacket(packet) -} - -func (n *errorKeyingTransport) readPacket() ([]byte, error) { - if n.readLeft == 0 { - n.Close() - return nil, errors.New("barf") - } - - n.readLeft-- - return n.packetConn.readPacket() -} - -func TestHandshakeErrorHandlingRead(t *testing.T) { - for i := 0; i < 20; i++ { - testHandshakeErrorHandlingN(t, i, -1) - } -} - -func TestHandshakeErrorHandlingWrite(t *testing.T) { - for i := 0; i < 20; i++ { - testHandshakeErrorHandlingN(t, -1, i) - } -} - -// testHandshakeErrorHandlingN runs handshakes, injecting errors. If -// handshakeTransport deadlocks, the go runtime will detect it and -// panic. -func testHandshakeErrorHandlingN(t *testing.T, readLimit, writeLimit int) { - msg := Marshal(&serviceRequestMsg{strings.Repeat("x", int(minRekeyThreshold)/4)}) - - a, b := memPipe() - defer a.Close() - defer b.Close() - - key := testSigners["ecdsa"] - serverConf := Config{RekeyThreshold: minRekeyThreshold} - serverConf.SetDefaults() - serverConn := newHandshakeTransport(&errorKeyingTransport{a, readLimit, writeLimit}, &serverConf, []byte{'a'}, []byte{'b'}) - serverConn.hostKeys = []Signer{key} - go serverConn.readLoop() - - clientConf := Config{RekeyThreshold: 10 * minRekeyThreshold} - clientConf.SetDefaults() - clientConn := newHandshakeTransport(&errorKeyingTransport{b, -1, -1}, &clientConf, []byte{'a'}, []byte{'b'}) - clientConn.hostKeyAlgorithms = []string{key.PublicKey().Type()} - go clientConn.readLoop() - - var wg sync.WaitGroup - wg.Add(4) - - for _, hs := range []packetConn{serverConn, clientConn} { - go func(c packetConn) { - for { - err := c.writePacket(msg) - if err != nil { - break - } - } - wg.Done() - }(hs) - go func(c packetConn) { - for { - _, err := c.readPacket() - if err != nil { - break - } - } - wg.Done() - }(hs) - } - - wg.Wait() -} diff --git a/modules/crypto/ssh/kex.go b/modules/crypto/ssh/kex.go deleted file mode 100755 index ea19d537..00000000 --- a/modules/crypto/ssh/kex.go +++ /dev/null @@ -1,526 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/subtle" - "crypto/rand" - "errors" - "io" - "math/big" - - "golang.org/x/crypto/curve25519" -) - -const ( - kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1" - kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" - kexAlgoECDH256 = "ecdh-sha2-nistp256" - kexAlgoECDH384 = "ecdh-sha2-nistp384" - kexAlgoECDH521 = "ecdh-sha2-nistp521" - kexAlgoCurve25519SHA256 = "curve25519-sha256@libssh.org" -) - -// kexResult captures the outcome of a key exchange. -type kexResult struct { - // Session hash. See also RFC 4253, section 8. - H []byte - - // Shared secret. See also RFC 4253, section 8. - K []byte - - // Host key as hashed into H. - HostKey []byte - - // Signature of H. - Signature []byte - - // A cryptographic hash function that matches the security - // level of the key exchange algorithm. It is used for - // calculating H, and for deriving keys from H and K. - Hash crypto.Hash - - // The session ID, which is the first H computed. This is used - // to signal data inside transport. - SessionID []byte -} - -// handshakeMagics contains data that is always included in the -// session hash. -type handshakeMagics struct { - clientVersion, serverVersion []byte - clientKexInit, serverKexInit []byte -} - -func (m *handshakeMagics) write(w io.Writer) { - writeString(w, m.clientVersion) - writeString(w, m.serverVersion) - writeString(w, m.clientKexInit) - writeString(w, m.serverKexInit) -} - -// kexAlgorithm abstracts different key exchange algorithms. -type kexAlgorithm interface { - // Server runs server-side key agreement, signing the result - // with a hostkey. - Server(p packetConn, rand io.Reader, magics *handshakeMagics, s Signer) (*kexResult, error) - - // Client runs the client-side key agreement. Caller is - // responsible for verifying the host key signature. - Client(p packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) -} - -// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement. -type dhGroup struct { - g, p *big.Int -} - -func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) { - if theirPublic.Sign() <= 0 || theirPublic.Cmp(group.p) >= 0 { - return nil, errors.New("ssh: DH parameter out of bounds") - } - return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil -} - -func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) { - hashFunc := crypto.SHA1 - - x, err := rand.Int(randSource, group.p) - if err != nil { - return nil, err - } - X := new(big.Int).Exp(group.g, x, group.p) - kexDHInit := kexDHInitMsg{ - X: X, - } - if err := c.writePacket(Marshal(&kexDHInit)); err != nil { - return nil, err - } - - packet, err := c.readPacket() - if err != nil { - return nil, err - } - - var kexDHReply kexDHReplyMsg - if err = Unmarshal(packet, &kexDHReply); err != nil { - return nil, err - } - - kInt, err := group.diffieHellman(kexDHReply.Y, x) - if err != nil { - return nil, err - } - - h := hashFunc.New() - magics.write(h) - writeString(h, kexDHReply.HostKey) - writeInt(h, X) - writeInt(h, kexDHReply.Y) - K := make([]byte, intLength(kInt)) - marshalInt(K, kInt) - h.Write(K) - - return &kexResult{ - H: h.Sum(nil), - K: K, - HostKey: kexDHReply.HostKey, - Signature: kexDHReply.Signature, - Hash: crypto.SHA1, - }, nil -} - -func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { - hashFunc := crypto.SHA1 - packet, err := c.readPacket() - if err != nil { - return - } - var kexDHInit kexDHInitMsg - if err = Unmarshal(packet, &kexDHInit); err != nil { - return - } - - y, err := rand.Int(randSource, group.p) - if err != nil { - return - } - - Y := new(big.Int).Exp(group.g, y, group.p) - kInt, err := group.diffieHellman(kexDHInit.X, y) - if err != nil { - return nil, err - } - - hostKeyBytes := priv.PublicKey().Marshal() - - h := hashFunc.New() - magics.write(h) - writeString(h, hostKeyBytes) - writeInt(h, kexDHInit.X) - writeInt(h, Y) - - K := make([]byte, intLength(kInt)) - marshalInt(K, kInt) - h.Write(K) - - H := h.Sum(nil) - - // H is already a hash, but the hostkey signing will apply its - // own key-specific hash algorithm. - sig, err := signAndMarshal(priv, randSource, H) - if err != nil { - return nil, err - } - - kexDHReply := kexDHReplyMsg{ - HostKey: hostKeyBytes, - Y: Y, - Signature: sig, - } - packet = Marshal(&kexDHReply) - - err = c.writePacket(packet) - return &kexResult{ - H: H, - K: K, - HostKey: hostKeyBytes, - Signature: sig, - Hash: crypto.SHA1, - }, nil -} - -// ecdh performs Elliptic Curve Diffie-Hellman key exchange as -// described in RFC 5656, section 4. -type ecdh struct { - curve elliptic.Curve -} - -func (kex *ecdh) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) { - ephKey, err := ecdsa.GenerateKey(kex.curve, rand) - if err != nil { - return nil, err - } - - kexInit := kexECDHInitMsg{ - ClientPubKey: elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y), - } - - serialized := Marshal(&kexInit) - if err := c.writePacket(serialized); err != nil { - return nil, err - } - - packet, err := c.readPacket() - if err != nil { - return nil, err - } - - var reply kexECDHReplyMsg - if err = Unmarshal(packet, &reply); err != nil { - return nil, err - } - - x, y, err := unmarshalECKey(kex.curve, reply.EphemeralPubKey) - if err != nil { - return nil, err - } - - // generate shared secret - secret, _ := kex.curve.ScalarMult(x, y, ephKey.D.Bytes()) - - h := ecHash(kex.curve).New() - magics.write(h) - writeString(h, reply.HostKey) - writeString(h, kexInit.ClientPubKey) - writeString(h, reply.EphemeralPubKey) - K := make([]byte, intLength(secret)) - marshalInt(K, secret) - h.Write(K) - - return &kexResult{ - H: h.Sum(nil), - K: K, - HostKey: reply.HostKey, - Signature: reply.Signature, - Hash: ecHash(kex.curve), - }, nil -} - -// unmarshalECKey parses and checks an EC key. -func unmarshalECKey(curve elliptic.Curve, pubkey []byte) (x, y *big.Int, err error) { - x, y = elliptic.Unmarshal(curve, pubkey) - if x == nil { - return nil, nil, errors.New("ssh: elliptic.Unmarshal failure") - } - if !validateECPublicKey(curve, x, y) { - return nil, nil, errors.New("ssh: public key not on curve") - } - return x, y, nil -} - -// validateECPublicKey checks that the point is a valid public key for -// the given curve. See [SEC1], 3.2.2 -func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool { - if x.Sign() == 0 && y.Sign() == 0 { - return false - } - - if x.Cmp(curve.Params().P) >= 0 { - return false - } - - if y.Cmp(curve.Params().P) >= 0 { - return false - } - - if !curve.IsOnCurve(x, y) { - return false - } - - // We don't check if N * PubKey == 0, since - // - // - the NIST curves have cofactor = 1, so this is implicit. - // (We don't foresee an implementation that supports non NIST - // curves) - // - // - for ephemeral keys, we don't need to worry about small - // subgroup attacks. - return true -} - -func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { - packet, err := c.readPacket() - if err != nil { - return nil, err - } - - var kexECDHInit kexECDHInitMsg - if err = Unmarshal(packet, &kexECDHInit); err != nil { - return nil, err - } - - clientX, clientY, err := unmarshalECKey(kex.curve, kexECDHInit.ClientPubKey) - if err != nil { - return nil, err - } - - // We could cache this key across multiple users/multiple - // connection attempts, but the benefit is small. OpenSSH - // generates a new key for each incoming connection. - ephKey, err := ecdsa.GenerateKey(kex.curve, rand) - if err != nil { - return nil, err - } - - hostKeyBytes := priv.PublicKey().Marshal() - - serializedEphKey := elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y) - - // generate shared secret - secret, _ := kex.curve.ScalarMult(clientX, clientY, ephKey.D.Bytes()) - - h := ecHash(kex.curve).New() - magics.write(h) - writeString(h, hostKeyBytes) - writeString(h, kexECDHInit.ClientPubKey) - writeString(h, serializedEphKey) - - K := make([]byte, intLength(secret)) - marshalInt(K, secret) - h.Write(K) - - H := h.Sum(nil) - - // H is already a hash, but the hostkey signing will apply its - // own key-specific hash algorithm. - sig, err := signAndMarshal(priv, rand, H) - if err != nil { - return nil, err - } - - reply := kexECDHReplyMsg{ - EphemeralPubKey: serializedEphKey, - HostKey: hostKeyBytes, - Signature: sig, - } - - serialized := Marshal(&reply) - if err := c.writePacket(serialized); err != nil { - return nil, err - } - - return &kexResult{ - H: H, - K: K, - HostKey: reply.HostKey, - Signature: sig, - Hash: ecHash(kex.curve), - }, nil -} - -var kexAlgoMap = map[string]kexAlgorithm{} - -func init() { - // This is the group called diffie-hellman-group1-sha1 in RFC - // 4253 and Oakley Group 2 in RFC 2409. - p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) - kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{ - g: new(big.Int).SetInt64(2), - p: p, - } - - // This is the group called diffie-hellman-group14-sha1 in RFC - // 4253 and Oakley Group 14 in RFC 3526. - p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) - - kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{ - g: new(big.Int).SetInt64(2), - p: p, - } - - kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()} - kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()} - kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()} - kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{} -} - -// curve25519sha256 implements the curve25519-sha256@libssh.org key -// agreement protocol, as described in -// https://git.libssh.org/projects/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt -type curve25519sha256 struct{} - -type curve25519KeyPair struct { - priv [32]byte - pub [32]byte -} - -func (kp *curve25519KeyPair) generate(rand io.Reader) error { - if _, err := io.ReadFull(rand, kp.priv[:]); err != nil { - return err - } - curve25519.ScalarBaseMult(&kp.pub, &kp.priv) - return nil -} - -// curve25519Zeros is just an array of 32 zero bytes so that we have something -// convenient to compare against in order to reject curve25519 points with the -// wrong order. -var curve25519Zeros [32]byte - -func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) { - var kp curve25519KeyPair - if err := kp.generate(rand); err != nil { - return nil, err - } - if err := c.writePacket(Marshal(&kexECDHInitMsg{kp.pub[:]})); err != nil { - return nil, err - } - - packet, err := c.readPacket() - if err != nil { - return nil, err - } - - var reply kexECDHReplyMsg - if err = Unmarshal(packet, &reply); err != nil { - return nil, err - } - if len(reply.EphemeralPubKey) != 32 { - return nil, errors.New("ssh: peer's curve25519 public value has wrong length") - } - - var servPub, secret [32]byte - copy(servPub[:], reply.EphemeralPubKey) - curve25519.ScalarMult(&secret, &kp.priv, &servPub) - if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 { - return nil, errors.New("ssh: peer's curve25519 public value has wrong order") - } - - h := crypto.SHA256.New() - magics.write(h) - writeString(h, reply.HostKey) - writeString(h, kp.pub[:]) - writeString(h, reply.EphemeralPubKey) - - kInt := new(big.Int).SetBytes(secret[:]) - K := make([]byte, intLength(kInt)) - marshalInt(K, kInt) - h.Write(K) - - return &kexResult{ - H: h.Sum(nil), - K: K, - HostKey: reply.HostKey, - Signature: reply.Signature, - Hash: crypto.SHA256, - }, nil -} - -func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { - packet, err := c.readPacket() - if err != nil { - return - } - var kexInit kexECDHInitMsg - if err = Unmarshal(packet, &kexInit); err != nil { - return - } - - if len(kexInit.ClientPubKey) != 32 { - return nil, errors.New("ssh: peer's curve25519 public value has wrong length") - } - - var kp curve25519KeyPair - if err := kp.generate(rand); err != nil { - return nil, err - } - - var clientPub, secret [32]byte - copy(clientPub[:], kexInit.ClientPubKey) - curve25519.ScalarMult(&secret, &kp.priv, &clientPub) - if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 { - return nil, errors.New("ssh: peer's curve25519 public value has wrong order") - } - - hostKeyBytes := priv.PublicKey().Marshal() - - h := crypto.SHA256.New() - magics.write(h) - writeString(h, hostKeyBytes) - writeString(h, kexInit.ClientPubKey) - writeString(h, kp.pub[:]) - - kInt := new(big.Int).SetBytes(secret[:]) - K := make([]byte, intLength(kInt)) - marshalInt(K, kInt) - h.Write(K) - - H := h.Sum(nil) - - sig, err := signAndMarshal(priv, rand, H) - if err != nil { - return nil, err - } - - reply := kexECDHReplyMsg{ - EphemeralPubKey: kp.pub[:], - HostKey: hostKeyBytes, - Signature: sig, - } - if err := c.writePacket(Marshal(&reply)); err != nil { - return nil, err - } - return &kexResult{ - H: H, - K: K, - HostKey: hostKeyBytes, - Signature: sig, - Hash: crypto.SHA256, - }, nil -} diff --git a/modules/crypto/ssh/kex_test.go b/modules/crypto/ssh/kex_test.go deleted file mode 100755 index 12ca0acd..00000000 --- a/modules/crypto/ssh/kex_test.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -// Key exchange tests. - -import ( - "crypto/rand" - "reflect" - "testing" -) - -func TestKexes(t *testing.T) { - type kexResultErr struct { - result *kexResult - err error - } - - for name, kex := range kexAlgoMap { - a, b := memPipe() - - s := make(chan kexResultErr, 1) - c := make(chan kexResultErr, 1) - var magics handshakeMagics - go func() { - r, e := kex.Client(a, rand.Reader, &magics) - a.Close() - c <- kexResultErr{r, e} - }() - go func() { - r, e := kex.Server(b, rand.Reader, &magics, testSigners["ecdsa"]) - b.Close() - s <- kexResultErr{r, e} - }() - - clientRes := <-c - serverRes := <-s - if clientRes.err != nil { - t.Errorf("client: %v", clientRes.err) - } - if serverRes.err != nil { - t.Errorf("server: %v", serverRes.err) - } - if !reflect.DeepEqual(clientRes.result, serverRes.result) { - t.Errorf("kex %q: mismatch %#v, %#v", name, clientRes.result, serverRes.result) - } - } -} diff --git a/modules/crypto/ssh/keys.go b/modules/crypto/ssh/keys.go deleted file mode 100755 index 3272d7c9..00000000 --- a/modules/crypto/ssh/keys.go +++ /dev/null @@ -1,628 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bytes" - "crypto" - "crypto/dsa" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rsa" - "crypto/x509" - "encoding/asn1" - "encoding/base64" - "encoding/pem" - "errors" - "fmt" - "io" - "math/big" -) - -// These constants represent the algorithm names for key types supported by this -// package. -const ( - KeyAlgoRSA = "ssh-rsa" - KeyAlgoDSA = "ssh-dss" - KeyAlgoECDSA256 = "ecdsa-sha2-nistp256" - KeyAlgoECDSA384 = "ecdsa-sha2-nistp384" - KeyAlgoECDSA521 = "ecdsa-sha2-nistp521" -) - -// parsePubKey parses a public key of the given algorithm. -// Use ParsePublicKey for keys with prepended algorithm. -func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err error) { - switch algo { - case KeyAlgoRSA: - return parseRSA(in) - case KeyAlgoDSA: - return parseDSA(in) - case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521: - return parseECDSA(in) - case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01: - cert, err := parseCert(in, certToPrivAlgo(algo)) - if err != nil { - return nil, nil, err - } - return cert, nil, nil - } - return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", err) -} - -// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format -// (see sshd(8) manual page) once the options and key type fields have been -// removed. -func parseAuthorizedKey(in []byte) (out PublicKey, comment string, err error) { - in = bytes.TrimSpace(in) - - i := bytes.IndexAny(in, " \t") - if i == -1 { - i = len(in) - } - base64Key := in[:i] - - key := make([]byte, base64.StdEncoding.DecodedLen(len(base64Key))) - n, err := base64.StdEncoding.Decode(key, base64Key) - if err != nil { - return nil, "", err - } - key = key[:n] - out, err = ParsePublicKey(key) - if err != nil { - return nil, "", err - } - comment = string(bytes.TrimSpace(in[i:])) - return out, comment, nil -} - -// ParseAuthorizedKeys parses a public key from an authorized_keys -// file used in OpenSSH according to the sshd(8) manual page. -func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) { - for len(in) > 0 { - end := bytes.IndexByte(in, '\n') - if end != -1 { - rest = in[end+1:] - in = in[:end] - } else { - rest = nil - } - - end = bytes.IndexByte(in, '\r') - if end != -1 { - in = in[:end] - } - - in = bytes.TrimSpace(in) - if len(in) == 0 || in[0] == '#' { - in = rest - continue - } - - i := bytes.IndexAny(in, " \t") - if i == -1 { - in = rest - continue - } - - if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { - return out, comment, options, rest, nil - } - - // No key type recognised. Maybe there's an options field at - // the beginning. - var b byte - inQuote := false - var candidateOptions []string - optionStart := 0 - for i, b = range in { - isEnd := !inQuote && (b == ' ' || b == '\t') - if (b == ',' && !inQuote) || isEnd { - if i-optionStart > 0 { - candidateOptions = append(candidateOptions, string(in[optionStart:i])) - } - optionStart = i + 1 - } - if isEnd { - break - } - if b == '"' && (i == 0 || (i > 0 && in[i-1] != '\\')) { - inQuote = !inQuote - } - } - for i < len(in) && (in[i] == ' ' || in[i] == '\t') { - i++ - } - if i == len(in) { - // Invalid line: unmatched quote - in = rest - continue - } - - in = in[i:] - i = bytes.IndexAny(in, " \t") - if i == -1 { - in = rest - continue - } - - if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { - options = candidateOptions - return out, comment, options, rest, nil - } - - in = rest - continue - } - - return nil, "", nil, nil, errors.New("ssh: no key found") -} - -// ParsePublicKey parses an SSH public key formatted for use in -// the SSH wire protocol according to RFC 4253, section 6.6. -func ParsePublicKey(in []byte) (out PublicKey, err error) { - algo, in, ok := parseString(in) - if !ok { - return nil, errShortRead - } - var rest []byte - out, rest, err = parsePubKey(in, string(algo)) - if len(rest) > 0 { - return nil, errors.New("ssh: trailing junk in public key") - } - - return out, err -} - -// MarshalAuthorizedKey serializes key for inclusion in an OpenSSH -// authorized_keys file. The return value ends with newline. -func MarshalAuthorizedKey(key PublicKey) []byte { - b := &bytes.Buffer{} - b.WriteString(key.Type()) - b.WriteByte(' ') - e := base64.NewEncoder(base64.StdEncoding, b) - e.Write(key.Marshal()) - e.Close() - b.WriteByte('\n') - return b.Bytes() -} - -// PublicKey is an abstraction of different types of public keys. -type PublicKey interface { - // Type returns the key's type, e.g. "ssh-rsa". - Type() string - - // Marshal returns the serialized key data in SSH wire format, - // with the name prefix. - Marshal() []byte - - // Verify that sig is a signature on the given data using this - // key. This function will hash the data appropriately first. - Verify(data []byte, sig *Signature) error -} - -// A Signer can create signatures that verify against a public key. -type Signer interface { - // PublicKey returns an associated PublicKey instance. - PublicKey() PublicKey - - // Sign returns raw signature for the given data. This method - // will apply the hash specified for the keytype to the data. - Sign(rand io.Reader, data []byte) (*Signature, error) -} - -type rsaPublicKey rsa.PublicKey - -func (r *rsaPublicKey) Type() string { - return "ssh-rsa" -} - -// parseRSA parses an RSA key according to RFC 4253, section 6.6. -func parseRSA(in []byte) (out PublicKey, rest []byte, err error) { - var w struct { - E *big.Int - N *big.Int - Rest []byte `ssh:"rest"` - } - if err := Unmarshal(in, &w); err != nil { - return nil, nil, err - } - - if w.E.BitLen() > 24 { - return nil, nil, errors.New("ssh: exponent too large") - } - e := w.E.Int64() - if e < 3 || e&1 == 0 { - return nil, nil, errors.New("ssh: incorrect exponent") - } - - var key rsa.PublicKey - key.E = int(e) - key.N = w.N - return (*rsaPublicKey)(&key), w.Rest, nil -} - -func (r *rsaPublicKey) Marshal() []byte { - e := new(big.Int).SetInt64(int64(r.E)) - wirekey := struct { - Name string - E *big.Int - N *big.Int - }{ - KeyAlgoRSA, - e, - r.N, - } - return Marshal(&wirekey) -} - -func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error { - if sig.Format != r.Type() { - return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, r.Type()) - } - h := crypto.SHA1.New() - h.Write(data) - digest := h.Sum(nil) - return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig.Blob) -} - -type rsaPrivateKey struct { - *rsa.PrivateKey -} - -func (r *rsaPrivateKey) PublicKey() PublicKey { - return (*rsaPublicKey)(&r.PrivateKey.PublicKey) -} - -func (r *rsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) { - h := crypto.SHA1.New() - h.Write(data) - digest := h.Sum(nil) - blob, err := rsa.SignPKCS1v15(rand, r.PrivateKey, crypto.SHA1, digest) - if err != nil { - return nil, err - } - return &Signature{ - Format: r.PublicKey().Type(), - Blob: blob, - }, nil -} - -type dsaPublicKey dsa.PublicKey - -func (r *dsaPublicKey) Type() string { - return "ssh-dss" -} - -// parseDSA parses an DSA key according to RFC 4253, section 6.6. -func parseDSA(in []byte) (out PublicKey, rest []byte, err error) { - var w struct { - P, Q, G, Y *big.Int - Rest []byte `ssh:"rest"` - } - if err := Unmarshal(in, &w); err != nil { - return nil, nil, err - } - - key := &dsaPublicKey{ - Parameters: dsa.Parameters{ - P: w.P, - Q: w.Q, - G: w.G, - }, - Y: w.Y, - } - return key, w.Rest, nil -} - -func (k *dsaPublicKey) Marshal() []byte { - w := struct { - Name string - P, Q, G, Y *big.Int - }{ - k.Type(), - k.P, - k.Q, - k.G, - k.Y, - } - - return Marshal(&w) -} - -func (k *dsaPublicKey) Verify(data []byte, sig *Signature) error { - if sig.Format != k.Type() { - return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) - } - h := crypto.SHA1.New() - h.Write(data) - digest := h.Sum(nil) - - // Per RFC 4253, section 6.6, - // The value for 'dss_signature_blob' is encoded as a string containing - // r, followed by s (which are 160-bit integers, without lengths or - // padding, unsigned, and in network byte order). - // For DSS purposes, sig.Blob should be exactly 40 bytes in length. - if len(sig.Blob) != 40 { - return errors.New("ssh: DSA signature parse error") - } - r := new(big.Int).SetBytes(sig.Blob[:20]) - s := new(big.Int).SetBytes(sig.Blob[20:]) - if dsa.Verify((*dsa.PublicKey)(k), digest, r, s) { - return nil - } - return errors.New("ssh: signature did not verify") -} - -type dsaPrivateKey struct { - *dsa.PrivateKey -} - -func (k *dsaPrivateKey) PublicKey() PublicKey { - return (*dsaPublicKey)(&k.PrivateKey.PublicKey) -} - -func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) { - h := crypto.SHA1.New() - h.Write(data) - digest := h.Sum(nil) - r, s, err := dsa.Sign(rand, k.PrivateKey, digest) - if err != nil { - return nil, err - } - - sig := make([]byte, 40) - rb := r.Bytes() - sb := s.Bytes() - - copy(sig[20-len(rb):20], rb) - copy(sig[40-len(sb):], sb) - - return &Signature{ - Format: k.PublicKey().Type(), - Blob: sig, - }, nil -} - -type ecdsaPublicKey ecdsa.PublicKey - -func (key *ecdsaPublicKey) Type() string { - return "ecdsa-sha2-" + key.nistID() -} - -func (key *ecdsaPublicKey) nistID() string { - switch key.Params().BitSize { - case 256: - return "nistp256" - case 384: - return "nistp384" - case 521: - return "nistp521" - } - panic("ssh: unsupported ecdsa key size") -} - -func supportedEllipticCurve(curve elliptic.Curve) bool { - return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521() -} - -// ecHash returns the hash to match the given elliptic curve, see RFC -// 5656, section 6.2.1 -func ecHash(curve elliptic.Curve) crypto.Hash { - bitSize := curve.Params().BitSize - switch { - case bitSize <= 256: - return crypto.SHA256 - case bitSize <= 384: - return crypto.SHA384 - } - return crypto.SHA512 -} - -// parseECDSA parses an ECDSA key according to RFC 5656, section 3.1. -func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) { - var w struct { - Curve string - KeyBytes []byte - Rest []byte `ssh:"rest"` - } - - if err := Unmarshal(in, &w); err != nil { - return nil, nil, err - } - - key := new(ecdsa.PublicKey) - - switch w.Curve { - case "nistp256": - key.Curve = elliptic.P256() - case "nistp384": - key.Curve = elliptic.P384() - case "nistp521": - key.Curve = elliptic.P521() - default: - return nil, nil, errors.New("ssh: unsupported curve") - } - - key.X, key.Y = elliptic.Unmarshal(key.Curve, w.KeyBytes) - if key.X == nil || key.Y == nil { - return nil, nil, errors.New("ssh: invalid curve point") - } - return (*ecdsaPublicKey)(key), w.Rest, nil -} - -func (key *ecdsaPublicKey) Marshal() []byte { - // See RFC 5656, section 3.1. - keyBytes := elliptic.Marshal(key.Curve, key.X, key.Y) - w := struct { - Name string - ID string - Key []byte - }{ - key.Type(), - key.nistID(), - keyBytes, - } - - return Marshal(&w) -} - -func (key *ecdsaPublicKey) Verify(data []byte, sig *Signature) error { - if sig.Format != key.Type() { - return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, key.Type()) - } - - h := ecHash(key.Curve).New() - h.Write(data) - digest := h.Sum(nil) - - // Per RFC 5656, section 3.1.2, - // The ecdsa_signature_blob value has the following specific encoding: - // mpint r - // mpint s - var ecSig struct { - R *big.Int - S *big.Int - } - - if err := Unmarshal(sig.Blob, &ecSig); err != nil { - return err - } - - if ecdsa.Verify((*ecdsa.PublicKey)(key), digest, ecSig.R, ecSig.S) { - return nil - } - return errors.New("ssh: signature did not verify") -} - -type ecdsaPrivateKey struct { - *ecdsa.PrivateKey -} - -func (k *ecdsaPrivateKey) PublicKey() PublicKey { - return (*ecdsaPublicKey)(&k.PrivateKey.PublicKey) -} - -func (k *ecdsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) { - h := ecHash(k.PrivateKey.PublicKey.Curve).New() - h.Write(data) - digest := h.Sum(nil) - r, s, err := ecdsa.Sign(rand, k.PrivateKey, digest) - if err != nil { - return nil, err - } - - sig := make([]byte, intLength(r)+intLength(s)) - rest := marshalInt(sig, r) - marshalInt(rest, s) - return &Signature{ - Format: k.PublicKey().Type(), - Blob: sig, - }, nil -} - -// NewSignerFromKey takes a pointer to rsa, dsa or ecdsa PrivateKey -// returns a corresponding Signer instance. EC keys should use P256, -// P384 or P521. -func NewSignerFromKey(k interface{}) (Signer, error) { - var sshKey Signer - switch t := k.(type) { - case *rsa.PrivateKey: - sshKey = &rsaPrivateKey{t} - case *dsa.PrivateKey: - sshKey = &dsaPrivateKey{t} - case *ecdsa.PrivateKey: - if !supportedEllipticCurve(t.Curve) { - return nil, errors.New("ssh: only P256, P384 and P521 EC keys are supported.") - } - - sshKey = &ecdsaPrivateKey{t} - default: - return nil, fmt.Errorf("ssh: unsupported key type %T", k) - } - return sshKey, nil -} - -// NewPublicKey takes a pointer to rsa, dsa or ecdsa PublicKey -// and returns a corresponding ssh PublicKey instance. EC keys should use P256, P384 or P521. -func NewPublicKey(k interface{}) (PublicKey, error) { - var sshKey PublicKey - switch t := k.(type) { - case *rsa.PublicKey: - sshKey = (*rsaPublicKey)(t) - case *ecdsa.PublicKey: - if !supportedEllipticCurve(t.Curve) { - return nil, errors.New("ssh: only P256, P384 and P521 EC keys are supported.") - } - sshKey = (*ecdsaPublicKey)(t) - case *dsa.PublicKey: - sshKey = (*dsaPublicKey)(t) - default: - return nil, fmt.Errorf("ssh: unsupported key type %T", k) - } - return sshKey, nil -} - -// ParsePrivateKey returns a Signer from a PEM encoded private key. It supports -// the same keys as ParseRawPrivateKey. -func ParsePrivateKey(pemBytes []byte) (Signer, error) { - key, err := ParseRawPrivateKey(pemBytes) - if err != nil { - return nil, err - } - - return NewSignerFromKey(key) -} - -// ParseRawPrivateKey returns a private key from a PEM encoded private key. It -// supports RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys. -func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) { - block, _ := pem.Decode(pemBytes) - if block == nil { - return nil, errors.New("ssh: no key found") - } - - switch block.Type { - case "RSA PRIVATE KEY": - return x509.ParsePKCS1PrivateKey(block.Bytes) - case "EC PRIVATE KEY": - return x509.ParseECPrivateKey(block.Bytes) - case "DSA PRIVATE KEY": - return ParseDSAPrivateKey(block.Bytes) - default: - return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type) - } -} - -// ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as -// specified by the OpenSSL DSA man page. -func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { - var k struct { - Version int - P *big.Int - Q *big.Int - G *big.Int - Priv *big.Int - Pub *big.Int - } - rest, err := asn1.Unmarshal(der, &k) - if err != nil { - return nil, errors.New("ssh: failed to parse DSA key: " + err.Error()) - } - if len(rest) > 0 { - return nil, errors.New("ssh: garbage after DSA key") - } - - return &dsa.PrivateKey{ - PublicKey: dsa.PublicKey{ - Parameters: dsa.Parameters{ - P: k.P, - Q: k.Q, - G: k.G, - }, - Y: k.Priv, - }, - X: k.Pub, - }, nil -} diff --git a/modules/crypto/ssh/keys_test.go b/modules/crypto/ssh/keys_test.go deleted file mode 100755 index 2b19ef92..00000000 --- a/modules/crypto/ssh/keys_test.go +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bytes" - "crypto/dsa" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "encoding/base64" - "fmt" - "reflect" - "strings" - "testing" - - "github.com/gogits/gogs/modules/crypto/ssh/testdata" -) - -func rawKey(pub PublicKey) interface{} { - switch k := pub.(type) { - case *rsaPublicKey: - return (*rsa.PublicKey)(k) - case *dsaPublicKey: - return (*dsa.PublicKey)(k) - case *ecdsaPublicKey: - return (*ecdsa.PublicKey)(k) - case *Certificate: - return k - } - panic("unknown key type") -} - -func TestKeyMarshalParse(t *testing.T) { - for _, priv := range testSigners { - pub := priv.PublicKey() - roundtrip, err := ParsePublicKey(pub.Marshal()) - if err != nil { - t.Errorf("ParsePublicKey(%T): %v", pub, err) - } - - k1 := rawKey(pub) - k2 := rawKey(roundtrip) - - if !reflect.DeepEqual(k1, k2) { - t.Errorf("got %#v in roundtrip, want %#v", k2, k1) - } - } -} - -func TestUnsupportedCurves(t *testing.T) { - raw, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader) - if err != nil { - t.Fatalf("GenerateKey: %v", err) - } - - if _, err = NewSignerFromKey(raw); err == nil || !strings.Contains(err.Error(), "only P256") { - t.Fatalf("NewPrivateKey should not succeed with P224, got: %v", err) - } - - if _, err = NewPublicKey(&raw.PublicKey); err == nil || !strings.Contains(err.Error(), "only P256") { - t.Fatalf("NewPublicKey should not succeed with P224, got: %v", err) - } -} - -func TestNewPublicKey(t *testing.T) { - for _, k := range testSigners { - raw := rawKey(k.PublicKey()) - // Skip certificates, as NewPublicKey does not support them. - if _, ok := raw.(*Certificate); ok { - continue - } - pub, err := NewPublicKey(raw) - if err != nil { - t.Errorf("NewPublicKey(%#v): %v", raw, err) - } - if !reflect.DeepEqual(k.PublicKey(), pub) { - t.Errorf("NewPublicKey(%#v) = %#v, want %#v", raw, pub, k.PublicKey()) - } - } -} - -func TestKeySignVerify(t *testing.T) { - for _, priv := range testSigners { - pub := priv.PublicKey() - - data := []byte("sign me") - sig, err := priv.Sign(rand.Reader, data) - if err != nil { - t.Fatalf("Sign(%T): %v", priv, err) - } - - if err := pub.Verify(data, sig); err != nil { - t.Errorf("publicKey.Verify(%T): %v", priv, err) - } - sig.Blob[5]++ - if err := pub.Verify(data, sig); err == nil { - t.Errorf("publicKey.Verify on broken sig did not fail") - } - } -} - -func TestParseRSAPrivateKey(t *testing.T) { - key := testPrivateKeys["rsa"] - - rsa, ok := key.(*rsa.PrivateKey) - if !ok { - t.Fatalf("got %T, want *rsa.PrivateKey", rsa) - } - - if err := rsa.Validate(); err != nil { - t.Errorf("Validate: %v", err) - } -} - -func TestParseECPrivateKey(t *testing.T) { - key := testPrivateKeys["ecdsa"] - - ecKey, ok := key.(*ecdsa.PrivateKey) - if !ok { - t.Fatalf("got %T, want *ecdsa.PrivateKey", ecKey) - } - - if !validateECPublicKey(ecKey.Curve, ecKey.X, ecKey.Y) { - t.Fatalf("public key does not validate.") - } -} - -func TestParseDSA(t *testing.T) { - // We actually exercise the ParsePrivateKey codepath here, as opposed to - // using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go - // uses. - s, err := ParsePrivateKey(testdata.PEMBytes["dsa"]) - if err != nil { - t.Fatalf("ParsePrivateKey returned error: %s", err) - } - - data := []byte("sign me") - sig, err := s.Sign(rand.Reader, data) - if err != nil { - t.Fatalf("dsa.Sign: %v", err) - } - - if err := s.PublicKey().Verify(data, sig); err != nil { - t.Errorf("Verify failed: %v", err) - } -} - -// Tests for authorized_keys parsing. - -// getTestKey returns a public key, and its base64 encoding. -func getTestKey() (PublicKey, string) { - k := testPublicKeys["rsa"] - - b := &bytes.Buffer{} - e := base64.NewEncoder(base64.StdEncoding, b) - e.Write(k.Marshal()) - e.Close() - - return k, b.String() -} - -func TestMarshalParsePublicKey(t *testing.T) { - pub, pubSerialized := getTestKey() - line := fmt.Sprintf("%s %s user@host", pub.Type(), pubSerialized) - - authKeys := MarshalAuthorizedKey(pub) - actualFields := strings.Fields(string(authKeys)) - if len(actualFields) == 0 { - t.Fatalf("failed authKeys: %v", authKeys) - } - - // drop the comment - expectedFields := strings.Fields(line)[0:2] - - if !reflect.DeepEqual(actualFields, expectedFields) { - t.Errorf("got %v, expected %v", actualFields, expectedFields) - } - - actPub, _, _, _, err := ParseAuthorizedKey([]byte(line)) - if err != nil { - t.Fatalf("cannot parse %v: %v", line, err) - } - if !reflect.DeepEqual(actPub, pub) { - t.Errorf("got %v, expected %v", actPub, pub) - } -} - -type authResult struct { - pubKey PublicKey - options []string - comments string - rest string - ok bool -} - -func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []authResult) { - rest := authKeys - var values []authResult - for len(rest) > 0 { - var r authResult - var err error - r.pubKey, r.comments, r.options, rest, err = ParseAuthorizedKey(rest) - r.ok = (err == nil) - t.Log(err) - r.rest = string(rest) - values = append(values, r) - } - - if !reflect.DeepEqual(values, expected) { - t.Errorf("got %#v, expected %#v", values, expected) - } -} - -func TestAuthorizedKeyBasic(t *testing.T) { - pub, pubSerialized := getTestKey() - line := "ssh-rsa " + pubSerialized + " user@host" - testAuthorizedKeys(t, []byte(line), - []authResult{ - {pub, nil, "user@host", "", true}, - }) -} - -func TestAuth(t *testing.T) { - pub, pubSerialized := getTestKey() - authWithOptions := []string{ - `# comments to ignore before any keys...`, - ``, - `env="HOME=/home/root",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`, - `# comments to ignore, along with a blank line`, - ``, - `env="HOME=/home/root2" ssh-rsa ` + pubSerialized + ` user2@host2`, - ``, - `# more comments, plus a invalid entry`, - `ssh-rsa data-that-will-not-parse user@host3`, - } - for _, eol := range []string{"\n", "\r\n"} { - authOptions := strings.Join(authWithOptions, eol) - rest2 := strings.Join(authWithOptions[3:], eol) - rest3 := strings.Join(authWithOptions[6:], eol) - testAuthorizedKeys(t, []byte(authOptions), []authResult{ - {pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "user@host", rest2, true}, - {pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3, true}, - {nil, nil, "", "", false}, - }) - } -} - -func TestAuthWithQuotedSpaceInEnv(t *testing.T) { - pub, pubSerialized := getTestKey() - authWithQuotedSpaceInEnv := []byte(`env="HOME=/home/root dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`) - testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []authResult{ - {pub, []string{`env="HOME=/home/root dir"`, "no-port-forwarding"}, "user@host", "", true}, - }) -} - -func TestAuthWithQuotedCommaInEnv(t *testing.T) { - pub, pubSerialized := getTestKey() - authWithQuotedCommaInEnv := []byte(`env="HOME=/home/root,dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`) - testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []authResult{ - {pub, []string{`env="HOME=/home/root,dir"`, "no-port-forwarding"}, "user@host", "", true}, - }) -} - -func TestAuthWithQuotedQuoteInEnv(t *testing.T) { - pub, pubSerialized := getTestKey() - authWithQuotedQuoteInEnv := []byte(`env="HOME=/home/\"root dir",no-port-forwarding` + "\t" + `ssh-rsa` + "\t" + pubSerialized + ` user@host`) - authWithDoubleQuotedQuote := []byte(`no-port-forwarding,env="HOME=/home/ \"root dir\"" ssh-rsa ` + pubSerialized + "\t" + `user@host`) - testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []authResult{ - {pub, []string{`env="HOME=/home/\"root dir"`, "no-port-forwarding"}, "user@host", "", true}, - }) - - testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []authResult{ - {pub, []string{"no-port-forwarding", `env="HOME=/home/ \"root dir\""`}, "user@host", "", true}, - }) -} - -func TestAuthWithInvalidSpace(t *testing.T) { - _, pubSerialized := getTestKey() - authWithInvalidSpace := []byte(`env="HOME=/home/root dir", no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host -#more to follow but still no valid keys`) - testAuthorizedKeys(t, []byte(authWithInvalidSpace), []authResult{ - {nil, nil, "", "", false}, - }) -} - -func TestAuthWithMissingQuote(t *testing.T) { - pub, pubSerialized := getTestKey() - authWithMissingQuote := []byte(`env="HOME=/home/root,no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host -env="HOME=/home/root",shared-control ssh-rsa ` + pubSerialized + ` user@host`) - - testAuthorizedKeys(t, []byte(authWithMissingQuote), []authResult{ - {pub, []string{`env="HOME=/home/root"`, `shared-control`}, "user@host", "", true}, - }) -} - -func TestInvalidEntry(t *testing.T) { - authInvalid := []byte(`ssh-rsa`) - _, _, _, _, err := ParseAuthorizedKey(authInvalid) - if err == nil { - t.Errorf("got valid entry for %q", authInvalid) - } -} diff --git a/modules/crypto/ssh/mac.go b/modules/crypto/ssh/mac.go deleted file mode 100755 index 07744ad6..00000000 --- a/modules/crypto/ssh/mac.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -// Message authentication support - -import ( - "crypto/hmac" - "crypto/sha1" - "crypto/sha256" - "hash" -) - -type macMode struct { - keySize int - new func(key []byte) hash.Hash -} - -// truncatingMAC wraps around a hash.Hash and truncates the output digest to -// a given size. -type truncatingMAC struct { - length int - hmac hash.Hash -} - -func (t truncatingMAC) Write(data []byte) (int, error) { - return t.hmac.Write(data) -} - -func (t truncatingMAC) Sum(in []byte) []byte { - out := t.hmac.Sum(in) - return out[:len(in)+t.length] -} - -func (t truncatingMAC) Reset() { - t.hmac.Reset() -} - -func (t truncatingMAC) Size() int { - return t.length -} - -func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() } - -var macModes = map[string]*macMode{ - "hmac-sha2-256": {32, func(key []byte) hash.Hash { - return hmac.New(sha256.New, key) - }}, - "hmac-sha1": {20, func(key []byte) hash.Hash { - return hmac.New(sha1.New, key) - }}, - "hmac-sha1-96": {20, func(key []byte) hash.Hash { - return truncatingMAC{12, hmac.New(sha1.New, key)} - }}, -} diff --git a/modules/crypto/ssh/mempipe_test.go b/modules/crypto/ssh/mempipe_test.go deleted file mode 100755 index 8697cd61..00000000 --- a/modules/crypto/ssh/mempipe_test.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "io" - "sync" - "testing" -) - -// An in-memory packetConn. It is safe to call Close and writePacket -// from different goroutines. -type memTransport struct { - eof bool - pending [][]byte - write *memTransport - sync.Mutex - *sync.Cond -} - -func (t *memTransport) readPacket() ([]byte, error) { - t.Lock() - defer t.Unlock() - for { - if len(t.pending) > 0 { - r := t.pending[0] - t.pending = t.pending[1:] - return r, nil - } - if t.eof { - return nil, io.EOF - } - t.Cond.Wait() - } -} - -func (t *memTransport) closeSelf() error { - t.Lock() - defer t.Unlock() - if t.eof { - return io.EOF - } - t.eof = true - t.Cond.Broadcast() - return nil -} - -func (t *memTransport) Close() error { - err := t.write.closeSelf() - t.closeSelf() - return err -} - -func (t *memTransport) writePacket(p []byte) error { - t.write.Lock() - defer t.write.Unlock() - if t.write.eof { - return io.EOF - } - c := make([]byte, len(p)) - copy(c, p) - t.write.pending = append(t.write.pending, c) - t.write.Cond.Signal() - return nil -} - -func memPipe() (a, b packetConn) { - t1 := memTransport{} - t2 := memTransport{} - t1.write = &t2 - t2.write = &t1 - t1.Cond = sync.NewCond(&t1.Mutex) - t2.Cond = sync.NewCond(&t2.Mutex) - return &t1, &t2 -} - -func TestMemPipe(t *testing.T) { - a, b := memPipe() - if err := a.writePacket([]byte{42}); err != nil { - t.Fatalf("writePacket: %v", err) - } - if err := a.Close(); err != nil { - t.Fatal("Close: ", err) - } - p, err := b.readPacket() - if err != nil { - t.Fatal("readPacket: ", err) - } - if len(p) != 1 || p[0] != 42 { - t.Fatalf("got %v, want {42}", p) - } - p, err = b.readPacket() - if err != io.EOF { - t.Fatalf("got %v, %v, want EOF", p, err) - } -} - -func TestDoubleClose(t *testing.T) { - a, _ := memPipe() - err := a.Close() - if err != nil { - t.Errorf("Close: %v", err) - } - err = a.Close() - if err != io.EOF { - t.Errorf("expect EOF on double close.") - } -} diff --git a/modules/crypto/ssh/messages.go b/modules/crypto/ssh/messages.go deleted file mode 100755 index eaf61066..00000000 --- a/modules/crypto/ssh/messages.go +++ /dev/null @@ -1,725 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "io" - "math/big" - "reflect" - "strconv" -) - -// These are SSH message type numbers. They are scattered around several -// documents but many were taken from [SSH-PARAMETERS]. -const ( - msgIgnore = 2 - msgUnimplemented = 3 - msgDebug = 4 - msgNewKeys = 21 - - // Standard authentication messages - msgUserAuthSuccess = 52 - msgUserAuthBanner = 53 -) - -// SSH messages: -// -// These structures mirror the wire format of the corresponding SSH messages. -// They are marshaled using reflection with the marshal and unmarshal functions -// in this file. The only wrinkle is that a final member of type []byte with a -// ssh tag of "rest" receives the remainder of a packet when unmarshaling. - -// See RFC 4253, section 11.1. -const msgDisconnect = 1 - -// disconnectMsg is the message that signals a disconnect. It is also -// the error type returned from mux.Wait() -type disconnectMsg struct { - Reason uint32 `sshtype:"1"` - Message string - Language string -} - -func (d *disconnectMsg) Error() string { - return fmt.Sprintf("ssh: disconnect reason %d: %s", d.Reason, d.Message) -} - -// See RFC 4253, section 7.1. -const msgKexInit = 20 - -type kexInitMsg struct { - Cookie [16]byte `sshtype:"20"` - KexAlgos []string - ServerHostKeyAlgos []string - CiphersClientServer []string - CiphersServerClient []string - MACsClientServer []string - MACsServerClient []string - CompressionClientServer []string - CompressionServerClient []string - LanguagesClientServer []string - LanguagesServerClient []string - FirstKexFollows bool - Reserved uint32 -} - -// See RFC 4253, section 8. - -// Diffie-Helman -const msgKexDHInit = 30 - -type kexDHInitMsg struct { - X *big.Int `sshtype:"30"` -} - -const msgKexECDHInit = 30 - -type kexECDHInitMsg struct { - ClientPubKey []byte `sshtype:"30"` -} - -const msgKexECDHReply = 31 - -type kexECDHReplyMsg struct { - HostKey []byte `sshtype:"31"` - EphemeralPubKey []byte - Signature []byte -} - -const msgKexDHReply = 31 - -type kexDHReplyMsg struct { - HostKey []byte `sshtype:"31"` - Y *big.Int - Signature []byte -} - -// See RFC 4253, section 10. -const msgServiceRequest = 5 - -type serviceRequestMsg struct { - Service string `sshtype:"5"` -} - -// See RFC 4253, section 10. -const msgServiceAccept = 6 - -type serviceAcceptMsg struct { - Service string `sshtype:"6"` -} - -// See RFC 4252, section 5. -const msgUserAuthRequest = 50 - -type userAuthRequestMsg struct { - User string `sshtype:"50"` - Service string - Method string - Payload []byte `ssh:"rest"` -} - -// See RFC 4252, section 5.1 -const msgUserAuthFailure = 51 - -type userAuthFailureMsg struct { - Methods []string `sshtype:"51"` - PartialSuccess bool -} - -// See RFC 4256, section 3.2 -const msgUserAuthInfoRequest = 60 -const msgUserAuthInfoResponse = 61 - -type userAuthInfoRequestMsg struct { - User string `sshtype:"60"` - Instruction string - DeprecatedLanguage string - NumPrompts uint32 - Prompts []byte `ssh:"rest"` -} - -// See RFC 4254, section 5.1. -const msgChannelOpen = 90 - -type channelOpenMsg struct { - ChanType string `sshtype:"90"` - PeersId uint32 - PeersWindow uint32 - MaxPacketSize uint32 - TypeSpecificData []byte `ssh:"rest"` -} - -const msgChannelExtendedData = 95 -const msgChannelData = 94 - -// See RFC 4254, section 5.1. -const msgChannelOpenConfirm = 91 - -type channelOpenConfirmMsg struct { - PeersId uint32 `sshtype:"91"` - MyId uint32 - MyWindow uint32 - MaxPacketSize uint32 - TypeSpecificData []byte `ssh:"rest"` -} - -// See RFC 4254, section 5.1. -const msgChannelOpenFailure = 92 - -type channelOpenFailureMsg struct { - PeersId uint32 `sshtype:"92"` - Reason RejectionReason - Message string - Language string -} - -const msgChannelRequest = 98 - -type channelRequestMsg struct { - PeersId uint32 `sshtype:"98"` - Request string - WantReply bool - RequestSpecificData []byte `ssh:"rest"` -} - -// See RFC 4254, section 5.4. -const msgChannelSuccess = 99 - -type channelRequestSuccessMsg struct { - PeersId uint32 `sshtype:"99"` -} - -// See RFC 4254, section 5.4. -const msgChannelFailure = 100 - -type channelRequestFailureMsg struct { - PeersId uint32 `sshtype:"100"` -} - -// See RFC 4254, section 5.3 -const msgChannelClose = 97 - -type channelCloseMsg struct { - PeersId uint32 `sshtype:"97"` -} - -// See RFC 4254, section 5.3 -const msgChannelEOF = 96 - -type channelEOFMsg struct { - PeersId uint32 `sshtype:"96"` -} - -// See RFC 4254, section 4 -const msgGlobalRequest = 80 - -type globalRequestMsg struct { - Type string `sshtype:"80"` - WantReply bool - Data []byte `ssh:"rest"` -} - -// See RFC 4254, section 4 -const msgRequestSuccess = 81 - -type globalRequestSuccessMsg struct { - Data []byte `ssh:"rest" sshtype:"81"` -} - -// See RFC 4254, section 4 -const msgRequestFailure = 82 - -type globalRequestFailureMsg struct { - Data []byte `ssh:"rest" sshtype:"82"` -} - -// See RFC 4254, section 5.2 -const msgChannelWindowAdjust = 93 - -type windowAdjustMsg struct { - PeersId uint32 `sshtype:"93"` - AdditionalBytes uint32 -} - -// See RFC 4252, section 7 -const msgUserAuthPubKeyOk = 60 - -type userAuthPubKeyOkMsg struct { - Algo string `sshtype:"60"` - PubKey []byte -} - -// typeTag returns the type byte for the given type. The type should -// be struct. -func typeTag(structType reflect.Type) byte { - var tag byte - var tagStr string - tagStr = structType.Field(0).Tag.Get("sshtype") - i, err := strconv.Atoi(tagStr) - if err == nil { - tag = byte(i) - } - return tag -} - -func fieldError(t reflect.Type, field int, problem string) error { - if problem != "" { - problem = ": " + problem - } - return fmt.Errorf("ssh: unmarshal error for field %s of type %s%s", t.Field(field).Name, t.Name(), problem) -} - -var errShortRead = errors.New("ssh: short read") - -// Unmarshal parses data in SSH wire format into a structure. The out -// argument should be a pointer to struct. If the first member of the -// struct has the "sshtype" tag set to a number in decimal, the packet -// must start that number. In case of error, Unmarshal returns a -// ParseError or UnexpectedMessageError. -func Unmarshal(data []byte, out interface{}) error { - v := reflect.ValueOf(out).Elem() - structType := v.Type() - expectedType := typeTag(structType) - if len(data) == 0 { - return parseError(expectedType) - } - if expectedType > 0 { - if data[0] != expectedType { - return unexpectedMessageError(expectedType, data[0]) - } - data = data[1:] - } - - var ok bool - for i := 0; i < v.NumField(); i++ { - field := v.Field(i) - t := field.Type() - switch t.Kind() { - case reflect.Bool: - if len(data) < 1 { - return errShortRead - } - field.SetBool(data[0] != 0) - data = data[1:] - case reflect.Array: - if t.Elem().Kind() != reflect.Uint8 { - return fieldError(structType, i, "array of unsupported type") - } - if len(data) < t.Len() { - return errShortRead - } - for j, n := 0, t.Len(); j < n; j++ { - field.Index(j).Set(reflect.ValueOf(data[j])) - } - data = data[t.Len():] - case reflect.Uint64: - var u64 uint64 - if u64, data, ok = parseUint64(data); !ok { - return errShortRead - } - field.SetUint(u64) - case reflect.Uint32: - var u32 uint32 - if u32, data, ok = parseUint32(data); !ok { - return errShortRead - } - field.SetUint(uint64(u32)) - case reflect.Uint8: - if len(data) < 1 { - return errShortRead - } - field.SetUint(uint64(data[0])) - data = data[1:] - case reflect.String: - var s []byte - if s, data, ok = parseString(data); !ok { - return fieldError(structType, i, "") - } - field.SetString(string(s)) - case reflect.Slice: - switch t.Elem().Kind() { - case reflect.Uint8: - if structType.Field(i).Tag.Get("ssh") == "rest" { - field.Set(reflect.ValueOf(data)) - data = nil - } else { - var s []byte - if s, data, ok = parseString(data); !ok { - return errShortRead - } - field.Set(reflect.ValueOf(s)) - } - case reflect.String: - var nl []string - if nl, data, ok = parseNameList(data); !ok { - return errShortRead - } - field.Set(reflect.ValueOf(nl)) - default: - return fieldError(structType, i, "slice of unsupported type") - } - case reflect.Ptr: - if t == bigIntType { - var n *big.Int - if n, data, ok = parseInt(data); !ok { - return errShortRead - } - field.Set(reflect.ValueOf(n)) - } else { - return fieldError(structType, i, "pointer to unsupported type") - } - default: - return fieldError(structType, i, "unsupported type") - } - } - - if len(data) != 0 { - return parseError(expectedType) - } - - return nil -} - -// Marshal serializes the message in msg to SSH wire format. The msg -// argument should be a struct or pointer to struct. If the first -// member has the "sshtype" tag set to a number in decimal, that -// number is prepended to the result. If the last of member has the -// "ssh" tag set to "rest", its contents are appended to the output. -func Marshal(msg interface{}) []byte { - out := make([]byte, 0, 64) - return marshalStruct(out, msg) -} - -func marshalStruct(out []byte, msg interface{}) []byte { - v := reflect.Indirect(reflect.ValueOf(msg)) - msgType := typeTag(v.Type()) - if msgType > 0 { - out = append(out, msgType) - } - - for i, n := 0, v.NumField(); i < n; i++ { - field := v.Field(i) - switch t := field.Type(); t.Kind() { - case reflect.Bool: - var v uint8 - if field.Bool() { - v = 1 - } - out = append(out, v) - case reflect.Array: - if t.Elem().Kind() != reflect.Uint8 { - panic(fmt.Sprintf("array of non-uint8 in field %d: %T", i, field.Interface())) - } - for j, l := 0, t.Len(); j < l; j++ { - out = append(out, uint8(field.Index(j).Uint())) - } - case reflect.Uint32: - out = appendU32(out, uint32(field.Uint())) - case reflect.Uint64: - out = appendU64(out, uint64(field.Uint())) - case reflect.Uint8: - out = append(out, uint8(field.Uint())) - case reflect.String: - s := field.String() - out = appendInt(out, len(s)) - out = append(out, s...) - case reflect.Slice: - switch t.Elem().Kind() { - case reflect.Uint8: - if v.Type().Field(i).Tag.Get("ssh") != "rest" { - out = appendInt(out, field.Len()) - } - out = append(out, field.Bytes()...) - case reflect.String: - offset := len(out) - out = appendU32(out, 0) - if n := field.Len(); n > 0 { - for j := 0; j < n; j++ { - f := field.Index(j) - if j != 0 { - out = append(out, ',') - } - out = append(out, f.String()...) - } - // overwrite length value - binary.BigEndian.PutUint32(out[offset:], uint32(len(out)-offset-4)) - } - default: - panic(fmt.Sprintf("slice of unknown type in field %d: %T", i, field.Interface())) - } - case reflect.Ptr: - if t == bigIntType { - var n *big.Int - nValue := reflect.ValueOf(&n) - nValue.Elem().Set(field) - needed := intLength(n) - oldLength := len(out) - - if cap(out)-len(out) < needed { - newOut := make([]byte, len(out), 2*(len(out)+needed)) - copy(newOut, out) - out = newOut - } - out = out[:oldLength+needed] - marshalInt(out[oldLength:], n) - } else { - panic(fmt.Sprintf("pointer to unknown type in field %d: %T", i, field.Interface())) - } - } - } - - return out -} - -var bigOne = big.NewInt(1) - -func parseString(in []byte) (out, rest []byte, ok bool) { - if len(in) < 4 { - return - } - length := binary.BigEndian.Uint32(in) - in = in[4:] - if uint32(len(in)) < length { - return - } - out = in[:length] - rest = in[length:] - ok = true - return -} - -var ( - comma = []byte{','} - emptyNameList = []string{} -) - -func parseNameList(in []byte) (out []string, rest []byte, ok bool) { - contents, rest, ok := parseString(in) - if !ok { - return - } - if len(contents) == 0 { - out = emptyNameList - return - } - parts := bytes.Split(contents, comma) - out = make([]string, len(parts)) - for i, part := range parts { - out[i] = string(part) - } - return -} - -func parseInt(in []byte) (out *big.Int, rest []byte, ok bool) { - contents, rest, ok := parseString(in) - if !ok { - return - } - out = new(big.Int) - - if len(contents) > 0 && contents[0]&0x80 == 0x80 { - // This is a negative number - notBytes := make([]byte, len(contents)) - for i := range notBytes { - notBytes[i] = ^contents[i] - } - out.SetBytes(notBytes) - out.Add(out, bigOne) - out.Neg(out) - } else { - // Positive number - out.SetBytes(contents) - } - ok = true - return -} - -func parseUint32(in []byte) (uint32, []byte, bool) { - if len(in) < 4 { - return 0, nil, false - } - return binary.BigEndian.Uint32(in), in[4:], true -} - -func parseUint64(in []byte) (uint64, []byte, bool) { - if len(in) < 8 { - return 0, nil, false - } - return binary.BigEndian.Uint64(in), in[8:], true -} - -func intLength(n *big.Int) int { - length := 4 /* length bytes */ - if n.Sign() < 0 { - nMinus1 := new(big.Int).Neg(n) - nMinus1.Sub(nMinus1, bigOne) - bitLen := nMinus1.BitLen() - if bitLen%8 == 0 { - // The number will need 0xff padding - length++ - } - length += (bitLen + 7) / 8 - } else if n.Sign() == 0 { - // A zero is the zero length string - } else { - bitLen := n.BitLen() - if bitLen%8 == 0 { - // The number will need 0x00 padding - length++ - } - length += (bitLen + 7) / 8 - } - - return length -} - -func marshalUint32(to []byte, n uint32) []byte { - binary.BigEndian.PutUint32(to, n) - return to[4:] -} - -func marshalUint64(to []byte, n uint64) []byte { - binary.BigEndian.PutUint64(to, n) - return to[8:] -} - -func marshalInt(to []byte, n *big.Int) []byte { - lengthBytes := to - to = to[4:] - length := 0 - - if n.Sign() < 0 { - // A negative number has to be converted to two's-complement - // form. So we'll subtract 1 and invert. If the - // most-significant-bit isn't set then we'll need to pad the - // beginning with 0xff in order to keep the number negative. - nMinus1 := new(big.Int).Neg(n) - nMinus1.Sub(nMinus1, bigOne) - bytes := nMinus1.Bytes() - for i := range bytes { - bytes[i] ^= 0xff - } - if len(bytes) == 0 || bytes[0]&0x80 == 0 { - to[0] = 0xff - to = to[1:] - length++ - } - nBytes := copy(to, bytes) - to = to[nBytes:] - length += nBytes - } else if n.Sign() == 0 { - // A zero is the zero length string - } else { - bytes := n.Bytes() - if len(bytes) > 0 && bytes[0]&0x80 != 0 { - // We'll have to pad this with a 0x00 in order to - // stop it looking like a negative number. - to[0] = 0 - to = to[1:] - length++ - } - nBytes := copy(to, bytes) - to = to[nBytes:] - length += nBytes - } - - lengthBytes[0] = byte(length >> 24) - lengthBytes[1] = byte(length >> 16) - lengthBytes[2] = byte(length >> 8) - lengthBytes[3] = byte(length) - return to -} - -func writeInt(w io.Writer, n *big.Int) { - length := intLength(n) - buf := make([]byte, length) - marshalInt(buf, n) - w.Write(buf) -} - -func writeString(w io.Writer, s []byte) { - var lengthBytes [4]byte - lengthBytes[0] = byte(len(s) >> 24) - lengthBytes[1] = byte(len(s) >> 16) - lengthBytes[2] = byte(len(s) >> 8) - lengthBytes[3] = byte(len(s)) - w.Write(lengthBytes[:]) - w.Write(s) -} - -func stringLength(n int) int { - return 4 + n -} - -func marshalString(to []byte, s []byte) []byte { - to[0] = byte(len(s) >> 24) - to[1] = byte(len(s) >> 16) - to[2] = byte(len(s) >> 8) - to[3] = byte(len(s)) - to = to[4:] - copy(to, s) - return to[len(s):] -} - -var bigIntType = reflect.TypeOf((*big.Int)(nil)) - -// Decode a packet into its corresponding message. -func decode(packet []byte) (interface{}, error) { - var msg interface{} - switch packet[0] { - case msgDisconnect: - msg = new(disconnectMsg) - case msgServiceRequest: - msg = new(serviceRequestMsg) - case msgServiceAccept: - msg = new(serviceAcceptMsg) - case msgKexInit: - msg = new(kexInitMsg) - case msgKexDHInit: - msg = new(kexDHInitMsg) - case msgKexDHReply: - msg = new(kexDHReplyMsg) - case msgUserAuthRequest: - msg = new(userAuthRequestMsg) - case msgUserAuthFailure: - msg = new(userAuthFailureMsg) - case msgUserAuthPubKeyOk: - msg = new(userAuthPubKeyOkMsg) - case msgGlobalRequest: - msg = new(globalRequestMsg) - case msgRequestSuccess: - msg = new(globalRequestSuccessMsg) - case msgRequestFailure: - msg = new(globalRequestFailureMsg) - case msgChannelOpen: - msg = new(channelOpenMsg) - case msgChannelOpenConfirm: - msg = new(channelOpenConfirmMsg) - case msgChannelOpenFailure: - msg = new(channelOpenFailureMsg) - case msgChannelWindowAdjust: - msg = new(windowAdjustMsg) - case msgChannelEOF: - msg = new(channelEOFMsg) - case msgChannelClose: - msg = new(channelCloseMsg) - case msgChannelRequest: - msg = new(channelRequestMsg) - case msgChannelSuccess: - msg = new(channelRequestSuccessMsg) - case msgChannelFailure: - msg = new(channelRequestFailureMsg) - default: - return nil, unexpectedMessageError(0, packet[0]) - } - if err := Unmarshal(packet, msg); err != nil { - return nil, err - } - return msg, nil -} diff --git a/modules/crypto/ssh/messages_test.go b/modules/crypto/ssh/messages_test.go deleted file mode 100755 index 955b5127..00000000 --- a/modules/crypto/ssh/messages_test.go +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bytes" - "math/big" - "math/rand" - "reflect" - "testing" - "testing/quick" -) - -var intLengthTests = []struct { - val, length int -}{ - {0, 4 + 0}, - {1, 4 + 1}, - {127, 4 + 1}, - {128, 4 + 2}, - {-1, 4 + 1}, -} - -func TestIntLength(t *testing.T) { - for _, test := range intLengthTests { - v := new(big.Int).SetInt64(int64(test.val)) - length := intLength(v) - if length != test.length { - t.Errorf("For %d, got length %d but expected %d", test.val, length, test.length) - } - } -} - -type msgAllTypes struct { - Bool bool `sshtype:"21"` - Array [16]byte - Uint64 uint64 - Uint32 uint32 - Uint8 uint8 - String string - Strings []string - Bytes []byte - Int *big.Int - Rest []byte `ssh:"rest"` -} - -func (t *msgAllTypes) Generate(rand *rand.Rand, size int) reflect.Value { - m := &msgAllTypes{} - m.Bool = rand.Intn(2) == 1 - randomBytes(m.Array[:], rand) - m.Uint64 = uint64(rand.Int63n(1<<63 - 1)) - m.Uint32 = uint32(rand.Intn((1 << 31) - 1)) - m.Uint8 = uint8(rand.Intn(1 << 8)) - m.String = string(m.Array[:]) - m.Strings = randomNameList(rand) - m.Bytes = m.Array[:] - m.Int = randomInt(rand) - m.Rest = m.Array[:] - return reflect.ValueOf(m) -} - -func TestMarshalUnmarshal(t *testing.T) { - rand := rand.New(rand.NewSource(0)) - iface := &msgAllTypes{} - ty := reflect.ValueOf(iface).Type() - - n := 100 - if testing.Short() { - n = 5 - } - for j := 0; j < n; j++ { - v, ok := quick.Value(ty, rand) - if !ok { - t.Errorf("failed to create value") - break - } - - m1 := v.Elem().Interface() - m2 := iface - - marshaled := Marshal(m1) - if err := Unmarshal(marshaled, m2); err != nil { - t.Errorf("Unmarshal %#v: %s", m1, err) - break - } - - if !reflect.DeepEqual(v.Interface(), m2) { - t.Errorf("got: %#v\nwant:%#v\n%x", m2, m1, marshaled) - break - } - } -} - -func TestUnmarshalEmptyPacket(t *testing.T) { - var b []byte - var m channelRequestSuccessMsg - if err := Unmarshal(b, &m); err == nil { - t.Fatalf("unmarshal of empty slice succeeded") - } -} - -func TestUnmarshalUnexpectedPacket(t *testing.T) { - type S struct { - I uint32 `sshtype:"43"` - S string - B bool - } - - s := S{11, "hello", true} - packet := Marshal(s) - packet[0] = 42 - roundtrip := S{} - err := Unmarshal(packet, &roundtrip) - if err == nil { - t.Fatal("expected error, not nil") - } -} - -func TestMarshalPtr(t *testing.T) { - s := struct { - S string - }{"hello"} - - m1 := Marshal(s) - m2 := Marshal(&s) - if !bytes.Equal(m1, m2) { - t.Errorf("got %q, want %q for marshaled pointer", m2, m1) - } -} - -func TestBareMarshalUnmarshal(t *testing.T) { - type S struct { - I uint32 - S string - B bool - } - - s := S{42, "hello", true} - packet := Marshal(s) - roundtrip := S{} - Unmarshal(packet, &roundtrip) - - if !reflect.DeepEqual(s, roundtrip) { - t.Errorf("got %#v, want %#v", roundtrip, s) - } -} - -func TestBareMarshal(t *testing.T) { - type S2 struct { - I uint32 - } - s := S2{42} - packet := Marshal(s) - i, rest, ok := parseUint32(packet) - if len(rest) > 0 || !ok { - t.Errorf("parseInt(%q): parse error", packet) - } - if i != s.I { - t.Errorf("got %d, want %d", i, s.I) - } -} - -func TestUnmarshalShortKexInitPacket(t *testing.T) { - // This used to panic. - // Issue 11348 - packet := []byte{0x14, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xff, 0xff, 0xff, 0xff} - kim := &kexInitMsg{} - if err := Unmarshal(packet, kim); err == nil { - t.Error("truncated packet unmarshaled without error") - } -} - -func randomBytes(out []byte, rand *rand.Rand) { - for i := 0; i < len(out); i++ { - out[i] = byte(rand.Int31()) - } -} - -func randomNameList(rand *rand.Rand) []string { - ret := make([]string, rand.Int31()&15) - for i := range ret { - s := make([]byte, 1+(rand.Int31()&15)) - for j := range s { - s[j] = 'a' + uint8(rand.Int31()&15) - } - ret[i] = string(s) - } - return ret -} - -func randomInt(rand *rand.Rand) *big.Int { - return new(big.Int).SetInt64(int64(int32(rand.Uint32()))) -} - -func (*kexInitMsg) Generate(rand *rand.Rand, size int) reflect.Value { - ki := &kexInitMsg{} - randomBytes(ki.Cookie[:], rand) - ki.KexAlgos = randomNameList(rand) - ki.ServerHostKeyAlgos = randomNameList(rand) - ki.CiphersClientServer = randomNameList(rand) - ki.CiphersServerClient = randomNameList(rand) - ki.MACsClientServer = randomNameList(rand) - ki.MACsServerClient = randomNameList(rand) - ki.CompressionClientServer = randomNameList(rand) - ki.CompressionServerClient = randomNameList(rand) - ki.LanguagesClientServer = randomNameList(rand) - ki.LanguagesServerClient = randomNameList(rand) - if rand.Int31()&1 == 1 { - ki.FirstKexFollows = true - } - return reflect.ValueOf(ki) -} - -func (*kexDHInitMsg) Generate(rand *rand.Rand, size int) reflect.Value { - dhi := &kexDHInitMsg{} - dhi.X = randomInt(rand) - return reflect.ValueOf(dhi) -} - -var ( - _kexInitMsg = new(kexInitMsg).Generate(rand.New(rand.NewSource(0)), 10).Elem().Interface() - _kexDHInitMsg = new(kexDHInitMsg).Generate(rand.New(rand.NewSource(0)), 10).Elem().Interface() - - _kexInit = Marshal(_kexInitMsg) - _kexDHInit = Marshal(_kexDHInitMsg) -) - -func BenchmarkMarshalKexInitMsg(b *testing.B) { - for i := 0; i < b.N; i++ { - Marshal(_kexInitMsg) - } -} - -func BenchmarkUnmarshalKexInitMsg(b *testing.B) { - m := new(kexInitMsg) - for i := 0; i < b.N; i++ { - Unmarshal(_kexInit, m) - } -} - -func BenchmarkMarshalKexDHInitMsg(b *testing.B) { - for i := 0; i < b.N; i++ { - Marshal(_kexDHInitMsg) - } -} - -func BenchmarkUnmarshalKexDHInitMsg(b *testing.B) { - m := new(kexDHInitMsg) - for i := 0; i < b.N; i++ { - Unmarshal(_kexDHInit, m) - } -} diff --git a/modules/crypto/ssh/mux.go b/modules/crypto/ssh/mux.go deleted file mode 100755 index 321880ad..00000000 --- a/modules/crypto/ssh/mux.go +++ /dev/null @@ -1,356 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "encoding/binary" - "fmt" - "io" - "log" - "sync" - "sync/atomic" -) - -// debugMux, if set, causes messages in the connection protocol to be -// logged. -const debugMux = false - -// chanList is a thread safe channel list. -type chanList struct { - // protects concurrent access to chans - sync.Mutex - - // chans are indexed by the local id of the channel, which the - // other side should send in the PeersId field. - chans []*channel - - // This is a debugging aid: it offsets all IDs by this - // amount. This helps distinguish otherwise identical - // server/client muxes - offset uint32 -} - -// Assigns a channel ID to the given channel. -func (c *chanList) add(ch *channel) uint32 { - c.Lock() - defer c.Unlock() - for i := range c.chans { - if c.chans[i] == nil { - c.chans[i] = ch - return uint32(i) + c.offset - } - } - c.chans = append(c.chans, ch) - return uint32(len(c.chans)-1) + c.offset -} - -// getChan returns the channel for the given ID. -func (c *chanList) getChan(id uint32) *channel { - id -= c.offset - - c.Lock() - defer c.Unlock() - if id < uint32(len(c.chans)) { - return c.chans[id] - } - return nil -} - -func (c *chanList) remove(id uint32) { - id -= c.offset - c.Lock() - if id < uint32(len(c.chans)) { - c.chans[id] = nil - } - c.Unlock() -} - -// dropAll forgets all channels it knows, returning them in a slice. -func (c *chanList) dropAll() []*channel { - c.Lock() - defer c.Unlock() - var r []*channel - - for _, ch := range c.chans { - if ch == nil { - continue - } - r = append(r, ch) - } - c.chans = nil - return r -} - -// mux represents the state for the SSH connection protocol, which -// multiplexes many channels onto a single packet transport. -type mux struct { - conn packetConn - chanList chanList - - incomingChannels chan NewChannel - - globalSentMu sync.Mutex - globalResponses chan interface{} - incomingRequests chan *Request - - errCond *sync.Cond - err error -} - -// When debugging, each new chanList instantiation has a different -// offset. -var globalOff uint32 - -func (m *mux) Wait() error { - m.errCond.L.Lock() - defer m.errCond.L.Unlock() - for m.err == nil { - m.errCond.Wait() - } - return m.err -} - -// newMux returns a mux that runs over the given connection. -func newMux(p packetConn) *mux { - m := &mux{ - conn: p, - incomingChannels: make(chan NewChannel, 16), - globalResponses: make(chan interface{}, 1), - incomingRequests: make(chan *Request, 16), - errCond: newCond(), - } - if debugMux { - m.chanList.offset = atomic.AddUint32(&globalOff, 1) - } - - go m.loop() - return m -} - -func (m *mux) sendMessage(msg interface{}) error { - p := Marshal(msg) - return m.conn.writePacket(p) -} - -func (m *mux) SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error) { - if wantReply { - m.globalSentMu.Lock() - defer m.globalSentMu.Unlock() - } - - if err := m.sendMessage(globalRequestMsg{ - Type: name, - WantReply: wantReply, - Data: payload, - }); err != nil { - return false, nil, err - } - - if !wantReply { - return false, nil, nil - } - - msg, ok := <-m.globalResponses - if !ok { - return false, nil, io.EOF - } - switch msg := msg.(type) { - case *globalRequestFailureMsg: - return false, msg.Data, nil - case *globalRequestSuccessMsg: - return true, msg.Data, nil - default: - return false, nil, fmt.Errorf("ssh: unexpected response to request: %#v", msg) - } -} - -// ackRequest must be called after processing a global request that -// has WantReply set. -func (m *mux) ackRequest(ok bool, data []byte) error { - if ok { - return m.sendMessage(globalRequestSuccessMsg{Data: data}) - } - return m.sendMessage(globalRequestFailureMsg{Data: data}) -} - -// TODO(hanwen): Disconnect is a transport layer message. We should -// probably send and receive Disconnect somewhere in the transport -// code. - -// Disconnect sends a disconnect message. -func (m *mux) Disconnect(reason uint32, message string) error { - return m.sendMessage(disconnectMsg{ - Reason: reason, - Message: message, - }) -} - -func (m *mux) Close() error { - return m.conn.Close() -} - -// loop runs the connection machine. It will process packets until an -// error is encountered. To synchronize on loop exit, use mux.Wait. -func (m *mux) loop() { - var err error - for err == nil { - err = m.onePacket() - } - - for _, ch := range m.chanList.dropAll() { - ch.close() - } - - close(m.incomingChannels) - close(m.incomingRequests) - close(m.globalResponses) - - m.conn.Close() - - m.errCond.L.Lock() - m.err = err - m.errCond.Broadcast() - m.errCond.L.Unlock() - - if debugMux { - log.Println("loop exit", err) - } -} - -// onePacket reads and processes one packet. -func (m *mux) onePacket() error { - packet, err := m.conn.readPacket() - if err != nil { - return err - } - - if debugMux { - if packet[0] == msgChannelData || packet[0] == msgChannelExtendedData { - log.Printf("decoding(%d): data packet - %d bytes", m.chanList.offset, len(packet)) - } else { - p, _ := decode(packet) - log.Printf("decoding(%d): %d %#v - %d bytes", m.chanList.offset, packet[0], p, len(packet)) - } - } - - switch packet[0] { - case msgNewKeys: - // Ignore notification of key change. - return nil - case msgDisconnect: - return m.handleDisconnect(packet) - case msgChannelOpen: - return m.handleChannelOpen(packet) - case msgGlobalRequest, msgRequestSuccess, msgRequestFailure: - return m.handleGlobalPacket(packet) - } - - // assume a channel packet. - if len(packet) < 5 { - return parseError(packet[0]) - } - id := binary.BigEndian.Uint32(packet[1:]) - ch := m.chanList.getChan(id) - if ch == nil { - return fmt.Errorf("ssh: invalid channel %d", id) - } - - return ch.handlePacket(packet) -} - -func (m *mux) handleDisconnect(packet []byte) error { - var d disconnectMsg - if err := Unmarshal(packet, &d); err != nil { - return err - } - - if debugMux { - log.Printf("caught disconnect: %v", d) - } - return &d -} - -func (m *mux) handleGlobalPacket(packet []byte) error { - msg, err := decode(packet) - if err != nil { - return err - } - - switch msg := msg.(type) { - case *globalRequestMsg: - m.incomingRequests <- &Request{ - Type: msg.Type, - WantReply: msg.WantReply, - Payload: msg.Data, - mux: m, - } - case *globalRequestSuccessMsg, *globalRequestFailureMsg: - m.globalResponses <- msg - default: - panic(fmt.Sprintf("not a global message %#v", msg)) - } - - return nil -} - -// handleChannelOpen schedules a channel to be Accept()ed. -func (m *mux) handleChannelOpen(packet []byte) error { - var msg channelOpenMsg - if err := Unmarshal(packet, &msg); err != nil { - return err - } - - if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 { - failMsg := channelOpenFailureMsg{ - PeersId: msg.PeersId, - Reason: ConnectionFailed, - Message: "invalid request", - Language: "en_US.UTF-8", - } - return m.sendMessage(failMsg) - } - - c := m.newChannel(msg.ChanType, channelInbound, msg.TypeSpecificData) - c.remoteId = msg.PeersId - c.maxRemotePayload = msg.MaxPacketSize - c.remoteWin.add(msg.PeersWindow) - m.incomingChannels <- c - return nil -} - -func (m *mux) OpenChannel(chanType string, extra []byte) (Channel, <-chan *Request, error) { - ch, err := m.openChannel(chanType, extra) - if err != nil { - return nil, nil, err - } - - return ch, ch.incomingRequests, nil -} - -func (m *mux) openChannel(chanType string, extra []byte) (*channel, error) { - ch := m.newChannel(chanType, channelOutbound, extra) - - ch.maxIncomingPayload = channelMaxPacket - - open := channelOpenMsg{ - ChanType: chanType, - PeersWindow: ch.myWindow, - MaxPacketSize: ch.maxIncomingPayload, - TypeSpecificData: extra, - PeersId: ch.localId, - } - if err := m.sendMessage(open); err != nil { - return nil, err - } - - switch msg := (<-ch.msg).(type) { - case *channelOpenConfirmMsg: - return ch, nil - case *channelOpenFailureMsg: - return nil, &OpenChannelError{msg.Reason, msg.Message} - default: - return nil, fmt.Errorf("ssh: unexpected packet in response to channel open: %T", msg) - } -} diff --git a/modules/crypto/ssh/mux_test.go b/modules/crypto/ssh/mux_test.go deleted file mode 100755 index 52303896..00000000 --- a/modules/crypto/ssh/mux_test.go +++ /dev/null @@ -1,525 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "io" - "io/ioutil" - "sync" - "testing" -) - -func muxPair() (*mux, *mux) { - a, b := memPipe() - - s := newMux(a) - c := newMux(b) - - return s, c -} - -// Returns both ends of a channel, and the mux for the the 2nd -// channel. -func channelPair(t *testing.T) (*channel, *channel, *mux) { - c, s := muxPair() - - res := make(chan *channel, 1) - go func() { - newCh, ok := <-s.incomingChannels - if !ok { - t.Fatalf("No incoming channel") - } - if newCh.ChannelType() != "chan" { - t.Fatalf("got type %q want chan", newCh.ChannelType()) - } - ch, _, err := newCh.Accept() - if err != nil { - t.Fatalf("Accept %v", err) - } - res <- ch.(*channel) - }() - - ch, err := c.openChannel("chan", nil) - if err != nil { - t.Fatalf("OpenChannel: %v", err) - } - - return <-res, ch, c -} - -// Test that stderr and stdout can be addressed from different -// goroutines. This is intended for use with the race detector. -func TestMuxChannelExtendedThreadSafety(t *testing.T) { - writer, reader, mux := channelPair(t) - defer writer.Close() - defer reader.Close() - defer mux.Close() - - var wr, rd sync.WaitGroup - magic := "hello world" - - wr.Add(2) - go func() { - io.WriteString(writer, magic) - wr.Done() - }() - go func() { - io.WriteString(writer.Stderr(), magic) - wr.Done() - }() - - rd.Add(2) - go func() { - c, err := ioutil.ReadAll(reader) - if string(c) != magic { - t.Fatalf("stdout read got %q, want %q (error %s)", c, magic, err) - } - rd.Done() - }() - go func() { - c, err := ioutil.ReadAll(reader.Stderr()) - if string(c) != magic { - t.Fatalf("stderr read got %q, want %q (error %s)", c, magic, err) - } - rd.Done() - }() - - wr.Wait() - writer.CloseWrite() - rd.Wait() -} - -func TestMuxReadWrite(t *testing.T) { - s, c, mux := channelPair(t) - defer s.Close() - defer c.Close() - defer mux.Close() - - magic := "hello world" - magicExt := "hello stderr" - go func() { - _, err := s.Write([]byte(magic)) - if err != nil { - t.Fatalf("Write: %v", err) - } - _, err = s.Extended(1).Write([]byte(magicExt)) - if err != nil { - t.Fatalf("Write: %v", err) - } - err = s.Close() - if err != nil { - t.Fatalf("Close: %v", err) - } - }() - - var buf [1024]byte - n, err := c.Read(buf[:]) - if err != nil { - t.Fatalf("server Read: %v", err) - } - got := string(buf[:n]) - if got != magic { - t.Fatalf("server: got %q want %q", got, magic) - } - - n, err = c.Extended(1).Read(buf[:]) - if err != nil { - t.Fatalf("server Read: %v", err) - } - - got = string(buf[:n]) - if got != magicExt { - t.Fatalf("server: got %q want %q", got, magic) - } -} - -func TestMuxChannelOverflow(t *testing.T) { - reader, writer, mux := channelPair(t) - defer reader.Close() - defer writer.Close() - defer mux.Close() - - wDone := make(chan int, 1) - go func() { - if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil { - t.Errorf("could not fill window: %v", err) - } - writer.Write(make([]byte, 1)) - wDone <- 1 - }() - writer.remoteWin.waitWriterBlocked() - - // Send 1 byte. - packet := make([]byte, 1+4+4+1) - packet[0] = msgChannelData - marshalUint32(packet[1:], writer.remoteId) - marshalUint32(packet[5:], uint32(1)) - packet[9] = 42 - - if err := writer.mux.conn.writePacket(packet); err != nil { - t.Errorf("could not send packet") - } - if _, err := reader.SendRequest("hello", true, nil); err == nil { - t.Errorf("SendRequest succeeded.") - } - <-wDone -} - -func TestMuxChannelCloseWriteUnblock(t *testing.T) { - reader, writer, mux := channelPair(t) - defer reader.Close() - defer writer.Close() - defer mux.Close() - - wDone := make(chan int, 1) - go func() { - if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil { - t.Errorf("could not fill window: %v", err) - } - if _, err := writer.Write(make([]byte, 1)); err != io.EOF { - t.Errorf("got %v, want EOF for unblock write", err) - } - wDone <- 1 - }() - - writer.remoteWin.waitWriterBlocked() - reader.Close() - <-wDone -} - -func TestMuxConnectionCloseWriteUnblock(t *testing.T) { - reader, writer, mux := channelPair(t) - defer reader.Close() - defer writer.Close() - defer mux.Close() - - wDone := make(chan int, 1) - go func() { - if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil { - t.Errorf("could not fill window: %v", err) - } - if _, err := writer.Write(make([]byte, 1)); err != io.EOF { - t.Errorf("got %v, want EOF for unblock write", err) - } - wDone <- 1 - }() - - writer.remoteWin.waitWriterBlocked() - mux.Close() - <-wDone -} - -func TestMuxReject(t *testing.T) { - client, server := muxPair() - defer server.Close() - defer client.Close() - - go func() { - ch, ok := <-server.incomingChannels - if !ok { - t.Fatalf("Accept") - } - if ch.ChannelType() != "ch" || string(ch.ExtraData()) != "extra" { - t.Fatalf("unexpected channel: %q, %q", ch.ChannelType(), ch.ExtraData()) - } - ch.Reject(RejectionReason(42), "message") - }() - - ch, err := client.openChannel("ch", []byte("extra")) - if ch != nil { - t.Fatal("openChannel not rejected") - } - - ocf, ok := err.(*OpenChannelError) - if !ok { - t.Errorf("got %#v want *OpenChannelError", err) - } else if ocf.Reason != 42 || ocf.Message != "message" { - t.Errorf("got %#v, want {Reason: 42, Message: %q}", ocf, "message") - } - - want := "ssh: rejected: unknown reason 42 (message)" - if err.Error() != want { - t.Errorf("got %q, want %q", err.Error(), want) - } -} - -func TestMuxChannelRequest(t *testing.T) { - client, server, mux := channelPair(t) - defer server.Close() - defer client.Close() - defer mux.Close() - - var received int - var wg sync.WaitGroup - wg.Add(1) - go func() { - for r := range server.incomingRequests { - received++ - r.Reply(r.Type == "yes", nil) - } - wg.Done() - }() - _, err := client.SendRequest("yes", false, nil) - if err != nil { - t.Fatalf("SendRequest: %v", err) - } - ok, err := client.SendRequest("yes", true, nil) - if err != nil { - t.Fatalf("SendRequest: %v", err) - } - - if !ok { - t.Errorf("SendRequest(yes): %v", ok) - - } - - ok, err = client.SendRequest("no", true, nil) - if err != nil { - t.Fatalf("SendRequest: %v", err) - } - if ok { - t.Errorf("SendRequest(no): %v", ok) - - } - - client.Close() - wg.Wait() - - if received != 3 { - t.Errorf("got %d requests, want %d", received, 3) - } -} - -func TestMuxGlobalRequest(t *testing.T) { - clientMux, serverMux := muxPair() - defer serverMux.Close() - defer clientMux.Close() - - var seen bool - go func() { - for r := range serverMux.incomingRequests { - seen = seen || r.Type == "peek" - if r.WantReply { - err := r.Reply(r.Type == "yes", - append([]byte(r.Type), r.Payload...)) - if err != nil { - t.Errorf("AckRequest: %v", err) - } - } - } - }() - - _, _, err := clientMux.SendRequest("peek", false, nil) - if err != nil { - t.Errorf("SendRequest: %v", err) - } - - ok, data, err := clientMux.SendRequest("yes", true, []byte("a")) - if !ok || string(data) != "yesa" || err != nil { - t.Errorf("SendRequest(\"yes\", true, \"a\"): %v %v %v", - ok, data, err) - } - if ok, data, err := clientMux.SendRequest("yes", true, []byte("a")); !ok || string(data) != "yesa" || err != nil { - t.Errorf("SendRequest(\"yes\", true, \"a\"): %v %v %v", - ok, data, err) - } - - if ok, data, err := clientMux.SendRequest("no", true, []byte("a")); ok || string(data) != "noa" || err != nil { - t.Errorf("SendRequest(\"no\", true, \"a\"): %v %v %v", - ok, data, err) - } - - clientMux.Disconnect(0, "") - if !seen { - t.Errorf("never saw 'peek' request") - } -} - -func TestMuxGlobalRequestUnblock(t *testing.T) { - clientMux, serverMux := muxPair() - defer serverMux.Close() - defer clientMux.Close() - - result := make(chan error, 1) - go func() { - _, _, err := clientMux.SendRequest("hello", true, nil) - result <- err - }() - - <-serverMux.incomingRequests - serverMux.conn.Close() - err := <-result - - if err != io.EOF { - t.Errorf("want EOF, got %v", io.EOF) - } -} - -func TestMuxChannelRequestUnblock(t *testing.T) { - a, b, connB := channelPair(t) - defer a.Close() - defer b.Close() - defer connB.Close() - - result := make(chan error, 1) - go func() { - _, err := a.SendRequest("hello", true, nil) - result <- err - }() - - <-b.incomingRequests - connB.conn.Close() - err := <-result - - if err != io.EOF { - t.Errorf("want EOF, got %v", err) - } -} - -func TestMuxDisconnect(t *testing.T) { - a, b := muxPair() - defer a.Close() - defer b.Close() - - go func() { - for r := range b.incomingRequests { - r.Reply(true, nil) - } - }() - - a.Disconnect(42, "whatever") - ok, _, err := a.SendRequest("hello", true, nil) - if ok || err == nil { - t.Errorf("got reply after disconnecting") - } - err = b.Wait() - if d, ok := err.(*disconnectMsg); !ok || d.Reason != 42 { - t.Errorf("got %#v, want disconnectMsg{Reason:42}", err) - } -} - -func TestMuxCloseChannel(t *testing.T) { - r, w, mux := channelPair(t) - defer mux.Close() - defer r.Close() - defer w.Close() - - result := make(chan error, 1) - go func() { - var b [1024]byte - _, err := r.Read(b[:]) - result <- err - }() - if err := w.Close(); err != nil { - t.Errorf("w.Close: %v", err) - } - - if _, err := w.Write([]byte("hello")); err != io.EOF { - t.Errorf("got err %v, want io.EOF after Close", err) - } - - if err := <-result; err != io.EOF { - t.Errorf("got %v (%T), want io.EOF", err, err) - } -} - -func TestMuxCloseWriteChannel(t *testing.T) { - r, w, mux := channelPair(t) - defer mux.Close() - - result := make(chan error, 1) - go func() { - var b [1024]byte - _, err := r.Read(b[:]) - result <- err - }() - if err := w.CloseWrite(); err != nil { - t.Errorf("w.CloseWrite: %v", err) - } - - if _, err := w.Write([]byte("hello")); err != io.EOF { - t.Errorf("got err %v, want io.EOF after CloseWrite", err) - } - - if err := <-result; err != io.EOF { - t.Errorf("got %v (%T), want io.EOF", err, err) - } -} - -func TestMuxInvalidRecord(t *testing.T) { - a, b := muxPair() - defer a.Close() - defer b.Close() - - packet := make([]byte, 1+4+4+1) - packet[0] = msgChannelData - marshalUint32(packet[1:], 29348723 /* invalid channel id */) - marshalUint32(packet[5:], 1) - packet[9] = 42 - - a.conn.writePacket(packet) - go a.SendRequest("hello", false, nil) - // 'a' wrote an invalid packet, so 'b' has exited. - req, ok := <-b.incomingRequests - if ok { - t.Errorf("got request %#v after receiving invalid packet", req) - } -} - -func TestZeroWindowAdjust(t *testing.T) { - a, b, mux := channelPair(t) - defer a.Close() - defer b.Close() - defer mux.Close() - - go func() { - io.WriteString(a, "hello") - // bogus adjust. - a.sendMessage(windowAdjustMsg{}) - io.WriteString(a, "world") - a.Close() - }() - - want := "helloworld" - c, _ := ioutil.ReadAll(b) - if string(c) != want { - t.Errorf("got %q want %q", c, want) - } -} - -func TestMuxMaxPacketSize(t *testing.T) { - a, b, mux := channelPair(t) - defer a.Close() - defer b.Close() - defer mux.Close() - - large := make([]byte, a.maxRemotePayload+1) - packet := make([]byte, 1+4+4+1+len(large)) - packet[0] = msgChannelData - marshalUint32(packet[1:], a.remoteId) - marshalUint32(packet[5:], uint32(len(large))) - packet[9] = 42 - - if err := a.mux.conn.writePacket(packet); err != nil { - t.Errorf("could not send packet") - } - - go a.SendRequest("hello", false, nil) - - _, ok := <-b.incomingRequests - if ok { - t.Errorf("connection still alive after receiving large packet.") - } -} - -// Don't ship code with debug=true. -func TestDebug(t *testing.T) { - if debugMux { - t.Error("mux debug switched on") - } - if debugHandshake { - t.Error("handshake debug switched on") - } -} diff --git a/modules/crypto/ssh/server.go b/modules/crypto/ssh/server.go deleted file mode 100755 index baedf5bb..00000000 --- a/modules/crypto/ssh/server.go +++ /dev/null @@ -1,493 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bytes" - "errors" - "fmt" - "io" - "net" -) - -// The Permissions type holds fine-grained permissions that are -// specific to a user or a specific authentication method for a -// user. Permissions, except for "source-address", must be enforced in -// the server application layer, after successful authentication. The -// Permissions are passed on in ServerConn so a server implementation -// can honor them. -type Permissions struct { - // Critical options restrict default permissions. Common - // restrictions are "source-address" and "force-command". If - // the server cannot enforce the restriction, or does not - // recognize it, the user should not authenticate. - CriticalOptions map[string]string - - // Extensions are extra functionality that the server may - // offer on authenticated connections. Common extensions are - // "permit-agent-forwarding", "permit-X11-forwarding". Lack of - // support for an extension does not preclude authenticating a - // user. - Extensions map[string]string -} - -// ServerConfig holds server specific configuration data. -type ServerConfig struct { - // Config contains configuration shared between client and server. - Config - - hostKeys []Signer - - // NoClientAuth is true if clients are allowed to connect without - // authenticating. - NoClientAuth bool - - // PasswordCallback, if non-nil, is called when a user - // attempts to authenticate using a password. - PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error) - - // PublicKeyCallback, if non-nil, is called when a client attempts public - // key authentication. It must return true if the given public key is - // valid for the given user. For example, see CertChecker.Authenticate. - PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error) - - // KeyboardInteractiveCallback, if non-nil, is called when - // keyboard-interactive authentication is selected (RFC - // 4256). The client object's Challenge function should be - // used to query the user. The callback may offer multiple - // Challenge rounds. To avoid information leaks, the client - // should be presented a challenge even if the user is - // unknown. - KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error) - - // AuthLogCallback, if non-nil, is called to log all authentication - // attempts. - AuthLogCallback func(conn ConnMetadata, method string, err error) - - // ServerVersion is the version identification string to - // announce in the public handshake. - // If empty, a reasonable default is used. - ServerVersion string -} - -// AddHostKey adds a private key as a host key. If an existing host -// key exists with the same algorithm, it is overwritten. Each server -// config must have at least one host key. -func (s *ServerConfig) AddHostKey(key Signer) { - for i, k := range s.hostKeys { - if k.PublicKey().Type() == key.PublicKey().Type() { - s.hostKeys[i] = key - return - } - } - - s.hostKeys = append(s.hostKeys, key) -} - -// cachedPubKey contains the results of querying whether a public key is -// acceptable for a user. -type cachedPubKey struct { - user string - pubKeyData []byte - result error - perms *Permissions -} - -const maxCachedPubKeys = 16 - -// pubKeyCache caches tests for public keys. Since SSH clients -// will query whether a public key is acceptable before attempting to -// authenticate with it, we end up with duplicate queries for public -// key validity. The cache only applies to a single ServerConn. -type pubKeyCache struct { - keys []cachedPubKey -} - -// get returns the result for a given user/algo/key tuple. -func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) { - for _, k := range c.keys { - if k.user == user && bytes.Equal(k.pubKeyData, pubKeyData) { - return k, true - } - } - return cachedPubKey{}, false -} - -// add adds the given tuple to the cache. -func (c *pubKeyCache) add(candidate cachedPubKey) { - if len(c.keys) < maxCachedPubKeys { - c.keys = append(c.keys, candidate) - } -} - -// ServerConn is an authenticated SSH connection, as seen from the -// server -type ServerConn struct { - Conn - - // If the succeeding authentication callback returned a - // non-nil Permissions pointer, it is stored here. - Permissions *Permissions -} - -// NewServerConn starts a new SSH server with c as the underlying -// transport. It starts with a handshake and, if the handshake is -// unsuccessful, it closes the connection and returns an error. The -// Request and NewChannel channels must be serviced, or the connection -// will hang. -func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) { - fullConf := *config - fullConf.SetDefaults() - s := &connection{ - sshConn: sshConn{conn: c}, - } - perms, err := s.serverHandshake(&fullConf) - if err != nil { - c.Close() - return nil, nil, nil, err - } - return &ServerConn{s, perms}, s.mux.incomingChannels, s.mux.incomingRequests, nil -} - -// signAndMarshal signs the data with the appropriate algorithm, -// and serializes the result in SSH wire format. -func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) { - sig, err := k.Sign(rand, data) - if err != nil { - return nil, err - } - - return Marshal(sig), nil -} - -// handshake performs key exchange and user authentication. -func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) { - if len(config.hostKeys) == 0 { - return nil, errors.New("ssh: server has no host keys") - } - - if !config.NoClientAuth && config.PasswordCallback == nil && config.PublicKeyCallback == nil && config.KeyboardInteractiveCallback == nil { - return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false") - } - - if config.ServerVersion != "" { - s.serverVersion = []byte(config.ServerVersion) - } else { - s.serverVersion = []byte(packageVersion) - } - var err error - s.clientVersion, err = exchangeVersions(s.sshConn.conn, s.serverVersion) - if err != nil { - return nil, err - } - - tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */) - s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config) - - if err := s.transport.requestKeyChange(); err != nil { - return nil, err - } - - if packet, err := s.transport.readPacket(); err != nil { - return nil, err - } else if packet[0] != msgNewKeys { - return nil, unexpectedMessageError(msgNewKeys, packet[0]) - } - - // We just did the key change, so the session ID is established. - s.sessionID = s.transport.getSessionID() - - var packet []byte - if packet, err = s.transport.readPacket(); err != nil { - return nil, err - } - - var serviceRequest serviceRequestMsg - if err = Unmarshal(packet, &serviceRequest); err != nil { - return nil, err - } - if serviceRequest.Service != serviceUserAuth { - return nil, errors.New("ssh: requested service '" + serviceRequest.Service + "' before authenticating") - } - serviceAccept := serviceAcceptMsg{ - Service: serviceUserAuth, - } - if err := s.transport.writePacket(Marshal(&serviceAccept)); err != nil { - return nil, err - } - - perms, err := s.serverAuthenticate(config) - if err != nil { - return nil, err - } - s.mux = newMux(s.transport) - return perms, err -} - -func isAcceptableAlgo(algo string) bool { - switch algo { - case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, - CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01: - return true - } - return false -} - -func checkSourceAddress(addr net.Addr, sourceAddr string) error { - if addr == nil { - return errors.New("ssh: no address known for client, but source-address match required") - } - - tcpAddr, ok := addr.(*net.TCPAddr) - if !ok { - return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr) - } - - if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil { - if bytes.Equal(allowedIP, tcpAddr.IP) { - return nil - } - } else { - _, ipNet, err := net.ParseCIDR(sourceAddr) - if err != nil { - return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err) - } - - if ipNet.Contains(tcpAddr.IP) { - return nil - } - } - - return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr) -} - -func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) { - var err error - var cache pubKeyCache - var perms *Permissions - -userAuthLoop: - for { - var userAuthReq userAuthRequestMsg - if packet, err := s.transport.readPacket(); err != nil { - return nil, err - } else if err = Unmarshal(packet, &userAuthReq); err != nil { - return nil, err - } - - if userAuthReq.Service != serviceSSH { - return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service) - } - - s.user = userAuthReq.User - perms = nil - authErr := errors.New("no auth passed yet") - - switch userAuthReq.Method { - case "none": - if config.NoClientAuth { - s.user = "" - authErr = nil - } - case "password": - if config.PasswordCallback == nil { - authErr = errors.New("ssh: password auth not configured") - break - } - payload := userAuthReq.Payload - if len(payload) < 1 || payload[0] != 0 { - return nil, parseError(msgUserAuthRequest) - } - payload = payload[1:] - password, payload, ok := parseString(payload) - if !ok || len(payload) > 0 { - return nil, parseError(msgUserAuthRequest) - } - - perms, authErr = config.PasswordCallback(s, password) - case "keyboard-interactive": - if config.KeyboardInteractiveCallback == nil { - authErr = errors.New("ssh: keyboard-interactive auth not configubred") - break - } - - prompter := &sshClientKeyboardInteractive{s} - perms, authErr = config.KeyboardInteractiveCallback(s, prompter.Challenge) - case "publickey": - if config.PublicKeyCallback == nil { - authErr = errors.New("ssh: publickey auth not configured") - break - } - payload := userAuthReq.Payload - if len(payload) < 1 { - return nil, parseError(msgUserAuthRequest) - } - isQuery := payload[0] == 0 - payload = payload[1:] - algoBytes, payload, ok := parseString(payload) - if !ok { - return nil, parseError(msgUserAuthRequest) - } - algo := string(algoBytes) - if !isAcceptableAlgo(algo) { - authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo) - break - } - - pubKeyData, payload, ok := parseString(payload) - if !ok { - return nil, parseError(msgUserAuthRequest) - } - - pubKey, err := ParsePublicKey(pubKeyData) - if err != nil { - return nil, err - } - - candidate, ok := cache.get(s.user, pubKeyData) - if !ok { - candidate.user = s.user - candidate.pubKeyData = pubKeyData - candidate.perms, candidate.result = config.PublicKeyCallback(s, pubKey) - if candidate.result == nil && candidate.perms != nil && candidate.perms.CriticalOptions != nil && candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" { - candidate.result = checkSourceAddress( - s.RemoteAddr(), - candidate.perms.CriticalOptions[sourceAddressCriticalOption]) - } - cache.add(candidate) - } - - if isQuery { - // The client can query if the given public key - // would be okay. - if len(payload) > 0 { - return nil, parseError(msgUserAuthRequest) - } - - if candidate.result == nil { - okMsg := userAuthPubKeyOkMsg{ - Algo: algo, - PubKey: pubKeyData, - } - if err = s.transport.writePacket(Marshal(&okMsg)); err != nil { - return nil, err - } - continue userAuthLoop - } - authErr = candidate.result - } else { - sig, payload, ok := parseSignature(payload) - if !ok || len(payload) > 0 { - return nil, parseError(msgUserAuthRequest) - } - // Ensure the public key algo and signature algo - // are supported. Compare the private key - // algorithm name that corresponds to algo with - // sig.Format. This is usually the same, but - // for certs, the names differ. - if !isAcceptableAlgo(sig.Format) { - break - } - signedData := buildDataSignedForAuth(s.transport.getSessionID(), userAuthReq, algoBytes, pubKeyData) - - if err := pubKey.Verify(signedData, sig); err != nil { - return nil, err - } - - authErr = candidate.result - perms = candidate.perms - } - default: - authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method) - } - - if config.AuthLogCallback != nil { - config.AuthLogCallback(s, userAuthReq.Method, authErr) - } - - if authErr == nil { - break userAuthLoop - } - - var failureMsg userAuthFailureMsg - if config.PasswordCallback != nil { - failureMsg.Methods = append(failureMsg.Methods, "password") - } - if config.PublicKeyCallback != nil { - failureMsg.Methods = append(failureMsg.Methods, "publickey") - } - if config.KeyboardInteractiveCallback != nil { - failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive") - } - - if len(failureMsg.Methods) == 0 { - return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false") - } - - if err = s.transport.writePacket(Marshal(&failureMsg)); err != nil { - return nil, err - } - } - - if err = s.transport.writePacket([]byte{msgUserAuthSuccess}); err != nil { - return nil, err - } - return perms, nil -} - -// sshClientKeyboardInteractive implements a ClientKeyboardInteractive by -// asking the client on the other side of a ServerConn. -type sshClientKeyboardInteractive struct { - *connection -} - -func (c *sshClientKeyboardInteractive) Challenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) { - if len(questions) != len(echos) { - return nil, errors.New("ssh: echos and questions must have equal length") - } - - var prompts []byte - for i := range questions { - prompts = appendString(prompts, questions[i]) - prompts = appendBool(prompts, echos[i]) - } - - if err := c.transport.writePacket(Marshal(&userAuthInfoRequestMsg{ - Instruction: instruction, - NumPrompts: uint32(len(questions)), - Prompts: prompts, - })); err != nil { - return nil, err - } - - packet, err := c.transport.readPacket() - if err != nil { - return nil, err - } - if packet[0] != msgUserAuthInfoResponse { - return nil, unexpectedMessageError(msgUserAuthInfoResponse, packet[0]) - } - packet = packet[1:] - - n, packet, ok := parseUint32(packet) - if !ok || int(n) != len(questions) { - return nil, parseError(msgUserAuthInfoResponse) - } - - for i := uint32(0); i < n; i++ { - ans, rest, ok := parseString(packet) - if !ok { - return nil, parseError(msgUserAuthInfoResponse) - } - - answers = append(answers, string(ans)) - packet = rest - } - if len(packet) != 0 { - return nil, errors.New("ssh: junk at end of message") - } - - return answers, nil -} diff --git a/modules/crypto/ssh/session.go b/modules/crypto/ssh/session.go deleted file mode 100755 index 3b42b508..00000000 --- a/modules/crypto/ssh/session.go +++ /dev/null @@ -1,605 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -// Session implements an interactive session described in -// "RFC 4254, section 6". - -import ( - "bytes" - "errors" - "fmt" - "io" - "io/ioutil" - "sync" -) - -type Signal string - -// POSIX signals as listed in RFC 4254 Section 6.10. -const ( - SIGABRT Signal = "ABRT" - SIGALRM Signal = "ALRM" - SIGFPE Signal = "FPE" - SIGHUP Signal = "HUP" - SIGILL Signal = "ILL" - SIGINT Signal = "INT" - SIGKILL Signal = "KILL" - SIGPIPE Signal = "PIPE" - SIGQUIT Signal = "QUIT" - SIGSEGV Signal = "SEGV" - SIGTERM Signal = "TERM" - SIGUSR1 Signal = "USR1" - SIGUSR2 Signal = "USR2" -) - -var signals = map[Signal]int{ - SIGABRT: 6, - SIGALRM: 14, - SIGFPE: 8, - SIGHUP: 1, - SIGILL: 4, - SIGINT: 2, - SIGKILL: 9, - SIGPIPE: 13, - SIGQUIT: 3, - SIGSEGV: 11, - SIGTERM: 15, -} - -type TerminalModes map[uint8]uint32 - -// POSIX terminal mode flags as listed in RFC 4254 Section 8. -const ( - tty_OP_END = 0 - VINTR = 1 - VQUIT = 2 - VERASE = 3 - VKILL = 4 - VEOF = 5 - VEOL = 6 - VEOL2 = 7 - VSTART = 8 - VSTOP = 9 - VSUSP = 10 - VDSUSP = 11 - VREPRINT = 12 - VWERASE = 13 - VLNEXT = 14 - VFLUSH = 15 - VSWTCH = 16 - VSTATUS = 17 - VDISCARD = 18 - IGNPAR = 30 - PARMRK = 31 - INPCK = 32 - ISTRIP = 33 - INLCR = 34 - IGNCR = 35 - ICRNL = 36 - IUCLC = 37 - IXON = 38 - IXANY = 39 - IXOFF = 40 - IMAXBEL = 41 - ISIG = 50 - ICANON = 51 - XCASE = 52 - ECHO = 53 - ECHOE = 54 - ECHOK = 55 - ECHONL = 56 - NOFLSH = 57 - TOSTOP = 58 - IEXTEN = 59 - ECHOCTL = 60 - ECHOKE = 61 - PENDIN = 62 - OPOST = 70 - OLCUC = 71 - ONLCR = 72 - OCRNL = 73 - ONOCR = 74 - ONLRET = 75 - CS7 = 90 - CS8 = 91 - PARENB = 92 - PARODD = 93 - TTY_OP_ISPEED = 128 - TTY_OP_OSPEED = 129 -) - -// A Session represents a connection to a remote command or shell. -type Session struct { - // Stdin specifies the remote process's standard input. - // If Stdin is nil, the remote process reads from an empty - // bytes.Buffer. - Stdin io.Reader - - // Stdout and Stderr specify the remote process's standard - // output and error. - // - // If either is nil, Run connects the corresponding file - // descriptor to an instance of ioutil.Discard. There is a - // fixed amount of buffering that is shared for the two streams. - // If either blocks it may eventually cause the remote - // command to block. - Stdout io.Writer - Stderr io.Writer - - ch Channel // the channel backing this session - started bool // true once Start, Run or Shell is invoked. - copyFuncs []func() error - errors chan error // one send per copyFunc - - // true if pipe method is active - stdinpipe, stdoutpipe, stderrpipe bool - - // stdinPipeWriter is non-nil if StdinPipe has not been called - // and Stdin was specified by the user; it is the write end of - // a pipe connecting Session.Stdin to the stdin channel. - stdinPipeWriter io.WriteCloser - - exitStatus chan error -} - -// SendRequest sends an out-of-band channel request on the SSH channel -// underlying the session. -func (s *Session) SendRequest(name string, wantReply bool, payload []byte) (bool, error) { - return s.ch.SendRequest(name, wantReply, payload) -} - -func (s *Session) Close() error { - return s.ch.Close() -} - -// RFC 4254 Section 6.4. -type setenvRequest struct { - Name string - Value string -} - -// Setenv sets an environment variable that will be applied to any -// command executed by Shell or Run. -func (s *Session) Setenv(name, value string) error { - msg := setenvRequest{ - Name: name, - Value: value, - } - ok, err := s.ch.SendRequest("env", true, Marshal(&msg)) - if err == nil && !ok { - err = errors.New("ssh: setenv failed") - } - return err -} - -// RFC 4254 Section 6.2. -type ptyRequestMsg struct { - Term string - Columns uint32 - Rows uint32 - Width uint32 - Height uint32 - Modelist string -} - -// RequestPty requests the association of a pty with the session on the remote host. -func (s *Session) RequestPty(term string, h, w int, termmodes TerminalModes) error { - var tm []byte - for k, v := range termmodes { - kv := struct { - Key byte - Val uint32 - }{k, v} - - tm = append(tm, Marshal(&kv)...) - } - tm = append(tm, tty_OP_END) - req := ptyRequestMsg{ - Term: term, - Columns: uint32(w), - Rows: uint32(h), - Width: uint32(w * 8), - Height: uint32(h * 8), - Modelist: string(tm), - } - ok, err := s.ch.SendRequest("pty-req", true, Marshal(&req)) - if err == nil && !ok { - err = errors.New("ssh: pty-req failed") - } - return err -} - -// RFC 4254 Section 6.5. -type subsystemRequestMsg struct { - Subsystem string -} - -// RequestSubsystem requests the association of a subsystem with the session on the remote host. -// A subsystem is a predefined command that runs in the background when the ssh session is initiated -func (s *Session) RequestSubsystem(subsystem string) error { - msg := subsystemRequestMsg{ - Subsystem: subsystem, - } - ok, err := s.ch.SendRequest("subsystem", true, Marshal(&msg)) - if err == nil && !ok { - err = errors.New("ssh: subsystem request failed") - } - return err -} - -// RFC 4254 Section 6.9. -type signalMsg struct { - Signal string -} - -// Signal sends the given signal to the remote process. -// sig is one of the SIG* constants. -func (s *Session) Signal(sig Signal) error { - msg := signalMsg{ - Signal: string(sig), - } - - _, err := s.ch.SendRequest("signal", false, Marshal(&msg)) - return err -} - -// RFC 4254 Section 6.5. -type execMsg struct { - Command string -} - -// Start runs cmd on the remote host. Typically, the remote -// server passes cmd to the shell for interpretation. -// A Session only accepts one call to Run, Start or Shell. -func (s *Session) Start(cmd string) error { - if s.started { - return errors.New("ssh: session already started") - } - req := execMsg{ - Command: cmd, - } - - ok, err := s.ch.SendRequest("exec", true, Marshal(&req)) - if err == nil && !ok { - err = fmt.Errorf("ssh: command %v failed", cmd) - } - if err != nil { - return err - } - return s.start() -} - -// Run runs cmd on the remote host. Typically, the remote -// server passes cmd to the shell for interpretation. -// A Session only accepts one call to Run, Start, Shell, Output, -// or CombinedOutput. -// -// The returned error is nil if the command runs, has no problems -// copying stdin, stdout, and stderr, and exits with a zero exit -// status. -// -// If the command fails to run or doesn't complete successfully, the -// error is of type *ExitError. Other error types may be -// returned for I/O problems. -func (s *Session) Run(cmd string) error { - err := s.Start(cmd) - if err != nil { - return err - } - return s.Wait() -} - -// Output runs cmd on the remote host and returns its standard output. -func (s *Session) Output(cmd string) ([]byte, error) { - if s.Stdout != nil { - return nil, errors.New("ssh: Stdout already set") - } - var b bytes.Buffer - s.Stdout = &b - err := s.Run(cmd) - return b.Bytes(), err -} - -type singleWriter struct { - b bytes.Buffer - mu sync.Mutex -} - -func (w *singleWriter) Write(p []byte) (int, error) { - w.mu.Lock() - defer w.mu.Unlock() - return w.b.Write(p) -} - -// CombinedOutput runs cmd on the remote host and returns its combined -// standard output and standard error. -func (s *Session) CombinedOutput(cmd string) ([]byte, error) { - if s.Stdout != nil { - return nil, errors.New("ssh: Stdout already set") - } - if s.Stderr != nil { - return nil, errors.New("ssh: Stderr already set") - } - var b singleWriter - s.Stdout = &b - s.Stderr = &b - err := s.Run(cmd) - return b.b.Bytes(), err -} - -// Shell starts a login shell on the remote host. A Session only -// accepts one call to Run, Start, Shell, Output, or CombinedOutput. -func (s *Session) Shell() error { - if s.started { - return errors.New("ssh: session already started") - } - - ok, err := s.ch.SendRequest("shell", true, nil) - if err == nil && !ok { - return fmt.Errorf("ssh: cound not start shell") - } - if err != nil { - return err - } - return s.start() -} - -func (s *Session) start() error { - s.started = true - - type F func(*Session) - for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} { - setupFd(s) - } - - s.errors = make(chan error, len(s.copyFuncs)) - for _, fn := range s.copyFuncs { - go func(fn func() error) { - s.errors <- fn() - }(fn) - } - return nil -} - -// Wait waits for the remote command to exit. -// -// The returned error is nil if the command runs, has no problems -// copying stdin, stdout, and stderr, and exits with a zero exit -// status. -// -// If the command fails to run or doesn't complete successfully, the -// error is of type *ExitError. Other error types may be -// returned for I/O problems. -func (s *Session) Wait() error { - if !s.started { - return errors.New("ssh: session not started") - } - waitErr := <-s.exitStatus - - if s.stdinPipeWriter != nil { - s.stdinPipeWriter.Close() - } - var copyError error - for _ = range s.copyFuncs { - if err := <-s.errors; err != nil && copyError == nil { - copyError = err - } - } - if waitErr != nil { - return waitErr - } - return copyError -} - -func (s *Session) wait(reqs <-chan *Request) error { - wm := Waitmsg{status: -1} - // Wait for msg channel to be closed before returning. - for msg := range reqs { - switch msg.Type { - case "exit-status": - d := msg.Payload - wm.status = int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3]) - case "exit-signal": - var sigval struct { - Signal string - CoreDumped bool - Error string - Lang string - } - if err := Unmarshal(msg.Payload, &sigval); err != nil { - return err - } - - // Must sanitize strings? - wm.signal = sigval.Signal - wm.msg = sigval.Error - wm.lang = sigval.Lang - default: - // This handles keepalives and matches - // OpenSSH's behaviour. - if msg.WantReply { - msg.Reply(false, nil) - } - } - } - if wm.status == 0 { - return nil - } - if wm.status == -1 { - // exit-status was never sent from server - if wm.signal == "" { - return errors.New("wait: remote command exited without exit status or exit signal") - } - wm.status = 128 - if _, ok := signals[Signal(wm.signal)]; ok { - wm.status += signals[Signal(wm.signal)] - } - } - return &ExitError{wm} -} - -func (s *Session) stdin() { - if s.stdinpipe { - return - } - var stdin io.Reader - if s.Stdin == nil { - stdin = new(bytes.Buffer) - } else { - r, w := io.Pipe() - go func() { - _, err := io.Copy(w, s.Stdin) - w.CloseWithError(err) - }() - stdin, s.stdinPipeWriter = r, w - } - s.copyFuncs = append(s.copyFuncs, func() error { - _, err := io.Copy(s.ch, stdin) - if err1 := s.ch.CloseWrite(); err == nil && err1 != io.EOF { - err = err1 - } - return err - }) -} - -func (s *Session) stdout() { - if s.stdoutpipe { - return - } - if s.Stdout == nil { - s.Stdout = ioutil.Discard - } - s.copyFuncs = append(s.copyFuncs, func() error { - _, err := io.Copy(s.Stdout, s.ch) - return err - }) -} - -func (s *Session) stderr() { - if s.stderrpipe { - return - } - if s.Stderr == nil { - s.Stderr = ioutil.Discard - } - s.copyFuncs = append(s.copyFuncs, func() error { - _, err := io.Copy(s.Stderr, s.ch.Stderr()) - return err - }) -} - -// sessionStdin reroutes Close to CloseWrite. -type sessionStdin struct { - io.Writer - ch Channel -} - -func (s *sessionStdin) Close() error { - return s.ch.CloseWrite() -} - -// StdinPipe returns a pipe that will be connected to the -// remote command's standard input when the command starts. -func (s *Session) StdinPipe() (io.WriteCloser, error) { - if s.Stdin != nil { - return nil, errors.New("ssh: Stdin already set") - } - if s.started { - return nil, errors.New("ssh: StdinPipe after process started") - } - s.stdinpipe = true - return &sessionStdin{s.ch, s.ch}, nil -} - -// StdoutPipe returns a pipe that will be connected to the -// remote command's standard output when the command starts. -// There is a fixed amount of buffering that is shared between -// stdout and stderr streams. If the StdoutPipe reader is -// not serviced fast enough it may eventually cause the -// remote command to block. -func (s *Session) StdoutPipe() (io.Reader, error) { - if s.Stdout != nil { - return nil, errors.New("ssh: Stdout already set") - } - if s.started { - return nil, errors.New("ssh: StdoutPipe after process started") - } - s.stdoutpipe = true - return s.ch, nil -} - -// StderrPipe returns a pipe that will be connected to the -// remote command's standard error when the command starts. -// There is a fixed amount of buffering that is shared between -// stdout and stderr streams. If the StderrPipe reader is -// not serviced fast enough it may eventually cause the -// remote command to block. -func (s *Session) StderrPipe() (io.Reader, error) { - if s.Stderr != nil { - return nil, errors.New("ssh: Stderr already set") - } - if s.started { - return nil, errors.New("ssh: StderrPipe after process started") - } - s.stderrpipe = true - return s.ch.Stderr(), nil -} - -// newSession returns a new interactive session on the remote host. -func newSession(ch Channel, reqs <-chan *Request) (*Session, error) { - s := &Session{ - ch: ch, - } - s.exitStatus = make(chan error, 1) - go func() { - s.exitStatus <- s.wait(reqs) - }() - - return s, nil -} - -// An ExitError reports unsuccessful completion of a remote command. -type ExitError struct { - Waitmsg -} - -func (e *ExitError) Error() string { - return e.Waitmsg.String() -} - -// Waitmsg stores the information about an exited remote command -// as reported by Wait. -type Waitmsg struct { - status int - signal string - msg string - lang string -} - -// ExitStatus returns the exit status of the remote command. -func (w Waitmsg) ExitStatus() int { - return w.status -} - -// Signal returns the exit signal of the remote command if -// it was terminated violently. -func (w Waitmsg) Signal() string { - return w.signal -} - -// Msg returns the exit message given by the remote command -func (w Waitmsg) Msg() string { - return w.msg -} - -// Lang returns the language tag. See RFC 3066 -func (w Waitmsg) Lang() string { - return w.lang -} - -func (w Waitmsg) String() string { - return fmt.Sprintf("Process exited with: %v. Reason was: %v (%v)", w.status, w.msg, w.signal) -} diff --git a/modules/crypto/ssh/session_test.go b/modules/crypto/ssh/session_test.go deleted file mode 100755 index 628845e4..00000000 --- a/modules/crypto/ssh/session_test.go +++ /dev/null @@ -1,774 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -// Session tests. - -import ( - "bytes" - crypto_rand "crypto/rand" - "errors" - "io" - "io/ioutil" - "math/rand" - "net" - "testing" - - "github.com/gogits/gogs/modules/crypto/ssh/terminal" -) - -type serverType func(Channel, <-chan *Request, *testing.T) - -// dial constructs a new test server and returns a *ClientConn. -func dial(handler serverType, t *testing.T) *Client { - c1, c2, err := netPipe() - if err != nil { - t.Fatalf("netPipe: %v", err) - } - - go func() { - defer c1.Close() - conf := ServerConfig{ - NoClientAuth: true, - } - conf.AddHostKey(testSigners["rsa"]) - - _, chans, reqs, err := NewServerConn(c1, &conf) - if err != nil { - t.Fatalf("Unable to handshake: %v", err) - } - go DiscardRequests(reqs) - - for newCh := range chans { - if newCh.ChannelType() != "session" { - newCh.Reject(UnknownChannelType, "unknown channel type") - continue - } - - ch, inReqs, err := newCh.Accept() - if err != nil { - t.Errorf("Accept: %v", err) - continue - } - go func() { - handler(ch, inReqs, t) - }() - } - }() - - config := &ClientConfig{ - User: "testuser", - } - - conn, chans, reqs, err := NewClientConn(c2, "", config) - if err != nil { - t.Fatalf("unable to dial remote side: %v", err) - } - - return NewClient(conn, chans, reqs) -} - -// Test a simple string is returned to session.Stdout. -func TestSessionShell(t *testing.T) { - conn := dial(shellHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %v", err) - } - defer session.Close() - stdout := new(bytes.Buffer) - session.Stdout = stdout - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %s", err) - } - if err := session.Wait(); err != nil { - t.Fatalf("Remote command did not exit cleanly: %v", err) - } - actual := stdout.String() - if actual != "golang" { - t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual) - } -} - -// TODO(dfc) add support for Std{in,err}Pipe when the Server supports it. - -// Test a simple string is returned via StdoutPipe. -func TestSessionStdoutPipe(t *testing.T) { - conn := dial(shellHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %v", err) - } - defer session.Close() - stdout, err := session.StdoutPipe() - if err != nil { - t.Fatalf("Unable to request StdoutPipe(): %v", err) - } - var buf bytes.Buffer - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %v", err) - } - done := make(chan bool, 1) - go func() { - if _, err := io.Copy(&buf, stdout); err != nil { - t.Errorf("Copy of stdout failed: %v", err) - } - done <- true - }() - if err := session.Wait(); err != nil { - t.Fatalf("Remote command did not exit cleanly: %v", err) - } - <-done - actual := buf.String() - if actual != "golang" { - t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual) - } -} - -// Test that a simple string is returned via the Output helper, -// and that stderr is discarded. -func TestSessionOutput(t *testing.T) { - conn := dial(fixedOutputHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %v", err) - } - defer session.Close() - - buf, err := session.Output("") // cmd is ignored by fixedOutputHandler - if err != nil { - t.Error("Remote command did not exit cleanly:", err) - } - w := "this-is-stdout." - g := string(buf) - if g != w { - t.Error("Remote command did not return expected string:") - t.Logf("want %q", w) - t.Logf("got %q", g) - } -} - -// Test that both stdout and stderr are returned -// via the CombinedOutput helper. -func TestSessionCombinedOutput(t *testing.T) { - conn := dial(fixedOutputHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %v", err) - } - defer session.Close() - - buf, err := session.CombinedOutput("") // cmd is ignored by fixedOutputHandler - if err != nil { - t.Error("Remote command did not exit cleanly:", err) - } - const stdout = "this-is-stdout." - const stderr = "this-is-stderr." - g := string(buf) - if g != stdout+stderr && g != stderr+stdout { - t.Error("Remote command did not return expected string:") - t.Logf("want %q, or %q", stdout+stderr, stderr+stdout) - t.Logf("got %q", g) - } -} - -// Test non-0 exit status is returned correctly. -func TestExitStatusNonZero(t *testing.T) { - conn := dial(exitStatusNonZeroHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %v", err) - } - defer session.Close() - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %v", err) - } - err = session.Wait() - if err == nil { - t.Fatalf("expected command to fail but it didn't") - } - e, ok := err.(*ExitError) - if !ok { - t.Fatalf("expected *ExitError but got %T", err) - } - if e.ExitStatus() != 15 { - t.Fatalf("expected command to exit with 15 but got %v", e.ExitStatus()) - } -} - -// Test 0 exit status is returned correctly. -func TestExitStatusZero(t *testing.T) { - conn := dial(exitStatusZeroHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %v", err) - } - defer session.Close() - - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %v", err) - } - err = session.Wait() - if err != nil { - t.Fatalf("expected nil but got %v", err) - } -} - -// Test exit signal and status are both returned correctly. -func TestExitSignalAndStatus(t *testing.T) { - conn := dial(exitSignalAndStatusHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %v", err) - } - defer session.Close() - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %v", err) - } - err = session.Wait() - if err == nil { - t.Fatalf("expected command to fail but it didn't") - } - e, ok := err.(*ExitError) - if !ok { - t.Fatalf("expected *ExitError but got %T", err) - } - if e.Signal() != "TERM" || e.ExitStatus() != 15 { - t.Fatalf("expected command to exit with signal TERM and status 15 but got signal %s and status %v", e.Signal(), e.ExitStatus()) - } -} - -// Test exit signal and status are both returned correctly. -func TestKnownExitSignalOnly(t *testing.T) { - conn := dial(exitSignalHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %v", err) - } - defer session.Close() - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %v", err) - } - err = session.Wait() - if err == nil { - t.Fatalf("expected command to fail but it didn't") - } - e, ok := err.(*ExitError) - if !ok { - t.Fatalf("expected *ExitError but got %T", err) - } - if e.Signal() != "TERM" || e.ExitStatus() != 143 { - t.Fatalf("expected command to exit with signal TERM and status 143 but got signal %s and status %v", e.Signal(), e.ExitStatus()) - } -} - -// Test exit signal and status are both returned correctly. -func TestUnknownExitSignal(t *testing.T) { - conn := dial(exitSignalUnknownHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %v", err) - } - defer session.Close() - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %v", err) - } - err = session.Wait() - if err == nil { - t.Fatalf("expected command to fail but it didn't") - } - e, ok := err.(*ExitError) - if !ok { - t.Fatalf("expected *ExitError but got %T", err) - } - if e.Signal() != "SYS" || e.ExitStatus() != 128 { - t.Fatalf("expected command to exit with signal SYS and status 128 but got signal %s and status %v", e.Signal(), e.ExitStatus()) - } -} - -// Test WaitMsg is not returned if the channel closes abruptly. -func TestExitWithoutStatusOrSignal(t *testing.T) { - conn := dial(exitWithoutSignalOrStatus, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatalf("Unable to request new session: %v", err) - } - defer session.Close() - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %v", err) - } - err = session.Wait() - if err == nil { - t.Fatalf("expected command to fail but it didn't") - } - _, ok := err.(*ExitError) - if ok { - // you can't actually test for errors.errorString - // because it's not exported. - t.Fatalf("expected *errorString but got %T", err) - } -} - -// windowTestBytes is the number of bytes that we'll send to the SSH server. -const windowTestBytes = 16000 * 200 - -// TestServerWindow writes random data to the server. The server is expected to echo -// the same data back, which is compared against the original. -func TestServerWindow(t *testing.T) { - origBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes)) - io.CopyN(origBuf, crypto_rand.Reader, windowTestBytes) - origBytes := origBuf.Bytes() - - conn := dial(echoHandler, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatal(err) - } - defer session.Close() - result := make(chan []byte) - - go func() { - defer close(result) - echoedBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes)) - serverStdout, err := session.StdoutPipe() - if err != nil { - t.Errorf("StdoutPipe failed: %v", err) - return - } - n, err := copyNRandomly("stdout", echoedBuf, serverStdout, windowTestBytes) - if err != nil && err != io.EOF { - t.Errorf("Read only %d bytes from server, expected %d: %v", n, windowTestBytes, err) - } - result <- echoedBuf.Bytes() - }() - - serverStdin, err := session.StdinPipe() - if err != nil { - t.Fatalf("StdinPipe failed: %v", err) - } - written, err := copyNRandomly("stdin", serverStdin, origBuf, windowTestBytes) - if err != nil { - t.Fatalf("failed to copy origBuf to serverStdin: %v", err) - } - if written != windowTestBytes { - t.Fatalf("Wrote only %d of %d bytes to server", written, windowTestBytes) - } - - echoedBytes := <-result - - if !bytes.Equal(origBytes, echoedBytes) { - t.Fatalf("Echoed buffer differed from original, orig %d, echoed %d", len(origBytes), len(echoedBytes)) - } -} - -// Verify the client can handle a keepalive packet from the server. -func TestClientHandlesKeepalives(t *testing.T) { - conn := dial(channelKeepaliveSender, t) - defer conn.Close() - session, err := conn.NewSession() - if err != nil { - t.Fatal(err) - } - defer session.Close() - if err := session.Shell(); err != nil { - t.Fatalf("Unable to execute command: %v", err) - } - err = session.Wait() - if err != nil { - t.Fatalf("expected nil but got: %v", err) - } -} - -type exitStatusMsg struct { - Status uint32 -} - -type exitSignalMsg struct { - Signal string - CoreDumped bool - Errmsg string - Lang string -} - -func handleTerminalRequests(in <-chan *Request) { - for req := range in { - ok := false - switch req.Type { - case "shell": - ok = true - if len(req.Payload) > 0 { - // We don't accept any commands, only the default shell. - ok = false - } - case "env": - ok = true - } - req.Reply(ok, nil) - } -} - -func newServerShell(ch Channel, in <-chan *Request, prompt string) *terminal.Terminal { - term := terminal.NewTerminal(ch, prompt) - go handleTerminalRequests(in) - return term -} - -func exitStatusZeroHandler(ch Channel, in <-chan *Request, t *testing.T) { - defer ch.Close() - // this string is returned to stdout - shell := newServerShell(ch, in, "> ") - readLine(shell, t) - sendStatus(0, ch, t) -} - -func exitStatusNonZeroHandler(ch Channel, in <-chan *Request, t *testing.T) { - defer ch.Close() - shell := newServerShell(ch, in, "> ") - readLine(shell, t) - sendStatus(15, ch, t) -} - -func exitSignalAndStatusHandler(ch Channel, in <-chan *Request, t *testing.T) { - defer ch.Close() - shell := newServerShell(ch, in, "> ") - readLine(shell, t) - sendStatus(15, ch, t) - sendSignal("TERM", ch, t) -} - -func exitSignalHandler(ch Channel, in <-chan *Request, t *testing.T) { - defer ch.Close() - shell := newServerShell(ch, in, "> ") - readLine(shell, t) - sendSignal("TERM", ch, t) -} - -func exitSignalUnknownHandler(ch Channel, in <-chan *Request, t *testing.T) { - defer ch.Close() - shell := newServerShell(ch, in, "> ") - readLine(shell, t) - sendSignal("SYS", ch, t) -} - -func exitWithoutSignalOrStatus(ch Channel, in <-chan *Request, t *testing.T) { - defer ch.Close() - shell := newServerShell(ch, in, "> ") - readLine(shell, t) -} - -func shellHandler(ch Channel, in <-chan *Request, t *testing.T) { - defer ch.Close() - // this string is returned to stdout - shell := newServerShell(ch, in, "golang") - readLine(shell, t) - sendStatus(0, ch, t) -} - -// Ignores the command, writes fixed strings to stderr and stdout. -// Strings are "this-is-stdout." and "this-is-stderr.". -func fixedOutputHandler(ch Channel, in <-chan *Request, t *testing.T) { - defer ch.Close() - _, err := ch.Read(nil) - - req, ok := <-in - if !ok { - t.Fatalf("error: expected channel request, got: %#v", err) - return - } - - // ignore request, always send some text - req.Reply(true, nil) - - _, err = io.WriteString(ch, "this-is-stdout.") - if err != nil { - t.Fatalf("error writing on server: %v", err) - } - _, err = io.WriteString(ch.Stderr(), "this-is-stderr.") - if err != nil { - t.Fatalf("error writing on server: %v", err) - } - sendStatus(0, ch, t) -} - -func readLine(shell *terminal.Terminal, t *testing.T) { - if _, err := shell.ReadLine(); err != nil && err != io.EOF { - t.Errorf("unable to read line: %v", err) - } -} - -func sendStatus(status uint32, ch Channel, t *testing.T) { - msg := exitStatusMsg{ - Status: status, - } - if _, err := ch.SendRequest("exit-status", false, Marshal(&msg)); err != nil { - t.Errorf("unable to send status: %v", err) - } -} - -func sendSignal(signal string, ch Channel, t *testing.T) { - sig := exitSignalMsg{ - Signal: signal, - CoreDumped: false, - Errmsg: "Process terminated", - Lang: "en-GB-oed", - } - if _, err := ch.SendRequest("exit-signal", false, Marshal(&sig)); err != nil { - t.Errorf("unable to send signal: %v", err) - } -} - -func discardHandler(ch Channel, t *testing.T) { - defer ch.Close() - io.Copy(ioutil.Discard, ch) -} - -func echoHandler(ch Channel, in <-chan *Request, t *testing.T) { - defer ch.Close() - if n, err := copyNRandomly("echohandler", ch, ch, windowTestBytes); err != nil { - t.Errorf("short write, wrote %d, expected %d: %v ", n, windowTestBytes, err) - } -} - -// copyNRandomly copies n bytes from src to dst. It uses a variable, and random, -// buffer size to exercise more code paths. -func copyNRandomly(title string, dst io.Writer, src io.Reader, n int) (int, error) { - var ( - buf = make([]byte, 32*1024) - written int - remaining = n - ) - for remaining > 0 { - l := rand.Intn(1 << 15) - if remaining < l { - l = remaining - } - nr, er := src.Read(buf[:l]) - nw, ew := dst.Write(buf[:nr]) - remaining -= nw - written += nw - if ew != nil { - return written, ew - } - if nr != nw { - return written, io.ErrShortWrite - } - if er != nil && er != io.EOF { - return written, er - } - } - return written, nil -} - -func channelKeepaliveSender(ch Channel, in <-chan *Request, t *testing.T) { - defer ch.Close() - shell := newServerShell(ch, in, "> ") - readLine(shell, t) - if _, err := ch.SendRequest("keepalive@openssh.com", true, nil); err != nil { - t.Errorf("unable to send channel keepalive request: %v", err) - } - sendStatus(0, ch, t) -} - -func TestClientWriteEOF(t *testing.T) { - conn := dial(simpleEchoHandler, t) - defer conn.Close() - - session, err := conn.NewSession() - if err != nil { - t.Fatal(err) - } - defer session.Close() - stdin, err := session.StdinPipe() - if err != nil { - t.Fatalf("StdinPipe failed: %v", err) - } - stdout, err := session.StdoutPipe() - if err != nil { - t.Fatalf("StdoutPipe failed: %v", err) - } - - data := []byte(`0000`) - _, err = stdin.Write(data) - if err != nil { - t.Fatalf("Write failed: %v", err) - } - stdin.Close() - - res, err := ioutil.ReadAll(stdout) - if err != nil { - t.Fatalf("Read failed: %v", err) - } - - if !bytes.Equal(data, res) { - t.Fatalf("Read differed from write, wrote: %v, read: %v", data, res) - } -} - -func simpleEchoHandler(ch Channel, in <-chan *Request, t *testing.T) { - defer ch.Close() - data, err := ioutil.ReadAll(ch) - if err != nil { - t.Errorf("handler read error: %v", err) - } - _, err = ch.Write(data) - if err != nil { - t.Errorf("handler write error: %v", err) - } -} - -func TestSessionID(t *testing.T) { - c1, c2, err := netPipe() - if err != nil { - t.Fatalf("netPipe: %v", err) - } - defer c1.Close() - defer c2.Close() - - serverID := make(chan []byte, 1) - clientID := make(chan []byte, 1) - - serverConf := &ServerConfig{ - NoClientAuth: true, - } - serverConf.AddHostKey(testSigners["ecdsa"]) - clientConf := &ClientConfig{ - User: "user", - } - - go func() { - conn, chans, reqs, err := NewServerConn(c1, serverConf) - if err != nil { - t.Fatalf("server handshake: %v", err) - } - serverID <- conn.SessionID() - go DiscardRequests(reqs) - for ch := range chans { - ch.Reject(Prohibited, "") - } - }() - - go func() { - conn, chans, reqs, err := NewClientConn(c2, "", clientConf) - if err != nil { - t.Fatalf("client handshake: %v", err) - } - clientID <- conn.SessionID() - go DiscardRequests(reqs) - for ch := range chans { - ch.Reject(Prohibited, "") - } - }() - - s := <-serverID - c := <-clientID - if bytes.Compare(s, c) != 0 { - t.Errorf("server session ID (%x) != client session ID (%x)", s, c) - } else if len(s) == 0 { - t.Errorf("client and server SessionID were empty.") - } -} - -type noReadConn struct { - readSeen bool - net.Conn -} - -func (c *noReadConn) Close() error { - return nil -} - -func (c *noReadConn) Read(b []byte) (int, error) { - c.readSeen = true - return 0, errors.New("noReadConn error") -} - -func TestInvalidServerConfiguration(t *testing.T) { - c1, c2, err := netPipe() - if err != nil { - t.Fatalf("netPipe: %v", err) - } - defer c1.Close() - defer c2.Close() - - serveConn := noReadConn{Conn: c1} - serverConf := &ServerConfig{} - - NewServerConn(&serveConn, serverConf) - if serveConn.readSeen { - t.Fatalf("NewServerConn attempted to Read() from Conn while configuration is missing host key") - } - - serverConf.AddHostKey(testSigners["ecdsa"]) - - NewServerConn(&serveConn, serverConf) - if serveConn.readSeen { - t.Fatalf("NewServerConn attempted to Read() from Conn while configuration is missing authentication method") - } -} - -func TestHostKeyAlgorithms(t *testing.T) { - serverConf := &ServerConfig{ - NoClientAuth: true, - } - serverConf.AddHostKey(testSigners["rsa"]) - serverConf.AddHostKey(testSigners["ecdsa"]) - - connect := func(clientConf *ClientConfig, want string) { - var alg string - clientConf.HostKeyCallback = func(h string, a net.Addr, key PublicKey) error { - alg = key.Type() - return nil - } - c1, c2, err := netPipe() - if err != nil { - t.Fatalf("netPipe: %v", err) - } - defer c1.Close() - defer c2.Close() - - go NewServerConn(c1, serverConf) - _, _, _, err = NewClientConn(c2, "", clientConf) - if err != nil { - t.Fatalf("NewClientConn: %v", err) - } - if alg != want { - t.Errorf("selected key algorithm %s, want %s", alg, want) - } - } - - // By default, we get the preferred algorithm, which is ECDSA 256. - - clientConf := &ClientConfig{} - connect(clientConf, KeyAlgoECDSA256) - - // Client asks for RSA explicitly. - clientConf.HostKeyAlgorithms = []string{KeyAlgoRSA} - connect(clientConf, KeyAlgoRSA) - - c1, c2, err := netPipe() - if err != nil { - t.Fatalf("netPipe: %v", err) - } - defer c1.Close() - defer c2.Close() - - go NewServerConn(c1, serverConf) - clientConf.HostKeyAlgorithms = []string{"nonexistent-hostkey-algo"} - _, _, _, err = NewClientConn(c2, "", clientConf) - if err == nil { - t.Fatal("succeeded connecting with unknown hostkey algorithm") - } -} diff --git a/modules/crypto/ssh/tcpip.go b/modules/crypto/ssh/tcpip.go deleted file mode 100755 index 6151241f..00000000 --- a/modules/crypto/ssh/tcpip.go +++ /dev/null @@ -1,407 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "errors" - "fmt" - "io" - "math/rand" - "net" - "strconv" - "strings" - "sync" - "time" -) - -// Listen requests the remote peer open a listening socket on -// addr. Incoming connections will be available by calling Accept on -// the returned net.Listener. The listener must be serviced, or the -// SSH connection may hang. -func (c *Client) Listen(n, addr string) (net.Listener, error) { - laddr, err := net.ResolveTCPAddr(n, addr) - if err != nil { - return nil, err - } - return c.ListenTCP(laddr) -} - -// Automatic port allocation is broken with OpenSSH before 6.0. See -// also https://bugzilla.mindrot.org/show_bug.cgi?id=2017. In -// particular, OpenSSH 5.9 sends a channelOpenMsg with port number 0, -// rather than the actual port number. This means you can never open -// two different listeners with auto allocated ports. We work around -// this by trying explicit ports until we succeed. - -const openSSHPrefix = "OpenSSH_" - -var portRandomizer = rand.New(rand.NewSource(time.Now().UnixNano())) - -// isBrokenOpenSSHVersion returns true if the given version string -// specifies a version of OpenSSH that is known to have a bug in port -// forwarding. -func isBrokenOpenSSHVersion(versionStr string) bool { - i := strings.Index(versionStr, openSSHPrefix) - if i < 0 { - return false - } - i += len(openSSHPrefix) - j := i - for ; j < len(versionStr); j++ { - if versionStr[j] < '0' || versionStr[j] > '9' { - break - } - } - version, _ := strconv.Atoi(versionStr[i:j]) - return version < 6 -} - -// autoPortListenWorkaround simulates automatic port allocation by -// trying random ports repeatedly. -func (c *Client) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener, error) { - var sshListener net.Listener - var err error - const tries = 10 - for i := 0; i < tries; i++ { - addr := *laddr - addr.Port = 1024 + portRandomizer.Intn(60000) - sshListener, err = c.ListenTCP(&addr) - if err == nil { - laddr.Port = addr.Port - return sshListener, err - } - } - return nil, fmt.Errorf("ssh: listen on random port failed after %d tries: %v", tries, err) -} - -// RFC 4254 7.1 -type channelForwardMsg struct { - addr string - rport uint32 -} - -// ListenTCP requests the remote peer open a listening socket -// on laddr. Incoming connections will be available by calling -// Accept on the returned net.Listener. -func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) { - if laddr.Port == 0 && isBrokenOpenSSHVersion(string(c.ServerVersion())) { - return c.autoPortListenWorkaround(laddr) - } - - m := channelForwardMsg{ - laddr.IP.String(), - uint32(laddr.Port), - } - // send message - ok, resp, err := c.SendRequest("tcpip-forward", true, Marshal(&m)) - if err != nil { - return nil, err - } - if !ok { - return nil, errors.New("ssh: tcpip-forward request denied by peer") - } - - // If the original port was 0, then the remote side will - // supply a real port number in the response. - if laddr.Port == 0 { - var p struct { - Port uint32 - } - if err := Unmarshal(resp, &p); err != nil { - return nil, err - } - laddr.Port = int(p.Port) - } - - // Register this forward, using the port number we obtained. - ch := c.forwards.add(*laddr) - - return &tcpListener{laddr, c, ch}, nil -} - -// forwardList stores a mapping between remote -// forward requests and the tcpListeners. -type forwardList struct { - sync.Mutex - entries []forwardEntry -} - -// forwardEntry represents an established mapping of a laddr on a -// remote ssh server to a channel connected to a tcpListener. -type forwardEntry struct { - laddr net.TCPAddr - c chan forward -} - -// forward represents an incoming forwarded tcpip connection. The -// arguments to add/remove/lookup should be address as specified in -// the original forward-request. -type forward struct { - newCh NewChannel // the ssh client channel underlying this forward - raddr *net.TCPAddr // the raddr of the incoming connection -} - -func (l *forwardList) add(addr net.TCPAddr) chan forward { - l.Lock() - defer l.Unlock() - f := forwardEntry{ - addr, - make(chan forward, 1), - } - l.entries = append(l.entries, f) - return f.c -} - -// See RFC 4254, section 7.2 -type forwardedTCPPayload struct { - Addr string - Port uint32 - OriginAddr string - OriginPort uint32 -} - -// parseTCPAddr parses the originating address from the remote into a *net.TCPAddr. -func parseTCPAddr(addr string, port uint32) (*net.TCPAddr, error) { - if port == 0 || port > 65535 { - return nil, fmt.Errorf("ssh: port number out of range: %d", port) - } - ip := net.ParseIP(string(addr)) - if ip == nil { - return nil, fmt.Errorf("ssh: cannot parse IP address %q", addr) - } - return &net.TCPAddr{IP: ip, Port: int(port)}, nil -} - -func (l *forwardList) handleChannels(in <-chan NewChannel) { - for ch := range in { - var payload forwardedTCPPayload - if err := Unmarshal(ch.ExtraData(), &payload); err != nil { - ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error()) - continue - } - - // RFC 4254 section 7.2 specifies that incoming - // addresses should list the address, in string - // format. It is implied that this should be an IP - // address, as it would be impossible to connect to it - // otherwise. - laddr, err := parseTCPAddr(payload.Addr, payload.Port) - if err != nil { - ch.Reject(ConnectionFailed, err.Error()) - continue - } - raddr, err := parseTCPAddr(payload.OriginAddr, payload.OriginPort) - if err != nil { - ch.Reject(ConnectionFailed, err.Error()) - continue - } - - if ok := l.forward(*laddr, *raddr, ch); !ok { - // Section 7.2, implementations MUST reject spurious incoming - // connections. - ch.Reject(Prohibited, "no forward for address") - continue - } - } -} - -// remove removes the forward entry, and the channel feeding its -// listener. -func (l *forwardList) remove(addr net.TCPAddr) { - l.Lock() - defer l.Unlock() - for i, f := range l.entries { - if addr.IP.Equal(f.laddr.IP) && addr.Port == f.laddr.Port { - l.entries = append(l.entries[:i], l.entries[i+1:]...) - close(f.c) - return - } - } -} - -// closeAll closes and clears all forwards. -func (l *forwardList) closeAll() { - l.Lock() - defer l.Unlock() - for _, f := range l.entries { - close(f.c) - } - l.entries = nil -} - -func (l *forwardList) forward(laddr, raddr net.TCPAddr, ch NewChannel) bool { - l.Lock() - defer l.Unlock() - for _, f := range l.entries { - if laddr.IP.Equal(f.laddr.IP) && laddr.Port == f.laddr.Port { - f.c <- forward{ch, &raddr} - return true - } - } - return false -} - -type tcpListener struct { - laddr *net.TCPAddr - - conn *Client - in <-chan forward -} - -// Accept waits for and returns the next connection to the listener. -func (l *tcpListener) Accept() (net.Conn, error) { - s, ok := <-l.in - if !ok { - return nil, io.EOF - } - ch, incoming, err := s.newCh.Accept() - if err != nil { - return nil, err - } - go DiscardRequests(incoming) - - return &tcpChanConn{ - Channel: ch, - laddr: l.laddr, - raddr: s.raddr, - }, nil -} - -// Close closes the listener. -func (l *tcpListener) Close() error { - m := channelForwardMsg{ - l.laddr.IP.String(), - uint32(l.laddr.Port), - } - - // this also closes the listener. - l.conn.forwards.remove(*l.laddr) - ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m)) - if err == nil && !ok { - err = errors.New("ssh: cancel-tcpip-forward failed") - } - return err -} - -// Addr returns the listener's network address. -func (l *tcpListener) Addr() net.Addr { - return l.laddr -} - -// Dial initiates a connection to the addr from the remote host. -// The resulting connection has a zero LocalAddr() and RemoteAddr(). -func (c *Client) Dial(n, addr string) (net.Conn, error) { - // Parse the address into host and numeric port. - host, portString, err := net.SplitHostPort(addr) - if err != nil { - return nil, err - } - port, err := strconv.ParseUint(portString, 10, 16) - if err != nil { - return nil, err - } - // Use a zero address for local and remote address. - zeroAddr := &net.TCPAddr{ - IP: net.IPv4zero, - Port: 0, - } - ch, err := c.dial(net.IPv4zero.String(), 0, host, int(port)) - if err != nil { - return nil, err - } - return &tcpChanConn{ - Channel: ch, - laddr: zeroAddr, - raddr: zeroAddr, - }, nil -} - -// DialTCP connects to the remote address raddr on the network net, -// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used -// as the local address for the connection. -func (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) { - if laddr == nil { - laddr = &net.TCPAddr{ - IP: net.IPv4zero, - Port: 0, - } - } - ch, err := c.dial(laddr.IP.String(), laddr.Port, raddr.IP.String(), raddr.Port) - if err != nil { - return nil, err - } - return &tcpChanConn{ - Channel: ch, - laddr: laddr, - raddr: raddr, - }, nil -} - -// RFC 4254 7.2 -type channelOpenDirectMsg struct { - raddr string - rport uint32 - laddr string - lport uint32 -} - -func (c *Client) dial(laddr string, lport int, raddr string, rport int) (Channel, error) { - msg := channelOpenDirectMsg{ - raddr: raddr, - rport: uint32(rport), - laddr: laddr, - lport: uint32(lport), - } - ch, in, err := c.OpenChannel("direct-tcpip", Marshal(&msg)) - if err != nil { - return nil, err - } - go DiscardRequests(in) - return ch, err -} - -type tcpChan struct { - Channel // the backing channel -} - -// tcpChanConn fulfills the net.Conn interface without -// the tcpChan having to hold laddr or raddr directly. -type tcpChanConn struct { - Channel - laddr, raddr net.Addr -} - -// LocalAddr returns the local network address. -func (t *tcpChanConn) LocalAddr() net.Addr { - return t.laddr -} - -// RemoteAddr returns the remote network address. -func (t *tcpChanConn) RemoteAddr() net.Addr { - return t.raddr -} - -// SetDeadline sets the read and write deadlines associated -// with the connection. -func (t *tcpChanConn) SetDeadline(deadline time.Time) error { - if err := t.SetReadDeadline(deadline); err != nil { - return err - } - return t.SetWriteDeadline(deadline) -} - -// SetReadDeadline sets the read deadline. -// A zero value for t means Read will not time out. -// After the deadline, the error from Read will implement net.Error -// with Timeout() == true. -func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error { - return errors.New("ssh: tcpChan: deadline not supported") -} - -// SetWriteDeadline exists to satisfy the net.Conn interface -// but is not implemented by this type. It always returns an error. -func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error { - return errors.New("ssh: tcpChan: deadline not supported") -} diff --git a/modules/crypto/ssh/tcpip_test.go b/modules/crypto/ssh/tcpip_test.go deleted file mode 100755 index f1265cb4..00000000 --- a/modules/crypto/ssh/tcpip_test.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "testing" -) - -func TestAutoPortListenBroken(t *testing.T) { - broken := "SSH-2.0-OpenSSH_5.9hh11" - works := "SSH-2.0-OpenSSH_6.1" - if !isBrokenOpenSSHVersion(broken) { - t.Errorf("version %q not marked as broken", broken) - } - if isBrokenOpenSSHVersion(works) { - t.Errorf("version %q marked as broken", works) - } -} diff --git a/modules/crypto/ssh/terminal/terminal.go b/modules/crypto/ssh/terminal/terminal.go deleted file mode 100755 index 741eeb13..00000000 --- a/modules/crypto/ssh/terminal/terminal.go +++ /dev/null @@ -1,892 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package terminal - -import ( - "bytes" - "io" - "sync" - "unicode/utf8" -) - -// EscapeCodes contains escape sequences that can be written to the terminal in -// order to achieve different styles of text. -type EscapeCodes struct { - // Foreground colors - Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte - - // Reset all attributes - Reset []byte -} - -var vt100EscapeCodes = EscapeCodes{ - Black: []byte{keyEscape, '[', '3', '0', 'm'}, - Red: []byte{keyEscape, '[', '3', '1', 'm'}, - Green: []byte{keyEscape, '[', '3', '2', 'm'}, - Yellow: []byte{keyEscape, '[', '3', '3', 'm'}, - Blue: []byte{keyEscape, '[', '3', '4', 'm'}, - Magenta: []byte{keyEscape, '[', '3', '5', 'm'}, - Cyan: []byte{keyEscape, '[', '3', '6', 'm'}, - White: []byte{keyEscape, '[', '3', '7', 'm'}, - - Reset: []byte{keyEscape, '[', '0', 'm'}, -} - -// Terminal contains the state for running a VT100 terminal that is capable of -// reading lines of input. -type Terminal struct { - // AutoCompleteCallback, if non-null, is called for each keypress with - // the full input line and the current position of the cursor (in - // bytes, as an index into |line|). If it returns ok=false, the key - // press is processed normally. Otherwise it returns a replacement line - // and the new cursor position. - AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool) - - // Escape contains a pointer to the escape codes for this terminal. - // It's always a valid pointer, although the escape codes themselves - // may be empty if the terminal doesn't support them. - Escape *EscapeCodes - - // lock protects the terminal and the state in this object from - // concurrent processing of a key press and a Write() call. - lock sync.Mutex - - c io.ReadWriter - prompt []rune - - // line is the current line being entered. - line []rune - // pos is the logical position of the cursor in line - pos int - // echo is true if local echo is enabled - echo bool - // pasteActive is true iff there is a bracketed paste operation in - // progress. - pasteActive bool - - // cursorX contains the current X value of the cursor where the left - // edge is 0. cursorY contains the row number where the first row of - // the current line is 0. - cursorX, cursorY int - // maxLine is the greatest value of cursorY so far. - maxLine int - - termWidth, termHeight int - - // outBuf contains the terminal data to be sent. - outBuf []byte - // remainder contains the remainder of any partial key sequences after - // a read. It aliases into inBuf. - remainder []byte - inBuf [256]byte - - // history contains previously entered commands so that they can be - // accessed with the up and down keys. - history stRingBuffer - // historyIndex stores the currently accessed history entry, where zero - // means the immediately previous entry. - historyIndex int - // When navigating up and down the history it's possible to return to - // the incomplete, initial line. That value is stored in - // historyPending. - historyPending string -} - -// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is -// a local terminal, that terminal must first have been put into raw mode. -// prompt is a string that is written at the start of each input line (i.e. -// "> "). -func NewTerminal(c io.ReadWriter, prompt string) *Terminal { - return &Terminal{ - Escape: &vt100EscapeCodes, - c: c, - prompt: []rune(prompt), - termWidth: 80, - termHeight: 24, - echo: true, - historyIndex: -1, - } -} - -const ( - keyCtrlD = 4 - keyCtrlU = 21 - keyEnter = '\r' - keyEscape = 27 - keyBackspace = 127 - keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota - keyUp - keyDown - keyLeft - keyRight - keyAltLeft - keyAltRight - keyHome - keyEnd - keyDeleteWord - keyDeleteLine - keyClearScreen - keyPasteStart - keyPasteEnd -) - -var pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'} -var pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'} - -// bytesToKey tries to parse a key sequence from b. If successful, it returns -// the key and the remainder of the input. Otherwise it returns utf8.RuneError. -func bytesToKey(b []byte, pasteActive bool) (rune, []byte) { - if len(b) == 0 { - return utf8.RuneError, nil - } - - if !pasteActive { - switch b[0] { - case 1: // ^A - return keyHome, b[1:] - case 5: // ^E - return keyEnd, b[1:] - case 8: // ^H - return keyBackspace, b[1:] - case 11: // ^K - return keyDeleteLine, b[1:] - case 12: // ^L - return keyClearScreen, b[1:] - case 23: // ^W - return keyDeleteWord, b[1:] - } - } - - if b[0] != keyEscape { - if !utf8.FullRune(b) { - return utf8.RuneError, b - } - r, l := utf8.DecodeRune(b) - return r, b[l:] - } - - if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' { - switch b[2] { - case 'A': - return keyUp, b[3:] - case 'B': - return keyDown, b[3:] - case 'C': - return keyRight, b[3:] - case 'D': - return keyLeft, b[3:] - case 'H': - return keyHome, b[3:] - case 'F': - return keyEnd, b[3:] - } - } - - if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' { - switch b[5] { - case 'C': - return keyAltRight, b[6:] - case 'D': - return keyAltLeft, b[6:] - } - } - - if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) { - return keyPasteStart, b[6:] - } - - if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) { - return keyPasteEnd, b[6:] - } - - // If we get here then we have a key that we don't recognise, or a - // partial sequence. It's not clear how one should find the end of a - // sequence without knowing them all, but it seems that [a-zA-Z~] only - // appears at the end of a sequence. - for i, c := range b[0:] { - if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' { - return keyUnknown, b[i+1:] - } - } - - return utf8.RuneError, b -} - -// queue appends data to the end of t.outBuf -func (t *Terminal) queue(data []rune) { - t.outBuf = append(t.outBuf, []byte(string(data))...) -} - -var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'} -var space = []rune{' '} - -func isPrintable(key rune) bool { - isInSurrogateArea := key >= 0xd800 && key <= 0xdbff - return key >= 32 && !isInSurrogateArea -} - -// moveCursorToPos appends data to t.outBuf which will move the cursor to the -// given, logical position in the text. -func (t *Terminal) moveCursorToPos(pos int) { - if !t.echo { - return - } - - x := visualLength(t.prompt) + pos - y := x / t.termWidth - x = x % t.termWidth - - up := 0 - if y < t.cursorY { - up = t.cursorY - y - } - - down := 0 - if y > t.cursorY { - down = y - t.cursorY - } - - left := 0 - if x < t.cursorX { - left = t.cursorX - x - } - - right := 0 - if x > t.cursorX { - right = x - t.cursorX - } - - t.cursorX = x - t.cursorY = y - t.move(up, down, left, right) -} - -func (t *Terminal) move(up, down, left, right int) { - movement := make([]rune, 3*(up+down+left+right)) - m := movement - for i := 0; i < up; i++ { - m[0] = keyEscape - m[1] = '[' - m[2] = 'A' - m = m[3:] - } - for i := 0; i < down; i++ { - m[0] = keyEscape - m[1] = '[' - m[2] = 'B' - m = m[3:] - } - for i := 0; i < left; i++ { - m[0] = keyEscape - m[1] = '[' - m[2] = 'D' - m = m[3:] - } - for i := 0; i < right; i++ { - m[0] = keyEscape - m[1] = '[' - m[2] = 'C' - m = m[3:] - } - - t.queue(movement) -} - -func (t *Terminal) clearLineToRight() { - op := []rune{keyEscape, '[', 'K'} - t.queue(op) -} - -const maxLineLength = 4096 - -func (t *Terminal) setLine(newLine []rune, newPos int) { - if t.echo { - t.moveCursorToPos(0) - t.writeLine(newLine) - for i := len(newLine); i < len(t.line); i++ { - t.writeLine(space) - } - t.moveCursorToPos(newPos) - } - t.line = newLine - t.pos = newPos -} - -func (t *Terminal) advanceCursor(places int) { - t.cursorX += places - t.cursorY += t.cursorX / t.termWidth - if t.cursorY > t.maxLine { - t.maxLine = t.cursorY - } - t.cursorX = t.cursorX % t.termWidth - - if places > 0 && t.cursorX == 0 { - // Normally terminals will advance the current position - // when writing a character. But that doesn't happen - // for the last character in a line. However, when - // writing a character (except a new line) that causes - // a line wrap, the position will be advanced two - // places. - // - // So, if we are stopping at the end of a line, we - // need to write a newline so that our cursor can be - // advanced to the next line. - t.outBuf = append(t.outBuf, '\n') - } -} - -func (t *Terminal) eraseNPreviousChars(n int) { - if n == 0 { - return - } - - if t.pos < n { - n = t.pos - } - t.pos -= n - t.moveCursorToPos(t.pos) - - copy(t.line[t.pos:], t.line[n+t.pos:]) - t.line = t.line[:len(t.line)-n] - if t.echo { - t.writeLine(t.line[t.pos:]) - for i := 0; i < n; i++ { - t.queue(space) - } - t.advanceCursor(n) - t.moveCursorToPos(t.pos) - } -} - -// countToLeftWord returns then number of characters from the cursor to the -// start of the previous word. -func (t *Terminal) countToLeftWord() int { - if t.pos == 0 { - return 0 - } - - pos := t.pos - 1 - for pos > 0 { - if t.line[pos] != ' ' { - break - } - pos-- - } - for pos > 0 { - if t.line[pos] == ' ' { - pos++ - break - } - pos-- - } - - return t.pos - pos -} - -// countToRightWord returns then number of characters from the cursor to the -// start of the next word. -func (t *Terminal) countToRightWord() int { - pos := t.pos - for pos < len(t.line) { - if t.line[pos] == ' ' { - break - } - pos++ - } - for pos < len(t.line) { - if t.line[pos] != ' ' { - break - } - pos++ - } - return pos - t.pos -} - -// visualLength returns the number of visible glyphs in s. -func visualLength(runes []rune) int { - inEscapeSeq := false - length := 0 - - for _, r := range runes { - switch { - case inEscapeSeq: - if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') { - inEscapeSeq = false - } - case r == '\x1b': - inEscapeSeq = true - default: - length++ - } - } - - return length -} - -// handleKey processes the given key and, optionally, returns a line of text -// that the user has entered. -func (t *Terminal) handleKey(key rune) (line string, ok bool) { - if t.pasteActive && key != keyEnter { - t.addKeyToLine(key) - return - } - - switch key { - case keyBackspace: - if t.pos == 0 { - return - } - t.eraseNPreviousChars(1) - case keyAltLeft: - // move left by a word. - t.pos -= t.countToLeftWord() - t.moveCursorToPos(t.pos) - case keyAltRight: - // move right by a word. - t.pos += t.countToRightWord() - t.moveCursorToPos(t.pos) - case keyLeft: - if t.pos == 0 { - return - } - t.pos-- - t.moveCursorToPos(t.pos) - case keyRight: - if t.pos == len(t.line) { - return - } - t.pos++ - t.moveCursorToPos(t.pos) - case keyHome: - if t.pos == 0 { - return - } - t.pos = 0 - t.moveCursorToPos(t.pos) - case keyEnd: - if t.pos == len(t.line) { - return - } - t.pos = len(t.line) - t.moveCursorToPos(t.pos) - case keyUp: - entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1) - if !ok { - return "", false - } - if t.historyIndex == -1 { - t.historyPending = string(t.line) - } - t.historyIndex++ - runes := []rune(entry) - t.setLine(runes, len(runes)) - case keyDown: - switch t.historyIndex { - case -1: - return - case 0: - runes := []rune(t.historyPending) - t.setLine(runes, len(runes)) - t.historyIndex-- - default: - entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1) - if ok { - t.historyIndex-- - runes := []rune(entry) - t.setLine(runes, len(runes)) - } - } - case keyEnter: - t.moveCursorToPos(len(t.line)) - t.queue([]rune("\r\n")) - line = string(t.line) - ok = true - t.line = t.line[:0] - t.pos = 0 - t.cursorX = 0 - t.cursorY = 0 - t.maxLine = 0 - case keyDeleteWord: - // Delete zero or more spaces and then one or more characters. - t.eraseNPreviousChars(t.countToLeftWord()) - case keyDeleteLine: - // Delete everything from the current cursor position to the - // end of line. - for i := t.pos; i < len(t.line); i++ { - t.queue(space) - t.advanceCursor(1) - } - t.line = t.line[:t.pos] - t.moveCursorToPos(t.pos) - case keyCtrlD: - // Erase the character under the current position. - // The EOF case when the line is empty is handled in - // readLine(). - if t.pos < len(t.line) { - t.pos++ - t.eraseNPreviousChars(1) - } - case keyCtrlU: - t.eraseNPreviousChars(t.pos) - case keyClearScreen: - // Erases the screen and moves the cursor to the home position. - t.queue([]rune("\x1b[2J\x1b[H")) - t.queue(t.prompt) - t.cursorX, t.cursorY = 0, 0 - t.advanceCursor(visualLength(t.prompt)) - t.setLine(t.line, t.pos) - default: - if t.AutoCompleteCallback != nil { - prefix := string(t.line[:t.pos]) - suffix := string(t.line[t.pos:]) - - t.lock.Unlock() - newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key) - t.lock.Lock() - - if completeOk { - t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos])) - return - } - } - if !isPrintable(key) { - return - } - if len(t.line) == maxLineLength { - return - } - t.addKeyToLine(key) - } - return -} - -// addKeyToLine inserts the given key at the current position in the current -// line. -func (t *Terminal) addKeyToLine(key rune) { - if len(t.line) == cap(t.line) { - newLine := make([]rune, len(t.line), 2*(1+len(t.line))) - copy(newLine, t.line) - t.line = newLine - } - t.line = t.line[:len(t.line)+1] - copy(t.line[t.pos+1:], t.line[t.pos:]) - t.line[t.pos] = key - if t.echo { - t.writeLine(t.line[t.pos:]) - } - t.pos++ - t.moveCursorToPos(t.pos) -} - -func (t *Terminal) writeLine(line []rune) { - for len(line) != 0 { - remainingOnLine := t.termWidth - t.cursorX - todo := len(line) - if todo > remainingOnLine { - todo = remainingOnLine - } - t.queue(line[:todo]) - t.advanceCursor(visualLength(line[:todo])) - line = line[todo:] - } -} - -func (t *Terminal) Write(buf []byte) (n int, err error) { - t.lock.Lock() - defer t.lock.Unlock() - - if t.cursorX == 0 && t.cursorY == 0 { - // This is the easy case: there's nothing on the screen that we - // have to move out of the way. - return t.c.Write(buf) - } - - // We have a prompt and possibly user input on the screen. We - // have to clear it first. - t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */) - t.cursorX = 0 - t.clearLineToRight() - - for t.cursorY > 0 { - t.move(1 /* up */, 0, 0, 0) - t.cursorY-- - t.clearLineToRight() - } - - if _, err = t.c.Write(t.outBuf); err != nil { - return - } - t.outBuf = t.outBuf[:0] - - if n, err = t.c.Write(buf); err != nil { - return - } - - t.writeLine(t.prompt) - if t.echo { - t.writeLine(t.line) - } - - t.moveCursorToPos(t.pos) - - if _, err = t.c.Write(t.outBuf); err != nil { - return - } - t.outBuf = t.outBuf[:0] - return -} - -// ReadPassword temporarily changes the prompt and reads a password, without -// echo, from the terminal. -func (t *Terminal) ReadPassword(prompt string) (line string, err error) { - t.lock.Lock() - defer t.lock.Unlock() - - oldPrompt := t.prompt - t.prompt = []rune(prompt) - t.echo = false - - line, err = t.readLine() - - t.prompt = oldPrompt - t.echo = true - - return -} - -// ReadLine returns a line of input from the terminal. -func (t *Terminal) ReadLine() (line string, err error) { - t.lock.Lock() - defer t.lock.Unlock() - - return t.readLine() -} - -func (t *Terminal) readLine() (line string, err error) { - // t.lock must be held at this point - - if t.cursorX == 0 && t.cursorY == 0 { - t.writeLine(t.prompt) - t.c.Write(t.outBuf) - t.outBuf = t.outBuf[:0] - } - - lineIsPasted := t.pasteActive - - for { - rest := t.remainder - lineOk := false - for !lineOk { - var key rune - key, rest = bytesToKey(rest, t.pasteActive) - if key == utf8.RuneError { - break - } - if !t.pasteActive { - if key == keyCtrlD { - if len(t.line) == 0 { - return "", io.EOF - } - } - if key == keyPasteStart { - t.pasteActive = true - if len(t.line) == 0 { - lineIsPasted = true - } - continue - } - } else if key == keyPasteEnd { - t.pasteActive = false - continue - } - if !t.pasteActive { - lineIsPasted = false - } - line, lineOk = t.handleKey(key) - } - if len(rest) > 0 { - n := copy(t.inBuf[:], rest) - t.remainder = t.inBuf[:n] - } else { - t.remainder = nil - } - t.c.Write(t.outBuf) - t.outBuf = t.outBuf[:0] - if lineOk { - if t.echo { - t.historyIndex = -1 - t.history.Add(line) - } - if lineIsPasted { - err = ErrPasteIndicator - } - return - } - - // t.remainder is a slice at the beginning of t.inBuf - // containing a partial key sequence - readBuf := t.inBuf[len(t.remainder):] - var n int - - t.lock.Unlock() - n, err = t.c.Read(readBuf) - t.lock.Lock() - - if err != nil { - return - } - - t.remainder = t.inBuf[:n+len(t.remainder)] - } - - panic("unreachable") // for Go 1.0. -} - -// SetPrompt sets the prompt to be used when reading subsequent lines. -func (t *Terminal) SetPrompt(prompt string) { - t.lock.Lock() - defer t.lock.Unlock() - - t.prompt = []rune(prompt) -} - -func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) { - // Move cursor to column zero at the start of the line. - t.move(t.cursorY, 0, t.cursorX, 0) - t.cursorX, t.cursorY = 0, 0 - t.clearLineToRight() - for t.cursorY < numPrevLines { - // Move down a line - t.move(0, 1, 0, 0) - t.cursorY++ - t.clearLineToRight() - } - // Move back to beginning. - t.move(t.cursorY, 0, 0, 0) - t.cursorX, t.cursorY = 0, 0 - - t.queue(t.prompt) - t.advanceCursor(visualLength(t.prompt)) - t.writeLine(t.line) - t.moveCursorToPos(t.pos) -} - -func (t *Terminal) SetSize(width, height int) error { - t.lock.Lock() - defer t.lock.Unlock() - - if width == 0 { - width = 1 - } - - oldWidth := t.termWidth - t.termWidth, t.termHeight = width, height - - switch { - case width == oldWidth: - // If the width didn't change then nothing else needs to be - // done. - return nil - case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0: - // If there is nothing on current line and no prompt printed, - // just do nothing - return nil - case width < oldWidth: - // Some terminals (e.g. xterm) will truncate lines that were - // too long when shinking. Others, (e.g. gnome-terminal) will - // attempt to wrap them. For the former, repainting t.maxLine - // works great, but that behaviour goes badly wrong in the case - // of the latter because they have doubled every full line. - - // We assume that we are working on a terminal that wraps lines - // and adjust the cursor position based on every previous line - // wrapping and turning into two. This causes the prompt on - // xterms to move upwards, which isn't great, but it avoids a - // huge mess with gnome-terminal. - if t.cursorX >= t.termWidth { - t.cursorX = t.termWidth - 1 - } - t.cursorY *= 2 - t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2) - case width > oldWidth: - // If the terminal expands then our position calculations will - // be wrong in the future because we think the cursor is - // |t.pos| chars into the string, but there will be a gap at - // the end of any wrapped line. - // - // But the position will actually be correct until we move, so - // we can move back to the beginning and repaint everything. - t.clearAndRepaintLinePlusNPrevious(t.maxLine) - } - - _, err := t.c.Write(t.outBuf) - t.outBuf = t.outBuf[:0] - return err -} - -type pasteIndicatorError struct{} - -func (pasteIndicatorError) Error() string { - return "terminal: ErrPasteIndicator not correctly handled" -} - -// ErrPasteIndicator may be returned from ReadLine as the error, in addition -// to valid line data. It indicates that bracketed paste mode is enabled and -// that the returned line consists only of pasted data. Programs may wish to -// interpret pasted data more literally than typed data. -var ErrPasteIndicator = pasteIndicatorError{} - -// SetBracketedPasteMode requests that the terminal bracket paste operations -// with markers. Not all terminals support this but, if it is supported, then -// enabling this mode will stop any autocomplete callback from running due to -// pastes. Additionally, any lines that are completely pasted will be returned -// from ReadLine with the error set to ErrPasteIndicator. -func (t *Terminal) SetBracketedPasteMode(on bool) { - if on { - io.WriteString(t.c, "\x1b[?2004h") - } else { - io.WriteString(t.c, "\x1b[?2004l") - } -} - -// stRingBuffer is a ring buffer of strings. -type stRingBuffer struct { - // entries contains max elements. - entries []string - max int - // head contains the index of the element most recently added to the ring. - head int - // size contains the number of elements in the ring. - size int -} - -func (s *stRingBuffer) Add(a string) { - if s.entries == nil { - const defaultNumEntries = 100 - s.entries = make([]string, defaultNumEntries) - s.max = defaultNumEntries - } - - s.head = (s.head + 1) % s.max - s.entries[s.head] = a - if s.size < s.max { - s.size++ - } -} - -// NthPreviousEntry returns the value passed to the nth previous call to Add. -// If n is zero then the immediately prior value is returned, if one, then the -// next most recent, and so on. If such an element doesn't exist then ok is -// false. -func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) { - if n >= s.size { - return "", false - } - index := s.head - n - if index < 0 { - index += s.max - } - return s.entries[index], true -} diff --git a/modules/crypto/ssh/terminal/terminal_test.go b/modules/crypto/ssh/terminal/terminal_test.go deleted file mode 100755 index a663fe41..00000000 --- a/modules/crypto/ssh/terminal/terminal_test.go +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package terminal - -import ( - "io" - "testing" -) - -type MockTerminal struct { - toSend []byte - bytesPerRead int - received []byte -} - -func (c *MockTerminal) Read(data []byte) (n int, err error) { - n = len(data) - if n == 0 { - return - } - if n > len(c.toSend) { - n = len(c.toSend) - } - if n == 0 { - return 0, io.EOF - } - if c.bytesPerRead > 0 && n > c.bytesPerRead { - n = c.bytesPerRead - } - copy(data, c.toSend[:n]) - c.toSend = c.toSend[n:] - return -} - -func (c *MockTerminal) Write(data []byte) (n int, err error) { - c.received = append(c.received, data...) - return len(data), nil -} - -func TestClose(t *testing.T) { - c := &MockTerminal{} - ss := NewTerminal(c, "> ") - line, err := ss.ReadLine() - if line != "" { - t.Errorf("Expected empty line but got: %s", line) - } - if err != io.EOF { - t.Errorf("Error should have been EOF but got: %s", err) - } -} - -var keyPressTests = []struct { - in string - line string - err error - throwAwayLines int -}{ - { - err: io.EOF, - }, - { - in: "\r", - line: "", - }, - { - in: "foo\r", - line: "foo", - }, - { - in: "a\x1b[Cb\r", // right - line: "ab", - }, - { - in: "a\x1b[Db\r", // left - line: "ba", - }, - { - in: "a\177b\r", // backspace - line: "b", - }, - { - in: "\x1b[A\r", // up - }, - { - in: "\x1b[B\r", // down - }, - { - in: "line\x1b[A\x1b[B\r", // up then down - line: "line", - }, - { - in: "line1\rline2\x1b[A\r", // recall previous line. - line: "line1", - throwAwayLines: 1, - }, - { - // recall two previous lines and append. - in: "line1\rline2\rline3\x1b[A\x1b[Axxx\r", - line: "line1xxx", - throwAwayLines: 2, - }, - { - // Ctrl-A to move to beginning of line followed by ^K to kill - // line. - in: "a b \001\013\r", - line: "", - }, - { - // Ctrl-A to move to beginning of line, Ctrl-E to move to end, - // finally ^K to kill nothing. - in: "a b \001\005\013\r", - line: "a b ", - }, - { - in: "\027\r", - line: "", - }, - { - in: "a\027\r", - line: "", - }, - { - in: "a \027\r", - line: "", - }, - { - in: "a b\027\r", - line: "a ", - }, - { - in: "a b \027\r", - line: "a ", - }, - { - in: "one two thr\x1b[D\027\r", - line: "one two r", - }, - { - in: "\013\r", - line: "", - }, - { - in: "a\013\r", - line: "a", - }, - { - in: "ab\x1b[D\013\r", - line: "a", - }, - { - in: "Ξεσκεπάζω\r", - line: "Ξεσκεπάζω", - }, - { - in: "£\r\x1b[A\177\r", // non-ASCII char, enter, up, backspace. - line: "", - throwAwayLines: 1, - }, - { - in: "£\r££\x1b[A\x1b[B\177\r", // non-ASCII char, enter, 2x non-ASCII, up, down, backspace, enter. - line: "£", - throwAwayLines: 1, - }, - { - // Ctrl-D at the end of the line should be ignored. - in: "a\004\r", - line: "a", - }, - { - // a, b, left, Ctrl-D should erase the b. - in: "ab\x1b[D\004\r", - line: "a", - }, - { - // a, b, c, d, left, left, ^U should erase to the beginning of - // the line. - in: "abcd\x1b[D\x1b[D\025\r", - line: "cd", - }, - { - // Bracketed paste mode: control sequences should be returned - // verbatim in paste mode. - in: "abc\x1b[200~de\177f\x1b[201~\177\r", - line: "abcde\177", - }, - { - // Enter in bracketed paste mode should still work. - in: "abc\x1b[200~d\refg\x1b[201~h\r", - line: "efgh", - throwAwayLines: 1, - }, - { - // Lines consisting entirely of pasted data should be indicated as such. - in: "\x1b[200~a\r", - line: "a", - err: ErrPasteIndicator, - }, -} - -func TestKeyPresses(t *testing.T) { - for i, test := range keyPressTests { - for j := 1; j < len(test.in); j++ { - c := &MockTerminal{ - toSend: []byte(test.in), - bytesPerRead: j, - } - ss := NewTerminal(c, "> ") - for k := 0; k < test.throwAwayLines; k++ { - _, err := ss.ReadLine() - if err != nil { - t.Errorf("Throwaway line %d from test %d resulted in error: %s", k, i, err) - } - } - line, err := ss.ReadLine() - if line != test.line { - t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line) - break - } - if err != test.err { - t.Errorf("Error resulting from test %d (%d bytes per read) was '%v', expected '%v'", i, j, err, test.err) - break - } - } - } -} - -func TestPasswordNotSaved(t *testing.T) { - c := &MockTerminal{ - toSend: []byte("password\r\x1b[A\r"), - bytesPerRead: 1, - } - ss := NewTerminal(c, "> ") - pw, _ := ss.ReadPassword("> ") - if pw != "password" { - t.Fatalf("failed to read password, got %s", pw) - } - line, _ := ss.ReadLine() - if len(line) > 0 { - t.Fatalf("password was saved in history") - } -} - -var setSizeTests = []struct { - width, height int -}{ - {40, 13}, - {80, 24}, - {132, 43}, -} - -func TestTerminalSetSize(t *testing.T) { - for _, setSize := range setSizeTests { - c := &MockTerminal{ - toSend: []byte("password\r\x1b[A\r"), - bytesPerRead: 1, - } - ss := NewTerminal(c, "> ") - ss.SetSize(setSize.width, setSize.height) - pw, _ := ss.ReadPassword("Password: ") - if pw != "password" { - t.Fatalf("failed to read password, got %s", pw) - } - if string(c.received) != "Password: \r\n" { - t.Errorf("failed to set the temporary prompt expected %q, got %q", "Password: ", c.received) - } - } -} diff --git a/modules/crypto/ssh/terminal/util.go b/modules/crypto/ssh/terminal/util.go deleted file mode 100755 index 0763c9a9..00000000 --- a/modules/crypto/ssh/terminal/util.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd - -// Package terminal provides support functions for dealing with terminals, as -// commonly found on UNIX systems. -// -// Putting a terminal into raw mode is the most common requirement: -// -// oldState, err := terminal.MakeRaw(0) -// if err != nil { -// panic(err) -// } -// defer terminal.Restore(0, oldState) -package terminal - -import ( - "io" - "syscall" - "unsafe" -) - -// State contains the state of a terminal. -type State struct { - termios syscall.Termios -} - -// IsTerminal returns true if the given file descriptor is a terminal. -func IsTerminal(fd int) bool { - var termios syscall.Termios - _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) - return err == 0 -} - -// MakeRaw put the terminal connected to the given file descriptor into raw -// mode and returns the previous state of the terminal so that it can be -// restored. -func MakeRaw(fd int) (*State, error) { - var oldState State - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { - return nil, err - } - - newState := oldState.termios - newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF - newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { - return nil, err - } - - return &oldState, nil -} - -// GetState returns the current state of a terminal which may be useful to -// restore the terminal after a signal. -func GetState(fd int) (*State, error) { - var oldState State - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { - return nil, err - } - - return &oldState, nil -} - -// Restore restores the terminal connected to the given file descriptor to a -// previous state. -func Restore(fd int, state *State) error { - _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0) - return err -} - -// GetSize returns the dimensions of the given terminal. -func GetSize(fd int) (width, height int, err error) { - var dimensions [4]uint16 - - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0); err != 0 { - return -1, -1, err - } - return int(dimensions[1]), int(dimensions[0]), nil -} - -// ReadPassword reads a line of input from a terminal without local echo. This -// is commonly used for inputting passwords and other sensitive data. The slice -// returned does not include the \n. -func ReadPassword(fd int) ([]byte, error) { - var oldState syscall.Termios - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); err != 0 { - return nil, err - } - - newState := oldState - newState.Lflag &^= syscall.ECHO - newState.Lflag |= syscall.ICANON | syscall.ISIG - newState.Iflag |= syscall.ICRNL - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { - return nil, err - } - - defer func() { - syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0) - }() - - var buf [16]byte - var ret []byte - for { - n, err := syscall.Read(fd, buf[:]) - if err != nil { - return nil, err - } - if n == 0 { - if len(ret) == 0 { - return nil, io.EOF - } - break - } - if buf[n-1] == '\n' { - n-- - } - ret = append(ret, buf[:n]...) - if n < len(buf) { - break - } - } - - return ret, nil -} diff --git a/modules/crypto/ssh/terminal/util_bsd.go b/modules/crypto/ssh/terminal/util_bsd.go deleted file mode 100755 index 9c1ffd14..00000000 --- a/modules/crypto/ssh/terminal/util_bsd.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin dragonfly freebsd netbsd openbsd - -package terminal - -import "syscall" - -const ioctlReadTermios = syscall.TIOCGETA -const ioctlWriteTermios = syscall.TIOCSETA diff --git a/modules/crypto/ssh/terminal/util_linux.go b/modules/crypto/ssh/terminal/util_linux.go deleted file mode 100755 index 5883b22d..00000000 --- a/modules/crypto/ssh/terminal/util_linux.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package terminal - -// These constants are declared here, rather than importing -// them from the syscall package as some syscall packages, even -// on linux, for example gccgo, do not declare them. -const ioctlReadTermios = 0x5401 // syscall.TCGETS -const ioctlWriteTermios = 0x5402 // syscall.TCSETS diff --git a/modules/crypto/ssh/terminal/util_windows.go b/modules/crypto/ssh/terminal/util_windows.go deleted file mode 100755 index 2dd6c3d9..00000000 --- a/modules/crypto/ssh/terminal/util_windows.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build windows - -// Package terminal provides support functions for dealing with terminals, as -// commonly found on UNIX systems. -// -// Putting a terminal into raw mode is the most common requirement: -// -// oldState, err := terminal.MakeRaw(0) -// if err != nil { -// panic(err) -// } -// defer terminal.Restore(0, oldState) -package terminal - -import ( - "io" - "syscall" - "unsafe" -) - -const ( - enableLineInput = 2 - enableEchoInput = 4 - enableProcessedInput = 1 - enableWindowInput = 8 - enableMouseInput = 16 - enableInsertMode = 32 - enableQuickEditMode = 64 - enableExtendedFlags = 128 - enableAutoPosition = 256 - enableProcessedOutput = 1 - enableWrapAtEolOutput = 2 -) - -var kernel32 = syscall.NewLazyDLL("kernel32.dll") - -var ( - procGetConsoleMode = kernel32.NewProc("GetConsoleMode") - procSetConsoleMode = kernel32.NewProc("SetConsoleMode") - procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") -) - -type ( - short int16 - word uint16 - - coord struct { - x short - y short - } - smallRect struct { - left short - top short - right short - bottom short - } - consoleScreenBufferInfo struct { - size coord - cursorPosition coord - attributes word - window smallRect - maximumWindowSize coord - } -) - -type State struct { - mode uint32 -} - -// IsTerminal returns true if the given file descriptor is a terminal. -func IsTerminal(fd int) bool { - var st uint32 - r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) - return r != 0 && e == 0 -} - -// MakeRaw put the terminal connected to the given file descriptor into raw -// mode and returns the previous state of the terminal so that it can be -// restored. -func MakeRaw(fd int) (*State, error) { - var st uint32 - _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) - if e != 0 { - return nil, error(e) - } - st &^= (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput) - _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0) - if e != 0 { - return nil, error(e) - } - return &State{st}, nil -} - -// GetState returns the current state of a terminal which may be useful to -// restore the terminal after a signal. -func GetState(fd int) (*State, error) { - var st uint32 - _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) - if e != 0 { - return nil, error(e) - } - return &State{st}, nil -} - -// Restore restores the terminal connected to the given file descriptor to a -// previous state. -func Restore(fd int, state *State) error { - _, _, err := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(state.mode), 0) - return err -} - -// GetSize returns the dimensions of the given terminal. -func GetSize(fd int) (width, height int, err error) { - var info consoleScreenBufferInfo - _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&info)), 0) - if e != 0 { - return 0, 0, error(e) - } - return int(info.size.x), int(info.size.y), nil -} - -// ReadPassword reads a line of input from a terminal without local echo. This -// is commonly used for inputting passwords and other sensitive data. The slice -// returned does not include the \n. -func ReadPassword(fd int) ([]byte, error) { - var st uint32 - _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) - if e != 0 { - return nil, error(e) - } - old := st - - st &^= (enableEchoInput) - st |= (enableProcessedInput | enableLineInput | enableProcessedOutput) - _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0) - if e != 0 { - return nil, error(e) - } - - defer func() { - syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0) - }() - - var buf [16]byte - var ret []byte - for { - n, err := syscall.Read(syscall.Handle(fd), buf[:]) - if err != nil { - return nil, err - } - if n == 0 { - if len(ret) == 0 { - return nil, io.EOF - } - break - } - if buf[n-1] == '\n' { - n-- - } - if n > 0 && buf[n-1] == '\r' { - n-- - } - ret = append(ret, buf[:n]...) - if n < len(buf) { - break - } - } - - return ret, nil -} diff --git a/modules/crypto/ssh/test/agent_unix_test.go b/modules/crypto/ssh/test/agent_unix_test.go deleted file mode 100755 index f481253c..00000000 --- a/modules/crypto/ssh/test/agent_unix_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin dragonfly freebsd linux netbsd openbsd - -package test - -import ( - "bytes" - "testing" - - "golang.org/x/crypto/ssh" - "golang.org/x/crypto/ssh/agent" -) - -func TestAgentForward(t *testing.T) { - server := newServer(t) - defer server.Shutdown() - conn := server.Dial(clientConfig()) - defer conn.Close() - - keyring := agent.NewKeyring() - if err := keyring.Add(agent.AddedKey{PrivateKey: testPrivateKeys["dsa"]}); err != nil { - t.Fatalf("Error adding key: %s", err) - } - if err := keyring.Add(agent.AddedKey{ - PrivateKey: testPrivateKeys["dsa"], - ConfirmBeforeUse: true, - LifetimeSecs: 3600, - }); err != nil { - t.Fatalf("Error adding key with constraints: %s", err) - } - pub := testPublicKeys["dsa"] - - sess, err := conn.NewSession() - if err != nil { - t.Fatalf("NewSession: %v", err) - } - if err := agent.RequestAgentForwarding(sess); err != nil { - t.Fatalf("RequestAgentForwarding: %v", err) - } - - if err := agent.ForwardToAgent(conn, keyring); err != nil { - t.Fatalf("SetupForwardKeyring: %v", err) - } - out, err := sess.CombinedOutput("ssh-add -L") - if err != nil { - t.Fatalf("running ssh-add: %v, out %s", err, out) - } - key, _, _, _, err := ssh.ParseAuthorizedKey(out) - if err != nil { - t.Fatalf("ParseAuthorizedKey(%q): %v", out, err) - } - - if !bytes.Equal(key.Marshal(), pub.Marshal()) { - t.Fatalf("got key %s, want %s", ssh.MarshalAuthorizedKey(key), ssh.MarshalAuthorizedKey(pub)) - } -} diff --git a/modules/crypto/ssh/test/cert_test.go b/modules/crypto/ssh/test/cert_test.go deleted file mode 100755 index 364790f1..00000000 --- a/modules/crypto/ssh/test/cert_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin dragonfly freebsd linux netbsd openbsd - -package test - -import ( - "crypto/rand" - "testing" - - "golang.org/x/crypto/ssh" -) - -func TestCertLogin(t *testing.T) { - s := newServer(t) - defer s.Shutdown() - - // Use a key different from the default. - clientKey := testSigners["dsa"] - caAuthKey := testSigners["ecdsa"] - cert := &ssh.Certificate{ - Key: clientKey.PublicKey(), - ValidPrincipals: []string{username()}, - CertType: ssh.UserCert, - ValidBefore: ssh.CertTimeInfinity, - } - if err := cert.SignCert(rand.Reader, caAuthKey); err != nil { - t.Fatalf("SetSignature: %v", err) - } - - certSigner, err := ssh.NewCertSigner(cert, clientKey) - if err != nil { - t.Fatalf("NewCertSigner: %v", err) - } - - conf := &ssh.ClientConfig{ - User: username(), - } - conf.Auth = append(conf.Auth, ssh.PublicKeys(certSigner)) - client, err := s.TryDial(conf) - if err != nil { - t.Fatalf("TryDial: %v", err) - } - client.Close() -} diff --git a/modules/crypto/ssh/test/doc.go b/modules/crypto/ssh/test/doc.go deleted file mode 100755 index 29ad65e9..00000000 --- a/modules/crypto/ssh/test/doc.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This package contains integration tests for the -// golang.org/x/crypto/ssh package. -package test diff --git a/modules/crypto/ssh/test/forward_unix_test.go b/modules/crypto/ssh/test/forward_unix_test.go deleted file mode 100755 index 877a88cd..00000000 --- a/modules/crypto/ssh/test/forward_unix_test.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin dragonfly freebsd linux netbsd openbsd - -package test - -import ( - "bytes" - "io" - "io/ioutil" - "math/rand" - "net" - "testing" - "time" -) - -func TestPortForward(t *testing.T) { - server := newServer(t) - defer server.Shutdown() - conn := server.Dial(clientConfig()) - defer conn.Close() - - sshListener, err := conn.Listen("tcp", "localhost:0") - if err != nil { - t.Fatal(err) - } - - go func() { - sshConn, err := sshListener.Accept() - if err != nil { - t.Fatalf("listen.Accept failed: %v", err) - } - - _, err = io.Copy(sshConn, sshConn) - if err != nil && err != io.EOF { - t.Fatalf("ssh client copy: %v", err) - } - sshConn.Close() - }() - - forwardedAddr := sshListener.Addr().String() - tcpConn, err := net.Dial("tcp", forwardedAddr) - if err != nil { - t.Fatalf("TCP dial failed: %v", err) - } - - readChan := make(chan []byte) - go func() { - data, _ := ioutil.ReadAll(tcpConn) - readChan <- data - }() - - // Invent some data. - data := make([]byte, 100*1000) - for i := range data { - data[i] = byte(i % 255) - } - - var sent []byte - for len(sent) < 1000*1000 { - // Send random sized chunks - m := rand.Intn(len(data)) - n, err := tcpConn.Write(data[:m]) - if err != nil { - break - } - sent = append(sent, data[:n]...) - } - if err := tcpConn.(*net.TCPConn).CloseWrite(); err != nil { - t.Errorf("tcpConn.CloseWrite: %v", err) - } - - read := <-readChan - - if len(sent) != len(read) { - t.Fatalf("got %d bytes, want %d", len(read), len(sent)) - } - if bytes.Compare(sent, read) != 0 { - t.Fatalf("read back data does not match") - } - - if err := sshListener.Close(); err != nil { - t.Fatalf("sshListener.Close: %v", err) - } - - // Check that the forward disappeared. - tcpConn, err = net.Dial("tcp", forwardedAddr) - if err == nil { - tcpConn.Close() - t.Errorf("still listening to %s after closing", forwardedAddr) - } -} - -func TestAcceptClose(t *testing.T) { - server := newServer(t) - defer server.Shutdown() - conn := server.Dial(clientConfig()) - - sshListener, err := conn.Listen("tcp", "localhost:0") - if err != nil { - t.Fatal(err) - } - - quit := make(chan error, 1) - go func() { - for { - c, err := sshListener.Accept() - if err != nil { - quit <- err - break - } - c.Close() - } - }() - sshListener.Close() - - select { - case <-time.After(1 * time.Second): - t.Errorf("timeout: listener did not close.") - case err := <-quit: - t.Logf("quit as expected (error %v)", err) - } -} - -// Check that listeners exit if the underlying client transport dies. -func TestPortForwardConnectionClose(t *testing.T) { - server := newServer(t) - defer server.Shutdown() - conn := server.Dial(clientConfig()) - - sshListener, err := conn.Listen("tcp", "localhost:0") - if err != nil { - t.Fatal(err) - } - - quit := make(chan error, 1) - go func() { - for { - c, err := sshListener.Accept() - if err != nil { - quit <- err - break - } - c.Close() - } - }() - - // It would be even nicer if we closed the server side, but it - // is more involved as the fd for that side is dup()ed. - server.clientConn.Close() - - select { - case <-time.After(1 * time.Second): - t.Errorf("timeout: listener did not close.") - case err := <-quit: - t.Logf("quit as expected (error %v)", err) - } -} diff --git a/modules/crypto/ssh/test/session_test.go b/modules/crypto/ssh/test/session_test.go deleted file mode 100755 index c0e714ba..00000000 --- a/modules/crypto/ssh/test/session_test.go +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !windows - -package test - -// Session functional tests. - -import ( - "bytes" - "errors" - "io" - "strings" - "testing" - - "golang.org/x/crypto/ssh" -) - -func TestRunCommandSuccess(t *testing.T) { - server := newServer(t) - defer server.Shutdown() - conn := server.Dial(clientConfig()) - defer conn.Close() - - session, err := conn.NewSession() - if err != nil { - t.Fatalf("session failed: %v", err) - } - defer session.Close() - err = session.Run("true") - if err != nil { - t.Fatalf("session failed: %v", err) - } -} - -func TestHostKeyCheck(t *testing.T) { - server := newServer(t) - defer server.Shutdown() - - conf := clientConfig() - hostDB := hostKeyDB() - conf.HostKeyCallback = hostDB.Check - - // change the keys. - hostDB.keys[ssh.KeyAlgoRSA][25]++ - hostDB.keys[ssh.KeyAlgoDSA][25]++ - hostDB.keys[ssh.KeyAlgoECDSA256][25]++ - - conn, err := server.TryDial(conf) - if err == nil { - conn.Close() - t.Fatalf("dial should have failed.") - } else if !strings.Contains(err.Error(), "host key mismatch") { - t.Fatalf("'host key mismatch' not found in %v", err) - } -} - -func TestRunCommandStdin(t *testing.T) { - server := newServer(t) - defer server.Shutdown() - conn := server.Dial(clientConfig()) - defer conn.Close() - - session, err := conn.NewSession() - if err != nil { - t.Fatalf("session failed: %v", err) - } - defer session.Close() - - r, w := io.Pipe() - defer r.Close() - defer w.Close() - session.Stdin = r - - err = session.Run("true") - if err != nil { - t.Fatalf("session failed: %v", err) - } -} - -func TestRunCommandStdinError(t *testing.T) { - server := newServer(t) - defer server.Shutdown() - conn := server.Dial(clientConfig()) - defer conn.Close() - - session, err := conn.NewSession() - if err != nil { - t.Fatalf("session failed: %v", err) - } - defer session.Close() - - r, w := io.Pipe() - defer r.Close() - session.Stdin = r - pipeErr := errors.New("closing write end of pipe") - w.CloseWithError(pipeErr) - - err = session.Run("true") - if err != pipeErr { - t.Fatalf("expected %v, found %v", pipeErr, err) - } -} - -func TestRunCommandFailed(t *testing.T) { - server := newServer(t) - defer server.Shutdown() - conn := server.Dial(clientConfig()) - defer conn.Close() - - session, err := conn.NewSession() - if err != nil { - t.Fatalf("session failed: %v", err) - } - defer session.Close() - err = session.Run(`bash -c "kill -9 $$"`) - if err == nil { - t.Fatalf("session succeeded: %v", err) - } -} - -func TestRunCommandWeClosed(t *testing.T) { - server := newServer(t) - defer server.Shutdown() - conn := server.Dial(clientConfig()) - defer conn.Close() - - session, err := conn.NewSession() - if err != nil { - t.Fatalf("session failed: %v", err) - } - err = session.Shell() - if err != nil { - t.Fatalf("shell failed: %v", err) - } - err = session.Close() - if err != nil { - t.Fatalf("shell failed: %v", err) - } -} - -func TestFuncLargeRead(t *testing.T) { - server := newServer(t) - defer server.Shutdown() - conn := server.Dial(clientConfig()) - defer conn.Close() - - session, err := conn.NewSession() - if err != nil { - t.Fatalf("unable to create new session: %s", err) - } - - stdout, err := session.StdoutPipe() - if err != nil { - t.Fatalf("unable to acquire stdout pipe: %s", err) - } - - err = session.Start("dd if=/dev/urandom bs=2048 count=1024") - if err != nil { - t.Fatalf("unable to execute remote command: %s", err) - } - - buf := new(bytes.Buffer) - n, err := io.Copy(buf, stdout) - if err != nil { - t.Fatalf("error reading from remote stdout: %s", err) - } - - if n != 2048*1024 { - t.Fatalf("Expected %d bytes but read only %d from remote command", 2048, n) - } -} - -func TestKeyChange(t *testing.T) { - server := newServer(t) - defer server.Shutdown() - conf := clientConfig() - hostDB := hostKeyDB() - conf.HostKeyCallback = hostDB.Check - conf.RekeyThreshold = 1024 - conn := server.Dial(conf) - defer conn.Close() - - for i := 0; i < 4; i++ { - session, err := conn.NewSession() - if err != nil { - t.Fatalf("unable to create new session: %s", err) - } - - stdout, err := session.StdoutPipe() - if err != nil { - t.Fatalf("unable to acquire stdout pipe: %s", err) - } - - err = session.Start("dd if=/dev/urandom bs=1024 count=1") - if err != nil { - t.Fatalf("unable to execute remote command: %s", err) - } - buf := new(bytes.Buffer) - n, err := io.Copy(buf, stdout) - if err != nil { - t.Fatalf("error reading from remote stdout: %s", err) - } - - want := int64(1024) - if n != want { - t.Fatalf("Expected %d bytes but read only %d from remote command", want, n) - } - } - - if changes := hostDB.checkCount; changes < 4 { - t.Errorf("got %d key changes, want 4", changes) - } -} - -func TestInvalidTerminalMode(t *testing.T) { - server := newServer(t) - defer server.Shutdown() - conn := server.Dial(clientConfig()) - defer conn.Close() - - session, err := conn.NewSession() - if err != nil { - t.Fatalf("session failed: %v", err) - } - defer session.Close() - - if err = session.RequestPty("vt100", 80, 40, ssh.TerminalModes{255: 1984}); err == nil { - t.Fatalf("req-pty failed: successful request with invalid mode") - } -} - -func TestValidTerminalMode(t *testing.T) { - server := newServer(t) - defer server.Shutdown() - conn := server.Dial(clientConfig()) - defer conn.Close() - - session, err := conn.NewSession() - if err != nil { - t.Fatalf("session failed: %v", err) - } - defer session.Close() - - stdout, err := session.StdoutPipe() - if err != nil { - t.Fatalf("unable to acquire stdout pipe: %s", err) - } - - stdin, err := session.StdinPipe() - if err != nil { - t.Fatalf("unable to acquire stdin pipe: %s", err) - } - - tm := ssh.TerminalModes{ssh.ECHO: 0} - if err = session.RequestPty("xterm", 80, 40, tm); err != nil { - t.Fatalf("req-pty failed: %s", err) - } - - err = session.Shell() - if err != nil { - t.Fatalf("session failed: %s", err) - } - - stdin.Write([]byte("stty -a && exit\n")) - - var buf bytes.Buffer - if _, err := io.Copy(&buf, stdout); err != nil { - t.Fatalf("reading failed: %s", err) - } - - if sttyOutput := buf.String(); !strings.Contains(sttyOutput, "-echo ") { - t.Fatalf("terminal mode failure: expected -echo in stty output, got %s", sttyOutput) - } -} - -func TestCiphers(t *testing.T) { - var config ssh.Config - config.SetDefaults() - cipherOrder := config.Ciphers - // This cipher will not be tested when commented out in cipher.go it will - // fallback to the next available as per line 292. - cipherOrder = append(cipherOrder, "aes128-cbc") - - for _, ciph := range cipherOrder { - server := newServer(t) - defer server.Shutdown() - conf := clientConfig() - conf.Ciphers = []string{ciph} - // Don't fail if sshd doesnt have the cipher. - conf.Ciphers = append(conf.Ciphers, cipherOrder...) - conn, err := server.TryDial(conf) - if err == nil { - conn.Close() - } else { - t.Fatalf("failed for cipher %q", ciph) - } - } -} - -func TestMACs(t *testing.T) { - var config ssh.Config - config.SetDefaults() - macOrder := config.MACs - - for _, mac := range macOrder { - server := newServer(t) - defer server.Shutdown() - conf := clientConfig() - conf.MACs = []string{mac} - // Don't fail if sshd doesnt have the MAC. - conf.MACs = append(conf.MACs, macOrder...) - if conn, err := server.TryDial(conf); err == nil { - conn.Close() - } else { - t.Fatalf("failed for MAC %q", mac) - } - } -} - -func TestKeyExchanges(t *testing.T) { - var config ssh.Config - config.SetDefaults() - kexOrder := config.KeyExchanges - for _, kex := range kexOrder { - server := newServer(t) - defer server.Shutdown() - conf := clientConfig() - // Don't fail if sshd doesnt have the kex. - conf.KeyExchanges = append([]string{kex}, kexOrder...) - conn, err := server.TryDial(conf) - if err == nil { - conn.Close() - } else { - t.Errorf("failed for kex %q", kex) - } - } -} diff --git a/modules/crypto/ssh/test/tcpip_test.go b/modules/crypto/ssh/test/tcpip_test.go deleted file mode 100755 index a2eb9358..00000000 --- a/modules/crypto/ssh/test/tcpip_test.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !windows - -package test - -// direct-tcpip functional tests - -import ( - "io" - "net" - "testing" -) - -func TestDial(t *testing.T) { - server := newServer(t) - defer server.Shutdown() - sshConn := server.Dial(clientConfig()) - defer sshConn.Close() - - l, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatalf("Listen: %v", err) - } - defer l.Close() - - go func() { - for { - c, err := l.Accept() - if err != nil { - break - } - - io.WriteString(c, c.RemoteAddr().String()) - c.Close() - } - }() - - conn, err := sshConn.Dial("tcp", l.Addr().String()) - if err != nil { - t.Fatalf("Dial: %v", err) - } - defer conn.Close() -} diff --git a/modules/crypto/ssh/test/test_unix_test.go b/modules/crypto/ssh/test/test_unix_test.go deleted file mode 100755 index f1fc50b2..00000000 --- a/modules/crypto/ssh/test/test_unix_test.go +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin dragonfly freebsd linux netbsd openbsd plan9 - -package test - -// functional test harness for unix. - -import ( - "bytes" - "fmt" - "io/ioutil" - "log" - "net" - "os" - "os/exec" - "os/user" - "path/filepath" - "testing" - "text/template" - - "golang.org/x/crypto/ssh" - "golang.org/x/crypto/ssh/testdata" -) - -const sshd_config = ` -Protocol 2 -HostKey {{.Dir}}/id_rsa -HostKey {{.Dir}}/id_dsa -HostKey {{.Dir}}/id_ecdsa -Pidfile {{.Dir}}/sshd.pid -#UsePrivilegeSeparation no -KeyRegenerationInterval 3600 -ServerKeyBits 768 -SyslogFacility AUTH -LogLevel DEBUG2 -LoginGraceTime 120 -PermitRootLogin no -StrictModes no -RSAAuthentication yes -PubkeyAuthentication yes -AuthorizedKeysFile {{.Dir}}/id_user.pub -TrustedUserCAKeys {{.Dir}}/id_ecdsa.pub -IgnoreRhosts yes -RhostsRSAAuthentication no -HostbasedAuthentication no -` - -var configTmpl = template.Must(template.New("").Parse(sshd_config)) - -type server struct { - t *testing.T - cleanup func() // executed during Shutdown - configfile string - cmd *exec.Cmd - output bytes.Buffer // holds stderr from sshd process - - // Client half of the network connection. - clientConn net.Conn -} - -func username() string { - var username string - if user, err := user.Current(); err == nil { - username = user.Username - } else { - // user.Current() currently requires cgo. If an error is - // returned attempt to get the username from the environment. - log.Printf("user.Current: %v; falling back on $USER", err) - username = os.Getenv("USER") - } - if username == "" { - panic("Unable to get username") - } - return username -} - -type storedHostKey struct { - // keys map from an algorithm string to binary key data. - keys map[string][]byte - - // checkCount counts the Check calls. Used for testing - // rekeying. - checkCount int -} - -func (k *storedHostKey) Add(key ssh.PublicKey) { - if k.keys == nil { - k.keys = map[string][]byte{} - } - k.keys[key.Type()] = key.Marshal() -} - -func (k *storedHostKey) Check(addr string, remote net.Addr, key ssh.PublicKey) error { - k.checkCount++ - algo := key.Type() - - if k.keys == nil || bytes.Compare(key.Marshal(), k.keys[algo]) != 0 { - return fmt.Errorf("host key mismatch. Got %q, want %q", key, k.keys[algo]) - } - return nil -} - -func hostKeyDB() *storedHostKey { - keyChecker := &storedHostKey{} - keyChecker.Add(testPublicKeys["ecdsa"]) - keyChecker.Add(testPublicKeys["rsa"]) - keyChecker.Add(testPublicKeys["dsa"]) - return keyChecker -} - -func clientConfig() *ssh.ClientConfig { - config := &ssh.ClientConfig{ - User: username(), - Auth: []ssh.AuthMethod{ - ssh.PublicKeys(testSigners["user"]), - }, - HostKeyCallback: hostKeyDB().Check, - } - return config -} - -// unixConnection creates two halves of a connected net.UnixConn. It -// is used for connecting the Go SSH client with sshd without opening -// ports. -func unixConnection() (*net.UnixConn, *net.UnixConn, error) { - dir, err := ioutil.TempDir("", "unixConnection") - if err != nil { - return nil, nil, err - } - defer os.Remove(dir) - - addr := filepath.Join(dir, "ssh") - listener, err := net.Listen("unix", addr) - if err != nil { - return nil, nil, err - } - defer listener.Close() - c1, err := net.Dial("unix", addr) - if err != nil { - return nil, nil, err - } - - c2, err := listener.Accept() - if err != nil { - c1.Close() - return nil, nil, err - } - - return c1.(*net.UnixConn), c2.(*net.UnixConn), nil -} - -func (s *server) TryDial(config *ssh.ClientConfig) (*ssh.Client, error) { - sshd, err := exec.LookPath("sshd") - if err != nil { - s.t.Skipf("skipping test: %v", err) - } - - c1, c2, err := unixConnection() - if err != nil { - s.t.Fatalf("unixConnection: %v", err) - } - - s.cmd = exec.Command(sshd, "-f", s.configfile, "-i", "-e") - f, err := c2.File() - if err != nil { - s.t.Fatalf("UnixConn.File: %v", err) - } - defer f.Close() - s.cmd.Stdin = f - s.cmd.Stdout = f - s.cmd.Stderr = &s.output - if err := s.cmd.Start(); err != nil { - s.t.Fail() - s.Shutdown() - s.t.Fatalf("s.cmd.Start: %v", err) - } - s.clientConn = c1 - conn, chans, reqs, err := ssh.NewClientConn(c1, "", config) - if err != nil { - return nil, err - } - return ssh.NewClient(conn, chans, reqs), nil -} - -func (s *server) Dial(config *ssh.ClientConfig) *ssh.Client { - conn, err := s.TryDial(config) - if err != nil { - s.t.Fail() - s.Shutdown() - s.t.Fatalf("ssh.Client: %v", err) - } - return conn -} - -func (s *server) Shutdown() { - if s.cmd != nil && s.cmd.Process != nil { - // Don't check for errors; if it fails it's most - // likely "os: process already finished", and we don't - // care about that. Use os.Interrupt, so child - // processes are killed too. - s.cmd.Process.Signal(os.Interrupt) - s.cmd.Wait() - } - if s.t.Failed() { - // log any output from sshd process - s.t.Logf("sshd: %s", s.output.String()) - } - s.cleanup() -} - -func writeFile(path string, contents []byte) { - f, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600) - if err != nil { - panic(err) - } - defer f.Close() - if _, err := f.Write(contents); err != nil { - panic(err) - } -} - -// newServer returns a new mock ssh server. -func newServer(t *testing.T) *server { - if testing.Short() { - t.Skip("skipping test due to -short") - } - dir, err := ioutil.TempDir("", "sshtest") - if err != nil { - t.Fatal(err) - } - f, err := os.Create(filepath.Join(dir, "sshd_config")) - if err != nil { - t.Fatal(err) - } - err = configTmpl.Execute(f, map[string]string{ - "Dir": dir, - }) - if err != nil { - t.Fatal(err) - } - f.Close() - - for k, v := range testdata.PEMBytes { - filename := "id_" + k - writeFile(filepath.Join(dir, filename), v) - writeFile(filepath.Join(dir, filename+".pub"), ssh.MarshalAuthorizedKey(testPublicKeys[k])) - } - - return &server{ - t: t, - configfile: f.Name(), - cleanup: func() { - if err := os.RemoveAll(dir); err != nil { - t.Error(err) - } - }, - } -} diff --git a/modules/crypto/ssh/test/testdata_test.go b/modules/crypto/ssh/test/testdata_test.go deleted file mode 100755 index ae48c751..00000000 --- a/modules/crypto/ssh/test/testdata_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// IMPLEMENTOR NOTE: To avoid a package loop, this file is in three places: -// ssh/, ssh/agent, and ssh/test/. It should be kept in sync across all three -// instances. - -package test - -import ( - "crypto/rand" - "fmt" - - "golang.org/x/crypto/ssh" - "golang.org/x/crypto/ssh/testdata" -) - -var ( - testPrivateKeys map[string]interface{} - testSigners map[string]ssh.Signer - testPublicKeys map[string]ssh.PublicKey -) - -func init() { - var err error - - n := len(testdata.PEMBytes) - testPrivateKeys = make(map[string]interface{}, n) - testSigners = make(map[string]ssh.Signer, n) - testPublicKeys = make(map[string]ssh.PublicKey, n) - for t, k := range testdata.PEMBytes { - testPrivateKeys[t], err = ssh.ParseRawPrivateKey(k) - if err != nil { - panic(fmt.Sprintf("Unable to parse test key %s: %v", t, err)) - } - testSigners[t], err = ssh.NewSignerFromKey(testPrivateKeys[t]) - if err != nil { - panic(fmt.Sprintf("Unable to create signer for test key %s: %v", t, err)) - } - testPublicKeys[t] = testSigners[t].PublicKey() - } - - // Create a cert and sign it for use in tests. - testCert := &ssh.Certificate{ - Nonce: []byte{}, // To pass reflect.DeepEqual after marshal & parse, this must be non-nil - ValidPrincipals: []string{"gopher1", "gopher2"}, // increases test coverage - ValidAfter: 0, // unix epoch - ValidBefore: ssh.CertTimeInfinity, // The end of currently representable time. - Reserved: []byte{}, // To pass reflect.DeepEqual after marshal & parse, this must be non-nil - Key: testPublicKeys["ecdsa"], - SignatureKey: testPublicKeys["rsa"], - Permissions: ssh.Permissions{ - CriticalOptions: map[string]string{}, - Extensions: map[string]string{}, - }, - } - testCert.SignCert(rand.Reader, testSigners["rsa"]) - testPrivateKeys["cert"] = testPrivateKeys["ecdsa"] - testSigners["cert"], err = ssh.NewCertSigner(testCert, testSigners["ecdsa"]) - if err != nil { - panic(fmt.Sprintf("Unable to create certificate signer: %v", err)) - } -} diff --git a/modules/crypto/ssh/testdata/doc.go b/modules/crypto/ssh/testdata/doc.go deleted file mode 100755 index ae7bd8b8..00000000 --- a/modules/crypto/ssh/testdata/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This package contains test data shared between the various subpackages of -// the golang.org/x/crypto/ssh package. Under no circumstance should -// this data be used for production code. -package testdata diff --git a/modules/crypto/ssh/testdata/keys.go b/modules/crypto/ssh/testdata/keys.go deleted file mode 100755 index 5ff1c0e0..00000000 --- a/modules/crypto/ssh/testdata/keys.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package testdata - -var PEMBytes = map[string][]byte{ - "dsa": []byte(`-----BEGIN DSA PRIVATE KEY----- -MIIBuwIBAAKBgQD6PDSEyXiI9jfNs97WuM46MSDCYlOqWw80ajN16AohtBncs1YB -lHk//dQOvCYOsYaE+gNix2jtoRjwXhDsc25/IqQbU1ahb7mB8/rsaILRGIbA5WH3 -EgFtJmXFovDz3if6F6TzvhFpHgJRmLYVR8cqsezL3hEZOvvs2iH7MorkxwIVAJHD -nD82+lxh2fb4PMsIiaXudAsBAoGAQRf7Q/iaPRn43ZquUhd6WwvirqUj+tkIu6eV -2nZWYmXLlqFQKEy4Tejl7Wkyzr2OSYvbXLzo7TNxLKoWor6ips0phYPPMyXld14r -juhT24CrhOzuLMhDduMDi032wDIZG4Y+K7ElU8Oufn8Sj5Wge8r6ANmmVgmFfynr -FhdYCngCgYEA3ucGJ93/Mx4q4eKRDxcWD3QzWyqpbRVRRV1Vmih9Ha/qC994nJFz -DQIdjxDIT2Rk2AGzMqFEB68Zc3O+Wcsmz5eWWzEwFxaTwOGWTyDqsDRLm3fD+QYj -nOwuxb0Kce+gWI8voWcqC9cyRm09jGzu2Ab3Bhtpg8JJ8L7gS3MRZK4CFEx4UAfY -Fmsr0W6fHB9nhS4/UXM8 ------END DSA PRIVATE KEY----- -`), - "ecdsa": []byte(`-----BEGIN EC PRIVATE KEY----- -MHcCAQEEINGWx0zo6fhJ/0EAfrPzVFyFC9s18lBt3cRoEDhS3ARooAoGCCqGSM49 -AwEHoUQDQgAEi9Hdw6KvZcWxfg2IDhA7UkpDtzzt6ZqJXSsFdLd+Kx4S3Sx4cVO+ -6/ZOXRnPmNAlLUqjShUsUBBngG0u2fqEqA== ------END EC PRIVATE KEY----- -`), - "rsa": []byte(`-----BEGIN RSA PRIVATE KEY----- -MIIBOwIBAAJBALdGZxkXDAjsYk10ihwU6Id2KeILz1TAJuoq4tOgDWxEEGeTrcld -r/ZwVaFzjWzxaf6zQIJbfaSEAhqD5yo72+sCAwEAAQJBAK8PEVU23Wj8mV0QjwcJ -tZ4GcTUYQL7cF4+ezTCE9a1NrGnCP2RuQkHEKxuTVrxXt+6OF15/1/fuXnxKjmJC -nxkCIQDaXvPPBi0c7vAxGwNY9726x01/dNbHCE0CBtcotobxpwIhANbbQbh3JHVW -2haQh4fAG5mhesZKAGcxTyv4mQ7uMSQdAiAj+4dzMpJWdSzQ+qGHlHMIBvVHLkqB -y2VdEyF7DPCZewIhAI7GOI/6LDIFOvtPo6Bj2nNmyQ1HU6k/LRtNIXi4c9NJAiAr -rrxx26itVhJmcvoUhOjwuzSlP2bE5VHAvkGB352YBg== ------END RSA PRIVATE KEY----- -`), - "user": []byte(`-----BEGIN EC PRIVATE KEY----- -MHcCAQEEILYCAeq8f7V4vSSypRw7pxy8yz3V5W4qg8kSC3zJhqpQoAoGCCqGSM49 -AwEHoUQDQgAEYcO2xNKiRUYOLEHM7VYAp57HNyKbOdYtHD83Z4hzNPVC4tM5mdGD -PLL8IEwvYu2wq+lpXfGQnNMbzYf9gspG0w== ------END EC PRIVATE KEY----- -`), -} diff --git a/modules/crypto/ssh/testdata_test.go b/modules/crypto/ssh/testdata_test.go deleted file mode 100755 index ca43fc6d..00000000 --- a/modules/crypto/ssh/testdata_test.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// IMPLEMENTOR NOTE: To avoid a package loop, this file is in three places: -// ssh/, ssh/agent, and ssh/test/. It should be kept in sync across all three -// instances. - -package ssh - -import ( - "crypto/rand" - "fmt" - - "github.com/gogits/gogs/modules/crypto/ssh/testdata" -) - -var ( - testPrivateKeys map[string]interface{} - testSigners map[string]Signer - testPublicKeys map[string]PublicKey -) - -func init() { - var err error - - n := len(testdata.PEMBytes) - testPrivateKeys = make(map[string]interface{}, n) - testSigners = make(map[string]Signer, n) - testPublicKeys = make(map[string]PublicKey, n) - for t, k := range testdata.PEMBytes { - testPrivateKeys[t], err = ParseRawPrivateKey(k) - if err != nil { - panic(fmt.Sprintf("Unable to parse test key %s: %v", t, err)) - } - testSigners[t], err = NewSignerFromKey(testPrivateKeys[t]) - if err != nil { - panic(fmt.Sprintf("Unable to create signer for test key %s: %v", t, err)) - } - testPublicKeys[t] = testSigners[t].PublicKey() - } - - // Create a cert and sign it for use in tests. - testCert := &Certificate{ - Nonce: []byte{}, // To pass reflect.DeepEqual after marshal & parse, this must be non-nil - ValidPrincipals: []string{"gopher1", "gopher2"}, // increases test coverage - ValidAfter: 0, // unix epoch - ValidBefore: CertTimeInfinity, // The end of currently representable time. - Reserved: []byte{}, // To pass reflect.DeepEqual after marshal & parse, this must be non-nil - Key: testPublicKeys["ecdsa"], - SignatureKey: testPublicKeys["rsa"], - Permissions: Permissions{ - CriticalOptions: map[string]string{}, - Extensions: map[string]string{}, - }, - } - testCert.SignCert(rand.Reader, testSigners["rsa"]) - testPrivateKeys["cert"] = testPrivateKeys["ecdsa"] - testSigners["cert"], err = NewCertSigner(testCert, testSigners["ecdsa"]) - if err != nil { - panic(fmt.Sprintf("Unable to create certificate signer: %v", err)) - } -} diff --git a/modules/crypto/ssh/transport.go b/modules/crypto/ssh/transport.go deleted file mode 100755 index 8351d378..00000000 --- a/modules/crypto/ssh/transport.go +++ /dev/null @@ -1,332 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bufio" - "errors" - "io" -) - -const ( - gcmCipherID = "aes128-gcm@openssh.com" - aes128cbcID = "aes128-cbc" -) - -// packetConn represents a transport that implements packet based -// operations. -type packetConn interface { - // Encrypt and send a packet of data to the remote peer. - writePacket(packet []byte) error - - // Read a packet from the connection - readPacket() ([]byte, error) - - // Close closes the write-side of the connection. - Close() error -} - -// transport is the keyingTransport that implements the SSH packet -// protocol. -type transport struct { - reader connectionState - writer connectionState - - bufReader *bufio.Reader - bufWriter *bufio.Writer - rand io.Reader - - io.Closer - - // Initial H used for the session ID. Once assigned this does - // not change, even during subsequent key exchanges. - sessionID []byte -} - -// getSessionID returns the ID of the SSH connection. The return value -// should not be modified. -func (t *transport) getSessionID() []byte { - if t.sessionID == nil { - panic("session ID not set yet") - } - return t.sessionID -} - -// packetCipher represents a combination of SSH encryption/MAC -// protocol. A single instance should be used for one direction only. -type packetCipher interface { - // writePacket encrypts the packet and writes it to w. The - // contents of the packet are generally scrambled. - writePacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error - - // readPacket reads and decrypts a packet of data. The - // returned packet may be overwritten by future calls of - // readPacket. - readPacket(seqnum uint32, r io.Reader) ([]byte, error) -} - -// connectionState represents one side (read or write) of the -// connection. This is necessary because each direction has its own -// keys, and can even have its own algorithms -type connectionState struct { - packetCipher - seqNum uint32 - dir direction - pendingKeyChange chan packetCipher -} - -// prepareKeyChange sets up key material for a keychange. The key changes in -// both directions are triggered by reading and writing a msgNewKey packet -// respectively. -func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error { - if t.sessionID == nil { - t.sessionID = kexResult.H - } - - kexResult.SessionID = t.sessionID - - if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil { - return err - } else { - t.reader.pendingKeyChange <- ciph - } - - if ciph, err := newPacketCipher(t.writer.dir, algs.w, kexResult); err != nil { - return err - } else { - t.writer.pendingKeyChange <- ciph - } - - return nil -} - -// Read and decrypt next packet. -func (t *transport) readPacket() ([]byte, error) { - return t.reader.readPacket(t.bufReader) -} - -func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { - packet, err := s.packetCipher.readPacket(s.seqNum, r) - s.seqNum++ - if err == nil && len(packet) == 0 { - err = errors.New("ssh: zero length packet") - } - - if len(packet) > 0 && packet[0] == msgNewKeys { - select { - case cipher := <-s.pendingKeyChange: - s.packetCipher = cipher - default: - return nil, errors.New("ssh: got bogus newkeys message.") - } - } - - // The packet may point to an internal buffer, so copy the - // packet out here. - fresh := make([]byte, len(packet)) - copy(fresh, packet) - - return fresh, err -} - -func (t *transport) writePacket(packet []byte) error { - return t.writer.writePacket(t.bufWriter, t.rand, packet) -} - -func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error { - changeKeys := len(packet) > 0 && packet[0] == msgNewKeys - - err := s.packetCipher.writePacket(s.seqNum, w, rand, packet) - if err != nil { - return err - } - if err = w.Flush(); err != nil { - return err - } - s.seqNum++ - if changeKeys { - select { - case cipher := <-s.pendingKeyChange: - s.packetCipher = cipher - default: - panic("ssh: no key material for msgNewKeys") - } - } - return err -} - -func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transport { - t := &transport{ - bufReader: bufio.NewReader(rwc), - bufWriter: bufio.NewWriter(rwc), - rand: rand, - reader: connectionState{ - packetCipher: &streamPacketCipher{cipher: noneCipher{}}, - pendingKeyChange: make(chan packetCipher, 1), - }, - writer: connectionState{ - packetCipher: &streamPacketCipher{cipher: noneCipher{}}, - pendingKeyChange: make(chan packetCipher, 1), - }, - Closer: rwc, - } - if isClient { - t.reader.dir = serverKeys - t.writer.dir = clientKeys - } else { - t.reader.dir = clientKeys - t.writer.dir = serverKeys - } - - return t -} - -type direction struct { - ivTag []byte - keyTag []byte - macKeyTag []byte -} - -var ( - serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}} - clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}} -) - -// generateKeys generates key material for IV, MAC and encryption. -func generateKeys(d direction, algs directionAlgorithms, kex *kexResult) (iv, key, macKey []byte) { - cipherMode := cipherModes[algs.Cipher] - macMode := macModes[algs.MAC] - - iv = make([]byte, cipherMode.ivSize) - key = make([]byte, cipherMode.keySize) - macKey = make([]byte, macMode.keySize) - - generateKeyMaterial(iv, d.ivTag, kex) - generateKeyMaterial(key, d.keyTag, kex) - generateKeyMaterial(macKey, d.macKeyTag, kex) - return -} - -// setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as -// described in RFC 4253, section 6.4. direction should either be serverKeys -// (to setup server->client keys) or clientKeys (for client->server keys). -func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) { - iv, key, macKey := generateKeys(d, algs, kex) - - if algs.Cipher == gcmCipherID { - return newGCMCipher(iv, key, macKey) - } - - if algs.Cipher == aes128cbcID { - return newAESCBCCipher(iv, key, macKey, algs) - } - - c := &streamPacketCipher{ - mac: macModes[algs.MAC].new(macKey), - } - c.macResult = make([]byte, c.mac.Size()) - - var err error - c.cipher, err = cipherModes[algs.Cipher].createStream(key, iv) - if err != nil { - return nil, err - } - - return c, nil -} - -// generateKeyMaterial fills out with key material generated from tag, K, H -// and sessionId, as specified in RFC 4253, section 7.2. -func generateKeyMaterial(out, tag []byte, r *kexResult) { - var digestsSoFar []byte - - h := r.Hash.New() - for len(out) > 0 { - h.Reset() - h.Write(r.K) - h.Write(r.H) - - if len(digestsSoFar) == 0 { - h.Write(tag) - h.Write(r.SessionID) - } else { - h.Write(digestsSoFar) - } - - digest := h.Sum(nil) - n := copy(out, digest) - out = out[n:] - if len(out) > 0 { - digestsSoFar = append(digestsSoFar, digest...) - } - } -} - -const packageVersion = "SSH-2.0-Go" - -// Sends and receives a version line. The versionLine string should -// be US ASCII, start with "SSH-2.0-", and should not include a -// newline. exchangeVersions returns the other side's version line. -func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err error) { - // Contrary to the RFC, we do not ignore lines that don't - // start with "SSH-2.0-" to make the library usable with - // nonconforming servers. - for _, c := range versionLine { - // The spec disallows non US-ASCII chars, and - // specifically forbids null chars. - if c < 32 { - return nil, errors.New("ssh: junk character in version line") - } - } - if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil { - return - } - - them, err = readVersion(rw) - return them, err -} - -// maxVersionStringBytes is the maximum number of bytes that we'll -// accept as a version string. RFC 4253 section 4.2 limits this at 255 -// chars -const maxVersionStringBytes = 255 - -// Read version string as specified by RFC 4253, section 4.2. -func readVersion(r io.Reader) ([]byte, error) { - versionString := make([]byte, 0, 64) - var ok bool - var buf [1]byte - - for len(versionString) < maxVersionStringBytes { - _, err := io.ReadFull(r, buf[:]) - if err != nil { - return nil, err - } - // The RFC says that the version should be terminated with \r\n - // but several SSH servers actually only send a \n. - if buf[0] == '\n' { - ok = true - break - } - - // non ASCII chars are disallowed, but we are lenient, - // since Go doesn't use null-terminated strings. - - // The RFC allows a comment after a space, however, - // all of it (version and comments) goes into the - // session hash. - versionString = append(versionString, buf[0]) - } - - if !ok { - return nil, errors.New("ssh: overflow reading version string") - } - - // There might be a '\r' on the end which we should remove. - if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' { - versionString = versionString[:len(versionString)-1] - } - return versionString, nil -} diff --git a/modules/crypto/ssh/transport_test.go b/modules/crypto/ssh/transport_test.go deleted file mode 100755 index 92d83abf..00000000 --- a/modules/crypto/ssh/transport_test.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssh - -import ( - "bytes" - "crypto/rand" - "encoding/binary" - "strings" - "testing" -) - -func TestReadVersion(t *testing.T) { - longversion := strings.Repeat("SSH-2.0-bla", 50)[:253] - cases := map[string]string{ - "SSH-2.0-bla\r\n": "SSH-2.0-bla", - "SSH-2.0-bla\n": "SSH-2.0-bla", - longversion + "\r\n": longversion, - } - - for in, want := range cases { - result, err := readVersion(bytes.NewBufferString(in)) - if err != nil { - t.Errorf("readVersion(%q): %s", in, err) - } - got := string(result) - if got != want { - t.Errorf("got %q, want %q", got, want) - } - } -} - -func TestReadVersionError(t *testing.T) { - longversion := strings.Repeat("SSH-2.0-bla", 50)[:253] - cases := []string{ - longversion + "too-long\r\n", - } - for _, in := range cases { - if _, err := readVersion(bytes.NewBufferString(in)); err == nil { - t.Errorf("readVersion(%q) should have failed", in) - } - } -} - -func TestExchangeVersionsBasic(t *testing.T) { - v := "SSH-2.0-bla" - buf := bytes.NewBufferString(v + "\r\n") - them, err := exchangeVersions(buf, []byte("xyz")) - if err != nil { - t.Errorf("exchangeVersions: %v", err) - } - - if want := "SSH-2.0-bla"; string(them) != want { - t.Errorf("got %q want %q for our version", them, want) - } -} - -func TestExchangeVersions(t *testing.T) { - cases := []string{ - "not\x000allowed", - "not allowed\n", - } - for _, c := range cases { - buf := bytes.NewBufferString("SSH-2.0-bla\r\n") - if _, err := exchangeVersions(buf, []byte(c)); err == nil { - t.Errorf("exchangeVersions(%q): should have failed", c) - } - } -} - -type closerBuffer struct { - bytes.Buffer -} - -func (b *closerBuffer) Close() error { - return nil -} - -func TestTransportMaxPacketWrite(t *testing.T) { - buf := &closerBuffer{} - tr := newTransport(buf, rand.Reader, true) - huge := make([]byte, maxPacket+1) - err := tr.writePacket(huge) - if err == nil { - t.Errorf("transport accepted write for a huge packet.") - } -} - -func TestTransportMaxPacketReader(t *testing.T) { - var header [5]byte - huge := make([]byte, maxPacket+128) - binary.BigEndian.PutUint32(header[0:], uint32(len(huge))) - // padding. - header[4] = 0 - - buf := &closerBuffer{} - buf.Write(header[:]) - buf.Write(huge) - - tr := newTransport(buf, rand.Reader, true) - _, err := tr.readPacket() - if err == nil { - t.Errorf("transport succeeded reading huge packet.") - } else if !strings.Contains(err.Error(), "large") { - t.Errorf("got %q, should mention %q", err.Error(), "large") - } -} diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 65c5d0fb..a7726722 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -22,7 +22,6 @@ import ( "github.com/gogits/gogs/modules/bindata" "github.com/gogits/gogs/modules/log" - // "github.com/gogits/gogs/modules/ssh" "github.com/gogits/gogs/modules/user" ) @@ -51,6 +50,7 @@ var ( AppName string AppUrl string AppSubUrl string + AppPath string AppDataPath = "data" // Server settings. @@ -58,8 +58,9 @@ var ( Domain string HttpAddr, HttpPort string DisableSSH bool - SSHPort int + StartSSHServer bool SSHDomain string + SSHPort int OfflineMode bool DisableRouterLog bool CertFile, KeyFile string @@ -196,21 +197,27 @@ func DateLang(lang string) string { return "en" } -func init() { - IsWindows = runtime.GOOS == "windows" - log.NewLogger(0, "console", `{"level": 0}`) -} - -func ExecPath() (string, error) { +// execPath returns the executable path. +func execPath() (string, error) { file, err := exec.LookPath(os.Args[0]) if err != nil { return "", err } - p, err := filepath.Abs(file) - if err != nil { - return "", err + return filepath.Abs(file) +} + +func init() { + IsWindows = runtime.GOOS == "windows" + log.NewLogger(0, "console", `{"level": 0}`) + + var err error + if AppPath, err = execPath(); err != nil { + log.Fatal(4, "fail to get app path: %v\n", err) } - return p, nil + + // Note: we don't use path.Dir here because it does not handle case + // which path starts with two "/" in Windows: "//psf/Home/..." + AppPath = strings.Replace(AppPath, "\\", "/", -1) } // WorkDir returns absolute path of work directory. @@ -220,19 +227,11 @@ func WorkDir() (string, error) { return wd, nil } - execPath, err := ExecPath() - if err != nil { - return execPath, err - } - - // Note: we don't use path.Dir here because it does not handle case - // which path starts with two "/" in Windows: "//psf/Home/..." - execPath = strings.Replace(execPath, "\\", "/", -1) - i := strings.LastIndex(execPath, "/") + i := strings.LastIndex(AppPath, "/") if i == -1 { - return execPath, nil + return AppPath, nil } - return execPath[:i], nil + return AppPath[:i], nil } func forcePathSeparator(path string) { @@ -301,6 +300,9 @@ func NewContext() { HttpAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0") HttpPort = sec.Key("HTTP_PORT").MustString("3000") DisableSSH = sec.Key("DISABLE_SSH").MustBool() + if !DisableSSH { + StartSSHServer = sec.Key("START_SSH_SERVER").MustBool() + } SSHDomain = sec.Key("SSH_DOMAIN").MustString(Domain) SSHPort = sec.Key("SSH_PORT").MustInt(22) OfflineMode = sec.Key("OFFLINE_MODE").MustBool() @@ -655,5 +657,4 @@ func NewServices() { newRegisterMailService() newNotifyMailService() newWebhookService() - // ssh.Listen("2222") } diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go index 557f08ff..5e3761ca 100644 --- a/modules/ssh/ssh.go +++ b/modules/ssh/ssh.go @@ -1,80 +1,110 @@ +// +build go1.4 + // Copyright 2014 The Gogs Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -// Prototype, git client looks like do not recognize req.Reply. package ssh import ( - "fmt" + "io" "io/ioutil" "net" "os" "os/exec" + "path/filepath" "strings" "github.com/Unknwon/com" + "golang.org/x/crypto/ssh" - "github.com/gogits/gogs/modules/crypto/ssh" + "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/setting" ) -func handleServerConn(keyId string, chans <-chan ssh.NewChannel) { +func cleanCommand(cmd string) string { + i := strings.Index(cmd, "git") + if i == -1 { + return cmd + } + return cmd[i:] +} + +func handleServerConn(keyID string, chans <-chan ssh.NewChannel) { for newChan := range chans { if newChan.ChannelType() != "session" { newChan.Reject(ssh.UnknownChannelType, "unknown channel type") continue } - channel, requests, err := newChan.Accept() + + ch, reqs, err := newChan.Accept() if err != nil { - log.Error(3, "Could not accept channel: %v", err) + log.Error(3, "Error accepting channel: %v", err) continue } go func(in <-chan *ssh.Request) { - defer channel.Close() + defer ch.Close() for req := range in { - ok, payload := false, strings.TrimLeft(string(req.Payload), "\x00&") - fmt.Println("Request:", req.Type, req.WantReply, payload) - if req.WantReply { - fmt.Println(req.Reply(true, nil)) - } + payload := cleanCommand(string(req.Payload)) switch req.Type { case "env": args := strings.Split(strings.Replace(payload, "\x00", "", -1), "\v") if len(args) != 2 { - break + return } args[0] = strings.TrimLeft(args[0], "\x04") _, _, err := com.ExecCmdBytes("env", args[0]+"="+args[1]) if err != nil { log.Error(3, "env: %v", err) - channel.Stderr().Write([]byte(err.Error())) - break + return } - ok = true case "exec": - os.Setenv("SSH_ORIGINAL_COMMAND", strings.TrimLeft(payload, "'(")) - log.Info("Payload: %v", strings.TrimLeft(payload, "'(")) - cmd := exec.Command("/Users/jiahuachen/Applications/Go/src/github.com/gogits/gogs/gogs", "serv", "key-"+keyId) - cmd.Stdout = channel - cmd.Stdin = channel - cmd.Stderr = channel.Stderr() - if err := cmd.Run(); err != nil { - log.Error(3, "exec: %v", err) - } else { - ok = true + cmdName := strings.TrimLeft(payload, "'()") + os.Setenv("SSH_ORIGINAL_COMMAND", cmdName) + log.Trace("Payload: %v", cmdName) + cmd := exec.Command(setting.AppPath, "serv", "key-"+keyID) + + stdout, err := cmd.StdoutPipe() + if err != nil { + log.Error(3, "StdoutPipe: %v", err) + return + } + stderr, err := cmd.StderrPipe() + if err != nil { + log.Error(3, "StderrPipe: %v", err) + return + } + input, err := cmd.StdinPipe() + if err != nil { + log.Error(3, "StdinPipe: %v", err) + return } + + go io.Copy(ch, stdout) + go io.Copy(ch.Stderr(), stderr) + go io.Copy(input, ch) + + if err = cmd.Start(); err != nil { + log.Error(3, "Start: %v", err) + return + } else if err = cmd.Wait(); err != nil { + log.Error(3, "Wait: %v", err) + return + } + + ch.SendRequest("exit-status", false, []byte{0, 0, 0, 0}) + return + default: } - fmt.Println("Done:", ok) } - fmt.Println("Done!!!") - }(requests) + }(reqs) } } -func listen(config *ssh.ServerConfig, port string) { - listener, err := net.Listen("tcp", "0.0.0.0:"+port) +func listen(config *ssh.ServerConfig, port int) { + listener, err := net.Listen("tcp", "0.0.0.0:"+com.ToStr(port)) if err != nil { panic(err) } @@ -82,15 +112,17 @@ func listen(config *ssh.ServerConfig, port string) { // Once a ServerConfig has been configured, connections can be accepted. conn, err := listener.Accept() if err != nil { - log.Error(3, "Fail to accept incoming connection: %v", err) + log.Error(3, "Error accepting incoming connection: %v", err) continue } // Before use, a handshake must be performed on the incoming net.Conn. sConn, chans, reqs, err := ssh.NewServerConn(conn, config) if err != nil { - log.Error(3, "Fail to handshake: %v", err) + log.Error(3, "Error on handshaking: %v", err) continue } + + log.Trace("Connection from %s (%s)", sConn.RemoteAddr(), sConn.ClientVersion()) // The incoming Request channel must be serviced. go ssh.DiscardRequests(reqs) go handleServerConn(sConn.Permissions.Extensions["key-id"], chans) @@ -98,21 +130,25 @@ func listen(config *ssh.ServerConfig, port string) { } // Listen starts a SSH server listens on given port. -func Listen(port string) { +func Listen(port int) { config := &ssh.ServerConfig{ PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { - // keyCache[string(ssh.MarshalAuthorizedKey(key))] = 2 - return &ssh.Permissions{Extensions: map[string]string{"key-id": "1"}}, nil + pkey, err := models.SearchPublicKeyByContent(strings.TrimSpace(string(ssh.MarshalAuthorizedKey(key)))) + if err != nil { + log.Error(3, "SearchPublicKeyByContent: %v", err) + return nil, err + } + return &ssh.Permissions{Extensions: map[string]string{"key-id": com.ToStr(pkey.ID)}}, nil }, } - privateBytes, err := ioutil.ReadFile("/Users/jiahuachen/.ssh/id_rsa") + privateBytes, err := ioutil.ReadFile(filepath.Join(models.SSHPath, "id_rsa")) if err != nil { - panic("failed to load private key") + panic("Fail to load private key") } private, err := ssh.ParsePrivateKey(privateBytes) if err != nil { - panic("failed to parse private key") + panic("Fail to parse private key") } config.AddHostKey(private) diff --git a/modules/ssh/ssh_1.3.go b/modules/ssh/ssh_1.3.go new file mode 100644 index 00000000..62275ee6 --- /dev/null +++ b/modules/ssh/ssh_1.3.go @@ -0,0 +1,7 @@ +// +build !go1.4 + +package ssh + +func Listen(port int) { + panic("Gogs requires Go 1.4 for starting a SSH server") +} diff --git a/routers/install.go b/routers/install.go index 6fa7c12a..5240b88e 100644 --- a/routers/install.go +++ b/routers/install.go @@ -25,6 +25,7 @@ import ( "github.com/gogits/gogs/modules/mailer" "github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/setting" + "github.com/gogits/gogs/modules/ssh" "github.com/gogits/gogs/modules/user" ) @@ -76,6 +77,11 @@ func GlobalInit() { log.Info("TiDB Supported") } checkRunMode() + + if setting.StartSSHServer { + ssh.Listen(setting.SSHPort) + log.Info("SSH server started on :%v", setting.SSHPort) + } } func InstallInit(ctx *middleware.Context) { -- cgit v1.2.3 From 4f03b81ec777813820d4e33ec11893e214ec27d2 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Sun, 15 Nov 2015 14:41:36 -0500 Subject: #1931 Test patch does not checkout correct base branch --- models/pull.go | 11 ++++++++++- models/repo.go | 2 +- modules/middleware/repo.go | 1 - templates/repo/sidebar.tmpl | 6 ++++-- 4 files changed, 15 insertions(+), 5 deletions(-) (limited to 'models/repo.go') diff --git a/models/pull.go b/models/pull.go index 56af5f82..094cb9b2 100644 --- a/models/pull.go +++ b/models/pull.go @@ -218,6 +218,7 @@ var patchConflicts = []string{ } // testPatch checks if patch can be merged to base repository without conflit. +// FIXME: make a mechanism to clean up stable local copies. func (pr *PullRequest) testPatch() (err error) { if pr.BaseRepo == nil { pr.BaseRepo, err = GetRepositoryByID(pr.BaseRepoID) @@ -243,8 +244,16 @@ func (pr *PullRequest) testPatch() (err error) { return fmt.Errorf("UpdateLocalCopy: %v", err) } - pr.Status = PULL_REQUEST_STATUS_CHECKING + // Checkout base branch. _, stderr, err := process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(), + fmt.Sprintf("PullRequest.Merge(git checkout): %s", pr.BaseRepo.ID), + "git", "checkout", pr.BaseBranch) + if err != nil { + return fmt.Errorf("git checkout: %s", stderr) + } + + pr.Status = PULL_REQUEST_STATUS_CHECKING + _, stderr, err = process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(), fmt.Sprintf("testPatch(git apply --check): %d", pr.BaseRepo.ID), "git", "apply", "--check", patchPath) if err != nil { diff --git a/models/repo.go b/models/repo.go index fab463b9..bbd978ff 100644 --- a/models/repo.go +++ b/models/repo.go @@ -320,7 +320,7 @@ func (repo *Repository) UpdateLocalCopy() error { } } else { _, stderr, err := process.ExecDir(-1, localPath, - fmt.Sprintf("UpdateLocalCopy(git pull): %s", repoPath), "git", "pull") + fmt.Sprintf("UpdateLocalCopy(git pull --all): %s", repoPath), "git", "pull", "--all") if err != nil { return fmt.Errorf("git pull: %v - %s", err, stderr) } diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go index afa95a25..c7481743 100644 --- a/modules/middleware/repo.go +++ b/modules/middleware/repo.go @@ -81,7 +81,6 @@ func RepoRef() macaron.Handler { return func(ctx *Context) { // Empty repository does not have reference information. if ctx.Repo.Repository.IsBare { - ctx.Data["CommitsCount"] = 0 return } diff --git a/templates/repo/sidebar.tmpl b/templates/repo/sidebar.tmpl index df033c0e..2e4fdfb6 100644 --- a/templates/repo/sidebar.tmpl +++ b/templates/repo/sidebar.tmpl @@ -1,4 +1,5 @@ - +{{end}} \ No newline at end of file -- cgit v1.2.3 From 18de67380c5049d6a4d10cc3f6f8fd80a9ac7a89 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Sun, 15 Nov 2015 23:52:46 -0500 Subject: fix #1958 --- README.md | 2 +- conf/locale/locale_en-US.ini | 14 ++-- gogs.go | 2 +- models/error.go | 26 +++++++ models/release.go | 34 ++++----- models/repo.go | 2 +- modules/auth/repo_form.go | 10 +-- modules/bindata/bindata.go | 4 +- public/css/gogs.css | 79 +++++++++++++++++++++ public/less/_repository.less | 96 +++++++++++++++++++++++++ routers/repo/release.go | 71 +++++++------------ templates/.VERSION | 2 +- templates/repo/release/edit.tmpl | 59 ---------------- templates/repo/release/list.tmpl | 147 ++++++++++++++++++++------------------- templates/repo/release/new.tmpl | 145 +++++++++++++++++++++----------------- 15 files changed, 416 insertions(+), 277 deletions(-) delete mode 100644 templates/repo/release/edit.tmpl (limited to 'models/repo.go') diff --git a/README.md b/README.md index 73e35c4b..8be1adc6 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra ![](public/img/gogs-large-resize.png) -##### Current version: 0.7.11 Beta +##### Current version: 0.7.12 Beta diff --git a/conf/locale/locale_en-US.ini b/conf/locale/locale_en-US.ini index 93180daa..04e01457 100644 --- a/conf/locale/locale_en-US.ini +++ b/conf/locale/locale_en-US.ini @@ -368,9 +368,7 @@ fork_from_self = You cannot fork repository you already owned! copy_link = Copy copy_link_success = Copied! copy_link_error = Press ⌘-C or Ctrl-C to copy -click_to_copy = Copy to clipboard copied = Copied OK -clone_helper = Need help cloning? Visit Help! unwatch = Unwatch watch = Watch unstar = Unstar @@ -634,21 +632,21 @@ release.stable = Stable release.edit = edit release.ahead = %d commits to %s since this release release.source_code = Source Code +release.new_subheader = Publish releases to iterate product. +release.edit_subheader = Detailed change log can help users understand what has been improved. release.tag_name = Tag name release.target = Target release.tag_helper = Choose an existing tag, or create a new tag on publish. -release.release_title = Release title -release.content_with_md = Content with Markdown -release.write = Write -release.preview = Preview -release.content_placeholder = Write some content -release.loading = Loading... +release.title = Title +release.content = Content release.prerelease_desc = This is a pre-release release.prerelease_helper = We’ll point out that this release is not production-ready. +release.cancel = Cancel release.publish = Publish Release release.save_draft = Save Draft release.edit_release = Edit Release release.tag_name_already_exist = Release with this tag name has already existed. +release.downloads = Downloads [org] org_name_holder = Organization Name diff --git a/gogs.go b/gogs.go index 04e1cf65..a2ff0b5e 100644 --- a/gogs.go +++ b/gogs.go @@ -17,7 +17,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.7.11.1115 Beta" +const APP_VER = "0.7.12.1115 Beta" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/models/error.go b/models/error.go index 8f508598..f8fea6f5 100644 --- a/models/error.go +++ b/models/error.go @@ -288,6 +288,32 @@ func (err ErrUpdateTaskNotExist) Error() string { return fmt.Sprintf("update task does not exist [uuid: %s]", err.UUID) } +type ErrReleaseAlreadyExist struct { + TagName string +} + +func IsErrReleaseAlreadyExist(err error) bool { + _, ok := err.(ErrReleaseAlreadyExist) + return ok +} + +func (err ErrReleaseAlreadyExist) Error() string { + return fmt.Sprintf("Release tag already exist [tag_name: %s]", err.TagName) +} + +type ErrReleaseNotExist struct { + TagName string +} + +func IsErrReleaseNotExist(err error) bool { + _, ok := err.(ErrReleaseNotExist) + return ok +} + +func (err ErrReleaseNotExist) Error() string { + return fmt.Sprintf("Release tag does not exist [tag_name: %s]", err.TagName) +} + // __ __ ___. .__ __ // / \ / \ ____\_ |__ | |__ ____ ____ | | __ // \ \/\/ // __ \| __ \| | \ / _ \ / _ \| |/ / diff --git a/models/release.go b/models/release.go index 38f3e4f4..1c9c7d60 100644 --- a/models/release.go +++ b/models/release.go @@ -5,7 +5,6 @@ package models import ( - "errors" "sort" "strings" "time" @@ -15,16 +14,11 @@ import ( "github.com/gogits/gogs/modules/git" ) -var ( - ErrReleaseAlreadyExist = errors.New("Release already exist") - ErrReleaseNotExist = errors.New("Release does not exist") -) - // Release represents a release of repository. type Release struct { - Id int64 - RepoId int64 - PublisherId int64 + ID int64 `xorm:"pk autoincr"` + RepoID int64 + PublisherID int64 Publisher *User `xorm:"-"` TagName string LowerTagName string @@ -47,12 +41,12 @@ func (r *Release) AfterSet(colName string, _ xorm.Cell) { } // IsReleaseExist returns true if release with given tag name already exists. -func IsReleaseExist(repoId int64, tagName string) (bool, error) { +func IsReleaseExist(repoID int64, tagName string) (bool, error) { if len(tagName) == 0 { return false, nil } - return x.Get(&Release{RepoId: repoId, LowerTagName: strings.ToLower(tagName)}) + return x.Get(&Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)}) } func createTag(gitRepo *git.Repository, rel *Release) error { @@ -84,11 +78,11 @@ func createTag(gitRepo *git.Repository, rel *Release) error { // CreateRelease creates a new release of repository. func CreateRelease(gitRepo *git.Repository, rel *Release) error { - isExist, err := IsReleaseExist(rel.RepoId, rel.TagName) + isExist, err := IsReleaseExist(rel.RepoID, rel.TagName) if err != nil { return err } else if isExist { - return ErrReleaseAlreadyExist + return ErrReleaseAlreadyExist{rel.TagName} } if err = createTag(gitRepo, rel); err != nil { @@ -100,22 +94,22 @@ func CreateRelease(gitRepo *git.Repository, rel *Release) error { } // GetRelease returns release by given ID. -func GetRelease(repoId int64, tagName string) (*Release, error) { - isExist, err := IsReleaseExist(repoId, tagName) +func GetRelease(repoID int64, tagName string) (*Release, error) { + isExist, err := IsReleaseExist(repoID, tagName) if err != nil { return nil, err } else if !isExist { - return nil, ErrReleaseNotExist + return nil, ErrReleaseNotExist{tagName} } - rel := &Release{RepoId: repoId, LowerTagName: strings.ToLower(tagName)} + rel := &Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)} _, err = x.Get(rel) return rel, err } // GetReleasesByRepoId returns a list of releases of repository. -func GetReleasesByRepoId(repoId int64) (rels []*Release, err error) { - err = x.Desc("created").Find(&rels, Release{RepoId: repoId}) +func GetReleasesByRepoId(repoID int64) (rels []*Release, err error) { + err = x.Desc("created").Find(&rels, Release{RepoID: repoID}) return rels, err } @@ -150,6 +144,6 @@ func UpdateRelease(gitRepo *git.Repository, rel *Release) (err error) { if err = createTag(gitRepo, rel); err != nil { return err } - _, err = x.Id(rel.Id).AllCols().Update(rel) + _, err = x.Id(rel.ID).AllCols().Update(rel) return err } diff --git a/models/repo.go b/models/repo.go index bbd978ff..9b2c7bc6 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1116,7 +1116,7 @@ func DeleteRepository(uid, repoID int64) error { return err } else if _, err = sess.Delete(&Milestone{RepoID: repoID}); err != nil { return err - } else if _, err = sess.Delete(&Release{RepoId: repoID}); err != nil { + } else if _, err = sess.Delete(&Release{RepoID: repoID}); err != nil { return err } else if _, err = sess.Delete(&Collaboration{RepoID: repoID}); err != nil { return err diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index 766f540f..8e10dc24 100644 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -215,12 +215,12 @@ func (f *CreateLabelForm) Validate(ctx *macaron.Context, errs binding.Errors) bi // \/ \/ \/ \/ \/ \/ type NewReleaseForm struct { - TagName string `form:"tag_name" binding:"Required"` + TagName string `binding:"Required"` Target string `form:"tag_target" binding:"Required"` - Title string `form:"title" binding:"Required"` - Content string `form:"content" binding:"Required"` - Draft string `form:"draft"` - Prerelease bool `form:"prerelease"` + Title string `binding:"Required"` + Content string + Draft string + Prerelease bool } func (f *NewReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { diff --git a/modules/bindata/bindata.go b/modules/bindata/bindata.go index d5e9ded5..ac30952b 100644 --- a/modules/bindata/bindata.go +++ b/modules/bindata/bindata.go @@ -4364,7 +4364,7 @@ func confLocaleLocale_deDeIni() (*asset, error) { return a, nil } -var _confLocaleLocale_enUsIni = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x7d\xeb\x72\xdc\x48\xae\xe6\x7f\x3e\x05\xdb\x27\x1c\xb6\x23\xe4\x72\x74\xf7\xde\xa2\xa3\xed\x5e\x59\x6a\x5f\xe6\x58\x96\xc6\x52\xcf\x6c\xaf\xc3\xc1\x66\x15\x59\x55\x1c\x57\x91\xd5\x24\xcb\xb2\xe6\xc4\x89\xd8\x07\xd8\x07\xd8\x7d\xbd\xf3\x24\x0b\x7c\x00\xf2\x42\xb2\x24\xbb\x67\x62\xfd\xc3\x62\x65\x22\x6f\x48\x24\x12\x89\x04\x90\xf9\x6e\x97\x15\x65\xb7\x48\x9f\xa6\xc7\xe9\x2e\xaf\xea\x4d\xd9\x75\x69\x57\x6e\x96\x8f\xd7\x4d\xd7\x97\x45\xfa\xb2\xea\xe9\x77\xfb\xa9\x5a\x94\x49\xb2\x6e\xb6\x25\x81\xbe\xa2\x3f\x49\x91\x77\xeb\x79\x93\xb7\x05\x25\x9c\xda\x77\x52\x7e\xde\x6d\x9a\x96\x81\x7e\x96\xaf\x64\x5d\x6e\x76\x5c\x86\xfe\x24\x5d\xb5\xaa\xb3\xaa\xa6\x9f\x97\xf4\x95\xbe\xae\x25\xa5\xd9\xf7\x96\x74\xbe\xef\x25\x6d\xbf\xb3\xa4\x5f\x76\x49\x5b\xae\x2a\xea\x4d\x4b\x49\xef\xf4\x33\xb9\x2e\xe7\x5d\xd5\x73\x4b\x7f\x95\xaf\xe4\x53\xd9\x76\x55\xc3\xb5\xff\x45\xbe\x92\x5d\xbe\x62\x80\x0b\xfa\x93\xf4\xe5\x76\xb7\xc9\x51\xe0\x4a\x3f\x93\x4d\x5e\xaf\xf6\x02\xf3\x46\x3f\x93\x45\x5b\x52\x56\x56\x97\xd7\x94\x7a\x82\x1f\xb3\xd9\x2c\xd9\x13\x12\xb2\x5d\xdb\x2c\xab\x4d\x99\xe5\x75\x91\x6d\x65\x98\xbf\x50\x7a\xaa\xe9\x29\xa5\xa7\x9c\x8e\x21\x94\x05\x0d\x35\xcb\x3b\x1d\x07\xe1\x92\x46\x9e\x77\x09\xaa\xaa\xf3\xad\x95\xe6\xcf\xa4\xdc\xe6\xd5\x86\xb1\xf6\x98\x3f\xa8\xe3\x5d\x77\xdd\x00\xb7\x17\xfa\x49\x48\xc8\xfa\x9b\x5d\x09\x1c\x3c\xbe\xa2\xaf\x64\x91\xef\xfa\xc5\x3a\xe7\x7e\xca\x57\x42\x40\xbb\x86\x90\xd1\xb4\x37\x80\xb3\x1f\x49\xd3\xae\xf2\xba\xfa\x7b\xde\x0b\x82\xce\x83\x9f\xc9\xb6\x6a\xdb\x86\x71\x7b\x86\x8f\x84\x86\x9e\x71\x3d\x94\xf2\x96\xb0\x10\xd4\xc2\x39\xdb\x6a\xd5\x0a\x1a\x39\xf3\x0c\xbf\xb8\x16\xce\x5b\x36\xed\x47\xcd\x78\xc1\x9f\x83\xa2\xd4\x09\xcd\x8d\xdb\xcf\x6b\x42\xbc\xe6\x9e\xe1\x47\x04\xd0\x25\x79\xb1\x25\x54\xee\xf2\xba\x64\x1c\x1d\xf3\x2f\xc2\x0b\xfd\x4a\xf2\xc5\xa2\xd9\xd7\x7d\xd6\x95\x7d\x5f\xd5\x2b\x46\xf6\xb1\x24\xa5\x97\x9a\x94\x04\x79\x2e\xed\xa6\xd9\xbb\xe9\xa4\xf4\x5f\xe9\x67\x7a\x21\x3f\x25\x2f\x28\x84\x4c\x57\x92\x47\xd2\x65\xcb\xb2\x2c\x64\x2c\x5d\xfa\x82\xbe\x93\xdd\x7e\xb3\x21\xac\xfd\xbe\x2f\xbb\x9e\x0b\x5d\xd0\x6f\x1a\xbf\xfc\x4e\xaa\xae\xa3\x0f\x4a\x7e\x8d\x8f\x84\xa6\xae\x5e\x60\x30\x27\xf8\x48\x92\xf7\x5d\x99\xb7\x8b\xf5\x87\x44\xfe\xa2\xaf\xfc\xc1\xb4\x77\x68\x52\x99\x90\x94\x88\xa4\x05\x6b\x20\x59\x34\x05\xff\x38\xa1\x3f\x54\x75\x55\x77\x7d\xbe\xd9\x7c\x48\xf4\x83\xc1\xe4\x4b\x26\xa0\xaf\x7a\x60\x41\x13\xd3\xcb\xbe\xdc\x75\x3c\x83\xe9\x8b\xaa\xed\xfa\xc7\x7d\x45\xc4\xfa\x6e\x5f\x27\x45\xb3\xf8\x48\xcb\x80\x97\x34\x5a\x7e\xbd\x4c\x09\x59\x0f\x68\x21\xb4\xfb\xba\x26\xf4\xa4\x2f\x1b\x42\x19\x35\x53\x51\xfb\xa7\x80\x3e\x4a\x77\x9b\x32\xef\x08\xa4\xcc\x8b\xf4\xc7\x3c\xed\xf3\x76\x55\xf6\x4f\xef\x65\x73\x5a\x7e\x1f\xef\xa5\xeb\xb6\x5c\x3e\xbd\x77\xbf\xbb\xf7\xec\xe5\x9e\x8a\x6d\xaa\xba\xec\x7e\x7c\x92\x3f\x4b\x17\x39\xe5\x10\x1a\x6f\xd2\x79\xb9\xe4\xd5\x46\x6d\xa5\x44\xe5\xf5\x8a\x57\xda\x4d\xbf\xe6\x06\x89\x12\xe8\xa3\x4b\x79\xa9\x7f\x93\xf0\x04\x10\x2b\xc8\x8a\xb9\xb1\x35\x74\x08\xc9\x2d\x4d\xc0\xd9\xcd\xe5\x9f\xdf\x1c\xa5\x17\xc4\xdb\x56\x6d\x89\x6f\xfa\x8f\x4a\x7c\x9f\xd2\x68\xaf\xaa\xd3\xe7\xb3\x84\xca\x1a\x42\x4e\xf3\x3e\x9f\x73\xdf\xdd\xec\x73\xa6\x2c\x42\x97\x87\xa5\xc8\xdc\x12\x9c\xb1\xeb\xa3\x69\x99\x5a\xc8\x54\x87\x2e\x7f\x57\xc7\x5b\xe6\x01\x94\xee\x30\x7b\x21\x38\xa3\xaa\xd2\xd7\x6f\xdf\x9e\x9f\x3e\x4f\xcb\x7a\x45\x98\x49\xaf\xab\x7e\x9d\xee\xfb\xe5\x7f\xcb\x56\x65\x5d\xb6\xf9\x26\x5b\x54\x8c\x94\x96\x08\x36\x25\x2c\xc9\x10\x67\x49\xd7\x6d\x88\x45\x81\x0a\x2e\x2f\xdf\xa4\x67\x4c\x09\xbb\xbc\x5f\xa3\x23\xfd\x3a\xe9\x7e\xdf\x30\xa2\x5c\x83\x57\xeb\x32\xc5\x62\x00\x50\xb3\x1c\xe2\x25\x2d\xb4\xaf\xb3\xa4\x6c\xdb\x8c\x38\x68\x7f\xc3\x68\xd6\x3a\x0f\x41\x4b\x75\x44\xed\x75\xd3\xd3\x34\xa6\x28\x27\x55\x54\xf5\xa7\x7c\x53\x15\x84\x6c\x8f\x90\xb8\x2c\x12\x8b\x86\xe6\x8d\x4b\x13\x65\x36\xd7\x18\x6a\xbe\xa0\x0d\xa0\x4b\xef\xcd\xee\x81\xe3\xde\x7b\x7c\x6f\x96\xd4\x4d\x26\x5c\x82\x79\x73\x51\x75\xf9\x9c\xf8\xb4\xec\x1b\xad\x71\xbd\x5f\x99\x7e\xa4\x2b\x0a\x91\x46\x10\x8c\x5b\xde\x8b\xb0\x05\x30\x71\xe5\xc4\xb0\xc1\x6c\x94\xcd\x84\x63\x37\x9e\xe4\xe6\x57\xd8\x92\x4b\x18\x8d\x39\xb1\x09\x33\xea\x3a\xde\xed\x36\xd5\x42\x9a\x7e\x29\x79\x9e\xd0\x78\x67\x56\xa4\x84\x70\x20\x14\xcb\x0b\xc8\x85\x7a\xcd\x6c\x2b\x8d\xf8\x3c\xca\xaf\x4b\x5a\x39\xeb\xfd\x4a\x76\xa7\x4d\xb3\x2f\xbe\x01\x43\xb1\x99\xf3\xfc\x24\x7d\xd7\x50\x87\x41\x1d\x0e\xc0\x37\x71\x4c\x8c\x81\x85\x81\xb6\xdc\x36\x3d\x23\x4e\x8b\x55\x34\x3d\xd7\x15\x65\xd2\x48\xbb\xfc\x13\xb1\xc5\xbe\x91\x25\x59\xd0\x92\x5b\x70\xc5\xc4\xc1\xf6\xb4\xa3\xcb\xb2\x20\x3e\x22\x4b\xc3\xd2\x62\x1a\x04\xd4\x76\x4f\xab\x69\x4d\x95\x31\xe2\x59\x22\xa1\x2a\xa7\xfa\x89\x21\x51\x3d\x58\xe5\xb4\x72\x1b\xda\x3c\x79\xa2\x4f\xf1\xa1\xbf\xc3\xfa\xa9\x57\xf9\x72\x49\xbd\xea\x68\x55\xbc\x4a\x17\x9b\x86\x96\xd4\x2f\xef\xde\x74\xbc\x60\xd6\xd9\xae\x69\x21\x89\x50\xd6\x05\x7d\xba\xb4\x00\xd1\x0c\x51\xef\xb7\x73\xfa\x75\xbd\xae\x88\x51\x03\xed\x5c\x82\xa5\x24\x4a\xa5\x26\xf6\x1d\x4d\xe1\x51\x4a\x4b\x98\x46\x40\x28\x03\x01\xf0\x18\x8c\xea\x18\x7c\x49\x34\xb6\x6f\x69\x39\xad\xfb\x7e\x67\x2d\xbf\xba\xba\xba\x90\xa6\x5d\xea\x6d\x6d\xe7\x01\x65\x60\x0e\x36\x2c\x1b\xd5\x69\x53\xcf\x40\x24\xfb\x76\x33\xa0\x1f\x1a\xab\xe5\x1c\xc0\x0b\x77\xe1\x09\xff\x77\xe9\xd1\x03\x3c\x77\x24\xf5\x5d\x83\x9a\x08\xc7\x25\xe4\x14\x22\xea\x66\xc7\xf5\x06\x54\x7d\xae\x09\x9e\x94\x21\xdb\xb8\x7c\x91\x70\x28\x17\x32\x65\xb0\x4b\x6f\x69\xc0\xca\x46\x2f\xcf\x08\x0d\xe0\xa5\x48\x5d\xb6\xcd\x96\x52\x5f\xd0\x1f\x9f\xe0\xbb\x7f\xc6\xf5\x01\x26\x2f\x0a\xe2\xf2\xdd\x51\xfa\xee\xc5\x49\xfa\x9f\xbf\xff\xee\xbb\x59\xfa\xba\xe7\x95\xc8\xc4\xf9\x37\x26\x2a\xfa\x14\x51\xcb\x81\x12\xc7\xea\x89\xee\xee\xf1\xca\xba\x97\xfe\x88\xdc\xff\x5e\x7e\xce\x49\x44\x2c\x67\x8b\x66\xfb\x8c\xb9\xea\x36\xa7\xb5\xcf\x39\x44\xae\x4a\xc7\x97\x65\x5d\xd0\x87\x0a\x6c\x9a\x17\xb0\x03\xcd\x0f\xc4\x37\x11\x5c\xb3\x45\x53\x2f\xab\x96\x07\xf4\x73\x0d\x6a\x30\x91\x96\xb6\x6b\xe4\x98\x54\x44\x48\x23\x0e\x52\x2d\x6f\x3c\x28\x86\xfa\x96\x13\x75\x42\x13\xa1\xba\x4c\x45\x74\x87\xe5\x4b\x21\x46\x9e\xb7\x73\x1a\x5e\x6b\xf8\xee\x3c\xc2\x9b\xe5\x92\xf7\x5a\xdb\x25\xb4\x85\x73\x49\x95\x0d\x23\x04\x21\x62\xdc\x41\x28\x3f\x55\x22\x3e\x39\x7d\x9b\x96\x9f\x88\xda\x98\xeb\xb5\x4d\xb1\x5f\x80\xc2\x18\xf6\x88\x99\x35\xb1\x88\x8e\xd6\xc6\x42\xf6\x95\x80\x49\x70\xd7\x98\x13\x2d\x08\x88\x78\x83\x31\x6b\x12\x24\x3f\x11\xe7\x6f\x83\x26\x5e\x5a\x92\xf6\x7e\x04\x3b\xea\x94\x2b\xc1\x23\x5f\xd0\x8c\x13\x55\x48\x2f\x3a\xe9\x94\x64\x13\xb9\x13\x1d\xef\xe9\x84\x92\x17\xd4\x97\xf9\x0d\xf8\x4e\xc7\xc4\x50\x94\xcb\x7c\xbf\xe9\x7d\xbf\x06\x9b\x88\xb5\x74\xc9\x87\xa4\x30\x6f\xb2\xc0\xa8\x83\xa0\x9e\x6e\x58\x96\xc8\xb0\x26\x39\x47\x36\x1b\xa6\x57\x39\x85\xd8\xbe\x43\xec\xa9\xc4\xf4\x64\x5e\xe4\xd7\xf9\x32\xc9\x3f\xce\x77\xcd\xbe\x13\xc9\x27\xc5\x56\xcb\x35\x5a\x05\x2c\x2a\x4c\xf7\x65\x96\xa8\xb8\x94\xe9\x71\x2d\xfb\x54\xe1\x30\xe4\xc8\x55\xaa\xd4\x23\x1c\xf3\xb5\xbf\x30\x00\x9f\xb2\xba\xc9\xb2\xae\x37\xe7\x3c\xc8\xce\x1d\x86\x04\xe7\x3c\x5c\xb4\xc0\x22\x1c\xcd\xd2\xa7\x0a\x6c\x5e\x09\x06\x78\x21\xaa\x41\xd3\xd4\x54\x57\x96\xa8\x81\xca\x3f\xa1\x3a\x51\x66\xa6\x07\x04\x95\xd9\x4d\xf4\xe3\xed\xbe\x68\x20\x3b\x60\x2f\xa1\xd2\x86\xd6\xc1\xbe\x9e\xb6\xd5\x6a\x4d\xbc\xb5\xb9\x3e\x12\xa4\x5c\xaf\x9b\x92\xd7\xcf\xeb\xd3\xa7\xdf\x4a\x3f\x56\xbc\xb3\xb8\x42\xbc\x27\xe5\x7b\x22\x2e\xc2\x98\x92\xb1\x74\xc1\xed\xed\x80\x1c\x1d\x45\x04\x68\x78\xf8\x1b\x89\x12\x8e\x69\x28\xaf\x08\xf3\x94\x49\x78\x18\x29\x6d\x07\x48\x69\x58\xb9\x92\xca\xfb\xd9\xaa\xc1\x41\xc6\xe4\x7b\xde\x2d\xe9\x3c\xdc\xf5\xd9\xaa\xea\xb3\x25\xb3\x2e\xae\xf9\x05\xd7\xc0\x9b\x37\xe5\xa4\x0f\x28\xeb\x41\x4a\xfc\x8f\x4e\x67\xc5\x0f\xe9\xfd\x4f\x2a\x31\x7e\xcf\x3c\x29\xa3\x55\x54\x6d\x30\x25\x7a\x3c\x6a\x4b\x11\x58\xed\x0c\xee\xa4\xb6\x6e\xbf\xc3\xde\xa6\x02\xa2\x3b\x0d\x14\xcd\x75\xcd\xab\x0f\xcc\x97\xf8\x4c\xb5\xa8\x68\xcf\x98\x57\x75\x4e\x1b\xbc\xd5\x02\xa6\x7e\x9f\x68\xe2\xed\xf9\x15\x00\x57\xcd\x7c\x5f\x6d\x0a\x03\x98\x25\x26\x44\x92\x08\xa9\xb3\x1f\x8a\xd5\x96\x54\x49\x5f\x16\x4d\xcb\x12\x09\x46\x63\x05\x0f\x88\x42\x2d\x8b\x18\x48\xae\xf8\x3c\x03\x58\x94\x73\x52\x0b\xa3\x81\xa6\x1f\x47\x35\x96\x69\x40\x37\x55\x57\x3f\xe8\xd1\xd3\xc5\x9e\xda\xa2\xa9\xe7\x64\x2a\xd8\xa5\x8f\x9f\xd1\xff\x09\x4b\x48\xb2\x03\xac\xc6\x88\xe7\xcc\x54\x32\xf7\xb2\x16\xa3\xae\x46\x44\xee\xa6\xda\x48\x38\x18\x6b\xd8\x5f\x23\x81\x6e\x2f\x54\xcb\xea\x92\x0d\x4d\x6b\xf9\x0d\x7d\xf0\xc9\x6d\xb5\xc1\x24\xe4\xbd\x1e\xaf\x1a\xc2\x1b\x13\xc8\x91\x2c\x9a\x25\x0d\x8d\x79\x69\x9f\x7f\x2c\x71\x22\xa3\x3d\xff\x3d\xeb\x81\x3e\x24\x7b\x91\x41\x9b\x4d\xe1\xce\x3b\xa0\xec\xa6\x1d\xaa\x31\x3c\x90\xa3\xda\x8e\x84\xed\xc5\x3a\x73\x5a\x24\x46\x4a\x5f\x7e\xc6\xee\x8f\x2c\xaf\x54\x62\x92\xe7\xac\x64\x7b\x83\xe9\xe2\x41\x9c\xdd\xf8\xd9\x22\x09\x94\x16\x0a\x1d\x66\xe7\x0d\x63\xed\x53\xe9\xa0\x4e\xc2\xd4\xb8\x00\xd5\x45\xb2\xb2\x56\x15\x6b\x1b\x28\x4b\x54\x22\x9a\x2b\x6a\x91\x2e\x01\x2b\x53\x15\x18\x38\x1e\xcd\xa7\x9e\xec\x67\x34\x31\x50\x1b\x58\xcb\xc4\x17\x6f\x64\x5d\x04\x6d\x26\xef\x55\x3d\xf6\x21\x31\xb8\x77\x71\x3e\xf1\x94\xf5\x87\x40\x05\x95\xd9\xec\x9a\x2a\x0a\xda\x13\x65\x2b\x5e\xa4\x58\x97\x3b\x96\x3e\xb6\x1d\xc8\x62\xc3\x27\xed\x1b\x95\x9f\x1d\x81\xfc\x24\x0c\x9b\x28\x86\xd8\xdc\x37\x49\xd7\xf0\x82\xcb\xbe\xb2\x8a\xe7\x15\x91\x02\xca\xc7\x9b\x9d\xe8\xc6\x48\xcc\xe5\xe9\xa3\x55\x76\x73\x14\x9f\xac\xd6\x79\x47\x4c\x9c\x64\x05\x2d\x56\xcc\xec\x84\xcb\xd3\x4e\xe7\x39\xac\x19\xe8\xf3\x40\xe5\x52\xb2\x69\x87\xbb\x30\xf7\x50\xf8\x9c\xb6\xe2\x64\x27\x48\x46\xa1\x00\x35\xd1\x26\x21\x6c\x5b\xb2\xf8\x9c\x6d\x45\x8d\x26\xbf\xd2\xb3\x32\xa1\xed\x70\x45\x0b\xda\x08\xf6\x29\x6b\x3f\x56\x38\x65\x28\xbd\x32\x40\xd9\x87\x8c\x58\x21\x2c\xe5\x27\xd3\x5b\x12\x67\xb8\x86\x6a\x88\xd6\xf6\x08\xfd\xb4\x65\x51\xf6\xcc\x18\xbb\xc8\x08\x10\xf5\x3a\xe2\x16\x1e\x89\xc7\x29\x2b\x20\x43\x28\x15\xb9\xfd\xb0\xb8\x00\x73\x8d\x1f\xe7\xcf\xee\x77\x3f\x3e\x99\x3f\x73\xbc\x75\xb1\x2e\x17\x1f\x85\xfe\xaa\x7a\xde\x7c\xc6\xc1\x16\x8a\x12\x3a\x53\xf3\x1a\xbb\x5f\xa4\x74\xd0\x6d\x71\xae\x22\x5e\x40\xc5\x08\xf1\x9c\x1b\x4d\x1a\x75\x86\x59\x06\x6d\x6d\x0b\xac\x2a\x10\xb8\x27\xc8\x63\x4e\x65\x92\xc4\x06\xe0\x69\x12\x03\xd9\x54\xdb\xaa\x1f\xd1\x04\x73\x98\x5c\x69\x4b\x75\x65\x86\x24\xd4\x85\x61\x62\x94\xc4\xa7\xa9\x1a\xda\x57\x8d\x4e\xae\x73\x3a\x48\x7d\x9f\x12\x6d\xec\x69\x7f\xe2\xce\xd2\x78\x88\x51\xe7\xbc\x31\xd3\x21\x2a\xef\xb2\x7d\xad\xf8\x2a\x0b\xa3\x92\x57\x15\xb6\x0f\x6e\xd7\x68\x39\x80\x32\x94\xea\x59\x20\x7d\xe8\x50\xf9\x68\xa6\xba\x2d\x14\x63\x9e\xce\x1d\xaa\x58\x6e\xcd\x27\x67\x85\x78\x5e\x5d\xca\xd9\x17\x18\x60\x38\x9e\x41\x3a\x40\xf9\x69\xa1\x53\xd8\x47\x4a\x01\xa6\xe7\xfb\xbe\x6f\xf8\x5c\xb2\x61\x72\x90\x32\xd6\xeb\x13\x00\xe2\xa8\xe5\xeb\xc3\x74\x86\x78\x12\x16\x5b\xda\x39\x21\x23\x3a\xe4\x35\x2d\x1a\x6e\x3e\xd1\x0d\x46\xa7\x9b\xa0\x03\x2b\x44\x97\x94\xd7\x37\x5e\xbd\x81\x5e\x70\x83\xfd\x74\x5f\x1e\xb6\xe5\x23\xdf\x1b\xb7\x18\x50\xc2\x7a\x24\xc5\x83\x85\xf2\x0e\xb9\xa2\x62\xb5\xe5\x64\x7b\x9a\x2a\x2a\x3d\x7d\xb4\x31\x7a\x91\xcf\x24\x4f\x9c\x93\xc4\xca\x02\x88\xa6\x51\xa0\xf4\x6c\xd0\x96\x3f\x12\x8e\x31\xd8\xc7\x5d\xf6\x5b\x53\xdf\x34\x59\xb7\x96\xe3\xb7\x75\x8f\x8e\xee\xf5\x2a\xd2\x5b\xe1\x5e\x04\x44\xf7\x5f\x78\x03\xe4\x81\x7e\x48\x74\x36\xca\x60\x51\x28\xb5\x5a\x8e\xcd\x9a\xac\x0d\x07\x6f\xc2\xda\x5f\xca\x96\x8f\x77\x00\x8a\x67\xeb\x10\x16\xe3\x41\x38\xa6\xe8\x77\x77\xc7\x10\x35\xe9\xc8\xf6\x7b\xee\x75\x53\xe4\xd4\xed\x1b\x68\xa2\x7f\xa5\x0d\xa7\x86\x8e\xbf\x49\x28\x43\x8e\x99\x67\xf8\x20\x50\x3e\xf3\x7e\x48\x78\x4b\x7f\x3b\x10\x56\x79\xc7\xd2\xb4\x40\x5e\x42\xd6\xcf\xd1\x25\x86\x1b\xca\xc5\x84\x60\xfb\xae\xf4\x77\x19\xf8\x72\x63\xba\xbc\x7c\x75\x65\x87\xd8\xcb\x57\xe9\xc7\x52\x2b\x7f\xd5\xf7\xbb\xee\x17\x28\x34\x44\x3b\xc1\xaa\x8c\x8b\xfc\x86\x85\x48\x49\xd6\x1f\xc8\xb8\x2a\xf3\xad\xf6\x92\x3f\xa5\x8a\x63\xda\x5d\x35\x91\x3f\x69\xd3\x0d\x14\x65\x09\xc4\xa9\x9f\xa7\xc4\x68\x77\xa8\x29\xf5\xa2\xe4\xb7\x91\x76\xef\xb7\x24\xdf\xec\xe8\xdc\xc5\xf2\x4c\x00\x06\x45\xd6\x5c\x8f\x5f\x29\x40\x40\xc1\xfb\x2d\xcd\xfc\x02\xc7\x4d\x2a\xf0\xf0\x71\xf6\x28\x50\x6c\xc6\x95\x15\xb4\xb4\xff\x50\x85\xfc\xcd\x42\x6f\x58\x6f\x57\xfd\xdd\x46\x11\x55\xc7\xe9\xc4\x29\x09\x02\x22\xa6\x87\x72\x40\xd8\xa7\x59\xdc\xec\x59\xaf\x45\x09\x24\xd2\x46\x55\x6f\xf3\xcf\x77\x15\xdc\x36\x13\xe5\x84\x81\xf9\x42\xc6\xa6\x74\x88\xf1\xb2\x20\x78\xd6\x5c\x1d\x84\xa6\xa9\x27\x90\xaa\x5e\x6c\xf6\xc5\xc1\x9e\x74\xfb\x39\xed\x71\x2c\x2b\x3f\xb8\xdf\x3d\xe0\x2a\xeb\x8f\xb4\x29\xd7\x0e\xfe\x17\xf9\x9d\xe2\xf7\x0f\x76\xcd\x46\xa7\x59\x3d\x40\xa4\xee\xc2\x8d\x64\x8b\x82\x77\x07\x1c\x04\x66\x9e\xa9\x84\x87\x03\x47\xfe\x50\x49\xe8\xe9\xcd\x2d\x6c\xd6\x43\xe0\x9c\x44\x24\x38\xf3\x77\x83\x19\x6f\xf1\x19\x0b\xdd\x75\x28\x5a\xbb\xcd\xdf\x76\x51\x40\xc8\x0d\x51\x36\x2e\x37\x58\xa0\x07\x8b\x93\x24\x33\x51\xfa\x7c\xac\x4b\x3e\x50\xbe\xa7\x25\x36\x51\x81\x5b\x79\x07\x0b\xca\xe4\xa3\x10\x8d\xbc\x18\xf1\x8e\x71\x41\x06\xa3\x63\xdf\x66\x53\xae\x58\xe9\x68\x0d\x47\xad\xe9\x44\xd3\x96\x27\x60\x21\xc1\x79\x0c\xbb\xc9\x0a\xe7\x35\x3c\xc4\xb8\x39\x8a\x8f\x8f\xac\x8b\xa1\xaa\x5a\xdc\xef\x06\x87\x48\xed\x86\xee\x00\x5b\x3e\x2f\x75\x7b\xde\x80\xf8\x6c\x25\xc2\x55\x3c\x1b\x2c\x5e\xa0\xaa\x12\x4d\x1c\xae\x9e\x68\x91\x0f\x9c\x77\xd5\x0f\xb0\xaf\xac\x3a\x54\x3a\x8c\x2b\xd6\xca\x1d\xd0\xa1\x6a\xdd\x81\xb8\xfc\x5c\x41\x81\xfb\xb2\x62\xc5\x20\x8e\xc4\x4e\x13\x80\xbc\x59\xb2\x21\xe6\xc1\x47\x2f\x19\x95\x88\xe1\xcd\x27\x5e\x8d\xdc\x1e\xe7\x4a\x39\x51\xe8\xea\xa0\x78\x9e\xf5\x70\x8d\x6b\xa0\xb2\x38\x22\x41\x86\x4b\x50\x3f\xb1\xb8\xf3\xcd\x75\x7e\xd3\x41\x53\x64\x1c\x8a\x95\xd7\x52\x9c\xd9\x0f\x89\x39\x2b\xf4\x2a\xbc\x22\xa1\x15\x67\x98\x60\x5d\x3f\x6f\x36\x4e\x18\xb9\xc6\xf1\x18\xdc\x45\x75\x4f\x9f\x82\xed\x5a\xf7\x26\x3e\xda\xf3\x41\x98\x8f\x28\x92\x1d\x54\x84\xbb\x47\xdd\x29\x26\xca\x1e\xb1\x10\x48\xcd\xb0\x48\x46\xfc\x5b\x70\x4d\x52\x2e\x61\x16\x5d\x0a\x74\x25\x7b\xaa\xff\xb1\x88\xf5\x15\xe1\x90\x8f\x89\x5e\x7d\xc0\x7b\x19\xcd\x8a\xa9\xf8\x25\x1d\x87\xff\xa4\xeb\x69\x09\x30\xa6\xed\x42\xff\xd7\x40\x1e\x49\x91\x8b\x25\x06\x34\x75\xeb\x6a\x97\x36\x50\x1b\x87\x28\xf4\x64\x1b\x08\xd2\x7c\x99\x51\xe2\xd8\xc0\xfa\xf3\x36\xaf\xbb\x65\x09\x45\xfa\x36\x5d\xf2\x9d\xf1\x4c\x9b\x66\xb9\x5c\x2e\xf6\x0f\xb4\x2c\x47\x30\x34\x1d\xee\x2e\x98\xbb\x60\xa2\xe2\xa6\xe5\x66\x05\xca\x5a\xf4\x01\x58\xf5\x35\x75\xd6\x07\x26\xb3\x11\x0a\x20\x1b\x47\xf7\x64\xd6\x9b\x4f\x65\x88\x88\xe5\x1f\x1d\x79\x80\x75\xbd\x2b\x90\x0b\x96\x78\x9a\xa4\x51\x68\x6b\x70\xcd\x3b\xbf\x89\x47\xcf\x45\x83\xbb\x73\x5a\x23\xa5\xb6\xc2\x0b\x83\xd7\xca\xa0\x42\x68\x69\xfc\x89\x28\x91\x7b\xf6\x6c\x4e\x5d\x5c\xac\xa3\xd5\x79\x85\x9c\x54\x72\x46\x0b\x34\x79\xcf\x4d\x7f\x48\xe4\xa6\x3d\x73\x4a\xf9\x13\xb9\x79\x17\x89\x56\x95\xec\x7d\x6a\x9a\x78\xbe\x2a\xb1\x22\xa2\x77\xbf\xb5\x24\x6f\xc3\xa6\x14\xfd\x5b\x43\x32\x07\x74\xeb\x7f\xa2\x2f\x96\xf1\xeb\x24\xba\x5e\x1c\xa8\x49\x20\x46\x57\x3d\xaf\xb0\x0b\x5a\x18\x24\xf6\x1c\x6b\x0a\x9d\xd2\xc1\x1d\xa0\xb9\x79\x61\xdf\x34\x1f\x39\x33\x3d\x5e\xda\xf2\xa5\x70\xa2\x46\x7b\x61\xdf\x09\x1f\xf2\xb7\x33\x6c\x0e\x2c\x7e\xe3\x9a\x22\xd8\x12\x58\x5a\xe0\x09\xb3\xbc\x59\x00\xbf\xcb\x7b\x62\x8b\xb5\x1c\xc4\x84\x43\x85\x45\x35\xdb\x55\xe1\xee\xb3\xb9\x16\xb6\xfd\x10\x54\x7c\x48\xbc\x49\x8a\x59\xa3\x4c\xa9\x85\x95\xc5\x74\x2a\x23\xff\x2b\x7d\xaa\x42\x07\xec\x0b\x1f\x7a\x20\xc7\x4d\xb2\x5d\xff\xc1\x3c\x26\xf8\x99\xa8\x0a\x2c\xd6\x7f\x29\x79\x3f\x4d\x4f\xe5\xc3\x8e\xf6\xfb\x0a\x63\xaa\x8a\x24\xd9\x01\xef\x81\x01\x8d\x4e\x84\xeb\xb4\x1a\x4a\x79\x4d\x7c\x3b\xdc\xd9\xd9\x66\x43\x0a\x31\xe1\xda\xe5\x10\xa4\x00\xbe\x9b\x08\x8e\xa5\xac\x5c\xc6\x79\xb5\x0e\x2e\xbe\xf8\x3a\x87\x4f\xd9\x04\x76\x5d\xce\x53\x56\xf7\x12\xe1\xd0\xe9\x4f\x07\xba\xcd\xe9\xe0\xf8\xa9\xca\x9d\x62\x89\x66\x8b\x4d\x74\x74\x17\x7d\xc1\xe6\x39\xb8\x4c\x1f\xdb\x91\xf1\xcd\x94\x5e\xf6\xbc\xd1\xcf\x64\xbf\xe3\xdb\x93\x60\xc0\xbf\x20\xc1\x0d\x38\xce\x0f\xce\x63\x18\xba\x15\x73\xd2\x8c\x80\x17\x76\x48\x83\x95\xcb\xcc\x96\xcf\x84\x7d\x98\x2e\xa1\x62\x08\xe2\x35\x2c\x60\x31\x6a\x1c\x03\x64\xca\x7d\x2e\x86\x4f\x3b\x63\xba\x6e\xae\xd3\x4d\x55\x7f\xec\x14\x9b\xcc\xc7\xc2\xc3\x29\x54\x52\x44\x84\x7b\xb1\x1b\x92\xcf\xb1\x99\x92\x5d\x33\x0d\x56\xb8\x5d\x46\xc9\x85\xdb\x31\x92\x27\x61\xfd\x11\xdd\x2e\xc4\x96\x25\x8b\xc9\x60\x6a\x76\x79\x47\xa3\x6c\x9a\x4e\x15\xa0\x9e\x89\x70\x1a\x94\x2a\x0a\xa5\x38\x77\x10\x3a\x25\xc7\x76\x65\x88\x35\x95\xd8\x25\x9f\x75\x00\x2b\x34\xab\xb6\x62\xf6\xf7\x8b\x5d\x01\x62\x7e\xdc\x69\x02\xd9\x30\x2a\x89\x7b\x1f\xde\x7b\xbc\x6d\xec\x82\xd1\xb8\xa1\x65\x1e\xd9\xa6\x2f\x18\xc0\x96\x1d\x75\x76\x48\x1f\x5a\x81\xa9\xf0\xef\x20\x13\x23\x82\xf0\x52\x48\x26\xde\x31\x88\x66\x13\x89\x76\x27\x7a\x19\xe1\xf2\x19\xb3\x41\xfe\x5b\x5c\xdf\x39\xa5\x02\x1f\xc8\xb3\x01\x88\x1e\xd8\x23\xc8\x49\x09\xda\xda\x3a\x28\x3d\x0f\x7a\x3f\x5a\x2b\x56\xee\x9a\xb0\x10\x0e\x5c\xa9\xbb\x98\x99\x1d\x0f\x6b\x52\xe5\x2e\x10\x06\x17\x62\x74\x52\xe3\x22\x51\xaa\x20\x54\xe1\x80\xd1\xf9\x73\xc5\xb1\x70\x1f\xbe\x00\x10\xab\x43\x07\xa0\x86\x87\xf1\x79\xb3\x34\xeb\x85\x90\x91\xed\x5a\x22\x0f\xda\x68\x07\xfa\xb5\x11\x0b\x8b\xd8\x15\xb8\x55\x83\xab\x78\xcf\xa5\xe8\xc4\xa8\x75\x31\xbf\xc7\x97\xa5\x38\x1d\x11\xd1\x31\x8b\xba\x9a\xac\xcc\xd9\xe5\x0a\x8b\x76\x9d\xa4\x1f\xc2\xb8\x74\xb8\xa7\x9a\x32\x00\xb0\xe1\x28\x83\xef\x27\xb4\x85\x18\x8d\x8a\x1d\xc6\x7f\xab\x5a\x4c\x21\xdc\xd5\x5c\xc4\x40\xd2\x53\x70\x14\x9a\x37\xd1\x4b\x1b\x3f\xf9\x69\xd8\xb8\x9f\xf0\x9f\x07\x2a\x6d\x19\x5c\x4c\xef\xdf\x24\xd4\x25\x50\xa3\xbf\xe2\x2c\x30\xcd\x03\x8d\x19\x83\x85\x20\xaa\x8e\x74\xc9\x59\xa4\x73\x87\xf6\xfc\xab\xf4\xec\xbc\x77\xff\x13\x54\xec\x51\x5b\x5e\xc5\xee\x7a\x39\x58\x0e\xdc\xbd\xc1\xce\x39\x5a\x18\x94\x01\x39\x42\x49\x3a\x90\x0e\x94\xa8\x9d\x90\xc0\xcd\xc8\xd9\x84\x31\x44\x49\x10\x25\x94\x1a\xb0\x85\xb0\xa0\x0a\x33\x22\xd8\x00\xca\x41\xa5\x1b\x29\x8d\xe3\x89\x3f\xc6\x49\x8c\xb0\x22\xb0\xb0\xd3\xa3\x8d\x59\xa4\x58\x3d\xd9\x6d\x19\x11\x72\x89\xee\x4c\xba\x46\x37\x64\x47\x7a\xfc\x59\x57\xab\x35\x8d\xab\xda\xf2\xd5\x31\xc8\xc9\xee\x27\xfd\xe9\x94\x7f\x11\x43\x69\x56\x35\xeb\xae\xb8\x05\xb1\xe1\x72\x1b\xcc\x8f\x5d\xdf\x36\xf5\xea\xd9\x69\xc3\xc7\x46\xd6\xe8\xf0\x26\xf8\xd3\x8f\x4f\x34\x9d\x98\x26\xcf\x21\x1b\xfc\xbd\xac\xfa\x57\xfb\xf9\x83\x2e\x5d\xb1\x05\x2a\x2e\x55\xf2\xc0\x2e\x55\xad\x06\xc4\xc0\xee\xba\x76\x68\x81\x95\x2a\x2d\xf4\xae\xd9\xd0\x2a\x89\x8b\x34\xdb\xad\xcc\x2f\x6d\x00\x5b\x81\x44\xff\x61\x68\x50\xd6\xc0\x5c\xd9\x2a\x7e\xa8\xc2\x99\x23\x73\x3f\x3f\x3a\x6d\x26\xee\x45\x7a\x12\x15\xb8\x18\x18\x37\xa7\x75\x1f\x6c\x1b\xac\x23\x49\x5d\x31\x08\x0a\xe3\x62\x98\x48\x56\x3b\x79\x15\x8d\x29\x59\x70\x12\x40\x1d\x56\x9e\x8a\x52\x4f\x44\x62\xe2\x34\x6b\x53\x64\x85\x92\x95\xdb\x42\x5a\x01\xfd\xf2\x5e\x61\x2a\x5c\x08\xbe\x5e\x99\xc3\x04\x3b\x58\xe5\xca\xd8\x64\xf4\xca\xd6\x6c\x04\x01\x63\x53\x9c\x78\xce\x36\x84\x99\xe2\x6d\xd6\x8b\x90\xa9\x89\x85\x92\x30\x36\x21\x49\x3a\x69\x30\xdb\xfe\x42\xa6\x36\x6a\xd7\x0f\xdc\x9a\xfb\x02\xbe\x86\x31\x1d\x03\x1d\x34\x16\xe8\x46\x74\xa6\xde\xa8\x26\x04\x19\x6c\xdd\xea\x4f\x3d\x6f\x1b\xbd\x1f\x4b\x2d\x11\x73\x42\xc7\x9c\xbe\x8c\x16\x33\x77\x02\xf6\x88\x62\x6f\x03\xe5\xca\x7f\x4d\x8b\x9c\x38\x41\xdf\x7c\x24\x62\x1a\x17\x41\xfa\xa1\x42\x8e\xc3\xd8\x59\x43\xf9\xcb\xb1\x67\x0f\xc3\xd3\x87\xde\x33\x1f\x64\x31\x01\x67\xd1\x5a\x9d\xcd\x93\x68\x86\x60\xeb\xcd\x86\x21\x85\x70\x12\x65\x04\x6a\xd8\xe3\x38\x00\x49\x58\x35\x03\x41\x7d\xcb\x1f\xfa\x3b\x9c\x96\xa8\xfe\x60\xb9\x10\x03\xdf\xd7\x01\x03\x15\x72\xc8\x04\x15\x6e\x90\x17\x74\x94\x84\x61\xe3\xb1\x54\x78\xc5\xd9\x9d\x5a\xf5\xea\x75\xbd\x15\x79\xa9\x89\x58\x03\x00\x14\x84\x77\x0e\x11\xf8\xe5\xb5\x0a\x56\x8b\x9a\x62\xa8\xc9\x22\xe6\x80\xa8\xce\x58\xe6\x5a\x4c\x33\xd2\xe3\x8b\xd7\xb4\x67\xb8\x06\xad\xd2\x9f\x73\x92\xa4\xa5\x0b\xd7\x4e\xa3\xc1\xc4\x36\xe4\xb9\x4e\xe6\x97\xe2\xa6\x40\x45\x49\x2c\x71\x37\xa8\xd1\x80\x64\x30\x71\xbe\xe0\xb8\xec\x02\x2d\x8f\xb4\x86\x9e\x0c\x77\x2b\x37\xd4\x6f\x08\xb3\x4e\xd7\xc8\x4b\x6b\x77\xc3\xfc\x3f\xb0\xc5\xca\x05\x43\xd7\xe0\xe0\x03\x23\x30\x82\x84\xa6\x23\xe5\x25\xdc\x3a\xfe\x61\x1d\x56\x0e\x12\x4e\x65\xc8\x46\x26\x27\xd3\x33\x95\xc9\x62\x53\x9c\x65\x67\xf5\xc4\x63\xbe\x8b\xcf\x30\xe1\xfb\x73\xf8\x2d\x5c\x26\x1c\x55\x40\xca\x17\x93\xcd\x3a\x8a\x96\xa6\x07\xfc\x26\x95\x8d\x50\x0c\x19\xb8\x15\x39\x5d\x28\x45\x04\x36\xc2\x54\xcb\x75\xb9\x61\xe3\x5e\x6d\xdd\x5f\x6f\xea\xd0\xa3\x1b\x7f\x05\x0a\x4e\xa2\xa5\x17\x71\x05\x15\xa1\x9a\xce\x2a\x23\x08\x5a\x6e\xb8\xe4\x97\xa3\xbc\xed\xd7\x27\xc7\x6f\xdf\x9e\x5f\xf9\x6d\x9a\xd7\x41\x5d\x90\x30\xf1\x8d\x33\x87\x1b\xf5\xcb\x8c\xe2\xdc\x04\xc6\x10\xde\x2c\x4f\x4b\x1c\x82\x0b\xd9\x94\xd5\x4e\x9f\xab\x06\xbc\xa7\xe1\xbe\x18\x2f\x8f\xfa\x5f\x1c\x9a\xbf\xe4\x3d\xcb\x37\x1f\x12\x53\x76\x9f\xf3\xdf\x24\xbc\x2f\x08\xee\x68\xb0\xf4\xfc\x55\x8e\x37\xbd\xa7\x0e\x34\xc5\xe8\xfe\x00\x4c\x7a\x9f\xe3\x68\x44\xb8\x6f\xb0\x57\x2c\x53\x5c\x66\x1f\xb1\x3a\xb4\x69\xb1\x60\x18\xb9\xfb\xba\xfa\x7d\x0f\x01\x8d\x0f\x46\xc4\x3c\xd8\xca\x72\x5e\x6d\x64\x43\xf9\x8b\xfb\x21\xe9\xfc\x35\x30\x0f\x0f\x1a\xa7\x5f\x3f\x76\x3b\x36\x52\xa5\xbd\xa1\x7b\x7a\x6f\x5f\xa5\xac\x5d\x63\x13\xad\x7b\xcf\xe8\x18\xc3\x57\xdc\x34\x7d\x04\xf1\x6c\x54\x1d\xbb\x88\x2d\x44\x15\xe7\x8c\x7d\x40\xb7\x9a\xce\xab\x85\x45\xde\x48\xff\x27\x88\xff\x03\x6d\xb2\x3f\x9a\x1f\xc7\x43\x3d\x25\x13\x8e\xb0\x76\x3f\xe5\x9b\x7d\xac\x2c\xe1\xd6\xb9\x4c\xf7\x28\x41\x51\xd5\x18\x0f\x7d\xd9\x90\x67\x46\xe9\x9c\x07\xcb\x74\xa4\x4e\xa0\x2f\xf0\x3a\xc9\x37\xbd\xe8\x8a\xd3\x00\xfd\xcc\x0b\xd0\x6a\x19\x4e\xb1\xde\xe9\x39\x96\xd3\x2d\xda\x0a\x96\xf5\x92\xce\x9e\x8b\x69\xe0\xb5\xe8\x12\x7d\xbb\x97\x44\xa8\x34\xa6\xd9\xaa\xea\xe9\x8c\xcc\xfe\x53\xb0\xc3\x4e\x68\x9d\xd3\xd6\x03\x9f\x47\xf9\xb2\x94\x51\x51\xde\xa5\x05\x16\x3a\x2e\x96\x0d\x95\x64\xf9\x43\x7f\x4f\x94\x52\x40\xf3\xb8\xe4\xeb\x8a\x26\xab\xea\x8a\x57\xea\x6b\xfa\x43\xbb\xb0\xc8\xec\x31\x5d\x89\x40\x8a\x4a\x54\x21\x23\xa7\x66\x57\x8f\xda\xc5\xe9\xac\xa8\x41\x5c\x30\x2f\x6a\xba\xad\x2a\x6f\xa0\x0d\x09\xe9\x73\x24\xa8\xa3\x23\xf5\x84\x66\xe1\x93\xc8\x2f\xe2\xfa\xf8\xda\x52\x1e\xf2\x99\xed\xd1\x01\x45\xf0\xf0\x36\xf5\xeb\xf5\xc1\xc3\x1a\x6e\x57\x0b\xb3\x41\x51\xc6\x4a\xfe\x54\xad\xc9\x22\xa3\x85\x44\x1d\x31\xcd\x1f\xcd\x79\x62\x8a\x43\x5a\x98\x7b\x78\x29\x9b\xce\x22\x8f\x57\x17\x0c\x31\xe7\xb4\x3a\xee\x3d\x13\x9c\xd9\xd2\xb2\x5a\x75\x0a\xce\xd4\x17\x34\x98\x03\x85\x98\xc1\x77\x24\xb3\x23\x2b\x5b\xe4\xf0\x71\x50\xd5\x2f\xd3\x50\x11\xf7\x55\x09\x28\x0f\xfc\x51\x9e\xbc\x7c\x7d\x05\x6f\x14\x9a\x31\x78\x0f\x98\xcb\x0d\xdb\xe9\xce\x5c\x9d\xcc\x98\xab\xae\x93\x0d\xbb\xae\x80\x78\x5e\x83\x76\xd3\x6e\xd3\x41\x5c\x45\x0e\x98\x5a\x59\xc8\x72\x7c\x6d\x76\x3d\x08\x18\x33\x14\x7e\x2d\x89\x5a\x90\x13\x71\x72\x8d\x6f\x52\xcc\x54\x29\x0f\x1d\xa1\x12\x59\xe8\xc6\x3d\x74\xd9\x2f\x1d\xff\x80\x67\x0b\x9b\xe4\xc7\x8c\x03\x8e\xb5\xc1\xbc\x85\x86\x74\xbc\xb5\x14\xbc\x37\xee\x6e\x32\xd6\xf0\x62\x3b\xdc\xdd\xf8\x84\x40\x6e\xa0\x8c\x2a\x02\x76\x36\x0f\x17\x98\xa7\xff\xf8\xdf\xff\xe7\xf1\x09\x77\xfc\xa4\x6f\x37\xf4\xa5\x62\x59\x02\xd3\x35\x92\x42\x32\x08\x69\xd2\x00\x32\x37\xd5\x4e\x1c\xbf\x17\xa8\xd9\x35\x91\x9e\xff\x6b\x22\x93\xeb\x68\x0f\x24\x0c\x6f\x70\xce\xa0\x0d\xf5\x27\xec\x3b\xfd\xed\x3e\xa1\xec\x37\xce\xa7\xe7\x6f\x48\x9e\xbf\x56\x8b\x8a\x5f\xe4\x2b\xb1\xdf\x7f\xc5\xaf\x3d\x5b\x3a\x8b\xf9\x06\x7f\x24\xfa\x8b\xaf\x7c\x12\x75\x4c\x66\x66\x9d\xf0\x61\x4a\x29\x8d\x0e\x52\x21\x67\xfd\x7d\xcf\xa3\x14\x1d\xc0\xd3\xf4\xcf\xfc\x2b\x85\x4f\xaa\x0e\x85\x19\x96\xe3\x3e\xa0\xe7\x01\x0b\x0b\x2d\x77\xc1\x91\xd5\x7e\xde\x73\xab\x3c\xde\xdd\x6e\xcc\x1c\xd0\x00\xd9\xdd\x26\xd9\xed\xd9\x88\x88\x69\xc8\x5a\xbb\xa0\x14\xf8\x2e\x71\x22\xcb\x22\x41\x0d\xee\x3e\x30\xaa\x03\xcd\x53\x77\xc5\xf7\x6c\x72\x13\x47\x96\xd7\x68\xb1\x61\xf3\x3c\xa7\x21\xab\x40\x9d\x24\x8e\x8f\x2a\xff\xec\xdb\x12\xe7\x04\xfa\x93\x10\x7b\x66\xc3\x33\xbd\x5d\x64\xa7\xcb\x3e\xc7\x6d\x1a\xd2\xed\x6e\x91\xaf\x48\xf3\x95\x56\x84\x03\xc2\x73\xfd\x4c\x28\x9d\x7f\x5f\xd1\x9f\x91\x7b\x34\x3b\x53\x8f\x9d\xa8\x37\xf9\xbc\x44\xf2\x1b\x7c\xd0\x12\xa5\x1d\xa2\xa7\x69\x80\xa6\xcb\xfd\x48\x18\x0f\x55\x2f\xe4\x8e\xaf\x44\xbd\x13\xe4\x26\x51\x3e\x13\xdc\xd3\xb4\x39\x9b\xea\xbe\xcb\xaf\xe5\x27\xe1\x48\xbd\xac\x5f\xc9\x97\x24\xc3\xf0\x5b\x40\x61\xf7\xed\xe0\x21\x03\xea\x9a\xbb\xb0\xef\xc4\x3a\x30\x1b\x77\xc4\x72\x06\x4e\xde\xe9\x62\x90\xbf\x94\x93\xec\x0b\x3e\xc7\x5a\x5a\x0e\xee\x9f\x9a\xed\x9a\x4b\xdf\xd2\xc2\x95\xcb\x8c\x33\xf9\x72\x39\x85\x18\x83\x9e\x62\xef\xd4\x34\x33\xc4\x3f\xe7\xbf\x2e\x95\x88\x52\xd7\x28\xfd\x75\x46\xed\x12\x03\x81\x8f\xb0\xe2\x55\xee\x93\x67\xc3\xb9\x08\xb2\x6a\x16\x44\xe6\xb8\x35\xa2\xf5\x85\xfc\x30\x7b\x41\xf8\x6f\x33\x57\xfe\x84\x7f\xa6\x9b\x51\x2d\x6e\x72\xc3\xb9\x1d\x34\x13\xc2\x50\x53\x93\x60\xd2\x5c\x08\x29\x2d\x6e\xa7\x80\xe9\xd8\x52\x47\xb0\xe7\x94\x10\x92\x56\x54\x31\x0b\xdc\x83\x9a\x21\x83\x4f\xc3\xd3\xc6\xca\x8e\x4f\x38\x85\xe8\xe7\xb8\x9f\x01\x90\x74\x33\x9f\x00\x65\x65\x90\x87\xa3\x81\x0f\x81\x54\x5f\xe9\x98\xce\x70\xf6\xfc\xfc\xd0\xd4\x0e\x27\x48\x32\x33\x92\xb8\x16\xa5\x73\xdb\x00\x10\x64\x16\x8e\x47\x10\x35\xe3\x2a\xd3\xc6\xa2\xfa\x80\xd0\x3e\x9f\x53\xf6\xfd\x02\xd8\x74\x85\x19\x57\x3e\x4b\x50\x67\x99\xca\x5c\xac\xe6\xa8\xca\x30\x8f\xc4\xab\x4c\x04\x46\x41\x84\x13\x1e\x37\x13\x25\x6e\xa5\xa8\x21\xcc\xc1\x9a\x47\x74\xa3\x25\x6f\x99\x5e\x0f\xc1\x1e\xfc\x87\xab\x3e\x50\x4e\xe5\x3b\x48\x75\xe3\x9c\x19\x3b\xf7\x38\xfe\x79\x0c\x93\x12\xf0\xd0\x29\xd0\x4e\xe3\x92\xd0\x46\xce\x12\x84\xeb\x6a\xa1\x9a\xa1\xa9\x42\x32\xcb\x45\x36\xbf\xd1\x32\x32\xcf\x70\x9a\x3c\x50\x64\xcb\x56\x29\xd8\xe2\xb5\xc8\x99\x4b\x98\x28\xd2\xa9\xd3\x35\x7b\x3d\x8f\x73\x66\xbc\x1b\xc1\x6a\x85\x79\x53\x37\x09\xc2\x54\x0a\x90\x73\x7c\x4c\x81\x88\xbe\x54\x35\x1e\xbc\x0b\x88\x7b\x81\xdd\xb0\x4e\x36\xcc\xa6\x38\xae\xc4\x1b\x18\xe6\xb4\x5f\x50\x8e\xad\x5c\x99\xaf\x8a\x7a\xfc\xac\x81\xe5\x29\x7e\xde\xd2\x8e\x2f\x20\x0d\x8d\x4a\xf0\x4a\xc2\x2c\x10\x88\x7c\xa7\xf7\xdf\x7f\xfb\xa1\xe3\x69\xf0\x37\x0f\xef\xbf\xfb\x40\x32\xd3\xfd\xf7\xdf\x7f\xc0\x95\xc3\xa8\x70\xb6\x64\x8d\xdb\xb8\x06\x14\x34\xe8\x5d\x5b\x7e\xaa\x9a\x7d\x27\x52\x21\x3e\x3d\x7f\xf8\x2c\x53\xf1\xb9\x8f\x97\xb8\x73\xfe\x1e\xac\xf0\xc2\x65\xc5\x2b\xbc\xde\x6f\x33\x1d\x63\x27\x1c\xc0\x7e\xb9\xe2\x86\x81\x2c\xe7\x26\x7f\x73\xbf\x79\xb8\x55\xc1\x83\xa5\xce\x9b\xa8\xf8\x2f\xf2\xeb\x19\x06\xc2\x43\xff\xcd\xb5\xd4\x04\x97\x15\x57\xe2\xbf\xce\x52\xba\xbb\x36\xb9\x29\xfb\x59\xcc\x95\x2c\x96\x0a\xba\x1c\x67\x69\x2f\x3c\x88\xce\x1b\x6c\x7b\x43\xf0\xb6\x04\x62\x0c\xee\x1d\x7e\x0e\x32\x6f\xab\xac\x8d\x0a\x28\xab\xf5\x54\xa2\xa0\x03\x5c\x2b\xa6\x64\x1b\xfa\x3a\x34\x49\x7b\xae\x0e\xfb\xf9\x95\xb5\x88\x3c\x41\x52\xeb\xd2\xd5\xb3\x24\x8c\xd7\x0b\x28\xb6\xa1\xfb\xe7\xa1\xaa\x6d\xa7\x40\x7f\x65\x13\xbb\x46\x23\x41\x5d\xe0\xc3\x92\x45\x93\xa4\x86\xfb\x8e\x36\x23\xad\x9b\x26\x9a\x9b\x16\x9d\x09\xe8\xb8\x05\x8e\x6d\xae\x59\x7c\xfd\xc3\x49\x11\x68\x55\x67\x66\xff\xaf\xc7\x06\x62\x96\x6c\xb3\x26\x23\x22\x32\x62\x6f\x54\x55\xe4\x1e\xf4\x9c\x8b\x6e\x07\xcd\x13\x4f\xee\x86\x79\x22\xc3\xd5\x5a\x16\xd0\x94\xfc\x4c\x7f\x1c\x5e\x07\x76\x38\xd6\x3f\x6e\x85\xba\x4f\x7f\x2c\x49\xf6\x45\x5b\x74\x7e\xdf\x8e\xf3\x17\xcd\xa6\xf1\xfb\x3a\x7e\x0d\x01\x44\xb1\x7a\xbf\x18\xc8\x66\x92\xed\x69\x5b\x57\x2f\x27\x0c\x76\x1e\x81\x9c\x18\x8c\x64\x0c\xac\xcc\xe2\x4c\xe7\x90\x22\x1d\x84\x5b\x8a\x45\x3c\x18\xd7\xa2\xb6\x5a\x00\x75\x9a\xdd\x49\xb0\x29\x15\xbe\x48\x19\xa1\xca\x9e\x65\xf6\xd0\xd6\x81\x2f\xad\x03\x2d\xbe\xd6\x7c\x58\x69\x3f\xdd\xb4\x3f\x85\x4b\x4f\xef\xb8\x1d\x94\x43\x10\xaf\xa8\x5d\x4e\xa4\x27\xc6\x2f\x7a\x96\xe0\x14\xb5\xf9\xe9\xa6\xe1\x6c\xa0\x06\xdc\x5f\x37\xa9\x3b\x85\x21\x4c\x19\x6f\x04\x79\xca\x85\xcd\x15\x0f\xe4\xaf\xe5\x67\x83\x6a\xe1\x4e\xfd\x14\x66\x76\xc3\x06\xb5\x85\xa7\xa9\x7e\x69\x7e\x74\x40\x1c\x1e\x0c\x15\x86\x78\x73\x5b\x76\xfb\x0d\xf6\x00\xdc\x6a\xca\x8f\xa5\xdc\xc7\x19\x10\x22\x3d\x89\xf6\xc1\xda\x0a\x18\xb9\xc4\x81\x52\x33\x0b\xce\x9d\x97\x8b\x1c\x16\xb5\xf0\xc4\xaa\x59\xe9\x90\x17\xc1\xe8\x09\x84\xe3\x56\x58\xfd\x6c\xa2\x1c\x46\xef\x62\xb6\xe5\xaa\x37\x25\xcb\x00\x53\xf3\xb2\xbf\xe6\xa9\x13\xb3\x07\x46\xae\x68\x30\xba\x1f\xc2\xcd\x98\x38\xd8\x13\xb4\xf1\x84\x77\xe4\x42\xb9\xd9\xbf\xe0\x87\xf0\x34\x45\xe5\x40\x5e\x0f\x8f\xbd\x0a\x82\x05\x6d\x93\xca\x14\x07\xad\xf8\xb6\xa4\x46\xb1\x8b\x17\x76\x84\x14\xde\xfa\x23\xfb\xcd\x19\xf3\xc4\x37\x11\x31\x9b\x35\x68\xfa\xf7\x2e\x5d\xeb\x47\x4d\xba\x59\x5b\x33\x92\xf6\x8f\x55\x4f\xa5\xff\xd3\x07\xa3\x51\x92\xf6\xb3\x90\x5d\x82\x3e\xfd\xcf\x08\x6a\x78\x72\xf6\x79\xa2\x18\x06\x41\x95\x66\xf2\x58\x68\xbe\x6e\xac\x44\x2a\x82\x1a\xe7\xd5\x20\x19\x7a\x67\x17\xce\x24\xf5\x9a\x4e\xf1\xbc\xd6\x15\x9b\xee\xea\x6a\x16\xa1\x06\x52\x6c\xeb\x5b\x62\xaa\x71\x39\x57\xa3\x6a\xdd\xe2\x56\x98\x78\x6d\x4b\x15\x1c\xe7\x8a\xd6\x87\x5d\x58\xd2\x2f\x77\x35\x31\x5d\x97\xc2\x16\x7b\x6f\x85\xce\x58\xa4\x42\xd0\x6f\x05\x2c\xcb\xfa\x5e\x75\x19\x2c\x95\xc4\xd2\xf9\x4a\xcd\x8f\x36\xd5\xa2\x4f\x5d\x3a\x35\xa7\xb6\xe8\xb0\x57\x59\x49\x58\x1c\x67\x78\x4e\xfb\x61\xb7\x46\x9c\x0d\x06\x58\xd2\x89\x71\xdb\x40\x50\x73\x2c\x22\xaf\x33\x68\xf8\x31\xd4\x30\xf8\x06\xeb\x70\x0d\xb9\x0c\xf1\x78\x80\x61\x51\x7e\x0d\x86\x1b\x54\x0b\xe5\xf9\xa1\x9a\x1f\xf4\xb7\xd7\x6d\x5c\x40\x7c\x44\x78\xd1\xdb\xd0\x3b\xb7\x7c\x4d\xfd\x71\xb0\xc9\xa9\x78\x6c\xa1\x9a\x8d\xe6\xa1\x91\xd0\x0d\x30\x01\xaa\xfa\x88\x68\x86\x6c\x05\x04\x34\xb5\xba\x41\xb4\xfb\x5a\x17\x21\x4a\x41\xd3\xc7\x64\xfe\xdb\x78\xbc\x4a\xcc\x87\xc6\x1a\xb2\xad\x5a\x76\xe6\x88\x9e\x1e\xfe\xcb\xfd\xe2\x91\x2c\x64\x98\xff\x8c\xae\x60\x38\x51\xd0\x19\xee\x82\x3c\xe6\xaa\x83\x23\x36\x93\x0d\x6f\x16\x0c\x44\xdf\xb3\xdf\x92\x40\x19\x17\xa8\x8d\xfc\x31\x3b\xc8\x9e\xd0\x09\x04\xb9\xd3\x7a\x81\x21\x40\xe1\xb5\x2d\xf7\xbb\xa8\xed\x26\xa3\xe5\x91\xe9\xa1\x8d\xb6\x14\x5e\x2c\xf8\x35\xec\x82\x9d\x56\x86\x55\x3b\xb9\x3f\x1e\x11\x6d\xdf\x73\xde\x47\xc4\xbd\x58\xf8\x74\xa0\x80\x24\x12\x50\x97\x12\xbd\xe6\x56\x19\x20\xaa\x7e\xc0\xe6\x27\xb1\x63\x92\x1b\x7c\x60\xc3\x8c\x89\x9b\xc0\x30\xd7\x0f\xfa\x94\x46\xcc\x2a\xbf\xf4\xa1\xc5\xe6\x7a\x14\x0f\xb2\x14\x7b\x6a\xfe\x1b\x66\xb8\x50\x2a\x5a\x55\x26\x53\xaf\x35\xa2\x72\x4d\xf1\x21\x46\x8e\x9c\x07\xe9\x83\x1b\xfa\xf7\x78\xbb\x7d\x5c\x14\x0f\x26\x46\x1d\x48\x3f\x6e\xd8\x03\xa3\x30\x55\x34\x0c\x58\x65\x50\x53\x20\x4a\x4e\xe3\x8e\x01\xa2\x79\xfa\x85\xa5\x80\x92\xef\xd8\xd2\xc2\xe3\x4d\x48\xd7\xcf\x5d\xc7\x5b\x40\x43\x0c\xcf\x1b\x9a\x30\xab\x10\x4f\xc3\x70\x24\x03\x21\x3c\xc8\x1a\x38\x50\xdf\xda\x3d\x77\x17\xa3\x12\x1d\xb1\xef\xed\x01\x94\x48\x3c\xbd\x83\x08\x09\x84\x5f\x8f\x54\x27\x00\x4f\x00\x4e\x89\xbf\xbe\xed\x7f\xa6\x08\x3c\xd5\xf8\x14\x09\xdc\x25\x04\x4f\xc5\x6e\xb5\xb4\x99\xd0\x37\x1c\x58\xe4\xcb\x67\x05\xf1\x60\x54\xce\x08\x7e\x7b\xb0\x75\xd3\x7c\x94\x98\x38\x73\x7c\xfa\x9c\x15\xc7\x82\x94\x4c\x8e\x7a\xf8\x2a\xce\x25\xd1\xb2\x5a\x84\x31\x62\x9f\x73\xc2\x44\x17\x0b\x9e\xe3\x36\xfb\xbb\xa8\x1d\x4f\xf1\x2b\xfd\x9f\x4c\x18\x0e\x44\xbd\x4f\xce\x2d\x06\x12\x1b\x46\xf8\x5c\xf5\x1b\x08\x9a\x52\x37\x87\x71\x5b\x6a\x58\xcf\xec\xfc\xcb\x7c\x43\xa6\x7c\x42\x62\x47\xd5\x99\xaf\xdd\xb9\xba\xf1\x05\x90\x7e\x9e\x9b\xb7\xdc\x18\xcc\x5d\xe6\x7a\x0f\xb9\xf8\x02\x8a\xcd\xda\x6a\x31\x88\x87\x97\x1c\x5f\x15\x35\xd8\xb0\x42\xd7\x3c\xa2\x3b\x17\x66\x51\x0f\xd5\x38\xe8\xc3\x48\xac\x0b\xba\x87\xf8\xc2\xf0\x93\x65\xc9\xac\x93\xab\x7b\xf5\xf7\x13\xaf\x11\x51\x06\xe4\xee\x80\xde\xc1\x52\x61\x62\xeb\x83\x9a\xdb\xc5\xa7\x11\xb7\x13\xeb\x2a\xf2\x82\xe9\x1d\xb8\x4a\x01\xd3\xc1\x6d\xf8\x00\xd0\x90\x72\x4e\xfc\x43\xac\x18\xa5\x58\x1e\xb9\x1a\xf6\x81\x92\x4a\x2c\x8f\xf8\x5a\xce\xf5\x88\xd9\x53\xd9\xf6\x70\xf2\x1b\xa3\x9d\x9d\x0e\x68\x01\x65\xdf\x52\x33\x8f\x21\x2b\x49\x60\x48\x0c\x42\xd6\x5f\xb5\x0c\xf0\x01\x63\x4c\x36\xae\xfc\x54\x15\x7b\xa2\x3e\x9e\x8b\xdb\xea\xfd\x2e\xae\x97\x56\x3c\x2e\xe1\x0f\xd6\x3d\x98\x4f\x08\x11\x2e\x6a\x30\xbc\x3c\x97\xde\x79\xb9\x9b\x6a\x99\x99\x90\x53\x68\x28\x0e\xe0\x84\x9c\x7a\x2f\xbe\x90\x55\x09\x1f\x82\x39\x98\x58\x6c\x9b\x98\xf4\x43\x3a\x9a\x8f\x18\x5b\xe2\x19\xea\xa4\xaa\x3b\x0d\xd2\x46\x84\x30\xc0\xd2\xa0\x3e\x20\x2c\x30\x1b\xb3\xd9\xe7\xe1\x73\xb4\x39\x8d\x68\x6c\x67\x80\x90\x24\x24\xa2\x01\x02\x2c\xb3\x9d\x01\x1d\x15\x8e\x94\x07\x1f\x39\xc5\xa9\xf8\xc3\x05\x16\x86\x9e\x07\x36\x11\x66\x07\x7d\x85\xdd\x81\x20\xe0\xf5\xa8\x69\xef\xa6\x77\xe4\xad\xa3\x9c\xd9\x08\xcb\x9d\x6c\x88\x56\x17\xe5\x8e\xc3\x5d\xb2\xee\x72\x29\xbb\xad\xf0\xfc\x3b\x5a\xfd\xee\xb6\x56\xc5\xa8\x6b\xaa\x59\x33\x6f\x54\xbf\x77\x2c\x5a\x8e\x51\x7d\x47\x6b\xdf\x5b\x6b\xe1\x96\xf5\xb1\x2c\x77\x41\x13\x71\xf7\x03\x77\x0f\x30\xcf\xd8\x6a\x6b\x82\xa3\xa9\x47\xa3\xb9\x40\x1f\x60\xe2\x51\xb8\x16\x6f\x09\xa0\xbb\xd9\x1d\xde\x5f\xe3\x05\x62\x5a\x4e\x04\x56\x87\xa6\xd3\xc1\xb0\x96\x27\x0b\x38\x37\x0c\x6e\x8d\x25\x4f\x54\x25\x86\xbc\x03\x53\x25\xef\x13\xed\xba\x66\x05\xda\xc3\xdd\x8b\x6d\x35\xd3\x09\x1b\x4d\x07\xca\x86\xf0\x21\xb1\xa6\xe2\xfe\xc0\xe3\x39\x09\x92\x0f\x17\x18\x38\x1d\x44\x75\xc5\x4e\x07\x41\x07\x85\x8a\x0e\xd5\x73\x32\x59\x87\x52\x5e\x38\xb5\x1c\xfa\xa0\x82\x93\x7b\xa6\xe1\xc4\xf4\x51\x80\xa1\x97\xb9\xe6\x5e\xaf\x9b\x20\x7a\x8c\x78\x42\x60\x2f\x0a\x3b\x32\x8b\xc7\x7a\x2d\xe2\x89\xe2\x45\x85\x95\x81\x14\x63\x7b\x8b\x89\x32\x38\xf2\x6e\xf7\xb4\x75\x6e\xaa\x8f\x50\x86\x11\x61\x4a\x7c\xe1\xf3\xcb\x2b\x68\xc0\x68\x01\xd0\x3e\xba\x62\xbe\x9b\xfe\x75\x5d\xd6\x08\x79\xc9\x61\x7e\x95\x11\x2d\x16\xec\xc0\x54\xd5\x1a\x0f\xf0\xba\x34\xb3\xf2\xba\xd8\x08\xdb\x0a\x5d\xdc\x4c\x7a\x10\x4d\x58\x8a\x50\xbe\xbc\xd2\xba\x5d\xb9\x20\x99\x78\xc6\x37\x5b\x6d\x8d\xf7\x11\x5c\xd4\xf6\x5b\x4d\x7f\xdc\x48\x60\x83\xc3\x0a\xb3\x00\x2d\x8a\x92\x50\x01\xac\x7b\xf0\x08\x3d\x43\xd0\x29\x29\xd8\x30\x7c\x9b\x0c\x0c\xfe\x2a\xd6\xcc\x15\xb3\xeb\x54\xcd\x45\x6e\x73\x12\x39\xd8\x87\x30\x1e\xa3\x34\x7d\x87\x28\x3c\xac\x6a\xe6\xf5\x0a\xa6\x4c\x98\x00\xe9\x76\x8d\xd8\x7a\xbe\xd3\xcf\x31\x90\x9c\x96\xb8\x27\xaf\xe4\x6b\x0c\xb2\xd3\xc8\x4a\x2e\xc6\xd2\x18\x64\xde\x14\x7c\xfe\x79\x4e\x7f\xc6\x42\xb4\x0b\xc6\x6f\x92\x34\x88\x73\xc7\xde\xf9\x72\x91\xcc\x19\x84\xee\x72\xb3\x94\x48\x0b\xac\x39\xc2\x71\x4f\x94\x7d\x6c\xd5\x2c\x71\x44\xd9\x84\x0c\x15\xa8\xaf\x1d\xdc\x48\x10\x44\x2d\xd4\xe4\xa9\x5f\x6e\xe8\x68\x39\xec\x13\x2e\x26\xac\x5f\xaf\x45\x06\xc1\x34\xe0\x70\x2b\x11\xef\x8e\x78\x6b\xe1\x73\xa1\x5d\x15\xda\x06\xb4\x93\x28\x77\xec\x21\x45\x44\x8d\xe0\x25\x06\x22\x32\xac\x58\x5d\x05\x06\xc6\x16\xf9\x1b\xc4\x06\x84\x8d\x7b\xa4\x06\xe1\x8c\x20\x31\x05\x1f\x41\xf8\x8b\x4c\x00\x99\xeb\xd5\x70\x9f\x51\x70\x7f\x56\x78\x15\xad\x87\x80\xa3\x44\xaf\x24\xa0\xa3\x1a\xbb\x4e\x34\xb9\xcc\x29\x4c\x91\x1b\x28\x4c\x19\x57\x6c\x87\x19\xac\x6e\xde\xa6\x49\x38\x12\x29\xba\x2d\x57\x79\x5b\x58\x48\x17\xe5\x34\xec\xd6\x02\x8e\x12\x7a\xf0\xe6\x1b\x3a\x7c\x6b\x15\xc4\x19\x09\xe4\x23\x5b\x3e\xd1\x7c\xb3\x8c\x63\xfa\x06\x96\x16\x0b\x61\x63\xec\x44\x48\xcc\x65\xbf\x63\x7e\x23\xcc\xcb\xda\xc1\x90\x1f\xfe\xe9\xf2\xfc\xed\x51\xfa\xf9\xf1\xf5\xf5\xf5\x63\x2e\xfe\x78\xdf\x6e\xd8\xdd\xae\xe0\x90\x31\xff\xe3\xec\xcd\x51\x5a\xf6\x8b\x47\x33\x3a\xa8\xb7\xb1\x7a\x4b\x0d\x4e\x71\xf5\xc0\xd4\xc5\xa2\xe3\x1f\x67\x4f\xba\x62\x34\x02\x7b\x18\x99\x2c\xdc\x20\x79\xf6\xcc\xbe\x43\x27\x53\xec\x3c\xfc\xe1\xb0\x5c\xb4\x25\xcc\x23\xf0\x11\x64\x6c\xe8\x4c\x30\x15\x2a\x60\x08\x52\x51\x3b\xda\x8d\xd7\x0b\x8d\x00\x3f\x00\xb1\xdb\xc0\x13\xdc\x03\xba\x4c\x4c\x9c\xdb\x56\x38\xf6\x5d\xb7\x6e\xf6\x9b\x22\xe6\x98\x84\x33\x9d\x88\xb2\xf8\x69\x58\x18\x96\x8c\x08\xe1\xfc\x34\xfd\x13\x6b\x8a\x78\xa2\x84\xb6\x38\xcb\x68\x0b\xc0\xb3\x61\x61\x04\x23\x0c\x04\x63\x1a\x80\x04\x59\x34\xc1\xdc\xe7\x39\xe1\x7c\x54\x89\x9e\xdf\xd8\xae\xa2\x4f\xb7\xee\x3c\x07\x5a\x93\xea\xc6\x45\x62\x3d\xdd\x74\xb6\xe1\x45\xec\x19\x8f\xd4\xd2\xd1\x94\x58\x53\x78\x48\xc5\x8c\x73\x12\x45\x01\x7f\x04\x28\x73\x91\xd0\xae\xd4\xaf\x5d\x30\xa6\x54\xa3\x6f\x96\xc3\x8c\x20\xbc\x42\xd9\xc3\xb1\x7d\x6a\x2d\xca\x89\xda\xcd\x9a\x5f\x3d\xc6\xdf\x74\x87\x13\xc9\x44\x7c\x81\x22\xee\x01\xd6\x11\xcb\x5c\xd7\xc3\x5d\x6c\x28\x6e\x29\x6f\xf2\xa2\x8c\xf2\xa6\xd1\x76\xad\x80\x83\x36\x46\xbb\xa4\x4a\xc7\x63\x99\xdf\xb7\x70\x48\x20\x10\x2b\x9e\x4c\x47\x69\x11\x66\xe0\x50\x79\xea\xd2\x62\xf1\xca\x56\x29\xf8\x6e\xbc\x44\x19\x21\xb2\x8e\x42\x8e\xca\x82\x5a\x7c\xe7\xcf\x20\xf0\x03\x66\xff\x03\xb3\xd5\x1f\x79\x41\x87\x22\xb4\xd4\x6a\x1e\x6d\xe2\x79\x37\xc8\x1c\x3e\x79\x31\x5c\xd9\x24\xab\xc9\xa3\x49\x27\xf2\x15\x62\x6b\xb7\x69\x6e\xcc\x51\xfc\x14\xbf\x34\x92\x4c\x38\x32\x0f\xa6\x83\xf2\x90\x81\xf2\xa5\xc9\xe2\xea\x7e\x0d\x22\xa7\xaa\x88\x5b\xf3\x79\x17\x45\x09\x26\x3c\xc6\x44\x0a\xef\x89\xee\x4d\xf8\x1a\x3b\xa8\xa1\x57\xf4\xa9\x6b\xe1\x80\x57\x74\x5c\x34\xf4\x8c\x0e\x8a\x7e\x81\x67\x74\x8c\xa4\xb1\xdf\xb3\x1f\xea\x17\xb8\x3e\x4f\x0d\x7a\x2c\xd7\x4e\x21\x7e\xa2\xc0\x94\x74\x5b\x84\x63\xfb\x02\x17\xe8\xc1\xc9\xf6\x4b\x04\xdc\xa9\x9e\x78\x94\x04\xc8\xbd\x4b\xe3\x5b\x54\xcb\xe5\x6c\xde\x36\xd7\x1d\xfb\x19\xe3\xfd\x08\xe6\xb2\xfc\x3b\xbd\xc4\x6f\x01\xe1\xab\x7e\x10\x85\x7c\x48\xa2\x5a\x14\x3d\xd5\x9b\x3d\x49\xc4\x3d\xeb\x30\x6a\xfd\x29\xe5\xc8\x9d\xeb\x5b\x3a\x89\x1d\x5b\xce\x4c\x8a\xd0\x46\x77\x9d\xf1\x17\x3c\xa4\xa1\x7d\x66\x65\x29\x0a\x5d\x72\x8a\x82\xf1\xa7\x21\xdc\x76\x25\x18\xb3\xc9\xad\xb4\x88\xaf\x5e\x73\x04\xc2\x32\x38\x02\x23\x62\xa8\x20\x9f\x7a\x90\xd0\x13\x92\x20\x0c\x97\x1e\x42\x11\x84\x45\xff\xfc\xf5\x5b\xf9\x09\x0b\x75\x8d\x4c\x04\x13\x75\xbe\x1c\x4f\xcc\xee\x7d\x36\x65\xff\x6e\x79\xe2\xab\x20\x6a\x0e\x7b\xf0\x0d\xbf\x1c\x44\xd1\xe6\x4b\x5c\x03\xf1\x5f\x97\x4a\x32\xb0\x2f\x76\xd1\x96\x8f\x87\xc5\x08\x39\x82\xea\x4b\x7c\xb8\x74\xbd\xc6\xe1\x3f\x2e\x2d\x87\x89\xc6\xd3\x60\xe4\x1e\x23\x66\x0b\x40\x74\x77\xbf\x4b\xbb\x8a\x75\xa7\x4a\xa0\x83\x06\x41\x1d\x3e\xa4\x30\x68\x07\x4f\xa0\x19\x04\xed\xd0\xce\xd9\x99\x36\xeb\x5a\xfc\x2d\x2d\x0f\xc7\x56\x8b\x8e\x16\x95\xf1\x71\x85\x4d\x1b\xec\x3d\x31\x28\x1f\xbb\xff\x22\x74\xf0\x60\x51\x80\xc3\x3f\xb0\x3a\xa8\x5b\xcf\x86\x13\xe1\xd4\x99\x8a\xb3\x14\xbf\x1d\x94\x49\x86\x4c\x2e\xd9\xb6\x08\x84\x43\x21\xa0\x70\x5b\x39\xcb\xdb\x8f\xfc\x96\x02\x0c\xc8\xac\x82\xeb\x56\x23\x5a\xf1\xdf\x70\xc6\xf4\x25\x8f\x0b\xf9\x1a\x35\x18\x1b\x7d\xa3\x34\xf4\x01\xc6\x4c\x5d\x01\x96\x66\x45\x24\x7b\x23\x5f\xf2\x4c\xdd\x90\x32\xc6\x6e\xff\x94\xf7\x78\x38\x6f\x01\xbc\x43\xf4\x5f\xcb\xff\xf8\x5f\xff\x97\xd5\xa5\x0d\xed\x96\x88\xd1\xa1\x61\x2e\xfd\xbc\x9b\xc3\x9c\x7f\x7b\xe6\x31\x78\x74\xd0\x11\x41\x7f\xaa\x61\x2f\xe8\x6b\x44\xa3\xfc\x1c\x83\xd1\x37\x9b\xd1\x0d\x88\x1c\x87\x44\x4f\xe6\xb8\x7a\x1c\xd6\x61\x44\x95\xe9\x1e\xe1\xc2\xec\xd9\xe4\x62\xd2\xd0\xf3\x5e\x89\x6e\x7a\x4b\x49\xde\x37\xed\xea\x83\x0f\xc6\xea\x26\x22\x0a\xc4\x8a\x93\x21\xc3\xb8\x70\x66\x07\x00\x7d\x88\x33\x5f\xa3\xa1\xf7\x25\x13\xeb\xf8\xb1\x30\x39\x97\x4b\x34\x6c\x18\x89\x99\x0f\xf1\xcc\x3c\x9b\x24\x96\xa3\x5e\x60\x47\xaf\x38\xc2\xe1\xc8\x54\x96\x26\xdd\x15\x89\xde\xac\xb2\x7f\x0c\x7f\x70\xb8\x4d\x7e\x77\x8e\xa9\x4a\xee\xc4\x5e\x23\x81\x96\x2b\x12\x10\x4a\x16\x7e\x41\xfc\x37\x41\x00\x3f\xd5\xab\x71\xaa\x7e\x69\xfa\x20\x48\x60\xf4\xd6\x42\xe0\x89\x85\xe0\xa1\xd1\x03\x0a\x5c\x39\xb0\x32\x71\xa9\x3e\x0a\x29\x0b\x14\x22\xf5\x36\xe8\xc8\xdf\xf7\xc1\x06\x17\x29\x1a\x91\x0a\xea\x69\x36\xd6\xaa\x45\xe4\x03\x25\x20\xa0\x69\x1d\x59\x8f\x76\x33\xdf\x4c\xb0\x12\xd6\x72\xe3\xee\x8b\xe1\x19\xa3\x39\xad\x89\x9f\x04\x3e\x72\x78\xd4\xc3\x5a\x2e\x0e\xf6\x92\x9c\x6e\x48\xee\xdf\x44\xa7\x37\x54\xc4\x12\xda\x4f\x07\x9c\x5d\xc7\xc1\x7f\xbf\xde\xdd\x75\x5c\xc7\xed\x0e\xaf\x7f\xf4\xb2\x77\x3a\xb0\x5f\xa8\xa2\x1a\x44\xf8\x73\x59\x53\xa1\xfe\xfe\xc8\xd5\x6b\x0c\x1a\x08\x3e\x11\x0a\x5c\x4d\x5f\xaa\xe2\xd7\x1b\x5d\xa2\xd4\x7f\xec\x42\x37\x0e\x79\x3b\xec\xf5\x28\x26\x5d\xd4\xe9\xaf\x8b\x4d\x77\xf0\x66\x34\xe2\x15\xc3\x23\xdb\x28\xc0\x04\x06\x78\x6b\x91\x38\xdc\x44\xd8\x61\xa7\xa4\x0b\x6e\xda\x54\x6f\x2f\x81\x26\x44\xf3\x7c\x77\xb4\x89\x03\x57\x19\xb7\x85\x9d\x18\xf6\x92\x79\x8c\xf3\x8d\x08\x3b\x79\x6b\x89\x70\xd7\x8c\x6f\xc3\xff\x91\x50\x14\xd3\xd7\x05\x7c\xa4\xbb\x36\x4d\x16\xf6\x58\xc3\x9f\xd7\x0f\xf0\x91\xc0\xf0\x25\x67\x06\xcf\x68\x3d\xe6\xf6\x78\xef\xae\x1f\x76\x9a\xc3\xea\x08\xd7\x9e\xe9\xed\x98\x45\xa1\x1a\xa4\x7b\x96\x07\xdb\x64\xbd\xff\xf3\x40\xf2\x1b\xd2\xcb\x64\xce\xb0\x7c\xdc\x46\xec\x0a\x60\xa9\xee\xc6\xe6\x0c\x1f\x2e\x9d\xb0\xb6\x28\x11\x21\xe0\x44\xbe\x5c\x8e\x1e\x9d\x34\x6c\xb5\xef\x84\x84\x24\x86\xfb\x4e\x90\xaa\xbb\x9d\xe2\x9a\x5d\x91\x69\x52\x6e\x76\x3c\x85\x79\xea\x94\x77\xec\x89\x2e\xbb\xa0\x48\x8f\xda\x2b\x08\xbc\x3f\x0c\xeb\x92\xd7\x65\x74\xd7\x7c\xdb\x5c\x27\xb2\x65\xce\xe0\x91\x20\x21\x74\x35\x25\xee\x92\xa4\xb1\xc8\xa1\xf1\x8d\x52\x09\xe4\xa0\x11\x70\xc6\xf9\x83\xa8\x01\xd8\x31\x5c\xbc\x00\x8b\x88\xcd\xf2\xa4\xfa\xbe\xd4\x72\x63\x12\xbb\xd1\x4b\xad\x90\x47\x7d\xb3\x22\x58\x46\xed\x86\x10\x5f\xd2\x30\x1e\x13\x1e\x36\x77\x64\xea\x2a\x04\x4c\x54\x3d\x9a\x44\x86\x93\x56\xf4\x85\x58\xeb\x87\x7b\xa4\xcd\xf7\x23\x84\xf8\x92\x7e\x70\x2b\xb0\xf1\xc6\x24\xde\xd6\x1f\x3a\xeb\x69\xfc\xc7\xe8\x5e\x7e\xd8\x45\xef\x9c\x7e\x15\xec\xd3\xb0\x05\x29\x06\x72\x07\x6b\x87\xc7\x1b\xa6\xe4\xc8\x9d\xed\x84\x68\x20\x26\x3b\x93\xa1\xa1\xee\x5e\xe2\x30\xa6\xe7\x92\x0e\x34\x30\xc6\xf1\x60\x93\xbb\x8e\xf4\xcb\x8b\x72\x90\xad\xce\x54\x9e\x93\xcc\xbb\x37\x5c\x81\xb3\xa0\x48\x22\xd7\x85\x5b\x06\x04\x3b\x9b\xc9\x42\x9e\x07\x70\x6b\x9c\x59\x5d\xd0\xea\xb8\x32\xc7\xaa\x01\xe5\x58\xf4\x18\xce\x78\x67\x28\x95\x05\xba\x53\x66\xca\x47\x26\xab\x3a\x4b\x01\x40\x6d\xf3\x9b\xc8\x16\x07\xa6\xc3\xdb\xf8\x39\xdd\x5b\xd4\x2d\xe3\xae\xf8\x5d\x5b\x62\xee\x3b\x82\x39\xa8\x63\x99\x85\x4b\x7d\x4c\x20\x9e\xec\x56\x2d\xfc\x0c\x6c\xae\x99\x59\x04\xa4\x80\x0a\x7f\x70\xa3\x74\xaf\x38\x7a\x6e\x80\xcb\x60\xaa\xe8\xc1\x6d\x4c\xe1\x2b\x3a\x00\xb6\x71\x7b\x0f\xc0\x16\xc4\xbb\x8c\xba\x11\xb0\x80\xdb\x3a\xa2\xcf\x2f\x7e\x79\x47\xc0\x37\xbe\xb0\x23\x47\xd6\x0b\x8d\x57\x5d\x14\x93\xeb\xff\xb6\xfe\x0d\x8e\x39\x20\xce\x28\x20\xfa\x80\xe0\xa3\xe7\xe2\x1d\xd1\x07\x56\x69\x56\x2d\xec\x1f\xd4\x4a\x4e\xb7\x33\x5f\x55\x4d\x53\x88\x93\x69\xdd\x87\x96\x74\x71\x10\x12\x36\xe2\xea\xdb\x1b\x15\x49\x78\x70\x71\x0c\x14\x6f\x40\x23\x87\x2f\xdc\xe8\x4a\x90\xfc\xf7\x40\xfb\x87\xc4\xbd\x00\xc8\x4b\xd9\xbe\x13\x79\x1a\x54\x6e\xb5\x10\xa6\xdc\xc7\x27\x4f\x87\xf1\xca\x6f\x8d\x15\x1f\xc7\xc8\x1f\x3e\x96\xd0\x49\x48\xb1\x95\xc9\x72\xf6\xee\x62\xa2\x86\x43\xcc\x58\x6f\x08\x07\x5b\x3c\xba\xbb\xe0\xb0\xc1\x4d\x5d\x89\x89\xca\x99\x7c\xd1\xd8\x13\x8c\x29\xdb\x49\x14\x06\xbc\x99\x2f\xa1\x1c\x35\x85\x43\x39\x26\x7d\xd3\x43\xa0\xb8\xe2\xbf\x3f\xa4\xf7\x8b\xc4\x0f\x1d\x8a\x44\xd6\x27\xa9\x94\x20\xdf\x41\x7e\x10\xd8\x1c\x66\xeb\xad\x85\x6a\xf7\x35\xa0\x9b\x50\x57\xee\x83\x6e\x6b\x27\x51\xe9\xbe\x9b\x6a\x31\xe3\x4b\xd0\x54\xaf\x80\xdd\xd3\xeb\xcc\x41\xf8\x35\xb4\x82\x5f\x43\x93\x87\x5a\x8f\x82\x84\x68\x42\xc2\x8c\x9d\x8b\x2f\x1a\x25\xc7\xbb\xa2\x4f\x47\x04\x97\x38\x89\xc3\xb6\x44\x09\xf9\x62\xd4\x8a\x29\xab\xc3\x34\x33\x87\xf3\x29\x66\x18\x17\xd5\x1e\xc7\x98\x0c\xb3\xc4\x9a\x30\x4a\xd2\x67\x20\xe3\x91\x88\xfe\x34\x4c\xdb\x34\x2b\x7e\xcf\xc0\x9e\x19\x0e\x86\xa7\x82\x75\x5c\xa7\x59\x46\x47\x55\xc0\xc9\x32\x4c\xc1\x1d\x56\x9f\x77\x71\x69\xac\xcf\x30\x41\x1d\xd4\x47\x80\x74\xd0\xce\x17\x6b\x75\xd4\x99\x20\x24\x3b\x2f\x3b\x62\x92\x43\xf3\x14\xa4\x3c\xd5\x99\xda\xc3\x9c\x93\x30\xfc\x08\x3b\xde\x41\x0d\x72\xd9\xd3\x80\xdd\x5a\x10\x86\xb3\xd1\x40\x56\xec\x76\xe0\x42\x6e\xa6\xe7\x58\x8e\xdd\xad\x85\x82\x2d\x8e\xc3\x1b\x68\x98\x4f\x2d\x29\xe2\xc8\x2d\x7b\x9d\xaf\x59\x77\x4d\x35\xee\x08\x9e\x6d\xeb\xbc\x10\x91\xc3\x0d\x4a\x2f\x59\x2d\xfb\x8b\xea\x18\xf4\xd2\x43\xb8\x6a\xbe\xbe\xab\x50\xa9\x71\x7c\x18\xea\xcd\xa0\x93\x11\xcf\x33\x90\x3b\x6a\x18\x74\x71\xb2\x8a\xaf\xe8\x24\xbf\x1d\xbc\x5a\xb8\x17\x4f\x4f\x39\xbc\x73\x3b\x67\x8e\xc7\x1b\x5c\xb9\x30\x0f\xaf\x48\x2d\x37\x5d\xfc\xb6\x9e\xa1\x43\x7c\x20\x9f\xaa\xfe\x50\xdf\xda\xb2\xbb\xa9\x17\x19\x1e\xbe\xed\xd6\x7a\x29\xf9\xae\x14\xbd\xf8\x83\x19\xa5\x3d\xc9\x35\x98\x5a\x89\xeb\xbb\xee\x81\xbc\x00\xf0\x70\x41\xe9\xb0\x15\xa6\xfd\xef\x31\x78\x22\x4a\x9b\x74\x47\xb2\x5b\xff\xe8\xd6\x86\x06\x63\x09\x18\x62\x80\xdb\x16\x5d\xe1\xa7\xfa\xbf\x60\x04\xc1\x85\x78\x38\x0c\x26\x03\x5d\xfd\xe0\x15\xe1\x83\x35\x8c\xb8\x87\x6c\xdc\xc0\x01\xbb\xd9\x72\x43\x2d\xa2\x74\xb7\xb3\x87\x8d\xf5\x9a\xea\xc0\x80\xc2\x76\x6f\x99\xa1\x07\x51\x2f\xee\x1e\x63\xb8\x09\xc9\xe3\xf5\xfb\x1d\x5b\xef\xa6\xee\xd5\xfa\x5f\xf0\x3b\x64\x0a\xf2\xa6\x40\xb6\x6a\xda\x86\xa6\x47\x82\xed\xe8\x3b\x03\x2f\x2d\xad\x9b\x28\x00\x05\xf6\x4d\xb6\xd7\x00\x49\x56\xe6\x0c\xc9\x24\x5c\x70\xb4\x24\x5f\x0a\x5b\xb4\x95\x61\xb5\xe4\x42\x95\xd9\xd8\xb3\xad\xd4\xb1\x65\x04\x25\xb5\x4c\x33\x67\xb3\x7c\x75\x16\x05\xf0\xb9\xa6\x04\xb0\xb8\xd2\xe0\x08\x36\x84\xae\xfd\x2e\xe3\xa1\x22\xd4\x86\x24\xa7\x6f\x90\x9c\x5e\x71\xf2\xb8\x05\xeb\x95\x2b\x36\xe8\xd4\xa1\x72\x1c\xd5\x60\x58\xe6\x05\x07\x3f\x18\xc2\x1b\xe6\xd6\x65\xbe\x1b\xe1\xed\x15\x25\x8e\xb0\x06\xc8\x31\x02\x00\x7b\x18\x0b\x61\xa9\xaa\xc0\xa9\x2b\x2c\xf1\x9a\x92\x0e\x41\xe3\xb6\x7f\x08\x5f\xb3\xa8\x78\xa0\x84\xee\xd9\xc3\x5e\xe9\xfd\xcc\xa8\x57\xcd\xfc\x6f\xe5\xa2\xef\x0c\xfa\x5c\x7e\x06\x50\xf3\xa6\xe9\xf9\x95\xdc\x1d\x8b\x5b\xb0\xc2\x12\x34\x3d\xb7\x74\x16\xb7\x16\x1f\x47\x98\x12\xe8\x31\xaa\x04\xfa\x30\xae\xb6\x1c\x7c\x91\xda\x6a\xf7\x8b\x7e\x4f\x0b\xd4\x35\x78\x76\xc9\x41\x1b\x2f\x5d\xc6\xa8\xc5\x51\xc9\x90\x42\x87\x85\xa7\x5a\x5e\x90\x10\x51\x4e\x36\x7d\xc2\x39\xb7\xb6\x3d\x2a\x1b\x36\x3e\x2a\x3e\xb5\x52\xf0\x6c\x0e\x2b\xd4\xe7\xfb\xc5\xc7\xb2\x67\xd7\x9e\x75\x86\xdb\xe4\xb0\xae\x0b\x03\x4b\x9f\x03\x2c\x7d\x45\x60\xe9\x15\x54\x34\x13\xb5\xd2\xa6\xb3\x2d\xfb\x1c\x56\x01\x41\x2d\x2f\x4f\x68\x06\x38\xb9\xc8\xa7\x4a\x41\x75\x93\xa9\x94\xad\xab\x90\x05\x9f\xa0\x86\x73\x68\x77\x54\xf0\x3e\x76\x20\x53\xb5\x71\x1c\x1d\xd9\xfd\x16\x37\x0b\x79\x4d\x86\x23\xeb\x50\x1f\xde\x49\x4a\x00\x8b\x93\x04\xc1\x1a\x8f\xc4\x05\x38\xa2\xc3\x13\xf8\x55\xcc\x28\x85\x83\x79\x60\x61\x5c\x04\x77\xc1\xfe\xc1\x53\x80\xbb\x5c\x16\xd3\x41\x48\x6b\xde\x00\xad\xe5\x21\x9c\x36\xda\x09\x2a\x85\xad\xc8\x31\x4e\x6c\xe4\x35\xb6\x3a\xd1\x1c\xec\x91\x60\x22\x6f\x91\xd5\x39\x4d\x61\xef\x7c\xf4\x5c\xc1\x44\x7a\x85\xcc\x2a\x29\x26\x6f\xe1\x45\x3c\xfb\xb6\xbc\x28\x38\x8c\xa4\x45\x2f\xb0\x6b\x9a\xb9\xa0\xba\x20\x57\x9a\xae\xcf\x22\xeb\x63\xbb\x30\x7a\xc7\x43\x81\x6c\x2b\xfb\x2e\x7c\x32\xfb\x6d\x68\x0f\x7f\xd5\x60\x94\xc1\xc0\x62\x1b\x21\x1b\xe6\xdd\x6e\xb0\x33\xad\x23\x0c\xa6\xa2\x23\x83\x80\x6c\x66\x32\x83\x97\x00\xd5\x5c\x46\x20\x25\x56\xa9\xdc\x73\x6d\xc2\xd2\x38\xbd\xd8\x71\x60\x50\xc3\x1b\x9c\x6c\x02\x2c\x8f\x9f\x5a\x86\x76\x99\x0f\xf6\x62\xf3\x0c\xdd\x2c\x6c\xd2\xf6\xb5\x3d\xd8\x63\x64\x70\xe8\x8d\x2c\x8b\xca\xfd\x85\xcf\x64\x79\x5c\x04\x94\x82\xab\xf9\x98\x46\xaa\x2e\x0b\x89\x62\x18\xd4\x3b\x1f\x10\x09\x83\x2b\x9d\x44\xa0\xb8\xab\x0f\x1f\xa5\x0f\x6e\x56\x8d\x70\x70\x87\xc9\x76\xd7\x99\x9a\x1e\x8e\x6a\x08\xca\x40\xa5\x27\x84\xcd\x06\x9f\xe2\x78\x1a\xd6\x23\x61\x69\x33\x9b\xb1\xbb\xea\x3a\x18\xc5\x76\x12\xef\x5e\xab\x6a\x68\x77\x6f\x98\x01\xfa\xd6\x2b\xb9\x18\xc1\xd3\x4f\x2b\xfe\x7f\x79\x5d\x32\xec\x80\x7f\x63\xf2\x40\xfb\xff\x94\x37\x26\x87\x1a\xed\x2e\xee\xca\x84\xd1\xdb\xf1\xf0\xa1\x89\x03\x16\x6f\xfc\x08\xdf\x0c\xfe\x43\x31\x8b\x8c\x2e\x0c\x23\x56\x89\x12\x21\x0b\x44\x42\x6c\x3a\x81\x24\xaf\x6e\x37\x4d\xbb\x68\xcb\xc0\xfd\x86\xed\x05\x2e\x5f\x51\x6b\x52\x62\x1c\xc1\x3e\xee\x82\xa4\x8c\x6f\xe9\x24\x5d\x15\x3d\xa9\x06\xeb\x2d\x55\x6b\x37\x83\xb6\x47\xaf\xc6\x2c\x6d\x18\x0d\x16\x4a\x3c\x65\x56\x83\x2e\x0f\xd8\x55\xd4\x6d\x29\x25\xc1\x28\xcc\x9d\x4c\x39\xa2\x66\x05\xbd\x97\x94\x30\xd4\xa2\xa4\xc8\x0b\x6e\x78\x98\x58\xbe\x34\x7d\x6c\xe8\x12\x74\x52\xab\x19\x74\x2e\xa8\x15\x50\xd3\x1c\x37\xe8\xcd\xd0\xb6\x57\x52\xe1\x57\xc5\x86\xc8\x5d\xaf\x29\x3b\x7d\xbc\x9e\x03\x28\x4a\x0a\x94\x27\x05\x2c\x04\x59\x57\x72\xfa\x36\x4c\x0f\x1e\x75\x43\xae\x7b\xce\x6d\x02\x26\x30\x43\xc9\x5b\x8e\xdf\xf8\x83\xc6\xba\x09\x1e\x77\x63\x1f\x28\x79\x2a\x66\xb7\xe1\xfe\x72\xf0\x70\x5c\x61\xb0\x16\x98\x65\x86\x1c\xef\x39\xe1\x42\x97\xd8\xcc\x4a\x8c\x4d\xe5\xfd\x11\x45\x26\xcb\x07\x1a\x61\x0a\x72\x81\x46\xc5\x7d\xce\x66\x56\x01\x48\x61\x6f\x61\xfb\x11\xe5\x7d\xdf\x56\xf3\x3d\xdf\x8b\xaa\xfd\x07\x2f\x4a\x31\x36\x71\x79\x23\xd8\x6e\x6f\x5e\x13\x97\xfa\x75\x18\x76\xf0\xa6\xfd\x00\x4e\xa2\x5c\x59\xb7\x24\xc6\x95\x55\x81\x6b\x05\x07\x20\x97\x8d\x11\xc4\x96\x77\x9c\xac\xcb\x79\x79\x12\x6f\x2d\xd2\xcb\x63\xcd\xe9\xb6\xfd\xce\x42\xbf\x5f\x9e\x5d\x5d\xdc\x42\x4b\x0c\xaa\x34\x01\xc8\x80\x30\x38\x4b\x89\x03\x59\x01\x85\xa8\xd1\x8d\xda\x8f\xeb\xb1\x1e\x66\x3b\x42\x6c\xdd\x34\xdc\x6d\xdb\xbe\x3c\xc7\xc3\x2f\xab\xf3\x23\x00\x6c\xee\x2d\x65\x66\xe9\xd9\x7e\xd3\x57\x6c\x05\x66\xad\xa9\x25\x12\x3f\x3c\x5f\xee\xf2\xd6\xc2\x89\x22\xb2\x4e\xfa\xe0\xe8\xc1\x2c\x5a\x7d\x59\x2f\x2f\xf8\xc9\x63\x8a\x57\x6f\x2e\xe9\x73\xd1\xde\xc8\x45\xa8\x8e\xf4\x63\xb5\x63\x30\x7d\x83\x99\x07\x4c\x29\x80\xfd\x0b\x52\x6c\xa9\xf0\x8d\x59\xd9\x7e\xaa\x16\x8e\x60\x2e\x8e\xcf\xa0\x77\xa0\xa4\x70\xf1\x69\xd3\x88\x05\x64\x92\x9f\xef\x04\x4d\x47\x13\x49\x7e\xc6\x40\xf8\xf1\x61\xb6\x65\xdf\x19\x02\xc3\xa0\x25\x43\xf1\x4c\x5f\xa8\x54\x4c\x8f\x44\x95\x18\x3a\x90\x58\x3c\x6b\x1b\x4a\x94\x71\x91\xbb\x2c\xcf\x67\x11\x33\x0b\x77\xae\xc1\x53\xc5\x5f\x66\xfb\x13\x56\x16\x48\x19\xb7\x0d\x7a\x32\x7e\x42\x5c\x22\x82\xcc\x84\xbf\xda\x6b\x2e\x71\xd5\xfe\xf5\x9e\x51\x89\xe8\x5d\x97\x11\x5e\x27\x6c\x6a\x6e\xb1\xa3\x09\x6a\x1f\xec\xf7\x71\xc5\x77\x6d\xfb\xa2\x8b\x33\x1d\x98\xbb\x87\x52\x1d\x58\x7c\x1d\xa5\xb0\xf9\x6e\xe7\xb6\x8d\xe0\xc1\x1e\x90\x6d\x00\xf2\x49\x18\x4e\x00\x41\x8b\xa0\x1b\xd4\x23\x2e\x61\x21\x10\x7b\x86\x29\xc0\x70\xeb\xd1\xe4\x66\xb9\xe4\xa8\x57\x1c\x9e\x51\x43\x96\x20\x08\xd6\x19\xdb\x58\x5b\x49\x71\x74\xcc\x58\x29\x07\x25\x17\x8f\xc9\xde\x7a\x7d\x87\x44\x3e\x55\x18\x78\xbb\x77\x8f\x64\xbf\xdb\xd7\x72\x5e\x0a\xb2\xb4\x21\xce\x0a\x1b\x81\xf4\xd2\x36\x4d\x6f\x0f\x32\x04\xa2\xcb\x3b\x4a\xa6\x3d\xad\x5f\x3b\x04\xf3\x4d\xd7\x22\x93\x78\xef\x41\x19\xdc\xb3\x2d\x60\x29\x3f\x2e\x44\xfd\x1e\x97\xa0\x7e\x1f\x00\x17\xc3\x0c\xdb\xf8\x2f\xf1\x4b\x98\xb4\xeb\x31\x9b\x79\x2a\x35\xda\x80\x25\x6d\x48\x37\x21\x0e\x8a\xb9\x27\x8c\x53\xbb\x9b\x9b\x24\x0d\x82\x0c\xa5\x17\x9f\x1a\xca\x0b\x3e\x35\x94\x7d\x7c\xaa\x76\x6c\xd0\x83\xae\xdb\xd8\x44\x5c\x5e\xbe\x89\x67\xdb\xe7\x06\xef\xec\xb0\xbd\xd8\x3d\x8e\xd3\xca\xb1\xe4\xee\xa5\xec\xff\xf7\x28\x28\xa1\xd8\x0c\xf1\xa7\xa9\xc3\x3a\xba\xdf\x37\x55\x5f\x7e\x7f\x0f\x57\xe7\xf7\xfa\xaa\x98\xdf\x7b\x14\xae\x9b\x0a\xe6\xfe\xc1\xc2\xa9\x16\x07\xd0\xe3\x0e\xef\xd1\x53\xa0\xa9\xf8\x4e\x57\x6d\x69\xfb\xfb\x49\xf0\x3a\xe7\x88\xa4\xfd\x36\xe0\x08\x3a\xdc\x02\xac\x63\xec\x3b\xd2\x06\x19\x19\xc9\x0b\xbd\x3c\x5e\xc8\x06\x9a\xef\xac\x9a\xe7\x48\xf6\x3d\x94\x18\xb3\x16\x73\x56\x4d\xf5\xad\x7f\x08\x19\xfb\xba\x86\x77\x87\x15\xb1\x07\x96\xa1\x67\x1b\xbd\xc5\x0c\x05\x9b\x3e\xc5\xac\x05\x30\x76\xa7\xbe\x38\xe3\x01\x87\x1a\x8b\xe1\x80\xe1\xd6\x54\xfd\xbd\x94\x18\x82\xc1\xb0\xcf\xe8\x30\xbc\xdd\x6f\xf1\x10\xe3\x25\x87\x3c\xc3\x53\x9a\xa3\x6e\xed\x48\xd2\xcf\xc3\x1e\x21\xc1\x31\x21\xf1\x58\x64\x77\x8d\x6c\xa3\x37\x5c\xe2\xd5\x08\xa7\x8d\xf4\x0d\xae\xb4\x1c\x76\x68\x13\xf2\x62\x69\x54\xe8\x1d\xe7\x39\x31\x76\xa2\xb0\x39\x3b\x3b\x52\x31\x67\xc2\x49\x52\xf9\x7d\x5f\xee\xa9\xf2\xb2\x5e\x81\x4c\xff\xcc\x3f\x49\xdc\xe1\x9f\x0e\x41\xe2\x25\x08\x85\x17\xfb\x26\x3c\x35\xbf\x41\xe8\xbd\x28\xc5\xd1\xc2\x9d\x72\x49\x30\x33\xe1\x2e\x70\x86\xdf\xd3\x1d\x54\xd8\xf1\xd1\x24\xce\xb7\x59\xa4\x35\xd5\x04\x73\xf7\xea\xe7\x37\xe7\x03\xc8\x09\x66\xa0\x39\x13\xcc\x43\x73\x26\x58\x85\xdc\xd6\xba\x21\xe0\x86\x76\x7a\x04\x02\x79\x70\x00\x42\xd0\xde\x32\x03\x94\x3c\x59\x91\x92\x7e\x41\x94\x25\xfe\x39\x42\xf4\xf2\x3b\x06\x0a\x5e\x7d\x12\x28\x7b\xf4\x69\xd4\x6a\x1d\xb6\x59\xcb\x4d\xa3\xe7\x3a\x62\x22\x14\x70\x1d\x31\xb1\x9f\xec\x9e\x41\xef\xda\xe6\x53\x55\xe8\x23\x59\x02\x7f\xa1\x49\x06\x6a\x20\xbe\x66\x83\xd0\xaa\x5d\x37\x89\x70\x2b\x27\xbd\x9e\xe0\x57\x34\x75\xba\xfc\x78\xbd\x08\xac\x5f\x81\xfc\x96\xb4\x94\x30\xe0\xd5\xc2\x21\xc6\x74\xc6\x2f\x4f\xfc\x7b\x58\x50\x2f\x0f\x06\xb3\xa9\x96\xa5\x53\x46\xeb\x68\xde\x50\x5a\x04\xbc\xee\xfb\x5d\x67\x9e\xdf\x78\xbd\x29\x3d\xa7\x1f\x83\x41\x84\x55\xe9\x48\x46\x35\xed\x2a\x5c\x10\x04\x78\x91\x84\x69\x8c\x1b\xb4\xee\x0e\x01\xb8\x6e\x0f\x43\x1e\xb7\x6a\x1d\xe3\xb4\x15\xe2\x9f\xa1\xf7\xb2\x80\x6b\x9d\x65\x80\xc9\x96\x19\x4a\x77\x49\x86\xc1\x2e\x69\xd6\x42\xb3\x45\x2b\x51\xe8\xf8\xcf\x15\x9b\x6a\xb8\x9c\x70\xed\x59\x5a\x47\xb4\x57\xec\xc5\x77\x4e\x3f\x3d\xbc\x8f\xc0\x2f\x68\xb2\x8c\x89\xa8\xfd\x31\x40\xf9\xb9\x5c\xec\x83\x9b\xc3\x9f\xe5\xb7\xaa\xea\x7d\x35\x8d\x19\x07\xef\x6b\xbc\xd8\x70\x21\x29\x01\xcc\x54\x28\x4a\xeb\x3a\x4c\x9c\xcd\xd4\xf9\x60\xfb\xae\x79\x1c\x66\x19\xca\x2c\xae\xcc\x90\x49\x7e\x66\x1b\x71\xa5\x1a\x18\x61\x19\x6c\x28\xf1\x84\x69\x08\x67\x15\x18\xbc\x59\xde\x44\xc7\x2d\xab\xd9\x31\xcb\xda\xcd\x02\x58\x1c\x1f\x82\xe7\x63\xa5\x0f\x92\x7f\x97\x89\x65\xf2\x5e\xcc\x96\x3e\x0c\x1e\xad\x33\xed\x7e\x60\x46\x17\xb9\xf3\xdd\xef\xd4\x91\xaf\x0e\x22\xd8\xc9\xaf\x62\xf4\xe8\x93\x85\x5b\xfe\xd6\x87\x5b\x8e\x9e\xae\x1e\xbe\x06\xe1\xa2\xf3\xa3\x56\xb6\x4b\x94\x97\x3f\x82\x1e\x3c\xe9\xda\xc5\x93\xfb\x61\xe0\x7d\xd6\x97\xc6\x31\xad\xa3\x2a\x65\x74\xf6\x82\xc1\x6f\xfa\x6a\x80\xfc\x0e\xeb\x15\xa5\x9e\x54\xcd\x21\xb0\x5d\x58\x7f\xad\x61\x18\x81\xdb\x10\x15\x45\xae\x0d\x2b\xd4\xc0\xda\xe3\xfa\x06\x8f\x2a\x04\x0f\x47\x34\xf5\xd7\x74\x6c\x32\x84\xef\x6f\x1a\xcf\xf9\xab\xbb\xe5\x42\x6c\x29\xf6\xed\xf7\x80\x16\x64\x46\xa7\xa7\xd3\x93\x07\x62\x46\xc8\x7b\x5b\x36\x8b\xf4\xe3\xf6\x69\x8c\x29\x63\x38\x8d\x1a\xcc\xfd\xbb\x20\xf2\x36\xfc\x88\x25\xa3\xea\x34\x6a\xaa\xc4\x3b\xff\xce\xbd\x57\x95\xbc\xe7\x00\xc8\x1f\x92\x7c\xc5\x63\xa2\xff\x13\x3c\x60\x27\x3e\x0a\xa0\x51\xfa\x4c\xe4\x27\x7f\x7d\xcb\x15\x7f\x9b\x76\x25\x31\x4d\x84\xee\xfd\x76\x8b\x84\x2d\x1d\xad\x89\x15\x71\xc2\x1a\x09\xfc\x0e\x23\x7e\x16\xf8\x59\xe4\x37\xf8\x75\x8d\x5f\xd7\x65\xf9\x51\x0a\x83\xab\x52\x71\x3a\x9c\xaf\x91\x72\x83\xdf\x37\x1c\xe2\xf6\x3e\xfb\x67\x71\x3b\xfa\x44\x81\xfd\x40\xc0\x60\x6e\x4e\xd3\xed\x07\xa5\xcb\x8b\xfd\x4f\xfd\xe3\xfd\xf7\xf9\xda\xff\x46\x93\xf0\x45\x29\xdc\xbc\x26\xc9\xe7\x7d\xb0\xc6\x7e\x6d\x15\xca\x37\xa5\x72\x3f\x34\x51\x3e\x29\xad\xcd\xaf\x33\xdf\x2f\xfd\x42\xaa\xef\x95\x7e\x11\x7a\x8b\xb6\xd9\x71\xf0\xd0\x0f\xee\x71\x4b\xff\xdc\xd7\x29\xe5\x69\x80\x24\x44\x8c\x64\x27\x64\x7e\xa7\x4f\xde\xf6\x65\x17\xdd\x59\x62\x41\x7d\xab\x7a\xb7\x77\xe7\x53\x1f\x51\x5a\xc0\x7c\x94\x25\x31\x54\xe7\x37\x7c\xe4\x81\x33\x9a\xdd\x6c\x5e\xe9\xbb\x6e\x65\xca\x87\x81\x87\xff\xf6\x6f\x00\xa7\xcf\x7f\xff\xf7\xf4\xec\xf9\xa3\xb4\xfc\xcc\x31\xe3\xba\x74\x9b\x7f\xc6\xa9\x40\xa1\xe8\xe7\x8b\x08\x90\x1d\x73\x61\x72\xac\xd7\x50\xfa\xbc\x37\xee\x9e\xfe\x5f\x00\x00\x00\xff\xff\x1e\x79\x55\x67\x89\xae\x00\x00") +var _confLocaleLocale_enUsIni = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x7d\xeb\x72\xdc\x48\xae\xe6\x7f\x3e\x05\xdb\x27\x1c\xb6\x23\xe4\x72\x74\xf7\xde\xa2\xa3\xed\x5e\x59\x6a\x5f\xe6\x58\x96\xc6\x52\xcf\x6c\xaf\xc3\xc1\x66\x15\x59\x55\x1c\x57\x91\xd5\x24\xcb\xb2\xe6\xc4\x89\xd8\x07\xd8\x07\xd8\x7d\xbd\xf3\x24\x0b\x7c\x00\xf2\x42\xb2\x24\xbb\x67\x62\xfd\xc3\x62\x65\x22\x6f\x48\x24\x12\x89\x04\x90\xf9\x6e\x97\x15\x65\xb7\x48\x9f\xa6\xc7\xe9\x2e\xaf\xea\x4d\xd9\x75\x69\x57\x6e\x96\x8f\xd7\x4d\xd7\x97\x45\xfa\xb2\xea\xe9\x77\xfb\xa9\x5a\x94\x49\xb2\x6e\xb6\x25\x81\xbe\xa2\x3f\x49\x91\x77\xeb\x79\x93\xb7\x05\x25\x9c\xda\x77\x52\x7e\xde\x6d\x9a\x96\x81\x7e\x96\xaf\x64\x5d\x6e\x76\x5c\x86\xfe\x24\x5d\xb5\xaa\xb3\xaa\xa6\x9f\x97\xf4\x95\xbe\xae\x25\xa5\xd9\xf7\x96\x74\xbe\xef\x25\x6d\xbf\xb3\xa4\x5f\x76\x49\x5b\xae\x2a\xea\x4d\x4b\x49\xef\xf4\x33\xb9\x2e\xe7\x5d\xd5\x73\x4b\x7f\x95\xaf\xe4\x53\xd9\x76\x55\xc3\xb5\xff\x45\xbe\x92\x5d\xbe\x62\x80\x0b\xfa\x93\xf4\xe5\x76\xb7\xc9\x51\xe0\x4a\x3f\x93\x4d\x5e\xaf\xf6\x02\xf3\x46\x3f\x93\x45\x5b\x52\x56\x56\x97\xd7\x94\x7a\x82\x1f\xb3\xd9\x2c\xd9\x13\x12\xb2\x5d\xdb\x2c\xab\x4d\x99\xe5\x75\x91\x6d\x65\x98\xbf\x50\x7a\xaa\xe9\x29\xa5\xa7\x9c\x8e\x21\x94\x05\x0d\x35\xcb\x3b\x1d\x07\xe1\x92\x46\x9e\x77\x09\xaa\xaa\xf3\xad\x95\xe6\xcf\xa4\xdc\xe6\xd5\x86\xb1\xf6\x98\x3f\xa8\xe3\x5d\x77\xdd\x00\xb7\x17\xfa\x49\x48\xc8\xfa\x9b\x5d\x09\x1c\x3c\xbe\xa2\xaf\x64\x91\xef\xfa\xc5\x3a\xe7\x7e\xca\x57\x42\x40\xbb\x86\x90\xd1\xb4\x37\x80\xb3\x1f\x49\xd3\xae\xf2\xba\xfa\x7b\xde\x0b\x82\xce\x83\x9f\xc9\xb6\x6a\xdb\x86\x71\x7b\x86\x8f\x84\x86\x9e\x71\x3d\x94\xf2\x96\xb0\x10\xd4\xc2\x39\xdb\x6a\xd5\x0a\x1a\x39\xf3\x0c\xbf\xb8\x16\xce\x5b\x36\xed\x47\xcd\x78\xc1\x9f\x83\xa2\xd4\x09\xcd\x8d\xdb\xcf\x6b\x42\xbc\xe6\x9e\xe1\x47\x04\xd0\x25\x79\xb1\x25\x54\xee\xf2\xba\x64\x1c\x1d\xf3\x2f\xc2\x0b\xfd\x4a\xf2\xc5\xa2\xd9\xd7\x7d\xd6\x95\x7d\x5f\xd5\x2b\x46\xf6\xb1\x24\xa5\x97\x9a\x94\x04\x79\x2e\xed\xa6\xd9\xbb\xe9\xa4\xf4\x5f\xe9\x67\x7a\x21\x3f\x25\x2f\x28\x84\x4c\x57\x92\x47\xd2\x65\xcb\xb2\x2c\x64\x2c\x5d\xfa\x82\xbe\x93\xdd\x7e\xb3\x21\xac\xfd\xbe\x2f\xbb\x9e\x0b\x5d\xd0\x6f\x1a\xbf\xfc\x4e\xaa\xae\xa3\x0f\x4a\x7e\x8d\x8f\x84\xa6\xae\x5e\x60\x30\x27\xf8\x48\x92\xf7\x5d\x99\xb7\x8b\xf5\x87\x44\xfe\xa2\xaf\xfc\xc1\xb4\x77\x68\x52\x99\x90\x94\x88\xa4\x05\x6b\x20\x59\x34\x05\xff\x38\xa1\x3f\x54\x75\x55\x77\x7d\xbe\xd9\x7c\x48\xf4\x83\xc1\xe4\x4b\x26\xa0\xaf\x7a\x60\x41\x13\xd3\xcb\xbe\xdc\x75\x3c\x83\xe9\x8b\xaa\xed\xfa\xc7\x7d\x45\xc4\xfa\x6e\x5f\x27\x45\xb3\xf8\x48\xcb\x80\x97\x34\x5a\x7e\xbd\x4c\x09\x59\x0f\x68\x21\xb4\xfb\xba\x26\xf4\xa4\x2f\x1b\x42\x19\x35\x53\x51\xfb\xa7\x80\x3e\x4a\x77\x9b\x32\xef\x08\xa4\xcc\x8b\xf4\xc7\x3c\xed\xf3\x76\x55\xf6\x4f\xef\x65\x73\x5a\x7e\x1f\xef\xa5\xeb\xb6\x5c\x3e\xbd\x77\xbf\xbb\xf7\xec\xe5\x9e\x8a\x6d\xaa\xba\xec\x7e\x7c\x92\x3f\x4b\x17\x39\xe5\x10\x1a\x6f\xd2\x79\xb9\xe4\xd5\x46\x6d\xa5\x44\xe5\xf5\x8a\x57\xda\x4d\xbf\xe6\x06\x89\x12\xe8\xa3\x4b\x79\xa9\x7f\x93\xf0\x04\x10\x2b\xc8\x8a\xb9\xb1\x35\x74\x08\xc9\x2d\x4d\xc0\xd9\xcd\xe5\x9f\xdf\x1c\xa5\x17\xc4\xdb\x56\x6d\x89\x6f\xfa\x8f\x4a\x7c\x9f\xd2\x68\xaf\xaa\xd3\xe7\xb3\x84\xca\x1a\x42\x4e\xf3\x3e\x9f\x73\xdf\xdd\xec\x73\xa6\x2c\x42\x97\x87\xa5\xc8\xdc\x12\x9c\xb1\xeb\xa3\x69\x99\x5a\xc8\x54\x87\x2e\x7f\x57\xc7\x5b\xe6\x01\x94\xee\x30\x7b\x21\x38\xa3\xaa\xd2\xd7\x6f\xdf\x9e\x9f\x3e\x4f\xcb\x7a\x45\x98\x49\xaf\xab\x7e\x9d\xee\xfb\xe5\x7f\xcb\x56\x65\x5d\xb6\xf9\x26\x5b\x54\x8c\x94\x96\x08\x36\x25\x2c\xc9\x10\x67\x49\xd7\x6d\x88\x45\x81\x0a\x2e\x2f\xdf\xa4\x67\x4c\x09\xbb\xbc\x5f\xa3\x23\xfd\x3a\xe9\x7e\xdf\x30\xa2\x5c\x83\x57\xeb\x32\xc5\x62\x00\x50\xb3\x1c\xe2\x25\x2d\xb4\xaf\xb3\xa4\x6c\xdb\x8c\x38\x68\x7f\xc3\x68\xd6\x3a\x0f\x41\x4b\x75\x44\xed\x75\xd3\xd3\x34\xa6\x28\x27\x55\x54\xf5\xa7\x7c\x53\x15\x84\x6c\x8f\x90\xb8\x2c\x12\x8b\x86\xe6\x8d\x4b\x13\x65\x36\xd7\x18\x6a\xbe\xa0\x0d\xa0\x4b\xef\xcd\xee\x81\xe3\xde\x7b\x7c\x6f\x96\xd4\x4d\x26\x5c\x82\x79\x73\x51\x75\xf9\x9c\xf8\xb4\xec\x1b\xad\x71\xbd\x5f\x99\x7e\xa4\x2b\x0a\x91\x46\x10\x8c\x5b\xde\x8b\xb0\x05\x30\x71\xe5\xc4\xb0\xc1\x6c\x94\xcd\x84\x63\x37\x9e\xe4\xe6\x57\xd8\x92\x4b\x18\x8d\x39\xb1\x09\x33\xea\x3a\xde\xed\x36\xd5\x42\x9a\x7e\x29\x79\x9e\xd0\x78\x67\x56\xa4\x84\x70\x20\x14\xcb\x0b\xc8\x85\x7a\xcd\x6c\x2b\x8d\xf8\x3c\xca\xaf\x4b\x5a\x39\xeb\xfd\x4a\x76\xa7\x4d\xb3\x2f\xbe\x01\x43\xb1\x99\xf3\xfc\x24\x7d\xd7\x50\x87\x41\x1d\x0e\xc0\x37\x71\x4c\x8c\x81\x85\x81\xb6\xdc\x36\x3d\x23\x4e\x8b\x55\x34\x3d\xd7\x15\x65\xd2\x48\xbb\xfc\x13\xb1\xc5\xbe\x91\x25\x59\xd0\x92\x5b\x70\xc5\xc4\xc1\xf6\xb4\xa3\xcb\xb2\x20\x3e\x22\x4b\xc3\xd2\x62\x1a\x04\xd4\x76\x4f\xab\x69\x4d\x95\x31\xe2\x59\x22\xa1\x2a\xa7\xfa\x89\x21\x51\x3d\x58\xe5\xb4\x72\x1b\xda\x3c\x79\xa2\x4f\xf1\xa1\xbf\xc3\xfa\xa9\x57\xf9\x72\x49\xbd\xea\x68\x55\xbc\x4a\x17\x9b\x86\x96\xd4\x2f\xef\xde\x74\xbc\x60\xd6\xd9\xae\x69\x21\x89\x50\xd6\x05\x7d\xba\xb4\x00\xd1\x0c\x51\xef\xb7\x73\xfa\x75\xbd\xae\x88\x51\x03\xed\x5c\x82\xa5\x24\x4a\xa5\x26\xf6\x1d\x4d\xe1\x51\x4a\x4b\x98\x46\x40\x28\x03\x01\xf0\x18\x8c\xea\x18\x7c\x49\x34\xb6\x6f\x69\x39\xad\xfb\x7e\x67\x2d\xbf\xba\xba\xba\x90\xa6\x5d\xea\x6d\x6d\xe7\x01\x65\x60\x0e\x36\x2c\x1b\xd5\x69\x53\xcf\x40\x24\xfb\x76\x33\xa0\x1f\x1a\xab\xe5\x1c\xc0\x0b\x77\xe1\x09\xff\x77\xe9\xd1\x03\x3c\x77\x24\xf5\x5d\x83\x9a\x08\xc7\x25\xe4\x14\x22\xea\x66\xc7\xf5\x06\x54\x7d\xae\x09\x9e\x94\x21\xdb\xb8\x7c\x91\x70\x28\x17\x32\x65\xb0\x4b\x6f\x69\xc0\xca\x46\x2f\xcf\x08\x0d\xe0\xa5\x48\x5d\xb6\xcd\x96\x52\x5f\xd0\x1f\x9f\xe0\xbb\x7f\xc6\xf5\x01\x26\x2f\x0a\xe2\xf2\xdd\x51\xfa\xee\xc5\x49\xfa\x9f\xbf\xff\xee\xbb\x59\xfa\xba\xe7\x95\xc8\xc4\xf9\x37\x26\x2a\xfa\x14\x51\xcb\x81\x12\xc7\xea\x89\xee\xee\xf1\xca\xba\x97\xfe\x88\xdc\xff\x5e\x7e\xce\x49\x44\x2c\x67\x8b\x66\xfb\x8c\xb9\xea\x36\xa7\xb5\xcf\x39\x44\xae\x4a\xc7\x97\x65\x5d\xd0\x87\x0a\x6c\x9a\x17\xb0\x03\xcd\x0f\xc4\x37\x11\x5c\xb3\x45\x53\x2f\xab\x96\x07\xf4\x73\x0d\x6a\x30\x91\x96\xb6\x6b\xe4\x98\x54\x44\x48\x23\x0e\x52\x2d\x6f\x3c\x28\x86\xfa\x96\x13\x75\x42\x13\xa1\xba\x4c\x45\x74\x87\xe5\x4b\x21\x46\x9e\xb7\x73\x1a\x5e\x6b\xf8\xee\x3c\xc2\x9b\xe5\x92\xf7\x5a\xdb\x25\xb4\x85\x73\x49\x95\x0d\x23\x04\x21\x62\xdc\x41\x28\x3f\x55\x22\x3e\x39\x7d\x9b\x96\x9f\x88\xda\x98\xeb\xb5\x4d\xb1\x5f\x80\xc2\x18\xf6\x88\x99\x35\xb1\x88\x8e\xd6\xc6\x42\xf6\x95\x80\x49\x70\xd7\x98\x13\x2d\x08\x88\x78\x83\x31\x6b\x12\x24\x3f\x11\xe7\x6f\x83\x26\x5e\x5a\x92\xf6\x7e\x04\x3b\xea\x94\x2b\xc1\x23\x5f\xd0\x8c\x13\x55\x48\x2f\x3a\xe9\x94\x64\x13\xb9\x13\x1d\xef\xe9\x84\x92\x17\xd4\x97\xf9\x0d\xf8\x4e\xc7\xc4\x50\x94\xcb\x7c\xbf\xe9\x7d\xbf\x06\x9b\x88\xb5\x74\xc9\x87\xa4\x30\x6f\xb2\xc0\xa8\x83\xa0\x9e\x6e\x58\x96\xc8\xb0\x26\x39\x47\x36\x1b\xa6\x57\x39\x85\xd8\xbe\x43\xec\xa9\xc4\xf4\x64\x5e\xe4\xd7\xf9\x32\xc9\x3f\xce\x77\xcd\xbe\x13\xc9\x27\xc5\x56\xcb\x35\x5a\x05\x2c\x2a\x4c\xf7\x65\x96\xa8\xb8\x94\xe9\x71\x2d\xfb\x54\xe1\x30\xe4\xc8\x55\xaa\xd4\x23\x1c\xf3\xb5\xbf\x30\x00\x9f\xb2\xba\xc9\xb2\xae\x37\xe7\x3c\xc8\xce\x1d\x86\x04\xe7\x3c\x5c\xb4\xc0\x22\x1c\xcd\xd2\xa7\x0a\x6c\x5e\x09\x06\x78\x21\xaa\x41\xd3\xd4\x54\x57\x96\xa8\x81\xca\x3f\xa1\x3a\x51\x66\xa6\x07\x04\x95\xd9\x4d\xf4\xe3\xed\xbe\x68\x20\x3b\x60\x2f\xa1\xd2\x86\xd6\xc1\xbe\x9e\xb6\xd5\x6a\x4d\xbc\xb5\xb9\x3e\x12\xa4\x5c\xaf\x9b\x92\xd7\xcf\xeb\xd3\xa7\xdf\x4a\x3f\x56\xbc\xb3\xb8\x42\xbc\x27\xe5\x7b\x22\x2e\xc2\x98\x92\xb1\x74\xc1\xed\xed\x80\x1c\x1d\x45\x04\x68\x78\xf8\x1b\x89\x12\x8e\x69\x28\xaf\x08\xf3\x94\x49\x78\x18\x29\x6d\x07\x48\x69\x58\xb9\x92\xca\xfb\xd9\xaa\xc1\x41\xc6\xe4\x7b\xde\x2d\xe9\x3c\xdc\xf5\xd9\xaa\xea\xb3\x25\xb3\x2e\xae\xf9\x05\xd7\xc0\x9b\x37\xe5\xa4\x0f\x28\xeb\x41\x4a\xfc\x8f\x4e\x67\xc5\x0f\xe9\xfd\x4f\x2a\x31\x7e\xcf\x3c\x29\xa3\x55\x54\x6d\x30\x25\x7a\x3c\x6a\x4b\x11\x58\xed\x0c\xee\xa4\xb6\x6e\xbf\xc3\xde\xa6\x02\xa2\x3b\x0d\x14\xcd\x75\xcd\xab\x0f\xcc\x97\xf8\x4c\xb5\xa8\x68\xcf\x98\x57\x75\x4e\x1b\xbc\xd5\x02\xa6\x7e\x9f\x68\xe2\xed\xf9\x15\x00\x57\xcd\x7c\x5f\x6d\x0a\x03\x98\x25\x26\x44\x92\x08\xa9\xb3\x1f\x8a\xd5\x96\x54\x49\x5f\x16\x4d\xcb\x12\x09\x46\x63\x05\x0f\x88\x42\x2d\x8b\x18\x48\xae\xf8\x3c\x03\x58\x94\x73\x52\x0b\xa3\x81\xa6\x1f\x47\x35\x96\x69\x40\x37\x55\x57\x3f\xe8\xd1\xd3\xc5\x9e\xda\xa2\xa9\xe7\x64\x2a\xd8\xa5\x8f\x9f\xd1\xff\x09\x4b\x48\xb2\x03\xac\xc6\x88\xe7\xcc\x54\x32\xf7\xb2\x16\xa3\xae\x46\x44\xee\xa6\xda\x48\x38\x18\x6b\xd8\x5f\x23\x81\x6e\x2f\x54\xcb\xea\x92\x0d\x4d\x6b\xf9\x0d\x7d\xf0\xc9\x6d\xb5\xc1\x24\xe4\xbd\x1e\xaf\x1a\xc2\x1b\x13\xc8\x91\x2c\x9a\x25\x0d\x8d\x79\x69\x9f\x7f\x2c\x71\x22\xa3\x3d\xff\x3d\xeb\x81\x3e\x24\x7b\x91\x41\x9b\x4d\xe1\xce\x3b\xa0\xec\xa6\x1d\xaa\x31\x3c\x90\xa3\xda\x8e\x84\xed\xc5\x3a\x73\x5a\x24\x46\x4a\x5f\x7e\xc6\xee\x8f\x2c\xaf\x54\x62\x92\xe7\xac\x64\x7b\x83\xe9\xe2\x41\x9c\xdd\xf8\xd9\x22\x09\x94\x16\x0a\x1d\x66\xe7\x0d\x63\xed\x53\xe9\xa0\x4e\xc2\xd4\xb8\x00\xd5\x45\xb2\xb2\x56\x15\x6b\x1b\x28\x4b\x54\x22\x9a\x2b\x6a\x91\x2e\x01\x2b\x53\x15\x18\x38\x1e\xcd\xa7\x9e\xec\x67\x34\x31\x50\x1b\x58\xcb\xc4\x17\x6f\x64\x5d\x04\x6d\x26\xef\x55\x3d\xf6\x21\x31\xb8\x77\x71\x3e\xf1\x94\xf5\x87\x40\x05\x95\xd9\xec\x9a\x2a\x0a\xda\x13\x65\x2b\x5e\xa4\x58\x97\x3b\x96\x3e\xb6\x1d\xc8\x62\xc3\x27\xed\x1b\x95\x9f\x1d\x81\xfc\x24\x0c\x9b\x28\x86\xd8\xdc\x37\x49\xd7\xf0\x82\xcb\xbe\xb2\x8a\xe7\x15\x91\x02\xca\xc7\x9b\x9d\xe8\xc6\x48\xcc\xe5\xe9\xa3\x55\x76\x73\x14\x9f\xac\xd6\x79\x47\x4c\x9c\x64\x05\x2d\x56\xcc\xec\x84\xcb\xd3\x4e\xe7\x39\xac\x19\xe8\xf3\x40\xe5\x52\xb2\x69\x87\xbb\x30\xf7\x50\xf8\x9c\xb6\xe2\x64\x27\x48\x46\xa1\x00\x35\xd1\x26\x21\x6c\x5b\xb2\xf8\x9c\x6d\x45\x8d\x26\xbf\xd2\xb3\x32\xa1\xed\x70\x45\x0b\xda\x08\xf6\x29\x6b\x3f\x56\x38\x65\x28\xbd\x32\x40\xd9\x87\x8c\x58\x21\x2c\xe5\x27\xd3\x5b\x12\x67\xb8\x86\x6a\x88\xd6\xf6\x08\xfd\xb4\x65\x51\xf6\xcc\x18\xbb\xc8\x08\x10\xf5\x3a\xe2\x16\x1e\x89\xc7\x29\x2b\x20\x43\x28\x15\xb9\xfd\xb0\xb8\x00\x73\x8d\x1f\xe7\xcf\xee\x77\x3f\x3e\x99\x3f\x73\xbc\x75\xb1\x2e\x17\x1f\x85\xfe\xaa\x7a\xde\x7c\xc6\xc1\x16\x8a\x12\x3a\x53\xf3\x1a\xbb\x5f\xa4\x74\xd0\x6d\x71\xae\x22\x5e\x40\xc5\x08\xf1\x9c\x1b\x4d\x1a\x75\x86\x59\x06\x6d\x6d\x0b\xac\x2a\x10\xb8\x27\xc8\x63\x4e\x65\x92\xc4\x06\xe0\x69\x12\x03\xd9\x54\xdb\xaa\x1f\xd1\x04\x73\x98\x5c\x69\x4b\x75\x65\x86\x24\xd4\x85\x61\x62\x94\xc4\xa7\xa9\x1a\xda\x57\x8d\x4e\xae\x73\x3a\x48\x7d\x9f\x12\x6d\xec\x69\x7f\xe2\xce\xd2\x78\x88\x51\xe7\xbc\x31\xd3\x21\x2a\xef\xb2\x7d\xad\xf8\x2a\x0b\xa3\x92\x57\x15\xb6\x0f\x6e\xd7\x68\x39\x80\x32\x94\xea\x59\x20\x7d\xe8\x50\xf9\x68\xa6\xba\x2d\x14\x63\x9e\xce\x1d\xaa\x58\x6e\xcd\x27\x67\x85\x78\x5e\x5d\xca\xd9\x17\x18\x60\x38\x9e\x41\x3a\x40\xf9\x69\xa1\x53\xd8\x47\x4a\x01\xa6\xe7\xfb\xbe\x6f\xf8\x5c\xb2\x61\x72\x90\x32\xd6\xeb\x13\x00\xe2\xa8\xe5\xeb\xc3\x74\x86\x78\x12\x16\x5b\xda\x39\x21\x23\x3a\xe4\x35\x2d\x1a\x6e\x3e\xd1\x0d\x46\xa7\x9b\xa0\x03\x2b\x44\x97\x94\xd7\x37\x5e\xbd\x81\x5e\x70\x83\xfd\x74\x5f\x1e\xb6\xe5\x23\xdf\x1b\xb7\x18\x50\xc2\x7a\x24\xc5\x83\x85\xf2\x0e\xb9\xa2\x62\xb5\xe5\x64\x7b\x9a\x2a\x2a\x3d\x7d\xb4\x31\x7a\x91\xcf\x24\x4f\x9c\x93\xc4\xca\x02\x88\xa6\x51\xa0\xf4\x6c\xd0\x96\x3f\x12\x8e\x31\xd8\xc7\x5d\xf6\x5b\x53\xdf\x34\x59\xb7\x96\xe3\xb7\x75\x8f\x8e\xee\xf5\x2a\xd2\x5b\xe1\x5e\x04\x44\xf7\x5f\x78\x03\xe4\x81\x7e\x48\x74\x36\xca\x60\x51\x28\xb5\x5a\x8e\xcd\x9a\xac\x0d\x07\x6f\xc2\xda\x5f\xca\x96\x8f\x77\x00\x8a\x67\xeb\x10\x16\xe3\x41\x38\xa6\xe8\x77\x77\xc7\x10\x35\xe9\xc8\xf6\x7b\xee\x75\x53\xe4\xd4\xed\x1b\x68\xa2\x7f\xa5\x0d\xa7\x86\x8e\xbf\x49\x28\x43\x8e\x99\x67\xf8\x20\x50\x3e\xf3\x7e\x48\x78\x4b\x7f\x3b\x10\x56\x79\xc7\xd2\xb4\x40\x5e\x42\xd6\xcf\xd1\x25\x86\x1b\xca\xc5\x84\x60\xfb\xae\xf4\x77\x19\xf8\x72\x63\xba\xbc\x7c\x75\x65\x87\xd8\xcb\x57\xe9\xc7\x52\x2b\x7f\xd5\xf7\xbb\xee\x17\x28\x34\x44\x3b\xc1\xaa\x8c\x8b\xfc\x86\x85\x48\x49\xd6\x1f\xc8\xb8\x2a\xf3\xad\xf6\x92\x3f\xa5\x8a\x63\xda\x5d\x35\x91\x3f\x69\xd3\x0d\x14\x65\x09\xc4\xa9\x9f\xa7\xc4\x68\x77\xa8\x29\xf5\xa2\xe4\xb7\x91\x76\xef\xb7\x24\xdf\xec\xe8\xdc\xc5\xf2\x4c\x00\x06\x45\xd6\x5c\x8f\x5f\x29\x40\x40\xc1\xfb\x2d\xcd\xfc\x02\xc7\x4d\x2a\xf0\xf0\x71\xf6\x28\x50\x6c\xc6\x95\x15\xb4\xb4\xff\x50\x85\xfc\xcd\x42\x6f\x58\x6f\x57\xfd\xdd\x46\x11\x55\xc7\xe9\xc4\x29\x09\x02\x22\xa6\x87\x72\x40\xd8\xa7\x59\xdc\xec\x59\xaf\x45\x09\x24\xd2\x46\x55\x6f\xf3\xcf\x77\x15\xdc\x36\x13\xe5\x84\x81\xf9\x42\xc6\xa6\x74\x88\xf1\xb2\x20\x78\xd6\x5c\x1d\x84\xa6\xa9\x27\x90\xaa\x5e\x6c\xf6\xc5\xc1\x9e\x74\xfb\x39\xed\x71\x2c\x2b\x3f\xb8\xdf\x3d\xe0\x2a\xeb\x8f\xb4\x29\xd7\x0e\xfe\x17\xf9\x9d\xe2\xf7\x0f\x76\xcd\x46\xa7\x59\x3d\x40\xa4\xee\xc2\x8d\x64\x8b\x82\x77\x07\x1c\x04\x66\x9e\xa9\x84\x87\x03\x47\xfe\x50\x49\xe8\xe9\xcd\x2d\x6c\xd6\x43\xe0\x9c\x44\x24\x38\xf3\x77\x83\x19\x6f\xf1\x19\x0b\xdd\x75\x28\x5a\xbb\xcd\xdf\x76\x51\x40\xc8\x0d\x51\x36\x2e\x37\x58\xa0\x07\x8b\x93\x24\x33\x51\xfa\x7c\xac\x4b\x3e\x50\xbe\xa7\x25\x36\x51\x81\x5b\x79\x07\x0b\xca\xe4\xa3\x10\x8d\xbc\x18\xf1\x8e\x71\x41\x06\xa3\x63\xdf\x66\x53\xae\x58\xe9\x68\x0d\x47\xad\xe9\x44\xd3\x96\x27\x60\x21\xc1\x79\x0c\xbb\xc9\x0a\xe7\x35\x3c\xc4\xb8\x39\x8a\x8f\x8f\xac\x8b\xa1\xaa\x5a\xdc\xef\x06\x87\x48\xed\x86\xee\x00\x5b\x3e\x2f\x75\x7b\xde\x80\xf8\x6c\x25\xc2\x55\x3c\x1b\x2c\x5e\xa0\xaa\x12\x4d\x1c\xae\x9e\x68\x91\x0f\x9c\x77\xd5\x0f\xb0\xaf\xac\x3a\x54\x3a\x8c\x2b\xd6\xca\x1d\xd0\xa1\x6a\xdd\x81\xb8\xfc\x5c\x41\x81\xfb\xb2\x62\xc5\x20\x8e\xc4\x4e\x13\x80\xbc\x59\xb2\x21\xe6\xc1\x47\x2f\x19\x95\x88\xe1\xcd\x27\x5e\x8d\xdc\x1e\xe7\x4a\x39\x51\xe8\xea\xa0\x78\x9e\xf5\x70\x8d\x6b\xa0\xb2\x38\x22\x41\x86\x4b\x50\x3f\xb1\xb8\xf3\xcd\x75\x7e\xd3\x41\x53\x64\x1c\x8a\x95\xd7\x52\x9c\xd9\x0f\x89\x39\x2b\xf4\x2a\xbc\x22\xa1\x15\x67\x98\x60\x5d\x3f\x6f\x36\x4e\x18\xb9\xc6\xf1\x18\xdc\x45\x75\x4f\x9f\x82\xed\x5a\xf7\x26\x3e\xda\xf3\x41\x98\x8f\x28\x92\x1d\x54\x84\xbb\x47\xdd\x29\x26\xca\x1e\xb1\x10\x48\xcd\xb0\x48\x46\xfc\x5b\x70\x4d\x52\x2e\x61\x16\x5d\x0a\x74\x25\x7b\xaa\xff\xb1\x88\xf5\x15\xe1\x90\x8f\x89\x5e\x7d\xc0\x7b\x19\xcd\x8a\xa9\xf8\x25\x1d\x87\xff\xa4\xeb\x69\x09\x30\xa6\xed\x42\xff\xd7\x40\x1e\x49\x91\x8b\x25\x06\x34\x75\xeb\x6a\x97\x36\x50\x1b\x87\x28\xf4\x64\x1b\x08\xd2\x7c\x99\x51\xe2\xd8\xc0\xfa\xf3\x36\xaf\xbb\x65\x09\x45\xfa\x36\x5d\xf2\x9d\xf1\x4c\x9b\x66\xb9\x5c\x2e\xf6\x0f\xb4\x2c\x47\x30\x34\x1d\xee\x2e\x98\xbb\x60\xa2\xe2\xa6\xe5\x66\x05\xca\x5a\xf4\x01\x58\xf5\x35\x75\xd6\x07\x26\xb3\x11\x0a\x20\x1b\x47\xf7\x64\xd6\x9b\x4f\x65\x88\x88\xe5\x1f\x1d\x79\x80\x75\xbd\x2b\x90\x0b\x96\x78\x9a\xa4\x51\x68\x6b\x70\xcd\x3b\xbf\x89\x47\xcf\x45\x83\xbb\x73\x5a\x23\xa5\xb6\xc2\x0b\x83\xd7\xca\xa0\x42\x68\x69\xfc\x89\x28\x91\x7b\xf6\x6c\x4e\x5d\x5c\xac\xa3\xd5\x79\x85\x9c\x54\x72\x46\x0b\x34\x79\xcf\x4d\x7f\x48\xe4\xa6\x3d\x73\x4a\xf9\x13\xb9\x79\x17\x89\x56\x95\xec\x7d\x6a\x9a\x78\xbe\x2a\xb1\x22\xa2\x77\xbf\xb5\x24\x6f\xc3\xa6\x14\xfd\x5b\x43\x32\x07\x74\xeb\x7f\xa2\x2f\x96\xf1\xeb\x24\xba\x5e\x1c\xa8\x49\x20\x46\x57\x3d\xaf\xb0\x0b\x5a\x18\x24\xf6\x1c\x6b\x0a\x9d\xd2\xc1\x1d\xa0\xb9\x79\x61\xdf\x34\x1f\x39\x33\x3d\x5e\xda\xf2\xa5\x70\xa2\x46\x7b\x61\xdf\x09\x1f\xf2\xb7\x33\x6c\x0e\x2c\x7e\xe3\x9a\x22\xd8\x12\x58\x5a\xe0\x09\xb3\xbc\x59\x00\xbf\xcb\x7b\x62\x8b\xb5\x1c\xc4\x84\x43\x85\x45\x35\xdb\x55\xe1\xee\xb3\xb9\x16\xb6\xfd\x10\x54\x7c\x48\xbc\x49\x8a\x59\xa3\x4c\xa9\x85\x95\xc5\x74\x2a\x23\xff\x2b\x7d\xaa\x42\x07\xec\x0b\x1f\x7a\x20\xc7\x4d\xb2\x5d\xff\xc1\x3c\x26\xf8\x99\xa8\x0a\x2c\xd6\x7f\x29\x79\x3f\x4d\x4f\xe5\xc3\x8e\xf6\xfb\x0a\x63\xaa\x8a\x24\xd9\x01\xef\x81\x01\x8d\x4e\x84\xeb\xb4\x1a\x4a\x79\x4d\x7c\x3b\xdc\xd9\xd9\x66\x43\x0a\x31\xe1\xda\xe5\x10\xa4\x00\xbe\x9b\x08\x8e\xa5\xac\x5c\xc6\x79\xb5\x0e\x2e\xbe\xf8\x3a\x87\x4f\xd9\x04\x76\x5d\xce\x53\x56\xf7\x12\xe1\xd0\xe9\x4f\x07\xba\xcd\xe9\xe0\xf8\xa9\xca\x9d\x62\x89\x66\x8b\x4d\x74\x74\x17\x7d\xc1\xe6\x39\xb8\x4c\x1f\xdb\x91\xf1\xcd\x94\x5e\xf6\xbc\xd1\xcf\x64\xbf\xe3\xdb\x93\x60\xc0\xbf\x20\xc1\x0d\x38\xce\x0f\xce\x63\x18\xba\x15\x73\xd2\x8c\x80\x17\x76\x48\x83\x95\xcb\xcc\x96\xcf\x84\x7d\x98\x2e\xa1\x62\x08\xe2\x35\x2c\x60\x31\x6a\x1c\x03\x64\xca\x7d\x2e\x86\x4f\x3b\x63\xba\x6e\xae\xd3\x4d\x55\x7f\xec\x14\x9b\xcc\xc7\xc2\xc3\x29\x54\x52\x44\x84\x7b\xb1\x1b\x92\xcf\xb1\x99\x92\x5d\x33\x0d\x56\xb8\x5d\x46\xc9\x85\xdb\x31\x92\x27\x61\xfd\x11\xdd\x2e\xc4\x96\x25\x8b\xc9\x60\x6a\x76\x79\x47\xa3\x6c\x9a\x4e\x15\xa0\x9e\x89\x70\x1a\x94\x2a\x0a\xa5\x38\x77\x10\x3a\x25\xc7\x76\x65\x88\x35\x95\xd8\x25\x9f\x75\x00\x2b\x34\xab\xb6\x62\xf6\xf7\x8b\x5d\x01\x62\x7e\xdc\x69\x02\xd9\x30\x2a\x89\x7b\x1f\xde\x7b\xbc\x6d\xec\x82\xd1\xb8\xa1\x65\x1e\xd9\xa6\x2f\x18\xc0\x96\x1d\x75\x76\x48\x1f\x5a\x81\xa9\xf0\xef\x20\x13\x23\x82\xf0\x52\x48\x26\xde\x31\x88\x66\x13\x89\x76\x27\x7a\x19\xe1\xf2\x19\xb3\x41\xfe\x5b\x5c\xdf\x39\xa5\x02\x1f\xc8\xb3\x01\x88\x1e\xd8\x23\xc8\x49\x09\xda\xda\x3a\x28\x3d\x0f\x7a\x3f\x5a\x2b\x56\xee\x9a\xb0\x10\x0e\x5c\xa9\xbb\x98\x99\x1d\x0f\x6b\x52\xe5\x2e\x10\x06\x17\x62\x74\x52\xe3\x22\x51\xaa\x20\x54\xe1\x80\xd1\xf9\x73\xc5\xb1\x70\x1f\xbe\x00\x10\xab\x43\x07\xa0\x86\x87\xf1\x79\xb3\x34\xeb\x85\x90\x91\xed\x5a\x22\x0f\xda\x68\x07\xfa\xb5\x11\x0b\x8b\xd8\x15\xb8\x55\x83\xab\x78\xcf\xa5\xe8\xc4\xa8\x75\x31\xbf\xc7\x97\xa5\x38\x1d\x11\xd1\x31\x8b\xba\x9a\xac\xcc\xd9\xe5\x0a\x8b\x76\x9d\xa4\x1f\xc2\xb8\x74\xb8\xa7\x9a\x32\x00\xb0\xe1\x28\x83\xef\x27\xb4\x85\x18\x8d\x8a\x1d\xc6\x7f\xab\x5a\x4c\x21\xdc\xd5\x5c\xc4\x40\xd2\x53\x70\x14\x9a\x37\xd1\x4b\x1b\x3f\xf9\x69\xd8\xb8\x9f\xf0\x9f\x07\x2a\x6d\x19\x5c\x4c\xef\xdf\x24\xd4\x25\x50\xa3\xbf\xe2\x2c\x30\xcd\x03\x8d\x19\x83\x85\x20\xaa\x8e\x74\xc9\x59\xa4\x73\x87\xf6\xfc\xab\xf4\xec\xbc\x77\xff\x13\x54\xec\x51\x5b\x5e\xc5\xee\x7a\x39\x58\x0e\xdc\xbd\xc1\xce\x39\x5a\x18\x94\x01\x39\x42\x49\x3a\x90\x0e\x94\xa8\x9d\x90\xc0\xcd\xc8\xd9\x84\x31\x44\x49\x10\x25\x94\x1a\xb0\x85\xb0\xa0\x0a\x33\x22\xd8\x00\xca\x41\xa5\x1b\x29\x8d\xe3\x89\x3f\xc6\x49\x8c\xb0\x22\xb0\xb0\xd3\xa3\x8d\x59\xa4\x58\x3d\xd9\x6d\x19\x11\x72\x89\xee\x4c\xba\x46\x37\x64\x47\x7a\xfc\x59\x57\xab\x35\x8d\xab\xda\xf2\xd5\x31\xc8\xc9\xee\x27\xfd\xe9\x94\x7f\x11\x43\x69\x56\x35\xeb\xae\xb8\x05\xb1\xe1\x72\x1b\xcc\x8f\x5d\xdf\x36\xf5\xea\xd9\x69\xc3\xc7\x46\xd6\xe8\xf0\x26\xf8\xd3\x8f\x4f\x34\x9d\x98\x26\xcf\x21\x1b\xfc\xbd\xac\xfa\x57\xfb\xf9\x83\x2e\x5d\xb1\x05\x2a\x2e\x55\xf2\xc0\x2e\x55\xad\x06\xc4\xc0\xee\xba\x76\x68\x81\x95\x2a\x2d\xf4\xae\xd9\xd0\x2a\x89\x8b\x34\xdb\xad\xcc\x2f\x6d\x00\x5b\x81\x44\xff\x61\x68\x50\xd6\xc0\x5c\xd9\x2a\x7e\xa8\xc2\x99\x23\x73\x3f\x3f\x3a\x6d\x26\xee\x45\x7a\x12\x15\xb8\x18\x18\x37\xa7\x75\x1f\x6c\x1b\xac\x23\x49\x5d\x31\x08\x0a\xe3\x62\x98\x48\x56\x3b\x79\x15\x8d\x29\x59\x70\x12\x40\x1d\x56\x9e\x8a\x52\x4f\x44\x62\xe2\x34\x6b\x53\x64\x85\x92\x95\xdb\x42\x5a\x01\xfd\xf2\x5e\x61\x2a\x5c\x08\xbe\x5e\x99\xc3\x04\x3b\x58\xe5\xca\xd8\x64\xf4\xca\xd6\x6c\x04\x01\x63\x53\x9c\x78\xce\x36\x84\x99\xe2\x6d\xd6\x8b\x90\xa9\x89\x85\x92\x30\x36\x21\x49\x3a\x69\x30\xdb\xfe\x42\xa6\x36\x6a\xd7\x0f\xdc\x9a\xfb\x02\xbe\x86\x31\x1d\x03\x1d\x34\x16\xe8\x46\x74\xa6\xde\xa8\x26\x04\x19\x6c\xdd\xea\x4f\x3d\x6f\x1b\xbd\x1f\x4b\x2d\x11\x73\x42\xc7\x9c\xbe\x8c\x16\x33\x77\x02\xf6\x88\x62\x6f\x03\xe5\xca\x7f\x4d\x8b\x9c\x38\x41\xdf\x7c\x24\x62\x1a\x17\x41\xfa\xa1\x42\x8e\xc3\xd8\x59\x43\xf9\xcb\xb1\x67\x0f\xc3\xd3\x87\xde\x33\x1f\x64\x31\x01\x67\xd1\x5a\x9d\xcd\x93\x68\x86\x60\xeb\xcd\x86\x21\x85\x70\x12\x65\x04\x6a\xd8\xe3\x38\x00\x49\x58\x35\x03\x41\x7d\xcb\x1f\xfa\x3b\x9c\x96\xa8\xfe\x60\xb9\x10\x03\xdf\xd7\x01\x03\x15\x72\xc8\x04\x15\x6e\x90\x17\x74\x94\x84\x61\xe3\xb1\x54\x78\xc5\xd9\x9d\x5a\xf5\xea\x75\xbd\x15\x79\xa9\x89\x58\x03\x00\x14\x84\x77\x0e\x11\xf8\xe5\xb5\x0a\x56\x8b\x9a\x62\xa8\xc9\x22\xe6\x80\xa8\xce\x58\xe6\x5a\x4c\x33\xd2\xe3\x8b\xd7\xb4\x67\xb8\x06\xad\xd2\x9f\x73\x92\xa4\xa5\x0b\xd7\x4e\xa3\xc1\xc4\x36\xe4\xb9\x4e\xe6\x97\xe2\xa6\x40\x45\x49\x2c\x71\x37\xa8\xd1\x80\x64\x30\x71\xbe\xe0\xb8\xec\x02\x2d\x8f\xb4\x86\x9e\x0c\x77\x2b\x37\xd4\x6f\x08\xb3\x4e\xd7\xc8\x4b\x6b\x77\xc3\xfc\x3f\xb0\xc5\xca\x05\x43\xd7\xe0\xe0\x03\x23\x30\x82\x84\xa6\x23\xe5\x25\xdc\x3a\xfe\x61\x1d\x56\x0e\x12\x4e\x65\xc8\x46\x26\x27\xd3\x33\x95\xc9\x62\x53\x9c\x65\x67\xf5\xc4\x63\xbe\x8b\xcf\x30\xe1\xfb\x73\xf8\x2d\x5c\x26\x1c\x55\x40\xca\x17\x93\xcd\x3a\x8a\x96\xa6\x07\xfc\x26\x95\x8d\x50\x0c\x19\xb8\x15\x39\x5d\x28\x45\x04\x36\xc2\x54\xcb\x75\xb9\x61\xe3\x5e\x6d\xdd\x5f\x6f\xea\xd0\xa3\x1b\x7f\x05\x0a\x4e\xa2\xa5\x17\x71\x05\x15\xa1\x9a\xce\x2a\x23\x08\x5a\x6e\xb8\xe4\x97\xa3\xbc\xed\xd7\x27\xc7\x6f\xdf\x9e\x5f\xf9\x6d\x9a\xd7\x41\x5d\x90\x30\xf1\x8d\x33\x87\x1b\xf5\xcb\x8c\xe2\xdc\x04\xc6\x10\xde\x2c\x4f\x4b\x1c\x82\x0b\xd9\x94\xd5\x4e\x9f\xab\x06\xbc\xa7\xe1\xbe\x18\x2f\x8f\xfa\x5f\x1c\x9a\xbf\xe4\x3d\xcb\x37\x1f\x12\x53\x76\x9f\xf3\xdf\x24\xbc\x2f\x08\xee\x68\xb0\xf4\xfc\x55\x8e\x37\xbd\xa7\x0e\x34\xc5\xe8\xfe\x00\x4c\x7a\x9f\xe3\x68\x44\xb8\x6f\xb0\x57\x2c\x53\x5c\x66\x1f\xb1\x3a\xb4\x69\xb1\x60\x18\xb9\xfb\xba\xfa\x7d\x0f\x01\x8d\x0f\x46\xc4\x3c\xd8\xca\x72\x5e\x6d\x64\x43\xf9\x8b\xfb\x21\xe9\xfc\x35\x30\x0f\x0f\x1a\xa7\x5f\x3f\x76\x3b\x36\x52\xa5\xbd\xa1\x7b\x7a\x6f\x5f\xa5\xac\x5d\x63\x13\xad\x7b\xcf\xe8\x18\xc3\x57\xdc\x34\x7d\x04\xf1\x6c\x54\x1d\xbb\x88\x2d\x44\x15\xe7\x8c\x7d\x40\xb7\x9a\xce\xab\x85\x45\xde\x48\xff\x27\x88\xff\x03\x6d\xb2\x3f\x9a\x1f\xc7\x43\x3d\x25\x13\x8e\xb0\x76\x3f\xe5\x9b\x7d\xac\x2c\xe1\xd6\xb9\x4c\xf7\x28\x41\x51\xd5\x18\x0f\x7d\xd9\x90\x67\x46\xe9\x9c\x07\xcb\x74\xa4\x4e\xa0\x2f\xf0\x3a\xc9\x37\xbd\xe8\x8a\xd3\x00\xfd\xcc\x0b\xd0\x6a\x19\x4e\xb1\xde\xe9\x39\x96\xd3\x2d\xda\x0a\x96\xf5\x92\xce\x9e\x8b\x69\xe0\xb5\xe8\x12\x7d\xbb\x97\x44\xa8\x34\xa6\xd9\xaa\xea\xe9\x8c\xcc\xfe\x53\xb0\xc3\x4e\x68\x9d\xd3\xd6\x03\x9f\x47\xf9\xb2\x94\x51\x51\xde\xa5\x05\x16\x3a\x2e\x96\x0d\x95\x64\xf9\x43\x7f\x4f\x94\x52\x40\xf3\xb8\xe4\xeb\x8a\x26\xab\xea\x8a\x57\xea\x6b\xfa\x43\xbb\xb0\xc8\xec\x31\x5d\x89\x40\x8a\x4a\x54\x21\x23\xa7\x66\x57\x8f\xda\xc5\xe9\xac\xa8\x41\x5c\x30\x2f\x6a\xba\xad\x2a\x6f\xa0\x0d\x09\xe9\x73\x24\xa8\xa3\x23\xf5\x84\x66\xe1\x93\xc8\x2f\xe2\xfa\xf8\xda\x52\x1e\xf2\x99\xed\xd1\x01\x45\xf0\xf0\x36\xf5\xeb\xf5\xc1\xc3\x1a\x6e\x57\x0b\xb3\x41\x51\xc6\x4a\xfe\x54\xad\xc9\x22\xa3\x85\x44\x1d\x31\xcd\x1f\xcd\x79\x62\x8a\x43\x5a\x98\x7b\x78\x29\x9b\xce\x22\x8f\x57\x17\x0c\x31\xe7\xb4\x3a\xee\x3d\x13\x9c\xd9\xd2\xb2\x5a\x75\x0a\xce\xd4\x17\x34\x98\x03\x85\x98\xc1\x77\x24\xb3\x23\x2b\x5b\xe4\xf0\x71\x50\xd5\x2f\xd3\x50\x11\xf7\x55\x09\x28\x0f\xfc\x51\x9e\xbc\x7c\x7d\x05\x6f\x14\x9a\x31\x78\x0f\x98\xcb\x0d\xdb\xe9\xce\x5c\x9d\xcc\x98\xab\xae\x93\x0d\xbb\xae\x80\x78\x5e\x83\x76\xd3\x6e\xd3\x41\x5c\x45\x0e\x98\x5a\x59\xc8\x72\x7c\x6d\x76\x3d\x08\x18\x33\x14\x7e\x2d\x89\x5a\x90\x13\x71\x72\x8d\x6f\x52\xcc\x54\x29\x0f\x1d\xa1\x12\x59\xe8\xc6\x3d\x74\xd9\x2f\x1d\xff\x80\x67\x0b\x9b\xe4\xc7\x8c\x03\x8e\xb5\xc1\xbc\x85\x86\x74\xbc\xb5\x14\xbc\x37\xee\x6e\x32\xd6\xf0\x62\x3b\xdc\xdd\xf8\x84\x40\x6e\xa0\x8c\x2a\x02\x76\x36\x0f\x17\x98\xa7\xff\xf8\xdf\xff\xe7\xf1\x09\x77\xfc\xa4\x6f\x37\xf4\xa5\x62\x19\xc3\x0b\x22\xa5\x82\xf4\xfc\x5f\x49\xbc\xbe\x56\x03\x87\x5f\xe4\x2b\xb1\xdf\x7f\xc5\xaf\x3d\x1b\x1e\x8b\x35\x05\x7f\x24\xfa\x8b\x6f\x60\x12\xf5\x13\x66\xde\x99\xf0\xd9\x46\x27\x9e\xce\x35\x21\xa3\xfb\x7d\x5f\x2d\x3e\x66\x72\x24\x7f\x9a\xfe\x99\x7f\xa5\x70\x11\x4d\x84\x6c\x98\x7f\x38\x66\x00\xf2\x1a\x70\x94\xd0\x90\x16\x0c\x52\xcd\xd9\x3d\xf3\xc8\xe3\xcd\xe6\xc6\xac\xf3\x0c\x90\xbd\x5f\x92\xdd\x9e\x6d\x7a\x78\x4a\xad\xb5\x0b\x4a\x81\x2b\x11\x27\xb2\x68\x10\xd4\xe0\xae\xe7\xa2\x3a\xd0\x3c\x75\x57\x5c\xc1\x26\xf7\x54\x64\x79\x05\x13\xdb\x19\xcf\x73\x1a\xb2\xca\xb7\x49\xe2\xd8\x9a\xb2\xb3\xbe\x2d\x21\xb6\xd3\x9f\x84\xb8\x25\xdb\x81\xe9\x65\x1f\xfb\x40\xf6\x39\x2e\xb7\x90\x6e\x57\x7d\x7c\x63\x99\xaf\xb4\x22\xc8\xeb\xcf\xf5\x33\xa1\x74\xfe\x7d\x45\x7f\x46\xde\xca\xec\xdb\x3c\xf6\x69\xde\xe4\xf3\x12\xc9\x6f\xf0\x41\x2b\x86\x18\x76\x4f\xd3\x00\xc5\x93\xfb\x91\x30\x1e\xaa\x5e\xa8\x0f\x5f\x89\x3a\x0b\xc8\xc5\x9e\x7c\x26\xb8\x36\x69\x73\xb6\x9c\x7d\x97\x5f\xcb\x4f\xc2\x91\x3a\x3d\xbf\x92\x2f\x49\x86\x1d\xb6\x80\xc2\x0c\xdb\xc1\x43\x24\xd3\x25\x70\x61\xdf\x89\x75\x60\x36\xee\x88\xe5\x0c\x7c\xae\xd3\xc5\x20\x7f\x29\x07\xcb\x17\x7c\xac\xb4\xb4\x1c\xcc\x38\x35\x53\x32\x97\xbe\xa5\x75\x24\x77\x0b\x67\xf2\xe5\x72\x0a\xb1\xcd\x3c\xc5\x56\xa6\x69\x66\x17\x7f\xce\x7f\x5d\x2a\x11\x25\x52\xdf\xf2\x5f\x67\x63\x2e\x21\x09\xf8\x44\x29\x4e\xde\x3e\x79\x36\x9c\x8b\x20\xab\x66\xb9\x60\x8e\x4b\x1c\x5a\x5f\xc8\x0f\xb3\x17\x84\xff\x36\x73\xe5\x4f\xf8\x67\xba\x19\xd5\xe2\x26\x37\x9c\xdb\x41\x33\x21\x0c\x35\x35\x09\x26\xcd\x85\x90\xd2\xe2\x76\x0a\x98\x4e\x11\x75\x04\x7b\x4e\x09\x21\x69\x45\x15\xb3\xfc\x3b\xa8\x19\x22\xf1\x34\x3c\xed\x73\xec\x87\x84\x43\x81\x7e\x8e\xfb\x19\x00\x49\x37\xf3\x09\x50\xd6\xcd\x78\x38\x1a\xf8\x10\x48\xd5\x87\x8e\xe9\x0c\x67\xcf\xcf\x0f\x4d\xed\x70\x82\x24\x33\x23\x01\x68\x51\x3a\x2f\x0a\x00\x41\x84\xe0\xf0\x00\x51\x33\xae\x32\x6d\x2c\xaa\x0f\x08\xed\xf3\x39\x65\xdf\x2f\x80\x4d\x57\x98\x71\xe5\xb3\x04\x75\x96\xa9\xcc\xc5\x6a\x8e\xaa\x0c\xf3\x48\xda\xc9\x44\x7e\x13\x44\x38\x59\x6e\x33\x51\xe2\x56\x8a\x1a\xc2\x1c\xac\x79\x44\x37\x5a\xf2\x96\xe9\xf5\x10\xec\x50\x7f\xb8\xea\x03\xe5\x54\xdc\x82\x90\x35\xce\x99\xb1\xaf\x8d\xe3\x9f\xc7\xb0\xf0\x00\x0f\x9d\x02\xed\x34\x4c\x08\x49\xc7\xbc\xa1\xbb\xae\x16\xaa\xa8\x99\x2a\x24\xb3\x5c\x64\xf3\x1b\x2d\x23\xf3\x0c\x1f\xc6\x03\x45\xb6\x6c\x24\xd2\x70\x78\x06\x2d\x72\xe6\x12\x26\x8a\x74\xea\x03\xcd\x4e\xc8\xe3\x9c\x19\xef\x46\x30\x22\x61\xde\xd4\x4d\x82\x30\x95\x02\xe4\x1c\x1f\x53\x20\xa2\xbe\x54\x05\x04\xef\x02\x62\xed\x6f\x17\x9e\x93\x0d\xb3\x65\x8c\x2b\xf1\x06\x76\x32\xed\x17\x94\x63\xa3\x53\xe6\xab\xa2\xad\x3e\x6b\x60\x08\x8a\x9f\xb7\xb4\xe3\x0b\x48\x43\xa3\x12\xbc\x92\x30\x0b\x04\x22\xdf\xe9\xfd\xf7\xdf\x7e\xe8\x78\x1a\xfc\x45\xc0\xfb\xef\x3e\x74\xf7\x9e\xdd\x7f\xff\xfd\x07\xdc\x00\x8c\x0a\x67\x4b\x56\x80\x8d\x6b\x40\x41\x83\xde\xb5\xe5\xa7\xaa\xd9\x77\x22\xa4\xe1\xd3\xf3\x87\xcf\x32\x15\x9f\xfb\x78\x89\x3b\x5f\xec\xc1\x0a\x2f\x5c\x56\xbc\xc2\xeb\xfd\x36\xd3\x31\x76\xc2\x01\xec\x97\x2b\x6e\x18\xc8\x72\x6e\xf2\x37\xf7\x9b\x87\x5b\x15\x3c\x58\xea\xbc\x45\xf3\xf8\x17\xf9\xf5\x0c\x03\xe1\xa1\xff\xe6\x5a\x6a\x82\xbb\x83\x2b\x71\x27\x67\xa1\xd9\xdd\x62\xdc\x94\xfd\x2c\xe6\x4a\x16\xda\x04\x5d\x8e\xb3\xb4\x17\x1e\x44\xe7\x0d\xa6\xb6\x21\x78\x5b\x02\x31\x06\xf7\x0e\x3f\x07\x99\xb7\x55\xd6\x46\x05\x94\xd5\x7a\x2a\x51\xd0\x01\xae\x15\x53\xb2\x0d\x7d\x1d\x9a\xa4\x3d\x57\x87\xfd\xfc\xca\x5a\x44\x9e\x20\xa9\x75\xe9\xea\x59\x12\xc6\xeb\x05\xf4\xcc\x50\xc5\xf3\x50\xd5\xd4\x52\xa0\xbf\xb2\x89\x5d\xa3\x81\x99\x2e\xf0\x61\xc9\xa2\xd8\x51\x3b\x7a\x47\x9b\x91\x12\x4c\x13\xcd\x6b\x8a\xce\x04\x74\xfa\x01\xc7\x36\x4f\x29\xbe\x8d\xe1\xa4\x08\xb4\xaa\x33\x33\xc7\xd7\x63\x03\x31\x4b\x36\x21\x93\x11\x11\x19\xb1\x73\xa8\xea\x55\x0f\x3a\xb2\x45\x97\x75\xe6\x18\x27\x57\xb5\x3c\x91\xe1\x6a\x2d\x0b\x28\x2e\x7e\xa6\x3f\x0e\xaf\x03\xb3\x18\xeb\x1f\xb7\x42\xdd\xa7\x3f\x96\x24\xfb\xa2\x2d\x3a\xbf\x6f\xc7\xf9\x8b\x66\xd3\xf8\x7d\x1d\xbf\x86\x00\xa2\xe7\xbc\x5f\x0c\x64\x33\xc9\xf6\xb4\xad\xab\x97\x13\x06\x3b\x8f\x40\x4e\x0c\x46\x32\x06\x46\x5f\x71\xa6\xf3\x0f\x91\x0e\xc2\x4b\xc4\x02\x10\x8c\x6b\x51\xd3\x29\x80\x3a\x45\xeb\x24\xd8\x94\x46\x5d\xa4\x8c\x50\x83\xce\x32\x7b\x68\x7a\xc0\x77\xc8\x81\x52\x5d\x6b\x3e\xac\x43\x9f\x6e\xda\x1f\x8a\xa5\xa7\x77\x5c\xd6\xc9\x21\x88\x57\xd4\x2e\x27\xd2\x13\x5b\x14\x3d\x4b\x70\x8a\x9a\xe0\x74\xd3\x70\x36\x50\x03\xee\xaf\x9b\xd4\x9d\xc2\x10\x35\x8c\x37\x82\x3c\xe5\xc2\xe6\x19\x07\xf2\xd7\xf2\xb3\x41\xb5\xf0\x6e\x7e\x0a\xab\xb7\x61\x83\xda\xc2\xd3\x54\xbf\x34\x3f\x3a\x20\x0e\x0f\x86\x0a\x43\xbc\xb9\x2d\xbb\xfd\x06\x7b\x00\x2e\x19\xe5\xc7\x52\xae\xc7\x0c\x08\x81\x97\x58\x68\xf1\x6d\x05\x8c\x5c\xc2\x32\xa9\xd5\x03\xe7\xce\xcb\x45\x0e\x03\x57\x38\x46\xd1\x58\xd7\x1c\x08\xca\x8f\x9e\x40\x38\x8c\x84\xd5\xcf\x16\xc3\x61\x30\x2d\x66\x5b\xae\x7a\xd3\x79\x0c\x30\x35\x2f\xfb\x6b\x9e\x3a\xb1\x42\x60\xe4\x8a\xf1\x6b\xf7\x43\xb8\x19\x13\x07\x7b\x82\x36\x9e\xf0\x8e\x5c\x28\x37\xfb\x17\xfc\x10\x9e\xa6\xa8\x1c\xc8\xeb\xe1\xb1\x57\x41\xb0\xa0\x6d\x52\x99\xe2\xa0\xa4\xde\x96\xd4\x28\x76\xf1\xc2\x8e\x90\xc2\x5b\x7f\x64\x37\x36\x63\x9e\xf8\x26\x22\x66\x2b\x03\x4d\xff\xde\xa5\x6b\xfd\xa8\x49\x37\x6b\x6b\x46\xd2\xfe\xb1\xea\xa9\xf4\x7f\xfa\x60\x34\x4a\xd2\x7e\x16\xb2\x4b\xd0\xa7\xff\x19\x41\x0d\x4f\xce\x3e\x4f\xf4\xb4\x20\xa8\xd2\x2c\x10\x0b\xcd\xd7\x8d\x95\x48\x45\x50\xe3\x9c\x0c\x24\x43\xaf\xd0\xc2\x99\xa4\x5e\xd3\x29\x9e\xd7\xba\x62\xd3\xdd\x24\xcd\x22\xd4\x40\x8a\x6d\x7d\x4b\x4c\x35\x2e\xe7\x6a\x54\xad\x5b\xdc\x0a\x13\xaf\x6d\xa9\x82\xc3\x4e\xd1\xfa\xb0\xfb\x43\xfa\xe5\x6e\x0a\xa6\xeb\x52\xd8\x62\xef\x8d\xc2\x19\x8b\x54\x08\xfa\xad\x80\x65\x59\xdf\xab\x2e\x83\xe1\x90\x18\x1e\x5f\xa9\x35\xd0\xa6\x5a\xf4\xa9\x4b\xa7\xe6\xd4\x34\x1c\xe6\x23\x2b\x89\x52\xe3\xec\xc0\x69\x3f\xec\xd6\x08\x7b\xc1\x00\x4b\x3a\x31\x6e\x1b\x08\x6a\x8e\x45\xe4\x75\x06\x85\x3b\x86\x1a\xc6\xc2\x60\x95\xaa\x21\x97\x21\x1e\x0f\x30\x2c\xca\xaf\xc1\x70\x83\x6a\xa1\xcb\x3e\x54\xf3\x83\xfe\xf6\xba\x8d\x0b\x88\xcb\x06\x2f\x7a\x1b\x7a\xe7\x96\xaf\xa9\x3f\x0e\x36\x39\x15\x1e\x2d\x54\xb3\xd1\x3c\x34\x12\x49\x01\x16\x39\x55\x1f\x11\xcd\x90\xad\x80\x80\xa6\x56\x37\x88\x76\x5f\xeb\x22\x44\x29\x68\xfa\x98\xcc\x7f\x1b\x8f\x57\x89\xf9\xd0\x58\x43\xb6\x55\xcb\xce\x1c\xd1\xd3\xc3\x7f\xb9\x5f\x3c\x92\x85\x0c\x6b\x9c\xd1\x8d\x08\x27\x0a\x3a\xc3\x5d\x90\xc7\x5c\x75\xf0\x8b\x66\xb2\xe1\xcd\x82\x81\xe8\x7b\xf6\x5b\x12\x28\xe3\x02\xb5\x91\x3f\x66\x07\xd9\x13\x3a\x81\x20\x77\x5a\x2f\x30\x04\x28\xbc\xb6\xe5\x7e\x17\xb5\xdd\x64\xb4\x3c\x32\x3d\xb4\xd1\x96\xc2\x8b\x05\xbf\x86\x5d\xb0\xd3\xca\xb0\x6a\x27\xf7\xc7\x23\xa2\xed\x7b\xce\xfb\x88\x78\xfb\x0a\x9f\x0e\x14\x90\x44\x02\xea\xe1\xa1\xb7\xce\x2a\x03\x44\xd5\x0f\xd8\xfc\x24\x76\x4c\x72\x83\x4b\x6a\x98\x31\x71\x31\x17\xe6\xfa\x41\x9f\xd2\x88\x59\xe5\x97\x3e\xb4\x50\x59\x8f\xe2\x41\x96\x62\xde\xcc\x7f\xc3\x0c\x17\xd9\x44\xab\xca\x64\xea\xb5\x46\x54\xae\x29\x3e\xe2\xc7\x91\x73\xe8\x7c\x70\x43\xff\x1e\x6f\xb7\x8f\x8b\xe2\xc1\xc4\xa8\x03\xe9\xc7\x0d\x7b\x60\xa3\xa5\x8a\x86\x01\xab\x0c\x6a\x0a\x44\xc9\x69\xdc\x31\x40\x34\x4f\xbf\xb0\x14\x50\xf2\x95\x57\x5a\x78\xbc\x09\xe9\xfa\xb9\xeb\x78\x0b\x68\x88\xe1\x79\xbb\x0f\x66\x15\xe2\xf8\x17\x8e\x64\x20\x84\x07\x59\x03\x7f\xe6\x5b\xbb\xe7\xae\x46\x54\xa2\x23\xf6\xbd\x3d\x80\x12\x09\x6f\x77\x10\x21\x81\xf0\xeb\x91\xea\x04\xe0\x09\xc0\x29\xf1\xd7\xb7\xfd\xcf\x14\x81\xa7\x1a\x9f\x22\x81\xbb\x84\xe0\xa9\x50\xaa\x96\x36\x13\xfa\x86\x3f\x89\x7c\xf9\xac\x20\x3c\x8b\xca\x19\xc1\x6f\x0f\xb6\x6e\x9a\x8f\x12\xa2\x66\x8e\x4f\x9f\xb3\xe2\xd0\x8c\x92\xc9\x41\x08\x5f\xc5\xb9\x24\x5a\x56\x8b\x30\x64\xeb\x73\x4e\x98\xe8\x62\xc1\x73\xdc\x66\x7f\x17\xb5\xe3\x29\x7e\xa5\xff\x93\x09\xc3\x81\xa8\x33\xc8\xb9\x85\x24\x62\x3b\x05\x9f\xab\x66\xfc\x41\x53\xea\x75\x30\x6e\x4b\xed\xdc\x99\x9d\x7f\x99\xab\xc6\x94\x8b\x46\xec\x37\x3a\xf3\xb5\x3b\xcf\x33\xbe\x00\xd2\xcf\x73\x73\x5e\x1b\x83\xb9\xbb\x55\xef\xb0\x16\x5f\x40\xb1\x95\x59\x2d\xf6\xe9\x70\x5a\xe3\xab\xa2\x06\x1b\x56\xe8\x29\x47\x74\xe7\xa2\x1e\xea\xa1\x1a\x07\x7d\xd8\x6c\x75\x41\xf7\x10\xee\x17\x6e\xab\x2c\x99\x75\x72\x93\xae\xee\x77\xe2\xc4\x21\xca\x80\xdc\x1d\xd0\x3b\x18\x0e\x4c\x6c\x7d\x50\x73\xbb\x70\x31\xe2\x05\x62\x5d\x45\x5e\x30\xbd\x03\xcf\x25\x60\x3a\xb8\x9c\x1e\x00\x1a\x52\xce\x89\x7f\x88\x51\xa1\x14\xcb\x23\xcf\xbf\x3e\x50\x52\x89\x21\x10\x5f\xcb\xb9\x1e\x31\x7b\x2a\xdb\x1e\x3e\x77\x63\xb4\xb3\x0f\x00\x2d\xa0\xec\x5b\x6a\xe6\x31\x64\x25\x89\xd3\x88\x41\xc8\xfa\xab\x96\x01\x3e\x60\x1b\xc9\xb6\x8e\x9f\xaa\x62\x4f\xd4\xc7\x73\x71\x5b\xbd\xdf\xc5\xf5\xd2\x8a\xc7\x9d\xf8\xc1\xba\x07\xf3\x09\x21\xc2\x05\xf1\x85\xd3\xe5\xd2\xfb\x12\x77\x53\x2d\x33\x13\x72\x0a\x0d\xc5\x01\x7c\x82\x53\xef\x54\x17\xb2\x2a\xe1\x43\xb0\xce\x12\x03\x6a\x13\x93\x7e\x48\x47\xf3\x11\x63\x4b\x1c\x35\x9d\x54\x75\xa7\x7d\xd8\x88\x10\x06\x58\x1a\xd4\x07\x84\x05\x56\x5c\x36\xfb\x3c\x7c\x0e\xfe\xa6\x01\x86\xed\x0c\x10\x92\x84\x04\x18\x40\xbc\x63\xbe\xf6\xa7\xa3\xc2\x91\xf2\xe0\x23\xa7\x38\x15\xf7\xb4\xc0\xe0\xcf\xf3\xc0\x26\xc2\xec\xa0\xaf\x30\x03\x10\x04\xbc\x1e\x35\xed\xbd\xe6\x8e\xbc\xb1\x92\xb3\xe2\x60\xb9\x93\xed\xc2\xea\xa2\xdc\x71\xf4\x49\xd6\x5d\x2e\x65\xb7\x15\x9e\x7f\x47\xab\xdf\xdd\xd6\xaa\xd8\x58\x4d\x35\x6b\xd6\x86\xea\x86\x8e\x45\xcb\x21\xa3\xef\x68\xed\x7b\x6b\x2d\xdc\xb2\x3e\x96\xe5\x2e\x68\x22\xee\x7e\xe0\x7d\x01\xe6\x19\x1b\x51\x4d\x70\x34\x75\x30\x34\x8f\xe4\x03\x4c\x3c\x8a\x9e\xe2\x2d\x01\x74\x37\xbb\xc3\x19\x6b\xbc\x40\x4c\xcb\x89\x38\xe7\xd0\x74\x3a\x18\xd6\xf2\x64\x01\xe7\x86\xfd\xab\xb1\xe4\x89\xaa\xc4\xae\x76\x60\x39\xe4\x5d\x94\x5d\xd7\xac\x40\x7b\xb8\x7b\xb1\xe9\x64\x3a\x61\x32\xe9\x40\xd9\x2e\x3d\x24\xd6\x54\xbc\x11\x78\x3c\x27\x41\xf2\xe1\x02\x03\x1f\x80\xa8\xae\xd8\x07\x20\xe8\xa0\x50\xd1\xa1\x7a\x4e\x26\xeb\x50\xca\x0b\xa7\x96\x23\x11\x54\xf0\x39\xcf\x34\xba\x97\xc6\xe8\x1f\x3a\x7d\x6b\xee\xf5\xba\x09\x82\xb9\x88\x63\x02\xf6\xa2\xb0\x23\xb3\x78\xac\xd7\x22\x9e\x28\x5e\x54\x58\x19\x48\x31\xb6\xb7\x98\x28\x83\x23\xef\x76\x4f\x5b\xe7\xa6\xfa\x08\x65\x18\x11\xa6\x84\xfb\x3d\xbf\xbc\x82\x06\x8c\x16\x00\xed\xa3\x2b\xe6\xbb\xe9\x5f\xd7\x65\x8d\x08\x94\x1c\x75\x57\x19\xd1\x62\xc1\xfe\x44\x55\xad\xe1\xf9\xae\x4b\xb3\xf2\xae\x8b\x8d\xb0\xad\xd0\xe3\xcc\xa4\x07\xd1\x84\xa5\x88\xac\xcb\x2b\xad\xdb\x95\x0b\x92\x89\x67\x7c\xb3\xd5\xd6\x78\xae\xc0\x05\x51\xbf\x35\x3a\xbb\x1b\x09\x6c\x70\x58\x61\x16\xa0\x45\x51\x12\x2a\x80\x75\x0f\x1e\xa1\x67\x08\x3a\x25\x05\x1b\x86\x6f\x93\x81\xc1\x5f\xc5\xb8\xb8\x62\x76\x9d\xaa\xb9\xc8\x6d\x3e\x1b\x07\xfb\x10\x86\x47\x94\xa6\xef\x10\x85\x87\x55\xcd\xbc\x5e\xc1\x94\x09\x13\x20\xdd\xae\x11\xd3\xcb\x77\xfa\x39\x06\x92\xd3\x12\xf7\xe4\x95\x7c\x8d\x41\x76\x1a\xe8\xc8\x85\x3c\x1a\x83\xcc\x9b\x82\xcf\x3f\xcf\xe9\xcf\x58\x88\x76\xb1\xf1\x4d\x92\x06\x71\xee\xd8\x59\x5e\x2e\x92\x39\x83\xd0\x5d\x6e\x96\x12\xf8\x80\x35\x47\x38\xee\x89\xb2\x8f\x8d\x8c\x25\xac\x67\xc9\x5e\x6e\x5c\x81\xba\xbe\xc1\xab\x03\x31\xcd\x42\x4d\x9e\xba\xc9\x86\x7e\x8f\xc3\x3e\xe1\x62\xc2\xfa\xf5\x5a\x64\x10\x4c\x03\x0e\xb7\x12\x80\xee\x88\xb7\x16\x3e\x17\xda\x55\xa1\x6d\x40\x3b\x09\x3a\xc7\x0e\x4b\x44\xd4\x88\x25\x62\x20\x22\xc3\x8a\xd5\x55\x60\xef\x6b\x81\xb8\x41\x6c\x40\xd8\xb8\x47\x6a\x9f\xcd\x08\x12\xcb\xec\x11\x84\xbf\xc8\x04\x90\x79\x42\x0d\xf7\x19\x05\xf7\x67\x85\x57\xd1\x7a\x08\x38\x4a\xf4\x68\x01\x3a\xaa\xa1\xe4\x44\x93\xcb\x9c\xc2\x14\xb9\x81\xc2\x94\x71\xc5\x66\x91\xc1\xea\xe6\x6d\x9a\x84\x23\x91\xa2\xdb\x72\x95\xb7\x85\x45\x58\x51\x4e\xc3\x5e\x26\xe0\x28\xa1\x43\x6d\xbe\xa1\xc3\xb7\x56\x41\x9c\x91\x40\x3e\xb2\xe5\x13\xcd\x37\xcb\x38\xa6\x6f\x60\x69\xb1\x10\x36\xc6\x3e\x7d\xc4\x5c\xf6\x3b\xe6\x37\xc2\xbc\xac\x1d\x0c\xf9\xe1\x9f\x2e\xcf\xdf\x1e\xa5\x9f\x1f\x5f\x5f\x5f\x3f\xe6\xe2\x8f\xf7\xed\x86\xbd\xdf\x0a\x8e\xe0\xf2\x3f\xce\xde\x1c\xa5\x65\xbf\x78\x34\xa3\x83\x7a\x1b\xab\xb7\xd4\xfe\x13\x57\x0f\x4c\x5d\x2c\x3a\xfe\x71\xf6\xa4\x2b\x46\x03\xa2\x87\x81\xc2\xc2\x0d\x92\x67\xcf\xec\x3b\x74\x32\xc5\xce\xc3\x1f\x0e\xcb\x45\x5b\xc2\x3c\x02\x1f\x41\xc6\x86\xce\x04\x53\x9e\xfb\x43\x90\x8a\xda\xd1\x6e\xbc\x5e\x68\x40\xf6\x01\x88\xdd\x06\x9e\xe0\x1e\xd0\x65\x62\xe2\xdc\xb6\xc2\xa1\xe8\xba\x75\xb3\xdf\x14\x31\xc7\x24\x9c\xe9\x44\x94\xc5\x4f\xc3\xc2\xb0\x64\x44\x44\xe5\xa7\xe9\x9f\x58\x53\xc4\x13\x25\xb4\xc5\x59\x46\x5b\x00\x9e\x0d\x0b\x23\x36\x60\x20\x18\xd3\x00\x24\xe6\xa1\x09\xe6\x3e\xcf\x09\xe7\xa3\x4a\xf4\xfc\xc6\x76\x15\x7d\xba\x75\xe7\x39\xd0\x9a\x54\x37\x2e\x12\xeb\xe9\xa6\xb3\x0d\x2f\x62\xcf\x78\xa4\x96\x8e\xa6\xc4\x9a\xc2\x43\x2a\x66\x9c\x93\x28\x0a\xf8\x23\x40\x99\x8b\x84\x76\xa5\x7e\xed\x82\x31\xa5\x1a\x0c\xb3\x1c\x66\x04\xd1\x0e\xca\x1e\x7e\xe6\x53\x6b\x51\x4e\xd4\x6e\xd6\xfc\xea\x31\xfe\xa6\x3b\x9c\x48\x26\xe2\x9a\x13\x71\x0f\xb0\x8e\x58\xe6\xba\x1e\xee\x62\x43\x71\x4b\x79\x93\x17\x65\x94\x37\x8d\xb6\x6b\x05\x1c\xb4\x31\xda\x25\x55\x3a\x1e\xcb\xfc\xbe\x85\x43\x02\x81\x58\xf1\x64\x3a\x4a\x0b\xf8\x02\xff\xc6\x53\x97\x16\x8b\x57\xb6\x4a\xc1\x77\xe3\x25\xca\x08\x91\x75\x14\x72\x54\x16\xd4\xe2\x3b\x7f\x06\x81\x5b\x2e\xbb\x03\x98\xe9\xfc\xc8\x29\x39\x14\xa1\xa5\x56\x73\x30\x13\x47\xb8\x41\xe6\xf0\x05\x8a\xe1\xca\x26\x59\x4d\xde\x30\x3a\x91\xaf\x10\x5b\xbb\x4d\x73\x63\x7e\xdb\xa7\xf8\xa5\x81\x5d\xc2\x91\x79\x30\x1d\x94\x87\x0c\x94\x2f\x4d\x16\x57\xf7\x6b\x10\xc8\x54\x45\xdc\x9a\xcf\xbb\x28\x4a\x30\xe1\x31\x26\x52\x78\x4f\x74\x6f\xc2\xf5\xd7\x41\x0d\x9d\x94\x4f\x5d\x0b\x07\x9c\x94\xe3\xa2\xa1\xa3\x72\x50\xf4\x0b\x1c\x95\x63\x24\x8d\xdd\x90\xfd\x50\xbf\xc0\x13\x79\x6a\xd0\x63\xb9\x76\x0a\xf1\x13\x05\xa6\xa4\xdb\x22\x1c\xdb\x17\x78\x24\x0f\x4e\xb6\x5f\x22\xe0\x4e\xf5\xc4\xa3\x24\x40\xee\x5d\x1a\xdf\xa2\x5a\x2e\x67\xf3\xb6\xb9\xee\xd8\xed\x17\xcf\x39\x30\x97\xe5\xdf\xe9\x25\x7e\x0b\x08\x5f\xf5\x83\x28\xe4\x43\x12\xd5\xa2\xe8\xa9\xde\xec\x49\x22\xee\x59\x87\x41\xe4\x4f\x29\x47\xee\x5c\xdf\xd2\x49\xec\xd8\x72\x66\x52\x84\x36\xba\xeb\x8c\xbf\xe0\xb0\x0c\xed\x33\x2b\x4b\x51\xe8\x92\x53\x14\x8c\x3f\x0d\xe1\xb6\x2b\xc1\x98\x4d\x6e\xa5\x45\x7c\xf5\x9a\x23\x10\x96\xc1\x11\x18\x11\x43\x05\xf9\xd4\x83\x84\x8e\x89\x04\x61\xb8\xf4\x10\x8a\x20\x2c\xfa\xe7\xaf\xdf\xca\x4f\x58\xa8\x6b\xa0\x20\x98\xa8\xf3\xe5\x78\x62\x76\xef\xb3\x29\xfb\x77\xcb\x13\x5f\x05\x51\x73\xd8\xfb\x6b\xf8\xe5\x20\x8a\x36\x5f\xe2\x1a\x88\xff\xba\x54\x92\x81\x7d\xb1\x8b\xb6\x7c\x3c\x2c\x46\xc8\x11\x54\x5f\xe2\xc3\xa5\xeb\x35\x0e\xff\x71\x69\x39\x4c\x34\x9e\x06\x23\xf7\x18\x31\x5b\x00\xa2\xbb\xfb\x5d\xda\x55\xac\x3b\x55\x02\x1d\x34\x08\xea\xf0\x11\x7e\x41\x3b\x78\x91\x2c\x1c\x6b\x78\x3f\x84\xe0\x04\xdd\x3a\x75\xf8\x61\x17\x9d\x5e\xfc\x8e\xf5\x85\x91\x59\xd4\xef\xa8\xb4\x6c\xb1\xa5\xcd\x76\xba\x69\x56\x90\x22\xf1\x00\xa1\x3c\x35\xb1\xe7\x27\x59\x08\x11\x1c\x70\x8a\x25\x0e\x47\xfc\xd5\x96\xea\xff\x24\x21\xc2\xa5\x7a\x92\x1e\x9c\x5f\x34\x09\x12\xb5\xb8\x66\x5a\x1e\x8e\xd4\x16\x48\x2d\x2a\xe3\x43\x10\x9b\xa6\xda\x7b\x89\x50\x3e\x24\x93\x45\xe8\x7c\xc2\x62\x0a\x47\x8a\x90\xb1\x07\x1d\x88\x18\xb1\xa5\x8e\x99\xef\x98\x00\xc6\xce\xf6\x94\xf7\x78\x38\x3d\x01\xbc\xeb\xf3\x5f\xcb\xff\xf8\x5f\xff\x97\xb5\xa2\x0d\x6d\x8a\x88\x8c\xa1\xc1\x25\xfd\xf4\x9a\x9b\x9a\x7f\xf1\xe5\x31\x58\xb1\xef\xf8\xf0\x5e\xce\x35\xa8\xb3\xeb\xe7\x79\x44\xa2\xfc\x38\x82\x91\x37\x5b\xd1\x0d\x68\x1c\x53\xee\xa9\x1c\x37\x8f\xc3\x3a\x6c\xde\x32\xdd\x22\x5c\xd0\x3b\x05\x94\x45\x8f\x11\xf5\x3a\xaf\xd3\x3b\x8a\x5b\x6e\xfa\x50\x05\xf8\xa6\x7d\x27\xc9\xfb\xa6\x5d\x7d\xf0\x81\x53\x9d\x69\x7e\x14\x34\x15\xc7\x46\x86\x71\xa1\xc7\x0e\x00\xfa\x70\x64\xbe\x46\x9b\x94\x97\x4c\x2d\xe3\x87\xbd\xe4\xd0\x2e\x91\xab\x61\x41\x66\xfe\xbe\x33\x73\x7b\x92\xb8\x8b\x7a\xbb\x1d\xbd\xb8\x08\x6f\x24\xd3\x67\x9a\xe8\x57\x24\x7a\xed\xca\xce\x33\xfc\xc1\xa1\x31\xf9\x8d\x38\x56\x41\xc9\x85\xd9\x6b\x24\xd0\x5a\x46\x02\xc2\xbe\xc2\x69\x88\xff\x26\x08\xb6\xa7\x4a\x37\x4e\xd5\x2f\x4d\x1f\x04\xf4\x8b\xde\x45\x08\xdc\xb4\x10\xe8\x33\x7a\xec\x80\x2b\x07\x56\x26\x6e\xdc\x47\xe1\x5f\x81\x42\xa4\xde\x06\x1d\xf9\xe6\x3e\xd8\xe0\x96\x45\xa3\x47\x41\x77\xcd\x96\x5c\xb5\xc8\x83\xa0\x13\x04\x1f\xad\x23\xd3\xd2\x6e\xe6\x9b\x09\xd6\xcf\x5a\xae\xe3\x7d\x31\x3c\x39\x34\xa7\x95\xf4\x93\xc0\x47\xce\x89\x7a\x92\xcb\xc5\x19\x5e\x92\xd3\x0d\x1d\x0a\x36\xd1\xd1\x0e\x15\xb1\xf8\xf6\xd3\x01\xc7\xd4\x71\xa0\xde\xaf\x77\x4d\x1d\xd7\x71\xbb\x73\xea\x1f\xbd\x09\x9e\x0e\xc2\x17\xea\xaf\x06\xd1\xf8\x5c\xd6\x54\x58\xbe\x3f\x72\x2f\x1b\x83\x06\x52\x51\x84\x02\x57\xd3\x97\xea\xff\xf5\xba\x97\x28\xf5\x1f\xbb\xed\x8d\xc3\xd3\x0e\x7b\x3d\x8a\x1f\x17\x75\xfa\xeb\xe2\xc8\x1d\xbc\x36\x8d\x78\xc5\xf0\x3c\x37\x0a\x06\x81\x01\xde\x5a\x24\x0e\x0d\x11\x76\xd8\x69\xf0\x82\x6b\x38\x55\xea\x4b\x50\x08\x51\x4b\xdf\x1d\x19\xe2\xc0\x3d\xc7\x6d\x21\x22\x86\xbd\x64\x1e\xe3\x1c\x27\xc2\x4e\xde\x5a\x22\xdc\x6b\xe3\xab\xf2\x7f\x24\x6c\xc4\xf4\x5d\x02\x9f\xf7\xae\x4d\xcd\x85\x9d\xd9\xf0\xe7\x95\x07\x7c\x5e\x30\x7c\xc9\x81\xc2\x33\x5a\x8f\x39\x08\x42\x82\xdc\x41\x38\x64\xe5\xda\x33\xbd\x3a\xb3\x88\x51\x83\x74\xcf\xf2\x60\xb8\xac\x97\x83\x1e\x48\x7e\x43\x12\x9d\xcc\x19\x96\x8f\xdb\x88\xfd\x04\x2c\xd5\x5d\xe7\x9c\xe1\xc3\xa5\x13\xd6\x16\x25\xbc\xf9\x4f\xe4\xcb\xe5\xe8\xb9\x4a\x43\x4c\xfb\x4e\x48\xf8\x60\xf8\xf6\x04\xa9\xba\xdb\x29\xae\xd9\x4f\x99\x26\xe5\x66\xc7\x53\x98\xa7\x4e\xb3\xc7\x22\xa9\xec\x82\x22\xbe\x69\xaf\x20\x0d\xff\x30\xac\x4b\x5e\x82\xd1\x5d\xf3\x6d\x73\x9d\xc8\x96\x39\x83\xbb\x82\x84\xbb\xd5\x94\xb8\x4b\x92\xc6\x02\x89\xc6\x22\x4a\x25\xe8\x82\x46\xab\x19\xe7\x0f\x3c\xfc\xb1\x63\x38\xdf\x7e\x8b\x5e\xcd\x07\x11\x75\x8c\xa9\xe5\x3a\x25\x76\x79\x97\x5a\xaf\xdb\xaa\x2f\x7d\xb3\x7f\xe5\x9f\x71\xbb\x21\xc4\x97\x34\x8c\x87\x7f\x87\xcd\x1d\x99\x2e\x0b\xc1\x0d\x55\xc9\x26\x51\xdc\xa4\x15\x7d\xcd\xd5\xfa\xe1\x1e\x54\xf3\xfd\x08\x21\xbe\xa4\x1f\xdc\x0a\x0c\xc0\xe5\x5c\x71\x4b\x7f\xe8\x20\xa8\xb1\x1a\xa3\x4b\xfb\x61\x17\xbd\xe7\xfa\x55\xb0\x4f\xc3\x50\xa4\x18\xc8\x1d\xac\x3a\x1e\x6f\x98\x92\x23\x17\xba\x13\xa2\x81\xd8\xf3\x4c\x86\x71\xba\x7b\x89\xc3\xd2\x9e\x4b\x3a\xd0\xc0\x52\xc7\x83\x4d\xee\x3a\xd2\x2f\x2f\xca\x41\xb6\x3a\x53\x79\x4e\x32\xef\xde\x70\x05\xce\x02\x18\x89\x5c\x17\x6e\x19\x10\xec\x6c\x26\x0b\x09\xe5\xef\xd6\x38\xb3\xba\xa0\xd5\x71\x65\x8e\x55\x03\xca\xb1\xe8\x31\x9c\xf1\xce\x50\x2a\x0b\x14\xab\xcc\x94\x8f\x4c\x56\x75\x66\x04\x80\xda\xe6\x37\x91\xa1\x0e\xec\x8a\xb7\xf1\xd3\xb7\xb7\xe8\x62\xc6\x5d\xf1\xbb\xb6\xc4\xc7\x77\x04\x73\x50\x01\x33\x0b\x97\xfa\x98\x40\x3c\xd9\xad\x5a\x38\x21\xd8\x5c\x33\xb3\x08\x48\x01\x15\xfe\xe0\x46\xe9\x5e\x5c\xf4\xdc\x00\x37\xc5\x54\xd1\x83\xdb\x98\xc2\x57\x74\x00\x6c\xe3\xf6\x1e\x80\x2d\x88\xeb\x19\x75\x23\x60\x01\xb7\x75\x44\x9f\x4a\xfc\xf2\x8e\x80\x6f\x7c\x61\x47\x8e\xac\x17\x1a\x5b\xba\x28\x26\xd7\xff\x6d\xfd\x1b\x1c\x73\x40\x9c\x51\xf0\xf2\x01\xc1\x47\x4f\xbb\x3b\xa2\x0f\x4c\xd6\xac\x5a\x18\x47\xa8\x09\x9d\x6e\x67\xbe\xaa\x9a\xa6\x10\xe7\xd6\xba\x0f\xcd\xec\xe2\x80\x21\x6c\xe1\xd5\xb7\x37\x2a\x92\xf0\xe0\xe2\x78\x25\xde\xba\x46\x0e\x5f\xb8\xee\x95\x80\xf6\xef\x81\xf6\x0f\x89\x7b\xad\x8f\x97\xb2\x7d\x27\xa2\x5b\x91\x2b\x2f\x84\x14\xf7\xb1\xc4\xd3\x61\x6c\xf1\x5b\xe3\xba\xc7\xf1\xec\x87\x0f\x1b\x74\x12\xfe\x6b\x65\xb2\x9c\xbd\x91\x98\xa8\x55\x11\x33\xd6\x1b\xc2\xc1\x16\x0f\xe4\x2e\x38\xc4\x6f\x53\x57\x62\xbf\x72\x26\x5f\x34\xf6\x04\x63\xca\x76\x12\xa2\x01\xef\xdb\x4b\xd8\x45\x4d\xe1\xb0\x8b\x49\xdf\xf4\x10\x28\xae\xf8\xef\x0f\xe9\xfd\x22\xf1\x43\x87\x96\x91\x15\x3a\x2a\x25\xc8\x77\x90\x1f\x04\x21\x87\x4d\x7b\x6b\x61\xd5\x7d\x0d\xe8\x26\x74\x99\xfb\xa0\xdb\xda\x49\x54\xba\xef\xa6\x5a\xcc\xf8\x86\x34\xd5\xfb\x61\xf7\x4c\x3a\x73\x10\x7e\xb9\xac\xe0\x97\xcb\x44\xd3\x75\x14\x24\x44\x13\x12\x66\xec\x5c\x2c\xd0\x28\x39\xde\x15\x7d\x3a\xc2\xbb\xc4\x49\x1c\xd3\x25\x4a\xc8\x17\xa3\x56\x4c\x93\x1d\xa6\x99\xad\x9c\x4f\x31\xab\xb9\xa8\xf6\x38\x1e\x64\x98\x25\xa6\x86\x51\x92\x3e\xd9\x18\x8f\x44\xf4\x88\x61\xda\xa6\x59\xf1\xdb\x03\xf6\x24\x70\x30\x3c\x15\xac\xe3\x3a\xcd\x6c\x3a\xaa\x02\x1e\x98\x61\x0a\x2e\xb8\xfa\xbc\x8b\x4b\x63\x7d\x86\x09\xea\xbd\x3e\x02\xa4\x83\x76\xbe\x58\xab\x17\xcf\x04\x21\xd9\x79\xd9\x11\x93\x1c\x9a\xa7\x20\xe5\x59\xcd\xd4\x1e\xd1\x9c\x84\xe1\x07\xd3\xf1\x66\x69\x90\xcb\x6e\x08\xec\xf3\x82\x90\x99\x8d\x06\x9d\x62\x9f\x04\x17\x1e\x33\x3d\xc7\x72\xec\x6e\x2d\x14\x6c\x71\x1c\xfb\x40\x43\x72\x6a\x49\x11\x47\x6e\xd9\xeb\x7c\xcd\xba\x6b\xaa\xe5\x47\xf0\xc4\x5a\xe7\x85\x88\x1c\x3e\x52\x7a\x03\x6b\xd9\x5f\x54\xc7\xa0\x97\x1e\xc2\x55\xf3\xf5\x5d\x85\x4a\x8d\x83\xc7\x50\x6f\x06\x9d\x8c\x78\x9e\x81\xdc\x51\xc3\xa0\x8b\x93\x55\x7c\x45\x27\xf9\x9d\xdf\xd5\xc2\xbd\x4e\x7a\xca\xa1\x98\xdb\x39\x73\x3c\xde\xe0\xca\x85\xb9\x7f\x45\x6a\xb9\xe9\xe2\xb7\xf5\x0c\x1d\xe2\x03\xf9\x54\xf5\x87\xfa\xd6\x96\xdd\x4d\xbd\xc8\xf0\x48\x6d\xb7\xd6\x1b\xcb\x77\x25\x44\x8e\xf4\xc1\x8c\xd2\x9e\xe4\x1a\xf8\xac\xc4\xdd\x5e\xf7\x40\xa2\xf5\x3f\x5c\x50\x3a\x0c\x89\x69\xff\x7b\x0c\x9e\x88\xd2\x26\xdd\x91\xec\xd6\x3f\xba\xb5\xa1\xc1\x58\x02\x86\x18\xe0\xb6\x45\x57\xf8\x59\xfd\x2f\x18\x41\x70\x5b\x1e\x0e\x83\xc9\x40\x57\x3f\x78\x45\xf8\xb8\x0c\x23\xee\x21\x5b\x3e\x70\x70\x6d\x36\xeb\x50\x73\x29\xdd\xed\xec\x11\x62\xbd\xc3\x3a\x30\xa0\xb0\xdd\x5b\x66\xe8\x41\xd4\x8b\xbb\xc7\x18\x6e\x42\xf2\xd0\xfc\x7e\xc7\xa6\xbd\xa9\x7b\x61\xfe\x17\xfc\x0e\x99\x82\xc4\xff\xcf\x56\x4d\xdb\xd0\xf4\x48\x24\x1e\x7d\x13\xe0\xa5\xa5\x75\x13\x05\xa0\xc0\xbe\xc9\xf6\x1a\x3d\xc9\xca\x9c\x21\x99\x84\x0b\x0e\xa5\xe4\x4b\x61\x8b\xb6\x32\xac\x96\x5c\xa8\x32\x1b\x7b\xb6\x95\x3a\xb6\x8c\xa0\xa4\x96\x69\xe6\x6c\xb3\xaf\x9e\xa4\x00\x3e\xd7\x94\x00\x16\x17\x21\x1c\xde\x86\xd0\xb5\xdf\x65\x3c\x54\xc4\xe1\x90\xe4\xf4\x0d\x92\xd3\x2b\x4e\x1e\xb7\x60\xbd\x72\xc5\x06\x9d\x3a\x54\x8e\x43\x1e\x0c\xcb\xbc\xe0\xc8\x08\x43\x78\xc3\xdc\xba\xcc\x77\x23\xbc\xbd\xa2\xc4\x11\xd6\x00\x39\x46\x00\x60\x0f\x63\x21\x2c\x55\x15\x38\x75\x85\x25\x5e\x53\xd2\x21\x68\x98\x02\x0c\xe1\x6b\x16\x15\x0f\x94\xd0\x3d\x7b\xd8\x2b\xbd\xbd\x19\xf5\xaa\x99\xff\xad\x5c\xf4\x9d\x41\x9f\xcb\xcf\x00\x6a\xde\x34\x3d\xbf\x68\xbb\x63\x71\x0b\x26\x5a\x82\xa6\xe7\x96\xce\xe2\xd6\xe2\xe3\x08\x53\x02\x3d\x46\x95\x40\x1f\xc6\xd5\x96\x03\x25\x52\x5b\xed\x7e\xd1\xef\x69\x81\xba\x06\xcf\x2e\x39\xc0\xe2\xa5\xcb\x18\xb5\x38\x2a\x19\x52\xe8\xb0\xf0\x54\xcb\x0b\x12\x22\xca\xc9\xa6\x4f\x38\xe7\xd6\xb6\x47\x65\xc3\xc6\x47\xc5\xa7\x56\x0a\x9e\xb8\x61\x85\xfa\x7c\xbf\xf8\x58\xf6\xec\xf7\xb3\xce\x70\xd5\x1c\xd6\x75\x61\x60\xe9\x73\x80\xa5\xaf\x08\x2c\xbd\x82\x8a\x66\xa2\x56\xda\x74\xb6\x65\x9f\xc3\x64\x20\xa8\xe5\xe5\x09\xcd\x00\x27\x17\xf9\x54\x29\xa8\x6e\x32\x95\xb2\x75\x15\xb2\xe0\x13\xd4\x70\x0e\xed\x8e\x0a\xde\xc7\x0e\x64\xaa\x36\x0e\xb2\x23\xbb\xdf\xe2\x66\x21\x2f\xbf\x70\xd8\x1d\xea\xc3\x3b\x49\x09\x60\x71\x92\x20\x58\xe3\x91\xb8\x1d\x47\x24\x77\x02\xbf\x8a\x19\xa5\x70\x30\x0f\x2c\x8c\x8b\xe0\x2e\xd8\x79\x78\x0a\x70\x97\xcb\x62\x3a\x08\x69\xcd\x1b\xa0\xb5\x3c\x84\xd3\x46\x3b\x41\xa5\xb0\x15\x39\xc6\x89\x01\xbd\xc6\x41\x27\x9a\xc3\x45\x2e\xec\xe7\x2d\x0a\x3a\xa7\x29\xec\x9d\x0f\x94\x2b\x98\x48\xaf\x90\x59\x25\xc5\xe4\x2d\xbc\x5e\x67\xdf\x96\x17\x45\x8e\x91\xb4\xe8\xb5\x74\x4d\x33\xff\x54\x17\x01\x4b\xd3\xf5\x09\x63\x7d\x18\x17\x16\xf1\x78\xd4\x8f\x0d\x69\xdf\x85\xcf\x5b\xbf\x0d\x8d\xe5\xaf\x1a\x8c\x32\x18\x58\x6c\x40\x64\xc3\xbc\xdb\x47\x76\xa6\x75\x84\x91\x56\x74\x64\x10\x90\xcd\x86\x66\xf0\x6a\x9f\xda\xd2\x08\xa4\xc4\x15\x95\x7b\xae\x4d\x58\x1a\xa7\x17\x3b\x0e\x0c\x6a\x78\x83\x93\x4d\x80\xe5\xf1\xb3\xc8\xd0\x2e\xf3\xc1\x5e\x0c\xa2\xa1\x9b\x85\xc1\xda\xbe\xb6\xc7\x75\x8c\x0c\x0e\xbd\x67\x65\x11\xb4\xbf\xf0\x49\x2b\x8f\x8b\x80\x52\x70\x71\x1f\xd3\x48\xd5\x65\x21\x51\x0c\x03\x70\xe7\x03\x22\x61\x70\xa5\x93\x08\x14\x37\xf9\xe1\x03\xf2\xc1\xcd\xaa\x11\x0e\xee\x30\xd9\x28\x3b\x53\xbb\xc4\x51\x0d\x41\x19\xa8\xf4\x84\xb0\xd9\x1a\x54\xbc\x52\xc3\x7a\x24\x84\x6c\x66\x33\x76\x57\x5d\x07\x23\xce\x4e\xe2\xdd\x6b\x55\x0d\xed\xee\xbd\x31\x40\xdf\x7a\x25\x17\x23\x78\xfa\x19\xc4\xff\x2f\x2f\x41\x86\x1d\xf0\xef\x41\x1e\x68\xff\x9f\xf2\x1e\xe4\x50\xa3\xdd\xc5\x5d\x99\xb0\x88\x3b\x1e\x3e\x0a\x71\xc0\x1c\x8e\x1f\xcc\x9b\xc1\xb9\x28\x66\x91\xd1\x85\x61\xc4\x2a\x51\x22\x64\x81\x48\x88\x4d\x27\x90\xe4\xd5\xed\xa6\x69\x17\x6d\x19\xb8\xdf\xb0\xbd\xc0\x1f\x2c\x6a\x4d\x4a\x8c\xa3\xcd\xc7\x5d\x90\x94\xf1\x2d\x9d\xa4\xab\xa2\x27\xd5\x48\xbe\xa5\x6a\xed\x66\xd0\xf6\xe8\xd5\x98\xa5\x0d\x43\xc5\x42\x89\xa7\xcc\x6a\xd0\xe5\x01\xbb\x8a\xba\x2d\xa5\x24\x52\x85\xf9\x9a\x29\x47\xd4\xac\xa0\xf7\x92\x12\xc6\x61\x94\x14\x79\x6d\x0d\x8f\x08\xcb\x97\xa6\x8f\x0d\x5d\x82\x4e\x6a\x35\x83\xce\x05\xb5\x02\x6a\x9a\xe3\x06\xbd\x19\x1a\xfe\x4a\x2a\x9c\xae\xd8\x4a\xb9\xeb\x35\x65\xa7\x0f\xcd\x73\x74\x45\x49\x81\xf2\xa4\x80\xf9\x20\xeb\x4a\x4e\xdf\x86\xe9\xc1\x03\x6c\xc8\x75\x4f\xaf\x4d\xc0\x04\x66\x28\x79\xcb\xc1\x1d\x7f\xd0\x40\x38\xc1\x43\x6c\xec\x20\x25\xcf\xba\xec\x36\xdc\x5f\x0e\xf4\x8d\x2b\x0c\xd6\x02\xb3\xcc\x90\xe3\xed\x25\x5c\xe8\x12\x9b\x59\x89\x25\xaa\xbc\x15\xa2\xc8\x64\xf9\x40\xc3\x4f\x41\x2e\xd0\x90\xb9\xcf\xd9\x08\x2b\x00\x29\xec\xdd\x6a\x3f\xa2\xbc\xef\xdb\x6a\xbe\xe7\x7b\x51\xb5\xff\xe0\x45\x29\xc6\x26\x2e\x6f\x04\xdb\xed\xcd\xa5\xe2\x52\xbf\x0e\xc3\x0e\xde\x9f\x1f\xc0\x49\x08\x2c\xeb\x96\x04\xc0\xb2\x2a\x70\xad\xe0\x00\xe4\xb2\x31\x82\xd8\xf2\x8e\x93\x75\x39\x2f\x4f\xe2\xad\x45\x7a\x79\xac\x39\xdd\xb6\xdf\x59\x98\xf6\xcb\xb3\xab\x8b\x5b\x68\x89\x41\x95\x26\x00\x19\x10\x06\x67\x29\x71\x20\x2b\xa0\x10\x35\xba\x51\xe3\x72\x3d\xd6\xc3\x6c\x47\x88\xad\x9b\x86\xbb\x6d\xdb\x97\xa7\x73\xf8\x15\x74\x0e\xd8\xcf\xb6\xe0\x52\x66\x96\x9e\xed\x37\x7d\xc5\x56\x60\xd6\x9a\x5a\x22\xf1\x23\xf1\xe5\x2e\x6f\x2d\xd6\x28\xc2\xee\xa4\x0f\x8e\x1e\xcc\xa2\xd5\x97\xf5\xf2\xda\x9e\x3c\x7c\x78\xf5\xe6\x92\x3e\x17\xed\x8d\x5c\x84\xea\x48\x3f\x56\x3b\x06\xd3\xf7\x92\x79\xc0\x94\x02\xd8\xbf\x20\xc5\x96\x0a\xdf\x98\x95\xed\xa7\x6a\xe1\x08\xe6\xe2\xf8\x0c\x7a\x07\x4a\x0a\x17\x9f\x36\x8d\x40\x41\x26\xf9\xf9\x4e\xd0\x74\x34\x91\xe4\x67\x0c\x84\x1f\x0a\x66\xfb\xca\x9d\x21\x30\x8c\x68\x32\x14\xcf\xf4\x35\x49\xc5\xf4\x48\x54\x89\xa1\x03\x89\xc5\xb3\xb6\xa1\x44\x19\x17\xb9\xcb\x2c\x7d\x16\x31\xb3\x70\xe7\x1a\x3c\x2b\xfc\x65\xb6\x3f\x61\x65\x81\x94\x71\xdb\xa0\x27\x83\x2b\xc4\x25\x22\xc8\x4c\xf8\xab\xbd\xbc\x12\x57\xed\x5f\xda\x19\x95\x88\xde\x60\x19\xe1\x75\xc2\xa6\xe6\x16\x3b\x9a\xa0\xf6\xc1\x7e\x1f\x57\x7c\xd7\xb6\x2f\xba\x38\xd3\x81\xb9\x7b\x28\xd5\x81\xc5\xd7\x51\x0a\x9b\xef\x76\x6e\xdb\x08\x1e\xd7\x01\xd9\x06\x20\x9f\x84\xe1\x04\x10\xb4\x08\xba\x41\x3d\xe2\x2f\x16\x02\xb1\xdb\x98\x02\x0c\xb7\x1e\x4d\x6e\x96\x4b\x0e\x89\xc5\xb1\x1b\x35\x9e\x09\x22\x64\x9d\xb1\x01\xb6\x95\x14\x2f\xc8\x8c\x95\x72\x50\x72\xf1\x98\xec\x5d\xd6\x77\x48\xe4\x53\x85\x81\xb7\x7b\xf7\xa0\xf5\xbb\x7d\x2d\xe7\xa5\x20\x4b\x1b\xe2\xac\xb0\x11\x48\x2f\x6d\xd3\xf4\xf6\x78\x42\x20\xba\xbc\xa3\x64\xda\xd3\xfa\xb5\x43\x30\xdf\x74\x2d\x32\x09\x06\x1f\x94\xc1\x3d\xdb\x02\x66\xf4\xe3\x42\xd4\xef\x71\x09\xea\xf7\x01\x70\x31\xcc\xb0\x8d\xff\x12\xbf\x84\x49\xbb\x1e\xb3\x99\xa7\x52\xa3\x0d\x58\xd2\x86\x74\x13\xe2\xa0\x98\x7b\xc2\x38\xb5\xbb\xb9\x49\xd2\x20\xc8\x50\x7a\xf1\xa9\xa1\xbc\xe0\x53\x43\xd9\xc7\xa7\x6a\xc7\x06\x3d\xe8\xba\x8d\x4d\xc4\xe5\xe5\x9b\x78\xb6\x7d\x6e\xf0\x26\x0e\xdb\x8b\xdd\xe3\x20\xae\x1c\x68\xee\x5e\xca\xce\x81\x8f\x82\x12\x8a\xcd\x10\x7f\x9a\x3a\xac\xa3\xfb\x7d\x53\xf5\xe5\xf7\xf7\x70\x75\x7e\xaf\xaf\x8a\xf9\xbd\x47\xe1\xba\xa9\xe0\x0b\x10\x2c\x9c\x6a\x71\x00\x3d\xee\xf0\x1e\x3d\xdb\x99\x8a\x63\x75\xd5\x96\xb6\xbf\x9f\x04\x2f\x69\x8e\x48\xda\x6f\x03\x8e\xa0\xc3\x2d\xc0\x3a\xc6\x8e\x25\x6d\x90\x91\x91\xbc\xd0\xcb\x43\x83\x6c\xa0\xf9\xce\xaa\x79\x8e\x64\xdf\x43\x09\x40\x6b\x01\x69\x61\x4c\xe1\xfb\x87\x78\xb2\xaf\x6b\xb8\x7e\x58\x11\x7b\x0c\x19\x7a\xb6\xd1\xbb\xc9\x50\xb0\xe9\xb3\xc9\x5a\x00\x63\x77\xea\x8b\x33\x1e\x70\xa8\xb1\x18\x0e\x18\x3e\x4f\xd5\xdf\x4b\x09\x30\x18\x0c\xfb\x8c\x0e\xc3\xdb\xfd\x16\x8f\x26\x5e\x72\x3c\x34\x3c\x7b\x39\xea\xd6\x8e\x24\xfd\x3c\xec\x11\x12\x1c\x13\x12\x77\x46\xf6\xe5\xc8\x36\x7a\xc3\x25\x2e\x8f\xf0\xe8\x48\xdf\xe0\x4a\xcb\x61\x87\x36\x21\x2f\x96\x46\x85\xde\x71\x9e\x13\x63\x27\x0a\x9b\x27\xb4\x23\x15\xf3\x34\x9c\x24\x95\xdf\xf7\xe5\x9e\x2a\x2f\xeb\x15\xc8\xf4\xcf\xfc\x93\xc4\x1d\xfe\xe9\x10\x24\x2e\x84\x50\x78\xb1\x47\xc3\x53\x73\x2a\x84\xde\x8b\x52\x1c\x2d\xdc\x29\x97\x04\x33\x13\xee\x02\x67\xf8\x3d\xdd\x41\x85\x1d\x1f\x4d\xe2\x7c\x9b\x45\x5a\x53\x4d\x30\x77\xaf\x7e\x7e\x73\x3e\x80\x9c\x60\x06\x9a\x33\xc1\x3c\x34\x67\x82\x55\xc8\x6d\xad\x1b\x02\x6e\x68\xa7\x47\x20\x90\x07\x07\x20\x04\xed\x2d\x33\x40\xc9\x93\x15\x29\xe9\x17\x44\x59\xe2\x20\x23\x44\x2f\xbf\x63\xa0\xe0\x85\x26\x81\xb2\x07\x9a\x46\xad\xd6\x61\x9b\xb5\xdc\x34\x7a\xae\x23\x26\x42\x01\xd7\x11\x13\xfb\xc9\xee\x19\x34\x3b\x02\x55\x85\x3e\x68\x25\xf0\x17\x9a\x64\xa0\x06\xe2\x6b\x36\x08\xad\xda\x75\x93\x08\xb7\x72\xd2\xeb\x09\x7e\x45\x53\xa7\xcb\x8f\xd7\x8b\xc0\xfa\x15\xc8\xef\x3e\x4b\x09\x03\x5e\x2d\x1c\x62\x4c\x67\xfc\xf2\xc4\xbf\x5d\x05\xf5\xf2\x60\x30\x9b\x6a\x59\x3a\x65\xb4\x8e\xe6\x0d\xa5\x45\xc0\xeb\xbe\xdf\x75\xe6\x16\x8e\x97\x96\xd2\x73\xfa\x31\x18\x44\x58\x95\x8e\x64\x54\xd3\xae\xc2\x05\x41\x80\x17\x49\x98\xc6\xb8\x41\xeb\xee\x10\x80\xeb\xf6\x30\xe4\x71\xab\xd6\x31\x4e\x5b\x21\xfe\xc9\x78\x2f\x0b\xb8\xd6\x59\x06\x98\x6c\x99\xa1\x74\x97\x64\x18\xec\x92\x66\x2d\x34\x5b\xb4\x12\xa2\x8e\xff\x5c\xb1\xa9\x86\xcb\x09\xd7\x9e\xa5\x75\x44\x7b\xc5\x5e\x1c\xeb\xf4\xd3\xc3\xfb\xf0\xfc\x82\x26\xcb\x98\x08\xe9\x1f\x03\x94\x9f\xcb\xc5\x3e\xb8\x39\xfc\x59\x7e\xab\xaa\xde\x57\xd3\x98\x71\xf0\xbe\xc6\x73\x0e\x17\x92\x12\xc0\x4c\xc5\xa9\xb4\xae\xc3\xc4\xd9\x4c\x9d\x0f\xb6\xef\x9a\xc7\x61\x96\xa1\xcc\xe2\xca\x0c\x99\xe4\x67\xb6\x11\x47\xab\x81\x11\x96\xc1\x86\x12\x4f\x98\x86\x58\x57\x81\xc1\x9b\xe5\x4d\x74\xdc\xb2\x9a\x1d\xb3\xac\xdd\x2c\x80\xc5\xf1\x21\x78\xea\x55\xfa\x20\xf9\x77\x99\x58\x26\xef\xc5\x6c\xe9\xc3\xe0\x81\x39\xd3\xee\x07\x66\x74\x91\x0b\xf9\x7d\x79\xd4\xa1\x2d\xeb\x20\xbc\x9d\xfc\x2a\x46\x2f\x42\x59\x2c\xe6\x6f\x7d\x2c\xe6\xe8\x99\xe9\xe1\x53\x11\x2e\x74\x3f\x6a\x65\xbb\x44\x79\x16\x24\xe8\xc1\x93\xae\x5d\x3c\xb9\x1f\x46\xe5\x67\x7d\x69\x1c\xf0\x3a\xaa\x52\x46\x67\xcf\x1b\xfc\xa6\x4f\x0a\xc8\xef\xb0\x5e\x51\xea\x49\xd5\x1c\x1f\xdb\xc5\xfc\xd7\x1a\x86\xe1\xb9\x0d\x51\x51\x58\xdb\xb0\x42\x8d\xba\x3d\xae\x6f\xf0\xe2\x42\xf0\xaa\x44\x53\x7f\x4d\xc7\x26\xe3\xfb\xfe\xa6\xc1\x9e\xbf\xba\x5b\x2e\xfe\x96\x62\xdf\x7e\x0f\x68\x41\x66\x74\x7a\x3a\x3d\x79\x20\xa0\x84\x3c\xc6\x65\xb3\x48\x3f\x6e\x9f\xc6\x98\x32\x86\xd3\xa8\x91\xde\xbf\x0b\xc2\x72\xc3\xc9\x58\x32\xaa\x4e\x43\xaa\x4a\x30\xf4\xef\xdc\x63\x56\xc9\x7b\x8e\x8e\xfc\x21\xc9\x57\x3c\x26\xfa\x3f\xc1\x63\x73\xe2\xa3\x00\x1a\xa5\xcf\x44\x7e\xf2\xd7\xb7\x5c\xf1\xb7\x69\x57\x12\xd3\x44\x5c\xdf\x6f\xb7\x48\xd8\xd2\xd1\x9a\x58\x11\x27\xac\x91\xc0\x6f\x26\xe2\x67\x81\x9f\x45\x7e\x83\x5f\xd7\xf8\x75\x5d\x96\x1f\xa5\x30\xb8\x2a\x15\xa7\xc3\xf9\x1a\x29\x37\xf8\x7d\xc3\xf1\x6f\xef\xb3\x7f\x16\xb7\xa3\xef\x17\xd8\x0f\x44\x13\xe6\xe6\x34\xdd\x7e\x50\xba\xbc\xae\xff\xd4\x3f\xb4\x7f\x9f\xaf\xfd\x6f\x34\x09\x5f\x94\xc2\xcd\x6b\x92\x7c\xde\x07\x6b\xec\xd7\x56\xa1\x7c\x53\x2a\xf7\x43\x13\xe5\x93\xd2\xda\xfc\x3a\xf3\xfd\xd2\x2f\xa4\xfa\x5e\xe9\x17\xa1\xb7\x68\x9b\x1d\x47\x16\xfd\xe0\x1e\xa2\xf4\x6f\x81\x9d\x52\x9e\x46\x4f\x42\x38\x49\xf6\x02\xde\xf0\x83\x76\x78\x87\x97\x7d\x4a\x67\x89\x45\xfc\xad\xea\xdd\xde\x9d\x4f\x7d\xb8\x69\x01\xf3\x21\x98\xc4\x50\x9d\x1f\xf8\x91\xd7\xcf\x68\x76\xb3\x79\xa5\x8f\xbe\x95\x29\x1f\x06\x1e\xfe\xdb\xbf\x01\x9c\x3e\xff\xfd\xdf\xd3\xb3\xe7\x8f\xd2\xf2\x33\x07\x94\xeb\xd2\x6d\xfe\x19\xa7\x02\x85\xa2\x9f\x2f\x22\x40\xf6\x7f\x85\xc9\xb1\x5e\x43\xe9\x53\xdc\xb8\x7b\xfa\x7f\x01\x00\x00\xff\xff\x28\xac\x01\xc4\x35\xae\x00\x00") func confLocaleLocale_enUsIniBytes() ([]byte, error) { return bindataRead( @@ -4379,7 +4379,7 @@ func confLocaleLocale_enUsIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_en-US.ini", size: 44681, mode: os.FileMode(420), modTime: time.Unix(1447476053, 0)} + info := bindataFileInfo{name: "conf/locale/locale_en-US.ini", size: 44597, mode: os.FileMode(420), modTime: time.Unix(1447648800, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/public/css/gogs.css b/public/css/gogs.css index 5ade64d9..b4f4782d 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -2393,6 +2393,85 @@ footer .container .links > *:first-child { padding: 5px 10px; font-size: 1.2em; } +.repository.release #release-list { + border-top: 1px solid #DDD; + margin-top: 20px; + padding-top: 15px; +} +.repository.release #release-list > li { + list-style: none; +} +.repository.release #release-list > li .meta, +.repository.release #release-list > li .detail { + padding-top: 30px; + padding-bottom: 40px; +} +.repository.release #release-list > li .meta { + text-align: right; + position: relative; +} +.repository.release #release-list > li .meta .tag:not(.icon) { + display: block; + margin-top: 15px; +} +.repository.release #release-list > li .meta .commit { + display: block; + margin-top: 10px; +} +.repository.release #release-list > li .detail { + border-left: 1px solid #DDD; +} +.repository.release #release-list > li .detail .author img { + margin-bottom: -3px; +} +.repository.release #release-list > li .detail .download { + margin-top: 20px; +} +.repository.release #release-list > li .detail .download > a .octicon { + margin-left: 5px; + margin-right: 5px; +} +.repository.release #release-list > li .detail .download .list { + padding-left: 0; + border-top: 1px solid #eee; +} +.repository.release #release-list > li .detail .download .list li { + list-style: none; + display: block; + padding-top: 8px; + padding-bottom: 8px; + border-bottom: 1px solid #eee; +} +.repository.release #release-list > li .detail .dot { + width: 9px; + height: 9px; + background-color: #ccc; + z-index: 999; + position: absolute; + display: block; + left: -5px; + top: 40px; + border-radius: 6px; + border: 1px solid #FFF; +} +.repository.new.release .target { + min-width: 500px; +} +.repository.new.release .target .at { + margin-left: -5px; + margin-right: 5px; +} +.repository.new.release .target .dropdown.icon { + margin: 0; + padding-top: 3px; +} +.repository.new.release .target .selection.dropdown { + padding-top: 10px; + padding-bottom: 10px; +} +.repository.new.release .prerelease.field { + margin-bottom: 0; +} .issue.list { list-style: none; padding-top: 15px; diff --git a/public/less/_repository.less b/public/less/_repository.less index e73d78c4..004e7233 100644 --- a/public/less/_repository.less +++ b/public/less/_repository.less @@ -794,6 +794,102 @@ } } } + + &.release { + #release-list { + border-top: 1px solid #DDD; + margin-top: 20px; + padding-top: 15px; + + >li { + list-style: none; + + .meta, + .detail { + padding-top: 30px; + padding-bottom: 40px; + } + .meta { + text-align: right; + position: relative; + + .tag:not(.icon) { + display: block; + margin-top: 15px; + } + .commit { + display: block; + margin-top: 10px; + } + } + .detail { + border-left: 1px solid #DDD; + + .author { + img { + margin-bottom: -3px; + } + } + .download { + margin-top: 20px; + + >a { + .octicon { + margin-left: 5px; + margin-right: 5px; + } + } + + .list { + padding-left: 0; + border-top: 1px solid #eee; + + li { + list-style: none; + display: block; + padding-top: 8px; + padding-bottom: 8px; + border-bottom: 1px solid #eee; + } + } + } + .dot { + width: 9px; + height: 9px; + background-color: #ccc; + z-index: 999; + position: absolute; + display: block; + left: -5px; + top: 40px; + border-radius: 6px; + border: 1px solid #FFF; + } + } + } + } + } + &.new.release { + .target { + min-width: 500px; + + .at { + margin-left: -5px; + margin-right: 5px; + } + .dropdown.icon { + margin: 0; + padding-top: 3px; + } + .selection.dropdown { + padding-top: 10px; + padding-bottom: 10px; + } + } + .prerelease.field { + margin-bottom: 0; + } + } } // End of .repository diff --git a/routers/repo/release.go b/routers/repo/release.go index 680ecd1b..e2a8d6f6 100644 --- a/routers/repo/release.go +++ b/routers/repo/release.go @@ -13,15 +13,12 @@ import ( ) const ( - RELEASES base.TplName = "repo/release/list" - RELEASE_NEW base.TplName = "repo/release/new" - RELEASE_EDIT base.TplName = "repo/release/edit" + RELEASES base.TplName = "repo/release/list" + RELEASE_NEW base.TplName = "repo/release/new" ) func Releases(ctx *middleware.Context) { ctx.Data["Title"] = ctx.Tr("repo.release.releases") - ctx.Data["IsRepoToolbarReleases"] = true - ctx.Data["IsRepoReleaseNew"] = false rawTags, err := ctx.Repo.GitRepo.GetTags() if err != nil { @@ -45,7 +42,7 @@ func Releases(ctx *middleware.Context) { continue } if rel.TagName == rawTag { - rel.Publisher, err = models.GetUserByID(rel.PublisherId) + rel.Publisher, err = models.GetUserByID(rel.PublisherID) if err != nil { ctx.Handle(500, "GetUserById", err) return @@ -105,7 +102,7 @@ func Releases(ctx *middleware.Context) { continue } - rel.Publisher, err = models.GetUserByID(rel.PublisherId) + rel.Publisher, err = models.GetUserByID(rel.PublisherID) if err != nil { ctx.Handle(500, "GetUserById", err) return @@ -140,27 +137,13 @@ func Releases(ctx *middleware.Context) { } func NewRelease(ctx *middleware.Context) { - if !ctx.Repo.IsOwner() { - ctx.Handle(403, "release.ReleasesNew", nil) - return - } - ctx.Data["Title"] = ctx.Tr("repo.release.new_release") ctx.Data["tag_target"] = ctx.Repo.Repository.DefaultBranch - ctx.Data["IsRepoToolbarReleases"] = true - ctx.Data["IsRepoReleaseNew"] = true ctx.HTML(200, RELEASE_NEW) } func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) { - if !ctx.Repo.IsOwner() { - ctx.Handle(403, "release.ReleasesNew", nil) - return - } - ctx.Data["Title"] = ctx.Tr("repo.release.new_release") - ctx.Data["IsRepoToolbarReleases"] = true - ctx.Data["IsRepoReleaseNew"] = true if ctx.HasError() { ctx.HTML(200, RELEASE_NEW) @@ -185,8 +168,8 @@ func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) { } rel := &models.Release{ - RepoId: ctx.Repo.Repository.ID, - PublisherId: ctx.User.Id, + RepoID: ctx.Repo.Repository.ID, + PublisherID: ctx.User.Id, Title: form.Title, TagName: form.TagName, Target: form.Target, @@ -198,67 +181,67 @@ func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) { } if err = models.CreateRelease(ctx.Repo.GitRepo, rel); err != nil { - if err == models.ErrReleaseAlreadyExist { + if models.IsErrReleaseAlreadyExist(err) { + ctx.Data["Err_TagName"] = true ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), RELEASE_NEW, &form) } else { ctx.Handle(500, "CreateRelease", err) } return } - log.Trace("%s Release created: %s/%s:%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.Name, form.TagName) + log.Trace("Release created: %s/%s:%s", ctx.User.LowerName, ctx.Repo.Repository.Name, form.TagName) ctx.Redirect(ctx.Repo.RepoLink + "/releases") } func EditRelease(ctx *middleware.Context) { - if !ctx.Repo.IsOwner() { - ctx.Handle(403, "release.ReleasesEdit", nil) - return - } + ctx.Data["Title"] = ctx.Tr("repo.release.edit_release") + ctx.Data["PageIsEditRelease"] = true tagName := ctx.Params(":tagname") rel, err := models.GetRelease(ctx.Repo.Repository.ID, tagName) if err != nil { - if err == models.ErrReleaseNotExist { + if models.IsErrReleaseNotExist(err) { ctx.Handle(404, "GetRelease", err) } else { ctx.Handle(500, "GetRelease", err) } return } - ctx.Data["Release"] = rel + ctx.Data["tag_name"] = rel.TagName + ctx.Data["tag_target"] = rel.Target + ctx.Data["title"] = rel.Title + ctx.Data["content"] = rel.Note + ctx.Data["prerelease"] = rel.IsPrerelease - ctx.Data["Title"] = ctx.Tr("repo.release.edit_release") - ctx.Data["IsRepoToolbarReleases"] = true - ctx.HTML(200, RELEASE_EDIT) + ctx.HTML(200, RELEASE_NEW) } func EditReleasePost(ctx *middleware.Context, form auth.EditReleaseForm) { - if !ctx.Repo.IsOwner() { - ctx.Handle(403, "release.EditReleasePost", nil) - return - } + ctx.Data["Title"] = ctx.Tr("repo.release.edit_release") + ctx.Data["PageIsEditRelease"] = true tagName := ctx.Params(":tagname") rel, err := models.GetRelease(ctx.Repo.Repository.ID, tagName) if err != nil { - if err == models.ErrReleaseNotExist { + if models.IsErrReleaseNotExist(err) { ctx.Handle(404, "GetRelease", err) } else { ctx.Handle(500, "GetRelease", err) } return } - ctx.Data["Release"] = rel + ctx.Data["tag_name"] = rel.TagName + ctx.Data["tag_target"] = rel.Target + ctx.Data["title"] = rel.Title + ctx.Data["content"] = rel.Note + ctx.Data["prerelease"] = rel.IsPrerelease if ctx.HasError() { - ctx.HTML(200, RELEASE_EDIT) + ctx.HTML(200, RELEASE_NEW) return } - ctx.Data["Title"] = ctx.Tr("repo.release.edit_release") - ctx.Data["IsRepoToolbarReleases"] = true - rel.Title = form.Title rel.Note = form.Content rel.IsDraft = len(form.Draft) > 0 diff --git a/templates/.VERSION b/templates/.VERSION index 7b98f0fd..21364203 100644 --- a/templates/.VERSION +++ b/templates/.VERSION @@ -1 +1 @@ -0.7.11.1115 Beta \ No newline at end of file +0.7.12.1115 Beta \ No newline at end of file diff --git a/templates/repo/release/edit.tmpl b/templates/repo/release/edit.tmpl deleted file mode 100644 index 062884e2..00000000 --- a/templates/repo/release/edit.tmpl +++ /dev/null @@ -1,59 +0,0 @@ -{{template "ng/base/head" .}} -{{template "ng/base/header" .}} -
- {{template "repo/header_old" .}} -
-
- {{template "ng/base/alert" .}} -
-

{{.i18n.Tr "repo.release.edit_release"}}

-
- {{.CsrfTokenHtml}} -
- {{.Release.TagName}} - @ - {{.Repository.DefaultBranch}} -
-
- -
-
-
-
- {{.i18n.Tr "repo.release.content_with_md" "https://help.github.com/articles/markdown-basics" | Str2html}} -
-
-
    -
  • - -
  • -
  • - -
  • -
-
-
-
- -
-
-
{{.i18n.Tr "repo.release.loading"}}
-
-
-
- - {{.i18n.Tr "repo.release.prerelease_desc"}} -

{{.i18n.Tr "repo.release.prerelease_helper"}}

- - {{if .Release.IsDraft}} - - {{end}} -
-
- -
-
- {{template "repo/sidebar" .}} -
-
-{{template "ng/base/footer" .}} \ No newline at end of file diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl index eb3ba91d..5fb23bda 100644 --- a/templates/repo/release/list.tmpl +++ b/templates/repo/release/list.tmpl @@ -1,73 +1,80 @@ -{{template "ng/base/head" .}} -{{template "ng/base/header" .}} -
- {{template "repo/header_old" .}} -
-
-
-

- {{.i18n.Tr "repo.release.releases"}} - - {{if .IsRepositoryAdmin}} - - - - {{end}} -

- +{{template "base/head" .}} +
+ {{template "repo/header" .}} +
+ {{template "repo/sidebar" .}} +

+ {{.i18n.Tr "repo.release.releases"}} + {{if .IsRepositoryAdmin}} + + {{end}} +

+
+ + {{end}} + +
-{{template "ng/base/footer" .}} \ No newline at end of file +{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/repo/release/new.tmpl b/templates/repo/release/new.tmpl index d8909142..8514b2c8 100644 --- a/templates/repo/release/new.tmpl +++ b/templates/repo/release/new.tmpl @@ -1,68 +1,83 @@ -{{template "ng/base/head" .}} -{{template "ng/base/header" .}} -
- {{template "repo/header_old" .}} -
-
- {{template "ng/base/alert" .}} -
-

{{.i18n.Tr "repo.release.new_release"}}

-
- {{.CsrfTokenHtml}} -
- - @ -
- - -
- -
-

{{.i18n.Tr "repo.release.tag_helper"}}

-
- -
-
-
-
- {{.i18n.Tr "repo.release.content_with_md" "https://help.github.com/articles/markdown-basics" | Str2html}} -
-
-
    -
  • - -
  • -
  • - -
  • -
-
-
-
- -
-
-
{{.i18n.Tr "repo.release.loading"}}
-
-
- - {{.i18n.Tr "repo.release.prerelease_desc"}} -

{{.i18n.Tr "repo.release.prerelease_helper"}}

- - -
-
- +{{template "base/head" .}} +
+ {{template "repo/header" .}} +
+ {{template "repo/sidebar" .}} +

+ {{if .PageIsEditRelease}} + {{.i18n.Tr "repo.release.edit_release"}} +
{{.i18n.Tr "repo.release.edit_subheader"}}
+ {{else}} + {{.i18n.Tr "repo.release.new_release"}} +
{{.i18n.Tr "repo.release.new_subheader"}}
+ {{end}} +

+ {{template "base/alert" .}} +
+ {{.CsrfTokenHtml}} +
+
+ {{if .PageIsEditRelease}} + {{.tag_name}}@{{.tag_target}} + {{else}} + + @ + + {{.i18n.Tr "repo.release.tag_helper"}} + {{end}} +
+
+
+
+ + +
+
+ + +
+
+
+
+
+
+
+ + +
+
+ {{.i18n.Tr "repo.release.prerelease_helper"}} +
+ {{if .PageIsEditRelease}} + + {{.i18n.Tr "repo.release.cancel"}} + + + {{else}} + + + {{end}} +
- {{template "repo/sidebar" .}} -
+
+ +
-{{template "ng/base/footer" .}} \ No newline at end of file +{{template "base/footer" .}} \ No newline at end of file -- cgit v1.2.3 From 9ab96172fc41c5a10f88045d6a19e83641e3b859 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Mon, 16 Nov 2015 23:28:46 -0500 Subject: new watchers, stars and forks UI --- conf/locale/locale_en-US.ini | 4 +- models/repo.go | 35 ++++++-------- modules/bindata/bindata.go | 4 +- public/css/gogs.css | 40 ++++++++++++++++ public/less/_repository.less | 49 +++++++++++++++++++ routers/repo/stars.go | 44 ----------------- routers/repo/view.go | 35 +++++++++++++- routers/repo/watchers.go | 44 ----------------- templates/repo/forks.tmpl | 44 ++++++++--------- templates/repo/home.tmpl | 2 +- templates/repo/stars.tmpl | 61 ------------------------ templates/repo/watchers.tmpl | 110 +++++++++++++++++++++---------------------- 12 files changed, 216 insertions(+), 256 deletions(-) delete mode 100644 routers/repo/stars.go delete mode 100644 routers/repo/watchers.go delete mode 100644 templates/repo/stars.tmpl (limited to 'models/repo.go') diff --git a/conf/locale/locale_en-US.ini b/conf/locale/locale_en-US.ini index 341f1add..1fb3f2fa 100644 --- a/conf/locale/locale_en-US.ini +++ b/conf/locale/locale_en-US.ini @@ -350,6 +350,9 @@ auto_init = Initialize this repository with selected files and template create_repo = Create Repository default_branch = Default Branch mirror_interval = Mirror Interval (hour) +watchers = Watchers +stargazers = Stargazers +forks = Forks form.name_reserved = Repository name '%s' is reserved. form.name_pattern_not_allowed = Repository name pattern '%s' is not allowed. @@ -382,7 +385,6 @@ create_new_repo_command = Create a new repository on the command line push_exist_repo = Push an existing repository from the command line repo_is_empty = This repository is empty, please come back later! - branch = Branch tree = Tree filter_branch_and_tag = Filter branch or tag diff --git a/models/repo.go b/models/repo.go index 9b2c7bc6..0e10e171 100644 --- a/models/repo.go +++ b/models/repo.go @@ -49,7 +49,7 @@ var ( Gitignores, Licenses, Readmes []string // Maximum items per page in forks, watchers and stars of a repo - ItemsPerPage = 54 + ItemsPerPage = 40 ) func LoadRepoConfig() { @@ -1696,25 +1696,21 @@ func WatchRepo(uid, repoId int64, watch bool) (err error) { return watchRepo(x, uid, repoId, watch) } -func getWatchers(e Engine, rid int64) ([]*Watch, error) { +func getWatchers(e Engine, repoID int64) ([]*Watch, error) { watches := make([]*Watch, 0, 10) - err := e.Find(&watches, &Watch{RepoID: rid}) - return watches, err + return watches, e.Find(&watches, &Watch{RepoID: repoID}) } // GetWatchers returns all watchers of given repository. -func GetWatchers(rid int64) ([]*Watch, error) { - return getWatchers(x, rid) +func GetWatchers(repoID int64) ([]*Watch, error) { + return getWatchers(x, repoID) } -// Repository.GetWatchers returns all users watching given repository. -func (repo *Repository) GetWatchers(offset int) ([]*User, error) { - users := make([]*User, 0, 10) - offset = (offset - 1) * ItemsPerPage - - err := x.Limit(ItemsPerPage, offset).Where("repo_id=?", repo.ID).Join("LEFT", "watch", "user.id=watch.user_id").Find(&users) - - return users, err +// Repository.GetWatchers returns range of users watching given repository. +func (repo *Repository) GetWatchers(page int) ([]*User, error) { + users := make([]*User, 0, ItemsPerPage) + return users, x.Limit(ItemsPerPage, (page-1)*ItemsPerPage). + Where("repo_id=?", repo.ID).Join("LEFT", "watch", "user.id=watch.user_id").Find(&users) } func notifyWatchers(e Engine, act *Action) error { @@ -1794,13 +1790,10 @@ func IsStaring(uid, repoId int64) bool { return has } -func (repo *Repository) GetStars(offset int) ([]*User, error) { - users := make([]*User, 0, 10) - offset = (offset - 1) * ItemsPerPage - - err := x.Limit(ItemsPerPage, offset).Where("repo_id=?", repo.ID).Join("LEFT", "star", "user.id=star.uid").Find(&users) - - return users, err +func (repo *Repository) GetStargazers(page int) ([]*User, error) { + users := make([]*User, 0, ItemsPerPage) + return users, x.Limit(ItemsPerPage, (page-1)*ItemsPerPage). + Where("repo_id=?", repo.ID).Join("LEFT", "star", "user.id=star.uid").Find(&users) } // ___________ __ diff --git a/modules/bindata/bindata.go b/modules/bindata/bindata.go index 24e50531..33e44645 100644 --- a/modules/bindata/bindata.go +++ b/modules/bindata/bindata.go @@ -4364,7 +4364,7 @@ func confLocaleLocale_deDeIni() (*asset, error) { return a, nil } -var _confLocaleLocale_enUsIni = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x7d\xeb\x72\xdc\x48\xae\xe6\x7f\x3e\x05\xdb\x27\x1c\xb6\x23\xe4\x72\x74\xf7\xde\xa2\xa3\xed\x5e\x59\x6a\x5f\xe6\x58\x96\xc6\x52\xcf\x6c\xaf\xc3\xc1\x66\x15\x59\x55\x1c\x57\x91\xd5\x24\xcb\xb2\xe6\xc4\x89\xd8\x07\xd8\x07\xd8\x7d\xbd\xf3\x24\x0b\x7c\x00\xf2\x42\xb2\x24\xbb\x67\x62\xfd\xc3\x62\x65\x22\x6f\x48\x24\x12\x89\x04\x90\xf9\x6e\x97\x15\x65\xb7\x48\x9f\xa6\xc7\xe9\x2e\xaf\xea\x4d\xd9\x75\x69\x57\x6e\x96\x8f\xd7\x4d\xd7\x97\x45\xfa\xb2\xea\xe9\x77\xfb\xa9\x5a\x94\x49\xb2\x6e\xb6\x25\x81\xbe\xa2\x3f\x49\x91\x77\xeb\x79\x93\xb7\x05\x25\x9c\xda\x77\x52\x7e\xde\x6d\x9a\x96\x81\x7e\x96\xaf\x64\x5d\x6e\x76\x5c\x86\xfe\x24\x5d\xb5\xaa\xb3\xaa\xa6\x9f\x97\xf4\x95\xbe\xae\x25\xa5\xd9\xf7\x96\x74\xbe\xef\x25\x6d\xbf\xb3\xa4\x5f\x76\x49\x5b\xae\x2a\xea\x4d\x4b\x49\xef\xf4\x33\xb9\x2e\xe7\x5d\xd5\x73\x4b\x7f\x95\xaf\xe4\x53\xd9\x76\x55\xc3\xb5\xff\x45\xbe\x92\x5d\xbe\x62\x80\x0b\xfa\x93\xf4\xe5\x76\xb7\xc9\x51\xe0\x4a\x3f\x93\x4d\x5e\xaf\xf6\x02\xf3\x46\x3f\x93\x45\x5b\x52\x56\x56\x97\xd7\x94\x7a\x82\x1f\xb3\xd9\x2c\xd9\x13\x12\xb2\x5d\xdb\x2c\xab\x4d\x99\xe5\x75\x91\x6d\x65\x98\xbf\x50\x7a\xaa\xe9\x29\xa5\xa7\x9c\x8e\x21\x94\x05\x0d\x35\xcb\x3b\x1d\x07\xe1\x92\x46\x9e\x77\x09\xaa\xaa\xf3\xad\x95\xe6\xcf\xa4\xdc\xe6\xd5\x86\xb1\xf6\x98\x3f\xa8\xe3\x5d\x77\xdd\x00\xb7\x17\xfa\x49\x48\xc8\xfa\x9b\x5d\x09\x1c\x3c\xbe\xa2\xaf\x64\x91\xef\xfa\xc5\x3a\xe7\x7e\xca\x57\x42\x40\xbb\x86\x90\xd1\xb4\x37\x80\xb3\x1f\x49\xd3\xae\xf2\xba\xfa\x7b\xde\x0b\x82\xce\x83\x9f\xc9\xb6\x6a\xdb\x86\x71\x7b\x86\x8f\x84\x86\x9e\x71\x3d\x94\xf2\x96\xb0\x10\xd4\xc2\x39\xdb\x6a\xd5\x0a\x1a\x39\xf3\x0c\xbf\xb8\x16\xce\x5b\x36\xed\x47\xcd\x78\xc1\x9f\x83\xa2\xd4\x09\xcd\x8d\xdb\xcf\x6b\x42\xbc\xe6\x9e\xe1\x47\x04\xd0\x25\x79\xb1\x25\x54\xee\xf2\xba\x64\x1c\x1d\xf3\x2f\xc2\x0b\xfd\x4a\xf2\xc5\xa2\xd9\xd7\x7d\xd6\x95\x7d\x5f\xd5\x2b\x46\xf6\xb1\x24\xa5\x97\x9a\x94\x04\x79\x2e\xed\xa6\xd9\xbb\xe9\xa4\xf4\x5f\xe9\x67\x7a\x21\x3f\x25\x2f\x28\x84\x4c\x57\x92\x47\xd2\x65\xcb\xb2\x2c\x64\x2c\x5d\xfa\x82\xbe\x93\xdd\x7e\xb3\x21\xac\xfd\xbe\x2f\xbb\x9e\x0b\x5d\xd0\x6f\x1a\xbf\xfc\x4e\xaa\xae\xa3\x0f\x4a\x7e\x8d\x8f\x84\xa6\xae\x5e\x60\x30\x27\xf8\x48\x92\xf7\x5d\x99\xb7\x8b\xf5\x87\x44\xfe\xa2\xaf\xfc\xc1\xb4\x77\x68\x52\x99\x90\x94\x88\xa4\x05\x6b\x20\x59\x34\x05\xff\x38\xa1\x3f\x54\x75\x55\x77\x7d\xbe\xd9\x7c\x48\xf4\x83\xc1\xe4\x4b\x26\xa0\xaf\x7a\x60\x41\x13\xd3\xcb\xbe\xdc\x75\x3c\x83\xe9\x8b\xaa\xed\xfa\xc7\x7d\x45\xc4\xfa\x6e\x5f\x27\x45\xb3\xf8\x48\xcb\x80\x97\x34\x5a\x7e\xbd\x4c\x09\x59\x0f\x68\x21\xb4\xfb\xba\x26\xf4\xa4\x2f\x1b\x42\x19\x35\x53\x51\xfb\xa7\x80\x3e\x4a\x77\x9b\x32\xef\x08\xa4\xcc\x8b\xf4\xc7\x3c\xed\xf3\x76\x55\xf6\x4f\xef\x65\x73\x5a\x7e\x1f\xef\xa5\xeb\xb6\x5c\x3e\xbd\x77\xbf\xbb\xf7\xec\xe5\x9e\x8a\x6d\xaa\xba\xec\x7e\x7c\x92\x3f\x4b\x17\x39\xe5\x10\x1a\x6f\xd2\x79\xb9\xe4\xd5\x46\x6d\xa5\x44\xe5\xf5\x8a\x57\xda\x4d\xbf\xe6\x06\x89\x12\xe8\xa3\x4b\x79\xa9\x7f\x93\xf0\x04\x10\x2b\xc8\x8a\xb9\xb1\x35\x74\x08\xc9\x2d\x4d\xc0\xd9\xcd\xe5\x9f\xdf\x1c\xa5\x17\xc4\xdb\x56\x6d\x89\x6f\xfa\x8f\x4a\x7c\x9f\xd2\x68\xaf\xaa\xd3\xe7\xb3\x84\xca\x1a\x42\x4e\xf3\x3e\x9f\x73\xdf\xdd\xec\x73\xa6\x2c\x42\x97\x87\xa5\xc8\xdc\x12\x9c\xb1\xeb\xa3\x69\x99\x5a\xc8\x54\x87\x2e\x7f\x57\xc7\x5b\xe6\x01\x94\xee\x30\x7b\x21\x38\xa3\xaa\xd2\xd7\x6f\xdf\x9e\x9f\x3e\x4f\xcb\x7a\x45\x98\x49\xaf\xab\x7e\x9d\xee\xfb\xe5\x7f\xcb\x56\x65\x5d\xb6\xf9\x26\x5b\x54\x8c\x94\x96\x08\x36\x25\x2c\xc9\x10\x67\x49\xd7\x6d\x88\x45\x81\x0a\x2e\x2f\xdf\xa4\x67\x4c\x09\xbb\xbc\x5f\xa3\x23\xfd\x3a\xe9\x7e\xdf\x30\xa2\x5c\x83\x57\xeb\x32\xc5\x62\x00\x50\xb3\x1c\xe2\x25\x2d\xb4\xaf\xb3\xa4\x6c\xdb\x8c\x38\x68\x7f\xc3\x68\xd6\x3a\x0f\x41\x4b\x75\x44\xed\x75\xd3\xd3\x34\xa6\x28\x27\x55\x54\xf5\xa7\x7c\x53\x15\x84\x6c\x8f\x90\xb8\x2c\x12\x8b\x86\xe6\x8d\x4b\x13\x65\x36\xd7\x18\x6a\xbe\xa0\x0d\xa0\x4b\xef\xcd\xee\x81\xe3\xde\x7b\x7c\x6f\x96\xd4\x4d\x26\x5c\x82\x79\x73\x51\x75\xf9\x9c\xf8\xb4\xec\x1b\xad\x71\xbd\x5f\x99\x7e\xa4\x2b\x0a\x91\x46\x10\x8c\x5b\xde\x8b\xb0\x05\x30\x71\xe5\xc4\xb0\xc1\x6c\x94\xcd\x84\x63\x37\x9e\xe4\xe6\x57\xd8\x92\x4b\x18\x8d\x39\xb1\x09\x33\xea\x3a\xde\xed\x36\xd5\x42\x9a\x7e\x29\x79\x9e\xd0\x78\x67\x56\xa4\x84\x70\x20\x14\xcb\x0b\xc8\x85\x7a\xcd\x6c\x2b\x8d\xf8\x3c\xca\xaf\x4b\x5a\x39\xeb\xfd\x4a\x76\xa7\x4d\xb3\x2f\xbe\x01\x43\xb1\x99\xf3\xfc\x24\x7d\xd7\x50\x87\x41\x1d\x0e\xc0\x37\x71\x4c\x8c\x81\x85\x81\xb6\xdc\x36\x3d\x23\x4e\x8b\x55\x34\x3d\xd7\x15\x65\xd2\x48\xbb\xfc\x13\xb1\xc5\xbe\x91\x25\x59\xd0\x92\x5b\x70\xc5\xc4\xc1\xf6\xb4\xa3\xcb\xb2\x20\x3e\x22\x4b\xc3\xd2\x62\x1a\x04\xd4\x76\x4f\xab\x69\x4d\x95\x31\xe2\x59\x22\xa1\x2a\xa7\xfa\x89\x21\x51\x3d\x58\xe5\xb4\x72\x1b\xda\x3c\x79\xa2\x4f\xf1\xa1\xbf\xc3\xfa\xa9\x57\xf9\x72\x49\xbd\xea\x68\x55\xbc\x4a\x17\x9b\x86\x96\xd4\x2f\xef\xde\x74\xbc\x60\xd6\xd9\xae\x69\x21\x89\x50\xd6\x05\x7d\xba\xb4\x00\xd1\x0c\x51\xef\xb7\x73\xfa\x75\xbd\xae\x88\x51\x03\xed\x5c\x82\xa5\x24\x4a\xa5\x26\xf6\x1d\x4d\xe1\x51\x4a\x4b\x98\x46\x40\x28\x03\x01\xf0\x18\x8c\xea\x18\x7c\x49\x34\xb6\x6f\x69\x39\xad\xfb\x7e\x67\x2d\xbf\xba\xba\xba\x90\xa6\x5d\xea\x6d\x6d\xe7\x01\x65\x60\x0e\x36\x2c\x1b\xd5\x69\x53\xcf\x40\x24\xfb\x76\x33\xa0\x1f\x1a\xab\xe5\x1c\xc0\x0b\x77\xe1\x09\xff\x77\xe9\xd1\x03\x3c\x77\x24\xf5\x5d\x83\x9a\x08\xc7\x25\xe4\x14\x22\xea\x66\xc7\xf5\x06\x54\x7d\xae\x09\x9e\x94\x21\xdb\xb8\x7c\x91\x70\x28\x17\x32\x65\xb0\x4b\x6f\x69\xc0\xca\x46\x2f\xcf\x08\x0d\xe0\xa5\x48\x5d\xb6\xcd\x96\x52\x5f\xd0\x1f\x9f\xe0\xbb\x7f\xc6\xf5\x01\x26\x2f\x0a\xe2\xf2\xdd\x51\xfa\xee\xc5\x49\xfa\x9f\xbf\xff\xee\xbb\x59\xfa\xba\xe7\x95\xc8\xc4\xf9\x37\x26\x2a\xfa\x14\x51\xcb\x81\x12\xc7\xea\x89\xee\xee\xf1\xca\xba\x97\xfe\x88\xdc\xff\x5e\x7e\xce\x49\x44\x2c\x67\x8b\x66\xfb\x8c\xb9\xea\x36\xa7\xb5\xcf\x39\x44\xae\x4a\xc7\x97\x65\x5d\xd0\x87\x0a\x6c\x9a\x17\xb0\x03\xcd\x0f\xc4\x37\x11\x5c\xb3\x45\x53\x2f\xab\x96\x07\xf4\x73\x0d\x6a\x30\x91\x96\xb6\x6b\xe4\x98\x54\x44\x48\x23\x0e\x52\x2d\x6f\x3c\x28\x86\xfa\x96\x13\x75\x42\x13\xa1\xba\x4c\x45\x74\x87\xe5\x4b\x21\x46\x9e\xb7\x73\x1a\x5e\x6b\xf8\xee\x3c\xc2\x9b\xe5\x92\xf7\x5a\xdb\x25\xb4\x85\x73\x49\x95\x0d\x23\x04\x21\x62\xdc\x41\x28\x3f\x55\x22\x3e\x39\x7d\x9b\x96\x9f\x88\xda\x98\xeb\xb5\x4d\xb1\x5f\x80\xc2\x18\xf6\x88\x99\x35\xb1\x88\x8e\xd6\xc6\x42\xf6\x95\x80\x49\x70\xd7\x98\x13\x2d\x08\x88\x78\x83\x31\x6b\x12\x24\x3f\x11\xe7\x6f\x83\x26\x5e\x5a\x92\xf6\x7e\x04\x3b\xea\x94\x2b\xc1\x23\x5f\xd0\x8c\x13\x55\x48\x2f\x3a\xe9\x94\x64\x13\xb9\x13\x1d\xef\xe9\x84\x92\x17\xd4\x97\xf9\x0d\xf8\x4e\xc7\xc4\x50\x94\xcb\x7c\xbf\xe9\x7d\xbf\x06\x9b\x88\xb5\x74\xc9\x87\xa4\x30\x6f\xb2\xc0\xa8\x83\xa0\x9e\x6e\x58\x96\xc8\xb0\x26\x39\x47\x36\x1b\xa6\x57\x39\x85\xd8\xbe\x43\xec\xa9\xc4\xf4\x64\x5e\xe4\xd7\xf9\x32\xc9\x3f\xce\x77\xcd\xbe\x13\xc9\x27\xc5\x56\xcb\x35\x5a\x05\x2c\x2a\x4c\xf7\x65\x96\xa8\xb8\x94\xe9\x71\x2d\xfb\x54\xe1\x30\xe4\xc8\x55\xaa\xd4\x23\x1c\xf3\xb5\xbf\x30\x00\x9f\xb2\xba\xc9\xb2\xae\x37\xe7\x3c\xc8\xce\x1d\x86\x04\xe7\x3c\x5c\xb4\xc0\x22\x1c\xcd\xd2\xa7\x0a\x6c\x5e\x09\x06\x78\x21\xaa\x41\xd3\xd4\x54\x57\x96\xa8\x81\xca\x3f\xa1\x3a\x51\x66\xa6\x07\x04\x95\xd9\x4d\xf4\xe3\xed\xbe\x68\x20\x3b\x60\x2f\xa1\xd2\x86\xd6\xc1\xbe\x9e\xb6\xd5\x6a\x4d\xbc\xb5\xb9\x3e\x12\xa4\x5c\xaf\x9b\x92\xd7\xcf\xeb\xd3\xa7\xdf\x4a\x3f\x56\xbc\xb3\xb8\x42\xbc\x27\xe5\x7b\x22\x2e\xc2\x98\x92\xb1\x74\xc1\xed\xed\x80\x1c\x1d\x45\x04\x68\x78\xf8\x1b\x89\x12\x8e\x69\x28\xaf\x08\xf3\x94\x49\x78\x18\x29\x6d\x07\x48\x69\x58\xb9\x92\xca\xfb\xd9\xaa\xc1\x41\xc6\xe4\x7b\xde\x2d\xe9\x3c\xdc\xf5\xd9\xaa\xea\xb3\x25\xb3\x2e\xae\xf9\x05\xd7\xc0\x9b\x37\xe5\xa4\x0f\x28\xeb\x41\x4a\xfc\x8f\x4e\x67\xc5\x0f\xe9\xfd\x4f\x2a\x31\x7e\xcf\x3c\x29\xa3\x55\x54\x6d\x30\x25\x7a\x3c\x6a\x4b\x11\x58\xed\x0c\xee\xa4\xb6\x6e\xbf\xc3\xde\xa6\x02\xa2\x3b\x0d\x14\xcd\x75\xcd\xab\x0f\xcc\x97\xf8\x4c\xb5\xa8\x68\xcf\x98\x57\x75\x4e\x1b\xbc\xd5\x02\xa6\x7e\x9f\x68\xe2\xed\xf9\x15\x00\x57\xcd\x7c\x5f\x6d\x0a\x03\x98\x25\x26\x44\x92\x08\xa9\xb3\x1f\x8a\xd5\x96\x54\x49\x5f\x16\x4d\xcb\x12\x09\x46\x63\x05\x0f\x88\x42\x2d\x8b\x18\x48\xae\xf8\x3c\x03\x58\x94\x73\x52\x0b\xa3\x81\xa6\x1f\x47\x35\x96\x69\x40\x37\x55\x57\x3f\xe8\xd1\xd3\xc5\x9e\xda\xa2\xa9\xe7\x64\x2a\xd8\xa5\x8f\x9f\xd1\xff\x09\x4b\x48\xb2\x03\xac\xc6\x88\xe7\xcc\x54\x32\xf7\xb2\x16\xa3\xae\x46\x44\xee\xa6\xda\x48\x38\x18\x6b\xd8\x5f\x23\x81\x6e\x2f\x54\xcb\xea\x92\x0d\x4d\x6b\xf9\x0d\x7d\xf0\xc9\x6d\xb5\xc1\x24\xe4\xbd\x1e\xaf\x1a\xc2\x1b\x13\xc8\x91\x2c\x9a\x25\x0d\x8d\x79\x69\x9f\x7f\x2c\x71\x22\xa3\x3d\xff\x3d\xeb\x81\x3e\x24\x7b\x91\x41\x9b\x4d\xe1\xce\x3b\xa0\xec\xa6\x1d\xaa\x31\x3c\x90\xa3\xda\x8e\x84\xed\xc5\x3a\x73\x5a\x24\x46\x4a\x5f\x7e\xc6\xee\x8f\x2c\xaf\x54\x62\x92\xe7\xac\x64\x7b\x83\xe9\xe2\x41\x9c\xdd\xf8\xd9\x22\x09\x94\x16\x0a\x1d\x66\xe7\x0d\x63\xed\x53\xe9\xa0\x4e\xc2\xd4\xb8\x00\xd5\x45\xb2\xb2\x56\x15\x6b\x1b\x28\x4b\x54\x22\x9a\x2b\x6a\x91\x2e\x01\x2b\x53\x15\x18\x38\x1e\xcd\xa7\x9e\xec\x67\x34\x31\x50\x1b\x58\xcb\xc4\x17\x6f\x64\x5d\x04\x6d\x26\xef\x55\x3d\xf6\x21\x31\xb8\x77\x71\x3e\xf1\x94\xf5\x87\x40\x05\x95\xd9\xec\x9a\x2a\x0a\xda\x13\x65\x2b\x5e\xa4\x58\x97\x3b\x96\x3e\xb6\x1d\xc8\x62\xc3\x27\xed\x1b\x95\x9f\x1d\x81\xfc\x24\x0c\x9b\x28\x86\xd8\xdc\x37\x49\xd7\xf0\x82\xcb\xbe\xb2\x8a\xe7\x15\x91\x02\xca\xc7\x9b\x9d\xe8\xc6\x48\xcc\xe5\xe9\xa3\x55\x76\x73\x14\x9f\xac\xd6\x79\x47\x4c\x9c\x64\x05\x2d\x56\xcc\xec\x84\xcb\xd3\x4e\xe7\x39\xac\x19\xe8\xf3\x40\xe5\x52\xb2\x69\x87\xbb\x30\xf7\x50\xf8\x9c\xb6\xe2\x64\x27\x48\x46\xa1\x00\x35\xd1\x26\x21\x6c\x5b\xb2\xf8\x9c\x6d\x45\x8d\x26\xbf\xd2\xb3\x32\xa1\xed\x70\x45\x0b\xda\x08\xf6\x29\x6b\x3f\x56\x38\x65\x28\xbd\x32\x40\xd9\x87\x8c\x58\x21\x2c\xe5\x27\xd3\x5b\x12\x67\xb8\x86\x6a\x88\xd6\xf6\x08\xfd\xb4\x65\x51\xf6\xcc\x18\xbb\xc8\x08\x10\xf5\x3a\xe2\x16\x1e\x89\xc7\x29\x2b\x20\x43\x28\x15\xb9\xfd\xb0\xb8\x00\x73\x8d\x1f\xe7\xcf\xee\x77\x3f\x3e\x99\x3f\x73\xbc\x75\xb1\x2e\x17\x1f\x85\xfe\xaa\x7a\xde\x7c\xc6\xc1\x16\x8a\x12\x3a\x53\xf3\x1a\xbb\x5f\xa4\x74\xd0\x6d\x71\xae\x22\x5e\x40\xc5\x08\xf1\x9c\x1b\x4d\x1a\x75\x86\x59\x06\x6d\x6d\x0b\xac\x2a\x10\xb8\x27\xc8\x63\x4e\x65\x92\xc4\x06\xe0\x69\x12\x03\xd9\x54\xdb\xaa\x1f\xd1\x04\x73\x98\x5c\x69\x4b\x75\x65\x86\x24\xd4\x85\x61\x62\x94\xc4\xa7\xa9\x1a\xda\x57\x8d\x4e\xae\x73\x3a\x48\x7d\x9f\x12\x6d\xec\x69\x7f\xe2\xce\xd2\x78\x88\x51\xe7\xbc\x31\xd3\x21\x2a\xef\xb2\x7d\xad\xf8\x2a\x0b\xa3\x92\x57\x15\xb6\x0f\x6e\xd7\x68\x39\x80\x32\x94\xea\x59\x20\x7d\xe8\x50\xf9\x68\xa6\xba\x2d\x14\x63\x9e\xce\x1d\xaa\x58\x6e\xcd\x27\x67\x85\x78\x5e\x5d\xca\xd9\x17\x18\x60\x38\x9e\x41\x3a\x40\xf9\x69\xa1\x53\xd8\x47\x4a\x01\xa6\xe7\xfb\xbe\x6f\xf8\x5c\xb2\x61\x72\x90\x32\xd6\xeb\x13\x00\xe2\xa8\xe5\xeb\xc3\x74\x86\x78\x12\x16\x5b\xda\x39\x21\x23\x3a\xe4\x35\x2d\x1a\x6e\x3e\xd1\x0d\x46\xa7\x9b\xa0\x03\x2b\x44\x97\x94\xd7\x37\x5e\xbd\x81\x5e\x70\x83\xfd\x74\x5f\x1e\xb6\xe5\x23\xdf\x1b\xb7\x18\x50\xc2\x7a\x24\xc5\x83\x85\xf2\x0e\xb9\xa2\x62\xb5\xe5\x64\x7b\x9a\x2a\x2a\x3d\x7d\xb4\x31\x7a\x91\xcf\x24\x4f\x9c\x93\xc4\xca\x02\x88\xa6\x51\xa0\xf4\x6c\xd0\x96\x3f\x12\x8e\x31\xd8\xc7\x5d\xf6\x5b\x53\xdf\x34\x59\xb7\x96\xe3\xb7\x75\x8f\x8e\xee\xf5\x2a\xd2\x5b\xe1\x5e\x04\x44\xf7\x5f\x78\x03\xe4\x81\x7e\x48\x74\x36\xca\x60\x51\x28\xb5\x5a\x8e\xcd\x9a\xac\x0d\x07\x6f\xc2\xda\x5f\xca\x96\x8f\x77\x00\x8a\x67\xeb\x10\x16\xe3\x41\x38\xa6\xe8\x77\x77\xc7\x10\x35\xe9\xc8\xf6\x7b\xee\x75\x53\xe4\xd4\xed\x1b\x68\xa2\x7f\xa5\x0d\xa7\x86\x8e\xbf\x49\x28\x43\x8e\x99\x67\xf8\x20\x50\x3e\xf3\x7e\x48\x78\x4b\x7f\x3b\x10\x56\x79\xc7\xd2\xb4\x40\x5e\x42\xd6\xcf\xd1\x25\x86\x1b\xca\xc5\x84\x60\xfb\xae\xf4\x77\x19\xf8\x72\x63\xba\xbc\x7c\x75\x65\x87\xd8\xcb\x57\xe9\xc7\x52\x2b\x7f\xd5\xf7\xbb\xee\x17\x28\x34\x44\x3b\xc1\xaa\x8c\x8b\xfc\x86\x85\x48\x49\xd6\x1f\xc8\xb8\x2a\xf3\xad\xf6\x92\x3f\xa5\x8a\x63\xda\x5d\x35\x91\x3f\x69\xd3\x0d\x14\x65\x09\xc4\xa9\x9f\xa7\xc4\x68\x77\xa8\x29\xf5\xa2\xe4\xb7\x91\x76\xef\xb7\x24\xdf\xec\xe8\xdc\xc5\xf2\x4c\x00\x06\x45\xd6\x5c\x8f\x5f\x29\x40\x40\xc1\xfb\x2d\xcd\xfc\x02\xc7\x4d\x2a\xf0\xf0\x71\xf6\x28\x50\x6c\xc6\x95\x15\xb4\xb4\xff\x50\x85\xfc\xcd\x42\x6f\x58\x6f\x57\xfd\xdd\x46\x11\x55\xc7\xe9\xc4\x29\x09\x02\x22\xa6\x87\x72\x40\xd8\xa7\x59\xdc\xec\x59\xaf\x45\x09\x24\xd2\x46\x55\x6f\xf3\xcf\x77\x15\xdc\x36\x13\xe5\x84\x81\xf9\x42\xc6\xa6\x74\x88\xf1\xb2\x20\x78\xd6\x5c\x1d\x84\xa6\xa9\x27\x90\xaa\x5e\x6c\xf6\xc5\xc1\x9e\x74\xfb\x39\xed\x71\x2c\x2b\x3f\xb8\xdf\x3d\xe0\x2a\xeb\x8f\xb4\x29\xd7\x0e\xfe\x17\xf9\x9d\xe2\xf7\x0f\x76\xcd\x46\xa7\x59\x3d\x40\xa4\xee\xc2\x8d\x64\x8b\x82\x77\x07\x1c\x04\x66\x9e\xa9\x84\x87\x03\x47\xfe\x50\x49\xe8\xe9\xcd\x2d\x6c\xd6\x43\xe0\x9c\x44\x24\x38\xf3\x77\x83\x19\x6f\xf1\x19\x0b\xdd\x75\x28\x5a\xbb\xcd\xdf\x76\x51\x40\xc8\x0d\x51\x36\x2e\x37\x58\xa0\x07\x8b\x93\x24\x33\x51\xfa\x7c\xac\x4b\x3e\x50\xbe\xa7\x25\x36\x51\x81\x5b\x79\x07\x0b\xca\xe4\xa3\x10\x8d\xbc\x18\xf1\x8e\x71\x41\x06\xa3\x63\xdf\x66\x53\xae\x58\xe9\x68\x0d\x47\xad\xe9\x44\xd3\x96\x27\x60\x21\xc1\x79\x0c\xbb\xc9\x0a\xe7\x35\x3c\xc4\xb8\x39\x8a\x8f\x8f\xac\x8b\xa1\xaa\x5a\xdc\xef\x06\x87\x48\xed\x86\xee\x00\x5b\x3e\x2f\x75\x7b\xde\x80\xf8\x6c\x25\xc2\x55\x3c\x1b\x2c\x5e\xa0\xaa\x12\x4d\x1c\xae\x9e\x68\x91\x0f\x9c\x77\xd5\x0f\xb0\xaf\xac\x3a\x54\x3a\x8c\x2b\xd6\xca\x1d\xd0\xa1\x6a\xdd\x81\xb8\xfc\x5c\x41\x81\xfb\xb2\x62\xc5\x20\x8e\xc4\x4e\x13\x80\xbc\x59\xb2\x21\xe6\xc1\x47\x2f\x19\x95\x88\xe1\xcd\x27\x5e\x8d\xdc\x1e\xe7\x4a\x39\x51\xe8\xea\xa0\x78\x9e\xf5\x70\x8d\x6b\xa0\xb2\x38\x22\x41\x86\x4b\x50\x3f\xb1\xb8\xf3\xcd\x75\x7e\xd3\x41\x53\x64\x1c\x8a\x95\xd7\x52\x9c\xd9\x0f\x89\x39\x2b\xf4\x2a\xbc\x22\xa1\x15\x67\x98\x60\x5d\x3f\x6f\x36\x4e\x18\xb9\xc6\xf1\x18\xdc\x45\x75\x4f\x9f\x82\xed\x5a\xf7\x26\x3e\xda\xf3\x41\x98\x8f\x28\x92\x1d\x54\x84\xbb\x47\xdd\x29\x26\xca\x1e\xb1\x10\x48\xcd\xb0\x48\x46\xfc\x5b\x70\x4d\x52\x2e\x61\x16\x5d\x0a\x74\x25\x7b\xaa\xff\xb1\x88\xf5\x15\xe1\x90\x8f\x89\x5e\x7d\xc0\x7b\x19\xcd\x8a\xa9\xf8\x25\x1d\x87\xff\xa4\xeb\x69\x09\x30\xa6\xed\x42\xff\xd7\x40\x1e\x49\x91\x8b\x25\x06\x34\x75\xeb\x6a\x97\x36\x50\x1b\x87\x28\xf4\x64\x1b\x08\xd2\x7c\x99\x51\xe2\xd8\xc0\xfa\xf3\x36\xaf\xbb\x65\x09\x45\xfa\x36\x5d\xf2\x9d\xf1\x4c\x9b\x66\xb9\x5c\x2e\xf6\x0f\xb4\x2c\x47\x30\x34\x1d\xee\x2e\x98\xbb\x60\xa2\xe2\xa6\xe5\x66\x05\xca\x5a\xf4\x01\x58\xf5\x35\x75\xd6\x07\x26\xb3\x11\x0a\x20\x1b\x47\xf7\x64\xd6\x9b\x4f\x65\x88\x88\xe5\x1f\x1d\x79\x80\x75\xbd\x2b\x90\x0b\x96\x78\x9a\xa4\x51\x68\x6b\x70\xcd\x3b\xbf\x89\x47\xcf\x45\x83\xbb\x73\x5a\x23\xa5\xb6\xc2\x0b\x83\xd7\xca\xa0\x42\x68\x69\xfc\x89\x28\x91\x7b\xf6\x6c\x4e\x5d\x5c\xac\xa3\xd5\x79\x85\x9c\x54\x72\x46\x0b\x34\x79\xcf\x4d\x7f\x48\xe4\xa6\x3d\x73\x4a\xf9\x13\xb9\x79\x17\x89\x56\x95\xec\x7d\x6a\x9a\x78\xbe\x2a\xb1\x22\xa2\x77\xbf\xb5\x24\x6f\xc3\xa6\x14\xfd\x5b\x43\x32\x07\x74\xeb\x7f\xa2\x2f\x96\xf1\xeb\x24\xba\x5e\x1c\xa8\x49\x20\x46\x57\x3d\xaf\xb0\x0b\x5a\x18\x24\xf6\x1c\x6b\x0a\x9d\xd2\xc1\x1d\xa0\xb9\x79\x61\xdf\x34\x1f\x39\x33\x3d\x5e\xda\xf2\xa5\x70\xa2\x46\x7b\x61\xdf\x09\x1f\xf2\xb7\x33\x6c\x0e\x2c\x7e\xe3\x9a\x22\xd8\x12\x58\x5a\xe0\x09\xb3\xbc\x59\x00\xbf\xcb\x7b\x62\x8b\xb5\x1c\xc4\x84\x43\x85\x45\x35\xdb\x55\xe1\xee\xb3\xb9\x16\xb6\xfd\x10\x54\x7c\x48\xbc\x49\x8a\x59\xa3\x4c\xa9\x85\x95\xc5\x74\x2a\x23\xff\x2b\x7d\xaa\x42\x07\xec\x0b\x1f\x7a\x20\xc7\x4d\xb2\x5d\xff\xc1\x3c\x26\xf8\x99\xa8\x0a\x2c\xd6\x7f\x29\x79\x3f\x4d\x4f\xe5\xc3\x8e\xf6\xfb\x0a\x63\xaa\x8a\x24\xd9\x01\xef\x81\x01\x8d\x4e\x84\xeb\xb4\x1a\x4a\x79\x4d\x7c\x3b\xdc\xd9\xd9\x66\x43\x0a\x31\xe1\xda\xe5\x10\xa4\x00\xbe\x9b\x08\x8e\xa5\xac\x5c\xc6\x79\xb5\x0e\x2e\xbe\xf8\x3a\x87\x4f\xd9\x04\x76\x5d\xce\x53\x56\xf7\x12\xe1\xd0\xe9\x4f\x07\xba\xcd\xe9\xe0\xf8\xa9\xca\x9d\x62\x89\x66\x8b\x4d\x74\x74\x17\x7d\xc1\xe6\x39\xb8\x4c\x1f\xdb\x91\xf1\xcd\x94\x5e\xf6\xbc\xd1\xcf\x64\xbf\xe3\xdb\x93\x60\xc0\xbf\x20\xc1\x0d\x38\xce\x0f\xce\x63\x18\xba\x15\x73\xd2\x8c\x80\x17\x76\x48\x83\x95\xcb\xcc\x96\xcf\x84\x7d\x98\x2e\xa1\x62\x08\xe2\x35\x2c\x60\x31\x6a\x1c\x03\x64\xca\x7d\x2e\x86\x4f\x3b\x63\xba\x6e\xae\xd3\x4d\x55\x7f\xec\x14\x9b\xcc\xc7\xc2\xc3\x29\x54\x52\x44\x84\x7b\xb1\x1b\x92\xcf\xb1\x99\x92\x5d\x33\x0d\x56\xb8\x5d\x46\xc9\x85\xdb\x31\x92\x27\x61\xfd\x11\xdd\x2e\xc4\x96\x25\x8b\xc9\x60\x6a\x76\x79\x47\xa3\x6c\x9a\x4e\x15\xa0\x9e\x89\x70\x1a\x94\x2a\x0a\xa5\x38\x77\x10\x3a\x25\xc7\x76\x65\x88\x35\x95\xd8\x25\x9f\x75\x00\x2b\x34\xab\xb6\x62\xf6\xf7\x8b\x5d\x01\x62\x7e\xdc\x69\x02\xd9\x30\x2a\x89\x7b\x1f\xde\x7b\xbc\x6d\xec\x82\xd1\xb8\xa1\x65\x1e\xd9\xa6\x2f\x18\xc0\x96\x1d\x75\x76\x48\x1f\x5a\x81\xa9\xf0\xef\x20\x13\x23\x82\xf0\x52\x48\x26\xde\x31\x88\x66\x13\x89\x76\x27\x7a\x19\xe1\xf2\x19\xb3\x41\xfe\x5b\x5c\xdf\x39\xa5\x02\x1f\xc8\xb3\x01\x88\x1e\xd8\x23\xc8\x49\x09\xda\xda\x3a\x28\x3d\x0f\x7a\x3f\x5a\x2b\x56\xee\x9a\xb0\x10\x0e\x5c\xa9\xbb\x98\x99\x1d\x0f\x6b\x52\xe5\x2e\x10\x06\x17\x62\x74\x52\xe3\x22\x51\xaa\x20\x54\xe1\x80\xd1\xf9\x73\xc5\xb1\x70\x1f\xbe\x00\x10\xab\x43\x07\xa0\x86\x87\xf1\x79\xb3\x34\xeb\x85\x90\x91\xed\x5a\x22\x0f\xda\x68\x07\xfa\xb5\x11\x0b\x8b\xd8\x15\xb8\x55\x83\xab\x78\xcf\xa5\xe8\xc4\xa8\x75\x31\xbf\xc7\x97\xa5\x38\x1d\x11\xd1\x31\x8b\xba\x9a\xac\xcc\xd9\xe5\x0a\x8b\x76\x9d\xa4\x1f\xc2\xb8\x74\xb8\xa7\x9a\x32\x00\xb0\xe1\x28\x83\xef\x27\xb4\x85\x18\x8d\x8a\x1d\xc6\x7f\xab\x5a\x4c\x21\xdc\xd5\x5c\xc4\x40\xd2\x53\x70\x14\x9a\x37\xd1\x4b\x1b\x3f\xf9\x69\xd8\xb8\x9f\xf0\x9f\x07\x2a\x6d\x19\x5c\x4c\xef\xdf\x24\xd4\x25\x50\xa3\xbf\xe2\x2c\x30\xcd\x03\x8d\x19\x83\x85\x20\xaa\x8e\x74\xc9\x59\xa4\x73\x87\xf6\xfc\xab\xf4\xec\xbc\x77\xff\x13\x54\xec\x51\x5b\x5e\xc5\xee\x7a\x39\x58\x0e\xdc\xbd\xc1\xce\x39\x5a\x18\x94\x01\x39\x42\x49\x3a\x90\x0e\x94\xa8\x9d\x90\xc0\xcd\xc8\xd9\x84\x31\x44\x49\x10\x25\x94\x1a\xb0\x85\xb0\xa0\x0a\x33\x22\xd8\x00\xca\x41\xa5\x1b\x29\x8d\xe3\x89\x3f\xc6\x49\x8c\xb0\x22\xb0\xb0\xd3\xa3\x8d\x59\xa4\x58\x3d\xd9\x6d\x19\x11\x72\x89\xee\x4c\xba\x46\x37\x64\x47\x7a\xfc\x59\x57\xab\x35\x8d\xab\xda\xf2\xd5\x31\xc8\xc9\xee\x27\xfd\xe9\x94\x7f\x11\x43\x69\x56\x35\xeb\xae\xb8\x05\xb1\xe1\x72\x1b\xcc\x8f\x5d\xdf\x36\xf5\xea\xd9\x69\xc3\xc7\x46\xd6\xe8\xf0\x26\xf8\xd3\x8f\x4f\x34\x9d\x98\x26\xcf\x21\x1b\xfc\xbd\xac\xfa\x57\xfb\xf9\x83\x2e\x5d\xb1\x05\x2a\x2e\x55\xf2\xc0\x2e\x55\xad\x06\xc4\xc0\xee\xba\x76\x68\x81\x95\x2a\x2d\xf4\xae\xd9\xd0\x2a\x89\x8b\x34\xdb\xad\xcc\x2f\x6d\x00\x5b\x81\x44\xff\x61\x68\x50\xd6\xc0\x5c\xd9\x2a\x7e\xa8\xc2\x99\x23\x73\x3f\x3f\x3a\x6d\x26\xee\x45\x7a\x12\x15\xb8\x18\x18\x37\xa7\x75\x1f\x6c\x1b\xac\x23\x49\x5d\x31\x08\x0a\xe3\x62\x98\x48\x56\x3b\x79\x15\x8d\x29\x59\x70\x12\x40\x1d\x56\x9e\x8a\x52\x4f\x44\x62\xe2\x34\x6b\x53\x64\x85\x92\x95\xdb\x42\x5a\x01\xfd\xf2\x5e\x61\x2a\x5c\x08\xbe\x5e\x99\xc3\x04\x3b\x58\xe5\xca\xd8\x64\xf4\xca\xd6\x6c\x04\x01\x63\x53\x9c\x78\xce\x36\x84\x99\xe2\x6d\xd6\x8b\x90\xa9\x89\x85\x92\x30\x36\x21\x49\x3a\x69\x30\xdb\xfe\x42\xa6\x36\x6a\xd7\x0f\xdc\x9a\xfb\x02\xbe\x86\x31\x1d\x03\x1d\x34\x16\xe8\x46\x74\xa6\xde\xa8\x26\x04\x19\x6c\xdd\xea\x4f\x3d\x6f\x1b\xbd\x1f\x4b\x2d\x11\x73\x42\xc7\x9c\xbe\x8c\x16\x33\x77\x02\xf6\x88\x62\x6f\x03\xe5\xca\x7f\x4d\x8b\x9c\x38\x41\xdf\x7c\x24\x62\x1a\x17\x41\xfa\xa1\x42\x8e\xc3\xd8\x59\x43\xf9\xcb\xb1\x67\x0f\xc3\xd3\x87\xde\x33\x1f\x64\x31\x01\x67\xd1\x5a\x9d\xcd\x93\x68\x86\x60\xeb\xcd\x86\x21\x85\x70\x12\x65\x04\x6a\xd8\xe3\x38\x00\x49\x58\x35\x03\x41\x7d\xcb\x1f\xfa\x3b\x9c\x96\xa8\xfe\x60\xb9\x10\x03\xdf\xd7\x01\x03\x15\x72\xc8\x04\x15\x6e\x90\x17\x74\x94\x84\x61\xe3\xb1\x54\x78\xc5\xd9\x9d\x5a\xf5\xea\x75\xbd\x15\x79\xa9\x89\x58\x03\x00\x14\x84\x77\x0e\x11\xf8\xe5\xb5\x0a\x56\x8b\x9a\x62\xa8\xc9\x22\xe6\x80\xa8\xce\x58\xe6\x5a\x4c\x33\xd2\xe3\x8b\xd7\xb4\x67\xb8\x06\xad\xd2\x9f\x73\x92\xa4\xa5\x0b\xd7\x4e\xa3\xc1\xc4\x36\xe4\xb9\x4e\xe6\x97\xe2\xa6\x40\x45\x49\x2c\x71\x37\xa8\xd1\x80\x64\x30\x71\xbe\xe0\xb8\xec\x02\x2d\x8f\xb4\x86\x9e\x0c\x77\x2b\x37\xd4\x6f\x08\xb3\x4e\xd7\xc8\x4b\x6b\x77\xc3\xfc\x3f\xb0\xc5\xca\x05\x43\xd7\xe0\xe0\x03\x23\x30\x82\x84\xa6\x23\xe5\x25\xdc\x3a\xfe\x61\x1d\x56\x0e\x12\x4e\x65\xc8\x46\x26\x27\xd3\x33\x95\xc9\x62\x53\x9c\x65\x67\xf5\xc4\x63\xbe\x8b\xcf\x30\xe1\xfb\x73\xf8\x2d\x5c\x26\x1c\x55\x40\xca\x17\x93\xcd\x3a\x8a\x96\xa6\x07\xfc\x26\x95\x8d\x50\x0c\x19\xb8\x15\x39\x5d\x28\x45\x04\x36\xc2\x54\xcb\x75\xb9\x61\xe3\x5e\x6d\xdd\x5f\x6f\xea\xd0\xa3\x1b\x7f\x05\x0a\x4e\xa2\xa5\x17\x71\x05\x15\xa1\x9a\xce\x2a\x23\x08\x5a\x6e\xb8\xe4\x97\xa3\xbc\xed\xd7\x27\xc7\x6f\xdf\x9e\x5f\xf9\x6d\x9a\xd7\x41\x5d\x90\x30\xf1\x8d\x33\x87\x1b\xf5\xcb\x8c\xe2\xdc\x04\xc6\x10\xde\x2c\x4f\x4b\x1c\x82\x0b\xd9\x94\xd5\x4e\x9f\xab\x06\xbc\xa7\xe1\xbe\x18\x2f\x8f\xfa\x5f\x1c\x9a\xbf\xe4\x3d\xcb\x37\x1f\x12\x53\x76\x9f\xf3\xdf\x24\xbc\x2f\x08\xee\x68\xb0\xf4\xfc\x55\x8e\x37\xbd\xa7\x0e\x34\xc5\xe8\xfe\x00\x4c\x7a\x9f\xe3\x68\x44\xb8\x6f\xb0\x57\x2c\x53\x5c\x66\x1f\xb1\x3a\xb4\x69\xb1\x60\x18\xb9\xfb\xba\xfa\x7d\x0f\x01\x8d\x0f\x46\xc4\x3c\xd8\xca\x72\x5e\x6d\x64\x43\xf9\x8b\xfb\x21\xe9\xfc\x35\x30\x0f\x0f\x1a\xa7\x5f\x3f\x76\x3b\x36\x52\xa5\xbd\xa1\x7b\x7a\x6f\x5f\xa5\xac\x5d\x63\x13\xad\x7b\xcf\xe8\x18\xc3\x57\xdc\x34\x7d\x04\xf1\x6c\x54\x1d\xbb\x88\x2d\x44\x15\xe7\x8c\x7d\x40\xb7\x9a\xce\xab\x85\x45\xde\x48\xff\x27\x88\xff\x03\x6d\xb2\x3f\x9a\x1f\xc7\x43\x3d\x25\x13\x8e\xb0\x76\x3f\xe5\x9b\x7d\xac\x2c\xe1\xd6\xb9\x4c\xf7\x28\x41\x51\xd5\x18\x0f\x7d\xd9\x90\x67\x46\xe9\x9c\x07\xcb\x74\xa4\x4e\xa0\x2f\xf0\x3a\xc9\x37\xbd\xe8\x8a\xd3\x00\xfd\xcc\x0b\xd0\x6a\x19\x4e\xb1\xde\xe9\x39\x96\xd3\x2d\xda\x0a\x96\xf5\x92\xce\x9e\x8b\x69\xe0\xb5\xe8\x12\x7d\xbb\x97\x44\xa8\x34\xa6\xd9\xaa\xea\xe9\x8c\xcc\xfe\x53\xb0\xc3\x4e\x68\x9d\xd3\xd6\x03\x9f\x47\xf9\xb2\x94\x51\x51\xde\xa5\x05\x16\x3a\x2e\x96\x0d\x95\x64\xf9\x43\x7f\x4f\x94\x52\x40\xf3\xb8\xe4\xeb\x8a\x26\xab\xea\x8a\x57\xea\x6b\xfa\x43\xbb\xb0\xc8\xec\x31\x5d\x89\x40\x8a\x4a\x54\x21\x23\xa7\x66\x57\x8f\xda\xc5\xe9\xac\xa8\x41\x5c\x30\x2f\x6a\xba\xad\x2a\x6f\xa0\x0d\x09\xe9\x73\x24\xa8\xa3\x23\xf5\x84\x66\xe1\x93\xc8\x2f\xe2\xfa\xf8\xda\x52\x1e\xf2\x99\xed\xd1\x01\x45\xf0\xf0\x36\xf5\xeb\xf5\xc1\xc3\x1a\x6e\x57\x0b\xb3\x41\x51\xc6\x4a\xfe\x54\xad\xc9\x22\xa3\x85\x44\x1d\x31\xcd\x1f\xcd\x79\x62\x8a\x43\x5a\x98\x7b\x78\x29\x9b\xce\x22\x8f\x57\x17\x0c\x31\xe7\xb4\x3a\xee\x3d\x13\x9c\xd9\xd2\xb2\x5a\x75\x0a\xce\xd4\x17\x34\x98\x03\x85\x98\xc1\x77\x24\xb3\x23\x2b\x5b\xe4\xf0\x71\x50\xd5\x2f\xd3\x50\x11\xf7\x55\x09\x28\x0f\xfc\x51\x9e\xbc\x7c\x7d\x05\x6f\x14\x9a\x31\x78\x0f\x98\xcb\x0d\xdb\xe9\xce\x5c\x9d\xcc\x98\xab\xae\x93\x0d\xbb\xae\x80\x78\x5e\x83\x76\xd3\x6e\xd3\x41\x5c\x45\x0e\x98\x5a\x59\xc8\x72\x7c\x6d\x76\x3d\x08\x18\x33\x14\x7e\x2d\x89\x5a\x90\x13\x71\x72\x8d\x6f\x52\xcc\x54\x29\x0f\x1d\xa1\x12\x59\xe8\xc6\x3d\x74\xd9\x2f\x1d\xff\x80\x67\x0b\x9b\xe4\xc7\x8c\x03\x8e\xb5\xc1\xbc\x85\x86\x74\xbc\xb5\x14\xbc\x37\xee\x6e\x32\xd6\xf0\x62\x3b\xdc\xdd\xf8\x84\x40\x6e\xa0\x8c\x2a\x02\x76\x36\x0f\x17\x98\xa7\xff\xf8\xdf\xff\xe7\xf1\x09\x77\xfc\xa4\x6f\x37\xf4\xa5\x62\x19\xc3\x0b\x22\xa5\x82\xf4\xfc\x5f\x49\xbc\xbe\x56\x03\x87\x5f\xe4\x2b\xb1\xdf\x7f\xc5\xaf\x3d\x1b\x1e\x8b\x35\x05\x7f\x24\xfa\x8b\x6f\x60\x12\xf5\x13\x66\xde\x99\xf0\xd9\x46\x27\x9e\xce\x35\x21\xa3\xfb\x7d\x5f\x2d\x3e\x66\x72\x24\x7f\x9a\xfe\x99\x7f\xa5\x70\x11\x4d\x84\x6c\x98\x7f\x38\x66\x00\xf2\x1a\x70\x94\xd0\x90\x16\x0c\x52\xcd\xd9\x3d\xf3\xc8\xe3\xcd\xe6\xc6\xac\xf3\x0c\x90\xbd\x5f\x92\xdd\x9e\x6d\x7a\x78\x4a\xad\xb5\x0b\x4a\x81\x2b\x11\x27\xb2\x68\x10\xd4\xe0\xae\xe7\xa2\x3a\xd0\x3c\x75\x57\x5c\xc1\x26\xf7\x54\x64\x79\x05\x13\xdb\x19\xcf\x73\x1a\xb2\xca\xb7\x49\xe2\xd8\x9a\xb2\xb3\xbe\x2d\x21\xb6\xd3\x9f\x84\xb8\x25\xdb\x81\xe9\x65\x1f\xfb\x40\xf6\x39\x2e\xb7\x90\x6e\x57\x7d\x7c\x63\x99\xaf\xb4\x22\xc8\xeb\xcf\xf5\x33\xa1\x74\xfe\x7d\x45\x7f\x46\xde\xca\xec\xdb\x3c\xf6\x69\xde\xe4\xf3\x12\xc9\x6f\xf0\x41\x2b\x86\x18\x76\x4f\xd3\x00\xc5\x93\xfb\x91\x30\x1e\xaa\x5e\xa8\x0f\x5f\x89\x3a\x0b\xc8\xc5\x9e\x7c\x26\xb8\x36\x69\x73\xb6\x9c\x7d\x97\x5f\xcb\x4f\xc2\x91\x3a\x3d\xbf\x92\x2f\x49\x86\x1d\xb6\x80\xc2\x0c\xdb\xc1\x43\x24\xd3\x25\x70\x61\xdf\x89\x75\x60\x36\xee\x88\xe5\x0c\x7c\xae\xd3\xc5\x20\x7f\x29\x07\xcb\x17\x7c\xac\xb4\xb4\x1c\xcc\x38\x35\x53\x32\x97\xbe\xa5\x75\x24\x77\x0b\x67\xf2\xe5\x72\x0a\xb1\xcd\x3c\xc5\x56\xa6\x69\x66\x17\x7f\xce\x7f\x5d\x2a\x11\x25\x52\xdf\xf2\x5f\x67\x63\x2e\x21\x09\xf8\x44\x29\x4e\xde\x3e\x79\x36\x9c\x8b\x20\xab\x66\xb9\x60\x8e\x4b\x1c\x5a\x5f\xc8\x0f\xb3\x17\x84\xff\x36\x73\xe5\x4f\xf8\x67\xba\x19\xd5\xe2\x26\x37\x9c\xdb\x41\x33\x21\x0c\x35\x35\x09\x26\xcd\x85\x90\xd2\xe2\x76\x0a\x98\x4e\x11\x75\x04\x7b\x4e\x09\x21\x69\x45\x15\xb3\xfc\x3b\xa8\x19\x22\xf1\x34\x3c\xed\x73\xec\x87\x84\x43\x81\x7e\x8e\xfb\x19\x00\x49\x37\xf3\x09\x50\xd6\xcd\x78\x38\x1a\xf8\x10\x48\xd5\x87\x8e\xe9\x0c\x67\xcf\xcf\x0f\x4d\xed\x70\x82\x24\x33\x23\x01\x68\x51\x3a\x2f\x0a\x00\x41\x84\xe0\xf0\x00\x51\x33\xae\x32\x6d\x2c\xaa\x0f\x08\xed\xf3\x39\x65\xdf\x2f\x80\x4d\x57\x98\x71\xe5\xb3\x04\x75\x96\xa9\xcc\xc5\x6a\x8e\xaa\x0c\xf3\x48\xda\xc9\x44\x7e\x13\x44\x38\x59\x6e\x33\x51\xe2\x56\x8a\x1a\xc2\x1c\xac\x79\x44\x37\x5a\xf2\x96\xe9\xf5\x10\xec\x50\x7f\xb8\xea\x03\xe5\x54\xdc\x82\x90\x35\xce\x99\xb1\xaf\x8d\xe3\x9f\xc7\xb0\xf0\x00\x0f\x9d\x02\xed\x34\x4c\x08\x49\xc7\xbc\xa1\xbb\xae\x16\xaa\xa8\x99\x2a\x24\xb3\x5c\x64\xf3\x1b\x2d\x23\xf3\x0c\x1f\xc6\x03\x45\xb6\x6c\x24\xd2\x70\x78\x06\x2d\x72\xe6\x12\x26\x8a\x74\xea\x03\xcd\x4e\xc8\xe3\x9c\x19\xef\x46\x30\x22\x61\xde\xd4\x4d\x82\x30\x95\x02\xe4\x1c\x1f\x53\x20\xa2\xbe\x54\x05\x04\xef\x02\x62\xed\x6f\x17\x9e\x93\x0d\xb3\x65\x8c\x2b\xf1\x06\x76\x32\xed\x17\x94\x63\xa3\x53\xe6\xab\xa2\xad\x3e\x6b\x60\x08\x8a\x9f\xb7\xb4\xe3\x0b\x48\x43\xa3\x12\xbc\x92\x30\x0b\x04\x22\xdf\xe9\xfd\xf7\xdf\x7e\xe8\x78\x1a\xfc\x45\xc0\xfb\xef\x3e\x74\xf7\x9e\xdd\x7f\xff\xfd\x07\xdc\x00\x8c\x0a\x67\x4b\x56\x80\x8d\x6b\x40\x41\x83\xde\xb5\xe5\xa7\xaa\xd9\x77\x22\xa4\xe1\xd3\xf3\x87\xcf\x32\x15\x9f\xfb\x78\x89\x3b\x5f\xec\xc1\x0a\x2f\x5c\x56\xbc\xc2\xeb\xfd\x36\xd3\x31\x76\xc2\x01\xec\x97\x2b\x6e\x18\xc8\x72\x6e\xf2\x37\xf7\x9b\x87\x5b\x15\x3c\x58\xea\xbc\x45\xf3\xf8\x17\xf9\xf5\x0c\x03\xe1\xa1\xff\xe6\x5a\x6a\x82\xbb\x83\x2b\x71\x27\x67\xa1\xd9\xdd\x62\xdc\x94\xfd\x2c\xe6\x4a\x16\xda\x04\x5d\x8e\xb3\xb4\x17\x1e\x44\xe7\x0d\xa6\xb6\x21\x78\x5b\x02\x31\x06\xf7\x0e\x3f\x07\x99\xb7\x55\xd6\x46\x05\x94\xd5\x7a\x2a\x51\xd0\x01\xae\x15\x53\xb2\x0d\x7d\x1d\x9a\xa4\x3d\x57\x87\xfd\xfc\xca\x5a\x44\x9e\x20\xa9\x75\xe9\xea\x59\x12\xc6\xeb\x05\xf4\xcc\x50\xc5\xf3\x50\xd5\xd4\x52\xa0\xbf\xb2\x89\x5d\xa3\x81\x99\x2e\xf0\x61\xc9\xa2\xd8\x51\x3b\x7a\x47\x9b\x91\x12\x4c\x13\xcd\x6b\x8a\xce\x04\x74\xfa\x01\xc7\x36\x4f\x29\xbe\x8d\xe1\xa4\x08\xb4\xaa\x33\x33\xc7\xd7\x63\x03\x31\x4b\x36\x21\x93\x11\x11\x19\xb1\x73\xa8\xea\x55\x0f\x3a\xb2\x45\x97\x75\xe6\x18\x27\x57\xb5\x3c\x91\xe1\x6a\x2d\x0b\x28\x2e\x7e\xa6\x3f\x0e\xaf\x03\xb3\x18\xeb\x1f\xb7\x42\xdd\xa7\x3f\x96\x24\xfb\xa2\x2d\x3a\xbf\x6f\xc7\xf9\x8b\x66\xd3\xf8\x7d\x1d\xbf\x86\x00\xa2\xe7\xbc\x5f\x0c\x64\x33\xc9\xf6\xb4\xad\xab\x97\x13\x06\x3b\x8f\x40\x4e\x0c\x46\x32\x06\x46\x5f\x71\xa6\xf3\x0f\x91\x0e\xc2\x4b\xc4\x02\x10\x8c\x6b\x51\xd3\x29\x80\x3a\x45\xeb\x24\xd8\x94\x46\x5d\xa4\x8c\x50\x83\xce\x32\x7b\x68\x7a\xc0\x77\xc8\x81\x52\x5d\x6b\x3e\xac\x43\x9f\x6e\xda\x1f\x8a\xa5\xa7\x77\x5c\xd6\xc9\x21\x28\x90\xc1\xc3\xc3\x90\x66\xb2\x16\x36\x27\xba\x14\x43\x15\x3d\x68\x70\x8a\xda\xe7\x74\xd3\x70\x86\x05\x03\xee\xaf\x9b\xd4\x1d\xd1\x10\x52\x8c\x77\x89\x3c\xe5\xc2\xe6\x36\x87\xb5\xa1\xe5\x67\x83\x6a\xe1\xfa\xfc\x14\x26\x71\xc3\x06\xb5\x85\xa7\xa9\x7e\x69\x7e\x74\x7a\x1c\x9e\x1a\x6d\xe4\x0d\xeb\xc2\xf6\x1b\x6c\x10\xb8\x81\x94\x1f\x4b\xb9\x3b\x33\x20\x44\x65\x62\x89\xc6\xb7\x15\x70\x79\x89\xd9\xa4\x26\x11\x9c\x3b\x2f\x17\x39\xac\x5f\xe1\x35\x45\x63\x5d\x73\x94\x28\x3f\x7a\x02\xe1\x18\x13\x56\x3f\x9b\x13\x87\x91\xb6\x98\xa7\xb9\xea\x4d\x21\x32\xc0\xd4\xbc\xec\xaf\x79\x5e\xc5\x44\x81\x91\x2b\x96\xb1\xdd\x0f\xe1\x4e\x4d\xec\xed\x09\xda\x78\xc2\xdb\x75\xa1\xac\xee\x5f\xf0\x43\x18\x9e\xa2\x72\x20\xcc\x4f\x90\x01\x56\xbb\x4d\x2a\x93\x23\x34\xd8\xdb\x92\x1a\xc5\x16\x5f\xd8\xf9\x52\x18\xef\x8f\xec\xe3\x66\x9c\x15\xdf\x44\xe1\x6c\x82\xa0\xe9\xdf\xbb\x74\xad\x1f\x35\xe9\x4e\x6e\xcd\x48\xda\x3f\x56\x3d\x95\xfe\x4f\x1f\x8c\x46\xe9\x28\x90\x85\xbc\x14\xf4\xe9\x7f\x46\x50\xc3\x63\xb5\xcf\x13\x25\x2e\x08\xaa\x34\xf3\xc4\x42\xf3\x75\xd7\x25\x52\x11\xd4\x38\x0f\x04\xc9\xd0\xfb\xb5\x70\x26\xa9\xd7\x74\xc4\x67\x46\xa0\xd8\x74\xd7\x4c\xb3\x08\x35\x10\x71\x5b\xdf\x12\x53\x8d\xcb\xb9\x1a\x55\xeb\x56\xbe\xc2\xc4\x0b\x5f\xaa\xe0\x98\x54\xb4\x3e\xec\x72\x91\x7e\xb9\x6b\x84\xe9\xba\x14\xb6\xd8\x7b\x8b\x71\xc6\x22\x15\x82\xf2\x2b\xe0\x67\xd6\xf7\xaa\xcb\x60\x55\x24\x56\xc9\x57\x6a\x2a\xb4\xa9\x16\x7d\xea\xd2\xa9\x39\xb5\x1b\x87\x6d\xc9\x4a\x42\xd8\x38\x23\x71\xda\x2c\xbb\x35\x62\x62\x30\xc0\x92\xb8\xd4\xb6\x81\x14\xe7\x58\x44\x5e\x67\xd0\xc6\x63\xa8\x61\xa0\x0c\xd6\xb7\x1a\x72\x19\xe2\xf1\x00\xc3\xa2\x19\x1b\x0c\x37\xa8\x16\x8a\xee\x43\x35\x3f\xe8\x6f\xaf\xdb\xb8\x80\xf8\x73\xf0\xa2\xb7\xa1\x77\x6e\xf9\x9a\x6e\xe4\x60\x93\x53\xb1\xd3\x42\x1d\x1c\xcd\x43\x23\x61\x16\x60\xae\x53\xf5\x11\xd1\x0c\xd9\x0a\x08\x68\x6a\x75\x83\x68\xf7\xb5\x2e\x42\x94\x82\x1a\x90\xc9\xfc\xb7\xf1\x78\x95\x98\x0f\x8d\x35\x64\x5b\xb5\x6c\xdb\x11\x3d\x3d\xfc\x97\xfb\xc5\x23\x59\xc8\x30\xd5\x19\x5d\x97\x70\xa2\xa0\x33\xdc\x22\x79\xcc\x55\x07\xa7\x69\x26\x1b\xde\x2c\x18\x88\xbe\x67\xbf\x25\x81\xa6\x2e\xd8\xcf\xfc\x19\x3c\xc8\x9e\x50\x18\x04\xb9\xd3\x4a\x83\x21\x40\xe1\x55\x31\xf7\xbb\xa8\xed\x26\xa3\xe5\x91\xe9\x89\x8e\xb6\x14\x5e\x2c\xf8\x35\xec\x82\x1d\x65\x86\x55\xbb\x43\x41\x3c\x22\xda\xdb\xe7\xbc\x8f\x88\x2b\xb0\xf0\xe9\x40\x3b\x49\x24\xa0\xee\x1f\x7a\x25\xad\x02\x42\x54\xfd\x80\xcd\x4f\x62\xc7\xc4\x3a\xf8\xab\x86\x19\x13\xb7\x76\x61\xae\x1f\xf4\x29\x8d\x98\xf5\x81\xe9\x43\x8b\xa3\xf5\x28\x1e\x64\x29\xb6\xcf\xfc\x37\xcc\x70\x61\x4f\xb4\xaa\x4c\xa6\x5e\x6b\x44\xe5\x9a\xe2\xc3\x81\x1c\x39\x6f\xcf\x07\x37\xf4\xef\xf1\x76\xfb\xb8\x28\x1e\x4c\x8c\x3a\x10\x8d\xdc\xb0\x07\x06\x5c\xaa\x85\x18\xb0\xca\xa0\xa6\x40\xce\x9c\xc6\x1d\x03\x44\xf3\xf4\x0b\x4b\x01\x25\xdf\x87\xa5\x85\xc7\x9b\x90\xae\x9f\xbb\x8e\xb7\x80\x86\x18\x9e\x37\x0a\x61\x56\x21\x5e\x81\xe1\x48\x06\x12\x7a\x90\x35\x70\x76\xbe\xb5\x7b\xee\xde\x44\x25\x3a\x62\xdf\xdb\x03\x28\x91\xd8\x77\x07\x11\x12\x48\xc6\x1e\xa9\x4e\x3a\x9e\x00\x9c\x92\x8d\x7d\xdb\xff\x4c\xf9\x78\xaa\xf1\x29\x12\xb8\x4b\x42\x9e\x8a\xb3\x6a\x69\x33\xa1\x6f\x38\x9b\xc8\x97\xcf\x0a\x62\xb7\xa8\x9c\x11\xfc\xf6\x60\xeb\xa6\xf9\x28\xf1\x6b\xe6\xf8\xf4\x39\x2b\x8e\xdb\x28\x99\x1c\xa1\xf0\x55\x9c\x4b\xa2\x65\xb5\x08\xe3\xb9\x3e\xe7\x84\x89\x2e\x16\x3c\xc7\x6d\xf6\x77\xd1\x49\x9e\xe2\x57\xfa\x3f\x99\x30\x1c\x88\x7a\x8a\x9c\x5b\xbc\x22\x36\x62\xf0\xb9\x6a\xe3\x1f\x34\xa5\x2e\x09\xe3\xb6\xd4\x08\x9e\xd9\xf9\x97\xf9\x71\x4c\xf9\x6f\xc4\x4e\xa5\x33\x5f\xbb\x73\x4b\xe3\xdb\x21\xfd\x3c\x37\xcf\xb6\x31\x98\xbb\x78\xf5\xde\x6c\xf1\xed\x14\x9b\xa0\xd5\x62\xbc\x0e\x8f\x36\xbe\x47\x6a\xb0\x61\x85\x6e\x74\x44\x77\x2e\x24\xa2\x9e\xb8\xa1\x05\x80\x41\x57\x17\x74\x0f\xb1\x80\xe1\xd3\xca\x92\x59\x27\xd7\xec\xea\x9b\x27\x1e\x1e\xa2\x29\xc8\xdd\xe9\xbd\x83\x55\xc1\xc4\xd6\x07\x1d\xb8\x8b\x25\x23\x2e\x22\xd6\x55\xe4\x05\xd3\x3b\x70\x6b\x02\xa6\x83\x9b\xeb\x01\xa0\x21\xe5\x9c\xf8\x87\x58\x1c\x4a\xb1\x3c\x72\x0b\xec\x03\x0d\x96\x58\x09\xf1\x9d\x9d\xeb\x11\xb3\xa7\xb2\xed\xe1\x90\x37\x46\x3b\x3b\x08\xd0\x02\xca\xbe\xa5\x66\x1e\x43\x56\x92\x20\x8e\x18\x84\xac\xbf\x6a\x19\xe0\x03\x86\x93\x6c\x08\xf9\xa9\x2a\xf6\x44\x7d\x3c\x17\xb7\xd5\xfb\x5d\x5c\x2f\xad\x78\x5c\x98\x1f\xac\x7b\x30\x9f\x10\x22\x5c\x84\x5f\x78\x64\x2e\xbd\xa3\x71\x37\xd5\x32\x33\x21\xa7\xed\x50\x1c\xc0\x61\x38\xf5\x1e\x77\x21\xab\x12\x3e\x04\xd3\x2d\xb1\xae\x36\x31\xe9\x87\x74\x34\x1f\x31\xb6\xc4\x8b\xd3\x49\x55\x77\x1a\x8f\x8d\x08\x61\x80\xa5\x41\x7d\x40\x58\x60\xe2\x65\xb3\xcf\xc3\xe7\xc8\x70\x1a\x7d\xd8\xce\x00\x21\x49\x48\xf4\x01\x04\x43\x66\x9b\x00\x3a\x2a\x1c\x29\x0f\x3e\x72\x5a\x55\xf1\x5d\x0b\xac\x01\x3d\x0f\x6c\x22\xcc\x0e\xfa\x0a\x1b\x01\x41\xc0\xeb\x51\xd3\xde\xa5\xee\xc8\x5b\x32\x39\x13\x0f\x96\x3b\xd9\x68\xac\x2e\xca\x1d\x87\xa6\x64\xc5\xe6\x52\x76\x5b\xe1\xf9\x77\xb4\xfa\xdd\x6d\xad\x8a\x01\xd6\x54\xb3\x66\x8a\xa8\x3e\xea\x58\xb4\x1c\x4f\xfa\x8e\xd6\xbe\xb7\xd6\xc2\x2d\xeb\x63\x59\xee\x82\x26\xe2\xee\x07\xae\x19\x60\x9e\xb1\x85\xd5\x04\x47\x53\xef\x43\x73\x57\x3e\xc0\xc4\xa3\xd0\x2a\xde\x4c\x40\x77\xb3\x3b\x3c\xb5\xc6\x0b\xc4\x54\xa0\x08\x82\x0e\x35\xa8\x83\x61\x2d\x4f\x16\x70\x6e\x18\xc7\x1a\x4b\x9e\xa8\x4a\x8c\x6e\x07\x66\x45\xde\x7f\xd9\x75\xcd\x0a\xb4\x87\xbb\x17\xdb\x55\xa6\x13\xf6\x94\x0e\x94\x8d\xd6\x43\x62\x4d\xc5\x55\x81\xc7\x73\x12\x24\x1f\x2e\x30\x70\x10\x88\xea\x8a\x1d\x04\x82\x0e\x0a\x15\x1d\xaa\xe7\x64\xb2\x0e\xa5\xbc\x70\x6a\x39\x4c\x41\x05\x87\xf4\x4c\x43\x7f\x69\x00\xff\xa1\x47\xb8\xe6\x5e\xaf\x9b\x20\xd2\x8b\x78\x2d\x60\x2f\x0a\x3b\x32\x8b\xc7\x7a\x2d\xe2\x89\xe2\x45\x85\x95\x81\x14\x63\x7b\x8b\x89\x32\x38\xf2\x6e\xf7\xb4\x75\x6e\xaa\x8f\x50\x86\x11\x61\x4a\x2c\xe0\xf3\xcb\x2b\x68\xc0\x68\x01\xd0\x3e\xba\x62\xbe\x9b\xfe\x75\x5d\xd6\x08\x4f\xc9\x21\x79\x95\x11\x2d\x16\xec\x6c\x54\xd5\x1a\xbb\xef\xba\x34\x13\xf0\xba\xd8\x08\xdb\x0a\xdd\xd1\x4c\x7a\x10\x4d\x58\x8a\xb0\xbb\xbc\xd2\xba\x5d\xb9\x20\x99\x78\xc6\xd7\x5e\x6d\x8d\xb7\x0c\x5c\x84\xf5\x5b\x43\xb7\xbb\x91\xc0\x40\x87\x15\x66\x01\x5a\x14\x25\xa1\x76\x58\xf7\xe0\x11\x7a\x86\xa0\x53\x52\xb0\x61\xf8\x36\x19\x18\xfc\x55\x2c\x8f\x2b\x66\xd7\xa9\xda\x92\xdc\xe6\xd0\x71\xb0\x0f\x61\xec\x44\x69\xfa\x0e\x51\x78\x58\xd5\xcc\xeb\x15\x4c\x99\x30\x01\xd2\xed\x1a\xb1\xcb\x7c\xa7\x9f\x63\x20\x39\x2d\x71\x4f\x5e\xc9\xd7\x18\x64\xa7\x51\x90\x5c\x3c\xa4\x31\xc8\xbc\x29\xf8\xfc\xf3\x9c\xfe\x8c\x85\x68\x17\x38\xdf\x24\x69\x10\xe7\x8e\x3d\xe9\xe5\x96\x99\x33\x08\xdd\xe5\x66\x29\x51\x11\x58\x73\x84\xe3\x9e\x28\xfb\xd8\x02\x59\x62\x7e\x96\xec\x02\xc7\x15\xa8\x5f\x1c\x5c\x3e\x10\xf0\x2c\xd4\xe4\xa9\x0f\x6d\xe8\x14\x39\xec\x13\x6e\x2d\xac\x5f\xaf\x45\x06\xc1\x34\xe0\x70\x2b\xd1\xe9\x8e\x78\x6b\xe1\x73\xa1\xdd\x23\xda\x06\xb4\x93\x88\x74\xec\xcd\x44\x44\x8d\x40\x23\x06\x22\x32\xac\x98\x64\x05\xc6\xc0\x16\xa5\x1b\xc4\x06\x84\x8d\x7b\xa4\xc6\xdb\x8c\x20\x31\xdb\x1e\x41\xf8\x5b\x4e\x00\x99\x9b\xd4\x70\x9f\x51\x70\x7f\x56\x78\x15\xad\x87\x80\xa3\x44\x2f\x1a\xa0\xa3\x1a\x67\x4e\x34\xb9\xcc\x29\x4c\x91\x1b\x28\x4c\x19\x57\x6c\x33\x19\xac\x6e\xde\xa6\x49\x38\x12\x29\xba\x2d\x57\x79\x5b\x58\xf8\x15\xe5\x34\xec\x82\x02\x8e\x12\x7a\xdb\xe6\x1b\x3a\x7c\x6b\x15\xc4\x19\x09\xe4\x23\x9b\x45\xd1\x7c\xb3\x8c\x63\xfa\x06\x96\x16\x0b\x61\x63\xec\xf0\x47\xcc\x65\xbf\x63\x7e\x23\xcc\xcb\xda\xc1\x90\x1f\xfe\xe9\xf2\xfc\xed\x51\xfa\xf9\xf1\xf5\xf5\xf5\x63\x2e\xfe\x78\xdf\x6e\xd8\x35\xae\xe0\xf0\x2e\xff\xe3\xec\xcd\x51\x5a\xf6\x8b\x47\x33\x3a\xa8\xb7\xb1\x7a\x4b\x8d\x43\x71\xf5\xc0\xd4\xc5\xa2\xe3\x1f\x67\x4f\xba\x62\x34\x5a\x7a\x18\x45\x2c\xdc\x20\x79\xf6\xcc\xf8\x43\x27\x53\x8c\x40\xfc\xe1\xb0\x5c\xb4\x25\x6c\x27\xf0\x11\x64\x6c\xe8\x4c\x30\xe5\xd6\x3f\x04\xa9\xa8\x1d\xed\xc6\xeb\x85\x46\x6b\x1f\x80\xd8\x55\xe1\x09\x2e\x09\x5d\x26\x26\xce\x6d\x2b\x1c\xa7\xae\x5b\x37\xfb\x4d\x11\x73\x4c\xc2\x99\x4e\x44\x59\xfc\x34\x2c\x0c\x33\x47\x84\x5b\x7e\x9a\xfe\x89\x35\x45\x3c\x51\x42\x5b\x9c\x65\xb4\x05\xe0\xd9\xb0\x30\x02\x07\x06\x82\x31\x0d\x40\x02\x22\x9a\x60\xee\xf3\x9c\x70\x3e\xaa\x44\xcf\x6f\x6c\x74\xd1\xa7\x5b\x77\x9e\x03\xad\x49\x75\xe3\x22\xb1\x9e\x6e\x3a\xdb\xf0\x22\xc6\x8e\x47\x6a\x06\x69\x4a\xac\x29\x3c\xa4\x62\xe3\x39\x89\xa2\x80\x3f\x02\x94\xb9\x48\x68\x74\xea\xd7\x2e\x18\x53\xaa\x91\x32\xcb\x61\x46\x10\x0a\xa1\xec\xe1\x84\x3e\xb5\x16\xe5\x44\xed\x66\xcd\xaf\x1e\xe3\x6f\xba\xc3\x89\x64\x22\x7e\x3b\x11\xf7\x00\xeb\x88\x65\xae\xeb\xe1\x2e\x36\x14\xb7\x94\x37\x79\x51\x46\x79\xd3\x68\xbb\x56\xc0\x41\x1b\xa3\x5d\x52\xa5\xe3\xb1\xcc\xef\x5b\x38\x24\x10\x88\x89\x4f\xa6\xa3\xb4\x68\x30\x70\x7e\x3c\x75\x69\xb1\x78\x65\xab\x14\x7c\x37\x5e\xa2\x8c\x10\x59\x47\x21\x47\x65\x41\x2d\x36\x08\x60\x10\xf8\xec\xb2\xaf\x80\xd9\xd5\x8f\x3c\x96\x43\x11\x5a\x6a\x35\xef\x33\xf1\x92\x1b\x64\x0e\x9f\xa7\x18\xae\x6c\x92\xd5\xe4\x81\xa3\x13\xf9\x0a\xb1\xb5\xdb\x34\x37\xe6\xd4\x7d\x8a\x5f\x1a\xf5\x25\x1c\x99\x07\xd3\x41\x79\xc8\x40\xf9\xd2\x64\x71\x75\xbf\x06\x51\x4e\x55\xc4\xad\xf9\xbc\x8b\xa2\x04\x13\x1e\x63\x22\x85\xf7\x44\xf7\x26\xfc\x82\x1d\xd4\xd0\x83\xf9\xd4\xb5\x70\xc0\x83\x39\x2e\x1a\x7a\x31\x07\x45\xbf\xc0\x8b\x39\x46\xd2\xd8\x47\xd9\x0f\xf5\x0b\xdc\x94\xa7\x06\x3d\x96\x6b\xa7\x10\x3f\x51\x60\x4a\xba\x2d\xc2\xb1\x7d\x81\xbb\xf2\xe0\x64\xfb\x25\x02\xee\x54\x4f\x3c\x4a\x02\xe4\xde\xa5\xf1\x2d\xaa\xe5\x72\x36\x6f\x9b\xeb\x8e\x7d\x82\xf1\xd6\x03\x73\x59\xfe\x9d\x5e\xe2\xb7\x80\xf0\x55\x3f\x88\x42\x3e\x24\x51\xcd\x8d\x9e\xea\xcd\x9e\x24\xe2\x9e\x75\x18\x61\xfe\x94\x72\xe4\xce\xf5\x2d\x9d\xc4\x8e\x2d\x67\x26\x45\x68\xa3\xbb\xce\xf8\x0b\xde\xcc\xd0\x3e\xb3\xb2\x14\x85\x2e\x39\x45\xc1\xf8\xd3\x10\x6e\xbb\x12\x2c\xdd\xe4\x56\x5a\xc4\x57\xaf\x39\x02\x61\x19\x1c\x81\x11\x31\x54\x90\x4f\x3d\x48\xe8\xb5\x48\x10\x86\x4b\x0f\xa1\x08\xc2\xa2\x7f\xfe\xfa\xad\xfc\x84\xf9\xba\x46\x11\x82\xfd\x3a\x5f\x8e\x27\x66\x14\x3f\x9b\x32\x8e\xb7\x3c\x71\x64\x10\x35\x87\x3d\xce\x86\x5f\x0e\xa2\x68\xf3\x25\xae\x81\xf8\xaf\x4b\x25\x19\xd8\x17\xbb\x68\xcb\xc7\xc3\x62\x84\x1c\x41\xf5\x25\x3e\x5c\xba\x5e\xe3\xf0\x1f\x97\x96\xc3\x44\xe3\x69\x30\x72\x8f\x11\xb3\x05\x20\xba\xbb\xdf\xa5\x5d\xc5\xba\x53\x25\xd0\x41\x83\xa0\x0e\x1f\xfe\x17\xb4\x83\xe7\xca\xc2\xb1\x86\xf7\x43\x88\x5c\xd0\xad\x53\x87\x1f\xf6\xdf\xe9\xc5\x29\x59\x9f\x1f\x99\x45\xfd\x8e\x4a\xcb\x16\x5b\xda\x6c\xa7\x9b\x66\x05\x29\x12\xaf\x13\xca\x3b\x14\x7b\x7e\xaf\x85\x10\xc1\xd1\xa8\x58\xe2\x70\xc4\x5f\x6d\xa9\xfe\x4f\x12\x3f\x5c\xaa\x27\xe9\xc1\x39\x4d\x93\x20\x51\x8b\xdf\xa6\xe5\xe1\x48\x6d\x51\xd6\xa2\x32\x3e\x3e\xb1\x69\xaa\xbd\x0b\x09\xe5\x43\x32\x59\x84\x9e\x29\x2c\xa6\x70\x18\x09\x19\x7b\xd0\x81\x88\x11\x5b\xea\x98\xf9\x5a\xce\x75\xab\x21\xae\xf8\x6f\x48\x16\xfa\xb4\xc7\x85\x7c\xb9\x1c\x96\x80\x45\x8c\x7b\x23\x5f\xf2\x0c\xdd\x90\x9a\xc6\x6e\xfd\x94\xf7\x78\x38\xd7\x01\xbc\x43\xc0\x5f\xcb\xff\xf8\x5f\xff\x97\x55\xac\x0d\xed\xb0\x88\xc1\xa1\x61\x2c\x3d\xad\x98\x43\x9c\x7f\x5b\xe6\x31\xf8\xba\xef\xc8\xf0\x92\xcf\x35\xa8\xa4\xe2\x89\x66\x44\xef\xfc\x0c\x83\xad\x15\xb6\xd7\x1b\x2c\x18\xd0\x8f\x5f\x32\xb8\xc6\x1c\xd6\x61\x44\x90\xe9\x7e\xe3\xc2\xeb\x29\xa0\x70\x10\x8c\xa8\x57\x22\x99\xde\x9e\xdc\xda\xd5\x27\x31\xc0\x84\xed\x3b\x49\xde\x37\xed\xea\x83\x0f\xd1\xea\x9c\x00\xa2\xf0\xac\x38\x83\x32\x8c\x0b\x72\x76\x00\xd0\x07\x3e\xf3\x35\xda\xa4\xbc\x64\xd2\x1b\x3f\x21\x26\x1a\x00\x89\x91\x0d\x73\x34\xf3\x2c\x9e\x99\x83\x95\x44\x78\xd4\xab\xf2\xe8\x6d\x47\xf8\x3d\x99\x72\xd4\xe4\xc8\x22\xd1\x3b\x5c\x76\xd3\xe1\x0f\x0e\xc2\xc9\xaf\xd1\xb1\x3e\x4b\x6e\xdf\x5e\x23\x81\x18\x03\x12\x10\x60\x16\xee\x49\xfc\x37\x41\x58\x3f\xd5\xe0\x71\xaa\x7e\x69\xfa\x20\x74\x60\xf4\x02\x43\xe0\x10\x86\x90\xa2\xd1\xb3\x0a\x5c\x39\xb0\x32\x71\x7d\x3f\x0a\x34\x0b\x14\x22\xf5\x36\xe8\xc8\x0b\xf8\xc1\x06\x57\x36\x1a\xa7\x0a\x8a\x70\x36\x0b\xab\x45\xb8\x04\x9d\x20\xcc\x69\x1d\x19\xb1\x76\x33\xdf\x4c\xb0\x7e\xd6\x72\xb7\xef\x8b\xe1\x71\xa3\x39\xad\xa4\x9f\x04\x3e\x72\x83\xd4\x63\x61\x2e\x6e\xf7\x92\x9c\x6e\xe8\x84\xb1\x89\xce\x89\xa8\x88\x65\xc1\x9f\x0e\xb8\xc0\x8e\x43\x02\x7f\xbd\x13\xec\xb8\x8e\xdb\xdd\x60\xff\xe8\xb5\xf2\x74\xb8\xbf\x50\x19\x36\x88\xfb\xe7\xb2\xa6\x02\x00\xfe\x91\x4b\xde\x18\x34\x10\xb1\x22\x14\xb8\x9a\xbe\xf4\x32\x41\xef\x8e\x89\x52\xff\xb1\xab\xe3\x38\x10\xee\xb0\xd7\xa3\x48\x75\x51\xa7\xbf\x2e\x62\xdd\xc1\x3b\xd8\x88\x57\x0c\x0f\x87\xa3\xb0\x13\x18\xe0\xad\x45\xe2\x20\x14\x61\x87\x9d\x3a\x30\xb8\xd3\xd3\x1b\x02\x09\x3f\x21\x3a\xee\xbb\x63\x50\x1c\xb8\x34\xb9\x2d\x18\xc5\xb0\x97\xcc\x63\x9c\x8b\x46\xd8\xc9\x5b\x4b\x84\x7b\x6d\x7c\xef\xfe\x8f\x04\xa8\x98\xbe\x98\xe0\xc3\xe3\xb5\xe9\xcc\xb0\x33\x1b\xfe\xbc\x26\x82\x0f\x1f\x86\x2f\x39\x9d\x78\x46\xeb\x31\x07\xa9\x4a\x90\x3b\x08\xbc\xac\x5c\x7b\xa6\xf7\x70\x16\x9b\x6a\x90\xee\x59\x1e\xac\xa0\xf5\xa6\xd1\x03\xc9\x6f\x88\x30\x93\x39\xc3\xf2\x71\x1b\xb1\x47\x82\xa5\xba\xbb\xa1\x33\x7c\xb8\x74\xc2\xda\xa2\x44\xdc\x80\x13\xf9\x72\x39\x7a\x48\xd3\x60\xd6\xbe\x13\x12\xa8\x18\x5e\x44\x41\xaa\xee\x76\x8a\x6b\xf6\x88\xa6\x49\xb9\xd9\xf1\x14\xe6\xa9\x53\x13\xb2\x7c\x2b\xbb\xa0\xc8\x82\xda\x2b\x88\xd6\x3f\x0c\xeb\x92\x37\x67\x74\xd7\x7c\xdb\x5c\x27\xb2\x65\xce\xe0\x18\x21\x81\x75\x35\x25\xee\x92\xa4\xb1\x40\xa2\x51\x8f\x52\x09\xef\xa0\x71\x71\xc6\xf9\x83\x58\x02\xd8\x31\x5c\x14\x01\x8b\x93\xcd\x42\xa5\xba\xe0\xd4\x72\x37\x13\x3b\xd7\x4b\xad\x10\x4a\x7d\xb3\x90\x4d\xe3\x76\x43\x88\x2f\x69\x18\x4f\x0c\x0f\x9b\x3b\x32\xc5\x18\xc2\x28\xaa\xc6\x4e\xe2\xc5\x49\x2b\xfa\x6e\xac\xf5\xc3\x3d\xdd\xe6\xfb\x11\x42\x7c\x49\x3f\xb8\x15\x58\x93\xcb\x21\xe5\x96\xfe\xd0\xa9\x52\xa3\x42\x46\x16\x00\xc3\x2e\x7a\x1f\xf9\xab\x60\x9f\x86\xd5\x49\x31\x90\x3b\x58\x0f\x3d\xde\x30\x25\x47\x6e\x87\x27\x44\x03\x31\x0e\x9a\x0c\x18\x75\xf7\x12\x87\xd9\x3e\x97\x74\xa0\x81\xd9\x8f\x07\x9b\xdc\x75\xa4\x5f\x5e\x94\x83\x6c\x75\xa6\xf2\x9c\x64\xde\xbd\xe1\x0a\x9c\x85\x4a\x12\xb9\x2e\xdc\x32\x20\xd8\xd9\x4c\x16\xf2\x68\x80\x5b\xe3\xcc\xea\x82\x56\xc7\x95\x39\x56\x0d\x28\xc7\xa2\xc7\x70\xc6\x3b\x43\xa9\x2c\xd0\xd2\x32\x53\x3e\x32\x59\xd5\xd9\x24\x00\x6a\x9b\xdf\x44\x56\x3f\x30\x52\xde\xc6\x8f\xec\xde\xa2\xd8\x19\x77\xc5\xef\xda\x12\x89\xdf\x11\xcc\x41\x6d\xce\x2c\x5c\xea\x63\x02\xf1\x64\xb7\x6a\xe1\xd1\x60\x73\xcd\xcc\x22\x20\x05\x54\xf8\x83\x1b\xa5\x7b\xdb\xd1\x73\x03\x5c\x3b\x53\x45\x0f\x6e\x63\x0a\x5f\xd1\x01\xb0\x8d\xdb\x7b\x00\xb6\x20\x4e\x6e\xd4\x8d\x80\x05\xdc\xd6\x11\x7d\x94\xf1\xcb\x3b\x02\xbe\xf1\x85\x1d\x39\xb2\x5e\x68\x14\xeb\xa2\x98\x5c\xff\xb7\xf5\x6f\x70\xcc\x01\x71\x46\x61\xd2\x07\x04\x1f\x3d\x22\xef\x88\x3e\xb0\x7f\xb3\x6a\x61\x69\xa1\xf6\x78\xba\x9d\xf9\xaa\x6a\x9a\x42\x9c\x5b\xeb\x3e\xb4\xd9\x8b\x43\x93\xb0\xb9\x58\xdf\xde\xa8\x48\xc2\x83\x8b\x23\xa3\x78\x53\x1d\x39\x7c\xe1\xee\x58\x42\xe7\xbf\x07\xda\x3f\x24\xee\x5d\x40\x5e\xca\xf6\x9d\x88\xa2\x46\xee\xcf\x10\xbc\xdc\x47\x2d\x4f\x87\x51\xcc\x6f\x8d\x20\x1f\x47\xce\x1f\x3e\xa1\xd0\x49\xa0\xb1\x95\xc9\x72\xf6\x1a\x63\xa2\x26\x4a\xcc\x58\x6f\x08\x07\x5b\x3c\xc5\xbb\xe0\x60\xc2\x4d\x5d\x89\x31\xcc\x99\x7c\xd1\xd8\x13\x8c\x29\xdb\x49\x30\x08\xbc\xa4\x2f\x01\x1e\x35\x85\x03\x3c\x26\x7d\xd3\x43\xa0\xb8\xe2\xbf\x3f\xa4\xf7\x8b\xc4\x0f\x1d\x2a\x4b\xd6\x0e\xa9\x94\x20\xdf\x41\x7e\x10\xee\x1c\x06\xf2\xad\x05\x70\xf7\x35\xa0\x9b\x50\x8c\xee\x83\x6e\x6b\x27\x51\xe9\xbe\x9b\x6a\x31\xe3\xeb\xd6\x54\x2f\x9b\xdd\x83\xec\xcc\x41\xf8\x8d\xb4\x82\xdf\x48\x13\xb5\xd9\x51\x90\x10\x4d\x48\x98\xb1\x73\x51\x47\xa3\xe4\x78\x57\xf4\xe9\x08\x24\x13\x27\x71\xf4\x98\x28\x21\x5f\x8c\x5a\x31\xb5\x78\x98\x66\x86\x77\x3e\xc5\x4c\xf0\xa2\xda\xe3\xc8\x93\x61\x96\xd8\x2d\x46\x49\xfa\x38\x64\x3c\x12\x51\x4a\x86\x69\x9b\x66\xc5\xaf\x1c\xd8\xe3\xc3\xc1\xf0\x54\xb0\x8e\xeb\x34\x1b\xec\xa8\x0a\xf8\x7a\x86\x29\xb8\x2d\xeb\xf3\x2e\x2e\x8d\xf5\x19\x26\xa8\x9f\xfc\x08\x90\x0e\xda\xf9\x62\xad\x2e\x41\x13\x84\x64\xe7\x65\x47\x4c\x72\x68\x9e\x82\x94\x07\x3c\x53\x7b\xae\x73\x12\x86\x9f\x66\xc7\xeb\xa8\x41\x2e\xfb\x34\xb0\x03\x0d\x82\x73\x36\x1a\xde\x8a\x1d\x1c\x5c\x20\xce\xf4\x1c\xcb\xb1\xbb\xb5\x50\xb0\xc5\x71\x94\x05\x0d\xfe\xa9\x25\x45\x1c\xb9\x65\xaf\xf3\x35\xeb\xae\xa9\x66\x24\xc1\x63\x6e\x9d\x17\x22\x72\x38\x5c\xe9\x75\xae\x65\x7f\x51\x1d\x83\x5e\x7a\x08\x57\xcd\xd7\x77\x15\x2a\x35\x0e\x53\x43\xbd\x19\x74\x32\xe2\x79\x06\x72\x47\x0d\x83\x2e\x4e\x56\xf1\x15\x9d\xe4\x17\x85\x57\x0b\xf7\x0e\xea\x29\x07\x7d\x6e\xe7\xcc\xf1\x78\x83\x2b\x17\xe6\x4b\x16\xa9\xe5\xa6\x8b\xdf\xd6\x33\x74\x88\x0f\xe4\x53\xd5\x1f\xea\x5b\x5b\x76\x37\xf5\x22\xc3\x73\xb8\xdd\x5a\xaf\x3f\xdf\x95\xa2\x1c\x7f\x30\xa3\xb4\x27\xb9\x86\x58\x2b\x71\x51\xd8\x3d\x90\x77\x01\x1e\x2e\x28\x1d\x56\xc9\xb4\xff\x3d\x06\x4f\x44\x69\x93\xee\x48\x76\xeb\x1f\xdd\xda\xd0\x60\x2c\x01\x43\x0c\x70\xdb\xa2\x2b\xfc\x80\xff\x17\x8c\x20\xb8\x7a\x0f\x87\xc1\x64\xa0\xab\x1f\xbc\x22\x7c\xc6\x86\x11\xf7\x90\xcd\x28\x38\x8c\x37\xdb\x88\xa8\xed\x95\xee\x76\xf6\xdc\xb1\x5e\x88\x1d\x18\x50\xd8\xee\x2d\x33\xf4\x20\xea\xc5\xdd\x63\x0c\x37\x21\x79\xd2\x7e\xbf\x63\x3b\xe1\xd4\xbd\x65\xff\x0b\x7e\x87\x4c\x41\x5e\x1a\xc8\x56\x4d\xdb\xd0\xf4\x48\xcc\x1f\x7d\x7d\xe0\xa5\xa5\x75\x13\x05\xa0\xc0\xbe\xc9\xf6\x1a\xa7\xc9\xca\x9c\x21\x99\x84\x0b\x0e\xda\xe4\x4b\x61\x8b\xb6\x32\xac\x96\x5c\xa8\x32\x1b\x7b\xb6\x95\x3a\xb6\x8c\xa0\xa4\x96\x69\xe6\xec\x00\xa0\x6e\xa9\x00\x3e\xd7\x94\x00\x16\x17\x21\x1c\x48\x87\xd0\xb5\xdf\x65\x3c\x54\x44\xfc\x90\xe4\xf4\x0d\x92\xd3\x2b\x4e\x1e\xb7\x60\xbd\x72\xc5\x06\x9d\x3a\x54\x8e\x83\x2b\x0c\xcb\xbc\xe0\x18\x0c\x43\x78\xc3\xdc\xba\xcc\x77\x23\xbc\xbd\xa2\xc4\x11\xd6\x00\x39\x46\x00\x60\x0f\x63\x21\x2c\x55\x15\x38\x75\x85\x25\x5e\x53\xd2\x21\x68\xd8\x15\x0c\xe1\x6b\x16\x15\x0f\x94\xd0\x3d\x7b\xd8\x2b\xbd\xbd\x19\xf5\xaa\x99\xff\xad\x5c\xf4\x9d\x41\x9f\xcb\xcf\x00\x6a\xde\x34\x3d\xbf\x9d\xbb\x63\x71\x0b\xf6\x5e\x82\xa6\xe7\x96\xce\xe2\xd6\xe2\xe3\x08\x53\x02\x3d\x46\x95\x40\x1f\xc6\xd5\x96\x43\x32\x52\x5b\xed\x7e\xd1\xef\x69\x81\xba\x06\xcf\x2e\x39\x94\xe3\xa5\xcb\x18\xb5\x38\x2a\x19\x52\xe8\xb0\xf0\x54\xcb\x0b\x12\x22\xca\xc9\xa6\x4f\x38\xe7\xd6\xb6\x47\x65\xc3\xc6\x47\xc5\xa7\x56\x0a\x1e\xd3\x61\x85\xfa\x7c\xbf\xf8\x58\xf6\xec\x44\xb4\xce\x70\x6f\x1d\xd6\x75\x61\x60\xe9\x73\x80\xa5\xaf\x08\x2c\xbd\x82\x8a\x66\xa2\x56\xda\x74\xb6\x65\x9f\xc3\xfe\x20\xa8\xe5\xe5\x09\xcd\x00\x27\x17\xf9\x54\x29\xa8\x6e\x32\x95\xb2\x75\x15\xb2\xe0\x13\xd4\x70\x0e\xed\x8e\x0a\xde\xc7\x0e\x64\xaa\x36\x0e\xe7\x23\xbb\xdf\xe2\x66\x21\x6f\xcc\x70\x80\x1f\xea\xc3\x3b\x49\x09\x60\x71\x92\x20\x58\xe3\x91\xb8\x6a\x47\xcc\x78\x02\xbf\x8a\x19\xa5\x70\x30\x0f\x2c\x8c\x8b\xe0\x2e\xd8\x13\x79\x0a\x70\x97\xcb\x62\x3a\x08\x69\xcd\x1b\xa0\xb5\x3c\x84\xd3\x46\x3b\x41\xa5\xb0\x15\x39\xc6\x89\x35\xbe\x46\x5c\x27\x9a\xc3\x45\x2e\x8c\xf1\x2d\xde\x3a\xa7\x29\xec\x9d\x4f\xa1\x2b\x98\x48\xaf\x90\x59\x25\xc5\xe4\x2d\xbc\x93\x67\xdf\x96\x17\xc5\xa8\x91\xb4\xe8\x5d\x76\x4d\x33\x67\x57\x17\x6b\x4b\xd3\xf5\xb1\x64\x7d\x82\x17\xe6\xf5\x78\x3e\x90\xad\x72\xdf\x85\x0f\x69\xbf\x0d\x2d\xef\xaf\x1a\x8c\x32\x18\x58\x6c\x8d\x64\xc3\xbc\xdb\xe1\x76\xa6\x75\x84\x31\x5d\x74\x64\x10\x90\xcd\x20\x67\xf0\x3e\xa0\x1a\xe6\x08\xa4\x44\x30\x95\x7b\xae\x4d\x58\x1a\xa7\x17\x3b\x0e\x0c\x6a\x78\x83\x93\x4d\x80\xe5\xf1\x03\xcc\xd0\x2e\xf3\xc1\x5e\xac\xab\xa1\x9b\x85\xf5\xdb\xbe\xb6\x67\x7c\x8c\x0c\x0e\xbd\x9c\x65\xb1\xba\xbf\xf0\xf1\x2c\x8f\x8b\x80\x52\x70\x71\x1f\xd3\x48\xd5\x65\x21\x51\x0c\x43\x7d\xe7\x03\x22\x61\x70\xa5\x93\x08\x14\x37\xf9\xe1\x53\xf5\xc1\xcd\xaa\x11\x0e\xee\x30\xd9\xc2\x3b\x53\x23\xc7\x51\x0d\x41\x19\xa8\xf4\x84\xb0\xd9\xb4\x54\x5c\x5c\xc3\x7a\x24\x58\x6d\x66\x33\x76\x57\x5d\x07\x63\xdb\x4e\xe2\xdd\x6b\x55\x0d\xed\xee\x65\x33\x40\xdf\x7a\x25\x17\x23\x78\xfa\xc1\xc5\xff\x2f\x6f\x4e\x86\x1d\xf0\x2f\x4f\x1e\x68\xff\x9f\xf2\xf2\xe4\x50\xa3\xdd\xc5\x5d\x99\x30\xaf\x3b\x1e\x3e\x3f\x71\xc0\xb6\x8e\x9f\xe6\x9b\xc1\x53\x29\x66\x91\xd1\x85\x61\xc4\x2a\x51\x22\x64\x81\x48\x88\x4d\x27\x90\xe4\xd5\xed\xa6\x69\x17\x6d\x19\xb8\xdf\xb0\xbd\xc0\xb9\x2c\x6a\x4d\x4a\x8c\xe3\xda\xc7\x5d\x90\x94\xf1\x2d\x9d\xa4\xab\xa2\x27\xd5\x98\xc1\xa5\x6a\xed\x66\xd0\xf6\xe8\xd5\x98\xa5\x0d\x83\xd2\x42\x89\xa7\xcc\x6a\xd0\xe5\x01\xbb\x8a\xba\x2d\xa5\x24\xec\x85\x39\xae\x29\x47\xd4\xac\xa0\xf7\x92\x12\x46\x7c\x94\x14\x79\xd7\x0d\xcf\x15\xcb\x97\xa6\x8f\x0d\x5d\x82\x4e\x6a\x35\x83\xce\x05\xb5\x02\x6a\x9a\xe3\x06\xbd\x19\x5a\x11\x4b\x2a\x3c\xb8\xd8\xe4\xb9\xeb\x35\x65\xa7\x4f\xda\x73\x1c\x47\x49\x81\xf2\xa4\x80\x2d\x22\xeb\x4a\x4e\xdf\x86\xe9\xc1\x53\x6f\xc8\x75\x8f\xbc\x4d\xc0\x04\x66\x28\x79\xcb\x61\x24\x7f\xd0\xa8\x3a\xc1\x93\x6f\xec\x6d\x25\x0f\xc8\xec\x36\xdc\x5f\x0e\x29\x8e\x2b\x0c\xd6\x02\xb3\xcc\x90\xe3\x95\x27\x5c\xe8\x12\x9b\x59\x89\x59\xab\xbc\x4a\xa2\xc8\x64\xf9\x40\x63\x59\x41\x2e\xd0\xe0\xbc\xcf\xd9\x08\x2b\x00\x29\xec\x85\x6c\x3f\xa2\xbc\xef\xdb\x6a\xbe\xe7\x7b\x51\xb5\xff\xe0\x45\x29\xc6\x26\x2e\x6f\x04\xdb\xed\xcd\x3f\xe3\x52\xbf\x0e\xc3\x0e\x5e\xba\x1f\xc0\x49\x3c\x2d\xeb\x96\x44\xd3\xb2\x2a\x70\xad\xe0\x00\xe4\xb2\x31\x82\xd8\xf2\x8e\x93\x75\x39\x2f\x4f\xe2\xad\x45\x7a\x79\xac\x39\xdd\xb6\xdf\x59\x40\xf8\xcb\xb3\xab\x8b\x5b\x68\x89\x41\x95\x26\x00\x19\x10\x06\x67\x29\x71\x20\x2b\xa0\x10\x35\xba\x51\x4b\x75\x3d\xd6\xc3\x6c\x47\x88\xad\x9b\x86\xbb\x6d\xdb\x97\x47\x7a\xf8\xbd\x75\x7e\x1a\x80\x0d\xcb\xa5\xcc\x2c\x3d\xdb\x6f\xfa\x8a\xad\xc0\xac\x35\xb5\x44\xe2\xe7\xe8\xcb\x5d\xde\x5a\x54\x53\xc4\xf0\x49\x1f\x1c\x3d\x98\x45\xab\x2f\xeb\xe5\x5d\x3f\x79\x62\xf1\xea\xcd\x25\x7d\x2e\xda\x1b\xb9\x08\xd5\x91\x7e\xac\x76\x0c\xa6\x2f\x33\xf3\x80\x29\x05\xb0\x7f\x41\x8a\x2d\x15\xbe\x31\x2b\xdb\x4f\xd5\xc2\x11\xcc\xc5\xf1\x19\xf4\x0e\x94\x14\x2e\x3e\x6d\x1a\x51\x87\x4c\xf2\xf3\x9d\xa0\xe9\x68\x22\xc9\xcf\x18\x08\x3f\x49\xcc\xc6\x9a\x3b\x43\x60\x18\x1e\x65\x28\x9e\xe9\xbb\x95\x8a\xe9\x91\xa8\x12\x43\x07\x12\x8b\x67\x6d\x43\x89\x32\x2e\x72\x97\x8d\xfb\x2c\x62\x66\xe1\xce\x35\x78\xc0\xf8\xcb\x6c\x7f\xc2\xca\x02\x29\xe3\xb6\x41\x4f\x46\x6a\x88\x4b\x44\x90\x99\xf0\x57\x7b\xe3\x25\xae\xda\xbf\xe9\x33\x2a\x11\xbd\xf6\x32\xc2\xeb\x84\x4d\xcd\x2d\x76\x34\x41\xed\x83\xfd\x3e\xae\xf8\xae\x6d\x5f\x74\x71\xa6\x03\x73\xf7\x50\xaa\x03\x8b\xaf\xa3\x14\x36\xdf\xed\xdc\xb6\x11\x3c\xe3\x03\xb2\x0d\x40\x3e\x09\xc3\x09\x20\x68\x11\x74\x83\x7a\xc4\xf9\x2c\x04\x62\x1f\x34\x05\x18\x6e\x3d\x9a\xdc\x2c\x97\x1c\x5f\x8b\xa3\x44\x6a\x70\x14\x84\xdb\x3a\x63\x6b\x6e\x2b\x29\x2e\x95\x19\x2b\xe5\xa0\xe4\xe2\x31\xd9\x0b\xb0\xef\x90\xc8\xa7\x0a\x03\x6f\xf7\xee\xe9\xec\x77\xfb\x5a\xce\x4b\x41\x96\x36\xc4\x59\x61\x23\x90\x5e\xda\xa6\xe9\xed\x99\x86\x40\x74\x79\x47\xc9\xb4\xa7\xf5\x6b\x87\x60\xbe\xe9\x5a\x64\x12\x76\x3e\x28\x83\x7b\xb6\x05\x6c\xf2\xc7\x85\xa8\xdf\xe3\x12\xd4\xef\x03\xe0\x62\x98\x61\x1b\xff\x25\x7e\x09\x93\x76\x3d\x66\x33\x4f\xa5\x46\x1b\xb0\xa4\x0d\xe9\x26\xc4\x41\x31\xf7\x84\x71\x6a\x77\x73\x93\xa4\x41\x90\xa1\xf4\xe2\x53\x43\x79\xc1\xa7\x86\xb2\x8f\x4f\xd5\x8e\x0d\x7a\xd0\x75\x1b\x9b\x88\xcb\xcb\x37\xf1\x6c\xfb\xdc\xe0\xf5\x1d\xb6\x17\xbb\xc7\xe1\x62\x39\x6a\xdd\xbd\x94\x3d\x0d\x1f\x05\x25\x14\x9b\x21\xfe\x34\x75\x58\x47\xf7\xfb\xa6\xea\xcb\xef\xef\xe1\xea\xfc\x5e\x5f\x15\xf3\x7b\x8f\xc2\x75\x53\xc1\xb1\x20\x58\x38\xd5\xe2\x00\x7a\xdc\xe1\x3d\x7a\x20\x34\x15\x2f\xed\xaa\x2d\x6d\x7f\x3f\x09\xde\xec\x1c\x91\xb4\xdf\x06\x1c\x41\x87\x5b\x80\x75\x8c\xbd\x54\xda\x20\x23\x23\x79\xa1\x97\x27\x0d\xd9\x40\xf3\x9d\x55\xf3\x1c\xc9\xbe\x87\x12\xea\xd6\x42\xdf\xaa\xbd\xbe\xf5\x0f\x91\x6b\x5f\xd7\xf0\x23\xb1\x22\xf6\xec\x32\xf4\x6c\xa3\x17\x9a\xa1\x60\xd3\x07\x9a\xb5\x00\xc6\xee\xd4\x17\x67\x3c\xe0\x50\x63\x31\x1c\x30\x1c\xa8\xaa\xbf\x97\x12\xad\x30\x18\xf6\x19\x1d\x86\xb7\xfb\x2d\x9e\x67\xbc\xe4\xe0\x6a\x78\x60\x73\xd4\xad\x1d\x49\xfa\x79\xd8\x23\x24\x38\x26\x24\xbe\x91\xec\x18\x92\x6d\xf4\x86\x4b\xfc\x27\xe1\x1e\x92\xbe\xc1\x95\x96\xc3\x0e\x6d\x42\x5e\x2c\x8d\x0a\xbd\xe3\x3c\x27\xc6\x4e\x14\x36\xb7\x6a\x47\x2a\xe6\xb6\x38\x49\x2a\xbf\xef\xcb\x3d\x55\x5e\xd6\x2b\x90\xe9\x9f\xf9\x27\x89\x3b\xfc\xd3\x21\x48\xfc\x11\xa1\xf0\x62\x8f\x86\xa7\xe6\xa1\x08\xbd\x17\xa5\x38\x5a\xb8\x53\x2e\x09\x66\x26\xdc\x05\xce\xf0\x7b\xba\x83\x0a\x3b\x3e\x9a\xc4\xf9\x36\x8b\xb4\xa6\x9a\x60\xee\x5e\xfd\xfc\xe6\x7c\x00\x39\xc1\x0c\x34\x67\x82\x79\x68\xce\x04\xab\x90\xdb\x5a\x37\x04\xdc\xd0\x4e\x8f\x40\x20\x0f\x0e\x40\x08\xda\x5b\x66\x80\x92\x27\x2b\x52\xd2\x2f\x88\xb2\xc4\xdb\x46\x88\x5e\x7e\xc7\x40\xc1\x5b\x50\x02\x65\x4f\x41\x8d\x5a\xad\xc3\x36\x6b\xb9\x69\xf4\x5c\x47\x4c\x84\x02\xae\x23\x26\xf6\x93\xdd\x33\x68\xf6\x2a\xaa\x0a\x7d\x3a\x4b\xe0\x2f\x34\xc9\x40\x0d\xc4\xd7\x6c\x10\x5a\xb5\xeb\x26\x11\x6e\xe5\xa4\xd7\x13\xfc\x8a\xa6\x4e\x97\x1f\xaf\x17\x81\xf5\x2b\x90\x5f\x98\x96\x12\x06\xbc\x5a\x38\xc4\x98\xce\xf8\xe5\x89\x7f\x25\x0b\xea\xe5\xc1\x60\x36\xd5\xb2\x74\xca\x68\x1d\xcd\x1b\x4a\x8b\x80\xd7\x7d\xbf\xeb\xcc\xc7\x1c\x6f\x3a\xa5\xe7\xf4\x63\x30\x88\xb0\x2a\x1d\xc9\xa8\xa6\x5d\x85\x0b\x82\x00\x2f\x92\x30\x8d\x71\x83\xd6\xdd\x21\x00\xd7\xed\x61\xc8\xe3\x56\xad\x63\x9c\xb6\x42\xfc\xe3\xf4\x5e\x16\x70\xad\xb3\x0c\x30\xd9\x32\x43\xe9\x2e\xc9\x30\xd8\x25\xcd\x5a\x68\xb6\x68\x25\xde\x1d\xff\xb9\x62\x53\x0d\x97\x13\xae\x3d\x4b\xeb\x88\xf6\x8a\xbd\x78\xe9\xe9\xa7\x87\xf7\x0f\x01\x08\x9a\x2c\x63\xe2\xf1\x80\x18\xa0\xfc\x5c\x2e\xf6\xc1\xcd\xe1\xcf\xf2\x5b\x55\xf5\xbe\x9a\xc6\x8c\x83\xf7\x35\x1e\x8e\xb8\x90\x94\x00\x66\x2a\xe8\xa5\x75\x1d\x26\xce\x66\xea\x7c\xb0\x7d\xd7\x3c\x0e\xb3\x0c\x65\x16\x57\x66\xc8\x24\x3f\xb3\x8d\x38\x5a\x0d\x8c\xb0\x0c\x36\x94\x78\xc2\x34\x04\xce\x0a\x0c\xde\x2c\x6f\xa2\xe3\x96\xd5\xec\x98\x65\xed\x66\x01\x2c\x8e\x0f\xc1\xa3\xb2\xd2\x07\xc9\xbf\xcb\xc4\x32\x79\x2f\x66\x4b\x1f\x06\x4f\xd9\x99\x76\x3f\x30\xa3\x8b\xfc\xd1\xef\xcb\xf3\x11\x6d\x59\x07\xb1\xf2\xe4\x57\x31\x7a\x7b\xca\x02\x3b\x7f\xeb\x03\x3b\x47\x0f\x5a\x0f\x1f\xa5\x70\x8f\x04\xa0\x56\xb6\x4b\x94\x07\x48\x82\x1e\x3c\xe9\xda\xc5\x93\xfb\x61\xfc\x7f\xd6\x97\xc6\xd1\xb3\xa3\x2a\x65\x74\xf6\x90\xc2\x6f\xfa\x78\x81\xfc\x0e\xeb\x15\xa5\x9e\x54\xcd\xc1\xb6\xdd\xeb\x02\x5a\xc3\x30\xd6\xb7\x21\x2a\x8a\x91\x1b\x56\xa8\x21\xbc\xc7\xf5\x0d\xde\x76\x08\xde\xaf\x68\xea\xaf\xe9\xd8\x64\xb0\xe0\xdf\x34\x72\xf4\x57\x77\xcb\x05\xf3\x52\xec\xdb\xef\x01\x2d\xc8\x8c\x4e\x4f\xa7\x27\x0f\x44\xa7\x90\x67\xbf\x6c\x16\xe9\xc7\xed\xd3\x18\x53\xc6\x70\x1a\x35\x6c\xfc\x77\x41\x8c\x6f\x78\x2c\x4b\x46\xd5\x69\x7c\x56\x89\xac\xfe\x9d\x7b\x36\x2b\x79\xcf\xa1\x96\x3f\x24\xf9\x8a\xc7\x44\xff\x27\x78\xd6\x4e\x7c\x14\x40\xa3\xf4\x99\xc8\x4f\xfe\xfa\x96\x2b\xfe\x36\xed\x4a\x62\x9a\x08\x12\xfc\xed\x16\x09\x5b\x3a\x5a\x13\x2b\xe2\x84\x35\x12\xf8\x75\x46\xfc\x2c\xf0\xb3\xc8\x6f\xf0\xeb\x1a\xbf\xae\xcb\xf2\xa3\x14\x06\x57\xa5\xe2\x74\x38\x5f\x23\xe5\x06\xbf\x6f\x38\x98\xee\x7d\xf6\xcf\xe2\x76\xf4\xa5\x04\xfb\x81\xd0\xc4\xdc\x9c\xa6\xdb\x0f\x4a\x97\x77\xfc\x9f\xfa\x27\xfd\xef\xf3\xb5\xff\x8d\x26\xe1\x8b\x52\xb8\x79\x4d\x92\xcf\xfb\x60\x8d\xfd\xda\x2a\x94\x6f\x4a\xe5\x7e\x68\xa2\x7c\x52\x5a\x9b\x5f\x67\xbe\x5f\xfa\x85\x54\xdf\x2b\xfd\x22\xf4\x16\x6d\xb3\xe3\x30\xa5\x1f\xdc\x93\x97\xfe\xd5\xb1\x53\xca\xd3\x50\x4c\x88\x4d\xc9\x2e\xc5\x1b\x7e\x3a\x0f\x2f\xfe\xb2\x4f\xe9\x2c\xb1\xf0\xc1\x55\xbd\xdb\xbb\xf3\xa9\x8f\x5d\x2d\x60\x3e\x9e\x93\x18\xaa\xf3\x53\x42\xf2\xce\x1a\xcd\x6e\x36\xaf\xf4\x79\xb9\x32\xe5\xc3\xc0\xc3\x7f\xfb\x37\x80\xd3\xe7\xbf\xff\x7b\x7a\xf6\xfc\x51\x5a\x7e\xe6\xe8\x74\x5d\xba\xcd\x3f\xe3\x54\xa0\x50\xf4\xf3\x45\x04\xc8\xfe\xaf\x30\x39\xd6\x6b\x28\x7d\xf4\x1b\x77\x4f\xff\x2f\x00\x00\xff\xff\xc8\xc1\x82\x2c\x9f\xae\x00\x00") +var _confLocaleLocale_enUsIni = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x7d\xeb\x72\xdc\x48\xae\xe6\x7f\x3e\x05\xdb\x27\x1c\xb6\x23\xe4\x72\x74\xf7\xde\xa2\xa3\xed\x5e\x59\x6a\x5f\xe6\x58\x96\xc6\x52\xcf\xec\xac\xc3\xc1\x66\x15\xa9\x2a\x8e\xab\xc8\x6a\x92\xe5\xb2\xfa\xc4\x89\xd8\x07\xd8\x07\xd8\x7d\xbd\xf3\x24\x0b\x7c\x00\xf2\x42\xb2\x24\xbb\x67\x62\xfd\xc3\x62\x65\x22\x6f\x48\x24\x12\x89\x04\x90\xf9\x76\x9b\x15\x65\xb7\x48\x9f\xa6\xc7\xe9\x36\xaf\xea\x75\xd9\x75\x69\x57\xae\xaf\x1f\xaf\x9a\xae\x2f\x8b\xf4\x65\xd5\xd3\xef\xf6\x53\xb5\x28\x93\x64\xd5\x6c\x4a\x02\x7d\x45\x7f\x92\x22\xef\x56\xf3\x26\x6f\x0b\x4a\x38\xb5\xef\xa4\xfc\xbc\x5d\x37\x2d\x03\xfd\x2c\x5f\xc9\xaa\x5c\x6f\xb9\x0c\xfd\x49\xba\x6a\x59\x67\x55\x4d\x3f\x2f\xe9\x2b\x7d\x5d\x4b\x4a\xb3\xeb\x2d\xe9\x7c\xd7\x4b\xda\x6e\x6b\x49\xbf\x6c\x93\xb6\x5c\x56\xd4\x9b\x96\x92\xde\xe9\x67\xb2\x2f\xe7\x5d\xd5\x73\x4b\x7f\x95\xaf\xe4\x53\xd9\x76\x55\xc3\xb5\xff\x45\xbe\x92\x6d\xbe\x64\x80\x0b\xfa\x93\xf4\xe5\x66\xbb\xce\x51\xe0\x4a\x3f\x93\x75\x5e\x2f\x77\x02\xf3\x46\x3f\x93\x45\x5b\x52\x56\x56\x97\x7b\x4a\x3d\xc1\x8f\xd9\x6c\x96\xec\x08\x09\xd9\xb6\x6d\xae\xab\x75\x99\xe5\x75\x91\x6d\x64\x98\xbf\x50\x7a\xaa\xe9\x29\xa5\xa7\x9c\x8e\x21\x94\x05\x0d\x35\xcb\x3b\x1d\x07\xe1\x92\x46\x9e\x77\x09\xaa\xaa\xf3\x8d\x95\xe6\xcf\xa4\xdc\xe4\xd5\x9a\xb1\xf6\x98\x3f\xa8\xe3\x5d\xb7\x6f\x80\xdb\x0b\xfd\x24\x24\x64\xfd\xcd\xb6\x04\x0e\x1e\x5f\xd1\x57\xb2\xc8\xb7\xfd\x62\x95\x73\x3f\xe5\x2b\x21\xa0\x6d\x43\xc8\x68\xda\x1b\xc0\xd9\x8f\xa4\x69\x97\x79\x5d\xfd\x9e\xf7\x82\xa0\xf3\xe0\x67\xb2\xa9\xda\xb6\x61\xdc\x9e\xe1\x23\xa1\xa1\x67\x5c\x0f\xa5\xbc\x25\x2c\x04\xb5\x70\xce\xa6\x5a\xb6\x82\x46\xce\x3c\xc3\x2f\xae\x85\xf3\xae\x9b\xf6\xa3\x66\xbc\xe0\xcf\x41\x51\xea\x84\xe6\xc6\xed\xe7\x35\x21\x5e\x73\xcf\xf0\x23\x02\xe8\x92\xbc\xd8\x10\x2a\xb7\x79\x5d\x32\x8e\x8e\xf9\x17\xe1\x85\x7e\x25\xf9\x62\xd1\xec\xea\x3e\xeb\xca\xbe\xaf\xea\x25\x23\xfb\x58\x92\xd2\x4b\x4d\x4a\x82\x3c\x97\x76\xd3\xec\xdc\x74\x52\xfa\xdf\xe8\x67\x7a\x21\x3f\x25\x2f\x28\x84\x4c\x57\x92\x47\xd2\x65\xd7\x65\x59\xc8\x58\xba\xf4\x05\x7d\x27\xdb\xdd\x7a\x4d\x58\xfb\x6d\x57\x76\x3d\x17\xba\xa0\xdf\x34\x7e\xf9\x9d\x54\x5d\x47\x1f\x94\xfc\x1a\x1f\x09\x4d\x5d\xbd\xc0\x60\x4e\xf0\x91\x24\xef\xbb\x32\x6f\x17\xab\x0f\x89\xfc\x45\x5f\xf9\x83\x69\xef\xd0\xa4\x32\x21\x29\x11\x49\x0b\xd6\x40\xb2\x68\x0a\xfe\x71\x42\x7f\xa8\xea\xaa\xee\xfa\x7c\xbd\xfe\x90\xe8\x07\x83\xc9\x97\x4c\x40\x5f\xf5\xc0\x82\x26\xa6\x97\x7d\xb9\xed\x78\x06\xd3\x17\x55\xdb\xf5\x8f\xfb\x8a\x88\xf5\xdd\xae\x4e\x8a\x66\xf1\x91\x96\x01\x2f\x69\xb4\xfc\xfa\x3a\x25\x64\x3d\xa0\x85\xd0\xee\xea\x9a\xd0\x93\xbe\x6c\x08\x65\xd4\x4c\x45\xed\x9f\x02\xfa\x28\xdd\xae\xcb\xbc\x23\x90\x32\x2f\xd2\x1f\xf3\xb4\xcf\xdb\x65\xd9\x3f\xbd\x97\xcd\x69\xf9\x7d\xbc\x97\xae\xda\xf2\xfa\xe9\xbd\xfb\xdd\xbd\x67\x2f\x77\x54\x6c\x5d\xd5\x65\xf7\xe3\x93\xfc\x59\xba\xc8\x29\x87\xd0\x78\x93\xce\xcb\x6b\x5e\x6d\xd4\x56\x4a\x54\x5e\x2f\x79\xa5\xdd\xf4\x2b\x6e\x90\x28\x81\x3e\xba\x94\x97\xfa\x37\x09\x4f\x00\xb1\x82\xac\x98\x1b\x5b\x43\x87\x90\xdc\xd2\x04\x9c\xdd\x5c\xfe\xf9\xcd\x51\x7a\x41\xbc\x6d\xd9\x96\xf8\xa6\xff\xa8\xc4\xf7\x29\x8d\xf6\xaa\x3a\x7d\x3e\x4b\xa8\xac\x21\xe4\x34\xef\xf3\x39\xf7\xdd\xcd\x3e\x67\xca\x22\x74\x79\x58\x8a\xcc\x2d\xc1\x19\xbb\x3e\x9a\x96\xa9\x85\x4c\x75\xe8\xf2\x77\x75\xbc\x65\x1e\x40\xe9\x0e\xb3\x17\x82\x33\xaa\x2a\x7d\xfd\xf6\xed\xf9\xe9\xf3\xb4\xac\x97\x84\x99\x74\x5f\xf5\xab\x74\xd7\x5f\xff\xb7\x6c\x59\xd6\x65\x9b\xaf\xb3\x45\xc5\x48\x69\x89\x60\x53\xc2\x92\x0c\x71\x96\x74\xdd\x9a\x58\x14\xa8\xe0\xf2\xf2\x4d\x7a\xc6\x94\xb0\xcd\xfb\x15\x3a\xd2\xaf\x92\xee\xb7\x35\x23\xca\x35\x78\xb5\x2a\x53\x2c\x06\x00\x35\xd7\x43\xbc\xa4\x85\xf6\x75\x96\x94\x6d\x9b\x11\x07\xed\x6f\x18\xcd\x5a\xe7\x21\x68\xa9\x8e\xa8\xbd\x6e\x7a\x9a\xc6\x14\xe5\xa4\x8a\xaa\xfe\x94\xaf\xab\x82\x90\xed\x11\x12\x97\x45\x62\xd1\xd0\xbc\x71\x69\xa2\xcc\x66\x8f\xa1\xe6\x0b\xda\x00\xba\xf4\xde\xec\x1e\x38\xee\xbd\xc7\xf7\x66\x49\xdd\x64\xc2\x25\x98\x37\x17\x55\x97\xcf\x89\x4f\xcb\xbe\xd1\x1a\xd7\xfb\x1b\xd3\x8f\x74\x45\x21\xd2\x08\x82\x71\xcb\x7b\x11\xb6\x00\x26\xae\x9c\x18\x36\x98\x8d\xb2\x99\x70\xec\xc6\x93\xdc\xfc\x0a\x5b\x72\x09\xa3\x31\x27\x36\x61\x46\x5d\xc7\xdb\xed\xba\x5a\x48\xd3\x2f\x25\xcf\x13\x1a\xef\xcc\x8a\x94\x10\x0e\x84\x62\x79\x01\xb9\x50\xaf\x99\x6d\xa5\x11\x9f\x47\xf9\x55\x49\x2b\x67\xb5\x5b\xca\xee\xb4\x6e\x76\xc5\x37\x60\x28\x36\x73\x9e\x9f\xa4\xef\x1a\xea\x30\xa8\xc3\x01\xf8\x26\x8e\x89\x31\xb0\x30\xd0\x96\x9b\xa6\x67\xc4\x69\xb1\x8a\xa6\x67\x5f\x51\x26\x8d\xb4\xcb\x3f\x11\x5b\xec\x1b\x59\x92\x05\x2d\xb9\x05\x57\x4c\x1c\x6c\x47\x3b\xba\x2c\x0b\xe2\x23\xb2\x34\x2c\x2d\xa6\x41\x40\x6d\x76\xb4\x9a\x56\x54\x19\x23\x9e\x25\x12\xaa\x72\xaa\x9f\x18\x12\xd5\x83\x55\x4e\x2b\xb7\xa1\xcd\x93\x27\xfa\x14\x1f\xfa\x3b\xac\x9f\x7a\x95\x5f\x5f\x53\xaf\x3a\x5a\x15\xaf\xd2\xc5\xba\xa1\x25\xf5\xcb\xbb\x37\x1d\x2f\x98\x55\xb6\x6d\x5a\x48\x22\x94\x75\x41\x9f\x2e\x2d\x40\x34\x43\xd4\xbb\xcd\x9c\x7e\xed\x57\x15\x31\x6a\xa0\x9d\x4b\xb0\x94\x44\xa9\xd4\xc4\xae\xa3\x29\x3c\x4a\x69\x09\xd3\x08\x08\x65\x20\x00\x1e\x83\x51\x1d\x83\x5f\x13\x8d\xed\x5a\x5a\x4e\xab\xbe\xdf\x5a\xcb\xaf\xae\xae\x2e\xa4\x69\x97\x7a\x5b\xdb\x79\x40\x19\x98\x83\x35\xcb\x46\x75\xda\xd4\x33\x10\xc9\xae\x5d\x0f\xe8\x87\xc6\x6a\x39\x07\xf0\xc2\x5d\x78\xc2\xff\x5d\x7a\xf4\x00\xcf\x1d\x49\x7d\x7b\x50\x13\xe1\xb8\x84\x9c\x42\x44\xdd\x6c\xb9\xde\x80\xaa\xcf\x35\xc1\x93\x32\x64\x1b\x97\x2f\x12\x0e\xe5\x42\xa6\x0c\x76\xe9\x0d\x0d\x58\xd9\xe8\xe5\x19\xa1\x01\xbc\x14\xa9\xd7\x6d\xb3\xa1\xd4\x17\xf4\xc7\x27\xf8\xee\x9f\x71\x7d\x80\xc9\x8b\x82\xb8\x7c\x77\x94\xbe\x7b\x71\x92\xfe\xe7\xef\xbf\xfb\x6e\x96\xbe\xee\x79\x25\x32\x71\xfe\x9d\x89\x8a\x3e\x45\xd4\x72\xa0\xc4\xb1\x7a\xa2\xbb\x7b\xbc\xb2\xee\xa5\x3f\x22\xf7\xbf\x97\x9f\x73\x12\x11\xcb\xd9\xa2\xd9\x3c\x63\xae\xba\xc9\x69\xed\x73\x0e\x91\xab\xd2\xf1\x65\x59\x17\xf4\xa1\x02\x9b\xe6\x05\xec\x40\xf3\x03\xf1\x4d\x04\xd7\x6c\xd1\xd4\xd7\x55\xcb\x03\xfa\xb9\x06\x35\x98\x48\x4b\xdb\x35\x72\x4c\x2a\x22\xa4\x11\x07\xa9\xae\x6f\x3c\x28\x86\xfa\x96\x13\x75\x42\x13\xa1\xba\x4c\x45\x74\x87\xe5\x4b\x21\x46\x9e\xb7\x73\x1a\x5e\x6b\xf8\xee\x3c\xc2\x9b\xeb\x6b\xde\x6b\x6d\x97\xd0\x16\xce\x25\x55\x36\x8c\x10\x84\x88\x71\x0b\xa1\xfc\x54\x89\xf8\xe4\xf4\x6d\x5a\x7e\x22\x6a\x63\xae\xd7\x36\xc5\x6e\x01\x0a\x63\xd8\x23\x66\xd6\xc4\x22\x3a\x5a\x1b\x0b\xd9\x57\x02\x26\xc1\x5d\x63\x4e\xb4\x20\x20\xe2\x0d\xc6\xac\x49\x90\xfc\x44\x9c\xbf\x0d\x9a\x78\x69\x49\xda\xfb\x11\xec\xa8\x53\xae\x04\x8f\x7c\x41\x33\x4e\x54\x21\xbd\xe8\xa4\x53\x92\x4d\xe4\x4e\x74\xbc\xa3\x13\x4a\x5e\x50\x5f\xe6\x37\xe0\x3b\x1d\x13\x43\x51\x5e\xe7\xbb\x75\xef\xfb\x35\xd8\x44\xac\xa5\x4b\x3e\x24\x85\x79\x93\x05\x46\x1d\x04\xf5\x74\xc3\xb2\x44\x86\x35\xc9\x39\xb2\xd9\x30\xbd\xca\x29\xc4\xf6\x1d\x62\x4f\x25\xa6\x27\xf3\x22\xbf\xce\x97\x49\xfe\x71\xbe\x6b\xf6\x9d\x48\x3e\x29\xb6\x5a\xae\xd1\x2a\x60\x51\x61\xba\x2f\xb3\x44\xc5\xa5\x4c\x8f\x6b\xd9\xa7\x0a\x87\x21\x47\xae\x52\xa5\x1e\xe1\x98\xaf\xfd\x85\x01\xf8\x94\xd5\x4d\x96\x75\xbd\x39\xe7\x41\x76\xee\x30\x24\x38\xe7\xe1\xa2\x05\x16\xe1\x68\x96\x3e\x55\x60\xf3\x4a\x30\xc0\x0b\x51\x0d\x9a\xa6\xa6\xba\xb2\x44\x0d\x54\xfe\x09\xd5\x89\x32\x33\x3d\x20\xa8\xcc\x6e\xa2\x1f\x6f\xf7\x45\x03\xd9\x01\x7b\x09\x95\x36\xb4\x0e\xf6\xf5\xb4\xad\x96\x2b\xe2\xad\xcd\xfe\x48\x90\xb2\x5f\x35\x25\xaf\x9f\xd7\xa7\x4f\xbf\x95\x7e\x2c\x79\x67\x71\x85\x78\x4f\xca\x77\x44\x5c\x84\x31\x25\x63\xe9\x82\xdb\xdb\x01\x39\x3a\x8a\x08\xd0\xf0\xf0\x37\x12\x25\x1c\xd3\x50\x5e\x11\xe6\x29\x93\xf0\x30\x52\xda\x0e\x90\xd2\xb0\x72\x25\x95\xf7\xb3\x65\x83\x83\x8c\xc9\xf7\xbc\x5b\xd2\x79\xb8\xeb\xb3\x65\xd5\x67\xd7\xcc\xba\xb8\xe6\x17\x5c\x03\x6f\xde\x94\x93\x3e\xa0\xac\x07\x29\xf1\x3f\x3a\x9d\x15\x3f\xa4\xf7\x3f\xa9\xc4\xf8\x3d\xf3\xa4\x8c\x56\x51\xb5\xc6\x94\xe8\xf1\xa8\x2d\x45\x60\xb5\x33\xb8\x93\xda\xba\xdd\x16\x7b\x9b\x0a\x88\xee\x34\x50\x34\xfb\x9a\x57\x1f\x98\x2f\xf1\x99\x6a\x51\xd1\x9e\x31\xaf\xea\x9c\x36\x78\xab\x05\x4c\xfd\x3e\xd1\xc4\xdb\xf3\x2b\x00\x2e\x9b\xf9\xae\x5a\x17\x06\x30\x4b\x4c\x88\x24\x11\x52\x67\x3f\x14\xab\x2d\xa9\x92\xbe\x2c\x9a\x96\x25\x12\x8c\xc6\x0a\x1e\x10\x85\x5a\x16\x31\x90\x5c\xf1\x79\x06\xb0\x28\xe7\xa4\x16\x46\x03\x4d\x3f\x8e\x6a\x2c\xd3\x80\x6e\xaa\xae\x7e\xd0\xa3\xa7\x8b\x1d\xb5\x45\x53\xcf\xc9\x54\xb0\x4b\x1f\x3f\xa3\xff\x13\x96\x90\x64\x07\x58\x8e\x11\xcf\x99\xa9\x64\xee\x64\x2d\x46\x5d\x8d\x88\xdc\x4d\xb5\x91\x70\x30\xd6\xb0\xbf\x46\x02\xdd\x4e\xa8\x96\xd5\x25\x6b\x9a\xd6\xf2\x1b\xfa\xe0\x93\xdb\x72\x8d\x49\xc8\x7b\x3d\x5e\x35\x84\x37\x26\x90\x23\x59\x34\xd7\x34\x34\xe6\xa5\x7d\xfe\xb1\xc4\x89\x8c\xf6\xfc\xf7\xac\x07\xfa\x90\xec\x44\x06\x6d\xd6\x85\x3b\xef\x80\xb2\x9b\x76\xa8\xc6\xf0\x40\x8e\x6a\x3b\x12\xb6\x17\xab\xcc\x69\x91\x18\x29\x7d\xf9\x19\xbb\x3f\xb2\xbc\x52\x89\x49\x9e\xb3\x92\xcd\x0d\xa6\x8b\x07\x71\x76\xe3\x67\x8b\x24\x50\x5a\x28\x74\x98\x9d\x37\x8c\xb5\x4f\xa5\x83\x3a\x09\x53\xe3\x02\x54\x17\xc9\xca\x5a\x55\xac\x6d\xa0\x2c\x51\x89\x68\xae\xa8\x45\xba\x04\xac\x4c\x55\x60\xe0\x78\x34\x9f\x7a\xb2\x9f\xd1\xc4\x40\x6d\x60\x2d\x13\x5f\xbc\x91\x75\x11\xb4\x99\xbc\x57\xf5\xd8\x87\xc4\xe0\xde\xc5\xf9\xc4\x53\x56\x1f\x02\x15\x54\x66\xb3\x6b\xaa\x28\x68\x4f\x94\xad\x78\x91\x62\x55\x6e\x59\xfa\xd8\x74\x20\x8b\x35\x9f\xb4\x6f\x54\x7e\x76\x04\xf2\x93\x30\x6c\xa2\x18\x62\x73\xdf\x24\x5d\xc3\x0b\x2e\xfb\xca\x2a\x9e\x57\x44\x0a\x28\x1f\x6f\x76\xa2\x1b\x23\x31\x97\xa7\x8f\x56\xd9\xcd\x51\x7c\xb2\x5a\xe5\x1d\x31\x71\x92\x15\xb4\x58\x31\xb3\x13\x2e\x4f\x3b\x9d\xe7\xb0\x66\xa0\xcf\x03\x95\x4b\xc9\xa6\x1d\xee\xc2\xdc\x43\xe1\x73\xda\x8a\x93\x9d\x20\x19\x85\x02\xd4\x44\x9b\x84\xb0\x4d\xc9\xe2\x73\xb6\x11\x35\x9a\xfc\x4a\xcf\xca\x84\xb6\xc3\x25\x2d\x68\x23\xd8\xa7\xac\xfd\x58\xe2\x94\xa1\xf4\xca\x00\x65\x1f\x32\x62\x85\xb0\x94\x9f\x4c\x6f\x49\x9c\x61\x0f\xd5\x10\xad\xed\x11\xfa\x69\xcb\xa2\xec\x99\x31\x76\x91\x11\x20\xea\x75\xc4\x2d\x3c\x12\x8f\x53\x56\x40\x86\x50\x2a\x72\xfb\x61\x71\x01\xe6\x1a\x3f\xce\x9f\xdd\xef\x7e\x7c\x32\x7f\xe6\x78\xeb\x62\x55\x2e\x3e\x0a\xfd\x55\xf5\xbc\xf9\x8c\x83\x2d\x14\x25\x74\xa6\xe6\x35\x76\xbf\x48\xe9\xa0\xdb\xe2\x5c\x45\xbc\x80\x8a\x11\xe2\x39\x37\x9a\x34\xea\x0c\xb3\x0c\xda\xda\x16\x58\x55\x20\x70\x4f\x90\xc7\x9c\xca\x24\x89\x0d\xc0\xd3\x24\x06\xb2\xae\x36\x55\x3f\xa2\x09\xe6\x30\xb9\xd2\x96\xea\xca\x0c\x49\xa8\x0b\xc3\xc4\x28\x89\x4f\x53\x35\xb4\xaf\x1a\x9d\xec\x73\x3a\x48\x7d\x9f\x12\x6d\xec\x68\x7f\xe2\xce\xd2\x78\x88\x51\xe7\xbc\x31\xd3\x21\x2a\xef\xb2\x5d\xad\xf8\x2a\x0b\xa3\x92\x57\x15\xb6\x0f\x6e\xd7\x68\x39\x80\x32\x94\xea\x59\x20\x7d\xe8\x50\xf9\x68\xa6\xba\x2d\x14\x63\x9e\xce\x1d\xaa\x58\x6e\xcd\x27\x67\x85\x78\x5e\x5d\xca\xd9\x17\x18\x60\x38\x9e\x41\x3a\x40\xf9\x69\xa1\x53\xd8\x47\x4a\x01\xa6\xe7\xbb\xbe\x6f\xf8\x5c\xb2\x66\x72\x90\x32\xd6\xeb\x13\x00\xe2\xa8\xe5\xeb\xc3\x74\x86\x78\x12\x16\x5b\xda\x39\x21\x23\x3a\xe4\x35\x2d\x1a\x6e\x3e\xd1\x0d\x46\xa7\x9b\xa0\x03\x2b\x44\x97\x94\xd7\x37\x5e\xbd\x81\x5e\x70\x83\xfd\x74\x5f\x1e\xb6\xe5\x23\xdf\x1b\xb7\x18\x50\xc2\x7a\x24\xc5\x83\x85\xf2\x0e\xb9\xa2\x62\xb5\xe5\x64\x7b\x9a\x2a\x2a\x3d\x7d\xb4\x31\x7a\x91\xcf\x24\x4f\x9c\x93\xc4\xca\x02\x88\xa6\x51\xa0\xf4\x6c\xd0\x96\x3f\x12\x8e\x31\xd8\xc7\x5d\xf6\x5b\x53\xdf\x34\x59\xb7\x92\xe3\xb7\x75\x8f\x8e\xee\xf5\x32\xd2\x5b\xe1\x5e\x04\x44\xf7\x5f\x78\x03\xe4\x81\x7e\x48\x74\x36\xca\x60\x51\x28\xb5\x5a\x8e\xcd\x9a\xac\x0d\x07\x6f\xc2\xda\x5f\xca\x96\x8f\x77\x00\x8a\x67\xeb\x10\x16\xe3\x41\x38\xa6\xe8\x77\x77\xc7\x10\x35\xe9\xc8\xf6\x7b\xee\x75\x53\xe4\xd4\xed\x1b\x68\xa2\xff\x46\x1b\x4e\x0d\x1d\x7f\x93\x50\x86\x1c\x33\xcf\xf0\x41\xa0\x7c\xe6\xfd\x90\xf0\x96\xfe\x76\x20\xac\xf2\x8e\xa5\x69\x81\xbc\x84\xac\x9f\xa3\x4b\x0c\x37\x94\x8b\x09\xc1\xf6\x5d\xe9\xef\x32\xf0\xe5\xc6\x74\x79\xf9\xea\xca\x0e\xb1\x97\xaf\xd2\x8f\xa5\x56\xfe\xaa\xef\xb7\xdd\x2f\x50\x68\x88\x76\x82\x55\x19\x17\xf9\x0d\x0b\x91\x92\xac\x3f\x90\x71\x55\xe6\x1b\xed\x25\x7f\x4a\x15\xc7\xb4\xbb\x6a\x22\x7f\xd2\xa6\x1b\x28\xca\x12\x88\x53\x3f\x4f\x89\xd1\xee\x50\x53\xea\x45\xc9\xaf\x23\xed\xde\xaf\x49\xbe\xde\xd2\xb9\x8b\xe5\x99\x00\x0c\x8a\xac\xb9\x1e\xbf\x52\x80\x80\x82\x77\x1b\x9a\xf9\x05\x8e\x9b\x54\xe0\xe1\xe3\xec\x51\xa0\xd8\x8c\x2b\x2b\x68\x69\xff\xa1\x0a\xf9\x9b\x85\xde\xb0\xde\xae\xfa\xdd\x46\x11\x55\xc7\xe9\xc4\x29\x09\x02\x22\xa6\x87\x72\x40\xd8\xa7\x59\xdc\xec\x59\xaf\x45\x09\x24\xd2\x46\x55\x6f\xf2\xcf\x77\x15\xdc\x34\x13\xe5\x84\x81\xf9\x42\xc6\xa6\x74\x88\xf1\xb2\x20\x78\xd6\x5c\x1d\x84\xa6\xa9\x27\x90\xaa\x5e\xac\x77\xc5\xc1\x9e\x74\xbb\x39\xed\x71\x2c\x2b\x3f\xb8\xdf\x3d\xe0\x2a\xeb\x8f\xb4\x29\xd7\x0e\xfe\x17\xf9\x9d\xe2\xf7\x0f\x76\xcd\x46\xa7\x59\x3d\x40\xa4\xee\xc2\x8d\x64\x8b\x82\x77\x07\x1c\x04\x66\x9e\xa9\x84\x87\x03\x47\xfe\x50\x49\xe8\xe9\xcd\x2d\x6c\xd6\x43\xe0\x9c\x44\x24\x38\xf3\x77\x83\x19\x6f\xf1\x19\x0b\xdd\x75\x28\x5a\xbb\xcd\xdf\x76\x51\x40\xc8\x0d\x51\x36\x2e\x37\x58\xa0\x07\x8b\x93\x24\x33\x51\xfa\x7c\xac\x4b\x3e\x50\xbe\xa7\x25\x36\x51\x81\x5b\x79\x07\x0b\xca\xe4\xa3\x10\x8d\xbc\x18\xf1\x8e\x71\x41\x06\xa3\x63\xdf\x7a\x5d\x2e\x59\xe9\x68\x0d\x47\xad\xe9\x44\xd3\x96\x27\x60\x21\xc1\x79\x0c\xbb\xc9\x0a\xe7\x35\x3c\xc4\xb8\x39\x8a\x8f\x8f\xac\x8b\xa1\xaa\x5a\xdc\xef\x06\x87\x48\xed\x86\xee\x00\x1b\x3e\x2f\x75\x3b\xde\x80\xf8\x6c\x25\xc2\x55\x3c\x1b\x2c\x5e\xa0\xaa\x12\x4d\x1c\xae\x9e\x68\x91\x0f\x9c\x77\xd5\x0f\xb0\xaf\xac\x3a\x54\x3a\x8c\x2b\xd6\xca\x1d\xd0\xa1\x6a\xdd\x81\xb8\xfc\x5c\x41\x81\xfb\xb2\x62\xc5\x20\x8e\xc4\x4e\x13\x80\xbc\x59\xb2\x26\xe6\xc1\x47\x2f\x19\x95\x88\xe1\xcd\x27\x5e\x8d\xdc\x1e\xe7\x4a\x39\x51\xe8\xea\xa0\x78\x9e\xf5\x70\x8d\x6b\xa0\xb2\x38\x22\x41\x86\x4b\x50\x3f\xb1\xb8\xf3\xf5\x3e\xbf\xe9\xa0\x29\x32\x0e\xc5\xca\x6b\x29\xce\xec\x87\xc4\x9c\x25\x7a\x15\x5e\x91\xd0\x8a\x33\x4c\xb0\xae\x9f\x37\x1b\x27\x8c\xec\x71\x3c\x06\x77\x51\xdd\xd3\xa7\x60\xbb\xd6\xbd\x89\x8f\xf6\x7c\x10\xe6\x23\x8a\x64\x07\x15\xe1\xee\x51\x77\x8a\x89\xb2\x47\x2c\x04\x52\x33\x2c\x92\x11\xff\x16\x5c\x93\x94\x4b\x98\x45\x97\x02\x5d\xc9\x8e\xea\x7f\x2c\x62\x7d\x45\x38\xe4\x63\xa2\x57\x1f\xf0\x5e\x46\xb3\x62\x2a\x7e\x49\xc7\xe1\x3f\xe9\x7a\x5a\x02\x8c\x69\xbb\xd0\xff\x5b\x20\x8f\xa4\xc8\xc5\x12\x03\x9a\xba\x55\xb5\x4d\x1b\xa8\x8d\x43\x14\x7a\xb2\x0d\x04\x69\xbe\xcc\x28\x71\x6c\x60\xfd\x79\x9b\xd7\xdd\x75\x09\x45\xfa\x26\xbd\xe6\x3b\xe3\x99\x36\xcd\x72\xb9\x5c\xec\x1f\x68\x59\x8e\x60\x68\x3a\xdc\x5d\x30\x77\xc1\x44\xc5\x4d\xcb\xcd\x0a\x94\xb5\xe8\x03\xb0\xea\x6b\xea\xac\x0f\x4c\x66\x23\x14\x40\x36\x8e\xee\xc9\xac\x37\x9f\xca\x10\x11\xd7\x7f\x74\xe4\x01\xd6\xf5\xae\x40\x2e\x58\xe2\x69\x92\x46\xa1\xad\xc1\x35\xef\xfc\x26\x1e\x3d\x17\x0d\xee\xce\x69\x8d\x94\xda\x0a\x2f\x0c\x5e\x2b\x83\x0a\xa1\xa5\xf1\x27\xa2\x44\xee\xd9\xb3\x39\x75\x71\xb1\x8a\x56\xe7\x15\x72\x52\xc9\x19\x2d\xd0\xe4\x3d\x37\xfd\x21\x91\x9b\xf6\xcc\x29\xe5\x4f\xe4\xe6\x5d\x24\x5a\x55\xb2\xf7\xa9\x69\xe2\xf9\xaa\xc4\x8a\x88\xde\xfd\xd6\x92\xbc\x0d\x9b\x52\xf4\xef\x0d\xc9\x1c\xd0\xad\xff\x89\xbe\x58\xc6\xaf\x93\xe8\x7a\x71\xa0\x26\x81\x18\x5d\xf5\xbc\xc2\x2e\x68\x61\x90\xd8\x73\xac\x29\x74\x4a\x07\x77\x80\xe6\xe6\x85\x7d\xd3\x7c\xe4\xcc\xf4\x78\x69\xcb\x97\xc2\x89\x1a\xed\x85\x7d\x27\x7c\xc8\xdf\xcc\xb0\x39\xb0\xf8\x8d\x6b\x8a\x60\x4b\x60\x69\x81\x27\xcc\xf2\x66\x01\xfc\x36\xef\x89\x2d\xd6\x72\x10\x13\x0e\x15\x16\xd5\x6c\x57\x85\xbb\xcf\xe6\x5a\xd8\xf6\x43\x50\xf1\x21\xf1\x26\x29\x66\x8d\x32\xa5\x16\x56\x16\xd3\xa9\x8c\xfc\xaf\xf4\xa9\x0a\x1d\xb0\x2f\x7c\xe8\x81\x1c\x37\xc9\x76\xfd\x07\xf3\x98\xe0\x67\xa2\x2a\xb0\x58\xff\xa5\xe4\xfd\x34\x3d\x95\x0f\x3b\xda\xef\x2a\x8c\xa9\x2a\x92\x64\x0b\xbc\x07\x06\x34\x3a\x11\xae\xd3\x6a\x28\xe5\x35\xf1\xed\x70\x67\x67\x9b\x0d\x29\xc4\x84\x6b\x97\x43\x90\x02\xf8\x6e\x22\x38\x96\xb2\x72\x19\xe7\xd5\x3a\xb8\xf8\xe2\xeb\x1c\x3e\x65\x13\xd8\xbe\x9c\xa7\xac\xee\x25\xc2\xa1\xd3\x9f\x0e\x74\x93\xd3\xc1\xf1\x53\x95\x3b\xc5\x12\xcd\x16\x9b\xe8\xe8\x2e\xfa\x82\xcd\x73\x70\x99\x3e\xb6\x23\xe3\x9b\x29\xbd\xec\x79\xa3\x9f\xc9\x6e\xcb\xb7\x27\xc1\x80\x7f\x41\x82\x1b\x70\x9c\x1f\x9c\xc7\x30\x74\x2b\xe6\xa4\x19\x01\x2f\xec\x90\x06\x2b\x97\x99\x2d\x9f\x09\xfb\x30\x5d\x42\xc5\x10\xc4\x6b\x58\xc0\x62\xd4\x38\x06\xc8\x94\xfb\x5c\x0c\x9f\x76\xc6\x74\xd5\xec\xd3\x75\x55\x7f\xec\x14\x9b\xcc\xc7\xc2\xc3\x29\x54\x52\x44\x84\x3b\xb1\x1b\x92\xcf\xb1\x99\x92\x5d\x33\x0d\x56\xb8\x5d\x46\xc9\x85\xdb\x31\x92\x27\x61\xfd\x11\xdd\x2e\xc4\xae\x4b\x16\x93\xc1\xd4\xec\xf2\x8e\x46\xd9\x34\x9d\x2a\x40\x3d\x13\xe1\x34\x28\x55\x14\x4a\x71\xee\x20\x74\x4a\x8e\xed\xca\x10\x6b\x2a\xb1\x4b\x3e\xeb\x00\x56\x68\x56\x6d\xc4\xec\xef\x17\xbb\x02\xc4\xfc\xb8\xd3\x04\xb2\x61\x54\x12\xf7\x3e\xbc\xf7\x78\xdb\xd8\x05\xa3\x71\x43\xcb\x3c\xb2\x4d\x5f\x30\x80\x2d\x3b\xea\xec\x90\x3e\xb4\x02\x53\xe1\xdf\x41\x26\x46\x04\xe1\xa5\x90\x4c\xbc\x63\x10\xcd\x3a\x12\xed\x4e\xf4\x32\xc2\xe5\x33\x66\x83\xfc\xb7\xb8\xbe\x73\x4a\x05\x3e\x90\x67\x03\x10\x3d\xb0\x47\x90\x93\x12\xb4\xb5\x75\x50\x7a\x1e\xf4\x7e\xb4\x56\xac\xdc\x9e\xb0\x10\x0e\x5c\xa9\xbb\x98\x99\x1d\x0f\x6b\x52\xe5\x2e\x10\x06\x17\x62\x74\x52\xe3\x22\x51\xaa\x20\x54\xe1\x80\xd1\xf9\x73\xc5\xb1\x70\x1f\xbe\x00\x10\xab\x43\x07\xa0\x86\x87\xf1\x79\xb3\x34\xeb\x85\x90\x91\x6d\x5b\x22\x0f\xda\x68\x07\xfa\xb5\x11\x0b\x8b\xd8\x15\xb8\x55\x83\xab\x78\xcf\xa5\xe8\xc4\xa8\x75\x31\xbf\xc7\x97\xa5\x38\x1d\x11\xd1\x31\x8b\xba\x9a\xac\xcc\xd9\xe5\x0a\x8b\x76\x9d\xa4\x1f\xc2\xb8\x74\xb8\xa7\x9a\x32\x00\xb0\xe1\x28\x83\xef\x27\xb4\x85\x18\x8d\x8a\x1d\xc6\x7f\xab\x5a\x4c\x21\xdc\xd5\x5c\xc4\x40\xd2\x53\x70\x14\x9a\x37\xd1\x4b\x1b\x3f\xf9\x69\xd8\xb8\x9f\xf0\x9f\x07\x2a\x6d\x19\x5c\x4c\xef\xdf\x24\xd4\x25\x50\xa3\xbf\xe2\x2c\x30\xcd\x03\x8d\x19\x83\x85\x20\xaa\x8e\x74\xc9\x59\xa4\x73\x87\xf6\xfc\xab\xf4\xec\xbc\x77\xff\x13\x54\xec\x51\x5b\x5e\xc5\xee\x7a\x39\x58\x0e\xdc\xbd\xc1\xce\x39\x5a\x18\x94\x01\x39\x42\x49\x3a\x90\x0e\x94\xa8\x9d\x90\xc0\xcd\xc8\xd9\x84\x31\x44\x49\x10\x25\x94\x1a\xb0\x85\xb0\xa0\x0a\x33\x22\xd8\x00\xca\x41\xa5\x1b\x29\x8d\xe3\x89\x3f\xc6\x49\x8c\xb0\x22\xb0\xb0\xd3\xa3\x8d\x59\xa4\x58\x3d\xd9\x6d\x18\x11\x72\x89\xee\x4c\xba\x46\x37\x64\x47\x7a\xfc\x59\x55\xcb\x15\x8d\xab\xda\xf0\xd5\x31\xc8\xc9\xee\x27\xfd\xe9\x94\x7f\x11\x43\x69\x96\x35\xeb\xae\xb8\x05\xb1\xe1\x72\x1b\xcc\x8f\x5d\xdf\x36\xf5\xf2\xd9\x69\xc3\xc7\x46\xd6\xe8\xf0\x26\xf8\xd3\x8f\x4f\x34\x9d\x98\x26\xcf\x21\x1b\xfc\xbd\xac\xfa\x57\xbb\xf9\x83\x2e\x5d\xb2\x05\x2a\x2e\x55\xf2\xc0\x2e\x55\xad\x06\xc4\xc0\x6e\x5f\x3b\xb4\xc0\x4a\x95\x16\x7a\xd7\xac\x69\x95\xc4\x45\x9a\xcd\x46\xe6\x97\x36\x80\x8d\x40\xa2\xff\x30\x34\x28\x6b\x60\xae\x6c\x15\x3f\x54\xe1\xcc\x91\xb9\x9f\x1f\x9d\x36\x13\xf7\x22\x3d\x89\x0a\x5c\x0c\x8c\x9b\xd3\xba\x0f\xb6\x0d\xd6\x91\xa4\xae\x18\x04\x85\x71\x31\x4c\x24\xab\x9d\xbc\x8a\xc6\x94\x2c\x38\x09\xa0\x0e\x2b\x4f\x45\xa9\x27\x22\x31\x71\x9a\xb5\x29\xb2\x42\xc9\xca\x6d\x21\xad\x80\x7e\x79\xaf\x30\x15\x2e\x04\x5f\xaf\xcc\x61\x82\x1d\xac\x72\x65\x6c\x32\x7a\x65\x6b\x36\x82\x80\xb1\x29\x4e\x3c\x67\x1b\xc2\x4c\xf1\x36\xeb\x45\xc8\xd4\xc4\x42\x49\x18\x9b\x90\x24\x9d\x34\x98\x6d\x7f\x21\x53\x1b\xb5\xeb\x07\x6e\xcd\x7d\x01\x5f\xc3\x98\x8e\x81\x0e\x1a\x0b\x74\x23\x3a\x53\x6f\x54\x13\x82\x0c\xb6\x6e\xf5\xa7\x9e\xb7\x8d\xde\x8f\xa5\x96\x88\x39\xa1\x63\x4e\x5f\x46\x8b\x99\x3b\x01\x7b\x44\xb1\xb7\x81\x72\xe5\xbf\xa6\x45\x4e\x9c\xa0\x6f\x3e\x12\x31\x8d\x8b\x20\xfd\x50\x21\xc7\x61\xec\xac\xa1\xfc\xe5\xd8\xb3\x87\xe1\xe9\x43\xef\x99\x0f\xb2\x98\x80\xb3\x68\xad\xce\xe6\x49\x34\x43\xb0\xf5\x66\xc3\x90\x42\x38\x89\x32\x02\x35\xec\x71\x1c\x80\x24\xac\x9a\x81\xa0\xbe\xe5\x0f\xfd\x1d\x4e\x4b\x54\x7f\xb0\x5c\x88\x81\xef\xea\x80\x81\x0a\x39\x64\x82\x0a\x37\xc8\x0b\x3a\x4a\xc2\xb0\xf1\x58\x2a\xbc\xe2\xec\x4e\xad\x7a\xf5\xba\xde\x8a\xbc\xd4\x44\xac\x01\x00\x0a\xc2\x3b\x87\x08\xfc\xf2\x5a\x05\xab\x45\x4d\x31\xd4\x64\x11\x73\x40\x54\x67\x2c\x73\x25\xa6\x19\xe9\xf1\xc5\x6b\xda\x33\x5c\x83\x56\xe9\xcf\x39\x49\xd2\xd2\x85\xbd\xd3\x68\x30\xb1\x0d\x79\xae\x93\xf9\xa5\xb8\x29\x50\x51\x12\x4b\xdc\x0d\x6a\x34\x20\x19\x4c\x9c\x2f\x38\x2e\xbb\x40\xcb\x23\xad\xa1\x27\xc3\xdd\xca\x0d\xf5\x1b\xc2\xac\xd3\x35\xf2\xd2\xda\xde\x30\xff\x0f\x6c\xb1\x72\xc1\xd0\x1e\x1c\x7c\x60\x04\x46\x90\xd0\x74\xa4\xbc\x84\x5b\xc7\x3f\xac\xc3\xca\x41\xc2\xa9\x0c\xd9\xc8\xe4\x64\x7a\xa6\x32\x59\x6c\x8a\xb3\x6c\xad\x9e\x78\xcc\x77\xf1\x19\x26\x7c\x7f\x0e\xbf\x85\xcb\x84\xa3\x0a\x48\xf9\x62\xb2\x59\x47\xd1\xd2\xf4\x80\xdf\xa4\xb2\x11\x8a\x21\x03\xb7\x22\xa7\x0b\xa5\x88\xc0\x46\x98\x6a\xd9\x97\x6b\x36\xee\xd5\xd6\xfd\xf5\xa6\x0e\x3d\xba\xf1\x57\xa0\xe0\x24\x5a\x7a\x11\x57\x50\x11\xaa\xe9\xac\x32\x82\xa0\xe5\x86\x4b\x7e\x39\xca\xdb\x7e\x7d\x72\xfc\xf6\xed\xf9\x95\xdf\xa6\x79\x1d\xd4\x05\x09\x13\xdf\x38\x73\xb8\x51\xbf\xcc\x28\xce\x4d\x60\x0c\xe1\xcd\xf2\xb4\xc4\x21\xb8\x90\x4d\x59\xed\xf4\xb9\x6c\xc0\x7b\x1a\xee\x8b\xf1\xf2\xa8\xff\xc5\xa1\xf9\x4b\xde\xb3\x7c\xf3\x21\x31\x65\xf7\x39\xff\x4d\xc2\xfb\x82\xe0\x8e\x06\x4b\xcf\x5f\xe5\x78\xd3\x7b\xea\x40\x53\x8c\xee\x0f\xc0\xa4\x77\x39\x8e\x46\x84\xfb\x06\x7b\xc5\x75\x8a\xcb\xec\x23\x56\x87\x36\x2d\x16\x0c\x23\x77\x57\x57\xbf\xed\x20\xa0\xf1\xc1\x88\x98\x07\x5b\x59\xce\xab\xb5\x6c\x28\x7f\x71\x3f\x24\x9d\xbf\x06\xe6\xe1\x41\xe3\xf4\xeb\xc7\x6e\xcb\x46\xaa\xb4\x37\x74\x4f\xef\xed\xaa\x94\xb5\x6b\x6c\xa2\x75\xef\x19\x1d\x63\xf8\x8a\x9b\xa6\x8f\x20\x9e\x8d\xaa\x63\x17\xb1\x85\xa8\xe2\x9c\xb1\x0f\xe8\x56\xd3\x79\xb5\xb0\xc8\x1b\xe9\xff\x04\xf1\x7f\xa0\x4d\xf6\x47\xf3\xe3\x78\xa8\xa7\x64\xc2\x11\xd6\xee\xa7\x7c\xbd\x8b\x95\x25\xdc\x3a\x97\xe9\x1e\x25\x28\xaa\x1a\xe3\xa1\x2f\x1b\xf2\xcc\x28\x9d\xf3\x60\x99\x8e\xd4\x09\xf4\x05\x5e\x27\xf9\xba\x17\x5d\x71\x1a\xa0\x9f\x79\x01\x5a\x2d\xc3\x29\xd6\x3b\x3d\xc7\x72\xba\x45\x5b\xc1\xb2\x5e\xd2\xd9\x73\x31\x0d\xbc\x16\x5d\xa2\x6f\xf7\x92\x08\x95\xc6\x34\x5b\x56\x3d\x9d\x91\xd9\x7f\x0a\x76\xd8\x09\xad\x73\xda\x7a\xe0\xf3\x28\x5f\x96\x32\x2a\xca\xbb\xb4\xc0\x42\xc7\xc5\xb2\xa1\x92\x2c\x7f\xe8\xef\x89\x52\x0a\x68\x1e\x97\x7c\x5d\xd1\x64\x55\x5d\xf1\x4a\x7d\x4d\x7f\x68\x17\x16\x99\x3d\xa6\x2b\x11\x48\x51\x89\x2a\x64\xe4\xd4\xec\xea\x51\xbb\x38\x9d\x15\x35\x88\x0b\xe6\x45\x4d\xb7\x55\xe5\x0d\xb4\x21\x21\x7d\x8e\x04\x75\x74\xa4\x9e\xd0\x2c\x7c\x12\xf9\x45\x5c\x1f\x5f\x5b\xca\x43\x3e\xb3\x3d\x4a\xf6\x7c\x75\x2b\x5a\xe4\xbf\xea\x27\x94\xc8\xcb\xfc\x77\x49\xbd\x74\x3f\x30\xed\x9d\x12\x42\x77\x40\x85\x3c\xbc\x87\xfd\x7a\x4d\xf2\xb0\x86\xdb\x15\xca\x6c\x8a\x94\xf1\xf5\x40\xaa\x76\x68\x91\xb9\x43\xa2\x2e\x9c\xe6\xc9\xe6\x7c\x38\xc5\x95\x2d\xcc\x3d\xcc\x04\x4c\xdb\x91\xc7\xeb\x12\x26\x9c\x73\x5a\x57\xf7\x9e\x09\xb6\x6d\x51\x5a\xad\x3a\x79\x67\xea\x45\x1a\xcc\x9e\x42\xcc\xe0\x75\x92\xd9\x61\x97\x6d\x79\xf8\x20\xa9\x8a\x9b\x69\xa8\x88\x6f\xab\xec\x94\x07\x9e\x2c\x4f\x5e\xbe\xbe\x82\x1f\x0b\xcd\x35\xfc\x0e\xcc\x59\x87\x2d\x7c\x67\xae\x4e\x66\xe9\x55\xd7\xc9\x56\x5f\x57\x40\x3c\xaf\x5e\xbb\xa3\xb7\xe9\x20\x7e\x24\x47\x53\xad\x2c\x64\x56\xbe\x36\xbb\x58\x04\x8c\x99\x18\xbf\x96\x44\x2d\xc8\x89\x38\xf3\xc6\x77\x30\x66\xe4\x94\x87\x2e\x54\x89\xb0\x08\xe3\x3b\xca\x30\xae\x1d\xe7\x81\x4f\x0c\x1b\xf3\xc7\x2c\x07\x2e\xb9\xc1\xbc\x85\x26\x78\xbc\x29\x15\xbc\xab\x6e\x6f\x32\xd6\x0d\x63\x23\xdd\xde\xf8\x84\x40\xe2\xa0\x8c\x2a\x02\x76\xd6\x12\x17\x98\xa7\xff\xf8\xdf\xff\xe7\xf1\x09\x77\xfc\xa4\x6f\xd7\xf4\xa5\x02\x1d\xc3\x0b\x22\xa5\x82\xf4\xfc\x5f\x49\x30\xdf\xab\x69\xc4\x2f\xf2\x95\xd8\x6f\x2c\x36\xca\xef\x54\xa1\x8b\x8f\x44\x7f\xf1\x9a\x4b\xd4\xc3\x98\x17\x5b\xc2\xa7\x22\x9d\x78\x3a\x11\x85\x2c\xf2\xb7\x5d\xb5\xf8\x98\xc9\x61\xfe\x69\xfa\x67\xfe\x95\xc2\xb9\x34\x11\xb2\x61\xce\xe3\xd8\x08\xc8\x6b\xc0\x8b\x42\x13\x5c\xb0\x56\x35\x84\xf7\x6c\x27\x8f\xb7\xa9\x1b\xb3\xeb\x33\x40\xf6\x9b\x49\xb6\x3b\xb6\x06\xe2\x29\xb5\xd6\x2e\x28\x05\x4e\x48\x9c\xc8\x42\x45\x50\x83\xbb\xd8\x8b\xea\x40\xf3\xd4\x5d\x71\x22\x9b\xdc\x8d\x91\xe5\x55\x53\x6c\xa1\x3c\xcf\x69\xc8\x2a\x19\x27\x8e\x1f\x2a\x1f\xec\xdb\x12\xf2\x3e\xfd\x49\x88\xcd\xb2\x01\x99\xde\x12\xb2\xf3\x64\x9f\xe3\x56\x0c\xe9\x76\x47\xc8\x57\x9d\xf9\x52\x2b\x82\xa0\xff\x5c\x3f\x13\x4a\xe7\xdf\x57\xf4\x67\xe4\xe6\xcc\x4e\xd1\x63\x67\xe8\x75\x3e\x2f\x91\xfc\x06\x1f\xb4\x60\x88\xd3\xf7\x34\x0b\xd0\x58\xb9\x1f\x09\xa3\xa1\xea\x85\xf8\xf0\x95\xa8\x97\x81\xdc\x08\xca\x67\x82\xfb\x96\x36\x67\x93\xdb\x77\xf9\x5e\x7e\x12\x8a\xd4\x5b\xfa\x95\x7c\x49\x32\x0c\xb8\x05\x14\xf6\xdb\x0e\x1e\xb2\x9c\xae\x80\x0b\xfb\x4e\xac\x03\xb3\x71\x47\x2c\x67\xe0\xac\x9d\x2e\x06\xf9\xd7\x72\x22\x7d\xc1\xe7\x51\x4b\xcb\xc1\x8b\x53\xb3\x41\x73\xe9\x1b\x5a\x46\x72\x29\x71\x26\x5f\x2e\xa7\x10\xa3\xce\x53\xec\x81\x9a\x66\x06\xf5\xe7\xfc\xd7\xa5\x12\x4d\x22\xf5\x2d\xff\x75\xc6\xe9\x12\xcb\x80\x8f\xa2\xe2\x1d\xee\x93\x67\xc3\xb9\x08\xb2\x6a\x16\x28\xe6\xb8\xfd\xa1\xe5\x85\xfc\x30\x7b\x41\xf8\x6f\x33\x57\xfe\x84\x7f\xa6\xeb\x51\x2d\x6e\x72\xc3\xb9\x1d\x34\x13\xc2\x50\x53\x93\x60\xd2\x5c\x08\x29\x2d\x6e\xa6\x80\xe9\xf8\x51\x47\xb0\xe7\x94\x10\x92\x56\x54\x31\x0b\xce\x83\x9a\x21\x4b\x4f\xc3\xd3\x36\xc7\x0e\x4c\x38\x4d\xe8\xe7\xb8\x9f\x01\x90\x74\x33\x9f\x00\x65\xa5\x8e\x87\xa3\x81\x0f\x81\x54\xef\xe8\x78\xce\x70\xf6\xfc\xfc\xd0\xd4\x0e\x27\x48\x32\x33\x92\x9c\x16\xa5\x73\xbf\x00\x10\x24\x08\x8e\x2b\x10\x35\xe3\x2a\xd3\xc6\xa2\xfa\x80\xd0\x3e\x9f\x53\xf6\xfd\x02\xd8\x74\x85\x19\x57\x3e\x4b\x50\x67\x99\xca\x5c\xac\xe6\xa8\xca\x30\x8f\x84\x9d\x4c\x04\x3f\x41\x84\x13\x02\xd7\x13\x25\x6e\xa5\xa8\x21\xcc\xc1\x9a\x47\x74\xa3\x25\x6f\x99\x5e\x0f\xc1\x9e\xf8\x87\xab\x3e\x50\x4e\xa5\x2d\xc8\x58\xe3\x9c\x19\x3b\xe9\x38\xfe\x79\x0c\xd3\x10\xf0\xd0\x29\xd0\x4e\xe3\x8b\x90\x58\xcd\xfb\xb9\xeb\x6a\xa1\x1a\x9e\xa9\x42\x32\xcb\x45\x36\xbf\xd1\x32\x32\xcf\x70\x7e\x3c\x50\x64\xc3\xd6\x25\x0d\xc7\x75\xd0\x22\x67\x2e\x61\xa2\x48\xa7\xce\xd3\xec\xbd\x3c\xce\x99\xf1\x66\x04\xeb\x13\xe6\x4d\xdd\x24\x08\x53\x29\x40\xce\xf1\x31\x05\x22\x7a\x4f\xd5\x5c\xf0\x2e\x20\x6e\x02\x76\x53\x3a\xd9\x30\x9b\xd4\xb8\x12\x6f\x60\x60\xd3\x7e\x41\x39\xb6\x56\x65\xbe\x2a\x6a\xee\xb3\x06\x16\xa4\xf8\x79\x4b\x3b\xbe\x80\x34\x34\x2a\xc1\x2b\x09\xb3\x40\x20\xf2\x9d\xde\x7f\xff\xed\x87\x8e\xa7\xc1\xdf\x20\xbc\xff\xee\x43\x77\xef\xd9\xfd\xf7\xdf\x7f\xc0\xd5\xc1\xa8\x70\x76\xcd\x9a\xb3\x71\x0d\x28\x68\xd0\xdb\xb6\xfc\x54\x35\xbb\x4e\x64\x34\x7c\x7a\xfe\xf0\x59\xa6\xe2\x73\x1f\x2f\x71\xe7\xc4\x3d\x58\xe1\x85\xcb\x8a\x57\x78\xbd\xdb\x64\x3a\xc6\x4e\x38\x80\xfd\x72\xc5\x0d\x03\x59\xce\x4d\xfe\xea\x7e\xf3\x70\xab\x82\x07\x4b\x9d\xb7\x30\x20\xff\x22\xbf\x9e\x61\x20\x3c\xf4\x5f\x5d\x4b\x4d\x70\xe9\x70\x25\x7e\xe8\x2c\x33\xbb\xeb\x8f\x9b\xb2\x9f\xc5\x5c\xc9\x62\xa2\xa0\xcb\x71\x96\xf6\xc2\x83\xe8\xbc\xc1\x46\x37\x04\x6f\x4b\x20\xc6\xe0\xde\xe1\xe7\x20\xf3\xb6\xca\xda\xa8\x80\xb2\x5a\x4f\x25\x0a\x3a\xc0\xb5\x62\x4a\xb6\xa1\xaf\x43\x93\xb4\xe7\xea\xb0\x9f\x5f\x59\x8b\xc8\x13\x24\xb4\x5e\xbb\x7a\xae\x09\xe3\xf5\x02\x0a\x6a\xe8\xf0\x79\xa8\x6a\xa3\x29\xd0\x5f\xd9\xc4\xb6\xd1\x88\x4e\x17\xf8\xb0\x64\xd1\x08\xa9\x01\xbe\xa3\xcd\x48\x7b\xa6\x89\xe6\x6e\x45\x47\x02\x3a\xfc\x80\x63\x9b\x8b\x15\x5f\xe3\x70\x52\x04\x5a\xd5\x99\xd9\xf1\xeb\xa9\x81\x98\x25\xdb\x9e\xc9\x88\x88\x8c\xd8\xab\x54\x15\xb2\x07\x3d\xe0\xa2\x5b\x3e\xf3\xa8\x93\x3b\x5e\x9e\xc8\x70\xb5\x96\x05\x34\x1e\x3f\xd3\x1f\x87\xd7\x81\x3d\x8d\xf5\x8f\x5b\xa1\xee\xd3\x1f\x4b\x92\x7d\xd1\x16\x9d\xdf\xb7\xe3\xfc\x45\xb3\x6e\xfc\xbe\x8e\x5f\x43\x00\x51\x90\xde\x2f\x06\xb2\x99\x64\x7b\xda\xd6\xd5\xcb\x09\x83\x9d\x47\x20\x27\x06\x23\x19\x03\x6b\xb1\x38\xd3\x39\x96\x48\x07\xe1\x5e\x62\x91\x0b\xc6\xb5\xa8\xcd\x15\x40\x9d\x86\x76\x12\x6c\x4a\x15\x2f\x52\x46\xa8\x7a\x67\x99\x3d\xb4\x59\xe0\xcb\xe7\x40\x1b\xaf\x35\x1f\x56\xbe\x4f\x37\xed\xcf\xc4\xd2\xd3\x3b\x6e\xf9\xe4\x10\x14\xc8\xe0\xe1\x61\x48\x33\x59\x7d\x9b\x13\x5d\x8a\x85\x8b\x1e\x34\x38\x45\x0d\x7b\xba\x69\x38\xc3\x82\x01\xf7\xfb\x26\x75\x47\x34\xc4\x22\xe3\x5d\x22\x4f\xb9\xb0\xf9\xdb\x61\x6d\x68\xf9\xd9\xa0\x5a\xf8\x4c\x3f\x85\x2d\xdd\xb0\x41\x6d\xe1\x69\xaa\x5f\x9a\x1f\x9d\x1e\x87\xa7\x46\x1b\x79\xc3\xaa\xb0\xdd\x1a\x1b\x04\xae\x2e\xe5\xc7\xb5\x5c\xba\x19\x10\xc2\x39\xb1\x44\xe3\xdb\x0a\xb8\xbc\x04\x7b\x52\x5b\x0a\xce\x9d\x97\x8b\x1c\x66\xb3\x70\xb7\xa2\xb1\xae\x38\xbc\x94\x1f\x3d\x81\x70\x70\x0a\xab\x9f\xed\x90\xc3\x10\x5d\xcc\xd3\x5c\xf5\xa6\x0f\x19\x60\x6a\x5e\xf6\x7b\x9e\x57\xb1\x6d\x60\xe4\x8a\x49\x6d\xf7\x43\xb8\x53\x13\x7b\x7b\x82\x36\x9e\xf0\x76\x5d\x28\xab\xfb\x17\xfc\x10\x86\xa7\xa8\x1c\x08\xf3\x13\x64\x80\xd5\x6e\x93\xca\xe4\x08\xd5\xf7\xa6\xa4\x46\xb1\xc5\x17\x76\xbe\x14\xc6\xfb\x23\x3b\xc7\x19\x67\xc5\x37\x51\x38\xdb\x2e\x68\xfa\xf7\x2e\x5d\xeb\x47\x4d\xba\x93\x5b\x33\x92\xf6\x8f\x55\x4f\xa5\xff\xd3\x07\xa3\x51\x3a\x0a\x64\x21\x2f\x05\x7d\xfa\x9f\x11\xd4\xf0\x58\xed\xf3\x44\xfb\x0b\x82\x2a\xcd\xae\xb1\xd0\x7c\xdd\x75\x89\x54\x04\x35\xce\x75\x41\x32\xf4\x62\x2e\x9c\x49\xea\x35\x1d\xf1\x99\x11\x28\x36\xdd\xfd\xd4\x2c\x42\x0d\x44\xdc\xd6\xb7\xc4\x54\xe3\x72\xae\x46\xd5\xba\x95\xaf\x30\xf1\xc2\x97\x2a\x38\x98\x15\xad\x0f\xbb\x95\xa4\x5f\xee\xfe\x61\xba\x2e\x85\x2d\x76\xde\xd4\x9c\xb1\x48\x85\xa0\xfb\x0a\xf8\x99\xf5\xbd\xea\x32\x98\x23\x89\x39\xf3\x95\xda\x18\xad\xab\x45\x9f\xba\x74\x6a\x4e\x0d\xce\x61\x94\xb2\x94\xd8\x37\xce\xba\x9c\x36\xcb\x6e\x85\x60\x1a\x0c\x70\x4d\x5c\x6a\xd3\x40\x8a\x73\x2c\x22\xaf\x33\xa8\xf1\x31\xd4\x30\xc2\x06\xab\x5b\x0d\xb9\x0c\xf1\x78\x80\x61\x51\x8c\x0d\x86\x1b\x54\x0b\x3d\xf7\xa1\x9a\x1f\xf4\xb7\xd7\x6d\x5c\x40\x1c\x41\x78\xd1\xdb\xd0\x3b\xb7\x7c\x4d\x37\x72\xb0\xc9\xa9\xa0\x6b\xa1\x0a\x8e\xe6\xa1\x91\xf8\x0c\xb0\xf3\xa9\xfa\x88\x68\x86\x6c\x05\x04\x34\xb5\xba\x41\xb4\xbb\x5a\x17\x21\x4a\x41\x0b\xc8\x64\xfe\xeb\x78\xbc\x4a\xcc\x87\xc6\x1a\xb2\xad\x5a\xb6\xed\x88\x9e\x1e\xfe\xcb\xfd\xe2\x91\x2c\x64\xd8\xf8\x8c\xee\x59\x38\x51\xd0\x19\x6e\x91\x3c\xe6\xaa\x83\xb7\x35\x93\x0d\x6f\x16\x0c\x44\xdf\xb3\x5f\x93\x40\x53\x17\xec\x67\xfe\x0c\x1e\x64\x4f\x28\x0c\x82\xdc\x69\xa5\xc1\x10\xa0\xf0\xaa\x98\xfb\x5d\xd4\x76\x93\xd1\xf2\xc8\xf4\x44\x47\x5b\x0a\x2f\x16\xfc\x1a\x76\xc1\x8e\x32\xc3\xaa\xdd\xa1\x20\x1e\x11\xed\xed\x73\xde\x47\xc4\x87\x58\xf8\x74\xa0\x9d\x24\x12\x50\xbf\x11\xbd\xcb\x56\x01\x21\xaa\x7e\xc0\xe6\x27\xb1\x63\x62\x1d\x1c\x5d\xc3\x8c\x89\xeb\xbe\x30\xd7\x0f\xfa\x94\x46\xcc\xfa\xc0\xf4\xa1\x05\xe0\x7a\x14\x0f\xb2\x14\xa3\x69\xfe\x1b\x66\xb8\x78\x29\x5a\x55\x26\x53\xaf\x35\xa2\x72\x4d\xf1\x71\x44\x8e\x9c\x9b\xe8\x83\x1b\xfa\xf7\x78\xb3\x79\x5c\x14\x0f\x26\x46\x1d\x88\x46\x6e\xd8\x03\xcb\x2f\xd5\x42\x0c\x58\x65\x50\x53\x20\x67\x4e\xe3\x8e\x01\xa2\x79\xfa\x85\xa5\x80\x92\xaf\xc3\xd2\xc2\xe3\x4d\x48\xd7\xcf\x5d\xc7\x5b\x40\x43\x0c\xcf\x5b\x93\x30\xab\x10\x77\xc2\x70\x24\x03\x09\x3d\xc8\x1a\x78\x49\xdf\xda\x3d\x77\x6d\xa2\x12\x1d\xb1\xef\xcd\x01\x94\x48\xd0\xbc\x83\x08\x09\x24\x63\x8f\x54\x27\x1d\x4f\x00\x4e\xc9\xc6\xbe\xed\x7f\xa6\x7c\x3c\xd5\xf8\x14\x09\xdc\x25\x21\x4f\x05\x68\xb5\xb4\x99\xd0\x37\xbc\x54\xe4\xcb\x67\x05\x41\x5f\x54\xce\x08\x7e\x7b\xb0\x55\xd3\x7c\x94\xc0\x37\x73\x7c\xfa\x9c\x25\x07\x7c\x94\x4c\x0e\x6d\xf8\x2a\xce\x25\xd1\xb2\x5a\x84\x81\x60\x9f\x73\xc2\x44\x17\x0b\x9e\xe3\x36\xfb\x5d\x74\x92\xa7\xf8\x95\xfe\x4f\x26\x0c\x07\xa2\x2e\x26\xe7\x16\xe8\x88\xad\x1f\x7c\xae\x3a\x07\x04\x4d\xa9\x2f\xc3\xb8\x2d\xb5\x9e\x67\x76\xfe\x65\x0e\x20\x53\x8e\x1f\xb1\x37\xea\xcc\xd7\xee\xfc\xd9\xf8\x76\x48\x3f\xcf\xcd\x25\x6e\x0c\xe6\xee\x5d\xbd\x1b\x5c\x7c\x39\xc5\xb6\x6b\xb5\x58\xbd\xc3\x15\x8e\xef\x91\x1a\x6c\x58\xa1\xff\x1d\xd1\x9d\x8b\xa5\xa8\x27\x6e\x68\x01\x60\x09\xd6\x05\xdd\x43\x10\x61\x38\xc3\xb2\x64\xd6\xc9\x2d\xbb\x3a\xf5\x89\x6b\x88\x68\x0a\x72\x77\x7a\xef\x60\x8e\x30\xb1\xf5\x41\x07\xee\x82\xd0\x88\x6f\x89\x75\x15\x79\xc1\xf4\x0e\xfc\xa1\x80\xe9\xe0\xe2\x7a\x00\x68\x48\x39\x27\xfe\x21\xa6\x8a\x52\x2c\x8f\xfc\x09\xfb\x40\x83\x25\xe6\x45\x7c\x65\xe7\x7a\xc4\xec\xa9\x6c\x7b\x78\xf2\x8d\xd1\xce\x9e\x05\xb4\x80\xb2\x6f\xa9\x99\xc7\x90\x95\x24\xfa\x23\x06\x21\xeb\xaf\xba\x0e\xf0\x01\x8b\x4b\xb6\xa0\xfc\x54\x15\x3b\xa2\x3e\x9e\x8b\xdb\xea\xfd\x2e\xae\x97\x56\x3c\xee\xcb\x0f\xd6\x3d\x98\x4f\x08\x11\x2e\x34\x30\x5c\x39\xaf\xbd\x87\x72\x37\xd5\x32\x33\x21\xa7\xed\x50\x1c\xc0\xd3\x38\xf5\xae\x7a\x21\xab\x12\x3e\x04\x9b\x2f\x31\xcb\x36\x31\xe9\x87\x74\x34\x1f\x31\xb6\xc4\xfd\xd3\x49\x55\x77\x5a\x9d\x8d\x08\x61\x80\xa5\x41\x7d\x40\x58\x60\x1b\x66\xb3\xcf\xc3\xe7\x90\x72\x1a\xb6\xd8\xce\x00\x21\x49\x48\xd8\x02\x44\x51\x66\x93\x00\x3a\x2a\x1c\x29\x0f\x3e\x72\x5a\x55\x71\x7a\x0b\xcc\x08\x3d\x0f\x6c\x22\xcc\x0e\xfa\x0a\x13\x01\x41\xc0\xeb\x51\xd3\xde\x17\xef\xc8\x9b\x40\x39\x0b\x0f\x96\x3b\xd9\xda\xac\x2e\xca\x2d\xc7\xb4\x64\xc5\xe6\xb5\xec\xb6\xc2\xf3\xef\x68\xf5\xbb\xdb\x5a\x15\xcb\xad\xa9\x66\xcd\x86\x51\x9d\xdb\xb1\x68\x39\x10\xf5\x1d\xad\x7d\x6f\xad\x85\x5b\xd6\xc7\xb2\xdc\x06\x4d\xc4\xdd\x0f\x7c\x3a\xc0\x3c\x63\xd3\xac\x09\x8e\xa6\x6e\x8b\xe6\xe7\x7c\x80\x89\x47\x31\x59\xbc\x95\x80\xee\x66\x77\xb8\x78\x8d\x17\x88\xa9\x40\x11\x3d\x1d\x6a\x50\x07\xc3\x5a\x9e\x2c\xe0\xdc\xb0\xaa\x35\x96\x3c\x51\x95\x58\xeb\x0e\xac\x8a\xbc\xe3\xb3\xeb\x9a\x15\x68\x0f\x77\x2f\x36\xc8\x4c\x27\x0c\x31\x1d\x28\x5b\xbb\x87\xc4\x9a\x8a\x8f\x03\x8f\xe7\x24\x48\x3e\x5c\x60\xe0\x59\x10\xd5\x15\x7b\x16\x04\x1d\x14\x2a\x3a\x54\xcf\xc9\x64\x1d\x4a\x79\xe1\xd4\x72\x7c\x83\x0a\x9e\xec\x99\xc6\x0c\xd3\xc8\xff\x43\x57\x72\xcd\xdd\xaf\x9a\x20\x44\x8c\xb8\x3b\x60\x2f\x0a\x3b\x32\x8b\xc7\xba\x17\xf1\x44\xf1\xa2\xc2\xca\x40\x8a\xb1\xbd\xc5\x44\x19\x1c\x79\x37\x3b\xda\x3a\xd7\xd5\x47\x28\xc3\x88\x30\x25\x88\xf0\xf9\xe5\x15\x34\x60\xb4\x00\x68\x1f\x5d\x32\xdf\x4d\xff\xba\x2a\x6b\xc4\xb5\xe4\x58\xbe\xca\x88\x16\x0b\xf6\x52\xaa\x6a\x0d\xfa\xb7\x2f\xcd\x76\xbc\x2e\xd6\xc2\xb6\x42\x3f\x36\x93\x1e\x44\x13\x96\x22\x5e\x2f\xaf\xb4\x6e\x5b\x2e\x48\x26\x9e\xf1\xb5\x57\x5b\xe3\x11\x04\x17\x9a\xfd\xd6\x98\xef\x6e\x24\xb0\xcf\x61\x85\x59\x80\x16\x45\x49\xa8\x1d\xd6\x3d\x78\x84\x9e\x21\xe8\x94\x14\x6c\x18\xbe\x4d\x06\x06\x7f\x15\x93\xe5\x8a\xd9\x75\xaa\xb6\x24\xb7\x79\x82\x1c\xec\x43\x18\x74\x51\x9a\xbe\x43\x14\x1e\x56\x35\xf3\x7a\x05\x53\x26\x4c\x80\x74\xdb\x46\x0c\x3a\xdf\xe9\xe7\x18\x48\x4e\x4b\xdc\x93\x57\xf2\x35\x06\xd9\x6a\xf8\x24\x17\x48\x69\x0c\x32\x6f\x0a\x3e\xff\x3c\xa7\x3f\x63\x21\xda\x45\xdc\x37\x49\x1a\xc4\xb9\x65\x17\x7c\xb9\x65\xe6\x0c\x42\x77\xb9\xbe\x96\x70\x0a\xac\x39\xc2\x71\x4f\x94\x7d\x6c\xba\x2c\xc1\x42\x4b\xf6\x9d\xe3\x0a\xd4\xa1\x0e\xbe\x22\x88\x94\x16\x6a\xf2\xd4\xf9\x36\xf4\xa6\x1c\xf6\x09\xb7\x16\xd6\xaf\xd7\x22\x83\x60\x1a\x70\xb8\x95\xb0\x76\x47\xbc\xb5\xf0\xb9\xd0\xee\x11\x6d\x03\xda\x4a\x28\x3b\x76\x83\x22\xa2\x46\x84\x12\x03\x11\x19\x56\x2c\xb2\x02\x2b\x62\x0b\xef\x0d\x62\x03\xc2\xc6\x3d\x52\xab\x6f\x46\x90\xd8\x7b\x8f\x20\xfc\x2d\x27\x80\xcc\xbf\x6a\xb8\xcf\x28\xb8\x3f\x2b\xbc\x8a\xd6\x43\xc0\x51\xa2\xa7\x10\xd0\x51\x0d\x50\x27\x9a\x5c\xe6\x14\xa6\xc8\x0d\x14\xa6\x8c\x2b\x36\x99\x0c\x56\x37\x6f\xd3\x24\x1c\x89\x14\xdd\x96\xcb\xbc\x2d\x2c\x6e\x8b\x72\x1a\xf6\x5d\x01\x47\x09\xdd\x74\xf3\x35\x1d\xbe\xb5\x0a\xe2\x8c\x04\xf2\x91\xcd\xa2\x68\xbe\x59\xc6\x31\x7d\x03\x4b\x8b\x85\xb0\x31\xf6\x14\x24\xe6\xb2\xdb\x32\xbf\x11\xe6\x65\xed\x60\xc8\x0f\xff\x74\x79\xfe\xf6\x28\xfd\xfc\x78\xbf\xdf\x3f\xe6\xe2\x8f\x77\xed\x9a\x7d\xea\x0a\x8e\x0b\xf3\x3f\xce\xde\x1c\xa5\x65\xbf\x78\x34\xa3\x83\x7a\x1b\xab\xb7\xd4\x36\x14\x57\x0f\x4c\x5d\x2c\x3a\xfe\x71\xf6\xa4\x2b\x46\xc3\xac\x87\xe1\xc7\xc2\x0d\x92\x67\xcf\x8c\x3f\x74\x32\xc5\x08\xc4\x1f\x0e\xcb\x45\x5b\xc2\x76\x02\x1f\x41\xc6\x9a\xce\x04\x53\xf1\x00\x86\x20\x15\xb5\xa3\xdd\x78\xbd\xd0\x30\xef\x03\x10\xbb\x2a\x3c\xc1\x25\xa1\xcb\xc4\xc4\xb9\x6d\x85\x03\xdc\x75\xab\x66\xb7\x2e\x62\x8e\x49\x38\xd3\x89\x28\x8b\x9f\x86\x85\x61\xe5\x88\x38\xcd\x4f\xd3\x3f\xb1\xa6\x88\x27\x4a\x68\x8b\xb3\x8c\xb6\x00\x3c\x1b\x16\x46\xc4\xc1\x40\x30\xa6\x01\x48\x24\x45\x13\xcc\x7d\x9e\x13\xce\x47\x95\xe8\xf9\x8d\x8d\x2e\xfa\x74\xe3\xce\x73\xa0\x35\xa9\x6e\x5c\x24\xd6\xd3\x4d\x67\x1b\x5e\xc4\xd8\xf1\x48\xcd\x20\x4d\x89\x35\x85\x87\x54\x4c\x3c\x27\x51\x14\xf0\x47\x80\x32\x17\x09\x6d\x4e\xfd\xda\x05\x63\x4a\x35\xc4\x66\x39\xcc\x08\x62\x28\x94\x3d\xbc\xd7\xa7\xd6\xa2\x9c\xa8\xdd\xac\xf9\xd5\x63\xfc\x4d\x77\x38\x91\x4c\xc4\xe1\x27\xe2\x1e\x60\x1d\xb1\xcc\xb5\x1f\xee\x62\x43\x71\x4b\x79\x93\x17\x65\x94\x37\x8d\xb6\x6b\x05\x1c\xb4\x31\xda\x25\x55\x3a\x1e\xcb\xfc\xbe\x85\x43\x02\x81\x98\xf8\x64\x3a\x4a\x0b\x23\x03\xaf\xc9\x53\x97\x16\x8b\x57\xb6\x4a\xc1\x77\xe3\x25\xca\x08\x91\x75\x14\x72\x54\x16\xd4\x62\x83\x00\x06\x81\xb3\x2f\x3b\x19\x98\x59\xfd\xc8\xd5\x39\x14\xa1\xa5\x56\x73\x5b\x13\xf7\xba\x41\xe6\xf0\x5d\x8b\xe1\xca\x26\x59\x4d\x5e\x46\x3a\x91\xaf\x10\x5b\xdb\x75\x73\x63\xde\xe0\xa7\xf8\xa5\xe1\x62\xc2\x91\x79\x30\x1d\x94\x87\x0c\x94\x2f\x4d\x16\x57\xf7\xb7\x20\x3c\xaa\x8a\xb8\x35\x9f\x77\x51\x94\x60\xc2\x63\x4c\xa4\xf0\x9e\xe8\xde\x84\x43\xb1\x83\x1a\xba\x3e\x9f\xba\x16\x0e\xb8\x3e\xc7\x45\x43\xf7\xe7\xa0\xe8\x17\xb8\x3f\xc7\x48\x1a\x3b\x37\xfb\xa1\x7e\x81\x7f\xf3\xd4\xa0\xc7\x72\xed\x14\xe2\x27\x0a\x4c\x49\xb7\x45\x38\xb6\x2f\xf0\x73\x1e\x9c\x6c\xbf\x44\xc0\x9d\xea\x89\x47\x49\x80\xdc\xbb\x34\xbe\x45\x75\x7d\x3d\x9b\xb7\xcd\xbe\x63\x67\x62\x3c\x12\xc1\x5c\x96\x7f\xa7\x97\xf8\x2d\x20\x7c\xd5\x0f\xa2\x90\x0f\x49\x54\x73\xa3\xa7\x7a\xb3\x27\x89\xb8\x67\x1d\x86\xa6\x3f\xa5\x1c\xb9\x73\x7d\x4b\x27\xb1\x63\xcb\x99\x49\x11\xda\xe8\xf6\x19\x7f\xc1\x0d\x1a\xda\x67\x56\x96\xa2\xd0\x25\xa7\x28\x18\x7f\x1a\xc2\x6d\x57\x82\xa5\x9b\xdc\x4a\x8b\xf8\xea\x35\x47\x20\x2c\x83\x23\x30\x22\x86\x0a\xf2\xa9\x07\x09\xdd\x1d\x09\xc2\x70\xe9\x21\x14\x41\x58\xf4\xcf\x5f\xbf\x95\x9f\x30\x5f\xd7\xf0\x43\xb0\x5f\xe7\xcb\xf1\xc4\x8c\xe2\x67\x53\xc6\xf1\x96\x27\x7e\x0c\xa2\xe6\xb0\x57\xdd\xf0\xcb\x41\x14\x6d\x7e\x8d\x6b\x20\xfe\xeb\x52\x49\x06\xf6\xc5\x2e\xda\xf2\xf1\xb0\x18\x21\x47\x50\x7d\x89\x0f\x97\xae\xd7\x38\xfc\xc7\xa5\xe5\x30\xd1\x78\x1a\x8c\xdc\x63\xc4\x6c\x01\x88\xee\xee\x77\x69\x57\xb1\xee\x54\x09\x74\xd0\x20\xa8\xc3\xc7\x0d\x06\xed\xe0\x9d\xb3\x70\xac\xe1\xfd\x10\x42\x1e\x74\xab\xd4\xe1\x87\xdd\x77\x7a\xf1\x66\xd6\x77\x4b\x66\x51\xbf\xa3\xd2\xb2\xc5\x96\x36\xdb\xe9\xba\x59\x42\x8a\xc4\xb3\x86\xf2\x80\xc5\x8e\x1f\x7a\x21\x44\x70\x18\x2b\x96\x38\x1c\xf1\x57\x1b\xaa\xff\x93\x04\x1e\x97\xea\x49\x7a\x70\xde\xd6\x24\x48\xd4\xe2\xf0\x69\x79\x38\x52\x5b\x78\xb6\xa8\x8c\x0f\x6c\x6c\x9a\x6a\xef\x41\x42\xf9\x90\x4c\x16\xa1\x63\x0a\x8b\x29\x1c\x7f\x42\xc6\x1e\x74\x20\x62\xc4\x96\x3a\x66\xbe\x96\xb3\x6f\x35\x36\x16\xff\x0d\xc9\x42\xdf\x04\xb9\x90\x2f\x97\xc3\x12\xb0\x88\x71\x6f\xe4\x4b\xde\xaf\x1b\x52\xd3\x38\x1e\x00\xe5\x3d\x1e\xce\x75\x00\xef\x10\xf0\xd7\xf2\x3f\xfe\xd7\xff\x65\x15\x6b\x43\x3b\x2c\x82\x77\x68\xfc\x4b\x4f\x2b\xe6\x0f\xe7\x1f\xa5\x79\x0c\xbe\xee\x3b\x32\xbc\xe4\x73\x0d\x2a\xa9\x78\xa2\x19\xd1\x3b\xbf\xdf\x60\x6b\x85\xed\xf5\x06\x0b\x06\xf4\xe3\x97\x0c\xae\x31\x87\x75\x18\x11\x64\xba\xdf\xb8\xb8\x7c\x0a\x28\x1c\x04\x23\xea\x95\x48\xa6\xb7\x27\xb7\x76\xf5\x2d\x0d\x30\x61\xfb\x4e\x92\xf7\x4d\xbb\xfc\xe0\x63\xbb\x3a\x27\x80\x28\xae\x2b\xce\xa0\x0c\xe3\xa2\xa3\x1d\x00\xf4\x11\xd3\x7c\x8d\x36\x29\x2f\x99\xf4\xc6\x6f\x8f\x89\x06\x40\x82\x6b\xc3\x1c\xcd\x5c\x92\x67\xe6\x5f\x25\xa1\x21\xf5\xaa\x3c\x7a\x14\x12\x6e\x4f\xa6\x1c\x35\x39\xb2\x48\xf4\x0e\x97\xdd\x74\xf8\x83\xa3\x77\xf2\x33\x76\xac\xcf\x92\xdb\xb7\xd7\x48\x20\xc6\x80\x04\x44\xa6\x85\x7b\x12\xff\x4d\x10\x0f\x50\x35\x78\x9c\xaa\x5f\x9a\x3e\x88\x39\x18\x3d\xdd\x10\xf8\x83\x21\x16\x69\xf4\x1e\x03\x57\x0e\xac\x4c\x5c\xdf\x8f\x22\xd4\x02\x85\x48\xbd\x0d\x3a\x72\x1f\x7e\xb0\xc6\x95\x8d\x06\xb8\x82\x22\x9c\xcd\xc2\x6a\x11\x2e\x41\x27\x88\x8f\x5a\x47\x46\xac\xdd\xcc\x37\x13\xac\x9f\x95\xdc\xed\xfb\x62\x78\x15\x69\x4e\x2b\xe9\x27\x81\x8f\xbc\x20\xf5\x58\x98\x8b\xbf\xbe\x24\xa7\x6b\x3a\x61\xac\xa3\x73\x22\x2a\x62\x59\xf0\xa7\x03\x1e\xb0\xe3\x58\xc2\x5f\xef\x03\x3b\xae\xe3\x76\x2f\xd8\x3f\x7a\xad\x3c\x1d\x27\x30\x54\x86\x0d\x02\x06\xba\xac\xa9\xc8\x81\x7f\xe4\x92\x37\x06\x0d\x44\xac\x08\x05\xae\xa6\x2f\xbd\x4c\xd0\xbb\x63\xa2\xd4\x7f\xec\xea\x38\x8e\xa0\x3b\xec\xf5\x28\xc4\x5d\xd4\xe9\xaf\x0b\x75\x77\xf0\x0e\x36\xe2\x15\xc3\xc3\xe1\x28\x5e\x05\x06\x78\x6b\x91\x38\x7a\x45\xd8\x61\xa7\x0e\x0c\xee\xf4\xf4\x86\x40\xe2\x56\x88\x8e\xfb\xee\xe0\x15\x07\x2e\x4d\x6e\x8b\x62\x31\xec\x25\xf3\x18\xe7\xa2\x11\x76\xf2\xd6\x12\xe1\x5e\x1b\xdf\xbb\xff\x23\x91\x2d\xa6\x2f\x26\xf8\xf0\xb8\x37\x9d\x19\x76\x66\xc3\x9f\xd7\x44\xf0\xe1\xc3\xf0\x25\xa7\x13\xcf\x68\x3d\xe6\x20\x55\x09\x72\x07\x11\x9b\x95\x6b\xcf\xf4\x1e\xce\x82\x5a\x0d\xd2\x3d\xcb\x83\x15\xb4\xde\x34\x7a\x20\xf9\x0d\x11\x66\x32\x67\x58\x3e\x6e\x23\xf6\x48\xb0\x54\x77\x37\x74\x86\x0f\x97\x4e\x58\x5b\x94\x08\x38\x70\x22\x5f\x2e\x47\x0f\x69\x1a\x05\xdb\x77\x42\x22\x1c\xc3\x8b\x28\x48\xd5\xdd\x4e\x71\xcd\x0e\xd1\x34\x29\x37\x5b\x9e\xc2\x3c\x75\x6a\x42\x96\x6f\x65\x17\x14\x59\x50\x7b\x05\xd1\xfa\x87\x61\x5d\xf2\x58\x8d\xee\x9a\x6f\x9b\x7d\x22\x5b\xe6\x0c\x8e\x11\x12\x91\x57\x53\xe2\x2e\x49\x1a\x0b\x24\x1a\x2e\x29\x95\xb8\x10\x1a\x50\x67\x9c\x3f\x08\x25\x80\x1d\xc3\x05\x11\xb0\x00\xdb\x2c\x54\xaa\x0b\x4e\x2d\x77\x33\xb1\x6f\xbd\xd4\x0a\xa1\xd4\x37\x0b\xd9\x34\x6e\x37\x84\xf8\x92\x86\xf1\x36\xf1\xb0\xb9\x23\x53\x8c\x21\xfe\xa2\x6a\xec\x24\xd0\x9c\xb4\xa2\x0f\xce\x5a\x3f\xdc\x9b\x6f\xbe\x1f\x21\xc4\x97\xf4\x83\x5b\x81\x35\xb9\x1c\x52\x6e\xe9\x0f\x9d\x2a\x35\x9c\x64\x64\x01\x30\xec\xa2\x77\x91\xbf\x0a\xf6\x69\x58\x9d\x14\x03\xb9\x83\xf5\xd0\xe3\x0d\x53\x72\xe4\x76\x78\x42\x34\x10\xe3\xa0\xc9\x48\x53\x77\x2f\x71\x98\xed\x73\x49\x07\x1a\x98\xfd\x78\xb0\xc9\x5d\x47\xfa\xe5\x45\x39\xc8\x56\x67\x2a\xcf\x49\xe6\xdd\x1b\xae\xc0\x59\x8c\x25\x91\xeb\xc2\x2d\x03\x82\x9d\xcd\x64\x21\xaf\x0d\xb8\x35\xce\xac\x2e\x68\x75\x5c\x99\x63\xd5\x80\x72\x2c\x7a\x0c\x67\xbc\x33\x94\xca\x02\x2d\x2d\x33\xe5\x23\x93\x55\x9d\x4d\x02\xa0\x36\xf9\x4d\x64\xf5\x03\x23\xe5\x4d\xfc\x3a\xef\x2d\x8a\x9d\x71\x57\xfc\xae\x2d\x21\xfc\x1d\xc1\x1c\xd4\xe6\xcc\xc2\xa5\x3e\x26\x10\x4f\x76\xcb\x16\x1e\x0d\x36\xd7\xcc\x2c\x02\x52\x40\x85\x3f\xb8\x51\xba\x47\x21\x3d\x37\xc0\xb5\x33\x55\xf4\xe0\x36\xa6\xf0\x15\x1d\x00\xdb\xb8\xbd\x07\x60\x0b\xe2\xe4\x46\xdd\x08\x58\xc0\x6d\x1d\xd1\xd7\x1c\xbf\xbc\x23\xe0\x1b\x5f\xd8\x91\x23\xeb\x85\x86\xbf\x2e\x8a\xc9\xf5\x7f\x5b\xff\x06\xc7\x1c\x10\x67\x14\x5f\x7d\x40\xf0\xd1\xeb\xf3\x8e\xe8\x03\xfb\x37\xab\x16\x96\x16\x6a\x8f\xa7\xdb\x99\xaf\xaa\xa6\x29\xc4\xb9\xb5\xee\x43\x9b\xbd\x38\x32\x09\x9b\x8b\xf5\xed\x8d\x8a\x24\x3c\xb8\x38\x30\x8a\x37\xd5\x91\xc3\x17\xee\x8e\x25\xe6\xfe\x7b\xa0\xfd\x43\xe2\x1e\x14\xe4\xa5\x6c\xdf\x89\x28\x6a\xe4\xfe\x0c\x51\xcf\x7d\xb8\xf3\x74\x18\xfe\xfc\xd6\xd0\xf3\x71\xc8\xfd\xe1\xdb\x0b\x9d\x44\x28\x5b\x9a\x2c\x67\xcf\x38\x26\x6a\xa2\xc4\x8c\xf5\x86\x70\xb0\xc1\x1b\xbe\x0b\x8e\x42\xdc\xd4\x95\x18\xc3\x9c\xc9\x17\x8d\x3d\xc1\x98\xb2\xad\x04\x83\xc0\x13\xfc\x12\x19\x52\x53\x38\x32\x64\xd2\x37\x3d\x04\x8a\x2b\xfe\xfb\x43\x7a\xbf\x48\xfc\xd0\xa1\xb2\x64\xed\x90\x4a\x09\xf2\x1d\xe4\x07\x71\xd2\x61\x20\xdf\x5a\xe4\x77\x5f\x03\xba\x09\xc5\xe8\x2e\xe8\xb6\x76\x12\x95\xee\xba\xa9\x16\x33\xbe\x6e\x4d\xf5\xb2\xd9\xbd\xe4\xce\x1c\x84\x1f\x57\x2b\xf8\x71\x35\x51\x9b\x1d\x05\x09\xd1\x84\x84\x19\x5b\x17\xae\x34\x4a\x8e\x77\x45\x9f\x2e\x71\x9b\xa2\x24\x0e\x1e\x13\x25\xe4\x8b\x51\x2b\xa6\x16\x0f\xd3\xcc\xf0\xce\xa7\x98\x09\x5e\x54\x7b\x1c\xb2\x32\xcc\x12\xbb\xc5\x28\x49\x5f\x95\x8c\x47\x22\x4a\xc9\x30\x6d\xdd\x2c\xf9\x79\x04\x7b\xb5\x38\x18\x9e\x0a\xd6\x71\x9d\x66\x83\x1d\x55\x01\x5f\xcf\x30\x05\xb7\x65\x7d\xde\xc5\xa5\xb1\x3e\xc3\x04\xf5\x93\x1f\x01\xd2\x41\x3b\x5f\xac\xd4\x25\x68\x82\x90\xec\xbc\xec\x88\x49\x0e\xcd\x53\x90\xf2\xf2\x67\x6a\xef\x7c\x4e\xc2\xf0\x9b\xee\x78\x56\x35\xc8\x65\x9f\x06\x76\xa0\x41\x54\xcf\x46\xa3\x5b\xb1\x83\x83\x8b\xe0\x99\x9e\x63\x39\x76\xb7\x16\x0a\xb6\x38\x8e\xb2\xa0\x51\x43\xb5\xa4\x88\x23\xb7\xec\x75\xbe\x66\xdd\x35\xd5\x8c\x24\x78\x05\xae\xf3\x42\x44\x0e\x87\x2b\xbd\xce\xb5\xec\x2f\xaa\x63\xd0\x4b\x0f\xe1\xaa\xf9\xfa\xae\x42\xa5\xc6\x61\x6a\xa8\x37\x83\x4e\x46\x3c\xcf\x40\xee\xa8\x61\xd0\xc5\xc9\x2a\xbe\xa2\x93\xfc\x14\xf1\x72\xe1\x1e\x50\x3d\xe5\x68\xd1\xed\x9c\x39\x1e\x6f\x70\xe5\xc2\x7c\xc9\x22\xb5\xdc\x74\xf1\xdb\x7a\x86\x0e\xf1\x81\x7c\xaa\xfa\x43\x7d\x6b\xcb\xee\xa6\x5e\x64\x78\x47\xb7\x5b\xe9\xf5\xe7\xbb\x52\x94\xe3\x0f\x66\x94\xf6\x24\xd7\x08\x6b\x25\x2e\x0a\xbb\x07\xf2\xa0\xc0\xc3\x05\xa5\xc3\x2a\x99\xf6\xbf\xc7\xe0\x89\x28\x6d\xd2\x1d\xc9\x6e\xfd\xa3\x5b\x1b\x1a\x8c\x25\x60\x88\x01\x6e\x5b\x74\x85\x5f\xfe\xff\x82\x11\x04\x57\xef\xe1\x30\x98\x0c\x74\xf5\x83\x57\x84\xef\xdf\x30\xe2\x1e\xb2\x19\x05\xc7\xff\x66\x1b\x11\xb5\xbd\xd2\xdd\xce\xde\x49\xd6\x0b\xb1\x03\x03\x0a\xdb\xbd\x65\x86\x1e\x44\xbd\xb8\x7b\x8c\xe1\x26\x24\x6f\xe1\xef\xb6\x6c\x27\x9c\xba\x47\xf0\x7f\xc1\xef\x90\x29\xc8\x13\x05\xd9\xb2\x69\x1b\x9a\x1e\x89\xf9\xa3\xcf\x16\xbc\xb4\xb4\x6e\xa2\x00\x14\xd8\x37\xd9\x4e\xe3\x34\x59\x99\x33\x24\x93\x70\xc1\x41\x9b\x7c\x29\x6c\xd1\x56\x86\xd5\x92\x0b\x55\x66\x63\xcf\xb6\x52\xc7\x96\x11\x94\xd4\x32\xcd\x9c\x1d\x00\xd4\x2d\x15\xc0\xe7\x9a\x12\xc0\xe2\x22\x84\x03\xe9\x10\xba\x76\xdb\x8c\x87\x8a\x88\x1f\x92\x9c\xbe\x41\x72\x7a\xc5\xc9\xe3\x16\xac\x57\xae\xd8\xa0\x53\x87\xca\x71\x70\x85\x61\x99\x17\x1c\x83\x61\x08\x6f\x98\x5b\x95\xf9\x76\x84\xb7\x57\x94\x38\xc2\x1a\x20\xc7\x08\x00\xec\x61\x2c\x84\xa5\xaa\x02\xa7\xae\xb0\xc4\x6b\x4a\x3a\x04\x0d\xbb\x82\x21\x7c\xcd\xa2\xe2\x81\x12\xba\x67\x0f\x7b\xa5\xb7\x37\xa3\x5e\x35\xf3\xbf\x97\x8b\xbe\x33\xe8\x73\xf9\x19\x40\xcd\x9b\xa6\xe7\x47\x77\xb7\x2c\x6e\xc1\xde\x4b\xd0\xf4\xdc\xd2\x59\xdc\x5a\x7c\x1c\x61\x4a\xa0\xc7\xa8\x12\xe8\xc3\xb8\xda\x70\x44\x46\x6a\xab\xdd\x2d\xfa\x1d\x2d\x50\xd7\xe0\xd9\x25\x47\x72\xbc\x74\x19\xa3\x16\x47\x25\x43\x0a\x1d\x16\x9e\x6a\x79\x41\x42\x44\x39\xd9\xf4\x09\xe7\xdc\xda\xf6\xa8\x6c\xd8\xf8\xa8\xf8\xd4\x4a\xc1\x2b\x3c\xac\x50\x9f\xef\x16\x1f\xcb\x9e\x9d\x88\x56\x19\xee\xad\xc3\xba\x2e\x0c\x2c\x7d\x0e\xb0\xf4\x15\x81\xa5\x57\x50\xd1\x4c\xd4\x4a\x9b\xce\xa6\xec\x73\xd8\x1f\x04\xb5\xbc\x3c\xa1\x19\xe0\xe4\x22\x9f\x2a\x05\xd5\x4d\xa6\x52\xb6\xae\x42\x16\x7c\x82\x1a\xce\xa1\xdd\x51\xc1\xfb\xd8\x81\x4c\xd5\xc6\xe1\x7c\x64\xf7\x5b\xdc\x2c\xe4\x71\x1a\x0e\xf0\x43\x7d\x78\x27\x29\x01\x2c\x4e\x12\x04\x6b\x3c\x12\x57\xed\x08\x36\x4f\xe0\x57\x31\xa3\x14\x0e\xe6\x81\x85\x71\x11\xdc\x05\x7b\x22\x4f\x01\x6e\x73\x59\x4c\x07\x21\xad\x79\x03\xb4\x96\x87\x70\xda\x68\x27\xa8\x14\xb6\x22\xc7\x38\xb1\xc6\xd7\x50\xed\x44\x73\xb8\xc8\x85\x31\xbe\x05\x6a\xe7\x34\x85\xbd\xf3\x0d\x75\x05\x13\xe9\x15\x32\xab\xa4\x98\xbc\x85\x07\xf6\xec\xdb\xf2\xa2\x18\x35\x92\x16\x3d\xe8\xae\x69\xe6\xec\xea\x62\x6d\x69\xba\xbe\xb2\xac\x6f\xf7\xc2\xbc\x1e\xef\x0e\xb2\x55\xee\xbb\xf0\x05\xee\xb7\xa1\xe5\xfd\x55\x83\x51\x06\x03\x8b\xad\x91\x6c\x98\x77\x3b\xdc\xce\xb4\x8e\x30\xa6\x8b\x8e\x0c\x02\xb2\x19\xe4\x0c\x1e\x16\x54\xc3\x1c\x81\x94\x00\xa6\x72\xcf\xb5\x0e\x4b\xe3\xf4\x62\xc7\x81\x41\x0d\x6f\x70\xb2\x09\xb0\x3c\x7e\xb9\x19\xda\x65\x3e\xd8\x8b\x75\x35\x74\xb3\xb0\x7e\xdb\xd5\xf6\xfe\x8f\x91\xc1\xa1\x27\xb7\x2c\xc8\xf7\x17\xbe\xba\xe5\x71\x11\x50\x0a\x2e\xee\x63\x1a\xa9\xba\x2c\x24\x8a\x61\x8c\xf0\x7c\x40\x24\x0c\xae\x74\x12\x81\xe2\x26\x3f\x7c\xe3\x3e\xb8\x59\x35\xc2\xc1\x1d\x26\x5b\x78\x67\x6a\xe4\x38\xaa\x21\x28\x03\x95\x9e\x10\x36\x9b\x96\x8a\x8b\x6b\x58\x8f\xc4\xaa\xcd\x6c\xc6\xee\xaa\xeb\x60\x68\xdb\x49\xbc\x7b\xad\xaa\xa1\xdd\x3d\x89\x06\xe8\x5b\xaf\xe4\x62\x04\x4f\xbf\xd4\xf8\xff\xe5\xb1\xca\xb0\x03\xfe\xc9\xca\x03\xed\xff\x53\x9e\xac\x1c\x6a\xb4\xbb\xb8\x2b\x13\xe6\x75\xc7\xc3\x77\x2b\x0e\xd8\xd6\xf1\x9b\x7e\x33\x78\x2a\xc5\x2c\x32\xba\x30\x8c\x58\x25\x4a\x84\x2c\x10\x09\xb1\xe9\x04\x92\xbc\xba\xdd\x34\xed\xa2\x2d\x03\xf7\x1b\xb6\x17\x38\x97\x45\xad\x49\x89\x71\x40\xfc\xb8\x0b\x92\x32\xbe\xa5\x93\x74\x55\xf4\xa4\x16\x9f\x5b\xb5\x76\x33\x68\x7b\xf4\x6a\xcc\xd2\x86\x41\x69\xa1\xc4\x53\x66\x35\xe8\xf2\x80\x5d\x45\xdd\x96\x52\x12\xf6\xc2\x1c\xd7\x94\x23\x6a\x56\xd0\x7b\x49\x09\x23\x3e\x4a\x8a\x3c\x08\x87\x77\x8e\xe5\x4b\xd3\xc7\x86\x2e\x41\x27\xb5\x9a\x41\xe7\x82\x5a\x01\x35\xcd\x71\x83\xde\x0c\xad\x88\x25\x15\x1e\x5c\x6c\xf2\xdc\xf5\x9a\xb2\xd5\xb7\xf0\x39\x8e\xa3\xa4\x40\x79\x52\xc0\x16\x91\x75\x25\xa7\x6f\xc3\xf4\xe0\x8d\x38\xe4\xba\xd7\xe1\x26\x60\x02\x33\x94\xbc\xe5\x30\x92\x3f\x68\x54\x9d\xe0\xad\x38\xf6\xb6\x92\x97\x67\xb6\x6b\xee\x2f\x47\x14\xc7\x15\x06\x6b\x81\x59\x66\xc8\xf1\x3c\x14\x2e\x74\x89\xcd\x2c\xc5\xac\x55\x9e\x33\x51\x64\xb2\x7c\xa0\xb1\xac\x20\x17\x68\x70\xde\xe7\x6c\x84\x15\x80\x14\xf6\xb4\xb6\x1f\x51\xde\xf7\x6d\x35\xdf\xf1\xbd\xa8\xda\x7f\xf0\xa2\x14\x63\x13\x97\x37\x82\xed\x76\xe6\x9f\x71\xa9\x5f\x87\x61\x07\x4f\xe4\x0f\xe0\x24\x9e\x96\x75\x4b\xa2\x69\x59\x15\xb8\x56\x70\x00\x72\xd9\x18\x41\x6c\x78\xc7\xc9\xba\x9c\x97\x27\xf1\xd6\x22\xbd\x3c\xd6\x9c\x6e\xd3\x6f\x2d\x1e\xfc\xe5\xd9\xd5\xc5\x2d\xb4\xc4\xa0\x4a\x13\x80\x0c\x08\x83\xb3\x94\x38\x90\x15\x50\x88\x1a\xdd\xa8\xa5\xba\x1e\xeb\x61\xb6\x23\xc4\xd6\x4d\xc3\xdd\xb6\xed\xcb\xeb\x3e\xfc\x50\x3b\xbf\x29\xc0\x86\xe5\x52\x66\x96\x9e\xed\xd6\x7d\xc5\x56\x60\xd6\x9a\x5a\x22\xf1\x3b\xf6\xe5\x36\x6f\x2d\xaa\x29\x62\xf8\xa4\x0f\x8e\x1e\xcc\xa2\xd5\x97\xf5\xf2\x20\xa0\xbc\xcd\x78\xf5\xe6\x92\x3e\x17\xed\x8d\x5c\x84\xea\x48\x3f\x56\x5b\x06\xd3\x27\x9d\x79\xc0\x94\x02\xd8\xbf\x20\xc5\x96\x0a\xdf\x98\x95\xed\xa7\x6a\xe1\x08\xe6\xe2\xf8\x0c\x7a\x07\x4a\x0a\x17\x9f\x36\x8d\xa8\x43\x26\xf9\xf9\x4e\xd0\x74\x34\x91\xe4\x67\x0c\x84\xdf\x32\x66\x63\xcd\xad\x21\x30\x0c\x8f\x32\x14\xcf\xf4\xc1\x4b\xc5\xf4\x48\x54\x89\xa1\x03\x89\xc5\xb3\xb6\xa1\x44\x19\x17\xb9\xcb\xc6\x7d\x16\x31\xb3\x70\xe7\x1a\xbc\x7c\xfc\x65\xb6\x3f\x61\x65\x81\x94\x71\xdb\xa0\x27\x23\x35\xc4\x25\x22\xc8\x4c\xf8\xab\x3d\x0e\x13\x57\xed\x1f\x03\x1a\x95\x88\x9e\x89\x19\xe1\x75\xc2\xa6\xe6\x16\x3b\x9a\xa0\xf6\xc1\x7e\x1f\x57\x7c\xd7\xb6\x2f\xba\x38\xd3\x81\xb9\x7b\x28\xd5\x81\xc5\xd7\x51\x0a\x9b\x6f\xb7\x6e\xdb\x08\xde\xff\x01\xd9\x06\x20\x9f\x84\xe1\x04\x10\xb4\x08\xba\x41\x3d\xe2\x7c\x16\x02\xb1\x0f\x9a\x02\x0c\xb7\x1e\x4d\x6e\xae\xaf\x39\xbe\x16\x47\x89\xd4\xe0\x28\x08\xb7\x75\xc6\xd6\xdc\x56\x52\x5c\x2a\x33\x56\xca\x41\xc9\xc5\x63\xb2\xa7\x63\xdf\x21\x91\x4f\x15\x06\xde\xee\xdc\x9b\xdb\xef\x76\xb5\x9c\x97\x82\x2c\x6d\x88\xb3\xc2\x46\x20\xbd\xb4\x4d\xd3\xdb\x2b\x0d\x81\xe8\xf2\x8e\x92\x69\x4f\xeb\x57\x0e\xc1\x7c\xd3\xb5\xc8\x24\xec\x7c\x50\x06\xf7\x6c\x0b\xd8\xe4\x8f\x0b\x51\xbf\xc7\x25\xa8\xdf\x07\xc0\xc5\x30\xc3\x36\xfe\x4b\xfc\x12\x26\xed\x7a\xcc\x66\x9e\x4a\x8d\x36\x60\x49\x1b\xd2\x4d\x88\x83\x62\xee\x09\xe3\xd4\xee\xe6\x26\x49\x83\x20\x43\xe9\xc5\xa7\x86\xf2\x82\x4f\x0d\x65\x1f\x9f\xaa\x1d\x1b\xf4\xa0\xeb\xd6\x36\x11\x97\x97\x6f\xe2\xd9\xf6\xb9\xc1\xb3\x3d\x6c\x2f\x76\x8f\xc3\xc5\x72\xd4\xba\x7b\x29\x7b\x1a\x3e\x0a\x4a\x28\x36\x43\xfc\x69\xea\xb0\x8e\xee\xb7\x75\xd5\x97\xdf\xdf\xc3\xd5\xf9\xbd\xbe\x2a\xe6\xf7\x1e\x85\xeb\xa6\x82\x63\x41\xb0\x70\xaa\xc5\x01\xf4\xb8\xc3\x7b\xf4\xb2\x68\x2a\x5e\xda\x55\x5b\xda\xfe\x7e\x12\x3c\xf6\x39\x22\x69\xbf\x0d\x38\x82\x0e\xb7\x00\xeb\x18\x7b\xa9\xb4\x41\x46\x46\xf2\x42\x2f\x6f\x21\xb2\x81\xe6\x3b\xab\xe6\x39\x92\x7d\x0f\x25\xd4\xad\x85\xbe\x55\x7b\x7d\xeb\x1f\x22\xd7\xbe\xae\xe1\x47\x62\x45\xec\xbd\x66\xe8\xd9\x46\x4f\x3b\x43\xc1\xa6\x2f\x3b\x6b\x01\x8c\xdd\xa9\x2f\xce\x78\xc0\xa1\xc6\x62\x38\x60\x38\x50\x55\xbf\x97\x12\xad\x30\x18\xf6\x19\x1d\x86\x37\xbb\x0d\xde\x75\xbc\xe4\xe0\x6a\x78\x99\x73\xd4\xad\x2d\x49\xfa\x79\xd8\x23\x24\x38\x26\x24\xbe\x91\xec\x18\x92\xad\xf5\x86\x4b\xfc\x27\xe1\x1e\x92\xbe\xc1\x95\x96\xc3\x0e\x6d\x42\x5e\x2c\x8d\x0a\xbd\xe3\x3c\x27\xc6\x4e\x14\x36\xb7\x6a\x47\x2a\xe6\xb6\x38\x49\x2a\xbf\xed\xca\x1d\x55\x5e\xd6\x4b\x90\xe9\x9f\xf9\x27\x89\x3b\xfc\xd3\x21\x48\xfc\x11\xa1\xf0\x62\x8f\x86\xa7\xe6\xa1\x08\xbd\x17\xa5\x38\x5a\xb8\x53\x2e\x09\x66\x26\xdc\x05\xce\xf0\x7b\xba\x83\x0a\x3b\x3e\x9a\xc4\xf9\x36\x8b\xb4\xa6\x9a\x60\xee\x5e\xfd\xfc\xe6\x7c\x00\x39\xc1\x0c\x34\x67\x82\x79\x68\xce\x04\xab\x90\xdb\x5a\x37\x04\xdc\xd0\x4e\x8f\x40\x20\x0f\x0e\x40\x08\xda\x5b\x66\x80\x92\x27\x2b\x52\xd2\x2f\x88\xb2\xc4\xdb\x46\x88\x5e\x7e\xc7\x40\xc1\x23\x52\x02\x65\x6f\x48\x8d\x5a\xad\xc3\x36\x6b\xb9\x69\xf4\x5c\x47\x4c\x84\x02\xae\x23\x26\xf6\x93\xdd\x33\x68\xf6\x2a\xaa\x0a\x7d\x73\x4b\xe0\x2f\x34\xc9\x40\x0d\xc4\xd7\x6c\x10\x5a\xb5\xeb\x26\x11\x6e\xe5\xa4\xd7\x13\xfc\x8a\xa6\x4e\x97\x1f\xaf\x17\x81\xf5\x2b\x90\x9f\xa6\x96\x12\x06\xbc\x5c\x38\xc4\x98\xce\xf8\xe5\x89\x7f\x5e\x0b\xea\xe5\xc1\x60\xd6\xd5\x75\xe9\x94\xd1\x3a\x9a\x37\x94\x16\x01\xaf\xfa\x7e\xdb\x99\x8f\x39\x9e\x74\x4a\xcf\xe9\xc7\x60\x10\x61\x55\x3a\x92\x51\x4d\xdb\x0a\x17\x04\x01\x5e\x24\x61\x1a\xe3\x06\xad\xbb\x43\x00\xae\xdb\xc3\x90\xc7\x2d\x5b\xc7\x38\x6d\x85\xf8\x57\xed\xbd\x2c\xe0\x5a\x67\x19\x60\xb2\x65\x86\xd2\x5d\x92\x61\xb0\x4b\x9a\xb5\xd0\x6c\xd1\x4a\xbc\x3b\xfe\x73\xc5\xa6\x1a\x2e\x27\x5c\x7b\x96\xd6\x11\xed\x15\x3b\xf1\xd2\xd3\x4f\x0f\xef\x1f\x02\x10\x34\x59\xc6\xc4\xe3\x01\x31\x40\xf9\xb9\x5c\xec\x82\x9b\xc3\x9f\xe5\xb7\xaa\xea\x7d\x35\x8d\x19\x07\xef\x6a\x3c\x1c\x71\x21\x29\x01\xcc\x54\xd0\x4b\xeb\x3a\x4c\x9c\xcd\xd4\xf9\x60\xfb\xae\x79\x1c\x66\x19\xca\x2c\xae\xcc\x90\x49\x7e\x66\x6b\x71\xb4\x1a\x18\x61\x19\x6c\x28\xf1\x84\x69\x08\x9c\x15\x18\xbc\x59\xde\x44\xc7\x2d\xab\xd9\x32\xcb\xda\xce\x02\x58\x1c\x1f\x82\xd7\x68\xa5\x0f\x92\x7f\x97\x89\x65\xf2\x5e\xcc\x96\x3e\x0c\xde\xc0\x33\xed\x7e\x60\x46\x17\xf9\xa3\xdf\x97\xe7\x23\xda\xb2\x0e\x62\xe5\xc9\xaf\x62\xf4\xf4\x94\x05\x76\xfe\xd6\x07\x76\x8e\x5e\xc2\x1e\x3e\x4a\xe1\x1e\x09\x40\xad\x6c\x97\x28\x0f\x90\x04\x3d\x78\xd2\xb5\x8b\x27\xf7\xc3\xf8\xff\xac\x2f\x8d\xa3\x67\x47\x55\xca\xe8\xec\x21\x85\x5f\xf5\xf1\x02\xf9\x1d\xd6\x2b\x4a\x3d\xa9\x9a\x83\x6d\xbb\xd7\x05\xb4\x86\x61\xac\x6f\x43\x54\x14\x23\x37\xac\x50\x43\x78\x8f\xeb\x1b\xbc\xed\x10\xbc\x5f\xd1\xd4\x5f\xd3\xb1\xc9\x60\xc1\xbf\x6a\xe4\xe8\xaf\xee\x96\x0b\xe6\xa5\xd8\xb7\xdf\x03\x5a\x90\x19\x9d\x9e\x4e\x4f\x1e\x88\x4e\x21\xcf\x7e\xd9\x2c\xd2\x8f\xdb\xa7\x31\xa6\x8c\xe1\x34\x6a\xd8\xf8\xef\x82\x18\xdf\xf0\x58\x96\x8c\xaa\xd3\xf8\xac\x12\x59\xfd\x3b\xf7\x6c\x56\xf2\x9e\x43\x2d\x7f\x48\xf2\x25\x8f\x89\xfe\x4f\xf0\xaa\x9d\xf8\x28\x80\x46\xe9\x33\x91\x9f\xfc\xf5\x2d\x57\xfc\x6d\xda\x95\xc4\x34\x11\x24\xf8\xdb\x0d\x12\x36\x74\xb4\x26\x56\xc4\x09\x2b\x24\xf0\xb3\x8e\xf8\x59\xe0\x67\x91\xdf\xe0\xd7\x1e\xbf\xf6\x65\xf9\x51\x0a\x83\xab\x52\x71\x3a\x9c\xaf\x90\x72\x83\xdf\x37\x1c\x4c\xf7\x3e\xfb\x67\x71\x3b\xfa\x52\x82\xfd\x40\x68\x62\x6e\x4e\xd3\xed\x07\xa5\x73\xab\x9a\x2a\x9f\xf7\xf9\xda\xff\x46\x93\xf0\x45\x29\xdc\xbc\x26\xc9\xe7\x7d\xb0\xc6\x7e\x65\x15\xca\x37\xa5\x72\x3f\x34\x51\x3e\x29\xad\xcd\xf7\x99\xef\x97\x7e\x21\xd5\xf7\x4a\xbf\x08\xbd\x45\xdb\x6c\x39\x4c\xe9\x07\xf7\x56\xa6\x7f\x75\xec\x94\xf2\x34\x14\x13\x62\x53\xb2\x4b\xf1\x9a\x5f\xce\xc3\x53\xc1\xec\x53\x3a\x4b\x2c\x7c\x70\x55\x6f\x77\xee\x7c\xea\x63\x57\x0b\x98\x8f\xe7\x24\x86\xea\xfc\x94\x90\xbc\xb3\x46\xb3\x9b\xcd\x2b\x7d\x5e\xae\x4c\xf9\x30\xf0\xf0\xdf\xfe\x0d\xe0\xf4\xf9\xef\xff\x9e\x9e\x3d\x7f\x94\x96\x9f\x39\x3a\x5d\x97\x6e\xf2\xcf\x38\x15\x28\x14\xfd\x7c\x11\x01\xb2\xff\x2b\x4c\x8e\xf5\x1a\x4a\x5f\x0b\xc7\xdd\xd3\xff\x0b\x00\x00\xff\xff\x17\x8a\x67\x7c\xd8\xae\x00\x00") func confLocaleLocale_enUsIniBytes() ([]byte, error) { return bindataRead( @@ -4379,7 +4379,7 @@ func confLocaleLocale_enUsIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_en-US.ini", size: 44703, mode: os.FileMode(420), modTime: time.Unix(1447686173, 0)} + info := bindataFileInfo{name: "conf/locale/locale_en-US.ini", size: 44760, mode: os.FileMode(420), modTime: time.Unix(1447733937, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/public/css/gogs.css b/public/css/gogs.css index db86e3de..fb0b7715 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -2470,6 +2470,46 @@ footer .container .links > *:first-child { .repository.new.release .prerelease.field { margin-bottom: 0; } +.repository.watchers .list { + padding: 0; +} +.repository.watchers .list .item { + list-style: none; + width: 25%; + margin: 10px 10px 10px 0; + padding-bottom: 14px; + float: left; +} +.repository.watchers .list .item .avatar { + width: 48px; + height: 48px; + float: left; + display: block; + margin-right: 10px; +} +.repository.watchers .list .item .name { + margin-top: 0; + margin-bottom: 0; + font-weight: normal; +} +.repository.watchers .list .item .meta { + margin-top: 5px; +} +.repository.forks .list { + margin-top: 0; +} +.repository.forks .list .item { + padding-top: 10px; + padding-bottom: 10px; + border-bottom: 1px solid #DDD; +} +.repository.forks .list .item .ui.avatar { + float: left; + margin-right: 5px; +} +.repository.forks .list .item .link { + padding-top: 5px; +} .issue.list { list-style: none; padding-top: 15px; diff --git a/public/less/_repository.less b/public/less/_repository.less index 75070f74..2ff7d053 100644 --- a/public/less/_repository.less +++ b/public/less/_repository.less @@ -885,6 +885,55 @@ margin-bottom: 0; } } + + &.watchers { + .list { + padding: 0; + + .item { + list-style: none; + width: 25%; + margin: 10px 10px 10px 0; + padding-bottom: 14px; + float: left; + + .avatar { + width: 48px; + height: 48px; + float: left; + display: block; + margin-right: 10px; + } + .name { + margin-top: 0; + margin-bottom: 0; + font-weight: normal; + } + .meta { + margin-top: 5px; + } + } + } + } + &.forks { + .list { + margin-top: 0; + + .item { + padding-top: 10px; + padding-bottom: 10px; + border-bottom: 1px solid #DDD; + + .ui.avatar { + float: left; + margin-right: 5px; + } + .link { + padding-top: 5px; + } + } + } + } } // End of .repository diff --git a/routers/repo/stars.go b/routers/repo/stars.go deleted file mode 100644 index 93854886..00000000 --- a/routers/repo/stars.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "github.com/Unknwon/paginater" - - "github.com/gogits/gogs/models" - "github.com/gogits/gogs/modules/base" - "github.com/gogits/gogs/modules/middleware" -) - -const ( - STARS base.TplName = "repo/stars" -) - -func Stars(ctx *middleware.Context) { - ctx.Data["Title"] = ctx.Tr("repos.stars") - - page := ctx.QueryInt("page") - if page <= 0 { - page = 1 - } - - ctx.Data["Page"] = paginater.New(ctx.Repo.Repository.NumStars, models.ItemsPerPage, page, 5) - - stars, err := ctx.Repo.Repository.GetStars(ctx.QueryInt("page")) - - if err != nil { - ctx.Handle(500, "GetStars", err) - return - } - - if (ctx.QueryInt("page")-1)*models.ItemsPerPage > ctx.Repo.Repository.NumStars { - ctx.Handle(404, "ctx.Repo.Repository.NumStars", nil) - return - } - - ctx.Data["Stars"] = stars - - ctx.HTML(200, STARS) -} diff --git a/routers/repo/view.go b/routers/repo/view.go index e9cb7b33..8c62b7e4 100644 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -11,6 +11,8 @@ import ( "path/filepath" "strings" + "github.com/Unknwon/paginater" + "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/git" @@ -20,7 +22,8 @@ import ( ) const ( - HOME base.TplName = "repo/home" + HOME base.TplName = "repo/home" + WATCHERS base.TplName = "repo/watchers" ) func Home(ctx *middleware.Context) { @@ -245,3 +248,33 @@ func Home(ctx *middleware.Context) { ctx.Data["BranchLink"] = branchLink ctx.HTML(200, HOME) } + +func renderItems(ctx *middleware.Context, total int, getter func(page int) ([]*models.User, error)) { + page := ctx.QueryInt("page") + if page <= 0 { + page = 1 + } + pager := paginater.New(total, models.ItemsPerPage, page, 5) + ctx.Data["Page"] = pager + + items, err := getter(pager.Current()) + if err != nil { + ctx.Handle(500, "getter", err) + return + } + ctx.Data["Watchers"] = items + + ctx.HTML(200, WATCHERS) +} + +func Watchers(ctx *middleware.Context) { + ctx.Data["Title"] = ctx.Tr("repo.watchers") + ctx.Data["PageIsWatchers"] = true + renderItems(ctx, ctx.Repo.Repository.NumWatches, ctx.Repo.Repository.GetWatchers) +} + +func Stars(ctx *middleware.Context) { + ctx.Data["Title"] = ctx.Tr("repo.stargazers") + ctx.Data["PageIsStargazers"] = true + renderItems(ctx, ctx.Repo.Repository.NumStars, ctx.Repo.Repository.GetStargazers) +} diff --git a/routers/repo/watchers.go b/routers/repo/watchers.go deleted file mode 100644 index 8626fa23..00000000 --- a/routers/repo/watchers.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "github.com/Unknwon/paginater" - - "github.com/gogits/gogs/models" - "github.com/gogits/gogs/modules/base" - "github.com/gogits/gogs/modules/middleware" -) - -const ( - WATCHERS base.TplName = "repo/watchers" -) - -func Watchers(ctx *middleware.Context) { - ctx.Data["Title"] = ctx.Tr("repos.watches") - - page := ctx.QueryInt("page") - if page <= 0 { - page = 1 - } - - ctx.Data["Page"] = paginater.New(ctx.Repo.Repository.NumWatches, models.ItemsPerPage, page, 5) - - watchers, err := ctx.Repo.Repository.GetWatchers(ctx.QueryInt("page")) - - if err != nil { - ctx.Handle(500, "GetWatchers", err) - return - } - - if (ctx.QueryInt("page")-1)*models.ItemsPerPage > ctx.Repo.Repository.NumWatches { - ctx.Handle(404, "ctx.Repo.Repository.NumWatches", nil) - return - } - - ctx.Data["Watchers"] = watchers - - ctx.HTML(200, WATCHERS) -} diff --git a/templates/repo/forks.tmpl b/templates/repo/forks.tmpl index d1fd0320..e3fd90c6 100644 --- a/templates/repo/forks.tmpl +++ b/templates/repo/forks.tmpl @@ -1,27 +1,23 @@ -{{template "ng/base/head" .}} -{{template "ng/base/header" .}} -
- {{template "repo/header_old" .}} -
-
-
-

- {{.i18n.Tr "repos.forks"}} -

- -
    - {{range .Forks}} -

    - - {{.Owner.Name}} - / - {{.Name}} -

    - {{end}} -
+{{template "base/head" .}} +
+ {{template "repo/header" .}} +
+ {{template "repo/sidebar" .}} +

+ {{.i18n.Tr "repo.forks"}} +

+
+ {{range .Forks}} +
+ +
- - {{template "repo/sidebar" .}} + {{end}}
+
-{{template "ng/base/footer" .}} +{{template "base/footer" .}} diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index d15e7b30..ea077832 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -114,4 +114,4 @@ {{end}}
-{{template "base/footer" .}} +{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/repo/stars.tmpl b/templates/repo/stars.tmpl deleted file mode 100644 index af3193dc..00000000 --- a/templates/repo/stars.tmpl +++ /dev/null @@ -1,61 +0,0 @@ -{{template "ng/base/head" .}} -{{template "ng/base/header" .}} -
- {{template "repo/header_old" .}} -
-
-
-

- {{.i18n.Tr "repos.stars"}} -

- -
    - {{range .Stars}} -
  1. - - - -

    {{.Name}}

    -
    - -

    - {{if .Website}} - {{.Website}} - {{else if .Location}} - {{.Location}} - {{else}} - {{$.i18n.Tr "user.join_on"}} {{DateFmtShort .Created}} - {{end}} -

    -
  2. - {{end}} -
- - {{with .Page}} - {{if gt .TotalPages 1}} - - {{end}} - {{end}} -
-
- - {{template "repo/sidebar" .}} -
-
-{{template "ng/base/footer" .}} diff --git a/templates/repo/watchers.tmpl b/templates/repo/watchers.tmpl index 03aba0e9..bfac88bb 100644 --- a/templates/repo/watchers.tmpl +++ b/templates/repo/watchers.tmpl @@ -1,61 +1,57 @@ -{{template "ng/base/head" .}} -{{template "ng/base/header" .}} -
- {{template "repo/header_old" .}} -
-
-
-

- {{.i18n.Tr "repos.watches"}} -

+{{template "base/head" .}} +
+ {{template "repo/header" .}} +
+ {{template "repo/sidebar" .}} +

+ {{if .PageIsWatchers}} + {{.i18n.Tr "repo.watchers"}} + {{else}} + {{.i18n.Tr "repo.stargazers"}} + {{end}} +

+
+
+ {{if .Website}} + {{.Website}} + {{else if .Location}} + {{.Location}} + {{else}} + {{$.i18n.Tr "user.join_on"}} {{DateFmtShort .Created}} + {{end}}
- - {{template "repo/sidebar" .}} + + {{end}} + + + {{with .Page}} + {{if gt .TotalPages 1}} +
+
+ {{end}} + {{end}} +
-{{template "ng/base/footer" .}} +{{template "base/footer" .}} \ No newline at end of file -- cgit v1.2.3 From ab9411be2ae01970f4c2c092683bd9d9b71c994b Mon Sep 17 00:00:00 2001 From: Unknwon Date: Mon, 16 Nov 2015 23:33:40 -0500 Subject: clean up code --- models/repo.go | 7 ++----- routers/repo/forks.go | 37 ------------------------------------- routers/repo/view.go | 21 +++++++++++++++++++++ 3 files changed, 23 insertions(+), 42 deletions(-) delete mode 100644 routers/repo/forks.go (limited to 'models/repo.go') diff --git a/models/repo.go b/models/repo.go index 0e10e171..174fbd9d 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1865,9 +1865,6 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit } func (repo *Repository) GetForks() ([]*Repository, error) { - forks := make([]*Repository, 0, 10) - - err := x.Find(&forks, &Repository{ForkID: repo.ID}) - - return forks, err + forks := make([]*Repository, 0, repo.NumForks) + return forks, x.Find(&forks, &Repository{ForkID: repo.ID}) } diff --git a/routers/repo/forks.go b/routers/repo/forks.go deleted file mode 100644 index 099f0cc4..00000000 --- a/routers/repo/forks.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repo - -import ( - "fmt" - "github.com/gogits/gogs/modules/base" - "github.com/gogits/gogs/modules/middleware" -) - -const ( - FORKS base.TplName = "repo/forks" -) - -func Forks(ctx *middleware.Context) { - ctx.Data["Title"] = ctx.Tr("repos.forks") - - forks, err := ctx.Repo.Repository.GetForks() - - if err != nil { - ctx.Handle(500, "GetForks", err) - return - } - - for _, fork := range forks { - if err = fork.GetOwner(); err != nil { - ctx.Handle(500, "GetOwner", fmt.Errorf("%d: %v", fork.ID, err)) - return - } - } - - ctx.Data["Forks"] = forks - - ctx.HTML(200, FORKS) -} diff --git a/routers/repo/view.go b/routers/repo/view.go index 8c62b7e4..eeb5d5c0 100644 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -24,6 +24,7 @@ import ( const ( HOME base.TplName = "repo/home" WATCHERS base.TplName = "repo/watchers" + FORKS base.TplName = "repo/forks" ) func Home(ctx *middleware.Context) { @@ -278,3 +279,23 @@ func Stars(ctx *middleware.Context) { ctx.Data["PageIsStargazers"] = true renderItems(ctx, ctx.Repo.Repository.NumStars, ctx.Repo.Repository.GetStargazers) } + +func Forks(ctx *middleware.Context) { + ctx.Data["Title"] = ctx.Tr("repos.forks") + + forks, err := ctx.Repo.Repository.GetForks() + if err != nil { + ctx.Handle(500, "GetForks", err) + return + } + + for _, fork := range forks { + if err = fork.GetOwner(); err != nil { + ctx.Handle(500, "GetOwner", err) + return + } + } + ctx.Data["Forks"] = forks + + ctx.HTML(200, FORKS) +} -- cgit v1.2.3 From efea642d6cf419c9587d44b95ff2bc04e89f7bfe Mon Sep 17 00:00:00 2001 From: Unknwon Date: Wed, 18 Nov 2015 15:37:48 -0500 Subject: add admin op: delete missing repos --- conf/locale/locale_en-US.ini | 2 ++ models/repo.go | 36 ++++++++++++++++++++++++++++++++++++ modules/bindata/bindata.go | 4 ++-- routers/admin/admin.go | 4 ++++ templates/admin/dashboard.tmpl | 10 +++++++--- 5 files changed, 51 insertions(+), 5 deletions(-) (limited to 'models/repo.go') diff --git a/conf/locale/locale_en-US.ini b/conf/locale/locale_en-US.ini index 07926059..30540d47 100644 --- a/conf/locale/locale_en-US.ini +++ b/conf/locale/locale_en-US.ini @@ -760,6 +760,8 @@ dashboard.delete_inactivate_accounts = Delete all inactive accounts dashboard.delete_inactivate_accounts_success = All inactivate accounts have been deleted successfully. dashboard.delete_repo_archives = Delete all repositories archives dashboard.delete_repo_archives_success = All repositories archives have been deleted successfully. +dashboard.delete_missing_repos = Delete all repository records that lost Git files +dashboard.delete_missing_repos_success = All repository records that lost Git files have been deleted successfully. dashboard.git_gc_repos = Do garbage collection on repositories dashboard.git_gc_repos_success = All repositories have done garbage collection successfully. dashboard.resync_all_sshkeys = Rewrite '.ssh/authorized_keys' file (caution: non-Gogs keys will be lost) diff --git a/models/repo.go b/models/repo.go index 174fbd9d..1521f1e0 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1325,6 +1325,42 @@ func DeleteRepositoryArchives() error { }) } +// DeleteMissingRepositories deletes all repository records that lost Git files. +func DeleteMissingRepositories() error { + repos := make([]*Repository, 0, 5) + if err := x.Where("id > 0").Iterate(new(Repository), + func(idx int, bean interface{}) error { + repo := bean.(*Repository) + repoPath, err := repo.RepoPath() + if err != nil { + return fmt.Errorf("RepoPath [%d]: %v", repo.ID, err) + } + + if !com.IsDir(repoPath) { + repos = append(repos, repo) + } + return nil + }); err != nil { + if err2 := CreateRepositoryNotice(fmt.Sprintf("DeleteMissingRepositories: %v", err)); err2 != nil { + log.Error(4, "CreateRepositoryNotice: %v", err2) + } + return nil + } + + if len(repos) == 0 { + return nil + } + + for _, repo := range repos { + if err := DeleteRepository(repo.OwnerID, repo.ID); err != nil { + if err2 := CreateRepositoryNotice(fmt.Sprintf("DeleteRepository [%d]: %v", repo.ID, err)); err2 != nil { + log.Error(4, "CreateRepositoryNotice: %v", err2) + } + } + } + return nil +} + // RewriteRepositoryUpdateHook rewrites all repositories' update hook. func RewriteRepositoryUpdateHook() error { return x.Where("id > 0").Iterate(new(Repository), diff --git a/modules/bindata/bindata.go b/modules/bindata/bindata.go index 45ebb24a..12b5011a 100644 --- a/modules/bindata/bindata.go +++ b/modules/bindata/bindata.go @@ -4364,7 +4364,7 @@ func confLocaleLocale_deDeIni() (*asset, error) { return a, nil } -var _confLocaleLocale_enUsIni = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x7d\xeb\x72\xdc\x48\xae\xe6\x7f\x3e\x05\xdb\x27\x1c\xb6\x23\xe4\x72\x74\xf7\xde\xa2\xa3\xed\x5e\x59\x6a\x5f\xe6\x58\x96\xc6\x52\xcf\xec\xac\xc3\xc1\x66\x15\xa9\x2a\x8e\xab\xc8\x6a\x92\xe5\xb2\xfa\xc4\x89\xd8\x07\xd8\x07\xd8\x7d\xbd\xf3\x24\x0b\x7c\x00\xf2\x42\xb2\x24\xbb\x67\x62\xfd\xc3\x62\x65\x22\x6f\x48\x24\x12\x89\x04\x90\xf9\x76\x9b\x15\x65\xb7\x48\x9f\xa6\xc7\xe9\x36\xaf\xea\x75\xd9\x75\x69\x57\xae\xaf\x1f\xaf\x9a\xae\x2f\x8b\xf4\x65\xd5\xd3\xef\xf6\x53\xb5\x28\x93\x64\xd5\x6c\x4a\x02\x7d\x45\x7f\x92\x22\xef\x56\xf3\x26\x6f\x0b\x4a\x38\xb5\xef\xa4\xfc\xbc\x5d\x37\x2d\x03\xfd\x2c\x5f\xc9\xaa\x5c\x6f\xb9\x0c\xfd\x49\xba\x6a\x59\x67\x55\x4d\x3f\x2f\xe9\x2b\x7d\x5d\x4b\x4a\xb3\xeb\x2d\xe9\x7c\xd7\x4b\xda\x6e\x6b\x49\xbf\x6c\x93\xb6\x5c\x56\xd4\x9b\x96\x92\xde\xe9\x67\xb2\x2f\xe7\x5d\xd5\x73\x4b\x7f\x95\xaf\xe4\x53\xd9\x76\x55\xc3\xb5\xff\x45\xbe\x92\x6d\xbe\x64\x80\x0b\xfa\x93\xf4\xe5\x66\xbb\xce\x51\xe0\x4a\x3f\x93\x75\x5e\x2f\x77\x02\xf3\x46\x3f\x93\x45\x5b\x52\x56\x56\x97\x7b\x4a\x3d\xc1\x8f\xd9\x6c\x96\xec\x08\x09\xd9\xb6\x6d\xae\xab\x75\x99\xe5\x75\x91\x6d\x64\x98\xbf\x50\x7a\xaa\xe9\x29\xa5\xa7\x9c\x8e\x21\x94\x05\x0d\x35\xcb\x3b\x1d\x07\xe1\x92\x46\x9e\x77\x09\xaa\xaa\xf3\x8d\x95\xe6\xcf\xa4\xdc\xe4\xd5\x9a\xb1\xf6\x98\x3f\xa8\xe3\x5d\xb7\x6f\x80\xdb\x0b\xfd\x24\x24\x64\xfd\xcd\xb6\x04\x0e\x1e\x5f\xd1\x57\xb2\xc8\xb7\xfd\x62\x95\x73\x3f\xe5\x2b\x21\xa0\x6d\x43\xc8\x68\xda\x1b\xc0\xd9\x8f\xa4\x69\x97\x79\x5d\xfd\x9e\xf7\x82\xa0\xf3\xe0\x67\xb2\xa9\xda\xb6\x61\xdc\x9e\xe1\x23\xa1\xa1\x67\x5c\x0f\xa5\xbc\x25\x2c\x04\xb5\x70\xce\xa6\x5a\xb6\x82\x46\xce\x3c\xc3\x2f\xae\x85\xf3\xae\x9b\xf6\xa3\x66\xbc\xe0\xcf\x41\x51\xea\x84\xe6\xc6\xed\xe7\x35\x21\x5e\x73\xcf\xf0\x23\x02\xe8\x92\xbc\xd8\x10\x2a\xb7\x79\x5d\x32\x8e\x8e\xf9\x17\xe1\x85\x7e\x25\xf9\x62\xd1\xec\xea\x3e\xeb\xca\xbe\xaf\xea\x25\x23\xfb\x58\x92\xd2\x4b\x4d\x4a\x82\x3c\x97\x76\xd3\xec\xdc\x74\x52\xfa\xdf\xe8\x67\x7a\x21\x3f\x25\x2f\x28\x84\x4c\x57\x92\x47\xd2\x65\xd7\x65\x59\xc8\x58\xba\xf4\x05\x7d\x27\xdb\xdd\x7a\x4d\x58\xfb\x6d\x57\x76\x3d\x17\xba\xa0\xdf\x34\x7e\xf9\x9d\x54\x5d\x47\x1f\x94\xfc\x1a\x1f\x09\x4d\x5d\xbd\xc0\x60\x4e\xf0\x91\x24\xef\xbb\x32\x6f\x17\xab\x0f\x89\xfc\x45\x5f\xf9\x83\x69\xef\xd0\xa4\x32\x21\x29\x11\x49\x0b\xd6\x40\xb2\x68\x0a\xfe\x71\x42\x7f\xa8\xea\xaa\xee\xfa\x7c\xbd\xfe\x90\xe8\x07\x83\xc9\x97\x4c\x40\x5f\xf5\xc0\x82\x26\xa6\x97\x7d\xb9\xed\x78\x06\xd3\x17\x55\xdb\xf5\x8f\xfb\x8a\x88\xf5\xdd\xae\x4e\x8a\x66\xf1\x91\x96\x01\x2f\x69\xb4\xfc\xfa\x3a\x25\x64\x3d\xa0\x85\xd0\xee\xea\x9a\xd0\x93\xbe\x6c\x08\x65\xd4\x4c\x45\xed\x9f\x02\xfa\x28\xdd\xae\xcb\xbc\x23\x90\x32\x2f\xd2\x1f\xf3\xb4\xcf\xdb\x65\xd9\x3f\xbd\x97\xcd\x69\xf9\x7d\xbc\x97\xae\xda\xf2\xfa\xe9\xbd\xfb\xdd\xbd\x67\x2f\x77\x54\x6c\x5d\xd5\x65\xf7\xe3\x93\xfc\x59\xba\xc8\x29\x87\xd0\x78\x93\xce\xcb\x6b\x5e\x6d\xd4\x56\x4a\x54\x5e\x2f\x79\xa5\xdd\xf4\x2b\x6e\x90\x28\x81\x3e\xba\x94\x97\xfa\x37\x09\x4f\x00\xb1\x82\xac\x98\x1b\x5b\x43\x87\x90\xdc\xd2\x04\x9c\xdd\x5c\xfe\xf9\xcd\x51\x7a\x41\xbc\x6d\xd9\x96\xf8\xa6\xff\xa8\xc4\xf7\x29\x8d\xf6\xaa\x3a\x7d\x3e\x4b\xa8\xac\x21\xe4\x34\xef\xf3\x39\xf7\xdd\xcd\x3e\x67\xca\x22\x74\x79\x58\x8a\xcc\x2d\xc1\x19\xbb\x3e\x9a\x96\xa9\x85\x4c\x75\xe8\xf2\x77\x75\xbc\x65\x1e\x40\xe9\x0e\xb3\x17\x82\x33\xaa\x2a\x7d\xfd\xf6\xed\xf9\xe9\xf3\xb4\xac\x97\x84\x99\x74\x5f\xf5\xab\x74\xd7\x5f\xff\xb7\x6c\x59\xd6\x65\x9b\xaf\xb3\x45\xc5\x48\x69\x89\x60\x53\xc2\x92\x0c\x71\x96\x74\xdd\x9a\x58\x14\xa8\xe0\xf2\xf2\x4d\x7a\xc6\x94\xb0\xcd\xfb\x15\x3a\xd2\xaf\x92\xee\xb7\x35\x23\xca\x35\x78\xb5\x2a\x53\x2c\x06\x00\x35\xd7\x43\xbc\xa4\x85\xf6\x75\x96\x94\x6d\x9b\x11\x07\xed\x6f\x18\xcd\x5a\xe7\x21\x68\xa9\x8e\xa8\xbd\x6e\x7a\x9a\xc6\x14\xe5\xa4\x8a\xaa\xfe\x94\xaf\xab\x82\x90\xed\x11\x12\x97\x45\x62\xd1\xd0\xbc\x71\x69\xa2\xcc\x66\x8f\xa1\xe6\x0b\xda\x00\xba\xf4\xde\xec\x1e\x38\xee\xbd\xc7\xf7\x66\x49\xdd\x64\xc2\x25\x98\x37\x17\x55\x97\xcf\x89\x4f\xcb\xbe\xd1\x1a\xd7\xfb\x1b\xd3\x8f\x74\x45\x21\xd2\x08\x82\x71\xcb\x7b\x11\xb6\x00\x26\xae\x9c\x18\x36\x98\x8d\xb2\x99\x70\xec\xc6\x93\xdc\xfc\x0a\x5b\x72\x09\xa3\x31\x27\x36\x61\x46\x5d\xc7\xdb\xed\xba\x5a\x48\xd3\x2f\x25\xcf\x13\x1a\xef\xcc\x8a\x94\x10\x0e\x84\x62\x79\x01\xb9\x50\xaf\x99\x6d\xa5\x11\x9f\x47\xf9\x55\x49\x2b\x67\xb5\x5b\xca\xee\xb4\x6e\x76\xc5\x37\x60\x28\x36\x73\x9e\x9f\xa4\xef\x1a\xea\x30\xa8\xc3\x01\xf8\x26\x8e\x89\x31\xb0\x30\xd0\x96\x9b\xa6\x67\xc4\x69\xb1\x8a\xa6\x67\x5f\x51\x26\x8d\xb4\xcb\x3f\x11\x5b\xec\x1b\x59\x92\x05\x2d\xb9\x05\x57\x4c\x1c\x6c\x47\x3b\xba\x2c\x0b\xe2\x23\xb2\x34\x2c\x2d\xa6\x41\x40\x6d\x76\xb4\x9a\x56\x54\x19\x23\x9e\x25\x12\xaa\x72\xaa\x9f\x18\x12\xd5\x83\x55\x4e\x2b\xb7\xa1\xcd\x93\x27\xfa\x14\x1f\xfa\x3b\xac\x9f\x7a\x95\x5f\x5f\x53\xaf\x3a\x5a\x15\xaf\xd2\xc5\xba\xa1\x25\xf5\xcb\xbb\x37\x1d\x2f\x98\x55\xb6\x6d\x5a\x48\x22\x94\x75\x41\x9f\x2e\x2d\x40\x34\x43\xd4\xbb\xcd\x9c\x7e\xed\x57\x15\x31\x6a\xa0\x9d\x4b\xb0\x94\x44\xa9\xd4\xc4\xae\xa3\x29\x3c\x4a\x69\x09\xd3\x08\x08\x65\x20\x00\x1e\x83\x51\x1d\x83\x5f\x13\x8d\xed\x5a\x5a\x4e\xab\xbe\xdf\x5a\xcb\xaf\xae\xae\x2e\xa4\x69\x97\x7a\x5b\xdb\x79\x40\x19\x98\x83\x35\xcb\x46\x75\xda\xd4\x33\x10\xc9\xae\x5d\x0f\xe8\x87\xc6\x6a\x39\x07\xf0\xc2\x5d\x78\xc2\xff\x5d\x7a\xf4\x00\xcf\x1d\x49\x7d\x7b\x50\x13\xe1\xb8\x84\x9c\x42\x44\xdd\x6c\xb9\xde\x80\xaa\xcf\x35\xc1\x93\x32\x64\x1b\x97\x2f\x12\x0e\xe5\x42\xa6\x0c\x76\xe9\x0d\x0d\x58\xd9\xe8\xe5\x19\xa1\x01\xbc\x14\xa9\xd7\x6d\xb3\xa1\xd4\x17\xf4\xc7\x27\xf8\xee\x9f\x71\x7d\x80\xc9\x8b\x82\xb8\x7c\x77\x94\xbe\x7b\x71\x92\xfe\xe7\xef\xbf\xfb\x6e\x96\xbe\xee\x79\x25\x32\x71\xfe\x9d\x89\x8a\x3e\x45\xd4\x72\xa0\xc4\xb1\x7a\xa2\xbb\x7b\xbc\xb2\xee\xa5\x3f\x22\xf7\xbf\x97\x9f\x73\x12\x11\xcb\xd9\xa2\xd9\x3c\x63\xae\xba\xc9\x69\xed\x73\x0e\x91\xab\xd2\xf1\x65\x59\x17\xf4\xa1\x02\x9b\xe6\x05\xec\x40\xf3\x03\xf1\x4d\x04\xd7\x6c\xd1\xd4\xd7\x55\xcb\x03\xfa\xb9\x06\x35\x98\x48\x4b\xdb\x35\x72\x4c\x2a\x22\xa4\x11\x07\xa9\xae\x6f\x3c\x28\x86\xfa\x96\x13\x75\x42\x13\xa1\xba\x4c\x45\x74\x87\xe5\x4b\x21\x46\x9e\xb7\x73\x1a\x5e\x6b\xf8\xee\x3c\xc2\x9b\xeb\x6b\xde\x6b\x6d\x97\xd0\x16\xce\x25\x55\x36\x8c\x10\x84\x88\x71\x0b\xa1\xfc\x54\x89\xf8\xe4\xf4\x6d\x5a\x7e\x22\x6a\x63\xae\xd7\x36\xc5\x6e\x01\x0a\x63\xd8\x23\x66\xd6\xc4\x22\x3a\x5a\x1b\x0b\xd9\x57\x02\x26\xc1\x5d\x63\x4e\xb4\x20\x20\xe2\x0d\xc6\xac\x49\x90\xfc\x44\x9c\xbf\x0d\x9a\x78\x69\x49\xda\xfb\x11\xec\xa8\x53\xae\x04\x8f\x7c\x41\x33\x4e\x54\x21\xbd\xe8\xa4\x53\x92\x4d\xe4\x4e\x74\xbc\xa3\x13\x4a\x5e\x50\x5f\xe6\x37\xe0\x3b\x1d\x13\x43\x51\x5e\xe7\xbb\x75\xef\xfb\x35\xd8\x44\xac\xa5\x4b\x3e\x24\x85\x79\x93\x05\x46\x1d\x04\xf5\x74\xc3\xb2\x44\x86\x35\xc9\x39\xb2\xd9\x30\xbd\xca\x29\xc4\xf6\x1d\x62\x4f\x25\xa6\x27\xf3\x22\xbf\xce\x97\x49\xfe\x71\xbe\x6b\xf6\x9d\x48\x3e\x29\xb6\x5a\xae\xd1\x2a\x60\x51\x61\xba\x2f\xb3\x44\xc5\xa5\x4c\x8f\x6b\xd9\xa7\x0a\x87\x21\x47\xae\x52\xa5\x1e\xe1\x98\xaf\xfd\x85\x01\xf8\x94\xd5\x4d\x96\x75\xbd\x39\xe7\x41\x76\xee\x30\x24\x38\xe7\xe1\xa2\x05\x16\xe1\x68\x96\x3e\x55\x60\xf3\x4a\x30\xc0\x0b\x51\x0d\x9a\xa6\xa6\xba\xb2\x44\x0d\x54\xfe\x09\xd5\x89\x32\x33\x3d\x20\xa8\xcc\x6e\xa2\x1f\x6f\xf7\x45\x03\xd9\x01\x7b\x09\x95\x36\xb4\x0e\xf6\xf5\xb4\xad\x96\x2b\xe2\xad\xcd\xfe\x48\x90\xb2\x5f\x35\x25\xaf\x9f\xd7\xa7\x4f\xbf\x95\x7e\x2c\x79\x67\x71\x85\x78\x4f\xca\x77\x44\x5c\x84\x31\x25\x63\xe9\x82\xdb\xdb\x01\x39\x3a\x8a\x08\xd0\xf0\xf0\x37\x12\x25\x1c\xd3\x50\x5e\x11\xe6\x29\x93\xf0\x30\x52\xda\x0e\x90\xd2\xb0\x72\x25\x95\xf7\xb3\x65\x83\x83\x8c\xc9\xf7\xbc\x5b\xd2\x79\xb8\xeb\xb3\x65\xd5\x67\xd7\xcc\xba\xb8\xe6\x17\x5c\x03\x6f\xde\x94\x93\x3e\xa0\xac\x07\x29\xf1\x3f\x3a\x9d\x15\x3f\xa4\xf7\x3f\xa9\xc4\xf8\x3d\xf3\xa4\x8c\x56\x51\xb5\xc6\x94\xe8\xf1\xa8\x2d\x45\x60\xb5\x33\xb8\x93\xda\xba\xdd\x16\x7b\x9b\x0a\x88\xee\x34\x50\x34\xfb\x9a\x57\x1f\x98\x2f\xf1\x99\x6a\x51\xd1\x9e\x31\xaf\xea\x9c\x36\x78\xab\x05\x4c\xfd\x3e\xd1\xc4\xdb\xf3\x2b\x00\x2e\x9b\xf9\xae\x5a\x17\x06\x30\x4b\x4c\x88\x24\x11\x52\x67\x3f\x14\xab\x2d\xa9\x92\xbe\x2c\x9a\x96\x25\x12\x8c\xc6\x0a\x1e\x10\x85\x5a\x16\x31\x90\x5c\xf1\x79\x06\xb0\x28\xe7\xa4\x16\x46\x03\x4d\x3f\x8e\x6a\x2c\xd3\x80\x6e\xaa\xae\x7e\xd0\xa3\xa7\x8b\x1d\xb5\x45\x53\xcf\xc9\x54\xb0\x4b\x1f\x3f\xa3\xff\x13\x96\x90\x64\x07\x58\x8e\x11\xcf\x99\xa9\x64\xee\x64\x2d\x46\x5d\x8d\x88\xdc\x4d\xb5\x91\x70\x30\xd6\xb0\xbf\x46\x02\xdd\x4e\xa8\x96\xd5\x25\x6b\x9a\xd6\xf2\x1b\xfa\xe0\x93\xdb\x72\x8d\x49\xc8\x7b\x3d\x5e\x35\x84\x37\x26\x90\x23\x59\x34\xd7\x34\x34\xe6\xa5\x7d\xfe\xb1\xc4\x89\x8c\xf6\xfc\xf7\xac\x07\xfa\x90\xec\x44\x06\x6d\xd6\x85\x3b\xef\x80\xb2\x9b\x76\xa8\xc6\xf0\x40\x8e\x6a\x3b\x12\xb6\x17\xab\xcc\x69\x91\x18\x29\x7d\xf9\x19\xbb\x3f\xb2\xbc\x52\x89\x49\x9e\xb3\x92\xcd\x0d\xa6\x8b\x07\x71\x76\xe3\x67\x8b\x24\x50\x5a\x28\x74\x98\x9d\x37\x8c\xb5\x4f\xa5\x83\x3a\x09\x53\xe3\x02\x54\x17\xc9\xca\x5a\x55\xac\x6d\xa0\x2c\x51\x89\x68\xae\xa8\x45\xba\x04\xac\x4c\x55\x60\xe0\x78\x34\x9f\x7a\xb2\x9f\xd1\xc4\x40\x6d\x60\x2d\x13\x5f\xbc\x91\x75\x11\xb4\x99\xbc\x57\xf5\xd8\x87\xc4\xe0\xde\xc5\xf9\xc4\x53\x56\x1f\x02\x15\x54\x66\xb3\x6b\xaa\x28\x68\x4f\x94\xad\x78\x91\x62\x55\x6e\x59\xfa\xd8\x74\x20\x8b\x35\x9f\xb4\x6f\x54\x7e\x76\x04\xf2\x93\x30\x6c\xa2\x18\x62\x73\xdf\x24\x5d\xc3\x0b\x2e\xfb\xca\x2a\x9e\x57\x44\x0a\x28\x1f\x6f\x76\xa2\x1b\x23\x31\x97\xa7\x8f\x56\xd9\xcd\x51\x7c\xb2\x5a\xe5\x1d\x31\x71\x92\x15\xb4\x58\x31\xb3\x13\x2e\x4f\x3b\x9d\xe7\xb0\x66\xa0\xcf\x03\x95\x4b\xc9\xa6\x1d\xee\xc2\xdc\x43\xe1\x73\xda\x8a\x93\x9d\x20\x19\x85\x02\xd4\x44\x9b\x84\xb0\x4d\xc9\xe2\x73\xb6\x11\x35\x9a\xfc\x4a\xcf\xca\x84\xb6\xc3\x25\x2d\x68\x23\xd8\xa7\xac\xfd\x58\xe2\x94\xa1\xf4\xca\x00\x65\x1f\x32\x62\x85\xb0\x94\x9f\x4c\x6f\x49\x9c\x61\x0f\xd5\x10\xad\xed\x11\xfa\x69\xcb\xa2\xec\x99\x31\x76\x91\x11\x20\xea\x75\xc4\x2d\x3c\x12\x8f\x53\x56\x40\x86\x50\x2a\x72\xfb\x61\x71\x01\xe6\x1a\x3f\xce\x9f\xdd\xef\x7e\x7c\x32\x7f\xe6\x78\xeb\x62\x55\x2e\x3e\x0a\xfd\x55\xf5\xbc\xf9\x8c\x83\x2d\x14\x25\x74\xa6\xe6\x35\x76\xbf\x48\xe9\xa0\xdb\xe2\x5c\x45\xbc\x80\x8a\x11\xe2\x39\x37\x9a\x34\xea\x0c\xb3\x0c\xda\xda\x16\x58\x55\x20\x70\x4f\x90\xc7\x9c\xca\x24\x89\x0d\xc0\xd3\x24\x06\xb2\xae\x36\x55\x3f\xa2\x09\xe6\x30\xb9\xd2\x96\xea\xca\x0c\x49\xa8\x0b\xc3\xc4\x28\x89\x4f\x53\x35\xb4\xaf\x1a\x9d\xec\x73\x3a\x48\x7d\x9f\x12\x6d\xec\x68\x7f\xe2\xce\xd2\x78\x88\x51\xe7\xbc\x31\xd3\x21\x2a\xef\xb2\x5d\xad\xf8\x2a\x0b\xa3\x92\x57\x15\xb6\x0f\x6e\xd7\x68\x39\x80\x32\x94\xea\x59\x20\x7d\xe8\x50\xf9\x68\xa6\xba\x2d\x14\x63\x9e\xce\x1d\xaa\x58\x6e\xcd\x27\x67\x85\x78\x5e\x5d\xca\xd9\x17\x18\x60\x38\x9e\x41\x3a\x40\xf9\x69\xa1\x53\xd8\x47\x4a\x01\xa6\xe7\xbb\xbe\x6f\xf8\x5c\xb2\x66\x72\x90\x32\xd6\xeb\x13\x00\xe2\xa8\xe5\xeb\xc3\x74\x86\x78\x12\x16\x5b\xda\x39\x21\x23\x3a\xe4\x35\x2d\x1a\x6e\x3e\xd1\x0d\x46\xa7\x9b\xa0\x03\x2b\x44\x97\x94\xd7\x37\x5e\xbd\x81\x5e\x70\x83\xfd\x74\x5f\x1e\xb6\xe5\x23\xdf\x1b\xb7\x18\x50\xc2\x7a\x24\xc5\x83\x85\xf2\x0e\xb9\xa2\x62\xb5\xe5\x64\x7b\x9a\x2a\x2a\x3d\x7d\xb4\x31\x7a\x91\xcf\x24\x4f\x9c\x93\xc4\xca\x02\x88\xa6\x51\xa0\xf4\x6c\xd0\x96\x3f\x12\x8e\x31\xd8\xc7\x5d\xf6\x5b\x53\xdf\x34\x59\xb7\x92\xe3\xb7\x75\x8f\x8e\xee\xf5\x32\xd2\x5b\xe1\x5e\x04\x44\xf7\x5f\x78\x03\xe4\x81\x7e\x48\x74\x36\xca\x60\x51\x28\xb5\x5a\x8e\xcd\x9a\xac\x0d\x07\x6f\xc2\xda\x5f\xca\x96\x8f\x77\x00\x8a\x67\xeb\x10\x16\xe3\x41\x38\xa6\xe8\x77\x77\xc7\x10\x35\xe9\xc8\xf6\x7b\x0f\xec\x0e\x95\x9a\xc3\x24\x29\xb7\x37\x34\xb0\xa6\xc8\x69\x64\x37\x50\x56\xff\x8d\xf6\xa4\x1a\xd7\x00\x4d\x42\x19\x52\xe8\x0c\x1f\x04\xca\xc7\xe2\x0f\x09\xef\xfa\x6f\x07\xf2\x2c\x6f\x6a\x9a\x16\x88\x54\xc8\xfa\x39\xba\xe7\x70\xa3\xbd\x98\x90\x7d\xdf\x95\xfe\xba\x03\x5f\x6e\xd8\x97\x97\xaf\xae\xec\x9c\x7b\xf9\x2a\xfd\x58\x6a\xe5\xaf\xfa\x7e\xdb\xfd\x02\x9d\x87\x28\x30\x58\xdb\x71\x91\xdf\xb0\x9c\x29\xc9\xfa\x03\x19\x57\x65\xbe\xd1\x5e\xf2\xa7\x54\x71\x4c\x1b\xb0\x26\xf2\x27\xed\xcb\x81\x2e\x2d\x81\xc4\xf5\xf3\x94\xa4\xed\xce\x3d\xa5\xde\xa5\xfc\x3a\x52\x00\xfe\x9a\xe4\xeb\x2d\x1d\xcd\x58\xe4\x09\xc0\xa0\xeb\x9a\xeb\x09\x2d\x05\x08\x88\x7c\xb7\x21\xe2\x58\xe0\x44\x4a\x05\x1e\x3e\xce\x1e\x05\xba\xcf\xb8\xb2\x82\x56\xff\x1f\xaa\x90\xbf\x59\x2e\x0e\xeb\xed\xaa\xdf\x6d\x14\x51\x75\x9c\x4e\xcc\x94\x20\x20\x85\x7a\x28\x07\x84\xad\x9c\x25\xd2\x9e\x55\x5f\x94\x40\x52\x6f\x54\xf5\x26\xff\x7c\x57\xc1\x4d\x33\x51\x4e\x78\x9c\x2f\x64\x9c\x4c\x87\x18\xaf\x1c\x82\x67\xe5\xd6\x41\x68\x9a\x7a\x02\xa9\xea\xc5\x7a\x57\x1c\xec\x49\xb7\x9b\xd3\x36\xc8\xe2\xf4\x83\xfb\xdd\x03\xae\xb2\xfe\x48\xfb\x76\xed\xe0\x7f\x91\xdf\x29\x7e\xff\x60\x37\x71\x74\xe0\xd5\x33\x46\xea\xee\xe4\x48\xfc\x28\x78\x03\xc1\x59\x61\xe6\xf9\x4e\x78\x7e\x70\xe4\x0f\xad\x85\x1e\xf0\xdc\xda\x67\x55\x05\x8e\x52\x44\x82\x33\x7f\x7d\x98\xb1\x14\x90\xb1\x5c\x5e\x87\xd2\xb7\x93\x0f\x6c\xa3\x05\x84\x5c\x22\x65\xe3\x72\x83\x05\x7a\xb0\x38\x09\x3b\x13\xa5\xcf\xc7\xea\xe6\x03\xe5\x7b\x5a\x62\x13\x15\xb8\x95\x77\xb0\xa0\x4c\x3e\x0a\xd1\xc8\x8b\x11\xef\x18\x17\x64\x30\x3a\x19\xae\xd7\xe5\x92\xf5\x92\xd6\x70\xd4\x9a\x4e\x34\xed\x8a\x02\x16\x12\x9c\xc7\xb0\x9b\xac\x70\x5e\xc3\x73\x8e\x9b\xa3\xf8\x84\xc9\xea\x1a\xaa\xaa\xc5\x15\x70\x70\xce\xd4\x6e\xe8\x26\xb1\xe1\x23\x55\xb7\xe3\x3d\x8a\x8f\x5f\x22\x7f\xc5\xb3\xc1\x12\x08\xaa\x2a\xd1\xc4\xe1\xea\x89\x16\x99\xb3\xdf\x55\x3f\xc0\xbe\xb2\xea\x50\x2f\x31\xae\x58\x2b\x77\x40\x87\xaa\x75\x67\xe6\xf2\x73\x05\x1d\xef\xcb\x8a\x75\x87\x38\x35\x3b\x65\x01\xf2\x66\xc9\x9a\x98\x07\x9f\xce\x64\x54\x22\xa9\x37\x9f\x78\x35\x72\x7b\x9c\x2b\xe5\x44\xe7\xab\x83\xe2\x79\xd6\xf3\x37\x6e\x8a\xca\xe2\x88\x64\x1d\x2e\x41\xfd\xc4\xe2\xce\xd7\xfb\xfc\xa6\x83\x32\xc9\x38\x14\xeb\xb7\xa5\x38\xb3\x1f\x92\x84\x96\xe8\x55\x78\x8b\x42\x2b\xce\x30\xc1\xd7\x01\xbc\xd9\x38\x79\x65\x8f\x13\x34\xb8\x8b\xaa\xa7\x3e\x05\x3b\xba\xee\x4d\x7c\xfa\xe7\xb3\x32\x9f\x62\x24\x3b\xa8\x08\xd7\x93\xba\x53\x4c\x94\x3d\x62\x39\x91\x9a\x61\xa9\x8d\xf8\xb7\xe0\x9a\x04\x61\xc2\x2c\xba\x14\xa8\x53\x76\x54\xff\x63\x91\xfc\x2b\xc2\x21\x9f\x24\xbd\x86\x81\xf7\x32\x9a\x15\xbb\x05\x90\x74\xe8\x07\x92\xae\xa7\x25\xc0\x98\xb6\x3b\xff\xbf\x05\x22\x4b\x8a\x5c\x2c\x31\xa0\xa9\x5b\x55\xdb\xb4\x81\x66\x39\x44\xa1\x27\xdb\x40\xd6\xe6\xfb\x8e\x12\x27\x0b\x56\xb1\xb7\x79\xdd\x5d\x97\xd0\xb5\x6f\xd2\x6b\xbe\x56\x9e\x69\xd3\x2c\xba\xcb\xdd\xff\x81\x96\xe5\x94\x86\xa6\xc3\xdd\x05\x73\x17\x4c\x54\xdc\xb4\x5c\xbe\x40\x9f\x8b\x3e\x00\xab\xbe\xa6\xce\xfa\xc0\x64\x36\x42\x01\xc4\xe7\xe8\x2a\xcd\x7a\xf3\xa9\x0c\x11\x71\xfd\x47\x47\x1e\x60\x5d\xaf\x13\xe4\x0e\x26\x9e\x26\x69\x14\x0a\x1d\xdc\x04\xcf\x6f\xe2\xd1\x73\xd1\xe0\x7a\x9d\xd6\x48\xa9\xad\xf0\xc2\xe0\xb5\x32\xa8\x10\x8a\x1c\x7f\x68\x4a\xe4\x2a\x3e\x9b\x53\x17\x17\xab\x68\x75\x5e\x21\x27\x95\x9c\xd1\x02\x4d\xde\x73\xd3\x1f\x12\xb9\x8c\xcf\x9c\xde\xfe\x44\x2e\xe7\x45\xe8\x55\x3d\x7c\x9f\x9a\xb2\x9e\x6f\x53\xac\x88\xa8\xe6\x6f\x2d\xc9\xdb\xb0\xe9\x4d\xff\xde\x90\xcc\x01\xf5\xfb\x9f\xe8\x8b\x8f\x01\x75\x12\xdd\x40\x0e\x34\x29\x90\xb4\xab\x9e\x57\xd8\x05\x2d\x0c\x12\x7b\x8e\x35\x85\x0e\xf2\xe0\x0e\x50\xee\xbc\xb0\x6f\x9a\x8f\x9c\x99\x1e\x2f\x6d\xf9\x52\x38\xd1\xb4\xbd\xb0\xef\x84\xf5\x00\x9b\x19\x36\x07\x96\xd0\x71\x93\x11\x6c\x09\x2c\x2d\xf0\x84\x59\xde\x2c\x80\xdf\xe6\x3d\xb1\xc5\x5a\xce\x6a\xc2\xa1\xc2\xa2\x9a\xed\xaa\x70\x57\xde\x5c\x0b\x9b\x87\x08\x2a\x3e\x24\xde\x6a\xc5\x0c\x56\xa6\x34\xc7\xca\x62\x3a\x95\x91\xff\x95\x3e\x55\xe7\x03\xf6\x85\x0f\x3d\xb3\xe3\xb2\xd9\x6e\x08\x61\x41\x13\xfc\x4c\x54\x4b\x16\xab\xc8\x94\xbc\x9f\xa6\xa7\xf2\x61\xa7\xff\x5d\x85\x31\x55\x74\x94\xd8\x02\xef\x81\x8d\x8d\x4e\x84\xeb\xb4\xda\x52\x79\x65\x7d\x3b\xdc\xd9\xd9\xac\x43\x0a\x31\xe1\xda\xfd\x11\xa4\x00\xbe\xbe\x08\x4e\xae\xac\x7f\xc6\x91\xb6\x0e\xee\xc6\xf8\xc6\x87\x0f\xe2\x04\xb6\x2f\xe7\x29\x6b\x84\x89\x70\xe8\x80\xa8\x03\xdd\xe4\x74\xb6\xfc\x54\xe5\x4e\xf7\x44\xb3\xc5\x56\x3c\xba\x8b\xbe\x60\x0b\x1e\xdc\xb7\x8f\x4d\xcd\xf8\xf2\x4a\xef\x83\xde\xe8\x67\xb2\xdb\xf2\x05\x4b\x30\xe0\x5f\x90\xe0\x06\x1c\xe7\x07\x47\x36\x0c\xdd\x8a\x39\x69\x46\xc0\x0b\x3b\xc7\xc1\x10\x66\x66\xcb\x67\xc2\x84\x4c\x97\x50\x31\x04\xf1\x4a\x18\xb0\x18\xb5\x9f\x01\x32\xe5\xca\x17\xc3\xa7\x9d\x31\x5d\x35\xfb\x74\x5d\xd5\x1f\x3b\xc5\x26\xf3\xb1\xf0\xfc\x0a\xad\x15\x11\xe1\x4e\x4c\x8b\xe4\x73\x6c\xc9\x64\x37\x51\x83\x15\x6e\xf7\x55\x72\x27\x77\x8c\xe4\x49\x58\x7f\x8a\xb7\x3b\xb3\xeb\x92\xc5\x64\x30\x35\xbb\xdf\xa3\x51\x36\x4d\xa7\x3a\x52\xcf\x44\x38\x0d\x7a\x17\x85\x52\x9c\x3b\x08\x9d\x92\x63\xbb\x55\xc4\x9a\x4a\xec\x1e\xd0\x3a\x80\x15\x9a\x55\x1b\xb1\x0c\xfc\xc5\x6e\x09\x31\x3f\xee\x34\x81\x6c\xd8\x9d\xc4\xbd\x0f\xaf\x46\xde\x36\x76\x07\x69\xdc\xd0\x32\x8f\x6c\xd3\x17\x0c\x60\xcb\x8e\x3a\x3b\xa4\x0f\xad\xc0\xb4\xfc\x77\x90\x89\x11\x41\x78\x6f\x24\x13\xef\x18\x44\xb3\x8e\x44\xbb\x13\xbd\xaf\x70\xf9\x8c\xd9\x20\xff\x2d\x6e\xf8\x9c\xde\x81\x0f\xe4\xd9\x00\x44\x0f\xec\x11\xe4\xa4\x04\x6d\x6d\x1d\x94\x9e\x07\xbd\x1f\xad\x15\x2b\xb7\x27\x2c\x84\x03\x57\xea\x2e\x66\x66\xea\xc3\xca\x56\xb9\x2e\x84\x4d\x86\xd8\xa5\xd4\xb8\x6b\x94\x2a\x08\x55\x38\x60\x74\xfe\x5c\x71\x2c\xdc\x87\xef\x08\xc4\x30\xd1\x01\xa8\x6d\x62\x7c\xde\x2c\xcd\xc0\x21\x64\x64\xdb\x96\xc8\x83\x36\xda\x81\x0a\x6e\xc4\xc2\x22\x76\x05\x6e\xd5\xe0\xb6\xde\x73\x29\x3a\x31\x6a\x5d\xcc\xef\xf1\x65\x29\x4e\x8d\x44\x74\xcc\xa2\xae\x26\x2b\x73\x76\xb9\xc2\xa2\x5d\x27\xe9\x87\x30\x2e\x1d\xee\xa9\xa6\x0c\x00\x6c\x38\xca\xe0\xfb\x09\x85\x22\x46\xa3\x62\x87\xf1\xdf\xaa\x16\x6b\x09\x77\x7b\x17\x31\x90\xf4\x14\x1c\x85\xe6\x4d\x54\xd7\xc6\x4f\x7e\x1a\x36\xee\x27\xfc\xe7\x81\xd6\x5b\x06\x17\xd3\xfb\x37\x09\x75\x09\xd4\xe8\x6f\x41\x0b\x4c\xf3\x40\xa9\xc6\x60\x21\x88\x6a\x2c\x5d\x72\x16\xa9\xe5\xa1\x60\xff\x2a\x55\x3c\xef\xdd\xff\x04\x2d\x7c\xd4\x96\xd7\xc2\xbb\x5e\x0e\x96\x03\x77\x6f\xb0\x73\x8e\x16\x06\x65\x40\x8e\x50\x92\x0e\xa4\x03\x25\x6a\x27\x24\x70\x33\x72\x36\x61\x0c\x51\x12\x44\x09\xa5\x06\x6c\x21\x2c\xa8\xc2\xd2\x08\x66\x82\x72\x50\xe9\x46\x7a\xe5\x78\xe2\x8f\x71\x12\x23\xac\x08\x2c\x4c\xf9\x68\x63\x16\x29\x56\x4f\x76\x1b\x46\x84\xdc\xb3\x3b\xab\xaf\xd1\x25\xda\x91\x1e\x7f\x56\xd5\x72\x45\xe3\xaa\x36\x7c\xbb\x0c\x72\xb2\x2b\x4c\x7f\x3a\xe5\x5f\xc4\x50\x9a\x65\xcd\xba\x2b\x6e\x41\xcc\xbc\xdc\x06\xf3\x63\xd7\xb7\x4d\xbd\x7c\x76\xda\xf0\xb1\x91\x35\x3a\xbc\x09\xfe\xf4\xe3\x13\x4d\x27\xa6\xc9\x73\xc8\x36\x81\x2f\xab\xfe\xd5\x6e\xfe\xa0\x4b\x97\x6c\xa4\x8a\x7b\x97\x3c\x30\x5d\x55\xc3\x02\xb1\xc1\xdb\xd7\x0e\x2d\x30\x64\xa5\x85\xde\x35\x6b\x5a\x25\x71\x91\x66\xb3\x91\xf9\xa5\x0d\x60\x23\x90\xe8\x3f\x6c\x11\xca\x1a\x98\x2b\x5b\xc5\x0f\x55\x38\x73\x64\xee\xe7\x47\xa7\xcd\xc4\xbd\x48\x4f\xa2\x02\x17\x03\xe3\x72\xb5\xee\x83\x6d\x83\x75\x24\xa9\x2b\x06\x41\x61\x5c\x0c\x13\xc9\x6a\x27\xaf\xa2\x31\x25\x0b\x4e\x02\xa8\xc3\xca\x53\x51\xea\x89\x48\x4c\x9c\x66\x6d\x8a\xac\x50\xb2\xfe\x5b\x48\x2b\xa0\x5f\xde\x2b\x4c\x85\x0b\xc1\xd7\x2b\x73\x98\x60\x07\xab\x5c\x19\x9b\x8c\x5e\xd9\x9a\x8d\x20\x60\x6c\x8a\x13\xcf\xd9\x86\x30\x53\xbc\xcd\x7a\x11\x32\x35\x31\x62\x12\xc6\x26\x24\x49\x27\x0d\x66\xdb\x5f\xc8\xd4\x46\xed\xfa\x81\x5b\x73\x5f\xc0\xd7\x30\xa6\x63\xa0\x83\xc6\x02\xdd\x88\xce\xd4\x1b\xd5\x84\x20\x83\x0d\x60\xfd\xa9\xe7\x6d\xa3\x57\x68\xa9\x25\x62\x4e\xe8\x98\xd3\x97\xd1\x62\xe6\x4e\xc0\x64\x51\x4c\x72\xa0\x5c\xf9\xaf\x69\x91\x13\x27\xe8\x9b\x8f\x44\x4c\xe3\x22\x48\x3f\x54\xc8\x71\x18\x3b\x6b\x28\x7f\x39\xf6\xec\x61\x78\xfa\xd0\xab\xe8\x83\x2c\x26\xe0\x2c\x5a\xab\x33\x8b\x12\xcd\x10\xcc\xc1\xd9\x76\xa4\x10\x4e\xa2\x8c\x40\x6d\x7f\x1c\x07\x20\x09\xab\x66\x20\xa8\x6f\xf9\x43\x7f\x87\xd3\x12\xd5\x1f\x2c\x17\x62\xe0\xbb\x3a\x60\xa0\x42\x0e\x99\xa0\xc2\x0d\xf2\x82\x8e\x92\xb0\x7d\x3c\x96\x0a\xaf\x38\xbb\x53\xc3\x5f\xbd\xd1\xb7\x22\x2f\x35\x11\x6b\x00\x80\x82\xf0\xce\x21\x02\xbf\xbc\x56\xc1\x6a\x51\x6b\x0d\xb5\x6a\xc4\x1c\x10\xd5\x19\xcb\x5c\x89\xf5\x46\x7a\x7c\xf1\x9a\xf6\x0c\xd7\xa0\x55\xfa\x73\x4e\x92\xb4\x74\x61\xef\x34\x1a\x4c\x6c\x43\x9e\xeb\x64\x7e\x29\x6e\x0a\x54\x94\xc4\x12\x77\x83\x1a\x0d\x48\x06\x13\xe7\x0b\x8e\xcb\x2e\xd0\xf2\x48\x6b\xe8\xc9\x70\xb7\x72\x43\xfd\x86\x30\xeb\x74\x8d\xbc\xb4\xb6\x37\xcc\xff\x03\x73\xad\x5c\x30\xb4\x07\x07\x1f\xd8\x89\x11\x24\x34\x1d\x29\x2f\xe1\xd6\xf1\x0f\xeb\xb0\x72\x90\x70\x2a\x43\x36\x32\x39\x99\x9e\xa9\x4c\x16\x9b\xe2\x2c\x5b\xab\x27\x1e\xf3\x5d\x7c\x86\x09\xdf\x9f\xc3\x6f\xe1\x32\xe1\xa8\x02\x52\xbe\x98\x6c\xd6\x51\xb4\x34\x3d\xe0\x37\xa9\x6c\x84\x62\xeb\xc0\xad\xc8\xe9\x42\x29\x22\x30\x23\xa6\x5a\xf6\xe5\x9a\xed\x7f\xb5\x75\x7f\x03\xaa\x43\x8f\x8c\x02\x14\x28\x38\x89\x96\x5e\xc4\x15\x54\x84\x6a\x3a\xab\x8c\x20\x68\xb9\xc1\x0e\x40\x8e\xf2\xb6\x5f\x9f\x1c\xbf\x7d\x7b\x7e\xe5\xb7\x69\x5e\x07\x75\x41\xc2\xc4\x37\xce\x62\x6e\xd4\x2f\xb3\x9b\x73\x13\x18\x43\x78\xcb\x3d\x2d\x71\x08\x2e\x64\x53\x56\x3b\x7d\x2e\x1b\xf0\x9e\x86\xfb\x62\xbc\x3c\xea\x7f\x71\x68\xfe\x92\xf7\x2c\xdf\x7c\x48\x4c\xd9\x7d\xce\x7f\x93\xf0\xbe\x20\xb8\xa3\xc1\xd2\xf3\x57\x39\xde\x3a\x9f\x3a\xd0\x14\xa3\xfb\x03\x30\xe9\x5d\x8e\xa3\x11\xe1\xbe\xc1\x5e\x71\x9d\xe2\xbe\xfb\x88\xd5\xa1\x4d\x8b\x05\xc3\xc8\xdd\xd5\xd5\x6f\x3b\x08\x68\x7c\x30\x22\xe6\xc1\x86\x98\xf3\x6a\x2d\x1b\xca\x5f\xdc\x0f\x49\xe7\xaf\x81\x05\x79\xd0\x38\xfd\xfa\xb1\xdb\xb2\x1d\x2b\xed\x0d\xdd\xd3\x7b\xbb\x2a\x65\xed\x1a\x5b\x71\xdd\x7b\x46\xc7\x18\xbe\x05\xa7\xe9\x23\x88\x67\xa3\xea\xd8\x8b\x6c\x21\xaa\x38\x67\x0f\x04\xba\xd5\x74\x5e\x2d\x2c\xf2\x46\xfa\x3f\x41\xfc\x1f\x68\x93\x5d\xd6\xfc\x38\x1e\xea\x29\x99\x70\x84\xb5\xfb\x29\x5f\xef\x62\x65\x09\xb7\xce\x65\xba\x47\x09\xcc\xe3\x7d\x59\x18\xfc\xc0\xdb\x91\x33\x88\x1a\x7e\x02\xd2\xfa\xdb\x7d\x9e\xd8\x2f\x92\x45\xbf\x6f\x12\xf4\x44\x15\xd0\x43\xef\x39\xe4\x99\x19\x3c\xe7\xc1\x16\x1e\xa9\x13\xb3\x11\xf8\xb9\xe4\xeb\x5e\x54\xcf\x69\x30\x9b\xcc\x5a\x30\x88\x32\xa4\x18\xbd\x22\x74\x1c\xac\x5b\xb4\x15\x6c\xf9\x25\x9d\x7d\x25\xd3\xc0\x4f\xd2\x25\xfa\x76\x2f\x89\xee\x09\x45\xb3\x65\xd5\xd3\x91\x9b\x3d\xb6\x60\xf9\x9d\x10\xdb\xa0\x9d\x0c\x5e\x96\xf2\x65\x29\xa3\xa2\xbc\xe9\x0b\x2c\x54\x66\x2c\x6a\xea\x0a\xe0\x0f\xfd\x3d\x51\x4a\x01\xcd\xc7\x93\x6f\x3f\x9a\xac\xaa\x2b\x5e\xf8\xaf\xe9\x0f\x6d\xea\x72\x04\x88\xc9\x54\xe4\x5b\x54\xa2\xfa\x1d\x39\x84\xbb\x7a\xd4\x12\x4f\x67\x45\x4d\xf0\x82\x79\x51\x63\x71\xd5\xa0\x03\x6d\x48\x48\x9f\x23\x41\x5d\x2b\xa9\x27\x34\x0b\x9f\x44\x1c\x12\x67\xcb\xd7\x96\xf2\x90\x8f\x80\x8f\x92\x3d\xdf\x04\x8b\x52\xfa\xaf\xfa\x09\x9d\xf4\x32\xff\x5d\x52\x2f\xdd\x0f\x4c\x7b\xa7\x84\xd0\x1d\xd0\x48\x0f\xaf\x75\xbf\x5e\x31\x3d\xac\xe1\x76\xfd\x34\x1b\x3f\x65\x7c\xdb\x60\x0b\x21\xb2\x9e\x48\xd4\x69\xd4\x7c\xe7\x9c\xd7\xa8\x38\xcf\x85\xb9\x87\x79\x8a\x29\x4f\xf2\x78\x99\xc3\x68\x74\x4e\xcb\xf4\xde\x33\xc1\xb6\xad\x71\xab\x55\x27\xef\x4c\xfd\x56\x83\xd9\x53\x88\x99\x2c\x64\x3b\x3b\xb3\xf5\x10\x9f\x4b\x55\x0f\x34\x0d\x15\x6d\x03\x2a\x8a\xe5\x81\xef\xcc\x93\x97\xaf\xaf\xe0\x39\x43\x73\x0d\x4f\x07\x73\x0f\x62\x9b\xe2\x99\xab\x93\x77\x88\xaa\xeb\x44\x72\xa8\x2b\x20\x9e\x57\xaf\x5d\xf9\xdb\x74\x10\x7b\x93\x93\xae\x56\x16\xf2\x3e\x5f\x9b\xdd\x53\x02\xc6\x8c\x9a\x5f\x4b\xa2\x16\xe4\x44\x1c\xa1\xe3\x2b\x1d\x33\xab\xca\x43\xa7\xad\x44\x58\x84\xf1\x1d\x65\x18\xd7\x8e\xf3\xc0\x0b\x87\xdd\x07\x62\x96\x03\x27\xe0\x60\xde\x42\xa3\x3f\xde\xe3\x0a\xde\xa4\xb7\x37\x19\xab\x9a\xb1\x2f\x6f\x6f\x7c\x42\x20\xc0\x50\x46\x15\x01\x3b\xe3\x8b\x0b\xcc\xd3\x7f\xfc\xef\xff\xf3\xf8\x84\x3b\x7e\xd2\xb7\x6b\xfa\x52\xf9\x90\xe1\x05\x91\x52\x41\x7a\xfe\xaf\x24\xe7\xef\xd5\xd2\xe2\x17\xf9\x4a\xec\x37\x16\x1b\xe5\x77\xaa\x1f\xc6\x47\xa2\xbf\x78\xcd\x25\xea\xd3\xcc\x8b\x2d\xe1\x43\x96\x4e\x3c\x1d\xb0\x42\x16\xf9\xdb\xae\x5a\x7c\xcc\x44\x37\xf0\x34\xfd\x33\xff\x4a\xe1\xce\xaa\xbb\x04\x73\x1e\xc7\x46\x40\x5e\x03\x5e\x14\x1a\xfd\x82\xb5\xaa\xe9\xbd\x67\x3b\x79\xbc\xeb\xdd\x98\x25\xa1\x01\xb2\xa7\x4e\xb2\xdd\xb1\x71\x11\x4f\xa9\xb5\x76\x41\x29\x70\x7b\xe2\x44\x96\x51\x82\x1a\xdc\x3d\x61\x54\x07\x9a\xa7\xee\x8a\xdb\xda\xe4\xe6\x8e\x2c\xaf\xe9\x62\x23\xb2\x79\x4e\x43\x56\x41\x3b\x71\xfc\x50\xf9\x60\xdf\x96\x38\x3e\xd0\x9f\x84\xd8\x2c\x5b\xa1\xe9\xa5\x23\xbb\x6b\xf6\x39\x2e\xd9\x90\x6e\x57\x8e\x7c\x73\x9a\x2f\xb5\x22\x9c\x1b\x9e\xeb\x67\x42\xe9\xfc\xfb\x8a\xfe\x8c\x1c\xab\xd9\x0d\x7b\xec\x7e\xbd\xce\xe7\x25\x92\xdf\xe0\x83\x16\x0c\x71\xfa\x9e\x66\x01\x0a\x30\xf7\x23\x61\x34\x54\xbd\x10\x1f\xbe\x12\xf5\x6b\x90\x0b\x46\xf9\x4c\x70\x7d\xd3\xe6\x6c\xe4\xfb\x2e\xdf\xcb\x4f\x42\x91\xfa\x67\xbf\x92\x2f\x49\x86\xc9\xb8\x80\xc2\x62\xdc\xc1\x43\x34\xd4\x15\x70\x61\xdf\x89\x75\x60\x36\xee\x88\xe5\x0c\xdc\xc3\xd3\xc5\x20\xff\x5a\x0e\xb8\x2f\xf8\x78\x6b\x69\x39\x78\x71\x6a\x26\x6d\x2e\x7d\x43\xcb\x48\xee\x38\xce\xe4\xcb\xe5\x14\x62\x46\x7a\x8a\x3d\x50\xd3\xcc\x84\xff\x9c\xff\xba\x54\xa2\x49\x95\x7e\xe8\xaf\x33\x87\x97\xe8\x09\x7c\xb2\x15\x7f\x74\x9f\x3c\x1b\xce\x45\x90\x55\xb3\x40\x31\xc7\x65\x12\x2d\x2f\xe4\x87\xd9\x0b\xc2\x7f\x9b\xb9\xf2\x27\xfc\x33\x5d\x8f\x6a\x71\x93\x1b\xce\xed\xa0\x99\x10\x86\x9a\x9a\x04\x93\xe6\x42\x48\x69\x71\x33\x05\x4c\xa7\x99\x3a\x82\x3d\xa7\x84\x90\xb4\xa2\x8a\x59\x0e\x1f\xd4\x0c\xd1\x7c\x1a\x9e\xb6\x39\x76\x99\xc2\xe1\x44\x3f\xc7\xfd\x0c\x80\xa4\x9b\xf9\x04\x28\xeb\x88\x3c\x1c\x0d\x7c\x08\xa4\x6a\x4c\xc7\x73\x86\xb3\xe7\xe7\x87\xa6\x76\x38\x41\x92\x99\x91\xe4\xb4\x28\x9d\xc3\x07\x80\x20\x41\x70\x24\x83\xa8\x19\x57\x99\x36\x16\xd5\x07\x84\xf6\xf9\x9c\xb2\xef\x17\xc0\xa6\x2b\xcc\xb8\xf2\x59\x82\x3a\xcb\x54\xe6\x62\x35\x47\x55\x86\x79\x24\xec\x64\x22\xf8\x09\x22\x9c\x10\xb8\x9e\x28\x71\x2b\x45\x0d\x61\x0e\xd6\x3c\xa2\x1b\x2d\x79\xcb\xf4\x7a\x08\xf6\xfd\x3f\x5c\xf5\x81\x72\x2a\x6d\x41\xc6\x1a\xe7\xcc\xd8\x2d\xc8\xf1\xcf\x63\x58\x9a\x80\x87\x4e\x81\x76\x1a\xd1\x84\xc4\x6a\xde\xcf\x5d\x57\x0b\x55\x18\x4d\x15\x92\x59\x2e\xb2\xf9\x8d\x96\x91\x79\x86\xbb\xe5\x81\x22\x1b\x36\x56\xc1\xe1\x49\x8b\x9c\xb9\x84\x89\x22\x9d\xba\x6b\xb3\xbf\xf4\x38\x67\xc6\x9b\x11\x8c\x59\x98\x37\x75\x93\x20\x4c\xa5\x00\x39\xc7\xc7\x14\x88\xa8\x51\x55\x11\xc2\xbb\x80\x38\x26\xd8\xc5\xeb\x64\xc3\x6c\xa1\xe3\x4a\xbc\x81\xbd\x4e\xfb\x05\xe5\xd8\xf8\x95\xf9\xaa\x68\xcd\xcf\x1a\x18\xa4\xe2\xe7\x2d\xed\xf8\x02\xd2\xd0\xa8\x04\xaf\x24\xcc\x02\x81\xc8\x77\x7a\xff\xfd\xb7\x1f\x3a\x9e\x06\x7f\x21\xf1\xfe\xbb\x0f\x74\x1a\xbd\xff\xfe\xfb\x0f\xb8\x89\x18\x15\xce\xae\x59\x11\x37\xae\x01\x05\x0d\x7a\xdb\x96\x9f\xaa\x66\xd7\x89\x8c\x86\x4f\xcf\x1f\x3e\xcb\x54\x7c\xee\xe3\x25\xee\xdc\xc6\x07\x2b\xbc\x70\x59\xf1\x0a\xaf\x77\x9b\x4c\xc7\xd8\x09\x07\xb0\x5f\xae\xb8\x61\x20\xcb\xb9\xc9\x5f\xdd\x6f\x1e\x6e\x55\xf0\x60\xa9\xf3\x76\x08\xff\x17\xf9\xf5\x0c\x03\xe1\xa1\xff\xea\x5a\x6a\x82\x3b\x8c\x2b\xf1\x7c\x67\x99\xd9\xdd\xa6\xdc\x94\xfd\x2c\xe6\x4a\x16\x85\x05\x5d\x8e\xb3\xb4\x17\x1e\x44\xe7\x0d\x26\xbf\x21\x78\x5b\x02\x31\x06\xf7\x0e\x3f\x07\x99\xb7\x55\xd6\x46\x05\x94\xd5\x7a\x2a\x51\xd0\x01\xae\x15\x53\xb2\x0d\x7d\x1d\x9a\xa4\x3d\x57\x87\xfd\xfc\xca\x5a\x44\x9e\x20\xa1\xf5\xda\xd5\x73\x4d\x18\xaf\x17\xd0\x77\xe3\x4a\x80\x87\xaa\x26\x9f\x02\xfd\x95\x4d\x6c\x1b\x8d\x21\x75\x81\x0f\x4b\x16\x05\x93\xda\xf3\x3b\xda\x8c\x94\x71\x9a\x68\x0e\x5e\x74\x24\xa0\xc3\x0f\x38\xb6\x39\x75\xf1\xad\x10\x27\x45\xa0\x55\x9d\x99\x5b\x80\x9e\x1a\x88\x59\xb2\x29\x9b\x8c\x88\xc8\x88\xfd\x58\x55\xbf\x7b\xd0\xe7\x2e\xba\x34\x34\x1f\x3e\xb9\x32\xe6\x89\x0c\x57\x6b\x59\x40\xe3\xf1\x33\xfd\x71\x78\x1d\x98\xe7\x58\xff\xb8\x15\xea\x3e\xfd\xb1\x24\xd9\x17\x6d\xd1\xf9\x7d\x3b\xce\x5f\x34\xeb\xc6\xef\xeb\xf8\x35\x04\x10\x7d\xeb\xfd\x62\x20\x9b\x49\xb6\xa7\x6d\x5d\xbd\x9c\x30\xd8\x79\x04\x72\x62\x30\x92\x31\x30\x3e\x8b\x33\x9d\x9f\x8a\x74\x10\xde\x2a\x16\x2b\x61\x5c\x8b\x9a\x70\x01\xd4\x29\x7c\x27\xc1\xa6\x34\xfb\x22\x65\x84\x9a\x7c\x96\xd9\x43\x13\x08\xbe\xcb\x0e\x94\xfb\x5a\xf3\x61\x5d\xfe\x74\xd3\xfe\x4c\x2c\x3d\xbd\xe3\xd2\x50\x0e\x41\x81\x0c\x1e\x1e\x86\x34\x93\xb5\xc1\x39\xd1\xa5\x18\xcc\xe8\x41\x83\x53\xd4\x4e\xa8\x9b\x86\x33\x2c\x18\x70\xbf\x6f\x52\x77\x44\x43\xf4\x33\xde\x25\xf2\x94\x0b\x9b\x87\x1f\xd6\x86\x96\x9f\x0d\xaa\x85\x97\xf6\x53\x98\xe6\x0d\x1b\xd4\x16\x9e\xa6\xfa\xa5\xf9\xd1\xe9\x71\x78\x6a\xb4\x91\x37\xac\x0a\xdb\xad\xb1\x41\xe0\x26\x54\x7e\x5c\xcb\x1d\x9e\x01\x21\x80\x14\x4b\x34\xbe\xad\x80\xcb\x4b\x78\x29\x35\xcd\xe0\xdc\x79\xb9\xc8\x61\x85\x0b\x07\xaf\x9a\x75\xbd\x79\x11\x8c\x9e\x40\x38\x1c\x86\xd5\xcf\x66\xcd\x61\x50\x30\xe6\x69\xae\x7a\xd3\x87\x0c\x30\x35\x2f\xfb\x3d\xcf\xab\x98\x4a\x30\x72\x45\x71\xdc\xfd\x10\xee\xd4\xc4\xde\x9e\xa0\x8d\x27\xbc\x5d\x17\xca\xea\xfe\x05\x3f\x84\xe1\x29\x2a\x07\xc2\xfc\x04\x19\x60\xb5\xdb\xa4\x32\x39\x42\x93\xbe\x29\xa9\x51\x6c\xf1\x85\x9d\x2f\x85\xf1\xfe\xc8\xee\x78\xc6\x59\xf1\x4d\x14\xce\xa6\x10\x9a\xfe\xbd\x4b\xd7\xfa\x51\x93\xee\xe4\xd6\x8c\xa4\xfd\x63\xd5\x53\xe9\xff\xf4\xc1\x68\x94\x8e\x02\x59\xc8\x4b\x41\x9f\xfe\x67\x04\x35\x3c\x56\xfb\x3c\xd1\xfe\x82\xa0\x4a\x33\x93\x2c\x34\x5f\x77\x5d\x22\x15\x41\x8d\xf3\x84\x90\x0c\xbd\xe7\x0b\x67\x92\x7a\x4d\x47\x7c\x66\x04\x8a\x4d\x77\xdd\x35\x8b\x50\x03\x11\xb7\xf5\x2d\x31\xd5\xb8\x9c\xab\x51\xb5\x6e\xe5\x2b\x4c\xbc\xf0\xa5\x0a\x0e\x9f\x45\xeb\xc3\x2e\x39\xe9\x97\xbb\xce\x98\xae\x4b\x61\x8b\x9d\xb7\x5c\x67\x2c\x52\x21\xe8\xbe\x02\x7e\x66\x7d\xaf\xba\x0c\xd6\x4d\x62\x1d\x7d\xa5\x26\x4b\xeb\x6a\xd1\xa7\x2e\x9d\x9a\x53\xfb\x75\xd8\xb8\x2c\x25\xda\x8e\x33\x56\xa7\xcd\xb2\x5b\x21\x7c\x07\x03\x5c\x13\x97\xda\x34\x90\xe2\x1c\x8b\xc8\xeb\x0c\x6a\x7c\x0c\x35\x8c\xe9\xc1\xea\x56\x43\x2e\x43\x3c\x1e\x60\x58\x14\x63\x83\xe1\x06\xd5\x42\xcf\x7d\xa8\xe6\x07\xfd\xed\x75\x1b\x17\x10\xbf\x12\x5e\xf4\x36\xf4\xce\x2d\x5f\xd3\x8d\x1c\x6c\x72\x2a\xcc\x5b\xa8\x82\xa3\x79\x68\x24\x22\x04\xcc\x86\xaa\x3e\x22\x9a\x21\x5b\x01\x01\x4d\xad\x6e\x10\xed\xae\xd6\x45\x88\x52\xd0\x02\x32\x99\xff\x3a\x1e\xaf\x12\xf3\xa1\xb1\x86\x6c\xab\x96\x6d\x3b\xa2\xa7\x87\xff\x72\xbf\x78\x24\x0b\x19\x26\x43\xa3\x7b\x16\x4e\x14\x74\x86\x5b\x24\x8f\xb9\xea\xe0\xdf\xcd\x64\xc3\x9b\x05\x03\xd1\xf7\xec\xd7\x24\xd0\xd4\x05\xfb\x99\x3f\x83\x07\xd9\x13\x0a\x83\x20\x77\x5a\x69\x30\x04\x28\xbc\x2a\xe6\x7e\x17\xb5\xdd\x64\xb4\x3c\x32\x3d\xd1\xd1\x96\xc2\x8b\x05\xbf\x86\x5d\xb0\xa3\xcc\xb0\x6a\x77\x28\x88\x47\x44\x7b\xfb\x9c\xf7\x11\xf1\x5a\x16\x3e\x1d\x68\x27\x89\x04\xd4\x0d\x45\xaf\xc6\x55\x40\x88\xaa\x1f\xb0\xf9\x49\xec\x98\x58\x07\xbf\xd9\x30\x63\xe2\xba\x2f\xcc\xf5\x83\x3e\xa5\x11\xb3\x3e\x30\x7d\x68\x21\xbf\x1e\xc5\x83\x2c\xc5\x06\x9b\xff\x86\x19\x2e\x42\x8b\x56\x95\xc9\xd4\x6b\x8d\xa8\x5c\x53\x7c\xe4\x92\x23\xe7\x75\xfa\xe0\x86\xfe\x3d\xde\x6c\x1e\x17\xc5\x83\x89\x51\x07\xa2\x91\x1b\xf6\xc0\x90\x4c\xb5\x10\x03\x56\x19\xd4\x14\xc8\x99\xd3\xb8\x63\x80\x68\x9e\x7e\x61\x29\xa0\xe4\xeb\xb0\xb4\xf0\x78\x13\xd2\xf5\x73\xd7\xf1\x16\xd0\x10\xc3\xf3\xc6\x29\xcc\x2a\xc4\x3b\x31\x1c\xc9\x40\x42\x0f\xb2\x06\x4e\xd7\xb7\x76\xcf\x5d\x9b\xa8\x44\x47\xec\x7b\x73\x00\x25\x12\xa6\xef\x20\x42\x02\xc9\xd8\x23\xd5\x49\xc7\x13\x80\x53\xb2\xb1\x6f\xfb\x9f\x29\x1f\x4f\x35\x3e\x45\x02\x77\x49\xc8\x53\x21\x61\x2d\x6d\x26\xf4\x0d\xa7\x17\xf9\xf2\x59\x41\x98\x19\x95\x33\x82\xdf\x1e\x6c\xd5\x34\x1f\x25\xd4\xce\x1c\x9f\x3e\x67\xc9\x21\x26\x25\x93\x83\x29\xbe\x8a\x73\x49\xb4\xac\x16\x61\xe8\xd9\xe7\x9c\x30\xd1\xc5\x82\xe7\xb8\xcd\x7e\x17\x9d\xe4\x29\x7e\xa5\xff\x93\x09\xc3\x81\xa8\xc7\xca\xb9\x85\x56\x62\x63\x0a\x9f\xab\xbe\x06\x41\x53\xea\x1a\x31\x6e\x4b\x8d\xf1\x99\x9d\x7f\x99\x3f\xc9\x94\x1f\x49\xec\xdc\x3a\xf3\xb5\x3b\xf7\x38\xbe\x1d\xd2\xcf\x73\xf3\xb0\x1b\x83\xb9\x7b\x57\xef\x55\x17\x5f\x4e\xb1\x29\x5c\x2d\x46\xf4\xf0\xac\xe3\x7b\xa4\x06\x1b\x56\xe8\xce\x47\x74\xe7\xa2\x37\xea\x89\x1b\x5a\x00\x18\x96\x75\x41\xf7\x10\xb6\x18\xbe\xb5\x2c\x99\x75\x72\xcb\xae\x3e\x82\xe2\x69\x22\x9a\x82\xdc\x9d\xde\x3b\x98\x23\x4c\x6c\x7d\xd0\x81\xbb\xb0\x37\xe2\xaa\x62\x5d\x45\x5e\x30\xbd\x03\xf7\x2a\x60\x3a\xb8\xb8\x1e\x00\x1a\x52\xce\x89\x7f\x88\xe5\xa3\x14\xcb\x23\xf7\xc4\x3e\xd0\x60\x89\xb5\x12\x5f\xd9\xb9\x1e\x31\x7b\x2a\xdb\x1e\x8e\x81\x63\xb4\xb3\xa3\x02\x2d\xa0\xec\x5b\x6a\xe6\x31\x64\x25\x89\x37\x89\x41\xc8\xfa\xab\xae\x03\x7c\xc0\x80\x93\x0d\x32\x3f\x55\xc5\x8e\xa8\x8f\xe7\xe2\xb6\x7a\xbf\x8b\xeb\xa5\x15\x8f\xfb\xf2\x83\x75\x0f\xe6\x13\x42\x84\x0b\x46\x0c\xcf\xd0\x6b\xef\xf0\xdc\x4d\xb5\xcc\x4c\xc8\x69\x3b\x14\x07\x70\x5c\x4e\xbd\xe7\x5f\xc8\xaa\x84\x0f\xc1\x84\x4c\xac\xbc\x4d\x4c\xfa\x21\x1d\xcd\x47\x8c\x2d\xf1\x26\x75\x52\xd5\x9d\x46\x6c\x23\x42\x18\x60\x69\x50\x1f\x10\x16\x98\x9a\xd9\xec\xf3\xf0\x39\x88\x9d\x06\x4a\xb6\x33\x40\x48\x12\x12\x05\x01\x71\x9b\xd9\x24\x80\x8e\x0a\x47\xca\x83\x8f\x9c\x56\x55\x7c\xe8\x02\xab\x44\xcf\x03\x9b\x08\xb3\x83\xbe\xc2\x44\x40\x10\xf0\x7a\xd4\xb4\x77\xed\x3b\xf2\x16\x55\xce\xc2\x83\xe5\x4e\x36\x5e\xab\x8b\x72\xcb\x51\x34\x59\xb1\x79\x2d\xbb\xad\xf0\xfc\x3b\x5a\xfd\xee\xb6\x56\xc5\x10\x6c\xaa\x59\x33\x89\x54\x5f\x79\x2c\x5a\x0e\x7d\x7d\x47\x6b\xdf\x5b\x6b\xe1\x96\xf5\xb1\x2c\xb7\x41\x13\x71\xf7\x03\x17\x11\x30\xcf\xd8\x34\x6b\x82\xa3\xa9\x17\xa4\xb9\x4d\x1f\x60\xe2\x51\x14\x18\x6f\x25\xa0\xbb\xd9\x1d\x1e\x63\xe3\x05\x62\x2a\x50\xc4\x6b\x87\x1a\xd4\xc1\xb0\x96\x27\x0b\x38\x37\x8c\x74\x8d\x25\x4f\x54\x25\xc6\xbf\x03\xab\x22\xef\x47\xed\xba\x66\x05\xda\xc3\xdd\x8b\xed\x3b\xd3\x09\xbb\x4e\x07\xca\xc6\xf3\x21\xb1\xa6\xe2\x32\xc1\xe3\x39\x09\x92\x0f\x17\x18\x38\x2a\x44\x75\xc5\x8e\x0a\x41\x07\x85\x8a\x0e\xd5\x73\x32\x59\x87\x52\x5e\x50\x8b\x5c\xf3\x4b\xa4\xc1\xf8\x42\x55\xef\xfd\xc1\x50\x67\x21\x31\x30\x68\x05\x57\xfa\x4c\xe3\x9a\xe9\xeb\x04\x43\x5f\x76\xcd\xdd\xaf\x9a\x20\x46\x8d\xf8\x5b\x60\xf7\x0a\xbb\x3e\x8b\xb1\xb3\x17\x81\x46\x31\xa9\xe2\xcd\x40\xee\xb1\xdd\xc8\x84\x1f\x1c\x92\x37\x3b\xea\xf3\xba\xfa\x08\xf5\x19\x91\xb2\x04\x3a\x3e\xbf\xbc\x82\xce\x8c\x96\x0c\xed\xbc\x4b\xe6\xd4\xe9\x5f\x57\x65\x8d\xd8\x9b\x1c\x6f\x58\x59\xd7\x62\xc1\x6e\x52\x55\xad\x81\x09\xf7\xa5\x19\xaf\xd7\xc5\x5a\x18\x5d\xe8\x48\x67\xf2\x86\xe8\xce\x52\xc4\x14\xe6\xb5\xd9\x6d\xcb\x05\x49\xd1\x33\xbe\x28\x6b\x6b\x3c\xd4\xe0\xc2\xc7\xdf\x6a\xa3\xe9\x46\x02\x8b\x1e\x56\xb1\x05\x68\x51\x94\x84\xfa\x64\xdd\xb5\x47\xe8\x19\x82\x4e\xc9\xcd\x86\xe1\xdb\xa4\x66\x70\x64\xb1\x99\xae\x98\xc1\xa7\x6a\x7d\x72\x9b\x2b\xca\xc1\x3e\x84\x81\x21\xa5\xe9\x3b\x84\xe7\x61\x55\x33\xaf\x89\x30\xf5\xc3\x04\x48\xb7\x6d\xc4\x04\xf4\x9d\x7e\x8e\x81\xe4\x7c\xc5\x3d\x79\x25\x5f\x63\x90\xad\xc6\x6f\x72\x91\x9c\xc6\x20\xf3\xa6\xe0\x13\xd3\x73\xfa\x33\x16\xbb\xdd\xab\x00\x26\x7b\x83\x38\xb7\x1c\x03\x40\xee\xa5\x39\x83\xd0\x5d\xae\xaf\x25\x9e\x03\xeb\x9a\x70\x40\x14\xf5\x20\xdb\x4e\x4b\x40\x53\xb6\xf5\x45\x05\xea\xd1\x07\x67\x15\x44\x73\x0b\x75\x7f\xea\xfd\x1b\xba\x73\x0e\xfb\x84\x7b\x0e\xeb\xd7\x6b\x91\x5a\x30\x0d\x38\x0e\x4b\xe8\xbd\x23\xde\x8c\xf8\x24\x69\x37\x8f\xb6\x65\x6d\x25\xdc\x1e\xfb\x61\x11\x51\x23\x44\x8a\x81\x88\xd4\x2b\x36\x5c\x81\x19\xb3\x85\x20\x07\xb1\x01\x61\xe3\x1e\xa9\xd9\x39\x23\x48\x0c\xce\x47\x10\xfe\x5e\x14\x40\xe6\xe0\x35\xdc\x99\x14\xdc\x9f\x2e\x5e\x45\xeb\x21\xe0\x28\xd1\x73\x0d\xe8\xa8\x06\xd1\x13\xdd\x2f\x73\x0a\x53\xfd\x06\x2a\x56\xc6\x15\x1b\x59\x06\xab\x9b\x37\x76\x12\xa7\x44\xee\x6e\xcb\x65\xde\x16\x16\x38\x46\x39\x0d\x3b\xcf\x80\xa3\x84\x7e\xc2\xf9\x9a\x8e\xeb\x5a\x05\x71\x46\x02\xf9\xc8\x86\x54\x34\xdf\x2c\x15\x99\x86\x82\xe5\xcb\x42\xd8\x18\xbb\x2a\x12\x73\xd9\x6d\x99\xdf\x08\xf3\xb2\x76\x30\xe4\x87\x7f\xba\x3c\x7f\x7b\x94\x7e\x7e\xbc\xdf\xef\x1f\x73\xf1\xc7\xbb\x76\xcd\x4e\x7d\x05\x07\xa6\xf9\x1f\x67\x6f\x8e\xd2\xb2\x5f\x3c\x9a\xd1\xd1\xbe\x8d\x15\x62\x6a\x4d\x8a\xcb\x0a\xa6\x2e\x16\x36\xff\x38\x7b\xd2\x15\xa3\xa1\xe0\xc3\xf8\x67\xe1\x96\xca\xb3\x67\xe6\x22\x3a\x99\x62\x36\x12\x6c\x48\x8b\xb6\x84\xb5\x05\x3e\x82\x0c\xda\x9d\x3e\x4e\x05\x24\x18\x82\x54\xd4\x8e\x76\xe3\xf5\x42\x43\xd1\x0f\x40\xec\x72\xf1\x04\xd7\x8a\x2e\x13\x13\xe7\xb6\x15\x0e\xc2\xd7\xad\x9a\xdd\xba\x88\x39\x26\xe1\x4c\x27\xa2\x2c\x7e\x1a\x16\x86\x5d\x24\x62\x49\x3f\x4d\xff\xc4\xba\x25\x9e\x28\xa1\x2d\xce\x32\xda\x02\xf0\x6c\x58\x18\x51\x11\x03\x51\x9a\x06\x20\xd1\x1e\x4d\x94\xf7\x79\x4e\x9c\x1f\x55\xa2\x27\x3e\x36\xd3\xe8\xd3\x8d\x3b\x01\x82\xd6\xa4\xba\x71\x91\x58\xb3\x37\x9d\x6d\x78\x11\xf3\xc8\x23\x35\x9c\x34\xb5\xd7\x14\x1e\x52\x31\x0a\x9d\x44\x51\xc0\x1f\x01\xca\x5c\x24\xb4\x52\xf5\x6b\x17\x8c\x29\xd5\x30\xa0\xe5\x30\x23\x08\xe2\x50\xf6\x70\x9f\x9f\x5a\x8b\x72\x06\x77\xb3\xe6\x57\x8f\xf1\x37\xdd\xe1\x44\x32\x11\x8f\xa3\x88\x7b\x80\x75\xc4\x52\xda\x7e\xb8\x8b\x0d\x05\x34\xe5\x4d\x5e\x94\x51\xde\x34\xda\xae\x15\x70\xd0\xc6\x68\x97\x54\x79\x7a\x7c\x4a\xf0\x2d\x1c\x12\x08\xc4\x28\x28\xd3\x51\x5a\x1c\x1b\xb8\x6d\x9e\xba\xb4\x58\xbc\xb2\x55\x0a\xbe\x1b\x2f\x51\x46\x88\xac\xa3\x90\xa3\xb2\xa0\x16\x9b\x10\x30\x08\xbc\x8d\xd9\x2d\xc1\x0c\xf1\x47\xbe\xd6\xa1\xd0\x2d\xb5\x9a\xdf\x9c\xf8\xf7\x0d\x32\x87\x6f\x6f\x0c\x57\x36\xc9\x6a\xf2\x7a\xd3\x89\x7c\x85\xd8\xda\xae\x9b\x1b\x73\x47\x3f\xc5\x2f\x8d\x57\x13\x8e\xcc\x83\xe9\xa0\x3c\x64\xa0\xae\x69\xb2\xb8\xba\xbf\x05\x21\x5c\x55\xc4\xad\xf9\x84\x8c\xa2\x04\x13\x1e\x7c\x22\x15\xf9\x44\xf7\x26\x3c\x9a\x1d\xd4\xd0\xf7\xfa\xd4\xb5\x70\xc0\xf7\x3a\x2e\x1a\xfa\x5f\x07\x45\xbf\xc0\xff\x3a\x46\xd2\xd8\xbb\xda\x0f\xf5\x0b\x1c\xac\xa7\x06\x3d\x96\x6b\xa7\x10\x3f\x51\x60\x4a\xba\x2d\xc2\xb1\x7d\x81\xa3\xf5\xe0\x2c\xfc\x25\x02\xee\x54\x4f\x3c\x4a\x02\xe4\xde\xa5\x23\x2e\xaa\xeb\xeb\xd9\xbc\x6d\xf6\x1d\x7b\x33\xe3\x21\x0b\xe6\xb2\xfc\x3b\xbd\xc4\x6f\x01\x61\xe3\x00\x10\x85\x7c\x48\xa2\x1a\x28\x3d\xd5\xbb\x40\x49\xc4\xcd\xec\x30\x7c\xfe\x29\xe5\xc8\x2d\xed\x5b\x3a\x89\x1d\x5b\xce\x4c\x8a\xd0\x46\xb7\xcf\xf8\x0b\x7e\xd8\xd0\x57\xb3\x7a\x15\x85\x2e\x39\x45\xc1\xf8\xd3\x10\x6e\xbb\x12\x6c\xe3\xe4\x1e\x5b\xc4\x57\xaf\x6b\x02\x61\x19\x1c\x81\x11\x31\x54\x90\x4f\x3d\x48\xe8\x6f\x49\x10\x86\x4b\x0f\xa1\x08\xc2\xa2\x7f\xfe\xfa\xad\xfc\x84\xc1\xbb\xc6\x3f\x82\xc5\x3b\x5f\xa7\x27\x66\x46\x3f\x9b\x32\xa7\xb7\x3c\xf1\x7c\x10\xc5\x88\xbd\x3c\x87\x5f\x0e\xa2\x68\xf3\x6b\x5c\x1c\xf1\x5f\x97\x4a\x32\xb0\x2f\x76\xd1\x96\x8f\x87\xc5\x08\x39\x82\xea\x4b\x7c\xb8\x74\xbd\xf8\xe1\x3f\x2e\x2d\x87\x51\xc7\xd3\x60\xe4\x1e\x23\x66\x3d\x40\x74\x77\xbf\x4b\xbb\x8a\xb5\xad\x4a\xa0\x83\x06\x41\x1d\x3e\xb6\x31\x68\x07\x6f\xb1\x85\x63\x0d\x6f\x94\x10\x73\xa1\x5b\xa5\x0e\x3f\xec\xf0\xd3\x8b\x3b\xb5\xbe\xad\x32\x8b\xfa\x1d\x95\x96\x2d\xb6\xb4\xd9\x4e\xd7\xcd\x12\x52\x24\x9c\x11\xe5\x91\x8d\x1d\x3f\x46\x43\x88\xe0\x38\x5a\x2c\x71\x38\xe2\xaf\x36\x54\xff\x27\x09\x8e\x2e\xd5\x93\xf4\xe0\xdc\xbd\x49\x90\xa8\xc5\xe3\xd4\xf2\x70\xa4\xb6\xf8\x70\x51\x19\x1f\x7c\xd9\x74\xdb\xde\xe7\x84\xf2\x21\x99\x2c\x42\x57\x16\x16\x53\x38\x00\x86\x8c\x3d\xe8\x40\xc4\x88\x2d\x75\xcc\x7c\x2d\x67\xdf\x6a\x70\x2e\xfe\x1b\x92\x85\xbe\x5b\x72\x21\x5f\x2e\x87\x25\x60\x11\xe3\xde\xc8\x97\xbc\xb1\x37\xa4\xa6\x71\x40\x02\xca\x7b\x3c\x9c\xeb\x00\xde\x21\xe0\xaf\xe5\x7f\xfc\xaf\xff\xcb\x4a\xd9\x86\x76\x58\x44\x0f\xd1\x00\x9c\x9e\x56\xcc\x83\xce\x3f\x9c\xf3\x18\x7c\xdd\x77\x64\x78\x2d\xe8\x1a\x54\x52\xf1\x44\x33\xa2\x77\x7e\x63\xc2\xd6\x0a\x5b\xf8\x0d\x16\x0c\xe8\xc7\x2f\x19\x5c\x7c\x0e\xeb\x30\x22\xc8\x74\xbf\x71\x81\x01\x15\x50\x38\x08\x46\xd4\x2b\x91\x4c\x6f\x4f\x6e\xed\xea\x7b\x1f\x60\xc2\xf6\x9d\x24\xef\x9b\x76\xf9\xc1\x07\x97\x75\x5a\xae\x28\xb0\x2c\xce\xa0\x0c\xe3\xc2\xb3\x1d\x00\xf4\x21\xdb\x7c\x8d\x36\x29\x2f\x99\xf4\xc6\xef\xa3\x89\x06\x40\x02\x80\xc3\x80\xcd\x7c\xa2\x67\xe6\x91\x25\xb1\x29\xf5\x72\x3d\x7a\xb8\x12\x8e\x52\xa6\x4e\x35\x39\xb2\x48\xf4\xd6\x97\x1d\x7b\xf8\x83\xc3\x87\xf2\x53\x7b\xac\xcf\x92\xfb\xba\xd7\x48\x20\xc6\x80\x04\x84\xc6\x85\x43\x13\xff\x4d\x10\x90\x50\x35\x78\x9c\xaa\x5f\x9a\x3e\x08\x7a\x18\x3d\x2f\x11\x78\x90\x21\x18\x6a\xf4\x66\x04\x57\x0e\xac\x4c\x5c\xf8\x8f\x42\xe4\x02\x85\x48\xbd\x0d\x3a\x72\x38\x7e\xb0\xc6\x25\x8f\x46\xd8\x82\xea\x9c\x0d\xc9\x6a\x11\x2e\x41\x27\x08\xd0\x5a\x47\x66\xaf\xdd\xcc\x37\x13\xac\x9f\x95\x58\x03\xf8\x62\x78\xb9\x69\x4e\x2b\xe9\x27\x81\x8f\xfc\x26\xf5\x58\x98\x4b\xc0\x00\x49\x4e\xd7\x74\xc2\x58\x47\xe7\x44\x54\xc4\xb2\xe0\x4f\x07\x7c\x66\xc7\xc1\x8c\xbf\xde\x6b\x76\x5c\xc7\xed\x7e\xb3\x7f\xf4\x22\x7a\x3a\x50\x61\xa8\x0c\x1b\x44\x2c\x74\x59\x53\xa1\x0b\xff\xc8\xb5\x70\x0c\x1a\x88\x58\x11\x0a\x5c\x4d\x5f\x7a\xfd\xa0\xb7\xcd\x44\xa9\xff\xd8\x65\x73\x1c\xc2\x77\xd8\xeb\x51\x8c\xbd\xa8\xd3\x5f\x17\x6b\xef\xe0\xad\x6d\xc4\x2b\x86\x87\xc3\x51\xc0\x0c\x0c\xf0\xd6\x22\x71\xf8\x8c\xb0\xc3\x4e\x1d\x18\xdc\x02\xea\x9d\x82\x04\xce\x10\x1d\xf7\xdd\xd1\x33\x0e\x5c\xb3\xdc\x16\x46\x63\xd8\x4b\xe6\x31\xce\xa9\x23\xec\xe4\xad\x25\xc2\xbd\x36\xbe\xa9\xff\x47\x42\x6b\x4c\x5f\x4c\xf0\xe1\x71\x6f\x3a\x33\xec\xcc\x86\x3f\xaf\x89\xe0\xc3\x87\xe1\x4b\x4e\x27\x9e\xd1\x7a\xcc\x41\xaa\x12\xe4\x0e\x42\x46\x2b\xd7\x9e\xe9\xcd\x9d\x45\xd5\x1a\xa4\x7b\x96\x07\xbb\x69\xbd\x9b\xf4\x40\xf2\x1b\x22\xcc\x64\xce\xb0\x7c\xdc\x46\xec\xc3\x60\xa9\xee\x6e\xe8\x0c\x1f\x2e\x9d\xb0\xb6\x28\x11\xa2\xe0\x44\xbe\x5c\x8e\x1e\xd2\x34\x0c\xb7\xef\x84\x84\x58\x86\xdf\x51\x90\xaa\xbb\x9d\xe2\x9a\x5d\xa8\x69\x52\x6e\xb6\x3c\x85\x79\xea\xd4\x84\x2c\xdf\xca\x2e\x28\xb2\xa0\xf6\x0a\xa2\xf5\x0f\xc3\xba\xe4\x41\x1d\xdd\x35\xdf\x36\xfb\x44\xb6\xcc\x19\x5c\x29\x24\x24\xb0\xa6\xc4\x5d\x92\x34\x16\x48\x34\x5e\x53\x2a\x91\x24\x34\xa2\xcf\x38\x7f\x10\x7c\x00\x3b\x86\x0b\x3b\x60\x11\xbe\x59\xa8\x54\xa7\x9d\x5a\xee\x66\x62\x6f\x7c\xa9\x15\x42\xa9\x6f\x16\xb2\x69\xdc\x6e\x08\xf1\x25\x0d\xe3\xfd\xe4\x61\x73\x47\xa6\x18\x43\x00\x48\xd5\xd8\x49\xa4\x3b\x69\x45\x1f\xc5\xb5\x7e\xb8\x77\xe9\x7c\x3f\x42\x88\x2f\xe9\x07\xb7\x02\xfb\x73\x39\xa4\xdc\xd2\x1f\x3a\x55\x6a\x3c\xcb\xc8\x66\x60\xd8\x45\xef\x54\x7f\x15\xec\xd3\xb0\x53\x29\x06\x72\x07\xeb\xa1\xc7\x1b\xa6\xe4\xc8\x7d\xf2\x84\x68\x20\xe6\x44\x93\xa1\xae\xee\x5e\xe2\x30\xf4\xe7\x92\x0e\x34\x30\x14\xf2\x60\x93\xbb\x8e\xf4\xcb\x8b\x72\x90\xad\xce\x54\x9e\x93\xcc\xbb\x37\x5c\x81\xb3\x20\x4f\x22\xd7\x85\x5b\x06\x04\x3b\x9b\xc9\x42\x9e\x3b\x70\x6b\x9c\x59\x5d\xd0\xea\xb8\x32\xc7\xaa\x01\xe5\x58\xf4\x18\xce\x78\x67\x28\x95\x05\x5a\x5a\x66\xca\x47\x26\xab\x3a\x2b\x06\x40\x6d\xf2\x9b\xc8\x4e\x08\x66\xcd\x9b\xf8\x05\xe1\x5b\x14\x3b\xe3\xae\xf8\x5d\x5b\xde\x10\x70\x04\x73\x50\x9b\x33\x0b\x97\xfa\x98\x40\x3c\xd9\x2d\x5b\xf8\x40\xd8\x5c\x33\xb3\x08\x48\x01\x15\xfe\xe0\x46\xe9\x1e\xae\xf4\xdc\x00\xd7\xce\x54\xd1\x83\xdb\x98\xc2\x57\x74\x00\x6c\xe3\xf6\x1e\x80\x2d\x88\x5b\x1c\x75\x23\x60\x01\xb7\x75\x44\x5f\x9c\xfc\xf2\x8e\x80\x6f\x7c\x61\x47\x8e\xac\x17\x1a\x7f\xbb\x28\x26\xd7\xff\x6d\xfd\x1b\x1c\x73\x40\x9c\x51\x80\xf7\x01\xc1\x47\x2f\xe4\x3b\xa2\x0f\x2c\xe6\xac\x5a\xd8\x66\xa8\x05\x9f\x6e\x67\xbe\xaa\x9a\xa6\x10\xe7\xd6\xba\x0f\xad\xfc\xe2\x58\x26\x6c\x60\xd6\xb7\x37\x2a\x92\xf0\xe0\xe2\x50\x2a\xde\xb8\x47\x0e\x5f\xb8\x3b\x96\xa0\xff\xef\x81\xf6\x0f\x89\x7b\xf4\x90\x97\xb2\x7d\x27\xa2\xa8\x91\xfb\x33\x84\x5d\xf7\xf1\xd6\xd3\x61\xfc\xf5\x5b\x63\xdf\xc7\x31\xff\x87\x8f\x3f\x74\x12\x22\x6d\x69\xb2\x9c\x3d\x35\x99\xa8\x51\x13\x33\xd6\x1b\xc2\xc1\x06\xef\x0c\x2f\x38\x0c\x72\x53\x57\x62\x3e\x73\x26\x5f\x34\xf6\x04\x63\xca\xb6\x12\x3e\xe2\x05\xff\x90\xd0\x94\x9a\xc2\xa1\x29\x93\xbe\xe9\x21\x50\x5c\xf1\xdf\x1f\xd2\xfb\x45\xe2\x87\x0e\x95\x25\x6b\x87\x54\x4a\x90\xef\x20\x3f\x08\xd4\x0e\x93\xfa\xd6\x42\xcf\xfb\x1a\xd0\x4d\x28\x46\x77\x41\xb7\xb5\x93\xa8\x74\xd7\x4d\xb5\x98\xf1\x75\x6b\xaa\x97\xcd\xee\xb5\x79\xe6\x20\xfc\x00\x5c\xc1\x0f\xc0\x89\xda\xec\x28\x48\x88\x26\x24\xcc\xd8\xba\x78\xa9\x51\x72\xbc\x2b\xfa\x74\x89\xf4\x14\x25\x71\xb8\x99\x28\x21\x5f\x8c\x5a\x31\xb5\x78\x98\x66\xa6\x7a\x3e\xc5\x8c\xf6\xa2\xda\xe3\x98\x99\x61\x96\x58\x3a\x46\x49\xfa\xf2\x65\x3c\x12\x51\x4a\x86\x69\xeb\x66\xc9\xef\x33\xd8\xcb\xca\xc1\xf0\x54\xb0\x8e\xeb\x34\xab\xed\xa8\x0a\x78\x87\x86\x29\xb8\x2d\xeb\xf3\x2e\x2e\x8d\xf5\x19\x26\xa8\x67\xfd\x08\x90\x0e\xda\xf9\x62\xa5\x4e\x44\x13\x84\x64\xe7\x65\x47\x4c\x72\x68\x9e\x82\x94\xd7\x49\x53\x7b\x8b\x74\x12\x86\xdf\x9d\xc7\xd3\xaf\x41\x2e\x7b\x41\xb0\xcb\x0d\xc2\x8a\x36\x1a\x0f\x8b\x5d\x22\x5c\x08\xd1\xf4\x1c\xcb\xb1\xbb\xb5\x50\xb0\xc5\x71\x5c\x06\x0d\x5b\xaa\x25\x45\x1c\xb9\x65\xaf\xf3\x35\xeb\xae\xa9\x66\x24\xc1\x4b\x75\x9d\x17\x22\x72\xb8\x68\xe9\x75\xae\x65\x7f\x51\x1d\x83\x5e\x7a\x08\x57\xcd\xd7\x77\x15\x2a\x35\x36\x70\xa3\xde\x0c\x3a\x19\xf1\x3c\x03\xb9\xa3\x86\x41\x17\x27\xab\xf8\x8a\x4e\xf2\x73\xc9\xcb\x85\x7b\xe4\xf5\x94\xc3\x55\xb7\x73\xe6\x78\xbc\xc1\x95\x0b\xf3\x3e\x8b\xd4\x72\xd3\xc5\x6f\xeb\x19\x3a\xc4\x07\xf2\xa9\xea\x0f\xf5\xad\x2d\xbb\x9b\x7a\x91\xe1\xad\xdf\x6e\xa5\xd7\x9f\xef\x4a\x51\x8e\x3f\x98\x51\xda\x93\x5c\x63\xb2\x95\xb8\x28\xec\x1e\xc8\x8b\x06\x0f\x17\x94\x0e\x3b\x66\xda\xff\x1e\x83\x27\xa2\xb4\x49\x77\x24\xbb\xf5\x8f\x6e\x6d\x68\x30\x96\x80\x21\x06\xb8\x6d\xd1\x95\xbe\xfc\xa2\x11\x04\x57\xef\xe1\x30\x98\x0c\x74\xf5\x83\x57\x84\x0f\xf0\x30\xe2\x1e\xb2\x19\x05\x07\x20\x67\x1b\x11\xb5\xbd\xd2\xdd\xce\xde\x72\xd6\x0b\xb1\x03\x03\x0a\xdb\xbd\x65\x86\x1e\x44\xbd\xb8\x7b\x8c\xe1\x26\x24\xef\xf5\xef\xb6\x6c\x59\x9c\xba\x87\xfa\x7f\xc1\xef\x90\x29\xc8\x1b\x09\xd9\xb2\x69\x1b\x9a\x1e\x89\x12\xa4\xef\x26\xbc\xb4\xb4\x6e\xa2\x00\x14\xd8\x37\xd9\x4e\x23\x3b\x59\x99\x33\x24\x93\x70\xc1\x61\x9e\x7c\x29\x6c\xd1\x56\x86\xd5\x92\x0b\x55\x66\x63\xcf\xb6\x52\xc7\x96\x11\x94\xd4\x32\xcd\x9c\x5d\x06\xd4\x91\x15\xc0\xe7\x9a\x12\xc0\xe2\x22\x84\x43\xef\x10\xba\x76\xdb\x8c\x87\x8a\x18\x21\x92\x9c\xbe\x41\x72\x7a\xc5\xc9\xe3\x16\xac\x57\xae\xd8\xa0\x53\x87\xca\x71\x38\x86\x61\x99\x17\x1c\xb5\x61\x08\x6f\x98\x5b\x95\xf9\x76\x84\xb7\x57\x94\x38\xc2\x1a\x20\xc7\x08\x00\xec\x61\x2c\x84\xa5\xaa\x02\xa7\xae\xb0\xc4\x6b\x4a\x3a\x04\x0d\xbb\x82\x21\x7c\xcd\xa2\xe2\x81\x12\xba\x67\x0f\x7b\xa5\xb7\x37\xa3\x5e\x35\xf3\xbf\x97\x8b\xbe\x33\xe8\x73\xf9\x19\x40\xcd\x9b\xa6\xe7\x87\x81\xb7\x2c\x6e\xc1\xde\x4b\xd0\xf4\xdc\xd2\x59\xdc\x5a\x7c\x1c\x61\x4a\xa0\xc7\xa8\x12\xe8\xc3\xb8\xda\x70\x0c\x47\x6a\xab\xdd\x2d\xfa\x1d\x2d\x50\xd7\xe0\xd9\x25\xc7\x7e\xbc\x74\x19\xa3\x16\x47\x25\x43\x0a\x1d\x16\x9e\x6a\x79\x41\x42\x44\x39\xd9\xf4\x09\xe7\xdc\xda\xf6\xa8\x6c\xd8\xf8\xa8\xf8\xd4\x4a\xc1\x33\x40\xac\x50\x9f\xef\x16\x1f\xcb\x9e\xdd\x8e\x56\x19\xee\xad\xc3\xba\x2e\x0c\x2c\x7d\x0e\xb0\xf4\x15\x81\xa5\x57\x50\xd1\x4c\xd4\x4a\x9b\xce\xa6\xec\x73\xd8\x1f\x04\xb5\xbc\x3c\xa1\x19\xe0\xe4\x22\x9f\x2a\x05\xd5\x4d\xa6\x52\xb6\xae\x42\x16\x7c\x82\x1a\xce\xa1\xdd\x51\xc1\xfb\xd8\x81\x4c\xd5\xc6\x01\x80\x64\xf7\x5b\xdc\x2c\xe4\x75\x1c\x0e\x09\x44\x7d\x78\x27\x29\x01\x2c\x4e\x12\x04\x6b\x3c\x12\x57\xed\x88\x76\x4f\xe0\x57\x31\xa3\x14\x0e\xe6\x81\x85\x71\x11\xdc\x05\xfb\x2e\x4f\x01\x6e\x73\x59\x4c\x07\x21\xad\x79\x03\xb4\x96\x87\x70\xda\x68\x27\xa8\x14\xb6\x22\xc7\x38\xb1\xc6\xd7\x58\xf1\x44\x73\xb8\xc8\x85\x31\xbe\x45\x8a\xe7\x34\x85\xbd\xf3\x9d\x77\x05\x13\xe9\x15\x32\xab\xa4\x98\xbc\x85\x17\xfe\xec\xdb\xf2\xa2\xa8\x36\x92\x16\x3d\x3a\xaf\x69\xe6\x1e\xeb\xa2\x73\x69\xba\xbe\x04\x3d\x7c\x32\xf8\x92\xad\x72\xdf\x85\xaf\x84\xbf\x0d\x2d\xef\xaf\x1a\x8c\x32\x18\x58\x6c\x8d\x64\xc3\xbc\xdb\x45\x77\xa6\x75\x84\x51\x60\x74\x64\x10\x90\xcd\x20\x67\xf0\xb2\xa1\x1a\xe6\x08\xa4\x84\x3c\x95\x7b\xae\x75\x58\x1a\xa7\x17\x3b\x0e\x0c\x6a\x78\x83\x93\x4d\x80\xe5\xf1\xeb\xd2\xd0\x2e\xf3\xc1\x5e\xac\xab\xa1\x9b\x85\xf5\xdb\xae\xb6\x07\x88\x8c\x0c\x0e\xbd\xf9\x65\x51\xc6\xbf\xf0\xd9\x2f\x8f\x8b\x80\x52\x70\x71\x1f\xd3\x48\xd5\x65\x21\x51\x0c\x83\x94\xe7\x03\x22\x61\x70\xa5\x93\x08\x14\x37\xf9\xe1\x3b\xfc\xc1\xcd\xaa\x11\x0e\xee\x30\xd9\xc2\x3b\x53\x23\xc7\x51\x0d\x41\x19\xa8\xf4\x84\xb0\xd9\xb4\x54\x9c\x62\xc3\x7a\x24\xba\x6d\x66\x33\x76\x57\x5d\x07\x83\xe1\x4e\xe2\xdd\x6b\x55\x0d\xed\xee\x4d\x36\x40\xdf\x7a\x25\x17\x23\x78\xfa\xa9\xc8\xff\x2f\xaf\x65\x86\x1d\xf0\x6f\x66\x1e\x68\xff\x9f\xf2\x66\xe6\x50\xa3\xdd\xc5\x5d\x99\x30\xaf\x3b\x1e\x3e\x9c\x71\xc0\xb6\x8e\x1f\x15\x9c\xc1\x53\x29\x66\x91\xd1\x85\x61\xc4\x2a\x51\x22\x64\x81\x48\x88\x4d\x27\x90\xe4\xd5\xed\xa6\x69\x17\x6d\x19\xb8\xdf\xb0\xbd\xc0\x1d\x2d\x6a\x4d\x4a\x8c\x23\xf2\xc7\x5d\x90\x94\xf1\x2d\x9d\xa4\xab\xa2\x27\xb5\x88\xde\xaa\xb5\x9b\x41\xdb\xa3\x57\x63\x96\x36\x0c\x63\x0b\x25\x9e\x32\xab\x41\x97\x07\xec\x2a\xea\xb6\x94\x92\x40\x19\xe6\xea\xa6\x1c\x51\xb3\x82\xde\x4b\x4a\x18\x23\x52\x52\xe4\x45\x3a\x3c\xb4\x2c\x5f\x9a\x3e\x36\x74\x09\x3a\xa9\xd5\x0c\x3a\x17\xd4\x0a\xa8\x69\x8e\x1b\xf4\x66\x68\x45\x2c\xa9\xf0\xe0\x62\x93\xe7\xae\xd7\x94\xad\xbe\xd7\xcf\x91\x1f\x25\x05\xca\x93\x02\xb6\x88\xac\x2b\x39\x7d\x1b\xa6\x07\x8f\xd4\x21\xd7\x3d\x4f\x37\x01\x13\x98\xa1\xe4\x2d\x07\x9e\xfc\x41\xe3\xf0\x04\x8f\xd5\xb1\xb7\x95\x3c\x7d\xb3\x5d\x73\x7f\x39\x06\x39\xae\x30\x58\x0b\xcc\x32\x43\x8e\xf7\xa9\x70\xa1\x4b\x6c\x66\x29\x66\xad\xf2\x9e\x8a\x22\x93\xe5\x03\x8d\x7e\x05\xb9\x40\xdd\xfa\x9e\xb3\x11\x56\x00\x52\xd8\xdb\xde\x7e\x44\x79\xdf\xb7\xd5\x7c\xc7\xf7\xa2\x6a\xff\xc1\x8b\x52\x8c\x4d\x5c\xde\x08\xb6\xdb\x99\x7f\xc6\xa5\x7e\x1d\x86\x1d\xbc\xd1\x3f\x80\x93\x08\x5c\xd6\x2d\x89\xbf\x65\x55\xe0\x5a\xc1\x01\xc8\x65\x63\x04\xb1\xe1\x1d\x27\xeb\x72\x5e\x9e\xc4\x5b\x8b\xf4\xf2\x58\x73\xba\x4d\xbf\xb5\x08\xf2\x97\x67\x57\x17\xb7\xd0\x12\x83\x2a\x4d\x00\x32\x20\x0c\xce\x52\xe2\x40\x56\x40\x21\x6a\x74\xa3\x96\xea\x7a\xac\x87\xd9\x8e\x10\x5b\x37\x0d\x77\xdb\xb6\x2f\xcf\x0b\xf1\x4b\xf1\xfc\x0a\x01\x1b\x96\x4b\x99\x59\x7a\xb6\x5b\xf7\x15\x5b\x81\x59\x6b\x6a\x89\xc4\x0f\xe9\x97\xdb\xbc\xb5\x38\xa8\x88\xfa\x93\x3e\x38\x7a\x30\x8b\x56\x5f\xd6\xcb\x8b\x84\xf2\x38\xe4\xd5\x9b\x4b\xfa\x5c\xb4\x37\x72\x11\xaa\x23\xfd\x58\x6d\x19\x4c\xdf\x94\xe6\x01\x53\x0a\x60\xff\x82\x14\x5b\x2a\x7c\x63\x56\xb6\x9f\xaa\x85\x23\x98\x8b\xe3\x33\xe8\x1d\x28\x29\x5c\x7c\xda\x34\xe2\x14\x99\xe4\xe7\x3b\x41\xd3\xd1\x44\x92\x9f\x31\x10\x7e\x4c\x99\x8d\x35\xb7\x86\xc0\x30\xa0\xca\x50\x3c\xd3\x17\x37\x15\xd3\x23\x51\x25\x86\x0e\x24\x16\xcf\xda\x86\x12\x65\x5c\xe4\x2e\x1b\xf7\x59\xc4\xcc\xc2\x9d\x6b\xf0\xf4\xf2\x97\xd9\xfe\x84\x95\x05\x52\xc6\x6d\x83\x9e\x8c\xed\x10\x97\x88\x20\x33\xe1\xaf\xf6\x3a\x4d\x5c\xb5\x7f\x8d\x68\x54\x22\x7a\xa7\x66\x84\xd7\x09\x9b\x9a\x5b\xec\x68\x82\xda\x07\xfb\x7d\x5c\xf1\x5d\xdb\xbe\xe8\xe2\x4c\x07\xe6\xee\xa1\x54\x07\x16\x5f\x47\x29\x6c\xbe\xdd\xba\x6d\x23\x78\x80\x08\x64\x1b\x80\x7c\x12\x86\x13\x40\xd0\x22\xe8\x06\xf5\x88\xf3\x59\x08\xc4\x3e\x68\x0a\x30\xdc\x7a\x34\xb9\xb9\xbe\xe6\x88\x5c\x1c\x57\x52\xc3\xa9\x20\x40\xd7\x19\x5b\x73\x5b\x49\x71\xa9\xcc\x58\x29\x07\x25\x17\x8f\xc9\xde\xae\x7d\x87\x44\x3e\x55\x18\x78\xbb\x73\x8f\x7e\xbf\xdb\xd5\x72\x5e\x0a\xb2\xb4\x21\xce\x0a\x1b\x81\xf4\xd2\x36\x4d\x6f\xef\x3a\x04\xa2\xcb\x3b\x4a\xa6\x3d\xad\x5f\x39\x04\xf3\x4d\xd7\x22\x93\x40\xf5\x41\x19\xdc\xb3\x2d\x60\x93\x3f\x2e\x44\xfd\x1e\x97\xa0\x7e\x1f\x00\x17\xc3\x0c\xdb\xf8\x2f\xf1\x4b\x98\xb4\xeb\x31\x9b\x79\x2a\x35\xda\x80\x25\x6d\x48\x37\x21\x0e\x8a\xb9\x27\x8c\x53\xbb\x9b\x9b\x24\x0d\x82\x0c\xa5\x17\x9f\x1a\xca\x0b\x3e\x35\x94\x7d\x7c\xaa\x76\x6c\xd0\x83\xae\x5b\xdb\x44\x5c\x5e\xbe\x89\x67\xdb\xe7\x06\xef\x06\xb1\xbd\xd8\x3d\x0e\x30\xcb\x71\xee\xee\xa5\xec\x69\xf8\x28\x28\xa1\xd8\x0c\xf1\xa7\xa9\xc3\x3a\xba\xdf\xd6\x55\x5f\x7e\x7f\x0f\x57\xe7\xf7\xfa\xaa\x98\xdf\x7b\x14\xae\x9b\x0a\x8e\x05\xc1\xc2\xa9\x16\x07\xd0\xe3\x0e\xef\xd1\xd3\xa6\xa9\x78\x69\x57\x6d\x69\xfb\xfb\x49\xf0\xda\xe8\x88\xa4\xfd\x36\xe0\x08\x3a\xdc\x02\xac\x63\xec\xa5\xd2\x06\x19\x19\xc9\x0b\xbd\x3c\xc6\xc8\x06\x9a\xef\xac\x9a\xe7\x48\xf6\x3d\x94\xe0\xb8\x16\x2c\x57\xed\xf5\xad\x7f\x88\x75\xfb\xba\x86\x1f\x89\x15\xb1\x07\xa3\xa1\x67\x1b\xbd\x2d\x0d\x05\x9b\x3e\x2d\xad\x05\x30\x76\xa7\xbe\x38\xe3\x01\x87\x1a\x8b\xe1\x80\xe1\x40\x55\xfd\x5e\x4a\x7c\xc3\x60\xd8\x67\x74\x18\xde\xec\x36\x78\x58\xf2\x92\xc3\xb1\xe1\x69\xd0\x51\xb7\xb6\x24\xe9\xe7\x61\x8f\x90\xe0\x98\x90\xf8\x46\xb2\x63\x48\xb6\xd6\x1b\x2e\xf1\x9f\x84\x7b\x48\xfa\x06\x57\x5a\x0e\x3b\xb4\x09\x79\xb1\x34\x2a\xf4\x8e\xf3\x9c\x18\x3b\x51\xd8\xdc\xaa\x1d\xa9\x98\xdb\xe2\x24\xa9\xfc\xb6\x2b\x77\x54\x79\x59\x2f\x41\xa6\x7f\xe6\x9f\x24\xee\xf0\x4f\x87\x20\xf1\x47\x84\xc2\x8b\x3d\x1a\x9e\x9a\x87\x22\xf4\x5e\x94\xe2\x68\xe1\x4e\xb9\x24\x98\x99\x70\x17\x38\xc3\xef\xe9\x0e\x2a\xec\xf8\x68\x12\xe7\xdb\x2c\xd2\x9a\x6a\x82\xb9\x7b\xf5\xf3\x9b\xf3\x01\xe4\x04\x33\xd0\x9c\x09\xe6\xa1\x39\x13\xac\x42\x6e\x6b\xdd\x10\x70\x43\x3b\x3d\x02\x81\x3c\x38\x00\x21\x68\x6f\x99\x01\x4a\x9e\xac\x48\x49\xbf\x20\xca\x12\x6f\x1b\x21\x7a\xf9\x1d\x03\x05\xcf\x4e\x09\x94\xbd\x3a\x35\x6a\xb5\x0e\xdb\xac\xe5\xa6\xd1\x73\x1d\x31\x11\x0a\xb8\x8e\x98\xd8\x4f\x76\xcf\xa0\xd9\xab\xa8\xb2\x88\x25\x02\x7f\xa1\x49\x06\x6a\x20\xbe\x66\x83\xd0\xaa\x5d\x37\x89\x70\x2b\x27\xbd\x9e\xe0\x57\x34\x75\xba\xfc\x78\xbd\x08\xac\x5f\x81\xfc\x36\xb6\x94\x30\xe0\xe5\xc2\x21\xc6\x74\xc6\x2f\x4f\xfc\x83\x5c\x50\x2f\x0f\x06\xb3\xae\xae\x4b\xa7\x8c\xd6\xd1\xbc\xa1\xb4\x08\x78\xd5\xf7\xdb\xce\x7c\xcc\xf1\x08\x54\x7a\x4e\x3f\x06\x83\x08\xab\xd2\x91\x8c\x6a\xda\x56\xb8\x20\x08\xf0\x22\x09\xd3\x18\x37\x68\xdd\x1d\x02\x70\xdd\x1e\x86\x3c\x6e\xd9\x3a\xc6\x69\x2b\xc4\x3f\xab\xef\x65\x01\xd7\x3a\xcb\x00\x93\x2d\x33\x94\xee\x92\x0c\x83\x5d\xd2\xac\x85\x66\x8b\x56\x22\xe4\xf1\x9f\x2b\x36\xd5\x70\x39\xe1\xda\xb3\xb4\x8e\x68\xaf\xd8\x89\x97\x9e\x7e\x7a\x78\xff\x74\x80\xa0\xc9\x32\x26\x9e\x1b\x88\x01\xca\xcf\xe5\x62\x17\xdc\x1c\xfe\x2c\xbf\x55\x55\xef\xab\x69\xcc\x38\x78\x57\xe3\xa9\x89\x0b\x49\x09\x60\xa6\xc2\x64\x5a\xd7\x61\xe2\x6c\xa6\xce\x07\xdb\x77\xcd\xe3\x30\xcb\x50\x66\x71\x65\x86\x4c\xf2\x33\x5b\x8b\xa3\xd5\xc0\x08\xcb\x60\x43\x89\x27\x4c\x43\xa8\xad\xc0\xe0\xcd\xf2\x26\x3a\x6e\x59\xcd\x96\x59\xd6\x76\x16\xc0\xe2\xf8\x10\x3c\x87\x2b\x7d\x90\xfc\xbb\x4c\x2c\x93\xf7\x62\xb6\xf4\x61\xf0\x6a\x9e\x69\xf7\x03\x33\xba\xc8\x1f\xfd\xbe\x3c\x38\xd1\x96\x75\x10\x5d\x4f\x7e\x15\xa3\xc7\xaa\x2c\x14\xf4\xb7\x3e\x14\x74\xf4\x14\xf7\xf0\x19\x0b\xf7\xac\x00\x6a\x65\xbb\x44\x79\xb2\x24\xe8\xc1\x93\xae\x5d\x3c\xb9\x1f\xbe\x18\xc0\xfa\xd2\x38\xde\x76\x54\xa5\x8c\xce\x9e\x5e\xf8\x55\x9f\x3b\x90\xdf\x61\xbd\xa2\xd4\x93\xaa\x39\x3c\xb7\x7b\x8f\x40\x6b\x18\x46\x07\x37\x44\x45\x51\x75\xc3\x0a\x35\xe8\xf7\xb8\xbe\xc1\x6b\x10\xc1\x8b\x17\x4d\xfd\x35\x1d\x9b\x0c\x2f\xfc\xab\xc6\x9a\xfe\xea\x6e\xb9\xf0\x5f\x8a\x7d\xfb\x3d\xa0\x05\x99\xd1\xe9\xe9\xf4\xe4\x81\xe8\x14\xf2\x50\x98\xcd\x22\xfd\xb8\x7d\x1a\x63\xca\x18\x4e\xa3\x06\x9a\xff\x2e\x88\x0a\x0e\x8f\x65\xc9\xa8\x3a\x8d\xe8\x2a\xb1\xd8\xbf\x73\x0f\x6d\x25\xef\x39\x38\xf3\x87\x24\x5f\xf2\x98\xe8\xff\x04\xef\xe0\x89\x8f\x02\x68\x94\x3e\x13\xf9\xc9\x5f\xdf\x72\xc5\xdf\xa6\x5d\x49\x4c\x13\x61\x85\xbf\xdd\x20\x61\x43\x47\x6b\x62\x45\x9c\xb0\x42\x02\x3f\x04\x89\x9f\x05\x7e\x16\xf9\x0d\x7e\xed\xf1\x6b\x5f\x96\x1f\xa5\x30\xb8\x2a\x15\xa7\xc3\xf9\x0a\x29\x37\xf8\x7d\xc3\xe1\x77\xef\xb3\x7f\x16\xb7\xa3\x6f\x2b\xd8\x0f\x04\x33\xe6\xe6\x34\xdd\x7e\x50\x3a\xb7\xaa\xa9\xf2\x79\x9f\xaf\xfd\x6f\x34\x09\x5f\x94\xc2\xcd\x6b\x92\x7c\xde\x07\x6b\xec\x57\x56\xa1\x7c\x53\x2a\xf7\x43\x13\xe5\x93\xd2\xda\x7c\x9f\xf9\x7e\xe9\x17\x52\x7d\xaf\xf4\x8b\xd0\x5b\xb4\xcd\x96\x03\x9b\x7e\x70\xaf\x6b\xfa\x77\xca\x4e\x29\x4f\x43\x31\x21\x9a\x25\xbb\x14\xaf\xf9\xad\x3d\xbc\x55\xcc\x3e\xa5\xb3\xc4\x02\x0e\x57\xf5\x76\xe7\xce\xa7\x3e\xda\xb5\x80\xf9\x78\x4e\x62\xa8\xce\x8f\x0f\xc9\xcb\x6c\x34\xbb\xd9\xbc\xd2\x07\xe9\xca\x94\x0f\x03\x0f\xff\xed\xdf\x00\x4e\x9f\xff\xfe\xef\xe9\xd9\xf3\x47\x69\xf9\x99\xe3\xd9\x75\xe9\x26\xff\x8c\x53\x81\x42\xd1\xcf\x17\x11\x20\xfb\xbf\xc2\xe4\x58\xaf\xa1\xf4\xb9\x72\xdc\x3d\xfd\xbf\x00\x00\x00\xff\xff\x77\x2f\x21\xf7\x7c\xaf\x00\x00") +var _confLocaleLocale_enUsIni = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x7d\xeb\x72\xdc\x48\xae\xe6\x7f\x3e\x05\xdb\x27\x1c\xb6\x23\xe4\x72\x74\xf7\xde\xa2\xa3\xed\x5e\x59\x6a\x5f\xe6\x58\x96\xc6\x52\xcf\xec\xac\xc3\xc1\x66\x15\xa9\x2a\x8e\xab\xc8\x6a\x92\xe5\xb2\xfa\xc4\x89\xd8\x07\xd8\x07\xd8\x7d\xbd\xf3\x24\x0b\x7c\x00\xf2\x42\xb2\x24\xbb\x67\x62\xfd\xc3\x62\x65\x22\x6f\x48\x24\x12\x89\x04\x90\xf9\x76\x9b\x15\x65\xb7\x48\x9f\xa6\xc7\xe9\x36\xaf\xea\x75\xd9\x75\x69\x57\xae\xaf\x1f\xaf\x9a\xae\x2f\x8b\xf4\x65\xd5\xd3\xef\xf6\x53\xb5\x28\x93\x64\xd5\x6c\x4a\x02\x7d\x45\x7f\x92\x22\xef\x56\xf3\x26\x6f\x0b\x4a\x38\xb5\xef\xa4\xfc\xbc\x5d\x37\x2d\x03\xfd\x2c\x5f\xc9\xaa\x5c\x6f\xb9\x0c\xfd\x49\xba\x6a\x59\x67\x55\x4d\x3f\x2f\xe9\x2b\x7d\x5d\x4b\x4a\xb3\xeb\x2d\xe9\x7c\xd7\x4b\xda\x6e\x6b\x49\xbf\x6c\x93\xb6\x5c\x56\xd4\x9b\x96\x92\xde\xe9\x67\xb2\x2f\xe7\x5d\xd5\x73\x4b\x7f\x95\xaf\xe4\x53\xd9\x76\x55\xc3\xb5\xff\x45\xbe\x92\x6d\xbe\x64\x80\x0b\xfa\x93\xf4\xe5\x66\xbb\xce\x51\xe0\x4a\x3f\x93\x75\x5e\x2f\x77\x02\xf3\x46\x3f\x93\x45\x5b\x52\x56\x56\x97\x7b\x4a\x3d\xc1\x8f\xd9\x6c\x96\xec\x08\x09\xd9\xb6\x6d\xae\xab\x75\x99\xe5\x75\x91\x6d\x64\x98\xbf\x50\x7a\xaa\xe9\x29\xa5\xa7\x9c\x8e\x21\x94\x05\x0d\x35\xcb\x3b\x1d\x07\xe1\x92\x46\x9e\x77\x09\xaa\xaa\xf3\x8d\x95\xe6\xcf\xa4\xdc\xe4\xd5\x9a\xb1\xf6\x98\x3f\xa8\xe3\x5d\xb7\x6f\x80\xdb\x0b\xfd\x24\x24\x64\xfd\xcd\xb6\x04\x0e\x1e\x5f\xd1\x57\xb2\xc8\xb7\xfd\x62\x95\x73\x3f\xe5\x2b\x21\xa0\x6d\x43\xc8\x68\xda\x1b\xc0\xd9\x8f\xa4\x69\x97\x79\x5d\xfd\x9e\xf7\x82\xa0\xf3\xe0\x67\xb2\xa9\xda\xb6\x61\xdc\x9e\xe1\x23\xa1\xa1\x67\x5c\x0f\xa5\xbc\x25\x2c\x04\xb5\x70\xce\xa6\x5a\xb6\x82\x46\xce\x3c\xc3\x2f\xae\x85\xf3\xae\x9b\xf6\xa3\x66\xbc\xe0\xcf\x41\x51\xea\x84\xe6\xc6\xed\xe7\x35\x21\x5e\x73\xcf\xf0\x23\x02\xe8\x92\xbc\xd8\x10\x2a\xb7\x79\x5d\x32\x8e\x8e\xf9\x17\xe1\x85\x7e\x25\xf9\x62\xd1\xec\xea\x3e\xeb\xca\xbe\xaf\xea\x25\x23\xfb\x58\x92\xd2\x4b\x4d\x4a\x82\x3c\x97\x76\xd3\xec\xdc\x74\x52\xfa\xdf\xe8\x67\x7a\x21\x3f\x25\x2f\x28\x84\x4c\x57\x92\x47\xd2\x65\xd7\x65\x59\xc8\x58\xba\xf4\x05\x7d\x27\xdb\xdd\x7a\x4d\x58\xfb\x6d\x57\x76\x3d\x17\xba\xa0\xdf\x34\x7e\xf9\x9d\x54\x5d\x47\x1f\x94\xfc\x1a\x1f\x09\x4d\x5d\xbd\xc0\x60\x4e\xf0\x91\x24\xef\xbb\x32\x6f\x17\xab\x0f\x89\xfc\x45\x5f\xf9\x83\x69\xef\xd0\xa4\x32\x21\x29\x11\x49\x0b\xd6\x40\xb2\x68\x0a\xfe\x71\x42\x7f\xa8\xea\xaa\xee\xfa\x7c\xbd\xfe\x90\xe8\x07\x83\xc9\x97\x4c\x40\x5f\xf5\xc0\x82\x26\xa6\x97\x7d\xb9\xed\x78\x06\xd3\x17\x55\xdb\xf5\x8f\xfb\x8a\x88\xf5\xdd\xae\x4e\x8a\x66\xf1\x91\x96\x01\x2f\x69\xb4\xfc\xfa\x3a\x25\x64\x3d\xa0\x85\xd0\xee\xea\x9a\xd0\x93\xbe\x6c\x08\x65\xd4\x4c\x45\xed\x9f\x02\xfa\x28\xdd\xae\xcb\xbc\x23\x90\x32\x2f\xd2\x1f\xf3\xb4\xcf\xdb\x65\xd9\x3f\xbd\x97\xcd\x69\xf9\x7d\xbc\x97\xae\xda\xf2\xfa\xe9\xbd\xfb\xdd\xbd\x67\x2f\x77\x54\x6c\x5d\xd5\x65\xf7\xe3\x93\xfc\x59\xba\xc8\x29\x87\xd0\x78\x93\xce\xcb\x6b\x5e\x6d\xd4\x56\x4a\x54\x5e\x2f\x79\xa5\xdd\xf4\x2b\x6e\x90\x28\x81\x3e\xba\x94\x97\xfa\x37\x09\x4f\x00\xb1\x82\xac\x98\x1b\x5b\x43\x87\x90\xdc\xd2\x04\x9c\xdd\x5c\xfe\xf9\xcd\x51\x7a\x41\xbc\x6d\xd9\x96\xf8\xa6\xff\xa8\xc4\xf7\x29\x8d\xf6\xaa\x3a\x7d\x3e\x4b\xa8\xac\x21\xe4\x34\xef\xf3\x39\xf7\xdd\xcd\x3e\x67\xca\x22\x74\x79\x58\x8a\xcc\x2d\xc1\x19\xbb\x3e\x9a\x96\xa9\x85\x4c\x75\xe8\xf2\x77\x75\xbc\x65\x1e\x40\xe9\x0e\xb3\x17\x82\x33\xaa\x2a\x7d\xfd\xf6\xed\xf9\xe9\xf3\xb4\xac\x97\x84\x99\x74\x5f\xf5\xab\x74\xd7\x5f\xff\xb7\x6c\x59\xd6\x65\x9b\xaf\xb3\x45\xc5\x48\x69\x89\x60\x53\xc2\x92\x0c\x71\x96\x74\xdd\x9a\x58\x14\xa8\xe0\xf2\xf2\x4d\x7a\xc6\x94\xb0\xcd\xfb\x15\x3a\xd2\xaf\x92\xee\xb7\x35\x23\xca\x35\x78\xb5\x2a\x53\x2c\x06\x00\x35\xd7\x43\xbc\xa4\x85\xf6\x75\x96\x94\x6d\x9b\x11\x07\xed\x6f\x18\xcd\x5a\xe7\x21\x68\xa9\x8e\xa8\xbd\x6e\x7a\x9a\xc6\x14\xe5\xa4\x8a\xaa\xfe\x94\xaf\xab\x82\x90\xed\x11\x12\x97\x45\x62\xd1\xd0\xbc\x71\x69\xa2\xcc\x66\x8f\xa1\xe6\x0b\xda\x00\xba\xf4\xde\xec\x1e\x38\xee\xbd\xc7\xf7\x66\x49\xdd\x64\xc2\x25\x98\x37\x17\x55\x97\xcf\x89\x4f\xcb\xbe\xd1\x1a\xd7\xfb\x1b\xd3\x8f\x74\x45\x21\xd2\x08\x82\x71\xcb\x7b\x11\xb6\x00\x26\xae\x9c\x18\x36\x98\x8d\xb2\x99\x70\xec\xc6\x93\xdc\xfc\x0a\x5b\x72\x09\xa3\x31\x27\x36\x61\x46\x5d\xc7\xdb\xed\xba\x5a\x48\xd3\x2f\x25\xcf\x13\x1a\xef\xcc\x8a\x94\x10\x0e\x84\x62\x79\x01\xb9\x50\xaf\x99\x6d\xa5\x11\x9f\x47\xf9\x55\x49\x2b\x67\xb5\x5b\xca\xee\xb4\x6e\x76\xc5\x37\x60\x28\x36\x73\x9e\x9f\xa4\xef\x1a\xea\x30\xa8\xc3\x01\xf8\x26\x8e\x89\x31\xb0\x30\xd0\x96\x9b\xa6\x67\xc4\x69\xb1\x8a\xa6\x67\x5f\x51\x26\x8d\xb4\xcb\x3f\x11\x5b\xec\x1b\x59\x92\x05\x2d\xb9\x05\x57\x4c\x1c\x6c\x47\x3b\xba\x2c\x0b\xe2\x23\xb2\x34\x2c\x2d\xa6\x41\x40\x6d\x76\xb4\x9a\x56\x54\x19\x23\x9e\x25\x12\xaa\x72\xaa\x9f\x18\x12\xd5\x83\x55\x4e\x2b\xb7\xa1\xcd\x93\x27\xfa\x14\x1f\xfa\x3b\xac\x9f\x7a\x95\x5f\x5f\x53\xaf\x3a\x5a\x15\xaf\xd2\xc5\xba\xa1\x25\xf5\xcb\xbb\x37\x1d\x2f\x98\x55\xb6\x6d\x5a\x48\x22\x94\x75\x41\x9f\x2e\x2d\x40\x34\x43\xd4\xbb\xcd\x9c\x7e\xed\x57\x15\x31\x6a\xa0\x9d\x4b\xb0\x94\x44\xa9\xd4\xc4\xae\xa3\x29\x3c\x4a\x69\x09\xd3\x08\x08\x65\x20\x00\x1e\x83\x51\x1d\x83\x5f\x13\x8d\xed\x5a\x5a\x4e\xab\xbe\xdf\x5a\xcb\xaf\xae\xae\x2e\xa4\x69\x97\x7a\x5b\xdb\x79\x40\x19\x98\x83\x35\xcb\x46\x75\xda\xd4\x33\x10\xc9\xae\x5d\x0f\xe8\x87\xc6\x6a\x39\x07\xf0\xc2\x5d\x78\xc2\xff\x5d\x7a\xf4\x00\xcf\x1d\x49\x7d\x7b\x50\x13\xe1\xb8\x84\x9c\x42\x44\xdd\x6c\xb9\xde\x80\xaa\xcf\x35\xc1\x93\x32\x64\x1b\x97\x2f\x12\x0e\xe5\x42\xa6\x0c\x76\xe9\x0d\x0d\x58\xd9\xe8\xe5\x19\xa1\x01\xbc\x14\xa9\xd7\x6d\xb3\xa1\xd4\x17\xf4\xc7\x27\xf8\xee\x9f\x71\x7d\x80\xc9\x8b\x82\xb8\x7c\x77\x94\xbe\x7b\x71\x92\xfe\xe7\xef\xbf\xfb\x6e\x96\xbe\xee\x79\x25\x32\x71\xfe\x9d\x89\x8a\x3e\x45\xd4\x72\xa0\xc4\xb1\x7a\xa2\xbb\x7b\xbc\xb2\xee\xa5\x3f\x22\xf7\xbf\x97\x9f\x73\x12\x11\xcb\xd9\xa2\xd9\x3c\x63\xae\xba\xc9\x69\xed\x73\x0e\x91\xab\xd2\xf1\x65\x59\x17\xf4\xa1\x02\x9b\xe6\x05\xec\x40\xf3\x03\xf1\x4d\x04\xd7\x6c\xd1\xd4\xd7\x55\xcb\x03\xfa\xb9\x06\x35\x98\x48\x4b\xdb\x35\x72\x4c\x2a\x22\xa4\x11\x07\xa9\xae\x6f\x3c\x28\x86\xfa\x96\x13\x75\x42\x13\xa1\xba\x4c\x45\x74\x87\xe5\x4b\x21\x46\x9e\xb7\x73\x1a\x5e\x6b\xf8\xee\x3c\xc2\x9b\xeb\x6b\xde\x6b\x6d\x97\xd0\x16\xce\x25\x55\x36\x8c\x10\x84\x88\x71\x0b\xa1\xfc\x54\x89\xf8\xe4\xf4\x6d\x5a\x7e\x22\x6a\x63\xae\xd7\x36\xc5\x6e\x01\x0a\x63\xd8\x23\x66\xd6\xc4\x22\x3a\x5a\x1b\x0b\xd9\x57\x02\x26\xc1\x5d\x63\x4e\xb4\x20\x20\xe2\x0d\xc6\xac\x49\x90\xfc\x44\x9c\xbf\x0d\x9a\x78\x69\x49\xda\xfb\x11\xec\xa8\x53\xae\x04\x8f\x7c\x41\x33\x4e\x54\x21\xbd\xe8\xa4\x53\x92\x4d\xe4\x4e\x74\xbc\xa3\x13\x4a\x5e\x50\x5f\xe6\x37\xe0\x3b\x1d\x13\x43\x51\x5e\xe7\xbb\x75\xef\xfb\x35\xd8\x44\xac\xa5\x4b\x3e\x24\x85\x79\x93\x05\x46\x1d\x04\xf5\x74\xc3\xb2\x44\x86\x35\xc9\x39\xb2\xd9\x30\xbd\xca\x29\xc4\xf6\x1d\x62\x4f\x25\xa6\x27\xf3\x22\xbf\xce\x97\x49\xfe\x71\xbe\x6b\xf6\x9d\x48\x3e\x29\xb6\x5a\xae\xd1\x2a\x60\x51\x61\xba\x2f\xb3\x44\xc5\xa5\x4c\x8f\x6b\xd9\xa7\x0a\x87\x21\x47\xae\x52\xa5\x1e\xe1\x98\xaf\xfd\x85\x01\xf8\x94\xd5\x4d\x96\x75\xbd\x39\xe7\x41\x76\xee\x30\x24\x38\xe7\xe1\xa2\x05\x16\xe1\x68\x96\x3e\x55\x60\xf3\x4a\x30\xc0\x0b\x51\x0d\x9a\xa6\xa6\xba\xb2\x44\x0d\x54\xfe\x09\xd5\x89\x32\x33\x3d\x20\xa8\xcc\x6e\xa2\x1f\x6f\xf7\x45\x03\xd9\x01\x7b\x09\x95\x36\xb4\x0e\xf6\xf5\xb4\xad\x96\x2b\xe2\xad\xcd\xfe\x48\x90\xb2\x5f\x35\x25\xaf\x9f\xd7\xa7\x4f\xbf\x95\x7e\x2c\x79\x67\x71\x85\x78\x4f\xca\x77\x44\x5c\x84\x31\x25\x63\xe9\x82\xdb\xdb\x01\x39\x3a\x8a\x08\xd0\xf0\xf0\x37\x12\x25\x1c\xd3\x50\x5e\x11\xe6\x29\x93\xf0\x30\x52\xda\x0e\x90\xd2\xb0\x72\x25\x95\xf7\xb3\x65\x83\x83\x8c\xc9\xf7\xbc\x5b\xd2\x79\xb8\xeb\xb3\x65\xd5\x67\xd7\xcc\xba\xb8\xe6\x17\x5c\x03\x6f\xde\x94\x93\x3e\xa0\xac\x07\x29\xf1\x3f\x3a\x9d\x15\x3f\xa4\xf7\x3f\xa9\xc4\xf8\x3d\xf3\xa4\x8c\x56\x51\xb5\xc6\x94\xe8\xf1\xa8\x2d\x45\x60\xb5\x33\xb8\x93\xda\xba\xdd\x16\x7b\x9b\x0a\x88\xee\x34\x50\x34\xfb\x9a\x57\x1f\x98\x2f\xf1\x99\x6a\x51\xd1\x9e\x31\xaf\xea\x9c\x36\x78\xab\x05\x4c\xfd\x3e\xd1\xc4\xdb\xf3\x2b\x00\x2e\x9b\xf9\xae\x5a\x17\x06\x30\x4b\x4c\x88\x24\x11\x52\x67\x3f\x14\xab\x2d\xa9\x92\xbe\x2c\x9a\x96\x25\x12\x8c\xc6\x0a\x1e\x10\x85\x5a\x16\x31\x90\x5c\xf1\x79\x06\xb0\x28\xe7\xa4\x16\x46\x03\x4d\x3f\x8e\x6a\x2c\xd3\x80\x6e\xaa\xae\x7e\xd0\xa3\xa7\x8b\x1d\xb5\x45\x53\xcf\xc9\x54\xb0\x4b\x1f\x3f\xa3\xff\x13\x96\x90\x64\x07\x58\x8e\x11\xcf\x99\xa9\x64\xee\x64\x2d\x46\x5d\x8d\x88\xdc\x4d\xb5\x91\x70\x30\xd6\xb0\xbf\x46\x02\xdd\x4e\xa8\x96\xd5\x25\x6b\x9a\xd6\xf2\x1b\xfa\xe0\x93\xdb\x72\x8d\x49\xc8\x7b\x3d\x5e\x35\x84\x37\x26\x90\x23\x59\x34\xd7\x34\x34\xe6\xa5\x7d\xfe\xb1\xc4\x89\x8c\xf6\xfc\xf7\xac\x07\xfa\x90\xec\x44\x06\x6d\xd6\x85\x3b\xef\x80\xb2\x9b\x76\xa8\xc6\xf0\x40\x8e\x6a\x3b\x12\xb6\x17\xab\xcc\x69\x91\x18\x29\x7d\xf9\x19\xbb\x3f\xb2\xbc\x52\x89\x49\x9e\xb3\x92\xcd\x0d\xa6\x8b\x07\x71\x76\xe3\x67\x8b\x24\x50\x5a\x28\x74\x98\x9d\x37\x8c\xb5\x4f\xa5\x83\x3a\x09\x53\xe3\x02\x54\x17\xc9\xca\x5a\x55\xac\x6d\xa0\x2c\x51\x89\x68\xae\xa8\x45\xba\x04\xac\x4c\x55\x60\xe0\x78\x34\x9f\x7a\xb2\x9f\xd1\xc4\x40\x6d\x60\x2d\x13\x5f\xbc\x91\x75\x11\xb4\x99\xbc\x57\xf5\xd8\x87\xc4\xe0\xde\xc5\xf9\xc4\x53\x56\x1f\x02\x15\x54\x66\xb3\x6b\xaa\x28\x68\x4f\x94\xad\x78\x91\x62\x55\x6e\x59\xfa\xd8\x74\x20\x8b\x35\x9f\xb4\x6f\x54\x7e\x76\x04\xf2\x93\x30\x6c\xa2\x18\x62\x73\xdf\x24\x5d\xc3\x0b\x2e\xfb\xca\x2a\x9e\x57\x44\x0a\x28\x1f\x6f\x76\xa2\x1b\x23\x31\x97\xa7\x8f\x56\xd9\xcd\x51\x7c\xb2\x5a\xe5\x1d\x31\x71\x92\x15\xb4\x58\x31\xb3\x13\x2e\x4f\x3b\x9d\xe7\xb0\x66\xa0\xcf\x03\x95\x4b\xc9\xa6\x1d\xee\xc2\xdc\x43\xe1\x73\xda\x8a\x93\x9d\x20\x19\x85\x02\xd4\x44\x9b\x84\xb0\x4d\xc9\xe2\x73\xb6\x11\x35\x9a\xfc\x4a\xcf\xca\x84\xb6\xc3\x25\x2d\x68\x23\xd8\xa7\xac\xfd\x58\xe2\x94\xa1\xf4\xca\x00\x65\x1f\x32\x62\x85\xb0\x94\x9f\x4c\x6f\x49\x9c\x61\x0f\xd5\x10\xad\xed\x11\xfa\x69\xcb\xa2\xec\x99\x31\x76\x91\x11\x20\xea\x75\xc4\x2d\x3c\x12\x8f\x53\x56\x40\x86\x50\x2a\x72\xfb\x61\x71\x01\xe6\x1a\x3f\xce\x9f\xdd\xef\x7e\x7c\x32\x7f\xe6\x78\xeb\x62\x55\x2e\x3e\x0a\xfd\x55\xf5\xbc\xf9\x8c\x83\x2d\x14\x25\x74\xa6\xe6\x35\x76\xbf\x48\xe9\xa0\xdb\xe2\x5c\x45\xbc\x80\x8a\x11\xe2\x39\x37\x9a\x34\xea\x0c\xb3\x0c\xda\xda\x16\x58\x55\x20\x70\x4f\x90\xc7\x9c\xca\x24\x89\x0d\xc0\xd3\x24\x06\xb2\xae\x36\x55\x3f\xa2\x09\xe6\x30\xb9\xd2\x96\xea\xca\x0c\x49\xa8\x0b\xc3\xc4\x28\x89\x4f\x53\x35\xb4\xaf\x1a\x9d\xec\x73\x3a\x48\x7d\x9f\x12\x6d\xec\x68\x7f\xe2\xce\xd2\x78\x88\x51\xe7\xbc\x31\xd3\x21\x2a\xef\xb2\x5d\xad\xf8\x2a\x0b\xa3\x92\x57\x15\xb6\x0f\x6e\xd7\x68\x39\x80\x32\x94\xea\x59\x20\x7d\xe8\x50\xf9\x68\xa6\xba\x2d\x14\x63\x9e\xce\x1d\xaa\x58\x6e\xcd\x27\x67\x85\x78\x5e\x5d\xca\xd9\x17\x18\x60\x38\x9e\x41\x3a\x40\xf9\x69\xa1\x53\xd8\x47\x4a\x01\xa6\xe7\xbb\xbe\x6f\xf8\x5c\xb2\x66\x72\x90\x32\xd6\xeb\x13\x00\xe2\xa8\xe5\xeb\xc3\x74\x86\x78\x12\x16\x5b\xda\x39\x21\x23\x3a\xe4\x35\x2d\x1a\x6e\x3e\xd1\x0d\x46\xa7\x9b\xa0\x03\x2b\x44\x97\x94\xd7\x37\x5e\xbd\x81\x5e\x70\x83\xfd\x74\x5f\x1e\xb6\xe5\x23\xdf\x1b\xb7\x18\x50\xc2\x7a\x24\xc5\x83\x85\xf2\x0e\xb9\xa2\x62\xb5\xe5\x64\x7b\x9a\x2a\x2a\x3d\x7d\xb4\x31\x7a\x91\xcf\x24\x4f\x9c\x93\xc4\xca\x02\x88\xa6\x51\xa0\xf4\x6c\xd0\x96\x3f\x12\x8e\x31\xd8\xc7\x5d\xf6\x5b\x53\xdf\x34\x59\xb7\x92\xe3\xb7\x75\x8f\x8e\xee\xf5\x32\xd2\x5b\xe1\x5e\x04\x44\xf7\x5f\x78\x03\xe4\x81\x7e\x48\x74\x36\xca\x60\x51\x28\xb5\x5a\x8e\xcd\x9a\xac\x0d\x07\x6f\xc2\xda\x5f\xca\x96\x8f\x77\x00\x8a\x67\xeb\x10\x16\xe3\x41\x38\xa6\xe8\x77\x77\xc7\x10\x35\xe9\xc8\xf6\x7b\x0f\xec\x0e\x95\x9a\xc3\x24\x29\xb7\x37\x34\xb0\xa6\xc8\x69\x64\x37\x50\x56\xff\x8d\xf6\xa4\x1a\xd7\x00\x4d\x42\x19\x52\xe8\x0c\x1f\x04\xca\xc7\xe2\x0f\x09\xef\xfa\x6f\x07\xf2\x2c\x6f\x6a\x9a\x16\x88\x54\xc8\xfa\x39\xba\xe7\x70\xa3\xbd\x98\x90\x7d\xdf\x95\xfe\xba\x03\x5f\x6e\xd8\x97\x97\xaf\xae\xec\x9c\x7b\xf9\x2a\xfd\x58\x6a\xe5\xaf\xfa\x7e\xdb\xfd\x02\x9d\x87\x28\x30\x58\xdb\x71\x91\xdf\xb0\x9c\x29\xc9\xfa\x03\x19\x57\x65\xbe\xd1\x5e\xf2\xa7\x54\x71\x4c\x1b\xb0\x26\xf2\x27\xed\xcb\x81\x2e\x2d\x81\xc4\xf5\xf3\x94\xa4\xed\xce\x3d\xa5\xde\xa5\xfc\x3a\x52\x00\xfe\x9a\xe4\xeb\x2d\x1d\xcd\x58\xe4\x09\xc0\xa0\xeb\x9a\xeb\x09\x2d\x05\x08\x88\x7c\xb7\x21\xe2\x58\xe0\x44\x4a\x05\x1e\x3e\xce\x1e\x05\xba\xcf\xb8\xb2\x82\x56\xff\x1f\xaa\x90\xbf\x59\x2e\x0e\xeb\xed\xaa\xdf\x6d\x14\x51\x75\x9c\x4e\xcc\x94\x20\x20\x85\x7a\x28\x07\x84\xad\x9c\x25\xd2\x9e\x55\x5f\x94\x40\x52\x6f\x54\xf5\x26\xff\x7c\x57\xc1\x4d\x33\x51\x4e\x78\x9c\x2f\x64\x9c\x4c\x87\x18\xaf\x1c\x82\x67\xe5\xd6\x41\x68\x9a\x7a\x02\xa9\xea\xc5\x7a\x57\x1c\xec\x49\xb7\x9b\xd3\x36\xc8\xe2\xf4\x83\xfb\xdd\x03\xae\xb2\xfe\x48\xfb\x76\xed\xe0\x7f\x91\xdf\x29\x7e\xff\x60\x37\x71\x74\xe0\xd5\x33\x46\xea\xee\xe4\x48\xfc\x28\x78\x03\xc1\x59\x61\xe6\xf9\x4e\x78\x7e\x70\xe4\x0f\xad\x85\x1e\xf0\xdc\xda\x67\x55\x05\x8e\x52\x44\x82\x33\x7f\x7d\x98\xb1\x14\x90\xb1\x5c\x5e\x87\xd2\xb7\x93\x0f\x6c\xa3\x05\x84\x5c\x22\x65\xe3\x72\x83\x05\x7a\xb0\x38\x09\x3b\x13\xa5\xcf\xc7\xea\xe6\x03\xe5\x7b\x5a\x62\x13\x15\xb8\x95\x77\xb0\xa0\x4c\x3e\x0a\xd1\xc8\x8b\x11\xef\x18\x17\x64\x30\x3a\x19\xae\xd7\xe5\x92\xf5\x92\xd6\x70\xd4\x9a\x4e\x34\xed\x8a\x02\x16\x12\x9c\xc7\xb0\x9b\xac\x70\x5e\xc3\x73\x8e\x9b\xa3\xf8\x84\xc9\xea\x1a\xaa\xaa\xc5\x15\x70\x70\xce\xd4\x6e\xe8\x26\xb1\xe1\x23\x55\xb7\xe3\x3d\x8a\x8f\x5f\x22\x7f\xc5\xb3\xc1\x12\x08\xaa\x2a\xd1\xc4\xe1\xea\x89\x16\x99\xb3\xdf\x55\x3f\xc0\xbe\xb2\xea\x50\x2f\x31\xae\x58\x2b\x77\x40\x87\xaa\x75\x67\xe6\xf2\x73\x05\x1d\xef\xcb\x8a\x75\x87\x38\x35\x3b\x65\x01\xf2\x66\xc9\x9a\x98\x07\x9f\xce\x64\x54\x22\xa9\x37\x9f\x78\x35\x72\x7b\x9c\x2b\xe5\x44\xe7\xab\x83\xe2\x79\xd6\xf3\x37\x6e\x8a\xca\xe2\x88\x64\x1d\x2e\x41\xfd\xc4\xe2\xce\xd7\xfb\xfc\xa6\x83\x32\xc9\x38\x14\xeb\xb7\xa5\x38\xb3\x1f\x92\x84\x96\xe8\x55\x78\x8b\x42\x2b\xce\x30\xc1\xd7\x01\xbc\xd9\x38\x79\x65\x8f\x13\x34\xb8\x8b\xaa\xa7\x3e\x05\x3b\xba\xee\x4d\x7c\xfa\xe7\xb3\x32\x9f\x62\x24\x3b\xa8\x08\xd7\x93\xba\x53\x4c\x94\x3d\x62\x39\x91\x9a\x61\xa9\x8d\xf8\xb7\xe0\x9a\x04\x61\xc2\x2c\xba\x14\xa8\x53\x76\x54\xff\x63\x91\xfc\x2b\xc2\x21\x9f\x24\xbd\x86\x81\xf7\x32\x9a\x15\xbb\x05\x90\x74\xe8\x07\x92\xae\xa7\x25\xc0\x98\xb6\x3b\xff\xbf\x05\x22\x4b\x8a\x5c\x2c\x31\xa0\xa9\x5b\x55\xdb\xb4\x81\x66\x39\x44\xa1\x27\xdb\x40\xd6\xe6\xfb\x8e\x12\x27\x0b\x56\xb1\xb7\x79\xdd\x5d\x97\xd0\xb5\x6f\xd2\x6b\xbe\x56\x9e\x69\xd3\x2c\xba\xcb\xdd\xff\x81\x96\xe5\x94\x86\xa6\xc3\xdd\x05\x73\x17\x4c\x54\xdc\xb4\x5c\xbe\x40\x9f\x8b\x3e\x00\xab\xbe\xa6\xce\xfa\xc0\x64\x36\x42\x01\xc4\xe7\xe8\x2a\xcd\x7a\xf3\xa9\x0c\x11\x71\xfd\x47\x47\x1e\x60\x5d\xaf\x13\xe4\x0e\x26\x9e\x26\x69\x14\x0a\x1d\xdc\x04\xcf\x6f\xe2\xd1\x73\xd1\xe0\x7a\x9d\xd6\x48\xa9\xad\xf0\xc2\xe0\xb5\x32\xa8\x10\x8a\x1c\x7f\x68\x4a\xe4\x2a\x3e\x9b\x53\x17\x17\xab\x68\x75\x5e\x21\x27\x95\x9c\xd1\x02\x4d\xde\x73\xd3\x1f\x12\xb9\x8c\xcf\x9c\xde\xfe\x44\x2e\xe7\x45\xe8\x55\x3d\x7c\x9f\x9a\xb2\x9e\x6f\x53\xac\x88\xa8\xe6\x6f\x2d\xc9\xdb\xb0\xe9\x4d\xff\xde\x90\xcc\x01\xf5\xfb\x9f\xe8\x8b\x8f\x01\x75\x12\xdd\x40\x0e\x34\x29\x90\xb4\xab\x9e\x57\xd8\x05\x2d\x0c\x12\x7b\x8e\x35\x85\x0e\xf2\xe0\x0e\x50\xee\xbc\xb0\x6f\x9a\x8f\x9c\x99\x1e\x2f\x6d\xf9\x52\x38\xd1\xb4\xbd\xb0\xef\x84\xf5\x00\x9b\x19\x36\x07\x96\xd0\x71\x93\x11\x6c\x09\x2c\x2d\xf0\x84\x59\xde\x2c\x80\xdf\xe6\x3d\xb1\xc5\x5a\xce\x6a\xc2\xa1\xc2\xa2\x9a\xed\xaa\x70\x57\xde\x5c\x0b\x9b\x87\x08\x2a\x3e\x24\xde\x6a\xc5\x0c\x56\xa6\x34\xc7\xca\x62\x3a\x95\x91\xff\x95\x3e\x55\xe7\x03\xf6\x85\x0f\x3d\xb3\xe3\xb2\xd9\x6e\x08\x61\x41\x13\xfc\x4c\x54\x4b\x16\xab\xc8\x94\xbc\x9f\xa6\xa7\xf2\x61\xa7\xff\x5d\x85\x31\x55\x74\x94\xd8\x02\xef\x81\x8d\x8d\x4e\x84\xeb\xb4\xda\x52\x79\x65\x7d\x3b\xdc\xd9\xd9\xac\x43\x0a\x31\xe1\xda\xfd\x11\xa4\x00\xbe\xbe\x08\x4e\xae\xac\x7f\xc6\x91\xb6\x0e\xee\xc6\xf8\xc6\x87\x0f\xe2\x04\xb6\x2f\xe7\x29\x6b\x84\x89\x70\xe8\x80\xa8\x03\xdd\xe4\x74\xb6\xfc\x54\xe5\x4e\xf7\x44\xb3\xc5\x56\x3c\xba\x8b\xbe\x60\x0b\x1e\xdc\xb7\x8f\x4d\xcd\xf8\xf2\x4a\xef\x83\xde\xe8\x67\xb2\xdb\xf2\x05\x4b\x30\xe0\x5f\x90\xe0\x06\x1c\xe7\x07\x47\x36\x0c\xdd\x8a\x39\x69\x46\xc0\x0b\x3b\xc7\xc1\x10\x66\x66\xcb\x67\xc2\x84\x4c\x97\x50\x31\x04\xf1\x4a\x18\xb0\x18\xb5\x9f\x01\x32\xe5\xca\x17\xc3\xa7\x9d\x31\x5d\x35\xfb\x74\x5d\xd5\x1f\x3b\xc5\x26\xf3\xb1\xf0\xfc\x0a\xad\x15\x11\xe1\x4e\x4c\x8b\xe4\x73\x6c\xc9\x64\x37\x51\x83\x15\x6e\xf7\x55\x72\x27\x77\x8c\xe4\x49\x58\x7f\x8a\xb7\x3b\xb3\xeb\x92\xc5\x64\x30\x35\xbb\xdf\xa3\x51\x36\x4d\xa7\x3a\x52\xcf\x44\x38\x0d\x7a\x17\x85\x52\x9c\x3b\x08\x9d\x92\x63\xbb\x55\xc4\x9a\x4a\xec\x1e\xd0\x3a\x80\x15\x9a\x55\x1b\xb1\x0c\xfc\xc5\x6e\x09\x31\x3f\xee\x34\x81\x6c\xd8\x9d\xc4\xbd\x0f\xaf\x46\xde\x36\x76\x07\x69\xdc\xd0\x32\x8f\x6c\xd3\x17\x0c\x60\xcb\x8e\x3a\x3b\xa4\x0f\xad\xc0\xb4\xfc\x77\x90\x89\x11\x41\x78\x6f\x24\x13\xef\x18\x44\xb3\x8e\x44\xbb\x13\xbd\xaf\x70\xf9\x8c\xd9\x20\xff\x2d\x6e\xf8\x9c\xde\x81\x0f\xe4\xd9\x00\x44\x0f\xec\x11\xe4\xa4\x04\x6d\x6d\x1d\x94\x9e\x07\xbd\x1f\xad\x15\x2b\xb7\x27\x2c\x84\x03\x57\xea\x2e\x66\x66\xea\xc3\xca\x56\xb9\x2e\x84\x4d\x86\xd8\xa5\xd4\xb8\x6b\x94\x2a\x08\x55\x38\x60\x74\xfe\x5c\x71\x2c\xdc\x87\xef\x08\xc4\x30\xd1\x01\xa8\x6d\x62\x7c\xde\x2c\xcd\xc0\x21\x64\x64\xdb\x96\xc8\x83\x36\xda\x81\x0a\x6e\xc4\xc2\x22\x76\x05\x6e\xd5\xe0\xb6\xde\x73\x29\x3a\x31\x6a\x5d\xcc\xef\xf1\x65\x29\x4e\x8d\x44\x74\xcc\xa2\xae\x26\x2b\x73\x76\xb9\xc2\xa2\x5d\x27\xe9\x87\x30\x2e\x1d\xee\xa9\xa6\x0c\x00\x6c\x38\xca\xe0\xfb\x09\x85\x22\x46\xa3\x62\x87\xf1\xdf\xaa\x16\x6b\x09\x77\x7b\x17\x31\x90\xf4\x14\x1c\x85\xe6\x4d\x54\xd7\xc6\x4f\x7e\x1a\x36\xee\x27\xfc\xe7\x81\xd6\x5b\x06\x17\xd3\xfb\x37\x09\x75\x09\xd4\xe8\x6f\x41\x0b\x4c\xf3\x40\xa9\xc6\x60\x21\x88\x6a\x2c\x5d\x72\x16\xa9\xe5\xa1\x60\xff\x2a\x55\x3c\xef\xdd\xff\x04\x2d\x7c\xd4\x96\xd7\xc2\xbb\x5e\x0e\x96\x03\x77\x6f\xb0\x73\x8e\x16\x06\x65\x40\x8e\x50\x92\x0e\xa4\x03\x25\x6a\x27\x24\x70\x33\x72\x36\x61\x0c\x51\x12\x44\x09\xa5\x06\x6c\x21\x2c\xa8\xc2\xd2\x08\x66\x82\x72\x50\xe9\x46\x7a\xe5\x78\xe2\x8f\x71\x12\x23\xac\x08\x2c\x4c\xf9\x68\x63\x16\x29\x56\x4f\x76\x1b\x46\x84\xdc\xb3\x3b\xab\xaf\xd1\x25\xda\x91\x1e\x7f\x56\xd5\x72\x45\xe3\xaa\x36\x7c\xbb\x0c\x72\xb2\x2b\x4c\x7f\x3a\xe5\x5f\xc4\x50\x9a\x65\xcd\xba\x2b\x6e\x41\xcc\xbc\xdc\x06\xf3\x63\xd7\xb7\x4d\xbd\x7c\x76\xda\xf0\xb1\x91\x35\x3a\xbc\x09\xfe\xf4\xe3\x13\x4d\x27\xa6\xc9\x73\xc8\x36\x81\x2f\xab\xfe\xd5\x6e\xfe\xa0\x4b\x97\x6c\xa4\x8a\x7b\x97\x3c\x30\x5d\x55\xc3\x02\xb1\xc1\xdb\xd7\x0e\x2d\x30\x64\xa5\x85\xde\x35\x6b\x5a\x25\x71\x91\x66\xb3\x91\xf9\xa5\x0d\x60\x23\x90\xe8\x3f\x6c\x11\xca\x1a\x98\x2b\x5b\xc5\x0f\x55\x38\x73\x64\xee\xe7\x47\xa7\xcd\xc4\xbd\x48\x4f\xa2\x02\x17\x03\xe3\x72\xb5\xee\x83\x6d\x83\x75\x24\xa9\x2b\x06\x41\x61\x5c\x0c\x13\xc9\x6a\x27\xaf\xa2\x31\x25\x0b\x4e\x02\xa8\xc3\xca\x53\x51\xea\x89\x48\x4c\x9c\x66\x6d\x8a\xac\x50\xb2\xfe\x5b\x48\x2b\xa0\x5f\xde\x2b\x4c\x85\x0b\xc1\xd7\x2b\x73\x98\x60\x07\xab\x5c\x19\x9b\x8c\x5e\xd9\x9a\x8d\x20\x60\x6c\x8a\x13\xcf\xd9\x86\x30\x53\xbc\xcd\x7a\x11\x32\x35\x31\x62\x12\xc6\x26\x24\x49\x27\x0d\x66\xdb\x5f\xc8\xd4\x46\xed\xfa\x81\x5b\x73\x5f\xc0\xd7\x30\xa6\x63\xa0\x83\xc6\x02\xdd\x88\xce\xd4\x1b\xd5\x84\x20\x83\x0d\x60\xfd\xa9\xe7\x6d\xa3\x57\x68\xa9\x25\x62\x4e\xe8\x98\xd3\x97\xd1\x62\xe6\x4e\xc0\x64\x51\x4c\x72\xa0\x5c\xf9\xaf\x69\x91\x13\x27\xe8\x9b\x8f\x44\x4c\xe3\x22\x48\x3f\x54\xc8\x71\x18\x3b\x6b\x28\x7f\x39\xf6\xec\x61\x78\xfa\xd0\xab\xe8\x83\x2c\x26\xe0\x2c\x5a\xab\x33\x8b\x12\xcd\x10\xcc\xc1\xd9\x76\xa4\x10\x4e\xa2\x8c\x40\x6d\x7f\x1c\x07\x20\x09\xab\x66\x20\xa8\x6f\xf9\x43\x7f\x87\xd3\x12\xd5\x1f\x2c\x17\x62\xe0\xbb\x3a\x60\xa0\x42\x0e\x99\xa0\xc2\x0d\xf2\x82\x8e\x92\xb0\x7d\x3c\x96\x0a\xaf\x38\xbb\x53\xc3\x5f\xbd\xd1\xb7\x22\x2f\x35\x11\x6b\x00\x80\x82\xf0\xce\x21\x02\xbf\xbc\x56\xc1\x6a\x51\x6b\x0d\xb5\x6a\xc4\x1c\x10\xd5\x19\xcb\x5c\x89\xf5\x46\x7a\x7c\xf1\x9a\xf6\x0c\xd7\xa0\x55\xfa\x73\x4e\x92\xb4\x74\x61\xef\x34\x1a\x4c\x6c\x43\x9e\xeb\x64\x7e\x29\x6e\x0a\x54\x94\xc4\x12\x77\x83\x1a\x0d\x48\x06\x13\xe7\x0b\x8e\xcb\x2e\xd0\xf2\x48\x6b\xe8\xc9\x70\xb7\x72\x43\xfd\x86\x30\xeb\x74\x8d\xbc\xb4\xb6\x37\xcc\xff\x03\x73\xad\x5c\x30\xb4\x07\x07\x1f\xd8\x89\x11\x24\x34\x1d\x29\x2f\xe1\xd6\xf1\x0f\xeb\xb0\x72\x90\x70\x2a\x43\x36\x32\x39\x99\x9e\xa9\x4c\x16\x9b\xe2\x2c\x5b\xab\x27\x1e\xf3\x5d\x7c\x86\x09\xdf\x9f\xc3\x6f\xe1\x32\xe1\xa8\x02\x52\xbe\x98\x6c\xd6\x51\xb4\x34\x3d\xe0\x37\xa9\x6c\x84\x62\xeb\xc0\xad\xc8\xe9\x42\x29\x22\x30\x23\xa6\x5a\xf6\xe5\x9a\xed\x7f\xb5\x75\x7f\x03\xaa\x43\x8f\x8c\x02\x14\x28\x38\x89\x96\x5e\xc4\x15\x54\x84\x6a\x3a\xab\x8c\x20\x68\xb9\xc1\x0e\x40\x8e\xf2\xb6\x5f\x9f\x1c\xbf\x7d\x7b\x7e\xe5\xb7\x69\x5e\x07\x75\x41\xc2\xc4\x37\xce\x62\x6e\xd4\x2f\xb3\x9b\x73\x13\x18\x43\x78\xcb\x3d\x2d\x71\x08\x2e\x64\x53\x56\x3b\x7d\x2e\x1b\xf0\x9e\x86\xfb\x62\xbc\x3c\xea\x7f\x71\x68\xfe\x92\xf7\x2c\xdf\x7c\x48\x4c\xd9\x7d\xce\x7f\x93\xf0\xbe\x20\xb8\xa3\xc1\xd2\xf3\x57\x39\xde\x3a\x9f\x3a\xd0\x14\xa3\xfb\x03\x30\xe9\x5d\x8e\xa3\x11\xe1\xbe\xc1\x5e\x71\x9d\xe2\xbe\xfb\x88\xd5\xa1\x4d\x8b\x05\xc3\xc8\xdd\xd5\xd5\x6f\x3b\x08\x68\x7c\x30\x22\xe6\xc1\x86\x98\xf3\x6a\x2d\x1b\xca\x5f\xdc\x0f\x49\xe7\xaf\x81\x05\x79\xd0\x38\xfd\xfa\xb1\xdb\xb2\x1d\x2b\xed\x0d\xdd\xd3\x7b\xbb\x2a\x65\xed\x1a\x5b\x71\xdd\x7b\x46\xc7\x18\xbe\x05\xa7\xe9\x23\x88\x67\xa3\xea\xd8\x8b\x6c\x21\xaa\x38\x67\x0f\x04\xba\xd5\x74\x5e\x2d\x2c\xf2\x46\xfa\x3f\x41\xfc\x1f\x68\x93\x5d\xd6\xfc\x38\x1e\xea\x29\x99\x70\x84\xb5\xfb\x29\x5f\xef\x62\x65\x09\xb7\xce\x65\xba\x47\x09\xcc\xe3\x7d\x59\x18\xfc\xc0\xdb\x91\x33\x88\x1a\x7e\x02\xd2\xfa\xdb\x7d\x9e\xd8\x2f\x92\x45\xbf\x6f\x12\xf4\x44\x15\xd0\x43\xef\x39\xe4\x99\x19\x3c\xe7\xc1\x16\x1e\xa9\x13\xb3\x11\xf8\xb9\xe4\xeb\x5e\x54\xcf\x69\x30\x9b\xcc\x5a\x30\x88\x32\xa4\x18\xbd\x22\x74\x1c\xac\x5b\xb4\x15\x6c\xf9\x25\x9d\x7d\x25\xd3\xc0\x4f\xd2\x25\xfa\x76\x2f\x89\xee\x09\x45\xb3\x65\xd5\xd3\x91\x9b\x3d\xb6\x60\xf9\x9d\x10\xdb\xa0\x9d\x0c\x5e\x96\xf2\x65\x29\xa3\xa2\xbc\xe9\x0b\x2c\x54\x66\x2c\x6a\xea\x0a\xe0\x0f\xfd\x3d\x51\x4a\x01\xcd\xc7\x93\x6f\x3f\x9a\xac\xaa\x2b\x5e\xf8\xaf\xe9\x0f\x6d\xea\x72\x04\x88\xc9\x54\xe4\x5b\x54\xa2\xfa\x1d\x39\x84\xbb\x7a\xd4\x12\x4f\x67\x45\x4d\xf0\x82\x79\x51\x63\x71\xd5\xa0\x03\x6d\x48\x48\x9f\x23\x41\x5d\x2b\xa9\x27\x34\x0b\x9f\x44\x1c\x12\x67\xcb\xd7\x96\xf2\x90\x8f\x80\x8f\x92\x3d\xdf\x04\x8b\x52\xfa\xaf\xfa\x09\x9d\xf4\x32\xff\x5d\x52\x2f\xdd\x0f\x4c\x7b\xa7\x84\xd0\x1d\xd0\x48\x0f\xaf\x75\xbf\x5e\x31\x3d\xac\xe1\x76\xfd\x34\x1b\x3f\x65\x7c\xdb\x60\x0b\x21\xb2\x9e\x48\xd4\x69\xd4\x7c\xe7\x9c\xd7\xa8\x38\xcf\x85\xb9\x87\x79\x8a\x29\x4f\xf2\x78\x99\xc3\x68\x74\x4e\xcb\xf4\xde\x33\xc1\xb6\xad\x71\xab\x55\x27\xef\x4c\xfd\x56\x83\xd9\x53\x88\x99\x2c\x64\x3b\x3b\xb3\xf5\x10\x9f\x4b\x55\x0f\x34\x0d\x15\x6d\x03\x2a\x8a\xe5\x81\xef\xcc\x93\x97\xaf\xaf\xe0\x39\x43\x73\x0d\x4f\x07\x73\x0f\x62\x9b\xe2\x99\xab\x93\x77\x88\xaa\xeb\x44\x72\xa8\x2b\x20\x9e\x57\xaf\x5d\xf9\xdb\x74\x10\x7b\x93\x93\xae\x56\x16\xf2\x3e\x5f\x9b\xdd\x53\x02\xc6\x8c\x9a\x5f\x4b\xa2\x16\xe4\x44\x1c\xa1\xe3\x2b\x1d\x33\xab\xca\x43\xa7\xad\x44\x58\x84\xf1\x1d\x65\x18\xd7\x8e\xf3\xc0\x0b\x87\xdd\x07\x62\x96\x03\x27\xe0\x60\xde\x42\xa3\x3f\xde\xe3\x0a\xde\xa4\xb7\x37\x19\xab\x9a\xb1\x2f\x6f\x6f\x7c\x42\x20\xc0\x50\x46\x15\x01\x3b\xe3\x8b\x0b\xcc\xd3\x7f\xfc\xef\xff\xf3\xf8\x84\x3b\x7e\xd2\xb7\x6b\xfa\x52\xf9\x90\xe1\x05\x91\x52\x41\x7a\xfe\xaf\x24\xe7\xef\xd5\xd2\xe2\x17\xf9\x4a\xec\x37\x16\x1b\xe5\x77\xaa\x1f\xc6\x47\xa2\xbf\x78\xcd\x25\xea\xd3\xcc\x8b\x2d\xe1\x43\x96\x4e\x3c\x1d\xb0\x42\x16\xf9\xdb\xae\x5a\x7c\xcc\x44\x37\xf0\x34\xfd\x33\xff\x4a\xe1\xce\xaa\xbb\x04\x73\x1e\xc7\x46\x40\x5e\x03\x5e\x14\x1a\xfd\x82\xb5\xaa\xe9\xbd\x67\x3b\x79\xbc\xeb\xdd\x98\x25\xa1\x01\xb2\xa7\x4e\xb2\xdd\xb1\x71\x11\x4f\xa9\xb5\x76\x41\x29\x70\x7b\xe2\x44\x96\x51\x82\x1a\xdc\x3d\x61\x54\x07\x9a\xa7\xee\x8a\xdb\xda\xe4\xe6\x8e\x2c\xaf\xe9\x62\x23\xb2\x79\x4e\x43\x56\x41\x3b\x71\xfc\x50\xf9\x60\xdf\x96\x38\x3e\xd0\x9f\x84\xd8\x2c\x5b\xa1\xe9\xa5\x23\xbb\x6b\xf6\x39\x2e\xd9\x90\x6e\x57\x8e\x7c\x73\x9a\x2f\xb5\x22\x9c\x1b\x9e\xeb\x67\x42\xe9\xfc\xfb\x8a\xfe\x8c\x1c\xab\xd9\x0d\x7b\xec\x7e\xbd\xce\xe7\x25\x92\xdf\xe0\x83\x16\x0c\x71\xfa\x9e\x66\x01\x0a\x30\xf7\x23\x61\x34\x54\xbd\x10\x1f\xbe\x12\xf5\x6b\x90\x0b\x46\xf9\x4c\x70\x7d\xd3\xe6\x6c\xe4\xfb\x2e\xdf\xcb\x4f\x42\x91\xfa\x67\xbf\x92\x2f\x49\x86\xc9\xb8\x80\xc2\x62\xdc\xc1\x43\x34\xd4\x15\x70\x61\xdf\x89\x75\x60\x36\xee\x88\xe5\x0c\xdc\xc3\xd3\xc5\x20\xff\x5a\x0e\xb8\x2f\xf8\x78\x6b\x69\x39\x78\x71\x6a\x26\x6d\x2e\x7d\x43\xcb\x48\xee\x38\xce\xe4\xcb\xe5\x14\x62\x46\x7a\x8a\x3d\x50\xd3\xcc\x84\xff\x9c\xff\xba\x54\xa2\x49\x95\x7e\xe8\xaf\x33\x87\x97\xe8\x09\x7c\xb2\x15\x7f\x74\x9f\x3c\x1b\xce\x45\x90\x55\xb3\x40\x31\xc7\x65\x12\x2d\x2f\xe4\x87\xd9\x0b\xc2\x7f\x9b\xb9\xf2\x27\xfc\x33\x5d\x8f\x6a\x71\x93\x1b\xce\xed\xa0\x99\x10\x86\x9a\x9a\x04\x93\xe6\x42\x48\x69\x71\x33\x05\x4c\xa7\x99\x3a\x82\x3d\xa7\x84\x90\xb4\xa2\x8a\x59\x0e\x1f\xd4\x0c\xd1\x7c\x1a\x9e\xb6\x39\x76\x99\xc2\xe1\x44\x3f\xc7\xfd\x0c\x80\xa4\x9b\xf9\x04\x28\xeb\x88\x3c\x1c\x0d\x7c\x08\xa4\x6a\x4c\xc7\x73\x86\xb3\xe7\xe7\x87\xa6\x76\x38\x41\x92\x99\x91\xe4\xb4\x28\x9d\xc3\x07\x80\x20\x41\x70\x24\x83\xa8\x19\x57\x99\x36\x16\xd5\x07\x84\xf6\xf9\x9c\xb2\xef\x17\xc0\xa6\x2b\xcc\xb8\xf2\x59\x82\x3a\xcb\x54\xe6\x62\x35\x47\x55\x86\x79\x24\xec\x64\x22\xf8\x09\x22\x9c\x10\xb8\x9e\x28\x71\x2b\x45\x0d\x61\x0e\xd6\x3c\xa2\x1b\x2d\x79\xcb\xf4\x7a\x08\xf6\xfd\x3f\x5c\xf5\x81\x72\x2a\x6d\x41\xc6\x1a\xe7\xcc\xd8\x2d\xc8\xf1\xcf\x63\x58\x9a\x80\x87\x4e\x81\x76\x1a\xd1\x84\xc4\x6a\xde\xcf\x5d\x57\x0b\x55\x18\x4d\x15\x92\x59\x2e\xb2\xf9\x8d\x96\x91\x79\x86\xbb\xe5\x81\x22\x1b\x36\x56\xc1\xe1\x49\x8b\x9c\xb9\x84\x89\x22\x9d\xba\x6b\xb3\xbf\xf4\x38\x67\xc6\x9b\x11\x8c\x59\x98\x37\x75\x93\x20\x4c\xa5\x00\x39\xc7\xc7\x14\x88\xa8\x51\x55\x11\xc2\xbb\x80\x38\x26\xd8\xc5\xeb\x64\xc3\x6c\xa1\xe3\x4a\xbc\x81\xbd\x4e\xfb\x05\xe5\xd8\xf8\x95\xf9\xaa\x68\xcd\xcf\x1a\x18\xa4\xe2\xe7\x2d\xed\xf8\x02\xd2\xd0\xa8\x04\xaf\x24\xcc\x02\x81\xc8\x77\x7a\xff\xfd\xb7\x1f\x3a\x9e\x06\x7f\x21\xf1\xfe\xbb\x0f\x74\x1a\xbd\xff\xfe\xfb\x0f\xb8\x89\x18\x15\xce\xae\x59\x11\x37\xae\x01\x05\x0d\x7a\xdb\x96\x9f\xaa\x66\xd7\x89\x8c\x86\x4f\xcf\x1f\x3e\xcb\x54\x7c\xee\xe3\x25\xee\xdc\xc6\x07\x2b\xbc\x70\x59\xf1\x0a\xaf\x77\x9b\x4c\xc7\xd8\x09\x07\xb0\x5f\xae\xb8\x61\x20\xcb\xb9\xc9\x5f\xdd\x6f\x1e\x6e\x55\xf0\x60\xa9\xf3\x76\x08\xff\x17\xf9\xf5\x0c\x03\xe1\xa1\xff\xea\x5a\x6a\x82\x3b\x8c\x2b\xf1\x7c\x67\x99\xd9\xdd\xa6\xdc\x94\xfd\x2c\xe6\x4a\x16\x85\x05\x5d\x8e\xb3\xb4\x17\x1e\x44\xe7\x0d\x26\xbf\x21\x78\x5b\x02\x31\x06\xf7\x0e\x3f\x07\x99\xb7\x55\xd6\x46\x05\x94\xd5\x7a\x2a\x51\xd0\x01\xae\x15\x53\xb2\x0d\x7d\x1d\x9a\xa4\x3d\x57\x87\xfd\xfc\xca\x5a\x44\x9e\x20\xa1\xf5\xda\xd5\x73\x4d\x18\xaf\x17\xd0\x77\xe3\x4a\x80\x87\xaa\x26\x9f\x02\xfd\x95\x4d\x6c\x1b\x8d\x21\x75\x81\x0f\x4b\x16\x05\x93\xda\xf3\x3b\xda\x8c\x94\x71\x9a\x68\x0e\x5e\x74\x24\xa0\xc3\x0f\x38\xb6\x39\x75\xf1\xad\x10\x27\x45\xa0\x55\x9d\x99\x5b\x80\x9e\x1a\x88\x59\xb2\x29\x9b\x8c\x88\xc8\x88\xfd\x58\x55\xbf\x7b\xd0\xe7\x2e\xba\x34\x34\x1f\x3e\xb9\x32\xe6\x89\x0c\x57\x6b\x59\x40\xe3\xf1\x33\xfd\x71\x78\x1d\x98\xe7\x58\xff\xb8\x15\xea\x3e\xfd\xb1\x24\xd9\x17\x6d\xd1\xf9\x7d\x3b\xce\x5f\x34\xeb\xc6\xef\xeb\xf8\x35\x04\x10\x7d\xeb\xfd\x62\x20\x9b\x49\xb6\xa7\x6d\x5d\xbd\x9c\x30\xd8\x79\x04\x72\x62\x30\x92\x31\x30\x3e\x8b\x33\x9d\x9f\x8a\x74\x10\xde\x2a\x16\x2b\x61\x5c\x8b\x9a\x70\x01\xd4\x29\x7c\x27\xc1\xa6\x34\xfb\x22\x65\x84\x9a\x7c\x96\xd9\x43\x13\x08\xbe\xcb\x0e\x94\xfb\x5a\xf3\x61\x5d\xfe\x74\xd3\xfe\x4c\x2c\x3d\xbd\xe3\xd2\x50\x0e\x41\x81\x0c\x1e\x1e\x86\x34\x93\xb5\xc1\x39\xd1\xa5\x18\xcc\xe8\x41\x83\x53\xd4\x4e\xa8\x9b\x86\x33\x2c\x18\x70\xbf\x6f\x52\x77\x44\x43\xf4\x33\xde\x25\xf2\x94\x0b\x9b\x87\x1f\xd6\x86\x96\x9f\x0d\xaa\x85\x97\xf6\x53\x98\xe6\x0d\x1b\xd4\x16\x9e\xa6\xfa\xa5\xf9\xd1\xe9\x71\x78\x6a\xb4\x91\x37\xac\x0a\xdb\xad\xb1\x41\xe0\x26\x54\x7e\x5c\xcb\x1d\x9e\x01\x21\x80\x14\x4b\x34\xbe\xad\x80\xcb\x4b\x78\x29\x35\xcd\xe0\xdc\x79\xb9\xc8\x61\x85\x0b\x07\xaf\x9a\x75\xbd\x79\x11\x8c\x9e\x40\x38\x1c\x86\xd5\xcf\x66\xcd\x61\x50\x30\xe6\x69\xae\x7a\xd3\x87\x0c\x30\x35\x2f\xfb\x3d\xcf\xab\x98\x4a\x30\x72\x45\x71\xdc\xfd\x10\xee\xd4\xc4\xde\x9e\xa0\x8d\x27\xbc\x5d\x17\xca\xea\xfe\x05\x3f\x84\xe1\x29\x2a\x07\xc2\xfc\x04\x19\x60\xb5\xdb\xa4\x32\x39\x42\x93\xbe\x29\xa9\x51\x6c\xf1\x85\x9d\x2f\x85\xf1\xfe\xc8\xee\x78\xc6\x59\xf1\x4d\x14\xce\xa6\x10\x9a\xfe\xbd\x4b\xd7\xfa\x51\x93\xee\xe4\xd6\x8c\xa4\xfd\x63\xd5\x53\xe9\xff\xf4\xc1\x68\x94\x8e\x02\x59\xc8\x4b\x41\x9f\xfe\x67\x04\x35\x3c\x56\xfb\x3c\xd1\xfe\x82\xa0\x4a\x33\x93\x2c\x34\x5f\x77\x5d\x22\x15\x41\x8d\xf3\x84\x90\x0c\xbd\xe7\x0b\x67\x92\x7a\x4d\x47\x7c\x66\x04\x8a\x4d\x77\xdd\x35\x8b\x50\x03\x11\xb7\xf5\x2d\x31\xd5\xb8\x9c\xab\x51\xb5\x6e\xe5\x2b\x4c\xbc\xf0\xa5\x0a\x0e\x9f\x45\xeb\xc3\x2e\x39\xe9\x97\xbb\xce\x98\xae\x4b\x61\x8b\x9d\xb7\x5c\x67\x2c\x52\x21\xe8\xbe\x02\x7e\x66\x7d\xaf\xba\x0c\xd6\x4d\x62\x1d\x7d\xa5\x26\x4b\xeb\x6a\xd1\xa7\x2e\x9d\x9a\x53\xfb\x75\xd8\xb8\x2c\x25\xda\x8e\x33\x56\xa7\xcd\xb2\x5b\x21\x7c\x07\x03\x5c\x13\x97\xda\x34\x90\xe2\x1c\x8b\xc8\xeb\x0c\x6a\x7c\x0c\x35\x8c\xe9\xc1\xea\x56\x43\x2e\x43\x3c\x1e\x60\x58\x14\x63\x83\xe1\x06\xd5\x42\xcf\x7d\xa8\xe6\x07\xfd\xed\x75\x1b\x17\x10\xbf\x12\x5e\xf4\x36\xf4\xce\x2d\x5f\xd3\x8d\x1c\x6c\x72\x2a\xcc\x5b\xa8\x82\xa3\x79\x68\x24\x22\x04\xcc\x86\xaa\x3e\x22\x9a\x21\x5b\x01\x01\x4d\xad\x6e\x10\xed\xae\xd6\x45\x88\x52\xd0\x02\x32\x99\xff\x3a\x1e\xaf\x12\xf3\xa1\xb1\x86\x6c\xab\x96\x6d\x3b\xa2\xa7\x87\xff\x72\xbf\x78\x24\x0b\x19\x26\x43\xa3\x7b\x16\x4e\x14\x74\x86\x5b\x24\x8f\xb9\xea\xe0\xdf\xcd\x64\xc3\x9b\x05\x03\xd1\xf7\xec\xd7\x24\xd0\xd4\x05\xfb\x99\x3f\x83\x07\xd9\x13\x0a\x83\x20\x77\x5a\x69\x30\x04\x28\xbc\x2a\xe6\x7e\x17\xb5\xdd\x64\xb4\x3c\x32\x3d\xd1\xd1\x96\xc2\x8b\x05\xbf\x86\x5d\xb0\xa3\xcc\xb0\x6a\x77\x28\x88\x47\x44\x7b\xfb\x9c\xf7\x11\xf1\x5a\x16\x3e\x1d\x68\x27\x89\x04\xd4\x0d\x45\xaf\xc6\x55\x40\x88\xaa\x1f\xb0\xf9\x49\xec\x98\x58\x07\xbf\xd9\x30\x63\xe2\xba\x2f\xcc\xf5\x83\x3e\xa5\x11\xb3\x3e\x30\x7d\x68\x21\xbf\x1e\xc5\x83\x2c\xc5\x06\x9b\xff\x86\x19\x2e\x42\x8b\x56\x95\xc9\xd4\x6b\x8d\xa8\x5c\x53\x7c\xe4\x92\x23\xe7\x75\xfa\xe0\x86\xfe\x3d\xde\x6c\x1e\x17\xc5\x83\x89\x51\x07\xa2\x91\x1b\xf6\xc0\x90\x4c\xb5\x10\x03\x56\x19\xd4\x14\xc8\x99\xd3\xb8\x63\x80\x68\x9e\x7e\x61\x29\xa0\xe4\xeb\xb0\xb4\xf0\x78\x13\xd2\xf5\x73\xd7\xf1\x16\xd0\x10\xc3\xf3\xc6\x29\xcc\x2a\xc4\x3b\x31\x1c\xc9\x40\x42\x0f\xb2\x06\x4e\xd7\xb7\x76\xcf\x5d\x9b\xa8\x44\x47\xec\x7b\x73\x00\x25\x12\xa6\xef\x20\x42\x02\xc9\xd8\x23\xd5\x49\xc7\x13\x80\x53\xb2\xb1\x6f\xfb\x9f\x29\x1f\x4f\x35\x3e\x45\x02\x77\x49\xc8\x53\x21\x61\x2d\x6d\x26\xf4\x0d\xa7\x17\xf9\xf2\x59\x41\x98\x19\x95\x33\x82\xdf\x1e\x6c\xd5\x34\x1f\x25\xd4\xce\x1c\x9f\x3e\x67\xc9\x21\x26\x25\x93\x83\x29\xbe\x8a\x73\x49\xb4\xac\x16\x61\xe8\xd9\xe7\x9c\x30\xd1\xc5\x82\xe7\xb8\xcd\x7e\x17\x9d\xe4\x29\x7e\xa5\xff\x93\x09\xc3\x81\xa8\xc7\xca\xb9\x85\x56\x62\x63\x0a\x9f\xab\xbe\x06\x41\x53\xea\x1a\x31\x6e\x4b\x8d\xf1\x99\x9d\x7f\x99\x3f\xc9\x94\x1f\x49\xec\xdc\x3a\xf3\xb5\x3b\xf7\x38\xbe\x1d\xd2\xcf\x73\xf3\xb0\x1b\x83\xb9\x7b\x57\xef\x55\x17\x5f\x4e\xb1\x29\x5c\x2d\x46\xf4\xf0\xac\xe3\x7b\xa4\x06\x1b\x56\xe8\xce\x47\x74\xe7\xa2\x37\xea\x89\x1b\x5a\x00\x18\x96\x75\x41\xf7\x10\xb6\x18\xbe\xb5\x2c\x99\x75\x72\xcb\xae\x3e\x82\xe2\x69\x22\x9a\x82\xdc\x9d\xde\x3b\x98\x23\x4c\x6c\x7d\xd0\x81\xbb\xb0\x37\xe2\xaa\x62\x5d\x45\x5e\x30\xbd\x03\xf7\x2a\x60\x3a\xb8\xb8\x1e\x00\x1a\x52\xce\x89\x7f\x88\xe5\xa3\x14\xcb\x23\xf7\xc4\x3e\xd0\x60\x89\xb5\x12\x5f\xd9\xb9\x1e\x31\x7b\x2a\xdb\x1e\x8e\x81\x63\xb4\xb3\xa3\x02\x2d\xa0\xec\x5b\x6a\xe6\x31\x64\x25\x89\x37\x89\x41\xc8\xfa\xab\xae\x03\x7c\xc0\x80\x93\x0d\x32\x3f\x55\xc5\x8e\xa8\x8f\xe7\xe2\xb6\x7a\xbf\x8b\xeb\xa5\x15\x8f\xfb\xf2\x83\x75\x0f\xe6\x13\x42\x84\x0b\x46\x0c\xcf\xd0\x6b\xef\xf0\xdc\x4d\xb5\xcc\x4c\xc8\x69\x3b\x14\x07\x70\x5c\x4e\xbd\xe7\x5f\xc8\xaa\x84\x0f\xc1\x84\x4c\xac\xbc\x4d\x4c\xfa\x21\x1d\xcd\x47\x8c\x2d\xf1\x26\x75\x52\xd5\x9d\x46\x6c\x23\x42\x18\x60\x69\x50\x1f\x10\x16\x98\x9a\xd9\xec\xf3\xf0\x39\x88\x9d\x06\x4a\xb6\x33\x40\x48\x12\x12\x05\x01\x71\x9b\xd9\x24\x80\x8e\x0a\x47\xca\x83\x8f\x9c\x56\x55\x7c\xe8\x02\xab\x44\xcf\x03\x9b\x08\xb3\x83\xbe\xc2\x44\x40\x10\xf0\x7a\xd4\xb4\x77\xed\x3b\xf2\x16\x55\xce\xc2\x83\xe5\x4e\x36\x5e\xab\x8b\x72\xcb\x51\x34\x59\xb1\x79\x2d\xbb\xad\xf0\xfc\x3b\x5a\xfd\xee\xb6\x56\xc5\x10\x6c\xaa\x59\x33\x89\x54\x5f\x79\x2c\x5a\x0e\x7d\x7d\x47\x6b\xdf\x5b\x6b\xe1\x96\xf5\xb1\x2c\xb7\x41\x13\x71\xf7\x03\x17\x11\x30\xcf\xd8\x34\x6b\x82\xa3\xa9\x17\xa4\xb9\x4d\x1f\x60\xe2\x51\x14\x18\x6f\x25\xa0\xbb\xd9\x1d\x1e\x63\xe3\x05\x62\x2a\x50\xc4\x6b\x87\x1a\xd4\xc1\xb0\x96\x27\x0b\x38\x37\x8c\x74\x8d\x25\x4f\x54\x25\xc6\xbf\x03\xab\x22\xef\x47\xed\xba\x66\x05\xda\xc3\xdd\x8b\xed\x3b\xd3\x09\xbb\x4e\x07\xca\xc6\xf3\x21\xb1\xa6\xe2\x32\xc1\xe3\x39\x09\x92\x0f\x17\x18\x38\x2a\x44\x75\xc5\x8e\x0a\x41\x07\x85\x8a\x0e\xd5\x73\x32\x59\x87\x52\x5e\x50\x8b\x5c\xf3\x4b\xa4\xc1\xf8\x42\x55\xef\xfd\xc1\x50\x67\x21\x31\x30\x68\x05\x57\xfa\x4c\xe3\x9a\xe9\xeb\x04\x43\x5f\x76\xcd\xdd\xaf\x9a\x20\x46\x8d\xf8\x5b\x60\xf7\x0a\xbb\x3e\x8b\xb1\xb3\x17\x81\x46\x31\xa9\xe2\xcd\x40\xee\xb1\xdd\xc8\x84\x1f\x1c\x92\x37\x3b\xea\xf3\xba\xfa\x08\xf5\x19\x91\xb2\x04\x3a\x3e\xbf\xbc\x82\xce\x8c\x96\x0c\xed\xbc\x4b\xe6\xd4\xe9\x5f\x57\x65\x8d\xd8\x9b\x1c\x6f\x58\x59\xd7\x62\xc1\x6e\x52\x55\xad\x81\x09\xf7\xa5\x19\xaf\xd7\xc5\x5a\x18\x5d\xe8\x48\x67\xf2\x86\xe8\xce\x52\xc4\x14\xe6\xb5\xd9\x6d\xcb\x05\x49\xd1\x33\xbe\x28\x6b\x6b\x3c\xd4\xe0\xc2\xc7\xdf\x6a\xa3\xe9\x46\x02\x8b\x1e\x56\xb1\x05\x68\x51\x94\x84\xfa\x64\xdd\xb5\x47\xe8\x19\x82\x4e\xc9\xcd\x86\xe1\xdb\xa4\x66\x70\x64\xb1\x99\xae\x98\xc1\xa7\x6a\x7d\x72\x9b\x2b\xca\xc1\x3e\x84\x81\x21\xa5\xe9\x3b\x84\xe7\x61\x55\x33\xaf\x89\x30\xf5\xc3\x04\x48\xb7\x6d\xc4\x04\xf4\x9d\x7e\x8e\x81\xe4\x7c\xc5\x3d\x79\x25\x5f\x63\x90\xad\xc6\x6f\x72\x91\x9c\xc6\x20\xf3\xa6\xe0\x13\xd3\x73\xfa\x33\x16\xbb\xdd\xab\x00\x26\x7b\x83\x38\xb7\x1c\x03\x40\xee\xa5\x39\x83\xd0\x5d\xae\xaf\x25\x9e\x03\xeb\x9a\x70\x40\x14\xf5\x20\xdb\x4e\x4b\x40\x53\xb6\xf5\x45\x05\xea\xd1\x07\x67\x15\x44\x73\x0b\x75\x7f\xea\xfd\x1b\xba\x73\x0e\xfb\x84\x7b\x0e\xeb\xd7\x6b\x91\x5a\x30\x0d\x38\x0e\x4b\xe8\xbd\x23\xde\x8c\xf8\x24\x69\x37\x8f\xb6\x65\x6d\x25\xdc\x1e\xfb\x61\x11\x51\x23\x44\x8a\x81\x88\xd4\x2b\x36\x5c\x81\x19\xb3\x85\x20\x07\xb1\x01\x61\xe3\x1e\xa9\xd9\x39\x23\x48\x0c\xce\x47\x10\xfe\x5e\x14\x40\xe6\xe0\x35\xdc\x99\x14\xdc\x9f\x2e\x5e\x45\xeb\x21\xe0\x28\xd1\x73\x0d\xe8\xa8\x06\xd1\x13\xdd\x2f\x73\x0a\x53\xfd\x06\x2a\x56\xc6\x15\x1b\x59\x06\xab\x9b\x37\x76\x12\xa7\x44\xee\x6e\xcb\x65\xde\x16\x16\x38\x46\x39\x0d\x3b\xcf\x80\xa3\x84\x7e\xc2\xf9\x9a\x8e\xeb\x5a\x05\x71\x46\x02\xf9\xc8\x86\x54\x34\xdf\x2c\x15\x99\x86\x82\xe5\xcb\x42\xd8\x18\xbb\x2a\x12\x73\xd9\x6d\x99\xdf\x08\xf3\xb2\x76\x30\xe4\x87\x7f\xba\x3c\x7f\x7b\x94\x7e\x7e\xbc\xdf\xef\x1f\x73\xf1\xc7\xbb\x76\xcd\x4e\x7d\x05\x07\xa6\xf9\x1f\x67\x6f\x8e\xd2\xb2\x5f\x3c\x9a\xd1\xd1\xbe\x8d\x15\x62\x6a\x4d\x8a\xcb\x0a\xa6\x2e\x16\x36\xff\x38\x7b\xd2\x15\xa3\xa1\xe0\xc3\xf8\x67\xe1\x96\xca\xb3\x67\xe6\x22\x3a\x99\x62\x36\x12\x6c\x48\x8b\xb6\x84\xb5\x05\x3e\x82\x0c\xda\x9d\x3e\x4e\x05\x24\x18\x82\x54\xd4\x8e\x76\xe3\xf5\x42\x43\xd1\x0f\x40\xec\x72\xf1\x04\xd7\x8a\x2e\x13\x13\xe7\xb6\x15\x0e\xc2\xd7\xad\x9a\xdd\xba\x88\x39\x26\xe1\x4c\x27\xa2\x2c\x7e\x1a\x16\x86\x5d\x24\x62\x49\x3f\x4d\xff\xc4\xba\x25\x9e\x28\xa1\x2d\xce\x32\xda\x02\xf0\x6c\x58\x18\x51\x11\x03\x51\x9a\x06\x20\xd1\x1e\x4d\x94\xf7\x79\x4e\x9c\x1f\x55\xa2\x27\x3e\x36\xd3\xe8\xd3\x8d\x3b\x01\x82\xd6\xa4\xba\x71\x91\x58\xb3\x37\x9d\x6d\x78\x11\xf3\xc8\x23\x35\x9c\x34\xb5\xd7\x14\x1e\x52\x31\x0a\x9d\x44\x51\xc0\x1f\x01\xca\x5c\x24\xb4\x52\xf5\x6b\x17\x8c\x29\xd5\x30\xa0\xe5\x30\x23\x08\xe2\x50\xf6\x70\x9f\x9f\x5a\x8b\x72\x06\x77\xb3\xe6\x57\x8f\xf1\x37\xdd\xe1\x44\x32\x11\x8f\xa3\x88\x7b\x80\x75\xc4\x52\xda\x7e\xb8\x8b\x0d\x05\x34\xe5\x4d\x5e\x94\x51\xde\x34\xda\xae\x15\x70\xd0\xc6\x68\x97\x54\x79\x7a\x7c\x4a\xf0\x2d\x1c\x12\x08\xc4\x28\x28\xd3\x51\x5a\x1c\x1b\xb8\x6d\x9e\xba\xb4\x58\xbc\xb2\x55\x0a\xbe\x1b\x2f\x51\x46\x88\xac\xa3\x90\xa3\xb2\xa0\x16\x9b\x10\x30\x08\xbc\x8d\xd9\x2d\xc1\x0c\xf1\x47\xbe\xd6\xa1\xd0\x2d\xb5\x9a\xdf\x9c\xf8\xf7\x0d\x32\x87\x6f\x6f\x0c\x57\x36\xc9\x6a\xf2\x7a\xd3\x89\x7c\x85\xd8\xda\xae\x9b\x1b\x73\x47\x3f\xc5\x2f\x8d\x57\x13\x8e\xcc\x83\xe9\xa0\x3c\x64\xa0\xae\x69\xb2\xb8\xba\xbf\x05\x21\x5c\x55\xc4\xad\xf9\x84\x8c\xa2\x04\x13\x1e\x7c\x22\x15\xf9\x44\xf7\x26\x3c\x9a\x1d\xd4\xd0\xf7\xfa\xd4\xb5\x70\xc0\xf7\x3a\x2e\x1a\xfa\x5f\x07\x45\xbf\xc0\xff\x3a\x46\xd2\xd8\xbb\xda\x0f\xf5\x0b\x1c\xac\xa7\x06\x3d\x96\x6b\xa7\x10\x3f\x51\x60\x4a\xba\x2d\xc2\xb1\x7d\x81\xa3\xf5\xe0\x2c\xfc\x25\x02\xee\x54\x4f\x3c\x4a\x02\xe4\xde\xa5\x23\x2e\xaa\xeb\xeb\xd9\xbc\x6d\xf6\x1d\x7b\x33\xe3\x21\x0b\xe6\xb2\xfc\x3b\xbd\xc4\x6f\x01\x61\xe3\x00\x10\x85\x7c\x48\xa2\x1a\x28\x3d\xd5\xbb\x40\x49\xc4\xcd\xec\x30\x7c\xfe\x29\xe5\xc8\x2d\xed\x5b\x3a\x89\x1d\x5b\xce\x4c\x8a\xd0\x46\xb7\xcf\xf8\x0b\x7e\xd8\xd0\x57\xb3\x7a\x15\x85\x2e\x39\x45\xc1\xf8\xd3\x10\x6e\xbb\x12\x6c\xe3\xe4\x1e\x5b\xc4\x57\xaf\x6b\x02\x61\x19\x1c\x81\x11\x31\x54\x90\x4f\x3d\x48\xe8\x6f\x49\x10\x86\x4b\x0f\xa1\x08\xc2\xa2\x7f\xfe\xfa\xad\xfc\x84\xc1\xbb\xc6\x3f\x82\xc5\x3b\x5f\xa7\x27\x66\x46\x3f\x9b\x32\xa7\xb7\x3c\xf1\x7c\x10\xc5\x88\xbd\x3c\x87\x5f\x0e\xa2\x68\xf3\x6b\x5c\x1c\xf1\x5f\x97\x4a\x32\xb0\x2f\x76\xd1\x96\x8f\x87\xc5\x08\x39\x82\xea\x4b\x7c\xb8\x74\xbd\xf8\xe1\x3f\x2e\x2d\x87\x51\xc7\xd3\x60\xe4\x1e\x23\x66\x3d\x40\x74\x77\xbf\x4b\xbb\x8a\xb5\xad\x4a\xa0\x83\x06\x41\x1d\x3e\xb6\x31\x68\x07\x6f\xb1\x85\x63\x0d\x6f\x94\x10\x73\xa1\x5b\xa5\x0e\x3f\xec\xf0\xd3\x8b\x3b\xb5\xbe\xad\x32\x8b\xfa\x1d\x95\x96\x2d\xb6\xb4\xd9\x4e\xd7\xcd\x12\x52\x24\x9c\x11\xe5\x91\x8d\x1d\x3f\x46\x43\x88\xe0\x38\x5a\x2c\x71\x38\xe2\xaf\x36\x54\xff\x27\x09\x8e\x2e\xd5\x93\xf4\xe0\xdc\xbd\x49\x90\xa8\xc5\xe3\xd4\xf2\x70\xa4\xb6\xf8\x70\x51\x19\x1f\x7c\xd9\x74\xdb\xde\xe7\x84\xf2\x21\x99\x2c\x42\x57\x16\x16\x53\x38\x00\x86\x8c\x3d\xe8\x40\xc4\x88\x2d\x75\xcc\x7c\x2d\x67\xdf\x6a\x70\x2e\xfe\x1b\x92\x85\xbe\x5b\x72\x21\x5f\x2e\x87\x25\x60\x11\xe3\xde\xc8\x97\xbc\xb1\x37\xa4\xa6\x71\x40\x02\xca\x7b\x3c\x9c\xeb\x00\xde\x21\xe0\xaf\xe5\x7f\xfc\xaf\xff\xcb\x4a\xd9\x86\x76\x58\x44\x0f\xd1\x00\x9c\x9e\x56\xcc\x83\xce\x3f\x9c\xf3\x18\x7c\xdd\x77\x64\x78\x2d\xe8\x1a\x54\x52\xf1\x44\x33\xa2\x77\x7e\x63\xc2\xd6\x0a\x5b\xf8\x0d\x16\x0c\xe8\xc7\x2f\x19\x5c\x7c\x0e\xeb\x30\x22\xc8\x74\xbf\x71\x81\x01\x15\x50\x38\x08\x46\xd4\x2b\x91\x4c\x6f\x4f\x6e\xed\xea\x7b\x1f\x60\xc2\xf6\x9d\x24\xef\x9b\x76\xf9\xc1\x07\x97\x75\x5a\xae\x28\xb0\x2c\xce\xa0\x0c\xe3\xc2\xb3\x1d\x00\xf4\x21\xdb\x7c\x8d\x36\x29\x2f\x99\xf4\xc6\xef\xa3\x89\x06\x40\x02\x80\xc3\x80\xcd\x7c\xa2\x67\xe6\x91\x25\xb1\x29\xf5\x72\x3d\x7a\xb8\x12\x8e\x52\xa6\x4e\x35\x39\xb2\x48\xf4\xd6\x97\x1d\x7b\xf8\x83\xc3\x87\xf2\x53\x7b\xac\xcf\x92\xfb\xba\xd7\x48\x20\xc6\x80\x04\x84\xc6\x85\x43\x13\xff\x4d\x10\x90\x50\x35\x78\x9c\xaa\x5f\x9a\x3e\x08\x7a\x18\x3d\x2f\x11\x78\x90\x21\x18\x6a\xf4\x66\x04\x57\x0e\xac\x4c\x5c\xf8\x8f\x42\xe4\x02\x85\x48\xbd\x0d\x3a\x72\x38\x7e\xb0\xc6\x25\x8f\x46\xd8\x82\xea\x9c\x0d\xc9\x6a\x11\x2e\x41\x27\x08\xd0\x5a\x47\x66\xaf\xdd\xcc\x37\x13\xac\x9f\x95\x58\x03\xf8\x62\x78\xb9\x69\x4e\x2b\xe9\x27\x81\x8f\xfc\x26\xf5\x58\x98\x4b\xc0\x00\x49\x4e\xd7\x74\xc2\x58\x47\xe7\x44\x54\xc4\xb2\xe0\x4f\x07\x7c\x66\xc7\xc1\x8c\xbf\xde\x6b\x76\x5c\xc7\xed\x7e\xb3\x7f\xf4\x22\x7a\x3a\x50\x61\xa8\x0c\x1b\x44\x2c\x74\x59\x53\xa1\x0b\xff\xc8\xb5\x70\x0c\x1a\x88\x58\x11\x0a\x5c\x4d\x5f\x7a\xfd\xa0\xb7\xcd\x44\xa9\xff\xd8\x65\x73\x1c\xc2\x77\xd8\xeb\x51\x8c\xbd\xa8\xd3\x5f\x17\x6b\xef\xe0\xad\x6d\xc4\x2b\x86\x87\xc3\x51\xc0\x0c\x0c\xf0\xd6\x22\x71\xf8\x8c\xb0\xc3\x4e\x1d\x18\xdc\x02\xea\x9d\x82\x04\xce\x10\x1d\xf7\xdd\xd1\x33\x0e\x5c\xb3\xdc\x16\x46\x63\xd8\x4b\xe6\x31\xce\xa9\x23\xec\xe4\xad\x25\xc2\xbd\x36\xbe\xa9\xff\x47\x42\x6b\x4c\x5f\x4c\xf0\xe1\x71\x6f\x3a\x33\xec\xcc\x86\x3f\xaf\x89\xe0\xc3\x87\xe1\x4b\x4e\x27\x9e\xd1\x7a\xcc\x41\xaa\x12\xe4\x0e\x42\x46\x2b\xd7\x9e\xe9\xcd\x9d\x45\xd5\x1a\xa4\x7b\x96\x07\xbb\x69\xbd\x9b\xf4\x40\xf2\x1b\x22\xcc\x64\xce\xb0\x7c\xdc\x46\xec\xc3\x60\xa9\xee\x6e\xe8\x0c\x1f\x2e\x9d\xb0\xb6\x28\x11\xa2\xe0\x44\xbe\x5c\x8e\x1e\xd2\x34\x0c\xb7\xef\x84\x84\x58\x86\xdf\x51\x90\xaa\xbb\x9d\xe2\x9a\x5d\xa8\x69\x52\x6e\xb6\x3c\x85\x79\xea\xd4\x84\x2c\xdf\xca\x2e\x28\xb2\xa0\xf6\x0a\xa2\xf5\x0f\xc3\xba\xe4\x41\x1d\xdd\x35\xdf\x36\xfb\x44\xb6\xcc\x19\x5c\x29\x24\x24\xb0\xa6\xc4\x5d\x92\x34\x16\x48\x34\x5e\x53\x2a\x91\x24\x34\xa2\xcf\x38\x7f\x10\x7c\x00\x3b\x86\x0b\x3b\x60\x11\xbe\x59\xa8\x54\xa7\x9d\x5a\xee\x66\x62\x6f\x7c\xa9\x15\x42\xa9\x6f\x16\xb2\x69\xdc\x6e\x08\xf1\x25\x0d\xe3\xfd\xe4\x61\x73\x47\xa6\x18\x43\x00\x48\xd5\xd8\x49\xa4\x3b\x69\x45\x1f\xc5\xb5\x7e\xb8\x77\xe9\x7c\x3f\x42\x88\x2f\xe9\x07\xb7\x02\xfb\x73\x39\xa4\xdc\xd2\x1f\x3a\x55\x6a\x3c\xcb\xc8\x66\x60\xd8\x45\xef\x54\x7f\x15\xec\xd3\xb0\x53\x29\x06\x72\x07\xeb\xa1\xc7\x1b\xa6\xe4\xc8\x7d\xf2\x84\x68\x20\xe6\x44\x93\xa1\xae\xee\x5e\xe2\x30\xf4\xe7\x92\x0e\x34\x30\x14\xf2\x60\x93\xbb\x8e\xf4\xcb\x8b\x72\x90\xad\xce\x54\x9e\x93\xcc\xbb\x37\x5c\x81\xb3\x20\x4f\x22\xd7\x85\x5b\x06\x04\x3b\x9b\xc9\x42\x9e\x3b\x70\x6b\x9c\x59\x5d\xd0\xea\xb8\x32\xc7\xaa\x01\xe5\x58\xf4\x18\xce\x78\x67\x28\x95\x05\x5a\x5a\x66\xca\x47\x26\xab\x3a\x2b\x06\x40\x6d\xf2\x9b\xc8\x4e\x08\x66\xcd\x9b\xf8\x05\xe1\x5b\x14\x3b\xe3\xae\xf8\x5d\x5b\xde\x10\x70\x04\x73\x50\x9b\x33\x0b\x97\xfa\x98\x40\x3c\xd9\x2d\x5b\xf8\x40\xd8\x5c\x33\xb3\x08\x48\x01\x15\xfe\xe0\x46\xe9\x1e\xae\xf4\xdc\x00\xd7\xce\x54\xd1\x83\xdb\x98\xc2\x57\x74\x00\x6c\xe3\xf6\x1e\x80\x2d\x88\x5b\x1c\x75\x23\x60\x01\xb7\x75\x44\x5f\x9c\xfc\xf2\x8e\x80\x6f\x7c\x61\x47\x8e\xac\x17\x1a\x7f\xbb\x28\x26\xd7\xff\x6d\xfd\x1b\x1c\x73\x40\x9c\x51\x80\xf7\x01\xc1\x47\x2f\xe4\x3b\xa2\x0f\x2c\xe6\xac\x5a\xd8\x66\xa8\x05\x9f\x6e\x67\xbe\xaa\x9a\xa6\x10\xe7\xd6\xba\x0f\xad\xfc\xe2\x58\x26\x6c\x60\xd6\xb7\x37\x2a\x92\xf0\xe0\xe2\x50\x2a\xde\xb8\x47\x0e\x5f\xb8\x3b\x96\xa0\xff\xef\x81\xf6\x0f\x89\x7b\xf4\x90\x97\xb2\x7d\x27\xa2\xa8\x91\xfb\x33\x84\x5d\xf7\xf1\xd6\xd3\x61\xfc\xf5\x5b\x63\xdf\xc7\x31\xff\x87\x8f\x3f\x74\x12\x22\x6d\x69\xb2\x9c\x3d\x35\x99\xa8\x51\x13\x33\xd6\x1b\xc2\xc1\x06\xef\x0c\x2f\x38\x0c\x72\x53\x57\x62\x3e\x73\x26\x5f\x34\xf6\x04\x63\xca\xb6\x12\x3e\xe2\x05\xff\x90\xd0\x94\x9a\xc2\xa1\x29\x93\xbe\xe9\x21\x50\x5c\xf1\xdf\x1f\xd2\xfb\x45\xe2\x87\x0e\x95\x25\x6b\x87\x54\x4a\x90\xef\x20\x3f\x08\xd4\x0e\x93\xfa\xd6\x42\xcf\xfb\x1a\xd0\x4d\x28\x46\x77\x41\xb7\xb5\x93\xa8\x74\xd7\x4d\xb5\x98\xf1\x75\x6b\xaa\x97\xcd\xee\xb5\x79\xe6\x20\xfc\x00\x5c\xc1\x0f\xc0\x89\xda\xec\x28\x48\x88\x26\x24\xcc\xd8\xba\x78\xa9\x51\x72\xbc\x2b\xfa\x74\x89\xf4\x14\x25\x71\xb8\x99\x28\x21\x5f\x8c\x5a\x31\xb5\x78\x98\x66\xa6\x7a\x3e\xc5\x8c\xf6\xa2\xda\xe3\x98\x99\x61\x96\x58\x3a\x46\x49\xfa\xf2\x65\x3c\x12\x51\x4a\x86\x69\xeb\x66\xc9\xef\x33\xd8\xcb\xca\xc1\xf0\x54\xb0\x8e\xeb\x34\xab\xed\xa8\x0a\x78\x87\x86\x29\xb8\x2d\xeb\xf3\x2e\x2e\x8d\xf5\x19\x26\xa8\x67\xfd\x08\x90\x0e\xda\xf9\x62\xa5\x4e\x44\x13\x84\x64\xe7\x65\x47\x4c\x72\x68\x9e\x82\x94\xd7\x49\x53\x7b\x8b\x74\x12\x86\xdf\x9d\xc7\xd3\xaf\x41\x2e\x7b\x41\xb0\xcb\x0d\xc2\x8a\x36\x1a\x0f\x8b\x5d\x22\x5c\x08\xd1\xf4\x1c\xcb\xb1\xbb\xb5\x50\xb0\xc5\x71\x5c\x06\x0d\x5b\xaa\x25\x45\x1c\xb9\x65\xaf\xf3\x35\xeb\xae\xa9\x66\x24\xc1\x4b\x75\x9d\x17\x22\x72\xb8\x68\xe9\x75\xae\x65\x7f\x51\x1d\x83\x5e\x7a\x08\x57\xcd\xd7\x77\x15\x2a\x35\x36\x70\xa3\xde\x0c\x3a\x19\xf1\x3c\x03\xb9\xa3\x86\x41\x17\x27\xab\xf8\xfa\x4e\x62\xd3\xac\x97\xee\xb1\xd7\xa9\x4e\xde\x20\xee\x6c\x5b\xe8\x09\x73\xcd\x46\x68\x2f\xcd\x8e\xe8\x8e\x2a\x0f\xf5\xfa\xd6\x3a\xbf\x62\x18\xfc\xea\xf3\x72\xe1\xbb\xcf\x51\xb7\xdb\x39\x33\x6e\xde\xa7\xcb\x85\x39\xd1\x45\xda\xc5\xe9\xe2\xb7\x21\x18\x1d\x62\xbd\xc2\x54\xf5\x87\xfa\xd6\x96\xdd\x4d\xbd\xc8\xf0\x64\x71\xb7\xd2\x5b\xdc\x77\xa5\xe8\xf8\x1f\xcc\x28\xed\x49\xae\xa1\xe5\x4a\xdc\x77\x76\x0f\xe4\x61\x86\x87\x0b\x4a\x87\x39\x36\x6d\xe3\x8f\xc1\xda\x51\xda\x84\x54\xc6\xd6\xa3\x5b\x1b\x1a\x8c\x25\xe0\xeb\x01\x6e\x5b\x74\xa5\x2f\xbf\x68\x04\x81\x05\x41\x38\x0c\x26\x14\x65\x62\x60\x79\xe1\x3b\x42\x8c\xb8\x87\x6c\x0d\xc2\x71\xd4\xd9\xd4\x45\x4d\xc8\x74\xd3\xb6\x27\xa9\xf5\x5e\xef\xc0\x80\xc2\x76\x6f\x99\xa1\x07\x51\x2f\xee\x1e\x63\xb8\x97\x22\x9a\x1d\x35\xc4\x06\xd2\x38\x8b\x21\xba\xdd\x2f\xf8\x1d\xf2\x36\x79\xea\x21\x5b\x36\x6d\x43\xd3\x23\xc1\x8e\xf4\xf9\x87\x97\x96\xd6\x4d\x14\x80\x1e\xfe\x26\xdb\x69\x80\x2a\x2b\x73\x86\x64\x92\x91\x38\x5a\x95\x2f\x05\x49\xc3\xca\xb0\x76\x75\xa1\x3a\x79\x88\x1e\x56\xea\xd8\x32\x82\x92\x5a\xa6\x99\xb3\xe7\x83\xfa\xe3\x02\xf8\x5c\x53\x02\x58\xdc\xe7\x70\x04\x21\x42\xd7\x6e\x9b\xf1\x50\x11\xea\x44\x92\xd3\x37\x48\x4e\xaf\x38\x79\xdc\x82\xf5\xca\x15\x1b\x74\xea\x50\x39\x8e\x2a\x31\x2c\xf3\x82\x83\x4f\x0c\xe1\x0d\x73\xab\x32\xdf\x8e\xf0\xf6\x8a\x12\x47\x58\x03\xe4\x18\x01\x80\x3d\x8c\x85\xb0\x54\x55\xe0\xf0\x18\x96\x78\x4d\x49\x87\xa0\x61\x1e\x31\x84\xaf\x59\xe2\x3d\x50\x42\x45\x8f\x61\xaf\xf4\x12\x6a\xd4\xab\x66\xfe\xf7\x72\xd1\x77\x06\x7d\x2e\x3f\x03\xa8\x79\xd3\xf4\xfc\xbe\xf1\x96\xa5\x46\x98\xad\x09\x9a\x9e\x5b\x3a\x4b\x8d\x8b\x8f\x23\x4c\x09\xf4\x18\x55\x02\x7d\x18\x57\x1b\x0e\x45\x49\x6d\xb5\xbb\x45\xbf\xa3\x05\xea\x1a\x3c\xbb\xe4\x10\x96\x97\x2e\x63\xd4\xe2\xa8\x64\x48\xa1\xc3\xc2\x53\x2d\x2f\x48\x16\x2a\x27\x9b\x3e\xe1\x9c\x5b\xdb\x1e\x95\x0d\x1b\x1f\x15\x9f\x5a\x29\x78\xcd\x88\x37\xb4\xf9\x6e\xf1\xb1\xec\xd9\x7b\x6a\x95\xe1\xfa\x3d\xac\xeb\xc2\xc0\xd2\xe7\x00\x4b\x5f\x11\x58\x7a\x05\x4d\xd3\x44\xad\xb4\xe9\x6c\xca\x3e\x87\x19\x45\x50\xcb\xcb\x13\x9a\x01\x4e\x2e\xf2\xa9\x52\xd0\x40\x65\x7a\x58\xd0\x55\xc8\xf2\x5b\x50\xc3\x39\x94\x54\x7a\x7e\x38\x76\x20\x53\xb5\x71\x1c\x23\xd9\xfd\x16\x37\x0b\x79\xe4\x87\x23\x1b\x51\x1f\xde\x49\x4a\x00\x8b\x03\x11\xc1\x1a\x8f\x84\xc5\x00\x82\xf6\x13\xf8\x55\xcc\x28\x85\x83\x79\x60\x61\x5c\x04\x77\xc1\x2e\xd8\x53\x80\xdb\x5c\x16\xd3\x41\x48\x6b\xde\x00\xad\xe5\x21\x9c\x36\xda\x09\x2a\x85\xad\xc8\x69\x54\x9c\x0a\x34\xe4\x3d\xd1\x1c\xee\xa3\xe1\x53\x60\x01\xef\x39\x4d\x61\xef\x7c\xae\x5e\xc1\x44\x08\x87\xe8\x2d\x29\x26\x36\xe2\xa1\x42\xfb\xb6\xbc\x28\x38\x8f\xa4\x45\x6f\xe7\x6b\x9a\x79\xf9\xba\x20\x63\x9a\xae\x0f\x5a\x0f\x5f\x3e\xbe\x64\xe3\xe2\x77\xe1\x63\xe7\x6f\x43\x07\x82\xab\x06\xa3\x0c\x06\x16\x1b\x55\xd9\x30\xef\xf6\x34\x9e\x69\x1d\x61\x30\x1b\x1d\x19\xe4\x7c\xb3\x2b\x1a\x3c\xd0\xa8\xf6\x45\x02\x29\x91\x5b\xe5\xba\x6e\x1d\x96\xc6\x21\xcc\x4e\x35\x83\x1a\xde\xe0\x80\x16\x60\x79\xfc\x48\x36\x94\xe4\xac\x9f\x10\x23\x71\xa8\x98\x61\xc4\xb7\xab\xed\x1d\x25\x23\x83\x43\x4f\x97\x59\xb0\xf4\x2f\x7c\xbd\xcc\xe3\x22\xa0\x14\xd8\x1f\xc4\x34\x52\x75\x59\x48\x14\xc3\x58\xeb\xf9\x80\x48\x18\x5c\xe9\x24\x02\x85\x41\x02\x67\xc8\x34\xf3\x7b\xad\x4e\xe3\x65\x84\x83\xab\x58\x36\x54\xcf\xd4\x56\x73\x54\x43\x50\x06\x9a\x49\x21\x6c\x96\xbb\xc5\xb7\x37\xac\x47\x82\xf4\x66\x36\x63\x77\xd5\x75\x30\xa6\xef\x24\xde\xbd\x72\xd8\xd0\xee\x9e\x96\x03\xf4\xad\x37\x8b\x31\x82\xa7\x5f\xbc\xfc\xff\xf2\xe8\x67\xd8\x01\xff\xf4\xe7\x81\xf6\xff\x29\x4f\x7f\x0e\x15\xf3\x5d\xdc\x95\x09\x2b\xc1\xe3\xe1\xfb\x1f\x07\x4c\x04\xf9\x6d\xc4\x19\x1c\xae\x62\x16\x19\xdd\x7b\x46\xac\x12\x25\x42\x16\x88\x84\xd8\x02\x04\x49\xfe\xd6\xc0\x2e\x0c\x44\xe9\x07\xee\x37\x6c\x2f\xf0\xaa\x8b\x5a\x93\x12\xe3\x87\x05\xe2\x2e\x48\xca\xf8\xb2\x51\xd2\x55\x5f\x95\x5a\x60\x72\x55\x3e\xce\xa0\xb4\xd2\x1b\x3e\x4b\x1b\x46\xe3\x85\x2e\x52\x99\xd5\xa0\xcb\x03\x76\x15\x75\x5b\x4a\x49\xbc\x0f\xf3\xd8\x53\x8e\xa8\x59\x41\xef\x25\x25\x0c\x75\x29\x29\xf2\xb0\x1e\xde\x8b\x96\x2f\x4d\x1f\xdb\xeb\x04\x9d\xd4\x6a\x06\x9d\x0b\x6a\x05\xd4\x34\xc7\x0d\x7a\x33\x34\x86\x96\x54\x38\xa2\xb1\xe5\x76\xd7\x6b\xca\x56\x02\x58\x5e\x70\x00\x4b\x49\x81\x0e\xa8\x80\x49\x25\xab\x7c\x4e\xdf\x86\xe9\xc1\x5b\x7b\xc8\x75\xaf\xec\x4d\xc0\x04\xd6\x34\x79\xcb\xf1\x33\x7f\xd0\x70\x42\xc1\x9b\x7b\xec\x34\x26\x2f\xf8\x6c\xd7\xdc\x5f\x0e\xa5\x8e\x9b\x18\x56\x66\xb3\xcc\x90\xe3\x99\x2d\xdc\x4b\x13\x9b\x59\x8a\x75\xae\x3c\x0b\xa3\xc8\x64\xf9\x40\x83\x78\x41\x2e\x50\xef\xc4\xe7\x6c\x4b\x16\x80\x14\xf6\x44\xb9\x1f\x51\xde\xf7\x6d\x35\xdf\xf1\xf5\xae\x9a\xb1\xf0\xa2\x14\x9b\x19\x97\x37\x82\xed\x76\xe6\x66\x72\xa9\x5f\x87\x61\xf5\xa1\x37\x7b\x1a\x6d\x00\x27\x81\xc4\xac\x5b\x12\x46\xcc\xaa\xc0\xed\x88\x03\x90\x3b\xd3\x08\x62\xc3\x3b\x4e\xd6\xe5\xbc\x3c\x89\xb7\x16\xe9\xe5\xb1\xe6\x74\x9b\x7e\x6b\x81\xf0\x2f\xcf\xae\x2e\x6e\xa1\x25\x06\x55\x9a\x00\x64\x40\x18\x9c\xa5\xc4\x81\xac\x80\x42\xd4\x76\x48\x0d\xee\xf5\x58\x0f\xeb\x23\x21\xb6\x6e\x1a\xee\xb6\x6d\x5f\x5e\x49\xe2\x07\xef\xf9\x31\x05\xb6\x8f\x97\x32\xb3\xf4\x6c\xb7\xee\x2b\x36\x66\xb3\xd6\xd4\xa0\x6a\x5e\xa6\x5d\xb9\xcd\x5b\x0b\xe7\x8a\xe0\x45\xe9\x83\xa3\x07\xb3\x68\xf5\x65\xbd\x3c\xac\x28\x6f\x5c\x5e\xbd\xb9\xa4\xcf\x45\x7b\x23\xf7\xb9\x3a\xd2\x8f\xd5\x96\xc1\xf4\x69\x6c\x1e\x30\xa5\x00\xf6\x2f\x48\xb1\xa5\xc2\x17\x7f\x65\xfb\xa9\x5a\x38\x82\xb9\x38\x3e\x83\xde\x81\x92\xc2\xc5\xa7\x4d\x23\xdc\x92\x49\x7e\xbe\x13\x34\x1d\x4d\x24\xf9\x19\x03\xe1\x37\xa1\xd9\xe6\x74\x6b\x08\x0c\xe3\xc2\x0c\xc5\x33\x7d\x38\x54\x31\x3d\x12\x55\x62\xe8\x40\x62\xf1\xac\x6d\x28\x51\xc6\x45\xee\x32\xd5\x9f\x45\xcc\x2c\xdc\xb9\x06\x2f\x48\x7f\x99\x09\x53\x58\x59\x20\x65\xdc\x36\xe8\xc9\x10\x15\x71\x89\x08\x32\x13\xfe\x6a\x8f\xec\xc4\x55\xfb\x47\x95\x46\x25\xa2\xe7\x76\x46\x78\x9d\x30\x0d\xba\xc5\x1c\x28\xa8\x7d\xb0\xdf\xc7\x15\xdf\xb5\xed\x8b\x2e\xce\x74\x60\xee\x3a\x4d\x75\x60\xf1\xad\x9a\xc2\xe6\xdb\xad\xdb\x36\x82\x77\x94\x40\xb6\x01\xc8\x27\x61\x38\x01\x04\x2d\x82\x6e\x50\x8f\xf8\xd0\x85\x40\xec\x4a\xa7\x00\xc3\xad\x47\x93\x9b\xeb\x6b\x0e\x2c\xc6\xe1\x31\x35\x2a\x0c\xe2\x8c\x9d\xb1\x51\xba\x95\x14\xcf\xd0\x8c\x95\x72\x50\x72\xf1\x98\xec\x09\xde\x77\x48\xe4\x53\x85\x81\xb7\x3b\xf7\x76\xf9\xbb\x5d\x2d\xe7\xa5\x20\x4b\x1b\xe2\xac\xb0\x11\x48\x2f\x6d\xd3\xf4\xf6\x3c\x45\x20\xba\xbc\xa3\x64\xda\xd3\xfa\x95\x43\x30\x5f\xd8\x2d\x32\x89\xb7\x1f\x94\xc1\x75\xe1\x02\xae\x05\xe3\x42\xd4\xef\x71\x09\xea\xf7\x01\x70\xb1\x2f\xb1\x8d\xff\x12\xbf\x84\x49\xbb\x1e\xb3\xb5\xaa\x52\xa3\x0d\x58\xd2\x86\x74\x13\xe2\xa0\x98\x7b\xc2\x38\xb5\x2b\xc6\x49\xd2\x20\xc8\x50\x7a\xf1\xa9\xa1\xbc\xe0\x53\x43\xd9\xc7\xa7\x6a\xc7\x06\x3d\xe8\xba\xb5\x4d\xc4\xe5\xe5\x9b\x78\xb6\x7d\x6e\xf0\xfc\x11\x9b\xbd\xdd\xe3\x38\xb9\x1c\xae\xef\x5e\xca\x0e\x93\x8f\x82\x12\x8a\xcd\x10\x7f\x9a\x3a\xac\xa3\xfb\x6d\x5d\xf5\xe5\xf7\xf7\x60\x01\x70\xaf\xaf\x8a\xf9\xbd\x47\xe1\xba\xa9\xe0\x1f\x11\x2c\x9c\x6a\x71\x00\x3d\xee\xf0\x1e\xbd\xd0\x9a\x8a\xb3\x79\xd5\x96\xb6\xbf\x9f\x04\x8f\xa6\x8e\x48\xda\x6f\x03\x8e\xa0\xc3\x2d\xc0\x3a\xc6\xce\x36\x6d\x90\x91\x91\xbc\xd0\xcb\x9b\x92\x6c\x67\xfa\xce\xaa\x79\x8e\x64\xdf\x43\x89\xf1\x6b\x31\x7f\xd5\xed\xc0\xfa\x87\x90\xbd\xaf\x6b\xb8\xc3\x58\x11\x7b\xf7\x1a\x7a\xb6\xd1\x13\xd9\x50\xb0\xe9\x0b\xd9\x5a\x00\x63\x77\xea\x8b\x33\x1e\x70\xa8\xb1\x18\x0e\x18\x7e\x60\xd5\xef\xa5\x84\x69\x0c\x86\x7d\x46\x87\xe1\xcd\x6e\x83\xf7\x31\x2f\x39\xaa\x1c\x5e\x38\x1d\x75\x6b\x4b\x92\x7e\x1e\xf6\x08\x09\x8e\x09\x89\x8b\x27\xfb\xb7\x64\x6b\xbd\xa8\x13\x37\x50\x78\xb9\xa4\x6f\x70\x33\xe7\xb0\x43\x9b\x90\x17\x4b\xa3\x42\xef\x38\xcf\x89\xb1\x13\x85\xcd\x3b\xdc\x91\x8a\x79\x5f\x4e\x92\xca\x6f\xbb\x72\x47\x95\x97\xf5\x12\x64\xfa\x67\xfe\x49\xe2\x0e\xff\x74\x08\x12\xb7\x4a\x28\xbc\xd8\x31\xe3\xa9\x39\x5a\x42\xef\x45\x29\x8e\x16\xee\x94\x4b\x82\x99\x09\x77\x81\x33\xfc\x9e\xee\xa0\xc2\x8e\x8f\x26\x71\xbe\xcd\x22\xad\xa9\x26\x98\xbb\x57\x3f\xbf\x39\x1f\x40\x4e\x30\x03\xcd\x99\x60\x1e\x9a\x33\xc1\x2a\xe4\xd2\xd9\x0d\x01\x17\xcd\xd3\x23\x10\xc8\x83\x03\x10\x82\xf6\x06\x26\xa0\xe4\xc9\x8a\x94\xf4\x0b\xa2\x2c\x71\x1a\x12\xa2\x97\xdf\x31\x50\xf0\x7a\x96\x40\xd9\xe3\x59\xa3\x56\xeb\xb0\xcd\x5a\x6e\x1a\x3d\xd7\x11\x4b\xa7\x80\xeb\x88\xa7\xc0\x64\xf7\x0c\x9a\x9d\xa3\x2a\x0b\xbc\x22\xf0\x17\x9a\x64\xa0\x06\xe2\x6b\x36\x08\xad\xda\x75\x93\x08\xb7\x72\xd2\xeb\x09\x7e\x45\x53\xa7\xcb\x8f\xd7\x8b\xc0\xfa\x15\xc8\x4f\x7c\x4b\x09\x03\x5e\x2e\x1c\x62\x4c\x67\xfc\xf2\xc4\xbf\x2b\x06\xf5\xf2\x60\x30\xeb\xea\xba\x74\xca\x68\x1d\xcd\x1b\x4a\x8b\x80\x57\x7d\xbf\xed\xcc\x55\x1e\x6f\x59\xa5\xe7\xf4\x63\x30\x88\xb0\x2a\x1d\xc9\xa8\xa6\x6d\x85\x0b\x82\x00\x2f\x92\x30\x8d\x71\x83\xd6\xdd\x21\x00\xd7\xed\x61\xc8\xe3\x96\xad\x63\x9c\xb6\x42\x5e\x6a\x52\x28\x0b\xb8\xd6\x59\x06\x98\x6c\x99\xa1\x74\x97\x64\x18\xec\x92\x66\xf4\x34\x5b\xb4\x12\xe8\x8f\xff\x5c\xb1\xc5\x89\xcb\x09\xd7\x9e\xa5\x75\x44\x7b\xc5\x4e\x9c\x0d\xf5\xd3\xc3\xfb\x17\x10\x04\x4d\x96\x31\xf1\x6a\x42\x0c\x50\x7e\x2e\x17\xbb\xe0\xe6\xf0\x67\xf9\xad\xaa\x7a\x5f\x4d\x63\x36\xce\xbb\x1a\x2f\x66\x5c\x48\x4a\x00\x33\x15\xed\xd3\xba\x0e\x4b\x6d\xb3\xd8\x3e\xd8\xbe\x6b\x1e\x87\x59\x86\x32\xc3\x31\xb3\xc7\x92\x9f\xd9\x5a\xfc\xc5\x06\xb6\x64\x06\x1b\x4a\x3c\x61\x1a\x22\x86\x05\x76\x7b\x96\x37\xd1\x71\xcb\x6a\xb6\xcc\xb2\xb6\xb3\x00\x16\xc7\x87\xe0\x55\x5f\xe9\x83\xe4\xdf\x65\x29\x9a\xbc\x17\xeb\xab\x0f\x83\xc7\xff\x4c\xbb\x1f\x58\x63\x44\x6e\xf5\xf7\xe5\xdd\x8c\xb6\xac\x83\x20\x81\xf2\xab\x18\xbd\xb9\x65\x11\xad\xbf\xf5\x11\xad\xa3\x17\xc5\x87\xaf\x71\xb8\xd7\x11\x50\x2b\x9b\x57\xca\xcb\x2b\x41\x0f\x9e\x74\xed\xe2\xc9\xfd\xf0\xe1\x03\xd6\x97\xc6\x61\xc3\xa3\x2a\x65\x74\xf6\x82\xc4\xaf\xfa\x6a\x83\xfc\x0e\xeb\x15\xa5\x9e\x54\xcd\x51\xc6\xdd\xb3\x0a\x5a\xc3\x30\xc8\xb9\x21\x2a\x0a\x0e\x1c\x56\xa8\xb1\xcb\xc7\xf5\x0d\x1e\xb5\x08\x1e\xee\x68\xea\xaf\xe9\xd8\x64\x94\xe4\x5f\x35\x64\xf6\x57\x77\xcb\x45\x31\x53\xec\xdb\xef\x01\x2d\xc8\x8c\x4e\x4f\xa7\x27\x0f\x04\xd9\x90\xf7\xce\x6c\x16\xe9\xc7\xed\xd3\x18\x53\xc6\x70\x1a\x35\x5e\xfe\x77\x41\x70\x73\x38\x5e\x4b\x46\xd5\x69\x60\x5a\x09\x29\xff\x9d\x7b\x2f\x2c\x79\xcf\x31\xa6\x3f\x24\xf9\x92\xc7\x44\xff\x27\x78\xce\x4f\x5c\x2d\x40\xa3\xf4\x99\xc8\x4f\xfe\xfa\x96\x2b\xfe\x36\xed\x4a\x62\x9a\x88\x8e\xfc\xed\x06\x09\x1b\x3a\x5a\x13\x2b\xe2\x84\x15\x12\xf8\x3d\x4b\xfc\x2c\xf0\xb3\xc8\x6f\xf0\x6b\x8f\x5f\xfb\xb2\xfc\x28\x85\xc1\x55\xa9\x38\x1d\xce\x57\x48\xb9\xc1\xef\x1b\x8e\x22\x7c\x9f\xdd\xcc\xb8\x1d\x7d\x22\xc2\x7e\x20\x26\x33\x37\xa7\xe9\xf6\x83\xd2\xb9\x55\x4d\x95\xcf\xfb\x7c\xed\x7f\xa3\x49\xf8\xa2\x14\x6e\x5e\x93\xe4\xf3\x3e\x58\x63\xbf\xb2\x0a\xe5\x9b\x52\xb9\x1f\x9a\x28\x9f\x94\xd6\xe6\xfb\xcc\xf7\x4b\xbf\x90\xea\x7b\xa5\x5f\x84\xde\xa2\x6d\xb6\x1c\x9f\xf5\x83\x7b\x24\xd4\x3f\xb7\x76\x4a\x79\x66\xb5\xc5\x41\x39\xd9\x33\x7a\xcd\x4f\x06\xe2\xc9\x65\x76\x8d\x9d\x25\x16\x37\xb9\xaa\xb7\x3b\x77\x3e\xf5\x41\xbb\x05\xcc\x87\xa5\x12\x7b\x7b\x7e\x43\x49\x1e\x98\xa3\xd9\xcd\xe6\x95\xbe\xab\x57\xa6\x7c\x18\x78\xf8\x6f\xff\x06\x70\xfa\xfc\xf7\x7f\x4f\xcf\x9e\x3f\x4a\xcb\xcf\x1c\x96\xaf\x4b\x37\xf9\x67\x9c\x0a\x14\x8a\x7e\xbe\x88\x00\xd9\x8d\x17\x96\xd3\x7a\x0d\xa5\xaf\xae\xe3\xee\xe9\xff\x05\x00\x00\xff\xff\xa5\xc4\xd7\xe1\x43\xb0\x00\x00") func confLocaleLocale_enUsIniBytes() ([]byte, error) { return bindataRead( @@ -4379,7 +4379,7 @@ func confLocaleLocale_enUsIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_en-US.ini", size: 44924, mode: os.FileMode(420), modTime: time.Unix(1447876625, 0)} + info := bindataFileInfo{name: "conf/locale/locale_en-US.ini", size: 45123, mode: os.FileMode(420), modTime: time.Unix(1447878366, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/routers/admin/admin.go b/routers/admin/admin.go index 54e4559f..f530cd43 100644 --- a/routers/admin/admin.go +++ b/routers/admin/admin.go @@ -116,6 +116,7 @@ type AdminOperation int const ( CLEAN_INACTIVATE_USER AdminOperation = iota + 1 CLEAN_REPO_ARCHIVES + CLEAN_MISSING_REPOS GIT_GC_REPOS SYNC_SSH_AUTHORIZED_KEY SYNC_REPOSITORY_UPDATE_HOOK @@ -139,6 +140,9 @@ func Dashboard(ctx *middleware.Context) { case CLEAN_REPO_ARCHIVES: success = ctx.Tr("admin.dashboard.delete_repo_archives_success") err = models.DeleteRepositoryArchives() + case CLEAN_MISSING_REPOS: + success = ctx.Tr("admin.dashboard.delete_missing_repos_success") + err = models.DeleteMissingRepositories() case GIT_GC_REPOS: success = ctx.Tr("admin.dashboard.git_gc_repos_success") err = models.GitGcRepos() diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl index 6c2d0aa5..769375ec 100644 --- a/templates/admin/dashboard.tmpl +++ b/templates/admin/dashboard.tmpl @@ -28,17 +28,21 @@
- + - + - + + + + +
{{.i18n.Tr "admin.dashboard.operation_run"}}
{{.i18n.Tr "admin.dashboard.git_gc_repos"}}{{.i18n.Tr "admin.dashboard.delete_missing_repos"}} {{.i18n.Tr "admin.dashboard.operation_run"}}
{{.i18n.Tr "admin.dashboard.resync_all_sshkeys"}}{{.i18n.Tr "admin.dashboard.git_gc_repos"}} {{.i18n.Tr "admin.dashboard.operation_run"}}
{{.i18n.Tr "admin.dashboard.resync_all_update_hooks"}}{{.i18n.Tr "admin.dashboard.resync_all_sshkeys"}} {{.i18n.Tr "admin.dashboard.operation_run"}}
{{.i18n.Tr "admin.dashboard.resync_all_update_hooks"}} {{.i18n.Tr "admin.dashboard.operation_run"}}
-- cgit v1.2.3 From 3d14e73fd835f2a0ed4a22daa4c67df245be8103 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Fri, 20 Nov 2015 00:47:35 -0500 Subject: fix #1119 and data race in timming tasks --- models/repo.go | 63 ++++++++++++++++++++++++++++++++++++++++------------ modules/git/tree.go | 25 +++++++++++++++++++-- routers/user/home.go | 3 +++ 3 files changed, 75 insertions(+), 16 deletions(-) (limited to 'models/repo.go') diff --git a/models/repo.go b/models/repo.go index 1521f1e0..80009409 100644 --- a/models/repo.go +++ b/models/repo.go @@ -17,6 +17,7 @@ import ( "regexp" "sort" "strings" + "sync" "time" "unicode/utf8" @@ -1377,20 +1378,54 @@ func RewriteRepositoryUpdateHook() error { }) } -var ( - // Prevent duplicate running tasks. - isMirrorUpdating = false - isGitFscking = false - isCheckingRepos = false +// statusPool represents a pool of status with true/false. +type statusPool struct { + lock sync.RWMutex + pool map[string]bool +} + +// Start sets value of given name to true in the pool. +func (p *statusPool) Start(name string) { + p.lock.Lock() + defer p.lock.Unlock() + + p.pool[name] = true +} + +// Stop sets value of given name to false in the pool. +func (p *statusPool) Stop(name string) { + p.lock.Lock() + defer p.lock.Unlock() + + p.pool[name] = false +} + +// IsRunning checks if value of given name is set to true in the pool. +func (p *statusPool) IsRunning(name string) bool { + p.lock.RLock() + defer p.lock.RUnlock() + + return p.pool[name] +} + +// Prevent duplicate running tasks. +var taskStatusPool = &statusPool{ + pool: make(map[string]bool), +} + +const ( + _MIRROR_UPDATE = "mirror_update" + _GIT_FSCK = "git_fsck" + _CHECK_REPOs = "check_repos" ) // MirrorUpdate checks and updates mirror repositories. func MirrorUpdate() { - if isMirrorUpdating { + if taskStatusPool.IsRunning(_MIRROR_UPDATE) { return } - isMirrorUpdating = true - defer func() { isMirrorUpdating = false }() + taskStatusPool.Start(_MIRROR_UPDATE) + defer taskStatusPool.Stop(_MIRROR_UPDATE) log.Trace("Doing: MirrorUpdate") @@ -1438,11 +1473,11 @@ func MirrorUpdate() { // GitFsck calls 'git fsck' to check repository health. func GitFsck() { - if isGitFscking { + if taskStatusPool.IsRunning(_GIT_FSCK) { return } - isGitFscking = true - defer func() { isGitFscking = false }() + taskStatusPool.Start(_GIT_FSCK) + defer taskStatusPool.Stop(_GIT_FSCK) log.Trace("Doing: GitFsck") @@ -1507,11 +1542,11 @@ func repoStatsCheck(checker *repoChecker) { } func CheckRepoStats() { - if isCheckingRepos { + if taskStatusPool.IsRunning(_CHECK_REPOs) { return } - isCheckingRepos = true - defer func() { isCheckingRepos = false }() + taskStatusPool.Start(_CHECK_REPOs) + defer taskStatusPool.Stop(_CHECK_REPOs) log.Trace("Doing: CheckRepoStats") diff --git a/modules/git/tree.go b/modules/git/tree.go index cc62a2d5..6cfdbf47 100644 --- a/modules/git/tree.go +++ b/modules/git/tree.go @@ -28,6 +28,28 @@ type Tree struct { entriesParsed bool } +var escapeChar = []byte("\\") + +func unescapeChars(in []byte) []byte { + if bytes.Index(in, escapeChar) == -1 { + return in + } + + endIdx := len(in) - 1 + isEscape := false + out := make([]byte, 0, endIdx+1) + for i := range in { + if in[i] == '\\' && i != endIdx { + isEscape = !isEscape + if isEscape { + continue + } + } + out = append(out, in[i]) + } + return out +} + // Parse tree information from the (uncompressed) raw // data from the tree object. func parseTreeData(tree *Tree, data []byte) ([]*TreeEntry, error) { @@ -74,8 +96,7 @@ func parseTreeData(tree *Tree, data []byte) ([]*TreeEntry, error) { // In case entry name is surrounded by double quotes(it happens only in git-shell). if entry.name[0] == '"' { - entry.name = string(data[pos+1 : pos+step-1]) - entry.name = strings.Replace(entry.name, `\"`, `"`, -1) + entry.name = string(unescapeChars(data[pos+1 : pos+step-1])) } pos += step + 1 diff --git a/routers/user/home.go b/routers/user/home.go index 98033fc1..96202ed0 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -319,6 +319,9 @@ func Profile(ctx *middleware.Context) { if uname == "favicon.ico" { ctx.Redirect(setting.AppSubUrl + "/img/favicon.png") return + } else if strings.HasSuffix(uname, ".png") { + ctx.Error(404) + return } isShowKeys := false -- cgit v1.2.3 From 1d4a5b1825e46b51a1cbd6dc5d24a4ea585a0e72 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Fri, 20 Nov 2015 02:53:54 -0500 Subject: fix #2002 --- models/pull.go | 30 ++++++++++++++++++------------ models/release.go | 2 +- models/repo.go | 8 ++++---- 3 files changed, 23 insertions(+), 17 deletions(-) (limited to 'models/repo.go') diff --git a/models/pull.go b/models/pull.go index 5c7d4a3f..e67d242e 100644 --- a/models/pull.go +++ b/models/pull.go @@ -166,43 +166,49 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error var stderr string if _, stderr, err = process.ExecTimeout(5*time.Minute, - fmt.Sprintf("PullRequest.Merge(git clone): %s", tmpBasePath), + fmt.Sprintf("PullRequest.Merge (git clone): %s", tmpBasePath), "git", "clone", baseGitRepo.Path, tmpBasePath); err != nil { return fmt.Errorf("git clone: %s", stderr) } // Check out base branch. if _, stderr, err = process.ExecDir(-1, tmpBasePath, - fmt.Sprintf("PullRequest.Merge(git checkout): %s", tmpBasePath), + fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath), "git", "checkout", pr.BaseBranch); err != nil { return fmt.Errorf("git checkout: %s", stderr) } // Add head repo remote. if _, stderr, err = process.ExecDir(-1, tmpBasePath, - fmt.Sprintf("PullRequest.Merge(git remote add): %s", tmpBasePath), + fmt.Sprintf("PullRequest.Merge (git remote add): %s", tmpBasePath), "git", "remote", "add", "head_repo", headRepoPath); err != nil { - return fmt.Errorf("git remote add[%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) + return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) } // Merge commits. if _, stderr, err = process.ExecDir(-1, tmpBasePath, - fmt.Sprintf("PullRequest.Merge(git fetch): %s", tmpBasePath), + fmt.Sprintf("PullRequest.Merge (git fetch): %s", tmpBasePath), "git", "fetch", "head_repo"); err != nil { - return fmt.Errorf("git fetch[%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) + return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) } if _, stderr, err = process.ExecDir(-1, tmpBasePath, - fmt.Sprintf("PullRequest.Merge(git merge): %s", tmpBasePath), - "git", "merge", "--no-ff", "-m", - fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.HeadUserName, pr.HeadRepo.Name, pr.BaseBranch), - "head_repo/"+pr.HeadBranch); err != nil { - return fmt.Errorf("git merge[%s]: %s", tmpBasePath, stderr) + fmt.Sprintf("PullRequest.Merge (git merge --no-commit): %s", tmpBasePath), + "git", "merge", "--no-commit", "head_repo/"+pr.HeadBranch); err != nil { + return fmt.Errorf("git merge --no-commit [%s]: %s", tmpBasePath, stderr) + } + + sig := doer.NewGitSig() + if _, stderr, err = process.ExecDir(-1, tmpBasePath, + fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath), + "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), + "-m", fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.HeadUserName, pr.HeadRepo.Name, pr.BaseBranch)); err != nil { + return fmt.Errorf("git commit [%s]: %s", tmpBasePath, stderr) } // Push back to upstream. if _, stderr, err = process.ExecDir(-1, tmpBasePath, - fmt.Sprintf("PullRequest.Merge(git push): %s", tmpBasePath), + fmt.Sprintf("PullRequest.Merge (git push): %s", tmpBasePath), "git", "push", baseGitRepo.Path, pr.BaseBranch); err != nil { return fmt.Errorf("git push: %s", stderr) } diff --git a/models/release.go b/models/release.go index 8b98f0b8..ef1d640d 100644 --- a/models/release.go +++ b/models/release.go @@ -180,7 +180,7 @@ func DeleteReleaseByID(id int64) error { return fmt.Errorf("RepoPath: %v", err) } - _, stderr, err := process.ExecDir(-1, repoPath, fmt.Sprintf("Delete release [%d]", rel.ID), + _, stderr, err := process.ExecDir(-1, repoPath, fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID), "git", "tag", "-d", rel.TagName) if err != nil && !strings.Contains(stderr, "not found") { return fmt.Errorf("git tag -d: %v - %s", err, stderr) diff --git a/models/repo.go b/models/repo.go index 80009409..cc7bde3a 100644 --- a/models/repo.go +++ b/models/repo.go @@ -576,20 +576,20 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) { func initRepoCommit(tmpPath string, sig *git.Signature) (err error) { var stderr string if _, stderr, err = process.ExecDir(-1, - tmpPath, fmt.Sprintf("initRepoCommit(git add): %s", tmpPath), + tmpPath, fmt.Sprintf("initRepoCommit (git add): %s", tmpPath), "git", "add", "--all"); err != nil { return fmt.Errorf("git add: %s", stderr) } if _, stderr, err = process.ExecDir(-1, - tmpPath, fmt.Sprintf("initRepoCommit(git commit): %s", tmpPath), + tmpPath, fmt.Sprintf("initRepoCommit (git commit): %s", tmpPath), "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", "initial commit"); err != nil { return fmt.Errorf("git commit: %s", stderr) } if _, stderr, err = process.ExecDir(-1, - tmpPath, fmt.Sprintf("initRepoCommit(git push): %s", tmpPath), + tmpPath, fmt.Sprintf("initRepoCommit (git push): %s", tmpPath), "git", "push", "origin", "master"); err != nil { return fmt.Errorf("git push: %s", stderr) } @@ -700,7 +700,7 @@ func initRepository(e Engine, repoPath string, u *User, repo *Repository, opts C // Init bare new repository. os.MkdirAll(repoPath, os.ModePerm) _, stderr, err := process.ExecDir(-1, repoPath, - fmt.Sprintf("initRepository(git init --bare): %s", repoPath), "git", "init", "--bare") + fmt.Sprintf("initRepository (git init --bare): %s", repoPath), "git", "init", "--bare") if err != nil { return fmt.Errorf("git init --bare: %v - %s", err, stderr) } -- cgit v1.2.3 From 8966750fd456226773a201dbab41b87c4c32eda4 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Sat, 21 Nov 2015 19:11:57 -0500 Subject: add some log --- models/repo.go | 1 + templates/admin/org/list.tmpl | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'models/repo.go') diff --git a/models/repo.go b/models/repo.go index cc7bde3a..b5324d82 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1353,6 +1353,7 @@ func DeleteMissingRepositories() error { } for _, repo := range repos { + log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID) if err := DeleteRepository(repo.OwnerID, repo.ID); err != nil { if err2 := CreateRepositoryNotice(fmt.Sprintf("DeleteRepository [%d]: %v", repo.ID, err)); err2 != nil { log.Error(4, "CreateRepositoryNotice: %v", err2) diff --git a/templates/admin/org/list.tmpl b/templates/admin/org/list.tmpl index 4070b46e..bc4610b6 100644 --- a/templates/admin/org/list.tmpl +++ b/templates/admin/org/list.tmpl @@ -24,7 +24,7 @@ {{range .Orgs}} {{.Id}} - {{.Name}} + {{.Name}} {{.NumTeams}} {{.NumMembers}} {{.NumRepos}} -- cgit v1.2.3 From dcc740fd26d519e7c59ec3d56012a7b961d3e912 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Sat, 21 Nov 2015 19:30:11 -0500 Subject: fix incorrect --- models/repo.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'models/repo.go') diff --git a/models/repo.go b/models/repo.go index b5324d82..b628e752 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1317,7 +1317,7 @@ func DeleteRepositoryArchives() error { repo := bean.(*Repository) repoPath, err := repo.RepoPath() if err != nil { - if err2 := CreateRepositoryNotice(fmt.Sprintf("DeleteRepositoryArchives[%d]: %v", repo.ID, err)); err2 != nil { + if err2 := CreateRepositoryNotice(fmt.Sprintf("DeleteRepositoryArchives.RepoPath [%d]: %v", repo.ID, err)); err2 != nil { log.Error(4, "CreateRepositoryNotice: %v", err2) } return nil @@ -1334,7 +1334,10 @@ func DeleteMissingRepositories() error { repo := bean.(*Repository) repoPath, err := repo.RepoPath() if err != nil { - return fmt.Errorf("RepoPath [%d]: %v", repo.ID, err) + if err2 := CreateRepositoryNotice(fmt.Sprintf("DeleteRepositoryArchives.RepoPath [%d]: %v", repo.ID, err)); err2 != nil { + log.Error(4, "CreateRepositoryNotice: %v", err2) + } + return nil } if !com.IsDir(repoPath) { -- cgit v1.2.3