diff --git a/Gopkg.lock b/Gopkg.lock
new file mode 100644
index 0000000..a973a42
--- /dev/null
+++ b/Gopkg.lock
@@ -0,0 +1,61 @@
+# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
+
+
+[[projects]]
+ name = "github.com/abbot/go-http-auth"
+ packages = ["."]
+ revision = "0ddd408d5d60ea76e320503cc7dd091992dee608"
+ version = "v0.4.0"
+
+[[projects]]
+ name = "github.com/beevik/etree"
+ packages = ["."]
+ revision = "15a30b44cfd6c5a16a7ddfe271bf146aaf2d3195"
+ version = "v1.0.0"
+
+[[projects]]
+ name = "github.com/laurent22/ical-go"
+ packages = ["ical"]
+ revision = "4811ac5553eae5fed7cd5d7a9024727f1311b2a2"
+ version = "v0.1.0"
+
+[[projects]]
+ name = "github.com/samedi/caldav-go"
+ packages = [
+ ".",
+ "data",
+ "errs",
+ "files",
+ "global",
+ "handlers",
+ "ixml",
+ "lib"
+ ]
+ revision = "4d45d84038c6026578b403ceff7d3081dcd84efd"
+ version = "v3.0.0"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/crypto"
+ packages = [
+ "bcrypt",
+ "blowfish"
+ ]
+ revision = "12892e8c234f4fe6f6803f052061de9057903bb2"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/net"
+ packages = [
+ "context",
+ "webdav",
+ "webdav/internal/xml"
+ ]
+ revision = "b68f30494add4df6bd8ef5e82803f308e7f7c59c"
+
+[solve-meta]
+ analyzer-name = "dep"
+ analyzer-version = 1
+ inputs-digest = "65e0b2109be1138b841139e4bf266a60b62dc0e84d2ab6dc9785ea94b766074c"
+ solver-name = "gps-cdcl"
+ solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
new file mode 100644
index 0000000..c10ef93
--- /dev/null
+++ b/Gopkg.toml
@@ -0,0 +1,42 @@
+# Gopkg.toml example
+#
+# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
+# for detailed Gopkg.toml documentation.
+#
+# required = ["github.com/user/thing/cmd/thing"]
+# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
+#
+# [[constraint]]
+# name = "github.com/user/project"
+# version = "1.0.0"
+#
+# [[constraint]]
+# name = "github.com/user/project2"
+# branch = "dev"
+# source = "github.com/myfork/project2"
+#
+# [[override]]
+# name = "github.com/x/y"
+# version = "2.4.0"
+#
+# [prune]
+# non-go = false
+# go-tests = true
+# unused-packages = true
+
+
+[[constraint]]
+ name = "github.com/abbot/go-http-auth"
+ version = "0.4.0"
+
+[[constraint]]
+ name = "github.com/samedi/caldav-go"
+ version = "3.0.0"
+
+[[constraint]]
+ branch = "master"
+ name = "golang.org/x/net"
+
+[prune]
+ go-tests = true
+ unused-packages = true
diff --git a/main.go b/main.go
index 2693965..30b6fef 100644
--- a/main.go
+++ b/main.go
@@ -1,6 +1,7 @@
package main
import (
+ "context"
"flag"
"fmt"
"log"
@@ -11,6 +12,7 @@ import (
"time"
"github.com/abbot/go-http-auth"
+ caldav "github.com/samedi/caldav-go"
"golang.org/x/net/webdav"
)
@@ -19,6 +21,7 @@ var (
flCert = flag.String("cert", "", "server SSL cert (both -cert and -key must be present to use SSL). See `go run $(go env GOROOT)/src/crypto/tls/generate_cert.go -h` to generate development cert/key")
flKey = flag.String("key", "", "server SSL key")
flHtpasswd = flag.String("htpasswd", "", "htpasswd file for auth (must be present to use auth) See htpasswd(1) to create this file.")
+ flCalDav = flag.String("caldav", "", "local path to store caldav data ('' means no caldav is served)")
)
func main() {
@@ -60,10 +63,19 @@ func main() {
authHandlerFunc := func(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
h.ServeHTTP(w, &r.Request)
}
+ if *flCalDav != "" {
+ http.HandleFunc("/caldav", authenticator.Wrap(func(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
+ caldav.RequestHandler(w, &r.Request)
+ }))
+ }
http.HandleFunc("/", authenticator.Wrap(authHandlerFunc))
} else {
log.Println("WARNING: connections are not authenticated. STRONGLY consider using -htpasswd.")
+ if *flCalDav != "" {
+ http.HandleFunc("/caldav", caldav.RequestHandler)
+ }
+
http.Handle("/", h)
}
addr := fmt.Sprintf(":%d", *flPort)
@@ -85,23 +97,23 @@ type passThroughFS struct {
root string
}
-func (ptfs *passThroughFS) Mkdir(name string, perm os.FileMode) error {
+func (ptfs *passThroughFS) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
// TODO(vbatts) check for escaping the root directory
return os.Mkdir(filepath.Join(ptfs.root, name), perm)
}
-func (ptfs *passThroughFS) OpenFile(name string, flag int, perm os.FileMode) (webdav.File, error) {
+func (ptfs *passThroughFS) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
// TODO(vbatts) check for escaping the root directory
return os.OpenFile(filepath.Join(ptfs.root, name), flag, perm)
}
-func (ptfs *passThroughFS) RemoveAll(name string) error {
+func (ptfs *passThroughFS) RemoveAll(ctx context.Context, name string) error {
// TODO(vbatts) check for escaping the root directory
return os.RemoveAll(filepath.Join(ptfs.root, name))
}
-func (ptfs *passThroughFS) Rename(oldName, newName string) error {
+func (ptfs *passThroughFS) Rename(ctx context.Context, oldName, newName string) error {
// TODO(vbatts) check for escaping the root directory
return os.Rename(filepath.Join(ptfs.root, oldName), filepath.Join(ptfs.root, newName))
}
-func (ptfs *passThroughFS) Stat(name string) (os.FileInfo, error) {
+func (ptfs *passThroughFS) Stat(ctx context.Context, name string) (os.FileInfo, error) {
// TODO(vbatts) check for escaping the root directory
return os.Stat(filepath.Join(ptfs.root, name))
}
diff --git a/vendor/github.com/abbot/go-http-auth/.gitignore b/vendor/github.com/abbot/go-http-auth/.gitignore
new file mode 100644
index 0000000..112ea39
--- /dev/null
+++ b/vendor/github.com/abbot/go-http-auth/.gitignore
@@ -0,0 +1,5 @@
+*~
+*.a
+*.6
+*.out
+_testmain.go
diff --git a/vendor/github.com/abbot/go-http-auth/LICENSE b/vendor/github.com/abbot/go-http-auth/LICENSE
new file mode 100644
index 0000000..e454a52
--- /dev/null
+++ b/vendor/github.com/abbot/go-http-auth/LICENSE
@@ -0,0 +1,178 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/vendor/github.com/abbot/go-http-auth/Makefile b/vendor/github.com/abbot/go-http-auth/Makefile
new file mode 100644
index 0000000..25f208d
--- /dev/null
+++ b/vendor/github.com/abbot/go-http-auth/Makefile
@@ -0,0 +1,12 @@
+include $(GOROOT)/src/Make.inc
+
+TARG=auth_digest
+GOFILES=\
+ auth.go\
+ digest.go\
+ basic.go\
+ misc.go\
+ md5crypt.go\
+ users.go\
+
+include $(GOROOT)/src/Make.pkg
diff --git a/vendor/github.com/abbot/go-http-auth/README.md b/vendor/github.com/abbot/go-http-auth/README.md
new file mode 100644
index 0000000..73ae985
--- /dev/null
+++ b/vendor/github.com/abbot/go-http-auth/README.md
@@ -0,0 +1,71 @@
+HTTP Authentication implementation in Go
+========================================
+
+This is an implementation of HTTP Basic and HTTP Digest authentication
+in Go language. It is designed as a simple wrapper for
+http.RequestHandler functions.
+
+Features
+--------
+
+ * Supports HTTP Basic and HTTP Digest authentication.
+ * Supports htpasswd and htdigest formatted files.
+ * Automatic reloading of password files.
+ * Pluggable interface for user/password storage.
+ * Supports MD5, SHA1 and BCrypt for Basic authentication password storage.
+ * Configurable Digest nonce cache size with expiration.
+ * Wrapper for legacy http handlers (http.HandlerFunc interface)
+
+Example usage
+-------------
+
+This is a complete working example for Basic auth:
+
+ package main
+
+ import (
+ "fmt"
+ "net/http"
+
+ auth "github.com/abbot/go-http-auth"
+ )
+
+ func Secret(user, realm string) string {
+ if user == "john" {
+ // password is "hello"
+ return "$1$dlPL2MqE$oQmn16q49SqdmhenQuNgs1"
+ }
+ return ""
+ }
+
+ func handle(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
+ fmt.Fprintf(w, "
Hello, %s!
", r.Username)
+ }
+
+ func main() {
+ authenticator := auth.NewBasicAuthenticator("example.com", Secret)
+ http.HandleFunc("/", authenticator.Wrap(handle))
+ http.ListenAndServe(":8080", nil)
+ }
+
+See more examples in the "examples" directory.
+
+Legal
+-----
+
+This module is developed under Apache 2.0 license, and can be used for
+open and proprietary projects.
+
+Copyright 2012-2013 Lev Shamardin
+
+Licensed under the Apache License, Version 2.0 (the "License"); you
+may not use this file or any other part of this project except in
+compliance with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+implied. See the License for the specific language governing
+permissions and limitations under the License.
diff --git a/vendor/github.com/abbot/go-http-auth/auth.go b/vendor/github.com/abbot/go-http-auth/auth.go
new file mode 100644
index 0000000..05ded16
--- /dev/null
+++ b/vendor/github.com/abbot/go-http-auth/auth.go
@@ -0,0 +1,109 @@
+// Package auth is an implementation of HTTP Basic and HTTP Digest authentication.
+package auth
+
+import (
+ "net/http"
+
+ "golang.org/x/net/context"
+)
+
+/*
+ Request handlers must take AuthenticatedRequest instead of http.Request
+*/
+type AuthenticatedRequest struct {
+ http.Request
+ /*
+ Authenticated user name. Current API implies that Username is
+ never empty, which means that authentication is always done
+ before calling the request handler.
+ */
+ Username string
+}
+
+/*
+ AuthenticatedHandlerFunc is like http.HandlerFunc, but takes
+ AuthenticatedRequest instead of http.Request
+*/
+type AuthenticatedHandlerFunc func(http.ResponseWriter, *AuthenticatedRequest)
+
+/*
+ Authenticator wraps an AuthenticatedHandlerFunc with
+ authentication-checking code.
+
+ Typical Authenticator usage is something like:
+
+ authenticator := SomeAuthenticator(...)
+ http.HandleFunc("/", authenticator(my_handler))
+
+ Authenticator wrapper checks the user authentication and calls the
+ wrapped function only after authentication has succeeded. Otherwise,
+ it returns a handler which initiates the authentication procedure.
+*/
+type Authenticator func(AuthenticatedHandlerFunc) http.HandlerFunc
+
+// Info contains authentication information for the request.
+type Info struct {
+ // Authenticated is set to true when request was authenticated
+ // successfully, i.e. username and password passed in request did
+ // pass the check.
+ Authenticated bool
+
+ // Username contains a user name passed in the request when
+ // Authenticated is true. It's value is undefined if Authenticated
+ // is false.
+ Username string
+
+ // ResponseHeaders contains extra headers that must be set by server
+ // when sending back HTTP response.
+ ResponseHeaders http.Header
+}
+
+// UpdateHeaders updates headers with this Info's ResponseHeaders. It is
+// safe to call this function on nil Info.
+func (i *Info) UpdateHeaders(headers http.Header) {
+ if i == nil {
+ return
+ }
+ for k, values := range i.ResponseHeaders {
+ for _, v := range values {
+ headers.Add(k, v)
+ }
+ }
+}
+
+type key int // used for context keys
+
+var infoKey key = 0
+
+type AuthenticatorInterface interface {
+ // NewContext returns a new context carrying authentication
+ // information extracted from the request.
+ NewContext(ctx context.Context, r *http.Request) context.Context
+
+ // Wrap returns an http.HandlerFunc which wraps
+ // AuthenticatedHandlerFunc with this authenticator's
+ // authentication checks.
+ Wrap(AuthenticatedHandlerFunc) http.HandlerFunc
+}
+
+// FromContext returns authentication information from the context or
+// nil if no such information present.
+func FromContext(ctx context.Context) *Info {
+ info, ok := ctx.Value(infoKey).(*Info)
+ if !ok {
+ return nil
+ }
+ return info
+}
+
+// AuthUsernameHeader is the header set by JustCheck functions. It
+// contains an authenticated username (if authentication was
+// successful).
+const AuthUsernameHeader = "X-Authenticated-Username"
+
+func JustCheck(auth AuthenticatorInterface, wrapped http.HandlerFunc) http.HandlerFunc {
+ return auth.Wrap(func(w http.ResponseWriter, ar *AuthenticatedRequest) {
+ ar.Header.Set(AuthUsernameHeader, ar.Username)
+ wrapped(w, &ar.Request)
+ })
+}
diff --git a/vendor/github.com/abbot/go-http-auth/basic.go b/vendor/github.com/abbot/go-http-auth/basic.go
new file mode 100644
index 0000000..b03dd58
--- /dev/null
+++ b/vendor/github.com/abbot/go-http-auth/basic.go
@@ -0,0 +1,163 @@
+package auth
+
+import (
+ "bytes"
+ "crypto/sha1"
+ "crypto/subtle"
+ "encoding/base64"
+ "errors"
+ "net/http"
+ "strings"
+
+ "golang.org/x/crypto/bcrypt"
+ "golang.org/x/net/context"
+)
+
+type compareFunc func(hashedPassword, password []byte) error
+
+var (
+ errMismatchedHashAndPassword = errors.New("mismatched hash and password")
+
+ compareFuncs = []struct {
+ prefix string
+ compare compareFunc
+ }{
+ {"", compareMD5HashAndPassword}, // default compareFunc
+ {"{SHA}", compareShaHashAndPassword},
+ // Bcrypt is complicated. According to crypt(3) from
+ // crypt_blowfish version 1.3 (fetched from
+ // http://www.openwall.com/crypt/crypt_blowfish-1.3.tar.gz), there
+ // are three different has prefixes: "$2a$", used by versions up
+ // to 1.0.4, and "$2x$" and "$2y$", used in all later
+ // versions. "$2a$" has a known bug, "$2x$" was added as a
+ // migration path for systems with "$2a$" prefix and still has a
+ // bug, and only "$2y$" should be used by modern systems. The bug
+ // has something to do with handling of 8-bit characters. Since
+ // both "$2a$" and "$2x$" are deprecated, we are handling them the
+ // same way as "$2y$", which will yield correct results for 7-bit
+ // character passwords, but is wrong for 8-bit character
+ // passwords. You have to upgrade to "$2y$" if you want sant 8-bit
+ // character password support with bcrypt. To add to the mess,
+ // OpenBSD 5.5. introduced "$2b$" prefix, which behaves exactly
+ // like "$2y$" according to the same source.
+ {"$2a$", bcrypt.CompareHashAndPassword},
+ {"$2b$", bcrypt.CompareHashAndPassword},
+ {"$2x$", bcrypt.CompareHashAndPassword},
+ {"$2y$", bcrypt.CompareHashAndPassword},
+ }
+)
+
+type BasicAuth struct {
+ Realm string
+ Secrets SecretProvider
+ // Headers used by authenticator. Set to ProxyHeaders to use with
+ // proxy server. When nil, NormalHeaders are used.
+ Headers *Headers
+}
+
+// check that BasicAuth implements AuthenticatorInterface
+var _ = (AuthenticatorInterface)((*BasicAuth)(nil))
+
+/*
+ Checks the username/password combination from the request. Returns
+ either an empty string (authentication failed) or the name of the
+ authenticated user.
+
+ Supports MD5 and SHA1 password entries
+*/
+func (a *BasicAuth) CheckAuth(r *http.Request) string {
+ s := strings.SplitN(r.Header.Get(a.Headers.V().Authorization), " ", 2)
+ if len(s) != 2 || s[0] != "Basic" {
+ return ""
+ }
+
+ b, err := base64.StdEncoding.DecodeString(s[1])
+ if err != nil {
+ return ""
+ }
+ pair := strings.SplitN(string(b), ":", 2)
+ if len(pair) != 2 {
+ return ""
+ }
+ user, password := pair[0], pair[1]
+ secret := a.Secrets(user, a.Realm)
+ if secret == "" {
+ return ""
+ }
+ compare := compareFuncs[0].compare
+ for _, cmp := range compareFuncs[1:] {
+ if strings.HasPrefix(secret, cmp.prefix) {
+ compare = cmp.compare
+ break
+ }
+ }
+ if compare([]byte(secret), []byte(password)) != nil {
+ return ""
+ }
+ return pair[0]
+}
+
+func compareShaHashAndPassword(hashedPassword, password []byte) error {
+ d := sha1.New()
+ d.Write(password)
+ if subtle.ConstantTimeCompare(hashedPassword[5:], []byte(base64.StdEncoding.EncodeToString(d.Sum(nil)))) != 1 {
+ return errMismatchedHashAndPassword
+ }
+ return nil
+}
+
+func compareMD5HashAndPassword(hashedPassword, password []byte) error {
+ parts := bytes.SplitN(hashedPassword, []byte("$"), 4)
+ if len(parts) != 4 {
+ return errMismatchedHashAndPassword
+ }
+ magic := []byte("$" + string(parts[1]) + "$")
+ salt := parts[2]
+ if subtle.ConstantTimeCompare(hashedPassword, MD5Crypt(password, salt, magic)) != 1 {
+ return errMismatchedHashAndPassword
+ }
+ return nil
+}
+
+/*
+ http.Handler for BasicAuth which initiates the authentication process
+ (or requires reauthentication).
+*/
+func (a *BasicAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set(contentType, a.Headers.V().UnauthContentType)
+ w.Header().Set(a.Headers.V().Authenticate, `Basic realm="`+a.Realm+`"`)
+ w.WriteHeader(a.Headers.V().UnauthCode)
+ w.Write([]byte(a.Headers.V().UnauthResponse))
+}
+
+/*
+ BasicAuthenticator returns a function, which wraps an
+ AuthenticatedHandlerFunc converting it to http.HandlerFunc. This
+ wrapper function checks the authentication and either sends back
+ required authentication headers, or calls the wrapped function with
+ authenticated username in the AuthenticatedRequest.
+*/
+func (a *BasicAuth) Wrap(wrapped AuthenticatedHandlerFunc) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ if username := a.CheckAuth(r); username == "" {
+ a.RequireAuth(w, r)
+ } else {
+ ar := &AuthenticatedRequest{Request: *r, Username: username}
+ wrapped(w, ar)
+ }
+ }
+}
+
+// NewContext returns a context carrying authentication information for the request.
+func (a *BasicAuth) NewContext(ctx context.Context, r *http.Request) context.Context {
+ info := &Info{Username: a.CheckAuth(r), ResponseHeaders: make(http.Header)}
+ info.Authenticated = (info.Username != "")
+ if !info.Authenticated {
+ info.ResponseHeaders.Set(a.Headers.V().Authenticate, `Basic realm="`+a.Realm+`"`)
+ }
+ return context.WithValue(ctx, infoKey, info)
+}
+
+func NewBasicAuthenticator(realm string, secrets SecretProvider) *BasicAuth {
+ return &BasicAuth{Realm: realm, Secrets: secrets}
+}
diff --git a/vendor/github.com/abbot/go-http-auth/digest.go b/vendor/github.com/abbot/go-http-auth/digest.go
new file mode 100644
index 0000000..21b0933
--- /dev/null
+++ b/vendor/github.com/abbot/go-http-auth/digest.go
@@ -0,0 +1,274 @@
+package auth
+
+import (
+ "crypto/subtle"
+ "fmt"
+ "net/http"
+ "net/url"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "golang.org/x/net/context"
+)
+
+type digest_client struct {
+ nc uint64
+ last_seen int64
+}
+
+type DigestAuth struct {
+ Realm string
+ Opaque string
+ Secrets SecretProvider
+ PlainTextSecrets bool
+ IgnoreNonceCount bool
+ // Headers used by authenticator. Set to ProxyHeaders to use with
+ // proxy server. When nil, NormalHeaders are used.
+ Headers *Headers
+
+ /*
+ Approximate size of Client's Cache. When actual number of
+ tracked client nonces exceeds
+ ClientCacheSize+ClientCacheTolerance, ClientCacheTolerance*2
+ older entries are purged.
+ */
+ ClientCacheSize int
+ ClientCacheTolerance int
+
+ clients map[string]*digest_client
+ mutex sync.Mutex
+}
+
+// check that DigestAuth implements AuthenticatorInterface
+var _ = (AuthenticatorInterface)((*DigestAuth)(nil))
+
+type digest_cache_entry struct {
+ nonce string
+ last_seen int64
+}
+
+type digest_cache []digest_cache_entry
+
+func (c digest_cache) Less(i, j int) bool {
+ return c[i].last_seen < c[j].last_seen
+}
+
+func (c digest_cache) Len() int {
+ return len(c)
+}
+
+func (c digest_cache) Swap(i, j int) {
+ c[i], c[j] = c[j], c[i]
+}
+
+/*
+ Remove count oldest entries from DigestAuth.clients
+*/
+func (a *DigestAuth) Purge(count int) {
+ entries := make([]digest_cache_entry, 0, len(a.clients))
+ for nonce, client := range a.clients {
+ entries = append(entries, digest_cache_entry{nonce, client.last_seen})
+ }
+ cache := digest_cache(entries)
+ sort.Sort(cache)
+ for _, client := range cache[:count] {
+ delete(a.clients, client.nonce)
+ }
+}
+
+/*
+ http.Handler for DigestAuth which initiates the authentication process
+ (or requires reauthentication).
+*/
+func (a *DigestAuth) RequireAuth(w http.ResponseWriter, r *http.Request) {
+ if len(a.clients) > a.ClientCacheSize+a.ClientCacheTolerance {
+ a.Purge(a.ClientCacheTolerance * 2)
+ }
+ nonce := RandomKey()
+ a.clients[nonce] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()}
+ w.Header().Set(contentType, a.Headers.V().UnauthContentType)
+ w.Header().Set(a.Headers.V().Authenticate,
+ fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm="MD5", qop="auth"`,
+ a.Realm, nonce, a.Opaque))
+ w.WriteHeader(a.Headers.V().UnauthCode)
+ w.Write([]byte(a.Headers.V().UnauthResponse))
+}
+
+/*
+ Parse Authorization header from the http.Request. Returns a map of
+ auth parameters or nil if the header is not a valid parsable Digest
+ auth header.
+*/
+func DigestAuthParams(authorization string) map[string]string {
+ s := strings.SplitN(authorization, " ", 2)
+ if len(s) != 2 || s[0] != "Digest" {
+ return nil
+ }
+
+ return ParsePairs(s[1])
+}
+
+/*
+ Check if request contains valid authentication data. Returns a pair
+ of username, authinfo where username is the name of the authenticated
+ user or an empty string and authinfo is the contents for the optional
+ Authentication-Info response header.
+*/
+func (da *DigestAuth) CheckAuth(r *http.Request) (username string, authinfo *string) {
+ da.mutex.Lock()
+ defer da.mutex.Unlock()
+ username = ""
+ authinfo = nil
+ auth := DigestAuthParams(r.Header.Get(da.Headers.V().Authorization))
+ if auth == nil {
+ return "", nil
+ }
+ // RFC2617 Section 3.2.1 specifies that unset value of algorithm in
+ // WWW-Authenticate Response header should be treated as
+ // "MD5". According to section 3.2.2 the "algorithm" value in
+ // subsequent Request Authorization header must be set to whatever
+ // was supplied in the WWW-Authenticate Response header. This
+ // implementation always returns an algorithm in WWW-Authenticate
+ // header, however there seems to be broken clients in the wild
+ // which do not set the algorithm. Assume the unset algorithm in
+ // Authorization header to be equal to MD5.
+ if _, ok := auth["algorithm"]; !ok {
+ auth["algorithm"] = "MD5"
+ }
+ if da.Opaque != auth["opaque"] || auth["algorithm"] != "MD5" || auth["qop"] != "auth" {
+ return "", nil
+ }
+
+ // Check if the requested URI matches auth header
+ if r.RequestURI != auth["uri"] {
+ // We allow auth["uri"] to be a full path prefix of request-uri
+ // for some reason lost in history, which is probably wrong, but
+ // used to be like that for quite some time
+ // (https://tools.ietf.org/html/rfc2617#section-3.2.2 explicitly
+ // says that auth["uri"] is the request-uri).
+ //
+ // TODO: make an option to allow only strict checking.
+ switch u, err := url.Parse(auth["uri"]); {
+ case err != nil:
+ return "", nil
+ case r.URL == nil:
+ return "", nil
+ case len(u.Path) > len(r.URL.Path):
+ return "", nil
+ case !strings.HasPrefix(r.URL.Path, u.Path):
+ return "", nil
+ }
+ }
+
+ HA1 := da.Secrets(auth["username"], da.Realm)
+ if da.PlainTextSecrets {
+ HA1 = H(auth["username"] + ":" + da.Realm + ":" + HA1)
+ }
+ HA2 := H(r.Method + ":" + auth["uri"])
+ KD := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], HA2}, ":"))
+
+ if subtle.ConstantTimeCompare([]byte(KD), []byte(auth["response"])) != 1 {
+ return "", nil
+ }
+
+ // At this point crypto checks are completed and validated.
+ // Now check if the session is valid.
+
+ nc, err := strconv.ParseUint(auth["nc"], 16, 64)
+ if err != nil {
+ return "", nil
+ }
+
+ if client, ok := da.clients[auth["nonce"]]; !ok {
+ return "", nil
+ } else {
+ if client.nc != 0 && client.nc >= nc && !da.IgnoreNonceCount {
+ return "", nil
+ }
+ client.nc = nc
+ client.last_seen = time.Now().UnixNano()
+ }
+
+ resp_HA2 := H(":" + auth["uri"])
+ rspauth := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], resp_HA2}, ":"))
+
+ info := fmt.Sprintf(`qop="auth", rspauth="%s", cnonce="%s", nc="%s"`, rspauth, auth["cnonce"], auth["nc"])
+ return auth["username"], &info
+}
+
+/*
+ Default values for ClientCacheSize and ClientCacheTolerance for DigestAuth
+*/
+const DefaultClientCacheSize = 1000
+const DefaultClientCacheTolerance = 100
+
+/*
+ Wrap returns an Authenticator which uses HTTP Digest
+ authentication. Arguments:
+
+ realm: The authentication realm.
+
+ secrets: SecretProvider which must return HA1 digests for the same
+ realm as above.
+*/
+func (a *DigestAuth) Wrap(wrapped AuthenticatedHandlerFunc) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ if username, authinfo := a.CheckAuth(r); username == "" {
+ a.RequireAuth(w, r)
+ } else {
+ ar := &AuthenticatedRequest{Request: *r, Username: username}
+ if authinfo != nil {
+ w.Header().Set(a.Headers.V().AuthInfo, *authinfo)
+ }
+ wrapped(w, ar)
+ }
+ }
+}
+
+/*
+ JustCheck returns function which converts an http.HandlerFunc into a
+ http.HandlerFunc which requires authentication. Username is passed as
+ an extra X-Authenticated-Username header.
+*/
+func (a *DigestAuth) JustCheck(wrapped http.HandlerFunc) http.HandlerFunc {
+ return a.Wrap(func(w http.ResponseWriter, ar *AuthenticatedRequest) {
+ ar.Header.Set(AuthUsernameHeader, ar.Username)
+ wrapped(w, &ar.Request)
+ })
+}
+
+// NewContext returns a context carrying authentication information for the request.
+func (a *DigestAuth) NewContext(ctx context.Context, r *http.Request) context.Context {
+ username, authinfo := a.CheckAuth(r)
+ info := &Info{Username: username, ResponseHeaders: make(http.Header)}
+ if username != "" {
+ info.Authenticated = true
+ info.ResponseHeaders.Set(a.Headers.V().AuthInfo, *authinfo)
+ } else {
+ // return back digest WWW-Authenticate header
+ if len(a.clients) > a.ClientCacheSize+a.ClientCacheTolerance {
+ a.Purge(a.ClientCacheTolerance * 2)
+ }
+ nonce := RandomKey()
+ a.clients[nonce] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()}
+ info.ResponseHeaders.Set(a.Headers.V().Authenticate,
+ fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm="MD5", qop="auth"`,
+ a.Realm, nonce, a.Opaque))
+ }
+ return context.WithValue(ctx, infoKey, info)
+}
+
+func NewDigestAuthenticator(realm string, secrets SecretProvider) *DigestAuth {
+ da := &DigestAuth{
+ Opaque: RandomKey(),
+ Realm: realm,
+ Secrets: secrets,
+ PlainTextSecrets: false,
+ ClientCacheSize: DefaultClientCacheSize,
+ ClientCacheTolerance: DefaultClientCacheTolerance,
+ clients: map[string]*digest_client{}}
+ return da
+}
diff --git a/vendor/github.com/abbot/go-http-auth/md5crypt.go b/vendor/github.com/abbot/go-http-auth/md5crypt.go
new file mode 100644
index 0000000..a7a031c
--- /dev/null
+++ b/vendor/github.com/abbot/go-http-auth/md5crypt.go
@@ -0,0 +1,92 @@
+package auth
+
+import "crypto/md5"
+import "strings"
+
+const itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+
+var md5_crypt_swaps = [16]int{12, 6, 0, 13, 7, 1, 14, 8, 2, 15, 9, 3, 5, 10, 4, 11}
+
+type MD5Entry struct {
+ Magic, Salt, Hash []byte
+}
+
+func NewMD5Entry(e string) *MD5Entry {
+ parts := strings.SplitN(e, "$", 4)
+ if len(parts) != 4 {
+ return nil
+ }
+ return &MD5Entry{
+ Magic: []byte("$" + parts[1] + "$"),
+ Salt: []byte(parts[2]),
+ Hash: []byte(parts[3]),
+ }
+}
+
+/*
+ MD5 password crypt implementation
+*/
+func MD5Crypt(password, salt, magic []byte) []byte {
+ d := md5.New()
+
+ d.Write(password)
+ d.Write(magic)
+ d.Write(salt)
+
+ d2 := md5.New()
+ d2.Write(password)
+ d2.Write(salt)
+ d2.Write(password)
+
+ for i, mixin := 0, d2.Sum(nil); i < len(password); i++ {
+ d.Write([]byte{mixin[i%16]})
+ }
+
+ for i := len(password); i != 0; i >>= 1 {
+ if i&1 == 0 {
+ d.Write([]byte{password[0]})
+ } else {
+ d.Write([]byte{0})
+ }
+ }
+
+ final := d.Sum(nil)
+
+ for i := 0; i < 1000; i++ {
+ d2 := md5.New()
+ if i&1 == 0 {
+ d2.Write(final)
+ } else {
+ d2.Write(password)
+ }
+
+ if i%3 != 0 {
+ d2.Write(salt)
+ }
+
+ if i%7 != 0 {
+ d2.Write(password)
+ }
+
+ if i&1 == 0 {
+ d2.Write(password)
+ } else {
+ d2.Write(final)
+ }
+ final = d2.Sum(nil)
+ }
+
+ result := make([]byte, 0, 22)
+ v := uint(0)
+ bits := uint(0)
+ for _, i := range md5_crypt_swaps {
+ v |= (uint(final[i]) << bits)
+ for bits = bits + 8; bits > 6; bits -= 6 {
+ result = append(result, itoa64[v&0x3f])
+ v >>= 6
+ }
+ }
+ result = append(result, itoa64[v&0x3f])
+
+ return append(append(append(magic, salt...), '$'), result...)
+}
diff --git a/vendor/github.com/abbot/go-http-auth/misc.go b/vendor/github.com/abbot/go-http-auth/misc.go
new file mode 100644
index 0000000..4536ce6
--- /dev/null
+++ b/vendor/github.com/abbot/go-http-auth/misc.go
@@ -0,0 +1,141 @@
+package auth
+
+import (
+ "bytes"
+ "crypto/md5"
+ "crypto/rand"
+ "encoding/base64"
+ "fmt"
+ "net/http"
+ "strings"
+)
+
+// RandomKey returns a random 16-byte base64 alphabet string
+func RandomKey() string {
+ k := make([]byte, 12)
+ for bytes := 0; bytes < len(k); {
+ n, err := rand.Read(k[bytes:])
+ if err != nil {
+ panic("rand.Read() failed")
+ }
+ bytes += n
+ }
+ return base64.StdEncoding.EncodeToString(k)
+}
+
+// H function for MD5 algorithm (returns a lower-case hex MD5 digest)
+func H(data string) string {
+ digest := md5.New()
+ digest.Write([]byte(data))
+ return fmt.Sprintf("%x", digest.Sum(nil))
+}
+
+// ParseList parses a comma-separated list of values as described by
+// RFC 2068 and returns list elements.
+//
+// Lifted from https://code.google.com/p/gorilla/source/browse/http/parser/parser.go
+// which was ported from urllib2.parse_http_list, from the Python
+// standard library.
+func ParseList(value string) []string {
+ var list []string
+ var escape, quote bool
+ b := new(bytes.Buffer)
+ for _, r := range value {
+ switch {
+ case escape:
+ b.WriteRune(r)
+ escape = false
+ case quote:
+ if r == '\\' {
+ escape = true
+ } else {
+ if r == '"' {
+ quote = false
+ }
+ b.WriteRune(r)
+ }
+ case r == ',':
+ list = append(list, strings.TrimSpace(b.String()))
+ b.Reset()
+ case r == '"':
+ quote = true
+ b.WriteRune(r)
+ default:
+ b.WriteRune(r)
+ }
+ }
+ // Append last part.
+ if s := b.String(); s != "" {
+ list = append(list, strings.TrimSpace(s))
+ }
+ return list
+}
+
+// ParsePairs extracts key/value pairs from a comma-separated list of
+// values as described by RFC 2068 and returns a map[key]value. The
+// resulting values are unquoted. If a list element doesn't contain a
+// "=", the key is the element itself and the value is an empty
+// string.
+//
+// Lifted from https://code.google.com/p/gorilla/source/browse/http/parser/parser.go
+func ParsePairs(value string) map[string]string {
+ m := make(map[string]string)
+ for _, pair := range ParseList(strings.TrimSpace(value)) {
+ if i := strings.Index(pair, "="); i < 0 {
+ m[pair] = ""
+ } else {
+ v := pair[i+1:]
+ if v[0] == '"' && v[len(v)-1] == '"' {
+ // Unquote it.
+ v = v[1 : len(v)-1]
+ }
+ m[pair[:i]] = v
+ }
+ }
+ return m
+}
+
+// Headers contains header and error codes used by authenticator.
+type Headers struct {
+ Authenticate string // WWW-Authenticate
+ Authorization string // Authorization
+ AuthInfo string // Authentication-Info
+ UnauthCode int // 401
+ UnauthContentType string // text/plain
+ UnauthResponse string // Unauthorized.
+}
+
+// V returns NormalHeaders when h is nil, or h otherwise. Allows to
+// use uninitialized *Headers values in structs.
+func (h *Headers) V() *Headers {
+ if h == nil {
+ return NormalHeaders
+ }
+ return h
+}
+
+var (
+ // NormalHeaders are the regular Headers used by an HTTP Server for
+ // request authentication.
+ NormalHeaders = &Headers{
+ Authenticate: "WWW-Authenticate",
+ Authorization: "Authorization",
+ AuthInfo: "Authentication-Info",
+ UnauthCode: http.StatusUnauthorized,
+ UnauthContentType: "text/plain",
+ UnauthResponse: fmt.Sprintf("%d %s\n", http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized)),
+ }
+
+ // ProxyHeaders are Headers used by an HTTP Proxy server for proxy
+ // access authentication.
+ ProxyHeaders = &Headers{
+ Authenticate: "Proxy-Authenticate",
+ Authorization: "Proxy-Authorization",
+ AuthInfo: "Proxy-Authentication-Info",
+ UnauthCode: http.StatusProxyAuthRequired,
+ UnauthContentType: "text/plain",
+ UnauthResponse: fmt.Sprintf("%d %s\n", http.StatusProxyAuthRequired, http.StatusText(http.StatusProxyAuthRequired)),
+ }
+)
+
+const contentType = "Content-Type"
diff --git a/vendor/github.com/abbot/go-http-auth/test.htdigest b/vendor/github.com/abbot/go-http-auth/test.htdigest
new file mode 100644
index 0000000..6c8c75b
--- /dev/null
+++ b/vendor/github.com/abbot/go-http-auth/test.htdigest
@@ -0,0 +1 @@
+test:example.com:aa78524fceb0e50fd8ca96dd818b8cf9
diff --git a/vendor/github.com/abbot/go-http-auth/test.htpasswd b/vendor/github.com/abbot/go-http-auth/test.htpasswd
new file mode 100644
index 0000000..4844a3c
--- /dev/null
+++ b/vendor/github.com/abbot/go-http-auth/test.htpasswd
@@ -0,0 +1,4 @@
+test:{SHA}qvTGHdzF6KLavt4PO0gs2a6pQ00=
+test2:$apr1$a0j62R97$mYqFkloXH0/UOaUnAiV2b0
+test16:$apr1$JI4wh3am$AmhephVqLTUyAVpFQeHZC0
+test3:$2y$05$ih3C91zUBSTFcAh2mQnZYuob0UOZVEf16wl/ukgjDhjvj.xgM1WwS
diff --git a/vendor/github.com/abbot/go-http-auth/users.go b/vendor/github.com/abbot/go-http-auth/users.go
new file mode 100644
index 0000000..3771812
--- /dev/null
+++ b/vendor/github.com/abbot/go-http-auth/users.go
@@ -0,0 +1,154 @@
+package auth
+
+import (
+ "encoding/csv"
+ "os"
+ "sync"
+)
+
+/*
+ SecretProvider is used by authenticators. Takes user name and realm
+ as an argument, returns secret required for authentication (HA1 for
+ digest authentication, properly encrypted password for basic).
+
+ Returning an empty string means failing the authentication.
+*/
+type SecretProvider func(user, realm string) string
+
+/*
+ Common functions for file auto-reloading
+*/
+type File struct {
+ Path string
+ Info os.FileInfo
+ /* must be set in inherited types during initialization */
+ Reload func()
+ mu sync.Mutex
+}
+
+func (f *File) ReloadIfNeeded() {
+ info, err := os.Stat(f.Path)
+ if err != nil {
+ panic(err)
+ }
+ f.mu.Lock()
+ defer f.mu.Unlock()
+ if f.Info == nil || f.Info.ModTime() != info.ModTime() {
+ f.Info = info
+ f.Reload()
+ }
+}
+
+/*
+ Structure used for htdigest file authentication. Users map realms to
+ maps of users to their HA1 digests.
+*/
+type HtdigestFile struct {
+ File
+ Users map[string]map[string]string
+ mu sync.RWMutex
+}
+
+func reload_htdigest(hf *HtdigestFile) {
+ r, err := os.Open(hf.Path)
+ if err != nil {
+ panic(err)
+ }
+ csv_reader := csv.NewReader(r)
+ csv_reader.Comma = ':'
+ csv_reader.Comment = '#'
+ csv_reader.TrimLeadingSpace = true
+
+ records, err := csv_reader.ReadAll()
+ if err != nil {
+ panic(err)
+ }
+
+ hf.mu.Lock()
+ defer hf.mu.Unlock()
+ hf.Users = make(map[string]map[string]string)
+ for _, record := range records {
+ _, exists := hf.Users[record[1]]
+ if !exists {
+ hf.Users[record[1]] = make(map[string]string)
+ }
+ hf.Users[record[1]][record[0]] = record[2]
+ }
+}
+
+/*
+ SecretProvider implementation based on htdigest-formated files. Will
+ reload htdigest file on changes. Will panic on syntax errors in
+ htdigest files.
+*/
+func HtdigestFileProvider(filename string) SecretProvider {
+ hf := &HtdigestFile{File: File{Path: filename}}
+ hf.Reload = func() { reload_htdigest(hf) }
+ return func(user, realm string) string {
+ hf.ReloadIfNeeded()
+ hf.mu.RLock()
+ defer hf.mu.RUnlock()
+ _, exists := hf.Users[realm]
+ if !exists {
+ return ""
+ }
+ digest, exists := hf.Users[realm][user]
+ if !exists {
+ return ""
+ }
+ return digest
+ }
+}
+
+/*
+ Structure used for htdigest file authentication. Users map users to
+ their salted encrypted password
+*/
+type HtpasswdFile struct {
+ File
+ Users map[string]string
+ mu sync.RWMutex
+}
+
+func reload_htpasswd(h *HtpasswdFile) {
+ r, err := os.Open(h.Path)
+ if err != nil {
+ panic(err)
+ }
+ csv_reader := csv.NewReader(r)
+ csv_reader.Comma = ':'
+ csv_reader.Comment = '#'
+ csv_reader.TrimLeadingSpace = true
+
+ records, err := csv_reader.ReadAll()
+ if err != nil {
+ panic(err)
+ }
+
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ h.Users = make(map[string]string)
+ for _, record := range records {
+ h.Users[record[0]] = record[1]
+ }
+}
+
+/*
+ SecretProvider implementation based on htpasswd-formated files. Will
+ reload htpasswd file on changes. Will panic on syntax errors in
+ htpasswd files. Realm argument of the SecretProvider is ignored.
+*/
+func HtpasswdFileProvider(filename string) SecretProvider {
+ h := &HtpasswdFile{File: File{Path: filename}}
+ h.Reload = func() { reload_htpasswd(h) }
+ return func(user, realm string) string {
+ h.ReloadIfNeeded()
+ h.mu.RLock()
+ password, exists := h.Users[user]
+ h.mu.RUnlock()
+ if !exists {
+ return ""
+ }
+ return password
+ }
+}
diff --git a/vendor/github.com/beevik/etree/.travis.yml b/vendor/github.com/beevik/etree/.travis.yml
new file mode 100644
index 0000000..7caa247
--- /dev/null
+++ b/vendor/github.com/beevik/etree/.travis.yml
@@ -0,0 +1,16 @@
+language: go
+sudo: false
+
+go:
+ - 1.4.2
+ - 1.5.1
+ - 1.6
+ - tip
+
+matrix:
+ allow_failures:
+ - go: tip
+
+script:
+ - go vet ./...
+ - go test -v ./...
diff --git a/vendor/github.com/beevik/etree/CONTRIBUTORS b/vendor/github.com/beevik/etree/CONTRIBUTORS
new file mode 100644
index 0000000..084662c
--- /dev/null
+++ b/vendor/github.com/beevik/etree/CONTRIBUTORS
@@ -0,0 +1,8 @@
+Brett Vickers (beevik)
+Felix Geisendörfer (felixge)
+Kamil Kisiel (kisielk)
+Graham King (grahamking)
+Matt Smith (ma314smith)
+Michal Jemala (michaljemala)
+Nicolas Piganeau (npiganeau)
+Chris Brown (ccbrown)
diff --git a/vendor/github.com/beevik/etree/LICENSE b/vendor/github.com/beevik/etree/LICENSE
new file mode 100644
index 0000000..e14ad68
--- /dev/null
+++ b/vendor/github.com/beevik/etree/LICENSE
@@ -0,0 +1,24 @@
+Copyright 2015 Brett Vickers. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/beevik/etree/README.md b/vendor/github.com/beevik/etree/README.md
new file mode 100644
index 0000000..2855843
--- /dev/null
+++ b/vendor/github.com/beevik/etree/README.md
@@ -0,0 +1,203 @@
+[![Build Status](https://travis-ci.org/beevik/etree.svg?branch=master)](https://travis-ci.org/beevik/etree)
+[![GoDoc](https://godoc.org/github.com/beevik/etree?status.svg)](https://godoc.org/github.com/beevik/etree)
+
+etree
+=====
+
+The etree package is a lightweight, pure go package that expresses XML in
+the form of an element tree. Its design was inspired by the Python
+[ElementTree](http://docs.python.org/2/library/xml.etree.elementtree.html)
+module. Some of the package's features include:
+
+* Represents XML documents as trees of elements for easy traversal.
+* Imports, serializes, modifies or creates XML documents from scratch.
+* Writes and reads XML to/from files, byte slices, strings and io interfaces.
+* Performs simple or complex searches with lightweight XPath-like query APIs.
+* Auto-indents XML using spaces or tabs for better readability.
+* Implemented in pure go; depends only on standard go libraries.
+* Built on top of the go [encoding/xml](http://golang.org/pkg/encoding/xml)
+ package.
+
+### Creating an XML document
+
+The following example creates an XML document from scratch using the etree
+package and outputs its indented contents to stdout.
+```go
+doc := etree.NewDocument()
+doc.CreateProcInst("xml", `version="1.0" encoding="UTF-8"`)
+doc.CreateProcInst("xml-stylesheet", `type="text/xsl" href="style.xsl"`)
+
+people := doc.CreateElement("People")
+people.CreateComment("These are all known people")
+
+jon := people.CreateElement("Person")
+jon.CreateAttr("name", "Jon")
+
+sally := people.CreateElement("Person")
+sally.CreateAttr("name", "Sally")
+
+doc.Indent(2)
+doc.WriteTo(os.Stdout)
+```
+
+Output:
+```xml
+
+
+
+
+
+
+
+```
+
+### Reading an XML file
+
+Suppose you have a file on disk called `bookstore.xml` containing the
+following data:
+
+```xml
+
+
+
+ Everyday Italian
+ Giada De Laurentiis
+ 2005
+ 30.00
+
+
+
+ Harry Potter
+ J K. Rowling
+ 2005
+ 29.99
+
+
+
+ XQuery Kick Start
+ James McGovern
+ Per Bothner
+ Kurt Cagle
+ James Linn
+ Vaidyanathan Nagarajan
+ 2003
+ 49.99
+
+
+
+ Learning XML
+ Erik T. Ray
+ 2003
+ 39.95
+
+
+
+```
+
+This code reads the file's contents into an etree document.
+```go
+doc := etree.NewDocument()
+if err := doc.ReadFromFile("bookstore.xml"); err != nil {
+ panic(err)
+}
+```
+
+You can also read XML from a string, a byte slice, or an `io.Reader`.
+
+### Processing elements and attributes
+
+This example illustrates several ways to access elements and attributes using
+etree selection queries.
+```go
+root := doc.SelectElement("bookstore")
+fmt.Println("ROOT element:", root.Tag)
+
+for _, book := range root.SelectElements("book") {
+ fmt.Println("CHILD element:", book.Tag)
+ if title := book.SelectElement("title"); title != nil {
+ lang := title.SelectAttrValue("lang", "unknown")
+ fmt.Printf(" TITLE: %s (%s)\n", title.Text(), lang)
+ }
+ for _, attr := range book.Attr {
+ fmt.Printf(" ATTR: %s=%s\n", attr.Key, attr.Value)
+ }
+}
+```
+Output:
+```
+ROOT element: bookstore
+CHILD element: book
+ TITLE: Everyday Italian (en)
+ ATTR: category=COOKING
+CHILD element: book
+ TITLE: Harry Potter (en)
+ ATTR: category=CHILDREN
+CHILD element: book
+ TITLE: XQuery Kick Start (en)
+ ATTR: category=WEB
+CHILD element: book
+ TITLE: Learning XML (en)
+ ATTR: category=WEB
+```
+
+### Path queries
+
+This example uses etree's path functions to select all book titles that fall
+into the category of 'WEB'. The double-slash prefix in the path causes the
+search for book elements to occur recursively; book elements may appear at any
+level of the XML hierarchy.
+```go
+for _, t := range doc.FindElements("//book[@category='WEB']/title") {
+ fmt.Println("Title:", t.Text())
+}
+```
+
+Output:
+```
+Title: XQuery Kick Start
+Title: Learning XML
+```
+
+This example finds the first book element under the root bookstore element and
+outputs the tag and text of each of its child elements.
+```go
+for _, e := range doc.FindElements("./bookstore/book[1]/*") {
+ fmt.Printf("%s: %s\n", e.Tag, e.Text())
+}
+```
+
+Output:
+```
+title: Everyday Italian
+author: Giada De Laurentiis
+year: 2005
+price: 30.00
+```
+
+This example finds all books with a price of 49.99 and outputs their titles.
+```go
+path := etree.MustCompilePath("./bookstore/book[p:price='49.99']/title")
+for _, e := range doc.FindElementsPath(path) {
+ fmt.Println(e.Text())
+}
+```
+
+Output:
+```
+XQuery Kick Start
+```
+
+Note that this example uses the FindElementsPath function, which takes as an
+argument a pre-compiled path object. Use precompiled paths when you plan to
+search with the same path more than once.
+
+### Other features
+
+These are just a few examples of the things the etree package can do. See the
+[documentation](http://godoc.org/github.com/beevik/etree) for a complete
+description of its capabilities.
+
+### Contributing
+
+This project accepts contributions. Just fork the repo and submit a pull
+request!
diff --git a/vendor/github.com/beevik/etree/etree.go b/vendor/github.com/beevik/etree/etree.go
new file mode 100644
index 0000000..36b279f
--- /dev/null
+++ b/vendor/github.com/beevik/etree/etree.go
@@ -0,0 +1,943 @@
+// Copyright 2015 Brett Vickers.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package etree provides XML services through an Element Tree
+// abstraction.
+package etree
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/xml"
+ "errors"
+ "io"
+ "os"
+ "strings"
+)
+
+const (
+ // NoIndent is used with Indent to disable all indenting.
+ NoIndent = -1
+)
+
+// ErrXML is returned when XML parsing fails due to incorrect formatting.
+var ErrXML = errors.New("etree: invalid XML format")
+
+// ReadSettings allow for changing the default behavior of the ReadFrom*
+// methods.
+type ReadSettings struct {
+ // CharsetReader to be passed to standard xml.Decoder. Default: nil.
+ CharsetReader func(charset string, input io.Reader) (io.Reader, error)
+
+ // Permissive allows input containing common mistakes such as missing tags
+ // or attribute values. Default: false.
+ Permissive bool
+}
+
+// newReadSettings creates a default ReadSettings record.
+func newReadSettings() ReadSettings {
+ return ReadSettings{}
+}
+
+// WriteSettings allow for changing the serialization behavior of the WriteTo*
+// methods.
+type WriteSettings struct {
+ // CanonicalEndTags forces the production of XML end tags, even for
+ // elements that have no child elements. Default: false.
+ CanonicalEndTags bool
+
+ // CanonicalText forces the production of XML character references for
+ // text data characters &, <, and >. If false, XML character references
+ // are also produced for " and '. Default: false.
+ CanonicalText bool
+
+ // CanonicalAttrVal forces the production of XML character references for
+ // attribute value characters &, < and ". If false, XML character
+ // references are also produced for > and '. Default: false.
+ CanonicalAttrVal bool
+}
+
+// newWriteSettings creates a default WriteSettings record.
+func newWriteSettings() WriteSettings {
+ return WriteSettings{
+ CanonicalEndTags: false,
+ CanonicalText: false,
+ CanonicalAttrVal: false,
+ }
+}
+
+// A Token is an empty interface that represents an Element, CharData,
+// Comment, Directive, or ProcInst.
+type Token interface {
+ Parent() *Element
+ dup(parent *Element) Token
+ setParent(parent *Element)
+ writeTo(w *bufio.Writer, s *WriteSettings)
+}
+
+// A Document is a container holding a complete XML hierarchy. Its embedded
+// element contains zero or more children, one of which is usually the root
+// element. The embedded element may include other children such as
+// processing instructions or BOM CharData tokens.
+type Document struct {
+ Element
+ ReadSettings ReadSettings
+ WriteSettings WriteSettings
+}
+
+// An Element represents an XML element, its attributes, and its child tokens.
+type Element struct {
+ Space, Tag string // namespace and tag
+ Attr []Attr // key-value attribute pairs
+ Child []Token // child tokens (elements, comments, etc.)
+ parent *Element // parent element
+}
+
+// An Attr represents a key-value attribute of an XML element.
+type Attr struct {
+ Space, Key string // The attribute's namespace and key
+ Value string // The attribute value string
+}
+
+// CharData represents character data within XML.
+type CharData struct {
+ Data string
+ parent *Element
+ whitespace bool
+}
+
+// A Comment represents an XML comment.
+type Comment struct {
+ Data string
+ parent *Element
+}
+
+// A Directive represents an XML directive.
+type Directive struct {
+ Data string
+ parent *Element
+}
+
+// A ProcInst represents an XML processing instruction.
+type ProcInst struct {
+ Target string
+ Inst string
+ parent *Element
+}
+
+// NewDocument creates an XML document without a root element.
+func NewDocument() *Document {
+ return &Document{
+ Element{Child: make([]Token, 0)},
+ newReadSettings(),
+ newWriteSettings(),
+ }
+}
+
+// Copy returns a recursive, deep copy of the document.
+func (d *Document) Copy() *Document {
+ return &Document{*(d.dup(nil).(*Element)), d.ReadSettings, d.WriteSettings}
+}
+
+// Root returns the root element of the document, or nil if there is no root
+// element.
+func (d *Document) Root() *Element {
+ for _, t := range d.Child {
+ if c, ok := t.(*Element); ok {
+ return c
+ }
+ }
+ return nil
+}
+
+// SetRoot replaces the document's root element with e. If the document
+// already has a root when this function is called, then the document's
+// original root is unbound first. If the element e is bound to another
+// document (or to another element within a document), then it is unbound
+// first.
+func (d *Document) SetRoot(e *Element) {
+ if e.parent != nil {
+ e.parent.RemoveChild(e)
+ }
+ e.setParent(&d.Element)
+
+ for i, t := range d.Child {
+ if _, ok := t.(*Element); ok {
+ t.setParent(nil)
+ d.Child[i] = e
+ return
+ }
+ }
+ d.Child = append(d.Child, e)
+}
+
+// ReadFrom reads XML from the reader r into the document d. It returns the
+// number of bytes read and any error encountered.
+func (d *Document) ReadFrom(r io.Reader) (n int64, err error) {
+ return d.Element.readFrom(r, d.ReadSettings)
+}
+
+// ReadFromFile reads XML from the string s into the document d.
+func (d *Document) ReadFromFile(filename string) error {
+ f, err := os.Open(filename)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ _, err = d.ReadFrom(f)
+ return err
+}
+
+// ReadFromBytes reads XML from the byte slice b into the document d.
+func (d *Document) ReadFromBytes(b []byte) error {
+ _, err := d.ReadFrom(bytes.NewReader(b))
+ return err
+}
+
+// ReadFromString reads XML from the string s into the document d.
+func (d *Document) ReadFromString(s string) error {
+ _, err := d.ReadFrom(strings.NewReader(s))
+ return err
+}
+
+// WriteTo serializes an XML document into the writer w. It
+// returns the number of bytes written and any error encountered.
+func (d *Document) WriteTo(w io.Writer) (n int64, err error) {
+ cw := newCountWriter(w)
+ b := bufio.NewWriter(cw)
+ for _, c := range d.Child {
+ c.writeTo(b, &d.WriteSettings)
+ }
+ err, n = b.Flush(), cw.bytes
+ return
+}
+
+// WriteToFile serializes an XML document into the file named
+// filename.
+func (d *Document) WriteToFile(filename string) error {
+ f, err := os.Create(filename)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ _, err = d.WriteTo(f)
+ return err
+}
+
+// WriteToBytes serializes the XML document into a slice of
+// bytes.
+func (d *Document) WriteToBytes() (b []byte, err error) {
+ var buf bytes.Buffer
+ if _, err = d.WriteTo(&buf); err != nil {
+ return
+ }
+ return buf.Bytes(), nil
+}
+
+// WriteToString serializes the XML document into a string.
+func (d *Document) WriteToString() (s string, err error) {
+ var b []byte
+ if b, err = d.WriteToBytes(); err != nil {
+ return
+ }
+ return string(b), nil
+}
+
+type indentFunc func(depth int) string
+
+// Indent modifies the document's element tree by inserting CharData entities
+// containing carriage returns and indentation. The amount of indentation per
+// depth level is given as spaces. Pass etree.NoIndent for spaces if you want
+// no indentation at all.
+func (d *Document) Indent(spaces int) {
+ var indent indentFunc
+ switch {
+ case spaces < 0:
+ indent = func(depth int) string { return "" }
+ default:
+ indent = func(depth int) string { return crIndent(depth*spaces, crsp) }
+ }
+ d.Element.indent(0, indent)
+}
+
+// IndentTabs modifies the document's element tree by inserting CharData
+// entities containing carriage returns and tabs for indentation. One tab is
+// used per indentation level.
+func (d *Document) IndentTabs() {
+ indent := func(depth int) string { return crIndent(depth, crtab) }
+ d.Element.indent(0, indent)
+}
+
+// NewElement creates an unparented element with the specified tag. The tag
+// may be prefixed by a namespace and a colon.
+func NewElement(tag string) *Element {
+ space, stag := spaceDecompose(tag)
+ return newElement(space, stag, nil)
+}
+
+// newElement is a helper function that creates an element and binds it to
+// a parent element if possible.
+func newElement(space, tag string, parent *Element) *Element {
+ e := &Element{
+ Space: space,
+ Tag: tag,
+ Attr: make([]Attr, 0),
+ Child: make([]Token, 0),
+ parent: parent,
+ }
+ if parent != nil {
+ parent.addChild(e)
+ }
+ return e
+}
+
+// Copy creates a recursive, deep copy of the element and all its attributes
+// and children. The returned element has no parent but can be parented to a
+// another element using AddElement, or to a document using SetRoot.
+func (e *Element) Copy() *Element {
+ var parent *Element
+ return e.dup(parent).(*Element)
+}
+
+// Text returns the characters immediately following the element's
+// opening tag.
+func (e *Element) Text() string {
+ if len(e.Child) == 0 {
+ return ""
+ }
+ if cd, ok := e.Child[0].(*CharData); ok {
+ return cd.Data
+ }
+ return ""
+}
+
+// SetText replaces an element's subsidiary CharData text with a new string.
+func (e *Element) SetText(text string) {
+ if len(e.Child) > 0 {
+ if cd, ok := e.Child[0].(*CharData); ok {
+ cd.Data = text
+ return
+ }
+ }
+ cd := newCharData(text, false, e)
+ copy(e.Child[1:], e.Child[0:])
+ e.Child[0] = cd
+}
+
+// CreateElement creates an element with the specified tag and adds it as the
+// last child element of the element e. The tag may be prefixed by a namespace
+// and a colon.
+func (e *Element) CreateElement(tag string) *Element {
+ space, stag := spaceDecompose(tag)
+ return newElement(space, stag, e)
+}
+
+// AddChild adds the token t as the last child of element e. If token t was
+// already the child of another element, it is first removed from its current
+// parent element.
+func (e *Element) AddChild(t Token) {
+ if t.Parent() != nil {
+ t.Parent().RemoveChild(t)
+ }
+ t.setParent(e)
+ e.addChild(t)
+}
+
+// InsertChild inserts the token t before e's existing child token ex. If ex
+// is nil (or if ex is not a child of e), then t is added to the end of e's
+// child token list. If token t was already the child of another element, it
+// is first removed from its current parent element.
+func (e *Element) InsertChild(ex Token, t Token) {
+ if t.Parent() != nil {
+ t.Parent().RemoveChild(t)
+ }
+ t.setParent(e)
+
+ for i, c := range e.Child {
+ if c == ex {
+ e.Child = append(e.Child, nil)
+ copy(e.Child[i+1:], e.Child[i:])
+ e.Child[i] = t
+ return
+ }
+ }
+ e.addChild(t)
+}
+
+// RemoveChild attempts to remove the token t from element e's list of
+// children. If the token t is a child of e, then it is returned. Otherwise,
+// nil is returned.
+func (e *Element) RemoveChild(t Token) Token {
+ for i, c := range e.Child {
+ if c == t {
+ e.Child = append(e.Child[:i], e.Child[i+1:]...)
+ c.setParent(nil)
+ return t
+ }
+ }
+ return nil
+}
+
+// ReadFrom reads XML from the reader r and stores the result as a new child
+// of element e.
+func (e *Element) readFrom(ri io.Reader, settings ReadSettings) (n int64, err error) {
+ r := newCountReader(ri)
+ dec := xml.NewDecoder(r)
+ dec.CharsetReader = settings.CharsetReader
+ dec.Strict = !settings.Permissive
+ var stack stack
+ stack.push(e)
+ for {
+ t, err := dec.RawToken()
+ switch {
+ case err == io.EOF:
+ return r.bytes, nil
+ case err != nil:
+ return r.bytes, err
+ case stack.empty():
+ return r.bytes, ErrXML
+ }
+
+ top := stack.peek().(*Element)
+
+ switch t := t.(type) {
+ case xml.StartElement:
+ e := newElement(t.Name.Space, t.Name.Local, top)
+ for _, a := range t.Attr {
+ e.createAttr(a.Name.Space, a.Name.Local, a.Value)
+ }
+ stack.push(e)
+ case xml.EndElement:
+ stack.pop()
+ case xml.CharData:
+ data := string(t)
+ newCharData(data, isWhitespace(data), top)
+ case xml.Comment:
+ newComment(string(t), top)
+ case xml.Directive:
+ newDirective(string(t), top)
+ case xml.ProcInst:
+ newProcInst(t.Target, string(t.Inst), top)
+ }
+ }
+}
+
+// SelectAttr finds an element attribute matching the requested key and
+// returns it if found. The key may be prefixed by a namespace and a colon.
+func (e *Element) SelectAttr(key string) *Attr {
+ space, skey := spaceDecompose(key)
+ for i, a := range e.Attr {
+ if spaceMatch(space, a.Space) && skey == a.Key {
+ return &e.Attr[i]
+ }
+ }
+ return nil
+}
+
+// SelectAttrValue finds an element attribute matching the requested key and
+// returns its value if found. The key may be prefixed by a namespace and a
+// colon. If the key is not found, the dflt value is returned instead.
+func (e *Element) SelectAttrValue(key, dflt string) string {
+ space, skey := spaceDecompose(key)
+ for _, a := range e.Attr {
+ if spaceMatch(space, a.Space) && skey == a.Key {
+ return a.Value
+ }
+ }
+ return dflt
+}
+
+// ChildElements returns all elements that are children of element e.
+func (e *Element) ChildElements() []*Element {
+ var elements []*Element
+ for _, t := range e.Child {
+ if c, ok := t.(*Element); ok {
+ elements = append(elements, c)
+ }
+ }
+ return elements
+}
+
+// SelectElement returns the first child element with the given tag. The tag
+// may be prefixed by a namespace and a colon.
+func (e *Element) SelectElement(tag string) *Element {
+ space, stag := spaceDecompose(tag)
+ for _, t := range e.Child {
+ if c, ok := t.(*Element); ok && spaceMatch(space, c.Space) && stag == c.Tag {
+ return c
+ }
+ }
+ return nil
+}
+
+// SelectElements returns a slice of all child elements with the given tag.
+// The tag may be prefixed by a namespace and a colon.
+func (e *Element) SelectElements(tag string) []*Element {
+ space, stag := spaceDecompose(tag)
+ var elements []*Element
+ for _, t := range e.Child {
+ if c, ok := t.(*Element); ok && spaceMatch(space, c.Space) && stag == c.Tag {
+ elements = append(elements, c)
+ }
+ }
+ return elements
+}
+
+// FindElement returns the first element matched by the XPath-like path
+// string. Panics if an invalid path string is supplied.
+func (e *Element) FindElement(path string) *Element {
+ return e.FindElementPath(MustCompilePath(path))
+}
+
+// FindElementPath returns the first element matched by the XPath-like path
+// string.
+func (e *Element) FindElementPath(path Path) *Element {
+ p := newPather()
+ elements := p.traverse(e, path)
+ switch {
+ case len(elements) > 0:
+ return elements[0]
+ default:
+ return nil
+ }
+}
+
+// FindElements returns a slice of elements matched by the XPath-like path
+// string. Panics if an invalid path string is supplied.
+func (e *Element) FindElements(path string) []*Element {
+ return e.FindElementsPath(MustCompilePath(path))
+}
+
+// FindElementsPath returns a slice of elements matched by the Path object.
+func (e *Element) FindElementsPath(path Path) []*Element {
+ p := newPather()
+ return p.traverse(e, path)
+}
+
+// indent recursively inserts proper indentation between an
+// XML element's child tokens.
+func (e *Element) indent(depth int, indent indentFunc) {
+ e.stripIndent()
+ n := len(e.Child)
+ if n == 0 {
+ return
+ }
+
+ oldChild := e.Child
+ e.Child = make([]Token, 0, n*2+1)
+ isCharData, firstNonCharData := false, true
+ for _, c := range oldChild {
+
+ // Insert CR+indent before child if it's not character data.
+ // Exceptions: when it's the first non-character-data child, or when
+ // the child is at root depth.
+ _, isCharData = c.(*CharData)
+ if !isCharData {
+ if !firstNonCharData || depth > 0 {
+ newCharData(indent(depth), true, e)
+ }
+ firstNonCharData = false
+ }
+
+ e.addChild(c)
+
+ // Recursively process child elements.
+ if ce, ok := c.(*Element); ok {
+ ce.indent(depth+1, indent)
+ }
+ }
+
+ // Insert CR+indent before the last child.
+ if !isCharData {
+ if !firstNonCharData || depth > 0 {
+ newCharData(indent(depth-1), true, e)
+ }
+ }
+}
+
+// stripIndent removes any previously inserted indentation.
+func (e *Element) stripIndent() {
+ // Count the number of non-indent child tokens
+ n := len(e.Child)
+ for _, c := range e.Child {
+ if cd, ok := c.(*CharData); ok && cd.whitespace {
+ n--
+ }
+ }
+ if n == len(e.Child) {
+ return
+ }
+
+ // Strip out indent CharData
+ newChild := make([]Token, n)
+ j := 0
+ for _, c := range e.Child {
+ if cd, ok := c.(*CharData); ok && cd.whitespace {
+ continue
+ }
+ newChild[j] = c
+ j++
+ }
+ e.Child = newChild
+}
+
+// dup duplicates the element.
+func (e *Element) dup(parent *Element) Token {
+ ne := &Element{
+ Space: e.Space,
+ Tag: e.Tag,
+ Attr: make([]Attr, len(e.Attr)),
+ Child: make([]Token, len(e.Child)),
+ parent: parent,
+ }
+ for i, t := range e.Child {
+ ne.Child[i] = t.dup(ne)
+ }
+ for i, a := range e.Attr {
+ ne.Attr[i] = a
+ }
+ return ne
+}
+
+// Parent returns the element token's parent element, or nil if it has no
+// parent.
+func (e *Element) Parent() *Element {
+ return e.parent
+}
+
+// setParent replaces the element token's parent.
+func (e *Element) setParent(parent *Element) {
+ e.parent = parent
+}
+
+// writeTo serializes the element to the writer w.
+func (e *Element) writeTo(w *bufio.Writer, s *WriteSettings) {
+ w.WriteByte('<')
+ if e.Space != "" {
+ w.WriteString(e.Space)
+ w.WriteByte(':')
+ }
+ w.WriteString(e.Tag)
+ for _, a := range e.Attr {
+ w.WriteByte(' ')
+ a.writeTo(w, s)
+ }
+ if len(e.Child) > 0 {
+ w.WriteString(">")
+ for _, c := range e.Child {
+ c.writeTo(w, s)
+ }
+ w.Write([]byte{'<', '/'})
+ if e.Space != "" {
+ w.WriteString(e.Space)
+ w.WriteByte(':')
+ }
+ w.WriteString(e.Tag)
+ w.WriteByte('>')
+ } else {
+ if s.CanonicalEndTags {
+ w.Write([]byte{'>', '<', '/'})
+ if e.Space != "" {
+ w.WriteString(e.Space)
+ w.WriteByte(':')
+ }
+ w.WriteString(e.Tag)
+ w.WriteByte('>')
+ } else {
+ w.Write([]byte{'/', '>'})
+ }
+ }
+}
+
+// addChild adds a child token to the element e.
+func (e *Element) addChild(t Token) {
+ e.Child = append(e.Child, t)
+}
+
+// CreateAttr creates an attribute and adds it to element e. The key may be
+// prefixed by a namespace and a colon. If an attribute with the key already
+// exists, its value is replaced.
+func (e *Element) CreateAttr(key, value string) *Attr {
+ space, skey := spaceDecompose(key)
+ return e.createAttr(space, skey, value)
+}
+
+// createAttr is a helper function that creates attributes.
+func (e *Element) createAttr(space, key, value string) *Attr {
+ for i, a := range e.Attr {
+ if space == a.Space && key == a.Key {
+ e.Attr[i].Value = value
+ return &e.Attr[i]
+ }
+ }
+ a := Attr{space, key, value}
+ e.Attr = append(e.Attr, a)
+ return &e.Attr[len(e.Attr)-1]
+}
+
+// RemoveAttr removes and returns the first attribute of the element whose key
+// matches the given key. The key may be prefixed by a namespace and a colon.
+// If an equal attribute does not exist, nil is returned.
+func (e *Element) RemoveAttr(key string) *Attr {
+ space, skey := spaceDecompose(key)
+ for i, a := range e.Attr {
+ if space == a.Space && skey == a.Key {
+ e.Attr = append(e.Attr[0:i], e.Attr[i+1:]...)
+ return &a
+ }
+ }
+ return nil
+}
+
+var xmlReplacerNormal = strings.NewReplacer(
+ "&", "&",
+ "<", "<",
+ ">", ">",
+ "'", "'",
+ `"`, """,
+)
+
+var xmlReplacerCanonicalText = strings.NewReplacer(
+ "&", "&",
+ "<", "<",
+ ">", ">",
+ "\r", "
",
+)
+
+var xmlReplacerCanonicalAttrVal = strings.NewReplacer(
+ "&", "&",
+ "<", "<",
+ `"`, """,
+ "\t", " ",
+ "\n", "
",
+ "\r", "
",
+)
+
+// writeTo serializes the attribute to the writer.
+func (a *Attr) writeTo(w *bufio.Writer, s *WriteSettings) {
+ if a.Space != "" {
+ w.WriteString(a.Space)
+ w.WriteByte(':')
+ }
+ w.WriteString(a.Key)
+ w.WriteString(`="`)
+ var r *strings.Replacer
+ if s.CanonicalAttrVal {
+ r = xmlReplacerCanonicalAttrVal
+ } else {
+ r = xmlReplacerNormal
+ }
+ w.WriteString(r.Replace(a.Value))
+ w.WriteByte('"')
+}
+
+// NewCharData creates a parentless XML character data entity.
+func NewCharData(data string) *CharData {
+ return newCharData(data, false, nil)
+}
+
+// newCharData creates an XML character data entity and binds it to a parent
+// element. If parent is nil, the CharData token remains unbound.
+func newCharData(data string, whitespace bool, parent *Element) *CharData {
+ c := &CharData{
+ Data: data,
+ whitespace: whitespace,
+ parent: parent,
+ }
+ if parent != nil {
+ parent.addChild(c)
+ }
+ return c
+}
+
+// CreateCharData creates an XML character data entity and adds it as a child
+// of element e.
+func (e *Element) CreateCharData(data string) *CharData {
+ return newCharData(data, false, e)
+}
+
+// dup duplicates the character data.
+func (c *CharData) dup(parent *Element) Token {
+ return &CharData{
+ Data: c.Data,
+ whitespace: c.whitespace,
+ parent: parent,
+ }
+}
+
+// Parent returns the character data token's parent element, or nil if it has
+// no parent.
+func (c *CharData) Parent() *Element {
+ return c.parent
+}
+
+// setParent replaces the character data token's parent.
+func (c *CharData) setParent(parent *Element) {
+ c.parent = parent
+}
+
+// writeTo serializes the character data entity to the writer.
+func (c *CharData) writeTo(w *bufio.Writer, s *WriteSettings) {
+ var r *strings.Replacer
+ if s.CanonicalText {
+ r = xmlReplacerCanonicalText
+ } else {
+ r = xmlReplacerNormal
+ }
+ w.WriteString(r.Replace(c.Data))
+}
+
+// NewComment creates a parentless XML comment.
+func NewComment(comment string) *Comment {
+ return newComment(comment, nil)
+}
+
+// NewComment creates an XML comment and binds it to a parent element. If
+// parent is nil, the Comment remains unbound.
+func newComment(comment string, parent *Element) *Comment {
+ c := &Comment{
+ Data: comment,
+ parent: parent,
+ }
+ if parent != nil {
+ parent.addChild(c)
+ }
+ return c
+}
+
+// CreateComment creates an XML comment and adds it as a child of element e.
+func (e *Element) CreateComment(comment string) *Comment {
+ return newComment(comment, e)
+}
+
+// dup duplicates the comment.
+func (c *Comment) dup(parent *Element) Token {
+ return &Comment{
+ Data: c.Data,
+ parent: parent,
+ }
+}
+
+// Parent returns comment token's parent element, or nil if it has no parent.
+func (c *Comment) Parent() *Element {
+ return c.parent
+}
+
+// setParent replaces the comment token's parent.
+func (c *Comment) setParent(parent *Element) {
+ c.parent = parent
+}
+
+// writeTo serialies the comment to the writer.
+func (c *Comment) writeTo(w *bufio.Writer, s *WriteSettings) {
+ w.WriteString("")
+}
+
+// NewDirective creates a parentless XML directive.
+func NewDirective(data string) *Directive {
+ return newDirective(data, nil)
+}
+
+// newDirective creates an XML directive and binds it to a parent element. If
+// parent is nil, the Directive remains unbound.
+func newDirective(data string, parent *Element) *Directive {
+ d := &Directive{
+ Data: data,
+ parent: parent,
+ }
+ if parent != nil {
+ parent.addChild(d)
+ }
+ return d
+}
+
+// CreateDirective creates an XML directive and adds it as the last child of
+// element e.
+func (e *Element) CreateDirective(data string) *Directive {
+ return newDirective(data, e)
+}
+
+// dup duplicates the directive.
+func (d *Directive) dup(parent *Element) Token {
+ return &Directive{
+ Data: d.Data,
+ parent: parent,
+ }
+}
+
+// Parent returns directive token's parent element, or nil if it has no
+// parent.
+func (d *Directive) Parent() *Element {
+ return d.parent
+}
+
+// setParent replaces the directive token's parent.
+func (d *Directive) setParent(parent *Element) {
+ d.parent = parent
+}
+
+// writeTo serializes the XML directive to the writer.
+func (d *Directive) writeTo(w *bufio.Writer, s *WriteSettings) {
+ w.WriteString("")
+}
+
+// NewProcInst creates a parentless XML processing instruction.
+func NewProcInst(target, inst string) *ProcInst {
+ return newProcInst(target, inst, nil)
+}
+
+// newProcInst creates an XML processing instruction and binds it to a parent
+// element. If parent is nil, the ProcInst remains unbound.
+func newProcInst(target, inst string, parent *Element) *ProcInst {
+ p := &ProcInst{
+ Target: target,
+ Inst: inst,
+ parent: parent,
+ }
+ if parent != nil {
+ parent.addChild(p)
+ }
+ return p
+}
+
+// CreateProcInst creates a processing instruction and adds it as a child of
+// element e.
+func (e *Element) CreateProcInst(target, inst string) *ProcInst {
+ return newProcInst(target, inst, e)
+}
+
+// dup duplicates the procinst.
+func (p *ProcInst) dup(parent *Element) Token {
+ return &ProcInst{
+ Target: p.Target,
+ Inst: p.Inst,
+ parent: parent,
+ }
+}
+
+// Parent returns processing instruction token's parent element, or nil if it
+// has no parent.
+func (p *ProcInst) Parent() *Element {
+ return p.parent
+}
+
+// setParent replaces the processing instruction token's parent.
+func (p *ProcInst) setParent(parent *Element) {
+ p.parent = parent
+}
+
+// writeTo serializes the processing instruction to the writer.
+func (p *ProcInst) writeTo(w *bufio.Writer, s *WriteSettings) {
+ w.WriteString("")
+ w.WriteString(p.Target)
+ if p.Inst != "" {
+ w.WriteByte(' ')
+ w.WriteString(p.Inst)
+ }
+ w.WriteString("?>")
+}
diff --git a/vendor/github.com/beevik/etree/helpers.go b/vendor/github.com/beevik/etree/helpers.go
new file mode 100644
index 0000000..4f8350e
--- /dev/null
+++ b/vendor/github.com/beevik/etree/helpers.go
@@ -0,0 +1,188 @@
+// Copyright 2015 Brett Vickers.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package etree
+
+import (
+ "io"
+ "strings"
+)
+
+// A simple stack
+type stack struct {
+ data []interface{}
+}
+
+func (s *stack) empty() bool {
+ return len(s.data) == 0
+}
+
+func (s *stack) push(value interface{}) {
+ s.data = append(s.data, value)
+}
+
+func (s *stack) pop() interface{} {
+ value := s.data[len(s.data)-1]
+ s.data[len(s.data)-1] = nil
+ s.data = s.data[:len(s.data)-1]
+ return value
+}
+
+func (s *stack) peek() interface{} {
+ return s.data[len(s.data)-1]
+}
+
+// A fifo is a simple first-in-first-out queue.
+type fifo struct {
+ data []interface{}
+ head, tail int
+}
+
+func (f *fifo) add(value interface{}) {
+ if f.len()+1 >= len(f.data) {
+ f.grow()
+ }
+ f.data[f.tail] = value
+ if f.tail++; f.tail == len(f.data) {
+ f.tail = 0
+ }
+}
+
+func (f *fifo) remove() interface{} {
+ value := f.data[f.head]
+ f.data[f.head] = nil
+ if f.head++; f.head == len(f.data) {
+ f.head = 0
+ }
+ return value
+}
+
+func (f *fifo) len() int {
+ if f.tail >= f.head {
+ return f.tail - f.head
+ }
+ return len(f.data) - f.head + f.tail
+}
+
+func (f *fifo) grow() {
+ c := len(f.data) * 2
+ if c == 0 {
+ c = 4
+ }
+ buf, count := make([]interface{}, c), f.len()
+ if f.tail >= f.head {
+ copy(buf[0:count], f.data[f.head:f.tail])
+ } else {
+ hindex := len(f.data) - f.head
+ copy(buf[0:hindex], f.data[f.head:])
+ copy(buf[hindex:count], f.data[:f.tail])
+ }
+ f.data, f.head, f.tail = buf, 0, count
+}
+
+// countReader implements a proxy reader that counts the number of
+// bytes read from its encapsulated reader.
+type countReader struct {
+ r io.Reader
+ bytes int64
+}
+
+func newCountReader(r io.Reader) *countReader {
+ return &countReader{r: r}
+}
+
+func (cr *countReader) Read(p []byte) (n int, err error) {
+ b, err := cr.r.Read(p)
+ cr.bytes += int64(b)
+ return b, err
+}
+
+// countWriter implements a proxy writer that counts the number of
+// bytes written by its encapsulated writer.
+type countWriter struct {
+ w io.Writer
+ bytes int64
+}
+
+func newCountWriter(w io.Writer) *countWriter {
+ return &countWriter{w: w}
+}
+
+func (cw *countWriter) Write(p []byte) (n int, err error) {
+ b, err := cw.w.Write(p)
+ cw.bytes += int64(b)
+ return b, err
+}
+
+// isWhitespace returns true if the byte slice contains only
+// whitespace characters.
+func isWhitespace(s string) bool {
+ for i := 0; i < len(s); i++ {
+ if c := s[i]; c != ' ' && c != '\t' && c != '\n' && c != '\r' {
+ return false
+ }
+ }
+ return true
+}
+
+// spaceMatch returns true if namespace a is the empty string
+// or if namespace a equals namespace b.
+func spaceMatch(a, b string) bool {
+ switch {
+ case a == "":
+ return true
+ default:
+ return a == b
+ }
+}
+
+// spaceDecompose breaks a namespace:tag identifier at the ':'
+// and returns the two parts.
+func spaceDecompose(str string) (space, key string) {
+ colon := strings.IndexByte(str, ':')
+ if colon == -1 {
+ return "", str
+ }
+ return str[:colon], str[colon+1:]
+}
+
+// Strings used by crIndent
+const (
+ crsp = "\n "
+ crtab = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
+)
+
+// crIndent returns a carriage return followed by n copies of the
+// first non-CR character in the source string.
+func crIndent(n int, source string) string {
+ switch {
+ case n < 0:
+ return source[:1]
+ case n < len(source):
+ return source[:n+1]
+ default:
+ return source + strings.Repeat(source[1:2], n-len(source)+1)
+ }
+}
+
+// nextIndex returns the index of the next occurrence of sep in s,
+// starting from offset. It returns -1 if the sep string is not found.
+func nextIndex(s, sep string, offset int) int {
+ switch i := strings.Index(s[offset:], sep); i {
+ case -1:
+ return -1
+ default:
+ return offset + i
+ }
+}
+
+// isInteger returns true if the string s contains an integer.
+func isInteger(s string) bool {
+ for i := 0; i < len(s); i++ {
+ if (s[i] < '0' || s[i] > '9') && !(i == 0 && s[i] == '-') {
+ return false
+ }
+ }
+ return true
+}
diff --git a/vendor/github.com/beevik/etree/path.go b/vendor/github.com/beevik/etree/path.go
new file mode 100644
index 0000000..9cf245e
--- /dev/null
+++ b/vendor/github.com/beevik/etree/path.go
@@ -0,0 +1,516 @@
+// Copyright 2015 Brett Vickers.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package etree
+
+import (
+ "strconv"
+ "strings"
+)
+
+/*
+A Path is an object that represents an optimized version of an
+XPath-like search string. Although path strings are XPath-like,
+only the following limited syntax is supported:
+
+ . Selects the current element
+ .. Selects the parent of the current element
+ * Selects all child elements
+ // Selects all descendants of the current element
+ tag Selects all child elements with the given tag
+ [#] Selects the element of the given index (1-based,
+ negative starts from the end)
+ [@attrib] Selects all elements with the given attribute
+ [@attrib='val'] Selects all elements with the given attribute set to val
+ [tag] Selects all elements with a child element named tag
+ [tag='val'] Selects all elements with a child element named tag
+ and text matching val
+ [text()] Selects all elements with non-empty text
+ [text()='val'] Selects all elements whose text matches val
+
+Examples:
+
+Select the title elements of all descendant book elements having a
+'category' attribute of 'WEB':
+ //book[@category='WEB']/title
+
+Select the first book element with a title child containing the text
+'Great Expectations':
+ .//book[title='Great Expectations'][1]
+
+Starting from the current element, select all children of book elements
+with an attribute 'language' set to 'english':
+ ./book/*[@language='english']
+
+Starting from the current element, select all children of book elements
+containing the text 'special':
+ ./book/*[text()='special']
+
+Select all descendant book elements whose title element has an attribute
+'language' set to 'french':
+ //book/title[@language='french']/..
+
+*/
+type Path struct {
+ segments []segment
+}
+
+// ErrPath is returned by path functions when an invalid etree path is provided.
+type ErrPath string
+
+// Error returns the string describing a path error.
+func (err ErrPath) Error() string {
+ return "etree: " + string(err)
+}
+
+// CompilePath creates an optimized version of an XPath-like string that
+// can be used to query elements in an element tree.
+func CompilePath(path string) (Path, error) {
+ var comp compiler
+ segments := comp.parsePath(path)
+ if comp.err != ErrPath("") {
+ return Path{nil}, comp.err
+ }
+ return Path{segments}, nil
+}
+
+// MustCompilePath creates an optimized version of an XPath-like string that
+// can be used to query elements in an element tree. Panics if an error
+// occurs. Use this function to create Paths when you know the path is
+// valid (i.e., if it's hard-coded).
+func MustCompilePath(path string) Path {
+ p, err := CompilePath(path)
+ if err != nil {
+ panic(err)
+ }
+ return p
+}
+
+// A segment is a portion of a path between "/" characters.
+// It contains one selector and zero or more [filters].
+type segment struct {
+ sel selector
+ filters []filter
+}
+
+func (seg *segment) apply(e *Element, p *pather) {
+ seg.sel.apply(e, p)
+ for _, f := range seg.filters {
+ f.apply(p)
+ }
+}
+
+// A selector selects XML elements for consideration by the
+// path traversal.
+type selector interface {
+ apply(e *Element, p *pather)
+}
+
+// A filter pares down a list of candidate XML elements based
+// on a path filter in [brackets].
+type filter interface {
+ apply(p *pather)
+}
+
+// A pather is helper object that traverses an element tree using
+// a Path object. It collects and deduplicates all elements matching
+// the path query.
+type pather struct {
+ queue fifo
+ results []*Element
+ inResults map[*Element]bool
+ candidates []*Element
+ scratch []*Element // used by filters
+}
+
+// A node represents an element and the remaining path segments that
+// should be applied against it by the pather.
+type node struct {
+ e *Element
+ segments []segment
+}
+
+func newPather() *pather {
+ return &pather{
+ results: make([]*Element, 0),
+ inResults: make(map[*Element]bool),
+ candidates: make([]*Element, 0),
+ scratch: make([]*Element, 0),
+ }
+}
+
+// traverse follows the path from the element e, collecting
+// and then returning all elements that match the path's selectors
+// and filters.
+func (p *pather) traverse(e *Element, path Path) []*Element {
+ for p.queue.add(node{e, path.segments}); p.queue.len() > 0; {
+ p.eval(p.queue.remove().(node))
+ }
+ return p.results
+}
+
+// eval evalutes the current path node by applying the remaining
+// path's selector rules against the node's element.
+func (p *pather) eval(n node) {
+ p.candidates = p.candidates[0:0]
+ seg, remain := n.segments[0], n.segments[1:]
+ seg.apply(n.e, p)
+
+ if len(remain) == 0 {
+ for _, c := range p.candidates {
+ if in := p.inResults[c]; !in {
+ p.inResults[c] = true
+ p.results = append(p.results, c)
+ }
+ }
+ } else {
+ for _, c := range p.candidates {
+ p.queue.add(node{c, remain})
+ }
+ }
+}
+
+// A compiler generates a compiled path from a path string.
+type compiler struct {
+ err ErrPath
+}
+
+// parsePath parses an XPath-like string describing a path
+// through an element tree and returns a slice of segment
+// descriptors.
+func (c *compiler) parsePath(path string) []segment {
+ // If path starts or ends with //, fix it
+ if strings.HasPrefix(path, "//") {
+ path = "." + path
+ }
+ if strings.HasSuffix(path, "//") {
+ path = path + "*"
+ }
+
+ // Paths cannot be absolute
+ if strings.HasPrefix(path, "/") {
+ c.err = ErrPath("paths cannot be absolute.")
+ return nil
+ }
+
+ // Split path into segment objects
+ var segments []segment
+ for _, s := range splitPath(path) {
+ segments = append(segments, c.parseSegment(s))
+ if c.err != ErrPath("") {
+ break
+ }
+ }
+ return segments
+}
+
+func splitPath(path string) []string {
+ pieces := make([]string, 0)
+ start := 0
+ inquote := false
+ for i := 0; i+1 <= len(path); i++ {
+ if path[i] == '\'' {
+ inquote = !inquote
+ } else if path[i] == '/' && !inquote {
+ pieces = append(pieces, path[start:i])
+ start = i + 1
+ }
+ }
+ return append(pieces, path[start:])
+}
+
+// parseSegment parses a path segment between / characters.
+func (c *compiler) parseSegment(path string) segment {
+ pieces := strings.Split(path, "[")
+ seg := segment{
+ sel: c.parseSelector(pieces[0]),
+ filters: make([]filter, 0),
+ }
+ for i := 1; i < len(pieces); i++ {
+ fpath := pieces[i]
+ if fpath[len(fpath)-1] != ']' {
+ c.err = ErrPath("path has invalid filter [brackets].")
+ break
+ }
+ seg.filters = append(seg.filters, c.parseFilter(fpath[:len(fpath)-1]))
+ }
+ return seg
+}
+
+// parseSelector parses a selector at the start of a path segment.
+func (c *compiler) parseSelector(path string) selector {
+ switch path {
+ case ".":
+ return new(selectSelf)
+ case "..":
+ return new(selectParent)
+ case "*":
+ return new(selectChildren)
+ case "":
+ return new(selectDescendants)
+ default:
+ return newSelectChildrenByTag(path)
+ }
+}
+
+// parseFilter parses a path filter contained within [brackets].
+func (c *compiler) parseFilter(path string) filter {
+ if len(path) == 0 {
+ c.err = ErrPath("path contains an empty filter expression.")
+ return nil
+ }
+
+ // Filter contains [@attr='val'], [text()='val'], or [tag='val']?
+ eqindex := strings.Index(path, "='")
+ if eqindex >= 0 {
+ rindex := nextIndex(path, "'", eqindex+2)
+ if rindex != len(path)-1 {
+ c.err = ErrPath("path has mismatched filter quotes.")
+ return nil
+ }
+ switch {
+ case path[0] == '@':
+ return newFilterAttrVal(path[1:eqindex], path[eqindex+2:rindex])
+ case strings.HasPrefix(path, "text()"):
+ return newFilterTextVal(path[eqindex+2 : rindex])
+ default:
+ return newFilterChildText(path[:eqindex], path[eqindex+2:rindex])
+ }
+ }
+
+ // Filter contains [@attr], [N], [tag] or [text()]
+ switch {
+ case path[0] == '@':
+ return newFilterAttr(path[1:])
+ case path == "text()":
+ return newFilterText()
+ case isInteger(path):
+ pos, _ := strconv.Atoi(path)
+ switch {
+ case pos > 0:
+ return newFilterPos(pos - 1)
+ default:
+ return newFilterPos(pos)
+ }
+ default:
+ return newFilterChild(path)
+ }
+}
+
+// selectSelf selects the current element into the candidate list.
+type selectSelf struct{}
+
+func (s *selectSelf) apply(e *Element, p *pather) {
+ p.candidates = append(p.candidates, e)
+}
+
+// selectParent selects the element's parent into the candidate list.
+type selectParent struct{}
+
+func (s *selectParent) apply(e *Element, p *pather) {
+ if e.parent != nil {
+ p.candidates = append(p.candidates, e.parent)
+ }
+}
+
+// selectChildren selects the element's child elements into the
+// candidate list.
+type selectChildren struct{}
+
+func (s *selectChildren) apply(e *Element, p *pather) {
+ for _, c := range e.Child {
+ if c, ok := c.(*Element); ok {
+ p.candidates = append(p.candidates, c)
+ }
+ }
+}
+
+// selectDescendants selects all descendant child elements
+// of the element into the candidate list.
+type selectDescendants struct{}
+
+func (s *selectDescendants) apply(e *Element, p *pather) {
+ var queue fifo
+ for queue.add(e); queue.len() > 0; {
+ e := queue.remove().(*Element)
+ p.candidates = append(p.candidates, e)
+ for _, c := range e.Child {
+ if c, ok := c.(*Element); ok {
+ queue.add(c)
+ }
+ }
+ }
+}
+
+// selectChildrenByTag selects into the candidate list all child
+// elements of the element having the specified tag.
+type selectChildrenByTag struct {
+ space, tag string
+}
+
+func newSelectChildrenByTag(path string) *selectChildrenByTag {
+ s, l := spaceDecompose(path)
+ return &selectChildrenByTag{s, l}
+}
+
+func (s *selectChildrenByTag) apply(e *Element, p *pather) {
+ for _, c := range e.Child {
+ if c, ok := c.(*Element); ok && spaceMatch(s.space, c.Space) && s.tag == c.Tag {
+ p.candidates = append(p.candidates, c)
+ }
+ }
+}
+
+// filterPos filters the candidate list, keeping only the
+// candidate at the specified index.
+type filterPos struct {
+ index int
+}
+
+func newFilterPos(pos int) *filterPos {
+ return &filterPos{pos}
+}
+
+func (f *filterPos) apply(p *pather) {
+ if f.index >= 0 {
+ if f.index < len(p.candidates) {
+ p.scratch = append(p.scratch, p.candidates[f.index])
+ }
+ } else {
+ if -f.index <= len(p.candidates) {
+ p.scratch = append(p.scratch, p.candidates[len(p.candidates)+f.index])
+ }
+ }
+ p.candidates, p.scratch = p.scratch, p.candidates[0:0]
+}
+
+// filterAttr filters the candidate list for elements having
+// the specified attribute.
+type filterAttr struct {
+ space, key string
+}
+
+func newFilterAttr(str string) *filterAttr {
+ s, l := spaceDecompose(str)
+ return &filterAttr{s, l}
+}
+
+func (f *filterAttr) apply(p *pather) {
+ for _, c := range p.candidates {
+ for _, a := range c.Attr {
+ if spaceMatch(f.space, a.Space) && f.key == a.Key {
+ p.scratch = append(p.scratch, c)
+ break
+ }
+ }
+ }
+ p.candidates, p.scratch = p.scratch, p.candidates[0:0]
+}
+
+// filterAttrVal filters the candidate list for elements having
+// the specified attribute with the specified value.
+type filterAttrVal struct {
+ space, key, val string
+}
+
+func newFilterAttrVal(str, value string) *filterAttrVal {
+ s, l := spaceDecompose(str)
+ return &filterAttrVal{s, l, value}
+}
+
+func (f *filterAttrVal) apply(p *pather) {
+ for _, c := range p.candidates {
+ for _, a := range c.Attr {
+ if spaceMatch(f.space, a.Space) && f.key == a.Key && f.val == a.Value {
+ p.scratch = append(p.scratch, c)
+ break
+ }
+ }
+ }
+ p.candidates, p.scratch = p.scratch, p.candidates[0:0]
+}
+
+// filterText filters the candidate list for elements having text.
+type filterText struct{}
+
+func newFilterText() *filterText {
+ return &filterText{}
+}
+
+func (f *filterText) apply(p *pather) {
+ for _, c := range p.candidates {
+ if c.Text() != "" {
+ p.scratch = append(p.scratch, c)
+ }
+ }
+ p.candidates, p.scratch = p.scratch, p.candidates[0:0]
+}
+
+// filterTextVal filters the candidate list for elements having
+// text equal to the specified value.
+type filterTextVal struct {
+ val string
+}
+
+func newFilterTextVal(value string) *filterTextVal {
+ return &filterTextVal{value}
+}
+
+func (f *filterTextVal) apply(p *pather) {
+ for _, c := range p.candidates {
+ if c.Text() == f.val {
+ p.scratch = append(p.scratch, c)
+ }
+ }
+ p.candidates, p.scratch = p.scratch, p.candidates[0:0]
+}
+
+// filterChild filters the candidate list for elements having
+// a child element with the specified tag.
+type filterChild struct {
+ space, tag string
+}
+
+func newFilterChild(str string) *filterChild {
+ s, l := spaceDecompose(str)
+ return &filterChild{s, l}
+}
+
+func (f *filterChild) apply(p *pather) {
+ for _, c := range p.candidates {
+ for _, cc := range c.Child {
+ if cc, ok := cc.(*Element); ok &&
+ spaceMatch(f.space, cc.Space) &&
+ f.tag == cc.Tag {
+ p.scratch = append(p.scratch, c)
+ }
+ }
+ }
+ p.candidates, p.scratch = p.scratch, p.candidates[0:0]
+}
+
+// filterChildText filters the candidate list for elements having
+// a child element with the specified tag and text.
+type filterChildText struct {
+ space, tag, text string
+}
+
+func newFilterChildText(str, text string) *filterChildText {
+ s, l := spaceDecompose(str)
+ return &filterChildText{s, l, text}
+}
+
+func (f *filterChildText) apply(p *pather) {
+ for _, c := range p.candidates {
+ for _, cc := range c.Child {
+ if cc, ok := cc.(*Element); ok &&
+ spaceMatch(f.space, cc.Space) &&
+ f.tag == cc.Tag &&
+ f.text == cc.Text() {
+ p.scratch = append(p.scratch, c)
+ }
+ }
+ }
+ p.candidates, p.scratch = p.scratch, p.candidates[0:0]
+}
diff --git a/vendor/github.com/laurent22/ical-go/ical/calendar.go b/vendor/github.com/laurent22/ical-go/ical/calendar.go
new file mode 100644
index 0000000..b857f58
--- /dev/null
+++ b/vendor/github.com/laurent22/ical-go/ical/calendar.go
@@ -0,0 +1,17 @@
+package ical
+
+type Calendar struct {
+ Items []CalendarEvent
+}
+
+func (this *Calendar) Serialize() string {
+ serializer := calSerializer{
+ calendar: this,
+ buffer: new(strBuffer),
+ }
+ return serializer.serialize()
+}
+
+func (this *Calendar) ToICS() string {
+ return this.Serialize()
+}
diff --git a/vendor/github.com/laurent22/ical-go/ical/calendar_event.go b/vendor/github.com/laurent22/ical-go/ical/calendar_event.go
new file mode 100644
index 0000000..52c9580
--- /dev/null
+++ b/vendor/github.com/laurent22/ical-go/ical/calendar_event.go
@@ -0,0 +1,50 @@
+package ical
+
+import (
+ "time"
+)
+
+type CalendarEvent struct {
+ Id string
+ Summary string
+ Description string
+ Location string
+ CreatedAtUTC *time.Time
+ ModifiedAtUTC *time.Time
+ StartAt *time.Time
+ EndAt *time.Time
+}
+
+func (this *CalendarEvent) StartAtUTC() *time.Time {
+ return inUTC(this.StartAt)
+}
+
+func (this *CalendarEvent) EndAtUTC() *time.Time {
+ return inUTC(this.EndAt)
+}
+
+func (this *CalendarEvent) Serialize() string {
+ buffer := new(strBuffer)
+ return this.serializeWithBuffer(buffer)
+}
+
+func (this *CalendarEvent) ToICS() string {
+ return this.Serialize()
+}
+
+func (this *CalendarEvent) serializeWithBuffer(buffer *strBuffer) string {
+ serializer := calEventSerializer{
+ event: this,
+ buffer: buffer,
+ }
+ return serializer.serialize()
+}
+
+func inUTC(t *time.Time) *time.Time {
+ if t == nil {
+ return nil
+ }
+
+ tUTC := t.UTC()
+ return &tUTC
+}
diff --git a/vendor/github.com/laurent22/ical-go/ical/lib.go b/vendor/github.com/laurent22/ical-go/ical/lib.go
new file mode 100644
index 0000000..4230c09
--- /dev/null
+++ b/vendor/github.com/laurent22/ical-go/ical/lib.go
@@ -0,0 +1,18 @@
+package ical
+
+import (
+ "fmt"
+ "bytes"
+)
+
+type strBuffer struct {
+ buffer bytes.Buffer
+}
+
+func (b *strBuffer) Write(format string, elem ...interface{}) {
+ b.buffer.WriteString(fmt.Sprintf(format, elem...))
+}
+
+func (b *strBuffer) String() string {
+ return b.buffer.String()
+}
diff --git a/vendor/github.com/laurent22/ical-go/ical/node.go b/vendor/github.com/laurent22/ical-go/ical/node.go
new file mode 100644
index 0000000..252fe52
--- /dev/null
+++ b/vendor/github.com/laurent22/ical-go/ical/node.go
@@ -0,0 +1,168 @@
+package ical
+
+import (
+ "time"
+ "regexp"
+ "strconv"
+)
+
+type Node struct {
+ Name string
+ Value string
+ Type int // 1 = Object, 0 = Name/Value
+ Parameters map[string]string
+ Children []*Node
+}
+
+func (this *Node) ChildrenByName(name string) []*Node {
+ var output []*Node
+ for _, child := range this.Children {
+ if child.Name == name {
+ output = append(output, child)
+ }
+ }
+ return output
+}
+
+func (this *Node) ChildByName(name string) *Node {
+ for _, child := range this.Children {
+ if child.Name == name {
+ return child
+ }
+ }
+ return nil
+}
+
+func (this *Node) PropString(name string, defaultValue string) string {
+ for _, child := range this.Children {
+ if child.Name == name {
+ return child.Value
+ }
+ }
+ return defaultValue
+}
+
+func (this *Node) PropDate(name string, defaultValue time.Time) time.Time {
+ node := this.ChildByName(name)
+ if node == nil { return defaultValue }
+ tzid := node.Parameter("TZID", "")
+ var output time.Time
+ var err error
+ if tzid != "" {
+ loc, err := time.LoadLocation(tzid)
+ if err != nil { panic(err) }
+ output, err = time.ParseInLocation("20060102T150405", node.Value, loc)
+ } else {
+ output, err = time.Parse("20060102T150405Z", node.Value)
+ }
+
+ if err != nil { panic(err) }
+ return output
+}
+
+func (this *Node) PropDuration(name string) time.Duration {
+ durStr := this.PropString(name, "")
+
+ if durStr == "" {
+ return time.Duration(0)
+ }
+
+ durRgx := regexp.MustCompile("PT(?:([0-9]+)H)?(?:([0-9]+)M)?(?:([0-9]+)S)?")
+ matches := durRgx.FindStringSubmatch(durStr)
+
+ if len(matches) != 4 {
+ return time.Duration(0)
+ }
+
+ strToDuration := func(value string) time.Duration {
+ d := 0
+ if value != "" {
+ d, _ = strconv.Atoi(value)
+ }
+ return time.Duration(d)
+ }
+
+ hours := strToDuration(matches[1])
+ min := strToDuration(matches[2])
+ sec := strToDuration(matches[3])
+
+ return hours * time.Hour + min * time.Minute + sec * time.Second
+}
+
+func (this *Node) PropInt(name string, defaultValue int) int {
+ n := this.PropString(name, "")
+ if n == "" { return defaultValue }
+ output, err := strconv.Atoi(n)
+ if err != nil { panic(err) }
+ return output
+}
+
+func (this *Node) DigProperty(propPath... string) (string, bool) {
+ return this.dig("prop", propPath...)
+}
+
+func (this *Node) Parameter(name string, defaultValue string) string {
+ if len(this.Parameters) <= 0 { return defaultValue }
+ v, ok := this.Parameters[name]
+ if !ok { return defaultValue }
+ return v
+}
+
+func (this *Node) DigParameter(paramPath... string) (string, bool) {
+ return this.dig("param", paramPath...)
+}
+
+// Digs a value based on a given value path.
+// valueType: can be "param" or "prop".
+// valuePath: the path to access the value.
+// Returns ("", false) when not found or (value, true) when found.
+//
+// Example:
+// dig("param", "VCALENDAR", "VEVENT", "DTEND", "TYPE") -> It will search for "VCALENDAR" node,
+// then a "VEVENT" node, then a "DTEND" note, then finally the "TYPE" param.
+func (this *Node) dig(valueType string, valuePath... string) (string, bool) {
+ current := this
+ lastIndex := len(valuePath) - 1
+ for _, v := range valuePath[:lastIndex] {
+ current = current.ChildByName(v)
+
+ if current == nil {
+ return "", false
+ }
+ }
+
+ target := valuePath[lastIndex]
+
+ value := ""
+ if valueType == "param" {
+ value = current.Parameter(target, "")
+ } else if valueType == "prop" {
+ value = current.PropString(target, "")
+ }
+
+ if value == "" {
+ return "", false
+ }
+
+ return value, true
+}
+
+func (this *Node) String() string {
+ s := ""
+ if this.Type == 1 {
+ s += "===== " + this.Name
+ s += "\n"
+ } else {
+ s += this.Name
+ s += ":" + this.Value
+ s += "\n"
+ }
+ for _, child := range this.Children {
+ s += child.String()
+ }
+ if this.Type == 1 {
+ s += "===== /" + this.Name
+ s += "\n"
+ }
+ return s
+}
diff --git a/vendor/github.com/laurent22/ical-go/ical/parsers.go b/vendor/github.com/laurent22/ical-go/ical/parsers.go
new file mode 100644
index 0000000..5ad3c7b
--- /dev/null
+++ b/vendor/github.com/laurent22/ical-go/ical/parsers.go
@@ -0,0 +1,106 @@
+package ical
+
+import (
+ "log"
+ "errors"
+ "regexp"
+ "strings"
+)
+
+func ParseCalendar(data string) (*Node, error) {
+ r := regexp.MustCompile("([\r|\t| ]*\n[\r|\t| ]*)+")
+ lines := r.Split(strings.TrimSpace(data), -1)
+ node, _, err, _ := parseCalendarNode(lines, 0)
+
+ return node, err
+}
+
+func parseCalendarNode(lines []string, lineIndex int) (*Node, bool, error, int) {
+ line := strings.TrimSpace(lines[lineIndex])
+ _ = log.Println
+ colonIndex := strings.Index(line, ":")
+ if colonIndex <= 0 {
+ return nil, false, errors.New("Invalid value/pair: " + line), lineIndex + 1
+ }
+ name := line[0:colonIndex]
+ splitted := strings.Split(name, ";")
+ var parameters map[string]string
+ if len(splitted) >= 2 {
+ name = splitted[0]
+ parameters = make(map[string]string)
+ for i := 1; i < len(splitted); i++ {
+ p := strings.Split(splitted[i], "=")
+ if len(p) != 2 { panic("Invalid parameter format: " + name) }
+ parameters[p[0]] = p[1]
+ }
+ }
+ value := line[colonIndex+1:len(line)]
+
+ if name == "BEGIN" {
+ node := new(Node)
+ node.Name = value
+ node.Type = 1
+ lineIndex = lineIndex + 1
+ for {
+ child, finished, _, newLineIndex := parseCalendarNode(lines, lineIndex)
+ if finished {
+ return node, false, nil, newLineIndex
+ } else {
+ if child != nil {
+ node.Children = append(node.Children, child)
+ }
+ lineIndex = newLineIndex
+ }
+ }
+ } else if name == "END" {
+ return nil, true, nil, lineIndex + 1
+ } else {
+ node := new(Node)
+ node.Name = name
+ if name == "DESCRIPTION" || name == "SUMMARY" {
+ text, newLineIndex := parseTextType(lines, lineIndex)
+ node.Value = text
+ node.Parameters = parameters
+ return node, false, nil, newLineIndex
+ } else {
+ node.Value = value
+ node.Parameters = parameters
+ return node, false, nil, lineIndex + 1
+ }
+ }
+
+ panic("Unreachable")
+ return nil, false, nil, lineIndex + 1
+}
+
+func parseTextType(lines []string, lineIndex int) (string, int) {
+ line := lines[lineIndex]
+ colonIndex := strings.Index(line, ":")
+ output := strings.TrimSpace(line[colonIndex+1:len(line)])
+ lineIndex++
+ for {
+ line := lines[lineIndex]
+ if line == "" || line[0] != ' ' {
+ return unescapeTextType(output), lineIndex
+ }
+ output += line[1:len(line)]
+ lineIndex++
+ }
+ return unescapeTextType(output), lineIndex
+}
+
+func escapeTextType(input string) string {
+ output := strings.Replace(input, "\\", "\\\\", -1)
+ output = strings.Replace(output, ";", "\\;", -1)
+ output = strings.Replace(output, ",", "\\,", -1)
+ output = strings.Replace(output, "\n", "\\n", -1)
+ return output
+}
+
+func unescapeTextType(s string) string {
+ s = strings.Replace(s, "\\;", ";", -1)
+ s = strings.Replace(s, "\\,", ",", -1)
+ s = strings.Replace(s, "\\n", "\n", -1)
+ s = strings.Replace(s, "\\\\", "\\", -1)
+ return s
+}
diff --git a/vendor/github.com/laurent22/ical-go/ical/properties.go b/vendor/github.com/laurent22/ical-go/ical/properties.go
new file mode 100644
index 0000000..44acf9a
--- /dev/null
+++ b/vendor/github.com/laurent22/ical-go/ical/properties.go
@@ -0,0 +1,9 @@
+package ical
+
+const (
+ VCALENDAR = "VCALENDAR"
+ VEVENT = "VEVENT"
+ DTSTART = "DTSTART"
+ DTEND = "DTEND"
+ DURATION = "DURATION"
+)
diff --git a/vendor/github.com/laurent22/ical-go/ical/serializers.go b/vendor/github.com/laurent22/ical-go/ical/serializers.go
new file mode 100644
index 0000000..12c0070
--- /dev/null
+++ b/vendor/github.com/laurent22/ical-go/ical/serializers.go
@@ -0,0 +1,121 @@
+package ical
+
+import (
+ "time"
+ "strings"
+)
+
+type calSerializer struct {
+ calendar *Calendar
+ buffer *strBuffer
+}
+
+func (this *calSerializer) serialize() string {
+ this.serializeCalendar()
+ return strings.TrimSpace(this.buffer.String())
+}
+
+func (this *calSerializer) serializeCalendar() {
+ this.begin()
+ this.version()
+ this.items()
+ this.end()
+}
+
+func (this *calSerializer) begin() {
+ this.buffer.Write("BEGIN:VCALENDAR\n")
+}
+
+func (this *calSerializer) end() {
+ this.buffer.Write("END:VCALENDAR\n")
+}
+
+func (this *calSerializer) version() {
+ this.buffer.Write("VERSION:2.0\n")
+}
+
+func (this *calSerializer) items() {
+ for _, item := range this.calendar.Items {
+ item.serializeWithBuffer(this.buffer)
+ }
+}
+
+type calEventSerializer struct {
+ event *CalendarEvent
+ buffer *strBuffer
+}
+
+const (
+ eventSerializerTimeFormat = "20060102T150405Z"
+)
+
+func (this *calEventSerializer) serialize() string {
+ this.serializeEvent()
+ return strings.TrimSpace(this.buffer.String())
+}
+
+func (this *calEventSerializer) serializeEvent() {
+ this.begin()
+ this.uid()
+ this.created()
+ this.lastModified()
+ this.dtstart()
+ this.dtend()
+ this.summary()
+ this.description()
+ this.location()
+ this.end()
+}
+
+func (this *calEventSerializer) begin() {
+ this.buffer.Write("BEGIN:VEVENT\n")
+}
+
+func (this *calEventSerializer) end() {
+ this.buffer.Write("END:VEVENT\n")
+}
+
+func (this *calEventSerializer) uid() {
+ this.serializeStringProp("UID", this.event.Id)
+}
+
+func (this *calEventSerializer) summary() {
+ this.serializeStringProp("SUMMARY", this.event.Summary)
+}
+
+func (this *calEventSerializer) description() {
+ this.serializeStringProp("DESCRIPTION", this.event.Description)
+}
+
+func (this *calEventSerializer) location() {
+ this.serializeStringProp("LOCATION", this.event.Location)
+}
+
+func (this *calEventSerializer) dtstart() {
+ this.serializeTimeProp("DTSTART", this.event.StartAtUTC())
+}
+
+func (this *calEventSerializer) dtend() {
+ this.serializeTimeProp("DTEND", this.event.EndAtUTC())
+}
+
+func (this *calEventSerializer) created() {
+ this.serializeTimeProp("CREATED", this.event.CreatedAtUTC)
+}
+
+func (this *calEventSerializer) lastModified() {
+ this.serializeTimeProp("LAST-MODIFIED", this.event.ModifiedAtUTC)
+}
+
+func (this *calEventSerializer) serializeStringProp(name, value string) {
+ if value != "" {
+ escapedValue := escapeTextType(value)
+ this.buffer.Write("%s:%s\n", name, escapedValue)
+ }
+}
+
+func (this *calEventSerializer) serializeTimeProp(name string, value *time.Time) {
+ if value != nil {
+ this.buffer.Write("%s:%s\n", name, value.Format(eventSerializerTimeFormat))
+ }
+}
diff --git a/vendor/github.com/laurent22/ical-go/ical/todo.go b/vendor/github.com/laurent22/ical-go/ical/todo.go
new file mode 100644
index 0000000..4818900
--- /dev/null
+++ b/vendor/github.com/laurent22/ical-go/ical/todo.go
@@ -0,0 +1,94 @@
+package ical
+
+// import (
+// "time"
+// "strconv"
+// "strings"
+// )
+//
+// func TodoFromNode(node *Node) Todo {
+// if node.Name != "VTODO" { panic("Node is not a VTODO") }
+//
+// var todo Todo
+// todo.SetId(node.PropString("UID", ""))
+// todo.SetSummary(node.PropString("SUMMARY", ""))
+// todo.SetDescription(node.PropString("DESCRIPTION", ""))
+// todo.SetDueDate(node.PropDate("DUE", time.Time{}))
+// //todo.SetAlarmDate(this.TimestampBytesToTime(reminderDate))
+// todo.SetCreatedDate(node.PropDate("CREATED", time.Time{}))
+// todo.SetModifiedDate(node.PropDate("DTSTAMP", time.Time{}))
+// todo.SetPriority(node.PropInt("PRIORITY", 0))
+// todo.SetPercentComplete(node.PropInt("PERCENT-COMPLETE", 0))
+// return todo
+// }
+//
+// type Todo struct {
+// CalendarItem
+// dueDate time.Time
+// }
+//
+// func (this *Todo) SetDueDate(v time.Time) { this.dueDate = v }
+// func (this *Todo) DueDate() time.Time { return this.dueDate }
+//
+// func (this *Todo) ICalString(target string) string {
+// s := "BEGIN:VTODO\n"
+//
+// if target == "macTodo" {
+// status := "NEEDS-ACTION"
+// if this.PercentComplete() == 100 {
+// status = "COMPLETED"
+// }
+// s += "STATUS:" + status + "\n"
+// }
+//
+// s += encodeDateProperty("CREATED", this.CreatedDate()) + "\n"
+// s += "UID:" + this.Id() + "\n"
+// s += "SUMMARY:" + escapeTextType(this.Summary()) + "\n"
+// if this.PercentComplete() == 100 && !this.CompletedDate().IsZero() {
+// s += encodeDateProperty("COMPLETED", this.CompletedDate()) + "\n"
+// }
+// s += encodeDateProperty("DTSTAMP", this.ModifiedDate()) + "\n"
+// if this.Priority() != 0 {
+// s += "PRIORITY:" + strconv.Itoa(this.Priority()) + "\n"
+// }
+// if this.PercentComplete() != 0 {
+// s += "PERCENT-COMPLETE:" + strconv.Itoa(this.PercentComplete()) + "\n"
+// }
+// if target == "macTodo" {
+// s += "SEQUENCE:" + strconv.Itoa(this.Sequence()) + "\n"
+// }
+// if this.Description() != "" {
+// s += "DESCRIPTION:" + encodeTextType(this.Description()) + "\n"
+// }
+//
+// s += "END:VTODO\n"
+//
+// return s
+// }
+//
+// func encodeDateProperty(name string, t time.Time) string {
+// var output string
+// zone, _ := t.Zone()
+// if zone != "UTC" && zone != "" {
+// output = ";TZID=" + zone + ":" + t.Format("20060102T150405")
+// } else {
+// output = ":" + t.Format("20060102T150405") + "Z"
+// }
+// return name + output
+// }
+//
+//
+// func encodeTextType(s string) string {
+// output := ""
+// s = escapeTextType(s)
+// lineLength := 0
+// for _, c := range s {
+// if lineLength + len(string(c)) > 75 {
+// output += "\n "
+// lineLength = 1
+// }
+// output += string(c)
+// lineLength += len(string(c))
+// }
+// return output
+// }
diff --git a/vendor/github.com/samedi/caldav-go/.gitignore b/vendor/github.com/samedi/caldav-go/.gitignore
new file mode 100644
index 0000000..7812586
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/.gitignore
@@ -0,0 +1,2 @@
+test-data/
+vendor
diff --git a/vendor/github.com/samedi/caldav-go/CHANGELOG.md b/vendor/github.com/samedi/caldav-go/CHANGELOG.md
new file mode 100644
index 0000000..d336f6b
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/CHANGELOG.md
@@ -0,0 +1,69 @@
+# CHANGELOG
+
+v3.0.0
+-----------
+2017-08-01 Daniel Ferraz
+
+Main change:
+
+Add two ways to get resource from the storage: shallow or not.
+
+`data.GetShallowResource`: means that, if it's collection resource, it will not include its child VEVENTs in the ICS data.
+This is used throughout the palces where the children dont matter.
+
+`data.GetResource`: means that the child VEVENTs will be included in the returned ICS content data for collection resources.
+This is used then sending a GET request to fetch a specific resource and expecting its full ICS data in response.
+
+Other changes:
+
+* Removed the need to pass the useless `writer http.ResponseWriter` parameter when calling the `caldav.HandleRequest` function.
+* Added a `caldav.HandleRequestWithStorage` function that makes it easy to pass a custom storage to be used and handle the request with a single function call.
+
+
+v2.0.0
+-----------
+2017-05-10 Daniel Ferraz
+
+All commits squashed and LICENSE updated to release as OSS in github.
+Feature-wise it remains the same.
+
+
+v1.0.1
+-----------
+2017-01-25 Daniel Ferraz
+
+Escape the contents in `` and `` in the `multistatus` XML responses. Fixing possible bugs
+related to having special characters (e.g. &) in the XML multistatus responses that would possible break the encoding.
+
+v1.0.0
+-----------
+2017-01-18 Daniel Ferraz
+
+Main feature:
+
+* Handles the `Prefer` header on PROPFIND and REPORT requests (defined in this [draft/proposal](https://tools.ietf.org/html/draft-murchison-webdav-prefer-05)). Useful to shrink down possible big and verbose responses when the client demands. Ex: current iOS calendar client uses this feature on its PROPFIND requests.
+
+Other changes:
+
+* Added the `handlers.Response` to allow clients of the lib to interact with the generated response before being written/sent back to the client.
+* Added `GetResourcesByFilters` to the storage interface to allow filtering of resources in the storage level. Useful to provide an already filtered and smaller resource collection to a the REPORT handler when dealing with a filtered REPORT request.
+* Added `GetResourcesByList` to the storage interface to fetch a set a of resources based on a set of paths. Useful to provide, in one call, the correct resource collection to the REPORT handler when dealing with a REPORT request for specific `hrefs`.
+* Remove useless `IsResourcePresent` from the storage interface.
+
+
+v0.1.0
+-----------
+2016-09-23 Daniel Ferraz
+
+This version implements:
+
+* Allow: "GET, HEAD, PUT, DELETE, OPTIONS, PROPFIND, REPORT"
+* DAV: "1, 3, calendar-access"
+* Also only handles the following components: `VCALENDAR`, `VEVENT`
+
+Currently unsupported:
+
+* Components `VTODO`, `VJOURNAL`, `VFREEBUSY`
+* `VEVENT` recurrences
+* Resource locking
+* User authentication
diff --git a/vendor/github.com/samedi/caldav-go/LICENSE b/vendor/github.com/samedi/caldav-go/LICENSE
new file mode 100644
index 0000000..3213aa0
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/LICENSE
@@ -0,0 +1,20 @@
+Copyright 2017 samedi GmbH
+
+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
+NON INFRINGEMENT. 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.
diff --git a/vendor/github.com/samedi/caldav-go/README.md b/vendor/github.com/samedi/caldav-go/README.md
new file mode 100644
index 0000000..973aad0
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/README.md
@@ -0,0 +1,140 @@
+# go CalDAV
+
+This is a Go lib that aims to implement the CalDAV specification ([RFC4791]). It allows the quick implementation of a CalDAV server in Go. Basically, it provides the request handlers that will handle the several CalDAV HTTP requests, fetch the appropriate resources, build and return the responses.
+
+### How to install
+
+```
+go get github.com/samedi/caldav-go
+```
+
+### Dependencies
+
+For dependency management, `glide` is used.
+
+```bash
+# install glide (once!)
+curl https://glide.sh/get | sh
+
+# install dependencies
+glide install
+```
+
+### How to use it
+
+The easiest way to quickly implement a CalDAV server is by just using the lib's request handler. Example:
+
+```go
+package mycaldav
+
+import (
+ "net/http"
+ "github.com/samedi/caldav-go"
+)
+
+func runServer() {
+ http.HandleFunc(PATH, caldav.RequestHandler)
+ http.ListenAndServe(PORT, nil)
+}
+```
+
+With that, all the HTTP requests (GET, PUT, REPORT, PROPFIND, etc) will be handled and responded by the `caldav` handler. In case of any HTTP methods not supported by the lib, a `501 Not Implemented` response will be returned.
+
+In case you want more flexibility to handle the requests, e.g., if you wanted to access the generated response before being sent back to the caller, you could do like:
+
+```go
+package mycaldav
+
+import (
+ "net/http"
+ "github.com/samedi/caldav-go"
+)
+
+func runServer() {
+ http.HandleFunc(PATH, myHandler)
+ http.ListenAndServe(PORT, nil)
+}
+
+func myHandler(writer http.ResponseWriter, request *http.Request) {
+ response := caldav.HandleRequest(writer, request)
+ // ... do something with the `response` ...
+ // the response is written with the current `http.ResponseWriter` and ready to be sent back
+ response.Write(writer)
+}
+```
+
+### Storage & Resources
+
+The storage is where the caldav resources are stored. To interact with that, the caldav lib needs only a type that conforms with the `data.Storage` interface to operate on top of the storage. Basically, this interface defines all the CRUD functions to work on top of the resources. With that, resources can be stored anywhere: in the filesystem, in the cloud, database, etc. As long as the used storage implements all the required storage interface functions, the caldav lib will work fine.
+
+For example, we could use the following dummy storage implementation:
+
+```go
+type DummyStorage struct{
+}
+
+func (d *DummyStorage) GetResources(rpath string, withChildren bool) ([]Resource, error) {
+ return []Resource{}, nil
+}
+
+func (d *DummyStorage) GetResourcesByFilters(rpath string, filters *ResourceFilter) ([]Resource, error) {
+ return []Resource{}, nil
+}
+
+func (d *DummyStorage) GetResourcesByList(rpaths []string) ([]Resource, error) {
+ return []Resource{}, nil
+}
+
+func (d *DummyStorage) GetResource(rpath string) (*Resource, bool, error) {
+ return nil, false, nil
+}
+
+func (d *DummyStorage) GetShallowResource(rpath string) (*Resource, bool, error) {
+ return nil, false, nil
+}
+
+func (d *DummyStorage) CreateResource(rpath, content string) (*Resource, error) {
+ return nil, nil
+}
+
+func (d *DummyStorage) UpdateResource(rpath, content string) (*Resource, error) {
+ return nil, nil
+}
+
+func (d *DummyStorage) DeleteResource(rpath string) error {
+ return nil
+}
+```
+
+Then we just need to tell the caldav lib to use our dummy storage:
+
+```go
+dummyStg := new(DummyStorage)
+caldav.SetupStorage(dummyStg)
+```
+
+All the CRUD operations on resources will then be forwarded to our dummy storage.
+
+The default storage used (if none is explicitly set) is the `data.FileStorage` which deals with resources as files in the File System.
+
+The resources can be of two types: collection and non-collection. A collection resource is basically a resource that has children resources, but does not have any data content. A non-collection resource is a resource that does not have children, but has data. In the case of a file storage, collections correspond to directories and non-collection to plain files. The data of a caldav resource is all the info that shows up in the calendar client, in the [iCalendar](https://en.wikipedia.org/wiki/ICalendar) format.
+
+### Features
+
+Please check the **CHANGELOG** to see specific features that are currently implemented.
+
+### Contributing and testing
+
+Everyone is welcome to contribute. Please raise an issue or pull request accordingly.
+
+To run the tests:
+
+```
+./test.sh
+```
+
+### License
+
+MIT License.
+
+[RFC4791]: https://tools.ietf.org/html/rfc4791
diff --git a/vendor/github.com/samedi/caldav-go/config.go b/vendor/github.com/samedi/caldav-go/config.go
new file mode 100644
index 0000000..8a2d7bc
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/config.go
@@ -0,0 +1,14 @@
+package caldav
+
+import (
+ "github.com/samedi/caldav-go/data"
+ "github.com/samedi/caldav-go/global"
+)
+
+func SetupStorage(stg data.Storage) {
+ global.Storage = stg
+}
+
+func SetupUser(username string) {
+ global.User = &data.CalUser{username}
+}
diff --git a/vendor/github.com/samedi/caldav-go/data/filters.go b/vendor/github.com/samedi/caldav-go/data/filters.go
new file mode 100644
index 0000000..cf0ec62
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/data/filters.go
@@ -0,0 +1,362 @@
+package data
+
+import (
+ "log"
+ "time"
+ "strings"
+ "errors"
+ "github.com/beevik/etree"
+
+ "github.com/samedi/caldav-go/lib"
+)
+
+// ================ FILTERS ==================
+// Filters are a set of rules used to retrieve a range of resources. It is used primarily
+// on REPORT requests and is described in details here (RFC4791#7.8).
+
+const (
+ TAG_FILTER = "filter"
+ TAG_COMP_FILTER = "comp-filter"
+ TAG_PROP_FILTER = "prop-filter"
+ TAG_PARAM_FILTER = "param-filter"
+ TAG_TIME_RANGE = "time-range"
+ TAG_TEXT_MATCH = "text-match"
+ TAG_IS_NOT_DEFINED = "is-not-defined"
+
+ // from the RFC, the time range `start` and `end` attributes MUST be in UTC and in this specific format
+ FILTER_TIME_FORMAT = "20060102T150405Z"
+)
+
+type ResourceFilter struct {
+ name string
+ text string
+ attrs map[string]string
+ children []ResourceFilter // collection of child filters.
+ etreeElem *etree.Element // holds the parsed XML node/tag as an `etree` element.
+}
+
+// This function creates a new filter object from a piece of XML string.
+func ParseResourceFilters(xml string) (*ResourceFilter, error) {
+ doc := etree.NewDocument()
+ if err := doc.ReadFromString(xml); err != nil {
+ log.Printf("ERROR: Could not parse filter from XML string. XML:\n%s", xml)
+ return new(ResourceFilter), err
+ }
+
+ // Right now we're searching for a tag to initialize the filter struct from it.
+ // It SHOULD be a valid XML CALDAV:filter tag (RFC4791#9.7). We're not checking namespaces yet.
+ // TODO: check for XML namespaces and restrict it to accept only CALDAV:filter tag.
+ elem := doc.FindElement("//" + TAG_FILTER)
+ if elem == nil {
+ log.Printf("WARNING: The filter XML should contain a <%s> element. XML:\n%s", TAG_FILTER, xml)
+ return new(ResourceFilter), errors.New("invalid XML filter")
+ }
+
+ filter := newFilterFromEtreeElem(elem)
+ return &filter, nil
+}
+
+func newFilterFromEtreeElem(elem *etree.Element) ResourceFilter {
+ // init filter from etree element
+ filter := ResourceFilter{
+ name: elem.Tag,
+ text: strings.TrimSpace(elem.Text()),
+ etreeElem: elem,
+ attrs: make(map[string]string),
+ }
+
+ // set attributes
+ for _, attr := range elem.Attr {
+ filter.attrs[attr.Key] = attr.Value
+ }
+
+ return filter
+}
+
+func (f *ResourceFilter) Attr(attrName string) string {
+ return f.attrs[attrName]
+}
+
+func (f *ResourceFilter) TimeAttr(attrName string) *time.Time {
+
+
+ t, err := time.Parse(FILTER_TIME_FORMAT, f.attrs[attrName])
+ if err != nil {
+ return nil
+ }
+
+ return &t
+}
+
+// GetTimeRangeFilter checks if the current filter has a child "time-range" filter and
+// returns it (wrapped in a `ResourceFilter` type). It returns nil if the current filter does
+// not contain any "time-range" filter.
+func (f *ResourceFilter) GetTimeRangeFilter() *ResourceFilter {
+ return f.findChild(TAG_TIME_RANGE, true)
+}
+
+func (f *ResourceFilter) Match(target ResourceInterface) bool {
+ if f.name == TAG_FILTER {
+ return f.rootFilterMatch(target)
+ }
+
+ return false
+}
+
+func (f *ResourceFilter) rootFilterMatch(target ResourceInterface) bool {
+ if f.isEmpty() {
+ return false
+ }
+
+ return f.rootChildrenMatch(target)
+}
+
+// checks if all the root's child filters match the target resource
+func (f *ResourceFilter) rootChildrenMatch(target ResourceInterface) bool {
+ scope := []string{}
+
+ for _, child := range f.getChildren() {
+ // root filters only accept comp filters as children
+ if child.name != TAG_COMP_FILTER || !child.compMatch(target, scope) {
+ return false
+ }
+ }
+
+ return true
+}
+
+// See RFC4791-9.7.1.
+func (f *ResourceFilter) compMatch(target ResourceInterface, scope []string) bool {
+ targetComp := target.ComponentName()
+ compName := f.attrs["name"]
+
+ if f.isEmpty() {
+ // Point #1 of RFC4791#9.7.1
+ return compName == targetComp
+ } else if f.contains(TAG_IS_NOT_DEFINED) {
+ // Point #2 of RFC4791#9.7.1
+ return compName != targetComp
+ } else {
+ // check each child of the current filter if they all match.
+ childrenScope := append(scope, compName)
+ return f.compChildrenMatch(target, childrenScope)
+ }
+}
+
+// checks if all the comp's child filters match the target resource
+func (f *ResourceFilter) compChildrenMatch(target ResourceInterface, scope []string) bool {
+ for _, child := range f.getChildren() {
+ var match bool
+
+ switch child.name {
+ case TAG_TIME_RANGE:
+ // Point #3 of RFC4791#9.7.1
+ match = child.timeRangeMatch(target)
+ case TAG_PROP_FILTER:
+ // Point #4 of RFC4791#9.7.1
+ match = child.propMatch(target, scope)
+ case TAG_COMP_FILTER:
+ // Point #4 of RFC4791#9.7.1
+ match = child.compMatch(target, scope)
+ }
+
+ if !match {
+ return false
+ }
+ }
+
+ return true
+}
+
+// See RFC4791-9.9
+func (f *ResourceFilter) timeRangeMatch(target ResourceInterface) bool {
+ startAttr := f.attrs["start"]
+ endAttr := f.attrs["end"]
+
+ // at least one of the two MUST be present
+ if startAttr == "" && endAttr == "" {
+ // if both of them are missing, return false
+ return false
+ } else if startAttr == "" {
+ // if missing only the `start`, set it open ended to the left
+ startAttr = "00010101T000000Z"
+ } else if endAttr == "" {
+ // if missing only the `end`, set it open ended to the right
+ endAttr = "99991231T235959Z"
+ }
+
+ // The logic below is only applicable for VEVENT components. So
+ // we return false if the resource is not a VEVENT component.
+ if target.ComponentName() != lib.VEVENT {
+ return false
+ }
+
+ rangeStart, err := time.Parse(FILTER_TIME_FORMAT, startAttr)
+ if err != nil {
+ log.Printf("ERROR: Could not parse start time in time-range filter.\nError: %s.\nStart attr: %s", err, startAttr)
+ return false
+ }
+
+ rangeEnd, err := time.Parse(FILTER_TIME_FORMAT, endAttr)
+ if err != nil {
+ log.Printf("ERROR: Could not parse end time in time-range filter.\nError: %s.\nEnd attr: %s", err, endAttr)
+ return false
+ }
+
+ // the following logic is inferred from the rules table for VEVENT components,
+ // described in RFC4791-9.9.
+ overlapRange := func(dtStart, dtEnd, rangeStart, rangeEnd time.Time) bool {
+ if dtStart.Equal(dtEnd) {
+ // Lines 3 and 4 of the table deal when the DTSTART and DTEND dates are equals.
+ // In this case we use the rule: (start <= DTSTART && end > DTSTART)
+ return (rangeStart.Before(dtStart) || rangeStart.Equal(dtStart)) && rangeEnd.After(dtStart)
+ } else {
+ // Lines 1, 2 and 6 of the table deal when the DTSTART and DTEND dates are different.
+ // In this case we use the rule: (start < DTEND && end > DTSTART)
+ return rangeStart.Before(dtEnd) && rangeEnd.After(dtStart)
+ }
+ }
+
+ // first we check each of the target recurrences (if any).
+ for _, recurrence := range target.Recurrences() {
+ // if any of them overlap the filter range, we return true right away
+ if overlapRange(recurrence.StartTime, recurrence.EndTime, rangeStart, rangeEnd) {
+ return true
+ }
+ }
+
+ // if none of the recurrences match, we just return if the actual
+ // resource's `start` and `end` times match the filter range
+ return overlapRange(target.StartTimeUTC(), target.EndTimeUTC(), rangeStart, rangeEnd)
+}
+
+// See RFC4791-9.7.2.
+func (f *ResourceFilter) propMatch(target ResourceInterface, scope []string) bool {
+ propName := f.attrs["name"]
+ propPath := append(scope, propName)
+
+ if f.isEmpty() {
+ // Point #1 of RFC4791#9.7.2
+ return target.HasProperty(propPath...)
+ } else if f.contains(TAG_IS_NOT_DEFINED) {
+ // Point #2 of RFC4791#9.7.2
+ return !target.HasProperty(propPath...)
+ } else {
+ // check each child of the current filter if they all match.
+ return f.propChildrenMatch(target, propPath)
+ }
+}
+
+// checks if all the prop's child filters match the target resource
+func (f *ResourceFilter) propChildrenMatch(target ResourceInterface, propPath []string) bool {
+ for _, child := range f.getChildren() {
+ var match bool
+
+ switch child.name {
+ case TAG_TIME_RANGE:
+ // Point #3 of RFC4791#9.7.2
+ // TODO: this point is not very clear on how to match time range against properties.
+ // So we're returning `false` in the meantime.
+ match = false
+ case TAG_TEXT_MATCH:
+ // Point #4 of RFC4791#9.7.2
+ propText := target.GetPropertyValue(propPath...)
+ match = child.textMatch(propText)
+ case TAG_PARAM_FILTER:
+ // Point #4 of RFC4791#9.7.2
+ match = child.paramMatch(target, propPath)
+ }
+
+ if !match {
+ return false
+ }
+ }
+
+ return true
+}
+
+// See RFC4791-9.7.3
+func (f *ResourceFilter) paramMatch(target ResourceInterface, parentPropPath []string) bool {
+ paramName := f.attrs["name"]
+ paramPath := append(parentPropPath, paramName)
+
+ if f.isEmpty() {
+ // Point #1 of RFC4791#9.7.3
+ return target.HasPropertyParam(paramPath...)
+ } else if f.contains(TAG_IS_NOT_DEFINED) {
+ // Point #2 of RFC4791#9.7.3
+ return !target.HasPropertyParam(paramPath...)
+ } else {
+ child := f.getChildren()[0]
+ // param filters can also have (only-one) nested text-match filter
+ if child.name == TAG_TEXT_MATCH {
+ paramValue := target.GetPropertyParamValue(paramPath...)
+ return child.textMatch(paramValue)
+ }
+ }
+
+ return false
+}
+
+// See RFC4791-9.7.5
+func (f *ResourceFilter) textMatch(targetText string) bool {
+ // TODO: collations are not being considered/supported yet.
+ // Texts are lowered to be case-insensitive, almost as the "i;ascii-casemap" value.
+
+ targetText = strings.ToLower(targetText)
+ expectedSubstr := strings.ToLower(f.text)
+
+ match := strings.Contains(targetText, expectedSubstr)
+
+ if f.attrs["negate-condition"] == "yes" {
+ return !match
+ }
+
+ return match
+}
+
+func (f *ResourceFilter) isEmpty() bool {
+ return len(f.getChildren()) == 0 && f.text == ""
+}
+
+func (f *ResourceFilter) contains(filterName string) bool {
+ if f.findChild(filterName, false) != nil {
+ return true
+ }
+
+ return false
+}
+
+func (f *ResourceFilter) findChild(filterName string, dig bool) *ResourceFilter {
+ for _, child := range f.getChildren() {
+ if child.name == filterName {
+ return &child
+ }
+
+ if !dig {
+ continue
+ }
+
+ dugChild := child.findChild(filterName, true)
+
+ if dugChild != nil {
+ return dugChild
+ }
+ }
+
+ return nil
+}
+
+// lazy evaluation of the child filters
+func (f *ResourceFilter) getChildren() []ResourceFilter {
+ if f.children == nil {
+ f.children = []ResourceFilter{}
+
+ for _, childElem := range f.etreeElem.ChildElements() {
+ childFilter := newFilterFromEtreeElem(childElem)
+ f.children = append(f.children, childFilter)
+ }
+ }
+
+ return f.children
+}
diff --git a/vendor/github.com/samedi/caldav-go/data/resource.go b/vendor/github.com/samedi/caldav-go/data/resource.go
new file mode 100644
index 0000000..541bb23
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/data/resource.go
@@ -0,0 +1,277 @@
+package data
+
+import (
+ "os"
+ "fmt"
+ "log"
+ "time"
+ "strings"
+ "strconv"
+ "io/ioutil"
+
+ "github.com/laurent22/ical-go/ical"
+
+ "github.com/samedi/caldav-go/lib"
+ "github.com/samedi/caldav-go/files"
+)
+
+type ResourceInterface interface {
+ ComponentName() string
+ StartTimeUTC() time.Time
+ EndTimeUTC() time.Time
+ Recurrences() []ResourceRecurrence
+ HasProperty(propPath... string) bool
+ GetPropertyValue(propPath... string) string
+ HasPropertyParam(paramName... string) bool
+ GetPropertyParamValue(paramName... string) string
+}
+
+type ResourceAdapter interface {
+ IsCollection() bool
+ CalculateEtag() string
+ GetContent() string
+ GetContentSize() int64
+ GetModTime() time.Time
+}
+
+type ResourceRecurrence struct {
+ StartTime time.Time
+ EndTime time.Time
+}
+
+type Resource struct {
+ Name string
+ Path string
+
+ pathSplit []string
+ adapter ResourceAdapter
+
+ emptyTime time.Time
+}
+
+func NewResource(resPath string, adp ResourceAdapter) Resource {
+ pClean := lib.ToSlashPath(resPath)
+ pSplit := strings.Split(strings.Trim(pClean, "/"), "/")
+
+ return Resource {
+ Name: pSplit[len(pSplit) - 1],
+ Path: pClean,
+ pathSplit: pSplit,
+ adapter: adp,
+ }
+}
+
+func (r *Resource) IsCollection() bool {
+ return r.adapter.IsCollection()
+}
+
+func (r *Resource) IsPrincipal() bool {
+ return len(r.pathSplit) <= 1
+}
+
+func (r *Resource) ComponentName() string {
+ if r.IsCollection() {
+ return lib.VCALENDAR
+ } else {
+ return lib.VEVENT
+ }
+}
+
+func (r *Resource) StartTimeUTC() time.Time {
+ vevent := r.icalVEVENT()
+ dtstart := vevent.PropDate(ical.DTSTART, r.emptyTime)
+
+ if dtstart == r.emptyTime {
+ log.Printf("WARNING: The property DTSTART was not found in the resource's ical data.\nResource path: %s", r.Path)
+ return r.emptyTime
+ }
+
+ return dtstart.UTC()
+}
+
+func (r *Resource) EndTimeUTC() time.Time {
+ vevent := r.icalVEVENT()
+ dtend := vevent.PropDate(ical.DTEND, r.emptyTime)
+
+ // when the DTEND property is not present, we just add the DURATION (if any) to the DTSTART
+ if dtend == r.emptyTime {
+ duration := vevent.PropDuration(ical.DURATION)
+ dtend = r.StartTimeUTC().Add(duration)
+ }
+
+ return dtend.UTC()
+}
+
+func (r *Resource) Recurrences() []ResourceRecurrence {
+ // TODO: Implement. This server does not support ical recurrences yet. We just return an empty array.
+ return []ResourceRecurrence{}
+}
+
+func (r *Resource) HasProperty(propPath... string) bool {
+ return r.GetPropertyValue(propPath...) != ""
+}
+
+func (r *Resource) GetPropertyValue(propPath... string) string {
+ if propPath[0] == ical.VCALENDAR {
+ propPath = propPath[1:]
+ }
+
+ prop, _ := r.icalendar().DigProperty(propPath...)
+ return prop
+}
+
+func (r *Resource) HasPropertyParam(paramPath... string) bool {
+ return r.GetPropertyParamValue(paramPath...) != ""
+}
+
+func (r *Resource) GetPropertyParamValue(paramPath... string) string {
+ if paramPath[0] == ical.VCALENDAR {
+ paramPath = paramPath[1:]
+ }
+
+ param, _ := r.icalendar().DigParameter(paramPath...)
+ return param
+}
+
+func (r *Resource) GetEtag() (string, bool) {
+ if r.IsCollection() {
+ return "", false
+ }
+
+ return r.adapter.CalculateEtag(), true
+}
+
+func (r *Resource) GetContentType() (string, bool) {
+ if r.IsCollection() {
+ return "text/calendar", true
+ } else {
+ return "text/calendar; component=vcalendar", true
+ }
+}
+
+func (r *Resource) GetDisplayName() (string, bool) {
+ return r.Name, true
+}
+
+func (r *Resource) GetContentData() (string, bool) {
+ data := r.adapter.GetContent()
+ found := data != ""
+
+ return data, found
+}
+
+func (r *Resource) GetContentLength() (string, bool) {
+ // If its collection, it does not have any content, so mark it as not found
+ if r.IsCollection() {
+ return "", false
+ }
+
+ contentSize := r.adapter.GetContentSize()
+ return strconv.FormatInt(contentSize, 10), true
+}
+
+func (r *Resource) GetLastModified(format string) (string, bool) {
+ return r.adapter.GetModTime().Format(format), true
+}
+
+func (r *Resource) GetOwner() (string, bool) {
+ var owner string
+ if len(r.pathSplit) > 1 {
+ owner = r.pathSplit[0]
+ } else {
+ owner = ""
+ }
+
+ return owner, true
+}
+
+func (r *Resource) GetOwnerPath() (string, bool) {
+ owner, _ := r.GetOwner()
+
+ if owner != "" {
+ return fmt.Sprintf("/%s/", owner), true
+ } else {
+ return "", false
+ }
+}
+
+// TODO: memoize
+func (r *Resource) icalVEVENT() *ical.Node {
+ vevent := r.icalendar().ChildByName(ical.VEVENT)
+
+ // if nil, log it and return an empty vevent
+ if vevent == nil {
+ log.Printf("WARNING: The resource's ical data is missing the VEVENT property.\nResource path: %s", r.Path)
+
+ return &ical.Node{
+ Name: ical.VEVENT,
+ }
+ }
+
+ return vevent
+}
+
+// TODO: memoize
+func (r *Resource) icalendar() *ical.Node {
+ data, found := r.GetContentData()
+
+ if !found {
+ log.Printf("WARNING: The resource's ical data does not have any data.\nResource path: %s", r.Path)
+ return &ical.Node{
+ Name: ical.VCALENDAR,
+ }
+ }
+
+ icalNode, err := ical.ParseCalendar(data)
+ if err != nil {
+ log.Printf("ERROR: Could not parse the resource's ical data.\nError: %s.\nResource path: %s", err, r.Path)
+ return &ical.Node{
+ Name: ical.VCALENDAR,
+ }
+ }
+
+ return icalNode
+}
+
+type FileResourceAdapter struct {
+ finfo os.FileInfo
+ resourcePath string
+}
+
+func (adp *FileResourceAdapter) IsCollection() bool {
+ return adp.finfo.IsDir()
+}
+
+func (adp *FileResourceAdapter) GetContent() string {
+ if adp.IsCollection() {
+ return ""
+ }
+
+ data, err := ioutil.ReadFile(files.AbsPath(adp.resourcePath))
+ if err != nil {
+ log.Printf("ERROR: Could not read file content for the resource.\nError: %s.\nResource path: %s.", err, adp.resourcePath)
+ return ""
+ }
+
+ return string(data)
+}
+
+func (adp *FileResourceAdapter) GetContentSize() int64 {
+ return adp.finfo.Size()
+}
+
+func (adp *FileResourceAdapter) CalculateEtag() string {
+ // returns ETag as the concatenated hex values of a file's
+ // modification time and size. This is not a reliable synchronization
+ // mechanism for directories, so for collections we return empty.
+ if adp.IsCollection() {
+ return ""
+ }
+
+ fi := adp.finfo
+ return fmt.Sprintf(`"%x%x"`, fi.ModTime().UnixNano(), fi.Size())
+}
+
+func (adp *FileResourceAdapter) GetModTime() time.Time {
+ return adp.finfo.ModTime()
+}
diff --git a/vendor/github.com/samedi/caldav-go/data/storage.go b/vendor/github.com/samedi/caldav-go/data/storage.go
new file mode 100644
index 0000000..c4e40d8
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/data/storage.go
@@ -0,0 +1,217 @@
+package data
+
+import (
+ "os"
+ "log"
+ "io/ioutil"
+ "github.com/samedi/caldav-go/errs"
+ "github.com/samedi/caldav-go/files"
+)
+
+// The Storage is the responsible for the CRUD operations on the caldav resources.
+type Storage interface {
+ // GetResources gets a list of resources based on a given `rpath`. The
+ // `rpath` is the path to the original resource that's being requested. The resultant list
+ // will/must contain that original resource in it, apart from any additional resources. It also receives
+ // `withChildren` flag to say if the result must also include all the original resource`s
+ // children (if original is a collection resource). If `true`, the result will have the requested resource + children.
+ // If `false`, it will have only the requested original resource (from the `rpath` path).
+ // It returns errors if anything went wrong or if it could not find any resource on `rpath` path.
+ GetResources(rpath string, withChildren bool) ([]Resource, error)
+ // GetResourcesByList fetches a list of resources by path from the storage.
+ // This method fetches all the `rpaths` and return an array of the reosurces found.
+ // No error 404 will be returned if one of the resources cannot be found.
+ // Errors are returned if any errors other than "not found" happens.
+ GetResourcesByList(rpaths []string) ([]Resource, error)
+ // GetResourcesByFilters returns the filtered children of a target collection resource.
+ // The target collection resource is the one pointed by the `rpath` parameter. All of its children
+ // will be checked against a set of `filters` and the matching ones are returned. The results
+ // contains only the filtered children and does NOT include the target resource. If the target resource
+ // is not a collection, an empty array is returned as the result.
+ GetResourcesByFilters(rpath string, filters *ResourceFilter) ([]Resource, error)
+ // GetResource gets the requested resource based on a given `rpath` path. It returns the resource (if found) or
+ // nil (if not found). Also returns a flag specifying if the resource was found or not.
+ GetResource(rpath string) (*Resource, bool, error)
+ // GetShallowResource has the same behaviour of `storage.GetResource`. The only difference is that, for collection resources,
+ // it does not return its children in the collection `storage.Resource` struct (hence the name shallow). The motive is
+ // for optimizations reasons, as this function is used on places where the collection's children are not important.
+ GetShallowResource(rpath string) (*Resource, bool, error)
+ // CreateResource creates a new resource on the `rpath` path with a given `content`.
+ CreateResource(rpath, content string) (*Resource, error)
+ // UpdateResource udpates a resource on the `rpath` path with a given `content`.
+ UpdateResource(rpath, content string) (*Resource, error)
+ // DeleteResource deletes a resource on the `rpath` path.
+ DeleteResource(rpath string) error
+}
+
+// FileStorage is the storage that deals with resources as files in the file system. So, a collection resource
+// is treated as a folder/directory and its children resources are the files it contains. On the other hand, non-collection
+// resources are just plain files.
+type FileStorage struct {
+}
+
+func (fs *FileStorage) GetResources(rpath string, withChildren bool) ([]Resource, error) {
+ result := []Resource{}
+
+ // tries to open the file by the given path
+ f, e := fs.openResourceFile(rpath, os.O_RDONLY)
+ if e != nil {
+ return nil, e
+ }
+
+ // add it as a resource to the result list
+ finfo, _ := f.Stat()
+ resource := NewResource(rpath, &FileResourceAdapter{finfo, rpath})
+ result = append(result, resource)
+
+ // if the file is a dir, add its children to the result list
+ if withChildren && finfo.IsDir() {
+ dirFiles, _ := f.Readdir(0)
+ for _, finfo := range dirFiles {
+ childPath := files.JoinPaths(rpath, finfo.Name())
+ resource = NewResource(childPath, &FileResourceAdapter{finfo, childPath})
+ result = append(result, resource)
+ }
+ }
+
+ return result, nil
+}
+
+func (fs *FileStorage) GetResourcesByFilters(rpath string, filters *ResourceFilter) ([]Resource, error) {
+ result := []Resource{}
+
+ childPaths := fs.getDirectoryChildPaths(rpath)
+ for _, path := range childPaths {
+ resource, _, err := fs.GetShallowResource(path)
+
+ if err != nil {
+ // if we can't find this resource, something weird went wrong, but not that serious, so we log it and continue
+ log.Printf("WARNING: returned error when trying to get resource with path %s from collection with path %s. Error: %s", path, rpath, err)
+ continue
+ }
+
+ // only add it if the resource matches the filters
+ if filters == nil || filters.Match(resource) {
+ result = append(result, *resource)
+ }
+ }
+
+ return result, nil
+}
+
+func (fs *FileStorage) GetResourcesByList(rpaths []string) ([]Resource, error) {
+ results := []Resource{}
+
+ for _, rpath := range rpaths {
+ resource, found, err := fs.GetShallowResource(rpath)
+
+ if err != nil && err != errs.ResourceNotFoundError {
+ return nil, err
+ }
+
+ if found {
+ results = append(results, *resource)
+ }
+ }
+
+ return results, nil
+}
+
+func (fs *FileStorage) GetResource(rpath string) (*Resource, bool, error) {
+ // For simplicity we just return the shallow resource.
+ return fs.GetShallowResource(rpath)
+}
+
+func (fs *FileStorage) GetShallowResource(rpath string) (*Resource, bool, error) {
+ resources, err := fs.GetResources(rpath, false)
+
+ if err != nil {
+ return nil, false, err
+ }
+
+ if resources == nil || len(resources) == 0 {
+ return nil, false, errs.ResourceNotFoundError
+ }
+
+ res := resources[0]
+ return &res, true, nil
+}
+
+func (fs *FileStorage) CreateResource(rpath, content string) (*Resource, error) {
+ rAbsPath := files.AbsPath(rpath)
+
+ if fs.isResourcePresent(rAbsPath) {
+ return nil, errs.ResourceAlreadyExistsError
+ }
+
+ // create parent directories (if needed)
+ if err := os.MkdirAll(files.DirPath(rAbsPath), os.ModePerm); err != nil {
+ return nil, err
+ }
+
+ // create file/resource and write content
+ f, err := os.Create(rAbsPath)
+ if err != nil {
+ return nil, err
+ }
+ f.WriteString(content)
+
+ finfo, _ := f.Stat()
+ res := NewResource(rpath, &FileResourceAdapter{finfo, rpath})
+ return &res, nil
+}
+
+func (fs *FileStorage) UpdateResource(rpath, content string) (*Resource, error) {
+ f, e := fs.openResourceFile(rpath, os.O_RDWR)
+ if e != nil {
+ return nil, e
+ }
+
+ // update content
+ f.Truncate(0)
+ f.WriteString(content)
+
+ finfo, _ := f.Stat()
+ res := NewResource(rpath, &FileResourceAdapter{finfo, rpath})
+ return &res, nil
+}
+
+func (fs *FileStorage) DeleteResource(rpath string) error {
+ err := os.Remove(files.AbsPath(rpath))
+
+ return err
+}
+
+func (fs *FileStorage) isResourcePresent(rpath string) bool {
+ _, found, _ := fs.GetShallowResource(rpath)
+
+ return found
+}
+
+func (fs *FileStorage) openResourceFile(filepath string, mode int) (*os.File, error) {
+ f, e := os.OpenFile(files.AbsPath(filepath), mode, 0666)
+ if e != nil {
+ if os.IsNotExist(e) {
+ return nil, errs.ResourceNotFoundError
+ }
+ return nil, e
+ }
+
+ return f, nil
+}
+
+func (fs *FileStorage) getDirectoryChildPaths(dirpath string) []string {
+ content, err := ioutil.ReadDir(files.AbsPath(dirpath))
+ if err != nil {
+ log.Printf("ERROR: Could not read resource as file directory.\nError: %s.\nResource path: %s.", err, dirpath)
+ return nil
+ }
+
+ result := []string{}
+ for _, file := range content {
+ fpath := files.JoinPaths(dirpath, file.Name())
+ result = append(result, fpath)
+ }
+
+ return result
+}
diff --git a/vendor/github.com/samedi/caldav-go/data/user.go b/vendor/github.com/samedi/caldav-go/data/user.go
new file mode 100644
index 0000000..5ceba9a
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/data/user.go
@@ -0,0 +1,5 @@
+package data
+
+type CalUser struct {
+ Name string
+}
diff --git a/vendor/github.com/samedi/caldav-go/errs/errors.go b/vendor/github.com/samedi/caldav-go/errs/errors.go
new file mode 100644
index 0000000..bc1fa96
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/errs/errors.go
@@ -0,0 +1,12 @@
+package errs
+
+import (
+ "errors"
+)
+
+var (
+ ResourceNotFoundError = errors.New("caldav: resource not found")
+ ResourceAlreadyExistsError = errors.New("caldav: resource already exists")
+ UnauthorizedError = errors.New("caldav: unauthorized. credentials needed.")
+ ForbiddenError = errors.New("caldav: forbidden operation.")
+)
diff --git a/vendor/github.com/samedi/caldav-go/files/paths.go b/vendor/github.com/samedi/caldav-go/files/paths.go
new file mode 100644
index 0000000..4e41c53
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/files/paths.go
@@ -0,0 +1,30 @@
+package files
+
+import (
+ "strings"
+ "path/filepath"
+ "github.com/samedi/caldav-go/lib"
+)
+
+const (
+ Separator = string(filepath.Separator)
+)
+
+func AbsPath(path string) string {
+ path = strings.Trim(path, "/")
+ absPath, _ := filepath.Abs(path)
+
+ return absPath
+}
+
+func DirPath(path string) string {
+ return filepath.Dir(path)
+}
+
+func JoinPaths(paths ...string) string {
+ return filepath.Join(paths...)
+}
+
+func ToSlashPath(path string) string {
+ return lib.ToSlashPath(path)
+}
diff --git a/vendor/github.com/samedi/caldav-go/glide.lock b/vendor/github.com/samedi/caldav-go/glide.lock
new file mode 100644
index 0000000..7243eb5
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/glide.lock
@@ -0,0 +1,10 @@
+hash: 2796726e69757f4af1a13f6ebd056ebc626d712051aa213875bb03f5bdc1ebfd
+updated: 2017-01-18T11:43:23.127761353+01:00
+imports:
+- name: github.com/beevik/etree
+ version: 4cd0dd976db869f817248477718071a28e978df0
+- name: github.com/laurent22/ical-go
+ version: 4811ac5553eae5fed7cd5d7a9024727f1311b2a2
+ subpackages:
+ - ical
+testImports: []
diff --git a/vendor/github.com/samedi/caldav-go/glide.yaml b/vendor/github.com/samedi/caldav-go/glide.yaml
new file mode 100644
index 0000000..18b4ab1
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/glide.yaml
@@ -0,0 +1,7 @@
+package: github.com/samedi/caldav-go
+import:
+- package: github.com/beevik/etree
+- package: github.com/laurent22/ical-go
+ version: ~0.1.0
+ subpackages:
+ - ical
diff --git a/vendor/github.com/samedi/caldav-go/global/global.go b/vendor/github.com/samedi/caldav-go/global/global.go
new file mode 100644
index 0000000..9e1d930
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/global/global.go
@@ -0,0 +1,12 @@
+package global
+
+// This file defines accessible variables used to setup the caldav server.
+
+import (
+ "github.com/samedi/caldav-go/data"
+)
+
+// The global storage used in the CRUD operations of resources. Default storage is the `FileStorage`.
+var Storage data.Storage = new(data.FileStorage)
+// Current caldav user. It is used to keep the info of the current user that is interacting with the calendar.
+var User *data.CalUser
diff --git a/vendor/github.com/samedi/caldav-go/handler.go b/vendor/github.com/samedi/caldav-go/handler.go
new file mode 100644
index 0000000..a1945b5
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/handler.go
@@ -0,0 +1,28 @@
+package caldav
+
+import (
+ "net/http"
+ "github.com/samedi/caldav-go/data"
+ "github.com/samedi/caldav-go/handlers"
+)
+
+// RequestHandler handles the given CALDAV request and writes the reponse righ away. This function is to be
+// used by passing it directly as the handle func to the `http` lib. Example: http.HandleFunc("/", caldav.RequestHandler).
+func RequestHandler(writer http.ResponseWriter, request *http.Request) {
+ response := HandleRequest(request)
+ response.Write(writer)
+}
+
+// HandleRequest handles the given CALDAV request and returns the response. Useful when the caller
+// wants to do something else with the response before writing it to the response stream.
+func HandleRequest(request *http.Request) *handlers.Response {
+ handler := handlers.NewHandler(request)
+ return handler.Handle()
+}
+
+// HandleRequestWithStorage handles the request the same way as `HandleRequest` does, but before,
+// it sets the given storage that will be used throughout the request handling flow.
+func HandleRequestWithStorage(request *http.Request, stg data.Storage) *handlers.Response {
+ SetupStorage(stg)
+ return HandleRequest(request)
+}
diff --git a/vendor/github.com/samedi/caldav-go/handlers/builder.go b/vendor/github.com/samedi/caldav-go/handlers/builder.go
new file mode 100644
index 0000000..0d99f97
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/handlers/builder.go
@@ -0,0 +1,24 @@
+package handlers
+
+import (
+ "net/http"
+)
+
+type handlerInterface interface {
+ Handle() *Response
+}
+
+func NewHandler(request *http.Request) handlerInterface {
+ response := NewResponse()
+
+ switch request.Method {
+ case "GET": return getHandler{request, response, false}
+ case "HEAD": return getHandler{request, response, true}
+ case "PUT": return putHandler{request, response}
+ case "DELETE": return deleteHandler{request, response}
+ case "PROPFIND": return propfindHandler{request, response}
+ case "OPTIONS": return optionsHandler{response}
+ case "REPORT": return reportHandler{request, response}
+ default: return notImplementedHandler{response}
+ }
+}
diff --git a/vendor/github.com/samedi/caldav-go/handlers/delete.go b/vendor/github.com/samedi/caldav-go/handlers/delete.go
new file mode 100644
index 0000000..d66b27a
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/handlers/delete.go
@@ -0,0 +1,40 @@
+package handlers
+
+import (
+ "net/http"
+ "github.com/samedi/caldav-go/global"
+)
+
+type deleteHandler struct {
+ request *http.Request
+ response *Response
+}
+
+func (dh deleteHandler) Handle() *Response {
+ precond := requestPreconditions{dh.request}
+
+ // get the event from the storage
+ resource, _, err := global.Storage.GetShallowResource(dh.request.URL.Path)
+ if err != nil {
+ return dh.response.SetError(err)
+ }
+
+ // TODO: Handle delete on collections
+ if resource.IsCollection() {
+ return dh.response.Set(http.StatusMethodNotAllowed, "")
+ }
+
+ // check ETag pre-condition
+ resourceEtag, _ := resource.GetEtag()
+ if !precond.IfMatch(resourceEtag) {
+ return dh.response.Set(http.StatusPreconditionFailed, "")
+ }
+
+ // delete event after pre-condition passed
+ err = global.Storage.DeleteResource(resource.Path)
+ if err != nil {
+ return dh.response.SetError(err)
+ }
+
+ return dh.response.Set(http.StatusNoContent, "")
+}
diff --git a/vendor/github.com/samedi/caldav-go/handlers/get.go b/vendor/github.com/samedi/caldav-go/handlers/get.go
new file mode 100644
index 0000000..2b42a47
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/handlers/get.go
@@ -0,0 +1,37 @@
+package handlers
+
+import (
+ "net/http"
+ "github.com/samedi/caldav-go/global"
+)
+
+type getHandler struct {
+ request *http.Request
+ response *Response
+ onlyHeaders bool
+}
+
+func (gh getHandler) Handle() *Response {
+ resource, _, err := global.Storage.GetResource(gh.request.URL.Path)
+ if err != nil {
+ return gh.response.SetError(err)
+ }
+
+ var response string
+ if gh.onlyHeaders {
+ response = ""
+ } else {
+ response, _ = resource.GetContentData()
+ }
+
+ etag, _ := resource.GetEtag()
+ lastm, _ := resource.GetLastModified(http.TimeFormat)
+ ctype, _ := resource.GetContentType()
+
+ gh.response.SetHeader("ETag", etag).
+ SetHeader("Last-Modified", lastm).
+ SetHeader("Content-Type", ctype).
+ Set(http.StatusOK, response)
+
+ return gh.response
+}
diff --git a/vendor/github.com/samedi/caldav-go/handlers/headers.go b/vendor/github.com/samedi/caldav-go/handlers/headers.go
new file mode 100644
index 0000000..d7d692a
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/handlers/headers.go
@@ -0,0 +1,27 @@
+package handlers
+
+import (
+ "net/http"
+)
+
+const (
+ HD_DEPTH = "Depth"
+ HD_DEPTH_DEEP = "1"
+ HD_PREFER = "Prefer"
+ HD_PREFER_MINIMAL = "return=minimal"
+ HD_PREFERENCE_APPLIED = "Preference-Applied"
+)
+
+type headers struct {
+ http.Header
+}
+
+func (this headers) IsDeep() bool {
+ depth := this.Get(HD_DEPTH)
+ return (depth == HD_DEPTH_DEEP)
+}
+
+func (this headers) IsMinimal() bool {
+ prefer := this.Get(HD_PREFER)
+ return (prefer == HD_PREFER_MINIMAL)
+}
diff --git a/vendor/github.com/samedi/caldav-go/handlers/multistatus.go b/vendor/github.com/samedi/caldav-go/handlers/multistatus.go
new file mode 100644
index 0000000..038d139
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/handlers/multistatus.go
@@ -0,0 +1,207 @@
+package handlers
+
+import (
+ "fmt"
+ "net/http"
+ "encoding/xml"
+ "github.com/samedi/caldav-go/lib"
+ "github.com/samedi/caldav-go/global"
+ "github.com/samedi/caldav-go/data"
+ "github.com/samedi/caldav-go/ixml"
+)
+
+// Wraps a multistatus response. It contains the set of `Responses`
+// that will serve to build the final XML. Multistatus responses are
+// used by the REPORT and PROPFIND methods.
+type multistatusResp struct {
+ // The set of multistatus responses used to build each of the nodes.
+ Responses []msResponse
+ // Flag that XML should be minimal or not
+ // [defined in the draft https://tools.ietf.org/html/draft-murchison-webdav-prefer-05]
+ Minimal bool
+}
+
+type msResponse struct {
+ Href string
+ Found bool
+ Propstats msPropstats
+}
+
+type msPropstats map[int]msProps
+
+// Adds a msProp to the map with the key being the prop status.
+func (stats msPropstats) Add(prop msProp) {
+ stats[prop.Status] = append(stats[prop.Status], prop)
+}
+
+func (stats msPropstats) Clone() msPropstats {
+ clone := make(msPropstats)
+
+ for k, v := range stats {
+ clone[k] = v
+ }
+
+ return clone
+}
+
+type msProps []msProp
+
+type msProp struct {
+ Tag xml.Name
+ Content string
+ Contents []string
+ Status int
+}
+
+// Function that processes all the required props for a given resource.
+// ## Params
+// resource: the target calendar resource.
+// reqprops: set of required props that must be processed for the resource.
+// ## Returns
+// The set of props (msProp) processed. Each prop is mapped to a HTTP status code.
+// So if a prop is found and processed ok, it'll be mapped to 200. If it's not found,
+// it'll be mapped to 404, and so on.
+func (ms *multistatusResp) Propstats(resource *data.Resource, reqprops []xml.Name) msPropstats {
+ if resource == nil {
+ return nil
+ }
+
+ result := make(msPropstats)
+
+ for _, ptag := range reqprops {
+ pvalue := msProp{
+ Tag: ptag,
+ Status: http.StatusOK,
+ }
+
+ pfound := false
+ switch ptag {
+ case ixml.CALENDAR_DATA_TG:
+ pvalue.Content, pfound = resource.GetContentData()
+ if pfound {
+ pvalue.Content = ixml.EscapeText(pvalue.Content)
+ }
+ case ixml.GET_ETAG_TG:
+ pvalue.Content, pfound = resource.GetEtag()
+ case ixml.GET_CONTENT_TYPE_TG:
+ pvalue.Content, pfound = resource.GetContentType()
+ case ixml.GET_CONTENT_LENGTH_TG:
+ pvalue.Content, pfound = resource.GetContentLength()
+ case ixml.DISPLAY_NAME_TG:
+ pvalue.Content, pfound = resource.GetDisplayName()
+ if pfound {
+ pvalue.Content = ixml.EscapeText(pvalue.Content)
+ }
+ case ixml.GET_LAST_MODIFIED_TG:
+ pvalue.Content, pfound = resource.GetLastModified(http.TimeFormat)
+ case ixml.OWNER_TG:
+ pvalue.Content, pfound = resource.GetOwnerPath()
+ case ixml.GET_CTAG_TG:
+ pvalue.Content, pfound = resource.GetEtag()
+ case ixml.PRINCIPAL_URL_TG,
+ ixml.PRINCIPAL_COLLECTION_SET_TG,
+ ixml.CALENDAR_USER_ADDRESS_SET_TG,
+ ixml.CALENDAR_HOME_SET_TG:
+ pvalue.Content, pfound = ixml.HrefTag(resource.Path), true
+ case ixml.RESOURCE_TYPE_TG:
+ if resource.IsCollection() {
+ pvalue.Content, pfound = ixml.Tag(ixml.COLLECTION_TG, "") + ixml.Tag(ixml.CALENDAR_TG, ""), true
+
+ if resource.IsPrincipal() {
+ pvalue.Content += ixml.Tag(ixml.PRINCIPAL_TG, "")
+ }
+ } else {
+ // resourcetype must be returned empty for non-collection elements
+ pvalue.Content, pfound = "", true
+ }
+ case ixml.CURRENT_USER_PRINCIPAL_TG:
+ if global.User != nil {
+ path := fmt.Sprintf("/%s/", global.User.Name)
+ pvalue.Content, pfound = ixml.HrefTag(path), true
+ }
+ case ixml.SUPPORTED_CALENDAR_COMPONENT_SET_TG:
+ if resource.IsCollection() {
+ for _, component := range supportedComponents {
+ // TODO: use ixml somehow to build the below tag
+ compTag := fmt.Sprintf(``, component)
+ pvalue.Contents = append(pvalue.Contents, compTag)
+ }
+ pfound = true
+ }
+ }
+
+ if !pfound {
+ pvalue.Status = http.StatusNotFound
+ }
+
+ result.Add(pvalue)
+ }
+
+ return result
+}
+
+// Adds a new `msResponse` to the `Responses` array.
+func (ms *multistatusResp) AddResponse(href string, found bool, propstats msPropstats) {
+ ms.Responses = append(ms.Responses, msResponse{
+ Href: href,
+ Found: found,
+ Propstats: propstats,
+ })
+}
+
+func (ms *multistatusResp) ToXML() string {
+ // init multistatus
+ var bf lib.StringBuffer
+ bf.Write(``)
+ bf.Write(``, ixml.Namespaces())
+
+ // iterate over event hrefs and build multistatus XML on the fly
+ for _, response := range ms.Responses {
+ bf.Write("")
+ bf.Write(ixml.HrefTag(response.Href))
+
+ if response.Found {
+ propstats := response.Propstats.Clone()
+
+ if ms.Minimal {
+ delete(propstats, http.StatusNotFound)
+
+ if len(propstats) == 0 {
+ bf.Write("")
+ bf.Write("")
+ bf.Write(ixml.StatusTag(http.StatusOK))
+ bf.Write("")
+ bf.Write("")
+
+ continue
+ }
+ }
+
+ for status, props := range propstats {
+ bf.Write("")
+ bf.Write("")
+ for _, prop := range props {
+ bf.Write(ms.propToXML(prop))
+ }
+ bf.Write("")
+ bf.Write(ixml.StatusTag(status))
+ bf.Write("")
+ }
+ } else {
+ // if does not find the resource set 404
+ bf.Write(ixml.StatusTag(http.StatusNotFound))
+ }
+ bf.Write("")
+ }
+ bf.Write("")
+
+ return bf.String()
+}
+
+func (ms *multistatusResp) propToXML(prop msProp) string {
+ for _, content := range prop.Contents {
+ prop.Content += content
+ }
+ xmlString := ixml.Tag(prop.Tag, prop.Content)
+ return xmlString
+}
diff --git a/vendor/github.com/samedi/caldav-go/handlers/not_implemented.go b/vendor/github.com/samedi/caldav-go/handlers/not_implemented.go
new file mode 100644
index 0000000..8fb4f46
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/handlers/not_implemented.go
@@ -0,0 +1,13 @@
+package handlers
+
+import (
+ "net/http"
+)
+
+type notImplementedHandler struct {
+ response *Response
+}
+
+func (h notImplementedHandler) Handle() *Response {
+ return h.response.Set(http.StatusNotImplemented, "")
+}
diff --git a/vendor/github.com/samedi/caldav-go/handlers/options.go b/vendor/github.com/samedi/caldav-go/handlers/options.go
new file mode 100644
index 0000000..7c4c749
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/handlers/options.go
@@ -0,0 +1,23 @@
+package handlers
+
+import (
+ "net/http"
+)
+
+type optionsHandler struct {
+ response *Response
+}
+
+// Returns the allowed methods and the DAV features implemented by the current server.
+// For more information about the values and format read RFC4918 Sections 10.1 and 18.
+func (oh optionsHandler) Handle() *Response {
+ // Set the DAV compliance header:
+ // 1: Server supports all the requirements specified in RFC2518
+ // 3: Server supports all the revisions specified in RFC4918
+ // calendar-access: Server supports all the extensions specified in RFC4791
+ oh.response.SetHeader("DAV", "1, 3, calendar-access").
+ SetHeader("Allow", "GET, HEAD, PUT, DELETE, OPTIONS, PROPFIND, REPORT").
+ Set(http.StatusOK, "")
+
+ return oh.response
+}
diff --git a/vendor/github.com/samedi/caldav-go/handlers/preconditions.go b/vendor/github.com/samedi/caldav-go/handlers/preconditions.go
new file mode 100644
index 0000000..d104b0a
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/handlers/preconditions.go
@@ -0,0 +1,23 @@
+package handlers
+
+import (
+ "net/http"
+)
+
+type requestPreconditions struct {
+ request *http.Request
+}
+
+func (p *requestPreconditions) IfMatch(etag string) bool {
+ etagMatch := p.request.Header["If-Match"]
+ return len(etagMatch) == 0 || etagMatch[0] == "*" || etagMatch[0] == etag
+}
+
+func (p *requestPreconditions) IfMatchPresent() bool {
+ return len(p.request.Header["If-Match"]) != 0
+}
+
+func (p *requestPreconditions) IfNoneMatch(value string) bool {
+ valueMatch := p.request.Header["If-None-Match"]
+ return len(valueMatch) == 1 && valueMatch[0] == value
+}
diff --git a/vendor/github.com/samedi/caldav-go/handlers/propfind.go b/vendor/github.com/samedi/caldav-go/handlers/propfind.go
new file mode 100644
index 0000000..35fc807
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/handlers/propfind.go
@@ -0,0 +1,49 @@
+package handlers
+
+import (
+ "net/http"
+ "encoding/xml"
+ "github.com/samedi/caldav-go/global"
+)
+
+type propfindHandler struct {
+ request *http.Request
+ response *Response
+}
+
+func (ph propfindHandler) Handle() *Response {
+ requestBody := readRequestBody(ph.request)
+ header := headers{ph.request.Header}
+
+ // get the target resources based on the request URL
+ resources, err := global.Storage.GetResources(ph.request.URL.Path, header.IsDeep())
+ if err != nil {
+ return ph.response.SetError(err)
+ }
+
+ // read body string to xml struct
+ type XMLProp2 struct {
+ Tags []xml.Name `xml:",any"`
+ }
+ type XMLRoot2 struct {
+ XMLName xml.Name
+ Prop XMLProp2 `xml:"DAV: prop"`
+ }
+ var requestXML XMLRoot2
+ xml.Unmarshal([]byte(requestBody), &requestXML)
+
+ multistatus := &multistatusResp{
+ Minimal: header.IsMinimal(),
+ }
+ // for each href, build the multistatus responses
+ for _, resource := range resources {
+ propstats := multistatus.Propstats(&resource, requestXML.Prop.Tags)
+ multistatus.AddResponse(resource.Path, true, propstats)
+ }
+
+ if multistatus.Minimal {
+ ph.response.SetHeader(HD_PREFERENCE_APPLIED, HD_PREFER_MINIMAL)
+ }
+
+ return ph.response.Set(207, multistatus.ToXML())
+}
diff --git a/vendor/github.com/samedi/caldav-go/handlers/put.go b/vendor/github.com/samedi/caldav-go/handlers/put.go
new file mode 100644
index 0000000..68fbc1d
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/handlers/put.go
@@ -0,0 +1,65 @@
+package handlers
+
+import (
+ "net/http"
+ "github.com/samedi/caldav-go/errs"
+ "github.com/samedi/caldav-go/global"
+)
+
+type putHandler struct {
+ request *http.Request
+ response *Response
+}
+
+func (ph putHandler) Handle() *Response {
+ requestBody := readRequestBody(ph.request)
+ precond := requestPreconditions{ph.request}
+ success := false
+
+ // check if resource exists
+ resourcePath := ph.request.URL.Path
+ resource, found, err := global.Storage.GetShallowResource(resourcePath)
+ if err != nil && err != errs.ResourceNotFoundError {
+ return ph.response.SetError(err)
+ }
+
+ // PUT is allowed in 2 cases:
+ //
+ // 1. Item NOT FOUND and there is NO ETAG match header: CREATE a new item
+ if !found && !precond.IfMatchPresent() {
+ // create new event resource
+ resource, err = global.Storage.CreateResource(resourcePath, requestBody)
+ if err != nil {
+ return ph.response.SetError(err)
+ }
+
+ success = true
+ }
+
+ if found {
+ // TODO: Handle PUT on collections
+ if resource.IsCollection() {
+ return ph.response.Set(http.StatusPreconditionFailed, "")
+ }
+
+ // 2. Item exists, the resource etag is verified and there's no IF-NONE-MATCH=* header: UPDATE the item
+ resourceEtag, _ := resource.GetEtag()
+ if found && precond.IfMatch(resourceEtag) && !precond.IfNoneMatch("*") {
+ // update resource
+ resource, err = global.Storage.UpdateResource(resourcePath, requestBody)
+ if err != nil {
+ return ph.response.SetError(err)
+ }
+
+ success = true
+ }
+ }
+
+ if !success {
+ return ph.response.Set(http.StatusPreconditionFailed, "")
+ }
+
+ resourceEtag, _ := resource.GetEtag()
+ return ph.response.SetHeader("ETag", resourceEtag).
+ Set(http.StatusCreated, "")
+}
diff --git a/vendor/github.com/samedi/caldav-go/handlers/report.go b/vendor/github.com/samedi/caldav-go/handlers/report.go
new file mode 100644
index 0000000..39a6cca
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/handlers/report.go
@@ -0,0 +1,168 @@
+package handlers
+
+import (
+ "fmt"
+ "strings"
+ "net/http"
+ "encoding/xml"
+
+ "github.com/samedi/caldav-go/data"
+ "github.com/samedi/caldav-go/ixml"
+ "github.com/samedi/caldav-go/global"
+)
+
+type reportHandler struct{
+ request *http.Request
+ response *Response
+}
+
+// See more at RFC4791#section-7.1
+func (rh reportHandler) Handle() *Response {
+ requestBody := readRequestBody(rh.request)
+ header := headers{rh.request.Header}
+
+ urlResource, found, err := global.Storage.GetShallowResource(rh.request.URL.Path)
+ if !found {
+ return rh.response.Set(http.StatusNotFound, "")
+ } else if err != nil {
+ return rh.response.SetError(err)
+ }
+
+ // read body string to xml struct
+ var requestXML reportRootXML
+ xml.Unmarshal([]byte(requestBody), &requestXML)
+
+ // The resources to be reported are fetched by the type of the request. If it is
+ // a `calendar-multiget`, the resources come based on a set of `hrefs` in the request body.
+ // If it is a `calendar-query`, the resources are calculated based on set of filters in the request.
+ var resourcesToReport []reportRes
+ switch requestXML.XMLName {
+ case ixml.CALENDAR_MULTIGET_TG:
+ resourcesToReport, err = rh.fetchResourcesByList(urlResource, requestXML.Hrefs)
+ case ixml.CALENDAR_QUERY_TG:
+ resourcesToReport, err = rh.fetchResourcesByFilters(urlResource, requestXML.Filters)
+ default:
+ return rh.response.Set(http.StatusPreconditionFailed, "")
+ }
+
+ if err != nil {
+ return rh.response.SetError(err)
+ }
+
+ multistatus := &multistatusResp{
+ Minimal: header.IsMinimal(),
+ }
+ // for each href, build the multistatus responses
+ for _, r := range resourcesToReport {
+ propstats := multistatus.Propstats(r.resource, requestXML.Prop.Tags)
+ multistatus.AddResponse(r.href, r.found, propstats)
+ }
+
+ if multistatus.Minimal {
+ rh.response.SetHeader(HD_PREFERENCE_APPLIED, HD_PREFER_MINIMAL)
+ }
+
+ return rh.response.Set(207, multistatus.ToXML())
+}
+
+type reportPropXML struct {
+ Tags []xml.Name `xml:",any"`
+}
+
+type reportRootXML struct {
+ XMLName xml.Name
+ Prop reportPropXML `xml:"DAV: prop"`
+ Hrefs []string `xml:"DAV: href"`
+ Filters reportFilterXML `xml:"urn:ietf:params:xml:ns:caldav filter"`
+}
+
+type reportFilterXML struct {
+ XMLName xml.Name
+ InnerContent string `xml:",innerxml"`
+}
+
+func (this reportFilterXML) toString() string {
+ return fmt.Sprintf("<%s>%s%s>", this.XMLName.Local, this.InnerContent, this.XMLName.Local)
+}
+
+// Wraps a resource that has to be reported, either fetched by filters or by a list.
+// Basically it contains the original requested `href`, the actual `resource` (can be nil)
+// and if the `resource` was `found` or not
+type reportRes struct {
+ href string
+ resource *data.Resource
+ found bool
+}
+
+// The resources are fetched based on the origin resource and a set of filters.
+// If the origin resource is a collection, the filters are checked against each of the collection's resources
+// to see if they match. The collection's resources that match the filters are returned. The ones that will be returned
+// are the resources that were not found (does not exist) and the ones that matched the filters. The ones that did not
+// match the filter will not appear in the response result.
+// If the origin resource is not a collection, the function just returns it and ignore any filter processing.
+// [See RFC4791#section-7.8]
+func (rh reportHandler) fetchResourcesByFilters(origin *data.Resource, filtersXML reportFilterXML) ([]reportRes, error) {
+ // The list of resources that has to be reported back in the response.
+ reps := []reportRes{}
+
+ if origin.IsCollection() {
+ filters, _ := data.ParseResourceFilters(filtersXML.toString())
+ resources, err := global.Storage.GetResourcesByFilters(origin.Path, filters)
+
+ if err != nil {
+ return reps, err
+ }
+
+ for _, resource := range resources {
+ reps = append(reps, reportRes{resource.Path, &resource, true})
+ }
+ } else {
+ // the origin resource is not a collection, so returns just that as the result
+ reps = append(reps, reportRes{origin.Path, origin, true})
+ }
+
+ return reps, nil
+}
+
+// The hrefs can come from (1) the request URL or (2) from the request body itself.
+// If the origin resource from the URL points to a collection (2), we will check the request body
+// to get the requested `hrefs` (resource paths). Each requested href has to be related to the collection.
+// The ones that are not, we simply ignore them.
+// If the resource from the URL is NOT a collection (1) we process the the report only for this resource
+// and ignore any othre requested hrefs that might be present in the request body.
+// [See RFC4791#section-7.9]
+func (rh reportHandler) fetchResourcesByList(origin *data.Resource, requestedPaths []string) ([]reportRes, error) {
+ reps := []reportRes{}
+
+ if origin.IsCollection() {
+ resources, err := global.Storage.GetResourcesByList(requestedPaths)
+
+ if err != nil {
+ return reps, err
+ }
+
+ // we put all the resources found in a map path -> resource.
+ // this will be used later to query which requested resource was found
+ // or not and mount the response
+ resourcesMap := make(map[string]*data.Resource)
+ for _, resource := range resources {
+ r := resource
+ resourcesMap[resource.Path] = &r
+ }
+
+ for _, requestedPath := range requestedPaths {
+ // if the requested path does not belong to the origin collection, skip
+ // ('belonging' means that the path's prefix is the same as the collection path)
+ if !strings.HasPrefix(requestedPath, origin.Path) {
+ continue
+ }
+
+ resource, found := resourcesMap[requestedPath]
+ reps = append(reps, reportRes{requestedPath, resource, found})
+ }
+ } else {
+ reps = append(reps, reportRes{origin.Path, origin, true})
+ }
+
+ return reps, nil
+}
diff --git a/vendor/github.com/samedi/caldav-go/handlers/response.go b/vendor/github.com/samedi/caldav-go/handlers/response.go
new file mode 100644
index 0000000..f478def
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/handlers/response.go
@@ -0,0 +1,65 @@
+package handlers
+
+import (
+ "io"
+ "net/http"
+ "github.com/samedi/caldav-go/errs"
+)
+
+type Response struct {
+ Status int
+ Header http.Header
+ Body string
+ Error error
+}
+
+func NewResponse() *Response {
+ return &Response{
+ Header: make(http.Header),
+ }
+}
+
+func (this *Response) Set(status int, body string) *Response {
+ this.Status = status
+ this.Body = body
+
+ return this
+}
+
+func (this *Response) SetHeader(key, value string) *Response {
+ this.Header.Set(key, value)
+
+ return this
+}
+
+func (this *Response) SetError(err error) *Response {
+ this.Error = err
+
+ switch err {
+ case errs.ResourceNotFoundError:
+ this.Status = http.StatusNotFound
+ case errs.UnauthorizedError:
+ this.Status = http.StatusUnauthorized
+ case errs.ForbiddenError:
+ this.Status = http.StatusForbidden
+ default:
+ this.Status = http.StatusInternalServerError
+ }
+
+ return this
+}
+
+func (this *Response) Write(writer http.ResponseWriter) {
+ if this.Error == errs.UnauthorizedError {
+ this.SetHeader("WWW-Authenticate", `Basic realm="Restricted"`)
+ }
+
+ for key, values := range this.Header {
+ for _, value := range values {
+ writer.Header().Set(key, value)
+ }
+ }
+
+ writer.WriteHeader(this.Status)
+ io.WriteString(writer, this.Body)
+}
diff --git a/vendor/github.com/samedi/caldav-go/handlers/shared.go b/vendor/github.com/samedi/caldav-go/handlers/shared.go
new file mode 100644
index 0000000..b133fe3
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/handlers/shared.go
@@ -0,0 +1,22 @@
+package handlers
+
+import (
+ "net/http"
+ "io/ioutil"
+ "bytes"
+ "github.com/samedi/caldav-go/lib"
+)
+
+// Supported ICal components on this server.
+var supportedComponents = []string{lib.VCALENDAR, lib.VEVENT}
+
+// This function reads the request body and restore its content, so that
+// the request body can be read a second time.
+func readRequestBody(request *http.Request) string {
+ // Read the content
+ body, _ := ioutil.ReadAll(request.Body)
+ // Restore the io.ReadCloser to its original state
+ request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
+ // Use the content
+ return string(body)
+}
diff --git a/vendor/github.com/samedi/caldav-go/ixml/ixml.go b/vendor/github.com/samedi/caldav-go/ixml/ixml.go
new file mode 100644
index 0000000..6d3b94c
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/ixml/ixml.go
@@ -0,0 +1,93 @@
+package ixml
+
+import (
+ "fmt"
+ "bytes"
+ "net/http"
+ "encoding/xml"
+
+ "github.com/samedi/caldav-go/lib"
+)
+
+const (
+ DAV_NS = "DAV:"
+ CALDAV_NS = "urn:ietf:params:xml:ns:caldav"
+ CALSERV_NS = "http://calendarserver.org/ns/"
+)
+
+var NS_PREFIXES = map[string]string{
+ DAV_NS: "D",
+ CALDAV_NS: "C",
+ CALSERV_NS: "CS",
+}
+
+var (
+ CALENDAR_TG = xml.Name{CALDAV_NS, "calendar"}
+ CALENDAR_DATA_TG = xml.Name{CALDAV_NS, "calendar-data"}
+ CALENDAR_HOME_SET_TG = xml.Name{CALDAV_NS, "calendar-home-set"}
+ CALENDAR_QUERY_TG = xml.Name{CALDAV_NS, "calendar-query"}
+ CALENDAR_MULTIGET_TG = xml.Name{CALDAV_NS, "calendar-multiget"}
+ CALENDAR_USER_ADDRESS_SET_TG = xml.Name{CALDAV_NS, "calendar-user-address-set"}
+ COLLECTION_TG = xml.Name{DAV_NS, "collection"}
+ CURRENT_USER_PRINCIPAL_TG = xml.Name{DAV_NS, "current-user-principal"}
+ DISPLAY_NAME_TG = xml.Name{DAV_NS, "displayname"}
+ GET_CONTENT_LENGTH_TG = xml.Name{DAV_NS, "getcontentlength"}
+ GET_CONTENT_TYPE_TG = xml.Name{DAV_NS, "getcontenttype"}
+ GET_CTAG_TG = xml.Name{CALSERV_NS, "getctag"}
+ GET_ETAG_TG = xml.Name{DAV_NS, "getetag"}
+ GET_LAST_MODIFIED_TG = xml.Name{DAV_NS, "getlastmodified"}
+ HREF_TG = xml.Name{DAV_NS, "href"}
+ OWNER_TG = xml.Name{DAV_NS, "owner"}
+ PRINCIPAL_TG = xml.Name{DAV_NS, "principal"}
+ PRINCIPAL_COLLECTION_SET_TG = xml.Name{DAV_NS, "principal-collection-set"}
+ PRINCIPAL_URL_TG = xml.Name{DAV_NS, "principal-URL"}
+ RESOURCE_TYPE_TG = xml.Name{DAV_NS, "resourcetype"}
+ STATUS_TG = xml.Name{DAV_NS, "status"}
+ SUPPORTED_CALENDAR_COMPONENT_SET_TG = xml.Name{CALDAV_NS, "supported-calendar-component-set"}
+)
+
+func Namespaces() string {
+ bf := new(lib.StringBuffer)
+ bf.Write(`xmlns:%s="%s" `, NS_PREFIXES[DAV_NS], DAV_NS)
+ bf.Write(`xmlns:%s="%s" `, NS_PREFIXES[CALDAV_NS], CALDAV_NS)
+ bf.Write(`xmlns:%s="%s"`, NS_PREFIXES[CALSERV_NS], CALSERV_NS)
+
+ return bf.String()
+}
+
+// Tag returns a XML tag as string based on the given tag name and content. It
+// takes in consideration the namespace and also if it is an empty content or not.
+func Tag(xmlName xml.Name, content string) string {
+ name := xmlName.Local
+ ns := NS_PREFIXES[xmlName.Space]
+
+ if ns != "" {
+ ns = ns + ":"
+ }
+
+ if content != "" {
+ return fmt.Sprintf("<%s%s>%s%s%s>", ns, name, content, ns, name)
+ } else {
+ return fmt.Sprintf("<%s%s/>", ns, name)
+ }
+}
+
+// HrefTag returns a DAV tag with the given href path.
+func HrefTag(href string) (tag string) {
+ return Tag(HREF_TG, href)
+}
+
+// StatusTag returns a DAV tag with the given HTTP status. The
+// status is translated into a label, e.g.: HTTP/1.1 404 NotFound.
+func StatusTag(status int) string {
+ statusText := fmt.Sprintf("HTTP/1.1 %d %s", status, http.StatusText(status))
+ return Tag(STATUS_TG, statusText)
+}
+
+// EscapeText escapes any special character in the given text and returns the result.
+func EscapeText(text string) string {
+ buffer := bytes.NewBufferString("")
+ xml.EscapeText(buffer, []byte(text))
+
+ return buffer.String()
+}
diff --git a/vendor/github.com/samedi/caldav-go/lib/components.go b/vendor/github.com/samedi/caldav-go/lib/components.go
new file mode 100644
index 0000000..99ac3e7
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/lib/components.go
@@ -0,0 +1,8 @@
+package lib
+
+const (
+ VCALENDAR = "VCALENDAR"
+ VEVENT = "VEVENT"
+ VJOURNAL = "VJOURNAL"
+ VTODO = "VTODO"
+)
diff --git a/vendor/github.com/samedi/caldav-go/lib/paths.go b/vendor/github.com/samedi/caldav-go/lib/paths.go
new file mode 100644
index 0000000..d46b176
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/lib/paths.go
@@ -0,0 +1,10 @@
+package lib
+
+import (
+ "path/filepath"
+)
+
+func ToSlashPath(path string) string {
+ cleanPath := filepath.Clean(path)
+ return filepath.ToSlash(cleanPath)
+}
diff --git a/vendor/github.com/samedi/caldav-go/lib/strbuff.go b/vendor/github.com/samedi/caldav-go/lib/strbuff.go
new file mode 100644
index 0000000..45a5beb
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/lib/strbuff.go
@@ -0,0 +1,18 @@
+package lib
+
+import (
+ "fmt"
+ "bytes"
+)
+
+type StringBuffer struct {
+ buffer bytes.Buffer
+}
+
+func (b *StringBuffer) Write(format string, elem ...interface{}) {
+ b.buffer.WriteString(fmt.Sprintf(format, elem...))
+}
+
+func (b *StringBuffer) String() string {
+ return b.buffer.String()
+}
diff --git a/vendor/github.com/samedi/caldav-go/test.sh b/vendor/github.com/samedi/caldav-go/test.sh
new file mode 100755
index 0000000..09efdc7
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/test.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+go test -race ./...
+rm -rf test-data
diff --git a/vendor/github.com/samedi/caldav-go/version.go b/vendor/github.com/samedi/caldav-go/version.go
new file mode 100644
index 0000000..bf787e3
--- /dev/null
+++ b/vendor/github.com/samedi/caldav-go/version.go
@@ -0,0 +1,5 @@
+package caldav
+
+const (
+ VERSION = "3.0.0"
+)
diff --git a/vendor/golang.org/x/crypto/AUTHORS b/vendor/golang.org/x/crypto/AUTHORS
new file mode 100644
index 0000000..2b00ddb
--- /dev/null
+++ b/vendor/golang.org/x/crypto/AUTHORS
@@ -0,0 +1,3 @@
+# This source code refers to The Go Authors for copyright purposes.
+# The master list of authors is in the main Go distribution,
+# visible at https://tip.golang.org/AUTHORS.
diff --git a/vendor/golang.org/x/crypto/CONTRIBUTORS b/vendor/golang.org/x/crypto/CONTRIBUTORS
new file mode 100644
index 0000000..1fbd3e9
--- /dev/null
+++ b/vendor/golang.org/x/crypto/CONTRIBUTORS
@@ -0,0 +1,3 @@
+# This source code was written by the Go contributors.
+# The master list of contributors is in the main Go distribution,
+# visible at https://tip.golang.org/CONTRIBUTORS.
diff --git a/vendor/golang.org/x/crypto/LICENSE b/vendor/golang.org/x/crypto/LICENSE
new file mode 100644
index 0000000..6a66aea
--- /dev/null
+++ b/vendor/golang.org/x/crypto/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/golang.org/x/crypto/PATENTS b/vendor/golang.org/x/crypto/PATENTS
new file mode 100644
index 0000000..7330990
--- /dev/null
+++ b/vendor/golang.org/x/crypto/PATENTS
@@ -0,0 +1,22 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of the Go project.
+
+Google hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section)
+patent license to make, have made, use, offer to sell, sell, import,
+transfer and otherwise run, modify and propagate the contents of this
+implementation of Go, where such license applies only to those patent
+claims, both currently owned or controlled by Google and acquired in
+the future, licensable by Google that are necessarily infringed by this
+implementation of Go. This grant does not include claims that would be
+infringed only as a consequence of further modification of this
+implementation. If you or your agent or exclusive licensee institute or
+order or agree to the institution of patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that this implementation of Go or any code incorporated within this
+implementation of Go constitutes direct or contributory patent
+infringement, or inducement of patent infringement, then any patent
+rights granted to you under this License for this implementation of Go
+shall terminate as of the date such litigation is filed.
diff --git a/vendor/golang.org/x/crypto/bcrypt/base64.go b/vendor/golang.org/x/crypto/bcrypt/base64.go
new file mode 100644
index 0000000..fc31160
--- /dev/null
+++ b/vendor/golang.org/x/crypto/bcrypt/base64.go
@@ -0,0 +1,35 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bcrypt
+
+import "encoding/base64"
+
+const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+
+var bcEncoding = base64.NewEncoding(alphabet)
+
+func base64Encode(src []byte) []byte {
+ n := bcEncoding.EncodedLen(len(src))
+ dst := make([]byte, n)
+ bcEncoding.Encode(dst, src)
+ for dst[n-1] == '=' {
+ n--
+ }
+ return dst[:n]
+}
+
+func base64Decode(src []byte) ([]byte, error) {
+ numOfEquals := 4 - (len(src) % 4)
+ for i := 0; i < numOfEquals; i++ {
+ src = append(src, '=')
+ }
+
+ dst := make([]byte, bcEncoding.DecodedLen(len(src)))
+ n, err := bcEncoding.Decode(dst, src)
+ if err != nil {
+ return nil, err
+ }
+ return dst[:n], nil
+}
diff --git a/vendor/golang.org/x/crypto/bcrypt/bcrypt.go b/vendor/golang.org/x/crypto/bcrypt/bcrypt.go
new file mode 100644
index 0000000..aeb73f8
--- /dev/null
+++ b/vendor/golang.org/x/crypto/bcrypt/bcrypt.go
@@ -0,0 +1,295 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing
+// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
+package bcrypt // import "golang.org/x/crypto/bcrypt"
+
+// The code is a port of Provos and Mazières's C implementation.
+import (
+ "crypto/rand"
+ "crypto/subtle"
+ "errors"
+ "fmt"
+ "io"
+ "strconv"
+
+ "golang.org/x/crypto/blowfish"
+)
+
+const (
+ MinCost int = 4 // the minimum allowable cost as passed in to GenerateFromPassword
+ MaxCost int = 31 // the maximum allowable cost as passed in to GenerateFromPassword
+ DefaultCost int = 10 // the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword
+)
+
+// The error returned from CompareHashAndPassword when a password and hash do
+// not match.
+var ErrMismatchedHashAndPassword = errors.New("crypto/bcrypt: hashedPassword is not the hash of the given password")
+
+// The error returned from CompareHashAndPassword when a hash is too short to
+// be a bcrypt hash.
+var ErrHashTooShort = errors.New("crypto/bcrypt: hashedSecret too short to be a bcrypted password")
+
+// The error returned from CompareHashAndPassword when a hash was created with
+// a bcrypt algorithm newer than this implementation.
+type HashVersionTooNewError byte
+
+func (hv HashVersionTooNewError) Error() string {
+ return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' requested is newer than current version '%c'", byte(hv), majorVersion)
+}
+
+// The error returned from CompareHashAndPassword when a hash starts with something other than '$'
+type InvalidHashPrefixError byte
+
+func (ih InvalidHashPrefixError) Error() string {
+ return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih))
+}
+
+type InvalidCostError int
+
+func (ic InvalidCostError) Error() string {
+ return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost))
+}
+
+const (
+ majorVersion = '2'
+ minorVersion = 'a'
+ maxSaltSize = 16
+ maxCryptedHashSize = 23
+ encodedSaltSize = 22
+ encodedHashSize = 31
+ minHashSize = 59
+)
+
+// magicCipherData is an IV for the 64 Blowfish encryption calls in
+// bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes.
+var magicCipherData = []byte{
+ 0x4f, 0x72, 0x70, 0x68,
+ 0x65, 0x61, 0x6e, 0x42,
+ 0x65, 0x68, 0x6f, 0x6c,
+ 0x64, 0x65, 0x72, 0x53,
+ 0x63, 0x72, 0x79, 0x44,
+ 0x6f, 0x75, 0x62, 0x74,
+}
+
+type hashed struct {
+ hash []byte
+ salt []byte
+ cost int // allowed range is MinCost to MaxCost
+ major byte
+ minor byte
+}
+
+// GenerateFromPassword returns the bcrypt hash of the password at the given
+// cost. If the cost given is less than MinCost, the cost will be set to
+// DefaultCost, instead. Use CompareHashAndPassword, as defined in this package,
+// to compare the returned hashed password with its cleartext version.
+func GenerateFromPassword(password []byte, cost int) ([]byte, error) {
+ p, err := newFromPassword(password, cost)
+ if err != nil {
+ return nil, err
+ }
+ return p.Hash(), nil
+}
+
+// CompareHashAndPassword compares a bcrypt hashed password with its possible
+// plaintext equivalent. Returns nil on success, or an error on failure.
+func CompareHashAndPassword(hashedPassword, password []byte) error {
+ p, err := newFromHash(hashedPassword)
+ if err != nil {
+ return err
+ }
+
+ otherHash, err := bcrypt(password, p.cost, p.salt)
+ if err != nil {
+ return err
+ }
+
+ otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor}
+ if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 {
+ return nil
+ }
+
+ return ErrMismatchedHashAndPassword
+}
+
+// Cost returns the hashing cost used to create the given hashed
+// password. When, in the future, the hashing cost of a password system needs
+// to be increased in order to adjust for greater computational power, this
+// function allows one to establish which passwords need to be updated.
+func Cost(hashedPassword []byte) (int, error) {
+ p, err := newFromHash(hashedPassword)
+ if err != nil {
+ return 0, err
+ }
+ return p.cost, nil
+}
+
+func newFromPassword(password []byte, cost int) (*hashed, error) {
+ if cost < MinCost {
+ cost = DefaultCost
+ }
+ p := new(hashed)
+ p.major = majorVersion
+ p.minor = minorVersion
+
+ err := checkCost(cost)
+ if err != nil {
+ return nil, err
+ }
+ p.cost = cost
+
+ unencodedSalt := make([]byte, maxSaltSize)
+ _, err = io.ReadFull(rand.Reader, unencodedSalt)
+ if err != nil {
+ return nil, err
+ }
+
+ p.salt = base64Encode(unencodedSalt)
+ hash, err := bcrypt(password, p.cost, p.salt)
+ if err != nil {
+ return nil, err
+ }
+ p.hash = hash
+ return p, err
+}
+
+func newFromHash(hashedSecret []byte) (*hashed, error) {
+ if len(hashedSecret) < minHashSize {
+ return nil, ErrHashTooShort
+ }
+ p := new(hashed)
+ n, err := p.decodeVersion(hashedSecret)
+ if err != nil {
+ return nil, err
+ }
+ hashedSecret = hashedSecret[n:]
+ n, err = p.decodeCost(hashedSecret)
+ if err != nil {
+ return nil, err
+ }
+ hashedSecret = hashedSecret[n:]
+
+ // The "+2" is here because we'll have to append at most 2 '=' to the salt
+ // when base64 decoding it in expensiveBlowfishSetup().
+ p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2)
+ copy(p.salt, hashedSecret[:encodedSaltSize])
+
+ hashedSecret = hashedSecret[encodedSaltSize:]
+ p.hash = make([]byte, len(hashedSecret))
+ copy(p.hash, hashedSecret)
+
+ return p, nil
+}
+
+func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) {
+ cipherData := make([]byte, len(magicCipherData))
+ copy(cipherData, magicCipherData)
+
+ c, err := expensiveBlowfishSetup(password, uint32(cost), salt)
+ if err != nil {
+ return nil, err
+ }
+
+ for i := 0; i < 24; i += 8 {
+ for j := 0; j < 64; j++ {
+ c.Encrypt(cipherData[i:i+8], cipherData[i:i+8])
+ }
+ }
+
+ // Bug compatibility with C bcrypt implementations. We only encode 23 of
+ // the 24 bytes encrypted.
+ hsh := base64Encode(cipherData[:maxCryptedHashSize])
+ return hsh, nil
+}
+
+func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) {
+ csalt, err := base64Decode(salt)
+ if err != nil {
+ return nil, err
+ }
+
+ // Bug compatibility with C bcrypt implementations. They use the trailing
+ // NULL in the key string during expansion.
+ // We copy the key to prevent changing the underlying array.
+ ckey := append(key[:len(key):len(key)], 0)
+
+ c, err := blowfish.NewSaltedCipher(ckey, csalt)
+ if err != nil {
+ return nil, err
+ }
+
+ var i, rounds uint64
+ rounds = 1 << cost
+ for i = 0; i < rounds; i++ {
+ blowfish.ExpandKey(ckey, c)
+ blowfish.ExpandKey(csalt, c)
+ }
+
+ return c, nil
+}
+
+func (p *hashed) Hash() []byte {
+ arr := make([]byte, 60)
+ arr[0] = '$'
+ arr[1] = p.major
+ n := 2
+ if p.minor != 0 {
+ arr[2] = p.minor
+ n = 3
+ }
+ arr[n] = '$'
+ n++
+ copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
+ n += 2
+ arr[n] = '$'
+ n++
+ copy(arr[n:], p.salt)
+ n += encodedSaltSize
+ copy(arr[n:], p.hash)
+ n += encodedHashSize
+ return arr[:n]
+}
+
+func (p *hashed) decodeVersion(sbytes []byte) (int, error) {
+ if sbytes[0] != '$' {
+ return -1, InvalidHashPrefixError(sbytes[0])
+ }
+ if sbytes[1] > majorVersion {
+ return -1, HashVersionTooNewError(sbytes[1])
+ }
+ p.major = sbytes[1]
+ n := 3
+ if sbytes[2] != '$' {
+ p.minor = sbytes[2]
+ n++
+ }
+ return n, nil
+}
+
+// sbytes should begin where decodeVersion left off.
+func (p *hashed) decodeCost(sbytes []byte) (int, error) {
+ cost, err := strconv.Atoi(string(sbytes[0:2]))
+ if err != nil {
+ return -1, err
+ }
+ err = checkCost(cost)
+ if err != nil {
+ return -1, err
+ }
+ p.cost = cost
+ return 3, nil
+}
+
+func (p *hashed) String() string {
+ return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor)
+}
+
+func checkCost(cost int) error {
+ if cost < MinCost || cost > MaxCost {
+ return InvalidCostError(cost)
+ }
+ return nil
+}
diff --git a/vendor/golang.org/x/crypto/blowfish/block.go b/vendor/golang.org/x/crypto/blowfish/block.go
new file mode 100644
index 0000000..9d80f19
--- /dev/null
+++ b/vendor/golang.org/x/crypto/blowfish/block.go
@@ -0,0 +1,159 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package blowfish
+
+// getNextWord returns the next big-endian uint32 value from the byte slice
+// at the given position in a circular manner, updating the position.
+func getNextWord(b []byte, pos *int) uint32 {
+ var w uint32
+ j := *pos
+ for i := 0; i < 4; i++ {
+ w = w<<8 | uint32(b[j])
+ j++
+ if j >= len(b) {
+ j = 0
+ }
+ }
+ *pos = j
+ return w
+}
+
+// ExpandKey performs a key expansion on the given *Cipher. Specifically, it
+// performs the Blowfish algorithm's key schedule which sets up the *Cipher's
+// pi and substitution tables for calls to Encrypt. This is used, primarily,
+// by the bcrypt package to reuse the Blowfish key schedule during its
+// set up. It's unlikely that you need to use this directly.
+func ExpandKey(key []byte, c *Cipher) {
+ j := 0
+ for i := 0; i < 18; i++ {
+ // Using inlined getNextWord for performance.
+ var d uint32
+ for k := 0; k < 4; k++ {
+ d = d<<8 | uint32(key[j])
+ j++
+ if j >= len(key) {
+ j = 0
+ }
+ }
+ c.p[i] ^= d
+ }
+
+ var l, r uint32
+ for i := 0; i < 18; i += 2 {
+ l, r = encryptBlock(l, r, c)
+ c.p[i], c.p[i+1] = l, r
+ }
+
+ for i := 0; i < 256; i += 2 {
+ l, r = encryptBlock(l, r, c)
+ c.s0[i], c.s0[i+1] = l, r
+ }
+ for i := 0; i < 256; i += 2 {
+ l, r = encryptBlock(l, r, c)
+ c.s1[i], c.s1[i+1] = l, r
+ }
+ for i := 0; i < 256; i += 2 {
+ l, r = encryptBlock(l, r, c)
+ c.s2[i], c.s2[i+1] = l, r
+ }
+ for i := 0; i < 256; i += 2 {
+ l, r = encryptBlock(l, r, c)
+ c.s3[i], c.s3[i+1] = l, r
+ }
+}
+
+// This is similar to ExpandKey, but folds the salt during the key
+// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero
+// salt passed in, reusing ExpandKey turns out to be a place of inefficiency
+// and specializing it here is useful.
+func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) {
+ j := 0
+ for i := 0; i < 18; i++ {
+ c.p[i] ^= getNextWord(key, &j)
+ }
+
+ j = 0
+ var l, r uint32
+ for i := 0; i < 18; i += 2 {
+ l ^= getNextWord(salt, &j)
+ r ^= getNextWord(salt, &j)
+ l, r = encryptBlock(l, r, c)
+ c.p[i], c.p[i+1] = l, r
+ }
+
+ for i := 0; i < 256; i += 2 {
+ l ^= getNextWord(salt, &j)
+ r ^= getNextWord(salt, &j)
+ l, r = encryptBlock(l, r, c)
+ c.s0[i], c.s0[i+1] = l, r
+ }
+
+ for i := 0; i < 256; i += 2 {
+ l ^= getNextWord(salt, &j)
+ r ^= getNextWord(salt, &j)
+ l, r = encryptBlock(l, r, c)
+ c.s1[i], c.s1[i+1] = l, r
+ }
+
+ for i := 0; i < 256; i += 2 {
+ l ^= getNextWord(salt, &j)
+ r ^= getNextWord(salt, &j)
+ l, r = encryptBlock(l, r, c)
+ c.s2[i], c.s2[i+1] = l, r
+ }
+
+ for i := 0; i < 256; i += 2 {
+ l ^= getNextWord(salt, &j)
+ r ^= getNextWord(salt, &j)
+ l, r = encryptBlock(l, r, c)
+ c.s3[i], c.s3[i+1] = l, r
+ }
+}
+
+func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
+ xl, xr := l, r
+ xl ^= c.p[0]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[1]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[2]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[3]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[4]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[5]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[6]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[7]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[8]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[9]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[10]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[11]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[12]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[13]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[14]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[15]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[16]
+ xr ^= c.p[17]
+ return xr, xl
+}
+
+func decryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
+ xl, xr := l, r
+ xl ^= c.p[17]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[16]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[15]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[14]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[13]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[12]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[11]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[10]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[9]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[8]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[7]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[6]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[5]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[4]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[3]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[2]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[1]
+ xr ^= c.p[0]
+ return xr, xl
+}
diff --git a/vendor/golang.org/x/crypto/blowfish/cipher.go b/vendor/golang.org/x/crypto/blowfish/cipher.go
new file mode 100644
index 0000000..2641dad
--- /dev/null
+++ b/vendor/golang.org/x/crypto/blowfish/cipher.go
@@ -0,0 +1,91 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package blowfish implements Bruce Schneier's Blowfish encryption algorithm.
+package blowfish // import "golang.org/x/crypto/blowfish"
+
+// The code is a port of Bruce Schneier's C implementation.
+// See https://www.schneier.com/blowfish.html.
+
+import "strconv"
+
+// The Blowfish block size in bytes.
+const BlockSize = 8
+
+// A Cipher is an instance of Blowfish encryption using a particular key.
+type Cipher struct {
+ p [18]uint32
+ s0, s1, s2, s3 [256]uint32
+}
+
+type KeySizeError int
+
+func (k KeySizeError) Error() string {
+ return "crypto/blowfish: invalid key size " + strconv.Itoa(int(k))
+}
+
+// NewCipher creates and returns a Cipher.
+// The key argument should be the Blowfish key, from 1 to 56 bytes.
+func NewCipher(key []byte) (*Cipher, error) {
+ var result Cipher
+ if k := len(key); k < 1 || k > 56 {
+ return nil, KeySizeError(k)
+ }
+ initCipher(&result)
+ ExpandKey(key, &result)
+ return &result, nil
+}
+
+// NewSaltedCipher creates a returns a Cipher that folds a salt into its key
+// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is
+// sufficient and desirable. For bcrypt compatibility, the key can be over 56
+// bytes.
+func NewSaltedCipher(key, salt []byte) (*Cipher, error) {
+ if len(salt) == 0 {
+ return NewCipher(key)
+ }
+ var result Cipher
+ if k := len(key); k < 1 {
+ return nil, KeySizeError(k)
+ }
+ initCipher(&result)
+ expandKeyWithSalt(key, salt, &result)
+ return &result, nil
+}
+
+// BlockSize returns the Blowfish block size, 8 bytes.
+// It is necessary to satisfy the Block interface in the
+// package "crypto/cipher".
+func (c *Cipher) BlockSize() int { return BlockSize }
+
+// Encrypt encrypts the 8-byte buffer src using the key k
+// and stores the result in dst.
+// Note that for amounts of data larger than a block,
+// it is not safe to just call Encrypt on successive blocks;
+// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
+func (c *Cipher) Encrypt(dst, src []byte) {
+ l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
+ r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
+ l, r = encryptBlock(l, r, c)
+ dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
+ dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
+}
+
+// Decrypt decrypts the 8-byte buffer src using the key k
+// and stores the result in dst.
+func (c *Cipher) Decrypt(dst, src []byte) {
+ l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
+ r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
+ l, r = decryptBlock(l, r, c)
+ dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
+ dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
+}
+
+func initCipher(c *Cipher) {
+ copy(c.p[0:], p[0:])
+ copy(c.s0[0:], s0[0:])
+ copy(c.s1[0:], s1[0:])
+ copy(c.s2[0:], s2[0:])
+ copy(c.s3[0:], s3[0:])
+}
diff --git a/vendor/golang.org/x/crypto/blowfish/const.go b/vendor/golang.org/x/crypto/blowfish/const.go
new file mode 100644
index 0000000..d040775
--- /dev/null
+++ b/vendor/golang.org/x/crypto/blowfish/const.go
@@ -0,0 +1,199 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The startup permutation array and substitution boxes.
+// They are the hexadecimal digits of PI; see:
+// https://www.schneier.com/code/constants.txt.
+
+package blowfish
+
+var s0 = [256]uint32{
+ 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,
+ 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+ 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658,
+ 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+ 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e,
+ 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+ 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6,
+ 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+ 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c,
+ 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+ 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1,
+ 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+ 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a,
+ 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+ 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176,
+ 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+ 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706,
+ 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+ 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b,
+ 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+ 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c,
+ 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+ 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a,
+ 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+ 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,
+ 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+ 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8,
+ 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+ 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33,
+ 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+ 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0,
+ 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+ 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777,
+ 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+ 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,
+ 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+ 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e,
+ 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+ 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9,
+ 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+ 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f,
+ 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+ 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
+}
+
+var s1 = [256]uint32{
+ 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d,
+ 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+ 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65,
+ 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+ 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9,
+ 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+ 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d,
+ 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+ 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,
+ 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+ 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908,
+ 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+ 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124,
+ 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+ 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908,
+ 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+ 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b,
+ 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+ 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,
+ 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+ 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d,
+ 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+ 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5,
+ 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+ 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96,
+ 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+ 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca,
+ 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+ 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,
+ 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+ 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054,
+ 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+ 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea,
+ 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+ 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646,
+ 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+ 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea,
+ 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+ 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,
+ 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+ 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd,
+ 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+ 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
+}
+
+var s2 = [256]uint32{
+ 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7,
+ 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+ 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af,
+ 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+ 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4,
+ 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+ 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec,
+ 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+ 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332,
+ 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+ 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58,
+ 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+ 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22,
+ 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+ 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60,
+ 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+ 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99,
+ 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+ 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74,
+ 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+ 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3,
+ 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+ 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979,
+ 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+ 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa,
+ 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+ 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086,
+ 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+ 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24,
+ 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+ 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84,
+ 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+ 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09,
+ 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+ 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe,
+ 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+ 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0,
+ 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+ 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188,
+ 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+ 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8,
+ 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+ 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
+}
+
+var s3 = [256]uint32{
+ 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742,
+ 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+ 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79,
+ 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+ 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a,
+ 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+ 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,
+ 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+ 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797,
+ 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+ 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6,
+ 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+ 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba,
+ 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+ 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5,
+ 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+ 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,
+ 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+ 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd,
+ 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+ 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb,
+ 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+ 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc,
+ 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+ 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc,
+ 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+ 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a,
+ 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+ 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a,
+ 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+ 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b,
+ 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+ 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e,
+ 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+ 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623,
+ 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+ 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a,
+ 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+ 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3,
+ 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+ 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c,
+ 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+ 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
+}
+
+var p = [18]uint32{
+ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
+ 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+ 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b,
+}
diff --git a/vendor/golang.org/x/net/AUTHORS b/vendor/golang.org/x/net/AUTHORS
new file mode 100644
index 0000000..15167cd
--- /dev/null
+++ b/vendor/golang.org/x/net/AUTHORS
@@ -0,0 +1,3 @@
+# This source code refers to The Go Authors for copyright purposes.
+# The master list of authors is in the main Go distribution,
+# visible at http://tip.golang.org/AUTHORS.
diff --git a/vendor/golang.org/x/net/CONTRIBUTORS b/vendor/golang.org/x/net/CONTRIBUTORS
new file mode 100644
index 0000000..1c4577e
--- /dev/null
+++ b/vendor/golang.org/x/net/CONTRIBUTORS
@@ -0,0 +1,3 @@
+# This source code was written by the Go contributors.
+# The master list of contributors is in the main Go distribution,
+# visible at http://tip.golang.org/CONTRIBUTORS.
diff --git a/vendor/golang.org/x/net/LICENSE b/vendor/golang.org/x/net/LICENSE
new file mode 100644
index 0000000..6a66aea
--- /dev/null
+++ b/vendor/golang.org/x/net/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/golang.org/x/net/PATENTS b/vendor/golang.org/x/net/PATENTS
new file mode 100644
index 0000000..7330990
--- /dev/null
+++ b/vendor/golang.org/x/net/PATENTS
@@ -0,0 +1,22 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of the Go project.
+
+Google hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section)
+patent license to make, have made, use, offer to sell, sell, import,
+transfer and otherwise run, modify and propagate the contents of this
+implementation of Go, where such license applies only to those patent
+claims, both currently owned or controlled by Google and acquired in
+the future, licensable by Google that are necessarily infringed by this
+implementation of Go. This grant does not include claims that would be
+infringed only as a consequence of further modification of this
+implementation. If you or your agent or exclusive licensee institute or
+order or agree to the institution of patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that this implementation of Go or any code incorporated within this
+implementation of Go constitutes direct or contributory patent
+infringement, or inducement of patent infringement, then any patent
+rights granted to you under this License for this implementation of Go
+shall terminate as of the date such litigation is filed.
diff --git a/vendor/golang.org/x/net/context/context.go b/vendor/golang.org/x/net/context/context.go
new file mode 100644
index 0000000..a3c021d
--- /dev/null
+++ b/vendor/golang.org/x/net/context/context.go
@@ -0,0 +1,56 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package context defines the Context type, which carries deadlines,
+// cancelation signals, and other request-scoped values across API boundaries
+// and between processes.
+// As of Go 1.7 this package is available in the standard library under the
+// name context. https://golang.org/pkg/context.
+//
+// Incoming requests to a server should create a Context, and outgoing calls to
+// servers should accept a Context. The chain of function calls between must
+// propagate the Context, optionally replacing it with a modified copy created
+// using WithDeadline, WithTimeout, WithCancel, or WithValue.
+//
+// Programs that use Contexts should follow these rules to keep interfaces
+// consistent across packages and enable static analysis tools to check context
+// propagation:
+//
+// Do not store Contexts inside a struct type; instead, pass a Context
+// explicitly to each function that needs it. The Context should be the first
+// parameter, typically named ctx:
+//
+// func DoSomething(ctx context.Context, arg Arg) error {
+// // ... use ctx ...
+// }
+//
+// Do not pass a nil Context, even if a function permits it. Pass context.TODO
+// if you are unsure about which Context to use.
+//
+// Use context Values only for request-scoped data that transits processes and
+// APIs, not for passing optional parameters to functions.
+//
+// The same Context may be passed to functions running in different goroutines;
+// Contexts are safe for simultaneous use by multiple goroutines.
+//
+// See http://blog.golang.org/context for example code for a server that uses
+// Contexts.
+package context // import "golang.org/x/net/context"
+
+// Background returns a non-nil, empty Context. It is never canceled, has no
+// values, and has no deadline. It is typically used by the main function,
+// initialization, and tests, and as the top-level Context for incoming
+// requests.
+func Background() Context {
+ return background
+}
+
+// TODO returns a non-nil, empty Context. Code should use context.TODO when
+// it's unclear which Context to use or it is not yet available (because the
+// surrounding function has not yet been extended to accept a Context
+// parameter). TODO is recognized by static analysis tools that determine
+// whether Contexts are propagated correctly in a program.
+func TODO() Context {
+ return todo
+}
diff --git a/vendor/golang.org/x/net/context/go17.go b/vendor/golang.org/x/net/context/go17.go
new file mode 100644
index 0000000..d20f52b
--- /dev/null
+++ b/vendor/golang.org/x/net/context/go17.go
@@ -0,0 +1,72 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.7
+
+package context
+
+import (
+ "context" // standard library's context, as of Go 1.7
+ "time"
+)
+
+var (
+ todo = context.TODO()
+ background = context.Background()
+)
+
+// Canceled is the error returned by Context.Err when the context is canceled.
+var Canceled = context.Canceled
+
+// DeadlineExceeded is the error returned by Context.Err when the context's
+// deadline passes.
+var DeadlineExceeded = context.DeadlineExceeded
+
+// WithCancel returns a copy of parent with a new Done channel. The returned
+// context's Done channel is closed when the returned cancel function is called
+// or when the parent context's Done channel is closed, whichever happens first.
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete.
+func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
+ ctx, f := context.WithCancel(parent)
+ return ctx, CancelFunc(f)
+}
+
+// WithDeadline returns a copy of the parent context with the deadline adjusted
+// to be no later than d. If the parent's deadline is already earlier than d,
+// WithDeadline(parent, d) is semantically equivalent to parent. The returned
+// context's Done channel is closed when the deadline expires, when the returned
+// cancel function is called, or when the parent context's Done channel is
+// closed, whichever happens first.
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete.
+func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
+ ctx, f := context.WithDeadline(parent, deadline)
+ return ctx, CancelFunc(f)
+}
+
+// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete:
+//
+// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
+// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
+// defer cancel() // releases resources if slowOperation completes before timeout elapses
+// return slowOperation(ctx)
+// }
+func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
+ return WithDeadline(parent, time.Now().Add(timeout))
+}
+
+// WithValue returns a copy of parent in which the value associated with key is
+// val.
+//
+// Use context Values only for request-scoped data that transits processes and
+// APIs, not for passing optional parameters to functions.
+func WithValue(parent Context, key interface{}, val interface{}) Context {
+ return context.WithValue(parent, key, val)
+}
diff --git a/vendor/golang.org/x/net/context/go19.go b/vendor/golang.org/x/net/context/go19.go
new file mode 100644
index 0000000..d88bd1d
--- /dev/null
+++ b/vendor/golang.org/x/net/context/go19.go
@@ -0,0 +1,20 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.9
+
+package context
+
+import "context" // standard library's context, as of Go 1.7
+
+// A Context carries a deadline, a cancelation signal, and other values across
+// API boundaries.
+//
+// Context's methods may be called by multiple goroutines simultaneously.
+type Context = context.Context
+
+// A CancelFunc tells an operation to abandon its work.
+// A CancelFunc does not wait for the work to stop.
+// After the first call, subsequent calls to a CancelFunc do nothing.
+type CancelFunc = context.CancelFunc
diff --git a/vendor/golang.org/x/net/context/pre_go17.go b/vendor/golang.org/x/net/context/pre_go17.go
new file mode 100644
index 0000000..0f35592
--- /dev/null
+++ b/vendor/golang.org/x/net/context/pre_go17.go
@@ -0,0 +1,300 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.7
+
+package context
+
+import (
+ "errors"
+ "fmt"
+ "sync"
+ "time"
+)
+
+// An emptyCtx is never canceled, has no values, and has no deadline. It is not
+// struct{}, since vars of this type must have distinct addresses.
+type emptyCtx int
+
+func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
+ return
+}
+
+func (*emptyCtx) Done() <-chan struct{} {
+ return nil
+}
+
+func (*emptyCtx) Err() error {
+ return nil
+}
+
+func (*emptyCtx) Value(key interface{}) interface{} {
+ return nil
+}
+
+func (e *emptyCtx) String() string {
+ switch e {
+ case background:
+ return "context.Background"
+ case todo:
+ return "context.TODO"
+ }
+ return "unknown empty Context"
+}
+
+var (
+ background = new(emptyCtx)
+ todo = new(emptyCtx)
+)
+
+// Canceled is the error returned by Context.Err when the context is canceled.
+var Canceled = errors.New("context canceled")
+
+// DeadlineExceeded is the error returned by Context.Err when the context's
+// deadline passes.
+var DeadlineExceeded = errors.New("context deadline exceeded")
+
+// WithCancel returns a copy of parent with a new Done channel. The returned
+// context's Done channel is closed when the returned cancel function is called
+// or when the parent context's Done channel is closed, whichever happens first.
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete.
+func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
+ c := newCancelCtx(parent)
+ propagateCancel(parent, c)
+ return c, func() { c.cancel(true, Canceled) }
+}
+
+// newCancelCtx returns an initialized cancelCtx.
+func newCancelCtx(parent Context) *cancelCtx {
+ return &cancelCtx{
+ Context: parent,
+ done: make(chan struct{}),
+ }
+}
+
+// propagateCancel arranges for child to be canceled when parent is.
+func propagateCancel(parent Context, child canceler) {
+ if parent.Done() == nil {
+ return // parent is never canceled
+ }
+ if p, ok := parentCancelCtx(parent); ok {
+ p.mu.Lock()
+ if p.err != nil {
+ // parent has already been canceled
+ child.cancel(false, p.err)
+ } else {
+ if p.children == nil {
+ p.children = make(map[canceler]bool)
+ }
+ p.children[child] = true
+ }
+ p.mu.Unlock()
+ } else {
+ go func() {
+ select {
+ case <-parent.Done():
+ child.cancel(false, parent.Err())
+ case <-child.Done():
+ }
+ }()
+ }
+}
+
+// parentCancelCtx follows a chain of parent references until it finds a
+// *cancelCtx. This function understands how each of the concrete types in this
+// package represents its parent.
+func parentCancelCtx(parent Context) (*cancelCtx, bool) {
+ for {
+ switch c := parent.(type) {
+ case *cancelCtx:
+ return c, true
+ case *timerCtx:
+ return c.cancelCtx, true
+ case *valueCtx:
+ parent = c.Context
+ default:
+ return nil, false
+ }
+ }
+}
+
+// removeChild removes a context from its parent.
+func removeChild(parent Context, child canceler) {
+ p, ok := parentCancelCtx(parent)
+ if !ok {
+ return
+ }
+ p.mu.Lock()
+ if p.children != nil {
+ delete(p.children, child)
+ }
+ p.mu.Unlock()
+}
+
+// A canceler is a context type that can be canceled directly. The
+// implementations are *cancelCtx and *timerCtx.
+type canceler interface {
+ cancel(removeFromParent bool, err error)
+ Done() <-chan struct{}
+}
+
+// A cancelCtx can be canceled. When canceled, it also cancels any children
+// that implement canceler.
+type cancelCtx struct {
+ Context
+
+ done chan struct{} // closed by the first cancel call.
+
+ mu sync.Mutex
+ children map[canceler]bool // set to nil by the first cancel call
+ err error // set to non-nil by the first cancel call
+}
+
+func (c *cancelCtx) Done() <-chan struct{} {
+ return c.done
+}
+
+func (c *cancelCtx) Err() error {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ return c.err
+}
+
+func (c *cancelCtx) String() string {
+ return fmt.Sprintf("%v.WithCancel", c.Context)
+}
+
+// cancel closes c.done, cancels each of c's children, and, if
+// removeFromParent is true, removes c from its parent's children.
+func (c *cancelCtx) cancel(removeFromParent bool, err error) {
+ if err == nil {
+ panic("context: internal error: missing cancel error")
+ }
+ c.mu.Lock()
+ if c.err != nil {
+ c.mu.Unlock()
+ return // already canceled
+ }
+ c.err = err
+ close(c.done)
+ for child := range c.children {
+ // NOTE: acquiring the child's lock while holding parent's lock.
+ child.cancel(false, err)
+ }
+ c.children = nil
+ c.mu.Unlock()
+
+ if removeFromParent {
+ removeChild(c.Context, c)
+ }
+}
+
+// WithDeadline returns a copy of the parent context with the deadline adjusted
+// to be no later than d. If the parent's deadline is already earlier than d,
+// WithDeadline(parent, d) is semantically equivalent to parent. The returned
+// context's Done channel is closed when the deadline expires, when the returned
+// cancel function is called, or when the parent context's Done channel is
+// closed, whichever happens first.
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete.
+func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
+ if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
+ // The current deadline is already sooner than the new one.
+ return WithCancel(parent)
+ }
+ c := &timerCtx{
+ cancelCtx: newCancelCtx(parent),
+ deadline: deadline,
+ }
+ propagateCancel(parent, c)
+ d := deadline.Sub(time.Now())
+ if d <= 0 {
+ c.cancel(true, DeadlineExceeded) // deadline has already passed
+ return c, func() { c.cancel(true, Canceled) }
+ }
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ if c.err == nil {
+ c.timer = time.AfterFunc(d, func() {
+ c.cancel(true, DeadlineExceeded)
+ })
+ }
+ return c, func() { c.cancel(true, Canceled) }
+}
+
+// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
+// implement Done and Err. It implements cancel by stopping its timer then
+// delegating to cancelCtx.cancel.
+type timerCtx struct {
+ *cancelCtx
+ timer *time.Timer // Under cancelCtx.mu.
+
+ deadline time.Time
+}
+
+func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
+ return c.deadline, true
+}
+
+func (c *timerCtx) String() string {
+ return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))
+}
+
+func (c *timerCtx) cancel(removeFromParent bool, err error) {
+ c.cancelCtx.cancel(false, err)
+ if removeFromParent {
+ // Remove this timerCtx from its parent cancelCtx's children.
+ removeChild(c.cancelCtx.Context, c)
+ }
+ c.mu.Lock()
+ if c.timer != nil {
+ c.timer.Stop()
+ c.timer = nil
+ }
+ c.mu.Unlock()
+}
+
+// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete:
+//
+// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
+// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
+// defer cancel() // releases resources if slowOperation completes before timeout elapses
+// return slowOperation(ctx)
+// }
+func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
+ return WithDeadline(parent, time.Now().Add(timeout))
+}
+
+// WithValue returns a copy of parent in which the value associated with key is
+// val.
+//
+// Use context Values only for request-scoped data that transits processes and
+// APIs, not for passing optional parameters to functions.
+func WithValue(parent Context, key interface{}, val interface{}) Context {
+ return &valueCtx{parent, key, val}
+}
+
+// A valueCtx carries a key-value pair. It implements Value for that key and
+// delegates all other calls to the embedded Context.
+type valueCtx struct {
+ Context
+ key, val interface{}
+}
+
+func (c *valueCtx) String() string {
+ return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)
+}
+
+func (c *valueCtx) Value(key interface{}) interface{} {
+ if c.key == key {
+ return c.val
+ }
+ return c.Context.Value(key)
+}
diff --git a/vendor/golang.org/x/net/context/pre_go19.go b/vendor/golang.org/x/net/context/pre_go19.go
new file mode 100644
index 0000000..b105f80
--- /dev/null
+++ b/vendor/golang.org/x/net/context/pre_go19.go
@@ -0,0 +1,109 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.9
+
+package context
+
+import "time"
+
+// A Context carries a deadline, a cancelation signal, and other values across
+// API boundaries.
+//
+// Context's methods may be called by multiple goroutines simultaneously.
+type Context interface {
+ // Deadline returns the time when work done on behalf of this context
+ // should be canceled. Deadline returns ok==false when no deadline is
+ // set. Successive calls to Deadline return the same results.
+ Deadline() (deadline time.Time, ok bool)
+
+ // Done returns a channel that's closed when work done on behalf of this
+ // context should be canceled. Done may return nil if this context can
+ // never be canceled. Successive calls to Done return the same value.
+ //
+ // WithCancel arranges for Done to be closed when cancel is called;
+ // WithDeadline arranges for Done to be closed when the deadline
+ // expires; WithTimeout arranges for Done to be closed when the timeout
+ // elapses.
+ //
+ // Done is provided for use in select statements:
+ //
+ // // Stream generates values with DoSomething and sends them to out
+ // // until DoSomething returns an error or ctx.Done is closed.
+ // func Stream(ctx context.Context, out chan<- Value) error {
+ // for {
+ // v, err := DoSomething(ctx)
+ // if err != nil {
+ // return err
+ // }
+ // select {
+ // case <-ctx.Done():
+ // return ctx.Err()
+ // case out <- v:
+ // }
+ // }
+ // }
+ //
+ // See http://blog.golang.org/pipelines for more examples of how to use
+ // a Done channel for cancelation.
+ Done() <-chan struct{}
+
+ // Err returns a non-nil error value after Done is closed. Err returns
+ // Canceled if the context was canceled or DeadlineExceeded if the
+ // context's deadline passed. No other values for Err are defined.
+ // After Done is closed, successive calls to Err return the same value.
+ Err() error
+
+ // Value returns the value associated with this context for key, or nil
+ // if no value is associated with key. Successive calls to Value with
+ // the same key returns the same result.
+ //
+ // Use context values only for request-scoped data that transits
+ // processes and API boundaries, not for passing optional parameters to
+ // functions.
+ //
+ // A key identifies a specific value in a Context. Functions that wish
+ // to store values in Context typically allocate a key in a global
+ // variable then use that key as the argument to context.WithValue and
+ // Context.Value. A key can be any type that supports equality;
+ // packages should define keys as an unexported type to avoid
+ // collisions.
+ //
+ // Packages that define a Context key should provide type-safe accessors
+ // for the values stores using that key:
+ //
+ // // Package user defines a User type that's stored in Contexts.
+ // package user
+ //
+ // import "golang.org/x/net/context"
+ //
+ // // User is the type of value stored in the Contexts.
+ // type User struct {...}
+ //
+ // // key is an unexported type for keys defined in this package.
+ // // This prevents collisions with keys defined in other packages.
+ // type key int
+ //
+ // // userKey is the key for user.User values in Contexts. It is
+ // // unexported; clients use user.NewContext and user.FromContext
+ // // instead of using this key directly.
+ // var userKey key = 0
+ //
+ // // NewContext returns a new Context that carries value u.
+ // func NewContext(ctx context.Context, u *User) context.Context {
+ // return context.WithValue(ctx, userKey, u)
+ // }
+ //
+ // // FromContext returns the User value stored in ctx, if any.
+ // func FromContext(ctx context.Context) (*User, bool) {
+ // u, ok := ctx.Value(userKey).(*User)
+ // return u, ok
+ // }
+ Value(key interface{}) interface{}
+}
+
+// A CancelFunc tells an operation to abandon its work.
+// A CancelFunc does not wait for the work to stop.
+// After the first call, subsequent calls to a CancelFunc do nothing.
+type CancelFunc func()
diff --git a/vendor/golang.org/x/net/webdav/file.go b/vendor/golang.org/x/net/webdav/file.go
new file mode 100644
index 0000000..748118d
--- /dev/null
+++ b/vendor/golang.org/x/net/webdav/file.go
@@ -0,0 +1,796 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package webdav
+
+import (
+ "encoding/xml"
+ "io"
+ "net/http"
+ "os"
+ "path"
+ "path/filepath"
+ "strings"
+ "sync"
+ "time"
+
+ "golang.org/x/net/context"
+)
+
+// slashClean is equivalent to but slightly more efficient than
+// path.Clean("/" + name).
+func slashClean(name string) string {
+ if name == "" || name[0] != '/' {
+ name = "/" + name
+ }
+ return path.Clean(name)
+}
+
+// A FileSystem implements access to a collection of named files. The elements
+// in a file path are separated by slash ('/', U+002F) characters, regardless
+// of host operating system convention.
+//
+// Each method has the same semantics as the os package's function of the same
+// name.
+//
+// Note that the os.Rename documentation says that "OS-specific restrictions
+// might apply". In particular, whether or not renaming a file or directory
+// overwriting another existing file or directory is an error is OS-dependent.
+type FileSystem interface {
+ Mkdir(ctx context.Context, name string, perm os.FileMode) error
+ OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (File, error)
+ RemoveAll(ctx context.Context, name string) error
+ Rename(ctx context.Context, oldName, newName string) error
+ Stat(ctx context.Context, name string) (os.FileInfo, error)
+}
+
+// A File is returned by a FileSystem's OpenFile method and can be served by a
+// Handler.
+//
+// A File may optionally implement the DeadPropsHolder interface, if it can
+// load and save dead properties.
+type File interface {
+ http.File
+ io.Writer
+}
+
+// A Dir implements FileSystem using the native file system restricted to a
+// specific directory tree.
+//
+// While the FileSystem.OpenFile method takes '/'-separated paths, a Dir's
+// string value is a filename on the native file system, not a URL, so it is
+// separated by filepath.Separator, which isn't necessarily '/'.
+//
+// An empty Dir is treated as ".".
+type Dir string
+
+func (d Dir) resolve(name string) string {
+ // This implementation is based on Dir.Open's code in the standard net/http package.
+ if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 ||
+ strings.Contains(name, "\x00") {
+ return ""
+ }
+ dir := string(d)
+ if dir == "" {
+ dir = "."
+ }
+ return filepath.Join(dir, filepath.FromSlash(slashClean(name)))
+}
+
+func (d Dir) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
+ if name = d.resolve(name); name == "" {
+ return os.ErrNotExist
+ }
+ return os.Mkdir(name, perm)
+}
+
+func (d Dir) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (File, error) {
+ if name = d.resolve(name); name == "" {
+ return nil, os.ErrNotExist
+ }
+ f, err := os.OpenFile(name, flag, perm)
+ if err != nil {
+ return nil, err
+ }
+ return f, nil
+}
+
+func (d Dir) RemoveAll(ctx context.Context, name string) error {
+ if name = d.resolve(name); name == "" {
+ return os.ErrNotExist
+ }
+ if name == filepath.Clean(string(d)) {
+ // Prohibit removing the virtual root directory.
+ return os.ErrInvalid
+ }
+ return os.RemoveAll(name)
+}
+
+func (d Dir) Rename(ctx context.Context, oldName, newName string) error {
+ if oldName = d.resolve(oldName); oldName == "" {
+ return os.ErrNotExist
+ }
+ if newName = d.resolve(newName); newName == "" {
+ return os.ErrNotExist
+ }
+ if root := filepath.Clean(string(d)); root == oldName || root == newName {
+ // Prohibit renaming from or to the virtual root directory.
+ return os.ErrInvalid
+ }
+ return os.Rename(oldName, newName)
+}
+
+func (d Dir) Stat(ctx context.Context, name string) (os.FileInfo, error) {
+ if name = d.resolve(name); name == "" {
+ return nil, os.ErrNotExist
+ }
+ return os.Stat(name)
+}
+
+// NewMemFS returns a new in-memory FileSystem implementation.
+func NewMemFS() FileSystem {
+ return &memFS{
+ root: memFSNode{
+ children: make(map[string]*memFSNode),
+ mode: 0660 | os.ModeDir,
+ modTime: time.Now(),
+ },
+ }
+}
+
+// A memFS implements FileSystem, storing all metadata and actual file data
+// in-memory. No limits on filesystem size are used, so it is not recommended
+// this be used where the clients are untrusted.
+//
+// Concurrent access is permitted. The tree structure is protected by a mutex,
+// and each node's contents and metadata are protected by a per-node mutex.
+//
+// TODO: Enforce file permissions.
+type memFS struct {
+ mu sync.Mutex
+ root memFSNode
+}
+
+// TODO: clean up and rationalize the walk/find code.
+
+// walk walks the directory tree for the fullname, calling f at each step. If f
+// returns an error, the walk will be aborted and return that same error.
+//
+// dir is the directory at that step, frag is the name fragment, and final is
+// whether it is the final step. For example, walking "/foo/bar/x" will result
+// in 3 calls to f:
+// - "/", "foo", false
+// - "/foo/", "bar", false
+// - "/foo/bar/", "x", true
+// The frag argument will be empty only if dir is the root node and the walk
+// ends at that root node.
+func (fs *memFS) walk(op, fullname string, f func(dir *memFSNode, frag string, final bool) error) error {
+ original := fullname
+ fullname = slashClean(fullname)
+
+ // Strip any leading "/"s to make fullname a relative path, as the walk
+ // starts at fs.root.
+ if fullname[0] == '/' {
+ fullname = fullname[1:]
+ }
+ dir := &fs.root
+
+ for {
+ frag, remaining := fullname, ""
+ i := strings.IndexRune(fullname, '/')
+ final := i < 0
+ if !final {
+ frag, remaining = fullname[:i], fullname[i+1:]
+ }
+ if frag == "" && dir != &fs.root {
+ panic("webdav: empty path fragment for a clean path")
+ }
+ if err := f(dir, frag, final); err != nil {
+ return &os.PathError{
+ Op: op,
+ Path: original,
+ Err: err,
+ }
+ }
+ if final {
+ break
+ }
+ child := dir.children[frag]
+ if child == nil {
+ return &os.PathError{
+ Op: op,
+ Path: original,
+ Err: os.ErrNotExist,
+ }
+ }
+ if !child.mode.IsDir() {
+ return &os.PathError{
+ Op: op,
+ Path: original,
+ Err: os.ErrInvalid,
+ }
+ }
+ dir, fullname = child, remaining
+ }
+ return nil
+}
+
+// find returns the parent of the named node and the relative name fragment
+// from the parent to the child. For example, if finding "/foo/bar/baz" then
+// parent will be the node for "/foo/bar" and frag will be "baz".
+//
+// If the fullname names the root node, then parent, frag and err will be zero.
+//
+// find returns an error if the parent does not already exist or the parent
+// isn't a directory, but it will not return an error per se if the child does
+// not already exist. The error returned is either nil or an *os.PathError
+// whose Op is op.
+func (fs *memFS) find(op, fullname string) (parent *memFSNode, frag string, err error) {
+ err = fs.walk(op, fullname, func(parent0 *memFSNode, frag0 string, final bool) error {
+ if !final {
+ return nil
+ }
+ if frag0 != "" {
+ parent, frag = parent0, frag0
+ }
+ return nil
+ })
+ return parent, frag, err
+}
+
+func (fs *memFS) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
+ fs.mu.Lock()
+ defer fs.mu.Unlock()
+
+ dir, frag, err := fs.find("mkdir", name)
+ if err != nil {
+ return err
+ }
+ if dir == nil {
+ // We can't create the root.
+ return os.ErrInvalid
+ }
+ if _, ok := dir.children[frag]; ok {
+ return os.ErrExist
+ }
+ dir.children[frag] = &memFSNode{
+ children: make(map[string]*memFSNode),
+ mode: perm.Perm() | os.ModeDir,
+ modTime: time.Now(),
+ }
+ return nil
+}
+
+func (fs *memFS) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (File, error) {
+ fs.mu.Lock()
+ defer fs.mu.Unlock()
+
+ dir, frag, err := fs.find("open", name)
+ if err != nil {
+ return nil, err
+ }
+ var n *memFSNode
+ if dir == nil {
+ // We're opening the root.
+ if flag&(os.O_WRONLY|os.O_RDWR) != 0 {
+ return nil, os.ErrPermission
+ }
+ n, frag = &fs.root, "/"
+
+ } else {
+ n = dir.children[frag]
+ if flag&(os.O_SYNC|os.O_APPEND) != 0 {
+ // memFile doesn't support these flags yet.
+ return nil, os.ErrInvalid
+ }
+ if flag&os.O_CREATE != 0 {
+ if flag&os.O_EXCL != 0 && n != nil {
+ return nil, os.ErrExist
+ }
+ if n == nil {
+ n = &memFSNode{
+ mode: perm.Perm(),
+ }
+ dir.children[frag] = n
+ }
+ }
+ if n == nil {
+ return nil, os.ErrNotExist
+ }
+ if flag&(os.O_WRONLY|os.O_RDWR) != 0 && flag&os.O_TRUNC != 0 {
+ n.mu.Lock()
+ n.data = nil
+ n.mu.Unlock()
+ }
+ }
+
+ children := make([]os.FileInfo, 0, len(n.children))
+ for cName, c := range n.children {
+ children = append(children, c.stat(cName))
+ }
+ return &memFile{
+ n: n,
+ nameSnapshot: frag,
+ childrenSnapshot: children,
+ }, nil
+}
+
+func (fs *memFS) RemoveAll(ctx context.Context, name string) error {
+ fs.mu.Lock()
+ defer fs.mu.Unlock()
+
+ dir, frag, err := fs.find("remove", name)
+ if err != nil {
+ return err
+ }
+ if dir == nil {
+ // We can't remove the root.
+ return os.ErrInvalid
+ }
+ delete(dir.children, frag)
+ return nil
+}
+
+func (fs *memFS) Rename(ctx context.Context, oldName, newName string) error {
+ fs.mu.Lock()
+ defer fs.mu.Unlock()
+
+ oldName = slashClean(oldName)
+ newName = slashClean(newName)
+ if oldName == newName {
+ return nil
+ }
+ if strings.HasPrefix(newName, oldName+"/") {
+ // We can't rename oldName to be a sub-directory of itself.
+ return os.ErrInvalid
+ }
+
+ oDir, oFrag, err := fs.find("rename", oldName)
+ if err != nil {
+ return err
+ }
+ if oDir == nil {
+ // We can't rename from the root.
+ return os.ErrInvalid
+ }
+
+ nDir, nFrag, err := fs.find("rename", newName)
+ if err != nil {
+ return err
+ }
+ if nDir == nil {
+ // We can't rename to the root.
+ return os.ErrInvalid
+ }
+
+ oNode, ok := oDir.children[oFrag]
+ if !ok {
+ return os.ErrNotExist
+ }
+ if oNode.children != nil {
+ if nNode, ok := nDir.children[nFrag]; ok {
+ if nNode.children == nil {
+ return errNotADirectory
+ }
+ if len(nNode.children) != 0 {
+ return errDirectoryNotEmpty
+ }
+ }
+ }
+ delete(oDir.children, oFrag)
+ nDir.children[nFrag] = oNode
+ return nil
+}
+
+func (fs *memFS) Stat(ctx context.Context, name string) (os.FileInfo, error) {
+ fs.mu.Lock()
+ defer fs.mu.Unlock()
+
+ dir, frag, err := fs.find("stat", name)
+ if err != nil {
+ return nil, err
+ }
+ if dir == nil {
+ // We're stat'ting the root.
+ return fs.root.stat("/"), nil
+ }
+ if n, ok := dir.children[frag]; ok {
+ return n.stat(path.Base(name)), nil
+ }
+ return nil, os.ErrNotExist
+}
+
+// A memFSNode represents a single entry in the in-memory filesystem and also
+// implements os.FileInfo.
+type memFSNode struct {
+ // children is protected by memFS.mu.
+ children map[string]*memFSNode
+
+ mu sync.Mutex
+ data []byte
+ mode os.FileMode
+ modTime time.Time
+ deadProps map[xml.Name]Property
+}
+
+func (n *memFSNode) stat(name string) *memFileInfo {
+ n.mu.Lock()
+ defer n.mu.Unlock()
+ return &memFileInfo{
+ name: name,
+ size: int64(len(n.data)),
+ mode: n.mode,
+ modTime: n.modTime,
+ }
+}
+
+func (n *memFSNode) DeadProps() (map[xml.Name]Property, error) {
+ n.mu.Lock()
+ defer n.mu.Unlock()
+ if len(n.deadProps) == 0 {
+ return nil, nil
+ }
+ ret := make(map[xml.Name]Property, len(n.deadProps))
+ for k, v := range n.deadProps {
+ ret[k] = v
+ }
+ return ret, nil
+}
+
+func (n *memFSNode) Patch(patches []Proppatch) ([]Propstat, error) {
+ n.mu.Lock()
+ defer n.mu.Unlock()
+ pstat := Propstat{Status: http.StatusOK}
+ for _, patch := range patches {
+ for _, p := range patch.Props {
+ pstat.Props = append(pstat.Props, Property{XMLName: p.XMLName})
+ if patch.Remove {
+ delete(n.deadProps, p.XMLName)
+ continue
+ }
+ if n.deadProps == nil {
+ n.deadProps = map[xml.Name]Property{}
+ }
+ n.deadProps[p.XMLName] = p
+ }
+ }
+ return []Propstat{pstat}, nil
+}
+
+type memFileInfo struct {
+ name string
+ size int64
+ mode os.FileMode
+ modTime time.Time
+}
+
+func (f *memFileInfo) Name() string { return f.name }
+func (f *memFileInfo) Size() int64 { return f.size }
+func (f *memFileInfo) Mode() os.FileMode { return f.mode }
+func (f *memFileInfo) ModTime() time.Time { return f.modTime }
+func (f *memFileInfo) IsDir() bool { return f.mode.IsDir() }
+func (f *memFileInfo) Sys() interface{} { return nil }
+
+// A memFile is a File implementation for a memFSNode. It is a per-file (not
+// per-node) read/write position, and a snapshot of the memFS' tree structure
+// (a node's name and children) for that node.
+type memFile struct {
+ n *memFSNode
+ nameSnapshot string
+ childrenSnapshot []os.FileInfo
+ // pos is protected by n.mu.
+ pos int
+}
+
+// A *memFile implements the optional DeadPropsHolder interface.
+var _ DeadPropsHolder = (*memFile)(nil)
+
+func (f *memFile) DeadProps() (map[xml.Name]Property, error) { return f.n.DeadProps() }
+func (f *memFile) Patch(patches []Proppatch) ([]Propstat, error) { return f.n.Patch(patches) }
+
+func (f *memFile) Close() error {
+ return nil
+}
+
+func (f *memFile) Read(p []byte) (int, error) {
+ f.n.mu.Lock()
+ defer f.n.mu.Unlock()
+ if f.n.mode.IsDir() {
+ return 0, os.ErrInvalid
+ }
+ if f.pos >= len(f.n.data) {
+ return 0, io.EOF
+ }
+ n := copy(p, f.n.data[f.pos:])
+ f.pos += n
+ return n, nil
+}
+
+func (f *memFile) Readdir(count int) ([]os.FileInfo, error) {
+ f.n.mu.Lock()
+ defer f.n.mu.Unlock()
+ if !f.n.mode.IsDir() {
+ return nil, os.ErrInvalid
+ }
+ old := f.pos
+ if old >= len(f.childrenSnapshot) {
+ // The os.File Readdir docs say that at the end of a directory,
+ // the error is io.EOF if count > 0 and nil if count <= 0.
+ if count > 0 {
+ return nil, io.EOF
+ }
+ return nil, nil
+ }
+ if count > 0 {
+ f.pos += count
+ if f.pos > len(f.childrenSnapshot) {
+ f.pos = len(f.childrenSnapshot)
+ }
+ } else {
+ f.pos = len(f.childrenSnapshot)
+ old = 0
+ }
+ return f.childrenSnapshot[old:f.pos], nil
+}
+
+func (f *memFile) Seek(offset int64, whence int) (int64, error) {
+ f.n.mu.Lock()
+ defer f.n.mu.Unlock()
+ npos := f.pos
+ // TODO: How to handle offsets greater than the size of system int?
+ switch whence {
+ case os.SEEK_SET:
+ npos = int(offset)
+ case os.SEEK_CUR:
+ npos += int(offset)
+ case os.SEEK_END:
+ npos = len(f.n.data) + int(offset)
+ default:
+ npos = -1
+ }
+ if npos < 0 {
+ return 0, os.ErrInvalid
+ }
+ f.pos = npos
+ return int64(f.pos), nil
+}
+
+func (f *memFile) Stat() (os.FileInfo, error) {
+ return f.n.stat(f.nameSnapshot), nil
+}
+
+func (f *memFile) Write(p []byte) (int, error) {
+ lenp := len(p)
+ f.n.mu.Lock()
+ defer f.n.mu.Unlock()
+
+ if f.n.mode.IsDir() {
+ return 0, os.ErrInvalid
+ }
+ if f.pos < len(f.n.data) {
+ n := copy(f.n.data[f.pos:], p)
+ f.pos += n
+ p = p[n:]
+ } else if f.pos > len(f.n.data) {
+ // Write permits the creation of holes, if we've seek'ed past the
+ // existing end of file.
+ if f.pos <= cap(f.n.data) {
+ oldLen := len(f.n.data)
+ f.n.data = f.n.data[:f.pos]
+ hole := f.n.data[oldLen:]
+ for i := range hole {
+ hole[i] = 0
+ }
+ } else {
+ d := make([]byte, f.pos, f.pos+len(p))
+ copy(d, f.n.data)
+ f.n.data = d
+ }
+ }
+
+ if len(p) > 0 {
+ // We should only get here if f.pos == len(f.n.data).
+ f.n.data = append(f.n.data, p...)
+ f.pos = len(f.n.data)
+ }
+ f.n.modTime = time.Now()
+ return lenp, nil
+}
+
+// moveFiles moves files and/or directories from src to dst.
+//
+// See section 9.9.4 for when various HTTP status codes apply.
+func moveFiles(ctx context.Context, fs FileSystem, src, dst string, overwrite bool) (status int, err error) {
+ created := false
+ if _, err := fs.Stat(ctx, dst); err != nil {
+ if !os.IsNotExist(err) {
+ return http.StatusForbidden, err
+ }
+ created = true
+ } else if overwrite {
+ // Section 9.9.3 says that "If a resource exists at the destination
+ // and the Overwrite header is "T", then prior to performing the move,
+ // the server must perform a DELETE with "Depth: infinity" on the
+ // destination resource.
+ if err := fs.RemoveAll(ctx, dst); err != nil {
+ return http.StatusForbidden, err
+ }
+ } else {
+ return http.StatusPreconditionFailed, os.ErrExist
+ }
+ if err := fs.Rename(ctx, src, dst); err != nil {
+ return http.StatusForbidden, err
+ }
+ if created {
+ return http.StatusCreated, nil
+ }
+ return http.StatusNoContent, nil
+}
+
+func copyProps(dst, src File) error {
+ d, ok := dst.(DeadPropsHolder)
+ if !ok {
+ return nil
+ }
+ s, ok := src.(DeadPropsHolder)
+ if !ok {
+ return nil
+ }
+ m, err := s.DeadProps()
+ if err != nil {
+ return err
+ }
+ props := make([]Property, 0, len(m))
+ for _, prop := range m {
+ props = append(props, prop)
+ }
+ _, err = d.Patch([]Proppatch{{Props: props}})
+ return err
+}
+
+// copyFiles copies files and/or directories from src to dst.
+//
+// See section 9.8.5 for when various HTTP status codes apply.
+func copyFiles(ctx context.Context, fs FileSystem, src, dst string, overwrite bool, depth int, recursion int) (status int, err error) {
+ if recursion == 1000 {
+ return http.StatusInternalServerError, errRecursionTooDeep
+ }
+ recursion++
+
+ // TODO: section 9.8.3 says that "Note that an infinite-depth COPY of /A/
+ // into /A/B/ could lead to infinite recursion if not handled correctly."
+
+ srcFile, err := fs.OpenFile(ctx, src, os.O_RDONLY, 0)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return http.StatusNotFound, err
+ }
+ return http.StatusInternalServerError, err
+ }
+ defer srcFile.Close()
+ srcStat, err := srcFile.Stat()
+ if err != nil {
+ if os.IsNotExist(err) {
+ return http.StatusNotFound, err
+ }
+ return http.StatusInternalServerError, err
+ }
+ srcPerm := srcStat.Mode() & os.ModePerm
+
+ created := false
+ if _, err := fs.Stat(ctx, dst); err != nil {
+ if os.IsNotExist(err) {
+ created = true
+ } else {
+ return http.StatusForbidden, err
+ }
+ } else {
+ if !overwrite {
+ return http.StatusPreconditionFailed, os.ErrExist
+ }
+ if err := fs.RemoveAll(ctx, dst); err != nil && !os.IsNotExist(err) {
+ return http.StatusForbidden, err
+ }
+ }
+
+ if srcStat.IsDir() {
+ if err := fs.Mkdir(ctx, dst, srcPerm); err != nil {
+ return http.StatusForbidden, err
+ }
+ if depth == infiniteDepth {
+ children, err := srcFile.Readdir(-1)
+ if err != nil {
+ return http.StatusForbidden, err
+ }
+ for _, c := range children {
+ name := c.Name()
+ s := path.Join(src, name)
+ d := path.Join(dst, name)
+ cStatus, cErr := copyFiles(ctx, fs, s, d, overwrite, depth, recursion)
+ if cErr != nil {
+ // TODO: MultiStatus.
+ return cStatus, cErr
+ }
+ }
+ }
+
+ } else {
+ dstFile, err := fs.OpenFile(ctx, dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, srcPerm)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return http.StatusConflict, err
+ }
+ return http.StatusForbidden, err
+
+ }
+ _, copyErr := io.Copy(dstFile, srcFile)
+ propsErr := copyProps(dstFile, srcFile)
+ closeErr := dstFile.Close()
+ if copyErr != nil {
+ return http.StatusInternalServerError, copyErr
+ }
+ if propsErr != nil {
+ return http.StatusInternalServerError, propsErr
+ }
+ if closeErr != nil {
+ return http.StatusInternalServerError, closeErr
+ }
+ }
+
+ if created {
+ return http.StatusCreated, nil
+ }
+ return http.StatusNoContent, nil
+}
+
+// walkFS traverses filesystem fs starting at name up to depth levels.
+//
+// Allowed values for depth are 0, 1 or infiniteDepth. For each visited node,
+// walkFS calls walkFn. If a visited file system node is a directory and
+// walkFn returns filepath.SkipDir, walkFS will skip traversal of this node.
+func walkFS(ctx context.Context, fs FileSystem, depth int, name string, info os.FileInfo, walkFn filepath.WalkFunc) error {
+ // This implementation is based on Walk's code in the standard path/filepath package.
+ err := walkFn(name, info, nil)
+ if err != nil {
+ if info.IsDir() && err == filepath.SkipDir {
+ return nil
+ }
+ return err
+ }
+ if !info.IsDir() || depth == 0 {
+ return nil
+ }
+ if depth == 1 {
+ depth = 0
+ }
+
+ // Read directory names.
+ f, err := fs.OpenFile(ctx, name, os.O_RDONLY, 0)
+ if err != nil {
+ return walkFn(name, info, err)
+ }
+ fileInfos, err := f.Readdir(0)
+ f.Close()
+ if err != nil {
+ return walkFn(name, info, err)
+ }
+
+ for _, fileInfo := range fileInfos {
+ filename := path.Join(name, fileInfo.Name())
+ fileInfo, err := fs.Stat(ctx, filename)
+ if err != nil {
+ if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
+ return err
+ }
+ } else {
+ err = walkFS(ctx, fs, depth, filename, fileInfo, walkFn)
+ if err != nil {
+ if !fileInfo.IsDir() || err != filepath.SkipDir {
+ return err
+ }
+ }
+ }
+ }
+ return nil
+}
diff --git a/vendor/golang.org/x/net/webdav/file_go1.6.go b/vendor/golang.org/x/net/webdav/file_go1.6.go
new file mode 100644
index 0000000..fa38770
--- /dev/null
+++ b/vendor/golang.org/x/net/webdav/file_go1.6.go
@@ -0,0 +1,17 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.7
+
+package webdav
+
+import (
+ "net/http"
+
+ "golang.org/x/net/context"
+)
+
+func getContext(r *http.Request) context.Context {
+ return context.Background()
+}
diff --git a/vendor/golang.org/x/net/webdav/file_go1.7.go b/vendor/golang.org/x/net/webdav/file_go1.7.go
new file mode 100644
index 0000000..d1c3de8
--- /dev/null
+++ b/vendor/golang.org/x/net/webdav/file_go1.7.go
@@ -0,0 +1,16 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.7
+
+package webdav
+
+import (
+ "context"
+ "net/http"
+)
+
+func getContext(r *http.Request) context.Context {
+ return r.Context()
+}
diff --git a/vendor/golang.org/x/net/webdav/if.go b/vendor/golang.org/x/net/webdav/if.go
new file mode 100644
index 0000000..416e81c
--- /dev/null
+++ b/vendor/golang.org/x/net/webdav/if.go
@@ -0,0 +1,173 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package webdav
+
+// The If header is covered by Section 10.4.
+// http://www.webdav.org/specs/rfc4918.html#HEADER_If
+
+import (
+ "strings"
+)
+
+// ifHeader is a disjunction (OR) of ifLists.
+type ifHeader struct {
+ lists []ifList
+}
+
+// ifList is a conjunction (AND) of Conditions, and an optional resource tag.
+type ifList struct {
+ resourceTag string
+ conditions []Condition
+}
+
+// parseIfHeader parses the "If: foo bar" HTTP header. The httpHeader string
+// should omit the "If:" prefix and have any "\r\n"s collapsed to a " ", as is
+// returned by req.Header.Get("If") for a http.Request req.
+func parseIfHeader(httpHeader string) (h ifHeader, ok bool) {
+ s := strings.TrimSpace(httpHeader)
+ switch tokenType, _, _ := lex(s); tokenType {
+ case '(':
+ return parseNoTagLists(s)
+ case angleTokenType:
+ return parseTaggedLists(s)
+ default:
+ return ifHeader{}, false
+ }
+}
+
+func parseNoTagLists(s string) (h ifHeader, ok bool) {
+ for {
+ l, remaining, ok := parseList(s)
+ if !ok {
+ return ifHeader{}, false
+ }
+ h.lists = append(h.lists, l)
+ if remaining == "" {
+ return h, true
+ }
+ s = remaining
+ }
+}
+
+func parseTaggedLists(s string) (h ifHeader, ok bool) {
+ resourceTag, n := "", 0
+ for first := true; ; first = false {
+ tokenType, tokenStr, remaining := lex(s)
+ switch tokenType {
+ case angleTokenType:
+ if !first && n == 0 {
+ return ifHeader{}, false
+ }
+ resourceTag, n = tokenStr, 0
+ s = remaining
+ case '(':
+ n++
+ l, remaining, ok := parseList(s)
+ if !ok {
+ return ifHeader{}, false
+ }
+ l.resourceTag = resourceTag
+ h.lists = append(h.lists, l)
+ if remaining == "" {
+ return h, true
+ }
+ s = remaining
+ default:
+ return ifHeader{}, false
+ }
+ }
+}
+
+func parseList(s string) (l ifList, remaining string, ok bool) {
+ tokenType, _, s := lex(s)
+ if tokenType != '(' {
+ return ifList{}, "", false
+ }
+ for {
+ tokenType, _, remaining = lex(s)
+ if tokenType == ')' {
+ if len(l.conditions) == 0 {
+ return ifList{}, "", false
+ }
+ return l, remaining, true
+ }
+ c, remaining, ok := parseCondition(s)
+ if !ok {
+ return ifList{}, "", false
+ }
+ l.conditions = append(l.conditions, c)
+ s = remaining
+ }
+}
+
+func parseCondition(s string) (c Condition, remaining string, ok bool) {
+ tokenType, tokenStr, s := lex(s)
+ if tokenType == notTokenType {
+ c.Not = true
+ tokenType, tokenStr, s = lex(s)
+ }
+ switch tokenType {
+ case strTokenType, angleTokenType:
+ c.Token = tokenStr
+ case squareTokenType:
+ c.ETag = tokenStr
+ default:
+ return Condition{}, "", false
+ }
+ return c, s, true
+}
+
+// Single-rune tokens like '(' or ')' have a token type equal to their rune.
+// All other tokens have a negative token type.
+const (
+ errTokenType = rune(-1)
+ eofTokenType = rune(-2)
+ strTokenType = rune(-3)
+ notTokenType = rune(-4)
+ angleTokenType = rune(-5)
+ squareTokenType = rune(-6)
+)
+
+func lex(s string) (tokenType rune, tokenStr string, remaining string) {
+ // The net/textproto Reader that parses the HTTP header will collapse
+ // Linear White Space that spans multiple "\r\n" lines to a single " ",
+ // so we don't need to look for '\r' or '\n'.
+ for len(s) > 0 && (s[0] == '\t' || s[0] == ' ') {
+ s = s[1:]
+ }
+ if len(s) == 0 {
+ return eofTokenType, "", ""
+ }
+ i := 0
+loop:
+ for ; i < len(s); i++ {
+ switch s[i] {
+ case '\t', ' ', '(', ')', '<', '>', '[', ']':
+ break loop
+ }
+ }
+
+ if i != 0 {
+ tokenStr, remaining = s[:i], s[i:]
+ if tokenStr == "Not" {
+ return notTokenType, "", remaining
+ }
+ return strTokenType, tokenStr, remaining
+ }
+
+ j := 0
+ switch s[0] {
+ case '<':
+ j, tokenType = strings.IndexByte(s, '>'), angleTokenType
+ case '[':
+ j, tokenType = strings.IndexByte(s, ']'), squareTokenType
+ default:
+ return rune(s[0]), "", s[1:]
+ }
+ if j < 0 {
+ return errTokenType, "", ""
+ }
+ return tokenType, s[1:j], s[j+1:]
+}
diff --git a/vendor/golang.org/x/net/webdav/internal/xml/README b/vendor/golang.org/x/net/webdav/internal/xml/README
new file mode 100644
index 0000000..89656f4
--- /dev/null
+++ b/vendor/golang.org/x/net/webdav/internal/xml/README
@@ -0,0 +1,11 @@
+This is a fork of the encoding/xml package at ca1d6c4, the last commit before
+https://go.googlesource.com/go/+/c0d6d33 "encoding/xml: restore Go 1.4 name
+space behavior" made late in the lead-up to the Go 1.5 release.
+
+The list of encoding/xml changes is at
+https://go.googlesource.com/go/+log/master/src/encoding/xml
+
+This fork is temporary, and I (nigeltao) expect to revert it after Go 1.6 is
+released.
+
+See http://golang.org/issue/11841
diff --git a/vendor/golang.org/x/net/webdav/internal/xml/marshal.go b/vendor/golang.org/x/net/webdav/internal/xml/marshal.go
new file mode 100644
index 0000000..cb82ec2
--- /dev/null
+++ b/vendor/golang.org/x/net/webdav/internal/xml/marshal.go
@@ -0,0 +1,1223 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xml
+
+import (
+ "bufio"
+ "bytes"
+ "encoding"
+ "fmt"
+ "io"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+const (
+ // A generic XML header suitable for use with the output of Marshal.
+ // This is not automatically added to any output of this package,
+ // it is provided as a convenience.
+ Header = `` + "\n"
+)
+
+// Marshal returns the XML encoding of v.
+//
+// Marshal handles an array or slice by marshalling each of the elements.
+// Marshal handles a pointer by marshalling the value it points at or, if the
+// pointer is nil, by writing nothing. Marshal handles an interface value by
+// marshalling the value it contains or, if the interface value is nil, by
+// writing nothing. Marshal handles all other data by writing one or more XML
+// elements containing the data.
+//
+// The name for the XML elements is taken from, in order of preference:
+// - the tag on the XMLName field, if the data is a struct
+// - the value of the XMLName field of type xml.Name
+// - the tag of the struct field used to obtain the data
+// - the name of the struct field used to obtain the data
+// - the name of the marshalled type
+//
+// The XML element for a struct contains marshalled elements for each of the
+// exported fields of the struct, with these exceptions:
+// - the XMLName field, described above, is omitted.
+// - a field with tag "-" is omitted.
+// - a field with tag "name,attr" becomes an attribute with
+// the given name in the XML element.
+// - a field with tag ",attr" becomes an attribute with the
+// field name in the XML element.
+// - a field with tag ",chardata" is written as character data,
+// not as an XML element.
+// - a field with tag ",innerxml" is written verbatim, not subject
+// to the usual marshalling procedure.
+// - a field with tag ",comment" is written as an XML comment, not
+// subject to the usual marshalling procedure. It must not contain
+// the "--" string within it.
+// - a field with a tag including the "omitempty" option is omitted
+// if the field value is empty. The empty values are false, 0, any
+// nil pointer or interface value, and any array, slice, map, or
+// string of length zero.
+// - an anonymous struct field is handled as if the fields of its
+// value were part of the outer struct.
+//
+// If a field uses a tag "a>b>c", then the element c will be nested inside
+// parent elements a and b. Fields that appear next to each other that name
+// the same parent will be enclosed in one XML element.
+//
+// See MarshalIndent for an example.
+//
+// Marshal will return an error if asked to marshal a channel, function, or map.
+func Marshal(v interface{}) ([]byte, error) {
+ var b bytes.Buffer
+ if err := NewEncoder(&b).Encode(v); err != nil {
+ return nil, err
+ }
+ return b.Bytes(), nil
+}
+
+// Marshaler is the interface implemented by objects that can marshal
+// themselves into valid XML elements.
+//
+// MarshalXML encodes the receiver as zero or more XML elements.
+// By convention, arrays or slices are typically encoded as a sequence
+// of elements, one per entry.
+// Using start as the element tag is not required, but doing so
+// will enable Unmarshal to match the XML elements to the correct
+// struct field.
+// One common implementation strategy is to construct a separate
+// value with a layout corresponding to the desired XML and then
+// to encode it using e.EncodeElement.
+// Another common strategy is to use repeated calls to e.EncodeToken
+// to generate the XML output one token at a time.
+// The sequence of encoded tokens must make up zero or more valid
+// XML elements.
+type Marshaler interface {
+ MarshalXML(e *Encoder, start StartElement) error
+}
+
+// MarshalerAttr is the interface implemented by objects that can marshal
+// themselves into valid XML attributes.
+//
+// MarshalXMLAttr returns an XML attribute with the encoded value of the receiver.
+// Using name as the attribute name is not required, but doing so
+// will enable Unmarshal to match the attribute to the correct
+// struct field.
+// If MarshalXMLAttr returns the zero attribute Attr{}, no attribute
+// will be generated in the output.
+// MarshalXMLAttr is used only for struct fields with the
+// "attr" option in the field tag.
+type MarshalerAttr interface {
+ MarshalXMLAttr(name Name) (Attr, error)
+}
+
+// MarshalIndent works like Marshal, but each XML element begins on a new
+// indented line that starts with prefix and is followed by one or more
+// copies of indent according to the nesting depth.
+func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
+ var b bytes.Buffer
+ enc := NewEncoder(&b)
+ enc.Indent(prefix, indent)
+ if err := enc.Encode(v); err != nil {
+ return nil, err
+ }
+ return b.Bytes(), nil
+}
+
+// An Encoder writes XML data to an output stream.
+type Encoder struct {
+ p printer
+}
+
+// NewEncoder returns a new encoder that writes to w.
+func NewEncoder(w io.Writer) *Encoder {
+ e := &Encoder{printer{Writer: bufio.NewWriter(w)}}
+ e.p.encoder = e
+ return e
+}
+
+// Indent sets the encoder to generate XML in which each element
+// begins on a new indented line that starts with prefix and is followed by
+// one or more copies of indent according to the nesting depth.
+func (enc *Encoder) Indent(prefix, indent string) {
+ enc.p.prefix = prefix
+ enc.p.indent = indent
+}
+
+// Encode writes the XML encoding of v to the stream.
+//
+// See the documentation for Marshal for details about the conversion
+// of Go values to XML.
+//
+// Encode calls Flush before returning.
+func (enc *Encoder) Encode(v interface{}) error {
+ err := enc.p.marshalValue(reflect.ValueOf(v), nil, nil)
+ if err != nil {
+ return err
+ }
+ return enc.p.Flush()
+}
+
+// EncodeElement writes the XML encoding of v to the stream,
+// using start as the outermost tag in the encoding.
+//
+// See the documentation for Marshal for details about the conversion
+// of Go values to XML.
+//
+// EncodeElement calls Flush before returning.
+func (enc *Encoder) EncodeElement(v interface{}, start StartElement) error {
+ err := enc.p.marshalValue(reflect.ValueOf(v), nil, &start)
+ if err != nil {
+ return err
+ }
+ return enc.p.Flush()
+}
+
+var (
+ begComment = []byte("")
+ endProcInst = []byte("?>")
+ endDirective = []byte(">")
+)
+
+// EncodeToken writes the given XML token to the stream.
+// It returns an error if StartElement and EndElement tokens are not
+// properly matched.
+//
+// EncodeToken does not call Flush, because usually it is part of a
+// larger operation such as Encode or EncodeElement (or a custom
+// Marshaler's MarshalXML invoked during those), and those will call
+// Flush when finished. Callers that create an Encoder and then invoke
+// EncodeToken directly, without using Encode or EncodeElement, need to
+// call Flush when finished to ensure that the XML is written to the
+// underlying writer.
+//
+// EncodeToken allows writing a ProcInst with Target set to "xml" only
+// as the first token in the stream.
+//
+// When encoding a StartElement holding an XML namespace prefix
+// declaration for a prefix that is not already declared, contained
+// elements (including the StartElement itself) will use the declared
+// prefix when encoding names with matching namespace URIs.
+func (enc *Encoder) EncodeToken(t Token) error {
+
+ p := &enc.p
+ switch t := t.(type) {
+ case StartElement:
+ if err := p.writeStart(&t); err != nil {
+ return err
+ }
+ case EndElement:
+ if err := p.writeEnd(t.Name); err != nil {
+ return err
+ }
+ case CharData:
+ escapeText(p, t, false)
+ case Comment:
+ if bytes.Contains(t, endComment) {
+ return fmt.Errorf("xml: EncodeToken of Comment containing --> marker")
+ }
+ p.WriteString("")
+ return p.cachedWriteError()
+ case ProcInst:
+ // First token to be encoded which is also a ProcInst with target of xml
+ // is the xml declaration. The only ProcInst where target of xml is allowed.
+ if t.Target == "xml" && p.Buffered() != 0 {
+ return fmt.Errorf("xml: EncodeToken of ProcInst xml target only valid for xml declaration, first token encoded")
+ }
+ if !isNameString(t.Target) {
+ return fmt.Errorf("xml: EncodeToken of ProcInst with invalid Target")
+ }
+ if bytes.Contains(t.Inst, endProcInst) {
+ return fmt.Errorf("xml: EncodeToken of ProcInst containing ?> marker")
+ }
+ p.WriteString("")
+ p.WriteString(t.Target)
+ if len(t.Inst) > 0 {
+ p.WriteByte(' ')
+ p.Write(t.Inst)
+ }
+ p.WriteString("?>")
+ case Directive:
+ if !isValidDirective(t) {
+ return fmt.Errorf("xml: EncodeToken of Directive containing wrong < or > markers")
+ }
+ p.WriteString("")
+ default:
+ return fmt.Errorf("xml: EncodeToken of invalid token type")
+
+ }
+ return p.cachedWriteError()
+}
+
+// isValidDirective reports whether dir is a valid directive text,
+// meaning angle brackets are matched, ignoring comments and strings.
+func isValidDirective(dir Directive) bool {
+ var (
+ depth int
+ inquote uint8
+ incomment bool
+ )
+ for i, c := range dir {
+ switch {
+ case incomment:
+ if c == '>' {
+ if n := 1 + i - len(endComment); n >= 0 && bytes.Equal(dir[n:i+1], endComment) {
+ incomment = false
+ }
+ }
+ // Just ignore anything in comment
+ case inquote != 0:
+ if c == inquote {
+ inquote = 0
+ }
+ // Just ignore anything within quotes
+ case c == '\'' || c == '"':
+ inquote = c
+ case c == '<':
+ if i+len(begComment) < len(dir) && bytes.Equal(dir[i:i+len(begComment)], begComment) {
+ incomment = true
+ } else {
+ depth++
+ }
+ case c == '>':
+ if depth == 0 {
+ return false
+ }
+ depth--
+ }
+ }
+ return depth == 0 && inquote == 0 && !incomment
+}
+
+// Flush flushes any buffered XML to the underlying writer.
+// See the EncodeToken documentation for details about when it is necessary.
+func (enc *Encoder) Flush() error {
+ return enc.p.Flush()
+}
+
+type printer struct {
+ *bufio.Writer
+ encoder *Encoder
+ seq int
+ indent string
+ prefix string
+ depth int
+ indentedIn bool
+ putNewline bool
+ defaultNS string
+ attrNS map[string]string // map prefix -> name space
+ attrPrefix map[string]string // map name space -> prefix
+ prefixes []printerPrefix
+ tags []Name
+}
+
+// printerPrefix holds a namespace undo record.
+// When an element is popped, the prefix record
+// is set back to the recorded URL. The empty
+// prefix records the URL for the default name space.
+//
+// The start of an element is recorded with an element
+// that has mark=true.
+type printerPrefix struct {
+ prefix string
+ url string
+ mark bool
+}
+
+func (p *printer) prefixForNS(url string, isAttr bool) string {
+ // The "http://www.w3.org/XML/1998/namespace" name space is predefined as "xml"
+ // and must be referred to that way.
+ // (The "http://www.w3.org/2000/xmlns/" name space is also predefined as "xmlns",
+ // but users should not be trying to use that one directly - that's our job.)
+ if url == xmlURL {
+ return "xml"
+ }
+ if !isAttr && url == p.defaultNS {
+ // We can use the default name space.
+ return ""
+ }
+ return p.attrPrefix[url]
+}
+
+// defineNS pushes any namespace definition found in the given attribute.
+// If ignoreNonEmptyDefault is true, an xmlns="nonempty"
+// attribute will be ignored.
+func (p *printer) defineNS(attr Attr, ignoreNonEmptyDefault bool) error {
+ var prefix string
+ if attr.Name.Local == "xmlns" {
+ if attr.Name.Space != "" && attr.Name.Space != "xml" && attr.Name.Space != xmlURL {
+ return fmt.Errorf("xml: cannot redefine xmlns attribute prefix")
+ }
+ } else if attr.Name.Space == "xmlns" && attr.Name.Local != "" {
+ prefix = attr.Name.Local
+ if attr.Value == "" {
+ // Technically, an empty XML namespace is allowed for an attribute.
+ // From http://www.w3.org/TR/xml-names11/#scoping-defaulting:
+ //
+ // The attribute value in a namespace declaration for a prefix may be
+ // empty. This has the effect, within the scope of the declaration, of removing
+ // any association of the prefix with a namespace name.
+ //
+ // However our namespace prefixes here are used only as hints. There's
+ // no need to respect the removal of a namespace prefix, so we ignore it.
+ return nil
+ }
+ } else {
+ // Ignore: it's not a namespace definition
+ return nil
+ }
+ if prefix == "" {
+ if attr.Value == p.defaultNS {
+ // No need for redefinition.
+ return nil
+ }
+ if attr.Value != "" && ignoreNonEmptyDefault {
+ // We have an xmlns="..." value but
+ // it can't define a name space in this context,
+ // probably because the element has an empty
+ // name space. In this case, we just ignore
+ // the name space declaration.
+ return nil
+ }
+ } else if _, ok := p.attrPrefix[attr.Value]; ok {
+ // There's already a prefix for the given name space,
+ // so use that. This prevents us from
+ // having two prefixes for the same name space
+ // so attrNS and attrPrefix can remain bijective.
+ return nil
+ }
+ p.pushPrefix(prefix, attr.Value)
+ return nil
+}
+
+// createNSPrefix creates a name space prefix attribute
+// to use for the given name space, defining a new prefix
+// if necessary.
+// If isAttr is true, the prefix is to be created for an attribute
+// prefix, which means that the default name space cannot
+// be used.
+func (p *printer) createNSPrefix(url string, isAttr bool) {
+ if _, ok := p.attrPrefix[url]; ok {
+ // We already have a prefix for the given URL.
+ return
+ }
+ switch {
+ case !isAttr && url == p.defaultNS:
+ // We can use the default name space.
+ return
+ case url == "":
+ // The only way we can encode names in the empty
+ // name space is by using the default name space,
+ // so we must use that.
+ if p.defaultNS != "" {
+ // The default namespace is non-empty, so we
+ // need to set it to empty.
+ p.pushPrefix("", "")
+ }
+ return
+ case url == xmlURL:
+ return
+ }
+ // TODO If the URL is an existing prefix, we could
+ // use it as is. That would enable the
+ // marshaling of elements that had been unmarshaled
+ // and with a name space prefix that was not found.
+ // although technically it would be incorrect.
+
+ // Pick a name. We try to use the final element of the path
+ // but fall back to _.
+ prefix := strings.TrimRight(url, "/")
+ if i := strings.LastIndex(prefix, "/"); i >= 0 {
+ prefix = prefix[i+1:]
+ }
+ if prefix == "" || !isName([]byte(prefix)) || strings.Contains(prefix, ":") {
+ prefix = "_"
+ }
+ if strings.HasPrefix(prefix, "xml") {
+ // xmlanything is reserved.
+ prefix = "_" + prefix
+ }
+ if p.attrNS[prefix] != "" {
+ // Name is taken. Find a better one.
+ for p.seq++; ; p.seq++ {
+ if id := prefix + "_" + strconv.Itoa(p.seq); p.attrNS[id] == "" {
+ prefix = id
+ break
+ }
+ }
+ }
+
+ p.pushPrefix(prefix, url)
+}
+
+// writeNamespaces writes xmlns attributes for all the
+// namespace prefixes that have been defined in
+// the current element.
+func (p *printer) writeNamespaces() {
+ for i := len(p.prefixes) - 1; i >= 0; i-- {
+ prefix := p.prefixes[i]
+ if prefix.mark {
+ return
+ }
+ p.WriteString(" ")
+ if prefix.prefix == "" {
+ // Default name space.
+ p.WriteString(`xmlns="`)
+ } else {
+ p.WriteString("xmlns:")
+ p.WriteString(prefix.prefix)
+ p.WriteString(`="`)
+ }
+ EscapeText(p, []byte(p.nsForPrefix(prefix.prefix)))
+ p.WriteString(`"`)
+ }
+}
+
+// pushPrefix pushes a new prefix on the prefix stack
+// without checking to see if it is already defined.
+func (p *printer) pushPrefix(prefix, url string) {
+ p.prefixes = append(p.prefixes, printerPrefix{
+ prefix: prefix,
+ url: p.nsForPrefix(prefix),
+ })
+ p.setAttrPrefix(prefix, url)
+}
+
+// nsForPrefix returns the name space for the given
+// prefix. Note that this is not valid for the
+// empty attribute prefix, which always has an empty
+// name space.
+func (p *printer) nsForPrefix(prefix string) string {
+ if prefix == "" {
+ return p.defaultNS
+ }
+ return p.attrNS[prefix]
+}
+
+// markPrefix marks the start of an element on the prefix
+// stack.
+func (p *printer) markPrefix() {
+ p.prefixes = append(p.prefixes, printerPrefix{
+ mark: true,
+ })
+}
+
+// popPrefix pops all defined prefixes for the current
+// element.
+func (p *printer) popPrefix() {
+ for len(p.prefixes) > 0 {
+ prefix := p.prefixes[len(p.prefixes)-1]
+ p.prefixes = p.prefixes[:len(p.prefixes)-1]
+ if prefix.mark {
+ break
+ }
+ p.setAttrPrefix(prefix.prefix, prefix.url)
+ }
+}
+
+// setAttrPrefix sets an attribute name space prefix.
+// If url is empty, the attribute is removed.
+// If prefix is empty, the default name space is set.
+func (p *printer) setAttrPrefix(prefix, url string) {
+ if prefix == "" {
+ p.defaultNS = url
+ return
+ }
+ if url == "" {
+ delete(p.attrPrefix, p.attrNS[prefix])
+ delete(p.attrNS, prefix)
+ return
+ }
+ if p.attrPrefix == nil {
+ // Need to define a new name space.
+ p.attrPrefix = make(map[string]string)
+ p.attrNS = make(map[string]string)
+ }
+ // Remove any old prefix value. This is OK because we maintain a
+ // strict one-to-one mapping between prefix and URL (see
+ // defineNS)
+ delete(p.attrPrefix, p.attrNS[prefix])
+ p.attrPrefix[url] = prefix
+ p.attrNS[prefix] = url
+}
+
+var (
+ marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
+ marshalerAttrType = reflect.TypeOf((*MarshalerAttr)(nil)).Elem()
+ textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
+)
+
+// marshalValue writes one or more XML elements representing val.
+// If val was obtained from a struct field, finfo must have its details.
+func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplate *StartElement) error {
+ if startTemplate != nil && startTemplate.Name.Local == "" {
+ return fmt.Errorf("xml: EncodeElement of StartElement with missing name")
+ }
+
+ if !val.IsValid() {
+ return nil
+ }
+ if finfo != nil && finfo.flags&fOmitEmpty != 0 && isEmptyValue(val) {
+ return nil
+ }
+
+ // Drill into interfaces and pointers.
+ // This can turn into an infinite loop given a cyclic chain,
+ // but it matches the Go 1 behavior.
+ for val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr {
+ if val.IsNil() {
+ return nil
+ }
+ val = val.Elem()
+ }
+
+ kind := val.Kind()
+ typ := val.Type()
+
+ // Check for marshaler.
+ if val.CanInterface() && typ.Implements(marshalerType) {
+ return p.marshalInterface(val.Interface().(Marshaler), p.defaultStart(typ, finfo, startTemplate))
+ }
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(marshalerType) {
+ return p.marshalInterface(pv.Interface().(Marshaler), p.defaultStart(pv.Type(), finfo, startTemplate))
+ }
+ }
+
+ // Check for text marshaler.
+ if val.CanInterface() && typ.Implements(textMarshalerType) {
+ return p.marshalTextInterface(val.Interface().(encoding.TextMarshaler), p.defaultStart(typ, finfo, startTemplate))
+ }
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
+ return p.marshalTextInterface(pv.Interface().(encoding.TextMarshaler), p.defaultStart(pv.Type(), finfo, startTemplate))
+ }
+ }
+
+ // Slices and arrays iterate over the elements. They do not have an enclosing tag.
+ if (kind == reflect.Slice || kind == reflect.Array) && typ.Elem().Kind() != reflect.Uint8 {
+ for i, n := 0, val.Len(); i < n; i++ {
+ if err := p.marshalValue(val.Index(i), finfo, startTemplate); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+
+ tinfo, err := getTypeInfo(typ)
+ if err != nil {
+ return err
+ }
+
+ // Create start element.
+ // Precedence for the XML element name is:
+ // 0. startTemplate
+ // 1. XMLName field in underlying struct;
+ // 2. field name/tag in the struct field; and
+ // 3. type name
+ var start StartElement
+
+ // explicitNS records whether the element's name space has been
+ // explicitly set (for example an XMLName field).
+ explicitNS := false
+
+ if startTemplate != nil {
+ start.Name = startTemplate.Name
+ explicitNS = true
+ start.Attr = append(start.Attr, startTemplate.Attr...)
+ } else if tinfo.xmlname != nil {
+ xmlname := tinfo.xmlname
+ if xmlname.name != "" {
+ start.Name.Space, start.Name.Local = xmlname.xmlns, xmlname.name
+ } else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" {
+ start.Name = v
+ }
+ explicitNS = true
+ }
+ if start.Name.Local == "" && finfo != nil {
+ start.Name.Local = finfo.name
+ if finfo.xmlns != "" {
+ start.Name.Space = finfo.xmlns
+ explicitNS = true
+ }
+ }
+ if start.Name.Local == "" {
+ name := typ.Name()
+ if name == "" {
+ return &UnsupportedTypeError{typ}
+ }
+ start.Name.Local = name
+ }
+
+ // defaultNS records the default name space as set by a xmlns="..."
+ // attribute. We don't set p.defaultNS because we want to let
+ // the attribute writing code (in p.defineNS) be solely responsible
+ // for maintaining that.
+ defaultNS := p.defaultNS
+
+ // Attributes
+ for i := range tinfo.fields {
+ finfo := &tinfo.fields[i]
+ if finfo.flags&fAttr == 0 {
+ continue
+ }
+ attr, err := p.fieldAttr(finfo, val)
+ if err != nil {
+ return err
+ }
+ if attr.Name.Local == "" {
+ continue
+ }
+ start.Attr = append(start.Attr, attr)
+ if attr.Name.Space == "" && attr.Name.Local == "xmlns" {
+ defaultNS = attr.Value
+ }
+ }
+ if !explicitNS {
+ // Historic behavior: elements use the default name space
+ // they are contained in by default.
+ start.Name.Space = defaultNS
+ }
+ // Historic behaviour: an element that's in a namespace sets
+ // the default namespace for all elements contained within it.
+ start.setDefaultNamespace()
+
+ if err := p.writeStart(&start); err != nil {
+ return err
+ }
+
+ if val.Kind() == reflect.Struct {
+ err = p.marshalStruct(tinfo, val)
+ } else {
+ s, b, err1 := p.marshalSimple(typ, val)
+ if err1 != nil {
+ err = err1
+ } else if b != nil {
+ EscapeText(p, b)
+ } else {
+ p.EscapeString(s)
+ }
+ }
+ if err != nil {
+ return err
+ }
+
+ if err := p.writeEnd(start.Name); err != nil {
+ return err
+ }
+
+ return p.cachedWriteError()
+}
+
+// fieldAttr returns the attribute of the given field.
+// If the returned attribute has an empty Name.Local,
+// it should not be used.
+// The given value holds the value containing the field.
+func (p *printer) fieldAttr(finfo *fieldInfo, val reflect.Value) (Attr, error) {
+ fv := finfo.value(val)
+ name := Name{Space: finfo.xmlns, Local: finfo.name}
+ if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
+ return Attr{}, nil
+ }
+ if fv.Kind() == reflect.Interface && fv.IsNil() {
+ return Attr{}, nil
+ }
+ if fv.CanInterface() && fv.Type().Implements(marshalerAttrType) {
+ attr, err := fv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
+ return attr, err
+ }
+ if fv.CanAddr() {
+ pv := fv.Addr()
+ if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) {
+ attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
+ return attr, err
+ }
+ }
+ if fv.CanInterface() && fv.Type().Implements(textMarshalerType) {
+ text, err := fv.Interface().(encoding.TextMarshaler).MarshalText()
+ if err != nil {
+ return Attr{}, err
+ }
+ return Attr{name, string(text)}, nil
+ }
+ if fv.CanAddr() {
+ pv := fv.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
+ text, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
+ if err != nil {
+ return Attr{}, err
+ }
+ return Attr{name, string(text)}, nil
+ }
+ }
+ // Dereference or skip nil pointer, interface values.
+ switch fv.Kind() {
+ case reflect.Ptr, reflect.Interface:
+ if fv.IsNil() {
+ return Attr{}, nil
+ }
+ fv = fv.Elem()
+ }
+ s, b, err := p.marshalSimple(fv.Type(), fv)
+ if err != nil {
+ return Attr{}, err
+ }
+ if b != nil {
+ s = string(b)
+ }
+ return Attr{name, s}, nil
+}
+
+// defaultStart returns the default start element to use,
+// given the reflect type, field info, and start template.
+func (p *printer) defaultStart(typ reflect.Type, finfo *fieldInfo, startTemplate *StartElement) StartElement {
+ var start StartElement
+ // Precedence for the XML element name is as above,
+ // except that we do not look inside structs for the first field.
+ if startTemplate != nil {
+ start.Name = startTemplate.Name
+ start.Attr = append(start.Attr, startTemplate.Attr...)
+ } else if finfo != nil && finfo.name != "" {
+ start.Name.Local = finfo.name
+ start.Name.Space = finfo.xmlns
+ } else if typ.Name() != "" {
+ start.Name.Local = typ.Name()
+ } else {
+ // Must be a pointer to a named type,
+ // since it has the Marshaler methods.
+ start.Name.Local = typ.Elem().Name()
+ }
+ // Historic behaviour: elements use the name space of
+ // the element they are contained in by default.
+ if start.Name.Space == "" {
+ start.Name.Space = p.defaultNS
+ }
+ start.setDefaultNamespace()
+ return start
+}
+
+// marshalInterface marshals a Marshaler interface value.
+func (p *printer) marshalInterface(val Marshaler, start StartElement) error {
+ // Push a marker onto the tag stack so that MarshalXML
+ // cannot close the XML tags that it did not open.
+ p.tags = append(p.tags, Name{})
+ n := len(p.tags)
+
+ err := val.MarshalXML(p.encoder, start)
+ if err != nil {
+ return err
+ }
+
+ // Make sure MarshalXML closed all its tags. p.tags[n-1] is the mark.
+ if len(p.tags) > n {
+ return fmt.Errorf("xml: %s.MarshalXML wrote invalid XML: <%s> not closed", receiverType(val), p.tags[len(p.tags)-1].Local)
+ }
+ p.tags = p.tags[:n-1]
+ return nil
+}
+
+// marshalTextInterface marshals a TextMarshaler interface value.
+func (p *printer) marshalTextInterface(val encoding.TextMarshaler, start StartElement) error {
+ if err := p.writeStart(&start); err != nil {
+ return err
+ }
+ text, err := val.MarshalText()
+ if err != nil {
+ return err
+ }
+ EscapeText(p, text)
+ return p.writeEnd(start.Name)
+}
+
+// writeStart writes the given start element.
+func (p *printer) writeStart(start *StartElement) error {
+ if start.Name.Local == "" {
+ return fmt.Errorf("xml: start tag with no name")
+ }
+
+ p.tags = append(p.tags, start.Name)
+ p.markPrefix()
+ // Define any name spaces explicitly declared in the attributes.
+ // We do this as a separate pass so that explicitly declared prefixes
+ // will take precedence over implicitly declared prefixes
+ // regardless of the order of the attributes.
+ ignoreNonEmptyDefault := start.Name.Space == ""
+ for _, attr := range start.Attr {
+ if err := p.defineNS(attr, ignoreNonEmptyDefault); err != nil {
+ return err
+ }
+ }
+ // Define any new name spaces implied by the attributes.
+ for _, attr := range start.Attr {
+ name := attr.Name
+ // From http://www.w3.org/TR/xml-names11/#defaulting
+ // "Default namespace declarations do not apply directly
+ // to attribute names; the interpretation of unprefixed
+ // attributes is determined by the element on which they
+ // appear."
+ // This means we don't need to create a new namespace
+ // when an attribute name space is empty.
+ if name.Space != "" && !name.isNamespace() {
+ p.createNSPrefix(name.Space, true)
+ }
+ }
+ p.createNSPrefix(start.Name.Space, false)
+
+ p.writeIndent(1)
+ p.WriteByte('<')
+ p.writeName(start.Name, false)
+ p.writeNamespaces()
+ for _, attr := range start.Attr {
+ name := attr.Name
+ if name.Local == "" || name.isNamespace() {
+ // Namespaces have already been written by writeNamespaces above.
+ continue
+ }
+ p.WriteByte(' ')
+ p.writeName(name, true)
+ p.WriteString(`="`)
+ p.EscapeString(attr.Value)
+ p.WriteByte('"')
+ }
+ p.WriteByte('>')
+ return nil
+}
+
+// writeName writes the given name. It assumes
+// that p.createNSPrefix(name) has already been called.
+func (p *printer) writeName(name Name, isAttr bool) {
+ if prefix := p.prefixForNS(name.Space, isAttr); prefix != "" {
+ p.WriteString(prefix)
+ p.WriteByte(':')
+ }
+ p.WriteString(name.Local)
+}
+
+func (p *printer) writeEnd(name Name) error {
+ if name.Local == "" {
+ return fmt.Errorf("xml: end tag with no name")
+ }
+ if len(p.tags) == 0 || p.tags[len(p.tags)-1].Local == "" {
+ return fmt.Errorf("xml: end tag %s> without start tag", name.Local)
+ }
+ if top := p.tags[len(p.tags)-1]; top != name {
+ if top.Local != name.Local {
+ return fmt.Errorf("xml: end tag %s> does not match start tag <%s>", name.Local, top.Local)
+ }
+ return fmt.Errorf("xml: end tag %s> in namespace %s does not match start tag <%s> in namespace %s", name.Local, name.Space, top.Local, top.Space)
+ }
+ p.tags = p.tags[:len(p.tags)-1]
+
+ p.writeIndent(-1)
+ p.WriteByte('<')
+ p.WriteByte('/')
+ p.writeName(name, false)
+ p.WriteByte('>')
+ p.popPrefix()
+ return nil
+}
+
+func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) (string, []byte, error) {
+ switch val.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return strconv.FormatInt(val.Int(), 10), nil, nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return strconv.FormatUint(val.Uint(), 10), nil, nil
+ case reflect.Float32, reflect.Float64:
+ return strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits()), nil, nil
+ case reflect.String:
+ return val.String(), nil, nil
+ case reflect.Bool:
+ return strconv.FormatBool(val.Bool()), nil, nil
+ case reflect.Array:
+ if typ.Elem().Kind() != reflect.Uint8 {
+ break
+ }
+ // [...]byte
+ var bytes []byte
+ if val.CanAddr() {
+ bytes = val.Slice(0, val.Len()).Bytes()
+ } else {
+ bytes = make([]byte, val.Len())
+ reflect.Copy(reflect.ValueOf(bytes), val)
+ }
+ return "", bytes, nil
+ case reflect.Slice:
+ if typ.Elem().Kind() != reflect.Uint8 {
+ break
+ }
+ // []byte
+ return "", val.Bytes(), nil
+ }
+ return "", nil, &UnsupportedTypeError{typ}
+}
+
+var ddBytes = []byte("--")
+
+func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
+ s := parentStack{p: p}
+ for i := range tinfo.fields {
+ finfo := &tinfo.fields[i]
+ if finfo.flags&fAttr != 0 {
+ continue
+ }
+ vf := finfo.value(val)
+
+ // Dereference or skip nil pointer, interface values.
+ switch vf.Kind() {
+ case reflect.Ptr, reflect.Interface:
+ if !vf.IsNil() {
+ vf = vf.Elem()
+ }
+ }
+
+ switch finfo.flags & fMode {
+ case fCharData:
+ if err := s.setParents(&noField, reflect.Value{}); err != nil {
+ return err
+ }
+ if vf.CanInterface() && vf.Type().Implements(textMarshalerType) {
+ data, err := vf.Interface().(encoding.TextMarshaler).MarshalText()
+ if err != nil {
+ return err
+ }
+ Escape(p, data)
+ continue
+ }
+ if vf.CanAddr() {
+ pv := vf.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
+ data, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
+ if err != nil {
+ return err
+ }
+ Escape(p, data)
+ continue
+ }
+ }
+ var scratch [64]byte
+ switch vf.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ Escape(p, strconv.AppendInt(scratch[:0], vf.Int(), 10))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ Escape(p, strconv.AppendUint(scratch[:0], vf.Uint(), 10))
+ case reflect.Float32, reflect.Float64:
+ Escape(p, strconv.AppendFloat(scratch[:0], vf.Float(), 'g', -1, vf.Type().Bits()))
+ case reflect.Bool:
+ Escape(p, strconv.AppendBool(scratch[:0], vf.Bool()))
+ case reflect.String:
+ if err := EscapeText(p, []byte(vf.String())); err != nil {
+ return err
+ }
+ case reflect.Slice:
+ if elem, ok := vf.Interface().([]byte); ok {
+ if err := EscapeText(p, elem); err != nil {
+ return err
+ }
+ }
+ }
+ continue
+
+ case fComment:
+ if err := s.setParents(&noField, reflect.Value{}); err != nil {
+ return err
+ }
+ k := vf.Kind()
+ if !(k == reflect.String || k == reflect.Slice && vf.Type().Elem().Kind() == reflect.Uint8) {
+ return fmt.Errorf("xml: bad type for comment field of %s", val.Type())
+ }
+ if vf.Len() == 0 {
+ continue
+ }
+ p.writeIndent(0)
+ p.WriteString("" is invalid grammar. Make it "- -->"
+ p.WriteByte(' ')
+ }
+ p.WriteString("-->")
+ continue
+
+ case fInnerXml:
+ iface := vf.Interface()
+ switch raw := iface.(type) {
+ case []byte:
+ p.Write(raw)
+ continue
+ case string:
+ p.WriteString(raw)
+ continue
+ }
+
+ case fElement, fElement | fAny:
+ if err := s.setParents(finfo, vf); err != nil {
+ return err
+ }
+ }
+ if err := p.marshalValue(vf, finfo, nil); err != nil {
+ return err
+ }
+ }
+ if err := s.setParents(&noField, reflect.Value{}); err != nil {
+ return err
+ }
+ return p.cachedWriteError()
+}
+
+var noField fieldInfo
+
+// return the bufio Writer's cached write error
+func (p *printer) cachedWriteError() error {
+ _, err := p.Write(nil)
+ return err
+}
+
+func (p *printer) writeIndent(depthDelta int) {
+ if len(p.prefix) == 0 && len(p.indent) == 0 {
+ return
+ }
+ if depthDelta < 0 {
+ p.depth--
+ if p.indentedIn {
+ p.indentedIn = false
+ return
+ }
+ p.indentedIn = false
+ }
+ if p.putNewline {
+ p.WriteByte('\n')
+ } else {
+ p.putNewline = true
+ }
+ if len(p.prefix) > 0 {
+ p.WriteString(p.prefix)
+ }
+ if len(p.indent) > 0 {
+ for i := 0; i < p.depth; i++ {
+ p.WriteString(p.indent)
+ }
+ }
+ if depthDelta > 0 {
+ p.depth++
+ p.indentedIn = true
+ }
+}
+
+type parentStack struct {
+ p *printer
+ xmlns string
+ parents []string
+}
+
+// setParents sets the stack of current parents to those found in finfo.
+// It only writes the start elements if vf holds a non-nil value.
+// If finfo is &noField, it pops all elements.
+func (s *parentStack) setParents(finfo *fieldInfo, vf reflect.Value) error {
+ xmlns := s.p.defaultNS
+ if finfo.xmlns != "" {
+ xmlns = finfo.xmlns
+ }
+ commonParents := 0
+ if xmlns == s.xmlns {
+ for ; commonParents < len(finfo.parents) && commonParents < len(s.parents); commonParents++ {
+ if finfo.parents[commonParents] != s.parents[commonParents] {
+ break
+ }
+ }
+ }
+ // Pop off any parents that aren't in common with the previous field.
+ for i := len(s.parents) - 1; i >= commonParents; i-- {
+ if err := s.p.writeEnd(Name{
+ Space: s.xmlns,
+ Local: s.parents[i],
+ }); err != nil {
+ return err
+ }
+ }
+ s.parents = finfo.parents
+ s.xmlns = xmlns
+ if commonParents >= len(s.parents) {
+ // No new elements to push.
+ return nil
+ }
+ if (vf.Kind() == reflect.Ptr || vf.Kind() == reflect.Interface) && vf.IsNil() {
+ // The element is nil, so no need for the start elements.
+ s.parents = s.parents[:commonParents]
+ return nil
+ }
+ // Push any new parents required.
+ for _, name := range s.parents[commonParents:] {
+ start := &StartElement{
+ Name: Name{
+ Space: s.xmlns,
+ Local: name,
+ },
+ }
+ // Set the default name space for parent elements
+ // to match what we do with other elements.
+ if s.xmlns != s.p.defaultNS {
+ start.setDefaultNamespace()
+ }
+ if err := s.p.writeStart(start); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// A MarshalXMLError is returned when Marshal encounters a type
+// that cannot be converted into XML.
+type UnsupportedTypeError struct {
+ Type reflect.Type
+}
+
+func (e *UnsupportedTypeError) Error() string {
+ return "xml: unsupported type: " + e.Type.String()
+}
+
+func isEmptyValue(v reflect.Value) bool {
+ switch v.Kind() {
+ case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+ return v.Len() == 0
+ case reflect.Bool:
+ return !v.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return v.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return v.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return v.Float() == 0
+ case reflect.Interface, reflect.Ptr:
+ return v.IsNil()
+ }
+ return false
+}
diff --git a/vendor/golang.org/x/net/webdav/internal/xml/read.go b/vendor/golang.org/x/net/webdav/internal/xml/read.go
new file mode 100644
index 0000000..4089056
--- /dev/null
+++ b/vendor/golang.org/x/net/webdav/internal/xml/read.go
@@ -0,0 +1,692 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xml
+
+import (
+ "bytes"
+ "encoding"
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+// BUG(rsc): Mapping between XML elements and data structures is inherently flawed:
+// an XML element is an order-dependent collection of anonymous
+// values, while a data structure is an order-independent collection
+// of named values.
+// See package json for a textual representation more suitable
+// to data structures.
+
+// Unmarshal parses the XML-encoded data and stores the result in
+// the value pointed to by v, which must be an arbitrary struct,
+// slice, or string. Well-formed data that does not fit into v is
+// discarded.
+//
+// Because Unmarshal uses the reflect package, it can only assign
+// to exported (upper case) fields. Unmarshal uses a case-sensitive
+// comparison to match XML element names to tag values and struct
+// field names.
+//
+// Unmarshal maps an XML element to a struct using the following rules.
+// In the rules, the tag of a field refers to the value associated with the
+// key 'xml' in the struct field's tag (see the example above).
+//
+// * If the struct has a field of type []byte or string with tag
+// ",innerxml", Unmarshal accumulates the raw XML nested inside the
+// element in that field. The rest of the rules still apply.
+//
+// * If the struct has a field named XMLName of type xml.Name,
+// Unmarshal records the element name in that field.
+//
+// * If the XMLName field has an associated tag of the form
+// "name" or "namespace-URL name", the XML element must have
+// the given name (and, optionally, name space) or else Unmarshal
+// returns an error.
+//
+// * If the XML element has an attribute whose name matches a
+// struct field name with an associated tag containing ",attr" or
+// the explicit name in a struct field tag of the form "name,attr",
+// Unmarshal records the attribute value in that field.
+//
+// * If the XML element contains character data, that data is
+// accumulated in the first struct field that has tag ",chardata".
+// The struct field may have type []byte or string.
+// If there is no such field, the character data is discarded.
+//
+// * If the XML element contains comments, they are accumulated in
+// the first struct field that has tag ",comment". The struct
+// field may have type []byte or string. If there is no such
+// field, the comments are discarded.
+//
+// * If the XML element contains a sub-element whose name matches
+// the prefix of a tag formatted as "a" or "a>b>c", unmarshal
+// will descend into the XML structure looking for elements with the
+// given names, and will map the innermost elements to that struct
+// field. A tag starting with ">" is equivalent to one starting
+// with the field name followed by ">".
+//
+// * If the XML element contains a sub-element whose name matches
+// a struct field's XMLName tag and the struct field has no
+// explicit name tag as per the previous rule, unmarshal maps
+// the sub-element to that struct field.
+//
+// * If the XML element contains a sub-element whose name matches a
+// field without any mode flags (",attr", ",chardata", etc), Unmarshal
+// maps the sub-element to that struct field.
+//
+// * If the XML element contains a sub-element that hasn't matched any
+// of the above rules and the struct has a field with tag ",any",
+// unmarshal maps the sub-element to that struct field.
+//
+// * An anonymous struct field is handled as if the fields of its
+// value were part of the outer struct.
+//
+// * A struct field with tag "-" is never unmarshalled into.
+//
+// Unmarshal maps an XML element to a string or []byte by saving the
+// concatenation of that element's character data in the string or
+// []byte. The saved []byte is never nil.
+//
+// Unmarshal maps an attribute value to a string or []byte by saving
+// the value in the string or slice.
+//
+// Unmarshal maps an XML element to a slice by extending the length of
+// the slice and mapping the element to the newly created value.
+//
+// Unmarshal maps an XML element or attribute value to a bool by
+// setting it to the boolean value represented by the string.
+//
+// Unmarshal maps an XML element or attribute value to an integer or
+// floating-point field by setting the field to the result of
+// interpreting the string value in decimal. There is no check for
+// overflow.
+//
+// Unmarshal maps an XML element to an xml.Name by recording the
+// element name.
+//
+// Unmarshal maps an XML element to a pointer by setting the pointer
+// to a freshly allocated value and then mapping the element to that value.
+//
+func Unmarshal(data []byte, v interface{}) error {
+ return NewDecoder(bytes.NewReader(data)).Decode(v)
+}
+
+// Decode works like xml.Unmarshal, except it reads the decoder
+// stream to find the start element.
+func (d *Decoder) Decode(v interface{}) error {
+ return d.DecodeElement(v, nil)
+}
+
+// DecodeElement works like xml.Unmarshal except that it takes
+// a pointer to the start XML element to decode into v.
+// It is useful when a client reads some raw XML tokens itself
+// but also wants to defer to Unmarshal for some elements.
+func (d *Decoder) DecodeElement(v interface{}, start *StartElement) error {
+ val := reflect.ValueOf(v)
+ if val.Kind() != reflect.Ptr {
+ return errors.New("non-pointer passed to Unmarshal")
+ }
+ return d.unmarshal(val.Elem(), start)
+}
+
+// An UnmarshalError represents an error in the unmarshalling process.
+type UnmarshalError string
+
+func (e UnmarshalError) Error() string { return string(e) }
+
+// Unmarshaler is the interface implemented by objects that can unmarshal
+// an XML element description of themselves.
+//
+// UnmarshalXML decodes a single XML element
+// beginning with the given start element.
+// If it returns an error, the outer call to Unmarshal stops and
+// returns that error.
+// UnmarshalXML must consume exactly one XML element.
+// One common implementation strategy is to unmarshal into
+// a separate value with a layout matching the expected XML
+// using d.DecodeElement, and then to copy the data from
+// that value into the receiver.
+// Another common strategy is to use d.Token to process the
+// XML object one token at a time.
+// UnmarshalXML may not use d.RawToken.
+type Unmarshaler interface {
+ UnmarshalXML(d *Decoder, start StartElement) error
+}
+
+// UnmarshalerAttr is the interface implemented by objects that can unmarshal
+// an XML attribute description of themselves.
+//
+// UnmarshalXMLAttr decodes a single XML attribute.
+// If it returns an error, the outer call to Unmarshal stops and
+// returns that error.
+// UnmarshalXMLAttr is used only for struct fields with the
+// "attr" option in the field tag.
+type UnmarshalerAttr interface {
+ UnmarshalXMLAttr(attr Attr) error
+}
+
+// receiverType returns the receiver type to use in an expression like "%s.MethodName".
+func receiverType(val interface{}) string {
+ t := reflect.TypeOf(val)
+ if t.Name() != "" {
+ return t.String()
+ }
+ return "(" + t.String() + ")"
+}
+
+// unmarshalInterface unmarshals a single XML element into val.
+// start is the opening tag of the element.
+func (p *Decoder) unmarshalInterface(val Unmarshaler, start *StartElement) error {
+ // Record that decoder must stop at end tag corresponding to start.
+ p.pushEOF()
+
+ p.unmarshalDepth++
+ err := val.UnmarshalXML(p, *start)
+ p.unmarshalDepth--
+ if err != nil {
+ p.popEOF()
+ return err
+ }
+
+ if !p.popEOF() {
+ return fmt.Errorf("xml: %s.UnmarshalXML did not consume entire <%s> element", receiverType(val), start.Name.Local)
+ }
+
+ return nil
+}
+
+// unmarshalTextInterface unmarshals a single XML element into val.
+// The chardata contained in the element (but not its children)
+// is passed to the text unmarshaler.
+func (p *Decoder) unmarshalTextInterface(val encoding.TextUnmarshaler, start *StartElement) error {
+ var buf []byte
+ depth := 1
+ for depth > 0 {
+ t, err := p.Token()
+ if err != nil {
+ return err
+ }
+ switch t := t.(type) {
+ case CharData:
+ if depth == 1 {
+ buf = append(buf, t...)
+ }
+ case StartElement:
+ depth++
+ case EndElement:
+ depth--
+ }
+ }
+ return val.UnmarshalText(buf)
+}
+
+// unmarshalAttr unmarshals a single XML attribute into val.
+func (p *Decoder) unmarshalAttr(val reflect.Value, attr Attr) error {
+ if val.Kind() == reflect.Ptr {
+ if val.IsNil() {
+ val.Set(reflect.New(val.Type().Elem()))
+ }
+ val = val.Elem()
+ }
+
+ if val.CanInterface() && val.Type().Implements(unmarshalerAttrType) {
+ // This is an unmarshaler with a non-pointer receiver,
+ // so it's likely to be incorrect, but we do what we're told.
+ return val.Interface().(UnmarshalerAttr).UnmarshalXMLAttr(attr)
+ }
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(unmarshalerAttrType) {
+ return pv.Interface().(UnmarshalerAttr).UnmarshalXMLAttr(attr)
+ }
+ }
+
+ // Not an UnmarshalerAttr; try encoding.TextUnmarshaler.
+ if val.CanInterface() && val.Type().Implements(textUnmarshalerType) {
+ // This is an unmarshaler with a non-pointer receiver,
+ // so it's likely to be incorrect, but we do what we're told.
+ return val.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(attr.Value))
+ }
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) {
+ return pv.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(attr.Value))
+ }
+ }
+
+ copyValue(val, []byte(attr.Value))
+ return nil
+}
+
+var (
+ unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
+ unmarshalerAttrType = reflect.TypeOf((*UnmarshalerAttr)(nil)).Elem()
+ textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
+)
+
+// Unmarshal a single XML element into val.
+func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
+ // Find start element if we need it.
+ if start == nil {
+ for {
+ tok, err := p.Token()
+ if err != nil {
+ return err
+ }
+ if t, ok := tok.(StartElement); ok {
+ start = &t
+ break
+ }
+ }
+ }
+
+ // Load value from interface, but only if the result will be
+ // usefully addressable.
+ if val.Kind() == reflect.Interface && !val.IsNil() {
+ e := val.Elem()
+ if e.Kind() == reflect.Ptr && !e.IsNil() {
+ val = e
+ }
+ }
+
+ if val.Kind() == reflect.Ptr {
+ if val.IsNil() {
+ val.Set(reflect.New(val.Type().Elem()))
+ }
+ val = val.Elem()
+ }
+
+ if val.CanInterface() && val.Type().Implements(unmarshalerType) {
+ // This is an unmarshaler with a non-pointer receiver,
+ // so it's likely to be incorrect, but we do what we're told.
+ return p.unmarshalInterface(val.Interface().(Unmarshaler), start)
+ }
+
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(unmarshalerType) {
+ return p.unmarshalInterface(pv.Interface().(Unmarshaler), start)
+ }
+ }
+
+ if val.CanInterface() && val.Type().Implements(textUnmarshalerType) {
+ return p.unmarshalTextInterface(val.Interface().(encoding.TextUnmarshaler), start)
+ }
+
+ if val.CanAddr() {
+ pv := val.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) {
+ return p.unmarshalTextInterface(pv.Interface().(encoding.TextUnmarshaler), start)
+ }
+ }
+
+ var (
+ data []byte
+ saveData reflect.Value
+ comment []byte
+ saveComment reflect.Value
+ saveXML reflect.Value
+ saveXMLIndex int
+ saveXMLData []byte
+ saveAny reflect.Value
+ sv reflect.Value
+ tinfo *typeInfo
+ err error
+ )
+
+ switch v := val; v.Kind() {
+ default:
+ return errors.New("unknown type " + v.Type().String())
+
+ case reflect.Interface:
+ // TODO: For now, simply ignore the field. In the near
+ // future we may choose to unmarshal the start
+ // element on it, if not nil.
+ return p.Skip()
+
+ case reflect.Slice:
+ typ := v.Type()
+ if typ.Elem().Kind() == reflect.Uint8 {
+ // []byte
+ saveData = v
+ break
+ }
+
+ // Slice of element values.
+ // Grow slice.
+ n := v.Len()
+ if n >= v.Cap() {
+ ncap := 2 * n
+ if ncap < 4 {
+ ncap = 4
+ }
+ new := reflect.MakeSlice(typ, n, ncap)
+ reflect.Copy(new, v)
+ v.Set(new)
+ }
+ v.SetLen(n + 1)
+
+ // Recur to read element into slice.
+ if err := p.unmarshal(v.Index(n), start); err != nil {
+ v.SetLen(n)
+ return err
+ }
+ return nil
+
+ case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.String:
+ saveData = v
+
+ case reflect.Struct:
+ typ := v.Type()
+ if typ == nameType {
+ v.Set(reflect.ValueOf(start.Name))
+ break
+ }
+
+ sv = v
+ tinfo, err = getTypeInfo(typ)
+ if err != nil {
+ return err
+ }
+
+ // Validate and assign element name.
+ if tinfo.xmlname != nil {
+ finfo := tinfo.xmlname
+ if finfo.name != "" && finfo.name != start.Name.Local {
+ return UnmarshalError("expected element type <" + finfo.name + "> but have <" + start.Name.Local + ">")
+ }
+ if finfo.xmlns != "" && finfo.xmlns != start.Name.Space {
+ e := "expected element <" + finfo.name + "> in name space " + finfo.xmlns + " but have "
+ if start.Name.Space == "" {
+ e += "no name space"
+ } else {
+ e += start.Name.Space
+ }
+ return UnmarshalError(e)
+ }
+ fv := finfo.value(sv)
+ if _, ok := fv.Interface().(Name); ok {
+ fv.Set(reflect.ValueOf(start.Name))
+ }
+ }
+
+ // Assign attributes.
+ // Also, determine whether we need to save character data or comments.
+ for i := range tinfo.fields {
+ finfo := &tinfo.fields[i]
+ switch finfo.flags & fMode {
+ case fAttr:
+ strv := finfo.value(sv)
+ // Look for attribute.
+ for _, a := range start.Attr {
+ if a.Name.Local == finfo.name && (finfo.xmlns == "" || finfo.xmlns == a.Name.Space) {
+ if err := p.unmarshalAttr(strv, a); err != nil {
+ return err
+ }
+ break
+ }
+ }
+
+ case fCharData:
+ if !saveData.IsValid() {
+ saveData = finfo.value(sv)
+ }
+
+ case fComment:
+ if !saveComment.IsValid() {
+ saveComment = finfo.value(sv)
+ }
+
+ case fAny, fAny | fElement:
+ if !saveAny.IsValid() {
+ saveAny = finfo.value(sv)
+ }
+
+ case fInnerXml:
+ if !saveXML.IsValid() {
+ saveXML = finfo.value(sv)
+ if p.saved == nil {
+ saveXMLIndex = 0
+ p.saved = new(bytes.Buffer)
+ } else {
+ saveXMLIndex = p.savedOffset()
+ }
+ }
+ }
+ }
+ }
+
+ // Find end element.
+ // Process sub-elements along the way.
+Loop:
+ for {
+ var savedOffset int
+ if saveXML.IsValid() {
+ savedOffset = p.savedOffset()
+ }
+ tok, err := p.Token()
+ if err != nil {
+ return err
+ }
+ switch t := tok.(type) {
+ case StartElement:
+ consumed := false
+ if sv.IsValid() {
+ consumed, err = p.unmarshalPath(tinfo, sv, nil, &t)
+ if err != nil {
+ return err
+ }
+ if !consumed && saveAny.IsValid() {
+ consumed = true
+ if err := p.unmarshal(saveAny, &t); err != nil {
+ return err
+ }
+ }
+ }
+ if !consumed {
+ if err := p.Skip(); err != nil {
+ return err
+ }
+ }
+
+ case EndElement:
+ if saveXML.IsValid() {
+ saveXMLData = p.saved.Bytes()[saveXMLIndex:savedOffset]
+ if saveXMLIndex == 0 {
+ p.saved = nil
+ }
+ }
+ break Loop
+
+ case CharData:
+ if saveData.IsValid() {
+ data = append(data, t...)
+ }
+
+ case Comment:
+ if saveComment.IsValid() {
+ comment = append(comment, t...)
+ }
+ }
+ }
+
+ if saveData.IsValid() && saveData.CanInterface() && saveData.Type().Implements(textUnmarshalerType) {
+ if err := saveData.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil {
+ return err
+ }
+ saveData = reflect.Value{}
+ }
+
+ if saveData.IsValid() && saveData.CanAddr() {
+ pv := saveData.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) {
+ if err := pv.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil {
+ return err
+ }
+ saveData = reflect.Value{}
+ }
+ }
+
+ if err := copyValue(saveData, data); err != nil {
+ return err
+ }
+
+ switch t := saveComment; t.Kind() {
+ case reflect.String:
+ t.SetString(string(comment))
+ case reflect.Slice:
+ t.Set(reflect.ValueOf(comment))
+ }
+
+ switch t := saveXML; t.Kind() {
+ case reflect.String:
+ t.SetString(string(saveXMLData))
+ case reflect.Slice:
+ t.Set(reflect.ValueOf(saveXMLData))
+ }
+
+ return nil
+}
+
+func copyValue(dst reflect.Value, src []byte) (err error) {
+ dst0 := dst
+
+ if dst.Kind() == reflect.Ptr {
+ if dst.IsNil() {
+ dst.Set(reflect.New(dst.Type().Elem()))
+ }
+ dst = dst.Elem()
+ }
+
+ // Save accumulated data.
+ switch dst.Kind() {
+ case reflect.Invalid:
+ // Probably a comment.
+ default:
+ return errors.New("cannot unmarshal into " + dst0.Type().String())
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ itmp, err := strconv.ParseInt(string(src), 10, dst.Type().Bits())
+ if err != nil {
+ return err
+ }
+ dst.SetInt(itmp)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ utmp, err := strconv.ParseUint(string(src), 10, dst.Type().Bits())
+ if err != nil {
+ return err
+ }
+ dst.SetUint(utmp)
+ case reflect.Float32, reflect.Float64:
+ ftmp, err := strconv.ParseFloat(string(src), dst.Type().Bits())
+ if err != nil {
+ return err
+ }
+ dst.SetFloat(ftmp)
+ case reflect.Bool:
+ value, err := strconv.ParseBool(strings.TrimSpace(string(src)))
+ if err != nil {
+ return err
+ }
+ dst.SetBool(value)
+ case reflect.String:
+ dst.SetString(string(src))
+ case reflect.Slice:
+ if len(src) == 0 {
+ // non-nil to flag presence
+ src = []byte{}
+ }
+ dst.SetBytes(src)
+ }
+ return nil
+}
+
+// unmarshalPath walks down an XML structure looking for wanted
+// paths, and calls unmarshal on them.
+// The consumed result tells whether XML elements have been consumed
+// from the Decoder until start's matching end element, or if it's
+// still untouched because start is uninteresting for sv's fields.
+func (p *Decoder) unmarshalPath(tinfo *typeInfo, sv reflect.Value, parents []string, start *StartElement) (consumed bool, err error) {
+ recurse := false
+Loop:
+ for i := range tinfo.fields {
+ finfo := &tinfo.fields[i]
+ if finfo.flags&fElement == 0 || len(finfo.parents) < len(parents) || finfo.xmlns != "" && finfo.xmlns != start.Name.Space {
+ continue
+ }
+ for j := range parents {
+ if parents[j] != finfo.parents[j] {
+ continue Loop
+ }
+ }
+ if len(finfo.parents) == len(parents) && finfo.name == start.Name.Local {
+ // It's a perfect match, unmarshal the field.
+ return true, p.unmarshal(finfo.value(sv), start)
+ }
+ if len(finfo.parents) > len(parents) && finfo.parents[len(parents)] == start.Name.Local {
+ // It's a prefix for the field. Break and recurse
+ // since it's not ok for one field path to be itself
+ // the prefix for another field path.
+ recurse = true
+
+ // We can reuse the same slice as long as we
+ // don't try to append to it.
+ parents = finfo.parents[:len(parents)+1]
+ break
+ }
+ }
+ if !recurse {
+ // We have no business with this element.
+ return false, nil
+ }
+ // The element is not a perfect match for any field, but one
+ // or more fields have the path to this element as a parent
+ // prefix. Recurse and attempt to match these.
+ for {
+ var tok Token
+ tok, err = p.Token()
+ if err != nil {
+ return true, err
+ }
+ switch t := tok.(type) {
+ case StartElement:
+ consumed2, err := p.unmarshalPath(tinfo, sv, parents, &t)
+ if err != nil {
+ return true, err
+ }
+ if !consumed2 {
+ if err := p.Skip(); err != nil {
+ return true, err
+ }
+ }
+ case EndElement:
+ return true, nil
+ }
+ }
+}
+
+// Skip reads tokens until it has consumed the end element
+// matching the most recent start element already consumed.
+// It recurs if it encounters a start element, so it can be used to
+// skip nested structures.
+// It returns nil if it finds an end element matching the start
+// element; otherwise it returns an error describing the problem.
+func (d *Decoder) Skip() error {
+ for {
+ tok, err := d.Token()
+ if err != nil {
+ return err
+ }
+ switch tok.(type) {
+ case StartElement:
+ if err := d.Skip(); err != nil {
+ return err
+ }
+ case EndElement:
+ return nil
+ }
+ }
+}
diff --git a/vendor/golang.org/x/net/webdav/internal/xml/typeinfo.go b/vendor/golang.org/x/net/webdav/internal/xml/typeinfo.go
new file mode 100644
index 0000000..fdde288
--- /dev/null
+++ b/vendor/golang.org/x/net/webdav/internal/xml/typeinfo.go
@@ -0,0 +1,371 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xml
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+ "sync"
+)
+
+// typeInfo holds details for the xml representation of a type.
+type typeInfo struct {
+ xmlname *fieldInfo
+ fields []fieldInfo
+}
+
+// fieldInfo holds details for the xml representation of a single field.
+type fieldInfo struct {
+ idx []int
+ name string
+ xmlns string
+ flags fieldFlags
+ parents []string
+}
+
+type fieldFlags int
+
+const (
+ fElement fieldFlags = 1 << iota
+ fAttr
+ fCharData
+ fInnerXml
+ fComment
+ fAny
+
+ fOmitEmpty
+
+ fMode = fElement | fAttr | fCharData | fInnerXml | fComment | fAny
+)
+
+var tinfoMap = make(map[reflect.Type]*typeInfo)
+var tinfoLock sync.RWMutex
+
+var nameType = reflect.TypeOf(Name{})
+
+// getTypeInfo returns the typeInfo structure with details necessary
+// for marshalling and unmarshalling typ.
+func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
+ tinfoLock.RLock()
+ tinfo, ok := tinfoMap[typ]
+ tinfoLock.RUnlock()
+ if ok {
+ return tinfo, nil
+ }
+ tinfo = &typeInfo{}
+ if typ.Kind() == reflect.Struct && typ != nameType {
+ n := typ.NumField()
+ for i := 0; i < n; i++ {
+ f := typ.Field(i)
+ if f.PkgPath != "" || f.Tag.Get("xml") == "-" {
+ continue // Private field
+ }
+
+ // For embedded structs, embed its fields.
+ if f.Anonymous {
+ t := f.Type
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ if t.Kind() == reflect.Struct {
+ inner, err := getTypeInfo(t)
+ if err != nil {
+ return nil, err
+ }
+ if tinfo.xmlname == nil {
+ tinfo.xmlname = inner.xmlname
+ }
+ for _, finfo := range inner.fields {
+ finfo.idx = append([]int{i}, finfo.idx...)
+ if err := addFieldInfo(typ, tinfo, &finfo); err != nil {
+ return nil, err
+ }
+ }
+ continue
+ }
+ }
+
+ finfo, err := structFieldInfo(typ, &f)
+ if err != nil {
+ return nil, err
+ }
+
+ if f.Name == "XMLName" {
+ tinfo.xmlname = finfo
+ continue
+ }
+
+ // Add the field if it doesn't conflict with other fields.
+ if err := addFieldInfo(typ, tinfo, finfo); err != nil {
+ return nil, err
+ }
+ }
+ }
+ tinfoLock.Lock()
+ tinfoMap[typ] = tinfo
+ tinfoLock.Unlock()
+ return tinfo, nil
+}
+
+// structFieldInfo builds and returns a fieldInfo for f.
+func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, error) {
+ finfo := &fieldInfo{idx: f.Index}
+
+ // Split the tag from the xml namespace if necessary.
+ tag := f.Tag.Get("xml")
+ if i := strings.Index(tag, " "); i >= 0 {
+ finfo.xmlns, tag = tag[:i], tag[i+1:]
+ }
+
+ // Parse flags.
+ tokens := strings.Split(tag, ",")
+ if len(tokens) == 1 {
+ finfo.flags = fElement
+ } else {
+ tag = tokens[0]
+ for _, flag := range tokens[1:] {
+ switch flag {
+ case "attr":
+ finfo.flags |= fAttr
+ case "chardata":
+ finfo.flags |= fCharData
+ case "innerxml":
+ finfo.flags |= fInnerXml
+ case "comment":
+ finfo.flags |= fComment
+ case "any":
+ finfo.flags |= fAny
+ case "omitempty":
+ finfo.flags |= fOmitEmpty
+ }
+ }
+
+ // Validate the flags used.
+ valid := true
+ switch mode := finfo.flags & fMode; mode {
+ case 0:
+ finfo.flags |= fElement
+ case fAttr, fCharData, fInnerXml, fComment, fAny:
+ if f.Name == "XMLName" || tag != "" && mode != fAttr {
+ valid = false
+ }
+ default:
+ // This will also catch multiple modes in a single field.
+ valid = false
+ }
+ if finfo.flags&fMode == fAny {
+ finfo.flags |= fElement
+ }
+ if finfo.flags&fOmitEmpty != 0 && finfo.flags&(fElement|fAttr) == 0 {
+ valid = false
+ }
+ if !valid {
+ return nil, fmt.Errorf("xml: invalid tag in field %s of type %s: %q",
+ f.Name, typ, f.Tag.Get("xml"))
+ }
+ }
+
+ // Use of xmlns without a name is not allowed.
+ if finfo.xmlns != "" && tag == "" {
+ return nil, fmt.Errorf("xml: namespace without name in field %s of type %s: %q",
+ f.Name, typ, f.Tag.Get("xml"))
+ }
+
+ if f.Name == "XMLName" {
+ // The XMLName field records the XML element name. Don't
+ // process it as usual because its name should default to
+ // empty rather than to the field name.
+ finfo.name = tag
+ return finfo, nil
+ }
+
+ if tag == "" {
+ // If the name part of the tag is completely empty, get
+ // default from XMLName of underlying struct if feasible,
+ // or field name otherwise.
+ if xmlname := lookupXMLName(f.Type); xmlname != nil {
+ finfo.xmlns, finfo.name = xmlname.xmlns, xmlname.name
+ } else {
+ finfo.name = f.Name
+ }
+ return finfo, nil
+ }
+
+ if finfo.xmlns == "" && finfo.flags&fAttr == 0 {
+ // If it's an element no namespace specified, get the default
+ // from the XMLName of enclosing struct if possible.
+ if xmlname := lookupXMLName(typ); xmlname != nil {
+ finfo.xmlns = xmlname.xmlns
+ }
+ }
+
+ // Prepare field name and parents.
+ parents := strings.Split(tag, ">")
+ if parents[0] == "" {
+ parents[0] = f.Name
+ }
+ if parents[len(parents)-1] == "" {
+ return nil, fmt.Errorf("xml: trailing '>' in field %s of type %s", f.Name, typ)
+ }
+ finfo.name = parents[len(parents)-1]
+ if len(parents) > 1 {
+ if (finfo.flags & fElement) == 0 {
+ return nil, fmt.Errorf("xml: %s chain not valid with %s flag", tag, strings.Join(tokens[1:], ","))
+ }
+ finfo.parents = parents[:len(parents)-1]
+ }
+
+ // If the field type has an XMLName field, the names must match
+ // so that the behavior of both marshalling and unmarshalling
+ // is straightforward and unambiguous.
+ if finfo.flags&fElement != 0 {
+ ftyp := f.Type
+ xmlname := lookupXMLName(ftyp)
+ if xmlname != nil && xmlname.name != finfo.name {
+ return nil, fmt.Errorf("xml: name %q in tag of %s.%s conflicts with name %q in %s.XMLName",
+ finfo.name, typ, f.Name, xmlname.name, ftyp)
+ }
+ }
+ return finfo, nil
+}
+
+// lookupXMLName returns the fieldInfo for typ's XMLName field
+// in case it exists and has a valid xml field tag, otherwise
+// it returns nil.
+func lookupXMLName(typ reflect.Type) (xmlname *fieldInfo) {
+ for typ.Kind() == reflect.Ptr {
+ typ = typ.Elem()
+ }
+ if typ.Kind() != reflect.Struct {
+ return nil
+ }
+ for i, n := 0, typ.NumField(); i < n; i++ {
+ f := typ.Field(i)
+ if f.Name != "XMLName" {
+ continue
+ }
+ finfo, err := structFieldInfo(typ, &f)
+ if finfo.name != "" && err == nil {
+ return finfo
+ }
+ // Also consider errors as a non-existent field tag
+ // and let getTypeInfo itself report the error.
+ break
+ }
+ return nil
+}
+
+func min(a, b int) int {
+ if a <= b {
+ return a
+ }
+ return b
+}
+
+// addFieldInfo adds finfo to tinfo.fields if there are no
+// conflicts, or if conflicts arise from previous fields that were
+// obtained from deeper embedded structures than finfo. In the latter
+// case, the conflicting entries are dropped.
+// A conflict occurs when the path (parent + name) to a field is
+// itself a prefix of another path, or when two paths match exactly.
+// It is okay for field paths to share a common, shorter prefix.
+func addFieldInfo(typ reflect.Type, tinfo *typeInfo, newf *fieldInfo) error {
+ var conflicts []int
+Loop:
+ // First, figure all conflicts. Most working code will have none.
+ for i := range tinfo.fields {
+ oldf := &tinfo.fields[i]
+ if oldf.flags&fMode != newf.flags&fMode {
+ continue
+ }
+ if oldf.xmlns != "" && newf.xmlns != "" && oldf.xmlns != newf.xmlns {
+ continue
+ }
+ minl := min(len(newf.parents), len(oldf.parents))
+ for p := 0; p < minl; p++ {
+ if oldf.parents[p] != newf.parents[p] {
+ continue Loop
+ }
+ }
+ if len(oldf.parents) > len(newf.parents) {
+ if oldf.parents[len(newf.parents)] == newf.name {
+ conflicts = append(conflicts, i)
+ }
+ } else if len(oldf.parents) < len(newf.parents) {
+ if newf.parents[len(oldf.parents)] == oldf.name {
+ conflicts = append(conflicts, i)
+ }
+ } else {
+ if newf.name == oldf.name {
+ conflicts = append(conflicts, i)
+ }
+ }
+ }
+ // Without conflicts, add the new field and return.
+ if conflicts == nil {
+ tinfo.fields = append(tinfo.fields, *newf)
+ return nil
+ }
+
+ // If any conflict is shallower, ignore the new field.
+ // This matches the Go field resolution on embedding.
+ for _, i := range conflicts {
+ if len(tinfo.fields[i].idx) < len(newf.idx) {
+ return nil
+ }
+ }
+
+ // Otherwise, if any of them is at the same depth level, it's an error.
+ for _, i := range conflicts {
+ oldf := &tinfo.fields[i]
+ if len(oldf.idx) == len(newf.idx) {
+ f1 := typ.FieldByIndex(oldf.idx)
+ f2 := typ.FieldByIndex(newf.idx)
+ return &TagPathError{typ, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")}
+ }
+ }
+
+ // Otherwise, the new field is shallower, and thus takes precedence,
+ // so drop the conflicting fields from tinfo and append the new one.
+ for c := len(conflicts) - 1; c >= 0; c-- {
+ i := conflicts[c]
+ copy(tinfo.fields[i:], tinfo.fields[i+1:])
+ tinfo.fields = tinfo.fields[:len(tinfo.fields)-1]
+ }
+ tinfo.fields = append(tinfo.fields, *newf)
+ return nil
+}
+
+// A TagPathError represents an error in the unmarshalling process
+// caused by the use of field tags with conflicting paths.
+type TagPathError struct {
+ Struct reflect.Type
+ Field1, Tag1 string
+ Field2, Tag2 string
+}
+
+func (e *TagPathError) Error() string {
+ return fmt.Sprintf("%s field %q with tag %q conflicts with field %q with tag %q", e.Struct, e.Field1, e.Tag1, e.Field2, e.Tag2)
+}
+
+// value returns v's field value corresponding to finfo.
+// It's equivalent to v.FieldByIndex(finfo.idx), but initializes
+// and dereferences pointers as necessary.
+func (finfo *fieldInfo) value(v reflect.Value) reflect.Value {
+ for i, x := range finfo.idx {
+ if i > 0 {
+ t := v.Type()
+ if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
+ if v.IsNil() {
+ v.Set(reflect.New(v.Type().Elem()))
+ }
+ v = v.Elem()
+ }
+ }
+ v = v.Field(x)
+ }
+ return v
+}
diff --git a/vendor/golang.org/x/net/webdav/internal/xml/xml.go b/vendor/golang.org/x/net/webdav/internal/xml/xml.go
new file mode 100644
index 0000000..5b79cbe
--- /dev/null
+++ b/vendor/golang.org/x/net/webdav/internal/xml/xml.go
@@ -0,0 +1,1998 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package xml implements a simple XML 1.0 parser that
+// understands XML name spaces.
+package xml
+
+// References:
+// Annotated XML spec: http://www.xml.com/axml/testaxml.htm
+// XML name spaces: http://www.w3.org/TR/REC-xml-names/
+
+// TODO(rsc):
+// Test error handling.
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+// A SyntaxError represents a syntax error in the XML input stream.
+type SyntaxError struct {
+ Msg string
+ Line int
+}
+
+func (e *SyntaxError) Error() string {
+ return "XML syntax error on line " + strconv.Itoa(e.Line) + ": " + e.Msg
+}
+
+// A Name represents an XML name (Local) annotated with a name space
+// identifier (Space). In tokens returned by Decoder.Token, the Space
+// identifier is given as a canonical URL, not the short prefix used in
+// the document being parsed.
+//
+// As a special case, XML namespace declarations will use the literal
+// string "xmlns" for the Space field instead of the fully resolved URL.
+// See Encoder.EncodeToken for more information on namespace encoding
+// behaviour.
+type Name struct {
+ Space, Local string
+}
+
+// isNamespace reports whether the name is a namespace-defining name.
+func (name Name) isNamespace() bool {
+ return name.Local == "xmlns" || name.Space == "xmlns"
+}
+
+// An Attr represents an attribute in an XML element (Name=Value).
+type Attr struct {
+ Name Name
+ Value string
+}
+
+// A Token is an interface holding one of the token types:
+// StartElement, EndElement, CharData, Comment, ProcInst, or Directive.
+type Token interface{}
+
+// A StartElement represents an XML start element.
+type StartElement struct {
+ Name Name
+ Attr []Attr
+}
+
+func (e StartElement) Copy() StartElement {
+ attrs := make([]Attr, len(e.Attr))
+ copy(attrs, e.Attr)
+ e.Attr = attrs
+ return e
+}
+
+// End returns the corresponding XML end element.
+func (e StartElement) End() EndElement {
+ return EndElement{e.Name}
+}
+
+// setDefaultNamespace sets the namespace of the element
+// as the default for all elements contained within it.
+func (e *StartElement) setDefaultNamespace() {
+ if e.Name.Space == "" {
+ // If there's no namespace on the element, don't
+ // set the default. Strictly speaking this might be wrong, as
+ // we can't tell if the element had no namespace set
+ // or was just using the default namespace.
+ return
+ }
+ // Don't add a default name space if there's already one set.
+ for _, attr := range e.Attr {
+ if attr.Name.Space == "" && attr.Name.Local == "xmlns" {
+ return
+ }
+ }
+ e.Attr = append(e.Attr, Attr{
+ Name: Name{
+ Local: "xmlns",
+ },
+ Value: e.Name.Space,
+ })
+}
+
+// An EndElement represents an XML end element.
+type EndElement struct {
+ Name Name
+}
+
+// A CharData represents XML character data (raw text),
+// in which XML escape sequences have been replaced by
+// the characters they represent.
+type CharData []byte
+
+func makeCopy(b []byte) []byte {
+ b1 := make([]byte, len(b))
+ copy(b1, b)
+ return b1
+}
+
+func (c CharData) Copy() CharData { return CharData(makeCopy(c)) }
+
+// A Comment represents an XML comment of the form .
+// The bytes do not include the comment markers.
+type Comment []byte
+
+func (c Comment) Copy() Comment { return Comment(makeCopy(c)) }
+
+// A ProcInst represents an XML processing instruction of the form
+type ProcInst struct {
+ Target string
+ Inst []byte
+}
+
+func (p ProcInst) Copy() ProcInst {
+ p.Inst = makeCopy(p.Inst)
+ return p
+}
+
+// A Directive represents an XML directive of the form .
+// The bytes do not include the markers.
+type Directive []byte
+
+func (d Directive) Copy() Directive { return Directive(makeCopy(d)) }
+
+// CopyToken returns a copy of a Token.
+func CopyToken(t Token) Token {
+ switch v := t.(type) {
+ case CharData:
+ return v.Copy()
+ case Comment:
+ return v.Copy()
+ case Directive:
+ return v.Copy()
+ case ProcInst:
+ return v.Copy()
+ case StartElement:
+ return v.Copy()
+ }
+ return t
+}
+
+// A Decoder represents an XML parser reading a particular input stream.
+// The parser assumes that its input is encoded in UTF-8.
+type Decoder struct {
+ // Strict defaults to true, enforcing the requirements
+ // of the XML specification.
+ // If set to false, the parser allows input containing common
+ // mistakes:
+ // * If an element is missing an end tag, the parser invents
+ // end tags as necessary to keep the return values from Token
+ // properly balanced.
+ // * In attribute values and character data, unknown or malformed
+ // character entities (sequences beginning with &) are left alone.
+ //
+ // Setting:
+ //
+ // d.Strict = false;
+ // d.AutoClose = HTMLAutoClose;
+ // d.Entity = HTMLEntity
+ //
+ // creates a parser that can handle typical HTML.
+ //
+ // Strict mode does not enforce the requirements of the XML name spaces TR.
+ // In particular it does not reject name space tags using undefined prefixes.
+ // Such tags are recorded with the unknown prefix as the name space URL.
+ Strict bool
+
+ // When Strict == false, AutoClose indicates a set of elements to
+ // consider closed immediately after they are opened, regardless
+ // of whether an end element is present.
+ AutoClose []string
+
+ // Entity can be used to map non-standard entity names to string replacements.
+ // The parser behaves as if these standard mappings are present in the map,
+ // regardless of the actual map content:
+ //
+ // "lt": "<",
+ // "gt": ">",
+ // "amp": "&",
+ // "apos": "'",
+ // "quot": `"`,
+ Entity map[string]string
+
+ // CharsetReader, if non-nil, defines a function to generate
+ // charset-conversion readers, converting from the provided
+ // non-UTF-8 charset into UTF-8. If CharsetReader is nil or
+ // returns an error, parsing stops with an error. One of the
+ // the CharsetReader's result values must be non-nil.
+ CharsetReader func(charset string, input io.Reader) (io.Reader, error)
+
+ // DefaultSpace sets the default name space used for unadorned tags,
+ // as if the entire XML stream were wrapped in an element containing
+ // the attribute xmlns="DefaultSpace".
+ DefaultSpace string
+
+ r io.ByteReader
+ buf bytes.Buffer
+ saved *bytes.Buffer
+ stk *stack
+ free *stack
+ needClose bool
+ toClose Name
+ nextToken Token
+ nextByte int
+ ns map[string]string
+ err error
+ line int
+ offset int64
+ unmarshalDepth int
+}
+
+// NewDecoder creates a new XML parser reading from r.
+// If r does not implement io.ByteReader, NewDecoder will
+// do its own buffering.
+func NewDecoder(r io.Reader) *Decoder {
+ d := &Decoder{
+ ns: make(map[string]string),
+ nextByte: -1,
+ line: 1,
+ Strict: true,
+ }
+ d.switchToReader(r)
+ return d
+}
+
+// Token returns the next XML token in the input stream.
+// At the end of the input stream, Token returns nil, io.EOF.
+//
+// Slices of bytes in the returned token data refer to the
+// parser's internal buffer and remain valid only until the next
+// call to Token. To acquire a copy of the bytes, call CopyToken
+// or the token's Copy method.
+//
+// Token expands self-closing elements such as
+// into separate start and end elements returned by successive calls.
+//
+// Token guarantees that the StartElement and EndElement
+// tokens it returns are properly nested and matched:
+// if Token encounters an unexpected end element,
+// it will return an error.
+//
+// Token implements XML name spaces as described by
+// http://www.w3.org/TR/REC-xml-names/. Each of the
+// Name structures contained in the Token has the Space
+// set to the URL identifying its name space when known.
+// If Token encounters an unrecognized name space prefix,
+// it uses the prefix as the Space rather than report an error.
+func (d *Decoder) Token() (t Token, err error) {
+ if d.stk != nil && d.stk.kind == stkEOF {
+ err = io.EOF
+ return
+ }
+ if d.nextToken != nil {
+ t = d.nextToken
+ d.nextToken = nil
+ } else if t, err = d.rawToken(); err != nil {
+ return
+ }
+
+ if !d.Strict {
+ if t1, ok := d.autoClose(t); ok {
+ d.nextToken = t
+ t = t1
+ }
+ }
+ switch t1 := t.(type) {
+ case StartElement:
+ // In XML name spaces, the translations listed in the
+ // attributes apply to the element name and
+ // to the other attribute names, so process
+ // the translations first.
+ for _, a := range t1.Attr {
+ if a.Name.Space == "xmlns" {
+ v, ok := d.ns[a.Name.Local]
+ d.pushNs(a.Name.Local, v, ok)
+ d.ns[a.Name.Local] = a.Value
+ }
+ if a.Name.Space == "" && a.Name.Local == "xmlns" {
+ // Default space for untagged names
+ v, ok := d.ns[""]
+ d.pushNs("", v, ok)
+ d.ns[""] = a.Value
+ }
+ }
+
+ d.translate(&t1.Name, true)
+ for i := range t1.Attr {
+ d.translate(&t1.Attr[i].Name, false)
+ }
+ d.pushElement(t1.Name)
+ t = t1
+
+ case EndElement:
+ d.translate(&t1.Name, true)
+ if !d.popElement(&t1) {
+ return nil, d.err
+ }
+ t = t1
+ }
+ return
+}
+
+const xmlURL = "http://www.w3.org/XML/1998/namespace"
+
+// Apply name space translation to name n.
+// The default name space (for Space=="")
+// applies only to element names, not to attribute names.
+func (d *Decoder) translate(n *Name, isElementName bool) {
+ switch {
+ case n.Space == "xmlns":
+ return
+ case n.Space == "" && !isElementName:
+ return
+ case n.Space == "xml":
+ n.Space = xmlURL
+ case n.Space == "" && n.Local == "xmlns":
+ return
+ }
+ if v, ok := d.ns[n.Space]; ok {
+ n.Space = v
+ } else if n.Space == "" {
+ n.Space = d.DefaultSpace
+ }
+}
+
+func (d *Decoder) switchToReader(r io.Reader) {
+ // Get efficient byte at a time reader.
+ // Assume that if reader has its own
+ // ReadByte, it's efficient enough.
+ // Otherwise, use bufio.
+ if rb, ok := r.(io.ByteReader); ok {
+ d.r = rb
+ } else {
+ d.r = bufio.NewReader(r)
+ }
+}
+
+// Parsing state - stack holds old name space translations
+// and the current set of open elements. The translations to pop when
+// ending a given tag are *below* it on the stack, which is
+// more work but forced on us by XML.
+type stack struct {
+ next *stack
+ kind int
+ name Name
+ ok bool
+}
+
+const (
+ stkStart = iota
+ stkNs
+ stkEOF
+)
+
+func (d *Decoder) push(kind int) *stack {
+ s := d.free
+ if s != nil {
+ d.free = s.next
+ } else {
+ s = new(stack)
+ }
+ s.next = d.stk
+ s.kind = kind
+ d.stk = s
+ return s
+}
+
+func (d *Decoder) pop() *stack {
+ s := d.stk
+ if s != nil {
+ d.stk = s.next
+ s.next = d.free
+ d.free = s
+ }
+ return s
+}
+
+// Record that after the current element is finished
+// (that element is already pushed on the stack)
+// Token should return EOF until popEOF is called.
+func (d *Decoder) pushEOF() {
+ // Walk down stack to find Start.
+ // It might not be the top, because there might be stkNs
+ // entries above it.
+ start := d.stk
+ for start.kind != stkStart {
+ start = start.next
+ }
+ // The stkNs entries below a start are associated with that
+ // element too; skip over them.
+ for start.next != nil && start.next.kind == stkNs {
+ start = start.next
+ }
+ s := d.free
+ if s != nil {
+ d.free = s.next
+ } else {
+ s = new(stack)
+ }
+ s.kind = stkEOF
+ s.next = start.next
+ start.next = s
+}
+
+// Undo a pushEOF.
+// The element must have been finished, so the EOF should be at the top of the stack.
+func (d *Decoder) popEOF() bool {
+ if d.stk == nil || d.stk.kind != stkEOF {
+ return false
+ }
+ d.pop()
+ return true
+}
+
+// Record that we are starting an element with the given name.
+func (d *Decoder) pushElement(name Name) {
+ s := d.push(stkStart)
+ s.name = name
+}
+
+// Record that we are changing the value of ns[local].
+// The old value is url, ok.
+func (d *Decoder) pushNs(local string, url string, ok bool) {
+ s := d.push(stkNs)
+ s.name.Local = local
+ s.name.Space = url
+ s.ok = ok
+}
+
+// Creates a SyntaxError with the current line number.
+func (d *Decoder) syntaxError(msg string) error {
+ return &SyntaxError{Msg: msg, Line: d.line}
+}
+
+// Record that we are ending an element with the given name.
+// The name must match the record at the top of the stack,
+// which must be a pushElement record.
+// After popping the element, apply any undo records from
+// the stack to restore the name translations that existed
+// before we saw this element.
+func (d *Decoder) popElement(t *EndElement) bool {
+ s := d.pop()
+ name := t.Name
+ switch {
+ case s == nil || s.kind != stkStart:
+ d.err = d.syntaxError("unexpected end element " + name.Local + ">")
+ return false
+ case s.name.Local != name.Local:
+ if !d.Strict {
+ d.needClose = true
+ d.toClose = t.Name
+ t.Name = s.name
+ return true
+ }
+ d.err = d.syntaxError("element <" + s.name.Local + "> closed by " + name.Local + ">")
+ return false
+ case s.name.Space != name.Space:
+ d.err = d.syntaxError("element <" + s.name.Local + "> in space " + s.name.Space +
+ "closed by " + name.Local + "> in space " + name.Space)
+ return false
+ }
+
+ // Pop stack until a Start or EOF is on the top, undoing the
+ // translations that were associated with the element we just closed.
+ for d.stk != nil && d.stk.kind != stkStart && d.stk.kind != stkEOF {
+ s := d.pop()
+ if s.ok {
+ d.ns[s.name.Local] = s.name.Space
+ } else {
+ delete(d.ns, s.name.Local)
+ }
+ }
+
+ return true
+}
+
+// If the top element on the stack is autoclosing and
+// t is not the end tag, invent the end tag.
+func (d *Decoder) autoClose(t Token) (Token, bool) {
+ if d.stk == nil || d.stk.kind != stkStart {
+ return nil, false
+ }
+ name := strings.ToLower(d.stk.name.Local)
+ for _, s := range d.AutoClose {
+ if strings.ToLower(s) == name {
+ // This one should be auto closed if t doesn't close it.
+ et, ok := t.(EndElement)
+ if !ok || et.Name.Local != name {
+ return EndElement{d.stk.name}, true
+ }
+ break
+ }
+ }
+ return nil, false
+}
+
+var errRawToken = errors.New("xml: cannot use RawToken from UnmarshalXML method")
+
+// RawToken is like Token but does not verify that
+// start and end elements match and does not translate
+// name space prefixes to their corresponding URLs.
+func (d *Decoder) RawToken() (Token, error) {
+ if d.unmarshalDepth > 0 {
+ return nil, errRawToken
+ }
+ return d.rawToken()
+}
+
+func (d *Decoder) rawToken() (Token, error) {
+ if d.err != nil {
+ return nil, d.err
+ }
+ if d.needClose {
+ // The last element we read was self-closing and
+ // we returned just the StartElement half.
+ // Return the EndElement half now.
+ d.needClose = false
+ return EndElement{d.toClose}, nil
+ }
+
+ b, ok := d.getc()
+ if !ok {
+ return nil, d.err
+ }
+
+ if b != '<' {
+ // Text section.
+ d.ungetc(b)
+ data := d.text(-1, false)
+ if data == nil {
+ return nil, d.err
+ }
+ return CharData(data), nil
+ }
+
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ switch b {
+ case '/':
+ // ' {
+ d.err = d.syntaxError("invalid characters between " + name.Local + " and >")
+ return nil, d.err
+ }
+ return EndElement{name}, nil
+
+ case '?':
+ // ' {
+ break
+ }
+ b0 = b
+ }
+ data := d.buf.Bytes()
+ data = data[0 : len(data)-2] // chop ?>
+
+ if target == "xml" {
+ content := string(data)
+ ver := procInst("version", content)
+ if ver != "" && ver != "1.0" {
+ d.err = fmt.Errorf("xml: unsupported version %q; only version 1.0 is supported", ver)
+ return nil, d.err
+ }
+ enc := procInst("encoding", content)
+ if enc != "" && enc != "utf-8" && enc != "UTF-8" {
+ if d.CharsetReader == nil {
+ d.err = fmt.Errorf("xml: encoding %q declared but Decoder.CharsetReader is nil", enc)
+ return nil, d.err
+ }
+ newr, err := d.CharsetReader(enc, d.r.(io.Reader))
+ if err != nil {
+ d.err = fmt.Errorf("xml: opening charset %q: %v", enc, err)
+ return nil, d.err
+ }
+ if newr == nil {
+ panic("CharsetReader returned a nil Reader for charset " + enc)
+ }
+ d.switchToReader(newr)
+ }
+ }
+ return ProcInst{target, data}, nil
+
+ case '!':
+ // ' {
+ break
+ }
+ b0, b1 = b1, b
+ }
+ data := d.buf.Bytes()
+ data = data[0 : len(data)-3] // chop -->
+ return Comment(data), nil
+
+ case '[': // .
+ data := d.text(-1, true)
+ if data == nil {
+ return nil, d.err
+ }
+ return CharData(data), nil
+ }
+
+ // Probably a directive: , , etc.
+ // We don't care, but accumulate for caller. Quoted angle
+ // brackets do not count for nesting.
+ d.buf.Reset()
+ d.buf.WriteByte(b)
+ inquote := uint8(0)
+ depth := 0
+ for {
+ if b, ok = d.mustgetc(); !ok {
+ return nil, d.err
+ }
+ if inquote == 0 && b == '>' && depth == 0 {
+ break
+ }
+ HandleB:
+ d.buf.WriteByte(b)
+ switch {
+ case b == inquote:
+ inquote = 0
+
+ case inquote != 0:
+ // in quotes, no special action
+
+ case b == '\'' || b == '"':
+ inquote = b
+
+ case b == '>' && inquote == 0:
+ depth--
+
+ case b == '<' && inquote == 0:
+ // Look for