From 90cdae837b23afe563695d760bdc022ad90a361d Mon Sep 17 00:00:00 2001 From: Brendan O'Brien Date: Thu, 7 Sep 2017 11:40:56 -0400 Subject: [PATCH] update godeps and add support for customcrawls endpoint --- Godeps/Godeps.json | 65 ++--- custom_crawl_handlers.go | 91 +++++++ custom_crawl_requests.go | 59 +++++ server.go | 4 + .../datatogether/archive/collection.go | 63 ++--- .../datatogether/archive/collection_item.go | 166 +++++++++++++ .../datatogether/archive/collection_items.go | 106 +++++++++ .../datatogether/archive/collections.go | 34 +++ .../datatogether/archive/custom_crawl.go | 178 ++++++++++++++ .../datatogether/archive/custom_crawls.go | 52 ++++ .../datatogether/archive/datarepo.go | 4 +- .../github.com/datatogether/archive/link.go | 6 +- .../datatogether/archive/metadata.go | 2 +- .../github.com/datatogether/archive/primer.go | 5 +- .../datatogether/archive/queries.go | 144 +++++++++-- .../github.com/datatogether/archive/source.go | 6 +- .../datatogether/archive/uncrawlable.go | 4 +- vendor/github.com/datatogether/archive/url.go | 71 ++++-- .../datatogether/identity/user/queries.go | 56 ++++- .../datatogether/identity/user/user.go | 53 +++-- .../identity/user/user_requests.go | 84 ++++++- .../datatogether/identity/user/user_type.go | 10 +- .../datatogether/identity/user/users.go | 46 +++- .../datatogether/identity/user/validators.go | 2 +- .../datatogether/sql_datastore/cmd.go | 5 +- .../datatogether/sql_datastore/model.go | 19 +- .../sql_datastore/query_filters.go | 27 +++ .../sql_datastore/query_orders.go | 34 +++ .../datatogether/sql_datastore/readme.md | 124 +++++++++- .../sql_datastore/sql_datastore.go | 120 ++++++++-- .../github.com/datatogether/sqlutil/readme.md | 201 ++++++++++++++++ .../datatogether/sqlutil/sqlutil.go | 46 ++-- .../github.com/ipfs/go-datastore/.travis.yml | 2 +- .../github.com/ipfs/go-datastore/basic_ds.go | 41 +++- vendor/github.com/ipfs/go-datastore/key.go | 23 ++ .../github.com/ipfs/go-datastore/package.json | 2 +- .../multiformats/go-multihash/.gitignore | 1 + .../multiformats/go-multihash/.travis.yml | 3 +- .../multiformats/go-multihash/README.md | 69 ++++-- .../multiformats/go-multihash/multihash.go | 85 ++++++- .../multiformats/go-multihash/package.json | 15 +- .../multiformats/go-multihash/sum.go | 107 ++++++++- .../github.com/spaolacci/murmur3/.gitignore | 22 ++ .../github.com/spaolacci/murmur3/.travis.yml | 7 + vendor/github.com/spaolacci/murmur3/LICENSE | 24 ++ vendor/github.com/spaolacci/murmur3/README.md | 86 +++++++ vendor/github.com/spaolacci/murmur3/murmur.go | 65 +++++ .../github.com/spaolacci/murmur3/murmur128.go | 194 +++++++++++++++ .../github.com/spaolacci/murmur3/murmur32.go | 160 +++++++++++++ .../github.com/spaolacci/murmur3/murmur64.go | 49 ++++ vendor/golang.org/x/crypto/AUTHORS | 2 +- vendor/golang.org/x/crypto/CONTRIBUTORS | 2 +- vendor/golang.org/x/crypto/acme/acme.go | 40 +--- .../x/crypto/acme/autocert/listener.go | 9 +- vendor/golang.org/x/crypto/acme/types.go | 34 +++ vendor/golang.org/x/crypto/blake2b/blake2b.go | 29 ++- vendor/golang.org/x/crypto/blake2b/blake2x.go | 177 ++++++++++++++ vendor/golang.org/x/crypto/blake2s/blake2s.go | 43 +++- .../x/crypto/blake2s/blake2s_386.go | 7 +- .../x/crypto/blake2s/blake2s_amd64.go | 11 +- .../x/crypto/blake2s/blake2s_ref.go | 7 +- vendor/golang.org/x/crypto/blake2s/blake2x.go | 178 ++++++++++++++ vendor/golang.org/x/crypto/blowfish/cipher.go | 2 +- vendor/golang.org/x/crypto/blowfish/const.go | 2 +- .../x/crypto/curve25519/const_amd64.h | 2 +- .../x/crypto/curve25519/const_amd64.s | 2 +- vendor/golang.org/x/crypto/curve25519/doc.go | 2 +- .../x/crypto/curve25519/freeze_amd64.s | 2 +- .../x/crypto/curve25519/ladderstep_amd64.s | 2 +- .../x/crypto/curve25519/mul_amd64.s | 2 +- .../x/crypto/curve25519/square_amd64.s | 2 +- vendor/golang.org/x/crypto/ed25519/ed25519.go | 6 +- vendor/golang.org/x/crypto/ssh/cipher.go | 4 +- vendor/golang.org/x/crypto/ssh/kex.go | 8 +- vendor/golang.org/x/crypto/ssh/keys.go | 49 ++++ vendor/golang.org/x/crypto/ssh/server.go | 71 ++++-- vendor/golang.org/x/crypto/ssh/session.go | 20 ++ vendor/golang.org/x/net/idna/idna.go | 64 +++-- vendor/golang.org/x/text/width/kind_string.go | 2 +- vendor/leb.io/hashland/LICENSE | 22 ++ vendor/leb.io/hashland/keccakpg/keccak.go | 224 ++++++++++++++++++ 81 files changed, 3487 insertions(+), 382 deletions(-) create mode 100644 custom_crawl_handlers.go create mode 100644 custom_crawl_requests.go create mode 100644 vendor/github.com/datatogether/archive/collection_item.go create mode 100644 vendor/github.com/datatogether/archive/collection_items.go create mode 100644 vendor/github.com/datatogether/archive/custom_crawl.go create mode 100644 vendor/github.com/datatogether/archive/custom_crawls.go create mode 100644 vendor/github.com/datatogether/sql_datastore/query_filters.go create mode 100644 vendor/github.com/datatogether/sql_datastore/query_orders.go create mode 100644 vendor/github.com/datatogether/sqlutil/readme.md create mode 100644 vendor/github.com/multiformats/go-multihash/.gitignore create mode 100644 vendor/github.com/spaolacci/murmur3/.gitignore create mode 100644 vendor/github.com/spaolacci/murmur3/.travis.yml create mode 100644 vendor/github.com/spaolacci/murmur3/LICENSE create mode 100644 vendor/github.com/spaolacci/murmur3/README.md create mode 100644 vendor/github.com/spaolacci/murmur3/murmur.go create mode 100644 vendor/github.com/spaolacci/murmur3/murmur128.go create mode 100644 vendor/github.com/spaolacci/murmur3/murmur32.go create mode 100644 vendor/github.com/spaolacci/murmur3/murmur64.go create mode 100644 vendor/golang.org/x/crypto/blake2b/blake2x.go create mode 100644 vendor/golang.org/x/crypto/blake2s/blake2x.go create mode 100644 vendor/leb.io/hashland/LICENSE create mode 100644 vendor/leb.io/hashland/keccakpg/keccak.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 1906807..8feb503 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -148,7 +148,7 @@ }, { "ImportPath": "github.com/datatogether/archive", - "Rev": "4bcbd848055be97d5091c4ccc204d88d9c0b0437" + "Rev": "e37b9b11e17706e3ac2b9fdd34deca713e9a64d4" }, { "ImportPath": "github.com/datatogether/config", @@ -196,19 +196,19 @@ }, { "ImportPath": "github.com/datatogether/identity/access_token", - "Rev": "56e601b2b22d8a84b5d559eb27096b8e017b02e2" + "Rev": "f0de01b531a84b91192b73ba5e652b1ce69c3e04" }, { "ImportPath": "github.com/datatogether/identity/user", - "Rev": "56e601b2b22d8a84b5d559eb27096b8e017b02e2" + "Rev": "f0de01b531a84b91192b73ba5e652b1ce69c3e04" }, { "ImportPath": "github.com/datatogether/sql_datastore", - "Rev": "a74da6a9ede6dfeb88280282ac9abaa908dfdfa2" + "Rev": "b0c91273ed161344348fb9c89e90a7b4edcb3a68" }, { "ImportPath": "github.com/datatogether/sqlutil", - "Rev": "ec9f4403bc0568985249d9964ace5867b1e08019" + "Rev": "c471ed88dde35a8af1fb164020b88b65badee7f7" }, { "ImportPath": "github.com/gchaincl/dotsql", @@ -222,11 +222,11 @@ }, { "ImportPath": "github.com/ipfs/go-datastore", - "Rev": "e7613971227c1afc1799b1ec9d3947c592f547eb" + "Rev": "e742294375282d992c112f37943c7f5ca87fb268" }, { "ImportPath": "github.com/ipfs/go-datastore/query", - "Rev": "e7613971227c1afc1799b1ec9d3947c592f547eb" + "Rev": "e742294375282d992c112f37943c7f5ca87fb268" }, { "ImportPath": "github.com/jbenet/go-base58", @@ -259,8 +259,8 @@ }, { "ImportPath": "github.com/multiformats/go-multihash", - "Comment": "0.1.0-75-gd6ebd61", - "Rev": "d6ebd610a180b411b34dba68aa29b651b312281a" + "Comment": "0.1.0-103-gf1ef5a0", + "Rev": "f1ef5a02f28c862ca5a2037907cf76cc6c98dbf9" }, { "ImportPath": "github.com/pborman/uuid", @@ -277,61 +277,66 @@ "Comment": "v0.11.5-46-g5e5dc89", "Rev": "5e5dc898656f695e2a086b8e12559febbfc01562" }, + { + "ImportPath": "github.com/spaolacci/murmur3", + "Comment": "v1.0-6-g4ec5a0f", + "Rev": "4ec5a0f56d4fc178129a8433576bf6f2fe672a9e" + }, { "ImportPath": "golang.org/x/crypto/acme", - "Rev": "e1a4589e7d3ea14a3352255d04b6f1a418845e5e" + "Rev": "b176d7def5d71bdd214203491f89843ed217f420" }, { "ImportPath": "golang.org/x/crypto/acme/autocert", - "Rev": "e1a4589e7d3ea14a3352255d04b6f1a418845e5e" + "Rev": "b176d7def5d71bdd214203491f89843ed217f420" }, { "ImportPath": "golang.org/x/crypto/bcrypt", - "Rev": "e1a4589e7d3ea14a3352255d04b6f1a418845e5e" + "Rev": "b176d7def5d71bdd214203491f89843ed217f420" }, { "ImportPath": "golang.org/x/crypto/blake2b", - "Rev": "e1a4589e7d3ea14a3352255d04b6f1a418845e5e" + "Rev": "b176d7def5d71bdd214203491f89843ed217f420" }, { "ImportPath": "golang.org/x/crypto/blake2s", - "Rev": "e1a4589e7d3ea14a3352255d04b6f1a418845e5e" + "Rev": "b176d7def5d71bdd214203491f89843ed217f420" }, { "ImportPath": "golang.org/x/crypto/blowfish", - "Rev": "e1a4589e7d3ea14a3352255d04b6f1a418845e5e" + "Rev": "b176d7def5d71bdd214203491f89843ed217f420" }, { "ImportPath": "golang.org/x/crypto/curve25519", - "Rev": "e1a4589e7d3ea14a3352255d04b6f1a418845e5e" + "Rev": "b176d7def5d71bdd214203491f89843ed217f420" }, { "ImportPath": "golang.org/x/crypto/ed25519", - "Rev": "e1a4589e7d3ea14a3352255d04b6f1a418845e5e" + "Rev": "b176d7def5d71bdd214203491f89843ed217f420" }, { "ImportPath": "golang.org/x/crypto/ed25519/internal/edwards25519", - "Rev": "e1a4589e7d3ea14a3352255d04b6f1a418845e5e" + "Rev": "b176d7def5d71bdd214203491f89843ed217f420" }, { "ImportPath": "golang.org/x/crypto/sha3", - "Rev": "e1a4589e7d3ea14a3352255d04b6f1a418845e5e" + "Rev": "b176d7def5d71bdd214203491f89843ed217f420" }, { "ImportPath": "golang.org/x/crypto/ssh", - "Rev": "e1a4589e7d3ea14a3352255d04b6f1a418845e5e" + "Rev": "b176d7def5d71bdd214203491f89843ed217f420" }, { "ImportPath": "golang.org/x/net/html", - "Rev": "59a0b19b5533c7977ddeb86b017bf507ed407b12" + "Rev": "1c05540f6879653db88113bc4a2b70aec4bd491f" }, { "ImportPath": "golang.org/x/net/html/atom", - "Rev": "59a0b19b5533c7977ddeb86b017bf507ed407b12" + "Rev": "1c05540f6879653db88113bc4a2b70aec4bd491f" }, { "ImportPath": "golang.org/x/net/idna", - "Rev": "59a0b19b5533c7977ddeb86b017bf507ed407b12" + "Rev": "1c05540f6879653db88113bc4a2b70aec4bd491f" }, { "ImportPath": "golang.org/x/sys/unix", @@ -339,23 +344,27 @@ }, { "ImportPath": "golang.org/x/text/secure/bidirule", - "Rev": "ccbd3f7822129ff389f8ca4858a9b9d4d910531c" + "Rev": "e56139fd9c5bc7244c76116c68e500765bb6db6b" }, { "ImportPath": "golang.org/x/text/transform", - "Rev": "ccbd3f7822129ff389f8ca4858a9b9d4d910531c" + "Rev": "e56139fd9c5bc7244c76116c68e500765bb6db6b" }, { "ImportPath": "golang.org/x/text/unicode/bidi", - "Rev": "ccbd3f7822129ff389f8ca4858a9b9d4d910531c" + "Rev": "e56139fd9c5bc7244c76116c68e500765bb6db6b" }, { "ImportPath": "golang.org/x/text/unicode/norm", - "Rev": "ccbd3f7822129ff389f8ca4858a9b9d4d910531c" + "Rev": "e56139fd9c5bc7244c76116c68e500765bb6db6b" }, { "ImportPath": "golang.org/x/text/width", - "Rev": "ccbd3f7822129ff389f8ca4858a9b9d4d910531c" + "Rev": "e56139fd9c5bc7244c76116c68e500765bb6db6b" + }, + { + "ImportPath": "leb.io/hashland/keccakpg", + "Rev": "e13accbe55f7fa03c73c74ace4cca4c425e47260" } ] } diff --git a/custom_crawl_handlers.go b/custom_crawl_handlers.go new file mode 100644 index 0000000..6b66407 --- /dev/null +++ b/custom_crawl_handlers.go @@ -0,0 +1,91 @@ +package main + +import ( + "encoding/json" + "github.com/datatogether/api/apiutil" + "github.com/datatogether/archive" + "net/http" +) + +func CustomCrawlHandler(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case "OPTIONS": + EmptyOkHandler(w, r) + case "GET": + GetCustomCrawlHandler(w, r) + case "PUT": + SaveCustomCrawlHandler(w, r) + case "DELETE": + DeleteCustomCrawlHandler(w, r) + default: + NotFoundHandler(w, r) + } +} + +func CustomCrawlsHandler(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case "GET": + ListCustomCrawlsHandler(w, r) + case "PUT", "POST": + SaveCustomCrawlHandler(w, r) + default: + NotFoundHandler(w, r) + } +} + +func GetCustomCrawlHandler(w http.ResponseWriter, r *http.Request) { + res := &archive.CustomCrawl{} + args := &CustomCrawlsGetParams{ + Id: r.URL.Path[len("/customcrawls/"):], + } + err := new(CustomCrawls).Get(args, res) + if err != nil { + apiutil.WriteErrResponse(w, http.StatusInternalServerError, err) + return + } + apiutil.WriteResponse(w, res) +} + +func ListCustomCrawlsHandler(w http.ResponseWriter, r *http.Request) { + p := apiutil.PageFromRequest(r) + res := make([]*archive.CustomCrawl, p.Size) + args := &CustomCrawlsListParams{ + Limit: p.Limit(), + Offset: p.Offset(), + OrderBy: "created", + } + err := new(CustomCrawls).List(args, &res) + if err != nil { + apiutil.WriteErrResponse(w, http.StatusInternalServerError, err) + return + } + apiutil.WritePageResponse(w, res, r, p) +} + +func SaveCustomCrawlHandler(w http.ResponseWriter, r *http.Request) { + un := &archive.CustomCrawl{} + if err := json.NewDecoder(r.Body).Decode(un); err != nil { + apiutil.WriteErrResponse(w, http.StatusInternalServerError, err) + return + } + res := &archive.CustomCrawl{} + if err := new(CustomCrawls).Save(un, res); err != nil { + apiutil.WriteErrResponse(w, http.StatusInternalServerError, err) + return + } + apiutil.WriteResponse(w, res) +} + +func DeleteCustomCrawlHandler(w http.ResponseWriter, r *http.Request) { + un := &archive.CustomCrawl{} + if err := json.NewDecoder(r.Body).Decode(un); err != nil { + apiutil.WriteErrResponse(w, http.StatusInternalServerError, err) + return + } + res := &archive.CustomCrawl{} + if err := new(CustomCrawls).Save(un, res); err != nil { + apiutil.WriteErrResponse(w, http.StatusInternalServerError, err) + return + } + apiutil.WriteResponse(w, res) +} diff --git a/custom_crawl_requests.go b/custom_crawl_requests.go new file mode 100644 index 0000000..2f2d3c2 --- /dev/null +++ b/custom_crawl_requests.go @@ -0,0 +1,59 @@ +package main + +import ( + "github.com/datatogether/archive" +) + +type CustomCrawls int + +type CustomCrawlsGetParams struct { + Id string +} + +func (u *CustomCrawls) Get(p *CustomCrawlsGetParams, res *archive.CustomCrawl) (err error) { + url := &archive.CustomCrawl{ + Id: p.Id, + } + err = url.Read(store) + if err != nil { + return err + } + + *res = *url + return nil +} + +type CustomCrawlsListParams struct { + OrderBy string + Limit int + Offset int +} + +func (u *CustomCrawls) List(p *CustomCrawlsListParams, res *[]*archive.CustomCrawl) (err error) { + urls, err := archive.ListCustomCrawls(store, p.Limit, p.Offset) + if err != nil { + return err + } + *res = urls + return nil +} + +func (u *CustomCrawls) Save(model *archive.CustomCrawl, res *archive.CustomCrawl) (err error) { + err = model.Save(store) + if err != nil { + return err + } + + *res = *model + return nil +} + +func (u *CustomCrawls) Delete(model *archive.CustomCrawl, res *archive.CustomCrawl) (err error) { + err = model.Delete(store) + if err != nil { + return err + } + + *res = *model + return nil +} diff --git a/server.go b/server.go index ad961c1..78b490f 100644 --- a/server.go +++ b/server.go @@ -102,6 +102,9 @@ func NewServerRoutes() *http.ServeMux { m.Handle("/uncrawlables", middleware(UncrawlablesHandler)) m.Handle("/uncrawlables/", middleware(UncrawlableHandler)) + m.Handle("/customcrawls", middleware(CustomCrawlsHandler)) + m.Handle("/customcrawls/", middleware(CustomCrawlHandler)) + m.HandleFunc("/.well-known/acme-challenge/", CertbotHandler) return m @@ -138,6 +141,7 @@ func initPostgres() { &archive.Primer{}, &archive.Source{}, &archive.Uncrawlable{}, + &archive.CustomCrawl{}, &archive.Url{}, ) } diff --git a/vendor/github.com/datatogether/archive/collection.go b/vendor/github.com/datatogether/archive/collection.go index 347f348..e5948ea 100644 --- a/vendor/github.com/datatogether/archive/collection.go +++ b/vendor/github.com/datatogether/archive/collection.go @@ -2,7 +2,6 @@ package archive import ( "database/sql" - "encoding/json" "fmt" "github.com/datatogether/sql_datastore" "github.com/datatogether/sqlutil" @@ -26,12 +25,10 @@ type Collection struct { Creator string `json:"creator"` // human-readable title of the collection Title string `json:"title"` + // description of the collection + Description string `json:"description"` // url this collection originates from Url string `json:"url,omitempty"` - // csv column headers, first value must always be "hash" - Schema []string `json:"schema,omitempty"` - // actuall collection contents - Contents [][]string `json:"contents,omitempty"` } func (c Collection) DatastoreType() string { @@ -88,9 +85,9 @@ func (c *Collection) Delete(store datastore.Datastore) error { return store.Delete(c.Key()) } -func (c *Collection) NewSQLModel(id string) sql_datastore.Model { +func (c *Collection) NewSQLModel(key datastore.Key) sql_datastore.Model { return &Collection{ - Id: id, + Id: key.Name(), } } @@ -122,24 +119,14 @@ func (c *Collection) SQLParams(cmd sql_datastore.Cmd) []interface{} { case sql_datastore.CmdList: return nil default: - schemaBytes, err := json.Marshal(c.Schema) - if err != nil { - panic(err) - } - contentBytes, err := json.Marshal(c.Contents) - if err != nil { - panic(err) - } - return []interface{}{ c.Id, c.Created.In(time.UTC), c.Updated.In(time.UTC), c.Creator, c.Title, + c.Description, c.Url, - schemaBytes, - contentBytes, } } } @@ -148,45 +135,25 @@ func (c *Collection) SQLParams(cmd sql_datastore.Cmd) []interface{} { // it expects the request to have used collectionCols() for selection func (c *Collection) UnmarshalSQL(row sqlutil.Scannable) (err error) { var ( - id, creator, title, url string - created, updated time.Time - schemaBytes, contentBytes []byte + id, creator, title, description, url string + created, updated time.Time ) - if err := row.Scan(&id, &created, &updated, &creator, &title, &url, &schemaBytes, &contentBytes); err != nil { + if err := row.Scan(&id, &created, &updated, &creator, &title, &description, &url); err != nil { if err == sql.ErrNoRows { return ErrNotFound } return err } - var schema []string - if schemaBytes != nil { - schema = []string{} - err = json.Unmarshal(schemaBytes, &schema) - if err != nil { - return err - } - } - - var contents [][]string - if contentBytes != nil { - contents = [][]string{} - err = json.Unmarshal(contentBytes, &contents) - if err != nil { - return err - } - } - *c = Collection{ - Id: id, - Created: created.In(time.UTC), - Updated: updated.In(time.UTC), - Creator: creator, - Title: title, - Url: url, - Schema: schema, - Contents: contents, + Id: id, + Created: created.In(time.UTC), + Updated: updated.In(time.UTC), + Creator: creator, + Title: title, + Description: description, + Url: url, } return nil diff --git a/vendor/github.com/datatogether/archive/collection_item.go b/vendor/github.com/datatogether/archive/collection_item.go new file mode 100644 index 0000000..26b113a --- /dev/null +++ b/vendor/github.com/datatogether/archive/collection_item.go @@ -0,0 +1,166 @@ +package archive + +import ( + "database/sql" + "fmt" + "github.com/datatogether/sql_datastore" + "github.com/datatogether/sqlutil" + "github.com/ipfs/go-datastore" +) + +// CollectionItem is an item in a collection. They are urls +// with added collection-specific information. +// This has the effect of storing all of the "main properties" +// of a collection item in the common list of urls +type CollectionItem struct { + // Collection Items are Url's at heart + Url + // need a reference to the collection Id to be set to distinguish + // this item's membership in this particular list + collectionId string + // this item's index in the collection + Index int `json:"index"` + // unique description of this item + Description string `json:"description"` +} + +// DatastoreType is to satisfy sql_datastore.Model interface +func (c CollectionItem) DatastoreType() string { + return "CollectionItem" +} + +// GetId returns the Id of the collectionItem, which is the id +// of the underlying Url +func (c CollectionItem) GetId() string { + return c.Url.Id +} + +// Key is somewhat special as CollectionItems always have a Collection +// as their parent. This relationship is represented in directory-form: +// /Collection:[collection-id]/CollectionItem:[item-id] +func (c CollectionItem) Key() datastore.Key { + return datastore.NewKey(fmt.Sprintf("%s:%s/%s:%s", Collection{}.DatastoreType(), c.collectionId, c.DatastoreType(), c.GetId())) +} + +// Read collection from db +func (c *CollectionItem) Read(store datastore.Datastore) error { + if c.Url.Id == "" && c.Url.Url != "" { + if sqls, ok := store.(*sql_datastore.Datastore); ok { + row := sqls.DB.QueryRow(qUrlByUrlString, c.Url.Url) + prev := &Url{} + if err := prev.UnmarshalSQL(row); err == nil { + c.Id = prev.Id + // exists = true + } + } + } + + ci, err := store.Get(c.Key()) + if err != nil { + return err + } + + got, ok := ci.(*CollectionItem) + if !ok { + return ErrInvalidResponse + } + *c = *got + return nil +} + +// Save a collection item to a store +func (c *CollectionItem) Save(store datastore.Datastore) (err error) { + u := &c.Url + if err := u.Save(store); err != nil { + return err + } + + c.Url = *u + return store.Put(c.Key(), c) +} + +// Delete a collection item +func (c *CollectionItem) Delete(store datastore.Datastore) error { + return store.Delete(c.Key()) +} + +func (c *CollectionItem) NewSQLModel(key datastore.Key) sql_datastore.Model { + l := key.List() + if len(l) == 1 { + return &CollectionItem{ + collectionId: datastore.NamespaceValue(l[0]), + } + } else if len(l) == 2 { + return &CollectionItem{ + collectionId: datastore.NamespaceValue(l[0]), + Url: Url{Id: datastore.NamespaceValue(l[1])}, + } + } + return &CollectionItem{} +} + +// SQLQuery is to satisfy the sql_datastore.Model interface, it +// returns the concrete query for a given type of SQL command +func (c CollectionItem) SQLQuery(cmd sql_datastore.Cmd) string { + switch cmd { + case sql_datastore.CmdCreateTable: + return qCollectionItemCreateTable + case sql_datastore.CmdExistsOne: + return qCollectionItemExists + case sql_datastore.CmdSelectOne: + return qCollectionItemById + case sql_datastore.CmdInsertOne: + return qCollectionItemInsert + case sql_datastore.CmdUpdateOne: + return qCollectionItemUpdate + case sql_datastore.CmdDeleteOne: + return qCollectionItemDelete + case sql_datastore.CmdList: + return qCollectionItems + default: + return "" + } +} + +// SQLQuery is to satisfy the sql_datastore.Model interface, it +// returns this CollectionItem's parameters for a given type of SQL command +func (c *CollectionItem) SQLParams(cmd sql_datastore.Cmd) []interface{} { + switch cmd { + case sql_datastore.CmdSelectOne, sql_datastore.CmdExistsOne, sql_datastore.CmdDeleteOne: + return []interface{}{c.collectionId, c.Url.Id} + case sql_datastore.CmdList: + return []interface{}{c.collectionId} + default: + return []interface{}{ + c.collectionId, + c.Url.Id, + c.Index, + c.Description, + } + } +} + +// UnmarshalSQL reads an sql response into the collection receiver +// it expects the request to have used collectionCols() for selection +func (c *CollectionItem) UnmarshalSQL(row sqlutil.Scannable) (err error) { + var ( + collectionId, urlId, url, hash, title, description string + index int + ) + + if err := row.Scan(&collectionId, &urlId, &hash, &url, &title, &index, &description); err != nil { + if err == sql.ErrNoRows { + return ErrNotFound + } + return err + } + + *c = CollectionItem{ + collectionId: collectionId, + Url: Url{Id: urlId, Hash: hash, Url: url, Title: title}, + Index: index, + Description: description, + } + + return nil +} diff --git a/vendor/github.com/datatogether/archive/collection_items.go b/vendor/github.com/datatogether/archive/collection_items.go new file mode 100644 index 0000000..188627d --- /dev/null +++ b/vendor/github.com/datatogether/archive/collection_items.go @@ -0,0 +1,106 @@ +package archive + +import ( + "github.com/datatogether/sql_datastore" + "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/query" +) + +// ItemCount gets the number of items in the collection +func (c *Collection) ItemCount(store datastore.Datastore) (count int, err error) { + if sqls, ok := store.(*sql_datastore.Datastore); ok { + row := sqls.DB.QueryRow(qCollectionLength, c.Id) + err = row.Scan(&count) + return + } + + // TODO - untested code :( + res, err := store.Query(query.Query{ + Prefix: c.Key().String(), + KeysOnly: true, + }) + if err != nil { + return 0, err + } + + for r := range res.Next() { + if r.Error != nil { + return 0, err + } + if _, ok := r.Value.(*CollectionItem); ok { + count++ + } + } + + return +} + +// SaveItems saves a slice of items to the collection. +// It's up to you to ensure that the "index" param doesn't get all messed up. +// TODO - validate / automate the Index param? +func (c *Collection) SaveItems(store datastore.Datastore, items []*CollectionItem) error { + for _, item := range items { + item.collectionId = c.Id + if err := item.Save(store); err != nil { + return err + } + } + return nil +} + +// DeleteItems removes a given list of items from the collection +func (c *Collection) DeleteItems(store datastore.Datastore, items []*CollectionItem) error { + for _, item := range items { + item.collectionId = c.Id + if err := item.Delete(store); err != nil { + return err + } + } + return nil +} + +// ReadItems reads a bounded set of items from the collection +// the orderby param currently only supports SQL-style input of a single proprty, eg: "index" or "index DESC" +func (c *Collection) ReadItems(store datastore.Datastore, orderby string, limit, offset int) (items []*CollectionItem, err error) { + items = make([]*CollectionItem, limit) + + res, err := store.Query(query.Query{ + Limit: limit, + Offset: offset, + // Keeping in mind that CollectionItem keys take the form /Collection:[id]/CollectionItem:[id] + // and Collections have the key /Collection:[id], the Collection key is the prefix for looking up keys + Prefix: c.Key().String(), + Filters: []query.Filter{ + // Pass in a Filter Type to specify that results must be of type CollectionItem + // In abstract terms this combined with the Prefix query param amounts to querying: + // /Collection:[id]/CollectionItem:* + sql_datastore.FilterKeyTypeEq(CollectionItem{}.DatastoreType()), + }, + Orders: []query.Order{ + query.OrderByValue{ + TypedOrder: sql_datastore.OrderBy(orderby), + }, + }, + }) + if err != nil { + return nil, err + } + + i := 0 + for r := range res.Next() { + if r.Error != nil { + return nil, r.Error + } + + c, ok := r.Value.(*CollectionItem) + if !ok { + return nil, ErrInvalidResponse + } + + items[i] = c + i++ + } + + // fmt.Println(items) + return items[:i], nil +} diff --git a/vendor/github.com/datatogether/archive/collections.go b/vendor/github.com/datatogether/archive/collections.go index 8570d13..d108845 100644 --- a/vendor/github.com/datatogether/archive/collections.go +++ b/vendor/github.com/datatogether/archive/collections.go @@ -1,6 +1,9 @@ package archive import ( + "database/sql" + "fmt" + "github.com/datatogether/sql_datastore" "github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore/query" ) @@ -35,3 +38,34 @@ func ListCollections(store datastore.Datastore, limit, offset int) ([]*Collectio return collections[:i], nil } + +func CollectionsByCreator(store datastore.Datastore, creator, orderby string, limit, offset int) ([]*Collection, error) { + sqls, ok := store.(*sql_datastore.Datastore) + if !ok { + return nil, fmt.Errorf("collections for creator only works with SQL datastores for now") + } + + rows, err := sqls.DB.Query(qCollectionsByCreator, limit, offset, orderby, creator) + if err != nil { + return nil, err + } + + return unmarshalBoundedCollections(rows, limit) +} + +// unmarshalBoundedCollections turns a standard sql.Rows of Collection results into a *Collection slice +func unmarshalBoundedCollections(rows *sql.Rows, limit int) ([]*Collection, error) { + defer rows.Close() + collections := make([]*Collection, limit) + i := 0 + for rows.Next() { + c := &Collection{} + if err := c.UnmarshalSQL(rows); err != nil { + return nil, err + } + collections[i] = c + i++ + } + + return collections[:i], nil +} diff --git a/vendor/github.com/datatogether/archive/custom_crawl.go b/vendor/github.com/datatogether/archive/custom_crawl.go new file mode 100644 index 0000000..f1a7819 --- /dev/null +++ b/vendor/github.com/datatogether/archive/custom_crawl.go @@ -0,0 +1,178 @@ +package archive + +import ( + "database/sql" + "fmt" + "github.com/datatogether/sql_datastore" + "github.com/datatogether/sqlutil" + "github.com/ipfs/go-datastore" + "github.com/pborman/uuid" + "time" +) + +// CustomCrawls are urls that contain content that cannot be extracted +// with traditional web crawling / scraping methods. This model classifies +// the nature of the custom crawl, setting the stage for writing custom scripts +// to extract the underlying content. +type CustomCrawl struct { + // version 4 uuid + Id string `json:"id"` + // Created timestamp rounded to seconds in UTC + Created time.Time `json:"created"` + // Updated timestamp rounded to seconds in UTC + Updated time.Time `json:"updated"` + // Json Web token that created this request + Jwt string `json:"jwt"` + // MorphRunId + MorphRunId string `json:"morphRunId"` + // timestamp this run was completed + DateCompleted time.Time + // repository for code that ran the crawl + GithubRepo string `json:"githubRepo"` + // OriginalUrl + OriginalUrl string `json:"originalUrl"` + // SqliteChecksum + SqliteChecksum string `json:"sqliteChecksum"` +} + +func (CustomCrawl) DatastoreType() string { + return "CustomCrawl" +} + +func (c CustomCrawl) GetId() string { + return c.Id +} + +func (u CustomCrawl) Key() datastore.Key { + return datastore.NewKey(fmt.Sprintf("%s:%s", u.DatastoreType(), u.GetId())) +} + +// Read custom crawl from db +func (c *CustomCrawl) Read(store datastore.Datastore) error { + + if c.Id != "" { + ci, err := store.Get(c.Key()) + if err != nil { + return err + } + + got, ok := ci.(*CustomCrawl) + if !ok { + return ErrInvalidResponse + } + *c = *got + return nil + } + + return ErrNotFound +} + +// Save a custom crawl +func (c *CustomCrawl) Save(store datastore.Datastore) (err error) { + var exists bool + + if c.Id != "" { + exists, err = store.Has(c.Key()) + if err != nil { + return err + } + } + + if !exists { + c.Id = uuid.New() + c.Created = time.Now().Round(time.Second) + c.Updated = c.Created + } else { + c.Updated = time.Now().Round(time.Second) + } + + return store.Put(c.Key(), c) +} + +// Delete a custom crawl, should only do for erronious additions +func (c *CustomCrawl) Delete(store datastore.Datastore) error { + return store.Delete(c.Key()) +} + +func (c *CustomCrawl) NewSQLModel(key datastore.Key) sql_datastore.Model { + return &CustomCrawl{ + Id: key.Name(), + } +} + +func (c *CustomCrawl) SQLQuery(cmd sql_datastore.Cmd) string { + switch cmd { + case sql_datastore.CmdCreateTable: + return qCustomCrawlCreateTable + case sql_datastore.CmdExistsOne: + return qCustomCrawlExists + case sql_datastore.CmdSelectOne: + return qCustomCrawlById + case sql_datastore.CmdInsertOne: + return qCustomCrawlInsert + case sql_datastore.CmdUpdateOne: + return qCustomCrawlUpdate + case sql_datastore.CmdDeleteOne: + return qCustomCrawlDelete + case sql_datastore.CmdList: + return qCustomCrawlsList + default: + return "" + } +} + +// SQLParams formats a custom crawl struct for inserting / updating into postgres +func (c *CustomCrawl) SQLParams(cmd sql_datastore.Cmd) []interface{} { + switch cmd { + case sql_datastore.CmdList: + return []interface{}{} + case sql_datastore.CmdSelectOne, sql_datastore.CmdExistsOne, sql_datastore.CmdDeleteOne: + return []interface{}{c.Id} + default: + return []interface{}{ + c.Id, + c.Created.In(time.UTC), + c.Updated.In(time.UTC), + c.Jwt, + c.MorphRunId, + c.DateCompleted, + c.GithubRepo, + c.OriginalUrl, + c.SqliteChecksum, + } + } +} + +// UnmarshalSQL reads an sql response into the custom crawl receiver +// it expects the request to have used custom crawlCols() for selection +func (c *CustomCrawl) UnmarshalSQL(row sqlutil.Scannable) (err error) { + var ( + created, updated, dateCompleted time.Time + id, jwt, morphRunId, githubRepo, originalUrl, sqliteChecksum string + ) + + if err := row.Scan( + &id, &created, &updated, + &jwt, &morphRunId, &dateCompleted, + &githubRepo, &originalUrl, + &sqliteChecksum); err != nil { + if err == sql.ErrNoRows { + return ErrNotFound + } + return err + } + + *c = CustomCrawl{ + Id: id, + Created: created.In(time.UTC), + Updated: updated.In(time.UTC), + Jwt: jwt, + MorphRunId: morphRunId, + DateCompleted: dateCompleted, + GithubRepo: githubRepo, + OriginalUrl: originalUrl, + SqliteChecksum: sqliteChecksum, + } + + return nil +} diff --git a/vendor/github.com/datatogether/archive/custom_crawls.go b/vendor/github.com/datatogether/archive/custom_crawls.go new file mode 100644 index 0000000..002065d --- /dev/null +++ b/vendor/github.com/datatogether/archive/custom_crawls.go @@ -0,0 +1,52 @@ +package archive + +import ( + "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/query" +) + +func ListCustomCrawls(store datastore.Datastore, limit, offset int) ([]*CustomCrawl, error) { + q := query.Query{ + Prefix: CustomCrawl{}.DatastoreType(), + Limit: limit, + Offset: offset, + } + + res, err := store.Query(q) + if err != nil { + return nil, err + } + + uncrawlables := make([]*CustomCrawl, limit) + i := 0 + for r := range res.Next() { + if r.Error != nil { + return nil, err + } + c, ok := r.Value.(*CustomCrawl) + if !ok { + return nil, ErrInvalidResponse + } + + uncrawlables[i] = c + i++ + } + + return uncrawlables[:i], nil +} + +// func UnmarshalBoundedCustomCrawls(rows *sql.Rows, limit int) ([]*CustomCrawl, error) { +// defer rows.Close() +// subuncrawlables := make([]*CustomCrawl, limit) +// i := 0 +// for rows.Next() { +// u := &CustomCrawl{} +// if err := u.UnmarshalSQL(rows); err != nil { +// return nil, err +// } +// subuncrawlables[i] = u +// i++ +// } + +// return subuncrawlables[:i], nil +// } diff --git a/vendor/github.com/datatogether/archive/datarepo.go b/vendor/github.com/datatogether/archive/datarepo.go index eb31195..0e6283f 100644 --- a/vendor/github.com/datatogether/archive/datarepo.go +++ b/vendor/github.com/datatogether/archive/datarepo.go @@ -80,9 +80,9 @@ func (d *DataRepo) Delete(store datastore.Datastore) error { return store.Delete(d.Key()) } -func (d *DataRepo) NewSQLModel(id string) sql_datastore.Model { +func (d *DataRepo) NewSQLModel(key datastore.Key) sql_datastore.Model { return &DataRepo{ - Id: id, + Id: key.Name(), } } diff --git a/vendor/github.com/datatogether/archive/link.go b/vendor/github.com/datatogether/archive/link.go index a548311..707b9c2 100644 --- a/vendor/github.com/datatogether/archive/link.go +++ b/vendor/github.com/datatogether/archive/link.go @@ -52,7 +52,7 @@ func (l *Link) Read(store datastore.Datastore) (err error) { return ErrNotFound } - // TODO - can't use "store.Get" here b/c we aren't actually storing + // TODO - can't use "store.Get" here b/c we aren't actually storing links by a cannonical ID if sqlStore, ok := store.(*sql_datastore.Datastore); ok { row := sqlStore.DB.QueryRow(qLinkRead, l.Src.Url, l.Dst.Url) return l.UnmarshalSQL(row) @@ -109,9 +109,9 @@ func (l *Link) calcHash() { l.Hash = hex.EncodeToString(mhBuf) } -func (l *Link) NewSQLModel(id string) sql_datastore.Model { +func (l *Link) NewSQLModel(key datastore.Key) sql_datastore.Model { return &Link{ - Hash: id, + Hash: key.Name(), Src: l.Src, Dst: l.Dst, } diff --git a/vendor/github.com/datatogether/archive/metadata.go b/vendor/github.com/datatogether/archive/metadata.go index 893e67b..3ddcbb3 100644 --- a/vendor/github.com/datatogether/archive/metadata.go +++ b/vendor/github.com/datatogether/archive/metadata.go @@ -208,7 +208,7 @@ func (m *Metadata) Write(db *sql.DB) error { // TODO - this is a straight set, should be derived from consensus calculation u.Title = str - if err := u.Update(store); err != nil { + if err := u.Save(store); err != nil { return } }() diff --git a/vendor/github.com/datatogether/archive/primer.go b/vendor/github.com/datatogether/archive/primer.go index 5040350..4ae7c45 100644 --- a/vendor/github.com/datatogether/archive/primer.go +++ b/vendor/github.com/datatogether/archive/primer.go @@ -175,15 +175,14 @@ func (p *Primer) Save(store datastore.Datastore) (err error) { } return store.Put(p.Key(), p) - return nil } func (p *Primer) Delete(store datastore.Datastore) error { return store.Delete(p.Key()) } -func (p *Primer) NewSQLModel(id string) sql_datastore.Model { - return &Primer{Id: id} +func (p *Primer) NewSQLModel(key datastore.Key) sql_datastore.Model { + return &Primer{Id: key.Name()} } func (p *Primer) SQLQuery(cmd sql_datastore.Cmd) string { diff --git a/vendor/github.com/datatogether/archive/queries.go b/vendor/github.com/datatogether/archive/queries.go index 143e662..32d3418 100644 --- a/vendor/github.com/datatogether/archive/queries.go +++ b/vendor/github.com/datatogether/archive/queries.go @@ -8,32 +8,48 @@ CREATE TABLE IF NOT EXISTS collections ( updated timestamp NOT NULL, creator text NOT NULL DEFAULT '', title text NOT NULL DEFAULT '', - url text NOT NULL DEFAULT '', - schema json, - contents json + url text NOT NULL DEFAULT '' );` +// list collections by reverse cronological date created +// paginated +const qCollections = ` +SELECT + id, created, updated, creator, title, description, url +FROM collections +ORDER BY created DESC +LIMIT $1 OFFSET $2;` + +// list collections by creator +const qCollectionsByCreator = ` +SELECT + id, created, updated, creator, title, description, url +FROM collections +WHERE creator = $4 +ORDER BY $3 +LIMIT $1 OFFSET $2;` + // check for existence of a collection const qCollectionExists = ` - SELECT exists(SELECT 1 FROM collections WHERE id = $1) +SELECT exists(SELECT 1 FROM collections WHERE id = $1) ` // insert a collection const qCollectionInsert = ` INSERT INTO collections - (id, created, updated, creator, title, url, schema, contents ) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8);` + (id, created, updated, creator, title, description, url ) +VALUES ($1, $2, $3, $4, $5, $6, $7);` // update an existing collection, selecting by ID const qCollectionUpdate = ` UPDATE collections -SET created=$2, updated=$3, creator=$4, title=$5, url=$6, schema=$7, contents=$8 +SET created=$2, updated=$3, creator=$4, title=$5, description=$6, url=$7 WHERE id = $1;` // read collection info by ID const qCollectionById = ` SELECT - id, created, updated, creator, title, url, schema, contents + id, created, updated, creator, title, description, url FROM collections WHERE id = $1;` @@ -42,13 +58,49 @@ const qCollectionDelete = ` DELETE from collections WHERE id = $1;` -// list collections by reverse cronological date created -// paginated -const qCollections = ` +const qCollectionItemCreateTable = ` +CREATE TABLE IF NOT EXISTS collection_items ( + collection_id UUID NOT NULL, + url_id text NOT NULL default '', + index integer NOT NULL default -1, + description text NOT NULL default '', + PRIMARY KEY (collection_id, url_id) +);` + +const qCollectionItemInsert = ` +INSERT INTO collection_items + (collection_id, url_id, index, description) +VALUES + ($1, $2, $3, $4);` + +const qCollectionItemUpdate = ` +UPDATE collection_items +SET index = $3, description = $4 +WHERE collection_id = $1 and url_id = $2;` + +const qCollectionItemDelete = ` +DELETE FROM collection_items +WHERE collection_id = $1 AND url_id = $2;` + +const qCollectionItemExists = ` +SELECT exists(SELECT 1 FROM collection_items where collection_id = $1 AND url_id = $2);` + +const qCollectionItemById = ` SELECT - id, created, updated, creator, title, url, schema, contents -FROM collections -ORDER BY created DESC + ci.collection_id, u.id, u.hash, u.url, u.title, ci.index, ci.description +FROM collection_items as ci, urls as u +WHERE collection_id = $1 AND url_id = $2 AND u.id = ci.url_id;` + +const qCollectionLength = ` +SELECT count(1) FROM collection_items WHERE collection_id = $1;` + +const qCollectionItems = ` +SELECT + ci.collection_id, u.id, u.hash, u.url, u.title, ci.index, ci.description +FROM collection_items as ci, urls as u +WHERE collection_id = $4 +AND u.id = ci.url_id +ORDER BY $3 LIMIT $1 OFFSET $2;` // insert a dataRepo @@ -726,14 +778,14 @@ set where id = $1;` const qUncrawlableByUrl = ` -select +SELECT id, url,created,updated,creator_key_id, name,email,event_name,agency_name, agency_id,subagency_id,org_id,suborg_id,subprimer_id, ftp,database,interactive,many_files, comments -from uncrawlables -where url = $1;` +FROM uncrawlables +WHERE url = $1;` const qUncrawlableById = ` select @@ -748,3 +800,61 @@ where id = $1;` const qUncrawlableDelete = ` delete from uncrawlables where url = $1;` + +const qCustomCrawlCreateTable = ` +CREATE TABLE IF NOT EXISTS custom_crawls ( + id UUID PRIMARY KEY NOT NULL, + created timestamp NOT NULL default (now() at time zone 'utc'), + updated timestamp NOT NULL default (now() at time zone 'utc'), + jwt text NOT NULL default '', + morphRunId text NOT NULL default '', + dateCompleted timestamp NOT NULL default (now() at time zone 'utc'), + githubRepo text NOT NULL default '', + originalUrl text NOT NULL default '', + sqliteChecksum text NOT NULL default '' +);` + +const qCustomCrawlExists = `SELECT exists(SELECT 1 FROM custom_crawls WHERE id = $1)` + +const qCustomCrawlsList = ` +select + id, created, updated, + jwt, morphRunId, dateCompleted, githubRepo, originalUrl, + sqliteChecksum +from custom_crawls +order by created DESC +limit $1 offset $2` + +const qCustomCrawlInsert = ` +insert into custom_crawls + (id, created, updated, + jwt, morphRunId, dateCompleted, githubRepo, originalUrl, + sqliteChecksum) +values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` + +const qCustomCrawlUpdate = ` +update custom_crawls +set + created = $2, updated = $3, + jwt = $4, morphRunId = $5, dateCompleted = $6, githubRepo = $7, originalUrl = $8, + sqliteChecksum = $9 +where id = $1` + +const qCustomCrawlByUrl = ` +SELECT + id, created, updated, + jwt, morphRunId, dateCompleted, githubRepo, originalUrl, + sqliteChecksum +FROM custom_crawls +WHERE url = $1;` + +const qCustomCrawlById = ` +select + id, created, updated, + jwt, morphRunId, dateCompleted, githubRepo, originalUrl, + sqliteChecksum +from custom_crawls +where id = $1;` + +const qCustomCrawlDelete = ` +delete from custom_crawls where id = $1;` diff --git a/vendor/github.com/datatogether/archive/source.go b/vendor/github.com/datatogether/archive/source.go index 7680437..b9a647c 100644 --- a/vendor/github.com/datatogether/archive/source.go +++ b/vendor/github.com/datatogether/archive/source.go @@ -135,7 +135,7 @@ func (c *Source) AsUrl(db *sql.DB) (*Url, error) { u := &Url{Url: addr.String()} if err := u.Read(store); err != nil { if err == ErrNotFound { - if err := u.Insert(store); err != nil { + if err := u.Save(store); err != nil { return u, err } } else { @@ -243,9 +243,9 @@ func (s *Source) Delete(store datastore.Datastore) error { return store.Delete(s.Key()) } -func (s *Source) NewSQLModel(id string) sql_datastore.Model { +func (s *Source) NewSQLModel(key datastore.Key) sql_datastore.Model { return &Source{ - Id: id, + Id: key.Name(), Url: s.Url, } } diff --git a/vendor/github.com/datatogether/archive/uncrawlable.go b/vendor/github.com/datatogether/archive/uncrawlable.go index ef7bb13..91477a5 100644 --- a/vendor/github.com/datatogether/archive/uncrawlable.go +++ b/vendor/github.com/datatogether/archive/uncrawlable.go @@ -124,9 +124,9 @@ func (u *Uncrawlable) Delete(store datastore.Datastore) error { return store.Delete(u.Key()) } -func (u *Uncrawlable) NewSQLModel(id string) sql_datastore.Model { +func (u *Uncrawlable) NewSQLModel(key datastore.Key) sql_datastore.Model { return &Uncrawlable{ - Id: id, + Id: key.Name(), Url: u.Url, } } diff --git a/vendor/github.com/datatogether/archive/url.go b/vendor/github.com/datatogether/archive/url.go index 04362fa..4a29880 100644 --- a/vendor/github.com/datatogether/archive/url.go +++ b/vendor/github.com/datatogether/archive/url.go @@ -39,6 +39,7 @@ var notContentExtensions = map[string]bool{ } // URL represents... a url. +// TODO - consider renaming to Resource type Url struct { // version 4 uuid // urls can/should/must also be be uniquely identified by Url @@ -47,9 +48,9 @@ type Url struct { // any normalization. Url strings must always be absolute. Url string `json:"url"` // Created timestamp rounded to seconds in UTC - Created time.Time `json:"created"` + Created time.Time `json:"created,omitempty"` // Updated timestamp rounded to seconds in UTC - Updated time.Time `json:"updated"` + Updated time.Time `json:"updated,omitempty"` // Timestamp for most recent GET request LastGet *time.Time `json:"lastGet,omitempty"` @@ -212,7 +213,7 @@ func (u *Url) HandleGetResponse(db *sql.DB, res *http.Response, done func(err er } } - err = u.Update(store) + err = u.Save(store) if err != nil { return } @@ -385,33 +386,59 @@ func (u *Url) Read(store datastore.Datastore) error { return ErrNotFound } -// Insert (create) -func (u *Url) Insert(store datastore.Datastore) error { - u.Created = time.Now().Round(time.Second) - u.Updated = u.Created - u.Id = uuid.New() - return store.Put(u.Key(), u) -} +func (u *Url) Save(store datastore.Datastore) (err error) { + var exists bool -// Update url db entry -func (u *Url) Update(store datastore.Datastore) error { - // Need to fetch ID - if u.Url != "" && u.Id == "" { - prev := &Url{Url: u.Url} - if err := prev.Read(store); err != ErrNotFound { + if u.Id != "" { + exists, err = store.Has(u.Key()) + if err != nil { return err } - u.Id = prev.Id + } else if sqls, ok := store.(*sql_datastore.Datastore); ok { + // if no Id is set, attempt to set one + if u.Url != "" { + row := sqls.DB.QueryRow(qUrlByUrlString, u.Url) + prev := &Url{} + if err := prev.UnmarshalSQL(row); err == nil { + u.Id = prev.Id + exists = true + } + } + } + + // TODO - support fetching ID via url entry + // // Need to fetch ID + // if u.Url != "" && u.Id == "" { + // prev := &Url{Url: u.Url} + // if err := prev.Read(store); err != ErrNotFound { + // return err + // } + // u.Id = prev.Id + // } + + if err = u.validate(); err != nil { + return } - u.Updated = time.Now().Round(time.Second) + if !exists { + u.Id = uuid.New() + u.Created = time.Now().Round(time.Second).In(time.UTC) + u.Updated = u.Created + } else { + u.Updated = time.Now().Round(time.Second).In(time.UTC) + } + + return store.Put(u.Key(), u) +} + +func (u *Url) validate() error { if u.ContentLength < -1 { u.ContentLength = -1 } if u.Status < -1 { u.Status = -1 } - return store.Put(u.Key(), u) + return nil } // Delete a url, should only do for erronious additions @@ -448,7 +475,7 @@ func (u *Url) ExtractDocLinks(db *sql.DB, doc *goquery.Document) ([]*Link, error // Check to see if url exists, creating if not if err = dst.Read(store); err != nil { if err == ErrNotFound { - if err = dst.Insert(store); err != nil { + if err = dst.Save(store); err != nil { return } } else { @@ -497,9 +524,9 @@ func (u *Url) HeadersMap() (headers map[string]string) { return } -func (u *Url) NewSQLModel(id string) sql_datastore.Model { +func (u *Url) NewSQLModel(key datastore.Key) sql_datastore.Model { return &Url{ - Id: id, + Id: key.Name(), Url: u.Url, Hash: u.Hash, } diff --git a/vendor/github.com/datatogether/identity/user/queries.go b/vendor/github.com/datatogether/identity/user/queries.go index eb89089..8285e3b 100644 --- a/vendor/github.com/datatogether/identity/user/queries.go +++ b/vendor/github.com/datatogether/identity/user/queries.go @@ -2,10 +2,64 @@ package user const qUsersSearch = ` SELECT - id, created, updated, username, type, name, description, home_url, email, current_key, email_confirmed, is_admin + id, created, updated, username, type, name, description, home_url, color, + thumb_url, profile_url, poster_url, email, current_key, email_confirmed, is_admin FROM users WHERE username ilike $1 OR name ilike $1 OR email ilike $1 LIMIT $2 OFFSET $3;` + +const qUserInsert = ` +INSERT INTO users + (id, created, updated, username, type, password_hash, email, name, description, home_url, color, thumb_url, profile_url, poster_url, email_confirmed, access_token) +VALUES + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)` + +const qCommunityRemoveUser = ` +delete from community_users +where + community_id = $1 AND + user_id = $2;` + +const qUserAcceptCommunityInvite = ` +update community_users +set joined = $3 +where + community_id = $1 AND + user_id = $2;` + +const qCommunityInviteUser = ` +insert into community_users + (community_id, user_id, invited_by) +values + ($1, $2, $3, $4);` + +const qCommunityMembers = ` +SELECT + users.id, users.created, users.updated, + users.username, users.type, users.name, users.description, users.home_url, + users.color, users.thumb_url, users.profile_url, users.poster_url, users.email, + users.current_key, users.email_confirmed, users.is_admin +FROM community_users, users +WHERE + community_users.community_id = $1 AND + community_users.user_id = users.id AND + joined is not null +ORDER BY $2 +LIMIT $3 OFFSET $4;` + +const qUserCommunities = ` +SELECT + users.id, users.created, users.updated, + users.username, users.type, users.name, users.description, users.home_url, + users.color, users.thumb_url, users.profile_url, users.poster_url, users.email, + users.current_key, users.email_confirmed, users.is_admin +FROM community_users, users +WHERE + community_users.user_id = $1 AND + community_users.community_id = users.id AND + joined is not null +ORDER BY $2 +LIMIT $3 OFFSET $4;` diff --git a/vendor/github.com/datatogether/identity/user/user.go b/vendor/github.com/datatogether/identity/user/user.go index 5cfbaf2..21f9d63 100644 --- a/vendor/github.com/datatogether/identity/user/user.go +++ b/vendor/github.com/datatogether/identity/user/user.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/datatogether/errors" "github.com/datatogether/identity/access_token" + // "github.com/datatogether/identity/community" "github.com/datatogether/sqlutil" "github.com/pborman/uuid" "strings" @@ -29,13 +30,21 @@ type User struct { // password, only really used on account creation password string // user's email address - Email string `json:"email" sql:"email"` + Email string `json:"email,omitempty" sql:"email"` // user name field. could be first[space]last, but not strictly enforced Name string `json:"name" sql:"name"` // user-filled description of self Description string `json:"description" sql:"description"` // url this user wants the world to click HomeUrl string `json:"home_url" sql:"home_url"` + // color this user likes to use as their theme color + Color string `json:"color"` + // url for their thumbnail + ThumbUrl string `json:"thumbUrl"` + // profile photo url + ProfileUrl string `json:"profileUrl"` + // header image url + PosterUrl string `json:"posterUrl"` // sh256 multihash of public key that this user is currently using for signatures CurrentKey string `json:"currentKey"` // have we ever successfully sent this user an email? @@ -47,7 +56,7 @@ type User struct { // often users get auto-generated based on IP for rate lmiting & stuff // this flag tracks that. // TODO - for this to be useful it'll need to be Exported - Anonymous bool `json:"_"` + Anonymous bool `json:",omitempty"` } // create a new user struct pointer from a provided id string @@ -73,7 +82,7 @@ func NewUserFromString(s string) *User { } func userColumns() string { - return "id, created, updated, username, type, name, description, home_url, email, current_key, email_confirmed, is_admin" + return "id, created, updated, username, type, name, description, home_url, color, thumb_url, profile_url, poster_url, email, current_key, email_confirmed, is_admin" } // _user is a private struct for marshaling & unmarshaling @@ -211,7 +220,7 @@ func (u *User) Save(db sqlutil.Transactable) error { } u.accessToken = token - if _, e = db.Exec("INSERT INTO users VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, false, $12, false)", u.Id, u.Created, u.Updated, u.Username, u.Type, hash, u.Email, u.Name, u.Description, u.HomeUrl, u.emailConfirmed, u.accessToken); e != nil { + if _, e = db.Exec(qUserInsert, u.Id, u.Created, u.Updated, u.Username, u.Type, hash, u.Email, u.Name, u.Description, u.HomeUrl, u.Color, u.ThumbUrl, u.ProfileUrl, u.PosterUrl, u.emailConfirmed, u.accessToken); e != nil { return errors.NewFmtError(500, e.Error()) } @@ -241,7 +250,7 @@ func (u *User) Save(db sqlutil.Transactable) error { return errors.New500Error(err.Error()) } - if _, err := tx.Exec("UPDATE users SET updated=$2, username= $3, type=$4, name=$5, description=$6, home_url= $7, email_confirmed=$8, access_token=$9 WHERE id= $1 AND deleted=false", u.Id, u.Updated, u.Username, u.Type, u.Name, u.Description, u.HomeUrl, u.emailConfirmed, u.accessToken); err != nil { + if _, err := tx.Exec("UPDATE users SET updated=$2, username= $3, type=$4, name=$5, description=$6, home_url= $7, color = $8, thumb_url = $9, profile_url = $10, poster_url = $11, email_confirmed=$12 WHERE id= $1 AND deleted=false", u.Id, u.Updated, u.Username, u.Type, u.Name, u.Description, u.HomeUrl, u.Color, u.ThumbUrl, u.ProfileUrl, u.PosterUrl, u.emailConfirmed); err != nil { tx.Rollback() // return errors.Error500IfErr(err) return err @@ -292,7 +301,7 @@ func (u *User) Delete(db sqlutil.Transactable) error { return errors.Error500IfErr(err) } - // TODO - Users that delete their profile will need to have all their datasets deleted as well + // TODO - Users that delete their profile will need to have all their dependant stuff deleted as well if err := tx.Commit(); err != nil { tx.Rollback() @@ -374,12 +383,14 @@ func (u *User) validateUpdate(db sqlutil.Queryable, prev *User) error { u.Email = prev.Email } + // un-clobber access token + u.accessToken = prev.accessToken + if err := u.valFields(); err != nil { return err } if u.Username != prev.Username { - // log.Info(u.Username, prev.Username) if taken, err := UsernameTaken(db, u.Username); err != nil { return err } else if taken { @@ -477,13 +488,14 @@ func (u *User) SavePassword(db sqlutil.Execable, password string) error { func (u *User) UnmarshalSQL(row sqlutil.Scannable) error { var ( id, username, name, email, description, homeUrl, key string + color, thumbUrl, profileUrl, posterUrl string created, updated int64 emailConfirmed, isAdmin bool t UserType ) - // "id, created, updated, username, type, name, email, email_confirmed" - if err := row.Scan(&id, &created, &updated, &username, &t, &name, &description, &homeUrl, &email, &key, &emailConfirmed, &isAdmin); err != nil { + if err := row.Scan(&id, &created, &updated, &username, &t, &name, &description, &homeUrl, + &color, &thumbUrl, &profileUrl, &posterUrl, &email, &key, &emailConfirmed, &isAdmin); err != nil { return err } *u = User{ @@ -496,6 +508,11 @@ func (u *User) UnmarshalSQL(row sqlutil.Scannable) error { Email: email, emailConfirmed: emailConfirmed, Description: description, + HomeUrl: homeUrl, + Color: color, + ThumbUrl: thumbUrl, + ProfileUrl: profileUrl, + PosterUrl: posterUrl, isAdmin: isAdmin, CurrentKey: key, } @@ -503,13 +520,13 @@ func (u *User) UnmarshalSQL(row sqlutil.Scannable) error { return nil } -// func (u *User) AcceptGroupInvite(db *sql.DB, g *Group) error { -// t := time.Now().Round(time.Second).In(time.UTC) -// _, err := db.Exec(qUserAcceptGroupInvite, g.Id, u.Id, t) -// return err -// } +func (u *User) AcceptCommunityInvite(db *sql.DB, c *User) error { + t := time.Now().Round(time.Second).In(time.UTC) + _, err := db.Exec(qUserAcceptCommunityInvite, c.Id, u.Id, t) + return err +} -// func (u *User) DeclineGroupInvite(db *sql.DB, g *Group) error { -// _, err := db.Exec(qGroupRemoveUser, g.Id, u.Id) -// return err -// } +func (u *User) DeclineCommunityInvite(db *sql.DB, c *User) error { + _, err := db.Exec(qCommunityRemoveUser, c.Id, u.Id) + return err +} diff --git a/vendor/github.com/datatogether/identity/user/user_requests.go b/vendor/github.com/datatogether/identity/user/user_requests.go index 9e7e41e..ceb2f6c 100644 --- a/vendor/github.com/datatogether/identity/user/user_requests.go +++ b/vendor/github.com/datatogether/identity/user/user_requests.go @@ -7,8 +7,6 @@ import ( ) // Requests holds all types of requests for users -// it's based on an int b/c it's stateless and Go lets us -// do this sort of thing type UserRequests struct { Store sqlutil.Transactable } @@ -18,13 +16,14 @@ type UserRequests struct { type UsersListParams struct { // the user performing the request User *User `required:"true"` + Type UserType // users requests embeds pagination info Limit int Offset int } func (r UserRequests) List(p *UsersListParams, res *[]*User) error { - users, err := ReadUsers(r.Store, p.Limit, p.Offset) + users, err := ReadUsers(r.Store, p.Type, p.Limit, p.Offset) if err != nil { return err } @@ -75,9 +74,10 @@ type UsersSaveParams struct { } func (r UserRequests) Save(p *UsersSaveParams, res *User) error { - if !p.User.isAdmin && p.User.Id != p.Subject.Id { - return errors.ErrAccessDenied - } + // TODO - restore w community membership lookup + // if !p.User.isAdmin && p.User.Id != p.Subject.Id { + // return errors.ErrAccessDenied + // } if err := p.Subject.Save(r.Store); err != nil { return err @@ -103,3 +103,75 @@ func (r UserRequests) Search(p *UsersSearchParams, res *[]*User) error { *res = users return nil } + +type UsersCommunityMembersParams struct { + User *User + Community *User + Order string + Limit int + Offset int +} + +func (r UserRequests) CommunityMembers(p *UsersCommunityMembersParams, res *[]*User) error { + // override order for now + p.Order = "community_users.joined DESC" + + users, err := CommunityUsers(r.Store, p.Community, p.Order, p.Limit, p.Offset) + if err != nil { + return err + } + + *res = users + return nil +} + +type UsersCommunitiesParams struct { + User *User + Order string + Limit int + Offset int +} + +func (r UserRequests) UserCommunities(p *UsersCommunitiesParams, res *[]*User) error { + // override order for now + p.Order = "users.created DESC" + + users, err := UserCommunities(r.Store, p.User, p.Order, p.Limit, p.Offset) + if err != nil { + return err + } + + *res = users + return nil +} + +// type UsersCreateCommunityParams struct { +// User *User +// Name string +// } + +// func (r UserRequests) CreateCommunity(p *UsersCreateCommunityParams, res *User) error { +// return nil +// } + +// type UsersDeleteCommunityParams struct { +// } + +// func (r UserRequests) DeleteCommunity(p *UsersDeleteCommunityParams, res *User) error { +// return nil +// } + +// type UsersCreateCommunityInviteParams struct { +// User *User +// } + +// func (r UserRequests) CreateCommunityInvite(p *UsersCreateCommunityParams, res *User) error { +// return nil +// } + +// type UsersRemoveCommunityUserParams struct { +// } + +// func (r UserRequests) UsersRemoveCommunityUser(p *UsersRemoveCommunityUserParams, res *bool) error { +// return nil +// } diff --git a/vendor/github.com/datatogether/identity/user/user_type.go b/vendor/github.com/datatogether/identity/user/user_type.go index 5d29816..1dbaaa0 100644 --- a/vendor/github.com/datatogether/identity/user/user_type.go +++ b/vendor/github.com/datatogether/identity/user/user_type.go @@ -10,7 +10,7 @@ type UserType int const ( UserTypeNone UserType = iota UserTypeUser - UserTypeOrganization + UserTypeCommunity ) func (ut UserType) String() string { @@ -19,15 +19,15 @@ func (ut UserType) String() string { return "none" case UserTypeUser: return "user" - case UserTypeOrganization: - return "organization" + case UserTypeCommunity: + return "community" } return "unknown" } func (ut UserType) MarshalJSON() ([]byte, error) { - s, ok := map[UserType]string{UserTypeNone: "none", UserTypeUser: "user", UserTypeOrganization: "organization"}[ut] + s, ok := map[UserType]string{UserTypeNone: "none", UserTypeUser: "user", UserTypeCommunity: "community"}[ut] if !ok { return nil, fmt.Errorf("invalid UserType %d", ut) } @@ -41,7 +41,7 @@ func (ut *UserType) UnmarshalJSON(data []byte) error { return fmt.Errorf("User type should be a string, got %s", data) } - got, ok := map[string]UserType{"none": UserTypeNone, "user": UserTypeUser, "organization": UserTypeOrganization}[s] + got, ok := map[string]UserType{"none": UserTypeNone, "user": UserTypeUser, "community": UserTypeCommunity}[s] if !ok { return fmt.Errorf("invalid UserType %q", s) } diff --git a/vendor/github.com/datatogether/identity/user/users.go b/vendor/github.com/datatogether/identity/user/users.go index 10672d2..720ba2d 100644 --- a/vendor/github.com/datatogether/identity/user/users.go +++ b/vendor/github.com/datatogether/identity/user/users.go @@ -7,10 +7,18 @@ import ( "github.com/datatogether/sqlutil" ) -// grab all users -func ReadUsers(db sqlutil.Queryable, limit, offset int) (users []*User, err error) { +// ReadUsers reads a page of users +func ReadUsers(db sqlutil.Queryable, userType UserType, limit, offset int) (users []*User, err error) { users = make([]*User, 0) - rows, e := db.Query(fmt.Sprintf("SELECT %s FROM users WHERE deleted=false ORDER BY created DESC LIMIT $1 OFFSET $2", userColumns()), limit, offset) + // TODO - make this not bad + query := fmt.Sprintf("SELECT %s FROM users WHERE deleted=false ORDER BY created DESC LIMIT $1 OFFSET $2", userColumns()) + if userType == UserTypeCommunity { + query = fmt.Sprintf("SELECT %s FROM users WHERE deleted=false AND type = 2 ORDER BY created DESC LIMIT $1 OFFSET $2", userColumns()) + } else if userType == UserTypeUser { + query = fmt.Sprintf("SELECT %s FROM users WHERE deleted=false AND type = 1 ORDER BY created DESC LIMIT $1 OFFSET $2", userColumns()) + } + + rows, e := db.Query(query, limit, offset) if e != nil { if e == sql.ErrNoRows { return []*User{}, nil @@ -49,3 +57,35 @@ func UsersSearch(db sqlutil.Queryable, query string, limit, offset int) ([]*User defer rows.Close() return scanUsers(rows) } + +func CommunityUsers(db sqlutil.Queryable, community *User, order string, limit, offset int) ([]*User, error) { + rows, e := db.Query(qCommunityMembers, community.Id, order, limit, offset) + if e != nil { + if e == sql.ErrNoRows { + return []*User{}, nil + } + return nil, errors.New500Error(e.Error()) + } + defer rows.Close() + if us, e := scanUsers(rows); e != nil { + return nil, errors.New500Error(e.Error()) + } else { + return us, nil + } +} + +func UserCommunities(db sqlutil.Queryable, user *User, order string, limit, offset int) ([]*User, error) { + rows, e := db.Query(qUserCommunities, user.Id, order, limit, offset) + if e != nil { + if e == sql.ErrNoRows { + return []*User{}, nil + } + return nil, errors.New500Error(e.Error()) + } + defer rows.Close() + if us, e := scanUsers(rows); e != nil { + return nil, errors.New500Error(e.Error()) + } else { + return us, nil + } +} diff --git a/vendor/github.com/datatogether/identity/user/validators.go b/vendor/github.com/datatogether/identity/user/validators.go index 2142f2c..de8147e 100644 --- a/vendor/github.com/datatogether/identity/user/validators.go +++ b/vendor/github.com/datatogether/identity/user/validators.go @@ -13,7 +13,7 @@ import ( var ( // alphanumeric must start with a letter and contian only letters & numbers - alphaNumericRegex = regexp.MustCompile(`^[a-z0-9_-]{2,35}$`) + alphaNumericRegex = regexp.MustCompile(`^[A-Za-z0-9_-]{2,35}$`) titleRegex = regexp.MustCompile(`^[\sa-z0-9_-]{1,200}$`) // yes, this is just ripped from the internet somewhere. Yes it should be improved. TODO - validate emails the right way emailRegex = regexp.MustCompile(`(?i)[A-Z0-9!#$%&'*+/=?^_{|}~-]+(?:\.[A-Z0-9!#$%&'*+/=?^_{|}~-]+)*@(?:[A-Z0-9](?:[A-Z0-9-]*[A-Z0-9])?\.)+[A-Z0-9](?:[A-Z0-9-]*[A-Z0-9])?`) diff --git a/vendor/github.com/datatogether/sql_datastore/cmd.go b/vendor/github.com/datatogether/sql_datastore/cmd.go index a3bf7fb..76d40e6 100644 --- a/vendor/github.com/datatogether/sql_datastore/cmd.go +++ b/vendor/github.com/datatogether/sql_datastore/cmd.go @@ -2,11 +2,12 @@ package sql_datastore // Cmd represents a set of standardized SQL queries these abstractions // define a common set of commands that a model can provide to sql_datastore -// for execution +// for execution. type Cmd int const ( - // Unknown as default, errored state + // Unknown as default, errored state. CmdUnknown should never + // be intentionally passed to... anything. CmdUnknown Cmd = iota // starting with DDL statements: // CREATE TABLE query diff --git a/vendor/github.com/datatogether/sql_datastore/model.go b/vendor/github.com/datatogether/sql_datastore/model.go index f0a95f5..6d3e228 100644 --- a/vendor/github.com/datatogether/sql_datastore/model.go +++ b/vendor/github.com/datatogether/sql_datastore/model.go @@ -2,6 +2,7 @@ package sql_datastore import ( "github.com/datatogether/sqlutil" + datastore "github.com/ipfs/go-datastore" ) // Model is the interface that must be implemented to work @@ -12,23 +13,27 @@ type Model interface { // DatastoreType must return the "type" of object, which is a consistent // name for the object being stored. DatastoreType works in conjunction // with GetId to construct the key for storage. - // Since SQL doesn't support the "pathing" aspect of keys, any path - // values are ignored DatastoreType() string - // GetId should return the cannonical ID for the object. + // GetId should return the standalone cannonical ID for the object. GetId() string - // While not explicitly required by this package, most implementations - // will want to have a "Key" method that combines DatastoreType() and GetId() + // Key is a methoda that traditionally combines DatastoreType() and GetId() // to form a key that can be provided to Get & Has commands // eg: // func (m) Key() datastore.Key { // return datastore.NewKey(fmt.Sprintf("%s:%s", m.DatastoreType(), m.GetId())) // } + // in examples of "submodels" of another model it makes sense to leverage the + // POSIX structure of keys. for example: + // func (m) Key() datastore.Key { + // return datastore.NewKey(fmt.Sprintf("%s:%s/%s", m.DatastoreType(), m.ParentId(), m.GetId())) + // } + Key() datastore.Key // NewSQLModel must allocate & return a new instance of the - // model with id set such that GetId returns the passed-in id string - NewSQLModel(id string) Model + // model with id set such that GetId returns the passed-in Key + // NewSQLModel will be passed keys for creation of new blank models + NewSQLModel(key datastore.Key) Model // SQLQuery gives the datastore the query to execute for a given command type // As an example, if CmdSelectOne is passed in, something like diff --git a/vendor/github.com/datatogether/sql_datastore/query_filters.go b/vendor/github.com/datatogether/sql_datastore/query_filters.go new file mode 100644 index 0000000..5c140d4 --- /dev/null +++ b/vendor/github.com/datatogether/sql_datastore/query_filters.go @@ -0,0 +1,27 @@ +package sql_datastore + +import ( + "fmt" + "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/query" +) + +// FilterTypeEq filters for a specific key Type (which should match a registerd model on the sql_datastore.Datastore) +// FilterTypeEq is a string that specifies the key type we're after +type FilterKeyTypeEq string + +// Key return s FilterKeyTypeEq formatted as a datastore.Key +func (f FilterKeyTypeEq) Key() datastore.Key { + return datastore.NewKey(fmt.Sprintf("/%s:", f.String())) +} + +// Satisfy the Stringer interface +func (f FilterKeyTypeEq) String() string { + return string(f) +} + +// Filter satisfies the query.Filter interface +// TODO - make this work properly for the sake of other datastores +func (f FilterKeyTypeEq) Filter(e query.Entry) bool { + return true +} diff --git a/vendor/github.com/datatogether/sql_datastore/query_orders.go b/vendor/github.com/datatogether/sql_datastore/query_orders.go new file mode 100644 index 0000000..b32f6aa --- /dev/null +++ b/vendor/github.com/datatogether/sql_datastore/query_orders.go @@ -0,0 +1,34 @@ +package sql_datastore + +import ( + "fmt" + "github.com/ipfs/go-datastore/query" +) + +// Order a query by a field +type OrderBy string + +// String value, used to inject the field name istself as a SQL query param +func (o OrderBy) String() string { + return string(o) +} + +// satisfy datastore.Order interface, this is a no-op b/c sql sorting +// will happen at query-time +// TODO - In the future this should be generalized to facilitate supplying +// sql_datastore.OrderBy orders to other datastores, providing parity +func (o OrderBy) Sort([]query.Entry) {} + +// Order a query by a field, descending +type OrderByDesc string + +// String value, used to inject the field name istself as a SQL query param +func (o OrderByDesc) String() string { + return fmt.Sprintf("%s DESC", o) +} + +// satisfy datastore.Order interface, this is a no-op b/c sql sorting +// will happen at query-time +// TODO - In the future this should be generalized to facilitate supplying +// sql_datastore.OrderBy orders to other datastores, providing parity +func (o OrderByDesc) Sort([]query.Entry) {} diff --git a/vendor/github.com/datatogether/sql_datastore/readme.md b/vendor/github.com/datatogether/sql_datastore/readme.md index e014b1d..9e14d87 100644 --- a/vendor/github.com/datatogether/sql_datastore/readme.md +++ b/vendor/github.com/datatogether/sql_datastore/readme.md @@ -14,6 +14,18 @@ requiring implementers to provide a standard set of queries and parameters to glue everything together. Whenever the datastore interface is not expressive enough, one can always fall back to standard SQL work. +sql_datastore reconciles the key-value orientation of the datastore interface +with the tables/relational orientation of SQL databases through the concept of a +"Model". Model is a bit of an unfortunate name, as it implies this package is an +ORM, which isn't a design goal. + +Annnnnnnnyway, the important patterns of this approach are: + + 1. The Model interface defines how to get stuff into and out of SQL + 2. All Models that will be interacted with must be "Registered" to the store. + Registered Models map to a datastore.Key Type. + 3. All Get/Put/Delete/Has/Query to sql_datastore must map to a single Model + This implementation leads to a great deal of required boilerplate code to implement. In the future this package could be expanded to become syntax-aware, accepting a table name & schema definition for registered models. From here the @@ -33,6 +45,7 @@ Package Level Datastore. Be sure to call SetDB before using! ```go func Register(models ...Model) error ``` +Register a number of models to the DefaultStore #### func SetDB @@ -48,11 +61,12 @@ type Cmd int ``` Cmd represents a set of standardized SQL queries these abstractions define a -common set of commands that a model can provide to sql_datastore for execution +common set of commands that a model can provide to sql_datastore for execution. ```go const ( - // Unknown as default, errored state + // Unknown as default, errored state. CmdUnknown should never + // be intentionally passed to... anything. CmdUnknown Cmd = iota // starting with DDL statements: // CREATE TABLE query @@ -89,7 +103,7 @@ type Datastore struct { } ``` -Datastore +Datastore implements the ipfs datastore interface for SQL databases #### func NewDatastore @@ -152,6 +166,38 @@ func (ds *Datastore) Register(models ...Model) error Register one or more models that will be used by this datastore. Must be called before a model can be manipulated by the store +#### type FilterKeyTypeEq + +```go +type FilterKeyTypeEq string +``` + +FilterTypeEq filters for a specific key Type (which should match a registerd +model on the sql_datastore.Datastore) FilterTypeEq is a string that specifies +the key type we're after + +#### func (FilterKeyTypeEq) Filter + +```go +func (f FilterKeyTypeEq) Filter(e query.Entry) bool +``` +Filter satisfies the query.Filter interface TODO - make this work properly for +the sake of other datastores + +#### func (FilterKeyTypeEq) Key + +```go +func (f FilterKeyTypeEq) Key() datastore.Key +``` +Key return s FilterKeyTypeEq formatted as a datastore.Key + +#### func (FilterKeyTypeEq) String + +```go +func (f FilterKeyTypeEq) String() string +``` +Satisfy the Stringer interface + #### type Model ```go @@ -159,15 +205,27 @@ type Model interface { // DatastoreType must return the "type" of object, which is a consistent // name for the object being stored. DatastoreType works in conjunction // with GetId to construct the key for storage. - // Since SQL doesn't support the "pathing" aspect of keys, any path - // values are ignored DatastoreType() string - // GetId should return the cannonical ID for the object. + // GetId should return the standalone cannonical ID for the object. GetId() string + // Key is a methoda that traditionally combines DatastoreType() and GetId() + // to form a key that can be provided to Get & Has commands + // eg: + // func (m) Key() datastore.Key { + // return datastore.NewKey(fmt.Sprintf("%s:%s", m.DatastoreType(), m.GetId())) + // } + // in examples of "submodels" of another model it makes sense to leverage the + // POSIX structure of keys. for example: + // func (m) Key() datastore.Key { + // return datastore.NewKey(fmt.Sprintf("%s:%s/%s", m.DatastoreType(), m.ParentId(), m.GetId())) + // } + Key() datastore.Key + // NewSQLModel must allocate & return a new instance of the - // model with id set such that GetId returns the passed-in id string - NewSQLModel(id string) Model + // model with id set such that GetId returns the passed-in Key + // NewSQLModel will be passed keys for creation of new blank models + NewSQLModel(key datastore.Key) Model // SQLQuery gives the datastore the query to execute for a given command type // As an example, if CmdSelectOne is passed in, something like @@ -187,4 +245,52 @@ type Model interface { Model is the interface that must be implemented to work with sql_datastore. There are some fairly heavy constraints here. For a working example checkout: -https://github.com/datatogether/archive and have a look at primer.go +https://github.com/archivers-space/archive and have a look at primer.go + +#### type OrderBy + +```go +type OrderBy string +``` + +Order a query by a field + +#### func (OrderBy) Sort + +```go +func (o OrderBy) Sort([]query.Entry) +``` +satisfy datastore.Order interface, this is a no-op b/c sql sorting will happen +at query-time TODO - In the future this should be generalized to facilitate +supplying sql_datastore.OrderBy orders to other datastores, providing parity + +#### func (OrderBy) String + +```go +func (o OrderBy) String() string +``` +String value, used to inject the field name istself as a SQL query param + +#### type OrderByDesc + +```go +type OrderByDesc string +``` + +Order a query by a field, descending + +#### func (OrderByDesc) Sort + +```go +func (o OrderByDesc) Sort([]query.Entry) +``` +satisfy datastore.Order interface, this is a no-op b/c sql sorting will happen +at query-time TODO - In the future this should be generalized to facilitate +supplying sql_datastore.OrderBy orders to other datastores, providing parity + +#### func (OrderByDesc) String + +```go +func (o OrderByDesc) String() string +``` +String value, used to inject the field name istself as a SQL query param diff --git a/vendor/github.com/datatogether/sql_datastore/sql_datastore.go b/vendor/github.com/datatogether/sql_datastore/sql_datastore.go index 4b1a7f6..b7ffda2 100644 --- a/vendor/github.com/datatogether/sql_datastore/sql_datastore.go +++ b/vendor/github.com/datatogether/sql_datastore/sql_datastore.go @@ -12,6 +12,17 @@ // together. Whenever the datastore interface is not expressive enough, // one can always fall back to standard SQL work. // +// sql_datastore reconciles the key-value orientation of the datastore +// interface with the tables/relational orientation of SQL databases +// through the concept of a "Model". Model is a bit of an unfortunate name, +// as it implies this package is an ORM, which isn't a design goal. +// +// Annnnnnnnyway, the important patterns of this approach are: +// 1. The Model interface defines how to get stuff into and out of SQL +// 2. All Models that will be interacted with must be "Registered" to the store. +// Registered Models map to a datastore.Key Type. +// 3. All Get/Put/Delete/Has/Query to sql_datastore must map to a single Model +// // This implementation leads to a great deal of required boilerplate code // to implement. In the future this package could be expanded to become // syntax-aware, accepting a table name & schema definition for registered @@ -37,11 +48,12 @@ func SetDB(db *sql.DB) { DefaultStore.DB = db } +// Register a number of models to the DefaultStore func Register(models ...Model) error { return DefaultStore.Register(models...) } -// Datastore +// Datastore implements the ipfs datastore interface for SQL databases type Datastore struct { // DB is the underlying DB handler // it should be safe for use outside of the @@ -100,7 +112,7 @@ func (ds Datastore) Get(key datastore.Key) (value interface{}, err error) { return nil, err } - v := m.NewSQLModel(key.Name()) + v := m.NewSQLModel(key) if err := v.UnmarshalSQL(row); err != nil { return nil, err } @@ -140,46 +152,56 @@ func (ds Datastore) Delete(key datastore.Key) error { // Currently it's required that the passed-in prefix be equal to DatastoreType() // which query will use to determine what model to ask for a ListCmd func (ds Datastore) Query(q query.Query) (query.Results, error) { - // TODO - support query Filters - if len(q.Filters) > 0 { - return nil, fmt.Errorf("sql datastore queries do not support filters") - } - // TODO - support query Orders - if len(q.Orders) > 0 { - return nil, fmt.Errorf("sql datastore queries do not support ordering") - } + var rows *sql.Rows + // TODO - support KeysOnly if q.KeysOnly { - return nil, fmt.Errorf("sql datastore doesn't support keysonly ordering") + return nil, fmt.Errorf("sql datastore doesn't support keysonly querying") } - // TODO - ugh this so bad - m, err := ds.modelForKey(datastore.NewKey(fmt.Sprintf("/%s:", q.Prefix))) + // determine what type of model we're querying for + m, err := ds.modelForQuery(q) if err != nil { return nil, err } + // here we attach query information to a new model + // TODO - currently this is basing off of the prefix, might need + // to do smarter things in relation to filters? + m = m.NewSQLModel(datastore.NewKey(q.Prefix)) + // This is totally janky, but will work for now. It's expected that // the returned CmdList will have at least 2 bindvars: - // $1 : LIMIT - // $2 : OFFSET + // $1 : LIMIT value + // $2 : OFFSET value + // if compatible Orders are provided to the query, the order string + // will be provided as a third bindvar: + // $3 : ORDERBY value // From there it can provide zero or more additional bindvars to // organize the query, which should be returned by the SQLParams method // TODO - this seems to hint at a need for some sort of Controller-like // pattern in userland. Have a think. - rows, err := ds.query(m, CmdList, q.Limit, q.Offset) - if err != nil { - return nil, err + os := orderString(q) + if os != "" { + rows, err = ds.query(m, CmdList, q.Limit, q.Offset, os) + if err != nil { + return nil, err + } + } else { + rows, err = ds.query(m, CmdList, q.Limit, q.Offset) + if err != nil { + return nil, err + } } - // TODO - should this be q.Limit or query.NormalBufferSize + // TODO - should this be q.Limit or query.NormalBufferSize? reschan := make(chan query.Result, q.Limit) go func() { defer close(reschan) for rows.Next() { - model := m.NewSQLModel("") + model := m.NewSQLModel(datastore.NewKey(q.Prefix)) if err := model.UnmarshalSQL(rows); err != nil { reschan <- query.Result{ Error: err, @@ -199,21 +221,71 @@ func (ds Datastore) Query(q query.Query) (query.Results, error) { return query.ResultsWithChan(q, reschan), nil } +// modelForQuery determines what type of model this query should return +func (ds Datastore) modelForQuery(q query.Query) (Model, error) { + for _, f := range q.Filters { + switch t := f.(type) { + case FilterKeyTypeEq: + return ds.modelForKey(t.Key()) + } + } + return ds.modelForKey(datastore.NewKey(fmt.Sprintf("/%s:", q.Prefix))) +} + +// orderString generates orders from any OrderBy or OrderByDesc values +func orderString(q query.Query) string { + if len(q.Orders) == 0 { + return "" + } + orders := []string{} + for _, o := range q.Orders { + // TODO - should this cross-correct by casting based on + // the outer order? + switch ot := o.(type) { + case query.OrderByValue: + switch obv := ot.TypedOrder.(type) { + case OrderBy: + orders = append(orders, obv.String()) + case OrderByDesc: + orders = append(orders, obv.String()) + } + case query.OrderByValueDescending: + switch obv := ot.TypedOrder.(type) { + case OrderBy: + orders = append(orders, obv.String()) + case OrderByDesc: + orders = append(orders, obv.String()) + } + } + } + os := "" + for i, o := range orders { + if i == 0 { + os += o + } else { + os += fmt.Sprintf(", %s", o) + } + } + return os +} + // Batch commands are currently not supported func (ds *Datastore) Batch() (datastore.Batch, error) { return nil, datastore.ErrBatchUnsupported } +// for a given key, determine what kind of Model we're looking for func (ds Datastore) modelForKey(key datastore.Key) (Model, error) { for _, m := range ds.models { if m.DatastoreType() == key.Type() { // return a model with "ID" set to the key param - return m.NewSQLModel(key.Name()), nil + return m.NewSQLModel(key), nil } } return nil, fmt.Errorf("no usable model found for key, did you call register on the model?: %s", key.String()) } +// does this datastore func (ds Datastore) hasModel(m Model) (exists bool, err error) { row, err := ds.queryRow(m, CmdExistsOne) if err != nil { @@ -223,6 +295,7 @@ func (ds Datastore) hasModel(m Model) (exists bool, err error) { return } +// execute a Cmd against a given model func (ds Datastore) exec(m Model, t Cmd) error { if ds.DB == nil { return fmt.Errorf("datastore has no DB") @@ -235,6 +308,7 @@ func (ds Datastore) exec(m Model, t Cmd) error { return err } +// query for a single row, given a type of command and model func (ds Datastore) queryRow(m Model, t Cmd) (*sql.Row, error) { if ds.DB == nil { return nil, fmt.Errorf("datastore has no DB") @@ -246,6 +320,8 @@ func (ds Datastore) queryRow(m Model, t Cmd) (*sql.Row, error) { return ds.DB.QueryRow(query, params...), nil } +// run a query against the db for a given command and model, with optionally prebound +// arguments derived from the query func (ds Datastore) query(m Model, t Cmd, prebind ...interface{}) (*sql.Rows, error) { if ds.DB == nil { return nil, fmt.Errorf("datastore has no DB") @@ -254,9 +330,11 @@ func (ds Datastore) query(m Model, t Cmd, prebind ...interface{}) (*sql.Rows, er if err != nil { return nil, err } + return ds.DB.Query(query, append(prebind, params...)...) } +// prepare a query, grabbing the command sql & params from the model func (ds Datastore) prepQuery(m Model, t Cmd) (string, []interface{}, error) { query := m.SQLQuery(t) if query == "" { diff --git a/vendor/github.com/datatogether/sqlutil/readme.md b/vendor/github.com/datatogether/sqlutil/readme.md new file mode 100644 index 0000000..9cef788 --- /dev/null +++ b/vendor/github.com/datatogether/sqlutil/readme.md @@ -0,0 +1,201 @@ +# sqlutil +-- + import "github.com/datatogether/sqlutil" + +utils for working with dotsql structs + +abstractions for working with postgres databases + +## Usage + +#### func ConnectToDb + +```go +func ConnectToDb(driverName, url string, db *sql.DB) error +``` +Uniform Database connector + +#### func EnsureSeedData + +```go +func EnsureSeedData(db *sql.DB, schemaFilepath, dataFilepath string, tables ...string) (err error) +``` +EnsureSeedData runs "EnsureTables", and then injects seed data for any +newly-created tables + +#### func EnsureTables + +```go +func EnsureTables(db *sql.DB, schemaFilepath string, tables ...string) ([]string, error) +``` +EnsureTables checks for table existence, creating them from the schema file if +not, returning a slice of table names that were created + +#### func SetupConnection + +```go +func SetupConnection(driverName, connString string) (db *sql.DB, err error) +``` +Sets up a connection with a given postgres db connection string + +#### type DataCommands + +```go +type DataCommands struct { +} +``` + +SchemaFile is an sql file that defines a database schema + +#### func LoadDataCommands + +```go +func LoadDataCommands(sqlFilePath string) (*DataCommands, error) +``` +LoadDataCommands takes a filepath to a sql file with create & drop table +commands and returns a DataCommands + +#### func LoadDataString + +```go +func LoadDataString(sql string) (*DataCommands, error) +``` + +#### func (*DataCommands) Commands + +```go +func (d *DataCommands) Commands() []string +``` + +#### func (*DataCommands) DeleteAll + +```go +func (d *DataCommands) DeleteAll(db Execable) error +``` +DropAll executes the command named "drop-all" from the sql file this should be a +command in the form: DROP TABLE IF EXISTS foo, bar, baz ... + +#### func (*DataCommands) Reset + +```go +func (d *DataCommands) Reset(db Execable, tables ...string) error +``` + +#### type Execable + +```go +type Execable interface { + Queryable + Exec(query string, args ...interface{}) (sql.Result, error) +} +``` + +Execable ugrades a read-only interface to be able to execute sql DDL statements + +#### type Queryable + +```go +type Queryable interface { + Query(query string, args ...interface{}) (*sql.Rows, error) + QueryRow(query string, args ...interface{}) *sql.Row +} +``` + +Querable unifies both *sql.DB & *sql.Tx for querying purposes + +#### type Scannable + +```go +type Scannable interface { + Scan(...interface{}) error +} +``` + +Scannable unifies both *sql.Row & *sql.Rows, functions can accept Scannable & +work with both + +#### type SchemaCommands + +```go +type SchemaCommands struct { +} +``` + +SchemaCommands is an sql file that defines a database schema + +#### func LoadSchemaCommands + +```go +func LoadSchemaCommands(sqlFilePath string) (*SchemaCommands, error) +``` +LoadSchemaCommands takes a filepath to a sql file with create & drop table +commands and returns a SchemaCommands + +#### func LoadSchemaString + +```go +func LoadSchemaString(sql string) (*SchemaCommands, error) +``` + +#### func (*SchemaCommands) Create + +```go +func (s *SchemaCommands) Create(db Execable, tables ...string) ([]string, error) +``` +Create tables if they don't already exist + +#### func (*SchemaCommands) DropAll + +```go +func (s *SchemaCommands) DropAll(db Execable) error +``` +DropAll executes the command named "drop-all" from the sql file this should be a +command in the form: DROP TABLE IF EXISTS foo, bar, baz ... + +#### func (*SchemaCommands) DropAllCreate + +```go +func (s *SchemaCommands) DropAllCreate(db Execable, tables ...string) error +``` + +#### type TestSuite + +```go +type TestSuite struct { + DB *sql.DB + Schema *SchemaCommands + Data *DataCommands + Cascade []string +} +``` + + +#### func InitTestSuite + +```go +func InitTestSuite(o *TestSuiteOpts) (*TestSuite, error) +``` + +#### type TestSuiteOpts + +```go +type TestSuiteOpts struct { + DriverName string + ConnString string + SchemaPath string + SchemaSqlString string + DataPath string + DataSqlString string + Cascade []string +} +``` + + +#### type Transactable + +```go +type Transactable interface { + Execable + Begin() (*sql.Tx, error) +} +``` diff --git a/vendor/github.com/datatogether/sqlutil/sqlutil.go b/vendor/github.com/datatogether/sqlutil/sqlutil.go index 53485be..366dc05 100644 --- a/vendor/github.com/datatogether/sqlutil/sqlutil.go +++ b/vendor/github.com/datatogether/sqlutil/sqlutil.go @@ -59,7 +59,35 @@ func SetupConnection(driverName, connString string) (db *sql.DB, err error) { return } -// EnsureTables checks for table existence, creating them from the schema file if not. +// EnsureSeedData runs "EnsureTables", and then injects seed data for any newly-created tables +func EnsureSeedData(db *sql.DB, schemaFilepath, dataFilepath string, tables ...string) (created []string, err error) { + // test query to check for database schema existence + sc, err := LoadSchemaCommands(schemaFilepath) + if err != nil { + return nil, err + } + + created, err = sc.Create(db, tables...) + if err != nil { + return created, err + } + + if len(created) > 0 { + dc, err := LoadDataCommands(dataFilepath) + if err != nil { + return created, err + } + + if err := dc.Reset(db, created...); err != nil { + return created, err + } + } + + return created, nil +} + +// EnsureTables checks for table existence, creating them from the schema file if not, +// returning a slice of table names that were created func EnsureTables(db *sql.DB, schemaFilepath string, tables ...string) ([]string, error) { sc, err := LoadSchemaCommands(schemaFilepath) if err != nil { @@ -67,19 +95,3 @@ func EnsureTables(db *sql.DB, schemaFilepath string, tables ...string) ([]string } return sc.Create(db, tables...) } - -// drops test data tables & re-inserts base data from sql/test_data.sql, based on -// passed in table names -// func InsertTestData(db *sql.DB, tables ...string) error { -// schema, err := dotsql.LoadFromFile() -// if err != nil { -// return err -// } -// for _, t := range tables { -// if _, err := schema.Exec(db, fmt.Sprintf("insert-%s", t)); err != nil { -// err = fmt.Errorf("error insert-%s: %s", t, err.Error()) -// return err -// } -// } -// return nil -// } diff --git a/vendor/github.com/ipfs/go-datastore/.travis.yml b/vendor/github.com/ipfs/go-datastore/.travis.yml index 4a4da4d..b4cc7ee 100644 --- a/vendor/github.com/ipfs/go-datastore/.travis.yml +++ b/vendor/github.com/ipfs/go-datastore/.travis.yml @@ -1,7 +1,7 @@ language: go go: - - 1.7.3 + - 1.8 - tip script: diff --git a/vendor/github.com/ipfs/go-datastore/basic_ds.go b/vendor/github.com/ipfs/go-datastore/basic_ds.go index aad2ada..f63d8af 100644 --- a/vendor/github.com/ipfs/go-datastore/basic_ds.go +++ b/vendor/github.com/ipfs/go-datastore/basic_ds.go @@ -169,17 +169,56 @@ func (d *LogDatastore) Delete(key Key) (err error) { // Query implements Datastore.Query func (d *LogDatastore) Query(q dsq.Query) (dsq.Results, error) { log.Printf("%s: Query\n", d.Name) + log.Printf("%s: q.Prefix: %s\n", d.Name, q.Prefix) + log.Printf("%s: q.KeysOnly: %s\n", d.Name, q.KeysOnly) + log.Printf("%s: q.Filters: %d\n", d.Name, len(q.Filters)) + log.Printf("%s: q.Orders: %d\n", d.Name, len(q.Orders)) + log.Printf("%s: q.Offset: %d\n", d.Name, q.Offset) + return d.child.Query(q) } +// LogBatch logs all accesses through the batch. +type LogBatch struct { + Name string + child Batch +} + func (d *LogDatastore) Batch() (Batch, error) { log.Printf("%s: Batch\n", d.Name) if bds, ok := d.child.(Batching); ok { - return bds.Batch() + b, err := bds.Batch() + + if err != nil { + return nil, err + } + return &LogBatch{ + Name: d.Name, + child: b, + }, nil } return nil, ErrBatchUnsupported } +// Put implements Batch.Put +func (d *LogBatch) Put(key Key, value interface{}) (err error) { + log.Printf("%s: BatchPut %s\n", d.Name, key) + // log.Printf("%s: Put %s ```%s```", d.Name, key, value) + return d.child.Put(key, value) +} + +// Delete implements Batch.Delete +func (d *LogBatch) Delete(key Key) (err error) { + log.Printf("%s: BatchDelete %s\n", d.Name, key) + return d.child.Delete(key) +} + +// Commit implements Batch.Commit +func (d *LogBatch) Commit() (err error) { + log.Printf("%s: BatchCommit\n", d.Name) + return d.child.Commit() +} + func (d *LogDatastore) Close() error { log.Printf("%s: Close\n", d.Name) if cds, ok := d.child.(io.Closer); ok { diff --git a/vendor/github.com/ipfs/go-datastore/key.go b/vendor/github.com/ipfs/go-datastore/key.go index cf8ce10..8c52874 100644 --- a/vendor/github.com/ipfs/go-datastore/key.go +++ b/vendor/github.com/ipfs/go-datastore/key.go @@ -1,6 +1,7 @@ package datastore import ( + "errors" "path" "strings" @@ -282,3 +283,25 @@ func EntryKeys(e []dsq.Entry) []Key { } return ks } + +func (k Key) MarshalJSON() ([]byte, error) { + return []byte(`"` + k.String() + `"`), nil +} + +func (k *Key) UnmarshalJSON(data []byte) error { + // TODO - normally we'd do this: + // var s string + // if err := json.Unmarshal(data, &s); err == nil { + // *k = NewKey(s) + // return nil + // } + + // but that would bring the added weight of full json + // package just for a string coercion here, so the following + // may be acceptable. + if len(data) < 2 { + return errors.New("too short to unmarshal json string") + } + *k = NewKey(string(data[1 : len(data)-1])) + return nil +} diff --git a/vendor/github.com/ipfs/go-datastore/package.json b/vendor/github.com/ipfs/go-datastore/package.json index cac34f2..36257d5 100644 --- a/vendor/github.com/ipfs/go-datastore/package.json +++ b/vendor/github.com/ipfs/go-datastore/package.json @@ -25,6 +25,6 @@ "license": "MIT", "name": "go-datastore", "releaseCmd": "git commit -a -m \"gx publish $VERSION\"", - "version": "1.2.0" + "version": "1.2.2" } diff --git a/vendor/github.com/multiformats/go-multihash/.gitignore b/vendor/github.com/multiformats/go-multihash/.gitignore new file mode 100644 index 0000000..1d74e21 --- /dev/null +++ b/vendor/github.com/multiformats/go-multihash/.gitignore @@ -0,0 +1 @@ +.vscode/ diff --git a/vendor/github.com/multiformats/go-multihash/.travis.yml b/vendor/github.com/multiformats/go-multihash/.travis.yml index 12c9db2..6187945 100644 --- a/vendor/github.com/multiformats/go-multihash/.travis.yml +++ b/vendor/github.com/multiformats/go-multihash/.travis.yml @@ -1,10 +1,11 @@ +sudo: false os: - linux language: go go: - - 1.7 + - 1.8 install: true diff --git a/vendor/github.com/multiformats/go-multihash/README.md b/vendor/github.com/multiformats/go-multihash/README.md index c700c82..318974a 100644 --- a/vendor/github.com/multiformats/go-multihash/README.md +++ b/vendor/github.com/multiformats/go-multihash/README.md @@ -4,6 +4,7 @@ [![](https://img.shields.io/badge/project-multiformats-blue.svg?style=flat-square)](https://github.com/multiformats/multiformats) [![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs) [![](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) +[![GoDoc](https://godoc.org/github.com/multiformats/go-multihash?status.svg)](https://godoc.org/github.com/multiformats/go-multihash) [![Travis CI](https://img.shields.io/travis/multiformats/go-multihash.svg?style=flat-square&branch=master)](https://travis-ci.org/multiformats/go-multihash) [![codecov.io](https://img.shields.io/codecov/c/github/multiformats/go-multihash.svg?style=flat-square&branch=master)](https://codecov.io/github/multiformats/go-multihash?branch=master) @@ -19,42 +20,76 @@ ## Install +`go-multihash` is a standard Go module which can be installed with: + ```sh go get github.com/multiformats/go-multihash ``` +Note that `go-multihash` is packaged with Gx, so it is recommended to use Gx to install and use it (see Usage section). + ## Usage +### Using Gx and Gx-go + +This module is packaged with [Gx](https://github.com/whyrusleeping/gx). In order to use it in your own project it is recommended that you: + +```sh +go get -u github.com/whyrusleeping/gx +go get -u github.com/whyrusleeping/gx-go +cd +gx init +gx import github.com/multiformats/go-multihash +gx install --global +gx-go --rewrite +``` + +Please check [Gx](https://github.com/whyrusleeping/gx) and [Gx-go](https://github.com/whyrusleeping/gx-go) documentation for more information. + +### Example + +This example takes a standard hex-encoded data and uses `EncodeName` to calculate the SHA1 multihash value for the buffer. + +The resulting hex-encoded data corresponds to: ``, which could be re-parsed +with `Multihash.FromHexString()`. + + ```go package main import ( - "encoding/hex" - "fmt" - "github.com/multiformats/go-multihash" + "encoding/hex" + "fmt" + + "github.com/multiformats/go-multihash" ) func main() { - // ignores errors for simplicity. - // don't do that at home. - - buf, _ := hex.DecodeString("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33") - mhbuf, _ := multihash.EncodeName(buf, "sha1"); - mhhex := hex.EncodeToString(mhbuf) - fmt.Printf("hex: %v\n", mhhex); - - o, _ := multihash.Decode(mhbuf); - mhhex = hex.EncodeToString(o.Digest); - fmt.Printf("obj: %v 0x%x %d %s\n", o.Name, o.Code, o.Length, mhhex); + // ignores errors for simplicity. + // don't do that at home. + // Decode a SHA1 hash to a binary buffer + buf, _ := hex.DecodeString("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33") + + // Create a new multihash with it. + mHashBuf, _ := multihash.EncodeName(buf, "sha1") + // Print the multihash as hex string + fmt.Printf("hex: %s\n", hex.EncodeToString(mHashBuf)) + + // Parse the binary multihash to a DecodedMultihash + mHash, _ := multihash.Decode(mHashBuf) + // Convert the sha1 value to hex string + sha1hex := hex.EncodeToString(mHash.Digest) + // Print all the information in the multihash + fmt.Printf("obj: %v 0x%x %d %s\n", mHash.Name, mHash.Code, mHash.Length, sha1hex) } ``` -Run [test/foo.go](test/foo.go) +To run, copy to [example/foo.go](example/foo.go) and: ``` -> cd test/ +> cd example/ > go build -> ./test +> ./example hex: 11140beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33 obj: sha1 0x11 20 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33 ``` diff --git a/vendor/github.com/multiformats/go-multihash/multihash.go b/vendor/github.com/multiformats/go-multihash/multihash.go index 8a9e47d..1b6c0ef 100644 --- a/vendor/github.com/multiformats/go-multihash/multihash.go +++ b/vendor/github.com/multiformats/go-multihash/multihash.go @@ -1,3 +1,6 @@ +// Package multihash is the Go implementation of +// https://github.com/multiformats/multihash, or self-describing +// hashes. package multihash import ( @@ -33,10 +36,22 @@ func (e ErrInconsistentLen) Error() string { // constants const ( - SHA1 = 0x11 - SHA2_256 = 0x12 - SHA2_512 = 0x13 - SHA3 = 0x14 + ID = 0x00 + SHA1 = 0x11 + SHA2_256 = 0x12 + SHA2_512 = 0x13 + SHA3_224 = 0x17 + SHA3_256 = 0x16 + SHA3_384 = 0x15 + SHA3_512 = 0x14 + SHA3 = SHA3_512 + KECCAK_224 = 0x1A + KECCAK_256 = 0x1B + KECCAK_384 = 0x1C + KECCAK_512 = 0x1D + + SHAKE_128 = 0x18 + SHAKE_256 = 0x19 BLAKE2B_MIN = 0xb201 BLAKE2B_MAX = 0xb240 @@ -44,6 +59,8 @@ const ( BLAKE2S_MAX = 0xb260 DBL_SHA2_256 = 0x56 + + MURMUR3 = 0x22 ) func init() { @@ -68,29 +85,63 @@ func init() { // Names maps the name of a hash to the code var Names = map[string]uint64{ + "id": ID, "sha1": SHA1, "sha2-256": SHA2_256, "sha2-512": SHA2_512, - "sha3": SHA3, + "sha3": SHA3_512, + "sha3-224": SHA3_224, + "sha3-256": SHA3_256, + "sha3-384": SHA3_384, + "sha3-512": SHA3_512, "dbl-sha2-256": DBL_SHA2_256, + "murmur3": MURMUR3, + "keccak-224": KECCAK_224, + "keccak-256": KECCAK_256, + "keccak-384": KECCAK_384, + "keccak-512": KECCAK_512, + "shake-128": SHAKE_128, + "shake-256": SHAKE_256, } // Codes maps a hash code to it's name var Codes = map[uint64]string{ + ID: "id", SHA1: "sha1", SHA2_256: "sha2-256", SHA2_512: "sha2-512", - SHA3: "sha3", + SHA3_224: "sha3-224", + SHA3_256: "sha3-256", + SHA3_384: "sha3-384", + SHA3_512: "sha3-512", DBL_SHA2_256: "dbl-sha2-256", + MURMUR3: "murmur3", + KECCAK_224: "keccak-224", + KECCAK_256: "keccak-256", + KECCAK_384: "keccak-384", + KECCAK_512: "keccak-512", + SHAKE_128: "shake-128", + SHAKE_256: "shake-256", } // DefaultLengths maps a hash code to it's default length var DefaultLengths = map[uint64]int{ + ID: -1, SHA1: 20, SHA2_256: 32, SHA2_512: 64, - SHA3: 64, + SHA3_224: 28, + SHA3_256: 32, + SHA3_384: 48, + SHA3_512: 64, DBL_SHA2_256: 32, + KECCAK_224: 28, + KECCAK_256: 32, + MURMUR3: 4, + KECCAK_384: 48, + KECCAK_512: 64, + SHAKE_128: 32, + SHAKE_256: 64, } func uvarint(buf []byte) (uint64, []byte, error) { @@ -105,23 +156,31 @@ func uvarint(buf []byte) (uint64, []byte, error) { } } +// DecodedMultihash represents a parsed multihash and allows +// easy access to the different parts of a multihash. type DecodedMultihash struct { Code uint64 Name string - Length int // Length is just int as it is type of len() opearator - Digest []byte + Length int // Length is just int as it is type of len() opearator + Digest []byte // Digest holds the raw multihash bytes } +// Multihash is byte slice with the following form: +// . +// See the spec for more information. type Multihash []byte +// HexString returns the hex-encoded representation of a multihash. func (m *Multihash) HexString() string { return hex.EncodeToString([]byte(*m)) } +// String is an alias to HexString(). func (m *Multihash) String() string { return m.HexString() } +// FromHexString parses a hex-encoded multihash. func FromHexString(s string) (Multihash, error) { b, err := hex.DecodeString(s) if err != nil { @@ -131,10 +190,12 @@ func FromHexString(s string) (Multihash, error) { return Cast(b) } +// B58String returns the B58-encoded representation of a multihash. func (m Multihash) B58String() string { return b58.Encode([]byte(m)) } +// FromB58String parses a B58-encoded multihash. func FromB58String(s string) (m Multihash, err error) { // panic handler, in case we try accessing bytes incorrectly. defer func() { @@ -153,6 +214,8 @@ func FromB58String(s string) (m Multihash, err error) { return Cast(b) } +// Cast casts a buffer onto a multihash, and returns an error +// if it does not work. func Cast(buf []byte) (Multihash, error) { dm, err := Decode(buf) if err != nil { @@ -166,7 +229,7 @@ func Cast(buf []byte) (Multihash, error) { return Multihash(buf), nil } -// Decode a hash from the given Multihash. +// Decode parses multihash bytes into a DecodedMultihash. func Decode(buf []byte) (*DecodedMultihash, error) { if len(buf) < 3 { @@ -221,6 +284,8 @@ func Encode(buf []byte, code uint64) ([]byte, error) { return append(start[:n], buf...), nil } +// EncodeName is like Encode() but providing a string name +// instead of a numeric code. See Names for allowed values. func EncodeName(buf []byte, name string) ([]byte, error) { return Encode(buf, Names[name]) } diff --git a/vendor/github.com/multiformats/go-multihash/package.json b/vendor/github.com/multiformats/go-multihash/package.json index 63099ce..77e0668 100644 --- a/vendor/github.com/multiformats/go-multihash/package.json +++ b/vendor/github.com/multiformats/go-multihash/package.json @@ -18,12 +18,25 @@ "hash": "QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf", "name": "go-base58", "version": "0.0.0" + }, + { + "author": "whyrusleeping", + "hash": "QmQPWTeQJnJE7MYu6dJTiNTQRNuqBr41dis6UgY6Uekmgd", + "name": "keccakpg", + "version": "0.0.0" + }, + { + "author": "whyrusleeping", + "hash": "QmfJHywXQu98UeZtGJBQrPAR6AtmDjjbe3qjTo9piXHPnx", + "name": "murmur3", + "version": "0.0.0" } ], "gxVersion": "0.9.0", "language": "go", "license": "MIT", "name": "go-multihash", - "version": "1.0.2" + "releaseCmd": "git commit -a -m \"gx release $VERSION\"", + "version": "1.0.5" } diff --git a/vendor/github.com/multiformats/go-multihash/sum.go b/vendor/github.com/multiformats/go-multihash/sum.go index ce937c1..af194a0 100644 --- a/vendor/github.com/multiformats/go-multihash/sum.go +++ b/vendor/github.com/multiformats/go-multihash/sum.go @@ -7,13 +7,19 @@ import ( "errors" "fmt" + "github.com/spaolacci/murmur3" blake2b "golang.org/x/crypto/blake2b" blake2s "golang.org/x/crypto/blake2s" sha3 "golang.org/x/crypto/sha3" + keccak "leb.io/hashland/keccakpg" ) +// ErrSumNotSupported is returned when the Sum function code is not implemented var ErrSumNotSupported = errors.New("Function not implemented. Complain to lib maintainer.") +// Sum obtains the cryptographic sum of a given buffer. The length parameter +// indicates the length of the resulting digest and passing a negative value +// use default length values for the selected hash function. func Sum(data []byte, code uint64, length int) (Multihash, error) { m := Multihash{} err := error(nil) @@ -57,16 +63,38 @@ func Sum(data []byte, code uint64, length int) (Multihash, error) { } default: switch code { + case ID: + d = sumID(data) case SHA1: d = sumSHA1(data) case SHA2_256: d = sumSHA256(data) case SHA2_512: d = sumSHA512(data) - case SHA3: - d, err = sumSHA3(data) + case KECCAK_224: + d = sumKeccak224(data) + case KECCAK_256: + d = sumKeccak256(data) + case KECCAK_384: + d = sumKeccak384(data) + case KECCAK_512: + d = sumKeccak512(data) + case SHA3_224: + d = sumSHA3_224(data) + case SHA3_256: + d = sumSHA3_256(data) + case SHA3_384: + d = sumSHA3_384(data) + case SHA3_512: + d = sumSHA3_512(data) case DBL_SHA2_256: d = sumSHA256(sumSHA256(data)) + case MURMUR3: + d, err = sumMURMUR3(data) + case SHAKE_128: + d = sumSHAKE128(data) + case SHAKE_256: + d = sumSHAKE256(data) default: return m, ErrSumNotSupported } @@ -74,7 +102,10 @@ func Sum(data []byte, code uint64, length int) (Multihash, error) { if err != nil { return m, err } - return Encode(d[0:length], code) + if length >= 0 { + d = d[:length] + } + return Encode(d, code) } func isBlake2s(code uint64) bool { @@ -84,6 +115,10 @@ func isBlake2b(code uint64) bool { return code >= BLAKE2B_MIN && code <= BLAKE2B_MAX } +func sumID(data []byte) []byte { + return data +} + func sumSHA1(data []byte) []byte { a := sha1.Sum(data) return a[0:20] @@ -99,6 +134,30 @@ func sumSHA512(data []byte) []byte { return a[0:64] } +func sumKeccak224(data []byte) []byte { + h := keccak.New224() + h.Write(data) + return h.Sum(nil) +} + +func sumKeccak256(data []byte) []byte { + h := keccak.New256() + h.Write(data) + return h.Sum(nil) +} + +func sumKeccak384(data []byte) []byte { + h := keccak.New384() + h.Write(data) + return h.Sum(nil) +} + +func sumKeccak512(data []byte) []byte { + h := keccak.New512() + h.Write(data) + return h.Sum(nil) +} + func sumSHA3(data []byte) ([]byte, error) { h := sha3.New512() if _, err := h.Write(data); err != nil { @@ -106,3 +165,45 @@ func sumSHA3(data []byte) ([]byte, error) { } return h.Sum(nil), nil } + +func sumSHA3_512(data []byte) []byte { + a := sha3.Sum512(data) + return a[:] +} + +func sumMURMUR3(data []byte) ([]byte, error) { + number := murmur3.Sum32(data) + bytes := make([]byte, 4) + for i := range bytes { + bytes[i] = byte(number & 0xff) + number >>= 8 + } + return bytes, nil +} + +func sumSHAKE128(data []byte) []byte { + bytes := make([]byte, 32) + sha3.ShakeSum128(bytes, data) + return bytes +} + +func sumSHAKE256(data []byte) []byte { + bytes := make([]byte, 64) + sha3.ShakeSum256(bytes, data) + return bytes +} + +func sumSHA3_384(data []byte) []byte { + a := sha3.Sum384(data) + return a[:] +} + +func sumSHA3_256(data []byte) []byte { + a := sha3.Sum256(data) + return a[:] +} + +func sumSHA3_224(data []byte) []byte { + a := sha3.Sum224(data) + return a[:] +} diff --git a/vendor/github.com/spaolacci/murmur3/.gitignore b/vendor/github.com/spaolacci/murmur3/.gitignore new file mode 100644 index 0000000..0026861 --- /dev/null +++ b/vendor/github.com/spaolacci/murmur3/.gitignore @@ -0,0 +1,22 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe diff --git a/vendor/github.com/spaolacci/murmur3/.travis.yml b/vendor/github.com/spaolacci/murmur3/.travis.yml new file mode 100644 index 0000000..9bfca9c --- /dev/null +++ b/vendor/github.com/spaolacci/murmur3/.travis.yml @@ -0,0 +1,7 @@ +language: go + +go: + - 1.x + - master + +script: go test diff --git a/vendor/github.com/spaolacci/murmur3/LICENSE b/vendor/github.com/spaolacci/murmur3/LICENSE new file mode 100644 index 0000000..2a46fd7 --- /dev/null +++ b/vendor/github.com/spaolacci/murmur3/LICENSE @@ -0,0 +1,24 @@ +Copyright 2013, Sébastien Paolacci. +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 the library 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 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/spaolacci/murmur3/README.md b/vendor/github.com/spaolacci/murmur3/README.md new file mode 100644 index 0000000..e463678 --- /dev/null +++ b/vendor/github.com/spaolacci/murmur3/README.md @@ -0,0 +1,86 @@ +murmur3 +======= + +[![Build Status](https://travis-ci.org/spaolacci/murmur3.svg?branch=master)](https://travis-ci.org/spaolacci/murmur3) + +Native Go implementation of Austin Appleby's third MurmurHash revision (aka +MurmurHash3). + +Reference algorithm has been slightly hacked as to support the streaming mode +required by Go's standard [Hash interface](http://golang.org/pkg/hash/#Hash). + + +Benchmarks +---------- + +Go tip as of 2014-06-12 (i.e almost go1.3), core i7 @ 3.4 Ghz. All runs +include hasher instantiation and sequence finalization. + +
+
+Benchmark32_1        500000000     7.69 ns/op      130.00 MB/s
+Benchmark32_2        200000000     8.83 ns/op      226.42 MB/s
+Benchmark32_4        500000000     7.99 ns/op      500.39 MB/s
+Benchmark32_8        200000000     9.47 ns/op      844.69 MB/s
+Benchmark32_16       100000000     12.1 ns/op     1321.61 MB/s
+Benchmark32_32       100000000     18.3 ns/op     1743.93 MB/s
+Benchmark32_64        50000000     30.9 ns/op     2071.64 MB/s
+Benchmark32_128       50000000     57.6 ns/op     2222.96 MB/s
+Benchmark32_256       20000000      116 ns/op     2188.60 MB/s
+Benchmark32_512       10000000      226 ns/op     2260.59 MB/s
+Benchmark32_1024       5000000      452 ns/op     2263.73 MB/s
+Benchmark32_2048       2000000      891 ns/op     2296.02 MB/s
+Benchmark32_4096       1000000     1787 ns/op     2290.92 MB/s
+Benchmark32_8192        500000     3593 ns/op     2279.68 MB/s
+Benchmark128_1       100000000     26.1 ns/op       38.33 MB/s
+Benchmark128_2       100000000     29.0 ns/op       69.07 MB/s
+Benchmark128_4        50000000     29.8 ns/op      134.17 MB/s
+Benchmark128_8        50000000     31.6 ns/op      252.86 MB/s
+Benchmark128_16      100000000     26.5 ns/op      603.42 MB/s
+Benchmark128_32      100000000     28.6 ns/op     1117.15 MB/s
+Benchmark128_64       50000000     35.5 ns/op     1800.97 MB/s
+Benchmark128_128      50000000     50.9 ns/op     2515.50 MB/s
+Benchmark128_256      20000000     76.9 ns/op     3330.11 MB/s
+Benchmark128_512      20000000      135 ns/op     3769.09 MB/s
+Benchmark128_1024     10000000      250 ns/op     4094.38 MB/s
+Benchmark128_2048      5000000      477 ns/op     4290.75 MB/s
+Benchmark128_4096      2000000      940 ns/op     4353.29 MB/s
+Benchmark128_8192      1000000     1838 ns/op     4455.47 MB/s
+
+
+ + +
+
+benchmark              Go1.0 MB/s    Go1.1 MB/s  speedup    Go1.2 MB/s  speedup    Go1.3 MB/s  speedup
+Benchmark32_1               98.90        118.59    1.20x        114.79    0.97x        130.00    1.13x
+Benchmark32_2              168.04        213.31    1.27x        210.65    0.99x        226.42    1.07x
+Benchmark32_4              414.01        494.19    1.19x        490.29    0.99x        500.39    1.02x
+Benchmark32_8              662.19        836.09    1.26x        836.46    1.00x        844.69    1.01x
+Benchmark32_16             917.46       1304.62    1.42x       1297.63    0.99x       1321.61    1.02x
+Benchmark32_32            1141.93       1737.54    1.52x       1728.24    0.99x       1743.93    1.01x
+Benchmark32_64            1289.47       2039.51    1.58x       2038.20    1.00x       2071.64    1.02x
+Benchmark32_128           1299.23       2097.63    1.61x       2177.13    1.04x       2222.96    1.02x
+Benchmark32_256           1369.90       2202.34    1.61x       2213.15    1.00x       2188.60    0.99x
+Benchmark32_512           1399.56       2255.72    1.61x       2264.49    1.00x       2260.59    1.00x
+Benchmark32_1024          1410.90       2285.82    1.62x       2270.99    0.99x       2263.73    1.00x
+Benchmark32_2048          1422.14       2297.62    1.62x       2269.59    0.99x       2296.02    1.01x
+Benchmark32_4096          1420.53       2307.81    1.62x       2273.43    0.99x       2290.92    1.01x
+Benchmark32_8192          1424.79       2312.87    1.62x       2286.07    0.99x       2279.68    1.00x
+Benchmark128_1               8.32         30.15    3.62x         30.84    1.02x         38.33    1.24x
+Benchmark128_2              16.38         59.72    3.65x         59.37    0.99x         69.07    1.16x
+Benchmark128_4              32.26        112.96    3.50x        114.24    1.01x        134.17    1.17x
+Benchmark128_8              62.68        217.88    3.48x        218.18    1.00x        252.86    1.16x
+Benchmark128_16            128.47        451.57    3.51x        474.65    1.05x        603.42    1.27x
+Benchmark128_32            246.18        910.42    3.70x        871.06    0.96x       1117.15    1.28x
+Benchmark128_64            449.05       1477.64    3.29x       1449.24    0.98x       1800.97    1.24x
+Benchmark128_128           762.61       2222.42    2.91x       2217.30    1.00x       2515.50    1.13x
+Benchmark128_256          1179.92       3005.46    2.55x       2931.55    0.98x       3330.11    1.14x
+Benchmark128_512          1616.51       3590.75    2.22x       3592.08    1.00x       3769.09    1.05x
+Benchmark128_1024         1964.36       3979.67    2.03x       4034.01    1.01x       4094.38    1.01x
+Benchmark128_2048         2225.07       4156.93    1.87x       4244.17    1.02x       4290.75    1.01x
+Benchmark128_4096         2360.15       4299.09    1.82x       4392.35    1.02x       4353.29    0.99x
+Benchmark128_8192         2411.50       4356.84    1.81x       4480.68    1.03x       4455.47    0.99x
+
+
+ diff --git a/vendor/github.com/spaolacci/murmur3/murmur.go b/vendor/github.com/spaolacci/murmur3/murmur.go new file mode 100644 index 0000000..f99557c --- /dev/null +++ b/vendor/github.com/spaolacci/murmur3/murmur.go @@ -0,0 +1,65 @@ +// Copyright 2013, Sébastien Paolacci. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Native (and fast) implementation of Austin Appleby's MurmurHash3. + +Package murmur3 implements Austin Appleby's non-cryptographic MurmurHash3. + + Reference implementation: + http://code.google.com/p/smhasher/wiki/MurmurHash3 + + History, characteristics and (legacy) perfs: + https://sites.google.com/site/murmurhash/ + https://sites.google.com/site/murmurhash/statistics +*/ +package murmur3 + +type bmixer interface { + bmix(p []byte) (tail []byte) + Size() (n int) + reset() +} + +type digest struct { + clen int // Digested input cumulative length. + tail []byte // 0 to Size()-1 bytes view of `buf'. + buf [16]byte // Expected (but not required) to be Size() large. + bmixer +} + +func (d *digest) BlockSize() int { return 1 } + +func (d *digest) Write(p []byte) (n int, err error) { + n = len(p) + d.clen += n + + if len(d.tail) > 0 { + // Stick back pending bytes. + nfree := d.Size() - len(d.tail) // nfree ∈ [1, d.Size()-1]. + if nfree < len(p) { + // One full block can be formed. + block := append(d.tail, p[:nfree]...) + p = p[nfree:] + _ = d.bmix(block) // No tail. + } else { + // Tail's buf is large enough to prevent reallocs. + p = append(d.tail, p...) + } + } + + d.tail = d.bmix(p) + + // Keep own copy of the 0 to Size()-1 pending bytes. + nn := copy(d.buf[:], d.tail) + d.tail = d.buf[:nn] + + return n, nil +} + +func (d *digest) Reset() { + d.clen = 0 + d.tail = nil + d.bmixer.reset() +} diff --git a/vendor/github.com/spaolacci/murmur3/murmur128.go b/vendor/github.com/spaolacci/murmur3/murmur128.go new file mode 100644 index 0000000..568eba7 --- /dev/null +++ b/vendor/github.com/spaolacci/murmur3/murmur128.go @@ -0,0 +1,194 @@ +package murmur3 + +import ( + //"encoding/binary" + "hash" + "unsafe" +) + +const ( + c1_128 = 0x87c37b91114253d5 + c2_128 = 0x4cf5ad432745937f +) + +// Make sure interfaces are correctly implemented. +var ( + _ hash.Hash = new(digest128) + _ Hash128 = new(digest128) + _ bmixer = new(digest128) +) + +// Hack: the standard api doesn't define any Hash128 interface. +type Hash128 interface { + hash.Hash + Sum128() (uint64, uint64) +} + +// digest128 represents a partial evaluation of a 128 bites hash. +type digest128 struct { + digest + seed uint64 + h1 uint64 // Unfinalized running hash part 1. + h2 uint64 // Unfinalized running hash part 2. +} + +func New128() Hash128 { return New128WithSeed(0) } + +func New128WithSeed(seed uint32) Hash128 { + d := &digest128{seed: uint64(seed)} + d.bmixer = d + d.Reset() + return d +} + +func (d *digest128) Size() int { return 16 } + +func (d *digest128) reset() { d.h1, d.h2 = d.seed, d.seed } + +func (d *digest128) Sum(b []byte) []byte { + h1, h2 := d.Sum128() + return append(b, + byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32), + byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1), + + byte(h2>>56), byte(h2>>48), byte(h2>>40), byte(h2>>32), + byte(h2>>24), byte(h2>>16), byte(h2>>8), byte(h2), + ) +} + +func (d *digest128) bmix(p []byte) (tail []byte) { + h1, h2 := d.h1, d.h2 + + nblocks := len(p) / 16 + for i := 0; i < nblocks; i++ { + t := (*[2]uint64)(unsafe.Pointer(&p[i*16])) + k1, k2 := t[0], t[1] + + k1 *= c1_128 + k1 = (k1 << 31) | (k1 >> 33) // rotl64(k1, 31) + k1 *= c2_128 + h1 ^= k1 + + h1 = (h1 << 27) | (h1 >> 37) // rotl64(h1, 27) + h1 += h2 + h1 = h1*5 + 0x52dce729 + + k2 *= c2_128 + k2 = (k2 << 33) | (k2 >> 31) // rotl64(k2, 33) + k2 *= c1_128 + h2 ^= k2 + + h2 = (h2 << 31) | (h2 >> 33) // rotl64(h2, 31) + h2 += h1 + h2 = h2*5 + 0x38495ab5 + } + d.h1, d.h2 = h1, h2 + return p[nblocks*d.Size():] +} + +func (d *digest128) Sum128() (h1, h2 uint64) { + + h1, h2 = d.h1, d.h2 + + var k1, k2 uint64 + switch len(d.tail) & 15 { + case 15: + k2 ^= uint64(d.tail[14]) << 48 + fallthrough + case 14: + k2 ^= uint64(d.tail[13]) << 40 + fallthrough + case 13: + k2 ^= uint64(d.tail[12]) << 32 + fallthrough + case 12: + k2 ^= uint64(d.tail[11]) << 24 + fallthrough + case 11: + k2 ^= uint64(d.tail[10]) << 16 + fallthrough + case 10: + k2 ^= uint64(d.tail[9]) << 8 + fallthrough + case 9: + k2 ^= uint64(d.tail[8]) << 0 + + k2 *= c2_128 + k2 = (k2 << 33) | (k2 >> 31) // rotl64(k2, 33) + k2 *= c1_128 + h2 ^= k2 + + fallthrough + + case 8: + k1 ^= uint64(d.tail[7]) << 56 + fallthrough + case 7: + k1 ^= uint64(d.tail[6]) << 48 + fallthrough + case 6: + k1 ^= uint64(d.tail[5]) << 40 + fallthrough + case 5: + k1 ^= uint64(d.tail[4]) << 32 + fallthrough + case 4: + k1 ^= uint64(d.tail[3]) << 24 + fallthrough + case 3: + k1 ^= uint64(d.tail[2]) << 16 + fallthrough + case 2: + k1 ^= uint64(d.tail[1]) << 8 + fallthrough + case 1: + k1 ^= uint64(d.tail[0]) << 0 + k1 *= c1_128 + k1 = (k1 << 31) | (k1 >> 33) // rotl64(k1, 31) + k1 *= c2_128 + h1 ^= k1 + } + + h1 ^= uint64(d.clen) + h2 ^= uint64(d.clen) + + h1 += h2 + h2 += h1 + + h1 = fmix64(h1) + h2 = fmix64(h2) + + h1 += h2 + h2 += h1 + + return h1, h2 +} + +func fmix64(k uint64) uint64 { + k ^= k >> 33 + k *= 0xff51afd7ed558ccd + k ^= k >> 33 + k *= 0xc4ceb9fe1a85ec53 + k ^= k >> 33 + return k +} + +/* +func rotl64(x uint64, r byte) uint64 { + return (x << r) | (x >> (64 - r)) +} +*/ + +// Sum128 returns the MurmurHash3 sum of data. It is equivalent to the +// following sequence (without the extra burden and the extra allocation): +// hasher := New128() +// hasher.Write(data) +// return hasher.Sum128() +func Sum128(data []byte) (h1 uint64, h2 uint64) { return Sum128WithSeed(data, 0) } + +func Sum128WithSeed(data []byte, seed uint32) (h1 uint64, h2 uint64) { + d := &digest128{h1: uint64(seed), h2: uint64(seed)} + d.tail = d.bmix(data) + d.clen = len(data) + return d.Sum128() +} diff --git a/vendor/github.com/spaolacci/murmur3/murmur32.go b/vendor/github.com/spaolacci/murmur3/murmur32.go new file mode 100644 index 0000000..7f687ea --- /dev/null +++ b/vendor/github.com/spaolacci/murmur3/murmur32.go @@ -0,0 +1,160 @@ +package murmur3 + +// http://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/hash/Murmur3_32HashFunction.java + +import ( + "hash" + "unsafe" +) + +// Make sure interfaces are correctly implemented. +var ( + _ hash.Hash = new(digest32) + _ hash.Hash32 = new(digest32) + _ bmixer = new(digest32) +) + +const ( + c1_32 uint32 = 0xcc9e2d51 + c2_32 uint32 = 0x1b873593 +) + +// digest32 represents a partial evaluation of a 32 bites hash. +type digest32 struct { + digest + seed uint32 + h1 uint32 // Unfinalized running hash. +} + +func New32() hash.Hash32 { return New32WithSeed(0) } + +func New32WithSeed(seed uint32) hash.Hash32 { + d := &digest32{seed: seed} + d.bmixer = d + d.Reset() + return d +} + +func (d *digest32) Size() int { return 4 } + +func (d *digest32) reset() { d.h1 = d.seed } + +func (d *digest32) Sum(b []byte) []byte { + h := d.Sum32() + return append(b, byte(h>>24), byte(h>>16), byte(h>>8), byte(h)) +} + +// Digest as many blocks as possible. +func (d *digest32) bmix(p []byte) (tail []byte) { + h1 := d.h1 + + nblocks := len(p) / 4 + for i := 0; i < nblocks; i++ { + k1 := *(*uint32)(unsafe.Pointer(&p[i*4])) + + k1 *= c1_32 + k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15) + k1 *= c2_32 + + h1 ^= k1 + h1 = (h1 << 13) | (h1 >> 19) // rotl32(h1, 13) + h1 = h1*5 + 0xe6546b64 + } + d.h1 = h1 + return p[nblocks*d.Size():] +} + +func (d *digest32) Sum32() (h1 uint32) { + + h1 = d.h1 + + var k1 uint32 + switch len(d.tail) & 3 { + case 3: + k1 ^= uint32(d.tail[2]) << 16 + fallthrough + case 2: + k1 ^= uint32(d.tail[1]) << 8 + fallthrough + case 1: + k1 ^= uint32(d.tail[0]) + k1 *= c1_32 + k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15) + k1 *= c2_32 + h1 ^= k1 + } + + h1 ^= uint32(d.clen) + + h1 ^= h1 >> 16 + h1 *= 0x85ebca6b + h1 ^= h1 >> 13 + h1 *= 0xc2b2ae35 + h1 ^= h1 >> 16 + + return h1 +} + +/* +func rotl32(x uint32, r byte) uint32 { + return (x << r) | (x >> (32 - r)) +} +*/ + +// Sum32 returns the MurmurHash3 sum of data. It is equivalent to the +// following sequence (without the extra burden and the extra allocation): +// hasher := New32() +// hasher.Write(data) +// return hasher.Sum32() +func Sum32(data []byte) uint32 { return Sum32WithSeed(data, 0) } + +func Sum32WithSeed(data []byte, seed uint32) uint32 { + + var h1 uint32 = seed + + nblocks := len(data) / 4 + var p uintptr + if len(data) > 0 { + p = uintptr(unsafe.Pointer(&data[0])) + } + p1 := p + uintptr(4*nblocks) + for ; p < p1; p += 4 { + k1 := *(*uint32)(unsafe.Pointer(p)) + + k1 *= c1_32 + k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15) + k1 *= c2_32 + + h1 ^= k1 + h1 = (h1 << 13) | (h1 >> 19) // rotl32(h1, 13) + h1 = h1*5 + 0xe6546b64 + } + + tail := data[nblocks*4:] + + var k1 uint32 + switch len(tail) & 3 { + case 3: + k1 ^= uint32(tail[2]) << 16 + fallthrough + case 2: + k1 ^= uint32(tail[1]) << 8 + fallthrough + case 1: + k1 ^= uint32(tail[0]) + k1 *= c1_32 + k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15) + k1 *= c2_32 + h1 ^= k1 + } + + h1 ^= uint32(len(data)) + + h1 ^= h1 >> 16 + h1 *= 0x85ebca6b + h1 ^= h1 >> 13 + h1 *= 0xc2b2ae35 + h1 ^= h1 >> 16 + + return h1 +} diff --git a/vendor/github.com/spaolacci/murmur3/murmur64.go b/vendor/github.com/spaolacci/murmur3/murmur64.go new file mode 100644 index 0000000..85580d5 --- /dev/null +++ b/vendor/github.com/spaolacci/murmur3/murmur64.go @@ -0,0 +1,49 @@ +package murmur3 + +import ( + "hash" +) + +// Make sure interfaces are correctly implemented. +var ( + _ hash.Hash = new(digest64) + _ hash.Hash64 = new(digest64) + _ bmixer = new(digest64) +) + +// digest64 is half a digest128. +type digest64 digest128 + +func New64() hash.Hash64 { return New64WithSeed(0) } + +func New64WithSeed(seed uint32) hash.Hash64 { + d := (*digest64)(New128WithSeed(seed).(*digest128)) + return d +} + +func (d *digest64) Sum(b []byte) []byte { + h1 := d.Sum64() + return append(b, + byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32), + byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1)) +} + +func (d *digest64) Sum64() uint64 { + h1, _ := (*digest128)(d).Sum128() + return h1 +} + +// Sum64 returns the MurmurHash3 sum of data. It is equivalent to the +// following sequence (without the extra burden and the extra allocation): +// hasher := New64() +// hasher.Write(data) +// return hasher.Sum64() +func Sum64(data []byte) uint64 { return Sum64WithSeed(data, 0) } + +func Sum64WithSeed(data []byte, seed uint32) uint64 { + d := &digest128{h1: uint64(seed), h2: uint64(seed)} + d.tail = d.bmix(data) + d.clen = len(data) + h1, _ := d.Sum128() + return h1 +} diff --git a/vendor/golang.org/x/crypto/AUTHORS b/vendor/golang.org/x/crypto/AUTHORS index 15167cd..2b00ddb 100644 --- a/vendor/golang.org/x/crypto/AUTHORS +++ b/vendor/golang.org/x/crypto/AUTHORS @@ -1,3 +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. +# visible at https://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/crypto/CONTRIBUTORS b/vendor/golang.org/x/crypto/CONTRIBUTORS index 1c4577e..1fbd3e9 100644 --- a/vendor/golang.org/x/crypto/CONTRIBUTORS +++ b/vendor/golang.org/x/crypto/CONTRIBUTORS @@ -1,3 +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. +# visible at https://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/crypto/acme/acme.go b/vendor/golang.org/x/crypto/acme/acme.go index a7b6ce4..d6a09dd 100644 --- a/vendor/golang.org/x/crypto/acme/acme.go +++ b/vendor/golang.org/x/crypto/acme/acme.go @@ -51,38 +51,6 @@ const ( maxNonces = 100 ) -// CertOption is an optional argument type for Client methods which manipulate -// certificate data. -type CertOption interface { - privateCertOpt() -} - -// WithKey creates an option holding a private/public key pair. -// The private part signs a certificate, and the public part represents the signee. -func WithKey(key crypto.Signer) CertOption { - return &certOptKey{key} -} - -type certOptKey struct { - key crypto.Signer -} - -func (*certOptKey) privateCertOpt() {} - -// WithTemplate creates an option for specifying a certificate template. -// See x509.CreateCertificate for template usage details. -// -// In TLSSNIxChallengeCert methods, the template is also used as parent, -// resulting in a self-signed certificate. -// The DNSNames field of t is always overwritten for tls-sni challenge certs. -func WithTemplate(t *x509.Certificate) CertOption { - return (*certOptTemplate)(t) -} - -type certOptTemplate x509.Certificate - -func (*certOptTemplate) privateCertOpt() {} - // Client is an ACME client. // The only required field is Key. An example of creating a client with a new key // is as follows: @@ -207,7 +175,7 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration, return nil, "", responseError(res) } - curl := res.Header.Get("location") // cert permanent URL + curl := res.Header.Get("Location") // cert permanent URL if res.ContentLength == 0 { // no cert in the body; poll until we get it cert, err := c.FetchCert(ctx, curl, bundle) @@ -240,7 +208,7 @@ func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]by if res.StatusCode > 299 { return nil, responseError(res) } - d := retryAfter(res.Header.Get("retry-after"), 3*time.Second) + d := retryAfter(res.Header.Get("Retry-After"), 3*time.Second) select { case <-time.After(d): // retry @@ -444,7 +412,7 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat if err != nil { return nil, err } - retry := res.Header.Get("retry-after") + retry := res.Header.Get("Retry-After") if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted { res.Body.Close() if err := sleep(retry, 1); err != nil { @@ -703,7 +671,7 @@ func (c *Client) retryPostJWS(ctx context.Context, key crypto.Signer, url string // clear any nonces that we might've stored that might now be // considered bad c.clearNonces() - retry := res.Header.Get("retry-after") + retry := res.Header.Get("Retry-After") if err := sleep(retry, 1); err != nil { return nil, err } diff --git a/vendor/golang.org/x/crypto/acme/autocert/listener.go b/vendor/golang.org/x/crypto/acme/autocert/listener.go index d4c93d2..d744df0 100644 --- a/vendor/golang.org/x/crypto/acme/autocert/listener.go +++ b/vendor/golang.org/x/crypto/acme/autocert/listener.go @@ -36,6 +36,9 @@ import ( // operating system-specific cache or temp directory. This may not // be suitable for servers spanning multiple machines. // +// The returned listener uses a *tls.Config that enables HTTP/2, and +// should only be used with servers that support HTTP/2. +// // The returned Listener also enables TCP keep-alives on the accepted // connections. The returned *tls.Conn are returned before their TLS // handshake has completed. @@ -58,6 +61,9 @@ func NewListener(domains ...string) net.Listener { // Listener listens on the standard TLS port (443) on all interfaces // and returns a net.Listener returning *tls.Conn connections. // +// The returned listener uses a *tls.Config that enables HTTP/2, and +// should only be used with servers that support HTTP/2. +// // The returned Listener also enables TCP keep-alives on the accepted // connections. The returned *tls.Conn are returned before their TLS // handshake has completed. @@ -68,7 +74,8 @@ func (m *Manager) Listener() net.Listener { ln := &listener{ m: m, conf: &tls.Config{ - GetCertificate: m.GetCertificate, // bonus: panic on nil m + GetCertificate: m.GetCertificate, // bonus: panic on nil m + NextProtos: []string{"h2", "http/1.1"}, // Enable HTTP/2 }, } ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443") diff --git a/vendor/golang.org/x/crypto/acme/types.go b/vendor/golang.org/x/crypto/acme/types.go index ab4de0b..3e19974 100644 --- a/vendor/golang.org/x/crypto/acme/types.go +++ b/vendor/golang.org/x/crypto/acme/types.go @@ -5,6 +5,8 @@ package acme import ( + "crypto" + "crypto/x509" "errors" "fmt" "net/http" @@ -293,3 +295,35 @@ func (e *wireError) error(h http.Header) *Error { Header: h, } } + +// CertOption is an optional argument type for the TLSSNIxChallengeCert methods for +// customizing a temporary certificate for TLS-SNI challenges. +type CertOption interface { + privateCertOpt() +} + +// WithKey creates an option holding a private/public key pair. +// The private part signs a certificate, and the public part represents the signee. +func WithKey(key crypto.Signer) CertOption { + return &certOptKey{key} +} + +type certOptKey struct { + key crypto.Signer +} + +func (*certOptKey) privateCertOpt() {} + +// WithTemplate creates an option for specifying a certificate template. +// See x509.CreateCertificate for template usage details. +// +// In TLSSNIxChallengeCert methods, the template is also used as parent, +// resulting in a self-signed certificate. +// The DNSNames field of t is always overwritten for tls-sni challenge certs. +func WithTemplate(t *x509.Certificate) CertOption { + return (*certOptTemplate)(t) +} + +type certOptTemplate x509.Certificate + +func (*certOptTemplate) privateCertOpt() {} diff --git a/vendor/golang.org/x/crypto/blake2b/blake2b.go b/vendor/golang.org/x/crypto/blake2b/blake2b.go index fa9e48e..7f0a86e 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2b.go +++ b/vendor/golang.org/x/crypto/blake2b/blake2b.go @@ -2,8 +2,18 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package blake2b implements the BLAKE2b hash algorithm as -// defined in RFC 7693. +// Package blake2b implements the BLAKE2b hash algorithm defined by RFC 7693 +// and the extendable output function (XOF) BLAKE2Xb. +// +// For a detailed specification of BLAKE2b see https://blake2.net/blake2.pdf +// and for BLAKE2Xb see https://blake2.net/blake2x.pdf +// +// If you aren't sure which function you need, use BLAKE2b (Sum512 or New512). +// If you need a secret-key MAC (message authentication code), use the New512 +// function with a non-nil key. +// +// BLAKE2X is a construction to compute hash values larger than 64 bytes. It +// can produce hash values between 0 and 4 GiB. package blake2b import ( @@ -171,7 +181,13 @@ func (d *digest) Write(p []byte) (n int, err error) { return } -func (d *digest) Sum(b []byte) []byte { +func (d *digest) Sum(sum []byte) []byte { + var hash [Size]byte + d.finalize(&hash) + return append(sum, hash[:d.size]...) +} + +func (d *digest) finalize(hash *[Size]byte) { var block [BlockSize]byte copy(block[:], d.block[:d.offset]) remaining := uint64(BlockSize - d.offset) @@ -185,10 +201,7 @@ func (d *digest) Sum(b []byte) []byte { h := d.h hashBlocks(&h, &c, 0xFFFFFFFFFFFFFFFF, block[:]) - var sum [Size]byte - for i, v := range h[:(d.size+7)/8] { - binary.LittleEndian.PutUint64(sum[8*i:], v) + for i, v := range h { + binary.LittleEndian.PutUint64(hash[8*i:], v) } - - return append(b, sum[:d.size]...) } diff --git a/vendor/golang.org/x/crypto/blake2b/blake2x.go b/vendor/golang.org/x/crypto/blake2b/blake2x.go new file mode 100644 index 0000000..c814496 --- /dev/null +++ b/vendor/golang.org/x/crypto/blake2b/blake2x.go @@ -0,0 +1,177 @@ +// 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. + +package blake2b + +import ( + "encoding/binary" + "errors" + "io" +) + +// XOF defines the interface to hash functions that +// support arbitrary-length output. +type XOF interface { + // Write absorbs more data into the hash's state. It panics if called + // after Read. + io.Writer + + // Read reads more output from the hash. It returns io.EOF if the limit + // has been reached. + io.Reader + + // Clone returns a copy of the XOF in its current state. + Clone() XOF + + // Reset resets the XOF to its initial state. + Reset() +} + +// OutputLengthUnknown can be used as the size argument to NewXOF to indicate +// the the length of the output is not known in advance. +const OutputLengthUnknown = 0 + +// magicUnknownOutputLength is a magic value for the output size that indicates +// an unknown number of output bytes. +const magicUnknownOutputLength = (1 << 32) - 1 + +// maxOutputLength is the absolute maximum number of bytes to produce when the +// number of output bytes is unknown. +const maxOutputLength = (1 << 32) * 64 + +// NewXOF creates a new variable-output-length hash. The hash either produce a +// known number of bytes (1 <= size < 2**32-1), or an unknown number of bytes +// (size == OutputLengthUnknown). In the latter case, an absolute limit of +// 256GiB applies. +// +// A non-nil key turns the hash into a MAC. The key must between +// zero and 32 bytes long. +func NewXOF(size uint32, key []byte) (XOF, error) { + if len(key) > Size { + return nil, errKeySize + } + if size == magicUnknownOutputLength { + // 2^32-1 indicates an unknown number of bytes and thus isn't a + // valid length. + return nil, errors.New("blake2b: XOF length too large") + } + if size == OutputLengthUnknown { + size = magicUnknownOutputLength + } + x := &xof{ + d: digest{ + size: Size, + keyLen: len(key), + }, + length: size, + } + copy(x.d.key[:], key) + x.Reset() + return x, nil +} + +type xof struct { + d digest + length uint32 + remaining uint64 + cfg, root, block [Size]byte + offset int + nodeOffset uint32 + readMode bool +} + +func (x *xof) Write(p []byte) (n int, err error) { + if x.readMode { + panic("blake2b: write to XOF after read") + } + return x.d.Write(p) +} + +func (x *xof) Clone() XOF { + clone := *x + return &clone +} + +func (x *xof) Reset() { + x.cfg[0] = byte(Size) + binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length + binary.LittleEndian.PutUint32(x.cfg[12:], x.length) // XOF length + x.cfg[17] = byte(Size) // inner hash size + + x.d.Reset() + x.d.h[1] ^= uint64(x.length) << 32 + + x.remaining = uint64(x.length) + if x.remaining == magicUnknownOutputLength { + x.remaining = maxOutputLength + } + x.offset, x.nodeOffset = 0, 0 + x.readMode = false +} + +func (x *xof) Read(p []byte) (n int, err error) { + if !x.readMode { + x.d.finalize(&x.root) + x.readMode = true + } + + if x.remaining == 0 { + return 0, io.EOF + } + + n = len(p) + if uint64(n) > x.remaining { + n = int(x.remaining) + p = p[:n] + } + + if x.offset > 0 { + blockRemaining := Size - x.offset + if n < blockRemaining { + x.offset += copy(p, x.block[x.offset:]) + x.remaining -= uint64(n) + return + } + copy(p, x.block[x.offset:]) + p = p[blockRemaining:] + x.offset = 0 + x.remaining -= uint64(blockRemaining) + } + + for len(p) >= Size { + binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset) + x.nodeOffset++ + + x.d.initConfig(&x.cfg) + x.d.Write(x.root[:]) + x.d.finalize(&x.block) + + copy(p, x.block[:]) + p = p[Size:] + x.remaining -= uint64(Size) + } + + if todo := len(p); todo > 0 { + if x.remaining < uint64(Size) { + x.cfg[0] = byte(x.remaining) + } + binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset) + x.nodeOffset++ + + x.d.initConfig(&x.cfg) + x.d.Write(x.root[:]) + x.d.finalize(&x.block) + + x.offset = copy(p, x.block[:todo]) + x.remaining -= uint64(todo) + } + return +} + +func (d *digest) initConfig(cfg *[Size]byte) { + d.offset, d.c[0], d.c[1] = 0, 0, 0 + for i := range d.h { + d.h[i] = iv[i] ^ binary.LittleEndian.Uint64(cfg[i*8:]) + } +} diff --git a/vendor/golang.org/x/crypto/blake2s/blake2s.go b/vendor/golang.org/x/crypto/blake2s/blake2s.go index 394c121..839af1f 100644 --- a/vendor/golang.org/x/crypto/blake2s/blake2s.go +++ b/vendor/golang.org/x/crypto/blake2s/blake2s.go @@ -2,8 +2,18 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package blake2s implements the BLAKE2s hash algorithm as -// defined in RFC 7693. +// Package blake2s implements the BLAKE2s hash algorithm defined by RFC 7693 +// and the extendable output function (XOF) BLAKE2Xs. +// +// For a detailed specification of BLAKE2s see https://blake2.net/blake2.pdf +// and for BLAKE2Xs see https://blake2.net/blake2x.pdf +// +// If you aren't sure which function you need, use BLAKE2s (Sum256 or New256). +// If you need a secret-key MAC (message authentication code), use the New256 +// function with a non-nil key. +// +// BLAKE2X is a construction to compute hash values larger than 32 bytes. It +// can produce hash values between 0 and 65535 bytes. package blake2s import ( @@ -15,8 +25,12 @@ import ( const ( // The blocksize of BLAKE2s in bytes. BlockSize = 64 + // The hash size of BLAKE2s-256 in bytes. Size = 32 + + // The hash size of BLAKE2s-128 in bytes. + Size128 = 16 ) var errKeySize = errors.New("blake2s: invalid key size") @@ -37,6 +51,17 @@ func Sum256(data []byte) [Size]byte { // key turns the hash into a MAC. The key must between zero and 32 bytes long. func New256(key []byte) (hash.Hash, error) { return newDigest(Size, key) } +// New128 returns a new hash.Hash computing the BLAKE2s-128 checksum given a +// non-empty key. Note that a 128-bit digest is too small to be secure as a +// cryptographic hash and should only be used as a MAC, thus the key argument +// is not optional. +func New128(key []byte) (hash.Hash, error) { + if len(key) == 0 { + return nil, errors.New("blake2s: a key is required for a 128-bit hash") + } + return newDigest(Size128, key) +} + func newDigest(hashSize int, key []byte) (*digest, error) { if len(key) > Size { return nil, errKeySize @@ -137,7 +162,13 @@ func (d *digest) Write(p []byte) (n int, err error) { return } -func (d *digest) Sum(b []byte) []byte { +func (d *digest) Sum(sum []byte) []byte { + var hash [Size]byte + d.finalize(&hash) + return append(sum, hash[:d.size]...) +} + +func (d *digest) finalize(hash *[Size]byte) { var block [BlockSize]byte h := d.h c := d.c @@ -150,11 +181,7 @@ func (d *digest) Sum(b []byte) []byte { c[0] -= remaining hashBlocks(&h, &c, 0xFFFFFFFF, block[:]) - - var sum [Size]byte for i, v := range h { - binary.LittleEndian.PutUint32(sum[4*i:], v) + binary.LittleEndian.PutUint32(hash[4*i:], v) } - - return append(b, sum[:d.size]...) } diff --git a/vendor/golang.org/x/crypto/blake2s/blake2s_386.go b/vendor/golang.org/x/crypto/blake2s/blake2s_386.go index 8575080..45ae546 100644 --- a/vendor/golang.org/x/crypto/blake2s/blake2s_386.go +++ b/vendor/golang.org/x/crypto/blake2s/blake2s_386.go @@ -7,10 +7,9 @@ package blake2s var ( - useSSE4 = false - useSSSE3 = supportSSSE3() - useSSE2 = supportSSE2() - useGeneric = true + useSSE4 = false + useSSSE3 = supportSSSE3() + useSSE2 = supportSSE2() ) //go:noescape diff --git a/vendor/golang.org/x/crypto/blake2s/blake2s_amd64.go b/vendor/golang.org/x/crypto/blake2s/blake2s_amd64.go index 43a7625..a925e6b 100644 --- a/vendor/golang.org/x/crypto/blake2s/blake2s_amd64.go +++ b/vendor/golang.org/x/crypto/blake2s/blake2s_amd64.go @@ -7,10 +7,9 @@ package blake2s var ( - useSSE4 = supportSSE4() - useSSSE3 = supportSSSE3() - useSSE2 = true // Always available on amd64 - useGeneric = false + useSSE4 = supportSSE4() + useSSSE3 = supportSSSE3() + useSSE2 = true // Always available on amd64 ) //go:noescape @@ -33,7 +32,9 @@ func hashBlocks(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte) { hashBlocksSSE4(h, c, flag, blocks) } else if useSSSE3 { hashBlocksSSSE3(h, c, flag, blocks) - } else { + } else if useSSE2 { hashBlocksSSE2(h, c, flag, blocks) + } else { + hashBlocksGeneric(h, c, flag, blocks) } } diff --git a/vendor/golang.org/x/crypto/blake2s/blake2s_ref.go b/vendor/golang.org/x/crypto/blake2s/blake2s_ref.go index 7e54230..a311273 100644 --- a/vendor/golang.org/x/crypto/blake2s/blake2s_ref.go +++ b/vendor/golang.org/x/crypto/blake2s/blake2s_ref.go @@ -7,10 +7,9 @@ package blake2s var ( - useSSE4 = false - useSSSE3 = false - useSSE2 = false - useGeneric = true + useSSE4 = false + useSSSE3 = false + useSSE2 = false ) func hashBlocks(h *[8]uint32, c *[2]uint32, flag uint32, blocks []byte) { diff --git a/vendor/golang.org/x/crypto/blake2s/blake2x.go b/vendor/golang.org/x/crypto/blake2s/blake2x.go new file mode 100644 index 0000000..eaff2a7 --- /dev/null +++ b/vendor/golang.org/x/crypto/blake2s/blake2x.go @@ -0,0 +1,178 @@ +// 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. + +package blake2s + +import ( + "encoding/binary" + "errors" + "io" +) + +// XOF defines the interface to hash functions that +// support arbitrary-length output. +type XOF interface { + // Write absorbs more data into the hash's state. It panics if called + // after Read. + io.Writer + + // Read reads more output from the hash. It returns io.EOF if the limit + // has been reached. + io.Reader + + // Clone returns a copy of the XOF in its current state. + Clone() XOF + + // Reset resets the XOF to its initial state. + Reset() +} + +// OutputLengthUnknown can be used as the size argument to NewXOF to indicate +// the the length of the output is not known in advance. +const OutputLengthUnknown = 0 + +// magicUnknownOutputLength is a magic value for the output size that indicates +// an unknown number of output bytes. +const magicUnknownOutputLength = 65535 + +// maxOutputLength is the absolute maximum number of bytes to produce when the +// number of output bytes is unknown. +const maxOutputLength = (1 << 32) * 32 + +// NewXOF creates a new variable-output-length hash. The hash either produce a +// known number of bytes (1 <= size < 65535), or an unknown number of bytes +// (size == OutputLengthUnknown). In the latter case, an absolute limit of +// 128GiB applies. +// +// A non-nil key turns the hash into a MAC. The key must between +// zero and 32 bytes long. +func NewXOF(size uint16, key []byte) (XOF, error) { + if len(key) > Size { + return nil, errKeySize + } + if size == magicUnknownOutputLength { + // 2^16-1 indicates an unknown number of bytes and thus isn't a + // valid length. + return nil, errors.New("blake2s: XOF length too large") + } + if size == OutputLengthUnknown { + size = magicUnknownOutputLength + } + x := &xof{ + d: digest{ + size: Size, + keyLen: len(key), + }, + length: size, + } + copy(x.d.key[:], key) + x.Reset() + return x, nil +} + +type xof struct { + d digest + length uint16 + remaining uint64 + cfg, root, block [Size]byte + offset int + nodeOffset uint32 + readMode bool +} + +func (x *xof) Write(p []byte) (n int, err error) { + if x.readMode { + panic("blake2s: write to XOF after read") + } + return x.d.Write(p) +} + +func (x *xof) Clone() XOF { + clone := *x + return &clone +} + +func (x *xof) Reset() { + x.cfg[0] = byte(Size) + binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length + binary.LittleEndian.PutUint16(x.cfg[12:], x.length) // XOF length + x.cfg[15] = byte(Size) // inner hash size + + x.d.Reset() + x.d.h[3] ^= uint32(x.length) + + x.remaining = uint64(x.length) + if x.remaining == magicUnknownOutputLength { + x.remaining = maxOutputLength + } + x.offset, x.nodeOffset = 0, 0 + x.readMode = false +} + +func (x *xof) Read(p []byte) (n int, err error) { + if !x.readMode { + x.d.finalize(&x.root) + x.readMode = true + } + + if x.remaining == 0 { + return 0, io.EOF + } + + n = len(p) + if uint64(n) > x.remaining { + n = int(x.remaining) + p = p[:n] + } + + if x.offset > 0 { + blockRemaining := Size - x.offset + if n < blockRemaining { + x.offset += copy(p, x.block[x.offset:]) + x.remaining -= uint64(n) + return + } + copy(p, x.block[x.offset:]) + p = p[blockRemaining:] + x.offset = 0 + x.remaining -= uint64(blockRemaining) + } + + for len(p) >= Size { + binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset) + x.nodeOffset++ + + x.d.initConfig(&x.cfg) + x.d.Write(x.root[:]) + x.d.finalize(&x.block) + + copy(p, x.block[:]) + p = p[Size:] + x.remaining -= uint64(Size) + } + + if todo := len(p); todo > 0 { + if x.remaining < uint64(Size) { + x.cfg[0] = byte(x.remaining) + } + binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset) + x.nodeOffset++ + + x.d.initConfig(&x.cfg) + x.d.Write(x.root[:]) + x.d.finalize(&x.block) + + x.offset = copy(p, x.block[:todo]) + x.remaining -= uint64(todo) + } + + return +} + +func (d *digest) initConfig(cfg *[Size]byte) { + d.offset, d.c[0], d.c[1] = 0, 0, 0 + for i := range d.h { + d.h[i] = iv[i] ^ binary.LittleEndian.Uint32(cfg[i*4:]) + } +} diff --git a/vendor/golang.org/x/crypto/blowfish/cipher.go b/vendor/golang.org/x/crypto/blowfish/cipher.go index 65fa760..e404e9b 100644 --- a/vendor/golang.org/x/crypto/blowfish/cipher.go +++ b/vendor/golang.org/x/crypto/blowfish/cipher.go @@ -6,7 +6,7 @@ package blowfish // The code is a port of Bruce Schneier's C implementation. -// See http://www.schneier.com/blowfish.html. +// See https://www.schneier.com/blowfish.html. import "strconv" diff --git a/vendor/golang.org/x/crypto/blowfish/const.go b/vendor/golang.org/x/crypto/blowfish/const.go index 8c5ee4c..d040775 100644 --- a/vendor/golang.org/x/crypto/blowfish/const.go +++ b/vendor/golang.org/x/crypto/blowfish/const.go @@ -4,7 +4,7 @@ // The startup permutation array and substitution boxes. // They are the hexadecimal digits of PI; see: -// http://www.schneier.com/code/constants.txt. +// https://www.schneier.com/code/constants.txt. package blowfish diff --git a/vendor/golang.org/x/crypto/curve25519/const_amd64.h b/vendor/golang.org/x/crypto/curve25519/const_amd64.h index 80ad222..b3f7416 100644 --- a/vendor/golang.org/x/crypto/curve25519/const_amd64.h +++ b/vendor/golang.org/x/crypto/curve25519/const_amd64.h @@ -3,6 +3,6 @@ // license that can be found in the LICENSE file. // This code was translated into a form compatible with 6a from the public -// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html +// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html #define REDMASK51 0x0007FFFFFFFFFFFF diff --git a/vendor/golang.org/x/crypto/curve25519/const_amd64.s b/vendor/golang.org/x/crypto/curve25519/const_amd64.s index 0ad5398..ee7b4bd 100644 --- a/vendor/golang.org/x/crypto/curve25519/const_amd64.s +++ b/vendor/golang.org/x/crypto/curve25519/const_amd64.s @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // This code was translated into a form compatible with 6a from the public -// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html +// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html // +build amd64,!gccgo,!appengine diff --git a/vendor/golang.org/x/crypto/curve25519/doc.go b/vendor/golang.org/x/crypto/curve25519/doc.go index f7db9c1..076a8d4 100644 --- a/vendor/golang.org/x/crypto/curve25519/doc.go +++ b/vendor/golang.org/x/crypto/curve25519/doc.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Package curve25519 provides an implementation of scalar multiplication on -// the elliptic curve known as curve25519. See http://cr.yp.to/ecdh.html +// the elliptic curve known as curve25519. See https://cr.yp.to/ecdh.html package curve25519 // basePoint is the x coordinate of the generator of the curve. diff --git a/vendor/golang.org/x/crypto/curve25519/freeze_amd64.s b/vendor/golang.org/x/crypto/curve25519/freeze_amd64.s index 536479b..3908161 100644 --- a/vendor/golang.org/x/crypto/curve25519/freeze_amd64.s +++ b/vendor/golang.org/x/crypto/curve25519/freeze_amd64.s @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // This code was translated into a form compatible with 6a from the public -// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html +// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html // +build amd64,!gccgo,!appengine diff --git a/vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s b/vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s index 7074e5c..9e9040b 100644 --- a/vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s +++ b/vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // This code was translated into a form compatible with 6a from the public -// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html +// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html // +build amd64,!gccgo,!appengine diff --git a/vendor/golang.org/x/crypto/curve25519/mul_amd64.s b/vendor/golang.org/x/crypto/curve25519/mul_amd64.s index b162e65..5ce80a2 100644 --- a/vendor/golang.org/x/crypto/curve25519/mul_amd64.s +++ b/vendor/golang.org/x/crypto/curve25519/mul_amd64.s @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // This code was translated into a form compatible with 6a from the public -// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html +// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html // +build amd64,!gccgo,!appengine diff --git a/vendor/golang.org/x/crypto/curve25519/square_amd64.s b/vendor/golang.org/x/crypto/curve25519/square_amd64.s index 4e864a8..12f7373 100644 --- a/vendor/golang.org/x/crypto/curve25519/square_amd64.s +++ b/vendor/golang.org/x/crypto/curve25519/square_amd64.s @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // This code was translated into a form compatible with 6a from the public -// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html +// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html // +build amd64,!gccgo,!appengine diff --git a/vendor/golang.org/x/crypto/ed25519/ed25519.go b/vendor/golang.org/x/crypto/ed25519/ed25519.go index f1d9567..16cd385 100644 --- a/vendor/golang.org/x/crypto/ed25519/ed25519.go +++ b/vendor/golang.org/x/crypto/ed25519/ed25519.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Package ed25519 implements the Ed25519 signature algorithm. See -// http://ed25519.cr.yp.to/. +// https://ed25519.cr.yp.to/. // // These functions are also compatible with the “Ed25519” function defined in // https://tools.ietf.org/html/draft-irtf-cfrg-eddsa-05. @@ -13,10 +13,10 @@ package ed25519 // from SUPERCOP. import ( + "bytes" "crypto" cryptorand "crypto/rand" "crypto/sha512" - "crypto/subtle" "errors" "io" "strconv" @@ -177,5 +177,5 @@ func Verify(publicKey PublicKey, message, sig []byte) bool { var checkR [32]byte R.ToBytes(&checkR) - return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1 + return bytes.Equal(sig[:32], checkR[:]) } diff --git a/vendor/golang.org/x/crypto/ssh/cipher.go b/vendor/golang.org/x/crypto/ssh/cipher.go index 13484ab..22bb30c 100644 --- a/vendor/golang.org/x/crypto/ssh/cipher.go +++ b/vendor/golang.org/x/crypto/ssh/cipher.go @@ -392,7 +392,9 @@ func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { c.incIV() padding := plain[0] - if padding < 4 || padding >= 20 { + if padding < 4 { + // padding is a byte, so it automatically satisfies + // the maximum size, which is 255. return nil, fmt.Errorf("ssh: illegal padding %d", padding) } diff --git a/vendor/golang.org/x/crypto/ssh/kex.go b/vendor/golang.org/x/crypto/ssh/kex.go index c87fbeb..f91c277 100644 --- a/vendor/golang.org/x/crypto/ssh/kex.go +++ b/vendor/golang.org/x/crypto/ssh/kex.go @@ -383,8 +383,8 @@ func init() { // 4253 and Oakley Group 2 in RFC 2409. p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{ - g: new(big.Int).SetInt64(2), - p: p, + g: new(big.Int).SetInt64(2), + p: p, pMinus1: new(big.Int).Sub(p, bigOne), } @@ -393,8 +393,8 @@ func init() { p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{ - g: new(big.Int).SetInt64(2), - p: p, + g: new(big.Int).SetInt64(2), + p: p, pMinus1: new(big.Int).Sub(p, bigOne), } diff --git a/vendor/golang.org/x/crypto/ssh/keys.go b/vendor/golang.org/x/crypto/ssh/keys.go index cf68532..7a8756a 100644 --- a/vendor/golang.org/x/crypto/ssh/keys.go +++ b/vendor/golang.org/x/crypto/ssh/keys.go @@ -756,6 +756,18 @@ func ParsePrivateKey(pemBytes []byte) (Signer, error) { return NewSignerFromKey(key) } +// ParsePrivateKeyWithPassphrase returns a Signer from a PEM encoded private +// key and passphrase. It supports the same keys as +// ParseRawPrivateKeyWithPassphrase. +func ParsePrivateKeyWithPassphrase(pemBytes, passPhrase []byte) (Signer, error) { + key, err := ParseRawPrivateKeyWithPassphrase(pemBytes, passPhrase) + if err != nil { + return nil, err + } + + return NewSignerFromKey(key) +} + // encryptedBlock tells whether a private key is // encrypted by examining its Proc-Type header // for a mention of ENCRYPTED @@ -790,6 +802,43 @@ func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) { } } +// ParseRawPrivateKeyWithPassphrase returns a private key decrypted with +// passphrase from a PEM encoded private key. If wrong passphrase, return +// x509.IncorrectPasswordError. +func ParseRawPrivateKeyWithPassphrase(pemBytes, passPhrase []byte) (interface{}, error) { + block, _ := pem.Decode(pemBytes) + if block == nil { + return nil, errors.New("ssh: no key found") + } + buf := block.Bytes + + if encryptedBlock(block) { + if x509.IsEncryptedPEMBlock(block) { + var err error + buf, err = x509.DecryptPEMBlock(block, passPhrase) + if err != nil { + if err == x509.IncorrectPasswordError { + return nil, err + } + return nil, fmt.Errorf("ssh: cannot decode encrypted private keys: %v", err) + } + } + } + + switch block.Type { + case "RSA PRIVATE KEY": + return x509.ParsePKCS1PrivateKey(buf) + case "EC PRIVATE KEY": + return x509.ParseECPrivateKey(buf) + case "DSA PRIVATE KEY": + return ParseDSAPrivateKey(buf) + case "OPENSSH PRIVATE KEY": + return parseOpenSSHPrivateKey(buf) + default: + return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type) + } +} + // ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as // specified by the OpenSSL DSA man page. func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go index 23b41d9..8a78b7c 100644 --- a/vendor/golang.org/x/crypto/ssh/server.go +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -14,23 +14,34 @@ import ( ) // The Permissions type holds fine-grained permissions that are -// specific to a user or a specific authentication method for a -// user. Permissions, except for "source-address", must be enforced in -// the server application layer, after successful authentication. The -// Permissions are passed on in ServerConn so a server implementation -// can honor them. +// specific to a user or a specific authentication method for a user. +// The Permissions value for a successful authentication attempt is +// available in ServerConn, so it can be used to pass information from +// the user-authentication phase to the application layer. type Permissions struct { - // Critical options restrict default permissions. Common - // restrictions are "source-address" and "force-command". If - // the server cannot enforce the restriction, or does not - // recognize it, the user should not authenticate. + // CriticalOptions indicate restrictions to the default + // permissions, and are typically used in conjunction with + // user certificates. The standard for SSH certificates + // defines "force-command" (only allow the given command to + // execute) and "source-address" (only allow connections from + // the given address). The SSH package currently only enforces + // the "source-address" critical option. It is up to server + // implementations to enforce other critical options, such as + // "force-command", by checking them after the SSH handshake + // is successful. In general, SSH servers should reject + // connections that specify critical options that are unknown + // or not supported. CriticalOptions map[string]string // Extensions are extra functionality that the server may - // offer on authenticated connections. Common extensions are - // "permit-agent-forwarding", "permit-X11-forwarding". Lack of - // support for an extension does not preclude authenticating a - // user. + // offer on authenticated connections. Lack of support for an + // extension does not preclude authenticating a user. Common + // extensions are "permit-agent-forwarding", + // "permit-X11-forwarding". The Go SSH library currently does + // not act on any extension, and it is up to server + // implementations to honor them. Extensions can be used to + // pass data from the authentication callbacks to the server + // application layer. Extensions map[string]string } @@ -55,9 +66,14 @@ type ServerConfig struct { // attempts to authenticate using a password. PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error) - // PublicKeyCallback, if non-nil, is called when a client attempts public - // key authentication. It must return true if the given public key is - // valid for the given user. For example, see CertChecker.Authenticate. + // PublicKeyCallback, if non-nil, is called when a client + // offers a public key for authentication. It must return a nil error + // if the given public key can be used to authenticate the + // given user. For example, see CertChecker.Authenticate. A + // call to this function does not guarantee that the key + // offered is in fact used to authenticate. To record any data + // depending on the public key, store it inside a + // Permissions.Extensions entry. PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error) // KeyboardInteractiveCallback, if non-nil, is called when @@ -272,12 +288,30 @@ func checkSourceAddress(addr net.Addr, sourceAddrs string) error { return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr) } +// ServerAuthError implements the error interface. It appends any authentication +// errors that may occur, and is returned if all of the authentication methods +// provided by the user failed to authenticate. +type ServerAuthError struct { + // Errors contains authentication errors returned by the authentication + // callback methods. + Errors []error +} + +func (l ServerAuthError) Error() string { + var errs []string + for _, err := range l.Errors { + errs = append(errs, err.Error()) + } + return "[" + strings.Join(errs, ", ") + "]" +} + func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) { sessionID := s.transport.getSessionID() var cache pubKeyCache var perms *Permissions authFailures := 0 + var authErrs []error userAuthLoop: for { @@ -296,6 +330,9 @@ userAuthLoop: var userAuthReq userAuthRequestMsg if packet, err := s.transport.readPacket(); err != nil { + if err == io.EOF { + return nil, &ServerAuthError{Errors: authErrs} + } return nil, err } else if err = Unmarshal(packet, &userAuthReq); err != nil { return nil, err @@ -432,6 +469,8 @@ userAuthLoop: authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method) } + authErrs = append(authErrs, authErr) + if config.AuthLogCallback != nil { config.AuthLogCallback(s, userAuthReq.Method, authErr) } diff --git a/vendor/golang.org/x/crypto/ssh/session.go b/vendor/golang.org/x/crypto/ssh/session.go index 17e2aa8..cc06e03 100644 --- a/vendor/golang.org/x/crypto/ssh/session.go +++ b/vendor/golang.org/x/crypto/ssh/session.go @@ -231,6 +231,26 @@ func (s *Session) RequestSubsystem(subsystem string) error { return err } +// RFC 4254 Section 6.7. +type ptyWindowChangeMsg struct { + Columns uint32 + Rows uint32 + Width uint32 + Height uint32 +} + +// WindowChange informs the remote host about a terminal window dimension change to h rows and w columns. +func (s *Session) WindowChange(h, w int) error { + req := ptyWindowChangeMsg{ + Columns: uint32(w), + Rows: uint32(h), + Width: uint32(w * 8), + Height: uint32(h * 8), + } + _, err := s.ch.SendRequest("window-change", false, Marshal(&req)) + return err +} + // RFC 4254 Section 6.9. type signalMsg struct { Signal string diff --git a/vendor/golang.org/x/net/idna/idna.go b/vendor/golang.org/x/net/idna/idna.go index e2273ac..1810100 100644 --- a/vendor/golang.org/x/net/idna/idna.go +++ b/vendor/golang.org/x/net/idna/idna.go @@ -67,6 +67,15 @@ func VerifyDNSLength(verify bool) Option { return func(o *options) { o.verifyDNSLength = verify } } +// RemoveLeadingDots removes leading label separators. Leading runes that map to +// dots, such as U+3002, are removed as well. +// +// This is the behavior suggested by the UTS #46 and is adopted by some +// browsers. +func RemoveLeadingDots(remove bool) Option { + return func(o *options) { o.removeLeadingDots = remove } +} + // ValidateLabels sets whether to check the mandatory label validation criteria // as defined in Section 5.4 of RFC 5891. This includes testing for correct use // of hyphens ('-'), normalization, validity of runes, and the context rules. @@ -133,14 +142,16 @@ func MapForLookup() Option { o.mapping = validateAndMap StrictDomainName(true)(o) ValidateLabels(true)(o) + RemoveLeadingDots(true)(o) } } type options struct { - transitional bool - useSTD3Rules bool - validateLabels bool - verifyDNSLength bool + transitional bool + useSTD3Rules bool + validateLabels bool + verifyDNSLength bool + removeLeadingDots bool trie *idnaTrie @@ -240,21 +251,23 @@ var ( punycode = &Profile{} lookup = &Profile{options{ - transitional: true, - useSTD3Rules: true, - validateLabels: true, - trie: trie, - fromPuny: validateFromPunycode, - mapping: validateAndMap, - bidirule: bidirule.ValidString, + transitional: true, + useSTD3Rules: true, + validateLabels: true, + removeLeadingDots: true, + trie: trie, + fromPuny: validateFromPunycode, + mapping: validateAndMap, + bidirule: bidirule.ValidString, }} display = &Profile{options{ - useSTD3Rules: true, - validateLabels: true, - trie: trie, - fromPuny: validateFromPunycode, - mapping: validateAndMap, - bidirule: bidirule.ValidString, + useSTD3Rules: true, + validateLabels: true, + removeLeadingDots: true, + trie: trie, + fromPuny: validateFromPunycode, + mapping: validateAndMap, + bidirule: bidirule.ValidString, }} registration = &Profile{options{ useSTD3Rules: true, @@ -293,7 +306,9 @@ func (p *Profile) process(s string, toASCII bool) (string, error) { s, err = p.mapping(p, s) } // Remove leading empty labels. - for ; len(s) > 0 && s[0] == '.'; s = s[1:] { + if p.removeLeadingDots { + for ; len(s) > 0 && s[0] == '.'; s = s[1:] { + } } // It seems like we should only create this error on ToASCII, but the // UTS 46 conformance tests suggests we should always check this. @@ -373,23 +388,20 @@ func validateRegistration(p *Profile, s string) (string, error) { if !norm.NFC.IsNormalString(s) { return s, &labelError{s, "V1"} } - var err error for i := 0; i < len(s); { v, sz := trie.lookupString(s[i:]) - i += sz // Copy bytes not copied so far. switch p.simplify(info(v).category()) { // TODO: handle the NV8 defined in the Unicode idna data set to allow // for strict conformance to IDNA2008. case valid, deviation: case disallowed, mapped, unknown, ignored: - if err == nil { - r, _ := utf8.DecodeRuneInString(s[i:]) - err = runeError(r) - } + r, _ := utf8.DecodeRuneInString(s[i:]) + return s, runeError(r) } + i += sz } - return s, err + return s, nil } func validateAndMap(p *Profile, s string) (string, error) { @@ -408,7 +420,7 @@ func validateAndMap(p *Profile, s string) (string, error) { continue case disallowed: if err == nil { - r, _ := utf8.DecodeRuneInString(s[i:]) + r, _ := utf8.DecodeRuneInString(s[start:]) err = runeError(r) } continue diff --git a/vendor/golang.org/x/text/width/kind_string.go b/vendor/golang.org/x/text/width/kind_string.go index ab4fee5..49bfbf7 100644 --- a/vendor/golang.org/x/text/width/kind_string.go +++ b/vendor/golang.org/x/text/width/kind_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=Kind"; DO NOT EDIT +// Code generated by "stringer -type=Kind"; DO NOT EDIT. package width diff --git a/vendor/leb.io/hashland/LICENSE b/vendor/leb.io/hashland/LICENSE new file mode 100644 index 0000000..ee9d3fa --- /dev/null +++ b/vendor/leb.io/hashland/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 Lawrence E. Bakst + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/leb.io/hashland/keccakpg/keccak.go b/vendor/leb.io/hashland/keccakpg/keccak.go new file mode 100644 index 0000000..e97a49a --- /dev/null +++ b/vendor/leb.io/hashland/keccakpg/keccak.go @@ -0,0 +1,224 @@ +// Package keccak implements the Keccak (SHA-3) hash algorithm. +// http://keccak.noekeon.org. +package keccakpg + +import ( + _ "fmt" + "hash" +) + +const stdRounds = 24 + +var roundConstants = []uint64{ + 0x0000000000000001, 0x0000000000008082, + 0x800000000000808A, 0x8000000080008000, + 0x000000000000808B, 0x0000000080000001, + 0x8000000080008081, 0x8000000000008009, + 0x000000000000008A, 0x0000000000000088, + 0x0000000080008009, 0x000000008000000A, + 0x000000008000808B, 0x800000000000008B, + 0x8000000000008089, 0x8000000000008003, + 0x8000000000008002, 0x8000000000000080, + 0x000000000000800A, 0x800000008000000A, + 0x8000000080008081, 0x8000000000008080, + 0x0000000080000001, 0x8000000080008008, +} + +var rotationConstants = [24]uint{ + 1, 3, 6, 10, 15, 21, 28, 36, + 45, 55, 2, 14, 27, 41, 56, 8, + 25, 43, 62, 18, 39, 61, 20, 44, +} + +var piLane = [24]uint{ + 10, 7, 11, 17, 18, 3, 5, 16, + 8, 21, 24, 4, 15, 23, 19, 13, + 12, 2, 20, 14, 22, 9, 6, 1, +} + +type keccak struct { + S [25]uint64 + size int + blockSize int + rounds int + buf []byte +} + +func newKeccak(bitlen, rounds int) hash.Hash { + var h keccak + h.size = bitlen / 8 + h.blockSize = (200 - 2*h.size) + h.rounds = rounds + if rounds != stdRounds { + //fmt.Printf("keccak: warning non standard number of rounds %d vs %d\n", rounds, stdRounds) + } + return &h +} + +func NewCustom(bits, rounds int) hash.Hash { + return newKeccak(bits, rounds) +} + +func New160() hash.Hash { + return newKeccak(160, stdRounds) +} + +func New224() hash.Hash { + return newKeccak(224, stdRounds) +} + +func New256() hash.Hash { + return newKeccak(256, stdRounds) +} + +func New384() hash.Hash { + return newKeccak(384, stdRounds) +} + +func New512() hash.Hash { + return newKeccak(512, stdRounds) +} + +func (k *keccak) Write(b []byte) (int, error) { + n := len(b) + + if len(k.buf) > 0 { + x := k.blockSize - len(k.buf) + if x > len(b) { + x = len(b) + } + k.buf = append(k.buf, b[:x]...) + b = b[x:] + + if len(k.buf) < k.blockSize { + return n, nil + } + + k.f(k.buf) + k.buf = nil + } + + for len(b) >= k.blockSize { + k.f(b[:k.blockSize]) + b = b[k.blockSize:] + } + + k.buf = b + + return n, nil +} + +func (k0 *keccak) Sum(b []byte) []byte { + + k := *k0 + + last := k.pad(k.buf) + k.f(last) + + buf := make([]byte, len(k.S)*8) + for i := range k.S { + putUint64le(buf[i*8:], k.S[i]) + } + return append(b, buf[:k.size]...) +} + +func (k *keccak) Reset() { + for i := range k.S { + k.S[i] = 0 + } + k.buf = nil +} + +func (k *keccak) Size() int { + return k.size +} + +func (k *keccak) BlockSize() int { + return k.blockSize +} + +func rotl64(x uint64, n uint) uint64 { + return (x << n) | (x >> (64 - n)) +} + +func (k *keccak) f(block []byte) { + + if len(block) != k.blockSize { + panic("f() called with invalid block size") + } + + for i := 0; i < k.blockSize/8; i++ { + k.S[i] ^= uint64le(block[i*8:]) + } + + for r := 0; r < k.rounds; r++ { + var bc [5]uint64 + + // theta + for i := range bc { + bc[i] = k.S[i] ^ k.S[5+i] ^ k.S[10+i] ^ k.S[15+i] ^ k.S[20+i] + } + for i := range bc { + t := bc[(i+4)%5] ^ rotl64(bc[(i+1)%5], 1) + for j := 0; j < len(k.S); j += 5 { + k.S[i+j] ^= t + } + } + + // rho phi + temp := k.S[1] + for i := range piLane { + j := piLane[i] + temp2 := k.S[j] + k.S[j] = rotl64(temp, rotationConstants[i]) + temp = temp2 + } + + // chi + for j := 0; j < len(k.S); j += 5 { + for i := range bc { + bc[i] = k.S[j+i] + } + for i := range bc { + k.S[j+i] ^= (^bc[(i+1)%5]) & bc[(i+2)%5] + } + } + + // iota + k.S[0] ^= roundConstants[r] + } +} + +func (k *keccak) pad(block []byte) []byte { + + padded := make([]byte, k.blockSize) + + copy(padded, k.buf) + padded[len(k.buf)] = 0x01 + padded[len(padded)-1] |= 0x80 + + return padded +} + +func uint64le(v []byte) uint64 { + return uint64(v[0]) | + uint64(v[1])<<8 | + uint64(v[2])<<16 | + uint64(v[3])<<24 | + uint64(v[4])<<32 | + uint64(v[5])<<40 | + uint64(v[6])<<48 | + uint64(v[7])<<56 + +} + +func putUint64le(v []byte, x uint64) { + v[0] = byte(x) + v[1] = byte(x >> 8) + v[2] = byte(x >> 16) + v[3] = byte(x >> 24) + v[4] = byte(x >> 32) + v[5] = byte(x >> 40) + v[6] = byte(x >> 48) + v[7] = byte(x >> 56) +}