diff options
59 files changed, 427 insertions, 2572 deletions
@@ -1,6 +1,7 @@ [run] init_cmds = [ #["grep", "-rn", "FIXME", "."], + ["make", "build-dev"], ["./gogs", "web"] ] watch_all = true diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 45719ac78a..c02412d785 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -48,10 +48,6 @@ Please read detailed information on [Wiki](https://github.com/gogits/gogs/wiki/C Before opening an issue, please make sure your problem isn't already addressed on the [Troubleshooting](http://gogs.io/docs/intro/troubleshooting.html) and [FAQs](http://gogs.io/docs/intro/faqs.html) pages. -## Things To Notice - -Please take a moment to check that an issue on [GitHub](https://github.com/gogits/gogs/issues) doesn't already exist documenting your bug report or improvement proposal. If it does, it never hurts to add a quick "+1" or "I have this problem too". This will help prioritize the most common problems and requests. - ## Code of conduct As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. @@ -17,6 +17,7 @@ github.com/go-sql-driver/mysql = commit:b4db83c github.com/go-xorm/core = commit:1e2868c github.com/go-xorm/xorm = github.com/gogits/chardet = commit:2404f77725 +github.com/gogits/cron = github.com/gogits/git-module = commit:3b40eae github.com/gogits/go-gogs-client = github.com/issue9/identicon = commit:f8c0d2c @@ -31,6 +32,7 @@ github.com/microcosm-cc/bluemonday = commit:4ac6f27 github.com/msteinert/pam = commit:02ccfbf github.com/nfnt/resize = commit:4d93a29 github.com/russross/blackfriday = commit:006144a +github.com/satori/go.uuid = github.com/sergi/go-diff = commit:ec7fdbb github.com/shurcooL/sanitized_anchor_name = commit:10ef21a github.com/Unknwon/cae = commit:7f5e046 @@ -3,7 +3,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra ![](https://github.com/gogits/gogs/blob/master/public/img/gogs-large-resize.png?raw=true) -##### Current version: 0.8.40 +##### Current version: 0.8.41 | Web | UI | Preview | |:-------------:|:-------:|:-------:| diff --git a/cmd/serve.go b/cmd/serve.go index 305aaa6486..abfa46a241 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -15,13 +15,13 @@ import ( "github.com/Unknwon/com" "github.com/codegangsta/cli" + gouuid "github.com/satori/go.uuid" "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/httplib" "github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/setting" - "github.com/gogits/gogs/modules/uuid" ) const ( @@ -42,11 +42,6 @@ func setup(logPath string) { setting.NewContext() log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath)) - if setting.DisableSSH { - println("Gogs: SSH has been disabled") - os.Exit(1) - } - models.LoadConfigs() if setting.UseSQLite3 || setting.UseTiDB { @@ -138,8 +133,14 @@ func runServ(c *cli.Context) { if c.IsSet("config") { setting.CustomConf = c.String("config") } + setup("serv.log") + if setting.DisableSSH { + println("Gogs: SSH has been disabled") + return + } + if len(c.Args()) < 1 { fail("Not enough arguments", "Not enough arguments") } @@ -250,7 +251,7 @@ func runServ(c *cli.Context) { } } - uuid := uuid.NewV4().String() + uuid := gouuid.NewV4().String() os.Setenv("uuid", uuid) // Special handle for Windows. diff --git a/cmd/update.go b/cmd/update.go index 30f778f229..aca2a7cbe7 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -16,7 +16,7 @@ import ( var CmdUpdate = cli.Command{ Name: "update", - Usage: "This command should only be called by SSH shell", + Usage: "This command should only be called by Git hook", Description: `Update get pushed info and insert into database`, Action: runUpdate, Flags: []cli.Flag{ diff --git a/cmd/web.go b/cmd/web.go index 072d31917a..44d9bab00f 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -398,7 +398,7 @@ func runWeb(ctx *cli.Context) { m.Group("/settings", func() { m.Combo("").Get(repo.Settings). Post(bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost) - m.Route("/collaboration", "GET,POST", repo.Collaboration) + m.Combo("/collaboration").Get(repo.Collaboration).Post(repo.CollaborationPost) m.Group("/hooks", func() { m.Get("", repo.Webhooks) diff --git a/conf/locale/locale_en-US.ini b/conf/locale/locale_en-US.ini index 5c279c3dfe..38fbfa1a19 100644 --- a/conf/locale/locale_en-US.ini +++ b/conf/locale/locale_en-US.ini @@ -612,6 +612,7 @@ settings.add_collaborator = Add New Collaborator settings.add_collaborator_success = New collaborator has been added. settings.remove_collaborator_success = Collaborator has been removed. settings.search_user_placeholder = Search user... +settings.org_not_allowed_to_be_collaborator = Organization is not allowed to be added as a collaborator. settings.user_is_org_member = User is organization member who cannot be added as a collaborator. settings.add_webhook = Add Webhook settings.hooks_desc = Webhooks are much like basic HTTP POST event triggers. Whenever something occurs in Gogs, we will handle the notification to the target host you specify. Learn more in this <a target="_blank" href="%s">Webhooks Guide</a>. @@ -921,6 +922,7 @@ auths.attribute_username_placeholder = Leave empty to use sign-in form field val auths.attribute_name = First name attribute auths.attribute_surname = Surname attribute auths.attribute_mail = Email attribute +auths.attributes_in_bind = Fetch attributes in Bind DN context auths.filter = User Filter auths.admin_filter = Admin Filter auths.ms_ad_sa = Ms Ad SA @@ -17,7 +17,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.8.40.0219" +const APP_VER = "0.8.41.0221" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/models/cron/cron.go b/models/cron/cron.go deleted file mode 100644 index 8e494e5537..0000000000 --- a/models/cron/cron.go +++ /dev/null @@ -1,59 +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 cron - -import ( - "time" - - "github.com/gogits/gogs/models" - "github.com/gogits/gogs/modules/cron" - "github.com/gogits/gogs/modules/log" - "github.com/gogits/gogs/modules/setting" -) - -var c = cron.New() - -func NewContext() { - var ( - entry *cron.Entry - err error - ) - if setting.Cron.UpdateMirror.Enabled { - entry, err = c.AddFunc("Update mirrors", setting.Cron.UpdateMirror.Schedule, models.MirrorUpdate) - if err != nil { - log.Fatal(4, "Cron[Update mirrors]: %v", err) - } - if setting.Cron.UpdateMirror.RunAtStart { - entry.Prev = time.Now() - go models.MirrorUpdate() - } - } - if setting.Cron.RepoHealthCheck.Enabled { - entry, err = c.AddFunc("Repository health check", setting.Cron.RepoHealthCheck.Schedule, models.GitFsck) - if err != nil { - log.Fatal(4, "Cron[Repository health check]: %v", err) - } - if setting.Cron.RepoHealthCheck.RunAtStart { - entry.Prev = time.Now() - go models.GitFsck() - } - } - if setting.Cron.CheckRepoStats.Enabled { - entry, err = c.AddFunc("Check repository statistics", setting.Cron.CheckRepoStats.Schedule, models.CheckRepoStats) - if err != nil { - log.Fatal(4, "Cron[Check repository statistics]: %v", err) - } - if setting.Cron.CheckRepoStats.RunAtStart { - entry.Prev = time.Now() - go models.CheckRepoStats() - } - } - c.Start() -} - -// ListTasks returns all running cron tasks. -func ListTasks() []*cron.Entry { - return c.Entries() -} diff --git a/models/git_diff.go b/models/git_diff.go index 9796ef59f8..ab70139fb3 100644 --- a/models/git_diff.go +++ b/models/git_diff.go @@ -161,7 +161,6 @@ type DiffFile struct { IsBin bool IsRenamed bool Sections []*DiffSection - HighlightClass string } func (diffFile *DiffFile) GetType() int { @@ -169,10 +168,7 @@ func (diffFile *DiffFile) GetType() int { } func (diffFile *DiffFile) GetHighlightClass() string { - if diffFile.HighlightClass == "" { - diffFile.HighlightClass = highlight.FileNameToHighlightClass(diffFile.Name) - } - return diffFile.HighlightClass + return highlight.FileNameToHighlightClass(diffFile.Name) } type Diff struct { diff --git a/models/issue.go b/models/issue.go index 326454630e..c34c3f5574 100644 --- a/models/issue.go +++ b/models/issue.go @@ -17,11 +17,11 @@ import ( "github.com/Unknwon/com" "github.com/go-xorm/xorm" + gouuid "github.com/satori/go.uuid" "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/setting" - gouuid "github.com/gogits/gogs/modules/uuid" ) var ( diff --git a/models/login.go b/models/login.go index df89050965..cbad646d1e 100644 --- a/models/login.go +++ b/models/login.go @@ -28,11 +28,11 @@ type LoginType int // Note: new type must be added at the end of list to maintain compatibility. const ( LOGIN_NOTYPE LoginType = iota - LOGIN_PLAIN - LOGIN_LDAP - LOGIN_SMTP - LOGIN_PAM - LOGIN_DLDAP + LOGIN_PLAIN // 1 + LOGIN_LDAP // 2 + LOGIN_SMTP // 3 + LOGIN_PAM // 4 + LOGIN_DLDAP // 5 ) var ( @@ -42,7 +42,7 @@ var ( var LoginNames = map[LoginType]string{ LOGIN_LDAP: "LDAP (via BindDN)", - LOGIN_DLDAP: "LDAP (simple auth)", + LOGIN_DLDAP: "LDAP (simple auth)", // Via direct bind LOGIN_SMTP: "SMTP", LOGIN_PAM: "PAM", } diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 78729bdedb..c8e399e15a 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -16,12 +16,12 @@ import ( "github.com/Unknwon/com" "github.com/go-xorm/xorm" + gouuid "github.com/satori/go.uuid" "gopkg.in/ini.v1" "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/setting" - gouuid "github.com/gogits/gogs/modules/uuid" ) const _MIN_DB_VER = 4 diff --git a/models/repo.go b/models/repo.go index 98bb1107b3..089a5998e7 100644 --- a/models/repo.go +++ b/models/repo.go @@ -30,9 +30,9 @@ import ( "github.com/gogits/git-module" api "github.com/gogits/go-gogs-client" - "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/bindata" "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/markdown" "github.com/gogits/gogs/modules/process" "github.com/gogits/gogs/modules/setting" ) @@ -342,7 +342,7 @@ func (repo *Repository) CanEnablePulls() bool { // AllowPulls returns true if repository meets the requirements of accepting pulls and has them enabled. func (repo *Repository) AllowsPulls() bool { - return repo.CanEnablePulls() && repo.EnablePulls; + return repo.CanEnablePulls() && repo.EnablePulls } func (repo *Repository) NextIssueIndex() int64 { @@ -358,7 +358,7 @@ func (repo *Repository) DescriptionHtml() template.HTML { sanitize := func(s string) string { return fmt.Sprintf(`<a href="%[1]s" target="_blank">%[1]s</a>`, s) } - return template.HTML(DescPattern.ReplaceAllStringFunc(base.Sanitizer.Sanitize(repo.Description), sanitize)) + return template.HTML(DescPattern.ReplaceAllStringFunc(markdown.Sanitizer.Sanitize(repo.Description), sanitize)) } func (repo *Repository) LocalCopyPath() string { diff --git a/models/token.go b/models/token.go index 136753c324..adf50a4321 100644 --- a/models/token.go +++ b/models/token.go @@ -7,8 +7,9 @@ package models import ( "time" + gouuid "github.com/satori/go.uuid" + "github.com/gogits/gogs/modules/base" - "github.com/gogits/gogs/modules/uuid" ) // AccessToken represents a personal access token. @@ -25,7 +26,7 @@ type AccessToken struct { // NewAccessToken creates new access token. func NewAccessToken(t *AccessToken) error { - t.Sha1 = base.EncodeSha1(uuid.NewV4().String()) + t.Sha1 = base.EncodeSha1(gouuid.NewV4().String()) _, err := x.Insert(t) return err } diff --git a/models/user.go b/models/user.go index 7a8817591c..3264c0634b 100644 --- a/models/user.go +++ b/models/user.go @@ -29,6 +29,7 @@ import ( "github.com/gogits/gogs/modules/avatar" "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/markdown" "github.com/gogits/gogs/modules/setting" ) @@ -111,7 +112,7 @@ func (u *User) BeforeUpdate() { func (u *User) AfterSet(colName string, _ xorm.Cell) { switch colName { case "full_name": - u.FullName = base.Sanitizer.Sanitize(u.FullName) + u.FullName = markdown.Sanitizer.Sanitize(u.FullName) case "created": u.Created = regulateTimeZone(u.Created) } @@ -641,7 +642,7 @@ func updateUser(e Engine, u *User) error { u.Description = u.Description[:255] } - u.FullName = base.Sanitizer.Sanitize(u.FullName) + u.FullName = markdown.Sanitizer.Sanitize(u.FullName) _, err := e.Id(u.Id).AllCols().Update(u) return err } diff --git a/models/webhook.go b/models/webhook.go index bdfb62d899..c20a72e97e 100644 --- a/models/webhook.go +++ b/models/webhook.go @@ -15,13 +15,13 @@ import ( "github.com/Unknwon/com" "github.com/go-xorm/xorm" + gouuid "github.com/satori/go.uuid" api "github.com/gogits/go-gogs-client" "github.com/gogits/gogs/modules/httplib" "github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/setting" - "github.com/gogits/gogs/modules/uuid" ) type HookContentType int @@ -361,7 +361,7 @@ func CreateHookTask(t *HookTask) error { if err != nil { return err } - t.UUID = uuid.NewV4().String() + t.UUID = gouuid.NewV4().String() t.PayloadContent = string(data) _, err = x.Insert(t) return err diff --git a/modules/auth/auth.go b/modules/auth/auth.go index 7c4cfafcf4..0c1f2fbdc1 100644 --- a/modules/auth/auth.go +++ b/modules/auth/auth.go @@ -12,13 +12,13 @@ import ( "github.com/Unknwon/com" "github.com/go-macaron/binding" "github.com/go-macaron/session" + gouuid "github.com/satori/go.uuid" "gopkg.in/macaron.v1" "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/setting" - "github.com/gogits/gogs/modules/uuid" ) func IsAPIPath(url string) bool { @@ -102,7 +102,7 @@ func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool) if setting.Service.EnableReverseProxyAutoRegister { u := &models.User{ Name: webAuthUser, - Email: uuid.NewV4().String() + "@localhost", + Email: gouuid.NewV4().String() + "@localhost", Passwd: webAuthUser, IsActive: true, } diff --git a/modules/auth/auth_form.go b/modules/auth/auth_form.go index 68a9688303..15dbb3605b 100644 --- a/modules/auth/auth_form.go +++ b/modules/auth/auth_form.go @@ -23,6 +23,7 @@ type AuthenticationForm struct { AttributeName string AttributeSurname string AttributeMail string + AttributesInBind bool Filter string AdminFilter string IsActive bool diff --git a/modules/auth/ldap/ldap.go b/modules/auth/ldap/ldap.go index 7f36c8bd57..8a8cb89b67 100644 --- a/modules/auth/ldap/ldap.go +++ b/modules/auth/ldap/ldap.go @@ -31,6 +31,7 @@ type Source struct { AttributeName string // First name attribute AttributeSurname string // Surname attribute AttributeMail string // E-mail attribute + AttributesInBind bool // fetch attributes in bind context (not user) Filter string // Query filter to validate entry AdminFilter string // Query filter to check if user is admin Enabled bool // if this source is disabled @@ -58,18 +59,10 @@ func (ls *Source) sanitizedUserDN(username string) (string, bool) { return fmt.Sprintf(ls.UserDN, username), true } -func (ls *Source) FindUserDN(name string) (string, bool) { - l, err := ldapDial(ls) - if err != nil { - log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err) - ls.Enabled = false - return "", false - } - defer l.Close() - +func (ls *Source) findUserDN(l *ldap.Conn, name string) (string, bool) { log.Trace("Search for LDAP user: %s", name) if ls.BindDN != "" && ls.BindPassword != "" { - err = l.Bind(ls.BindDN, ls.BindPassword) + err := l.Bind(ls.BindDN, ls.BindPassword) if err != nil { log.Debug("Failed to bind as BindDN[%s]: %v", ls.BindDN, err) return "", false @@ -85,7 +78,7 @@ func (ls *Source) FindUserDN(name string) (string, bool) { return "", false } - log.Trace("Searching using filter %s", userFilter) + log.Trace("Searching for DN using filter %s and base %s", userFilter, ls.UserBase) search := ldap.NewSearchRequest( ls.UserBase, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter, []string{}, nil) @@ -111,6 +104,14 @@ func (ls *Source) FindUserDN(name string) (string, bool) { // searchEntry : search an LDAP source if an entry (name, passwd) is valid and in the specific filter func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, string, string, string, bool, bool) { + l, err := ldapDial(ls) + if err != nil { + log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err) + ls.Enabled = false + return "", "", "", "", false, false + } + defer l.Close() + var userDN string if directBind { log.Trace("LDAP will bind directly via UserDN template: %s", ls.UserDN) @@ -124,33 +125,26 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str log.Trace("LDAP will use BindDN.") var found bool - userDN, found = ls.FindUserDN(name) + userDN, found = ls.findUserDN(l, name) if !found { return "", "", "", "", false, false } } - l, err := ldapDial(ls) - if err != nil { - log.Error(4, "LDAP Connect error (%s): %v", ls.Host, err) - ls.Enabled = false - return "", "", "", "", false, false - } - defer l.Close() - - log.Trace("Binding with userDN: %s", userDN) - err = l.Bind(userDN, passwd) - if err != nil { - log.Debug("LDAP auth. failed for %s, reason: %v", userDN, err) - return "", "", "", "", false, false + if directBind || !ls.AttributesInBind { + // binds user (checking password) before looking-up attributes in user context + err = bindUser(l, userDN, passwd) + if err != nil { + return "", "", "", "", false, false + } } - log.Trace("Bound successfully with userDN: %s", userDN) userFilter, ok := ls.sanitizedUserQuery(name) if !ok { return "", "", "", "", false, false } + log.Trace("Fetching attributes '%v', '%v', '%v', '%v' with filter %s and base %s", ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, userFilter, userDN) search := ldap.NewSearchRequest( userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter, []string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail}, @@ -177,6 +171,7 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str admin_attr := false if len(ls.AdminFilter) > 0 { + log.Trace("Checking admin with filter %s and base %s", ls.AdminFilter, userDN) search = ldap.NewSearchRequest( userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, ls.AdminFilter, []string{ls.AttributeName}, @@ -192,9 +187,28 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, str } } + if !directBind && ls.AttributesInBind { + // binds user (checking password) after looking-up attributes in BindDN context + err = bindUser(l, userDN, passwd) + if err != nil { + return "", "", "", "", false, false + } + } + return username_attr, name_attr, sn_attr, mail_attr, admin_attr, true } +func bindUser(l *ldap.Conn, userDN, passwd string) error { + log.Trace("Binding with userDN: %s", userDN) + err := l.Bind(userDN, passwd) + if err != nil { + log.Debug("LDAP auth. failed for %s, reason: %v", userDN, err) + return err + } + log.Trace("Bound successfully with userDN: %s", userDN) + return err +} + func ldapDial(ls *Source) (*ldap.Conn, error) { if ls.UseSSL { log.Debug("Using TLS for LDAP without verifying: %v", ls.SkipVerify) diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go index bd68feafcf..333f468a1c 100644 --- a/modules/auth/repo_form.go +++ b/modules/auth/repo_form.go @@ -57,7 +57,7 @@ func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) bi // It also checks if given user has permission when remote address // is actually a local path. func (f MigrateRepoForm) ParseRemoteAddr(user *models.User) (string, error) { - remoteAddr := f.CloneAddr + remoteAddr := strings.TrimSpace(f.CloneAddr) // Remote address can be HTTP/HTTPS/Git URL or local path. if strings.HasPrefix(remoteAddr, "http://") || diff --git a/modules/avatar/avatar.go b/modules/avatar/avatar.go index 3d885eb3d4..e9d0c1282e 100644 --- a/modules/avatar/avatar.go +++ b/modules/avatar/avatar.go @@ -16,6 +16,8 @@ import ( const _RANDOM_AVATAR_SIZE = 200 +// RandomImage generates and returns a random avatar image unique to input data +// in custom size (height and width). func RandomImageSize(size int, data []byte) (image.Image, error) { randExtent := len(palette.WebSafe) - 32 rand.Seed(time.Now().UnixNano()) @@ -34,8 +36,8 @@ func RandomImageSize(size int, data []byte) (image.Image, error) { return imgMaker.Make(data), nil } -// RandomImage generates and returns a random avatar image. -// The data should normally be the []byte type of email address. +// RandomImage generates and returns a random avatar image unique to input data +// in default size (height and width). func RandomImage(data []byte) (image.Image, error) { return RandomImageSize(_RANDOM_AVATAR_SIZE, data) } diff --git a/modules/base/base.go b/modules/base/base.go index 45e2151e38..8ba211aa7a 100644 --- a/modules/base/base.go +++ b/modules/base/base.go @@ -4,27 +4,8 @@ package base -import ( - "os" - "os/exec" - "path/filepath" -) - const DOC_URL = "https://github.com/gogits/go-gogs-client/wiki" type ( TplName string ) - -// 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/base/tool.go b/modules/base/tool.go index 811a76960c..bc6ff81a14 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -15,14 +15,14 @@ import ( "hash" "html/template" "math" - "regexp" + "net/http" "strings" "time" + "unicode" "unicode/utf8" "github.com/Unknwon/com" "github.com/Unknwon/i18n" - "github.com/microcosm-cc/bluemonday" "github.com/gogits/chardet" @@ -30,20 +30,6 @@ import ( "github.com/gogits/gogs/modules/setting" ) -var Sanitizer = bluemonday.UGCPolicy() - -func BuildSanitizer() { - // Normal markdown-stuff - Sanitizer.AllowAttrs("class").Matching(regexp.MustCompile(`[\p{L}\p{N}\s\-_',:\[\]!\./\\\(\)&]*`)).OnElements("code") - - // Checkboxes - Sanitizer.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input") - Sanitizer.AllowAttrs("checked", "disabled").OnElements("input") - - // Custom URL-Schemes - Sanitizer.AllowURLSchemes(setting.Markdown.CustomURLSchemes...) -} - // EncodeMD5 encodes string to md5 hex value. func EncodeMD5(str string) string { m := md5.New() @@ -504,3 +490,25 @@ func Int64sToMap(ints []int64) map[int64]bool { } return m } + +// IsLetter reports whether the rune is a letter (category L). +// https://github.com/golang/go/blob/master/src/go/scanner/scanner.go#L257 +func IsLetter(ch rune) bool { + return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch) +} + +func IsTextFile(data []byte) (string, bool) { + contentType := http.DetectContentType(data) + if strings.Index(contentType, "text/") != -1 { + return contentType, true + } + return contentType, false +} + +func IsImageFile(data []byte) (string, bool) { + contentType := http.DetectContentType(data) + if strings.Index(contentType, "image/") != -1 { + return contentType, true + } + return contentType, false +} diff --git a/modules/bindata/bindata.go b/modules/bindata/bindata.go index 395df747f8..729ddf2ad7 100644 --- a/modules/bindata/bindata.go +++ b/modules/bindata/bindata.go @@ -297,7 +297,7 @@ func confAppIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/app.ini", size: 10329, mode: os.FileMode(420), modTime: time.Unix(1455507054, 0)} + info := bindataFileInfo{name: "conf/app.ini", size: 10329, mode: os.FileMode(420), modTime: time.Unix(1455859268, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4297,7 +4297,7 @@ func confLocaleLocale_bgBgIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_bg-BG.ini", size: 75250, mode: os.FileMode(493), modTime: time.Unix(1455584004, 0)} + info := bindataFileInfo{name: "conf/locale/locale_bg-BG.ini", size: 75250, mode: os.FileMode(420), modTime: time.Unix(1455859270, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4317,12 +4317,12 @@ func confLocaleLocale_deDeIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_de-DE.ini", size: 52512, mode: os.FileMode(493), modTime: time.Unix(1455584008, 0)} + info := bindataFileInfo{name: "conf/locale/locale_de-DE.ini", size: 52512, mode: os.FileMode(420), modTime: time.Unix(1455859270, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _confLocaleLocale_enUsIni = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xac\xbd\xeb\x92\xdc\x46\xae\x27\xfe\x9d\x4f\x41\x6b\x42\x21\x3b\xa2\x55\x0e\x8f\xcf\xff\xbf\x1b\x0e\xcb\x5e\x5d\xac\xcb\x39\x6a\x75\x1f\x75\xcb\xb3\xb3\x0e\x45\x99\x5d\x64\x57\x73\x54\x45\xd6\x90\x2c\xb5\x7a\x26\xe6\x0d\xf6\x01\xf6\xf9\xf6\x49\x16\xf8\x01\xc8\x44\x92\xac\x6e\x69\xe6\xe8\x83\x9a\x95\x89\xbc\x23\x91\x00\x12\x40\x16\xbb\xdd\xb2\xac\xfa\x55\xfe\x28\x7f\x9c\xef\x8a\xba\xd9\x54\x7d\x9f\xf7\xd5\xe6\xf2\xe1\x55\xdb\x0f\x55\x99\xbf\xa8\x07\xfa\xdd\x7d\xac\x57\x55\x96\x5d\xb5\xdb\x8a\x40\x5f\xd2\x9f\xac\x2c\xfa\xab\x8b\xb6\xe8\x4a\x4a\x78\x66\xdf\x59\xf5\x69\xb7\x69\x3b\x06\xfa\x45\xbe\xb2\xab\x6a\xb3\xe3\x32\xf4\x27\xeb\xeb\x75\xb3\xac\x1b\xfa\x79\x46\x5f\xf9\xab\x46\x52\xda\xfd\x60\x49\x27\xfb\x41\xd2\xf6\x3b\x4b\x7a\xb7\xcb\xba\x6a\x5d\x53\x6f\x3a\x4a\x7a\xab\x9f\xd9\x75\x75\xd1\xd7\x03\xb7\xf4\x27\xf9\xca\x3e\x56\x5d\x5f\xb7\x5c\xfb\xaf\xf2\x95\xed\x8a\x35\x03\x9c\xd2\x9f\x6c\xa8\xb6\xbb\x4d\x81\x02\xe7\xfa\x99\x6d\x8a\x66\xbd\x17\x98\xd7\xfa\x99\xad\xba\x8a\xb2\x96\x4d\x75\x4d\xa9\x4f\xf1\x63\xb1\x58\x64\x7b\x9a\x84\xe5\xae\x6b\x2f\xeb\x4d\xb5\x2c\x9a\x72\xb9\x95\x61\xbe\xa3\xf4\x5c\xd3\x73\x4a\xcf\x39\x1d\x43\xa8\x4a\x1a\xea\xb2\xe8\x75\x1c\x34\x97\x34\xf2\xa2\xcf\x50\x55\x53\x6c\xad\x34\x7f\x66\xd5\xb6\xa8\x37\x3c\x6b\xfc\x97\xfa\xdd\xf7\xd7\x2d\xa6\xf6\x54\x3f\x69\x0e\x96\xc3\xcd\xae\xc2\x14\x3c\x3c\xa7\xaf\x6c\x55\xec\x86\xd5\x55\xc1\xdd\x94\xaf\x8c\x80\x76\x2d\xcd\x45\xdb\xdd\x00\xce\x7e\x64\x6d\xb7\x2e\x9a\xfa\x6f\xc5\x20\xf3\x73\xe2\x7e\x66\xdb\xba\xeb\x5a\x9e\xda\x63\x7c\x64\x34\xf2\x25\xd7\x43\x29\x6f\x68\x12\x5c\x2d\x9c\xb3\xad\xd7\x9d\xcc\x22\x67\x1e\xe3\x17\xd7\x22\x79\x5a\x93\x64\x85\xda\x2e\xdb\xee\x83\xa6\x3e\xe7\xcf\x51\x95\xd4\x39\xcd\x4d\xfb\x55\x34\xb4\x1e\x9a\x7b\x8c\x1f\x09\x40\x9f\x15\xe5\x96\x66\x78\x57\x34\x15\x4f\xdd\x63\xfe\x45\xf3\x45\xbf\xb2\x62\xb5\x6a\xf7\xcd\xb0\xec\xab\x61\xa8\x9b\x35\xaf\xc1\x63\x49\xca\xcf\x34\x29\x73\x79\x21\xed\xa6\xdd\x87\x55\xa6\xf4\x3f\xd3\xcf\xfc\x54\x7e\x4a\x9e\x2b\x84\xcc\x50\x92\x9a\x1c\xea\x8f\xf5\x50\x57\xd2\x98\xfd\xc8\x76\xfb\xcd\x86\xe6\xf3\xaf\xfb\xaa\x1f\x38\xeb\x94\x7e\xd3\x0c\xc8\xef\xac\xee\xfb\x3d\x4a\xbc\xc2\x47\x46\x8b\xda\xac\x30\x9c\xa7\xf8\xc8\xb2\xdf\xfa\xaa\xe8\x56\x57\xef\x33\xf9\x8b\xde\xf2\x07\x23\xe5\xa1\xe5\x66\x0c\x53\xec\x92\x16\xac\x81\x6c\xd5\x96\xfc\xe3\x29\xfd\xa1\xaa\xeb\xa6\x1f\x8a\xcd\xe6\x7d\xa6\x1f\x0c\x26\x5f\xb2\x04\x43\x3d\x60\x1e\x34\x31\x3f\x1b\xaa\x5d\xcf\x6b\x98\x3f\xaf\xbb\x7e\x78\x38\xd4\x84\xc5\x6f\xf7\x4d\x56\xb6\xab\x0f\xb4\x3f\x78\xaf\xa3\xe5\x57\x97\x39\x4d\xd7\x03\xda\x21\xdd\xbe\x69\x68\x82\xf2\x17\x2d\x4d\x1a\x35\x53\x53\xfb\xcf\x00\x7d\x94\xef\x36\x55\xd1\x13\x48\x55\x94\xf9\x8f\x45\x3e\x14\xdd\xba\x1a\x1e\xdd\x5b\x5e\xd0\xbe\xfc\x70\x2f\xbf\xea\xaa\xcb\x47\xf7\xee\xf7\xf7\x7e\x7a\xb1\xa7\x62\x9b\xba\xa9\xfa\x1f\xbf\x2d\x7e\xca\x57\x05\xe5\xd0\x34\xde\xe4\x17\xd5\x25\x6f\x43\x6a\x2b\x27\xfc\x6f\xd6\xbc\x05\x6f\x86\x2b\x6e\x90\x70\x81\x3e\xfa\x9c\x69\xc0\x57\x19\x2f\x00\xd1\x88\x65\x79\x61\xf4\x0e\x1d\x42\x72\x47\x0b\x70\x7c\x73\xf6\x9f\xaf\x8f\xf2\x53\x22\x7a\xeb\xae\xc2\x37\xfd\x47\x25\xbe\xcf\x69\xb4\xe7\xf5\xb3\x27\x8b\x8c\xca\xda\x84\x3c\x2b\x86\xe2\x82\xfb\x1e\xd6\x9f\x33\x65\x7b\x86\x3c\x6c\x52\x26\xa3\x20\x99\xfd\x90\x2c\xcb\xdc\x16\xa7\x3a\x94\x2e\x84\x3a\xde\x30\x71\xa0\xf4\x30\xb3\xa7\x32\x67\x54\x55\xfe\xea\xcd\x9b\x93\x67\x4f\xf2\xaa\x59\xd3\xcc\xe4\xd7\xf5\x70\x95\xef\x87\xcb\xff\xbe\x5c\x57\x4d\xd5\x15\x9b\xe5\xaa\xe6\x49\xe9\x08\x65\x73\x9a\x25\x19\xe2\x22\xeb\xfb\x0d\xd1\x2e\x60\xc1\xd9\xd9\xeb\xfc\x98\x31\x61\x57\x0c\x57\xe8\xc8\x70\x95\xf5\x7f\xdd\xf0\x44\x85\x06\xcf\xaf\xaa\x1c\xdb\x01\x40\xed\xe5\x78\x5e\xf2\x52\xfb\xba\xc8\x7f\xbc\xe8\x7e\x72\xfd\x2b\x2e\xfa\x76\xb3\x1f\xb4\xe4\xf5\x55\xd5\x60\xa1\x08\x95\xba\x81\x08\xa1\x1d\x2b\x8b\xac\xea\xba\x25\x91\xe4\xe1\x86\x97\x47\xfb\x72\xa8\x15\xa9\x8c\x76\x49\xd3\x0e\xb4\xfc\x39\xca\x49\x15\x75\xf3\xb1\xd8\xd4\x25\x2d\x52\x9c\xc8\xb4\x2c\x12\xcb\x96\xd6\x9b\x4b\x13\x46\xb7\xd7\x98\x22\xda\xbb\x74\x62\xe4\xf7\x16\xf7\x40\xc2\xef\x3d\xbc\xb7\xc8\x9a\x76\x29\xf4\x85\x89\x7d\x59\xf7\xc5\x05\x11\x7e\x39\x88\x3a\xa3\xa3\x7f\x66\xbc\x93\xae\x28\x44\x9e\x40\xf0\x9a\xf0\xe1\x86\x33\x85\x91\xb2\xa0\x13\x00\x64\x4a\x09\x94\x1f\xbb\x51\xb3\x80\x17\x42\xd0\x42\xc2\x64\xcc\x99\x2d\xb4\x61\xe5\xe3\xdd\x6e\x53\xaf\xa4\xe9\x17\x92\x17\x11\x94\x8f\x7a\x9d\x14\x0f\x07\x04\xb3\x3c\x87\x66\xd4\x6b\x26\x78\x79\x72\x72\xa0\xfc\x55\x45\x3b\xee\x6a\xbf\x96\xe3\x6e\xd3\xee\xcb\xaf\x40\x88\x6c\xe5\x22\x1d\xca\xdf\xb6\xd4\x61\x60\x55\x00\x88\x4d\x3c\x26\x82\xc2\xdc\x45\x57\x6d\xdb\x81\x27\x4e\x8b\x31\x05\xbd\xae\x29\x93\x46\xda\x17\x1f\xe9\xdc\x1c\x5a\xd9\xca\x25\x6d\xd5\x15\x57\x4c\x94\x6f\x4f\x2c\x82\x6c\x27\xa2\x3f\xb2\xa5\x2c\x2d\xc5\x5d\x40\x6d\xf7\xb4\x0b\xaf\xa8\x32\x9e\x78\x66\x71\xa8\xca\xb9\x7e\x62\x48\x54\x0f\xa8\x03\xed\xf8\x96\x8e\x63\x5e\xe8\x67\xf8\xd0\xdf\xbe\x7e\xea\x55\x71\x79\x49\xbd\xea\x69\x37\xbd\xcc\x57\x9b\x96\xb6\xe2\xbb\xb7\xaf\x7b\xde\x68\x57\xcb\x5d\xdb\x81\xb5\xa1\xac\x53\xfa\x0c\x69\x6e\xa2\x19\xa2\xd9\x6f\x2f\xe8\xd7\xf5\x55\x4d\x04\x1e\xd3\xce\x25\x78\x7f\x50\x2a\x35\xb1\xef\x69\x09\x8f\x72\xda\x5a\x34\x02\x9a\x32\x20\x00\x8f\xc1\xb0\x8e\xc1\x2f\x09\xc7\xf6\x1d\x6d\xa7\xab\x61\xd8\x59\xcb\x2f\xcf\xcf\x4f\xa5\xe9\x90\x7a\x5b\xdb\x85\xc3\x0c\xac\xc1\x86\x99\xad\x26\x6f\x9b\x05\x90\x64\xdf\x6d\x46\xf8\x43\x63\xb5\x9c\x03\xf3\xc2\x5d\xf8\x96\xff\x3b\x8b\xd3\x83\x79\xee\x89\x8d\xbc\x06\x36\xd1\x1c\x83\x01\x5a\x64\x9b\x76\xbd\xec\x68\x35\x0c\x99\x5e\xb7\x6b\x41\xa0\x24\x23\xb6\xf4\xcc\x50\x82\x67\xe3\xba\x63\x86\x90\x20\x41\xb0\x78\x91\x69\x93\xb4\x3b\xee\xa7\xdb\x25\x27\x9a\x10\xb7\x06\xda\x0e\xf9\x60\xc1\x28\x13\xc4\xc9\xb1\x0b\x5b\x9a\x3f\xa5\xe6\x67\xc7\x34\xab\x20\xe9\x48\xbd\xec\xda\x2d\xa5\x3e\xa7\x3f\x31\x21\xf6\xf1\x98\xeb\x03\x4c\x51\x96\x74\xd8\xf4\x47\xf9\xdb\xe7\x4f\xf3\xff\xef\xfb\x3f\xfe\x71\x91\xbf\x1a\x78\x63\x33\xae\xff\x85\x71\xb4\xd0\x99\x88\xa0\x44\x00\x07\x42\xe3\x7b\xbc\x51\xef\xe5\x3f\x22\xf7\x7f\x54\x9f\x0a\x62\x61\xab\xc5\xaa\xdd\xfe\xc4\xc4\x7d\x5b\x10\x29\xe1\x1c\xc2\x7e\xdd\x16\x67\x55\x53\xd2\x87\x30\x94\x9a\xe5\x88\x8b\x66\x3b\xf6\x52\xf8\xea\xe5\xaa\x6d\x2e\xeb\x8e\xc7\xf3\x4b\x03\xdc\x32\x8e\x9b\x98\x06\xe4\x18\x77\x46\x53\x46\xf4\xa8\xbe\xbc\x89\xa0\x18\xe9\x1b\x4e\x54\xf4\xc8\x04\x87\x97\x4a\xea\xc3\x1c\x9f\x09\x6a\x33\x16\x9c\xd0\xe8\x3a\x9b\xee\x3e\xce\x77\x7b\x79\xc9\x27\xbe\x9d\x55\xda\xc2\x89\xa4\xca\xb1\xe5\x41\x08\xb5\x77\x90\x19\x9e\xe9\x96\x78\xfa\xec\x4d\x5e\x7d\x24\xdc\x65\x1a\xda\xb5\xe5\x7e\x05\x7c\x65\xd8\x23\x26\xfd\x44\x70\x7a\xda\x69\xab\x4a\x91\x25\x90\x1c\xee\x1a\xd3\xb5\x15\x01\x11\xa5\x31\xd2\x4f\x8c\xee\x47\x3a\x47\x3a\xd7\xc4\x0b\x4b\xd2\xde\x4f\x60\x27\x9d\x0a\x25\x78\xe4\x2b\x5a\x70\x42\x0a\xe9\x45\x2f\x9d\x92\x6c\xda\x3c\xb4\x2b\xf6\x24\x40\x15\x25\xf5\xe5\xe2\x06\x54\xac\x67\x5c\x28\xab\xcb\x62\xbf\x19\x62\xbf\x46\x47\x92\xb5\x74\xc6\x32\x9c\xcf\x9b\x2d\x30\xe9\x20\x90\xa7\x1f\x97\x25\x2c\x6c\x88\xdb\x92\xa3\x8b\xd1\x55\x84\x24\x3b\xc5\x88\xd8\x55\x58\x9e\x65\x14\x49\x74\xbd\x4c\x32\x49\xf3\x43\xb3\x6f\x85\xff\xca\x71\x70\x73\x8d\x56\x01\x33\x2c\xf3\x7d\x59\x64\xca\xb4\x2d\x55\x9a\x5c\x7e\xac\x21\xab\x05\x74\x95\x2a\x55\xc2\x64\xba\xf0\x2b\x03\xb0\x10\xd8\xcf\x96\x0d\xbd\x39\xe1\x41\xf6\x41\x56\x93\x39\xe7\xe1\xa2\x05\x66\x24\x69\x95\x3e\xd6\x38\x34\x14\x61\x30\x2f\x17\xcc\xeb\x50\xd3\xd4\x54\x5f\x55\xa8\x81\xca\x7f\x4b\x75\xa2\xcc\x42\x05\x15\x95\x1d\x8c\x01\x65\xe6\xa1\x6c\xc1\x89\xe0\x64\xa2\xd2\x36\xad\x23\x2e\x21\xef\xea\xf5\x15\x51\xea\xf6\xfa\x48\x26\xe5\xfa\xaa\xad\x78\xff\xbc\x7a\xf6\xe8\x3b\xe9\xc7\x9a\xcf\xa9\x50\x88\x4f\xb8\x62\x4f\xc8\x45\x33\xa6\x68\x2c\x5d\x08\x9c\x02\x20\x27\x22\x91\x00\x8d\x65\xd3\x09\x63\x12\x88\x86\xd2\x0a\x9f\xa7\x44\x22\xc2\x48\x69\x93\x6f\xa5\x61\x21\x4a\x2a\x74\x2c\xd7\x2d\xe4\x29\x13\x32\xf8\xe8\x25\x69\xbd\x1f\x96\xeb\x7a\x58\x5e\x32\xe5\xe2\x8a\x9f\x73\x05\xcc\x09\x50\x4e\xfe\x80\xb2\x1e\xe4\x44\xfd\x48\x48\x2c\x7f\xc8\xef\x7f\x54\xb6\xf5\x7b\x26\x49\x4b\xda\x44\xf5\x06\x2b\xa2\x52\x5a\x57\x09\x57\x6a\x1a\x82\xc0\x02\xf6\xfb\x1d\x0e\x4a\xe5\x36\x83\x48\x52\xb6\xd7\x0d\x6f\x3e\x90\x5e\x22\x33\xf5\xaa\xa6\x03\xe3\xa2\x6e\x0a\x3a\x69\xac\x16\x90\xf4\xfb\x84\x12\x6f\x4e\xce\x01\xb8\x6e\x2f\xf6\xf5\xa6\x34\x80\x45\x66\x1c\x29\xf1\xa3\xba\xf8\x9e\xb7\xb7\xa4\x5a\xfa\xb2\x6a\x3b\x3e\xcb\x30\x1a\x2b\x78\x80\xaf\xe2\x83\x50\x18\xe1\x9a\x85\x2a\xc0\xa2\x5c\x60\x81\x78\x1a\x68\xf5\x21\x2f\x32\x83\x04\xb4\xa9\xfb\xe6\xc1\x80\x9e\xae\xf6\xd4\x16\xad\x3c\x27\x53\xc1\x3e\x7f\xf8\x13\xfd\x9f\x31\xbb\x25\x07\xc0\x7a\x3a\xf1\x9c\x99\x4b\xe6\x5e\xb6\x62\xd2\xd5\x04\xc7\xc3\x4a\x1b\x06\xbb\xb1\xfa\xfe\x1a\x0a\xf4\x7b\x41\x5a\x56\xe6\x6c\x68\x59\xab\xaf\xe8\x83\xc5\xc7\xf5\x06\x8b\x50\x0c\x2a\xe3\xb5\x34\x6f\x8c\x20\x47\xb2\x67\x2e\x69\x68\x4c\x4a\x87\xe2\x43\x05\xb1\x30\xce\xf9\x1c\x27\x71\x70\xde\xb2\xdf\x58\xb5\xf5\x3e\xdb\x0b\x17\xdc\x6e\xca\x20\xa9\x61\x37\x10\x35\xaa\x12\xcd\x4c\x84\x09\x88\xde\x13\xb7\xbf\xba\x5a\x06\xbd\x18\x4f\xe4\x50\x7d\x02\xbf\x80\xac\xa8\x26\xe3\x5d\xc2\x59\xd9\xf6\x06\x4b\xcc\x03\x3f\xbe\x89\x2b\xcc\x7a\x83\x55\x4b\x52\xf8\x45\xcb\x33\xfd\xb1\x0a\x50\x4f\x7d\x6a\x5a\x80\xea\x22\x66\x5d\xab\x4a\x15\x25\x94\x25\xba\x19\xcd\x15\xdd\x4c\x9f\x81\xfa\xa9\x52\x0f\x44\x92\x70\x40\x55\x12\x0b\x5a\x4c\x68\x3c\xac\xe5\x57\x8d\x70\xa6\x9e\x4f\xa7\x79\x53\x85\xdf\xfb\xcc\xe0\xde\xa6\xf9\x44\x86\xae\xde\x3b\xa5\xda\xd2\x30\xc2\x94\x6b\x50\xfc\x28\x25\x8a\x5c\xc8\x55\xb5\x63\x86\x65\xdb\x03\x95\x36\xac\x22\xb8\x51\x06\x3e\x20\xd5\xcf\x42\xe3\x09\xcb\x88\x32\x7e\x95\xf5\x2d\x6f\xd2\xe5\x17\x56\xf1\xa4\x26\xf4\x41\xf9\xf4\x7c\x14\x6d\x1f\xf1\xd9\xbc\x7c\xb4\x33\x6f\x8e\x52\xd1\xee\x8a\x04\xd8\x8b\x8a\xd8\x0b\x2d\x56\x2e\x4c\x34\xe7\x65\x27\x81\x12\xfb\x0c\x1a\x4a\xec\x0c\x29\xd9\x76\xe3\x83\x9b\x7b\x28\xa4\x51\x5b\x09\xec\x16\x98\x29\xcf\x73\xcd\xb4\x49\x13\xb6\xad\x98\x7f\x5f\x6e\x45\x33\x28\xbf\xf2\xe3\x2a\xa3\x13\x74\x0d\xec\x17\xf4\x7c\xc4\x6a\x9b\x35\xc4\x1c\xc5\x57\x06\xa8\x06\x4f\xbb\x15\xc2\x52\x7e\x36\x4d\x2c\x51\x93\x6b\x68\xe8\x88\x1e\x4c\xa6\x9f\x4e\x39\xca\x5e\xd8\x59\x20\x6c\x05\xb8\xc3\x9e\x28\x4c\x9c\xc4\xc7\x39\xab\x54\x3d\x94\x72\xba\x61\x54\x0c\xcf\x84\xe6\xc7\x8b\x9f\xee\xf7\x3f\x7e\x7b\xf1\x53\x20\xc7\xab\xab\x6a\xf5\x41\xd0\xaf\x6e\x2e\xda\x4f\x10\xac\xa1\xe0\x21\x99\x9e\xb7\xd8\xfd\x32\x27\x41\xbb\x83\x5c\x47\xe4\x83\x8a\xd1\xbc\x73\x6e\xb2\x66\xd4\x17\xa6\x32\x0b\xd1\xd5\x55\x82\xdf\x11\x1f\xa1\xb4\x63\x8c\xc4\x99\x11\x51\x12\xe3\xd8\xd4\xdb\x7a\x98\xa0\x04\x13\xa5\x42\x51\x4b\x75\x7c\x36\x47\xa8\x2b\x8e\x92\x48\x3b\x55\x43\x27\xb1\xa1\xc9\x75\x41\x82\xdc\xf7\x39\xa1\xc6\x7e\x60\x59\x85\xd5\x23\x03\xd1\xf6\x82\x8f\x72\x12\xe2\x8a\x7e\xb9\x6f\x74\xba\xaa\xd2\x90\xe4\x65\x8d\x13\x87\xdb\x35\x54\x76\x50\xa9\xec\x90\x7f\x1d\x66\xf2\x9b\x85\xaa\xe4\x50\x8a\x4f\x01\xee\x4f\xcd\x8c\x6e\x31\xb7\x26\x44\xef\x9a\x4a\x24\x6f\x8c\x9f\xc1\x78\xf9\x48\x7c\x8b\x8b\x42\x32\xe0\x07\x4a\xc1\x3c\x5f\xec\x87\xa1\x65\x31\x66\xc3\xb8\x20\x65\xac\xcf\x4f\x01\x08\x41\x2f\xd6\x87\xc5\x1c\xcf\x92\x4a\x62\x38\xc3\x7b\xec\x67\xd1\xd7\xb3\x38\x99\x0e\x4d\xcf\xcc\x00\x55\x8a\xfe\xab\x68\x6e\xa2\x6a\x05\x7d\xe0\xe6\x86\xf9\x9e\x7c\xdd\x55\xdf\xc4\xbe\x84\x7d\x80\x12\xda\x1f\x29\xed\xb6\xc8\x5b\x64\x8a\x5e\xd8\x36\x92\x9d\x38\xaa\x5b\x8d\xa8\xd1\xa5\x53\x8b\x7c\xc6\x76\xa2\x99\xc4\x83\x96\x98\x65\x1a\x04\x4a\x2f\x46\x6d\x45\xf1\x71\x3a\x7d\x43\xda\xe3\x78\x28\x0d\x6d\xbb\xec\xaf\x44\xf2\xb7\xee\xe5\x9b\xaa\x59\x27\x2a\x33\xdc\xf1\x00\xdf\xfe\x7f\x92\x8f\x7f\xe3\x81\xbe\xcf\x74\x29\x2a\xb7\x1f\x14\x51\x2d\xc7\x96\x4c\xb6\x45\x80\x37\xce\xee\xd7\xaa\x63\x59\x10\x40\xc9\x5a\x1d\x9a\xc4\x74\x0c\x81\x1a\x46\x56\xe0\xad\xdf\xbb\x9a\x7c\xb9\xdf\x1c\xe5\xd7\xc2\x23\xc4\x32\x41\x0e\x55\xee\x81\xb1\x52\xee\xa3\x68\x78\x6d\x59\xd0\xf8\x6e\xa0\x65\xff\x33\x9d\x49\x0d\x6e\x36\xda\x8c\x32\xa4\xd0\x31\x3e\x08\x94\x05\xe9\xf7\x19\x1f\xfa\x6f\x46\x2c\x30\x1f\x6a\x9a\xe6\xd8\x30\x64\xfd\xe2\x6f\x6e\xc2\x98\x4f\x67\xb8\xe5\xb7\x55\xbc\xc0\xc1\x57\x18\xfc\xd9\xd9\xcb\x73\x93\x8c\xcf\x5e\xe6\x1f\x2a\xad\xfb\xe5\x30\xec\xfa\x77\xd0\xb9\x88\x02\x85\xb5\x2d\xa7\xc5\x0d\xb3\xa6\x92\xac\x3f\x90\x71\x5e\x15\x5b\xed\x24\x7f\x4a\x15\x8f\xe9\xfc\xd5\x44\xfe\xa4\x63\xd9\xe9\xf2\x32\x30\x69\xbf\x24\xbc\xb9\x20\x7e\x10\x94\x2a\xbd\xd2\xf9\x7d\xa2\x7f\xfc\x3d\x2b\x36\x3b\x92\xe5\x98\xe1\x71\x60\x50\xb5\x5d\xa8\x48\x97\x03\x04\x88\xbe\xdf\x12\x82\xac\x20\xc2\x52\x81\xaf\x1f\x2e\xbf\x71\xaa\xd7\xb4\xb2\x92\xf6\xff\x3f\x55\x21\x7f\x33\x27\xed\xeb\xed\xeb\xbf\xd9\x28\x92\xea\x38\x9d\x68\x29\x41\x80\x6f\x8d\x50\x01\x08\x07\x39\xf3\xb0\x03\x6b\xde\x28\x81\xf8\xe4\xa4\xea\x6d\xf1\xe9\xae\x82\xdb\x76\xa6\x9c\x50\xb9\x58\xc8\x88\x99\x0e\x31\xd9\x3d\x04\xce\xaa\xb5\x83\xc0\xb4\xf0\x04\x52\x37\xab\xcd\xbe\x3c\xd8\x91\x7e\x7f\x41\x1b\x89\xf9\xef\x07\xf7\xfb\x07\x5c\x65\xf3\x81\x0e\xed\x26\xc0\xbf\x93\xdf\x39\x7e\xff\x60\x37\x8b\x24\x20\xab\x50\x92\x87\x3b\x46\xe2\x3d\x4a\x3e\x3f\x20\x5c\x2c\x22\xe9\xf1\x02\x47\x40\x7e\x68\x39\x54\x20\x0c\xfb\x9f\x55\x1b\x90\xbd\x08\x01\x17\xf1\x36\x74\xc9\x3c\xc0\x92\x19\xf9\xc6\x73\xde\x4c\x2f\xed\x84\x05\x97\x00\x08\xb9\xfa\x5a\x4e\xcb\x8d\x76\xe7\xc1\xe2\xc4\xe9\xcc\x94\x3e\x99\x2a\xbb\x0f\x94\x1f\x68\x83\xcd\x54\x10\xf6\xdd\xc1\x82\xb2\xf6\x28\x44\x23\x2f\xc7\x84\x63\x5a\x8e\xa1\x16\x71\x96\xc2\x84\xfb\xb5\xf1\x72\x4a\x98\xe7\x54\xac\x64\x15\x0d\xa1\x5f\x87\x5b\x69\x27\x5c\xaa\xb0\xaf\xb4\x7e\xcb\x72\x54\xbf\xe7\xa3\x86\x65\x2e\xe1\xa0\xd2\x19\x65\x26\x02\x55\x55\x68\xe2\x70\xf5\x84\x4f\x4c\x9a\xef\xaa\x1f\x60\x5f\x58\xb5\xd7\x45\x4c\x2b\xd6\xca\x03\xd0\xa1\x6a\x83\xa0\x5c\x7d\xaa\xa1\xd6\x7d\x51\x7f\xac\x54\x54\x0e\x1a\x02\xe4\x2d\xb2\x0d\xed\x7f\x16\xaf\x64\x54\xc2\x6a\xb7\x1f\x79\x47\x71\x7b\x9c\x2b\xe5\x44\xcd\xab\x83\x62\x24\x51\xa1\x1b\x77\x4d\x55\x79\xc4\xf7\x5e\x03\xce\x72\x6c\xd0\x62\x73\x5d\xdc\xf4\x50\x20\x19\x91\x61\x0d\xb9\x14\x67\x0a\x42\xfc\xcc\x1a\xbd\xf2\xf7\x30\xb4\x6b\x6c\x26\xf8\x42\x81\x8f\x8b\xc0\x76\x5c\x43\x6c\x06\x85\x50\x95\xd4\x47\x77\x30\xeb\xe9\xc2\x22\x3f\xcb\xba\x2c\x86\x48\xb6\xab\x08\x17\xa3\x4a\xec\x67\xca\x1e\x31\xaf\x47\xcd\x30\xef\x45\x24\x58\xe6\x9a\x58\x59\x9a\x59\x74\xc9\xe9\x50\xf6\x54\xff\x43\xe1\xdd\x6b\x9a\x43\x16\x05\xa3\x5a\x81\x4f\x23\x5a\x15\xbb\x47\x90\x74\x11\xc6\xfb\xa1\xde\x6c\x78\xa6\xcd\x0e\xe1\xcf\x8e\xf3\xc8\x91\x8b\x7d\x82\x69\xea\xaf\xea\x5d\xde\x42\x9b\xec\xa7\x30\xa2\xad\xe3\x96\xf9\xc6\xa4\x82\x6c\xc0\x5a\xf5\xae\x68\xfa\xcb\x0a\xea\xf5\x6d\x7e\xc9\x17\xda\x0b\x6d\x9a\x99\x6f\xb1\x3b\x38\xd0\xb2\x88\x59\x68\xda\x1f\x10\x58\x3b\xb7\x50\x69\xd3\x72\x7d\x03\x1d\x2e\xfa\x80\x59\x8d\x35\xf5\xd6\x07\x46\xb3\xc9\x14\x80\x07\x4e\x2e\xe3\x66\xe7\xe1\x32\x91\xcf\xa5\x7d\x60\xda\x1d\xe3\x76\x73\xae\xf7\x07\x72\x87\x93\x2e\x12\xa5\x48\xab\xa2\x1d\x65\xc5\x74\x32\x76\x2e\xea\xae\xf5\x69\x87\x54\xda\x0a\x6f\x0b\xde\x29\xa3\x0a\xa1\xbb\x89\x42\x4f\x26\x26\x00\xcb\x0b\xea\xe2\xea\x2a\xd9\x9b\xe7\xc8\xc9\x25\x67\xb2\x3d\xb3\xdf\xb8\xe9\xf7\x99\x18\x01\x2c\x83\xa6\xfe\xa9\x18\x05\x08\xe7\xaa\x9a\xf7\x21\x37\xf5\x3c\x5f\x9f\x58\x11\x51\xc6\xdf\x5a\x92\x0f\x52\xd3\x94\xfe\xa5\x25\xa6\x01\x0a\xf7\x7f\xa7\x2f\xe6\xe5\x9b\x2c\xb9\xc1\x1c\x29\x42\xd4\x3a\x84\xf7\xd7\x29\x6d\x0b\xe2\x5b\xd4\x44\xe4\x86\xe4\x70\xd0\x06\xe8\x66\x9e\xdb\x77\xc6\xb7\xe4\x1d\x36\xca\x99\x7e\x25\x8a\x17\x29\x24\x9a\xb6\xe7\xf6\xad\xa9\x21\x89\xb6\x78\x48\x79\xa7\x9f\x19\xcb\xfd\xdb\x05\xce\x12\x66\xcc\x71\xd9\xe1\x4e\x10\x66\x10\x78\x9d\x2d\x6f\xe1\xe0\x77\xc5\x40\x54\xb4\x11\xf9\x4c\x08\x9a\x2f\xaa\xd9\xa1\x8a\x70\xc7\xce\xb5\xb0\x1d\x8b\xcc\xdd\xfb\x2c\x1a\xd8\x98\x6d\xcd\x9c\x72\x59\x29\x52\xaf\x4c\xf1\x7f\xd0\xa7\xea\x78\x40\xed\xf0\xa1\x42\x3a\x6e\xb7\xed\x4a\x12\xf6\x37\xee\x67\xa6\x5a\xb1\x54\x25\xa6\xfb\xe1\x51\xfe\x4c\x3e\x4c\xdc\xdf\xd7\x18\x53\x4d\xa2\xc3\x0e\x0b\xe5\xcc\x81\x74\xe5\x42\xa7\xd5\x1a\x2c\xea\xf3\xbb\xa9\x94\x2a\x95\x00\xd1\xed\x86\x09\xe7\x3e\x5f\x70\x38\x69\x95\x55\xd4\x10\x63\x1b\x77\x7b\xc6\x77\x42\x2c\x7a\x13\xd8\x75\x75\x91\xb3\xd2\x98\x10\x8d\xa4\x42\x1d\xe7\xb6\x20\x81\xf2\x63\x5d\x04\x55\x93\xe3\xc7\x02\xc3\x60\xba\x22\x08\x3d\xcd\x43\xdc\x6b\xe5\x10\x3f\xe4\x7e\xc3\xd8\x31\x5b\x50\xd6\xa0\x08\xee\x53\xad\xb5\xdc\xc2\x34\x60\xd5\xd8\xe4\xc6\xce\xf3\xe7\x6c\xc5\x04\xdb\x81\xa9\x1d\x1e\x37\xa1\xb7\x51\xaf\xf5\x33\xdb\xef\xf8\x7a\xc7\xcd\xe5\x3b\x24\x84\xb9\x4c\xf3\x9d\x10\x88\x59\xb5\x62\x41\x55\x24\xe0\xa5\x93\x0a\xf9\x8e\x43\xb7\xf2\x8c\x7d\x9d\x6e\xe7\x72\x0c\x12\x15\x3a\x20\x77\x3a\x70\x2c\x94\x5c\x5f\x63\x6a\xe9\x8c\xce\xaf\x68\x17\x6d\xea\xe6\x43\xaf\x2b\xc5\xf3\xe4\x05\x62\x28\xc0\x08\xbf\xf7\x62\x5e\x25\x9f\x53\x6b\x2e\xbb\x07\x1b\x51\x1b\xbb\x2d\x93\x1b\xc1\xc7\x48\x9e\x85\xf5\x37\xdf\x72\x63\x77\x59\x31\xd3\x0d\x02\x6b\xb7\x8b\x34\xca\xb6\xed\x55\xdd\x1a\x09\x1a\xa7\x41\x8b\xa3\x50\x3a\xe7\x01\x42\x97\xe4\xb1\xdd\x69\x62\xbb\x66\x76\x0b\x69\x1d\xc0\xe6\x5f\xd6\x5b\x31\x9b\x7c\x67\x77\x94\x58\x9f\x20\x9b\x20\x1b\x36\x34\x69\xef\xfd\xcd\xcc\x9b\xd6\x6e\x40\x8d\x32\x5b\xe6\x91\xb1\x1f\x32\x03\x60\x1e\x92\xce\x8e\xf1\x43\x2b\xb0\x4b\x86\x3b\xd0\xc4\x90\xc0\xdf\x5a\xc9\xc2\x07\xda\xd3\x6e\x12\x26\xf3\xa9\x5e\x97\x84\x7c\x9e\x59\x97\xff\x06\xf7\x8b\x41\x93\xc1\xbb\x6b\x39\x02\x51\xe1\x3f\x81\x9c\xe5\xe5\xad\xad\x83\x7c\xfc\xa8\xf7\x93\xbd\x62\xe5\xae\xd9\xc4\xca\x0d\x5c\xb1\xbb\x5c\x98\xd9\x12\xeb\x6d\xe5\xb2\x12\xf6\x25\x62\x63\xd3\xe0\xa6\x53\xaa\x70\xe4\x44\x1b\xfd\x57\x89\x49\xac\x59\x04\xa1\x3e\xc8\x3f\x8f\x85\x64\xf2\x3d\x86\xd8\x7d\x86\x7c\x35\xfd\x4c\x28\x6b\x65\x46\x1b\x9e\xf6\xee\x3a\x42\x3b\x62\x26\x52\x1a\x3c\xa1\xba\x09\x85\x05\x81\x6d\x61\x82\x10\x09\x2b\x8d\x5b\xab\xe2\x13\x0a\x5f\x96\x12\xd4\x5d\xb4\x3d\x98\x97\xd7\x64\x3d\x4e\x42\xae\x1c\x2a\xa1\x8f\xf4\x43\xe8\xa1\x8c\xf5\x99\x26\x8c\xf2\x6d\x30\x92\x6d\x0b\x32\x33\x1a\xe5\xac\xec\xc8\xa8\x1b\xb1\x00\x09\x77\x92\x09\x5d\xca\x9f\x81\x50\x11\x3a\x88\x76\xdd\xc8\xd4\xcf\xe3\xd6\x23\x1e\xfd\x92\xea\xe5\x65\x6c\xe9\x2e\xfa\x2a\xa3\x1e\x01\xc7\xe3\xcd\x6e\x09\xe4\x49\x75\x7f\x0c\xe5\x21\x44\xbb\x14\x52\x97\xc9\xad\x01\x2e\x00\xbe\xe4\xa6\x80\x39\x8d\xff\x82\x4b\x82\xa4\xa9\x78\x49\x10\x3a\x39\xda\x61\x93\x51\x4e\xb7\x1a\x65\x80\xe9\x51\x5c\x76\xac\x8c\x62\x73\xe0\x68\xb8\x15\x91\xbb\x78\x7a\x28\x09\x7c\x8f\x62\x02\x0e\x25\x66\xc3\x61\x87\x05\xe3\x4b\x11\xc2\xfa\x89\xe6\x3b\x5d\xf3\xc7\x90\x32\x69\x52\x04\x16\x3c\x20\xb1\x11\xc2\xa3\xab\xd4\xba\xe5\x79\x10\xbb\x81\x60\x13\x37\xb9\xe1\x3b\x52\xd1\xee\xaa\x5e\x5f\xd1\xb8\xea\x2d\x5f\x97\x03\x93\xec\x4e\x36\x4a\xde\xfc\x8b\x48\x54\xbb\x6e\x58\xb5\xc6\x2d\x88\x11\x5c\x38\xb2\x7e\xec\x87\xae\x6d\xd6\x3f\x3d\x6b\x59\x24\x66\x8d\x13\x1f\xab\x3f\xff\xf8\xad\xa6\x13\x19\xe6\x25\x64\x8b\xc9\x17\xf5\xf0\x72\x7f\xf1\xa0\xcf\xd7\x6c\xfa\x8b\x5b\xa1\xc2\x19\x04\xab\xa1\x84\x58\x28\x5e\x37\x61\x5a\x60\x1e\x4c\x7b\xbc\x6f\x37\xb4\x41\xd2\x22\xed\x76\x2b\xcb\x4b\x04\x6c\x2b\x90\xe8\x3f\x6c\x2b\xaa\x06\x33\x57\x75\x3a\x3f\x54\xe1\x22\xa0\x78\x5c\x1f\x5d\x36\xe3\x4d\x13\x3d\x8e\x72\x87\x0c\x8c\x9b\xdf\x66\x70\x07\x11\x94\x38\x56\x0a\x9c\xc7\xb4\x14\xd6\x91\xb5\x62\x53\x0d\x12\xc4\x1c\xae\xc2\x8a\x53\x49\xea\x87\x70\x60\x9c\x66\x2d\x0a\xef\x51\xb1\x82\x5e\x10\xcb\x21\x2f\x9f\x3d\xa6\x5e\x06\x8f\x1e\xba\x07\x74\x1d\xed\x6f\xa5\x68\x32\x76\xa5\x67\x36\x00\x47\xd1\x74\x46\x22\x4d\x1b\xc3\x24\x54\xad\x12\x9a\x66\xbd\xf0\xd4\x4c\x4c\xb2\x84\xa2\x09\x42\x92\x14\xc5\xf4\xfa\x33\xa9\xd9\xa4\xdd\x38\x70\x6b\xee\x33\x28\x1a\xc6\xf4\x18\xd3\x41\x63\x81\xd6\x47\x17\xea\xb5\xea\x78\x90\xc1\xc6\xc1\x51\xa2\x7b\xd3\xea\xf5\x5e\x6e\x89\x58\x13\x12\xe1\x86\x2a\xd9\xca\xdc\x09\x98\x73\x8a\x81\x11\xd4\x46\xff\x2d\x2f\x0b\xa2\x03\x43\xfb\x81\x50\x69\x5a\x04\xe9\x87\x0a\x05\xfa\x62\x62\x91\x52\x97\xc7\x91\x38\x8c\x05\x25\xbd\x25\x3f\x48\x60\x1c\x5d\xd1\x5a\x83\x91\x97\xe8\xbc\x60\x62\xcf\xa6\x30\xa5\xd0\x11\x25\x03\x6a\xc9\x14\xf6\x3f\x71\x6c\x0d\x03\x41\xf4\xe4\x0f\xfd\xed\x97\x25\xa9\xdf\x6d\x16\xa2\xde\xfb\xc6\x91\x4f\x41\x87\xa5\x4c\x45\x18\xe4\x29\x31\x1c\xb0\xe3\x7c\x2c\x15\x9e\x73\x76\xaf\x46\xd1\x6a\x6c\x60\x45\x5e\x68\x22\xf6\x00\x00\x65\xc2\xfb\x30\x11\xf8\x15\xd5\x35\x56\x8b\x1a\x9f\xa8\x89\x26\xd6\x80\xb0\xce\x08\xe6\x95\x18\xa3\xe4\x8f\x4f\x5f\xd1\x81\x11\x1a\xb4\x4a\x7f\x29\x88\x33\x97\x2e\x5c\x8b\xae\x06\x26\x2b\x9b\xcd\x98\xe2\x06\x19\x42\x8a\x9b\xed\x3a\x4a\x62\x8b\x87\x41\x4d\x06\x24\x83\x49\xf3\x65\x8e\xab\xde\xe9\xaf\xa4\x35\xf4\x64\x7c\x56\x85\xa1\x7e\x45\x33\x1b\xb4\xa8\xbc\xb5\x76\x37\x4c\xfd\x9d\xf1\x59\x21\x33\x74\x0d\xfa\x3d\xb2\x7a\x23\x48\x68\x71\x72\xde\xc2\x5d\xa0\x1f\xd6\x61\xa5\x20\x7e\x29\x3d\x19\x99\x5d\xcc\x48\x54\x66\x8b\xcd\x51\x96\x9d\xd5\x93\x8e\xf9\x2e\x3a\xc3\x88\x1f\x55\x06\xb7\x50\x19\x3f\x2a\x87\xca\xa7\xb3\xcd\x06\x8c\x96\xa6\x47\xf4\x26\x97\x63\x50\xcc\x30\xb8\x15\x91\x56\x14\x23\x9c\x89\x35\xd5\x72\x5d\x6d\x36\xb4\x1f\xb4\xf5\x78\x45\xab\x43\x4f\x0c\x16\x14\xc8\x49\xb6\x55\xe4\x6d\x65\x2a\xbc\x02\xd2\x2a\x23\x08\xda\x6e\xb0\x51\x10\xb5\x83\x9d\xd6\x4f\x1f\xbf\x79\x73\x72\x1e\x0f\x69\xde\x07\x4d\x49\xac\xc4\x57\xc1\xfe\x6f\xd2\x2f\xb3\x02\x0c\x0b\x98\x42\x44\x3b\x44\x2d\x71\x08\xce\x93\x29\xab\x9d\x3e\xd7\x2d\x68\x4f\xcb\x7d\x31\x5a\x9e\xf4\xbf\x3c\xb4\x7e\xd9\x6f\xcc\xdd\xbc\xcf\x4c\x8d\x7f\xc2\x7f\x33\x7f\x13\xe2\x6e\x90\xb0\xf5\xe2\x45\x53\xf4\x5c\xa0\x0e\xb4\xe5\xe4\x66\x04\x44\x7a\x5f\x40\xd4\xa2\xb9\x6f\x71\x56\x5c\xe6\xb8\x90\x3f\x62\x45\x6f\xdb\x61\xc3\xf0\xe4\xee\x9b\xfa\xaf\x7b\xb0\x67\x2c\x0e\x11\xf1\x60\xb3\xd2\x8b\x7a\x23\x07\xca\xaf\xe1\x87\xa4\xf3\xd7\xc8\xba\xde\x35\x4e\xbf\x7e\xec\x77\x6c\x95\x4b\x67\x43\xff\xe8\xde\xbe\xce\x59\x5f\xc8\x06\x66\xf7\x7e\x22\xf9\x85\xaf\xe9\x69\xf9\x08\xe2\xa7\x49\x75\xec\x9b\xb7\x12\x35\x63\x30\x55\x02\xde\x6a\x3a\xef\x16\xe6\x77\x13\xdd\xa6\x4c\xfc\x3f\xd1\x26\x3b\x02\xc6\x71\x7c\xad\x52\x37\xcd\x11\xf6\xee\xc7\x62\xb3\x4f\x95\x2f\xdc\x3a\x97\xe9\xbf\xc9\xe0\x3a\x10\xcb\xc2\x16\x09\xae\xa5\x9c\x41\xd8\xf0\x33\x26\x6d\xb8\xdd\x8f\x8c\x9d\x50\x99\xf1\xfb\x2a\x43\x4f\x54\xb5\x3e\xf6\x49\x44\x9e\xd9\xf4\x73\x1e\x0c\xfb\x91\x3a\xb3\x1a\xce\x07\xa8\xd8\x0c\xa2\x56\xcf\xdd\x6a\x32\x69\xc1\x20\xbc\x0a\xf7\x46\x2f\x30\x03\x05\xeb\x57\x5d\x0d\xbf\x04\x49\x67\xc7\xd4\xdc\x39\xa5\x86\xc4\xd8\xee\x19\xe1\x3d\x4d\xd1\x62\x5d\x0f\x24\xc2\xb3\x17\x1c\xec\xd8\x33\x22\x1b\x74\x92\xc1\xa5\x55\xbe\x2c\x65\x52\x94\x0f\x7d\x81\x85\x0a\x8e\x39\x4d\xdd\x01\xfc\xa1\xbf\x67\x4a\x29\xa0\x39\xd4\xf2\xbd\x4e\xbb\xac\x9b\x9a\x37\xfe\x2b\xfa\x43\x87\xba\x08\x00\x29\x9a\x0a\x7b\x8b\x4a\x54\x5f\x24\xd2\x77\xa8\x47\x8d\x04\x75\x55\xd4\x3a\xd0\xad\x8b\x9a\xbe\xeb\xed\x00\xa6\x0d\x09\xf9\x13\x24\xa8\x23\x2b\xf5\x84\x56\xe1\xa3\xb0\x43\xe2\x90\xfa\xca\x52\xbe\x66\xf9\xef\x1b\x03\x34\xe9\x2d\xc0\xa9\x0e\x62\x94\x6f\x8b\xa4\x37\x8d\x7a\xd5\x4e\xbb\x82\x49\x39\x2b\x09\x70\x61\x42\x9d\x2f\xf9\x62\xa3\xd8\xf4\xb9\x0a\x9d\x76\x83\x9f\x5d\xf3\xbd\xb8\x28\xf8\xff\xa4\x9f\xd0\xef\xaf\x8b\xbf\x49\xea\x59\xf8\x01\x34\xeb\x15\xf1\x7a\x55\xd6\xd3\x4c\xac\xae\xd4\x02\xad\xbd\x5c\x8a\x37\x19\x8e\xcd\xf3\x70\x81\xca\x7b\x16\x70\x34\xb7\xdb\xe2\x53\xbd\xdd\x6f\xf3\x00\x88\xa2\x8c\x89\xf7\xd3\x6b\x84\xc5\xfc\x65\xc0\xf8\x12\xfd\xcb\xef\x04\xc6\x35\xdc\x7e\x35\xc0\xa6\x66\x4b\xbe\x19\xb2\x8d\x9d\x58\xaa\x64\xea\x72\x6c\xfe\x95\xc1\xe7\x58\x1c\x2c\x7d\xee\x61\x1a\x69\x5a\xa0\x22\x25\x5b\xb0\xcf\xbd\x20\xb2\x73\xef\x27\x59\x74\xa3\x59\x56\xab\x22\xe3\xb1\x7a\x3d\x3b\x6c\x54\x88\x85\x10\xa6\x88\x4b\x4f\xe1\xe3\x14\x51\x69\x06\x2a\x39\xd6\x94\xb5\x2c\x9c\x9f\xd4\xb7\x2f\x5e\x9d\xc3\x4b\x8a\x70\x52\x54\x6c\xea\x0a\xc6\xa6\xcb\x8b\x50\x27\x9f\x78\x75\xdf\x0b\x27\xd4\xd4\x98\x78\xa6\x46\x33\x4a\x38\x91\xdb\xb5\xb2\x14\x03\xac\xb6\x68\x37\x4d\x30\x66\x34\xfd\x4a\x12\xb5\x20\x27\x42\x21\x90\x5e\xbf\x99\x1d\x5b\xe1\x1d\xf4\xac\xda\x70\xdf\x1b\x97\xcd\x5f\xf5\xea\x56\x53\x6a\xab\x0e\xe4\xed\x65\x26\x04\xd3\xd2\x95\x7c\x5e\x06\x3a\x0c\x07\x2b\x76\x0d\x49\x09\x30\x1c\xcd\x0b\xbf\xee\xde\x40\x93\x36\x0a\xb3\x2c\xbb\x9b\x25\x2b\xf2\xc1\xa5\xec\x6e\x62\x82\x63\xe7\x28\x83\xa6\xd3\x01\x07\x43\x99\x53\xac\xf2\xff\xfd\xdf\xff\xe7\xe1\x53\x1e\xf6\xd3\xa1\xdb\xd0\x97\x72\xcb\x0c\x2f\xcb\x20\x15\xe4\x27\xff\x41\x52\xcf\xb5\x5a\xc5\xbc\x93\xaf\xcc\x7e\x83\x14\x50\x7e\xaf\xda\x77\x7c\x64\xfa\x8b\x29\x42\xa6\x7e\xf3\x4c\x0a\x32\x16\x39\x15\x6d\x48\xdc\xf4\x07\xc6\x5f\xf7\xf5\xea\xc3\x52\xf4\x24\x8f\xf2\xff\xe4\x5f\x39\x1c\xa6\xf5\xcc\x64\x3a\x1c\x88\x2a\x90\x73\x44\x99\xbd\x75\x36\x0e\x1a\xf5\xab\x88\x44\xb8\x48\x79\x80\x1b\xb3\xfa\x34\x40\xf6\xc2\xca\x76\x7b\xb6\x03\x63\x84\xb0\xd6\x4e\x29\x05\x1e\x6d\x9c\xc8\x1c\x9b\xab\x21\xdc\x08\x27\x75\xa0\x79\xea\xae\x38\x38\xce\xb2\x3a\xc8\x8a\x4a\x3f\xb6\xf6\xbb\x28\x68\xc8\x2a\x76\x24\x1e\xee\xe1\xa4\xd0\x13\x62\xe8\x2a\x08\x56\xf4\x27\xa3\x03\x88\x4d\x07\xf5\xaa\x99\x9d\x7c\x87\x02\xb7\xa9\x48\xb7\x8b\x66\xbe\x2f\x2f\xd6\x5a\x11\x24\xaa\x27\xfa\x99\x51\x3a\xff\x3e\xa7\x3f\x13\x37\x7e\x76\xfa\x9f\x3a\xfb\x6f\x8a\x8b\x0a\xc9\xaf\xf1\x41\xc8\x4f\x67\xe0\x40\x2b\x22\x67\x90\xfd\xc8\x78\x4a\xea\x41\x10\x11\x5f\x99\x3a\xb0\xc8\xb5\xb2\x7c\x66\xb8\x28\xeb\x0a\xbe\xdc\x7d\x5b\x5c\xcb\x4f\x9a\x2e\x8d\x06\xf0\x52\xbe\x24\x19\x76\xfe\x02\x0a\x33\xff\x00\x0f\xa6\x59\x77\xc3\xa9\x7d\x67\xd6\x81\xc5\xb4\x23\x96\x33\x0a\x46\x90\xaf\x46\xf9\x97\x22\xfa\x3f\x67\xc1\xdf\xd2\x0a\x50\xf5\xdc\x0c\x11\x43\xfa\x96\xcf\x51\xdc\x26\x1d\xcb\x57\xc8\x29\xc5\xfe\xf7\x19\xb8\x03\x4d\x33\xbf\x8b\x13\xfe\x1b\x52\x09\x3f\x95\x2f\xa4\xbf\xc1\x87\x41\x82\x78\xb0\xcc\x2f\xd1\x0f\x62\xf2\x62\xbc\x16\x2e\xab\x61\x56\xeb\x02\xd7\x76\xb4\xd5\x90\xef\xb3\x57\x34\xff\xdd\x32\x94\x7f\xca\x3f\xf3\xcd\xa4\x96\xb0\xb8\x7e\x6d\x47\xcd\x78\x18\x6a\x6a\x16\x4c\x9a\xf3\x90\xd2\xe2\x76\x0e\x98\xe4\xbc\x26\x81\x3d\xa1\x04\x8f\x5a\x49\xc5\x2c\xa1\x8c\x6a\x86\xd0\x32\x0f\x4f\x07\x26\xbb\xc6\x41\x6c\xd3\xcf\x69\x3f\x1d\x90\x74\xb3\x98\x01\x65\xed\x59\x84\xa3\x81\x8f\x81\x54\xbd\x1b\xe8\xcf\x78\xf5\xe2\xfa\xd0\xd2\x8e\x17\x48\x32\x97\xc4\x53\xae\xaa\xe0\xa5\x03\x20\xf0\x22\x1c\x37\x23\x69\x26\x54\xa6\x8d\x25\xf5\x61\x42\x87\xe2\x82\xb2\x89\x77\xe2\xd9\x0c\x85\x79\xae\x62\x96\x4c\x9d\x65\x2a\x71\xb1\x9a\x93\x2a\x7d\x1e\xb1\x4d\x4b\x61\x89\x65\x22\x02\x7b\xbc\x99\x29\x71\x2b\x46\x8d\x61\x0e\xd6\x3c\xc1\x1b\x2d\x79\xcb\xf2\x46\x08\x8e\x34\x71\xb8\xea\x03\xe5\x94\x6f\x03\xb7\x36\xcd\x59\xb0\xff\x57\xa0\x9f\x1c\x14\x40\x7e\xcc\x82\xf6\x1a\x58\x87\x04\x0e\x3e\xd9\x43\x57\x4b\x55\xa5\xcd\x15\x92\x55\x2e\x97\x17\x37\x5a\x46\xd6\x19\x6e\xb5\x07\x8a\x6c\x99\x93\x87\x58\xa9\x45\x8e\x43\xc2\x4c\x91\x5e\x9d\xfc\xd9\xcb\x7e\x9a\xb3\xe0\x83\x09\x26\x4c\x4c\x9b\xfa\x59\x10\xc6\x52\x80\x9c\xe0\x63\x0e\x44\x14\xcc\xaa\x22\xe2\x53\x40\xdc\x49\xec\x8a\x7b\xb6\x61\xb6\xcb\x0a\x25\x5e\xc3\x4a\xab\xfb\x8c\x72\x6c\xb3\xcc\x74\x55\xee\x13\x8e\x5b\x18\x12\xe3\xe7\x2d\xed\xc4\x02\xd2\xd0\xa4\x04\xef\x24\xac\x02\x81\xc8\x77\x7e\xff\xb7\xef\xde\xf7\xbc\x0c\xf1\xa2\xe6\xb7\x3f\xbe\x27\x39\xfd\xfe\x6f\xdf\xbf\xc7\x0d\xcd\xa4\xf0\xf2\x92\x55\x94\xd3\x1a\x50\xd0\xa0\x77\x5d\xf5\xb1\x6e\xf7\xbd\xf0\x6b\xf8\x8c\xf4\xe1\x93\x2c\xc5\xa7\x21\xdd\xe2\x21\x38\xc0\x68\x87\x97\x21\x2b\xdd\xe1\xcd\x7e\xbb\xd4\x31\xf6\x42\x01\xec\x57\x28\x6e\x33\xb0\x2c\xb8\xc9\xdf\xc3\x6f\x1e\x6e\x5d\xf2\x60\xa9\xf3\xa6\x9e\xf8\x83\xfc\xfa\x09\x03\xe1\xa1\xff\x1e\x5a\x6a\xdd\xed\xce\xb9\xc4\x4b\x60\xee\x3b\xdc\x32\xdd\x54\xc3\x22\xa5\x4a\x16\xf3\x07\x5d\x4e\xb3\xb4\x17\x11\x44\xd7\x0d\xa6\xda\x1e\xbc\xab\x30\x31\x06\xf7\x16\x3f\x47\x99\xb7\x55\xd6\x25\x05\x94\xd4\x46\x2c\x51\xd0\xd1\x5c\xeb\x4c\xc9\x31\xf4\x65\xd3\x24\xed\x85\x3a\xec\xe7\x17\xd6\x22\xfc\x04\x31\xb0\x97\xa1\x9e\x4b\x9a\xf1\x66\x85\x9b\x00\x5c\x96\xf0\x50\xd5\xcc\x57\xa0\xbf\xb0\x89\x5d\xab\xa1\xcc\x4e\xf1\x61\xc9\xa2\x7a\x53\x2f\x8c\x80\x9b\x89\x9a\x52\x13\xcd\x2b\xef\x12\x82\x13\x28\xb6\x79\xe2\xf1\x7d\x19\x27\x25\xa0\x75\xb3\x34\x6f\x0e\x95\x20\x88\x58\xb2\x01\xa3\x8c\x88\xd0\x88\x1d\x96\x55\xf3\x7d\xd0\x51\x32\xb9\x4c\x35\xc7\x4b\xb9\x49\xe7\x85\xf4\xbb\xb5\x2a\xa1\x0b\xfa\x85\xfe\x84\x79\x1d\x19\x42\x59\xff\xb8\x15\xea\x3e\xfd\xb1\x24\x39\x17\x6d\xd3\xc5\x73\x3b\xcd\x5f\xb5\x9b\x36\x9e\xeb\xf8\x35\x06\x10\x4d\xf4\xfd\x72\xc4\x9b\x49\x76\xc4\x6d\xdd\xbd\x9c\x30\x3a\x79\x04\x72\x66\x30\x92\x31\xb2\x20\x4c\x33\x83\x73\x91\x74\x10\x2e\x46\x16\x13\x63\x5a\x8b\x1a\xcb\x01\x34\xa8\xc2\x67\xc1\xe6\x6d\x44\x84\xcf\xf0\xb7\x1c\xcc\xb5\x7b\xbb\x10\xbe\xe5\x77\x17\x1f\x5a\xf7\xe1\x7b\x8e\xf9\xc6\xa3\x84\x2c\x7d\xbd\xe3\x42\xd5\x91\xca\x5d\xd1\x0d\xf5\xaa\xde\x15\x81\x5c\x9e\xba\x94\x4c\x04\x26\xc7\xaf\x7b\xc1\x49\x33\x59\xa7\x5e\x10\x0e\x8b\x41\x91\x0a\x25\x9c\xa2\xd6\x5b\xfd\x3c\x9c\xcd\x98\x01\x0f\xd7\x6d\x1e\xc4\x39\x04\xec\xe3\x13\xa5\xc8\xb9\xb0\xf9\x70\x62\x1f\x69\xf9\xc5\xa8\x5a\xb8\xee\x3f\x82\x31\xe6\xb8\x41\x6d\xe1\x51\xae\x5f\x9a\x9f\x48\x9a\x63\x09\xd3\x46\xde\xb2\x02\x6e\xbf\xc1\xec\xe0\x3e\x59\x7e\x5c\xca\x4d\xa8\x01\x21\xb4\x19\x73\x3f\xb1\x2d\x77\x22\x48\xe0\x33\xb5\x6e\xe1\xdc\x8b\x6a\x55\xc0\x4e\x1b\x7e\x7c\x0d\x6b\xcc\x8b\xd2\x8d\x9e\x40\x38\x44\x8a\xd5\xcf\x66\xef\x3e\x5c\x1d\xd3\xbf\x50\xbd\x69\x51\x46\x33\x75\x51\x0d\xd7\x70\x6f\x81\xb9\x09\x4f\xae\xa8\xdf\xfb\x1f\xfc\xa9\x4e\xa4\xf0\x5b\xb4\xf1\x2d\x1f\xed\xa5\x92\xc5\x3f\xe0\x87\x10\x47\x9d\xca\x11\xe3\x3f\x83\x06\xa0\x0c\xb6\xa8\xd7\xc0\x27\x1a\xf1\xb6\xa2\x46\xc1\x0e\x94\x26\x8b\x0a\x91\xfe\x91\xf5\x01\x46\x85\xf1\x4d\x7b\x81\xcd\x49\x34\xfd\xfb\x90\xae\xf5\xa3\x26\x3d\xf5\xad\x19\x49\xfb\xd7\xaa\xa7\xd2\xff\xf6\xde\x70\x94\xc4\x86\xa5\xa7\xbb\xc0\xcf\xf8\x33\x81\x1a\x8b\xe0\x31\x4f\x74\xe8\x40\xa8\xca\x8c\x57\x4b\xcd\xd7\x13\x9a\x50\x45\xa6\x26\xe8\xaf\x25\x43\x6f\x4b\xfd\x4a\x52\xaf\x77\x55\xc7\x24\x43\x67\x33\x5c\x1a\x2e\x92\xa9\x01\x3b\xdc\xc5\x96\x18\x6b\x42\xce\xf9\xa4\xda\x40\x23\x14\x26\x25\x11\x52\x05\x07\x68\xa3\xfd\x61\x57\xc5\xf4\x2b\x5c\x0a\xcd\xd7\xa5\xb0\xe5\x3e\xfa\x74\xf0\x2c\x52\x21\xe8\xcc\x1c\xe5\xb3\xbe\xd7\xfd\x12\x06\x62\x62\x32\x7f\xae\x56\x5f\x9b\x7a\x35\xe4\x21\xdd\xbb\x37\xec\xba\x76\x2d\x01\x98\x82\x3b\x03\x1d\xac\xfd\x15\x42\xba\x30\xc0\x25\x51\xa9\x6d\x0b\x8e\x2f\x90\x88\xa2\x59\xe2\x32\x04\x43\x4d\xb4\xbc\xc9\x30\x54\xe5\xab\x13\x32\x0a\xd4\x12\xaa\x82\x46\xfd\xf3\x6a\x93\xdb\xf8\xb9\xfa\x02\x09\x10\xa7\x23\xde\xf1\x36\xee\xfe\x70\x5b\xe3\x68\x83\x82\x0f\xdb\xa2\x91\xdb\xcd\x9a\xdd\x91\x58\xae\x16\x7f\x64\xd8\x5a\x0d\x57\x33\x35\x4b\x6d\x23\x92\x02\xe4\x99\xdb\xd9\x40\xd8\x7d\xa3\x1b\x10\xa5\xa0\x39\x64\x14\xff\x5d\xb5\xbc\x0f\x86\x80\xa4\x8a\xc8\xf1\x6a\x3b\x1d\xaa\x27\x59\x8d\x1c\xef\xc9\xb4\x7d\xfd\x87\xfb\xe5\x37\xb2\x89\x61\x73\x35\xb9\xa9\xe2\x44\x19\xb8\x3f\x48\x99\x8a\xd6\x3d\xbc\xf7\x19\x65\xf8\xa0\x60\x20\xfa\x5e\xfc\x9e\x39\x8d\x9e\x3b\xcb\xa2\xac\xee\xb2\x67\x14\x0b\x2e\x77\x5e\xb9\x30\x06\x28\xa3\xca\xe6\x7e\x9f\xb4\xdd\x2e\x69\x6b\x2c\x55\xf2\xa3\xe3\x84\x37\x0a\xff\x1a\xf7\xc0\x24\x9e\x71\xcd\x41\x76\x48\x07\x44\x0c\xc0\x05\x1f\x21\xe2\x97\x2e\x24\xda\x29\x31\x09\x1d\xd4\x43\x49\x6d\x0b\x94\x01\x48\xaa\x1f\x51\xf8\xd9\xc9\x31\xee\x0f\x4e\xd1\x3e\x63\xe6\xbe\xd4\xe7\xc6\x31\x3f\xa3\x01\xb3\xda\x30\xff\xda\xe2\xbf\x7d\x93\x0e\xb2\x12\xa3\x78\xfe\xeb\x33\x42\xc4\x1e\xad\x6a\x29\x2b\xaf\x35\xa2\x72\x4d\x89\x11\x64\x8e\x82\x4f\xf1\x83\x1b\xfa\xf7\x70\xbb\x7d\x58\x96\x0f\x66\x46\xed\xf8\xa7\x30\xec\x91\x25\x9e\x2a\x2b\x46\x54\xd2\xd5\xe4\xd8\xd1\xf9\xb9\x63\x80\x64\x9d\xde\xf1\xd9\xcf\xe7\x34\x33\x1d\x65\x9c\x39\xc1\xdd\xb8\x7a\x3d\xd3\xff\x96\xa8\x5d\xb4\xef\xe1\x0d\x2d\xa6\x8b\x7e\x2c\x23\x56\xde\x65\x8d\x5c\xea\x6f\xed\x60\xb8\x6b\x51\x76\x8e\x68\xf7\xf6\xc0\xa4\x48\x14\xc8\x83\x53\xe2\x58\xe8\x38\xad\x81\x8d\x9e\x01\x9c\x67\xa2\x63\xeb\xff\x95\x8c\xf4\x5c\xf3\x73\x68\x70\x07\x2b\x9d\x5d\xd7\x1f\x6a\x2a\xf0\x27\xfa\x83\xef\x85\x06\x41\xc8\x63\xd0\x03\x6a\x97\xb3\xbf\x4a\xf2\x6d\xac\x9c\xc3\x38\xcb\x74\x1a\xaa\xd1\x5c\x42\x2f\x8a\x3d\xd7\x7e\xc3\x17\x30\x1f\xe4\x34\x6d\x57\x7b\x08\xe9\x37\xea\x7a\xf3\x17\xf8\xc1\xb4\xc4\xd4\x5d\x69\xcc\x3e\xb0\xcc\xf5\xa0\x48\xb5\x90\x06\x15\xc7\xe1\x50\xb8\xd4\xc8\xdb\xba\xc9\x07\xc4\x90\xa5\x74\x9c\x9e\x02\xee\x63\x73\x23\x41\xd9\x64\x4d\x57\x26\x39\xc2\x8b\x3f\x85\xaf\xf5\x8d\x46\x75\x93\x7c\xb3\x0e\x50\x09\x3e\x5e\x2a\xfc\x09\x51\x21\x0b\xe6\x8f\xd9\x62\x8d\xad\x8b\xb1\xde\xaa\x19\x8b\x04\x42\xc7\x81\x20\x58\xda\x12\x0b\xa6\xae\x0d\xd8\x83\x6a\x03\x8c\x14\x4c\x9d\xfb\x9c\x11\xda\xd4\x03\x28\x47\xc4\x18\xe0\xc0\x74\x4e\x59\x6a\x78\x14\x15\x23\x93\xf1\xc4\xbc\x74\x3c\xc8\xd3\x13\x2c\x9e\x83\x58\x46\x34\x32\xb2\x16\x4e\x2c\x85\xfb\x45\xac\xa3\xd7\x49\xee\xdd\x10\xcc\x2f\xc6\xac\x5a\xf5\x27\x47\x7f\x9a\x8b\x8a\x6d\x69\x0b\x99\x2a\x38\xd3\xc9\x57\xcc\x72\xe1\xaa\x94\x9d\x75\xbf\x23\xd8\x55\xdb\x7e\x90\x30\x5f\x17\xf8\x8c\x39\x6b\x8e\x95\x2b\x99\x1c\x15\xf6\x65\x9a\x4b\x12\x4c\xbd\xf2\xd1\xb7\x9f\x70\xc2\x4c\x17\xd5\x17\xed\xc4\x62\xb6\xb1\x59\x53\xcc\x55\x2f\x22\x57\x8f\x3a\x3d\x4d\x2b\x52\x8f\x17\x66\x0b\x3e\xcf\x53\x6c\xce\x43\x2c\x75\xa0\x5f\xc4\xda\x8b\xf2\x23\x53\xcf\x32\x89\x50\xae\x69\x33\x9d\xe1\xa5\x0b\x56\xa5\xe2\x14\x05\x82\xc0\xb6\xa8\x30\xee\x37\xca\x0d\x5c\xb7\x1d\xec\x2a\x20\x2a\x4e\x48\xc4\x36\x18\xc5\x66\xa9\xb4\x84\x0f\x06\x4b\x43\x75\x11\x3c\x01\xd5\xa8\xaf\xbf\x18\x28\x70\x90\x03\x8e\x1c\x06\xb7\xbe\xfe\x9a\x04\x4b\xbc\xe0\xc9\x10\x1b\x01\xd1\xd8\xb3\x8d\x03\xe2\x42\xd3\x3c\xdd\xa4\x01\x85\x88\xf7\x71\xd3\x25\x64\x76\x34\x03\x1c\x6c\x6f\x60\x83\x53\x36\x50\xbd\xae\x60\xa6\x2a\xea\xb9\xa1\x2b\x38\xc2\xf8\x81\xe1\x03\x66\xa9\x30\xe3\x79\x38\x50\x81\x26\x60\x6c\x81\x25\x08\x33\x82\xfb\xa9\xfc\x5c\x6b\xe4\x51\x3d\x07\xcc\x6d\xe5\x7d\x18\x48\x16\x08\x98\x73\xf5\x77\x56\x22\x41\xfe\x9d\x8d\x8b\xfe\x91\xff\x9d\x91\x88\xfe\xd4\x4d\x59\x7d\xfa\x87\x49\x94\x21\x48\x26\x23\xe8\xd1\xc4\x1e\x51\x58\x55\xee\x19\x8a\xb9\xe9\x04\xbf\x3d\x9a\x4d\xcf\x1c\xf7\x66\xe4\x4c\x08\xaf\x0e\xac\x7c\xa2\x75\x35\xd1\xaa\x74\xdb\x97\xbc\x0d\xba\xe5\xdf\xe4\x56\xea\x19\x7e\xe5\xff\x8b\x4f\xfc\x00\x82\x10\xff\x88\x05\xc1\x92\x62\x2f\xb6\x46\xea\xd3\x2e\xfe\x88\xa2\xe5\x2c\x82\xe6\xb1\x4f\x6d\x38\x52\x4a\x17\xe3\xac\x89\x43\x63\xd1\x88\xfb\x96\xf8\xa8\x3a\x7a\xc4\xe2\xf4\x10\x04\xeb\x21\x3f\xe7\x90\xcf\xeb\xfd\x86\x18\x3f\x67\xc6\x33\x2e\x30\x5e\x16\xab\x47\x59\x04\x98\x81\xf0\xe4\x70\x64\x31\xd4\xe5\xb6\x77\x30\xe8\xd1\x38\x39\x1d\x07\xe9\x14\xcf\x8f\x71\x2b\xec\x84\x46\xc7\xfb\xf2\x3b\x6a\xea\xa1\x3a\xf7\xa7\x36\xba\x49\xc3\x6e\x36\xb4\x0f\xd0\x32\xcc\xf5\x42\x34\xf9\xa1\x0f\x62\xaa\x3b\xd3\x83\x18\xe7\xd7\x8c\x75\x55\x03\x91\xd0\x6b\x83\x16\x8b\xf2\x91\x69\x57\xe4\x5a\x05\xca\x02\x86\x49\x97\x70\xed\x97\x7a\x5e\xfa\xed\x20\x31\x08\xd8\x18\x43\x3f\x4f\x2c\x8a\xc1\x14\x2c\x08\xbf\x31\x74\x41\x3a\x29\x3c\x17\x8a\x07\xd8\x10\xba\x48\x69\xd8\x0c\xe6\xdd\x42\x88\x6d\x55\x70\x43\xe9\x0e\x0b\xf7\x7e\xa6\x7b\xa3\x65\x62\x9c\x90\xa8\xd8\x40\x3c\x61\xe2\xea\x4b\x87\xc3\x70\xa5\x60\xd7\x88\x8f\x75\x49\xb2\x32\x3a\x73\x5b\xbd\x7f\x4c\xeb\xa5\x79\x84\xa5\xd7\xc1\xba\x47\x03\xc2\x0e\x0f\x4f\x2d\x20\xfa\xc4\x65\x0c\xaa\x32\x3b\x22\x26\x3e\x41\xbb\xae\x3b\x09\xc1\x51\xf2\x18\x52\xc0\xf3\xbb\xc2\xcc\x02\x3f\xc4\xdb\xca\xb0\xf4\x07\x47\x04\x46\xbe\xf4\x40\xe7\xb9\x3d\xa6\xd6\xdd\xba\x9a\x27\x74\xd8\x89\xef\x88\x14\x2b\x92\xa0\x1d\x83\xbb\xe9\x12\x7b\x6f\x36\xf3\x09\xbb\x9f\xa5\x13\x42\x39\x84\x8d\x18\xd7\x7f\xeb\xe6\xba\xd3\xac\xfd\x70\x7d\x7f\x3c\xb8\x59\x9d\xf1\xb9\x8d\x86\x97\x81\xf7\xbf\x3e\x47\x61\xfa\x2c\x3f\x44\x31\x25\xc5\xeb\x18\x6c\x54\x57\x0c\xc5\x91\x0a\x14\x47\xe1\x36\x51\x22\x00\x38\x3f\x85\xc8\x68\xb5\xc9\x0a\x8f\xfa\x0a\x33\x39\x99\x80\x57\x93\xa6\x63\x60\x82\xa3\x68\x63\x6d\x67\x34\x64\x09\x3e\x31\x76\x1c\x22\x9c\x6f\xf3\x2e\x45\x72\x14\xe9\xe5\x8e\x26\xff\x78\x5b\x93\x62\x17\x3e\xd3\x66\xf0\x90\xd0\xa0\x40\xa0\xf6\xfc\xba\xc8\x1d\xad\x7d\x6f\xad\x79\xe1\xeb\x43\x55\xed\x5c\x13\x69\xf7\x9d\xbb\x28\x38\xb8\xd4\x52\x7b\x86\xae\xe8\xf9\x67\xf1\x61\x92\xde\xa4\xc2\xdc\x1c\x65\x9c\x97\xe6\x0e\xf1\xa3\xf3\x95\x19\xd7\x7d\x87\x5b\xfb\x74\xaf\xdb\xed\x21\x9e\xd6\xc1\x0d\x62\x80\x61\x09\x6e\xe9\xa8\x30\x3c\x7f\x8c\xbc\xce\x54\x35\x4b\xff\x63\xdc\x99\xd0\x35\x2b\xd0\x1d\xee\x5e\xea\x34\x92\xcf\x38\x8b\x38\x2e\x99\xa3\x2a\x46\x7c\xcf\xc5\x0b\x93\xc7\xf3\xd4\x25\x1f\x2e\x30\xf2\x7e\x4c\xea\x4a\xbd\x1f\x5d\x07\x05\x17\x0f\xd5\xf3\x74\xb6\x0e\xc5\x5f\x57\x8b\x58\xc8\x49\x34\xe6\xd4\x16\x49\x4d\xe6\x70\x36\x2c\x16\x09\x93\xda\xb1\xf1\x23\xc7\xff\xd1\x38\xae\xfa\xbe\xd4\x38\xf4\x8f\xe6\x5e\x5f\xb5\xee\xa4\x17\x27\x4e\x30\x4f\xbe\xeb\x8b\x74\x76\xae\x45\xf0\xd2\x99\x54\x31\x6c\x24\x9f\x05\x9d\x81\x0a\x69\x50\x1b\x6f\xf7\xd4\x67\xe8\x08\x20\x8b\xe9\xcb\x12\x27\x67\xe7\xb8\x42\xa2\x8d\x47\xa7\xe8\x9a\x0f\x9d\xfc\x4f\xc4\xd1\x23\x3c\x39\x3f\xf0\xa0\xd4\x6f\xb5\x62\xc7\xeb\xba\xd1\xe0\xcd\xd7\x95\x79\xc4\x35\xe5\x46\x68\xa5\x77\xcb\x37\xd1\x49\xae\x92\x72\xbc\xba\x80\xa7\x63\x76\xd5\xaa\xbe\x24\x46\xeb\x35\x4d\x61\x83\xa7\xb6\xc2\x3b\x3f\xb7\x3a\x7e\x84\x91\xc0\x30\x96\x6f\x9c\xdc\xb4\xe8\x94\xf8\xab\x58\x3d\xc8\x26\xd3\x33\x06\x9d\x73\x41\xb3\x19\xbe\x4d\x8b\x04\xa2\x2e\x8e\x58\x35\x9f\x11\xb9\x1a\x6e\xde\xe6\xdf\x7a\xb0\x0f\x3e\x78\xb6\x34\xfd\xb9\xe4\x47\xab\x5a\x20\xda\x7a\xe8\x0b\x87\xb1\xeb\xe1\x93\x85\xdf\x77\x80\xdb\x14\x9c\x49\x94\x58\xd8\xe7\xb0\x29\xb0\xa2\x45\xa8\xd5\x42\xb7\x83\x8b\xb0\x39\xea\xa7\x92\xee\x6c\x1b\x71\x88\xe8\xda\xf5\x78\x9c\x82\xfb\x72\x15\x24\xcd\x91\x1c\xb3\xaf\xf0\xbc\xc6\xb6\xb8\x91\xf8\xe0\x7c\x63\xd3\xd3\xf1\xd6\x94\xbd\x3d\x1c\xc5\x8f\xdb\x91\xdc\xce\xca\x20\xf3\xda\x98\x2c\xc9\xb4\x6f\xf1\x2e\xc3\x2e\x30\x66\x40\xfa\x5d\x2b\x6e\x38\x6f\xf5\x73\x0a\x24\x2a\x5a\x1e\xd5\x4b\xf9\x9a\x82\xec\x34\xbe\x67\x88\xf4\x39\x05\xb9\x68\x4b\x5e\xb3\x27\xf4\x67\xaa\x4d\x09\xaf\x5d\x99\x4a\x05\x7b\x79\xc7\x31\xa6\xc4\x02\x8e\x33\x08\x3b\xab\xcd\xa5\x04\x0b\x63\x99\x07\x3a\x66\xb9\x5c\x64\xff\x35\x89\x91\xcf\xfe\x56\xa8\x40\xe7\x09\x0e\xc3\x88\xf7\xeb\x6f\x0e\x35\xa2\x8b\x8f\xa5\x31\xee\x13\x2c\x2a\xac\x5f\xaf\x84\x5f\xc5\x6a\x42\xa3\x2e\xa1\x99\x8f\x98\x03\x60\x85\x86\xd9\x38\x19\x9f\xb0\x93\x70\xcc\x1c\xc5\x84\x68\x00\x02\xf0\x19\x88\x30\xfc\x62\x39\xee\x5c\xc9\xec\x89\x1c\xec\x4d\x4c\xd8\xb4\x47\xea\xfa\xc7\x13\x24\x4e\x7f\x13\x88\x68\x81\x05\x20\x73\xb2\x1f\x1f\xe4\x0a\x1e\xf5\x4a\x2f\x13\xf2\xe1\x08\x70\xf2\x0c\x19\x3a\xaa\x61\x96\x45\xee\x67\xc2\x6a\x62\xbe\xbb\xa0\xe5\xb9\x62\xf5\x82\x23\x86\xcc\x4d\x11\x03\x2b\x52\x32\xc9\x5e\x45\x57\x5a\x58\x42\x25\xcc\xec\xc0\x0c\x02\xec\x63\xbf\x14\x9b\xbe\xb5\x2a\xe8\x20\x21\x90\x0f\x6c\xb2\x4d\xeb\xcd\x7c\xa8\x5d\x72\xb0\x64\x11\x35\xc3\x4c\x8b\xf7\x3b\x26\xcf\x42\xeb\xad\x1d\x0c\xf9\xeb\x7f\x3f\x3b\x79\x73\x94\x7f\x7a\x78\x7d\x7d\xfd\x90\x8b\x3f\xdc\x77\x1b\x0e\xab\x50\x72\xd8\xc3\xff\x79\xfc\xfa\x28\xaf\x86\xd5\x37\x8b\xfc\x58\xa8\x76\x24\x86\x7a\x1d\x0a\x53\x07\xdc\x2d\x12\x81\xf8\xe7\xa9\xb9\xee\x18\x55\x5a\xf9\xf8\xb8\x9e\x03\xe1\xd5\x33\xc3\x54\x5d\x4c\x31\x50\x75\xe7\xf7\xaa\xab\x60\xd7\x89\x0f\x97\x41\x87\xf9\x87\xb9\x20\x53\x63\x90\x9a\xda\xd1\x6e\xbc\x5a\xe9\x53\x49\x23\x10\x33\x63\x7a\x0a\x03\xa6\xa8\x4f\xe3\x85\x0b\xa7\x30\x2b\xc8\x88\x4a\xb1\x8e\x3e\x39\x60\x2e\x2a\x5b\x88\xaa\xfc\x79\x5c\x18\xde\x18\x78\x9d\xe4\x51\xfe\xef\x7c\x3d\xc5\x0b\x25\xb8\xc5\x59\x86\x5b\x00\x5e\x8c\x0b\x23\x72\xb6\x13\x5e\x68\x00\x12\x0f\xdc\x84\xa7\x98\x17\x04\xa8\x49\x25\xaa\x9f\x61\x83\x50\x22\xc2\x41\x5f\x03\x5c\x93\xea\xa6\x45\xd2\xcb\xc1\xf9\x6c\x9b\x17\x71\xc4\x38\x52\x17\x0d\xbb\x39\x9b\x9b\x87\x5c\x5c\x51\x66\xa7\xc8\xd1\x47\x80\xaa\x56\x65\x2a\xbb\x0a\x61\xca\x35\x4c\x7c\x35\xce\x70\x81\xb9\xaa\x01\x91\x8b\xe6\xf6\xa2\xa8\x1f\xc2\xaa\xc5\xdd\x63\xf4\x4d\x4f\x1f\x61\xe4\xc4\xeb\x3b\xa1\x1e\x20\x1d\x29\x53\x3b\x7f\x18\x2e\x26\xb4\x29\x72\x7e\x4a\x9b\x26\xdc\x8d\x02\x8e\xda\x98\x30\x15\x2a\x7e\x4c\x45\xb3\xd8\xc2\x21\xfe\x49\xcc\x8f\xed\x5c\xb7\x38\x89\x08\x9d\xf1\x2c\xa4\xa5\xdc\xa8\xed\x52\xd0\xdd\x74\x8b\xf2\x84\xc8\x3e\xf2\x14\x95\xf9\xda\xd4\x58\x91\x41\x10\xef\x85\x5d\x43\xcd\x0b\x6d\x12\xed\xc6\x9f\xf4\x52\xab\xc5\x2e\x90\x18\x0b\xa3\xcc\xf1\xdb\x70\xe3\x9d\x4d\xac\xad\xbc\x4b\xfa\x54\xbe\xfc\x6c\xed\x36\xed\x8d\x05\x04\x7a\x86\x5f\x1a\xde\xd0\x8f\x2c\x82\xe9\xa0\x22\xe4\x5c\x5d\x91\x15\x05\x14\x6a\x87\x8e\x8b\xb5\xaa\x0f\xe5\xfd\x26\x2c\x29\xab\x29\xa9\x4e\xf3\x12\x0c\xe2\x76\x91\x46\x4f\x90\x97\x81\x34\xae\x8e\x9b\x1a\xf8\xa1\xf9\x01\xfc\xd9\xbd\x2a\xa0\x32\x48\xc3\x5a\x90\xd0\x0d\x2f\x9a\x26\x17\xfb\x73\xa3\x98\x06\xb2\x09\x50\xe3\x80\x3b\x71\xa4\x07\x03\xee\xf8\xa2\x3e\xea\x8e\x2b\x8a\x73\x33\x4c\xc2\xec\x5d\x5a\xb2\x2c\xd3\x98\x3a\x71\xa8\x9f\x11\x56\x67\x7e\xe5\xc6\x82\xc7\x9d\x4b\x7d\xdb\x45\x76\xe9\x07\xf7\x19\x01\x76\x46\x4a\x8f\xcf\x91\x41\xe6\xfa\x12\x27\xc5\xcd\xee\x5d\xd7\xda\x65\x7d\x79\xb9\xb8\xe8\x88\x05\xe7\x28\x36\x78\x8e\x8d\x29\x3b\xff\xce\xcf\xf0\x5b\x40\xd8\x9c\x11\x58\x21\x1f\x92\xa8\xe6\xd7\x8f\xd4\x24\x4f\x12\x61\x4b\x36\x7e\x05\xea\x19\xe5\x88\x5d\xd9\x1b\x42\xf9\xc7\x96\xb3\x90\x22\x2c\x02\x2c\xf9\x0b\xf1\x77\x70\xf5\xc9\x97\x79\x28\x74\xc6\x29\x0e\xac\xdf\x6d\x88\x7b\xd5\x27\xc7\xce\xf8\x07\x5c\xea\x1c\xc4\xbe\x21\x39\xb6\x2a\x0d\xe6\x9d\xfc\xf4\x50\x5c\xa5\x2d\x9d\x9d\xa8\xf0\x20\x10\x0b\x3e\x61\xbd\xa3\x66\x12\x18\x6a\x70\x04\x46\x68\x55\x83\xb7\x8e\x20\x3e\x5e\x07\x41\xd8\x9a\x44\x08\x9d\x68\x10\xac\x27\xaf\xde\xc8\x4f\xb8\x05\x6a\x3c\x4e\xf8\x05\xb2\x21\x61\x66\xce\x86\x8b\x39\xa7\x43\xcb\x13\x5f\x51\xd1\xa4\xd9\x3b\xd1\xf8\x15\x20\xca\xae\xb8\x84\xdd\x0c\xff\x0d\xa9\xc4\xbf\xc7\x62\xa7\x5d\xf5\x70\x5c\x8c\x26\x47\x96\xec\x0c\x1f\x21\x5d\xed\x5e\xf8\x4f\x48\x2b\x60\xce\xfa\xc8\x8d\x3c\xce\x88\xd9\x4d\x12\xfe\xde\xef\xf3\xbe\x66\x5d\xb3\x22\xfa\xa8\x41\x60\x59\x7c\xbc\x03\x38\x08\xef\x51\x3f\x56\x6f\x50\x83\x90\x5d\xfd\x55\x1e\xe6\x87\x1d\xac\x07\x09\xc7\xa3\x2f\x0d\x2e\x92\x7e\x27\xa5\x85\x3d\xa8\x6c\xb5\xf1\x5c\x25\x73\xc0\x08\x66\x21\x51\x14\xf7\xfc\x34\x23\x4d\x04\xc7\x8c\x65\x6e\x29\x6c\xa2\x7a\x4b\xf5\x7f\x94\x77\x7f\xa4\x7a\xe2\x7c\x42\xb8\x20\x62\x82\x1a\x89\x58\x62\x79\xd0\x9e\x58\xec\xe4\xa4\x4c\x7c\x5d\xc4\x6e\xd1\xa2\x97\x2e\xe5\x83\xab\x5a\x79\xe7\x5f\x66\xb1\x38\x7c\x9a\x8c\xdd\x75\x20\xa1\xe8\x96\x3a\xa5\xe2\x96\x23\x37\xd4\x6a\x95\xe1\xd1\x42\xb7\xcb\xa9\x7c\x85\x1c\xe6\xde\x85\x05\x7d\x2d\x5f\xf2\xee\xf5\x18\x9b\xa6\x01\xad\x28\xef\xe1\x78\xad\x1d\x7c\x98\x80\x3f\x55\x0f\x58\x81\xdf\x12\x6f\x90\x8b\x6d\x48\x31\x24\x98\x62\xf1\x0a\xe2\x23\x92\x0f\x71\x3c\xc4\x6e\x8c\x2d\xa2\x42\x73\x8a\x28\x11\x65\x26\xd8\xce\xb6\x26\xb6\x53\x60\x6c\x92\x6e\x17\x60\x4f\xdc\x30\xb0\xfa\x9a\x6c\x34\x61\xbe\x22\x54\x7a\x0b\x33\x03\x2c\x47\x8d\x66\x45\xb5\xeb\x18\x66\xfe\x74\xb1\x76\x7c\x20\x21\x31\xfe\xec\x44\xe9\x11\x2e\x34\x08\x65\x6e\x39\x4b\x26\xad\x79\xdd\xb7\x34\x71\xc7\xe1\x31\xde\x03\xa9\xb5\x8c\xab\x47\x8f\x78\xb6\x7e\xd2\x3d\x32\x39\xe2\x43\x6f\xf4\xe1\x3f\x1c\x63\xf6\x9d\x65\xbf\xb5\xdd\xfa\x7d\x7c\x34\x22\xa8\x72\x93\x07\x23\xa0\x39\x60\x98\x10\x28\xf9\x00\x60\x0c\x9e\x1c\x6b\x34\x74\x7c\xc1\x9b\x6e\xfa\xea\xb2\xe8\x6d\xe4\x6d\x1f\x58\x60\x59\x34\xa1\x85\x79\xef\x4b\xbc\x7a\x35\x8d\x4a\x1e\xd2\x87\x53\x7d\x34\xf9\x79\xa7\x9e\x7f\x6a\x32\xc2\x8e\xdf\xfc\xc1\x4f\x0a\xf0\xc3\xdf\xac\xb4\x95\xdb\xfc\x57\x48\x20\x92\x88\x04\x3c\x79\x21\x1a\x38\xfa\x9b\x21\x4c\xb9\xaa\xa9\x39\x55\xbf\x34\x7d\x14\x0a\x3d\x09\x5d\xee\xa2\x0d\xe0\x81\x84\xc4\x9e\x8b\x2b\xc7\xac\xcc\x58\x7a\x86\x37\x37\xb4\x13\x32\x85\x48\xbd\x0d\x3a\x09\xd5\xc3\x7b\x5d\x6c\x84\x79\x57\x17\x62\x35\xa7\xee\x9e\x8a\x22\x78\xb4\xa1\x49\xdc\xa2\xfa\x45\x6c\xc6\x51\x8e\x2b\x31\x03\x8d\xc5\xf0\x82\x2b\x5b\x98\xfd\x2c\xf0\x49\x84\x0e\x15\xe6\x0b\x09\xb5\x25\xc9\xf9\x86\xe4\xc2\x4d\x22\xdd\xa3\x22\xe6\xa7\x7f\x3e\x10\xaa\x7d\xfa\x48\xc9\x97\xc7\x67\x99\xd6\x71\x7b\x84\x96\x7f\xd6\x2a\x6c\x3e\x64\xb8\x57\x61\x8e\x62\x87\x87\xac\xb9\x20\xe2\xff\x8c\x19\x57\x0a\xea\x88\x4c\x32\x05\xa1\xa6\xcf\xbd\x63\x53\xeb\x30\xc2\xd4\x7f\xcd\x38\x2c\x7d\xd6\x63\xdc\xeb\x49\xb4\xeb\xa4\xd3\x5f\x16\xf5\xfa\xe0\x6d\x7d\x42\x2b\xc6\x22\xfd\x24\xd4\x1c\x06\x78\x6b\x91\x34\xf0\x9c\xef\x70\x50\xe2\xba\xdb\x72\xbd\x38\x93\x90\x73\x72\x91\x73\x77\xdc\xb9\x03\x77\x89\xb7\x05\xa0\x1b\xf7\x92\x69\x4c\x70\xfa\xf5\x9d\xbc\xb5\x84\xe7\x32\x52\xd3\x92\x7f\x25\x28\xdd\xfc\xed\x1b\x8b\xfc\xd7\xa6\xe9\x04\x57\x62\xf3\x17\xf5\x47\x2c\xbe\xd9\x7c\x89\x7c\x17\x09\x6d\x9c\x39\xf0\x93\x32\xb9\xa3\x67\x64\x94\x6a\x2f\xe2\x2b\x24\xcb\x24\x10\xdd\x71\x7c\xe7\x24\xc6\xa4\xfb\x21\x14\xd3\xbb\x71\x8b\x62\x3b\x4a\x8f\x94\x12\xf6\xc2\x7a\xfb\x1f\x81\xe4\x37\x78\xbe\xd9\x9c\x71\xf9\xb4\x0d\x7d\xf7\xb2\x6b\x37\x55\xe8\x68\xfe\x96\x7e\xc5\xee\xa5\xde\xb3\x69\xc1\x50\x26\xa4\xab\x98\xac\x4f\xfb\xc4\xde\xc8\xb3\x2d\xf0\x6b\x77\xa9\x7a\x5a\xba\xb5\x12\x3e\x59\x6b\x87\xd8\xf1\xc3\x18\x5a\xde\xd1\xd4\x73\xf5\x0d\x3f\xc6\x81\x43\x75\x01\x67\x5c\x79\x4a\x44\x53\xd2\x46\x25\x8d\x39\x16\x8d\x85\x9a\x4b\x94\x36\x8d\x96\x39\xcd\x1f\x05\xc2\xc2\x99\x12\x42\x60\xd9\xbb\x40\xcc\x70\xab\xdb\x77\x23\x57\x94\x69\x64\x28\xa9\x15\x0c\x7b\x6c\x56\xac\xa9\x93\x76\x3d\xc4\xe7\x34\xcc\xfd\x9c\x34\x77\x64\x0a\x4f\xe8\xa1\x54\x13\x2b\x31\xa4\xa5\x15\x79\x5f\x38\xf4\x23\xbc\x60\x1d\xfb\xe1\x21\x3e\xa7\x1f\xdc\x0a\xbc\x12\x45\x80\xbb\xa5\x3f\x24\x71\x6b\x90\xf8\xc4\xfa\x66\xdc\xc5\x18\xa2\xe9\xdc\x9d\xe4\xb0\x60\x2a\x47\x9c\x09\xdf\x2f\x4c\x8f\x54\xc9\x11\xb3\x8a\x19\xe6\x41\x2c\xe4\x66\xc3\xc8\xde\x4d\x04\xe0\xfe\xc9\x25\x03\xa8\xb3\x7d\x8b\x60\xb3\xe7\x92\xf4\x2b\x32\x7b\xe0\xbe\x94\x36\x68\xe6\xdd\x47\xb2\xc0\x59\x00\x55\xe1\xfc\xfc\xa1\x02\xd6\xcf\x56\xb2\x04\x44\x34\x83\xe0\x0d\xe6\x5a\x9d\x56\x16\x88\x39\xa0\x02\x11\x9f\xc2\xd9\x8e\xf5\x7c\x9b\xd3\xbe\x33\xd9\x3e\x32\x6e\x36\x98\x04\x01\x8a\x2f\x92\xbd\xe5\x1f\x07\xbe\x6d\xbd\xe7\x58\x7d\xab\x27\xc8\xb4\x2b\xf1\x5c\x97\x97\xc7\x02\xc2\x1c\x14\x7a\x16\x7e\xab\x4f\x11\x24\xa2\xdd\xba\x83\x67\xac\xad\x35\x13\x0b\x87\x0a\xa8\xf0\x87\x30\xca\xf0\xc4\x7d\xa4\x06\xb0\xbe\xa0\x8a\x1e\xdc\x46\x14\xbe\xa0\x03\x20\x1b\xb7\xf7\x00\x64\x41\x02\x2b\x50\x37\x1c\x09\xb8\xad\x23\xfa\x36\xfd\xe7\x77\x04\x74\xe3\x33\x3b\x72\x64\xbd\xd0\x77\x78\xca\x72\x76\xff\xdf\xd6\xbf\x91\x20\x04\xe4\x4c\x1e\x86\x32\x62\x00\x63\x21\x79\xee\x7d\xce\x58\xc8\xa9\x67\x17\x8b\xf1\x2e\x71\x06\x6b\x6e\xa7\x38\x03\x4c\xeb\x0b\xec\x9a\xd4\xf8\x5a\x4f\xb9\x58\x55\x43\xeb\x8e\x30\xf3\xcd\xe0\x0d\xb4\xd3\x60\x7a\x6c\x67\x3a\x74\x37\xca\xe9\xf0\x8c\xa4\xb1\x00\xa3\x79\x9d\xc8\x74\x30\x24\x90\x17\xc6\x7e\xc3\x5a\xbd\xcf\xc2\x03\xe9\xbc\xff\xed\x3b\x13\xcd\x97\x5c\xa6\xe2\xc9\xa6\xf8\x56\x53\x3e\x7e\xbb\xe9\xd6\x87\xb6\xd2\x07\xc6\xc6\xef\xcc\xf5\x12\xb3\x78\x6d\x2c\xa2\x3d\x65\x9f\xa9\x59\x21\xcf\xf8\x0d\xcd\xc1\x96\x15\xc5\x9c\x90\x6d\xdb\xa6\x16\xd3\xb3\x63\xf9\xaa\xf9\xa1\x2d\xef\xe4\xf4\x9c\x7f\x48\xac\x78\x4d\x61\xaf\x9a\x6c\x68\x07\x04\x21\x3d\xe7\xbf\x3f\xe4\xf7\xcb\x2c\x0e\x1d\x3a\x60\x56\xb7\xad\x44\xd3\x29\xdf\x2e\xdf\xbd\xf2\x04\x17\xcd\xce\x9e\xad\x8a\x35\xa0\x9b\xd0\x58\xef\x5d\xb7\xb5\x93\xa8\x74\xdf\xcf\xb5\x68\x9e\x4b\xb0\x3c\x60\x6d\xf9\x85\xe9\x5a\xf8\xb9\xe8\x92\x9f\x8b\x16\x3d\xe4\x91\x4b\x48\x16\xc4\x67\xec\xc2\xfb\x05\x49\x72\x7a\x94\xc6\x74\x09\x85\x9a\x24\x71\xc4\xc3\x24\xa1\x58\x4d\x5a\xb1\xfb\x0a\x9f\x66\x96\xb2\x31\xc5\x6c\x66\x93\xda\xd3\x20\xf6\x3e\x4b\x0c\x9e\x93\x24\x7d\x25\x3f\x1d\x89\x68\x79\x7d\xda\xa6\x5d\xf3\x63\x70\xd0\x15\xa7\xc3\x53\x7e\x3d\xad\xd3\x3c\x00\x93\x2a\x10\x94\xc4\xa7\xe0\xea\x74\x28\xfa\xb4\x34\xf6\xa7\x4f\x50\xb7\xb5\x09\x20\xc9\xef\xc5\xea\x4a\xfd\xd1\x67\x10\xc9\xc4\xf0\x80\x4c\x22\x8b\xcf\x41\xf6\xd7\xb5\x84\xa8\x3c\xc3\xc7\x2c\x4c\xb7\x87\x0e\x71\xdf\xb8\x5c\xf6\xaa\x65\x0f\x6e\xc4\xf9\x6f\x35\xa0\x2b\xbb\xd8\x86\x98\xfe\xf9\x09\xb6\x63\x7f\x6b\x21\x77\x2e\x72\x38\x30\x7d\x47\x40\x4b\x0a\x0f\x73\xcb\x01\x19\x6b\xd6\xa3\x56\x6d\x8a\xdc\xdb\xd6\x7d\xe4\x3c\x0a\x78\xfb\xeb\xdd\xbe\x65\x7f\x56\x1d\xa3\x5e\x46\x88\x50\xcd\x97\x77\x15\xf4\x9f\xe9\x3d\xf5\x66\xd4\xc9\x84\xe6\x19\xc8\x1d\x35\x8c\xba\x38\x5b\xc5\x97\x77\x12\x27\x6d\xb3\x96\x53\xe7\x40\x27\x6f\xf0\x10\x44\x57\xaa\xe0\xba\x61\x03\xce\x17\x66\x54\x76\x47\x95\x87\x7a\x7d\x6b\x9d\x5f\x30\x8c\x75\x3d\x2c\xd7\xab\xd8\x7d\x7e\x04\xa7\xbb\x60\xc2\xcd\x87\x7b\xb5\x92\x48\x12\x4d\xaa\xb4\x9c\x2f\x7e\xdb\x04\xa3\x43\xac\xae\x98\xab\xfe\x50\xdf\xba\xaa\xbf\x69\x56\xac\xa8\xe3\x37\x83\xf4\x82\xfd\x6d\x25\x97\x26\x0f\x16\x94\xf6\x6d\xa1\xb1\x91\x2b\x5c\x45\xf7\x0f\xe4\xe5\xb5\xaf\x57\x05\x5c\xb3\x7e\xa0\xa3\xb8\x79\x08\xd2\x8e\xd2\xc6\xd9\xf2\x6c\x7d\x73\x6b\x43\xa3\xb1\x38\xba\xee\xe6\xb6\x43\x57\x86\xea\xb3\x46\xe0\xcc\x49\xfc\x30\x18\x51\x94\x88\x81\xe4\x8d\xde\x2c\xcd\xbf\x66\xd3\x20\x7e\xd6\x88\xed\x9e\xd4\x9e\x50\x0f\x6d\x44\x25\x0e\x0a\xb6\xf2\xc0\x80\x7c\xbb\xb7\xac\xd0\x83\xa4\x17\x5f\x36\x46\x8e\x76\x3e\xd9\x08\x6f\x91\xac\xd1\xcf\xff\xa9\xed\x30\x57\xf1\xbf\xba\x1d\x3a\xd7\xab\xc9\x4b\x78\x8e\x3d\x40\x84\x69\x9a\x3b\xf6\xba\x00\xdf\x89\x88\xd3\xef\xf0\xdb\x93\x6b\x79\x9e\x6e\xb9\x6e\xbb\x96\x30\x4e\xc2\x86\xea\x93\x75\x2f\x2c\xad\x9f\x29\x80\x1b\x8b\x9b\xe5\x5e\xbd\xb2\xad\xcc\x31\x92\x89\xed\x63\xc7\xe7\x58\x0a\xcc\x93\x95\x61\x3d\xf4\x4a\x6f\x2f\xc0\x4d\x59\xa9\xc7\x96\xe1\x4a\x6a\x99\xf6\x82\x7d\x83\x34\x5a\x0d\x80\x4f\x34\xc5\xc1\xe2\xd6\x8f\x63\x71\x12\x06\xec\x77\x4b\x1e\x2a\xdc\xa5\x25\x39\x7f\x8d\xe4\xfc\x9c\x93\xa7\x2d\x58\xaf\x42\xb1\x51\xa7\x0e\x95\xe3\xf8\x6c\xe3\x32\xcf\x39\x8c\xdb\x18\xde\x66\xee\xaa\x2a\x76\x93\x79\x7b\x49\x89\x93\x59\x03\xe4\x74\x02\x00\x7b\x78\x16\x7c\xa9\xba\x84\x10\xed\x4b\xbc\xa2\xa4\x43\xd0\xb0\xc5\x19\xc3\x37\xcc\xc4\x1f\x28\xa1\xdc\xd4\xb8\x57\x7a\x53\x37\xe9\x55\x7b\xc1\xc1\x07\x7a\x83\x3e\x91\x9f\x0e\xea\xa2\x6d\x07\x92\xe5\x08\x94\xd8\x48\x98\x65\xca\x34\x3d\xb1\x74\x66\x84\x57\x1f\x26\x33\x25\xd0\xd3\xa9\x12\xe8\xc3\x73\xb5\xe5\xf0\xf0\xd4\x56\xb7\x5f\x0d\x7b\xa2\x39\xa1\xc1\xe3\x33\x0e\x2b\x7f\x16\x32\x26\x2d\x4e\x4a\x7a\x0c\x1d\x17\x9e\x6b\x79\xc5\xc1\xfd\x67\x9b\x7e\xca\x39\xb7\xb6\x3d\x29\xeb\x1b\x9f\x14\x9f\xdb\x29\x78\x81\x95\x89\xd2\xc5\x7e\xf5\xa1\x1a\xd8\x97\xf7\x6a\x09\x13\x0d\x5f\xd7\xa9\x81\xe5\x4f\x00\x96\xbf\x24\xb0\xfc\x1c\x1a\xb7\x99\x5a\xe9\x1c\xdd\x56\x43\x01\x93\x1d\x57\xcb\x8b\xa7\xb4\x02\x92\x3c\x57\x0a\x9a\xb8\xa5\xca\x3f\xba\x0b\x99\x25\x75\x35\x9c\x40\x59\xa7\x22\xd1\xe3\x00\x32\x57\x1b\x47\x04\x95\x03\x7d\x75\xb3\x92\x87\x49\x39\x46\x28\xf5\xe1\xad\xa4\x38\x58\xc8\x78\x04\x6b\x34\x12\x56\x25\x08\xa1\x40\xe0\xe7\x29\xa1\x14\x0a\x16\x81\x85\x70\x11\xdc\x29\x07\x29\x9a\x03\xdc\x15\xb2\x99\x0e\x42\x5a\xf3\x06\x68\x2d\x8f\xe1\xb4\xd1\x5e\xa6\x52\xc8\x8a\x08\xd8\xe2\x63\xa4\xcf\x6a\x11\xce\xc1\x6a\x01\x2e\x46\xf6\xa8\x16\xa7\x29\x2c\x9e\x8f\x8d\x37\x2a\xf1\x92\x36\xbc\x95\x2c\x60\x22\x57\x40\x9a\x90\x14\xe3\x84\xf1\xcc\xbb\x7d\x5b\x5e\x12\xe6\x52\xd2\xe2\x01\x4a\x7f\x35\xcd\x02\xe1\x84\x70\xbd\x9a\x0e\xd3\xe4\xae\x5a\xb3\xa2\x42\xfc\x68\x11\x70\x06\xde\x27\x6f\x91\x6c\xd2\x8d\xf7\x27\x3a\x6f\x31\x4a\x37\xb0\xd4\x84\xcf\x86\x79\x77\x30\x9e\x85\xd6\xe1\xc3\x42\xea\xc8\x20\xba\x98\x0d\xdb\xe8\x79\x7b\xb5\x65\x13\x48\x79\x4d\x41\x2e\x36\x37\xbe\x34\xe4\x4a\x13\xd4\x46\x35\xbc\x86\xcc\xe9\x66\x39\xbc\xcd\x1a\x54\xdd\xb8\x2c\x60\x95\x8b\x38\x41\x40\xd5\x0e\x23\xd5\x7d\x63\x6f\xbf\x1a\x1a\x1c\x7a\x6e\xd9\x1e\x64\xfa\xcc\x17\x97\xe3\x5c\x38\x4c\x81\x95\x4a\x8a\x23\xdb\xe2\x93\x3e\x2d\x10\xdf\x2e\x39\xd6\x57\x4a\x9c\x43\xe1\x53\xcb\x7d\xcd\x0f\x96\x1c\x2a\x6b\x3a\xbe\xaf\xcf\x88\xc0\x3c\xfc\x0e\x4f\x67\xd1\x7e\x58\x6f\xda\x8b\x82\x4d\x52\xe4\x51\x18\xbc\x79\xf2\x8d\xd6\x51\xf7\x4b\x8f\x94\xe3\xf7\xa4\x8a\x11\x92\x32\xb8\xe2\x69\x02\x8a\xd0\x04\x9c\x21\x68\xd6\x76\xee\x2a\xdf\x10\x17\x97\xe6\xec\x08\xb2\x54\x5b\xe8\x49\x0d\xae\x0c\x34\xc4\xb2\xb1\x98\x77\x93\x90\x28\xbe\x1e\x79\xb8\x63\x69\x18\x73\x57\x5d\x07\xdf\xf9\x98\x5d\xf7\xa8\xa4\xb7\x65\x0f\xcf\x71\x03\xfa\xd6\x3b\xe0\x74\x81\x11\x37\x8f\x1d\x4e\xa3\xea\xd0\xf5\x54\xa2\xea\x71\x7f\xa3\xcb\x68\xcb\xec\x25\x33\xae\x08\x17\xcd\x72\x92\x77\x8d\x0e\x1e\xfa\x16\xde\xaf\x82\x9d\x7f\xf4\xff\x27\xba\xae\x8a\x45\xdf\x01\x8e\x30\x21\x86\x30\x07\xda\x8f\x17\x9d\x88\xe4\xe4\x9b\xf7\x2a\xae\xb4\x03\x72\x19\xd7\x76\xde\xdc\x29\xd5\x50\x26\x5d\x99\xb1\x68\x7a\x3c\x7e\xe3\xf0\x80\x39\x2c\x3f\x55\xbf\x80\xff\x67\x4a\xa2\x93\x1b\xea\x84\x54\xa3\x84\x27\xc1\x48\x48\x6d\x75\x90\x14\x6f\x6f\xec\xe2\x46\xf4\xa8\xa0\xbe\xe3\xf6\xdc\x9e\x4c\x5a\x93\x12\xd3\xc7\xd3\xd2\x2e\x48\xca\xf4\x7e\x57\xd2\x55\x05\x98\xdb\x63\x48\xaa\xcf\x5d\x40\x0f\x28\x2c\x58\x67\x69\xe3\x77\x35\xa0\xde\x55\x62\x39\xea\xf2\x88\x5c\x26\xdd\x96\x52\x12\x91\xcf\x1c\x88\x95\x22\x6b\x96\xeb\xbd\xa4\xf8\xa0\xf5\x92\x22\x8f\x91\x33\xf9\x90\x88\x29\xa5\xa6\x4f\x2d\xab\x5c\x27\xb5\x9a\x51\xe7\x5c\xad\x80\x9a\xa7\xf8\xae\x37\x63\x67\x03\x49\x85\x5f\x2c\x7b\x46\xf4\x83\xa6\xec\x24\x14\xfd\x29\x87\xa2\x97\x14\xa8\xd5\x4a\x98\xfd\xb2\x16\xed\xd9\x1b\x9f\xee\xde\x27\x47\x6e\x78\x99\x7c\x06\xc6\xd9\x3d\x15\x1d\x47\xc2\xff\x41\x63\x51\xba\x77\xca\xd9\x61\x52\x5e\x29\xdd\x6d\xb8\xbf\xfc\xbc\x12\x6e\xc4\xf8\x7e\x60\x8f\x38\x74\xfc\x90\x30\x4c\x01\x88\xcc\xac\xc5\x12\x5d\x9e\xbe\xd4\xc9\x64\xfe\x44\x43\xec\x82\x2f\xd1\xfb\x8f\x27\x6c\xc4\xe8\x40\x30\x22\x00\x84\x11\x15\x83\x04\xa8\xa9\xe6\x7c\xb3\xf2\x90\x7b\x10\x7a\xfc\x62\x04\x36\x7d\x38\x49\xb9\xf7\x1c\x4d\xfb\x61\x8d\xc0\x79\x4c\x7d\xaa\x4d\xa9\x8e\x86\x49\x20\x9e\xc5\xa4\x05\x33\x80\x42\x04\xb4\x3b\x7a\xd3\xef\xad\xeb\x67\xfb\xbb\x7a\xae\xef\x6a\xcb\x73\xdd\x63\x30\x09\xa7\x66\xb3\x24\xe1\xd4\xac\x06\x5c\x9a\x05\x00\xb9\x4a\x4f\x20\xb6\x7c\x00\x2e\xfb\x82\xa9\x05\x91\xfa\x32\x3f\x7b\xac\x39\xfd\x76\xd8\xd9\x5b\x5d\x67\xc7\xe7\xa7\xb7\xa0\x36\x83\x2a\x8a\x02\xd2\xe1\x29\x67\x29\xae\x22\xcb\x21\xac\x1a\x9d\xa9\x7f\x8d\xaa\x3d\x60\xb6\x26\xb8\xdf\xcf\xc3\xdd\xc6\x05\xc9\xc3\xb4\x74\x64\x73\xb4\x57\x38\xa7\x48\x99\x45\x7e\x4c\x9c\x42\xcd\x56\x90\xd6\x9a\x5a\xe2\x5d\xd0\x62\x57\xbb\xa2\xb3\x77\x22\xf0\x48\x51\xfe\xe0\xe8\xc1\x22\x21\x06\xcb\x41\x5e\xb0\x97\x08\x4a\xe7\xaf\xcf\xe8\x73\xd5\xdd\xc8\x35\xbf\x8e\xf4\x43\xbd\x63\xb0\x25\xfb\x36\x09\xab\x4a\x29\x80\xfd\x15\x29\xb6\x73\xf9\x3e\xb8\xea\x3e\xd6\xab\x80\x2f\xa7\x8f\x8f\xa1\x86\xa1\x24\x4f\x0b\xb4\x69\x84\x6b\x35\x46\x38\x76\x82\x96\xa3\x4d\x18\x61\xa3\x67\xf5\x0e\x07\x02\xfd\xb1\x7a\x5c\x24\xc9\x31\xb7\x2a\x77\xf6\x36\xd3\x13\xce\x29\x85\x76\x0c\x54\xa4\xb4\x63\x06\x3b\x2d\x72\x97\x9f\xcc\x22\xa1\xad\xfe\x20\x4d\xeb\xf9\x4c\xdb\x37\x5f\x99\x63\x7a\x6e\x1b\xf4\x6c\x4c\x9b\xb4\x44\x02\xb9\x14\x72\x6f\xef\x9a\xa6\x55\xc7\x77\x6c\x27\x25\x92\x17\x4e\x27\xf3\x3a\x63\x53\x76\x8b\x1d\x99\xab\x7d\xc4\x7e\xa4\x15\xdf\xc5\x85\x88\xb6\xd5\x54\x82\xe1\xc2\x54\x55\x82\xe9\xbd\xa9\xc2\x16\xbb\x5d\x38\xc5\xdc\xd3\xb5\x40\x5b\x07\xf2\x51\x08\x8e\x83\xf8\x55\x23\x4e\x39\x20\x71\x99\xf5\x40\xec\x39\xab\x00\xe3\x93\x50\x93\xdb\xcb\x4b\x7e\x40\x8c\xe3\xee\x6b\xf8\x3f\xfe\xc9\x71\x48\x43\xfb\xea\x08\xbe\x64\x1d\x25\x74\x7e\x3c\xa6\x67\xea\x1d\xfe\x16\x89\x2c\x64\x19\x78\xb7\x87\x72\x8b\xfb\xfb\x76\xdf\x88\xf8\xe8\xb2\xb4\x21\xce\xf2\x8d\x80\x99\xea\xda\x76\xb0\x17\xf4\x1c\x27\xf5\x96\x92\xe9\x88\x1d\xae\xc2\x04\xf3\x95\xec\x6a\x29\x0f\x79\xb9\x32\xb8\x10\x5e\xc1\x1b\x67\x5a\x88\xfa\x3d\x2d\x41\xfd\x3e\x00\x2e\x66\x47\xc6\x87\x9c\xe1\x97\x10\xe9\xd0\x63\x04\x29\x13\x6c\xb4\x01\x4b\xda\x18\x6f\xfc\x1c\x94\x17\x11\x31\x9e\xd9\x25\xf2\x2c\x6a\x10\xa4\x67\xa6\x62\xaa\x67\x5f\x62\xaa\x67\xc5\x62\xaa\x76\x6c\xd4\x83\xbe\xdf\xd8\x42\x9c\x9d\xbd\x4e\x57\x3b\xe6\xba\x17\x67\xf9\xa0\xbe\xc7\x0f\x70\x70\x6c\xef\x7b\x39\x7b\x45\x7e\xe3\x4a\xe8\x6c\xfa\xf9\xd3\xd4\x71\x1d\xfd\x5f\x37\xf5\x50\x7d\x7f\x0f\x86\x21\xf7\x86\xba\xbc\xb8\xf7\x8d\xdf\x37\x35\x5c\x8a\xdc\xc6\xa9\x57\x07\xa6\x27\xe8\x32\x58\x54\xdf\xb8\xb0\x6c\x6f\xe5\xcd\x0e\x3d\xde\xd5\xa0\x35\x9d\x59\xc3\xe8\x78\x0a\x04\x7c\xf6\x27\x80\xf5\x8b\xbd\xd3\x3a\x97\x11\x43\x8f\xc2\xdf\xed\xad\x55\xf3\x04\xc9\xb1\x83\xf2\x76\x88\xbd\x25\xa2\x8e\x3a\xd6\x3d\x3c\x05\xf2\xaa\x11\xff\x36\x2d\xa2\x47\x94\x68\x1d\xd5\x4c\x39\x9c\x50\xa2\x6e\x7c\x8c\x54\x2b\x80\xa1\x07\x65\xce\x31\x0f\xd8\xeb\x6f\xc6\x03\x86\x0f\x66\xfd\xb7\x4a\x42\xba\xbb\x61\x1f\x93\x68\xce\x3a\x85\xff\xa8\x6e\xa8\x63\x7f\xe3\xa7\x21\x08\x60\xd2\xad\x1d\xc9\x1d\x85\xef\x11\x12\x02\x0d\x12\x87\x6e\xf6\x08\x5b\x6e\xf4\x26\x56\x9c\xbe\xe1\x17\x96\xbf\xc6\xd5\x6b\x98\x1d\x3a\x83\x22\x93\x9c\x14\x7a\xcb\x79\x81\xa9\x9e\x29\x6c\xb1\x20\x02\xa6\x98\xaf\xf5\x2c\xa6\x20\xa4\xc9\x72\x53\x35\x6b\x60\xe9\x7f\xf2\x4f\xe2\x76\xf8\x67\x98\x20\x71\xa2\x86\xfa\x8f\x9d\x99\x1e\x99\x5b\x35\xb4\x80\x94\x12\x70\xe1\x4e\xb6\xc4\xad\x8c\x3f\x04\x8e\xf1\x7b\xbe\x83\x0a\x3b\x15\x94\xd2\x7c\x5b\x45\xda\x52\xad\x5b\xbb\x97\xbf\xbc\x3e\x19\x41\xce\xd0\x02\xcd\x99\xa1\x1d\x9a\x33\x43\x29\xc4\xaa\x20\x0c\x01\x96\x04\xf3\x23\x10\xc8\x83\x03\x10\x84\x8e\x16\x44\xc0\xe4\xd9\x8a\x14\xf5\x4b\xc2\x2c\x71\xb3\x13\xa4\x97\xdf\x29\x90\x7b\xaf\x58\xa0\xec\xb9\xe2\x49\xab\x8d\x6f\xb3\x91\xab\xe4\x48\x74\xc4\xfe\xcd\x11\x1d\xf1\x30\x99\xed\x9e\x41\xb3\x3b\x61\x6d\x86\x66\x02\x7f\xaa\x49\x06\x6a\x20\xb1\x66\x83\xd0\xaa\x43\x37\x09\x71\xeb\xc0\xbc\x3e\xc5\xaf\x64\xe9\x74\xfb\xf1\x7e\x11\xd8\xb8\x03\x59\x93\x27\x25\x0c\x78\xbd\x0a\x13\x63\x1a\xf4\x17\x4f\xe3\x4b\xce\x50\xb6\x8f\x06\xb3\xa9\x2f\xab\xa0\x9a\xd7\xd1\xbc\xa6\xb4\x04\xf8\x6a\x18\x76\xbd\x05\xc6\xc0\x6b\xbb\xf9\x09\xfd\x18\x0d\xc2\x57\xa5\x23\x99\xd4\xb4\xab\x71\x5d\xe2\xe6\x45\x12\xe6\x67\xdc\xa0\xf5\x70\x70\xe0\x7a\x3a\x8c\x69\xdc\xba\x0b\x84\xd3\x76\xc8\x0b\x4d\xf2\xac\x40\x68\x9d\x59\x80\xd9\x96\x19\x4a\x0f\x49\x86\xc1\x21\x69\x56\x6d\x8b\x55\x27\xd1\x9a\xf9\xcf\x39\x9b\x14\x85\x1c\xbf\xf7\x2c\xad\xe7\xe7\xa5\xf7\xe2\x9e\xab\x9f\x11\x3e\xbe\xac\x26\xd3\x64\x19\x33\xaf\xb1\xa5\x00\xd5\xa7\x6a\xb5\x77\xf7\xa8\xbf\xc8\x6f\xbd\xb8\x88\xd5\xb4\x66\xf9\xbe\x6f\xf0\x12\xdf\xa9\xa4\x38\x98\xb9\xe7\x01\xac\xeb\x34\x6f\x83\x6a\x97\x86\xc3\xed\x87\xe6\x21\xcb\x32\x94\x59\x06\x9a\xc1\x9d\xfc\x5c\x6e\xc4\xc5\x70\x64\x2c\x68\xb0\xf0\xac\x2e\xe1\xe9\xbb\x0c\x9e\xbf\x70\xb1\x16\x48\xf5\x02\x0e\xf0\x6a\xf2\xa6\x07\x0d\x2b\x93\x43\xab\x15\xdb\x8c\xf0\x35\x57\x1e\x9e\x60\x27\x81\x38\xe4\xd3\x90\x3d\xc4\x33\xfd\x99\xc0\xd4\x8d\xb0\x78\x92\x25\xda\xf7\x57\x92\xa6\x55\x3a\x0b\x48\x93\x4e\xc2\xab\x8e\x41\x04\x3a\xd3\x94\x31\xa4\xb5\x0c\x20\xb6\x51\x18\xcf\x86\x67\xff\x7c\x1a\xe2\x53\x3a\x33\x55\x37\xa6\xf1\x32\x5a\x56\xbb\x63\x02\xbe\x5b\x4c\x7a\x1b\x64\x1d\x5d\x11\xb3\xe7\xbc\xcb\x2e\x28\xfb\x4d\xe6\xfe\xfd\xe8\xf9\x79\xbb\xfb\x71\xf6\x16\x49\x50\x91\xfb\xf2\x3e\x61\x57\x35\x2e\xa0\xb1\xfc\x2a\x27\xef\x1c\xdb\x6b\x40\xdf\xc5\xd7\x80\xd8\x66\xff\xe0\xab\x87\xe1\x15\x3a\xd4\xca\x46\xc8\x12\xdd\x6c\xf4\xa2\x52\xdf\xad\xbe\x1d\x97\x65\x9d\x76\x0a\xc6\x99\xff\x66\x15\xcb\x18\xed\xbd\xbe\xdf\xf5\x8d\x3c\xf9\xed\xc6\xf7\xad\x28\x5e\xbf\x95\x91\xfe\xc1\x3d\x62\xa7\x35\x8c\x9f\x89\xb2\xe9\x4a\x9e\x58\xf1\x15\xea\xeb\x4f\xd3\xfa\x46\x4f\x08\xba\x67\x12\xdb\xe6\x4b\x3a\x36\xfb\xd6\xcc\xef\xfa\x26\xce\x17\x77\x2b\x04\xbe\xd4\x35\xb0\xdf\x23\x8c\x90\x75\x9d\x5f\xd4\x88\x24\x08\x34\x24\xaf\x4b\xdb\x5a\xd2\x0f\xdf\x0d\xac\xa4\x7f\x2d\x70\xba\xda\x23\xfc\xb0\x17\xc7\xf4\x71\x28\xd0\x16\x49\xae\x7b\x7d\xdb\x43\x9e\xe4\xba\x1f\x5e\xac\x22\x5c\x1f\xda\x76\xf3\x3e\x2b\xd6\x3c\x24\xfa\x3f\xc3\x3b\xea\xe2\x95\x04\x44\xa5\xcf\x4c\x7e\xf2\xd7\x77\x5c\xf3\x77\x1a\x33\x8f\x5f\x07\xf8\x6e\x8b\x84\x6d\xdd\x30\x7d\xe6\x84\x2b\x24\x5c\x71\x20\x1d\xfe\x59\xe2\x67\x59\xdc\xe0\xd7\x35\x7e\x5d\x57\xd5\x07\x29\x0c\xc2\x43\xc5\xdb\x86\xf8\x2f\x4e\xb9\xc1\xef\x1b\x7e\x8b\xe5\x3e\xfb\x6c\x4a\x6c\x3e\x3c\x79\x63\x3f\xf0\xb0\x0d\x37\xa7\xe9\xf6\x83\xd2\xb9\x55\x4d\x95\xcf\xfb\x6c\x19\x72\xa3\x49\xf8\xe2\x97\x18\xa8\x79\x4d\x92\xcf\xfb\x38\x2f\x86\x2b\xab\x50\xbe\x29\x95\xfb\xa1\x89\xf2\x49\x69\x5d\x71\xbd\x8c\xfd\xd2\x2f\xa4\xc6\x5e\xe9\x17\x4d\x6f\xd9\xb5\x3b\x0e\x85\xfe\x3e\xb3\xd7\x28\xe2\x33\x14\xcf\x28\xcf\x8c\xb3\x38\xb2\x31\x07\x58\x40\xc0\x7b\x56\x2e\xef\xd8\xcf\x7c\x91\xd9\xeb\x33\x75\xb3\xdb\x07\x99\x3d\xbe\x7c\x24\x60\x31\x32\x9f\xb8\xa6\xf0\x83\xb5\xf2\x9a\x37\xad\xee\xf2\xa2\xd6\x47\xcc\x59\x5d\x4d\x02\xd0\xd7\x7f\xff\x3b\xe0\xe9\xfb\x1f\xff\xc8\x8f\x9f\x7c\x93\x57\x9f\x38\x92\x6b\x9f\x6f\xf5\xfe\xd5\xc0\xe8\xf7\xf3\x04\x92\xbd\xe2\xe1\x31\xa0\x77\x85\xe2\x31\x80\xe6\xb3\xff\x17\x00\x00\xff\xff\x2e\xbd\x85\x7a\x39\xc0\x00\x00") +var _confLocaleLocale_enUsIni = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xac\xbd\xed\x72\x1c\x47\xae\x26\xfc\xbf\xae\xa2\xac\x09\x85\xac\x08\xaa\x1d\x1e\x9f\xf7\xdd\x0d\x87\x65\xaf\x24\x5a\x1f\xe7\x88\x22\x8f\x48\x79\x76\xd6\xa1\x68\x57\x77\x17\x9b\x35\xea\xae\xea\xa9\xaa\x16\x45\x4f\xcc\x1d\xec\x05\xec\xf5\xed\x95\x2c\xf0\x00\xc8\x44\x56\x55\x93\x92\xe7\xe8\x87\x58\x9d\x89\xfc\x46\x22\x01\x24\x80\x2c\x76\xbb\xf9\xaa\xec\x96\xf9\xe3\xfc\x49\xbe\x2b\xaa\x7a\x53\x76\x5d\xde\x95\x9b\xcb\x47\x57\x4d\xd7\x97\xab\xfc\x45\xd5\xd3\xef\xf6\x63\xb5\x2c\xb3\xec\xaa\xd9\x96\x04\xfa\x92\xfe\x64\xab\xa2\xbb\x5a\x34\x45\xbb\xa2\x84\x63\xfb\xce\xca\x4f\xbb\x4d\xd3\x32\xd0\xcf\xf2\x95\x5d\x95\x9b\x1d\x97\xa1\x3f\x59\x57\xad\xeb\x79\x55\xd3\xcf\x73\xfa\xca\x5f\xd5\x92\xd2\xec\x7b\x4b\x3a\xdd\xf7\x92\xb6\xdf\x59\xd2\xbb\x5d\xd6\x96\xeb\x8a\x7a\xd3\x52\xd2\x5b\xfd\xcc\xae\xcb\x45\x57\xf5\xdc\xd2\x5f\xe4\x2b\xfb\x58\xb6\x5d\xd5\x70\xed\xbf\xc8\x57\xb6\x2b\xd6\x0c\x70\x46\x7f\xb2\xbe\xdc\xee\x36\x05\x0a\x5c\xe8\x67\xb6\x29\xea\xf5\x5e\x60\x5e\xeb\x67\xb6\x6c\x4b\xca\x9a\xd7\xe5\x35\xa5\x3e\xc3\x8f\xd9\x6c\x96\xed\x69\x12\xe6\xbb\xb6\xb9\xac\x36\xe5\xbc\xa8\x57\xf3\xad\x0c\xf3\x1d\xa5\xe7\x9a\x9e\x53\x7a\xce\xe9\x18\x42\xb9\xa2\xa1\xce\x8b\x4e\xc7\x41\x73\x49\x23\x2f\xba\x0c\x55\xd5\xc5\xd6\x4a\xf3\x67\x56\x6e\x8b\x6a\xc3\xb3\xc6\x7f\xa9\xdf\x5d\x77\xdd\x60\x6a\xcf\xf4\x93\xe6\x60\xde\xdf\xec\x4a\x4c\xc1\xa3\x0b\xfa\xca\x96\xc5\xae\x5f\x5e\x15\xdc\x4d\xf9\xca\x08\x68\xd7\xd0\x5c\x34\xed\x0d\xe0\xec\x47\xd6\xb4\xeb\xa2\xae\x7e\x2f\x7a\x99\x9f\x53\xf7\x33\xdb\x56\x6d\xdb\xf0\xd4\x9e\xe0\x23\xa3\x91\xcf\xb9\x1e\x4a\x79\x43\x93\xe0\x6a\xe1\x9c\x6d\xb5\x6e\x65\x16\x39\xf3\x04\xbf\xb8\x16\xc9\xd3\x9a\x24\x2b\xd4\x76\xd9\xb4\x1f\x34\xf5\x39\x7f\x0e\xaa\xa4\xce\x69\x6e\xda\xaf\xa2\xa6\xf5\xd0\xdc\x13\xfc\x48\x00\xba\xac\x58\x6d\x69\x86\x77\x45\x5d\xf2\xd4\x3d\xe1\x5f\x34\x5f\xf4\x2b\x2b\x96\xcb\x66\x5f\xf7\xf3\xae\xec\xfb\xaa\x5e\xf3\x1a\x3c\x91\xa4\xfc\x5c\x93\x32\x97\x17\xd2\x6e\x9a\x7d\x58\x65\x4a\xff\x2b\xfd\xcc\xcf\xe4\xa7\xe4\xb9\x42\xc8\x0c\x25\xa9\xc9\xbe\xfa\x58\xf5\x55\x29\x8d\xd9\x8f\x6c\xb7\xdf\x6c\x68\x3e\xff\xbe\x2f\xbb\x9e\xb3\xce\xe8\x37\xcd\x80\xfc\xce\xaa\xae\xdb\xa3\xc4\x2b\x7c\x64\xb4\xa8\xf5\x12\xc3\x79\x86\x8f\x2c\xfb\xb5\x2b\x8b\x76\x79\xf5\x3e\x93\xbf\xe8\x2d\x7f\x30\x52\x1e\x5a\x6e\xc6\x30\xc5\x2e\x69\xc1\x1a\xc8\x96\xcd\x8a\x7f\x3c\xa3\x3f\x54\x75\x55\x77\x7d\xb1\xd9\xbc\xcf\xf4\x83\xc1\xe4\x4b\x96\xa0\xaf\x7a\xcc\x83\x26\xe6\xe7\x7d\xb9\xeb\x78\x0d\xf3\xe7\x55\xdb\xf5\x8f\xfa\x8a\xb0\xf8\xed\xbe\xce\x56\xcd\xf2\x03\xed\x0f\xde\xeb\x68\xf9\xd5\x65\x4e\xd3\xf5\x80\x76\x48\xbb\xaf\x6b\x9a\xa0\xfc\x45\x43\x93\x46\xcd\x54\xd4\xfe\x31\xa0\x8f\xf2\xdd\xa6\x2c\x3a\x02\x29\x8b\x55\xfe\x43\x91\xf7\x45\xbb\x2e\xfb\xc7\xf7\xe6\x0b\xda\x97\x1f\xee\xe5\x57\x6d\x79\xf9\xf8\xde\xfd\xee\xde\x8f\x2f\xf6\x54\x6c\x53\xd5\x65\xf7\xc3\x37\xc5\x8f\xf9\xb2\xa0\x1c\x9a\xc6\x9b\x7c\x51\x5e\xf2\x36\xa4\xb6\x72\xc2\xff\x7a\xcd\x5b\xf0\xa6\xbf\xe2\x06\x09\x17\xe8\xa3\xcb\x99\x06\x7c\x95\xf1\x02\x10\x8d\x98\xaf\x16\x46\xef\xd0\x21\x24\xb7\xb4\x00\x27\x37\xe7\xff\xf9\xfa\x28\x3f\x23\xa2\xb7\x6e\x4b\x7c\xd3\x7f\x54\xe2\xbb\x9c\x46\x7b\x51\x1d\x3f\x9d\x65\x54\xd6\x26\xe4\xb8\xe8\x8b\x05\xf7\x3d\xac\x3f\x67\xca\xf6\x0c\x79\xd8\xa4\x4c\x46\x41\x32\xbb\x3e\x59\x96\xa9\x2d\x4e\x75\x28\x5d\x08\x75\xbc\x61\xe2\x40\xe9\x61\x66\xcf\x64\xce\xa8\xaa\xfc\xd5\x9b\x37\xa7\xc7\x4f\xf3\xb2\x5e\xd3\xcc\xe4\xd7\x55\x7f\x95\xef\xfb\xcb\xff\x3e\x5f\x97\x75\xd9\x16\x9b\xf9\xb2\xe2\x49\x69\x09\x65\x73\x9a\x25\x19\xe2\x2c\xeb\xba\x0d\xd1\x2e\x60\xc1\xf9\xf9\xeb\xfc\x84\x31\x61\x57\xf4\x57\xe8\x48\x7f\x95\x75\x7f\xdf\xf0\x44\x85\x06\x2f\xae\xca\x1c\xdb\x01\x40\xcd\xe5\x70\x5e\xf2\x95\xf6\x75\x96\xff\xb0\x68\x7f\x74\xfd\x2b\x16\x5d\xb3\xd9\xf7\x5a\xf2\xfa\xaa\xac\xb1\x50\x84\x4a\x6d\x4f\x84\xd0\x8e\x95\x59\x56\xb6\xed\x9c\x48\x72\x7f\xc3\xcb\xa3\x7d\x39\xd4\x8a\x54\x46\xbb\xa4\x6e\x7a\x5a\xfe\x1c\xe5\xa4\x8a\xaa\xfe\x58\x6c\xaa\x15\x2d\x52\x9c\xc8\xb4\x2c\x12\x57\x0d\xad\x37\x97\x26\x8c\x6e\xae\x31\x45\xb4\x77\xe9\xc4\xc8\xef\xcd\xee\x81\x84\xdf\x7b\x74\x6f\x96\xd5\xcd\x5c\xe8\x0b\x13\xfb\x55\xd5\x15\x0b\x22\xfc\x72\x10\xb5\x46\x47\xff\xca\x78\x27\x5d\x51\x88\x3c\x81\xe0\x35\xe1\xc3\x0d\x67\x0a\x23\x65\x41\x27\x00\xc8\x94\x12\x28\x3f\x76\xa3\x66\x01\x2f\x84\xa0\x85\x84\xd1\x98\x33\x5b\x68\xc3\xca\x27\xbb\xdd\xa6\x5a\x4a\xd3\x2f\x24\x2f\x22\x28\x1f\xf5\x3a\x29\x1e\x0e\x08\x66\x79\x0e\xcd\xa8\xd7\x4c\xf0\xf2\xe4\xe4\x40\xf9\xab\x92\x76\xdc\xd5\x7e\x2d\xc7\xdd\xa6\xd9\xaf\xbe\x02\x21\xb2\x95\x8b\x74\x28\x7f\xdb\x50\x87\x81\x55\x01\x20\x36\xf1\x84\x08\x0a\x73\x17\x6d\xb9\x6d\x7a\x9e\x38\x2d\xc6\x14\xf4\xba\xa2\x4c\x1a\x69\x57\x7c\xa4\x73\xb3\x6f\x64\x2b\xaf\x68\xab\x2e\xb9\x62\xa2\x7c\x7b\x62\x11\x64\x3b\x11\xfd\x91\x2d\x65\x69\x29\xee\x02\x6a\xbb\xa7\x5d\x78\x45\x95\xf1\xc4\x33\x8b\x43\x55\x4e\xf5\x13\x43\xa2\x7a\x40\x1d\x68\xc7\x37\x74\x1c\xf3\x42\x1f\xe3\x43\x7f\xfb\xfa\xa9\x57\xc5\xe5\x25\xf5\xaa\xa3\xdd\xf4\x32\x5f\x6e\x1a\xda\x8a\xef\xde\xbe\xee\x78\xa3\x5d\xcd\x77\x4d\x0b\xd6\x86\xb2\xce\xe8\x33\xa4\xb9\x89\x66\x88\x7a\xbf\x5d\xd0\xaf\xeb\xab\x8a\x08\x3c\xa6\x9d\x4b\xf0\xfe\xa0\x54\x6a\x62\xdf\xd1\x12\x1e\xe5\xb4\xb5\x68\x04\x34\x65\x40\x00\x1e\x83\x61\x1d\x83\x5f\x12\x8e\xed\x5b\xda\x4e\x57\x7d\xbf\xb3\x96\x5f\x5e\x5c\x9c\x49\xd3\x21\xf5\xb6\xb6\x0b\x87\x19\x58\x83\x0d\x33\x5b\x75\xde\xd4\x33\x20\xc9\xbe\xdd\x0c\xf0\x87\xc6\x6a\x39\x07\xe6\x85\xbb\xf0\x0d\xff\x77\x1e\xa7\x07\xf3\xdc\x11\x1b\x79\x0d\x6c\xa2\x39\x06\x03\x34\xcb\x36\xcd\x7a\xde\xd2\x6a\x18\x32\xbd\x6e\xd6\x82\x40\x49\x46\x6c\xe9\xd8\x50\x82\x67\xe3\xba\x65\x86\x90\x20\x41\xb0\x78\x91\x69\x93\x34\x3b\xee\xa7\xdb\x25\xa7\x9a\x10\xb7\x06\xda\x0e\xf9\x60\xc1\x28\x13\xc4\xc9\xb1\x0b\x5b\x9a\x3f\xa5\xe6\xe7\x27\x34\xab\x20\xe9\x48\xbd\x6c\x9b\x2d\xa5\x3e\xa7\x3f\x31\x21\xf6\xf1\x84\xeb\x03\x4c\xb1\x5a\xd1\x61\xd3\x1d\xe5\x6f\x9f\x3f\xcb\xff\xbf\xef\xfe\xfc\xe7\x59\xfe\xaa\xe7\x8d\xcd\xb8\xfe\x37\xc6\xd1\x42\x67\x22\x82\x12\x01\xec\x09\x8d\xef\xf1\x46\xbd\x97\xff\x80\xdc\xff\x51\x7e\x2a\x88\x85\x2d\x67\xcb\x66\xfb\x23\x13\xf7\x6d\x41\xa4\x84\x73\x08\xfb\x75\x5b\x9c\x97\xf5\x8a\x3e\x84\xa1\xd4\x2c\x47\x5c\x34\xdb\xb1\x97\xc2\x57\xcf\x97\x4d\x7d\x59\xb5\x3c\x9e\x9f\x6b\xe0\x96\x71\xdc\xc4\x34\x20\xc7\xb8\x33\x9a\x32\xa2\x47\xd5\xe5\x4d\x04\xc5\x48\xdf\x70\xa2\xa2\x47\x26\x38\x3c\x57\x52\x1f\xe6\xf8\x5c\x50\x9b\xb1\xe0\x94\x46\xd7\xda\x74\x77\x71\xbe\x9b\xcb\x4b\x3e\xf1\xed\xac\xd2\x16\x4e\x25\x55\x8e\x2d\x0f\x42\xa8\xbd\x83\xcc\x70\xac\x5b\xe2\xd9\xf1\x9b\xbc\xfc\x48\xb8\xcb\x34\xb4\x6d\x56\xfb\x25\xf0\x95\x61\x8f\x98\xf4\x13\xc1\xe9\x68\xa7\x2d\x4b\x45\x96\x40\x72\xb8\x6b\x4c\xd7\x96\x04\x44\x94\xc6\x48\x3f\x31\xba\x1f\xe9\x1c\x69\x5d\x13\x2f\x2c\x49\x7b\x3f\x82\x1d\x75\x2a\x94\xe0\x91\x2f\x69\xc1\x09\x29\xa4\x17\x9d\x74\x4a\xb2\x69\xf3\xd0\xae\xd8\x93\x00\x55\xac\xa8\x2f\x8b\x1b\x50\xb1\x8e\x71\x61\x55\x5e\x16\xfb\x4d\x1f\xfb\x35\x38\x92\xac\xa5\x73\x96\xe1\x7c\xde\x64\x81\x51\x07\x81\x3c\xdd\xb0\x2c\x61\x61\x4d\xdc\x96\x1c\x5d\x8c\xae\x22\x24\xd9\x29\x46\xc4\xae\xc4\xf2\xcc\xa3\x48\xa2\xeb\x65\x92\x49\x9a\x1f\x9a\x7d\x2b\xfc\x57\x8e\x83\x9b\x6b\xb4\x0a\x98\x61\x99\xee\xcb\x2c\x53\xa6\x6d\xae\xd2\xe4\xfc\x63\x05\x59\x2d\xa0\xab\x54\xa9\x12\x26\xd3\x85\x5f\x18\x80\x85\xc0\x6e\xb2\x6c\xe8\xcd\x29\x0f\xb2\x0b\xb2\x9a\xcc\x39\x0f\x17\x2d\x30\x23\x49\xab\xf4\xb1\xc2\xa1\xa1\x08\x83\x79\x59\x30\xaf\x43\x4d\x53\x53\x5d\x59\xa2\x06\x2a\xff\x0d\xd5\x89\x32\x33\x15\x54\x54\x76\x30\x06\x94\x99\x87\x55\x03\x4e\x04\x27\x13\x95\xb6\x69\x1d\x70\x09\x79\x5b\xad\xaf\x88\x52\x37\xd7\x47\x32\x29\xd7\x57\x4d\xc9\xfb\xe7\xd5\xf1\xe3\x6f\xa5\x1f\x6b\x3e\xa7\x42\x21\x3e\xe1\x8a\x3d\x21\x17\xcd\x98\xa2\xb1\x74\x21\x70\x0a\x80\x1c\x89\x44\x02\x34\x94\x4d\x47\x8c\x49\x20\x1a\x4a\x2b\x7c\x9e\x12\x89\x08\x23\xa5\x4d\xbe\x95\x86\x85\x28\xa9\xd0\x31\x5f\x37\x90\xa7\x4c\xc8\xe0\xa3\x97\xa4\xf5\xae\x9f\xaf\xab\x7e\x7e\xc9\x94\x8b\x2b\x7e\xce\x15\x30\x27\x40\x39\xf9\x03\xca\x7a\x90\x13\xf5\x23\x21\x71\xf5\x7d\x7e\xff\xa3\xb2\xad\xdf\x31\x49\x9a\xd3\x26\xaa\x36\x58\x11\x95\xd2\xda\x52\xb8\x52\xd3\x10\x04\x16\xb0\xdb\xef\x70\x50\x2a\xb7\x19\x44\x92\x55\x73\x5d\xf3\xe6\x03\xe9\x25\x32\x53\x2d\x2b\x3a\x30\x16\x55\x5d\xd0\x49\x63\xb5\x80\xa4\xdf\x27\x94\x78\x73\x7a\x01\xc0\x75\xb3\xd8\x57\x9b\x95\x01\xcc\x32\xe3\x48\x89\x1f\xd5\xc5\xf7\xbc\xbd\x25\x55\xd2\x97\x65\xd3\xf2\x59\x86\xd1\x58\xc1\x03\x7c\x15\x1f\x84\xc2\x08\x57\x2c\x54\x01\x16\xe5\x02\x0b\xc4\xd3\x40\xab\x0f\x79\x91\x19\x24\xa0\x4d\xd5\xd5\x0f\x7a\xf4\x74\xb9\xa7\xb6\x68\xe5\x39\x99\x0a\x76\xf9\xa3\x1f\xe9\xff\x8c\xd9\x2d\x39\x00\xd6\xe3\x89\xe7\xcc\x5c\x32\xf7\xb2\x15\x93\xae\x26\x38\x1e\x56\xda\x30\xd8\x8d\xd5\xf7\xd7\x50\xa0\xdb\x0b\xd2\xb2\x32\x67\x43\xcb\x5a\x7e\x45\x1f\x2c\x3e\xae\x37\x58\x84\xa2\x57\x19\xaf\xa1\x79\x63\x04\x39\x92\x3d\x73\x49\x43\x63\x52\xda\x17\x1f\x4a\x88\x85\x71\xce\xa7\x38\x89\x83\xf3\x96\xfd\xca\xaa\xad\xf7\xd9\x5e\xb8\xe0\x66\xb3\x0a\x92\x1a\x76\x03\x51\xa3\x32\xd1\xcc\x44\x98\x80\xe8\x1d\x71\xfb\xcb\xab\x79\xd0\x8b\xf1\x44\xf6\xe5\x27\xf0\x0b\xc8\x8a\x6a\x32\xde\x25\x9c\x95\x6d\x6f\xb0\xc4\x3c\xf0\x93\x9b\xb8\xc2\xac\x37\x58\x36\x24\x85\x2f\x1a\x9e\xe9\x8f\x65\x80\x7a\xe6\x53\xd3\x02\x54\x17\x31\xeb\x5a\x55\xaa\x28\xa1\x2c\xd1\xcd\x68\xae\xe8\x66\xba\x0c\xd4\x4f\x95\x7a\x20\x92\x84\x03\xaa\x92\x98\xd1\x62\x42\xe3\x61\x2d\xbf\xaa\x85\x33\xf5\x7c\x3a\xcd\x9b\x2a\xfc\xde\x67\x06\xf7\x36\xcd\x27\x32\x74\xf5\xde\x29\xd5\xe6\x86\x11\xa6\x5c\x83\xe2\x47\x29\x51\xe4\x42\xae\xca\x1d\x33\x2c\xdb\x0e\xa8\xb4\x61\x15\xc1\x8d\x32\xf0\x01\xa9\x7e\x12\x1a\x4f\x58\x46\x94\xf1\xab\xac\x6b\x78\x93\xce\xbf\xb0\x8a\xa7\x15\xa1\x0f\xca\xa7\xe7\xa3\x68\xfb\x88\xcf\xe6\xe5\xa3\x9d\x79\x73\x94\x8a\x76\x57\x24\xc0\x2e\x4a\x62\x2f\xb4\xd8\x6a\x66\xa2\x39\x2f\x3b\x09\x94\xd8\x67\xd0\x50\x62\x67\x48\xc9\xa6\x1d\x1e\xdc\xdc\x43\x21\x8d\xda\x4a\x60\xb7\xc0\x4c\x79\x9e\x6b\xa2\x4d\x9a\xb0\x6d\xc9\xfc\xfb\x7c\x2b\x9a\x41\xf9\x95\x9f\x94\x19\x9d\xa0\x6b\x60\xbf\xa0\xe7\x63\x56\xdb\xac\x21\xe6\x28\xbe\x32\x40\xd9\x7b\xda\xad\x10\x96\xf2\x93\x69\x62\x89\x9a\x5c\x43\x43\x47\xf4\x60\x34\xfd\x74\xca\x51\xf6\xcc\xce\x02\x61\x2b\xc0\x1d\x76\x44\x61\xe2\x24\x3e\xc9\x59\xa5\xea\xa1\x94\xd3\x0d\xa3\x62\x78\x26\x34\x3f\x2c\x7e\xbc\xdf\xfd\xf0\xcd\xe2\xc7\x40\x8e\x97\x57\xe5\xf2\x83\xa0\x5f\x55\x2f\x9a\x4f\x10\xac\xa1\xe0\x21\x99\x9e\xb7\xd8\xfd\x55\x4e\x82\x76\x0b\xb9\x8e\xc8\x07\x15\xa3\x79\xe7\xdc\x64\xcd\xa8\x2f\x4c\x65\x66\xa2\xab\x2b\x05\xbf\x23\x3e\x42\x69\xc7\x18\x89\x33\x23\xa2\x24\xc6\xb1\xa9\xb6\x55\x3f\x42\x09\x26\x4a\x85\xa2\x96\xea\xf8\x6c\x8e\x50\x57\x1c\x25\x91\x76\xaa\x86\x4e\x62\x43\x93\xeb\x82\x04\xb9\xef\x72\x42\x8d\x7d\xcf\xb2\x0a\xab\x47\x7a\xa2\xed\x05\x1f\xe5\x24\xc4\x15\xdd\x7c\x5f\xeb\x74\x95\x2b\x43\x92\x97\x15\x4e\x1c\x6e\xd7\x50\xd9\x41\xa5\xb2\x43\xfe\x75\x98\xc9\x87\x33\x55\xc9\xa1\x14\x9f\x02\xdc\x9f\x8a\x19\xdd\x62\x6a\x4d\x88\xde\xd5\xa5\x48\xde\x18\x3f\x83\xf1\xf2\x91\xf8\x16\x17\x85\x64\xc0\x0f\x94\x82\x79\x5e\xec\xfb\xbe\x61\x31\x66\xc3\xb8\x20\x65\xac\xcf\xcf\x00\x08\x41\x2f\xd6\x87\xc5\x1c\xce\x92\x4a\x62\x38\xc3\x3b\xec\x67\xd1\xd7\xb3\x38\x99\x0e\x4d\xcf\xcc\x00\xb5\x12\xfd\x57\x51\xdf\x44\xd5\x0a\xfa\xc0\xcd\xf5\xd3\x3d\xf9\xba\x2d\x1f\xc6\xbe\x84\x7d\x80\x12\xda\x1f\x29\xed\xb6\xc8\x5b\x64\x8a\x5e\xd8\x36\x92\x9d\x38\xaa\x5b\x8d\xa8\xd1\xa6\x53\x8b\x7c\xc6\x76\xa2\x99\xc4\x83\xae\x30\xcb\x34\x08\x94\x9e\x0d\xda\x8a\xe2\xe3\x78\xfa\xfa\xb4\xc7\xf1\x50\xea\x9b\x66\xde\x5d\x89\xe4\x6f\xdd\xcb\x37\x65\xbd\x4e\x54\x66\xb8\xe3\x01\xbe\xfd\xff\x24\x1f\xff\xca\x03\x7d\x9f\xe9\x52\x94\x6e\x3f\x28\xa2\x5a\x8e\x2d\x99\x6c\x8b\x00\x6f\x9c\xdd\x2f\x65\xcb\xb2\x20\x80\x92\xb5\x3a\x34\x89\xe9\x18\x02\x35\x8c\xac\xc0\x5b\xbf\x77\x35\xf9\x72\xbf\x39\xca\xaf\x85\x47\x88\x65\x82\x1c\xaa\xdc\x03\x63\xa5\xdc\x47\xd1\xf0\x9a\x55\x41\xe3\xbb\x81\x96\xfd\xaf\x74\x26\xd5\xb8\xd9\x68\x32\xca\x90\x42\x27\xf8\x20\x50\x16\xa4\xdf\x67\x7c\xe8\xbf\x19\xb0\xc0\x7c\xa8\x69\x9a\x63\xc3\x90\xf5\xb3\xbf\xb9\x09\x63\x3e\x9b\xe0\x96\xdf\x96\xf1\x02\x07\x5f\x61\xf0\xe7\xe7\x2f\x2f\x4c\x32\x3e\x7f\x99\x7f\x28\xb5\xee\x97\x7d\xbf\xeb\xde\x41\xe7\x22\x0a\x14\xd6\xb6\x9c\x15\x37\xcc\x9a\x4a\xb2\xfe\x40\xc6\x45\x59\x6c\xb5\x93\xfc\x29\x55\x3c\xa1\xf3\x57\x13\xf9\x93\x8e\x65\xa7\xcb\xcb\xc0\xa4\xfd\x9c\xf0\xe6\x82\xf8\x41\x50\x2a\xf5\x4a\xe7\xb7\x91\xfe\xf1\xb7\xac\xd8\xec\x48\x96\x63\x86\xc7\x81\x41\xd5\xb6\x50\x91\x2e\x07\x08\x10\x7d\xbf\x25\x04\x59\x42\x84\xa5\x02\x5f\x3f\x9a\x3f\x74\xaa\xd7\xb4\xb2\x15\xed\xff\x3f\x54\x21\x7f\x33\x27\xed\xeb\xed\xaa\xdf\x6d\x14\x49\x75\x9c\x4e\xb4\x94\x20\xc0\xb7\x46\xa8\x00\x84\x83\x9c\x79\xd8\x9e\x35\x6f\x94\x40\x7c\x72\x52\xf5\xb6\xf8\x74\x57\xc1\x6d\x33\x51\x4e\xa8\x5c\x2c\x64\xc4\x4c\x87\x98\xec\x1e\x02\x67\xd5\xda\x41\x60\x5a\x78\x02\xa9\xea\xe5\x66\xbf\x3a\xd8\x91\x6e\xbf\xa0\x8d\xc4\xfc\xf7\x83\xfb\xdd\x03\xae\xb2\xfe\x40\x87\x76\x1d\xe0\xdf\xc9\xef\x1c\xbf\xbf\xb7\x9b\x45\x12\x90\x55\x28\xc9\xc3\x1d\x23\xf1\x1e\x2b\x3e\x3f\x20\x5c\xcc\x22\xe9\xf1\x02\x47\x40\x7e\x68\x39\x54\x20\x0c\xfb\x9f\x55\x1b\x90\xbd\x08\x01\x67\xf1\x36\x74\xce\x3c\xc0\x9c\x19\xf9\xda\x73\xde\x4c\x2f\xed\x84\x05\x97\x00\x08\xb9\xfa\x9a\x8f\xcb\x0d\x76\xe7\xc1\xe2\xc4\xe9\x4c\x94\x3e\x1d\x2b\xbb\x0f\x94\xef\x69\x83\x4d\x54\x10\xf6\xdd\xc1\x82\xb2\xf6\x28\x44\x23\x5f\x0d\x09\xc7\xb8\x1c\x43\xcd\xe2\x2c\x85\x09\xf7\x6b\xe3\xe5\x94\x30\xcf\xa9\x58\xc9\x2a\x1a\x42\xbf\x16\xb7\xd2\x4e\xb8\x54\x61\x5f\x69\xfd\x96\xe5\xa8\x6e\xcf\x47\x0d\xcb\x5c\xc2\x41\xa5\x33\xca\x4c\x04\xaa\x2a\xd1\xc4\xe1\xea\x09\x9f\x98\x34\xdf\x55\x3f\xc0\xbe\xb0\x6a\xaf\x8b\x18\x57\xac\x95\x07\xa0\x43\xd5\x06\x41\xb9\xfc\x54\x41\xad\xfb\xa2\xfa\x58\xaa\xa8\x1c\x34\x04\xc8\x9b\x65\x1b\xda\xff\x2c\x5e\xc9\xa8\x84\xd5\x6e\x3e\xf2\x8e\xe2\xf6\x38\x57\xca\x89\x9a\x57\x07\xc5\x48\xa2\x42\x37\xee\x9a\xca\xd5\x11\xdf\x7b\xf5\x38\xcb\xb1\x41\x8b\xcd\x75\x71\xd3\x41\x81\x64\x44\x86\x35\xe4\x52\x9c\x29\x08\xf1\x33\x6b\xf4\xca\xdf\xc3\xd0\xae\xb1\x99\xe0\x0b\x05\x3e\x2e\x02\xdb\x71\x0d\xb1\x19\x14\x42\x55\x52\x1f\xdd\xc1\xac\xa7\x0b\x8b\xfc\x2c\xeb\xb2\x18\x22\xd9\xae\x22\x5c\x8c\x2a\xb1\x9f\x28\x7b\xc4\xbc\x1e\x35\xc3\xbc\x17\x91\x60\x99\x6b\x62\x65\x69\x66\xd1\x25\xa7\x43\xd9\x53\xfd\x8f\x84\x77\xaf\x68\x0e\x59\x14\x8c\x6a\x05\x3e\x8d\x68\x55\xec\x1e\x41\xd2\x45\x18\xef\xfa\x6a\xb3\xe1\x99\x36\x3b\x84\xbf\x3a\xce\x23\x47\x2e\xf6\x09\xa6\xa9\xbb\xaa\x76\x79\x03\x6d\xb2\x9f\xc2\x88\xb6\x8e\x5b\xe6\x1b\x93\x12\xb2\x01\x6b\xd5\xdb\xa2\xee\x2e\x4b\xa8\xd7\xb7\xf9\x25\x5f\x68\xcf\xb4\x69\x66\xbe\xc5\xee\xe0\x40\xcb\x22\x66\xa1\x69\x7f\x40\x60\xed\xdc\x42\xa5\x4d\xcb\xf5\x0d\x74\xb8\xe8\x03\x66\x35\xd6\xd4\x59\x1f\x18\xcd\x46\x53\x00\x1e\x38\xb9\x8c\x9b\x9c\x87\xcb\x44\x3e\x97\xf6\x81\x69\x77\x8c\xdb\xcd\xb9\xde\x1f\xc8\x1d\x4e\xba\x48\x94\x22\xad\x8a\x76\x94\x15\xd3\xc9\xd8\xb9\xa8\xbb\xd6\xa7\x1d\x52\x6a\x2b\xbc\x2d\x78\xa7\x0c\x2a\x84\xee\x26\x0a\x3d\x99\x98\x00\xcc\x17\xd4\xc5\xe5\x55\xb2\x37\x2f\x90\x93\x4b\xce\x68\x7b\x66\xbf\x72\xd3\xef\x33\x31\x02\x98\x07\x4d\xfd\x33\x31\x0a\x10\xce\x55\x35\xef\x7d\x6e\xea\x79\xbe\x3e\xb1\x22\xa2\x8c\xbf\xb5\x24\x1f\xa4\xa6\x29\xfd\x5b\x43\x4c\x03\x14\xee\xff\x4e\x5f\xcc\xcb\xd7\x59\x72\x83\x39\x50\x84\xa8\x75\x08\xef\xaf\x33\xda\x16\xc4\xb7\xa8\x89\xc8\x0d\xc9\xe1\xa0\x0d\xd0\xcd\x3c\xb7\xef\x8c\x6f\xc9\x5b\x6c\x94\x73\xfd\x4a\x14\x2f\x52\x48\x34\x6d\xcf\xed\x5b\x53\x43\x12\x6d\xf1\x90\xf2\x4e\x3f\x33\x96\xfb\xb7\x33\x9c\x25\xcc\x98\xe3\xb2\xc3\x9d\x20\xcc\x20\xf0\x3a\x5b\xde\xcc\xc1\xef\x8a\x9e\xa8\x68\x2d\xf2\x99\x10\x34\x5f\x54\xb3\x43\x15\xe1\x8e\x9d\x6b\x61\x3b\x16\x99\xbb\xf7\x59\x34\xb0\x31\xdb\x9a\x29\xe5\xb2\x52\xa4\x4e\x99\xe2\xff\xa0\x4f\xd5\xf1\x80\xda\xe1\x43\x85\x74\xdc\x6e\xdb\x95\x24\xec\x6f\xdc\xcf\x4c\xb5\x62\xa9\x4a\x4c\xf7\xc3\xe3\xfc\x58\x3e\x4c\xdc\xdf\x57\x18\x53\x45\xa2\xc3\x0e\x0b\xe5\xcc\x81\x74\xe5\x42\xa7\xd5\x1a\x2c\xea\xf3\xdb\xb1\x94\x2a\x95\x00\xd1\xed\x86\x09\xe7\x3e\x5f\x70\x38\x69\x95\x55\xd4\x10\x63\x6b\x77\x7b\xc6\x77\x42\x2c\x7a\x13\xd8\x75\xb9\xc8\x59\x69\x4c\x88\x46\x52\xa1\x8e\x73\x5b\x90\x40\xf9\xb1\x2a\x82\xaa\xc9\xf1\x63\x81\x61\x30\x5d\x11\x84\x9e\xfa\x11\xee\xb5\x72\x88\x1f\x72\xbf\x61\xec\x98\x2d\x28\x6b\x50\x04\xf7\xa9\xd6\x4a\x6e\x61\x6a\xb0\x6a\x6c\x72\x63\xe7\xf9\x73\xb6\x62\x82\xed\xc0\xd8\x0e\x8f\x9b\xd0\xdb\xa8\xd7\xfa\x99\xed\x77\x7c\xbd\xe3\xe6\xf2\x1d\x12\xc2\x5c\xa6\xf9\x4e\x08\xc4\xac\x5a\xb1\xa0\x2a\x12\xf0\x95\x93\x0a\xf9\x8e\x43\xb7\xf2\x84\x7d\x9d\x6e\xe7\xd5\x10\x24\x2a\x74\x40\xee\x74\xe0\x58\x28\xb9\xbe\xc6\xd4\xd2\x19\x9d\x5f\xd1\x2e\xda\x54\xf5\x87\x4e\x57\x8a\xe7\xc9\x0b\xc4\x50\x80\x11\x7e\xef\xc5\xbc\x4a\x3e\xc7\xd6\x5c\x76\x0f\x36\xa0\x36\x76\x5b\x26\x37\x82\x4f\x90\x3c\x09\xeb\x6f\xbe\xe5\xc6\xee\xb2\x64\xa6\x1b\x04\xd6\x6e\x17\x69\x94\x4d\xd3\xa9\xba\x35\x12\x34\x4e\x83\x16\x47\xa1\x74\xce\x03\x84\x2e\xc9\x13\xbb\xd3\xc4\x76\xcd\xec\x16\xd2\x3a\x80\xcd\x3f\xaf\xb6\x62\x36\xf9\xce\xee\x28\xb1\x3e\x41\x36\x41\x36\x6c\x68\xd2\xde\xfb\x9b\x99\x37\x8d\xdd\x80\x1a\x65\xb6\xcc\x23\x63\x3f\x64\x06\xc0\x3c\x24\x9d\x1d\xe2\x87\x56\x60\x97\x0c\x77\xa0\x89\x21\x81\xbf\xb5\x92\x85\x0f\xb4\xa7\xd9\x24\x4c\xe6\x33\xbd\x2e\x09\xf9\x3c\xb3\x2e\xff\x0d\xee\x17\x83\x26\x83\x77\xd7\x7c\x00\xa2\xc2\x7f\x02\x39\xc9\xcb\x5b\x5b\x07\xf9\xf8\x41\xef\x47\x7b\xc5\xca\x5d\xb3\x89\x95\x1b\xb8\x62\xf7\x6a\x66\x66\x4b\xac\xb7\x95\xcb\x4a\xd8\x97\x88\x8d\x4d\x8d\x9b\x4e\xa9\xc2\x91\x13\x6d\xf4\x5f\x25\x26\xb1\x66\x11\x84\xba\x20\xff\x3c\x11\x92\xc9\xf7\x18\x62\xf7\x19\xf2\xd5\xf4\x33\xa1\xac\xa5\x19\x6d\x78\xda\xbb\x6b\x09\xed\x88\x99\x48\x69\xf0\x88\xea\x26\x14\x16\x04\xb6\x81\x09\x42\x24\xac\x34\x6e\xad\x8a\x4f\x28\x7c\x59\x4a\x50\x77\xd1\xf6\x60\x5e\x5e\x93\xf5\x38\x09\xb9\x72\xa8\x84\x3e\xd2\x0f\xa1\x87\x32\xd6\x63\x4d\x18\xe4\xdb\x60\x24\xdb\x16\x64\x62\x34\xca\x59\xd9\x91\x51\xd5\x62\x01\x12\xee\x24\x13\xba\x94\x1f\x83\x50\x11\x3a\x88\x76\xdd\xc8\xd4\x4f\xc3\xd6\x23\x1e\xfd\x9c\xea\xe5\x65\x6c\xe9\x2e\xfa\x2a\xa3\x1e\x01\xc7\xe3\xcd\xee\x0a\xc8\x93\xea\xfe\x18\xca\x43\x88\x76\x29\xa4\xce\x93\x5b\x03\x5c\x00\x7c\xc9\x4d\x01\x73\x1a\xff\x05\x97\x04\x49\x53\xf1\x92\x20\x74\x72\xb0\xc3\x46\xa3\x1c\x6f\x35\xca\x00\xd3\xa3\xb8\xec\x58\x19\xc5\xe6\xc0\xd1\x70\x2b\x22\x77\xf1\xf4\x50\x12\xf8\x1e\xc5\x04\x1c\x4a\xcc\x86\xc3\x0e\x0b\xc6\x97\x22\x84\x75\x23\xcd\x77\xba\xe6\x4f\x20\x65\xd2\xa4\x08\x2c\x78\x40\x62\x23\x84\x47\x57\xa9\x75\xcb\xf3\x20\x76\x03\xc1\x26\x6e\x74\xc3\x77\xa4\xa2\xdd\x55\xb5\xbe\xa2\x71\x55\x5b\xbe\x2e\x07\x26\xd9\x9d\x6c\x94\xbc\xf9\x17\x91\xa8\x66\x5d\xb3\x6a\x8d\x5b\x10\x23\xb8\x70\x64\xfd\xd0\xf5\x6d\x53\xaf\x7f\x3c\x6e\x58\x24\x66\x8d\x13\x1f\xab\x3f\xfd\xf0\x8d\xa6\x13\x19\xe6\x25\x64\x8b\xc9\x17\x55\xff\x72\xbf\x78\xd0\xe5\x6b\x36\xfd\xc5\xad\x50\xe1\x0c\x82\xd5\x50\x42\x2c\x14\xaf\xeb\x30\x2d\x30\x0f\xa6\x3d\xde\x35\x1b\xda\x20\x69\x91\x66\xbb\x95\xe5\x25\x02\xb6\x15\x48\xf4\x1f\xb6\x15\x65\x8d\x99\x2b\x5b\x9d\x1f\xaa\x70\x16\x50\x3c\xae\x8f\x2e\x9b\xf1\xa6\x89\x1e\x47\xb9\x43\x06\xc6\xcd\x6f\xdd\xbb\x83\x08\x4a\x1c\x2b\x05\xce\x63\x5c\x0a\xeb\xc8\x5a\xb1\xb1\x06\x09\x62\x0e\x57\x61\xc5\xa9\x24\xf5\x43\x38\x30\x4e\xb3\x16\x85\xf7\x28\x59\x41\x2f\x88\xe5\x90\x97\xcf\x1e\x53\x2f\x83\x47\x0f\xdd\x03\xba\x0e\xf6\xb7\x52\x34\x19\xbb\xd2\x33\x1b\x80\xa3\x68\x3a\x23\x91\xa6\x0d\x61\x12\xaa\x56\x0a\x4d\xb3\x5e\x78\x6a\x26\x26\x59\x42\xd1\x04\x21\x49\x8a\x62\x7a\xfd\x99\xd4\x6c\xd4\x6e\x1c\xb8\x35\xf7\x19\x14\x0d\x63\x7a\x82\xe9\xa0\xb1\x40\xeb\xa3\x0b\xf5\x5a\x75\x3c\xc8\x60\xe3\xe0\x28\xd1\xbd\x69\xf4\x7a\x2f\xb7\x44\xac\x09\x89\x70\x7d\x99\x6c\x65\xee\x04\xcc\x39\xc5\xc0\x08\x6a\xa3\xff\x96\xaf\x0a\xa2\x03\x7d\xf3\x81\x50\x69\x5c\x04\xe9\x87\x0a\x05\xfa\x62\x62\x91\x52\x97\x27\x91\x38\x0c\x05\x25\xbd\x25\x3f\x48\x60\x1c\x5d\xd1\x5a\x83\x91\x97\xe8\xbc\x60\x62\xcf\xa6\x30\x2b\xa1\x23\x4a\x06\xd4\x92\x29\xec\x7f\xe2\xd8\x6a\x06\x82\xe8\xc9\x1f\xfa\xdb\x2f\x4b\x52\xbf\xdb\x2c\x44\xbd\xf7\xb5\x23\x9f\x82\x0e\x73\x99\x8a\x30\xc8\x33\x62\x38\x60\xc7\xf9\x44\x2a\xbc\xe0\xec\x4e\x8d\xa2\xd5\xd8\xc0\x8a\xbc\xd0\x44\xec\x01\x00\xca\x84\x77\x61\x22\xf0\x2b\xaa\x6b\xac\x16\x35\x3e\x51\x13\x4d\xac\x01\x61\x9d\x11\xcc\x2b\x31\x46\xc9\x9f\x9c\xbd\xa2\x03\x23\x34\x68\x95\xfe\x5c\x10\x67\x2e\x5d\xb8\x16\x5d\x0d\x4c\x56\x36\x9b\x21\xc5\x0d\x32\x84\x14\x37\xdb\x75\x94\xc4\x16\x0f\x83\x1a\x0d\x48\x06\x93\xe6\xcb\x1c\x97\x9d\xd3\x5f\x49\x6b\xe8\xc9\xf0\xac\x0a\x43\xfd\x8a\x66\x36\x68\x51\x79\x6b\xed\x6e\x98\xfa\x3b\xe3\xb3\x42\x66\xe8\x1a\xf4\x7b\x60\xf5\x46\x90\xd0\xe2\xe4\xbc\x85\xdb\x40\x3f\xac\xc3\x4a\x41\xfc\x52\x7a\x32\x32\xb9\x98\x91\xa8\x4c\x16\x9b\xa2\x2c\x3b\xab\x27\x1d\xf3\x5d\x74\x86\x11\x3f\xaa\x0c\x6e\xa1\x32\x7e\x54\x0e\x95\xcf\x26\x9b\x0d\x18\x2d\x4d\x0f\xe8\x4d\x2e\xc7\xa0\x98\x61\x70\x2b\x22\xad\x28\x46\x38\x13\x6b\xaa\xe5\xba\xdc\x6c\x68\x3f\x68\xeb\xf1\x8a\x56\x87\x9e\x18\x2c\x28\x90\x93\x6c\xcb\xc8\xdb\xca\x54\x78\x05\xa4\x55\x46\x10\xb4\xdd\x60\xa3\x20\x6a\x07\x3b\xad\x9f\x3d\x79\xf3\xe6\xf4\x22\x1e\xd2\xbc\x0f\xea\x15\xb1\x12\x5f\x05\xfb\xbf\x51\xbf\xcc\x0a\x30\x2c\x60\x0a\x11\xed\x10\xb5\xc4\x21\x38\x4f\xa6\xac\x76\xfa\x5c\x37\xa0\x3d\x0d\xf7\xc5\x68\x79\xd2\xff\xd5\xa1\xf5\xcb\x7e\x65\xee\xe6\x7d\x66\x6a\xfc\x53\xfe\x9b\xf9\x9b\x10\x77\x83\x84\xad\x17\x2f\x9a\xa2\xe7\x02\x75\xa0\x59\x8d\x6e\x46\x40\xa4\xf7\x05\x44\x2d\x9a\xfb\x06\x67\xc5\x65\x8e\x0b\xf9\x23\x56\xf4\x36\x2d\x36\x0c\x4f\xee\xbe\xae\xfe\xbe\x07\x7b\xc6\xe2\x10\x11\x0f\x36\x2b\x5d\x54\x1b\x39\x50\x7e\x09\x3f\x24\x9d\xbf\x06\xd6\xf5\xae\x71\xfa\xf5\x43\xb7\x63\xab\x5c\x3a\x1b\xba\xc7\xf7\xf6\x55\xce\xfa\x42\x36\x30\xbb\xf7\x23\xc9\x2f\x7c\x4d\x4f\xcb\x47\x10\x3f\x8e\xaa\x63\xdf\xbc\xa5\xa8\x19\x83\xa9\x12\xf0\x56\xd3\x79\xb7\x30\xbf\x9b\xe8\x36\x65\xe2\xff\x40\x9b\xec\x08\x18\xc7\xf1\xb5\x4a\xdd\x34\x47\xd8\xbb\x1f\x8b\xcd\x3e\x55\xbe\x70\xeb\x5c\xa6\x7b\x98\xc1\x75\x20\x96\x85\x2d\x12\x5c\x4b\x39\x83\xb0\xe1\x27\x4c\x5a\x7f\xbb\x1f\x19\x3b\xa1\x32\xe3\xf7\x55\x86\x9e\xa8\x6a\x7d\xe8\x93\x88\x3c\xb3\xe9\xe7\x3c\x18\xf6\x23\x75\x62\x35\x9c\x0f\x50\xb1\xe9\x45\xad\x9e\xbb\xd5\x64\xd2\x82\x41\x78\x15\xee\x8d\x5e\x60\x06\x0a\xd6\x2d\xdb\x0a\x7e\x09\x92\xce\x8e\xa9\xb9\x73\x4a\x0d\x89\xb1\xdd\x73\xc2\x7b\x9a\xa2\xd9\xba\xea\x49\x84\x67\x2f\x38\xd8\xb1\x67\x44\x36\xe8\x24\x83\x4b\xab\x7c\x59\xca\xa8\x28\x1f\xfa\x02\x0b\x15\x1c\x73\x9a\xba\x03\xf8\x43\x7f\x4f\x94\x52\x40\x73\xa8\xe5\x7b\x9d\x66\x5e\xd5\x15\x6f\xfc\x57\xf4\x87\x0e\x75\x11\x00\x52\x34\x15\xf6\x16\x95\xa8\xbe\x48\xa4\xef\x50\x8f\x1a\x09\xea\xaa\xa8\x75\xa0\x5b\x17\x35\x7d\xd7\xdb\x01\x4c\x1b\x12\xf2\xa7\x48\x50\x47\x56\xea\x09\xad\xc2\x47\x61\x87\xc4\x21\xf5\x95\xa5\x7c\xcd\xf2\xdf\x43\x03\x34\xe9\x2d\xc0\xa9\x0e\x62\x90\x6f\x8b\xa4\x37\x8d\x7a\xd5\x4e\xbb\x82\x49\x39\x2b\x09\x70\x61\x42\x9d\x5f\xf1\xc5\x46\xb1\xe9\x72\x15\x3a\xed\x06\x3f\xbb\xe6\x7b\x71\x51\xf0\xff\x45\x3f\xa1\xdf\x5f\x17\xbf\x4b\xea\x79\xf8\x01\x34\xeb\x14\xf1\x3a\x55\xd6\xd3\x4c\x2c\xaf\xd4\x02\xad\xb9\x9c\x8b\x37\x19\x8e\xcd\x8b\x70\x81\xca\x7b\x16\x70\x34\xb7\xdb\xe2\x53\xb5\xdd\x6f\xf3\x00\x88\xa2\x8c\x89\xf7\xd3\x6b\x84\xd9\xf4\x65\xc0\xf0\x12\xfd\xcb\xef\x04\x86\x35\xdc\x7e\x35\xc0\xa6\x66\x73\xbe\x19\xb2\x8d\x9d\x58\xaa\x64\xea\x72\x6c\xfe\x95\xc1\xe7\x58\x1c\x2c\x7d\xee\x61\x1a\x69\x5a\xa0\x22\x25\x5b\xb0\xcf\x5d\x10\xd9\xb9\xf7\xa3\x2c\xba\xd1\x2c\xab\x55\x91\xf1\x44\xbd\x9e\x1d\x36\x2a\xc4\x4c\x08\x53\xc4\xa5\x67\xf0\x71\x8a\xa8\x34\x01\x95\x1c\x6b\xca\x5a\x16\xce\x4f\xea\x9b\x17\xaf\x2e\xe0\x25\x45\x38\x29\x2a\x36\x75\x05\x63\xd3\xe5\x59\xa8\x93\x4f\xbc\xaa\xeb\x84\x13\xaa\x2b\x4c\x3c\x53\xa3\x09\x25\x9c\xc8\xed\x5a\x59\x8a\x01\x56\x5b\xb4\x9b\x26\x18\x33\x9a\x7e\x25\x89\x5a\x90\x13\xa1\x10\x48\xaf\xdf\xcc\x8e\xad\xf0\x0e\x7a\x56\x6d\xb8\xef\x8d\xcb\xe6\xaf\x7a\x75\xab\x29\xb5\x55\x07\xf2\xe6\x32\x13\x82\x69\xe9\x4a\x3e\x2f\x03\x1d\x86\x83\x15\xbb\x86\xa4\x04\x18\x8e\xe6\x85\x5f\x77\x6f\xa0\x49\x1b\x85\x59\x96\xdd\xcd\x9c\x15\xf9\xe0\x52\x76\x37\x31\xc1\xb1\x73\x94\x41\xd3\xe9\x80\x83\xa1\xcc\x19\x56\xf9\xff\xfe\xef\xff\xf3\xe8\x19\x0f\xfb\x59\xdf\x6e\xe8\x4b\xb9\x65\x86\x97\x65\x90\x0a\xf2\xd3\xff\x20\xa9\xe7\x5a\xad\x62\xde\xc9\x57\x66\xbf\x41\x0a\x28\xbf\x53\xed\x3b\x3e\x32\xfd\xc5\x14\x21\x53\xbf\x79\x26\x05\x19\x8b\x9c\x8a\x36\x24\x6e\xfa\x03\xe3\xef\xfb\x6a\xf9\x61\x2e\x7a\x92\xc7\xf9\x7f\xf2\xaf\x1c\x0e\xd3\x7a\x66\x32\x1d\x0e\x44\x15\xc8\x39\xa0\xcc\xde\x3a\x1b\x07\x8d\xfa\x55\x44\x22\x5c\xa4\x3c\xc0\x8d\x59\x7d\x1a\x20\x7b\x61\x65\xbb\x3d\xdb\x81\x31\x42\x58\x6b\x67\x94\x02\x8f\x36\x4e\x64\x8e\xcd\xd5\x10\x6e\x84\x93\x3a\xd0\x3c\x75\x57\x1c\x1c\x27\x59\x1d\x64\x45\xa5\x1f\x5b\xfb\x2d\x0a\x1a\xb2\x8a\x1d\x89\x87\x7b\x38\x29\xf4\x84\xe8\xdb\x12\x82\x15\xfd\xc9\xe8\x00\x62\xd3\x41\xbd\x6a\x66\x27\xdf\xbe\xc0\x6d\x2a\xd2\xed\xa2\x99\xef\xcb\x8b\xb5\x56\x04\x89\xea\xa9\x7e\x66\x94\xce\xbf\x2f\xe8\xcf\xc8\x8d\x9f\x9d\xfe\xc7\xce\xfe\x9b\x62\x51\x22\xf9\x35\x3e\x08\xf9\xe9\x0c\xec\x69\x45\xe4\x0c\xb2\x1f\x19\x4f\x49\xd5\x0b\x22\xe2\x2b\x53\x07\x16\xb9\x56\x96\xcf\x0c\x17\x65\x6d\xc1\x97\xbb\x6f\x8b\x6b\xf9\x49\xd3\xa5\xd1\x00\x5e\xca\x97\x24\xc3\xce\x5f\x40\x61\xe6\x1f\xe0\xc1\x34\xeb\x6e\x38\xb3\xef\xcc\x3a\x30\x1b\x77\xc4\x72\x06\xc1\x08\xf2\xe5\x20\xff\x52\x44\xff\xe7\x2c\xf8\x5b\x5a\x01\xaa\x9e\x9b\x21\x62\x48\xdf\xf2\x39\x8a\xdb\xa4\x13\xf9\x0a\x39\x2b\xb1\xff\x3d\x06\x77\xa0\x69\xe6\x77\x71\xca\x7f\x43\x2a\xe1\xa7\xf2\x85\xf4\x37\xf8\x30\x48\x10\x0f\x96\xf9\x25\xfa\x41\x4c\x9e\x0d\xd7\xc2\x65\xd5\xcc\x6a\x2d\x70\x6d\x47\x5b\x0d\xf9\x3e\x7b\x49\xf3\xdf\xce\x43\xf9\x67\xfc\x33\xdf\x8c\x6a\x09\x8b\xeb\xd7\x76\xd0\x8c\x87\xa1\xa6\x26\xc1\xa4\x39\x0f\x29\x2d\x6e\xa7\x80\x49\xce\xab\x13\xd8\x53\x4a\xf0\xa8\x95\x54\xcc\x12\xca\xa0\x66\x08\x2d\xd3\xf0\x74\x60\xb2\x6b\x1c\xc4\x36\xfd\x1c\xf7\xd3\x01\x49\x37\x8b\x09\x50\xd6\x9e\x45\x38\x1a\xf8\x10\x48\xd5\xbb\x81\xfe\x0c\x57\x2f\xae\x0f\x2d\xed\x70\x81\x24\x73\x4e\x3c\xe5\xb2\x0c\x5e\x3a\x00\x02\x2f\xc2\x71\x33\x92\x66\x42\x65\xda\x58\x52\x1f\x26\xb4\x2f\x16\x94\x4d\xbc\x13\xcf\x66\x28\xcc\x73\x15\xb3\x64\xea\x2c\x53\x89\x8b\xd5\x9c\x54\xe9\xf3\x88\x6d\x9a\x0b\x4b\x2c\x13\x11\xd8\xe3\xcd\x44\x89\x5b\x31\x6a\x08\x73\xb0\xe6\x11\xde\x68\xc9\x5b\x96\x37\x42\x70\xa4\x89\xc3\x55\x1f\x28\xa7\x7c\x1b\xb8\xb5\x71\xce\x8c\xfd\xbf\x02\xfd\xe4\xa0\x00\xf2\x63\x12\xb4\xd3\xc0\x3a\x24\x70\xf0\xc9\x1e\xba\xba\x52\x55\xda\x54\x21\x59\xe5\xd5\x7c\x71\xa3\x65\x64\x9d\xe1\x56\x7b\xa0\xc8\x96\x39\x79\x88\x95\x5a\xe4\x24\x24\x4c\x14\xe9\xd4\xc9\x9f\xbd\xec\xc7\x39\x33\x3e\x98\x60\xc2\xc4\xb4\xa9\x9b\x04\x61\x2c\x05\xc8\x29\x3e\xa6\x40\x44\xc1\xac\x2a\x22\x3e\x05\xc4\x9d\xc4\xae\xb8\x27\x1b\x66\xbb\xac\x50\xe2\x35\xac\xb4\xda\xcf\x28\xc7\x36\xcb\x4c\x57\xe5\x3e\xe1\xa4\x81\x21\x31\x7e\xde\xd2\x4e\x2c\x20\x0d\x8d\x4a\xf0\x4e\xc2\x2a\x10\x88\x7c\xe7\xf7\x7f\xfd\xf6\x7d\xc7\xcb\x10\x2f\x6a\x7e\xfd\xf3\x7b\x92\xd3\xef\xff\xfa\xdd\x7b\xdc\xd0\x8c\x0a\xcf\x2f\x59\x45\x39\xae\x01\x05\x0d\x7a\xd7\x96\x1f\xab\x66\xdf\x09\xbf\x86\xcf\x48\x1f\x3e\xc9\x52\x7c\xea\xd3\x2d\x1e\x82\x03\x0c\x76\xf8\x2a\x64\xa5\x3b\xbc\xde\x6f\xe7\x3a\xc6\x4e\x28\x80\xfd\x0a\xc5\x6d\x06\xe6\x05\x37\xf9\x5b\xf8\xcd\xc3\xad\x56\x3c\x58\xea\xbc\xa9\x27\xfe\x24\xbf\x7e\xc4\x40\x78\xe8\xbf\x85\x96\x1a\x77\xbb\x73\x21\xf1\x12\x98\xfb\x0e\xb7\x4c\x37\x65\x3f\x4b\xa9\x92\xc5\xfc\x41\x97\xd3\x2c\xed\x45\x04\xd1\x75\x83\xa9\xb6\x07\x6f\x4b\x4c\x8c\xc1\xbd\xc5\xcf\x41\xe6\x6d\x95\xb5\x49\x01\x25\xb5\x11\x4b\x14\x74\x30\xd7\x3a\x53\x72\x0c\x7d\xd9\x34\x49\x7b\xa1\x0e\xfb\xf9\x85\xb5\x08\x3f\x41\x0c\xec\x65\xa8\xe7\x92\x66\xbc\x5e\xe2\x26\x00\x97\x25\x3c\x54\x35\xf3\x15\xe8\x2f\x6c\x62\xd7\x68\x28\xb3\x33\x7c\x58\xb2\xa8\xde\xd4\x0b\x23\xe0\x66\xa2\xa6\xd4\x44\xf3\xca\xbb\x84\xe0\x04\x8a\x6d\x9e\x78\x7c\x5f\xc6\x49\x09\x68\x55\xcf\xcd\x9b\x43\x25\x08\x22\x96\x6c\xc0\x28\x23\x22\x34\x62\x87\x65\xd5\x7c\x1f\x74\x94\x4c\x2e\x53\xcd\xf1\x52\x6e\xd2\x79\x21\xfd\x6e\x2d\x57\xd0\x05\xfd\x4c\x7f\xc2\xbc\x0e\x0c\xa1\xac\x7f\xdc\x0a\x75\x9f\xfe\x58\x92\x9c\x8b\xb6\xe9\xe2\xb9\x9d\xe6\x2f\x9b\x4d\x13\xcf\x75\xfc\x1a\x02\x88\x26\xfa\xfe\x6a\xc0\x9b\x49\x76\xc4\x6d\xdd\xbd\x9c\x30\x38\x79\x04\x72\x62\x30\x92\x31\xb0\x20\x4c\x33\x83\x73\x91\x74\x10\x2e\x46\x16\x13\x63\x5c\x8b\x1a\xcb\x01\x34\xa8\xc2\x27\xc1\xa6\x6d\x44\x84\xcf\xf0\xb7\x1c\xcc\xb5\x7b\xbb\x10\xbe\xe5\x77\x17\x1f\x5a\xf7\xe1\x7b\x8e\xe9\xc6\xa3\x84\x2c\x7d\xbd\xe3\x42\xd5\x91\xca\x5d\xd1\xf6\xd5\xb2\xda\x15\x81\x5c\x9e\xb9\x94\x4c\x04\x26\xc7\xaf\x7b\xc1\x49\x33\x59\xa7\x5e\x10\x0e\x8b\x41\x91\x0a\x25\x9c\xa2\xd6\x5b\xdd\x34\x9c\xcd\x98\x01\xf7\xd7\x4d\x1e\xc4\x39\x04\xec\xe3\x13\xa5\xc8\xb9\xb0\xf9\x70\x62\x1f\x69\xf9\xd9\xa0\x5a\xb8\xee\x3f\x86\x31\xe6\xb0\x41\x6d\xe1\x71\xae\x5f\x9a\x9f\x48\x9a\x43\x09\xd3\x46\xde\xb0\x02\x6e\xbf\xc1\xec\xe0\x3e\x59\x7e\x5c\xca\x4d\xa8\x01\x21\xb4\x19\x73\x3f\xb1\x2d\x77\x22\x48\xe0\x33\xb5\x6e\xe1\xdc\x45\xb9\x2c\x60\xa7\x0d\x3f\xbe\x9a\x35\xe6\xc5\xca\x8d\x9e\x40\x38\x44\x8a\xd5\xcf\x66\xef\x3e\x5c\x1d\xd3\xbf\x50\xbd\x69\x51\x06\x33\xb5\x28\xfb\x6b\xb8\xb7\xc0\xdc\x84\x27\x57\xd4\xef\xdd\xf7\xfe\x54\x27\x52\xf8\x0d\xda\xf8\x86\x8f\xf6\x95\x92\xc5\x3f\xe1\x87\x10\x47\x9d\xca\x01\xe3\x3f\x81\x06\xa0\x0c\xb6\xa8\xd7\xc0\x27\x1a\xf1\xb6\xa4\x46\xc1\x0e\xac\x4c\x16\x15\x22\xfd\x03\xeb\x03\x8c\x0a\xe3\x9b\xf6\x02\x9b\x93\x68\xfa\x77\x21\x5d\xeb\x47\x4d\x7a\xea\x5b\x33\x92\xf6\xaf\x55\x4f\xa5\xff\xed\xbd\xe1\x28\x89\x0d\x73\x4f\x77\x81\x9f\xf1\x67\x02\x35\x14\xc1\x63\x9e\xe8\xd0\x81\x50\xa5\x19\xaf\xae\x34\x5f\x4f\x68\x42\x15\x99\x9a\xa0\xbf\x96\x0c\xbd\x2d\xf5\x2b\x49\xbd\xde\x95\x2d\x93\x0c\x9d\xcd\x70\x69\x38\x4b\xa6\x06\xec\x70\x1b\x5b\x62\xac\x09\x39\x17\xa3\x6a\x03\x8d\x50\x98\x94\x44\x48\x15\x1c\xa0\x8d\xf6\x87\x5d\x15\xd3\xaf\x70\x29\x34\x5d\x97\xc2\xae\xf6\xd1\xa7\x83\x67\x91\x0a\x41\x67\xe6\x28\x9f\xf5\xbd\xea\xe6\x30\x10\x13\x93\xf9\x0b\xb5\xfa\xda\x54\xcb\x3e\x0f\xe9\xde\xbd\x61\xd7\x36\x6b\x09\xc0\x14\xdc\x19\xe8\x60\xed\xae\x10\xd2\x85\x01\x2e\x89\x4a\x6d\x1b\x70\x7c\x81\x44\x14\xf5\x1c\x97\x21\x18\x6a\xa2\xe5\x4d\x86\xa1\x2a\x5f\x9d\x90\x41\xa0\x96\x50\x15\x34\xea\x9f\x57\x9b\xdc\xc6\x4f\xd5\x17\x48\x80\x38\x1d\xf1\x8e\xb7\x71\x77\x87\xdb\x1a\x46\x1b\x14\x7c\xd8\x16\xb5\xdc\x6e\x56\xec\x8e\xc4\x72\xb5\xf8\x23\xc3\xd6\xaa\xbf\x9a\xa8\x59\x6a\x1b\x90\x14\x20\xcf\xd4\xce\x06\xc2\xee\x6b\xdd\x80\x28\x05\xcd\x21\xa3\xf8\x6f\xaa\xe5\x7d\xd0\x07\x24\x55\x44\x8e\x57\xdb\xe9\x50\x3d\xc9\xaa\xe5\x78\x4f\xa6\xed\xeb\x3f\xdd\x5f\x3d\x94\x4d\x0c\x9b\xab\xd1\x4d\x15\x27\xca\xc0\xfd\x41\xca\x54\xb4\xea\xe0\xbd\xcf\x28\xc3\x07\x05\x03\xd1\xf7\xec\xb7\xcc\x69\xf4\xdc\x59\x16\x65\x75\x97\x3d\xa1\x58\x70\xb9\xd3\xca\x85\x21\xc0\x2a\xaa\x6c\xee\x77\x49\xdb\xcd\x9c\xb6\xc6\x5c\x25\x3f\x3a\x4e\x78\xa3\xf0\xaf\x61\x0f\x4c\xe2\x19\xd6\x1c\x64\x87\x74\x40\xc4\x00\x2c\xf8\x08\x11\xbf\x74\x21\xd1\x4e\x89\x49\xe8\xa0\x1e\x4a\x6a\x5b\xa0\x0c\x40\x52\xfd\x80\xc2\x4f\x4e\x8e\x71\x7f\x70\x8a\xf6\x19\x13\xf7\xa5\x3e\x37\x8e\xf9\x98\x06\xcc\x6a\xc3\xfc\x6b\x8b\xff\xf6\x30\x1d\x64\x29\x46\xf1\xfc\xd7\x67\x84\x88\x3d\x5a\xd5\x5c\x56\x5e\x6b\x44\xe5\x9a\x12\x23\xc8\x1c\x05\x9f\xe2\x07\x37\xf4\xef\xd1\x76\xfb\x68\xb5\x7a\x30\x31\x6a\xc7\x3f\x85\x61\x0f\x2c\xf1\x54\x59\x31\xa0\x92\xae\x26\xc7\x8e\x4e\xcf\x1d\x03\x24\xeb\xf4\x8e\xcf\x7e\x3e\xa7\x99\xe9\x58\xc5\x99\x13\xdc\x8d\xab\xd7\x31\xfd\x6f\x88\xda\x45\xfb\x1e\xde\xd0\x62\xba\xe8\xc7\x32\x60\xe5\x5d\xd6\xc0\xa5\xfe\xd6\x0e\x86\xbb\x16\x65\xe7\x88\x76\x6f\x0f\x4c\x8a\x44\x81\x3c\x38\x25\x8e\x85\x8e\xd3\x1a\xd8\xe8\x09\xc0\x69\x26\x3a\xb6\xfe\x5f\xc9\x48\x4f\x35\x3f\x85\x06\x77\xb0\xd2\xd9\x75\xf5\xa1\xa2\x02\x7f\xa1\x3f\xf8\x9e\x69\x10\x84\x3c\x06\x3d\xa0\x76\x39\xfb\xab\x24\xdf\xc6\xca\x39\x8c\xb3\x4c\xa7\xa1\x1a\xcd\x25\xf4\xa2\xd8\x73\xed\x37\x7c\x01\xf3\x41\x4e\xd3\x66\xb9\x87\x90\x7e\xa3\xae\x37\x7f\x83\x1f\x4c\x43\x4c\xdd\x95\xc6\xec\x03\xcb\x5c\xf5\x8a\x54\x33\x69\x50\x71\x1c\x0e\x85\x73\x8d\xbc\xad\x9b\xbc\x47\x0c\x59\x4a\xc7\xe9\x29\xe0\x3e\x36\x37\x12\x94\x4d\xd6\x74\x65\x92\x23\xbc\xf8\x53\xf8\x5a\xdf\x68\x54\x37\xc9\x37\xeb\x00\x95\xe0\xe3\xa5\xc2\x5f\x10\x15\xb2\x60\xfe\x98\x2d\xd6\xd8\xba\x18\xeb\xad\x9a\xb1\x48\x20\x74\x1c\x08\x82\xa5\x2d\xb1\x60\xea\xda\x80\x3d\xa8\x36\xc0\x48\xc1\xd4\xb9\xcb\x19\xa1\x4d\x3d\x80\x72\x44\x8c\x01\x0e\x4c\xe7\x94\xb9\x86\x47\x51\x31\x32\x19\x4f\xcc\x4b\xc7\x83\x3c\x3d\xc1\xe2\x39\x88\x65\x44\x23\x03\x6b\xe1\xc4\x52\xb8\x9b\xc5\x3a\x3a\x9d\xe4\xce\x0d\xc1\xfc\x62\xcc\xaa\x55\x7f\x72\xf4\xa7\xa9\xa8\xd8\x96\x36\x93\xa9\x82\x33\x9d\x7c\xc5\x2c\x17\xae\x4a\xd9\x59\xf7\x3b\x82\x5d\x35\xcd\x07\x09\xf3\xb5\xc0\x67\xcc\x59\x73\xac\x5c\xc9\xe4\xa8\xb0\x2f\xd3\x5c\x92\x60\xaa\xa5\x8f\xbe\xfd\x94\x13\x26\xba\xa8\xbe\x68\xa7\x16\xb3\x8d\xcd\x9a\x62\xae\x7a\x11\xb9\x7a\xd4\xe9\x69\x5c\x91\x7a\xbc\x30\x5b\xf0\x79\x9e\x62\x53\x1e\x62\xa9\x03\xfd\x2c\xd6\x5e\xac\x3e\x32\xf5\x5c\x25\x11\xca\x35\x6d\xa2\x33\xbc\x74\xc1\xaa\x54\x9c\xa2\x40\x10\xd8\x16\x15\xc6\xfd\x46\xb9\x81\xeb\xb6\x83\x5d\x05\x44\xc5\x09\x89\xd8\x06\xa3\xd8\xcc\x95\x96\xf0\xc1\x60\x69\xa8\x2e\x82\x27\xa0\x1a\xf5\xf5\x67\x03\x05\x0e\x72\xc0\x91\xc3\xe0\xd6\xd7\x5f\x92\x60\x89\x0b\x9e\x0c\xb1\x11\x10\x8d\x3d\xdb\x38\x20\x2e\x34\xcd\xd3\x4d\x1a\x50\x88\x78\x1f\x37\x5d\x42\x66\x07\x33\xc0\xc1\xf6\x7a\x36\x38\x65\x03\xd5\xeb\x12\x66\xaa\xa2\x9e\xeb\xdb\x82\x23\x8c\x1f\x18\x3e\x60\xe6\x0a\x33\x9c\x87\x03\x15\x68\x02\xc6\x16\x58\x82\x30\x23\xb8\x9f\xca\x2f\xb4\x46\x1e\xd5\x73\xc0\xdc\x56\xde\x87\x81\x64\x81\x80\x39\x57\x7f\x67\x25\x12\xe4\x3f\xd8\xb8\xe8\x9f\xf9\x3f\x18\x89\xe8\x4f\x55\xaf\xca\x4f\xff\x34\x89\x32\x04\xc9\x64\x04\x3d\x1a\xd9\x23\x0a\xab\xca\x3d\x43\x31\x37\x9d\xe0\xb7\x07\xb3\xe9\x99\xe3\xce\x8c\x9c\x09\xe1\xd5\x81\x95\x4f\xb4\xb6\x22\x5a\x95\x6e\xfb\x15\x6f\x83\x76\xfe\xbb\xdc\x4a\x1d\xe3\x57\xfe\xbf\xf8\xc4\x0f\x20\x08\xf1\x8f\x58\x10\x2c\x29\x76\x62\x6b\xa4\x3e\xed\xe2\x8f\x28\x5a\xce\x22\x68\x1e\xbb\xd4\x86\x23\xa5\x74\x31\xce\x9a\x38\x34\x16\xb5\xb8\x6f\x89\x8f\xaa\xa3\x47\x2c\x4e\xf7\x41\xb0\xee\xf3\x0b\x0e\xf9\xbc\xde\x6f\x88\xf1\x73\x66\x3c\xc3\x02\xc3\x65\xb1\x7a\x94\x45\x80\x19\x08\x4f\x0e\x47\x16\x43\x5d\x6e\x7b\x07\x83\x1e\x8d\x93\xd3\x72\x90\x4e\xf1\xfc\x18\xb6\xc2\x4e\x68\x74\xbc\xcf\xbf\xa5\xa6\x1e\xa9\x73\x7f\x6a\xa3\x9b\x34\xec\x66\x43\xfb\x00\x2d\xc3\x54\x2f\x44\x93\x1f\xfa\x20\xa6\xba\x13\x3d\x88\x71\x7e\xcd\x58\x57\x35\x10\x09\xbd\x36\x68\xb1\x28\x1f\x98\x76\x45\xae\x55\xa0\x2c\x60\x98\x74\x09\xd7\x7e\xa9\xe7\xa5\xdf\x0e\x12\x83\x80\x8d\x31\xf4\xf3\xd4\xa2\x18\x8c\xc1\x82\xf0\x1b\x43\x17\xa4\x93\xc2\x73\xa1\x78\x80\x0d\xa1\x8b\x94\x86\xcd\x60\xde\x2d\x84\xd8\x56\x05\x37\x94\xee\xb0\x70\xef\x26\xba\x37\x58\x26\xc6\x09\x89\x8a\x0d\xc4\x13\x26\xae\xba\x74\x38\x0c\x57\x0a\x76\x8d\xf8\x58\xad\x48\x56\x46\x67\x6e\xab\xf7\xcf\x69\xbd\x34\x8f\xb0\xf4\x3a\x58\xf7\x60\x40\xd8\xe1\xe1\xa9\x05\x44\x9f\xb8\x8c\x41\x55\x26\x47\xc4\xc4\x27\x68\xd7\x75\x27\x21\x38\x4a\x1e\x43\x0a\x78\x7e\x57\x98\x59\xe0\x87\x78\x5b\x19\x96\x7e\xef\x88\xc0\xc0\x97\x1e\xe8\x3c\xb5\xc7\xd4\xba\x5b\x57\xf3\x94\x0e\x3b\xf1\x1d\x91\x62\x45\x12\xb4\xa3\x77\x37\x5d\x62\xef\xcd\x66\x3e\x61\xf7\xb3\x74\x42\x28\x87\xb0\x11\xc3\xfa\x6f\xdd\x5c\x77\x9a\xb5\x1f\xae\xef\xcf\x07\x37\xab\x33\x3e\xb7\xd1\xf0\x32\xf0\xfe\xd7\xe7\x28\x4c\x9f\xe5\x87\x28\xa6\xa4\x78\x1d\x83\x8d\xea\x8a\xbe\x38\x52\x81\xe2\x28\xdc\x26\x4a\x04\x00\xe7\xa7\x10\x19\xad\x26\x59\xe1\x41\x5f\x61\x26\x27\x13\xf0\x6a\xd4\x74\x0c\x4c\x70\x14\x6d\xac\xed\x8c\x86\x2c\xc1\x27\xc6\x8e\x43\x84\xf3\x6d\xde\xa5\x48\x8e\x22\xbd\xdc\xd1\xe4\x9f\x6f\x6b\x52\xec\xc2\x27\xda\x0c\x1e\x12\x1a\x14\x08\xd4\x9e\x5f\x17\xb9\xa3\xb5\xef\xac\x35\x2f\x7c\x7d\x28\xcb\x9d\x6b\x22\xed\xbe\x73\x17\x05\x07\x97\x5a\x6a\x4f\xd0\x15\x3d\xff\x2c\x3e\x4c\xd2\x9b\x54\x98\x9b\xa2\x8c\xd3\xd2\xdc\x21\x7e\x74\xba\x32\xe3\xba\xef\x70\x6b\x1f\xef\x75\xbb\x3d\xc4\xd3\x3a\xb8\x41\x0c\x30\x2c\xc1\xcd\x1d\x15\x86\xe7\x8f\x91\xd7\x89\xaa\x26\xe9\x7f\x8c\x3b\x13\xba\x66\x05\xda\xc3\xdd\x4b\x9d\x46\xf2\x09\x67\x11\xc7\x25\x73\x54\xc5\x88\xef\xb9\x78\x61\xf2\x78\x9e\xb9\xe4\xc3\x05\x06\xde\x8f\x49\x5d\xa9\xf7\xa3\xeb\xa0\xe0\xe2\xa1\x7a\x9e\x4d\xd6\xa1\xf8\xeb\x6a\x11\x0b\x39\x89\xc6\x9c\xda\x22\xa9\xc9\x1c\xce\x86\x99\x2b\x81\x98\x64\xd1\x8c\x9a\x75\xf7\x8b\x72\x38\xfe\x24\x44\x59\x6a\x49\xad\x7e\x19\xe2\xcc\x09\x26\xca\x97\x9d\x25\xdc\x70\xcb\x56\x96\xdc\xa0\x06\x8c\xd5\x87\xac\x86\x31\x86\x34\xf7\xfa\xaa\x71\x2c\xc5\xdd\x0d\xf0\x32\x5c\x8b\x84\xa7\x4b\xa6\xf2\xde\x40\x10\x0c\xca\x09\x95\x06\xa1\x9f\xde\xee\x69\x72\xa0\x8c\x80\xd0\xa7\x4f\x58\x9c\x9e\x5f\xe0\xae\x8a\x76\x38\x1d\xd7\x6b\x3e\xdd\xf2\xbf\x90\xe8\x80\x38\xe8\xfc\x92\x84\x92\xd9\xe5\x92\x3d\xbc\xab\x5a\xa3\x44\x5f\x97\xe6\x7a\x57\xaf\x36\x42\x94\xbd\xff\xbf\xc9\x68\x72\x67\x95\xe3\x79\x07\xbc\x51\xb3\x2b\x97\xd5\x25\x71\x74\xaf\x69\xad\x6a\xbc\xe9\x15\x1e\x14\xba\xd5\xc3\x24\x8c\x04\x16\xb8\x7c\xb5\xe5\xa6\x45\xa7\xc4\xdf\xf9\xea\x89\x39\x9a\x9e\x21\xe8\x94\xaf\x9b\xcd\xf0\x6d\xea\x2a\x9c\x1e\xe2\xf1\x55\xf1\x61\x94\xab\x85\xe8\x6d\x8e\xb4\x07\xfb\xe0\xa3\x74\x4b\xd3\x9f\x4b\xe7\xb4\xaa\x19\xc2\xba\x87\xbe\x70\xbc\xbc\x0e\xce\x5f\xf8\x7d\x07\xb8\x4d\xc1\xb9\x84\xa3\x85\x21\x10\xdb\x1c\x2b\x5a\x84\x5a\x2d\x46\x3c\xd8\x15\x9b\xa3\x6e\x2c\x52\x4f\xb6\x11\x87\x88\xae\x5d\x0f\xc7\x29\xb8\x2f\x77\x4e\xd2\x1c\x09\x4c\xfb\x12\xef\x78\x6c\x8b\x1b\x09\x44\xce\x57\x43\x1d\x9d\xa3\xf5\xaa\xb3\x17\xaa\xf8\x15\xbd\xab\xe6\x9a\xb5\x4e\xe6\x1e\x32\x5a\x92\x71\xdf\xe2\xa5\x89\xdd\x94\x4c\x80\x74\xbb\x46\xfc\x7d\xde\xea\xe7\x18\x48\x74\xc1\x3c\xaa\x97\xf2\x35\x06\xd9\x69\x20\xd1\x10\x52\x74\x0c\xb2\x68\x56\xbc\x66\x4f\xe9\xcf\x58\x6d\x13\x9e\xd5\x32\xdd\x0d\xf6\xf2\x8e\x83\x59\x89\xa9\x1d\x67\x10\x76\x96\x9b\x4b\x89\x4a\xc6\xc2\x15\x94\xd9\x72\x8b\xc9\x8e\x72\x12\x8c\x9f\x1d\xbb\x50\x81\xce\x13\x3c\x93\x11\x58\xd8\x5f\x51\x6a\xe8\x18\x1f\xb4\x63\xd8\x27\x98\x6e\x58\xbf\x5e\x09\x63\x8c\xd5\x84\xea\x5e\x62\x40\x1f\x31\xab\xc1\x9a\x13\x33\xa6\x32\x86\x64\x27\x71\x9f\x39\x5c\x0a\xd1\x00\x44\xfa\x33\x10\x91\x2c\xc4\x44\xdd\xf9\xac\xd9\x5b\x3c\xd8\x9b\x98\xb0\x71\x8f\xd4\xc7\x90\x27\x48\xbc\x0b\x47\x10\xd1\xd4\x0b\x40\xe6\xcd\x3f\xe4\x18\x14\x3c\x2a\xb0\x5e\x26\xe4\xc3\x11\xe0\xe4\xbd\x33\x74\x54\xe3\x39\x8b\x82\x81\x09\xab\xe9\x13\xdc\x4d\x30\xcf\x15\xeb\x31\x1c\x31\x64\xb6\x8d\x38\x65\x11\xc7\x49\xc8\x2b\xda\x95\xc5\x3f\x54\xc2\xcc\x9e\xd2\x20\xc0\x3e\xc8\x4c\xb1\xe9\x1a\xab\x82\x0e\x12\x02\xf9\xc0\xb6\xe1\xb4\xde\xcc\xf0\xda\x6d\x0a\x8b\x30\x51\x05\xcd\xb4\x78\xbf\x63\xf2\x2c\xb4\xde\xda\xc1\x90\xbf\xfe\xf7\xf3\xd3\x37\x47\xf9\xa7\x47\xd7\xd7\xd7\x8f\xb8\xf8\xa3\x7d\xbb\xe1\xf8\x0d\x2b\x8e\xaf\xf8\x3f\x4f\x5e\x1f\xe5\x65\xbf\x7c\x38\xcb\x4f\x84\x6a\x47\x62\xa8\xf7\xae\xb0\xa9\xc0\x25\x26\x11\x88\x3f\x4e\xcd\x75\xc7\xa8\x76\xcc\x07\xe2\xf5\xac\x0e\xaf\x9e\x59\xc0\xea\x62\x8a\x25\xac\x63\x14\x96\x6d\x09\x03\x52\x7c\xb8\x0c\xe2\x1a\x3e\x4c\x45\xb3\x1a\x82\x54\xd4\x8e\x76\xe3\xd5\x52\xdf\x64\x1a\x80\x98\xbd\xd4\x33\x58\x4a\x45\xc5\x1d\x2f\x5c\x38\x85\x59\x13\x47\x54\x8a\x2f\x03\x92\x03\x66\x51\xda\x42\x94\xab\x9f\x86\x85\xe1\xf6\x81\x67\x50\x1e\xe7\xff\xce\xf7\x60\xbc\x50\x82\x5b\x9c\x65\xb8\x05\xe0\xd9\xb0\x30\x42\x74\x3b\x29\x89\x06\x20\x81\xc7\x4d\x4a\x8b\x79\x41\x52\x1b\x55\xa2\x8a\x20\xb6\x3c\x25\x22\x1c\x14\x43\xc0\x35\xa9\x6e\x5c\x24\xbd\x85\x9c\xce\xb6\x79\x11\x8f\x8f\x23\xf5\x05\xb1\x2b\xba\xa9\x79\xc8\xc5\xe7\x65\x72\x8a\x1c\x7d\x04\xa8\xaa\x6f\xc6\x42\xb2\x10\xa6\x5c\xe3\xd1\x97\xc3\x0c\x17\x01\xac\xec\x11\x22\x69\x6a\x2f\x8a\x9e\x23\xac\x5a\xdc\x3d\x46\xdf\xf4\xf4\x11\x46\x4e\xdc\xcb\x13\xea\x01\xd2\x91\x72\xcf\xd3\x87\xe1\x6c\x44\x9b\x22\xe7\xa7\xb4\x69\xc4\xdd\x28\xe0\xa0\x8d\x11\x53\xa1\x72\xce\x58\x06\x8c\x2d\x1c\xe2\x9f\xc4\xce\xd9\xce\x75\x0b\xc8\x88\x18\x1d\xc7\x21\x2d\xe5\x46\x6d\x97\x82\xee\xa6\x5b\x94\x27\x44\xf6\x91\xa7\xa8\xcc\xd7\xa6\x56\x91\x0c\x82\xc0\x32\xec\x83\x6a\xee\x6e\xa3\xb0\x3a\xfe\xa4\x97\x5a\x2d\x48\x82\x04\x73\x18\x64\x0e\x1f\xa1\x1b\xee\x6c\x62\x6d\xe5\x01\xd4\x67\xf2\xe5\x67\x6b\xb7\x69\x6e\x2c\xf2\xd0\x31\x7e\x69\x1c\x45\x3f\xb2\x08\xa6\x83\x8a\x90\x53\x75\x45\x56\x14\x50\xa8\x1d\xca\x34\x56\xdf\x3e\x92\x87\xa2\xb0\xa4\xac\x0f\xa5\x3a\xcd\x1d\x31\xc8\xf5\x45\x1a\xa6\x41\x9e\x20\xd2\x00\x3e\x6e\x6a\xe0\xf0\xe6\x07\xf0\x57\xf7\x7c\x81\xca\x20\x35\xab\x5b\x42\x37\xbc\x0c\x9c\x58\x10\x4c\x8d\x62\x1c\x31\x27\x40\x0d\x23\xfb\xc4\x91\x1e\x8c\xec\xe3\x8b\xfa\xf0\x3e\xae\x28\xce\xcd\x30\x09\x93\x97\x76\xc9\xb2\x8c\x83\xf7\xc4\xa1\x7e\x46\xfc\x9e\xe9\x95\x1b\x0a\x1e\x77\x2e\xf5\x6d\x37\xe6\x2b\x3f\xb8\xcf\x88\xe4\x33\xd0\xae\x7c\x8e\x0c\x32\xd5\x97\x38\x29\x6e\x76\xef\xba\x3f\x5f\x55\x97\x97\xb3\x45\x4b\x2c\x38\x87\xcb\xc1\xbb\x6f\x4c\xd9\xf9\x77\x7e\x8e\xdf\x02\xc2\x76\x93\xc0\x0a\xf9\x90\x44\xb5\xf3\x7e\xac\xb6\x7f\x92\x08\xa3\xb5\xe1\x73\x53\xc7\x94\x23\x06\x6c\x6f\x08\xe5\x9f\x58\xce\x4c\x8a\xb0\x08\x30\xe7\x2f\x04\xfa\xc1\x1d\x2b\xdf\x1a\xa2\xd0\x39\xa7\x38\xb0\x6e\xb7\x21\xee\x55\xdf\x36\x3b\xe7\x1f\xf0\xdd\x73\x10\xfb\x9a\xe4\xd8\x72\x65\x30\xef\xe4\xa7\x87\xe2\x2a\x6d\xe9\xec\x44\x85\xab\x82\x98\x0a\x0a\xeb\x1d\x55\xa0\xc0\x50\x83\x23\x30\x42\xab\x0a\xbc\x75\x04\xf1\x81\x41\x08\xc2\xd6\x24\x42\xe8\x44\x83\x60\x3d\x7d\xf5\x46\x7e\xc2\xff\x50\x03\x7f\xc2\x01\x91\x2d\x16\x33\xf3\x6a\x9c\x4d\x79\x37\x5a\x9e\x38\xa5\x8a\xca\xce\x1e\xa4\xc6\xaf\x00\xb1\x6a\x8b\x4b\x18\xe8\xf0\xdf\x90\x4a\xfc\x7b\x2c\x76\xd6\x96\x8f\x86\xc5\x68\x72\x64\xc9\xce\xf1\x11\xd2\xd5\xc0\x86\xff\x84\xb4\x02\x76\xb3\x8f\xdd\xc8\xe3\x8c\x98\x81\x26\xe1\xef\xfd\x2e\xef\x2a\x56\x6a\x2b\xa2\x0f\x1a\x04\x96\xc5\x57\x42\x80\x83\x70\x53\xf5\x63\xf5\x96\x3b\x88\x0d\xd6\x5d\xe5\x61\x7e\xd8\x93\xbb\x97\xb8\x3f\xfa\xa4\xe1\x2c\xe9\x77\x52\x5a\xd8\x83\xd2\x56\x1b\xef\x62\x32\x07\x8c\xa8\x19\x12\xae\x71\xcf\x6f\x40\xd2\x44\x70\x70\x5a\xe6\x96\xc2\x26\xaa\xb6\x54\xff\x47\x79\x60\x48\xaa\x27\xce\x27\xc4\x25\x22\x26\xa8\x96\xd0\x28\x96\x07\xed\x89\x05\x69\x4e\xca\xc4\x67\x4c\xec\xba\x2e\xba\x03\x53\x3e\xb8\xaa\xa5\xf7\x32\x66\x16\x8b\xe3\xb4\xc9\xd8\x5d\x07\x12\x8a\x6e\xa9\x63\x2a\x6e\x39\x72\x15\xae\xe6\x1f\x1e\x2d\x74\xbb\x9c\xc9\x57\xc8\x61\xee\x5d\x58\xd0\xd7\xf2\x25\x0f\x6c\x0f\xb1\x69\x1c\x39\x8b\xf2\x1e\x0d\xd7\xda\xc1\x87\x09\xf8\x4b\xf9\x80\x6f\x0a\x1a\xe2\x0d\x72\x31\x42\x29\xfa\x04\x53\x4c\x9d\x17\x5f\xab\x7c\x84\xe3\x21\x76\x63\x68\x7a\x15\x9a\x53\x44\x89\x28\x33\xc2\x76\x36\x6a\xb1\x9d\x02\xab\x96\x74\xbb\x00\x7b\xe2\x86\x81\x79\xd9\x68\xa3\x09\xf3\x15\xa1\xd2\xeb\x9e\x09\x60\x39\x6a\x34\x2b\xea\x77\x87\x30\xd3\xa7\x8b\xb5\xe3\x23\x16\x89\x95\x69\x2b\x4a\x8f\x70\x73\x42\x28\x73\xcb\x59\x32\x6a\xcd\x2b\xd9\xa5\x89\x3b\x0e\x8f\xe1\x1e\x48\xcd\x72\x5c\x3d\x7a\xc4\xb3\x99\x95\xee\x91\xd1\x11\x1f\x7a\xa3\x2f\x0c\xe2\x18\xb3\xef\x2c\xfb\xb5\x69\xd7\xef\xe3\xeb\x14\x41\x67\x9c\xa8\x7d\xa1\x39\x60\x98\x10\x91\xf9\x00\x60\x8c\xd2\x1c\x6b\x34\x74\x7c\xc1\x9b\x6e\xfc\xbc\xb3\xe8\x6d\xe4\x11\x21\x98\x7a\x59\xd8\xa2\x99\x85\x09\x90\xc0\xf8\x6a\x83\x95\xbc\xd8\x0f\xef\xfd\x68\x5b\xf4\x4e\x5d\x0c\xd5\x36\x85\x3d\xcc\xf9\x83\xdf\x2e\xe0\x17\xc6\x59\x69\x2b\x66\x03\xaf\x90\x40\x24\x11\x09\x78\x5b\x43\x34\x70\xf4\x37\x43\x3c\x74\x55\x53\x73\xaa\x7e\x69\xfa\x20\xe6\x7a\x12\x23\xdd\x85\x35\xc0\x4b\x0c\x89\xe1\x18\x57\x8e\x59\x99\x30\x29\x0d\x8f\x7b\x68\x27\x64\x0a\x91\x7a\x1b\x74\x12\x13\x88\xf7\xba\x18\x23\xf3\xae\x2e\xc4\x3c\x4f\xfd\x4a\x15\x45\xf0\x3a\x44\x9d\xf8\x5f\x75\xb3\xd8\x8c\xa3\x1c\x57\x62\x6f\x1a\x8b\xe1\xa9\x58\x36\x65\xfb\x49\xe0\x93\x50\x20\x2a\xcc\x17\x12\xd3\x4b\x92\xf3\x0d\xc9\x85\x9b\x44\xba\x47\x45\xcc\x4f\xff\x74\x20\x26\xfc\xf8\x35\x94\x2f\x0f\x04\x33\xae\xe3\xf6\x50\x30\x7f\xd4\xfc\x6c\x3a\x36\xb9\x57\x61\x0e\x82\x94\x87\xac\xa9\x68\xe5\x7f\xc4\x5e\x2c\x05\x75\x44\x26\x99\x82\x50\xd3\xe7\x5e\xe6\xa9\x19\x1a\x61\xea\xbf\x66\x85\x96\xbe\x1f\x32\xec\xf5\x28\xac\x76\xd2\xe9\x2f\x0b\xaf\x7d\xd0\x2c\x20\xa1\x15\x43\x91\x7e\x14\xd3\x0e\x03\xbc\xb5\x48\x1a\xe1\xce\x77\x38\x28\x71\xdd\xb5\xbc\xde\xd0\x49\x6c\x3b\xb9\xc8\xb9\x3b\xc0\xdd\x81\x4b\xcb\xdb\x22\xdd\x0d\x7b\xc9\x34\x26\x78\x17\xfb\x4e\xde\x5a\xc2\x73\x19\xcd\xe0\xca\xef\x8f\x47\xbf\x9b\xbe\x7d\x63\x91\xff\xda\x34\x9d\xe0\x4a\x6c\xfe\xa2\xfe\x88\xc5\x37\x9b\x2f\x91\xef\x22\xa1\x8d\x33\x07\x7e\x52\x26\x77\xf0\x5e\x8d\x52\xed\x59\x7c\xee\x64\x9e\x44\xbc\x3b\x89\x0f\xaa\xc4\xe0\x77\xdf\x87\x62\x7a\x09\x6f\xe1\x72\x07\xe9\x91\x52\xc2\x30\x59\xcd\x0c\x22\x90\xfc\x06\xcf\x37\x99\x33\x2c\x9f\xb6\xa1\x0f\x6c\xb6\xcd\xa6\x0c\x1d\xcd\xdf\xd2\xaf\xd8\xbd\xd4\x4d\x37\x2d\x18\xca\x84\x74\x15\x93\xf5\x0d\xa1\xd8\x1b\x79\x1f\x06\x0e\xf4\x2e\x55\x4f\x4b\xb7\x56\xc2\x27\x6b\xed\x10\x3b\xbe\x1f\x42\xcb\x83\x9d\x7a\xae\xbe\xe1\x57\x3f\x70\xa8\xce\xe0\xf5\x2b\x6f\x96\x68\x4a\xda\xa8\xa4\x31\xc7\xa2\x41\x57\x73\x09\x07\xa7\x61\x39\xc7\xf9\x83\x88\x5b\x38\x53\x42\xac\x2d\x7b\x80\x88\x19\x6e\xf5\x2f\xaf\xe5\x8a\x32\x0d\x41\x25\xb5\x82\x61\x8f\xcd\x8a\xd9\x76\xd2\xae\x87\xf8\x9c\x86\xb9\x9f\xa3\xe6\x8e\x4c\xe1\x09\x3d\x94\x6a\x62\x25\x58\xb5\xb4\x22\x0f\x19\x87\x7e\x84\xa7\xb2\x63\x3f\x3c\xc4\xe7\xf4\x83\x5b\x81\xfb\xa3\x08\x70\xb7\xf4\x87\x24\x6e\x8d\x46\x9f\x98\xf9\x0c\xbb\x18\x63\x41\x5d\xb8\x93\x1c\xa6\x52\xab\x01\x67\xc2\xf7\x0b\xe3\x23\x55\x72\xc4\x7e\x63\x82\x79\x10\x53\xbc\xc9\x78\xb5\x77\x13\x01\xf8\x99\x72\xc9\x00\xea\x8c\xec\x22\xd8\xe4\xb9\x24\xfd\x8a\xcc\x1e\xb8\x2f\xa5\x0d\x9a\x79\xf7\x91\x2c\x70\x16\xa9\x55\x38\x3f\x7f\xa8\x80\xf5\xb3\x95\x5c\x01\x22\x9a\x41\xf0\x06\x73\xad\x8e\x2b\x0b\xc4\x1c\x50\x81\x88\x8f\xe1\x6c\xc7\x7a\xbe\xcd\x69\xdf\x99\x6c\x1f\x19\x37\x1b\x6c\x8f\x00\xc5\x17\xc9\xde\xc4\x90\x23\xec\x36\xde\x45\xad\xba\xd5\xe5\x64\xdc\x95\x78\xae\xcb\x13\x67\x01\x61\x0e\x0a\x3d\x33\xbf\xd5\xc7\x08\x12\xd1\x6e\xdd\xc2\x05\xd7\xd6\x9a\x89\x85\x43\x05\x54\xf8\x7d\x18\x65\x78\x4b\x3f\x52\x03\x58\x5f\x50\x45\x0f\x6e\x23\x0a\x5f\xd0\x01\x90\x8d\xdb\x7b\x00\xb2\x20\x11\x1c\xa8\x1b\x8e\x04\xdc\xd6\x11\x7d\x04\xff\xf3\x3b\x02\xba\xf1\x99\x1d\x39\xb2\x5e\xe8\x83\x3f\xab\xd5\xe4\xfe\xbf\xad\x7f\x03\x41\x08\xc8\x99\xbc\x40\x65\xc4\x00\x56\x49\xf2\xae\xfc\x94\x55\x92\x53\xcf\xce\x66\xc3\x5d\xe2\x2c\xe3\xdc\x4e\x71\x96\x9e\xd6\x17\x18\x50\xa9\x95\xb7\x9e\x72\xb1\xaa\x9a\xd6\x1d\xf1\xec\xeb\xde\x5b\x82\xa7\x51\xfb\xd8\xa0\xb5\x6f\x6f\x94\xd3\xe1\x19\x49\x83\x0e\x46\x3b\x3e\x91\xe9\x60\x48\x20\x4f\x99\xfd\x8a\xb5\x7a\x9f\x85\x97\xd8\x79\xff\xdb\x77\x26\x9a\x2f\xb9\x4c\xc5\xdb\x50\xf1\x51\xa8\x7c\xf8\x48\xd4\xad\x2f\x7a\xa5\x2f\x99\x0d\x1f\xb4\xeb\x24\x38\xf2\xda\x58\x44\x7b\x33\x3f\x53\xfb\x45\x9e\xf1\x1b\x9a\x83\x2d\x2b\x8a\x39\x21\xdb\x36\x75\x25\x36\x5e\x27\xf2\x55\xf1\x8b\x5e\xde\x9b\xea\x39\xff\x90\xa0\xf4\x9a\xc2\xee\x3b\x59\xdf\xf4\x88\x76\x7a\xc1\x7f\xbf\xcf\xef\xaf\xb2\x38\x74\xe8\x80\x59\xdd\xb6\x14\x4d\xa7\x7c\xbb\x7c\xf7\x9c\x14\x7c\x41\x5b\x7b\x1f\x2b\xd6\x80\x6e\x42\x63\xbd\x77\xdd\xd6\x4e\xa2\xd2\x7d\x37\xd5\xa2\xb9\x48\xc1\xf2\x80\xb5\xe5\x0b\xd3\xb5\xf0\xbb\xd4\x2b\x7e\x97\x5a\xf4\x90\x47\x2e\x21\x59\x10\x9f\xb1\x0b\x0f\x25\x24\xc9\xe9\x51\x1a\xd3\x25\xe6\x6a\x92\xc4\xa1\x15\x93\x84\x62\x39\x6a\xc5\xee\x2b\x7c\x9a\x99\xe4\xc6\x14\x33\xce\x4d\x6a\x4f\xa3\xe5\xfb\x2c\xb1\xac\x4e\x92\xf4\x39\xfe\x74\x24\xa2\xe5\xf5\x69\x9b\x66\xcd\xaf\xce\x41\x57\x9c\x0e\x4f\xf9\xf5\xb4\x4e\x73\x35\x4c\xaa\x40\xf4\x13\x9f\x82\xab\xd3\xbe\xe8\xd2\xd2\xd8\x9f\x3e\x41\xfd\xe3\x46\x80\x24\xbf\x17\xcb\x2b\x75\x7c\x9f\x40\x24\x13\xc3\x03\x32\x89\x2c\x3e\x05\xd9\x5d\x57\x12\x0b\xf3\x1c\x1f\x93\x30\xed\x1e\x3a\xc4\x7d\xed\x72\xd9\x7d\x97\x5d\xc5\xf1\xa0\x40\xa3\x91\x63\xd9\x97\x37\x3c\x1e\x90\x9f\x62\x3b\x76\xb7\x16\x72\xe7\x22\xc7\x1d\xd3\x07\x0b\xb4\xa4\xf0\x30\xb7\x1c\x90\xb1\x66\x3d\x6a\xd5\xa6\xc8\x3d\xa2\xdd\x45\xce\xa3\x40\x58\x01\xbd\xdb\xb7\xec\xcf\xaa\x63\xd0\xcb\x08\x11\xaa\xf9\xf2\xae\x82\xfe\x33\xbd\xa7\xde\x0c\x3a\x99\xd0\x3c\x03\xb9\xa3\x86\x41\x17\x27\xab\xf8\xf2\x4e\xe2\xa4\xad\xd7\x72\xea\x1c\xe8\xe4\x0d\x5e\x9c\x68\x57\x2a\xb8\x6e\xd8\x80\xf3\x85\x19\x95\xdd\x51\xe5\xa1\x5e\xdf\x5a\xe7\x17\x0c\x63\x5d\xf5\xf3\xf5\x32\x76\x9f\x5f\xdb\x69\x17\x4c\xb8\xf9\x70\x2f\x97\x12\xb2\xa2\x4e\x95\x96\xd3\xc5\x6f\x9b\x60\x74\x88\xd5\x15\x53\xd5\x1f\xea\x5b\x5b\x76\x37\xf5\x92\x15\x75\xfc\x38\x91\x5e\xb0\xbf\x2d\xe5\xd2\xe4\xc1\x8c\xd2\xbe\x29\x34\x08\x73\x89\xab\xe8\xee\x81\x3c\xf1\xf6\xf5\xb2\x80\x0f\xd8\xf7\x74\x14\xd7\x8f\x40\xda\x51\xda\x38\x5b\x9e\xad\x87\xb7\x36\x34\x18\x8b\xa3\xeb\x6e\x6e\x5b\x74\xa5\x2f\x3f\x6b\x04\xce\x9c\xc4\x0f\x83\x11\x45\x89\x18\x48\xde\xe0\x71\xd4\xfc\x6b\x36\x0d\xe2\xf7\x93\xd8\xee\x49\xed\x09\xf5\xd0\x46\xf8\xe3\xa0\x60\x5b\x1d\x18\x90\x6f\xf7\x96\x15\x7a\x90\xf4\xe2\xcb\xc6\xc8\x61\xd5\x47\x1b\xe1\x2d\x92\x35\xcc\xfa\x1f\xda\x0e\x53\x15\xff\xab\xdb\xa1\x75\xbd\x1a\x3d\xb9\xe7\xd8\x03\x84\xb2\xa6\xb9\x63\xf7\x0e\xf0\x9d\x08\x6d\xfd\x0e\xbf\x3d\xb9\x96\x77\xf0\xe6\xeb\xa6\x6d\x08\xe3\x24\x3e\xa9\xbe\x8d\xf7\xc2\xd2\xba\x89\x02\xb8\xb1\xb8\x99\xef\xd5\xfd\xdb\xca\x9c\x20\x99\xd8\x3e\xf6\xb0\x8e\xa5\xc0\x3c\x59\x19\xd6\x43\x2f\xf5\xf6\x02\xdc\x94\x95\x7a\x62\x19\xae\xa4\x96\x69\x16\xec\x84\xa4\x61\x71\x00\x7c\xaa\x29\x0e\x16\xb7\x7e\x1c\xf4\x93\x30\x60\xbf\x9b\xf3\x50\xe1\x97\x2d\xc9\xf9\x6b\x24\xe7\x17\x9c\x3c\x6e\xc1\x7a\x15\x8a\x0d\x3a\x75\xa8\x1c\x07\x82\x1b\x96\x79\xce\xf1\xe2\x86\xf0\x36\x73\x57\x65\xb1\x1b\xcd\xdb\x4b\x4a\x1c\xcd\x1a\x20\xc7\x13\x00\xd8\xc3\xb3\xe0\x4b\x55\x2b\x08\xd1\xbe\xc4\x2b\x4a\x3a\x04\x0d\x5b\x9c\x21\x7c\xcd\x4c\xfc\x81\x12\xca\x4d\x0d\x7b\xa5\x37\x75\xa3\x5e\x35\x0b\x8e\x72\xd0\x19\xf4\xa9\xfc\x74\x50\x8b\xa6\xe9\x49\x96\x23\x50\x62\x23\x61\x96\x29\xd3\xf4\xd4\xd2\x99\x11\x5e\x7e\x18\xcd\x94\x40\x8f\xa7\x4a\xa0\x0f\xcf\xd5\x96\xe3\xd0\x53\x5b\xed\x7e\xd9\xef\x89\xe6\x84\x06\x4f\xce\x39\x7e\xfd\x79\xc8\x18\xb5\x38\x2a\xe9\x31\x74\x58\x78\xaa\xe5\x25\xbf\x22\x30\xd9\xf4\x33\xce\xb9\xb5\xed\x51\x59\xdf\xf8\xa8\xf8\xd4\x4e\xc1\x53\xaf\x4c\x94\x16\xfb\xe5\x87\xb2\x67\xa7\xe1\xab\x39\x4c\x34\x7c\x5d\x67\x06\x96\x3f\x05\x58\xfe\x92\xc0\xf2\x0b\x68\xdc\x26\x6a\xa5\x73\x74\x5b\xf6\x05\x4c\x76\x5c\x2d\x2f\x9e\xd1\x0a\x48\xf2\x54\x29\x68\xe2\xe6\x2a\xff\xe8\x2e\x64\x96\xd4\xd5\x70\x0a\x65\x9d\x8a\x44\x4f\x02\xc8\x54\x6d\x1c\x7a\x54\x0e\xf4\xe5\xcd\x52\x5e\x40\xe5\x60\xa4\xd4\x87\xb7\x92\xe2\x60\x21\xe3\x11\xac\xd1\x48\x58\x95\x20\x56\x03\x81\x5f\xa4\x84\x52\x28\x58\x04\x16\xc2\x45\x70\x67\x1c\x0d\x69\x0a\x70\x57\xc8\x66\x3a\x08\x69\xcd\x1b\xa0\xb5\x3c\x84\xd3\x46\x3b\x99\x4a\x21\x2b\x22\x60\x8b\x8f\x91\xbe\xdf\x45\x38\x07\xab\x05\xb8\x18\xd9\xeb\x5d\x9c\xa6\xb0\x78\xa7\x36\xde\xa8\xc4\x4b\xda\xf0\x28\xb3\x80\x89\x5c\x01\x69\x42\x52\x8c\x13\xc6\x7b\xf2\xf6\x6d\x79\x49\x3c\x4d\x49\x8b\x07\x28\xfd\xd5\x34\x8b\xb8\x13\xe2\x02\x6b\x3a\x4c\x93\xdb\x72\xcd\x8a\x0a\x71\xd8\x45\x64\x1b\x78\x9f\xbc\x45\xb2\x49\x37\xde\x9f\xe8\xa2\xc1\x28\xdd\xc0\x52\x13\x3e\x1b\xe6\xdd\x51\x7f\x66\x5a\x87\x8f\x3f\xa9\x23\x83\xe8\x62\x36\x6c\x83\x77\xf4\xd5\x96\x4d\x20\xe5\xd9\x06\xb9\xd8\xdc\xf8\xd2\x90\x2b\x4d\x50\x1b\xd4\xf0\x1a\x32\xa7\x9b\xe5\xf0\x08\x6c\x50\x75\xe3\xb2\x80\x55\x2e\xe2\x04\x01\x55\x3b\x8c\x54\xf7\xb5\x3d\x32\x6b\x68\x70\xe8\x5d\x67\x7b\xf9\xe9\x33\x9f\x76\x8e\x73\xe1\x30\x05\x56\x2a\x29\x8e\x6c\x8b\x4f\xfa\x86\x41\x7c\x24\xe5\x44\x9f\x43\x71\x9e\x8b\xcf\x2c\xf7\x35\xbf\x8c\x72\xa8\xac\xe9\xf8\xbe\x3e\x27\x02\xf3\xe8\x5b\xbc\xd1\x45\xfb\x61\xbd\x69\x16\x05\x9b\xa4\xc8\xeb\x33\x78\x5c\xe5\xa1\xd6\x51\x75\x73\x8f\x94\xc3\x87\xab\x8a\x01\x92\x32\xb8\xe2\x69\x02\x8a\x18\x08\x9c\x21\x68\xd6\xb4\xee\x2a\xdf\x10\x17\x97\xe6\xec\x08\x32\x57\x5b\xe8\x51\x0d\xae\x0c\x34\xc4\xb2\xb1\x98\x77\x93\xd8\x2b\xbe\x1e\x79\x21\x64\x6e\x18\x73\x57\x5d\x07\x1f\x14\x99\x5c\xf7\xa8\xa4\xb7\x65\x0f\xef\x7e\x03\xfa\xd6\x3b\xe0\x74\x81\x11\xa0\x8f\x3d\x5b\xa3\xea\xd0\xf5\x54\xc2\xf7\x71\x7f\xa3\x6f\x6a\xc3\xec\x25\x33\xae\x88\x4b\xcd\x72\x92\xf7\xc1\x0e\xa1\x00\x2c\x8e\x60\x09\x3b\xff\x18\x68\x80\xe8\xba\x2a\x16\x7d\x07\x38\x94\x85\x18\xc2\x1c\x68\x3f\x5e\x74\x22\x64\x94\x6f\xde\xab\xb8\xd2\x0e\xc8\x65\x5c\xd3\x7a\x73\xa7\x54\x43\x99\x74\x65\xc2\xa2\xe9\xc9\xf0\x31\xc5\x03\xe6\xb0\xfc\x26\x3e\x1c\x4e\x07\x24\x3a\xb9\xa1\x4e\x48\x35\x4a\x78\x12\x8c\x84\xd4\x56\x07\x49\xf1\xf6\xc6\x2e\x6e\x44\x8f\x0a\xea\x3b\x6c\xcf\xed\xc9\xa4\x35\x29\x31\x7e\xa5\x2d\xed\x82\xa4\x8c\xef\x77\x25\x5d\x55\x80\xb9\xbd\xba\xa4\xfa\xdc\x19\xf4\x80\xc2\x82\xb5\x96\x36\x7c\xc0\x03\xea\x5d\x25\x96\x83\x2e\x0f\xc8\x65\xd2\x6d\x29\x25\xa1\xff\xcc\x53\x59\x29\xb2\x66\xb9\xde\x4b\x8a\x8f\x8e\x2f\x29\xf2\xea\x39\x93\x0f\x09\xcd\xb2\xd2\xf4\xb1\x65\x95\xeb\xa4\x56\x33\xe8\x9c\xab\x15\x50\xd3\x14\xdf\xf5\x66\xe8\x6c\x20\xa9\xf0\x8b\x65\xcf\x88\xae\xd7\x94\x9d\xc4\xbc\x3f\xe3\x98\xf7\x92\x02\xb5\xda\x0a\x66\xbf\xac\x45\x3b\x7e\xe3\xd3\xdd\x43\xe8\xc8\x0d\x4f\xa0\x4f\xc0\x38\xbb\xa7\xa2\xe5\x90\xfb\xdf\x6b\xd0\x4b\xf7\x20\x3a\x3b\x4c\xca\x73\xa8\xbb\x0d\xf7\x97\xdf\x71\xc2\x8d\x18\xdf\x0f\xec\x11\xf0\x8e\x5f\x2c\x86\x29\x00\x91\x99\xb5\x58\xa2\xcb\x1b\x9b\x3a\x99\xcc\x9f\x68\x2c\x5f\xf0\x25\x7a\xff\xf1\x94\x8d\x18\x1d\x08\x46\x04\x80\x30\xa2\xa2\x97\x48\x38\xe5\x94\x6f\x56\x1e\x72\x0f\x42\x0f\x9f\xa6\xc0\xa6\x0f\x27\x29\xf7\x9e\xc3\x76\x3f\xaa\x10\xa1\x8f\xa9\x4f\xb9\x59\xa9\xa3\x61\x12\xf1\x67\x36\x6a\xc1\x0c\xa0\x10\x6a\xed\x8e\xde\x74\x7b\xeb\xfa\xf9\xfe\xae\x9e\xeb\x03\xde\xf2\x2e\xf8\x41\xb0\x8e\xc5\x34\x7d\xf3\xf5\x79\xc9\x8a\xdd\x98\xc5\x4b\xa5\x78\x21\x3e\x14\x9f\x0c\x6f\x24\xec\x9b\x4d\xb2\x84\x7d\xb3\x9a\x71\xe7\x16\x00\xe4\x26\x3e\x81\xd8\xf2\xf9\x39\xef\x0a\x26\x36\x74\x52\xac\xf2\xf3\x27\x9a\xd3\x6d\xfb\x9d\xbd\x29\x76\x7e\x72\x71\x76\xcb\xce\x60\x50\xc5\x70\x40\x3a\x34\xe7\x2c\x45\x75\x64\x39\x7c\x37\x2f\x7d\xd9\x31\xaa\x35\x81\xd5\x9b\x6c\x9d\x6e\x1a\xee\x36\x26\x4a\x1e\xd0\xa5\x13\x9f\xa3\xd2\xc2\xb7\x45\xca\xcc\xf2\x13\x62\x34\x2a\x36\xa2\xb4\xd6\xd4\x90\x6f\x41\xb8\x52\xee\x8a\xd6\xde\xb3\xc0\x63\x4a\xf9\x83\xa3\x07\xb3\x84\x96\xcc\x7b\x79\x69\x5f\x22\x3d\x5d\xbc\x3e\xa7\xcf\x65\x7b\x23\x56\x02\x3a\xd2\x0f\xd5\x8e\xc1\xe6\xec\x1a\x25\x9c\x2e\xa5\x00\xf6\x17\xa4\xd8\xc6\xe7\xeb\xe4\xb2\xfd\x58\x2d\x03\xba\x9d\x3d\x39\x81\x16\x87\x92\x3c\x29\xd1\xa6\x11\x56\xd6\xf8\xe8\xd8\x09\x5a\x8e\x26\xe1\xa3\x8d\x1c\x56\x3b\x9c\x27\xf4\xc7\xea\x71\x11\x2f\x87\xcc\xae\x5c\xf9\xdb\x4c\x8f\x18\xaf\x14\xda\xf1\x5f\x91\x50\x0f\xf9\xf3\xb4\xc8\x5d\x6e\x36\xb3\x84\x34\xfb\x73\x38\xad\xe7\x33\x4d\xe7\x7c\x65\x8e\x67\xba\x6d\xd0\x93\xb1\x77\xd2\x12\x09\xe4\x5c\x4e\x0b\x7b\x7f\x35\xad\x3a\xbe\xb7\x3b\x2a\x91\xbc\xc4\x3a\x9a\xd7\x09\x93\xb4\x5b\xcc\xd0\x5c\xed\x03\xee\x25\xad\xf8\x2e\x26\x46\x94\xb5\xa6\x51\x0c\xf7\xad\xaa\x51\x4c\xaf\x5d\x15\xb6\xd8\xed\xc2\x21\xe8\x9e\xd8\x05\xda\x3a\x90\x8f\x42\x70\x1c\xc4\x2f\x1a\x19\xcb\x01\x89\xc7\xad\x07\x62\xc7\x5b\x05\x18\x1e\xa4\x9a\xdc\x5c\x5e\xf2\x43\x67\xfc\x3e\x80\x86\x29\xe4\x9f\x1c\x2f\x35\xb4\xaf\x7e\xe4\x73\x56\x71\x42\x65\xc8\x63\x3a\x56\xe7\xf2\xb7\x48\x64\x19\xcd\xc0\xdb\x3d\x74\x63\xdc\xdf\xb7\xfb\x5a\xa4\x4f\x97\xa5\x0d\x71\x96\x6f\x04\xbc\x58\xdb\x34\xbd\xbd\xf4\xe7\x18\xb1\xb7\x94\x4c\x27\x74\x7f\x15\x26\x98\x6f\x74\x97\x73\x79\x70\xcc\x95\xc1\x7d\xf2\x12\xce\x3c\xe3\x42\xd4\xef\x71\x09\xea\xf7\x01\x70\xb1\x5a\x32\x36\xe6\x1c\xbf\x84\x48\x87\x1e\x23\x98\x9a\x60\xa3\x0d\x58\xd2\x86\x78\xe3\xe7\x60\xb5\x88\x88\x71\x6c\x77\xd0\x93\xa8\x41\x90\x9e\x17\x8b\xa9\x9e\xfb\x89\xa9\x9e\x93\x8b\xa9\xda\xb1\x41\x0f\xba\x6e\x63\x0b\x71\x7e\xfe\x3a\x5d\xed\x98\xeb\x5e\xc6\xe5\x73\xfe\x1e\x3f\x14\xc2\x31\xc8\xef\xe5\xec\x54\xf9\xd0\x95\xd0\xd9\xf4\xf3\xa7\xa9\xc3\x3a\xba\xbf\x6f\xaa\xbe\xfc\xee\x1e\xec\x4a\xee\xf5\xd5\x6a\x71\xef\xa1\xdf\x37\x15\x3c\x92\xdc\xc6\xa9\x96\x07\xa6\x27\xa8\x42\x58\xd2\xdf\xb8\xf0\x71\x6f\xe5\x6d\x11\xe5\x0e\xd4\x1e\x36\x9d\x59\xc3\xe8\x78\x0a\x04\x7c\xf6\x27\x80\xf5\x8b\x9d\xdb\x5a\x97\x11\x43\xa4\xc2\x5d\xee\xad\x55\xf3\x14\xc9\xb1\x83\xf2\xc6\x89\xbd\x79\xa2\x7e\x3e\xd6\x3d\x3c\x59\xf2\xaa\x16\xf7\x38\x2d\x82\x91\x04\xd5\xce\x09\xf7\xdf\x6b\x73\x86\xfd\x87\x47\x66\xf5\x7b\x29\x91\xe4\xdd\x28\x4e\x48\x50\x67\x0d\xc3\x7f\x94\x37\xd4\xce\xef\xfc\x22\x05\x01\x58\x71\x3d\x08\x97\xc5\x8e\x78\xa2\x22\x1e\x81\xcf\x24\x21\x90\x14\x71\xef\x66\xff\xb0\xf9\x46\xef\x65\xc5\x05\x1c\x5e\x62\xf9\x6b\x5c\xc4\x86\xc1\xd2\x91\x12\x59\xe6\xa4\xd0\x5b\xce\x0b\x2c\xf6\x44\x61\x8b\x0c\x11\x16\xde\x3c\xaf\x27\x17\x1e\x01\x4e\xe6\x9b\xb2\x5e\x03\xe9\xfe\x93\x7f\x12\xf3\xc2\x3f\xc3\x04\x89\x4b\x35\x94\x81\xec\xda\xf4\xd8\x9c\xac\xa1\x13\xa4\x94\xb0\xb4\x77\x72\x19\x6e\x65\x3c\x4d\x3f\xc1\xef\xe9\x0e\x2a\xec\x58\x6c\x4a\xf3\x6d\x15\x69\x87\x34\x6e\xed\x5e\xfe\xfc\xfa\x74\x00\x39\xb1\xb5\x35\x67\x82\x14\x68\xce\xc4\xc6\x17\x1b\x83\x30\x04\xd8\x15\x4c\x8f\x40\x20\x0f\x0e\x40\x94\xea\xd1\x9e\x08\x6a\xf4\xc9\x8a\x04\xb2\x58\x11\x66\x89\xd3\x1d\x40\x9f\xc8\xef\x14\xc8\x3d\x93\x2c\x50\xf6\x4a\xf2\xa8\xd5\xda\xb7\x59\xcb\xc5\x72\xa4\x21\x62\x0d\xe7\x68\x88\xf8\x9b\x4c\x76\xcf\xa0\xd9\xb9\xb0\x32\xb3\x33\x81\x3f\xd3\x24\x03\x35\x90\x58\xb3\x41\x68\xd5\xa1\x9b\x84\xb8\x55\xe0\x45\x9f\xe1\x57\xb2\x74\xba\xfd\x78\xbf\x08\x6c\xdc\x81\xac\xd7\x93\x12\x06\xbc\x5e\x86\x89\x31\x7d\xfa\x8b\x67\xf1\x01\x69\xa8\xde\x07\x83\xd9\x54\x97\x65\x50\xd4\xeb\x68\x5e\x53\x5a\x02\x7c\xd5\xf7\xbb\xce\xc2\x64\xe0\x91\xdf\xfc\x94\x7e\x0c\x06\xe1\xab\xd2\x91\x8c\x6a\xda\x55\xb8\x3c\x71\xf3\x22\x09\xd3\x33\x6e\xd0\x4a\xeb\x1d\xb8\x12\xfb\x21\x8d\x5b\xb7\xea\xed\x11\x77\xc8\x0b\x4d\xf2\x27\x7b\x68\x9d\x4f\xf4\xc9\x96\x19\x4a\xcf\x3c\x86\xc1\x99\x67\x36\x6e\xb3\x65\x2b\x41\xa2\xf9\xcf\x05\x1b\x18\x85\x1c\xbf\xf7\x2c\xad\xe3\x57\xad\xf7\xe2\xac\xab\x9f\x11\x3e\x3e\xe8\x26\xd3\x64\x19\x13\x8f\xc0\xa5\x00\xe5\xa7\x72\xb9\x77\xb7\xaa\x3f\xcb\x6f\xbd\xc6\x88\xd5\x34\x66\x07\xbf\xaf\xf1\x00\xe0\x99\xa4\x38\x98\xa9\x57\x09\xac\xeb\x34\x6f\xbd\xea\x9a\xfa\xc3\xed\x87\xe6\x21\x9a\x32\x94\xd9\x09\x9a\xf9\x9d\xfc\x9c\x6f\xc4\xe1\x70\x60\x3a\x68\xb0\xf0\xb3\x5e\xc1\xef\x77\x1e\xfc\x80\xe1\x70\x2d\x90\xea\x13\x1c\xe0\xd5\x00\x4e\x0f\x1a\x56\x2d\x87\x56\x4b\xb6\x20\xe1\x4b\xaf\x3c\xbc\xfc\x4e\xf2\x6d\xc8\xa7\x21\x7b\x88\x63\xfd\x99\xc0\x54\xb5\x70\x6c\x92\x25\xba\xf8\x57\x92\xa6\x55\x3a\x7b\x48\x13\x36\xc2\x63\x92\x41\xa2\x39\xd7\x94\x21\xa4\xb5\x0c\x20\xb6\x58\x18\xce\x86\xe7\xe6\x7c\x1a\xc2\x62\x3a\xa3\x55\x37\xa6\xe1\x32\x5a\x56\xb3\x63\x02\xbe\x9b\x8d\x7a\x1b\x44\x17\x5d\x11\xb3\xee\xbc\xcb\x4a\x28\xfb\x55\xe6\xfe\xfd\xe0\xd5\x7b\xbb\x09\x72\xd6\x17\x49\x88\x91\xfb\xf2\x2c\x62\x5b\xd6\x2e\x8e\xb2\xfc\x5a\x8d\x9e\x57\xb6\x47\x88\xbe\x8d\x8f\x10\xb1\x05\xff\xc1\xc7\x16\xc3\xe3\x77\xa8\x95\x4d\x92\x25\xd6\xd9\xe0\x21\xa7\xae\x5d\x7e\x33\x2c\xcb\x1a\xee\x14\x8c\x33\xff\xcd\x2a\x96\x31\xda\x33\x81\xbf\xe9\xd3\x7c\xf2\xdb\x8d\xef\x1b\x51\xc3\x7e\x23\x23\xfd\x93\x7b\x3b\x4f\x6b\x18\xbe\x4e\x65\xd3\x95\xbc\xec\xe2\x2b\xd4\x47\xa7\xc6\xf5\x0d\x5e\x2e\x74\xaf\x33\x36\xf5\x97\x74\x6c\xf2\x89\x9b\xdf\xf4\x29\x9e\x2f\xee\x56\x88\xb7\xa9\x6b\x60\xbf\x07\x18\x21\xeb\x3a\xbd\xa8\x11\x49\x10\x76\x48\x1e\xb5\xb6\xb5\xa4\x1f\xbe\x1b\x58\x49\xff\x48\xe1\x78\xb5\x07\xf8\x61\x0f\x9d\xe9\x9b\x54\xa0\x2d\x92\x5c\x75\xfa\xa4\x88\xbc\x04\x76\x3f\x3c\x94\x45\xb8\xde\x37\xcd\xe6\x7d\x56\xac\x79\x48\xf4\x7f\x86\xe7\xdb\xc5\x47\x09\x88\x4a\x9f\x99\xfc\xe4\xaf\x6f\xb9\xe6\x6f\x35\x82\x1e\x3f\x4a\xf0\xed\x16\x09\xdb\xaa\x66\xfa\xcc\x09\x57\x48\xb8\xe2\xb0\x3a\xfc\x73\x85\x9f\xab\xe2\x06\xbf\xae\xf1\xeb\xba\x2c\x3f\x48\x61\x10\x1e\x2a\xde\xd4\xc4\x7f\x71\xca\x0d\x7e\xdf\xf0\x13\x30\xf7\xd9\x83\x53\x22\xf5\xe1\xa5\x1d\xfb\x81\xf7\x74\x6a\x68\x2e\x91\x6e\x3f\x28\x9d\x5b\xd5\x54\xf9\xbc\xcf\x76\x22\x37\x9a\x84\x2f\x7e\x00\x82\x9a\xd7\x24\xf9\xbc\x8f\xf3\xa2\xbf\xb2\x0a\xe5\x9b\x52\xb9\x1f\x9a\x28\x9f\x94\xd6\x16\xd7\xf3\xd8\x2f\xfd\x42\x6a\xec\x95\x7e\xd1\xf4\xae\xda\x66\xc7\x11\xd8\xdf\x67\xf6\x08\x46\x7c\xfd\xe2\x98\xf2\xcc\x54\x8b\x03\x2a\x73\xb8\x05\xc4\xd9\x67\x55\xf3\x8e\xbd\xce\x67\x99\x3d\x7a\x53\xd5\xbb\x7d\x10\xc1\xe3\x83\x4b\x02\x16\xe3\xf4\x89\xa3\x0a\xbf\x93\x2b\x8f\x88\xd3\xea\xce\x17\x95\xbe\x9d\xce\xca\x6b\x12\x80\xbe\xfe\xc7\x3f\x00\x4f\xdf\xff\xfc\x67\x7e\xf2\xf4\x61\x5e\x7e\xe2\x00\xb2\x5d\xbe\xd5\xdb\x58\x03\xa3\xdf\xcf\x13\x48\xf6\x91\x87\xff\x80\xde\x1c\x8a\xff\x00\x9a\xcf\xfe\x5f\x00\x00\x00\xff\xff\x01\x80\xdd\x31\xb0\xc0\x00\x00") func confLocaleLocale_enUsIniBytes() ([]byte, error) { return bindataRead( @@ -4337,7 +4337,7 @@ func confLocaleLocale_enUsIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_en-US.ini", size: 49209, mode: os.FileMode(420), modTime: time.Unix(1455565854, 0)} + info := bindataFileInfo{name: "conf/locale/locale_en-US.ini", size: 49328, mode: os.FileMode(420), modTime: time.Unix(1455998912, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4357,7 +4357,7 @@ func confLocaleLocale_esEsIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_es-ES.ini", size: 53465, mode: os.FileMode(493), modTime: time.Unix(1455584004, 0)} + info := bindataFileInfo{name: "conf/locale/locale_es-ES.ini", size: 53465, mode: os.FileMode(420), modTime: time.Unix(1455859270, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4377,7 +4377,7 @@ func confLocaleLocale_frFrIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_fr-FR.ini", size: 53050, mode: os.FileMode(493), modTime: time.Unix(1455584002, 0)} + info := bindataFileInfo{name: "conf/locale/locale_fr-FR.ini", size: 53050, mode: os.FileMode(420), modTime: time.Unix(1455859270, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4397,7 +4397,7 @@ func confLocaleLocale_itItIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_it-IT.ini", size: 50878, mode: os.FileMode(493), modTime: time.Unix(1455584014, 0)} + info := bindataFileInfo{name: "conf/locale/locale_it-IT.ini", size: 50878, mode: os.FileMode(420), modTime: time.Unix(1455859270, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4417,7 +4417,7 @@ func confLocaleLocale_jaJpIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_ja-JP.ini", size: 58412, mode: os.FileMode(493), modTime: time.Unix(1455584016, 0)} + info := bindataFileInfo{name: "conf/locale/locale_ja-JP.ini", size: 58412, mode: os.FileMode(420), modTime: time.Unix(1455859270, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4437,7 +4437,7 @@ func confLocaleLocale_lvLvIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_lv-LV.ini", size: 53389, mode: os.FileMode(493), modTime: time.Unix(1455584044, 0)} + info := bindataFileInfo{name: "conf/locale/locale_lv-LV.ini", size: 53389, mode: os.FileMode(420), modTime: time.Unix(1455859270, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4457,7 +4457,7 @@ func confLocaleLocale_nlNlIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_nl-NL.ini", size: 49739, mode: os.FileMode(493), modTime: time.Unix(1455584022, 0)} + info := bindataFileInfo{name: "conf/locale/locale_nl-NL.ini", size: 49739, mode: os.FileMode(420), modTime: time.Unix(1455859270, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4477,7 +4477,7 @@ func confLocaleLocale_plPlIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_pl-PL.ini", size: 51330, mode: os.FileMode(493), modTime: time.Unix(1455584022, 0)} + info := bindataFileInfo{name: "conf/locale/locale_pl-PL.ini", size: 51330, mode: os.FileMode(420), modTime: time.Unix(1455859270, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4497,7 +4497,7 @@ func confLocaleLocale_ptBrIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_pt-BR.ini", size: 51740, mode: os.FileMode(493), modTime: time.Unix(1455584038, 0)} + info := bindataFileInfo{name: "conf/locale/locale_pt-BR.ini", size: 51740, mode: os.FileMode(420), modTime: time.Unix(1455859270, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4517,7 +4517,7 @@ func confLocaleLocale_ruRuIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_ru-RU.ini", size: 75645, mode: os.FileMode(493), modTime: time.Unix(1455584026, 0)} + info := bindataFileInfo{name: "conf/locale/locale_ru-RU.ini", size: 75645, mode: os.FileMode(420), modTime: time.Unix(1455859270, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4537,7 +4537,7 @@ func confLocaleLocale_zhCnIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_zh-CN.ini", size: 47039, mode: os.FileMode(493), modTime: time.Unix(1455584034, 0)} + info := bindataFileInfo{name: "conf/locale/locale_zh-CN.ini", size: 47039, mode: os.FileMode(420), modTime: time.Unix(1455859270, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -4557,7 +4557,7 @@ func confLocaleLocale_zhHkIni() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "conf/locale/locale_zh-HK.ini", size: 47044, mode: os.FileMode(493), modTime: time.Unix(1455584036, 0)} + info := bindataFileInfo{name: "conf/locale/locale_zh-HK.ini", size: 47044, mode: os.FileMode(420), modTime: time.Unix(1455859270, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/modules/cron/constantdelay.go b/modules/cron/constantdelay.go deleted file mode 100644 index cd6e7b1be9..0000000000 --- a/modules/cron/constantdelay.go +++ /dev/null @@ -1,27 +0,0 @@ -package cron - -import "time" - -// ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes". -// It does not support jobs more frequent than once a second. -type ConstantDelaySchedule struct { - Delay time.Duration -} - -// Every returns a crontab Schedule that activates once every duration. -// Delays of less than a second are not supported (will round up to 1 second). -// Any fields less than a Second are truncated. -func Every(duration time.Duration) ConstantDelaySchedule { - if duration < time.Second { - duration = time.Second - } - return ConstantDelaySchedule{ - Delay: duration - time.Duration(duration.Nanoseconds())%time.Second, - } -} - -// Next returns the next time this should be run. -// This rounds so that the next activation time will be on the second. -func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time { - return t.Add(schedule.Delay - time.Duration(t.Nanosecond())*time.Nanosecond) -} diff --git a/modules/cron/constantdelay_test.go b/modules/cron/constantdelay_test.go deleted file mode 100644 index f43a58ad26..0000000000 --- a/modules/cron/constantdelay_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package cron - -import ( - "testing" - "time" -) - -func TestConstantDelayNext(t *testing.T) { - tests := []struct { - time string - delay time.Duration - expected string - }{ - // Simple cases - {"Mon Jul 9 14:45 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"}, - {"Mon Jul 9 14:59 2012", 15 * time.Minute, "Mon Jul 9 15:14 2012"}, - {"Mon Jul 9 14:59:59 2012", 15 * time.Minute, "Mon Jul 9 15:14:59 2012"}, - - // Wrap around hours - {"Mon Jul 9 15:45 2012", 35 * time.Minute, "Mon Jul 9 16:20 2012"}, - - // Wrap around days - {"Mon Jul 9 23:46 2012", 14 * time.Minute, "Tue Jul 10 00:00 2012"}, - {"Mon Jul 9 23:45 2012", 35 * time.Minute, "Tue Jul 10 00:20 2012"}, - {"Mon Jul 9 23:35:51 2012", 44*time.Minute + 24*time.Second, "Tue Jul 10 00:20:15 2012"}, - {"Mon Jul 9 23:35:51 2012", 25*time.Hour + 44*time.Minute + 24*time.Second, "Thu Jul 11 01:20:15 2012"}, - - // Wrap around months - {"Mon Jul 9 23:35 2012", 91*24*time.Hour + 25*time.Minute, "Thu Oct 9 00:00 2012"}, - - // Wrap around minute, hour, day, month, and year - {"Mon Dec 31 23:59:45 2012", 15 * time.Second, "Tue Jan 1 00:00:00 2013"}, - - // Round to nearest second on the delay - {"Mon Jul 9 14:45 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"}, - - // Round up to 1 second if the duration is less. - {"Mon Jul 9 14:45:00 2012", 15 * time.Millisecond, "Mon Jul 9 14:45:01 2012"}, - - // Round to nearest second when calculating the next time. - {"Mon Jul 9 14:45:00.005 2012", 15 * time.Minute, "Mon Jul 9 15:00 2012"}, - - // Round to nearest second for both. - {"Mon Jul 9 14:45:00.005 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"}, - } - - for _, c := range tests { - actual := Every(c.delay).Next(getTime(c.time)) - expected := getTime(c.expected) - if actual != expected { - t.Errorf("%s, \"%s\": (expected) %v != %v (actual)", c.time, c.delay, expected, actual) - } - } -} diff --git a/modules/cron/cron.go b/modules/cron/cron.go index 3491afac99..7695b974bb 100644 --- a/modules/cron/cron.go +++ b/modules/cron/cron.go @@ -1,4 +1,3 @@ -// Copyright 2012 Rob Figueiredo. All rights reserved. // 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. @@ -6,207 +5,59 @@ package cron import ( - "sort" "time" -) - -// Cron keeps track of any number of entries, invoking the associated func as -// specified by the schedule. It may be started, stopped, and the entries may -// be inspected while running. -type Cron struct { - entries []*Entry - stop chan struct{} - add chan *Entry - snapshot chan []*Entry - running bool -} - -// Job is an interface for submitted cron jobs. -type Job interface { - Run() -} - -// The Schedule describes a job's duty cycle. -type Schedule interface { - // Return the next activation time, later than the given time. - // Next is invoked initially, and then each time the job is run. - Next(time.Time) time.Time -} - -// Entry consists of a schedule and the func to execute on that schedule. -type Entry struct { - Description string - Spec string - - // The schedule on which this job should be run. - Schedule Schedule - - // The next time the job will run. This is the zero time if Cron has not been - // started or this entry's schedule is unsatisfiable - Next time.Time - - // The last time this job was run. This is the zero time if the job has never - // been run. - Prev time.Time - - // The Job to run. - Job Job - - ExecTimes int // Execute times count. -} - -// byTime is a wrapper for sorting the entry array by time -// (with zero time at the end). -type byTime []*Entry - -func (s byTime) Len() int { return len(s) } -func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s byTime) Less(i, j int) bool { - // Two zero times should return false. - // Otherwise, zero is "greater" than any other time. - // (To sort it at the end of the list.) - if s[i].Next.IsZero() { - return false - } - if s[j].Next.IsZero() { - return true - } - return s[i].Next.Before(s[j].Next) -} - -// New returns a new Cron job runner. -func New() *Cron { - return &Cron{ - entries: nil, - add: make(chan *Entry), - stop: make(chan struct{}), - snapshot: make(chan []*Entry), - running: false, - } -} - -// A wrapper that turns a func() into a cron.Job -type FuncJob func() - -func (f FuncJob) Run() { f() } - -// AddFunc adds a func to the Cron to be run on the given schedule. -func (c *Cron) AddFunc(desc, spec string, cmd func()) (*Entry, error) { - return c.AddJob(desc, spec, FuncJob(cmd)) -} -// AddFunc adds a Job to the Cron to be run on the given schedule. -func (c *Cron) AddJob(desc, spec string, cmd Job) (*Entry, error) { - schedule, err := Parse(spec) - if err != nil { - return nil, err - } - return c.Schedule(desc, spec, schedule, cmd), nil -} + "github.com/gogits/cron" -// Schedule adds a Job to the Cron to be run on the given schedule. -func (c *Cron) Schedule(desc, spec string, schedule Schedule, cmd Job) *Entry { - entry := &Entry{ - Description: desc, - Spec: spec, - Schedule: schedule, - Job: cmd, - } - if c.running { - c.add <- entry - } else { - c.entries = append(c.entries, entry) - } - return entry -} + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/setting" +) -// Entries returns a snapshot of the cron entries. -func (c *Cron) Entries() []*Entry { - if c.running { - c.snapshot <- nil - x := <-c.snapshot - return x +var c = cron.New() + +func NewContext() { + var ( + entry *cron.Entry + err error + ) + if setting.Cron.UpdateMirror.Enabled { + entry, err = c.AddFunc("Update mirrors", setting.Cron.UpdateMirror.Schedule, models.MirrorUpdate) + if err != nil { + log.Fatal(4, "Cron[Update mirrors]: %v", err) + } + if setting.Cron.UpdateMirror.RunAtStart { + entry.Prev = time.Now() + entry.ExecTimes++ + go models.MirrorUpdate() + } } - return c.entrySnapshot() -} - -// Start the cron scheduler in its own go-routine. -func (c *Cron) Start() { - c.running = true - go c.run() -} - -// Run the scheduler.. this is private just due to the need to synchronize -// access to the 'running' state variable. -func (c *Cron) run() { - // Figure out the next activation times for each entry. - now := time.Now().Local() - for _, entry := range c.entries { - entry.Next = entry.Schedule.Next(now) + if setting.Cron.RepoHealthCheck.Enabled { + entry, err = c.AddFunc("Repository health check", setting.Cron.RepoHealthCheck.Schedule, models.GitFsck) + if err != nil { + log.Fatal(4, "Cron[Repository health check]: %v", err) + } + if setting.Cron.RepoHealthCheck.RunAtStart { + entry.Prev = time.Now() + entry.ExecTimes++ + go models.GitFsck() + } } - - for { - // Determine the next entry to run. - sort.Sort(byTime(c.entries)) - - var effective time.Time - if len(c.entries) == 0 || c.entries[0].Next.IsZero() { - // If there are no entries yet, just sleep - it still handles new entries - // and stop requests. - effective = now.AddDate(10, 0, 0) - } else { - effective = c.entries[0].Next + if setting.Cron.CheckRepoStats.Enabled { + entry, err = c.AddFunc("Check repository statistics", setting.Cron.CheckRepoStats.Schedule, models.CheckRepoStats) + if err != nil { + log.Fatal(4, "Cron[Check repository statistics]: %v", err) } - - select { - case now = <-time.After(effective.Sub(now)): - // Run every entry whose next time was this effective time. - for _, e := range c.entries { - if e.Next != effective { - break - } - go e.Job.Run() - e.ExecTimes++ - e.Prev = e.Next - e.Next = e.Schedule.Next(effective) - } - continue - - case newEntry := <-c.add: - c.entries = append(c.entries, newEntry) - newEntry.Next = newEntry.Schedule.Next(now) - - case <-c.snapshot: - c.snapshot <- c.entrySnapshot() - - case <-c.stop: - return + if setting.Cron.CheckRepoStats.RunAtStart { + entry.Prev = time.Now() + entry.ExecTimes++ + go models.CheckRepoStats() } - - // 'now' should be updated after newEntry and snapshot cases. - now = time.Now().Local() } + c.Start() } -// Stop the cron scheduler. -func (c *Cron) Stop() { - c.stop <- struct{}{} - c.running = false -} - -// entrySnapshot returns a copy of the current cron entry list. -func (c *Cron) entrySnapshot() []*Entry { - entries := make([]*Entry, 0, len(c.entries)) - for _, e := range c.entries { - entries = append(entries, &Entry{ - Description: e.Description, - Spec: e.Spec, - Schedule: e.Schedule, - Next: e.Next, - Prev: e.Prev, - Job: e.Job, - ExecTimes: e.ExecTimes, - }) - } - return entries +// ListTasks returns all running cron tasks. +func ListTasks() []*cron.Entry { + return c.Entries() } diff --git a/modules/cron/cron_test.go b/modules/cron/cron_test.go deleted file mode 100644 index 417247a05a..0000000000 --- a/modules/cron/cron_test.go +++ /dev/null @@ -1,255 +0,0 @@ -package cron - -import ( - "fmt" - "sync" - "testing" - "time" -) - -// Many tests schedule a job for every second, and then wait at most a second -// for it to run. This amount is just slightly larger than 1 second to -// compensate for a few milliseconds of runtime. -const ONE_SECOND = 1*time.Second + 10*time.Millisecond - -// Start and stop cron with no entries. -func TestNoEntries(t *testing.T) { - cron := New() - cron.Start() - - select { - case <-time.After(ONE_SECOND): - t.FailNow() - case <-stop(cron): - } -} - -// Start, stop, then add an entry. Verify entry doesn't run. -func TestStopCausesJobsToNotRun(t *testing.T) { - wg := &sync.WaitGroup{} - wg.Add(1) - - cron := New() - cron.Start() - cron.Stop() - cron.AddFunc("", "* * * * * ?", func() { wg.Done() }) - - select { - case <-time.After(ONE_SECOND): - // No job ran! - case <-wait(wg): - t.FailNow() - } -} - -// Add a job, start cron, expect it runs. -func TestAddBeforeRunning(t *testing.T) { - wg := &sync.WaitGroup{} - wg.Add(1) - - cron := New() - cron.AddFunc("", "* * * * * ?", func() { wg.Done() }) - cron.Start() - defer cron.Stop() - - // Give cron 2 seconds to run our job (which is always activated). - select { - case <-time.After(ONE_SECOND): - t.FailNow() - case <-wait(wg): - } -} - -// Start cron, add a job, expect it runs. -func TestAddWhileRunning(t *testing.T) { - wg := &sync.WaitGroup{} - wg.Add(1) - - cron := New() - cron.Start() - defer cron.Stop() - cron.AddFunc("", "* * * * * ?", func() { wg.Done() }) - - select { - case <-time.After(ONE_SECOND): - t.FailNow() - case <-wait(wg): - } -} - -// Test timing with Entries. -func TestSnapshotEntries(t *testing.T) { - wg := &sync.WaitGroup{} - wg.Add(1) - - cron := New() - cron.AddFunc("", "@every 2s", func() { wg.Done() }) - cron.Start() - defer cron.Stop() - - // Cron should fire in 2 seconds. After 1 second, call Entries. - select { - case <-time.After(ONE_SECOND): - cron.Entries() - } - - // Even though Entries was called, the cron should fire at the 2 second mark. - select { - case <-time.After(ONE_SECOND): - t.FailNow() - case <-wait(wg): - } - -} - -// Test that the entries are correctly sorted. -// Add a bunch of long-in-the-future entries, and an immediate entry, and ensure -// that the immediate entry runs immediately. -// Also: Test that multiple jobs run in the same instant. -func TestMultipleEntries(t *testing.T) { - wg := &sync.WaitGroup{} - wg.Add(2) - - cron := New() - cron.AddFunc("", "0 0 0 1 1 ?", func() {}) - cron.AddFunc("", "* * * * * ?", func() { wg.Done() }) - cron.AddFunc("", "0 0 0 31 12 ?", func() {}) - cron.AddFunc("", "* * * * * ?", func() { wg.Done() }) - - cron.Start() - defer cron.Stop() - - select { - case <-time.After(ONE_SECOND): - t.FailNow() - case <-wait(wg): - } -} - -// Test running the same job twice. -func TestRunningJobTwice(t *testing.T) { - wg := &sync.WaitGroup{} - wg.Add(2) - - cron := New() - cron.AddFunc("", "0 0 0 1 1 ?", func() {}) - cron.AddFunc("", "0 0 0 31 12 ?", func() {}) - cron.AddFunc("", "* * * * * ?", func() { wg.Done() }) - - cron.Start() - defer cron.Stop() - - select { - case <-time.After(2 * ONE_SECOND): - t.FailNow() - case <-wait(wg): - } -} - -func TestRunningMultipleSchedules(t *testing.T) { - wg := &sync.WaitGroup{} - wg.Add(2) - - cron := New() - cron.AddFunc("", "0 0 0 1 1 ?", func() {}) - cron.AddFunc("", "0 0 0 31 12 ?", func() {}) - cron.AddFunc("", "* * * * * ?", func() { wg.Done() }) - cron.Schedule("", "", Every(time.Minute), FuncJob(func() {})) - cron.Schedule("", "", Every(time.Second), FuncJob(func() { wg.Done() })) - cron.Schedule("", "", Every(time.Hour), FuncJob(func() {})) - - cron.Start() - defer cron.Stop() - - select { - case <-time.After(2 * ONE_SECOND): - t.FailNow() - case <-wait(wg): - } -} - -// Test that the cron is run in the local time zone (as opposed to UTC). -func TestLocalTimezone(t *testing.T) { - wg := &sync.WaitGroup{} - wg.Add(1) - - now := time.Now().Local() - spec := fmt.Sprintf("%d %d %d %d %d ?", - now.Second()+1, now.Minute(), now.Hour(), now.Day(), now.Month()) - - cron := New() - cron.AddFunc("", spec, func() { wg.Done() }) - cron.Start() - defer cron.Stop() - - select { - case <-time.After(ONE_SECOND): - t.FailNow() - case <-wait(wg): - } -} - -type testJob struct { - wg *sync.WaitGroup - name string -} - -func (t testJob) Run() { - t.wg.Done() -} - -// Simple test using Runnables. -func TestJob(t *testing.T) { - wg := &sync.WaitGroup{} - wg.Add(1) - - cron := New() - cron.AddJob("", "0 0 0 30 Feb ?", testJob{wg, "job0"}) - cron.AddJob("", "0 0 0 1 1 ?", testJob{wg, "job1"}) - cron.AddJob("", "* * * * * ?", testJob{wg, "job2"}) - cron.AddJob("", "1 0 0 1 1 ?", testJob{wg, "job3"}) - cron.Schedule("", "", Every(5*time.Second+5*time.Nanosecond), testJob{wg, "job4"}) - cron.Schedule("", "", Every(5*time.Minute), testJob{wg, "job5"}) - - cron.Start() - defer cron.Stop() - - select { - case <-time.After(ONE_SECOND): - t.FailNow() - case <-wait(wg): - } - - // Ensure the entries are in the right order. - expecteds := []string{"job2", "job4", "job5", "job1", "job3", "job0"} - - var actuals []string - for _, entry := range cron.Entries() { - actuals = append(actuals, entry.Job.(testJob).name) - } - - for i, expected := range expecteds { - if actuals[i] != expected { - t.Errorf("Jobs not in the right order. (expected) %s != %s (actual)", expecteds, actuals) - t.FailNow() - } - } -} - -func wait(wg *sync.WaitGroup) chan bool { - ch := make(chan bool) - go func() { - wg.Wait() - ch <- true - }() - return ch -} - -func stop(cron *Cron) chan bool { - ch := make(chan bool) - go func() { - cron.Stop() - ch <- true - }() - return ch -} diff --git a/modules/cron/doc.go b/modules/cron/doc.go deleted file mode 100644 index 258fe1eff8..0000000000 --- a/modules/cron/doc.go +++ /dev/null @@ -1,129 +0,0 @@ -/* -Package cron implements a cron spec parser and job runner. - -Usage - -Callers may register Funcs to be invoked on a given schedule. Cron will run -them in their own goroutines. - - c := cron.New() - c.AddFunc("Every hour on the half hour","0 30 * * * *", func() { fmt.Println("Every hour on the half hour") }) - c.AddFunc("Every hour","@hourly", func() { fmt.Println("Every hour") }) - c.AddFunc("Every hour and a half","@every 1h30m", func() { fmt.Println("Every hour thirty") }) - c.Start() - .. - // Funcs are invoked in their own goroutine, asynchronously. - ... - // Funcs may also be added to a running Cron - c.AddFunc("@daily", func() { fmt.Println("Every day") }) - .. - // Inspect the cron job entries' next and previous run times. - inspect(c.Entries()) - .. - c.Stop() // Stop the scheduler (does not stop any jobs already running). - -CRON Expression Format - -A cron expression represents a set of times, using 6 space-separated fields. - - Field name | Mandatory? | Allowed values | Allowed special characters - ---------- | ---------- | -------------- | -------------------------- - Seconds | Yes | 0-59 | * / , - - Minutes | Yes | 0-59 | * / , - - Hours | Yes | 0-23 | * / , - - Day of month | Yes | 1-31 | * / , - ? - Month | Yes | 1-12 or JAN-DEC | * / , - - Day of week | Yes | 0-6 or SUN-SAT | * / , - ? - -Note: Month and Day-of-week field values are case insensitive. "SUN", "Sun", -and "sun" are equally accepted. - -Special Characters - -Asterisk ( * ) - -The asterisk indicates that the cron expression will match for all values of the -field; e.g., using an asterisk in the 5th field (month) would indicate every -month. - -Slash ( / ) - -Slashes are used to describe increments of ranges. For example 3-59/15 in the -1st field (minutes) would indicate the 3rd minute of the hour and every 15 -minutes thereafter. The form "*\/..." is equivalent to the form "first-last/...", -that is, an increment over the largest possible range of the field. The form -"N/..." is accepted as meaning "N-MAX/...", that is, starting at N, use the -increment until the end of that specific range. It does not wrap around. - -Comma ( , ) - -Commas are used to separate items of a list. For example, using "MON,WED,FRI" in -the 5th field (day of week) would mean Mondays, Wednesdays and Fridays. - -Hyphen ( - ) - -Hyphens are used to define ranges. For example, 9-17 would indicate every -hour between 9am and 5pm inclusive. - -Question mark ( ? ) - -Question mark may be used instead of '*' for leaving either day-of-month or -day-of-week blank. - -Predefined schedules - -You may use one of several pre-defined schedules in place of a cron expression. - - Entry | Description | Equivalent To - ----- | ----------- | ------------- - @yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 0 1 1 * - @monthly | Run once a month, midnight, first of month | 0 0 0 1 * * - @weekly | Run once a week, midnight on Sunday | 0 0 0 * * 0 - @daily (or @midnight) | Run once a day, midnight | 0 0 0 * * * - @hourly | Run once an hour, beginning of hour | 0 0 * * * * - -Intervals - -You may also schedule a job to execute at fixed intervals. This is supported by -formatting the cron spec like this: - - @every <duration> - -where "duration" is a string accepted by time.ParseDuration -(http://golang.org/pkg/time/#ParseDuration). - -For example, "@every 1h30m10s" would indicate a schedule that activates every -1 hour, 30 minutes, 10 seconds. - -Note: The interval does not take the job runtime into account. For example, -if a job takes 3 minutes to run, and it is scheduled to run every 5 minutes, -it will have only 2 minutes of idle time between each run. - -Time zones - -All interpretation and scheduling is done in the machine's local time zone (as -provided by the Go time package (http://www.golang.org/pkg/time). - -Be aware that jobs scheduled during daylight-savings leap-ahead transitions will -not be run! - -Thread safety - -Since the Cron service runs concurrently with the calling code, some amount of -care must be taken to ensure proper synchronization. - -All cron methods are designed to be correctly synchronized as long as the caller -ensures that invocations have a clear happens-before ordering between them. - -Implementation - -Cron entries are stored in an array, sorted by their next activation time. Cron -sleeps until the next job is due to be run. - -Upon waking: - - it runs each entry that is active on that second - - it calculates the next run times for the jobs that were run - - it re-sorts the array of entries by next activation time. - - it goes to sleep until the soonest job. -*/ -package cron diff --git a/modules/cron/parser.go b/modules/cron/parser.go deleted file mode 100644 index 4224fa9308..0000000000 --- a/modules/cron/parser.go +++ /dev/null @@ -1,231 +0,0 @@ -package cron - -import ( - "fmt" - "log" - "math" - "strconv" - "strings" - "time" -) - -// Parse returns a new crontab schedule representing the given spec. -// It returns a descriptive error if the spec is not valid. -// -// It accepts -// - Full crontab specs, e.g. "* * * * * ?" -// - Descriptors, e.g. "@midnight", "@every 1h30m" -func Parse(spec string) (_ Schedule, err error) { - // Convert panics into errors - defer func() { - if recovered := recover(); recovered != nil { - err = fmt.Errorf("%v", recovered) - } - }() - - if spec[0] == '@' { - return parseDescriptor(spec), nil - } - - // Split on whitespace. We require 5 or 6 fields. - // (second) (minute) (hour) (day of month) (month) (day of week, optional) - fields := strings.Fields(spec) - if len(fields) != 5 && len(fields) != 6 { - log.Panicf("Expected 5 or 6 fields, found %d: %s", len(fields), spec) - } - - // If a sixth field is not provided (DayOfWeek), then it is equivalent to star. - if len(fields) == 5 { - fields = append(fields, "*") - } - - schedule := &SpecSchedule{ - Second: getField(fields[0], seconds), - Minute: getField(fields[1], minutes), - Hour: getField(fields[2], hours), - Dom: getField(fields[3], dom), - Month: getField(fields[4], months), - Dow: getField(fields[5], dow), - } - - return schedule, nil -} - -// getField returns an Int with the bits set representing all of the times that -// the field represents. A "field" is a comma-separated list of "ranges". -func getField(field string, r bounds) uint64 { - // list = range {"," range} - var bits uint64 - ranges := strings.FieldsFunc(field, func(r rune) bool { return r == ',' }) - for _, expr := range ranges { - bits |= getRange(expr, r) - } - return bits -} - -// getRange returns the bits indicated by the given expression: -// number | number "-" number [ "/" number ] -func getRange(expr string, r bounds) uint64 { - - var ( - start, end, step uint - rangeAndStep = strings.Split(expr, "/") - lowAndHigh = strings.Split(rangeAndStep[0], "-") - singleDigit = len(lowAndHigh) == 1 - ) - - var extra_star uint64 - if lowAndHigh[0] == "*" || lowAndHigh[0] == "?" { - start = r.min - end = r.max - extra_star = starBit - } else { - start = parseIntOrName(lowAndHigh[0], r.names) - switch len(lowAndHigh) { - case 1: - end = start - case 2: - end = parseIntOrName(lowAndHigh[1], r.names) - default: - log.Panicf("Too many hyphens: %s", expr) - } - } - - switch len(rangeAndStep) { - case 1: - step = 1 - case 2: - step = mustParseInt(rangeAndStep[1]) - - // Special handling: "N/step" means "N-max/step". - if singleDigit { - end = r.max - } - default: - log.Panicf("Too many slashes: %s", expr) - } - - if start < r.min { - log.Panicf("Beginning of range (%d) below minimum (%d): %s", start, r.min, expr) - } - if end > r.max { - log.Panicf("End of range (%d) above maximum (%d): %s", end, r.max, expr) - } - if start > end { - log.Panicf("Beginning of range (%d) beyond end of range (%d): %s", start, end, expr) - } - - return getBits(start, end, step) | extra_star -} - -// parseIntOrName returns the (possibly-named) integer contained in expr. -func parseIntOrName(expr string, names map[string]uint) uint { - if names != nil { - if namedInt, ok := names[strings.ToLower(expr)]; ok { - return namedInt - } - } - return mustParseInt(expr) -} - -// mustParseInt parses the given expression as an int or panics. -func mustParseInt(expr string) uint { - num, err := strconv.Atoi(expr) - if err != nil { - log.Panicf("Failed to parse int from %s: %s", expr, err) - } - if num < 0 { - log.Panicf("Negative number (%d) not allowed: %s", num, expr) - } - - return uint(num) -} - -// getBits sets all bits in the range [min, max], modulo the given step size. -func getBits(min, max, step uint) uint64 { - var bits uint64 - - // If step is 1, use shifts. - if step == 1 { - return ^(math.MaxUint64 << (max + 1)) & (math.MaxUint64 << min) - } - - // Else, use a simple loop. - for i := min; i <= max; i += step { - bits |= 1 << i - } - return bits -} - -// all returns all bits within the given bounds. (plus the star bit) -func all(r bounds) uint64 { - return getBits(r.min, r.max, 1) | starBit -} - -// parseDescriptor returns a pre-defined schedule for the expression, or panics -// if none matches. -func parseDescriptor(spec string) Schedule { - switch spec { - case "@yearly", "@annually": - return &SpecSchedule{ - Second: 1 << seconds.min, - Minute: 1 << minutes.min, - Hour: 1 << hours.min, - Dom: 1 << dom.min, - Month: 1 << months.min, - Dow: all(dow), - } - - case "@monthly": - return &SpecSchedule{ - Second: 1 << seconds.min, - Minute: 1 << minutes.min, - Hour: 1 << hours.min, - Dom: 1 << dom.min, - Month: all(months), - Dow: all(dow), - } - - case "@weekly": - return &SpecSchedule{ - Second: 1 << seconds.min, - Minute: 1 << minutes.min, - Hour: 1 << hours.min, - Dom: all(dom), - Month: all(months), - Dow: 1 << dow.min, - } - - case "@daily", "@midnight": - return &SpecSchedule{ - Second: 1 << seconds.min, - Minute: 1 << minutes.min, - Hour: 1 << hours.min, - Dom: all(dom), - Month: all(months), - Dow: all(dow), - } - - case "@hourly": - return &SpecSchedule{ - Second: 1 << seconds.min, - Minute: 1 << minutes.min, - Hour: all(hours), - Dom: all(dom), - Month: all(months), - Dow: all(dow), - } - } - - const every = "@every " - if strings.HasPrefix(spec, every) { - duration, err := time.ParseDuration(spec[len(every):]) - if err != nil { - log.Panicf("Failed to parse duration %s: %s", spec, err) - } - return Every(duration) - } - - log.Panicf("Unrecognized descriptor: %s", spec) - return nil -} diff --git a/modules/cron/parser_test.go b/modules/cron/parser_test.go deleted file mode 100644 index f03299e5e4..0000000000 --- a/modules/cron/parser_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package cron - -import ( - "reflect" - "testing" - "time" -) - -func TestRange(t *testing.T) { - ranges := []struct { - expr string - min, max uint - expected uint64 - }{ - {"5", 0, 7, 1 << 5}, - {"0", 0, 7, 1 << 0}, - {"7", 0, 7, 1 << 7}, - - {"5-5", 0, 7, 1 << 5}, - {"5-6", 0, 7, 1<<5 | 1<<6}, - {"5-7", 0, 7, 1<<5 | 1<<6 | 1<<7}, - - {"5-6/2", 0, 7, 1 << 5}, - {"5-7/2", 0, 7, 1<<5 | 1<<7}, - {"5-7/1", 0, 7, 1<<5 | 1<<6 | 1<<7}, - - {"*", 1, 3, 1<<1 | 1<<2 | 1<<3 | starBit}, - {"*/2", 1, 3, 1<<1 | 1<<3 | starBit}, - } - - for _, c := range ranges { - actual := getRange(c.expr, bounds{c.min, c.max, nil}) - if actual != c.expected { - t.Errorf("%s => (expected) %d != %d (actual)", c.expr, c.expected, actual) - } - } -} - -func TestField(t *testing.T) { - fields := []struct { - expr string - min, max uint - expected uint64 - }{ - {"5", 1, 7, 1 << 5}, - {"5,6", 1, 7, 1<<5 | 1<<6}, - {"5,6,7", 1, 7, 1<<5 | 1<<6 | 1<<7}, - {"1,5-7/2,3", 1, 7, 1<<1 | 1<<5 | 1<<7 | 1<<3}, - } - - for _, c := range fields { - actual := getField(c.expr, bounds{c.min, c.max, nil}) - if actual != c.expected { - t.Errorf("%s => (expected) %d != %d (actual)", c.expr, c.expected, actual) - } - } -} - -func TestBits(t *testing.T) { - allBits := []struct { - r bounds - expected uint64 - }{ - {minutes, 0xfffffffffffffff}, // 0-59: 60 ones - {hours, 0xffffff}, // 0-23: 24 ones - {dom, 0xfffffffe}, // 1-31: 31 ones, 1 zero - {months, 0x1ffe}, // 1-12: 12 ones, 1 zero - {dow, 0x7f}, // 0-6: 7 ones - } - - for _, c := range allBits { - actual := all(c.r) // all() adds the starBit, so compensate for that.. - if c.expected|starBit != actual { - t.Errorf("%d-%d/%d => (expected) %b != %b (actual)", - c.r.min, c.r.max, 1, c.expected|starBit, actual) - } - } - - bits := []struct { - min, max, step uint - expected uint64 - }{ - - {0, 0, 1, 0x1}, - {1, 1, 1, 0x2}, - {1, 5, 2, 0x2a}, // 101010 - {1, 4, 2, 0xa}, // 1010 - } - - for _, c := range bits { - actual := getBits(c.min, c.max, c.step) - if c.expected != actual { - t.Errorf("%d-%d/%d => (expected) %b != %b (actual)", - c.min, c.max, c.step, c.expected, actual) - } - } -} - -func TestSpecSchedule(t *testing.T) { - entries := []struct { - expr string - expected Schedule - }{ - {"* 5 * * * *", &SpecSchedule{all(seconds), 1 << 5, all(hours), all(dom), all(months), all(dow)}}, - {"@every 5m", ConstantDelaySchedule{time.Duration(5) * time.Minute}}, - } - - for _, c := range entries { - actual, err := Parse(c.expr) - if err != nil { - t.Error(err) - } - if !reflect.DeepEqual(actual, c.expected) { - t.Errorf("%s => (expected) %v != %v (actual)", c.expr, c.expected, actual) - } - } -} diff --git a/modules/cron/spec.go b/modules/cron/spec.go deleted file mode 100644 index cb3743325d..0000000000 --- a/modules/cron/spec.go +++ /dev/null @@ -1,161 +0,0 @@ -package cron - -import ( - "time" -) - -// SpecSchedule specifies a duty cycle (to the second granularity), based on a -// traditional crontab specification. It is computed initially and stored as bit sets. -type SpecSchedule struct { - Second, Minute, Hour, Dom, Month, Dow uint64 -} - -// bounds provides a range of acceptable values (plus a map of name to value). -type bounds struct { - min, max uint - names map[string]uint -} - -// The bounds for each field. -var ( - seconds = bounds{0, 59, nil} - minutes = bounds{0, 59, nil} - hours = bounds{0, 23, nil} - dom = bounds{1, 31, nil} - months = bounds{1, 12, map[string]uint{ - "jan": 1, - "feb": 2, - "mar": 3, - "apr": 4, - "may": 5, - "jun": 6, - "jul": 7, - "aug": 8, - "sep": 9, - "oct": 10, - "nov": 11, - "dec": 12, - }} - dow = bounds{0, 6, map[string]uint{ - "sun": 0, - "mon": 1, - "tue": 2, - "wed": 3, - "thu": 4, - "fri": 5, - "sat": 6, - }} -) - -const ( - // Set the top bit if a star was included in the expression. - starBit = 1 << 63 -) - -// Next returns the next time this schedule is activated, greater than the given -// time. If no time can be found to satisfy the schedule, return the zero time. -func (s *SpecSchedule) Next(t time.Time) time.Time { - // General approach: - // For Month, Day, Hour, Minute, Second: - // Check if the time value matches. If yes, continue to the next field. - // If the field doesn't match the schedule, then increment the field until it matches. - // While incrementing the field, a wrap-around brings it back to the beginning - // of the field list (since it is necessary to re-verify previous field - // values) - - // Start at the earliest possible time (the upcoming second). - t = t.Add(1*time.Second - time.Duration(t.Nanosecond())*time.Nanosecond) - - // This flag indicates whether a field has been incremented. - added := false - - // If no time is found within five years, return zero. - yearLimit := t.Year() + 5 - -WRAP: - if t.Year() > yearLimit { - return time.Time{} - } - - // Find the first applicable month. - // If it's this month, then do nothing. - for 1<<uint(t.Month())&s.Month == 0 { - // If we have to add a month, reset the other parts to 0. - if !added { - added = true - // Otherwise, set the date at the beginning (since the current time is irrelevant). - t = time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, t.Location()) - } - t = t.AddDate(0, 1, 0) - - // Wrapped around. - if t.Month() == time.January { - goto WRAP - } - } - - // Now get a day in that month. - for !dayMatches(s, t) { - if !added { - added = true - t = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) - } - t = t.AddDate(0, 0, 1) - - if t.Day() == 1 { - goto WRAP - } - } - - for 1<<uint(t.Hour())&s.Hour == 0 { - if !added { - added = true - t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, t.Location()) - } - t = t.Add(1 * time.Hour) - - if t.Hour() == 0 { - goto WRAP - } - } - - for 1<<uint(t.Minute())&s.Minute == 0 { - if !added { - added = true - t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), 0, 0, t.Location()) - } - t = t.Add(1 * time.Minute) - - if t.Minute() == 0 { - goto WRAP - } - } - - for 1<<uint(t.Second())&s.Second == 0 { - if !added { - added = true - t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), 0, t.Location()) - } - t = t.Add(1 * time.Second) - - if t.Second() == 0 { - goto WRAP - } - } - - return t -} - -// dayMatches returns true if the schedule's day-of-week and day-of-month -// restrictions are satisfied by the given time. -func dayMatches(s *SpecSchedule, t time.Time) bool { - var ( - domMatch bool = 1<<uint(t.Day())&s.Dom > 0 - dowMatch bool = 1<<uint(t.Weekday())&s.Dow > 0 - ) - - if s.Dom&starBit > 0 || s.Dow&starBit > 0 { - return domMatch && dowMatch - } - return domMatch || dowMatch -} diff --git a/modules/cron/spec_test.go b/modules/cron/spec_test.go deleted file mode 100644 index 855d79831b..0000000000 --- a/modules/cron/spec_test.go +++ /dev/null @@ -1,173 +0,0 @@ -package cron - -import ( - "testing" - "time" -) - -func TestActivation(t *testing.T) { - tests := []struct { - time, spec string - expected bool - }{ - // Every fifteen minutes. - {"Mon Jul 9 15:00 2012", "0 0/15 * * *", true}, - {"Mon Jul 9 15:45 2012", "0 0/15 * * *", true}, - {"Mon Jul 9 15:40 2012", "0 0/15 * * *", false}, - - // Every fifteen minutes, starting at 5 minutes. - {"Mon Jul 9 15:05 2012", "0 5/15 * * *", true}, - {"Mon Jul 9 15:20 2012", "0 5/15 * * *", true}, - {"Mon Jul 9 15:50 2012", "0 5/15 * * *", true}, - - // Named months - {"Sun Jul 15 15:00 2012", "0 0/15 * * Jul", true}, - {"Sun Jul 15 15:00 2012", "0 0/15 * * Jun", false}, - - // Everything set. - {"Sun Jul 15 08:30 2012", "0 30 08 ? Jul Sun", true}, - {"Sun Jul 15 08:30 2012", "0 30 08 15 Jul ?", true}, - {"Mon Jul 16 08:30 2012", "0 30 08 ? Jul Sun", false}, - {"Mon Jul 16 08:30 2012", "0 30 08 15 Jul ?", false}, - - // Predefined schedules - {"Mon Jul 9 15:00 2012", "@hourly", true}, - {"Mon Jul 9 15:04 2012", "@hourly", false}, - {"Mon Jul 9 15:00 2012", "@daily", false}, - {"Mon Jul 9 00:00 2012", "@daily", true}, - {"Mon Jul 9 00:00 2012", "@weekly", false}, - {"Sun Jul 8 00:00 2012", "@weekly", true}, - {"Sun Jul 8 01:00 2012", "@weekly", false}, - {"Sun Jul 8 00:00 2012", "@monthly", false}, - {"Sun Jul 1 00:00 2012", "@monthly", true}, - - // Test interaction of DOW and DOM. - // If both are specified, then only one needs to match. - {"Sun Jul 15 00:00 2012", "0 * * 1,15 * Sun", true}, - {"Fri Jun 15 00:00 2012", "0 * * 1,15 * Sun", true}, - {"Wed Aug 1 00:00 2012", "0 * * 1,15 * Sun", true}, - - // However, if one has a star, then both need to match. - {"Sun Jul 15 00:00 2012", "0 * * * * Mon", false}, - {"Sun Jul 15 00:00 2012", "0 * * */10 * Sun", false}, - {"Mon Jul 9 00:00 2012", "0 * * 1,15 * *", false}, - {"Sun Jul 15 00:00 2012", "0 * * 1,15 * *", true}, - {"Sun Jul 15 00:00 2012", "0 * * */2 * Sun", true}, - } - - for _, test := range tests { - sched, err := Parse(test.spec) - if err != nil { - t.Error(err) - continue - } - actual := sched.Next(getTime(test.time).Add(-1 * time.Second)) - expected := getTime(test.time) - if test.expected && expected != actual || !test.expected && expected == actual { - t.Errorf("Fail evaluating %s on %s: (expected) %s != %s (actual)", - test.spec, test.time, expected, actual) - } - } -} - -func TestNext(t *testing.T) { - runs := []struct { - time, spec string - expected string - }{ - // Simple cases - {"Mon Jul 9 14:45 2012", "0 0/15 * * *", "Mon Jul 9 15:00 2012"}, - {"Mon Jul 9 14:59 2012", "0 0/15 * * *", "Mon Jul 9 15:00 2012"}, - {"Mon Jul 9 14:59:59 2012", "0 0/15 * * *", "Mon Jul 9 15:00 2012"}, - - // Wrap around hours - {"Mon Jul 9 15:45 2012", "0 20-35/15 * * *", "Mon Jul 9 16:20 2012"}, - - // Wrap around days - {"Mon Jul 9 23:46 2012", "0 */15 * * *", "Tue Jul 10 00:00 2012"}, - {"Mon Jul 9 23:45 2012", "0 20-35/15 * * *", "Tue Jul 10 00:20 2012"}, - {"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 * * *", "Tue Jul 10 00:20:15 2012"}, - {"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 1/2 * *", "Tue Jul 10 01:20:15 2012"}, - {"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 10-12 * *", "Tue Jul 10 10:20:15 2012"}, - - {"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 1/2 */2 * *", "Thu Jul 11 01:20:15 2012"}, - {"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 * 9-20 * *", "Wed Jul 10 00:20:15 2012"}, - {"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 * 9-20 Jul *", "Wed Jul 10 00:20:15 2012"}, - - // Wrap around months - {"Mon Jul 9 23:35 2012", "0 0 0 9 Apr-Oct ?", "Thu Aug 9 00:00 2012"}, - {"Mon Jul 9 23:35 2012", "0 0 0 */5 Apr,Aug,Oct Mon", "Mon Aug 6 00:00 2012"}, - {"Mon Jul 9 23:35 2012", "0 0 0 */5 Oct Mon", "Mon Oct 1 00:00 2012"}, - - // Wrap around years - {"Mon Jul 9 23:35 2012", "0 0 0 * Feb Mon", "Mon Feb 4 00:00 2013"}, - {"Mon Jul 9 23:35 2012", "0 0 0 * Feb Mon/2", "Fri Feb 1 00:00 2013"}, - - // Wrap around minute, hour, day, month, and year - {"Mon Dec 31 23:59:45 2012", "0 * * * * *", "Tue Jan 1 00:00:00 2013"}, - - // Leap year - {"Mon Jul 9 23:35 2012", "0 0 0 29 Feb ?", "Mon Feb 29 00:00 2016"}, - - // Daylight savings time EST -> EDT - {"2012-03-11T00:00:00-0500", "0 30 2 11 Mar ?", "2013-03-11T02:30:00-0400"}, - - // Daylight savings time EDT -> EST - {"2012-11-04T00:00:00-0400", "0 30 2 04 Nov ?", "2012-11-04T02:30:00-0500"}, - {"2012-11-04T01:45:00-0400", "0 30 1 04 Nov ?", "2012-11-04T01:30:00-0500"}, - - // Unsatisfiable - {"Mon Jul 9 23:35 2012", "0 0 0 30 Feb ?", ""}, - {"Mon Jul 9 23:35 2012", "0 0 0 31 Apr ?", ""}, - } - - for _, c := range runs { - sched, err := Parse(c.spec) - if err != nil { - t.Error(err) - continue - } - actual := sched.Next(getTime(c.time)) - expected := getTime(c.expected) - if !actual.Equal(expected) { - t.Errorf("%s, \"%s\": (expected) %v != %v (actual)", c.time, c.spec, expected, actual) - } - } -} - -func TestErrors(t *testing.T) { - invalidSpecs := []string{ - "xyz", - "60 0 * * *", - "0 60 * * *", - "0 0 * * XYZ", - } - for _, spec := range invalidSpecs { - _, err := Parse(spec) - if err == nil { - t.Error("expected an error parsing: ", spec) - } - } -} - -func getTime(value string) time.Time { - if value == "" { - return time.Time{} - } - t, err := time.Parse("Mon Jan 2 15:04 2006", value) - if err != nil { - t, err = time.Parse("Mon Jan 2 15:04:05 2006", value) - if err != nil { - t, err = time.Parse("2006-01-02T15:04:05-0700", value) - if err != nil { - panic(err) - } - // Daylight savings time tests require location - if ny, err := time.LoadLocation("America/New_York"); err == nil { - t = t.In(ny) - } - } - } - - return t -} diff --git a/modules/mailer/mail.go b/modules/mailer/mail.go index ca5618f1bc..53455fc012 100644 --- a/modules/mailer/mail.go +++ b/modules/mailer/mail.go @@ -14,6 +14,7 @@ import ( "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/markdown" "github.com/gogits/gogs/modules/setting" ) @@ -126,7 +127,7 @@ func SendIssueNotifyMail(u, owner *models.User, repo *models.Repository, issue * subject := fmt.Sprintf("[%s] %s (#%d)", repo.Name, issue.Name, issue.Index) content := fmt.Sprintf("%s<br>-<br> <a href=\"%s%s/%s/issues/%d\">View it on Gogs</a>.", - base.RenderSpecialLink([]byte(strings.Replace(issue.Content, "\n", "<br>", -1)), owner.Name+"/"+repo.Name, repo.ComposeMetas()), + markdown.RenderSpecialLink([]byte(strings.Replace(issue.Content, "\n", "<br>", -1)), owner.Name+"/"+repo.Name, repo.ComposeMetas()), setting.AppUrl, owner.Name, repo.Name, issue.Index) msg := NewMessage(tos, subject, content) msg.Info = fmt.Sprintf("Subject: %s, issue notify", subject) @@ -149,7 +150,7 @@ func SendIssueMentionMail(r macaron.Render, u, owner *models.User, data["IssueLink"] = fmt.Sprintf("%s/%s/issues/%d", owner.Name, repo.Name, issue.Index) data["Subject"] = subject data["ActUserName"] = u.DisplayName() - data["Content"] = string(base.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name, repo.ComposeMetas())) + data["Content"] = string(markdown.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name, repo.ComposeMetas())) body, err := r.HTMLString(string(NOTIFY_MENTION), data) if err != nil { diff --git a/modules/mailer/mailer.go b/modules/mailer/mailer.go index eafe04680a..e1c453a22e 100644 --- a/modules/mailer/mailer.go +++ b/modules/mailer/mailer.go @@ -197,7 +197,10 @@ func processMailQueue() { var mailQueue chan *Message func NewContext() { - if setting.MailService == nil { + // Need to check if mailQueue is nil because in during reinstall (user had installed + // before but swithed install lock off), this function will be called again + // while mail queue is already processing tasks, and produces a race condition. + if setting.MailService == nil || mailQueue != nil { return } diff --git a/modules/base/markdown.go b/modules/markdown/markdown.go index 10158edd32..aad0553afe 100644 --- a/modules/base/markdown.go +++ b/modules/markdown/markdown.go @@ -2,41 +2,51 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package base +package markdown import ( "bytes" "fmt" "io" - "net/http" "path" "path/filepath" "regexp" "strings" "github.com/Unknwon/com" + "github.com/microcosm-cc/bluemonday" "github.com/russross/blackfriday" "golang.org/x/net/html" + "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/setting" ) -// TODO: put this into 'markdown' module. +var Sanitizer = bluemonday.UGCPolicy() -func isletter(c byte) bool { - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') -} +// BuildSanitizer initializes sanitizer with allowed attributes based on settings. +// This function should only be called once during entire application lifecycle. +func BuildSanitizer() { + // Normal markdown-stuff + Sanitizer.AllowAttrs("class").Matching(regexp.MustCompile(`[\p{L}\p{N}\s\-_',:\[\]!\./\\\(\)&]*`)).OnElements("code") + + // Checkboxes + Sanitizer.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input") + Sanitizer.AllowAttrs("checked", "disabled").OnElements("input") -func isalnum(c byte) bool { - return (c >= '0' && c <= '9') || isletter(c) + // Custom URL-Schemes + Sanitizer.AllowURLSchemes(setting.Markdown.CustomURLSchemes...) } var validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`) +// isLink reports whether link fits valid format. func isLink(link []byte) bool { return validLinksPattern.Match(link) } +// IsMarkdownFile reports whether name looks like a Markdown file +// based on its extension. func IsMarkdownFile(name string) bool { name = strings.ToLower(name) switch filepath.Ext(name) { @@ -46,57 +56,46 @@ func IsMarkdownFile(name string) bool { return false } -func IsTextFile(data []byte) (string, bool) { - contentType := http.DetectContentType(data) - if strings.Index(contentType, "text/") != -1 { - return contentType, true - } - return contentType, false -} - -func IsImageFile(data []byte) (string, bool) { - contentType := http.DetectContentType(data) - if strings.Index(contentType, "image/") != -1 { - return contentType, true - } - return contentType, false -} - -// IsReadmeFile returns true if given file name suppose to be a README file. +// IsReadmeFile reports whether name looks like a README file +// based on its extension. func IsReadmeFile(name string) bool { name = strings.ToLower(name) if len(name) < 6 { return false } else if len(name) == 6 { - if name == "readme" { - return true - } - return false - } - if name[:7] == "readme." { - return true + return name == "readme" } - return false + return name[:7] == "readme." } var ( - MentionPattern = regexp.MustCompile(`(\s|^)@[0-9a-zA-Z_\.]+`) - commitPattern = regexp.MustCompile(`(\s|^)https?.*commit/[0-9a-zA-Z]+(#+[0-9a-zA-Z-]*)?`) - issueFullPattern = regexp.MustCompile(`(\s|^)https?.*issues/[0-9]+(#+[0-9a-zA-Z-]*)?`) - issueIndexPattern = regexp.MustCompile(`( |^|\()#[0-9]+\b`) - sha1CurrentPattern = regexp.MustCompile(`\b[0-9a-f]{40}\b`) + // MentionPattern matches string that mentions someone, e.g. @Unknwon + MentionPattern = regexp.MustCompile(`(\s|^)@[0-9a-zA-Z_\.]+`) + + // CommitPattern matches link to certain commit with or without trailing hash, + // e.g. https://try.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2 + CommitPattern = regexp.MustCompile(`(\s|^)https?.*commit/[0-9a-zA-Z]+(#+[0-9a-zA-Z-]*)?`) + + // IssueFullPattern matches link to an issue with or without trailing hash, + // e.g. https://try.gogs.io/gogs/gogs/issues/4#issue-685 + IssueFullPattern = regexp.MustCompile(`(\s|^)https?.*issues/[0-9]+(#+[0-9a-zA-Z-]*)?`) + // IssueIndexPattern matches string that references to an issue, e.g. #1287 + IssueIndexPattern = regexp.MustCompile(`( |^|\()#[0-9]+\b`) + + // Sha1CurrentPattern matches string that represents a commit SHA, e.g. d8a994ef243349f321568f9e36d5c3f444b99cae + Sha1CurrentPattern = regexp.MustCompile(`\b[0-9a-f]{40}\b`) ) -type CustomRender struct { +// Renderer is a extended version of underlying render object. +type Renderer struct { blackfriday.Renderer urlPrefix string } -func (r *CustomRender) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) { +// Link defines how formal links should be processed to produce corresponding HTML elements. +func (r *Renderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) { if len(link) > 0 && !isLink(link) { - if link[0] == '#' { - // link = append([]byte(options.urlPrefix), link...) - } else { + if link[0] != '#' { link = []byte(path.Join(r.urlPrefix, string(link))) } } @@ -104,14 +103,17 @@ func (r *CustomRender) Link(out *bytes.Buffer, link []byte, title []byte, conten r.Renderer.Link(out, link, title, content) } -func (r *CustomRender) AutoLink(out *bytes.Buffer, link []byte, kind int) { - if kind != 1 { +// AutoLink defines how auto-detected links should be processed to produce corresponding HTML elements. +// Reference for kind: https://github.com/russross/blackfriday/blob/master/markdown.go#L69-L76 +func (r *Renderer) AutoLink(out *bytes.Buffer, link []byte, kind int) { + if kind != blackfriday.LINK_TYPE_NORMAL { r.Renderer.AutoLink(out, link, kind) return } - // This method could only possibly serve one link at a time, no need to find all. - m := commitPattern.Find(link) + // Since this method could only possibly serve one link at a time, + // we do not need to find all. + m := CommitPattern.Find(link) if m != nil { m = bytes.TrimSpace(m) i := strings.Index(string(m), "commit/") @@ -119,11 +121,11 @@ func (r *CustomRender) AutoLink(out *bytes.Buffer, link []byte, kind int) { if j == -1 { j = len(m) } - out.WriteString(fmt.Sprintf(` <code><a href="%s">%s</a></code>`, m, ShortSha(string(m[i+7:j])))) + out.WriteString(fmt.Sprintf(` <code><a href="%s">%s</a></code>`, m, base.ShortSha(string(m[i+7:j])))) return } - m = issueFullPattern.Find(link) + m = IssueFullPattern.Find(link) if m != nil { m = bytes.TrimSpace(m) i := strings.Index(string(m), "issues/") @@ -131,14 +133,16 @@ func (r *CustomRender) AutoLink(out *bytes.Buffer, link []byte, kind int) { if j == -1 { j = len(m) } - out.WriteString(fmt.Sprintf(` <a href="%s">#%s</a>`, m, ShortSha(string(m[i+7:j])))) + out.WriteString(fmt.Sprintf(` <a href="%s">#%s</a>`, m, base.ShortSha(string(m[i+7:j])))) return } r.Renderer.AutoLink(out, link, kind) } -func (options *CustomRender) ListItem(out *bytes.Buffer, text []byte, flags int) { +// ListItem defines how list items should be processed to produce corresponding HTML elements. +func (options *Renderer) ListItem(out *bytes.Buffer, text []byte, flags int) { + // Detect procedures to draw checkboxes. switch { case bytes.HasPrefix(text, []byte("[ ] ")): text = append([]byte(`<input type="checkbox" disabled="" />`), text[3:]...) @@ -148,6 +152,8 @@ func (options *CustomRender) ListItem(out *bytes.Buffer, text []byte, flags int) options.Renderer.ListItem(out, text, flags) } +// Note: this section is for purpose of increase performance and +// reduce memory allocation at runtime since they are constant literals. var ( svgSuffix = []byte(".svg") svgSuffixWithMark = []byte(".svg?") @@ -155,11 +161,13 @@ var ( spaceEncodedBytes = []byte("%20") ) -func (r *CustomRender) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) { +// Image defines how images should be processed to produce corresponding HTML elements. +func (r *Renderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) { prefix := strings.Replace(r.urlPrefix, "/src/", "/raw/", 1) if len(link) > 0 { if isLink(link) { // External link with .svg suffix usually means CI status. + // TODO: define a keyword to allow non-svg images render as external link. if bytes.HasSuffix(link, svgSuffix) || bytes.Contains(link, svgSuffixWithMark) { r.Renderer.Image(out, link, title, alt) return @@ -180,6 +188,8 @@ func (r *CustomRender) Image(out *bytes.Buffer, link []byte, title []byte, alt [ out.WriteString("</a>") } +// cutoutVerbosePrefix cutouts URL prefix including sub-path to +// return a clean unified string of request URL path. func cutoutVerbosePrefix(prefix string) string { count := 0 for i := 0; i < len(prefix); i++ { @@ -193,29 +203,40 @@ func cutoutVerbosePrefix(prefix string) string { return prefix } +// RenderIssueIndexPattern renders issue indexes to corresponding links. func RenderIssueIndexPattern(rawBytes []byte, urlPrefix string, metas map[string]string) []byte { urlPrefix = cutoutVerbosePrefix(urlPrefix) - ms := issueIndexPattern.FindAll(rawBytes, -1) + ms := IssueIndexPattern.FindAll(rawBytes, -1) for _, m := range ms { var space string - m2 := m - if m2[0] != '#' { - space = string(m2[0]) - m2 = m2[1:] + if m[0] != '#' { + space = string(m[0]) + m = m[1:] } if metas == nil { rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(`%s<a href="%s/issues/%s">%s</a>`, - space, urlPrefix, m2[1:], m2)), 1) + space, urlPrefix, m[1:], m)), 1) } else { // Support for external issue tracker - metas["index"] = string(m2[1:]) + metas["index"] = string(m[1:]) rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(`%s<a href="%s">%s</a>`, - space, com.Expand(metas["format"], metas), m2)), 1) + space, com.Expand(metas["format"], metas), m)), 1) } } return rawBytes } +// RenderSha1CurrentPattern renders SHA1 strings to corresponding links that assumes in the same repository. +func RenderSha1CurrentPattern(rawBytes []byte, urlPrefix string) []byte { + ms := Sha1CurrentPattern.FindAll(rawBytes, -1) + for _, m := range ms { + rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf( + `<a href="%s/commit/%s"><code>%s</code></a>`, urlPrefix, m, base.ShortSha(string(m)))), -1) + } + return rawBytes +} + +// RenderSpecialLink renders mentions, indexes and SHA1 strings to corresponding links. func RenderSpecialLink(rawBytes []byte, urlPrefix string, metas map[string]string) []byte { ms := MentionPattern.FindAll(rawBytes, -1) for _, m := range ms { @@ -229,20 +250,12 @@ func RenderSpecialLink(rawBytes []byte, urlPrefix string, metas map[string]strin return rawBytes } -func RenderSha1CurrentPattern(rawBytes []byte, urlPrefix string) []byte { - ms := sha1CurrentPattern.FindAll(rawBytes, -1) - for _, m := range ms { - rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf( - `<a href="%s/commit/%s"><code>%s</code></a>`, urlPrefix, m, ShortSha(string(m)))), -1) - } - return rawBytes -} - -func RenderRawMarkdown(body []byte, urlPrefix string) []byte { +// RenderRaw renders Markdown to HTML without handling special links. +func RenderRaw(body []byte, urlPrefix string) []byte { htmlFlags := 0 htmlFlags |= blackfriday.HTML_SKIP_STYLE htmlFlags |= blackfriday.HTML_OMIT_CONTENTS - renderer := &CustomRender{ + renderer := &Renderer{ Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""), urlPrefix: urlPrefix, } @@ -272,9 +285,9 @@ var ( var noEndTags = []string{"img", "input", "br", "hr"} -// PostProcessMarkdown treats different types of HTML differently, +// PostProcess treats different types of HTML differently, // and only renders special links for plain text blocks. -func PostProcessMarkdown(rawHtml []byte, urlPrefix string, metas map[string]string) []byte { +func PostProcess(rawHtml []byte, urlPrefix string, metas map[string]string) []byte { startTags := make([]string, 0, 5) var buf bytes.Buffer tokenizer := html.NewTokenizer(bytes.NewReader(rawHtml)) @@ -342,13 +355,15 @@ OUTER_LOOP: return rawHtml } -func RenderMarkdown(rawBytes []byte, urlPrefix string, metas map[string]string) []byte { - result := RenderRawMarkdown(rawBytes, urlPrefix) - result = PostProcessMarkdown(result, urlPrefix, metas) +// Render renders Markdown to HTML with special links. +func Render(rawBytes []byte, urlPrefix string, metas map[string]string) []byte { + result := RenderRaw(rawBytes, urlPrefix) + result = PostProcess(result, urlPrefix, metas) result = Sanitizer.SanitizeBytes(result) return result } -func RenderMarkdownString(raw, urlPrefix string, metas map[string]string) string { - return string(RenderMarkdown([]byte(raw), urlPrefix, metas)) +// RenderString renders Markdown to HTML with special links and returns string type. +func RenderString(raw, urlPrefix string, metas map[string]string) string { + return string(Render([]byte(raw), urlPrefix, metas)) } diff --git a/modules/template/template.go b/modules/template/template.go index aefb21946d..52ec054500 100644 --- a/modules/template/template.go +++ b/modules/template/template.go @@ -18,6 +18,7 @@ import ( "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/markdown" "github.com/gogits/gogs/modules/setting" ) @@ -108,7 +109,7 @@ func Safe(raw string) template.HTML { } func Str2html(raw string) template.HTML { - return template.HTML(base.Sanitizer.Sanitize(raw)) + return template.HTML(markdown.Sanitizer.Sanitize(raw)) } func Range(l int) []int { @@ -188,7 +189,7 @@ func ReplaceLeft(s, old, new string) string { // RenderCommitMessage renders commit message with XSS-safe and special links. func RenderCommitMessage(full bool, msg, urlPrefix string, metas map[string]string) template.HTML { cleanMsg := template.HTMLEscapeString(msg) - fullMessage := string(base.RenderIssueIndexPattern([]byte(cleanMsg), urlPrefix, metas)) + fullMessage := string(markdown.RenderIssueIndexPattern([]byte(cleanMsg), urlPrefix, metas)) msgLines := strings.Split(strings.TrimSpace(fullMessage), "\n") numLines := len(msgLines) if numLines == 0 { diff --git a/modules/uuid/README.md b/modules/uuid/README.md deleted file mode 100644 index 242b27579f..0000000000 --- a/modules/uuid/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# UUID package for Go language - -[![Build Status](https://travis-ci.org/satori/go.uuid.png?branch=master)](https://travis-ci.org/satori/go.uuid) -[![GoDoc](http://godoc.org/github.com/satori/go.uuid?status.png)](http://godoc.org/github.com/satori/go.uuid) - -This package provides pure Go implementation of Universally Unique Identifier (UUID). Supported both creation and parsing of UUIDs. - -With 100% test coverage and benchmarks out of box. - -Supported versions: -* Version 1, based on timestamp and MAC address (RFC 4122) -* Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1) -* Version 3, based on MD5 hashing (RFC 4122) -* Version 4, based on random numbers (RFC 4122) -* Version 5, based on SHA-1 hashing (RFC 4122) - -## Installation - -Use the `go` command: - - $ go get github.com/satori/go.uuid - -## Requirements - -UUID package requires any stable version of Go Programming Language. - -It is tested against following versions of Go: 1.0, 1.1, 1.2 - -## Example - -```go -package main - -import ( - "fmt" - "github.com/satori/go.uuid" -) - -func main() { - // Creating UUID Version 4 - u1 := uuid.NewV4() - fmt.Printf("UUIDv4: %s\n", u1) - - // Parsing UUID from string input - u2, err := uuid.FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") - if err != nil { - fmt.Printf("Something gone wrong: %s", err) - } - fmt.Printf("Successfully parsed: %s", u2) -} -``` - -## Documentation - -[Documentation](http://godoc.org/github.com/satori/go.uuid) is hosted at GoDoc project. - -## Links -* [RFC 4122](http://tools.ietf.org/html/rfc4122) -* [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01) - -## Copyright - -Copyright (C) 2013 by Maxim Bublis <b@codemonkey.ru>. - -UUID package released under MIT License. -See [LICENSE](https://github.com/satori/go.uuid/blob/master/LICENSE) for details. diff --git a/modules/uuid/benchmarks_test.go b/modules/uuid/benchmarks_test.go deleted file mode 100644 index fd261d1bfb..0000000000 --- a/modules/uuid/benchmarks_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (C) 2013 by Maxim Bublis <b@codemonkey.ru> -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package uuid - -import ( - "testing" -) - -func BenchmarkFromBytes(b *testing.B) { - bytes := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} - for i := 0; i < b.N; i++ { - FromBytes(bytes) - } -} - -func BenchmarkFromString(b *testing.B) { - s := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" - for i := 0; i < b.N; i++ { - FromString(s) - } -} - -func BenchmarkFromStringUrn(b *testing.B) { - s := "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" - for i := 0; i < b.N; i++ { - FromString(s) - } -} - -func BenchmarkFromStringWithBrackets(b *testing.B) { - s := "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" - for i := 0; i < b.N; i++ { - FromString(s) - } -} - -func BenchmarkNewV1(b *testing.B) { - for i := 0; i < b.N; i++ { - NewV1() - } -} - -func BenchmarkNewV2(b *testing.B) { - for i := 0; i < b.N; i++ { - NewV2(DomainPerson) - } -} - -func BenchmarkNewV3(b *testing.B) { - for i := 0; i < b.N; i++ { - NewV3(NamespaceDNS, "www.example.com") - } -} - -func BenchmarkNewV4(b *testing.B) { - for i := 0; i < b.N; i++ { - NewV4() - } -} - -func BenchmarkNewV5(b *testing.B) { - for i := 0; i < b.N; i++ { - NewV5(NamespaceDNS, "www.example.com") - } -} diff --git a/modules/uuid/uuid.go b/modules/uuid/uuid.go deleted file mode 100644 index 5c5566f0d6..0000000000 --- a/modules/uuid/uuid.go +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright (C) 2013 by Maxim Bublis <b@codemonkey.ru> -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -// Package uuid provides implementation of Universally Unique Identifier (UUID). -// Supported versions are 1, 3, 4 and 5 (as specified in RFC 4122) and -// version 2 (as specified in DCE 1.1). -package uuid - -import ( - "bytes" - "crypto/md5" - "crypto/rand" - "crypto/sha1" - "encoding/binary" - "encoding/hex" - "fmt" - "hash" - "net" - "os" - "strings" - "sync" - "time" -) - -// UUID layout variants. -const ( - VariantNCS = iota - VariantRFC4122 - VariantMicrosoft - VariantFuture -) - -// UUID DCE domains. -const ( - DomainPerson = iota - DomainGroup - DomainOrg -) - -// Difference in 100-nanosecond intervals between -// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). -const epochStart = 122192928000000000 - -// UUID v1/v2 storage. -var ( - storageMutex sync.Mutex - clockSequence uint16 - lastTime uint64 - hardwareAddr [6]byte - posixUID = uint32(os.Getuid()) - posixGID = uint32(os.Getgid()) -) - -// Epoch calculation function -var epochFunc func() uint64 - -// Initialize storage -func init() { - buf := make([]byte, 2) - rand.Read(buf) - clockSequence = binary.BigEndian.Uint16(buf) - - // Initialize hardwareAddr randomly in case - // of real network interfaces absence - rand.Read(hardwareAddr[:]) - - // Set multicast bit as recommended in RFC 4122 - hardwareAddr[0] |= 0x01 - - interfaces, err := net.Interfaces() - if err == nil { - for _, iface := range interfaces { - if len(iface.HardwareAddr) >= 6 { - copy(hardwareAddr[:], iface.HardwareAddr) - break - } - } - } - epochFunc = unixTimeFunc -} - -// Returns difference in 100-nanosecond intervals between -// UUID epoch (October 15, 1582) and current time. -// This is default epoch calculation function. -func unixTimeFunc() uint64 { - return epochStart + uint64(time.Now().UnixNano()/100) -} - -// UUID representation compliant with specification -// described in RFC 4122. -type UUID [16]byte - -// Predefined namespace UUIDs. -var ( - NamespaceDNS, _ = FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") - NamespaceURL, _ = FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8") - NamespaceOID, _ = FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8") - NamespaceX500, _ = FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8") -) - -// And returns result of binary AND of two UUIDs. -func And(u1 UUID, u2 UUID) UUID { - u := UUID{} - for i := 0; i < 16; i++ { - u[i] = u1[i] & u2[i] - } - return u -} - -// Or returns result of binary OR of two UUIDs. -func Or(u1 UUID, u2 UUID) UUID { - u := UUID{} - for i := 0; i < 16; i++ { - u[i] = u1[i] | u2[i] - } - return u -} - -// Equal returns true if u1 and u2 equals, otherwise returns false. -func Equal(u1 UUID, u2 UUID) bool { - return bytes.Equal(u1[:], u2[:]) -} - -// Version returns algorithm version used to generate UUID. -func (u UUID) Version() uint { - return uint(u[6] >> 4) -} - -// Variant returns UUID layout variant. -func (u UUID) Variant() uint { - switch { - case (u[8] & 0x80) == 0x00: - return VariantNCS - case (u[8]&0xc0)|0x80 == 0x80: - return VariantRFC4122 - case (u[8]&0xe0)|0xc0 == 0xc0: - return VariantMicrosoft - } - return VariantFuture -} - -// Bytes returns bytes slice representation of UUID. -func (u UUID) Bytes() []byte { - return u[:] -} - -// Returns canonical string representation of UUID: -// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. -func (u UUID) String() string { - return fmt.Sprintf("%x-%x-%x-%x-%x", - u[:4], u[4:6], u[6:8], u[8:10], u[10:]) -} - -// SetVersion sets version bits. -func (u *UUID) SetVersion(v byte) { - u[6] = (u[6] & 0x0f) | (v << 4) -} - -// SetVariant sets variant bits as described in RFC 4122. -func (u *UUID) SetVariant() { - u[8] = (u[8] & 0xbf) | 0x80 -} - -// MarshalText implements the encoding.TextMarshaler interface. -// The encoding is the same as returned by String. -func (u UUID) MarshalText() (text []byte, err error) { - text = []byte(u.String()) - return -} - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -// UUID is expected in a form accepted by FromString. -func (u *UUID) UnmarshalText(text []byte) error { - s := string(text) - u2, err := FromString(s) - if err != nil { - return err - } - *u = u2 - return nil -} - -// MarshalBinary implements the encoding.BinaryMarshaler interface. -func (u UUID) MarshalBinary() (data []byte, err error) { - data = u.Bytes() - return -} - -// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. -func (u *UUID) UnmarshalBinary(data []byte) error { - u2, err := FromBytes(data) - if err != nil { - return err - } - *u = u2 - return nil -} - -// FromBytes returns UUID converted from raw byte slice input. -// It will return error if the slice isn't 16 bytes long. -func FromBytes(input []byte) (u UUID, err error) { - if len(input) != 16 { - err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(input)) - return - } - - copy(u[:], input) - - return -} - -// FromString returns UUID parsed from string input. -// Following formats are supported: -// "6ba7b810-9dad-11d1-80b4-00c04fd430c8", -// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", -// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" -func FromString(input string) (u UUID, err error) { - s := strings.Replace(input, "-", "", -1) - - if len(s) == 41 && s[:9] == "urn:uuid:" { - s = s[9:] - } else if len(s) == 34 && s[0] == '{' && s[33] == '}' { - s = s[1:33] - } - - if len(s) != 32 { - err = fmt.Errorf("uuid: invalid UUID string: %s", input) - return - } - - b := []byte(s) - _, err = hex.Decode(u[:], b) - - return -} - -// Returns UUID v1/v2 storage state. -// Returns epoch timestamp and clock sequence. -func getStorage() (uint64, uint16) { - storageMutex.Lock() - defer storageMutex.Unlock() - - timeNow := epochFunc() - // Clock changed backwards since last UUID generation. - // Should increase clock sequence. - if timeNow <= lastTime { - clockSequence++ - } - lastTime = timeNow - - return timeNow, clockSequence -} - -// NewV1 returns UUID based on current timestamp and MAC address. -func NewV1() UUID { - u := UUID{} - - timeNow, clockSeq := getStorage() - - binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) - binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) - binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) - binary.BigEndian.PutUint16(u[8:], clockSeq) - - copy(u[10:], hardwareAddr[:]) - - u.SetVersion(1) - u.SetVariant() - - return u -} - -// NewV2 returns DCE Security UUID based on POSIX UID/GID. -func NewV2(domain byte) UUID { - u := UUID{} - - switch domain { - case DomainPerson: - binary.BigEndian.PutUint32(u[0:], posixUID) - case DomainGroup: - binary.BigEndian.PutUint32(u[0:], posixGID) - } - - timeNow, clockSeq := getStorage() - - binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) - binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) - binary.BigEndian.PutUint16(u[8:], clockSeq) - u[9] = domain - - copy(u[10:], hardwareAddr[:]) - - u.SetVersion(2) - u.SetVariant() - - return u -} - -// NewV3 returns UUID based on MD5 hash of namespace UUID and name. -func NewV3(ns UUID, name string) UUID { - u := newFromHash(md5.New(), ns, name) - u.SetVersion(3) - u.SetVariant() - - return u -} - -// NewV4 returns random generated UUID. -func NewV4() UUID { - u := UUID{} - rand.Read(u[:]) - u.SetVersion(4) - u.SetVariant() - - return u -} - -// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. -func NewV5(ns UUID, name string) UUID { - u := newFromHash(sha1.New(), ns, name) - u.SetVersion(5) - u.SetVariant() - - return u -} - -// Returns UUID based on hashing of namespace UUID and name. -func newFromHash(h hash.Hash, ns UUID, name string) UUID { - u := UUID{} - h.Write(ns[:]) - h.Write([]byte(name)) - copy(u[:], h.Sum(nil)) - - return u -} diff --git a/modules/uuid/uuid_test.go b/modules/uuid/uuid_test.go deleted file mode 100644 index 0bc85b287a..0000000000 --- a/modules/uuid/uuid_test.go +++ /dev/null @@ -1,399 +0,0 @@ -// Copyright (C) 2013 by Maxim Bublis <b@codemonkey.ru> -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package uuid - -import ( - "bytes" - "testing" -) - -func TestBytes(t *testing.T) { - u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} - - bytes1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} - - if !bytes.Equal(u.Bytes(), bytes1) { - t.Errorf("Incorrect bytes representation for UUID: %s", u) - } -} - -func TestString(t *testing.T) { - if NamespaceDNS.String() != "6ba7b810-9dad-11d1-80b4-00c04fd430c8" { - t.Errorf("Incorrect string representation for UUID: %s", NamespaceDNS.String()) - } -} - -func TestEqual(t *testing.T) { - if !Equal(NamespaceDNS, NamespaceDNS) { - t.Errorf("Incorrect comparison of %s and %s", NamespaceDNS, NamespaceDNS) - } - - if Equal(NamespaceDNS, NamespaceURL) { - t.Errorf("Incorrect comparison of %s and %s", NamespaceDNS, NamespaceURL) - } -} - -func TestOr(t *testing.T) { - u1 := UUID{0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff} - u2 := UUID{0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00} - - u := UUID{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} - - if !Equal(u, Or(u1, u2)) { - t.Errorf("Incorrect bitwise OR result %s", Or(u1, u2)) - } -} - -func TestAnd(t *testing.T) { - u1 := UUID{0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff} - u2 := UUID{0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00} - - u := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - - if !Equal(u, And(u1, u2)) { - t.Errorf("Incorrect bitwise AND result %s", And(u1, u2)) - } -} - -func TestVersion(t *testing.T) { - u := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - - if u.Version() != 1 { - t.Errorf("Incorrect version for UUID: %d", u.Version()) - } -} - -func TestSetVersion(t *testing.T) { - u := UUID{} - u.SetVersion(4) - - if u.Version() != 4 { - t.Errorf("Incorrect version for UUID after u.setVersion(4): %d", u.Version()) - } -} - -func TestVariant(t *testing.T) { - u1 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - - if u1.Variant() != VariantNCS { - t.Errorf("Incorrect variant for UUID variant %d: %d", VariantNCS, u1.Variant()) - } - - u2 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - - if u2.Variant() != VariantRFC4122 { - t.Errorf("Incorrect variant for UUID variant %d: %d", VariantRFC4122, u2.Variant()) - } - - u3 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - - if u3.Variant() != VariantMicrosoft { - t.Errorf("Incorrect variant for UUID variant %d: %d", VariantMicrosoft, u3.Variant()) - } - - u4 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - - if u4.Variant() != VariantFuture { - t.Errorf("Incorrect variant for UUID variant %d: %d", VariantFuture, u4.Variant()) - } -} - -func TestSetVariant(t *testing.T) { - u := new(UUID) - u.SetVariant() - - if u.Variant() != VariantRFC4122 { - t.Errorf("Incorrect variant for UUID after u.setVariant(): %d", u.Variant()) - } -} - -func TestFromBytes(t *testing.T) { - u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} - b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} - - u1, err := FromBytes(b1) - if err != nil { - t.Errorf("Error parsing UUID from bytes: %s", err) - } - - if !Equal(u, u1) { - t.Errorf("UUIDs should be equal: %s and %s", u, u1) - } - - b2 := []byte{} - - _, err = FromBytes(b2) - if err == nil { - t.Errorf("Should return error parsing from empty byte slice, got %s", err) - } -} - -func TestMarshalBinary(t *testing.T) { - u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} - b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} - - b2, err := u.MarshalBinary() - if err != nil { - t.Errorf("Error marshaling UUID: %s", err) - } - - if !bytes.Equal(b1, b2) { - t.Errorf("Marshaled UUID should be %s, got %s", b1, b2) - } -} - -func TestUnmarshalBinary(t *testing.T) { - u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} - b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} - - u1 := UUID{} - err := u1.UnmarshalBinary(b1) - if err != nil { - t.Errorf("Error unmarshaling UUID: %s", err) - } - - if !Equal(u, u1) { - t.Errorf("UUIDs should be equal: %s and %s", u, u1) - } - - b2 := []byte{} - u2 := UUID{} - - err = u2.UnmarshalBinary(b2) - if err == nil { - t.Errorf("Should return error unmarshalling from empty byte slice, got %s", err) - } -} - -func TestFromString(t *testing.T) { - u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} - - s1 := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" - s2 := "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" - s3 := "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" - - _, err := FromString("") - if err == nil { - t.Errorf("Should return error trying to parse empty string, got %s", err) - } - - u1, err := FromString(s1) - if err != nil { - t.Errorf("Error parsing UUID from string: %s", err) - } - - if !Equal(u, u1) { - t.Errorf("UUIDs should be equal: %s and %s", u, u1) - } - - u2, err := FromString(s2) - if err != nil { - t.Errorf("Error parsing UUID from string: %s", err) - } - - if !Equal(u, u2) { - t.Errorf("UUIDs should be equal: %s and %s", u, u2) - } - - u3, err := FromString(s3) - if err != nil { - t.Errorf("Error parsing UUID from string: %s", err) - } - - if !Equal(u, u3) { - t.Errorf("UUIDs should be equal: %s and %s", u, u3) - } -} - -func TestMarshalText(t *testing.T) { - u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} - b1 := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8") - - b2, err := u.MarshalText() - if err != nil { - t.Errorf("Error marshaling UUID: %s", err) - } - - if !bytes.Equal(b1, b2) { - t.Errorf("Marshaled UUID should be %s, got %s", b1, b2) - } -} - -func TestUnmarshalText(t *testing.T) { - u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} - b1 := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8") - - u1 := UUID{} - err := u1.UnmarshalText(b1) - if err != nil { - t.Errorf("Error unmarshaling UUID: %s", err) - } - - if !Equal(u, u1) { - t.Errorf("UUIDs should be equal: %s and %s", u, u1) - } - - b2 := []byte("") - u2 := UUID{} - - err = u2.UnmarshalText(b2) - if err == nil { - t.Errorf("Should return error trying to unmarshal from empty string") - } -} - -func TestNewV1(t *testing.T) { - u := NewV1() - - if u.Version() != 1 { - t.Errorf("UUIDv1 generated with incorrect version: %d", u.Version()) - } - - if u.Variant() != VariantRFC4122 { - t.Errorf("UUIDv1 generated with incorrect variant: %d", u.Variant()) - } - - u1 := NewV1() - u2 := NewV1() - - if Equal(u1, u2) { - t.Errorf("UUIDv1 generated two equal UUIDs: %s and %s", u1, u2) - } - - oldFunc := epochFunc - epochFunc = func() uint64 { return 0 } - - u3 := NewV1() - u4 := NewV1() - - if Equal(u3, u4) { - t.Errorf("UUIDv1 generated two equal UUIDs: %s and %s", u3, u4) - } - - epochFunc = oldFunc -} - -func TestNewV2(t *testing.T) { - u1 := NewV2(DomainPerson) - - if u1.Version() != 2 { - t.Errorf("UUIDv2 generated with incorrect version: %d", u1.Version()) - } - - if u1.Variant() != VariantRFC4122 { - t.Errorf("UUIDv2 generated with incorrect variant: %d", u1.Variant()) - } - - u2 := NewV2(DomainGroup) - - if u2.Version() != 2 { - t.Errorf("UUIDv2 generated with incorrect version: %d", u2.Version()) - } - - if u2.Variant() != VariantRFC4122 { - t.Errorf("UUIDv2 generated with incorrect variant: %d", u2.Variant()) - } -} - -func TestNewV3(t *testing.T) { - u := NewV3(NamespaceDNS, "www.example.com") - - if u.Version() != 3 { - t.Errorf("UUIDv3 generated with incorrect version: %d", u.Version()) - } - - if u.Variant() != VariantRFC4122 { - t.Errorf("UUIDv3 generated with incorrect variant: %d", u.Variant()) - } - - if u.String() != "5df41881-3aed-3515-88a7-2f4a814cf09e" { - t.Errorf("UUIDv3 generated incorrectly: %s", u.String()) - } - - u = NewV3(NamespaceDNS, "python.org") - - if u.String() != "6fa459ea-ee8a-3ca4-894e-db77e160355e" { - t.Errorf("UUIDv3 generated incorrectly: %s", u.String()) - } - - u1 := NewV3(NamespaceDNS, "golang.org") - u2 := NewV3(NamespaceDNS, "golang.org") - if !Equal(u1, u2) { - t.Errorf("UUIDv3 generated different UUIDs for same namespace and name: %s and %s", u1, u2) - } - - u3 := NewV3(NamespaceDNS, "example.com") - if Equal(u1, u3) { - t.Errorf("UUIDv3 generated same UUIDs for different names in same namespace: %s and %s", u1, u2) - } - - u4 := NewV3(NamespaceURL, "golang.org") - if Equal(u1, u4) { - t.Errorf("UUIDv3 generated same UUIDs for sane names in different namespaces: %s and %s", u1, u4) - } -} - -func TestNewV4(t *testing.T) { - u := NewV4() - - if u.Version() != 4 { - t.Errorf("UUIDv4 generated with incorrect version: %d", u.Version()) - } - - if u.Variant() != VariantRFC4122 { - t.Errorf("UUIDv4 generated with incorrect variant: %d", u.Variant()) - } -} - -func TestNewV5(t *testing.T) { - u := NewV5(NamespaceDNS, "www.example.com") - - if u.Version() != 5 { - t.Errorf("UUIDv5 generated with incorrect version: %d", u.Version()) - } - - if u.Variant() != VariantRFC4122 { - t.Errorf("UUIDv5 generated with incorrect variant: %d", u.Variant()) - } - - u = NewV5(NamespaceDNS, "python.org") - - if u.String() != "886313e1-3b8a-5372-9b90-0c9aee199e5d" { - t.Errorf("UUIDv5 generated incorrectly: %s", u.String()) - } - - u1 := NewV5(NamespaceDNS, "golang.org") - u2 := NewV5(NamespaceDNS, "golang.org") - if !Equal(u1, u2) { - t.Errorf("UUIDv5 generated different UUIDs for same namespace and name: %s and %s", u1, u2) - } - - u3 := NewV5(NamespaceDNS, "example.com") - if Equal(u1, u3) { - t.Errorf("UUIDv5 generated same UUIDs for different names in same namespace: %s and %s", u1, u2) - } - - u4 := NewV5(NamespaceURL, "golang.org") - if Equal(u1, u4) { - t.Errorf("UUIDv3 generated same UUIDs for sane names in different namespaces: %s and %s", u1, u4) - } -} diff --git a/routers/admin/admin.go b/routers/admin/admin.go index 5d665adbad..27f2fa5b24 100644 --- a/routers/admin/admin.go +++ b/routers/admin/admin.go @@ -14,8 +14,8 @@ import ( "gopkg.in/macaron.v1" "github.com/gogits/gogs/models" - "github.com/gogits/gogs/models/cron" "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/cron" "github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/process" "github.com/gogits/gogs/modules/setting" diff --git a/routers/admin/auths.go b/routers/admin/auths.go index 659b8fcf67..c519d5a7e0 100644 --- a/routers/admin/auths.go +++ b/routers/admin/auths.go @@ -81,6 +81,7 @@ func parseLDAPConfig(form auth.AuthenticationForm) *models.LDAPConfig { AttributeName: form.AttributeName, AttributeSurname: form.AttributeSurname, AttributeMail: form.AttributeMail, + AttributesInBind: form.AttributesInBind, Filter: form.Filter, AdminFilter: form.AdminFilter, Enabled: true, diff --git a/routers/api/v1/misc/markdown.go b/routers/api/v1/misc/markdown.go index e4c4202694..b98a362c3d 100644 --- a/routers/api/v1/misc/markdown.go +++ b/routers/api/v1/misc/markdown.go @@ -7,7 +7,7 @@ package misc import ( api "github.com/gogits/go-gogs-client" - "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/markdown" "github.com/gogits/gogs/modules/middleware" ) @@ -25,9 +25,9 @@ func Markdown(ctx *middleware.Context, form api.MarkdownOption) { switch form.Mode { case "gfm": - ctx.Write(base.RenderMarkdown([]byte(form.Text), form.Context, nil)) + ctx.Write(markdown.Render([]byte(form.Text), form.Context, nil)) default: - ctx.Write(base.RenderRawMarkdown([]byte(form.Text), "")) + ctx.Write(markdown.RenderRaw([]byte(form.Text), "")) } } @@ -38,5 +38,5 @@ func MarkdownRaw(ctx *middleware.Context) { ctx.APIError(422, "", err) return } - ctx.Write(base.RenderRawMarkdown(body, "")) + ctx.Write(markdown.RenderRaw(body, "")) } diff --git a/routers/install.go b/routers/install.go index b8b7dec1e7..c65f26296a 100644 --- a/routers/install.go +++ b/routers/install.go @@ -20,11 +20,12 @@ import ( "github.com/gogits/git-module" "github.com/gogits/gogs/models" - "github.com/gogits/gogs/models/cron" "github.com/gogits/gogs/modules/auth" "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/cron" "github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/mailer" + "github.com/gogits/gogs/modules/markdown" "github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/ssh" @@ -93,7 +94,7 @@ func GlobalInit() { } // Build Sanitizer - base.BuildSanitizer() + markdown.BuildSanitizer() } func InstallInit(ctx *middleware.Context) { diff --git a/routers/repo/issue.go b/routers/repo/issue.go index 400da72029..a783e88cd1 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -7,6 +7,8 @@ package repo import ( "errors" "fmt" + "io" + "io/ioutil" "net/http" "net/url" "strings" @@ -20,6 +22,7 @@ import ( "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/mailer" + "github.com/gogits/gogs/modules/markdown" "github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/setting" ) @@ -34,11 +37,19 @@ const ( MILESTONE base.TplName = "repo/issue/milestones" MILESTONE_NEW base.TplName = "repo/issue/milestone_new" MILESTONE_EDIT base.TplName = "repo/issue/milestone_edit" + + ISSUE_TEMPLATE_KEY = "IssueTemplate" ) var ( ErrFileTypeForbidden = errors.New("File type is not allowed") ErrTooManyFiles = errors.New("Maximum number of files to upload exceeded") + + IssueTemplateCandidates = []string{ + "ISSUE_TEMPLATE.md", + ".gogs/ISSUE_TEMPLATE.md", + ".github/ISSUE_TEMPLATE.md", + } ) func MustEnableIssues(ctx *middleware.Context) { @@ -281,9 +292,47 @@ func RetrieveRepoMetas(ctx *middleware.Context, repo *models.Repository) []*mode return labels } +func getFileContentFromDefaultBranch(ctx *middleware.Context, filename string) (string, bool) { + var r io.Reader + var bytes []byte + + if ctx.Repo.Commit == nil { + var err error + ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch) + if err != nil { + return "", false + } + } + + entry, err := ctx.Repo.Commit.GetTreeEntryByPath(filename) + if err != nil { + return "", false + } + r, err = entry.Blob().Data() + if err != nil { + return "", false + } + bytes, err = ioutil.ReadAll(r) + if err != nil { + return "", false + } + return string(bytes), true +} + +func setTemplateIfExists(ctx *middleware.Context, ctxDataKey string, possibleFiles []string) { + for _, filename := range possibleFiles { + content, found := getFileContentFromDefaultBranch(ctx, filename) + if found { + ctx.Data[ctxDataKey] = content + return + } + } +} + func NewIssue(ctx *middleware.Context) { ctx.Data["Title"] = ctx.Tr("repo.issues.new") ctx.Data["PageIsIssueList"] = true + setTemplateIfExists(ctx, ISSUE_TEMPLATE_KEY, IssueTemplateCandidates) renderAttachmentSettings(ctx) RetrieveRepoMetas(ctx, ctx.Repo.Repository) @@ -352,7 +401,7 @@ func ValidateRepoMetas(ctx *middleware.Context, form auth.CreateIssueForm) ([]in func notifyWatchersAndMentions(ctx *middleware.Context, issue *models.Issue) { // Update mentions - mentions := base.MentionPattern.FindAllString(issue.Content, -1) + mentions := markdown.MentionPattern.FindAllString(issue.Content, -1) if len(mentions) > 0 { for i := range mentions { mentions[i] = strings.TrimSpace(mentions[i])[1:] @@ -531,7 +580,7 @@ func ViewIssue(ctx *middleware.Context) { ctx.Handle(500, "GetPoster", err) return } - issue.RenderedContent = string(base.RenderMarkdown([]byte(issue.Content), ctx.Repo.RepoLink, + issue.RenderedContent = string(markdown.Render([]byte(issue.Content), ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())) repo := ctx.Repo.Repository @@ -602,7 +651,7 @@ func ViewIssue(ctx *middleware.Context) { participants[0] = issue.Poster for _, comment = range issue.Comments { if comment.Type == models.COMMENT_TYPE_COMMENT { - comment.RenderedContent = string(base.RenderMarkdown([]byte(comment.Content), ctx.Repo.RepoLink, + comment.RenderedContent = string(markdown.Render([]byte(comment.Content), ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())) // Check tag. @@ -705,7 +754,7 @@ func UpdateIssueContent(ctx *middleware.Context) { } ctx.JSON(200, map[string]interface{}{ - "content": string(base.RenderMarkdown([]byte(issue.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())), + "content": string(markdown.Render([]byte(issue.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())), }) } @@ -942,7 +991,7 @@ func UpdateCommentContent(ctx *middleware.Context) { } ctx.JSON(200, map[string]interface{}{ - "content": string(base.RenderMarkdown([]byte(comment.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())), + "content": string(markdown.Render([]byte(comment.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())), }) } @@ -1040,7 +1089,7 @@ func Milestones(ctx *middleware.Context) { return } for _, m := range miles { - m.RenderedContent = string(base.RenderMarkdown([]byte(m.Content), ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())) + m.RenderedContent = string(markdown.Render([]byte(m.Content), ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())) m.CalOpenIssues() } ctx.Data["Milestones"] = miles diff --git a/routers/repo/pull.go b/routers/repo/pull.go index 3240461c84..cf8c4829b4 100644 --- a/routers/repo/pull.go +++ b/routers/repo/pull.go @@ -26,6 +26,16 @@ const ( COMPARE_PULL base.TplName = "repo/pulls/compare" PULL_COMMITS base.TplName = "repo/pulls/commits" PULL_FILES base.TplName = "repo/pulls/files" + + PULL_REQUEST_TEMPLATE_KEY = "PullRequestTemplate" +) + +var ( + PullRequestTemplateCandidates = []string{ + "PULL_REQUEST.md", + ".gogs/PULL_REQUEST.md", + ".github/PULL_REQUEST.md", + } ) func getForkRepository(ctx *middleware.Context) *models.Repository { @@ -540,6 +550,7 @@ func CompareAndPullRequest(ctx *middleware.Context) { ctx.Data["PageIsComparePull"] = true ctx.Data["IsDiffCompare"] = true ctx.Data["RequireHighlightJS"] = true + setTemplateIfExists(ctx, PULL_REQUEST_TEMPLATE_KEY, PullRequestTemplateCandidates) renderAttachmentSettings(ctx) headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch := ParseCompareInfo(ctx) diff --git a/routers/repo/release.go b/routers/repo/release.go index 8baa317e0c..92db5e806a 100644 --- a/routers/repo/release.go +++ b/routers/repo/release.go @@ -9,6 +9,7 @@ import ( "github.com/gogits/gogs/modules/auth" "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/markdown" "github.com/gogits/gogs/modules/middleware" ) @@ -73,7 +74,7 @@ func Releases(ctx *middleware.Context) { rel.NumCommitsBehind = ctx.Repo.CommitsCount - rel.NumCommits } - rel.Note = base.RenderMarkdownString(rel.Note, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()) + rel.Note = markdown.RenderString(rel.Note, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()) tags[i] = rel rels[j] = nil // Mark as used. break @@ -137,7 +138,7 @@ func Releases(ctx *middleware.Context) { rel.NumCommitsBehind = ctx.Repo.CommitsCount - rel.NumCommits } - rel.Note = base.RenderMarkdownString(rel.Note, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()) + rel.Note = markdown.RenderString(rel.Note, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas()) tags = append(tags, rel) } models.SortReleases(tags) diff --git a/routers/repo/setting.go b/routers/repo/setting.go index ac3f534bbc..5f268f3bfb 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -233,48 +233,6 @@ func Collaboration(ctx *middleware.Context) { ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsCollaboration"] = true - if ctx.Req.Method == "POST" { - name := strings.ToLower(ctx.Query("collaborator")) - if len(name) == 0 || ctx.Repo.Owner.LowerName == name { - ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path) - return - } - - u, err := models.GetUserByName(name) - if err != nil { - if models.IsErrUserNotExist(err) { - ctx.Flash.Error(ctx.Tr("form.user_not_exist")) - ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path) - } else { - ctx.Handle(500, "GetUserByName", err) - } - return - } - - // Check if user is organization member. - if ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOrgMember(u.Id) { - ctx.Flash.Info(ctx.Tr("repo.settings.user_is_org_member")) - ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") - return - } - - if err = ctx.Repo.Repository.AddCollaborator(u); err != nil { - ctx.Handle(500, "AddCollaborator", err) - return - } - - if setting.Service.EnableNotifyMail { - if err = mailer.SendCollaboratorMail(ctx.Render, u, ctx.User, ctx.Repo.Repository); err != nil { - ctx.Handle(500, "SendCollaboratorMail", err) - return - } - } - - ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success")) - ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path) - return - } - // Delete collaborator. remove := strings.ToLower(ctx.Query("remove")) if len(remove) > 0 && remove != ctx.Repo.Owner.LowerName { @@ -302,6 +260,54 @@ func Collaboration(ctx *middleware.Context) { ctx.HTML(200, COLLABORATION) } +func CollaborationPost(ctx *middleware.Context) { + name := strings.ToLower(ctx.Query("collaborator")) + if len(name) == 0 || ctx.Repo.Owner.LowerName == name { + ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path) + return + } + + u, err := models.GetUserByName(name) + if err != nil { + if models.IsErrUserNotExist(err) { + ctx.Flash.Error(ctx.Tr("form.user_not_exist")) + ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path) + } else { + ctx.Handle(500, "GetUserByName", err) + } + return + } + + // Organization is not allowed to be added as a collaborator. + if u.IsOrganization() { + ctx.Flash.Error(ctx.Tr("repo.settings.org_not_allowed_to_be_collaborator")) + ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path) + return + } + + // Check if user is organization member. + if ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOrgMember(u.Id) { + ctx.Flash.Info(ctx.Tr("repo.settings.user_is_org_member")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") + return + } + + if err = ctx.Repo.Repository.AddCollaborator(u); err != nil { + ctx.Handle(500, "AddCollaborator", err) + return + } + + if setting.Service.EnableNotifyMail { + if err = mailer.SendCollaboratorMail(ctx.Render, u, ctx.User, ctx.Repo.Repository); err != nil { + ctx.Handle(500, "SendCollaboratorMail", err) + return + } + } + + ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success")) + ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path) +} + func parseOwnerAndRepo(ctx *middleware.Context) (*models.User, *models.Repository) { owner, err := models.GetUserByName(ctx.Params(":username")) if err != nil { diff --git a/routers/repo/view.go b/routers/repo/view.go index 6a4ad64642..efff31a9a0 100644 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -17,6 +17,7 @@ import ( "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/markdown" "github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/modules/template" "github.com/gogits/gogs/modules/template/highlight" @@ -99,10 +100,10 @@ func Home(ctx *middleware.Context) { case isTextFile: d, _ := ioutil.ReadAll(dataRc) buf = append(buf, d...) - readmeExist := base.IsMarkdownFile(blob.Name()) || base.IsReadmeFile(blob.Name()) + readmeExist := markdown.IsMarkdownFile(blob.Name()) || markdown.IsReadmeFile(blob.Name()) ctx.Data["ReadmeExist"] = readmeExist if readmeExist { - ctx.Data["FileContent"] = string(base.RenderMarkdown(buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas())) + ctx.Data["FileContent"] = string(markdown.Render(buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas())) } else { if err, content := template.ToUtf8WithErr(buf); err != nil { if err != nil { @@ -138,7 +139,7 @@ func Home(ctx *middleware.Context) { var readmeFile *git.Blob for _, f := range entries { - if f.IsDir() || !base.IsReadmeFile(f.Name()) { + if f.IsDir() || !markdown.IsReadmeFile(f.Name()) { continue } else { readmeFile = f.Blob() @@ -169,8 +170,8 @@ func Home(ctx *middleware.Context) { d, _ := ioutil.ReadAll(dataRc) buf = append(buf, d...) switch { - case base.IsMarkdownFile(readmeFile.Name()): - buf = base.RenderMarkdown(buf, treeLink, ctx.Repo.Repository.ComposeMetas()) + case markdown.IsMarkdownFile(readmeFile.Name()): + buf = markdown.Render(buf, treeLink, ctx.Repo.Repository.ComposeMetas()) default: buf = bytes.Replace(buf, []byte("\n"), []byte(`<br>`), -1) } diff --git a/routers/repo/wiki.go b/routers/repo/wiki.go index d2931dc1d5..8032d1a591 100644 --- a/routers/repo/wiki.go +++ b/routers/repo/wiki.go @@ -14,6 +14,7 @@ import ( "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/auth" "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/markdown" "github.com/gogits/gogs/modules/middleware" ) @@ -106,7 +107,7 @@ func renderWikiPage(ctx *middleware.Context, isViewPage bool) (*git.Repository, return nil, "" } if isViewPage { - ctx.Data["content"] = string(base.RenderMarkdown(data, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())) + ctx.Data["content"] = string(markdown.Render(data, ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())) } else { ctx.Data["content"] = string(data) } diff --git a/templates/.VERSION b/templates/.VERSION index 356e27b108..fc51561487 100644 --- a/templates/.VERSION +++ b/templates/.VERSION @@ -1 +1 @@ -0.8.40.0219
\ No newline at end of file +0.8.41.0221
\ No newline at end of file diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 426d37e40a..9bda877980 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -79,6 +79,14 @@ <label for="attribute_mail">{{.i18n.Tr "admin.auths.attribute_mail"}}</label> <input id="attribute_mail" name="attribute_mail" value="{{$cfg.AttributeMail}}" placeholder="e.g. mail" required> </div> + {{if .Source.IsLDAP}} + <div class="inline field"> + <div class="ui checkbox"> + <label><strong>{{.i18n.Tr "admin.auths.attributes_in_bind"}}</strong></label> + <input name="attributes_in_bind" type="checkbox" {{if $cfg.AttributesInBind}}checked{{end}}> + </div> + </div> + {{end}} {{end}} <!-- SMTP --> diff --git a/templates/admin/auth/new.tmpl b/templates/admin/auth/new.tmpl index f06d3de7e3..418f21dad8 100644 --- a/templates/admin/auth/new.tmpl +++ b/templates/admin/auth/new.tmpl @@ -120,6 +120,12 @@ <input id="pam_service_name" name="pam_service_name" value="{{.pam_service_name}}" /> </div> + <div class="ldap field"> + <div class="ui checkbox"> + <label><strong>{{.i18n.Tr "admin.auths.attributes_in_bind"}}</strong></label> + <input name="attributes_in_bind" type="checkbox" {{if .attributes_in_bind}}checked{{end}}> + </div> + </div> <div class="ldap dldap smtp inline field {{if not (or (or (eq .type 2) (eq .type 5)) (eq .type 3))}}hide{{end}}"> <div class="ui checkbox"> <label><strong>{{.i18n.Tr "admin.auths.enable_tls"}}</strong></label> diff --git a/templates/repo/diff_box.tmpl b/templates/repo/diff_box.tmpl index f96ebda52d..455f07002c 100644 --- a/templates/repo/diff_box.tmpl +++ b/templates/repo/diff_box.tmpl @@ -34,6 +34,7 @@ </div> {{range $i, $file := .Diff.Files}} + {{$highlightClass := $file.GetHighlightClass}} <div class="diff-file-box diff-box file-content" id="diff-{{.Index}}"> <h4 class="ui top attached normal header"> <div class="diff-counter count ui left"> @@ -76,13 +77,13 @@ <span rel="{{if $line.LeftIdx}}diff-{{Sha1 $file.Name}}L{{$line.LeftIdx}}{{end}}">{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}</span> </td> <td class="lines-code halfwidth"> - <pre><code class="wrap {{if $file.GetHighlightClass}}language-{{$file.GetHighlightClass}}{{else}}nohighlight{{end}}">{{if $line.LeftIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</code></pre> + <pre><code class="wrap {{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{if $line.LeftIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</code></pre> </td> <td class="lines-num lines-num-new"> <span rel="{{if $line.RightIdx}}diff-{{Sha1 $file.Name}}R{{$line.RightIdx}}{{end}}">{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}</span> </td> <td class="lines-code halfwidth"> - <pre><code class="wrap {{if $file.GetHighlightClass}}language-{{$file.GetHighlightClass}}{{else}}nohighlight{{end}}">{{if $line.RightIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</code></pre> + <pre><code class="wrap {{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{if $line.RightIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</code></pre> </td> </tr> {{end}} @@ -104,7 +105,7 @@ </td> {{end}} <td class="lines-code"> - <pre><code class="{{if $file.GetHighlightClass}}language-{{$file.GetHighlightClass}}{{else}}nohighlight{{end}}">{{$section.GetComputedInlineDiffFor $line}}</code></pre> + <pre><code class="{{if $highlightClass}}language-{{$highlightClass}}{{else}}nohighlight{{end}}">{{$section.GetComputedInlineDiffFor $line}}</code></pre> </td> </tr> {{end}} diff --git a/templates/repo/issue/comment_tab.tmpl b/templates/repo/issue/comment_tab.tmpl index 8f6c8ca778..851c3b1fae 100644 --- a/templates/repo/issue/comment_tab.tmpl +++ b/templates/repo/issue/comment_tab.tmpl @@ -4,7 +4,7 @@ <a class="item" data-tab="preview" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}">{{.i18n.Tr "repo.release.preview"}}</a> </div> <div class="ui bottom attached active tab segment" data-tab="write"> - <textarea id="content" name="content" tabindex="4"></textarea> + <textarea id="content" name="content" tabindex="4">{{if .IssueTemplate}}{{.IssueTemplate}}{{end}}{{if .PullRequestTemplate}}{{.PullRequestTemplate}}{{end}}</textarea> </div> <div class="ui bottom attached tab segment markdown" data-tab="preview"> {{.i18n.Tr "repo.release.loading"}} diff --git a/templates/repo/settings/collaboration.tmpl b/templates/repo/settings/collaboration.tmpl index 57b76fd8bf..c06fc2d4ff 100644 --- a/templates/repo/settings/collaboration.tmpl +++ b/templates/repo/settings/collaboration.tmpl @@ -28,7 +28,7 @@ <div class="inline field ui left"> <div id="search-user-box"> <div class="ui input"> - <input class="prompt" name="collaborator" placeholder="{{.i18n.Tr "repo.settings.search_user_placeholder"}}" autocomplete="off" required> + <input class="prompt" name="collaborator" placeholder="{{.i18n.Tr "repo.settings.search_user_placeholder"}}" autocomplete="off" autofocus required> </div> <div class="ui segment results hide"></div> </div> |