summaryrefslogtreecommitdiffstats
path: root/vendor/github.com
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2016-05-12 15:08:58 -0400
committerChristopher Speller <crspeller@gmail.com>2016-05-12 16:37:29 -0400
commit84d2482ddbff9564c9ad75b2d30af66e3ddfd44d (patch)
tree8bfa567d2b6381f4a996ada2deff8a16aa85a3ac /vendor/github.com
parentd1efb66ad7b017f0fbfe6f0c20843b30f396e504 (diff)
downloadchat-84d2482ddbff9564c9ad75b2d30af66e3ddfd44d.tar.gz
chat-84d2482ddbff9564c9ad75b2d30af66e3ddfd44d.tar.bz2
chat-84d2482ddbff9564c9ad75b2d30af66e3ddfd44d.zip
Updating go depencancies. Switching to go1.6 vendoring (#2949)
Diffstat (limited to 'vendor/github.com')
-rw-r--r--vendor/github.com/NYTimes/gziphandler/LICENSE.md13
-rw-r--r--vendor/github.com/NYTimes/gziphandler/README.md52
-rw-r--r--vendor/github.com/NYTimes/gziphandler/gzip.go144
-rw-r--r--vendor/github.com/alecthomas/log4go/.gitignore2
-rw-r--r--vendor/github.com/alecthomas/log4go/LICENSE13
-rw-r--r--vendor/github.com/alecthomas/log4go/README14
-rw-r--r--vendor/github.com/alecthomas/log4go/config.go288
-rw-r--r--vendor/github.com/alecthomas/log4go/filelog.go264
-rw-r--r--vendor/github.com/alecthomas/log4go/log4go.go484
-rw-r--r--vendor/github.com/alecthomas/log4go/pattlog.go126
-rw-r--r--vendor/github.com/alecthomas/log4go/socklog.go57
-rw-r--r--vendor/github.com/alecthomas/log4go/termlog.go49
-rw-r--r--vendor/github.com/alecthomas/log4go/wrapper.go278
-rw-r--r--vendor/github.com/braintree/manners/LICENSE19
-rw-r--r--vendor/github.com/braintree/manners/README.md36
-rw-r--r--vendor/github.com/braintree/manners/interfaces.go7
-rw-r--r--vendor/github.com/braintree/manners/server.go292
-rw-r--r--vendor/github.com/braintree/manners/static.go47
-rw-r--r--vendor/github.com/cloudfoundry/jibber_jabber/.travis.yml11
-rw-r--r--vendor/github.com/cloudfoundry/jibber_jabber/LICENSE201
-rw-r--r--vendor/github.com/cloudfoundry/jibber_jabber/README.md44
-rw-r--r--vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber.go22
-rw-r--r--vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix.go57
-rw-r--r--vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows.go114
-rw-r--r--vendor/github.com/dgryski/dgoogauth/.travis.yml1
-rw-r--r--vendor/github.com/dgryski/dgoogauth/README.md15
-rw-r--r--vendor/github.com/dgryski/dgoogauth/googauth.go199
-rw-r--r--vendor/github.com/disintegration/imaging/.travis.yml19
-rw-r--r--vendor/github.com/disintegration/imaging/LICENSE21
-rw-r--r--vendor/github.com/disintegration/imaging/README.md198
-rw-r--r--vendor/github.com/disintegration/imaging/adjust.go200
-rw-r--r--vendor/github.com/disintegration/imaging/effects.go187
-rw-r--r--vendor/github.com/disintegration/imaging/helpers.go400
-rw-r--r--vendor/github.com/disintegration/imaging/resize.go583
-rw-r--r--vendor/github.com/disintegration/imaging/tools.go201
-rw-r--r--vendor/github.com/disintegration/imaging/transform.go201
-rw-r--r--vendor/github.com/disintegration/imaging/utils.go77
-rw-r--r--vendor/github.com/garyburd/redigo/LICENSE175
-rw-r--r--vendor/github.com/garyburd/redigo/internal/commandinfo.go54
-rw-r--r--vendor/github.com/garyburd/redigo/redis/conn.go570
-rw-r--r--vendor/github.com/garyburd/redigo/redis/doc.go169
-rw-r--r--vendor/github.com/garyburd/redigo/redis/log.go117
-rw-r--r--vendor/github.com/garyburd/redigo/redis/pool.go393
-rw-r--r--vendor/github.com/garyburd/redigo/redis/pubsub.go144
-rw-r--r--vendor/github.com/garyburd/redigo/redis/redis.go44
-rw-r--r--vendor/github.com/garyburd/redigo/redis/reply.go393
-rw-r--r--vendor/github.com/garyburd/redigo/redis/scan.go555
-rw-r--r--vendor/github.com/garyburd/redigo/redis/script.go86
-rw-r--r--vendor/github.com/go-gorp/gorp/.gitignore9
-rw-r--r--vendor/github.com/go-gorp/gorp/.travis.yml29
-rw-r--r--vendor/github.com/go-gorp/gorp/LICENSE22
-rw-r--r--vendor/github.com/go-gorp/gorp/README.md745
-rw-r--r--vendor/github.com/go-gorp/gorp/column.go83
-rw-r--r--vendor/github.com/go-gorp/gorp/db.go623
-rw-r--r--vendor/github.com/go-gorp/gorp/dialect.go111
-rw-r--r--vendor/github.com/go-gorp/gorp/dialect_mysql.go171
-rw-r--r--vendor/github.com/go-gorp/gorp/dialect_oracle.go146
-rw-r--r--vendor/github.com/go-gorp/gorp/dialect_postgres.go147
-rw-r--r--vendor/github.com/go-gorp/gorp/dialect_sqlite.go119
-rw-r--r--vendor/github.com/go-gorp/gorp/dialect_sqlserver.go152
-rw-r--r--vendor/github.com/go-gorp/gorp/errors.go38
-rw-r--r--vendor/github.com/go-gorp/gorp/gorp.go558
-rw-r--r--vendor/github.com/go-gorp/gorp/hooks.go49
-rw-r--r--vendor/github.com/go-gorp/gorp/index.go56
-rw-r--r--vendor/github.com/go-gorp/gorp/lockerror.go63
-rw-r--r--vendor/github.com/go-gorp/gorp/logging.go44
-rw-r--r--vendor/github.com/go-gorp/gorp/nulltypes.go58
-rw-r--r--vendor/github.com/go-gorp/gorp/select.go351
-rw-r--r--vendor/github.com/go-gorp/gorp/table.go247
-rw-r--r--vendor/github.com/go-gorp/gorp/table_bindings.go312
-rw-r--r--vendor/github.com/go-gorp/gorp/test_all.sh41
-rw-r--r--vendor/github.com/go-gorp/gorp/transaction.go193
-rw-r--r--vendor/github.com/go-ldap/ldap/.gitignore0
-rw-r--r--vendor/github.com/go-ldap/ldap/.travis.yml15
-rw-r--r--vendor/github.com/go-ldap/ldap/LICENSE27
-rw-r--r--vendor/github.com/go-ldap/ldap/README.md55
-rw-r--r--vendor/github.com/go-ldap/ldap/add.go108
-rw-r--r--vendor/github.com/go-ldap/ldap/bind.go145
-rw-r--r--vendor/github.com/go-ldap/ldap/client.go27
-rw-r--r--vendor/github.com/go-ldap/ldap/compare.go89
-rw-r--r--vendor/github.com/go-ldap/ldap/conn.go424
-rw-r--r--vendor/github.com/go-ldap/ldap/control.go332
-rw-r--r--vendor/github.com/go-ldap/ldap/debug.go24
-rw-r--r--vendor/github.com/go-ldap/ldap/del.go83
-rw-r--r--vendor/github.com/go-ldap/ldap/dn.go155
-rw-r--r--vendor/github.com/go-ldap/ldap/doc.go4
-rw-r--r--vendor/github.com/go-ldap/ldap/error.go142
-rw-r--r--vendor/github.com/go-ldap/ldap/filter.go456
-rw-r--r--vendor/github.com/go-ldap/ldap/ldap.go286
-rw-r--r--vendor/github.com/go-ldap/ldap/modify.go160
-rw-r--r--vendor/github.com/go-ldap/ldap/passwdmodify.go144
-rw-r--r--vendor/github.com/go-ldap/ldap/search.go426
-rw-r--r--vendor/github.com/go-sql-driver/mysql/.gitignore8
-rw-r--r--vendor/github.com/go-sql-driver/mysql/.travis.yml12
-rw-r--r--vendor/github.com/go-sql-driver/mysql/AUTHORS52
-rw-r--r--vendor/github.com/go-sql-driver/mysql/CHANGELOG.md103
-rw-r--r--vendor/github.com/go-sql-driver/mysql/CONTRIBUTING.md23
-rw-r--r--vendor/github.com/go-sql-driver/mysql/ISSUE_TEMPLATE.md21
-rw-r--r--vendor/github.com/go-sql-driver/mysql/LICENSE373
-rw-r--r--vendor/github.com/go-sql-driver/mysql/PULL_REQUEST_TEMPLATE.md9
-rw-r--r--vendor/github.com/go-sql-driver/mysql/README.md420
-rw-r--r--vendor/github.com/go-sql-driver/mysql/appengine.go19
-rw-r--r--vendor/github.com/go-sql-driver/mysql/buffer.go147
-rw-r--r--vendor/github.com/go-sql-driver/mysql/collations.go250
-rw-r--r--vendor/github.com/go-sql-driver/mysql/connection.go372
-rw-r--r--vendor/github.com/go-sql-driver/mysql/const.go163
-rw-r--r--vendor/github.com/go-sql-driver/mysql/driver.go167
-rw-r--r--vendor/github.com/go-sql-driver/mysql/dsn.go513
-rw-r--r--vendor/github.com/go-sql-driver/mysql/errors.go131
-rw-r--r--vendor/github.com/go-sql-driver/mysql/infile.go181
-rw-r--r--vendor/github.com/go-sql-driver/mysql/packets.go1243
-rw-r--r--vendor/github.com/go-sql-driver/mysql/result.go22
-rw-r--r--vendor/github.com/go-sql-driver/mysql/rows.go112
-rw-r--r--vendor/github.com/go-sql-driver/mysql/statement.go150
-rw-r--r--vendor/github.com/go-sql-driver/mysql/transaction.go31
-rw-r--r--vendor/github.com/go-sql-driver/mysql/utils.go740
-rw-r--r--vendor/github.com/goamz/goamz/LICENSE185
-rw-r--r--vendor/github.com/goamz/goamz/aws/attempt.go74
-rw-r--r--vendor/github.com/goamz/goamz/aws/aws.go432
-rw-r--r--vendor/github.com/goamz/goamz/aws/client.go124
-rw-r--r--vendor/github.com/goamz/goamz/aws/regions.go254
-rw-r--r--vendor/github.com/goamz/goamz/aws/sign.go357
-rw-r--r--vendor/github.com/goamz/goamz/s3/multi.go439
-rw-r--r--vendor/github.com/goamz/goamz/s3/s3.go1161
-rw-r--r--vendor/github.com/goamz/goamz/s3/sign.go141
-rw-r--r--vendor/github.com/golang/freetype/AUTHORS20
-rw-r--r--vendor/github.com/golang/freetype/CONTRIBUTORS38
-rw-r--r--vendor/github.com/golang/freetype/LICENSE12
-rw-r--r--vendor/github.com/golang/freetype/README21
-rw-r--r--vendor/github.com/golang/freetype/freetype.go341
-rw-r--r--vendor/github.com/golang/freetype/raster/geom.go245
-rw-r--r--vendor/github.com/golang/freetype/raster/paint.go287
-rw-r--r--vendor/github.com/golang/freetype/raster/raster.go601
-rw-r--r--vendor/github.com/golang/freetype/raster/stroke.go483
-rw-r--r--vendor/github.com/golang/freetype/truetype/face.go507
-rw-r--r--vendor/github.com/golang/freetype/truetype/glyph.go517
-rw-r--r--vendor/github.com/golang/freetype/truetype/hint.go1763
-rw-r--r--vendor/github.com/golang/freetype/truetype/opcodes.go289
-rw-r--r--vendor/github.com/golang/freetype/truetype/truetype.go643
-rw-r--r--vendor/github.com/golang/groupcache/LICENSE191
-rw-r--r--vendor/github.com/golang/groupcache/lru/lru.go121
-rw-r--r--vendor/github.com/gorilla/context/.travis.yml18
-rw-r--r--vendor/github.com/gorilla/context/LICENSE27
-rw-r--r--vendor/github.com/gorilla/context/README.md7
-rw-r--r--vendor/github.com/gorilla/context/context.go143
-rw-r--r--vendor/github.com/gorilla/context/doc.go82
-rw-r--r--vendor/github.com/gorilla/handlers/.travis.yml17
-rw-r--r--vendor/github.com/gorilla/handlers/LICENSE22
-rw-r--r--vendor/github.com/gorilla/handlers/README.md53
-rw-r--r--vendor/github.com/gorilla/handlers/canonical.go74
-rw-r--r--vendor/github.com/gorilla/handlers/compress.go145
-rw-r--r--vendor/github.com/gorilla/handlers/cors.go317
-rw-r--r--vendor/github.com/gorilla/handlers/doc.go9
-rw-r--r--vendor/github.com/gorilla/handlers/handlers.go403
-rw-r--r--vendor/github.com/gorilla/handlers/proxy_headers.go113
-rw-r--r--vendor/github.com/gorilla/handlers/recovery.go86
-rw-r--r--vendor/github.com/gorilla/mux/.travis.yml17
-rw-r--r--vendor/github.com/gorilla/mux/LICENSE27
-rw-r--r--vendor/github.com/gorilla/mux/README.md242
-rw-r--r--vendor/github.com/gorilla/mux/doc.go206
-rw-r--r--vendor/github.com/gorilla/mux/mux.go499
-rw-r--r--vendor/github.com/gorilla/mux/regexp.go312
-rw-r--r--vendor/github.com/gorilla/mux/route.go634
-rw-r--r--vendor/github.com/gorilla/websocket/.gitignore22
-rw-r--r--vendor/github.com/gorilla/websocket/.travis.yml17
-rw-r--r--vendor/github.com/gorilla/websocket/AUTHORS8
-rw-r--r--vendor/github.com/gorilla/websocket/LICENSE22
-rw-r--r--vendor/github.com/gorilla/websocket/README.md61
-rw-r--r--vendor/github.com/gorilla/websocket/client.go375
-rw-r--r--vendor/github.com/gorilla/websocket/conn.go950
-rw-r--r--vendor/github.com/gorilla/websocket/doc.go152
-rw-r--r--vendor/github.com/gorilla/websocket/json.go55
-rw-r--r--vendor/github.com/gorilla/websocket/server.go260
-rw-r--r--vendor/github.com/gorilla/websocket/util.go44
-rw-r--r--vendor/github.com/lib/pq/.gitignore4
-rw-r--r--vendor/github.com/lib/pq/.travis.yml67
-rw-r--r--vendor/github.com/lib/pq/CONTRIBUTING.md29
-rw-r--r--vendor/github.com/lib/pq/LICENSE.md8
-rw-r--r--vendor/github.com/lib/pq/README.md105
-rw-r--r--vendor/github.com/lib/pq/buf.go91
-rw-r--r--vendor/github.com/lib/pq/conn.go1847
-rw-r--r--vendor/github.com/lib/pq/copy.go267
-rw-r--r--vendor/github.com/lib/pq/doc.go212
-rw-r--r--vendor/github.com/lib/pq/encode.go571
-rw-r--r--vendor/github.com/lib/pq/error.go508
-rw-r--r--vendor/github.com/lib/pq/notify.go766
-rw-r--r--vendor/github.com/lib/pq/oid/doc.go6
-rw-r--r--vendor/github.com/lib/pq/oid/gen.go74
-rw-r--r--vendor/github.com/lib/pq/oid/types.go161
-rw-r--r--vendor/github.com/lib/pq/url.go76
-rw-r--r--vendor/github.com/lib/pq/user_posix.go24
-rw-r--r--vendor/github.com/lib/pq/user_windows.go27
-rw-r--r--vendor/github.com/mattermost/rsc/LICENSE27
-rw-r--r--vendor/github.com/mattermost/rsc/gf256/Makefile8
-rw-r--r--vendor/github.com/mattermost/rsc/gf256/gf256.go241
-rw-r--r--vendor/github.com/mattermost/rsc/qr/Makefile4
-rw-r--r--vendor/github.com/mattermost/rsc/qr/coding/Makefile7
-rw-r--r--vendor/github.com/mattermost/rsc/qr/coding/gen.go149
-rw-r--r--vendor/github.com/mattermost/rsc/qr/coding/qr.go815
-rw-r--r--vendor/github.com/mattermost/rsc/qr/png.go400
-rw-r--r--vendor/github.com/mattermost/rsc/qr/qr.go116
l---------vendor/github.com/mattermost/rsc/tmp/LICENSE1
-rw-r--r--vendor/github.com/mssola/user_agent/.travis.yml12
-rw-r--r--vendor/github.com/mssola/user_agent/LICENSE20
-rw-r--r--vendor/github.com/mssola/user_agent/README.md51
-rw-r--r--vendor/github.com/mssola/user_agent/bot.go123
-rw-r--r--vendor/github.com/mssola/user_agent/browser.go132
-rw-r--r--vendor/github.com/mssola/user_agent/operating_systems.go281
-rw-r--r--vendor/github.com/mssola/user_agent/user_agent.go174
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/LICENSE19
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle.go315
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/i18n.go152
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/language.go99
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/operands.go119
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/plural.go40
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec.go74
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_gen.go567
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation.go78
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/translation/single_translation.go57
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go61
-rw-r--r--vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go83
-rw-r--r--vendor/github.com/pborman/uuid/.travis.yml10
-rw-r--r--vendor/github.com/pborman/uuid/CONTRIBUTING.md10
-rw-r--r--vendor/github.com/pborman/uuid/CONTRIBUTORS1
-rw-r--r--vendor/github.com/pborman/uuid/LICENSE27
-rw-r--r--vendor/github.com/pborman/uuid/README.md13
-rw-r--r--vendor/github.com/pborman/uuid/dce.go84
-rw-r--r--vendor/github.com/pborman/uuid/doc.go8
-rw-r--r--vendor/github.com/pborman/uuid/hash.go53
-rw-r--r--vendor/github.com/pborman/uuid/json.go34
-rw-r--r--vendor/github.com/pborman/uuid/node.go117
-rw-r--r--vendor/github.com/pborman/uuid/sql.go66
-rw-r--r--vendor/github.com/pborman/uuid/time.go132
-rw-r--r--vendor/github.com/pborman/uuid/util.go43
-rw-r--r--vendor/github.com/pborman/uuid/uuid.go201
-rw-r--r--vendor/github.com/pborman/uuid/version1.go41
-rw-r--r--vendor/github.com/pborman/uuid/version4.go25
-rw-r--r--vendor/github.com/rwcarlsen/goexif/LICENSE24
-rw-r--r--vendor/github.com/rwcarlsen/goexif/exif/README.md4
-rw-r--r--vendor/github.com/rwcarlsen/goexif/exif/exif.go619
-rw-r--r--vendor/github.com/rwcarlsen/goexif/exif/fields.go293
-rw-r--r--vendor/github.com/rwcarlsen/goexif/exif/regen_regress.go79
-rw-r--r--vendor/github.com/rwcarlsen/goexif/exif/sample1.jpgbin0 -> 80603 bytes
-rw-r--r--vendor/github.com/rwcarlsen/goexif/tiff/sample1.tifbin0 -> 18382 bytes
-rw-r--r--vendor/github.com/rwcarlsen/goexif/tiff/tag.go438
-rw-r--r--vendor/github.com/rwcarlsen/goexif/tiff/tiff.go153
-rw-r--r--vendor/github.com/vaughan0/go-ini/LICENSE14
-rw-r--r--vendor/github.com/vaughan0/go-ini/README.md70
-rw-r--r--vendor/github.com/vaughan0/go-ini/ini.go123
-rw-r--r--vendor/github.com/vaughan0/go-ini/test.ini2
250 files changed, 46784 insertions, 0 deletions
diff --git a/vendor/github.com/NYTimes/gziphandler/LICENSE.md b/vendor/github.com/NYTimes/gziphandler/LICENSE.md
new file mode 100644
index 000000000..b7e2ecb63
--- /dev/null
+++ b/vendor/github.com/NYTimes/gziphandler/LICENSE.md
@@ -0,0 +1,13 @@
+Copyright (c) 2015 The New York Times Company
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this library except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/vendor/github.com/NYTimes/gziphandler/README.md b/vendor/github.com/NYTimes/gziphandler/README.md
new file mode 100644
index 000000000..b1d55e26e
--- /dev/null
+++ b/vendor/github.com/NYTimes/gziphandler/README.md
@@ -0,0 +1,52 @@
+Gzip Handler
+============
+
+This is a tiny Go package which wraps HTTP handlers to transparently gzip the
+response body, for clients which support it. Although it's usually simpler to
+leave that to a reverse proxy (like nginx or Varnish), this package is useful
+when that's undesirable.
+
+
+## Usage
+
+Call `GzipHandler` with any handler (an object which implements the
+`http.Handler` interface), and it'll return a new handler which gzips the
+response. For example:
+
+```go
+package main
+
+import (
+ "io"
+ "net/http"
+ "github.com/NYTimes/gziphandler"
+)
+
+func main() {
+ withoutGz := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/plain")
+ io.WriteString(w, "Hello, World")
+ })
+
+ withGz := gziphandler.GzipHandler(withoutGz)
+
+ http.Handle("/", withGz)
+ http.ListenAndServe("0.0.0.0:8000", nil)
+}
+```
+
+
+## Documentation
+
+The docs can be found at [godoc.org] [docs], as usual.
+
+
+## License
+
+[Apache 2.0] [license].
+
+
+
+
+[docs]: https://godoc.org/github.com/nytimes/gziphandler
+[license]: https://github.com/nytimes/gziphandler/blob/master/LICENSE.md
diff --git a/vendor/github.com/NYTimes/gziphandler/gzip.go b/vendor/github.com/NYTimes/gziphandler/gzip.go
new file mode 100644
index 000000000..ee1526f42
--- /dev/null
+++ b/vendor/github.com/NYTimes/gziphandler/gzip.go
@@ -0,0 +1,144 @@
+package gziphandler
+
+import (
+ "compress/gzip"
+ "fmt"
+ "net/http"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+const (
+ vary = "Vary"
+ acceptEncoding = "Accept-Encoding"
+ contentEncoding = "Content-Encoding"
+)
+
+type codings map[string]float64
+
+// The default qvalue to assign to an encoding if no explicit qvalue is set.
+// This is actually kind of ambiguous in RFC 2616, so hopefully it's correct.
+// The examples seem to indicate that it is.
+const DEFAULT_QVALUE = 1.0
+
+var gzipWriterPool = sync.Pool{
+ New: func() interface{} { return gzip.NewWriter(nil) },
+}
+
+// GzipResponseWriter provides an http.ResponseWriter interface, which gzips
+// bytes before writing them to the underlying response. This doesn't set the
+// Content-Encoding header, nor close the writers, so don't forget to do that.
+type GzipResponseWriter struct {
+ gw *gzip.Writer
+ http.ResponseWriter
+}
+
+// Write appends data to the gzip writer.
+func (w GzipResponseWriter) Write(b []byte) (int, error) {
+ if _, ok := w.Header()["Content-Type"]; !ok {
+ // If content type is not set, infer it from the uncompressed body.
+ w.Header().Set("Content-Type", http.DetectContentType(b))
+ }
+ return w.gw.Write(b)
+}
+
+// Flush flushes the underlying *gzip.Writer and then the underlying
+// http.ResponseWriter if it is an http.Flusher. This makes GzipResponseWriter
+// an http.Flusher.
+func (w GzipResponseWriter) Flush() {
+ w.gw.Flush()
+ if fw, ok := w.ResponseWriter.(http.Flusher); ok {
+ fw.Flush()
+ }
+}
+
+// GzipHandler wraps an HTTP handler, to transparently gzip the response body if
+// the client supports it (via the Accept-Encoding header).
+func GzipHandler(h http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Add(vary, acceptEncoding)
+
+ if acceptsGzip(r) {
+ // Bytes written during ServeHTTP are redirected to this gzip writer
+ // before being written to the underlying response.
+ gzw := gzipWriterPool.Get().(*gzip.Writer)
+ defer gzipWriterPool.Put(gzw)
+ gzw.Reset(w)
+ defer gzw.Close()
+
+ w.Header().Set(contentEncoding, "gzip")
+ h.ServeHTTP(GzipResponseWriter{gzw, w}, r)
+ } else {
+ h.ServeHTTP(w, r)
+ }
+ })
+}
+
+// acceptsGzip returns true if the given HTTP request indicates that it will
+// accept a gzippped response.
+func acceptsGzip(r *http.Request) bool {
+ acceptedEncodings, _ := parseEncodings(r.Header.Get(acceptEncoding))
+ return acceptedEncodings["gzip"] > 0.0
+}
+
+// parseEncodings attempts to parse a list of codings, per RFC 2616, as might
+// appear in an Accept-Encoding header. It returns a map of content-codings to
+// quality values, and an error containing the errors encounted. It's probably
+// safe to ignore those, because silently ignoring errors is how the internet
+// works.
+//
+// See: http://tools.ietf.org/html/rfc2616#section-14.3
+func parseEncodings(s string) (codings, error) {
+ c := make(codings)
+ e := make([]string, 0)
+
+ for _, ss := range strings.Split(s, ",") {
+ coding, qvalue, err := parseCoding(ss)
+
+ if err != nil {
+ e = append(e, err.Error())
+
+ } else {
+ c[coding] = qvalue
+ }
+ }
+
+ // TODO (adammck): Use a proper multi-error struct, so the individual errors
+ // can be extracted if anyone cares.
+ if len(e) > 0 {
+ return c, fmt.Errorf("errors while parsing encodings: %s", strings.Join(e, ", "))
+ }
+
+ return c, nil
+}
+
+// parseCoding parses a single conding (content-coding with an optional qvalue),
+// as might appear in an Accept-Encoding header. It attempts to forgive minor
+// formatting errors.
+func parseCoding(s string) (coding string, qvalue float64, err error) {
+ for n, part := range strings.Split(s, ";") {
+ part = strings.TrimSpace(part)
+ qvalue = DEFAULT_QVALUE
+
+ if n == 0 {
+ coding = strings.ToLower(part)
+
+ } else if strings.HasPrefix(part, "q=") {
+ qvalue, err = strconv.ParseFloat(strings.TrimPrefix(part, "q="), 64)
+
+ if qvalue < 0.0 {
+ qvalue = 0.0
+
+ } else if qvalue > 1.0 {
+ qvalue = 1.0
+ }
+ }
+ }
+
+ if coding == "" {
+ err = fmt.Errorf("empty content-coding")
+ }
+
+ return
+}
diff --git a/vendor/github.com/alecthomas/log4go/.gitignore b/vendor/github.com/alecthomas/log4go/.gitignore
new file mode 100644
index 000000000..f6207cd8a
--- /dev/null
+++ b/vendor/github.com/alecthomas/log4go/.gitignore
@@ -0,0 +1,2 @@
+*.sw[op]
+.DS_Store
diff --git a/vendor/github.com/alecthomas/log4go/LICENSE b/vendor/github.com/alecthomas/log4go/LICENSE
new file mode 100644
index 000000000..7093402bf
--- /dev/null
+++ b/vendor/github.com/alecthomas/log4go/LICENSE
@@ -0,0 +1,13 @@
+Copyright (c) 2010, Kyle Lemons <kyle@kylelemons.net>. 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.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/alecthomas/log4go/README b/vendor/github.com/alecthomas/log4go/README
new file mode 100644
index 000000000..3361567f7
--- /dev/null
+++ b/vendor/github.com/alecthomas/log4go/README
@@ -0,0 +1,14 @@
+# This is an unmaintained fork, left only so it doesn't break imports.
+
+Please see http://log4go.googlecode.com/
+
+Installation:
+- Run `goinstall log4go.googlecode.com/hg`
+
+Usage:
+- Add the following import:
+import l4g "log4go.googlecode.com/hg"
+
+Acknowledgements:
+- pomack
+ For providing awesome patches to bring log4go up to the latest Go spec
diff --git a/vendor/github.com/alecthomas/log4go/config.go b/vendor/github.com/alecthomas/log4go/config.go
new file mode 100644
index 000000000..577c3eb2f
--- /dev/null
+++ b/vendor/github.com/alecthomas/log4go/config.go
@@ -0,0 +1,288 @@
+// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
+
+package log4go
+
+import (
+ "encoding/xml"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strconv"
+ "strings"
+)
+
+type xmlProperty struct {
+ Name string `xml:"name,attr"`
+ Value string `xml:",chardata"`
+}
+
+type xmlFilter struct {
+ Enabled string `xml:"enabled,attr"`
+ Tag string `xml:"tag"`
+ Level string `xml:"level"`
+ Type string `xml:"type"`
+ Property []xmlProperty `xml:"property"`
+}
+
+type xmlLoggerConfig struct {
+ Filter []xmlFilter `xml:"filter"`
+}
+
+// Load XML configuration; see examples/example.xml for documentation
+func (log Logger) LoadConfiguration(filename string) {
+ log.Close()
+
+ // Open the configuration file
+ fd, err := os.Open(filename)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not open %q for reading: %s\n", filename, err)
+ os.Exit(1)
+ }
+
+ contents, err := ioutil.ReadAll(fd)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not read %q: %s\n", filename, err)
+ os.Exit(1)
+ }
+
+ xc := new(xmlLoggerConfig)
+ if err := xml.Unmarshal(contents, xc); err != nil {
+ fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not parse XML configuration in %q: %s\n", filename, err)
+ os.Exit(1)
+ }
+
+ for _, xmlfilt := range xc.Filter {
+ var filt LogWriter
+ var lvl Level
+ bad, good, enabled := false, true, false
+
+ // Check required children
+ if len(xmlfilt.Enabled) == 0 {
+ fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required attribute %s for filter missing in %s\n", "enabled", filename)
+ bad = true
+ } else {
+ enabled = xmlfilt.Enabled != "false"
+ }
+ if len(xmlfilt.Tag) == 0 {
+ fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "tag", filename)
+ bad = true
+ }
+ if len(xmlfilt.Type) == 0 {
+ fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "type", filename)
+ bad = true
+ }
+ if len(xmlfilt.Level) == 0 {
+ fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "level", filename)
+ bad = true
+ }
+
+ switch xmlfilt.Level {
+ case "FINEST":
+ lvl = FINEST
+ case "FINE":
+ lvl = FINE
+ case "DEBUG":
+ lvl = DEBUG
+ case "TRACE":
+ lvl = TRACE
+ case "INFO":
+ lvl = INFO
+ case "WARNING":
+ lvl = WARNING
+ case "ERROR":
+ lvl = ERROR
+ case "CRITICAL":
+ lvl = CRITICAL
+ default:
+ fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter has unknown value in %s: %s\n", "level", filename, xmlfilt.Level)
+ bad = true
+ }
+
+ // Just so all of the required attributes are errored at the same time if missing
+ if bad {
+ os.Exit(1)
+ }
+
+ switch xmlfilt.Type {
+ case "console":
+ filt, good = xmlToConsoleLogWriter(filename, xmlfilt.Property, enabled)
+ case "file":
+ filt, good = xmlToFileLogWriter(filename, xmlfilt.Property, enabled)
+ case "xml":
+ filt, good = xmlToXMLLogWriter(filename, xmlfilt.Property, enabled)
+ case "socket":
+ filt, good = xmlToSocketLogWriter(filename, xmlfilt.Property, enabled)
+ default:
+ fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not load XML configuration in %s: unknown filter type \"%s\"\n", filename, xmlfilt.Type)
+ os.Exit(1)
+ }
+
+ // Just so all of the required params are errored at the same time if wrong
+ if !good {
+ os.Exit(1)
+ }
+
+ // If we're disabled (syntax and correctness checks only), don't add to logger
+ if !enabled {
+ continue
+ }
+
+ log[xmlfilt.Tag] = &Filter{lvl, filt}
+ }
+}
+
+func xmlToConsoleLogWriter(filename string, props []xmlProperty, enabled bool) (*ConsoleLogWriter, bool) {
+ // Parse properties
+ for _, prop := range props {
+ switch prop.Name {
+ default:
+ fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for console filter in %s\n", prop.Name, filename)
+ }
+ }
+
+ // If it's disabled, we're just checking syntax
+ if !enabled {
+ return nil, true
+ }
+
+ return NewConsoleLogWriter(), true
+}
+
+// Parse a number with K/M/G suffixes based on thousands (1000) or 2^10 (1024)
+func strToNumSuffix(str string, mult int) int {
+ num := 1
+ if len(str) > 1 {
+ switch str[len(str)-1] {
+ case 'G', 'g':
+ num *= mult
+ fallthrough
+ case 'M', 'm':
+ num *= mult
+ fallthrough
+ case 'K', 'k':
+ num *= mult
+ str = str[0 : len(str)-1]
+ }
+ }
+ parsed, _ := strconv.Atoi(str)
+ return parsed * num
+}
+func xmlToFileLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
+ file := ""
+ format := "[%D %T] [%L] (%S) %M"
+ maxlines := 0
+ maxsize := 0
+ daily := false
+ rotate := false
+
+ // Parse properties
+ for _, prop := range props {
+ switch prop.Name {
+ case "filename":
+ file = strings.Trim(prop.Value, " \r\n")
+ case "format":
+ format = strings.Trim(prop.Value, " \r\n")
+ case "maxlines":
+ maxlines = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
+ case "maxsize":
+ maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
+ case "daily":
+ daily = strings.Trim(prop.Value, " \r\n") != "false"
+ case "rotate":
+ rotate = strings.Trim(prop.Value, " \r\n") != "false"
+ default:
+ fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
+ }
+ }
+
+ // Check properties
+ if len(file) == 0 {
+ fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "filename", filename)
+ return nil, false
+ }
+
+ // If it's disabled, we're just checking syntax
+ if !enabled {
+ return nil, true
+ }
+
+ flw := NewFileLogWriter(file, rotate)
+ flw.SetFormat(format)
+ flw.SetRotateLines(maxlines)
+ flw.SetRotateSize(maxsize)
+ flw.SetRotateDaily(daily)
+ return flw, true
+}
+
+func xmlToXMLLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
+ file := ""
+ maxrecords := 0
+ maxsize := 0
+ daily := false
+ rotate := false
+
+ // Parse properties
+ for _, prop := range props {
+ switch prop.Name {
+ case "filename":
+ file = strings.Trim(prop.Value, " \r\n")
+ case "maxrecords":
+ maxrecords = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
+ case "maxsize":
+ maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
+ case "daily":
+ daily = strings.Trim(prop.Value, " \r\n") != "false"
+ case "rotate":
+ rotate = strings.Trim(prop.Value, " \r\n") != "false"
+ default:
+ fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for xml filter in %s\n", prop.Name, filename)
+ }
+ }
+
+ // Check properties
+ if len(file) == 0 {
+ fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for xml filter missing in %s\n", "filename", filename)
+ return nil, false
+ }
+
+ // If it's disabled, we're just checking syntax
+ if !enabled {
+ return nil, true
+ }
+
+ xlw := NewXMLLogWriter(file, rotate)
+ xlw.SetRotateLines(maxrecords)
+ xlw.SetRotateSize(maxsize)
+ xlw.SetRotateDaily(daily)
+ return xlw, true
+}
+
+func xmlToSocketLogWriter(filename string, props []xmlProperty, enabled bool) (SocketLogWriter, bool) {
+ endpoint := ""
+ protocol := "udp"
+
+ // Parse properties
+ for _, prop := range props {
+ switch prop.Name {
+ case "endpoint":
+ endpoint = strings.Trim(prop.Value, " \r\n")
+ case "protocol":
+ protocol = strings.Trim(prop.Value, " \r\n")
+ default:
+ fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
+ }
+ }
+
+ // Check properties
+ if len(endpoint) == 0 {
+ fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "endpoint", filename)
+ return nil, false
+ }
+
+ // If it's disabled, we're just checking syntax
+ if !enabled {
+ return nil, true
+ }
+
+ return NewSocketLogWriter(protocol, endpoint), true
+}
diff --git a/vendor/github.com/alecthomas/log4go/filelog.go b/vendor/github.com/alecthomas/log4go/filelog.go
new file mode 100644
index 000000000..ee0ab0c04
--- /dev/null
+++ b/vendor/github.com/alecthomas/log4go/filelog.go
@@ -0,0 +1,264 @@
+// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
+
+package log4go
+
+import (
+ "fmt"
+ "os"
+ "time"
+)
+
+// This log writer sends output to a file
+type FileLogWriter struct {
+ rec chan *LogRecord
+ rot chan bool
+
+ // The opened file
+ filename string
+ file *os.File
+
+ // The logging format
+ format string
+
+ // File header/trailer
+ header, trailer string
+
+ // Rotate at linecount
+ maxlines int
+ maxlines_curlines int
+
+ // Rotate at size
+ maxsize int
+ maxsize_cursize int
+
+ // Rotate daily
+ daily bool
+ daily_opendate int
+
+ // Keep old logfiles (.001, .002, etc)
+ rotate bool
+ maxbackup int
+}
+
+// This is the FileLogWriter's output method
+func (w *FileLogWriter) LogWrite(rec *LogRecord) {
+ w.rec <- rec
+}
+
+func (w *FileLogWriter) Close() {
+ close(w.rec)
+ w.file.Sync()
+}
+
+// NewFileLogWriter creates a new LogWriter which writes to the given file and
+// has rotation enabled if rotate is true.
+//
+// If rotate is true, any time a new log file is opened, the old one is renamed
+// with a .### extension to preserve it. The various Set* methods can be used
+// to configure log rotation based on lines, size, and daily.
+//
+// The standard log-line format is:
+// [%D %T] [%L] (%S) %M
+func NewFileLogWriter(fname string, rotate bool) *FileLogWriter {
+ w := &FileLogWriter{
+ rec: make(chan *LogRecord, LogBufferLength),
+ rot: make(chan bool),
+ filename: fname,
+ format: "[%D %T] [%L] (%S) %M",
+ rotate: rotate,
+ maxbackup: 999,
+ }
+
+ // open the file for the first time
+ if err := w.intRotate(); err != nil {
+ fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
+ return nil
+ }
+
+ go func() {
+ defer func() {
+ if w.file != nil {
+ fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
+ w.file.Close()
+ }
+ }()
+
+ for {
+ select {
+ case <-w.rot:
+ if err := w.intRotate(); err != nil {
+ fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
+ return
+ }
+ case rec, ok := <-w.rec:
+ if !ok {
+ return
+ }
+ now := time.Now()
+ if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) ||
+ (w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) ||
+ (w.daily && now.Day() != w.daily_opendate) {
+ if err := w.intRotate(); err != nil {
+ fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
+ return
+ }
+ }
+
+ // Perform the write
+ n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
+ return
+ }
+
+ // Update the counts
+ w.maxlines_curlines++
+ w.maxsize_cursize += n
+ }
+ }
+ }()
+
+ return w
+}
+
+// Request that the logs rotate
+func (w *FileLogWriter) Rotate() {
+ w.rot <- true
+}
+
+// If this is called in a threaded context, it MUST be synchronized
+func (w *FileLogWriter) intRotate() error {
+ // Close any log file that may be open
+ if w.file != nil {
+ fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
+ w.file.Close()
+ }
+
+ // If we are keeping log files, move it to the next available number
+ if w.rotate {
+ _, err := os.Lstat(w.filename)
+ if err == nil { // file exists
+ // Find the next available number
+ num := 1
+ fname := ""
+ if w.daily && time.Now().Day() != w.daily_opendate {
+ yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
+
+ for ; err == nil && num <= 999; num++ {
+ fname = w.filename + fmt.Sprintf(".%s.%03d", yesterday, num)
+ _, err = os.Lstat(fname)
+ }
+ // return error if the last file checked still existed
+ if err == nil {
+ return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename)
+ }
+ } else {
+ num = w.maxbackup - 1
+ for ; num >= 1; num-- {
+ fname = w.filename + fmt.Sprintf(".%d", num)
+ nfname := w.filename + fmt.Sprintf(".%d", num+1)
+ _, err = os.Lstat(fname)
+ if err == nil {
+ os.Rename(fname, nfname)
+ }
+ }
+ }
+
+ w.file.Close()
+ // Rename the file to its newfound home
+ err = os.Rename(w.filename, fname)
+ if err != nil {
+ return fmt.Errorf("Rotate: %s\n", err)
+ }
+ }
+ }
+
+ // Open the log file
+ fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
+ if err != nil {
+ return err
+ }
+ w.file = fd
+
+ now := time.Now()
+ fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now}))
+
+ // Set the daily open date to the current date
+ w.daily_opendate = now.Day()
+
+ // initialize rotation values
+ w.maxlines_curlines = 0
+ w.maxsize_cursize = 0
+
+ return nil
+}
+
+// Set the logging format (chainable). Must be called before the first log
+// message is written.
+func (w *FileLogWriter) SetFormat(format string) *FileLogWriter {
+ w.format = format
+ return w
+}
+
+// Set the logfile header and footer (chainable). Must be called before the first log
+// message is written. These are formatted similar to the FormatLogRecord (e.g.
+// you can use %D and %T in your header/footer for date and time).
+func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter {
+ w.header, w.trailer = head, foot
+ if w.maxlines_curlines == 0 {
+ fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()}))
+ }
+ return w
+}
+
+// Set rotate at linecount (chainable). Must be called before the first log
+// message is written.
+func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter {
+ //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines)
+ w.maxlines = maxlines
+ return w
+}
+
+// Set rotate at size (chainable). Must be called before the first log message
+// is written.
+func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter {
+ //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize)
+ w.maxsize = maxsize
+ return w
+}
+
+// Set rotate daily (chainable). Must be called before the first log message is
+// written.
+func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter {
+ //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily)
+ w.daily = daily
+ return w
+}
+
+// Set max backup files. Must be called before the first log message
+// is written.
+func (w *FileLogWriter) SetRotateMaxBackup(maxbackup int) *FileLogWriter {
+ w.maxbackup = maxbackup
+ return w
+}
+
+// SetRotate changes whether or not the old logs are kept. (chainable) Must be
+// called before the first log message is written. If rotate is false, the
+// files are overwritten; otherwise, they are rotated to another file before the
+// new log is opened.
+func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter {
+ //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate)
+ w.rotate = rotate
+ return w
+}
+
+// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to
+// output XML record log messages instead of line-based ones.
+func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter {
+ return NewFileLogWriter(fname, rotate).SetFormat(
+ ` <record level="%L">
+ <timestamp>%D %T</timestamp>
+ <source>%S</source>
+ <message>%M</message>
+ </record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>")
+}
diff --git a/vendor/github.com/alecthomas/log4go/log4go.go b/vendor/github.com/alecthomas/log4go/log4go.go
new file mode 100644
index 000000000..822e890cc
--- /dev/null
+++ b/vendor/github.com/alecthomas/log4go/log4go.go
@@ -0,0 +1,484 @@
+// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
+
+// Package log4go provides level-based and highly configurable logging.
+//
+// Enhanced Logging
+//
+// This is inspired by the logging functionality in Java. Essentially, you create a Logger
+// object and create output filters for it. You can send whatever you want to the Logger,
+// and it will filter that based on your settings and send it to the outputs. This way, you
+// can put as much debug code in your program as you want, and when you're done you can filter
+// out the mundane messages so only the important ones show up.
+//
+// Utility functions are provided to make life easier. Here is some example code to get started:
+//
+// log := log4go.NewLogger()
+// log.AddFilter("stdout", log4go.DEBUG, log4go.NewConsoleLogWriter())
+// log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true))
+// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
+//
+// The first two lines can be combined with the utility NewDefaultLogger:
+//
+// log := log4go.NewDefaultLogger(log4go.DEBUG)
+// log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true))
+// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
+//
+// Usage notes:
+// - The ConsoleLogWriter does not display the source of the message to standard
+// output, but the FileLogWriter does.
+// - The utility functions (Info, Debug, Warn, etc) derive their source from the
+// calling function, and this incurs extra overhead.
+//
+// Changes from 2.0:
+// - The external interface has remained mostly stable, but a lot of the
+// internals have been changed, so if you depended on any of this or created
+// your own LogWriter, then you will probably have to update your code. In
+// particular, Logger is now a map and ConsoleLogWriter is now a channel
+// behind-the-scenes, and the LogWrite method no longer has return values.
+//
+// Future work: (please let me know if you think I should work on any of these particularly)
+// - Log file rotation
+// - Logging configuration files ala log4j
+// - Have the ability to remove filters?
+// - Have GetInfoChannel, GetDebugChannel, etc return a chan string that allows
+// for another method of logging
+// - Add an XML filter type
+package log4go
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "runtime"
+ "strings"
+ "time"
+)
+
+// Version information
+const (
+ L4G_VERSION = "log4go-v3.0.1"
+ L4G_MAJOR = 3
+ L4G_MINOR = 0
+ L4G_BUILD = 1
+)
+
+/****** Constants ******/
+
+// These are the integer logging levels used by the logger
+type Level int
+
+const (
+ FINEST Level = iota
+ FINE
+ DEBUG
+ TRACE
+ INFO
+ WARNING
+ ERROR
+ CRITICAL
+)
+
+// Logging level strings
+var (
+ levelStrings = [...]string{"FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"}
+)
+
+func (l Level) String() string {
+ if l < 0 || int(l) > len(levelStrings) {
+ return "UNKNOWN"
+ }
+ return levelStrings[int(l)]
+}
+
+/****** Variables ******/
+var (
+ // LogBufferLength specifies how many log messages a particular log4go
+ // logger can buffer at a time before writing them.
+ LogBufferLength = 32
+)
+
+/****** LogRecord ******/
+
+// A LogRecord contains all of the pertinent information for each message
+type LogRecord struct {
+ Level Level // The log level
+ Created time.Time // The time at which the log message was created (nanoseconds)
+ Source string // The message source
+ Message string // The log message
+}
+
+/****** LogWriter ******/
+
+// This is an interface for anything that should be able to write logs
+type LogWriter interface {
+ // This will be called to log a LogRecord message.
+ LogWrite(rec *LogRecord)
+
+ // This should clean up anything lingering about the LogWriter, as it is called before
+ // the LogWriter is removed. LogWrite should not be called after Close.
+ Close()
+}
+
+/****** Logger ******/
+
+// A Filter represents the log level below which no log records are written to
+// the associated LogWriter.
+type Filter struct {
+ Level Level
+ LogWriter
+}
+
+// A Logger represents a collection of Filters through which log messages are
+// written.
+type Logger map[string]*Filter
+
+// Create a new logger.
+//
+// DEPRECATED: Use make(Logger) instead.
+func NewLogger() Logger {
+ os.Stderr.WriteString("warning: use of deprecated NewLogger\n")
+ return make(Logger)
+}
+
+// Create a new logger with a "stdout" filter configured to send log messages at
+// or above lvl to standard output.
+//
+// DEPRECATED: use NewDefaultLogger instead.
+func NewConsoleLogger(lvl Level) Logger {
+ os.Stderr.WriteString("warning: use of deprecated NewConsoleLogger\n")
+ return Logger{
+ "stdout": &Filter{lvl, NewConsoleLogWriter()},
+ }
+}
+
+// Create a new logger with a "stdout" filter configured to send log messages at
+// or above lvl to standard output.
+func NewDefaultLogger(lvl Level) Logger {
+ return Logger{
+ "stdout": &Filter{lvl, NewConsoleLogWriter()},
+ }
+}
+
+// Closes all log writers in preparation for exiting the program or a
+// reconfiguration of logging. Calling this is not really imperative, unless
+// you want to guarantee that all log messages are written. Close removes
+// all filters (and thus all LogWriters) from the logger.
+func (log Logger) Close() {
+ // Close all open loggers
+ for name, filt := range log {
+ filt.Close()
+ delete(log, name)
+ }
+}
+
+// Add a new LogWriter to the Logger which will only log messages at lvl or
+// higher. This function should not be called from multiple goroutines.
+// Returns the logger for chaining.
+func (log Logger) AddFilter(name string, lvl Level, writer LogWriter) Logger {
+ log[name] = &Filter{lvl, writer}
+ return log
+}
+
+/******* Logging *******/
+// Send a formatted log message internally
+func (log Logger) intLogf(lvl Level, format string, args ...interface{}) {
+ skip := true
+
+ // Determine if any logging will be done
+ for _, filt := range log {
+ if lvl >= filt.Level {
+ skip = false
+ break
+ }
+ }
+ if skip {
+ return
+ }
+
+ // Determine caller func
+ pc, _, lineno, ok := runtime.Caller(2)
+ src := ""
+ if ok {
+ src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
+ }
+
+ msg := format
+ if len(args) > 0 {
+ msg = fmt.Sprintf(format, args...)
+ }
+
+ // Make the log record
+ rec := &LogRecord{
+ Level: lvl,
+ Created: time.Now(),
+ Source: src,
+ Message: msg,
+ }
+
+ // Dispatch the logs
+ for _, filt := range log {
+ if lvl < filt.Level {
+ continue
+ }
+ filt.LogWrite(rec)
+ }
+}
+
+// Send a closure log message internally
+func (log Logger) intLogc(lvl Level, closure func() string) {
+ skip := true
+
+ // Determine if any logging will be done
+ for _, filt := range log {
+ if lvl >= filt.Level {
+ skip = false
+ break
+ }
+ }
+ if skip {
+ return
+ }
+
+ // Determine caller func
+ pc, _, lineno, ok := runtime.Caller(2)
+ src := ""
+ if ok {
+ src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
+ }
+
+ // Make the log record
+ rec := &LogRecord{
+ Level: lvl,
+ Created: time.Now(),
+ Source: src,
+ Message: closure(),
+ }
+
+ // Dispatch the logs
+ for _, filt := range log {
+ if lvl < filt.Level {
+ continue
+ }
+ filt.LogWrite(rec)
+ }
+}
+
+// Send a log message with manual level, source, and message.
+func (log Logger) Log(lvl Level, source, message string) {
+ skip := true
+
+ // Determine if any logging will be done
+ for _, filt := range log {
+ if lvl >= filt.Level {
+ skip = false
+ break
+ }
+ }
+ if skip {
+ return
+ }
+
+ // Make the log record
+ rec := &LogRecord{
+ Level: lvl,
+ Created: time.Now(),
+ Source: source,
+ Message: message,
+ }
+
+ // Dispatch the logs
+ for _, filt := range log {
+ if lvl < filt.Level {
+ continue
+ }
+ filt.LogWrite(rec)
+ }
+}
+
+// Logf logs a formatted log message at the given log level, using the caller as
+// its source.
+func (log Logger) Logf(lvl Level, format string, args ...interface{}) {
+ log.intLogf(lvl, format, args...)
+}
+
+// Logc logs a string returned by the closure at the given log level, using the caller as
+// its source. If no log message would be written, the closure is never called.
+func (log Logger) Logc(lvl Level, closure func() string) {
+ log.intLogc(lvl, closure)
+}
+
+// Finest logs a message at the finest log level.
+// See Debug for an explanation of the arguments.
+func (log Logger) Finest(arg0 interface{}, args ...interface{}) {
+ const (
+ lvl = FINEST
+ )
+ switch first := arg0.(type) {
+ case string:
+ // Use the string as a format string
+ log.intLogf(lvl, first, args...)
+ case func() string:
+ // Log the closure (no other arguments used)
+ log.intLogc(lvl, first)
+ default:
+ // Build a format string so that it will be similar to Sprint
+ log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
+ }
+}
+
+// Fine logs a message at the fine log level.
+// See Debug for an explanation of the arguments.
+func (log Logger) Fine(arg0 interface{}, args ...interface{}) {
+ const (
+ lvl = FINE
+ )
+ switch first := arg0.(type) {
+ case string:
+ // Use the string as a format string
+ log.intLogf(lvl, first, args...)
+ case func() string:
+ // Log the closure (no other arguments used)
+ log.intLogc(lvl, first)
+ default:
+ // Build a format string so that it will be similar to Sprint
+ log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
+ }
+}
+
+// Debug is a utility method for debug log messages.
+// The behavior of Debug depends on the first argument:
+// - arg0 is a string
+// When given a string as the first argument, this behaves like Logf but with
+// the DEBUG log level: the first argument is interpreted as a format for the
+// latter arguments.
+// - arg0 is a func()string
+// When given a closure of type func()string, this logs the string returned by
+// the closure iff it will be logged. The closure runs at most one time.
+// - arg0 is interface{}
+// When given anything else, the log message will be each of the arguments
+// formatted with %v and separated by spaces (ala Sprint).
+func (log Logger) Debug(arg0 interface{}, args ...interface{}) {
+ const (
+ lvl = DEBUG
+ )
+ switch first := arg0.(type) {
+ case string:
+ // Use the string as a format string
+ log.intLogf(lvl, first, args...)
+ case func() string:
+ // Log the closure (no other arguments used)
+ log.intLogc(lvl, first)
+ default:
+ // Build a format string so that it will be similar to Sprint
+ log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
+ }
+}
+
+// Trace logs a message at the trace log level.
+// See Debug for an explanation of the arguments.
+func (log Logger) Trace(arg0 interface{}, args ...interface{}) {
+ const (
+ lvl = TRACE
+ )
+ switch first := arg0.(type) {
+ case string:
+ // Use the string as a format string
+ log.intLogf(lvl, first, args...)
+ case func() string:
+ // Log the closure (no other arguments used)
+ log.intLogc(lvl, first)
+ default:
+ // Build a format string so that it will be similar to Sprint
+ log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
+ }
+}
+
+// Info logs a message at the info log level.
+// See Debug for an explanation of the arguments.
+func (log Logger) Info(arg0 interface{}, args ...interface{}) {
+ const (
+ lvl = INFO
+ )
+ switch first := arg0.(type) {
+ case string:
+ // Use the string as a format string
+ log.intLogf(lvl, first, args...)
+ case func() string:
+ // Log the closure (no other arguments used)
+ log.intLogc(lvl, first)
+ default:
+ // Build a format string so that it will be similar to Sprint
+ log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
+ }
+}
+
+// Warn logs a message at the warning log level and returns the formatted error.
+// At the warning level and higher, there is no performance benefit if the
+// message is not actually logged, because all formats are processed and all
+// closures are executed to format the error message.
+// See Debug for further explanation of the arguments.
+func (log Logger) Warn(arg0 interface{}, args ...interface{}) error {
+ const (
+ lvl = WARNING
+ )
+ var msg string
+ switch first := arg0.(type) {
+ case string:
+ // Use the string as a format string
+ msg = fmt.Sprintf(first, args...)
+ case func() string:
+ // Log the closure (no other arguments used)
+ msg = first()
+ default:
+ // Build a format string so that it will be similar to Sprint
+ msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
+ }
+ log.intLogf(lvl, msg)
+ return errors.New(msg)
+}
+
+// Error logs a message at the error log level and returns the formatted error,
+// See Warn for an explanation of the performance and Debug for an explanation
+// of the parameters.
+func (log Logger) Error(arg0 interface{}, args ...interface{}) error {
+ const (
+ lvl = ERROR
+ )
+ var msg string
+ switch first := arg0.(type) {
+ case string:
+ // Use the string as a format string
+ msg = fmt.Sprintf(first, args...)
+ case func() string:
+ // Log the closure (no other arguments used)
+ msg = first()
+ default:
+ // Build a format string so that it will be similar to Sprint
+ msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
+ }
+ log.intLogf(lvl, msg)
+ return errors.New(msg)
+}
+
+// Critical logs a message at the critical log level and returns the formatted error,
+// See Warn for an explanation of the performance and Debug for an explanation
+// of the parameters.
+func (log Logger) Critical(arg0 interface{}, args ...interface{}) error {
+ const (
+ lvl = CRITICAL
+ )
+ var msg string
+ switch first := arg0.(type) {
+ case string:
+ // Use the string as a format string
+ msg = fmt.Sprintf(first, args...)
+ case func() string:
+ // Log the closure (no other arguments used)
+ msg = first()
+ default:
+ // Build a format string so that it will be similar to Sprint
+ msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
+ }
+ log.intLogf(lvl, msg)
+ return errors.New(msg)
+}
diff --git a/vendor/github.com/alecthomas/log4go/pattlog.go b/vendor/github.com/alecthomas/log4go/pattlog.go
new file mode 100644
index 000000000..82b4e36b1
--- /dev/null
+++ b/vendor/github.com/alecthomas/log4go/pattlog.go
@@ -0,0 +1,126 @@
+// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
+
+package log4go
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "strings"
+)
+
+const (
+ FORMAT_DEFAULT = "[%D %T] [%L] (%S) %M"
+ FORMAT_SHORT = "[%t %d] [%L] %M"
+ FORMAT_ABBREV = "[%L] %M"
+)
+
+type formatCacheType struct {
+ LastUpdateSeconds int64
+ shortTime, shortDate string
+ longTime, longDate string
+}
+
+var formatCache = &formatCacheType{}
+
+// Known format codes:
+// %T - Time (15:04:05 MST)
+// %t - Time (15:04)
+// %D - Date (2006/01/02)
+// %d - Date (01/02/06)
+// %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
+// %S - Source
+// %M - Message
+// Ignores unknown formats
+// Recommended: "[%D %T] [%L] (%S) %M"
+func FormatLogRecord(format string, rec *LogRecord) string {
+ if rec == nil {
+ return "<nil>"
+ }
+ if len(format) == 0 {
+ return ""
+ }
+
+ out := bytes.NewBuffer(make([]byte, 0, 64))
+ secs := rec.Created.UnixNano() / 1e9
+
+ cache := *formatCache
+ if cache.LastUpdateSeconds != secs {
+ month, day, year := rec.Created.Month(), rec.Created.Day(), rec.Created.Year()
+ hour, minute, second := rec.Created.Hour(), rec.Created.Minute(), rec.Created.Second()
+ zone, _ := rec.Created.Zone()
+ updated := &formatCacheType{
+ LastUpdateSeconds: secs,
+ shortTime: fmt.Sprintf("%02d:%02d", hour, minute),
+ shortDate: fmt.Sprintf("%02d/%02d/%02d", day, month, year%100),
+ longTime: fmt.Sprintf("%02d:%02d:%02d %s", hour, minute, second, zone),
+ longDate: fmt.Sprintf("%04d/%02d/%02d", year, month, day),
+ }
+ cache = *updated
+ formatCache = updated
+ }
+
+ // Split the string into pieces by % signs
+ pieces := bytes.Split([]byte(format), []byte{'%'})
+
+ // Iterate over the pieces, replacing known formats
+ for i, piece := range pieces {
+ if i > 0 && len(piece) > 0 {
+ switch piece[0] {
+ case 'T':
+ out.WriteString(cache.longTime)
+ case 't':
+ out.WriteString(cache.shortTime)
+ case 'D':
+ out.WriteString(cache.longDate)
+ case 'd':
+ out.WriteString(cache.shortDate)
+ case 'L':
+ out.WriteString(levelStrings[rec.Level])
+ case 'S':
+ out.WriteString(rec.Source)
+ case 's':
+ slice := strings.Split(rec.Source, "/")
+ out.WriteString(slice[len(slice)-1])
+ case 'M':
+ out.WriteString(rec.Message)
+ }
+ if len(piece) > 1 {
+ out.Write(piece[1:])
+ }
+ } else if len(piece) > 0 {
+ out.Write(piece)
+ }
+ }
+ out.WriteByte('\n')
+
+ return out.String()
+}
+
+// This is the standard writer that prints to standard output.
+type FormatLogWriter chan *LogRecord
+
+// This creates a new FormatLogWriter
+func NewFormatLogWriter(out io.Writer, format string) FormatLogWriter {
+ records := make(FormatLogWriter, LogBufferLength)
+ go records.run(out, format)
+ return records
+}
+
+func (w FormatLogWriter) run(out io.Writer, format string) {
+ for rec := range w {
+ fmt.Fprint(out, FormatLogRecord(format, rec))
+ }
+}
+
+// This is the FormatLogWriter's output method. This will block if the output
+// buffer is full.
+func (w FormatLogWriter) LogWrite(rec *LogRecord) {
+ w <- rec
+}
+
+// Close stops the logger from sending messages to standard output. Attempts to
+// send log messages to this logger after a Close have undefined behavior.
+func (w FormatLogWriter) Close() {
+ close(w)
+}
diff --git a/vendor/github.com/alecthomas/log4go/socklog.go b/vendor/github.com/alecthomas/log4go/socklog.go
new file mode 100644
index 000000000..1d224a99d
--- /dev/null
+++ b/vendor/github.com/alecthomas/log4go/socklog.go
@@ -0,0 +1,57 @@
+// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
+
+package log4go
+
+import (
+ "encoding/json"
+ "fmt"
+ "net"
+ "os"
+)
+
+// This log writer sends output to a socket
+type SocketLogWriter chan *LogRecord
+
+// This is the SocketLogWriter's output method
+func (w SocketLogWriter) LogWrite(rec *LogRecord) {
+ w <- rec
+}
+
+func (w SocketLogWriter) Close() {
+ close(w)
+}
+
+func NewSocketLogWriter(proto, hostport string) SocketLogWriter {
+ sock, err := net.Dial(proto, hostport)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "NewSocketLogWriter(%q): %s\n", hostport, err)
+ return nil
+ }
+
+ w := SocketLogWriter(make(chan *LogRecord, LogBufferLength))
+
+ go func() {
+ defer func() {
+ if sock != nil && proto == "tcp" {
+ sock.Close()
+ }
+ }()
+
+ for rec := range w {
+ // Marshall into JSON
+ js, err := json.Marshal(rec)
+ if err != nil {
+ fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err)
+ return
+ }
+
+ _, err = sock.Write(js)
+ if err != nil {
+ fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err)
+ return
+ }
+ }
+ }()
+
+ return w
+}
diff --git a/vendor/github.com/alecthomas/log4go/termlog.go b/vendor/github.com/alecthomas/log4go/termlog.go
new file mode 100644
index 000000000..8a941e269
--- /dev/null
+++ b/vendor/github.com/alecthomas/log4go/termlog.go
@@ -0,0 +1,49 @@
+// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
+
+package log4go
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "time"
+)
+
+var stdout io.Writer = os.Stdout
+
+// This is the standard writer that prints to standard output.
+type ConsoleLogWriter struct {
+ format string
+ w chan *LogRecord
+}
+
+// This creates a new ConsoleLogWriter
+func NewConsoleLogWriter() *ConsoleLogWriter {
+ consoleWriter := &ConsoleLogWriter{
+ format: "[%T %D] [%L] (%S) %M",
+ w: make(chan *LogRecord, LogBufferLength),
+ }
+ go consoleWriter.run(stdout)
+ return consoleWriter
+}
+func (c *ConsoleLogWriter) SetFormat(format string) {
+ c.format = format
+}
+func (c *ConsoleLogWriter) run(out io.Writer) {
+ for rec := range c.w {
+ fmt.Fprint(out, FormatLogRecord(c.format, rec))
+ }
+}
+
+// This is the ConsoleLogWriter's output method. This will block if the output
+// buffer is full.
+func (c *ConsoleLogWriter) LogWrite(rec *LogRecord) {
+ c.w <- rec
+}
+
+// Close stops the logger from sending messages to standard output. Attempts to
+// send log messages to this logger after a Close have undefined behavior.
+func (c *ConsoleLogWriter) Close() {
+ close(c.w)
+ time.Sleep(50 * time.Millisecond) // Try to give console I/O time to complete
+}
diff --git a/vendor/github.com/alecthomas/log4go/wrapper.go b/vendor/github.com/alecthomas/log4go/wrapper.go
new file mode 100644
index 000000000..2ae222b0c
--- /dev/null
+++ b/vendor/github.com/alecthomas/log4go/wrapper.go
@@ -0,0 +1,278 @@
+// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
+
+package log4go
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "strings"
+)
+
+var (
+ Global Logger
+)
+
+func init() {
+ Global = NewDefaultLogger(DEBUG)
+}
+
+// Wrapper for (*Logger).LoadConfiguration
+func LoadConfiguration(filename string) {
+ Global.LoadConfiguration(filename)
+}
+
+// Wrapper for (*Logger).AddFilter
+func AddFilter(name string, lvl Level, writer LogWriter) {
+ Global.AddFilter(name, lvl, writer)
+}
+
+// Wrapper for (*Logger).Close (closes and removes all logwriters)
+func Close() {
+ Global.Close()
+}
+
+func Crash(args ...interface{}) {
+ if len(args) > 0 {
+ Global.intLogf(CRITICAL, strings.Repeat(" %v", len(args))[1:], args...)
+ }
+ panic(args)
+}
+
+// Logs the given message and crashes the program
+func Crashf(format string, args ...interface{}) {
+ Global.intLogf(CRITICAL, format, args...)
+ Global.Close() // so that hopefully the messages get logged
+ panic(fmt.Sprintf(format, args...))
+}
+
+// Compatibility with `log`
+func Exit(args ...interface{}) {
+ if len(args) > 0 {
+ Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
+ }
+ Global.Close() // so that hopefully the messages get logged
+ os.Exit(0)
+}
+
+// Compatibility with `log`
+func Exitf(format string, args ...interface{}) {
+ Global.intLogf(ERROR, format, args...)
+ Global.Close() // so that hopefully the messages get logged
+ os.Exit(0)
+}
+
+// Compatibility with `log`
+func Stderr(args ...interface{}) {
+ if len(args) > 0 {
+ Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
+ }
+}
+
+// Compatibility with `log`
+func Stderrf(format string, args ...interface{}) {
+ Global.intLogf(ERROR, format, args...)
+}
+
+// Compatibility with `log`
+func Stdout(args ...interface{}) {
+ if len(args) > 0 {
+ Global.intLogf(INFO, strings.Repeat(" %v", len(args))[1:], args...)
+ }
+}
+
+// Compatibility with `log`
+func Stdoutf(format string, args ...interface{}) {
+ Global.intLogf(INFO, format, args...)
+}
+
+// Send a log message manually
+// Wrapper for (*Logger).Log
+func Log(lvl Level, source, message string) {
+ Global.Log(lvl, source, message)
+}
+
+// Send a formatted log message easily
+// Wrapper for (*Logger).Logf
+func Logf(lvl Level, format string, args ...interface{}) {
+ Global.intLogf(lvl, format, args...)
+}
+
+// Send a closure log message
+// Wrapper for (*Logger).Logc
+func Logc(lvl Level, closure func() string) {
+ Global.intLogc(lvl, closure)
+}
+
+// Utility for finest log messages (see Debug() for parameter explanation)
+// Wrapper for (*Logger).Finest
+func Finest(arg0 interface{}, args ...interface{}) {
+ const (
+ lvl = FINEST
+ )
+ switch first := arg0.(type) {
+ case string:
+ // Use the string as a format string
+ Global.intLogf(lvl, first, args...)
+ case func() string:
+ // Log the closure (no other arguments used)
+ Global.intLogc(lvl, first)
+ default:
+ // Build a format string so that it will be similar to Sprint
+ Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
+ }
+}
+
+// Utility for fine log messages (see Debug() for parameter explanation)
+// Wrapper for (*Logger).Fine
+func Fine(arg0 interface{}, args ...interface{}) {
+ const (
+ lvl = FINE
+ )
+ switch first := arg0.(type) {
+ case string:
+ // Use the string as a format string
+ Global.intLogf(lvl, first, args...)
+ case func() string:
+ // Log the closure (no other arguments used)
+ Global.intLogc(lvl, first)
+ default:
+ // Build a format string so that it will be similar to Sprint
+ Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
+ }
+}
+
+// Utility for debug log messages
+// When given a string as the first argument, this behaves like Logf but with the DEBUG log level (e.g. the first argument is interpreted as a format for the latter arguments)
+// When given a closure of type func()string, this logs the string returned by the closure iff it will be logged. The closure runs at most one time.
+// When given anything else, the log message will be each of the arguments formatted with %v and separated by spaces (ala Sprint).
+// Wrapper for (*Logger).Debug
+func Debug(arg0 interface{}, args ...interface{}) {
+ const (
+ lvl = DEBUG
+ )
+ switch first := arg0.(type) {
+ case string:
+ // Use the string as a format string
+ Global.intLogf(lvl, first, args...)
+ case func() string:
+ // Log the closure (no other arguments used)
+ Global.intLogc(lvl, first)
+ default:
+ // Build a format string so that it will be similar to Sprint
+ Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
+ }
+}
+
+// Utility for trace log messages (see Debug() for parameter explanation)
+// Wrapper for (*Logger).Trace
+func Trace(arg0 interface{}, args ...interface{}) {
+ const (
+ lvl = TRACE
+ )
+ switch first := arg0.(type) {
+ case string:
+ // Use the string as a format string
+ Global.intLogf(lvl, first, args...)
+ case func() string:
+ // Log the closure (no other arguments used)
+ Global.intLogc(lvl, first)
+ default:
+ // Build a format string so that it will be similar to Sprint
+ Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
+ }
+}
+
+// Utility for info log messages (see Debug() for parameter explanation)
+// Wrapper for (*Logger).Info
+func Info(arg0 interface{}, args ...interface{}) {
+ const (
+ lvl = INFO
+ )
+ switch first := arg0.(type) {
+ case string:
+ // Use the string as a format string
+ Global.intLogf(lvl, first, args...)
+ case func() string:
+ // Log the closure (no other arguments used)
+ Global.intLogc(lvl, first)
+ default:
+ // Build a format string so that it will be similar to Sprint
+ Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
+ }
+}
+
+// Utility for warn log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
+// These functions will execute a closure exactly once, to build the error message for the return
+// Wrapper for (*Logger).Warn
+func Warn(arg0 interface{}, args ...interface{}) error {
+ const (
+ lvl = WARNING
+ )
+ switch first := arg0.(type) {
+ case string:
+ // Use the string as a format string
+ Global.intLogf(lvl, first, args...)
+ return errors.New(fmt.Sprintf(first, args...))
+ case func() string:
+ // Log the closure (no other arguments used)
+ str := first()
+ Global.intLogf(lvl, "%s", str)
+ return errors.New(str)
+ default:
+ // Build a format string so that it will be similar to Sprint
+ Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
+ return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
+ }
+ return nil
+}
+
+// Utility for error log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
+// These functions will execute a closure exactly once, to build the error message for the return
+// Wrapper for (*Logger).Error
+func Error(arg0 interface{}, args ...interface{}) error {
+ const (
+ lvl = ERROR
+ )
+ switch first := arg0.(type) {
+ case string:
+ // Use the string as a format string
+ Global.intLogf(lvl, first, args...)
+ return errors.New(fmt.Sprintf(first, args...))
+ case func() string:
+ // Log the closure (no other arguments used)
+ str := first()
+ Global.intLogf(lvl, "%s", str)
+ return errors.New(str)
+ default:
+ // Build a format string so that it will be similar to Sprint
+ Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
+ return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
+ }
+ return nil
+}
+
+// Utility for critical log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
+// These functions will execute a closure exactly once, to build the error message for the return
+// Wrapper for (*Logger).Critical
+func Critical(arg0 interface{}, args ...interface{}) error {
+ const (
+ lvl = CRITICAL
+ )
+ switch first := arg0.(type) {
+ case string:
+ // Use the string as a format string
+ Global.intLogf(lvl, first, args...)
+ return errors.New(fmt.Sprintf(first, args...))
+ case func() string:
+ // Log the closure (no other arguments used)
+ str := first()
+ Global.intLogf(lvl, "%s", str)
+ return errors.New(str)
+ default:
+ // Build a format string so that it will be similar to Sprint
+ Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
+ return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
+ }
+ return nil
+}
diff --git a/vendor/github.com/braintree/manners/LICENSE b/vendor/github.com/braintree/manners/LICENSE
new file mode 100644
index 000000000..91ef5beed
--- /dev/null
+++ b/vendor/github.com/braintree/manners/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Braintree, a division of PayPal, Inc.
+
+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/github.com/braintree/manners/README.md b/vendor/github.com/braintree/manners/README.md
new file mode 100644
index 000000000..09f6f9693
--- /dev/null
+++ b/vendor/github.com/braintree/manners/README.md
@@ -0,0 +1,36 @@
+# Manners
+
+A *polite* webserver for Go.
+
+Manners allows you to shut your Go webserver down gracefully, without dropping any requests. It can act as a drop-in replacement for the standard library's http.ListenAndServe function:
+
+```go
+func main() {
+ handler := MyHTTPHandler()
+ manners.ListenAndServe(":7000", handler)
+}
+```
+
+Then, when you want to shut the server down:
+
+```go
+manners.Close()
+```
+
+(Note that this does not block until all the requests are finished. Rather, the call to manners.ListenAndServe will stop blocking when all the requests are finished.)
+
+Manners ensures that all requests are served by incrementing a WaitGroup when a request comes in and decrementing it when the request finishes.
+
+If your request handler spawns Goroutines that are not guaranteed to finish with the request, you can ensure they are also completed with the `StartRoutine` and `FinishRoutine` functions on the server.
+
+### Known Issues
+
+Manners does not correctly shut down long-lived keepalive connections when issued a shutdown command. Clients on an idle keepalive connection may see a connection reset error rather than a close. See https://github.com/braintree/manners/issues/13 for details.
+
+### Compatability
+
+Manners 0.3.0 and above uses standard library functionality introduced in Go 1.3.
+
+### Installation
+
+`go get github.com/braintree/manners`
diff --git a/vendor/github.com/braintree/manners/interfaces.go b/vendor/github.com/braintree/manners/interfaces.go
new file mode 100644
index 000000000..fd0732857
--- /dev/null
+++ b/vendor/github.com/braintree/manners/interfaces.go
@@ -0,0 +1,7 @@
+package manners
+
+type waitGroup interface {
+ Add(int)
+ Done()
+ Wait()
+}
diff --git a/vendor/github.com/braintree/manners/server.go b/vendor/github.com/braintree/manners/server.go
new file mode 100644
index 000000000..dfd3b873b
--- /dev/null
+++ b/vendor/github.com/braintree/manners/server.go
@@ -0,0 +1,292 @@
+/*
+Package manners provides a wrapper for a standard net/http server that
+ensures all active HTTP client have completed their current request
+before the server shuts down.
+
+It can be used a drop-in replacement for the standard http package,
+or can wrap a pre-configured Server.
+
+eg.
+
+ http.Handle("/hello", func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte("Hello\n"))
+ })
+
+ log.Fatal(manners.ListenAndServe(":8080", nil))
+
+or for a customized server:
+
+ s := manners.NewWithServer(&http.Server{
+ Addr: ":8080",
+ Handler: myHandler,
+ ReadTimeout: 10 * time.Second,
+ WriteTimeout: 10 * time.Second,
+ MaxHeaderBytes: 1 << 20,
+ })
+ log.Fatal(s.ListenAndServe())
+
+The server will shut down cleanly when the Close() method is called:
+
+ go func() {
+ sigchan := make(chan os.Signal, 1)
+ signal.Notify(sigchan, os.Interrupt, os.Kill)
+ <-sigchan
+ log.Info("Shutting down...")
+ manners.Close()
+ }()
+
+ http.Handle("/hello", myHandler)
+ log.Fatal(manners.ListenAndServe(":8080", nil))
+*/
+package manners
+
+import (
+ "crypto/tls"
+ "net"
+ "net/http"
+ "sync"
+ "sync/atomic"
+)
+
+// A GracefulServer maintains a WaitGroup that counts how many in-flight
+// requests the server is handling. When it receives a shutdown signal,
+// it stops accepting new requests but does not actually shut down until
+// all in-flight requests terminate.
+//
+// GracefulServer embeds the underlying net/http.Server making its non-override
+// methods and properties avaiable.
+//
+// It must be initialized by calling NewWithServer.
+type GracefulServer struct {
+ *http.Server
+
+ shutdown chan bool
+ shutdownFinished chan bool
+ wg waitGroup
+ routinesCount int
+
+ lcsmu sync.RWMutex
+ connections map[net.Conn]bool
+
+ up chan net.Listener // Only used by test code.
+}
+
+// NewServer creates a new GracefulServer.
+func NewServer() *GracefulServer {
+ return NewWithServer(new(http.Server))
+}
+
+// NewWithServer wraps an existing http.Server object and returns a
+// GracefulServer that supports all of the original Server operations.
+func NewWithServer(s *http.Server) *GracefulServer {
+ return &GracefulServer{
+ Server: s,
+ shutdown: make(chan bool),
+ shutdownFinished: make(chan bool, 1),
+ wg: new(sync.WaitGroup),
+ routinesCount: 0,
+ connections: make(map[net.Conn]bool),
+ }
+}
+
+// Close stops the server from accepting new requets and begins shutting down.
+// It returns true if it's the first time Close is called.
+func (s *GracefulServer) Close() bool {
+ return <-s.shutdown
+}
+
+// BlockingClose is similar to Close, except that it blocks until the last
+// connection has been closed.
+func (s *GracefulServer) BlockingClose() bool {
+ result := s.Close()
+ <-s.shutdownFinished
+ return result
+}
+
+// ListenAndServe provides a graceful equivalent of net/http.Serve.ListenAndServe.
+func (s *GracefulServer) ListenAndServe() error {
+ addr := s.Addr
+ if addr == "" {
+ addr = ":http"
+ }
+ listener, err := net.Listen("tcp", addr)
+ if err != nil {
+ return err
+ }
+
+ return s.Serve(listener)
+}
+
+// ListenAndServeTLS provides a graceful equivalent of net/http.Serve.ListenAndServeTLS.
+func (s *GracefulServer) ListenAndServeTLS(certFile, keyFile string) error {
+ // direct lift from net/http/server.go
+ addr := s.Addr
+ if addr == "" {
+ addr = ":https"
+ }
+ config := &tls.Config{}
+ if s.TLSConfig != nil {
+ *config = *s.TLSConfig
+ }
+ if config.NextProtos == nil {
+ config.NextProtos = []string{"http/1.1"}
+ }
+
+ var err error
+ config.Certificates = make([]tls.Certificate, 1)
+ config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
+ if err != nil {
+ return err
+ }
+
+ ln, err := net.Listen("tcp", addr)
+ if err != nil {
+ return err
+ }
+
+ return s.Serve(tls.NewListener(ln, config))
+}
+
+// Serve provides a graceful equivalent net/http.Server.Serve.
+func (s *GracefulServer) Serve(listener net.Listener) error {
+ // Wrap the server HTTP handler into graceful one, that will close kept
+ // alive connections if a new request is received after shutdown.
+ gracefulHandler := newGracefulHandler(s.Server.Handler)
+ s.Server.Handler = gracefulHandler
+
+ // Start a goroutine that waits for a shutdown signal and will stop the
+ // listener when it receives the signal. That in turn will result in
+ // unblocking of the http.Serve call.
+ go func() {
+ s.shutdown <- true
+ close(s.shutdown)
+ gracefulHandler.Close()
+ s.Server.SetKeepAlivesEnabled(false)
+ listener.Close()
+ }()
+
+ originalConnState := s.Server.ConnState
+
+ // s.ConnState is invoked by the net/http.Server every time a connection
+ // changes state. It keeps track of each connection's state over time,
+ // enabling manners to handle persisted connections correctly.
+ s.ConnState = func(conn net.Conn, newState http.ConnState) {
+ s.lcsmu.RLock()
+ protected := s.connections[conn]
+ s.lcsmu.RUnlock()
+
+ switch newState {
+
+ case http.StateNew:
+ // New connection -> StateNew
+ protected = true
+ s.StartRoutine()
+
+ case http.StateActive:
+ // (StateNew, StateIdle) -> StateActive
+ if gracefulHandler.IsClosed() {
+ conn.Close()
+ break
+ }
+
+ if !protected {
+ protected = true
+ s.StartRoutine()
+ }
+
+ default:
+ // (StateNew, StateActive) -> (StateIdle, StateClosed, StateHiJacked)
+ if protected {
+ s.FinishRoutine()
+ protected = false
+ }
+ }
+
+ s.lcsmu.Lock()
+ if newState == http.StateClosed || newState == http.StateHijacked {
+ delete(s.connections, conn)
+ } else {
+ s.connections[conn] = protected
+ }
+ s.lcsmu.Unlock()
+
+ if originalConnState != nil {
+ originalConnState(conn, newState)
+ }
+ }
+
+ // A hook to allow the server to notify others when it is ready to receive
+ // requests; only used by tests.
+ if s.up != nil {
+ s.up <- listener
+ }
+
+ err := s.Server.Serve(listener)
+ // An error returned on shutdown is not worth reporting.
+ if err != nil && gracefulHandler.IsClosed() {
+ err = nil
+ }
+
+ // Wait for pending requests to complete regardless the Serve result.
+ s.wg.Wait()
+ s.shutdownFinished <- true
+ return err
+}
+
+// StartRoutine increments the server's WaitGroup. Use this if a web request
+// starts more goroutines and these goroutines are not guaranteed to finish
+// before the request.
+func (s *GracefulServer) StartRoutine() {
+ s.lcsmu.Lock()
+ defer s.lcsmu.Unlock()
+ s.wg.Add(1)
+ s.routinesCount++
+}
+
+// FinishRoutine decrements the server's WaitGroup. Use this to complement
+// StartRoutine().
+func (s *GracefulServer) FinishRoutine() {
+ s.lcsmu.Lock()
+ defer s.lcsmu.Unlock()
+ s.wg.Done()
+ s.routinesCount--
+}
+
+// RoutinesCount returns the number of currently running routines
+func (s *GracefulServer) RoutinesCount() int {
+ s.lcsmu.RLock()
+ defer s.lcsmu.RUnlock()
+ return s.routinesCount
+}
+
+// gracefulHandler is used by GracefulServer to prevent calling ServeHTTP on
+// to be closed kept-alive connections during the server shutdown.
+type gracefulHandler struct {
+ closed int32 // accessed atomically.
+ wrapped http.Handler
+}
+
+func newGracefulHandler(wrapped http.Handler) *gracefulHandler {
+ return &gracefulHandler{
+ wrapped: wrapped,
+ }
+}
+
+func (gh *gracefulHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if atomic.LoadInt32(&gh.closed) == 0 {
+ gh.wrapped.ServeHTTP(w, r)
+ return
+ }
+ r.Body.Close()
+ // Server is shutting down at this moment, and the connection that this
+ // handler is being called on is about to be closed. So we do not need to
+ // actually execute the handler logic.
+}
+
+func (gh *gracefulHandler) Close() {
+ atomic.StoreInt32(&gh.closed, 1)
+}
+
+func (gh *gracefulHandler) IsClosed() bool {
+ return atomic.LoadInt32(&gh.closed) == 1
+}
diff --git a/vendor/github.com/braintree/manners/static.go b/vendor/github.com/braintree/manners/static.go
new file mode 100644
index 000000000..b53950675
--- /dev/null
+++ b/vendor/github.com/braintree/manners/static.go
@@ -0,0 +1,47 @@
+package manners
+
+import (
+ "net"
+ "net/http"
+ "sync"
+)
+
+var (
+ defaultServer *GracefulServer
+ defaultServerLock = &sync.Mutex{}
+)
+
+func init() {
+ defaultServerLock.Lock()
+}
+
+// ListenAndServe provides a graceful version of the function provided by the
+// net/http package. Call Close() to stop the server.
+func ListenAndServe(addr string, handler http.Handler) error {
+ defaultServer = NewWithServer(&http.Server{Addr: addr, Handler: handler})
+ defaultServerLock.Unlock()
+ return defaultServer.ListenAndServe()
+}
+
+// ListenAndServeTLS provides a graceful version of the function provided by the
+// net/http package. Call Close() to stop the server.
+func ListenAndServeTLS(addr string, certFile string, keyFile string, handler http.Handler) error {
+ defaultServer = NewWithServer(&http.Server{Addr: addr, Handler: handler})
+ defaultServerLock.Unlock()
+ return defaultServer.ListenAndServeTLS(certFile, keyFile)
+}
+
+// Serve provides a graceful version of the function provided by the net/http
+// package. Call Close() to stop the server.
+func Serve(l net.Listener, handler http.Handler) error {
+ defaultServer = NewWithServer(&http.Server{Handler: handler})
+ defaultServerLock.Unlock()
+ return defaultServer.Serve(l)
+}
+
+// Shuts down the default server used by ListenAndServe, ListenAndServeTLS and
+// Serve. It returns true if it's the first time Close is called.
+func Close() bool {
+ defaultServerLock.Lock()
+ return defaultServer.Close()
+}
diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/.travis.yml b/vendor/github.com/cloudfoundry/jibber_jabber/.travis.yml
new file mode 100644
index 000000000..b19c2e535
--- /dev/null
+++ b/vendor/github.com/cloudfoundry/jibber_jabber/.travis.yml
@@ -0,0 +1,11 @@
+language: go
+go:
+ - 1.2
+before_install:
+- go get github.com/onsi/ginkgo/...
+- go get github.com/onsi/gomega/...
+- go install github.com/onsi/ginkgo/ginkgo
+script: PATH=$PATH:$HOME/gopath/bin ginkgo -r .
+branches:
+ only:
+ - master
diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/LICENSE b/vendor/github.com/cloudfoundry/jibber_jabber/LICENSE
new file mode 100644
index 000000000..915b20892
--- /dev/null
+++ b/vendor/github.com/cloudfoundry/jibber_jabber/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright 2014 Pivotal
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/README.md b/vendor/github.com/cloudfoundry/jibber_jabber/README.md
new file mode 100644
index 000000000..d696eb6b6
--- /dev/null
+++ b/vendor/github.com/cloudfoundry/jibber_jabber/README.md
@@ -0,0 +1,44 @@
+# Jibber Jabber [![Build Status](https://travis-ci.org/cloudfoundry/jibber_jabber.svg?branch=master)](https://travis-ci.org/cloudfoundry/jibber_jabber)
+Jibber Jabber is a GoLang Library that can be used to detect an operating system's current language.
+
+### OS Support
+
+OSX and Linux via the `LC_ALL` and `LANG` environment variables. These are standard variables that are used in ALL versions of UNIX for language detection.
+
+Windows via [GetUserDefaultLocaleName](http://msdn.microsoft.com/en-us/library/windows/desktop/dd318136.aspx) and [GetSystemDefaultLocaleName](http://msdn.microsoft.com/en-us/library/windows/desktop/dd318122.aspx) system calls. These calls are supported in Windows Vista and up.
+
+# Usage
+Add the following line to your go `import`:
+
+```
+ "github.com/cloudfoundry/jibber_jabber"
+```
+
+### DetectIETF
+`DetectIETF` will return the current locale as a string. The format of the locale will be the [ISO 639](http://en.wikipedia.org/wiki/ISO_639) two-letter language code, a DASH, then an [ISO 3166](http://en.wikipedia.org/wiki/ISO_3166-1) two-letter country code.
+
+```
+ userLocale, err := jibber_jabber.DetectIETF()
+ println("Locale:", userLocale)
+```
+
+### DetectLanguage
+`DetectLanguage` will return the current languge as a string. The format will be the [ISO 639](http://en.wikipedia.org/wiki/ISO_639) two-letter language code.
+
+```
+ userLanguage, err := jibber_jabber.DetectLanguage()
+ println("Language:", userLanguage)
+```
+
+### DetectTerritory
+`DetectTerritory` will return the current locale territory as a string. The format will be the [ISO 3166](http://en.wikipedia.org/wiki/ISO_3166-1) two-letter country code.
+
+```
+ localeTerritory, err := jibber_jabber.DetectTerritory()
+ println("Territory:", localeTerritory)
+```
+
+### Errors
+All the Detect commands will return an error if they are unable to read the Locale from the system.
+
+For Windows, additional error information is provided due to the nature of the system call being used.
diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber.go b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber.go
new file mode 100644
index 000000000..45d288ea8
--- /dev/null
+++ b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber.go
@@ -0,0 +1,22 @@
+package jibber_jabber
+
+import (
+ "strings"
+)
+
+const (
+ COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE = "Could not detect Language"
+)
+
+func splitLocale(locale string) (string, string) {
+ formattedLocale := strings.Split(locale, ".")[0]
+ formattedLocale = strings.Replace(formattedLocale, "-", "_", -1)
+
+ pieces := strings.Split(formattedLocale, "_")
+ language := pieces[0]
+ territory := ""
+ if len(pieces) > 1 {
+ territory = strings.Split(formattedLocale, "_")[1]
+ }
+ return language, territory
+}
diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix.go b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix.go
new file mode 100644
index 000000000..374d76176
--- /dev/null
+++ b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix.go
@@ -0,0 +1,57 @@
+// +build darwin freebsd linux netbsd openbsd
+
+package jibber_jabber
+
+import (
+ "errors"
+ "os"
+ "strings"
+)
+
+func getLangFromEnv() (locale string) {
+ locale = os.Getenv("LC_ALL")
+ if locale == "" {
+ locale = os.Getenv("LANG")
+ }
+ return
+}
+
+func getUnixLocale() (unix_locale string, err error) {
+ unix_locale = getLangFromEnv()
+ if unix_locale == "" {
+ err = errors.New(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE)
+ }
+
+ return
+}
+
+func DetectIETF() (locale string, err error) {
+ unix_locale, err := getUnixLocale()
+ if err == nil {
+ language, territory := splitLocale(unix_locale)
+ locale = language
+ if territory != "" {
+ locale = strings.Join([]string{language, territory}, "-")
+ }
+ }
+
+ return
+}
+
+func DetectLanguage() (language string, err error) {
+ unix_locale, err := getUnixLocale()
+ if err == nil {
+ language, _ = splitLocale(unix_locale)
+ }
+
+ return
+}
+
+func DetectTerritory() (territory string, err error) {
+ unix_locale, err := getUnixLocale()
+ if err == nil {
+ _, territory = splitLocale(unix_locale)
+ }
+
+ return
+}
diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows.go b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows.go
new file mode 100644
index 000000000..1acd96c38
--- /dev/null
+++ b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows.go
@@ -0,0 +1,114 @@
+// +build windows
+
+package jibber_jabber
+
+import (
+ "errors"
+ "syscall"
+ "unsafe"
+)
+
+const LOCALE_NAME_MAX_LENGTH uint32 = 85
+
+var SUPPORTED_LOCALES = map[uintptr]string{
+ 0x0407: "de-DE",
+ 0x0409: "en-US",
+ 0x0c0a: "es-ES", //or is it 0x040a
+ 0x040c: "fr-FR",
+ 0x0410: "it-IT",
+ 0x0411: "ja-JA",
+ 0x0412: "ko_KR",
+ 0x0416: "pt-BR",
+ //0x0419: "ru_RU", - Will add support for Russian when nicksnyder/go-i18n supports Russian
+ 0x0804: "zh-CN",
+ 0x0c04: "zh-HK",
+ 0x0404: "zh-TW",
+}
+
+func getWindowsLocaleFrom(sysCall string) (locale string, err error) {
+ buffer := make([]uint16, LOCALE_NAME_MAX_LENGTH)
+
+ dll := syscall.MustLoadDLL("kernel32")
+ proc := dll.MustFindProc(sysCall)
+ r, _, dllError := proc.Call(uintptr(unsafe.Pointer(&buffer[0])), uintptr(LOCALE_NAME_MAX_LENGTH))
+ if r == 0 {
+ err = errors.New(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE + ":\n" + dllError.Error())
+ return
+ }
+
+ locale = syscall.UTF16ToString(buffer)
+
+ return
+}
+
+func getAllWindowsLocaleFrom(sysCall string) (string, error) {
+ dll, err := syscall.LoadDLL("kernel32")
+ if err != nil {
+ return "", errors.New("Could not find kernel32 dll")
+ }
+
+ proc, err := dll.FindProc(sysCall)
+ if err != nil {
+ return "", err
+ }
+
+ locale, _, dllError := proc.Call()
+ if locale == 0 {
+ return "", errors.New(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE + ":\n" + dllError.Error())
+ }
+
+ return SUPPORTED_LOCALES[locale], nil
+}
+
+func getWindowsLocale() (locale string, err error) {
+ dll, err := syscall.LoadDLL("kernel32")
+ if err != nil {
+ return "", errors.New("Could not find kernel32 dll")
+ }
+
+ proc, err := dll.FindProc("GetVersion")
+ if err != nil {
+ return "", err
+ }
+
+ v, _, _ := proc.Call()
+ windowsVersion := byte(v)
+ isVistaOrGreater := (windowsVersion >= 6)
+
+ if isVistaOrGreater {
+ locale, err = getWindowsLocaleFrom("GetUserDefaultLocaleName")
+ if err != nil {
+ locale, err = getWindowsLocaleFrom("GetSystemDefaultLocaleName")
+ }
+ } else if !isVistaOrGreater {
+ locale, err = getAllWindowsLocaleFrom("GetUserDefaultLCID")
+ if err != nil {
+ locale, err = getAllWindowsLocaleFrom("GetSystemDefaultLCID")
+ }
+ } else {
+ panic(v)
+ }
+ return
+}
+func DetectIETF() (locale string, err error) {
+ locale, err = getWindowsLocale()
+ return
+}
+
+func DetectLanguage() (language string, err error) {
+ windows_locale, err := getWindowsLocale()
+ if err == nil {
+ language, _ = splitLocale(windows_locale)
+ }
+
+ return
+}
+
+func DetectTerritory() (territory string, err error) {
+ windows_locale, err := getWindowsLocale()
+ if err == nil {
+ _, territory = splitLocale(windows_locale)
+ }
+
+ return
+}
diff --git a/vendor/github.com/dgryski/dgoogauth/.travis.yml b/vendor/github.com/dgryski/dgoogauth/.travis.yml
new file mode 100644
index 000000000..4f2ee4d97
--- /dev/null
+++ b/vendor/github.com/dgryski/dgoogauth/.travis.yml
@@ -0,0 +1 @@
+language: go
diff --git a/vendor/github.com/dgryski/dgoogauth/README.md b/vendor/github.com/dgryski/dgoogauth/README.md
new file mode 100644
index 000000000..75fdde78a
--- /dev/null
+++ b/vendor/github.com/dgryski/dgoogauth/README.md
@@ -0,0 +1,15 @@
+This is a Go implementation of the Google Authenticator library.
+
+[![Build Status](https://travis-ci.org/dgryski/dgoogauth.png)](https://travis-ci.org/dgryski/dgoogauth)
+
+Copyright (c) 2012 Damian Gryski <damian@gryski.com>
+This code is licensed under the Apache License, version 2.0
+
+It implements the one-time-password algorithms specified in:
+
+* RFC 4226 (HOTP: An HMAC-Based One-Time Password Algorithm)
+* RFC 6238 (TOTP: Time-Based One-Time Password Algorithm)
+
+You can learn more about the Google Authenticator library at its project page:
+
+* https://github.com/google/google-authenticator
diff --git a/vendor/github.com/dgryski/dgoogauth/googauth.go b/vendor/github.com/dgryski/dgoogauth/googauth.go
new file mode 100644
index 000000000..1efddcc20
--- /dev/null
+++ b/vendor/github.com/dgryski/dgoogauth/googauth.go
@@ -0,0 +1,199 @@
+/*
+Package dgoogauth implements the one-time password algorithms supported by Google Authenticator
+
+This package supports the HMAC-Based One-time Password (HOTP) algorithm
+specified in RFC 4226 and the Time-based One-time Password (TOTP) algorithm
+specified in RFC 6238.
+*/
+package dgoogauth
+
+import (
+ "crypto/hmac"
+ "crypto/sha1"
+ "encoding/base32"
+ "encoding/binary"
+ "errors"
+ "net/url"
+ "sort"
+ "strconv"
+ "time"
+)
+
+// Much of this code assumes int == int64, which probably is not the case.
+
+// ComputeCode computes the response code for a 64-bit challenge 'value' using the secret 'secret'.
+// To avoid breaking compatibility with the previous API, it returns an invalid code (-1) when an error occurs,
+// but does not silently ignore them (it forces a mismatch so the code will be rejected).
+func ComputeCode(secret string, value int64) int {
+
+ key, err := base32.StdEncoding.DecodeString(secret)
+ if err != nil {
+ return -1
+ }
+
+ hash := hmac.New(sha1.New, key)
+ err = binary.Write(hash, binary.BigEndian, value)
+ if err != nil {
+ return -1
+ }
+ h := hash.Sum(nil)
+
+ offset := h[19] & 0x0f
+
+ truncated := binary.BigEndian.Uint32(h[offset : offset+4])
+
+ truncated &= 0x7fffffff
+ code := truncated % 1000000
+
+ return int(code)
+}
+
+// ErrInvalidCode indicate the supplied one-time code was not valid
+var ErrInvalidCode = errors.New("invalid code")
+
+// OTPConfig is a one-time-password configuration. This object will be modified by calls to
+// Authenticate and should be saved to ensure the codes are in fact only used
+// once.
+type OTPConfig struct {
+ Secret string // 80-bit base32 encoded string of the user's secret
+ WindowSize int // valid range: technically 0..100 or so, but beyond 3-5 is probably bad security
+ HotpCounter int // the current otp counter. 0 if the user uses time-based codes instead.
+ DisallowReuse []int // timestamps in the current window unavailable for re-use
+ ScratchCodes []int // an array of 8-digit numeric codes that can be used to log in
+ UTC bool // use UTC for the timestamp instead of local time
+}
+
+func (c *OTPConfig) checkScratchCodes(code int) bool {
+
+ for i, v := range c.ScratchCodes {
+ if code == v {
+ // remove this code from the list of valid ones
+ l := len(c.ScratchCodes) - 1
+ c.ScratchCodes[i] = c.ScratchCodes[l] // copy last element over this element
+ c.ScratchCodes = c.ScratchCodes[0:l] // and trim the list length by 1
+ return true
+ }
+ }
+
+ return false
+}
+
+func (c *OTPConfig) checkHotpCode(code int) bool {
+
+ for i := 0; i < c.WindowSize; i++ {
+ if ComputeCode(c.Secret, int64(c.HotpCounter+i)) == code {
+ c.HotpCounter += i + 1
+ // We don't check for overflow here, which means you can only authenticate 2^63 times
+ // After that, the counter is negative and the above 'if' test will fail.
+ // This matches the behaviour of the PAM module.
+ return true
+ }
+ }
+
+ // we must always advance the counter if we tried to authenticate with it
+ c.HotpCounter++
+ return false
+}
+
+func (c *OTPConfig) checkTotpCode(t0, code int) bool {
+
+ minT := t0 - (c.WindowSize / 2)
+ maxT := t0 + (c.WindowSize / 2)
+ for t := minT; t <= maxT; t++ {
+ if ComputeCode(c.Secret, int64(t)) == code {
+
+ if c.DisallowReuse != nil {
+ for _, timeCode := range c.DisallowReuse {
+ if timeCode == t {
+ return false
+ }
+ }
+
+ // code hasn't been used before
+ c.DisallowReuse = append(c.DisallowReuse, t)
+
+ // remove all time codes outside of the valid window
+ sort.Ints(c.DisallowReuse)
+ min := 0
+ for c.DisallowReuse[min] < minT {
+ min++
+ }
+ // FIXME: check we don't have an off-by-one error here
+ c.DisallowReuse = c.DisallowReuse[min:]
+ }
+
+ return true
+ }
+ }
+
+ return false
+}
+
+// Authenticate a one-time-password against the given OTPConfig
+// Returns true/false if the authentication was successful.
+// Returns error if the password is incorrectly formatted (not a zero-padded 6 or non-zero-padded 8 digit number).
+func (c *OTPConfig) Authenticate(password string) (bool, error) {
+
+ var scratch bool
+
+ switch {
+ case len(password) == 6 && password[0] >= '0' && password[0] <= '9':
+ break
+ case len(password) == 8 && password[0] >= '1' && password[0] <= '9':
+ scratch = true
+ break
+ default:
+ return false, ErrInvalidCode
+ }
+
+ code, err := strconv.Atoi(password)
+
+ if err != nil {
+ return false, ErrInvalidCode
+ }
+
+ if scratch {
+ return c.checkScratchCodes(code), nil
+ }
+
+ // we have a counter value we can use
+ if c.HotpCounter > 0 {
+ return c.checkHotpCode(code), nil
+ }
+
+ var t0 int
+ // assume we're on Time-based OTP
+ if c.UTC {
+ t0 = int(time.Now().UTC().Unix() / 30)
+ } else {
+ t0 = int(time.Now().Unix() / 30)
+ }
+ return c.checkTotpCode(t0, code), nil
+}
+
+// ProvisionURI generates a URI that can be turned into a QR code to configure
+// a Google Authenticator mobile app.
+func (c *OTPConfig) ProvisionURI(user string) string {
+ return c.ProvisionURIWithIssuer(user, "")
+}
+
+// ProvisionURIWithIssuer generates a URI that can be turned into a QR code
+// to configure a Google Authenticator mobile app. It respects the recommendations
+// on how to avoid conflicting accounts.
+//
+// See https://code.google.com/p/google-authenticator/wiki/ConflictingAccounts
+func (c *OTPConfig) ProvisionURIWithIssuer(user string, issuer string) string {
+ auth := "totp/"
+ q := make(url.Values)
+ if c.HotpCounter > 0 {
+ auth = "hotp/"
+ q.Add("counter", strconv.Itoa(c.HotpCounter))
+ }
+ q.Add("secret", c.Secret)
+ if issuer != "" {
+ q.Add("issuer", issuer)
+ auth += issuer + ":"
+ }
+
+ return "otpauth://" + auth + user + "?" + q.Encode()
+}
diff --git a/vendor/github.com/disintegration/imaging/.travis.yml b/vendor/github.com/disintegration/imaging/.travis.yml
new file mode 100644
index 000000000..3bfcffd01
--- /dev/null
+++ b/vendor/github.com/disintegration/imaging/.travis.yml
@@ -0,0 +1,19 @@
+language: go
+
+sudo: false
+
+go:
+ - 1.2
+ - 1.3
+ - 1.4
+ - 1.5
+ - 1.6
+ - tip
+
+before_install:
+ - go get golang.org/x/tools/cmd/cover
+ - go get github.com/mattn/goveralls
+
+script:
+ - go test -v -covermode=count -coverprofile=coverage.out
+ - $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=coverage.out
diff --git a/vendor/github.com/disintegration/imaging/LICENSE b/vendor/github.com/disintegration/imaging/LICENSE
new file mode 100644
index 000000000..95ae410c3
--- /dev/null
+++ b/vendor/github.com/disintegration/imaging/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2012-2014 Grigory Dryapak
+
+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. \ No newline at end of file
diff --git a/vendor/github.com/disintegration/imaging/README.md b/vendor/github.com/disintegration/imaging/README.md
new file mode 100644
index 000000000..3dcea2000
--- /dev/null
+++ b/vendor/github.com/disintegration/imaging/README.md
@@ -0,0 +1,198 @@
+# Imaging
+
+[![GoDoc](https://godoc.org/github.com/disintegration/imaging?status.svg)](https://godoc.org/github.com/disintegration/imaging)
+[![Build Status](https://travis-ci.org/disintegration/imaging.svg?branch=master)](https://travis-ci.org/disintegration/imaging)
+[![Coverage Status](https://coveralls.io/repos/github/disintegration/imaging/badge.svg?branch=master)](https://coveralls.io/github/disintegration/imaging?branch=master)
+
+Package imaging provides basic image manipulation functions (resize, rotate, flip, crop, etc.).
+This package is based on the standard Go image package and works best along with it.
+
+Image manipulation functions provided by the package take any image type
+that implements `image.Image` interface as an input, and return a new image of
+`*image.NRGBA` type (32bit RGBA colors, not premultiplied by alpha).
+
+## Installation
+
+Imaging requires Go version 1.2 or greater.
+
+ go get -u github.com/disintegration/imaging
+
+## Documentation
+
+http://godoc.org/github.com/disintegration/imaging
+
+## Usage examples
+
+A few usage examples can be found below. See the documentation for the full list of supported functions.
+
+### Image resizing
+```go
+// resize srcImage to size = 128x128px using the Lanczos filter
+dstImage128 := imaging.Resize(srcImage, 128, 128, imaging.Lanczos)
+
+// resize srcImage to width = 800px preserving the aspect ratio
+dstImage800 := imaging.Resize(srcImage, 800, 0, imaging.Lanczos)
+
+// scale down srcImage to fit the 800x600px bounding box
+dstImageFit := imaging.Fit(srcImage, 800, 600, imaging.Lanczos)
+
+// resize and crop the srcImage to fill the 100x100px area
+dstImageFill := imaging.Fill(srcImage, 100, 100, imaging.Center, imaging.Lanczos)
+```
+
+Imaging supports image resizing using various resampling filters. The most notable ones:
+- `NearestNeighbor` - Fastest resampling filter, no antialiasing.
+- `Box` - Simple and fast averaging filter appropriate for downscaling. When upscaling it's similar to NearestNeighbor.
+- `Linear` - Bilinear filter, smooth and reasonably fast.
+- `MitchellNetravali` - А smooth bicubic filter.
+- `CatmullRom` - A sharp bicubic filter.
+- `Gaussian` - Blurring filter that uses gaussian function, useful for noise removal.
+- `Lanczos` - High-quality resampling filter for photographic images yielding sharp results, but it's slower than cubic filters.
+
+The full list of supported filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali, CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine. Custom filters can be created using ResampleFilter struct.
+
+**Resampling filters comparison**
+
+Original image. Will be resized from 512x512px to 128x128px.
+
+![srcImage](http://disintegration.github.io/imaging/in_lena_bw_512.png)
+
+Filter | Resize result
+---|---
+`imaging.NearestNeighbor` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_nearest.png)
+`imaging.Box` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_box.png)
+`imaging.Linear` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_linear.png)
+`imaging.MitchellNetravali` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_mitchell.png)
+`imaging.CatmullRom` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_catrom.png)
+`imaging.Gaussian` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_gaussian.png)
+`imaging.Lanczos` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_lanczos.png)
+
+**Resize functions comparison**
+
+Original image:
+
+![srcImage](http://disintegration.github.io/imaging/in.jpg)
+
+Resize the image to width=100px and height=100px:
+
+```go
+dstImage := imaging.Resize(srcImage, 100, 100, imaging.Lanczos)
+```
+![dstImage](http://disintegration.github.io/imaging/out-comp-resize.jpg)
+
+Resize the image to width=100px preserving the aspect ratio:
+
+```go
+dstImage := imaging.Resize(srcImage, 100, 0, imaging.Lanczos)
+```
+![dstImage](http://disintegration.github.io/imaging/out-comp-fit.jpg)
+
+Resize the image to fit the 100x100px boundng box preserving the aspect ratio:
+
+```go
+dstImage := imaging.Fit(srcImage, 100, 100, imaging.Lanczos)
+```
+![dstImage](http://disintegration.github.io/imaging/out-comp-fit.jpg)
+
+Resize and crop the image with a center anchor point to fill the 100x100px area:
+
+```go
+dstImage := imaging.Fill(srcImage, 100, 100, imaging.Center, imaging.Lanczos)
+```
+![dstImage](http://disintegration.github.io/imaging/out-comp-fill.jpg)
+
+### Gaussian Blur
+```go
+dstImage := imaging.Blur(srcImage, 0.5)
+```
+
+Sigma parameter allows to control the strength of the blurring effect.
+
+Original image | Sigma = 0.5 | Sigma = 1.5
+---|---|---
+![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_blur_0.5.png) | ![dstImage](http://disintegration.github.io/imaging/out_blur_1.5.png)
+
+### Sharpening
+```go
+dstImage := imaging.Sharpen(srcImage, 0.5)
+```
+
+Uses gaussian function internally. Sigma parameter allows to control the strength of the sharpening effect.
+
+Original image | Sigma = 0.5 | Sigma = 1.5
+---|---|---
+![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_sharpen_0.5.png) | ![dstImage](http://disintegration.github.io/imaging/out_sharpen_1.5.png)
+
+### Gamma correction
+```go
+dstImage := imaging.AdjustGamma(srcImage, 0.75)
+```
+
+Original image | Gamma = 0.75 | Gamma = 1.25
+---|---|---
+![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_gamma_0.75.png) | ![dstImage](http://disintegration.github.io/imaging/out_gamma_1.25.png)
+
+### Contrast adjustment
+```go
+dstImage := imaging.AdjustContrast(srcImage, 20)
+```
+
+Original image | Contrast = 20 | Contrast = -20
+---|---|---
+![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_contrast_p20.png) | ![dstImage](http://disintegration.github.io/imaging/out_contrast_m20.png)
+
+### Brightness adjustment
+```go
+dstImage := imaging.AdjustBrightness(srcImage, 20)
+```
+
+Original image | Brightness = 20 | Brightness = -20
+---|---|---
+![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_brightness_p20.png) | ![dstImage](http://disintegration.github.io/imaging/out_brightness_m20.png)
+
+
+### Complete code example
+Here is the code example that loads several images, makes thumbnails of them
+and combines them together side-by-side.
+
+```go
+package main
+
+import (
+ "image"
+ "image/color"
+
+ "github.com/disintegration/imaging"
+)
+
+func main() {
+
+ // input files
+ files := []string{"01.jpg", "02.jpg", "03.jpg"}
+
+ // load images and make 100x100 thumbnails of them
+ var thumbnails []image.Image
+ for _, file := range files {
+ img, err := imaging.Open(file)
+ if err != nil {
+ panic(err)
+ }
+ thumb := imaging.Thumbnail(img, 100, 100, imaging.CatmullRom)
+ thumbnails = append(thumbnails, thumb)
+ }
+
+ // create a new blank image
+ dst := imaging.New(100*len(thumbnails), 100, color.NRGBA{0, 0, 0, 0})
+
+ // paste thumbnails into the new image side by side
+ for i, thumb := range thumbnails {
+ dst = imaging.Paste(dst, thumb, image.Pt(i*100, 0))
+ }
+
+ // save the combined image to file
+ err := imaging.Save(dst, "dst.jpg")
+ if err != nil {
+ panic(err)
+ }
+}
+```
diff --git a/vendor/github.com/disintegration/imaging/adjust.go b/vendor/github.com/disintegration/imaging/adjust.go
new file mode 100644
index 000000000..9b1b83a4f
--- /dev/null
+++ b/vendor/github.com/disintegration/imaging/adjust.go
@@ -0,0 +1,200 @@
+package imaging
+
+import (
+ "image"
+ "image/color"
+ "math"
+)
+
+// AdjustFunc applies the fn function to each pixel of the img image and returns the adjusted image.
+//
+// Example:
+//
+// dstImage = imaging.AdjustFunc(
+// srcImage,
+// func(c color.NRGBA) color.NRGBA {
+// // shift the red channel by 16
+// r := int(c.R) + 16
+// if r > 255 {
+// r = 255
+// }
+// return color.NRGBA{uint8(r), c.G, c.B, c.A}
+// }
+// )
+//
+func AdjustFunc(img image.Image, fn func(c color.NRGBA) color.NRGBA) *image.NRGBA {
+ src := toNRGBA(img)
+ width := src.Bounds().Max.X
+ height := src.Bounds().Max.Y
+ dst := image.NewNRGBA(image.Rect(0, 0, width, height))
+
+ parallel(height, func(partStart, partEnd int) {
+ for y := partStart; y < partEnd; y++ {
+ for x := 0; x < width; x++ {
+ i := y*src.Stride + x*4
+ j := y*dst.Stride + x*4
+
+ r := src.Pix[i+0]
+ g := src.Pix[i+1]
+ b := src.Pix[i+2]
+ a := src.Pix[i+3]
+
+ c := fn(color.NRGBA{r, g, b, a})
+
+ dst.Pix[j+0] = c.R
+ dst.Pix[j+1] = c.G
+ dst.Pix[j+2] = c.B
+ dst.Pix[j+3] = c.A
+ }
+ }
+ })
+
+ return dst
+}
+
+// AdjustGamma performs a gamma correction on the image and returns the adjusted image.
+// Gamma parameter must be positive. Gamma = 1.0 gives the original image.
+// Gamma less than 1.0 darkens the image and gamma greater than 1.0 lightens it.
+//
+// Example:
+//
+// dstImage = imaging.AdjustGamma(srcImage, 0.7)
+//
+func AdjustGamma(img image.Image, gamma float64) *image.NRGBA {
+ e := 1.0 / math.Max(gamma, 0.0001)
+ lut := make([]uint8, 256)
+
+ for i := 0; i < 256; i++ {
+ lut[i] = clamp(math.Pow(float64(i)/255.0, e) * 255.0)
+ }
+
+ fn := func(c color.NRGBA) color.NRGBA {
+ return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
+ }
+
+ return AdjustFunc(img, fn)
+}
+
+func sigmoid(a, b, x float64) float64 {
+ return 1 / (1 + math.Exp(b*(a-x)))
+}
+
+// AdjustSigmoid changes the contrast of the image using a sigmoidal function and returns the adjusted image.
+// It's a non-linear contrast change useful for photo adjustments as it preserves highlight and shadow detail.
+// The midpoint parameter is the midpoint of contrast that must be between 0 and 1, typically 0.5.
+// The factor parameter indicates how much to increase or decrease the contrast, typically in range (-10, 10).
+// If the factor parameter is positive the image contrast is increased otherwise the contrast is decreased.
+//
+// Examples:
+//
+// dstImage = imaging.AdjustSigmoid(srcImage, 0.5, 3.0) // increase the contrast
+// dstImage = imaging.AdjustSigmoid(srcImage, 0.5, -3.0) // decrease the contrast
+//
+func AdjustSigmoid(img image.Image, midpoint, factor float64) *image.NRGBA {
+ if factor == 0 {
+ return Clone(img)
+ }
+
+ lut := make([]uint8, 256)
+ a := math.Min(math.Max(midpoint, 0.0), 1.0)
+ b := math.Abs(factor)
+ sig0 := sigmoid(a, b, 0)
+ sig1 := sigmoid(a, b, 1)
+ e := 1.0e-6
+
+ if factor > 0 {
+ for i := 0; i < 256; i++ {
+ x := float64(i) / 255.0
+ sigX := sigmoid(a, b, x)
+ f := (sigX - sig0) / (sig1 - sig0)
+ lut[i] = clamp(f * 255.0)
+ }
+ } else {
+ for i := 0; i < 256; i++ {
+ x := float64(i) / 255.0
+ arg := math.Min(math.Max((sig1-sig0)*x+sig0, e), 1.0-e)
+ f := a - math.Log(1.0/arg-1.0)/b
+ lut[i] = clamp(f * 255.0)
+ }
+ }
+
+ fn := func(c color.NRGBA) color.NRGBA {
+ return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
+ }
+
+ return AdjustFunc(img, fn)
+}
+
+// AdjustContrast changes the contrast of the image using the percentage parameter and returns the adjusted image.
+// The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
+// The percentage = -100 gives solid grey image.
+//
+// Examples:
+//
+// dstImage = imaging.AdjustContrast(srcImage, -10) // decrease image contrast by 10%
+// dstImage = imaging.AdjustContrast(srcImage, 20) // increase image contrast by 20%
+//
+func AdjustContrast(img image.Image, percentage float64) *image.NRGBA {
+ percentage = math.Min(math.Max(percentage, -100.0), 100.0)
+ lut := make([]uint8, 256)
+
+ v := (100.0 + percentage) / 100.0
+ for i := 0; i < 256; i++ {
+ if 0 <= v && v <= 1 {
+ lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*v) * 255.0)
+ } else if 1 < v && v < 2 {
+ lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*(1/(2.0-v))) * 255.0)
+ } else {
+ lut[i] = uint8(float64(i)/255.0+0.5) * 255
+ }
+ }
+
+ fn := func(c color.NRGBA) color.NRGBA {
+ return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
+ }
+
+ return AdjustFunc(img, fn)
+}
+
+// AdjustBrightness changes the brightness of the image using the percentage parameter and returns the adjusted image.
+// The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
+// The percentage = -100 gives solid black image. The percentage = 100 gives solid white image.
+//
+// Examples:
+//
+// dstImage = imaging.AdjustBrightness(srcImage, -15) // decrease image brightness by 15%
+// dstImage = imaging.AdjustBrightness(srcImage, 10) // increase image brightness by 10%
+//
+func AdjustBrightness(img image.Image, percentage float64) *image.NRGBA {
+ percentage = math.Min(math.Max(percentage, -100.0), 100.0)
+ lut := make([]uint8, 256)
+
+ shift := 255.0 * percentage / 100.0
+ for i := 0; i < 256; i++ {
+ lut[i] = clamp(float64(i) + shift)
+ }
+
+ fn := func(c color.NRGBA) color.NRGBA {
+ return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
+ }
+
+ return AdjustFunc(img, fn)
+}
+
+// Grayscale produces grayscale version of the image.
+func Grayscale(img image.Image) *image.NRGBA {
+ fn := func(c color.NRGBA) color.NRGBA {
+ f := 0.299*float64(c.R) + 0.587*float64(c.G) + 0.114*float64(c.B)
+ y := uint8(f + 0.5)
+ return color.NRGBA{y, y, y, c.A}
+ }
+ return AdjustFunc(img, fn)
+}
+
+// Invert produces inverted (negated) version of the image.
+func Invert(img image.Image) *image.NRGBA {
+ fn := func(c color.NRGBA) color.NRGBA {
+ return color.NRGBA{255 - c.R, 255 - c.G, 255 - c.B, c.A}
+ }
+ return AdjustFunc(img, fn)
+}
diff --git a/vendor/github.com/disintegration/imaging/effects.go b/vendor/github.com/disintegration/imaging/effects.go
new file mode 100644
index 000000000..fe92e10a2
--- /dev/null
+++ b/vendor/github.com/disintegration/imaging/effects.go
@@ -0,0 +1,187 @@
+package imaging
+
+import (
+ "image"
+ "math"
+)
+
+func gaussianBlurKernel(x, sigma float64) float64 {
+ return math.Exp(-(x*x)/(2*sigma*sigma)) / (sigma * math.Sqrt(2*math.Pi))
+}
+
+// Blur produces a blurred version of the image using a Gaussian function.
+// Sigma parameter must be positive and indicates how much the image will be blurred.
+//
+// Usage example:
+//
+// dstImage := imaging.Blur(srcImage, 3.5)
+//
+func Blur(img image.Image, sigma float64) *image.NRGBA {
+ if sigma <= 0 {
+ // sigma parameter must be positive!
+ return Clone(img)
+ }
+
+ src := toNRGBA(img)
+ radius := int(math.Ceil(sigma * 3.0))
+ kernel := make([]float64, radius+1)
+
+ for i := 0; i <= radius; i++ {
+ kernel[i] = gaussianBlurKernel(float64(i), sigma)
+ }
+
+ var dst *image.NRGBA
+ dst = blurHorizontal(src, kernel)
+ dst = blurVertical(dst, kernel)
+
+ return dst
+}
+
+func blurHorizontal(src *image.NRGBA, kernel []float64) *image.NRGBA {
+ radius := len(kernel) - 1
+ width := src.Bounds().Max.X
+ height := src.Bounds().Max.Y
+
+ dst := image.NewNRGBA(image.Rect(0, 0, width, height))
+
+ parallel(width, func(partStart, partEnd int) {
+ for x := partStart; x < partEnd; x++ {
+ start := x - radius
+ if start < 0 {
+ start = 0
+ }
+
+ end := x + radius
+ if end > width-1 {
+ end = width - 1
+ }
+
+ weightSum := 0.0
+ for ix := start; ix <= end; ix++ {
+ weightSum += kernel[absint(x-ix)]
+ }
+
+ for y := 0; y < height; y++ {
+
+ r, g, b, a := 0.0, 0.0, 0.0, 0.0
+ for ix := start; ix <= end; ix++ {
+ weight := kernel[absint(x-ix)]
+ i := y*src.Stride + ix*4
+ r += float64(src.Pix[i+0]) * weight
+ g += float64(src.Pix[i+1]) * weight
+ b += float64(src.Pix[i+2]) * weight
+ a += float64(src.Pix[i+3]) * weight
+ }
+
+ r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
+ g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
+ b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
+ a = math.Min(math.Max(a/weightSum, 0.0), 255.0)
+
+ j := y*dst.Stride + x*4
+ dst.Pix[j+0] = uint8(r + 0.5)
+ dst.Pix[j+1] = uint8(g + 0.5)
+ dst.Pix[j+2] = uint8(b + 0.5)
+ dst.Pix[j+3] = uint8(a + 0.5)
+
+ }
+ }
+ })
+
+ return dst
+}
+
+func blurVertical(src *image.NRGBA, kernel []float64) *image.NRGBA {
+ radius := len(kernel) - 1
+ width := src.Bounds().Max.X
+ height := src.Bounds().Max.Y
+
+ dst := image.NewNRGBA(image.Rect(0, 0, width, height))
+
+ parallel(height, func(partStart, partEnd int) {
+ for y := partStart; y < partEnd; y++ {
+ start := y - radius
+ if start < 0 {
+ start = 0
+ }
+
+ end := y + radius
+ if end > height-1 {
+ end = height - 1
+ }
+
+ weightSum := 0.0
+ for iy := start; iy <= end; iy++ {
+ weightSum += kernel[absint(y-iy)]
+ }
+
+ for x := 0; x < width; x++ {
+
+ r, g, b, a := 0.0, 0.0, 0.0, 0.0
+ for iy := start; iy <= end; iy++ {
+ weight := kernel[absint(y-iy)]
+ i := iy*src.Stride + x*4
+ r += float64(src.Pix[i+0]) * weight
+ g += float64(src.Pix[i+1]) * weight
+ b += float64(src.Pix[i+2]) * weight
+ a += float64(src.Pix[i+3]) * weight
+ }
+
+ r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
+ g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
+ b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
+ a = math.Min(math.Max(a/weightSum, 0.0), 255.0)
+
+ j := y*dst.Stride + x*4
+ dst.Pix[j+0] = uint8(r + 0.5)
+ dst.Pix[j+1] = uint8(g + 0.5)
+ dst.Pix[j+2] = uint8(b + 0.5)
+ dst.Pix[j+3] = uint8(a + 0.5)
+
+ }
+ }
+ })
+
+ return dst
+}
+
+// Sharpen produces a sharpened version of the image.
+// Sigma parameter must be positive and indicates how much the image will be sharpened.
+//
+// Usage example:
+//
+// dstImage := imaging.Sharpen(srcImage, 3.5)
+//
+func Sharpen(img image.Image, sigma float64) *image.NRGBA {
+ if sigma <= 0 {
+ // sigma parameter must be positive!
+ return Clone(img)
+ }
+
+ src := toNRGBA(img)
+ blurred := Blur(img, sigma)
+
+ width := src.Bounds().Max.X
+ height := src.Bounds().Max.Y
+ dst := image.NewNRGBA(image.Rect(0, 0, width, height))
+
+ parallel(height, func(partStart, partEnd int) {
+ for y := partStart; y < partEnd; y++ {
+ for x := 0; x < width; x++ {
+ i := y*src.Stride + x*4
+ for j := 0; j < 4; j++ {
+ k := i + j
+ val := int(src.Pix[k]) + (int(src.Pix[k]) - int(blurred.Pix[k]))
+ if val < 0 {
+ val = 0
+ } else if val > 255 {
+ val = 255
+ }
+ dst.Pix[k] = uint8(val)
+ }
+ }
+ }
+ })
+
+ return dst
+}
diff --git a/vendor/github.com/disintegration/imaging/helpers.go b/vendor/github.com/disintegration/imaging/helpers.go
new file mode 100644
index 000000000..79967ae44
--- /dev/null
+++ b/vendor/github.com/disintegration/imaging/helpers.go
@@ -0,0 +1,400 @@
+/*
+Package imaging provides basic image manipulation functions (resize, rotate, flip, crop, etc.).
+This package is based on the standard Go image package and works best along with it.
+
+Image manipulation functions provided by the package take any image type
+that implements `image.Image` interface as an input, and return a new image of
+`*image.NRGBA` type (32bit RGBA colors, not premultiplied by alpha).
+*/
+package imaging
+
+import (
+ "errors"
+ "image"
+ "image/color"
+ "image/gif"
+ "image/jpeg"
+ "image/png"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "golang.org/x/image/bmp"
+ "golang.org/x/image/tiff"
+)
+
+type Format int
+
+const (
+ JPEG Format = iota
+ PNG
+ GIF
+ TIFF
+ BMP
+)
+
+func (f Format) String() string {
+ switch f {
+ case JPEG:
+ return "JPEG"
+ case PNG:
+ return "PNG"
+ case GIF:
+ return "GIF"
+ case TIFF:
+ return "TIFF"
+ case BMP:
+ return "BMP"
+ default:
+ return "Unsupported"
+ }
+}
+
+var (
+ ErrUnsupportedFormat = errors.New("imaging: unsupported image format")
+)
+
+// Decode reads an image from r.
+func Decode(r io.Reader) (image.Image, error) {
+ img, _, err := image.Decode(r)
+ if err != nil {
+ return nil, err
+ }
+ return toNRGBA(img), nil
+}
+
+// Open loads an image from file
+func Open(filename string) (image.Image, error) {
+ file, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+ img, err := Decode(file)
+ return img, err
+}
+
+// Encode writes the image img to w in the specified format (JPEG, PNG, GIF, TIFF or BMP).
+func Encode(w io.Writer, img image.Image, format Format) error {
+ var err error
+ switch format {
+ case JPEG:
+ var rgba *image.RGBA
+ if nrgba, ok := img.(*image.NRGBA); ok {
+ if nrgba.Opaque() {
+ rgba = &image.RGBA{
+ Pix: nrgba.Pix,
+ Stride: nrgba.Stride,
+ Rect: nrgba.Rect,
+ }
+ }
+ }
+ if rgba != nil {
+ err = jpeg.Encode(w, rgba, &jpeg.Options{Quality: 95})
+ } else {
+ err = jpeg.Encode(w, img, &jpeg.Options{Quality: 95})
+ }
+
+ case PNG:
+ err = png.Encode(w, img)
+ case GIF:
+ err = gif.Encode(w, img, &gif.Options{NumColors: 256})
+ case TIFF:
+ err = tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true})
+ case BMP:
+ err = bmp.Encode(w, img)
+ default:
+ err = ErrUnsupportedFormat
+ }
+ return err
+}
+
+// Save saves the image to file with the specified filename.
+// The format is determined from the filename extension: "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
+func Save(img image.Image, filename string) (err error) {
+ formats := map[string]Format{
+ ".jpg": JPEG,
+ ".jpeg": JPEG,
+ ".png": PNG,
+ ".tif": TIFF,
+ ".tiff": TIFF,
+ ".bmp": BMP,
+ ".gif": GIF,
+ }
+
+ ext := strings.ToLower(filepath.Ext(filename))
+ f, ok := formats[ext]
+ if !ok {
+ return ErrUnsupportedFormat
+ }
+
+ file, err := os.Create(filename)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ return Encode(file, img, f)
+}
+
+// New creates a new image with the specified width and height, and fills it with the specified color.
+func New(width, height int, fillColor color.Color) *image.NRGBA {
+ if width <= 0 || height <= 0 {
+ return &image.NRGBA{}
+ }
+
+ dst := image.NewNRGBA(image.Rect(0, 0, width, height))
+ c := color.NRGBAModel.Convert(fillColor).(color.NRGBA)
+
+ if c.R == 0 && c.G == 0 && c.B == 0 && c.A == 0 {
+ return dst
+ }
+
+ cs := []uint8{c.R, c.G, c.B, c.A}
+
+ // fill the first row
+ for x := 0; x < width; x++ {
+ copy(dst.Pix[x*4:(x+1)*4], cs)
+ }
+ // copy the first row to other rows
+ for y := 1; y < height; y++ {
+ copy(dst.Pix[y*dst.Stride:y*dst.Stride+width*4], dst.Pix[0:width*4])
+ }
+
+ return dst
+}
+
+// Clone returns a copy of the given image.
+func Clone(img image.Image) *image.NRGBA {
+ srcBounds := img.Bounds()
+ srcMinX := srcBounds.Min.X
+ srcMinY := srcBounds.Min.Y
+
+ dstBounds := srcBounds.Sub(srcBounds.Min)
+ dstW := dstBounds.Dx()
+ dstH := dstBounds.Dy()
+ dst := image.NewNRGBA(dstBounds)
+
+ switch src := img.(type) {
+
+ case *image.NRGBA:
+ rowSize := srcBounds.Dx() * 4
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ si := src.PixOffset(srcMinX, srcMinY+dstY)
+ copy(dst.Pix[di:di+rowSize], src.Pix[si:si+rowSize])
+ }
+ })
+
+ case *image.NRGBA64:
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ si := src.PixOffset(srcMinX, srcMinY+dstY)
+ for dstX := 0; dstX < dstW; dstX++ {
+
+ dst.Pix[di+0] = src.Pix[si+0]
+ dst.Pix[di+1] = src.Pix[si+2]
+ dst.Pix[di+2] = src.Pix[si+4]
+ dst.Pix[di+3] = src.Pix[si+6]
+
+ di += 4
+ si += 8
+
+ }
+ }
+ })
+
+ case *image.RGBA:
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ si := src.PixOffset(srcMinX, srcMinY+dstY)
+ for dstX := 0; dstX < dstW; dstX++ {
+
+ a := src.Pix[si+3]
+ dst.Pix[di+3] = a
+ switch a {
+ case 0:
+ dst.Pix[di+0] = 0
+ dst.Pix[di+1] = 0
+ dst.Pix[di+2] = 0
+ case 0xff:
+ dst.Pix[di+0] = src.Pix[si+0]
+ dst.Pix[di+1] = src.Pix[si+1]
+ dst.Pix[di+2] = src.Pix[si+2]
+ default:
+ var tmp uint16
+ tmp = uint16(src.Pix[si+0]) * 0xff / uint16(a)
+ dst.Pix[di+0] = uint8(tmp)
+ tmp = uint16(src.Pix[si+1]) * 0xff / uint16(a)
+ dst.Pix[di+1] = uint8(tmp)
+ tmp = uint16(src.Pix[si+2]) * 0xff / uint16(a)
+ dst.Pix[di+2] = uint8(tmp)
+ }
+
+ di += 4
+ si += 4
+
+ }
+ }
+ })
+
+ case *image.RGBA64:
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ si := src.PixOffset(srcMinX, srcMinY+dstY)
+ for dstX := 0; dstX < dstW; dstX++ {
+
+ a := src.Pix[si+6]
+ dst.Pix[di+3] = a
+ switch a {
+ case 0:
+ dst.Pix[di+0] = 0
+ dst.Pix[di+1] = 0
+ dst.Pix[di+2] = 0
+ case 0xff:
+ dst.Pix[di+0] = src.Pix[si+0]
+ dst.Pix[di+1] = src.Pix[si+2]
+ dst.Pix[di+2] = src.Pix[si+4]
+ default:
+ var tmp uint16
+ tmp = uint16(src.Pix[si+0]) * 0xff / uint16(a)
+ dst.Pix[di+0] = uint8(tmp)
+ tmp = uint16(src.Pix[si+2]) * 0xff / uint16(a)
+ dst.Pix[di+1] = uint8(tmp)
+ tmp = uint16(src.Pix[si+4]) * 0xff / uint16(a)
+ dst.Pix[di+2] = uint8(tmp)
+ }
+
+ di += 4
+ si += 8
+
+ }
+ }
+ })
+
+ case *image.Gray:
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ si := src.PixOffset(srcMinX, srcMinY+dstY)
+ for dstX := 0; dstX < dstW; dstX++ {
+
+ c := src.Pix[si]
+ dst.Pix[di+0] = c
+ dst.Pix[di+1] = c
+ dst.Pix[di+2] = c
+ dst.Pix[di+3] = 0xff
+
+ di += 4
+ si += 1
+
+ }
+ }
+ })
+
+ case *image.Gray16:
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ si := src.PixOffset(srcMinX, srcMinY+dstY)
+ for dstX := 0; dstX < dstW; dstX++ {
+
+ c := src.Pix[si]
+ dst.Pix[di+0] = c
+ dst.Pix[di+1] = c
+ dst.Pix[di+2] = c
+ dst.Pix[di+3] = 0xff
+
+ di += 4
+ si += 2
+
+ }
+ }
+ })
+
+ case *image.YCbCr:
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ for dstX := 0; dstX < dstW; dstX++ {
+
+ srcX := srcMinX + dstX
+ srcY := srcMinY + dstY
+ siy := src.YOffset(srcX, srcY)
+ sic := src.COffset(srcX, srcY)
+ r, g, b := color.YCbCrToRGB(src.Y[siy], src.Cb[sic], src.Cr[sic])
+ dst.Pix[di+0] = r
+ dst.Pix[di+1] = g
+ dst.Pix[di+2] = b
+ dst.Pix[di+3] = 0xff
+
+ di += 4
+
+ }
+ }
+ })
+
+ case *image.Paletted:
+ plen := len(src.Palette)
+ pnew := make([]color.NRGBA, plen)
+ for i := 0; i < plen; i++ {
+ pnew[i] = color.NRGBAModel.Convert(src.Palette[i]).(color.NRGBA)
+ }
+
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ si := src.PixOffset(srcMinX, srcMinY+dstY)
+ for dstX := 0; dstX < dstW; dstX++ {
+
+ c := pnew[src.Pix[si]]
+ dst.Pix[di+0] = c.R
+ dst.Pix[di+1] = c.G
+ dst.Pix[di+2] = c.B
+ dst.Pix[di+3] = c.A
+
+ di += 4
+ si += 1
+
+ }
+ }
+ })
+
+ default:
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ for dstX := 0; dstX < dstW; dstX++ {
+
+ c := color.NRGBAModel.Convert(img.At(srcMinX+dstX, srcMinY+dstY)).(color.NRGBA)
+ dst.Pix[di+0] = c.R
+ dst.Pix[di+1] = c.G
+ dst.Pix[di+2] = c.B
+ dst.Pix[di+3] = c.A
+
+ di += 4
+
+ }
+ }
+ })
+
+ }
+
+ return dst
+}
+
+// This function used internally to convert any image type to NRGBA if needed.
+func toNRGBA(img image.Image) *image.NRGBA {
+ srcBounds := img.Bounds()
+ if srcBounds.Min.X == 0 && srcBounds.Min.Y == 0 {
+ if src0, ok := img.(*image.NRGBA); ok {
+ return src0
+ }
+ }
+ return Clone(img)
+}
diff --git a/vendor/github.com/disintegration/imaging/resize.go b/vendor/github.com/disintegration/imaging/resize.go
new file mode 100644
index 000000000..3c792e904
--- /dev/null
+++ b/vendor/github.com/disintegration/imaging/resize.go
@@ -0,0 +1,583 @@
+package imaging
+
+import (
+ "image"
+ "math"
+)
+
+type iwpair struct {
+ i int
+ w int32
+}
+
+type pweights struct {
+ iwpairs []iwpair
+ wsum int32
+}
+
+func precomputeWeights(dstSize, srcSize int, filter ResampleFilter) []pweights {
+ du := float64(srcSize) / float64(dstSize)
+ scale := du
+ if scale < 1.0 {
+ scale = 1.0
+ }
+ ru := math.Ceil(scale * filter.Support)
+
+ out := make([]pweights, dstSize)
+
+ for v := 0; v < dstSize; v++ {
+ fu := (float64(v)+0.5)*du - 0.5
+
+ startu := int(math.Ceil(fu - ru))
+ if startu < 0 {
+ startu = 0
+ }
+ endu := int(math.Floor(fu + ru))
+ if endu > srcSize-1 {
+ endu = srcSize - 1
+ }
+
+ wsum := int32(0)
+ for u := startu; u <= endu; u++ {
+ w := int32(0xff * filter.Kernel((float64(u)-fu)/scale))
+ if w != 0 {
+ wsum += w
+ out[v].iwpairs = append(out[v].iwpairs, iwpair{u, w})
+ }
+ }
+ out[v].wsum = wsum
+ }
+
+ return out
+}
+
+// Resize resizes the image to the specified width and height using the specified resampling
+// filter and returns the transformed image. If one of width or height is 0, the image aspect
+// ratio is preserved.
+//
+// Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
+// CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
+//
+// Usage example:
+//
+// dstImage := imaging.Resize(srcImage, 800, 600, imaging.Lanczos)
+//
+func Resize(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
+ dstW, dstH := width, height
+
+ if dstW < 0 || dstH < 0 {
+ return &image.NRGBA{}
+ }
+ if dstW == 0 && dstH == 0 {
+ return &image.NRGBA{}
+ }
+
+ src := toNRGBA(img)
+
+ srcW := src.Bounds().Max.X
+ srcH := src.Bounds().Max.Y
+
+ if srcW <= 0 || srcH <= 0 {
+ return &image.NRGBA{}
+ }
+
+ // if new width or height is 0 then preserve aspect ratio, minimum 1px
+ if dstW == 0 {
+ tmpW := float64(dstH) * float64(srcW) / float64(srcH)
+ dstW = int(math.Max(1.0, math.Floor(tmpW+0.5)))
+ }
+ if dstH == 0 {
+ tmpH := float64(dstW) * float64(srcH) / float64(srcW)
+ dstH = int(math.Max(1.0, math.Floor(tmpH+0.5)))
+ }
+
+ var dst *image.NRGBA
+
+ if filter.Support <= 0.0 {
+ // nearest-neighbor special case
+ dst = resizeNearest(src, dstW, dstH)
+
+ } else {
+ // two-pass resize
+ if srcW != dstW {
+ dst = resizeHorizontal(src, dstW, filter)
+ } else {
+ dst = src
+ }
+
+ if srcH != dstH {
+ dst = resizeVertical(dst, dstH, filter)
+ }
+ }
+
+ return dst
+}
+
+func resizeHorizontal(src *image.NRGBA, width int, filter ResampleFilter) *image.NRGBA {
+ srcBounds := src.Bounds()
+ srcW := srcBounds.Max.X
+ srcH := srcBounds.Max.Y
+
+ dstW := width
+ dstH := srcH
+
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ weights := precomputeWeights(dstW, srcW, filter)
+
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ for dstX := 0; dstX < dstW; dstX++ {
+ var c [4]int32
+ for _, iw := range weights[dstX].iwpairs {
+ i := dstY*src.Stride + iw.i*4
+ c[0] += int32(src.Pix[i+0]) * iw.w
+ c[1] += int32(src.Pix[i+1]) * iw.w
+ c[2] += int32(src.Pix[i+2]) * iw.w
+ c[3] += int32(src.Pix[i+3]) * iw.w
+ }
+ j := dstY*dst.Stride + dstX*4
+ sum := weights[dstX].wsum
+ dst.Pix[j+0] = clampint32(int32(float32(c[0])/float32(sum) + 0.5))
+ dst.Pix[j+1] = clampint32(int32(float32(c[1])/float32(sum) + 0.5))
+ dst.Pix[j+2] = clampint32(int32(float32(c[2])/float32(sum) + 0.5))
+ dst.Pix[j+3] = clampint32(int32(float32(c[3])/float32(sum) + 0.5))
+ }
+ }
+ })
+
+ return dst
+}
+
+func resizeVertical(src *image.NRGBA, height int, filter ResampleFilter) *image.NRGBA {
+ srcBounds := src.Bounds()
+ srcW := srcBounds.Max.X
+ srcH := srcBounds.Max.Y
+
+ dstW := srcW
+ dstH := height
+
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ weights := precomputeWeights(dstH, srcH, filter)
+
+ parallel(dstW, func(partStart, partEnd int) {
+
+ for dstX := partStart; dstX < partEnd; dstX++ {
+ for dstY := 0; dstY < dstH; dstY++ {
+ var c [4]int32
+ for _, iw := range weights[dstY].iwpairs {
+ i := iw.i*src.Stride + dstX*4
+ c[0] += int32(src.Pix[i+0]) * iw.w
+ c[1] += int32(src.Pix[i+1]) * iw.w
+ c[2] += int32(src.Pix[i+2]) * iw.w
+ c[3] += int32(src.Pix[i+3]) * iw.w
+ }
+ j := dstY*dst.Stride + dstX*4
+ sum := weights[dstY].wsum
+ dst.Pix[j+0] = clampint32(int32(float32(c[0])/float32(sum) + 0.5))
+ dst.Pix[j+1] = clampint32(int32(float32(c[1])/float32(sum) + 0.5))
+ dst.Pix[j+2] = clampint32(int32(float32(c[2])/float32(sum) + 0.5))
+ dst.Pix[j+3] = clampint32(int32(float32(c[3])/float32(sum) + 0.5))
+ }
+ }
+
+ })
+
+ return dst
+}
+
+// fast nearest-neighbor resize, no filtering
+func resizeNearest(src *image.NRGBA, width, height int) *image.NRGBA {
+ dstW, dstH := width, height
+
+ srcBounds := src.Bounds()
+ srcW := srcBounds.Max.X
+ srcH := srcBounds.Max.Y
+
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ dx := float64(srcW) / float64(dstW)
+ dy := float64(srcH) / float64(dstH)
+
+ parallel(dstH, func(partStart, partEnd int) {
+
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ fy := (float64(dstY)+0.5)*dy - 0.5
+
+ for dstX := 0; dstX < dstW; dstX++ {
+ fx := (float64(dstX)+0.5)*dx - 0.5
+
+ srcX := int(math.Min(math.Max(math.Floor(fx+0.5), 0.0), float64(srcW)))
+ srcY := int(math.Min(math.Max(math.Floor(fy+0.5), 0.0), float64(srcH)))
+
+ srcOff := srcY*src.Stride + srcX*4
+ dstOff := dstY*dst.Stride + dstX*4
+
+ copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
+ }
+ }
+
+ })
+
+ return dst
+}
+
+// Fit scales down the image using the specified resample filter to fit the specified
+// maximum width and height and returns the transformed image.
+//
+// Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
+// CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
+//
+// Usage example:
+//
+// dstImage := imaging.Fit(srcImage, 800, 600, imaging.Lanczos)
+//
+func Fit(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
+ maxW, maxH := width, height
+
+ if maxW <= 0 || maxH <= 0 {
+ return &image.NRGBA{}
+ }
+
+ srcBounds := img.Bounds()
+ srcW := srcBounds.Dx()
+ srcH := srcBounds.Dy()
+
+ if srcW <= 0 || srcH <= 0 {
+ return &image.NRGBA{}
+ }
+
+ if srcW <= maxW && srcH <= maxH {
+ return Clone(img)
+ }
+
+ srcAspectRatio := float64(srcW) / float64(srcH)
+ maxAspectRatio := float64(maxW) / float64(maxH)
+
+ var newW, newH int
+ if srcAspectRatio > maxAspectRatio {
+ newW = maxW
+ newH = int(float64(newW) / srcAspectRatio)
+ } else {
+ newH = maxH
+ newW = int(float64(newH) * srcAspectRatio)
+ }
+
+ return Resize(img, newW, newH, filter)
+}
+
+// Fill scales the image to the smallest possible size that will cover the specified dimensions,
+// crops the resized image to the specified dimensions using the given anchor point and returns
+// the transformed image.
+//
+// Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
+// CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
+//
+// Usage example:
+//
+// dstImage := imaging.Fill(srcImage, 800, 600, imaging.Center, imaging.Lanczos)
+//
+func Fill(img image.Image, width, height int, anchor Anchor, filter ResampleFilter) *image.NRGBA {
+ minW, minH := width, height
+
+ if minW <= 0 || minH <= 0 {
+ return &image.NRGBA{}
+ }
+
+ srcBounds := img.Bounds()
+ srcW := srcBounds.Dx()
+ srcH := srcBounds.Dy()
+
+ if srcW <= 0 || srcH <= 0 {
+ return &image.NRGBA{}
+ }
+
+ if srcW == minW && srcH == minH {
+ return Clone(img)
+ }
+
+ srcAspectRatio := float64(srcW) / float64(srcH)
+ minAspectRatio := float64(minW) / float64(minH)
+
+ var tmp *image.NRGBA
+ if srcAspectRatio < minAspectRatio {
+ tmp = Resize(img, minW, 0, filter)
+ } else {
+ tmp = Resize(img, 0, minH, filter)
+ }
+
+ return CropAnchor(tmp, minW, minH, anchor)
+}
+
+// Thumbnail scales the image up or down using the specified resample filter, crops it
+// to the specified width and hight and returns the transformed image.
+//
+// Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
+// CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
+//
+// Usage example:
+//
+// dstImage := imaging.Thumbnail(srcImage, 100, 100, imaging.Lanczos)
+//
+func Thumbnail(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
+ return Fill(img, width, height, Center, filter)
+}
+
+// Resample filter struct. It can be used to make custom filters.
+//
+// Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
+// CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
+//
+// General filter recommendations:
+//
+// - Lanczos
+// Probably the best resampling filter for photographic images yielding sharp results,
+// but it's slower than cubic filters (see below).
+//
+// - CatmullRom
+// A sharp cubic filter. It's a good filter for both upscaling and downscaling if sharp results are needed.
+//
+// - MitchellNetravali
+// A high quality cubic filter that produces smoother results with less ringing than CatmullRom.
+//
+// - BSpline
+// A good filter if a very smooth output is needed.
+//
+// - Linear
+// Bilinear interpolation filter, produces reasonably good, smooth output. It's faster than cubic filters.
+//
+// - Box
+// Simple and fast resampling filter appropriate for downscaling.
+// When upscaling it's similar to NearestNeighbor.
+//
+// - NearestNeighbor
+// Fastest resample filter, no antialiasing at all. Rarely used.
+//
+type ResampleFilter struct {
+ Support float64
+ Kernel func(float64) float64
+}
+
+// Nearest-neighbor filter, no anti-aliasing.
+var NearestNeighbor ResampleFilter
+
+// Box filter (averaging pixels).
+var Box ResampleFilter
+
+// Linear filter.
+var Linear ResampleFilter
+
+// Hermite cubic spline filter (BC-spline; B=0; C=0).
+var Hermite ResampleFilter
+
+// Mitchell-Netravali cubic filter (BC-spline; B=1/3; C=1/3).
+var MitchellNetravali ResampleFilter
+
+// Catmull-Rom - sharp cubic filter (BC-spline; B=0; C=0.5).
+var CatmullRom ResampleFilter
+
+// Cubic B-spline - smooth cubic filter (BC-spline; B=1; C=0).
+var BSpline ResampleFilter
+
+// Gaussian Blurring Filter.
+var Gaussian ResampleFilter
+
+// Bartlett-windowed sinc filter (3 lobes).
+var Bartlett ResampleFilter
+
+// Lanczos filter (3 lobes).
+var Lanczos ResampleFilter
+
+// Hann-windowed sinc filter (3 lobes).
+var Hann ResampleFilter
+
+// Hamming-windowed sinc filter (3 lobes).
+var Hamming ResampleFilter
+
+// Blackman-windowed sinc filter (3 lobes).
+var Blackman ResampleFilter
+
+// Welch-windowed sinc filter (parabolic window, 3 lobes).
+var Welch ResampleFilter
+
+// Cosine-windowed sinc filter (3 lobes).
+var Cosine ResampleFilter
+
+func bcspline(x, b, c float64) float64 {
+ x = math.Abs(x)
+ if x < 1.0 {
+ return ((12-9*b-6*c)*x*x*x + (-18+12*b+6*c)*x*x + (6 - 2*b)) / 6
+ }
+ if x < 2.0 {
+ return ((-b-6*c)*x*x*x + (6*b+30*c)*x*x + (-12*b-48*c)*x + (8*b + 24*c)) / 6
+ }
+ return 0
+}
+
+func sinc(x float64) float64 {
+ if x == 0 {
+ return 1
+ }
+ return math.Sin(math.Pi*x) / (math.Pi * x)
+}
+
+func init() {
+ NearestNeighbor = ResampleFilter{
+ Support: 0.0, // special case - not applying the filter
+ }
+
+ Box = ResampleFilter{
+ Support: 0.5,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x <= 0.5 {
+ return 1.0
+ }
+ return 0
+ },
+ }
+
+ Linear = ResampleFilter{
+ Support: 1.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 1.0 {
+ return 1.0 - x
+ }
+ return 0
+ },
+ }
+
+ Hermite = ResampleFilter{
+ Support: 1.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 1.0 {
+ return bcspline(x, 0.0, 0.0)
+ }
+ return 0
+ },
+ }
+
+ MitchellNetravali = ResampleFilter{
+ Support: 2.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 2.0 {
+ return bcspline(x, 1.0/3.0, 1.0/3.0)
+ }
+ return 0
+ },
+ }
+
+ CatmullRom = ResampleFilter{
+ Support: 2.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 2.0 {
+ return bcspline(x, 0.0, 0.5)
+ }
+ return 0
+ },
+ }
+
+ BSpline = ResampleFilter{
+ Support: 2.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 2.0 {
+ return bcspline(x, 1.0, 0.0)
+ }
+ return 0
+ },
+ }
+
+ Gaussian = ResampleFilter{
+ Support: 2.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 2.0 {
+ return math.Exp(-2 * x * x)
+ }
+ return 0
+ },
+ }
+
+ Bartlett = ResampleFilter{
+ Support: 3.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 3.0 {
+ return sinc(x) * (3.0 - x) / 3.0
+ }
+ return 0
+ },
+ }
+
+ Lanczos = ResampleFilter{
+ Support: 3.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 3.0 {
+ return sinc(x) * sinc(x/3.0)
+ }
+ return 0
+ },
+ }
+
+ Hann = ResampleFilter{
+ Support: 3.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 3.0 {
+ return sinc(x) * (0.5 + 0.5*math.Cos(math.Pi*x/3.0))
+ }
+ return 0
+ },
+ }
+
+ Hamming = ResampleFilter{
+ Support: 3.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 3.0 {
+ return sinc(x) * (0.54 + 0.46*math.Cos(math.Pi*x/3.0))
+ }
+ return 0
+ },
+ }
+
+ Blackman = ResampleFilter{
+ Support: 3.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 3.0 {
+ return sinc(x) * (0.42 - 0.5*math.Cos(math.Pi*x/3.0+math.Pi) + 0.08*math.Cos(2.0*math.Pi*x/3.0))
+ }
+ return 0
+ },
+ }
+
+ Welch = ResampleFilter{
+ Support: 3.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 3.0 {
+ return sinc(x) * (1.0 - (x * x / 9.0))
+ }
+ return 0
+ },
+ }
+
+ Cosine = ResampleFilter{
+ Support: 3.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 3.0 {
+ return sinc(x) * math.Cos((math.Pi/2.0)*(x/3.0))
+ }
+ return 0
+ },
+ }
+}
diff --git a/vendor/github.com/disintegration/imaging/tools.go b/vendor/github.com/disintegration/imaging/tools.go
new file mode 100644
index 000000000..76f14447b
--- /dev/null
+++ b/vendor/github.com/disintegration/imaging/tools.go
@@ -0,0 +1,201 @@
+package imaging
+
+import (
+ "image"
+ "math"
+)
+
+// Anchor is the anchor point for image alignment.
+type Anchor int
+
+const (
+ Center Anchor = iota
+ TopLeft
+ Top
+ TopRight
+ Left
+ Right
+ BottomLeft
+ Bottom
+ BottomRight
+)
+
+func anchorPt(b image.Rectangle, w, h int, anchor Anchor) image.Point {
+ var x, y int
+ switch anchor {
+ case TopLeft:
+ x = b.Min.X
+ y = b.Min.Y
+ case Top:
+ x = b.Min.X + (b.Dx()-w)/2
+ y = b.Min.Y
+ case TopRight:
+ x = b.Max.X - w
+ y = b.Min.Y
+ case Left:
+ x = b.Min.X
+ y = b.Min.Y + (b.Dy()-h)/2
+ case Right:
+ x = b.Max.X - w
+ y = b.Min.Y + (b.Dy()-h)/2
+ case BottomLeft:
+ x = b.Min.X
+ y = b.Max.Y - h
+ case Bottom:
+ x = b.Min.X + (b.Dx()-w)/2
+ y = b.Max.Y - h
+ case BottomRight:
+ x = b.Max.X - w
+ y = b.Max.Y - h
+ default:
+ x = b.Min.X + (b.Dx()-w)/2
+ y = b.Min.Y + (b.Dy()-h)/2
+ }
+ return image.Pt(x, y)
+}
+
+// Crop cuts out a rectangular region with the specified bounds
+// from the image and returns the cropped image.
+func Crop(img image.Image, rect image.Rectangle) *image.NRGBA {
+ src := toNRGBA(img)
+ srcRect := rect.Sub(img.Bounds().Min)
+ sub := src.SubImage(srcRect)
+ return Clone(sub) // New image Bounds().Min point will be (0, 0)
+}
+
+// CropAnchor cuts out a rectangular region with the specified size
+// from the image using the specified anchor point and returns the cropped image.
+func CropAnchor(img image.Image, width, height int, anchor Anchor) *image.NRGBA {
+ srcBounds := img.Bounds()
+ pt := anchorPt(srcBounds, width, height, anchor)
+ r := image.Rect(0, 0, width, height).Add(pt)
+ b := srcBounds.Intersect(r)
+ return Crop(img, b)
+}
+
+// CropCenter cuts out a rectangular region with the specified size
+// from the center of the image and returns the cropped image.
+func CropCenter(img image.Image, width, height int) *image.NRGBA {
+ return CropAnchor(img, width, height, Center)
+}
+
+// Paste pastes the img image to the background image at the specified position and returns the combined image.
+func Paste(background, img image.Image, pos image.Point) *image.NRGBA {
+ src := toNRGBA(img)
+ dst := Clone(background) // cloned image bounds start at (0, 0)
+ startPt := pos.Sub(background.Bounds().Min) // so we should translate start point
+ endPt := startPt.Add(src.Bounds().Size())
+ pasteBounds := image.Rectangle{startPt, endPt}
+
+ if dst.Bounds().Overlaps(pasteBounds) {
+ intersectBounds := dst.Bounds().Intersect(pasteBounds)
+
+ rowSize := intersectBounds.Dx() * 4
+ numRows := intersectBounds.Dy()
+
+ srcStartX := intersectBounds.Min.X - pasteBounds.Min.X
+ srcStartY := intersectBounds.Min.Y - pasteBounds.Min.Y
+
+ i0 := dst.PixOffset(intersectBounds.Min.X, intersectBounds.Min.Y)
+ j0 := src.PixOffset(srcStartX, srcStartY)
+
+ di := dst.Stride
+ dj := src.Stride
+
+ for row := 0; row < numRows; row++ {
+ copy(dst.Pix[i0:i0+rowSize], src.Pix[j0:j0+rowSize])
+ i0 += di
+ j0 += dj
+ }
+ }
+
+ return dst
+}
+
+// PasteCenter pastes the img image to the center of the background image and returns the combined image.
+func PasteCenter(background, img image.Image) *image.NRGBA {
+ bgBounds := background.Bounds()
+ bgW := bgBounds.Dx()
+ bgH := bgBounds.Dy()
+ bgMinX := bgBounds.Min.X
+ bgMinY := bgBounds.Min.Y
+
+ centerX := bgMinX + bgW/2
+ centerY := bgMinY + bgH/2
+
+ x0 := centerX - img.Bounds().Dx()/2
+ y0 := centerY - img.Bounds().Dy()/2
+
+ return Paste(background, img, image.Pt(x0, y0))
+}
+
+// Overlay draws the img image over the background image at given position
+// and returns the combined image. Opacity parameter is the opacity of the img
+// image layer, used to compose the images, it must be from 0.0 to 1.0.
+//
+// Usage examples:
+//
+// // draw the sprite over the background at position (50, 50)
+// dstImage := imaging.Overlay(backgroundImage, spriteImage, image.Pt(50, 50), 1.0)
+//
+// // blend two opaque images of the same size
+// dstImage := imaging.Overlay(imageOne, imageTwo, image.Pt(0, 0), 0.5)
+//
+func Overlay(background, img image.Image, pos image.Point, opacity float64) *image.NRGBA {
+ opacity = math.Min(math.Max(opacity, 0.0), 1.0) // check: 0.0 <= opacity <= 1.0
+
+ src := toNRGBA(img)
+ dst := Clone(background) // cloned image bounds start at (0, 0)
+ startPt := pos.Sub(background.Bounds().Min) // so we should translate start point
+ endPt := startPt.Add(src.Bounds().Size())
+ pasteBounds := image.Rectangle{startPt, endPt}
+
+ if dst.Bounds().Overlaps(pasteBounds) {
+ intersectBounds := dst.Bounds().Intersect(pasteBounds)
+
+ for y := intersectBounds.Min.Y; y < intersectBounds.Max.Y; y++ {
+ for x := intersectBounds.Min.X; x < intersectBounds.Max.X; x++ {
+ i := y*dst.Stride + x*4
+
+ srcX := x - pasteBounds.Min.X
+ srcY := y - pasteBounds.Min.Y
+ j := srcY*src.Stride + srcX*4
+
+ a1 := float64(dst.Pix[i+3])
+ a2 := float64(src.Pix[j+3])
+
+ coef2 := opacity * a2 / 255.0
+ coef1 := (1 - coef2) * a1 / 255.0
+ coefSum := coef1 + coef2
+ coef1 /= coefSum
+ coef2 /= coefSum
+
+ dst.Pix[i+0] = uint8(float64(dst.Pix[i+0])*coef1 + float64(src.Pix[j+0])*coef2)
+ dst.Pix[i+1] = uint8(float64(dst.Pix[i+1])*coef1 + float64(src.Pix[j+1])*coef2)
+ dst.Pix[i+2] = uint8(float64(dst.Pix[i+2])*coef1 + float64(src.Pix[j+2])*coef2)
+ dst.Pix[i+3] = uint8(math.Min(a1+a2*opacity*(255.0-a1)/255.0, 255.0))
+ }
+ }
+ }
+
+ return dst
+}
+
+// OverlayCenter overlays the img image to the center of the background image and
+// returns the combined image. Opacity parameter is the opacity of the img
+// image layer, used to compose the images, it must be from 0.0 to 1.0.
+func OverlayCenter(background, img image.Image, opacity float64) *image.NRGBA {
+ bgBounds := background.Bounds()
+ bgW := bgBounds.Dx()
+ bgH := bgBounds.Dy()
+ bgMinX := bgBounds.Min.X
+ bgMinY := bgBounds.Min.Y
+
+ centerX := bgMinX + bgW/2
+ centerY := bgMinY + bgH/2
+
+ x0 := centerX - img.Bounds().Dx()/2
+ y0 := centerY - img.Bounds().Dy()/2
+
+ return Overlay(background, img, image.Point{x0, y0}, opacity)
+}
diff --git a/vendor/github.com/disintegration/imaging/transform.go b/vendor/github.com/disintegration/imaging/transform.go
new file mode 100644
index 000000000..a11601bba
--- /dev/null
+++ b/vendor/github.com/disintegration/imaging/transform.go
@@ -0,0 +1,201 @@
+package imaging
+
+import (
+ "image"
+)
+
+// Rotate90 rotates the image 90 degrees counterclockwise and returns the transformed image.
+func Rotate90(img image.Image) *image.NRGBA {
+ src := toNRGBA(img)
+ srcW := src.Bounds().Max.X
+ srcH := src.Bounds().Max.Y
+ dstW := srcH
+ dstH := srcW
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ parallel(dstH, func(partStart, partEnd int) {
+
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ for dstX := 0; dstX < dstW; dstX++ {
+ srcX := dstH - dstY - 1
+ srcY := dstX
+
+ srcOff := srcY*src.Stride + srcX*4
+ dstOff := dstY*dst.Stride + dstX*4
+
+ copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
+ }
+ }
+
+ })
+
+ return dst
+}
+
+// Rotate180 rotates the image 180 degrees counterclockwise and returns the transformed image.
+func Rotate180(img image.Image) *image.NRGBA {
+ src := toNRGBA(img)
+ srcW := src.Bounds().Max.X
+ srcH := src.Bounds().Max.Y
+ dstW := srcW
+ dstH := srcH
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ parallel(dstH, func(partStart, partEnd int) {
+
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ for dstX := 0; dstX < dstW; dstX++ {
+ srcX := dstW - dstX - 1
+ srcY := dstH - dstY - 1
+
+ srcOff := srcY*src.Stride + srcX*4
+ dstOff := dstY*dst.Stride + dstX*4
+
+ copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
+ }
+ }
+
+ })
+
+ return dst
+}
+
+// Rotate270 rotates the image 270 degrees counterclockwise and returns the transformed image.
+func Rotate270(img image.Image) *image.NRGBA {
+ src := toNRGBA(img)
+ srcW := src.Bounds().Max.X
+ srcH := src.Bounds().Max.Y
+ dstW := srcH
+ dstH := srcW
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ parallel(dstH, func(partStart, partEnd int) {
+
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ for dstX := 0; dstX < dstW; dstX++ {
+ srcX := dstY
+ srcY := dstW - dstX - 1
+
+ srcOff := srcY*src.Stride + srcX*4
+ dstOff := dstY*dst.Stride + dstX*4
+
+ copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
+ }
+ }
+
+ })
+
+ return dst
+}
+
+// FlipH flips the image horizontally (from left to right) and returns the transformed image.
+func FlipH(img image.Image) *image.NRGBA {
+ src := toNRGBA(img)
+ srcW := src.Bounds().Max.X
+ srcH := src.Bounds().Max.Y
+ dstW := srcW
+ dstH := srcH
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ parallel(dstH, func(partStart, partEnd int) {
+
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ for dstX := 0; dstX < dstW; dstX++ {
+ srcX := dstW - dstX - 1
+ srcY := dstY
+
+ srcOff := srcY*src.Stride + srcX*4
+ dstOff := dstY*dst.Stride + dstX*4
+
+ copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
+ }
+ }
+
+ })
+
+ return dst
+}
+
+// FlipV flips the image vertically (from top to bottom) and returns the transformed image.
+func FlipV(img image.Image) *image.NRGBA {
+ src := toNRGBA(img)
+ srcW := src.Bounds().Max.X
+ srcH := src.Bounds().Max.Y
+ dstW := srcW
+ dstH := srcH
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ parallel(dstH, func(partStart, partEnd int) {
+
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ for dstX := 0; dstX < dstW; dstX++ {
+ srcX := dstX
+ srcY := dstH - dstY - 1
+
+ srcOff := srcY*src.Stride + srcX*4
+ dstOff := dstY*dst.Stride + dstX*4
+
+ copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
+ }
+ }
+
+ })
+
+ return dst
+}
+
+// Transpose flips the image horizontally and rotates 90 degrees counter-clockwise.
+func Transpose(img image.Image) *image.NRGBA {
+ src := toNRGBA(img)
+ srcW := src.Bounds().Max.X
+ srcH := src.Bounds().Max.Y
+ dstW := srcH
+ dstH := srcW
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ parallel(dstH, func(partStart, partEnd int) {
+
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ for dstX := 0; dstX < dstW; dstX++ {
+ srcX := dstY
+ srcY := dstX
+
+ srcOff := srcY*src.Stride + srcX*4
+ dstOff := dstY*dst.Stride + dstX*4
+
+ copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
+ }
+ }
+
+ })
+
+ return dst
+}
+
+// Transverse flips the image vertically and rotates 90 degrees counter-clockwise.
+func Transverse(img image.Image) *image.NRGBA {
+ src := toNRGBA(img)
+ srcW := src.Bounds().Max.X
+ srcH := src.Bounds().Max.Y
+ dstW := srcH
+ dstH := srcW
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ parallel(dstH, func(partStart, partEnd int) {
+
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ for dstX := 0; dstX < dstW; dstX++ {
+ srcX := dstH - dstY - 1
+ srcY := dstW - dstX - 1
+
+ srcOff := srcY*src.Stride + srcX*4
+ dstOff := dstY*dst.Stride + dstX*4
+
+ copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
+ }
+ }
+
+ })
+
+ return dst
+}
diff --git a/vendor/github.com/disintegration/imaging/utils.go b/vendor/github.com/disintegration/imaging/utils.go
new file mode 100644
index 000000000..8b1ab8adb
--- /dev/null
+++ b/vendor/github.com/disintegration/imaging/utils.go
@@ -0,0 +1,77 @@
+package imaging
+
+import (
+ "math"
+ "runtime"
+ "sync"
+ "sync/atomic"
+)
+
+var parallelizationEnabled = true
+
+// if GOMAXPROCS = 1: no goroutines used
+// if GOMAXPROCS > 1: spawn N=GOMAXPROCS workers in separate goroutines
+func parallel(dataSize int, fn func(partStart, partEnd int)) {
+ numGoroutines := 1
+ partSize := dataSize
+
+ if parallelizationEnabled {
+ numProcs := runtime.GOMAXPROCS(0)
+ if numProcs > 1 {
+ numGoroutines = numProcs
+ partSize = dataSize / (numGoroutines * 10)
+ if partSize < 1 {
+ partSize = 1
+ }
+ }
+ }
+
+ if numGoroutines == 1 {
+ fn(0, dataSize)
+ } else {
+ var wg sync.WaitGroup
+ wg.Add(numGoroutines)
+ idx := uint64(0)
+
+ for p := 0; p < numGoroutines; p++ {
+ go func() {
+ defer wg.Done()
+ for {
+ partStart := int(atomic.AddUint64(&idx, uint64(partSize))) - partSize
+ if partStart >= dataSize {
+ break
+ }
+ partEnd := partStart + partSize
+ if partEnd > dataSize {
+ partEnd = dataSize
+ }
+ fn(partStart, partEnd)
+ }
+ }()
+ }
+
+ wg.Wait()
+ }
+}
+
+func absint(i int) int {
+ if i < 0 {
+ return -i
+ }
+ return i
+}
+
+// clamp & round float64 to uint8 (0..255)
+func clamp(v float64) uint8 {
+ return uint8(math.Min(math.Max(v, 0.0), 255.0) + 0.5)
+}
+
+// clamp int32 to uint8 (0..255)
+func clampint32(v int32) uint8 {
+ if v < 0 {
+ return 0
+ } else if v > 255 {
+ return 255
+ }
+ return uint8(v)
+}
diff --git a/vendor/github.com/garyburd/redigo/LICENSE b/vendor/github.com/garyburd/redigo/LICENSE
new file mode 100644
index 000000000..67db85882
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/LICENSE
@@ -0,0 +1,175 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
diff --git a/vendor/github.com/garyburd/redigo/internal/commandinfo.go b/vendor/github.com/garyburd/redigo/internal/commandinfo.go
new file mode 100644
index 000000000..dbc60fc8e
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/internal/commandinfo.go
@@ -0,0 +1,54 @@
+// Copyright 2014 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package internal
+
+import (
+ "strings"
+)
+
+const (
+ WatchState = 1 << iota
+ MultiState
+ SubscribeState
+ MonitorState
+)
+
+type CommandInfo struct {
+ Set, Clear int
+}
+
+var commandInfos = map[string]CommandInfo{
+ "WATCH": {Set: WatchState},
+ "UNWATCH": {Clear: WatchState},
+ "MULTI": {Set: MultiState},
+ "EXEC": {Clear: WatchState | MultiState},
+ "DISCARD": {Clear: WatchState | MultiState},
+ "PSUBSCRIBE": {Set: SubscribeState},
+ "SUBSCRIBE": {Set: SubscribeState},
+ "MONITOR": {Set: MonitorState},
+}
+
+func init() {
+ for n, ci := range commandInfos {
+ commandInfos[strings.ToLower(n)] = ci
+ }
+}
+
+func LookupCommandInfo(commandName string) CommandInfo {
+ if ci, ok := commandInfos[commandName]; ok {
+ return ci
+ }
+ return commandInfos[strings.ToUpper(commandName)]
+}
diff --git a/vendor/github.com/garyburd/redigo/redis/conn.go b/vendor/github.com/garyburd/redigo/redis/conn.go
new file mode 100644
index 000000000..ed358c601
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/conn.go
@@ -0,0 +1,570 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "net/url"
+ "regexp"
+ "strconv"
+ "sync"
+ "time"
+)
+
+// conn is the low-level implementation of Conn
+type conn struct {
+
+ // Shared
+ mu sync.Mutex
+ pending int
+ err error
+ conn net.Conn
+
+ // Read
+ readTimeout time.Duration
+ br *bufio.Reader
+
+ // Write
+ writeTimeout time.Duration
+ bw *bufio.Writer
+
+ // Scratch space for formatting argument length.
+ // '*' or '$', length, "\r\n"
+ lenScratch [32]byte
+
+ // Scratch space for formatting integers and floats.
+ numScratch [40]byte
+}
+
+// DialTimeout acts like Dial but takes timeouts for establishing the
+// connection to the server, writing a command and reading a reply.
+//
+// Deprecated: Use Dial with options instead.
+func DialTimeout(network, address string, connectTimeout, readTimeout, writeTimeout time.Duration) (Conn, error) {
+ return Dial(network, address,
+ DialConnectTimeout(connectTimeout),
+ DialReadTimeout(readTimeout),
+ DialWriteTimeout(writeTimeout))
+}
+
+// DialOption specifies an option for dialing a Redis server.
+type DialOption struct {
+ f func(*dialOptions)
+}
+
+type dialOptions struct {
+ readTimeout time.Duration
+ writeTimeout time.Duration
+ dial func(network, addr string) (net.Conn, error)
+ db int
+ password string
+}
+
+// DialReadTimeout specifies the timeout for reading a single command reply.
+func DialReadTimeout(d time.Duration) DialOption {
+ return DialOption{func(do *dialOptions) {
+ do.readTimeout = d
+ }}
+}
+
+// DialWriteTimeout specifies the timeout for writing a single command.
+func DialWriteTimeout(d time.Duration) DialOption {
+ return DialOption{func(do *dialOptions) {
+ do.writeTimeout = d
+ }}
+}
+
+// DialConnectTimeout specifies the timeout for connecting to the Redis server.
+func DialConnectTimeout(d time.Duration) DialOption {
+ return DialOption{func(do *dialOptions) {
+ dialer := net.Dialer{Timeout: d}
+ do.dial = dialer.Dial
+ }}
+}
+
+// DialNetDial specifies a custom dial function for creating TCP
+// connections. If this option is left out, then net.Dial is
+// used. DialNetDial overrides DialConnectTimeout.
+func DialNetDial(dial func(network, addr string) (net.Conn, error)) DialOption {
+ return DialOption{func(do *dialOptions) {
+ do.dial = dial
+ }}
+}
+
+// DialDatabase specifies the database to select when dialing a connection.
+func DialDatabase(db int) DialOption {
+ return DialOption{func(do *dialOptions) {
+ do.db = db
+ }}
+}
+
+// DialPassword specifies the password to use when connecting to
+// the Redis server.
+func DialPassword(password string) DialOption {
+ return DialOption{func(do *dialOptions) {
+ do.password = password
+ }}
+}
+
+// Dial connects to the Redis server at the given network and
+// address using the specified options.
+func Dial(network, address string, options ...DialOption) (Conn, error) {
+ do := dialOptions{
+ dial: net.Dial,
+ }
+ for _, option := range options {
+ option.f(&do)
+ }
+
+ netConn, err := do.dial(network, address)
+ if err != nil {
+ return nil, err
+ }
+ c := &conn{
+ conn: netConn,
+ bw: bufio.NewWriter(netConn),
+ br: bufio.NewReader(netConn),
+ readTimeout: do.readTimeout,
+ writeTimeout: do.writeTimeout,
+ }
+
+ if do.password != "" {
+ if _, err := c.Do("AUTH", do.password); err != nil {
+ netConn.Close()
+ return nil, err
+ }
+ }
+
+ if do.db != 0 {
+ if _, err := c.Do("SELECT", do.db); err != nil {
+ netConn.Close()
+ return nil, err
+ }
+ }
+
+ return c, nil
+}
+
+var pathDBRegexp = regexp.MustCompile(`/(\d*)\z`)
+
+// DialURL connects to a Redis server at the given URL using the Redis
+// URI scheme. URLs should follow the draft IANA specification for the
+// scheme (https://www.iana.org/assignments/uri-schemes/prov/redis).
+func DialURL(rawurl string, options ...DialOption) (Conn, error) {
+ u, err := url.Parse(rawurl)
+ if err != nil {
+ return nil, err
+ }
+
+ if u.Scheme != "redis" {
+ return nil, fmt.Errorf("invalid redis URL scheme: %s", u.Scheme)
+ }
+
+ // As per the IANA draft spec, the host defaults to localhost and
+ // the port defaults to 6379.
+ host, port, err := net.SplitHostPort(u.Host)
+ if err != nil {
+ // assume port is missing
+ host = u.Host
+ port = "6379"
+ }
+ if host == "" {
+ host = "localhost"
+ }
+ address := net.JoinHostPort(host, port)
+
+ if u.User != nil {
+ password, isSet := u.User.Password()
+ if isSet {
+ options = append(options, DialPassword(password))
+ }
+ }
+
+ match := pathDBRegexp.FindStringSubmatch(u.Path)
+ if len(match) == 2 {
+ db := 0
+ if len(match[1]) > 0 {
+ db, err = strconv.Atoi(match[1])
+ if err != nil {
+ return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
+ }
+ }
+ if db != 0 {
+ options = append(options, DialDatabase(db))
+ }
+ } else if u.Path != "" {
+ return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
+ }
+
+ return Dial("tcp", address, options...)
+}
+
+// NewConn returns a new Redigo connection for the given net connection.
+func NewConn(netConn net.Conn, readTimeout, writeTimeout time.Duration) Conn {
+ return &conn{
+ conn: netConn,
+ bw: bufio.NewWriter(netConn),
+ br: bufio.NewReader(netConn),
+ readTimeout: readTimeout,
+ writeTimeout: writeTimeout,
+ }
+}
+
+func (c *conn) Close() error {
+ c.mu.Lock()
+ err := c.err
+ if c.err == nil {
+ c.err = errors.New("redigo: closed")
+ err = c.conn.Close()
+ }
+ c.mu.Unlock()
+ return err
+}
+
+func (c *conn) fatal(err error) error {
+ c.mu.Lock()
+ if c.err == nil {
+ c.err = err
+ // Close connection to force errors on subsequent calls and to unblock
+ // other reader or writer.
+ c.conn.Close()
+ }
+ c.mu.Unlock()
+ return err
+}
+
+func (c *conn) Err() error {
+ c.mu.Lock()
+ err := c.err
+ c.mu.Unlock()
+ return err
+}
+
+func (c *conn) writeLen(prefix byte, n int) error {
+ c.lenScratch[len(c.lenScratch)-1] = '\n'
+ c.lenScratch[len(c.lenScratch)-2] = '\r'
+ i := len(c.lenScratch) - 3
+ for {
+ c.lenScratch[i] = byte('0' + n%10)
+ i -= 1
+ n = n / 10
+ if n == 0 {
+ break
+ }
+ }
+ c.lenScratch[i] = prefix
+ _, err := c.bw.Write(c.lenScratch[i:])
+ return err
+}
+
+func (c *conn) writeString(s string) error {
+ c.writeLen('$', len(s))
+ c.bw.WriteString(s)
+ _, err := c.bw.WriteString("\r\n")
+ return err
+}
+
+func (c *conn) writeBytes(p []byte) error {
+ c.writeLen('$', len(p))
+ c.bw.Write(p)
+ _, err := c.bw.WriteString("\r\n")
+ return err
+}
+
+func (c *conn) writeInt64(n int64) error {
+ return c.writeBytes(strconv.AppendInt(c.numScratch[:0], n, 10))
+}
+
+func (c *conn) writeFloat64(n float64) error {
+ return c.writeBytes(strconv.AppendFloat(c.numScratch[:0], n, 'g', -1, 64))
+}
+
+func (c *conn) writeCommand(cmd string, args []interface{}) (err error) {
+ c.writeLen('*', 1+len(args))
+ err = c.writeString(cmd)
+ for _, arg := range args {
+ if err != nil {
+ break
+ }
+ switch arg := arg.(type) {
+ case string:
+ err = c.writeString(arg)
+ case []byte:
+ err = c.writeBytes(arg)
+ case int:
+ err = c.writeInt64(int64(arg))
+ case int64:
+ err = c.writeInt64(arg)
+ case float64:
+ err = c.writeFloat64(arg)
+ case bool:
+ if arg {
+ err = c.writeString("1")
+ } else {
+ err = c.writeString("0")
+ }
+ case nil:
+ err = c.writeString("")
+ default:
+ var buf bytes.Buffer
+ fmt.Fprint(&buf, arg)
+ err = c.writeBytes(buf.Bytes())
+ }
+ }
+ return err
+}
+
+type protocolError string
+
+func (pe protocolError) Error() string {
+ return fmt.Sprintf("redigo: %s (possible server error or unsupported concurrent read by application)", string(pe))
+}
+
+func (c *conn) readLine() ([]byte, error) {
+ p, err := c.br.ReadSlice('\n')
+ if err == bufio.ErrBufferFull {
+ return nil, protocolError("long response line")
+ }
+ if err != nil {
+ return nil, err
+ }
+ i := len(p) - 2
+ if i < 0 || p[i] != '\r' {
+ return nil, protocolError("bad response line terminator")
+ }
+ return p[:i], nil
+}
+
+// parseLen parses bulk string and array lengths.
+func parseLen(p []byte) (int, error) {
+ if len(p) == 0 {
+ return -1, protocolError("malformed length")
+ }
+
+ if p[0] == '-' && len(p) == 2 && p[1] == '1' {
+ // handle $-1 and $-1 null replies.
+ return -1, nil
+ }
+
+ var n int
+ for _, b := range p {
+ n *= 10
+ if b < '0' || b > '9' {
+ return -1, protocolError("illegal bytes in length")
+ }
+ n += int(b - '0')
+ }
+
+ return n, nil
+}
+
+// parseInt parses an integer reply.
+func parseInt(p []byte) (interface{}, error) {
+ if len(p) == 0 {
+ return 0, protocolError("malformed integer")
+ }
+
+ var negate bool
+ if p[0] == '-' {
+ negate = true
+ p = p[1:]
+ if len(p) == 0 {
+ return 0, protocolError("malformed integer")
+ }
+ }
+
+ var n int64
+ for _, b := range p {
+ n *= 10
+ if b < '0' || b > '9' {
+ return 0, protocolError("illegal bytes in length")
+ }
+ n += int64(b - '0')
+ }
+
+ if negate {
+ n = -n
+ }
+ return n, nil
+}
+
+var (
+ okReply interface{} = "OK"
+ pongReply interface{} = "PONG"
+)
+
+func (c *conn) readReply() (interface{}, error) {
+ line, err := c.readLine()
+ if err != nil {
+ return nil, err
+ }
+ if len(line) == 0 {
+ return nil, protocolError("short response line")
+ }
+ switch line[0] {
+ case '+':
+ switch {
+ case len(line) == 3 && line[1] == 'O' && line[2] == 'K':
+ // Avoid allocation for frequent "+OK" response.
+ return okReply, nil
+ case len(line) == 5 && line[1] == 'P' && line[2] == 'O' && line[3] == 'N' && line[4] == 'G':
+ // Avoid allocation in PING command benchmarks :)
+ return pongReply, nil
+ default:
+ return string(line[1:]), nil
+ }
+ case '-':
+ return Error(string(line[1:])), nil
+ case ':':
+ return parseInt(line[1:])
+ case '$':
+ n, err := parseLen(line[1:])
+ if n < 0 || err != nil {
+ return nil, err
+ }
+ p := make([]byte, n)
+ _, err = io.ReadFull(c.br, p)
+ if err != nil {
+ return nil, err
+ }
+ if line, err := c.readLine(); err != nil {
+ return nil, err
+ } else if len(line) != 0 {
+ return nil, protocolError("bad bulk string format")
+ }
+ return p, nil
+ case '*':
+ n, err := parseLen(line[1:])
+ if n < 0 || err != nil {
+ return nil, err
+ }
+ r := make([]interface{}, n)
+ for i := range r {
+ r[i], err = c.readReply()
+ if err != nil {
+ return nil, err
+ }
+ }
+ return r, nil
+ }
+ return nil, protocolError("unexpected response line")
+}
+
+func (c *conn) Send(cmd string, args ...interface{}) error {
+ c.mu.Lock()
+ c.pending += 1
+ c.mu.Unlock()
+ if c.writeTimeout != 0 {
+ c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
+ }
+ if err := c.writeCommand(cmd, args); err != nil {
+ return c.fatal(err)
+ }
+ return nil
+}
+
+func (c *conn) Flush() error {
+ if c.writeTimeout != 0 {
+ c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
+ }
+ if err := c.bw.Flush(); err != nil {
+ return c.fatal(err)
+ }
+ return nil
+}
+
+func (c *conn) Receive() (reply interface{}, err error) {
+ if c.readTimeout != 0 {
+ c.conn.SetReadDeadline(time.Now().Add(c.readTimeout))
+ }
+ if reply, err = c.readReply(); err != nil {
+ return nil, c.fatal(err)
+ }
+ // When using pub/sub, the number of receives can be greater than the
+ // number of sends. To enable normal use of the connection after
+ // unsubscribing from all channels, we do not decrement pending to a
+ // negative value.
+ //
+ // The pending field is decremented after the reply is read to handle the
+ // case where Receive is called before Send.
+ c.mu.Lock()
+ if c.pending > 0 {
+ c.pending -= 1
+ }
+ c.mu.Unlock()
+ if err, ok := reply.(Error); ok {
+ return nil, err
+ }
+ return
+}
+
+func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) {
+ c.mu.Lock()
+ pending := c.pending
+ c.pending = 0
+ c.mu.Unlock()
+
+ if cmd == "" && pending == 0 {
+ return nil, nil
+ }
+
+ if c.writeTimeout != 0 {
+ c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
+ }
+
+ if cmd != "" {
+ if err := c.writeCommand(cmd, args); err != nil {
+ return nil, c.fatal(err)
+ }
+ }
+
+ if err := c.bw.Flush(); err != nil {
+ return nil, c.fatal(err)
+ }
+
+ if c.readTimeout != 0 {
+ c.conn.SetReadDeadline(time.Now().Add(c.readTimeout))
+ }
+
+ if cmd == "" {
+ reply := make([]interface{}, pending)
+ for i := range reply {
+ r, e := c.readReply()
+ if e != nil {
+ return nil, c.fatal(e)
+ }
+ reply[i] = r
+ }
+ return reply, nil
+ }
+
+ var err error
+ var reply interface{}
+ for i := 0; i <= pending; i++ {
+ var e error
+ if reply, e = c.readReply(); e != nil {
+ return nil, c.fatal(e)
+ }
+ if e, ok := reply.(Error); ok && err == nil {
+ err = e
+ }
+ }
+ return reply, err
+}
diff --git a/vendor/github.com/garyburd/redigo/redis/doc.go b/vendor/github.com/garyburd/redigo/redis/doc.go
new file mode 100644
index 000000000..1ae6f0cc2
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/doc.go
@@ -0,0 +1,169 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+// Package redis is a client for the Redis database.
+//
+// The Redigo FAQ (https://github.com/garyburd/redigo/wiki/FAQ) contains more
+// documentation about this package.
+//
+// Connections
+//
+// The Conn interface is the primary interface for working with Redis.
+// Applications create connections by calling the Dial, DialWithTimeout or
+// NewConn functions. In the future, functions will be added for creating
+// sharded and other types of connections.
+//
+// The application must call the connection Close method when the application
+// is done with the connection.
+//
+// Executing Commands
+//
+// The Conn interface has a generic method for executing Redis commands:
+//
+// Do(commandName string, args ...interface{}) (reply interface{}, err error)
+//
+// The Redis command reference (http://redis.io/commands) lists the available
+// commands. An example of using the Redis APPEND command is:
+//
+// n, err := conn.Do("APPEND", "key", "value")
+//
+// The Do method converts command arguments to binary strings for transmission
+// to the server as follows:
+//
+// Go Type Conversion
+// []byte Sent as is
+// string Sent as is
+// int, int64 strconv.FormatInt(v)
+// float64 strconv.FormatFloat(v, 'g', -1, 64)
+// bool true -> "1", false -> "0"
+// nil ""
+// all other types fmt.Print(v)
+//
+// Redis command reply types are represented using the following Go types:
+//
+// Redis type Go type
+// error redis.Error
+// integer int64
+// simple string string
+// bulk string []byte or nil if value not present.
+// array []interface{} or nil if value not present.
+//
+// Use type assertions or the reply helper functions to convert from
+// interface{} to the specific Go type for the command result.
+//
+// Pipelining
+//
+// Connections support pipelining using the Send, Flush and Receive methods.
+//
+// Send(commandName string, args ...interface{}) error
+// Flush() error
+// Receive() (reply interface{}, err error)
+//
+// Send writes the command to the connection's output buffer. Flush flushes the
+// connection's output buffer to the server. Receive reads a single reply from
+// the server. The following example shows a simple pipeline.
+//
+// c.Send("SET", "foo", "bar")
+// c.Send("GET", "foo")
+// c.Flush()
+// c.Receive() // reply from SET
+// v, err = c.Receive() // reply from GET
+//
+// The Do method combines the functionality of the Send, Flush and Receive
+// methods. The Do method starts by writing the command and flushing the output
+// buffer. Next, the Do method receives all pending replies including the reply
+// for the command just sent by Do. If any of the received replies is an error,
+// then Do returns the error. If there are no errors, then Do returns the last
+// reply. If the command argument to the Do method is "", then the Do method
+// will flush the output buffer and receive pending replies without sending a
+// command.
+//
+// Use the Send and Do methods to implement pipelined transactions.
+//
+// c.Send("MULTI")
+// c.Send("INCR", "foo")
+// c.Send("INCR", "bar")
+// r, err := c.Do("EXEC")
+// fmt.Println(r) // prints [1, 1]
+//
+// Concurrency
+//
+// Connections do not support concurrent calls to the write methods (Send,
+// Flush) or concurrent calls to the read method (Receive). Connections do
+// allow a concurrent reader and writer.
+//
+// Because the Do method combines the functionality of Send, Flush and Receive,
+// the Do method cannot be called concurrently with the other methods.
+//
+// For full concurrent access to Redis, use the thread-safe Pool to get and
+// release connections from within a goroutine.
+//
+// Publish and Subscribe
+//
+// Use the Send, Flush and Receive methods to implement Pub/Sub subscribers.
+//
+// c.Send("SUBSCRIBE", "example")
+// c.Flush()
+// for {
+// reply, err := c.Receive()
+// if err != nil {
+// return err
+// }
+// // process pushed message
+// }
+//
+// The PubSubConn type wraps a Conn with convenience methods for implementing
+// subscribers. The Subscribe, PSubscribe, Unsubscribe and PUnsubscribe methods
+// send and flush a subscription management command. The receive method
+// converts a pushed message to convenient types for use in a type switch.
+//
+// psc := redis.PubSubConn{c}
+// psc.Subscribe("example")
+// for {
+// switch v := psc.Receive().(type) {
+// case redis.Message:
+// fmt.Printf("%s: message: %s\n", v.Channel, v.Data)
+// case redis.Subscription:
+// fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
+// case error:
+// return v
+// }
+// }
+//
+// Reply Helpers
+//
+// The Bool, Int, Bytes, String, Strings and Values functions convert a reply
+// to a value of a specific type. To allow convenient wrapping of calls to the
+// connection Do and Receive methods, the functions take a second argument of
+// type error. If the error is non-nil, then the helper function returns the
+// error. If the error is nil, the function converts the reply to the specified
+// type:
+//
+// exists, err := redis.Bool(c.Do("EXISTS", "foo"))
+// if err != nil {
+// // handle error return from c.Do or type conversion error.
+// }
+//
+// The Scan function converts elements of a array reply to Go types:
+//
+// var value1 int
+// var value2 string
+// reply, err := redis.Values(c.Do("MGET", "key1", "key2"))
+// if err != nil {
+// // handle error
+// }
+// if _, err := redis.Scan(reply, &value1, &value2); err != nil {
+// // handle error
+// }
+package redis
diff --git a/vendor/github.com/garyburd/redigo/redis/log.go b/vendor/github.com/garyburd/redigo/redis/log.go
new file mode 100644
index 000000000..129b86d67
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/log.go
@@ -0,0 +1,117 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+)
+
+// NewLoggingConn returns a logging wrapper around a connection.
+func NewLoggingConn(conn Conn, logger *log.Logger, prefix string) Conn {
+ if prefix != "" {
+ prefix = prefix + "."
+ }
+ return &loggingConn{conn, logger, prefix}
+}
+
+type loggingConn struct {
+ Conn
+ logger *log.Logger
+ prefix string
+}
+
+func (c *loggingConn) Close() error {
+ err := c.Conn.Close()
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "%sClose() -> (%v)", c.prefix, err)
+ c.logger.Output(2, buf.String())
+ return err
+}
+
+func (c *loggingConn) printValue(buf *bytes.Buffer, v interface{}) {
+ const chop = 32
+ switch v := v.(type) {
+ case []byte:
+ if len(v) > chop {
+ fmt.Fprintf(buf, "%q...", v[:chop])
+ } else {
+ fmt.Fprintf(buf, "%q", v)
+ }
+ case string:
+ if len(v) > chop {
+ fmt.Fprintf(buf, "%q...", v[:chop])
+ } else {
+ fmt.Fprintf(buf, "%q", v)
+ }
+ case []interface{}:
+ if len(v) == 0 {
+ buf.WriteString("[]")
+ } else {
+ sep := "["
+ fin := "]"
+ if len(v) > chop {
+ v = v[:chop]
+ fin = "...]"
+ }
+ for _, vv := range v {
+ buf.WriteString(sep)
+ c.printValue(buf, vv)
+ sep = ", "
+ }
+ buf.WriteString(fin)
+ }
+ default:
+ fmt.Fprint(buf, v)
+ }
+}
+
+func (c *loggingConn) print(method, commandName string, args []interface{}, reply interface{}, err error) {
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "%s%s(", c.prefix, method)
+ if method != "Receive" {
+ buf.WriteString(commandName)
+ for _, arg := range args {
+ buf.WriteString(", ")
+ c.printValue(&buf, arg)
+ }
+ }
+ buf.WriteString(") -> (")
+ if method != "Send" {
+ c.printValue(&buf, reply)
+ buf.WriteString(", ")
+ }
+ fmt.Fprintf(&buf, "%v)", err)
+ c.logger.Output(3, buf.String())
+}
+
+func (c *loggingConn) Do(commandName string, args ...interface{}) (interface{}, error) {
+ reply, err := c.Conn.Do(commandName, args...)
+ c.print("Do", commandName, args, reply, err)
+ return reply, err
+}
+
+func (c *loggingConn) Send(commandName string, args ...interface{}) error {
+ err := c.Conn.Send(commandName, args...)
+ c.print("Send", commandName, args, nil, err)
+ return err
+}
+
+func (c *loggingConn) Receive() (interface{}, error) {
+ reply, err := c.Conn.Receive()
+ c.print("Receive", "", nil, reply, err)
+ return reply, err
+}
diff --git a/vendor/github.com/garyburd/redigo/redis/pool.go b/vendor/github.com/garyburd/redigo/redis/pool.go
new file mode 100644
index 000000000..d66ef84b6
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/pool.go
@@ -0,0 +1,393 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import (
+ "bytes"
+ "container/list"
+ "crypto/rand"
+ "crypto/sha1"
+ "errors"
+ "io"
+ "strconv"
+ "sync"
+ "time"
+
+ "github.com/garyburd/redigo/internal"
+)
+
+var nowFunc = time.Now // for testing
+
+// ErrPoolExhausted is returned from a pool connection method (Do, Send,
+// Receive, Flush, Err) when the maximum number of database connections in the
+// pool has been reached.
+var ErrPoolExhausted = errors.New("redigo: connection pool exhausted")
+
+var (
+ errPoolClosed = errors.New("redigo: connection pool closed")
+ errConnClosed = errors.New("redigo: connection closed")
+)
+
+// Pool maintains a pool of connections. The application calls the Get method
+// to get a connection from the pool and the connection's Close method to
+// return the connection's resources to the pool.
+//
+// The following example shows how to use a pool in a web application. The
+// application creates a pool at application startup and makes it available to
+// request handlers using a global variable.
+//
+// func newPool(server, password string) *redis.Pool {
+// return &redis.Pool{
+// MaxIdle: 3,
+// IdleTimeout: 240 * time.Second,
+// Dial: func () (redis.Conn, error) {
+// c, err := redis.Dial("tcp", server)
+// if err != nil {
+// return nil, err
+// }
+// if _, err := c.Do("AUTH", password); err != nil {
+// c.Close()
+// return nil, err
+// }
+// return c, err
+// },
+// TestOnBorrow: func(c redis.Conn, t time.Time) error {
+// _, err := c.Do("PING")
+// return err
+// },
+// }
+// }
+//
+// var (
+// pool *redis.Pool
+// redisServer = flag.String("redisServer", ":6379", "")
+// redisPassword = flag.String("redisPassword", "", "")
+// )
+//
+// func main() {
+// flag.Parse()
+// pool = newPool(*redisServer, *redisPassword)
+// ...
+// }
+//
+// A request handler gets a connection from the pool and closes the connection
+// when the handler is done:
+//
+// func serveHome(w http.ResponseWriter, r *http.Request) {
+// conn := pool.Get()
+// defer conn.Close()
+// ....
+// }
+//
+type Pool struct {
+
+ // Dial is an application supplied function for creating and configuring a
+ // connection.
+ //
+ // The connection returned from Dial must not be in a special state
+ // (subscribed to pubsub channel, transaction started, ...).
+ Dial func() (Conn, error)
+
+ // TestOnBorrow is an optional application supplied function for checking
+ // the health of an idle connection before the connection is used again by
+ // the application. Argument t is the time that the connection was returned
+ // to the pool. If the function returns an error, then the connection is
+ // closed.
+ TestOnBorrow func(c Conn, t time.Time) error
+
+ // Maximum number of idle connections in the pool.
+ MaxIdle int
+
+ // Maximum number of connections allocated by the pool at a given time.
+ // When zero, there is no limit on the number of connections in the pool.
+ MaxActive int
+
+ // Close connections after remaining idle for this duration. If the value
+ // is zero, then idle connections are not closed. Applications should set
+ // the timeout to a value less than the server's timeout.
+ IdleTimeout time.Duration
+
+ // If Wait is true and the pool is at the MaxActive limit, then Get() waits
+ // for a connection to be returned to the pool before returning.
+ Wait bool
+
+ // mu protects fields defined below.
+ mu sync.Mutex
+ cond *sync.Cond
+ closed bool
+ active int
+
+ // Stack of idleConn with most recently used at the front.
+ idle list.List
+}
+
+type idleConn struct {
+ c Conn
+ t time.Time
+}
+
+// NewPool creates a new pool.
+//
+// Deprecated: Initialize the Pool directory as shown in the example.
+func NewPool(newFn func() (Conn, error), maxIdle int) *Pool {
+ return &Pool{Dial: newFn, MaxIdle: maxIdle}
+}
+
+// Get gets a connection. The application must close the returned connection.
+// This method always returns a valid connection so that applications can defer
+// error handling to the first use of the connection. If there is an error
+// getting an underlying connection, then the connection Err, Do, Send, Flush
+// and Receive methods return that error.
+func (p *Pool) Get() Conn {
+ c, err := p.get()
+ if err != nil {
+ return errorConnection{err}
+ }
+ return &pooledConnection{p: p, c: c}
+}
+
+// ActiveCount returns the number of active connections in the pool.
+func (p *Pool) ActiveCount() int {
+ p.mu.Lock()
+ active := p.active
+ p.mu.Unlock()
+ return active
+}
+
+// Close releases the resources used by the pool.
+func (p *Pool) Close() error {
+ p.mu.Lock()
+ idle := p.idle
+ p.idle.Init()
+ p.closed = true
+ p.active -= idle.Len()
+ if p.cond != nil {
+ p.cond.Broadcast()
+ }
+ p.mu.Unlock()
+ for e := idle.Front(); e != nil; e = e.Next() {
+ e.Value.(idleConn).c.Close()
+ }
+ return nil
+}
+
+// release decrements the active count and signals waiters. The caller must
+// hold p.mu during the call.
+func (p *Pool) release() {
+ p.active -= 1
+ if p.cond != nil {
+ p.cond.Signal()
+ }
+}
+
+// get prunes stale connections and returns a connection from the idle list or
+// creates a new connection.
+func (p *Pool) get() (Conn, error) {
+ p.mu.Lock()
+
+ // Prune stale connections.
+
+ if timeout := p.IdleTimeout; timeout > 0 {
+ for i, n := 0, p.idle.Len(); i < n; i++ {
+ e := p.idle.Back()
+ if e == nil {
+ break
+ }
+ ic := e.Value.(idleConn)
+ if ic.t.Add(timeout).After(nowFunc()) {
+ break
+ }
+ p.idle.Remove(e)
+ p.release()
+ p.mu.Unlock()
+ ic.c.Close()
+ p.mu.Lock()
+ }
+ }
+
+ for {
+
+ // Get idle connection.
+
+ for i, n := 0, p.idle.Len(); i < n; i++ {
+ e := p.idle.Front()
+ if e == nil {
+ break
+ }
+ ic := e.Value.(idleConn)
+ p.idle.Remove(e)
+ test := p.TestOnBorrow
+ p.mu.Unlock()
+ if test == nil || test(ic.c, ic.t) == nil {
+ return ic.c, nil
+ }
+ ic.c.Close()
+ p.mu.Lock()
+ p.release()
+ }
+
+ // Check for pool closed before dialing a new connection.
+
+ if p.closed {
+ p.mu.Unlock()
+ return nil, errors.New("redigo: get on closed pool")
+ }
+
+ // Dial new connection if under limit.
+
+ if p.MaxActive == 0 || p.active < p.MaxActive {
+ dial := p.Dial
+ p.active += 1
+ p.mu.Unlock()
+ c, err := dial()
+ if err != nil {
+ p.mu.Lock()
+ p.release()
+ p.mu.Unlock()
+ c = nil
+ }
+ return c, err
+ }
+
+ if !p.Wait {
+ p.mu.Unlock()
+ return nil, ErrPoolExhausted
+ }
+
+ if p.cond == nil {
+ p.cond = sync.NewCond(&p.mu)
+ }
+ p.cond.Wait()
+ }
+}
+
+func (p *Pool) put(c Conn, forceClose bool) error {
+ err := c.Err()
+ p.mu.Lock()
+ if !p.closed && err == nil && !forceClose {
+ p.idle.PushFront(idleConn{t: nowFunc(), c: c})
+ if p.idle.Len() > p.MaxIdle {
+ c = p.idle.Remove(p.idle.Back()).(idleConn).c
+ } else {
+ c = nil
+ }
+ }
+
+ if c == nil {
+ if p.cond != nil {
+ p.cond.Signal()
+ }
+ p.mu.Unlock()
+ return nil
+ }
+
+ p.release()
+ p.mu.Unlock()
+ return c.Close()
+}
+
+type pooledConnection struct {
+ p *Pool
+ c Conn
+ state int
+}
+
+var (
+ sentinel []byte
+ sentinelOnce sync.Once
+)
+
+func initSentinel() {
+ p := make([]byte, 64)
+ if _, err := rand.Read(p); err == nil {
+ sentinel = p
+ } else {
+ h := sha1.New()
+ io.WriteString(h, "Oops, rand failed. Use time instead.")
+ io.WriteString(h, strconv.FormatInt(time.Now().UnixNano(), 10))
+ sentinel = h.Sum(nil)
+ }
+}
+
+func (pc *pooledConnection) Close() error {
+ c := pc.c
+ if _, ok := c.(errorConnection); ok {
+ return nil
+ }
+ pc.c = errorConnection{errConnClosed}
+
+ if pc.state&internal.MultiState != 0 {
+ c.Send("DISCARD")
+ pc.state &^= (internal.MultiState | internal.WatchState)
+ } else if pc.state&internal.WatchState != 0 {
+ c.Send("UNWATCH")
+ pc.state &^= internal.WatchState
+ }
+ if pc.state&internal.SubscribeState != 0 {
+ c.Send("UNSUBSCRIBE")
+ c.Send("PUNSUBSCRIBE")
+ // To detect the end of the message stream, ask the server to echo
+ // a sentinel value and read until we see that value.
+ sentinelOnce.Do(initSentinel)
+ c.Send("ECHO", sentinel)
+ c.Flush()
+ for {
+ p, err := c.Receive()
+ if err != nil {
+ break
+ }
+ if p, ok := p.([]byte); ok && bytes.Equal(p, sentinel) {
+ pc.state &^= internal.SubscribeState
+ break
+ }
+ }
+ }
+ c.Do("")
+ pc.p.put(c, pc.state != 0)
+ return nil
+}
+
+func (pc *pooledConnection) Err() error {
+ return pc.c.Err()
+}
+
+func (pc *pooledConnection) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
+ ci := internal.LookupCommandInfo(commandName)
+ pc.state = (pc.state | ci.Set) &^ ci.Clear
+ return pc.c.Do(commandName, args...)
+}
+
+func (pc *pooledConnection) Send(commandName string, args ...interface{}) error {
+ ci := internal.LookupCommandInfo(commandName)
+ pc.state = (pc.state | ci.Set) &^ ci.Clear
+ return pc.c.Send(commandName, args...)
+}
+
+func (pc *pooledConnection) Flush() error {
+ return pc.c.Flush()
+}
+
+func (pc *pooledConnection) Receive() (reply interface{}, err error) {
+ return pc.c.Receive()
+}
+
+type errorConnection struct{ err error }
+
+func (ec errorConnection) Do(string, ...interface{}) (interface{}, error) { return nil, ec.err }
+func (ec errorConnection) Send(string, ...interface{}) error { return ec.err }
+func (ec errorConnection) Err() error { return ec.err }
+func (ec errorConnection) Close() error { return ec.err }
+func (ec errorConnection) Flush() error { return ec.err }
+func (ec errorConnection) Receive() (interface{}, error) { return nil, ec.err }
diff --git a/vendor/github.com/garyburd/redigo/redis/pubsub.go b/vendor/github.com/garyburd/redigo/redis/pubsub.go
new file mode 100644
index 000000000..c0ecce824
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/pubsub.go
@@ -0,0 +1,144 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import "errors"
+
+// Subscription represents a subscribe or unsubscribe notification.
+type Subscription struct {
+
+ // Kind is "subscribe", "unsubscribe", "psubscribe" or "punsubscribe"
+ Kind string
+
+ // The channel that was changed.
+ Channel string
+
+ // The current number of subscriptions for connection.
+ Count int
+}
+
+// Message represents a message notification.
+type Message struct {
+
+ // The originating channel.
+ Channel string
+
+ // The message data.
+ Data []byte
+}
+
+// PMessage represents a pmessage notification.
+type PMessage struct {
+
+ // The matched pattern.
+ Pattern string
+
+ // The originating channel.
+ Channel string
+
+ // The message data.
+ Data []byte
+}
+
+// Pong represents a pubsub pong notification.
+type Pong struct {
+ Data string
+}
+
+// PubSubConn wraps a Conn with convenience methods for subscribers.
+type PubSubConn struct {
+ Conn Conn
+}
+
+// Close closes the connection.
+func (c PubSubConn) Close() error {
+ return c.Conn.Close()
+}
+
+// Subscribe subscribes the connection to the specified channels.
+func (c PubSubConn) Subscribe(channel ...interface{}) error {
+ c.Conn.Send("SUBSCRIBE", channel...)
+ return c.Conn.Flush()
+}
+
+// PSubscribe subscribes the connection to the given patterns.
+func (c PubSubConn) PSubscribe(channel ...interface{}) error {
+ c.Conn.Send("PSUBSCRIBE", channel...)
+ return c.Conn.Flush()
+}
+
+// Unsubscribe unsubscribes the connection from the given channels, or from all
+// of them if none is given.
+func (c PubSubConn) Unsubscribe(channel ...interface{}) error {
+ c.Conn.Send("UNSUBSCRIBE", channel...)
+ return c.Conn.Flush()
+}
+
+// PUnsubscribe unsubscribes the connection from the given patterns, or from all
+// of them if none is given.
+func (c PubSubConn) PUnsubscribe(channel ...interface{}) error {
+ c.Conn.Send("PUNSUBSCRIBE", channel...)
+ return c.Conn.Flush()
+}
+
+// Ping sends a PING to the server with the specified data.
+func (c PubSubConn) Ping(data string) error {
+ c.Conn.Send("PING", data)
+ return c.Conn.Flush()
+}
+
+// Receive returns a pushed message as a Subscription, Message, PMessage, Pong
+// or error. The return value is intended to be used directly in a type switch
+// as illustrated in the PubSubConn example.
+func (c PubSubConn) Receive() interface{} {
+ reply, err := Values(c.Conn.Receive())
+ if err != nil {
+ return err
+ }
+
+ var kind string
+ reply, err = Scan(reply, &kind)
+ if err != nil {
+ return err
+ }
+
+ switch kind {
+ case "message":
+ var m Message
+ if _, err := Scan(reply, &m.Channel, &m.Data); err != nil {
+ return err
+ }
+ return m
+ case "pmessage":
+ var pm PMessage
+ if _, err := Scan(reply, &pm.Pattern, &pm.Channel, &pm.Data); err != nil {
+ return err
+ }
+ return pm
+ case "subscribe", "psubscribe", "unsubscribe", "punsubscribe":
+ s := Subscription{Kind: kind}
+ if _, err := Scan(reply, &s.Channel, &s.Count); err != nil {
+ return err
+ }
+ return s
+ case "pong":
+ var p Pong
+ if _, err := Scan(reply, &p.Data); err != nil {
+ return err
+ }
+ return p
+ }
+ return errors.New("redigo: unknown pubsub notification")
+}
diff --git a/vendor/github.com/garyburd/redigo/redis/redis.go b/vendor/github.com/garyburd/redigo/redis/redis.go
new file mode 100644
index 000000000..c90a48ed4
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/redis.go
@@ -0,0 +1,44 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+// Error represents an error returned in a command reply.
+type Error string
+
+func (err Error) Error() string { return string(err) }
+
+// Conn represents a connection to a Redis server.
+type Conn interface {
+ // Close closes the connection.
+ Close() error
+
+ // Err returns a non-nil value if the connection is broken. The returned
+ // value is either the first non-nil value returned from the underlying
+ // network connection or a protocol parsing error. Applications should
+ // close broken connections.
+ Err() error
+
+ // Do sends a command to the server and returns the received reply.
+ Do(commandName string, args ...interface{}) (reply interface{}, err error)
+
+ // Send writes the command to the client's output buffer.
+ Send(commandName string, args ...interface{}) error
+
+ // Flush flushes the output buffer to the Redis server.
+ Flush() error
+
+ // Receive receives a single reply from the Redis server
+ Receive() (reply interface{}, err error)
+}
diff --git a/vendor/github.com/garyburd/redigo/redis/reply.go b/vendor/github.com/garyburd/redigo/redis/reply.go
new file mode 100644
index 000000000..57896147f
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/reply.go
@@ -0,0 +1,393 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import (
+ "errors"
+ "fmt"
+ "strconv"
+)
+
+// ErrNil indicates that a reply value is nil.
+var ErrNil = errors.New("redigo: nil returned")
+
+// Int is a helper that converts a command reply to an integer. If err is not
+// equal to nil, then Int returns 0, err. Otherwise, Int converts the
+// reply to an int as follows:
+//
+// Reply type Result
+// integer int(reply), nil
+// bulk string parsed reply, nil
+// nil 0, ErrNil
+// other 0, error
+func Int(reply interface{}, err error) (int, error) {
+ if err != nil {
+ return 0, err
+ }
+ switch reply := reply.(type) {
+ case int64:
+ x := int(reply)
+ if int64(x) != reply {
+ return 0, strconv.ErrRange
+ }
+ return x, nil
+ case []byte:
+ n, err := strconv.ParseInt(string(reply), 10, 0)
+ return int(n), err
+ case nil:
+ return 0, ErrNil
+ case Error:
+ return 0, reply
+ }
+ return 0, fmt.Errorf("redigo: unexpected type for Int, got type %T", reply)
+}
+
+// Int64 is a helper that converts a command reply to 64 bit integer. If err is
+// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the
+// reply to an int64 as follows:
+//
+// Reply type Result
+// integer reply, nil
+// bulk string parsed reply, nil
+// nil 0, ErrNil
+// other 0, error
+func Int64(reply interface{}, err error) (int64, error) {
+ if err != nil {
+ return 0, err
+ }
+ switch reply := reply.(type) {
+ case int64:
+ return reply, nil
+ case []byte:
+ n, err := strconv.ParseInt(string(reply), 10, 64)
+ return n, err
+ case nil:
+ return 0, ErrNil
+ case Error:
+ return 0, reply
+ }
+ return 0, fmt.Errorf("redigo: unexpected type for Int64, got type %T", reply)
+}
+
+var errNegativeInt = errors.New("redigo: unexpected value for Uint64")
+
+// Uint64 is a helper that converts a command reply to 64 bit integer. If err is
+// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the
+// reply to an int64 as follows:
+//
+// Reply type Result
+// integer reply, nil
+// bulk string parsed reply, nil
+// nil 0, ErrNil
+// other 0, error
+func Uint64(reply interface{}, err error) (uint64, error) {
+ if err != nil {
+ return 0, err
+ }
+ switch reply := reply.(type) {
+ case int64:
+ if reply < 0 {
+ return 0, errNegativeInt
+ }
+ return uint64(reply), nil
+ case []byte:
+ n, err := strconv.ParseUint(string(reply), 10, 64)
+ return n, err
+ case nil:
+ return 0, ErrNil
+ case Error:
+ return 0, reply
+ }
+ return 0, fmt.Errorf("redigo: unexpected type for Uint64, got type %T", reply)
+}
+
+// Float64 is a helper that converts a command reply to 64 bit float. If err is
+// not equal to nil, then Float64 returns 0, err. Otherwise, Float64 converts
+// the reply to an int as follows:
+//
+// Reply type Result
+// bulk string parsed reply, nil
+// nil 0, ErrNil
+// other 0, error
+func Float64(reply interface{}, err error) (float64, error) {
+ if err != nil {
+ return 0, err
+ }
+ switch reply := reply.(type) {
+ case []byte:
+ n, err := strconv.ParseFloat(string(reply), 64)
+ return n, err
+ case nil:
+ return 0, ErrNil
+ case Error:
+ return 0, reply
+ }
+ return 0, fmt.Errorf("redigo: unexpected type for Float64, got type %T", reply)
+}
+
+// String is a helper that converts a command reply to a string. If err is not
+// equal to nil, then String returns "", err. Otherwise String converts the
+// reply to a string as follows:
+//
+// Reply type Result
+// bulk string string(reply), nil
+// simple string reply, nil
+// nil "", ErrNil
+// other "", error
+func String(reply interface{}, err error) (string, error) {
+ if err != nil {
+ return "", err
+ }
+ switch reply := reply.(type) {
+ case []byte:
+ return string(reply), nil
+ case string:
+ return reply, nil
+ case nil:
+ return "", ErrNil
+ case Error:
+ return "", reply
+ }
+ return "", fmt.Errorf("redigo: unexpected type for String, got type %T", reply)
+}
+
+// Bytes is a helper that converts a command reply to a slice of bytes. If err
+// is not equal to nil, then Bytes returns nil, err. Otherwise Bytes converts
+// the reply to a slice of bytes as follows:
+//
+// Reply type Result
+// bulk string reply, nil
+// simple string []byte(reply), nil
+// nil nil, ErrNil
+// other nil, error
+func Bytes(reply interface{}, err error) ([]byte, error) {
+ if err != nil {
+ return nil, err
+ }
+ switch reply := reply.(type) {
+ case []byte:
+ return reply, nil
+ case string:
+ return []byte(reply), nil
+ case nil:
+ return nil, ErrNil
+ case Error:
+ return nil, reply
+ }
+ return nil, fmt.Errorf("redigo: unexpected type for Bytes, got type %T", reply)
+}
+
+// Bool is a helper that converts a command reply to a boolean. If err is not
+// equal to nil, then Bool returns false, err. Otherwise Bool converts the
+// reply to boolean as follows:
+//
+// Reply type Result
+// integer value != 0, nil
+// bulk string strconv.ParseBool(reply)
+// nil false, ErrNil
+// other false, error
+func Bool(reply interface{}, err error) (bool, error) {
+ if err != nil {
+ return false, err
+ }
+ switch reply := reply.(type) {
+ case int64:
+ return reply != 0, nil
+ case []byte:
+ return strconv.ParseBool(string(reply))
+ case nil:
+ return false, ErrNil
+ case Error:
+ return false, reply
+ }
+ return false, fmt.Errorf("redigo: unexpected type for Bool, got type %T", reply)
+}
+
+// MultiBulk is a helper that converts an array command reply to a []interface{}.
+//
+// Deprecated: Use Values instead.
+func MultiBulk(reply interface{}, err error) ([]interface{}, error) { return Values(reply, err) }
+
+// Values is a helper that converts an array command reply to a []interface{}.
+// If err is not equal to nil, then Values returns nil, err. Otherwise, Values
+// converts the reply as follows:
+//
+// Reply type Result
+// array reply, nil
+// nil nil, ErrNil
+// other nil, error
+func Values(reply interface{}, err error) ([]interface{}, error) {
+ if err != nil {
+ return nil, err
+ }
+ switch reply := reply.(type) {
+ case []interface{}:
+ return reply, nil
+ case nil:
+ return nil, ErrNil
+ case Error:
+ return nil, reply
+ }
+ return nil, fmt.Errorf("redigo: unexpected type for Values, got type %T", reply)
+}
+
+// Strings is a helper that converts an array command reply to a []string. If
+// err is not equal to nil, then Strings returns nil, err. Nil array items are
+// converted to "" in the output slice. Strings returns an error if an array
+// item is not a bulk string or nil.
+func Strings(reply interface{}, err error) ([]string, error) {
+ if err != nil {
+ return nil, err
+ }
+ switch reply := reply.(type) {
+ case []interface{}:
+ result := make([]string, len(reply))
+ for i := range reply {
+ if reply[i] == nil {
+ continue
+ }
+ p, ok := reply[i].([]byte)
+ if !ok {
+ return nil, fmt.Errorf("redigo: unexpected element type for Strings, got type %T", reply[i])
+ }
+ result[i] = string(p)
+ }
+ return result, nil
+ case nil:
+ return nil, ErrNil
+ case Error:
+ return nil, reply
+ }
+ return nil, fmt.Errorf("redigo: unexpected type for Strings, got type %T", reply)
+}
+
+// ByteSlices is a helper that converts an array command reply to a [][]byte.
+// If err is not equal to nil, then ByteSlices returns nil, err. Nil array
+// items are stay nil. ByteSlices returns an error if an array item is not a
+// bulk string or nil.
+func ByteSlices(reply interface{}, err error) ([][]byte, error) {
+ if err != nil {
+ return nil, err
+ }
+ switch reply := reply.(type) {
+ case []interface{}:
+ result := make([][]byte, len(reply))
+ for i := range reply {
+ if reply[i] == nil {
+ continue
+ }
+ p, ok := reply[i].([]byte)
+ if !ok {
+ return nil, fmt.Errorf("redigo: unexpected element type for ByteSlices, got type %T", reply[i])
+ }
+ result[i] = p
+ }
+ return result, nil
+ case nil:
+ return nil, ErrNil
+ case Error:
+ return nil, reply
+ }
+ return nil, fmt.Errorf("redigo: unexpected type for ByteSlices, got type %T", reply)
+}
+
+// Ints is a helper that converts an array command reply to a []int. If
+// err is not equal to nil, then Ints returns nil, err.
+func Ints(reply interface{}, err error) ([]int, error) {
+ var ints []int
+ values, err := Values(reply, err)
+ if err != nil {
+ return ints, err
+ }
+ if err := ScanSlice(values, &ints); err != nil {
+ return ints, err
+ }
+ return ints, nil
+}
+
+// StringMap is a helper that converts an array of strings (alternating key, value)
+// into a map[string]string. The HGETALL and CONFIG GET commands return replies in this format.
+// Requires an even number of values in result.
+func StringMap(result interface{}, err error) (map[string]string, error) {
+ values, err := Values(result, err)
+ if err != nil {
+ return nil, err
+ }
+ if len(values)%2 != 0 {
+ return nil, errors.New("redigo: StringMap expects even number of values result")
+ }
+ m := make(map[string]string, len(values)/2)
+ for i := 0; i < len(values); i += 2 {
+ key, okKey := values[i].([]byte)
+ value, okValue := values[i+1].([]byte)
+ if !okKey || !okValue {
+ return nil, errors.New("redigo: ScanMap key not a bulk string value")
+ }
+ m[string(key)] = string(value)
+ }
+ return m, nil
+}
+
+// IntMap is a helper that converts an array of strings (alternating key, value)
+// into a map[string]int. The HGETALL commands return replies in this format.
+// Requires an even number of values in result.
+func IntMap(result interface{}, err error) (map[string]int, error) {
+ values, err := Values(result, err)
+ if err != nil {
+ return nil, err
+ }
+ if len(values)%2 != 0 {
+ return nil, errors.New("redigo: IntMap expects even number of values result")
+ }
+ m := make(map[string]int, len(values)/2)
+ for i := 0; i < len(values); i += 2 {
+ key, ok := values[i].([]byte)
+ if !ok {
+ return nil, errors.New("redigo: ScanMap key not a bulk string value")
+ }
+ value, err := Int(values[i+1], nil)
+ if err != nil {
+ return nil, err
+ }
+ m[string(key)] = value
+ }
+ return m, nil
+}
+
+// Int64Map is a helper that converts an array of strings (alternating key, value)
+// into a map[string]int64. The HGETALL commands return replies in this format.
+// Requires an even number of values in result.
+func Int64Map(result interface{}, err error) (map[string]int64, error) {
+ values, err := Values(result, err)
+ if err != nil {
+ return nil, err
+ }
+ if len(values)%2 != 0 {
+ return nil, errors.New("redigo: Int64Map expects even number of values result")
+ }
+ m := make(map[string]int64, len(values)/2)
+ for i := 0; i < len(values); i += 2 {
+ key, ok := values[i].([]byte)
+ if !ok {
+ return nil, errors.New("redigo: ScanMap key not a bulk string value")
+ }
+ value, err := Int64(values[i+1], nil)
+ if err != nil {
+ return nil, err
+ }
+ m[string(key)] = value
+ }
+ return m, nil
+}
diff --git a/vendor/github.com/garyburd/redigo/redis/scan.go b/vendor/github.com/garyburd/redigo/redis/scan.go
new file mode 100644
index 000000000..962e94bcc
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/scan.go
@@ -0,0 +1,555 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+func ensureLen(d reflect.Value, n int) {
+ if n > d.Cap() {
+ d.Set(reflect.MakeSlice(d.Type(), n, n))
+ } else {
+ d.SetLen(n)
+ }
+}
+
+func cannotConvert(d reflect.Value, s interface{}) error {
+ var sname string
+ switch s.(type) {
+ case string:
+ sname = "Redis simple string"
+ case Error:
+ sname = "Redis error"
+ case int64:
+ sname = "Redis integer"
+ case []byte:
+ sname = "Redis bulk string"
+ case []interface{}:
+ sname = "Redis array"
+ default:
+ sname = reflect.TypeOf(s).String()
+ }
+ return fmt.Errorf("cannot convert from %s to %s", sname, d.Type())
+}
+
+func convertAssignBulkString(d reflect.Value, s []byte) (err error) {
+ switch d.Type().Kind() {
+ case reflect.Float32, reflect.Float64:
+ var x float64
+ x, err = strconv.ParseFloat(string(s), d.Type().Bits())
+ d.SetFloat(x)
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ var x int64
+ x, err = strconv.ParseInt(string(s), 10, d.Type().Bits())
+ d.SetInt(x)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ var x uint64
+ x, err = strconv.ParseUint(string(s), 10, d.Type().Bits())
+ d.SetUint(x)
+ case reflect.Bool:
+ var x bool
+ x, err = strconv.ParseBool(string(s))
+ d.SetBool(x)
+ case reflect.String:
+ d.SetString(string(s))
+ case reflect.Slice:
+ if d.Type().Elem().Kind() != reflect.Uint8 {
+ err = cannotConvert(d, s)
+ } else {
+ d.SetBytes(s)
+ }
+ default:
+ err = cannotConvert(d, s)
+ }
+ return
+}
+
+func convertAssignInt(d reflect.Value, s int64) (err error) {
+ switch d.Type().Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ d.SetInt(s)
+ if d.Int() != s {
+ err = strconv.ErrRange
+ d.SetInt(0)
+ }
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ if s < 0 {
+ err = strconv.ErrRange
+ } else {
+ x := uint64(s)
+ d.SetUint(x)
+ if d.Uint() != x {
+ err = strconv.ErrRange
+ d.SetUint(0)
+ }
+ }
+ case reflect.Bool:
+ d.SetBool(s != 0)
+ default:
+ err = cannotConvert(d, s)
+ }
+ return
+}
+
+func convertAssignValue(d reflect.Value, s interface{}) (err error) {
+ switch s := s.(type) {
+ case []byte:
+ err = convertAssignBulkString(d, s)
+ case int64:
+ err = convertAssignInt(d, s)
+ default:
+ err = cannotConvert(d, s)
+ }
+ return err
+}
+
+func convertAssignArray(d reflect.Value, s []interface{}) error {
+ if d.Type().Kind() != reflect.Slice {
+ return cannotConvert(d, s)
+ }
+ ensureLen(d, len(s))
+ for i := 0; i < len(s); i++ {
+ if err := convertAssignValue(d.Index(i), s[i]); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func convertAssign(d interface{}, s interface{}) (err error) {
+ // Handle the most common destination types using type switches and
+ // fall back to reflection for all other types.
+ switch s := s.(type) {
+ case nil:
+ // ingore
+ case []byte:
+ switch d := d.(type) {
+ case *string:
+ *d = string(s)
+ case *int:
+ *d, err = strconv.Atoi(string(s))
+ case *bool:
+ *d, err = strconv.ParseBool(string(s))
+ case *[]byte:
+ *d = s
+ case *interface{}:
+ *d = s
+ case nil:
+ // skip value
+ default:
+ if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
+ err = cannotConvert(d, s)
+ } else {
+ err = convertAssignBulkString(d.Elem(), s)
+ }
+ }
+ case int64:
+ switch d := d.(type) {
+ case *int:
+ x := int(s)
+ if int64(x) != s {
+ err = strconv.ErrRange
+ x = 0
+ }
+ *d = x
+ case *bool:
+ *d = s != 0
+ case *interface{}:
+ *d = s
+ case nil:
+ // skip value
+ default:
+ if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
+ err = cannotConvert(d, s)
+ } else {
+ err = convertAssignInt(d.Elem(), s)
+ }
+ }
+ case string:
+ switch d := d.(type) {
+ case *string:
+ *d = string(s)
+ default:
+ err = cannotConvert(reflect.ValueOf(d), s)
+ }
+ case []interface{}:
+ switch d := d.(type) {
+ case *[]interface{}:
+ *d = s
+ case *interface{}:
+ *d = s
+ case nil:
+ // skip value
+ default:
+ if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
+ err = cannotConvert(d, s)
+ } else {
+ err = convertAssignArray(d.Elem(), s)
+ }
+ }
+ case Error:
+ err = s
+ default:
+ err = cannotConvert(reflect.ValueOf(d), s)
+ }
+ return
+}
+
+// Scan copies from src to the values pointed at by dest.
+//
+// The values pointed at by dest must be an integer, float, boolean, string,
+// []byte, interface{} or slices of these types. Scan uses the standard strconv
+// package to convert bulk strings to numeric and boolean types.
+//
+// If a dest value is nil, then the corresponding src value is skipped.
+//
+// If a src element is nil, then the corresponding dest value is not modified.
+//
+// To enable easy use of Scan in a loop, Scan returns the slice of src
+// following the copied values.
+func Scan(src []interface{}, dest ...interface{}) ([]interface{}, error) {
+ if len(src) < len(dest) {
+ return nil, errors.New("redigo.Scan: array short")
+ }
+ var err error
+ for i, d := range dest {
+ err = convertAssign(d, src[i])
+ if err != nil {
+ err = fmt.Errorf("redigo.Scan: cannot assign to dest %d: %v", i, err)
+ break
+ }
+ }
+ return src[len(dest):], err
+}
+
+type fieldSpec struct {
+ name string
+ index []int
+ omitEmpty bool
+}
+
+type structSpec struct {
+ m map[string]*fieldSpec
+ l []*fieldSpec
+}
+
+func (ss *structSpec) fieldSpec(name []byte) *fieldSpec {
+ return ss.m[string(name)]
+}
+
+func compileStructSpec(t reflect.Type, depth map[string]int, index []int, ss *structSpec) {
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ switch {
+ case f.PkgPath != "" && !f.Anonymous:
+ // Ignore unexported fields.
+ case f.Anonymous:
+ // TODO: Handle pointers. Requires change to decoder and
+ // protection against infinite recursion.
+ if f.Type.Kind() == reflect.Struct {
+ compileStructSpec(f.Type, depth, append(index, i), ss)
+ }
+ default:
+ fs := &fieldSpec{name: f.Name}
+ tag := f.Tag.Get("redis")
+ p := strings.Split(tag, ",")
+ if len(p) > 0 {
+ if p[0] == "-" {
+ continue
+ }
+ if len(p[0]) > 0 {
+ fs.name = p[0]
+ }
+ for _, s := range p[1:] {
+ switch s {
+ case "omitempty":
+ fs.omitEmpty = true
+ default:
+ panic(fmt.Errorf("redigo: unknown field tag %s for type %s", s, t.Name()))
+ }
+ }
+ }
+ d, found := depth[fs.name]
+ if !found {
+ d = 1 << 30
+ }
+ switch {
+ case len(index) == d:
+ // At same depth, remove from result.
+ delete(ss.m, fs.name)
+ j := 0
+ for i := 0; i < len(ss.l); i++ {
+ if fs.name != ss.l[i].name {
+ ss.l[j] = ss.l[i]
+ j += 1
+ }
+ }
+ ss.l = ss.l[:j]
+ case len(index) < d:
+ fs.index = make([]int, len(index)+1)
+ copy(fs.index, index)
+ fs.index[len(index)] = i
+ depth[fs.name] = len(index)
+ ss.m[fs.name] = fs
+ ss.l = append(ss.l, fs)
+ }
+ }
+ }
+}
+
+var (
+ structSpecMutex sync.RWMutex
+ structSpecCache = make(map[reflect.Type]*structSpec)
+ defaultFieldSpec = &fieldSpec{}
+)
+
+func structSpecForType(t reflect.Type) *structSpec {
+
+ structSpecMutex.RLock()
+ ss, found := structSpecCache[t]
+ structSpecMutex.RUnlock()
+ if found {
+ return ss
+ }
+
+ structSpecMutex.Lock()
+ defer structSpecMutex.Unlock()
+ ss, found = structSpecCache[t]
+ if found {
+ return ss
+ }
+
+ ss = &structSpec{m: make(map[string]*fieldSpec)}
+ compileStructSpec(t, make(map[string]int), nil, ss)
+ structSpecCache[t] = ss
+ return ss
+}
+
+var errScanStructValue = errors.New("redigo.ScanStruct: value must be non-nil pointer to a struct")
+
+// ScanStruct scans alternating names and values from src to a struct. The
+// HGETALL and CONFIG GET commands return replies in this format.
+//
+// ScanStruct uses exported field names to match values in the response. Use
+// 'redis' field tag to override the name:
+//
+// Field int `redis:"myName"`
+//
+// Fields with the tag redis:"-" are ignored.
+//
+// Integer, float, boolean, string and []byte fields are supported. Scan uses the
+// standard strconv package to convert bulk string values to numeric and
+// boolean types.
+//
+// If a src element is nil, then the corresponding field is not modified.
+func ScanStruct(src []interface{}, dest interface{}) error {
+ d := reflect.ValueOf(dest)
+ if d.Kind() != reflect.Ptr || d.IsNil() {
+ return errScanStructValue
+ }
+ d = d.Elem()
+ if d.Kind() != reflect.Struct {
+ return errScanStructValue
+ }
+ ss := structSpecForType(d.Type())
+
+ if len(src)%2 != 0 {
+ return errors.New("redigo.ScanStruct: number of values not a multiple of 2")
+ }
+
+ for i := 0; i < len(src); i += 2 {
+ s := src[i+1]
+ if s == nil {
+ continue
+ }
+ name, ok := src[i].([]byte)
+ if !ok {
+ return fmt.Errorf("redigo.ScanStruct: key %d not a bulk string value", i)
+ }
+ fs := ss.fieldSpec(name)
+ if fs == nil {
+ continue
+ }
+ if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil {
+ return fmt.Errorf("redigo.ScanStruct: cannot assign field %s: %v", fs.name, err)
+ }
+ }
+ return nil
+}
+
+var (
+ errScanSliceValue = errors.New("redigo.ScanSlice: dest must be non-nil pointer to a struct")
+)
+
+// ScanSlice scans src to the slice pointed to by dest. The elements the dest
+// slice must be integer, float, boolean, string, struct or pointer to struct
+// values.
+//
+// Struct fields must be integer, float, boolean or string values. All struct
+// fields are used unless a subset is specified using fieldNames.
+func ScanSlice(src []interface{}, dest interface{}, fieldNames ...string) error {
+ d := reflect.ValueOf(dest)
+ if d.Kind() != reflect.Ptr || d.IsNil() {
+ return errScanSliceValue
+ }
+ d = d.Elem()
+ if d.Kind() != reflect.Slice {
+ return errScanSliceValue
+ }
+
+ isPtr := false
+ t := d.Type().Elem()
+ if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
+ isPtr = true
+ t = t.Elem()
+ }
+
+ if t.Kind() != reflect.Struct {
+ ensureLen(d, len(src))
+ for i, s := range src {
+ if s == nil {
+ continue
+ }
+ if err := convertAssignValue(d.Index(i), s); err != nil {
+ return fmt.Errorf("redigo.ScanSlice: cannot assign element %d: %v", i, err)
+ }
+ }
+ return nil
+ }
+
+ ss := structSpecForType(t)
+ fss := ss.l
+ if len(fieldNames) > 0 {
+ fss = make([]*fieldSpec, len(fieldNames))
+ for i, name := range fieldNames {
+ fss[i] = ss.m[name]
+ if fss[i] == nil {
+ return fmt.Errorf("redigo.ScanSlice: ScanSlice bad field name %s", name)
+ }
+ }
+ }
+
+ if len(fss) == 0 {
+ return errors.New("redigo.ScanSlice: no struct fields")
+ }
+
+ n := len(src) / len(fss)
+ if n*len(fss) != len(src) {
+ return errors.New("redigo.ScanSlice: length not a multiple of struct field count")
+ }
+
+ ensureLen(d, n)
+ for i := 0; i < n; i++ {
+ d := d.Index(i)
+ if isPtr {
+ if d.IsNil() {
+ d.Set(reflect.New(t))
+ }
+ d = d.Elem()
+ }
+ for j, fs := range fss {
+ s := src[i*len(fss)+j]
+ if s == nil {
+ continue
+ }
+ if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil {
+ return fmt.Errorf("redigo.ScanSlice: cannot assign element %d to field %s: %v", i*len(fss)+j, fs.name, err)
+ }
+ }
+ }
+ return nil
+}
+
+// Args is a helper for constructing command arguments from structured values.
+type Args []interface{}
+
+// Add returns the result of appending value to args.
+func (args Args) Add(value ...interface{}) Args {
+ return append(args, value...)
+}
+
+// AddFlat returns the result of appending the flattened value of v to args.
+//
+// Maps are flattened by appending the alternating keys and map values to args.
+//
+// Slices are flattened by appending the slice elements to args.
+//
+// Structs are flattened by appending the alternating names and values of
+// exported fields to args. If v is a nil struct pointer, then nothing is
+// appended. The 'redis' field tag overrides struct field names. See ScanStruct
+// for more information on the use of the 'redis' field tag.
+//
+// Other types are appended to args as is.
+func (args Args) AddFlat(v interface{}) Args {
+ rv := reflect.ValueOf(v)
+ switch rv.Kind() {
+ case reflect.Struct:
+ args = flattenStruct(args, rv)
+ case reflect.Slice:
+ for i := 0; i < rv.Len(); i++ {
+ args = append(args, rv.Index(i).Interface())
+ }
+ case reflect.Map:
+ for _, k := range rv.MapKeys() {
+ args = append(args, k.Interface(), rv.MapIndex(k).Interface())
+ }
+ case reflect.Ptr:
+ if rv.Type().Elem().Kind() == reflect.Struct {
+ if !rv.IsNil() {
+ args = flattenStruct(args, rv.Elem())
+ }
+ } else {
+ args = append(args, v)
+ }
+ default:
+ args = append(args, v)
+ }
+ return args
+}
+
+func flattenStruct(args Args, v reflect.Value) Args {
+ ss := structSpecForType(v.Type())
+ for _, fs := range ss.l {
+ fv := v.FieldByIndex(fs.index)
+ if fs.omitEmpty {
+ var empty = false
+ switch fv.Kind() {
+ case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+ empty = fv.Len() == 0
+ case reflect.Bool:
+ empty = !fv.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ empty = fv.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ empty = fv.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ empty = fv.Float() == 0
+ case reflect.Interface, reflect.Ptr:
+ empty = fv.IsNil()
+ }
+ if empty {
+ continue
+ }
+ }
+ args = append(args, fs.name, fv.Interface())
+ }
+ return args
+}
diff --git a/vendor/github.com/garyburd/redigo/redis/script.go b/vendor/github.com/garyburd/redigo/redis/script.go
new file mode 100644
index 000000000..78605a90a
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/script.go
@@ -0,0 +1,86 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import (
+ "crypto/sha1"
+ "encoding/hex"
+ "io"
+ "strings"
+)
+
+// Script encapsulates the source, hash and key count for a Lua script. See
+// http://redis.io/commands/eval for information on scripts in Redis.
+type Script struct {
+ keyCount int
+ src string
+ hash string
+}
+
+// NewScript returns a new script object. If keyCount is greater than or equal
+// to zero, then the count is automatically inserted in the EVAL command
+// argument list. If keyCount is less than zero, then the application supplies
+// the count as the first value in the keysAndArgs argument to the Do, Send and
+// SendHash methods.
+func NewScript(keyCount int, src string) *Script {
+ h := sha1.New()
+ io.WriteString(h, src)
+ return &Script{keyCount, src, hex.EncodeToString(h.Sum(nil))}
+}
+
+func (s *Script) args(spec string, keysAndArgs []interface{}) []interface{} {
+ var args []interface{}
+ if s.keyCount < 0 {
+ args = make([]interface{}, 1+len(keysAndArgs))
+ args[0] = spec
+ copy(args[1:], keysAndArgs)
+ } else {
+ args = make([]interface{}, 2+len(keysAndArgs))
+ args[0] = spec
+ args[1] = s.keyCount
+ copy(args[2:], keysAndArgs)
+ }
+ return args
+}
+
+// Do evaluates the script. Under the covers, Do optimistically evaluates the
+// script using the EVALSHA command. If the command fails because the script is
+// not loaded, then Do evaluates the script using the EVAL command (thus
+// causing the script to load).
+func (s *Script) Do(c Conn, keysAndArgs ...interface{}) (interface{}, error) {
+ v, err := c.Do("EVALSHA", s.args(s.hash, keysAndArgs)...)
+ if e, ok := err.(Error); ok && strings.HasPrefix(string(e), "NOSCRIPT ") {
+ v, err = c.Do("EVAL", s.args(s.src, keysAndArgs)...)
+ }
+ return v, err
+}
+
+// SendHash evaluates the script without waiting for the reply. The script is
+// evaluated with the EVALSHA command. The application must ensure that the
+// script is loaded by a previous call to Send, Do or Load methods.
+func (s *Script) SendHash(c Conn, keysAndArgs ...interface{}) error {
+ return c.Send("EVALSHA", s.args(s.hash, keysAndArgs)...)
+}
+
+// Send evaluates the script without waiting for the reply.
+func (s *Script) Send(c Conn, keysAndArgs ...interface{}) error {
+ return c.Send("EVAL", s.args(s.src, keysAndArgs)...)
+}
+
+// Load loads the script without evaluating it.
+func (s *Script) Load(c Conn) error {
+ _, err := c.Do("SCRIPT", "LOAD", s.src)
+ return err
+}
diff --git a/vendor/github.com/go-gorp/gorp/.gitignore b/vendor/github.com/go-gorp/gorp/.gitignore
new file mode 100644
index 000000000..d18c8ca70
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/.gitignore
@@ -0,0 +1,9 @@
+_test
+*.test
+_testmain.go
+_obj
+*~
+*.6
+6.out
+gorptest.bin
+tmp
diff --git a/vendor/github.com/go-gorp/gorp/.travis.yml b/vendor/github.com/go-gorp/gorp/.travis.yml
new file mode 100644
index 000000000..a8023b1f4
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/.travis.yml
@@ -0,0 +1,29 @@
+language: go
+go:
+- 1.3
+- 1.4
+- tip
+
+services:
+- mysql
+- postgres
+- sqlite3
+
+env:
+ global:
+ - secure: RriLxF6+2yMl67hdVv8ImXlu0h62mhcpqjaOgYNU+IEbUQ7hx96CKY6gkpYubW3BgApvF5RH6j3+HKvh2kGp0XhDOYOQCODfBSaSipZ5Aa5RKjsEYLtuVIobvJ80awR9hUeql69+WXs0/s72WThG0qTbOUY4pqHWfteeY235hWM=
+
+before_script:
+- mysql -e "CREATE DATABASE gorptest;"
+- mysql -u root -e "GRANT ALL ON gorptest.* TO gorptest@localhost IDENTIFIED BY 'gorptest'"
+- psql -c "CREATE DATABASE gorptest;" -U postgres
+- psql -c "CREATE USER "gorptest" WITH SUPERUSER PASSWORD 'gorptest';" -U postgres
+- go get github.com/lib/pq
+- go get github.com/mattn/go-sqlite3
+- go get github.com/ziutek/mymysql/godrv
+- go get github.com/go-sql-driver/mysql
+- go get golang.org/x/tools/cmd/cover
+- go get github.com/mattn/goveralls
+- go get github.com/onsi/ginkgo/ginkgo
+
+script: ./test_all.sh
diff --git a/vendor/github.com/go-gorp/gorp/LICENSE b/vendor/github.com/go-gorp/gorp/LICENSE
new file mode 100644
index 000000000..b661111d0
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2012 James Cooper <james@bitmechanic.com>
+
+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/github.com/go-gorp/gorp/README.md b/vendor/github.com/go-gorp/gorp/README.md
new file mode 100644
index 000000000..8b9277805
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/README.md
@@ -0,0 +1,745 @@
+# Go Relational Persistence
+
+[![build status](https://img.shields.io/travis/go-gorp/gorp.svg)](http://travis-ci.org/go-gorp/gorp)
+[![code coverage](https://img.shields.io/coveralls/go-gorp/gorp.svg)](https://coveralls.io/r/go-gorp/gorp)
+[![issues](https://img.shields.io/github/issues/go-gorp/gorp.svg)](https://github.com/go-gorp/gorp/issues)
+[![godoc v1](https://img.shields.io/badge/godoc-v1-375EAB.svg)](https://godoc.org/gopkg.in/gorp.v1)
+[![godoc bleeding edge](https://img.shields.io/badge/godoc-bleeding--edge-375EAB.svg)](https://godoc.org/github.com/go-gorp/gorp)
+
+### Update 2015-07-01 Cleanup & feature freeze ([#270](https://github.com/go-gorp/gorp/issues/270))
+
+We are currently cleaning up the backlog of issues and PR's. When this is done the codebase will be split into separate files and there will be breaking changes to the API's. We're also adding better tests and documentation. As a result of these changes the `master` branch will be unstable. Please use `gopkg.in/gorp.v1`. When the cleanup and changes are done, we will release `v2.0`.
+
+At this time we won't accept new feature-related pull-requests because of changes to the codebase. Please create an issue for your feature and wait until `v2.0` has been released.
+
+For more information, please read [#270](https://github.com/go-gorp/gorp/issues/270).
+
+## Introduction
+
+I hesitate to call gorp an ORM. Go doesn't really have objects, at least not in the classic Smalltalk/Java sense. There goes the "O". gorp doesn't know anything about the relationships between your structs (at least not yet). So the "R" is questionable too (but I use it in the name because, well, it seemed more clever).
+
+The "M" is alive and well. Given some Go structs and a database, gorp should remove a fair amount of boilerplate busy-work from your code.
+
+I hope that gorp saves you time, minimizes the drudgery of getting data in and out of your database, and helps your code focus on algorithms, not infrastructure.
+
+* Bind struct fields to table columns via API or tag
+* Support for embedded structs
+* Support for transactions
+* Forward engineer db schema from structs (great for unit tests)
+* Pre/post insert/update/delete hooks
+* Automatically generate insert/update/delete statements for a struct
+* Automatic binding of auto increment PKs back to struct after insert
+* Delete by primary key(s)
+* Select by primary key(s)
+* Optional trace sql logging
+* Bind arbitrary SQL queries to a struct
+* Bind slice to SELECT query results without type assertions
+* Use positional or named bind parameters in custom SELECT queries
+* Optional optimistic locking using a version column (for update/deletes)
+
+## Installation
+
+ # install the library:
+ go get gopkg.in/gorp.v1
+
+ // use in your .go code:
+ import (
+ "gopkg.in/gorp.v1"
+ )
+
+## Versioning
+
+This project provides a stable release (v1.x tags) and a bleeding edge codebase (master).
+
+`gopkg.in/gorp.v1` points to the latest v1.x tag. The API's for v1 are stable and shouldn't change. Development takes place at the master branch. Althought the code in master should always compile and test successfully, it might break API's. We aim to maintain backwards compatibility, but API's and behaviour might be changed to fix a bug. Also note that API's that are new in the master branch can change until released as v2.
+
+If you want to use bleeding edge, use `github.com/go-gorp/gorp` as import path.
+
+## API Documentation
+
+Full godoc output from the latest v1 release is available here:
+
+https://godoc.org/gopkg.in/gorp.v1
+
+For the latest code in master:
+
+https://godoc.org/github.com/go-gorp/gorp
+
+## Supported Go versions
+
+This package is compatible with the last 2 major versions of Go, at this time `1.3` and `1.4`.
+
+Any earlier versions are only supported on a best effort basis and can be dropped any time.
+Go has a great compatibility promise. Upgrading your program to a newer version of Go should never really be a problem.
+
+## Quickstart
+
+```go
+package main
+
+import (
+ "database/sql"
+ "gopkg.in/gorp.v1"
+ _ "github.com/mattn/go-sqlite3"
+ "log"
+ "time"
+)
+
+func main() {
+ // initialize the DbMap
+ dbmap := initDb()
+ defer dbmap.Db.Close()
+
+ // delete any existing rows
+ err := dbmap.TruncateTables()
+ checkErr(err, "TruncateTables failed")
+
+ // create two posts
+ p1 := newPost("Go 1.1 released!", "Lorem ipsum lorem ipsum")
+ p2 := newPost("Go 1.2 released!", "Lorem ipsum lorem ipsum")
+
+ // insert rows - auto increment PKs will be set properly after the insert
+ err = dbmap.Insert(&p1, &p2)
+ checkErr(err, "Insert failed")
+
+ // use convenience SelectInt
+ count, err := dbmap.SelectInt("select count(*) from posts")
+ checkErr(err, "select count(*) failed")
+ log.Println("Rows after inserting:", count)
+
+ // update a row
+ p2.Title = "Go 1.2 is better than ever"
+ count, err = dbmap.Update(&p2)
+ checkErr(err, "Update failed")
+ log.Println("Rows updated:", count)
+
+ // fetch one row - note use of "post_id" instead of "Id" since column is aliased
+ //
+ // Postgres users should use $1 instead of ? placeholders
+ // See 'Known Issues' below
+ //
+ err = dbmap.SelectOne(&p2, "select * from posts where post_id=?", p2.Id)
+ checkErr(err, "SelectOne failed")
+ log.Println("p2 row:", p2)
+
+ // fetch all rows
+ var posts []Post
+ _, err = dbmap.Select(&posts, "select * from posts order by post_id")
+ checkErr(err, "Select failed")
+ log.Println("All rows:")
+ for x, p := range posts {
+ log.Printf(" %d: %v\n", x, p)
+ }
+
+ // delete row by PK
+ count, err = dbmap.Delete(&p1)
+ checkErr(err, "Delete failed")
+ log.Println("Rows deleted:", count)
+
+ // delete row manually via Exec
+ _, err = dbmap.Exec("delete from posts where post_id=?", p2.Id)
+ checkErr(err, "Exec failed")
+
+ // confirm count is zero
+ count, err = dbmap.SelectInt("select count(*) from posts")
+ checkErr(err, "select count(*) failed")
+ log.Println("Row count - should be zero:", count)
+
+ log.Println("Done!")
+}
+
+type Post struct {
+ // db tag lets you specify the column name if it differs from the struct field
+ Id int64 `db:"post_id"`
+ Created int64
+ Title string `db:",size:50"` // Column size set to 50
+ Body string `db:"article_body,size:1024"` // Set both column name and size
+}
+
+func newPost(title, body string) Post {
+ return Post{
+ Created: time.Now().UnixNano(),
+ Title: title,
+ Body: body,
+ }
+}
+
+func initDb() *gorp.DbMap {
+ // connect to db using standard Go database/sql API
+ // use whatever database/sql driver you wish
+ db, err := sql.Open("sqlite3", "/tmp/post_db.bin")
+ checkErr(err, "sql.Open failed")
+
+ // construct a gorp DbMap
+ dbmap := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}
+
+ // add a table, setting the table name to 'posts' and
+ // specifying that the Id property is an auto incrementing PK
+ dbmap.AddTableWithName(Post{}, "posts").SetKeys(true, "Id")
+
+ // create the table. in a production system you'd generally
+ // use a migration tool, or create the tables via scripts
+ err = dbmap.CreateTablesIfNotExists()
+ checkErr(err, "Create tables failed")
+
+ return dbmap
+}
+
+func checkErr(err error, msg string) {
+ if err != nil {
+ log.Fatalln(msg, err)
+ }
+}
+```
+
+## Examples
+
+### Mapping structs to tables
+
+First define some types:
+
+```go
+type Invoice struct {
+ Id int64
+ Created int64
+ Updated int64
+ Memo string
+ PersonId int64
+}
+
+type Person struct {
+ Id int64
+ Created int64
+ Updated int64
+ FName string
+ LName string
+}
+
+// Example of using tags to alias fields to column names
+// The 'db' value is the column name
+//
+// A hyphen will cause gorp to skip this field, similar to the
+// Go json package.
+//
+// This is equivalent to using the ColMap methods:
+//
+// table := dbmap.AddTableWithName(Product{}, "product")
+// table.ColMap("Id").Rename("product_id")
+// table.ColMap("Price").Rename("unit_price")
+// table.ColMap("IgnoreMe").SetTransient(true)
+//
+// You can optionally declare the field to be a primary key and/or autoincrement
+//
+type Product struct {
+ Id int64 `db:"product_id, primarykey, autoincrement"`
+ Price int64 `db:"unit_price"`
+ IgnoreMe string `db:"-"`
+}
+```
+
+Then create a mapper, typically you'd do this one time at app startup:
+
+```go
+// connect to db using standard Go database/sql API
+// use whatever database/sql driver you wish
+db, err := sql.Open("mymysql", "tcp:localhost:3306*mydb/myuser/mypassword")
+
+// construct a gorp DbMap
+dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}}
+
+// register the structs you wish to use with gorp
+// you can also use the shorter dbmap.AddTable() if you
+// don't want to override the table name
+//
+// SetKeys(true) means we have a auto increment primary key, which
+// will get automatically bound to your struct post-insert
+//
+t1 := dbmap.AddTableWithName(Invoice{}, "invoice_test").SetKeys(true, "Id")
+t2 := dbmap.AddTableWithName(Person{}, "person_test").SetKeys(true, "Id")
+t3 := dbmap.AddTableWithName(Product{}, "product_test").SetKeys(true, "Id")
+```
+
+### Struct Embedding
+
+gorp supports embedding structs. For example:
+
+```go
+type Names struct {
+ FirstName string
+ LastName string
+}
+
+type WithEmbeddedStruct struct {
+ Id int64
+ Names
+}
+
+es := &WithEmbeddedStruct{-1, Names{FirstName: "Alice", LastName: "Smith"}}
+err := dbmap.Insert(es)
+```
+
+See the `TestWithEmbeddedStruct` function in `gorp_test.go` for a full example.
+
+### Create/Drop Tables ###
+
+Automatically create / drop registered tables. This is useful for unit tests
+but is entirely optional. You can of course use gorp with tables created manually,
+or with a separate migration tool (like [goose](https://bitbucket.org/liamstask/goose) or [migrate](https://github.com/mattes/migrate)).
+
+```go
+// create all registered tables
+dbmap.CreateTables()
+
+// same as above, but uses "if not exists" clause to skip tables that are
+// already defined
+dbmap.CreateTablesIfNotExists()
+
+// drop
+dbmap.DropTables()
+```
+
+### SQL Logging
+
+Optionally you can pass in a logger to trace all SQL statements.
+I recommend enabling this initially while you're getting the feel for what
+gorp is doing on your behalf.
+
+Gorp defines a `GorpLogger` interface that Go's built in `log.Logger` satisfies.
+However, you can write your own `GorpLogger` implementation, or use a package such
+as `glog` if you want more control over how statements are logged.
+
+```go
+// Will log all SQL statements + args as they are run
+// The first arg is a string prefix to prepend to all log messages
+dbmap.TraceOn("[gorp]", log.New(os.Stdout, "myapp:", log.Lmicroseconds))
+
+// Turn off tracing
+dbmap.TraceOff()
+```
+
+### Insert
+
+```go
+// Must declare as pointers so optional callback hooks
+// can operate on your data, not copies
+inv1 := &Invoice{0, 100, 200, "first order", 0}
+inv2 := &Invoice{0, 100, 200, "second order", 0}
+
+// Insert your rows
+err := dbmap.Insert(inv1, inv2)
+
+// Because we called SetKeys(true) on Invoice, the Id field
+// will be populated after the Insert() automatically
+fmt.Printf("inv1.Id=%d inv2.Id=%d\n", inv1.Id, inv2.Id)
+```
+
+### Update
+
+Continuing the above example, use the `Update` method to modify an Invoice:
+
+```go
+// count is the # of rows updated, which should be 1 in this example
+count, err := dbmap.Update(inv1)
+```
+
+### Delete
+
+If you have primary key(s) defined for a struct, you can use the `Delete`
+method to remove rows:
+
+```go
+count, err := dbmap.Delete(inv1)
+```
+
+### Select by Key
+
+Use the `Get` method to fetch a single row by primary key. It returns
+nil if no row is found.
+
+```go
+// fetch Invoice with Id=99
+obj, err := dbmap.Get(Invoice{}, 99)
+inv := obj.(*Invoice)
+```
+
+### Ad Hoc SQL
+
+#### SELECT
+
+`Select()` and `SelectOne()` provide a simple way to bind arbitrary queries to a slice
+or a single struct.
+
+```go
+// Select a slice - first return value is not needed when a slice pointer is passed to Select()
+var posts []Post
+_, err := dbmap.Select(&posts, "select * from post order by id")
+
+// You can also use primitive types
+var ids []string
+_, err := dbmap.Select(&ids, "select id from post")
+
+// Select a single row.
+// Returns an error if no row found, or if more than one row is found
+var post Post
+err := dbmap.SelectOne(&post, "select * from post where id=?", id)
+```
+
+Want to do joins? Just write the SQL and the struct. gorp will bind them:
+
+```go
+// Define a type for your join
+// It *must* contain all the columns in your SELECT statement
+//
+// The names here should match the aliased column names you specify
+// in your SQL - no additional binding work required. simple.
+//
+type InvoicePersonView struct {
+ InvoiceId int64
+ PersonId int64
+ Memo string
+ FName string
+}
+
+// Create some rows
+p1 := &Person{0, 0, 0, "bob", "smith"}
+dbmap.Insert(p1)
+
+// notice how we can wire up p1.Id to the invoice easily
+inv1 := &Invoice{0, 0, 0, "xmas order", p1.Id}
+dbmap.Insert(inv1)
+
+// Run your query
+query := "select i.Id InvoiceId, p.Id PersonId, i.Memo, p.FName " +
+ "from invoice_test i, person_test p " +
+ "where i.PersonId = p.Id"
+
+// pass a slice to Select()
+var list []InvoicePersonView
+_, err := dbmap.Select(&list, query)
+
+// this should test true
+expected := InvoicePersonView{inv1.Id, p1.Id, inv1.Memo, p1.FName}
+if reflect.DeepEqual(list[0], expected) {
+ fmt.Println("Woot! My join worked!")
+}
+```
+
+#### SELECT string or int64
+
+gorp provides a few convenience methods for selecting a single string or int64.
+
+```go
+// select single int64 from db (use $1 instead of ? for postgresql)
+i64, err := dbmap.SelectInt("select count(*) from foo where blah=?", blahVal)
+
+// select single string from db:
+s, err := dbmap.SelectStr("select name from foo where blah=?", blahVal)
+
+```
+
+#### Named bind parameters
+
+You may use a map or struct to bind parameters by name. This is currently
+only supported in SELECT queries.
+
+```go
+_, err := dbm.Select(&dest, "select * from Foo where name = :name and age = :age", map[string]interface{}{
+ "name": "Rob",
+ "age": 31,
+})
+```
+
+#### UPDATE / DELETE
+
+You can execute raw SQL if you wish. Particularly good for batch operations.
+
+```go
+res, err := dbmap.Exec("delete from invoice_test where PersonId=?", 10)
+```
+
+### Transactions
+
+You can batch operations into a transaction:
+
+```go
+func InsertInv(dbmap *DbMap, inv *Invoice, per *Person) error {
+ // Start a new transaction
+ trans, err := dbmap.Begin()
+ if err != nil {
+ return err
+ }
+
+ trans.Insert(per)
+ inv.PersonId = per.Id
+ trans.Insert(inv)
+
+ // if the commit is successful, a nil error is returned
+ return trans.Commit()
+}
+```
+
+### Hooks
+
+Use hooks to update data before/after saving to the db. Good for timestamps:
+
+```go
+// implement the PreInsert and PreUpdate hooks
+func (i *Invoice) PreInsert(s gorp.SqlExecutor) error {
+ i.Created = time.Now().UnixNano()
+ i.Updated = i.Created
+ return nil
+}
+
+func (i *Invoice) PreUpdate(s gorp.SqlExecutor) error {
+ i.Updated = time.Now().UnixNano()
+ return nil
+}
+
+// You can use the SqlExecutor to cascade additional SQL
+// Take care to avoid cycles. gorp won't prevent them.
+//
+// Here's an example of a cascading delete
+//
+func (p *Person) PreDelete(s gorp.SqlExecutor) error {
+ query := "delete from invoice_test where PersonId=?"
+
+ _, err := s.Exec(query, p.Id)
+
+ if err != nil {
+ return err
+ }
+ return nil
+}
+```
+
+Full list of hooks that you can implement:
+
+ PostGet
+ PreInsert
+ PostInsert
+ PreUpdate
+ PostUpdate
+ PreDelete
+ PostDelete
+
+ All have the same signature. for example:
+
+ func (p *MyStruct) PostUpdate(s gorp.SqlExecutor) error
+
+### Optimistic Locking
+
+#### Note that this behaviour has changed in v2. See [Migration Guide](#migration-guide).
+
+gorp provides a simple optimistic locking feature, similar to Java's JPA, that
+will raise an error if you try to update/delete a row whose `version` column
+has a value different than the one in memory. This provides a safe way to do
+"select then update" style operations without explicit read and write locks.
+
+```go
+// Version is an auto-incremented number, managed by gorp
+// If this property is present on your struct, update
+// operations will be constrained
+//
+// For example, say we defined Person as:
+
+type Person struct {
+ Id int64
+ Created int64
+ Updated int64
+ FName string
+ LName string
+
+ // automatically used as the Version col
+ // use table.SetVersionCol("columnName") to map a different
+ // struct field as the version field
+ Version int64
+}
+
+p1 := &Person{0, 0, 0, "Bob", "Smith", 0}
+dbmap.Insert(p1) // Version is now 1
+
+obj, err := dbmap.Get(Person{}, p1.Id)
+p2 := obj.(*Person)
+p2.LName = "Edwards"
+dbmap.Update(p2) // Version is now 2
+
+p1.LName = "Howard"
+
+// Raises error because p1.Version == 1, which is out of date
+count, err := dbmap.Update(p1)
+_, ok := err.(gorp.OptimisticLockError)
+if ok {
+ // should reach this statement
+
+ // in a real app you might reload the row and retry, or
+ // you might propegate this to the user, depending on the desired
+ // semantics
+ fmt.Printf("Tried to update row with stale data: %v\n", err)
+} else {
+ // some other db error occurred - log or return up the stack
+ fmt.Printf("Unknown db err: %v\n", err)
+}
+```
+### Adding INDEX(es) on column(s) beyond the primary key ###
+
+Indexes are frequently critical for performance. Here is how to add them to your tables.
+
+NB: SqlServer and Oracle need testing and possible adjustment to the
+CreateIndexSuffix() and DropIndexSuffix() methods to make AddIndex()
+work for them.
+
+In the example below we put an index both on the Id field, and on the AcctId field.
+
+```
+type Account struct {
+ Id int64
+ AcctId string // e.g. this might be a long uuid for portability
+}
+
+// indexType (the 2nd param to AddIndex call) is "Btree" or "Hash" for MySQL.
+// demonstrate adding a second index on AcctId, and constrain that field to have unique values.
+dbm.AddTable(iptab.Account{}).SetKeys(true, "Id").AddIndex("AcctIdIndex", "Btree", []string{"AcctId"}).SetUnique(true)
+
+err = dbm.CreateTablesIfNotExists()
+checkErr(err, "CreateTablesIfNotExists failed")
+
+err = dbm.CreateIndex()
+checkErr(err, "CreateIndex failed")
+
+```
+Check the effect of the CreateIndex() call in mysql:
+```
+$ mysql
+
+MariaDB [test]> show create table Account;
++---------+--------------------------+
+| Account | CREATE TABLE `Account` (
+ `Id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `AcctId` varchar(255) DEFAULT NULL,
+ PRIMARY KEY (`Id`),
+ UNIQUE KEY `AcctIdIndex` (`AcctId`) USING BTREE <<<--- yes! index added.
+) ENGINE=InnoDB DEFAULT CHARSET=utf8
++---------+--------------------------+
+
+```
+
+
+## Database Drivers
+
+gorp uses the Go 1 `database/sql` package. A full list of compliant drivers is available here:
+
+http://code.google.com/p/go-wiki/wiki/SQLDrivers
+
+Sadly, SQL databases differ on various issues. gorp provides a Dialect interface that should be
+implemented per database vendor. Dialects are provided for:
+
+* MySQL
+* PostgreSQL
+* sqlite3
+
+Each of these three databases pass the test suite. See `gorp_test.go` for example
+DSNs for these three databases.
+
+Support is also provided for:
+
+* Oracle (contributed by @klaidliadon)
+* SQL Server (contributed by @qrawl) - use driver: github.com/denisenkom/go-mssqldb
+
+Note that these databases are not covered by CI and I (@coopernurse) have no good way to
+test them locally. So please try them and send patches as needed, but expect a bit more
+unpredicability.
+
+## Known Issues
+
+### SQL placeholder portability
+
+Different databases use different strings to indicate variable placeholders in
+prepared SQL statements. Unlike some database abstraction layers (such as JDBC),
+Go's `database/sql` does not standardize this.
+
+SQL generated by gorp in the `Insert`, `Update`, `Delete`, and `Get` methods delegates
+to a Dialect implementation for each database, and will generate portable SQL.
+
+Raw SQL strings passed to `Exec`, `Select`, `SelectOne`, `SelectInt`, etc will not be
+parsed. Consequently you may have portability issues if you write a query like this:
+
+```go
+// works on MySQL and Sqlite3, but not with Postgresql
+err := dbmap.SelectOne(&val, "select * from foo where id = ?", 30)
+```
+
+In `Select` and `SelectOne` you can use named parameters to work around this.
+The following is portable:
+
+```go
+err := dbmap.SelectOne(&val, "select * from foo where id = :id",
+ map[string]interface{} { "id": 30})
+```
+
+Additionally, when using Postgres as your database, you should utilize `$1` instead
+of `?` placeholders as utilizing `?` placeholders when querying Postgres will result
+in `pq: operator does not exist` errors. Alternatively, use
+`dbMap.Dialect.BindVar(varIdx)` to get the proper variable binding for your dialect.
+
+### time.Time and time zones
+
+gorp will pass `time.Time` fields through to the `database/sql` driver, but note that
+the behavior of this type varies across database drivers.
+
+MySQL users should be especially cautious. See: https://github.com/ziutek/mymysql/pull/77
+
+To avoid any potential issues with timezone/DST, consider using an integer field for time
+data and storing UNIX time.
+
+## Running the tests
+
+The included tests may be run against MySQL, Postgresql, or sqlite3.
+You must set two environment variables so the test code knows which driver to
+use, and how to connect to your database.
+
+```sh
+# MySQL example:
+export GORP_TEST_DSN=gomysql_test/gomysql_test/abc123
+export GORP_TEST_DIALECT=mysql
+
+# run the tests
+go test
+
+# run the tests and benchmarks
+go test -bench="Bench" -benchtime 10
+```
+
+Valid `GORP_TEST_DIALECT` values are: "mysql"(for mymysql), "gomysql"(for go-sql-driver), "postgres", "sqlite"
+See the `test_all.sh` script for examples of all 3 databases. This is the script I run
+locally to test the library.
+
+## Performance
+
+gorp uses reflection to construct SQL queries and bind parameters. See the BenchmarkNativeCrud vs BenchmarkGorpCrud in gorp_test.go for a simple perf test. On my MacBook Pro gorp is about 2-3% slower than hand written SQL.
+
+## Migration guide
+#### Pre-v2 to v2
+Automatic mapping of the version column used in optimistic locking has been removed as it could cause problems if the type was not int. The version column must now explicitly be set with tablemap.SetVersionCol().
+
+## Help/Support
+
+IRC: #gorp
+Mailing list: gorp-dev@googlegroups.com
+Bugs/Enhancements: Create a github issue
+
+## Pull requests / Contributions
+
+Contributions are very welcome. Please follow these guidelines:
+
+* Fork the `master` branch and issue pull requests targeting the `master` branch
+* If you are adding an enhancement, please open an issue first with your proposed change.
+* Changes that break backwards compatibility in the public API are only accepted after we
+ discuss on a GitHub issue for a while.
+
+Thanks!
+
+## Contributors
+
+* matthias-margush - column aliasing via tags
+* Rob Figueiredo - @robfig
+* Quinn Slack - @sqs
diff --git a/vendor/github.com/go-gorp/gorp/column.go b/vendor/github.com/go-gorp/gorp/column.go
new file mode 100644
index 000000000..99d4fd555
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/column.go
@@ -0,0 +1,83 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import "reflect"
+
+// ColumnMap represents a mapping between a Go struct field and a single
+// column in a table.
+// Unique and MaxSize only inform the
+// CreateTables() function and are not used by Insert/Update/Delete/Get.
+type ColumnMap struct {
+ // Column name in db table
+ ColumnName string
+
+ // If true, this column is skipped in generated SQL statements
+ Transient bool
+
+ // If true, " unique" is added to create table statements.
+ // Not used elsewhere
+ Unique bool
+
+ // Query used for getting generated id after insert
+ GeneratedIdQuery string
+
+ // Passed to Dialect.ToSqlType() to assist in informing the
+ // correct column type to map to in CreateTables()
+ MaxSize int
+
+ DefaultValue string
+
+ fieldName string
+ gotype reflect.Type
+ isPK bool
+ isAutoIncr bool
+ isNotNull bool
+}
+
+// Rename allows you to specify the column name in the table
+//
+// Example: table.ColMap("Updated").Rename("date_updated")
+//
+func (c *ColumnMap) Rename(colname string) *ColumnMap {
+ c.ColumnName = colname
+ return c
+}
+
+// SetTransient allows you to mark the column as transient. If true
+// this column will be skipped when SQL statements are generated
+func (c *ColumnMap) SetTransient(b bool) *ColumnMap {
+ c.Transient = b
+ return c
+}
+
+// SetUnique adds "unique" to the create table statements for this
+// column, if b is true.
+func (c *ColumnMap) SetUnique(b bool) *ColumnMap {
+ c.Unique = b
+ return c
+}
+
+// SetNotNull adds "not null" to the create table statements for this
+// column, if nn is true.
+func (c *ColumnMap) SetNotNull(nn bool) *ColumnMap {
+ c.isNotNull = nn
+ return c
+}
+
+// SetMaxSize specifies the max length of values of this column. This is
+// passed to the dialect.ToSqlType() function, which can use the value
+// to alter the generated type for "create table" statements
+func (c *ColumnMap) SetMaxSize(size int) *ColumnMap {
+ c.MaxSize = size
+ return c
+}
diff --git a/vendor/github.com/go-gorp/gorp/db.go b/vendor/github.com/go-gorp/gorp/db.go
new file mode 100644
index 000000000..5ce68fdfd
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/db.go
@@ -0,0 +1,623 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "bytes"
+ "database/sql"
+ "database/sql/driver"
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// DbMap is the root gorp mapping object. Create one of these for each
+// database schema you wish to map. Each DbMap contains a list of
+// mapped tables.
+//
+// Example:
+//
+// dialect := gorp.MySQLDialect{"InnoDB", "UTF8"}
+// dbmap := &gorp.DbMap{Db: db, Dialect: dialect}
+//
+type DbMap struct {
+ // Db handle to use with this map
+ Db *sql.DB
+
+ // Dialect implementation to use with this map
+ Dialect Dialect
+
+ TypeConverter TypeConverter
+
+ tables []*TableMap
+ logger GorpLogger
+ logPrefix string
+}
+
+func (m *DbMap) CreateIndex() error {
+
+ var err error
+ dialect := reflect.TypeOf(m.Dialect)
+ for _, table := range m.tables {
+ for _, index := range table.indexes {
+
+ s := bytes.Buffer{}
+ s.WriteString("create")
+ if index.Unique {
+ s.WriteString(" unique")
+ }
+ s.WriteString(" index")
+ s.WriteString(fmt.Sprintf(" %s on %s", index.IndexName, table.TableName))
+ if dname := dialect.Name(); dname == "PostgresDialect" && index.IndexType != "" {
+ s.WriteString(fmt.Sprintf(" %s %s", m.Dialect.CreateIndexSuffix(), index.IndexType))
+ }
+ s.WriteString(" (")
+ for x, col := range index.columns {
+ if x > 0 {
+ s.WriteString(", ")
+ }
+ s.WriteString(m.Dialect.QuoteField(col))
+ }
+ s.WriteString(")")
+
+ if dname := dialect.Name(); dname == "MySQLDialect" && index.IndexType != "" {
+ s.WriteString(fmt.Sprintf(" %s %s", m.Dialect.CreateIndexSuffix(), index.IndexType))
+ }
+ s.WriteString(";")
+ _, err = m.Exec(s.String())
+ if err != nil {
+ break
+ }
+ }
+ }
+ return err
+}
+
+func (t *TableMap) DropIndex(name string) error {
+
+ var err error
+ dialect := reflect.TypeOf(t.dbmap.Dialect)
+ for _, idx := range t.indexes {
+ if idx.IndexName == name {
+ s := bytes.Buffer{}
+ s.WriteString(fmt.Sprintf("DROP INDEX %s", idx.IndexName))
+
+ if dname := dialect.Name(); dname == "MySQLDialect" {
+ s.WriteString(fmt.Sprintf(" %s %s", t.dbmap.Dialect.DropIndexSuffix(), t.TableName))
+ }
+ s.WriteString(";")
+ _, e := t.dbmap.Exec(s.String())
+ if e != nil {
+ err = e
+ }
+ break
+ }
+ }
+ t.ResetSql()
+ return err
+}
+
+// AddTable registers the given interface type with gorp. The table name
+// will be given the name of the TypeOf(i). You must call this function,
+// or AddTableWithName, for any struct type you wish to persist with
+// the given DbMap.
+//
+// This operation is idempotent. If i's type is already mapped, the
+// existing *TableMap is returned
+func (m *DbMap) AddTable(i interface{}) *TableMap {
+ return m.AddTableWithName(i, "")
+}
+
+// AddTableWithName has the same behavior as AddTable, but sets
+// table.TableName to name.
+func (m *DbMap) AddTableWithName(i interface{}, name string) *TableMap {
+ return m.AddTableWithNameAndSchema(i, "", name)
+}
+
+// AddTableWithNameAndSchema has the same behavior as AddTable, but sets
+// table.TableName to name.
+func (m *DbMap) AddTableWithNameAndSchema(i interface{}, schema string, name string) *TableMap {
+ t := reflect.TypeOf(i)
+ if name == "" {
+ name = t.Name()
+ }
+
+ // check if we have a table for this type already
+ // if so, update the name and return the existing pointer
+ for i := range m.tables {
+ table := m.tables[i]
+ if table.gotype == t {
+ table.TableName = name
+ return table
+ }
+ }
+
+ tmap := &TableMap{gotype: t, TableName: name, SchemaName: schema, dbmap: m}
+ var primaryKey []*ColumnMap
+ tmap.Columns, primaryKey = m.readStructColumns(t)
+ m.tables = append(m.tables, tmap)
+ if len(primaryKey) > 0 {
+ tmap.keys = append(tmap.keys, primaryKey...)
+ }
+
+ return tmap
+}
+
+func (m *DbMap) readStructColumns(t reflect.Type) (cols []*ColumnMap, primaryKey []*ColumnMap) {
+ primaryKey = make([]*ColumnMap, 0)
+ n := t.NumField()
+ for i := 0; i < n; i++ {
+ f := t.Field(i)
+ if f.Anonymous && f.Type.Kind() == reflect.Struct {
+ // Recursively add nested fields in embedded structs.
+ subcols, subpk := m.readStructColumns(f.Type)
+ // Don't append nested fields that have the same field
+ // name as an already-mapped field.
+ for _, subcol := range subcols {
+ shouldAppend := true
+ for _, col := range cols {
+ if !subcol.Transient && subcol.fieldName == col.fieldName {
+ shouldAppend = false
+ break
+ }
+ }
+ if shouldAppend {
+ cols = append(cols, subcol)
+ }
+ }
+ if subpk != nil {
+ primaryKey = append(primaryKey, subpk...)
+ }
+ } else {
+ // Tag = Name { ',' Option }
+ // Option = OptionKey [ ':' OptionValue ]
+ cArguments := strings.Split(f.Tag.Get("db"), ",")
+ columnName := cArguments[0]
+ var maxSize int
+ var defaultValue string
+ var isAuto bool
+ var isPK bool
+ for _, argString := range cArguments[1:] {
+ argString = strings.TrimSpace(argString)
+ arg := strings.SplitN(argString, ":", 2)
+
+ // check mandatory/unexpected option values
+ switch arg[0] {
+ case "size", "default":
+ // options requiring value
+ if len(arg) == 1 {
+ panic(fmt.Sprintf("missing option value for option %v on field %v", arg[0], f.Name))
+ }
+ default:
+ // options where value is invalid (currently all other options)
+ if len(arg) == 2 {
+ panic(fmt.Sprintf("unexpected option value for option %v on field %v", arg[0], f.Name))
+ }
+ }
+
+ switch arg[0] {
+ case "size":
+ maxSize, _ = strconv.Atoi(arg[1])
+ case "default":
+ defaultValue = arg[1]
+ case "primarykey":
+ isPK = true
+ case "autoincrement":
+ isAuto = true
+ default:
+ panic(fmt.Sprintf("Unrecognized tag option for field %v: %v", f.Name, arg))
+ }
+ }
+ if columnName == "" {
+ columnName = f.Name
+ }
+
+ gotype := f.Type
+ valueType := gotype
+ if valueType.Kind() == reflect.Ptr {
+ valueType = valueType.Elem()
+ }
+ value := reflect.New(valueType).Interface()
+ if m.TypeConverter != nil {
+ // Make a new pointer to a value of type gotype and
+ // pass it to the TypeConverter's FromDb method to see
+ // if a different type should be used for the column
+ // type during table creation.
+ scanner, useHolder := m.TypeConverter.FromDb(value)
+ if useHolder {
+ value = scanner.Holder
+ gotype = reflect.TypeOf(value)
+ }
+ }
+ if typer, ok := value.(SqlTyper); ok {
+ gotype = reflect.TypeOf(typer.SqlType())
+ } else if valuer, ok := value.(driver.Valuer); ok {
+ // Only check for driver.Valuer if SqlTyper wasn't
+ // found.
+ v, err := valuer.Value()
+ if err == nil && v != nil {
+ gotype = reflect.TypeOf(v)
+ }
+ }
+ cm := &ColumnMap{
+ ColumnName: columnName,
+ DefaultValue: defaultValue,
+ Transient: columnName == "-",
+ fieldName: f.Name,
+ gotype: gotype,
+ isPK: isPK,
+ isAutoIncr: isAuto,
+ MaxSize: maxSize,
+ }
+ if isPK {
+ primaryKey = append(primaryKey, cm)
+ }
+ // Check for nested fields of the same field name and
+ // override them.
+ shouldAppend := true
+ for index, col := range cols {
+ if !col.Transient && col.fieldName == cm.fieldName {
+ cols[index] = cm
+ shouldAppend = false
+ break
+ }
+ }
+ if shouldAppend {
+ cols = append(cols, cm)
+ }
+ }
+
+ }
+ return
+}
+
+// CreateTables iterates through TableMaps registered to this DbMap and
+// executes "create table" statements against the database for each.
+//
+// This is particularly useful in unit tests where you want to create
+// and destroy the schema automatically.
+func (m *DbMap) CreateTables() error {
+ return m.createTables(false)
+}
+
+// CreateTablesIfNotExists is similar to CreateTables, but starts
+// each statement with "create table if not exists" so that existing
+// tables do not raise errors
+func (m *DbMap) CreateTablesIfNotExists() error {
+ return m.createTables(true)
+}
+
+func (m *DbMap) createTables(ifNotExists bool) error {
+ var err error
+ for i := range m.tables {
+ table := m.tables[i]
+ sql := table.SqlForCreate(ifNotExists)
+ _, err = m.Exec(sql)
+ if err != nil {
+ break
+ }
+ }
+ return err
+}
+
+// DropTable drops an individual table.
+// Returns an error when the table does not exist.
+func (m *DbMap) DropTable(table interface{}) error {
+ t := reflect.TypeOf(table)
+ return m.dropTable(t, false)
+}
+
+// DropTableIfExists drops an individual table when the table exists.
+func (m *DbMap) DropTableIfExists(table interface{}) error {
+ t := reflect.TypeOf(table)
+ return m.dropTable(t, true)
+}
+
+// DropTables iterates through TableMaps registered to this DbMap and
+// executes "drop table" statements against the database for each.
+func (m *DbMap) DropTables() error {
+ return m.dropTables(false)
+}
+
+// DropTablesIfExists is the same as DropTables, but uses the "if exists" clause to
+// avoid errors for tables that do not exist.
+func (m *DbMap) DropTablesIfExists() error {
+ return m.dropTables(true)
+}
+
+// Goes through all the registered tables, dropping them one by one.
+// If an error is encountered, then it is returned and the rest of
+// the tables are not dropped.
+func (m *DbMap) dropTables(addIfExists bool) (err error) {
+ for _, table := range m.tables {
+ err = m.dropTableImpl(table, addIfExists)
+ if err != nil {
+ return err
+ }
+ }
+ return err
+}
+
+// Implementation of dropping a single table.
+func (m *DbMap) dropTable(t reflect.Type, addIfExists bool) error {
+ table := tableOrNil(m, t)
+ if table == nil {
+ return fmt.Errorf("table %s was not registered", table.TableName)
+ }
+
+ return m.dropTableImpl(table, addIfExists)
+}
+
+func (m *DbMap) dropTableImpl(table *TableMap, ifExists bool) (err error) {
+ tableDrop := "drop table"
+ if ifExists {
+ tableDrop = m.Dialect.IfTableExists(tableDrop, table.SchemaName, table.TableName)
+ }
+ _, err = m.Exec(fmt.Sprintf("%s %s;", tableDrop, m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName)))
+ return err
+}
+
+// TruncateTables iterates through TableMaps registered to this DbMap and
+// executes "truncate table" statements against the database for each, or in the case of
+// sqlite, a "delete from" with no "where" clause, which uses the truncate optimization
+// (http://www.sqlite.org/lang_delete.html)
+func (m *DbMap) TruncateTables() error {
+ var err error
+ for i := range m.tables {
+ table := m.tables[i]
+ _, e := m.Exec(fmt.Sprintf("%s %s;", m.Dialect.TruncateClause(), m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName)))
+ if e != nil {
+ err = e
+ }
+ }
+ return err
+}
+
+// Insert runs a SQL INSERT statement for each element in list. List
+// items must be pointers.
+//
+// Any interface whose TableMap has an auto-increment primary key will
+// have its last insert id bound to the PK field on the struct.
+//
+// The hook functions PreInsert() and/or PostInsert() will be executed
+// before/after the INSERT statement if the interface defines them.
+//
+// Panics if any interface in the list has not been registered with AddTable
+func (m *DbMap) Insert(list ...interface{}) error {
+ return insert(m, m, list...)
+}
+
+// Update runs a SQL UPDATE statement for each element in list. List
+// items must be pointers.
+//
+// The hook functions PreUpdate() and/or PostUpdate() will be executed
+// before/after the UPDATE statement if the interface defines them.
+//
+// Returns the number of rows updated.
+//
+// Returns an error if SetKeys has not been called on the TableMap
+// Panics if any interface in the list has not been registered with AddTable
+func (m *DbMap) Update(list ...interface{}) (int64, error) {
+ return update(m, m, nil, list...)
+}
+
+// UpdateColumns runs a SQL UPDATE statement for each element in list. List
+// items must be pointers.
+//
+// Only the columns accepted by filter are included in the UPDATE.
+//
+// The hook functions PreUpdate() and/or PostUpdate() will be executed
+// before/after the UPDATE statement if the interface defines them.
+//
+// Returns the number of rows updated.
+//
+// Returns an error if SetKeys has not been called on the TableMap
+// Panics if any interface in the list has not been registered with AddTable
+func (m *DbMap) UpdateColumns(filter ColumnFilter, list ...interface{}) (int64, error) {
+ return update(m, m, filter, list...)
+}
+
+// Delete runs a SQL DELETE statement for each element in list. List
+// items must be pointers.
+//
+// The hook functions PreDelete() and/or PostDelete() will be executed
+// before/after the DELETE statement if the interface defines them.
+//
+// Returns the number of rows deleted.
+//
+// Returns an error if SetKeys has not been called on the TableMap
+// Panics if any interface in the list has not been registered with AddTable
+func (m *DbMap) Delete(list ...interface{}) (int64, error) {
+ return delete(m, m, list...)
+}
+
+// Get runs a SQL SELECT to fetch a single row from the table based on the
+// primary key(s)
+//
+// i should be an empty value for the struct to load. keys should be
+// the primary key value(s) for the row to load. If multiple keys
+// exist on the table, the order should match the column order
+// specified in SetKeys() when the table mapping was defined.
+//
+// The hook function PostGet() will be executed after the SELECT
+// statement if the interface defines them.
+//
+// Returns a pointer to a struct that matches or nil if no row is found.
+//
+// Returns an error if SetKeys has not been called on the TableMap
+// Panics if any interface in the list has not been registered with AddTable
+func (m *DbMap) Get(i interface{}, keys ...interface{}) (interface{}, error) {
+ return get(m, m, i, keys...)
+}
+
+// Select runs an arbitrary SQL query, binding the columns in the result
+// to fields on the struct specified by i. args represent the bind
+// parameters for the SQL statement.
+//
+// Column names on the SELECT statement should be aliased to the field names
+// on the struct i. Returns an error if one or more columns in the result
+// do not match. It is OK if fields on i are not part of the SQL
+// statement.
+//
+// The hook function PostGet() will be executed after the SELECT
+// statement if the interface defines them.
+//
+// Values are returned in one of two ways:
+// 1. If i is a struct or a pointer to a struct, returns a slice of pointers to
+// matching rows of type i.
+// 2. If i is a pointer to a slice, the results will be appended to that slice
+// and nil returned.
+//
+// i does NOT need to be registered with AddTable()
+func (m *DbMap) Select(i interface{}, query string, args ...interface{}) ([]interface{}, error) {
+ return hookedselect(m, m, i, query, args...)
+}
+
+// Exec runs an arbitrary SQL statement. args represent the bind parameters.
+// This is equivalent to running: Exec() using database/sql
+func (m *DbMap) Exec(query string, args ...interface{}) (sql.Result, error) {
+ if m.logger != nil {
+ now := time.Now()
+ defer m.trace(now, query, args...)
+ }
+ return exec(m, query, args...)
+}
+
+// SelectInt is a convenience wrapper around the gorp.SelectInt function
+func (m *DbMap) SelectInt(query string, args ...interface{}) (int64, error) {
+ return SelectInt(m, query, args...)
+}
+
+// SelectNullInt is a convenience wrapper around the gorp.SelectNullInt function
+func (m *DbMap) SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error) {
+ return SelectNullInt(m, query, args...)
+}
+
+// SelectFloat is a convenience wrapper around the gorp.SelectFloat function
+func (m *DbMap) SelectFloat(query string, args ...interface{}) (float64, error) {
+ return SelectFloat(m, query, args...)
+}
+
+// SelectNullFloat is a convenience wrapper around the gorp.SelectNullFloat function
+func (m *DbMap) SelectNullFloat(query string, args ...interface{}) (sql.NullFloat64, error) {
+ return SelectNullFloat(m, query, args...)
+}
+
+// SelectStr is a convenience wrapper around the gorp.SelectStr function
+func (m *DbMap) SelectStr(query string, args ...interface{}) (string, error) {
+ return SelectStr(m, query, args...)
+}
+
+// SelectNullStr is a convenience wrapper around the gorp.SelectNullStr function
+func (m *DbMap) SelectNullStr(query string, args ...interface{}) (sql.NullString, error) {
+ return SelectNullStr(m, query, args...)
+}
+
+// SelectOne is a convenience wrapper around the gorp.SelectOne function
+func (m *DbMap) SelectOne(holder interface{}, query string, args ...interface{}) error {
+ return SelectOne(m, m, holder, query, args...)
+}
+
+// Begin starts a gorp Transaction
+func (m *DbMap) Begin() (*Transaction, error) {
+ if m.logger != nil {
+ now := time.Now()
+ defer m.trace(now, "begin;")
+ }
+ tx, err := m.Db.Begin()
+ if err != nil {
+ return nil, err
+ }
+ return &Transaction{m, tx, false}, nil
+}
+
+// TableFor returns the *TableMap corresponding to the given Go Type
+// If no table is mapped to that type an error is returned.
+// If checkPK is true and the mapped table has no registered PKs, an error is returned.
+func (m *DbMap) TableFor(t reflect.Type, checkPK bool) (*TableMap, error) {
+ table := tableOrNil(m, t)
+ if table == nil {
+ return nil, fmt.Errorf("no table found for type: %v", t.Name())
+ }
+
+ if checkPK && len(table.keys) < 1 {
+ e := fmt.Sprintf("gorp: no keys defined for table: %s",
+ table.TableName)
+ return nil, errors.New(e)
+ }
+
+ return table, nil
+}
+
+// Prepare creates a prepared statement for later queries or executions.
+// Multiple queries or executions may be run concurrently from the returned statement.
+// This is equivalent to running: Prepare() using database/sql
+func (m *DbMap) Prepare(query string) (*sql.Stmt, error) {
+ if m.logger != nil {
+ now := time.Now()
+ defer m.trace(now, query, nil)
+ }
+ return m.Db.Prepare(query)
+}
+
+func tableOrNil(m *DbMap, t reflect.Type) *TableMap {
+ for i := range m.tables {
+ table := m.tables[i]
+ if table.gotype == t {
+ return table
+ }
+ }
+ return nil
+}
+
+func (m *DbMap) tableForPointer(ptr interface{}, checkPK bool) (*TableMap, reflect.Value, error) {
+ ptrv := reflect.ValueOf(ptr)
+ if ptrv.Kind() != reflect.Ptr {
+ e := fmt.Sprintf("gorp: passed non-pointer: %v (kind=%v)", ptr,
+ ptrv.Kind())
+ return nil, reflect.Value{}, errors.New(e)
+ }
+ elem := ptrv.Elem()
+ etype := reflect.TypeOf(elem.Interface())
+ t, err := m.TableFor(etype, checkPK)
+ if err != nil {
+ return nil, reflect.Value{}, err
+ }
+
+ return t, elem, nil
+}
+
+func (m *DbMap) queryRow(query string, args ...interface{}) *sql.Row {
+ if m.logger != nil {
+ now := time.Now()
+ defer m.trace(now, query, args...)
+ }
+ return m.Db.QueryRow(query, args...)
+}
+
+func (m *DbMap) query(query string, args ...interface{}) (*sql.Rows, error) {
+ if m.logger != nil {
+ now := time.Now()
+ defer m.trace(now, query, args...)
+ }
+ return m.Db.Query(query, args...)
+}
+
+func (m *DbMap) trace(started time.Time, query string, args ...interface{}) {
+ if m.logger != nil {
+ var margs = argsString(args...)
+ m.logger.Printf("%s%s [%s] (%v)", m.logPrefix, query, margs, (time.Now().Sub(started)))
+ }
+}
diff --git a/vendor/github.com/go-gorp/gorp/dialect.go b/vendor/github.com/go-gorp/gorp/dialect.go
new file mode 100644
index 000000000..203bc62b5
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/dialect.go
@@ -0,0 +1,111 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import "reflect"
+
+// The Dialect interface encapsulates behaviors that differ across
+// SQL databases. At present the Dialect is only used by CreateTables()
+// but this could change in the future
+type Dialect interface {
+
+ // adds a suffix to any query, usually ";"
+ QuerySuffix() string
+
+ // ToSqlType returns the SQL column type to use when creating a
+ // table of the given Go Type. maxsize can be used to switch based on
+ // size. For example, in MySQL []byte could map to BLOB, MEDIUMBLOB,
+ // or LONGBLOB depending on the maxsize
+ ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string
+
+ // string to append to primary key column definitions
+ AutoIncrStr() string
+
+ // string to bind autoincrement columns to. Empty string will
+ // remove reference to those columns in the INSERT statement.
+ AutoIncrBindValue() string
+
+ AutoIncrInsertSuffix(col *ColumnMap) string
+
+ // string to append to "create table" statement for vendor specific
+ // table attributes
+ CreateTableSuffix() string
+
+ // string to append to "create index" statement
+ CreateIndexSuffix() string
+
+ // string to append to "drop index" statement
+ DropIndexSuffix() string
+
+ // string to truncate tables
+ TruncateClause() string
+
+ // bind variable string to use when forming SQL statements
+ // in many dbs it is "?", but Postgres appears to use $1
+ //
+ // i is a zero based index of the bind variable in this statement
+ //
+ BindVar(i int) string
+
+ // Handles quoting of a field name to ensure that it doesn't raise any
+ // SQL parsing exceptions by using a reserved word as a field name.
+ QuoteField(field string) string
+
+ // Handles building up of a schema.database string that is compatible with
+ // the given dialect
+ //
+ // schema - The schema that <table> lives in
+ // table - The table name
+ QuotedTableForQuery(schema string, table string) string
+
+ // Existance clause for table creation / deletion
+ IfSchemaNotExists(command, schema string) string
+ IfTableExists(command, schema, table string) string
+ IfTableNotExists(command, schema, table string) string
+}
+
+// IntegerAutoIncrInserter is implemented by dialects that can perform
+// inserts with automatically incremented integer primary keys. If
+// the dialect can handle automatic assignment of more than just
+// integers, see TargetedAutoIncrInserter.
+type IntegerAutoIncrInserter interface {
+ InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error)
+}
+
+// TargetedAutoIncrInserter is implemented by dialects that can
+// perform automatic assignment of any primary key type (i.e. strings
+// for uuids, integers for serials, etc).
+type TargetedAutoIncrInserter interface {
+ // InsertAutoIncrToTarget runs an insert operation and assigns the
+ // automatically generated primary key directly to the passed in
+ // target. The target should be a pointer to the primary key
+ // field of the value being inserted.
+ InsertAutoIncrToTarget(exec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error
+}
+
+// TargetQueryInserter is implemented by dialects that can perform
+// assignment of integer primary key type by executing a query
+// like "select sequence.currval from dual".
+type TargetQueryInserter interface {
+ // TargetQueryInserter runs an insert operation and assigns the
+ // automatically generated primary key retrived by the query
+ // extracted from the GeneratedIdQuery field of the id column.
+ InsertQueryToTarget(exec SqlExecutor, insertSql, idSql string, target interface{}, params ...interface{}) error
+}
+
+func standardInsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
+ res, err := exec.Exec(insertSql, params...)
+ if err != nil {
+ return 0, err
+ }
+ return res.LastInsertId()
+}
diff --git a/vendor/github.com/go-gorp/gorp/dialect_mysql.go b/vendor/github.com/go-gorp/gorp/dialect_mysql.go
new file mode 100644
index 000000000..3d7d34027
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/dialect_mysql.go
@@ -0,0 +1,171 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// Implementation of Dialect for MySQL databases.
+type MySQLDialect struct {
+
+ // Engine is the storage engine to use "InnoDB" vs "MyISAM" for example
+ Engine string
+
+ // Encoding is the character encoding to use for created tables
+ Encoding string
+}
+
+func (d MySQLDialect) QuerySuffix() string { return ";" }
+
+func (d MySQLDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
+ switch val.Kind() {
+ case reflect.Ptr:
+ return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
+ case reflect.Bool:
+ return "boolean"
+ case reflect.Int8:
+ return "tinyint"
+ case reflect.Uint8:
+ return "tinyint unsigned"
+ case reflect.Int16:
+ return "smallint"
+ case reflect.Uint16:
+ return "smallint unsigned"
+ case reflect.Int, reflect.Int32:
+ return "int"
+ case reflect.Uint, reflect.Uint32:
+ return "int unsigned"
+ case reflect.Int64:
+ return "bigint"
+ case reflect.Uint64:
+ return "bigint unsigned"
+ case reflect.Float64, reflect.Float32:
+ return "double"
+ case reflect.Slice:
+ if val.Elem().Kind() == reflect.Uint8 {
+ return "mediumblob"
+ }
+ }
+
+ switch val.Name() {
+ case "NullInt64":
+ return "bigint"
+ case "NullFloat64":
+ return "double"
+ case "NullBool":
+ return "tinyint"
+ case "Time":
+ return "datetime"
+ }
+
+ if maxsize < 1 {
+ maxsize = 255
+ }
+
+ /* == About varchar(N) ==
+ * N is number of characters.
+ * A varchar column can store up to 65535 bytes.
+ * Remember that 1 character is 3 bytes in utf-8 charset.
+ * Also remember that each row can store up to 65535 bytes,
+ * and you have some overheads, so it's not possible for a
+ * varchar column to have 65535/3 characters really.
+ * So it would be better to use 'text' type in stead of
+ * large varchar type.
+ */
+ if maxsize < 256 {
+ return fmt.Sprintf("varchar(%d)", maxsize)
+ } else {
+ return "text"
+ }
+}
+
+// Returns auto_increment
+func (d MySQLDialect) AutoIncrStr() string {
+ return "auto_increment"
+}
+
+func (d MySQLDialect) AutoIncrBindValue() string {
+ return "null"
+}
+
+func (d MySQLDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
+ return ""
+}
+
+// Returns engine=%s charset=%s based on values stored on struct
+func (d MySQLDialect) CreateTableSuffix() string {
+ if d.Engine == "" || d.Encoding == "" {
+ msg := "gorp - undefined"
+
+ if d.Engine == "" {
+ msg += " MySQLDialect.Engine"
+ }
+ if d.Engine == "" && d.Encoding == "" {
+ msg += ","
+ }
+ if d.Encoding == "" {
+ msg += " MySQLDialect.Encoding"
+ }
+ msg += ". Check that your MySQLDialect was correctly initialized when declared."
+ panic(msg)
+ }
+
+ return fmt.Sprintf(" engine=%s charset=%s", d.Engine, d.Encoding)
+}
+
+func (m MySQLDialect) CreateIndexSuffix() string {
+ return "using"
+}
+
+func (m MySQLDialect) DropIndexSuffix() string {
+ return "on"
+}
+
+func (m MySQLDialect) TruncateClause() string {
+ return "truncate"
+}
+
+// Returns "?"
+func (d MySQLDialect) BindVar(i int) string {
+ return "?"
+}
+
+func (d MySQLDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
+ return standardInsertAutoIncr(exec, insertSql, params...)
+}
+
+func (d MySQLDialect) QuoteField(f string) string {
+ return "`" + f + "`"
+}
+
+func (d MySQLDialect) QuotedTableForQuery(schema string, table string) string {
+ if strings.TrimSpace(schema) == "" {
+ return d.QuoteField(table)
+ }
+
+ return schema + "." + d.QuoteField(table)
+}
+
+func (d MySQLDialect) IfSchemaNotExists(command, schema string) string {
+ return fmt.Sprintf("%s if not exists", command)
+}
+
+func (d MySQLDialect) IfTableExists(command, schema, table string) string {
+ return fmt.Sprintf("%s if exists", command)
+}
+
+func (d MySQLDialect) IfTableNotExists(command, schema, table string) string {
+ return fmt.Sprintf("%s if not exists", command)
+}
diff --git a/vendor/github.com/go-gorp/gorp/dialect_oracle.go b/vendor/github.com/go-gorp/gorp/dialect_oracle.go
new file mode 100644
index 000000000..c381380f9
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/dialect_oracle.go
@@ -0,0 +1,146 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// Implementation of Dialect for Oracle databases.
+type OracleDialect struct{}
+
+func (d OracleDialect) QuerySuffix() string { return "" }
+
+func (d OracleDialect) CreateIndexSuffix() string { return "" }
+
+func (d OracleDialect) DropIndexSuffix() string { return "" }
+
+func (d OracleDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
+ switch val.Kind() {
+ case reflect.Ptr:
+ return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
+ case reflect.Bool:
+ return "boolean"
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
+ if isAutoIncr {
+ return "serial"
+ }
+ return "integer"
+ case reflect.Int64, reflect.Uint64:
+ if isAutoIncr {
+ return "bigserial"
+ }
+ return "bigint"
+ case reflect.Float64:
+ return "double precision"
+ case reflect.Float32:
+ return "real"
+ case reflect.Slice:
+ if val.Elem().Kind() == reflect.Uint8 {
+ return "bytea"
+ }
+ }
+
+ switch val.Name() {
+ case "NullInt64":
+ return "bigint"
+ case "NullFloat64":
+ return "double precision"
+ case "NullBool":
+ return "boolean"
+ case "NullTime", "Time":
+ return "timestamp with time zone"
+ }
+
+ if maxsize > 0 {
+ return fmt.Sprintf("varchar(%d)", maxsize)
+ } else {
+ return "text"
+ }
+
+}
+
+// Returns empty string
+func (d OracleDialect) AutoIncrStr() string {
+ return ""
+}
+
+func (d OracleDialect) AutoIncrBindValue() string {
+ return "NULL"
+}
+
+func (d OracleDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
+ return ""
+}
+
+// Returns suffix
+func (d OracleDialect) CreateTableSuffix() string {
+ return ""
+}
+
+func (d OracleDialect) TruncateClause() string {
+ return "truncate"
+}
+
+// Returns "$(i+1)"
+func (d OracleDialect) BindVar(i int) string {
+ return fmt.Sprintf(":%d", i+1)
+}
+
+// After executing the insert uses the ColMap IdQuery to get the generated id
+func (d OracleDialect) InsertQueryToTarget(exec SqlExecutor, insertSql, idSql string, target interface{}, params ...interface{}) error {
+ _, err := exec.Exec(insertSql, params...)
+ if err != nil {
+ return err
+ }
+ id, err := exec.SelectInt(idSql)
+ if err != nil {
+ return err
+ }
+ switch target.(type) {
+ case *int64:
+ *(target.(*int64)) = id
+ case *int32:
+ *(target.(*int32)) = int32(id)
+ case int:
+ *(target.(*int)) = int(id)
+ default:
+ return fmt.Errorf("Id field can be int, int32 or int64")
+ }
+ return nil
+}
+
+func (d OracleDialect) QuoteField(f string) string {
+ return `"` + strings.ToUpper(f) + `"`
+}
+
+func (d OracleDialect) QuotedTableForQuery(schema string, table string) string {
+ if strings.TrimSpace(schema) == "" {
+ return d.QuoteField(table)
+ }
+
+ return schema + "." + d.QuoteField(table)
+}
+
+func (d OracleDialect) IfSchemaNotExists(command, schema string) string {
+ return fmt.Sprintf("%s if not exists", command)
+}
+
+func (d OracleDialect) IfTableExists(command, schema, table string) string {
+ return fmt.Sprintf("%s if exists", command)
+}
+
+func (d OracleDialect) IfTableNotExists(command, schema, table string) string {
+ return fmt.Sprintf("%s if not exists", command)
+}
diff --git a/vendor/github.com/go-gorp/gorp/dialect_postgres.go b/vendor/github.com/go-gorp/gorp/dialect_postgres.go
new file mode 100644
index 000000000..736788f2a
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/dialect_postgres.go
@@ -0,0 +1,147 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+type PostgresDialect struct {
+ suffix string
+}
+
+func (d PostgresDialect) QuerySuffix() string { return ";" }
+
+func (d PostgresDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
+ switch val.Kind() {
+ case reflect.Ptr:
+ return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
+ case reflect.Bool:
+ return "boolean"
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
+ if isAutoIncr {
+ return "serial"
+ }
+ return "integer"
+ case reflect.Int64, reflect.Uint64:
+ if isAutoIncr {
+ return "bigserial"
+ }
+ return "bigint"
+ case reflect.Float64:
+ return "double precision"
+ case reflect.Float32:
+ return "real"
+ case reflect.Slice:
+ if val.Elem().Kind() == reflect.Uint8 {
+ return "bytea"
+ }
+ }
+
+ switch val.Name() {
+ case "NullInt64":
+ return "bigint"
+ case "NullFloat64":
+ return "double precision"
+ case "NullBool":
+ return "boolean"
+ case "Time", "NullTime":
+ return "timestamp with time zone"
+ }
+
+ if maxsize > 0 {
+ return fmt.Sprintf("varchar(%d)", maxsize)
+ } else {
+ return "text"
+ }
+
+}
+
+// Returns empty string
+func (d PostgresDialect) AutoIncrStr() string {
+ return ""
+}
+
+func (d PostgresDialect) AutoIncrBindValue() string {
+ return "default"
+}
+
+func (d PostgresDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
+ return " returning " + d.QuoteField(col.ColumnName)
+}
+
+// Returns suffix
+func (d PostgresDialect) CreateTableSuffix() string {
+ return d.suffix
+}
+
+func (d PostgresDialect) CreateIndexSuffix() string {
+ return "using"
+}
+
+func (d PostgresDialect) DropIndexSuffix() string {
+ return ""
+}
+
+func (d PostgresDialect) TruncateClause() string {
+ return "truncate"
+}
+
+// Returns "$(i+1)"
+func (d PostgresDialect) BindVar(i int) string {
+ return fmt.Sprintf("$%d", i+1)
+}
+
+func (d PostgresDialect) InsertAutoIncrToTarget(exec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error {
+ rows, err := exec.query(insertSql, params...)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ if !rows.Next() {
+ return fmt.Errorf("No serial value returned for insert: %s Encountered error: %s", insertSql, rows.Err())
+ }
+ if err := rows.Scan(target); err != nil {
+ return err
+ }
+ if rows.Next() {
+ return fmt.Errorf("more than two serial value returned for insert: %s", insertSql)
+ }
+ return rows.Err()
+}
+
+func (d PostgresDialect) QuoteField(f string) string {
+ return `"` + f + `"`
+}
+
+func (d PostgresDialect) QuotedTableForQuery(schema string, table string) string {
+ if strings.TrimSpace(schema) == "" {
+ return d.QuoteField(table)
+ }
+
+ return schema + "." + d.QuoteField(table)
+}
+
+func (d PostgresDialect) IfSchemaNotExists(command, schema string) string {
+ return fmt.Sprintf("%s if not exists", command)
+}
+
+func (d PostgresDialect) IfTableExists(command, schema, table string) string {
+ return fmt.Sprintf("%s if exists", command)
+}
+
+func (d PostgresDialect) IfTableNotExists(command, schema, table string) string {
+ return fmt.Sprintf("%s if not exists", command)
+}
diff --git a/vendor/github.com/go-gorp/gorp/dialect_sqlite.go b/vendor/github.com/go-gorp/gorp/dialect_sqlite.go
new file mode 100644
index 000000000..7d9b29757
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/dialect_sqlite.go
@@ -0,0 +1,119 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "fmt"
+ "reflect"
+)
+
+type SqliteDialect struct {
+ suffix string
+}
+
+func (d SqliteDialect) QuerySuffix() string { return ";" }
+
+func (d SqliteDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
+ switch val.Kind() {
+ case reflect.Ptr:
+ return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
+ case reflect.Bool:
+ return "integer"
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return "integer"
+ case reflect.Float64, reflect.Float32:
+ return "real"
+ case reflect.Slice:
+ if val.Elem().Kind() == reflect.Uint8 {
+ return "blob"
+ }
+ }
+
+ switch val.Name() {
+ case "NullInt64":
+ return "integer"
+ case "NullFloat64":
+ return "real"
+ case "NullBool":
+ return "integer"
+ case "Time":
+ return "datetime"
+ }
+
+ if maxsize < 1 {
+ maxsize = 255
+ }
+ return fmt.Sprintf("varchar(%d)", maxsize)
+}
+
+// Returns autoincrement
+func (d SqliteDialect) AutoIncrStr() string {
+ return "autoincrement"
+}
+
+func (d SqliteDialect) AutoIncrBindValue() string {
+ return "null"
+}
+
+func (d SqliteDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
+ return ""
+}
+
+// Returns suffix
+func (d SqliteDialect) CreateTableSuffix() string {
+ return d.suffix
+}
+
+func (d SqliteDialect) CreateIndexSuffix() string {
+ return ""
+}
+
+func (d SqliteDialect) DropIndexSuffix() string {
+ return ""
+}
+
+// With sqlite, there technically isn't a TRUNCATE statement,
+// but a DELETE FROM uses a truncate optimization:
+// http://www.sqlite.org/lang_delete.html
+func (d SqliteDialect) TruncateClause() string {
+ return "delete from"
+}
+
+// Returns "?"
+func (d SqliteDialect) BindVar(i int) string {
+ return "?"
+}
+
+func (d SqliteDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
+ return standardInsertAutoIncr(exec, insertSql, params...)
+}
+
+func (d SqliteDialect) QuoteField(f string) string {
+ return `"` + f + `"`
+}
+
+// sqlite does not have schemas like PostgreSQL does, so just escape it like normal
+func (d SqliteDialect) QuotedTableForQuery(schema string, table string) string {
+ return d.QuoteField(table)
+}
+
+func (d SqliteDialect) IfSchemaNotExists(command, schema string) string {
+ return fmt.Sprintf("%s if not exists", command)
+}
+
+func (d SqliteDialect) IfTableExists(command, schema, table string) string {
+ return fmt.Sprintf("%s if exists", command)
+}
+
+func (d SqliteDialect) IfTableNotExists(command, schema, table string) string {
+ return fmt.Sprintf("%s if not exists", command)
+}
diff --git a/vendor/github.com/go-gorp/gorp/dialect_sqlserver.go b/vendor/github.com/go-gorp/gorp/dialect_sqlserver.go
new file mode 100644
index 000000000..8808af598
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/dialect_sqlserver.go
@@ -0,0 +1,152 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// Implementation of Dialect for Microsoft SQL Server databases.
+// Use gorp.SqlServerDialect{"2005"} for legacy datatypes.
+// Tested with driver: github.com/denisenkom/go-mssqldb
+
+type SqlServerDialect struct {
+
+ // If set to "2005" legacy datatypes will be used
+ Version string
+}
+
+func (d SqlServerDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
+ switch val.Kind() {
+ case reflect.Ptr:
+ return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
+ case reflect.Bool:
+ return "bit"
+ case reflect.Int8:
+ return "tinyint"
+ case reflect.Uint8:
+ return "smallint"
+ case reflect.Int16:
+ return "smallint"
+ case reflect.Uint16:
+ return "int"
+ case reflect.Int, reflect.Int32:
+ return "int"
+ case reflect.Uint, reflect.Uint32:
+ return "bigint"
+ case reflect.Int64:
+ return "bigint"
+ case reflect.Uint64:
+ return "numeric(20,0)"
+ case reflect.Float32:
+ return "float(24)"
+ case reflect.Float64:
+ return "float(53)"
+ case reflect.Slice:
+ if val.Elem().Kind() == reflect.Uint8 {
+ return "varbinary"
+ }
+ }
+
+ switch val.Name() {
+ case "NullInt64":
+ return "bigint"
+ case "NullFloat64":
+ return "float(53)"
+ case "NullBool":
+ return "bit"
+ case "NullTime", "Time":
+ if d.Version == "2005" {
+ return "datetime"
+ }
+ return "datetime2"
+ }
+
+ if maxsize < 1 {
+ if d.Version == "2005" {
+ maxsize = 255
+ } else {
+ return fmt.Sprintf("nvarchar(max)")
+ }
+ }
+ return fmt.Sprintf("nvarchar(%d)", maxsize)
+}
+
+// Returns auto_increment
+func (d SqlServerDialect) AutoIncrStr() string {
+ return "identity(0,1)"
+}
+
+// Empty string removes autoincrement columns from the INSERT statements.
+func (d SqlServerDialect) AutoIncrBindValue() string {
+ return ""
+}
+
+func (d SqlServerDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
+ return ""
+}
+
+func (d SqlServerDialect) CreateTableSuffix() string { return ";" }
+
+func (d SqlServerDialect) TruncateClause() string {
+ return "truncate table"
+}
+
+// Returns "?"
+func (d SqlServerDialect) BindVar(i int) string {
+ return "?"
+}
+
+func (d SqlServerDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
+ return standardInsertAutoIncr(exec, insertSql, params...)
+}
+
+func (d SqlServerDialect) QuoteField(f string) string {
+ return "[" + strings.Replace(f, "]", "]]", -1) + "]"
+}
+
+func (d SqlServerDialect) QuotedTableForQuery(schema string, table string) string {
+ if strings.TrimSpace(schema) == "" {
+ return d.QuoteField(table)
+ }
+ return d.QuoteField(schema) + "." + d.QuoteField(table)
+}
+
+func (d SqlServerDialect) QuerySuffix() string { return ";" }
+
+func (d SqlServerDialect) IfSchemaNotExists(command, schema string) string {
+ s := fmt.Sprintf("if schema_id(N'%s') is null %s", schema, command)
+ return s
+}
+
+func (d SqlServerDialect) IfTableExists(command, schema, table string) string {
+ var schema_clause string
+ if strings.TrimSpace(schema) != "" {
+ schema_clause = fmt.Sprintf("%s.", d.QuoteField(schema))
+ }
+ s := fmt.Sprintf("if object_id('%s%s') is not null %s", schema_clause, d.QuoteField(table), command)
+ return s
+}
+
+func (d SqlServerDialect) IfTableNotExists(command, schema, table string) string {
+ var schema_clause string
+ if strings.TrimSpace(schema) != "" {
+ schema_clause = fmt.Sprintf("%s.", schema)
+ }
+ s := fmt.Sprintf("if object_id('%s%s') is null %s", schema_clause, table, command)
+ return s
+}
+
+func (d SqlServerDialect) CreateIndexSuffix() string { return "" }
+func (d SqlServerDialect) DropIndexSuffix() string { return "" }
diff --git a/vendor/github.com/go-gorp/gorp/errors.go b/vendor/github.com/go-gorp/gorp/errors.go
new file mode 100644
index 000000000..d13f03fc3
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/errors.go
@@ -0,0 +1,38 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "fmt"
+)
+
+// A non-fatal error, when a select query returns columns that do not exist
+// as fields in the struct it is being mapped to
+// TODO: discuss wether this needs an error. encoding/json silently ignores missing fields
+type NoFieldInTypeError struct {
+ TypeName string
+ MissingColNames []string
+}
+
+func (err *NoFieldInTypeError) Error() string {
+ return fmt.Sprintf("gorp: no fields %+v in type %s", err.MissingColNames, err.TypeName)
+}
+
+// returns true if the error is non-fatal (ie, we shouldn't immediately return)
+func NonFatalError(err error) bool {
+ switch err.(type) {
+ case *NoFieldInTypeError:
+ return true
+ default:
+ return false
+ }
+}
diff --git a/vendor/github.com/go-gorp/gorp/gorp.go b/vendor/github.com/go-gorp/gorp/gorp.go
new file mode 100644
index 000000000..1f32283f5
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/gorp.go
@@ -0,0 +1,558 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+//
+package gorp
+
+import (
+ "database/sql"
+ "database/sql/driver"
+ "fmt"
+ "reflect"
+ "regexp"
+ "strings"
+ "time"
+)
+
+// OracleString (empty string is null)
+// TODO: move to dialect/oracle?, rename to String?
+type OracleString struct {
+ sql.NullString
+}
+
+// Scan implements the Scanner interface.
+func (os *OracleString) Scan(value interface{}) error {
+ if value == nil {
+ os.String, os.Valid = "", false
+ return nil
+ }
+ os.Valid = true
+ return os.NullString.Scan(value)
+}
+
+// Value implements the driver Valuer interface.
+func (os OracleString) Value() (driver.Value, error) {
+ if !os.Valid || os.String == "" {
+ return nil, nil
+ }
+ return os.String, nil
+}
+
+// SqlTyper is a type that returns its database type. Most of the
+// time, the type can just use "database/sql/driver".Valuer; but when
+// it returns nil for its empty value, it needs to implement SqlTyper
+// to have its column type detected properly during table creation.
+type SqlTyper interface {
+ SqlType() driver.Valuer
+}
+
+// for fields that exists in DB table, but not exists in struct
+type dummyField struct{}
+
+// Scan implements the Scanner interface.
+func (nt *dummyField) Scan(value interface{}) error {
+ return nil
+}
+
+var zeroVal reflect.Value
+var versFieldConst = "[gorp_ver_field]"
+
+// The TypeConverter interface provides a way to map a value of one
+// type to another type when persisting to, or loading from, a database.
+//
+// Example use cases: Implement type converter to convert bool types to "y"/"n" strings,
+// or serialize a struct member as a JSON blob.
+type TypeConverter interface {
+ // ToDb converts val to another type. Called before INSERT/UPDATE operations
+ ToDb(val interface{}) (interface{}, error)
+
+ // FromDb returns a CustomScanner appropriate for this type. This will be used
+ // to hold values returned from SELECT queries.
+ //
+ // In particular the CustomScanner returned should implement a Binder
+ // function appropriate for the Go type you wish to convert the db value to
+ //
+ // If bool==false, then no custom scanner will be used for this field.
+ FromDb(target interface{}) (CustomScanner, bool)
+}
+
+// Executor exposes the sql.DB and sql.Tx Exec function so that it can be used
+// on internal functions that convert named parameters for the Exec function.
+type executor interface {
+ Exec(query string, args ...interface{}) (sql.Result, error)
+}
+
+// SqlExecutor exposes gorp operations that can be run from Pre/Post
+// hooks. This hides whether the current operation that triggered the
+// hook is in a transaction.
+//
+// See the DbMap function docs for each of the functions below for more
+// information.
+type SqlExecutor interface {
+ Get(i interface{}, keys ...interface{}) (interface{}, error)
+ Insert(list ...interface{}) error
+ Update(list ...interface{}) (int64, error)
+ Delete(list ...interface{}) (int64, error)
+ Exec(query string, args ...interface{}) (sql.Result, error)
+ Select(i interface{}, query string,
+ args ...interface{}) ([]interface{}, error)
+ SelectInt(query string, args ...interface{}) (int64, error)
+ SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error)
+ SelectFloat(query string, args ...interface{}) (float64, error)
+ SelectNullFloat(query string, args ...interface{}) (sql.NullFloat64, error)
+ SelectStr(query string, args ...interface{}) (string, error)
+ SelectNullStr(query string, args ...interface{}) (sql.NullString, error)
+ SelectOne(holder interface{}, query string, args ...interface{}) error
+ query(query string, args ...interface{}) (*sql.Rows, error)
+ queryRow(query string, args ...interface{}) *sql.Row
+}
+
+// Compile-time check that DbMap and Transaction implement the SqlExecutor
+// interface.
+var _, _ SqlExecutor = &DbMap{}, &Transaction{}
+
+func argsString(args ...interface{}) string {
+ var margs string
+ for i, a := range args {
+ var v interface{} = a
+ if x, ok := v.(driver.Valuer); ok {
+ y, err := x.Value()
+ if err == nil {
+ v = y
+ }
+ }
+ switch v.(type) {
+ case string:
+ v = fmt.Sprintf("%q", v)
+ default:
+ v = fmt.Sprintf("%v", v)
+ }
+ margs += fmt.Sprintf("%d:%s", i+1, v)
+ if i+1 < len(args) {
+ margs += " "
+ }
+ }
+ return margs
+}
+
+// Calls the Exec function on the executor, but attempts to expand any eligible named
+// query arguments first.
+func exec(e SqlExecutor, query string, args ...interface{}) (sql.Result, error) {
+ var dbMap *DbMap
+ var executor executor
+ switch m := e.(type) {
+ case *DbMap:
+ executor = m.Db
+ dbMap = m
+ case *Transaction:
+ executor = m.tx
+ dbMap = m.dbmap
+ }
+
+ if len(args) == 1 {
+ query, args = maybeExpandNamedQuery(dbMap, query, args)
+ }
+
+ return executor.Exec(query, args...)
+}
+
+// maybeExpandNamedQuery checks the given arg to see if it's eligible to be used
+// as input to a named query. If so, it rewrites the query to use
+// dialect-dependent bindvars and instantiates the corresponding slice of
+// parameters by extracting data from the map / struct.
+// If not, returns the input values unchanged.
+func maybeExpandNamedQuery(m *DbMap, query string, args []interface{}) (string, []interface{}) {
+ var (
+ arg = args[0]
+ argval = reflect.ValueOf(arg)
+ )
+ if argval.Kind() == reflect.Ptr {
+ argval = argval.Elem()
+ }
+
+ if argval.Kind() == reflect.Map && argval.Type().Key().Kind() == reflect.String {
+ return expandNamedQuery(m, query, func(key string) reflect.Value {
+ return argval.MapIndex(reflect.ValueOf(key))
+ })
+ }
+ if argval.Kind() != reflect.Struct {
+ return query, args
+ }
+ if _, ok := arg.(time.Time); ok {
+ // time.Time is driver.Value
+ return query, args
+ }
+ if _, ok := arg.(driver.Valuer); ok {
+ // driver.Valuer will be converted to driver.Value.
+ return query, args
+ }
+
+ return expandNamedQuery(m, query, argval.FieldByName)
+}
+
+var keyRegexp = regexp.MustCompile(`:[[:word:]]+`)
+
+// expandNamedQuery accepts a query with placeholders of the form ":key", and a
+// single arg of Kind Struct or Map[string]. It returns the query with the
+// dialect's placeholders, and a slice of args ready for positional insertion
+// into the query.
+func expandNamedQuery(m *DbMap, query string, keyGetter func(key string) reflect.Value) (string, []interface{}) {
+ var (
+ n int
+ args []interface{}
+ )
+ return keyRegexp.ReplaceAllStringFunc(query, func(key string) string {
+ val := keyGetter(key[1:])
+ if !val.IsValid() {
+ return key
+ }
+ args = append(args, val.Interface())
+ newVar := m.Dialect.BindVar(n)
+ n++
+ return newVar
+ }), args
+}
+
+func columnToFieldIndex(m *DbMap, t reflect.Type, cols []string) ([][]int, error) {
+ colToFieldIndex := make([][]int, len(cols))
+
+ // check if type t is a mapped table - if so we'll
+ // check the table for column aliasing below
+ tableMapped := false
+ table := tableOrNil(m, t)
+ if table != nil {
+ tableMapped = true
+ }
+
+ // Loop over column names and find field in i to bind to
+ // based on column name. all returned columns must match
+ // a field in the i struct
+ missingColNames := []string{}
+ for x := range cols {
+ colName := strings.ToLower(cols[x])
+ field, found := t.FieldByNameFunc(func(fieldName string) bool {
+ field, _ := t.FieldByName(fieldName)
+ cArguments := strings.Split(field.Tag.Get("db"), ",")
+ fieldName = cArguments[0]
+
+ if fieldName == "-" {
+ return false
+ } else if fieldName == "" {
+ fieldName = field.Name
+ }
+ if tableMapped {
+ colMap := colMapOrNil(table, fieldName)
+ if colMap != nil {
+ fieldName = colMap.ColumnName
+ }
+ }
+ return colName == strings.ToLower(fieldName)
+ })
+ if found {
+ colToFieldIndex[x] = field.Index
+ }
+ if colToFieldIndex[x] == nil {
+ missingColNames = append(missingColNames, colName)
+ }
+ }
+ if len(missingColNames) > 0 {
+ return colToFieldIndex, &NoFieldInTypeError{
+ TypeName: t.Name(),
+ MissingColNames: missingColNames,
+ }
+ }
+ return colToFieldIndex, nil
+}
+
+func fieldByName(val reflect.Value, fieldName string) *reflect.Value {
+ // try to find field by exact match
+ f := val.FieldByName(fieldName)
+
+ if f != zeroVal {
+ return &f
+ }
+
+ // try to find by case insensitive match - only the Postgres driver
+ // seems to require this - in the case where columns are aliased in the sql
+ fieldNameL := strings.ToLower(fieldName)
+ fieldCount := val.NumField()
+ t := val.Type()
+ for i := 0; i < fieldCount; i++ {
+ sf := t.Field(i)
+ if strings.ToLower(sf.Name) == fieldNameL {
+ f := val.Field(i)
+ return &f
+ }
+ }
+
+ return nil
+}
+
+// toSliceType returns the element type of the given object, if the object is a
+// "*[]*Element" or "*[]Element". If not, returns nil.
+// err is returned if the user was trying to pass a pointer-to-slice but failed.
+func toSliceType(i interface{}) (reflect.Type, error) {
+ t := reflect.TypeOf(i)
+ if t.Kind() != reflect.Ptr {
+ // If it's a slice, return a more helpful error message
+ if t.Kind() == reflect.Slice {
+ return nil, fmt.Errorf("gorp: cannot SELECT into a non-pointer slice: %v", t)
+ }
+ return nil, nil
+ }
+ if t = t.Elem(); t.Kind() != reflect.Slice {
+ return nil, nil
+ }
+ return t.Elem(), nil
+}
+
+func toType(i interface{}) (reflect.Type, error) {
+ t := reflect.TypeOf(i)
+
+ // If a Pointer to a type, follow
+ for t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+
+ if t.Kind() != reflect.Struct {
+ return nil, fmt.Errorf("gorp: cannot SELECT into this type: %v", reflect.TypeOf(i))
+ }
+ return t, nil
+}
+
+func get(m *DbMap, exec SqlExecutor, i interface{},
+ keys ...interface{}) (interface{}, error) {
+
+ t, err := toType(i)
+ if err != nil {
+ return nil, err
+ }
+
+ table, err := m.TableFor(t, true)
+ if err != nil {
+ return nil, err
+ }
+
+ plan := table.bindGet()
+
+ v := reflect.New(t)
+ dest := make([]interface{}, len(plan.argFields))
+
+ conv := m.TypeConverter
+ custScan := make([]CustomScanner, 0)
+
+ for x, fieldName := range plan.argFields {
+ f := v.Elem().FieldByName(fieldName)
+ target := f.Addr().Interface()
+ if conv != nil {
+ scanner, ok := conv.FromDb(target)
+ if ok {
+ target = scanner.Holder
+ custScan = append(custScan, scanner)
+ }
+ }
+ dest[x] = target
+ }
+
+ row := exec.queryRow(plan.query, keys...)
+ err = row.Scan(dest...)
+ if err != nil {
+ if err == sql.ErrNoRows {
+ err = nil
+ }
+ return nil, err
+ }
+
+ for _, c := range custScan {
+ err = c.Bind()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if v, ok := v.Interface().(HasPostGet); ok {
+ err := v.PostGet(exec)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return v.Interface(), nil
+}
+
+func delete(m *DbMap, exec SqlExecutor, list ...interface{}) (int64, error) {
+ count := int64(0)
+ for _, ptr := range list {
+ table, elem, err := m.tableForPointer(ptr, true)
+ if err != nil {
+ return -1, err
+ }
+
+ eval := elem.Addr().Interface()
+ if v, ok := eval.(HasPreDelete); ok {
+ err = v.PreDelete(exec)
+ if err != nil {
+ return -1, err
+ }
+ }
+
+ bi, err := table.bindDelete(elem)
+ if err != nil {
+ return -1, err
+ }
+
+ res, err := exec.Exec(bi.query, bi.args...)
+ if err != nil {
+ return -1, err
+ }
+ rows, err := res.RowsAffected()
+ if err != nil {
+ return -1, err
+ }
+
+ if rows == 0 && bi.existingVersion > 0 {
+ return lockError(m, exec, table.TableName,
+ bi.existingVersion, elem, bi.keys...)
+ }
+
+ count += rows
+
+ if v, ok := eval.(HasPostDelete); ok {
+ err := v.PostDelete(exec)
+ if err != nil {
+ return -1, err
+ }
+ }
+ }
+
+ return count, nil
+}
+
+func update(m *DbMap, exec SqlExecutor, colFilter ColumnFilter, list ...interface{}) (int64, error) {
+ count := int64(0)
+ for _, ptr := range list {
+ table, elem, err := m.tableForPointer(ptr, true)
+ if err != nil {
+ return -1, err
+ }
+
+ eval := elem.Addr().Interface()
+ if v, ok := eval.(HasPreUpdate); ok {
+ err = v.PreUpdate(exec)
+ if err != nil {
+ return -1, err
+ }
+ }
+
+ bi, err := table.bindUpdate(elem, colFilter)
+ if err != nil {
+ return -1, err
+ }
+
+ res, err := exec.Exec(bi.query, bi.args...)
+ if err != nil {
+ return -1, err
+ }
+
+ rows, err := res.RowsAffected()
+ if err != nil {
+ return -1, err
+ }
+
+ if rows == 0 && bi.existingVersion > 0 {
+ return lockError(m, exec, table.TableName,
+ bi.existingVersion, elem, bi.keys...)
+ }
+
+ if bi.versField != "" {
+ elem.FieldByName(bi.versField).SetInt(bi.existingVersion + 1)
+ }
+
+ count += rows
+
+ if v, ok := eval.(HasPostUpdate); ok {
+ err = v.PostUpdate(exec)
+ if err != nil {
+ return -1, err
+ }
+ }
+ }
+ return count, nil
+}
+
+func insert(m *DbMap, exec SqlExecutor, list ...interface{}) error {
+ for _, ptr := range list {
+ table, elem, err := m.tableForPointer(ptr, false)
+ if err != nil {
+ return err
+ }
+
+ eval := elem.Addr().Interface()
+ if v, ok := eval.(HasPreInsert); ok {
+ err := v.PreInsert(exec)
+ if err != nil {
+ return err
+ }
+ }
+
+ bi, err := table.bindInsert(elem)
+ if err != nil {
+ return err
+ }
+
+ if bi.autoIncrIdx > -1 {
+ f := elem.FieldByName(bi.autoIncrFieldName)
+ switch inserter := m.Dialect.(type) {
+ case IntegerAutoIncrInserter:
+ id, err := inserter.InsertAutoIncr(exec, bi.query, bi.args...)
+ if err != nil {
+ return err
+ }
+ k := f.Kind()
+ if (k == reflect.Int) || (k == reflect.Int16) || (k == reflect.Int32) || (k == reflect.Int64) {
+ f.SetInt(id)
+ } else if (k == reflect.Uint) || (k == reflect.Uint16) || (k == reflect.Uint32) || (k == reflect.Uint64) {
+ f.SetUint(uint64(id))
+ } else {
+ return fmt.Errorf("gorp: cannot set autoincrement value on non-Int field. SQL=%s autoIncrIdx=%d autoIncrFieldName=%s", bi.query, bi.autoIncrIdx, bi.autoIncrFieldName)
+ }
+ case TargetedAutoIncrInserter:
+ err := inserter.InsertAutoIncrToTarget(exec, bi.query, f.Addr().Interface(), bi.args...)
+ if err != nil {
+ return err
+ }
+ case TargetQueryInserter:
+ var idQuery = table.ColMap(bi.autoIncrFieldName).GeneratedIdQuery
+ if idQuery == "" {
+ return fmt.Errorf("gorp: cannot set %s value if its ColumnMap.GeneratedIdQuery is empty", bi.autoIncrFieldName)
+ }
+ err := inserter.InsertQueryToTarget(exec, bi.query, idQuery, f.Addr().Interface(), bi.args...)
+ if err != nil {
+ return err
+ }
+ default:
+ return fmt.Errorf("gorp: cannot use autoincrement fields on dialects that do not implement an autoincrementing interface")
+ }
+ } else {
+ _, err := exec.Exec(bi.query, bi.args...)
+ if err != nil {
+ return err
+ }
+ }
+
+ if v, ok := eval.(HasPostInsert); ok {
+ err := v.PostInsert(exec)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/go-gorp/gorp/hooks.go b/vendor/github.com/go-gorp/gorp/hooks.go
new file mode 100644
index 000000000..192b51f00
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/hooks.go
@@ -0,0 +1,49 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+//++ TODO v2-phase3: HasPostGet => PostGetter, HasPostDelete => PostDeleter, etc.
+
+// PostUpdate() will be executed after the GET statement.
+type HasPostGet interface {
+ PostGet(SqlExecutor) error
+}
+
+// PostUpdate() will be executed after the DELETE statement
+type HasPostDelete interface {
+ PostDelete(SqlExecutor) error
+}
+
+// PostUpdate() will be executed after the UPDATE statement
+type HasPostUpdate interface {
+ PostUpdate(SqlExecutor) error
+}
+
+// PostInsert() will be executed after the INSERT statement
+type HasPostInsert interface {
+ PostInsert(SqlExecutor) error
+}
+
+// PreDelete() will be executed before the DELETE statement.
+type HasPreDelete interface {
+ PreDelete(SqlExecutor) error
+}
+
+// PreUpdate() will be executed before UPDATE statement.
+type HasPreUpdate interface {
+ PreUpdate(SqlExecutor) error
+}
+
+// PreInsert() will be executed before INSERT statement.
+type HasPreInsert interface {
+ PreInsert(SqlExecutor) error
+}
diff --git a/vendor/github.com/go-gorp/gorp/index.go b/vendor/github.com/go-gorp/gorp/index.go
new file mode 100644
index 000000000..01ecd9eca
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/index.go
@@ -0,0 +1,56 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+// IndexMap represents a mapping between a Go struct field and a single
+// index in a table.
+// Unique and MaxSize only inform the
+// CreateTables() function and are not used by Insert/Update/Delete/Get.
+type IndexMap struct {
+ // Index name in db table
+ IndexName string
+
+ // If true, " unique" is added to create index statements.
+ // Not used elsewhere
+ Unique bool
+
+ // Index type supported by Dialect
+ // Postgres: B-tree, Hash, GiST and GIN.
+ // Mysql: Btree, Hash.
+ // Sqlite: nil.
+ IndexType string
+
+ // Columns name for single and multiple indexes
+ columns []string
+}
+
+// Rename allows you to specify the index name in the table
+//
+// Example: table.IndMap("customer_test_idx").Rename("customer_idx")
+//
+func (idx *IndexMap) Rename(indname string) *IndexMap {
+ idx.IndexName = indname
+ return idx
+}
+
+// SetUnique adds "unique" to the create index statements for this
+// index, if b is true.
+func (idx *IndexMap) SetUnique(b bool) *IndexMap {
+ idx.Unique = b
+ return idx
+}
+
+// SetIndexType specifies the index type supported by chousen SQL Dialect
+func (idx *IndexMap) SetIndexType(indtype string) *IndexMap {
+ idx.IndexType = indtype
+ return idx
+}
diff --git a/vendor/github.com/go-gorp/gorp/lockerror.go b/vendor/github.com/go-gorp/gorp/lockerror.go
new file mode 100644
index 000000000..07b3047ae
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/lockerror.go
@@ -0,0 +1,63 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// OptimisticLockError is returned by Update() or Delete() if the
+// struct being modified has a Version field and the value is not equal to
+// the current value in the database
+type OptimisticLockError struct {
+ // Table name where the lock error occurred
+ TableName string
+
+ // Primary key values of the row being updated/deleted
+ Keys []interface{}
+
+ // true if a row was found with those keys, indicating the
+ // LocalVersion is stale. false if no value was found with those
+ // keys, suggesting the row has been deleted since loaded, or
+ // was never inserted to begin with
+ RowExists bool
+
+ // Version value on the struct passed to Update/Delete. This value is
+ // out of sync with the database.
+ LocalVersion int64
+}
+
+// Error returns a description of the cause of the lock error
+func (e OptimisticLockError) Error() string {
+ if e.RowExists {
+ return fmt.Sprintf("gorp: OptimisticLockError table=%s keys=%v out of date version=%d", e.TableName, e.Keys, e.LocalVersion)
+ }
+
+ return fmt.Sprintf("gorp: OptimisticLockError no row found for table=%s keys=%v", e.TableName, e.Keys)
+}
+
+func lockError(m *DbMap, exec SqlExecutor, tableName string,
+ existingVer int64, elem reflect.Value,
+ keys ...interface{}) (int64, error) {
+
+ existing, err := get(m, exec, elem.Interface(), keys...)
+ if err != nil {
+ return -1, err
+ }
+
+ ole := OptimisticLockError{tableName, keys, true, existingVer}
+ if existing == nil {
+ ole.RowExists = false
+ }
+ return -1, ole
+}
diff --git a/vendor/github.com/go-gorp/gorp/logging.go b/vendor/github.com/go-gorp/gorp/logging.go
new file mode 100644
index 000000000..89d6c0e79
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/logging.go
@@ -0,0 +1,44 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import "fmt"
+
+type GorpLogger interface {
+ Printf(format string, v ...interface{})
+}
+
+// TraceOn turns on SQL statement logging for this DbMap. After this is
+// called, all SQL statements will be sent to the logger. If prefix is
+// a non-empty string, it will be written to the front of all logged
+// strings, which can aid in filtering log lines.
+//
+// Use TraceOn if you want to spy on the SQL statements that gorp
+// generates.
+//
+// Note that the base log.Logger type satisfies GorpLogger, but adapters can
+// easily be written for other logging packages (e.g., the golang-sanctioned
+// glog framework).
+func (m *DbMap) TraceOn(prefix string, logger GorpLogger) {
+ m.logger = logger
+ if prefix == "" {
+ m.logPrefix = prefix
+ } else {
+ m.logPrefix = fmt.Sprintf("%s ", prefix)
+ }
+}
+
+// TraceOff turns off tracing. It is idempotent.
+func (m *DbMap) TraceOff() {
+ m.logger = nil
+ m.logPrefix = ""
+}
diff --git a/vendor/github.com/go-gorp/gorp/nulltypes.go b/vendor/github.com/go-gorp/gorp/nulltypes.go
new file mode 100644
index 000000000..870770372
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/nulltypes.go
@@ -0,0 +1,58 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "database/sql/driver"
+ "time"
+)
+
+// A nullable Time value
+type NullTime struct {
+ Time time.Time
+ Valid bool // Valid is true if Time is not NULL
+}
+
+// Scan implements the Scanner interface.
+func (nt *NullTime) Scan(value interface{}) error {
+ switch t := value.(type) {
+ case time.Time:
+ nt.Time, nt.Valid = t, true
+ case []byte:
+ nt.Valid = false
+ for _, dtfmt := range []string{
+ "2006-01-02 15:04:05.999999999",
+ "2006-01-02T15:04:05.999999999",
+ "2006-01-02 15:04:05",
+ "2006-01-02T15:04:05",
+ "2006-01-02 15:04",
+ "2006-01-02T15:04",
+ "2006-01-02",
+ "2006-01-02 15:04:05-07:00",
+ } {
+ var err error
+ if nt.Time, err = time.Parse(dtfmt, string(t)); err == nil {
+ nt.Valid = true
+ break
+ }
+ }
+ }
+ return nil
+}
+
+// Value implements the driver Valuer interface.
+func (nt NullTime) Value() (driver.Value, error) {
+ if !nt.Valid {
+ return nil, nil
+ }
+ return nt.Time, nil
+}
diff --git a/vendor/github.com/go-gorp/gorp/select.go b/vendor/github.com/go-gorp/gorp/select.go
new file mode 100644
index 000000000..d6ff92ee3
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/select.go
@@ -0,0 +1,351 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "database/sql"
+ "fmt"
+ "reflect"
+)
+
+// SelectInt executes the given query, which should be a SELECT statement for a single
+// integer column, and returns the value of the first row returned. If no rows are
+// found, zero is returned.
+func SelectInt(e SqlExecutor, query string, args ...interface{}) (int64, error) {
+ var h int64
+ err := selectVal(e, &h, query, args...)
+ if err != nil && err != sql.ErrNoRows {
+ return 0, err
+ }
+ return h, nil
+}
+
+// SelectNullInt executes the given query, which should be a SELECT statement for a single
+// integer column, and returns the value of the first row returned. If no rows are
+// found, the empty sql.NullInt64 value is returned.
+func SelectNullInt(e SqlExecutor, query string, args ...interface{}) (sql.NullInt64, error) {
+ var h sql.NullInt64
+ err := selectVal(e, &h, query, args...)
+ if err != nil && err != sql.ErrNoRows {
+ return h, err
+ }
+ return h, nil
+}
+
+// SelectFloat executes the given query, which should be a SELECT statement for a single
+// float column, and returns the value of the first row returned. If no rows are
+// found, zero is returned.
+func SelectFloat(e SqlExecutor, query string, args ...interface{}) (float64, error) {
+ var h float64
+ err := selectVal(e, &h, query, args...)
+ if err != nil && err != sql.ErrNoRows {
+ return 0, err
+ }
+ return h, nil
+}
+
+// SelectNullFloat executes the given query, which should be a SELECT statement for a single
+// float column, and returns the value of the first row returned. If no rows are
+// found, the empty sql.NullInt64 value is returned.
+func SelectNullFloat(e SqlExecutor, query string, args ...interface{}) (sql.NullFloat64, error) {
+ var h sql.NullFloat64
+ err := selectVal(e, &h, query, args...)
+ if err != nil && err != sql.ErrNoRows {
+ return h, err
+ }
+ return h, nil
+}
+
+// SelectStr executes the given query, which should be a SELECT statement for a single
+// char/varchar column, and returns the value of the first row returned. If no rows are
+// found, an empty string is returned.
+func SelectStr(e SqlExecutor, query string, args ...interface{}) (string, error) {
+ var h string
+ err := selectVal(e, &h, query, args...)
+ if err != nil && err != sql.ErrNoRows {
+ return "", err
+ }
+ return h, nil
+}
+
+// SelectNullStr executes the given query, which should be a SELECT
+// statement for a single char/varchar column, and returns the value
+// of the first row returned. If no rows are found, the empty
+// sql.NullString is returned.
+func SelectNullStr(e SqlExecutor, query string, args ...interface{}) (sql.NullString, error) {
+ var h sql.NullString
+ err := selectVal(e, &h, query, args...)
+ if err != nil && err != sql.ErrNoRows {
+ return h, err
+ }
+ return h, nil
+}
+
+// SelectOne executes the given query (which should be a SELECT statement)
+// and binds the result to holder, which must be a pointer.
+//
+// If no row is found, an error (sql.ErrNoRows specifically) will be returned
+//
+// If more than one row is found, an error will be returned.
+//
+func SelectOne(m *DbMap, e SqlExecutor, holder interface{}, query string, args ...interface{}) error {
+ t := reflect.TypeOf(holder)
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ } else {
+ return fmt.Errorf("gorp: SelectOne holder must be a pointer, but got: %t", holder)
+ }
+
+ // Handle pointer to pointer
+ isptr := false
+ if t.Kind() == reflect.Ptr {
+ isptr = true
+ t = t.Elem()
+ }
+
+ if t.Kind() == reflect.Struct {
+ var nonFatalErr error
+
+ list, err := hookedselect(m, e, holder, query, args...)
+ if err != nil {
+ if !NonFatalError(err) { // FIXME: double negative, rename NonFatalError to FatalError
+ return err
+ }
+ nonFatalErr = err
+ }
+
+ dest := reflect.ValueOf(holder)
+ if isptr {
+ dest = dest.Elem()
+ }
+
+ if list != nil && len(list) > 0 { // FIXME: invert if/else
+ // check for multiple rows
+ if len(list) > 1 {
+ return fmt.Errorf("gorp: multiple rows returned for: %s - %v", query, args)
+ }
+
+ // Initialize if nil
+ if dest.IsNil() {
+ dest.Set(reflect.New(t))
+ }
+
+ // only one row found
+ src := reflect.ValueOf(list[0])
+ dest.Elem().Set(src.Elem())
+ } else {
+ // No rows found, return a proper error.
+ return sql.ErrNoRows
+ }
+
+ return nonFatalErr
+ }
+
+ return selectVal(e, holder, query, args...)
+}
+
+func selectVal(e SqlExecutor, holder interface{}, query string, args ...interface{}) error {
+ if len(args) == 1 {
+ switch m := e.(type) {
+ case *DbMap:
+ query, args = maybeExpandNamedQuery(m, query, args)
+ case *Transaction:
+ query, args = maybeExpandNamedQuery(m.dbmap, query, args)
+ }
+ }
+ rows, err := e.query(query, args...)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ if !rows.Next() {
+ return sql.ErrNoRows
+ }
+
+ return rows.Scan(holder)
+}
+
+func hookedselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
+ args ...interface{}) ([]interface{}, error) {
+
+ var nonFatalErr error
+
+ list, err := rawselect(m, exec, i, query, args...)
+ if err != nil {
+ if !NonFatalError(err) {
+ return nil, err
+ }
+ nonFatalErr = err
+ }
+
+ // Determine where the results are: written to i, or returned in list
+ if t, _ := toSliceType(i); t == nil {
+ for _, v := range list {
+ if v, ok := v.(HasPostGet); ok {
+ err := v.PostGet(exec)
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+ } else {
+ resultsValue := reflect.Indirect(reflect.ValueOf(i))
+ for i := 0; i < resultsValue.Len(); i++ {
+ if v, ok := resultsValue.Index(i).Interface().(HasPostGet); ok {
+ err := v.PostGet(exec)
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+ }
+ return list, nonFatalErr
+}
+
+func rawselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
+ args ...interface{}) ([]interface{}, error) {
+ var (
+ appendToSlice = false // Write results to i directly?
+ intoStruct = true // Selecting into a struct?
+ pointerElements = true // Are the slice elements pointers (vs values)?
+ )
+
+ var nonFatalErr error
+
+ // get type for i, verifying it's a supported destination
+ t, err := toType(i)
+ if err != nil {
+ var err2 error
+ if t, err2 = toSliceType(i); t == nil {
+ if err2 != nil {
+ return nil, err2
+ }
+ return nil, err
+ }
+ pointerElements = t.Kind() == reflect.Ptr
+ if pointerElements {
+ t = t.Elem()
+ }
+ appendToSlice = true
+ intoStruct = t.Kind() == reflect.Struct
+ }
+
+ // If the caller supplied a single struct/map argument, assume a "named
+ // parameter" query. Extract the named arguments from the struct/map, create
+ // the flat arg slice, and rewrite the query to use the dialect's placeholder.
+ if len(args) == 1 {
+ query, args = maybeExpandNamedQuery(m, query, args)
+ }
+
+ // Run the query
+ rows, err := exec.query(query, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ // Fetch the column names as returned from db
+ cols, err := rows.Columns()
+ if err != nil {
+ return nil, err
+ }
+
+ if !intoStruct && len(cols) > 1 {
+ return nil, fmt.Errorf("gorp: select into non-struct slice requires 1 column, got %d", len(cols))
+ }
+
+ var colToFieldIndex [][]int
+ if intoStruct {
+ colToFieldIndex, err = columnToFieldIndex(m, t, cols)
+ if err != nil {
+ if !NonFatalError(err) {
+ return nil, err
+ }
+ nonFatalErr = err
+ }
+ }
+
+ conv := m.TypeConverter
+
+ // Add results to one of these two slices.
+ var (
+ list = make([]interface{}, 0)
+ sliceValue = reflect.Indirect(reflect.ValueOf(i))
+ )
+
+ for {
+ if !rows.Next() {
+ // if error occured return rawselect
+ if rows.Err() != nil {
+ return nil, rows.Err()
+ }
+ // time to exit from outer "for" loop
+ break
+ }
+ v := reflect.New(t)
+ dest := make([]interface{}, len(cols))
+
+ custScan := make([]CustomScanner, 0)
+
+ for x := range cols {
+ f := v.Elem()
+ if intoStruct {
+ index := colToFieldIndex[x]
+ if index == nil {
+ // this field is not present in the struct, so create a dummy
+ // value for rows.Scan to scan into
+ var dummy dummyField
+ dest[x] = &dummy
+ continue
+ }
+ f = f.FieldByIndex(index)
+ }
+ target := f.Addr().Interface()
+ if conv != nil {
+ scanner, ok := conv.FromDb(target)
+ if ok {
+ target = scanner.Holder
+ custScan = append(custScan, scanner)
+ }
+ }
+ dest[x] = target
+ }
+
+ err = rows.Scan(dest...)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, c := range custScan {
+ err = c.Bind()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if appendToSlice {
+ if !pointerElements {
+ v = v.Elem()
+ }
+ sliceValue.Set(reflect.Append(sliceValue, v))
+ } else {
+ list = append(list, v.Interface())
+ }
+ }
+
+ if appendToSlice && sliceValue.IsNil() {
+ sliceValue.Set(reflect.MakeSlice(sliceValue.Type(), 0, 0))
+ }
+
+ return list, nonFatalErr
+}
diff --git a/vendor/github.com/go-gorp/gorp/table.go b/vendor/github.com/go-gorp/gorp/table.go
new file mode 100644
index 000000000..5c513909a
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/table.go
@@ -0,0 +1,247 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// TableMap represents a mapping between a Go struct and a database table
+// Use dbmap.AddTable() or dbmap.AddTableWithName() to create these
+type TableMap struct {
+ // Name of database table.
+ TableName string
+ SchemaName string
+ gotype reflect.Type
+ Columns []*ColumnMap
+ keys []*ColumnMap
+ indexes []*IndexMap
+ uniqueTogether [][]string
+ version *ColumnMap
+ insertPlan bindPlan
+ updatePlan bindPlan
+ deletePlan bindPlan
+ getPlan bindPlan
+ dbmap *DbMap
+}
+
+// ResetSql removes cached insert/update/select/delete SQL strings
+// associated with this TableMap. Call this if you've modified
+// any column names or the table name itself.
+func (t *TableMap) ResetSql() {
+ t.insertPlan = bindPlan{}
+ t.updatePlan = bindPlan{}
+ t.deletePlan = bindPlan{}
+ t.getPlan = bindPlan{}
+}
+
+// SetKeys lets you specify the fields on a struct that map to primary
+// key columns on the table. If isAutoIncr is set, result.LastInsertId()
+// will be used after INSERT to bind the generated id to the Go struct.
+//
+// Automatically calls ResetSql() to ensure SQL statements are regenerated.
+//
+// Panics if isAutoIncr is true, and fieldNames length != 1
+//
+func (t *TableMap) SetKeys(isAutoIncr bool, fieldNames ...string) *TableMap {
+ if isAutoIncr && len(fieldNames) != 1 {
+ panic(fmt.Sprintf(
+ "gorp: SetKeys: fieldNames length must be 1 if key is auto-increment. (Saw %v fieldNames)",
+ len(fieldNames)))
+ }
+ t.keys = make([]*ColumnMap, 0)
+ for _, name := range fieldNames {
+ colmap := t.ColMap(name)
+ colmap.isPK = true
+ colmap.isAutoIncr = isAutoIncr
+ t.keys = append(t.keys, colmap)
+ }
+ t.ResetSql()
+
+ return t
+}
+
+// SetUniqueTogether lets you specify uniqueness constraints across multiple
+// columns on the table. Each call adds an additional constraint for the
+// specified columns.
+//
+// Automatically calls ResetSql() to ensure SQL statements are regenerated.
+//
+// Panics if fieldNames length < 2.
+//
+func (t *TableMap) SetUniqueTogether(fieldNames ...string) *TableMap {
+ if len(fieldNames) < 2 {
+ panic(fmt.Sprintf(
+ "gorp: SetUniqueTogether: must provide at least two fieldNames to set uniqueness constraint."))
+ }
+
+ columns := make([]string, 0)
+ for _, name := range fieldNames {
+ columns = append(columns, name)
+ }
+ t.uniqueTogether = append(t.uniqueTogether, columns)
+ t.ResetSql()
+
+ return t
+}
+
+// ColMap returns the ColumnMap pointer matching the given struct field
+// name. It panics if the struct does not contain a field matching this
+// name.
+func (t *TableMap) ColMap(field string) *ColumnMap {
+ col := colMapOrNil(t, field)
+ if col == nil {
+ e := fmt.Sprintf("No ColumnMap in table %s type %s with field %s",
+ t.TableName, t.gotype.Name(), field)
+
+ panic(e)
+ }
+ return col
+}
+
+func colMapOrNil(t *TableMap, field string) *ColumnMap {
+ for _, col := range t.Columns {
+ if col.fieldName == field || col.ColumnName == field {
+ return col
+ }
+ }
+ return nil
+}
+
+// IdxMap returns the IndexMap pointer matching the given index name.
+func (t *TableMap) IdxMap(field string) *IndexMap {
+ for _, idx := range t.indexes {
+ if idx.IndexName == field {
+ return idx
+ }
+ }
+ return nil
+}
+
+// AddIndex registers the index with gorp for specified table with given parameters.
+// This operation is idempotent. If index is already mapped, the
+// existing *IndexMap is returned
+// Function will panic if one of the given for index columns does not exists
+//
+// Automatically calls ResetSql() to ensure SQL statements are regenerated.
+//
+func (t *TableMap) AddIndex(name string, idxtype string, columns []string) *IndexMap {
+ // check if we have a index with this name already
+ for _, idx := range t.indexes {
+ if idx.IndexName == name {
+ return idx
+ }
+ }
+ for _, icol := range columns {
+ if res := t.ColMap(icol); res == nil {
+ e := fmt.Sprintf("No ColumnName in table %s to create index on", t.TableName)
+ panic(e)
+ }
+ }
+
+ idx := &IndexMap{IndexName: name, Unique: false, IndexType: idxtype, columns: columns}
+ t.indexes = append(t.indexes, idx)
+ t.ResetSql()
+ return idx
+}
+
+// SetVersionCol sets the column to use as the Version field. By default
+// the "Version" field is used. Returns the column found, or panics
+// if the struct does not contain a field matching this name.
+//
+// Automatically calls ResetSql() to ensure SQL statements are regenerated.
+func (t *TableMap) SetVersionCol(field string) *ColumnMap {
+ c := t.ColMap(field)
+ t.version = c
+ t.ResetSql()
+ return c
+}
+
+// SqlForCreateTable gets a sequence of SQL commands that will create
+// the specified table and any associated schema
+func (t *TableMap) SqlForCreate(ifNotExists bool) string {
+ s := bytes.Buffer{}
+ dialect := t.dbmap.Dialect
+
+ if strings.TrimSpace(t.SchemaName) != "" {
+ schemaCreate := "create schema"
+ if ifNotExists {
+ s.WriteString(dialect.IfSchemaNotExists(schemaCreate, t.SchemaName))
+ } else {
+ s.WriteString(schemaCreate)
+ }
+ s.WriteString(fmt.Sprintf(" %s;", t.SchemaName))
+ }
+
+ tableCreate := "create table"
+ if ifNotExists {
+ s.WriteString(dialect.IfTableNotExists(tableCreate, t.SchemaName, t.TableName))
+ } else {
+ s.WriteString(tableCreate)
+ }
+ s.WriteString(fmt.Sprintf(" %s (", dialect.QuotedTableForQuery(t.SchemaName, t.TableName)))
+
+ x := 0
+ for _, col := range t.Columns {
+ if !col.Transient {
+ if x > 0 {
+ s.WriteString(", ")
+ }
+ stype := dialect.ToSqlType(col.gotype, col.MaxSize, col.isAutoIncr)
+ s.WriteString(fmt.Sprintf("%s %s", dialect.QuoteField(col.ColumnName), stype))
+
+ if col.isPK || col.isNotNull {
+ s.WriteString(" not null")
+ }
+ if col.isPK && len(t.keys) == 1 {
+ s.WriteString(" primary key")
+ }
+ if col.Unique {
+ s.WriteString(" unique")
+ }
+ if col.isAutoIncr {
+ s.WriteString(fmt.Sprintf(" %s", dialect.AutoIncrStr()))
+ }
+
+ x++
+ }
+ }
+ if len(t.keys) > 1 {
+ s.WriteString(", primary key (")
+ for x := range t.keys {
+ if x > 0 {
+ s.WriteString(", ")
+ }
+ s.WriteString(dialect.QuoteField(t.keys[x].ColumnName))
+ }
+ s.WriteString(")")
+ }
+ if len(t.uniqueTogether) > 0 {
+ for _, columns := range t.uniqueTogether {
+ s.WriteString(", unique (")
+ for i, column := range columns {
+ if i > 0 {
+ s.WriteString(", ")
+ }
+ s.WriteString(dialect.QuoteField(column))
+ }
+ s.WriteString(")")
+ }
+ }
+ s.WriteString(") ")
+ s.WriteString(dialect.CreateTableSuffix())
+ s.WriteString(dialect.QuerySuffix())
+ return s.String()
+}
diff --git a/vendor/github.com/go-gorp/gorp/table_bindings.go b/vendor/github.com/go-gorp/gorp/table_bindings.go
new file mode 100644
index 000000000..5b049a360
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/table_bindings.go
@@ -0,0 +1,312 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+ "sync"
+)
+
+// CustomScanner binds a database column value to a Go type
+type CustomScanner struct {
+ // After a row is scanned, Holder will contain the value from the database column.
+ // Initialize the CustomScanner with the concrete Go type you wish the database
+ // driver to scan the raw column into.
+ Holder interface{}
+ // Target typically holds a pointer to the target struct field to bind the Holder
+ // value to.
+ Target interface{}
+ // Binder is a custom function that converts the holder value to the target type
+ // and sets target accordingly. This function should return error if a problem
+ // occurs converting the holder to the target.
+ Binder func(holder interface{}, target interface{}) error
+}
+
+// Used to filter columns when selectively updating
+type ColumnFilter func(*ColumnMap) bool
+
+func acceptAllFilter(col *ColumnMap) bool {
+ return true
+}
+
+// Bind is called automatically by gorp after Scan()
+func (me CustomScanner) Bind() error {
+ return me.Binder(me.Holder, me.Target)
+}
+
+type bindPlan struct {
+ query string
+ argFields []string
+ keyFields []string
+ versField string
+ autoIncrIdx int
+ autoIncrFieldName string
+ once sync.Once
+}
+
+func (plan *bindPlan) createBindInstance(elem reflect.Value, conv TypeConverter) (bindInstance, error) {
+ bi := bindInstance{query: plan.query, autoIncrIdx: plan.autoIncrIdx, autoIncrFieldName: plan.autoIncrFieldName, versField: plan.versField}
+ if plan.versField != "" {
+ bi.existingVersion = elem.FieldByName(plan.versField).Int()
+ }
+
+ var err error
+
+ for i := 0; i < len(plan.argFields); i++ {
+ k := plan.argFields[i]
+ if k == versFieldConst {
+ newVer := bi.existingVersion + 1
+ bi.args = append(bi.args, newVer)
+ if bi.existingVersion == 0 {
+ elem.FieldByName(plan.versField).SetInt(int64(newVer))
+ }
+ } else {
+ val := elem.FieldByName(k).Interface()
+ if conv != nil {
+ val, err = conv.ToDb(val)
+ if err != nil {
+ return bindInstance{}, err
+ }
+ }
+ bi.args = append(bi.args, val)
+ }
+ }
+
+ for i := 0; i < len(plan.keyFields); i++ {
+ k := plan.keyFields[i]
+ val := elem.FieldByName(k).Interface()
+ if conv != nil {
+ val, err = conv.ToDb(val)
+ if err != nil {
+ return bindInstance{}, err
+ }
+ }
+ bi.keys = append(bi.keys, val)
+ }
+
+ return bi, nil
+}
+
+type bindInstance struct {
+ query string
+ args []interface{}
+ keys []interface{}
+ existingVersion int64
+ versField string
+ autoIncrIdx int
+ autoIncrFieldName string
+}
+
+func (t *TableMap) bindInsert(elem reflect.Value) (bindInstance, error) {
+ plan := &t.insertPlan
+ plan.once.Do(func() {
+ plan.autoIncrIdx = -1
+
+ s := bytes.Buffer{}
+ s2 := bytes.Buffer{}
+ s.WriteString(fmt.Sprintf("insert into %s (", t.dbmap.Dialect.QuotedTableForQuery(t.SchemaName, t.TableName)))
+
+ x := 0
+ first := true
+ for y := range t.Columns {
+ col := t.Columns[y]
+ if !(col.isAutoIncr && t.dbmap.Dialect.AutoIncrBindValue() == "") {
+ if !col.Transient {
+ if !first {
+ s.WriteString(",")
+ s2.WriteString(",")
+ }
+ s.WriteString(t.dbmap.Dialect.QuoteField(col.ColumnName))
+
+ if col.isAutoIncr {
+ s2.WriteString(t.dbmap.Dialect.AutoIncrBindValue())
+ plan.autoIncrIdx = y
+ plan.autoIncrFieldName = col.fieldName
+ } else {
+ if col.DefaultValue == "" {
+ s2.WriteString(t.dbmap.Dialect.BindVar(x))
+ if col == t.version {
+ plan.versField = col.fieldName
+ plan.argFields = append(plan.argFields, versFieldConst)
+ } else {
+ plan.argFields = append(plan.argFields, col.fieldName)
+ }
+ x++
+ } else {
+ s2.WriteString(col.DefaultValue)
+ }
+ }
+ first = false
+ }
+ } else {
+ plan.autoIncrIdx = y
+ plan.autoIncrFieldName = col.fieldName
+ }
+ }
+ s.WriteString(") values (")
+ s.WriteString(s2.String())
+ s.WriteString(")")
+ if plan.autoIncrIdx > -1 {
+ s.WriteString(t.dbmap.Dialect.AutoIncrInsertSuffix(t.Columns[plan.autoIncrIdx]))
+ }
+ s.WriteString(t.dbmap.Dialect.QuerySuffix())
+
+ plan.query = s.String()
+ })
+
+ return plan.createBindInstance(elem, t.dbmap.TypeConverter)
+}
+
+func (t *TableMap) bindUpdate(elem reflect.Value, colFilter ColumnFilter) (bindInstance, error) {
+ if colFilter == nil {
+ colFilter = acceptAllFilter
+ }
+
+ plan := &t.updatePlan
+ plan.once.Do(func() {
+ s := bytes.Buffer{}
+ s.WriteString(fmt.Sprintf("update %s set ", t.dbmap.Dialect.QuotedTableForQuery(t.SchemaName, t.TableName)))
+ x := 0
+
+ for y := range t.Columns {
+ col := t.Columns[y]
+ if !col.isAutoIncr && !col.Transient && colFilter(col) {
+ if x > 0 {
+ s.WriteString(", ")
+ }
+ s.WriteString(t.dbmap.Dialect.QuoteField(col.ColumnName))
+ s.WriteString("=")
+ s.WriteString(t.dbmap.Dialect.BindVar(x))
+
+ if col == t.version {
+ plan.versField = col.fieldName
+ plan.argFields = append(plan.argFields, versFieldConst)
+ } else {
+ plan.argFields = append(plan.argFields, col.fieldName)
+ }
+ x++
+ }
+ }
+
+ s.WriteString(" where ")
+ for y := range t.keys {
+ col := t.keys[y]
+ if y > 0 {
+ s.WriteString(" and ")
+ }
+ s.WriteString(t.dbmap.Dialect.QuoteField(col.ColumnName))
+ s.WriteString("=")
+ s.WriteString(t.dbmap.Dialect.BindVar(x))
+
+ plan.argFields = append(plan.argFields, col.fieldName)
+ plan.keyFields = append(plan.keyFields, col.fieldName)
+ x++
+ }
+ if plan.versField != "" {
+ s.WriteString(" and ")
+ s.WriteString(t.dbmap.Dialect.QuoteField(t.version.ColumnName))
+ s.WriteString("=")
+ s.WriteString(t.dbmap.Dialect.BindVar(x))
+ plan.argFields = append(plan.argFields, plan.versField)
+ }
+ s.WriteString(t.dbmap.Dialect.QuerySuffix())
+
+ plan.query = s.String()
+ })
+
+ return plan.createBindInstance(elem, t.dbmap.TypeConverter)
+}
+
+func (t *TableMap) bindDelete(elem reflect.Value) (bindInstance, error) {
+ plan := &t.deletePlan
+ plan.once.Do(func() {
+ s := bytes.Buffer{}
+ s.WriteString(fmt.Sprintf("delete from %s", t.dbmap.Dialect.QuotedTableForQuery(t.SchemaName, t.TableName)))
+
+ for y := range t.Columns {
+ col := t.Columns[y]
+ if !col.Transient {
+ if col == t.version {
+ plan.versField = col.fieldName
+ }
+ }
+ }
+
+ s.WriteString(" where ")
+ for x := range t.keys {
+ k := t.keys[x]
+ if x > 0 {
+ s.WriteString(" and ")
+ }
+ s.WriteString(t.dbmap.Dialect.QuoteField(k.ColumnName))
+ s.WriteString("=")
+ s.WriteString(t.dbmap.Dialect.BindVar(x))
+
+ plan.keyFields = append(plan.keyFields, k.fieldName)
+ plan.argFields = append(plan.argFields, k.fieldName)
+ }
+ if plan.versField != "" {
+ s.WriteString(" and ")
+ s.WriteString(t.dbmap.Dialect.QuoteField(t.version.ColumnName))
+ s.WriteString("=")
+ s.WriteString(t.dbmap.Dialect.BindVar(len(plan.argFields)))
+
+ plan.argFields = append(plan.argFields, plan.versField)
+ }
+ s.WriteString(t.dbmap.Dialect.QuerySuffix())
+
+ plan.query = s.String()
+ })
+
+ return plan.createBindInstance(elem, t.dbmap.TypeConverter)
+}
+
+func (t *TableMap) bindGet() *bindPlan {
+ plan := &t.getPlan
+ plan.once.Do(func() {
+ s := bytes.Buffer{}
+ s.WriteString("select ")
+
+ x := 0
+ for _, col := range t.Columns {
+ if !col.Transient {
+ if x > 0 {
+ s.WriteString(",")
+ }
+ s.WriteString(t.dbmap.Dialect.QuoteField(col.ColumnName))
+ plan.argFields = append(plan.argFields, col.fieldName)
+ x++
+ }
+ }
+ s.WriteString(" from ")
+ s.WriteString(t.dbmap.Dialect.QuotedTableForQuery(t.SchemaName, t.TableName))
+ s.WriteString(" where ")
+ for x := range t.keys {
+ col := t.keys[x]
+ if x > 0 {
+ s.WriteString(" and ")
+ }
+ s.WriteString(t.dbmap.Dialect.QuoteField(col.ColumnName))
+ s.WriteString("=")
+ s.WriteString(t.dbmap.Dialect.BindVar(x))
+
+ plan.keyFields = append(plan.keyFields, col.fieldName)
+ }
+ s.WriteString(t.dbmap.Dialect.QuerySuffix())
+
+ plan.query = s.String()
+ })
+
+ return plan
+}
diff --git a/vendor/github.com/go-gorp/gorp/test_all.sh b/vendor/github.com/go-gorp/gorp/test_all.sh
new file mode 100644
index 000000000..4c99584ef
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/test_all.sh
@@ -0,0 +1,41 @@
+#!/bin/bash -e
+
+# on macs, you may need to:
+# export GOBUILDFLAG=-ldflags -linkmode=external
+
+coveralls_testflags="-v -covermode=count -coverprofile=coverage.out"
+
+echo "Running unit tests"
+ginkgo -r -race -randomizeAllSpecs -keepGoing -- -test.run TestGorp
+
+echo "Testing against mysql"
+export GORP_TEST_DSN=gorptest/gorptest/gorptest
+export GORP_TEST_DIALECT=mysql
+go test $coveralls_testflags $GOBUILDFLAG $@ .
+
+echo "Testing against gomysql"
+export GORP_TEST_DSN=gorptest:gorptest@/gorptest
+export GORP_TEST_DIALECT=gomysql
+go test $coveralls_testflags $GOBUILDFLAG $@ .
+
+echo "Testing against postgres"
+export GORP_TEST_DSN="user=gorptest password=gorptest dbname=gorptest sslmode=disable"
+export GORP_TEST_DIALECT=postgres
+go test $coveralls_testflags $GOBUILDFLAG $@ .
+
+echo "Testing against sqlite"
+export GORP_TEST_DSN=/tmp/gorptest.bin
+export GORP_TEST_DIALECT=sqlite
+go test $coveralls_testflags $GOBUILDFLAG $@ .
+rm -f /tmp/gorptest.bin
+
+case $(go version) in
+ *go1.4*)
+ if [ "$(type -p goveralls)" != "" ]; then
+ goveralls -covermode=count -coverprofile=coverage.out -service=travis-ci
+ elif [ -x $HOME/gopath/bin/goveralls ]; then
+ $HOME/gopath/bin/goveralls -covermode=count -coverprofile=coverage.out -service=travis-ci
+ fi
+ ;;
+ *) ;;
+esac
diff --git a/vendor/github.com/go-gorp/gorp/transaction.go b/vendor/github.com/go-gorp/gorp/transaction.go
new file mode 100644
index 000000000..6430f24f1
--- /dev/null
+++ b/vendor/github.com/go-gorp/gorp/transaction.go
@@ -0,0 +1,193 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "database/sql"
+ "time"
+)
+
+// Transaction represents a database transaction.
+// Insert/Update/Delete/Get/Exec operations will be run in the context
+// of that transaction. Transactions should be terminated with
+// a call to Commit() or Rollback()
+type Transaction struct {
+ dbmap *DbMap
+ tx *sql.Tx
+ closed bool
+}
+
+// Insert has the same behavior as DbMap.Insert(), but runs in a transaction.
+func (t *Transaction) Insert(list ...interface{}) error {
+ return insert(t.dbmap, t, list...)
+}
+
+// Update had the same behavior as DbMap.Update(), but runs in a transaction.
+func (t *Transaction) Update(list ...interface{}) (int64, error) {
+ return update(t.dbmap, t, nil, list...)
+}
+
+// UpdateColumns had the same behavior as DbMap.UpdateColumns(), but runs in a transaction.
+func (t *Transaction) UpdateColumns(filter ColumnFilter, list ...interface{}) (int64, error) {
+ return update(t.dbmap, t, filter, list...)
+}
+
+// Delete has the same behavior as DbMap.Delete(), but runs in a transaction.
+func (t *Transaction) Delete(list ...interface{}) (int64, error) {
+ return delete(t.dbmap, t, list...)
+}
+
+// Get has the same behavior as DbMap.Get(), but runs in a transaction.
+func (t *Transaction) Get(i interface{}, keys ...interface{}) (interface{}, error) {
+ return get(t.dbmap, t, i, keys...)
+}
+
+// Select has the same behavior as DbMap.Select(), but runs in a transaction.
+func (t *Transaction) Select(i interface{}, query string, args ...interface{}) ([]interface{}, error) {
+ return hookedselect(t.dbmap, t, i, query, args...)
+}
+
+// Exec has the same behavior as DbMap.Exec(), but runs in a transaction.
+func (t *Transaction) Exec(query string, args ...interface{}) (sql.Result, error) {
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, args...)
+ }
+ return exec(t, query, args...)
+}
+
+// SelectInt is a convenience wrapper around the gorp.SelectInt function.
+func (t *Transaction) SelectInt(query string, args ...interface{}) (int64, error) {
+ return SelectInt(t, query, args...)
+}
+
+// SelectNullInt is a convenience wrapper around the gorp.SelectNullInt function.
+func (t *Transaction) SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error) {
+ return SelectNullInt(t, query, args...)
+}
+
+// SelectFloat is a convenience wrapper around the gorp.SelectFloat function.
+func (t *Transaction) SelectFloat(query string, args ...interface{}) (float64, error) {
+ return SelectFloat(t, query, args...)
+}
+
+// SelectNullFloat is a convenience wrapper around the gorp.SelectNullFloat function.
+func (t *Transaction) SelectNullFloat(query string, args ...interface{}) (sql.NullFloat64, error) {
+ return SelectNullFloat(t, query, args...)
+}
+
+// SelectStr is a convenience wrapper around the gorp.SelectStr function.
+func (t *Transaction) SelectStr(query string, args ...interface{}) (string, error) {
+ return SelectStr(t, query, args...)
+}
+
+// SelectNullStr is a convenience wrapper around the gorp.SelectNullStr function.
+func (t *Transaction) SelectNullStr(query string, args ...interface{}) (sql.NullString, error) {
+ return SelectNullStr(t, query, args...)
+}
+
+// SelectOne is a convenience wrapper around the gorp.SelectOne function.
+func (t *Transaction) SelectOne(holder interface{}, query string, args ...interface{}) error {
+ return SelectOne(t.dbmap, t, holder, query, args...)
+}
+
+// Commit commits the underlying database transaction.
+func (t *Transaction) Commit() error {
+ if !t.closed {
+ t.closed = true
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, "commit;")
+ }
+ return t.tx.Commit()
+ }
+
+ return sql.ErrTxDone
+}
+
+// Rollback rolls back the underlying database transaction.
+func (t *Transaction) Rollback() error {
+ if !t.closed {
+ t.closed = true
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, "rollback;")
+ }
+ return t.tx.Rollback()
+ }
+
+ return sql.ErrTxDone
+}
+
+// Savepoint creates a savepoint with the given name. The name is interpolated
+// directly into the SQL SAVEPOINT statement, so you must sanitize it if it is
+// derived from user input.
+func (t *Transaction) Savepoint(name string) error {
+ query := "savepoint " + t.dbmap.Dialect.QuoteField(name)
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, nil)
+ }
+ _, err := t.tx.Exec(query)
+ return err
+}
+
+// RollbackToSavepoint rolls back to the savepoint with the given name. The
+// name is interpolated directly into the SQL SAVEPOINT statement, so you must
+// sanitize it if it is derived from user input.
+func (t *Transaction) RollbackToSavepoint(savepoint string) error {
+ query := "rollback to savepoint " + t.dbmap.Dialect.QuoteField(savepoint)
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, nil)
+ }
+ _, err := t.tx.Exec(query)
+ return err
+}
+
+// ReleaseSavepint releases the savepoint with the given name. The name is
+// interpolated directly into the SQL SAVEPOINT statement, so you must sanitize
+// it if it is derived from user input.
+func (t *Transaction) ReleaseSavepoint(savepoint string) error {
+ query := "release savepoint " + t.dbmap.Dialect.QuoteField(savepoint)
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, nil)
+ }
+ _, err := t.tx.Exec(query)
+ return err
+}
+
+// Prepare has the same behavior as DbMap.Prepare(), but runs in a transaction.
+func (t *Transaction) Prepare(query string) (*sql.Stmt, error) {
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, nil)
+ }
+ return t.tx.Prepare(query)
+}
+
+func (t *Transaction) queryRow(query string, args ...interface{}) *sql.Row {
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, args...)
+ }
+ return t.tx.QueryRow(query, args...)
+}
+
+func (t *Transaction) query(query string, args ...interface{}) (*sql.Rows, error) {
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, args...)
+ }
+ return t.tx.Query(query, args...)
+}
diff --git a/vendor/github.com/go-ldap/ldap/.gitignore b/vendor/github.com/go-ldap/ldap/.gitignore
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/.gitignore
diff --git a/vendor/github.com/go-ldap/ldap/.travis.yml b/vendor/github.com/go-ldap/ldap/.travis.yml
new file mode 100644
index 000000000..a7a38951b
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/.travis.yml
@@ -0,0 +1,15 @@
+language: go
+go:
+ - 1.2
+ - 1.3
+ - 1.4
+ - 1.5
+ - tip
+go_import_path: gopkg.in/ldap.v2
+install:
+ - go get gopkg.in/asn1-ber.v1
+ - go get gopkg.in/ldap.v2
+ - go get code.google.com/p/go.tools/cmd/cover || go get golang.org/x/tools/cmd/cover
+ - go build -v ./...
+script:
+ - go test -v -cover ./...
diff --git a/vendor/github.com/go-ldap/ldap/LICENSE b/vendor/github.com/go-ldap/ldap/LICENSE
new file mode 100644
index 000000000..744875676
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/go-ldap/ldap/README.md b/vendor/github.com/go-ldap/ldap/README.md
new file mode 100644
index 000000000..f49b4d6a1
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/README.md
@@ -0,0 +1,55 @@
+[![GoDoc](https://godoc.org/gopkg.in/ldap.v2?status.svg)](https://godoc.org/gopkg.in/ldap.v2)
+[![Build Status](https://travis-ci.org/go-ldap/ldap.svg)](https://travis-ci.org/go-ldap/ldap)
+
+# Basic LDAP v3 functionality for the GO programming language.
+
+## Install
+
+For the latest version use:
+
+ go get gopkg.in/ldap.v2
+
+Import the latest version with:
+
+ import "gopkg.in/ldap.v2"
+
+
+## Required Libraries:
+
+ - gopkg.in/asn1-ber.v1
+
+## Working:
+
+ - Connecting to LDAP server
+ - Binding to LDAP server
+ - Searching for entries
+ - Compiling string filters to LDAP filters
+ - Paging Search Results
+ - Modify Requests / Responses
+ - Add Requests / Responses
+ - Delete Requests / Responses
+ - Better Unicode support
+
+## Examples:
+
+ - search
+ - modify
+
+## Tests Implemented:
+
+ - Filter Compile / Decompile
+
+## TODO:
+
+ - [x] Add Requests / Responses
+ - [x] Delete Requests / Responses
+ - [x] Modify DN Requests / Responses
+ - [ ] Compare Requests / Responses
+ - [ ] Implement Tests / Benchmarks
+
+
+
+---
+The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/)
+The design is licensed under the Creative Commons 3.0 Attributions license.
+Read this article for more details: http://blog.golang.org/gopher
diff --git a/vendor/github.com/go-ldap/ldap/add.go b/vendor/github.com/go-ldap/ldap/add.go
new file mode 100644
index 000000000..61b795e0e
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/add.go
@@ -0,0 +1,108 @@
+//
+// https://tools.ietf.org/html/rfc4511
+//
+// AddRequest ::= [APPLICATION 8] SEQUENCE {
+// entry LDAPDN,
+// attributes AttributeList }
+//
+// AttributeList ::= SEQUENCE OF attribute Attribute
+
+package ldap
+
+import (
+ "errors"
+ "log"
+
+ "gopkg.in/asn1-ber.v1"
+)
+
+type Attribute struct {
+ attrType string
+ attrVals []string
+}
+
+func (a *Attribute) encode() *ber.Packet {
+ seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attribute")
+ seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.attrType, "Type"))
+ set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
+ for _, value := range a.attrVals {
+ set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
+ }
+ seq.AppendChild(set)
+ return seq
+}
+
+type AddRequest struct {
+ dn string
+ attributes []Attribute
+}
+
+func (a AddRequest) encode() *ber.Packet {
+ request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request")
+ request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.dn, "DN"))
+ attributes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
+ for _, attribute := range a.attributes {
+ attributes.AppendChild(attribute.encode())
+ }
+ request.AppendChild(attributes)
+ return request
+}
+
+func (a *AddRequest) Attribute(attrType string, attrVals []string) {
+ a.attributes = append(a.attributes, Attribute{attrType: attrType, attrVals: attrVals})
+}
+
+func NewAddRequest(dn string) *AddRequest {
+ return &AddRequest{
+ dn: dn,
+ }
+
+}
+
+func (l *Conn) Add(addRequest *AddRequest) error {
+ messageID := l.nextMessageID()
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ packet.AppendChild(addRequest.encode())
+
+ l.Debug.PrintPacket(packet)
+
+ channel, err := l.sendMessage(packet)
+ if err != nil {
+ return err
+ }
+ if channel == nil {
+ return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
+ }
+ defer l.finishMessage(messageID)
+
+ l.Debug.Printf("%d: waiting for response", messageID)
+ packetResponse, ok := <-channel
+ if !ok {
+ return NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ }
+ packet, err = packetResponse.ReadPacket()
+ l.Debug.Printf("%d: got response %p", messageID, packet)
+ if err != nil {
+ return err
+ }
+
+ if l.Debug {
+ if err := addLDAPDescriptions(packet); err != nil {
+ return err
+ }
+ ber.PrintPacket(packet)
+ }
+
+ if packet.Children[1].Tag == ApplicationAddResponse {
+ resultCode, resultDescription := getLDAPResultCode(packet)
+ if resultCode != 0 {
+ return NewError(resultCode, errors.New(resultDescription))
+ }
+ } else {
+ log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
+ }
+
+ l.Debug.Printf("%d: returning", messageID)
+ return nil
+}
diff --git a/vendor/github.com/go-ldap/ldap/bind.go b/vendor/github.com/go-ldap/ldap/bind.go
new file mode 100644
index 000000000..ae68eb481
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/bind.go
@@ -0,0 +1,145 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ldap
+
+import (
+ "errors"
+
+ "gopkg.in/asn1-ber.v1"
+)
+
+type SimpleBindRequest struct {
+ Username string
+ Password string
+ Controls []Control
+}
+
+type SimpleBindResult struct {
+ Controls []Control
+}
+
+func NewSimpleBindRequest(username string, password string, controls []Control) *SimpleBindRequest {
+ return &SimpleBindRequest{
+ Username: username,
+ Password: password,
+ Controls: controls,
+ }
+}
+
+func (bindRequest *SimpleBindRequest) encode() *ber.Packet {
+ request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
+ request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
+ request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, bindRequest.Username, "User Name"))
+ request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, bindRequest.Password, "Password"))
+
+ request.AppendChild(encodeControls(bindRequest.Controls))
+
+ return request
+}
+
+func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) {
+ messageID := l.nextMessageID()
+
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ encodedBindRequest := simpleBindRequest.encode()
+ packet.AppendChild(encodedBindRequest)
+
+ if l.Debug {
+ ber.PrintPacket(packet)
+ }
+
+ channel, err := l.sendMessage(packet)
+ if err != nil {
+ return nil, err
+ }
+ if channel == nil {
+ return nil, NewError(ErrorNetwork, errors.New("ldap: could not send message"))
+ }
+ defer l.finishMessage(messageID)
+
+ packetResponse, ok := <-channel
+ if !ok {
+ return nil, NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ }
+ packet, err = packetResponse.ReadPacket()
+ l.Debug.Printf("%d: got response %p", messageID, packet)
+ if err != nil {
+ return nil, err
+ }
+
+ if l.Debug {
+ if err := addLDAPDescriptions(packet); err != nil {
+ return nil, err
+ }
+ ber.PrintPacket(packet)
+ }
+
+ result := &SimpleBindResult{
+ Controls: make([]Control, 0),
+ }
+
+ if len(packet.Children) == 3 {
+ for _, child := range packet.Children[2].Children {
+ result.Controls = append(result.Controls, DecodeControl(child))
+ }
+ }
+
+ resultCode, resultDescription := getLDAPResultCode(packet)
+ if resultCode != 0 {
+ return result, NewError(resultCode, errors.New(resultDescription))
+ }
+
+ return result, nil
+}
+
+func (l *Conn) Bind(username, password string) error {
+ messageID := l.nextMessageID()
+
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ bindRequest := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
+ bindRequest.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
+ bindRequest.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, username, "User Name"))
+ bindRequest.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, password, "Password"))
+ packet.AppendChild(bindRequest)
+
+ if l.Debug {
+ ber.PrintPacket(packet)
+ }
+
+ channel, err := l.sendMessage(packet)
+ if err != nil {
+ return err
+ }
+ if channel == nil {
+ return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
+ }
+ defer l.finishMessage(messageID)
+
+ packetResponse, ok := <-channel
+ if !ok {
+ return NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ }
+ packet, err = packetResponse.ReadPacket()
+ l.Debug.Printf("%d: got response %p", messageID, packet)
+ if err != nil {
+ return err
+ }
+
+ if l.Debug {
+ if err := addLDAPDescriptions(packet); err != nil {
+ return err
+ }
+ ber.PrintPacket(packet)
+ }
+
+ resultCode, resultDescription := getLDAPResultCode(packet)
+ if resultCode != 0 {
+ return NewError(resultCode, errors.New(resultDescription))
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/go-ldap/ldap/client.go b/vendor/github.com/go-ldap/ldap/client.go
new file mode 100644
index 000000000..055b27b5f
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/client.go
@@ -0,0 +1,27 @@
+package ldap
+
+import (
+ "crypto/tls"
+ "time"
+)
+
+// Client knows how to interact with an LDAP server
+type Client interface {
+ Start()
+ StartTLS(config *tls.Config) error
+ Close()
+ SetTimeout(time.Duration)
+
+ Bind(username, password string) error
+ SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error)
+
+ Add(addRequest *AddRequest) error
+ Del(delRequest *DelRequest) error
+ Modify(modifyRequest *ModifyRequest) error
+
+ Compare(dn, attribute, value string) (bool, error)
+ PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error)
+
+ Search(searchRequest *SearchRequest) (*SearchResult, error)
+ SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error)
+}
diff --git a/vendor/github.com/go-ldap/ldap/compare.go b/vendor/github.com/go-ldap/ldap/compare.go
new file mode 100644
index 000000000..dfe728bad
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/compare.go
@@ -0,0 +1,89 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//
+// File contains Compare functionality
+//
+// https://tools.ietf.org/html/rfc4511
+//
+// CompareRequest ::= [APPLICATION 14] SEQUENCE {
+// entry LDAPDN,
+// ava AttributeValueAssertion }
+//
+// AttributeValueAssertion ::= SEQUENCE {
+// attributeDesc AttributeDescription,
+// assertionValue AssertionValue }
+//
+// AttributeDescription ::= LDAPString
+// -- Constrained to <attributedescription>
+// -- [RFC4512]
+//
+// AttributeValue ::= OCTET STRING
+//
+
+package ldap
+
+import (
+ "errors"
+ "fmt"
+
+ "gopkg.in/asn1-ber.v1"
+)
+
+// Compare checks to see if the attribute of the dn matches value. Returns true if it does otherwise
+// false with any error that occurs if any.
+func (l *Conn) Compare(dn, attribute, value string) (bool, error) {
+ messageID := l.nextMessageID()
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+
+ request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationCompareRequest, nil, "Compare Request")
+ request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, dn, "DN"))
+
+ ava := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "AttributeValueAssertion")
+ ava.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "AttributeDesc"))
+ ava.AppendChild(ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagOctetString, value, "AssertionValue"))
+ request.AppendChild(ava)
+ packet.AppendChild(request)
+
+ l.Debug.PrintPacket(packet)
+
+ channel, err := l.sendMessage(packet)
+ if err != nil {
+ return false, err
+ }
+ if channel == nil {
+ return false, NewError(ErrorNetwork, errors.New("ldap: could not send message"))
+ }
+ defer l.finishMessage(messageID)
+
+ l.Debug.Printf("%d: waiting for response", messageID)
+ packetResponse, ok := <-channel
+ if !ok {
+ return false, NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ }
+ packet, err = packetResponse.ReadPacket()
+ l.Debug.Printf("%d: got response %p", messageID, packet)
+ if err != nil {
+ return false, err
+ }
+
+ if l.Debug {
+ if err := addLDAPDescriptions(packet); err != nil {
+ return false, err
+ }
+ ber.PrintPacket(packet)
+ }
+
+ if packet.Children[1].Tag == ApplicationCompareResponse {
+ resultCode, resultDescription := getLDAPResultCode(packet)
+ if resultCode == LDAPResultCompareTrue {
+ return true, nil
+ } else if resultCode == LDAPResultCompareFalse {
+ return false, nil
+ } else {
+ return false, NewError(resultCode, errors.New(resultDescription))
+ }
+ }
+ return false, fmt.Errorf("Unexpected Response: %d", packet.Children[1].Tag)
+}
diff --git a/vendor/github.com/go-ldap/ldap/conn.go b/vendor/github.com/go-ldap/ldap/conn.go
new file mode 100644
index 000000000..6aad628be
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/conn.go
@@ -0,0 +1,424 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ldap
+
+import (
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "log"
+ "net"
+ "sync"
+ "time"
+
+ "gopkg.in/asn1-ber.v1"
+)
+
+const (
+ MessageQuit = 0
+ MessageRequest = 1
+ MessageResponse = 2
+ MessageFinish = 3
+ MessageTimeout = 4
+)
+
+type PacketResponse struct {
+ Packet *ber.Packet
+ Error error
+}
+
+func (pr *PacketResponse) ReadPacket() (*ber.Packet, error) {
+ if (pr == nil) || (pr.Packet == nil && pr.Error == nil) {
+ return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve response"))
+ }
+ return pr.Packet, pr.Error
+}
+
+type messagePacket struct {
+ Op int
+ MessageID int64
+ Packet *ber.Packet
+ Channel chan *PacketResponse
+}
+
+type sendMessageFlags uint
+
+const (
+ startTLS sendMessageFlags = 1 << iota
+)
+
+// Conn represents an LDAP Connection
+type Conn struct {
+ conn net.Conn
+ isTLS bool
+ isClosing bool
+ isStartingTLS bool
+ Debug debugging
+ chanConfirm chan bool
+ chanResults map[int64]chan *PacketResponse
+ chanMessage chan *messagePacket
+ chanMessageID chan int64
+ wgSender sync.WaitGroup
+ wgClose sync.WaitGroup
+ once sync.Once
+ outstandingRequests uint
+ messageMutex sync.Mutex
+ requestTimeout time.Duration
+}
+
+var _ Client = &Conn{}
+
+// DefaultTimeout is a package-level variable that sets the timeout value
+// used for the Dial and DialTLS methods.
+//
+// WARNING: since this is a package-level variable, setting this value from
+// multiple places will probably result in undesired behaviour.
+var DefaultTimeout = 60 * time.Second
+
+// Dial connects to the given address on the given network using net.Dial
+// and then returns a new Conn for the connection.
+func Dial(network, addr string) (*Conn, error) {
+ c, err := net.DialTimeout(network, addr, DefaultTimeout)
+ if err != nil {
+ return nil, NewError(ErrorNetwork, err)
+ }
+ conn := NewConn(c, false)
+ conn.Start()
+ return conn, nil
+}
+
+// DialTLS connects to the given address on the given network using tls.Dial
+// and then returns a new Conn for the connection.
+func DialTLS(network, addr string, config *tls.Config) (*Conn, error) {
+ dc, err := net.DialTimeout(network, addr, DefaultTimeout)
+ if err != nil {
+ return nil, NewError(ErrorNetwork, err)
+ }
+ c := tls.Client(dc, config)
+ err = c.Handshake()
+ if err != nil {
+ // Handshake error, close the established connection before we return an error
+ dc.Close()
+ return nil, NewError(ErrorNetwork, err)
+ }
+ conn := NewConn(c, true)
+ conn.Start()
+ return conn, nil
+}
+
+// NewConn returns a new Conn using conn for network I/O.
+func NewConn(conn net.Conn, isTLS bool) *Conn {
+ return &Conn{
+ conn: conn,
+ chanConfirm: make(chan bool),
+ chanMessageID: make(chan int64),
+ chanMessage: make(chan *messagePacket, 10),
+ chanResults: map[int64]chan *PacketResponse{},
+ requestTimeout: 0,
+ isTLS: isTLS,
+ }
+}
+
+func (l *Conn) Start() {
+ go l.reader()
+ go l.processMessages()
+ l.wgClose.Add(1)
+}
+
+// Close closes the connection.
+func (l *Conn) Close() {
+ l.once.Do(func() {
+ l.isClosing = true
+ l.wgSender.Wait()
+
+ l.Debug.Printf("Sending quit message and waiting for confirmation")
+ l.chanMessage <- &messagePacket{Op: MessageQuit}
+ <-l.chanConfirm
+ close(l.chanMessage)
+
+ l.Debug.Printf("Closing network connection")
+ if err := l.conn.Close(); err != nil {
+ log.Print(err)
+ }
+
+ l.wgClose.Done()
+ })
+ l.wgClose.Wait()
+}
+
+// Sets the time after a request is sent that a MessageTimeout triggers
+func (l *Conn) SetTimeout(timeout time.Duration) {
+ if timeout > 0 {
+ l.requestTimeout = timeout
+ }
+}
+
+// Returns the next available messageID
+func (l *Conn) nextMessageID() int64 {
+ if l.chanMessageID != nil {
+ if messageID, ok := <-l.chanMessageID; ok {
+ return messageID
+ }
+ }
+ return 0
+}
+
+// StartTLS sends the command to start a TLS session and then creates a new TLS Client
+func (l *Conn) StartTLS(config *tls.Config) error {
+ messageID := l.nextMessageID()
+
+ if l.isTLS {
+ return NewError(ErrorNetwork, errors.New("ldap: already encrypted"))
+ }
+
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Start TLS")
+ request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, "1.3.6.1.4.1.1466.20037", "TLS Extended Command"))
+ packet.AppendChild(request)
+ l.Debug.PrintPacket(packet)
+
+ channel, err := l.sendMessageWithFlags(packet, startTLS)
+ if err != nil {
+ return err
+ }
+ if channel == nil {
+ return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
+ }
+
+ l.Debug.Printf("%d: waiting for response", messageID)
+ defer l.finishMessage(messageID)
+ packetResponse, ok := <-channel
+ if !ok {
+ return NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ }
+ packet, err = packetResponse.ReadPacket()
+ l.Debug.Printf("%d: got response %p", messageID, packet)
+ if err != nil {
+ return err
+ }
+
+ if l.Debug {
+ if err := addLDAPDescriptions(packet); err != nil {
+ l.Close()
+ return err
+ }
+ ber.PrintPacket(packet)
+ }
+
+ if resultCode, message := getLDAPResultCode(packet); resultCode == LDAPResultSuccess {
+ conn := tls.Client(l.conn, config)
+
+ if err := conn.Handshake(); err != nil {
+ l.Close()
+ return NewError(ErrorNetwork, fmt.Errorf("TLS handshake failed (%v)", err))
+ }
+
+ l.isTLS = true
+ l.conn = conn
+ } else {
+ return NewError(resultCode, fmt.Errorf("ldap: cannot StartTLS (%s)", message))
+ }
+ go l.reader()
+
+ return nil
+}
+
+func (l *Conn) sendMessage(packet *ber.Packet) (chan *PacketResponse, error) {
+ return l.sendMessageWithFlags(packet, 0)
+}
+
+func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) (chan *PacketResponse, error) {
+ if l.isClosing {
+ return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed"))
+ }
+ l.messageMutex.Lock()
+ l.Debug.Printf("flags&startTLS = %d", flags&startTLS)
+ if l.isStartingTLS {
+ l.messageMutex.Unlock()
+ return nil, NewError(ErrorNetwork, errors.New("ldap: connection is in startls phase."))
+ }
+ if flags&startTLS != 0 {
+ if l.outstandingRequests != 0 {
+ l.messageMutex.Unlock()
+ return nil, NewError(ErrorNetwork, errors.New("ldap: cannot StartTLS with outstanding requests"))
+ } else {
+ l.isStartingTLS = true
+ }
+ }
+ l.outstandingRequests++
+
+ l.messageMutex.Unlock()
+
+ out := make(chan *PacketResponse)
+ message := &messagePacket{
+ Op: MessageRequest,
+ MessageID: packet.Children[0].Value.(int64),
+ Packet: packet,
+ Channel: out,
+ }
+ l.sendProcessMessage(message)
+ return out, nil
+}
+
+func (l *Conn) finishMessage(messageID int64) {
+ if l.isClosing {
+ return
+ }
+
+ l.messageMutex.Lock()
+ l.outstandingRequests--
+ if l.isStartingTLS {
+ l.isStartingTLS = false
+ }
+ l.messageMutex.Unlock()
+
+ message := &messagePacket{
+ Op: MessageFinish,
+ MessageID: messageID,
+ }
+ l.sendProcessMessage(message)
+}
+
+func (l *Conn) sendProcessMessage(message *messagePacket) bool {
+ if l.isClosing {
+ return false
+ }
+ l.wgSender.Add(1)
+ l.chanMessage <- message
+ l.wgSender.Done()
+ return true
+}
+
+func (l *Conn) processMessages() {
+ defer func() {
+ if err := recover(); err != nil {
+ log.Printf("ldap: recovered panic in processMessages: %v", err)
+ }
+ for messageID, channel := range l.chanResults {
+ l.Debug.Printf("Closing channel for MessageID %d", messageID)
+ close(channel)
+ delete(l.chanResults, messageID)
+ }
+ close(l.chanMessageID)
+ l.chanConfirm <- true
+ close(l.chanConfirm)
+ }()
+
+ var messageID int64 = 1
+ for {
+ select {
+ case l.chanMessageID <- messageID:
+ messageID++
+ case message, ok := <-l.chanMessage:
+ if !ok {
+ l.Debug.Printf("Shutting down - message channel is closed")
+ return
+ }
+ switch message.Op {
+ case MessageQuit:
+ l.Debug.Printf("Shutting down - quit message received")
+ return
+ case MessageRequest:
+ // Add to message list and write to network
+ l.Debug.Printf("Sending message %d", message.MessageID)
+ l.chanResults[message.MessageID] = message.Channel
+
+ buf := message.Packet.Bytes()
+ _, err := l.conn.Write(buf)
+ if err != nil {
+ l.Debug.Printf("Error Sending Message: %s", err.Error())
+ break
+ }
+
+ // Add timeout if defined
+ if l.requestTimeout > 0 {
+ go func() {
+ defer func() {
+ if err := recover(); err != nil {
+ log.Printf("ldap: recovered panic in RequestTimeout: %v", err)
+ }
+ }()
+ time.Sleep(l.requestTimeout)
+ timeoutMessage := &messagePacket{
+ Op: MessageTimeout,
+ MessageID: message.MessageID,
+ }
+ l.sendProcessMessage(timeoutMessage)
+ }()
+ }
+ case MessageResponse:
+ l.Debug.Printf("Receiving message %d", message.MessageID)
+ if chanResult, ok := l.chanResults[message.MessageID]; ok {
+ chanResult <- &PacketResponse{message.Packet, nil}
+ } else {
+ log.Printf("Received unexpected message %d, %v", message.MessageID, l.isClosing)
+ ber.PrintPacket(message.Packet)
+ }
+ case MessageTimeout:
+ // Handle the timeout by closing the channel
+ // All reads will return immediately
+ if chanResult, ok := l.chanResults[message.MessageID]; ok {
+ chanResult <- &PacketResponse{message.Packet, errors.New("ldap: connection timed out")}
+ l.Debug.Printf("Receiving message timeout for %d", message.MessageID)
+ delete(l.chanResults, message.MessageID)
+ close(chanResult)
+ }
+ case MessageFinish:
+ l.Debug.Printf("Finished message %d", message.MessageID)
+ if chanResult, ok := l.chanResults[message.MessageID]; ok {
+ close(chanResult)
+ delete(l.chanResults, message.MessageID)
+ }
+ }
+ }
+ }
+}
+
+func (l *Conn) reader() {
+ cleanstop := false
+ defer func() {
+ if err := recover(); err != nil {
+ log.Printf("ldap: recovered panic in reader: %v", err)
+ }
+ if !cleanstop {
+ l.Close()
+ }
+ }()
+
+ for {
+ if cleanstop {
+ l.Debug.Printf("reader clean stopping (without closing the connection)")
+ return
+ }
+ packet, err := ber.ReadPacket(l.conn)
+ if err != nil {
+ // A read error is expected here if we are closing the connection...
+ if !l.isClosing {
+ l.Debug.Printf("reader error: %s", err.Error())
+ }
+ return
+ }
+ addLDAPDescriptions(packet)
+ if len(packet.Children) == 0 {
+ l.Debug.Printf("Received bad ldap packet")
+ continue
+ }
+ l.messageMutex.Lock()
+ if l.isStartingTLS {
+ cleanstop = true
+ }
+ l.messageMutex.Unlock()
+ message := &messagePacket{
+ Op: MessageResponse,
+ MessageID: packet.Children[0].Value.(int64),
+ Packet: packet,
+ }
+ if !l.sendProcessMessage(message) {
+ return
+ }
+
+ }
+}
diff --git a/vendor/github.com/go-ldap/ldap/control.go b/vendor/github.com/go-ldap/ldap/control.go
new file mode 100644
index 000000000..4d8298093
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/control.go
@@ -0,0 +1,332 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ldap
+
+import (
+ "fmt"
+ "strconv"
+
+ "gopkg.in/asn1-ber.v1"
+)
+
+const (
+ ControlTypePaging = "1.2.840.113556.1.4.319"
+ ControlTypeBeheraPasswordPolicy = "1.3.6.1.4.1.42.2.27.8.5.1"
+ ControlTypeVChuPasswordMustChange = "2.16.840.1.113730.3.4.4"
+ ControlTypeVChuPasswordWarning = "2.16.840.1.113730.3.4.5"
+ ControlTypeManageDsaIT = "2.16.840.1.113730.3.4.2"
+)
+
+var ControlTypeMap = map[string]string{
+ ControlTypePaging: "Paging",
+ ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft",
+ ControlTypeManageDsaIT: "Manage DSA IT",
+}
+
+type Control interface {
+ GetControlType() string
+ Encode() *ber.Packet
+ String() string
+}
+
+type ControlString struct {
+ ControlType string
+ Criticality bool
+ ControlValue string
+}
+
+func (c *ControlString) GetControlType() string {
+ return c.ControlType
+}
+
+func (c *ControlString) Encode() *ber.Packet {
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
+ packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, c.ControlType, "Control Type ("+ControlTypeMap[c.ControlType]+")"))
+ if c.Criticality {
+ packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
+ }
+ packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, string(c.ControlValue), "Control Value"))
+ return packet
+}
+
+func (c *ControlString) String() string {
+ return fmt.Sprintf("Control Type: %s (%q) Criticality: %t Control Value: %s", ControlTypeMap[c.ControlType], c.ControlType, c.Criticality, c.ControlValue)
+}
+
+type ControlPaging struct {
+ PagingSize uint32
+ Cookie []byte
+}
+
+func (c *ControlPaging) GetControlType() string {
+ return ControlTypePaging
+}
+
+func (c *ControlPaging) Encode() *ber.Packet {
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
+ packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypePaging, "Control Type ("+ControlTypeMap[ControlTypePaging]+")"))
+
+ p2 := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (Paging)")
+ seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Search Control Value")
+ seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(c.PagingSize), "Paging Size"))
+ cookie := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Cookie")
+ cookie.Value = c.Cookie
+ cookie.Data.Write(c.Cookie)
+ seq.AppendChild(cookie)
+ p2.AppendChild(seq)
+
+ packet.AppendChild(p2)
+ return packet
+}
+
+func (c *ControlPaging) String() string {
+ return fmt.Sprintf(
+ "Control Type: %s (%q) Criticality: %t PagingSize: %d Cookie: %q",
+ ControlTypeMap[ControlTypePaging],
+ ControlTypePaging,
+ false,
+ c.PagingSize,
+ c.Cookie)
+}
+
+func (c *ControlPaging) SetCookie(cookie []byte) {
+ c.Cookie = cookie
+}
+
+type ControlBeheraPasswordPolicy struct {
+ Expire int64
+ Grace int64
+ Error int8
+ ErrorString string
+}
+
+func (c *ControlBeheraPasswordPolicy) GetControlType() string {
+ return ControlTypeBeheraPasswordPolicy
+}
+
+func (c *ControlBeheraPasswordPolicy) Encode() *ber.Packet {
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
+ packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeBeheraPasswordPolicy, "Control Type ("+ControlTypeMap[ControlTypeBeheraPasswordPolicy]+")"))
+
+ return packet
+}
+
+func (c *ControlBeheraPasswordPolicy) String() string {
+ return fmt.Sprintf(
+ "Control Type: %s (%q) Criticality: %t Expire: %d Grace: %d Error: %d, ErrorString: %s",
+ ControlTypeMap[ControlTypeBeheraPasswordPolicy],
+ ControlTypeBeheraPasswordPolicy,
+ false,
+ c.Expire,
+ c.Grace,
+ c.Error,
+ c.ErrorString)
+}
+
+type ControlVChuPasswordMustChange struct {
+ MustChange bool
+}
+
+func (c *ControlVChuPasswordMustChange) GetControlType() string {
+ return ControlTypeVChuPasswordMustChange
+}
+
+func (c *ControlVChuPasswordMustChange) Encode() *ber.Packet {
+ return nil
+}
+
+func (c *ControlVChuPasswordMustChange) String() string {
+ return fmt.Sprintf(
+ "Control Type: %s (%q) Criticality: %t MustChange: %b",
+ ControlTypeMap[ControlTypeVChuPasswordMustChange],
+ ControlTypeVChuPasswordMustChange,
+ false,
+ c.MustChange)
+}
+
+type ControlVChuPasswordWarning struct {
+ Expire int64
+}
+
+func (c *ControlVChuPasswordWarning) GetControlType() string {
+ return ControlTypeVChuPasswordWarning
+}
+
+func (c *ControlVChuPasswordWarning) Encode() *ber.Packet {
+ return nil
+}
+
+func (c *ControlVChuPasswordWarning) String() string {
+ return fmt.Sprintf(
+ "Control Type: %s (%q) Criticality: %t Expire: %b",
+ ControlTypeMap[ControlTypeVChuPasswordWarning],
+ ControlTypeVChuPasswordWarning,
+ false,
+ c.Expire)
+}
+
+type ControlManageDsaIT struct {
+ Criticality bool
+}
+
+func (c *ControlManageDsaIT) GetControlType() string {
+ return ControlTypeManageDsaIT
+}
+
+func (c *ControlManageDsaIT) Encode() *ber.Packet {
+ //FIXME
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
+ packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeManageDsaIT, "Control Type ("+ControlTypeMap[ControlTypeManageDsaIT]+")"))
+ if c.Criticality {
+ packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
+ }
+ return packet
+}
+
+func (c *ControlManageDsaIT) String() string {
+ return fmt.Sprintf(
+ "Control Type: %s (%q) Criticality: %t",
+ ControlTypeMap[ControlTypeManageDsaIT],
+ ControlTypeManageDsaIT,
+ c.Criticality)
+}
+
+func NewControlManageDsaIT(Criticality bool) *ControlManageDsaIT {
+ return &ControlManageDsaIT{Criticality: Criticality}
+}
+
+func FindControl(controls []Control, controlType string) Control {
+ for _, c := range controls {
+ if c.GetControlType() == controlType {
+ return c
+ }
+ }
+ return nil
+}
+
+func DecodeControl(packet *ber.Packet) Control {
+ ControlType := packet.Children[0].Value.(string)
+ Criticality := false
+
+ packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
+ value := packet.Children[1]
+ if len(packet.Children) == 3 {
+ value = packet.Children[2]
+ packet.Children[1].Description = "Criticality"
+ Criticality = packet.Children[1].Value.(bool)
+ }
+
+ value.Description = "Control Value"
+ switch ControlType {
+ case ControlTypePaging:
+ value.Description += " (Paging)"
+ c := new(ControlPaging)
+ if value.Value != nil {
+ valueChildren := ber.DecodePacket(value.Data.Bytes())
+ value.Data.Truncate(0)
+ value.Value = nil
+ value.AppendChild(valueChildren)
+ }
+ value = value.Children[0]
+ value.Description = "Search Control Value"
+ value.Children[0].Description = "Paging Size"
+ value.Children[1].Description = "Cookie"
+ c.PagingSize = uint32(value.Children[0].Value.(int64))
+ c.Cookie = value.Children[1].Data.Bytes()
+ value.Children[1].Value = c.Cookie
+ return c
+ case ControlTypeBeheraPasswordPolicy:
+ value.Description += " (Password Policy - Behera)"
+ c := NewControlBeheraPasswordPolicy()
+ if value.Value != nil {
+ valueChildren := ber.DecodePacket(value.Data.Bytes())
+ value.Data.Truncate(0)
+ value.Value = nil
+ value.AppendChild(valueChildren)
+ }
+
+ sequence := value.Children[0]
+
+ for _, child := range sequence.Children {
+ if child.Tag == 0 {
+ //Warning
+ child := child.Children[0]
+ packet := ber.DecodePacket(child.Data.Bytes())
+ val, ok := packet.Value.(int64)
+ if ok {
+ if child.Tag == 0 {
+ //timeBeforeExpiration
+ c.Expire = val
+ child.Value = c.Expire
+ } else if child.Tag == 1 {
+ //graceAuthNsRemaining
+ c.Grace = val
+ child.Value = c.Grace
+ }
+ }
+ } else if child.Tag == 1 {
+ // Error
+ packet := ber.DecodePacket(child.Data.Bytes())
+ val, ok := packet.Value.(int8)
+ if !ok {
+ // what to do?
+ val = -1
+ }
+ c.Error = val
+ child.Value = c.Error
+ c.ErrorString = BeheraPasswordPolicyErrorMap[c.Error]
+ }
+ }
+ return c
+ case ControlTypeVChuPasswordMustChange:
+ c := &ControlVChuPasswordMustChange{MustChange: true}
+ return c
+ case ControlTypeVChuPasswordWarning:
+ c := &ControlVChuPasswordWarning{Expire: -1}
+ expireStr := ber.DecodeString(value.Data.Bytes())
+
+ expire, err := strconv.ParseInt(expireStr, 10, 64)
+ if err != nil {
+ return nil
+ }
+ c.Expire = expire
+ value.Value = c.Expire
+
+ return c
+ }
+ c := new(ControlString)
+ c.ControlType = ControlType
+ c.Criticality = Criticality
+ c.ControlValue = value.Value.(string)
+ return c
+}
+
+func NewControlString(controlType string, criticality bool, controlValue string) *ControlString {
+ return &ControlString{
+ ControlType: controlType,
+ Criticality: criticality,
+ ControlValue: controlValue,
+ }
+}
+
+func NewControlPaging(pagingSize uint32) *ControlPaging {
+ return &ControlPaging{PagingSize: pagingSize}
+}
+
+func NewControlBeheraPasswordPolicy() *ControlBeheraPasswordPolicy {
+ return &ControlBeheraPasswordPolicy{
+ Expire: -1,
+ Grace: -1,
+ Error: -1,
+ }
+}
+
+func encodeControls(controls []Control) *ber.Packet {
+ packet := ber.Encode(ber.ClassContext, ber.TypeConstructed, 0, nil, "Controls")
+ for _, control := range controls {
+ packet.AppendChild(control.Encode())
+ }
+ return packet
+}
diff --git a/vendor/github.com/go-ldap/ldap/debug.go b/vendor/github.com/go-ldap/ldap/debug.go
new file mode 100644
index 000000000..b8a7ecbff
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/debug.go
@@ -0,0 +1,24 @@
+package ldap
+
+import (
+ "log"
+
+ "gopkg.in/asn1-ber.v1"
+)
+
+// debbuging type
+// - has a Printf method to write the debug output
+type debugging bool
+
+// write debug output
+func (debug debugging) Printf(format string, args ...interface{}) {
+ if debug {
+ log.Printf(format, args...)
+ }
+}
+
+func (debug debugging) PrintPacket(packet *ber.Packet) {
+ if debug {
+ ber.PrintPacket(packet)
+ }
+}
diff --git a/vendor/github.com/go-ldap/ldap/del.go b/vendor/github.com/go-ldap/ldap/del.go
new file mode 100644
index 000000000..5bb5a25d7
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/del.go
@@ -0,0 +1,83 @@
+//
+// https://tools.ietf.org/html/rfc4511
+//
+// DelRequest ::= [APPLICATION 10] LDAPDN
+
+package ldap
+
+import (
+ "errors"
+ "log"
+
+ "gopkg.in/asn1-ber.v1"
+)
+
+type DelRequest struct {
+ DN string
+ Controls []Control
+}
+
+func (d DelRequest) encode() *ber.Packet {
+ request := ber.Encode(ber.ClassApplication, ber.TypePrimitive, ApplicationDelRequest, d.DN, "Del Request")
+ request.Data.Write([]byte(d.DN))
+ return request
+}
+
+func NewDelRequest(DN string,
+ Controls []Control) *DelRequest {
+ return &DelRequest{
+ DN: DN,
+ Controls: Controls,
+ }
+}
+
+func (l *Conn) Del(delRequest *DelRequest) error {
+ messageID := l.nextMessageID()
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ packet.AppendChild(delRequest.encode())
+ if delRequest.Controls != nil {
+ packet.AppendChild(encodeControls(delRequest.Controls))
+ }
+
+ l.Debug.PrintPacket(packet)
+
+ channel, err := l.sendMessage(packet)
+ if err != nil {
+ return err
+ }
+ if channel == nil {
+ return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
+ }
+ defer l.finishMessage(messageID)
+
+ l.Debug.Printf("%d: waiting for response", messageID)
+ packetResponse, ok := <-channel
+ if !ok {
+ return NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ }
+ packet, err = packetResponse.ReadPacket()
+ l.Debug.Printf("%d: got response %p", messageID, packet)
+ if err != nil {
+ return err
+ }
+
+ if l.Debug {
+ if err := addLDAPDescriptions(packet); err != nil {
+ return err
+ }
+ ber.PrintPacket(packet)
+ }
+
+ if packet.Children[1].Tag == ApplicationDelResponse {
+ resultCode, resultDescription := getLDAPResultCode(packet)
+ if resultCode != 0 {
+ return NewError(resultCode, errors.New(resultDescription))
+ }
+ } else {
+ log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
+ }
+
+ l.Debug.Printf("%d: returning", messageID)
+ return nil
+}
diff --git a/vendor/github.com/go-ldap/ldap/dn.go b/vendor/github.com/go-ldap/ldap/dn.go
new file mode 100644
index 000000000..5d83c5e9a
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/dn.go
@@ -0,0 +1,155 @@
+// Copyright 2015 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.
+//
+// File contains DN parsing functionallity
+//
+// https://tools.ietf.org/html/rfc4514
+//
+// distinguishedName = [ relativeDistinguishedName
+// *( COMMA relativeDistinguishedName ) ]
+// relativeDistinguishedName = attributeTypeAndValue
+// *( PLUS attributeTypeAndValue )
+// attributeTypeAndValue = attributeType EQUALS attributeValue
+// attributeType = descr / numericoid
+// attributeValue = string / hexstring
+//
+// ; The following characters are to be escaped when they appear
+// ; in the value to be encoded: ESC, one of <escaped>, leading
+// ; SHARP or SPACE, trailing SPACE, and NULL.
+// string = [ ( leadchar / pair ) [ *( stringchar / pair )
+// ( trailchar / pair ) ] ]
+//
+// leadchar = LUTF1 / UTFMB
+// LUTF1 = %x01-1F / %x21 / %x24-2A / %x2D-3A /
+// %x3D / %x3F-5B / %x5D-7F
+//
+// trailchar = TUTF1 / UTFMB
+// TUTF1 = %x01-1F / %x21 / %x23-2A / %x2D-3A /
+// %x3D / %x3F-5B / %x5D-7F
+//
+// stringchar = SUTF1 / UTFMB
+// SUTF1 = %x01-21 / %x23-2A / %x2D-3A /
+// %x3D / %x3F-5B / %x5D-7F
+//
+// pair = ESC ( ESC / special / hexpair )
+// special = escaped / SPACE / SHARP / EQUALS
+// escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE
+// hexstring = SHARP 1*hexpair
+// hexpair = HEX HEX
+//
+// where the productions <descr>, <numericoid>, <COMMA>, <DQUOTE>,
+// <EQUALS>, <ESC>, <HEX>, <LANGLE>, <NULL>, <PLUS>, <RANGLE>, <SEMI>,
+// <SPACE>, <SHARP>, and <UTFMB> are defined in [RFC4512].
+//
+
+package ldap
+
+import (
+ "bytes"
+ enchex "encoding/hex"
+ "errors"
+ "fmt"
+ "strings"
+
+ ber "gopkg.in/asn1-ber.v1"
+)
+
+type AttributeTypeAndValue struct {
+ Type string
+ Value string
+}
+
+type RelativeDN struct {
+ Attributes []*AttributeTypeAndValue
+}
+
+type DN struct {
+ RDNs []*RelativeDN
+}
+
+func ParseDN(str string) (*DN, error) {
+ dn := new(DN)
+ dn.RDNs = make([]*RelativeDN, 0)
+ rdn := new(RelativeDN)
+ rdn.Attributes = make([]*AttributeTypeAndValue, 0)
+ buffer := bytes.Buffer{}
+ attribute := new(AttributeTypeAndValue)
+ escaping := false
+
+ for i := 0; i < len(str); i++ {
+ char := str[i]
+ if escaping {
+ escaping = false
+ switch char {
+ case ' ', '"', '#', '+', ',', ';', '<', '=', '>', '\\':
+ buffer.WriteByte(char)
+ continue
+ }
+ // Not a special character, assume hex encoded octet
+ if len(str) == i+1 {
+ return nil, errors.New("Got corrupted escaped character")
+ }
+
+ dst := []byte{0}
+ n, err := enchex.Decode([]byte(dst), []byte(str[i:i+2]))
+ if err != nil {
+ return nil, errors.New(
+ fmt.Sprintf("Failed to decode escaped character: %s", err))
+ } else if n != 1 {
+ return nil, errors.New(
+ fmt.Sprintf("Expected 1 byte when un-escaping, got %d", n))
+ }
+ buffer.WriteByte(dst[0])
+ i++
+ } else if char == '\\' {
+ escaping = true
+ } else if char == '=' {
+ attribute.Type = buffer.String()
+ buffer.Reset()
+ // Special case: If the first character in the value is # the
+ // following data is BER encoded so we can just fast forward
+ // and decode.
+ if len(str) > i+1 && str[i+1] == '#' {
+ i += 2
+ index := strings.IndexAny(str[i:], ",+")
+ data := str
+ if index > 0 {
+ data = str[i : i+index]
+ } else {
+ data = str[i:]
+ }
+ raw_ber, err := enchex.DecodeString(data)
+ if err != nil {
+ return nil, errors.New(
+ fmt.Sprintf("Failed to decode BER encoding: %s", err))
+ }
+ packet := ber.DecodePacket(raw_ber)
+ buffer.WriteString(packet.Data.String())
+ i += len(data) - 1
+ }
+ } else if char == ',' || char == '+' {
+ // We're done with this RDN or value, push it
+ attribute.Value = buffer.String()
+ rdn.Attributes = append(rdn.Attributes, attribute)
+ attribute = new(AttributeTypeAndValue)
+ if char == ',' {
+ dn.RDNs = append(dn.RDNs, rdn)
+ rdn = new(RelativeDN)
+ rdn.Attributes = make([]*AttributeTypeAndValue, 0)
+ }
+ buffer.Reset()
+ } else {
+ buffer.WriteByte(char)
+ }
+ }
+ if buffer.Len() > 0 {
+ if len(attribute.Type) == 0 {
+ return nil, errors.New("DN ended with incomplete type, value pair")
+ }
+ attribute.Value = buffer.String()
+ rdn.Attributes = append(rdn.Attributes, attribute)
+ dn.RDNs = append(dn.RDNs, rdn)
+ }
+ return dn, nil
+}
diff --git a/vendor/github.com/go-ldap/ldap/doc.go b/vendor/github.com/go-ldap/ldap/doc.go
new file mode 100644
index 000000000..f20d39bc9
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/doc.go
@@ -0,0 +1,4 @@
+/*
+Package ldap provides basic LDAP v3 functionality.
+*/
+package ldap
diff --git a/vendor/github.com/go-ldap/ldap/error.go b/vendor/github.com/go-ldap/ldap/error.go
new file mode 100644
index 000000000..97404eb65
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/error.go
@@ -0,0 +1,142 @@
+package ldap
+
+import (
+ "fmt"
+
+ "gopkg.in/asn1-ber.v1"
+)
+
+// LDAP Result Codes
+const (
+ LDAPResultSuccess = 0
+ LDAPResultOperationsError = 1
+ LDAPResultProtocolError = 2
+ LDAPResultTimeLimitExceeded = 3
+ LDAPResultSizeLimitExceeded = 4
+ LDAPResultCompareFalse = 5
+ LDAPResultCompareTrue = 6
+ LDAPResultAuthMethodNotSupported = 7
+ LDAPResultStrongAuthRequired = 8
+ LDAPResultReferral = 10
+ LDAPResultAdminLimitExceeded = 11
+ LDAPResultUnavailableCriticalExtension = 12
+ LDAPResultConfidentialityRequired = 13
+ LDAPResultSaslBindInProgress = 14
+ LDAPResultNoSuchAttribute = 16
+ LDAPResultUndefinedAttributeType = 17
+ LDAPResultInappropriateMatching = 18
+ LDAPResultConstraintViolation = 19
+ LDAPResultAttributeOrValueExists = 20
+ LDAPResultInvalidAttributeSyntax = 21
+ LDAPResultNoSuchObject = 32
+ LDAPResultAliasProblem = 33
+ LDAPResultInvalidDNSyntax = 34
+ LDAPResultAliasDereferencingProblem = 36
+ LDAPResultInappropriateAuthentication = 48
+ LDAPResultInvalidCredentials = 49
+ LDAPResultInsufficientAccessRights = 50
+ LDAPResultBusy = 51
+ LDAPResultUnavailable = 52
+ LDAPResultUnwillingToPerform = 53
+ LDAPResultLoopDetect = 54
+ LDAPResultNamingViolation = 64
+ LDAPResultObjectClassViolation = 65
+ LDAPResultNotAllowedOnNonLeaf = 66
+ LDAPResultNotAllowedOnRDN = 67
+ LDAPResultEntryAlreadyExists = 68
+ LDAPResultObjectClassModsProhibited = 69
+ LDAPResultAffectsMultipleDSAs = 71
+ LDAPResultOther = 80
+
+ ErrorNetwork = 200
+ ErrorFilterCompile = 201
+ ErrorFilterDecompile = 202
+ ErrorDebugging = 203
+ ErrorUnexpectedMessage = 204
+ ErrorUnexpectedResponse = 205
+)
+
+var LDAPResultCodeMap = map[uint8]string{
+ LDAPResultSuccess: "Success",
+ LDAPResultOperationsError: "Operations Error",
+ LDAPResultProtocolError: "Protocol Error",
+ LDAPResultTimeLimitExceeded: "Time Limit Exceeded",
+ LDAPResultSizeLimitExceeded: "Size Limit Exceeded",
+ LDAPResultCompareFalse: "Compare False",
+ LDAPResultCompareTrue: "Compare True",
+ LDAPResultAuthMethodNotSupported: "Auth Method Not Supported",
+ LDAPResultStrongAuthRequired: "Strong Auth Required",
+ LDAPResultReferral: "Referral",
+ LDAPResultAdminLimitExceeded: "Admin Limit Exceeded",
+ LDAPResultUnavailableCriticalExtension: "Unavailable Critical Extension",
+ LDAPResultConfidentialityRequired: "Confidentiality Required",
+ LDAPResultSaslBindInProgress: "Sasl Bind In Progress",
+ LDAPResultNoSuchAttribute: "No Such Attribute",
+ LDAPResultUndefinedAttributeType: "Undefined Attribute Type",
+ LDAPResultInappropriateMatching: "Inappropriate Matching",
+ LDAPResultConstraintViolation: "Constraint Violation",
+ LDAPResultAttributeOrValueExists: "Attribute Or Value Exists",
+ LDAPResultInvalidAttributeSyntax: "Invalid Attribute Syntax",
+ LDAPResultNoSuchObject: "No Such Object",
+ LDAPResultAliasProblem: "Alias Problem",
+ LDAPResultInvalidDNSyntax: "Invalid DN Syntax",
+ LDAPResultAliasDereferencingProblem: "Alias Dereferencing Problem",
+ LDAPResultInappropriateAuthentication: "Inappropriate Authentication",
+ LDAPResultInvalidCredentials: "Invalid Credentials",
+ LDAPResultInsufficientAccessRights: "Insufficient Access Rights",
+ LDAPResultBusy: "Busy",
+ LDAPResultUnavailable: "Unavailable",
+ LDAPResultUnwillingToPerform: "Unwilling To Perform",
+ LDAPResultLoopDetect: "Loop Detect",
+ LDAPResultNamingViolation: "Naming Violation",
+ LDAPResultObjectClassViolation: "Object Class Violation",
+ LDAPResultNotAllowedOnNonLeaf: "Not Allowed On Non Leaf",
+ LDAPResultNotAllowedOnRDN: "Not Allowed On RDN",
+ LDAPResultEntryAlreadyExists: "Entry Already Exists",
+ LDAPResultObjectClassModsProhibited: "Object Class Mods Prohibited",
+ LDAPResultAffectsMultipleDSAs: "Affects Multiple DSAs",
+ LDAPResultOther: "Other",
+}
+
+func getLDAPResultCode(packet *ber.Packet) (code uint8, description string) {
+ if packet == nil {
+ return ErrorUnexpectedResponse, "Empty packet"
+ } else if len(packet.Children) >= 2 {
+ response := packet.Children[1]
+ if response == nil {
+ return ErrorUnexpectedResponse, "Empty response in packet"
+ }
+ if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len(response.Children) >= 3 {
+ // Children[1].Children[2] is the diagnosticMessage which is guaranteed to exist as seen here: https://tools.ietf.org/html/rfc4511#section-4.1.9
+ return uint8(response.Children[0].Value.(int64)), response.Children[2].Value.(string)
+ }
+ }
+
+ return ErrorNetwork, "Invalid packet format"
+}
+
+type Error struct {
+ Err error
+ ResultCode uint8
+}
+
+func (e *Error) Error() string {
+ return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error())
+}
+
+func NewError(resultCode uint8, err error) error {
+ return &Error{ResultCode: resultCode, Err: err}
+}
+
+func IsErrorWithCode(err error, desiredResultCode uint8) bool {
+ if err == nil {
+ return false
+ }
+
+ serverError, ok := err.(*Error)
+ if !ok {
+ return false
+ }
+
+ return serverError.ResultCode == desiredResultCode
+}
diff --git a/vendor/github.com/go-ldap/ldap/filter.go b/vendor/github.com/go-ldap/ldap/filter.go
new file mode 100644
index 000000000..63bcec1e3
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/filter.go
@@ -0,0 +1,456 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ldap
+
+import (
+ "bytes"
+ hexpac "encoding/hex"
+ "errors"
+ "fmt"
+ "strings"
+ "unicode/utf8"
+
+ "gopkg.in/asn1-ber.v1"
+)
+
+const (
+ FilterAnd = 0
+ FilterOr = 1
+ FilterNot = 2
+ FilterEqualityMatch = 3
+ FilterSubstrings = 4
+ FilterGreaterOrEqual = 5
+ FilterLessOrEqual = 6
+ FilterPresent = 7
+ FilterApproxMatch = 8
+ FilterExtensibleMatch = 9
+)
+
+var FilterMap = map[uint64]string{
+ FilterAnd: "And",
+ FilterOr: "Or",
+ FilterNot: "Not",
+ FilterEqualityMatch: "Equality Match",
+ FilterSubstrings: "Substrings",
+ FilterGreaterOrEqual: "Greater Or Equal",
+ FilterLessOrEqual: "Less Or Equal",
+ FilterPresent: "Present",
+ FilterApproxMatch: "Approx Match",
+ FilterExtensibleMatch: "Extensible Match",
+}
+
+const (
+ FilterSubstringsInitial = 0
+ FilterSubstringsAny = 1
+ FilterSubstringsFinal = 2
+)
+
+var FilterSubstringsMap = map[uint64]string{
+ FilterSubstringsInitial: "Substrings Initial",
+ FilterSubstringsAny: "Substrings Any",
+ FilterSubstringsFinal: "Substrings Final",
+}
+
+const (
+ MatchingRuleAssertionMatchingRule = 1
+ MatchingRuleAssertionType = 2
+ MatchingRuleAssertionMatchValue = 3
+ MatchingRuleAssertionDNAttributes = 4
+)
+
+var MatchingRuleAssertionMap = map[uint64]string{
+ MatchingRuleAssertionMatchingRule: "Matching Rule Assertion Matching Rule",
+ MatchingRuleAssertionType: "Matching Rule Assertion Type",
+ MatchingRuleAssertionMatchValue: "Matching Rule Assertion Match Value",
+ MatchingRuleAssertionDNAttributes: "Matching Rule Assertion DN Attributes",
+}
+
+func CompileFilter(filter string) (*ber.Packet, error) {
+ if len(filter) == 0 || filter[0] != '(' {
+ return nil, NewError(ErrorFilterCompile, errors.New("ldap: filter does not start with an '('"))
+ }
+ packet, pos, err := compileFilter(filter, 1)
+ if err != nil {
+ return nil, err
+ }
+ if pos != len(filter) {
+ return nil, NewError(ErrorFilterCompile, errors.New("ldap: finished compiling filter with extra at end: "+fmt.Sprint(filter[pos:])))
+ }
+ return packet, nil
+}
+
+func DecompileFilter(packet *ber.Packet) (ret string, err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ err = NewError(ErrorFilterDecompile, errors.New("ldap: error decompiling filter"))
+ }
+ }()
+ ret = "("
+ err = nil
+ childStr := ""
+
+ switch packet.Tag {
+ case FilterAnd:
+ ret += "&"
+ for _, child := range packet.Children {
+ childStr, err = DecompileFilter(child)
+ if err != nil {
+ return
+ }
+ ret += childStr
+ }
+ case FilterOr:
+ ret += "|"
+ for _, child := range packet.Children {
+ childStr, err = DecompileFilter(child)
+ if err != nil {
+ return
+ }
+ ret += childStr
+ }
+ case FilterNot:
+ ret += "!"
+ childStr, err = DecompileFilter(packet.Children[0])
+ if err != nil {
+ return
+ }
+ ret += childStr
+
+ case FilterSubstrings:
+ ret += ber.DecodeString(packet.Children[0].Data.Bytes())
+ ret += "="
+ for i, child := range packet.Children[1].Children {
+ if i == 0 && child.Tag != FilterSubstringsInitial {
+ ret += "*"
+ }
+ ret += EscapeFilter(ber.DecodeString(child.Data.Bytes()))
+ if child.Tag != FilterSubstringsFinal {
+ ret += "*"
+ }
+ }
+ case FilterEqualityMatch:
+ ret += ber.DecodeString(packet.Children[0].Data.Bytes())
+ ret += "="
+ ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))
+ case FilterGreaterOrEqual:
+ ret += ber.DecodeString(packet.Children[0].Data.Bytes())
+ ret += ">="
+ ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))
+ case FilterLessOrEqual:
+ ret += ber.DecodeString(packet.Children[0].Data.Bytes())
+ ret += "<="
+ ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))
+ case FilterPresent:
+ ret += ber.DecodeString(packet.Data.Bytes())
+ ret += "=*"
+ case FilterApproxMatch:
+ ret += ber.DecodeString(packet.Children[0].Data.Bytes())
+ ret += "~="
+ ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))
+ case FilterExtensibleMatch:
+ attr := ""
+ dnAttributes := false
+ matchingRule := ""
+ value := ""
+
+ for _, child := range packet.Children {
+ switch child.Tag {
+ case MatchingRuleAssertionMatchingRule:
+ matchingRule = ber.DecodeString(child.Data.Bytes())
+ case MatchingRuleAssertionType:
+ attr = ber.DecodeString(child.Data.Bytes())
+ case MatchingRuleAssertionMatchValue:
+ value = ber.DecodeString(child.Data.Bytes())
+ case MatchingRuleAssertionDNAttributes:
+ dnAttributes = child.Value.(bool)
+ }
+ }
+
+ if len(attr) > 0 {
+ ret += attr
+ }
+ if dnAttributes {
+ ret += ":dn"
+ }
+ if len(matchingRule) > 0 {
+ ret += ":"
+ ret += matchingRule
+ }
+ ret += ":="
+ ret += EscapeFilter(value)
+ }
+
+ ret += ")"
+ return
+}
+
+func compileFilterSet(filter string, pos int, parent *ber.Packet) (int, error) {
+ for pos < len(filter) && filter[pos] == '(' {
+ child, newPos, err := compileFilter(filter, pos+1)
+ if err != nil {
+ return pos, err
+ }
+ pos = newPos
+ parent.AppendChild(child)
+ }
+ if pos == len(filter) {
+ return pos, NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter"))
+ }
+
+ return pos + 1, nil
+}
+
+func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
+ var (
+ packet *ber.Packet
+ err error
+ )
+
+ defer func() {
+ if r := recover(); r != nil {
+ err = NewError(ErrorFilterCompile, errors.New("ldap: error compiling filter"))
+ }
+ }()
+ newPos := pos
+
+ currentRune, currentWidth := utf8.DecodeRuneInString(filter[newPos:])
+
+ switch currentRune {
+ case utf8.RuneError:
+ return nil, 0, NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", newPos))
+ case '(':
+ packet, newPos, err = compileFilter(filter, pos+currentWidth)
+ newPos++
+ return packet, newPos, err
+ case '&':
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterAnd, nil, FilterMap[FilterAnd])
+ newPos, err = compileFilterSet(filter, pos+currentWidth, packet)
+ return packet, newPos, err
+ case '|':
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterOr, nil, FilterMap[FilterOr])
+ newPos, err = compileFilterSet(filter, pos+currentWidth, packet)
+ return packet, newPos, err
+ case '!':
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterNot, nil, FilterMap[FilterNot])
+ var child *ber.Packet
+ child, newPos, err = compileFilter(filter, pos+currentWidth)
+ packet.AppendChild(child)
+ return packet, newPos, err
+ default:
+ READING_ATTR := 0
+ READING_EXTENSIBLE_MATCHING_RULE := 1
+ READING_CONDITION := 2
+
+ state := READING_ATTR
+
+ attribute := ""
+ extensibleDNAttributes := false
+ extensibleMatchingRule := ""
+ condition := ""
+
+ for newPos < len(filter) {
+ remainingFilter := filter[newPos:]
+ currentRune, currentWidth = utf8.DecodeRuneInString(remainingFilter)
+ if currentRune == ')' {
+ break
+ }
+ if currentRune == utf8.RuneError {
+ return packet, newPos, NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", newPos))
+ }
+
+ switch state {
+ case READING_ATTR:
+ switch {
+ // Extensible rule, with only DN-matching
+ case currentRune == ':' && strings.HasPrefix(remainingFilter, ":dn:="):
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
+ extensibleDNAttributes = true
+ state = READING_CONDITION
+ newPos += 5
+
+ // Extensible rule, with DN-matching and a matching OID
+ case currentRune == ':' && strings.HasPrefix(remainingFilter, ":dn:"):
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
+ extensibleDNAttributes = true
+ state = READING_EXTENSIBLE_MATCHING_RULE
+ newPos += 4
+
+ // Extensible rule, with attr only
+ case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="):
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
+ state = READING_CONDITION
+ newPos += 2
+
+ // Extensible rule, with no DN attribute matching
+ case currentRune == ':':
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
+ state = READING_EXTENSIBLE_MATCHING_RULE
+ newPos += 1
+
+ // Equality condition
+ case currentRune == '=':
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterEqualityMatch, nil, FilterMap[FilterEqualityMatch])
+ state = READING_CONDITION
+ newPos += 1
+
+ // Greater-than or equal
+ case currentRune == '>' && strings.HasPrefix(remainingFilter, ">="):
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterGreaterOrEqual, nil, FilterMap[FilterGreaterOrEqual])
+ state = READING_CONDITION
+ newPos += 2
+
+ // Less-than or equal
+ case currentRune == '<' && strings.HasPrefix(remainingFilter, "<="):
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterLessOrEqual, nil, FilterMap[FilterLessOrEqual])
+ state = READING_CONDITION
+ newPos += 2
+
+ // Approx
+ case currentRune == '~' && strings.HasPrefix(remainingFilter, "~="):
+ packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterApproxMatch, nil, FilterMap[FilterApproxMatch])
+ state = READING_CONDITION
+ newPos += 2
+
+ // Still reading the attribute name
+ default:
+ attribute += fmt.Sprintf("%c", currentRune)
+ newPos += currentWidth
+ }
+
+ case READING_EXTENSIBLE_MATCHING_RULE:
+ switch {
+
+ // Matching rule OID is done
+ case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="):
+ state = READING_CONDITION
+ newPos += 2
+
+ // Still reading the matching rule oid
+ default:
+ extensibleMatchingRule += fmt.Sprintf("%c", currentRune)
+ newPos += currentWidth
+ }
+
+ case READING_CONDITION:
+ // append to the condition
+ condition += fmt.Sprintf("%c", currentRune)
+ newPos += currentWidth
+ }
+ }
+
+ if newPos == len(filter) {
+ err = NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter"))
+ return packet, newPos, err
+ }
+ if packet == nil {
+ err = NewError(ErrorFilterCompile, errors.New("ldap: error parsing filter"))
+ return packet, newPos, err
+ }
+
+ switch {
+ case packet.Tag == FilterExtensibleMatch:
+ // MatchingRuleAssertion ::= SEQUENCE {
+ // matchingRule [1] MatchingRuleID OPTIONAL,
+ // type [2] AttributeDescription OPTIONAL,
+ // matchValue [3] AssertionValue,
+ // dnAttributes [4] BOOLEAN DEFAULT FALSE
+ // }
+
+ // Include the matching rule oid, if specified
+ if len(extensibleMatchingRule) > 0 {
+ packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchingRule, extensibleMatchingRule, MatchingRuleAssertionMap[MatchingRuleAssertionMatchingRule]))
+ }
+
+ // Include the attribute, if specified
+ if len(attribute) > 0 {
+ packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionType, attribute, MatchingRuleAssertionMap[MatchingRuleAssertionType]))
+ }
+
+ // Add the value (only required child)
+ encodedString, err := escapedStringToEncodedBytes(condition)
+ if err != nil {
+ return packet, newPos, err
+ }
+ packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchValue, encodedString, MatchingRuleAssertionMap[MatchingRuleAssertionMatchValue]))
+
+ // Defaults to false, so only include in the sequence if true
+ if extensibleDNAttributes {
+ packet.AppendChild(ber.NewBoolean(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionDNAttributes, extensibleDNAttributes, MatchingRuleAssertionMap[MatchingRuleAssertionDNAttributes]))
+ }
+
+ case packet.Tag == FilterEqualityMatch && condition == "*":
+ packet = ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterPresent, attribute, FilterMap[FilterPresent])
+ case packet.Tag == FilterEqualityMatch && strings.Contains(condition, "*"):
+ packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
+ packet.Tag = FilterSubstrings
+ packet.Description = FilterMap[uint64(packet.Tag)]
+ seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Substrings")
+ parts := strings.Split(condition, "*")
+ for i, part := range parts {
+ if part == "" {
+ continue
+ }
+ var tag ber.Tag
+ switch i {
+ case 0:
+ tag = FilterSubstringsInitial
+ case len(parts) - 1:
+ tag = FilterSubstringsFinal
+ default:
+ tag = FilterSubstringsAny
+ }
+ encodedString, err := escapedStringToEncodedBytes(part)
+ if err != nil {
+ return packet, newPos, err
+ }
+ seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, tag, encodedString, FilterSubstringsMap[uint64(tag)]))
+ }
+ packet.AppendChild(seq)
+ default:
+ encodedString, err := escapedStringToEncodedBytes(condition)
+ if err != nil {
+ return packet, newPos, err
+ }
+ packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
+ packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, encodedString, "Condition"))
+ }
+
+ newPos += currentWidth
+ return packet, newPos, err
+ }
+}
+
+// Convert from "ABC\xx\xx\xx" form to literal bytes for transport
+func escapedStringToEncodedBytes(escapedString string) (string, error) {
+ var buffer bytes.Buffer
+ i := 0
+ for i < len(escapedString) {
+ currentRune, currentWidth := utf8.DecodeRuneInString(escapedString[i:])
+ if currentRune == utf8.RuneError {
+ return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", i))
+ }
+
+ // Check for escaped hex characters and convert them to their literal value for transport.
+ if currentRune == '\\' {
+ // http://tools.ietf.org/search/rfc4515
+ // \ (%x5C) is not a valid character unless it is followed by two HEX characters due to not
+ // being a member of UTF1SUBSET.
+ if i+2 > len(escapedString) {
+ return "", NewError(ErrorFilterCompile, errors.New("ldap: missing characters for escape in filter"))
+ }
+ if escByte, decodeErr := hexpac.DecodeString(escapedString[i+1 : i+3]); decodeErr != nil {
+ return "", NewError(ErrorFilterCompile, errors.New("ldap: invalid characters for escape in filter"))
+ } else {
+ buffer.WriteByte(escByte[0])
+ i += 2 // +1 from end of loop, so 3 total for \xx.
+ }
+ } else {
+ buffer.WriteRune(currentRune)
+ }
+
+ i += currentWidth
+ }
+ return buffer.String(), nil
+}
diff --git a/vendor/github.com/go-ldap/ldap/ldap.go b/vendor/github.com/go-ldap/ldap/ldap.go
new file mode 100644
index 000000000..1620aaea6
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/ldap.go
@@ -0,0 +1,286 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ldap
+
+import (
+ "errors"
+ "io/ioutil"
+ "os"
+
+ ber "gopkg.in/asn1-ber.v1"
+)
+
+// LDAP Application Codes
+const (
+ ApplicationBindRequest = 0
+ ApplicationBindResponse = 1
+ ApplicationUnbindRequest = 2
+ ApplicationSearchRequest = 3
+ ApplicationSearchResultEntry = 4
+ ApplicationSearchResultDone = 5
+ ApplicationModifyRequest = 6
+ ApplicationModifyResponse = 7
+ ApplicationAddRequest = 8
+ ApplicationAddResponse = 9
+ ApplicationDelRequest = 10
+ ApplicationDelResponse = 11
+ ApplicationModifyDNRequest = 12
+ ApplicationModifyDNResponse = 13
+ ApplicationCompareRequest = 14
+ ApplicationCompareResponse = 15
+ ApplicationAbandonRequest = 16
+ ApplicationSearchResultReference = 19
+ ApplicationExtendedRequest = 23
+ ApplicationExtendedResponse = 24
+)
+
+var ApplicationMap = map[uint8]string{
+ ApplicationBindRequest: "Bind Request",
+ ApplicationBindResponse: "Bind Response",
+ ApplicationUnbindRequest: "Unbind Request",
+ ApplicationSearchRequest: "Search Request",
+ ApplicationSearchResultEntry: "Search Result Entry",
+ ApplicationSearchResultDone: "Search Result Done",
+ ApplicationModifyRequest: "Modify Request",
+ ApplicationModifyResponse: "Modify Response",
+ ApplicationAddRequest: "Add Request",
+ ApplicationAddResponse: "Add Response",
+ ApplicationDelRequest: "Del Request",
+ ApplicationDelResponse: "Del Response",
+ ApplicationModifyDNRequest: "Modify DN Request",
+ ApplicationModifyDNResponse: "Modify DN Response",
+ ApplicationCompareRequest: "Compare Request",
+ ApplicationCompareResponse: "Compare Response",
+ ApplicationAbandonRequest: "Abandon Request",
+ ApplicationSearchResultReference: "Search Result Reference",
+ ApplicationExtendedRequest: "Extended Request",
+ ApplicationExtendedResponse: "Extended Response",
+}
+
+// Ldap Behera Password Policy Draft 10 (https://tools.ietf.org/html/draft-behera-ldap-password-policy-10)
+const (
+ BeheraPasswordExpired = 0
+ BeheraAccountLocked = 1
+ BeheraChangeAfterReset = 2
+ BeheraPasswordModNotAllowed = 3
+ BeheraMustSupplyOldPassword = 4
+ BeheraInsufficientPasswordQuality = 5
+ BeheraPasswordTooShort = 6
+ BeheraPasswordTooYoung = 7
+ BeheraPasswordInHistory = 8
+)
+
+var BeheraPasswordPolicyErrorMap = map[int8]string{
+ BeheraPasswordExpired: "Password expired",
+ BeheraAccountLocked: "Account locked",
+ BeheraChangeAfterReset: "Password must be changed",
+ BeheraPasswordModNotAllowed: "Policy prevents password modification",
+ BeheraMustSupplyOldPassword: "Policy requires old password in order to change password",
+ BeheraInsufficientPasswordQuality: "Password fails quality checks",
+ BeheraPasswordTooShort: "Password is too short for policy",
+ BeheraPasswordTooYoung: "Password has been changed too recently",
+ BeheraPasswordInHistory: "New password is in list of old passwords",
+}
+
+// Adds descriptions to an LDAP Response packet for debugging
+func addLDAPDescriptions(packet *ber.Packet) (err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ err = NewError(ErrorDebugging, errors.New("ldap: cannot process packet to add descriptions"))
+ }
+ }()
+ packet.Description = "LDAP Response"
+ packet.Children[0].Description = "Message ID"
+
+ application := uint8(packet.Children[1].Tag)
+ packet.Children[1].Description = ApplicationMap[application]
+
+ switch application {
+ case ApplicationBindRequest:
+ addRequestDescriptions(packet)
+ case ApplicationBindResponse:
+ addDefaultLDAPResponseDescriptions(packet)
+ case ApplicationUnbindRequest:
+ addRequestDescriptions(packet)
+ case ApplicationSearchRequest:
+ addRequestDescriptions(packet)
+ case ApplicationSearchResultEntry:
+ packet.Children[1].Children[0].Description = "Object Name"
+ packet.Children[1].Children[1].Description = "Attributes"
+ for _, child := range packet.Children[1].Children[1].Children {
+ child.Description = "Attribute"
+ child.Children[0].Description = "Attribute Name"
+ child.Children[1].Description = "Attribute Values"
+ for _, grandchild := range child.Children[1].Children {
+ grandchild.Description = "Attribute Value"
+ }
+ }
+ if len(packet.Children) == 3 {
+ addControlDescriptions(packet.Children[2])
+ }
+ case ApplicationSearchResultDone:
+ addDefaultLDAPResponseDescriptions(packet)
+ case ApplicationModifyRequest:
+ addRequestDescriptions(packet)
+ case ApplicationModifyResponse:
+ case ApplicationAddRequest:
+ addRequestDescriptions(packet)
+ case ApplicationAddResponse:
+ case ApplicationDelRequest:
+ addRequestDescriptions(packet)
+ case ApplicationDelResponse:
+ case ApplicationModifyDNRequest:
+ addRequestDescriptions(packet)
+ case ApplicationModifyDNResponse:
+ case ApplicationCompareRequest:
+ addRequestDescriptions(packet)
+ case ApplicationCompareResponse:
+ case ApplicationAbandonRequest:
+ addRequestDescriptions(packet)
+ case ApplicationSearchResultReference:
+ case ApplicationExtendedRequest:
+ addRequestDescriptions(packet)
+ case ApplicationExtendedResponse:
+ }
+
+ return nil
+}
+
+func addControlDescriptions(packet *ber.Packet) {
+ packet.Description = "Controls"
+ for _, child := range packet.Children {
+ child.Description = "Control"
+ child.Children[0].Description = "Control Type (" + ControlTypeMap[child.Children[0].Value.(string)] + ")"
+ value := child.Children[1]
+ if len(child.Children) == 3 {
+ child.Children[1].Description = "Criticality"
+ value = child.Children[2]
+ }
+ value.Description = "Control Value"
+
+ switch child.Children[0].Value.(string) {
+ case ControlTypePaging:
+ value.Description += " (Paging)"
+ if value.Value != nil {
+ valueChildren := ber.DecodePacket(value.Data.Bytes())
+ value.Data.Truncate(0)
+ value.Value = nil
+ valueChildren.Children[1].Value = valueChildren.Children[1].Data.Bytes()
+ value.AppendChild(valueChildren)
+ }
+ value.Children[0].Description = "Real Search Control Value"
+ value.Children[0].Children[0].Description = "Paging Size"
+ value.Children[0].Children[1].Description = "Cookie"
+
+ case ControlTypeBeheraPasswordPolicy:
+ value.Description += " (Password Policy - Behera Draft)"
+ if value.Value != nil {
+ valueChildren := ber.DecodePacket(value.Data.Bytes())
+ value.Data.Truncate(0)
+ value.Value = nil
+ value.AppendChild(valueChildren)
+ }
+ sequence := value.Children[0]
+ for _, child := range sequence.Children {
+ if child.Tag == 0 {
+ //Warning
+ child := child.Children[0]
+ packet := ber.DecodePacket(child.Data.Bytes())
+ val, ok := packet.Value.(int64)
+ if ok {
+ if child.Tag == 0 {
+ //timeBeforeExpiration
+ value.Description += " (TimeBeforeExpiration)"
+ child.Value = val
+ } else if child.Tag == 1 {
+ //graceAuthNsRemaining
+ value.Description += " (GraceAuthNsRemaining)"
+ child.Value = val
+ }
+ }
+ } else if child.Tag == 1 {
+ // Error
+ packet := ber.DecodePacket(child.Data.Bytes())
+ val, ok := packet.Value.(int8)
+ if !ok {
+ val = -1
+ }
+ child.Description = "Error"
+ child.Value = val
+ }
+ }
+ }
+ }
+}
+
+func addRequestDescriptions(packet *ber.Packet) {
+ packet.Description = "LDAP Request"
+ packet.Children[0].Description = "Message ID"
+ packet.Children[1].Description = ApplicationMap[uint8(packet.Children[1].Tag)]
+ if len(packet.Children) == 3 {
+ addControlDescriptions(packet.Children[2])
+ }
+}
+
+func addDefaultLDAPResponseDescriptions(packet *ber.Packet) {
+ resultCode, _ := getLDAPResultCode(packet)
+ packet.Children[1].Children[0].Description = "Result Code (" + LDAPResultCodeMap[resultCode] + ")"
+ packet.Children[1].Children[1].Description = "Matched DN"
+ packet.Children[1].Children[2].Description = "Error Message"
+ if len(packet.Children[1].Children) > 3 {
+ packet.Children[1].Children[3].Description = "Referral"
+ }
+ if len(packet.Children) == 3 {
+ addControlDescriptions(packet.Children[2])
+ }
+}
+
+func DebugBinaryFile(fileName string) error {
+ file, err := ioutil.ReadFile(fileName)
+ if err != nil {
+ return NewError(ErrorDebugging, err)
+ }
+ ber.PrintBytes(os.Stdout, file, "")
+ packet := ber.DecodePacket(file)
+ addLDAPDescriptions(packet)
+ ber.PrintPacket(packet)
+
+ return nil
+}
+
+var hex = "0123456789abcdef"
+
+func mustEscape(c byte) bool {
+ return c > 0x7f || c == '(' || c == ')' || c == '\\' || c == '*' || c == 0
+}
+
+// EscapeFilter escapes from the provided LDAP filter string the special
+// characters in the set `()*\` and those out of the range 0 < c < 0x80,
+// as defined in RFC4515.
+func EscapeFilter(filter string) string {
+ escape := 0
+ for i := 0; i < len(filter); i++ {
+ if mustEscape(filter[i]) {
+ escape++
+ }
+ }
+ if escape == 0 {
+ return filter
+ }
+ buf := make([]byte, len(filter)+escape*2)
+ for i, j := 0, 0; i < len(filter); i++ {
+ c := filter[i]
+ if mustEscape(c) {
+ buf[j+0] = '\\'
+ buf[j+1] = hex[c>>4]
+ buf[j+2] = hex[c&0xf]
+ j += 3
+ } else {
+ buf[j] = c
+ j++
+ }
+ }
+ return string(buf)
+}
diff --git a/vendor/github.com/go-ldap/ldap/modify.go b/vendor/github.com/go-ldap/ldap/modify.go
new file mode 100644
index 000000000..5c042af79
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/modify.go
@@ -0,0 +1,160 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//
+// File contains Modify functionality
+//
+// https://tools.ietf.org/html/rfc4511
+//
+// ModifyRequest ::= [APPLICATION 6] SEQUENCE {
+// object LDAPDN,
+// changes SEQUENCE OF change SEQUENCE {
+// operation ENUMERATED {
+// add (0),
+// delete (1),
+// replace (2),
+// ... },
+// modification PartialAttribute } }
+//
+// PartialAttribute ::= SEQUENCE {
+// type AttributeDescription,
+// vals SET OF value AttributeValue }
+//
+// AttributeDescription ::= LDAPString
+// -- Constrained to <attributedescription>
+// -- [RFC4512]
+//
+// AttributeValue ::= OCTET STRING
+//
+
+package ldap
+
+import (
+ "errors"
+ "log"
+
+ "gopkg.in/asn1-ber.v1"
+)
+
+const (
+ AddAttribute = 0
+ DeleteAttribute = 1
+ ReplaceAttribute = 2
+)
+
+type PartialAttribute struct {
+ attrType string
+ attrVals []string
+}
+
+func (p *PartialAttribute) encode() *ber.Packet {
+ seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "PartialAttribute")
+ seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, p.attrType, "Type"))
+ set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
+ for _, value := range p.attrVals {
+ set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
+ }
+ seq.AppendChild(set)
+ return seq
+}
+
+type ModifyRequest struct {
+ dn string
+ addAttributes []PartialAttribute
+ deleteAttributes []PartialAttribute
+ replaceAttributes []PartialAttribute
+}
+
+func (m *ModifyRequest) Add(attrType string, attrVals []string) {
+ m.addAttributes = append(m.addAttributes, PartialAttribute{attrType: attrType, attrVals: attrVals})
+}
+
+func (m *ModifyRequest) Delete(attrType string, attrVals []string) {
+ m.deleteAttributes = append(m.deleteAttributes, PartialAttribute{attrType: attrType, attrVals: attrVals})
+}
+
+func (m *ModifyRequest) Replace(attrType string, attrVals []string) {
+ m.replaceAttributes = append(m.replaceAttributes, PartialAttribute{attrType: attrType, attrVals: attrVals})
+}
+
+func (m ModifyRequest) encode() *ber.Packet {
+ request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyRequest, nil, "Modify Request")
+ request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.dn, "DN"))
+ changes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Changes")
+ for _, attribute := range m.addAttributes {
+ change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
+ change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(AddAttribute), "Operation"))
+ change.AppendChild(attribute.encode())
+ changes.AppendChild(change)
+ }
+ for _, attribute := range m.deleteAttributes {
+ change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
+ change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(DeleteAttribute), "Operation"))
+ change.AppendChild(attribute.encode())
+ changes.AppendChild(change)
+ }
+ for _, attribute := range m.replaceAttributes {
+ change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change")
+ change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(ReplaceAttribute), "Operation"))
+ change.AppendChild(attribute.encode())
+ changes.AppendChild(change)
+ }
+ request.AppendChild(changes)
+ return request
+}
+
+func NewModifyRequest(
+ dn string,
+) *ModifyRequest {
+ return &ModifyRequest{
+ dn: dn,
+ }
+}
+
+func (l *Conn) Modify(modifyRequest *ModifyRequest) error {
+ messageID := l.nextMessageID()
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ packet.AppendChild(modifyRequest.encode())
+
+ l.Debug.PrintPacket(packet)
+
+ channel, err := l.sendMessage(packet)
+ if err != nil {
+ return err
+ }
+ if channel == nil {
+ return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
+ }
+ defer l.finishMessage(messageID)
+
+ l.Debug.Printf("%d: waiting for response", messageID)
+ packetResponse, ok := <-channel
+ if !ok {
+ return NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ }
+ packet, err = packetResponse.ReadPacket()
+ l.Debug.Printf("%d: got response %p", messageID, packet)
+ if err != nil {
+ return err
+ }
+
+ if l.Debug {
+ if err := addLDAPDescriptions(packet); err != nil {
+ return err
+ }
+ ber.PrintPacket(packet)
+ }
+
+ if packet.Children[1].Tag == ApplicationModifyResponse {
+ resultCode, resultDescription := getLDAPResultCode(packet)
+ if resultCode != 0 {
+ return NewError(resultCode, errors.New(resultDescription))
+ }
+ } else {
+ log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
+ }
+
+ l.Debug.Printf("%d: returning", messageID)
+ return nil
+}
diff --git a/vendor/github.com/go-ldap/ldap/passwdmodify.go b/vendor/github.com/go-ldap/ldap/passwdmodify.go
new file mode 100644
index 000000000..6d5ca975a
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/passwdmodify.go
@@ -0,0 +1,144 @@
+// This file contains the password modify extended operation as specified in rfc 3062
+//
+// https://tools.ietf.org/html/rfc3062
+//
+
+package ldap
+
+import (
+ "errors"
+ "fmt"
+
+ "gopkg.in/asn1-ber.v1"
+)
+
+const (
+ passwordModifyOID = "1.3.6.1.4.1.4203.1.11.1"
+)
+
+type PasswordModifyRequest struct {
+ UserIdentity string
+ OldPassword string
+ NewPassword string
+}
+
+type PasswordModifyResult struct {
+ GeneratedPassword string
+}
+
+func (r *PasswordModifyRequest) encode() (*ber.Packet, error) {
+ request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Password Modify Extended Operation")
+ request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, passwordModifyOID, "Extended Request Name: Password Modify OID"))
+ extendedRequestValue := ber.Encode(ber.ClassContext, ber.TypePrimitive, 1, nil, "Extended Request Value: Password Modify Request")
+ passwordModifyRequestValue := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Password Modify Request")
+ if r.UserIdentity != "" {
+ passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, r.UserIdentity, "User Identity"))
+ }
+ if r.OldPassword != "" {
+ passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 1, r.OldPassword, "Old Password"))
+ }
+ if r.NewPassword != "" {
+ passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 2, r.NewPassword, "New Password"))
+ }
+
+ extendedRequestValue.AppendChild(passwordModifyRequestValue)
+ request.AppendChild(extendedRequestValue)
+
+ return request, nil
+}
+
+// Create a new PasswordModifyRequest
+//
+// According to the RFC 3602:
+// userIdentity is a string representing the user associated with the request.
+// This string may or may not be an LDAPDN (RFC 2253).
+// If userIdentity is empty then the operation will act on the user associated
+// with the session.
+//
+// oldPassword is the current user's password, it can be empty or it can be
+// needed depending on the session user access rights (usually an administrator
+// can change a user's password without knowing the current one) and the
+// password policy (see pwdSafeModify password policy's attribute)
+//
+// newPassword is the desired user's password. If empty the server can return
+// an error or generate a new password that will be available in the
+// PasswordModifyResult.GeneratedPassword
+//
+func NewPasswordModifyRequest(userIdentity string, oldPassword string, newPassword string) *PasswordModifyRequest {
+ return &PasswordModifyRequest{
+ UserIdentity: userIdentity,
+ OldPassword: oldPassword,
+ NewPassword: newPassword,
+ }
+}
+
+func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error) {
+ messageID := l.nextMessageID()
+
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+
+ encodedPasswordModifyRequest, err := passwordModifyRequest.encode()
+ if err != nil {
+ return nil, err
+ }
+ packet.AppendChild(encodedPasswordModifyRequest)
+
+ l.Debug.PrintPacket(packet)
+
+ channel, err := l.sendMessage(packet)
+ if err != nil {
+ return nil, err
+ }
+ if channel == nil {
+ return nil, NewError(ErrorNetwork, errors.New("ldap: could not send message"))
+ }
+ defer l.finishMessage(messageID)
+
+ result := &PasswordModifyResult{}
+
+ l.Debug.Printf("%d: waiting for response", messageID)
+ packetResponse, ok := <-channel
+ if !ok {
+ return nil, NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ }
+ packet, err = packetResponse.ReadPacket()
+ l.Debug.Printf("%d: got response %p", messageID, packet)
+ if err != nil {
+ return nil, err
+ }
+
+ if packet == nil {
+ return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve message"))
+ }
+
+ if l.Debug {
+ if err := addLDAPDescriptions(packet); err != nil {
+ return nil, err
+ }
+ ber.PrintPacket(packet)
+ }
+
+ if packet.Children[1].Tag == ApplicationExtendedResponse {
+ resultCode, resultDescription := getLDAPResultCode(packet)
+ if resultCode != 0 {
+ return nil, NewError(resultCode, errors.New(resultDescription))
+ }
+ } else {
+ return nil, NewError(ErrorUnexpectedResponse, fmt.Errorf("Unexpected Response: %d", packet.Children[1].Tag))
+ }
+
+ extendedResponse := packet.Children[1]
+ for _, child := range extendedResponse.Children {
+ if child.Tag == 11 {
+ passwordModifyReponseValue := ber.DecodePacket(child.Data.Bytes())
+ if len(passwordModifyReponseValue.Children) == 1 {
+ if passwordModifyReponseValue.Children[0].Tag == 0 {
+ result.GeneratedPassword = ber.DecodeString(passwordModifyReponseValue.Children[0].Data.Bytes())
+ }
+ }
+ }
+ }
+
+ return result, nil
+}
diff --git a/vendor/github.com/go-ldap/ldap/search.go b/vendor/github.com/go-ldap/ldap/search.go
new file mode 100644
index 000000000..7e9495bdc
--- /dev/null
+++ b/vendor/github.com/go-ldap/ldap/search.go
@@ -0,0 +1,426 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//
+// File contains Search functionality
+//
+// https://tools.ietf.org/html/rfc4511
+//
+// SearchRequest ::= [APPLICATION 3] SEQUENCE {
+// baseObject LDAPDN,
+// scope ENUMERATED {
+// baseObject (0),
+// singleLevel (1),
+// wholeSubtree (2),
+// ... },
+// derefAliases ENUMERATED {
+// neverDerefAliases (0),
+// derefInSearching (1),
+// derefFindingBaseObj (2),
+// derefAlways (3) },
+// sizeLimit INTEGER (0 .. maxInt),
+// timeLimit INTEGER (0 .. maxInt),
+// typesOnly BOOLEAN,
+// filter Filter,
+// attributes AttributeSelection }
+//
+// AttributeSelection ::= SEQUENCE OF selector LDAPString
+// -- The LDAPString is constrained to
+// -- <attributeSelector> in Section 4.5.1.8
+//
+// Filter ::= CHOICE {
+// and [0] SET SIZE (1..MAX) OF filter Filter,
+// or [1] SET SIZE (1..MAX) OF filter Filter,
+// not [2] Filter,
+// equalityMatch [3] AttributeValueAssertion,
+// substrings [4] SubstringFilter,
+// greaterOrEqual [5] AttributeValueAssertion,
+// lessOrEqual [6] AttributeValueAssertion,
+// present [7] AttributeDescription,
+// approxMatch [8] AttributeValueAssertion,
+// extensibleMatch [9] MatchingRuleAssertion,
+// ... }
+//
+// SubstringFilter ::= SEQUENCE {
+// type AttributeDescription,
+// substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE {
+// initial [0] AssertionValue, -- can occur at most once
+// any [1] AssertionValue,
+// final [2] AssertionValue } -- can occur at most once
+// }
+//
+// MatchingRuleAssertion ::= SEQUENCE {
+// matchingRule [1] MatchingRuleId OPTIONAL,
+// type [2] AttributeDescription OPTIONAL,
+// matchValue [3] AssertionValue,
+// dnAttributes [4] BOOLEAN DEFAULT FALSE }
+//
+//
+
+package ldap
+
+import (
+ "errors"
+ "fmt"
+ "sort"
+ "strings"
+
+ "gopkg.in/asn1-ber.v1"
+)
+
+const (
+ ScopeBaseObject = 0
+ ScopeSingleLevel = 1
+ ScopeWholeSubtree = 2
+)
+
+var ScopeMap = map[int]string{
+ ScopeBaseObject: "Base Object",
+ ScopeSingleLevel: "Single Level",
+ ScopeWholeSubtree: "Whole Subtree",
+}
+
+const (
+ NeverDerefAliases = 0
+ DerefInSearching = 1
+ DerefFindingBaseObj = 2
+ DerefAlways = 3
+)
+
+var DerefMap = map[int]string{
+ NeverDerefAliases: "NeverDerefAliases",
+ DerefInSearching: "DerefInSearching",
+ DerefFindingBaseObj: "DerefFindingBaseObj",
+ DerefAlways: "DerefAlways",
+}
+
+// NewEntry returns an Entry object with the specified distinguished name and attribute key-value pairs.
+// The map of attributes is accessed in alphabetical order of the keys in order to ensure that, for the
+// same input map of attributes, the output entry will contain the same order of attributes
+func NewEntry(dn string, attributes map[string][]string) *Entry {
+ var attributeNames []string
+ for attributeName := range attributes {
+ attributeNames = append(attributeNames, attributeName)
+ }
+ sort.Strings(attributeNames)
+
+ var encodedAttributes []*EntryAttribute
+ for _, attributeName := range attributeNames {
+ encodedAttributes = append(encodedAttributes, NewEntryAttribute(attributeName, attributes[attributeName]))
+ }
+ return &Entry{
+ DN: dn,
+ Attributes: encodedAttributes,
+ }
+}
+
+type Entry struct {
+ DN string
+ Attributes []*EntryAttribute
+}
+
+func (e *Entry) GetAttributeValues(attribute string) []string {
+ for _, attr := range e.Attributes {
+ if attr.Name == attribute {
+ return attr.Values
+ }
+ }
+ return []string{}
+}
+
+func (e *Entry) GetRawAttributeValues(attribute string) [][]byte {
+ for _, attr := range e.Attributes {
+ if attr.Name == attribute {
+ return attr.ByteValues
+ }
+ }
+ return [][]byte{}
+}
+
+func (e *Entry) GetAttributeValue(attribute string) string {
+ values := e.GetAttributeValues(attribute)
+ if len(values) == 0 {
+ return ""
+ }
+ return values[0]
+}
+
+func (e *Entry) GetRawAttributeValue(attribute string) []byte {
+ values := e.GetRawAttributeValues(attribute)
+ if len(values) == 0 {
+ return []byte{}
+ }
+ return values[0]
+}
+
+func (e *Entry) Print() {
+ fmt.Printf("DN: %s\n", e.DN)
+ for _, attr := range e.Attributes {
+ attr.Print()
+ }
+}
+
+func (e *Entry) PrettyPrint(indent int) {
+ fmt.Printf("%sDN: %s\n", strings.Repeat(" ", indent), e.DN)
+ for _, attr := range e.Attributes {
+ attr.PrettyPrint(indent + 2)
+ }
+}
+
+// NewEntryAttribute returns a new EntryAttribute with the desired key-value pair
+func NewEntryAttribute(name string, values []string) *EntryAttribute {
+ var bytes [][]byte
+ for _, value := range values {
+ bytes = append(bytes, []byte(value))
+ }
+ return &EntryAttribute{
+ Name: name,
+ Values: values,
+ ByteValues: bytes,
+ }
+}
+
+type EntryAttribute struct {
+ Name string
+ Values []string
+ ByteValues [][]byte
+}
+
+func (e *EntryAttribute) Print() {
+ fmt.Printf("%s: %s\n", e.Name, e.Values)
+}
+
+func (e *EntryAttribute) PrettyPrint(indent int) {
+ fmt.Printf("%s%s: %s\n", strings.Repeat(" ", indent), e.Name, e.Values)
+}
+
+type SearchResult struct {
+ Entries []*Entry
+ Referrals []string
+ Controls []Control
+}
+
+func (s *SearchResult) Print() {
+ for _, entry := range s.Entries {
+ entry.Print()
+ }
+}
+
+func (s *SearchResult) PrettyPrint(indent int) {
+ for _, entry := range s.Entries {
+ entry.PrettyPrint(indent)
+ }
+}
+
+type SearchRequest struct {
+ BaseDN string
+ Scope int
+ DerefAliases int
+ SizeLimit int
+ TimeLimit int
+ TypesOnly bool
+ Filter string
+ Attributes []string
+ Controls []Control
+}
+
+func (s *SearchRequest) encode() (*ber.Packet, error) {
+ request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationSearchRequest, nil, "Search Request")
+ request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, s.BaseDN, "Base DN"))
+ request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.Scope), "Scope"))
+ request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.DerefAliases), "Deref Aliases"))
+ request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.SizeLimit), "Size Limit"))
+ request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.TimeLimit), "Time Limit"))
+ request.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, s.TypesOnly, "Types Only"))
+ // compile and encode filter
+ filterPacket, err := CompileFilter(s.Filter)
+ if err != nil {
+ return nil, err
+ }
+ request.AppendChild(filterPacket)
+ // encode attributes
+ attributesPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
+ for _, attribute := range s.Attributes {
+ attributesPacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
+ }
+ request.AppendChild(attributesPacket)
+ return request, nil
+}
+
+func NewSearchRequest(
+ BaseDN string,
+ Scope, DerefAliases, SizeLimit, TimeLimit int,
+ TypesOnly bool,
+ Filter string,
+ Attributes []string,
+ Controls []Control,
+) *SearchRequest {
+ return &SearchRequest{
+ BaseDN: BaseDN,
+ Scope: Scope,
+ DerefAliases: DerefAliases,
+ SizeLimit: SizeLimit,
+ TimeLimit: TimeLimit,
+ TypesOnly: TypesOnly,
+ Filter: Filter,
+ Attributes: Attributes,
+ Controls: Controls,
+ }
+}
+
+// SearchWithPaging accepts a search request and desired page size in order to execute LDAP queries to fulfill the
+// search request. All paged LDAP query responses will be buffered and the final result will be returned atomically.
+// The following four cases are possible given the arguments:
+// - given SearchRequest missing a control of type ControlTypePaging: we will add one with the desired paging size
+// - given SearchRequest contains a control of type ControlTypePaging that isn't actually a ControlPaging: fail without issuing any queries
+// - given SearchRequest contains a control of type ControlTypePaging with pagingSize equal to the size requested: no change to the search request
+// - given SearchRequest contains a control of type ControlTypePaging with pagingSize not equal to the size requested: fail without issuing any queries
+// A requested pagingSize of 0 is interpreted as no limit by LDAP servers.
+func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) {
+ var pagingControl *ControlPaging
+
+ control := FindControl(searchRequest.Controls, ControlTypePaging)
+ if control == nil {
+ pagingControl = NewControlPaging(pagingSize)
+ searchRequest.Controls = append(searchRequest.Controls, pagingControl)
+ } else {
+ castControl, ok := control.(*ControlPaging)
+ if !ok {
+ return nil, fmt.Errorf("Expected paging control to be of type *ControlPaging, got %v", control)
+ }
+ if castControl.PagingSize != pagingSize {
+ return nil, fmt.Errorf("Paging size given in search request (%d) conflicts with size given in search call (%d)", castControl.PagingSize, pagingSize)
+ }
+ pagingControl = castControl
+ }
+
+ searchResult := new(SearchResult)
+ for {
+ result, err := l.Search(searchRequest)
+ l.Debug.Printf("Looking for Paging Control...")
+ if err != nil {
+ return searchResult, err
+ }
+ if result == nil {
+ return searchResult, NewError(ErrorNetwork, errors.New("ldap: packet not received"))
+ }
+
+ for _, entry := range result.Entries {
+ searchResult.Entries = append(searchResult.Entries, entry)
+ }
+ for _, referral := range result.Referrals {
+ searchResult.Referrals = append(searchResult.Referrals, referral)
+ }
+ for _, control := range result.Controls {
+ searchResult.Controls = append(searchResult.Controls, control)
+ }
+
+ l.Debug.Printf("Looking for Paging Control...")
+ pagingResult := FindControl(result.Controls, ControlTypePaging)
+ if pagingResult == nil {
+ pagingControl = nil
+ l.Debug.Printf("Could not find paging control. Breaking...")
+ break
+ }
+
+ cookie := pagingResult.(*ControlPaging).Cookie
+ if len(cookie) == 0 {
+ pagingControl = nil
+ l.Debug.Printf("Could not find cookie. Breaking...")
+ break
+ }
+ pagingControl.SetCookie(cookie)
+ }
+
+ if pagingControl != nil {
+ l.Debug.Printf("Abandoning Paging...")
+ pagingControl.PagingSize = 0
+ l.Search(searchRequest)
+ }
+
+ return searchResult, nil
+}
+
+func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
+ messageID := l.nextMessageID()
+ packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
+ packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
+ // encode search request
+ encodedSearchRequest, err := searchRequest.encode()
+ if err != nil {
+ return nil, err
+ }
+ packet.AppendChild(encodedSearchRequest)
+ // encode search controls
+ if searchRequest.Controls != nil {
+ packet.AppendChild(encodeControls(searchRequest.Controls))
+ }
+
+ l.Debug.PrintPacket(packet)
+
+ channel, err := l.sendMessage(packet)
+ if err != nil {
+ return nil, err
+ }
+ if channel == nil {
+ return nil, NewError(ErrorNetwork, errors.New("ldap: could not send message"))
+ }
+ defer l.finishMessage(messageID)
+
+ result := &SearchResult{
+ Entries: make([]*Entry, 0),
+ Referrals: make([]string, 0),
+ Controls: make([]Control, 0)}
+
+ foundSearchResultDone := false
+ for !foundSearchResultDone {
+ l.Debug.Printf("%d: waiting for response", messageID)
+ packetResponse, ok := <-channel
+ if !ok {
+ return nil, NewError(ErrorNetwork, errors.New("ldap: channel closed"))
+ }
+ packet, err = packetResponse.ReadPacket()
+ l.Debug.Printf("%d: got response %p", messageID, packet)
+ if err != nil {
+ return nil, err
+ }
+
+ if l.Debug {
+ if err := addLDAPDescriptions(packet); err != nil {
+ return nil, err
+ }
+ ber.PrintPacket(packet)
+ }
+
+ switch packet.Children[1].Tag {
+ case 4:
+ entry := new(Entry)
+ entry.DN = packet.Children[1].Children[0].Value.(string)
+ for _, child := range packet.Children[1].Children[1].Children {
+ attr := new(EntryAttribute)
+ attr.Name = child.Children[0].Value.(string)
+ for _, value := range child.Children[1].Children {
+ attr.Values = append(attr.Values, value.Value.(string))
+ attr.ByteValues = append(attr.ByteValues, value.ByteValue)
+ }
+ entry.Attributes = append(entry.Attributes, attr)
+ }
+ result.Entries = append(result.Entries, entry)
+ case 5:
+ resultCode, resultDescription := getLDAPResultCode(packet)
+ if resultCode != 0 {
+ return result, NewError(resultCode, errors.New(resultDescription))
+ }
+ if len(packet.Children) == 3 {
+ for _, child := range packet.Children[2].Children {
+ result.Controls = append(result.Controls, DecodeControl(child))
+ }
+ }
+ foundSearchResultDone = true
+ case 19:
+ result.Referrals = append(result.Referrals, packet.Children[1].Children[0].Value.(string))
+ }
+ }
+ l.Debug.Printf("%d: returning", messageID)
+ return result, nil
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/.gitignore b/vendor/github.com/go-sql-driver/mysql/.gitignore
new file mode 100644
index 000000000..ba8e0cb3a
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/.gitignore
@@ -0,0 +1,8 @@
+.DS_Store
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+Icon?
+ehthumbs.db
+Thumbs.db
diff --git a/vendor/github.com/go-sql-driver/mysql/.travis.yml b/vendor/github.com/go-sql-driver/mysql/.travis.yml
new file mode 100644
index 000000000..211969dfa
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/.travis.yml
@@ -0,0 +1,12 @@
+sudo: false
+language: go
+go:
+ - 1.2
+ - 1.3
+ - 1.4
+ - 1.5
+ - 1.6
+ - tip
+
+before_script:
+ - mysql -e 'create database gotest;'
diff --git a/vendor/github.com/go-sql-driver/mysql/AUTHORS b/vendor/github.com/go-sql-driver/mysql/AUTHORS
new file mode 100644
index 000000000..3774919d7
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/AUTHORS
@@ -0,0 +1,52 @@
+# This is the official list of Go-MySQL-Driver authors for copyright purposes.
+
+# If you are submitting a patch, please add your name or the name of the
+# organization which holds the copyright to this list in alphabetical order.
+
+# Names should be added to this file as
+# Name <email address>
+# The email address is not required for organizations.
+# Please keep the list sorted.
+
+
+# Individual Persons
+
+Aaron Hopkins <go-sql-driver at die.net>
+Arne Hormann <arnehormann at gmail.com>
+Carlos Nieto <jose.carlos at menteslibres.net>
+Chris Moos <chris at tech9computers.com>
+Daniel Nichter <nil at codenode.com>
+Daniël van Eeden <git at myname.nl>
+DisposaBoy <disposaboy at dby.me>
+Frederick Mayle <frederickmayle at gmail.com>
+Gustavo Kristic <gkristic at gmail.com>
+Hanno Braun <mail at hannobraun.com>
+Henri Yandell <flamefew at gmail.com>
+Hirotaka Yamamoto <ymmt2005 at gmail.com>
+INADA Naoki <songofacandy at gmail.com>
+James Harr <james.harr at gmail.com>
+Jian Zhen <zhenjl at gmail.com>
+Joshua Prunier <joshua.prunier at gmail.com>
+Julien Lefevre <julien.lefevr at gmail.com>
+Julien Schmidt <go-sql-driver at julienschmidt.com>
+Kamil Dziedzic <kamil at klecza.pl>
+Kevin Malachowski <kevin at chowski.com>
+Leonardo YongUk Kim <dalinaum at gmail.com>
+Luca Looz <luca.looz92 at gmail.com>
+Lucas Liu <extrafliu at gmail.com>
+Luke Scott <luke at webconnex.com>
+Michael Woolnough <michael.woolnough at gmail.com>
+Nicola Peduzzi <thenikso at gmail.com>
+Paul Bonser <misterpib at gmail.com>
+Runrioter Wung <runrioter at gmail.com>
+Soroush Pour <me at soroushjp.com>
+Stan Putrya <root.vagner at gmail.com>
+Stanley Gunawan <gunawan.stanley at gmail.com>
+Xiaobing Jiang <s7v7nislands at gmail.com>
+Xiuming Chen <cc at cxm.cc>
+
+# Organizations
+
+Barracuda Networks, Inc.
+Google Inc.
+Stripe Inc.
diff --git a/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md b/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md
new file mode 100644
index 000000000..381d91825
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/CHANGELOG.md
@@ -0,0 +1,103 @@
+## HEAD
+
+Changes:
+
+ - Go 1.1 is no longer supported
+ - Use decimals field from MySQL to format time types (#249)
+ - Buffer optimizations (#269)
+ - TLS ServerName defaults to the host (#283)
+
+Bugfixes:
+
+ - Enable microsecond resolution on TIME, DATETIME and TIMESTAMP (#249)
+ - Fixed handling of queries without columns and rows (#255)
+ - Fixed a panic when SetKeepAlive() failed (#298)
+ - Support receiving ERR packet while reading rows (#321)
+ - Fixed reading NULL length-encoded integers in MySQL 5.6+ (#349)
+ - Fixed absolute paths support in LOAD LOCAL DATA INFILE (#356)
+ - Actually zero out bytes in handshake response (#378)
+ - Fixed race condition in registering LOAD DATA INFILE handler (#383)
+ - Fixed tests with MySQL 5.7.9+ (#380)
+ - QueryUnescape TLS config names (#397)
+ - Fixed "broken pipe" error by writing to closed socket (#390)
+
+New Features:
+ - Support for returning table alias on Columns() (#289, #359, #382)
+ - Placeholder interpolation, can be actived with the DSN parameter `interpolateParams=true` (#309, #318)
+ - Support for uint64 parameters with high bit set (#332, #345)
+ - Cleartext authentication plugin support (#327)
+
+
+
+## Version 1.2 (2014-06-03)
+
+Changes:
+
+ - We switched back to a "rolling release". `go get` installs the current master branch again
+ - Version v1 of the driver will not be maintained anymore. Go 1.0 is no longer supported by this driver
+ - Exported errors to allow easy checking from application code
+ - Enabled TCP Keepalives on TCP connections
+ - Optimized INFILE handling (better buffer size calculation, lazy init, ...)
+ - The DSN parser also checks for a missing separating slash
+ - Faster binary date / datetime to string formatting
+ - Also exported the MySQLWarning type
+ - mysqlConn.Close returns the first error encountered instead of ignoring all errors
+ - writePacket() automatically writes the packet size to the header
+ - readPacket() uses an iterative approach instead of the recursive approach to merge splitted packets
+
+New Features:
+
+ - `RegisterDial` allows the usage of a custom dial function to establish the network connection
+ - Setting the connection collation is possible with the `collation` DSN parameter. This parameter should be preferred over the `charset` parameter
+ - Logging of critical errors is configurable with `SetLogger`
+ - Google CloudSQL support
+
+Bugfixes:
+
+ - Allow more than 32 parameters in prepared statements
+ - Various old_password fixes
+ - Fixed TestConcurrent test to pass Go's race detection
+ - Fixed appendLengthEncodedInteger for large numbers
+ - Renamed readLengthEnodedString to readLengthEncodedString and skipLengthEnodedString to skipLengthEncodedString (fixed typo)
+
+
+## Version 1.1 (2013-11-02)
+
+Changes:
+
+ - Go-MySQL-Driver now requires Go 1.1
+ - Connections now use the collation `utf8_general_ci` by default. Adding `&charset=UTF8` to the DSN should not be necessary anymore
+ - Made closing rows and connections error tolerant. This allows for example deferring rows.Close() without checking for errors
+ - `[]byte(nil)` is now treated as a NULL value. Before, it was treated like an empty string / `[]byte("")`
+ - DSN parameter values must now be url.QueryEscape'ed. This allows text values to contain special characters, such as '&'.
+ - Use the IO buffer also for writing. This results in zero allocations (by the driver) for most queries
+ - Optimized the buffer for reading
+ - stmt.Query now caches column metadata
+ - New Logo
+ - Changed the copyright header to include all contributors
+ - Improved the LOAD INFILE documentation
+ - The driver struct is now exported to make the driver directly accessible
+ - Refactored the driver tests
+ - Added more benchmarks and moved all to a separate file
+ - Other small refactoring
+
+New Features:
+
+ - Added *old_passwords* support: Required in some cases, but must be enabled by adding `allowOldPasswords=true` to the DSN since it is insecure
+ - Added a `clientFoundRows` parameter: Return the number of matching rows instead of the number of rows changed on UPDATEs
+ - Added TLS/SSL support: Use a TLS/SSL encrypted connection to the server. Custom TLS configs can be registered and used
+
+Bugfixes:
+
+ - Fixed MySQL 4.1 support: MySQL 4.1 sends packets with lengths which differ from the specification
+ - Convert to DB timezone when inserting `time.Time`
+ - Splitted packets (more than 16MB) are now merged correctly
+ - Fixed false positive `io.EOF` errors when the data was fully read
+ - Avoid panics on reuse of closed connections
+ - Fixed empty string producing false nil values
+ - Fixed sign byte for positive TIME fields
+
+
+## Version 1.0 (2013-05-14)
+
+Initial Release
diff --git a/vendor/github.com/go-sql-driver/mysql/CONTRIBUTING.md b/vendor/github.com/go-sql-driver/mysql/CONTRIBUTING.md
new file mode 100644
index 000000000..8fe16bcb4
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/CONTRIBUTING.md
@@ -0,0 +1,23 @@
+# Contributing Guidelines
+
+## Reporting Issues
+
+Before creating a new Issue, please check first if a similar Issue [already exists](https://github.com/go-sql-driver/mysql/issues?state=open) or was [recently closed](https://github.com/go-sql-driver/mysql/issues?direction=desc&page=1&sort=updated&state=closed).
+
+## Contributing Code
+
+By contributing to this project, you share your code under the Mozilla Public License 2, as specified in the LICENSE file.
+Don't forget to add yourself to the AUTHORS file.
+
+### Code Review
+
+Everyone is invited to review and comment on pull requests.
+If it looks fine to you, comment with "LGTM" (Looks good to me).
+
+If changes are required, notice the reviewers with "PTAL" (Please take another look) after committing the fixes.
+
+Before merging the Pull Request, at least one [team member](https://github.com/go-sql-driver?tab=members) must have commented with "LGTM".
+
+## Development Ideas
+
+If you are looking for ideas for code contributions, please check our [Development Ideas](https://github.com/go-sql-driver/mysql/wiki/Development-Ideas) Wiki page.
diff --git a/vendor/github.com/go-sql-driver/mysql/ISSUE_TEMPLATE.md b/vendor/github.com/go-sql-driver/mysql/ISSUE_TEMPLATE.md
new file mode 100644
index 000000000..d9771f1dd
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/ISSUE_TEMPLATE.md
@@ -0,0 +1,21 @@
+### Issue description
+Tell us what should happen and what happens instead
+
+### Example code
+```go
+If possible, please enter some example code here to reproduce the issue.
+```
+
+### Error log
+```
+If you have an error log, please paste it here.
+```
+
+### Configuration
+*Driver version (or git SHA):*
+
+*Go version:* run `go version` in your console
+
+*Server version:* E.g. MySQL 5.6, MariaDB 10.0.20
+
+*Server OS:* E.g. Debian 8.1 (Jessie), Windows 10
diff --git a/vendor/github.com/go-sql-driver/mysql/LICENSE b/vendor/github.com/go-sql-driver/mysql/LICENSE
new file mode 100644
index 000000000..14e2f777f
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/LICENSE
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
diff --git a/vendor/github.com/go-sql-driver/mysql/PULL_REQUEST_TEMPLATE.md b/vendor/github.com/go-sql-driver/mysql/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 000000000..6f5c7ebeb
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,9 @@
+### Description
+Please explain the changes you made here.
+
+### Checklist
+- [ ] Code compiles correctly
+- [ ] Created tests which fail without the change (if possible)
+- [ ] All tests passing
+- [ ] Extended the README / documentation, if necessary
+- [ ] Added myself / the copyright holder to the AUTHORS file
diff --git a/vendor/github.com/go-sql-driver/mysql/README.md b/vendor/github.com/go-sql-driver/mysql/README.md
new file mode 100644
index 000000000..c64aae264
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/README.md
@@ -0,0 +1,420 @@
+# Go-MySQL-Driver
+
+A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) package
+
+![Go-MySQL-Driver logo](https://raw.github.com/wiki/go-sql-driver/mysql/gomysql_m.png "Golang Gopher holding the MySQL Dolphin")
+
+**Latest stable Release:** [Version 1.2 (June 03, 2014)](https://github.com/go-sql-driver/mysql/releases)
+
+[![Build Status](https://travis-ci.org/go-sql-driver/mysql.png?branch=master)](https://travis-ci.org/go-sql-driver/mysql)
+
+---------------------------------------
+ * [Features](#features)
+ * [Requirements](#requirements)
+ * [Installation](#installation)
+ * [Usage](#usage)
+ * [DSN (Data Source Name)](#dsn-data-source-name)
+ * [Password](#password)
+ * [Protocol](#protocol)
+ * [Address](#address)
+ * [Parameters](#parameters)
+ * [Examples](#examples)
+ * [LOAD DATA LOCAL INFILE support](#load-data-local-infile-support)
+ * [time.Time support](#timetime-support)
+ * [Unicode support](#unicode-support)
+ * [Testing / Development](#testing--development)
+ * [License](#license)
+
+---------------------------------------
+
+## Features
+ * Lightweight and [fast](https://github.com/go-sql-driver/sql-benchmark "golang MySQL-Driver performance")
+ * Native Go implementation. No C-bindings, just pure Go
+ * Connections over TCP/IPv4, TCP/IPv6, Unix domain sockets or [custom protocols](http://godoc.org/github.com/go-sql-driver/mysql#DialFunc)
+ * Automatic handling of broken connections
+ * Automatic Connection Pooling *(by database/sql package)*
+ * Supports queries larger than 16MB
+ * Full [`sql.RawBytes`](http://golang.org/pkg/database/sql/#RawBytes) support.
+ * Intelligent `LONG DATA` handling in prepared statements
+ * Secure `LOAD DATA LOCAL INFILE` support with file Whitelisting and `io.Reader` support
+ * Optional `time.Time` parsing
+ * Optional placeholder interpolation
+
+## Requirements
+ * Go 1.2 or higher
+ * MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
+
+---------------------------------------
+
+## Installation
+Simple install the package to your [$GOPATH](http://code.google.com/p/go-wiki/wiki/GOPATH "GOPATH") with the [go tool](http://golang.org/cmd/go/ "go command") from shell:
+```bash
+$ go get github.com/go-sql-driver/mysql
+```
+Make sure [Git is installed](http://git-scm.com/downloads) on your machine and in your system's `PATH`.
+
+## Usage
+_Go MySQL Driver_ is an implementation of Go's `database/sql/driver` interface. You only need to import the driver and can use the full [`database/sql`](http://golang.org/pkg/database/sql) API then.
+
+Use `mysql` as `driverName` and a valid [DSN](#dsn-data-source-name) as `dataSourceName`:
+```go
+import "database/sql"
+import _ "github.com/go-sql-driver/mysql"
+
+db, err := sql.Open("mysql", "user:password@/dbname")
+```
+
+[Examples are available in our Wiki](https://github.com/go-sql-driver/mysql/wiki/Examples "Go-MySQL-Driver Examples").
+
+
+### DSN (Data Source Name)
+
+The Data Source Name has a common format, like e.g. [PEAR DB](http://pear.php.net/manual/en/package.database.db.intro-dsn.php) uses it, but without type-prefix (optional parts marked by squared brackets):
+```
+[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...&paramN=valueN]
+```
+
+A DSN in its fullest form:
+```
+username:password@protocol(address)/dbname?param=value
+```
+
+Except for the databasename, all values are optional. So the minimal DSN is:
+```
+/dbname
+```
+
+If you do not want to preselect a database, leave `dbname` empty:
+```
+/
+```
+This has the same effect as an empty DSN string:
+```
+
+```
+
+Alternatively, [Config.FormatDSN](https://godoc.org/github.com/go-sql-driver/mysql#Config.FormatDSN) can be used to create a DSN string by filling a struct.
+
+#### Password
+Passwords can consist of any character. Escaping is **not** necessary.
+
+#### Protocol
+See [net.Dial](http://golang.org/pkg/net/#Dial) for more information which networks are available.
+In general you should use an Unix domain socket if available and TCP otherwise for best performance.
+
+#### Address
+For TCP and UDP networks, addresses have the form `host:port`.
+If `host` is a literal IPv6 address, it must be enclosed in square brackets.
+The functions [net.JoinHostPort](http://golang.org/pkg/net/#JoinHostPort) and [net.SplitHostPort](http://golang.org/pkg/net/#SplitHostPort) manipulate addresses in this form.
+
+For Unix domain sockets the address is the absolute path to the MySQL-Server-socket, e.g. `/var/run/mysqld/mysqld.sock` or `/tmp/mysql.sock`.
+
+#### Parameters
+*Parameters are case-sensitive!*
+
+Notice that any of `true`, `TRUE`, `True` or `1` is accepted to stand for a true boolean value. Not surprisingly, false can be specified as any of: `false`, `FALSE`, `False` or `0`.
+
+##### `allowAllFiles`
+
+```
+Type: bool
+Valid Values: true, false
+Default: false
+```
+
+`allowAllFiles=true` disables the file Whitelist for `LOAD DATA LOCAL INFILE` and allows *all* files.
+[*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)
+
+##### `allowCleartextPasswords`
+
+```
+Type: bool
+Valid Values: true, false
+Default: false
+```
+
+`allowCleartextPasswords=true` allows using the [cleartext client side plugin](http://dev.mysql.com/doc/en/cleartext-authentication-plugin.html) if required by an account, such as one defined with the [PAM authentication plugin](http://dev.mysql.com/doc/en/pam-authentication-plugin.html). Sending passwords in clear text may be a security problem in some configurations. To avoid problems if there is any possibility that the password would be intercepted, clients should connect to MySQL Server using a method that protects the password. Possibilities include [TLS / SSL](#tls), IPsec, or a private network.
+
+##### `allowOldPasswords`
+
+```
+Type: bool
+Valid Values: true, false
+Default: false
+```
+`allowOldPasswords=true` allows the usage of the insecure old password method. This should be avoided, but is necessary in some cases. See also [the old_passwords wiki page](https://github.com/go-sql-driver/mysql/wiki/old_passwords).
+
+##### `charset`
+
+```
+Type: string
+Valid Values: <name>
+Default: none
+```
+
+Sets the charset used for client-server interaction (`"SET NAMES <value>"`). If multiple charsets are set (separated by a comma), the following charset is used if setting the charset failes. This enables for example support for `utf8mb4` ([introduced in MySQL 5.5.3](http://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html)) with fallback to `utf8` for older servers (`charset=utf8mb4,utf8`).
+
+Usage of the `charset` parameter is discouraged because it issues additional queries to the server.
+Unless you need the fallback behavior, please use `collation` instead.
+
+##### `collation`
+
+```
+Type: string
+Valid Values: <name>
+Default: utf8_general_ci
+```
+
+Sets the collation used for client-server interaction on connection. In contrast to `charset`, `collation` does not issue additional queries. If the specified collation is unavailable on the target server, the connection will fail.
+
+A list of valid charsets for a server is retrievable with `SHOW COLLATION`.
+
+##### `clientFoundRows`
+
+```
+Type: bool
+Valid Values: true, false
+Default: false
+```
+
+`clientFoundRows=true` causes an UPDATE to return the number of matching rows instead of the number of rows changed.
+
+##### `columnsWithAlias`
+
+```
+Type: bool
+Valid Values: true, false
+Default: false
+```
+
+When `columnsWithAlias` is true, calls to `sql.Rows.Columns()` will return the table alias and the column name separated by a dot. For example:
+
+```
+SELECT u.id FROM users as u
+```
+
+will return `u.id` instead of just `id` if `columnsWithAlias=true`.
+
+##### `interpolateParams`
+
+```
+Type: bool
+Valid Values: true, false
+Default: false
+```
+
+If `interpolateParams` is true, placeholders (`?`) in calls to `db.Query()` and `db.Exec()` are interpolated into a single query string with given parameters. This reduces the number of roundtrips, since the driver has to prepare a statement, execute it with given parameters and close the statement again with `interpolateParams=false`.
+
+*This can not be used together with the multibyte encodings BIG5, CP932, GB2312, GBK or SJIS. These are blacklisted as they may [introduce a SQL injection vulnerability](http://stackoverflow.com/a/12118602/3430118)!*
+
+##### `loc`
+
+```
+Type: string
+Valid Values: <escaped name>
+Default: UTC
+```
+
+Sets the location for time.Time values (when using `parseTime=true`). *"Local"* sets the system's location. See [time.LoadLocation](http://golang.org/pkg/time/#LoadLocation) for details.
+
+Note that this sets the location for time.Time values but does not change MySQL's [time_zone setting](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html). For that see the [time_zone system variable](#system-variables), which can also be set as a DSN parameter.
+
+Please keep in mind, that param values must be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed. Alternatively you can manually replace the `/` with `%2F`. For example `US/Pacific` would be `loc=US%2FPacific`.
+
+##### `multiStatements`
+
+```
+Type: bool
+Valid Values: true, false
+Default: false
+```
+
+Allow multiple statements in one query. While this allows batch queries, it also greatly increases the risk of SQL injections. Only the result of the first query is returned, all other results are silently discarded.
+
+When `multiStatements` is used, `?` parameters must only be used in the first statement.
+
+
+##### `parseTime`
+
+```
+Type: bool
+Valid Values: true, false
+Default: false
+```
+
+`parseTime=true` changes the output type of `DATE` and `DATETIME` values to `time.Time` instead of `[]byte` / `string`
+
+
+##### `readTimeout`
+
+```
+Type: decimal number
+Default: 0
+```
+
+I/O read timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*.
+
+
+##### `strict`
+
+```
+Type: bool
+Valid Values: true, false
+Default: false
+```
+
+`strict=true` enables the strict mode in which MySQL warnings are treated as errors.
+
+By default MySQL also treats notes as warnings. Use [`sql_notes=false`](http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_sql_notes) to ignore notes. See the [examples](#examples) for an DSN example.
+
+
+##### `timeout`
+
+```
+Type: decimal number
+Default: OS default
+```
+
+*Driver* side connection timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*. To set a server side timeout, use the parameter [`wait_timeout`](http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_wait_timeout).
+
+
+##### `tls`
+
+```
+Type: bool / string
+Valid Values: true, false, skip-verify, <name>
+Default: false
+```
+
+`tls=true` enables TLS / SSL encrypted connection to the server. Use `skip-verify` if you want to use a self-signed or invalid certificate (server side). Use a custom value registered with [`mysql.RegisterTLSConfig`](http://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig).
+
+
+##### `writeTimeout`
+
+```
+Type: decimal number
+Default: 0
+```
+
+I/O write timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*.
+
+
+##### System Variables
+
+All other parameters are interpreted as system variables:
+ * `autocommit`: `"SET autocommit=<value>"`
+ * [`time_zone`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `"SET time_zone=<value>"`
+ * [`tx_isolation`](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation): `"SET tx_isolation=<value>"`
+ * `param`: `"SET <param>=<value>"`
+
+*The values must be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed!*
+
+#### Examples
+```
+user@unix(/path/to/socket)/dbname
+```
+
+```
+root:pw@unix(/tmp/mysql.sock)/myDatabase?loc=Local
+```
+
+```
+user:password@tcp(localhost:5555)/dbname?tls=skip-verify&autocommit=true
+```
+
+Use the [strict mode](#strict) but ignore notes:
+```
+user:password@/dbname?strict=true&sql_notes=false
+```
+
+TCP via IPv6:
+```
+user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname?timeout=90s&collation=utf8mb4_unicode_ci
+```
+
+TCP on a remote host, e.g. Amazon RDS:
+```
+id:password@tcp(your-amazonaws-uri.com:3306)/dbname
+```
+
+Google Cloud SQL on App Engine:
+```
+user@cloudsql(project-id:instance-name)/dbname
+```
+
+TCP using default port (3306) on localhost:
+```
+user:password@tcp/dbname?charset=utf8mb4,utf8&sys_var=esc%40ped
+```
+
+Use the default protocol (tcp) and host (localhost:3306):
+```
+user:password@/dbname
+```
+
+No Database preselected:
+```
+user:password@/
+```
+
+### `LOAD DATA LOCAL INFILE` support
+For this feature you need direct access to the package. Therefore you must change the import path (no `_`):
+```go
+import "github.com/go-sql-driver/mysql"
+```
+
+Files must be whitelisted by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the Whitelist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)).
+
+To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::<name>` then. Choose different names for different handlers and `DeregisterReaderHandler` when you don't need it anymore.
+
+See the [godoc of Go-MySQL-Driver](http://godoc.org/github.com/go-sql-driver/mysql "golang mysql driver documentation") for details.
+
+
+### `time.Time` support
+The default internal output type of MySQL `DATE` and `DATETIME` values is `[]byte` which allows you to scan the value into a `[]byte`, `string` or `sql.RawBytes` variable in your programm.
+
+However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` variables, which is the logical opposite in Go to `DATE` and `DATETIME` in MySQL. You can do that by changing the internal output type from `[]byte` to `time.Time` with the DSN parameter `parseTime=true`. You can set the default [`time.Time` location](http://golang.org/pkg/time/#Location) with the `loc` DSN parameter.
+
+**Caution:** As of Go 1.1, this makes `time.Time` the only variable type you can scan `DATE` and `DATETIME` values into. This breaks for example [`sql.RawBytes` support](https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes).
+
+Alternatively you can use the [`NullTime`](http://godoc.org/github.com/go-sql-driver/mysql#NullTime) type as the scan destination, which works with both `time.Time` and `string` / `[]byte`.
+
+
+### Unicode support
+Since version 1.1 Go-MySQL-Driver automatically uses the collation `utf8_general_ci` by default.
+
+Other collations / charsets can be set using the [`collation`](#collation) DSN parameter.
+
+Version 1.0 of the driver recommended adding `&charset=utf8` (alias for `SET NAMES utf8`) to the DSN to enable proper UTF-8 support. This is not necessary anymore. The [`collation`](#collation) parameter should be preferred to set another collation / charset than the default.
+
+See http://dev.mysql.com/doc/refman/5.7/en/charset-unicode.html for more details on MySQL's Unicode support.
+
+
+## Testing / Development
+To run the driver tests you may need to adjust the configuration. See the [Testing Wiki-Page](https://github.com/go-sql-driver/mysql/wiki/Testing "Testing") for details.
+
+Go-MySQL-Driver is not feature-complete yet. Your help is very appreciated.
+If you want to contribute, you can work on an [open issue](https://github.com/go-sql-driver/mysql/issues?state=open) or review a [pull request](https://github.com/go-sql-driver/mysql/pulls).
+
+See the [Contribution Guidelines](https://github.com/go-sql-driver/mysql/blob/master/CONTRIBUTING.md) for details.
+
+---------------------------------------
+
+## License
+Go-MySQL-Driver is licensed under the [Mozilla Public License Version 2.0](https://raw.github.com/go-sql-driver/mysql/master/LICENSE)
+
+Mozilla summarizes the license scope as follows:
+> MPL: The copyleft applies to any files containing MPLed code.
+
+
+That means:
+ * You can **use** the **unchanged** source code both in private and commercially
+ * When distributing, you **must publish** the source code of any **changed files** licensed under the MPL 2.0 under a) the MPL 2.0 itself or b) a compatible license (e.g. GPL 3.0 or Apache License 2.0)
+ * You **needn't publish** the source code of your library as long as the files licensed under the MPL 2.0 are **unchanged**
+
+Please read the [MPL 2.0 FAQ](http://www.mozilla.org/MPL/2.0/FAQ.html) if you have further questions regarding the license.
+
+You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE)
+
+![Go Gopher and MySQL Dolphin](https://raw.github.com/wiki/go-sql-driver/mysql/go-mysql-driver_m.jpg "Golang Gopher transporting the MySQL Dolphin in a wheelbarrow")
+
diff --git a/vendor/github.com/go-sql-driver/mysql/appengine.go b/vendor/github.com/go-sql-driver/mysql/appengine.go
new file mode 100644
index 000000000..565614eef
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/appengine.go
@@ -0,0 +1,19 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
+//
+// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// +build appengine
+
+package mysql
+
+import (
+ "appengine/cloudsql"
+)
+
+func init() {
+ RegisterDial("cloudsql", cloudsql.Dial)
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/buffer.go b/vendor/github.com/go-sql-driver/mysql/buffer.go
new file mode 100644
index 000000000..2001feacd
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/buffer.go
@@ -0,0 +1,147 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
+//
+// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package mysql
+
+import (
+ "io"
+ "net"
+ "time"
+)
+
+const defaultBufSize = 4096
+
+// A buffer which is used for both reading and writing.
+// This is possible since communication on each connection is synchronous.
+// In other words, we can't write and read simultaneously on the same connection.
+// The buffer is similar to bufio.Reader / Writer but zero-copy-ish
+// Also highly optimized for this particular use case.
+type buffer struct {
+ buf []byte
+ nc net.Conn
+ idx int
+ length int
+ timeout time.Duration
+}
+
+func newBuffer(nc net.Conn) buffer {
+ var b [defaultBufSize]byte
+ return buffer{
+ buf: b[:],
+ nc: nc,
+ }
+}
+
+// fill reads into the buffer until at least _need_ bytes are in it
+func (b *buffer) fill(need int) error {
+ n := b.length
+
+ // move existing data to the beginning
+ if n > 0 && b.idx > 0 {
+ copy(b.buf[0:n], b.buf[b.idx:])
+ }
+
+ // grow buffer if necessary
+ // TODO: let the buffer shrink again at some point
+ // Maybe keep the org buf slice and swap back?
+ if need > len(b.buf) {
+ // Round up to the next multiple of the default size
+ newBuf := make([]byte, ((need/defaultBufSize)+1)*defaultBufSize)
+ copy(newBuf, b.buf)
+ b.buf = newBuf
+ }
+
+ b.idx = 0
+
+ for {
+ if b.timeout > 0 {
+ if err := b.nc.SetReadDeadline(time.Now().Add(b.timeout)); err != nil {
+ return err
+ }
+ }
+
+ nn, err := b.nc.Read(b.buf[n:])
+ n += nn
+
+ switch err {
+ case nil:
+ if n < need {
+ continue
+ }
+ b.length = n
+ return nil
+
+ case io.EOF:
+ if n >= need {
+ b.length = n
+ return nil
+ }
+ return io.ErrUnexpectedEOF
+
+ default:
+ return err
+ }
+ }
+}
+
+// returns next N bytes from buffer.
+// The returned slice is only guaranteed to be valid until the next read
+func (b *buffer) readNext(need int) ([]byte, error) {
+ if b.length < need {
+ // refill
+ if err := b.fill(need); err != nil {
+ return nil, err
+ }
+ }
+
+ offset := b.idx
+ b.idx += need
+ b.length -= need
+ return b.buf[offset:b.idx], nil
+}
+
+// returns a buffer with the requested size.
+// If possible, a slice from the existing buffer is returned.
+// Otherwise a bigger buffer is made.
+// Only one buffer (total) can be used at a time.
+func (b *buffer) takeBuffer(length int) []byte {
+ if b.length > 0 {
+ return nil
+ }
+
+ // test (cheap) general case first
+ if length <= defaultBufSize || length <= cap(b.buf) {
+ return b.buf[:length]
+ }
+
+ if length < maxPacketSize {
+ b.buf = make([]byte, length)
+ return b.buf
+ }
+ return make([]byte, length)
+}
+
+// shortcut which can be used if the requested buffer is guaranteed to be
+// smaller than defaultBufSize
+// Only one buffer (total) can be used at a time.
+func (b *buffer) takeSmallBuffer(length int) []byte {
+ if b.length == 0 {
+ return b.buf[:length]
+ }
+ return nil
+}
+
+// takeCompleteBuffer returns the complete existing buffer.
+// This can be used if the necessary buffer size is unknown.
+// Only one buffer (total) can be used at a time.
+func (b *buffer) takeCompleteBuffer() []byte {
+ if b.length == 0 {
+ return b.buf
+ }
+ return nil
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/collations.go b/vendor/github.com/go-sql-driver/mysql/collations.go
new file mode 100644
index 000000000..82079cfb9
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/collations.go
@@ -0,0 +1,250 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
+//
+// Copyright 2014 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package mysql
+
+const defaultCollation = "utf8_general_ci"
+
+// A list of available collations mapped to the internal ID.
+// To update this map use the following MySQL query:
+// SELECT COLLATION_NAME, ID FROM information_schema.COLLATIONS
+var collations = map[string]byte{
+ "big5_chinese_ci": 1,
+ "latin2_czech_cs": 2,
+ "dec8_swedish_ci": 3,
+ "cp850_general_ci": 4,
+ "latin1_german1_ci": 5,
+ "hp8_english_ci": 6,
+ "koi8r_general_ci": 7,
+ "latin1_swedish_ci": 8,
+ "latin2_general_ci": 9,
+ "swe7_swedish_ci": 10,
+ "ascii_general_ci": 11,
+ "ujis_japanese_ci": 12,
+ "sjis_japanese_ci": 13,
+ "cp1251_bulgarian_ci": 14,
+ "latin1_danish_ci": 15,
+ "hebrew_general_ci": 16,
+ "tis620_thai_ci": 18,
+ "euckr_korean_ci": 19,
+ "latin7_estonian_cs": 20,
+ "latin2_hungarian_ci": 21,
+ "koi8u_general_ci": 22,
+ "cp1251_ukrainian_ci": 23,
+ "gb2312_chinese_ci": 24,
+ "greek_general_ci": 25,
+ "cp1250_general_ci": 26,
+ "latin2_croatian_ci": 27,
+ "gbk_chinese_ci": 28,
+ "cp1257_lithuanian_ci": 29,
+ "latin5_turkish_ci": 30,
+ "latin1_german2_ci": 31,
+ "armscii8_general_ci": 32,
+ "utf8_general_ci": 33,
+ "cp1250_czech_cs": 34,
+ "ucs2_general_ci": 35,
+ "cp866_general_ci": 36,
+ "keybcs2_general_ci": 37,
+ "macce_general_ci": 38,
+ "macroman_general_ci": 39,
+ "cp852_general_ci": 40,
+ "latin7_general_ci": 41,
+ "latin7_general_cs": 42,
+ "macce_bin": 43,
+ "cp1250_croatian_ci": 44,
+ "utf8mb4_general_ci": 45,
+ "utf8mb4_bin": 46,
+ "latin1_bin": 47,
+ "latin1_general_ci": 48,
+ "latin1_general_cs": 49,
+ "cp1251_bin": 50,
+ "cp1251_general_ci": 51,
+ "cp1251_general_cs": 52,
+ "macroman_bin": 53,
+ "utf16_general_ci": 54,
+ "utf16_bin": 55,
+ "utf16le_general_ci": 56,
+ "cp1256_general_ci": 57,
+ "cp1257_bin": 58,
+ "cp1257_general_ci": 59,
+ "utf32_general_ci": 60,
+ "utf32_bin": 61,
+ "utf16le_bin": 62,
+ "binary": 63,
+ "armscii8_bin": 64,
+ "ascii_bin": 65,
+ "cp1250_bin": 66,
+ "cp1256_bin": 67,
+ "cp866_bin": 68,
+ "dec8_bin": 69,
+ "greek_bin": 70,
+ "hebrew_bin": 71,
+ "hp8_bin": 72,
+ "keybcs2_bin": 73,
+ "koi8r_bin": 74,
+ "koi8u_bin": 75,
+ "latin2_bin": 77,
+ "latin5_bin": 78,
+ "latin7_bin": 79,
+ "cp850_bin": 80,
+ "cp852_bin": 81,
+ "swe7_bin": 82,
+ "utf8_bin": 83,
+ "big5_bin": 84,
+ "euckr_bin": 85,
+ "gb2312_bin": 86,
+ "gbk_bin": 87,
+ "sjis_bin": 88,
+ "tis620_bin": 89,
+ "ucs2_bin": 90,
+ "ujis_bin": 91,
+ "geostd8_general_ci": 92,
+ "geostd8_bin": 93,
+ "latin1_spanish_ci": 94,
+ "cp932_japanese_ci": 95,
+ "cp932_bin": 96,
+ "eucjpms_japanese_ci": 97,
+ "eucjpms_bin": 98,
+ "cp1250_polish_ci": 99,
+ "utf16_unicode_ci": 101,
+ "utf16_icelandic_ci": 102,
+ "utf16_latvian_ci": 103,
+ "utf16_romanian_ci": 104,
+ "utf16_slovenian_ci": 105,
+ "utf16_polish_ci": 106,
+ "utf16_estonian_ci": 107,
+ "utf16_spanish_ci": 108,
+ "utf16_swedish_ci": 109,
+ "utf16_turkish_ci": 110,
+ "utf16_czech_ci": 111,
+ "utf16_danish_ci": 112,
+ "utf16_lithuanian_ci": 113,
+ "utf16_slovak_ci": 114,
+ "utf16_spanish2_ci": 115,
+ "utf16_roman_ci": 116,
+ "utf16_persian_ci": 117,
+ "utf16_esperanto_ci": 118,
+ "utf16_hungarian_ci": 119,
+ "utf16_sinhala_ci": 120,
+ "utf16_german2_ci": 121,
+ "utf16_croatian_ci": 122,
+ "utf16_unicode_520_ci": 123,
+ "utf16_vietnamese_ci": 124,
+ "ucs2_unicode_ci": 128,
+ "ucs2_icelandic_ci": 129,
+ "ucs2_latvian_ci": 130,
+ "ucs2_romanian_ci": 131,
+ "ucs2_slovenian_ci": 132,
+ "ucs2_polish_ci": 133,
+ "ucs2_estonian_ci": 134,
+ "ucs2_spanish_ci": 135,
+ "ucs2_swedish_ci": 136,
+ "ucs2_turkish_ci": 137,
+ "ucs2_czech_ci": 138,
+ "ucs2_danish_ci": 139,
+ "ucs2_lithuanian_ci": 140,
+ "ucs2_slovak_ci": 141,
+ "ucs2_spanish2_ci": 142,
+ "ucs2_roman_ci": 143,
+ "ucs2_persian_ci": 144,
+ "ucs2_esperanto_ci": 145,
+ "ucs2_hungarian_ci": 146,
+ "ucs2_sinhala_ci": 147,
+ "ucs2_german2_ci": 148,
+ "ucs2_croatian_ci": 149,
+ "ucs2_unicode_520_ci": 150,
+ "ucs2_vietnamese_ci": 151,
+ "ucs2_general_mysql500_ci": 159,
+ "utf32_unicode_ci": 160,
+ "utf32_icelandic_ci": 161,
+ "utf32_latvian_ci": 162,
+ "utf32_romanian_ci": 163,
+ "utf32_slovenian_ci": 164,
+ "utf32_polish_ci": 165,
+ "utf32_estonian_ci": 166,
+ "utf32_spanish_ci": 167,
+ "utf32_swedish_ci": 168,
+ "utf32_turkish_ci": 169,
+ "utf32_czech_ci": 170,
+ "utf32_danish_ci": 171,
+ "utf32_lithuanian_ci": 172,
+ "utf32_slovak_ci": 173,
+ "utf32_spanish2_ci": 174,
+ "utf32_roman_ci": 175,
+ "utf32_persian_ci": 176,
+ "utf32_esperanto_ci": 177,
+ "utf32_hungarian_ci": 178,
+ "utf32_sinhala_ci": 179,
+ "utf32_german2_ci": 180,
+ "utf32_croatian_ci": 181,
+ "utf32_unicode_520_ci": 182,
+ "utf32_vietnamese_ci": 183,
+ "utf8_unicode_ci": 192,
+ "utf8_icelandic_ci": 193,
+ "utf8_latvian_ci": 194,
+ "utf8_romanian_ci": 195,
+ "utf8_slovenian_ci": 196,
+ "utf8_polish_ci": 197,
+ "utf8_estonian_ci": 198,
+ "utf8_spanish_ci": 199,
+ "utf8_swedish_ci": 200,
+ "utf8_turkish_ci": 201,
+ "utf8_czech_ci": 202,
+ "utf8_danish_ci": 203,
+ "utf8_lithuanian_ci": 204,
+ "utf8_slovak_ci": 205,
+ "utf8_spanish2_ci": 206,
+ "utf8_roman_ci": 207,
+ "utf8_persian_ci": 208,
+ "utf8_esperanto_ci": 209,
+ "utf8_hungarian_ci": 210,
+ "utf8_sinhala_ci": 211,
+ "utf8_german2_ci": 212,
+ "utf8_croatian_ci": 213,
+ "utf8_unicode_520_ci": 214,
+ "utf8_vietnamese_ci": 215,
+ "utf8_general_mysql500_ci": 223,
+ "utf8mb4_unicode_ci": 224,
+ "utf8mb4_icelandic_ci": 225,
+ "utf8mb4_latvian_ci": 226,
+ "utf8mb4_romanian_ci": 227,
+ "utf8mb4_slovenian_ci": 228,
+ "utf8mb4_polish_ci": 229,
+ "utf8mb4_estonian_ci": 230,
+ "utf8mb4_spanish_ci": 231,
+ "utf8mb4_swedish_ci": 232,
+ "utf8mb4_turkish_ci": 233,
+ "utf8mb4_czech_ci": 234,
+ "utf8mb4_danish_ci": 235,
+ "utf8mb4_lithuanian_ci": 236,
+ "utf8mb4_slovak_ci": 237,
+ "utf8mb4_spanish2_ci": 238,
+ "utf8mb4_roman_ci": 239,
+ "utf8mb4_persian_ci": 240,
+ "utf8mb4_esperanto_ci": 241,
+ "utf8mb4_hungarian_ci": 242,
+ "utf8mb4_sinhala_ci": 243,
+ "utf8mb4_german2_ci": 244,
+ "utf8mb4_croatian_ci": 245,
+ "utf8mb4_unicode_520_ci": 246,
+ "utf8mb4_vietnamese_ci": 247,
+}
+
+// A blacklist of collations which is unsafe to interpolate parameters.
+// These multibyte encodings may contains 0x5c (`\`) in their trailing bytes.
+var unsafeCollations = map[string]bool{
+ "big5_chinese_ci": true,
+ "sjis_japanese_ci": true,
+ "gbk_chinese_ci": true,
+ "big5_bin": true,
+ "gb2312_bin": true,
+ "gbk_bin": true,
+ "sjis_bin": true,
+ "cp932_japanese_ci": true,
+ "cp932_bin": true,
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/connection.go b/vendor/github.com/go-sql-driver/mysql/connection.go
new file mode 100644
index 000000000..c3899de0e
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/connection.go
@@ -0,0 +1,372 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
+//
+// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package mysql
+
+import (
+ "database/sql/driver"
+ "net"
+ "strconv"
+ "strings"
+ "time"
+)
+
+type mysqlConn struct {
+ buf buffer
+ netConn net.Conn
+ affectedRows uint64
+ insertId uint64
+ cfg *Config
+ maxPacketAllowed int
+ maxWriteSize int
+ writeTimeout time.Duration
+ flags clientFlag
+ status statusFlag
+ sequence uint8
+ parseTime bool
+ strict bool
+}
+
+// Handles parameters set in DSN after the connection is established
+func (mc *mysqlConn) handleParams() (err error) {
+ for param, val := range mc.cfg.Params {
+ switch param {
+ // Charset
+ case "charset":
+ charsets := strings.Split(val, ",")
+ for i := range charsets {
+ // ignore errors here - a charset may not exist
+ err = mc.exec("SET NAMES " + charsets[i])
+ if err == nil {
+ break
+ }
+ }
+ if err != nil {
+ return
+ }
+
+ // System Vars
+ default:
+ err = mc.exec("SET " + param + "=" + val + "")
+ if err != nil {
+ return
+ }
+ }
+ }
+
+ return
+}
+
+func (mc *mysqlConn) Begin() (driver.Tx, error) {
+ if mc.netConn == nil {
+ errLog.Print(ErrInvalidConn)
+ return nil, driver.ErrBadConn
+ }
+ err := mc.exec("START TRANSACTION")
+ if err == nil {
+ return &mysqlTx{mc}, err
+ }
+
+ return nil, err
+}
+
+func (mc *mysqlConn) Close() (err error) {
+ // Makes Close idempotent
+ if mc.netConn != nil {
+ err = mc.writeCommandPacket(comQuit)
+ }
+
+ mc.cleanup()
+
+ return
+}
+
+// Closes the network connection and unsets internal variables. Do not call this
+// function after successfully authentication, call Close instead. This function
+// is called before auth or on auth failure because MySQL will have already
+// closed the network connection.
+func (mc *mysqlConn) cleanup() {
+ // Makes cleanup idempotent
+ if mc.netConn != nil {
+ if err := mc.netConn.Close(); err != nil {
+ errLog.Print(err)
+ }
+ mc.netConn = nil
+ }
+ mc.cfg = nil
+ mc.buf.nc = nil
+}
+
+func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
+ if mc.netConn == nil {
+ errLog.Print(ErrInvalidConn)
+ return nil, driver.ErrBadConn
+ }
+ // Send command
+ err := mc.writeCommandPacketStr(comStmtPrepare, query)
+ if err != nil {
+ return nil, err
+ }
+
+ stmt := &mysqlStmt{
+ mc: mc,
+ }
+
+ // Read Result
+ columnCount, err := stmt.readPrepareResultPacket()
+ if err == nil {
+ if stmt.paramCount > 0 {
+ if err = mc.readUntilEOF(); err != nil {
+ return nil, err
+ }
+ }
+
+ if columnCount > 0 {
+ err = mc.readUntilEOF()
+ }
+ }
+
+ return stmt, err
+}
+
+func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (string, error) {
+ buf := mc.buf.takeCompleteBuffer()
+ if buf == nil {
+ // can not take the buffer. Something must be wrong with the connection
+ errLog.Print(ErrBusyBuffer)
+ return "", driver.ErrBadConn
+ }
+ buf = buf[:0]
+ argPos := 0
+
+ for i := 0; i < len(query); i++ {
+ q := strings.IndexByte(query[i:], '?')
+ if q == -1 {
+ buf = append(buf, query[i:]...)
+ break
+ }
+ buf = append(buf, query[i:i+q]...)
+ i += q
+
+ arg := args[argPos]
+ argPos++
+
+ if arg == nil {
+ buf = append(buf, "NULL"...)
+ continue
+ }
+
+ switch v := arg.(type) {
+ case int64:
+ buf = strconv.AppendInt(buf, v, 10)
+ case float64:
+ buf = strconv.AppendFloat(buf, v, 'g', -1, 64)
+ case bool:
+ if v {
+ buf = append(buf, '1')
+ } else {
+ buf = append(buf, '0')
+ }
+ case time.Time:
+ if v.IsZero() {
+ buf = append(buf, "'0000-00-00'"...)
+ } else {
+ v := v.In(mc.cfg.Loc)
+ v = v.Add(time.Nanosecond * 500) // To round under microsecond
+ year := v.Year()
+ year100 := year / 100
+ year1 := year % 100
+ month := v.Month()
+ day := v.Day()
+ hour := v.Hour()
+ minute := v.Minute()
+ second := v.Second()
+ micro := v.Nanosecond() / 1000
+
+ buf = append(buf, []byte{
+ '\'',
+ digits10[year100], digits01[year100],
+ digits10[year1], digits01[year1],
+ '-',
+ digits10[month], digits01[month],
+ '-',
+ digits10[day], digits01[day],
+ ' ',
+ digits10[hour], digits01[hour],
+ ':',
+ digits10[minute], digits01[minute],
+ ':',
+ digits10[second], digits01[second],
+ }...)
+
+ if micro != 0 {
+ micro10000 := micro / 10000
+ micro100 := micro / 100 % 100
+ micro1 := micro % 100
+ buf = append(buf, []byte{
+ '.',
+ digits10[micro10000], digits01[micro10000],
+ digits10[micro100], digits01[micro100],
+ digits10[micro1], digits01[micro1],
+ }...)
+ }
+ buf = append(buf, '\'')
+ }
+ case []byte:
+ if v == nil {
+ buf = append(buf, "NULL"...)
+ } else {
+ buf = append(buf, "_binary'"...)
+ if mc.status&statusNoBackslashEscapes == 0 {
+ buf = escapeBytesBackslash(buf, v)
+ } else {
+ buf = escapeBytesQuotes(buf, v)
+ }
+ buf = append(buf, '\'')
+ }
+ case string:
+ buf = append(buf, '\'')
+ if mc.status&statusNoBackslashEscapes == 0 {
+ buf = escapeStringBackslash(buf, v)
+ } else {
+ buf = escapeStringQuotes(buf, v)
+ }
+ buf = append(buf, '\'')
+ default:
+ return "", driver.ErrSkip
+ }
+
+ if len(buf)+4 > mc.maxPacketAllowed {
+ return "", driver.ErrSkip
+ }
+ }
+ if argPos != len(args) {
+ return "", driver.ErrSkip
+ }
+ return string(buf), nil
+}
+
+func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
+ if mc.netConn == nil {
+ errLog.Print(ErrInvalidConn)
+ return nil, driver.ErrBadConn
+ }
+ if len(args) != 0 {
+ if !mc.cfg.InterpolateParams {
+ return nil, driver.ErrSkip
+ }
+ // try to interpolate the parameters to save extra roundtrips for preparing and closing a statement
+ prepared, err := mc.interpolateParams(query, args)
+ if err != nil {
+ return nil, err
+ }
+ query = prepared
+ args = nil
+ }
+ mc.affectedRows = 0
+ mc.insertId = 0
+
+ err := mc.exec(query)
+ if err == nil {
+ return &mysqlResult{
+ affectedRows: int64(mc.affectedRows),
+ insertId: int64(mc.insertId),
+ }, err
+ }
+ return nil, err
+}
+
+// Internal function to execute commands
+func (mc *mysqlConn) exec(query string) error {
+ // Send command
+ err := mc.writeCommandPacketStr(comQuery, query)
+ if err != nil {
+ return err
+ }
+
+ // Read Result
+ resLen, err := mc.readResultSetHeaderPacket()
+ if err == nil && resLen > 0 {
+ if err = mc.readUntilEOF(); err != nil {
+ return err
+ }
+
+ err = mc.readUntilEOF()
+ }
+
+ return err
+}
+
+func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) {
+ if mc.netConn == nil {
+ errLog.Print(ErrInvalidConn)
+ return nil, driver.ErrBadConn
+ }
+ if len(args) != 0 {
+ if !mc.cfg.InterpolateParams {
+ return nil, driver.ErrSkip
+ }
+ // try client-side prepare to reduce roundtrip
+ prepared, err := mc.interpolateParams(query, args)
+ if err != nil {
+ return nil, err
+ }
+ query = prepared
+ args = nil
+ }
+ // Send command
+ err := mc.writeCommandPacketStr(comQuery, query)
+ if err == nil {
+ // Read Result
+ var resLen int
+ resLen, err = mc.readResultSetHeaderPacket()
+ if err == nil {
+ rows := new(textRows)
+ rows.mc = mc
+
+ if resLen == 0 {
+ // no columns, no more data
+ return emptyRows{}, nil
+ }
+ // Columns
+ rows.columns, err = mc.readColumns(resLen)
+ return rows, err
+ }
+ }
+ return nil, err
+}
+
+// Gets the value of the given MySQL System Variable
+// The returned byte slice is only valid until the next read
+func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
+ // Send command
+ if err := mc.writeCommandPacketStr(comQuery, "SELECT @@"+name); err != nil {
+ return nil, err
+ }
+
+ // Read Result
+ resLen, err := mc.readResultSetHeaderPacket()
+ if err == nil {
+ rows := new(textRows)
+ rows.mc = mc
+ rows.columns = []mysqlField{{fieldType: fieldTypeVarChar}}
+
+ if resLen > 0 {
+ // Columns
+ if err := mc.readUntilEOF(); err != nil {
+ return nil, err
+ }
+ }
+
+ dest := make([]driver.Value, resLen)
+ if err = rows.readRow(dest); err == nil {
+ return dest[0].([]byte), mc.readUntilEOF()
+ }
+ }
+ return nil, err
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/const.go b/vendor/github.com/go-sql-driver/mysql/const.go
new file mode 100644
index 000000000..88cfff3fd
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/const.go
@@ -0,0 +1,163 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
+//
+// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package mysql
+
+const (
+ minProtocolVersion byte = 10
+ maxPacketSize = 1<<24 - 1
+ timeFormat = "2006-01-02 15:04:05.999999"
+)
+
+// MySQL constants documentation:
+// http://dev.mysql.com/doc/internals/en/client-server-protocol.html
+
+const (
+ iOK byte = 0x00
+ iLocalInFile byte = 0xfb
+ iEOF byte = 0xfe
+ iERR byte = 0xff
+)
+
+// https://dev.mysql.com/doc/internals/en/capability-flags.html#packet-Protocol::CapabilityFlags
+type clientFlag uint32
+
+const (
+ clientLongPassword clientFlag = 1 << iota
+ clientFoundRows
+ clientLongFlag
+ clientConnectWithDB
+ clientNoSchema
+ clientCompress
+ clientODBC
+ clientLocalFiles
+ clientIgnoreSpace
+ clientProtocol41
+ clientInteractive
+ clientSSL
+ clientIgnoreSIGPIPE
+ clientTransactions
+ clientReserved
+ clientSecureConn
+ clientMultiStatements
+ clientMultiResults
+ clientPSMultiResults
+ clientPluginAuth
+ clientConnectAttrs
+ clientPluginAuthLenEncClientData
+ clientCanHandleExpiredPasswords
+ clientSessionTrack
+ clientDeprecateEOF
+)
+
+const (
+ comQuit byte = iota + 1
+ comInitDB
+ comQuery
+ comFieldList
+ comCreateDB
+ comDropDB
+ comRefresh
+ comShutdown
+ comStatistics
+ comProcessInfo
+ comConnect
+ comProcessKill
+ comDebug
+ comPing
+ comTime
+ comDelayedInsert
+ comChangeUser
+ comBinlogDump
+ comTableDump
+ comConnectOut
+ comRegisterSlave
+ comStmtPrepare
+ comStmtExecute
+ comStmtSendLongData
+ comStmtClose
+ comStmtReset
+ comSetOption
+ comStmtFetch
+)
+
+// https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnType
+const (
+ fieldTypeDecimal byte = iota
+ fieldTypeTiny
+ fieldTypeShort
+ fieldTypeLong
+ fieldTypeFloat
+ fieldTypeDouble
+ fieldTypeNULL
+ fieldTypeTimestamp
+ fieldTypeLongLong
+ fieldTypeInt24
+ fieldTypeDate
+ fieldTypeTime
+ fieldTypeDateTime
+ fieldTypeYear
+ fieldTypeNewDate
+ fieldTypeVarChar
+ fieldTypeBit
+)
+const (
+ fieldTypeJSON byte = iota + 0xf5
+ fieldTypeNewDecimal
+ fieldTypeEnum
+ fieldTypeSet
+ fieldTypeTinyBLOB
+ fieldTypeMediumBLOB
+ fieldTypeLongBLOB
+ fieldTypeBLOB
+ fieldTypeVarString
+ fieldTypeString
+ fieldTypeGeometry
+)
+
+type fieldFlag uint16
+
+const (
+ flagNotNULL fieldFlag = 1 << iota
+ flagPriKey
+ flagUniqueKey
+ flagMultipleKey
+ flagBLOB
+ flagUnsigned
+ flagZeroFill
+ flagBinary
+ flagEnum
+ flagAutoIncrement
+ flagTimestamp
+ flagSet
+ flagUnknown1
+ flagUnknown2
+ flagUnknown3
+ flagUnknown4
+)
+
+// http://dev.mysql.com/doc/internals/en/status-flags.html
+type statusFlag uint16
+
+const (
+ statusInTrans statusFlag = 1 << iota
+ statusInAutocommit
+ statusReserved // Not in documentation
+ statusMoreResultsExists
+ statusNoGoodIndexUsed
+ statusNoIndexUsed
+ statusCursorExists
+ statusLastRowSent
+ statusDbDropped
+ statusNoBackslashEscapes
+ statusMetadataChanged
+ statusQueryWasSlow
+ statusPsOutParams
+ statusInTransReadonly
+ statusSessionStateChanged
+)
diff --git a/vendor/github.com/go-sql-driver/mysql/driver.go b/vendor/github.com/go-sql-driver/mysql/driver.go
new file mode 100644
index 000000000..899f955fb
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/driver.go
@@ -0,0 +1,167 @@
+// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// Package mysql provides a MySQL driver for Go's database/sql package
+//
+// The driver should be used via the database/sql package:
+//
+// import "database/sql"
+// import _ "github.com/go-sql-driver/mysql"
+//
+// db, err := sql.Open("mysql", "user:password@/dbname")
+//
+// See https://github.com/go-sql-driver/mysql#usage for details
+package mysql
+
+import (
+ "database/sql"
+ "database/sql/driver"
+ "net"
+)
+
+// MySQLDriver is exported to make the driver directly accessible.
+// In general the driver is used via the database/sql package.
+type MySQLDriver struct{}
+
+// DialFunc is a function which can be used to establish the network connection.
+// Custom dial functions must be registered with RegisterDial
+type DialFunc func(addr string) (net.Conn, error)
+
+var dials map[string]DialFunc
+
+// RegisterDial registers a custom dial function. It can then be used by the
+// network address mynet(addr), where mynet is the registered new network.
+// addr is passed as a parameter to the dial function.
+func RegisterDial(net string, dial DialFunc) {
+ if dials == nil {
+ dials = make(map[string]DialFunc)
+ }
+ dials[net] = dial
+}
+
+// Open new Connection.
+// See https://github.com/go-sql-driver/mysql#dsn-data-source-name for how
+// the DSN string is formated
+func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
+ var err error
+
+ // New mysqlConn
+ mc := &mysqlConn{
+ maxPacketAllowed: maxPacketSize,
+ maxWriteSize: maxPacketSize - 1,
+ }
+ mc.cfg, err = ParseDSN(dsn)
+ if err != nil {
+ return nil, err
+ }
+ mc.parseTime = mc.cfg.ParseTime
+ mc.strict = mc.cfg.Strict
+
+ // Connect to Server
+ if dial, ok := dials[mc.cfg.Net]; ok {
+ mc.netConn, err = dial(mc.cfg.Addr)
+ } else {
+ nd := net.Dialer{Timeout: mc.cfg.Timeout}
+ mc.netConn, err = nd.Dial(mc.cfg.Net, mc.cfg.Addr)
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ // Enable TCP Keepalives on TCP connections
+ if tc, ok := mc.netConn.(*net.TCPConn); ok {
+ if err := tc.SetKeepAlive(true); err != nil {
+ // Don't send COM_QUIT before handshake.
+ mc.netConn.Close()
+ mc.netConn = nil
+ return nil, err
+ }
+ }
+
+ mc.buf = newBuffer(mc.netConn)
+
+ // Set I/O timeouts
+ mc.buf.timeout = mc.cfg.ReadTimeout
+ mc.writeTimeout = mc.cfg.WriteTimeout
+
+ // Reading Handshake Initialization Packet
+ cipher, err := mc.readInitPacket()
+ if err != nil {
+ mc.cleanup()
+ return nil, err
+ }
+
+ // Send Client Authentication Packet
+ if err = mc.writeAuthPacket(cipher); err != nil {
+ mc.cleanup()
+ return nil, err
+ }
+
+ // Handle response to auth packet, switch methods if possible
+ if err = handleAuthResult(mc, cipher); err != nil {
+ // Authentication failed and MySQL has already closed the connection
+ // (https://dev.mysql.com/doc/internals/en/authentication-fails.html).
+ // Do not send COM_QUIT, just cleanup and return the error.
+ mc.cleanup()
+ return nil, err
+ }
+
+ // Get max allowed packet size
+ maxap, err := mc.getSystemVar("max_allowed_packet")
+ if err != nil {
+ mc.Close()
+ return nil, err
+ }
+ mc.maxPacketAllowed = stringToInt(maxap) - 1
+ if mc.maxPacketAllowed < maxPacketSize {
+ mc.maxWriteSize = mc.maxPacketAllowed
+ }
+
+ // Handle DSN Params
+ err = mc.handleParams()
+ if err != nil {
+ mc.Close()
+ return nil, err
+ }
+
+ return mc, nil
+}
+
+func handleAuthResult(mc *mysqlConn, cipher []byte) error {
+ // Read Result Packet
+ err := mc.readResultOK()
+ if err == nil {
+ return nil // auth successful
+ }
+
+ if mc.cfg == nil {
+ return err // auth failed and retry not possible
+ }
+
+ // Retry auth if configured to do so.
+ if mc.cfg.AllowOldPasswords && err == ErrOldPassword {
+ // Retry with old authentication method. Note: there are edge cases
+ // where this should work but doesn't; this is currently "wontfix":
+ // https://github.com/go-sql-driver/mysql/issues/184
+ if err = mc.writeOldAuthPacket(cipher); err != nil {
+ return err
+ }
+ err = mc.readResultOK()
+ } else if mc.cfg.AllowCleartextPasswords && err == ErrCleartextPassword {
+ // Retry with clear text password for
+ // http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
+ // http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
+ if err = mc.writeClearAuthPacket(); err != nil {
+ return err
+ }
+ err = mc.readResultOK()
+ }
+ return err
+}
+
+func init() {
+ sql.Register("mysql", &MySQLDriver{})
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/dsn.go b/vendor/github.com/go-sql-driver/mysql/dsn.go
new file mode 100644
index 000000000..73138bc57
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/dsn.go
@@ -0,0 +1,513 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
+//
+// Copyright 2016 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package mysql
+
+import (
+ "bytes"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "net"
+ "net/url"
+ "strings"
+ "time"
+)
+
+var (
+ errInvalidDSNUnescaped = errors.New("invalid DSN: did you forget to escape a param value?")
+ errInvalidDSNAddr = errors.New("invalid DSN: network address not terminated (missing closing brace)")
+ errInvalidDSNNoSlash = errors.New("invalid DSN: missing the slash separating the database name")
+ errInvalidDSNUnsafeCollation = errors.New("invalid DSN: interpolateParams can not be used with unsafe collations")
+)
+
+// Config is a configuration parsed from a DSN string
+type Config struct {
+ User string // Username
+ Passwd string // Password (requires User)
+ Net string // Network type
+ Addr string // Network address (requires Net)
+ DBName string // Database name
+ Params map[string]string // Connection parameters
+ Collation string // Connection collation
+ Loc *time.Location // Location for time.Time values
+ TLSConfig string // TLS configuration name
+ tls *tls.Config // TLS configuration
+ Timeout time.Duration // Dial timeout
+ ReadTimeout time.Duration // I/O read timeout
+ WriteTimeout time.Duration // I/O write timeout
+
+ AllowAllFiles bool // Allow all files to be used with LOAD DATA LOCAL INFILE
+ AllowCleartextPasswords bool // Allows the cleartext client side plugin
+ AllowOldPasswords bool // Allows the old insecure password method
+ ClientFoundRows bool // Return number of matching rows instead of rows changed
+ ColumnsWithAlias bool // Prepend table alias to column names
+ InterpolateParams bool // Interpolate placeholders into query string
+ MultiStatements bool // Allow multiple statements in one query
+ ParseTime bool // Parse time values to time.Time
+ Strict bool // Return warnings as errors
+}
+
+// FormatDSN formats the given Config into a DSN string which can be passed to
+// the driver.
+func (cfg *Config) FormatDSN() string {
+ var buf bytes.Buffer
+
+ // [username[:password]@]
+ if len(cfg.User) > 0 {
+ buf.WriteString(cfg.User)
+ if len(cfg.Passwd) > 0 {
+ buf.WriteByte(':')
+ buf.WriteString(cfg.Passwd)
+ }
+ buf.WriteByte('@')
+ }
+
+ // [protocol[(address)]]
+ if len(cfg.Net) > 0 {
+ buf.WriteString(cfg.Net)
+ if len(cfg.Addr) > 0 {
+ buf.WriteByte('(')
+ buf.WriteString(cfg.Addr)
+ buf.WriteByte(')')
+ }
+ }
+
+ // /dbname
+ buf.WriteByte('/')
+ buf.WriteString(cfg.DBName)
+
+ // [?param1=value1&...&paramN=valueN]
+ hasParam := false
+
+ if cfg.AllowAllFiles {
+ hasParam = true
+ buf.WriteString("?allowAllFiles=true")
+ }
+
+ if cfg.AllowCleartextPasswords {
+ if hasParam {
+ buf.WriteString("&allowCleartextPasswords=true")
+ } else {
+ hasParam = true
+ buf.WriteString("?allowCleartextPasswords=true")
+ }
+ }
+
+ if cfg.AllowOldPasswords {
+ if hasParam {
+ buf.WriteString("&allowOldPasswords=true")
+ } else {
+ hasParam = true
+ buf.WriteString("?allowOldPasswords=true")
+ }
+ }
+
+ if cfg.ClientFoundRows {
+ if hasParam {
+ buf.WriteString("&clientFoundRows=true")
+ } else {
+ hasParam = true
+ buf.WriteString("?clientFoundRows=true")
+ }
+ }
+
+ if col := cfg.Collation; col != defaultCollation && len(col) > 0 {
+ if hasParam {
+ buf.WriteString("&collation=")
+ } else {
+ hasParam = true
+ buf.WriteString("?collation=")
+ }
+ buf.WriteString(col)
+ }
+
+ if cfg.ColumnsWithAlias {
+ if hasParam {
+ buf.WriteString("&columnsWithAlias=true")
+ } else {
+ hasParam = true
+ buf.WriteString("?columnsWithAlias=true")
+ }
+ }
+
+ if cfg.InterpolateParams {
+ if hasParam {
+ buf.WriteString("&interpolateParams=true")
+ } else {
+ hasParam = true
+ buf.WriteString("?interpolateParams=true")
+ }
+ }
+
+ if cfg.Loc != time.UTC && cfg.Loc != nil {
+ if hasParam {
+ buf.WriteString("&loc=")
+ } else {
+ hasParam = true
+ buf.WriteString("?loc=")
+ }
+ buf.WriteString(url.QueryEscape(cfg.Loc.String()))
+ }
+
+ if cfg.MultiStatements {
+ if hasParam {
+ buf.WriteString("&multiStatements=true")
+ } else {
+ hasParam = true
+ buf.WriteString("?multiStatements=true")
+ }
+ }
+
+ if cfg.ParseTime {
+ if hasParam {
+ buf.WriteString("&parseTime=true")
+ } else {
+ hasParam = true
+ buf.WriteString("?parseTime=true")
+ }
+ }
+
+ if cfg.ReadTimeout > 0 {
+ if hasParam {
+ buf.WriteString("&readTimeout=")
+ } else {
+ hasParam = true
+ buf.WriteString("?readTimeout=")
+ }
+ buf.WriteString(cfg.ReadTimeout.String())
+ }
+
+ if cfg.Strict {
+ if hasParam {
+ buf.WriteString("&strict=true")
+ } else {
+ hasParam = true
+ buf.WriteString("?strict=true")
+ }
+ }
+
+ if cfg.Timeout > 0 {
+ if hasParam {
+ buf.WriteString("&timeout=")
+ } else {
+ hasParam = true
+ buf.WriteString("?timeout=")
+ }
+ buf.WriteString(cfg.Timeout.String())
+ }
+
+ if len(cfg.TLSConfig) > 0 {
+ if hasParam {
+ buf.WriteString("&tls=")
+ } else {
+ hasParam = true
+ buf.WriteString("?tls=")
+ }
+ buf.WriteString(url.QueryEscape(cfg.TLSConfig))
+ }
+
+ if cfg.WriteTimeout > 0 {
+ if hasParam {
+ buf.WriteString("&writeTimeout=")
+ } else {
+ hasParam = true
+ buf.WriteString("?writeTimeout=")
+ }
+ buf.WriteString(cfg.WriteTimeout.String())
+ }
+
+ // other params
+ if cfg.Params != nil {
+ for param, value := range cfg.Params {
+ if hasParam {
+ buf.WriteByte('&')
+ } else {
+ hasParam = true
+ buf.WriteByte('?')
+ }
+
+ buf.WriteString(param)
+ buf.WriteByte('=')
+ buf.WriteString(url.QueryEscape(value))
+ }
+ }
+
+ return buf.String()
+}
+
+// ParseDSN parses the DSN string to a Config
+func ParseDSN(dsn string) (cfg *Config, err error) {
+ // New config with some default values
+ cfg = &Config{
+ Loc: time.UTC,
+ Collation: defaultCollation,
+ }
+
+ // [user[:password]@][net[(addr)]]/dbname[?param1=value1&paramN=valueN]
+ // Find the last '/' (since the password or the net addr might contain a '/')
+ foundSlash := false
+ for i := len(dsn) - 1; i >= 0; i-- {
+ if dsn[i] == '/' {
+ foundSlash = true
+ var j, k int
+
+ // left part is empty if i <= 0
+ if i > 0 {
+ // [username[:password]@][protocol[(address)]]
+ // Find the last '@' in dsn[:i]
+ for j = i; j >= 0; j-- {
+ if dsn[j] == '@' {
+ // username[:password]
+ // Find the first ':' in dsn[:j]
+ for k = 0; k < j; k++ {
+ if dsn[k] == ':' {
+ cfg.Passwd = dsn[k+1 : j]
+ break
+ }
+ }
+ cfg.User = dsn[:k]
+
+ break
+ }
+ }
+
+ // [protocol[(address)]]
+ // Find the first '(' in dsn[j+1:i]
+ for k = j + 1; k < i; k++ {
+ if dsn[k] == '(' {
+ // dsn[i-1] must be == ')' if an address is specified
+ if dsn[i-1] != ')' {
+ if strings.ContainsRune(dsn[k+1:i], ')') {
+ return nil, errInvalidDSNUnescaped
+ }
+ return nil, errInvalidDSNAddr
+ }
+ cfg.Addr = dsn[k+1 : i-1]
+ break
+ }
+ }
+ cfg.Net = dsn[j+1 : k]
+ }
+
+ // dbname[?param1=value1&...&paramN=valueN]
+ // Find the first '?' in dsn[i+1:]
+ for j = i + 1; j < len(dsn); j++ {
+ if dsn[j] == '?' {
+ if err = parseDSNParams(cfg, dsn[j+1:]); err != nil {
+ return
+ }
+ break
+ }
+ }
+ cfg.DBName = dsn[i+1 : j]
+
+ break
+ }
+ }
+
+ if !foundSlash && len(dsn) > 0 {
+ return nil, errInvalidDSNNoSlash
+ }
+
+ if cfg.InterpolateParams && unsafeCollations[cfg.Collation] {
+ return nil, errInvalidDSNUnsafeCollation
+ }
+
+ // Set default network if empty
+ if cfg.Net == "" {
+ cfg.Net = "tcp"
+ }
+
+ // Set default address if empty
+ if cfg.Addr == "" {
+ switch cfg.Net {
+ case "tcp":
+ cfg.Addr = "127.0.0.1:3306"
+ case "unix":
+ cfg.Addr = "/tmp/mysql.sock"
+ default:
+ return nil, errors.New("default addr for network '" + cfg.Net + "' unknown")
+ }
+
+ }
+
+ return
+}
+
+// parseDSNParams parses the DSN "query string"
+// Values must be url.QueryEscape'ed
+func parseDSNParams(cfg *Config, params string) (err error) {
+ for _, v := range strings.Split(params, "&") {
+ param := strings.SplitN(v, "=", 2)
+ if len(param) != 2 {
+ continue
+ }
+
+ // cfg params
+ switch value := param[1]; param[0] {
+
+ // Disable INFILE whitelist / enable all files
+ case "allowAllFiles":
+ var isBool bool
+ cfg.AllowAllFiles, isBool = readBool(value)
+ if !isBool {
+ return errors.New("invalid bool value: " + value)
+ }
+
+ // Use cleartext authentication mode (MySQL 5.5.10+)
+ case "allowCleartextPasswords":
+ var isBool bool
+ cfg.AllowCleartextPasswords, isBool = readBool(value)
+ if !isBool {
+ return errors.New("invalid bool value: " + value)
+ }
+
+ // Use old authentication mode (pre MySQL 4.1)
+ case "allowOldPasswords":
+ var isBool bool
+ cfg.AllowOldPasswords, isBool = readBool(value)
+ if !isBool {
+ return errors.New("invalid bool value: " + value)
+ }
+
+ // Switch "rowsAffected" mode
+ case "clientFoundRows":
+ var isBool bool
+ cfg.ClientFoundRows, isBool = readBool(value)
+ if !isBool {
+ return errors.New("invalid bool value: " + value)
+ }
+
+ // Collation
+ case "collation":
+ cfg.Collation = value
+ break
+
+ case "columnsWithAlias":
+ var isBool bool
+ cfg.ColumnsWithAlias, isBool = readBool(value)
+ if !isBool {
+ return errors.New("invalid bool value: " + value)
+ }
+
+ // Compression
+ case "compress":
+ return errors.New("compression not implemented yet")
+
+ // Enable client side placeholder substitution
+ case "interpolateParams":
+ var isBool bool
+ cfg.InterpolateParams, isBool = readBool(value)
+ if !isBool {
+ return errors.New("invalid bool value: " + value)
+ }
+
+ // Time Location
+ case "loc":
+ if value, err = url.QueryUnescape(value); err != nil {
+ return
+ }
+ cfg.Loc, err = time.LoadLocation(value)
+ if err != nil {
+ return
+ }
+
+ // multiple statements in one query
+ case "multiStatements":
+ var isBool bool
+ cfg.MultiStatements, isBool = readBool(value)
+ if !isBool {
+ return errors.New("invalid bool value: " + value)
+ }
+
+ // time.Time parsing
+ case "parseTime":
+ var isBool bool
+ cfg.ParseTime, isBool = readBool(value)
+ if !isBool {
+ return errors.New("invalid bool value: " + value)
+ }
+
+ // I/O read Timeout
+ case "readTimeout":
+ cfg.ReadTimeout, err = time.ParseDuration(value)
+ if err != nil {
+ return
+ }
+
+ // Strict mode
+ case "strict":
+ var isBool bool
+ cfg.Strict, isBool = readBool(value)
+ if !isBool {
+ return errors.New("invalid bool value: " + value)
+ }
+
+ // Dial Timeout
+ case "timeout":
+ cfg.Timeout, err = time.ParseDuration(value)
+ if err != nil {
+ return
+ }
+
+ // TLS-Encryption
+ case "tls":
+ boolValue, isBool := readBool(value)
+ if isBool {
+ if boolValue {
+ cfg.TLSConfig = "true"
+ cfg.tls = &tls.Config{}
+ } else {
+ cfg.TLSConfig = "false"
+ }
+ } else if vl := strings.ToLower(value); vl == "skip-verify" {
+ cfg.TLSConfig = vl
+ cfg.tls = &tls.Config{InsecureSkipVerify: true}
+ } else {
+ name, err := url.QueryUnescape(value)
+ if err != nil {
+ return fmt.Errorf("invalid value for TLS config name: %v", err)
+ }
+
+ if tlsConfig, ok := tlsConfigRegister[name]; ok {
+ if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify {
+ host, _, err := net.SplitHostPort(cfg.Addr)
+ if err == nil {
+ tlsConfig.ServerName = host
+ }
+ }
+
+ cfg.TLSConfig = name
+ cfg.tls = tlsConfig
+ } else {
+ return errors.New("invalid value / unknown config name: " + name)
+ }
+ }
+
+ // I/O write Timeout
+ case "writeTimeout":
+ cfg.WriteTimeout, err = time.ParseDuration(value)
+ if err != nil {
+ return
+ }
+
+ default:
+ // lazy init
+ if cfg.Params == nil {
+ cfg.Params = make(map[string]string)
+ }
+
+ if cfg.Params[param[0]], err = url.QueryUnescape(value); err != nil {
+ return
+ }
+ }
+ }
+
+ return
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/errors.go b/vendor/github.com/go-sql-driver/mysql/errors.go
new file mode 100644
index 000000000..1543a8054
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/errors.go
@@ -0,0 +1,131 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
+//
+// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package mysql
+
+import (
+ "database/sql/driver"
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "os"
+)
+
+// Various errors the driver might return. Can change between driver versions.
+var (
+ ErrInvalidConn = errors.New("invalid connection")
+ ErrMalformPkt = errors.New("malformed packet")
+ ErrNoTLS = errors.New("TLS requested but server does not support TLS")
+ ErrOldPassword = errors.New("this user requires old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords")
+ ErrCleartextPassword = errors.New("this user requires clear text authentication. If you still want to use it, please add 'allowCleartextPasswords=1' to your DSN")
+ ErrUnknownPlugin = errors.New("this authentication plugin is not supported")
+ ErrOldProtocol = errors.New("MySQL server does not support required protocol 41+")
+ ErrPktSync = errors.New("commands out of sync. You can't run this command now")
+ ErrPktSyncMul = errors.New("commands out of sync. Did you run multiple statements at once?")
+ ErrPktTooLarge = errors.New("packet for query is too large. Try adjusting the 'max_allowed_packet' variable on the server")
+ ErrBusyBuffer = errors.New("busy buffer")
+)
+
+var errLog = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime|log.Lshortfile))
+
+// Logger is used to log critical error messages.
+type Logger interface {
+ Print(v ...interface{})
+}
+
+// SetLogger is used to set the logger for critical errors.
+// The initial logger is os.Stderr.
+func SetLogger(logger Logger) error {
+ if logger == nil {
+ return errors.New("logger is nil")
+ }
+ errLog = logger
+ return nil
+}
+
+// MySQLError is an error type which represents a single MySQL error
+type MySQLError struct {
+ Number uint16
+ Message string
+}
+
+func (me *MySQLError) Error() string {
+ return fmt.Sprintf("Error %d: %s", me.Number, me.Message)
+}
+
+// MySQLWarnings is an error type which represents a group of one or more MySQL
+// warnings
+type MySQLWarnings []MySQLWarning
+
+func (mws MySQLWarnings) Error() string {
+ var msg string
+ for i, warning := range mws {
+ if i > 0 {
+ msg += "\r\n"
+ }
+ msg += fmt.Sprintf(
+ "%s %s: %s",
+ warning.Level,
+ warning.Code,
+ warning.Message,
+ )
+ }
+ return msg
+}
+
+// MySQLWarning is an error type which represents a single MySQL warning.
+// Warnings are returned in groups only. See MySQLWarnings
+type MySQLWarning struct {
+ Level string
+ Code string
+ Message string
+}
+
+func (mc *mysqlConn) getWarnings() (err error) {
+ rows, err := mc.Query("SHOW WARNINGS", nil)
+ if err != nil {
+ return
+ }
+
+ var warnings = MySQLWarnings{}
+ var values = make([]driver.Value, 3)
+
+ for {
+ err = rows.Next(values)
+ switch err {
+ case nil:
+ warning := MySQLWarning{}
+
+ if raw, ok := values[0].([]byte); ok {
+ warning.Level = string(raw)
+ } else {
+ warning.Level = fmt.Sprintf("%s", values[0])
+ }
+ if raw, ok := values[1].([]byte); ok {
+ warning.Code = string(raw)
+ } else {
+ warning.Code = fmt.Sprintf("%s", values[1])
+ }
+ if raw, ok := values[2].([]byte); ok {
+ warning.Message = string(raw)
+ } else {
+ warning.Message = fmt.Sprintf("%s", values[0])
+ }
+
+ warnings = append(warnings, warning)
+
+ case io.EOF:
+ return warnings
+
+ default:
+ rows.Close()
+ return
+ }
+ }
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/infile.go b/vendor/github.com/go-sql-driver/mysql/infile.go
new file mode 100644
index 000000000..0f975bbc2
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/infile.go
@@ -0,0 +1,181 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
+//
+// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package mysql
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "sync"
+)
+
+var (
+ fileRegister map[string]bool
+ fileRegisterLock sync.RWMutex
+ readerRegister map[string]func() io.Reader
+ readerRegisterLock sync.RWMutex
+)
+
+// RegisterLocalFile adds the given file to the file whitelist,
+// so that it can be used by "LOAD DATA LOCAL INFILE <filepath>".
+// Alternatively you can allow the use of all local files with
+// the DSN parameter 'allowAllFiles=true'
+//
+// filePath := "/home/gopher/data.csv"
+// mysql.RegisterLocalFile(filePath)
+// err := db.Exec("LOAD DATA LOCAL INFILE '" + filePath + "' INTO TABLE foo")
+// if err != nil {
+// ...
+//
+func RegisterLocalFile(filePath string) {
+ fileRegisterLock.Lock()
+ // lazy map init
+ if fileRegister == nil {
+ fileRegister = make(map[string]bool)
+ }
+
+ fileRegister[strings.Trim(filePath, `"`)] = true
+ fileRegisterLock.Unlock()
+}
+
+// DeregisterLocalFile removes the given filepath from the whitelist.
+func DeregisterLocalFile(filePath string) {
+ fileRegisterLock.Lock()
+ delete(fileRegister, strings.Trim(filePath, `"`))
+ fileRegisterLock.Unlock()
+}
+
+// RegisterReaderHandler registers a handler function which is used
+// to receive a io.Reader.
+// The Reader can be used by "LOAD DATA LOCAL INFILE Reader::<name>".
+// If the handler returns a io.ReadCloser Close() is called when the
+// request is finished.
+//
+// mysql.RegisterReaderHandler("data", func() io.Reader {
+// var csvReader io.Reader // Some Reader that returns CSV data
+// ... // Open Reader here
+// return csvReader
+// })
+// err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE foo")
+// if err != nil {
+// ...
+//
+func RegisterReaderHandler(name string, handler func() io.Reader) {
+ readerRegisterLock.Lock()
+ // lazy map init
+ if readerRegister == nil {
+ readerRegister = make(map[string]func() io.Reader)
+ }
+
+ readerRegister[name] = handler
+ readerRegisterLock.Unlock()
+}
+
+// DeregisterReaderHandler removes the ReaderHandler function with
+// the given name from the registry.
+func DeregisterReaderHandler(name string) {
+ readerRegisterLock.Lock()
+ delete(readerRegister, name)
+ readerRegisterLock.Unlock()
+}
+
+func deferredClose(err *error, closer io.Closer) {
+ closeErr := closer.Close()
+ if *err == nil {
+ *err = closeErr
+ }
+}
+
+func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
+ var rdr io.Reader
+ var data []byte
+ packetSize := 16 * 1024 // 16KB is small enough for disk readahead and large enough for TCP
+ if mc.maxWriteSize < packetSize {
+ packetSize = mc.maxWriteSize
+ }
+
+ if idx := strings.Index(name, "Reader::"); idx == 0 || (idx > 0 && name[idx-1] == '/') { // io.Reader
+ // The server might return an an absolute path. See issue #355.
+ name = name[idx+8:]
+
+ readerRegisterLock.RLock()
+ handler, inMap := readerRegister[name]
+ readerRegisterLock.RUnlock()
+
+ if inMap {
+ rdr = handler()
+ if rdr != nil {
+ if cl, ok := rdr.(io.Closer); ok {
+ defer deferredClose(&err, cl)
+ }
+ } else {
+ err = fmt.Errorf("Reader '%s' is <nil>", name)
+ }
+ } else {
+ err = fmt.Errorf("Reader '%s' is not registered", name)
+ }
+ } else { // File
+ name = strings.Trim(name, `"`)
+ fileRegisterLock.RLock()
+ fr := fileRegister[name]
+ fileRegisterLock.RUnlock()
+ if mc.cfg.AllowAllFiles || fr {
+ var file *os.File
+ var fi os.FileInfo
+
+ if file, err = os.Open(name); err == nil {
+ defer deferredClose(&err, file)
+
+ // get file size
+ if fi, err = file.Stat(); err == nil {
+ rdr = file
+ if fileSize := int(fi.Size()); fileSize < packetSize {
+ packetSize = fileSize
+ }
+ }
+ }
+ } else {
+ err = fmt.Errorf("local file '%s' is not registered", name)
+ }
+ }
+
+ // send content packets
+ if err == nil {
+ data := make([]byte, 4+packetSize)
+ var n int
+ for err == nil {
+ n, err = rdr.Read(data[4:])
+ if n > 0 {
+ if ioErr := mc.writePacket(data[:4+n]); ioErr != nil {
+ return ioErr
+ }
+ }
+ }
+ if err == io.EOF {
+ err = nil
+ }
+ }
+
+ // send empty packet (termination)
+ if data == nil {
+ data = make([]byte, 4)
+ }
+ if ioErr := mc.writePacket(data[:4]); ioErr != nil {
+ return ioErr
+ }
+
+ // read OK packet
+ if err == nil {
+ return mc.readResultOK()
+ }
+
+ mc.readPacket()
+ return err
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/packets.go b/vendor/github.com/go-sql-driver/mysql/packets.go
new file mode 100644
index 000000000..8d9166578
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/packets.go
@@ -0,0 +1,1243 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
+//
+// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package mysql
+
+import (
+ "bytes"
+ "crypto/tls"
+ "database/sql/driver"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "math"
+ "time"
+)
+
+// Packets documentation:
+// http://dev.mysql.com/doc/internals/en/client-server-protocol.html
+
+// Read packet to buffer 'data'
+func (mc *mysqlConn) readPacket() ([]byte, error) {
+ var payload []byte
+ for {
+ // Read packet header
+ data, err := mc.buf.readNext(4)
+ if err != nil {
+ errLog.Print(err)
+ mc.Close()
+ return nil, driver.ErrBadConn
+ }
+
+ // Packet Length [24 bit]
+ pktLen := int(uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16)
+
+ if pktLen < 1 {
+ errLog.Print(ErrMalformPkt)
+ mc.Close()
+ return nil, driver.ErrBadConn
+ }
+
+ // Check Packet Sync [8 bit]
+ if data[3] != mc.sequence {
+ if data[3] > mc.sequence {
+ return nil, ErrPktSyncMul
+ }
+ return nil, ErrPktSync
+ }
+ mc.sequence++
+
+ // Read packet body [pktLen bytes]
+ data, err = mc.buf.readNext(pktLen)
+ if err != nil {
+ errLog.Print(err)
+ mc.Close()
+ return nil, driver.ErrBadConn
+ }
+
+ isLastPacket := (pktLen < maxPacketSize)
+
+ // Zero allocations for non-splitting packets
+ if isLastPacket && payload == nil {
+ return data, nil
+ }
+
+ payload = append(payload, data...)
+
+ if isLastPacket {
+ return payload, nil
+ }
+ }
+}
+
+// Write packet buffer 'data'
+func (mc *mysqlConn) writePacket(data []byte) error {
+ pktLen := len(data) - 4
+
+ if pktLen > mc.maxPacketAllowed {
+ return ErrPktTooLarge
+ }
+
+ for {
+ var size int
+ if pktLen >= maxPacketSize {
+ data[0] = 0xff
+ data[1] = 0xff
+ data[2] = 0xff
+ size = maxPacketSize
+ } else {
+ data[0] = byte(pktLen)
+ data[1] = byte(pktLen >> 8)
+ data[2] = byte(pktLen >> 16)
+ size = pktLen
+ }
+ data[3] = mc.sequence
+
+ // Write packet
+ if mc.writeTimeout > 0 {
+ if err := mc.netConn.SetWriteDeadline(time.Now().Add(mc.writeTimeout)); err != nil {
+ return err
+ }
+ }
+
+ n, err := mc.netConn.Write(data[:4+size])
+ if err == nil && n == 4+size {
+ mc.sequence++
+ if size != maxPacketSize {
+ return nil
+ }
+ pktLen -= size
+ data = data[size:]
+ continue
+ }
+
+ // Handle error
+ if err == nil { // n != len(data)
+ errLog.Print(ErrMalformPkt)
+ } else {
+ errLog.Print(err)
+ }
+ return driver.ErrBadConn
+ }
+}
+
+/******************************************************************************
+* Initialisation Process *
+******************************************************************************/
+
+// Handshake Initialization Packet
+// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::Handshake
+func (mc *mysqlConn) readInitPacket() ([]byte, error) {
+ data, err := mc.readPacket()
+ if err != nil {
+ return nil, err
+ }
+
+ if data[0] == iERR {
+ return nil, mc.handleErrorPacket(data)
+ }
+
+ // protocol version [1 byte]
+ if data[0] < minProtocolVersion {
+ return nil, fmt.Errorf(
+ "unsupported protocol version %d. Version %d or higher is required",
+ data[0],
+ minProtocolVersion,
+ )
+ }
+
+ // server version [null terminated string]
+ // connection id [4 bytes]
+ pos := 1 + bytes.IndexByte(data[1:], 0x00) + 1 + 4
+
+ // first part of the password cipher [8 bytes]
+ cipher := data[pos : pos+8]
+
+ // (filler) always 0x00 [1 byte]
+ pos += 8 + 1
+
+ // capability flags (lower 2 bytes) [2 bytes]
+ mc.flags = clientFlag(binary.LittleEndian.Uint16(data[pos : pos+2]))
+ if mc.flags&clientProtocol41 == 0 {
+ return nil, ErrOldProtocol
+ }
+ if mc.flags&clientSSL == 0 && mc.cfg.tls != nil {
+ return nil, ErrNoTLS
+ }
+ pos += 2
+
+ if len(data) > pos {
+ // character set [1 byte]
+ // status flags [2 bytes]
+ // capability flags (upper 2 bytes) [2 bytes]
+ // length of auth-plugin-data [1 byte]
+ // reserved (all [00]) [10 bytes]
+ pos += 1 + 2 + 2 + 1 + 10
+
+ // second part of the password cipher [mininum 13 bytes],
+ // where len=MAX(13, length of auth-plugin-data - 8)
+ //
+ // The web documentation is ambiguous about the length. However,
+ // according to mysql-5.7/sql/auth/sql_authentication.cc line 538,
+ // the 13th byte is "\0 byte, terminating the second part of
+ // a scramble". So the second part of the password cipher is
+ // a NULL terminated string that's at least 13 bytes with the
+ // last byte being NULL.
+ //
+ // The official Python library uses the fixed length 12
+ // which seems to work but technically could have a hidden bug.
+ cipher = append(cipher, data[pos:pos+12]...)
+
+ // TODO: Verify string termination
+ // EOF if version (>= 5.5.7 and < 5.5.10) or (>= 5.6.0 and < 5.6.2)
+ // \NUL otherwise
+ //
+ //if data[len(data)-1] == 0 {
+ // return
+ //}
+ //return ErrMalformPkt
+
+ // make a memory safe copy of the cipher slice
+ var b [20]byte
+ copy(b[:], cipher)
+ return b[:], nil
+ }
+
+ // make a memory safe copy of the cipher slice
+ var b [8]byte
+ copy(b[:], cipher)
+ return b[:], nil
+}
+
+// Client Authentication Packet
+// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse
+func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
+ // Adjust client flags based on server support
+ clientFlags := clientProtocol41 |
+ clientSecureConn |
+ clientLongPassword |
+ clientTransactions |
+ clientLocalFiles |
+ clientPluginAuth |
+ clientMultiResults |
+ mc.flags&clientLongFlag
+
+ if mc.cfg.ClientFoundRows {
+ clientFlags |= clientFoundRows
+ }
+
+ // To enable TLS / SSL
+ if mc.cfg.tls != nil {
+ clientFlags |= clientSSL
+ }
+
+ if mc.cfg.MultiStatements {
+ clientFlags |= clientMultiStatements
+ }
+
+ // User Password
+ scrambleBuff := scramblePassword(cipher, []byte(mc.cfg.Passwd))
+
+ pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.User) + 1 + 1 + len(scrambleBuff) + 21 + 1
+
+ // To specify a db name
+ if n := len(mc.cfg.DBName); n > 0 {
+ clientFlags |= clientConnectWithDB
+ pktLen += n + 1
+ }
+
+ // Calculate packet length and get buffer with that size
+ data := mc.buf.takeSmallBuffer(pktLen + 4)
+ if data == nil {
+ // can not take the buffer. Something must be wrong with the connection
+ errLog.Print(ErrBusyBuffer)
+ return driver.ErrBadConn
+ }
+
+ // ClientFlags [32 bit]
+ data[4] = byte(clientFlags)
+ data[5] = byte(clientFlags >> 8)
+ data[6] = byte(clientFlags >> 16)
+ data[7] = byte(clientFlags >> 24)
+
+ // MaxPacketSize [32 bit] (none)
+ data[8] = 0x00
+ data[9] = 0x00
+ data[10] = 0x00
+ data[11] = 0x00
+
+ // Charset [1 byte]
+ var found bool
+ data[12], found = collations[mc.cfg.Collation]
+ if !found {
+ // Note possibility for false negatives:
+ // could be triggered although the collation is valid if the
+ // collations map does not contain entries the server supports.
+ return errors.New("unknown collation")
+ }
+
+ // SSL Connection Request Packet
+ // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest
+ if mc.cfg.tls != nil {
+ // Send TLS / SSL request packet
+ if err := mc.writePacket(data[:(4+4+1+23)+4]); err != nil {
+ return err
+ }
+
+ // Switch to TLS
+ tlsConn := tls.Client(mc.netConn, mc.cfg.tls)
+ if err := tlsConn.Handshake(); err != nil {
+ return err
+ }
+ mc.netConn = tlsConn
+ mc.buf.nc = tlsConn
+ }
+
+ // Filler [23 bytes] (all 0x00)
+ pos := 13
+ for ; pos < 13+23; pos++ {
+ data[pos] = 0
+ }
+
+ // User [null terminated string]
+ if len(mc.cfg.User) > 0 {
+ pos += copy(data[pos:], mc.cfg.User)
+ }
+ data[pos] = 0x00
+ pos++
+
+ // ScrambleBuffer [length encoded integer]
+ data[pos] = byte(len(scrambleBuff))
+ pos += 1 + copy(data[pos+1:], scrambleBuff)
+
+ // Databasename [null terminated string]
+ if len(mc.cfg.DBName) > 0 {
+ pos += copy(data[pos:], mc.cfg.DBName)
+ data[pos] = 0x00
+ pos++
+ }
+
+ // Assume native client during response
+ pos += copy(data[pos:], "mysql_native_password")
+ data[pos] = 0x00
+
+ // Send Auth packet
+ return mc.writePacket(data)
+}
+
+// Client old authentication packet
+// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
+func (mc *mysqlConn) writeOldAuthPacket(cipher []byte) error {
+ // User password
+ scrambleBuff := scrambleOldPassword(cipher, []byte(mc.cfg.Passwd))
+
+ // Calculate the packet length and add a tailing 0
+ pktLen := len(scrambleBuff) + 1
+ data := mc.buf.takeSmallBuffer(4 + pktLen)
+ if data == nil {
+ // can not take the buffer. Something must be wrong with the connection
+ errLog.Print(ErrBusyBuffer)
+ return driver.ErrBadConn
+ }
+
+ // Add the scrambled password [null terminated string]
+ copy(data[4:], scrambleBuff)
+ data[4+pktLen-1] = 0x00
+
+ return mc.writePacket(data)
+}
+
+// Client clear text authentication packet
+// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
+func (mc *mysqlConn) writeClearAuthPacket() error {
+ // Calculate the packet length and add a tailing 0
+ pktLen := len(mc.cfg.Passwd) + 1
+ data := mc.buf.takeSmallBuffer(4 + pktLen)
+ if data == nil {
+ // can not take the buffer. Something must be wrong with the connection
+ errLog.Print(ErrBusyBuffer)
+ return driver.ErrBadConn
+ }
+
+ // Add the clear password [null terminated string]
+ copy(data[4:], mc.cfg.Passwd)
+ data[4+pktLen-1] = 0x00
+
+ return mc.writePacket(data)
+}
+
+/******************************************************************************
+* Command Packets *
+******************************************************************************/
+
+func (mc *mysqlConn) writeCommandPacket(command byte) error {
+ // Reset Packet Sequence
+ mc.sequence = 0
+
+ data := mc.buf.takeSmallBuffer(4 + 1)
+ if data == nil {
+ // can not take the buffer. Something must be wrong with the connection
+ errLog.Print(ErrBusyBuffer)
+ return driver.ErrBadConn
+ }
+
+ // Add command byte
+ data[4] = command
+
+ // Send CMD packet
+ return mc.writePacket(data)
+}
+
+func (mc *mysqlConn) writeCommandPacketStr(command byte, arg string) error {
+ // Reset Packet Sequence
+ mc.sequence = 0
+
+ pktLen := 1 + len(arg)
+ data := mc.buf.takeBuffer(pktLen + 4)
+ if data == nil {
+ // can not take the buffer. Something must be wrong with the connection
+ errLog.Print(ErrBusyBuffer)
+ return driver.ErrBadConn
+ }
+
+ // Add command byte
+ data[4] = command
+
+ // Add arg
+ copy(data[5:], arg)
+
+ // Send CMD packet
+ return mc.writePacket(data)
+}
+
+func (mc *mysqlConn) writeCommandPacketUint32(command byte, arg uint32) error {
+ // Reset Packet Sequence
+ mc.sequence = 0
+
+ data := mc.buf.takeSmallBuffer(4 + 1 + 4)
+ if data == nil {
+ // can not take the buffer. Something must be wrong with the connection
+ errLog.Print(ErrBusyBuffer)
+ return driver.ErrBadConn
+ }
+
+ // Add command byte
+ data[4] = command
+
+ // Add arg [32 bit]
+ data[5] = byte(arg)
+ data[6] = byte(arg >> 8)
+ data[7] = byte(arg >> 16)
+ data[8] = byte(arg >> 24)
+
+ // Send CMD packet
+ return mc.writePacket(data)
+}
+
+/******************************************************************************
+* Result Packets *
+******************************************************************************/
+
+// Returns error if Packet is not an 'Result OK'-Packet
+func (mc *mysqlConn) readResultOK() error {
+ data, err := mc.readPacket()
+ if err == nil {
+ // packet indicator
+ switch data[0] {
+
+ case iOK:
+ return mc.handleOkPacket(data)
+
+ case iEOF:
+ if len(data) > 1 {
+ plugin := string(data[1:bytes.IndexByte(data, 0x00)])
+ if plugin == "mysql_old_password" {
+ // using old_passwords
+ return ErrOldPassword
+ } else if plugin == "mysql_clear_password" {
+ // using clear text password
+ return ErrCleartextPassword
+ } else {
+ return ErrUnknownPlugin
+ }
+ } else {
+ return ErrOldPassword
+ }
+
+ default: // Error otherwise
+ return mc.handleErrorPacket(data)
+ }
+ }
+ return err
+}
+
+// Result Set Header Packet
+// http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-ProtocolText::Resultset
+func (mc *mysqlConn) readResultSetHeaderPacket() (int, error) {
+ data, err := mc.readPacket()
+ if err == nil {
+ switch data[0] {
+
+ case iOK:
+ return 0, mc.handleOkPacket(data)
+
+ case iERR:
+ return 0, mc.handleErrorPacket(data)
+
+ case iLocalInFile:
+ return 0, mc.handleInFileRequest(string(data[1:]))
+ }
+
+ // column count
+ num, _, n := readLengthEncodedInteger(data)
+ if n-len(data) == 0 {
+ return int(num), nil
+ }
+
+ return 0, ErrMalformPkt
+ }
+ return 0, err
+}
+
+// Error Packet
+// http://dev.mysql.com/doc/internals/en/generic-response-packets.html#packet-ERR_Packet
+func (mc *mysqlConn) handleErrorPacket(data []byte) error {
+ if data[0] != iERR {
+ return ErrMalformPkt
+ }
+
+ // 0xff [1 byte]
+
+ // Error Number [16 bit uint]
+ errno := binary.LittleEndian.Uint16(data[1:3])
+
+ pos := 3
+
+ // SQL State [optional: # + 5bytes string]
+ if data[3] == 0x23 {
+ //sqlstate := string(data[4 : 4+5])
+ pos = 9
+ }
+
+ // Error Message [string]
+ return &MySQLError{
+ Number: errno,
+ Message: string(data[pos:]),
+ }
+}
+
+func readStatus(b []byte) statusFlag {
+ return statusFlag(b[0]) | statusFlag(b[1])<<8
+}
+
+// Ok Packet
+// http://dev.mysql.com/doc/internals/en/generic-response-packets.html#packet-OK_Packet
+func (mc *mysqlConn) handleOkPacket(data []byte) error {
+ var n, m int
+
+ // 0x00 [1 byte]
+
+ // Affected rows [Length Coded Binary]
+ mc.affectedRows, _, n = readLengthEncodedInteger(data[1:])
+
+ // Insert id [Length Coded Binary]
+ mc.insertId, _, m = readLengthEncodedInteger(data[1+n:])
+
+ // server_status [2 bytes]
+ mc.status = readStatus(data[1+n+m : 1+n+m+2])
+ if err := mc.discardResults(); err != nil {
+ return err
+ }
+
+ // warning count [2 bytes]
+ if !mc.strict {
+ return nil
+ }
+
+ pos := 1 + n + m + 2
+ if binary.LittleEndian.Uint16(data[pos:pos+2]) > 0 {
+ return mc.getWarnings()
+ }
+ return nil
+}
+
+// Read Packets as Field Packets until EOF-Packet or an Error appears
+// http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnDefinition41
+func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
+ columns := make([]mysqlField, count)
+
+ for i := 0; ; i++ {
+ data, err := mc.readPacket()
+ if err != nil {
+ return nil, err
+ }
+
+ // EOF Packet
+ if data[0] == iEOF && (len(data) == 5 || len(data) == 1) {
+ if i == count {
+ return columns, nil
+ }
+ return nil, fmt.Errorf("column count mismatch n:%d len:%d", count, len(columns))
+ }
+
+ // Catalog
+ pos, err := skipLengthEncodedString(data)
+ if err != nil {
+ return nil, err
+ }
+
+ // Database [len coded string]
+ n, err := skipLengthEncodedString(data[pos:])
+ if err != nil {
+ return nil, err
+ }
+ pos += n
+
+ // Table [len coded string]
+ if mc.cfg.ColumnsWithAlias {
+ tableName, _, n, err := readLengthEncodedString(data[pos:])
+ if err != nil {
+ return nil, err
+ }
+ pos += n
+ columns[i].tableName = string(tableName)
+ } else {
+ n, err = skipLengthEncodedString(data[pos:])
+ if err != nil {
+ return nil, err
+ }
+ pos += n
+ }
+
+ // Original table [len coded string]
+ n, err = skipLengthEncodedString(data[pos:])
+ if err != nil {
+ return nil, err
+ }
+ pos += n
+
+ // Name [len coded string]
+ name, _, n, err := readLengthEncodedString(data[pos:])
+ if err != nil {
+ return nil, err
+ }
+ columns[i].name = string(name)
+ pos += n
+
+ // Original name [len coded string]
+ n, err = skipLengthEncodedString(data[pos:])
+ if err != nil {
+ return nil, err
+ }
+
+ // Filler [uint8]
+ // Charset [charset, collation uint8]
+ // Length [uint32]
+ pos += n + 1 + 2 + 4
+
+ // Field type [uint8]
+ columns[i].fieldType = data[pos]
+ pos++
+
+ // Flags [uint16]
+ columns[i].flags = fieldFlag(binary.LittleEndian.Uint16(data[pos : pos+2]))
+ pos += 2
+
+ // Decimals [uint8]
+ columns[i].decimals = data[pos]
+ //pos++
+
+ // Default value [len coded binary]
+ //if pos < len(data) {
+ // defaultVal, _, err = bytesToLengthCodedBinary(data[pos:])
+ //}
+ }
+}
+
+// Read Packets as Field Packets until EOF-Packet or an Error appears
+// http://dev.mysql.com/doc/internals/en/com-query-response.html#packet-ProtocolText::ResultsetRow
+func (rows *textRows) readRow(dest []driver.Value) error {
+ mc := rows.mc
+
+ data, err := mc.readPacket()
+ if err != nil {
+ return err
+ }
+
+ // EOF Packet
+ if data[0] == iEOF && len(data) == 5 {
+ // server_status [2 bytes]
+ rows.mc.status = readStatus(data[3:])
+ if err := rows.mc.discardResults(); err != nil {
+ return err
+ }
+ rows.mc = nil
+ return io.EOF
+ }
+ if data[0] == iERR {
+ rows.mc = nil
+ return mc.handleErrorPacket(data)
+ }
+
+ // RowSet Packet
+ var n int
+ var isNull bool
+ pos := 0
+
+ for i := range dest {
+ // Read bytes and convert to string
+ dest[i], isNull, n, err = readLengthEncodedString(data[pos:])
+ pos += n
+ if err == nil {
+ if !isNull {
+ if !mc.parseTime {
+ continue
+ } else {
+ switch rows.columns[i].fieldType {
+ case fieldTypeTimestamp, fieldTypeDateTime,
+ fieldTypeDate, fieldTypeNewDate:
+ dest[i], err = parseDateTime(
+ string(dest[i].([]byte)),
+ mc.cfg.Loc,
+ )
+ if err == nil {
+ continue
+ }
+ default:
+ continue
+ }
+ }
+
+ } else {
+ dest[i] = nil
+ continue
+ }
+ }
+ return err // err != nil
+ }
+
+ return nil
+}
+
+// Reads Packets until EOF-Packet or an Error appears. Returns count of Packets read
+func (mc *mysqlConn) readUntilEOF() error {
+ for {
+ data, err := mc.readPacket()
+
+ // No Err and no EOF Packet
+ if err == nil && data[0] != iEOF {
+ continue
+ }
+ if err == nil && data[0] == iEOF && len(data) == 5 {
+ mc.status = readStatus(data[3:])
+ }
+
+ return err // Err or EOF
+ }
+}
+
+/******************************************************************************
+* Prepared Statements *
+******************************************************************************/
+
+// Prepare Result Packets
+// http://dev.mysql.com/doc/internals/en/com-stmt-prepare-response.html
+func (stmt *mysqlStmt) readPrepareResultPacket() (uint16, error) {
+ data, err := stmt.mc.readPacket()
+ if err == nil {
+ // packet indicator [1 byte]
+ if data[0] != iOK {
+ return 0, stmt.mc.handleErrorPacket(data)
+ }
+
+ // statement id [4 bytes]
+ stmt.id = binary.LittleEndian.Uint32(data[1:5])
+
+ // Column count [16 bit uint]
+ columnCount := binary.LittleEndian.Uint16(data[5:7])
+
+ // Param count [16 bit uint]
+ stmt.paramCount = int(binary.LittleEndian.Uint16(data[7:9]))
+
+ // Reserved [8 bit]
+
+ // Warning count [16 bit uint]
+ if !stmt.mc.strict {
+ return columnCount, nil
+ }
+
+ // Check for warnings count > 0, only available in MySQL > 4.1
+ if len(data) >= 12 && binary.LittleEndian.Uint16(data[10:12]) > 0 {
+ return columnCount, stmt.mc.getWarnings()
+ }
+ return columnCount, nil
+ }
+ return 0, err
+}
+
+// http://dev.mysql.com/doc/internals/en/com-stmt-send-long-data.html
+func (stmt *mysqlStmt) writeCommandLongData(paramID int, arg []byte) error {
+ maxLen := stmt.mc.maxPacketAllowed - 1
+ pktLen := maxLen
+
+ // After the header (bytes 0-3) follows before the data:
+ // 1 byte command
+ // 4 bytes stmtID
+ // 2 bytes paramID
+ const dataOffset = 1 + 4 + 2
+
+ // Can not use the write buffer since
+ // a) the buffer is too small
+ // b) it is in use
+ data := make([]byte, 4+1+4+2+len(arg))
+
+ copy(data[4+dataOffset:], arg)
+
+ for argLen := len(arg); argLen > 0; argLen -= pktLen - dataOffset {
+ if dataOffset+argLen < maxLen {
+ pktLen = dataOffset + argLen
+ }
+
+ stmt.mc.sequence = 0
+ // Add command byte [1 byte]
+ data[4] = comStmtSendLongData
+
+ // Add stmtID [32 bit]
+ data[5] = byte(stmt.id)
+ data[6] = byte(stmt.id >> 8)
+ data[7] = byte(stmt.id >> 16)
+ data[8] = byte(stmt.id >> 24)
+
+ // Add paramID [16 bit]
+ data[9] = byte(paramID)
+ data[10] = byte(paramID >> 8)
+
+ // Send CMD packet
+ err := stmt.mc.writePacket(data[:4+pktLen])
+ if err == nil {
+ data = data[pktLen-dataOffset:]
+ continue
+ }
+ return err
+
+ }
+
+ // Reset Packet Sequence
+ stmt.mc.sequence = 0
+ return nil
+}
+
+// Execute Prepared Statement
+// http://dev.mysql.com/doc/internals/en/com-stmt-execute.html
+func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
+ if len(args) != stmt.paramCount {
+ return fmt.Errorf(
+ "argument count mismatch (got: %d; has: %d)",
+ len(args),
+ stmt.paramCount,
+ )
+ }
+
+ const minPktLen = 4 + 1 + 4 + 1 + 4
+ mc := stmt.mc
+
+ // Reset packet-sequence
+ mc.sequence = 0
+
+ var data []byte
+
+ if len(args) == 0 {
+ data = mc.buf.takeBuffer(minPktLen)
+ } else {
+ data = mc.buf.takeCompleteBuffer()
+ }
+ if data == nil {
+ // can not take the buffer. Something must be wrong with the connection
+ errLog.Print(ErrBusyBuffer)
+ return driver.ErrBadConn
+ }
+
+ // command [1 byte]
+ data[4] = comStmtExecute
+
+ // statement_id [4 bytes]
+ data[5] = byte(stmt.id)
+ data[6] = byte(stmt.id >> 8)
+ data[7] = byte(stmt.id >> 16)
+ data[8] = byte(stmt.id >> 24)
+
+ // flags (0: CURSOR_TYPE_NO_CURSOR) [1 byte]
+ data[9] = 0x00
+
+ // iteration_count (uint32(1)) [4 bytes]
+ data[10] = 0x01
+ data[11] = 0x00
+ data[12] = 0x00
+ data[13] = 0x00
+
+ if len(args) > 0 {
+ pos := minPktLen
+
+ var nullMask []byte
+ if maskLen, typesLen := (len(args)+7)/8, 1+2*len(args); pos+maskLen+typesLen >= len(data) {
+ // buffer has to be extended but we don't know by how much so
+ // we depend on append after all data with known sizes fit.
+ // We stop at that because we deal with a lot of columns here
+ // which makes the required allocation size hard to guess.
+ tmp := make([]byte, pos+maskLen+typesLen)
+ copy(tmp[:pos], data[:pos])
+ data = tmp
+ nullMask = data[pos : pos+maskLen]
+ pos += maskLen
+ } else {
+ nullMask = data[pos : pos+maskLen]
+ for i := 0; i < maskLen; i++ {
+ nullMask[i] = 0
+ }
+ pos += maskLen
+ }
+
+ // newParameterBoundFlag 1 [1 byte]
+ data[pos] = 0x01
+ pos++
+
+ // type of each parameter [len(args)*2 bytes]
+ paramTypes := data[pos:]
+ pos += len(args) * 2
+
+ // value of each parameter [n bytes]
+ paramValues := data[pos:pos]
+ valuesCap := cap(paramValues)
+
+ for i, arg := range args {
+ // build NULL-bitmap
+ if arg == nil {
+ nullMask[i/8] |= 1 << (uint(i) & 7)
+ paramTypes[i+i] = fieldTypeNULL
+ paramTypes[i+i+1] = 0x00
+ continue
+ }
+
+ // cache types and values
+ switch v := arg.(type) {
+ case int64:
+ paramTypes[i+i] = fieldTypeLongLong
+ paramTypes[i+i+1] = 0x00
+
+ if cap(paramValues)-len(paramValues)-8 >= 0 {
+ paramValues = paramValues[:len(paramValues)+8]
+ binary.LittleEndian.PutUint64(
+ paramValues[len(paramValues)-8:],
+ uint64(v),
+ )
+ } else {
+ paramValues = append(paramValues,
+ uint64ToBytes(uint64(v))...,
+ )
+ }
+
+ case float64:
+ paramTypes[i+i] = fieldTypeDouble
+ paramTypes[i+i+1] = 0x00
+
+ if cap(paramValues)-len(paramValues)-8 >= 0 {
+ paramValues = paramValues[:len(paramValues)+8]
+ binary.LittleEndian.PutUint64(
+ paramValues[len(paramValues)-8:],
+ math.Float64bits(v),
+ )
+ } else {
+ paramValues = append(paramValues,
+ uint64ToBytes(math.Float64bits(v))...,
+ )
+ }
+
+ case bool:
+ paramTypes[i+i] = fieldTypeTiny
+ paramTypes[i+i+1] = 0x00
+
+ if v {
+ paramValues = append(paramValues, 0x01)
+ } else {
+ paramValues = append(paramValues, 0x00)
+ }
+
+ case []byte:
+ // Common case (non-nil value) first
+ if v != nil {
+ paramTypes[i+i] = fieldTypeString
+ paramTypes[i+i+1] = 0x00
+
+ if len(v) < mc.maxPacketAllowed-pos-len(paramValues)-(len(args)-(i+1))*64 {
+ paramValues = appendLengthEncodedInteger(paramValues,
+ uint64(len(v)),
+ )
+ paramValues = append(paramValues, v...)
+ } else {
+ if err := stmt.writeCommandLongData(i, v); err != nil {
+ return err
+ }
+ }
+ continue
+ }
+
+ // Handle []byte(nil) as a NULL value
+ nullMask[i/8] |= 1 << (uint(i) & 7)
+ paramTypes[i+i] = fieldTypeNULL
+ paramTypes[i+i+1] = 0x00
+
+ case string:
+ paramTypes[i+i] = fieldTypeString
+ paramTypes[i+i+1] = 0x00
+
+ if len(v) < mc.maxPacketAllowed-pos-len(paramValues)-(len(args)-(i+1))*64 {
+ paramValues = appendLengthEncodedInteger(paramValues,
+ uint64(len(v)),
+ )
+ paramValues = append(paramValues, v...)
+ } else {
+ if err := stmt.writeCommandLongData(i, []byte(v)); err != nil {
+ return err
+ }
+ }
+
+ case time.Time:
+ paramTypes[i+i] = fieldTypeString
+ paramTypes[i+i+1] = 0x00
+
+ var val []byte
+ if v.IsZero() {
+ val = []byte("0000-00-00")
+ } else {
+ val = []byte(v.In(mc.cfg.Loc).Format(timeFormat))
+ }
+
+ paramValues = appendLengthEncodedInteger(paramValues,
+ uint64(len(val)),
+ )
+ paramValues = append(paramValues, val...)
+
+ default:
+ return fmt.Errorf("can not convert type: %T", arg)
+ }
+ }
+
+ // Check if param values exceeded the available buffer
+ // In that case we must build the data packet with the new values buffer
+ if valuesCap != cap(paramValues) {
+ data = append(data[:pos], paramValues...)
+ mc.buf.buf = data
+ }
+
+ pos += len(paramValues)
+ data = data[:pos]
+ }
+
+ return mc.writePacket(data)
+}
+
+func (mc *mysqlConn) discardResults() error {
+ for mc.status&statusMoreResultsExists != 0 {
+ resLen, err := mc.readResultSetHeaderPacket()
+ if err != nil {
+ return err
+ }
+ if resLen > 0 {
+ // columns
+ if err := mc.readUntilEOF(); err != nil {
+ return err
+ }
+ // rows
+ if err := mc.readUntilEOF(); err != nil {
+ return err
+ }
+ } else {
+ mc.status &^= statusMoreResultsExists
+ }
+ }
+ return nil
+}
+
+// http://dev.mysql.com/doc/internals/en/binary-protocol-resultset-row.html
+func (rows *binaryRows) readRow(dest []driver.Value) error {
+ data, err := rows.mc.readPacket()
+ if err != nil {
+ return err
+ }
+
+ // packet indicator [1 byte]
+ if data[0] != iOK {
+ // EOF Packet
+ if data[0] == iEOF && len(data) == 5 {
+ rows.mc.status = readStatus(data[3:])
+ if err := rows.mc.discardResults(); err != nil {
+ return err
+ }
+ rows.mc = nil
+ return io.EOF
+ }
+ rows.mc = nil
+
+ // Error otherwise
+ return rows.mc.handleErrorPacket(data)
+ }
+
+ // NULL-bitmap, [(column-count + 7 + 2) / 8 bytes]
+ pos := 1 + (len(dest)+7+2)>>3
+ nullMask := data[1:pos]
+
+ for i := range dest {
+ // Field is NULL
+ // (byte >> bit-pos) % 2 == 1
+ if ((nullMask[(i+2)>>3] >> uint((i+2)&7)) & 1) == 1 {
+ dest[i] = nil
+ continue
+ }
+
+ // Convert to byte-coded string
+ switch rows.columns[i].fieldType {
+ case fieldTypeNULL:
+ dest[i] = nil
+ continue
+
+ // Numeric Types
+ case fieldTypeTiny:
+ if rows.columns[i].flags&flagUnsigned != 0 {
+ dest[i] = int64(data[pos])
+ } else {
+ dest[i] = int64(int8(data[pos]))
+ }
+ pos++
+ continue
+
+ case fieldTypeShort, fieldTypeYear:
+ if rows.columns[i].flags&flagUnsigned != 0 {
+ dest[i] = int64(binary.LittleEndian.Uint16(data[pos : pos+2]))
+ } else {
+ dest[i] = int64(int16(binary.LittleEndian.Uint16(data[pos : pos+2])))
+ }
+ pos += 2
+ continue
+
+ case fieldTypeInt24, fieldTypeLong:
+ if rows.columns[i].flags&flagUnsigned != 0 {
+ dest[i] = int64(binary.LittleEndian.Uint32(data[pos : pos+4]))
+ } else {
+ dest[i] = int64(int32(binary.LittleEndian.Uint32(data[pos : pos+4])))
+ }
+ pos += 4
+ continue
+
+ case fieldTypeLongLong:
+ if rows.columns[i].flags&flagUnsigned != 0 {
+ val := binary.LittleEndian.Uint64(data[pos : pos+8])
+ if val > math.MaxInt64 {
+ dest[i] = uint64ToString(val)
+ } else {
+ dest[i] = int64(val)
+ }
+ } else {
+ dest[i] = int64(binary.LittleEndian.Uint64(data[pos : pos+8]))
+ }
+ pos += 8
+ continue
+
+ case fieldTypeFloat:
+ dest[i] = float32(math.Float32frombits(binary.LittleEndian.Uint32(data[pos : pos+4])))
+ pos += 4
+ continue
+
+ case fieldTypeDouble:
+ dest[i] = math.Float64frombits(binary.LittleEndian.Uint64(data[pos : pos+8]))
+ pos += 8
+ continue
+
+ // Length coded Binary Strings
+ case fieldTypeDecimal, fieldTypeNewDecimal, fieldTypeVarChar,
+ fieldTypeBit, fieldTypeEnum, fieldTypeSet, fieldTypeTinyBLOB,
+ fieldTypeMediumBLOB, fieldTypeLongBLOB, fieldTypeBLOB,
+ fieldTypeVarString, fieldTypeString, fieldTypeGeometry, fieldTypeJSON:
+ var isNull bool
+ var n int
+ dest[i], isNull, n, err = readLengthEncodedString(data[pos:])
+ pos += n
+ if err == nil {
+ if !isNull {
+ continue
+ } else {
+ dest[i] = nil
+ continue
+ }
+ }
+ return err
+
+ case
+ fieldTypeDate, fieldTypeNewDate, // Date YYYY-MM-DD
+ fieldTypeTime, // Time [-][H]HH:MM:SS[.fractal]
+ fieldTypeTimestamp, fieldTypeDateTime: // Timestamp YYYY-MM-DD HH:MM:SS[.fractal]
+
+ num, isNull, n := readLengthEncodedInteger(data[pos:])
+ pos += n
+
+ switch {
+ case isNull:
+ dest[i] = nil
+ continue
+ case rows.columns[i].fieldType == fieldTypeTime:
+ // database/sql does not support an equivalent to TIME, return a string
+ var dstlen uint8
+ switch decimals := rows.columns[i].decimals; decimals {
+ case 0x00, 0x1f:
+ dstlen = 8
+ case 1, 2, 3, 4, 5, 6:
+ dstlen = 8 + 1 + decimals
+ default:
+ return fmt.Errorf(
+ "protocol error, illegal decimals value %d",
+ rows.columns[i].decimals,
+ )
+ }
+ dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen, true)
+ case rows.mc.parseTime:
+ dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.Loc)
+ default:
+ var dstlen uint8
+ if rows.columns[i].fieldType == fieldTypeDate {
+ dstlen = 10
+ } else {
+ switch decimals := rows.columns[i].decimals; decimals {
+ case 0x00, 0x1f:
+ dstlen = 19
+ case 1, 2, 3, 4, 5, 6:
+ dstlen = 19 + 1 + decimals
+ default:
+ return fmt.Errorf(
+ "protocol error, illegal decimals value %d",
+ rows.columns[i].decimals,
+ )
+ }
+ }
+ dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen, false)
+ }
+
+ if err == nil {
+ pos += int(num)
+ continue
+ } else {
+ return err
+ }
+
+ // Please report if this happens!
+ default:
+ return fmt.Errorf("unknown field type %d", rows.columns[i].fieldType)
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/result.go b/vendor/github.com/go-sql-driver/mysql/result.go
new file mode 100644
index 000000000..c6438d034
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/result.go
@@ -0,0 +1,22 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
+//
+// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package mysql
+
+type mysqlResult struct {
+ affectedRows int64
+ insertId int64
+}
+
+func (res *mysqlResult) LastInsertId() (int64, error) {
+ return res.insertId, nil
+}
+
+func (res *mysqlResult) RowsAffected() (int64, error) {
+ return res.affectedRows, nil
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/rows.go b/vendor/github.com/go-sql-driver/mysql/rows.go
new file mode 100644
index 000000000..c08255eee
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/rows.go
@@ -0,0 +1,112 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
+//
+// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package mysql
+
+import (
+ "database/sql/driver"
+ "io"
+)
+
+type mysqlField struct {
+ tableName string
+ name string
+ flags fieldFlag
+ fieldType byte
+ decimals byte
+}
+
+type mysqlRows struct {
+ mc *mysqlConn
+ columns []mysqlField
+}
+
+type binaryRows struct {
+ mysqlRows
+}
+
+type textRows struct {
+ mysqlRows
+}
+
+type emptyRows struct{}
+
+func (rows *mysqlRows) Columns() []string {
+ columns := make([]string, len(rows.columns))
+ if rows.mc != nil && rows.mc.cfg.ColumnsWithAlias {
+ for i := range columns {
+ if tableName := rows.columns[i].tableName; len(tableName) > 0 {
+ columns[i] = tableName + "." + rows.columns[i].name
+ } else {
+ columns[i] = rows.columns[i].name
+ }
+ }
+ } else {
+ for i := range columns {
+ columns[i] = rows.columns[i].name
+ }
+ }
+ return columns
+}
+
+func (rows *mysqlRows) Close() error {
+ mc := rows.mc
+ if mc == nil {
+ return nil
+ }
+ if mc.netConn == nil {
+ return ErrInvalidConn
+ }
+
+ // Remove unread packets from stream
+ err := mc.readUntilEOF()
+ if err == nil {
+ if err = mc.discardResults(); err != nil {
+ return err
+ }
+ }
+
+ rows.mc = nil
+ return err
+}
+
+func (rows *binaryRows) Next(dest []driver.Value) error {
+ if mc := rows.mc; mc != nil {
+ if mc.netConn == nil {
+ return ErrInvalidConn
+ }
+
+ // Fetch next row from stream
+ return rows.readRow(dest)
+ }
+ return io.EOF
+}
+
+func (rows *textRows) Next(dest []driver.Value) error {
+ if mc := rows.mc; mc != nil {
+ if mc.netConn == nil {
+ return ErrInvalidConn
+ }
+
+ // Fetch next row from stream
+ return rows.readRow(dest)
+ }
+ return io.EOF
+}
+
+func (rows emptyRows) Columns() []string {
+ return nil
+}
+
+func (rows emptyRows) Close() error {
+ return nil
+}
+
+func (rows emptyRows) Next(dest []driver.Value) error {
+ return io.EOF
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/statement.go b/vendor/github.com/go-sql-driver/mysql/statement.go
new file mode 100644
index 000000000..ead9a6bf4
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/statement.go
@@ -0,0 +1,150 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
+//
+// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package mysql
+
+import (
+ "database/sql/driver"
+ "fmt"
+ "reflect"
+ "strconv"
+)
+
+type mysqlStmt struct {
+ mc *mysqlConn
+ id uint32
+ paramCount int
+ columns []mysqlField // cached from the first query
+}
+
+func (stmt *mysqlStmt) Close() error {
+ if stmt.mc == nil || stmt.mc.netConn == nil {
+ errLog.Print(ErrInvalidConn)
+ return driver.ErrBadConn
+ }
+
+ err := stmt.mc.writeCommandPacketUint32(comStmtClose, stmt.id)
+ stmt.mc = nil
+ return err
+}
+
+func (stmt *mysqlStmt) NumInput() int {
+ return stmt.paramCount
+}
+
+func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
+ return converter{}
+}
+
+func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
+ if stmt.mc.netConn == nil {
+ errLog.Print(ErrInvalidConn)
+ return nil, driver.ErrBadConn
+ }
+ // Send command
+ err := stmt.writeExecutePacket(args)
+ if err != nil {
+ return nil, err
+ }
+
+ mc := stmt.mc
+
+ mc.affectedRows = 0
+ mc.insertId = 0
+
+ // Read Result
+ resLen, err := mc.readResultSetHeaderPacket()
+ if err == nil {
+ if resLen > 0 {
+ // Columns
+ err = mc.readUntilEOF()
+ if err != nil {
+ return nil, err
+ }
+
+ // Rows
+ err = mc.readUntilEOF()
+ }
+ if err == nil {
+ return &mysqlResult{
+ affectedRows: int64(mc.affectedRows),
+ insertId: int64(mc.insertId),
+ }, nil
+ }
+ }
+
+ return nil, err
+}
+
+func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
+ if stmt.mc.netConn == nil {
+ errLog.Print(ErrInvalidConn)
+ return nil, driver.ErrBadConn
+ }
+ // Send command
+ err := stmt.writeExecutePacket(args)
+ if err != nil {
+ return nil, err
+ }
+
+ mc := stmt.mc
+
+ // Read Result
+ resLen, err := mc.readResultSetHeaderPacket()
+ if err != nil {
+ return nil, err
+ }
+
+ rows := new(binaryRows)
+
+ if resLen > 0 {
+ rows.mc = mc
+ // Columns
+ // If not cached, read them and cache them
+ if stmt.columns == nil {
+ rows.columns, err = mc.readColumns(resLen)
+ stmt.columns = rows.columns
+ } else {
+ rows.columns = stmt.columns
+ err = mc.readUntilEOF()
+ }
+ }
+
+ return rows, err
+}
+
+type converter struct{}
+
+func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
+ if driver.IsValue(v) {
+ return v, nil
+ }
+
+ rv := reflect.ValueOf(v)
+ switch rv.Kind() {
+ case reflect.Ptr:
+ // indirect pointers
+ if rv.IsNil() {
+ return nil, nil
+ }
+ return c.ConvertValue(rv.Elem().Interface())
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return rv.Int(), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
+ return int64(rv.Uint()), nil
+ case reflect.Uint64:
+ u64 := rv.Uint()
+ if u64 >= 1<<63 {
+ return strconv.FormatUint(u64, 10), nil
+ }
+ return int64(u64), nil
+ case reflect.Float32, reflect.Float64:
+ return rv.Float(), nil
+ }
+ return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/transaction.go b/vendor/github.com/go-sql-driver/mysql/transaction.go
new file mode 100644
index 000000000..33c749b35
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/transaction.go
@@ -0,0 +1,31 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
+//
+// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package mysql
+
+type mysqlTx struct {
+ mc *mysqlConn
+}
+
+func (tx *mysqlTx) Commit() (err error) {
+ if tx.mc == nil || tx.mc.netConn == nil {
+ return ErrInvalidConn
+ }
+ err = tx.mc.exec("COMMIT")
+ tx.mc = nil
+ return
+}
+
+func (tx *mysqlTx) Rollback() (err error) {
+ if tx.mc == nil || tx.mc.netConn == nil {
+ return ErrInvalidConn
+ }
+ err = tx.mc.exec("ROLLBACK")
+ tx.mc = nil
+ return
+}
diff --git a/vendor/github.com/go-sql-driver/mysql/utils.go b/vendor/github.com/go-sql-driver/mysql/utils.go
new file mode 100644
index 000000000..d523b7ffd
--- /dev/null
+++ b/vendor/github.com/go-sql-driver/mysql/utils.go
@@ -0,0 +1,740 @@
+// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
+//
+// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package mysql
+
+import (
+ "crypto/sha1"
+ "crypto/tls"
+ "database/sql/driver"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "strings"
+ "time"
+)
+
+var (
+ tlsConfigRegister map[string]*tls.Config // Register for custom tls.Configs
+)
+
+// RegisterTLSConfig registers a custom tls.Config to be used with sql.Open.
+// Use the key as a value in the DSN where tls=value.
+//
+// rootCertPool := x509.NewCertPool()
+// pem, err := ioutil.ReadFile("/path/ca-cert.pem")
+// if err != nil {
+// log.Fatal(err)
+// }
+// if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
+// log.Fatal("Failed to append PEM.")
+// }
+// clientCert := make([]tls.Certificate, 0, 1)
+// certs, err := tls.LoadX509KeyPair("/path/client-cert.pem", "/path/client-key.pem")
+// if err != nil {
+// log.Fatal(err)
+// }
+// clientCert = append(clientCert, certs)
+// mysql.RegisterTLSConfig("custom", &tls.Config{
+// RootCAs: rootCertPool,
+// Certificates: clientCert,
+// })
+// db, err := sql.Open("mysql", "user@tcp(localhost:3306)/test?tls=custom")
+//
+func RegisterTLSConfig(key string, config *tls.Config) error {
+ if _, isBool := readBool(key); isBool || strings.ToLower(key) == "skip-verify" {
+ return fmt.Errorf("key '%s' is reserved", key)
+ }
+
+ if tlsConfigRegister == nil {
+ tlsConfigRegister = make(map[string]*tls.Config)
+ }
+
+ tlsConfigRegister[key] = config
+ return nil
+}
+
+// DeregisterTLSConfig removes the tls.Config associated with key.
+func DeregisterTLSConfig(key string) {
+ if tlsConfigRegister != nil {
+ delete(tlsConfigRegister, key)
+ }
+}
+
+// Returns the bool value of the input.
+// The 2nd return value indicates if the input was a valid bool value
+func readBool(input string) (value bool, valid bool) {
+ switch input {
+ case "1", "true", "TRUE", "True":
+ return true, true
+ case "0", "false", "FALSE", "False":
+ return false, true
+ }
+
+ // Not a valid bool value
+ return
+}
+
+/******************************************************************************
+* Authentication *
+******************************************************************************/
+
+// Encrypt password using 4.1+ method
+func scramblePassword(scramble, password []byte) []byte {
+ if len(password) == 0 {
+ return nil
+ }
+
+ // stage1Hash = SHA1(password)
+ crypt := sha1.New()
+ crypt.Write(password)
+ stage1 := crypt.Sum(nil)
+
+ // scrambleHash = SHA1(scramble + SHA1(stage1Hash))
+ // inner Hash
+ crypt.Reset()
+ crypt.Write(stage1)
+ hash := crypt.Sum(nil)
+
+ // outer Hash
+ crypt.Reset()
+ crypt.Write(scramble)
+ crypt.Write(hash)
+ scramble = crypt.Sum(nil)
+
+ // token = scrambleHash XOR stage1Hash
+ for i := range scramble {
+ scramble[i] ^= stage1[i]
+ }
+ return scramble
+}
+
+// Encrypt password using pre 4.1 (old password) method
+// https://github.com/atcurtis/mariadb/blob/master/mysys/my_rnd.c
+type myRnd struct {
+ seed1, seed2 uint32
+}
+
+const myRndMaxVal = 0x3FFFFFFF
+
+// Pseudo random number generator
+func newMyRnd(seed1, seed2 uint32) *myRnd {
+ return &myRnd{
+ seed1: seed1 % myRndMaxVal,
+ seed2: seed2 % myRndMaxVal,
+ }
+}
+
+// Tested to be equivalent to MariaDB's floating point variant
+// http://play.golang.org/p/QHvhd4qved
+// http://play.golang.org/p/RG0q4ElWDx
+func (r *myRnd) NextByte() byte {
+ r.seed1 = (r.seed1*3 + r.seed2) % myRndMaxVal
+ r.seed2 = (r.seed1 + r.seed2 + 33) % myRndMaxVal
+
+ return byte(uint64(r.seed1) * 31 / myRndMaxVal)
+}
+
+// Generate binary hash from byte string using insecure pre 4.1 method
+func pwHash(password []byte) (result [2]uint32) {
+ var add uint32 = 7
+ var tmp uint32
+
+ result[0] = 1345345333
+ result[1] = 0x12345671
+
+ for _, c := range password {
+ // skip spaces and tabs in password
+ if c == ' ' || c == '\t' {
+ continue
+ }
+
+ tmp = uint32(c)
+ result[0] ^= (((result[0] & 63) + add) * tmp) + (result[0] << 8)
+ result[1] += (result[1] << 8) ^ result[0]
+ add += tmp
+ }
+
+ // Remove sign bit (1<<31)-1)
+ result[0] &= 0x7FFFFFFF
+ result[1] &= 0x7FFFFFFF
+
+ return
+}
+
+// Encrypt password using insecure pre 4.1 method
+func scrambleOldPassword(scramble, password []byte) []byte {
+ if len(password) == 0 {
+ return nil
+ }
+
+ scramble = scramble[:8]
+
+ hashPw := pwHash(password)
+ hashSc := pwHash(scramble)
+
+ r := newMyRnd(hashPw[0]^hashSc[0], hashPw[1]^hashSc[1])
+
+ var out [8]byte
+ for i := range out {
+ out[i] = r.NextByte() + 64
+ }
+
+ mask := r.NextByte()
+ for i := range out {
+ out[i] ^= mask
+ }
+
+ return out[:]
+}
+
+/******************************************************************************
+* Time related utils *
+******************************************************************************/
+
+// NullTime represents a time.Time that may be NULL.
+// NullTime implements the Scanner interface so
+// it can be used as a scan destination:
+//
+// var nt NullTime
+// err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt)
+// ...
+// if nt.Valid {
+// // use nt.Time
+// } else {
+// // NULL value
+// }
+//
+// This NullTime implementation is not driver-specific
+type NullTime struct {
+ Time time.Time
+ Valid bool // Valid is true if Time is not NULL
+}
+
+// Scan implements the Scanner interface.
+// The value type must be time.Time or string / []byte (formatted time-string),
+// otherwise Scan fails.
+func (nt *NullTime) Scan(value interface{}) (err error) {
+ if value == nil {
+ nt.Time, nt.Valid = time.Time{}, false
+ return
+ }
+
+ switch v := value.(type) {
+ case time.Time:
+ nt.Time, nt.Valid = v, true
+ return
+ case []byte:
+ nt.Time, err = parseDateTime(string(v), time.UTC)
+ nt.Valid = (err == nil)
+ return
+ case string:
+ nt.Time, err = parseDateTime(v, time.UTC)
+ nt.Valid = (err == nil)
+ return
+ }
+
+ nt.Valid = false
+ return fmt.Errorf("Can't convert %T to time.Time", value)
+}
+
+// Value implements the driver Valuer interface.
+func (nt NullTime) Value() (driver.Value, error) {
+ if !nt.Valid {
+ return nil, nil
+ }
+ return nt.Time, nil
+}
+
+func parseDateTime(str string, loc *time.Location) (t time.Time, err error) {
+ base := "0000-00-00 00:00:00.0000000"
+ switch len(str) {
+ case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
+ if str == base[:len(str)] {
+ return
+ }
+ t, err = time.Parse(timeFormat[:len(str)], str)
+ default:
+ err = fmt.Errorf("invalid time string: %s", str)
+ return
+ }
+
+ // Adjust location
+ if err == nil && loc != time.UTC {
+ y, mo, d := t.Date()
+ h, mi, s := t.Clock()
+ t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil
+ }
+
+ return
+}
+
+func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Value, error) {
+ switch num {
+ case 0:
+ return time.Time{}, nil
+ case 4:
+ return time.Date(
+ int(binary.LittleEndian.Uint16(data[:2])), // year
+ time.Month(data[2]), // month
+ int(data[3]), // day
+ 0, 0, 0, 0,
+ loc,
+ ), nil
+ case 7:
+ return time.Date(
+ int(binary.LittleEndian.Uint16(data[:2])), // year
+ time.Month(data[2]), // month
+ int(data[3]), // day
+ int(data[4]), // hour
+ int(data[5]), // minutes
+ int(data[6]), // seconds
+ 0,
+ loc,
+ ), nil
+ case 11:
+ return time.Date(
+ int(binary.LittleEndian.Uint16(data[:2])), // year
+ time.Month(data[2]), // month
+ int(data[3]), // day
+ int(data[4]), // hour
+ int(data[5]), // minutes
+ int(data[6]), // seconds
+ int(binary.LittleEndian.Uint32(data[7:11]))*1000, // nanoseconds
+ loc,
+ ), nil
+ }
+ return nil, fmt.Errorf("invalid DATETIME packet length %d", num)
+}
+
+// zeroDateTime is used in formatBinaryDateTime to avoid an allocation
+// if the DATE or DATETIME has the zero value.
+// It must never be changed.
+// The current behavior depends on database/sql copying the result.
+var zeroDateTime = []byte("0000-00-00 00:00:00.000000")
+
+const digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
+const digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+
+func formatBinaryDateTime(src []byte, length uint8, justTime bool) (driver.Value, error) {
+ // length expects the deterministic length of the zero value,
+ // negative time and 100+ hours are automatically added if needed
+ if len(src) == 0 {
+ if justTime {
+ return zeroDateTime[11 : 11+length], nil
+ }
+ return zeroDateTime[:length], nil
+ }
+ var dst []byte // return value
+ var pt, p1, p2, p3 byte // current digit pair
+ var zOffs byte // offset of value in zeroDateTime
+ if justTime {
+ switch length {
+ case
+ 8, // time (can be up to 10 when negative and 100+ hours)
+ 10, 11, 12, 13, 14, 15: // time with fractional seconds
+ default:
+ return nil, fmt.Errorf("illegal TIME length %d", length)
+ }
+ switch len(src) {
+ case 8, 12:
+ default:
+ return nil, fmt.Errorf("invalid TIME packet length %d", len(src))
+ }
+ // +2 to enable negative time and 100+ hours
+ dst = make([]byte, 0, length+2)
+ if src[0] == 1 {
+ dst = append(dst, '-')
+ }
+ if src[1] != 0 {
+ hour := uint16(src[1])*24 + uint16(src[5])
+ pt = byte(hour / 100)
+ p1 = byte(hour - 100*uint16(pt))
+ dst = append(dst, digits01[pt])
+ } else {
+ p1 = src[5]
+ }
+ zOffs = 11
+ src = src[6:]
+ } else {
+ switch length {
+ case 10, 19, 21, 22, 23, 24, 25, 26:
+ default:
+ t := "DATE"
+ if length > 10 {
+ t += "TIME"
+ }
+ return nil, fmt.Errorf("illegal %s length %d", t, length)
+ }
+ switch len(src) {
+ case 4, 7, 11:
+ default:
+ t := "DATE"
+ if length > 10 {
+ t += "TIME"
+ }
+ return nil, fmt.Errorf("illegal %s packet length %d", t, len(src))
+ }
+ dst = make([]byte, 0, length)
+ // start with the date
+ year := binary.LittleEndian.Uint16(src[:2])
+ pt = byte(year / 100)
+ p1 = byte(year - 100*uint16(pt))
+ p2, p3 = src[2], src[3]
+ dst = append(dst,
+ digits10[pt], digits01[pt],
+ digits10[p1], digits01[p1], '-',
+ digits10[p2], digits01[p2], '-',
+ digits10[p3], digits01[p3],
+ )
+ if length == 10 {
+ return dst, nil
+ }
+ if len(src) == 4 {
+ return append(dst, zeroDateTime[10:length]...), nil
+ }
+ dst = append(dst, ' ')
+ p1 = src[4] // hour
+ src = src[5:]
+ }
+ // p1 is 2-digit hour, src is after hour
+ p2, p3 = src[0], src[1]
+ dst = append(dst,
+ digits10[p1], digits01[p1], ':',
+ digits10[p2], digits01[p2], ':',
+ digits10[p3], digits01[p3],
+ )
+ if length <= byte(len(dst)) {
+ return dst, nil
+ }
+ src = src[2:]
+ if len(src) == 0 {
+ return append(dst, zeroDateTime[19:zOffs+length]...), nil
+ }
+ microsecs := binary.LittleEndian.Uint32(src[:4])
+ p1 = byte(microsecs / 10000)
+ microsecs -= 10000 * uint32(p1)
+ p2 = byte(microsecs / 100)
+ microsecs -= 100 * uint32(p2)
+ p3 = byte(microsecs)
+ switch decimals := zOffs + length - 20; decimals {
+ default:
+ return append(dst, '.',
+ digits10[p1], digits01[p1],
+ digits10[p2], digits01[p2],
+ digits10[p3], digits01[p3],
+ ), nil
+ case 1:
+ return append(dst, '.',
+ digits10[p1],
+ ), nil
+ case 2:
+ return append(dst, '.',
+ digits10[p1], digits01[p1],
+ ), nil
+ case 3:
+ return append(dst, '.',
+ digits10[p1], digits01[p1],
+ digits10[p2],
+ ), nil
+ case 4:
+ return append(dst, '.',
+ digits10[p1], digits01[p1],
+ digits10[p2], digits01[p2],
+ ), nil
+ case 5:
+ return append(dst, '.',
+ digits10[p1], digits01[p1],
+ digits10[p2], digits01[p2],
+ digits10[p3],
+ ), nil
+ }
+}
+
+/******************************************************************************
+* Convert from and to bytes *
+******************************************************************************/
+
+func uint64ToBytes(n uint64) []byte {
+ return []byte{
+ byte(n),
+ byte(n >> 8),
+ byte(n >> 16),
+ byte(n >> 24),
+ byte(n >> 32),
+ byte(n >> 40),
+ byte(n >> 48),
+ byte(n >> 56),
+ }
+}
+
+func uint64ToString(n uint64) []byte {
+ var a [20]byte
+ i := 20
+
+ // U+0030 = 0
+ // ...
+ // U+0039 = 9
+
+ var q uint64
+ for n >= 10 {
+ i--
+ q = n / 10
+ a[i] = uint8(n-q*10) + 0x30
+ n = q
+ }
+
+ i--
+ a[i] = uint8(n) + 0x30
+
+ return a[i:]
+}
+
+// treats string value as unsigned integer representation
+func stringToInt(b []byte) int {
+ val := 0
+ for i := range b {
+ val *= 10
+ val += int(b[i] - 0x30)
+ }
+ return val
+}
+
+// returns the string read as a bytes slice, wheter the value is NULL,
+// the number of bytes read and an error, in case the string is longer than
+// the input slice
+func readLengthEncodedString(b []byte) ([]byte, bool, int, error) {
+ // Get length
+ num, isNull, n := readLengthEncodedInteger(b)
+ if num < 1 {
+ return b[n:n], isNull, n, nil
+ }
+
+ n += int(num)
+
+ // Check data length
+ if len(b) >= n {
+ return b[n-int(num) : n], false, n, nil
+ }
+ return nil, false, n, io.EOF
+}
+
+// returns the number of bytes skipped and an error, in case the string is
+// longer than the input slice
+func skipLengthEncodedString(b []byte) (int, error) {
+ // Get length
+ num, _, n := readLengthEncodedInteger(b)
+ if num < 1 {
+ return n, nil
+ }
+
+ n += int(num)
+
+ // Check data length
+ if len(b) >= n {
+ return n, nil
+ }
+ return n, io.EOF
+}
+
+// returns the number read, whether the value is NULL and the number of bytes read
+func readLengthEncodedInteger(b []byte) (uint64, bool, int) {
+ // See issue #349
+ if len(b) == 0 {
+ return 0, true, 1
+ }
+ switch b[0] {
+
+ // 251: NULL
+ case 0xfb:
+ return 0, true, 1
+
+ // 252: value of following 2
+ case 0xfc:
+ return uint64(b[1]) | uint64(b[2])<<8, false, 3
+
+ // 253: value of following 3
+ case 0xfd:
+ return uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16, false, 4
+
+ // 254: value of following 8
+ case 0xfe:
+ return uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 |
+ uint64(b[4])<<24 | uint64(b[5])<<32 | uint64(b[6])<<40 |
+ uint64(b[7])<<48 | uint64(b[8])<<56,
+ false, 9
+ }
+
+ // 0-250: value of first byte
+ return uint64(b[0]), false, 1
+}
+
+// encodes a uint64 value and appends it to the given bytes slice
+func appendLengthEncodedInteger(b []byte, n uint64) []byte {
+ switch {
+ case n <= 250:
+ return append(b, byte(n))
+
+ case n <= 0xffff:
+ return append(b, 0xfc, byte(n), byte(n>>8))
+
+ case n <= 0xffffff:
+ return append(b, 0xfd, byte(n), byte(n>>8), byte(n>>16))
+ }
+ return append(b, 0xfe, byte(n), byte(n>>8), byte(n>>16), byte(n>>24),
+ byte(n>>32), byte(n>>40), byte(n>>48), byte(n>>56))
+}
+
+// reserveBuffer checks cap(buf) and expand buffer to len(buf) + appendSize.
+// If cap(buf) is not enough, reallocate new buffer.
+func reserveBuffer(buf []byte, appendSize int) []byte {
+ newSize := len(buf) + appendSize
+ if cap(buf) < newSize {
+ // Grow buffer exponentially
+ newBuf := make([]byte, len(buf)*2+appendSize)
+ copy(newBuf, buf)
+ buf = newBuf
+ }
+ return buf[:newSize]
+}
+
+// escapeBytesBackslash escapes []byte with backslashes (\)
+// This escapes the contents of a string (provided as []byte) by adding backslashes before special
+// characters, and turning others into specific escape sequences, such as
+// turning newlines into \n and null bytes into \0.
+// https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L823-L932
+func escapeBytesBackslash(buf, v []byte) []byte {
+ pos := len(buf)
+ buf = reserveBuffer(buf, len(v)*2)
+
+ for _, c := range v {
+ switch c {
+ case '\x00':
+ buf[pos] = '\\'
+ buf[pos+1] = '0'
+ pos += 2
+ case '\n':
+ buf[pos] = '\\'
+ buf[pos+1] = 'n'
+ pos += 2
+ case '\r':
+ buf[pos] = '\\'
+ buf[pos+1] = 'r'
+ pos += 2
+ case '\x1a':
+ buf[pos] = '\\'
+ buf[pos+1] = 'Z'
+ pos += 2
+ case '\'':
+ buf[pos] = '\\'
+ buf[pos+1] = '\''
+ pos += 2
+ case '"':
+ buf[pos] = '\\'
+ buf[pos+1] = '"'
+ pos += 2
+ case '\\':
+ buf[pos] = '\\'
+ buf[pos+1] = '\\'
+ pos += 2
+ default:
+ buf[pos] = c
+ pos++
+ }
+ }
+
+ return buf[:pos]
+}
+
+// escapeStringBackslash is similar to escapeBytesBackslash but for string.
+func escapeStringBackslash(buf []byte, v string) []byte {
+ pos := len(buf)
+ buf = reserveBuffer(buf, len(v)*2)
+
+ for i := 0; i < len(v); i++ {
+ c := v[i]
+ switch c {
+ case '\x00':
+ buf[pos] = '\\'
+ buf[pos+1] = '0'
+ pos += 2
+ case '\n':
+ buf[pos] = '\\'
+ buf[pos+1] = 'n'
+ pos += 2
+ case '\r':
+ buf[pos] = '\\'
+ buf[pos+1] = 'r'
+ pos += 2
+ case '\x1a':
+ buf[pos] = '\\'
+ buf[pos+1] = 'Z'
+ pos += 2
+ case '\'':
+ buf[pos] = '\\'
+ buf[pos+1] = '\''
+ pos += 2
+ case '"':
+ buf[pos] = '\\'
+ buf[pos+1] = '"'
+ pos += 2
+ case '\\':
+ buf[pos] = '\\'
+ buf[pos+1] = '\\'
+ pos += 2
+ default:
+ buf[pos] = c
+ pos++
+ }
+ }
+
+ return buf[:pos]
+}
+
+// escapeBytesQuotes escapes apostrophes in []byte by doubling them up.
+// This escapes the contents of a string by doubling up any apostrophes that
+// it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in
+// effect on the server.
+// https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L963-L1038
+func escapeBytesQuotes(buf, v []byte) []byte {
+ pos := len(buf)
+ buf = reserveBuffer(buf, len(v)*2)
+
+ for _, c := range v {
+ if c == '\'' {
+ buf[pos] = '\''
+ buf[pos+1] = '\''
+ pos += 2
+ } else {
+ buf[pos] = c
+ pos++
+ }
+ }
+
+ return buf[:pos]
+}
+
+// escapeStringQuotes is similar to escapeBytesQuotes but for string.
+func escapeStringQuotes(buf []byte, v string) []byte {
+ pos := len(buf)
+ buf = reserveBuffer(buf, len(v)*2)
+
+ for i := 0; i < len(v); i++ {
+ c := v[i]
+ if c == '\'' {
+ buf[pos] = '\''
+ buf[pos+1] = '\''
+ pos += 2
+ } else {
+ buf[pos] = c
+ pos++
+ }
+ }
+
+ return buf[:pos]
+}
diff --git a/vendor/github.com/goamz/goamz/LICENSE b/vendor/github.com/goamz/goamz/LICENSE
new file mode 100644
index 000000000..53320c352
--- /dev/null
+++ b/vendor/github.com/goamz/goamz/LICENSE
@@ -0,0 +1,185 @@
+This software is licensed under the LGPLv3, included below.
+
+As a special exception to the GNU Lesser General Public License version 3
+("LGPL3"), the copyright holders of this Library give you permission to
+convey to a third party a Combined Work that links statically or dynamically
+to this Library without providing any Minimal Corresponding Source or
+Minimal Application Code as set out in 4d or providing the installation
+information set out in section 4e, provided that you comply with the other
+provisions of LGPL3 and provided that you meet, for the Application the
+terms and conditions of the license(s) which apply to the Application.
+
+Except as stated in this special exception, the provisions of LGPL3 will
+continue to comply in full to this Library. If you modify this Library, you
+may apply this exception to your version of this Library, but you are not
+obliged to do so. If you do not wish to do so, delete this exception
+statement from your version. This exception does not (and cannot) modify any
+license terms which apply to the Application, with which you must still
+comply.
+
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/vendor/github.com/goamz/goamz/aws/attempt.go b/vendor/github.com/goamz/goamz/aws/attempt.go
new file mode 100644
index 000000000..c0654f5d8
--- /dev/null
+++ b/vendor/github.com/goamz/goamz/aws/attempt.go
@@ -0,0 +1,74 @@
+package aws
+
+import (
+ "time"
+)
+
+// AttemptStrategy represents a strategy for waiting for an action
+// to complete successfully. This is an internal type used by the
+// implementation of other goamz packages.
+type AttemptStrategy struct {
+ Total time.Duration // total duration of attempt.
+ Delay time.Duration // interval between each try in the burst.
+ Min int // minimum number of retries; overrides Total
+}
+
+type Attempt struct {
+ strategy AttemptStrategy
+ last time.Time
+ end time.Time
+ force bool
+ count int
+}
+
+// Start begins a new sequence of attempts for the given strategy.
+func (s AttemptStrategy) Start() *Attempt {
+ now := time.Now()
+ return &Attempt{
+ strategy: s,
+ last: now,
+ end: now.Add(s.Total),
+ force: true,
+ }
+}
+
+// Next waits until it is time to perform the next attempt or returns
+// false if it is time to stop trying.
+func (a *Attempt) Next() bool {
+ now := time.Now()
+ sleep := a.nextSleep(now)
+ if !a.force && !now.Add(sleep).Before(a.end) && a.strategy.Min <= a.count {
+ return false
+ }
+ a.force = false
+ if sleep > 0 && a.count > 0 {
+ time.Sleep(sleep)
+ now = time.Now()
+ }
+ a.count++
+ a.last = now
+ return true
+}
+
+func (a *Attempt) nextSleep(now time.Time) time.Duration {
+ sleep := a.strategy.Delay - now.Sub(a.last)
+ if sleep < 0 {
+ return 0
+ }
+ return sleep
+}
+
+// HasNext returns whether another attempt will be made if the current
+// one fails. If it returns true, the following call to Next is
+// guaranteed to return true.
+func (a *Attempt) HasNext() bool {
+ if a.force || a.strategy.Min > a.count {
+ return true
+ }
+ now := time.Now()
+ if now.Add(a.nextSleep(now)).Before(a.end) {
+ a.force = true
+ return true
+ }
+ return false
+}
diff --git a/vendor/github.com/goamz/goamz/aws/aws.go b/vendor/github.com/goamz/goamz/aws/aws.go
new file mode 100644
index 000000000..77bf563d6
--- /dev/null
+++ b/vendor/github.com/goamz/goamz/aws/aws.go
@@ -0,0 +1,432 @@
+//
+// goamz - Go packages to interact with the Amazon Web Services.
+//
+// https://wiki.ubuntu.com/goamz
+//
+// Copyright (c) 2011 Canonical Ltd.
+//
+// Written by Gustavo Niemeyer <gustavo.niemeyer@canonical.com>
+//
+package aws
+
+import (
+ "encoding/json"
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "os"
+ "time"
+
+ "github.com/vaughan0/go-ini"
+)
+
+// Defines the valid signers
+const (
+ V2Signature = iota
+ V4Signature = iota
+ Route53Signature = iota
+)
+
+// Defines the service endpoint and correct Signer implementation to use
+// to sign requests for this endpoint
+type ServiceInfo struct {
+ Endpoint string
+ Signer uint
+}
+
+// Region defines the URLs where AWS services may be accessed.
+//
+// See http://goo.gl/d8BP1 for more details.
+type Region struct {
+ Name string // the canonical name of this region.
+ EC2Endpoint string
+ S3Endpoint string
+ S3BucketEndpoint string // Not needed by AWS S3. Use ${bucket} for bucket name.
+ S3LocationConstraint bool // true if this region requires a LocationConstraint declaration.
+ S3LowercaseBucket bool // true if the region requires bucket names to be lower case.
+ SDBEndpoint string
+ SESEndpoint string
+ SNSEndpoint string
+ SQSEndpoint string
+ IAMEndpoint string
+ ELBEndpoint string
+ DynamoDBEndpoint string
+ CloudWatchServicepoint ServiceInfo
+ AutoScalingEndpoint string
+ RDSEndpoint ServiceInfo
+ STSEndpoint string
+ CloudFormationEndpoint string
+ ECSEndpoint string
+ DynamoDBStreamsEndpoint string
+}
+
+var Regions = map[string]Region{
+ APNortheast.Name: APNortheast,
+ APSoutheast.Name: APSoutheast,
+ APSoutheast2.Name: APSoutheast2,
+ EUCentral.Name: EUCentral,
+ EUWest.Name: EUWest,
+ USEast.Name: USEast,
+ USWest.Name: USWest,
+ USWest2.Name: USWest2,
+ USGovWest.Name: USGovWest,
+ SAEast.Name: SAEast,
+ CNNorth.Name: CNNorth,
+}
+
+// Designates a signer interface suitable for signing AWS requests, params
+// should be appropriately encoded for the request before signing.
+//
+// A signer should be initialized with Auth and the appropriate endpoint.
+type Signer interface {
+ Sign(method, path string, params map[string]string)
+}
+
+// An AWS Service interface with the API to query the AWS service
+//
+// Supplied as an easy way to mock out service calls during testing.
+type AWSService interface {
+ // Queries the AWS service at a given method/path with the params and
+ // returns an http.Response and error
+ Query(method, path string, params map[string]string) (*http.Response, error)
+ // Builds an error given an XML payload in the http.Response, can be used
+ // to process an error if the status code is not 200 for example.
+ BuildError(r *http.Response) error
+}
+
+// Implements a Server Query/Post API to easily query AWS services and build
+// errors when desired
+type Service struct {
+ service ServiceInfo
+ signer Signer
+}
+
+// Create a base set of params for an action
+func MakeParams(action string) map[string]string {
+ params := make(map[string]string)
+ params["Action"] = action
+ return params
+}
+
+// Create a new AWS server to handle making requests
+func NewService(auth Auth, service ServiceInfo) (s *Service, err error) {
+ var signer Signer
+ switch service.Signer {
+ case V2Signature:
+ signer, err = NewV2Signer(auth, service)
+ // case V4Signature:
+ // signer, err = NewV4Signer(auth, service, Regions["eu-west-1"])
+ default:
+ err = fmt.Errorf("Unsupported signer for service")
+ }
+ if err != nil {
+ return
+ }
+ s = &Service{service: service, signer: signer}
+ return
+}
+
+func (s *Service) Query(method, path string, params map[string]string) (resp *http.Response, err error) {
+ params["Timestamp"] = time.Now().UTC().Format(time.RFC3339)
+ u, err := url.Parse(s.service.Endpoint)
+ if err != nil {
+ return nil, err
+ }
+ u.Path = path
+
+ s.signer.Sign(method, path, params)
+ if method == "GET" {
+ u.RawQuery = multimap(params).Encode()
+ resp, err = http.Get(u.String())
+ } else if method == "POST" {
+ resp, err = http.PostForm(u.String(), multimap(params))
+ }
+
+ return
+}
+
+func (s *Service) BuildError(r *http.Response) error {
+ errors := ErrorResponse{}
+ xml.NewDecoder(r.Body).Decode(&errors)
+ var err Error
+ err = errors.Errors
+ err.RequestId = errors.RequestId
+ err.StatusCode = r.StatusCode
+ if err.Message == "" {
+ err.Message = r.Status
+ }
+ return &err
+}
+
+type ErrorResponse struct {
+ Errors Error `xml:"Error"`
+ RequestId string // A unique ID for tracking the request
+}
+
+type Error struct {
+ StatusCode int
+ Type string
+ Code string
+ Message string
+ RequestId string
+}
+
+func (err *Error) Error() string {
+ return fmt.Sprintf("Type: %s, Code: %s, Message: %s",
+ err.Type, err.Code, err.Message,
+ )
+}
+
+type Auth struct {
+ AccessKey, SecretKey string
+ token string
+ expiration time.Time
+}
+
+func (a *Auth) Token() string {
+ if a.token == "" {
+ return ""
+ }
+ if time.Since(a.expiration) >= -30*time.Second { //in an ideal world this should be zero assuming the instance is synching it's clock
+ *a, _ = GetAuth("", "", "", time.Time{})
+ }
+ return a.token
+}
+
+func (a *Auth) Expiration() time.Time {
+ return a.expiration
+}
+
+// To be used with other APIs that return auth credentials such as STS
+func NewAuth(accessKey, secretKey, token string, expiration time.Time) *Auth {
+ return &Auth{
+ AccessKey: accessKey,
+ SecretKey: secretKey,
+ token: token,
+ expiration: expiration,
+ }
+}
+
+// ResponseMetadata
+type ResponseMetadata struct {
+ RequestId string // A unique ID for tracking the request
+}
+
+type BaseResponse struct {
+ ResponseMetadata ResponseMetadata
+}
+
+var unreserved = make([]bool, 128)
+var hex = "0123456789ABCDEF"
+
+func init() {
+ // RFC3986
+ u := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890-_.~"
+ for _, c := range u {
+ unreserved[c] = true
+ }
+}
+
+func multimap(p map[string]string) url.Values {
+ q := make(url.Values, len(p))
+ for k, v := range p {
+ q[k] = []string{v}
+ }
+ return q
+}
+
+type credentials struct {
+ Code string
+ LastUpdated string
+ Type string
+ AccessKeyId string
+ SecretAccessKey string
+ Token string
+ Expiration string
+}
+
+// GetMetaData retrieves instance metadata about the current machine.
+//
+// See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AESDG-chapter-instancedata.html for more details.
+func GetMetaData(path string) (contents []byte, err error) {
+ url := "http://169.254.169.254/latest/meta-data/" + path
+
+ resp, err := RetryingClient.Get(url)
+ if err != nil {
+ return
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != 200 {
+ err = fmt.Errorf("Code %d returned for url %s", resp.StatusCode, url)
+ return
+ }
+
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return
+ }
+ return []byte(body), err
+}
+
+func getInstanceCredentials() (cred credentials, err error) {
+ credentialPath := "iam/security-credentials/"
+
+ // Get the instance role
+ role, err := GetMetaData(credentialPath)
+ if err != nil {
+ return
+ }
+
+ // Get the instance role credentials
+ credentialJSON, err := GetMetaData(credentialPath + string(role))
+ if err != nil {
+ return
+ }
+
+ err = json.Unmarshal([]byte(credentialJSON), &cred)
+ return
+}
+
+// GetAuth creates an Auth based on either passed in credentials,
+// environment information or instance based role credentials.
+func GetAuth(accessKey string, secretKey, token string, expiration time.Time) (auth Auth, err error) {
+ // First try passed in credentials
+ if accessKey != "" && secretKey != "" {
+ return Auth{accessKey, secretKey, token, expiration}, nil
+ }
+
+ // Next try to get auth from the shared credentials file
+ auth, err = SharedAuth()
+ if err == nil {
+ // Found auth, return
+ return
+ }
+
+ // Next try to get auth from the environment
+ auth, err = EnvAuth()
+ if err == nil {
+ // Found auth, return
+ return
+ }
+
+ // Next try getting auth from the instance role
+ cred, err := getInstanceCredentials()
+ if err == nil {
+ // Found auth, return
+ auth.AccessKey = cred.AccessKeyId
+ auth.SecretKey = cred.SecretAccessKey
+ auth.token = cred.Token
+ exptdate, err := time.Parse("2006-01-02T15:04:05Z", cred.Expiration)
+ if err != nil {
+ err = fmt.Errorf("Error Parseing expiration date: cred.Expiration :%s , error: %s \n", cred.Expiration, err)
+ }
+ auth.expiration = exptdate
+ return auth, err
+ }
+ err = errors.New("No valid AWS authentication found")
+ return auth, err
+}
+
+// EnvAuth creates an Auth based on environment information.
+// The AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment
+// variables are used.
+// AWS_SESSION_TOKEN is used if present.
+func EnvAuth() (auth Auth, err error) {
+ auth.AccessKey = os.Getenv("AWS_ACCESS_KEY_ID")
+ if auth.AccessKey == "" {
+ auth.AccessKey = os.Getenv("AWS_ACCESS_KEY")
+ }
+
+ auth.SecretKey = os.Getenv("AWS_SECRET_ACCESS_KEY")
+ if auth.SecretKey == "" {
+ auth.SecretKey = os.Getenv("AWS_SECRET_KEY")
+ }
+ if auth.AccessKey == "" {
+ err = errors.New("AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY not found in environment")
+ }
+ if auth.SecretKey == "" {
+ err = errors.New("AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY not found in environment")
+ }
+
+ auth.token = os.Getenv("AWS_SESSION_TOKEN")
+ return
+}
+
+// SharedAuth creates an Auth based on shared credentials stored in
+// $HOME/.aws/credentials. The AWS_PROFILE environment variables is used to
+// select the profile.
+func SharedAuth() (auth Auth, err error) {
+ var profileName = os.Getenv("AWS_PROFILE")
+
+ if profileName == "" {
+ profileName = "default"
+ }
+
+ var credentialsFile = os.Getenv("AWS_CREDENTIAL_FILE")
+ if credentialsFile == "" {
+ var homeDir = os.Getenv("HOME")
+ if homeDir == "" {
+ err = errors.New("Could not get HOME")
+ return
+ }
+ credentialsFile = homeDir + "/.aws/credentials"
+ }
+
+ file, err := ini.LoadFile(credentialsFile)
+ if err != nil {
+ err = errors.New("Couldn't parse AWS credentials file")
+ return
+ }
+
+ var profile = file[profileName]
+ if profile == nil {
+ err = errors.New("Couldn't find profile in AWS credentials file")
+ return
+ }
+
+ auth.AccessKey = profile["aws_access_key_id"]
+ auth.SecretKey = profile["aws_secret_access_key"]
+
+ if auth.AccessKey == "" {
+ err = errors.New("AWS_ACCESS_KEY_ID not found in environment in credentials file")
+ }
+ if auth.SecretKey == "" {
+ err = errors.New("AWS_SECRET_ACCESS_KEY not found in credentials file")
+ }
+ return
+}
+
+// Encode takes a string and URI-encodes it in a way suitable
+// to be used in AWS signatures.
+func Encode(s string) string {
+ encode := false
+ for i := 0; i != len(s); i++ {
+ c := s[i]
+ if c > 127 || !unreserved[c] {
+ encode = true
+ break
+ }
+ }
+ if !encode {
+ return s
+ }
+ e := make([]byte, len(s)*3)
+ ei := 0
+ for i := 0; i != len(s); i++ {
+ c := s[i]
+ if c > 127 || !unreserved[c] {
+ e[ei] = '%'
+ e[ei+1] = hex[c>>4]
+ e[ei+2] = hex[c&0xF]
+ ei += 3
+ } else {
+ e[ei] = c
+ ei += 1
+ }
+ }
+ return string(e[:ei])
+}
diff --git a/vendor/github.com/goamz/goamz/aws/client.go b/vendor/github.com/goamz/goamz/aws/client.go
new file mode 100644
index 000000000..86d2ccec8
--- /dev/null
+++ b/vendor/github.com/goamz/goamz/aws/client.go
@@ -0,0 +1,124 @@
+package aws
+
+import (
+ "math"
+ "net"
+ "net/http"
+ "time"
+)
+
+type RetryableFunc func(*http.Request, *http.Response, error) bool
+type WaitFunc func(try int)
+type DeadlineFunc func() time.Time
+
+type ResilientTransport struct {
+ // Timeout is the maximum amount of time a dial will wait for
+ // a connect to complete.
+ //
+ // The default is no timeout.
+ //
+ // With or without a timeout, the operating system may impose
+ // its own earlier timeout. For instance, TCP timeouts are
+ // often around 3 minutes.
+ DialTimeout time.Duration
+
+ // MaxTries, if non-zero, specifies the number of times we will retry on
+ // failure. Retries are only attempted for temporary network errors or known
+ // safe failures.
+ MaxTries int
+ Deadline DeadlineFunc
+ ShouldRetry RetryableFunc
+ Wait WaitFunc
+ transport *http.Transport
+}
+
+// Convenience method for creating an http client
+func NewClient(rt *ResilientTransport) *http.Client {
+ rt.transport = &http.Transport{
+ Dial: func(netw, addr string) (net.Conn, error) {
+ c, err := net.DialTimeout(netw, addr, rt.DialTimeout)
+ if err != nil {
+ return nil, err
+ }
+ c.SetDeadline(rt.Deadline())
+ return c, nil
+ },
+ Proxy: http.ProxyFromEnvironment,
+ }
+ // TODO: Would be nice is ResilientTransport allowed clients to initialize
+ // with http.Transport attributes.
+ return &http.Client{
+ Transport: rt,
+ }
+}
+
+var retryingTransport = &ResilientTransport{
+ Deadline: func() time.Time {
+ return time.Now().Add(5 * time.Second)
+ },
+ DialTimeout: 10 * time.Second,
+ MaxTries: 3,
+ ShouldRetry: awsRetry,
+ Wait: ExpBackoff,
+}
+
+// Exported default client
+var RetryingClient = NewClient(retryingTransport)
+
+func (t *ResilientTransport) RoundTrip(req *http.Request) (*http.Response, error) {
+ return t.tries(req)
+}
+
+// Retry a request a maximum of t.MaxTries times.
+// We'll only retry if the proper criteria are met.
+// If a wait function is specified, wait that amount of time
+// In between requests.
+func (t *ResilientTransport) tries(req *http.Request) (res *http.Response, err error) {
+ for try := 0; try < t.MaxTries; try += 1 {
+ res, err = t.transport.RoundTrip(req)
+
+ if !t.ShouldRetry(req, res, err) {
+ break
+ }
+ if res != nil {
+ res.Body.Close()
+ }
+ if t.Wait != nil {
+ t.Wait(try)
+ }
+ }
+
+ return
+}
+
+func ExpBackoff(try int) {
+ time.Sleep(100 * time.Millisecond *
+ time.Duration(math.Exp2(float64(try))))
+}
+
+func LinearBackoff(try int) {
+ time.Sleep(time.Duration(try*100) * time.Millisecond)
+}
+
+// Decide if we should retry a request.
+// In general, the criteria for retrying a request is described here
+// http://docs.aws.amazon.com/general/latest/gr/api-retries.html
+func awsRetry(req *http.Request, res *http.Response, err error) bool {
+ retry := false
+
+ // Retry if there's a temporary network error.
+ if neterr, ok := err.(net.Error); ok {
+ if neterr.Temporary() {
+ retry = true
+ }
+ }
+
+ // Retry if we get a 5xx series error.
+ if res != nil {
+ if res.StatusCode >= 500 && res.StatusCode < 600 {
+ retry = true
+ }
+ }
+
+ return retry
+}
diff --git a/vendor/github.com/goamz/goamz/aws/regions.go b/vendor/github.com/goamz/goamz/aws/regions.go
new file mode 100644
index 000000000..5e18f023d
--- /dev/null
+++ b/vendor/github.com/goamz/goamz/aws/regions.go
@@ -0,0 +1,254 @@
+package aws
+
+var USGovWest = Region{
+ "us-gov-west-1",
+ "https://ec2.us-gov-west-1.amazonaws.com",
+ "https://s3-fips-us-gov-west-1.amazonaws.com",
+ "",
+ true,
+ true,
+ "",
+ "",
+ "https://sns.us-gov-west-1.amazonaws.com",
+ "https://sqs.us-gov-west-1.amazonaws.com",
+ "https://iam.us-gov.amazonaws.com",
+ "https://elasticloadbalancing.us-gov-west-1.amazonaws.com",
+ "https://dynamodb.us-gov-west-1.amazonaws.com",
+ ServiceInfo{"https://monitoring.us-gov-west-1.amazonaws.com", V2Signature},
+ "https://autoscaling.us-gov-west-1.amazonaws.com",
+ ServiceInfo{"https://rds.us-gov-west-1.amazonaws.com", V2Signature},
+ "https://sts.amazonaws.com",
+ "https://cloudformation.us-gov-west-1.amazonaws.com",
+ "https://ecs.us-gov-west-1.amazonaws.com",
+ "https://streams.dynamodb.us-gov-west-1.amazonaws.com",
+}
+
+var USEast = Region{
+ "us-east-1",
+ "https://ec2.us-east-1.amazonaws.com",
+ "https://s3.amazonaws.com",
+ "",
+ false,
+ false,
+ "https://sdb.amazonaws.com",
+ "https://email.us-east-1.amazonaws.com",
+ "https://sns.us-east-1.amazonaws.com",
+ "https://sqs.us-east-1.amazonaws.com",
+ "https://iam.amazonaws.com",
+ "https://elasticloadbalancing.us-east-1.amazonaws.com",
+ "https://dynamodb.us-east-1.amazonaws.com",
+ ServiceInfo{"https://monitoring.us-east-1.amazonaws.com", V2Signature},
+ "https://autoscaling.us-east-1.amazonaws.com",
+ ServiceInfo{"https://rds.us-east-1.amazonaws.com", V2Signature},
+ "https://sts.amazonaws.com",
+ "https://cloudformation.us-east-1.amazonaws.com",
+ "https://ecs.us-east-1.amazonaws.com",
+ "https://streams.dynamodb.us-east-1.amazonaws.com",
+}
+
+var USWest = Region{
+ "us-west-1",
+ "https://ec2.us-west-1.amazonaws.com",
+ "https://s3-us-west-1.amazonaws.com",
+ "",
+ true,
+ true,
+ "https://sdb.us-west-1.amazonaws.com",
+ "",
+ "https://sns.us-west-1.amazonaws.com",
+ "https://sqs.us-west-1.amazonaws.com",
+ "https://iam.amazonaws.com",
+ "https://elasticloadbalancing.us-west-1.amazonaws.com",
+ "https://dynamodb.us-west-1.amazonaws.com",
+ ServiceInfo{"https://monitoring.us-west-1.amazonaws.com", V2Signature},
+ "https://autoscaling.us-west-1.amazonaws.com",
+ ServiceInfo{"https://rds.us-west-1.amazonaws.com", V2Signature},
+ "https://sts.amazonaws.com",
+ "https://cloudformation.us-west-1.amazonaws.com",
+ "https://ecs.us-west-1.amazonaws.com",
+ "https://streams.dynamodb.us-west-1.amazonaws.com",
+}
+
+var USWest2 = Region{
+ "us-west-2",
+ "https://ec2.us-west-2.amazonaws.com",
+ "https://s3-us-west-2.amazonaws.com",
+ "",
+ true,
+ true,
+ "https://sdb.us-west-2.amazonaws.com",
+ "https://email.us-west-2.amazonaws.com",
+ "https://sns.us-west-2.amazonaws.com",
+ "https://sqs.us-west-2.amazonaws.com",
+ "https://iam.amazonaws.com",
+ "https://elasticloadbalancing.us-west-2.amazonaws.com",
+ "https://dynamodb.us-west-2.amazonaws.com",
+ ServiceInfo{"https://monitoring.us-west-2.amazonaws.com", V2Signature},
+ "https://autoscaling.us-west-2.amazonaws.com",
+ ServiceInfo{"https://rds.us-west-2.amazonaws.com", V2Signature},
+ "https://sts.amazonaws.com",
+ "https://cloudformation.us-west-2.amazonaws.com",
+ "https://ecs.us-west-2.amazonaws.com",
+ "https://streams.dynamodb.us-west-2.amazonaws.com",
+}
+
+var EUWest = Region{
+ "eu-west-1",
+ "https://ec2.eu-west-1.amazonaws.com",
+ "https://s3-eu-west-1.amazonaws.com",
+ "",
+ true,
+ true,
+ "https://sdb.eu-west-1.amazonaws.com",
+ "https://email.eu-west-1.amazonaws.com",
+ "https://sns.eu-west-1.amazonaws.com",
+ "https://sqs.eu-west-1.amazonaws.com",
+ "https://iam.amazonaws.com",
+ "https://elasticloadbalancing.eu-west-1.amazonaws.com",
+ "https://dynamodb.eu-west-1.amazonaws.com",
+ ServiceInfo{"https://monitoring.eu-west-1.amazonaws.com", V2Signature},
+ "https://autoscaling.eu-west-1.amazonaws.com",
+ ServiceInfo{"https://rds.eu-west-1.amazonaws.com", V2Signature},
+ "https://sts.amazonaws.com",
+ "https://cloudformation.eu-west-1.amazonaws.com",
+ "https://ecs.eu-west-1.amazonaws.com",
+ "https://streams.dynamodb.eu-west-1.amazonaws.com",
+}
+
+var EUCentral = Region{
+ "eu-central-1",
+ "https://ec2.eu-central-1.amazonaws.com",
+ "https://s3-eu-central-1.amazonaws.com",
+ "",
+ true,
+ true,
+ "https://sdb.eu-central-1.amazonaws.com",
+ "https://email.eu-central-1.amazonaws.com",
+ "https://sns.eu-central-1.amazonaws.com",
+ "https://sqs.eu-central-1.amazonaws.com",
+ "https://iam.amazonaws.com",
+ "https://elasticloadbalancing.eu-central-1.amazonaws.com",
+ "https://dynamodb.eu-central-1.amazonaws.com",
+ ServiceInfo{"https://monitoring.eu-central-1.amazonaws.com", V2Signature},
+ "https://autoscaling.eu-central-1.amazonaws.com",
+ ServiceInfo{"https://rds.eu-central-1.amazonaws.com", V2Signature},
+ "https://sts.amazonaws.com",
+ "https://cloudformation.eu-central-1.amazonaws.com",
+ "https://ecs.eu-central-1.amazonaws.com",
+ "https://streams.dynamodb.eu-central-1.amazonaws.com",
+}
+
+var APSoutheast = Region{
+ "ap-southeast-1",
+ "https://ec2.ap-southeast-1.amazonaws.com",
+ "https://s3-ap-southeast-1.amazonaws.com",
+ "",
+ true,
+ true,
+ "https://sdb.ap-southeast-1.amazonaws.com",
+ "",
+ "https://sns.ap-southeast-1.amazonaws.com",
+ "https://sqs.ap-southeast-1.amazonaws.com",
+ "https://iam.amazonaws.com",
+ "https://elasticloadbalancing.ap-southeast-1.amazonaws.com",
+ "https://dynamodb.ap-southeast-1.amazonaws.com",
+ ServiceInfo{"https://monitoring.ap-southeast-1.amazonaws.com", V2Signature},
+ "https://autoscaling.ap-southeast-1.amazonaws.com",
+ ServiceInfo{"https://rds.ap-southeast-1.amazonaws.com", V2Signature},
+ "https://sts.amazonaws.com",
+ "https://cloudformation.ap-southeast-1.amazonaws.com",
+ "https://ecs.ap-southeast-1.amazonaws.com",
+ "https://streams.dynamodb.ap-southeast-1.amazonaws.com",
+}
+
+var APSoutheast2 = Region{
+ "ap-southeast-2",
+ "https://ec2.ap-southeast-2.amazonaws.com",
+ "https://s3-ap-southeast-2.amazonaws.com",
+ "",
+ true,
+ true,
+ "https://sdb.ap-southeast-2.amazonaws.com",
+ "",
+ "https://sns.ap-southeast-2.amazonaws.com",
+ "https://sqs.ap-southeast-2.amazonaws.com",
+ "https://iam.amazonaws.com",
+ "https://elasticloadbalancing.ap-southeast-2.amazonaws.com",
+ "https://dynamodb.ap-southeast-2.amazonaws.com",
+ ServiceInfo{"https://monitoring.ap-southeast-2.amazonaws.com", V2Signature},
+ "https://autoscaling.ap-southeast-2.amazonaws.com",
+ ServiceInfo{"https://rds.ap-southeast-2.amazonaws.com", V2Signature},
+ "https://sts.amazonaws.com",
+ "https://cloudformation.ap-southeast-2.amazonaws.com",
+ "https://ecs.ap-southeast-2.amazonaws.com",
+ "https://streams.dynamodb.ap-southeast-2.amazonaws.com",
+}
+
+var APNortheast = Region{
+ "ap-northeast-1",
+ "https://ec2.ap-northeast-1.amazonaws.com",
+ "https://s3-ap-northeast-1.amazonaws.com",
+ "",
+ true,
+ true,
+ "https://sdb.ap-northeast-1.amazonaws.com",
+ "",
+ "https://sns.ap-northeast-1.amazonaws.com",
+ "https://sqs.ap-northeast-1.amazonaws.com",
+ "https://iam.amazonaws.com",
+ "https://elasticloadbalancing.ap-northeast-1.amazonaws.com",
+ "https://dynamodb.ap-northeast-1.amazonaws.com",
+ ServiceInfo{"https://monitoring.ap-northeast-1.amazonaws.com", V2Signature},
+ "https://autoscaling.ap-northeast-1.amazonaws.com",
+ ServiceInfo{"https://rds.ap-northeast-1.amazonaws.com", V2Signature},
+ "https://sts.amazonaws.com",
+ "https://cloudformation.ap-northeast-1.amazonaws.com",
+ "https://ecs.ap-northeast-1.amazonaws.com",
+ "https://streams.dynamodb.ap-northeast-1.amazonaws.com",
+}
+
+var SAEast = Region{
+ "sa-east-1",
+ "https://ec2.sa-east-1.amazonaws.com",
+ "https://s3-sa-east-1.amazonaws.com",
+ "",
+ true,
+ true,
+ "https://sdb.sa-east-1.amazonaws.com",
+ "",
+ "https://sns.sa-east-1.amazonaws.com",
+ "https://sqs.sa-east-1.amazonaws.com",
+ "https://iam.amazonaws.com",
+ "https://elasticloadbalancing.sa-east-1.amazonaws.com",
+ "https://dynamodb.sa-east-1.amazonaws.com",
+ ServiceInfo{"https://monitoring.sa-east-1.amazonaws.com", V2Signature},
+ "https://autoscaling.sa-east-1.amazonaws.com",
+ ServiceInfo{"https://rds.sa-east-1.amazonaws.com", V2Signature},
+ "https://sts.amazonaws.com",
+ "https://cloudformation.sa-east-1.amazonaws.com",
+ "https://ecs.sa-east-1.amazonaws.com",
+ "https://streams.dynamodb.sa-east-1.amazonaws.com",
+}
+
+var CNNorth = Region{
+ "cn-north-1",
+ "https://ec2.cn-north-1.amazonaws.com.cn",
+ "https://s3.cn-north-1.amazonaws.com.cn",
+ "",
+ true,
+ true,
+ "https://sdb.cn-north-1.amazonaws.com.cn",
+ "",
+ "https://sns.cn-north-1.amazonaws.com.cn",
+ "https://sqs.cn-north-1.amazonaws.com.cn",
+ "https://iam.cn-north-1.amazonaws.com.cn",
+ "https://elasticloadbalancing.cn-north-1.amazonaws.com.cn",
+ "https://dynamodb.cn-north-1.amazonaws.com.cn",
+ ServiceInfo{"https://monitoring.cn-north-1.amazonaws.com.cn", V4Signature},
+ "https://autoscaling.cn-north-1.amazonaws.com.cn",
+ ServiceInfo{"https://rds.cn-north-1.amazonaws.com.cn", V4Signature},
+ "https://sts.cn-north-1.amazonaws.com.cn",
+ "https://cloudformation.cn-north-1.amazonaws.com.cn",
+ "https://ecs.cn-north-1.amazonaws.com.cn",
+ "https://streams.dynamodb.cn-north-1.amazonaws.com.cn",
+}
diff --git a/vendor/github.com/goamz/goamz/aws/sign.go b/vendor/github.com/goamz/goamz/aws/sign.go
new file mode 100644
index 000000000..22ce0781b
--- /dev/null
+++ b/vendor/github.com/goamz/goamz/aws/sign.go
@@ -0,0 +1,357 @@
+package aws
+
+import (
+ "bytes"
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/base64"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "path"
+ "sort"
+ "strings"
+ "time"
+)
+
+type V2Signer struct {
+ auth Auth
+ service ServiceInfo
+ host string
+}
+
+var b64 = base64.StdEncoding
+
+func NewV2Signer(auth Auth, service ServiceInfo) (*V2Signer, error) {
+ u, err := url.Parse(service.Endpoint)
+ if err != nil {
+ return nil, err
+ }
+ return &V2Signer{auth: auth, service: service, host: u.Host}, nil
+}
+
+func (s *V2Signer) Sign(method, path string, params map[string]string) {
+ params["AWSAccessKeyId"] = s.auth.AccessKey
+ params["SignatureVersion"] = "2"
+ params["SignatureMethod"] = "HmacSHA256"
+ if s.auth.Token() != "" {
+ params["SecurityToken"] = s.auth.Token()
+ }
+
+ // AWS specifies that the parameters in a signed request must
+ // be provided in the natural order of the keys. This is distinct
+ // from the natural order of the encoded value of key=value.
+ // Percent and Equals affect the sorting order.
+ var keys, sarray []string
+ for k, _ := range params {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ sarray = append(sarray, Encode(k)+"="+Encode(params[k]))
+ }
+ joined := strings.Join(sarray, "&")
+ payload := method + "\n" + s.host + "\n" + path + "\n" + joined
+ hash := hmac.New(sha256.New, []byte(s.auth.SecretKey))
+ hash.Write([]byte(payload))
+ signature := make([]byte, b64.EncodedLen(hash.Size()))
+ b64.Encode(signature, hash.Sum(nil))
+
+ params["Signature"] = string(signature)
+}
+
+// Common date formats for signing requests
+const (
+ ISO8601BasicFormat = "20060102T150405Z"
+ ISO8601BasicFormatShort = "20060102"
+)
+
+type Route53Signer struct {
+ auth Auth
+}
+
+func NewRoute53Signer(auth Auth) *Route53Signer {
+ return &Route53Signer{auth: auth}
+}
+
+// getCurrentDate fetches the date stamp from the aws servers to
+// ensure the auth headers are within 5 minutes of the server time
+func (s *Route53Signer) getCurrentDate() string {
+ response, err := http.Get("https://route53.amazonaws.com/date")
+ if err != nil {
+ fmt.Print("Unable to get date from amazon: ", err)
+ return ""
+ }
+
+ response.Body.Close()
+ return response.Header.Get("Date")
+}
+
+// Creates the authorize signature based on the date stamp and secret key
+func (s *Route53Signer) getHeaderAuthorize(message string) string {
+ hmacSha256 := hmac.New(sha256.New, []byte(s.auth.SecretKey))
+ hmacSha256.Write([]byte(message))
+ cryptedString := hmacSha256.Sum(nil)
+
+ return base64.StdEncoding.EncodeToString(cryptedString)
+}
+
+// Adds all the required headers for AWS Route53 API to the request
+// including the authorization
+func (s *Route53Signer) Sign(req *http.Request) {
+ date := s.getCurrentDate()
+ authHeader := fmt.Sprintf("AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=%s,Signature=%s",
+ s.auth.AccessKey, "HmacSHA256", s.getHeaderAuthorize(date))
+
+ req.Header.Set("Host", req.Host)
+ req.Header.Set("X-Amzn-Authorization", authHeader)
+ req.Header.Set("X-Amz-Date", date)
+ req.Header.Set("Content-Type", "application/xml")
+}
+
+/*
+The V4Signer encapsulates all of the functionality to sign a request with the AWS
+Signature Version 4 Signing Process. (http://goo.gl/u1OWZz)
+*/
+type V4Signer struct {
+ auth Auth
+ serviceName string
+ region Region
+}
+
+/*
+Return a new instance of a V4Signer capable of signing AWS requests.
+*/
+func NewV4Signer(auth Auth, serviceName string, region Region) *V4Signer {
+ return &V4Signer{auth: auth, serviceName: serviceName, region: region}
+}
+
+/*
+Sign a request according to the AWS Signature Version 4 Signing Process. (http://goo.gl/u1OWZz)
+
+The signed request will include an "x-amz-date" header with a current timestamp if a valid "x-amz-date"
+or "date" header was not available in the original request. In addition, AWS Signature Version 4 requires
+the "host" header to be a signed header, therefor the Sign method will manually set a "host" header from
+the request.Host.
+
+The signed request will include a new "Authorization" header indicating that the request has been signed.
+
+Any changes to the request after signing the request will invalidate the signature.
+*/
+func (s *V4Signer) Sign(req *http.Request) {
+ req.Header.Set("host", req.Host) // host header must be included as a signed header
+ t := s.requestTime(req) // Get requst time
+ creq := s.canonicalRequest(req) // Build canonical request
+ sts := s.stringToSign(t, creq) // Build string to sign
+ signature := s.signature(t, sts) // Calculate the AWS Signature Version 4
+ auth := s.authorization(req.Header, t, signature) // Create Authorization header value
+ req.Header.Set("Authorization", auth) // Add Authorization header to request
+ return
+}
+
+/*
+requestTime method will parse the time from the request "x-amz-date" or "date" headers.
+If the "x-amz-date" header is present, that will take priority over the "date" header.
+If neither header is defined or we are unable to parse either header as a valid date
+then we will create a new "x-amz-date" header with the current time.
+*/
+func (s *V4Signer) requestTime(req *http.Request) time.Time {
+
+ // Get "x-amz-date" header
+ date := req.Header.Get("x-amz-date")
+
+ // Attempt to parse as ISO8601BasicFormat
+ t, err := time.Parse(ISO8601BasicFormat, date)
+ if err == nil {
+ return t
+ }
+
+ // Attempt to parse as http.TimeFormat
+ t, err = time.Parse(http.TimeFormat, date)
+ if err == nil {
+ req.Header.Set("x-amz-date", t.Format(ISO8601BasicFormat))
+ return t
+ }
+
+ // Get "date" header
+ date = req.Header.Get("date")
+
+ // Attempt to parse as http.TimeFormat
+ t, err = time.Parse(http.TimeFormat, date)
+ if err == nil {
+ return t
+ }
+
+ // Create a current time header to be used
+ t = time.Now().UTC()
+ req.Header.Set("x-amz-date", t.Format(ISO8601BasicFormat))
+ return t
+}
+
+/*
+canonicalRequest method creates the canonical request according to Task 1 of the AWS Signature Version 4 Signing Process. (http://goo.gl/eUUZ3S)
+
+ CanonicalRequest =
+ HTTPRequestMethod + '\n' +
+ CanonicalURI + '\n' +
+ CanonicalQueryString + '\n' +
+ CanonicalHeaders + '\n' +
+ SignedHeaders + '\n' +
+ HexEncode(Hash(Payload))
+*/
+func (s *V4Signer) canonicalRequest(req *http.Request) string {
+ c := new(bytes.Buffer)
+ fmt.Fprintf(c, "%s\n", req.Method)
+ fmt.Fprintf(c, "%s\n", s.canonicalURI(req.URL))
+ fmt.Fprintf(c, "%s\n", s.canonicalQueryString(req.URL))
+ fmt.Fprintf(c, "%s\n\n", s.canonicalHeaders(req.Header))
+ fmt.Fprintf(c, "%s\n", s.signedHeaders(req.Header))
+ fmt.Fprintf(c, "%s", s.payloadHash(req))
+ return c.String()
+}
+
+func (s *V4Signer) canonicalURI(u *url.URL) string {
+ canonicalPath := u.RequestURI()
+ if u.RawQuery != "" {
+ canonicalPath = canonicalPath[:len(canonicalPath)-len(u.RawQuery)-1]
+ }
+ slash := strings.HasSuffix(canonicalPath, "/")
+ canonicalPath = path.Clean(canonicalPath)
+ if canonicalPath != "/" && slash {
+ canonicalPath += "/"
+ }
+ return canonicalPath
+}
+
+func (s *V4Signer) canonicalQueryString(u *url.URL) string {
+ var a []string
+ for k, vs := range u.Query() {
+ k = Encode(k)
+ for _, v := range vs {
+ if v == "" {
+ a = append(a, k+"=")
+ } else {
+ v = Encode(v)
+ a = append(a, k+"="+v)
+ }
+ }
+ }
+ sort.Strings(a)
+ return strings.Join(a, "&")
+}
+
+func (s *V4Signer) canonicalHeaders(h http.Header) string {
+ i, a := 0, make([]string, len(h))
+ for k, v := range h {
+ for j, w := range v {
+ v[j] = strings.Trim(w, " ")
+ }
+ sort.Strings(v)
+ a[i] = strings.ToLower(k) + ":" + strings.Join(v, ",")
+ i++
+ }
+ sort.Strings(a)
+ return strings.Join(a, "\n")
+}
+
+func (s *V4Signer) signedHeaders(h http.Header) string {
+ i, a := 0, make([]string, len(h))
+ for k, _ := range h {
+ a[i] = strings.ToLower(k)
+ i++
+ }
+ sort.Strings(a)
+ return strings.Join(a, ";")
+}
+
+func (s *V4Signer) payloadHash(req *http.Request) string {
+ var b []byte
+ if req.Body == nil {
+ b = []byte("")
+ } else {
+ var err error
+ b, err = ioutil.ReadAll(req.Body)
+ if err != nil {
+ // TODO: I REALLY DON'T LIKE THIS PANIC!!!!
+ panic(err)
+ }
+ }
+ req.Body = ioutil.NopCloser(bytes.NewBuffer(b))
+ return s.hash(string(b))
+}
+
+/*
+stringToSign method creates the string to sign accorting to Task 2 of the AWS Signature Version 4 Signing Process. (http://goo.gl/es1PAu)
+
+ StringToSign =
+ Algorithm + '\n' +
+ RequestDate + '\n' +
+ CredentialScope + '\n' +
+ HexEncode(Hash(CanonicalRequest))
+*/
+func (s *V4Signer) stringToSign(t time.Time, creq string) string {
+ w := new(bytes.Buffer)
+ fmt.Fprint(w, "AWS4-HMAC-SHA256\n")
+ fmt.Fprintf(w, "%s\n", t.Format(ISO8601BasicFormat))
+ fmt.Fprintf(w, "%s\n", s.credentialScope(t))
+ fmt.Fprintf(w, "%s", s.hash(creq))
+ return w.String()
+}
+
+func (s *V4Signer) credentialScope(t time.Time) string {
+ return fmt.Sprintf("%s/%s/%s/aws4_request", t.Format(ISO8601BasicFormatShort), s.region.Name, s.serviceName)
+}
+
+/*
+signature method calculates the AWS Signature Version 4 according to Task 3 of the AWS Signature Version 4 Signing Process. (http://goo.gl/j0Yqe1)
+
+ signature = HexEncode(HMAC(derived-signing-key, string-to-sign))
+*/
+func (s *V4Signer) signature(t time.Time, sts string) string {
+ h := s.hmac(s.derivedKey(t), []byte(sts))
+ return fmt.Sprintf("%x", h)
+}
+
+/*
+derivedKey method derives a signing key to be used for signing a request.
+
+ kSecret = Your AWS Secret Access Key
+ kDate = HMAC("AWS4" + kSecret, Date)
+ kRegion = HMAC(kDate, Region)
+ kService = HMAC(kRegion, Service)
+ kSigning = HMAC(kService, "aws4_request")
+*/
+func (s *V4Signer) derivedKey(t time.Time) []byte {
+ h := s.hmac([]byte("AWS4"+s.auth.SecretKey), []byte(t.Format(ISO8601BasicFormatShort)))
+ h = s.hmac(h, []byte(s.region.Name))
+ h = s.hmac(h, []byte(s.serviceName))
+ h = s.hmac(h, []byte("aws4_request"))
+ return h
+}
+
+/*
+authorization method generates the authorization header value.
+*/
+func (s *V4Signer) authorization(header http.Header, t time.Time, signature string) string {
+ w := new(bytes.Buffer)
+ fmt.Fprint(w, "AWS4-HMAC-SHA256 ")
+ fmt.Fprintf(w, "Credential=%s/%s, ", s.auth.AccessKey, s.credentialScope(t))
+ fmt.Fprintf(w, "SignedHeaders=%s, ", s.signedHeaders(header))
+ fmt.Fprintf(w, "Signature=%s", signature)
+ return w.String()
+}
+
+// hash method calculates the sha256 hash for a given string
+func (s *V4Signer) hash(in string) string {
+ h := sha256.New()
+ fmt.Fprintf(h, "%s", in)
+ return fmt.Sprintf("%x", h.Sum(nil))
+}
+
+// hmac method calculates the sha256 hmac for a given slice of bytes
+func (s *V4Signer) hmac(key, data []byte) []byte {
+ h := hmac.New(sha256.New, key)
+ h.Write(data)
+ return h.Sum(nil)
+}
diff --git a/vendor/github.com/goamz/goamz/s3/multi.go b/vendor/github.com/goamz/goamz/s3/multi.go
new file mode 100644
index 000000000..348ead300
--- /dev/null
+++ b/vendor/github.com/goamz/goamz/s3/multi.go
@@ -0,0 +1,439 @@
+package s3
+
+import (
+ "bytes"
+ "crypto/md5"
+ "encoding/base64"
+ "encoding/hex"
+ "encoding/xml"
+ "errors"
+ "io"
+ "sort"
+ "strconv"
+)
+
+// Multi represents an unfinished multipart upload.
+//
+// Multipart uploads allow sending big objects in smaller chunks.
+// After all parts have been sent, the upload must be explicitly
+// completed by calling Complete with the list of parts.
+//
+// See http://goo.gl/vJfTG for an overview of multipart uploads.
+type Multi struct {
+ Bucket *Bucket
+ Key string
+ UploadId string
+}
+
+// That's the default. Here just for testing.
+var listMultiMax = 1000
+
+type listMultiResp struct {
+ NextKeyMarker string
+ NextUploadIdMarker string
+ IsTruncated bool
+ Upload []Multi
+ CommonPrefixes []string `xml:"CommonPrefixes>Prefix"`
+}
+
+// ListMulti returns the list of unfinished multipart uploads in b.
+//
+// The prefix parameter limits the response to keys that begin with the
+// specified prefix. You can use prefixes to separate a bucket into different
+// groupings of keys (to get the feeling of folders, for example).
+//
+// The delim parameter causes the response to group all of the keys that
+// share a common prefix up to the next delimiter in a single entry within
+// the CommonPrefixes field. You can use delimiters to separate a bucket
+// into different groupings of keys, similar to how folders would work.
+//
+// See http://goo.gl/ePioY for details.
+func (b *Bucket) ListMulti(prefix, delim string) (multis []*Multi, prefixes []string, err error) {
+ params := map[string][]string{
+ "uploads": {""},
+ "max-uploads": {strconv.FormatInt(int64(listMultiMax), 10)},
+ "prefix": {prefix},
+ "delimiter": {delim},
+ }
+ for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
+ req := &request{
+ method: "GET",
+ bucket: b.Name,
+ params: params,
+ }
+ var resp listMultiResp
+ err := b.S3.query(req, &resp)
+ if shouldRetry(err) && attempt.HasNext() {
+ continue
+ }
+ if err != nil {
+ return nil, nil, err
+ }
+ for i := range resp.Upload {
+ multi := &resp.Upload[i]
+ multi.Bucket = b
+ multis = append(multis, multi)
+ }
+ prefixes = append(prefixes, resp.CommonPrefixes...)
+ if !resp.IsTruncated {
+ return multis, prefixes, nil
+ }
+ params["key-marker"] = []string{resp.NextKeyMarker}
+ params["upload-id-marker"] = []string{resp.NextUploadIdMarker}
+ attempt = b.S3.AttemptStrategy.Start() // Last request worked.
+ }
+ panic("unreachable")
+}
+
+// Multi returns a multipart upload handler for the provided key
+// inside b. If a multipart upload exists for key, it is returned,
+// otherwise a new multipart upload is initiated with contType and perm.
+func (b *Bucket) Multi(key, contType string, perm ACL) (*Multi, error) {
+ multis, _, err := b.ListMulti(key, "")
+ if err != nil && !hasCode(err, "NoSuchUpload") {
+ return nil, err
+ }
+ for _, m := range multis {
+ if m.Key == key {
+ return m, nil
+ }
+ }
+ return b.InitMulti(key, contType, perm)
+}
+
+// InitMulti initializes a new multipart upload at the provided
+// key inside b and returns a value for manipulating it.
+//
+// See http://goo.gl/XP8kL for details.
+func (b *Bucket) InitMulti(key string, contType string, perm ACL) (*Multi, error) {
+ headers := map[string][]string{
+ "Content-Type": {contType},
+ "Content-Length": {"0"},
+ "x-amz-acl": {string(perm)},
+ }
+ params := map[string][]string{
+ "uploads": {""},
+ }
+ req := &request{
+ method: "POST",
+ bucket: b.Name,
+ path: key,
+ headers: headers,
+ params: params,
+ }
+ var err error
+ var resp struct {
+ UploadId string `xml:"UploadId"`
+ }
+ for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
+ err = b.S3.query(req, &resp)
+ if !shouldRetry(err) {
+ break
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
+ return &Multi{Bucket: b, Key: key, UploadId: resp.UploadId}, nil
+}
+
+// PutPart sends part n of the multipart upload, reading all the content from r.
+// Each part, except for the last one, must be at least 5MB in size.
+//
+// See http://goo.gl/pqZer for details.
+func (m *Multi) PutPart(n int, r io.ReadSeeker) (Part, error) {
+ partSize, _, md5b64, err := seekerInfo(r)
+ if err != nil {
+ return Part{}, err
+ }
+ return m.putPart(n, r, partSize, md5b64)
+}
+
+func (m *Multi) putPart(n int, r io.ReadSeeker, partSize int64, md5b64 string) (Part, error) {
+ headers := map[string][]string{
+ "Content-Length": {strconv.FormatInt(partSize, 10)},
+ "Content-MD5": {md5b64},
+ }
+ params := map[string][]string{
+ "uploadId": {m.UploadId},
+ "partNumber": {strconv.FormatInt(int64(n), 10)},
+ }
+ for attempt := m.Bucket.S3.AttemptStrategy.Start(); attempt.Next(); {
+ _, err := r.Seek(0, 0)
+ if err != nil {
+ return Part{}, err
+ }
+ req := &request{
+ method: "PUT",
+ bucket: m.Bucket.Name,
+ path: m.Key,
+ headers: headers,
+ params: params,
+ payload: r,
+ }
+ err = m.Bucket.S3.prepare(req)
+ if err != nil {
+ return Part{}, err
+ }
+ resp, err := m.Bucket.S3.run(req, nil)
+ if shouldRetry(err) && attempt.HasNext() {
+ continue
+ }
+ if err != nil {
+ return Part{}, err
+ }
+ etag := resp.Header.Get("ETag")
+ if etag == "" {
+ return Part{}, errors.New("part upload succeeded with no ETag")
+ }
+ return Part{n, etag, partSize}, nil
+ }
+ panic("unreachable")
+}
+
+func seekerInfo(r io.ReadSeeker) (size int64, md5hex string, md5b64 string, err error) {
+ _, err = r.Seek(0, 0)
+ if err != nil {
+ return 0, "", "", err
+ }
+ digest := md5.New()
+ size, err = io.Copy(digest, r)
+ if err != nil {
+ return 0, "", "", err
+ }
+ sum := digest.Sum(nil)
+ md5hex = hex.EncodeToString(sum)
+ md5b64 = base64.StdEncoding.EncodeToString(sum)
+ return size, md5hex, md5b64, nil
+}
+
+type Part struct {
+ N int `xml:"PartNumber"`
+ ETag string
+ Size int64
+}
+
+type partSlice []Part
+
+func (s partSlice) Len() int { return len(s) }
+func (s partSlice) Less(i, j int) bool { return s[i].N < s[j].N }
+func (s partSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+type listPartsResp struct {
+ NextPartNumberMarker string
+ IsTruncated bool
+ Part []Part
+}
+
+// That's the default. Here just for testing.
+var listPartsMax = 1000
+
+// ListParts returns the list of previously uploaded parts in m,
+// ordered by part number.
+//
+// See http://goo.gl/ePioY for details.
+func (m *Multi) ListParts() ([]Part, error) {
+ params := map[string][]string{
+ "uploadId": {m.UploadId},
+ "max-parts": {strconv.FormatInt(int64(listPartsMax), 10)},
+ }
+ var parts partSlice
+ for attempt := m.Bucket.S3.AttemptStrategy.Start(); attempt.Next(); {
+ req := &request{
+ method: "GET",
+ bucket: m.Bucket.Name,
+ path: m.Key,
+ params: params,
+ }
+ var resp listPartsResp
+ err := m.Bucket.S3.query(req, &resp)
+ if shouldRetry(err) && attempt.HasNext() {
+ continue
+ }
+ if err != nil {
+ return nil, err
+ }
+ parts = append(parts, resp.Part...)
+ if !resp.IsTruncated {
+ sort.Sort(parts)
+ return parts, nil
+ }
+ params["part-number-marker"] = []string{resp.NextPartNumberMarker}
+ attempt = m.Bucket.S3.AttemptStrategy.Start() // Last request worked.
+ }
+ panic("unreachable")
+}
+
+type ReaderAtSeeker interface {
+ io.ReaderAt
+ io.ReadSeeker
+}
+
+// PutAll sends all of r via a multipart upload with parts no larger
+// than partSize bytes, which must be set to at least 5MB.
+// Parts previously uploaded are either reused if their checksum
+// and size match the new part, or otherwise overwritten with the
+// new content.
+// PutAll returns all the parts of m (reused or not).
+func (m *Multi) PutAll(r ReaderAtSeeker, partSize int64) ([]Part, error) {
+ old, err := m.ListParts()
+ if err != nil && !hasCode(err, "NoSuchUpload") {
+ return nil, err
+ }
+ reuse := 0 // Index of next old part to consider reusing.
+ current := 1 // Part number of latest good part handled.
+ totalSize, err := r.Seek(0, 2)
+ if err != nil {
+ return nil, err
+ }
+ first := true // Must send at least one empty part if the file is empty.
+ var result []Part
+NextSection:
+ for offset := int64(0); offset < totalSize || first; offset += partSize {
+ first = false
+ if offset+partSize > totalSize {
+ partSize = totalSize - offset
+ }
+ section := io.NewSectionReader(r, offset, partSize)
+ _, md5hex, md5b64, err := seekerInfo(section)
+ if err != nil {
+ return nil, err
+ }
+ for reuse < len(old) && old[reuse].N <= current {
+ // Looks like this part was already sent.
+ part := &old[reuse]
+ etag := `"` + md5hex + `"`
+ if part.N == current && part.Size == partSize && part.ETag == etag {
+ // Checksum matches. Reuse the old part.
+ result = append(result, *part)
+ current++
+ continue NextSection
+ }
+ reuse++
+ }
+
+ // Part wasn't found or doesn't match. Send it.
+ part, err := m.putPart(current, section, partSize, md5b64)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, part)
+ current++
+ }
+ return result, nil
+}
+
+type completeUpload struct {
+ XMLName xml.Name `xml:"CompleteMultipartUpload"`
+ Parts completeParts `xml:"Part"`
+}
+
+type completePart struct {
+ PartNumber int
+ ETag string
+}
+
+type completeParts []completePart
+
+func (p completeParts) Len() int { return len(p) }
+func (p completeParts) Less(i, j int) bool { return p[i].PartNumber < p[j].PartNumber }
+func (p completeParts) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+
+type completeResponse struct {
+ // The element name: should be either CompleteMultipartUploadResult or Error.
+ XMLName xml.Name
+ // If the element was error, then it should have the following:
+ Code string
+ Message string
+ RequestId string
+ HostId string
+}
+
+// Complete assembles the given previously uploaded parts into the
+// final object. This operation may take several minutes.
+//
+// The complete call to AMZ may still fail after returning HTTP 200,
+// so even though it's unusued, the body of the reply must be demarshalled
+// and checked to see whether or not the complete succeeded.
+//
+// See http://goo.gl/2Z7Tw for details.
+func (m *Multi) Complete(parts []Part) error {
+ params := map[string][]string{
+ "uploadId": {m.UploadId},
+ }
+ c := completeUpload{}
+ for _, p := range parts {
+ c.Parts = append(c.Parts, completePart{p.N, p.ETag})
+ }
+ sort.Sort(c.Parts)
+ data, err := xml.Marshal(&c)
+ if err != nil {
+ return err
+ }
+
+ // Setting Content-Length prevents breakage on DreamObjects
+ for attempt := m.Bucket.S3.AttemptStrategy.Start(); attempt.Next(); {
+ req := &request{
+ method: "POST",
+ bucket: m.Bucket.Name,
+ path: m.Key,
+ params: params,
+ payload: bytes.NewReader(data),
+ headers: map[string][]string{
+ "Content-Length": []string{strconv.Itoa(len(data))},
+ },
+ }
+
+ resp := &completeResponse{}
+ err := m.Bucket.S3.query(req, resp)
+ if shouldRetry(err) && attempt.HasNext() {
+ continue
+ }
+ if err == nil && resp.XMLName.Local == "Error" {
+ err = &Error{
+ StatusCode: 200,
+ Code: resp.Code,
+ Message: resp.Message,
+ RequestId: resp.RequestId,
+ HostId: resp.HostId,
+ }
+ }
+ return err
+ }
+ panic("unreachable")
+}
+
+// Abort deletes an unifinished multipart upload and any previously
+// uploaded parts for it.
+//
+// After a multipart upload is aborted, no additional parts can be
+// uploaded using it. However, if any part uploads are currently in
+// progress, those part uploads might or might not succeed. As a result,
+// it might be necessary to abort a given multipart upload multiple
+// times in order to completely free all storage consumed by all parts.
+//
+// NOTE: If the described scenario happens to you, please report back to
+// the goamz authors with details. In the future such retrying should be
+// handled internally, but it's not clear what happens precisely (Is an
+// error returned? Is the issue completely undetectable?).
+//
+// See http://goo.gl/dnyJw for details.
+func (m *Multi) Abort() error {
+ params := map[string][]string{
+ "uploadId": {m.UploadId},
+ }
+ for attempt := m.Bucket.S3.AttemptStrategy.Start(); attempt.Next(); {
+ req := &request{
+ method: "DELETE",
+ bucket: m.Bucket.Name,
+ path: m.Key,
+ params: params,
+ }
+ err := m.Bucket.S3.query(req, nil)
+ if shouldRetry(err) && attempt.HasNext() {
+ continue
+ }
+ return err
+ }
+ panic("unreachable")
+}
diff --git a/vendor/github.com/goamz/goamz/s3/s3.go b/vendor/github.com/goamz/goamz/s3/s3.go
new file mode 100644
index 000000000..c659aa6ba
--- /dev/null
+++ b/vendor/github.com/goamz/goamz/s3/s3.go
@@ -0,0 +1,1161 @@
+//
+// goamz - Go packages to interact with the Amazon Web Services.
+//
+// https://wiki.ubuntu.com/goamz
+//
+// Copyright (c) 2011 Canonical Ltd.
+//
+// Written by Gustavo Niemeyer <gustavo.niemeyer@canonical.com>
+//
+
+package s3
+
+import (
+ "bytes"
+ "crypto/hmac"
+ "crypto/md5"
+ "crypto/sha1"
+ "encoding/base64"
+ "encoding/xml"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "net"
+ "net/http"
+ "net/http/httputil"
+ "net/url"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/goamz/goamz/aws"
+)
+
+const debug = false
+
+// The S3 type encapsulates operations with an S3 region.
+type S3 struct {
+ aws.Auth
+ aws.Region
+
+ // ConnectTimeout is the maximum time a request attempt will
+ // wait for a successful connection to be made.
+ //
+ // A value of zero means no timeout.
+ ConnectTimeout time.Duration
+
+ // ReadTimeout is the maximum time a request attempt will wait
+ // for an individual read to complete.
+ //
+ // A value of zero means no timeout.
+ ReadTimeout time.Duration
+
+ // WriteTimeout is the maximum time a request attempt will
+ // wait for an individual write to complete.
+ //
+ // A value of zero means no timeout.
+ WriteTimeout time.Duration
+
+ // RequestTimeout is the maximum time a request attempt can
+ // take before operations return a timeout error.
+ //
+ // This includes connection time, any redirects, and reading
+ // the response body. The timer remains running after the request
+ // is made so it can interrupt reading of the response data.
+ //
+ // A Timeout of zero means no timeout.
+ RequestTimeout time.Duration
+
+ // AttemptStrategy is the attempt strategy used for requests.
+ aws.AttemptStrategy
+
+ // Reserve the right of using private data.
+ private byte
+
+ // client used for requests
+ client *http.Client
+}
+
+// The Bucket type encapsulates operations with an S3 bucket.
+type Bucket struct {
+ *S3
+ Name string
+}
+
+// The Owner type represents the owner of the object in an S3 bucket.
+type Owner struct {
+ ID string
+ DisplayName string
+}
+
+// Fold options into an Options struct
+//
+type Options struct {
+ SSE bool
+ Meta map[string][]string
+ ContentEncoding string
+ CacheControl string
+ RedirectLocation string
+ ContentMD5 string
+ // What else?
+ // Content-Disposition string
+ //// The following become headers so they are []strings rather than strings... I think
+ // x-amz-storage-class []string
+}
+
+type CopyOptions struct {
+ Options
+ MetadataDirective string
+ ContentType string
+}
+
+// CopyObjectResult is the output from a Copy request
+type CopyObjectResult struct {
+ ETag string
+ LastModified string
+}
+
+// DefaultAttemptStrategy is the default AttemptStrategy used by S3 objects created by New.
+var DefaultAttemptStrategy = aws.AttemptStrategy{
+ Min: 5,
+ Total: 5 * time.Second,
+ Delay: 200 * time.Millisecond,
+}
+
+// New creates a new S3. Optional client argument allows for custom http.clients to be used.
+func New(auth aws.Auth, region aws.Region, client ...*http.Client) *S3 {
+
+ var httpclient *http.Client
+
+ if len(client) > 0 {
+ httpclient = client[0]
+ }
+
+ return &S3{Auth: auth, Region: region, AttemptStrategy: DefaultAttemptStrategy, client: httpclient}
+}
+
+// Bucket returns a Bucket with the given name.
+func (s3 *S3) Bucket(name string) *Bucket {
+ if s3.Region.S3BucketEndpoint != "" || s3.Region.S3LowercaseBucket {
+ name = strings.ToLower(name)
+ }
+ return &Bucket{s3, name}
+}
+
+var createBucketConfiguration = `<CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
+ <LocationConstraint>%s</LocationConstraint>
+</CreateBucketConfiguration>`
+
+// locationConstraint returns an io.Reader specifying a LocationConstraint if
+// required for the region.
+//
+// See http://goo.gl/bh9Kq for details.
+func (s3 *S3) locationConstraint() io.Reader {
+ constraint := ""
+ if s3.Region.S3LocationConstraint {
+ constraint = fmt.Sprintf(createBucketConfiguration, s3.Region.Name)
+ }
+ return strings.NewReader(constraint)
+}
+
+type ACL string
+
+const (
+ Private = ACL("private")
+ PublicRead = ACL("public-read")
+ PublicReadWrite = ACL("public-read-write")
+ AuthenticatedRead = ACL("authenticated-read")
+ BucketOwnerRead = ACL("bucket-owner-read")
+ BucketOwnerFull = ACL("bucket-owner-full-control")
+)
+
+// PutBucket creates a new bucket.
+//
+// See http://goo.gl/ndjnR for details.
+func (b *Bucket) PutBucket(perm ACL) error {
+ headers := map[string][]string{
+ "x-amz-acl": {string(perm)},
+ }
+ req := &request{
+ method: "PUT",
+ bucket: b.Name,
+ path: "/",
+ headers: headers,
+ payload: b.locationConstraint(),
+ }
+ return b.S3.query(req, nil)
+}
+
+// DelBucket removes an existing S3 bucket. All objects in the bucket must
+// be removed before the bucket itself can be removed.
+//
+// See http://goo.gl/GoBrY for details.
+func (b *Bucket) DelBucket() (err error) {
+ req := &request{
+ method: "DELETE",
+ bucket: b.Name,
+ path: "/",
+ }
+ for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
+ err = b.S3.query(req, nil)
+ if !shouldRetry(err) {
+ break
+ }
+ }
+ return err
+}
+
+// Get retrieves an object from an S3 bucket.
+//
+// See http://goo.gl/isCO7 for details.
+func (b *Bucket) Get(path string) (data []byte, err error) {
+ body, err := b.GetReader(path)
+ defer func() {
+ if body != nil {
+ body.Close()
+ }
+ }()
+ if err != nil {
+ return nil, err
+ }
+ data, err = ioutil.ReadAll(body)
+ return data, err
+}
+
+// GetReader retrieves an object from an S3 bucket,
+// returning the body of the HTTP response.
+// It is the caller's responsibility to call Close on rc when
+// finished reading.
+func (b *Bucket) GetReader(path string) (rc io.ReadCloser, err error) {
+ resp, err := b.GetResponse(path)
+ if resp != nil {
+ return resp.Body, err
+ }
+ return nil, err
+}
+
+// GetResponse retrieves an object from an S3 bucket,
+// returning the HTTP response.
+// It is the caller's responsibility to call Close on rc when
+// finished reading
+func (b *Bucket) GetResponse(path string) (resp *http.Response, err error) {
+ return b.GetResponseWithHeaders(path, make(http.Header))
+}
+
+// GetReaderWithHeaders retrieves an object from an S3 bucket
+// Accepts custom headers to be sent as the second parameter
+// returning the body of the HTTP response.
+// It is the caller's responsibility to call Close on rc when
+// finished reading
+func (b *Bucket) GetResponseWithHeaders(path string, headers map[string][]string) (resp *http.Response, err error) {
+ req := &request{
+ bucket: b.Name,
+ path: path,
+ headers: headers,
+ }
+ err = b.S3.prepare(req)
+ if err != nil {
+ return nil, err
+ }
+ for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
+ resp, err := b.S3.run(req, nil)
+ if shouldRetry(err) && attempt.HasNext() {
+ continue
+ }
+ if err != nil {
+ return nil, err
+ }
+ return resp, nil
+ }
+ panic("unreachable")
+}
+
+// Exists checks whether or not an object exists on an S3 bucket using a HEAD request.
+func (b *Bucket) Exists(path string) (exists bool, err error) {
+ req := &request{
+ method: "HEAD",
+ bucket: b.Name,
+ path: path,
+ }
+ err = b.S3.prepare(req)
+ if err != nil {
+ return
+ }
+ for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
+ resp, err := b.S3.run(req, nil)
+
+ if shouldRetry(err) && attempt.HasNext() {
+ continue
+ }
+
+ if err != nil {
+ // We can treat a 403 or 404 as non existance
+ if e, ok := err.(*Error); ok && (e.StatusCode == 403 || e.StatusCode == 404) {
+ return false, nil
+ }
+ return false, err
+ }
+
+ if resp.StatusCode/100 == 2 {
+ exists = true
+ }
+ return exists, err
+ }
+ return false, fmt.Errorf("S3 Currently Unreachable")
+}
+
+// Head HEADs an object in the S3 bucket, returns the response with
+// no body see http://bit.ly/17K1ylI
+func (b *Bucket) Head(path string, headers map[string][]string) (*http.Response, error) {
+ req := &request{
+ method: "HEAD",
+ bucket: b.Name,
+ path: path,
+ headers: headers,
+ }
+ err := b.S3.prepare(req)
+ if err != nil {
+ return nil, err
+ }
+
+ for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
+ resp, err := b.S3.run(req, nil)
+ if shouldRetry(err) && attempt.HasNext() {
+ continue
+ }
+ if err != nil {
+ return nil, err
+ }
+ return resp, err
+ }
+ return nil, fmt.Errorf("S3 Currently Unreachable")
+}
+
+// Put inserts an object into the S3 bucket.
+//
+// See http://goo.gl/FEBPD for details.
+func (b *Bucket) Put(path string, data []byte, contType string, perm ACL, options Options) error {
+ body := bytes.NewBuffer(data)
+ return b.PutReader(path, body, int64(len(data)), contType, perm, options)
+}
+
+// PutCopy puts a copy of an object given by the key path into bucket b using b.Path as the target key
+func (b *Bucket) PutCopy(path string, perm ACL, options CopyOptions, source string) (result *CopyObjectResult, err error) {
+ headers := map[string][]string{
+ "x-amz-acl": {string(perm)},
+ "x-amz-copy-source": {source},
+ }
+ options.addHeaders(headers)
+ req := &request{
+ method: "PUT",
+ bucket: b.Name,
+ path: path,
+ headers: headers,
+ }
+ result = &CopyObjectResult{}
+ for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
+ err = b.S3.query(req, result)
+ if !shouldRetry(err) {
+ break
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+/*
+PutHeader - like Put, inserts an object into the S3 bucket.
+Instead of Content-Type string, pass in custom headers to override defaults.
+*/
+func (b *Bucket) PutHeader(path string, data []byte, customHeaders map[string][]string, perm ACL) error {
+ body := bytes.NewBuffer(data)
+ return b.PutReaderHeader(path, body, int64(len(data)), customHeaders, perm)
+}
+
+// PutReader inserts an object into the S3 bucket by consuming data
+// from r until EOF.
+func (b *Bucket) PutReader(path string, r io.Reader, length int64, contType string, perm ACL, options Options) error {
+ headers := map[string][]string{
+ "Content-Length": {strconv.FormatInt(length, 10)},
+ "Content-Type": {contType},
+ "x-amz-acl": {string(perm)},
+ }
+ options.addHeaders(headers)
+ req := &request{
+ method: "PUT",
+ bucket: b.Name,
+ path: path,
+ headers: headers,
+ payload: r,
+ }
+ return b.S3.query(req, nil)
+}
+
+/*
+PutReaderHeader - like PutReader, inserts an object into S3 from a reader.
+Instead of Content-Type string, pass in custom headers to override defaults.
+*/
+func (b *Bucket) PutReaderHeader(path string, r io.Reader, length int64, customHeaders map[string][]string, perm ACL) error {
+ // Default headers
+ headers := map[string][]string{
+ "Content-Length": {strconv.FormatInt(length, 10)},
+ "Content-Type": {"application/text"},
+ "x-amz-acl": {string(perm)},
+ }
+
+ // Override with custom headers
+ for key, value := range customHeaders {
+ headers[key] = value
+ }
+
+ req := &request{
+ method: "PUT",
+ bucket: b.Name,
+ path: path,
+ headers: headers,
+ payload: r,
+ }
+ return b.S3.query(req, nil)
+}
+
+// addHeaders adds o's specified fields to headers
+func (o Options) addHeaders(headers map[string][]string) {
+ if o.SSE {
+ headers["x-amz-server-side-encryption"] = []string{"AES256"}
+ }
+ if len(o.ContentEncoding) != 0 {
+ headers["Content-Encoding"] = []string{o.ContentEncoding}
+ }
+ if len(o.CacheControl) != 0 {
+ headers["Cache-Control"] = []string{o.CacheControl}
+ }
+ if len(o.ContentMD5) != 0 {
+ headers["Content-MD5"] = []string{o.ContentMD5}
+ }
+ if len(o.RedirectLocation) != 0 {
+ headers["x-amz-website-redirect-location"] = []string{o.RedirectLocation}
+ }
+ for k, v := range o.Meta {
+ headers["x-amz-meta-"+k] = v
+ }
+}
+
+// addHeaders adds o's specified fields to headers
+func (o CopyOptions) addHeaders(headers map[string][]string) {
+ o.Options.addHeaders(headers)
+ if len(o.MetadataDirective) != 0 {
+ headers["x-amz-metadata-directive"] = []string{o.MetadataDirective}
+ }
+ if len(o.ContentType) != 0 {
+ headers["Content-Type"] = []string{o.ContentType}
+ }
+}
+
+func makeXmlBuffer(doc []byte) *bytes.Buffer {
+ buf := new(bytes.Buffer)
+ buf.WriteString(xml.Header)
+ buf.Write(doc)
+ return buf
+}
+
+type RoutingRule struct {
+ ConditionKeyPrefixEquals string `xml:"Condition>KeyPrefixEquals"`
+ RedirectReplaceKeyPrefixWith string `xml:"Redirect>ReplaceKeyPrefixWith,omitempty"`
+ RedirectReplaceKeyWith string `xml:"Redirect>ReplaceKeyWith,omitempty"`
+}
+
+type WebsiteConfiguration struct {
+ XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ WebsiteConfiguration"`
+ IndexDocumentSuffix string `xml:"IndexDocument>Suffix"`
+ ErrorDocumentKey string `xml:"ErrorDocument>Key"`
+ RoutingRules *[]RoutingRule `xml:"RoutingRules>RoutingRule,omitempty"`
+}
+
+func (b *Bucket) PutBucketWebsite(configuration WebsiteConfiguration) error {
+
+ doc, err := xml.Marshal(configuration)
+ if err != nil {
+ return err
+ }
+
+ buf := makeXmlBuffer(doc)
+
+ return b.PutBucketSubresource("website", buf, int64(buf.Len()))
+}
+
+func (b *Bucket) PutBucketSubresource(subresource string, r io.Reader, length int64) error {
+ headers := map[string][]string{
+ "Content-Length": {strconv.FormatInt(length, 10)},
+ }
+ req := &request{
+ path: "/",
+ method: "PUT",
+ bucket: b.Name,
+ headers: headers,
+ payload: r,
+ params: url.Values{subresource: {""}},
+ }
+
+ return b.S3.query(req, nil)
+}
+
+// Del removes an object from the S3 bucket.
+//
+// See http://goo.gl/APeTt for details.
+func (b *Bucket) Del(path string) error {
+ req := &request{
+ method: "DELETE",
+ bucket: b.Name,
+ path: path,
+ }
+ return b.S3.query(req, nil)
+}
+
+type Delete struct {
+ Quiet bool `xml:"Quiet,omitempty"`
+ Objects []Object `xml:"Object"`
+}
+
+type Object struct {
+ Key string `xml:"Key"`
+ VersionId string `xml:"VersionId,omitempty"`
+}
+
+// DelMulti removes up to 1000 objects from the S3 bucket.
+//
+// See http://goo.gl/jx6cWK for details.
+func (b *Bucket) DelMulti(objects Delete) error {
+ doc, err := xml.Marshal(objects)
+ if err != nil {
+ return err
+ }
+
+ buf := makeXmlBuffer(doc)
+ digest := md5.New()
+ size, err := digest.Write(buf.Bytes())
+ if err != nil {
+ return err
+ }
+
+ headers := map[string][]string{
+ "Content-Length": {strconv.FormatInt(int64(size), 10)},
+ "Content-MD5": {base64.StdEncoding.EncodeToString(digest.Sum(nil))},
+ "Content-Type": {"text/xml"},
+ }
+ req := &request{
+ path: "/",
+ method: "POST",
+ params: url.Values{"delete": {""}},
+ bucket: b.Name,
+ headers: headers,
+ payload: buf,
+ }
+
+ return b.S3.query(req, nil)
+}
+
+// The ListResp type holds the results of a List bucket operation.
+type ListResp struct {
+ Name string
+ Prefix string
+ Delimiter string
+ Marker string
+ NextMarker string
+ MaxKeys int
+
+ // IsTruncated is true if the results have been truncated because
+ // there are more keys and prefixes than can fit in MaxKeys.
+ // N.B. this is the opposite sense to that documented (incorrectly) in
+ // http://goo.gl/YjQTc
+ IsTruncated bool
+ Contents []Key
+ CommonPrefixes []string `xml:">Prefix"`
+}
+
+// The Key type represents an item stored in an S3 bucket.
+type Key struct {
+ Key string
+ LastModified string
+ Size int64
+ // ETag gives the hex-encoded MD5 sum of the contents,
+ // surrounded with double-quotes.
+ ETag string
+ StorageClass string
+ Owner Owner
+}
+
+// List returns information about objects in an S3 bucket.
+//
+// The prefix parameter limits the response to keys that begin with the
+// specified prefix.
+//
+// The delim parameter causes the response to group all of the keys that
+// share a common prefix up to the next delimiter in a single entry within
+// the CommonPrefixes field. You can use delimiters to separate a bucket
+// into different groupings of keys, similar to how folders would work.
+//
+// The marker parameter specifies the key to start with when listing objects
+// in a bucket. Amazon S3 lists objects in alphabetical order and
+// will return keys alphabetically greater than the marker.
+//
+// The max parameter specifies how many keys + common prefixes to return in
+// the response. The default is 1000.
+//
+// For example, given these keys in a bucket:
+//
+// index.html
+// index2.html
+// photos/2006/January/sample.jpg
+// photos/2006/February/sample2.jpg
+// photos/2006/February/sample3.jpg
+// photos/2006/February/sample4.jpg
+//
+// Listing this bucket with delimiter set to "/" would yield the
+// following result:
+//
+// &ListResp{
+// Name: "sample-bucket",
+// MaxKeys: 1000,
+// Delimiter: "/",
+// Contents: []Key{
+// {Key: "index.html", "index2.html"},
+// },
+// CommonPrefixes: []string{
+// "photos/",
+// },
+// }
+//
+// Listing the same bucket with delimiter set to "/" and prefix set to
+// "photos/2006/" would yield the following result:
+//
+// &ListResp{
+// Name: "sample-bucket",
+// MaxKeys: 1000,
+// Delimiter: "/",
+// Prefix: "photos/2006/",
+// CommonPrefixes: []string{
+// "photos/2006/February/",
+// "photos/2006/January/",
+// },
+// }
+//
+// See http://goo.gl/YjQTc for details.
+func (b *Bucket) List(prefix, delim, marker string, max int) (result *ListResp, err error) {
+ params := map[string][]string{
+ "prefix": {prefix},
+ "delimiter": {delim},
+ "marker": {marker},
+ }
+ if max != 0 {
+ params["max-keys"] = []string{strconv.FormatInt(int64(max), 10)}
+ }
+ req := &request{
+ bucket: b.Name,
+ params: params,
+ }
+ result = &ListResp{}
+ for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
+ err = b.S3.query(req, result)
+ if !shouldRetry(err) {
+ break
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+// The VersionsResp type holds the results of a list bucket Versions operation.
+type VersionsResp struct {
+ Name string
+ Prefix string
+ KeyMarker string
+ VersionIdMarker string
+ MaxKeys int
+ Delimiter string
+ IsTruncated bool
+ Versions []Version
+ CommonPrefixes []string `xml:">Prefix"`
+}
+
+// The Version type represents an object version stored in an S3 bucket.
+type Version struct {
+ Key string
+ VersionId string
+ IsLatest bool
+ LastModified string
+ // ETag gives the hex-encoded MD5 sum of the contents,
+ // surrounded with double-quotes.
+ ETag string
+ Size int64
+ Owner Owner
+ StorageClass string
+}
+
+func (b *Bucket) Versions(prefix, delim, keyMarker string, versionIdMarker string, max int) (result *VersionsResp, err error) {
+ params := map[string][]string{
+ "versions": {""},
+ "prefix": {prefix},
+ "delimiter": {delim},
+ }
+
+ if len(versionIdMarker) != 0 {
+ params["version-id-marker"] = []string{versionIdMarker}
+ }
+ if len(keyMarker) != 0 {
+ params["key-marker"] = []string{keyMarker}
+ }
+
+ if max != 0 {
+ params["max-keys"] = []string{strconv.FormatInt(int64(max), 10)}
+ }
+ req := &request{
+ bucket: b.Name,
+ params: params,
+ }
+ result = &VersionsResp{}
+ for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
+ err = b.S3.query(req, result)
+ if !shouldRetry(err) {
+ break
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+// Returns a mapping of all key names in this bucket to Key objects
+func (b *Bucket) GetBucketContents() (*map[string]Key, error) {
+ bucket_contents := map[string]Key{}
+ prefix := ""
+ path_separator := ""
+ marker := ""
+ for {
+ contents, err := b.List(prefix, path_separator, marker, 1000)
+ if err != nil {
+ return &bucket_contents, err
+ }
+ for _, key := range contents.Contents {
+ bucket_contents[key.Key] = key
+ }
+ if contents.IsTruncated {
+ marker = contents.NextMarker
+ } else {
+ break
+ }
+ }
+
+ return &bucket_contents, nil
+}
+
+// URL returns a non-signed URL that allows retriving the
+// object at path. It only works if the object is publicly
+// readable (see SignedURL).
+func (b *Bucket) URL(path string) string {
+ req := &request{
+ bucket: b.Name,
+ path: path,
+ }
+ err := b.S3.prepare(req)
+ if err != nil {
+ panic(err)
+ }
+ u, err := req.url()
+ if err != nil {
+ panic(err)
+ }
+ u.RawQuery = ""
+ return u.String()
+}
+
+// SignedURL returns a signed URL that allows anyone holding the URL
+// to retrieve the object at path. The signature is valid until expires.
+func (b *Bucket) SignedURL(path string, expires time.Time) string {
+ req := &request{
+ bucket: b.Name,
+ path: path,
+ params: url.Values{"Expires": {strconv.FormatInt(expires.Unix(), 10)}},
+ }
+ err := b.S3.prepare(req)
+ if err != nil {
+ panic(err)
+ }
+ u, err := req.url()
+ if err != nil {
+ panic(err)
+ }
+ if b.S3.Auth.Token() != "" {
+ return u.String() + "&x-amz-security-token=" + url.QueryEscape(req.headers["X-Amz-Security-Token"][0])
+ } else {
+ return u.String()
+ }
+}
+
+// UploadSignedURL returns a signed URL that allows anyone holding the URL
+// to upload the object at path. The signature is valid until expires.
+// contenttype is a string like image/png
+// path is the resource name in s3 terminalogy like images/ali.png [obviously exclusing the bucket name itself]
+func (b *Bucket) UploadSignedURL(path, method, content_type string, expires time.Time) string {
+ expire_date := expires.Unix()
+ if method != "POST" {
+ method = "PUT"
+ }
+ stringToSign := method + "\n\n" + content_type + "\n" + strconv.FormatInt(expire_date, 10) + "\n/" + b.Name + "/" + path
+ fmt.Println("String to sign:\n", stringToSign)
+ a := b.S3.Auth
+ secretKey := a.SecretKey
+ accessId := a.AccessKey
+ mac := hmac.New(sha1.New, []byte(secretKey))
+ mac.Write([]byte(stringToSign))
+ macsum := mac.Sum(nil)
+ signature := base64.StdEncoding.EncodeToString([]byte(macsum))
+ signature = strings.TrimSpace(signature)
+
+ signedurl, err := url.Parse("https://" + b.Name + ".s3.amazonaws.com/")
+ if err != nil {
+ log.Println("ERROR sining url for S3 upload", err)
+ return ""
+ }
+ signedurl.Path += path
+ params := url.Values{}
+ params.Add("AWSAccessKeyId", accessId)
+ params.Add("Expires", strconv.FormatInt(expire_date, 10))
+ params.Add("Signature", signature)
+ if a.Token() != "" {
+ params.Add("token", a.Token())
+ }
+
+ signedurl.RawQuery = params.Encode()
+ return signedurl.String()
+}
+
+// PostFormArgs returns the action and input fields needed to allow anonymous
+// uploads to a bucket within the expiration limit
+func (b *Bucket) PostFormArgs(path string, expires time.Time, redirect string) (action string, fields map[string]string) {
+ conditions := make([]string, 0)
+ fields = map[string]string{
+ "AWSAccessKeyId": b.Auth.AccessKey,
+ "key": path,
+ }
+
+ conditions = append(conditions, fmt.Sprintf("{\"key\": \"%s\"}", path))
+ conditions = append(conditions, fmt.Sprintf("{\"bucket\": \"%s\"}", b.Name))
+ if redirect != "" {
+ conditions = append(conditions, fmt.Sprintf("{\"success_action_redirect\": \"%s\"}", redirect))
+ fields["success_action_redirect"] = redirect
+ }
+
+ vExpiration := expires.Format("2006-01-02T15:04:05Z")
+ vConditions := strings.Join(conditions, ",")
+ policy := fmt.Sprintf("{\"expiration\": \"%s\", \"conditions\": [%s]}", vExpiration, vConditions)
+ policy64 := base64.StdEncoding.EncodeToString([]byte(policy))
+ fields["policy"] = policy64
+
+ signer := hmac.New(sha1.New, []byte(b.Auth.SecretKey))
+ signer.Write([]byte(policy64))
+ fields["signature"] = base64.StdEncoding.EncodeToString(signer.Sum(nil))
+
+ action = fmt.Sprintf("%s/%s/", b.S3.Region.S3Endpoint, b.Name)
+ return
+}
+
+type request struct {
+ method string
+ bucket string
+ path string
+ signpath string
+ params url.Values
+ headers http.Header
+ baseurl string
+ payload io.Reader
+ prepared bool
+}
+
+func (req *request) url() (*url.URL, error) {
+ u, err := url.Parse(req.baseurl)
+ if err != nil {
+ return nil, fmt.Errorf("bad S3 endpoint URL %q: %v", req.baseurl, err)
+ }
+ u.RawQuery = req.params.Encode()
+ u.Path = req.path
+ return u, nil
+}
+
+// query prepares and runs the req request.
+// If resp is not nil, the XML data contained in the response
+// body will be unmarshalled on it.
+func (s3 *S3) query(req *request, resp interface{}) error {
+ err := s3.prepare(req)
+ if err == nil {
+ var httpResponse *http.Response
+ httpResponse, err = s3.run(req, resp)
+ if resp == nil && httpResponse != nil {
+ httpResponse.Body.Close()
+ }
+ }
+ return err
+}
+
+// prepare sets up req to be delivered to S3.
+func (s3 *S3) prepare(req *request) error {
+ var signpath = req.path
+
+ if !req.prepared {
+ req.prepared = true
+ if req.method == "" {
+ req.method = "GET"
+ }
+ // Copy so they can be mutated without affecting on retries.
+ params := make(url.Values)
+ headers := make(http.Header)
+ for k, v := range req.params {
+ params[k] = v
+ }
+ for k, v := range req.headers {
+ headers[k] = v
+ }
+ req.params = params
+ req.headers = headers
+ if !strings.HasPrefix(req.path, "/") {
+ req.path = "/" + req.path
+ }
+ signpath = req.path
+ if req.bucket != "" {
+ req.baseurl = s3.Region.S3BucketEndpoint
+ if req.baseurl == "" {
+ // Use the path method to address the bucket.
+ req.baseurl = s3.Region.S3Endpoint
+ req.path = "/" + req.bucket + req.path
+ } else {
+ // Just in case, prevent injection.
+ if strings.IndexAny(req.bucket, "/:@") >= 0 {
+ return fmt.Errorf("bad S3 bucket: %q", req.bucket)
+ }
+ req.baseurl = strings.Replace(req.baseurl, "${bucket}", req.bucket, -1)
+ }
+ signpath = "/" + req.bucket + signpath
+ }
+ }
+
+ // Always sign again as it's not clear how far the
+ // server has handled a previous attempt.
+ u, err := url.Parse(req.baseurl)
+ if err != nil {
+ return fmt.Errorf("bad S3 endpoint URL %q: %v", req.baseurl, err)
+ }
+ reqSignpathSpaceFix := (&url.URL{Path: signpath}).String()
+ req.headers["Host"] = []string{u.Host}
+ req.headers["Date"] = []string{time.Now().In(time.UTC).Format(time.RFC1123)}
+ if s3.Auth.Token() != "" {
+ req.headers["X-Amz-Security-Token"] = []string{s3.Auth.Token()}
+ }
+ sign(s3.Auth, req.method, reqSignpathSpaceFix, req.params, req.headers)
+ return nil
+}
+
+// run sends req and returns the http response from the server.
+// If resp is not nil, the XML data contained in the response
+// body will be unmarshalled on it.
+func (s3 *S3) run(req *request, resp interface{}) (*http.Response, error) {
+ if debug {
+ log.Printf("Running S3 request: %#v", req)
+ }
+
+ u, err := req.url()
+ if err != nil {
+ return nil, err
+ }
+
+ hreq := http.Request{
+ URL: u,
+ Method: req.method,
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Close: true,
+ Header: req.headers,
+ }
+
+ if v, ok := req.headers["Content-Length"]; ok {
+ hreq.ContentLength, _ = strconv.ParseInt(v[0], 10, 64)
+ delete(req.headers, "Content-Length")
+ }
+ if req.payload != nil {
+ hreq.Body = ioutil.NopCloser(req.payload)
+ }
+
+ if s3.client == nil {
+ s3.client = &http.Client{
+ Transport: &http.Transport{
+ Dial: func(netw, addr string) (c net.Conn, err error) {
+ c, err = net.DialTimeout(netw, addr, s3.ConnectTimeout)
+ if err != nil {
+ return
+ }
+
+ var deadline time.Time
+ if s3.RequestTimeout > 0 {
+ deadline = time.Now().Add(s3.RequestTimeout)
+ c.SetDeadline(deadline)
+ }
+
+ if s3.ReadTimeout > 0 || s3.WriteTimeout > 0 {
+ c = &ioTimeoutConn{
+ TCPConn: c.(*net.TCPConn),
+ readTimeout: s3.ReadTimeout,
+ writeTimeout: s3.WriteTimeout,
+ requestDeadline: deadline,
+ }
+ }
+ return
+ },
+ },
+ }
+ }
+
+ hresp, err := s3.client.Do(&hreq)
+ if err != nil {
+ return nil, err
+ }
+ if debug {
+ dump, _ := httputil.DumpResponse(hresp, true)
+ log.Printf("} -> %s\n", dump)
+ }
+ if hresp.StatusCode != 200 && hresp.StatusCode != 204 && hresp.StatusCode != 206 {
+ defer hresp.Body.Close()
+ return nil, buildError(hresp)
+ }
+ if resp != nil {
+ err = xml.NewDecoder(hresp.Body).Decode(resp)
+ hresp.Body.Close()
+ if debug {
+ log.Printf("goamz.s3> decoded xml into %#v", resp)
+ }
+ }
+ return hresp, err
+}
+
+// Error represents an error in an operation with S3.
+type Error struct {
+ StatusCode int // HTTP status code (200, 403, ...)
+ Code string // EC2 error code ("UnsupportedOperation", ...)
+ Message string // The human-oriented error message
+ BucketName string
+ RequestId string
+ HostId string
+}
+
+func (e *Error) Error() string {
+ return e.Message
+}
+
+func buildError(r *http.Response) error {
+ if debug {
+ log.Printf("got error (status code %v)", r.StatusCode)
+ data, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ log.Printf("\tread error: %v", err)
+ } else {
+ log.Printf("\tdata:\n%s\n\n", data)
+ }
+ r.Body = ioutil.NopCloser(bytes.NewBuffer(data))
+ }
+
+ err := Error{}
+ // TODO return error if Unmarshal fails?
+ xml.NewDecoder(r.Body).Decode(&err)
+ r.Body.Close()
+ err.StatusCode = r.StatusCode
+ if err.Message == "" {
+ err.Message = r.Status
+ }
+ if debug {
+ log.Printf("err: %#v\n", err)
+ }
+ return &err
+}
+
+func shouldRetry(err error) bool {
+ if err == nil {
+ return false
+ }
+ if e, ok := err.(*url.Error); ok {
+ // Transport returns this string if it detects a write on a connection which
+ // has already had an error
+ if e.Err.Error() == "http: can't write HTTP request on broken connection" {
+ return true
+ }
+ err = e.Err
+ }
+
+ switch err {
+ case io.ErrUnexpectedEOF, io.EOF:
+ return true
+ }
+ switch e := err.(type) {
+ case *net.DNSError:
+ return true
+ case *net.OpError:
+ switch e.Op {
+ case "read", "write", "WSARecv", "WSASend", "ConnectEx":
+ return true
+ }
+ case *Error:
+ switch e.Code {
+ case "InternalError", "NoSuchUpload", "NoSuchBucket", "RequestTimeout":
+ return true
+ }
+ // let's handle tls handshake timeout issues and similar temporary errors
+ case net.Error:
+ return e.Temporary()
+ }
+
+ return false
+}
+
+func hasCode(err error, code string) bool {
+ s3err, ok := err.(*Error)
+ return ok && s3err.Code == code
+}
+
+// ioTimeoutConn is a net.Conn which sets a deadline for each Read or Write operation
+type ioTimeoutConn struct {
+ *net.TCPConn
+ readTimeout time.Duration
+ writeTimeout time.Duration
+ requestDeadline time.Time
+}
+
+func (c *ioTimeoutConn) deadline(timeout time.Duration) time.Time {
+ dl := time.Now().Add(timeout)
+ if c.requestDeadline.IsZero() || dl.Before(c.requestDeadline) {
+ return dl
+ }
+
+ return c.requestDeadline
+}
+
+func (c *ioTimeoutConn) Read(b []byte) (int, error) {
+ if c.readTimeout > 0 {
+ err := c.TCPConn.SetReadDeadline(c.deadline(c.readTimeout))
+ if err != nil {
+ return 0, err
+ }
+ }
+ return c.TCPConn.Read(b)
+}
+
+func (c *ioTimeoutConn) Write(b []byte) (int, error) {
+ if c.writeTimeout > 0 {
+ err := c.TCPConn.SetWriteDeadline(c.deadline(c.writeTimeout))
+ if err != nil {
+ return 0, err
+ }
+ }
+ return c.TCPConn.Write(b)
+}
diff --git a/vendor/github.com/goamz/goamz/s3/sign.go b/vendor/github.com/goamz/goamz/s3/sign.go
new file mode 100644
index 000000000..722d97d29
--- /dev/null
+++ b/vendor/github.com/goamz/goamz/s3/sign.go
@@ -0,0 +1,141 @@
+package s3
+
+import (
+ "crypto/hmac"
+ "crypto/sha1"
+ "encoding/base64"
+ "github.com/goamz/goamz/aws"
+ "log"
+ "sort"
+ "strings"
+)
+
+var b64 = base64.StdEncoding
+
+// ----------------------------------------------------------------------------
+// S3 signing (http://goo.gl/G1LrK)
+
+var s3ParamsToSign = map[string]bool{
+ "acl": true,
+ "location": true,
+ "logging": true,
+ "notification": true,
+ "partNumber": true,
+ "policy": true,
+ "requestPayment": true,
+ "torrent": true,
+ "uploadId": true,
+ "uploads": true,
+ "versionId": true,
+ "versioning": true,
+ "versions": true,
+ "response-content-type": true,
+ "response-content-language": true,
+ "response-expires": true,
+ "response-cache-control": true,
+ "response-content-disposition": true,
+ "response-content-encoding": true,
+ "website": true,
+ "delete": true,
+}
+
+type keySortableTupleList []keySortableTuple
+
+type keySortableTuple struct {
+ Key string
+ TupleString string
+}
+
+func (l keySortableTupleList) StringSlice() []string {
+ slice := make([]string, len(l))
+ for i, v := range l {
+ slice[i] = v.TupleString
+ }
+ return slice
+}
+
+func (l keySortableTupleList) Len() int {
+ return len(l)
+}
+
+func (l keySortableTupleList) Less(i, j int) bool {
+ return l[i].Key < l[j].Key
+}
+
+func (l keySortableTupleList) Swap(i, j int) {
+ l[i], l[j] = l[j], l[i]
+}
+
+func sign(auth aws.Auth, method, canonicalPath string, params, headers map[string][]string) {
+ var md5, ctype, date, xamz string
+ var xamzDate bool
+ var sarray keySortableTupleList
+ for k, v := range headers {
+ k = strings.ToLower(k)
+ switch k {
+ case "content-md5":
+ md5 = v[0]
+ case "content-type":
+ ctype = v[0]
+ case "date":
+ if !xamzDate {
+ date = v[0]
+ }
+ default:
+ if strings.HasPrefix(k, "x-amz-") {
+ vall := strings.Join(v, ",")
+ sarray = append(sarray, keySortableTuple{k, k + ":" + vall})
+ if k == "x-amz-date" {
+ xamzDate = true
+ date = ""
+ }
+ }
+ }
+ }
+ if len(sarray) > 0 {
+ sort.Sort(sarray)
+ xamz = strings.Join(sarray.StringSlice(), "\n") + "\n"
+ }
+
+ expires := false
+ if v, ok := params["Expires"]; ok {
+ // Query string request authentication alternative.
+ expires = true
+ date = v[0]
+ params["AWSAccessKeyId"] = []string{auth.AccessKey}
+ }
+
+ sarray = sarray[0:0]
+ for k, v := range params {
+ if s3ParamsToSign[k] {
+ for _, vi := range v {
+ if vi == "" {
+ sarray = append(sarray, keySortableTuple{k, k})
+ } else {
+ // "When signing you do not encode these values."
+ sarray = append(sarray, keySortableTuple{k, k + "=" + vi})
+ }
+ }
+ }
+ }
+ if len(sarray) > 0 {
+ sort.Sort(sarray)
+ canonicalPath = canonicalPath + "?" + strings.Join(sarray.StringSlice(), "&")
+ }
+
+ payload := method + "\n" + md5 + "\n" + ctype + "\n" + date + "\n" + xamz + canonicalPath
+ hash := hmac.New(sha1.New, []byte(auth.SecretKey))
+ hash.Write([]byte(payload))
+ signature := make([]byte, b64.EncodedLen(hash.Size()))
+ b64.Encode(signature, hash.Sum(nil))
+
+ if expires {
+ params["Signature"] = []string{string(signature)}
+ } else {
+ headers["Authorization"] = []string{"AWS " + auth.AccessKey + ":" + string(signature)}
+ }
+ if debug {
+ log.Printf("Signature payload: %q", payload)
+ log.Printf("Signature: %q", signature)
+ }
+}
diff --git a/vendor/github.com/golang/freetype/AUTHORS b/vendor/github.com/golang/freetype/AUTHORS
new file mode 100644
index 000000000..5773ac7ed
--- /dev/null
+++ b/vendor/github.com/golang/freetype/AUTHORS
@@ -0,0 +1,20 @@
+# This is the official list of Freetype-Go authors for copyright purposes.
+# This file is distinct from the CONTRIBUTORS files.
+# See the latter for an explanation.
+#
+# Freetype-Go is derived from Freetype, which is written in C. The latter
+# is copyright 1996-2010 David Turner, Robert Wilhelm, and Werner Lemberg.
+
+# Names should be added to this file as
+# Name or Organization <email address>
+# The email address is not required for organizations.
+
+# Please keep the list sorted.
+
+Google Inc.
+Jeff R. Allen <jra@nella.org>
+Maksim Kochkin <maxxarts@gmail.com>
+Michael Fogleman <fogleman@gmail.com>
+Rémy Oudompheng <oudomphe@phare.normalesup.org>
+Roger Peppe <rogpeppe@gmail.com>
+Steven Edwards <steven@stephenwithav.com>
diff --git a/vendor/github.com/golang/freetype/CONTRIBUTORS b/vendor/github.com/golang/freetype/CONTRIBUTORS
new file mode 100644
index 000000000..7a1b0a278
--- /dev/null
+++ b/vendor/github.com/golang/freetype/CONTRIBUTORS
@@ -0,0 +1,38 @@
+# This is the official list of people who can contribute
+# (and typically have contributed) code to the Freetype-Go repository.
+# The AUTHORS file lists the copyright holders; this file
+# lists people. For example, Google employees are listed here
+# but not in AUTHORS, because Google holds the copyright.
+#
+# The submission process automatically checks to make sure
+# that people submitting code are listed in this file (by email address).
+#
+# Names should be added to this file only after verifying that
+# the individual or the individual's organization has agreed to
+# the appropriate Contributor License Agreement, found here:
+#
+# http://code.google.com/legal/individual-cla-v1.0.html
+# http://code.google.com/legal/corporate-cla-v1.0.html
+#
+# The agreement for individuals can be filled out on the web.
+#
+# When adding J Random Contributor's name to this file,
+# either J's name or J's organization's name should be
+# added to the AUTHORS file, depending on whether the
+# individual or corporate CLA was used.
+
+# Names should be added to this file like so:
+# Name <email address>
+
+# Please keep the list sorted.
+
+Andrew Gerrand <adg@golang.org>
+Jeff R. Allen <jra@nella.org> <jeff.allen@gmail.com>
+Maksim Kochkin <maxxarts@gmail.com>
+Michael Fogleman <fogleman@gmail.com>
+Nigel Tao <nigeltao@golang.org>
+Rémy Oudompheng <oudomphe@phare.normalesup.org> <remyoudompheng@gmail.com>
+Rob Pike <r@golang.org>
+Roger Peppe <rogpeppe@gmail.com>
+Russ Cox <rsc@golang.org>
+Steven Edwards <steven@stephenwithav.com>
diff --git a/vendor/github.com/golang/freetype/LICENSE b/vendor/github.com/golang/freetype/LICENSE
new file mode 100644
index 000000000..e854ba5db
--- /dev/null
+++ b/vendor/github.com/golang/freetype/LICENSE
@@ -0,0 +1,12 @@
+Use of the Freetype-Go software is subject to your choice of exactly one of
+the following two licenses:
+ * The FreeType License, which is similar to the original BSD license with
+ an advertising clause, or
+ * The GNU General Public License (GPL), version 2 or later.
+
+The text of these licenses are available in the licenses/ftl.txt and the
+licenses/gpl.txt files respectively. They are also available at
+http://freetype.sourceforge.net/license.html
+
+The Luxi fonts in the testdata directory are licensed separately. See the
+testdata/COPYING file for details.
diff --git a/vendor/github.com/golang/freetype/README b/vendor/github.com/golang/freetype/README
new file mode 100644
index 000000000..39b3d8250
--- /dev/null
+++ b/vendor/github.com/golang/freetype/README
@@ -0,0 +1,21 @@
+The Freetype font rasterizer in the Go programming language.
+
+To download and install from source:
+$ go get github.com/golang/freetype
+
+It is an incomplete port:
+ * It only supports TrueType fonts, and not Type 1 fonts nor bitmap fonts.
+ * It only supports the Unicode encoding.
+
+There are also some implementation differences:
+ * It uses a 26.6 fixed point co-ordinate system everywhere internally,
+ as opposed to the original Freetype's mix of 26.6 (or 10.6 for 16-bit
+ systems) in some places, and 24.8 in the "smooth" rasterizer.
+
+Freetype-Go is derived from Freetype, which is written in C. Freetype is
+copyright 1996-2010 David Turner, Robert Wilhelm, and Werner Lemberg.
+Freetype-Go is copyright The Freetype-Go Authors, who are listed in the
+AUTHORS file.
+
+Unless otherwise noted, the Freetype-Go source files are distributed
+under the BSD-style license found in the LICENSE file.
diff --git a/vendor/github.com/golang/freetype/freetype.go b/vendor/github.com/golang/freetype/freetype.go
new file mode 100644
index 000000000..bec01f6b7
--- /dev/null
+++ b/vendor/github.com/golang/freetype/freetype.go
@@ -0,0 +1,341 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+// The freetype package provides a convenient API to draw text onto an image.
+// Use the freetype/raster and freetype/truetype packages for lower level
+// control over rasterization and TrueType parsing.
+package freetype
+
+import (
+ "errors"
+ "image"
+ "image/draw"
+
+ "github.com/golang/freetype/raster"
+ "github.com/golang/freetype/truetype"
+ "golang.org/x/image/font"
+ "golang.org/x/image/math/fixed"
+)
+
+// These constants determine the size of the glyph cache. The cache is keyed
+// primarily by the glyph index modulo nGlyphs, and secondarily by sub-pixel
+// position for the mask image. Sub-pixel positions are quantized to
+// nXFractions possible values in both the x and y directions.
+const (
+ nGlyphs = 256
+ nXFractions = 4
+ nYFractions = 1
+)
+
+// An entry in the glyph cache is keyed explicitly by the glyph index and
+// implicitly by the quantized x and y fractional offset. It maps to a mask
+// image and an offset.
+type cacheEntry struct {
+ valid bool
+ glyph truetype.Index
+ advanceWidth fixed.Int26_6
+ mask *image.Alpha
+ offset image.Point
+}
+
+// ParseFont just calls the Parse function from the freetype/truetype package.
+// It is provided here so that code that imports this package doesn't need
+// to also include the freetype/truetype package.
+func ParseFont(b []byte) (*truetype.Font, error) {
+ return truetype.Parse(b)
+}
+
+// Pt converts from a co-ordinate pair measured in pixels to a fixed.Point26_6
+// co-ordinate pair measured in fixed.Int26_6 units.
+func Pt(x, y int) fixed.Point26_6 {
+ return fixed.Point26_6{
+ X: fixed.Int26_6(x << 6),
+ Y: fixed.Int26_6(y << 6),
+ }
+}
+
+// A Context holds the state for drawing text in a given font and size.
+type Context struct {
+ r *raster.Rasterizer
+ f *truetype.Font
+ glyphBuf truetype.GlyphBuf
+ // clip is the clip rectangle for drawing.
+ clip image.Rectangle
+ // dst and src are the destination and source images for drawing.
+ dst draw.Image
+ src image.Image
+ // fontSize and dpi are used to calculate scale. scale is the number of
+ // 26.6 fixed point units in 1 em. hinting is the hinting policy.
+ fontSize, dpi float64
+ scale fixed.Int26_6
+ hinting font.Hinting
+ // cache is the glyph cache.
+ cache [nGlyphs * nXFractions * nYFractions]cacheEntry
+}
+
+// PointToFixed converts the given number of points (as in "a 12 point font")
+// into a 26.6 fixed point number of pixels.
+func (c *Context) PointToFixed(x float64) fixed.Int26_6 {
+ return fixed.Int26_6(x * float64(c.dpi) * (64.0 / 72.0))
+}
+
+// drawContour draws the given closed contour with the given offset.
+func (c *Context) drawContour(ps []truetype.Point, dx, dy fixed.Int26_6) {
+ if len(ps) == 0 {
+ return
+ }
+
+ // The low bit of each point's Flags value is whether the point is on the
+ // curve. Truetype fonts only have quadratic Bézier curves, not cubics.
+ // Thus, two consecutive off-curve points imply an on-curve point in the
+ // middle of those two.
+ //
+ // See http://chanae.walon.org/pub/ttf/ttf_glyphs.htm for more details.
+
+ // ps[0] is a truetype.Point measured in FUnits and positive Y going
+ // upwards. start is the same thing measured in fixed point units and
+ // positive Y going downwards, and offset by (dx, dy).
+ start := fixed.Point26_6{
+ X: dx + ps[0].X,
+ Y: dy - ps[0].Y,
+ }
+ others := []truetype.Point(nil)
+ if ps[0].Flags&0x01 != 0 {
+ others = ps[1:]
+ } else {
+ last := fixed.Point26_6{
+ X: dx + ps[len(ps)-1].X,
+ Y: dy - ps[len(ps)-1].Y,
+ }
+ if ps[len(ps)-1].Flags&0x01 != 0 {
+ start = last
+ others = ps[:len(ps)-1]
+ } else {
+ start = fixed.Point26_6{
+ X: (start.X + last.X) / 2,
+ Y: (start.Y + last.Y) / 2,
+ }
+ others = ps
+ }
+ }
+ c.r.Start(start)
+ q0, on0 := start, true
+ for _, p := range others {
+ q := fixed.Point26_6{
+ X: dx + p.X,
+ Y: dy - p.Y,
+ }
+ on := p.Flags&0x01 != 0
+ if on {
+ if on0 {
+ c.r.Add1(q)
+ } else {
+ c.r.Add2(q0, q)
+ }
+ } else {
+ if on0 {
+ // No-op.
+ } else {
+ mid := fixed.Point26_6{
+ X: (q0.X + q.X) / 2,
+ Y: (q0.Y + q.Y) / 2,
+ }
+ c.r.Add2(q0, mid)
+ }
+ }
+ q0, on0 = q, on
+ }
+ // Close the curve.
+ if on0 {
+ c.r.Add1(start)
+ } else {
+ c.r.Add2(q0, start)
+ }
+}
+
+// rasterize returns the advance width, glyph mask and integer-pixel offset
+// to render the given glyph at the given sub-pixel offsets.
+// The 26.6 fixed point arguments fx and fy must be in the range [0, 1).
+func (c *Context) rasterize(glyph truetype.Index, fx, fy fixed.Int26_6) (
+ fixed.Int26_6, *image.Alpha, image.Point, error) {
+
+ if err := c.glyphBuf.Load(c.f, c.scale, glyph, c.hinting); err != nil {
+ return 0, nil, image.Point{}, err
+ }
+ // Calculate the integer-pixel bounds for the glyph.
+ xmin := int(fx+c.glyphBuf.Bounds.Min.X) >> 6
+ ymin := int(fy-c.glyphBuf.Bounds.Max.Y) >> 6
+ xmax := int(fx+c.glyphBuf.Bounds.Max.X+0x3f) >> 6
+ ymax := int(fy-c.glyphBuf.Bounds.Min.Y+0x3f) >> 6
+ if xmin > xmax || ymin > ymax {
+ return 0, nil, image.Point{}, errors.New("freetype: negative sized glyph")
+ }
+ // A TrueType's glyph's nodes can have negative co-ordinates, but the
+ // rasterizer clips anything left of x=0 or above y=0. xmin and ymin are
+ // the pixel offsets, based on the font's FUnit metrics, that let a
+ // negative co-ordinate in TrueType space be non-negative in rasterizer
+ // space. xmin and ymin are typically <= 0.
+ fx -= fixed.Int26_6(xmin << 6)
+ fy -= fixed.Int26_6(ymin << 6)
+ // Rasterize the glyph's vectors.
+ c.r.Clear()
+ e0 := 0
+ for _, e1 := range c.glyphBuf.Ends {
+ c.drawContour(c.glyphBuf.Points[e0:e1], fx, fy)
+ e0 = e1
+ }
+ a := image.NewAlpha(image.Rect(0, 0, xmax-xmin, ymax-ymin))
+ c.r.Rasterize(raster.NewAlphaSrcPainter(a))
+ return c.glyphBuf.AdvanceWidth, a, image.Point{xmin, ymin}, nil
+}
+
+// glyph returns the advance width, glyph mask and integer-pixel offset to
+// render the given glyph at the given sub-pixel point. It is a cache for the
+// rasterize method. Unlike rasterize, p's co-ordinates do not have to be in
+// the range [0, 1).
+func (c *Context) glyph(glyph truetype.Index, p fixed.Point26_6) (
+ fixed.Int26_6, *image.Alpha, image.Point, error) {
+
+ // Split p.X and p.Y into their integer and fractional parts.
+ ix, fx := int(p.X>>6), p.X&0x3f
+ iy, fy := int(p.Y>>6), p.Y&0x3f
+ // Calculate the index t into the cache array.
+ tg := int(glyph) % nGlyphs
+ tx := int(fx) / (64 / nXFractions)
+ ty := int(fy) / (64 / nYFractions)
+ t := ((tg*nXFractions)+tx)*nYFractions + ty
+ // Check for a cache hit.
+ if e := c.cache[t]; e.valid && e.glyph == glyph {
+ return e.advanceWidth, e.mask, e.offset.Add(image.Point{ix, iy}), nil
+ }
+ // Rasterize the glyph and put the result into the cache.
+ advanceWidth, mask, offset, err := c.rasterize(glyph, fx, fy)
+ if err != nil {
+ return 0, nil, image.Point{}, err
+ }
+ c.cache[t] = cacheEntry{true, glyph, advanceWidth, mask, offset}
+ return advanceWidth, mask, offset.Add(image.Point{ix, iy}), nil
+}
+
+// DrawString draws s at p and returns p advanced by the text extent. The text
+// is placed so that the left edge of the em square of the first character of s
+// and the baseline intersect at p. The majority of the affected pixels will be
+// above and to the right of the point, but some may be below or to the left.
+// For example, drawing a string that starts with a 'J' in an italic font may
+// affect pixels below and left of the point.
+//
+// p is a fixed.Point26_6 and can therefore represent sub-pixel positions.
+func (c *Context) DrawString(s string, p fixed.Point26_6) (fixed.Point26_6, error) {
+ if c.f == nil {
+ return fixed.Point26_6{}, errors.New("freetype: DrawText called with a nil font")
+ }
+ prev, hasPrev := truetype.Index(0), false
+ for _, rune := range s {
+ index := c.f.Index(rune)
+ if hasPrev {
+ kern := c.f.Kern(c.scale, prev, index)
+ if c.hinting != font.HintingNone {
+ kern = (kern + 32) &^ 63
+ }
+ p.X += kern
+ }
+ advanceWidth, mask, offset, err := c.glyph(index, p)
+ if err != nil {
+ return fixed.Point26_6{}, err
+ }
+ p.X += advanceWidth
+ glyphRect := mask.Bounds().Add(offset)
+ dr := c.clip.Intersect(glyphRect)
+ if !dr.Empty() {
+ mp := image.Point{0, dr.Min.Y - glyphRect.Min.Y}
+ draw.DrawMask(c.dst, dr, c.src, image.ZP, mask, mp, draw.Over)
+ }
+ prev, hasPrev = index, true
+ }
+ return p, nil
+}
+
+// recalc recalculates scale and bounds values from the font size, screen
+// resolution and font metrics, and invalidates the glyph cache.
+func (c *Context) recalc() {
+ c.scale = fixed.Int26_6(c.fontSize * c.dpi * (64.0 / 72.0))
+ if c.f == nil {
+ c.r.SetBounds(0, 0)
+ } else {
+ // Set the rasterizer's bounds to be big enough to handle the largest glyph.
+ b := c.f.Bounds(c.scale)
+ xmin := +int(b.Min.X) >> 6
+ ymin := -int(b.Max.Y) >> 6
+ xmax := +int(b.Max.X+63) >> 6
+ ymax := -int(b.Min.Y-63) >> 6
+ c.r.SetBounds(xmax-xmin, ymax-ymin)
+ }
+ for i := range c.cache {
+ c.cache[i] = cacheEntry{}
+ }
+}
+
+// SetDPI sets the screen resolution in dots per inch.
+func (c *Context) SetDPI(dpi float64) {
+ if c.dpi == dpi {
+ return
+ }
+ c.dpi = dpi
+ c.recalc()
+}
+
+// SetFont sets the font used to draw text.
+func (c *Context) SetFont(f *truetype.Font) {
+ if c.f == f {
+ return
+ }
+ c.f = f
+ c.recalc()
+}
+
+// SetFontSize sets the font size in points (as in "a 12 point font").
+func (c *Context) SetFontSize(fontSize float64) {
+ if c.fontSize == fontSize {
+ return
+ }
+ c.fontSize = fontSize
+ c.recalc()
+}
+
+// SetHinting sets the hinting policy.
+func (c *Context) SetHinting(hinting font.Hinting) {
+ c.hinting = hinting
+ for i := range c.cache {
+ c.cache[i] = cacheEntry{}
+ }
+}
+
+// SetDst sets the destination image for draw operations.
+func (c *Context) SetDst(dst draw.Image) {
+ c.dst = dst
+}
+
+// SetSrc sets the source image for draw operations. This is typically an
+// image.Uniform.
+func (c *Context) SetSrc(src image.Image) {
+ c.src = src
+}
+
+// SetClip sets the clip rectangle for drawing.
+func (c *Context) SetClip(clip image.Rectangle) {
+ c.clip = clip
+}
+
+// TODO(nigeltao): implement Context.SetGamma.
+
+// NewContext creates a new Context.
+func NewContext() *Context {
+ return &Context{
+ r: raster.NewRasterizer(0, 0),
+ fontSize: 12,
+ dpi: 72,
+ scale: 12 << 6,
+ }
+}
diff --git a/vendor/github.com/golang/freetype/raster/geom.go b/vendor/github.com/golang/freetype/raster/geom.go
new file mode 100644
index 000000000..f3696ea98
--- /dev/null
+++ b/vendor/github.com/golang/freetype/raster/geom.go
@@ -0,0 +1,245 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package raster
+
+import (
+ "fmt"
+ "math"
+
+ "golang.org/x/image/math/fixed"
+)
+
+// maxAbs returns the maximum of abs(a) and abs(b).
+func maxAbs(a, b fixed.Int26_6) fixed.Int26_6 {
+ if a < 0 {
+ a = -a
+ }
+ if b < 0 {
+ b = -b
+ }
+ if a < b {
+ return b
+ }
+ return a
+}
+
+// pNeg returns the vector -p, or equivalently p rotated by 180 degrees.
+func pNeg(p fixed.Point26_6) fixed.Point26_6 {
+ return fixed.Point26_6{-p.X, -p.Y}
+}
+
+// pDot returns the dot product p·q.
+func pDot(p fixed.Point26_6, q fixed.Point26_6) fixed.Int52_12 {
+ px, py := int64(p.X), int64(p.Y)
+ qx, qy := int64(q.X), int64(q.Y)
+ return fixed.Int52_12(px*qx + py*qy)
+}
+
+// pLen returns the length of the vector p.
+func pLen(p fixed.Point26_6) fixed.Int26_6 {
+ // TODO(nigeltao): use fixed point math.
+ x := float64(p.X)
+ y := float64(p.Y)
+ return fixed.Int26_6(math.Sqrt(x*x + y*y))
+}
+
+// pNorm returns the vector p normalized to the given length, or zero if p is
+// degenerate.
+func pNorm(p fixed.Point26_6, length fixed.Int26_6) fixed.Point26_6 {
+ d := pLen(p)
+ if d == 0 {
+ return fixed.Point26_6{}
+ }
+ s, t := int64(length), int64(d)
+ x := int64(p.X) * s / t
+ y := int64(p.Y) * s / t
+ return fixed.Point26_6{fixed.Int26_6(x), fixed.Int26_6(y)}
+}
+
+// pRot45CW returns the vector p rotated clockwise by 45 degrees.
+//
+// Note that the Y-axis grows downwards, so {1, 0}.Rot45CW is {1/√2, 1/√2}.
+func pRot45CW(p fixed.Point26_6) fixed.Point26_6 {
+ // 181/256 is approximately 1/√2, or sin(π/4).
+ px, py := int64(p.X), int64(p.Y)
+ qx := (+px - py) * 181 / 256
+ qy := (+px + py) * 181 / 256
+ return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
+}
+
+// pRot90CW returns the vector p rotated clockwise by 90 degrees.
+//
+// Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}.
+func pRot90CW(p fixed.Point26_6) fixed.Point26_6 {
+ return fixed.Point26_6{-p.Y, p.X}
+}
+
+// pRot135CW returns the vector p rotated clockwise by 135 degrees.
+//
+// Note that the Y-axis grows downwards, so {1, 0}.Rot135CW is {-1/√2, 1/√2}.
+func pRot135CW(p fixed.Point26_6) fixed.Point26_6 {
+ // 181/256 is approximately 1/√2, or sin(π/4).
+ px, py := int64(p.X), int64(p.Y)
+ qx := (-px - py) * 181 / 256
+ qy := (+px - py) * 181 / 256
+ return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
+}
+
+// pRot45CCW returns the vector p rotated counter-clockwise by 45 degrees.
+//
+// Note that the Y-axis grows downwards, so {1, 0}.Rot45CCW is {1/√2, -1/√2}.
+func pRot45CCW(p fixed.Point26_6) fixed.Point26_6 {
+ // 181/256 is approximately 1/√2, or sin(π/4).
+ px, py := int64(p.X), int64(p.Y)
+ qx := (+px + py) * 181 / 256
+ qy := (-px + py) * 181 / 256
+ return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
+}
+
+// pRot90CCW returns the vector p rotated counter-clockwise by 90 degrees.
+//
+// Note that the Y-axis grows downwards, so {1, 0}.Rot90CCW is {0, -1}.
+func pRot90CCW(p fixed.Point26_6) fixed.Point26_6 {
+ return fixed.Point26_6{p.Y, -p.X}
+}
+
+// pRot135CCW returns the vector p rotated counter-clockwise by 135 degrees.
+//
+// Note that the Y-axis grows downwards, so {1, 0}.Rot135CCW is {-1/√2, -1/√2}.
+func pRot135CCW(p fixed.Point26_6) fixed.Point26_6 {
+ // 181/256 is approximately 1/√2, or sin(π/4).
+ px, py := int64(p.X), int64(p.Y)
+ qx := (-px + py) * 181 / 256
+ qy := (-px - py) * 181 / 256
+ return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
+}
+
+// An Adder accumulates points on a curve.
+type Adder interface {
+ // Start starts a new curve at the given point.
+ Start(a fixed.Point26_6)
+ // Add1 adds a linear segment to the current curve.
+ Add1(b fixed.Point26_6)
+ // Add2 adds a quadratic segment to the current curve.
+ Add2(b, c fixed.Point26_6)
+ // Add3 adds a cubic segment to the current curve.
+ Add3(b, c, d fixed.Point26_6)
+}
+
+// A Path is a sequence of curves, and a curve is a start point followed by a
+// sequence of linear, quadratic or cubic segments.
+type Path []fixed.Int26_6
+
+// String returns a human-readable representation of a Path.
+func (p Path) String() string {
+ s := ""
+ for i := 0; i < len(p); {
+ if i != 0 {
+ s += " "
+ }
+ switch p[i] {
+ case 0:
+ s += "S0" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3]))
+ i += 4
+ case 1:
+ s += "A1" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3]))
+ i += 4
+ case 2:
+ s += "A2" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+5]))
+ i += 6
+ case 3:
+ s += "A3" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+7]))
+ i += 8
+ default:
+ panic("freetype/raster: bad path")
+ }
+ }
+ return s
+}
+
+// Clear cancels any previous calls to p.Start or p.AddXxx.
+func (p *Path) Clear() {
+ *p = (*p)[:0]
+}
+
+// Start starts a new curve at the given point.
+func (p *Path) Start(a fixed.Point26_6) {
+ *p = append(*p, 0, a.X, a.Y, 0)
+}
+
+// Add1 adds a linear segment to the current curve.
+func (p *Path) Add1(b fixed.Point26_6) {
+ *p = append(*p, 1, b.X, b.Y, 1)
+}
+
+// Add2 adds a quadratic segment to the current curve.
+func (p *Path) Add2(b, c fixed.Point26_6) {
+ *p = append(*p, 2, b.X, b.Y, c.X, c.Y, 2)
+}
+
+// Add3 adds a cubic segment to the current curve.
+func (p *Path) Add3(b, c, d fixed.Point26_6) {
+ *p = append(*p, 3, b.X, b.Y, c.X, c.Y, d.X, d.Y, 3)
+}
+
+// AddPath adds the Path q to p.
+func (p *Path) AddPath(q Path) {
+ *p = append(*p, q...)
+}
+
+// AddStroke adds a stroked Path.
+func (p *Path) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
+ Stroke(p, q, width, cr, jr)
+}
+
+// firstPoint returns the first point in a non-empty Path.
+func (p Path) firstPoint() fixed.Point26_6 {
+ return fixed.Point26_6{p[1], p[2]}
+}
+
+// lastPoint returns the last point in a non-empty Path.
+func (p Path) lastPoint() fixed.Point26_6 {
+ return fixed.Point26_6{p[len(p)-3], p[len(p)-2]}
+}
+
+// addPathReversed adds q reversed to p.
+// For example, if q consists of a linear segment from A to B followed by a
+// quadratic segment from B to C to D, then the values of q looks like:
+// index: 01234567890123
+// value: 0AA01BB12CCDD2
+// So, when adding q backwards to p, we want to Add2(C, B) followed by Add1(A).
+func addPathReversed(p Adder, q Path) {
+ if len(q) == 0 {
+ return
+ }
+ i := len(q) - 1
+ for {
+ switch q[i] {
+ case 0:
+ return
+ case 1:
+ i -= 4
+ p.Add1(
+ fixed.Point26_6{q[i-2], q[i-1]},
+ )
+ case 2:
+ i -= 6
+ p.Add2(
+ fixed.Point26_6{q[i+2], q[i+3]},
+ fixed.Point26_6{q[i-2], q[i-1]},
+ )
+ case 3:
+ i -= 8
+ p.Add3(
+ fixed.Point26_6{q[i+4], q[i+5]},
+ fixed.Point26_6{q[i+2], q[i+3]},
+ fixed.Point26_6{q[i-2], q[i-1]},
+ )
+ default:
+ panic("freetype/raster: bad path")
+ }
+ }
+}
diff --git a/vendor/github.com/golang/freetype/raster/paint.go b/vendor/github.com/golang/freetype/raster/paint.go
new file mode 100644
index 000000000..652256cca
--- /dev/null
+++ b/vendor/github.com/golang/freetype/raster/paint.go
@@ -0,0 +1,287 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package raster
+
+import (
+ "image"
+ "image/color"
+ "image/draw"
+ "math"
+)
+
+// A Span is a horizontal segment of pixels with constant alpha. X0 is an
+// inclusive bound and X1 is exclusive, the same as for slices. A fully opaque
+// Span has Alpha == 0xffff.
+type Span struct {
+ Y, X0, X1 int
+ Alpha uint32
+}
+
+// A Painter knows how to paint a batch of Spans. Rasterization may involve
+// Painting multiple batches, and done will be true for the final batch. The
+// Spans' Y values are monotonically increasing during a rasterization. Paint
+// may use all of ss as scratch space during the call.
+type Painter interface {
+ Paint(ss []Span, done bool)
+}
+
+// The PainterFunc type adapts an ordinary function to the Painter interface.
+type PainterFunc func(ss []Span, done bool)
+
+// Paint just delegates the call to f.
+func (f PainterFunc) Paint(ss []Span, done bool) { f(ss, done) }
+
+// An AlphaOverPainter is a Painter that paints Spans onto a *image.Alpha using
+// the Over Porter-Duff composition operator.
+type AlphaOverPainter struct {
+ Image *image.Alpha
+}
+
+// Paint satisfies the Painter interface.
+func (r AlphaOverPainter) Paint(ss []Span, done bool) {
+ b := r.Image.Bounds()
+ for _, s := range ss {
+ if s.Y < b.Min.Y {
+ continue
+ }
+ if s.Y >= b.Max.Y {
+ return
+ }
+ if s.X0 < b.Min.X {
+ s.X0 = b.Min.X
+ }
+ if s.X1 > b.Max.X {
+ s.X1 = b.Max.X
+ }
+ if s.X0 >= s.X1 {
+ continue
+ }
+ base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
+ p := r.Image.Pix[base+s.X0 : base+s.X1]
+ a := int(s.Alpha >> 8)
+ for i, c := range p {
+ v := int(c)
+ p[i] = uint8((v*255 + (255-v)*a) / 255)
+ }
+ }
+}
+
+// NewAlphaOverPainter creates a new AlphaOverPainter for the given image.
+func NewAlphaOverPainter(m *image.Alpha) AlphaOverPainter {
+ return AlphaOverPainter{m}
+}
+
+// An AlphaSrcPainter is a Painter that paints Spans onto a *image.Alpha using
+// the Src Porter-Duff composition operator.
+type AlphaSrcPainter struct {
+ Image *image.Alpha
+}
+
+// Paint satisfies the Painter interface.
+func (r AlphaSrcPainter) Paint(ss []Span, done bool) {
+ b := r.Image.Bounds()
+ for _, s := range ss {
+ if s.Y < b.Min.Y {
+ continue
+ }
+ if s.Y >= b.Max.Y {
+ return
+ }
+ if s.X0 < b.Min.X {
+ s.X0 = b.Min.X
+ }
+ if s.X1 > b.Max.X {
+ s.X1 = b.Max.X
+ }
+ if s.X0 >= s.X1 {
+ continue
+ }
+ base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
+ p := r.Image.Pix[base+s.X0 : base+s.X1]
+ color := uint8(s.Alpha >> 8)
+ for i := range p {
+ p[i] = color
+ }
+ }
+}
+
+// NewAlphaSrcPainter creates a new AlphaSrcPainter for the given image.
+func NewAlphaSrcPainter(m *image.Alpha) AlphaSrcPainter {
+ return AlphaSrcPainter{m}
+}
+
+// An RGBAPainter is a Painter that paints Spans onto a *image.RGBA.
+type RGBAPainter struct {
+ // Image is the image to compose onto.
+ Image *image.RGBA
+ // Op is the Porter-Duff composition operator.
+ Op draw.Op
+ // cr, cg, cb and ca are the 16-bit color to paint the spans.
+ cr, cg, cb, ca uint32
+}
+
+// Paint satisfies the Painter interface.
+func (r *RGBAPainter) Paint(ss []Span, done bool) {
+ b := r.Image.Bounds()
+ for _, s := range ss {
+ if s.Y < b.Min.Y {
+ continue
+ }
+ if s.Y >= b.Max.Y {
+ return
+ }
+ if s.X0 < b.Min.X {
+ s.X0 = b.Min.X
+ }
+ if s.X1 > b.Max.X {
+ s.X1 = b.Max.X
+ }
+ if s.X0 >= s.X1 {
+ continue
+ }
+ // This code mimics drawGlyphOver in $GOROOT/src/image/draw/draw.go.
+ ma := s.Alpha
+ const m = 1<<16 - 1
+ i0 := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride + (s.X0-r.Image.Rect.Min.X)*4
+ i1 := i0 + (s.X1-s.X0)*4
+ if r.Op == draw.Over {
+ for i := i0; i < i1; i += 4 {
+ dr := uint32(r.Image.Pix[i+0])
+ dg := uint32(r.Image.Pix[i+1])
+ db := uint32(r.Image.Pix[i+2])
+ da := uint32(r.Image.Pix[i+3])
+ a := (m - (r.ca * ma / m)) * 0x101
+ r.Image.Pix[i+0] = uint8((dr*a + r.cr*ma) / m >> 8)
+ r.Image.Pix[i+1] = uint8((dg*a + r.cg*ma) / m >> 8)
+ r.Image.Pix[i+2] = uint8((db*a + r.cb*ma) / m >> 8)
+ r.Image.Pix[i+3] = uint8((da*a + r.ca*ma) / m >> 8)
+ }
+ } else {
+ for i := i0; i < i1; i += 4 {
+ r.Image.Pix[i+0] = uint8(r.cr * ma / m >> 8)
+ r.Image.Pix[i+1] = uint8(r.cg * ma / m >> 8)
+ r.Image.Pix[i+2] = uint8(r.cb * ma / m >> 8)
+ r.Image.Pix[i+3] = uint8(r.ca * ma / m >> 8)
+ }
+ }
+ }
+}
+
+// SetColor sets the color to paint the spans.
+func (r *RGBAPainter) SetColor(c color.Color) {
+ r.cr, r.cg, r.cb, r.ca = c.RGBA()
+}
+
+// NewRGBAPainter creates a new RGBAPainter for the given image.
+func NewRGBAPainter(m *image.RGBA) *RGBAPainter {
+ return &RGBAPainter{Image: m}
+}
+
+// A MonochromePainter wraps another Painter, quantizing each Span's alpha to
+// be either fully opaque or fully transparent.
+type MonochromePainter struct {
+ Painter Painter
+ y, x0, x1 int
+}
+
+// Paint delegates to the wrapped Painter after quantizing each Span's alpha
+// value and merging adjacent fully opaque Spans.
+func (m *MonochromePainter) Paint(ss []Span, done bool) {
+ // We compact the ss slice, discarding any Spans whose alpha quantizes to zero.
+ j := 0
+ for _, s := range ss {
+ if s.Alpha >= 0x8000 {
+ if m.y == s.Y && m.x1 == s.X0 {
+ m.x1 = s.X1
+ } else {
+ ss[j] = Span{m.y, m.x0, m.x1, 1<<16 - 1}
+ j++
+ m.y, m.x0, m.x1 = s.Y, s.X0, s.X1
+ }
+ }
+ }
+ if done {
+ // Flush the accumulated Span.
+ finalSpan := Span{m.y, m.x0, m.x1, 1<<16 - 1}
+ if j < len(ss) {
+ ss[j] = finalSpan
+ j++
+ m.Painter.Paint(ss[:j], true)
+ } else if j == len(ss) {
+ m.Painter.Paint(ss, false)
+ if cap(ss) > 0 {
+ ss = ss[:1]
+ } else {
+ ss = make([]Span, 1)
+ }
+ ss[0] = finalSpan
+ m.Painter.Paint(ss, true)
+ } else {
+ panic("unreachable")
+ }
+ // Reset the accumulator, so that this Painter can be re-used.
+ m.y, m.x0, m.x1 = 0, 0, 0
+ } else {
+ m.Painter.Paint(ss[:j], false)
+ }
+}
+
+// NewMonochromePainter creates a new MonochromePainter that wraps the given
+// Painter.
+func NewMonochromePainter(p Painter) *MonochromePainter {
+ return &MonochromePainter{Painter: p}
+}
+
+// A GammaCorrectionPainter wraps another Painter, performing gamma-correction
+// on each Span's alpha value.
+type GammaCorrectionPainter struct {
+ // Painter is the wrapped Painter.
+ Painter Painter
+ // a is the precomputed alpha values for linear interpolation, with fully
+ // opaque == 0xffff.
+ a [256]uint16
+ // gammaIsOne is whether gamma correction is a no-op.
+ gammaIsOne bool
+}
+
+// Paint delegates to the wrapped Painter after performing gamma-correction on
+// each Span.
+func (g *GammaCorrectionPainter) Paint(ss []Span, done bool) {
+ if !g.gammaIsOne {
+ const n = 0x101
+ for i, s := range ss {
+ if s.Alpha == 0 || s.Alpha == 0xffff {
+ continue
+ }
+ p, q := s.Alpha/n, s.Alpha%n
+ // The resultant alpha is a linear interpolation of g.a[p] and g.a[p+1].
+ a := uint32(g.a[p])*(n-q) + uint32(g.a[p+1])*q
+ ss[i].Alpha = (a + n/2) / n
+ }
+ }
+ g.Painter.Paint(ss, done)
+}
+
+// SetGamma sets the gamma value.
+func (g *GammaCorrectionPainter) SetGamma(gamma float64) {
+ g.gammaIsOne = gamma == 1
+ if g.gammaIsOne {
+ return
+ }
+ for i := 0; i < 256; i++ {
+ a := float64(i) / 0xff
+ a = math.Pow(a, gamma)
+ g.a[i] = uint16(0xffff * a)
+ }
+}
+
+// NewGammaCorrectionPainter creates a new GammaCorrectionPainter that wraps
+// the given Painter.
+func NewGammaCorrectionPainter(p Painter, gamma float64) *GammaCorrectionPainter {
+ g := &GammaCorrectionPainter{Painter: p}
+ g.SetGamma(gamma)
+ return g
+}
diff --git a/vendor/github.com/golang/freetype/raster/raster.go b/vendor/github.com/golang/freetype/raster/raster.go
new file mode 100644
index 000000000..995925e2a
--- /dev/null
+++ b/vendor/github.com/golang/freetype/raster/raster.go
@@ -0,0 +1,601 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+// Package raster provides an anti-aliasing 2-D rasterizer.
+//
+// It is part of the larger Freetype suite of font-related packages, but the
+// raster package is not specific to font rasterization, and can be used
+// standalone without any other Freetype package.
+//
+// Rasterization is done by the same area/coverage accumulation algorithm as
+// the Freetype "smooth" module, and the Anti-Grain Geometry library. A
+// description of the area/coverage algorithm is at
+// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm
+package raster
+
+import (
+ "strconv"
+
+ "golang.org/x/image/math/fixed"
+)
+
+// A cell is part of a linked list (for a given yi co-ordinate) of accumulated
+// area/coverage for the pixel at (xi, yi).
+type cell struct {
+ xi int
+ area, cover int
+ next int
+}
+
+type Rasterizer struct {
+ // If false, the default behavior is to use the even-odd winding fill
+ // rule during Rasterize.
+ UseNonZeroWinding bool
+ // An offset (in pixels) to the painted spans.
+ Dx, Dy int
+
+ // The width of the Rasterizer. The height is implicit in len(cellIndex).
+ width int
+ // splitScaleN is the scaling factor used to determine how many times
+ // to decompose a quadratic or cubic segment into a linear approximation.
+ splitScale2, splitScale3 int
+
+ // The current pen position.
+ a fixed.Point26_6
+ // The current cell and its area/coverage being accumulated.
+ xi, yi int
+ area, cover int
+
+ // Saved cells.
+ cell []cell
+ // Linked list of cells, one per row.
+ cellIndex []int
+ // Buffers.
+ cellBuf [256]cell
+ cellIndexBuf [64]int
+ spanBuf [64]Span
+}
+
+// findCell returns the index in r.cell for the cell corresponding to
+// (r.xi, r.yi). The cell is created if necessary.
+func (r *Rasterizer) findCell() int {
+ if r.yi < 0 || r.yi >= len(r.cellIndex) {
+ return -1
+ }
+ xi := r.xi
+ if xi < 0 {
+ xi = -1
+ } else if xi > r.width {
+ xi = r.width
+ }
+ i, prev := r.cellIndex[r.yi], -1
+ for i != -1 && r.cell[i].xi <= xi {
+ if r.cell[i].xi == xi {
+ return i
+ }
+ i, prev = r.cell[i].next, i
+ }
+ c := len(r.cell)
+ if c == cap(r.cell) {
+ buf := make([]cell, c, 4*c)
+ copy(buf, r.cell)
+ r.cell = buf[0 : c+1]
+ } else {
+ r.cell = r.cell[0 : c+1]
+ }
+ r.cell[c] = cell{xi, 0, 0, i}
+ if prev == -1 {
+ r.cellIndex[r.yi] = c
+ } else {
+ r.cell[prev].next = c
+ }
+ return c
+}
+
+// saveCell saves any accumulated r.area/r.cover for (r.xi, r.yi).
+func (r *Rasterizer) saveCell() {
+ if r.area != 0 || r.cover != 0 {
+ i := r.findCell()
+ if i != -1 {
+ r.cell[i].area += r.area
+ r.cell[i].cover += r.cover
+ }
+ r.area = 0
+ r.cover = 0
+ }
+}
+
+// setCell sets the (xi, yi) cell that r is accumulating area/coverage for.
+func (r *Rasterizer) setCell(xi, yi int) {
+ if r.xi != xi || r.yi != yi {
+ r.saveCell()
+ r.xi, r.yi = xi, yi
+ }
+}
+
+// scan accumulates area/coverage for the yi'th scanline, going from
+// x0 to x1 in the horizontal direction (in 26.6 fixed point co-ordinates)
+// and from y0f to y1f fractional vertical units within that scanline.
+func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f fixed.Int26_6) {
+ // Break the 26.6 fixed point X co-ordinates into integral and fractional parts.
+ x0i := int(x0) / 64
+ x0f := x0 - fixed.Int26_6(64*x0i)
+ x1i := int(x1) / 64
+ x1f := x1 - fixed.Int26_6(64*x1i)
+
+ // A perfectly horizontal scan.
+ if y0f == y1f {
+ r.setCell(x1i, yi)
+ return
+ }
+ dx, dy := x1-x0, y1f-y0f
+ // A single cell scan.
+ if x0i == x1i {
+ r.area += int((x0f + x1f) * dy)
+ r.cover += int(dy)
+ return
+ }
+ // There are at least two cells. Apart from the first and last cells,
+ // all intermediate cells go through the full width of the cell,
+ // or 64 units in 26.6 fixed point format.
+ var (
+ p, q, edge0, edge1 fixed.Int26_6
+ xiDelta int
+ )
+ if dx > 0 {
+ p, q = (64-x0f)*dy, dx
+ edge0, edge1, xiDelta = 0, 64, 1
+ } else {
+ p, q = x0f*dy, -dx
+ edge0, edge1, xiDelta = 64, 0, -1
+ }
+ yDelta, yRem := p/q, p%q
+ if yRem < 0 {
+ yDelta -= 1
+ yRem += q
+ }
+ // Do the first cell.
+ xi, y := x0i, y0f
+ r.area += int((x0f + edge1) * yDelta)
+ r.cover += int(yDelta)
+ xi, y = xi+xiDelta, y+yDelta
+ r.setCell(xi, yi)
+ if xi != x1i {
+ // Do all the intermediate cells.
+ p = 64 * (y1f - y + yDelta)
+ fullDelta, fullRem := p/q, p%q
+ if fullRem < 0 {
+ fullDelta -= 1
+ fullRem += q
+ }
+ yRem -= q
+ for xi != x1i {
+ yDelta = fullDelta
+ yRem += fullRem
+ if yRem >= 0 {
+ yDelta += 1
+ yRem -= q
+ }
+ r.area += int(64 * yDelta)
+ r.cover += int(yDelta)
+ xi, y = xi+xiDelta, y+yDelta
+ r.setCell(xi, yi)
+ }
+ }
+ // Do the last cell.
+ yDelta = y1f - y
+ r.area += int((edge0 + x1f) * yDelta)
+ r.cover += int(yDelta)
+}
+
+// Start starts a new curve at the given point.
+func (r *Rasterizer) Start(a fixed.Point26_6) {
+ r.setCell(int(a.X/64), int(a.Y/64))
+ r.a = a
+}
+
+// Add1 adds a linear segment to the current curve.
+func (r *Rasterizer) Add1(b fixed.Point26_6) {
+ x0, y0 := r.a.X, r.a.Y
+ x1, y1 := b.X, b.Y
+ dx, dy := x1-x0, y1-y0
+ // Break the 26.6 fixed point Y co-ordinates into integral and fractional
+ // parts.
+ y0i := int(y0) / 64
+ y0f := y0 - fixed.Int26_6(64*y0i)
+ y1i := int(y1) / 64
+ y1f := y1 - fixed.Int26_6(64*y1i)
+
+ if y0i == y1i {
+ // There is only one scanline.
+ r.scan(y0i, x0, y0f, x1, y1f)
+
+ } else if dx == 0 {
+ // This is a vertical line segment. We avoid calling r.scan and instead
+ // manipulate r.area and r.cover directly.
+ var (
+ edge0, edge1 fixed.Int26_6
+ yiDelta int
+ )
+ if dy > 0 {
+ edge0, edge1, yiDelta = 0, 64, 1
+ } else {
+ edge0, edge1, yiDelta = 64, 0, -1
+ }
+ x0i, yi := int(x0)/64, y0i
+ x0fTimes2 := (int(x0) - (64 * x0i)) * 2
+ // Do the first pixel.
+ dcover := int(edge1 - y0f)
+ darea := int(x0fTimes2 * dcover)
+ r.area += darea
+ r.cover += dcover
+ yi += yiDelta
+ r.setCell(x0i, yi)
+ // Do all the intermediate pixels.
+ dcover = int(edge1 - edge0)
+ darea = int(x0fTimes2 * dcover)
+ for yi != y1i {
+ r.area += darea
+ r.cover += dcover
+ yi += yiDelta
+ r.setCell(x0i, yi)
+ }
+ // Do the last pixel.
+ dcover = int(y1f - edge0)
+ darea = int(x0fTimes2 * dcover)
+ r.area += darea
+ r.cover += dcover
+
+ } else {
+ // There are at least two scanlines. Apart from the first and last
+ // scanlines, all intermediate scanlines go through the full height of
+ // the row, or 64 units in 26.6 fixed point format.
+ var (
+ p, q, edge0, edge1 fixed.Int26_6
+ yiDelta int
+ )
+ if dy > 0 {
+ p, q = (64-y0f)*dx, dy
+ edge0, edge1, yiDelta = 0, 64, 1
+ } else {
+ p, q = y0f*dx, -dy
+ edge0, edge1, yiDelta = 64, 0, -1
+ }
+ xDelta, xRem := p/q, p%q
+ if xRem < 0 {
+ xDelta -= 1
+ xRem += q
+ }
+ // Do the first scanline.
+ x, yi := x0, y0i
+ r.scan(yi, x, y0f, x+xDelta, edge1)
+ x, yi = x+xDelta, yi+yiDelta
+ r.setCell(int(x)/64, yi)
+ if yi != y1i {
+ // Do all the intermediate scanlines.
+ p = 64 * dx
+ fullDelta, fullRem := p/q, p%q
+ if fullRem < 0 {
+ fullDelta -= 1
+ fullRem += q
+ }
+ xRem -= q
+ for yi != y1i {
+ xDelta = fullDelta
+ xRem += fullRem
+ if xRem >= 0 {
+ xDelta += 1
+ xRem -= q
+ }
+ r.scan(yi, x, edge0, x+xDelta, edge1)
+ x, yi = x+xDelta, yi+yiDelta
+ r.setCell(int(x)/64, yi)
+ }
+ }
+ // Do the last scanline.
+ r.scan(yi, x, edge0, x1, y1f)
+ }
+ // The next lineTo starts from b.
+ r.a = b
+}
+
+// Add2 adds a quadratic segment to the current curve.
+func (r *Rasterizer) Add2(b, c fixed.Point26_6) {
+ // Calculate nSplit (the number of recursive decompositions) based on how
+ // 'curvy' it is. Specifically, how much the middle point b deviates from
+ // (a+c)/2.
+ dev := maxAbs(r.a.X-2*b.X+c.X, r.a.Y-2*b.Y+c.Y) / fixed.Int26_6(r.splitScale2)
+ nsplit := 0
+ for dev > 0 {
+ dev /= 4
+ nsplit++
+ }
+ // dev is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit
+ // is 16.
+ const maxNsplit = 16
+ if nsplit > maxNsplit {
+ panic("freetype/raster: Add2 nsplit too large: " + strconv.Itoa(nsplit))
+ }
+ // Recursively decompose the curve nSplit levels deep.
+ var (
+ pStack [2*maxNsplit + 3]fixed.Point26_6
+ sStack [maxNsplit + 1]int
+ i int
+ )
+ sStack[0] = nsplit
+ pStack[0] = c
+ pStack[1] = b
+ pStack[2] = r.a
+ for i >= 0 {
+ s := sStack[i]
+ p := pStack[2*i:]
+ if s > 0 {
+ // Split the quadratic curve p[:3] into an equivalent set of two
+ // shorter curves: p[:3] and p[2:5]. The new p[4] is the old p[2],
+ // and p[0] is unchanged.
+ mx := p[1].X
+ p[4].X = p[2].X
+ p[3].X = (p[4].X + mx) / 2
+ p[1].X = (p[0].X + mx) / 2
+ p[2].X = (p[1].X + p[3].X) / 2
+ my := p[1].Y
+ p[4].Y = p[2].Y
+ p[3].Y = (p[4].Y + my) / 2
+ p[1].Y = (p[0].Y + my) / 2
+ p[2].Y = (p[1].Y + p[3].Y) / 2
+ // The two shorter curves have one less split to do.
+ sStack[i] = s - 1
+ sStack[i+1] = s - 1
+ i++
+ } else {
+ // Replace the level-0 quadratic with a two-linear-piece
+ // approximation.
+ midx := (p[0].X + 2*p[1].X + p[2].X) / 4
+ midy := (p[0].Y + 2*p[1].Y + p[2].Y) / 4
+ r.Add1(fixed.Point26_6{midx, midy})
+ r.Add1(p[0])
+ i--
+ }
+ }
+}
+
+// Add3 adds a cubic segment to the current curve.
+func (r *Rasterizer) Add3(b, c, d fixed.Point26_6) {
+ // Calculate nSplit (the number of recursive decompositions) based on how
+ // 'curvy' it is.
+ dev2 := maxAbs(r.a.X-3*(b.X+c.X)+d.X, r.a.Y-3*(b.Y+c.Y)+d.Y) / fixed.Int26_6(r.splitScale2)
+ dev3 := maxAbs(r.a.X-2*b.X+d.X, r.a.Y-2*b.Y+d.Y) / fixed.Int26_6(r.splitScale3)
+ nsplit := 0
+ for dev2 > 0 || dev3 > 0 {
+ dev2 /= 8
+ dev3 /= 4
+ nsplit++
+ }
+ // devN is 32-bit, and nsplit++ every time we shift off 2 bits, so
+ // maxNsplit is 16.
+ const maxNsplit = 16
+ if nsplit > maxNsplit {
+ panic("freetype/raster: Add3 nsplit too large: " + strconv.Itoa(nsplit))
+ }
+ // Recursively decompose the curve nSplit levels deep.
+ var (
+ pStack [3*maxNsplit + 4]fixed.Point26_6
+ sStack [maxNsplit + 1]int
+ i int
+ )
+ sStack[0] = nsplit
+ pStack[0] = d
+ pStack[1] = c
+ pStack[2] = b
+ pStack[3] = r.a
+ for i >= 0 {
+ s := sStack[i]
+ p := pStack[3*i:]
+ if s > 0 {
+ // Split the cubic curve p[:4] into an equivalent set of two
+ // shorter curves: p[:4] and p[3:7]. The new p[6] is the old p[3],
+ // and p[0] is unchanged.
+ m01x := (p[0].X + p[1].X) / 2
+ m12x := (p[1].X + p[2].X) / 2
+ m23x := (p[2].X + p[3].X) / 2
+ p[6].X = p[3].X
+ p[5].X = m23x
+ p[1].X = m01x
+ p[2].X = (m01x + m12x) / 2
+ p[4].X = (m12x + m23x) / 2
+ p[3].X = (p[2].X + p[4].X) / 2
+ m01y := (p[0].Y + p[1].Y) / 2
+ m12y := (p[1].Y + p[2].Y) / 2
+ m23y := (p[2].Y + p[3].Y) / 2
+ p[6].Y = p[3].Y
+ p[5].Y = m23y
+ p[1].Y = m01y
+ p[2].Y = (m01y + m12y) / 2
+ p[4].Y = (m12y + m23y) / 2
+ p[3].Y = (p[2].Y + p[4].Y) / 2
+ // The two shorter curves have one less split to do.
+ sStack[i] = s - 1
+ sStack[i+1] = s - 1
+ i++
+ } else {
+ // Replace the level-0 cubic with a two-linear-piece approximation.
+ midx := (p[0].X + 3*(p[1].X+p[2].X) + p[3].X) / 8
+ midy := (p[0].Y + 3*(p[1].Y+p[2].Y) + p[3].Y) / 8
+ r.Add1(fixed.Point26_6{midx, midy})
+ r.Add1(p[0])
+ i--
+ }
+ }
+}
+
+// AddPath adds the given Path.
+func (r *Rasterizer) AddPath(p Path) {
+ for i := 0; i < len(p); {
+ switch p[i] {
+ case 0:
+ r.Start(
+ fixed.Point26_6{p[i+1], p[i+2]},
+ )
+ i += 4
+ case 1:
+ r.Add1(
+ fixed.Point26_6{p[i+1], p[i+2]},
+ )
+ i += 4
+ case 2:
+ r.Add2(
+ fixed.Point26_6{p[i+1], p[i+2]},
+ fixed.Point26_6{p[i+3], p[i+4]},
+ )
+ i += 6
+ case 3:
+ r.Add3(
+ fixed.Point26_6{p[i+1], p[i+2]},
+ fixed.Point26_6{p[i+3], p[i+4]},
+ fixed.Point26_6{p[i+5], p[i+6]},
+ )
+ i += 8
+ default:
+ panic("freetype/raster: bad path")
+ }
+ }
+}
+
+// AddStroke adds a stroked Path.
+func (r *Rasterizer) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
+ Stroke(r, q, width, cr, jr)
+}
+
+// areaToAlpha converts an area value to a uint32 alpha value. A completely
+// filled pixel corresponds to an area of 64*64*2, and an alpha of 0xffff. The
+// conversion of area values greater than this depends on the winding rule:
+// even-odd or non-zero.
+func (r *Rasterizer) areaToAlpha(area int) uint32 {
+ // The C Freetype implementation (version 2.3.12) does "alpha := area>>1"
+ // without the +1. Round-to-nearest gives a more symmetric result than
+ // round-down. The C implementation also returns 8-bit alpha, not 16-bit
+ // alpha.
+ a := (area + 1) >> 1
+ if a < 0 {
+ a = -a
+ }
+ alpha := uint32(a)
+ if r.UseNonZeroWinding {
+ if alpha > 0x0fff {
+ alpha = 0x0fff
+ }
+ } else {
+ alpha &= 0x1fff
+ if alpha > 0x1000 {
+ alpha = 0x2000 - alpha
+ } else if alpha == 0x1000 {
+ alpha = 0x0fff
+ }
+ }
+ // alpha is now in the range [0x0000, 0x0fff]. Convert that 12-bit alpha to
+ // 16-bit alpha.
+ return alpha<<4 | alpha>>8
+}
+
+// Rasterize converts r's accumulated curves into Spans for p. The Spans passed
+// to p are non-overlapping, and sorted by Y and then X. They all have non-zero
+// width (and 0 <= X0 < X1 <= r.width) and non-zero A, except for the final
+// Span, which has Y, X0, X1 and A all equal to zero.
+func (r *Rasterizer) Rasterize(p Painter) {
+ r.saveCell()
+ s := 0
+ for yi := 0; yi < len(r.cellIndex); yi++ {
+ xi, cover := 0, 0
+ for c := r.cellIndex[yi]; c != -1; c = r.cell[c].next {
+ if cover != 0 && r.cell[c].xi > xi {
+ alpha := r.areaToAlpha(cover * 64 * 2)
+ if alpha != 0 {
+ xi0, xi1 := xi, r.cell[c].xi
+ if xi0 < 0 {
+ xi0 = 0
+ }
+ if xi1 >= r.width {
+ xi1 = r.width
+ }
+ if xi0 < xi1 {
+ r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
+ s++
+ }
+ }
+ }
+ cover += r.cell[c].cover
+ alpha := r.areaToAlpha(cover*64*2 - r.cell[c].area)
+ xi = r.cell[c].xi + 1
+ if alpha != 0 {
+ xi0, xi1 := r.cell[c].xi, xi
+ if xi0 < 0 {
+ xi0 = 0
+ }
+ if xi1 >= r.width {
+ xi1 = r.width
+ }
+ if xi0 < xi1 {
+ r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
+ s++
+ }
+ }
+ if s > len(r.spanBuf)-2 {
+ p.Paint(r.spanBuf[:s], false)
+ s = 0
+ }
+ }
+ }
+ p.Paint(r.spanBuf[:s], true)
+}
+
+// Clear cancels any previous calls to r.Start or r.AddXxx.
+func (r *Rasterizer) Clear() {
+ r.a = fixed.Point26_6{}
+ r.xi = 0
+ r.yi = 0
+ r.area = 0
+ r.cover = 0
+ r.cell = r.cell[:0]
+ for i := 0; i < len(r.cellIndex); i++ {
+ r.cellIndex[i] = -1
+ }
+}
+
+// SetBounds sets the maximum width and height of the rasterized image and
+// calls Clear. The width and height are in pixels, not fixed.Int26_6 units.
+func (r *Rasterizer) SetBounds(width, height int) {
+ if width < 0 {
+ width = 0
+ }
+ if height < 0 {
+ height = 0
+ }
+ // Use the same ssN heuristic as the C Freetype (version 2.4.0)
+ // implementation.
+ ss2, ss3 := 32, 16
+ if width > 24 || height > 24 {
+ ss2, ss3 = 2*ss2, 2*ss3
+ if width > 120 || height > 120 {
+ ss2, ss3 = 2*ss2, 2*ss3
+ }
+ }
+ r.width = width
+ r.splitScale2 = ss2
+ r.splitScale3 = ss3
+ r.cell = r.cellBuf[:0]
+ if height > len(r.cellIndexBuf) {
+ r.cellIndex = make([]int, height)
+ } else {
+ r.cellIndex = r.cellIndexBuf[:height]
+ }
+ r.Clear()
+}
+
+// NewRasterizer creates a new Rasterizer with the given bounds.
+func NewRasterizer(width, height int) *Rasterizer {
+ r := new(Rasterizer)
+ r.SetBounds(width, height)
+ return r
+}
diff --git a/vendor/github.com/golang/freetype/raster/stroke.go b/vendor/github.com/golang/freetype/raster/stroke.go
new file mode 100644
index 000000000..bcc66b268
--- /dev/null
+++ b/vendor/github.com/golang/freetype/raster/stroke.go
@@ -0,0 +1,483 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package raster
+
+import (
+ "golang.org/x/image/math/fixed"
+)
+
+// Two points are considered practically equal if the square of the distance
+// between them is less than one quarter (i.e. 1024 / 4096).
+const epsilon = fixed.Int52_12(1024)
+
+// A Capper signifies how to begin or end a stroked path.
+type Capper interface {
+ // Cap adds a cap to p given a pivot point and the normal vector of a
+ // terminal segment. The normal's length is half of the stroke width.
+ Cap(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6)
+}
+
+// The CapperFunc type adapts an ordinary function to be a Capper.
+type CapperFunc func(Adder, fixed.Int26_6, fixed.Point26_6, fixed.Point26_6)
+
+func (f CapperFunc) Cap(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
+ f(p, halfWidth, pivot, n1)
+}
+
+// A Joiner signifies how to join interior nodes of a stroked path.
+type Joiner interface {
+ // Join adds a join to the two sides of a stroked path given a pivot
+ // point and the normal vectors of the trailing and leading segments.
+ // Both normals have length equal to half of the stroke width.
+ Join(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6)
+}
+
+// The JoinerFunc type adapts an ordinary function to be a Joiner.
+type JoinerFunc func(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6)
+
+func (f JoinerFunc) Join(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
+ f(lhs, rhs, halfWidth, pivot, n0, n1)
+}
+
+// RoundCapper adds round caps to a stroked path.
+var RoundCapper Capper = CapperFunc(roundCapper)
+
+func roundCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
+ // The cubic Bézier approximation to a circle involves the magic number
+ // (√2 - 1) * 4/3, which is approximately 35/64.
+ const k = 35
+ e := pRot90CCW(n1)
+ side := pivot.Add(e)
+ start, end := pivot.Sub(n1), pivot.Add(n1)
+ d, e := n1.Mul(k), e.Mul(k)
+ p.Add3(start.Add(e), side.Sub(d), side)
+ p.Add3(side.Add(d), end.Add(e), end)
+}
+
+// ButtCapper adds butt caps to a stroked path.
+var ButtCapper Capper = CapperFunc(buttCapper)
+
+func buttCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
+ p.Add1(pivot.Add(n1))
+}
+
+// SquareCapper adds square caps to a stroked path.
+var SquareCapper Capper = CapperFunc(squareCapper)
+
+func squareCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
+ e := pRot90CCW(n1)
+ side := pivot.Add(e)
+ p.Add1(side.Sub(n1))
+ p.Add1(side.Add(n1))
+ p.Add1(pivot.Add(n1))
+}
+
+// RoundJoiner adds round joins to a stroked path.
+var RoundJoiner Joiner = JoinerFunc(roundJoiner)
+
+func roundJoiner(lhs, rhs Adder, haflWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
+ dot := pDot(pRot90CW(n0), n1)
+ if dot >= 0 {
+ addArc(lhs, pivot, n0, n1)
+ rhs.Add1(pivot.Sub(n1))
+ } else {
+ lhs.Add1(pivot.Add(n1))
+ addArc(rhs, pivot, pNeg(n0), pNeg(n1))
+ }
+}
+
+// BevelJoiner adds bevel joins to a stroked path.
+var BevelJoiner Joiner = JoinerFunc(bevelJoiner)
+
+func bevelJoiner(lhs, rhs Adder, haflWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
+ lhs.Add1(pivot.Add(n1))
+ rhs.Add1(pivot.Sub(n1))
+}
+
+// addArc adds a circular arc from pivot+n0 to pivot+n1 to p. The shorter of
+// the two possible arcs is taken, i.e. the one spanning <= 180 degrees. The
+// two vectors n0 and n1 must be of equal length.
+func addArc(p Adder, pivot, n0, n1 fixed.Point26_6) {
+ // r2 is the square of the length of n0.
+ r2 := pDot(n0, n0)
+ if r2 < epsilon {
+ // The arc radius is so small that we collapse to a straight line.
+ p.Add1(pivot.Add(n1))
+ return
+ }
+ // We approximate the arc by 0, 1, 2 or 3 45-degree quadratic segments plus
+ // a final quadratic segment from s to n1. Each 45-degree segment has
+ // control points {1, 0}, {1, tan(π/8)} and {1/√2, 1/√2} suitably scaled,
+ // rotated and translated. tan(π/8) is approximately 27/64.
+ const tpo8 = 27
+ var s fixed.Point26_6
+ // We determine which octant the angle between n0 and n1 is in via three
+ // dot products. m0, m1 and m2 are n0 rotated clockwise by 45, 90 and 135
+ // degrees.
+ m0 := pRot45CW(n0)
+ m1 := pRot90CW(n0)
+ m2 := pRot90CW(m0)
+ if pDot(m1, n1) >= 0 {
+ if pDot(n0, n1) >= 0 {
+ if pDot(m2, n1) <= 0 {
+ // n1 is between 0 and 45 degrees clockwise of n0.
+ s = n0
+ } else {
+ // n1 is between 45 and 90 degrees clockwise of n0.
+ p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
+ s = m0
+ }
+ } else {
+ pm1, n0t := pivot.Add(m1), n0.Mul(tpo8)
+ p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
+ p.Add2(pm1.Add(n0t), pm1)
+ if pDot(m0, n1) >= 0 {
+ // n1 is between 90 and 135 degrees clockwise of n0.
+ s = m1
+ } else {
+ // n1 is between 135 and 180 degrees clockwise of n0.
+ p.Add2(pm1.Sub(n0t), pivot.Add(m2))
+ s = m2
+ }
+ }
+ } else {
+ if pDot(n0, n1) >= 0 {
+ if pDot(m0, n1) >= 0 {
+ // n1 is between 0 and 45 degrees counter-clockwise of n0.
+ s = n0
+ } else {
+ // n1 is between 45 and 90 degrees counter-clockwise of n0.
+ p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
+ s = pNeg(m2)
+ }
+ } else {
+ pm1, n0t := pivot.Sub(m1), n0.Mul(tpo8)
+ p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
+ p.Add2(pm1.Add(n0t), pm1)
+ if pDot(m2, n1) <= 0 {
+ // n1 is between 90 and 135 degrees counter-clockwise of n0.
+ s = pNeg(m1)
+ } else {
+ // n1 is between 135 and 180 degrees counter-clockwise of n0.
+ p.Add2(pm1.Sub(n0t), pivot.Sub(m0))
+ s = pNeg(m0)
+ }
+ }
+ }
+ // The final quadratic segment has two endpoints s and n1 and the middle
+ // control point is a multiple of s.Add(n1), i.e. it is on the angle
+ // bisector of those two points. The multiple ranges between 128/256 and
+ // 150/256 as the angle between s and n1 ranges between 0 and 45 degrees.
+ //
+ // When the angle is 0 degrees (i.e. s and n1 are coincident) then
+ // s.Add(n1) is twice s and so the middle control point of the degenerate
+ // quadratic segment should be half s.Add(n1), and half = 128/256.
+ //
+ // When the angle is 45 degrees then 150/256 is the ratio of the lengths of
+ // the two vectors {1, tan(π/8)} and {1 + 1/√2, 1/√2}.
+ //
+ // d is the normalized dot product between s and n1. Since the angle ranges
+ // between 0 and 45 degrees then d ranges between 256/256 and 181/256.
+ d := 256 * pDot(s, n1) / r2
+ multiple := fixed.Int26_6(150-(150-128)*(d-181)/(256-181)) >> 2
+ p.Add2(pivot.Add(s.Add(n1).Mul(multiple)), pivot.Add(n1))
+}
+
+// midpoint returns the midpoint of two Points.
+func midpoint(a, b fixed.Point26_6) fixed.Point26_6 {
+ return fixed.Point26_6{(a.X + b.X) / 2, (a.Y + b.Y) / 2}
+}
+
+// angleGreaterThan45 returns whether the angle between two vectors is more
+// than 45 degrees.
+func angleGreaterThan45(v0, v1 fixed.Point26_6) bool {
+ v := pRot45CCW(v0)
+ return pDot(v, v1) < 0 || pDot(pRot90CW(v), v1) < 0
+}
+
+// interpolate returns the point (1-t)*a + t*b.
+func interpolate(a, b fixed.Point26_6, t fixed.Int52_12) fixed.Point26_6 {
+ s := 1<<12 - t
+ x := s*fixed.Int52_12(a.X) + t*fixed.Int52_12(b.X)
+ y := s*fixed.Int52_12(a.Y) + t*fixed.Int52_12(b.Y)
+ return fixed.Point26_6{fixed.Int26_6(x >> 12), fixed.Int26_6(y >> 12)}
+}
+
+// curviest2 returns the value of t for which the quadratic parametric curve
+// (1-t)²*a + 2*t*(1-t).b + t²*c has maximum curvature.
+//
+// The curvature of the parametric curve f(t) = (x(t), y(t)) is
+// |x′y″-y′x″| / (x′²+y′²)^(3/2).
+//
+// Let d = b-a and e = c-2*b+a, so that f′(t) = 2*d+2*e*t and f″(t) = 2*e.
+// The curvature's numerator is (2*dx+2*ex*t)*(2*ey)-(2*dy+2*ey*t)*(2*ex),
+// which simplifies to 4*dx*ey-4*dy*ex, which is constant with respect to t.
+//
+// Thus, curvature is extreme where the denominator is extreme, i.e. where
+// (x′²+y′²) is extreme. The first order condition is that
+// 2*x′*x″+2*y′*y″ = 0, or (dx+ex*t)*ex + (dy+ey*t)*ey = 0.
+// Solving for t gives t = -(dx*ex+dy*ey) / (ex*ex+ey*ey).
+func curviest2(a, b, c fixed.Point26_6) fixed.Int52_12 {
+ dx := int64(b.X - a.X)
+ dy := int64(b.Y - a.Y)
+ ex := int64(c.X - 2*b.X + a.X)
+ ey := int64(c.Y - 2*b.Y + a.Y)
+ if ex == 0 && ey == 0 {
+ return 2048
+ }
+ return fixed.Int52_12(-4096 * (dx*ex + dy*ey) / (ex*ex + ey*ey))
+}
+
+// A stroker holds state for stroking a path.
+type stroker struct {
+ // p is the destination that records the stroked path.
+ p Adder
+ // u is the half-width of the stroke.
+ u fixed.Int26_6
+ // cr and jr specify how to end and connect path segments.
+ cr Capper
+ jr Joiner
+ // r is the reverse path. Stroking a path involves constructing two
+ // parallel paths 2*u apart. The first path is added immediately to p,
+ // the second path is accumulated in r and eventually added in reverse.
+ r Path
+ // a is the most recent segment point. anorm is the segment normal of
+ // length u at that point.
+ a, anorm fixed.Point26_6
+}
+
+// addNonCurvy2 adds a quadratic segment to the stroker, where the segment
+// defined by (k.a, b, c) achieves maximum curvature at either k.a or c.
+func (k *stroker) addNonCurvy2(b, c fixed.Point26_6) {
+ // We repeatedly divide the segment at its middle until it is straight
+ // enough to approximate the stroke by just translating the control points.
+ // ds and ps are stacks of depths and points. t is the top of the stack.
+ const maxDepth = 5
+ var (
+ ds [maxDepth + 1]int
+ ps [2*maxDepth + 3]fixed.Point26_6
+ t int
+ )
+ // Initially the ps stack has one quadratic segment of depth zero.
+ ds[0] = 0
+ ps[2] = k.a
+ ps[1] = b
+ ps[0] = c
+ anorm := k.anorm
+ var cnorm fixed.Point26_6
+
+ for {
+ depth := ds[t]
+ a := ps[2*t+2]
+ b := ps[2*t+1]
+ c := ps[2*t+0]
+ ab := b.Sub(a)
+ bc := c.Sub(b)
+ abIsSmall := pDot(ab, ab) < fixed.Int52_12(1<<12)
+ bcIsSmall := pDot(bc, bc) < fixed.Int52_12(1<<12)
+ if abIsSmall && bcIsSmall {
+ // Approximate the segment by a circular arc.
+ cnorm = pRot90CCW(pNorm(bc, k.u))
+ mac := midpoint(a, c)
+ addArc(k.p, mac, anorm, cnorm)
+ addArc(&k.r, mac, pNeg(anorm), pNeg(cnorm))
+ } else if depth < maxDepth && angleGreaterThan45(ab, bc) {
+ // Divide the segment in two and push both halves on the stack.
+ mab := midpoint(a, b)
+ mbc := midpoint(b, c)
+ t++
+ ds[t+0] = depth + 1
+ ds[t-1] = depth + 1
+ ps[2*t+2] = a
+ ps[2*t+1] = mab
+ ps[2*t+0] = midpoint(mab, mbc)
+ ps[2*t-1] = mbc
+ continue
+ } else {
+ // Translate the control points.
+ bnorm := pRot90CCW(pNorm(c.Sub(a), k.u))
+ cnorm = pRot90CCW(pNorm(bc, k.u))
+ k.p.Add2(b.Add(bnorm), c.Add(cnorm))
+ k.r.Add2(b.Sub(bnorm), c.Sub(cnorm))
+ }
+ if t == 0 {
+ k.a, k.anorm = c, cnorm
+ return
+ }
+ t--
+ anorm = cnorm
+ }
+ panic("unreachable")
+}
+
+// Add1 adds a linear segment to the stroker.
+func (k *stroker) Add1(b fixed.Point26_6) {
+ bnorm := pRot90CCW(pNorm(b.Sub(k.a), k.u))
+ if len(k.r) == 0 {
+ k.p.Start(k.a.Add(bnorm))
+ k.r.Start(k.a.Sub(bnorm))
+ } else {
+ k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, bnorm)
+ }
+ k.p.Add1(b.Add(bnorm))
+ k.r.Add1(b.Sub(bnorm))
+ k.a, k.anorm = b, bnorm
+}
+
+// Add2 adds a quadratic segment to the stroker.
+func (k *stroker) Add2(b, c fixed.Point26_6) {
+ ab := b.Sub(k.a)
+ bc := c.Sub(b)
+ abnorm := pRot90CCW(pNorm(ab, k.u))
+ if len(k.r) == 0 {
+ k.p.Start(k.a.Add(abnorm))
+ k.r.Start(k.a.Sub(abnorm))
+ } else {
+ k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, abnorm)
+ }
+
+ // Approximate nearly-degenerate quadratics by linear segments.
+ abIsSmall := pDot(ab, ab) < epsilon
+ bcIsSmall := pDot(bc, bc) < epsilon
+ if abIsSmall || bcIsSmall {
+ acnorm := pRot90CCW(pNorm(c.Sub(k.a), k.u))
+ k.p.Add1(c.Add(acnorm))
+ k.r.Add1(c.Sub(acnorm))
+ k.a, k.anorm = c, acnorm
+ return
+ }
+
+ // The quadratic segment (k.a, b, c) has a point of maximum curvature.
+ // If this occurs at an end point, we process the segment as a whole.
+ t := curviest2(k.a, b, c)
+ if t <= 0 || 4096 <= t {
+ k.addNonCurvy2(b, c)
+ return
+ }
+
+ // Otherwise, we perform a de Casteljau decomposition at the point of
+ // maximum curvature and process the two straighter parts.
+ mab := interpolate(k.a, b, t)
+ mbc := interpolate(b, c, t)
+ mabc := interpolate(mab, mbc, t)
+
+ // If the vectors ab and bc are close to being in opposite directions,
+ // then the decomposition can become unstable, so we approximate the
+ // quadratic segment by two linear segments joined by an arc.
+ bcnorm := pRot90CCW(pNorm(bc, k.u))
+ if pDot(abnorm, bcnorm) < -fixed.Int52_12(k.u)*fixed.Int52_12(k.u)*2047/2048 {
+ pArc := pDot(abnorm, bc) < 0
+
+ k.p.Add1(mabc.Add(abnorm))
+ if pArc {
+ z := pRot90CW(abnorm)
+ addArc(k.p, mabc, abnorm, z)
+ addArc(k.p, mabc, z, bcnorm)
+ }
+ k.p.Add1(mabc.Add(bcnorm))
+ k.p.Add1(c.Add(bcnorm))
+
+ k.r.Add1(mabc.Sub(abnorm))
+ if !pArc {
+ z := pRot90CW(abnorm)
+ addArc(&k.r, mabc, pNeg(abnorm), z)
+ addArc(&k.r, mabc, z, pNeg(bcnorm))
+ }
+ k.r.Add1(mabc.Sub(bcnorm))
+ k.r.Add1(c.Sub(bcnorm))
+
+ k.a, k.anorm = c, bcnorm
+ return
+ }
+
+ // Process the decomposed parts.
+ k.addNonCurvy2(mab, mabc)
+ k.addNonCurvy2(mbc, c)
+}
+
+// Add3 adds a cubic segment to the stroker.
+func (k *stroker) Add3(b, c, d fixed.Point26_6) {
+ panic("freetype/raster: stroke unimplemented for cubic segments")
+}
+
+// stroke adds the stroked Path q to p, where q consists of exactly one curve.
+func (k *stroker) stroke(q Path) {
+ // Stroking is implemented by deriving two paths each k.u apart from q.
+ // The left-hand-side path is added immediately to k.p; the right-hand-side
+ // path is accumulated in k.r. Once we've finished adding the LHS to k.p,
+ // we add the RHS in reverse order.
+ k.r = make(Path, 0, len(q))
+ k.a = fixed.Point26_6{q[1], q[2]}
+ for i := 4; i < len(q); {
+ switch q[i] {
+ case 1:
+ k.Add1(
+ fixed.Point26_6{q[i+1], q[i+2]},
+ )
+ i += 4
+ case 2:
+ k.Add2(
+ fixed.Point26_6{q[i+1], q[i+2]},
+ fixed.Point26_6{q[i+3], q[i+4]},
+ )
+ i += 6
+ case 3:
+ k.Add3(
+ fixed.Point26_6{q[i+1], q[i+2]},
+ fixed.Point26_6{q[i+3], q[i+4]},
+ fixed.Point26_6{q[i+5], q[i+6]},
+ )
+ i += 8
+ default:
+ panic("freetype/raster: bad path")
+ }
+ }
+ if len(k.r) == 0 {
+ return
+ }
+ // TODO(nigeltao): if q is a closed curve then we should join the first and
+ // last segments instead of capping them.
+ k.cr.Cap(k.p, k.u, q.lastPoint(), pNeg(k.anorm))
+ addPathReversed(k.p, k.r)
+ pivot := q.firstPoint()
+ k.cr.Cap(k.p, k.u, pivot, pivot.Sub(fixed.Point26_6{k.r[1], k.r[2]}))
+}
+
+// Stroke adds q stroked with the given width to p. The result is typically
+// self-intersecting and should be rasterized with UseNonZeroWinding.
+// cr and jr may be nil, which defaults to a RoundCapper or RoundJoiner.
+func Stroke(p Adder, q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
+ if len(q) == 0 {
+ return
+ }
+ if cr == nil {
+ cr = RoundCapper
+ }
+ if jr == nil {
+ jr = RoundJoiner
+ }
+ if q[0] != 0 {
+ panic("freetype/raster: bad path")
+ }
+ s := stroker{p: p, u: width / 2, cr: cr, jr: jr}
+ i := 0
+ for j := 4; j < len(q); {
+ switch q[j] {
+ case 0:
+ s.stroke(q[i:j])
+ i, j = j, j+4
+ case 1:
+ j += 4
+ case 2:
+ j += 6
+ case 3:
+ j += 8
+ default:
+ panic("freetype/raster: bad path")
+ }
+ }
+ s.stroke(q[i:])
+}
diff --git a/vendor/github.com/golang/freetype/truetype/face.go b/vendor/github.com/golang/freetype/truetype/face.go
new file mode 100644
index 000000000..099006f05
--- /dev/null
+++ b/vendor/github.com/golang/freetype/truetype/face.go
@@ -0,0 +1,507 @@
+// Copyright 2015 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package truetype
+
+import (
+ "image"
+ "math"
+
+ "github.com/golang/freetype/raster"
+ "golang.org/x/image/font"
+ "golang.org/x/image/math/fixed"
+)
+
+func powerOf2(i int) bool {
+ return i != 0 && (i&(i-1)) == 0
+}
+
+// Options are optional arguments to NewFace.
+type Options struct {
+ // Size is the font size in points, as in "a 10 point font size".
+ //
+ // A zero value means to use a 12 point font size.
+ Size float64
+
+ // DPI is the dots-per-inch resolution.
+ //
+ // A zero value means to use 72 DPI.
+ DPI float64
+
+ // Hinting is how to quantize the glyph nodes.
+ //
+ // A zero value means to use no hinting.
+ Hinting font.Hinting
+
+ // GlyphCacheEntries is the number of entries in the glyph mask image
+ // cache.
+ //
+ // If non-zero, it must be a power of 2.
+ //
+ // A zero value means to use 512 entries.
+ GlyphCacheEntries int
+
+ // SubPixelsX is the number of sub-pixel locations a glyph's dot is
+ // quantized to, in the horizontal direction. For example, a value of 8
+ // means that the dot is quantized to 1/8th of a pixel. This quantization
+ // only affects the glyph mask image, not its bounding box or advance
+ // width. A higher value gives a more faithful glyph image, but reduces the
+ // effectiveness of the glyph cache.
+ //
+ // If non-zero, it must be a power of 2, and be between 1 and 64 inclusive.
+ //
+ // A zero value means to use 4 sub-pixel locations.
+ SubPixelsX int
+
+ // SubPixelsY is the number of sub-pixel locations a glyph's dot is
+ // quantized to, in the vertical direction. For example, a value of 8
+ // means that the dot is quantized to 1/8th of a pixel. This quantization
+ // only affects the glyph mask image, not its bounding box or advance
+ // width. A higher value gives a more faithful glyph image, but reduces the
+ // effectiveness of the glyph cache.
+ //
+ // If non-zero, it must be a power of 2, and be between 1 and 64 inclusive.
+ //
+ // A zero value means to use 1 sub-pixel location.
+ SubPixelsY int
+}
+
+func (o *Options) size() float64 {
+ if o != nil && o.Size > 0 {
+ return o.Size
+ }
+ return 12
+}
+
+func (o *Options) dpi() float64 {
+ if o != nil && o.DPI > 0 {
+ return o.DPI
+ }
+ return 72
+}
+
+func (o *Options) hinting() font.Hinting {
+ if o != nil {
+ switch o.Hinting {
+ case font.HintingVertical, font.HintingFull:
+ // TODO: support vertical hinting.
+ return font.HintingFull
+ }
+ }
+ return font.HintingNone
+}
+
+func (o *Options) glyphCacheEntries() int {
+ if o != nil && powerOf2(o.GlyphCacheEntries) {
+ return o.GlyphCacheEntries
+ }
+ // 512 is 128 * 4 * 1, which lets us cache 128 glyphs at 4 * 1 subpixel
+ // locations in the X and Y direction.
+ return 512
+}
+
+func (o *Options) subPixelsX() (value uint32, halfQuantum, mask fixed.Int26_6) {
+ if o != nil {
+ switch o.SubPixelsX {
+ case 1, 2, 4, 8, 16, 32, 64:
+ return subPixels(o.SubPixelsX)
+ }
+ }
+ // This default value of 4 isn't based on anything scientific, merely as
+ // small a number as possible that looks almost as good as no quantization,
+ // or returning subPixels(64).
+ return subPixels(4)
+}
+
+func (o *Options) subPixelsY() (value uint32, halfQuantum, mask fixed.Int26_6) {
+ if o != nil {
+ switch o.SubPixelsX {
+ case 1, 2, 4, 8, 16, 32, 64:
+ return subPixels(o.SubPixelsX)
+ }
+ }
+ // This default value of 1 isn't based on anything scientific, merely that
+ // vertical sub-pixel glyph rendering is pretty rare. Baseline locations
+ // can usually afford to snap to the pixel grid, so the vertical direction
+ // doesn't have the deal with the horizontal's fractional advance widths.
+ return subPixels(1)
+}
+
+// subPixels returns q and the bias and mask that leads to q quantized
+// sub-pixel locations per full pixel.
+//
+// For example, q == 4 leads to a bias of 8 and a mask of 0xfffffff0, or -16,
+// because we want to round fractions of fixed.Int26_6 as:
+// - 0 to 7 rounds to 0.
+// - 8 to 23 rounds to 16.
+// - 24 to 39 rounds to 32.
+// - 40 to 55 rounds to 48.
+// - 56 to 63 rounds to 64.
+// which means to add 8 and then bitwise-and with -16, in two's complement
+// representation.
+//
+// When q == 1, we want bias == 32 and mask == -64.
+// When q == 2, we want bias == 16 and mask == -32.
+// When q == 4, we want bias == 8 and mask == -16.
+// ...
+// When q == 64, we want bias == 0 and mask == -1. (The no-op case).
+// The pattern is clear.
+func subPixels(q int) (value uint32, bias, mask fixed.Int26_6) {
+ return uint32(q), 32 / fixed.Int26_6(q), -64 / fixed.Int26_6(q)
+}
+
+// glyphCacheEntry caches the arguments and return values of rasterize.
+type glyphCacheEntry struct {
+ key glyphCacheKey
+ val glyphCacheVal
+}
+
+type glyphCacheKey struct {
+ index Index
+ fx, fy uint8
+}
+
+type glyphCacheVal struct {
+ advanceWidth fixed.Int26_6
+ offset image.Point
+ gw int
+ gh int
+}
+
+type indexCacheEntry struct {
+ rune rune
+ index Index
+}
+
+// NewFace returns a new font.Face for the given Font.
+func NewFace(f *Font, opts *Options) font.Face {
+ a := &face{
+ f: f,
+ hinting: opts.hinting(),
+ scale: fixed.Int26_6(0.5 + (opts.size() * opts.dpi() * 64 / 72)),
+ glyphCache: make([]glyphCacheEntry, opts.glyphCacheEntries()),
+ }
+ a.subPixelX, a.subPixelBiasX, a.subPixelMaskX = opts.subPixelsX()
+ a.subPixelY, a.subPixelBiasY, a.subPixelMaskY = opts.subPixelsY()
+
+ // Fill the cache with invalid entries. Valid glyph cache entries have fx
+ // and fy in the range [0, 64). Valid index cache entries have rune >= 0.
+ for i := range a.glyphCache {
+ a.glyphCache[i].key.fy = 0xff
+ }
+ for i := range a.indexCache {
+ a.indexCache[i].rune = -1
+ }
+
+ // Set the rasterizer's bounds to be big enough to handle the largest glyph.
+ b := f.Bounds(a.scale)
+ xmin := +int(b.Min.X) >> 6
+ ymin := -int(b.Max.Y) >> 6
+ xmax := +int(b.Max.X+63) >> 6
+ ymax := -int(b.Min.Y-63) >> 6
+ a.maxw = xmax - xmin
+ a.maxh = ymax - ymin
+ a.masks = image.NewAlpha(image.Rect(0, 0, a.maxw, a.maxh*len(a.glyphCache)))
+ a.r.SetBounds(a.maxw, a.maxh)
+ a.p = facePainter{a}
+
+ return a
+}
+
+type face struct {
+ f *Font
+ hinting font.Hinting
+ scale fixed.Int26_6
+ subPixelX uint32
+ subPixelBiasX fixed.Int26_6
+ subPixelMaskX fixed.Int26_6
+ subPixelY uint32
+ subPixelBiasY fixed.Int26_6
+ subPixelMaskY fixed.Int26_6
+ masks *image.Alpha
+ glyphCache []glyphCacheEntry
+ r raster.Rasterizer
+ p raster.Painter
+ paintOffset int
+ maxw int
+ maxh int
+ glyphBuf GlyphBuf
+ indexCache [indexCacheLen]indexCacheEntry
+
+ // TODO: clip rectangle?
+}
+
+const indexCacheLen = 256
+
+func (a *face) index(r rune) Index {
+ const mask = indexCacheLen - 1
+ c := &a.indexCache[r&mask]
+ if c.rune == r {
+ return c.index
+ }
+ i := a.f.Index(r)
+ c.rune = r
+ c.index = i
+ return i
+}
+
+// Close satisfies the font.Face interface.
+func (a *face) Close() error { return nil }
+
+// Metrics satisfies the font.Face interface.
+func (a *face) Metrics() font.Metrics {
+ scale := float64(a.scale)
+ fupe := float64(a.f.FUnitsPerEm())
+ return font.Metrics{
+ Height: a.scale,
+ Ascent: fixed.Int26_6(math.Ceil(scale * float64(+a.f.ascent) / fupe)),
+ Descent: fixed.Int26_6(math.Ceil(scale * float64(-a.f.descent) / fupe)),
+ }
+}
+
+// Kern satisfies the font.Face interface.
+func (a *face) Kern(r0, r1 rune) fixed.Int26_6 {
+ i0 := a.index(r0)
+ i1 := a.index(r1)
+ kern := a.f.Kern(a.scale, i0, i1)
+ if a.hinting != font.HintingNone {
+ kern = (kern + 32) &^ 63
+ }
+ return kern
+}
+
+// Glyph satisfies the font.Face interface.
+func (a *face) Glyph(dot fixed.Point26_6, r rune) (
+ dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
+
+ // Quantize to the sub-pixel granularity.
+ dotX := (dot.X + a.subPixelBiasX) & a.subPixelMaskX
+ dotY := (dot.Y + a.subPixelBiasY) & a.subPixelMaskY
+
+ // Split the coordinates into their integer and fractional parts.
+ ix, fx := int(dotX>>6), dotX&0x3f
+ iy, fy := int(dotY>>6), dotY&0x3f
+
+ index := a.index(r)
+ cIndex := uint32(index)
+ cIndex = cIndex*a.subPixelX - uint32(fx/a.subPixelMaskX)
+ cIndex = cIndex*a.subPixelY - uint32(fy/a.subPixelMaskY)
+ cIndex &= uint32(len(a.glyphCache) - 1)
+ a.paintOffset = a.maxh * int(cIndex)
+ k := glyphCacheKey{
+ index: index,
+ fx: uint8(fx),
+ fy: uint8(fy),
+ }
+ var v glyphCacheVal
+ if a.glyphCache[cIndex].key != k {
+ var ok bool
+ v, ok = a.rasterize(index, fx, fy)
+ if !ok {
+ return image.Rectangle{}, nil, image.Point{}, 0, false
+ }
+ a.glyphCache[cIndex] = glyphCacheEntry{k, v}
+ } else {
+ v = a.glyphCache[cIndex].val
+ }
+
+ dr.Min = image.Point{
+ X: ix + v.offset.X,
+ Y: iy + v.offset.Y,
+ }
+ dr.Max = image.Point{
+ X: dr.Min.X + v.gw,
+ Y: dr.Min.Y + v.gh,
+ }
+ return dr, a.masks, image.Point{Y: a.paintOffset}, v.advanceWidth, true
+}
+
+func (a *face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
+ if err := a.glyphBuf.Load(a.f, a.scale, a.index(r), a.hinting); err != nil {
+ return fixed.Rectangle26_6{}, 0, false
+ }
+ xmin := +a.glyphBuf.Bounds.Min.X
+ ymin := -a.glyphBuf.Bounds.Max.Y
+ xmax := +a.glyphBuf.Bounds.Max.X
+ ymax := -a.glyphBuf.Bounds.Min.Y
+ if xmin > xmax || ymin > ymax {
+ return fixed.Rectangle26_6{}, 0, false
+ }
+ return fixed.Rectangle26_6{
+ Min: fixed.Point26_6{
+ X: xmin,
+ Y: ymin,
+ },
+ Max: fixed.Point26_6{
+ X: xmax,
+ Y: ymax,
+ },
+ }, a.glyphBuf.AdvanceWidth, true
+}
+
+func (a *face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
+ if err := a.glyphBuf.Load(a.f, a.scale, a.index(r), a.hinting); err != nil {
+ return 0, false
+ }
+ return a.glyphBuf.AdvanceWidth, true
+}
+
+// rasterize returns the advance width, integer-pixel offset to render at, and
+// the width and height of the given glyph at the given sub-pixel offsets.
+//
+// The 26.6 fixed point arguments fx and fy must be in the range [0, 1).
+func (a *face) rasterize(index Index, fx, fy fixed.Int26_6) (v glyphCacheVal, ok bool) {
+ if err := a.glyphBuf.Load(a.f, a.scale, index, a.hinting); err != nil {
+ return glyphCacheVal{}, false
+ }
+ // Calculate the integer-pixel bounds for the glyph.
+ xmin := int(fx+a.glyphBuf.Bounds.Min.X) >> 6
+ ymin := int(fy-a.glyphBuf.Bounds.Max.Y) >> 6
+ xmax := int(fx+a.glyphBuf.Bounds.Max.X+0x3f) >> 6
+ ymax := int(fy-a.glyphBuf.Bounds.Min.Y+0x3f) >> 6
+ if xmin > xmax || ymin > ymax {
+ return glyphCacheVal{}, false
+ }
+ // A TrueType's glyph's nodes can have negative co-ordinates, but the
+ // rasterizer clips anything left of x=0 or above y=0. xmin and ymin are
+ // the pixel offsets, based on the font's FUnit metrics, that let a
+ // negative co-ordinate in TrueType space be non-negative in rasterizer
+ // space. xmin and ymin are typically <= 0.
+ fx -= fixed.Int26_6(xmin << 6)
+ fy -= fixed.Int26_6(ymin << 6)
+ // Rasterize the glyph's vectors.
+ a.r.Clear()
+ pixOffset := a.paintOffset * a.maxw
+ clear(a.masks.Pix[pixOffset : pixOffset+a.maxw*a.maxh])
+ e0 := 0
+ for _, e1 := range a.glyphBuf.Ends {
+ a.drawContour(a.glyphBuf.Points[e0:e1], fx, fy)
+ e0 = e1
+ }
+ a.r.Rasterize(a.p)
+ return glyphCacheVal{
+ a.glyphBuf.AdvanceWidth,
+ image.Point{xmin, ymin},
+ xmax - xmin,
+ ymax - ymin,
+ }, true
+}
+
+func clear(pix []byte) {
+ for i := range pix {
+ pix[i] = 0
+ }
+}
+
+// drawContour draws the given closed contour with the given offset.
+func (a *face) drawContour(ps []Point, dx, dy fixed.Int26_6) {
+ if len(ps) == 0 {
+ return
+ }
+
+ // The low bit of each point's Flags value is whether the point is on the
+ // curve. Truetype fonts only have quadratic Bézier curves, not cubics.
+ // Thus, two consecutive off-curve points imply an on-curve point in the
+ // middle of those two.
+ //
+ // See http://chanae.walon.org/pub/ttf/ttf_glyphs.htm for more details.
+
+ // ps[0] is a truetype.Point measured in FUnits and positive Y going
+ // upwards. start is the same thing measured in fixed point units and
+ // positive Y going downwards, and offset by (dx, dy).
+ start := fixed.Point26_6{
+ X: dx + ps[0].X,
+ Y: dy - ps[0].Y,
+ }
+ var others []Point
+ if ps[0].Flags&0x01 != 0 {
+ others = ps[1:]
+ } else {
+ last := fixed.Point26_6{
+ X: dx + ps[len(ps)-1].X,
+ Y: dy - ps[len(ps)-1].Y,
+ }
+ if ps[len(ps)-1].Flags&0x01 != 0 {
+ start = last
+ others = ps[:len(ps)-1]
+ } else {
+ start = fixed.Point26_6{
+ X: (start.X + last.X) / 2,
+ Y: (start.Y + last.Y) / 2,
+ }
+ others = ps
+ }
+ }
+ a.r.Start(start)
+ q0, on0 := start, true
+ for _, p := range others {
+ q := fixed.Point26_6{
+ X: dx + p.X,
+ Y: dy - p.Y,
+ }
+ on := p.Flags&0x01 != 0
+ if on {
+ if on0 {
+ a.r.Add1(q)
+ } else {
+ a.r.Add2(q0, q)
+ }
+ } else {
+ if on0 {
+ // No-op.
+ } else {
+ mid := fixed.Point26_6{
+ X: (q0.X + q.X) / 2,
+ Y: (q0.Y + q.Y) / 2,
+ }
+ a.r.Add2(q0, mid)
+ }
+ }
+ q0, on0 = q, on
+ }
+ // Close the curve.
+ if on0 {
+ a.r.Add1(start)
+ } else {
+ a.r.Add2(q0, start)
+ }
+}
+
+// facePainter is like a raster.AlphaSrcPainter, with an additional Y offset
+// (face.paintOffset) to the painted spans.
+type facePainter struct {
+ a *face
+}
+
+func (p facePainter) Paint(ss []raster.Span, done bool) {
+ m := p.a.masks
+ b := m.Bounds()
+ b.Min.Y = p.a.paintOffset
+ b.Max.Y = p.a.paintOffset + p.a.maxh
+ for _, s := range ss {
+ s.Y += p.a.paintOffset
+ if s.Y < b.Min.Y {
+ continue
+ }
+ if s.Y >= b.Max.Y {
+ return
+ }
+ if s.X0 < b.Min.X {
+ s.X0 = b.Min.X
+ }
+ if s.X1 > b.Max.X {
+ s.X1 = b.Max.X
+ }
+ if s.X0 >= s.X1 {
+ continue
+ }
+ base := (s.Y-m.Rect.Min.Y)*m.Stride - m.Rect.Min.X
+ p := m.Pix[base+s.X0 : base+s.X1]
+ color := uint8(s.Alpha >> 8)
+ for i := range p {
+ p[i] = color
+ }
+ }
+}
diff --git a/vendor/github.com/golang/freetype/truetype/glyph.go b/vendor/github.com/golang/freetype/truetype/glyph.go
new file mode 100644
index 000000000..c2935a58e
--- /dev/null
+++ b/vendor/github.com/golang/freetype/truetype/glyph.go
@@ -0,0 +1,517 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package truetype
+
+import (
+ "golang.org/x/image/font"
+ "golang.org/x/image/math/fixed"
+)
+
+// TODO: implement VerticalHinting.
+
+// A Point is a co-ordinate pair plus whether it is 'on' a contour or an 'off'
+// control point.
+type Point struct {
+ X, Y fixed.Int26_6
+ // The Flags' LSB means whether or not this Point is 'on' the contour.
+ // Other bits are reserved for internal use.
+ Flags uint32
+}
+
+// A GlyphBuf holds a glyph's contours. A GlyphBuf can be re-used to load a
+// series of glyphs from a Font.
+type GlyphBuf struct {
+ // AdvanceWidth is the glyph's advance width.
+ AdvanceWidth fixed.Int26_6
+ // Bounds is the glyph's bounding box.
+ Bounds fixed.Rectangle26_6
+ // Points contains all Points from all contours of the glyph. If hinting
+ // was used to load a glyph then Unhinted contains those Points before they
+ // were hinted, and InFontUnits contains those Points before they were
+ // hinted and scaled.
+ Points, Unhinted, InFontUnits []Point
+ // Ends is the point indexes of the end point of each contour. The length
+ // of Ends is the number of contours in the glyph. The i'th contour
+ // consists of points Points[Ends[i-1]:Ends[i]], where Ends[-1] is
+ // interpreted to mean zero.
+ Ends []int
+
+ font *Font
+ scale fixed.Int26_6
+ hinting font.Hinting
+ hinter hinter
+ // phantomPoints are the co-ordinates of the synthetic phantom points
+ // used for hinting and bounding box calculations.
+ phantomPoints [4]Point
+ // pp1x is the X co-ordinate of the first phantom point. The '1' is
+ // using 1-based indexing; pp1x is almost always phantomPoints[0].X.
+ // TODO: eliminate this and consistently use phantomPoints[0].X.
+ pp1x fixed.Int26_6
+ // metricsSet is whether the glyph's metrics have been set yet. For a
+ // compound glyph, a sub-glyph may override the outer glyph's metrics.
+ metricsSet bool
+ // tmp is a scratch buffer.
+ tmp []Point
+}
+
+// Flags for decoding a glyph's contours. These flags are documented at
+// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
+const (
+ flagOnCurve = 1 << iota
+ flagXShortVector
+ flagYShortVector
+ flagRepeat
+ flagPositiveXShortVector
+ flagPositiveYShortVector
+
+ // The remaining flags are for internal use.
+ flagTouchedX
+ flagTouchedY
+)
+
+// The same flag bits (0x10 and 0x20) are overloaded to have two meanings,
+// dependent on the value of the flag{X,Y}ShortVector bits.
+const (
+ flagThisXIsSame = flagPositiveXShortVector
+ flagThisYIsSame = flagPositiveYShortVector
+)
+
+// Load loads a glyph's contours from a Font, overwriting any previously loaded
+// contours for this GlyphBuf. scale is the number of 26.6 fixed point units in
+// 1 em, i is the glyph index, and h is the hinting policy.
+func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h font.Hinting) error {
+ g.Points = g.Points[:0]
+ g.Unhinted = g.Unhinted[:0]
+ g.InFontUnits = g.InFontUnits[:0]
+ g.Ends = g.Ends[:0]
+ g.font = f
+ g.hinting = h
+ g.scale = scale
+ g.pp1x = 0
+ g.phantomPoints = [4]Point{}
+ g.metricsSet = false
+
+ if h != font.HintingNone {
+ if err := g.hinter.init(f, scale); err != nil {
+ return err
+ }
+ }
+ if err := g.load(0, i, true); err != nil {
+ return err
+ }
+ // TODO: this selection of either g.pp1x or g.phantomPoints[0].X isn't ideal,
+ // and should be cleaned up once we have all the testScaling tests passing,
+ // plus additional tests for Freetype-Go's bounding boxes matching C Freetype's.
+ pp1x := g.pp1x
+ if h != font.HintingNone {
+ pp1x = g.phantomPoints[0].X
+ }
+ if pp1x != 0 {
+ for i := range g.Points {
+ g.Points[i].X -= pp1x
+ }
+ }
+
+ advanceWidth := g.phantomPoints[1].X - g.phantomPoints[0].X
+ if h != font.HintingNone {
+ if len(f.hdmx) >= 8 {
+ if n := u32(f.hdmx, 4); n > 3+uint32(i) {
+ for hdmx := f.hdmx[8:]; uint32(len(hdmx)) >= n; hdmx = hdmx[n:] {
+ if fixed.Int26_6(hdmx[0]) == scale>>6 {
+ advanceWidth = fixed.Int26_6(hdmx[2+i]) << 6
+ break
+ }
+ }
+ }
+ }
+ advanceWidth = (advanceWidth + 32) &^ 63
+ }
+ g.AdvanceWidth = advanceWidth
+
+ // Set g.Bounds to the 'control box', which is the bounding box of the
+ // Bézier curves' control points. This is easier to calculate, no smaller
+ // than and often equal to the tightest possible bounding box of the curves
+ // themselves. This approach is what C Freetype does. We can't just scale
+ // the nominal bounding box in the glyf data as the hinting process and
+ // phantom point adjustment may move points outside of that box.
+ if len(g.Points) == 0 {
+ g.Bounds = fixed.Rectangle26_6{}
+ } else {
+ p := g.Points[0]
+ g.Bounds.Min.X = p.X
+ g.Bounds.Max.X = p.X
+ g.Bounds.Min.Y = p.Y
+ g.Bounds.Max.Y = p.Y
+ for _, p := range g.Points[1:] {
+ if g.Bounds.Min.X > p.X {
+ g.Bounds.Min.X = p.X
+ } else if g.Bounds.Max.X < p.X {
+ g.Bounds.Max.X = p.X
+ }
+ if g.Bounds.Min.Y > p.Y {
+ g.Bounds.Min.Y = p.Y
+ } else if g.Bounds.Max.Y < p.Y {
+ g.Bounds.Max.Y = p.Y
+ }
+ }
+ // Snap the box to the grid, if hinting is on.
+ if h != font.HintingNone {
+ g.Bounds.Min.X &^= 63
+ g.Bounds.Min.Y &^= 63
+ g.Bounds.Max.X += 63
+ g.Bounds.Max.X &^= 63
+ g.Bounds.Max.Y += 63
+ g.Bounds.Max.Y &^= 63
+ }
+ }
+ return nil
+}
+
+func (g *GlyphBuf) load(recursion uint32, i Index, useMyMetrics bool) (err error) {
+ // The recursion limit here is arbitrary, but defends against malformed glyphs.
+ if recursion >= 32 {
+ return UnsupportedError("excessive compound glyph recursion")
+ }
+ // Find the relevant slice of g.font.glyf.
+ var g0, g1 uint32
+ if g.font.locaOffsetFormat == locaOffsetFormatShort {
+ g0 = 2 * uint32(u16(g.font.loca, 2*int(i)))
+ g1 = 2 * uint32(u16(g.font.loca, 2*int(i)+2))
+ } else {
+ g0 = u32(g.font.loca, 4*int(i))
+ g1 = u32(g.font.loca, 4*int(i)+4)
+ }
+
+ // Decode the contour count and nominal bounding box, from the first
+ // 10 bytes of the glyf data. boundsYMin and boundsXMax, at offsets 4
+ // and 6, are unused.
+ glyf, ne, boundsXMin, boundsYMax := []byte(nil), 0, fixed.Int26_6(0), fixed.Int26_6(0)
+ if g0+10 <= g1 {
+ glyf = g.font.glyf[g0:g1]
+ ne = int(int16(u16(glyf, 0)))
+ boundsXMin = fixed.Int26_6(int16(u16(glyf, 2)))
+ boundsYMax = fixed.Int26_6(int16(u16(glyf, 8)))
+ }
+
+ // Create the phantom points.
+ uhm, pp1x := g.font.unscaledHMetric(i), fixed.Int26_6(0)
+ uvm := g.font.unscaledVMetric(i, boundsYMax)
+ g.phantomPoints = [4]Point{
+ {X: boundsXMin - uhm.LeftSideBearing},
+ {X: boundsXMin - uhm.LeftSideBearing + uhm.AdvanceWidth},
+ {X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing},
+ {X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing - uvm.AdvanceHeight},
+ }
+ if len(glyf) == 0 {
+ g.addPhantomsAndScale(len(g.Points), len(g.Points), true, true)
+ copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
+ g.Points = g.Points[:len(g.Points)-4]
+ return nil
+ }
+
+ // Load and hint the contours.
+ if ne < 0 {
+ if ne != -1 {
+ // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html says that
+ // "the values -2, -3, and so forth, are reserved for future use."
+ return UnsupportedError("negative number of contours")
+ }
+ pp1x = g.font.scale(g.scale * (boundsXMin - uhm.LeftSideBearing))
+ if err := g.loadCompound(recursion, uhm, i, glyf, useMyMetrics); err != nil {
+ return err
+ }
+ } else {
+ np0, ne0 := len(g.Points), len(g.Ends)
+ program := g.loadSimple(glyf, ne)
+ g.addPhantomsAndScale(np0, np0, true, true)
+ pp1x = g.Points[len(g.Points)-4].X
+ if g.hinting != font.HintingNone {
+ if len(program) != 0 {
+ err := g.hinter.run(
+ program,
+ g.Points[np0:],
+ g.Unhinted[np0:],
+ g.InFontUnits[np0:],
+ g.Ends[ne0:],
+ )
+ if err != nil {
+ return err
+ }
+ }
+ // Drop the four phantom points.
+ g.InFontUnits = g.InFontUnits[:len(g.InFontUnits)-4]
+ g.Unhinted = g.Unhinted[:len(g.Unhinted)-4]
+ }
+ if useMyMetrics {
+ copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
+ }
+ g.Points = g.Points[:len(g.Points)-4]
+ if np0 != 0 {
+ // The hinting program expects the []Ends values to be indexed
+ // relative to the inner glyph, not the outer glyph, so we delay
+ // adding np0 until after the hinting program (if any) has run.
+ for i := ne0; i < len(g.Ends); i++ {
+ g.Ends[i] += np0
+ }
+ }
+ }
+ if useMyMetrics && !g.metricsSet {
+ g.metricsSet = true
+ g.pp1x = pp1x
+ }
+ return nil
+}
+
+// loadOffset is the initial offset for loadSimple and loadCompound. The first
+// 10 bytes are the number of contours and the bounding box.
+const loadOffset = 10
+
+func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
+ offset := loadOffset
+ for i := 0; i < ne; i++ {
+ g.Ends = append(g.Ends, 1+int(u16(glyf, offset)))
+ offset += 2
+ }
+
+ // Note the TrueType hinting instructions.
+ instrLen := int(u16(glyf, offset))
+ offset += 2
+ program = glyf[offset : offset+instrLen]
+ offset += instrLen
+
+ np0 := len(g.Points)
+ np1 := np0 + int(g.Ends[len(g.Ends)-1])
+
+ // Decode the flags.
+ for i := np0; i < np1; {
+ c := uint32(glyf[offset])
+ offset++
+ g.Points = append(g.Points, Point{Flags: c})
+ i++
+ if c&flagRepeat != 0 {
+ count := glyf[offset]
+ offset++
+ for ; count > 0; count-- {
+ g.Points = append(g.Points, Point{Flags: c})
+ i++
+ }
+ }
+ }
+
+ // Decode the co-ordinates.
+ var x int16
+ for i := np0; i < np1; i++ {
+ f := g.Points[i].Flags
+ if f&flagXShortVector != 0 {
+ dx := int16(glyf[offset])
+ offset++
+ if f&flagPositiveXShortVector == 0 {
+ x -= dx
+ } else {
+ x += dx
+ }
+ } else if f&flagThisXIsSame == 0 {
+ x += int16(u16(glyf, offset))
+ offset += 2
+ }
+ g.Points[i].X = fixed.Int26_6(x)
+ }
+ var y int16
+ for i := np0; i < np1; i++ {
+ f := g.Points[i].Flags
+ if f&flagYShortVector != 0 {
+ dy := int16(glyf[offset])
+ offset++
+ if f&flagPositiveYShortVector == 0 {
+ y -= dy
+ } else {
+ y += dy
+ }
+ } else if f&flagThisYIsSame == 0 {
+ y += int16(u16(glyf, offset))
+ offset += 2
+ }
+ g.Points[i].Y = fixed.Int26_6(y)
+ }
+
+ return program
+}
+
+func (g *GlyphBuf) loadCompound(recursion uint32, uhm HMetric, i Index,
+ glyf []byte, useMyMetrics bool) error {
+
+ // Flags for decoding a compound glyph. These flags are documented at
+ // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
+ const (
+ flagArg1And2AreWords = 1 << iota
+ flagArgsAreXYValues
+ flagRoundXYToGrid
+ flagWeHaveAScale
+ flagUnused
+ flagMoreComponents
+ flagWeHaveAnXAndYScale
+ flagWeHaveATwoByTwo
+ flagWeHaveInstructions
+ flagUseMyMetrics
+ flagOverlapCompound
+ )
+ np0, ne0 := len(g.Points), len(g.Ends)
+ offset := loadOffset
+ for {
+ flags := u16(glyf, offset)
+ component := Index(u16(glyf, offset+2))
+ dx, dy, transform, hasTransform := fixed.Int26_6(0), fixed.Int26_6(0), [4]int16{}, false
+ if flags&flagArg1And2AreWords != 0 {
+ dx = fixed.Int26_6(int16(u16(glyf, offset+4)))
+ dy = fixed.Int26_6(int16(u16(glyf, offset+6)))
+ offset += 8
+ } else {
+ dx = fixed.Int26_6(int16(int8(glyf[offset+4])))
+ dy = fixed.Int26_6(int16(int8(glyf[offset+5])))
+ offset += 6
+ }
+ if flags&flagArgsAreXYValues == 0 {
+ return UnsupportedError("compound glyph transform vector")
+ }
+ if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 {
+ hasTransform = true
+ switch {
+ case flags&flagWeHaveAScale != 0:
+ transform[0] = int16(u16(glyf, offset+0))
+ transform[3] = transform[0]
+ offset += 2
+ case flags&flagWeHaveAnXAndYScale != 0:
+ transform[0] = int16(u16(glyf, offset+0))
+ transform[3] = int16(u16(glyf, offset+2))
+ offset += 4
+ case flags&flagWeHaveATwoByTwo != 0:
+ transform[0] = int16(u16(glyf, offset+0))
+ transform[1] = int16(u16(glyf, offset+2))
+ transform[2] = int16(u16(glyf, offset+4))
+ transform[3] = int16(u16(glyf, offset+6))
+ offset += 8
+ }
+ }
+ savedPP := g.phantomPoints
+ np0 := len(g.Points)
+ componentUMM := useMyMetrics && (flags&flagUseMyMetrics != 0)
+ if err := g.load(recursion+1, component, componentUMM); err != nil {
+ return err
+ }
+ if flags&flagUseMyMetrics == 0 {
+ g.phantomPoints = savedPP
+ }
+ if hasTransform {
+ for j := np0; j < len(g.Points); j++ {
+ p := &g.Points[j]
+ newX := 0 +
+ fixed.Int26_6((int64(p.X)*int64(transform[0])+1<<13)>>14) +
+ fixed.Int26_6((int64(p.Y)*int64(transform[2])+1<<13)>>14)
+ newY := 0 +
+ fixed.Int26_6((int64(p.X)*int64(transform[1])+1<<13)>>14) +
+ fixed.Int26_6((int64(p.Y)*int64(transform[3])+1<<13)>>14)
+ p.X, p.Y = newX, newY
+ }
+ }
+ dx = g.font.scale(g.scale * dx)
+ dy = g.font.scale(g.scale * dy)
+ if flags&flagRoundXYToGrid != 0 {
+ dx = (dx + 32) &^ 63
+ dy = (dy + 32) &^ 63
+ }
+ for j := np0; j < len(g.Points); j++ {
+ p := &g.Points[j]
+ p.X += dx
+ p.Y += dy
+ }
+ // TODO: also adjust g.InFontUnits and g.Unhinted?
+ if flags&flagMoreComponents == 0 {
+ break
+ }
+ }
+
+ instrLen := 0
+ if g.hinting != font.HintingNone && offset+2 <= len(glyf) {
+ instrLen = int(u16(glyf, offset))
+ offset += 2
+ }
+
+ g.addPhantomsAndScale(np0, len(g.Points), false, instrLen > 0)
+ points, ends := g.Points[np0:], g.Ends[ne0:]
+ g.Points = g.Points[:len(g.Points)-4]
+ for j := range points {
+ points[j].Flags &^= flagTouchedX | flagTouchedY
+ }
+
+ if instrLen == 0 {
+ if !g.metricsSet {
+ copy(g.phantomPoints[:], points[len(points)-4:])
+ }
+ return nil
+ }
+
+ // Hint the compound glyph.
+ program := glyf[offset : offset+instrLen]
+ // Temporarily adjust the ends to be relative to this compound glyph.
+ if np0 != 0 {
+ for i := range ends {
+ ends[i] -= np0
+ }
+ }
+ // Hinting instructions of a composite glyph completely refer to the
+ // (already) hinted subglyphs.
+ g.tmp = append(g.tmp[:0], points...)
+ if err := g.hinter.run(program, points, g.tmp, g.tmp, ends); err != nil {
+ return err
+ }
+ if np0 != 0 {
+ for i := range ends {
+ ends[i] += np0
+ }
+ }
+ if !g.metricsSet {
+ copy(g.phantomPoints[:], points[len(points)-4:])
+ }
+ return nil
+}
+
+func (g *GlyphBuf) addPhantomsAndScale(np0, np1 int, simple, adjust bool) {
+ // Add the four phantom points.
+ g.Points = append(g.Points, g.phantomPoints[:]...)
+ // Scale the points.
+ if simple && g.hinting != font.HintingNone {
+ g.InFontUnits = append(g.InFontUnits, g.Points[np1:]...)
+ }
+ for i := np1; i < len(g.Points); i++ {
+ p := &g.Points[i]
+ p.X = g.font.scale(g.scale * p.X)
+ p.Y = g.font.scale(g.scale * p.Y)
+ }
+ if g.hinting == font.HintingNone {
+ return
+ }
+ // Round the 1st phantom point to the grid, shifting all other points equally.
+ // Note that "all other points" starts from np0, not np1.
+ // TODO: delete this adjustment and the np0/np1 distinction, when
+ // we update the compatibility tests to C Freetype 2.5.3.
+ // See http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=05c786d990390a7ca18e62962641dac740bacb06
+ if adjust {
+ pp1x := g.Points[len(g.Points)-4].X
+ if dx := ((pp1x + 32) &^ 63) - pp1x; dx != 0 {
+ for i := np0; i < len(g.Points); i++ {
+ g.Points[i].X += dx
+ }
+ }
+ }
+ if simple {
+ g.Unhinted = append(g.Unhinted, g.Points[np1:]...)
+ }
+ // Round the 2nd and 4th phantom point to the grid.
+ p := &g.Points[len(g.Points)-3]
+ p.X = (p.X + 32) &^ 63
+ p = &g.Points[len(g.Points)-1]
+ p.Y = (p.Y + 32) &^ 63
+}
diff --git a/vendor/github.com/golang/freetype/truetype/hint.go b/vendor/github.com/golang/freetype/truetype/hint.go
new file mode 100644
index 000000000..0315de511
--- /dev/null
+++ b/vendor/github.com/golang/freetype/truetype/hint.go
@@ -0,0 +1,1763 @@
+// Copyright 2012 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package truetype
+
+// This file implements a Truetype bytecode interpreter.
+// The opcodes are described at https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html
+
+import (
+ "errors"
+ "math"
+
+ "golang.org/x/image/math/fixed"
+)
+
+const (
+ twilightZone = 0
+ glyphZone = 1
+ numZone = 2
+)
+
+type pointType uint32
+
+const (
+ current pointType = 0
+ unhinted pointType = 1
+ inFontUnits pointType = 2
+ numPointType = 3
+)
+
+// callStackEntry is a bytecode call stack entry.
+type callStackEntry struct {
+ program []byte
+ pc int
+ loopCount int32
+}
+
+// hinter implements bytecode hinting. A hinter can be re-used to hint a series
+// of glyphs from a Font.
+type hinter struct {
+ stack, store []int32
+
+ // functions is a map from function number to bytecode.
+ functions map[int32][]byte
+
+ // font and scale are the font and scale last used for this hinter.
+ // Changing the font will require running the new font's fpgm bytecode.
+ // Changing either will require running the font's prep bytecode.
+ font *Font
+ scale fixed.Int26_6
+
+ // gs and defaultGS are the current and default graphics state. The
+ // default graphics state is the global default graphics state after
+ // the font's fpgm and prep programs have been run.
+ gs, defaultGS graphicsState
+
+ // points and ends are the twilight zone's points, glyph's points
+ // and glyph's contour boundaries.
+ points [numZone][numPointType][]Point
+ ends []int
+
+ // scaledCVT is the lazily initialized scaled Control Value Table.
+ scaledCVTInitialized bool
+ scaledCVT []fixed.Int26_6
+}
+
+// graphicsState is described at https://developer.apple.com/fonts/TTRefMan/RM04/Chap4.html
+type graphicsState struct {
+ // Projection vector, freedom vector and dual projection vector.
+ pv, fv, dv [2]f2dot14
+ // Reference points and zone pointers.
+ rp, zp [3]int32
+ // Control Value / Single Width Cut-In.
+ controlValueCutIn, singleWidthCutIn, singleWidth fixed.Int26_6
+ // Delta base / shift.
+ deltaBase, deltaShift int32
+ // Minimum distance.
+ minDist fixed.Int26_6
+ // Loop count.
+ loop int32
+ // Rounding policy.
+ roundPeriod, roundPhase, roundThreshold fixed.Int26_6
+ roundSuper45 bool
+ // Auto-flip.
+ autoFlip bool
+}
+
+var globalDefaultGS = graphicsState{
+ pv: [2]f2dot14{0x4000, 0}, // Unit vector along the X axis.
+ fv: [2]f2dot14{0x4000, 0},
+ dv: [2]f2dot14{0x4000, 0},
+ zp: [3]int32{1, 1, 1},
+ controlValueCutIn: (17 << 6) / 16, // 17/16 as a fixed.Int26_6.
+ deltaBase: 9,
+ deltaShift: 3,
+ minDist: 1 << 6, // 1 as a fixed.Int26_6.
+ loop: 1,
+ roundPeriod: 1 << 6, // 1 as a fixed.Int26_6.
+ roundThreshold: 1 << 5, // 1/2 as a fixed.Int26_6.
+ roundSuper45: false,
+ autoFlip: true,
+}
+
+func resetTwilightPoints(f *Font, p []Point) []Point {
+ if n := int(f.maxTwilightPoints) + 4; n <= cap(p) {
+ p = p[:n]
+ for i := range p {
+ p[i] = Point{}
+ }
+ } else {
+ p = make([]Point, n)
+ }
+ return p
+}
+
+func (h *hinter) init(f *Font, scale fixed.Int26_6) error {
+ h.points[twilightZone][0] = resetTwilightPoints(f, h.points[twilightZone][0])
+ h.points[twilightZone][1] = resetTwilightPoints(f, h.points[twilightZone][1])
+ h.points[twilightZone][2] = resetTwilightPoints(f, h.points[twilightZone][2])
+
+ rescale := h.scale != scale
+ if h.font != f {
+ h.font, rescale = f, true
+ if h.functions == nil {
+ h.functions = make(map[int32][]byte)
+ } else {
+ for k := range h.functions {
+ delete(h.functions, k)
+ }
+ }
+
+ if x := int(f.maxStackElements); x > len(h.stack) {
+ x += 255
+ x &^= 255
+ h.stack = make([]int32, x)
+ }
+ if x := int(f.maxStorage); x > len(h.store) {
+ x += 15
+ x &^= 15
+ h.store = make([]int32, x)
+ }
+ if len(f.fpgm) != 0 {
+ if err := h.run(f.fpgm, nil, nil, nil, nil); err != nil {
+ return err
+ }
+ }
+ }
+
+ if rescale {
+ h.scale = scale
+ h.scaledCVTInitialized = false
+
+ h.defaultGS = globalDefaultGS
+
+ if len(f.prep) != 0 {
+ if err := h.run(f.prep, nil, nil, nil, nil); err != nil {
+ return err
+ }
+ h.defaultGS = h.gs
+ // The MS rasterizer doesn't allow the following graphics state
+ // variables to be modified by the CVT program.
+ h.defaultGS.pv = globalDefaultGS.pv
+ h.defaultGS.fv = globalDefaultGS.fv
+ h.defaultGS.dv = globalDefaultGS.dv
+ h.defaultGS.rp = globalDefaultGS.rp
+ h.defaultGS.zp = globalDefaultGS.zp
+ h.defaultGS.loop = globalDefaultGS.loop
+ }
+ }
+ return nil
+}
+
+func (h *hinter) run(program []byte, pCurrent, pUnhinted, pInFontUnits []Point, ends []int) error {
+ h.gs = h.defaultGS
+ h.points[glyphZone][current] = pCurrent
+ h.points[glyphZone][unhinted] = pUnhinted
+ h.points[glyphZone][inFontUnits] = pInFontUnits
+ h.ends = ends
+
+ if len(program) > 50000 {
+ return errors.New("truetype: hinting: too many instructions")
+ }
+ var (
+ steps, pc, top int
+ opcode uint8
+
+ callStack [32]callStackEntry
+ callStackTop int
+ )
+
+ for 0 <= pc && pc < len(program) {
+ steps++
+ if steps == 100000 {
+ return errors.New("truetype: hinting: too many steps")
+ }
+ opcode = program[pc]
+ if top < int(popCount[opcode]) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ switch opcode {
+
+ case opSVTCA0:
+ h.gs.pv = [2]f2dot14{0, 0x4000}
+ h.gs.fv = [2]f2dot14{0, 0x4000}
+ h.gs.dv = [2]f2dot14{0, 0x4000}
+
+ case opSVTCA1:
+ h.gs.pv = [2]f2dot14{0x4000, 0}
+ h.gs.fv = [2]f2dot14{0x4000, 0}
+ h.gs.dv = [2]f2dot14{0x4000, 0}
+
+ case opSPVTCA0:
+ h.gs.pv = [2]f2dot14{0, 0x4000}
+ h.gs.dv = [2]f2dot14{0, 0x4000}
+
+ case opSPVTCA1:
+ h.gs.pv = [2]f2dot14{0x4000, 0}
+ h.gs.dv = [2]f2dot14{0x4000, 0}
+
+ case opSFVTCA0:
+ h.gs.fv = [2]f2dot14{0, 0x4000}
+
+ case opSFVTCA1:
+ h.gs.fv = [2]f2dot14{0x4000, 0}
+
+ case opSPVTL0, opSPVTL1, opSFVTL0, opSFVTL1:
+ top -= 2
+ p1 := h.point(0, current, h.stack[top+0])
+ p2 := h.point(0, current, h.stack[top+1])
+ if p1 == nil || p2 == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ dx := f2dot14(p1.X - p2.X)
+ dy := f2dot14(p1.Y - p2.Y)
+ if dx == 0 && dy == 0 {
+ dx = 0x4000
+ } else if opcode&1 != 0 {
+ // Counter-clockwise rotation.
+ dx, dy = -dy, dx
+ }
+ v := normalize(dx, dy)
+ if opcode < opSFVTL0 {
+ h.gs.pv = v
+ h.gs.dv = v
+ } else {
+ h.gs.fv = v
+ }
+
+ case opSPVFS:
+ top -= 2
+ h.gs.pv = normalize(f2dot14(h.stack[top]), f2dot14(h.stack[top+1]))
+ h.gs.dv = h.gs.pv
+
+ case opSFVFS:
+ top -= 2
+ h.gs.fv = normalize(f2dot14(h.stack[top]), f2dot14(h.stack[top+1]))
+
+ case opGPV:
+ if top+1 >= len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ h.stack[top+0] = int32(h.gs.pv[0])
+ h.stack[top+1] = int32(h.gs.pv[1])
+ top += 2
+
+ case opGFV:
+ if top+1 >= len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ h.stack[top+0] = int32(h.gs.fv[0])
+ h.stack[top+1] = int32(h.gs.fv[1])
+ top += 2
+
+ case opSFVTPV:
+ h.gs.fv = h.gs.pv
+
+ case opISECT:
+ top -= 5
+ p := h.point(2, current, h.stack[top+0])
+ a0 := h.point(1, current, h.stack[top+1])
+ a1 := h.point(1, current, h.stack[top+2])
+ b0 := h.point(0, current, h.stack[top+3])
+ b1 := h.point(0, current, h.stack[top+4])
+ if p == nil || a0 == nil || a1 == nil || b0 == nil || b1 == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+
+ dbx := b1.X - b0.X
+ dby := b1.Y - b0.Y
+ dax := a1.X - a0.X
+ day := a1.Y - a0.Y
+ dx := b0.X - a0.X
+ dy := b0.Y - a0.Y
+ discriminant := mulDiv(int64(dax), int64(-dby), 0x40) +
+ mulDiv(int64(day), int64(dbx), 0x40)
+ dotProduct := mulDiv(int64(dax), int64(dbx), 0x40) +
+ mulDiv(int64(day), int64(dby), 0x40)
+ // The discriminant above is actually a cross product of vectors
+ // da and db. Together with the dot product, they can be used as
+ // surrogates for sine and cosine of the angle between the vectors.
+ // Indeed,
+ // dotproduct = |da||db|cos(angle)
+ // discriminant = |da||db|sin(angle)
+ // We use these equations to reject grazing intersections by
+ // thresholding abs(tan(angle)) at 1/19, corresponding to 3 degrees.
+ absDisc, absDotP := discriminant, dotProduct
+ if absDisc < 0 {
+ absDisc = -absDisc
+ }
+ if absDotP < 0 {
+ absDotP = -absDotP
+ }
+ if 19*absDisc > absDotP {
+ val := mulDiv(int64(dx), int64(-dby), 0x40) +
+ mulDiv(int64(dy), int64(dbx), 0x40)
+ rx := mulDiv(val, int64(dax), discriminant)
+ ry := mulDiv(val, int64(day), discriminant)
+ p.X = a0.X + fixed.Int26_6(rx)
+ p.Y = a0.Y + fixed.Int26_6(ry)
+ } else {
+ p.X = (a0.X + a1.X + b0.X + b1.X) / 4
+ p.Y = (a0.Y + a1.Y + b0.Y + b1.Y) / 4
+ }
+ p.Flags |= flagTouchedX | flagTouchedY
+
+ case opSRP0, opSRP1, opSRP2:
+ top--
+ h.gs.rp[opcode-opSRP0] = h.stack[top]
+
+ case opSZP0, opSZP1, opSZP2:
+ top--
+ h.gs.zp[opcode-opSZP0] = h.stack[top]
+
+ case opSZPS:
+ top--
+ h.gs.zp[0] = h.stack[top]
+ h.gs.zp[1] = h.stack[top]
+ h.gs.zp[2] = h.stack[top]
+
+ case opSLOOP:
+ top--
+ if h.stack[top] <= 0 {
+ return errors.New("truetype: hinting: invalid data")
+ }
+ h.gs.loop = h.stack[top]
+
+ case opRTG:
+ h.gs.roundPeriod = 1 << 6
+ h.gs.roundPhase = 0
+ h.gs.roundThreshold = 1 << 5
+ h.gs.roundSuper45 = false
+
+ case opRTHG:
+ h.gs.roundPeriod = 1 << 6
+ h.gs.roundPhase = 1 << 5
+ h.gs.roundThreshold = 1 << 5
+ h.gs.roundSuper45 = false
+
+ case opSMD:
+ top--
+ h.gs.minDist = fixed.Int26_6(h.stack[top])
+
+ case opELSE:
+ opcode = 1
+ goto ifelse
+
+ case opJMPR:
+ top--
+ pc += int(h.stack[top])
+ continue
+
+ case opSCVTCI:
+ top--
+ h.gs.controlValueCutIn = fixed.Int26_6(h.stack[top])
+
+ case opSSWCI:
+ top--
+ h.gs.singleWidthCutIn = fixed.Int26_6(h.stack[top])
+
+ case opSSW:
+ top--
+ h.gs.singleWidth = h.font.scale(h.scale * fixed.Int26_6(h.stack[top]))
+
+ case opDUP:
+ if top >= len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ h.stack[top] = h.stack[top-1]
+ top++
+
+ case opPOP:
+ top--
+
+ case opCLEAR:
+ top = 0
+
+ case opSWAP:
+ h.stack[top-1], h.stack[top-2] = h.stack[top-2], h.stack[top-1]
+
+ case opDEPTH:
+ if top >= len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ h.stack[top] = int32(top)
+ top++
+
+ case opCINDEX, opMINDEX:
+ x := int(h.stack[top-1])
+ if x <= 0 || x >= top {
+ return errors.New("truetype: hinting: invalid data")
+ }
+ h.stack[top-1] = h.stack[top-1-x]
+ if opcode == opMINDEX {
+ copy(h.stack[top-1-x:top-1], h.stack[top-x:top])
+ top--
+ }
+
+ case opALIGNPTS:
+ top -= 2
+ p := h.point(1, current, h.stack[top])
+ q := h.point(0, current, h.stack[top+1])
+ if p == nil || q == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ d := dotProduct(fixed.Int26_6(q.X-p.X), fixed.Int26_6(q.Y-p.Y), h.gs.pv) / 2
+ h.move(p, +d, true)
+ h.move(q, -d, true)
+
+ case opUTP:
+ top--
+ p := h.point(0, current, h.stack[top])
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ p.Flags &^= flagTouchedX | flagTouchedY
+
+ case opLOOPCALL, opCALL:
+ if callStackTop >= len(callStack) {
+ return errors.New("truetype: hinting: call stack overflow")
+ }
+ top--
+ f, ok := h.functions[h.stack[top]]
+ if !ok {
+ return errors.New("truetype: hinting: undefined function")
+ }
+ callStack[callStackTop] = callStackEntry{program, pc, 1}
+ if opcode == opLOOPCALL {
+ top--
+ if h.stack[top] == 0 {
+ break
+ }
+ callStack[callStackTop].loopCount = h.stack[top]
+ }
+ callStackTop++
+ program, pc = f, 0
+ continue
+
+ case opFDEF:
+ // Save all bytecode up until the next ENDF.
+ startPC := pc + 1
+ fdefloop:
+ for {
+ pc++
+ if pc >= len(program) {
+ return errors.New("truetype: hinting: unbalanced FDEF")
+ }
+ switch program[pc] {
+ case opFDEF:
+ return errors.New("truetype: hinting: nested FDEF")
+ case opENDF:
+ top--
+ h.functions[h.stack[top]] = program[startPC : pc+1]
+ break fdefloop
+ default:
+ var ok bool
+ pc, ok = skipInstructionPayload(program, pc)
+ if !ok {
+ return errors.New("truetype: hinting: unbalanced FDEF")
+ }
+ }
+ }
+
+ case opENDF:
+ if callStackTop == 0 {
+ return errors.New("truetype: hinting: call stack underflow")
+ }
+ callStackTop--
+ callStack[callStackTop].loopCount--
+ if callStack[callStackTop].loopCount != 0 {
+ callStackTop++
+ pc = 0
+ continue
+ }
+ program, pc = callStack[callStackTop].program, callStack[callStackTop].pc
+
+ case opMDAP0, opMDAP1:
+ top--
+ i := h.stack[top]
+ p := h.point(0, current, i)
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ distance := fixed.Int26_6(0)
+ if opcode == opMDAP1 {
+ distance = dotProduct(p.X, p.Y, h.gs.pv)
+ // TODO: metrics compensation.
+ distance = h.round(distance) - distance
+ }
+ h.move(p, distance, true)
+ h.gs.rp[0] = i
+ h.gs.rp[1] = i
+
+ case opIUP0, opIUP1:
+ iupY, mask := opcode == opIUP0, uint32(flagTouchedX)
+ if iupY {
+ mask = flagTouchedY
+ }
+ prevEnd := 0
+ for _, end := range h.ends {
+ for i := prevEnd; i < end; i++ {
+ for i < end && h.points[glyphZone][current][i].Flags&mask == 0 {
+ i++
+ }
+ if i == end {
+ break
+ }
+ firstTouched, curTouched := i, i
+ i++
+ for ; i < end; i++ {
+ if h.points[glyphZone][current][i].Flags&mask != 0 {
+ h.iupInterp(iupY, curTouched+1, i-1, curTouched, i)
+ curTouched = i
+ }
+ }
+ if curTouched == firstTouched {
+ h.iupShift(iupY, prevEnd, end, curTouched)
+ } else {
+ h.iupInterp(iupY, curTouched+1, end-1, curTouched, firstTouched)
+ if firstTouched > 0 {
+ h.iupInterp(iupY, prevEnd, firstTouched-1, curTouched, firstTouched)
+ }
+ }
+ }
+ prevEnd = end
+ }
+
+ case opSHP0, opSHP1:
+ if top < int(h.gs.loop) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ _, _, d, ok := h.displacement(opcode&1 == 0)
+ if !ok {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ for ; h.gs.loop != 0; h.gs.loop-- {
+ top--
+ p := h.point(2, current, h.stack[top])
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ h.move(p, d, true)
+ }
+ h.gs.loop = 1
+
+ case opSHC0, opSHC1:
+ top--
+ zonePointer, i, d, ok := h.displacement(opcode&1 == 0)
+ if !ok {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ if h.gs.zp[2] == 0 {
+ // TODO: implement this when we have a glyph that does this.
+ return errors.New("hinting: unimplemented SHC instruction")
+ }
+ contour := h.stack[top]
+ if contour < 0 || len(ends) <= int(contour) {
+ return errors.New("truetype: hinting: contour out of range")
+ }
+ j0, j1 := int32(0), int32(h.ends[contour])
+ if contour > 0 {
+ j0 = int32(h.ends[contour-1])
+ }
+ move := h.gs.zp[zonePointer] != h.gs.zp[2]
+ for j := j0; j < j1; j++ {
+ if move || j != i {
+ h.move(h.point(2, current, j), d, true)
+ }
+ }
+
+ case opSHZ0, opSHZ1:
+ top--
+ zonePointer, i, d, ok := h.displacement(opcode&1 == 0)
+ if !ok {
+ return errors.New("truetype: hinting: point out of range")
+ }
+
+ // As per C Freetype, SHZ doesn't move the phantom points, or mark
+ // the points as touched.
+ limit := int32(len(h.points[h.gs.zp[2]][current]))
+ if h.gs.zp[2] == glyphZone {
+ limit -= 4
+ }
+ for j := int32(0); j < limit; j++ {
+ if i != j || h.gs.zp[zonePointer] != h.gs.zp[2] {
+ h.move(h.point(2, current, j), d, false)
+ }
+ }
+
+ case opSHPIX:
+ top--
+ d := fixed.Int26_6(h.stack[top])
+ if top < int(h.gs.loop) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ for ; h.gs.loop != 0; h.gs.loop-- {
+ top--
+ p := h.point(2, current, h.stack[top])
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ h.move(p, d, true)
+ }
+ h.gs.loop = 1
+
+ case opIP:
+ if top < int(h.gs.loop) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ pointType := inFontUnits
+ twilight := h.gs.zp[0] == 0 || h.gs.zp[1] == 0 || h.gs.zp[2] == 0
+ if twilight {
+ pointType = unhinted
+ }
+ p := h.point(1, pointType, h.gs.rp[2])
+ oldP := h.point(0, pointType, h.gs.rp[1])
+ oldRange := dotProduct(p.X-oldP.X, p.Y-oldP.Y, h.gs.dv)
+
+ p = h.point(1, current, h.gs.rp[2])
+ curP := h.point(0, current, h.gs.rp[1])
+ curRange := dotProduct(p.X-curP.X, p.Y-curP.Y, h.gs.pv)
+ for ; h.gs.loop != 0; h.gs.loop-- {
+ top--
+ i := h.stack[top]
+ p = h.point(2, pointType, i)
+ oldDist := dotProduct(p.X-oldP.X, p.Y-oldP.Y, h.gs.dv)
+ p = h.point(2, current, i)
+ curDist := dotProduct(p.X-curP.X, p.Y-curP.Y, h.gs.pv)
+ newDist := fixed.Int26_6(0)
+ if oldDist != 0 {
+ if oldRange != 0 {
+ newDist = fixed.Int26_6(mulDiv(int64(oldDist), int64(curRange), int64(oldRange)))
+ } else {
+ newDist = -oldDist
+ }
+ }
+ h.move(p, newDist-curDist, true)
+ }
+ h.gs.loop = 1
+
+ case opMSIRP0, opMSIRP1:
+ top -= 2
+ i := h.stack[top]
+ distance := fixed.Int26_6(h.stack[top+1])
+
+ // TODO: special case h.gs.zp[1] == 0 in C Freetype.
+ ref := h.point(0, current, h.gs.rp[0])
+ p := h.point(1, current, i)
+ if ref == nil || p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ curDist := dotProduct(p.X-ref.X, p.Y-ref.Y, h.gs.pv)
+
+ // Set-RP0 bit.
+ if opcode == opMSIRP1 {
+ h.gs.rp[0] = i
+ }
+ h.gs.rp[1] = h.gs.rp[0]
+ h.gs.rp[2] = i
+
+ // Move the point.
+ h.move(p, distance-curDist, true)
+
+ case opALIGNRP:
+ if top < int(h.gs.loop) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ ref := h.point(0, current, h.gs.rp[0])
+ if ref == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ for ; h.gs.loop != 0; h.gs.loop-- {
+ top--
+ p := h.point(1, current, h.stack[top])
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ h.move(p, -dotProduct(p.X-ref.X, p.Y-ref.Y, h.gs.pv), true)
+ }
+ h.gs.loop = 1
+
+ case opRTDG:
+ h.gs.roundPeriod = 1 << 5
+ h.gs.roundPhase = 0
+ h.gs.roundThreshold = 1 << 4
+ h.gs.roundSuper45 = false
+
+ case opMIAP0, opMIAP1:
+ top -= 2
+ i := h.stack[top]
+ distance := h.getScaledCVT(h.stack[top+1])
+ if h.gs.zp[0] == 0 {
+ p := h.point(0, unhinted, i)
+ q := h.point(0, current, i)
+ p.X = fixed.Int26_6((int64(distance) * int64(h.gs.fv[0])) >> 14)
+ p.Y = fixed.Int26_6((int64(distance) * int64(h.gs.fv[1])) >> 14)
+ *q = *p
+ }
+ p := h.point(0, current, i)
+ oldDist := dotProduct(p.X, p.Y, h.gs.pv)
+ if opcode == opMIAP1 {
+ if fabs(distance-oldDist) > h.gs.controlValueCutIn {
+ distance = oldDist
+ }
+ // TODO: metrics compensation.
+ distance = h.round(distance)
+ }
+ h.move(p, distance-oldDist, true)
+ h.gs.rp[0] = i
+ h.gs.rp[1] = i
+
+ case opNPUSHB:
+ opcode = 0
+ goto push
+
+ case opNPUSHW:
+ opcode = 0x80
+ goto push
+
+ case opWS:
+ top -= 2
+ i := int(h.stack[top])
+ if i < 0 || len(h.store) <= i {
+ return errors.New("truetype: hinting: invalid data")
+ }
+ h.store[i] = h.stack[top+1]
+
+ case opRS:
+ i := int(h.stack[top-1])
+ if i < 0 || len(h.store) <= i {
+ return errors.New("truetype: hinting: invalid data")
+ }
+ h.stack[top-1] = h.store[i]
+
+ case opWCVTP:
+ top -= 2
+ h.setScaledCVT(h.stack[top], fixed.Int26_6(h.stack[top+1]))
+
+ case opRCVT:
+ h.stack[top-1] = int32(h.getScaledCVT(h.stack[top-1]))
+
+ case opGC0, opGC1:
+ i := h.stack[top-1]
+ if opcode == opGC0 {
+ p := h.point(2, current, i)
+ h.stack[top-1] = int32(dotProduct(p.X, p.Y, h.gs.pv))
+ } else {
+ p := h.point(2, unhinted, i)
+ // Using dv as per C Freetype.
+ h.stack[top-1] = int32(dotProduct(p.X, p.Y, h.gs.dv))
+ }
+
+ case opSCFS:
+ top -= 2
+ i := h.stack[top]
+ p := h.point(2, current, i)
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ c := dotProduct(p.X, p.Y, h.gs.pv)
+ h.move(p, fixed.Int26_6(h.stack[top+1])-c, true)
+ if h.gs.zp[2] != 0 {
+ break
+ }
+ q := h.point(2, unhinted, i)
+ if q == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ q.X = p.X
+ q.Y = p.Y
+
+ case opMD0, opMD1:
+ top--
+ pt, v, scale := pointType(0), [2]f2dot14{}, false
+ if opcode == opMD0 {
+ pt = current
+ v = h.gs.pv
+ } else if h.gs.zp[0] == 0 || h.gs.zp[1] == 0 {
+ pt = unhinted
+ v = h.gs.dv
+ } else {
+ pt = inFontUnits
+ v = h.gs.dv
+ scale = true
+ }
+ p := h.point(0, pt, h.stack[top-1])
+ q := h.point(1, pt, h.stack[top])
+ if p == nil || q == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ d := int32(dotProduct(p.X-q.X, p.Y-q.Y, v))
+ if scale {
+ d = int32(int64(d*int32(h.scale)) / int64(h.font.fUnitsPerEm))
+ }
+ h.stack[top-1] = d
+
+ case opMPPEM, opMPS:
+ if top >= len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ // For MPS, point size should be irrelevant; we return the PPEM.
+ h.stack[top] = int32(h.scale) >> 6
+ top++
+
+ case opFLIPON, opFLIPOFF:
+ h.gs.autoFlip = opcode == opFLIPON
+
+ case opDEBUG:
+ // No-op.
+
+ case opLT:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] < h.stack[top])
+
+ case opLTEQ:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] <= h.stack[top])
+
+ case opGT:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] > h.stack[top])
+
+ case opGTEQ:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] >= h.stack[top])
+
+ case opEQ:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] == h.stack[top])
+
+ case opNEQ:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] != h.stack[top])
+
+ case opODD, opEVEN:
+ i := h.round(fixed.Int26_6(h.stack[top-1])) >> 6
+ h.stack[top-1] = int32(i&1) ^ int32(opcode-opODD)
+
+ case opIF:
+ top--
+ if h.stack[top] == 0 {
+ opcode = 0
+ goto ifelse
+ }
+
+ case opEIF:
+ // No-op.
+
+ case opAND:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] != 0 && h.stack[top] != 0)
+
+ case opOR:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1]|h.stack[top] != 0)
+
+ case opNOT:
+ h.stack[top-1] = bool2int32(h.stack[top-1] == 0)
+
+ case opDELTAP1:
+ goto delta
+
+ case opSDB:
+ top--
+ h.gs.deltaBase = h.stack[top]
+
+ case opSDS:
+ top--
+ h.gs.deltaShift = h.stack[top]
+
+ case opADD:
+ top--
+ h.stack[top-1] += h.stack[top]
+
+ case opSUB:
+ top--
+ h.stack[top-1] -= h.stack[top]
+
+ case opDIV:
+ top--
+ if h.stack[top] == 0 {
+ return errors.New("truetype: hinting: division by zero")
+ }
+ h.stack[top-1] = int32(fdiv(fixed.Int26_6(h.stack[top-1]), fixed.Int26_6(h.stack[top])))
+
+ case opMUL:
+ top--
+ h.stack[top-1] = int32(fmul(fixed.Int26_6(h.stack[top-1]), fixed.Int26_6(h.stack[top])))
+
+ case opABS:
+ if h.stack[top-1] < 0 {
+ h.stack[top-1] = -h.stack[top-1]
+ }
+
+ case opNEG:
+ h.stack[top-1] = -h.stack[top-1]
+
+ case opFLOOR:
+ h.stack[top-1] &^= 63
+
+ case opCEILING:
+ h.stack[top-1] += 63
+ h.stack[top-1] &^= 63
+
+ case opROUND00, opROUND01, opROUND10, opROUND11:
+ // The four flavors of opROUND are equivalent. See the comment below on
+ // opNROUND for the rationale.
+ h.stack[top-1] = int32(h.round(fixed.Int26_6(h.stack[top-1])))
+
+ case opNROUND00, opNROUND01, opNROUND10, opNROUND11:
+ // No-op. The spec says to add one of four "compensations for the engine
+ // characteristics", to cater for things like "different dot-size printers".
+ // https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#engine_compensation
+ // This code does not implement engine compensation, as we don't expect to
+ // be used to output on dot-matrix printers.
+
+ case opWCVTF:
+ top -= 2
+ h.setScaledCVT(h.stack[top], h.font.scale(h.scale*fixed.Int26_6(h.stack[top+1])))
+
+ case opDELTAP2, opDELTAP3, opDELTAC1, opDELTAC2, opDELTAC3:
+ goto delta
+
+ case opSROUND, opS45ROUND:
+ top--
+ switch (h.stack[top] >> 6) & 0x03 {
+ case 0:
+ h.gs.roundPeriod = 1 << 5
+ case 1, 3:
+ h.gs.roundPeriod = 1 << 6
+ case 2:
+ h.gs.roundPeriod = 1 << 7
+ }
+ h.gs.roundSuper45 = opcode == opS45ROUND
+ if h.gs.roundSuper45 {
+ // The spec says to multiply by √2, but the C Freetype code says 1/√2.
+ // We go with 1/√2.
+ h.gs.roundPeriod *= 46341
+ h.gs.roundPeriod /= 65536
+ }
+ h.gs.roundPhase = h.gs.roundPeriod * fixed.Int26_6((h.stack[top]>>4)&0x03) / 4
+ if x := h.stack[top] & 0x0f; x != 0 {
+ h.gs.roundThreshold = h.gs.roundPeriod * fixed.Int26_6(x-4) / 8
+ } else {
+ h.gs.roundThreshold = h.gs.roundPeriod - 1
+ }
+
+ case opJROT:
+ top -= 2
+ if h.stack[top+1] != 0 {
+ pc += int(h.stack[top])
+ continue
+ }
+
+ case opJROF:
+ top -= 2
+ if h.stack[top+1] == 0 {
+ pc += int(h.stack[top])
+ continue
+ }
+
+ case opROFF:
+ h.gs.roundPeriod = 0
+ h.gs.roundPhase = 0
+ h.gs.roundThreshold = 0
+ h.gs.roundSuper45 = false
+
+ case opRUTG:
+ h.gs.roundPeriod = 1 << 6
+ h.gs.roundPhase = 0
+ h.gs.roundThreshold = 1<<6 - 1
+ h.gs.roundSuper45 = false
+
+ case opRDTG:
+ h.gs.roundPeriod = 1 << 6
+ h.gs.roundPhase = 0
+ h.gs.roundThreshold = 0
+ h.gs.roundSuper45 = false
+
+ case opSANGW, opAA:
+ // These ops are "anachronistic" and no longer used.
+ top--
+
+ case opFLIPPT:
+ if top < int(h.gs.loop) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ points := h.points[glyphZone][current]
+ for ; h.gs.loop != 0; h.gs.loop-- {
+ top--
+ i := h.stack[top]
+ if i < 0 || len(points) <= int(i) {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ points[i].Flags ^= flagOnCurve
+ }
+ h.gs.loop = 1
+
+ case opFLIPRGON, opFLIPRGOFF:
+ top -= 2
+ i, j, points := h.stack[top], h.stack[top+1], h.points[glyphZone][current]
+ if i < 0 || len(points) <= int(i) || j < 0 || len(points) <= int(j) {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ for ; i <= j; i++ {
+ if opcode == opFLIPRGON {
+ points[i].Flags |= flagOnCurve
+ } else {
+ points[i].Flags &^= flagOnCurve
+ }
+ }
+
+ case opSCANCTRL:
+ // We do not support dropout control, as we always rasterize grayscale glyphs.
+ top--
+
+ case opSDPVTL0, opSDPVTL1:
+ top -= 2
+ for i := 0; i < 2; i++ {
+ pt := unhinted
+ if i != 0 {
+ pt = current
+ }
+ p := h.point(1, pt, h.stack[top])
+ q := h.point(2, pt, h.stack[top+1])
+ if p == nil || q == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ dx := f2dot14(p.X - q.X)
+ dy := f2dot14(p.Y - q.Y)
+ if dx == 0 && dy == 0 {
+ dx = 0x4000
+ } else if opcode&1 != 0 {
+ // Counter-clockwise rotation.
+ dx, dy = -dy, dx
+ }
+ if i == 0 {
+ h.gs.dv = normalize(dx, dy)
+ } else {
+ h.gs.pv = normalize(dx, dy)
+ }
+ }
+
+ case opGETINFO:
+ res := int32(0)
+ if h.stack[top-1]&(1<<0) != 0 {
+ // Set the engine version. We hard-code this to 35, the same as
+ // the C freetype code, which says that "Version~35 corresponds
+ // to MS rasterizer v.1.7 as used e.g. in Windows~98".
+ res |= 35
+ }
+ if h.stack[top-1]&(1<<5) != 0 {
+ // Set that we support grayscale.
+ res |= 1 << 12
+ }
+ // We set no other bits, as we do not support rotated or stretched glyphs.
+ h.stack[top-1] = res
+
+ case opIDEF:
+ // IDEF is for ancient versions of the bytecode interpreter, and is no longer used.
+ return errors.New("truetype: hinting: unsupported IDEF instruction")
+
+ case opROLL:
+ h.stack[top-1], h.stack[top-3], h.stack[top-2] =
+ h.stack[top-3], h.stack[top-2], h.stack[top-1]
+
+ case opMAX:
+ top--
+ if h.stack[top-1] < h.stack[top] {
+ h.stack[top-1] = h.stack[top]
+ }
+
+ case opMIN:
+ top--
+ if h.stack[top-1] > h.stack[top] {
+ h.stack[top-1] = h.stack[top]
+ }
+
+ case opSCANTYPE:
+ // We do not support dropout control, as we always rasterize grayscale glyphs.
+ top--
+
+ case opINSTCTRL:
+ // TODO: support instruction execution control? It seems rare, and even when
+ // nominally used (e.g. Source Sans Pro), it seems conditional on extreme or
+ // unusual rasterization conditions. For example, the code snippet at
+ // https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#INSTCTRL
+ // uses INSTCTRL when grid-fitting a rotated or stretched glyph, but
+ // freetype-go does not support rotated or stretched glyphs.
+ top -= 2
+
+ default:
+ if opcode < opPUSHB000 {
+ return errors.New("truetype: hinting: unrecognized instruction")
+ }
+
+ if opcode < opMDRP00000 {
+ // PUSHxxxx opcode.
+
+ if opcode < opPUSHW000 {
+ opcode -= opPUSHB000 - 1
+ } else {
+ opcode -= opPUSHW000 - 1 - 0x80
+ }
+ goto push
+ }
+
+ if opcode < opMIRP00000 {
+ // MDRPxxxxx opcode.
+
+ top--
+ i := h.stack[top]
+ ref := h.point(0, current, h.gs.rp[0])
+ p := h.point(1, current, i)
+ if ref == nil || p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+
+ oldDist := fixed.Int26_6(0)
+ if h.gs.zp[0] == 0 || h.gs.zp[1] == 0 {
+ p0 := h.point(1, unhinted, i)
+ p1 := h.point(0, unhinted, h.gs.rp[0])
+ oldDist = dotProduct(p0.X-p1.X, p0.Y-p1.Y, h.gs.dv)
+ } else {
+ p0 := h.point(1, inFontUnits, i)
+ p1 := h.point(0, inFontUnits, h.gs.rp[0])
+ oldDist = dotProduct(p0.X-p1.X, p0.Y-p1.Y, h.gs.dv)
+ oldDist = h.font.scale(h.scale * oldDist)
+ }
+
+ // Single-width cut-in test.
+ if x := fabs(oldDist - h.gs.singleWidth); x < h.gs.singleWidthCutIn {
+ if oldDist >= 0 {
+ oldDist = +h.gs.singleWidth
+ } else {
+ oldDist = -h.gs.singleWidth
+ }
+ }
+
+ // Rounding bit.
+ // TODO: metrics compensation.
+ distance := oldDist
+ if opcode&0x04 != 0 {
+ distance = h.round(oldDist)
+ }
+
+ // Minimum distance bit.
+ if opcode&0x08 != 0 {
+ if oldDist >= 0 {
+ if distance < h.gs.minDist {
+ distance = h.gs.minDist
+ }
+ } else {
+ if distance > -h.gs.minDist {
+ distance = -h.gs.minDist
+ }
+ }
+ }
+
+ // Set-RP0 bit.
+ h.gs.rp[1] = h.gs.rp[0]
+ h.gs.rp[2] = i
+ if opcode&0x10 != 0 {
+ h.gs.rp[0] = i
+ }
+
+ // Move the point.
+ oldDist = dotProduct(p.X-ref.X, p.Y-ref.Y, h.gs.pv)
+ h.move(p, distance-oldDist, true)
+
+ } else {
+ // MIRPxxxxx opcode.
+
+ top -= 2
+ i := h.stack[top]
+ cvtDist := h.getScaledCVT(h.stack[top+1])
+ if fabs(cvtDist-h.gs.singleWidth) < h.gs.singleWidthCutIn {
+ if cvtDist >= 0 {
+ cvtDist = +h.gs.singleWidth
+ } else {
+ cvtDist = -h.gs.singleWidth
+ }
+ }
+
+ if h.gs.zp[1] == 0 {
+ // TODO: implement once we have a .ttf file that triggers
+ // this, so that we can step through C's freetype.
+ return errors.New("truetype: hinting: unimplemented twilight point adjustment")
+ }
+
+ ref := h.point(0, unhinted, h.gs.rp[0])
+ p := h.point(1, unhinted, i)
+ if ref == nil || p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ oldDist := dotProduct(p.X-ref.X, p.Y-ref.Y, h.gs.dv)
+
+ ref = h.point(0, current, h.gs.rp[0])
+ p = h.point(1, current, i)
+ if ref == nil || p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ curDist := dotProduct(p.X-ref.X, p.Y-ref.Y, h.gs.pv)
+
+ if h.gs.autoFlip && oldDist^cvtDist < 0 {
+ cvtDist = -cvtDist
+ }
+
+ // Rounding bit.
+ // TODO: metrics compensation.
+ distance := cvtDist
+ if opcode&0x04 != 0 {
+ // The CVT value is only used if close enough to oldDist.
+ if (h.gs.zp[0] == h.gs.zp[1]) &&
+ (fabs(cvtDist-oldDist) > h.gs.controlValueCutIn) {
+
+ distance = oldDist
+ }
+ distance = h.round(distance)
+ }
+
+ // Minimum distance bit.
+ if opcode&0x08 != 0 {
+ if oldDist >= 0 {
+ if distance < h.gs.minDist {
+ distance = h.gs.minDist
+ }
+ } else {
+ if distance > -h.gs.minDist {
+ distance = -h.gs.minDist
+ }
+ }
+ }
+
+ // Set-RP0 bit.
+ h.gs.rp[1] = h.gs.rp[0]
+ h.gs.rp[2] = i
+ if opcode&0x10 != 0 {
+ h.gs.rp[0] = i
+ }
+
+ // Move the point.
+ h.move(p, distance-curDist, true)
+ }
+ }
+ pc++
+ continue
+
+ ifelse:
+ // Skip past bytecode until the next ELSE (if opcode == 0) or the
+ // next EIF (for all opcodes). Opcode == 0 means that we have come
+ // from an IF. Opcode == 1 means that we have come from an ELSE.
+ {
+ ifelseloop:
+ for depth := 0; ; {
+ pc++
+ if pc >= len(program) {
+ return errors.New("truetype: hinting: unbalanced IF or ELSE")
+ }
+ switch program[pc] {
+ case opIF:
+ depth++
+ case opELSE:
+ if depth == 0 && opcode == 0 {
+ break ifelseloop
+ }
+ case opEIF:
+ depth--
+ if depth < 0 {
+ break ifelseloop
+ }
+ default:
+ var ok bool
+ pc, ok = skipInstructionPayload(program, pc)
+ if !ok {
+ return errors.New("truetype: hinting: unbalanced IF or ELSE")
+ }
+ }
+ }
+ pc++
+ continue
+ }
+
+ push:
+ // Push n elements from the program to the stack, where n is the low 7 bits of
+ // opcode. If the low 7 bits are zero, then n is the next byte from the program.
+ // The high bit being 0 means that the elements are zero-extended bytes.
+ // The high bit being 1 means that the elements are sign-extended words.
+ {
+ width := 1
+ if opcode&0x80 != 0 {
+ opcode &^= 0x80
+ width = 2
+ }
+ if opcode == 0 {
+ pc++
+ if pc >= len(program) {
+ return errors.New("truetype: hinting: insufficient data")
+ }
+ opcode = program[pc]
+ }
+ pc++
+ if top+int(opcode) > len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ if pc+width*int(opcode) > len(program) {
+ return errors.New("truetype: hinting: insufficient data")
+ }
+ for ; opcode > 0; opcode-- {
+ if width == 1 {
+ h.stack[top] = int32(program[pc])
+ } else {
+ h.stack[top] = int32(int8(program[pc]))<<8 | int32(program[pc+1])
+ }
+ top++
+ pc += width
+ }
+ continue
+ }
+
+ delta:
+ {
+ if opcode >= opDELTAC1 && !h.scaledCVTInitialized {
+ h.initializeScaledCVT()
+ }
+ top--
+ n := h.stack[top]
+ if int32(top) < 2*n {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ for ; n > 0; n-- {
+ top -= 2
+ b := h.stack[top]
+ c := (b & 0xf0) >> 4
+ switch opcode {
+ case opDELTAP2, opDELTAC2:
+ c += 16
+ case opDELTAP3, opDELTAC3:
+ c += 32
+ }
+ c += h.gs.deltaBase
+ if ppem := (int32(h.scale) + 1<<5) >> 6; ppem != c {
+ continue
+ }
+ b = (b & 0x0f) - 8
+ if b >= 0 {
+ b++
+ }
+ b = b * 64 / (1 << uint32(h.gs.deltaShift))
+ if opcode >= opDELTAC1 {
+ a := h.stack[top+1]
+ if a < 0 || len(h.scaledCVT) <= int(a) {
+ return errors.New("truetype: hinting: index out of range")
+ }
+ h.scaledCVT[a] += fixed.Int26_6(b)
+ } else {
+ p := h.point(0, current, h.stack[top+1])
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ h.move(p, fixed.Int26_6(b), true)
+ }
+ }
+ pc++
+ continue
+ }
+ }
+ return nil
+}
+
+func (h *hinter) initializeScaledCVT() {
+ h.scaledCVTInitialized = true
+ if n := len(h.font.cvt) / 2; n <= cap(h.scaledCVT) {
+ h.scaledCVT = h.scaledCVT[:n]
+ } else {
+ if n < 32 {
+ n = 32
+ }
+ h.scaledCVT = make([]fixed.Int26_6, len(h.font.cvt)/2, n)
+ }
+ for i := range h.scaledCVT {
+ unscaled := uint16(h.font.cvt[2*i])<<8 | uint16(h.font.cvt[2*i+1])
+ h.scaledCVT[i] = h.font.scale(h.scale * fixed.Int26_6(int16(unscaled)))
+ }
+}
+
+// getScaledCVT returns the scaled value from the font's Control Value Table.
+func (h *hinter) getScaledCVT(i int32) fixed.Int26_6 {
+ if !h.scaledCVTInitialized {
+ h.initializeScaledCVT()
+ }
+ if i < 0 || len(h.scaledCVT) <= int(i) {
+ return 0
+ }
+ return h.scaledCVT[i]
+}
+
+// setScaledCVT overrides the scaled value from the font's Control Value Table.
+func (h *hinter) setScaledCVT(i int32, v fixed.Int26_6) {
+ if !h.scaledCVTInitialized {
+ h.initializeScaledCVT()
+ }
+ if i < 0 || len(h.scaledCVT) <= int(i) {
+ return
+ }
+ h.scaledCVT[i] = v
+}
+
+func (h *hinter) point(zonePointer uint32, pt pointType, i int32) *Point {
+ points := h.points[h.gs.zp[zonePointer]][pt]
+ if i < 0 || len(points) <= int(i) {
+ return nil
+ }
+ return &points[i]
+}
+
+func (h *hinter) move(p *Point, distance fixed.Int26_6, touch bool) {
+ fvx := int64(h.gs.fv[0])
+ pvx := int64(h.gs.pv[0])
+ if fvx == 0x4000 && pvx == 0x4000 {
+ p.X += fixed.Int26_6(distance)
+ if touch {
+ p.Flags |= flagTouchedX
+ }
+ return
+ }
+
+ fvy := int64(h.gs.fv[1])
+ pvy := int64(h.gs.pv[1])
+ if fvy == 0x4000 && pvy == 0x4000 {
+ p.Y += fixed.Int26_6(distance)
+ if touch {
+ p.Flags |= flagTouchedY
+ }
+ return
+ }
+
+ fvDotPv := (fvx*pvx + fvy*pvy) >> 14
+
+ if fvx != 0 {
+ p.X += fixed.Int26_6(mulDiv(fvx, int64(distance), fvDotPv))
+ if touch {
+ p.Flags |= flagTouchedX
+ }
+ }
+
+ if fvy != 0 {
+ p.Y += fixed.Int26_6(mulDiv(fvy, int64(distance), fvDotPv))
+ if touch {
+ p.Flags |= flagTouchedY
+ }
+ }
+}
+
+func (h *hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) {
+ if p1 > p2 {
+ return
+ }
+ if ref1 >= len(h.points[glyphZone][current]) ||
+ ref2 >= len(h.points[glyphZone][current]) {
+ return
+ }
+
+ var ifu1, ifu2 fixed.Int26_6
+ if interpY {
+ ifu1 = h.points[glyphZone][inFontUnits][ref1].Y
+ ifu2 = h.points[glyphZone][inFontUnits][ref2].Y
+ } else {
+ ifu1 = h.points[glyphZone][inFontUnits][ref1].X
+ ifu2 = h.points[glyphZone][inFontUnits][ref2].X
+ }
+ if ifu1 > ifu2 {
+ ifu1, ifu2 = ifu2, ifu1
+ ref1, ref2 = ref2, ref1
+ }
+
+ var unh1, unh2, delta1, delta2 fixed.Int26_6
+ if interpY {
+ unh1 = h.points[glyphZone][unhinted][ref1].Y
+ unh2 = h.points[glyphZone][unhinted][ref2].Y
+ delta1 = h.points[glyphZone][current][ref1].Y - unh1
+ delta2 = h.points[glyphZone][current][ref2].Y - unh2
+ } else {
+ unh1 = h.points[glyphZone][unhinted][ref1].X
+ unh2 = h.points[glyphZone][unhinted][ref2].X
+ delta1 = h.points[glyphZone][current][ref1].X - unh1
+ delta2 = h.points[glyphZone][current][ref2].X - unh2
+ }
+
+ var xy, ifuXY fixed.Int26_6
+ if ifu1 == ifu2 {
+ for i := p1; i <= p2; i++ {
+ if interpY {
+ xy = h.points[glyphZone][unhinted][i].Y
+ } else {
+ xy = h.points[glyphZone][unhinted][i].X
+ }
+
+ if xy <= unh1 {
+ xy += delta1
+ } else {
+ xy += delta2
+ }
+
+ if interpY {
+ h.points[glyphZone][current][i].Y = xy
+ } else {
+ h.points[glyphZone][current][i].X = xy
+ }
+ }
+ return
+ }
+
+ scale, scaleOK := int64(0), false
+ for i := p1; i <= p2; i++ {
+ if interpY {
+ xy = h.points[glyphZone][unhinted][i].Y
+ ifuXY = h.points[glyphZone][inFontUnits][i].Y
+ } else {
+ xy = h.points[glyphZone][unhinted][i].X
+ ifuXY = h.points[glyphZone][inFontUnits][i].X
+ }
+
+ if xy <= unh1 {
+ xy += delta1
+ } else if xy >= unh2 {
+ xy += delta2
+ } else {
+ if !scaleOK {
+ scaleOK = true
+ scale = mulDiv(int64(unh2+delta2-unh1-delta1), 0x10000, int64(ifu2-ifu1))
+ }
+ numer := int64(ifuXY-ifu1) * scale
+ if numer >= 0 {
+ numer += 0x8000
+ } else {
+ numer -= 0x8000
+ }
+ xy = unh1 + delta1 + fixed.Int26_6(numer/0x10000)
+ }
+
+ if interpY {
+ h.points[glyphZone][current][i].Y = xy
+ } else {
+ h.points[glyphZone][current][i].X = xy
+ }
+ }
+}
+
+func (h *hinter) iupShift(interpY bool, p1, p2, p int) {
+ var delta fixed.Int26_6
+ if interpY {
+ delta = h.points[glyphZone][current][p].Y - h.points[glyphZone][unhinted][p].Y
+ } else {
+ delta = h.points[glyphZone][current][p].X - h.points[glyphZone][unhinted][p].X
+ }
+ if delta == 0 {
+ return
+ }
+ for i := p1; i < p2; i++ {
+ if i == p {
+ continue
+ }
+ if interpY {
+ h.points[glyphZone][current][i].Y += delta
+ } else {
+ h.points[glyphZone][current][i].X += delta
+ }
+ }
+}
+
+func (h *hinter) displacement(useZP1 bool) (zonePointer uint32, i int32, d fixed.Int26_6, ok bool) {
+ zonePointer, i = uint32(0), h.gs.rp[1]
+ if useZP1 {
+ zonePointer, i = 1, h.gs.rp[2]
+ }
+ p := h.point(zonePointer, current, i)
+ q := h.point(zonePointer, unhinted, i)
+ if p == nil || q == nil {
+ return 0, 0, 0, false
+ }
+ d = dotProduct(p.X-q.X, p.Y-q.Y, h.gs.pv)
+ return zonePointer, i, d, true
+}
+
+// skipInstructionPayload increments pc by the extra data that follows a
+// variable length PUSHB or PUSHW instruction.
+func skipInstructionPayload(program []byte, pc int) (newPC int, ok bool) {
+ switch program[pc] {
+ case opNPUSHB:
+ pc++
+ if pc >= len(program) {
+ return 0, false
+ }
+ pc += int(program[pc])
+ case opNPUSHW:
+ pc++
+ if pc >= len(program) {
+ return 0, false
+ }
+ pc += 2 * int(program[pc])
+ case opPUSHB000, opPUSHB001, opPUSHB010, opPUSHB011,
+ opPUSHB100, opPUSHB101, opPUSHB110, opPUSHB111:
+ pc += int(program[pc] - (opPUSHB000 - 1))
+ case opPUSHW000, opPUSHW001, opPUSHW010, opPUSHW011,
+ opPUSHW100, opPUSHW101, opPUSHW110, opPUSHW111:
+ pc += 2 * int(program[pc]-(opPUSHW000-1))
+ }
+ return pc, true
+}
+
+// f2dot14 is a 2.14 fixed point number.
+type f2dot14 int16
+
+func normalize(x, y f2dot14) [2]f2dot14 {
+ fx, fy := float64(x), float64(y)
+ l := 0x4000 / math.Hypot(fx, fy)
+ fx *= l
+ if fx >= 0 {
+ fx += 0.5
+ } else {
+ fx -= 0.5
+ }
+ fy *= l
+ if fy >= 0 {
+ fy += 0.5
+ } else {
+ fy -= 0.5
+ }
+ return [2]f2dot14{f2dot14(fx), f2dot14(fy)}
+}
+
+// fabs returns abs(x) in 26.6 fixed point arithmetic.
+func fabs(x fixed.Int26_6) fixed.Int26_6 {
+ if x < 0 {
+ return -x
+ }
+ return x
+}
+
+// fdiv returns x/y in 26.6 fixed point arithmetic.
+func fdiv(x, y fixed.Int26_6) fixed.Int26_6 {
+ return fixed.Int26_6((int64(x) << 6) / int64(y))
+}
+
+// fmul returns x*y in 26.6 fixed point arithmetic.
+func fmul(x, y fixed.Int26_6) fixed.Int26_6 {
+ return fixed.Int26_6((int64(x)*int64(y) + 1<<5) >> 6)
+}
+
+// dotProduct returns the dot product of [x, y] and q. It is almost the same as
+// px := int64(x)
+// py := int64(y)
+// qx := int64(q[0])
+// qy := int64(q[1])
+// return fixed.Int26_6((px*qx + py*qy + 1<<13) >> 14)
+// except that the computation is done with 32-bit integers to produce exactly
+// the same rounding behavior as C Freetype.
+func dotProduct(x, y fixed.Int26_6, q [2]f2dot14) fixed.Int26_6 {
+ // Compute x*q[0] as 64-bit value.
+ l := uint32((int32(x) & 0xFFFF) * int32(q[0]))
+ m := (int32(x) >> 16) * int32(q[0])
+
+ lo1 := l + (uint32(m) << 16)
+ hi1 := (m >> 16) + (int32(l) >> 31) + bool2int32(lo1 < l)
+
+ // Compute y*q[1] as 64-bit value.
+ l = uint32((int32(y) & 0xFFFF) * int32(q[1]))
+ m = (int32(y) >> 16) * int32(q[1])
+
+ lo2 := l + (uint32(m) << 16)
+ hi2 := (m >> 16) + (int32(l) >> 31) + bool2int32(lo2 < l)
+
+ // Add them.
+ lo := lo1 + lo2
+ hi := hi1 + hi2 + bool2int32(lo < lo1)
+
+ // Divide the result by 2^14 with rounding.
+ s := hi >> 31
+ l = lo + uint32(s)
+ hi += s + bool2int32(l < lo)
+ lo = l
+
+ l = lo + 0x2000
+ hi += bool2int32(l < lo)
+
+ return fixed.Int26_6((uint32(hi) << 18) | (l >> 14))
+}
+
+// mulDiv returns x*y/z, rounded to the nearest integer.
+func mulDiv(x, y, z int64) int64 {
+ xy := x * y
+ if z < 0 {
+ xy, z = -xy, -z
+ }
+ if xy >= 0 {
+ xy += z / 2
+ } else {
+ xy -= z / 2
+ }
+ return xy / z
+}
+
+// round rounds the given number. The rounding algorithm is described at
+// https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding
+func (h *hinter) round(x fixed.Int26_6) fixed.Int26_6 {
+ if h.gs.roundPeriod == 0 {
+ // Rounding is off.
+ return x
+ }
+ if x >= 0 {
+ ret := x - h.gs.roundPhase + h.gs.roundThreshold
+ if h.gs.roundSuper45 {
+ ret /= h.gs.roundPeriod
+ ret *= h.gs.roundPeriod
+ } else {
+ ret &= -h.gs.roundPeriod
+ }
+ if x != 0 && ret < 0 {
+ ret = 0
+ }
+ return ret + h.gs.roundPhase
+ }
+ ret := -x - h.gs.roundPhase + h.gs.roundThreshold
+ if h.gs.roundSuper45 {
+ ret /= h.gs.roundPeriod
+ ret *= h.gs.roundPeriod
+ } else {
+ ret &= -h.gs.roundPeriod
+ }
+ if ret < 0 {
+ ret = 0
+ }
+ return -ret - h.gs.roundPhase
+}
+
+func bool2int32(b bool) int32 {
+ if b {
+ return 1
+ }
+ return 0
+}
diff --git a/vendor/github.com/golang/freetype/truetype/opcodes.go b/vendor/github.com/golang/freetype/truetype/opcodes.go
new file mode 100644
index 000000000..1880e1e63
--- /dev/null
+++ b/vendor/github.com/golang/freetype/truetype/opcodes.go
@@ -0,0 +1,289 @@
+// Copyright 2012 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package truetype
+
+// The Truetype opcodes are summarized at
+// https://developer.apple.com/fonts/TTRefMan/RM07/appendixA.html
+
+const (
+ opSVTCA0 = 0x00 // Set freedom and projection Vectors To Coordinate Axis
+ opSVTCA1 = 0x01 // .
+ opSPVTCA0 = 0x02 // Set Projection Vector To Coordinate Axis
+ opSPVTCA1 = 0x03 // .
+ opSFVTCA0 = 0x04 // Set Freedom Vector to Coordinate Axis
+ opSFVTCA1 = 0x05 // .
+ opSPVTL0 = 0x06 // Set Projection Vector To Line
+ opSPVTL1 = 0x07 // .
+ opSFVTL0 = 0x08 // Set Freedom Vector To Line
+ opSFVTL1 = 0x09 // .
+ opSPVFS = 0x0a // Set Projection Vector From Stack
+ opSFVFS = 0x0b // Set Freedom Vector From Stack
+ opGPV = 0x0c // Get Projection Vector
+ opGFV = 0x0d // Get Freedom Vector
+ opSFVTPV = 0x0e // Set Freedom Vector To Projection Vector
+ opISECT = 0x0f // moves point p to the InterSECTion of two lines
+ opSRP0 = 0x10 // Set Reference Point 0
+ opSRP1 = 0x11 // Set Reference Point 1
+ opSRP2 = 0x12 // Set Reference Point 2
+ opSZP0 = 0x13 // Set Zone Pointer 0
+ opSZP1 = 0x14 // Set Zone Pointer 1
+ opSZP2 = 0x15 // Set Zone Pointer 2
+ opSZPS = 0x16 // Set Zone PointerS
+ opSLOOP = 0x17 // Set LOOP variable
+ opRTG = 0x18 // Round To Grid
+ opRTHG = 0x19 // Round To Half Grid
+ opSMD = 0x1a // Set Minimum Distance
+ opELSE = 0x1b // ELSE clause
+ opJMPR = 0x1c // JuMP Relative
+ opSCVTCI = 0x1d // Set Control Value Table Cut-In
+ opSSWCI = 0x1e // Set Single Width Cut-In
+ opSSW = 0x1f // Set Single Width
+ opDUP = 0x20 // DUPlicate top stack element
+ opPOP = 0x21 // POP top stack element
+ opCLEAR = 0x22 // CLEAR the stack
+ opSWAP = 0x23 // SWAP the top two elements on the stack
+ opDEPTH = 0x24 // DEPTH of the stack
+ opCINDEX = 0x25 // Copy the INDEXed element to the top of the stack
+ opMINDEX = 0x26 // Move the INDEXed element to the top of the stack
+ opALIGNPTS = 0x27 // ALIGN PoinTS
+ op_0x28 = 0x28 // deprecated
+ opUTP = 0x29 // UnTouch Point
+ opLOOPCALL = 0x2a // LOOP and CALL function
+ opCALL = 0x2b // CALL function
+ opFDEF = 0x2c // Function DEFinition
+ opENDF = 0x2d // END Function definition
+ opMDAP0 = 0x2e // Move Direct Absolute Point
+ opMDAP1 = 0x2f // .
+ opIUP0 = 0x30 // Interpolate Untouched Points through the outline
+ opIUP1 = 0x31 // .
+ opSHP0 = 0x32 // SHift Point using reference point
+ opSHP1 = 0x33 // .
+ opSHC0 = 0x34 // SHift Contour using reference point
+ opSHC1 = 0x35 // .
+ opSHZ0 = 0x36 // SHift Zone using reference point
+ opSHZ1 = 0x37 // .
+ opSHPIX = 0x38 // SHift point by a PIXel amount
+ opIP = 0x39 // Interpolate Point
+ opMSIRP0 = 0x3a // Move Stack Indirect Relative Point
+ opMSIRP1 = 0x3b // .
+ opALIGNRP = 0x3c // ALIGN to Reference Point
+ opRTDG = 0x3d // Round To Double Grid
+ opMIAP0 = 0x3e // Move Indirect Absolute Point
+ opMIAP1 = 0x3f // .
+ opNPUSHB = 0x40 // PUSH N Bytes
+ opNPUSHW = 0x41 // PUSH N Words
+ opWS = 0x42 // Write Store
+ opRS = 0x43 // Read Store
+ opWCVTP = 0x44 // Write Control Value Table in Pixel units
+ opRCVT = 0x45 // Read Control Value Table entry
+ opGC0 = 0x46 // Get Coordinate projected onto the projection vector
+ opGC1 = 0x47 // .
+ opSCFS = 0x48 // Sets Coordinate From the Stack using projection vector and freedom vector
+ opMD0 = 0x49 // Measure Distance
+ opMD1 = 0x4a // .
+ opMPPEM = 0x4b // Measure Pixels Per EM
+ opMPS = 0x4c // Measure Point Size
+ opFLIPON = 0x4d // set the auto FLIP Boolean to ON
+ opFLIPOFF = 0x4e // set the auto FLIP Boolean to OFF
+ opDEBUG = 0x4f // DEBUG call
+ opLT = 0x50 // Less Than
+ opLTEQ = 0x51 // Less Than or EQual
+ opGT = 0x52 // Greater Than
+ opGTEQ = 0x53 // Greater Than or EQual
+ opEQ = 0x54 // EQual
+ opNEQ = 0x55 // Not EQual
+ opODD = 0x56 // ODD
+ opEVEN = 0x57 // EVEN
+ opIF = 0x58 // IF test
+ opEIF = 0x59 // End IF
+ opAND = 0x5a // logical AND
+ opOR = 0x5b // logical OR
+ opNOT = 0x5c // logical NOT
+ opDELTAP1 = 0x5d // DELTA exception P1
+ opSDB = 0x5e // Set Delta Base in the graphics state
+ opSDS = 0x5f // Set Delta Shift in the graphics state
+ opADD = 0x60 // ADD
+ opSUB = 0x61 // SUBtract
+ opDIV = 0x62 // DIVide
+ opMUL = 0x63 // MULtiply
+ opABS = 0x64 // ABSolute value
+ opNEG = 0x65 // NEGate
+ opFLOOR = 0x66 // FLOOR
+ opCEILING = 0x67 // CEILING
+ opROUND00 = 0x68 // ROUND value
+ opROUND01 = 0x69 // .
+ opROUND10 = 0x6a // .
+ opROUND11 = 0x6b // .
+ opNROUND00 = 0x6c // No ROUNDing of value
+ opNROUND01 = 0x6d // .
+ opNROUND10 = 0x6e // .
+ opNROUND11 = 0x6f // .
+ opWCVTF = 0x70 // Write Control Value Table in Funits
+ opDELTAP2 = 0x71 // DELTA exception P2
+ opDELTAP3 = 0x72 // DELTA exception P3
+ opDELTAC1 = 0x73 // DELTA exception C1
+ opDELTAC2 = 0x74 // DELTA exception C2
+ opDELTAC3 = 0x75 // DELTA exception C3
+ opSROUND = 0x76 // Super ROUND
+ opS45ROUND = 0x77 // Super ROUND 45 degrees
+ opJROT = 0x78 // Jump Relative On True
+ opJROF = 0x79 // Jump Relative On False
+ opROFF = 0x7a // Round OFF
+ op_0x7b = 0x7b // deprecated
+ opRUTG = 0x7c // Round Up To Grid
+ opRDTG = 0x7d // Round Down To Grid
+ opSANGW = 0x7e // Set ANGle Weight
+ opAA = 0x7f // Adjust Angle
+ opFLIPPT = 0x80 // FLIP PoinT
+ opFLIPRGON = 0x81 // FLIP RanGe ON
+ opFLIPRGOFF = 0x82 // FLIP RanGe OFF
+ op_0x83 = 0x83 // deprecated
+ op_0x84 = 0x84 // deprecated
+ opSCANCTRL = 0x85 // SCAN conversion ConTRoL
+ opSDPVTL0 = 0x86 // Set Dual Projection Vector To Line
+ opSDPVTL1 = 0x87 // .
+ opGETINFO = 0x88 // GET INFOrmation
+ opIDEF = 0x89 // Instruction DEFinition
+ opROLL = 0x8a // ROLL the top three stack elements
+ opMAX = 0x8b // MAXimum of top two stack elements
+ opMIN = 0x8c // MINimum of top two stack elements
+ opSCANTYPE = 0x8d // SCANTYPE
+ opINSTCTRL = 0x8e // INSTRuction execution ConTRoL
+ op_0x8f = 0x8f
+ op_0x90 = 0x90
+ op_0x91 = 0x91
+ op_0x92 = 0x92
+ op_0x93 = 0x93
+ op_0x94 = 0x94
+ op_0x95 = 0x95
+ op_0x96 = 0x96
+ op_0x97 = 0x97
+ op_0x98 = 0x98
+ op_0x99 = 0x99
+ op_0x9a = 0x9a
+ op_0x9b = 0x9b
+ op_0x9c = 0x9c
+ op_0x9d = 0x9d
+ op_0x9e = 0x9e
+ op_0x9f = 0x9f
+ op_0xa0 = 0xa0
+ op_0xa1 = 0xa1
+ op_0xa2 = 0xa2
+ op_0xa3 = 0xa3
+ op_0xa4 = 0xa4
+ op_0xa5 = 0xa5
+ op_0xa6 = 0xa6
+ op_0xa7 = 0xa7
+ op_0xa8 = 0xa8
+ op_0xa9 = 0xa9
+ op_0xaa = 0xaa
+ op_0xab = 0xab
+ op_0xac = 0xac
+ op_0xad = 0xad
+ op_0xae = 0xae
+ op_0xaf = 0xaf
+ opPUSHB000 = 0xb0 // PUSH Bytes
+ opPUSHB001 = 0xb1 // .
+ opPUSHB010 = 0xb2 // .
+ opPUSHB011 = 0xb3 // .
+ opPUSHB100 = 0xb4 // .
+ opPUSHB101 = 0xb5 // .
+ opPUSHB110 = 0xb6 // .
+ opPUSHB111 = 0xb7 // .
+ opPUSHW000 = 0xb8 // PUSH Words
+ opPUSHW001 = 0xb9 // .
+ opPUSHW010 = 0xba // .
+ opPUSHW011 = 0xbb // .
+ opPUSHW100 = 0xbc // .
+ opPUSHW101 = 0xbd // .
+ opPUSHW110 = 0xbe // .
+ opPUSHW111 = 0xbf // .
+ opMDRP00000 = 0xc0 // Move Direct Relative Point
+ opMDRP00001 = 0xc1 // .
+ opMDRP00010 = 0xc2 // .
+ opMDRP00011 = 0xc3 // .
+ opMDRP00100 = 0xc4 // .
+ opMDRP00101 = 0xc5 // .
+ opMDRP00110 = 0xc6 // .
+ opMDRP00111 = 0xc7 // .
+ opMDRP01000 = 0xc8 // .
+ opMDRP01001 = 0xc9 // .
+ opMDRP01010 = 0xca // .
+ opMDRP01011 = 0xcb // .
+ opMDRP01100 = 0xcc // .
+ opMDRP01101 = 0xcd // .
+ opMDRP01110 = 0xce // .
+ opMDRP01111 = 0xcf // .
+ opMDRP10000 = 0xd0 // .
+ opMDRP10001 = 0xd1 // .
+ opMDRP10010 = 0xd2 // .
+ opMDRP10011 = 0xd3 // .
+ opMDRP10100 = 0xd4 // .
+ opMDRP10101 = 0xd5 // .
+ opMDRP10110 = 0xd6 // .
+ opMDRP10111 = 0xd7 // .
+ opMDRP11000 = 0xd8 // .
+ opMDRP11001 = 0xd9 // .
+ opMDRP11010 = 0xda // .
+ opMDRP11011 = 0xdb // .
+ opMDRP11100 = 0xdc // .
+ opMDRP11101 = 0xdd // .
+ opMDRP11110 = 0xde // .
+ opMDRP11111 = 0xdf // .
+ opMIRP00000 = 0xe0 // Move Indirect Relative Point
+ opMIRP00001 = 0xe1 // .
+ opMIRP00010 = 0xe2 // .
+ opMIRP00011 = 0xe3 // .
+ opMIRP00100 = 0xe4 // .
+ opMIRP00101 = 0xe5 // .
+ opMIRP00110 = 0xe6 // .
+ opMIRP00111 = 0xe7 // .
+ opMIRP01000 = 0xe8 // .
+ opMIRP01001 = 0xe9 // .
+ opMIRP01010 = 0xea // .
+ opMIRP01011 = 0xeb // .
+ opMIRP01100 = 0xec // .
+ opMIRP01101 = 0xed // .
+ opMIRP01110 = 0xee // .
+ opMIRP01111 = 0xef // .
+ opMIRP10000 = 0xf0 // .
+ opMIRP10001 = 0xf1 // .
+ opMIRP10010 = 0xf2 // .
+ opMIRP10011 = 0xf3 // .
+ opMIRP10100 = 0xf4 // .
+ opMIRP10101 = 0xf5 // .
+ opMIRP10110 = 0xf6 // .
+ opMIRP10111 = 0xf7 // .
+ opMIRP11000 = 0xf8 // .
+ opMIRP11001 = 0xf9 // .
+ opMIRP11010 = 0xfa // .
+ opMIRP11011 = 0xfb // .
+ opMIRP11100 = 0xfc // .
+ opMIRP11101 = 0xfd // .
+ opMIRP11110 = 0xfe // .
+ opMIRP11111 = 0xff // .
+)
+
+// popCount is the number of stack elements that each opcode pops.
+var popCount = [256]uint8{
+ // 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
+ 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 5, // 0x00 - 0x0f
+ 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, // 0x10 - 0x1f
+ 1, 1, 0, 2, 0, 1, 1, 2, 0, 1, 2, 1, 1, 0, 1, 1, // 0x20 - 0x2f
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 2, 2, 0, 0, 2, 2, // 0x30 - 0x3f
+ 0, 0, 2, 1, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, // 0x40 - 0x4f
+ 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 2, 2, 1, 1, 1, 1, // 0x50 - 0x5f
+ 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6f
+ 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 1, 1, // 0x70 - 0x7f
+ 0, 2, 2, 0, 0, 1, 2, 2, 1, 1, 3, 2, 2, 1, 2, 0, // 0x80 - 0x8f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 - 0x9f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0 - 0xaf
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0 - 0xbf
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 - 0xcf
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 - 0xdf
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 0xef
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0 - 0xff
+}
diff --git a/vendor/github.com/golang/freetype/truetype/truetype.go b/vendor/github.com/golang/freetype/truetype/truetype.go
new file mode 100644
index 000000000..9e943d3ec
--- /dev/null
+++ b/vendor/github.com/golang/freetype/truetype/truetype.go
@@ -0,0 +1,643 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+// Package truetype provides a parser for the TTF and TTC file formats.
+// Those formats are documented at http://developer.apple.com/fonts/TTRefMan/
+// and http://www.microsoft.com/typography/otspec/
+//
+// Some of a font's methods provide lengths or co-ordinates, e.g. bounds, font
+// metrics and control points. All these methods take a scale parameter, which
+// is the number of pixels in 1 em, expressed as a 26.6 fixed point value. For
+// example, if 1 em is 10 pixels then scale is fixed.I(10), which is equal to
+// fixed.Int26_6(10 << 6).
+//
+// To measure a TrueType font in ideal FUnit space, use scale equal to
+// font.FUnitsPerEm().
+package truetype
+
+import (
+ "fmt"
+
+ "golang.org/x/image/math/fixed"
+)
+
+// An Index is a Font's index of a rune.
+type Index uint16
+
+// A NameID identifies a name table entry.
+//
+// See https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
+type NameID uint16
+
+const (
+ NameIDCopyright NameID = 0
+ NameIDFontFamily = 1
+ NameIDFontSubfamily = 2
+ NameIDUniqueSubfamilyID = 3
+ NameIDFontFullName = 4
+ NameIDNameTableVersion = 5
+ NameIDPostscriptName = 6
+ NameIDTrademarkNotice = 7
+ NameIDManufacturerName = 8
+ NameIDDesignerName = 9
+ NameIDFontDescription = 10
+ NameIDFontVendorURL = 11
+ NameIDFontDesignerURL = 12
+ NameIDFontLicense = 13
+ NameIDFontLicenseURL = 14
+ NameIDPreferredFamily = 16
+ NameIDPreferredSubfamily = 17
+ NameIDCompatibleName = 18
+ NameIDSampleText = 19
+)
+
+const (
+ // A 32-bit encoding consists of a most-significant 16-bit Platform ID and a
+ // least-significant 16-bit Platform Specific ID. The magic numbers are
+ // specified at https://www.microsoft.com/typography/otspec/name.htm
+ unicodeEncoding = 0x00000003 // PID = 0 (Unicode), PSID = 3 (Unicode 2.0)
+ microsoftSymbolEncoding = 0x00030000 // PID = 3 (Microsoft), PSID = 0 (Symbol)
+ microsoftUCS2Encoding = 0x00030001 // PID = 3 (Microsoft), PSID = 1 (UCS-2)
+ microsoftUCS4Encoding = 0x0003000a // PID = 3 (Microsoft), PSID = 10 (UCS-4)
+)
+
+// An HMetric holds the horizontal metrics of a single glyph.
+type HMetric struct {
+ AdvanceWidth, LeftSideBearing fixed.Int26_6
+}
+
+// A VMetric holds the vertical metrics of a single glyph.
+type VMetric struct {
+ AdvanceHeight, TopSideBearing fixed.Int26_6
+}
+
+// A FormatError reports that the input is not a valid TrueType font.
+type FormatError string
+
+func (e FormatError) Error() string {
+ return "freetype: invalid TrueType format: " + string(e)
+}
+
+// An UnsupportedError reports that the input uses a valid but unimplemented
+// TrueType feature.
+type UnsupportedError string
+
+func (e UnsupportedError) Error() string {
+ return "freetype: unsupported TrueType feature: " + string(e)
+}
+
+// u32 returns the big-endian uint32 at b[i:].
+func u32(b []byte, i int) uint32 {
+ return uint32(b[i])<<24 | uint32(b[i+1])<<16 | uint32(b[i+2])<<8 | uint32(b[i+3])
+}
+
+// u16 returns the big-endian uint16 at b[i:].
+func u16(b []byte, i int) uint16 {
+ return uint16(b[i])<<8 | uint16(b[i+1])
+}
+
+// readTable returns a slice of the TTF data given by a table's directory entry.
+func readTable(ttf []byte, offsetLength []byte) ([]byte, error) {
+ offset := int(u32(offsetLength, 0))
+ if offset < 0 {
+ return nil, FormatError(fmt.Sprintf("offset too large: %d", uint32(offset)))
+ }
+ length := int(u32(offsetLength, 4))
+ if length < 0 {
+ return nil, FormatError(fmt.Sprintf("length too large: %d", uint32(length)))
+ }
+ end := offset + length
+ if end < 0 || end > len(ttf) {
+ return nil, FormatError(fmt.Sprintf("offset + length too large: %d", uint32(offset)+uint32(length)))
+ }
+ return ttf[offset:end], nil
+}
+
+// parseSubtables returns the offset and platformID of the best subtable in
+// table, where best favors a Unicode cmap encoding, and failing that, a
+// Microsoft cmap encoding. offset is the offset of the first subtable in
+// table, and size is the size of each subtable.
+//
+// If pred is non-nil, then only subtables that satisfy that predicate will be
+// considered.
+func parseSubtables(table []byte, name string, offset, size int, pred func([]byte) bool) (
+ bestOffset int, bestPID uint32, retErr error) {
+
+ if len(table) < 4 {
+ return 0, 0, FormatError(name + " too short")
+ }
+ nSubtables := int(u16(table, 2))
+ if len(table) < size*nSubtables+offset {
+ return 0, 0, FormatError(name + " too short")
+ }
+ ok := false
+ for i := 0; i < nSubtables; i, offset = i+1, offset+size {
+ if pred != nil && !pred(table[offset:]) {
+ continue
+ }
+ // We read the 16-bit Platform ID and 16-bit Platform Specific ID as a single uint32.
+ // All values are big-endian.
+ pidPsid := u32(table, offset)
+ // We prefer the Unicode cmap encoding. Failing to find that, we fall
+ // back onto the Microsoft cmap encoding.
+ if pidPsid == unicodeEncoding {
+ bestOffset, bestPID, ok = offset, pidPsid>>16, true
+ break
+
+ } else if pidPsid == microsoftSymbolEncoding ||
+ pidPsid == microsoftUCS2Encoding ||
+ pidPsid == microsoftUCS4Encoding {
+
+ bestOffset, bestPID, ok = offset, pidPsid>>16, true
+ // We don't break out of the for loop, so that Unicode can override Microsoft.
+ }
+ }
+ if !ok {
+ return 0, 0, UnsupportedError(name + " encoding")
+ }
+ return bestOffset, bestPID, nil
+}
+
+const (
+ locaOffsetFormatUnknown int = iota
+ locaOffsetFormatShort
+ locaOffsetFormatLong
+)
+
+// A cm holds a parsed cmap entry.
+type cm struct {
+ start, end, delta, offset uint32
+}
+
+// A Font represents a Truetype font.
+type Font struct {
+ // Tables sliced from the TTF data. The different tables are documented
+ // at http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html
+ cmap, cvt, fpgm, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, name, os2, prep, vmtx []byte
+
+ cmapIndexes []byte
+
+ // Cached values derived from the raw ttf data.
+ cm []cm
+ locaOffsetFormat int
+ nGlyph, nHMetric, nKern int
+ fUnitsPerEm int32
+ ascent int32 // In FUnits.
+ descent int32 // In FUnits; typically negative.
+ bounds fixed.Rectangle26_6 // In FUnits.
+ // Values from the maxp section.
+ maxTwilightPoints, maxStorage, maxFunctionDefs, maxStackElements uint16
+}
+
+func (f *Font) parseCmap() error {
+ const (
+ cmapFormat4 = 4
+ cmapFormat12 = 12
+ languageIndependent = 0
+ )
+
+ offset, _, err := parseSubtables(f.cmap, "cmap", 4, 8, nil)
+ if err != nil {
+ return err
+ }
+ offset = int(u32(f.cmap, offset+4))
+ if offset <= 0 || offset > len(f.cmap) {
+ return FormatError("bad cmap offset")
+ }
+
+ cmapFormat := u16(f.cmap, offset)
+ switch cmapFormat {
+ case cmapFormat4:
+ language := u16(f.cmap, offset+4)
+ if language != languageIndependent {
+ return UnsupportedError(fmt.Sprintf("language: %d", language))
+ }
+ segCountX2 := int(u16(f.cmap, offset+6))
+ if segCountX2%2 == 1 {
+ return FormatError(fmt.Sprintf("bad segCountX2: %d", segCountX2))
+ }
+ segCount := segCountX2 / 2
+ offset += 14
+ f.cm = make([]cm, segCount)
+ for i := 0; i < segCount; i++ {
+ f.cm[i].end = uint32(u16(f.cmap, offset))
+ offset += 2
+ }
+ offset += 2
+ for i := 0; i < segCount; i++ {
+ f.cm[i].start = uint32(u16(f.cmap, offset))
+ offset += 2
+ }
+ for i := 0; i < segCount; i++ {
+ f.cm[i].delta = uint32(u16(f.cmap, offset))
+ offset += 2
+ }
+ for i := 0; i < segCount; i++ {
+ f.cm[i].offset = uint32(u16(f.cmap, offset))
+ offset += 2
+ }
+ f.cmapIndexes = f.cmap[offset:]
+ return nil
+
+ case cmapFormat12:
+ if u16(f.cmap, offset+2) != 0 {
+ return FormatError(fmt.Sprintf("cmap format: % x", f.cmap[offset:offset+4]))
+ }
+ length := u32(f.cmap, offset+4)
+ language := u32(f.cmap, offset+8)
+ if language != languageIndependent {
+ return UnsupportedError(fmt.Sprintf("language: %d", language))
+ }
+ nGroups := u32(f.cmap, offset+12)
+ if length != 12*nGroups+16 {
+ return FormatError("inconsistent cmap length")
+ }
+ offset += 16
+ f.cm = make([]cm, nGroups)
+ for i := uint32(0); i < nGroups; i++ {
+ f.cm[i].start = u32(f.cmap, offset+0)
+ f.cm[i].end = u32(f.cmap, offset+4)
+ f.cm[i].delta = u32(f.cmap, offset+8) - f.cm[i].start
+ offset += 12
+ }
+ return nil
+ }
+ return UnsupportedError(fmt.Sprintf("cmap format: %d", cmapFormat))
+}
+
+func (f *Font) parseHead() error {
+ if len(f.head) != 54 {
+ return FormatError(fmt.Sprintf("bad head length: %d", len(f.head)))
+ }
+ f.fUnitsPerEm = int32(u16(f.head, 18))
+ f.bounds.Min.X = fixed.Int26_6(int16(u16(f.head, 36)))
+ f.bounds.Min.Y = fixed.Int26_6(int16(u16(f.head, 38)))
+ f.bounds.Max.X = fixed.Int26_6(int16(u16(f.head, 40)))
+ f.bounds.Max.Y = fixed.Int26_6(int16(u16(f.head, 42)))
+ switch i := u16(f.head, 50); i {
+ case 0:
+ f.locaOffsetFormat = locaOffsetFormatShort
+ case 1:
+ f.locaOffsetFormat = locaOffsetFormatLong
+ default:
+ return FormatError(fmt.Sprintf("bad indexToLocFormat: %d", i))
+ }
+ return nil
+}
+
+func (f *Font) parseHhea() error {
+ if len(f.hhea) != 36 {
+ return FormatError(fmt.Sprintf("bad hhea length: %d", len(f.hhea)))
+ }
+ f.ascent = int32(int16(u16(f.hhea, 4)))
+ f.descent = int32(int16(u16(f.hhea, 6)))
+ f.nHMetric = int(u16(f.hhea, 34))
+ if 4*f.nHMetric+2*(f.nGlyph-f.nHMetric) != len(f.hmtx) {
+ return FormatError(fmt.Sprintf("bad hmtx length: %d", len(f.hmtx)))
+ }
+ return nil
+}
+
+func (f *Font) parseKern() error {
+ // Apple's TrueType documentation (http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html) says:
+ // "Previous versions of the 'kern' table defined both the version and nTables fields in the header
+ // as UInt16 values and not UInt32 values. Use of the older format on the Mac OS is discouraged
+ // (although AAT can sense an old kerning table and still make correct use of it). Microsoft
+ // Windows still uses the older format for the 'kern' table and will not recognize the newer one.
+ // Fonts targeted for the Mac OS only should use the new format; fonts targeted for both the Mac OS
+ // and Windows should use the old format."
+ // Since we expect that almost all fonts aim to be Windows-compatible, we only parse the "older" format,
+ // just like the C Freetype implementation.
+ if len(f.kern) == 0 {
+ if f.nKern != 0 {
+ return FormatError("bad kern table length")
+ }
+ return nil
+ }
+ if len(f.kern) < 18 {
+ return FormatError("kern data too short")
+ }
+ version, offset := u16(f.kern, 0), 2
+ if version != 0 {
+ return UnsupportedError(fmt.Sprintf("kern version: %d", version))
+ }
+ n, offset := u16(f.kern, offset), offset+2
+ if n != 1 {
+ return UnsupportedError(fmt.Sprintf("kern nTables: %d", n))
+ }
+ offset += 2
+ length, offset := int(u16(f.kern, offset)), offset+2
+ coverage, offset := u16(f.kern, offset), offset+2
+ if coverage != 0x0001 {
+ // We only support horizontal kerning.
+ return UnsupportedError(fmt.Sprintf("kern coverage: 0x%04x", coverage))
+ }
+ f.nKern, offset = int(u16(f.kern, offset)), offset+2
+ if 6*f.nKern != length-14 {
+ return FormatError("bad kern table length")
+ }
+ return nil
+}
+
+func (f *Font) parseMaxp() error {
+ if len(f.maxp) != 32 {
+ return FormatError(fmt.Sprintf("bad maxp length: %d", len(f.maxp)))
+ }
+ f.nGlyph = int(u16(f.maxp, 4))
+ f.maxTwilightPoints = u16(f.maxp, 16)
+ f.maxStorage = u16(f.maxp, 18)
+ f.maxFunctionDefs = u16(f.maxp, 20)
+ f.maxStackElements = u16(f.maxp, 24)
+ return nil
+}
+
+// scale returns x divided by f.fUnitsPerEm, rounded to the nearest integer.
+func (f *Font) scale(x fixed.Int26_6) fixed.Int26_6 {
+ if x >= 0 {
+ x += fixed.Int26_6(f.fUnitsPerEm) / 2
+ } else {
+ x -= fixed.Int26_6(f.fUnitsPerEm) / 2
+ }
+ return x / fixed.Int26_6(f.fUnitsPerEm)
+}
+
+// Bounds returns the union of a Font's glyphs' bounds.
+func (f *Font) Bounds(scale fixed.Int26_6) fixed.Rectangle26_6 {
+ b := f.bounds
+ b.Min.X = f.scale(scale * b.Min.X)
+ b.Min.Y = f.scale(scale * b.Min.Y)
+ b.Max.X = f.scale(scale * b.Max.X)
+ b.Max.Y = f.scale(scale * b.Max.Y)
+ return b
+}
+
+// FUnitsPerEm returns the number of FUnits in a Font's em-square's side.
+func (f *Font) FUnitsPerEm() int32 {
+ return f.fUnitsPerEm
+}
+
+// Index returns a Font's index for the given rune.
+func (f *Font) Index(x rune) Index {
+ c := uint32(x)
+ for i, j := 0, len(f.cm); i < j; {
+ h := i + (j-i)/2
+ cm := &f.cm[h]
+ if c < cm.start {
+ j = h
+ } else if cm.end < c {
+ i = h + 1
+ } else if cm.offset == 0 {
+ return Index(c + cm.delta)
+ } else {
+ offset := int(cm.offset) + 2*(h-len(f.cm)+int(c-cm.start))
+ return Index(u16(f.cmapIndexes, offset))
+ }
+ }
+ return 0
+}
+
+// Name returns the Font's name value for the given NameID. It returns "" if
+// there was an error, or if that name was not found.
+func (f *Font) Name(id NameID) string {
+ x, platformID, err := parseSubtables(f.name, "name", 6, 12, func(b []byte) bool {
+ return NameID(u16(b, 6)) == id
+ })
+ if err != nil {
+ return ""
+ }
+ offset, length := u16(f.name, 4)+u16(f.name, x+10), u16(f.name, x+8)
+ // Return the ASCII value of the encoded string.
+ // The string is encoded as UTF-16 on non-Apple platformIDs; Apple is platformID 1.
+ src := f.name[offset : offset+length]
+ var dst []byte
+ if platformID != 1 { // UTF-16.
+ if len(src)&1 != 0 {
+ return ""
+ }
+ dst = make([]byte, len(src)/2)
+ for i := range dst {
+ dst[i] = printable(u16(src, 2*i))
+ }
+ } else { // ASCII.
+ dst = make([]byte, len(src))
+ for i, c := range src {
+ dst[i] = printable(uint16(c))
+ }
+ }
+ return string(dst)
+}
+
+func printable(r uint16) byte {
+ if 0x20 <= r && r < 0x7f {
+ return byte(r)
+ }
+ return '?'
+}
+
+// unscaledHMetric returns the unscaled horizontal metrics for the glyph with
+// the given index.
+func (f *Font) unscaledHMetric(i Index) (h HMetric) {
+ j := int(i)
+ if j < 0 || f.nGlyph <= j {
+ return HMetric{}
+ }
+ if j >= f.nHMetric {
+ p := 4 * (f.nHMetric - 1)
+ return HMetric{
+ AdvanceWidth: fixed.Int26_6(u16(f.hmtx, p)),
+ LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, p+2*(j-f.nHMetric)+4))),
+ }
+ }
+ return HMetric{
+ AdvanceWidth: fixed.Int26_6(u16(f.hmtx, 4*j)),
+ LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, 4*j+2))),
+ }
+}
+
+// HMetric returns the horizontal metrics for the glyph with the given index.
+func (f *Font) HMetric(scale fixed.Int26_6, i Index) HMetric {
+ h := f.unscaledHMetric(i)
+ h.AdvanceWidth = f.scale(scale * h.AdvanceWidth)
+ h.LeftSideBearing = f.scale(scale * h.LeftSideBearing)
+ return h
+}
+
+// unscaledVMetric returns the unscaled vertical metrics for the glyph with
+// the given index. yMax is the top of the glyph's bounding box.
+func (f *Font) unscaledVMetric(i Index, yMax fixed.Int26_6) (v VMetric) {
+ j := int(i)
+ if j < 0 || f.nGlyph <= j {
+ return VMetric{}
+ }
+ if 4*j+4 <= len(f.vmtx) {
+ return VMetric{
+ AdvanceHeight: fixed.Int26_6(u16(f.vmtx, 4*j)),
+ TopSideBearing: fixed.Int26_6(int16(u16(f.vmtx, 4*j+2))),
+ }
+ }
+ // The OS/2 table has grown over time.
+ // https://developer.apple.com/fonts/TTRefMan/RM06/Chap6OS2.html
+ // says that it was originally 68 bytes. Optional fields, including
+ // the ascender and descender, are described at
+ // http://www.microsoft.com/typography/otspec/os2.htm
+ if len(f.os2) >= 72 {
+ sTypoAscender := fixed.Int26_6(int16(u16(f.os2, 68)))
+ sTypoDescender := fixed.Int26_6(int16(u16(f.os2, 70)))
+ return VMetric{
+ AdvanceHeight: sTypoAscender - sTypoDescender,
+ TopSideBearing: sTypoAscender - yMax,
+ }
+ }
+ return VMetric{
+ AdvanceHeight: fixed.Int26_6(f.fUnitsPerEm),
+ TopSideBearing: 0,
+ }
+}
+
+// VMetric returns the vertical metrics for the glyph with the given index.
+func (f *Font) VMetric(scale fixed.Int26_6, i Index) VMetric {
+ // TODO: should 0 be bounds.YMax?
+ v := f.unscaledVMetric(i, 0)
+ v.AdvanceHeight = f.scale(scale * v.AdvanceHeight)
+ v.TopSideBearing = f.scale(scale * v.TopSideBearing)
+ return v
+}
+
+// Kern returns the horizontal adjustment for the given glyph pair. A positive
+// kern means to move the glyphs further apart.
+func (f *Font) Kern(scale fixed.Int26_6, i0, i1 Index) fixed.Int26_6 {
+ if f.nKern == 0 {
+ return 0
+ }
+ g := uint32(i0)<<16 | uint32(i1)
+ lo, hi := 0, f.nKern
+ for lo < hi {
+ i := (lo + hi) / 2
+ ig := u32(f.kern, 18+6*i)
+ if ig < g {
+ lo = i + 1
+ } else if ig > g {
+ hi = i
+ } else {
+ return f.scale(scale * fixed.Int26_6(int16(u16(f.kern, 22+6*i))))
+ }
+ }
+ return 0
+}
+
+// Parse returns a new Font for the given TTF or TTC data.
+//
+// For TrueType Collections, the first font in the collection is parsed.
+func Parse(ttf []byte) (font *Font, err error) {
+ return parse(ttf, 0)
+}
+
+func parse(ttf []byte, offset int) (font *Font, err error) {
+ if len(ttf)-offset < 12 {
+ err = FormatError("TTF data is too short")
+ return
+ }
+ originalOffset := offset
+ magic, offset := u32(ttf, offset), offset+4
+ switch magic {
+ case 0x00010000:
+ // No-op.
+ case 0x74746366: // "ttcf" as a big-endian uint32.
+ if originalOffset != 0 {
+ err = FormatError("recursive TTC")
+ return
+ }
+ ttcVersion, offset := u32(ttf, offset), offset+4
+ if ttcVersion != 0x00010000 {
+ // TODO: support TTC version 2.0, once I have such a .ttc file to test with.
+ err = FormatError("bad TTC version")
+ return
+ }
+ numFonts, offset := int(u32(ttf, offset)), offset+4
+ if numFonts <= 0 {
+ err = FormatError("bad number of TTC fonts")
+ return
+ }
+ if len(ttf[offset:])/4 < numFonts {
+ err = FormatError("TTC offset table is too short")
+ return
+ }
+ // TODO: provide an API to select which font in a TrueType collection to return,
+ // not just the first one. This may require an API to parse a TTC's name tables,
+ // so users of this package can select the font in a TTC by name.
+ offset = int(u32(ttf, offset))
+ if offset <= 0 || offset > len(ttf) {
+ err = FormatError("bad TTC offset")
+ return
+ }
+ return parse(ttf, offset)
+ default:
+ err = FormatError("bad TTF version")
+ return
+ }
+ n, offset := int(u16(ttf, offset)), offset+2
+ if len(ttf) < 16*n+12 {
+ err = FormatError("TTF data is too short")
+ return
+ }
+ f := new(Font)
+ // Assign the table slices.
+ for i := 0; i < n; i++ {
+ x := 16*i + 12
+ switch string(ttf[x : x+4]) {
+ case "cmap":
+ f.cmap, err = readTable(ttf, ttf[x+8:x+16])
+ case "cvt ":
+ f.cvt, err = readTable(ttf, ttf[x+8:x+16])
+ case "fpgm":
+ f.fpgm, err = readTable(ttf, ttf[x+8:x+16])
+ case "glyf":
+ f.glyf, err = readTable(ttf, ttf[x+8:x+16])
+ case "hdmx":
+ f.hdmx, err = readTable(ttf, ttf[x+8:x+16])
+ case "head":
+ f.head, err = readTable(ttf, ttf[x+8:x+16])
+ case "hhea":
+ f.hhea, err = readTable(ttf, ttf[x+8:x+16])
+ case "hmtx":
+ f.hmtx, err = readTable(ttf, ttf[x+8:x+16])
+ case "kern":
+ f.kern, err = readTable(ttf, ttf[x+8:x+16])
+ case "loca":
+ f.loca, err = readTable(ttf, ttf[x+8:x+16])
+ case "maxp":
+ f.maxp, err = readTable(ttf, ttf[x+8:x+16])
+ case "name":
+ f.name, err = readTable(ttf, ttf[x+8:x+16])
+ case "OS/2":
+ f.os2, err = readTable(ttf, ttf[x+8:x+16])
+ case "prep":
+ f.prep, err = readTable(ttf, ttf[x+8:x+16])
+ case "vmtx":
+ f.vmtx, err = readTable(ttf, ttf[x+8:x+16])
+ }
+ if err != nil {
+ return
+ }
+ }
+ // Parse and sanity-check the TTF data.
+ if err = f.parseHead(); err != nil {
+ return
+ }
+ if err = f.parseMaxp(); err != nil {
+ return
+ }
+ if err = f.parseCmap(); err != nil {
+ return
+ }
+ if err = f.parseKern(); err != nil {
+ return
+ }
+ if err = f.parseHhea(); err != nil {
+ return
+ }
+ font = f
+ return
+}
diff --git a/vendor/github.com/golang/groupcache/LICENSE b/vendor/github.com/golang/groupcache/LICENSE
new file mode 100644
index 000000000..37ec93a14
--- /dev/null
+++ b/vendor/github.com/golang/groupcache/LICENSE
@@ -0,0 +1,191 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "[]" replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same "printed page" as the copyright notice for easier identification within
+third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/golang/groupcache/lru/lru.go b/vendor/github.com/golang/groupcache/lru/lru.go
new file mode 100644
index 000000000..cdfe2991f
--- /dev/null
+++ b/vendor/github.com/golang/groupcache/lru/lru.go
@@ -0,0 +1,121 @@
+/*
+Copyright 2013 Google Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package lru implements an LRU cache.
+package lru
+
+import "container/list"
+
+// Cache is an LRU cache. It is not safe for concurrent access.
+type Cache struct {
+ // MaxEntries is the maximum number of cache entries before
+ // an item is evicted. Zero means no limit.
+ MaxEntries int
+
+ // OnEvicted optionally specificies a callback function to be
+ // executed when an entry is purged from the cache.
+ OnEvicted func(key Key, value interface{})
+
+ ll *list.List
+ cache map[interface{}]*list.Element
+}
+
+// A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators
+type Key interface{}
+
+type entry struct {
+ key Key
+ value interface{}
+}
+
+// New creates a new Cache.
+// If maxEntries is zero, the cache has no limit and it's assumed
+// that eviction is done by the caller.
+func New(maxEntries int) *Cache {
+ return &Cache{
+ MaxEntries: maxEntries,
+ ll: list.New(),
+ cache: make(map[interface{}]*list.Element),
+ }
+}
+
+// Add adds a value to the cache.
+func (c *Cache) Add(key Key, value interface{}) {
+ if c.cache == nil {
+ c.cache = make(map[interface{}]*list.Element)
+ c.ll = list.New()
+ }
+ if ee, ok := c.cache[key]; ok {
+ c.ll.MoveToFront(ee)
+ ee.Value.(*entry).value = value
+ return
+ }
+ ele := c.ll.PushFront(&entry{key, value})
+ c.cache[key] = ele
+ if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries {
+ c.RemoveOldest()
+ }
+}
+
+// Get looks up a key's value from the cache.
+func (c *Cache) Get(key Key) (value interface{}, ok bool) {
+ if c.cache == nil {
+ return
+ }
+ if ele, hit := c.cache[key]; hit {
+ c.ll.MoveToFront(ele)
+ return ele.Value.(*entry).value, true
+ }
+ return
+}
+
+// Remove removes the provided key from the cache.
+func (c *Cache) Remove(key Key) {
+ if c.cache == nil {
+ return
+ }
+ if ele, hit := c.cache[key]; hit {
+ c.removeElement(ele)
+ }
+}
+
+// RemoveOldest removes the oldest item from the cache.
+func (c *Cache) RemoveOldest() {
+ if c.cache == nil {
+ return
+ }
+ ele := c.ll.Back()
+ if ele != nil {
+ c.removeElement(ele)
+ }
+}
+
+func (c *Cache) removeElement(e *list.Element) {
+ c.ll.Remove(e)
+ kv := e.Value.(*entry)
+ delete(c.cache, kv.key)
+ if c.OnEvicted != nil {
+ c.OnEvicted(kv.key, kv.value)
+ }
+}
+
+// Len returns the number of items in the cache.
+func (c *Cache) Len() int {
+ if c.cache == nil {
+ return 0
+ }
+ return c.ll.Len()
+}
diff --git a/vendor/github.com/gorilla/context/.travis.yml b/vendor/github.com/gorilla/context/.travis.yml
new file mode 100644
index 000000000..24882fc7b
--- /dev/null
+++ b/vendor/github.com/gorilla/context/.travis.yml
@@ -0,0 +1,18 @@
+language: go
+sudo: false
+
+matrix:
+ include:
+ - go: 1.3
+ - go: 1.4
+ - go: 1.5
+ - go: 1.6
+ - go: tip
+ allow_failures:
+ - go: tip
+
+script:
+ - go get -t -v ./...
+ - diff -u <(echo -n) <(gofmt -d .)
+ - go vet $(go list ./... | grep -v /vendor/)
+ - go test -v -race ./...
diff --git a/vendor/github.com/gorilla/context/LICENSE b/vendor/github.com/gorilla/context/LICENSE
new file mode 100644
index 000000000..0e5fb8728
--- /dev/null
+++ b/vendor/github.com/gorilla/context/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/gorilla/context/README.md b/vendor/github.com/gorilla/context/README.md
new file mode 100644
index 000000000..c60a31b05
--- /dev/null
+++ b/vendor/github.com/gorilla/context/README.md
@@ -0,0 +1,7 @@
+context
+=======
+[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context)
+
+gorilla/context is a general purpose registry for global request variables.
+
+Read the full documentation here: http://www.gorillatoolkit.org/pkg/context
diff --git a/vendor/github.com/gorilla/context/context.go b/vendor/github.com/gorilla/context/context.go
new file mode 100644
index 000000000..81cb128b1
--- /dev/null
+++ b/vendor/github.com/gorilla/context/context.go
@@ -0,0 +1,143 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package context
+
+import (
+ "net/http"
+ "sync"
+ "time"
+)
+
+var (
+ mutex sync.RWMutex
+ data = make(map[*http.Request]map[interface{}]interface{})
+ datat = make(map[*http.Request]int64)
+)
+
+// Set stores a value for a given key in a given request.
+func Set(r *http.Request, key, val interface{}) {
+ mutex.Lock()
+ if data[r] == nil {
+ data[r] = make(map[interface{}]interface{})
+ datat[r] = time.Now().Unix()
+ }
+ data[r][key] = val
+ mutex.Unlock()
+}
+
+// Get returns a value stored for a given key in a given request.
+func Get(r *http.Request, key interface{}) interface{} {
+ mutex.RLock()
+ if ctx := data[r]; ctx != nil {
+ value := ctx[key]
+ mutex.RUnlock()
+ return value
+ }
+ mutex.RUnlock()
+ return nil
+}
+
+// GetOk returns stored value and presence state like multi-value return of map access.
+func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
+ mutex.RLock()
+ if _, ok := data[r]; ok {
+ value, ok := data[r][key]
+ mutex.RUnlock()
+ return value, ok
+ }
+ mutex.RUnlock()
+ return nil, false
+}
+
+// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.
+func GetAll(r *http.Request) map[interface{}]interface{} {
+ mutex.RLock()
+ if context, ok := data[r]; ok {
+ result := make(map[interface{}]interface{}, len(context))
+ for k, v := range context {
+ result[k] = v
+ }
+ mutex.RUnlock()
+ return result
+ }
+ mutex.RUnlock()
+ return nil
+}
+
+// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if
+// the request was registered.
+func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) {
+ mutex.RLock()
+ context, ok := data[r]
+ result := make(map[interface{}]interface{}, len(context))
+ for k, v := range context {
+ result[k] = v
+ }
+ mutex.RUnlock()
+ return result, ok
+}
+
+// Delete removes a value stored for a given key in a given request.
+func Delete(r *http.Request, key interface{}) {
+ mutex.Lock()
+ if data[r] != nil {
+ delete(data[r], key)
+ }
+ mutex.Unlock()
+}
+
+// Clear removes all values stored for a given request.
+//
+// This is usually called by a handler wrapper to clean up request
+// variables at the end of a request lifetime. See ClearHandler().
+func Clear(r *http.Request) {
+ mutex.Lock()
+ clear(r)
+ mutex.Unlock()
+}
+
+// clear is Clear without the lock.
+func clear(r *http.Request) {
+ delete(data, r)
+ delete(datat, r)
+}
+
+// Purge removes request data stored for longer than maxAge, in seconds.
+// It returns the amount of requests removed.
+//
+// If maxAge <= 0, all request data is removed.
+//
+// This is only used for sanity check: in case context cleaning was not
+// properly set some request data can be kept forever, consuming an increasing
+// amount of memory. In case this is detected, Purge() must be called
+// periodically until the problem is fixed.
+func Purge(maxAge int) int {
+ mutex.Lock()
+ count := 0
+ if maxAge <= 0 {
+ count = len(data)
+ data = make(map[*http.Request]map[interface{}]interface{})
+ datat = make(map[*http.Request]int64)
+ } else {
+ min := time.Now().Unix() - int64(maxAge)
+ for r := range data {
+ if datat[r] < min {
+ clear(r)
+ count++
+ }
+ }
+ }
+ mutex.Unlock()
+ return count
+}
+
+// ClearHandler wraps an http.Handler and clears request values at the end
+// of a request lifetime.
+func ClearHandler(h http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ defer Clear(r)
+ h.ServeHTTP(w, r)
+ })
+}
diff --git a/vendor/github.com/gorilla/context/doc.go b/vendor/github.com/gorilla/context/doc.go
new file mode 100644
index 000000000..73c740031
--- /dev/null
+++ b/vendor/github.com/gorilla/context/doc.go
@@ -0,0 +1,82 @@
+// Copyright 2012 The Gorilla Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package context stores values shared during a request lifetime.
+
+For example, a router can set variables extracted from the URL and later
+application handlers can access those values, or it can be used to store
+sessions values to be saved at the end of a request. There are several
+others common uses.
+
+The idea was posted by Brad Fitzpatrick to the go-nuts mailing list:
+
+ http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53
+
+Here's the basic usage: first define the keys that you will need. The key
+type is interface{} so a key can be of any type that supports equality.
+Here we define a key using a custom int type to avoid name collisions:
+
+ package foo
+
+ import (
+ "github.com/gorilla/context"
+ )
+
+ type key int
+
+ const MyKey key = 0
+
+Then set a variable. Variables are bound to an http.Request object, so you
+need a request instance to set a value:
+
+ context.Set(r, MyKey, "bar")
+
+The application can later access the variable using the same key you provided:
+
+ func MyHandler(w http.ResponseWriter, r *http.Request) {
+ // val is "bar".
+ val := context.Get(r, foo.MyKey)
+
+ // returns ("bar", true)
+ val, ok := context.GetOk(r, foo.MyKey)
+ // ...
+ }
+
+And that's all about the basic usage. We discuss some other ideas below.
+
+Any type can be stored in the context. To enforce a given type, make the key
+private and wrap Get() and Set() to accept and return values of a specific
+type:
+
+ type key int
+
+ const mykey key = 0
+
+ // GetMyKey returns a value for this package from the request values.
+ func GetMyKey(r *http.Request) SomeType {
+ if rv := context.Get(r, mykey); rv != nil {
+ return rv.(SomeType)
+ }
+ return nil
+ }
+
+ // SetMyKey sets a value for this package in the request values.
+ func SetMyKey(r *http.Request, val SomeType) {
+ context.Set(r, mykey, val)
+ }
+
+Variables must be cleared at the end of a request, to remove all values
+that were stored. This can be done in an http.Handler, after a request was
+served. Just call Clear() passing the request:
+
+ context.Clear(r)
+
+...or use ClearHandler(), which conveniently wraps an http.Handler to clear
+variables at the end of a request lifetime.
+
+The Routers from the packages gorilla/mux and gorilla/pat call Clear()
+so if you are using either of them you don't need to clear the context manually.
+*/
+package context
diff --git a/vendor/github.com/gorilla/handlers/.travis.yml b/vendor/github.com/gorilla/handlers/.travis.yml
new file mode 100644
index 000000000..66435ac0b
--- /dev/null
+++ b/vendor/github.com/gorilla/handlers/.travis.yml
@@ -0,0 +1,17 @@
+language: go
+sudo: false
+
+matrix:
+ include:
+ - go: 1.4
+ - go: 1.5
+ - go: 1.6
+ - go: tip
+ allow_failures:
+ - go: tip
+
+script:
+ - go get -t -v ./...
+ - diff -u <(echo -n) <(gofmt -d .)
+ - go vet $(go list ./... | grep -v /vendor/)
+ - go test -v -race ./...
diff --git a/vendor/github.com/gorilla/handlers/LICENSE b/vendor/github.com/gorilla/handlers/LICENSE
new file mode 100644
index 000000000..66ea3c8ae
--- /dev/null
+++ b/vendor/github.com/gorilla/handlers/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2013 The Gorilla Handlers Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/gorilla/handlers/README.md b/vendor/github.com/gorilla/handlers/README.md
new file mode 100644
index 000000000..a782c4152
--- /dev/null
+++ b/vendor/github.com/gorilla/handlers/README.md
@@ -0,0 +1,53 @@
+gorilla/handlers
+================
+[![GoDoc](https://godoc.org/github.com/gorilla/handlers?status.svg)](https://godoc.org/github.com/gorilla/handlers) [![Build Status](https://travis-ci.org/gorilla/handlers.svg?branch=master)](https://travis-ci.org/gorilla/handlers)
+
+Package handlers is a collection of handlers (aka "HTTP middleware") for use
+with Go's `net/http` package (or any framework supporting `http.Handler`), including:
+
+* [**LoggingHandler**](https://godoc.org/github.com/gorilla/handlers#LoggingHandler) for logging HTTP requests in the Apache [Common Log
+ Format](http://httpd.apache.org/docs/2.2/logs.html#common).
+* [**CombinedLoggingHandler**](https://godoc.org/github.com/gorilla/handlers#CombinedLoggingHandler) for logging HTTP requests in the Apache [Combined Log
+ Format](http://httpd.apache.org/docs/2.2/logs.html#combined) commonly used by
+ both Apache and nginx.
+* [**CompressHandler**](https://godoc.org/github.com/gorilla/handlers#CompressHandler) for gzipping responses.
+* [**ContentTypeHandler**](https://godoc.org/github.com/gorilla/handlers#ContentTypeHandler) for validating requests against a list of accepted
+ content types.
+* [**MethodHandler**](https://godoc.org/github.com/gorilla/handlers#MethodHandler) for matching HTTP methods against handlers in a
+ `map[string]http.Handler`
+* [**ProxyHeaders**](https://godoc.org/github.com/gorilla/handlers#ProxyHeaders) for populating `r.RemoteAddr` and `r.URL.Scheme` based on the
+ `X-Forwarded-For`, `X-Real-IP`, `X-Forwarded-Proto` and RFC7239 `Forwarded`
+ headers when running a Go server behind a HTTP reverse proxy.
+* [**CanonicalHost**](https://godoc.org/github.com/gorilla/handlers#CanonicalHost) for re-directing to the preferred host when handling multiple
+ domains (i.e. multiple CNAME aliases).
+* [**RecoveryHandler**](https://godoc.org/github.com/gorilla/handlers#RecoveryHandler) for recovering from unexpected panics.
+
+Other handlers are documented [on the Gorilla
+website](http://www.gorillatoolkit.org/pkg/handlers).
+
+## Example
+
+A simple example using `handlers.LoggingHandler` and `handlers.CompressHandler`:
+
+```go
+import (
+ "net/http"
+ "github.com/gorilla/handlers"
+)
+
+func main() {
+ r := http.NewServeMux()
+
+ // Only log requests to our admin dashboard to stdout
+ r.Handle("/admin", handlers.LoggingHandler(os.Stdout, http.HandlerFunc(ShowAdminDashboard)))
+ r.HandleFunc("/", ShowIndex)
+
+ // Wrap our server with our gzip handler to gzip compress all responses.
+ http.ListenAndServe(":8000", handlers.CompressHandler(r))
+}
+```
+
+## License
+
+BSD licensed. See the included LICENSE file for details.
+
diff --git a/vendor/github.com/gorilla/handlers/canonical.go b/vendor/github.com/gorilla/handlers/canonical.go
new file mode 100644
index 000000000..8437fefc1
--- /dev/null
+++ b/vendor/github.com/gorilla/handlers/canonical.go
@@ -0,0 +1,74 @@
+package handlers
+
+import (
+ "net/http"
+ "net/url"
+ "strings"
+)
+
+type canonical struct {
+ h http.Handler
+ domain string
+ code int
+}
+
+// CanonicalHost is HTTP middleware that re-directs requests to the canonical
+// domain. It accepts a domain and a status code (e.g. 301 or 302) and
+// re-directs clients to this domain. The existing request path is maintained.
+//
+// Note: If the provided domain is considered invalid by url.Parse or otherwise
+// returns an empty scheme or host, clients are not re-directed.
+//
+// Example:
+//
+// r := mux.NewRouter()
+// canonical := handlers.CanonicalHost("http://www.gorillatoolkit.org", 302)
+// r.HandleFunc("/route", YourHandler)
+//
+// log.Fatal(http.ListenAndServe(":7000", canonical(r)))
+//
+func CanonicalHost(domain string, code int) func(h http.Handler) http.Handler {
+ fn := func(h http.Handler) http.Handler {
+ return canonical{h, domain, code}
+ }
+
+ return fn
+}
+
+func (c canonical) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ dest, err := url.Parse(c.domain)
+ if err != nil {
+ // Call the next handler if the provided domain fails to parse.
+ c.h.ServeHTTP(w, r)
+ return
+ }
+
+ if dest.Scheme == "" || dest.Host == "" {
+ // Call the next handler if the scheme or host are empty.
+ // Note that url.Parse won't fail on in this case.
+ c.h.ServeHTTP(w, r)
+ return
+ }
+
+ if !strings.EqualFold(cleanHost(r.Host), dest.Host) {
+ // Re-build the destination URL
+ dest := dest.Scheme + "://" + dest.Host + r.URL.Path
+ if r.URL.RawQuery != "" {
+ dest += "?" + r.URL.RawQuery
+ }
+ http.Redirect(w, r, dest, c.code)
+ return
+ }
+
+ c.h.ServeHTTP(w, r)
+}
+
+// cleanHost cleans invalid Host headers by stripping anything after '/' or ' '.
+// This is backported from Go 1.5 (in response to issue #11206) and attempts to
+// mitigate malformed Host headers that do not match the format in RFC7230.
+func cleanHost(in string) string {
+ if i := strings.IndexAny(in, " /"); i != -1 {
+ return in[:i]
+ }
+ return in
+}
diff --git a/vendor/github.com/gorilla/handlers/compress.go b/vendor/github.com/gorilla/handlers/compress.go
new file mode 100644
index 000000000..5e140c503
--- /dev/null
+++ b/vendor/github.com/gorilla/handlers/compress.go
@@ -0,0 +1,145 @@
+// Copyright 2013 The Gorilla 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 handlers
+
+import (
+ "compress/flate"
+ "compress/gzip"
+ "io"
+ "net/http"
+ "strings"
+)
+
+type compressResponseWriter struct {
+ io.Writer
+ http.ResponseWriter
+ http.Hijacker
+ http.Flusher
+ http.CloseNotifier
+}
+
+func (w *compressResponseWriter) WriteHeader(c int) {
+ w.ResponseWriter.Header().Del("Content-Length")
+ w.ResponseWriter.WriteHeader(c)
+}
+
+func (w *compressResponseWriter) Header() http.Header {
+ return w.ResponseWriter.Header()
+}
+
+func (w *compressResponseWriter) Write(b []byte) (int, error) {
+ h := w.ResponseWriter.Header()
+ if h.Get("Content-Type") == "" {
+ h.Set("Content-Type", http.DetectContentType(b))
+ }
+ h.Del("Content-Length")
+
+ return w.Writer.Write(b)
+}
+
+type flusher interface {
+ Flush() error
+}
+
+func (w *compressResponseWriter) Flush() {
+ // Flush compressed data if compressor supports it.
+ if f, ok := w.Writer.(flusher); ok {
+ f.Flush()
+ }
+ // Flush HTTP response.
+ if w.Flusher != nil {
+ w.Flusher.Flush()
+ }
+}
+
+// CompressHandler gzip compresses HTTP responses for clients that support it
+// via the 'Accept-Encoding' header.
+func CompressHandler(h http.Handler) http.Handler {
+ return CompressHandlerLevel(h, gzip.DefaultCompression)
+}
+
+// CompressHandlerLevel gzip compresses HTTP responses with specified compression level
+// for clients that support it via the 'Accept-Encoding' header.
+//
+// The compression level should be gzip.DefaultCompression, gzip.NoCompression,
+// or any integer value between gzip.BestSpeed and gzip.BestCompression inclusive.
+// gzip.DefaultCompression is used in case of invalid compression level.
+func CompressHandlerLevel(h http.Handler, level int) http.Handler {
+ if level < gzip.DefaultCompression || level > gzip.BestCompression {
+ level = gzip.DefaultCompression
+ }
+
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ L:
+ for _, enc := range strings.Split(r.Header.Get("Accept-Encoding"), ",") {
+ switch strings.TrimSpace(enc) {
+ case "gzip":
+ w.Header().Set("Content-Encoding", "gzip")
+ w.Header().Add("Vary", "Accept-Encoding")
+
+ gw, _ := gzip.NewWriterLevel(w, level)
+ defer gw.Close()
+
+ h, hok := w.(http.Hijacker)
+ if !hok { /* w is not Hijacker... oh well... */
+ h = nil
+ }
+
+ f, fok := w.(http.Flusher)
+ if !fok {
+ f = nil
+ }
+
+ cn, cnok := w.(http.CloseNotifier)
+ if !cnok {
+ cn = nil
+ }
+
+ w = &compressResponseWriter{
+ Writer: gw,
+ ResponseWriter: w,
+ Hijacker: h,
+ Flusher: f,
+ CloseNotifier: cn,
+ }
+
+ break L
+ case "deflate":
+ w.Header().Set("Content-Encoding", "deflate")
+ w.Header().Add("Vary", "Accept-Encoding")
+
+ fw, _ := flate.NewWriter(w, level)
+ defer fw.Close()
+
+ h, hok := w.(http.Hijacker)
+ if !hok { /* w is not Hijacker... oh well... */
+ h = nil
+ }
+
+ f, fok := w.(http.Flusher)
+ if !fok {
+ f = nil
+ }
+
+ cn, cnok := w.(http.CloseNotifier)
+ if !cnok {
+ cn = nil
+ }
+
+ w = &compressResponseWriter{
+ Writer: fw,
+ ResponseWriter: w,
+ Hijacker: h,
+ Flusher: f,
+ CloseNotifier: cn,
+ }
+
+ break L
+ }
+ }
+
+ h.ServeHTTP(w, r)
+ })
+}
diff --git a/vendor/github.com/gorilla/handlers/cors.go b/vendor/github.com/gorilla/handlers/cors.go
new file mode 100644
index 000000000..1f92d1ad4
--- /dev/null
+++ b/vendor/github.com/gorilla/handlers/cors.go
@@ -0,0 +1,317 @@
+package handlers
+
+import (
+ "net/http"
+ "strconv"
+ "strings"
+)
+
+// CORSOption represents a functional option for configuring the CORS middleware.
+type CORSOption func(*cors) error
+
+type cors struct {
+ h http.Handler
+ allowedHeaders []string
+ allowedMethods []string
+ allowedOrigins []string
+ allowedOriginValidator OriginValidator
+ exposedHeaders []string
+ maxAge int
+ ignoreOptions bool
+ allowCredentials bool
+}
+
+// OriginValidator takes an origin string and returns whether or not that origin is allowed.
+type OriginValidator func(string) bool
+
+var (
+ defaultCorsMethods = []string{"GET", "HEAD", "POST"}
+ defaultCorsHeaders = []string{"Accept", "Accept-Language", "Content-Language", "Origin"}
+ // (WebKit/Safari v9 sends the Origin header by default in AJAX requests)
+)
+
+const (
+ corsOptionMethod string = "OPTIONS"
+ corsAllowOriginHeader string = "Access-Control-Allow-Origin"
+ corsExposeHeadersHeader string = "Access-Control-Expose-Headers"
+ corsMaxAgeHeader string = "Access-Control-Max-Age"
+ corsAllowMethodsHeader string = "Access-Control-Allow-Methods"
+ corsAllowHeadersHeader string = "Access-Control-Allow-Headers"
+ corsAllowCredentialsHeader string = "Access-Control-Allow-Credentials"
+ corsRequestMethodHeader string = "Access-Control-Request-Method"
+ corsRequestHeadersHeader string = "Access-Control-Request-Headers"
+ corsOriginHeader string = "Origin"
+ corsVaryHeader string = "Vary"
+ corsOriginMatchAll string = "*"
+)
+
+func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ origin := r.Header.Get(corsOriginHeader)
+ if !ch.isOriginAllowed(origin) {
+ ch.h.ServeHTTP(w, r)
+ return
+ }
+
+ if r.Method == corsOptionMethod {
+ if ch.ignoreOptions {
+ ch.h.ServeHTTP(w, r)
+ return
+ }
+
+ if _, ok := r.Header[corsRequestMethodHeader]; !ok {
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ method := r.Header.Get(corsRequestMethodHeader)
+ if !ch.isMatch(method, ch.allowedMethods) {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+
+ requestHeaders := strings.Split(r.Header.Get(corsRequestHeadersHeader), ",")
+ allowedHeaders := []string{}
+ for _, v := range requestHeaders {
+ canonicalHeader := http.CanonicalHeaderKey(strings.TrimSpace(v))
+ if canonicalHeader == "" || ch.isMatch(canonicalHeader, defaultCorsHeaders) {
+ continue
+ }
+
+ if !ch.isMatch(canonicalHeader, ch.allowedHeaders) {
+ w.WriteHeader(http.StatusForbidden)
+ return
+ }
+
+ allowedHeaders = append(allowedHeaders, canonicalHeader)
+ }
+
+ if len(allowedHeaders) > 0 {
+ w.Header().Set(corsAllowHeadersHeader, strings.Join(allowedHeaders, ","))
+ }
+
+ if ch.maxAge > 0 {
+ w.Header().Set(corsMaxAgeHeader, strconv.Itoa(ch.maxAge))
+ }
+
+ if !ch.isMatch(method, defaultCorsMethods) {
+ w.Header().Set(corsAllowMethodsHeader, method)
+ }
+ } else {
+ if len(ch.exposedHeaders) > 0 {
+ w.Header().Set(corsExposeHeadersHeader, strings.Join(ch.exposedHeaders, ","))
+ }
+ }
+
+ if ch.allowCredentials {
+ w.Header().Set(corsAllowCredentialsHeader, "true")
+ }
+
+ if len(ch.allowedOrigins) > 1 {
+ w.Header().Set(corsVaryHeader, corsOriginHeader)
+ }
+
+ w.Header().Set(corsAllowOriginHeader, origin)
+
+ if r.Method == corsOptionMethod {
+ return
+ }
+ ch.h.ServeHTTP(w, r)
+}
+
+// CORS provides Cross-Origin Resource Sharing middleware.
+// Example:
+//
+// import (
+// "net/http"
+//
+// "github.com/gorilla/handlers"
+// "github.com/gorilla/mux"
+// )
+//
+// func main() {
+// r := mux.NewRouter()
+// r.HandleFunc("/users", UserEndpoint)
+// r.HandleFunc("/projects", ProjectEndpoint)
+//
+// // Apply the CORS middleware to our top-level router, with the defaults.
+// http.ListenAndServe(":8000", handlers.CORS()(r))
+// }
+//
+func CORS(opts ...CORSOption) func(http.Handler) http.Handler {
+ return func(h http.Handler) http.Handler {
+ ch := parseCORSOptions(opts...)
+ ch.h = h
+ return ch
+ }
+}
+
+func parseCORSOptions(opts ...CORSOption) *cors {
+ ch := &cors{
+ allowedMethods: defaultCorsMethods,
+ allowedHeaders: defaultCorsHeaders,
+ allowedOrigins: []string{corsOriginMatchAll},
+ }
+
+ for _, option := range opts {
+ option(ch)
+ }
+
+ return ch
+}
+
+//
+// Functional options for configuring CORS.
+//
+
+// AllowedHeaders adds the provided headers to the list of allowed headers in a
+// CORS request.
+// This is an append operation so the headers Accept, Accept-Language,
+// and Content-Language are always allowed.
+// Content-Type must be explicitly declared if accepting Content-Types other than
+// application/x-www-form-urlencoded, multipart/form-data, or text/plain.
+func AllowedHeaders(headers []string) CORSOption {
+ return func(ch *cors) error {
+ for _, v := range headers {
+ normalizedHeader := http.CanonicalHeaderKey(strings.TrimSpace(v))
+ if normalizedHeader == "" {
+ continue
+ }
+
+ if !ch.isMatch(normalizedHeader, ch.allowedHeaders) {
+ ch.allowedHeaders = append(ch.allowedHeaders, normalizedHeader)
+ }
+ }
+
+ return nil
+ }
+}
+
+// AllowedMethods can be used to explicitly allow methods in the
+// Access-Control-Allow-Methods header.
+// This is a replacement operation so you must also
+// pass GET, HEAD, and POST if you wish to support those methods.
+func AllowedMethods(methods []string) CORSOption {
+ return func(ch *cors) error {
+ ch.allowedMethods = []string{}
+ for _, v := range methods {
+ normalizedMethod := strings.ToUpper(strings.TrimSpace(v))
+ if normalizedMethod == "" {
+ continue
+ }
+
+ if !ch.isMatch(normalizedMethod, ch.allowedMethods) {
+ ch.allowedMethods = append(ch.allowedMethods, normalizedMethod)
+ }
+ }
+
+ return nil
+ }
+}
+
+// AllowedOrigins sets the allowed origins for CORS requests, as used in the
+// 'Allow-Access-Control-Origin' HTTP header.
+// Note: Passing in a []string{"*"} will allow any domain.
+func AllowedOrigins(origins []string) CORSOption {
+ return func(ch *cors) error {
+ for _, v := range origins {
+ if v == corsOriginMatchAll {
+ ch.allowedOrigins = []string{corsOriginMatchAll}
+ return nil
+ }
+ }
+
+ ch.allowedOrigins = origins
+ return nil
+ }
+}
+
+// AllowedOriginValidator sets a function for evaluating allowed origins in CORS requests, represented by the
+// 'Allow-Access-Control-Origin' HTTP header.
+func AllowedOriginValidator(fn OriginValidator) CORSOption {
+ return func(ch *cors) error {
+ ch.allowedOriginValidator = fn
+ return nil
+ }
+}
+
+// ExposeHeaders can be used to specify headers that are available
+// and will not be stripped out by the user-agent.
+func ExposedHeaders(headers []string) CORSOption {
+ return func(ch *cors) error {
+ ch.exposedHeaders = []string{}
+ for _, v := range headers {
+ normalizedHeader := http.CanonicalHeaderKey(strings.TrimSpace(v))
+ if normalizedHeader == "" {
+ continue
+ }
+
+ if !ch.isMatch(normalizedHeader, ch.exposedHeaders) {
+ ch.exposedHeaders = append(ch.exposedHeaders, normalizedHeader)
+ }
+ }
+
+ return nil
+ }
+}
+
+// MaxAge determines the maximum age (in seconds) between preflight requests. A
+// maximum of 10 minutes is allowed. An age above this value will default to 10
+// minutes.
+func MaxAge(age int) CORSOption {
+ return func(ch *cors) error {
+ // Maximum of 10 minutes.
+ if age > 600 {
+ age = 600
+ }
+
+ ch.maxAge = age
+ return nil
+ }
+}
+
+// IgnoreOptions causes the CORS middleware to ignore OPTIONS requests, instead
+// passing them through to the next handler. This is useful when your application
+// or framework has a pre-existing mechanism for responding to OPTIONS requests.
+func IgnoreOptions() CORSOption {
+ return func(ch *cors) error {
+ ch.ignoreOptions = true
+ return nil
+ }
+}
+
+// AllowCredentials can be used to specify that the user agent may pass
+// authentication details along with the request.
+func AllowCredentials() CORSOption {
+ return func(ch *cors) error {
+ ch.allowCredentials = true
+ return nil
+ }
+}
+
+func (ch *cors) isOriginAllowed(origin string) bool {
+ if origin == "" {
+ return false
+ }
+
+ if ch.allowedOriginValidator != nil {
+ return ch.allowedOriginValidator(origin)
+ }
+
+ for _, allowedOrigin := range ch.allowedOrigins {
+ if allowedOrigin == origin || allowedOrigin == corsOriginMatchAll {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (ch *cors) isMatch(needle string, haystack []string) bool {
+ for _, v := range haystack {
+ if v == needle {
+ return true
+ }
+ }
+
+ return false
+}
diff --git a/vendor/github.com/gorilla/handlers/doc.go b/vendor/github.com/gorilla/handlers/doc.go
new file mode 100644
index 000000000..944e5a8ae
--- /dev/null
+++ b/vendor/github.com/gorilla/handlers/doc.go
@@ -0,0 +1,9 @@
+/*
+Package handlers is a collection of handlers (aka "HTTP middleware") for use
+with Go's net/http package (or any framework supporting http.Handler).
+
+The package includes handlers for logging in standardised formats, compressing
+HTTP responses, validating content types and other useful tools for manipulating
+requests and responses.
+*/
+package handlers
diff --git a/vendor/github.com/gorilla/handlers/handlers.go b/vendor/github.com/gorilla/handlers/handlers.go
new file mode 100644
index 000000000..9544d2f0a
--- /dev/null
+++ b/vendor/github.com/gorilla/handlers/handlers.go
@@ -0,0 +1,403 @@
+// Copyright 2013 The Gorilla 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 handlers
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "net/url"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+ "unicode/utf8"
+)
+
+// MethodHandler is an http.Handler that dispatches to a handler whose key in the
+// MethodHandler's map matches the name of the HTTP request's method, eg: GET
+//
+// If the request's method is OPTIONS and OPTIONS is not a key in the map then
+// the handler responds with a status of 200 and sets the Allow header to a
+// comma-separated list of available methods.
+//
+// If the request's method doesn't match any of its keys the handler responds
+// with a status of HTTP 405 "Method Not Allowed" and sets the Allow header to a
+// comma-separated list of available methods.
+type MethodHandler map[string]http.Handler
+
+func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ if handler, ok := h[req.Method]; ok {
+ handler.ServeHTTP(w, req)
+ } else {
+ allow := []string{}
+ for k := range h {
+ allow = append(allow, k)
+ }
+ sort.Strings(allow)
+ w.Header().Set("Allow", strings.Join(allow, ", "))
+ if req.Method == "OPTIONS" {
+ w.WriteHeader(http.StatusOK)
+ } else {
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ }
+ }
+}
+
+// loggingHandler is the http.Handler implementation for LoggingHandlerTo and its
+// friends
+type loggingHandler struct {
+ writer io.Writer
+ handler http.Handler
+}
+
+// combinedLoggingHandler is the http.Handler implementation for LoggingHandlerTo
+// and its friends
+type combinedLoggingHandler struct {
+ writer io.Writer
+ handler http.Handler
+}
+
+func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ t := time.Now()
+ logger := makeLogger(w)
+ url := *req.URL
+ h.handler.ServeHTTP(logger, req)
+ writeLog(h.writer, req, url, t, logger.Status(), logger.Size())
+}
+
+func (h combinedLoggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ t := time.Now()
+ logger := makeLogger(w)
+ url := *req.URL
+ h.handler.ServeHTTP(logger, req)
+ writeCombinedLog(h.writer, req, url, t, logger.Status(), logger.Size())
+}
+
+func makeLogger(w http.ResponseWriter) loggingResponseWriter {
+ var logger loggingResponseWriter = &responseLogger{w: w}
+ if _, ok := w.(http.Hijacker); ok {
+ logger = &hijackLogger{responseLogger{w: w}}
+ }
+ h, ok1 := logger.(http.Hijacker)
+ c, ok2 := w.(http.CloseNotifier)
+ if ok1 && ok2 {
+ return hijackCloseNotifier{logger, h, c}
+ }
+ if ok2 {
+ return &closeNotifyWriter{logger, c}
+ }
+ return logger
+}
+
+type loggingResponseWriter interface {
+ http.ResponseWriter
+ http.Flusher
+ Status() int
+ Size() int
+}
+
+// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP
+// status code and body size
+type responseLogger struct {
+ w http.ResponseWriter
+ status int
+ size int
+}
+
+func (l *responseLogger) Header() http.Header {
+ return l.w.Header()
+}
+
+func (l *responseLogger) Write(b []byte) (int, error) {
+ if l.status == 0 {
+ // The status will be StatusOK if WriteHeader has not been called yet
+ l.status = http.StatusOK
+ }
+ size, err := l.w.Write(b)
+ l.size += size
+ return size, err
+}
+
+func (l *responseLogger) WriteHeader(s int) {
+ l.w.WriteHeader(s)
+ l.status = s
+}
+
+func (l *responseLogger) Status() int {
+ return l.status
+}
+
+func (l *responseLogger) Size() int {
+ return l.size
+}
+
+func (l *responseLogger) Flush() {
+ f, ok := l.w.(http.Flusher)
+ if ok {
+ f.Flush()
+ }
+}
+
+type hijackLogger struct {
+ responseLogger
+}
+
+func (l *hijackLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+ h := l.responseLogger.w.(http.Hijacker)
+ conn, rw, err := h.Hijack()
+ if err == nil && l.responseLogger.status == 0 {
+ // The status will be StatusSwitchingProtocols if there was no error and
+ // WriteHeader has not been called yet
+ l.responseLogger.status = http.StatusSwitchingProtocols
+ }
+ return conn, rw, err
+}
+
+type closeNotifyWriter struct {
+ loggingResponseWriter
+ http.CloseNotifier
+}
+
+type hijackCloseNotifier struct {
+ loggingResponseWriter
+ http.Hijacker
+ http.CloseNotifier
+}
+
+const lowerhex = "0123456789abcdef"
+
+func appendQuoted(buf []byte, s string) []byte {
+ var runeTmp [utf8.UTFMax]byte
+ for width := 0; len(s) > 0; s = s[width:] {
+ r := rune(s[0])
+ width = 1
+ if r >= utf8.RuneSelf {
+ r, width = utf8.DecodeRuneInString(s)
+ }
+ if width == 1 && r == utf8.RuneError {
+ buf = append(buf, `\x`...)
+ buf = append(buf, lowerhex[s[0]>>4])
+ buf = append(buf, lowerhex[s[0]&0xF])
+ continue
+ }
+ if r == rune('"') || r == '\\' { // always backslashed
+ buf = append(buf, '\\')
+ buf = append(buf, byte(r))
+ continue
+ }
+ if strconv.IsPrint(r) {
+ n := utf8.EncodeRune(runeTmp[:], r)
+ buf = append(buf, runeTmp[:n]...)
+ continue
+ }
+ switch r {
+ case '\a':
+ buf = append(buf, `\a`...)
+ case '\b':
+ buf = append(buf, `\b`...)
+ case '\f':
+ buf = append(buf, `\f`...)
+ case '\n':
+ buf = append(buf, `\n`...)
+ case '\r':
+ buf = append(buf, `\r`...)
+ case '\t':
+ buf = append(buf, `\t`...)
+ case '\v':
+ buf = append(buf, `\v`...)
+ default:
+ switch {
+ case r < ' ':
+ buf = append(buf, `\x`...)
+ buf = append(buf, lowerhex[s[0]>>4])
+ buf = append(buf, lowerhex[s[0]&0xF])
+ case r > utf8.MaxRune:
+ r = 0xFFFD
+ fallthrough
+ case r < 0x10000:
+ buf = append(buf, `\u`...)
+ for s := 12; s >= 0; s -= 4 {
+ buf = append(buf, lowerhex[r>>uint(s)&0xF])
+ }
+ default:
+ buf = append(buf, `\U`...)
+ for s := 28; s >= 0; s -= 4 {
+ buf = append(buf, lowerhex[r>>uint(s)&0xF])
+ }
+ }
+ }
+ }
+ return buf
+
+}
+
+// buildCommonLogLine builds a log entry for req in Apache Common Log Format.
+// ts is the timestamp with which the entry should be logged.
+// status and size are used to provide the response HTTP status and size.
+func buildCommonLogLine(req *http.Request, url url.URL, ts time.Time, status int, size int) []byte {
+ username := "-"
+ if url.User != nil {
+ if name := url.User.Username(); name != "" {
+ username = name
+ }
+ }
+
+ host, _, err := net.SplitHostPort(req.RemoteAddr)
+
+ if err != nil {
+ host = req.RemoteAddr
+ }
+
+ uri := req.RequestURI
+
+ // Requests using the CONNECT method over HTTP/2.0 must use
+ // the authority field (aka r.Host) to identify the target.
+ // Refer: https://httpwg.github.io/specs/rfc7540.html#CONNECT
+ if req.ProtoMajor == 2 && req.Method == "CONNECT" {
+ uri = req.Host
+ }
+ if uri == "" {
+ uri = url.RequestURI()
+ }
+
+ buf := make([]byte, 0, 3*(len(host)+len(username)+len(req.Method)+len(uri)+len(req.Proto)+50)/2)
+ buf = append(buf, host...)
+ buf = append(buf, " - "...)
+ buf = append(buf, username...)
+ buf = append(buf, " ["...)
+ buf = append(buf, ts.Format("02/Jan/2006:15:04:05 -0700")...)
+ buf = append(buf, `] "`...)
+ buf = append(buf, req.Method...)
+ buf = append(buf, " "...)
+ buf = appendQuoted(buf, uri)
+ buf = append(buf, " "...)
+ buf = append(buf, req.Proto...)
+ buf = append(buf, `" `...)
+ buf = append(buf, strconv.Itoa(status)...)
+ buf = append(buf, " "...)
+ buf = append(buf, strconv.Itoa(size)...)
+ return buf
+}
+
+// writeLog writes a log entry for req to w in Apache Common Log Format.
+// ts is the timestamp with which the entry should be logged.
+// status and size are used to provide the response HTTP status and size.
+func writeLog(w io.Writer, req *http.Request, url url.URL, ts time.Time, status, size int) {
+ buf := buildCommonLogLine(req, url, ts, status, size)
+ buf = append(buf, '\n')
+ w.Write(buf)
+}
+
+// writeCombinedLog writes a log entry for req to w in Apache Combined Log Format.
+// ts is the timestamp with which the entry should be logged.
+// status and size are used to provide the response HTTP status and size.
+func writeCombinedLog(w io.Writer, req *http.Request, url url.URL, ts time.Time, status, size int) {
+ buf := buildCommonLogLine(req, url, ts, status, size)
+ buf = append(buf, ` "`...)
+ buf = appendQuoted(buf, req.Referer())
+ buf = append(buf, `" "`...)
+ buf = appendQuoted(buf, req.UserAgent())
+ buf = append(buf, '"', '\n')
+ w.Write(buf)
+}
+
+// CombinedLoggingHandler return a http.Handler that wraps h and logs requests to out in
+// Apache Combined Log Format.
+//
+// See http://httpd.apache.org/docs/2.2/logs.html#combined for a description of this format.
+//
+// LoggingHandler always sets the ident field of the log to -
+func CombinedLoggingHandler(out io.Writer, h http.Handler) http.Handler {
+ return combinedLoggingHandler{out, h}
+}
+
+// LoggingHandler return a http.Handler that wraps h and logs requests to out in
+// Apache Common Log Format (CLF).
+//
+// See http://httpd.apache.org/docs/2.2/logs.html#common for a description of this format.
+//
+// LoggingHandler always sets the ident field of the log to -
+//
+// Example:
+//
+// r := mux.NewRouter()
+// r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+// w.Write([]byte("This is a catch-all route"))
+// })
+// loggedRouter := handlers.LoggingHandler(os.Stdout, r)
+// http.ListenAndServe(":1123", loggedRouter)
+//
+func LoggingHandler(out io.Writer, h http.Handler) http.Handler {
+ return loggingHandler{out, h}
+}
+
+// isContentType validates the Content-Type header matches the supplied
+// contentType. That is, its type and subtype match.
+func isContentType(h http.Header, contentType string) bool {
+ ct := h.Get("Content-Type")
+ if i := strings.IndexRune(ct, ';'); i != -1 {
+ ct = ct[0:i]
+ }
+ return ct == contentType
+}
+
+// ContentTypeHandler wraps and returns a http.Handler, validating the request
+// content type is compatible with the contentTypes list. It writes a HTTP 415
+// error if that fails.
+//
+// Only PUT, POST, and PATCH requests are considered.
+func ContentTypeHandler(h http.Handler, contentTypes ...string) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if !(r.Method == "PUT" || r.Method == "POST" || r.Method == "PATCH") {
+ h.ServeHTTP(w, r)
+ return
+ }
+
+ for _, ct := range contentTypes {
+ if isContentType(r.Header, ct) {
+ h.ServeHTTP(w, r)
+ return
+ }
+ }
+ http.Error(w, fmt.Sprintf("Unsupported content type %q; expected one of %q", r.Header.Get("Content-Type"), contentTypes), http.StatusUnsupportedMediaType)
+ })
+}
+
+const (
+ // HTTPMethodOverrideHeader is a commonly used
+ // http header to override a request method.
+ HTTPMethodOverrideHeader = "X-HTTP-Method-Override"
+ // HTTPMethodOverrideFormKey is a commonly used
+ // HTML form key to override a request method.
+ HTTPMethodOverrideFormKey = "_method"
+)
+
+// HTTPMethodOverrideHandler wraps and returns a http.Handler which checks for
+// the X-HTTP-Method-Override header or the _method form key, and overrides (if
+// valid) request.Method with its value.
+//
+// This is especially useful for HTTP clients that don't support many http verbs.
+// It isn't secure to override e.g a GET to a POST, so only POST requests are
+// considered. Likewise, the override method can only be a "write" method: PUT,
+// PATCH or DELETE.
+//
+// Form method takes precedence over header method.
+func HTTPMethodOverrideHandler(h http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.Method == "POST" {
+ om := r.FormValue(HTTPMethodOverrideFormKey)
+ if om == "" {
+ om = r.Header.Get(HTTPMethodOverrideHeader)
+ }
+ if om == "PUT" || om == "PATCH" || om == "DELETE" {
+ r.Method = om
+ }
+ }
+ h.ServeHTTP(w, r)
+ })
+}
diff --git a/vendor/github.com/gorilla/handlers/proxy_headers.go b/vendor/github.com/gorilla/handlers/proxy_headers.go
new file mode 100644
index 000000000..268de9c6a
--- /dev/null
+++ b/vendor/github.com/gorilla/handlers/proxy_headers.go
@@ -0,0 +1,113 @@
+package handlers
+
+import (
+ "net/http"
+ "regexp"
+ "strings"
+)
+
+var (
+ // De-facto standard header keys.
+ xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
+ xRealIP = http.CanonicalHeaderKey("X-Real-IP")
+ xForwardedProto = http.CanonicalHeaderKey("X-Forwarded-Scheme")
+)
+
+var (
+ // RFC7239 defines a new "Forwarded: " header designed to replace the
+ // existing use of X-Forwarded-* headers.
+ // e.g. Forwarded: for=192.0.2.60;proto=https;by=203.0.113.43
+ forwarded = http.CanonicalHeaderKey("Forwarded")
+ // Allows for a sub-match of the first value after 'for=' to the next
+ // comma, semi-colon or space. The match is case-insensitive.
+ forRegex = regexp.MustCompile(`(?i)(?:for=)([^(;|,| )]+)`)
+ // Allows for a sub-match for the first instance of scheme (http|https)
+ // prefixed by 'proto='. The match is case-insensitive.
+ protoRegex = regexp.MustCompile(`(?i)(?:proto=)(https|http)`)
+)
+
+// ProxyHeaders inspects common reverse proxy headers and sets the corresponding
+// fields in the HTTP request struct. These are X-Forwarded-For and X-Real-IP
+// for the remote (client) IP address, X-Forwarded-Proto for the scheme
+// (http|https) and the RFC7239 Forwarded header, which may include both client
+// IPs and schemes.
+//
+// NOTE: This middleware should only be used when behind a reverse
+// proxy like nginx, HAProxy or Apache. Reverse proxies that don't (or are
+// configured not to) strip these headers from client requests, or where these
+// headers are accepted "as is" from a remote client (e.g. when Go is not behind
+// a proxy), can manifest as a vulnerability if your application uses these
+// headers for validating the 'trustworthiness' of a request.
+func ProxyHeaders(h http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ // Set the remote IP with the value passed from the proxy.
+ if fwd := getIP(r); fwd != "" {
+ r.RemoteAddr = fwd
+ }
+
+ // Set the scheme (proto) with the value passed from the proxy.
+ if scheme := getScheme(r); scheme != "" {
+ r.URL.Scheme = scheme
+ }
+
+ // Call the next handler in the chain.
+ h.ServeHTTP(w, r)
+ }
+
+ return http.HandlerFunc(fn)
+}
+
+// getIP retrieves the IP from the X-Forwarded-For, X-Real-IP and RFC7239
+// Forwarded headers (in that order).
+func getIP(r *http.Request) string {
+ var addr string
+
+ if fwd := r.Header.Get(xForwardedFor); fwd != "" {
+ // Only grab the first (client) address. Note that '192.168.0.1,
+ // 10.1.1.1' is a valid key for X-Forwarded-For where addresses after
+ // the first may represent forwarding proxies earlier in the chain.
+ s := strings.Index(fwd, ", ")
+ if s == -1 {
+ s = len(fwd)
+ }
+ addr = fwd[:s]
+ } else if fwd := r.Header.Get(xRealIP); fwd != "" {
+ // X-Real-IP should only contain one IP address (the client making the
+ // request).
+ addr = fwd
+ } else if fwd := r.Header.Get(forwarded); fwd != "" {
+ // match should contain at least two elements if the protocol was
+ // specified in the Forwarded header. The first element will always be
+ // the 'for=' capture, which we ignore. In the case of multiple IP
+ // addresses (for=8.8.8.8, 8.8.4.4,172.16.1.20 is valid) we only
+ // extract the first, which should be the client IP.
+ if match := forRegex.FindStringSubmatch(fwd); len(match) > 1 {
+ // IPv6 addresses in Forwarded headers are quoted-strings. We strip
+ // these quotes.
+ addr = strings.Trim(match[1], `"`)
+ }
+ }
+
+ return addr
+}
+
+// getScheme retrieves the scheme from the X-Forwarded-Proto and RFC7239
+// Forwarded headers (in that order).
+func getScheme(r *http.Request) string {
+ var scheme string
+
+ // Retrieve the scheme from X-Forwarded-Proto.
+ if proto := r.Header.Get(xForwardedProto); proto != "" {
+ scheme = strings.ToLower(proto)
+ } else if proto := r.Header.Get(forwarded); proto != "" {
+ // match should contain at least two elements if the protocol was
+ // specified in the Forwarded header. The first element will always be
+ // the 'proto=' capture, which we ignore. In the case of multiple proto
+ // parameters (invalid) we only extract the first.
+ if match := protoRegex.FindStringSubmatch(proto); len(match) > 1 {
+ scheme = strings.ToLower(match[1])
+ }
+ }
+
+ return scheme
+}
diff --git a/vendor/github.com/gorilla/handlers/recovery.go b/vendor/github.com/gorilla/handlers/recovery.go
new file mode 100644
index 000000000..65b7de58a
--- /dev/null
+++ b/vendor/github.com/gorilla/handlers/recovery.go
@@ -0,0 +1,86 @@
+package handlers
+
+import (
+ "log"
+ "net/http"
+ "runtime/debug"
+)
+
+type recoveryHandler struct {
+ handler http.Handler
+ logger *log.Logger
+ printStack bool
+}
+
+// RecoveryOption provides a functional approach to define
+// configuration for a handler; such as setting the logging
+// whether or not to print strack traces on panic.
+type RecoveryOption func(http.Handler)
+
+func parseRecoveryOptions(h http.Handler, opts ...RecoveryOption) http.Handler {
+ for _, option := range opts {
+ option(h)
+ }
+
+ return h
+}
+
+// RecoveryHandler is HTTP middleware that recovers from a panic,
+// logs the panic, writes http.StatusInternalServerError, and
+// continues to the next handler.
+//
+// Example:
+//
+// r := mux.NewRouter()
+// r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+// panic("Unexpected error!")
+// })
+//
+// http.ListenAndServe(":1123", handlers.RecoveryHandler()(r))
+func RecoveryHandler(opts ...RecoveryOption) func(h http.Handler) http.Handler {
+ return func(h http.Handler) http.Handler {
+ r := &recoveryHandler{handler: h}
+ return parseRecoveryOptions(r, opts...)
+ }
+}
+
+// RecoveryLogger is a functional option to override
+// the default logger
+func RecoveryLogger(logger *log.Logger) RecoveryOption {
+ return func(h http.Handler) {
+ r := h.(*recoveryHandler)
+ r.logger = logger
+ }
+}
+
+// PrintRecoveryStack is a functional option to enable
+// or disable printing stack traces on panic.
+func PrintRecoveryStack(print bool) RecoveryOption {
+ return func(h http.Handler) {
+ r := h.(*recoveryHandler)
+ r.printStack = print
+ }
+}
+
+func (h recoveryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ defer func() {
+ if err := recover(); err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ h.log(err)
+ }
+ }()
+
+ h.handler.ServeHTTP(w, req)
+}
+
+func (h recoveryHandler) log(message interface{}) {
+ if h.logger != nil {
+ h.logger.Println(message)
+ } else {
+ log.Println(message)
+ }
+
+ if h.printStack {
+ debug.PrintStack()
+ }
+}
diff --git a/vendor/github.com/gorilla/mux/.travis.yml b/vendor/github.com/gorilla/mux/.travis.yml
new file mode 100644
index 000000000..f4084bd81
--- /dev/null
+++ b/vendor/github.com/gorilla/mux/.travis.yml
@@ -0,0 +1,17 @@
+language: go
+sudo: false
+
+matrix:
+ include:
+ - go: 1.2
+ - go: 1.3
+ - go: 1.4
+ - go: 1.5
+ - go: 1.6
+ - go: tip
+
+script:
+ - go get -t -v ./...
+ - diff -u <(echo -n) <(gofmt -d .)
+ - go tool vet .
+ - go test -v -race ./...
diff --git a/vendor/github.com/gorilla/mux/LICENSE b/vendor/github.com/gorilla/mux/LICENSE
new file mode 100644
index 000000000..0e5fb8728
--- /dev/null
+++ b/vendor/github.com/gorilla/mux/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/gorilla/mux/README.md b/vendor/github.com/gorilla/mux/README.md
new file mode 100644
index 000000000..9516c5191
--- /dev/null
+++ b/vendor/github.com/gorilla/mux/README.md
@@ -0,0 +1,242 @@
+mux
+===
+[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux)
+[![Build Status](https://travis-ci.org/gorilla/mux.svg?branch=master)](https://travis-ci.org/gorilla/mux)
+
+http://www.gorillatoolkit.org/pkg/mux
+
+Package `gorilla/mux` implements a request router and dispatcher.
+
+The name mux stands for "HTTP request multiplexer". Like the standard `http.ServeMux`, `mux.Router` matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions. The main features are:
+
+* Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers.
+* URL hosts and paths can have variables with an optional regular expression.
+* Registered URLs can be built, or "reversed", which helps maintaining references to resources.
+* Routes can be used as subrouters: nested routes are only tested if the parent route matches. This is useful to define groups of routes that share common conditions like a host, a path prefix or other repeated attributes. As a bonus, this optimizes request matching.
+* It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`.
+
+Let's start registering a couple of URL paths and handlers:
+
+```go
+func main() {
+ r := mux.NewRouter()
+ r.HandleFunc("/", HomeHandler)
+ r.HandleFunc("/products", ProductsHandler)
+ r.HandleFunc("/articles", ArticlesHandler)
+ http.Handle("/", r)
+}
+```
+
+Here we register three routes mapping URL paths to handlers. This is equivalent to how `http.HandleFunc()` works: if an incoming request URL matches one of the paths, the corresponding handler is called passing (`http.ResponseWriter`, `*http.Request`) as parameters.
+
+Paths can have variables. They are defined using the format `{name}` or `{name:pattern}`. If a regular expression pattern is not defined, the matched variable will be anything until the next slash. For example:
+
+```go
+r := mux.NewRouter()
+r.HandleFunc("/products/{key}", ProductHandler)
+r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
+r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
+```
+
+The names are used to create a map of route variables which can be retrieved calling `mux.Vars()`:
+
+```go
+vars := mux.Vars(request)
+category := vars["category"]
+```
+
+And this is all you need to know about the basic usage. More advanced options are explained below.
+
+Routes can also be restricted to a domain or subdomain. Just define a host pattern to be matched. They can also have variables:
+
+```go
+r := mux.NewRouter()
+// Only matches if domain is "www.example.com".
+r.Host("www.example.com")
+// Matches a dynamic subdomain.
+r.Host("{subdomain:[a-z]+}.domain.com")
+```
+
+There are several other matchers that can be added. To match path prefixes:
+
+```go
+r.PathPrefix("/products/")
+```
+
+...or HTTP methods:
+
+```go
+r.Methods("GET", "POST")
+```
+
+...or URL schemes:
+
+```go
+r.Schemes("https")
+```
+
+...or header values:
+
+```go
+r.Headers("X-Requested-With", "XMLHttpRequest")
+```
+
+...or query values:
+
+```go
+r.Queries("key", "value")
+```
+
+...or to use a custom matcher function:
+
+```go
+r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
+ return r.ProtoMajor == 0
+})
+```
+
+...and finally, it is possible to combine several matchers in a single route:
+
+```go
+r.HandleFunc("/products", ProductsHandler).
+ Host("www.example.com").
+ Methods("GET").
+ Schemes("http")
+```
+
+Setting the same matching conditions again and again can be boring, so we have a way to group several routes that share the same requirements. We call it "subrouting".
+
+For example, let's say we have several URLs that should only match when the host is `www.example.com`. Create a route for that host and get a "subrouter" from it:
+
+```go
+r := mux.NewRouter()
+s := r.Host("www.example.com").Subrouter()
+```
+
+Then register routes in the subrouter:
+
+```go
+s.HandleFunc("/products/", ProductsHandler)
+s.HandleFunc("/products/{key}", ProductHandler)
+s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
+```
+
+The three URL paths we registered above will only be tested if the domain is `www.example.com`, because the subrouter is tested first. This is not only convenient, but also optimizes request matching. You can create subrouters combining any attribute matchers accepted by a route.
+
+Subrouters can be used to create domain or path "namespaces": you define subrouters in a central place and then parts of the app can register its paths relatively to a given subrouter.
+
+There's one more thing about subroutes. When a subrouter has a path prefix, the inner routes use it as base for their paths:
+
+```go
+r := mux.NewRouter()
+s := r.PathPrefix("/products").Subrouter()
+// "/products/"
+s.HandleFunc("/", ProductsHandler)
+// "/products/{key}/"
+s.HandleFunc("/{key}/", ProductHandler)
+// "/products/{key}/details"
+s.HandleFunc("/{key}/details", ProductDetailsHandler)
+```
+
+Now let's see how to build registered URLs.
+
+Routes can be named. All routes that define a name can have their URLs built, or "reversed". We define a name calling `Name()` on a route. For example:
+
+```go
+r := mux.NewRouter()
+r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
+ Name("article")
+```
+
+To build a URL, get the route and call the `URL()` method, passing a sequence of key/value pairs for the route variables. For the previous route, we would do:
+
+```go
+url, err := r.Get("article").URL("category", "technology", "id", "42")
+```
+
+...and the result will be a `url.URL` with the following path:
+
+```
+"/articles/technology/42"
+```
+
+This also works for host variables:
+
+```go
+r := mux.NewRouter()
+r.Host("{subdomain}.domain.com").
+ Path("/articles/{category}/{id:[0-9]+}").
+ HandlerFunc(ArticleHandler).
+ Name("article")
+
+// url.String() will be "http://news.domain.com/articles/technology/42"
+url, err := r.Get("article").URL("subdomain", "news",
+ "category", "technology",
+ "id", "42")
+```
+
+All variables defined in the route are required, and their values must conform to the corresponding patterns. These requirements guarantee that a generated URL will always match a registered route -- the only exception is for explicitly defined "build-only" routes which never match.
+
+Regex support also exists for matching Headers within a route. For example, we could do:
+
+```go
+r.HeadersRegexp("Content-Type", "application/(text|json)")
+```
+
+...and the route will match both requests with a Content-Type of `application/json` as well as `application/text`
+
+There's also a way to build only the URL host or path for a route: use the methods `URLHost()` or `URLPath()` instead. For the previous route, we would do:
+
+```go
+// "http://news.domain.com/"
+host, err := r.Get("article").URLHost("subdomain", "news")
+
+// "/articles/technology/42"
+path, err := r.Get("article").URLPath("category", "technology", "id", "42")
+```
+
+And if you use subrouters, host and path defined separately can be built as well:
+
+```go
+r := mux.NewRouter()
+s := r.Host("{subdomain}.domain.com").Subrouter()
+s.Path("/articles/{category}/{id:[0-9]+}").
+ HandlerFunc(ArticleHandler).
+ Name("article")
+
+// "http://news.domain.com/articles/technology/42"
+url, err := r.Get("article").URL("subdomain", "news",
+ "category", "technology",
+ "id", "42")
+```
+
+## Full Example
+
+Here's a complete, runnable example of a small `mux` based server:
+
+```go
+package main
+
+import (
+ "net/http"
+
+ "github.com/gorilla/mux"
+)
+
+func YourHandler(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte("Gorilla!\n"))
+}
+
+func main() {
+ r := mux.NewRouter()
+ // Routes consist of a path and a handler function.
+ r.HandleFunc("/", YourHandler)
+
+ // Bind to a port and pass our router in
+ http.ListenAndServe(":8000", r)
+}
+```
+
+## License
+
+BSD licensed. See the LICENSE file for details.
diff --git a/vendor/github.com/gorilla/mux/doc.go b/vendor/github.com/gorilla/mux/doc.go
new file mode 100644
index 000000000..835f5342e
--- /dev/null
+++ b/vendor/github.com/gorilla/mux/doc.go
@@ -0,0 +1,206 @@
+// Copyright 2012 The Gorilla 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 mux implements a request router and dispatcher.
+
+The name mux stands for "HTTP request multiplexer". Like the standard
+http.ServeMux, mux.Router matches incoming requests against a list of
+registered routes and calls a handler for the route that matches the URL
+or other conditions. The main features are:
+
+ * Requests can be matched based on URL host, path, path prefix, schemes,
+ header and query values, HTTP methods or using custom matchers.
+ * URL hosts and paths can have variables with an optional regular
+ expression.
+ * Registered URLs can be built, or "reversed", which helps maintaining
+ references to resources.
+ * Routes can be used as subrouters: nested routes are only tested if the
+ parent route matches. This is useful to define groups of routes that
+ share common conditions like a host, a path prefix or other repeated
+ attributes. As a bonus, this optimizes request matching.
+ * It implements the http.Handler interface so it is compatible with the
+ standard http.ServeMux.
+
+Let's start registering a couple of URL paths and handlers:
+
+ func main() {
+ r := mux.NewRouter()
+ r.HandleFunc("/", HomeHandler)
+ r.HandleFunc("/products", ProductsHandler)
+ r.HandleFunc("/articles", ArticlesHandler)
+ http.Handle("/", r)
+ }
+
+Here we register three routes mapping URL paths to handlers. This is
+equivalent to how http.HandleFunc() works: if an incoming request URL matches
+one of the paths, the corresponding handler is called passing
+(http.ResponseWriter, *http.Request) as parameters.
+
+Paths can have variables. They are defined using the format {name} or
+{name:pattern}. If a regular expression pattern is not defined, the matched
+variable will be anything until the next slash. For example:
+
+ r := mux.NewRouter()
+ r.HandleFunc("/products/{key}", ProductHandler)
+ r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
+ r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
+
+The names are used to create a map of route variables which can be retrieved
+calling mux.Vars():
+
+ vars := mux.Vars(request)
+ category := vars["category"]
+
+And this is all you need to know about the basic usage. More advanced options
+are explained below.
+
+Routes can also be restricted to a domain or subdomain. Just define a host
+pattern to be matched. They can also have variables:
+
+ r := mux.NewRouter()
+ // Only matches if domain is "www.example.com".
+ r.Host("www.example.com")
+ // Matches a dynamic subdomain.
+ r.Host("{subdomain:[a-z]+}.domain.com")
+
+There are several other matchers that can be added. To match path prefixes:
+
+ r.PathPrefix("/products/")
+
+...or HTTP methods:
+
+ r.Methods("GET", "POST")
+
+...or URL schemes:
+
+ r.Schemes("https")
+
+...or header values:
+
+ r.Headers("X-Requested-With", "XMLHttpRequest")
+
+...or query values:
+
+ r.Queries("key", "value")
+
+...or to use a custom matcher function:
+
+ r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
+ return r.ProtoMajor == 0
+ })
+
+...and finally, it is possible to combine several matchers in a single route:
+
+ r.HandleFunc("/products", ProductsHandler).
+ Host("www.example.com").
+ Methods("GET").
+ Schemes("http")
+
+Setting the same matching conditions again and again can be boring, so we have
+a way to group several routes that share the same requirements.
+We call it "subrouting".
+
+For example, let's say we have several URLs that should only match when the
+host is "www.example.com". Create a route for that host and get a "subrouter"
+from it:
+
+ r := mux.NewRouter()
+ s := r.Host("www.example.com").Subrouter()
+
+Then register routes in the subrouter:
+
+ s.HandleFunc("/products/", ProductsHandler)
+ s.HandleFunc("/products/{key}", ProductHandler)
+ s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
+
+The three URL paths we registered above will only be tested if the domain is
+"www.example.com", because the subrouter is tested first. This is not
+only convenient, but also optimizes request matching. You can create
+subrouters combining any attribute matchers accepted by a route.
+
+Subrouters can be used to create domain or path "namespaces": you define
+subrouters in a central place and then parts of the app can register its
+paths relatively to a given subrouter.
+
+There's one more thing about subroutes. When a subrouter has a path prefix,
+the inner routes use it as base for their paths:
+
+ r := mux.NewRouter()
+ s := r.PathPrefix("/products").Subrouter()
+ // "/products/"
+ s.HandleFunc("/", ProductsHandler)
+ // "/products/{key}/"
+ s.HandleFunc("/{key}/", ProductHandler)
+ // "/products/{key}/details"
+ s.HandleFunc("/{key}/details", ProductDetailsHandler)
+
+Now let's see how to build registered URLs.
+
+Routes can be named. All routes that define a name can have their URLs built,
+or "reversed". We define a name calling Name() on a route. For example:
+
+ r := mux.NewRouter()
+ r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
+ Name("article")
+
+To build a URL, get the route and call the URL() method, passing a sequence of
+key/value pairs for the route variables. For the previous route, we would do:
+
+ url, err := r.Get("article").URL("category", "technology", "id", "42")
+
+...and the result will be a url.URL with the following path:
+
+ "/articles/technology/42"
+
+This also works for host variables:
+
+ r := mux.NewRouter()
+ r.Host("{subdomain}.domain.com").
+ Path("/articles/{category}/{id:[0-9]+}").
+ HandlerFunc(ArticleHandler).
+ Name("article")
+
+ // url.String() will be "http://news.domain.com/articles/technology/42"
+ url, err := r.Get("article").URL("subdomain", "news",
+ "category", "technology",
+ "id", "42")
+
+All variables defined in the route are required, and their values must
+conform to the corresponding patterns. These requirements guarantee that a
+generated URL will always match a registered route -- the only exception is
+for explicitly defined "build-only" routes which never match.
+
+Regex support also exists for matching Headers within a route. For example, we could do:
+
+ r.HeadersRegexp("Content-Type", "application/(text|json)")
+
+...and the route will match both requests with a Content-Type of `application/json` as well as
+`application/text`
+
+There's also a way to build only the URL host or path for a route:
+use the methods URLHost() or URLPath() instead. For the previous route,
+we would do:
+
+ // "http://news.domain.com/"
+ host, err := r.Get("article").URLHost("subdomain", "news")
+
+ // "/articles/technology/42"
+ path, err := r.Get("article").URLPath("category", "technology", "id", "42")
+
+And if you use subrouters, host and path defined separately can be built
+as well:
+
+ r := mux.NewRouter()
+ s := r.Host("{subdomain}.domain.com").Subrouter()
+ s.Path("/articles/{category}/{id:[0-9]+}").
+ HandlerFunc(ArticleHandler).
+ Name("article")
+
+ // "http://news.domain.com/articles/technology/42"
+ url, err := r.Get("article").URL("subdomain", "news",
+ "category", "technology",
+ "id", "42")
+*/
+package mux
diff --git a/vendor/github.com/gorilla/mux/mux.go b/vendor/github.com/gorilla/mux/mux.go
new file mode 100644
index 000000000..94f5ddd9c
--- /dev/null
+++ b/vendor/github.com/gorilla/mux/mux.go
@@ -0,0 +1,499 @@
+// Copyright 2012 The Gorilla 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 mux
+
+import (
+ "errors"
+ "fmt"
+ "net/http"
+ "path"
+ "regexp"
+
+ "github.com/gorilla/context"
+)
+
+// NewRouter returns a new router instance.
+func NewRouter() *Router {
+ return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
+}
+
+// Router registers routes to be matched and dispatches a handler.
+//
+// It implements the http.Handler interface, so it can be registered to serve
+// requests:
+//
+// var router = mux.NewRouter()
+//
+// func main() {
+// http.Handle("/", router)
+// }
+//
+// Or, for Google App Engine, register it in a init() function:
+//
+// func init() {
+// http.Handle("/", router)
+// }
+//
+// This will send all incoming requests to the router.
+type Router struct {
+ // Configurable Handler to be used when no route matches.
+ NotFoundHandler http.Handler
+ // Parent route, if this is a subrouter.
+ parent parentRoute
+ // Routes to be matched, in order.
+ routes []*Route
+ // Routes by name for URL building.
+ namedRoutes map[string]*Route
+ // See Router.StrictSlash(). This defines the flag for new routes.
+ strictSlash bool
+ // See Router.SkipClean(). This defines the flag for new routes.
+ skipClean bool
+ // If true, do not clear the request context after handling the request
+ KeepContext bool
+}
+
+// Match matches registered routes against the request.
+func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
+ for _, route := range r.routes {
+ if route.Match(req, match) {
+ return true
+ }
+ }
+
+ // Closest match for a router (includes sub-routers)
+ if r.NotFoundHandler != nil {
+ match.Handler = r.NotFoundHandler
+ return true
+ }
+ return false
+}
+
+// ServeHTTP dispatches the handler registered in the matched route.
+//
+// When there is a match, the route variables can be retrieved calling
+// mux.Vars(request).
+func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ if !r.skipClean {
+ // Clean path to canonical form and redirect.
+ if p := cleanPath(req.URL.Path); p != req.URL.Path {
+
+ // Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query.
+ // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue:
+ // http://code.google.com/p/go/issues/detail?id=5252
+ url := *req.URL
+ url.Path = p
+ p = url.String()
+
+ w.Header().Set("Location", p)
+ w.WriteHeader(http.StatusMovedPermanently)
+ return
+ }
+ }
+ var match RouteMatch
+ var handler http.Handler
+ if r.Match(req, &match) {
+ handler = match.Handler
+ setVars(req, match.Vars)
+ setCurrentRoute(req, match.Route)
+ }
+ if handler == nil {
+ handler = http.NotFoundHandler()
+ }
+ if !r.KeepContext {
+ defer context.Clear(req)
+ }
+ handler.ServeHTTP(w, req)
+}
+
+// Get returns a route registered with the given name.
+func (r *Router) Get(name string) *Route {
+ return r.getNamedRoutes()[name]
+}
+
+// GetRoute returns a route registered with the given name. This method
+// was renamed to Get() and remains here for backwards compatibility.
+func (r *Router) GetRoute(name string) *Route {
+ return r.getNamedRoutes()[name]
+}
+
+// StrictSlash defines the trailing slash behavior for new routes. The initial
+// value is false.
+//
+// When true, if the route path is "/path/", accessing "/path" will redirect
+// to the former and vice versa. In other words, your application will always
+// see the path as specified in the route.
+//
+// When false, if the route path is "/path", accessing "/path/" will not match
+// this route and vice versa.
+//
+// Special case: when a route sets a path prefix using the PathPrefix() method,
+// strict slash is ignored for that route because the redirect behavior can't
+// be determined from a prefix alone. However, any subrouters created from that
+// route inherit the original StrictSlash setting.
+func (r *Router) StrictSlash(value bool) *Router {
+ r.strictSlash = value
+ return r
+}
+
+// SkipClean defines the path cleaning behaviour for new routes. The initial
+// value is false. Users should be careful about which routes are not cleaned
+//
+// When true, if the route path is "/path//to", it will remain with the double
+// slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/
+//
+// When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will
+// become /fetch/http/xkcd.com/534
+func (r *Router) SkipClean(value bool) *Router {
+ r.skipClean = value
+ return r
+}
+
+// ----------------------------------------------------------------------------
+// parentRoute
+// ----------------------------------------------------------------------------
+
+// getNamedRoutes returns the map where named routes are registered.
+func (r *Router) getNamedRoutes() map[string]*Route {
+ if r.namedRoutes == nil {
+ if r.parent != nil {
+ r.namedRoutes = r.parent.getNamedRoutes()
+ } else {
+ r.namedRoutes = make(map[string]*Route)
+ }
+ }
+ return r.namedRoutes
+}
+
+// getRegexpGroup returns regexp definitions from the parent route, if any.
+func (r *Router) getRegexpGroup() *routeRegexpGroup {
+ if r.parent != nil {
+ return r.parent.getRegexpGroup()
+ }
+ return nil
+}
+
+func (r *Router) buildVars(m map[string]string) map[string]string {
+ if r.parent != nil {
+ m = r.parent.buildVars(m)
+ }
+ return m
+}
+
+// ----------------------------------------------------------------------------
+// Route factories
+// ----------------------------------------------------------------------------
+
+// NewRoute registers an empty route.
+func (r *Router) NewRoute() *Route {
+ route := &Route{parent: r, strictSlash: r.strictSlash, skipClean: r.skipClean}
+ r.routes = append(r.routes, route)
+ return route
+}
+
+// Handle registers a new route with a matcher for the URL path.
+// See Route.Path() and Route.Handler().
+func (r *Router) Handle(path string, handler http.Handler) *Route {
+ return r.NewRoute().Path(path).Handler(handler)
+}
+
+// HandleFunc registers a new route with a matcher for the URL path.
+// See Route.Path() and Route.HandlerFunc().
+func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
+ *http.Request)) *Route {
+ return r.NewRoute().Path(path).HandlerFunc(f)
+}
+
+// Headers registers a new route with a matcher for request header values.
+// See Route.Headers().
+func (r *Router) Headers(pairs ...string) *Route {
+ return r.NewRoute().Headers(pairs...)
+}
+
+// Host registers a new route with a matcher for the URL host.
+// See Route.Host().
+func (r *Router) Host(tpl string) *Route {
+ return r.NewRoute().Host(tpl)
+}
+
+// MatcherFunc registers a new route with a custom matcher function.
+// See Route.MatcherFunc().
+func (r *Router) MatcherFunc(f MatcherFunc) *Route {
+ return r.NewRoute().MatcherFunc(f)
+}
+
+// Methods registers a new route with a matcher for HTTP methods.
+// See Route.Methods().
+func (r *Router) Methods(methods ...string) *Route {
+ return r.NewRoute().Methods(methods...)
+}
+
+// Path registers a new route with a matcher for the URL path.
+// See Route.Path().
+func (r *Router) Path(tpl string) *Route {
+ return r.NewRoute().Path(tpl)
+}
+
+// PathPrefix registers a new route with a matcher for the URL path prefix.
+// See Route.PathPrefix().
+func (r *Router) PathPrefix(tpl string) *Route {
+ return r.NewRoute().PathPrefix(tpl)
+}
+
+// Queries registers a new route with a matcher for URL query values.
+// See Route.Queries().
+func (r *Router) Queries(pairs ...string) *Route {
+ return r.NewRoute().Queries(pairs...)
+}
+
+// Schemes registers a new route with a matcher for URL schemes.
+// See Route.Schemes().
+func (r *Router) Schemes(schemes ...string) *Route {
+ return r.NewRoute().Schemes(schemes...)
+}
+
+// BuildVarsFunc registers a new route with a custom function for modifying
+// route variables before building a URL.
+func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route {
+ return r.NewRoute().BuildVarsFunc(f)
+}
+
+// Walk walks the router and all its sub-routers, calling walkFn for each route
+// in the tree. The routes are walked in the order they were added. Sub-routers
+// are explored depth-first.
+func (r *Router) Walk(walkFn WalkFunc) error {
+ return r.walk(walkFn, []*Route{})
+}
+
+// SkipRouter is used as a return value from WalkFuncs to indicate that the
+// router that walk is about to descend down to should be skipped.
+var SkipRouter = errors.New("skip this router")
+
+// WalkFunc is the type of the function called for each route visited by Walk.
+// At every invocation, it is given the current route, and the current router,
+// and a list of ancestor routes that lead to the current route.
+type WalkFunc func(route *Route, router *Router, ancestors []*Route) error
+
+func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error {
+ for _, t := range r.routes {
+ if t.regexp == nil || t.regexp.path == nil || t.regexp.path.template == "" {
+ continue
+ }
+
+ err := walkFn(t, r, ancestors)
+ if err == SkipRouter {
+ continue
+ }
+ for _, sr := range t.matchers {
+ if h, ok := sr.(*Router); ok {
+ err := h.walk(walkFn, ancestors)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ if h, ok := t.handler.(*Router); ok {
+ ancestors = append(ancestors, t)
+ err := h.walk(walkFn, ancestors)
+ if err != nil {
+ return err
+ }
+ ancestors = ancestors[:len(ancestors)-1]
+ }
+ }
+ return nil
+}
+
+// ----------------------------------------------------------------------------
+// Context
+// ----------------------------------------------------------------------------
+
+// RouteMatch stores information about a matched route.
+type RouteMatch struct {
+ Route *Route
+ Handler http.Handler
+ Vars map[string]string
+}
+
+type contextKey int
+
+const (
+ varsKey contextKey = iota
+ routeKey
+)
+
+// Vars returns the route variables for the current request, if any.
+func Vars(r *http.Request) map[string]string {
+ if rv := context.Get(r, varsKey); rv != nil {
+ return rv.(map[string]string)
+ }
+ return nil
+}
+
+// CurrentRoute returns the matched route for the current request, if any.
+// This only works when called inside the handler of the matched route
+// because the matched route is stored in the request context which is cleared
+// after the handler returns, unless the KeepContext option is set on the
+// Router.
+func CurrentRoute(r *http.Request) *Route {
+ if rv := context.Get(r, routeKey); rv != nil {
+ return rv.(*Route)
+ }
+ return nil
+}
+
+func setVars(r *http.Request, val interface{}) {
+ if val != nil {
+ context.Set(r, varsKey, val)
+ }
+}
+
+func setCurrentRoute(r *http.Request, val interface{}) {
+ if val != nil {
+ context.Set(r, routeKey, val)
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Helpers
+// ----------------------------------------------------------------------------
+
+// cleanPath returns the canonical path for p, eliminating . and .. elements.
+// Borrowed from the net/http package.
+func cleanPath(p string) string {
+ if p == "" {
+ return "/"
+ }
+ if p[0] != '/' {
+ p = "/" + p
+ }
+ np := path.Clean(p)
+ // path.Clean removes trailing slash except for root;
+ // put the trailing slash back if necessary.
+ if p[len(p)-1] == '/' && np != "/" {
+ np += "/"
+ }
+
+ return np
+}
+
+// uniqueVars returns an error if two slices contain duplicated strings.
+func uniqueVars(s1, s2 []string) error {
+ for _, v1 := range s1 {
+ for _, v2 := range s2 {
+ if v1 == v2 {
+ return fmt.Errorf("mux: duplicated route variable %q", v2)
+ }
+ }
+ }
+ return nil
+}
+
+// checkPairs returns the count of strings passed in, and an error if
+// the count is not an even number.
+func checkPairs(pairs ...string) (int, error) {
+ length := len(pairs)
+ if length%2 != 0 {
+ return length, fmt.Errorf(
+ "mux: number of parameters must be multiple of 2, got %v", pairs)
+ }
+ return length, nil
+}
+
+// mapFromPairsToString converts variadic string parameters to a
+// string to string map.
+func mapFromPairsToString(pairs ...string) (map[string]string, error) {
+ length, err := checkPairs(pairs...)
+ if err != nil {
+ return nil, err
+ }
+ m := make(map[string]string, length/2)
+ for i := 0; i < length; i += 2 {
+ m[pairs[i]] = pairs[i+1]
+ }
+ return m, nil
+}
+
+// mapFromPairsToRegex converts variadic string paramers to a
+// string to regex map.
+func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) {
+ length, err := checkPairs(pairs...)
+ if err != nil {
+ return nil, err
+ }
+ m := make(map[string]*regexp.Regexp, length/2)
+ for i := 0; i < length; i += 2 {
+ regex, err := regexp.Compile(pairs[i+1])
+ if err != nil {
+ return nil, err
+ }
+ m[pairs[i]] = regex
+ }
+ return m, nil
+}
+
+// matchInArray returns true if the given string value is in the array.
+func matchInArray(arr []string, value string) bool {
+ for _, v := range arr {
+ if v == value {
+ return true
+ }
+ }
+ return false
+}
+
+// matchMapWithString returns true if the given key/value pairs exist in a given map.
+func matchMapWithString(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool {
+ for k, v := range toCheck {
+ // Check if key exists.
+ if canonicalKey {
+ k = http.CanonicalHeaderKey(k)
+ }
+ if values := toMatch[k]; values == nil {
+ return false
+ } else if v != "" {
+ // If value was defined as an empty string we only check that the
+ // key exists. Otherwise we also check for equality.
+ valueExists := false
+ for _, value := range values {
+ if v == value {
+ valueExists = true
+ break
+ }
+ }
+ if !valueExists {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+// matchMapWithRegex returns true if the given key/value pairs exist in a given map compiled against
+// the given regex
+func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]string, canonicalKey bool) bool {
+ for k, v := range toCheck {
+ // Check if key exists.
+ if canonicalKey {
+ k = http.CanonicalHeaderKey(k)
+ }
+ if values := toMatch[k]; values == nil {
+ return false
+ } else if v != nil {
+ // If value was defined as an empty string we only check that the
+ // key exists. Otherwise we also check for equality.
+ valueExists := false
+ for _, value := range values {
+ if v.MatchString(value) {
+ valueExists = true
+ break
+ }
+ }
+ if !valueExists {
+ return false
+ }
+ }
+ }
+ return true
+}
diff --git a/vendor/github.com/gorilla/mux/regexp.go b/vendor/github.com/gorilla/mux/regexp.go
new file mode 100644
index 000000000..08710bc98
--- /dev/null
+++ b/vendor/github.com/gorilla/mux/regexp.go
@@ -0,0 +1,312 @@
+// Copyright 2012 The Gorilla 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 mux
+
+import (
+ "bytes"
+ "fmt"
+ "net/http"
+ "net/url"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+// newRouteRegexp parses a route template and returns a routeRegexp,
+// used to match a host, a path or a query string.
+//
+// It will extract named variables, assemble a regexp to be matched, create
+// a "reverse" template to build URLs and compile regexps to validate variable
+// values used in URL building.
+//
+// Previously we accepted only Python-like identifiers for variable
+// names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that
+// name and pattern can't be empty, and names can't contain a colon.
+func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash bool) (*routeRegexp, error) {
+ // Check if it is well-formed.
+ idxs, errBraces := braceIndices(tpl)
+ if errBraces != nil {
+ return nil, errBraces
+ }
+ // Backup the original.
+ template := tpl
+ // Now let's parse it.
+ defaultPattern := "[^/]+"
+ if matchQuery {
+ defaultPattern = "[^?&]*"
+ } else if matchHost {
+ defaultPattern = "[^.]+"
+ matchPrefix = false
+ }
+ // Only match strict slash if not matching
+ if matchPrefix || matchHost || matchQuery {
+ strictSlash = false
+ }
+ // Set a flag for strictSlash.
+ endSlash := false
+ if strictSlash && strings.HasSuffix(tpl, "/") {
+ tpl = tpl[:len(tpl)-1]
+ endSlash = true
+ }
+ varsN := make([]string, len(idxs)/2)
+ varsR := make([]*regexp.Regexp, len(idxs)/2)
+ pattern := bytes.NewBufferString("")
+ pattern.WriteByte('^')
+ reverse := bytes.NewBufferString("")
+ var end int
+ var err error
+ for i := 0; i < len(idxs); i += 2 {
+ // Set all values we are interested in.
+ raw := tpl[end:idxs[i]]
+ end = idxs[i+1]
+ parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2)
+ name := parts[0]
+ patt := defaultPattern
+ if len(parts) == 2 {
+ patt = parts[1]
+ }
+ // Name or pattern can't be empty.
+ if name == "" || patt == "" {
+ return nil, fmt.Errorf("mux: missing name or pattern in %q",
+ tpl[idxs[i]:end])
+ }
+ // Build the regexp pattern.
+ fmt.Fprintf(pattern, "%s(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(i/2), patt)
+
+ // Build the reverse template.
+ fmt.Fprintf(reverse, "%s%%s", raw)
+
+ // Append variable name and compiled pattern.
+ varsN[i/2] = name
+ varsR[i/2], err = regexp.Compile(fmt.Sprintf("^%s$", patt))
+ if err != nil {
+ return nil, err
+ }
+ }
+ // Add the remaining.
+ raw := tpl[end:]
+ pattern.WriteString(regexp.QuoteMeta(raw))
+ if strictSlash {
+ pattern.WriteString("[/]?")
+ }
+ if matchQuery {
+ // Add the default pattern if the query value is empty
+ if queryVal := strings.SplitN(template, "=", 2)[1]; queryVal == "" {
+ pattern.WriteString(defaultPattern)
+ }
+ }
+ if !matchPrefix {
+ pattern.WriteByte('$')
+ }
+ reverse.WriteString(raw)
+ if endSlash {
+ reverse.WriteByte('/')
+ }
+ // Compile full regexp.
+ reg, errCompile := regexp.Compile(pattern.String())
+ if errCompile != nil {
+ return nil, errCompile
+ }
+ // Done!
+ return &routeRegexp{
+ template: template,
+ matchHost: matchHost,
+ matchQuery: matchQuery,
+ strictSlash: strictSlash,
+ regexp: reg,
+ reverse: reverse.String(),
+ varsN: varsN,
+ varsR: varsR,
+ }, nil
+}
+
+// routeRegexp stores a regexp to match a host or path and information to
+// collect and validate route variables.
+type routeRegexp struct {
+ // The unmodified template.
+ template string
+ // True for host match, false for path or query string match.
+ matchHost bool
+ // True for query string match, false for path and host match.
+ matchQuery bool
+ // The strictSlash value defined on the route, but disabled if PathPrefix was used.
+ strictSlash bool
+ // Expanded regexp.
+ regexp *regexp.Regexp
+ // Reverse template.
+ reverse string
+ // Variable names.
+ varsN []string
+ // Variable regexps (validators).
+ varsR []*regexp.Regexp
+}
+
+// Match matches the regexp against the URL host or path.
+func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
+ if !r.matchHost {
+ if r.matchQuery {
+ return r.matchQueryString(req)
+ }
+
+ return r.regexp.MatchString(req.URL.Path)
+ }
+
+ return r.regexp.MatchString(getHost(req))
+}
+
+// url builds a URL part using the given values.
+func (r *routeRegexp) url(values map[string]string) (string, error) {
+ urlValues := make([]interface{}, len(r.varsN))
+ for k, v := range r.varsN {
+ value, ok := values[v]
+ if !ok {
+ return "", fmt.Errorf("mux: missing route variable %q", v)
+ }
+ urlValues[k] = value
+ }
+ rv := fmt.Sprintf(r.reverse, urlValues...)
+ if !r.regexp.MatchString(rv) {
+ // The URL is checked against the full regexp, instead of checking
+ // individual variables. This is faster but to provide a good error
+ // message, we check individual regexps if the URL doesn't match.
+ for k, v := range r.varsN {
+ if !r.varsR[k].MatchString(values[v]) {
+ return "", fmt.Errorf(
+ "mux: variable %q doesn't match, expected %q", values[v],
+ r.varsR[k].String())
+ }
+ }
+ }
+ return rv, nil
+}
+
+// getURLQuery returns a single query parameter from a request URL.
+// For a URL with foo=bar&baz=ding, we return only the relevant key
+// value pair for the routeRegexp.
+func (r *routeRegexp) getURLQuery(req *http.Request) string {
+ if !r.matchQuery {
+ return ""
+ }
+ templateKey := strings.SplitN(r.template, "=", 2)[0]
+ for key, vals := range req.URL.Query() {
+ if key == templateKey && len(vals) > 0 {
+ return key + "=" + vals[0]
+ }
+ }
+ return ""
+}
+
+func (r *routeRegexp) matchQueryString(req *http.Request) bool {
+ return r.regexp.MatchString(r.getURLQuery(req))
+}
+
+// braceIndices returns the first level curly brace indices from a string.
+// It returns an error in case of unbalanced braces.
+func braceIndices(s string) ([]int, error) {
+ var level, idx int
+ var idxs []int
+ for i := 0; i < len(s); i++ {
+ switch s[i] {
+ case '{':
+ if level++; level == 1 {
+ idx = i
+ }
+ case '}':
+ if level--; level == 0 {
+ idxs = append(idxs, idx, i+1)
+ } else if level < 0 {
+ return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
+ }
+ }
+ }
+ if level != 0 {
+ return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
+ }
+ return idxs, nil
+}
+
+// varGroupName builds a capturing group name for the indexed variable.
+func varGroupName(idx int) string {
+ return "v" + strconv.Itoa(idx)
+}
+
+// ----------------------------------------------------------------------------
+// routeRegexpGroup
+// ----------------------------------------------------------------------------
+
+// routeRegexpGroup groups the route matchers that carry variables.
+type routeRegexpGroup struct {
+ host *routeRegexp
+ path *routeRegexp
+ queries []*routeRegexp
+}
+
+// setMatch extracts the variables from the URL once a route matches.
+func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
+ // Store host variables.
+ if v.host != nil {
+ host := getHost(req)
+ matches := v.host.regexp.FindStringSubmatchIndex(host)
+ if len(matches) > 0 {
+ extractVars(host, matches, v.host.varsN, m.Vars)
+ }
+ }
+ // Store path variables.
+ if v.path != nil {
+ matches := v.path.regexp.FindStringSubmatchIndex(req.URL.Path)
+ if len(matches) > 0 {
+ extractVars(req.URL.Path, matches, v.path.varsN, m.Vars)
+ // Check if we should redirect.
+ if v.path.strictSlash {
+ p1 := strings.HasSuffix(req.URL.Path, "/")
+ p2 := strings.HasSuffix(v.path.template, "/")
+ if p1 != p2 {
+ u, _ := url.Parse(req.URL.String())
+ if p1 {
+ u.Path = u.Path[:len(u.Path)-1]
+ } else {
+ u.Path += "/"
+ }
+ m.Handler = http.RedirectHandler(u.String(), 301)
+ }
+ }
+ }
+ }
+ // Store query string variables.
+ for _, q := range v.queries {
+ queryURL := q.getURLQuery(req)
+ matches := q.regexp.FindStringSubmatchIndex(queryURL)
+ if len(matches) > 0 {
+ extractVars(queryURL, matches, q.varsN, m.Vars)
+ }
+ }
+}
+
+// getHost tries its best to return the request host.
+func getHost(r *http.Request) string {
+ if r.URL.IsAbs() {
+ return r.URL.Host
+ }
+ host := r.Host
+ // Slice off any port information.
+ if i := strings.Index(host, ":"); i != -1 {
+ host = host[:i]
+ }
+ return host
+
+}
+
+func extractVars(input string, matches []int, names []string, output map[string]string) {
+ matchesCount := 0
+ prevEnd := -1
+ for i := 2; i < len(matches) && matchesCount < len(names); i += 2 {
+ if prevEnd < matches[i+1] {
+ value := input[matches[i]:matches[i+1]]
+ output[names[matchesCount]] = value
+ prevEnd = matches[i+1]
+ matchesCount++
+ }
+ }
+}
diff --git a/vendor/github.com/gorilla/mux/route.go b/vendor/github.com/gorilla/mux/route.go
new file mode 100644
index 000000000..6c53f9f1d
--- /dev/null
+++ b/vendor/github.com/gorilla/mux/route.go
@@ -0,0 +1,634 @@
+// Copyright 2012 The Gorilla 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 mux
+
+import (
+ "errors"
+ "fmt"
+ "net/http"
+ "net/url"
+ "regexp"
+ "strings"
+)
+
+// Route stores information to match a request and build URLs.
+type Route struct {
+ // Parent where the route was registered (a Router).
+ parent parentRoute
+ // Request handler for the route.
+ handler http.Handler
+ // List of matchers.
+ matchers []matcher
+ // Manager for the variables from host and path.
+ regexp *routeRegexpGroup
+ // If true, when the path pattern is "/path/", accessing "/path" will
+ // redirect to the former and vice versa.
+ strictSlash bool
+ // If true, when the path pattern is "/path//to", accessing "/path//to"
+ // will not redirect
+ skipClean bool
+ // If true, this route never matches: it is only used to build URLs.
+ buildOnly bool
+ // The name used to build URLs.
+ name string
+ // Error resulted from building a route.
+ err error
+
+ buildVarsFunc BuildVarsFunc
+}
+
+func (r *Route) SkipClean() bool {
+ return r.skipClean
+}
+
+// Match matches the route against the request.
+func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
+ if r.buildOnly || r.err != nil {
+ return false
+ }
+ // Match everything.
+ for _, m := range r.matchers {
+ if matched := m.Match(req, match); !matched {
+ return false
+ }
+ }
+ // Yay, we have a match. Let's collect some info about it.
+ if match.Route == nil {
+ match.Route = r
+ }
+ if match.Handler == nil {
+ match.Handler = r.handler
+ }
+ if match.Vars == nil {
+ match.Vars = make(map[string]string)
+ }
+ // Set variables.
+ if r.regexp != nil {
+ r.regexp.setMatch(req, match, r)
+ }
+ return true
+}
+
+// ----------------------------------------------------------------------------
+// Route attributes
+// ----------------------------------------------------------------------------
+
+// GetError returns an error resulted from building the route, if any.
+func (r *Route) GetError() error {
+ return r.err
+}
+
+// BuildOnly sets the route to never match: it is only used to build URLs.
+func (r *Route) BuildOnly() *Route {
+ r.buildOnly = true
+ return r
+}
+
+// Handler --------------------------------------------------------------------
+
+// Handler sets a handler for the route.
+func (r *Route) Handler(handler http.Handler) *Route {
+ if r.err == nil {
+ r.handler = handler
+ }
+ return r
+}
+
+// HandlerFunc sets a handler function for the route.
+func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
+ return r.Handler(http.HandlerFunc(f))
+}
+
+// GetHandler returns the handler for the route, if any.
+func (r *Route) GetHandler() http.Handler {
+ return r.handler
+}
+
+// Name -----------------------------------------------------------------------
+
+// Name sets the name for the route, used to build URLs.
+// If the name was registered already it will be overwritten.
+func (r *Route) Name(name string) *Route {
+ if r.name != "" {
+ r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
+ r.name, name)
+ }
+ if r.err == nil {
+ r.name = name
+ r.getNamedRoutes()[name] = r
+ }
+ return r
+}
+
+// GetName returns the name for the route, if any.
+func (r *Route) GetName() string {
+ return r.name
+}
+
+// ----------------------------------------------------------------------------
+// Matchers
+// ----------------------------------------------------------------------------
+
+// matcher types try to match a request.
+type matcher interface {
+ Match(*http.Request, *RouteMatch) bool
+}
+
+// addMatcher adds a matcher to the route.
+func (r *Route) addMatcher(m matcher) *Route {
+ if r.err == nil {
+ r.matchers = append(r.matchers, m)
+ }
+ return r
+}
+
+// addRegexpMatcher adds a host or path matcher and builder to a route.
+func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery bool) error {
+ if r.err != nil {
+ return r.err
+ }
+ r.regexp = r.getRegexpGroup()
+ if !matchHost && !matchQuery {
+ if len(tpl) == 0 || tpl[0] != '/' {
+ return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
+ }
+ if r.regexp.path != nil {
+ tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
+ }
+ }
+ rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, matchQuery, r.strictSlash)
+ if err != nil {
+ return err
+ }
+ for _, q := range r.regexp.queries {
+ if err = uniqueVars(rr.varsN, q.varsN); err != nil {
+ return err
+ }
+ }
+ if matchHost {
+ if r.regexp.path != nil {
+ if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
+ return err
+ }
+ }
+ r.regexp.host = rr
+ } else {
+ if r.regexp.host != nil {
+ if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
+ return err
+ }
+ }
+ if matchQuery {
+ r.regexp.queries = append(r.regexp.queries, rr)
+ } else {
+ r.regexp.path = rr
+ }
+ }
+ r.addMatcher(rr)
+ return nil
+}
+
+// Headers --------------------------------------------------------------------
+
+// headerMatcher matches the request against header values.
+type headerMatcher map[string]string
+
+func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
+ return matchMapWithString(m, r.Header, true)
+}
+
+// Headers adds a matcher for request header values.
+// It accepts a sequence of key/value pairs to be matched. For example:
+//
+// r := mux.NewRouter()
+// r.Headers("Content-Type", "application/json",
+// "X-Requested-With", "XMLHttpRequest")
+//
+// The above route will only match if both request header values match.
+// If the value is an empty string, it will match any value if the key is set.
+func (r *Route) Headers(pairs ...string) *Route {
+ if r.err == nil {
+ var headers map[string]string
+ headers, r.err = mapFromPairsToString(pairs...)
+ return r.addMatcher(headerMatcher(headers))
+ }
+ return r
+}
+
+// headerRegexMatcher matches the request against the route given a regex for the header
+type headerRegexMatcher map[string]*regexp.Regexp
+
+func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool {
+ return matchMapWithRegex(m, r.Header, true)
+}
+
+// HeadersRegexp accepts a sequence of key/value pairs, where the value has regex
+// support. For example:
+//
+// r := mux.NewRouter()
+// r.HeadersRegexp("Content-Type", "application/(text|json)",
+// "X-Requested-With", "XMLHttpRequest")
+//
+// The above route will only match if both the request header matches both regular expressions.
+// It the value is an empty string, it will match any value if the key is set.
+func (r *Route) HeadersRegexp(pairs ...string) *Route {
+ if r.err == nil {
+ var headers map[string]*regexp.Regexp
+ headers, r.err = mapFromPairsToRegex(pairs...)
+ return r.addMatcher(headerRegexMatcher(headers))
+ }
+ return r
+}
+
+// Host -----------------------------------------------------------------------
+
+// Host adds a matcher for the URL host.
+// It accepts a template with zero or more URL variables enclosed by {}.
+// Variables can define an optional regexp pattern to be matched:
+//
+// - {name} matches anything until the next dot.
+//
+// - {name:pattern} matches the given regexp pattern.
+//
+// For example:
+//
+// r := mux.NewRouter()
+// r.Host("www.example.com")
+// r.Host("{subdomain}.domain.com")
+// r.Host("{subdomain:[a-z]+}.domain.com")
+//
+// Variable names must be unique in a given route. They can be retrieved
+// calling mux.Vars(request).
+func (r *Route) Host(tpl string) *Route {
+ r.err = r.addRegexpMatcher(tpl, true, false, false)
+ return r
+}
+
+// MatcherFunc ----------------------------------------------------------------
+
+// MatcherFunc is the function signature used by custom matchers.
+type MatcherFunc func(*http.Request, *RouteMatch) bool
+
+// Match returns the match for a given request.
+func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
+ return m(r, match)
+}
+
+// MatcherFunc adds a custom function to be used as request matcher.
+func (r *Route) MatcherFunc(f MatcherFunc) *Route {
+ return r.addMatcher(f)
+}
+
+// Methods --------------------------------------------------------------------
+
+// methodMatcher matches the request against HTTP methods.
+type methodMatcher []string
+
+func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
+ return matchInArray(m, r.Method)
+}
+
+// Methods adds a matcher for HTTP methods.
+// It accepts a sequence of one or more methods to be matched, e.g.:
+// "GET", "POST", "PUT".
+func (r *Route) Methods(methods ...string) *Route {
+ for k, v := range methods {
+ methods[k] = strings.ToUpper(v)
+ }
+ return r.addMatcher(methodMatcher(methods))
+}
+
+// Path -----------------------------------------------------------------------
+
+// Path adds a matcher for the URL path.
+// It accepts a template with zero or more URL variables enclosed by {}. The
+// template must start with a "/".
+// Variables can define an optional regexp pattern to be matched:
+//
+// - {name} matches anything until the next slash.
+//
+// - {name:pattern} matches the given regexp pattern.
+//
+// For example:
+//
+// r := mux.NewRouter()
+// r.Path("/products/").Handler(ProductsHandler)
+// r.Path("/products/{key}").Handler(ProductsHandler)
+// r.Path("/articles/{category}/{id:[0-9]+}").
+// Handler(ArticleHandler)
+//
+// Variable names must be unique in a given route. They can be retrieved
+// calling mux.Vars(request).
+func (r *Route) Path(tpl string) *Route {
+ r.err = r.addRegexpMatcher(tpl, false, false, false)
+ return r
+}
+
+// PathPrefix -----------------------------------------------------------------
+
+// PathPrefix adds a matcher for the URL path prefix. This matches if the given
+// template is a prefix of the full URL path. See Route.Path() for details on
+// the tpl argument.
+//
+// Note that it does not treat slashes specially ("/foobar/" will be matched by
+// the prefix "/foo") so you may want to use a trailing slash here.
+//
+// Also note that the setting of Router.StrictSlash() has no effect on routes
+// with a PathPrefix matcher.
+func (r *Route) PathPrefix(tpl string) *Route {
+ r.err = r.addRegexpMatcher(tpl, false, true, false)
+ return r
+}
+
+// Query ----------------------------------------------------------------------
+
+// Queries adds a matcher for URL query values.
+// It accepts a sequence of key/value pairs. Values may define variables.
+// For example:
+//
+// r := mux.NewRouter()
+// r.Queries("foo", "bar", "id", "{id:[0-9]+}")
+//
+// The above route will only match if the URL contains the defined queries
+// values, e.g.: ?foo=bar&id=42.
+//
+// It the value is an empty string, it will match any value if the key is set.
+//
+// Variables can define an optional regexp pattern to be matched:
+//
+// - {name} matches anything until the next slash.
+//
+// - {name:pattern} matches the given regexp pattern.
+func (r *Route) Queries(pairs ...string) *Route {
+ length := len(pairs)
+ if length%2 != 0 {
+ r.err = fmt.Errorf(
+ "mux: number of parameters must be multiple of 2, got %v", pairs)
+ return nil
+ }
+ for i := 0; i < length; i += 2 {
+ if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, false, true); r.err != nil {
+ return r
+ }
+ }
+
+ return r
+}
+
+// Schemes --------------------------------------------------------------------
+
+// schemeMatcher matches the request against URL schemes.
+type schemeMatcher []string
+
+func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
+ return matchInArray(m, r.URL.Scheme)
+}
+
+// Schemes adds a matcher for URL schemes.
+// It accepts a sequence of schemes to be matched, e.g.: "http", "https".
+func (r *Route) Schemes(schemes ...string) *Route {
+ for k, v := range schemes {
+ schemes[k] = strings.ToLower(v)
+ }
+ return r.addMatcher(schemeMatcher(schemes))
+}
+
+// BuildVarsFunc --------------------------------------------------------------
+
+// BuildVarsFunc is the function signature used by custom build variable
+// functions (which can modify route variables before a route's URL is built).
+type BuildVarsFunc func(map[string]string) map[string]string
+
+// BuildVarsFunc adds a custom function to be used to modify build variables
+// before a route's URL is built.
+func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
+ r.buildVarsFunc = f
+ return r
+}
+
+// Subrouter ------------------------------------------------------------------
+
+// Subrouter creates a subrouter for the route.
+//
+// It will test the inner routes only if the parent route matched. For example:
+//
+// r := mux.NewRouter()
+// s := r.Host("www.example.com").Subrouter()
+// s.HandleFunc("/products/", ProductsHandler)
+// s.HandleFunc("/products/{key}", ProductHandler)
+// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
+//
+// Here, the routes registered in the subrouter won't be tested if the host
+// doesn't match.
+func (r *Route) Subrouter() *Router {
+ router := &Router{parent: r, strictSlash: r.strictSlash}
+ r.addMatcher(router)
+ return router
+}
+
+// ----------------------------------------------------------------------------
+// URL building
+// ----------------------------------------------------------------------------
+
+// URL builds a URL for the route.
+//
+// It accepts a sequence of key/value pairs for the route variables. For
+// example, given this route:
+//
+// r := mux.NewRouter()
+// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
+// Name("article")
+//
+// ...a URL for it can be built using:
+//
+// url, err := r.Get("article").URL("category", "technology", "id", "42")
+//
+// ...which will return an url.URL with the following path:
+//
+// "/articles/technology/42"
+//
+// This also works for host variables:
+//
+// r := mux.NewRouter()
+// r.Host("{subdomain}.domain.com").
+// HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
+// Name("article")
+//
+// // url.String() will be "http://news.domain.com/articles/technology/42"
+// url, err := r.Get("article").URL("subdomain", "news",
+// "category", "technology",
+// "id", "42")
+//
+// All variables defined in the route are required, and their values must
+// conform to the corresponding patterns.
+func (r *Route) URL(pairs ...string) (*url.URL, error) {
+ if r.err != nil {
+ return nil, r.err
+ }
+ if r.regexp == nil {
+ return nil, errors.New("mux: route doesn't have a host or path")
+ }
+ values, err := r.prepareVars(pairs...)
+ if err != nil {
+ return nil, err
+ }
+ var scheme, host, path string
+ if r.regexp.host != nil {
+ // Set a default scheme.
+ scheme = "http"
+ if host, err = r.regexp.host.url(values); err != nil {
+ return nil, err
+ }
+ }
+ if r.regexp.path != nil {
+ if path, err = r.regexp.path.url(values); err != nil {
+ return nil, err
+ }
+ }
+ return &url.URL{
+ Scheme: scheme,
+ Host: host,
+ Path: path,
+ }, nil
+}
+
+// URLHost builds the host part of the URL for a route. See Route.URL().
+//
+// The route must have a host defined.
+func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
+ if r.err != nil {
+ return nil, r.err
+ }
+ if r.regexp == nil || r.regexp.host == nil {
+ return nil, errors.New("mux: route doesn't have a host")
+ }
+ values, err := r.prepareVars(pairs...)
+ if err != nil {
+ return nil, err
+ }
+ host, err := r.regexp.host.url(values)
+ if err != nil {
+ return nil, err
+ }
+ return &url.URL{
+ Scheme: "http",
+ Host: host,
+ }, nil
+}
+
+// URLPath builds the path part of the URL for a route. See Route.URL().
+//
+// The route must have a path defined.
+func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
+ if r.err != nil {
+ return nil, r.err
+ }
+ if r.regexp == nil || r.regexp.path == nil {
+ return nil, errors.New("mux: route doesn't have a path")
+ }
+ values, err := r.prepareVars(pairs...)
+ if err != nil {
+ return nil, err
+ }
+ path, err := r.regexp.path.url(values)
+ if err != nil {
+ return nil, err
+ }
+ return &url.URL{
+ Path: path,
+ }, nil
+}
+
+// GetPathTemplate returns the template used to build the
+// route match.
+// This is useful for building simple REST API documentation and for instrumentation
+// against third-party services.
+// An error will be returned if the route does not define a path.
+func (r *Route) GetPathTemplate() (string, error) {
+ if r.err != nil {
+ return "", r.err
+ }
+ if r.regexp == nil || r.regexp.path == nil {
+ return "", errors.New("mux: route doesn't have a path")
+ }
+ return r.regexp.path.template, nil
+}
+
+// GetHostTemplate returns the template used to build the
+// route match.
+// This is useful for building simple REST API documentation and for instrumentation
+// against third-party services.
+// An error will be returned if the route does not define a host.
+func (r *Route) GetHostTemplate() (string, error) {
+ if r.err != nil {
+ return "", r.err
+ }
+ if r.regexp == nil || r.regexp.host == nil {
+ return "", errors.New("mux: route doesn't have a host")
+ }
+ return r.regexp.host.template, nil
+}
+
+// prepareVars converts the route variable pairs into a map. If the route has a
+// BuildVarsFunc, it is invoked.
+func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {
+ m, err := mapFromPairsToString(pairs...)
+ if err != nil {
+ return nil, err
+ }
+ return r.buildVars(m), nil
+}
+
+func (r *Route) buildVars(m map[string]string) map[string]string {
+ if r.parent != nil {
+ m = r.parent.buildVars(m)
+ }
+ if r.buildVarsFunc != nil {
+ m = r.buildVarsFunc(m)
+ }
+ return m
+}
+
+// ----------------------------------------------------------------------------
+// parentRoute
+// ----------------------------------------------------------------------------
+
+// parentRoute allows routes to know about parent host and path definitions.
+type parentRoute interface {
+ getNamedRoutes() map[string]*Route
+ getRegexpGroup() *routeRegexpGroup
+ buildVars(map[string]string) map[string]string
+}
+
+// getNamedRoutes returns the map where named routes are registered.
+func (r *Route) getNamedRoutes() map[string]*Route {
+ if r.parent == nil {
+ // During tests router is not always set.
+ r.parent = NewRouter()
+ }
+ return r.parent.getNamedRoutes()
+}
+
+// getRegexpGroup returns regexp definitions from this route.
+func (r *Route) getRegexpGroup() *routeRegexpGroup {
+ if r.regexp == nil {
+ if r.parent == nil {
+ // During tests router is not always set.
+ r.parent = NewRouter()
+ }
+ regexp := r.parent.getRegexpGroup()
+ if regexp == nil {
+ r.regexp = new(routeRegexpGroup)
+ } else {
+ // Copy.
+ r.regexp = &routeRegexpGroup{
+ host: regexp.host,
+ path: regexp.path,
+ queries: regexp.queries,
+ }
+ }
+ }
+ return r.regexp
+}
diff --git a/vendor/github.com/gorilla/websocket/.gitignore b/vendor/github.com/gorilla/websocket/.gitignore
new file mode 100644
index 000000000..00268614f
--- /dev/null
+++ b/vendor/github.com/gorilla/websocket/.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/gorilla/websocket/.travis.yml b/vendor/github.com/gorilla/websocket/.travis.yml
new file mode 100644
index 000000000..66435ac0b
--- /dev/null
+++ b/vendor/github.com/gorilla/websocket/.travis.yml
@@ -0,0 +1,17 @@
+language: go
+sudo: false
+
+matrix:
+ include:
+ - go: 1.4
+ - go: 1.5
+ - go: 1.6
+ - go: tip
+ allow_failures:
+ - go: tip
+
+script:
+ - go get -t -v ./...
+ - diff -u <(echo -n) <(gofmt -d .)
+ - go vet $(go list ./... | grep -v /vendor/)
+ - go test -v -race ./...
diff --git a/vendor/github.com/gorilla/websocket/AUTHORS b/vendor/github.com/gorilla/websocket/AUTHORS
new file mode 100644
index 000000000..b003eca0c
--- /dev/null
+++ b/vendor/github.com/gorilla/websocket/AUTHORS
@@ -0,0 +1,8 @@
+# This is the official list of Gorilla WebSocket authors for copyright
+# purposes.
+#
+# Please keep the list sorted.
+
+Gary Burd <gary@beagledreams.com>
+Joachim Bauch <mail@joachim-bauch.de>
+
diff --git a/vendor/github.com/gorilla/websocket/LICENSE b/vendor/github.com/gorilla/websocket/LICENSE
new file mode 100644
index 000000000..9171c9722
--- /dev/null
+++ b/vendor/github.com/gorilla/websocket/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/gorilla/websocket/README.md b/vendor/github.com/gorilla/websocket/README.md
new file mode 100644
index 000000000..9d71959ea
--- /dev/null
+++ b/vendor/github.com/gorilla/websocket/README.md
@@ -0,0 +1,61 @@
+# Gorilla WebSocket
+
+Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
+[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
+
+### Documentation
+
+* [API Reference](http://godoc.org/github.com/gorilla/websocket)
+* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat)
+* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)
+* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)
+* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch)
+
+### Status
+
+The Gorilla WebSocket package provides a complete and tested implementation of
+the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The
+package API is stable.
+
+### Installation
+
+ go get github.com/gorilla/websocket
+
+### Protocol Compliance
+
+The Gorilla WebSocket package passes the server tests in the [Autobahn Test
+Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn
+subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn).
+
+### Gorilla WebSocket compared with other packages
+
+<table>
+<tr>
+<th></th>
+<th><a href="http://godoc.org/github.com/gorilla/websocket">github.com/gorilla</a></th>
+<th><a href="http://godoc.org/golang.org/x/net/websocket">golang.org/x/net</a></th>
+</tr>
+<tr>
+<tr><td colspan="3"><a href="http://tools.ietf.org/html/rfc6455">RFC 6455</a> Features</td></tr>
+<tr><td>Passes <a href="http://autobahn.ws/testsuite/">Autobahn Test Suite</a></td><td><a href="https://github.com/gorilla/websocket/tree/master/examples/autobahn">Yes</a></td><td>No</td></tr>
+<tr><td>Receive <a href="https://tools.ietf.org/html/rfc6455#section-5.4">fragmented</a> message<td>Yes</td><td><a href="https://code.google.com/p/go/issues/detail?id=7632">No</a>, see note 1</td></tr>
+<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">close</a> message</td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td><a href="https://code.google.com/p/go/issues/detail?id=4588">No</a></td></tr>
+<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">pings</a> and receive <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">pongs</a></td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td>No</td></tr>
+<tr><td>Get the <a href="https://tools.ietf.org/html/rfc6455#section-5.6">type</a> of a received data message</td><td>Yes</td><td>Yes, see note 2</td></tr>
+<tr><td colspan="3">Other Features</tr></td>
+<tr><td>Limit size of received message</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.SetReadLimit">Yes</a></td><td><a href="https://code.google.com/p/go/issues/detail?id=5082">No</a></td></tr>
+<tr><td>Read message using io.Reader</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextReader">Yes</a></td><td>No, see note 3</td></tr>
+<tr><td>Write message using io.WriteCloser</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextWriter">Yes</a></td><td>No, see note 3</td></tr>
+</table>
+
+Notes:
+
+1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html).
+2. The application can get the type of a received data message by implementing
+ a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal)
+ function.
+3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries.
+ Read returns when the input buffer is full or a frame boundary is
+ encountered. Each call to Write sends a single frame message. The Gorilla
+ io.Reader and io.WriteCloser operate on a single WebSocket message.
+
diff --git a/vendor/github.com/gorilla/websocket/client.go b/vendor/github.com/gorilla/websocket/client.go
new file mode 100644
index 000000000..879d33ed3
--- /dev/null
+++ b/vendor/github.com/gorilla/websocket/client.go
@@ -0,0 +1,375 @@
+// Copyright 2013 The Gorilla WebSocket 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 websocket
+
+import (
+ "bufio"
+ "bytes"
+ "crypto/tls"
+ "encoding/base64"
+ "errors"
+ "io"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+)
+
+// ErrBadHandshake is returned when the server response to opening handshake is
+// invalid.
+var ErrBadHandshake = errors.New("websocket: bad handshake")
+
+// NewClient creates a new client connection using the given net connection.
+// The URL u specifies the host and request URI. Use requestHeader to specify
+// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies
+// (Cookie). Use the response.Header to get the selected subprotocol
+// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
+//
+// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
+// non-nil *http.Response so that callers can handle redirects, authentication,
+// etc.
+//
+// Deprecated: Use Dialer instead.
+func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) {
+ d := Dialer{
+ ReadBufferSize: readBufSize,
+ WriteBufferSize: writeBufSize,
+ NetDial: func(net, addr string) (net.Conn, error) {
+ return netConn, nil
+ },
+ }
+ return d.Dial(u.String(), requestHeader)
+}
+
+// A Dialer contains options for connecting to WebSocket server.
+type Dialer struct {
+ // NetDial specifies the dial function for creating TCP connections. If
+ // NetDial is nil, net.Dial is used.
+ NetDial func(network, addr string) (net.Conn, error)
+
+ // Proxy specifies a function to return a proxy for a given
+ // Request. If the function returns a non-nil error, the
+ // request is aborted with the provided error.
+ // If Proxy is nil or returns a nil *URL, no proxy is used.
+ Proxy func(*http.Request) (*url.URL, error)
+
+ // TLSClientConfig specifies the TLS configuration to use with tls.Client.
+ // If nil, the default configuration is used.
+ TLSClientConfig *tls.Config
+
+ // HandshakeTimeout specifies the duration for the handshake to complete.
+ HandshakeTimeout time.Duration
+
+ // Input and output buffer sizes. If the buffer size is zero, then a
+ // default value of 4096 is used.
+ ReadBufferSize, WriteBufferSize int
+
+ // Subprotocols specifies the client's requested subprotocols.
+ Subprotocols []string
+}
+
+var errMalformedURL = errors.New("malformed ws or wss URL")
+
+// parseURL parses the URL.
+//
+// This function is a replacement for the standard library url.Parse function.
+// In Go 1.4 and earlier, url.Parse loses information from the path.
+func parseURL(s string) (*url.URL, error) {
+ // From the RFC:
+ //
+ // ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
+ // wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
+
+ var u url.URL
+ switch {
+ case strings.HasPrefix(s, "ws://"):
+ u.Scheme = "ws"
+ s = s[len("ws://"):]
+ case strings.HasPrefix(s, "wss://"):
+ u.Scheme = "wss"
+ s = s[len("wss://"):]
+ default:
+ return nil, errMalformedURL
+ }
+
+ if i := strings.Index(s, "?"); i >= 0 {
+ u.RawQuery = s[i+1:]
+ s = s[:i]
+ }
+
+ if i := strings.Index(s, "/"); i >= 0 {
+ u.Opaque = s[i:]
+ s = s[:i]
+ } else {
+ u.Opaque = "/"
+ }
+
+ u.Host = s
+
+ if strings.Contains(u.Host, "@") {
+ // Don't bother parsing user information because user information is
+ // not allowed in websocket URIs.
+ return nil, errMalformedURL
+ }
+
+ return &u, nil
+}
+
+func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
+ hostPort = u.Host
+ hostNoPort = u.Host
+ if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") {
+ hostNoPort = hostNoPort[:i]
+ } else {
+ switch u.Scheme {
+ case "wss":
+ hostPort += ":443"
+ case "https":
+ hostPort += ":443"
+ default:
+ hostPort += ":80"
+ }
+ }
+ return hostPort, hostNoPort
+}
+
+// DefaultDialer is a dialer with all fields set to the default zero values.
+var DefaultDialer = &Dialer{
+ Proxy: http.ProxyFromEnvironment,
+}
+
+// Dial creates a new client connection. Use requestHeader to specify the
+// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie).
+// Use the response.Header to get the selected subprotocol
+// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
+//
+// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
+// non-nil *http.Response so that callers can handle redirects, authentication,
+// etcetera. The response body may not contain the entire response and does not
+// need to be closed by the application.
+func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
+
+ if d == nil {
+ d = &Dialer{
+ Proxy: http.ProxyFromEnvironment,
+ }
+ }
+
+ challengeKey, err := generateChallengeKey()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ u, err := parseURL(urlStr)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ switch u.Scheme {
+ case "ws":
+ u.Scheme = "http"
+ case "wss":
+ u.Scheme = "https"
+ default:
+ return nil, nil, errMalformedURL
+ }
+
+ if u.User != nil {
+ // User name and password are not allowed in websocket URIs.
+ return nil, nil, errMalformedURL
+ }
+
+ req := &http.Request{
+ Method: "GET",
+ URL: u,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Header: make(http.Header),
+ Host: u.Host,
+ }
+
+ // Set the request headers using the capitalization for names and values in
+ // RFC examples. Although the capitalization shouldn't matter, there are
+ // servers that depend on it. The Header.Set method is not used because the
+ // method canonicalizes the header names.
+ req.Header["Upgrade"] = []string{"websocket"}
+ req.Header["Connection"] = []string{"Upgrade"}
+ req.Header["Sec-WebSocket-Key"] = []string{challengeKey}
+ req.Header["Sec-WebSocket-Version"] = []string{"13"}
+ if len(d.Subprotocols) > 0 {
+ req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")}
+ }
+ for k, vs := range requestHeader {
+ switch {
+ case k == "Host":
+ if len(vs) > 0 {
+ req.Host = vs[0]
+ }
+ case k == "Upgrade" ||
+ k == "Connection" ||
+ k == "Sec-Websocket-Key" ||
+ k == "Sec-Websocket-Version" ||
+ (k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
+ return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
+ default:
+ req.Header[k] = vs
+ }
+ }
+
+ hostPort, hostNoPort := hostPortNoPort(u)
+
+ var proxyURL *url.URL
+ // Check wether the proxy method has been configured
+ if d.Proxy != nil {
+ proxyURL, err = d.Proxy(req)
+ }
+ if err != nil {
+ return nil, nil, err
+ }
+
+ var targetHostPort string
+ if proxyURL != nil {
+ targetHostPort, _ = hostPortNoPort(proxyURL)
+ } else {
+ targetHostPort = hostPort
+ }
+
+ var deadline time.Time
+ if d.HandshakeTimeout != 0 {
+ deadline = time.Now().Add(d.HandshakeTimeout)
+ }
+
+ netDial := d.NetDial
+ if netDial == nil {
+ netDialer := &net.Dialer{Deadline: deadline}
+ netDial = netDialer.Dial
+ }
+
+ netConn, err := netDial("tcp", targetHostPort)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ defer func() {
+ if netConn != nil {
+ netConn.Close()
+ }
+ }()
+
+ if err := netConn.SetDeadline(deadline); err != nil {
+ return nil, nil, err
+ }
+
+ if proxyURL != nil {
+ connectHeader := make(http.Header)
+ if user := proxyURL.User; user != nil {
+ proxyUser := user.Username()
+ if proxyPassword, passwordSet := user.Password(); passwordSet {
+ credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword))
+ connectHeader.Set("Proxy-Authorization", "Basic "+credential)
+ }
+ }
+ connectReq := &http.Request{
+ Method: "CONNECT",
+ URL: &url.URL{Opaque: hostPort},
+ Host: hostPort,
+ Header: connectHeader,
+ }
+
+ connectReq.Write(netConn)
+
+ // Read response.
+ // Okay to use and discard buffered reader here, because
+ // TLS server will not speak until spoken to.
+ br := bufio.NewReader(netConn)
+ resp, err := http.ReadResponse(br, connectReq)
+ if err != nil {
+ return nil, nil, err
+ }
+ if resp.StatusCode != 200 {
+ f := strings.SplitN(resp.Status, " ", 2)
+ return nil, nil, errors.New(f[1])
+ }
+ }
+
+ if u.Scheme == "https" {
+ cfg := cloneTLSConfig(d.TLSClientConfig)
+ if cfg.ServerName == "" {
+ cfg.ServerName = hostNoPort
+ }
+ tlsConn := tls.Client(netConn, cfg)
+ netConn = tlsConn
+ if err := tlsConn.Handshake(); err != nil {
+ return nil, nil, err
+ }
+ if !cfg.InsecureSkipVerify {
+ if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
+ return nil, nil, err
+ }
+ }
+ }
+
+ conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize)
+
+ if err := req.Write(netConn); err != nil {
+ return nil, nil, err
+ }
+
+ resp, err := http.ReadResponse(conn.br, req)
+ if err != nil {
+ return nil, nil, err
+ }
+ if resp.StatusCode != 101 ||
+ !strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
+ !strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
+ resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) {
+ // Before closing the network connection on return from this
+ // function, slurp up some of the response to aid application
+ // debugging.
+ buf := make([]byte, 1024)
+ n, _ := io.ReadFull(resp.Body, buf)
+ resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n]))
+ return nil, resp, ErrBadHandshake
+ }
+
+ resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
+ conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
+
+ netConn.SetDeadline(time.Time{})
+ netConn = nil // to avoid close in defer.
+ return conn, resp, nil
+}
+
+// cloneTLSConfig clones all public fields except the fields
+// SessionTicketsDisabled and SessionTicketKey. This avoids copying the
+// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a
+// config in active use.
+func cloneTLSConfig(cfg *tls.Config) *tls.Config {
+ if cfg == nil {
+ return &tls.Config{}
+ }
+ return &tls.Config{
+ Rand: cfg.Rand,
+ Time: cfg.Time,
+ Certificates: cfg.Certificates,
+ NameToCertificate: cfg.NameToCertificate,
+ GetCertificate: cfg.GetCertificate,
+ RootCAs: cfg.RootCAs,
+ NextProtos: cfg.NextProtos,
+ ServerName: cfg.ServerName,
+ ClientAuth: cfg.ClientAuth,
+ ClientCAs: cfg.ClientCAs,
+ InsecureSkipVerify: cfg.InsecureSkipVerify,
+ CipherSuites: cfg.CipherSuites,
+ PreferServerCipherSuites: cfg.PreferServerCipherSuites,
+ ClientSessionCache: cfg.ClientSessionCache,
+ MinVersion: cfg.MinVersion,
+ MaxVersion: cfg.MaxVersion,
+ CurvePreferences: cfg.CurvePreferences,
+ }
+}
diff --git a/vendor/github.com/gorilla/websocket/conn.go b/vendor/github.com/gorilla/websocket/conn.go
new file mode 100644
index 000000000..ed7736c49
--- /dev/null
+++ b/vendor/github.com/gorilla/websocket/conn.go
@@ -0,0 +1,950 @@
+// Copyright 2013 The Gorilla WebSocket 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 websocket
+
+import (
+ "bufio"
+ "encoding/binary"
+ "errors"
+ "io"
+ "io/ioutil"
+ "math/rand"
+ "net"
+ "strconv"
+ "time"
+ "unicode/utf8"
+)
+
+const (
+ maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask
+ maxControlFramePayloadSize = 125
+ finalBit = 1 << 7
+ maskBit = 1 << 7
+ writeWait = time.Second
+
+ defaultReadBufferSize = 4096
+ defaultWriteBufferSize = 4096
+
+ continuationFrame = 0
+ noFrame = -1
+)
+
+// Close codes defined in RFC 6455, section 11.7.
+const (
+ CloseNormalClosure = 1000
+ CloseGoingAway = 1001
+ CloseProtocolError = 1002
+ CloseUnsupportedData = 1003
+ CloseNoStatusReceived = 1005
+ CloseAbnormalClosure = 1006
+ CloseInvalidFramePayloadData = 1007
+ ClosePolicyViolation = 1008
+ CloseMessageTooBig = 1009
+ CloseMandatoryExtension = 1010
+ CloseInternalServerErr = 1011
+ CloseServiceRestart = 1012
+ CloseTryAgainLater = 1013
+ CloseTLSHandshake = 1015
+)
+
+// The message types are defined in RFC 6455, section 11.8.
+const (
+ // TextMessage denotes a text data message. The text message payload is
+ // interpreted as UTF-8 encoded text data.
+ TextMessage = 1
+
+ // BinaryMessage denotes a binary data message.
+ BinaryMessage = 2
+
+ // CloseMessage denotes a close control message. The optional message
+ // payload contains a numeric code and text. Use the FormatCloseMessage
+ // function to format a close message payload.
+ CloseMessage = 8
+
+ // PingMessage denotes a ping control message. The optional message payload
+ // is UTF-8 encoded text.
+ PingMessage = 9
+
+ // PongMessage denotes a ping control message. The optional message payload
+ // is UTF-8 encoded text.
+ PongMessage = 10
+)
+
+// ErrCloseSent is returned when the application writes a message to the
+// connection after sending a close message.
+var ErrCloseSent = errors.New("websocket: close sent")
+
+// ErrReadLimit is returned when reading a message that is larger than the
+// read limit set for the connection.
+var ErrReadLimit = errors.New("websocket: read limit exceeded")
+
+// netError satisfies the net Error interface.
+type netError struct {
+ msg string
+ temporary bool
+ timeout bool
+}
+
+func (e *netError) Error() string { return e.msg }
+func (e *netError) Temporary() bool { return e.temporary }
+func (e *netError) Timeout() bool { return e.timeout }
+
+// CloseError represents close frame.
+type CloseError struct {
+
+ // Code is defined in RFC 6455, section 11.7.
+ Code int
+
+ // Text is the optional text payload.
+ Text string
+}
+
+func (e *CloseError) Error() string {
+ s := []byte("websocket: close ")
+ s = strconv.AppendInt(s, int64(e.Code), 10)
+ switch e.Code {
+ case CloseNormalClosure:
+ s = append(s, " (normal)"...)
+ case CloseGoingAway:
+ s = append(s, " (going away)"...)
+ case CloseProtocolError:
+ s = append(s, " (protocol error)"...)
+ case CloseUnsupportedData:
+ s = append(s, " (unsupported data)"...)
+ case CloseNoStatusReceived:
+ s = append(s, " (no status)"...)
+ case CloseAbnormalClosure:
+ s = append(s, " (abnormal closure)"...)
+ case CloseInvalidFramePayloadData:
+ s = append(s, " (invalid payload data)"...)
+ case ClosePolicyViolation:
+ s = append(s, " (policy violation)"...)
+ case CloseMessageTooBig:
+ s = append(s, " (message too big)"...)
+ case CloseMandatoryExtension:
+ s = append(s, " (mandatory extension missing)"...)
+ case CloseInternalServerErr:
+ s = append(s, " (internal server error)"...)
+ case CloseTLSHandshake:
+ s = append(s, " (TLS handshake error)"...)
+ }
+ if e.Text != "" {
+ s = append(s, ": "...)
+ s = append(s, e.Text...)
+ }
+ return string(s)
+}
+
+// IsCloseError returns boolean indicating whether the error is a *CloseError
+// with one of the specified codes.
+func IsCloseError(err error, codes ...int) bool {
+ if e, ok := err.(*CloseError); ok {
+ for _, code := range codes {
+ if e.Code == code {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// IsUnexpectedCloseError returns boolean indicating whether the error is a
+// *CloseError with a code not in the list of expected codes.
+func IsUnexpectedCloseError(err error, expectedCodes ...int) bool {
+ if e, ok := err.(*CloseError); ok {
+ for _, code := range expectedCodes {
+ if e.Code == code {
+ return false
+ }
+ }
+ return true
+ }
+ return false
+}
+
+var (
+ errWriteTimeout = &netError{msg: "websocket: write timeout", timeout: true, temporary: true}
+ errUnexpectedEOF = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()}
+ errBadWriteOpCode = errors.New("websocket: bad write message type")
+ errWriteClosed = errors.New("websocket: write closed")
+ errInvalidControlFrame = errors.New("websocket: invalid control frame")
+)
+
+func hideTempErr(err error) error {
+ if e, ok := err.(net.Error); ok && e.Temporary() {
+ err = &netError{msg: e.Error(), timeout: e.Timeout()}
+ }
+ return err
+}
+
+func isControl(frameType int) bool {
+ return frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage
+}
+
+func isData(frameType int) bool {
+ return frameType == TextMessage || frameType == BinaryMessage
+}
+
+var validReceivedCloseCodes = map[int]bool{
+ // see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number
+
+ CloseNormalClosure: true,
+ CloseGoingAway: true,
+ CloseProtocolError: true,
+ CloseUnsupportedData: true,
+ CloseNoStatusReceived: false,
+ CloseAbnormalClosure: false,
+ CloseInvalidFramePayloadData: true,
+ ClosePolicyViolation: true,
+ CloseMessageTooBig: true,
+ CloseMandatoryExtension: true,
+ CloseInternalServerErr: true,
+ CloseServiceRestart: true,
+ CloseTryAgainLater: true,
+ CloseTLSHandshake: false,
+}
+
+func isValidReceivedCloseCode(code int) bool {
+ return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999)
+}
+
+func maskBytes(key [4]byte, pos int, b []byte) int {
+ for i := range b {
+ b[i] ^= key[pos&3]
+ pos++
+ }
+ return pos & 3
+}
+
+func newMaskKey() [4]byte {
+ n := rand.Uint32()
+ return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
+}
+
+// Conn represents a WebSocket connection.
+type Conn struct {
+ conn net.Conn
+ isServer bool
+ subprotocol string
+
+ // Write fields
+ mu chan bool // used as mutex to protect write to conn and closeSent
+ closeSent bool // true if close message was sent
+
+ // Message writer fields.
+ writeErr error
+ writeBuf []byte // frame is constructed in this buffer.
+ writePos int // end of data in writeBuf.
+ writeFrameType int // type of the current frame.
+ writeSeq int // incremented to invalidate message writers.
+ writeDeadline time.Time
+ isWriting bool // for best-effort concurrent write detection
+
+ // Read fields
+ readErr error
+ br *bufio.Reader
+ readRemaining int64 // bytes remaining in current frame.
+ readFinal bool // true the current message has more frames.
+ readSeq int // incremented to invalidate message readers.
+ readLength int64 // Message size.
+ readLimit int64 // Maximum message size.
+ readMaskPos int
+ readMaskKey [4]byte
+ handlePong func(string) error
+ handlePing func(string) error
+ readErrCount int
+}
+
+func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn {
+ mu := make(chan bool, 1)
+ mu <- true
+
+ if readBufferSize == 0 {
+ readBufferSize = defaultReadBufferSize
+ }
+ if writeBufferSize == 0 {
+ writeBufferSize = defaultWriteBufferSize
+ }
+
+ c := &Conn{
+ isServer: isServer,
+ br: bufio.NewReaderSize(conn, readBufferSize),
+ conn: conn,
+ mu: mu,
+ readFinal: true,
+ writeBuf: make([]byte, writeBufferSize+maxFrameHeaderSize),
+ writeFrameType: noFrame,
+ writePos: maxFrameHeaderSize,
+ }
+ c.SetPingHandler(nil)
+ c.SetPongHandler(nil)
+ return c
+}
+
+// Subprotocol returns the negotiated protocol for the connection.
+func (c *Conn) Subprotocol() string {
+ return c.subprotocol
+}
+
+// Close closes the underlying network connection without sending or waiting for a close frame.
+func (c *Conn) Close() error {
+ return c.conn.Close()
+}
+
+// LocalAddr returns the local network address.
+func (c *Conn) LocalAddr() net.Addr {
+ return c.conn.LocalAddr()
+}
+
+// RemoteAddr returns the remote network address.
+func (c *Conn) RemoteAddr() net.Addr {
+ return c.conn.RemoteAddr()
+}
+
+// Write methods
+
+func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error {
+ <-c.mu
+ defer func() { c.mu <- true }()
+
+ if c.closeSent {
+ return ErrCloseSent
+ } else if frameType == CloseMessage {
+ c.closeSent = true
+ }
+
+ c.conn.SetWriteDeadline(deadline)
+ for _, buf := range bufs {
+ if len(buf) > 0 {
+ n, err := c.conn.Write(buf)
+ if n != len(buf) {
+ // Close on partial write.
+ c.conn.Close()
+ }
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// WriteControl writes a control message with the given deadline. The allowed
+// message types are CloseMessage, PingMessage and PongMessage.
+func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error {
+ if !isControl(messageType) {
+ return errBadWriteOpCode
+ }
+ if len(data) > maxControlFramePayloadSize {
+ return errInvalidControlFrame
+ }
+
+ b0 := byte(messageType) | finalBit
+ b1 := byte(len(data))
+ if !c.isServer {
+ b1 |= maskBit
+ }
+
+ buf := make([]byte, 0, maxFrameHeaderSize+maxControlFramePayloadSize)
+ buf = append(buf, b0, b1)
+
+ if c.isServer {
+ buf = append(buf, data...)
+ } else {
+ key := newMaskKey()
+ buf = append(buf, key[:]...)
+ buf = append(buf, data...)
+ maskBytes(key, 0, buf[6:])
+ }
+
+ d := time.Hour * 1000
+ if !deadline.IsZero() {
+ d = deadline.Sub(time.Now())
+ if d < 0 {
+ return errWriteTimeout
+ }
+ }
+
+ timer := time.NewTimer(d)
+ select {
+ case <-c.mu:
+ timer.Stop()
+ case <-timer.C:
+ return errWriteTimeout
+ }
+ defer func() { c.mu <- true }()
+
+ if c.closeSent {
+ return ErrCloseSent
+ } else if messageType == CloseMessage {
+ c.closeSent = true
+ }
+
+ c.conn.SetWriteDeadline(deadline)
+ n, err := c.conn.Write(buf)
+ if n != 0 && n != len(buf) {
+ c.conn.Close()
+ }
+ return hideTempErr(err)
+}
+
+// NextWriter returns a writer for the next message to send. The writer's
+// Close method flushes the complete message to the network.
+//
+// There can be at most one open writer on a connection. NextWriter closes the
+// previous writer if the application has not already done so.
+func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
+ if c.writeErr != nil {
+ return nil, c.writeErr
+ }
+
+ if c.writeFrameType != noFrame {
+ if err := c.flushFrame(true, nil); err != nil {
+ return nil, err
+ }
+ }
+
+ if !isControl(messageType) && !isData(messageType) {
+ return nil, errBadWriteOpCode
+ }
+
+ c.writeFrameType = messageType
+ return messageWriter{c, c.writeSeq}, nil
+}
+
+func (c *Conn) flushFrame(final bool, extra []byte) error {
+ length := c.writePos - maxFrameHeaderSize + len(extra)
+
+ // Check for invalid control frames.
+ if isControl(c.writeFrameType) &&
+ (!final || length > maxControlFramePayloadSize) {
+ c.writeSeq++
+ c.writeFrameType = noFrame
+ c.writePos = maxFrameHeaderSize
+ return errInvalidControlFrame
+ }
+
+ b0 := byte(c.writeFrameType)
+ if final {
+ b0 |= finalBit
+ }
+ b1 := byte(0)
+ if !c.isServer {
+ b1 |= maskBit
+ }
+
+ // Assume that the frame starts at beginning of c.writeBuf.
+ framePos := 0
+ if c.isServer {
+ // Adjust up if mask not included in the header.
+ framePos = 4
+ }
+
+ switch {
+ case length >= 65536:
+ c.writeBuf[framePos] = b0
+ c.writeBuf[framePos+1] = b1 | 127
+ binary.BigEndian.PutUint64(c.writeBuf[framePos+2:], uint64(length))
+ case length > 125:
+ framePos += 6
+ c.writeBuf[framePos] = b0
+ c.writeBuf[framePos+1] = b1 | 126
+ binary.BigEndian.PutUint16(c.writeBuf[framePos+2:], uint16(length))
+ default:
+ framePos += 8
+ c.writeBuf[framePos] = b0
+ c.writeBuf[framePos+1] = b1 | byte(length)
+ }
+
+ if !c.isServer {
+ key := newMaskKey()
+ copy(c.writeBuf[maxFrameHeaderSize-4:], key[:])
+ maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:c.writePos])
+ if len(extra) > 0 {
+ c.writeErr = errors.New("websocket: internal error, extra used in client mode")
+ return c.writeErr
+ }
+ }
+
+ // Write the buffers to the connection with best-effort detection of
+ // concurrent writes. See the concurrency section in the package
+ // documentation for more info.
+
+ if c.isWriting {
+ panic("concurrent write to websocket connection")
+ }
+ c.isWriting = true
+
+ c.writeErr = c.write(c.writeFrameType, c.writeDeadline, c.writeBuf[framePos:c.writePos], extra)
+
+ if !c.isWriting {
+ panic("concurrent write to websocket connection")
+ }
+ c.isWriting = false
+
+ // Setup for next frame.
+ c.writePos = maxFrameHeaderSize
+ c.writeFrameType = continuationFrame
+ if final {
+ c.writeSeq++
+ c.writeFrameType = noFrame
+ }
+ return c.writeErr
+}
+
+type messageWriter struct {
+ c *Conn
+ seq int
+}
+
+func (w messageWriter) err() error {
+ c := w.c
+ if c.writeSeq != w.seq {
+ return errWriteClosed
+ }
+ if c.writeErr != nil {
+ return c.writeErr
+ }
+ return nil
+}
+
+func (w messageWriter) ncopy(max int) (int, error) {
+ n := len(w.c.writeBuf) - w.c.writePos
+ if n <= 0 {
+ if err := w.c.flushFrame(false, nil); err != nil {
+ return 0, err
+ }
+ n = len(w.c.writeBuf) - w.c.writePos
+ }
+ if n > max {
+ n = max
+ }
+ return n, nil
+}
+
+func (w messageWriter) write(final bool, p []byte) (int, error) {
+ if err := w.err(); err != nil {
+ return 0, err
+ }
+
+ if len(p) > 2*len(w.c.writeBuf) && w.c.isServer {
+ // Don't buffer large messages.
+ err := w.c.flushFrame(final, p)
+ if err != nil {
+ return 0, err
+ }
+ return len(p), nil
+ }
+
+ nn := len(p)
+ for len(p) > 0 {
+ n, err := w.ncopy(len(p))
+ if err != nil {
+ return 0, err
+ }
+ copy(w.c.writeBuf[w.c.writePos:], p[:n])
+ w.c.writePos += n
+ p = p[n:]
+ }
+ return nn, nil
+}
+
+func (w messageWriter) Write(p []byte) (int, error) {
+ return w.write(false, p)
+}
+
+func (w messageWriter) WriteString(p string) (int, error) {
+ if err := w.err(); err != nil {
+ return 0, err
+ }
+
+ nn := len(p)
+ for len(p) > 0 {
+ n, err := w.ncopy(len(p))
+ if err != nil {
+ return 0, err
+ }
+ copy(w.c.writeBuf[w.c.writePos:], p[:n])
+ w.c.writePos += n
+ p = p[n:]
+ }
+ return nn, nil
+}
+
+func (w messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {
+ if err := w.err(); err != nil {
+ return 0, err
+ }
+ for {
+ if w.c.writePos == len(w.c.writeBuf) {
+ err = w.c.flushFrame(false, nil)
+ if err != nil {
+ break
+ }
+ }
+ var n int
+ n, err = r.Read(w.c.writeBuf[w.c.writePos:])
+ w.c.writePos += n
+ nn += int64(n)
+ if err != nil {
+ if err == io.EOF {
+ err = nil
+ }
+ break
+ }
+ }
+ return nn, err
+}
+
+func (w messageWriter) Close() error {
+ if err := w.err(); err != nil {
+ return err
+ }
+ return w.c.flushFrame(true, nil)
+}
+
+// WriteMessage is a helper method for getting a writer using NextWriter,
+// writing the message and closing the writer.
+func (c *Conn) WriteMessage(messageType int, data []byte) error {
+ wr, err := c.NextWriter(messageType)
+ if err != nil {
+ return err
+ }
+ w := wr.(messageWriter)
+ if _, err := w.write(true, data); err != nil {
+ return err
+ }
+ if c.writeSeq == w.seq {
+ if err := c.flushFrame(true, nil); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// SetWriteDeadline sets the write deadline on the underlying network
+// connection. After a write has timed out, the websocket state is corrupt and
+// all future writes will return an error. A zero value for t means writes will
+// not time out.
+func (c *Conn) SetWriteDeadline(t time.Time) error {
+ c.writeDeadline = t
+ return nil
+}
+
+// Read methods
+
+// readFull is like io.ReadFull except that io.EOF is never returned.
+func (c *Conn) readFull(p []byte) (err error) {
+ var n int
+ for n < len(p) && err == nil {
+ var nn int
+ nn, err = c.br.Read(p[n:])
+ n += nn
+ }
+ if n == len(p) {
+ err = nil
+ } else if err == io.EOF {
+ err = errUnexpectedEOF
+ }
+ return
+}
+
+func (c *Conn) advanceFrame() (int, error) {
+
+ // 1. Skip remainder of previous frame.
+
+ if c.readRemaining > 0 {
+ if _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil {
+ return noFrame, err
+ }
+ }
+
+ // 2. Read and parse first two bytes of frame header.
+
+ var b [8]byte
+ if err := c.readFull(b[:2]); err != nil {
+ return noFrame, err
+ }
+
+ final := b[0]&finalBit != 0
+ frameType := int(b[0] & 0xf)
+ reserved := int((b[0] >> 4) & 0x7)
+ mask := b[1]&maskBit != 0
+ c.readRemaining = int64(b[1] & 0x7f)
+
+ if reserved != 0 {
+ return noFrame, c.handleProtocolError("unexpected reserved bits " + strconv.Itoa(reserved))
+ }
+
+ switch frameType {
+ case CloseMessage, PingMessage, PongMessage:
+ if c.readRemaining > maxControlFramePayloadSize {
+ return noFrame, c.handleProtocolError("control frame length > 125")
+ }
+ if !final {
+ return noFrame, c.handleProtocolError("control frame not final")
+ }
+ case TextMessage, BinaryMessage:
+ if !c.readFinal {
+ return noFrame, c.handleProtocolError("message start before final message frame")
+ }
+ c.readFinal = final
+ case continuationFrame:
+ if c.readFinal {
+ return noFrame, c.handleProtocolError("continuation after final message frame")
+ }
+ c.readFinal = final
+ default:
+ return noFrame, c.handleProtocolError("unknown opcode " + strconv.Itoa(frameType))
+ }
+
+ // 3. Read and parse frame length.
+
+ switch c.readRemaining {
+ case 126:
+ if err := c.readFull(b[:2]); err != nil {
+ return noFrame, err
+ }
+ c.readRemaining = int64(binary.BigEndian.Uint16(b[:2]))
+ case 127:
+ if err := c.readFull(b[:8]); err != nil {
+ return noFrame, err
+ }
+ c.readRemaining = int64(binary.BigEndian.Uint64(b[:8]))
+ }
+
+ // 4. Handle frame masking.
+
+ if mask != c.isServer {
+ return noFrame, c.handleProtocolError("incorrect mask flag")
+ }
+
+ if mask {
+ c.readMaskPos = 0
+ if err := c.readFull(c.readMaskKey[:]); err != nil {
+ return noFrame, err
+ }
+ }
+
+ // 5. For text and binary messages, enforce read limit and return.
+
+ if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage {
+
+ c.readLength += c.readRemaining
+ if c.readLimit > 0 && c.readLength > c.readLimit {
+ c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait))
+ return noFrame, ErrReadLimit
+ }
+
+ return frameType, nil
+ }
+
+ // 6. Read control frame payload.
+
+ var payload []byte
+ if c.readRemaining > 0 {
+ payload = make([]byte, c.readRemaining)
+ c.readRemaining = 0
+ if err := c.readFull(payload); err != nil {
+ return noFrame, err
+ }
+ if c.isServer {
+ maskBytes(c.readMaskKey, 0, payload)
+ }
+ }
+
+ // 7. Process control frame payload.
+
+ switch frameType {
+ case PongMessage:
+ if err := c.handlePong(string(payload)); err != nil {
+ return noFrame, err
+ }
+ case PingMessage:
+ if err := c.handlePing(string(payload)); err != nil {
+ return noFrame, err
+ }
+ case CloseMessage:
+ echoMessage := []byte{}
+ closeCode := CloseNoStatusReceived
+ closeText := ""
+ if len(payload) >= 2 {
+ echoMessage = payload[:2]
+ closeCode = int(binary.BigEndian.Uint16(payload))
+ if !isValidReceivedCloseCode(closeCode) {
+ return noFrame, c.handleProtocolError("invalid close code")
+ }
+ closeText = string(payload[2:])
+ if !utf8.ValidString(closeText) {
+ return noFrame, c.handleProtocolError("invalid utf8 payload in close frame")
+ }
+ }
+ c.WriteControl(CloseMessage, echoMessage, time.Now().Add(writeWait))
+ return noFrame, &CloseError{Code: closeCode, Text: closeText}
+ }
+
+ return frameType, nil
+}
+
+func (c *Conn) handleProtocolError(message string) error {
+ c.WriteControl(CloseMessage, FormatCloseMessage(CloseProtocolError, message), time.Now().Add(writeWait))
+ return errors.New("websocket: " + message)
+}
+
+// NextReader returns the next data message received from the peer. The
+// returned messageType is either TextMessage or BinaryMessage.
+//
+// There can be at most one open reader on a connection. NextReader discards
+// the previous message if the application has not already consumed it.
+//
+// Applications must break out of the application's read loop when this method
+// returns a non-nil error value. Errors returned from this method are
+// permanent. Once this method returns a non-nil error, all subsequent calls to
+// this method return the same error.
+func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
+
+ c.readSeq++
+ c.readLength = 0
+
+ for c.readErr == nil {
+ frameType, err := c.advanceFrame()
+ if err != nil {
+ c.readErr = hideTempErr(err)
+ break
+ }
+ if frameType == TextMessage || frameType == BinaryMessage {
+ return frameType, messageReader{c, c.readSeq}, nil
+ }
+ }
+
+ // Applications that do handle the error returned from this method spin in
+ // tight loop on connection failure. To help application developers detect
+ // this error, panic on repeated reads to the failed connection.
+ c.readErrCount++
+ if c.readErrCount >= 1000 {
+ panic("repeated read on failed websocket connection")
+ }
+
+ return noFrame, nil, c.readErr
+}
+
+type messageReader struct {
+ c *Conn
+ seq int
+}
+
+func (r messageReader) Read(b []byte) (int, error) {
+
+ if r.seq != r.c.readSeq {
+ return 0, io.EOF
+ }
+
+ for r.c.readErr == nil {
+
+ if r.c.readRemaining > 0 {
+ if int64(len(b)) > r.c.readRemaining {
+ b = b[:r.c.readRemaining]
+ }
+ n, err := r.c.br.Read(b)
+ r.c.readErr = hideTempErr(err)
+ if r.c.isServer {
+ r.c.readMaskPos = maskBytes(r.c.readMaskKey, r.c.readMaskPos, b[:n])
+ }
+ r.c.readRemaining -= int64(n)
+ if r.c.readRemaining > 0 && r.c.readErr == io.EOF {
+ r.c.readErr = errUnexpectedEOF
+ }
+ return n, r.c.readErr
+ }
+
+ if r.c.readFinal {
+ r.c.readSeq++
+ return 0, io.EOF
+ }
+
+ frameType, err := r.c.advanceFrame()
+ switch {
+ case err != nil:
+ r.c.readErr = hideTempErr(err)
+ case frameType == TextMessage || frameType == BinaryMessage:
+ r.c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader")
+ }
+ }
+
+ err := r.c.readErr
+ if err == io.EOF && r.seq == r.c.readSeq {
+ err = errUnexpectedEOF
+ }
+ return 0, err
+}
+
+// ReadMessage is a helper method for getting a reader using NextReader and
+// reading from that reader to a buffer.
+func (c *Conn) ReadMessage() (messageType int, p []byte, err error) {
+ var r io.Reader
+ messageType, r, err = c.NextReader()
+ if err != nil {
+ return messageType, nil, err
+ }
+ p, err = ioutil.ReadAll(r)
+ return messageType, p, err
+}
+
+// SetReadDeadline sets the read deadline on the underlying network connection.
+// After a read has timed out, the websocket connection state is corrupt and
+// all future reads will return an error. A zero value for t means reads will
+// not time out.
+func (c *Conn) SetReadDeadline(t time.Time) error {
+ return c.conn.SetReadDeadline(t)
+}
+
+// SetReadLimit sets the maximum size for a message read from the peer. If a
+// message exceeds the limit, the connection sends a close frame to the peer
+// and returns ErrReadLimit to the application.
+func (c *Conn) SetReadLimit(limit int64) {
+ c.readLimit = limit
+}
+
+// SetPingHandler sets the handler for ping messages received from the peer.
+// The appData argument to h is the PING frame application data. The default
+// ping handler sends a pong to the peer.
+func (c *Conn) SetPingHandler(h func(appData string) error) {
+ if h == nil {
+ h = func(message string) error {
+ err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait))
+ if err == ErrCloseSent {
+ return nil
+ } else if e, ok := err.(net.Error); ok && e.Temporary() {
+ return nil
+ }
+ return err
+ }
+ }
+ c.handlePing = h
+}
+
+// SetPongHandler sets the handler for pong messages received from the peer.
+// The appData argument to h is the PONG frame application data. The default
+// pong handler does nothing.
+func (c *Conn) SetPongHandler(h func(appData string) error) {
+ if h == nil {
+ h = func(string) error { return nil }
+ }
+ c.handlePong = h
+}
+
+// UnderlyingConn returns the internal net.Conn. This can be used to further
+// modifications to connection specific flags.
+func (c *Conn) UnderlyingConn() net.Conn {
+ return c.conn
+}
+
+// FormatCloseMessage formats closeCode and text as a WebSocket close message.
+func FormatCloseMessage(closeCode int, text string) []byte {
+ buf := make([]byte, 2+len(text))
+ binary.BigEndian.PutUint16(buf, uint16(closeCode))
+ copy(buf[2:], text)
+ return buf
+}
diff --git a/vendor/github.com/gorilla/websocket/doc.go b/vendor/github.com/gorilla/websocket/doc.go
new file mode 100644
index 000000000..c901a7a94
--- /dev/null
+++ b/vendor/github.com/gorilla/websocket/doc.go
@@ -0,0 +1,152 @@
+// Copyright 2013 The Gorilla WebSocket 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 websocket implements the WebSocket protocol defined in RFC 6455.
+//
+// Overview
+//
+// The Conn type represents a WebSocket connection. A server application uses
+// the Upgrade function from an Upgrader object with a HTTP request handler
+// to get a pointer to a Conn:
+//
+// var upgrader = websocket.Upgrader{
+// ReadBufferSize: 1024,
+// WriteBufferSize: 1024,
+// }
+//
+// func handler(w http.ResponseWriter, r *http.Request) {
+// conn, err := upgrader.Upgrade(w, r, nil)
+// if err != nil {
+// log.Println(err)
+// return
+// }
+// ... Use conn to send and receive messages.
+// }
+//
+// Call the connection's WriteMessage and ReadMessage methods to send and
+// receive messages as a slice of bytes. This snippet of code shows how to echo
+// messages using these methods:
+//
+// for {
+// messageType, p, err := conn.ReadMessage()
+// if err != nil {
+// return
+// }
+// if err = conn.WriteMessage(messageType, p); err != nil {
+// return err
+// }
+// }
+//
+// In above snippet of code, p is a []byte and messageType is an int with value
+// websocket.BinaryMessage or websocket.TextMessage.
+//
+// An application can also send and receive messages using the io.WriteCloser
+// and io.Reader interfaces. To send a message, call the connection NextWriter
+// method to get an io.WriteCloser, write the message to the writer and close
+// the writer when done. To receive a message, call the connection NextReader
+// method to get an io.Reader and read until io.EOF is returned. This snippet
+// shows how to echo messages using the NextWriter and NextReader methods:
+//
+// for {
+// messageType, r, err := conn.NextReader()
+// if err != nil {
+// return
+// }
+// w, err := conn.NextWriter(messageType)
+// if err != nil {
+// return err
+// }
+// if _, err := io.Copy(w, r); err != nil {
+// return err
+// }
+// if err := w.Close(); err != nil {
+// return err
+// }
+// }
+//
+// Data Messages
+//
+// The WebSocket protocol distinguishes between text and binary data messages.
+// Text messages are interpreted as UTF-8 encoded text. The interpretation of
+// binary messages is left to the application.
+//
+// This package uses the TextMessage and BinaryMessage integer constants to
+// identify the two data message types. The ReadMessage and NextReader methods
+// return the type of the received message. The messageType argument to the
+// WriteMessage and NextWriter methods specifies the type of a sent message.
+//
+// It is the application's responsibility to ensure that text messages are
+// valid UTF-8 encoded text.
+//
+// Control Messages
+//
+// The WebSocket protocol defines three types of control messages: close, ping
+// and pong. Call the connection WriteControl, WriteMessage or NextWriter
+// methods to send a control message to the peer.
+//
+// Connections handle received close messages by sending a close message to the
+// peer and returning a *CloseError from the the NextReader, ReadMessage or the
+// message Read method.
+//
+// Connections handle received ping and pong messages by invoking callback
+// functions set with SetPingHandler and SetPongHandler methods. The callback
+// functions are called from the NextReader, ReadMessage and the message Read
+// methods.
+//
+// The default ping handler sends a pong to the peer. The application's reading
+// goroutine can block for a short time while the handler writes the pong data
+// to the connection.
+//
+// The application must read the connection to process ping, pong and close
+// messages sent from the peer. If the application is not otherwise interested
+// in messages from the peer, then the application should start a goroutine to
+// read and discard messages from the peer. A simple example is:
+//
+// func readLoop(c *websocket.Conn) {
+// for {
+// if _, _, err := c.NextReader(); err != nil {
+// c.Close()
+// break
+// }
+// }
+// }
+//
+// Concurrency
+//
+// Connections support one concurrent reader and one concurrent writer.
+//
+// Applications are responsible for ensuring that no more than one goroutine
+// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,
+// WriteJSON) concurrently and that no more than one goroutine calls the read
+// methods (NextReader, SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler,
+// SetPingHandler) concurrently.
+//
+// The Close and WriteControl methods can be called concurrently with all other
+// methods.
+//
+// Origin Considerations
+//
+// Web browsers allow Javascript applications to open a WebSocket connection to
+// any host. It's up to the server to enforce an origin policy using the Origin
+// request header sent by the browser.
+//
+// The Upgrader calls the function specified in the CheckOrigin field to check
+// the origin. If the CheckOrigin function returns false, then the Upgrade
+// method fails the WebSocket handshake with HTTP status 403.
+//
+// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail
+// the handshake if the Origin request header is present and not equal to the
+// Host request header.
+//
+// An application can allow connections from any origin by specifying a
+// function that always returns true:
+//
+// var upgrader = websocket.Upgrader{
+// CheckOrigin: func(r *http.Request) bool { return true },
+// }
+//
+// The deprecated Upgrade function does not enforce an origin policy. It's the
+// application's responsibility to check the Origin header before calling
+// Upgrade.
+package websocket
diff --git a/vendor/github.com/gorilla/websocket/json.go b/vendor/github.com/gorilla/websocket/json.go
new file mode 100644
index 000000000..4f0e36875
--- /dev/null
+++ b/vendor/github.com/gorilla/websocket/json.go
@@ -0,0 +1,55 @@
+// Copyright 2013 The Gorilla WebSocket 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 websocket
+
+import (
+ "encoding/json"
+ "io"
+)
+
+// WriteJSON is deprecated, use c.WriteJSON instead.
+func WriteJSON(c *Conn, v interface{}) error {
+ return c.WriteJSON(v)
+}
+
+// WriteJSON writes the JSON encoding of v to the connection.
+//
+// See the documentation for encoding/json Marshal for details about the
+// conversion of Go values to JSON.
+func (c *Conn) WriteJSON(v interface{}) error {
+ w, err := c.NextWriter(TextMessage)
+ if err != nil {
+ return err
+ }
+ err1 := json.NewEncoder(w).Encode(v)
+ err2 := w.Close()
+ if err1 != nil {
+ return err1
+ }
+ return err2
+}
+
+// ReadJSON is deprecated, use c.ReadJSON instead.
+func ReadJSON(c *Conn, v interface{}) error {
+ return c.ReadJSON(v)
+}
+
+// ReadJSON reads the next JSON-encoded message from the connection and stores
+// it in the value pointed to by v.
+//
+// See the documentation for the encoding/json Unmarshal function for details
+// about the conversion of JSON to a Go value.
+func (c *Conn) ReadJSON(v interface{}) error {
+ _, r, err := c.NextReader()
+ if err != nil {
+ return err
+ }
+ err = json.NewDecoder(r).Decode(v)
+ if err == io.EOF {
+ // One value is expected in the message.
+ err = io.ErrUnexpectedEOF
+ }
+ return err
+}
diff --git a/vendor/github.com/gorilla/websocket/server.go b/vendor/github.com/gorilla/websocket/server.go
new file mode 100644
index 000000000..8d7137de9
--- /dev/null
+++ b/vendor/github.com/gorilla/websocket/server.go
@@ -0,0 +1,260 @@
+// Copyright 2013 The Gorilla WebSocket 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 websocket
+
+import (
+ "bufio"
+ "errors"
+ "net"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+)
+
+// HandshakeError describes an error with the handshake from the peer.
+type HandshakeError struct {
+ message string
+}
+
+func (e HandshakeError) Error() string { return e.message }
+
+// Upgrader specifies parameters for upgrading an HTTP connection to a
+// WebSocket connection.
+type Upgrader struct {
+ // HandshakeTimeout specifies the duration for the handshake to complete.
+ HandshakeTimeout time.Duration
+
+ // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
+ // size is zero, then a default value of 4096 is used. The I/O buffer sizes
+ // do not limit the size of the messages that can be sent or received.
+ ReadBufferSize, WriteBufferSize int
+
+ // Subprotocols specifies the server's supported protocols in order of
+ // preference. If this field is set, then the Upgrade method negotiates a
+ // subprotocol by selecting the first match in this list with a protocol
+ // requested by the client.
+ Subprotocols []string
+
+ // Error specifies the function for generating HTTP error responses. If Error
+ // is nil, then http.Error is used to generate the HTTP response.
+ Error func(w http.ResponseWriter, r *http.Request, status int, reason error)
+
+ // CheckOrigin returns true if the request Origin header is acceptable. If
+ // CheckOrigin is nil, the host in the Origin header must not be set or
+ // must match the host of the request.
+ CheckOrigin func(r *http.Request) bool
+}
+
+func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {
+ err := HandshakeError{reason}
+ if u.Error != nil {
+ u.Error(w, r, status, err)
+ } else {
+ http.Error(w, http.StatusText(status), status)
+ }
+ return nil, err
+}
+
+// checkSameOrigin returns true if the origin is not set or is equal to the request host.
+func checkSameOrigin(r *http.Request) bool {
+ origin := r.Header["Origin"]
+ if len(origin) == 0 {
+ return true
+ }
+ u, err := url.Parse(origin[0])
+ if err != nil {
+ return false
+ }
+ return u.Host == r.Host
+}
+
+func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {
+ if u.Subprotocols != nil {
+ clientProtocols := Subprotocols(r)
+ for _, serverProtocol := range u.Subprotocols {
+ for _, clientProtocol := range clientProtocols {
+ if clientProtocol == serverProtocol {
+ return clientProtocol
+ }
+ }
+ }
+ } else if responseHeader != nil {
+ return responseHeader.Get("Sec-Websocket-Protocol")
+ }
+ return ""
+}
+
+// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
+//
+// The responseHeader is included in the response to the client's upgrade
+// request. Use the responseHeader to specify cookies (Set-Cookie) and the
+// application negotiated subprotocol (Sec-Websocket-Protocol).
+//
+// If the upgrade fails, then Upgrade replies to the client with an HTTP error
+// response.
+func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
+ if r.Method != "GET" {
+ return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: method not GET")
+ }
+ if values := r.Header["Sec-Websocket-Version"]; len(values) == 0 || values[0] != "13" {
+ return u.returnError(w, r, http.StatusBadRequest, "websocket: version != 13")
+ }
+
+ if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
+ return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find connection header with token 'upgrade'")
+ }
+
+ if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
+ return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find upgrade header with token 'websocket'")
+ }
+
+ checkOrigin := u.CheckOrigin
+ if checkOrigin == nil {
+ checkOrigin = checkSameOrigin
+ }
+ if !checkOrigin(r) {
+ return u.returnError(w, r, http.StatusForbidden, "websocket: origin not allowed")
+ }
+
+ challengeKey := r.Header.Get("Sec-Websocket-Key")
+ if challengeKey == "" {
+ return u.returnError(w, r, http.StatusBadRequest, "websocket: key missing or blank")
+ }
+
+ subprotocol := u.selectSubprotocol(r, responseHeader)
+
+ var (
+ netConn net.Conn
+ br *bufio.Reader
+ err error
+ )
+
+ h, ok := w.(http.Hijacker)
+ if !ok {
+ return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
+ }
+ var rw *bufio.ReadWriter
+ netConn, rw, err = h.Hijack()
+ if err != nil {
+ return u.returnError(w, r, http.StatusInternalServerError, err.Error())
+ }
+ br = rw.Reader
+
+ if br.Buffered() > 0 {
+ netConn.Close()
+ return nil, errors.New("websocket: client sent data before handshake is complete")
+ }
+
+ c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize)
+ c.subprotocol = subprotocol
+
+ p := c.writeBuf[:0]
+ p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...)
+ p = append(p, computeAcceptKey(challengeKey)...)
+ p = append(p, "\r\n"...)
+ if c.subprotocol != "" {
+ p = append(p, "Sec-Websocket-Protocol: "...)
+ p = append(p, c.subprotocol...)
+ p = append(p, "\r\n"...)
+ }
+ for k, vs := range responseHeader {
+ if k == "Sec-Websocket-Protocol" {
+ continue
+ }
+ for _, v := range vs {
+ p = append(p, k...)
+ p = append(p, ": "...)
+ for i := 0; i < len(v); i++ {
+ b := v[i]
+ if b <= 31 {
+ // prevent response splitting.
+ b = ' '
+ }
+ p = append(p, b)
+ }
+ p = append(p, "\r\n"...)
+ }
+ }
+ p = append(p, "\r\n"...)
+
+ // Clear deadlines set by HTTP server.
+ netConn.SetDeadline(time.Time{})
+
+ if u.HandshakeTimeout > 0 {
+ netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout))
+ }
+ if _, err = netConn.Write(p); err != nil {
+ netConn.Close()
+ return nil, err
+ }
+ if u.HandshakeTimeout > 0 {
+ netConn.SetWriteDeadline(time.Time{})
+ }
+
+ return c, nil
+}
+
+// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
+//
+// This function is deprecated, use websocket.Upgrader instead.
+//
+// The application is responsible for checking the request origin before
+// calling Upgrade. An example implementation of the same origin policy is:
+//
+// if req.Header.Get("Origin") != "http://"+req.Host {
+// http.Error(w, "Origin not allowed", 403)
+// return
+// }
+//
+// If the endpoint supports subprotocols, then the application is responsible
+// for negotiating the protocol used on the connection. Use the Subprotocols()
+// function to get the subprotocols requested by the client. Use the
+// Sec-Websocket-Protocol response header to specify the subprotocol selected
+// by the application.
+//
+// The responseHeader is included in the response to the client's upgrade
+// request. Use the responseHeader to specify cookies (Set-Cookie) and the
+// negotiated subprotocol (Sec-Websocket-Protocol).
+//
+// The connection buffers IO to the underlying network connection. The
+// readBufSize and writeBufSize parameters specify the size of the buffers to
+// use. Messages can be larger than the buffers.
+//
+// If the request is not a valid WebSocket handshake, then Upgrade returns an
+// error of type HandshakeError. Applications should handle this error by
+// replying to the client with an HTTP error response.
+func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) {
+ u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize}
+ u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) {
+ // don't return errors to maintain backwards compatibility
+ }
+ u.CheckOrigin = func(r *http.Request) bool {
+ // allow all connections by default
+ return true
+ }
+ return u.Upgrade(w, r, responseHeader)
+}
+
+// Subprotocols returns the subprotocols requested by the client in the
+// Sec-Websocket-Protocol header.
+func Subprotocols(r *http.Request) []string {
+ h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol"))
+ if h == "" {
+ return nil
+ }
+ protocols := strings.Split(h, ",")
+ for i := range protocols {
+ protocols[i] = strings.TrimSpace(protocols[i])
+ }
+ return protocols
+}
+
+// IsWebSocketUpgrade returns true if the client requested upgrade to the
+// WebSocket protocol.
+func IsWebSocketUpgrade(r *http.Request) bool {
+ return tokenListContainsValue(r.Header, "Connection", "upgrade") &&
+ tokenListContainsValue(r.Header, "Upgrade", "websocket")
+}
diff --git a/vendor/github.com/gorilla/websocket/util.go b/vendor/github.com/gorilla/websocket/util.go
new file mode 100644
index 000000000..ffdc265ed
--- /dev/null
+++ b/vendor/github.com/gorilla/websocket/util.go
@@ -0,0 +1,44 @@
+// Copyright 2013 The Gorilla WebSocket 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 websocket
+
+import (
+ "crypto/rand"
+ "crypto/sha1"
+ "encoding/base64"
+ "io"
+ "net/http"
+ "strings"
+)
+
+// tokenListContainsValue returns true if the 1#token header with the given
+// name contains token.
+func tokenListContainsValue(header http.Header, name string, value string) bool {
+ for _, v := range header[name] {
+ for _, s := range strings.Split(v, ",") {
+ if strings.EqualFold(value, strings.TrimSpace(s)) {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
+
+func computeAcceptKey(challengeKey string) string {
+ h := sha1.New()
+ h.Write([]byte(challengeKey))
+ h.Write(keyGUID)
+ return base64.StdEncoding.EncodeToString(h.Sum(nil))
+}
+
+func generateChallengeKey() (string, error) {
+ p := make([]byte, 16)
+ if _, err := io.ReadFull(rand.Reader, p); err != nil {
+ return "", err
+ }
+ return base64.StdEncoding.EncodeToString(p), nil
+}
diff --git a/vendor/github.com/lib/pq/.gitignore b/vendor/github.com/lib/pq/.gitignore
new file mode 100644
index 000000000..0f1d00e11
--- /dev/null
+++ b/vendor/github.com/lib/pq/.gitignore
@@ -0,0 +1,4 @@
+.db
+*.test
+*~
+*.swp
diff --git a/vendor/github.com/lib/pq/.travis.yml b/vendor/github.com/lib/pq/.travis.yml
new file mode 100644
index 000000000..567c7c666
--- /dev/null
+++ b/vendor/github.com/lib/pq/.travis.yml
@@ -0,0 +1,67 @@
+language: go
+
+go:
+ - 1.4
+ - 1.5
+ - 1.6
+ - tip
+
+before_install:
+ - psql --version
+ - sudo /etc/init.d/postgresql stop
+ - sudo apt-get -y --purge remove postgresql libpq-dev libpq5 postgresql-client-common postgresql-common
+ - sudo rm -rf /var/lib/postgresql
+ - wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
+ - sudo sh -c "echo deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main $PGVERSION >> /etc/apt/sources.list.d/postgresql.list"
+ - sudo apt-get update -qq
+ - sudo apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::="--force-confnew" install postgresql-$PGVERSION postgresql-server-dev-$PGVERSION postgresql-contrib-$PGVERSION
+ - sudo chmod 777 /etc/postgresql/$PGVERSION/main/pg_hba.conf
+ - echo "local all postgres trust" > /etc/postgresql/$PGVERSION/main/pg_hba.conf
+ - echo "local all all trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
+ - echo "hostnossl all pqgossltest 127.0.0.1/32 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
+ - echo "hostnossl all pqgosslcert 127.0.0.1/32 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
+ - echo "hostssl all pqgossltest 127.0.0.1/32 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
+ - echo "hostssl all pqgosslcert 127.0.0.1/32 cert" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
+ - echo "host all all 127.0.0.1/32 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
+ - echo "hostnossl all pqgossltest ::1/128 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
+ - echo "hostnossl all pqgosslcert ::1/128 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
+ - echo "hostssl all pqgossltest ::1/128 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
+ - echo "hostssl all pqgosslcert ::1/128 cert" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
+ - echo "host all all ::1/128 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
+ - sudo install -o postgres -g postgres -m 600 -t /var/lib/postgresql/$PGVERSION/main/ certs/server.key certs/server.crt certs/root.crt
+ - sudo bash -c "[[ '${PGVERSION}' < '9.2' ]] || (echo \"ssl_cert_file = 'server.crt'\" >> /etc/postgresql/$PGVERSION/main/postgresql.conf)"
+ - sudo bash -c "[[ '${PGVERSION}' < '9.2' ]] || (echo \"ssl_key_file = 'server.key'\" >> /etc/postgresql/$PGVERSION/main/postgresql.conf)"
+ - sudo bash -c "[[ '${PGVERSION}' < '9.2' ]] || (echo \"ssl_ca_file = 'root.crt'\" >> /etc/postgresql/$PGVERSION/main/postgresql.conf)"
+ - sudo sh -c "echo 127.0.0.1 postgres >> /etc/hosts"
+ - sudo ls -l /var/lib/postgresql/$PGVERSION/main/
+ - sudo cat /etc/postgresql/$PGVERSION/main/postgresql.conf
+ - sudo chmod 600 $PQSSLCERTTEST_PATH/postgresql.key
+ - sudo /etc/init.d/postgresql restart
+
+env:
+ global:
+ - PGUSER=postgres
+ - PQGOSSLTESTS=1
+ - PQSSLCERTTEST_PATH=$PWD/certs
+ - PGHOST=127.0.0.1
+ matrix:
+ - PGVERSION=9.5 PQTEST_BINARY_PARAMETERS=yes
+ - PGVERSION=9.4 PQTEST_BINARY_PARAMETERS=yes
+ - PGVERSION=9.3 PQTEST_BINARY_PARAMETERS=yes
+ - PGVERSION=9.2 PQTEST_BINARY_PARAMETERS=yes
+ - PGVERSION=9.1 PQTEST_BINARY_PARAMETERS=yes
+ - PGVERSION=9.0 PQTEST_BINARY_PARAMETERS=yes
+ - PGVERSION=9.5 PQTEST_BINARY_PARAMETERS=no
+ - PGVERSION=9.4 PQTEST_BINARY_PARAMETERS=no
+ - PGVERSION=9.3 PQTEST_BINARY_PARAMETERS=no
+ - PGVERSION=9.2 PQTEST_BINARY_PARAMETERS=no
+ - PGVERSION=9.1 PQTEST_BINARY_PARAMETERS=no
+ - PGVERSION=9.0 PQTEST_BINARY_PARAMETERS=no
+
+script:
+ - go test -v ./...
+
+before_script:
+ - psql -c 'create database pqgotest' -U postgres
+ - psql -c 'create user pqgossltest' -U postgres
+ - psql -c 'create user pqgosslcert' -U postgres
diff --git a/vendor/github.com/lib/pq/CONTRIBUTING.md b/vendor/github.com/lib/pq/CONTRIBUTING.md
new file mode 100644
index 000000000..84c937f15
--- /dev/null
+++ b/vendor/github.com/lib/pq/CONTRIBUTING.md
@@ -0,0 +1,29 @@
+## Contributing to pq
+
+`pq` has a backlog of pull requests, but contributions are still very
+much welcome. You can help with patch review, submitting bug reports,
+or adding new functionality. There is no formal style guide, but
+please conform to the style of existing code and general Go formatting
+conventions when submitting patches.
+
+### Patch review
+
+Help review existing open pull requests by commenting on the code or
+proposed functionality.
+
+### Bug reports
+
+We appreciate any bug reports, but especially ones with self-contained
+(doesn't depend on code outside of pq), minimal (can't be simplified
+further) test cases. It's especially helpful if you can submit a pull
+request with just the failing test case (you'll probably want to
+pattern it after the tests in
+[conn_test.go](https://github.com/lib/pq/blob/master/conn_test.go).
+
+### New functionality
+
+There are a number of pending patches for new functionality, so
+additional feature patches will take a while to merge. Still, patches
+are generally reviewed based on usefulness and complexity in addition
+to time-in-queue, so if you have a knockout idea, take a shot. Feel
+free to open an issue discussion your proposed patch beforehand.
diff --git a/vendor/github.com/lib/pq/LICENSE.md b/vendor/github.com/lib/pq/LICENSE.md
new file mode 100644
index 000000000..5773904a3
--- /dev/null
+++ b/vendor/github.com/lib/pq/LICENSE.md
@@ -0,0 +1,8 @@
+Copyright (c) 2011-2013, 'pq' Contributors
+Portions Copyright (C) 2011 Blake Mizerany
+
+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/github.com/lib/pq/README.md b/vendor/github.com/lib/pq/README.md
new file mode 100644
index 000000000..b4e3f45cb
--- /dev/null
+++ b/vendor/github.com/lib/pq/README.md
@@ -0,0 +1,105 @@
+# pq - A pure Go postgres driver for Go's database/sql package
+
+[![Build Status](https://travis-ci.org/lib/pq.png?branch=master)](https://travis-ci.org/lib/pq)
+
+## Install
+
+ go get github.com/lib/pq
+
+## Docs
+
+For detailed documentation and basic usage examples, please see the package
+documentation at <http://godoc.org/github.com/lib/pq>.
+
+## Tests
+
+`go test` is used for testing. A running PostgreSQL server is
+required, with the ability to log in. The default database to connect
+to test with is "pqgotest," but it can be overridden using environment
+variables.
+
+Example:
+
+ PGHOST=/var/run/postgresql go test github.com/lib/pq
+
+Optionally, a benchmark suite can be run as part of the tests:
+
+ PGHOST=/var/run/postgresql go test -bench .
+
+## Features
+
+* SSL
+* Handles bad connections for `database/sql`
+* Scan `time.Time` correctly (i.e. `timestamp[tz]`, `time[tz]`, `date`)
+* Scan binary blobs correctly (i.e. `bytea`)
+* Package for `hstore` support
+* COPY FROM support
+* pq.ParseURL for converting urls to connection strings for sql.Open.
+* Many libpq compatible environment variables
+* Unix socket support
+* Notifications: `LISTEN`/`NOTIFY`
+* pgpass support
+
+## Future / Things you can help with
+
+* Better COPY FROM / COPY TO (see discussion in #181)
+
+## Thank you (alphabetical)
+
+Some of these contributors are from the original library `bmizerany/pq.go` whose
+code still exists in here.
+
+* Andy Balholm (andybalholm)
+* Ben Berkert (benburkert)
+* Benjamin Heatwole (bheatwole)
+* Bill Mill (llimllib)
+* Bjørn Madsen (aeons)
+* Blake Gentry (bgentry)
+* Brad Fitzpatrick (bradfitz)
+* Charlie Melbye (cmelbye)
+* Chris Bandy (cbandy)
+* Chris Gilling (cgilling)
+* Chris Walsh (cwds)
+* Dan Sosedoff (sosedoff)
+* Daniel Farina (fdr)
+* Eric Chlebek (echlebek)
+* Eric Garrido (minusnine)
+* Eric Urban (hydrogen18)
+* Everyone at The Go Team
+* Evan Shaw (edsrzf)
+* Ewan Chou (coocood)
+* Fazal Majid (fazalmajid)
+* Federico Romero (federomero)
+* Fumin (fumin)
+* Gary Burd (garyburd)
+* Heroku (heroku)
+* James Pozdena (jpoz)
+* Jason McVetta (jmcvetta)
+* Jeremy Jay (pbnjay)
+* Joakim Sernbrant (serbaut)
+* John Gallagher (jgallagher)
+* Jonathan Rudenberg (titanous)
+* Joël Stemmer (jstemmer)
+* Kamil Kisiel (kisielk)
+* Kelly Dunn (kellydunn)
+* Keith Rarick (kr)
+* Kir Shatrov (kirs)
+* Lann Martin (lann)
+* Maciek Sakrejda (deafbybeheading)
+* Marc Brinkmann (mbr)
+* Marko Tiikkaja (johto)
+* Matt Newberry (MattNewberry)
+* Matt Robenolt (mattrobenolt)
+* Martin Olsen (martinolsen)
+* Mike Lewis (mikelikespie)
+* Nicolas Patry (Narsil)
+* Oliver Tonnhofer (olt)
+* Patrick Hayes (phayes)
+* Paul Hammond (paulhammond)
+* Ryan Smith (ryandotsmith)
+* Samuel Stauffer (samuel)
+* Timothée Peignier (cyberdelia)
+* Travis Cline (tmc)
+* TruongSinh Tran-Nguyen (truongsinh)
+* Yaismel Miranda (ympons)
+* notedit (notedit)
diff --git a/vendor/github.com/lib/pq/buf.go b/vendor/github.com/lib/pq/buf.go
new file mode 100644
index 000000000..666b0012a
--- /dev/null
+++ b/vendor/github.com/lib/pq/buf.go
@@ -0,0 +1,91 @@
+package pq
+
+import (
+ "bytes"
+ "encoding/binary"
+
+ "github.com/lib/pq/oid"
+)
+
+type readBuf []byte
+
+func (b *readBuf) int32() (n int) {
+ n = int(int32(binary.BigEndian.Uint32(*b)))
+ *b = (*b)[4:]
+ return
+}
+
+func (b *readBuf) oid() (n oid.Oid) {
+ n = oid.Oid(binary.BigEndian.Uint32(*b))
+ *b = (*b)[4:]
+ return
+}
+
+// N.B: this is actually an unsigned 16-bit integer, unlike int32
+func (b *readBuf) int16() (n int) {
+ n = int(binary.BigEndian.Uint16(*b))
+ *b = (*b)[2:]
+ return
+}
+
+func (b *readBuf) string() string {
+ i := bytes.IndexByte(*b, 0)
+ if i < 0 {
+ errorf("invalid message format; expected string terminator")
+ }
+ s := (*b)[:i]
+ *b = (*b)[i+1:]
+ return string(s)
+}
+
+func (b *readBuf) next(n int) (v []byte) {
+ v = (*b)[:n]
+ *b = (*b)[n:]
+ return
+}
+
+func (b *readBuf) byte() byte {
+ return b.next(1)[0]
+}
+
+type writeBuf struct {
+ buf []byte
+ pos int
+}
+
+func (b *writeBuf) int32(n int) {
+ x := make([]byte, 4)
+ binary.BigEndian.PutUint32(x, uint32(n))
+ b.buf = append(b.buf, x...)
+}
+
+func (b *writeBuf) int16(n int) {
+ x := make([]byte, 2)
+ binary.BigEndian.PutUint16(x, uint16(n))
+ b.buf = append(b.buf, x...)
+}
+
+func (b *writeBuf) string(s string) {
+ b.buf = append(b.buf, (s + "\000")...)
+}
+
+func (b *writeBuf) byte(c byte) {
+ b.buf = append(b.buf, c)
+}
+
+func (b *writeBuf) bytes(v []byte) {
+ b.buf = append(b.buf, v...)
+}
+
+func (b *writeBuf) wrap() []byte {
+ p := b.buf[b.pos:]
+ binary.BigEndian.PutUint32(p, uint32(len(p)))
+ return b.buf
+}
+
+func (b *writeBuf) next(c byte) {
+ p := b.buf[b.pos:]
+ binary.BigEndian.PutUint32(p, uint32(len(p)))
+ b.pos = len(b.buf) + 1
+ b.buf = append(b.buf, c, 0, 0, 0, 0)
+}
diff --git a/vendor/github.com/lib/pq/conn.go b/vendor/github.com/lib/pq/conn.go
new file mode 100644
index 000000000..336c89449
--- /dev/null
+++ b/vendor/github.com/lib/pq/conn.go
@@ -0,0 +1,1847 @@
+package pq
+
+import (
+ "bufio"
+ "crypto/md5"
+ "crypto/tls"
+ "crypto/x509"
+ "database/sql"
+ "database/sql/driver"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "os"
+ "os/user"
+ "path"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "time"
+ "unicode"
+
+ "github.com/lib/pq/oid"
+)
+
+// Common error types
+var (
+ ErrNotSupported = errors.New("pq: Unsupported command")
+ ErrInFailedTransaction = errors.New("pq: Could not complete operation in a failed transaction")
+ ErrSSLNotSupported = errors.New("pq: SSL is not enabled on the server")
+ ErrSSLKeyHasWorldPermissions = errors.New("pq: Private key file has group or world access. Permissions should be u=rw (0600) or less.")
+ ErrCouldNotDetectUsername = errors.New("pq: Could not detect default username. Please provide one explicitly.")
+)
+
+type drv struct{}
+
+func (d *drv) Open(name string) (driver.Conn, error) {
+ return Open(name)
+}
+
+func init() {
+ sql.Register("postgres", &drv{})
+}
+
+type parameterStatus struct {
+ // server version in the same format as server_version_num, or 0 if
+ // unavailable
+ serverVersion int
+
+ // the current location based on the TimeZone value of the session, if
+ // available
+ currentLocation *time.Location
+}
+
+type transactionStatus byte
+
+const (
+ txnStatusIdle transactionStatus = 'I'
+ txnStatusIdleInTransaction transactionStatus = 'T'
+ txnStatusInFailedTransaction transactionStatus = 'E'
+)
+
+func (s transactionStatus) String() string {
+ switch s {
+ case txnStatusIdle:
+ return "idle"
+ case txnStatusIdleInTransaction:
+ return "idle in transaction"
+ case txnStatusInFailedTransaction:
+ return "in a failed transaction"
+ default:
+ errorf("unknown transactionStatus %d", s)
+ }
+
+ panic("not reached")
+}
+
+type Dialer interface {
+ Dial(network, address string) (net.Conn, error)
+ DialTimeout(network, address string, timeout time.Duration) (net.Conn, error)
+}
+
+type defaultDialer struct{}
+
+func (d defaultDialer) Dial(ntw, addr string) (net.Conn, error) {
+ return net.Dial(ntw, addr)
+}
+func (d defaultDialer) DialTimeout(ntw, addr string, timeout time.Duration) (net.Conn, error) {
+ return net.DialTimeout(ntw, addr, timeout)
+}
+
+type conn struct {
+ c net.Conn
+ buf *bufio.Reader
+ namei int
+ scratch [512]byte
+ txnStatus transactionStatus
+
+ parameterStatus parameterStatus
+
+ saveMessageType byte
+ saveMessageBuffer []byte
+
+ // If true, this connection is bad and all public-facing functions should
+ // return ErrBadConn.
+ bad bool
+
+ // If set, this connection should never use the binary format when
+ // receiving query results from prepared statements. Only provided for
+ // debugging.
+ disablePreparedBinaryResult bool
+
+ // Whether to always send []byte parameters over as binary. Enables single
+ // round-trip mode for non-prepared Query calls.
+ binaryParameters bool
+}
+
+// Handle driver-side settings in parsed connection string.
+func (c *conn) handleDriverSettings(o values) (err error) {
+ boolSetting := func(key string, val *bool) error {
+ if value := o.Get(key); value != "" {
+ if value == "yes" {
+ *val = true
+ } else if value == "no" {
+ *val = false
+ } else {
+ return fmt.Errorf("unrecognized value %q for %s", value, key)
+ }
+ }
+ return nil
+ }
+
+ err = boolSetting("disable_prepared_binary_result", &c.disablePreparedBinaryResult)
+ if err != nil {
+ return err
+ }
+ err = boolSetting("binary_parameters", &c.binaryParameters)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (c *conn) handlePgpass(o values) {
+ // if a password was supplied, do not process .pgpass
+ _, ok := o["password"]
+ if ok {
+ return
+ }
+ filename := os.Getenv("PGPASSFILE")
+ if filename == "" {
+ // XXX this code doesn't work on Windows where the default filename is
+ // XXX %APPDATA%\postgresql\pgpass.conf
+ user, err := user.Current()
+ if err != nil {
+ return
+ }
+ filename = filepath.Join(user.HomeDir, ".pgpass")
+ }
+ fileinfo, err := os.Stat(filename)
+ if err != nil {
+ return
+ }
+ mode := fileinfo.Mode()
+ if mode&(0x77) != 0 {
+ // XXX should warn about incorrect .pgpass permissions as psql does
+ return
+ }
+ file, err := os.Open(filename)
+ if err != nil {
+ return
+ }
+ defer file.Close()
+ scanner := bufio.NewScanner(io.Reader(file))
+ hostname := o.Get("host")
+ ntw, _ := network(o)
+ port := o.Get("port")
+ db := o.Get("dbname")
+ username := o.Get("user")
+ // From: https://github.com/tg/pgpass/blob/master/reader.go
+ getFields := func(s string) []string {
+ fs := make([]string, 0, 5)
+ f := make([]rune, 0, len(s))
+
+ var esc bool
+ for _, c := range s {
+ switch {
+ case esc:
+ f = append(f, c)
+ esc = false
+ case c == '\\':
+ esc = true
+ case c == ':':
+ fs = append(fs, string(f))
+ f = f[:0]
+ default:
+ f = append(f, c)
+ }
+ }
+ return append(fs, string(f))
+ }
+ for scanner.Scan() {
+ line := scanner.Text()
+ if len(line) == 0 || line[0] == '#' {
+ continue
+ }
+ split := getFields(line)
+ if len(split) != 5 {
+ continue
+ }
+ if (split[0] == "*" || split[0] == hostname || (split[0] == "localhost" && (hostname == "" || ntw == "unix"))) && (split[1] == "*" || split[1] == port) && (split[2] == "*" || split[2] == db) && (split[3] == "*" || split[3] == username) {
+ o["password"] = split[4]
+ return
+ }
+ }
+}
+
+func (c *conn) writeBuf(b byte) *writeBuf {
+ c.scratch[0] = b
+ return &writeBuf{
+ buf: c.scratch[:5],
+ pos: 1,
+ }
+}
+
+func Open(name string) (_ driver.Conn, err error) {
+ return DialOpen(defaultDialer{}, name)
+}
+
+func DialOpen(d Dialer, name string) (_ driver.Conn, err error) {
+ // Handle any panics during connection initialization. Note that we
+ // specifically do *not* want to use errRecover(), as that would turn any
+ // connection errors into ErrBadConns, hiding the real error message from
+ // the user.
+ defer errRecoverNoErrBadConn(&err)
+
+ o := make(values)
+
+ // A number of defaults are applied here, in this order:
+ //
+ // * Very low precedence defaults applied in every situation
+ // * Environment variables
+ // * Explicitly passed connection information
+ o.Set("host", "localhost")
+ o.Set("port", "5432")
+ // N.B.: Extra float digits should be set to 3, but that breaks
+ // Postgres 8.4 and older, where the max is 2.
+ o.Set("extra_float_digits", "2")
+ for k, v := range parseEnviron(os.Environ()) {
+ o.Set(k, v)
+ }
+
+ if strings.HasPrefix(name, "postgres://") || strings.HasPrefix(name, "postgresql://") {
+ name, err = ParseURL(name)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if err := parseOpts(name, o); err != nil {
+ return nil, err
+ }
+
+ // Use the "fallback" application name if necessary
+ if fallback := o.Get("fallback_application_name"); fallback != "" {
+ if !o.Isset("application_name") {
+ o.Set("application_name", fallback)
+ }
+ }
+
+ // We can't work with any client_encoding other than UTF-8 currently.
+ // However, we have historically allowed the user to set it to UTF-8
+ // explicitly, and there's no reason to break such programs, so allow that.
+ // Note that the "options" setting could also set client_encoding, but
+ // parsing its value is not worth it. Instead, we always explicitly send
+ // client_encoding as a separate run-time parameter, which should override
+ // anything set in options.
+ if enc := o.Get("client_encoding"); enc != "" && !isUTF8(enc) {
+ return nil, errors.New("client_encoding must be absent or 'UTF8'")
+ }
+ o.Set("client_encoding", "UTF8")
+ // DateStyle needs a similar treatment.
+ if datestyle := o.Get("datestyle"); datestyle != "" {
+ if datestyle != "ISO, MDY" {
+ panic(fmt.Sprintf("setting datestyle must be absent or %v; got %v",
+ "ISO, MDY", datestyle))
+ }
+ } else {
+ o.Set("datestyle", "ISO, MDY")
+ }
+
+ // If a user is not provided by any other means, the last
+ // resort is to use the current operating system provided user
+ // name.
+ if o.Get("user") == "" {
+ u, err := userCurrent()
+ if err != nil {
+ return nil, err
+ } else {
+ o.Set("user", u)
+ }
+ }
+
+ cn := &conn{}
+ err = cn.handleDriverSettings(o)
+ if err != nil {
+ return nil, err
+ }
+ cn.handlePgpass(o)
+
+ cn.c, err = dial(d, o)
+ if err != nil {
+ return nil, err
+ }
+ cn.ssl(o)
+ cn.buf = bufio.NewReader(cn.c)
+ cn.startup(o)
+
+ // reset the deadline, in case one was set (see dial)
+ if timeout := o.Get("connect_timeout"); timeout != "" && timeout != "0" {
+ err = cn.c.SetDeadline(time.Time{})
+ }
+ return cn, err
+}
+
+func dial(d Dialer, o values) (net.Conn, error) {
+ ntw, addr := network(o)
+ // SSL is not necessary or supported over UNIX domain sockets
+ if ntw == "unix" {
+ o["sslmode"] = "disable"
+ }
+
+ // Zero or not specified means wait indefinitely.
+ if timeout := o.Get("connect_timeout"); timeout != "" && timeout != "0" {
+ seconds, err := strconv.ParseInt(timeout, 10, 0)
+ if err != nil {
+ return nil, fmt.Errorf("invalid value for parameter connect_timeout: %s", err)
+ }
+ duration := time.Duration(seconds) * time.Second
+ // connect_timeout should apply to the entire connection establishment
+ // procedure, so we both use a timeout for the TCP connection
+ // establishment and set a deadline for doing the initial handshake.
+ // The deadline is then reset after startup() is done.
+ deadline := time.Now().Add(duration)
+ conn, err := d.DialTimeout(ntw, addr, duration)
+ if err != nil {
+ return nil, err
+ }
+ err = conn.SetDeadline(deadline)
+ return conn, err
+ }
+ return d.Dial(ntw, addr)
+}
+
+func network(o values) (string, string) {
+ host := o.Get("host")
+
+ if strings.HasPrefix(host, "/") {
+ sockPath := path.Join(host, ".s.PGSQL."+o.Get("port"))
+ return "unix", sockPath
+ }
+
+ return "tcp", net.JoinHostPort(host, o.Get("port"))
+}
+
+type values map[string]string
+
+func (vs values) Set(k, v string) {
+ vs[k] = v
+}
+
+func (vs values) Get(k string) (v string) {
+ return vs[k]
+}
+
+func (vs values) Isset(k string) bool {
+ _, ok := vs[k]
+ return ok
+}
+
+// scanner implements a tokenizer for libpq-style option strings.
+type scanner struct {
+ s []rune
+ i int
+}
+
+// newScanner returns a new scanner initialized with the option string s.
+func newScanner(s string) *scanner {
+ return &scanner{[]rune(s), 0}
+}
+
+// Next returns the next rune.
+// It returns 0, false if the end of the text has been reached.
+func (s *scanner) Next() (rune, bool) {
+ if s.i >= len(s.s) {
+ return 0, false
+ }
+ r := s.s[s.i]
+ s.i++
+ return r, true
+}
+
+// SkipSpaces returns the next non-whitespace rune.
+// It returns 0, false if the end of the text has been reached.
+func (s *scanner) SkipSpaces() (rune, bool) {
+ r, ok := s.Next()
+ for unicode.IsSpace(r) && ok {
+ r, ok = s.Next()
+ }
+ return r, ok
+}
+
+// parseOpts parses the options from name and adds them to the values.
+//
+// The parsing code is based on conninfo_parse from libpq's fe-connect.c
+func parseOpts(name string, o values) error {
+ s := newScanner(name)
+
+ for {
+ var (
+ keyRunes, valRunes []rune
+ r rune
+ ok bool
+ )
+
+ if r, ok = s.SkipSpaces(); !ok {
+ break
+ }
+
+ // Scan the key
+ for !unicode.IsSpace(r) && r != '=' {
+ keyRunes = append(keyRunes, r)
+ if r, ok = s.Next(); !ok {
+ break
+ }
+ }
+
+ // Skip any whitespace if we're not at the = yet
+ if r != '=' {
+ r, ok = s.SkipSpaces()
+ }
+
+ // The current character should be =
+ if r != '=' || !ok {
+ return fmt.Errorf(`missing "=" after %q in connection info string"`, string(keyRunes))
+ }
+
+ // Skip any whitespace after the =
+ if r, ok = s.SkipSpaces(); !ok {
+ // If we reach the end here, the last value is just an empty string as per libpq.
+ o.Set(string(keyRunes), "")
+ break
+ }
+
+ if r != '\'' {
+ for !unicode.IsSpace(r) {
+ if r == '\\' {
+ if r, ok = s.Next(); !ok {
+ return fmt.Errorf(`missing character after backslash`)
+ }
+ }
+ valRunes = append(valRunes, r)
+
+ if r, ok = s.Next(); !ok {
+ break
+ }
+ }
+ } else {
+ quote:
+ for {
+ if r, ok = s.Next(); !ok {
+ return fmt.Errorf(`unterminated quoted string literal in connection string`)
+ }
+ switch r {
+ case '\'':
+ break quote
+ case '\\':
+ r, _ = s.Next()
+ fallthrough
+ default:
+ valRunes = append(valRunes, r)
+ }
+ }
+ }
+
+ o.Set(string(keyRunes), string(valRunes))
+ }
+
+ return nil
+}
+
+func (cn *conn) isInTransaction() bool {
+ return cn.txnStatus == txnStatusIdleInTransaction ||
+ cn.txnStatus == txnStatusInFailedTransaction
+}
+
+func (cn *conn) checkIsInTransaction(intxn bool) {
+ if cn.isInTransaction() != intxn {
+ cn.bad = true
+ errorf("unexpected transaction status %v", cn.txnStatus)
+ }
+}
+
+func (cn *conn) Begin() (_ driver.Tx, err error) {
+ if cn.bad {
+ return nil, driver.ErrBadConn
+ }
+ defer cn.errRecover(&err)
+
+ cn.checkIsInTransaction(false)
+ _, commandTag, err := cn.simpleExec("BEGIN")
+ if err != nil {
+ return nil, err
+ }
+ if commandTag != "BEGIN" {
+ cn.bad = true
+ return nil, fmt.Errorf("unexpected command tag %s", commandTag)
+ }
+ if cn.txnStatus != txnStatusIdleInTransaction {
+ cn.bad = true
+ return nil, fmt.Errorf("unexpected transaction status %v", cn.txnStatus)
+ }
+ return cn, nil
+}
+
+func (cn *conn) Commit() (err error) {
+ if cn.bad {
+ return driver.ErrBadConn
+ }
+ defer cn.errRecover(&err)
+
+ cn.checkIsInTransaction(true)
+ // We don't want the client to think that everything is okay if it tries
+ // to commit a failed transaction. However, no matter what we return,
+ // database/sql will release this connection back into the free connection
+ // pool so we have to abort the current transaction here. Note that you
+ // would get the same behaviour if you issued a COMMIT in a failed
+ // transaction, so it's also the least surprising thing to do here.
+ if cn.txnStatus == txnStatusInFailedTransaction {
+ if err := cn.Rollback(); err != nil {
+ return err
+ }
+ return ErrInFailedTransaction
+ }
+
+ _, commandTag, err := cn.simpleExec("COMMIT")
+ if err != nil {
+ if cn.isInTransaction() {
+ cn.bad = true
+ }
+ return err
+ }
+ if commandTag != "COMMIT" {
+ cn.bad = true
+ return fmt.Errorf("unexpected command tag %s", commandTag)
+ }
+ cn.checkIsInTransaction(false)
+ return nil
+}
+
+func (cn *conn) Rollback() (err error) {
+ if cn.bad {
+ return driver.ErrBadConn
+ }
+ defer cn.errRecover(&err)
+
+ cn.checkIsInTransaction(true)
+ _, commandTag, err := cn.simpleExec("ROLLBACK")
+ if err != nil {
+ if cn.isInTransaction() {
+ cn.bad = true
+ }
+ return err
+ }
+ if commandTag != "ROLLBACK" {
+ return fmt.Errorf("unexpected command tag %s", commandTag)
+ }
+ cn.checkIsInTransaction(false)
+ return nil
+}
+
+func (cn *conn) gname() string {
+ cn.namei++
+ return strconv.FormatInt(int64(cn.namei), 10)
+}
+
+func (cn *conn) simpleExec(q string) (res driver.Result, commandTag string, err error) {
+ b := cn.writeBuf('Q')
+ b.string(q)
+ cn.send(b)
+
+ for {
+ t, r := cn.recv1()
+ switch t {
+ case 'C':
+ res, commandTag = cn.parseComplete(r.string())
+ case 'Z':
+ cn.processReadyForQuery(r)
+ // done
+ return
+ case 'E':
+ err = parseError(r)
+ case 'T', 'D', 'I':
+ // ignore any results
+ default:
+ cn.bad = true
+ errorf("unknown response for simple query: %q", t)
+ }
+ }
+}
+
+func (cn *conn) simpleQuery(q string) (res *rows, err error) {
+ defer cn.errRecover(&err)
+
+ b := cn.writeBuf('Q')
+ b.string(q)
+ cn.send(b)
+
+ for {
+ t, r := cn.recv1()
+ switch t {
+ case 'C', 'I':
+ // We allow queries which don't return any results through Query as
+ // well as Exec. We still have to give database/sql a rows object
+ // the user can close, though, to avoid connections from being
+ // leaked. A "rows" with done=true works fine for that purpose.
+ if err != nil {
+ cn.bad = true
+ errorf("unexpected message %q in simple query execution", t)
+ }
+ if res == nil {
+ res = &rows{
+ cn: cn,
+ }
+ }
+ res.done = true
+ case 'Z':
+ cn.processReadyForQuery(r)
+ // done
+ return
+ case 'E':
+ res = nil
+ err = parseError(r)
+ case 'D':
+ if res == nil {
+ cn.bad = true
+ errorf("unexpected DataRow in simple query execution")
+ }
+ // the query didn't fail; kick off to Next
+ cn.saveMessage(t, r)
+ return
+ case 'T':
+ // res might be non-nil here if we received a previous
+ // CommandComplete, but that's fine; just overwrite it
+ res = &rows{cn: cn}
+ res.colNames, res.colFmts, res.colTyps = parsePortalRowDescribe(r)
+
+ // To work around a bug in QueryRow in Go 1.2 and earlier, wait
+ // until the first DataRow has been received.
+ default:
+ cn.bad = true
+ errorf("unknown response for simple query: %q", t)
+ }
+ }
+}
+
+// Decides which column formats to use for a prepared statement. The input is
+// an array of type oids, one element per result column.
+func decideColumnFormats(colTyps []oid.Oid, forceText bool) (colFmts []format, colFmtData []byte) {
+ if len(colTyps) == 0 {
+ return nil, colFmtDataAllText
+ }
+
+ colFmts = make([]format, len(colTyps))
+ if forceText {
+ return colFmts, colFmtDataAllText
+ }
+
+ allBinary := true
+ allText := true
+ for i, o := range colTyps {
+ switch o {
+ // This is the list of types to use binary mode for when receiving them
+ // through a prepared statement. If a type appears in this list, it
+ // must also be implemented in binaryDecode in encode.go.
+ case oid.T_bytea:
+ fallthrough
+ case oid.T_int8:
+ fallthrough
+ case oid.T_int4:
+ fallthrough
+ case oid.T_int2:
+ colFmts[i] = formatBinary
+ allText = false
+
+ default:
+ allBinary = false
+ }
+ }
+
+ if allBinary {
+ return colFmts, colFmtDataAllBinary
+ } else if allText {
+ return colFmts, colFmtDataAllText
+ } else {
+ colFmtData = make([]byte, 2+len(colFmts)*2)
+ binary.BigEndian.PutUint16(colFmtData, uint16(len(colFmts)))
+ for i, v := range colFmts {
+ binary.BigEndian.PutUint16(colFmtData[2+i*2:], uint16(v))
+ }
+ return colFmts, colFmtData
+ }
+}
+
+func (cn *conn) prepareTo(q, stmtName string) *stmt {
+ st := &stmt{cn: cn, name: stmtName}
+
+ b := cn.writeBuf('P')
+ b.string(st.name)
+ b.string(q)
+ b.int16(0)
+
+ b.next('D')
+ b.byte('S')
+ b.string(st.name)
+
+ b.next('S')
+ cn.send(b)
+
+ cn.readParseResponse()
+ st.paramTyps, st.colNames, st.colTyps = cn.readStatementDescribeResponse()
+ st.colFmts, st.colFmtData = decideColumnFormats(st.colTyps, cn.disablePreparedBinaryResult)
+ cn.readReadyForQuery()
+ return st
+}
+
+func (cn *conn) Prepare(q string) (_ driver.Stmt, err error) {
+ if cn.bad {
+ return nil, driver.ErrBadConn
+ }
+ defer cn.errRecover(&err)
+
+ if len(q) >= 4 && strings.EqualFold(q[:4], "COPY") {
+ return cn.prepareCopyIn(q)
+ }
+ return cn.prepareTo(q, cn.gname()), nil
+}
+
+func (cn *conn) Close() (err error) {
+ if cn.bad {
+ return driver.ErrBadConn
+ }
+ defer cn.errRecover(&err)
+
+ // Don't go through send(); ListenerConn relies on us not scribbling on the
+ // scratch buffer of this connection.
+ err = cn.sendSimpleMessage('X')
+ if err != nil {
+ return err
+ }
+
+ return cn.c.Close()
+}
+
+// Implement the "Queryer" interface
+func (cn *conn) Query(query string, args []driver.Value) (_ driver.Rows, err error) {
+ if cn.bad {
+ return nil, driver.ErrBadConn
+ }
+ defer cn.errRecover(&err)
+
+ // Check to see if we can use the "simpleQuery" interface, which is
+ // *much* faster than going through prepare/exec
+ if len(args) == 0 {
+ return cn.simpleQuery(query)
+ }
+
+ if cn.binaryParameters {
+ cn.sendBinaryModeQuery(query, args)
+
+ cn.readParseResponse()
+ cn.readBindResponse()
+ rows := &rows{cn: cn}
+ rows.colNames, rows.colFmts, rows.colTyps = cn.readPortalDescribeResponse()
+ cn.postExecuteWorkaround()
+ return rows, nil
+ } else {
+ st := cn.prepareTo(query, "")
+ st.exec(args)
+ return &rows{
+ cn: cn,
+ colNames: st.colNames,
+ colTyps: st.colTyps,
+ colFmts: st.colFmts,
+ }, nil
+ }
+}
+
+// Implement the optional "Execer" interface for one-shot queries
+func (cn *conn) Exec(query string, args []driver.Value) (res driver.Result, err error) {
+ if cn.bad {
+ return nil, driver.ErrBadConn
+ }
+ defer cn.errRecover(&err)
+
+ // Check to see if we can use the "simpleExec" interface, which is
+ // *much* faster than going through prepare/exec
+ if len(args) == 0 {
+ // ignore commandTag, our caller doesn't care
+ r, _, err := cn.simpleExec(query)
+ return r, err
+ }
+
+ if cn.binaryParameters {
+ cn.sendBinaryModeQuery(query, args)
+
+ cn.readParseResponse()
+ cn.readBindResponse()
+ cn.readPortalDescribeResponse()
+ cn.postExecuteWorkaround()
+ res, _, err = cn.readExecuteResponse("Execute")
+ return res, err
+ } else {
+ // Use the unnamed statement to defer planning until bind
+ // time, or else value-based selectivity estimates cannot be
+ // used.
+ st := cn.prepareTo(query, "")
+ r, err := st.Exec(args)
+ if err != nil {
+ panic(err)
+ }
+ return r, err
+ }
+}
+
+func (cn *conn) send(m *writeBuf) {
+ _, err := cn.c.Write(m.wrap())
+ if err != nil {
+ panic(err)
+ }
+}
+
+func (cn *conn) sendStartupPacket(m *writeBuf) {
+ // sanity check
+ if m.buf[0] != 0 {
+ panic("oops")
+ }
+
+ _, err := cn.c.Write((m.wrap())[1:])
+ if err != nil {
+ panic(err)
+ }
+}
+
+// Send a message of type typ to the server on the other end of cn. The
+// message should have no payload. This method does not use the scratch
+// buffer.
+func (cn *conn) sendSimpleMessage(typ byte) (err error) {
+ _, err = cn.c.Write([]byte{typ, '\x00', '\x00', '\x00', '\x04'})
+ return err
+}
+
+// saveMessage memorizes a message and its buffer in the conn struct.
+// recvMessage will then return these values on the next call to it. This
+// method is useful in cases where you have to see what the next message is
+// going to be (e.g. to see whether it's an error or not) but you can't handle
+// the message yourself.
+func (cn *conn) saveMessage(typ byte, buf *readBuf) {
+ if cn.saveMessageType != 0 {
+ cn.bad = true
+ errorf("unexpected saveMessageType %d", cn.saveMessageType)
+ }
+ cn.saveMessageType = typ
+ cn.saveMessageBuffer = *buf
+}
+
+// recvMessage receives any message from the backend, or returns an error if
+// a problem occurred while reading the message.
+func (cn *conn) recvMessage(r *readBuf) (byte, error) {
+ // workaround for a QueryRow bug, see exec
+ if cn.saveMessageType != 0 {
+ t := cn.saveMessageType
+ *r = cn.saveMessageBuffer
+ cn.saveMessageType = 0
+ cn.saveMessageBuffer = nil
+ return t, nil
+ }
+
+ x := cn.scratch[:5]
+ _, err := io.ReadFull(cn.buf, x)
+ if err != nil {
+ return 0, err
+ }
+
+ // read the type and length of the message that follows
+ t := x[0]
+ n := int(binary.BigEndian.Uint32(x[1:])) - 4
+ var y []byte
+ if n <= len(cn.scratch) {
+ y = cn.scratch[:n]
+ } else {
+ y = make([]byte, n)
+ }
+ _, err = io.ReadFull(cn.buf, y)
+ if err != nil {
+ return 0, err
+ }
+ *r = y
+ return t, nil
+}
+
+// recv receives a message from the backend, but if an error happened while
+// reading the message or the received message was an ErrorResponse, it panics.
+// NoticeResponses are ignored. This function should generally be used only
+// during the startup sequence.
+func (cn *conn) recv() (t byte, r *readBuf) {
+ for {
+ var err error
+ r = &readBuf{}
+ t, err = cn.recvMessage(r)
+ if err != nil {
+ panic(err)
+ }
+
+ switch t {
+ case 'E':
+ panic(parseError(r))
+ case 'N':
+ // ignore
+ default:
+ return
+ }
+ }
+}
+
+// recv1Buf is exactly equivalent to recv1, except it uses a buffer supplied by
+// the caller to avoid an allocation.
+func (cn *conn) recv1Buf(r *readBuf) byte {
+ for {
+ t, err := cn.recvMessage(r)
+ if err != nil {
+ panic(err)
+ }
+
+ switch t {
+ case 'A', 'N':
+ // ignore
+ case 'S':
+ cn.processParameterStatus(r)
+ default:
+ return t
+ }
+ }
+}
+
+// recv1 receives a message from the backend, panicking if an error occurs
+// while attempting to read it. All asynchronous messages are ignored, with
+// the exception of ErrorResponse.
+func (cn *conn) recv1() (t byte, r *readBuf) {
+ r = &readBuf{}
+ t = cn.recv1Buf(r)
+ return t, r
+}
+
+func (cn *conn) ssl(o values) {
+ verifyCaOnly := false
+ tlsConf := tls.Config{}
+ switch mode := o.Get("sslmode"); mode {
+ case "require", "":
+ tlsConf.InsecureSkipVerify = true
+ case "verify-ca":
+ // We must skip TLS's own verification since it requires full
+ // verification since Go 1.3.
+ tlsConf.InsecureSkipVerify = true
+ verifyCaOnly = true
+ case "verify-full":
+ tlsConf.ServerName = o.Get("host")
+ case "disable":
+ return
+ default:
+ errorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode)
+ }
+
+ cn.setupSSLClientCertificates(&tlsConf, o)
+ cn.setupSSLCA(&tlsConf, o)
+
+ w := cn.writeBuf(0)
+ w.int32(80877103)
+ cn.sendStartupPacket(w)
+
+ b := cn.scratch[:1]
+ _, err := io.ReadFull(cn.c, b)
+ if err != nil {
+ panic(err)
+ }
+
+ if b[0] != 'S' {
+ panic(ErrSSLNotSupported)
+ }
+
+ client := tls.Client(cn.c, &tlsConf)
+ if verifyCaOnly {
+ cn.verifyCA(client, &tlsConf)
+ }
+ cn.c = client
+}
+
+// verifyCA carries out a TLS handshake to the server and verifies the
+// presented certificate against the effective CA, i.e. the one specified in
+// sslrootcert or the system CA if sslrootcert was not specified.
+func (cn *conn) verifyCA(client *tls.Conn, tlsConf *tls.Config) {
+ err := client.Handshake()
+ if err != nil {
+ panic(err)
+ }
+ certs := client.ConnectionState().PeerCertificates
+ opts := x509.VerifyOptions{
+ DNSName: client.ConnectionState().ServerName,
+ Intermediates: x509.NewCertPool(),
+ Roots: tlsConf.RootCAs,
+ }
+ for i, cert := range certs {
+ if i == 0 {
+ continue
+ }
+ opts.Intermediates.AddCert(cert)
+ }
+ _, err = certs[0].Verify(opts)
+ if err != nil {
+ panic(err)
+ }
+}
+
+// This function sets up SSL client certificates based on either the "sslkey"
+// and "sslcert" settings (possibly set via the environment variables PGSSLKEY
+// and PGSSLCERT, respectively), or if they aren't set, from the .postgresql
+// directory in the user's home directory. If the file paths are set
+// explicitly, the files must exist. The key file must also not be
+// world-readable, or this function will panic with
+// ErrSSLKeyHasWorldPermissions.
+func (cn *conn) setupSSLClientCertificates(tlsConf *tls.Config, o values) {
+ var missingOk bool
+
+ sslkey := o.Get("sslkey")
+ sslcert := o.Get("sslcert")
+ if sslkey != "" && sslcert != "" {
+ // If the user has set an sslkey and sslcert, they *must* exist.
+ missingOk = false
+ } else {
+ // Automatically load certificates from ~/.postgresql.
+ user, err := user.Current()
+ if err != nil {
+ // user.Current() might fail when cross-compiling. We have to
+ // ignore the error and continue without client certificates, since
+ // we wouldn't know where to load them from.
+ return
+ }
+
+ sslkey = filepath.Join(user.HomeDir, ".postgresql", "postgresql.key")
+ sslcert = filepath.Join(user.HomeDir, ".postgresql", "postgresql.crt")
+ missingOk = true
+ }
+
+ // Check that both files exist, and report the error or stop, depending on
+ // which behaviour we want. Note that we don't do any more extensive
+ // checks than this (such as checking that the paths aren't directories);
+ // LoadX509KeyPair() will take care of the rest.
+ keyfinfo, err := os.Stat(sslkey)
+ if err != nil && missingOk {
+ return
+ } else if err != nil {
+ panic(err)
+ }
+ _, err = os.Stat(sslcert)
+ if err != nil && missingOk {
+ return
+ } else if err != nil {
+ panic(err)
+ }
+
+ // If we got this far, the key file must also have the correct permissions
+ kmode := keyfinfo.Mode()
+ if kmode != kmode&0600 {
+ panic(ErrSSLKeyHasWorldPermissions)
+ }
+
+ cert, err := tls.LoadX509KeyPair(sslcert, sslkey)
+ if err != nil {
+ panic(err)
+ }
+ tlsConf.Certificates = []tls.Certificate{cert}
+}
+
+// Sets up RootCAs in the TLS configuration if sslrootcert is set.
+func (cn *conn) setupSSLCA(tlsConf *tls.Config, o values) {
+ if sslrootcert := o.Get("sslrootcert"); sslrootcert != "" {
+ tlsConf.RootCAs = x509.NewCertPool()
+
+ cert, err := ioutil.ReadFile(sslrootcert)
+ if err != nil {
+ panic(err)
+ }
+
+ ok := tlsConf.RootCAs.AppendCertsFromPEM(cert)
+ if !ok {
+ errorf("couldn't parse pem in sslrootcert")
+ }
+ }
+}
+
+// isDriverSetting returns true iff a setting is purely for configuring the
+// driver's options and should not be sent to the server in the connection
+// startup packet.
+func isDriverSetting(key string) bool {
+ switch key {
+ case "host", "port":
+ return true
+ case "password":
+ return true
+ case "sslmode", "sslcert", "sslkey", "sslrootcert":
+ return true
+ case "fallback_application_name":
+ return true
+ case "connect_timeout":
+ return true
+ case "disable_prepared_binary_result":
+ return true
+ case "binary_parameters":
+ return true
+
+ default:
+ return false
+ }
+}
+
+func (cn *conn) startup(o values) {
+ w := cn.writeBuf(0)
+ w.int32(196608)
+ // Send the backend the name of the database we want to connect to, and the
+ // user we want to connect as. Additionally, we send over any run-time
+ // parameters potentially included in the connection string. If the server
+ // doesn't recognize any of them, it will reply with an error.
+ for k, v := range o {
+ if isDriverSetting(k) {
+ // skip options which can't be run-time parameters
+ continue
+ }
+ // The protocol requires us to supply the database name as "database"
+ // instead of "dbname".
+ if k == "dbname" {
+ k = "database"
+ }
+ w.string(k)
+ w.string(v)
+ }
+ w.string("")
+ cn.sendStartupPacket(w)
+
+ for {
+ t, r := cn.recv()
+ switch t {
+ case 'K':
+ case 'S':
+ cn.processParameterStatus(r)
+ case 'R':
+ cn.auth(r, o)
+ case 'Z':
+ cn.processReadyForQuery(r)
+ return
+ default:
+ errorf("unknown response for startup: %q", t)
+ }
+ }
+}
+
+func (cn *conn) auth(r *readBuf, o values) {
+ switch code := r.int32(); code {
+ case 0:
+ // OK
+ case 3:
+ w := cn.writeBuf('p')
+ w.string(o.Get("password"))
+ cn.send(w)
+
+ t, r := cn.recv()
+ if t != 'R' {
+ errorf("unexpected password response: %q", t)
+ }
+
+ if r.int32() != 0 {
+ errorf("unexpected authentication response: %q", t)
+ }
+ case 5:
+ s := string(r.next(4))
+ w := cn.writeBuf('p')
+ w.string("md5" + md5s(md5s(o.Get("password")+o.Get("user"))+s))
+ cn.send(w)
+
+ t, r := cn.recv()
+ if t != 'R' {
+ errorf("unexpected password response: %q", t)
+ }
+
+ if r.int32() != 0 {
+ errorf("unexpected authentication response: %q", t)
+ }
+ default:
+ errorf("unknown authentication response: %d", code)
+ }
+}
+
+type format int
+
+const formatText format = 0
+const formatBinary format = 1
+
+// One result-column format code with the value 1 (i.e. all binary).
+var colFmtDataAllBinary []byte = []byte{0, 1, 0, 1}
+
+// No result-column format codes (i.e. all text).
+var colFmtDataAllText []byte = []byte{0, 0}
+
+type stmt struct {
+ cn *conn
+ name string
+ colNames []string
+ colFmts []format
+ colFmtData []byte
+ colTyps []oid.Oid
+ paramTyps []oid.Oid
+ closed bool
+}
+
+func (st *stmt) Close() (err error) {
+ if st.closed {
+ return nil
+ }
+ if st.cn.bad {
+ return driver.ErrBadConn
+ }
+ defer st.cn.errRecover(&err)
+
+ w := st.cn.writeBuf('C')
+ w.byte('S')
+ w.string(st.name)
+ st.cn.send(w)
+
+ st.cn.send(st.cn.writeBuf('S'))
+
+ t, _ := st.cn.recv1()
+ if t != '3' {
+ st.cn.bad = true
+ errorf("unexpected close response: %q", t)
+ }
+ st.closed = true
+
+ t, r := st.cn.recv1()
+ if t != 'Z' {
+ st.cn.bad = true
+ errorf("expected ready for query, but got: %q", t)
+ }
+ st.cn.processReadyForQuery(r)
+
+ return nil
+}
+
+func (st *stmt) Query(v []driver.Value) (r driver.Rows, err error) {
+ if st.cn.bad {
+ return nil, driver.ErrBadConn
+ }
+ defer st.cn.errRecover(&err)
+
+ st.exec(v)
+ return &rows{
+ cn: st.cn,
+ colNames: st.colNames,
+ colTyps: st.colTyps,
+ colFmts: st.colFmts,
+ }, nil
+}
+
+func (st *stmt) Exec(v []driver.Value) (res driver.Result, err error) {
+ if st.cn.bad {
+ return nil, driver.ErrBadConn
+ }
+ defer st.cn.errRecover(&err)
+
+ st.exec(v)
+ res, _, err = st.cn.readExecuteResponse("simple query")
+ return res, err
+}
+
+func (st *stmt) exec(v []driver.Value) {
+ if len(v) >= 65536 {
+ errorf("got %d parameters but PostgreSQL only supports 65535 parameters", len(v))
+ }
+ if len(v) != len(st.paramTyps) {
+ errorf("got %d parameters but the statement requires %d", len(v), len(st.paramTyps))
+ }
+
+ cn := st.cn
+ w := cn.writeBuf('B')
+ w.byte(0) // unnamed portal
+ w.string(st.name)
+
+ if cn.binaryParameters {
+ cn.sendBinaryParameters(w, v)
+ } else {
+ w.int16(0)
+ w.int16(len(v))
+ for i, x := range v {
+ if x == nil {
+ w.int32(-1)
+ } else {
+ b := encode(&cn.parameterStatus, x, st.paramTyps[i])
+ w.int32(len(b))
+ w.bytes(b)
+ }
+ }
+ }
+ w.bytes(st.colFmtData)
+
+ w.next('E')
+ w.byte(0)
+ w.int32(0)
+
+ w.next('S')
+ cn.send(w)
+
+ cn.readBindResponse()
+ cn.postExecuteWorkaround()
+
+}
+
+func (st *stmt) NumInput() int {
+ return len(st.paramTyps)
+}
+
+// parseComplete parses the "command tag" from a CommandComplete message, and
+// returns the number of rows affected (if applicable) and a string
+// identifying only the command that was executed, e.g. "ALTER TABLE". If the
+// command tag could not be parsed, parseComplete panics.
+func (cn *conn) parseComplete(commandTag string) (driver.Result, string) {
+ commandsWithAffectedRows := []string{
+ "SELECT ",
+ // INSERT is handled below
+ "UPDATE ",
+ "DELETE ",
+ "FETCH ",
+ "MOVE ",
+ "COPY ",
+ }
+
+ var affectedRows *string
+ for _, tag := range commandsWithAffectedRows {
+ if strings.HasPrefix(commandTag, tag) {
+ t := commandTag[len(tag):]
+ affectedRows = &t
+ commandTag = tag[:len(tag)-1]
+ break
+ }
+ }
+ // INSERT also includes the oid of the inserted row in its command tag.
+ // Oids in user tables are deprecated, and the oid is only returned when
+ // exactly one row is inserted, so it's unlikely to be of value to any
+ // real-world application and we can ignore it.
+ if affectedRows == nil && strings.HasPrefix(commandTag, "INSERT ") {
+ parts := strings.Split(commandTag, " ")
+ if len(parts) != 3 {
+ cn.bad = true
+ errorf("unexpected INSERT command tag %s", commandTag)
+ }
+ affectedRows = &parts[len(parts)-1]
+ commandTag = "INSERT"
+ }
+ // There should be no affected rows attached to the tag, just return it
+ if affectedRows == nil {
+ return driver.RowsAffected(0), commandTag
+ }
+ n, err := strconv.ParseInt(*affectedRows, 10, 64)
+ if err != nil {
+ cn.bad = true
+ errorf("could not parse commandTag: %s", err)
+ }
+ return driver.RowsAffected(n), commandTag
+}
+
+type rows struct {
+ cn *conn
+ colNames []string
+ colTyps []oid.Oid
+ colFmts []format
+ done bool
+ rb readBuf
+}
+
+func (rs *rows) Close() error {
+ // no need to look at cn.bad as Next() will
+ for {
+ err := rs.Next(nil)
+ switch err {
+ case nil:
+ case io.EOF:
+ return nil
+ default:
+ return err
+ }
+ }
+}
+
+func (rs *rows) Columns() []string {
+ return rs.colNames
+}
+
+func (rs *rows) Next(dest []driver.Value) (err error) {
+ if rs.done {
+ return io.EOF
+ }
+
+ conn := rs.cn
+ if conn.bad {
+ return driver.ErrBadConn
+ }
+ defer conn.errRecover(&err)
+
+ for {
+ t := conn.recv1Buf(&rs.rb)
+ switch t {
+ case 'E':
+ err = parseError(&rs.rb)
+ case 'C', 'I':
+ continue
+ case 'Z':
+ conn.processReadyForQuery(&rs.rb)
+ rs.done = true
+ if err != nil {
+ return err
+ }
+ return io.EOF
+ case 'D':
+ n := rs.rb.int16()
+ if err != nil {
+ conn.bad = true
+ errorf("unexpected DataRow after error %s", err)
+ }
+ if n < len(dest) {
+ dest = dest[:n]
+ }
+ for i := range dest {
+ l := rs.rb.int32()
+ if l == -1 {
+ dest[i] = nil
+ continue
+ }
+ dest[i] = decode(&conn.parameterStatus, rs.rb.next(l), rs.colTyps[i], rs.colFmts[i])
+ }
+ return
+ default:
+ errorf("unexpected message after execute: %q", t)
+ }
+ }
+}
+
+// QuoteIdentifier quotes an "identifier" (e.g. a table or a column name) to be
+// used as part of an SQL statement. For example:
+//
+// tblname := "my_table"
+// data := "my_data"
+// err = db.Exec(fmt.Sprintf("INSERT INTO %s VALUES ($1)", pq.QuoteIdentifier(tblname)), data)
+//
+// Any double quotes in name will be escaped. The quoted identifier will be
+// case sensitive when used in a query. If the input string contains a zero
+// byte, the result will be truncated immediately before it.
+func QuoteIdentifier(name string) string {
+ end := strings.IndexRune(name, 0)
+ if end > -1 {
+ name = name[:end]
+ }
+ return `"` + strings.Replace(name, `"`, `""`, -1) + `"`
+}
+
+func md5s(s string) string {
+ h := md5.New()
+ h.Write([]byte(s))
+ return fmt.Sprintf("%x", h.Sum(nil))
+}
+
+func (cn *conn) sendBinaryParameters(b *writeBuf, args []driver.Value) {
+ // Do one pass over the parameters to see if we're going to send any of
+ // them over in binary. If we are, create a paramFormats array at the
+ // same time.
+ var paramFormats []int
+ for i, x := range args {
+ _, ok := x.([]byte)
+ if ok {
+ if paramFormats == nil {
+ paramFormats = make([]int, len(args))
+ }
+ paramFormats[i] = 1
+ }
+ }
+ if paramFormats == nil {
+ b.int16(0)
+ } else {
+ b.int16(len(paramFormats))
+ for _, x := range paramFormats {
+ b.int16(x)
+ }
+ }
+
+ b.int16(len(args))
+ for _, x := range args {
+ if x == nil {
+ b.int32(-1)
+ } else {
+ datum := binaryEncode(&cn.parameterStatus, x)
+ b.int32(len(datum))
+ b.bytes(datum)
+ }
+ }
+}
+
+func (cn *conn) sendBinaryModeQuery(query string, args []driver.Value) {
+ if len(args) >= 65536 {
+ errorf("got %d parameters but PostgreSQL only supports 65535 parameters", len(args))
+ }
+
+ b := cn.writeBuf('P')
+ b.byte(0) // unnamed statement
+ b.string(query)
+ b.int16(0)
+
+ b.next('B')
+ b.int16(0) // unnamed portal and statement
+ cn.sendBinaryParameters(b, args)
+ b.bytes(colFmtDataAllText)
+
+ b.next('D')
+ b.byte('P')
+ b.byte(0) // unnamed portal
+
+ b.next('E')
+ b.byte(0)
+ b.int32(0)
+
+ b.next('S')
+ cn.send(b)
+}
+
+func (c *conn) processParameterStatus(r *readBuf) {
+ var err error
+
+ param := r.string()
+ switch param {
+ case "server_version":
+ var major1 int
+ var major2 int
+ var minor int
+ _, err = fmt.Sscanf(r.string(), "%d.%d.%d", &major1, &major2, &minor)
+ if err == nil {
+ c.parameterStatus.serverVersion = major1*10000 + major2*100 + minor
+ }
+
+ case "TimeZone":
+ c.parameterStatus.currentLocation, err = time.LoadLocation(r.string())
+ if err != nil {
+ c.parameterStatus.currentLocation = nil
+ }
+
+ default:
+ // ignore
+ }
+}
+
+func (c *conn) processReadyForQuery(r *readBuf) {
+ c.txnStatus = transactionStatus(r.byte())
+}
+
+func (cn *conn) readReadyForQuery() {
+ t, r := cn.recv1()
+ switch t {
+ case 'Z':
+ cn.processReadyForQuery(r)
+ return
+ default:
+ cn.bad = true
+ errorf("unexpected message %q; expected ReadyForQuery", t)
+ }
+}
+
+func (cn *conn) readParseResponse() {
+ t, r := cn.recv1()
+ switch t {
+ case '1':
+ return
+ case 'E':
+ err := parseError(r)
+ cn.readReadyForQuery()
+ panic(err)
+ default:
+ cn.bad = true
+ errorf("unexpected Parse response %q", t)
+ }
+}
+
+func (cn *conn) readStatementDescribeResponse() (paramTyps []oid.Oid, colNames []string, colTyps []oid.Oid) {
+ for {
+ t, r := cn.recv1()
+ switch t {
+ case 't':
+ nparams := r.int16()
+ paramTyps = make([]oid.Oid, nparams)
+ for i := range paramTyps {
+ paramTyps[i] = r.oid()
+ }
+ case 'n':
+ return paramTyps, nil, nil
+ case 'T':
+ colNames, colTyps = parseStatementRowDescribe(r)
+ return paramTyps, colNames, colTyps
+ case 'E':
+ err := parseError(r)
+ cn.readReadyForQuery()
+ panic(err)
+ default:
+ cn.bad = true
+ errorf("unexpected Describe statement response %q", t)
+ }
+ }
+}
+
+func (cn *conn) readPortalDescribeResponse() (colNames []string, colFmts []format, colTyps []oid.Oid) {
+ t, r := cn.recv1()
+ switch t {
+ case 'T':
+ return parsePortalRowDescribe(r)
+ case 'n':
+ return nil, nil, nil
+ case 'E':
+ err := parseError(r)
+ cn.readReadyForQuery()
+ panic(err)
+ default:
+ cn.bad = true
+ errorf("unexpected Describe response %q", t)
+ }
+ panic("not reached")
+}
+
+func (cn *conn) readBindResponse() {
+ t, r := cn.recv1()
+ switch t {
+ case '2':
+ return
+ case 'E':
+ err := parseError(r)
+ cn.readReadyForQuery()
+ panic(err)
+ default:
+ cn.bad = true
+ errorf("unexpected Bind response %q", t)
+ }
+}
+
+func (cn *conn) postExecuteWorkaround() {
+ // Work around a bug in sql.DB.QueryRow: in Go 1.2 and earlier it ignores
+ // any errors from rows.Next, which masks errors that happened during the
+ // execution of the query. To avoid the problem in common cases, we wait
+ // here for one more message from the database. If it's not an error the
+ // query will likely succeed (or perhaps has already, if it's a
+ // CommandComplete), so we push the message into the conn struct; recv1
+ // will return it as the next message for rows.Next or rows.Close.
+ // However, if it's an error, we wait until ReadyForQuery and then return
+ // the error to our caller.
+ for {
+ t, r := cn.recv1()
+ switch t {
+ case 'E':
+ err := parseError(r)
+ cn.readReadyForQuery()
+ panic(err)
+ case 'C', 'D', 'I':
+ // the query didn't fail, but we can't process this message
+ cn.saveMessage(t, r)
+ return
+ default:
+ cn.bad = true
+ errorf("unexpected message during extended query execution: %q", t)
+ }
+ }
+}
+
+// Only for Exec(), since we ignore the returned data
+func (cn *conn) readExecuteResponse(protocolState string) (res driver.Result, commandTag string, err error) {
+ for {
+ t, r := cn.recv1()
+ switch t {
+ case 'C':
+ if err != nil {
+ cn.bad = true
+ errorf("unexpected CommandComplete after error %s", err)
+ }
+ res, commandTag = cn.parseComplete(r.string())
+ case 'Z':
+ cn.processReadyForQuery(r)
+ return res, commandTag, err
+ case 'E':
+ err = parseError(r)
+ case 'T', 'D', 'I':
+ if err != nil {
+ cn.bad = true
+ errorf("unexpected %q after error %s", t, err)
+ }
+ // ignore any results
+ default:
+ cn.bad = true
+ errorf("unknown %s response: %q", protocolState, t)
+ }
+ }
+}
+
+func parseStatementRowDescribe(r *readBuf) (colNames []string, colTyps []oid.Oid) {
+ n := r.int16()
+ colNames = make([]string, n)
+ colTyps = make([]oid.Oid, n)
+ for i := range colNames {
+ colNames[i] = r.string()
+ r.next(6)
+ colTyps[i] = r.oid()
+ r.next(6)
+ // format code not known when describing a statement; always 0
+ r.next(2)
+ }
+ return
+}
+
+func parsePortalRowDescribe(r *readBuf) (colNames []string, colFmts []format, colTyps []oid.Oid) {
+ n := r.int16()
+ colNames = make([]string, n)
+ colFmts = make([]format, n)
+ colTyps = make([]oid.Oid, n)
+ for i := range colNames {
+ colNames[i] = r.string()
+ r.next(6)
+ colTyps[i] = r.oid()
+ r.next(6)
+ colFmts[i] = format(r.int16())
+ }
+ return
+}
+
+// parseEnviron tries to mimic some of libpq's environment handling
+//
+// To ease testing, it does not directly reference os.Environ, but is
+// designed to accept its output.
+//
+// Environment-set connection information is intended to have a higher
+// precedence than a library default but lower than any explicitly
+// passed information (such as in the URL or connection string).
+func parseEnviron(env []string) (out map[string]string) {
+ out = make(map[string]string)
+
+ for _, v := range env {
+ parts := strings.SplitN(v, "=", 2)
+
+ accrue := func(keyname string) {
+ out[keyname] = parts[1]
+ }
+ unsupported := func() {
+ panic(fmt.Sprintf("setting %v not supported", parts[0]))
+ }
+
+ // The order of these is the same as is seen in the
+ // PostgreSQL 9.1 manual. Unsupported but well-defined
+ // keys cause a panic; these should be unset prior to
+ // execution. Options which pq expects to be set to a
+ // certain value are allowed, but must be set to that
+ // value if present (they can, of course, be absent).
+ switch parts[0] {
+ case "PGHOST":
+ accrue("host")
+ case "PGHOSTADDR":
+ unsupported()
+ case "PGPORT":
+ accrue("port")
+ case "PGDATABASE":
+ accrue("dbname")
+ case "PGUSER":
+ accrue("user")
+ case "PGPASSWORD":
+ accrue("password")
+ case "PGSERVICE", "PGSERVICEFILE", "PGREALM":
+ unsupported()
+ case "PGOPTIONS":
+ accrue("options")
+ case "PGAPPNAME":
+ accrue("application_name")
+ case "PGSSLMODE":
+ accrue("sslmode")
+ case "PGSSLCERT":
+ accrue("sslcert")
+ case "PGSSLKEY":
+ accrue("sslkey")
+ case "PGSSLROOTCERT":
+ accrue("sslrootcert")
+ case "PGREQUIRESSL", "PGSSLCRL":
+ unsupported()
+ case "PGREQUIREPEER":
+ unsupported()
+ case "PGKRBSRVNAME", "PGGSSLIB":
+ unsupported()
+ case "PGCONNECT_TIMEOUT":
+ accrue("connect_timeout")
+ case "PGCLIENTENCODING":
+ accrue("client_encoding")
+ case "PGDATESTYLE":
+ accrue("datestyle")
+ case "PGTZ":
+ accrue("timezone")
+ case "PGGEQO":
+ accrue("geqo")
+ case "PGSYSCONFDIR", "PGLOCALEDIR":
+ unsupported()
+ }
+ }
+
+ return out
+}
+
+// isUTF8 returns whether name is a fuzzy variation of the string "UTF-8".
+func isUTF8(name string) bool {
+ // Recognize all sorts of silly things as "UTF-8", like Postgres does
+ s := strings.Map(alnumLowerASCII, name)
+ return s == "utf8" || s == "unicode"
+}
+
+func alnumLowerASCII(ch rune) rune {
+ if 'A' <= ch && ch <= 'Z' {
+ return ch + ('a' - 'A')
+ }
+ if 'a' <= ch && ch <= 'z' || '0' <= ch && ch <= '9' {
+ return ch
+ }
+ return -1 // discard
+}
diff --git a/vendor/github.com/lib/pq/copy.go b/vendor/github.com/lib/pq/copy.go
new file mode 100644
index 000000000..101f11133
--- /dev/null
+++ b/vendor/github.com/lib/pq/copy.go
@@ -0,0 +1,267 @@
+package pq
+
+import (
+ "database/sql/driver"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "sync"
+)
+
+var (
+ errCopyInClosed = errors.New("pq: copyin statement has already been closed")
+ errBinaryCopyNotSupported = errors.New("pq: only text format supported for COPY")
+ errCopyToNotSupported = errors.New("pq: COPY TO is not supported")
+ errCopyNotSupportedOutsideTxn = errors.New("pq: COPY is only allowed inside a transaction")
+)
+
+// CopyIn creates a COPY FROM statement which can be prepared with
+// Tx.Prepare(). The target table should be visible in search_path.
+func CopyIn(table string, columns ...string) string {
+ stmt := "COPY " + QuoteIdentifier(table) + " ("
+ for i, col := range columns {
+ if i != 0 {
+ stmt += ", "
+ }
+ stmt += QuoteIdentifier(col)
+ }
+ stmt += ") FROM STDIN"
+ return stmt
+}
+
+// CopyInSchema creates a COPY FROM statement which can be prepared with
+// Tx.Prepare().
+func CopyInSchema(schema, table string, columns ...string) string {
+ stmt := "COPY " + QuoteIdentifier(schema) + "." + QuoteIdentifier(table) + " ("
+ for i, col := range columns {
+ if i != 0 {
+ stmt += ", "
+ }
+ stmt += QuoteIdentifier(col)
+ }
+ stmt += ") FROM STDIN"
+ return stmt
+}
+
+type copyin struct {
+ cn *conn
+ buffer []byte
+ rowData chan []byte
+ done chan bool
+
+ closed bool
+
+ sync.Mutex // guards err
+ err error
+}
+
+const ciBufferSize = 64 * 1024
+
+// flush buffer before the buffer is filled up and needs reallocation
+const ciBufferFlushSize = 63 * 1024
+
+func (cn *conn) prepareCopyIn(q string) (_ driver.Stmt, err error) {
+ if !cn.isInTransaction() {
+ return nil, errCopyNotSupportedOutsideTxn
+ }
+
+ ci := &copyin{
+ cn: cn,
+ buffer: make([]byte, 0, ciBufferSize),
+ rowData: make(chan []byte),
+ done: make(chan bool, 1),
+ }
+ // add CopyData identifier + 4 bytes for message length
+ ci.buffer = append(ci.buffer, 'd', 0, 0, 0, 0)
+
+ b := cn.writeBuf('Q')
+ b.string(q)
+ cn.send(b)
+
+awaitCopyInResponse:
+ for {
+ t, r := cn.recv1()
+ switch t {
+ case 'G':
+ if r.byte() != 0 {
+ err = errBinaryCopyNotSupported
+ break awaitCopyInResponse
+ }
+ go ci.resploop()
+ return ci, nil
+ case 'H':
+ err = errCopyToNotSupported
+ break awaitCopyInResponse
+ case 'E':
+ err = parseError(r)
+ case 'Z':
+ if err == nil {
+ cn.bad = true
+ errorf("unexpected ReadyForQuery in response to COPY")
+ }
+ cn.processReadyForQuery(r)
+ return nil, err
+ default:
+ cn.bad = true
+ errorf("unknown response for copy query: %q", t)
+ }
+ }
+
+ // something went wrong, abort COPY before we return
+ b = cn.writeBuf('f')
+ b.string(err.Error())
+ cn.send(b)
+
+ for {
+ t, r := cn.recv1()
+ switch t {
+ case 'c', 'C', 'E':
+ case 'Z':
+ // correctly aborted, we're done
+ cn.processReadyForQuery(r)
+ return nil, err
+ default:
+ cn.bad = true
+ errorf("unknown response for CopyFail: %q", t)
+ }
+ }
+}
+
+func (ci *copyin) flush(buf []byte) {
+ // set message length (without message identifier)
+ binary.BigEndian.PutUint32(buf[1:], uint32(len(buf)-1))
+
+ _, err := ci.cn.c.Write(buf)
+ if err != nil {
+ panic(err)
+ }
+}
+
+func (ci *copyin) resploop() {
+ for {
+ var r readBuf
+ t, err := ci.cn.recvMessage(&r)
+ if err != nil {
+ ci.cn.bad = true
+ ci.setError(err)
+ ci.done <- true
+ return
+ }
+ switch t {
+ case 'C':
+ // complete
+ case 'N':
+ // NoticeResponse
+ case 'Z':
+ ci.cn.processReadyForQuery(&r)
+ ci.done <- true
+ return
+ case 'E':
+ err := parseError(&r)
+ ci.setError(err)
+ default:
+ ci.cn.bad = true
+ ci.setError(fmt.Errorf("unknown response during CopyIn: %q", t))
+ ci.done <- true
+ return
+ }
+ }
+}
+
+func (ci *copyin) isErrorSet() bool {
+ ci.Lock()
+ isSet := (ci.err != nil)
+ ci.Unlock()
+ return isSet
+}
+
+// setError() sets ci.err if one has not been set already. Caller must not be
+// holding ci.Mutex.
+func (ci *copyin) setError(err error) {
+ ci.Lock()
+ if ci.err == nil {
+ ci.err = err
+ }
+ ci.Unlock()
+}
+
+func (ci *copyin) NumInput() int {
+ return -1
+}
+
+func (ci *copyin) Query(v []driver.Value) (r driver.Rows, err error) {
+ return nil, ErrNotSupported
+}
+
+// Exec inserts values into the COPY stream. The insert is asynchronous
+// and Exec can return errors from previous Exec calls to the same
+// COPY stmt.
+//
+// You need to call Exec(nil) to sync the COPY stream and to get any
+// errors from pending data, since Stmt.Close() doesn't return errors
+// to the user.
+func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) {
+ if ci.closed {
+ return nil, errCopyInClosed
+ }
+
+ if ci.cn.bad {
+ return nil, driver.ErrBadConn
+ }
+ defer ci.cn.errRecover(&err)
+
+ if ci.isErrorSet() {
+ return nil, ci.err
+ }
+
+ if len(v) == 0 {
+ return nil, ci.Close()
+ }
+
+ numValues := len(v)
+ for i, value := range v {
+ ci.buffer = appendEncodedText(&ci.cn.parameterStatus, ci.buffer, value)
+ if i < numValues-1 {
+ ci.buffer = append(ci.buffer, '\t')
+ }
+ }
+
+ ci.buffer = append(ci.buffer, '\n')
+
+ if len(ci.buffer) > ciBufferFlushSize {
+ ci.flush(ci.buffer)
+ // reset buffer, keep bytes for message identifier and length
+ ci.buffer = ci.buffer[:5]
+ }
+
+ return driver.RowsAffected(0), nil
+}
+
+func (ci *copyin) Close() (err error) {
+ if ci.closed { // Don't do anything, we're already closed
+ return nil
+ }
+ ci.closed = true
+
+ if ci.cn.bad {
+ return driver.ErrBadConn
+ }
+ defer ci.cn.errRecover(&err)
+
+ if len(ci.buffer) > 0 {
+ ci.flush(ci.buffer)
+ }
+ // Avoid touching the scratch buffer as resploop could be using it.
+ err = ci.cn.sendSimpleMessage('c')
+ if err != nil {
+ return err
+ }
+
+ <-ci.done
+
+ if ci.isErrorSet() {
+ err = ci.err
+ return err
+ }
+ return nil
+}
diff --git a/vendor/github.com/lib/pq/doc.go b/vendor/github.com/lib/pq/doc.go
new file mode 100644
index 000000000..19798dfc9
--- /dev/null
+++ b/vendor/github.com/lib/pq/doc.go
@@ -0,0 +1,212 @@
+/*
+Package pq is a pure Go Postgres driver for the database/sql package.
+
+In most cases clients will use the database/sql package instead of
+using this package directly. For example:
+
+ import (
+ "database/sql"
+
+ _ "github.com/lib/pq"
+ )
+
+ func main() {
+ db, err := sql.Open("postgres", "user=pqgotest dbname=pqgotest sslmode=verify-full")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ age := 21
+ rows, err := db.Query("SELECT name FROM users WHERE age = $1", age)
+ …
+ }
+
+You can also connect to a database using a URL. For example:
+
+ db, err := sql.Open("postgres", "postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full")
+
+
+Connection String Parameters
+
+
+Similarly to libpq, when establishing a connection using pq you are expected to
+supply a connection string containing zero or more parameters.
+A subset of the connection parameters supported by libpq are also supported by pq.
+Additionally, pq also lets you specify run-time parameters (such as search_path or work_mem)
+directly in the connection string. This is different from libpq, which does not allow
+run-time parameters in the connection string, instead requiring you to supply
+them in the options parameter.
+
+For compatibility with libpq, the following special connection parameters are
+supported:
+
+ * dbname - The name of the database to connect to
+ * user - The user to sign in as
+ * password - The user's password
+ * host - The host to connect to. Values that start with / are for unix domain sockets. (default is localhost)
+ * port - The port to bind to. (default is 5432)
+ * sslmode - Whether or not to use SSL (default is require, this is not the default for libpq)
+ * fallback_application_name - An application_name to fall back to if one isn't provided.
+ * connect_timeout - Maximum wait for connection, in seconds. Zero or not specified means wait indefinitely.
+ * sslcert - Cert file location. The file must contain PEM encoded data.
+ * sslkey - Key file location. The file must contain PEM encoded data.
+ * sslrootcert - The location of the root certificate file. The file must contain PEM encoded data.
+
+Valid values for sslmode are:
+
+ * disable - No SSL
+ * require - Always SSL (skip verification)
+ * verify-ca - Always SSL (verify that the certificate presented by the server was signed by a trusted CA)
+ * verify-full - Always SSL (verify that the certification presented by the server was signed by a trusted CA and the server host name matches the one in the certificate)
+
+See http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
+for more information about connection string parameters.
+
+Use single quotes for values that contain whitespace:
+
+ "user=pqgotest password='with spaces'"
+
+A backslash will escape the next character in values:
+
+ "user=space\ man password='it\'s valid'
+
+Note that the connection parameter client_encoding (which sets the
+text encoding for the connection) may be set but must be "UTF8",
+matching with the same rules as Postgres. It is an error to provide
+any other value.
+
+In addition to the parameters listed above, any run-time parameter that can be
+set at backend start time can be set in the connection string. For more
+information, see
+http://www.postgresql.org/docs/current/static/runtime-config.html.
+
+Most environment variables as specified at http://www.postgresql.org/docs/current/static/libpq-envars.html
+supported by libpq are also supported by pq. If any of the environment
+variables not supported by pq are set, pq will panic during connection
+establishment. Environment variables have a lower precedence than explicitly
+provided connection parameters.
+
+The pgpass mechanism as described in http://www.postgresql.org/docs/current/static/libpq-pgpass.html
+is supported, but on Windows PGPASSFILE must be specified explicitly.
+
+Queries
+
+database/sql does not dictate any specific format for parameter
+markers in query strings, and pq uses the Postgres-native ordinal markers,
+as shown above. The same marker can be reused for the same parameter:
+
+ rows, err := db.Query(`SELECT name FROM users WHERE favorite_fruit = $1
+ OR age BETWEEN $2 AND $2 + 3`, "orange", 64)
+
+pq does not support the LastInsertId() method of the Result type in database/sql.
+To return the identifier of an INSERT (or UPDATE or DELETE), use the Postgres
+RETURNING clause with a standard Query or QueryRow call:
+
+ var userid int
+ err := db.QueryRow(`INSERT INTO users(name, favorite_fruit, age)
+ VALUES('beatrice', 'starfruit', 93) RETURNING id`).Scan(&userid)
+
+For more details on RETURNING, see the Postgres documentation:
+
+ http://www.postgresql.org/docs/current/static/sql-insert.html
+ http://www.postgresql.org/docs/current/static/sql-update.html
+ http://www.postgresql.org/docs/current/static/sql-delete.html
+
+For additional instructions on querying see the documentation for the database/sql package.
+
+Errors
+
+pq may return errors of type *pq.Error which can be interrogated for error details:
+
+ if err, ok := err.(*pq.Error); ok {
+ fmt.Println("pq error:", err.Code.Name())
+ }
+
+See the pq.Error type for details.
+
+
+Bulk imports
+
+You can perform bulk imports by preparing a statement returned by pq.CopyIn (or
+pq.CopyInSchema) in an explicit transaction (sql.Tx). The returned statement
+handle can then be repeatedly "executed" to copy data into the target table.
+After all data has been processed you should call Exec() once with no arguments
+to flush all buffered data. Any call to Exec() might return an error which
+should be handled appropriately, but because of the internal buffering an error
+returned by Exec() might not be related to the data passed in the call that
+failed.
+
+CopyIn uses COPY FROM internally. It is not possible to COPY outside of an
+explicit transaction in pq.
+
+Usage example:
+
+ txn, err := db.Begin()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ stmt, err := txn.Prepare(pq.CopyIn("users", "name", "age"))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for _, user := range users {
+ _, err = stmt.Exec(user.Name, int64(user.Age))
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+
+ _, err = stmt.Exec()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ err = stmt.Close()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ err = txn.Commit()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+
+Notifications
+
+
+PostgreSQL supports a simple publish/subscribe model over database
+connections. See http://www.postgresql.org/docs/current/static/sql-notify.html
+for more information about the general mechanism.
+
+To start listening for notifications, you first have to open a new connection
+to the database by calling NewListener. This connection can not be used for
+anything other than LISTEN / NOTIFY. Calling Listen will open a "notification
+channel"; once a notification channel is open, a notification generated on that
+channel will effect a send on the Listener.Notify channel. A notification
+channel will remain open until Unlisten is called, though connection loss might
+result in some notifications being lost. To solve this problem, Listener sends
+a nil pointer over the Notify channel any time the connection is re-established
+following a connection loss. The application can get information about the
+state of the underlying connection by setting an event callback in the call to
+NewListener.
+
+A single Listener can safely be used from concurrent goroutines, which means
+that there is often no need to create more than one Listener in your
+application. However, a Listener is always connected to a single database, so
+you will need to create a new Listener instance for every database you want to
+receive notifications in.
+
+The channel name in both Listen and Unlisten is case sensitive, and can contain
+any characters legal in an identifier (see
+http://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
+for more information). Note that the channel name will be truncated to 63
+bytes by the PostgreSQL server.
+
+You can find a complete, working example of Listener usage at
+http://godoc.org/github.com/lib/pq/listen_example.
+
+*/
+package pq
diff --git a/vendor/github.com/lib/pq/encode.go b/vendor/github.com/lib/pq/encode.go
new file mode 100644
index 000000000..e52d39b26
--- /dev/null
+++ b/vendor/github.com/lib/pq/encode.go
@@ -0,0 +1,571 @@
+package pq
+
+import (
+ "bytes"
+ "database/sql/driver"
+ "encoding/binary"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "math"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/lib/pq/oid"
+)
+
+func binaryEncode(parameterStatus *parameterStatus, x interface{}) []byte {
+ switch v := x.(type) {
+ case []byte:
+ return v
+ default:
+ return encode(parameterStatus, x, oid.T_unknown)
+ }
+ panic("not reached")
+}
+
+func encode(parameterStatus *parameterStatus, x interface{}, pgtypOid oid.Oid) []byte {
+ switch v := x.(type) {
+ case int64:
+ return strconv.AppendInt(nil, v, 10)
+ case float64:
+ return strconv.AppendFloat(nil, v, 'f', -1, 64)
+ case []byte:
+ if pgtypOid == oid.T_bytea {
+ return encodeBytea(parameterStatus.serverVersion, v)
+ }
+
+ return v
+ case string:
+ if pgtypOid == oid.T_bytea {
+ return encodeBytea(parameterStatus.serverVersion, []byte(v))
+ }
+
+ return []byte(v)
+ case bool:
+ return strconv.AppendBool(nil, v)
+ case time.Time:
+ return formatTs(v)
+
+ default:
+ errorf("encode: unknown type for %T", v)
+ }
+
+ panic("not reached")
+}
+
+func decode(parameterStatus *parameterStatus, s []byte, typ oid.Oid, f format) interface{} {
+ if f == formatBinary {
+ return binaryDecode(parameterStatus, s, typ)
+ } else {
+ return textDecode(parameterStatus, s, typ)
+ }
+}
+
+func binaryDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interface{} {
+ switch typ {
+ case oid.T_bytea:
+ return s
+ case oid.T_int8:
+ return int64(binary.BigEndian.Uint64(s))
+ case oid.T_int4:
+ return int64(int32(binary.BigEndian.Uint32(s)))
+ case oid.T_int2:
+ return int64(int16(binary.BigEndian.Uint16(s)))
+
+ default:
+ errorf("don't know how to decode binary parameter of type %u", uint32(typ))
+ }
+
+ panic("not reached")
+}
+
+func textDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interface{} {
+ switch typ {
+ case oid.T_bytea:
+ return parseBytea(s)
+ case oid.T_timestamptz:
+ return parseTs(parameterStatus.currentLocation, string(s))
+ case oid.T_timestamp, oid.T_date:
+ return parseTs(nil, string(s))
+ case oid.T_time:
+ return mustParse("15:04:05", typ, s)
+ case oid.T_timetz:
+ return mustParse("15:04:05-07", typ, s)
+ case oid.T_bool:
+ return s[0] == 't'
+ case oid.T_int8, oid.T_int4, oid.T_int2:
+ i, err := strconv.ParseInt(string(s), 10, 64)
+ if err != nil {
+ errorf("%s", err)
+ }
+ return i
+ case oid.T_float4, oid.T_float8:
+ bits := 64
+ if typ == oid.T_float4 {
+ bits = 32
+ }
+ f, err := strconv.ParseFloat(string(s), bits)
+ if err != nil {
+ errorf("%s", err)
+ }
+ return f
+ }
+
+ return s
+}
+
+// appendEncodedText encodes item in text format as required by COPY
+// and appends to buf
+func appendEncodedText(parameterStatus *parameterStatus, buf []byte, x interface{}) []byte {
+ switch v := x.(type) {
+ case int64:
+ return strconv.AppendInt(buf, v, 10)
+ case float64:
+ return strconv.AppendFloat(buf, v, 'f', -1, 64)
+ case []byte:
+ encodedBytea := encodeBytea(parameterStatus.serverVersion, v)
+ return appendEscapedText(buf, string(encodedBytea))
+ case string:
+ return appendEscapedText(buf, v)
+ case bool:
+ return strconv.AppendBool(buf, v)
+ case time.Time:
+ return append(buf, formatTs(v)...)
+ case nil:
+ return append(buf, "\\N"...)
+ default:
+ errorf("encode: unknown type for %T", v)
+ }
+
+ panic("not reached")
+}
+
+func appendEscapedText(buf []byte, text string) []byte {
+ escapeNeeded := false
+ startPos := 0
+ var c byte
+
+ // check if we need to escape
+ for i := 0; i < len(text); i++ {
+ c = text[i]
+ if c == '\\' || c == '\n' || c == '\r' || c == '\t' {
+ escapeNeeded = true
+ startPos = i
+ break
+ }
+ }
+ if !escapeNeeded {
+ return append(buf, text...)
+ }
+
+ // copy till first char to escape, iterate the rest
+ result := append(buf, text[:startPos]...)
+ for i := startPos; i < len(text); i++ {
+ c = text[i]
+ switch c {
+ case '\\':
+ result = append(result, '\\', '\\')
+ case '\n':
+ result = append(result, '\\', 'n')
+ case '\r':
+ result = append(result, '\\', 'r')
+ case '\t':
+ result = append(result, '\\', 't')
+ default:
+ result = append(result, c)
+ }
+ }
+ return result
+}
+
+func mustParse(f string, typ oid.Oid, s []byte) time.Time {
+ str := string(s)
+
+ // check for a 30-minute-offset timezone
+ if (typ == oid.T_timestamptz || typ == oid.T_timetz) &&
+ str[len(str)-3] == ':' {
+ f += ":00"
+ }
+ t, err := time.Parse(f, str)
+ if err != nil {
+ errorf("decode: %s", err)
+ }
+ return t
+}
+
+var invalidTimestampErr = errors.New("invalid timestamp")
+
+type timestampParser struct {
+ err error
+}
+
+func (p *timestampParser) expect(str, char string, pos int) {
+ if p.err != nil {
+ return
+ }
+ if pos+1 > len(str) {
+ p.err = invalidTimestampErr
+ return
+ }
+ if c := str[pos : pos+1]; c != char && p.err == nil {
+ p.err = fmt.Errorf("expected '%v' at position %v; got '%v'", char, pos, c)
+ }
+}
+
+func (p *timestampParser) mustAtoi(str string, begin int, end int) int {
+ if p.err != nil {
+ return 0
+ }
+ if begin < 0 || end < 0 || begin > end || end > len(str) {
+ p.err = invalidTimestampErr
+ return 0
+ }
+ result, err := strconv.Atoi(str[begin:end])
+ if err != nil {
+ if p.err == nil {
+ p.err = fmt.Errorf("expected number; got '%v'", str)
+ }
+ return 0
+ }
+ return result
+}
+
+// The location cache caches the time zones typically used by the client.
+type locationCache struct {
+ cache map[int]*time.Location
+ lock sync.Mutex
+}
+
+// All connections share the same list of timezones. Benchmarking shows that
+// about 5% speed could be gained by putting the cache in the connection and
+// losing the mutex, at the cost of a small amount of memory and a somewhat
+// significant increase in code complexity.
+var globalLocationCache *locationCache = newLocationCache()
+
+func newLocationCache() *locationCache {
+ return &locationCache{cache: make(map[int]*time.Location)}
+}
+
+// Returns the cached timezone for the specified offset, creating and caching
+// it if necessary.
+func (c *locationCache) getLocation(offset int) *time.Location {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ location, ok := c.cache[offset]
+ if !ok {
+ location = time.FixedZone("", offset)
+ c.cache[offset] = location
+ }
+
+ return location
+}
+
+var infinityTsEnabled = false
+var infinityTsNegative time.Time
+var infinityTsPositive time.Time
+
+const (
+ infinityTsEnabledAlready = "pq: infinity timestamp enabled already"
+ infinityTsNegativeMustBeSmaller = "pq: infinity timestamp: negative value must be smaller (before) than positive"
+)
+
+/*
+ * If EnableInfinityTs is not called, "-infinity" and "infinity" will return
+ * []byte("-infinity") and []byte("infinity") respectively, and potentially
+ * cause error "sql: Scan error on column index 0: unsupported driver -> Scan pair: []uint8 -> *time.Time",
+ * when scanning into a time.Time value.
+ *
+ * Once EnableInfinityTs has been called, all connections created using this
+ * driver will decode Postgres' "-infinity" and "infinity" for "timestamp",
+ * "timestamp with time zone" and "date" types to the predefined minimum and
+ * maximum times, respectively. When encoding time.Time values, any time which
+ * equals or precedes the predefined minimum time will be encoded to
+ * "-infinity". Any values at or past the maximum time will similarly be
+ * encoded to "infinity".
+ *
+ *
+ * If EnableInfinityTs is called with negative >= positive, it will panic.
+ * Calling EnableInfinityTs after a connection has been established results in
+ * undefined behavior. If EnableInfinityTs is called more than once, it will
+ * panic.
+ */
+func EnableInfinityTs(negative time.Time, positive time.Time) {
+ if infinityTsEnabled {
+ panic(infinityTsEnabledAlready)
+ }
+ if !negative.Before(positive) {
+ panic(infinityTsNegativeMustBeSmaller)
+ }
+ infinityTsEnabled = true
+ infinityTsNegative = negative
+ infinityTsPositive = positive
+}
+
+/*
+ * Testing might want to toggle infinityTsEnabled
+ */
+func disableInfinityTs() {
+ infinityTsEnabled = false
+}
+
+// This is a time function specific to the Postgres default DateStyle
+// setting ("ISO, MDY"), the only one we currently support. This
+// accounts for the discrepancies between the parsing available with
+// time.Parse and the Postgres date formatting quirks.
+func parseTs(currentLocation *time.Location, str string) interface{} {
+ switch str {
+ case "-infinity":
+ if infinityTsEnabled {
+ return infinityTsNegative
+ }
+ return []byte(str)
+ case "infinity":
+ if infinityTsEnabled {
+ return infinityTsPositive
+ }
+ return []byte(str)
+ }
+ t, err := ParseTimestamp(currentLocation, str)
+ if err != nil {
+ panic(err)
+ }
+ return t
+}
+
+func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, error) {
+ p := timestampParser{}
+
+ monSep := strings.IndexRune(str, '-')
+ // this is Gregorian year, not ISO Year
+ // In Gregorian system, the year 1 BC is followed by AD 1
+ year := p.mustAtoi(str, 0, monSep)
+ daySep := monSep + 3
+ month := p.mustAtoi(str, monSep+1, daySep)
+ p.expect(str, "-", daySep)
+ timeSep := daySep + 3
+ day := p.mustAtoi(str, daySep+1, timeSep)
+
+ var hour, minute, second int
+ if len(str) > monSep+len("01-01")+1 {
+ p.expect(str, " ", timeSep)
+ minSep := timeSep + 3
+ p.expect(str, ":", minSep)
+ hour = p.mustAtoi(str, timeSep+1, minSep)
+ secSep := minSep + 3
+ p.expect(str, ":", secSep)
+ minute = p.mustAtoi(str, minSep+1, secSep)
+ secEnd := secSep + 3
+ second = p.mustAtoi(str, secSep+1, secEnd)
+ }
+ remainderIdx := monSep + len("01-01 00:00:00") + 1
+ // Three optional (but ordered) sections follow: the
+ // fractional seconds, the time zone offset, and the BC
+ // designation. We set them up here and adjust the other
+ // offsets if the preceding sections exist.
+
+ nanoSec := 0
+ tzOff := 0
+
+ if remainderIdx+1 <= len(str) && str[remainderIdx:remainderIdx+1] == "." {
+ fracStart := remainderIdx + 1
+ fracOff := strings.IndexAny(str[fracStart:], "-+ ")
+ if fracOff < 0 {
+ fracOff = len(str) - fracStart
+ }
+ fracSec := p.mustAtoi(str, fracStart, fracStart+fracOff)
+ nanoSec = fracSec * (1000000000 / int(math.Pow(10, float64(fracOff))))
+
+ remainderIdx += fracOff + 1
+ }
+ if tzStart := remainderIdx; tzStart+1 <= len(str) && (str[tzStart:tzStart+1] == "-" || str[tzStart:tzStart+1] == "+") {
+ // time zone separator is always '-' or '+' (UTC is +00)
+ var tzSign int
+ if c := str[tzStart : tzStart+1]; c == "-" {
+ tzSign = -1
+ } else if c == "+" {
+ tzSign = +1
+ } else {
+ return time.Time{}, fmt.Errorf("expected '-' or '+' at position %v; got %v", tzStart, c)
+ }
+ tzHours := p.mustAtoi(str, tzStart+1, tzStart+3)
+ remainderIdx += 3
+ var tzMin, tzSec int
+ if tzStart+4 <= len(str) && str[tzStart+3:tzStart+4] == ":" {
+ tzMin = p.mustAtoi(str, tzStart+4, tzStart+6)
+ remainderIdx += 3
+ }
+ if tzStart+7 <= len(str) && str[tzStart+6:tzStart+7] == ":" {
+ tzSec = p.mustAtoi(str, tzStart+7, tzStart+9)
+ remainderIdx += 3
+ }
+ tzOff = tzSign * ((tzHours * 60 * 60) + (tzMin * 60) + tzSec)
+ }
+ var isoYear int
+ if remainderIdx+3 <= len(str) && str[remainderIdx:remainderIdx+3] == " BC" {
+ isoYear = 1 - year
+ remainderIdx += 3
+ } else {
+ isoYear = year
+ }
+ if remainderIdx < len(str) {
+ return time.Time{}, fmt.Errorf("expected end of input, got %v", str[remainderIdx:])
+ }
+ t := time.Date(isoYear, time.Month(month), day,
+ hour, minute, second, nanoSec,
+ globalLocationCache.getLocation(tzOff))
+
+ if currentLocation != nil {
+ // Set the location of the returned Time based on the session's
+ // TimeZone value, but only if the local time zone database agrees with
+ // the remote database on the offset.
+ lt := t.In(currentLocation)
+ _, newOff := lt.Zone()
+ if newOff == tzOff {
+ t = lt
+ }
+ }
+
+ return t, p.err
+}
+
+// formatTs formats t into a format postgres understands.
+func formatTs(t time.Time) (b []byte) {
+ if infinityTsEnabled {
+ // t <= -infinity : ! (t > -infinity)
+ if !t.After(infinityTsNegative) {
+ return []byte("-infinity")
+ }
+ // t >= infinity : ! (!t < infinity)
+ if !t.Before(infinityTsPositive) {
+ return []byte("infinity")
+ }
+ }
+ // Need to send dates before 0001 A.D. with " BC" suffix, instead of the
+ // minus sign preferred by Go.
+ // Beware, "0000" in ISO is "1 BC", "-0001" is "2 BC" and so on
+ bc := false
+ if t.Year() <= 0 {
+ // flip year sign, and add 1, e.g: "0" will be "1", and "-10" will be "11"
+ t = t.AddDate((-t.Year())*2+1, 0, 0)
+ bc = true
+ }
+ b = []byte(t.Format(time.RFC3339Nano))
+
+ _, offset := t.Zone()
+ offset = offset % 60
+ if offset != 0 {
+ // RFC3339Nano already printed the minus sign
+ if offset < 0 {
+ offset = -offset
+ }
+
+ b = append(b, ':')
+ if offset < 10 {
+ b = append(b, '0')
+ }
+ b = strconv.AppendInt(b, int64(offset), 10)
+ }
+
+ if bc {
+ b = append(b, " BC"...)
+ }
+ return b
+}
+
+// Parse a bytea value received from the server. Both "hex" and the legacy
+// "escape" format are supported.
+func parseBytea(s []byte) (result []byte) {
+ if len(s) >= 2 && bytes.Equal(s[:2], []byte("\\x")) {
+ // bytea_output = hex
+ s = s[2:] // trim off leading "\\x"
+ result = make([]byte, hex.DecodedLen(len(s)))
+ _, err := hex.Decode(result, s)
+ if err != nil {
+ errorf("%s", err)
+ }
+ } else {
+ // bytea_output = escape
+ for len(s) > 0 {
+ if s[0] == '\\' {
+ // escaped '\\'
+ if len(s) >= 2 && s[1] == '\\' {
+ result = append(result, '\\')
+ s = s[2:]
+ continue
+ }
+
+ // '\\' followed by an octal number
+ if len(s) < 4 {
+ errorf("invalid bytea sequence %v", s)
+ }
+ r, err := strconv.ParseInt(string(s[1:4]), 8, 9)
+ if err != nil {
+ errorf("could not parse bytea value: %s", err.Error())
+ }
+ result = append(result, byte(r))
+ s = s[4:]
+ } else {
+ // We hit an unescaped, raw byte. Try to read in as many as
+ // possible in one go.
+ i := bytes.IndexByte(s, '\\')
+ if i == -1 {
+ result = append(result, s...)
+ break
+ }
+ result = append(result, s[:i]...)
+ s = s[i:]
+ }
+ }
+ }
+
+ return result
+}
+
+func encodeBytea(serverVersion int, v []byte) (result []byte) {
+ if serverVersion >= 90000 {
+ // Use the hex format if we know that the server supports it
+ result = make([]byte, 2+hex.EncodedLen(len(v)))
+ result[0] = '\\'
+ result[1] = 'x'
+ hex.Encode(result[2:], v)
+ } else {
+ // .. or resort to "escape"
+ for _, b := range v {
+ if b == '\\' {
+ result = append(result, '\\', '\\')
+ } else if b < 0x20 || b > 0x7e {
+ result = append(result, []byte(fmt.Sprintf("\\%03o", b))...)
+ } else {
+ result = append(result, b)
+ }
+ }
+ }
+
+ return result
+}
+
+// NullTime represents a time.Time that may be null. NullTime implements the
+// sql.Scanner interface so it can be used as a scan destination, similar to
+// sql.NullString.
+type NullTime struct {
+ Time time.Time
+ Valid bool // Valid is true if Time is not NULL
+}
+
+// Scan implements the Scanner interface.
+func (nt *NullTime) Scan(value interface{}) error {
+ nt.Time, nt.Valid = value.(time.Time)
+ return nil
+}
+
+// Value implements the driver Valuer interface.
+func (nt NullTime) Value() (driver.Value, error) {
+ if !nt.Valid {
+ return nil, nil
+ }
+ return nt.Time, nil
+}
diff --git a/vendor/github.com/lib/pq/error.go b/vendor/github.com/lib/pq/error.go
new file mode 100644
index 000000000..b4bb44cee
--- /dev/null
+++ b/vendor/github.com/lib/pq/error.go
@@ -0,0 +1,508 @@
+package pq
+
+import (
+ "database/sql/driver"
+ "fmt"
+ "io"
+ "net"
+ "runtime"
+)
+
+// Error severities
+const (
+ Efatal = "FATAL"
+ Epanic = "PANIC"
+ Ewarning = "WARNING"
+ Enotice = "NOTICE"
+ Edebug = "DEBUG"
+ Einfo = "INFO"
+ Elog = "LOG"
+)
+
+// Error represents an error communicating with the server.
+//
+// See http://www.postgresql.org/docs/current/static/protocol-error-fields.html for details of the fields
+type Error struct {
+ Severity string
+ Code ErrorCode
+ Message string
+ Detail string
+ Hint string
+ Position string
+ InternalPosition string
+ InternalQuery string
+ Where string
+ Schema string
+ Table string
+ Column string
+ DataTypeName string
+ Constraint string
+ File string
+ Line string
+ Routine string
+}
+
+// ErrorCode is a five-character error code.
+type ErrorCode string
+
+// Name returns a more human friendly rendering of the error code, namely the
+// "condition name".
+//
+// See http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html for
+// details.
+func (ec ErrorCode) Name() string {
+ return errorCodeNames[ec]
+}
+
+// ErrorClass is only the class part of an error code.
+type ErrorClass string
+
+// Name returns the condition name of an error class. It is equivalent to the
+// condition name of the "standard" error code (i.e. the one having the last
+// three characters "000").
+func (ec ErrorClass) Name() string {
+ return errorCodeNames[ErrorCode(ec+"000")]
+}
+
+// Class returns the error class, e.g. "28".
+//
+// See http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html for
+// details.
+func (ec ErrorCode) Class() ErrorClass {
+ return ErrorClass(ec[0:2])
+}
+
+// errorCodeNames is a mapping between the five-character error codes and the
+// human readable "condition names". It is derived from the list at
+// http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html
+var errorCodeNames = map[ErrorCode]string{
+ // Class 00 - Successful Completion
+ "00000": "successful_completion",
+ // Class 01 - Warning
+ "01000": "warning",
+ "0100C": "dynamic_result_sets_returned",
+ "01008": "implicit_zero_bit_padding",
+ "01003": "null_value_eliminated_in_set_function",
+ "01007": "privilege_not_granted",
+ "01006": "privilege_not_revoked",
+ "01004": "string_data_right_truncation",
+ "01P01": "deprecated_feature",
+ // Class 02 - No Data (this is also a warning class per the SQL standard)
+ "02000": "no_data",
+ "02001": "no_additional_dynamic_result_sets_returned",
+ // Class 03 - SQL Statement Not Yet Complete
+ "03000": "sql_statement_not_yet_complete",
+ // Class 08 - Connection Exception
+ "08000": "connection_exception",
+ "08003": "connection_does_not_exist",
+ "08006": "connection_failure",
+ "08001": "sqlclient_unable_to_establish_sqlconnection",
+ "08004": "sqlserver_rejected_establishment_of_sqlconnection",
+ "08007": "transaction_resolution_unknown",
+ "08P01": "protocol_violation",
+ // Class 09 - Triggered Action Exception
+ "09000": "triggered_action_exception",
+ // Class 0A - Feature Not Supported
+ "0A000": "feature_not_supported",
+ // Class 0B - Invalid Transaction Initiation
+ "0B000": "invalid_transaction_initiation",
+ // Class 0F - Locator Exception
+ "0F000": "locator_exception",
+ "0F001": "invalid_locator_specification",
+ // Class 0L - Invalid Grantor
+ "0L000": "invalid_grantor",
+ "0LP01": "invalid_grant_operation",
+ // Class 0P - Invalid Role Specification
+ "0P000": "invalid_role_specification",
+ // Class 0Z - Diagnostics Exception
+ "0Z000": "diagnostics_exception",
+ "0Z002": "stacked_diagnostics_accessed_without_active_handler",
+ // Class 20 - Case Not Found
+ "20000": "case_not_found",
+ // Class 21 - Cardinality Violation
+ "21000": "cardinality_violation",
+ // Class 22 - Data Exception
+ "22000": "data_exception",
+ "2202E": "array_subscript_error",
+ "22021": "character_not_in_repertoire",
+ "22008": "datetime_field_overflow",
+ "22012": "division_by_zero",
+ "22005": "error_in_assignment",
+ "2200B": "escape_character_conflict",
+ "22022": "indicator_overflow",
+ "22015": "interval_field_overflow",
+ "2201E": "invalid_argument_for_logarithm",
+ "22014": "invalid_argument_for_ntile_function",
+ "22016": "invalid_argument_for_nth_value_function",
+ "2201F": "invalid_argument_for_power_function",
+ "2201G": "invalid_argument_for_width_bucket_function",
+ "22018": "invalid_character_value_for_cast",
+ "22007": "invalid_datetime_format",
+ "22019": "invalid_escape_character",
+ "2200D": "invalid_escape_octet",
+ "22025": "invalid_escape_sequence",
+ "22P06": "nonstandard_use_of_escape_character",
+ "22010": "invalid_indicator_parameter_value",
+ "22023": "invalid_parameter_value",
+ "2201B": "invalid_regular_expression",
+ "2201W": "invalid_row_count_in_limit_clause",
+ "2201X": "invalid_row_count_in_result_offset_clause",
+ "22009": "invalid_time_zone_displacement_value",
+ "2200C": "invalid_use_of_escape_character",
+ "2200G": "most_specific_type_mismatch",
+ "22004": "null_value_not_allowed",
+ "22002": "null_value_no_indicator_parameter",
+ "22003": "numeric_value_out_of_range",
+ "22026": "string_data_length_mismatch",
+ "22001": "string_data_right_truncation",
+ "22011": "substring_error",
+ "22027": "trim_error",
+ "22024": "unterminated_c_string",
+ "2200F": "zero_length_character_string",
+ "22P01": "floating_point_exception",
+ "22P02": "invalid_text_representation",
+ "22P03": "invalid_binary_representation",
+ "22P04": "bad_copy_file_format",
+ "22P05": "untranslatable_character",
+ "2200L": "not_an_xml_document",
+ "2200M": "invalid_xml_document",
+ "2200N": "invalid_xml_content",
+ "2200S": "invalid_xml_comment",
+ "2200T": "invalid_xml_processing_instruction",
+ // Class 23 - Integrity Constraint Violation
+ "23000": "integrity_constraint_violation",
+ "23001": "restrict_violation",
+ "23502": "not_null_violation",
+ "23503": "foreign_key_violation",
+ "23505": "unique_violation",
+ "23514": "check_violation",
+ "23P01": "exclusion_violation",
+ // Class 24 - Invalid Cursor State
+ "24000": "invalid_cursor_state",
+ // Class 25 - Invalid Transaction State
+ "25000": "invalid_transaction_state",
+ "25001": "active_sql_transaction",
+ "25002": "branch_transaction_already_active",
+ "25008": "held_cursor_requires_same_isolation_level",
+ "25003": "inappropriate_access_mode_for_branch_transaction",
+ "25004": "inappropriate_isolation_level_for_branch_transaction",
+ "25005": "no_active_sql_transaction_for_branch_transaction",
+ "25006": "read_only_sql_transaction",
+ "25007": "schema_and_data_statement_mixing_not_supported",
+ "25P01": "no_active_sql_transaction",
+ "25P02": "in_failed_sql_transaction",
+ // Class 26 - Invalid SQL Statement Name
+ "26000": "invalid_sql_statement_name",
+ // Class 27 - Triggered Data Change Violation
+ "27000": "triggered_data_change_violation",
+ // Class 28 - Invalid Authorization Specification
+ "28000": "invalid_authorization_specification",
+ "28P01": "invalid_password",
+ // Class 2B - Dependent Privilege Descriptors Still Exist
+ "2B000": "dependent_privilege_descriptors_still_exist",
+ "2BP01": "dependent_objects_still_exist",
+ // Class 2D - Invalid Transaction Termination
+ "2D000": "invalid_transaction_termination",
+ // Class 2F - SQL Routine Exception
+ "2F000": "sql_routine_exception",
+ "2F005": "function_executed_no_return_statement",
+ "2F002": "modifying_sql_data_not_permitted",
+ "2F003": "prohibited_sql_statement_attempted",
+ "2F004": "reading_sql_data_not_permitted",
+ // Class 34 - Invalid Cursor Name
+ "34000": "invalid_cursor_name",
+ // Class 38 - External Routine Exception
+ "38000": "external_routine_exception",
+ "38001": "containing_sql_not_permitted",
+ "38002": "modifying_sql_data_not_permitted",
+ "38003": "prohibited_sql_statement_attempted",
+ "38004": "reading_sql_data_not_permitted",
+ // Class 39 - External Routine Invocation Exception
+ "39000": "external_routine_invocation_exception",
+ "39001": "invalid_sqlstate_returned",
+ "39004": "null_value_not_allowed",
+ "39P01": "trigger_protocol_violated",
+ "39P02": "srf_protocol_violated",
+ // Class 3B - Savepoint Exception
+ "3B000": "savepoint_exception",
+ "3B001": "invalid_savepoint_specification",
+ // Class 3D - Invalid Catalog Name
+ "3D000": "invalid_catalog_name",
+ // Class 3F - Invalid Schema Name
+ "3F000": "invalid_schema_name",
+ // Class 40 - Transaction Rollback
+ "40000": "transaction_rollback",
+ "40002": "transaction_integrity_constraint_violation",
+ "40001": "serialization_failure",
+ "40003": "statement_completion_unknown",
+ "40P01": "deadlock_detected",
+ // Class 42 - Syntax Error or Access Rule Violation
+ "42000": "syntax_error_or_access_rule_violation",
+ "42601": "syntax_error",
+ "42501": "insufficient_privilege",
+ "42846": "cannot_coerce",
+ "42803": "grouping_error",
+ "42P20": "windowing_error",
+ "42P19": "invalid_recursion",
+ "42830": "invalid_foreign_key",
+ "42602": "invalid_name",
+ "42622": "name_too_long",
+ "42939": "reserved_name",
+ "42804": "datatype_mismatch",
+ "42P18": "indeterminate_datatype",
+ "42P21": "collation_mismatch",
+ "42P22": "indeterminate_collation",
+ "42809": "wrong_object_type",
+ "42703": "undefined_column",
+ "42883": "undefined_function",
+ "42P01": "undefined_table",
+ "42P02": "undefined_parameter",
+ "42704": "undefined_object",
+ "42701": "duplicate_column",
+ "42P03": "duplicate_cursor",
+ "42P04": "duplicate_database",
+ "42723": "duplicate_function",
+ "42P05": "duplicate_prepared_statement",
+ "42P06": "duplicate_schema",
+ "42P07": "duplicate_table",
+ "42712": "duplicate_alias",
+ "42710": "duplicate_object",
+ "42702": "ambiguous_column",
+ "42725": "ambiguous_function",
+ "42P08": "ambiguous_parameter",
+ "42P09": "ambiguous_alias",
+ "42P10": "invalid_column_reference",
+ "42611": "invalid_column_definition",
+ "42P11": "invalid_cursor_definition",
+ "42P12": "invalid_database_definition",
+ "42P13": "invalid_function_definition",
+ "42P14": "invalid_prepared_statement_definition",
+ "42P15": "invalid_schema_definition",
+ "42P16": "invalid_table_definition",
+ "42P17": "invalid_object_definition",
+ // Class 44 - WITH CHECK OPTION Violation
+ "44000": "with_check_option_violation",
+ // Class 53 - Insufficient Resources
+ "53000": "insufficient_resources",
+ "53100": "disk_full",
+ "53200": "out_of_memory",
+ "53300": "too_many_connections",
+ "53400": "configuration_limit_exceeded",
+ // Class 54 - Program Limit Exceeded
+ "54000": "program_limit_exceeded",
+ "54001": "statement_too_complex",
+ "54011": "too_many_columns",
+ "54023": "too_many_arguments",
+ // Class 55 - Object Not In Prerequisite State
+ "55000": "object_not_in_prerequisite_state",
+ "55006": "object_in_use",
+ "55P02": "cant_change_runtime_param",
+ "55P03": "lock_not_available",
+ // Class 57 - Operator Intervention
+ "57000": "operator_intervention",
+ "57014": "query_canceled",
+ "57P01": "admin_shutdown",
+ "57P02": "crash_shutdown",
+ "57P03": "cannot_connect_now",
+ "57P04": "database_dropped",
+ // Class 58 - System Error (errors external to PostgreSQL itself)
+ "58000": "system_error",
+ "58030": "io_error",
+ "58P01": "undefined_file",
+ "58P02": "duplicate_file",
+ // Class F0 - Configuration File Error
+ "F0000": "config_file_error",
+ "F0001": "lock_file_exists",
+ // Class HV - Foreign Data Wrapper Error (SQL/MED)
+ "HV000": "fdw_error",
+ "HV005": "fdw_column_name_not_found",
+ "HV002": "fdw_dynamic_parameter_value_needed",
+ "HV010": "fdw_function_sequence_error",
+ "HV021": "fdw_inconsistent_descriptor_information",
+ "HV024": "fdw_invalid_attribute_value",
+ "HV007": "fdw_invalid_column_name",
+ "HV008": "fdw_invalid_column_number",
+ "HV004": "fdw_invalid_data_type",
+ "HV006": "fdw_invalid_data_type_descriptors",
+ "HV091": "fdw_invalid_descriptor_field_identifier",
+ "HV00B": "fdw_invalid_handle",
+ "HV00C": "fdw_invalid_option_index",
+ "HV00D": "fdw_invalid_option_name",
+ "HV090": "fdw_invalid_string_length_or_buffer_length",
+ "HV00A": "fdw_invalid_string_format",
+ "HV009": "fdw_invalid_use_of_null_pointer",
+ "HV014": "fdw_too_many_handles",
+ "HV001": "fdw_out_of_memory",
+ "HV00P": "fdw_no_schemas",
+ "HV00J": "fdw_option_name_not_found",
+ "HV00K": "fdw_reply_handle",
+ "HV00Q": "fdw_schema_not_found",
+ "HV00R": "fdw_table_not_found",
+ "HV00L": "fdw_unable_to_create_execution",
+ "HV00M": "fdw_unable_to_create_reply",
+ "HV00N": "fdw_unable_to_establish_connection",
+ // Class P0 - PL/pgSQL Error
+ "P0000": "plpgsql_error",
+ "P0001": "raise_exception",
+ "P0002": "no_data_found",
+ "P0003": "too_many_rows",
+ // Class XX - Internal Error
+ "XX000": "internal_error",
+ "XX001": "data_corrupted",
+ "XX002": "index_corrupted",
+}
+
+func parseError(r *readBuf) *Error {
+ err := new(Error)
+ for t := r.byte(); t != 0; t = r.byte() {
+ msg := r.string()
+ switch t {
+ case 'S':
+ err.Severity = msg
+ case 'C':
+ err.Code = ErrorCode(msg)
+ case 'M':
+ err.Message = msg
+ case 'D':
+ err.Detail = msg
+ case 'H':
+ err.Hint = msg
+ case 'P':
+ err.Position = msg
+ case 'p':
+ err.InternalPosition = msg
+ case 'q':
+ err.InternalQuery = msg
+ case 'W':
+ err.Where = msg
+ case 's':
+ err.Schema = msg
+ case 't':
+ err.Table = msg
+ case 'c':
+ err.Column = msg
+ case 'd':
+ err.DataTypeName = msg
+ case 'n':
+ err.Constraint = msg
+ case 'F':
+ err.File = msg
+ case 'L':
+ err.Line = msg
+ case 'R':
+ err.Routine = msg
+ }
+ }
+ return err
+}
+
+// Fatal returns true if the Error Severity is fatal.
+func (err *Error) Fatal() bool {
+ return err.Severity == Efatal
+}
+
+// Get implements the legacy PGError interface. New code should use the fields
+// of the Error struct directly.
+func (err *Error) Get(k byte) (v string) {
+ switch k {
+ case 'S':
+ return err.Severity
+ case 'C':
+ return string(err.Code)
+ case 'M':
+ return err.Message
+ case 'D':
+ return err.Detail
+ case 'H':
+ return err.Hint
+ case 'P':
+ return err.Position
+ case 'p':
+ return err.InternalPosition
+ case 'q':
+ return err.InternalQuery
+ case 'W':
+ return err.Where
+ case 's':
+ return err.Schema
+ case 't':
+ return err.Table
+ case 'c':
+ return err.Column
+ case 'd':
+ return err.DataTypeName
+ case 'n':
+ return err.Constraint
+ case 'F':
+ return err.File
+ case 'L':
+ return err.Line
+ case 'R':
+ return err.Routine
+ }
+ return ""
+}
+
+func (err Error) Error() string {
+ return "pq: " + err.Message
+}
+
+// PGError is an interface used by previous versions of pq. It is provided
+// only to support legacy code. New code should use the Error type.
+type PGError interface {
+ Error() string
+ Fatal() bool
+ Get(k byte) (v string)
+}
+
+func errorf(s string, args ...interface{}) {
+ panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...)))
+}
+
+func errRecoverNoErrBadConn(err *error) {
+ e := recover()
+ if e == nil {
+ // Do nothing
+ return
+ }
+ var ok bool
+ *err, ok = e.(error)
+ if !ok {
+ *err = fmt.Errorf("pq: unexpected error: %#v", e)
+ }
+}
+
+func (c *conn) errRecover(err *error) {
+ e := recover()
+ switch v := e.(type) {
+ case nil:
+ // Do nothing
+ case runtime.Error:
+ c.bad = true
+ panic(v)
+ case *Error:
+ if v.Fatal() {
+ *err = driver.ErrBadConn
+ } else {
+ *err = v
+ }
+ case *net.OpError:
+ *err = driver.ErrBadConn
+ case error:
+ if v == io.EOF || v.(error).Error() == "remote error: handshake failure" {
+ *err = driver.ErrBadConn
+ } else {
+ *err = v
+ }
+
+ default:
+ c.bad = true
+ panic(fmt.Sprintf("unknown error: %#v", e))
+ }
+
+ // Any time we return ErrBadConn, we need to remember it since *Tx doesn't
+ // mark the connection bad in database/sql.
+ if *err == driver.ErrBadConn {
+ c.bad = true
+ }
+}
diff --git a/vendor/github.com/lib/pq/notify.go b/vendor/github.com/lib/pq/notify.go
new file mode 100644
index 000000000..8cad57815
--- /dev/null
+++ b/vendor/github.com/lib/pq/notify.go
@@ -0,0 +1,766 @@
+package pq
+
+// Package pq is a pure Go Postgres driver for the database/sql package.
+// This module contains support for Postgres LISTEN/NOTIFY.
+
+import (
+ "errors"
+ "fmt"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+// Notification represents a single notification from the database.
+type Notification struct {
+ // Process ID (PID) of the notifying postgres backend.
+ BePid int
+ // Name of the channel the notification was sent on.
+ Channel string
+ // Payload, or the empty string if unspecified.
+ Extra string
+}
+
+func recvNotification(r *readBuf) *Notification {
+ bePid := r.int32()
+ channel := r.string()
+ extra := r.string()
+
+ return &Notification{bePid, channel, extra}
+}
+
+const (
+ connStateIdle int32 = iota
+ connStateExpectResponse
+ connStateExpectReadyForQuery
+)
+
+type message struct {
+ typ byte
+ err error
+}
+
+var errListenerConnClosed = errors.New("pq: ListenerConn has been closed")
+
+// ListenerConn is a low-level interface for waiting for notifications. You
+// should use Listener instead.
+type ListenerConn struct {
+ // guards cn and err
+ connectionLock sync.Mutex
+ cn *conn
+ err error
+
+ connState int32
+
+ // the sending goroutine will be holding this lock
+ senderLock sync.Mutex
+
+ notificationChan chan<- *Notification
+
+ replyChan chan message
+}
+
+// Creates a new ListenerConn. Use NewListener instead.
+func NewListenerConn(name string, notificationChan chan<- *Notification) (*ListenerConn, error) {
+ cn, err := Open(name)
+ if err != nil {
+ return nil, err
+ }
+
+ l := &ListenerConn{
+ cn: cn.(*conn),
+ notificationChan: notificationChan,
+ connState: connStateIdle,
+ replyChan: make(chan message, 2),
+ }
+
+ go l.listenerConnMain()
+
+ return l, nil
+}
+
+// We can only allow one goroutine at a time to be running a query on the
+// connection for various reasons, so the goroutine sending on the connection
+// must be holding senderLock.
+//
+// Returns an error if an unrecoverable error has occurred and the ListenerConn
+// should be abandoned.
+func (l *ListenerConn) acquireSenderLock() error {
+ // we must acquire senderLock first to avoid deadlocks; see ExecSimpleQuery
+ l.senderLock.Lock()
+
+ l.connectionLock.Lock()
+ err := l.err
+ l.connectionLock.Unlock()
+ if err != nil {
+ l.senderLock.Unlock()
+ return err
+ }
+ return nil
+}
+
+func (l *ListenerConn) releaseSenderLock() {
+ l.senderLock.Unlock()
+}
+
+// setState advances the protocol state to newState. Returns false if moving
+// to that state from the current state is not allowed.
+func (l *ListenerConn) setState(newState int32) bool {
+ var expectedState int32
+
+ switch newState {
+ case connStateIdle:
+ expectedState = connStateExpectReadyForQuery
+ case connStateExpectResponse:
+ expectedState = connStateIdle
+ case connStateExpectReadyForQuery:
+ expectedState = connStateExpectResponse
+ default:
+ panic(fmt.Sprintf("unexpected listenerConnState %d", newState))
+ }
+
+ return atomic.CompareAndSwapInt32(&l.connState, expectedState, newState)
+}
+
+// Main logic is here: receive messages from the postgres backend, forward
+// notifications and query replies and keep the internal state in sync with the
+// protocol state. Returns when the connection has been lost, is about to go
+// away or should be discarded because we couldn't agree on the state with the
+// server backend.
+func (l *ListenerConn) listenerConnLoop() (err error) {
+ defer errRecoverNoErrBadConn(&err)
+
+ r := &readBuf{}
+ for {
+ t, err := l.cn.recvMessage(r)
+ if err != nil {
+ return err
+ }
+
+ switch t {
+ case 'A':
+ // recvNotification copies all the data so we don't need to worry
+ // about the scratch buffer being overwritten.
+ l.notificationChan <- recvNotification(r)
+
+ case 'T', 'D':
+ // only used by tests; ignore
+
+ case 'E':
+ // We might receive an ErrorResponse even when not in a query; it
+ // is expected that the server will close the connection after
+ // that, but we should make sure that the error we display is the
+ // one from the stray ErrorResponse, not io.ErrUnexpectedEOF.
+ if !l.setState(connStateExpectReadyForQuery) {
+ return parseError(r)
+ }
+ l.replyChan <- message{t, parseError(r)}
+
+ case 'C', 'I':
+ if !l.setState(connStateExpectReadyForQuery) {
+ // protocol out of sync
+ return fmt.Errorf("unexpected CommandComplete")
+ }
+ // ExecSimpleQuery doesn't need to know about this message
+
+ case 'Z':
+ if !l.setState(connStateIdle) {
+ // protocol out of sync
+ return fmt.Errorf("unexpected ReadyForQuery")
+ }
+ l.replyChan <- message{t, nil}
+
+ case 'N', 'S':
+ // ignore
+ default:
+ return fmt.Errorf("unexpected message %q from server in listenerConnLoop", t)
+ }
+ }
+}
+
+// This is the main routine for the goroutine receiving on the database
+// connection. Most of the main logic is in listenerConnLoop.
+func (l *ListenerConn) listenerConnMain() {
+ err := l.listenerConnLoop()
+
+ // listenerConnLoop terminated; we're done, but we still have to clean up.
+ // Make sure nobody tries to start any new queries by making sure the err
+ // pointer is set. It is important that we do not overwrite its value; a
+ // connection could be closed by either this goroutine or one sending on
+ // the connection -- whoever closes the connection is assumed to have the
+ // more meaningful error message (as the other one will probably get
+ // net.errClosed), so that goroutine sets the error we expose while the
+ // other error is discarded. If the connection is lost while two
+ // goroutines are operating on the socket, it probably doesn't matter which
+ // error we expose so we don't try to do anything more complex.
+ l.connectionLock.Lock()
+ if l.err == nil {
+ l.err = err
+ }
+ l.cn.Close()
+ l.connectionLock.Unlock()
+
+ // There might be a query in-flight; make sure nobody's waiting for a
+ // response to it, since there's not going to be one.
+ close(l.replyChan)
+
+ // let the listener know we're done
+ close(l.notificationChan)
+
+ // this ListenerConn is done
+}
+
+// Send a LISTEN query to the server. See ExecSimpleQuery.
+func (l *ListenerConn) Listen(channel string) (bool, error) {
+ return l.ExecSimpleQuery("LISTEN " + QuoteIdentifier(channel))
+}
+
+// Send an UNLISTEN query to the server. See ExecSimpleQuery.
+func (l *ListenerConn) Unlisten(channel string) (bool, error) {
+ return l.ExecSimpleQuery("UNLISTEN " + QuoteIdentifier(channel))
+}
+
+// Send `UNLISTEN *` to the server. See ExecSimpleQuery.
+func (l *ListenerConn) UnlistenAll() (bool, error) {
+ return l.ExecSimpleQuery("UNLISTEN *")
+}
+
+// Ping the remote server to make sure it's alive. Non-nil error means the
+// connection has failed and should be abandoned.
+func (l *ListenerConn) Ping() error {
+ sent, err := l.ExecSimpleQuery("")
+ if !sent {
+ return err
+ }
+ if err != nil {
+ // shouldn't happen
+ panic(err)
+ }
+ return nil
+}
+
+// Attempt to send a query on the connection. Returns an error if sending the
+// query failed, and the caller should initiate closure of this connection.
+// The caller must be holding senderLock (see acquireSenderLock and
+// releaseSenderLock).
+func (l *ListenerConn) sendSimpleQuery(q string) (err error) {
+ defer errRecoverNoErrBadConn(&err)
+
+ // must set connection state before sending the query
+ if !l.setState(connStateExpectResponse) {
+ panic("two queries running at the same time")
+ }
+
+ // Can't use l.cn.writeBuf here because it uses the scratch buffer which
+ // might get overwritten by listenerConnLoop.
+ b := &writeBuf{
+ buf: []byte("Q\x00\x00\x00\x00"),
+ pos: 1,
+ }
+ b.string(q)
+ l.cn.send(b)
+
+ return nil
+}
+
+// Execute a "simple query" (i.e. one with no bindable parameters) on the
+// connection. The possible return values are:
+// 1) "executed" is true; the query was executed to completion on the
+// database server. If the query failed, err will be set to the error
+// returned by the database, otherwise err will be nil.
+// 2) If "executed" is false, the query could not be executed on the remote
+// server. err will be non-nil.
+//
+// After a call to ExecSimpleQuery has returned an executed=false value, the
+// connection has either been closed or will be closed shortly thereafter, and
+// all subsequently executed queries will return an error.
+func (l *ListenerConn) ExecSimpleQuery(q string) (executed bool, err error) {
+ if err = l.acquireSenderLock(); err != nil {
+ return false, err
+ }
+ defer l.releaseSenderLock()
+
+ err = l.sendSimpleQuery(q)
+ if err != nil {
+ // We can't know what state the protocol is in, so we need to abandon
+ // this connection.
+ l.connectionLock.Lock()
+ // Set the error pointer if it hasn't been set already; see
+ // listenerConnMain.
+ if l.err == nil {
+ l.err = err
+ }
+ l.connectionLock.Unlock()
+ l.cn.c.Close()
+ return false, err
+ }
+
+ // now we just wait for a reply..
+ for {
+ m, ok := <-l.replyChan
+ if !ok {
+ // We lost the connection to server, don't bother waiting for a
+ // a response. err should have been set already.
+ l.connectionLock.Lock()
+ err := l.err
+ l.connectionLock.Unlock()
+ return false, err
+ }
+ switch m.typ {
+ case 'Z':
+ // sanity check
+ if m.err != nil {
+ panic("m.err != nil")
+ }
+ // done; err might or might not be set
+ return true, err
+
+ case 'E':
+ // sanity check
+ if m.err == nil {
+ panic("m.err == nil")
+ }
+ // server responded with an error; ReadyForQuery to follow
+ err = m.err
+
+ default:
+ return false, fmt.Errorf("unknown response for simple query: %q", m.typ)
+ }
+ }
+}
+
+func (l *ListenerConn) Close() error {
+ l.connectionLock.Lock()
+ if l.err != nil {
+ l.connectionLock.Unlock()
+ return errListenerConnClosed
+ }
+ l.err = errListenerConnClosed
+ l.connectionLock.Unlock()
+ // We can't send anything on the connection without holding senderLock.
+ // Simply close the net.Conn to wake up everyone operating on it.
+ return l.cn.c.Close()
+}
+
+// Err() returns the reason the connection was closed. It is not safe to call
+// this function until l.Notify has been closed.
+func (l *ListenerConn) Err() error {
+ return l.err
+}
+
+var errListenerClosed = errors.New("pq: Listener has been closed")
+
+var ErrChannelAlreadyOpen = errors.New("pq: channel is already open")
+var ErrChannelNotOpen = errors.New("pq: channel is not open")
+
+type ListenerEventType int
+
+const (
+ // Emitted only when the database connection has been initially
+ // initialized. err will always be nil.
+ ListenerEventConnected ListenerEventType = iota
+
+ // Emitted after a database connection has been lost, either because of an
+ // error or because Close has been called. err will be set to the reason
+ // the database connection was lost.
+ ListenerEventDisconnected
+
+ // Emitted after a database connection has been re-established after
+ // connection loss. err will always be nil. After this event has been
+ // emitted, a nil pq.Notification is sent on the Listener.Notify channel.
+ ListenerEventReconnected
+
+ // Emitted after a connection to the database was attempted, but failed.
+ // err will be set to an error describing why the connection attempt did
+ // not succeed.
+ ListenerEventConnectionAttemptFailed
+)
+
+type EventCallbackType func(event ListenerEventType, err error)
+
+// Listener provides an interface for listening to notifications from a
+// PostgreSQL database. For general usage information, see section
+// "Notifications".
+//
+// Listener can safely be used from concurrently running goroutines.
+type Listener struct {
+ // Channel for receiving notifications from the database. In some cases a
+ // nil value will be sent. See section "Notifications" above.
+ Notify chan *Notification
+
+ name string
+ minReconnectInterval time.Duration
+ maxReconnectInterval time.Duration
+ eventCallback EventCallbackType
+
+ lock sync.Mutex
+ isClosed bool
+ reconnectCond *sync.Cond
+ cn *ListenerConn
+ connNotificationChan <-chan *Notification
+ channels map[string]struct{}
+}
+
+// NewListener creates a new database connection dedicated to LISTEN / NOTIFY.
+//
+// name should be set to a connection string to be used to establish the
+// database connection (see section "Connection String Parameters" above).
+//
+// minReconnectInterval controls the duration to wait before trying to
+// re-establish the database connection after connection loss. After each
+// consecutive failure this interval is doubled, until maxReconnectInterval is
+// reached. Successfully completing the connection establishment procedure
+// resets the interval back to minReconnectInterval.
+//
+// The last parameter eventCallback can be set to a function which will be
+// called by the Listener when the state of the underlying database connection
+// changes. This callback will be called by the goroutine which dispatches the
+// notifications over the Notify channel, so you should try to avoid doing
+// potentially time-consuming operations from the callback.
+func NewListener(name string,
+ minReconnectInterval time.Duration,
+ maxReconnectInterval time.Duration,
+ eventCallback EventCallbackType) *Listener {
+ l := &Listener{
+ name: name,
+ minReconnectInterval: minReconnectInterval,
+ maxReconnectInterval: maxReconnectInterval,
+ eventCallback: eventCallback,
+
+ channels: make(map[string]struct{}),
+
+ Notify: make(chan *Notification, 32),
+ }
+ l.reconnectCond = sync.NewCond(&l.lock)
+
+ go l.listenerMain()
+
+ return l
+}
+
+// Returns the notification channel for this listener. This is the same
+// channel as Notify, and will not be recreated during the life time of the
+// Listener.
+func (l *Listener) NotificationChannel() <-chan *Notification {
+ return l.Notify
+}
+
+// Listen starts listening for notifications on a channel. Calls to this
+// function will block until an acknowledgement has been received from the
+// server. Note that Listener automatically re-establishes the connection
+// after connection loss, so this function may block indefinitely if the
+// connection can not be re-established.
+//
+// Listen will only fail in three conditions:
+// 1) The channel is already open. The returned error will be
+// ErrChannelAlreadyOpen.
+// 2) The query was executed on the remote server, but PostgreSQL returned an
+// error message in response to the query. The returned error will be a
+// pq.Error containing the information the server supplied.
+// 3) Close is called on the Listener before the request could be completed.
+//
+// The channel name is case-sensitive.
+func (l *Listener) Listen(channel string) error {
+ l.lock.Lock()
+ defer l.lock.Unlock()
+
+ if l.isClosed {
+ return errListenerClosed
+ }
+
+ // The server allows you to issue a LISTEN on a channel which is already
+ // open, but it seems useful to be able to detect this case to spot for
+ // mistakes in application logic. If the application genuinely does't
+ // care, it can check the exported error and ignore it.
+ _, exists := l.channels[channel]
+ if exists {
+ return ErrChannelAlreadyOpen
+ }
+
+ if l.cn != nil {
+ // If gotResponse is true but error is set, the query was executed on
+ // the remote server, but resulted in an error. This should be
+ // relatively rare, so it's fine if we just pass the error to our
+ // caller. However, if gotResponse is false, we could not complete the
+ // query on the remote server and our underlying connection is about
+ // to go away, so we only add relname to l.channels, and wait for
+ // resync() to take care of the rest.
+ gotResponse, err := l.cn.Listen(channel)
+ if gotResponse && err != nil {
+ return err
+ }
+ }
+
+ l.channels[channel] = struct{}{}
+ for l.cn == nil {
+ l.reconnectCond.Wait()
+ // we let go of the mutex for a while
+ if l.isClosed {
+ return errListenerClosed
+ }
+ }
+
+ return nil
+}
+
+// Unlisten removes a channel from the Listener's channel list. Returns
+// ErrChannelNotOpen if the Listener is not listening on the specified channel.
+// Returns immediately with no error if there is no connection. Note that you
+// might still get notifications for this channel even after Unlisten has
+// returned.
+//
+// The channel name is case-sensitive.
+func (l *Listener) Unlisten(channel string) error {
+ l.lock.Lock()
+ defer l.lock.Unlock()
+
+ if l.isClosed {
+ return errListenerClosed
+ }
+
+ // Similarly to LISTEN, this is not an error in Postgres, but it seems
+ // useful to distinguish from the normal conditions.
+ _, exists := l.channels[channel]
+ if !exists {
+ return ErrChannelNotOpen
+ }
+
+ if l.cn != nil {
+ // Similarly to Listen (see comment in that function), the caller
+ // should only be bothered with an error if it came from the backend as
+ // a response to our query.
+ gotResponse, err := l.cn.Unlisten(channel)
+ if gotResponse && err != nil {
+ return err
+ }
+ }
+
+ // Don't bother waiting for resync if there's no connection.
+ delete(l.channels, channel)
+ return nil
+}
+
+// UnlistenAll removes all channels from the Listener's channel list. Returns
+// immediately with no error if there is no connection. Note that you might
+// still get notifications for any of the deleted channels even after
+// UnlistenAll has returned.
+func (l *Listener) UnlistenAll() error {
+ l.lock.Lock()
+ defer l.lock.Unlock()
+
+ if l.isClosed {
+ return errListenerClosed
+ }
+
+ if l.cn != nil {
+ // Similarly to Listen (see comment in that function), the caller
+ // should only be bothered with an error if it came from the backend as
+ // a response to our query.
+ gotResponse, err := l.cn.UnlistenAll()
+ if gotResponse && err != nil {
+ return err
+ }
+ }
+
+ // Don't bother waiting for resync if there's no connection.
+ l.channels = make(map[string]struct{})
+ return nil
+}
+
+// Ping the remote server to make sure it's alive. Non-nil return value means
+// that there is no active connection.
+func (l *Listener) Ping() error {
+ l.lock.Lock()
+ defer l.lock.Unlock()
+
+ if l.isClosed {
+ return errListenerClosed
+ }
+ if l.cn == nil {
+ return errors.New("no connection")
+ }
+
+ return l.cn.Ping()
+}
+
+// Clean up after losing the server connection. Returns l.cn.Err(), which
+// should have the reason the connection was lost.
+func (l *Listener) disconnectCleanup() error {
+ l.lock.Lock()
+ defer l.lock.Unlock()
+
+ // sanity check; can't look at Err() until the channel has been closed
+ select {
+ case _, ok := <-l.connNotificationChan:
+ if ok {
+ panic("connNotificationChan not closed")
+ }
+ default:
+ panic("connNotificationChan not closed")
+ }
+
+ err := l.cn.Err()
+ l.cn.Close()
+ l.cn = nil
+ return err
+}
+
+// Synchronize the list of channels we want to be listening on with the server
+// after the connection has been established.
+func (l *Listener) resync(cn *ListenerConn, notificationChan <-chan *Notification) error {
+ doneChan := make(chan error)
+ go func() {
+ for channel := range l.channels {
+ // If we got a response, return that error to our caller as it's
+ // going to be more descriptive than cn.Err().
+ gotResponse, err := cn.Listen(channel)
+ if gotResponse && err != nil {
+ doneChan <- err
+ return
+ }
+
+ // If we couldn't reach the server, wait for notificationChan to
+ // close and then return the error message from the connection, as
+ // per ListenerConn's interface.
+ if err != nil {
+ for _ = range notificationChan {
+ }
+ doneChan <- cn.Err()
+ return
+ }
+ }
+ doneChan <- nil
+ }()
+
+ // Ignore notifications while synchronization is going on to avoid
+ // deadlocks. We have to send a nil notification over Notify anyway as
+ // we can't possibly know which notifications (if any) were lost while
+ // the connection was down, so there's no reason to try and process
+ // these messages at all.
+ for {
+ select {
+ case _, ok := <-notificationChan:
+ if !ok {
+ notificationChan = nil
+ }
+
+ case err := <-doneChan:
+ return err
+ }
+ }
+}
+
+// caller should NOT be holding l.lock
+func (l *Listener) closed() bool {
+ l.lock.Lock()
+ defer l.lock.Unlock()
+
+ return l.isClosed
+}
+
+func (l *Listener) connect() error {
+ notificationChan := make(chan *Notification, 32)
+ cn, err := NewListenerConn(l.name, notificationChan)
+ if err != nil {
+ return err
+ }
+
+ l.lock.Lock()
+ defer l.lock.Unlock()
+
+ err = l.resync(cn, notificationChan)
+ if err != nil {
+ cn.Close()
+ return err
+ }
+
+ l.cn = cn
+ l.connNotificationChan = notificationChan
+ l.reconnectCond.Broadcast()
+
+ return nil
+}
+
+// Close disconnects the Listener from the database and shuts it down.
+// Subsequent calls to its methods will return an error. Close returns an
+// error if the connection has already been closed.
+func (l *Listener) Close() error {
+ l.lock.Lock()
+ defer l.lock.Unlock()
+
+ if l.isClosed {
+ return errListenerClosed
+ }
+
+ if l.cn != nil {
+ l.cn.Close()
+ }
+ l.isClosed = true
+
+ return nil
+}
+
+func (l *Listener) emitEvent(event ListenerEventType, err error) {
+ if l.eventCallback != nil {
+ l.eventCallback(event, err)
+ }
+}
+
+// Main logic here: maintain a connection to the server when possible, wait
+// for notifications and emit events.
+func (l *Listener) listenerConnLoop() {
+ var nextReconnect time.Time
+
+ reconnectInterval := l.minReconnectInterval
+ for {
+ for {
+ err := l.connect()
+ if err == nil {
+ break
+ }
+
+ if l.closed() {
+ return
+ }
+ l.emitEvent(ListenerEventConnectionAttemptFailed, err)
+
+ time.Sleep(reconnectInterval)
+ reconnectInterval *= 2
+ if reconnectInterval > l.maxReconnectInterval {
+ reconnectInterval = l.maxReconnectInterval
+ }
+ }
+
+ if nextReconnect.IsZero() {
+ l.emitEvent(ListenerEventConnected, nil)
+ } else {
+ l.emitEvent(ListenerEventReconnected, nil)
+ l.Notify <- nil
+ }
+
+ reconnectInterval = l.minReconnectInterval
+ nextReconnect = time.Now().Add(reconnectInterval)
+
+ for {
+ notification, ok := <-l.connNotificationChan
+ if !ok {
+ // lost connection, loop again
+ break
+ }
+ l.Notify <- notification
+ }
+
+ err := l.disconnectCleanup()
+ if l.closed() {
+ return
+ }
+ l.emitEvent(ListenerEventDisconnected, err)
+
+ time.Sleep(nextReconnect.Sub(time.Now()))
+ }
+}
+
+func (l *Listener) listenerMain() {
+ l.listenerConnLoop()
+ close(l.Notify)
+}
diff --git a/vendor/github.com/lib/pq/oid/doc.go b/vendor/github.com/lib/pq/oid/doc.go
new file mode 100644
index 000000000..caaede248
--- /dev/null
+++ b/vendor/github.com/lib/pq/oid/doc.go
@@ -0,0 +1,6 @@
+// Package oid contains OID constants
+// as defined by the Postgres server.
+package oid
+
+// Oid is a Postgres Object ID.
+type Oid uint32
diff --git a/vendor/github.com/lib/pq/oid/gen.go b/vendor/github.com/lib/pq/oid/gen.go
new file mode 100644
index 000000000..cd4aea808
--- /dev/null
+++ b/vendor/github.com/lib/pq/oid/gen.go
@@ -0,0 +1,74 @@
+// +build ignore
+
+// Generate the table of OID values
+// Run with 'go run gen.go'.
+package main
+
+import (
+ "database/sql"
+ "fmt"
+ "log"
+ "os"
+ "os/exec"
+
+ _ "github.com/lib/pq"
+)
+
+func main() {
+ datname := os.Getenv("PGDATABASE")
+ sslmode := os.Getenv("PGSSLMODE")
+
+ if datname == "" {
+ os.Setenv("PGDATABASE", "pqgotest")
+ }
+
+ if sslmode == "" {
+ os.Setenv("PGSSLMODE", "disable")
+ }
+
+ db, err := sql.Open("postgres", "")
+ if err != nil {
+ log.Fatal(err)
+ }
+ cmd := exec.Command("gofmt")
+ cmd.Stderr = os.Stderr
+ w, err := cmd.StdinPipe()
+ if err != nil {
+ log.Fatal(err)
+ }
+ f, err := os.Create("types.go")
+ if err != nil {
+ log.Fatal(err)
+ }
+ cmd.Stdout = f
+ err = cmd.Start()
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Fprintln(w, "// generated by 'go run gen.go'; do not edit")
+ fmt.Fprintln(w, "\npackage oid")
+ fmt.Fprintln(w, "const (")
+ rows, err := db.Query(`
+ SELECT typname, oid
+ FROM pg_type WHERE oid < 10000
+ ORDER BY oid;
+ `)
+ if err != nil {
+ log.Fatal(err)
+ }
+ var name string
+ var oid int
+ for rows.Next() {
+ err = rows.Scan(&name, &oid)
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Fprintf(w, "T_%s Oid = %d\n", name, oid)
+ }
+ if err = rows.Err(); err != nil {
+ log.Fatal(err)
+ }
+ fmt.Fprintln(w, ")")
+ w.Close()
+ cmd.Wait()
+}
diff --git a/vendor/github.com/lib/pq/oid/types.go b/vendor/github.com/lib/pq/oid/types.go
new file mode 100644
index 000000000..03df05a61
--- /dev/null
+++ b/vendor/github.com/lib/pq/oid/types.go
@@ -0,0 +1,161 @@
+// generated by 'go run gen.go'; do not edit
+
+package oid
+
+const (
+ T_bool Oid = 16
+ T_bytea Oid = 17
+ T_char Oid = 18
+ T_name Oid = 19
+ T_int8 Oid = 20
+ T_int2 Oid = 21
+ T_int2vector Oid = 22
+ T_int4 Oid = 23
+ T_regproc Oid = 24
+ T_text Oid = 25
+ T_oid Oid = 26
+ T_tid Oid = 27
+ T_xid Oid = 28
+ T_cid Oid = 29
+ T_oidvector Oid = 30
+ T_pg_type Oid = 71
+ T_pg_attribute Oid = 75
+ T_pg_proc Oid = 81
+ T_pg_class Oid = 83
+ T_json Oid = 114
+ T_xml Oid = 142
+ T__xml Oid = 143
+ T_pg_node_tree Oid = 194
+ T__json Oid = 199
+ T_smgr Oid = 210
+ T_point Oid = 600
+ T_lseg Oid = 601
+ T_path Oid = 602
+ T_box Oid = 603
+ T_polygon Oid = 604
+ T_line Oid = 628
+ T__line Oid = 629
+ T_cidr Oid = 650
+ T__cidr Oid = 651
+ T_float4 Oid = 700
+ T_float8 Oid = 701
+ T_abstime Oid = 702
+ T_reltime Oid = 703
+ T_tinterval Oid = 704
+ T_unknown Oid = 705
+ T_circle Oid = 718
+ T__circle Oid = 719
+ T_money Oid = 790
+ T__money Oid = 791
+ T_macaddr Oid = 829
+ T_inet Oid = 869
+ T__bool Oid = 1000
+ T__bytea Oid = 1001
+ T__char Oid = 1002
+ T__name Oid = 1003
+ T__int2 Oid = 1005
+ T__int2vector Oid = 1006
+ T__int4 Oid = 1007
+ T__regproc Oid = 1008
+ T__text Oid = 1009
+ T__tid Oid = 1010
+ T__xid Oid = 1011
+ T__cid Oid = 1012
+ T__oidvector Oid = 1013
+ T__bpchar Oid = 1014
+ T__varchar Oid = 1015
+ T__int8 Oid = 1016
+ T__point Oid = 1017
+ T__lseg Oid = 1018
+ T__path Oid = 1019
+ T__box Oid = 1020
+ T__float4 Oid = 1021
+ T__float8 Oid = 1022
+ T__abstime Oid = 1023
+ T__reltime Oid = 1024
+ T__tinterval Oid = 1025
+ T__polygon Oid = 1027
+ T__oid Oid = 1028
+ T_aclitem Oid = 1033
+ T__aclitem Oid = 1034
+ T__macaddr Oid = 1040
+ T__inet Oid = 1041
+ T_bpchar Oid = 1042
+ T_varchar Oid = 1043
+ T_date Oid = 1082
+ T_time Oid = 1083
+ T_timestamp Oid = 1114
+ T__timestamp Oid = 1115
+ T__date Oid = 1182
+ T__time Oid = 1183
+ T_timestamptz Oid = 1184
+ T__timestamptz Oid = 1185
+ T_interval Oid = 1186
+ T__interval Oid = 1187
+ T__numeric Oid = 1231
+ T_pg_database Oid = 1248
+ T__cstring Oid = 1263
+ T_timetz Oid = 1266
+ T__timetz Oid = 1270
+ T_bit Oid = 1560
+ T__bit Oid = 1561
+ T_varbit Oid = 1562
+ T__varbit Oid = 1563
+ T_numeric Oid = 1700
+ T_refcursor Oid = 1790
+ T__refcursor Oid = 2201
+ T_regprocedure Oid = 2202
+ T_regoper Oid = 2203
+ T_regoperator Oid = 2204
+ T_regclass Oid = 2205
+ T_regtype Oid = 2206
+ T__regprocedure Oid = 2207
+ T__regoper Oid = 2208
+ T__regoperator Oid = 2209
+ T__regclass Oid = 2210
+ T__regtype Oid = 2211
+ T_record Oid = 2249
+ T_cstring Oid = 2275
+ T_any Oid = 2276
+ T_anyarray Oid = 2277
+ T_void Oid = 2278
+ T_trigger Oid = 2279
+ T_language_handler Oid = 2280
+ T_internal Oid = 2281
+ T_opaque Oid = 2282
+ T_anyelement Oid = 2283
+ T__record Oid = 2287
+ T_anynonarray Oid = 2776
+ T_pg_authid Oid = 2842
+ T_pg_auth_members Oid = 2843
+ T__txid_snapshot Oid = 2949
+ T_uuid Oid = 2950
+ T__uuid Oid = 2951
+ T_txid_snapshot Oid = 2970
+ T_fdw_handler Oid = 3115
+ T_anyenum Oid = 3500
+ T_tsvector Oid = 3614
+ T_tsquery Oid = 3615
+ T_gtsvector Oid = 3642
+ T__tsvector Oid = 3643
+ T__gtsvector Oid = 3644
+ T__tsquery Oid = 3645
+ T_regconfig Oid = 3734
+ T__regconfig Oid = 3735
+ T_regdictionary Oid = 3769
+ T__regdictionary Oid = 3770
+ T_anyrange Oid = 3831
+ T_event_trigger Oid = 3838
+ T_int4range Oid = 3904
+ T__int4range Oid = 3905
+ T_numrange Oid = 3906
+ T__numrange Oid = 3907
+ T_tsrange Oid = 3908
+ T__tsrange Oid = 3909
+ T_tstzrange Oid = 3910
+ T__tstzrange Oid = 3911
+ T_daterange Oid = 3912
+ T__daterange Oid = 3913
+ T_int8range Oid = 3926
+ T__int8range Oid = 3927
+)
diff --git a/vendor/github.com/lib/pq/url.go b/vendor/github.com/lib/pq/url.go
new file mode 100644
index 000000000..f4d8a7c20
--- /dev/null
+++ b/vendor/github.com/lib/pq/url.go
@@ -0,0 +1,76 @@
+package pq
+
+import (
+ "fmt"
+ "net"
+ nurl "net/url"
+ "sort"
+ "strings"
+)
+
+// ParseURL no longer needs to be used by clients of this library since supplying a URL as a
+// connection string to sql.Open() is now supported:
+//
+// sql.Open("postgres", "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full")
+//
+// It remains exported here for backwards-compatibility.
+//
+// ParseURL converts a url to a connection string for driver.Open.
+// Example:
+//
+// "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full"
+//
+// converts to:
+//
+// "user=bob password=secret host=1.2.3.4 port=5432 dbname=mydb sslmode=verify-full"
+//
+// A minimal example:
+//
+// "postgres://"
+//
+// This will be blank, causing driver.Open to use all of the defaults
+func ParseURL(url string) (string, error) {
+ u, err := nurl.Parse(url)
+ if err != nil {
+ return "", err
+ }
+
+ if u.Scheme != "postgres" && u.Scheme != "postgresql" {
+ return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
+ }
+
+ var kvs []string
+ escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
+ accrue := func(k, v string) {
+ if v != "" {
+ kvs = append(kvs, k+"="+escaper.Replace(v))
+ }
+ }
+
+ if u.User != nil {
+ v := u.User.Username()
+ accrue("user", v)
+
+ v, _ = u.User.Password()
+ accrue("password", v)
+ }
+
+ if host, port, err := net.SplitHostPort(u.Host); err != nil {
+ accrue("host", u.Host)
+ } else {
+ accrue("host", host)
+ accrue("port", port)
+ }
+
+ if u.Path != "" {
+ accrue("dbname", u.Path[1:])
+ }
+
+ q := u.Query()
+ for k := range q {
+ accrue(k, q.Get(k))
+ }
+
+ sort.Strings(kvs) // Makes testing easier (not a performance concern)
+ return strings.Join(kvs, " "), nil
+}
diff --git a/vendor/github.com/lib/pq/user_posix.go b/vendor/github.com/lib/pq/user_posix.go
new file mode 100644
index 000000000..bf982524f
--- /dev/null
+++ b/vendor/github.com/lib/pq/user_posix.go
@@ -0,0 +1,24 @@
+// Package pq is a pure Go Postgres driver for the database/sql package.
+
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris rumprun
+
+package pq
+
+import (
+ "os"
+ "os/user"
+)
+
+func userCurrent() (string, error) {
+ u, err := user.Current()
+ if err == nil {
+ return u.Username, nil
+ }
+
+ name := os.Getenv("USER")
+ if name != "" {
+ return name, nil
+ }
+
+ return "", ErrCouldNotDetectUsername
+}
diff --git a/vendor/github.com/lib/pq/user_windows.go b/vendor/github.com/lib/pq/user_windows.go
new file mode 100644
index 000000000..2b691267b
--- /dev/null
+++ b/vendor/github.com/lib/pq/user_windows.go
@@ -0,0 +1,27 @@
+// Package pq is a pure Go Postgres driver for the database/sql package.
+package pq
+
+import (
+ "path/filepath"
+ "syscall"
+)
+
+// Perform Windows user name lookup identically to libpq.
+//
+// The PostgreSQL code makes use of the legacy Win32 function
+// GetUserName, and that function has not been imported into stock Go.
+// GetUserNameEx is available though, the difference being that a
+// wider range of names are available. To get the output to be the
+// same as GetUserName, only the base (or last) component of the
+// result is returned.
+func userCurrent() (string, error) {
+ pw_name := make([]uint16, 128)
+ pwname_size := uint32(len(pw_name)) - 1
+ err := syscall.GetUserNameEx(syscall.NameSamCompatible, &pw_name[0], &pwname_size)
+ if err != nil {
+ return "", ErrCouldNotDetectUsername
+ }
+ s := syscall.UTF16ToString(pw_name)
+ u := filepath.Base(s)
+ return u, nil
+}
diff --git a/vendor/github.com/mattermost/rsc/LICENSE b/vendor/github.com/mattermost/rsc/LICENSE
new file mode 100644
index 000000000..6a66aea5e
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/mattermost/rsc/gf256/Makefile b/vendor/github.com/mattermost/rsc/gf256/Makefile
new file mode 100644
index 000000000..518a034f3
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/gf256/Makefile
@@ -0,0 +1,8 @@
+# Copyright 2010 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include $(GOROOT)/src/Make.inc
+TARG=rsc.googlecode.com/hg/gf256
+GOFILES=gf256.go #rs.go
+include $(GOROOT)/src/Make.pkg
diff --git a/vendor/github.com/mattermost/rsc/gf256/gf256.go b/vendor/github.com/mattermost/rsc/gf256/gf256.go
new file mode 100644
index 000000000..34cc975a8
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/gf256/gf256.go
@@ -0,0 +1,241 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package gf256 implements arithmetic over the Galois Field GF(256).
+package gf256
+
+import "strconv"
+
+// A Field represents an instance of GF(256) defined by a specific polynomial.
+type Field struct {
+ log [256]byte // log[0] is unused
+ exp [510]byte
+}
+
+// NewField returns a new field corresponding to the polynomial poly
+// and generator α. The Reed-Solomon encoding in QR codes uses
+// polynomial 0x11d with generator 2.
+//
+// The choice of generator α only affects the Exp and Log operations.
+func NewField(poly, α int) *Field {
+ if poly < 0x100 || poly >= 0x200 || reducible(poly) {
+ panic("gf256: invalid polynomial: " + strconv.Itoa(poly))
+ }
+
+ var f Field
+ x := 1
+ for i := 0; i < 255; i++ {
+ if x == 1 && i != 0 {
+ panic("gf256: invalid generator " + strconv.Itoa(α) +
+ " for polynomial " + strconv.Itoa(poly))
+ }
+ f.exp[i] = byte(x)
+ f.exp[i+255] = byte(x)
+ f.log[x] = byte(i)
+ x = mul(x, α, poly)
+ }
+ f.log[0] = 255
+ for i := 0; i < 255; i++ {
+ if f.log[f.exp[i]] != byte(i) {
+ panic("bad log")
+ }
+ if f.log[f.exp[i+255]] != byte(i) {
+ panic("bad log")
+ }
+ }
+ for i := 1; i < 256; i++ {
+ if f.exp[f.log[i]] != byte(i) {
+ panic("bad log")
+ }
+ }
+
+ return &f
+}
+
+// nbit returns the number of significant in p.
+func nbit(p int) uint {
+ n := uint(0)
+ for ; p > 0; p >>= 1 {
+ n++
+ }
+ return n
+}
+
+// polyDiv divides the polynomial p by q and returns the remainder.
+func polyDiv(p, q int) int {
+ np := nbit(p)
+ nq := nbit(q)
+ for ; np >= nq; np-- {
+ if p&(1<<(np-1)) != 0 {
+ p ^= q << (np - nq)
+ }
+ }
+ return p
+}
+
+// mul returns the product x*y mod poly, a GF(256) multiplication.
+func mul(x, y, poly int) int {
+ z := 0
+ for x > 0 {
+ if x&1 != 0 {
+ z ^= y
+ }
+ x >>= 1
+ y <<= 1
+ if y&0x100 != 0 {
+ y ^= poly
+ }
+ }
+ return z
+}
+
+// reducible reports whether p is reducible.
+func reducible(p int) bool {
+ // Multiplying n-bit * n-bit produces (2n-1)-bit,
+ // so if p is reducible, one of its factors must be
+ // of np/2+1 bits or fewer.
+ np := nbit(p)
+ for q := 2; q < 1<<(np/2+1); q++ {
+ if polyDiv(p, q) == 0 {
+ return true
+ }
+ }
+ return false
+}
+
+// Add returns the sum of x and y in the field.
+func (f *Field) Add(x, y byte) byte {
+ return x ^ y
+}
+
+// Exp returns the the base-α exponential of e in the field.
+// If e < 0, Exp returns 0.
+func (f *Field) Exp(e int) byte {
+ if e < 0 {
+ return 0
+ }
+ return f.exp[e%255]
+}
+
+// Log returns the base-α logarithm of x in the field.
+// If x == 0, Log returns -1.
+func (f *Field) Log(x byte) int {
+ if x == 0 {
+ return -1
+ }
+ return int(f.log[x])
+}
+
+// Inv returns the multiplicative inverse of x in the field.
+// If x == 0, Inv returns 0.
+func (f *Field) Inv(x byte) byte {
+ if x == 0 {
+ return 0
+ }
+ return f.exp[255-f.log[x]]
+}
+
+// Mul returns the product of x and y in the field.
+func (f *Field) Mul(x, y byte) byte {
+ if x == 0 || y == 0 {
+ return 0
+ }
+ return f.exp[int(f.log[x])+int(f.log[y])]
+}
+
+// An RSEncoder implements Reed-Solomon encoding
+// over a given field using a given number of error correction bytes.
+type RSEncoder struct {
+ f *Field
+ c int
+ gen []byte
+ lgen []byte
+ p []byte
+}
+
+func (f *Field) gen(e int) (gen, lgen []byte) {
+ // p = 1
+ p := make([]byte, e+1)
+ p[e] = 1
+
+ for i := 0; i < e; i++ {
+ // p *= (x + Exp(i))
+ // p[j] = p[j]*Exp(i) + p[j+1].
+ c := f.Exp(i)
+ for j := 0; j < e; j++ {
+ p[j] = f.Mul(p[j], c) ^ p[j+1]
+ }
+ p[e] = f.Mul(p[e], c)
+ }
+
+ // lp = log p.
+ lp := make([]byte, e+1)
+ for i, c := range p {
+ if c == 0 {
+ lp[i] = 255
+ } else {
+ lp[i] = byte(f.Log(c))
+ }
+ }
+
+ return p, lp
+}
+
+// NewRSEncoder returns a new Reed-Solomon encoder
+// over the given field and number of error correction bytes.
+func NewRSEncoder(f *Field, c int) *RSEncoder {
+ gen, lgen := f.gen(c)
+ return &RSEncoder{f: f, c: c, gen: gen, lgen: lgen}
+}
+
+// ECC writes to check the error correcting code bytes
+// for data using the given Reed-Solomon parameters.
+func (rs *RSEncoder) ECC(data []byte, check []byte) {
+ if len(check) < rs.c {
+ panic("gf256: invalid check byte length")
+ }
+ if rs.c == 0 {
+ return
+ }
+
+ // The check bytes are the remainder after dividing
+ // data padded with c zeros by the generator polynomial.
+
+ // p = data padded with c zeros.
+ var p []byte
+ n := len(data) + rs.c
+ if len(rs.p) >= n {
+ p = rs.p
+ } else {
+ p = make([]byte, n)
+ }
+ copy(p, data)
+ for i := len(data); i < len(p); i++ {
+ p[i] = 0
+ }
+
+ // Divide p by gen, leaving the remainder in p[len(data):].
+ // p[0] is the most significant term in p, and
+ // gen[0] is the most significant term in the generator,
+ // which is always 1.
+ // To avoid repeated work, we store various values as
+ // lv, not v, where lv = log[v].
+ f := rs.f
+ lgen := rs.lgen[1:]
+ for i := 0; i < len(data); i++ {
+ c := p[i]
+ if c == 0 {
+ continue
+ }
+ q := p[i+1:]
+ exp := f.exp[f.log[c]:]
+ for j, lg := range lgen {
+ if lg != 255 { // lgen uses 255 for log 0
+ q[j] ^= exp[lg]
+ }
+ }
+ }
+ copy(check, p[len(data):])
+ rs.p = p
+}
diff --git a/vendor/github.com/mattermost/rsc/qr/Makefile b/vendor/github.com/mattermost/rsc/qr/Makefile
new file mode 100644
index 000000000..d00c470bb
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/qr/Makefile
@@ -0,0 +1,4 @@
+include $(GOROOT)/src/Make.inc
+TARG=rsc.googlecode.com/hg/qr
+GOFILES=qr.go png.go
+include $(GOROOT)/src/Make.pkg
diff --git a/vendor/github.com/mattermost/rsc/qr/coding/Makefile b/vendor/github.com/mattermost/rsc/qr/coding/Makefile
new file mode 100644
index 000000000..5d1c4d307
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/qr/coding/Makefile
@@ -0,0 +1,7 @@
+include $(GOROOT)/src/Make.inc
+
+TARG=rsc.googlecode.com/hg/qr/coding
+GOFILES=\
+ qr.go\
+
+include $(GOROOT)/src/Make.pkg
diff --git a/vendor/github.com/mattermost/rsc/qr/coding/gen.go b/vendor/github.com/mattermost/rsc/qr/coding/gen.go
new file mode 100644
index 000000000..a3857f277
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/qr/coding/gen.go
@@ -0,0 +1,149 @@
+// +build ignore
+
+package main
+
+import "fmt"
+
+// tables from qrencode-3.1.1/qrspec.c
+
+var capacity = [41]struct {
+ width int
+ words int
+ remainder int
+ ec [4]int
+}{
+ {0, 0, 0, [4]int{0, 0, 0, 0}},
+ {21, 26, 0, [4]int{7, 10, 13, 17}}, // 1
+ {25, 44, 7, [4]int{10, 16, 22, 28}},
+ {29, 70, 7, [4]int{15, 26, 36, 44}},
+ {33, 100, 7, [4]int{20, 36, 52, 64}},
+ {37, 134, 7, [4]int{26, 48, 72, 88}}, // 5
+ {41, 172, 7, [4]int{36, 64, 96, 112}},
+ {45, 196, 0, [4]int{40, 72, 108, 130}},
+ {49, 242, 0, [4]int{48, 88, 132, 156}},
+ {53, 292, 0, [4]int{60, 110, 160, 192}},
+ {57, 346, 0, [4]int{72, 130, 192, 224}}, //10
+ {61, 404, 0, [4]int{80, 150, 224, 264}},
+ {65, 466, 0, [4]int{96, 176, 260, 308}},
+ {69, 532, 0, [4]int{104, 198, 288, 352}},
+ {73, 581, 3, [4]int{120, 216, 320, 384}},
+ {77, 655, 3, [4]int{132, 240, 360, 432}}, //15
+ {81, 733, 3, [4]int{144, 280, 408, 480}},
+ {85, 815, 3, [4]int{168, 308, 448, 532}},
+ {89, 901, 3, [4]int{180, 338, 504, 588}},
+ {93, 991, 3, [4]int{196, 364, 546, 650}},
+ {97, 1085, 3, [4]int{224, 416, 600, 700}}, //20
+ {101, 1156, 4, [4]int{224, 442, 644, 750}},
+ {105, 1258, 4, [4]int{252, 476, 690, 816}},
+ {109, 1364, 4, [4]int{270, 504, 750, 900}},
+ {113, 1474, 4, [4]int{300, 560, 810, 960}},
+ {117, 1588, 4, [4]int{312, 588, 870, 1050}}, //25
+ {121, 1706, 4, [4]int{336, 644, 952, 1110}},
+ {125, 1828, 4, [4]int{360, 700, 1020, 1200}},
+ {129, 1921, 3, [4]int{390, 728, 1050, 1260}},
+ {133, 2051, 3, [4]int{420, 784, 1140, 1350}},
+ {137, 2185, 3, [4]int{450, 812, 1200, 1440}}, //30
+ {141, 2323, 3, [4]int{480, 868, 1290, 1530}},
+ {145, 2465, 3, [4]int{510, 924, 1350, 1620}},
+ {149, 2611, 3, [4]int{540, 980, 1440, 1710}},
+ {153, 2761, 3, [4]int{570, 1036, 1530, 1800}},
+ {157, 2876, 0, [4]int{570, 1064, 1590, 1890}}, //35
+ {161, 3034, 0, [4]int{600, 1120, 1680, 1980}},
+ {165, 3196, 0, [4]int{630, 1204, 1770, 2100}},
+ {169, 3362, 0, [4]int{660, 1260, 1860, 2220}},
+ {173, 3532, 0, [4]int{720, 1316, 1950, 2310}},
+ {177, 3706, 0, [4]int{750, 1372, 2040, 2430}}, //40
+}
+
+var eccTable = [41][4][2]int{
+ {{0, 0}, {0, 0}, {0, 0}, {0, 0}},
+ {{1, 0}, {1, 0}, {1, 0}, {1, 0}}, // 1
+ {{1, 0}, {1, 0}, {1, 0}, {1, 0}},
+ {{1, 0}, {1, 0}, {2, 0}, {2, 0}},
+ {{1, 0}, {2, 0}, {2, 0}, {4, 0}},
+ {{1, 0}, {2, 0}, {2, 2}, {2, 2}}, // 5
+ {{2, 0}, {4, 0}, {4, 0}, {4, 0}},
+ {{2, 0}, {4, 0}, {2, 4}, {4, 1}},
+ {{2, 0}, {2, 2}, {4, 2}, {4, 2}},
+ {{2, 0}, {3, 2}, {4, 4}, {4, 4}},
+ {{2, 2}, {4, 1}, {6, 2}, {6, 2}}, //10
+ {{4, 0}, {1, 4}, {4, 4}, {3, 8}},
+ {{2, 2}, {6, 2}, {4, 6}, {7, 4}},
+ {{4, 0}, {8, 1}, {8, 4}, {12, 4}},
+ {{3, 1}, {4, 5}, {11, 5}, {11, 5}},
+ {{5, 1}, {5, 5}, {5, 7}, {11, 7}}, //15
+ {{5, 1}, {7, 3}, {15, 2}, {3, 13}},
+ {{1, 5}, {10, 1}, {1, 15}, {2, 17}},
+ {{5, 1}, {9, 4}, {17, 1}, {2, 19}},
+ {{3, 4}, {3, 11}, {17, 4}, {9, 16}},
+ {{3, 5}, {3, 13}, {15, 5}, {15, 10}}, //20
+ {{4, 4}, {17, 0}, {17, 6}, {19, 6}},
+ {{2, 7}, {17, 0}, {7, 16}, {34, 0}},
+ {{4, 5}, {4, 14}, {11, 14}, {16, 14}},
+ {{6, 4}, {6, 14}, {11, 16}, {30, 2}},
+ {{8, 4}, {8, 13}, {7, 22}, {22, 13}}, //25
+ {{10, 2}, {19, 4}, {28, 6}, {33, 4}},
+ {{8, 4}, {22, 3}, {8, 26}, {12, 28}},
+ {{3, 10}, {3, 23}, {4, 31}, {11, 31}},
+ {{7, 7}, {21, 7}, {1, 37}, {19, 26}},
+ {{5, 10}, {19, 10}, {15, 25}, {23, 25}}, //30
+ {{13, 3}, {2, 29}, {42, 1}, {23, 28}},
+ {{17, 0}, {10, 23}, {10, 35}, {19, 35}},
+ {{17, 1}, {14, 21}, {29, 19}, {11, 46}},
+ {{13, 6}, {14, 23}, {44, 7}, {59, 1}},
+ {{12, 7}, {12, 26}, {39, 14}, {22, 41}}, //35
+ {{6, 14}, {6, 34}, {46, 10}, {2, 64}},
+ {{17, 4}, {29, 14}, {49, 10}, {24, 46}},
+ {{4, 18}, {13, 32}, {48, 14}, {42, 32}},
+ {{20, 4}, {40, 7}, {43, 22}, {10, 67}},
+ {{19, 6}, {18, 31}, {34, 34}, {20, 61}}, //40
+}
+
+var align = [41][2]int{
+ {0, 0},
+ {0, 0}, {18, 0}, {22, 0}, {26, 0}, {30, 0}, // 1- 5
+ {34, 0}, {22, 38}, {24, 42}, {26, 46}, {28, 50}, // 6-10
+ {30, 54}, {32, 58}, {34, 62}, {26, 46}, {26, 48}, //11-15
+ {26, 50}, {30, 54}, {30, 56}, {30, 58}, {34, 62}, //16-20
+ {28, 50}, {26, 50}, {30, 54}, {28, 54}, {32, 58}, //21-25
+ {30, 58}, {34, 62}, {26, 50}, {30, 54}, {26, 52}, //26-30
+ {30, 56}, {34, 60}, {30, 58}, {34, 62}, {30, 54}, //31-35
+ {24, 50}, {28, 54}, {32, 58}, {26, 54}, {30, 58}, //35-40
+}
+
+var versionPattern = [41]int{
+ 0,
+ 0, 0, 0, 0, 0, 0,
+ 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d,
+ 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9,
+ 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75,
+ 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64,
+ 0x27541, 0x28c69,
+}
+
+func main() {
+ fmt.Printf("\t{},\n")
+ for i := 1; i <= 40; i++ {
+ apos := align[i][0] - 2
+ if apos < 0 {
+ apos = 100
+ }
+ astride := align[i][1] - align[i][0]
+ if astride < 1 {
+ astride = 100
+ }
+ fmt.Printf("\t{%v, %v, %v, %#x, [4]level{{%v, %v}, {%v, %v}, {%v, %v}, {%v, %v}}}, // %v\n",
+ apos, astride, capacity[i].words,
+ versionPattern[i],
+ eccTable[i][0][0]+eccTable[i][0][1],
+ float64(capacity[i].ec[0])/float64(eccTable[i][0][0]+eccTable[i][0][1]),
+ eccTable[i][1][0]+eccTable[i][1][1],
+ float64(capacity[i].ec[1])/float64(eccTable[i][1][0]+eccTable[i][1][1]),
+ eccTable[i][2][0]+eccTable[i][2][1],
+ float64(capacity[i].ec[2])/float64(eccTable[i][2][0]+eccTable[i][2][1]),
+ eccTable[i][3][0]+eccTable[i][3][1],
+ float64(capacity[i].ec[3])/float64(eccTable[i][3][0]+eccTable[i][3][1]),
+ i,
+ )
+ }
+}
diff --git a/vendor/github.com/mattermost/rsc/qr/coding/qr.go b/vendor/github.com/mattermost/rsc/qr/coding/qr.go
new file mode 100644
index 000000000..35711a4eb
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/qr/coding/qr.go
@@ -0,0 +1,815 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package coding implements low-level QR coding details.
+package coding
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/mattermost/rsc/gf256"
+)
+
+// Field is the field for QR error correction.
+var Field = gf256.NewField(0x11d, 2)
+
+// A Version represents a QR version.
+// The version specifies the size of the QR code:
+// a QR code with version v has 4v+17 pixels on a side.
+// Versions number from 1 to 40: the larger the version,
+// the more information the code can store.
+type Version int
+
+const MinVersion = 1
+const MaxVersion = 40
+
+func (v Version) String() string {
+ return strconv.Itoa(int(v))
+}
+
+func (v Version) sizeClass() int {
+ if v <= 9 {
+ return 0
+ }
+ if v <= 26 {
+ return 1
+ }
+ return 2
+}
+
+// DataBytes returns the number of data bytes that can be
+// stored in a QR code with the given version and level.
+func (v Version) DataBytes(l Level) int {
+ vt := &vtab[v]
+ lev := &vt.level[l]
+ return vt.bytes - lev.nblock*lev.check
+}
+
+// Encoding implements a QR data encoding scheme.
+// The implementations--Numeric, Alphanumeric, and String--specify
+// the character set and the mapping from UTF-8 to code bits.
+// The more restrictive the mode, the fewer code bits are needed.
+type Encoding interface {
+ Check() error
+ Bits(v Version) int
+ Encode(b *Bits, v Version)
+}
+
+type Bits struct {
+ b []byte
+ nbit int
+}
+
+func (b *Bits) Reset() {
+ b.b = b.b[:0]
+ b.nbit = 0
+}
+
+func (b *Bits) Bits() int {
+ return b.nbit
+}
+
+func (b *Bits) Bytes() []byte {
+ if b.nbit%8 != 0 {
+ panic("fractional byte")
+ }
+ return b.b
+}
+
+func (b *Bits) Append(p []byte) {
+ if b.nbit%8 != 0 {
+ panic("fractional byte")
+ }
+ b.b = append(b.b, p...)
+ b.nbit += 8 * len(p)
+}
+
+func (b *Bits) Write(v uint, nbit int) {
+ for nbit > 0 {
+ n := nbit
+ if n > 8 {
+ n = 8
+ }
+ if b.nbit%8 == 0 {
+ b.b = append(b.b, 0)
+ } else {
+ m := -b.nbit & 7
+ if n > m {
+ n = m
+ }
+ }
+ b.nbit += n
+ sh := uint(nbit - n)
+ b.b[len(b.b)-1] |= uint8(v >> sh << uint(-b.nbit&7))
+ v -= v >> sh << sh
+ nbit -= n
+ }
+}
+
+// Num is the encoding for numeric data.
+// The only valid characters are the decimal digits 0 through 9.
+type Num string
+
+func (s Num) String() string {
+ return fmt.Sprintf("Num(%#q)", string(s))
+}
+
+func (s Num) Check() error {
+ for _, c := range s {
+ if c < '0' || '9' < c {
+ return fmt.Errorf("non-numeric string %#q", string(s))
+ }
+ }
+ return nil
+}
+
+var numLen = [3]int{10, 12, 14}
+
+func (s Num) Bits(v Version) int {
+ return 4 + numLen[v.sizeClass()] + (10*len(s)+2)/3
+}
+
+func (s Num) Encode(b *Bits, v Version) {
+ b.Write(1, 4)
+ b.Write(uint(len(s)), numLen[v.sizeClass()])
+ var i int
+ for i = 0; i+3 <= len(s); i += 3 {
+ w := uint(s[i]-'0')*100 + uint(s[i+1]-'0')*10 + uint(s[i+2]-'0')
+ b.Write(w, 10)
+ }
+ switch len(s) - i {
+ case 1:
+ w := uint(s[i] - '0')
+ b.Write(w, 4)
+ case 2:
+ w := uint(s[i]-'0')*10 + uint(s[i+1]-'0')
+ b.Write(w, 7)
+ }
+}
+
+// Alpha is the encoding for alphanumeric data.
+// The valid characters are 0-9A-Z$%*+-./: and space.
+type Alpha string
+
+const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
+
+func (s Alpha) String() string {
+ return fmt.Sprintf("Alpha(%#q)", string(s))
+}
+
+func (s Alpha) Check() error {
+ for _, c := range s {
+ if strings.IndexRune(alphabet, c) < 0 {
+ return fmt.Errorf("non-alphanumeric string %#q", string(s))
+ }
+ }
+ return nil
+}
+
+var alphaLen = [3]int{9, 11, 13}
+
+func (s Alpha) Bits(v Version) int {
+ return 4 + alphaLen[v.sizeClass()] + (11*len(s)+1)/2
+}
+
+func (s Alpha) Encode(b *Bits, v Version) {
+ b.Write(2, 4)
+ b.Write(uint(len(s)), alphaLen[v.sizeClass()])
+ var i int
+ for i = 0; i+2 <= len(s); i += 2 {
+ w := uint(strings.IndexRune(alphabet, rune(s[i])))*45 +
+ uint(strings.IndexRune(alphabet, rune(s[i+1])))
+ b.Write(w, 11)
+ }
+
+ if i < len(s) {
+ w := uint(strings.IndexRune(alphabet, rune(s[i])))
+ b.Write(w, 6)
+ }
+}
+
+// String is the encoding for 8-bit data. All bytes are valid.
+type String string
+
+func (s String) String() string {
+ return fmt.Sprintf("String(%#q)", string(s))
+}
+
+func (s String) Check() error {
+ return nil
+}
+
+var stringLen = [3]int{8, 16, 16}
+
+func (s String) Bits(v Version) int {
+ return 4 + stringLen[v.sizeClass()] + 8*len(s)
+}
+
+func (s String) Encode(b *Bits, v Version) {
+ b.Write(4, 4)
+ b.Write(uint(len(s)), stringLen[v.sizeClass()])
+ for i := 0; i < len(s); i++ {
+ b.Write(uint(s[i]), 8)
+ }
+}
+
+// A Pixel describes a single pixel in a QR code.
+type Pixel uint32
+
+const (
+ Black Pixel = 1 << iota
+ Invert
+)
+
+func (p Pixel) Offset() uint {
+ return uint(p >> 6)
+}
+
+func OffsetPixel(o uint) Pixel {
+ return Pixel(o << 6)
+}
+
+func (r PixelRole) Pixel() Pixel {
+ return Pixel(r << 2)
+}
+
+func (p Pixel) Role() PixelRole {
+ return PixelRole(p>>2) & 15
+}
+
+func (p Pixel) String() string {
+ s := p.Role().String()
+ if p&Black != 0 {
+ s += "+black"
+ }
+ if p&Invert != 0 {
+ s += "+invert"
+ }
+ s += "+" + strconv.FormatUint(uint64(p.Offset()), 10)
+ return s
+}
+
+// A PixelRole describes the role of a QR pixel.
+type PixelRole uint32
+
+const (
+ _ PixelRole = iota
+ Position // position squares (large)
+ Alignment // alignment squares (small)
+ Timing // timing strip between position squares
+ Format // format metadata
+ PVersion // version pattern
+ Unused // unused pixel
+ Data // data bit
+ Check // error correction check bit
+ Extra
+)
+
+var roles = []string{
+ "",
+ "position",
+ "alignment",
+ "timing",
+ "format",
+ "pversion",
+ "unused",
+ "data",
+ "check",
+ "extra",
+}
+
+func (r PixelRole) String() string {
+ if Position <= r && r <= Check {
+ return roles[r]
+ }
+ return strconv.Itoa(int(r))
+}
+
+// A Level represents a QR error correction level.
+// From least to most tolerant of errors, they are L, M, Q, H.
+type Level int
+
+const (
+ L Level = iota
+ M
+ Q
+ H
+)
+
+func (l Level) String() string {
+ if L <= l && l <= H {
+ return "LMQH"[l : l+1]
+ }
+ return strconv.Itoa(int(l))
+}
+
+// A Code is a square pixel grid.
+type Code struct {
+ Bitmap []byte // 1 is black, 0 is white
+ Size int // number of pixels on a side
+ Stride int // number of bytes per row
+}
+
+func (c *Code) Black(x, y int) bool {
+ return 0 <= x && x < c.Size && 0 <= y && y < c.Size &&
+ c.Bitmap[y*c.Stride+x/8]&(1<<uint(7-x&7)) != 0
+}
+
+// A Mask describes a mask that is applied to the QR
+// code to avoid QR artifacts being interpreted as
+// alignment and timing patterns (such as the squares
+// in the corners). Valid masks are integers from 0 to 7.
+type Mask int
+
+// http://www.swetake.com/qr/qr5_en.html
+var mfunc = []func(int, int) bool{
+ func(i, j int) bool { return (i+j)%2 == 0 },
+ func(i, j int) bool { return i%2 == 0 },
+ func(i, j int) bool { return j%3 == 0 },
+ func(i, j int) bool { return (i+j)%3 == 0 },
+ func(i, j int) bool { return (i/2+j/3)%2 == 0 },
+ func(i, j int) bool { return i*j%2+i*j%3 == 0 },
+ func(i, j int) bool { return (i*j%2+i*j%3)%2 == 0 },
+ func(i, j int) bool { return (i*j%3+(i+j)%2)%2 == 0 },
+}
+
+func (m Mask) Invert(y, x int) bool {
+ if m < 0 {
+ return false
+ }
+ return mfunc[m](y, x)
+}
+
+// A Plan describes how to construct a QR code
+// with a specific version, level, and mask.
+type Plan struct {
+ Version Version
+ Level Level
+ Mask Mask
+
+ DataBytes int // number of data bytes
+ CheckBytes int // number of error correcting (checksum) bytes
+ Blocks int // number of data blocks
+
+ Pixel [][]Pixel // pixel map
+}
+
+// NewPlan returns a Plan for a QR code with the given
+// version, level, and mask.
+func NewPlan(version Version, level Level, mask Mask) (*Plan, error) {
+ p, err := vplan(version)
+ if err != nil {
+ return nil, err
+ }
+ if err := fplan(level, mask, p); err != nil {
+ return nil, err
+ }
+ if err := lplan(version, level, p); err != nil {
+ return nil, err
+ }
+ if err := mplan(mask, p); err != nil {
+ return nil, err
+ }
+ return p, nil
+}
+
+func (b *Bits) Pad(n int) {
+ if n < 0 {
+ panic("qr: invalid pad size")
+ }
+ if n <= 4 {
+ b.Write(0, n)
+ } else {
+ b.Write(0, 4)
+ n -= 4
+ n -= -b.Bits() & 7
+ b.Write(0, -b.Bits()&7)
+ pad := n / 8
+ for i := 0; i < pad; i += 2 {
+ b.Write(0xec, 8)
+ if i+1 >= pad {
+ break
+ }
+ b.Write(0x11, 8)
+ }
+ }
+}
+
+func (b *Bits) AddCheckBytes(v Version, l Level) {
+ nd := v.DataBytes(l)
+ if b.nbit < nd*8 {
+ b.Pad(nd*8 - b.nbit)
+ }
+ if b.nbit != nd*8 {
+ panic("qr: too much data")
+ }
+
+ dat := b.Bytes()
+ vt := &vtab[v]
+ lev := &vt.level[l]
+ db := nd / lev.nblock
+ extra := nd % lev.nblock
+ chk := make([]byte, lev.check)
+ rs := gf256.NewRSEncoder(Field, lev.check)
+ for i := 0; i < lev.nblock; i++ {
+ if i == lev.nblock-extra {
+ db++
+ }
+ rs.ECC(dat[:db], chk)
+ b.Append(chk)
+ dat = dat[db:]
+ }
+
+ if len(b.Bytes()) != vt.bytes {
+ panic("qr: internal error")
+ }
+}
+
+func (p *Plan) Encode(text ...Encoding) (*Code, error) {
+ var b Bits
+ for _, t := range text {
+ if err := t.Check(); err != nil {
+ return nil, err
+ }
+ t.Encode(&b, p.Version)
+ }
+ if b.Bits() > p.DataBytes*8 {
+ return nil, fmt.Errorf("cannot encode %d bits into %d-bit code", b.Bits(), p.DataBytes*8)
+ }
+ b.AddCheckBytes(p.Version, p.Level)
+ bytes := b.Bytes()
+
+ // Now we have the checksum bytes and the data bytes.
+ // Construct the actual code.
+ c := &Code{Size: len(p.Pixel), Stride: (len(p.Pixel) + 7) &^ 7}
+ c.Bitmap = make([]byte, c.Stride*c.Size)
+ crow := c.Bitmap
+ for _, row := range p.Pixel {
+ for x, pix := range row {
+ switch pix.Role() {
+ case Data, Check:
+ o := pix.Offset()
+ if bytes[o/8]&(1<<uint(7-o&7)) != 0 {
+ pix ^= Black
+ }
+ }
+ if pix&Black != 0 {
+ crow[x/8] |= 1 << uint(7-x&7)
+ }
+ }
+ crow = crow[c.Stride:]
+ }
+ return c, nil
+}
+
+// A version describes metadata associated with a version.
+type version struct {
+ apos int
+ astride int
+ bytes int
+ pattern int
+ level [4]level
+}
+
+type level struct {
+ nblock int
+ check int
+}
+
+var vtab = []version{
+ {},
+ {100, 100, 26, 0x0, [4]level{{1, 7}, {1, 10}, {1, 13}, {1, 17}}}, // 1
+ {16, 100, 44, 0x0, [4]level{{1, 10}, {1, 16}, {1, 22}, {1, 28}}}, // 2
+ {20, 100, 70, 0x0, [4]level{{1, 15}, {1, 26}, {2, 18}, {2, 22}}}, // 3
+ {24, 100, 100, 0x0, [4]level{{1, 20}, {2, 18}, {2, 26}, {4, 16}}}, // 4
+ {28, 100, 134, 0x0, [4]level{{1, 26}, {2, 24}, {4, 18}, {4, 22}}}, // 5
+ {32, 100, 172, 0x0, [4]level{{2, 18}, {4, 16}, {4, 24}, {4, 28}}}, // 6
+ {20, 16, 196, 0x7c94, [4]level{{2, 20}, {4, 18}, {6, 18}, {5, 26}}}, // 7
+ {22, 18, 242, 0x85bc, [4]level{{2, 24}, {4, 22}, {6, 22}, {6, 26}}}, // 8
+ {24, 20, 292, 0x9a99, [4]level{{2, 30}, {5, 22}, {8, 20}, {8, 24}}}, // 9
+ {26, 22, 346, 0xa4d3, [4]level{{4, 18}, {5, 26}, {8, 24}, {8, 28}}}, // 10
+ {28, 24, 404, 0xbbf6, [4]level{{4, 20}, {5, 30}, {8, 28}, {11, 24}}}, // 11
+ {30, 26, 466, 0xc762, [4]level{{4, 24}, {8, 22}, {10, 26}, {11, 28}}}, // 12
+ {32, 28, 532, 0xd847, [4]level{{4, 26}, {9, 22}, {12, 24}, {16, 22}}}, // 13
+ {24, 20, 581, 0xe60d, [4]level{{4, 30}, {9, 24}, {16, 20}, {16, 24}}}, // 14
+ {24, 22, 655, 0xf928, [4]level{{6, 22}, {10, 24}, {12, 30}, {18, 24}}}, // 15
+ {24, 24, 733, 0x10b78, [4]level{{6, 24}, {10, 28}, {17, 24}, {16, 30}}}, // 16
+ {28, 24, 815, 0x1145d, [4]level{{6, 28}, {11, 28}, {16, 28}, {19, 28}}}, // 17
+ {28, 26, 901, 0x12a17, [4]level{{6, 30}, {13, 26}, {18, 28}, {21, 28}}}, // 18
+ {28, 28, 991, 0x13532, [4]level{{7, 28}, {14, 26}, {21, 26}, {25, 26}}}, // 19
+ {32, 28, 1085, 0x149a6, [4]level{{8, 28}, {16, 26}, {20, 30}, {25, 28}}}, // 20
+ {26, 22, 1156, 0x15683, [4]level{{8, 28}, {17, 26}, {23, 28}, {25, 30}}}, // 21
+ {24, 24, 1258, 0x168c9, [4]level{{9, 28}, {17, 28}, {23, 30}, {34, 24}}}, // 22
+ {28, 24, 1364, 0x177ec, [4]level{{9, 30}, {18, 28}, {25, 30}, {30, 30}}}, // 23
+ {26, 26, 1474, 0x18ec4, [4]level{{10, 30}, {20, 28}, {27, 30}, {32, 30}}}, // 24
+ {30, 26, 1588, 0x191e1, [4]level{{12, 26}, {21, 28}, {29, 30}, {35, 30}}}, // 25
+ {28, 28, 1706, 0x1afab, [4]level{{12, 28}, {23, 28}, {34, 28}, {37, 30}}}, // 26
+ {32, 28, 1828, 0x1b08e, [4]level{{12, 30}, {25, 28}, {34, 30}, {40, 30}}}, // 27
+ {24, 24, 1921, 0x1cc1a, [4]level{{13, 30}, {26, 28}, {35, 30}, {42, 30}}}, // 28
+ {28, 24, 2051, 0x1d33f, [4]level{{14, 30}, {28, 28}, {38, 30}, {45, 30}}}, // 29
+ {24, 26, 2185, 0x1ed75, [4]level{{15, 30}, {29, 28}, {40, 30}, {48, 30}}}, // 30
+ {28, 26, 2323, 0x1f250, [4]level{{16, 30}, {31, 28}, {43, 30}, {51, 30}}}, // 31
+ {32, 26, 2465, 0x209d5, [4]level{{17, 30}, {33, 28}, {45, 30}, {54, 30}}}, // 32
+ {28, 28, 2611, 0x216f0, [4]level{{18, 30}, {35, 28}, {48, 30}, {57, 30}}}, // 33
+ {32, 28, 2761, 0x228ba, [4]level{{19, 30}, {37, 28}, {51, 30}, {60, 30}}}, // 34
+ {28, 24, 2876, 0x2379f, [4]level{{19, 30}, {38, 28}, {53, 30}, {63, 30}}}, // 35
+ {22, 26, 3034, 0x24b0b, [4]level{{20, 30}, {40, 28}, {56, 30}, {66, 30}}}, // 36
+ {26, 26, 3196, 0x2542e, [4]level{{21, 30}, {43, 28}, {59, 30}, {70, 30}}}, // 37
+ {30, 26, 3362, 0x26a64, [4]level{{22, 30}, {45, 28}, {62, 30}, {74, 30}}}, // 38
+ {24, 28, 3532, 0x27541, [4]level{{24, 30}, {47, 28}, {65, 30}, {77, 30}}}, // 39
+ {28, 28, 3706, 0x28c69, [4]level{{25, 30}, {49, 28}, {68, 30}, {81, 30}}}, // 40
+}
+
+func grid(siz int) [][]Pixel {
+ m := make([][]Pixel, siz)
+ pix := make([]Pixel, siz*siz)
+ for i := range m {
+ m[i], pix = pix[:siz], pix[siz:]
+ }
+ return m
+}
+
+// vplan creates a Plan for the given version.
+func vplan(v Version) (*Plan, error) {
+ p := &Plan{Version: v}
+ if v < 1 || v > 40 {
+ return nil, fmt.Errorf("invalid QR version %d", int(v))
+ }
+ siz := 17 + int(v)*4
+ m := grid(siz)
+ p.Pixel = m
+
+ // Timing markers (overwritten by boxes).
+ const ti = 6 // timing is in row/column 6 (counting from 0)
+ for i := range m {
+ p := Timing.Pixel()
+ if i&1 == 0 {
+ p |= Black
+ }
+ m[i][ti] = p
+ m[ti][i] = p
+ }
+
+ // Position boxes.
+ posBox(m, 0, 0)
+ posBox(m, siz-7, 0)
+ posBox(m, 0, siz-7)
+
+ // Alignment boxes.
+ info := &vtab[v]
+ for x := 4; x+5 < siz; {
+ for y := 4; y+5 < siz; {
+ // don't overwrite timing markers
+ if (x < 7 && y < 7) || (x < 7 && y+5 >= siz-7) || (x+5 >= siz-7 && y < 7) {
+ } else {
+ alignBox(m, x, y)
+ }
+ if y == 4 {
+ y = info.apos
+ } else {
+ y += info.astride
+ }
+ }
+ if x == 4 {
+ x = info.apos
+ } else {
+ x += info.astride
+ }
+ }
+
+ // Version pattern.
+ pat := vtab[v].pattern
+ if pat != 0 {
+ v := pat
+ for x := 0; x < 6; x++ {
+ for y := 0; y < 3; y++ {
+ p := PVersion.Pixel()
+ if v&1 != 0 {
+ p |= Black
+ }
+ m[siz-11+y][x] = p
+ m[x][siz-11+y] = p
+ v >>= 1
+ }
+ }
+ }
+
+ // One lonely black pixel
+ m[siz-8][8] = Unused.Pixel() | Black
+
+ return p, nil
+}
+
+// fplan adds the format pixels
+func fplan(l Level, m Mask, p *Plan) error {
+ // Format pixels.
+ fb := uint32(l^1) << 13 // level: L=01, M=00, Q=11, H=10
+ fb |= uint32(m) << 10 // mask
+ const formatPoly = 0x537
+ rem := fb
+ for i := 14; i >= 10; i-- {
+ if rem&(1<<uint(i)) != 0 {
+ rem ^= formatPoly << uint(i-10)
+ }
+ }
+ fb |= rem
+ invert := uint32(0x5412)
+ siz := len(p.Pixel)
+ for i := uint(0); i < 15; i++ {
+ pix := Format.Pixel() + OffsetPixel(i)
+ if (fb>>i)&1 == 1 {
+ pix |= Black
+ }
+ if (invert>>i)&1 == 1 {
+ pix ^= Invert | Black
+ }
+ // top left
+ switch {
+ case i < 6:
+ p.Pixel[i][8] = pix
+ case i < 8:
+ p.Pixel[i+1][8] = pix
+ case i < 9:
+ p.Pixel[8][7] = pix
+ default:
+ p.Pixel[8][14-i] = pix
+ }
+ // bottom right
+ switch {
+ case i < 8:
+ p.Pixel[8][siz-1-int(i)] = pix
+ default:
+ p.Pixel[siz-1-int(14-i)][8] = pix
+ }
+ }
+ return nil
+}
+
+// lplan edits a version-only Plan to add information
+// about the error correction levels.
+func lplan(v Version, l Level, p *Plan) error {
+ p.Level = l
+
+ nblock := vtab[v].level[l].nblock
+ ne := vtab[v].level[l].check
+ nde := (vtab[v].bytes - ne*nblock) / nblock
+ extra := (vtab[v].bytes - ne*nblock) % nblock
+ dataBits := (nde*nblock + extra) * 8
+ checkBits := ne * nblock * 8
+
+ p.DataBytes = vtab[v].bytes - ne*nblock
+ p.CheckBytes = ne * nblock
+ p.Blocks = nblock
+
+ // Make data + checksum pixels.
+ data := make([]Pixel, dataBits)
+ for i := range data {
+ data[i] = Data.Pixel() | OffsetPixel(uint(i))
+ }
+ check := make([]Pixel, checkBits)
+ for i := range check {
+ check[i] = Check.Pixel() | OffsetPixel(uint(i+dataBits))
+ }
+
+ // Split into blocks.
+ dataList := make([][]Pixel, nblock)
+ checkList := make([][]Pixel, nblock)
+ for i := 0; i < nblock; i++ {
+ // The last few blocks have an extra data byte (8 pixels).
+ nd := nde
+ if i >= nblock-extra {
+ nd++
+ }
+ dataList[i], data = data[0:nd*8], data[nd*8:]
+ checkList[i], check = check[0:ne*8], check[ne*8:]
+ }
+ if len(data) != 0 || len(check) != 0 {
+ panic("data/check math")
+ }
+
+ // Build up bit sequence, taking first byte of each block,
+ // then second byte, and so on. Then checksums.
+ bits := make([]Pixel, dataBits+checkBits)
+ dst := bits
+ for i := 0; i < nde+1; i++ {
+ for _, b := range dataList {
+ if i*8 < len(b) {
+ copy(dst, b[i*8:(i+1)*8])
+ dst = dst[8:]
+ }
+ }
+ }
+ for i := 0; i < ne; i++ {
+ for _, b := range checkList {
+ if i*8 < len(b) {
+ copy(dst, b[i*8:(i+1)*8])
+ dst = dst[8:]
+ }
+ }
+ }
+ if len(dst) != 0 {
+ panic("dst math")
+ }
+
+ // Sweep up pair of columns,
+ // then down, assigning to right then left pixel.
+ // Repeat.
+ // See Figure 2 of http://www.pclviewer.com/rs2/qrtopology.htm
+ siz := len(p.Pixel)
+ rem := make([]Pixel, 7)
+ for i := range rem {
+ rem[i] = Extra.Pixel()
+ }
+ src := append(bits, rem...)
+ for x := siz; x > 0; {
+ for y := siz - 1; y >= 0; y-- {
+ if p.Pixel[y][x-1].Role() == 0 {
+ p.Pixel[y][x-1], src = src[0], src[1:]
+ }
+ if p.Pixel[y][x-2].Role() == 0 {
+ p.Pixel[y][x-2], src = src[0], src[1:]
+ }
+ }
+ x -= 2
+ if x == 7 { // vertical timing strip
+ x--
+ }
+ for y := 0; y < siz; y++ {
+ if p.Pixel[y][x-1].Role() == 0 {
+ p.Pixel[y][x-1], src = src[0], src[1:]
+ }
+ if p.Pixel[y][x-2].Role() == 0 {
+ p.Pixel[y][x-2], src = src[0], src[1:]
+ }
+ }
+ x -= 2
+ }
+ return nil
+}
+
+// mplan edits a version+level-only Plan to add the mask.
+func mplan(m Mask, p *Plan) error {
+ p.Mask = m
+ for y, row := range p.Pixel {
+ for x, pix := range row {
+ if r := pix.Role(); (r == Data || r == Check || r == Extra) && p.Mask.Invert(y, x) {
+ row[x] ^= Black | Invert
+ }
+ }
+ }
+ return nil
+}
+
+// posBox draws a position (large) box at upper left x, y.
+func posBox(m [][]Pixel, x, y int) {
+ pos := Position.Pixel()
+ // box
+ for dy := 0; dy < 7; dy++ {
+ for dx := 0; dx < 7; dx++ {
+ p := pos
+ if dx == 0 || dx == 6 || dy == 0 || dy == 6 || 2 <= dx && dx <= 4 && 2 <= dy && dy <= 4 {
+ p |= Black
+ }
+ m[y+dy][x+dx] = p
+ }
+ }
+ // white border
+ for dy := -1; dy < 8; dy++ {
+ if 0 <= y+dy && y+dy < len(m) {
+ if x > 0 {
+ m[y+dy][x-1] = pos
+ }
+ if x+7 < len(m) {
+ m[y+dy][x+7] = pos
+ }
+ }
+ }
+ for dx := -1; dx < 8; dx++ {
+ if 0 <= x+dx && x+dx < len(m) {
+ if y > 0 {
+ m[y-1][x+dx] = pos
+ }
+ if y+7 < len(m) {
+ m[y+7][x+dx] = pos
+ }
+ }
+ }
+}
+
+// alignBox draw an alignment (small) box at upper left x, y.
+func alignBox(m [][]Pixel, x, y int) {
+ // box
+ align := Alignment.Pixel()
+ for dy := 0; dy < 5; dy++ {
+ for dx := 0; dx < 5; dx++ {
+ p := align
+ if dx == 0 || dx == 4 || dy == 0 || dy == 4 || dx == 2 && dy == 2 {
+ p |= Black
+ }
+ m[y+dy][x+dx] = p
+ }
+ }
+}
diff --git a/vendor/github.com/mattermost/rsc/qr/png.go b/vendor/github.com/mattermost/rsc/qr/png.go
new file mode 100644
index 000000000..db49d0577
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/qr/png.go
@@ -0,0 +1,400 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qr
+
+// PNG writer for QR codes.
+
+import (
+ "bytes"
+ "encoding/binary"
+ "hash"
+ "hash/crc32"
+)
+
+// PNG returns a PNG image displaying the code.
+//
+// PNG uses a custom encoder tailored to QR codes.
+// Its compressed size is about 2x away from optimal,
+// but it runs about 20x faster than calling png.Encode
+// on c.Image().
+func (c *Code) PNG() []byte {
+ var p pngWriter
+ return p.encode(c)
+}
+
+type pngWriter struct {
+ tmp [16]byte
+ wctmp [4]byte
+ buf bytes.Buffer
+ zlib bitWriter
+ crc hash.Hash32
+}
+
+var pngHeader = []byte("\x89PNG\r\n\x1a\n")
+
+func (w *pngWriter) encode(c *Code) []byte {
+ scale := c.Scale
+ siz := c.Size
+
+ w.buf.Reset()
+
+ // Header
+ w.buf.Write(pngHeader)
+
+ // Header block
+ binary.BigEndian.PutUint32(w.tmp[0:4], uint32((siz+8)*scale))
+ binary.BigEndian.PutUint32(w.tmp[4:8], uint32((siz+8)*scale))
+ w.tmp[8] = 1 // 1-bit
+ w.tmp[9] = 0 // gray
+ w.tmp[10] = 0
+ w.tmp[11] = 0
+ w.tmp[12] = 0
+ w.writeChunk("IHDR", w.tmp[:13])
+
+ // Comment
+ w.writeChunk("tEXt", comment)
+
+ // Data
+ w.zlib.writeCode(c)
+ w.writeChunk("IDAT", w.zlib.bytes.Bytes())
+
+ // End
+ w.writeChunk("IEND", nil)
+
+ return w.buf.Bytes()
+}
+
+var comment = []byte("Software\x00QR-PNG http://qr.swtch.com/")
+
+func (w *pngWriter) writeChunk(name string, data []byte) {
+ if w.crc == nil {
+ w.crc = crc32.NewIEEE()
+ }
+ binary.BigEndian.PutUint32(w.wctmp[0:4], uint32(len(data)))
+ w.buf.Write(w.wctmp[0:4])
+ w.crc.Reset()
+ copy(w.wctmp[0:4], name)
+ w.buf.Write(w.wctmp[0:4])
+ w.crc.Write(w.wctmp[0:4])
+ w.buf.Write(data)
+ w.crc.Write(data)
+ crc := w.crc.Sum32()
+ binary.BigEndian.PutUint32(w.wctmp[0:4], crc)
+ w.buf.Write(w.wctmp[0:4])
+}
+
+func (b *bitWriter) writeCode(c *Code) {
+ const ftNone = 0
+
+ b.adler32.Reset()
+ b.bytes.Reset()
+ b.nbit = 0
+
+ scale := c.Scale
+ siz := c.Size
+
+ // zlib header
+ b.tmp[0] = 0x78
+ b.tmp[1] = 0
+ b.tmp[1] += uint8(31 - (uint16(b.tmp[0])<<8+uint16(b.tmp[1]))%31)
+ b.bytes.Write(b.tmp[0:2])
+
+ // Start flate block.
+ b.writeBits(1, 1, false) // final block
+ b.writeBits(1, 2, false) // compressed, fixed Huffman tables
+
+ // White border.
+ // First row.
+ b.byte(ftNone)
+ n := (scale*(siz+8) + 7) / 8
+ b.byte(255)
+ b.repeat(n-1, 1)
+ // 4*scale rows total.
+ b.repeat((4*scale-1)*(1+n), 1+n)
+
+ for i := 0; i < 4*scale; i++ {
+ b.adler32.WriteNByte(ftNone, 1)
+ b.adler32.WriteNByte(255, n)
+ }
+
+ row := make([]byte, 1+n)
+ for y := 0; y < siz; y++ {
+ row[0] = ftNone
+ j := 1
+ var z uint8
+ nz := 0
+ for x := -4; x < siz+4; x++ {
+ // Raw data.
+ for i := 0; i < scale; i++ {
+ z <<= 1
+ if !c.Black(x, y) {
+ z |= 1
+ }
+ if nz++; nz == 8 {
+ row[j] = z
+ j++
+ nz = 0
+ }
+ }
+ }
+ if j < len(row) {
+ row[j] = z
+ }
+ for _, z := range row {
+ b.byte(z)
+ }
+
+ // Scale-1 copies.
+ b.repeat((scale-1)*(1+n), 1+n)
+
+ b.adler32.WriteN(row, scale)
+ }
+
+ // White border.
+ // First row.
+ b.byte(ftNone)
+ b.byte(255)
+ b.repeat(n-1, 1)
+ // 4*scale rows total.
+ b.repeat((4*scale-1)*(1+n), 1+n)
+
+ for i := 0; i < 4*scale; i++ {
+ b.adler32.WriteNByte(ftNone, 1)
+ b.adler32.WriteNByte(255, n)
+ }
+
+ // End of block.
+ b.hcode(256)
+ b.flushBits()
+
+ // adler32
+ binary.BigEndian.PutUint32(b.tmp[0:], b.adler32.Sum32())
+ b.bytes.Write(b.tmp[0:4])
+}
+
+// A bitWriter is a write buffer for bit-oriented data like deflate.
+type bitWriter struct {
+ bytes bytes.Buffer
+ bit uint32
+ nbit uint
+
+ tmp [4]byte
+ adler32 adigest
+}
+
+func (b *bitWriter) writeBits(bit uint32, nbit uint, rev bool) {
+ // reverse, for huffman codes
+ if rev {
+ br := uint32(0)
+ for i := uint(0); i < nbit; i++ {
+ br |= ((bit >> i) & 1) << (nbit - 1 - i)
+ }
+ bit = br
+ }
+ b.bit |= bit << b.nbit
+ b.nbit += nbit
+ for b.nbit >= 8 {
+ b.bytes.WriteByte(byte(b.bit))
+ b.bit >>= 8
+ b.nbit -= 8
+ }
+}
+
+func (b *bitWriter) flushBits() {
+ if b.nbit > 0 {
+ b.bytes.WriteByte(byte(b.bit))
+ b.nbit = 0
+ b.bit = 0
+ }
+}
+
+func (b *bitWriter) hcode(v int) {
+ /*
+ Lit Value Bits Codes
+ --------- ---- -----
+ 0 - 143 8 00110000 through
+ 10111111
+ 144 - 255 9 110010000 through
+ 111111111
+ 256 - 279 7 0000000 through
+ 0010111
+ 280 - 287 8 11000000 through
+ 11000111
+ */
+ switch {
+ case v <= 143:
+ b.writeBits(uint32(v)+0x30, 8, true)
+ case v <= 255:
+ b.writeBits(uint32(v-144)+0x190, 9, true)
+ case v <= 279:
+ b.writeBits(uint32(v-256)+0, 7, true)
+ case v <= 287:
+ b.writeBits(uint32(v-280)+0xc0, 8, true)
+ default:
+ panic("invalid hcode")
+ }
+}
+
+func (b *bitWriter) byte(x byte) {
+ b.hcode(int(x))
+}
+
+func (b *bitWriter) codex(c int, val int, nx uint) {
+ b.hcode(c + val>>nx)
+ b.writeBits(uint32(val)&(1<<nx-1), nx, false)
+}
+
+func (b *bitWriter) repeat(n, d int) {
+ for ; n >= 258+3; n -= 258 {
+ b.repeat1(258, d)
+ }
+ if n > 258 {
+ // 258 < n < 258+3
+ b.repeat1(10, d)
+ b.repeat1(n-10, d)
+ return
+ }
+ if n < 3 {
+ panic("invalid flate repeat")
+ }
+ b.repeat1(n, d)
+}
+
+func (b *bitWriter) repeat1(n, d int) {
+ /*
+ Extra Extra Extra
+ Code Bits Length(s) Code Bits Lengths Code Bits Length(s)
+ ---- ---- ------ ---- ---- ------- ---- ---- -------
+ 257 0 3 267 1 15,16 277 4 67-82
+ 258 0 4 268 1 17,18 278 4 83-98
+ 259 0 5 269 2 19-22 279 4 99-114
+ 260 0 6 270 2 23-26 280 4 115-130
+ 261 0 7 271 2 27-30 281 5 131-162
+ 262 0 8 272 2 31-34 282 5 163-194
+ 263 0 9 273 3 35-42 283 5 195-226
+ 264 0 10 274 3 43-50 284 5 227-257
+ 265 1 11,12 275 3 51-58 285 0 258
+ 266 1 13,14 276 3 59-66
+ */
+ switch {
+ case n <= 10:
+ b.codex(257, n-3, 0)
+ case n <= 18:
+ b.codex(265, n-11, 1)
+ case n <= 34:
+ b.codex(269, n-19, 2)
+ case n <= 66:
+ b.codex(273, n-35, 3)
+ case n <= 130:
+ b.codex(277, n-67, 4)
+ case n <= 257:
+ b.codex(281, n-131, 5)
+ case n == 258:
+ b.hcode(285)
+ default:
+ panic("invalid repeat length")
+ }
+
+ /*
+ Extra Extra Extra
+ Code Bits Dist Code Bits Dist Code Bits Distance
+ ---- ---- ---- ---- ---- ------ ---- ---- --------
+ 0 0 1 10 4 33-48 20 9 1025-1536
+ 1 0 2 11 4 49-64 21 9 1537-2048
+ 2 0 3 12 5 65-96 22 10 2049-3072
+ 3 0 4 13 5 97-128 23 10 3073-4096
+ 4 1 5,6 14 6 129-192 24 11 4097-6144
+ 5 1 7,8 15 6 193-256 25 11 6145-8192
+ 6 2 9-12 16 7 257-384 26 12 8193-12288
+ 7 2 13-16 17 7 385-512 27 12 12289-16384
+ 8 3 17-24 18 8 513-768 28 13 16385-24576
+ 9 3 25-32 19 8 769-1024 29 13 24577-32768
+ */
+ if d <= 4 {
+ b.writeBits(uint32(d-1), 5, true)
+ } else if d <= 32768 {
+ nbit := uint(16)
+ for d <= 1<<(nbit-1) {
+ nbit--
+ }
+ v := uint32(d - 1)
+ v &^= 1 << (nbit - 1) // top bit is implicit
+ code := uint32(2*nbit - 2) // second bit is low bit of code
+ code |= v >> (nbit - 2)
+ v &^= 1 << (nbit - 2)
+ b.writeBits(code, 5, true)
+ // rest of bits follow
+ b.writeBits(uint32(v), nbit-2, false)
+ } else {
+ panic("invalid repeat distance")
+ }
+}
+
+func (b *bitWriter) run(v byte, n int) {
+ if n == 0 {
+ return
+ }
+ b.byte(v)
+ if n-1 < 3 {
+ for i := 0; i < n-1; i++ {
+ b.byte(v)
+ }
+ } else {
+ b.repeat(n-1, 1)
+ }
+}
+
+type adigest struct {
+ a, b uint32
+}
+
+func (d *adigest) Reset() { d.a, d.b = 1, 0 }
+
+const amod = 65521
+
+func aupdate(a, b uint32, pi byte, n int) (aa, bb uint32) {
+ // TODO(rsc): 6g doesn't do magic multiplies for b %= amod,
+ // only for b = b%amod.
+
+ // invariant: a, b < amod
+ if pi == 0 {
+ b += uint32(n%amod) * a
+ b = b % amod
+ return a, b
+ }
+
+ // n times:
+ // a += pi
+ // b += a
+ // is same as
+ // b += n*a + n*(n+1)/2*pi
+ // a += n*pi
+ m := uint32(n)
+ b += (m % amod) * a
+ b = b % amod
+ b += (m * (m + 1) / 2) % amod * uint32(pi)
+ b = b % amod
+ a += (m % amod) * uint32(pi)
+ a = a % amod
+ return a, b
+}
+
+func afinish(a, b uint32) uint32 {
+ return b<<16 | a
+}
+
+func (d *adigest) WriteN(p []byte, n int) {
+ for i := 0; i < n; i++ {
+ for _, pi := range p {
+ d.a, d.b = aupdate(d.a, d.b, pi, 1)
+ }
+ }
+}
+
+func (d *adigest) WriteNByte(pi byte, n int) {
+ d.a, d.b = aupdate(d.a, d.b, pi, n)
+}
+
+func (d *adigest) Sum32() uint32 { return afinish(d.a, d.b) }
diff --git a/vendor/github.com/mattermost/rsc/qr/qr.go b/vendor/github.com/mattermost/rsc/qr/qr.go
new file mode 100644
index 000000000..1d20d02f3
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/qr/qr.go
@@ -0,0 +1,116 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package qr encodes QR codes.
+*/
+package qr
+
+import (
+ "errors"
+ "image"
+ "image/color"
+
+ "github.com/mattermost/rsc/qr/coding"
+)
+
+// A Level denotes a QR error correction level.
+// From least to most tolerant of errors, they are L, M, Q, H.
+type Level int
+
+const (
+ L Level = iota // 20% redundant
+ M // 38% redundant
+ Q // 55% redundant
+ H // 65% redundant
+)
+
+// Encode returns an encoding of text at the given error correction level.
+func Encode(text string, level Level) (*Code, error) {
+ // Pick data encoding, smallest first.
+ // We could split the string and use different encodings
+ // but that seems like overkill for now.
+ var enc coding.Encoding
+ switch {
+ case coding.Num(text).Check() == nil:
+ enc = coding.Num(text)
+ case coding.Alpha(text).Check() == nil:
+ enc = coding.Alpha(text)
+ default:
+ enc = coding.String(text)
+ }
+
+ // Pick size.
+ l := coding.Level(level)
+ var v coding.Version
+ for v = coding.MinVersion; ; v++ {
+ if v > coding.MaxVersion {
+ return nil, errors.New("text too long to encode as QR")
+ }
+ if enc.Bits(v) <= v.DataBytes(l)*8 {
+ break
+ }
+ }
+
+ // Build and execute plan.
+ p, err := coding.NewPlan(v, l, 0)
+ if err != nil {
+ return nil, err
+ }
+ cc, err := p.Encode(enc)
+ if err != nil {
+ return nil, err
+ }
+
+ // TODO: Pick appropriate mask.
+
+ return &Code{cc.Bitmap, cc.Size, cc.Stride, 8}, nil
+}
+
+// A Code is a square pixel grid.
+// It implements image.Image and direct PNG encoding.
+type Code struct {
+ Bitmap []byte // 1 is black, 0 is white
+ Size int // number of pixels on a side
+ Stride int // number of bytes per row
+ Scale int // number of image pixels per QR pixel
+}
+
+// Black returns true if the pixel at (x,y) is black.
+func (c *Code) Black(x, y int) bool {
+ return 0 <= x && x < c.Size && 0 <= y && y < c.Size &&
+ c.Bitmap[y*c.Stride+x/8]&(1<<uint(7-x&7)) != 0
+}
+
+// Image returns an Image displaying the code.
+func (c *Code) Image() image.Image {
+ return &codeImage{c}
+
+}
+
+// codeImage implements image.Image
+type codeImage struct {
+ *Code
+}
+
+var (
+ whiteColor color.Color = color.Gray{0xFF}
+ blackColor color.Color = color.Gray{0x00}
+)
+
+func (c *codeImage) Bounds() image.Rectangle {
+ d := (c.Size + 8) * c.Scale
+ return image.Rect(0, 0, d, d)
+}
+
+func (c *codeImage) At(x, y int) color.Color {
+ if c.Black(x, y) {
+ return blackColor
+ }
+ return whiteColor
+}
+
+func (c *codeImage) ColorModel() color.Model {
+ return color.GrayModel
+}
diff --git a/vendor/github.com/mattermost/rsc/tmp/LICENSE b/vendor/github.com/mattermost/rsc/tmp/LICENSE
new file mode 120000
index 000000000..ea5b60640
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/tmp/LICENSE
@@ -0,0 +1 @@
+../LICENSE \ No newline at end of file
diff --git a/vendor/github.com/mssola/user_agent/.travis.yml b/vendor/github.com/mssola/user_agent/.travis.yml
new file mode 100644
index 000000000..33c596acb
--- /dev/null
+++ b/vendor/github.com/mssola/user_agent/.travis.yml
@@ -0,0 +1,12 @@
+language: go
+go:
+ - 1.0
+ - 1.1
+ - 1.2
+ - 1.3
+ - 1.4
+ - 1.5
+ - tip
+matrix:
+ allow_failures:
+ - go: tip
diff --git a/vendor/github.com/mssola/user_agent/LICENSE b/vendor/github.com/mssola/user_agent/LICENSE
new file mode 100644
index 000000000..2784cdcac
--- /dev/null
+++ b/vendor/github.com/mssola/user_agent/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2012-2016 Miquel Sabaté Solà
+
+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/github.com/mssola/user_agent/README.md b/vendor/github.com/mssola/user_agent/README.md
new file mode 100644
index 000000000..5c5e77276
--- /dev/null
+++ b/vendor/github.com/mssola/user_agent/README.md
@@ -0,0 +1,51 @@
+
+# UserAgent [![Build Status](https://travis-ci.org/mssola/user_agent.png?branch=master)](https://travis-ci.org/mssola/user_agent) [![GoDoc](https://godoc.org/github.com/mssola/user_agent?status.png)](http://godoc.org/github.com/mssola/user_agent)
+
+
+UserAgent is a Go library that parses HTTP User Agents.
+
+## Usage
+
+~~~ go
+package main
+
+import (
+ "fmt"
+
+ "github.com/mssola/user_agent"
+)
+
+func main() {
+ // The "New" function will create a new UserAgent object and it will parse
+ // the given string. If you need to parse more strings, you can re-use
+ // this object and call: ua.Parse("another string")
+ ua := user_agent.New("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11");
+
+ fmt.Printf("%v\n", ua.Mobile()) // => false
+ fmt.Printf("%v\n", ua.Bot()) // => false
+ fmt.Printf("%v\n", ua.Mozilla()) // => "5.0"
+
+ fmt.Printf("%v\n", ua.Platform()) // => "X11"
+ fmt.Printf("%v\n", ua.OS()) // => "Linux x86_64"
+
+ name, version := ua.Engine()
+ fmt.Printf("%v\n", name) // => "AppleWebKit"
+ fmt.Printf("%v\n", version) // => "537.11"
+
+ name, version = ua.Browser()
+ fmt.Printf("%v\n", name) // => "Chrome"
+ fmt.Printf("%v\n", version) // => "23.0.1271.97"
+
+ // Let's see an example with a bot.
+
+ ua.Parse("Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)")
+
+ fmt.Printf("%v\n", ua.Bot()) // => true
+
+ name, version = ua.Browser()
+ fmt.Printf("%v\n", name) // => Googlebot
+ fmt.Printf("%v\n", version) // => 2.1
+}
+~~~
+
+Copyright &copy; 2012-2016 Miquel Sabaté Solà, released under the MIT License.
diff --git a/vendor/github.com/mssola/user_agent/bot.go b/vendor/github.com/mssola/user_agent/bot.go
new file mode 100644
index 000000000..2b118d661
--- /dev/null
+++ b/vendor/github.com/mssola/user_agent/bot.go
@@ -0,0 +1,123 @@
+// Copyright (C) 2014-2016 Miquel Sabaté Solà <mikisabate@gmail.com>
+// This file is licensed under the MIT license.
+// See the LICENSE file.
+
+package user_agent
+
+import (
+ "regexp"
+ "strings"
+)
+
+var botFromSiteRegexp = regexp.MustCompile("http://.+\\.\\w+")
+
+// Get the name of the bot from the website that may be in the given comment. If
+// there is no website in the comment, then an empty string is returned.
+func getFromSite(comment []string) string {
+ if len(comment) == 0 {
+ return ""
+ }
+
+ // Where we should check the website.
+ idx := 2
+ if len(comment) < 3 {
+ idx = 0
+ }
+
+ // Pick the site.
+ results := botFromSiteRegexp.FindStringSubmatch(comment[idx])
+ if len(results) == 1 {
+ // If it's a simple comment, just return the name of the site.
+ if idx == 0 {
+ return results[0]
+ }
+
+ // This is a large comment, usually the name will be in the previous
+ // field of the comment.
+ return strings.TrimSpace(comment[1])
+ }
+ return ""
+}
+
+// Returns true if the info that we currently have corresponds to the Google
+// mobile bot. This function also modifies some attributes in the receiver
+// accordingly.
+func (p *UserAgent) googleBot() bool {
+ // This is a hackish way to detect Google's mobile bot.
+ if strings.Index(p.ua, "Googlebot") != -1 {
+ p.platform = ""
+ p.undecided = true
+ }
+ return p.undecided
+}
+
+// Set the attributes of the receiver as given by the parameters. All the other
+// parameters are set to empty.
+func (p *UserAgent) setSimple(name, version string, bot bool) {
+ p.bot = bot
+ if !bot {
+ p.mozilla = ""
+ }
+ p.browser.Name = name
+ p.browser.Version = version
+ p.browser.Engine = ""
+ p.browser.EngineVersion = ""
+ p.os = ""
+ p.localization = ""
+}
+
+// Fix some values for some weird browsers.
+func (p *UserAgent) fixOther(sections []section) {
+ if len(sections) > 0 {
+ p.browser.Name = sections[0].name
+ p.browser.Version = sections[0].version
+ p.mozilla = ""
+ }
+}
+
+var botRegex = regexp.MustCompile("(?i)(bot|crawler|sp(i|y)der|search|worm|fetch|nutch)")
+
+// Check if we're dealing with a bot or with some weird browser. If that is the
+// case, the receiver will be modified accordingly.
+func (p *UserAgent) checkBot(sections []section) {
+ // If there's only one element, and it's doesn't have the Mozilla string,
+ // check whether this is a bot or not.
+ if len(sections) == 1 && sections[0].name != "Mozilla" {
+ p.mozilla = ""
+
+ // Check whether the name has some suspicious "bot" or "crawler" in his name.
+ if botRegex.Match([]byte(sections[0].name)) {
+ p.setSimple(sections[0].name, "", true)
+ return
+ }
+
+ // Tough luck, let's try to see if it has a website in his comment.
+ if name := getFromSite(sections[0].comment); name != "" {
+ // First of all, this is a bot. Moreover, since it doesn't have the
+ // Mozilla string, we can assume that the name and the version are
+ // the ones from the first section.
+ p.setSimple(sections[0].name, sections[0].version, true)
+ return
+ }
+
+ // At this point we are sure that this is not a bot, but some weirdo.
+ p.setSimple(sections[0].name, sections[0].version, false)
+ } else {
+ // Let's iterate over the available comments and check for a website.
+ for _, v := range sections {
+ if name := getFromSite(v.comment); name != "" {
+ // Ok, we've got a bot name.
+ results := strings.SplitN(name, "/", 2)
+ version := ""
+ if len(results) == 2 {
+ version = results[1]
+ }
+ p.setSimple(results[0], version, true)
+ return
+ }
+ }
+
+ // We will assume that this is some other weird browser.
+ p.fixOther(sections)
+ }
+}
diff --git a/vendor/github.com/mssola/user_agent/browser.go b/vendor/github.com/mssola/user_agent/browser.go
new file mode 100644
index 000000000..17a243c36
--- /dev/null
+++ b/vendor/github.com/mssola/user_agent/browser.go
@@ -0,0 +1,132 @@
+// Copyright (C) 2012-2016 Miquel Sabaté Solà <mikisabate@gmail.com>
+// This file is licensed under the MIT license.
+// See the LICENSE file.
+
+package user_agent
+
+import (
+ "regexp"
+ "strings"
+)
+
+var ie11Regexp = regexp.MustCompile("^rv:(.+)$")
+
+// A struct containing all the information that we might be
+// interested from the browser.
+type Browser struct {
+ // The name of the browser's engine.
+ Engine string
+
+ // The version of the browser's engine.
+ EngineVersion string
+
+ // The name of the browser.
+ Name string
+
+ // The version of the browser.
+ Version string
+}
+
+// Extract all the information that we can get from the User-Agent string
+// about the browser and update the receiver with this information.
+//
+// The function receives just one argument "sections", that contains the
+// sections from the User-Agent string after being parsed.
+func (p *UserAgent) detectBrowser(sections []section) {
+ slen := len(sections)
+
+ if sections[0].name == "Opera" {
+ p.browser.Name = "Opera"
+ p.browser.Version = sections[0].version
+ p.browser.Engine = "Presto"
+ if slen > 1 {
+ p.browser.EngineVersion = sections[1].version
+ }
+ } else if sections[0].name == "Dalvik" {
+ // When Dalvik VM is in use, there is no browser info attached to ua.
+ // Although browser is still a Mozilla/5.0 compatible.
+ p.mozilla = "5.0"
+ } else if slen > 1 {
+ engine := sections[1]
+ p.browser.Engine = engine.name
+ p.browser.EngineVersion = engine.version
+ if slen > 2 {
+ p.browser.Version = sections[2].version
+ if engine.name == "AppleWebKit" {
+ switch sections[slen-1].name {
+ case "Edge":
+ p.browser.Name = "Edge"
+ p.browser.Version = sections[slen-1].version
+ p.browser.Engine = "EdgeHTML"
+ p.browser.EngineVersion = ""
+ case "OPR":
+ p.browser.Name = "Opera"
+ p.browser.Version = sections[slen-1].version
+ default:
+ if sections[2].name == "Chrome" {
+ p.browser.Name = "Chrome"
+ } else {
+ p.browser.Name = "Safari"
+ }
+ }
+ } else if engine.name == "Gecko" {
+ name := sections[2].name
+ if name == "MRA" && slen > 4 {
+ name = sections[4].name
+ p.browser.Version = sections[4].version
+ }
+ p.browser.Name = name
+ } else if engine.name == "like" && sections[2].name == "Gecko" {
+ // This is the new user agent from Internet Explorer 11.
+ p.browser.Engine = "Trident"
+ p.browser.Name = "Internet Explorer"
+ for _, c := range sections[0].comment {
+ version := ie11Regexp.FindStringSubmatch(c)
+ if len(version) > 0 {
+ p.browser.Version = version[1]
+ return
+ }
+ }
+ p.browser.Version = ""
+ }
+ }
+ } else if slen == 1 && len(sections[0].comment) > 1 {
+ comment := sections[0].comment
+ if comment[0] == "compatible" && strings.HasPrefix(comment[1], "MSIE") {
+ p.browser.Engine = "Trident"
+ p.browser.Name = "Internet Explorer"
+ // The MSIE version may be reported as the compatibility version.
+ // For IE 8 through 10, the Trident token is more accurate.
+ // http://msdn.microsoft.com/en-us/library/ie/ms537503(v=vs.85).aspx#VerToken
+ for _, v := range comment {
+ if strings.HasPrefix(v, "Trident/") {
+ switch v[8:] {
+ case "4.0":
+ p.browser.Version = "8.0"
+ case "5.0":
+ p.browser.Version = "9.0"
+ case "6.0":
+ p.browser.Version = "10.0"
+ }
+ break
+ }
+ }
+ // If the Trident token is not provided, fall back to MSIE token.
+ if p.browser.Version == "" {
+ p.browser.Version = strings.TrimSpace(comment[1][4:])
+ }
+ }
+ }
+}
+
+// Returns two strings. The first string is the name of the engine and the
+// second one is the version of the engine.
+func (p *UserAgent) Engine() (string, string) {
+ return p.browser.Engine, p.browser.EngineVersion
+}
+
+// Returns two strings. The first string is the name of the browser and the
+// second one is the version of the browser.
+func (p *UserAgent) Browser() (string, string) {
+ return p.browser.Name, p.browser.Version
+}
diff --git a/vendor/github.com/mssola/user_agent/operating_systems.go b/vendor/github.com/mssola/user_agent/operating_systems.go
new file mode 100644
index 000000000..c4720a75a
--- /dev/null
+++ b/vendor/github.com/mssola/user_agent/operating_systems.go
@@ -0,0 +1,281 @@
+// Copyright (C) 2012-2016 Miquel Sabaté Solà <mikisabate@gmail.com>
+// This file is licensed under the MIT license.
+// See the LICENSE file.
+
+package user_agent
+
+import "strings"
+
+// Normalize the name of the operating system. By now, this just
+// affects to Windows NT.
+//
+// Returns a string containing the normalized name for the Operating System.
+func normalizeOS(name string) string {
+ sp := strings.SplitN(name, " ", 3)
+ if len(sp) != 3 || sp[1] != "NT" {
+ return name
+ }
+
+ switch sp[2] {
+ case "5.0":
+ return "Windows 2000"
+ case "5.01":
+ return "Windows 2000, Service Pack 1 (SP1)"
+ case "5.1":
+ return "Windows XP"
+ case "5.2":
+ return "Windows XP x64 Edition"
+ case "6.0":
+ return "Windows Vista"
+ case "6.1":
+ return "Windows 7"
+ case "6.2":
+ return "Windows 8"
+ case "6.3":
+ return "Windows 8.1"
+ case "10.0":
+ return "Windows 10"
+ }
+ return name
+}
+
+// Guess the OS, the localization and if this is a mobile device for a
+// Webkit-powered browser.
+//
+// The first argument p is a reference to the current UserAgent and the second
+// argument is a slice of strings containing the comment.
+func webkit(p *UserAgent, comment []string) {
+ if p.platform == "webOS" {
+ p.browser.Name = p.platform
+ p.os = "Palm"
+ if len(comment) > 2 {
+ p.localization = comment[2]
+ }
+ p.mobile = true
+ } else if p.platform == "Symbian" {
+ p.mobile = true
+ p.browser.Name = p.platform
+ p.os = comment[0]
+ } else if p.platform == "Linux" {
+ p.mobile = true
+ if p.browser.Name == "Safari" {
+ p.browser.Name = "Android"
+ }
+ if len(comment) > 1 {
+ if comment[1] == "U" {
+ if len(comment) > 2 {
+ p.os = comment[2]
+ } else {
+ p.mobile = false
+ p.os = comment[0]
+ }
+ } else {
+ p.os = comment[1]
+ }
+ }
+ if len(comment) > 3 {
+ p.localization = comment[3]
+ }
+ } else if len(comment) > 0 {
+ if len(comment) > 3 {
+ p.localization = comment[3]
+ }
+ if strings.HasPrefix(comment[0], "Windows NT") {
+ p.os = normalizeOS(comment[0])
+ } else if len(comment) < 2 {
+ p.localization = comment[0]
+ } else if len(comment) < 3 {
+ if !p.googleBot() {
+ p.os = normalizeOS(comment[1])
+ }
+ } else {
+ p.os = normalizeOS(comment[2])
+ }
+ if p.platform == "BlackBerry" {
+ p.browser.Name = p.platform
+ if p.os == "Touch" {
+ p.os = p.platform
+ }
+ }
+ }
+}
+
+// Guess the OS, the localization and if this is a mobile device
+// for a Gecko-powered browser.
+//
+// The first argument p is a reference to the current UserAgent and the second
+// argument is a slice of strings containing the comment.
+func gecko(p *UserAgent, comment []string) {
+ if len(comment) > 1 {
+ if comment[1] == "U" {
+ if len(comment) > 2 {
+ p.os = normalizeOS(comment[2])
+ } else {
+ p.os = normalizeOS(comment[1])
+ }
+ } else {
+ if p.platform == "Android" {
+ p.mobile = true
+ p.platform, p.os = normalizeOS(comment[1]), p.platform
+ } else if comment[0] == "Mobile" || comment[0] == "Tablet" {
+ p.mobile = true
+ p.os = "FirefoxOS"
+ } else {
+ if p.os == "" {
+ p.os = normalizeOS(comment[1])
+ }
+ }
+ }
+ if len(comment) > 3 {
+ p.localization = comment[3]
+ }
+ }
+}
+
+// Guess the OS, the localization and if this is a mobile device
+// for Internet Explorer.
+//
+// The first argument p is a reference to the current UserAgent and the second
+// argument is a slice of strings containing the comment.
+func trident(p *UserAgent, comment []string) {
+ // Internet Explorer only runs on Windows.
+ p.platform = "Windows"
+
+ // The OS can be set before to handle a new case in IE11.
+ if p.os == "" {
+ if len(comment) > 2 {
+ p.os = normalizeOS(comment[2])
+ } else {
+ p.os = "Windows NT 4.0"
+ }
+ }
+
+ // Last but not least, let's detect if it comes from a mobile device.
+ for _, v := range comment {
+ if strings.HasPrefix(v, "IEMobile") {
+ p.mobile = true
+ return
+ }
+ }
+}
+
+// Guess the OS, the localization and if this is a mobile device
+// for Opera.
+//
+// The first argument p is a reference to the current UserAgent and the second
+// argument is a slice of strings containing the comment.
+func opera(p *UserAgent, comment []string) {
+ slen := len(comment)
+
+ if strings.HasPrefix(comment[0], "Windows") {
+ p.platform = "Windows"
+ p.os = normalizeOS(comment[0])
+ if slen > 2 {
+ if slen > 3 && strings.HasPrefix(comment[2], "MRA") {
+ p.localization = comment[3]
+ } else {
+ p.localization = comment[2]
+ }
+ }
+ } else {
+ if strings.HasPrefix(comment[0], "Android") {
+ p.mobile = true
+ }
+ p.platform = comment[0]
+ if slen > 1 {
+ p.os = comment[1]
+ if slen > 3 {
+ p.localization = comment[3]
+ }
+ } else {
+ p.os = comment[0]
+ }
+ }
+}
+
+// Guess the OS. Android browsers send Dalvik as the user agent in the
+// request header.
+//
+// The first argument p is a reference to the current UserAgent and the second
+// argument is a slice of strings containing the comment.
+func dalvik(p *UserAgent, comment []string) {
+ slen := len(comment)
+
+ if strings.HasPrefix(comment[0], "Linux") {
+ p.platform = comment[0]
+ if slen > 2 {
+ p.os = comment[2]
+ }
+ p.mobile = true
+ }
+}
+
+// Given the comment of the first section of the UserAgent string,
+// get the platform.
+func getPlatform(comment []string) string {
+ if len(comment) > 0 {
+ if comment[0] != "compatible" {
+ if strings.HasPrefix(comment[0], "Windows") {
+ return "Windows"
+ } else if strings.HasPrefix(comment[0], "Symbian") {
+ return "Symbian"
+ } else if strings.HasPrefix(comment[0], "webOS") {
+ return "webOS"
+ } else if comment[0] == "BB10" {
+ return "BlackBerry"
+ }
+ return comment[0]
+ }
+ }
+ return ""
+}
+
+// Detect some properties of the OS from the given section.
+func (p *UserAgent) detectOS(s section) {
+ if s.name == "Mozilla" {
+ // Get the platform here. Be aware that IE11 provides a new format
+ // that is not backwards-compatible with previous versions of IE.
+ p.platform = getPlatform(s.comment)
+ if p.platform == "Windows" && len(s.comment) > 0 {
+ p.os = normalizeOS(s.comment[0])
+ }
+
+ // And finally get the OS depending on the engine.
+ switch p.browser.Engine {
+ case "":
+ p.undecided = true
+ case "Gecko":
+ gecko(p, s.comment)
+ case "AppleWebKit":
+ webkit(p, s.comment)
+ case "Trident":
+ trident(p, s.comment)
+ }
+ } else if s.name == "Opera" {
+ if len(s.comment) > 0 {
+ opera(p, s.comment)
+ }
+ } else if s.name == "Dalvik" {
+ if len(s.comment) > 0 {
+ dalvik(p, s.comment)
+ }
+ } else {
+ // Check whether this is a bot or just a weird browser.
+ p.undecided = true
+ }
+}
+
+// Returns a string containing the platform..
+func (p *UserAgent) Platform() string {
+ return p.platform
+}
+
+// Returns a string containing the name of the Operating System.
+func (p *UserAgent) OS() string {
+ return p.os
+}
+
+// Returns a string containing the localization.
+func (p *UserAgent) Localization() string {
+ return p.localization
+}
diff --git a/vendor/github.com/mssola/user_agent/user_agent.go b/vendor/github.com/mssola/user_agent/user_agent.go
new file mode 100644
index 000000000..18a673eef
--- /dev/null
+++ b/vendor/github.com/mssola/user_agent/user_agent.go
@@ -0,0 +1,174 @@
+// Copyright (C) 2012-2016 Miquel Sabaté Solà <mikisabate@gmail.com>
+// This file is licensed under the MIT license.
+// See the LICENSE file.
+
+// Package user_agent implements an HTTP User Agent string parser. It defines
+// the type UserAgent that contains all the information from the parsed string.
+// It also implements the Parse function and getters for all the relevant
+// information that has been extracted from a parsed User Agent string.
+package user_agent
+
+import "strings"
+
+// A section contains the name of the product, its version and
+// an optional comment.
+type section struct {
+ name string
+ version string
+ comment []string
+}
+
+// The UserAgent struct contains all the info that can be extracted
+// from the User-Agent string.
+type UserAgent struct {
+ ua string
+ mozilla string
+ platform string
+ os string
+ localization string
+ browser Browser
+ bot bool
+ mobile bool
+ undecided bool
+}
+
+// Read from the given string until the given delimiter or the
+// end of the string have been reached.
+//
+// The first argument is the user agent string being parsed. The second
+// argument is a reference pointing to the current index of the user agent
+// string. The delimiter argument specifies which character is the delimiter
+// and the cat argument determines whether nested '(' should be ignored or not.
+//
+// Returns an array of bytes containing what has been read.
+func readUntil(ua string, index *int, delimiter byte, cat bool) []byte {
+ var buffer []byte
+
+ i := *index
+ catalan := 0
+ for ; i < len(ua); i = i + 1 {
+ if ua[i] == delimiter {
+ if catalan == 0 {
+ *index = i + 1
+ return buffer
+ }
+ catalan--
+ } else if cat && ua[i] == '(' {
+ catalan++
+ }
+ buffer = append(buffer, ua[i])
+ }
+ *index = i + 1
+ return buffer
+}
+
+// Parse the given product, that is, just a name or a string
+// formatted as Name/Version.
+//
+// It returns two strings. The first string is the name of the product and the
+// second string contains the version of the product.
+func parseProduct(product []byte) (string, string) {
+ prod := strings.SplitN(string(product), "/", 2)
+ if len(prod) == 2 {
+ return prod[0], prod[1]
+ }
+ return string(product), ""
+}
+
+// Parse a section. A section is typically formatted as follows
+// "Name/Version (comment)". Both, the comment and the version are optional.
+//
+// The first argument is the user agent string being parsed. The second
+// argument is a reference pointing to the current index of the user agent
+// string.
+//
+// Returns a section containing the information that we could extract
+// from the last parsed section.
+func parseSection(ua string, index *int) (s section) {
+ buffer := readUntil(ua, index, ' ', false)
+
+ s.name, s.version = parseProduct(buffer)
+ if *index < len(ua) && ua[*index] == '(' {
+ *index++
+ buffer = readUntil(ua, index, ')', true)
+ s.comment = strings.Split(string(buffer), "; ")
+ *index++
+ }
+ return s
+}
+
+// Initialize the parser.
+func (p *UserAgent) initialize() {
+ p.ua = ""
+ p.mozilla = ""
+ p.platform = ""
+ p.os = ""
+ p.localization = ""
+ p.browser.Engine = ""
+ p.browser.EngineVersion = ""
+ p.browser.Name = ""
+ p.browser.Version = ""
+ p.bot = false
+ p.mobile = false
+ p.undecided = false
+}
+
+// Parse the given User-Agent string and get the resulting UserAgent object.
+//
+// Returns an UserAgent object that has been initialized after parsing
+// the given User-Agent string.
+func New(ua string) *UserAgent {
+ o := &UserAgent{}
+ o.Parse(ua)
+ return o
+}
+
+// Parse the given User-Agent string. After calling this function, the
+// receiver will be setted up with all the information that we've extracted.
+func (p *UserAgent) Parse(ua string) {
+ var sections []section
+
+ p.initialize()
+ p.ua = ua
+ for index, limit := 0, len(ua); index < limit; {
+ s := parseSection(ua, &index)
+ if !p.mobile && s.name == "Mobile" {
+ p.mobile = true
+ }
+ sections = append(sections, s)
+ }
+
+ if len(sections) > 0 {
+ if sections[0].name == "Mozilla" {
+ p.mozilla = sections[0].version
+ }
+
+ p.detectBrowser(sections)
+ p.detectOS(sections[0])
+
+ if p.undecided {
+ p.checkBot(sections)
+ }
+ }
+}
+
+// Returns the mozilla version (it's how the User Agent string begins:
+// "Mozilla/5.0 ...", unless we're dealing with Opera, of course).
+func (p *UserAgent) Mozilla() string {
+ return p.mozilla
+}
+
+// Returns true if it's a bot, false otherwise.
+func (p *UserAgent) Bot() bool {
+ return p.bot
+}
+
+// Returns true if it's a mobile device, false otherwise.
+func (p *UserAgent) Mobile() bool {
+ return p.mobile
+}
+
+// Returns the original given user agent.
+func (p *UserAgent) UA() string {
+ return p.ua
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/LICENSE b/vendor/github.com/nicksnyder/go-i18n/LICENSE
new file mode 100644
index 000000000..609cce797
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Nick Snyder https://github.com/nicksnyder
+
+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/github.com/nicksnyder/go-i18n/i18n/bundle/bundle.go b/vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle.go
new file mode 100644
index 000000000..e93db95d7
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle.go
@@ -0,0 +1,315 @@
+// Package bundle manages translations for multiple languages.
+package bundle
+
+import (
+ "encoding/json"
+ "fmt"
+ "gopkg.in/yaml.v2"
+ "io/ioutil"
+ "reflect"
+
+ "path/filepath"
+
+ "github.com/nicksnyder/go-i18n/i18n/language"
+ "github.com/nicksnyder/go-i18n/i18n/translation"
+)
+
+// TranslateFunc is a copy of i18n.TranslateFunc to avoid a circular dependency.
+type TranslateFunc func(translationID string, args ...interface{}) string
+
+// Bundle stores the translations for multiple languages.
+type Bundle struct {
+ // The primary translations for a language tag and translation id.
+ translations map[string]map[string]translation.Translation
+
+ // Translations that can be used when an exact language match is not possible.
+ fallbackTranslations map[string]map[string]translation.Translation
+}
+
+// New returns an empty bundle.
+func New() *Bundle {
+ return &Bundle{
+ translations: make(map[string]map[string]translation.Translation),
+ fallbackTranslations: make(map[string]map[string]translation.Translation),
+ }
+}
+
+// MustLoadTranslationFile is similar to LoadTranslationFile
+// except it panics if an error happens.
+func (b *Bundle) MustLoadTranslationFile(filename string) {
+ if err := b.LoadTranslationFile(filename); err != nil {
+ panic(err)
+ }
+}
+
+// LoadTranslationFile loads the translations from filename into memory.
+//
+// The language that the translations are associated with is parsed from the filename (e.g. en-US.json).
+//
+// Generally you should load translation files once during your program's initialization.
+func (b *Bundle) LoadTranslationFile(filename string) error {
+ buf, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return err
+ }
+ return b.ParseTranslationFileBytes(filename, buf)
+}
+
+// ParseTranslationFileBytes is similar to LoadTranslationFile except it parses the bytes in buf.
+//
+// It is useful for parsing translation files embedded with go-bindata.
+func (b *Bundle) ParseTranslationFileBytes(filename string, buf []byte) error {
+ basename := filepath.Base(filename)
+ langs := language.Parse(basename)
+ switch l := len(langs); {
+ case l == 0:
+ return fmt.Errorf("no language found in %q", basename)
+ case l > 1:
+ return fmt.Errorf("multiple languages found in filename %q: %v; expected one", basename, langs)
+ }
+ translations, err := parseTranslations(filename, buf)
+ if err != nil {
+ return err
+ }
+ b.AddTranslation(langs[0], translations...)
+ return nil
+}
+
+func parseTranslations(filename string, buf []byte) ([]translation.Translation, error) {
+ var unmarshalFunc func([]byte, interface{}) error
+ switch format := filepath.Ext(filename); format {
+ case ".json":
+ unmarshalFunc = json.Unmarshal
+ case ".yaml":
+ unmarshalFunc = yaml.Unmarshal
+ default:
+ return nil, fmt.Errorf("unsupported file extension %s", format)
+ }
+
+ var translationsData []map[string]interface{}
+ if len(buf) > 0 {
+ if err := unmarshalFunc(buf, &translationsData); err != nil {
+ return nil, err
+ }
+ }
+
+ translations := make([]translation.Translation, 0, len(translationsData))
+ for i, translationData := range translationsData {
+ t, err := translation.NewTranslation(translationData)
+ if err != nil {
+ return nil, fmt.Errorf("unable to parse translation #%d in %s because %s\n%v", i, filename, err, translationData)
+ }
+ translations = append(translations, t)
+ }
+ return translations, nil
+}
+
+// AddTranslation adds translations for a language.
+//
+// It is useful if your translations are in a format not supported by LoadTranslationFile.
+func (b *Bundle) AddTranslation(lang *language.Language, translations ...translation.Translation) {
+ if b.translations[lang.Tag] == nil {
+ b.translations[lang.Tag] = make(map[string]translation.Translation, len(translations))
+ }
+ currentTranslations := b.translations[lang.Tag]
+ for _, newTranslation := range translations {
+ if currentTranslation := currentTranslations[newTranslation.ID()]; currentTranslation != nil {
+ currentTranslations[newTranslation.ID()] = currentTranslation.Merge(newTranslation)
+ } else {
+ currentTranslations[newTranslation.ID()] = newTranslation
+ }
+ }
+
+ // lang can provide translations for less specific language tags.
+ for _, tag := range lang.MatchingTags() {
+ b.fallbackTranslations[tag] = currentTranslations
+ }
+}
+
+// Translations returns all translations in the bundle.
+func (b *Bundle) Translations() map[string]map[string]translation.Translation {
+ return b.translations
+}
+
+// LanguageTags returns the tags of all languages that that have been added.
+func (b *Bundle) LanguageTags() []string {
+ var tags []string
+ for k := range b.translations {
+ tags = append(tags, k)
+ }
+ return tags
+}
+
+// LanguageTranslationIDs returns the ids of all translations that have been added for a given language.
+func (b *Bundle) LanguageTranslationIDs(languageTag string) []string {
+ var ids []string
+ for id := range b.translations[languageTag] {
+ ids = append(ids, id)
+ }
+ return ids
+}
+
+// MustTfunc is similar to Tfunc except it panics if an error happens.
+func (b *Bundle) MustTfunc(pref string, prefs ...string) TranslateFunc {
+ tfunc, err := b.Tfunc(pref, prefs...)
+ if err != nil {
+ panic(err)
+ }
+ return tfunc
+}
+
+// MustTfuncAndLanguage is similar to TfuncAndLanguage except it panics if an error happens.
+func (b *Bundle) MustTfuncAndLanguage(pref string, prefs ...string) (TranslateFunc, *language.Language) {
+ tfunc, language, err := b.TfuncAndLanguage(pref, prefs...)
+ if err != nil {
+ panic(err)
+ }
+ return tfunc, language
+}
+
+// Tfunc is similar to TfuncAndLanguage except is doesn't return the Language.
+func (b *Bundle) Tfunc(pref string, prefs ...string) (TranslateFunc, error) {
+ tfunc, _, err := b.TfuncAndLanguage(pref, prefs...)
+ return tfunc, err
+}
+
+// TfuncAndLanguage returns a TranslateFunc for the first Language that
+// has a non-zero number of translations in the bundle.
+//
+// The returned Language matches the the first language preference that could be satisfied,
+// but this may not strictly match the language of the translations used to satisfy that preference.
+//
+// For example, the user may request "zh". If there are no translations for "zh" but there are translations
+// for "zh-cn", then the translations for "zh-cn" will be used but the returned Language will be "zh".
+//
+// It can parse languages from Accept-Language headers (RFC 2616),
+// but it assumes weights are monotonically decreasing.
+func (b *Bundle) TfuncAndLanguage(pref string, prefs ...string) (TranslateFunc, *language.Language, error) {
+ lang := b.supportedLanguage(pref, prefs...)
+ var err error
+ if lang == nil {
+ err = fmt.Errorf("no supported languages found %#v", append(prefs, pref))
+ }
+ return func(translationID string, args ...interface{}) string {
+ return b.translate(lang, translationID, args...)
+ }, lang, err
+}
+
+// supportedLanguage returns the first language which
+// has a non-zero number of translations in the bundle.
+func (b *Bundle) supportedLanguage(pref string, prefs ...string) *language.Language {
+ lang := b.translatedLanguage(pref)
+ if lang == nil {
+ for _, pref := range prefs {
+ lang = b.translatedLanguage(pref)
+ if lang != nil {
+ break
+ }
+ }
+ }
+ return lang
+}
+
+func (b *Bundle) translatedLanguage(src string) *language.Language {
+ langs := language.Parse(src)
+ for _, lang := range langs {
+ if len(b.translations[lang.Tag]) > 0 ||
+ len(b.fallbackTranslations[lang.Tag]) > 0 {
+ return lang
+ }
+ }
+ return nil
+}
+
+func (b *Bundle) translate(lang *language.Language, translationID string, args ...interface{}) string {
+ if lang == nil {
+ return translationID
+ }
+
+ translations := b.translations[lang.Tag]
+ if translations == nil {
+ translations = b.fallbackTranslations[lang.Tag]
+ if translations == nil {
+ return translationID
+ }
+ }
+
+ translation := translations[translationID]
+ if translation == nil {
+ return translationID
+ }
+
+ var data interface{}
+ var count interface{}
+ if argc := len(args); argc > 0 {
+ if isNumber(args[0]) {
+ count = args[0]
+ if argc > 1 {
+ data = args[1]
+ }
+ } else {
+ data = args[0]
+ }
+ }
+
+ if count != nil {
+ if data == nil {
+ data = map[string]interface{}{"Count": count}
+ } else {
+ dataMap := toMap(data)
+ dataMap["Count"] = count
+ data = dataMap
+ }
+ }
+
+ p, _ := lang.Plural(count)
+ template := translation.Template(p)
+ if template == nil {
+ return translationID
+ }
+
+ s := template.Execute(data)
+ if s == "" {
+ return translationID
+ }
+ return s
+}
+
+func isNumber(n interface{}) bool {
+ switch n.(type) {
+ case int, int8, int16, int32, int64, string:
+ return true
+ }
+ return false
+}
+
+func toMap(input interface{}) map[string]interface{} {
+ if data, ok := input.(map[string]interface{}); ok {
+ return data
+ }
+ v := reflect.ValueOf(input)
+ switch v.Kind() {
+ case reflect.Ptr:
+ return toMap(v.Elem().Interface())
+ case reflect.Struct:
+ return structToMap(v)
+ default:
+ return nil
+ }
+}
+
+// Converts the top level of a struct to a map[string]interface{}.
+// Code inspired by github.com/fatih/structs.
+func structToMap(v reflect.Value) map[string]interface{} {
+ out := make(map[string]interface{})
+ t := v.Type()
+ for i := 0; i < t.NumField(); i++ {
+ field := t.Field(i)
+ if field.PkgPath != "" {
+ // unexported field. skip.
+ continue
+ }
+ out[field.Name] = v.FieldByName(field.Name).Interface()
+ }
+ return out
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/i18n.go b/vendor/github.com/nicksnyder/go-i18n/i18n/i18n.go
new file mode 100644
index 000000000..f96842966
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/i18n.go
@@ -0,0 +1,152 @@
+// Package i18n supports string translations with variable substitution and CLDR pluralization.
+// It is intended to be used in conjunction with the goi18n command, although that is not strictly required.
+//
+// Initialization
+//
+// Your Go program should load translations during its initialization.
+// i18n.MustLoadTranslationFile("path/to/fr-FR.all.json")
+// If your translations are in a file format not supported by (Must)?LoadTranslationFile,
+// then you can use the AddTranslation function to manually add translations.
+//
+// Fetching a translation
+//
+// Use Tfunc or MustTfunc to fetch a TranslateFunc that will return the translated string for a specific language.
+// func handleRequest(w http.ResponseWriter, r *http.Request) {
+// cookieLang := r.Cookie("lang")
+// acceptLang := r.Header.Get("Accept-Language")
+// defaultLang = "en-US" // known valid language
+// T, err := i18n.Tfunc(cookieLang, acceptLang, defaultLang)
+// fmt.Println(T("Hello world"))
+// }
+//
+// Usually it is a good idea to identify strings by a generic id rather than the English translation,
+// but the rest of this documentation will continue to use the English translation for readability.
+// T("Hello world") // ok
+// T("programGreeting") // better!
+//
+// Variables
+//
+// TranslateFunc supports strings that have variables using the text/template syntax.
+// T("Hello {{.Person}}", map[string]interface{}{
+// "Person": "Bob",
+// })
+//
+// Pluralization
+//
+// TranslateFunc supports the pluralization of strings using the CLDR pluralization rules defined here:
+// http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
+// T("You have {{.Count}} unread emails.", 2)
+// T("I am {{.Count}} meters tall.", "1.7")
+//
+// Plural strings may also have variables.
+// T("{{.Person}} has {{.Count}} unread emails", 2, map[string]interface{}{
+// "Person": "Bob",
+// })
+//
+// Sentences with multiple plural components can be supported with nesting.
+// T("{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.", 3, map[string]interface{}{
+// "Person": "Bob",
+// "Timeframe": T("{{.Count}} days", 2),
+// })
+//
+// Templates
+//
+// You can use the .Funcs() method of a text/template or html/template to register a TranslateFunc
+// for usage inside of that template.
+package i18n
+
+import (
+ "github.com/nicksnyder/go-i18n/i18n/bundle"
+ "github.com/nicksnyder/go-i18n/i18n/language"
+ "github.com/nicksnyder/go-i18n/i18n/translation"
+)
+
+// TranslateFunc returns the translation of the string identified by translationID.
+//
+// If there is no translation for translationID, then the translationID itself is returned.
+// This makes it easy to identify missing translations in your app.
+//
+// If translationID is a non-plural form, then the first variadic argument may be a map[string]interface{}
+// or struct that contains template data.
+//
+// If translationID is a plural form, then the first variadic argument must be an integer type
+// (int, int8, int16, int32, int64) or a float formatted as a string (e.g. "123.45").
+// The second variadic argument may be a map[string]interface{} or struct that contains template data.
+type TranslateFunc func(translationID string, args ...interface{}) string
+
+// IdentityTfunc returns a TranslateFunc that always returns the translationID passed to it.
+//
+// It is a useful placeholder when parsing a text/template or html/template
+// before the actual Tfunc is available.
+func IdentityTfunc() TranslateFunc {
+ return func(translationID string, args ...interface{}) string {
+ return translationID
+ }
+}
+
+var defaultBundle = bundle.New()
+
+// MustLoadTranslationFile is similar to LoadTranslationFile
+// except it panics if an error happens.
+func MustLoadTranslationFile(filename string) {
+ defaultBundle.MustLoadTranslationFile(filename)
+}
+
+// LoadTranslationFile loads the translations from filename into memory.
+//
+// The language that the translations are associated with is parsed from the filename (e.g. en-US.json).
+//
+// Generally you should load translation files once during your program's initialization.
+func LoadTranslationFile(filename string) error {
+ return defaultBundle.LoadTranslationFile(filename)
+}
+
+// ParseTranslationFileBytes is similar to LoadTranslationFile except it parses the bytes in buf.
+//
+// It is useful for parsing translation files embedded with go-bindata.
+func ParseTranslationFileBytes(filename string, buf []byte) error {
+ return defaultBundle.ParseTranslationFileBytes(filename, buf)
+}
+
+// AddTranslation adds translations for a language.
+//
+// It is useful if your translations are in a format not supported by LoadTranslationFile.
+func AddTranslation(lang *language.Language, translations ...translation.Translation) {
+ defaultBundle.AddTranslation(lang, translations...)
+}
+
+// LanguageTags returns the tags of all languages that have been added.
+func LanguageTags() []string {
+ return defaultBundle.LanguageTags()
+}
+
+// LanguageTranslationIDs returns the ids of all translations that have been added for a given language.
+func LanguageTranslationIDs(languageTag string) []string {
+ return defaultBundle.LanguageTranslationIDs(languageTag)
+}
+
+// MustTfunc is similar to Tfunc except it panics if an error happens.
+func MustTfunc(languageSource string, languageSources ...string) TranslateFunc {
+ return TranslateFunc(defaultBundle.MustTfunc(languageSource, languageSources...))
+}
+
+// Tfunc returns a TranslateFunc that will be bound to the first language which
+// has a non-zero number of translations.
+//
+// It can parse languages from Accept-Language headers (RFC 2616).
+func Tfunc(languageSource string, languageSources ...string) (TranslateFunc, error) {
+ tfunc, err := defaultBundle.Tfunc(languageSource, languageSources...)
+ return TranslateFunc(tfunc), err
+}
+
+// MustTfuncAndLanguage is similar to TfuncAndLanguage except it panics if an error happens.
+func MustTfuncAndLanguage(languageSource string, languageSources ...string) (TranslateFunc, *language.Language) {
+ tfunc, lang := defaultBundle.MustTfuncAndLanguage(languageSource, languageSources...)
+ return TranslateFunc(tfunc), lang
+}
+
+// TfuncAndLanguage is similar to Tfunc except it also returns the language which TranslateFunc is bound to.
+func TfuncAndLanguage(languageSource string, languageSources ...string) (TranslateFunc, *language.Language, error) {
+ tfunc, lang, err := defaultBundle.TfuncAndLanguage(languageSource, languageSources...)
+ return TranslateFunc(tfunc), lang, err
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/language.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/language.go
new file mode 100644
index 000000000..9a155efc5
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/language.go
@@ -0,0 +1,99 @@
+// Package language defines languages that implement CLDR pluralization.
+package language
+
+import (
+ "fmt"
+ "strings"
+)
+
+// Language is a written human language.
+type Language struct {
+ // Tag uniquely identifies the language as defined by RFC 5646.
+ //
+ // Most language tags are a two character language code (ISO 639-1)
+ // optionally followed by a dash and a two character country code (ISO 3166-1).
+ // (e.g. en, pt-br)
+ Tag string
+ *PluralSpec
+}
+
+func (l *Language) String() string {
+ return l.Tag
+}
+
+// MatchingTags returns the set of language tags that map to this Language.
+// e.g. "zh-hans-cn" yields {"zh", "zh-hans", "zh-hans-cn"}
+// BUG: This should be computed once and stored as a field on Language for efficiency,
+// but this would require changing how Languages are constructed.
+func (l *Language) MatchingTags() []string {
+ parts := strings.Split(l.Tag, "-")
+ var prefix, matches []string
+ for _, part := range parts {
+ prefix = append(prefix, part)
+ match := strings.Join(prefix, "-")
+ matches = append(matches, match)
+ }
+ return matches
+}
+
+// Parse returns a slice of supported languages found in src or nil if none are found.
+// It can parse language tags and Accept-Language headers.
+func Parse(src string) []*Language {
+ var langs []*Language
+ start := 0
+ for end, chr := range src {
+ switch chr {
+ case ',', ';', '.':
+ tag := strings.TrimSpace(src[start:end])
+ if spec := getPluralSpec(tag); spec != nil {
+ langs = append(langs, &Language{NormalizeTag(tag), spec})
+ }
+ start = end + 1
+ }
+ }
+ if start > 0 {
+ tag := strings.TrimSpace(src[start:])
+ if spec := getPluralSpec(tag); spec != nil {
+ langs = append(langs, &Language{NormalizeTag(tag), spec})
+ }
+ return dedupe(langs)
+ }
+ if spec := getPluralSpec(src); spec != nil {
+ langs = append(langs, &Language{NormalizeTag(src), spec})
+ }
+ return langs
+}
+
+func dedupe(langs []*Language) []*Language {
+ found := make(map[string]struct{}, len(langs))
+ deduped := make([]*Language, 0, len(langs))
+ for _, lang := range langs {
+ if _, ok := found[lang.Tag]; !ok {
+ found[lang.Tag] = struct{}{}
+ deduped = append(deduped, lang)
+ }
+ }
+ return deduped
+}
+
+// MustParse is similar to Parse except it panics instead of retuning a nil Language.
+func MustParse(src string) []*Language {
+ langs := Parse(src)
+ if len(langs) == 0 {
+ panic(fmt.Errorf("unable to parse language from %q", src))
+ }
+ return langs
+}
+
+// Add adds support for a new language.
+func Add(l *Language) {
+ tag := NormalizeTag(l.Tag)
+ pluralSpecs[tag] = l.PluralSpec
+}
+
+// NormalizeTag returns a language tag with all lower-case characters
+// and dashes "-" instead of underscores "_"
+func NormalizeTag(tag string) string {
+ tag = strings.ToLower(tag)
+ return strings.Replace(tag, "_", "-", -1)
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/operands.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/operands.go
new file mode 100644
index 000000000..877bcc89d
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/operands.go
@@ -0,0 +1,119 @@
+package language
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+// http://unicode.org/reports/tr35/tr35-numbers.html#Operands
+type operands struct {
+ N float64 // absolute value of the source number (integer and decimals)
+ I int64 // integer digits of n
+ V int64 // number of visible fraction digits in n, with trailing zeros
+ W int64 // number of visible fraction digits in n, without trailing zeros
+ F int64 // visible fractional digits in n, with trailing zeros
+ T int64 // visible fractional digits in n, without trailing zeros
+}
+
+// NmodEqualAny returns true if o represents an integer equal to any of the arguments.
+func (o *operands) NequalsAny(any ...int64) bool {
+ for _, i := range any {
+ if o.I == i && o.T == 0 {
+ return true
+ }
+ }
+ return false
+}
+
+// NmodEqualAny returns true if o represents an integer equal to any of the arguments modulo mod.
+func (o *operands) NmodEqualsAny(mod int64, any ...int64) bool {
+ modI := o.I % mod
+ for _, i := range any {
+ if modI == i && o.T == 0 {
+ return true
+ }
+ }
+ return false
+}
+
+// NmodInRange returns true if o represents an integer in the closed interval [from, to].
+func (o *operands) NinRange(from, to int64) bool {
+ return o.T == 0 && from <= o.I && o.I <= to
+}
+
+// NmodInRange returns true if o represents an integer in the closed interval [from, to] modulo mod.
+func (o *operands) NmodInRange(mod, from, to int64) bool {
+ modI := o.I % mod
+ return o.T == 0 && from <= modI && modI <= to
+}
+
+func newOperands(v interface{}) (*operands, error) {
+ switch v := v.(type) {
+ case int:
+ return newOperandsInt64(int64(v)), nil
+ case int8:
+ return newOperandsInt64(int64(v)), nil
+ case int16:
+ return newOperandsInt64(int64(v)), nil
+ case int32:
+ return newOperandsInt64(int64(v)), nil
+ case int64:
+ return newOperandsInt64(v), nil
+ case string:
+ return newOperandsString(v)
+ case float32, float64:
+ return nil, fmt.Errorf("floats should be formatted into a string")
+ default:
+ return nil, fmt.Errorf("invalid type %T; expected integer or string", v)
+ }
+}
+
+func newOperandsInt64(i int64) *operands {
+ if i < 0 {
+ i = -i
+ }
+ return &operands{float64(i), i, 0, 0, 0, 0}
+}
+
+func newOperandsString(s string) (*operands, error) {
+ if s[0] == '-' {
+ s = s[1:]
+ }
+ n, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ return nil, err
+ }
+ ops := &operands{N: n}
+ parts := strings.SplitN(s, ".", 2)
+ ops.I, err = strconv.ParseInt(parts[0], 10, 64)
+ if err != nil {
+ return nil, err
+ }
+ if len(parts) == 1 {
+ return ops, nil
+ }
+ fraction := parts[1]
+ ops.V = int64(len(fraction))
+ for i := ops.V - 1; i >= 0; i-- {
+ if fraction[i] != '0' {
+ ops.W = i + 1
+ break
+ }
+ }
+ if ops.V > 0 {
+ f, err := strconv.ParseInt(fraction, 10, 0)
+ if err != nil {
+ return nil, err
+ }
+ ops.F = f
+ }
+ if ops.W > 0 {
+ t, err := strconv.ParseInt(fraction[:ops.W], 10, 0)
+ if err != nil {
+ return nil, err
+ }
+ ops.T = t
+ }
+ return ops, nil
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/plural.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/plural.go
new file mode 100644
index 000000000..1f3ea5c69
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/plural.go
@@ -0,0 +1,40 @@
+package language
+
+import (
+ "fmt"
+)
+
+// Plural represents a language pluralization form as defined here:
+// http://cldr.unicode.org/index/cldr-spec/plural-rules
+type Plural string
+
+// All defined plural categories.
+const (
+ Invalid Plural = "invalid"
+ Zero = "zero"
+ One = "one"
+ Two = "two"
+ Few = "few"
+ Many = "many"
+ Other = "other"
+)
+
+// NewPlural returns src as a Plural
+// or Invalid and a non-nil error if src is not a valid Plural.
+func NewPlural(src string) (Plural, error) {
+ switch src {
+ case "zero":
+ return Zero, nil
+ case "one":
+ return One, nil
+ case "two":
+ return Two, nil
+ case "few":
+ return Few, nil
+ case "many":
+ return Many, nil
+ case "other":
+ return Other, nil
+ }
+ return Invalid, fmt.Errorf("invalid plural category %s", src)
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec.go
new file mode 100644
index 000000000..fc3522682
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec.go
@@ -0,0 +1,74 @@
+package language
+
+import "strings"
+
+// PluralSpec defines the CLDR plural rules for a language.
+// http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
+// http://unicode.org/reports/tr35/tr35-numbers.html#Operands
+type PluralSpec struct {
+ Plurals map[Plural]struct{}
+ PluralFunc func(*operands) Plural
+}
+
+var pluralSpecs = make(map[string]*PluralSpec)
+
+func normalizePluralSpecID(id string) string {
+ id = strings.Replace(id, "_", "-", -1)
+ id = strings.ToLower(id)
+ return id
+}
+
+func registerPluralSpec(ids []string, ps *PluralSpec) {
+ for _, id := range ids {
+ id = normalizePluralSpecID(id)
+ pluralSpecs[id] = ps
+ }
+}
+
+// Plural returns the plural category for number as defined by
+// the language's CLDR plural rules.
+func (ps *PluralSpec) Plural(number interface{}) (Plural, error) {
+ ops, err := newOperands(number)
+ if err != nil {
+ return Invalid, err
+ }
+ return ps.PluralFunc(ops), nil
+}
+
+// getPluralSpec returns the PluralSpec that matches the longest prefix of tag.
+// It returns nil if no PluralSpec matches tag.
+func getPluralSpec(tag string) *PluralSpec {
+ tag = NormalizeTag(tag)
+ subtag := tag
+ for {
+ if spec := pluralSpecs[subtag]; spec != nil {
+ return spec
+ }
+ end := strings.LastIndex(subtag, "-")
+ if end == -1 {
+ return nil
+ }
+ subtag = subtag[:end]
+ }
+}
+
+func newPluralSet(plurals ...Plural) map[Plural]struct{} {
+ set := make(map[Plural]struct{}, len(plurals))
+ for _, plural := range plurals {
+ set[plural] = struct{}{}
+ }
+ return set
+}
+
+func intInRange(i, from, to int64) bool {
+ return from <= i && i <= to
+}
+
+func intEqualsAny(i int64, any ...int64) bool {
+ for _, a := range any {
+ if i == a {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_gen.go b/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_gen.go
new file mode 100644
index 000000000..c9b4f2667
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_gen.go
@@ -0,0 +1,567 @@
+package language
+
+// This file is generated by i18n/language/codegen/generate.sh
+
+func init() {
+
+ registerPluralSpec([]string{"bm", "bo", "dz", "id", "ig", "ii", "in", "ja", "jbo", "jv", "jw", "kde", "kea", "km", "ko", "lkt", "lo", "ms", "my", "nqo", "root", "sah", "ses", "sg", "th", "to", "vi", "wo", "yo", "zh"}, &PluralSpec{
+ Plurals: newPluralSet(Other),
+ PluralFunc: func(ops *operands) Plural {
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"am", "as", "bn", "fa", "gu", "hi", "kn", "mr", "zu"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // i = 0 or n = 1
+ if intEqualsAny(ops.I, 0) ||
+ ops.NequalsAny(1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"ff", "fr", "hy", "kab"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // i = 0,1
+ if intEqualsAny(ops.I, 0, 1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"ast", "ca", "de", "en", "et", "fi", "fy", "gl", "it", "ji", "nl", "sv", "sw", "ur", "yi"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // i = 1 and v = 0
+ if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"si"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 0,1 or i = 0 and f = 1
+ if ops.NequalsAny(0, 1) ||
+ intEqualsAny(ops.I, 0) && intEqualsAny(ops.F, 1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"ak", "bh", "guw", "ln", "mg", "nso", "pa", "ti", "wa"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 0..1
+ if ops.NinRange(0, 1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"tzm"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 0..1 or n = 11..99
+ if ops.NinRange(0, 1) ||
+ ops.NinRange(11, 99) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"pt"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 0..2 and n != 2
+ if ops.NinRange(0, 2) && !ops.NequalsAny(2) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"af", "asa", "az", "bem", "bez", "bg", "brx", "ce", "cgg", "chr", "ckb", "dv", "ee", "el", "eo", "es", "eu", "fo", "fur", "gsw", "ha", "haw", "hu", "jgo", "jmc", "ka", "kaj", "kcg", "kk", "kkj", "kl", "ks", "ksb", "ku", "ky", "lb", "lg", "mas", "mgo", "ml", "mn", "nah", "nb", "nd", "ne", "nn", "nnh", "no", "nr", "ny", "nyn", "om", "or", "os", "pap", "ps", "rm", "rof", "rwk", "saq", "sdh", "seh", "sn", "so", "sq", "ss", "ssy", "st", "syr", "ta", "te", "teo", "tig", "tk", "tn", "tr", "ts", "ug", "uz", "ve", "vo", "vun", "wae", "xh", "xog"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 1
+ if ops.NequalsAny(1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"pt_PT"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 1 and v = 0
+ if ops.NequalsAny(1) && intEqualsAny(ops.V, 0) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"da"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 1 or t != 0 and i = 0,1
+ if ops.NequalsAny(1) ||
+ !intEqualsAny(ops.T, 0) && intEqualsAny(ops.I, 0, 1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"is"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // t = 0 and i % 10 = 1 and i % 100 != 11 or t != 0
+ if intEqualsAny(ops.T, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) ||
+ !intEqualsAny(ops.T, 0) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"mk"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // v = 0 and i % 10 = 1 or f % 10 = 1
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) ||
+ intEqualsAny(ops.F%10, 1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"fil", "tl"}, &PluralSpec{
+ Plurals: newPluralSet(One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I, 1, 2, 3) ||
+ intEqualsAny(ops.V, 0) && !intEqualsAny(ops.I%10, 4, 6, 9) ||
+ !intEqualsAny(ops.V, 0) && !intEqualsAny(ops.F%10, 4, 6, 9) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"lv", "prg"}, &PluralSpec{
+ Plurals: newPluralSet(Zero, One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19
+ if ops.NmodEqualsAny(10, 0) ||
+ ops.NmodInRange(100, 11, 19) ||
+ intEqualsAny(ops.V, 2) && intInRange(ops.F%100, 11, 19) {
+ return Zero
+ }
+ // n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1
+ if ops.NmodEqualsAny(10, 1) && !ops.NmodEqualsAny(100, 11) ||
+ intEqualsAny(ops.V, 2) && intEqualsAny(ops.F%10, 1) && !intEqualsAny(ops.F%100, 11) ||
+ !intEqualsAny(ops.V, 2) && intEqualsAny(ops.F%10, 1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"lag"}, &PluralSpec{
+ Plurals: newPluralSet(Zero, One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 0
+ if ops.NequalsAny(0) {
+ return Zero
+ }
+ // i = 0,1 and n != 0
+ if intEqualsAny(ops.I, 0, 1) && !ops.NequalsAny(0) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"ksh"}, &PluralSpec{
+ Plurals: newPluralSet(Zero, One, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 0
+ if ops.NequalsAny(0) {
+ return Zero
+ }
+ // n = 1
+ if ops.NequalsAny(1) {
+ return One
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"iu", "kw", "naq", "se", "sma", "smi", "smj", "smn", "sms"}, &PluralSpec{
+ Plurals: newPluralSet(One, Two, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 1
+ if ops.NequalsAny(1) {
+ return One
+ }
+ // n = 2
+ if ops.NequalsAny(2) {
+ return Two
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"shi"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // i = 0 or n = 1
+ if intEqualsAny(ops.I, 0) ||
+ ops.NequalsAny(1) {
+ return One
+ }
+ // n = 2..10
+ if ops.NinRange(2, 10) {
+ return Few
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"mo", "ro"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // i = 1 and v = 0
+ if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
+ return One
+ }
+ // v != 0 or n = 0 or n != 1 and n % 100 = 1..19
+ if !intEqualsAny(ops.V, 0) ||
+ ops.NequalsAny(0) ||
+ !ops.NequalsAny(1) && ops.NmodInRange(100, 1, 19) {
+ return Few
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"bs", "hr", "sh", "sr"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) ||
+ intEqualsAny(ops.F%10, 1) && !intEqualsAny(ops.F%100, 11) {
+ return One
+ }
+ // v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14
+ if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) ||
+ intInRange(ops.F%10, 2, 4) && !intInRange(ops.F%100, 12, 14) {
+ return Few
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"gd"}, &PluralSpec{
+ Plurals: newPluralSet(One, Two, Few, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 1,11
+ if ops.NequalsAny(1, 11) {
+ return One
+ }
+ // n = 2,12
+ if ops.NequalsAny(2, 12) {
+ return Two
+ }
+ // n = 3..10,13..19
+ if ops.NinRange(3, 10) || ops.NinRange(13, 19) {
+ return Few
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"sl"}, &PluralSpec{
+ Plurals: newPluralSet(One, Two, Few, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // v = 0 and i % 100 = 1
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 1) {
+ return One
+ }
+ // v = 0 and i % 100 = 2
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 2) {
+ return Two
+ }
+ // v = 0 and i % 100 = 3..4 or v != 0
+ if intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 3, 4) ||
+ !intEqualsAny(ops.V, 0) {
+ return Few
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"dsb", "hsb"}, &PluralSpec{
+ Plurals: newPluralSet(One, Two, Few, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // v = 0 and i % 100 = 1 or f % 100 = 1
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 1) ||
+ intEqualsAny(ops.F%100, 1) {
+ return One
+ }
+ // v = 0 and i % 100 = 2 or f % 100 = 2
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 2) ||
+ intEqualsAny(ops.F%100, 2) {
+ return Two
+ }
+ // v = 0 and i % 100 = 3..4 or f % 100 = 3..4
+ if intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 3, 4) ||
+ intInRange(ops.F%100, 3, 4) {
+ return Few
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"he", "iw"}, &PluralSpec{
+ Plurals: newPluralSet(One, Two, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // i = 1 and v = 0
+ if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
+ return One
+ }
+ // i = 2 and v = 0
+ if intEqualsAny(ops.I, 2) && intEqualsAny(ops.V, 0) {
+ return Two
+ }
+ // v = 0 and n != 0..10 and n % 10 = 0
+ if intEqualsAny(ops.V, 0) && !ops.NinRange(0, 10) && ops.NmodEqualsAny(10, 0) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"cs", "sk"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // i = 1 and v = 0
+ if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
+ return One
+ }
+ // i = 2..4 and v = 0
+ if intInRange(ops.I, 2, 4) && intEqualsAny(ops.V, 0) {
+ return Few
+ }
+ // v != 0
+ if !intEqualsAny(ops.V, 0) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"pl"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // i = 1 and v = 0
+ if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
+ return One
+ }
+ // v = 0 and i % 10 = 2..4 and i % 100 != 12..14
+ if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) {
+ return Few
+ }
+ // v = 0 and i != 1 and i % 10 = 0..1 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14
+ if intEqualsAny(ops.V, 0) && !intEqualsAny(ops.I, 1) && intInRange(ops.I%10, 0, 1) ||
+ intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 5, 9) ||
+ intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 12, 14) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"be"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n % 10 = 1 and n % 100 != 11
+ if ops.NmodEqualsAny(10, 1) && !ops.NmodEqualsAny(100, 11) {
+ return One
+ }
+ // n % 10 = 2..4 and n % 100 != 12..14
+ if ops.NmodInRange(10, 2, 4) && !ops.NmodInRange(100, 12, 14) {
+ return Few
+ }
+ // n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14
+ if ops.NmodEqualsAny(10, 0) ||
+ ops.NmodInRange(10, 5, 9) ||
+ ops.NmodInRange(100, 11, 14) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"lt"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n % 10 = 1 and n % 100 != 11..19
+ if ops.NmodEqualsAny(10, 1) && !ops.NmodInRange(100, 11, 19) {
+ return One
+ }
+ // n % 10 = 2..9 and n % 100 != 11..19
+ if ops.NmodInRange(10, 2, 9) && !ops.NmodInRange(100, 11, 19) {
+ return Few
+ }
+ // f != 0
+ if !intEqualsAny(ops.F, 0) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"mt"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 1
+ if ops.NequalsAny(1) {
+ return One
+ }
+ // n = 0 or n % 100 = 2..10
+ if ops.NequalsAny(0) ||
+ ops.NmodInRange(100, 2, 10) {
+ return Few
+ }
+ // n % 100 = 11..19
+ if ops.NmodInRange(100, 11, 19) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"ru", "uk"}, &PluralSpec{
+ Plurals: newPluralSet(One, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // v = 0 and i % 10 = 1 and i % 100 != 11
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) {
+ return One
+ }
+ // v = 0 and i % 10 = 2..4 and i % 100 != 12..14
+ if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) {
+ return Few
+ }
+ // v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 0) ||
+ intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 5, 9) ||
+ intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 11, 14) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"br"}, &PluralSpec{
+ Plurals: newPluralSet(One, Two, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n % 10 = 1 and n % 100 != 11,71,91
+ if ops.NmodEqualsAny(10, 1) && !ops.NmodEqualsAny(100, 11, 71, 91) {
+ return One
+ }
+ // n % 10 = 2 and n % 100 != 12,72,92
+ if ops.NmodEqualsAny(10, 2) && !ops.NmodEqualsAny(100, 12, 72, 92) {
+ return Two
+ }
+ // n % 10 = 3..4,9 and n % 100 != 10..19,70..79,90..99
+ if (ops.NmodInRange(10, 3, 4) || ops.NmodEqualsAny(10, 9)) && !(ops.NmodInRange(100, 10, 19) || ops.NmodInRange(100, 70, 79) || ops.NmodInRange(100, 90, 99)) {
+ return Few
+ }
+ // n != 0 and n % 1000000 = 0
+ if !ops.NequalsAny(0) && ops.NmodEqualsAny(1000000, 0) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"ga"}, &PluralSpec{
+ Plurals: newPluralSet(One, Two, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 1
+ if ops.NequalsAny(1) {
+ return One
+ }
+ // n = 2
+ if ops.NequalsAny(2) {
+ return Two
+ }
+ // n = 3..6
+ if ops.NinRange(3, 6) {
+ return Few
+ }
+ // n = 7..10
+ if ops.NinRange(7, 10) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"gv"}, &PluralSpec{
+ Plurals: newPluralSet(One, Two, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // v = 0 and i % 10 = 1
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) {
+ return One
+ }
+ // v = 0 and i % 10 = 2
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 2) {
+ return Two
+ }
+ // v = 0 and i % 100 = 0,20,40,60,80
+ if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 0, 20, 40, 60, 80) {
+ return Few
+ }
+ // v != 0
+ if !intEqualsAny(ops.V, 0) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"ar"}, &PluralSpec{
+ Plurals: newPluralSet(Zero, One, Two, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 0
+ if ops.NequalsAny(0) {
+ return Zero
+ }
+ // n = 1
+ if ops.NequalsAny(1) {
+ return One
+ }
+ // n = 2
+ if ops.NequalsAny(2) {
+ return Two
+ }
+ // n % 100 = 3..10
+ if ops.NmodInRange(100, 3, 10) {
+ return Few
+ }
+ // n % 100 = 11..99
+ if ops.NmodInRange(100, 11, 99) {
+ return Many
+ }
+ return Other
+ },
+ })
+ registerPluralSpec([]string{"cy"}, &PluralSpec{
+ Plurals: newPluralSet(Zero, One, Two, Few, Many, Other),
+ PluralFunc: func(ops *operands) Plural {
+ // n = 0
+ if ops.NequalsAny(0) {
+ return Zero
+ }
+ // n = 1
+ if ops.NequalsAny(1) {
+ return One
+ }
+ // n = 2
+ if ops.NequalsAny(2) {
+ return Two
+ }
+ // n = 3
+ if ops.NequalsAny(3) {
+ return Few
+ }
+ // n = 6
+ if ops.NequalsAny(6) {
+ return Many
+ }
+ return Other
+ },
+ })
+}
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation.go
new file mode 100644
index 000000000..4f579d16a
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation.go
@@ -0,0 +1,78 @@
+package translation
+
+import (
+ "github.com/nicksnyder/go-i18n/i18n/language"
+)
+
+type pluralTranslation struct {
+ id string
+ templates map[language.Plural]*template
+}
+
+func (pt *pluralTranslation) MarshalInterface() interface{} {
+ return map[string]interface{}{
+ "id": pt.id,
+ "translation": pt.templates,
+ }
+}
+
+func (pt *pluralTranslation) ID() string {
+ return pt.id
+}
+
+func (pt *pluralTranslation) Template(pc language.Plural) *template {
+ return pt.templates[pc]
+}
+
+func (pt *pluralTranslation) UntranslatedCopy() Translation {
+ return &pluralTranslation{pt.id, make(map[language.Plural]*template)}
+}
+
+func (pt *pluralTranslation) Normalize(l *language.Language) Translation {
+ // Delete plural categories that don't belong to this language.
+ for pc := range pt.templates {
+ if _, ok := l.Plurals[pc]; !ok {
+ delete(pt.templates, pc)
+ }
+ }
+ // Create map entries for missing valid categories.
+ for pc := range l.Plurals {
+ if _, ok := pt.templates[pc]; !ok {
+ pt.templates[pc] = mustNewTemplate("")
+ }
+ }
+ return pt
+}
+
+func (pt *pluralTranslation) Backfill(src Translation) Translation {
+ for pc, t := range pt.templates {
+ if t == nil || t.src == "" {
+ pt.templates[pc] = src.Template(language.Other)
+ }
+ }
+ return pt
+}
+
+func (pt *pluralTranslation) Merge(t Translation) Translation {
+ other, ok := t.(*pluralTranslation)
+ if !ok || pt.ID() != t.ID() {
+ return t
+ }
+ for pluralCategory, template := range other.templates {
+ if template != nil && template.src != "" {
+ pt.templates[pluralCategory] = template
+ }
+ }
+ return pt
+}
+
+func (pt *pluralTranslation) Incomplete(l *language.Language) bool {
+ for pc := range l.Plurals {
+ if t := pt.templates[pc]; t == nil || t.src == "" {
+ return true
+ }
+ }
+ return false
+}
+
+var _ = Translation(&pluralTranslation{})
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/single_translation.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/single_translation.go
new file mode 100644
index 000000000..1010e5947
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/single_translation.go
@@ -0,0 +1,57 @@
+package translation
+
+import (
+ "github.com/nicksnyder/go-i18n/i18n/language"
+)
+
+type singleTranslation struct {
+ id string
+ template *template
+}
+
+func (st *singleTranslation) MarshalInterface() interface{} {
+ return map[string]interface{}{
+ "id": st.id,
+ "translation": st.template,
+ }
+}
+
+func (st *singleTranslation) ID() string {
+ return st.id
+}
+
+func (st *singleTranslation) Template(pc language.Plural) *template {
+ return st.template
+}
+
+func (st *singleTranslation) UntranslatedCopy() Translation {
+ return &singleTranslation{st.id, mustNewTemplate("")}
+}
+
+func (st *singleTranslation) Normalize(language *language.Language) Translation {
+ return st
+}
+
+func (st *singleTranslation) Backfill(src Translation) Translation {
+ if st.template == nil || st.template.src == "" {
+ st.template = src.Template(language.Other)
+ }
+ return st
+}
+
+func (st *singleTranslation) Merge(t Translation) Translation {
+ other, ok := t.(*singleTranslation)
+ if !ok || st.ID() != t.ID() {
+ return t
+ }
+ if other.template != nil && other.template.src != "" {
+ st.template = other.template
+ }
+ return st
+}
+
+func (st *singleTranslation) Incomplete(l *language.Language) bool {
+ return st.template == nil || st.template.src == ""
+}
+
+var _ = Translation(&singleTranslation{})
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go
new file mode 100644
index 000000000..c8756fa4e
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go
@@ -0,0 +1,61 @@
+package translation
+
+import (
+ "bytes"
+ "encoding"
+ "strings"
+ gotemplate "text/template"
+)
+
+type template struct {
+ tmpl *gotemplate.Template
+ src string
+}
+
+func newTemplate(src string) (*template, error) {
+ var tmpl template
+ err := tmpl.parseTemplate(src)
+ return &tmpl, err
+}
+
+func mustNewTemplate(src string) *template {
+ t, err := newTemplate(src)
+ if err != nil {
+ panic(err)
+ }
+ return t
+}
+
+func (t *template) String() string {
+ return t.src
+}
+
+func (t *template) Execute(args interface{}) string {
+ if t.tmpl == nil {
+ return t.src
+ }
+ var buf bytes.Buffer
+ if err := t.tmpl.Execute(&buf, args); err != nil {
+ return err.Error()
+ }
+ return buf.String()
+}
+
+func (t *template) MarshalText() ([]byte, error) {
+ return []byte(t.src), nil
+}
+
+func (t *template) UnmarshalText(src []byte) error {
+ return t.parseTemplate(string(src))
+}
+
+func (t *template) parseTemplate(src string) (err error) {
+ t.src = src
+ if strings.Contains(src, "{{") {
+ t.tmpl, err = gotemplate.New(src).Parse(src)
+ }
+ return
+}
+
+var _ = encoding.TextMarshaler(&template{})
+var _ = encoding.TextUnmarshaler(&template{})
diff --git a/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go
new file mode 100644
index 000000000..fa93180b8
--- /dev/null
+++ b/vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go
@@ -0,0 +1,83 @@
+// Package translation defines the interface for a translation.
+package translation
+
+import (
+ "fmt"
+
+ "github.com/nicksnyder/go-i18n/i18n/language"
+)
+
+// Translation is the interface that represents a translated string.
+type Translation interface {
+ // MarshalInterface returns the object that should be used
+ // to serialize the translation.
+ MarshalInterface() interface{}
+ ID() string
+ Template(language.Plural) *template
+ UntranslatedCopy() Translation
+ Normalize(language *language.Language) Translation
+ Backfill(src Translation) Translation
+ Merge(Translation) Translation
+ Incomplete(l *language.Language) bool
+}
+
+// SortableByID implements sort.Interface for a slice of translations.
+type SortableByID []Translation
+
+func (a SortableByID) Len() int { return len(a) }
+func (a SortableByID) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a SortableByID) Less(i, j int) bool { return a[i].ID() < a[j].ID() }
+
+// NewTranslation reflects on data to create a new Translation.
+//
+// data["id"] must be a string and data["translation"] must be either a string
+// for a non-plural translation or a map[string]interface{} for a plural translation.
+func NewTranslation(data map[string]interface{}) (Translation, error) {
+ id, ok := data["id"].(string)
+ if !ok {
+ return nil, fmt.Errorf(`missing "id" key`)
+ }
+ var pluralObject map[string]interface{}
+ switch translation := data["translation"].(type) {
+ case string:
+ tmpl, err := newTemplate(translation)
+ if err != nil {
+ return nil, err
+ }
+ return &singleTranslation{id, tmpl}, nil
+ case map[interface{}]interface{}:
+ // The YAML parser uses interface{} keys so we first convert them to string keys.
+ pluralObject = make(map[string]interface{})
+ for k, v := range translation {
+ kStr, ok := k.(string)
+ if !ok {
+ return nil, fmt.Errorf(`invalid plural category type %T; expected string`, k)
+ }
+ pluralObject[kStr] = v
+ }
+ case map[string]interface{}:
+ pluralObject = translation
+ case nil:
+ return nil, fmt.Errorf(`missing "translation" key`)
+ default:
+ return nil, fmt.Errorf(`unsupported type for "translation" key %T`, translation)
+ }
+
+ templates := make(map[language.Plural]*template, len(pluralObject))
+ for k, v := range pluralObject {
+ pc, err := language.NewPlural(k)
+ if err != nil {
+ return nil, err
+ }
+ str, ok := v.(string)
+ if !ok {
+ return nil, fmt.Errorf(`plural category "%s" has value of type %T; expected string`, pc, v)
+ }
+ tmpl, err := newTemplate(str)
+ if err != nil {
+ return nil, err
+ }
+ templates[pc] = tmpl
+ }
+ return &pluralTranslation{id, templates}, nil
+}
diff --git a/vendor/github.com/pborman/uuid/.travis.yml b/vendor/github.com/pborman/uuid/.travis.yml
new file mode 100644
index 000000000..a6a98db8a
--- /dev/null
+++ b/vendor/github.com/pborman/uuid/.travis.yml
@@ -0,0 +1,10 @@
+language: go
+
+go:
+ - 1.4.3
+ - 1.5.3
+ - release
+ - tip
+
+script:
+ - go test -v ./...
diff --git a/vendor/github.com/pborman/uuid/CONTRIBUTING.md b/vendor/github.com/pborman/uuid/CONTRIBUTING.md
new file mode 100644
index 000000000..04fdf09f1
--- /dev/null
+++ b/vendor/github.com/pborman/uuid/CONTRIBUTING.md
@@ -0,0 +1,10 @@
+# How to contribute
+
+We definitely welcome patches and contribution to this project!
+
+### Legal requirements
+
+In order to protect both you and ourselves, you will need to sign the
+[Contributor License Agreement](https://cla.developers.google.com/clas).
+
+You may have already signed it for other Google projects.
diff --git a/vendor/github.com/pborman/uuid/CONTRIBUTORS b/vendor/github.com/pborman/uuid/CONTRIBUTORS
new file mode 100644
index 000000000..b382a04ed
--- /dev/null
+++ b/vendor/github.com/pborman/uuid/CONTRIBUTORS
@@ -0,0 +1 @@
+Paul Borman <borman@google.com>
diff --git a/vendor/github.com/pborman/uuid/LICENSE b/vendor/github.com/pborman/uuid/LICENSE
new file mode 100644
index 000000000..5dc68268d
--- /dev/null
+++ b/vendor/github.com/pborman/uuid/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009,2014 Google Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/pborman/uuid/README.md b/vendor/github.com/pborman/uuid/README.md
new file mode 100644
index 000000000..f023d47ca
--- /dev/null
+++ b/vendor/github.com/pborman/uuid/README.md
@@ -0,0 +1,13 @@
+This project was automatically exported from code.google.com/p/go-uuid
+
+# uuid ![build status](https://travis-ci.org/pborman/uuid.svg?branch=master)
+The uuid package generates and inspects UUIDs based on [RFC 412](http://tools.ietf.org/html/rfc4122) and DCE 1.1: Authentication and Security Services.
+
+###### Install
+`go get github.com/pborman/uuid`
+
+###### Documentation
+[![GoDoc](https://godoc.org/github.com/pborman/uuid?status.svg)](http://godoc.org/github.com/pborman/uuid)
+
+Full `go doc` style documentation for the package can be viewed online without installing this package by using the GoDoc site here:
+http://godoc.org/github.com/pborman/uuid
diff --git a/vendor/github.com/pborman/uuid/dce.go b/vendor/github.com/pborman/uuid/dce.go
new file mode 100644
index 000000000..50a0f2d09
--- /dev/null
+++ b/vendor/github.com/pborman/uuid/dce.go
@@ -0,0 +1,84 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "encoding/binary"
+ "fmt"
+ "os"
+)
+
+// A Domain represents a Version 2 domain
+type Domain byte
+
+// Domain constants for DCE Security (Version 2) UUIDs.
+const (
+ Person = Domain(0)
+ Group = Domain(1)
+ Org = Domain(2)
+)
+
+// NewDCESecurity returns a DCE Security (Version 2) UUID.
+//
+// The domain should be one of Person, Group or Org.
+// On a POSIX system the id should be the users UID for the Person
+// domain and the users GID for the Group. The meaning of id for
+// the domain Org or on non-POSIX systems is site defined.
+//
+// For a given domain/id pair the same token may be returned for up to
+// 7 minutes and 10 seconds.
+func NewDCESecurity(domain Domain, id uint32) UUID {
+ uuid := NewUUID()
+ if uuid != nil {
+ uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
+ uuid[9] = byte(domain)
+ binary.BigEndian.PutUint32(uuid[0:], id)
+ }
+ return uuid
+}
+
+// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
+// domain with the id returned by os.Getuid.
+//
+// NewDCEPerson(Person, uint32(os.Getuid()))
+func NewDCEPerson() UUID {
+ return NewDCESecurity(Person, uint32(os.Getuid()))
+}
+
+// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
+// domain with the id returned by os.Getgid.
+//
+// NewDCEGroup(Group, uint32(os.Getgid()))
+func NewDCEGroup() UUID {
+ return NewDCESecurity(Group, uint32(os.Getgid()))
+}
+
+// Domain returns the domain for a Version 2 UUID or false.
+func (uuid UUID) Domain() (Domain, bool) {
+ if v, _ := uuid.Version(); v != 2 {
+ return 0, false
+ }
+ return Domain(uuid[9]), true
+}
+
+// Id returns the id for a Version 2 UUID or false.
+func (uuid UUID) Id() (uint32, bool) {
+ if v, _ := uuid.Version(); v != 2 {
+ return 0, false
+ }
+ return binary.BigEndian.Uint32(uuid[0:4]), true
+}
+
+func (d Domain) String() string {
+ switch d {
+ case Person:
+ return "Person"
+ case Group:
+ return "Group"
+ case Org:
+ return "Org"
+ }
+ return fmt.Sprintf("Domain%d", int(d))
+}
diff --git a/vendor/github.com/pborman/uuid/doc.go b/vendor/github.com/pborman/uuid/doc.go
new file mode 100644
index 000000000..d8bd013e6
--- /dev/null
+++ b/vendor/github.com/pborman/uuid/doc.go
@@ -0,0 +1,8 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The uuid package generates and inspects UUIDs.
+//
+// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security Services.
+package uuid
diff --git a/vendor/github.com/pborman/uuid/hash.go b/vendor/github.com/pborman/uuid/hash.go
new file mode 100644
index 000000000..a0420c1ef
--- /dev/null
+++ b/vendor/github.com/pborman/uuid/hash.go
@@ -0,0 +1,53 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "crypto/md5"
+ "crypto/sha1"
+ "hash"
+)
+
+// Well known Name Space IDs and UUIDs
+var (
+ NameSpace_DNS = Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
+ NameSpace_URL = Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
+ NameSpace_OID = Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
+ NameSpace_X500 = Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
+ NIL = Parse("00000000-0000-0000-0000-000000000000")
+)
+
+// NewHash returns a new UUID derived from the hash of space concatenated with
+// data generated by h. The hash should be at least 16 byte in length. The
+// first 16 bytes of the hash are used to form the UUID. The version of the
+// UUID will be the lower 4 bits of version. NewHash is used to implement
+// NewMD5 and NewSHA1.
+func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
+ h.Reset()
+ h.Write(space)
+ h.Write([]byte(data))
+ s := h.Sum(nil)
+ uuid := make([]byte, 16)
+ copy(uuid, s)
+ uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
+ uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
+ return uuid
+}
+
+// NewMD5 returns a new MD5 (Version 3) UUID based on the
+// supplied name space and data.
+//
+// NewHash(md5.New(), space, data, 3)
+func NewMD5(space UUID, data []byte) UUID {
+ return NewHash(md5.New(), space, data, 3)
+}
+
+// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
+// supplied name space and data.
+//
+// NewHash(sha1.New(), space, data, 5)
+func NewSHA1(space UUID, data []byte) UUID {
+ return NewHash(sha1.New(), space, data, 5)
+}
diff --git a/vendor/github.com/pborman/uuid/json.go b/vendor/github.com/pborman/uuid/json.go
new file mode 100644
index 000000000..9dda1dfba
--- /dev/null
+++ b/vendor/github.com/pborman/uuid/json.go
@@ -0,0 +1,34 @@
+// Copyright 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import "errors"
+
+func (u UUID) MarshalJSON() ([]byte, error) {
+ if len(u) != 16 {
+ return []byte(`""`), nil
+ }
+ var js [38]byte
+ js[0] = '"'
+ encodeHex(js[1:], u)
+ js[37] = '"'
+ return js[:], nil
+}
+
+func (u *UUID) UnmarshalJSON(data []byte) error {
+ if string(data) == `""` {
+ return nil
+ }
+ if data[0] != '"' {
+ return errors.New("invalid UUID format")
+ }
+ data = data[1 : len(data)-1]
+ uu := Parse(string(data))
+ if uu == nil {
+ return errors.New("invalid UUID format")
+ }
+ *u = uu
+ return nil
+}
diff --git a/vendor/github.com/pborman/uuid/node.go b/vendor/github.com/pborman/uuid/node.go
new file mode 100644
index 000000000..42d60da8f
--- /dev/null
+++ b/vendor/github.com/pborman/uuid/node.go
@@ -0,0 +1,117 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "net"
+ "sync"
+)
+
+var (
+ nodeMu sync.Mutex
+ interfaces []net.Interface // cached list of interfaces
+ ifname string // name of interface being used
+ nodeID []byte // hardware for version 1 UUIDs
+)
+
+// NodeInterface returns the name of the interface from which the NodeID was
+// derived. The interface "user" is returned if the NodeID was set by
+// SetNodeID.
+func NodeInterface() string {
+ defer nodeMu.Unlock()
+ nodeMu.Lock()
+ return ifname
+}
+
+// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
+// If name is "" then the first usable interface found will be used or a random
+// Node ID will be generated. If a named interface cannot be found then false
+// is returned.
+//
+// SetNodeInterface never fails when name is "".
+func SetNodeInterface(name string) bool {
+ defer nodeMu.Unlock()
+ nodeMu.Lock()
+ return setNodeInterface(name)
+}
+
+func setNodeInterface(name string) bool {
+ if interfaces == nil {
+ var err error
+ interfaces, err = net.Interfaces()
+ if err != nil && name != "" {
+ return false
+ }
+ }
+
+ for _, ifs := range interfaces {
+ if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
+ if setNodeID(ifs.HardwareAddr) {
+ ifname = ifs.Name
+ return true
+ }
+ }
+ }
+
+ // We found no interfaces with a valid hardware address. If name
+ // does not specify a specific interface generate a random Node ID
+ // (section 4.1.6)
+ if name == "" {
+ if nodeID == nil {
+ nodeID = make([]byte, 6)
+ }
+ randomBits(nodeID)
+ return true
+ }
+ return false
+}
+
+// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
+// if not already set.
+func NodeID() []byte {
+ defer nodeMu.Unlock()
+ nodeMu.Lock()
+ if nodeID == nil {
+ setNodeInterface("")
+ }
+ nid := make([]byte, 6)
+ copy(nid, nodeID)
+ return nid
+}
+
+// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
+// of id are used. If id is less than 6 bytes then false is returned and the
+// Node ID is not set.
+func SetNodeID(id []byte) bool {
+ defer nodeMu.Unlock()
+ nodeMu.Lock()
+ if setNodeID(id) {
+ ifname = "user"
+ return true
+ }
+ return false
+}
+
+func setNodeID(id []byte) bool {
+ if len(id) < 6 {
+ return false
+ }
+ if nodeID == nil {
+ nodeID = make([]byte, 6)
+ }
+ copy(nodeID, id)
+ return true
+}
+
+// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
+// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
+func (uuid UUID) NodeID() []byte {
+ if len(uuid) != 16 {
+ return nil
+ }
+ node := make([]byte, 6)
+ copy(node, uuid[10:])
+ return node
+}
diff --git a/vendor/github.com/pborman/uuid/sql.go b/vendor/github.com/pborman/uuid/sql.go
new file mode 100644
index 000000000..d015bfd13
--- /dev/null
+++ b/vendor/github.com/pborman/uuid/sql.go
@@ -0,0 +1,66 @@
+// Copyright 2015 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "database/sql/driver"
+ "errors"
+ "fmt"
+)
+
+// Scan implements sql.Scanner so UUIDs can be read from databases transparently
+// Currently, database types that map to string and []byte are supported. Please
+// consult database-specific driver documentation for matching types.
+func (uuid *UUID) Scan(src interface{}) error {
+ switch src.(type) {
+ case string:
+ // if an empty UUID comes from a table, we return a null UUID
+ if src.(string) == "" {
+ return nil
+ }
+
+ // see uuid.Parse for required string format
+ parsed := Parse(src.(string))
+
+ if parsed == nil {
+ return errors.New("Scan: invalid UUID format")
+ }
+
+ *uuid = parsed
+ case []byte:
+ b := src.([]byte)
+
+ // if an empty UUID comes from a table, we return a null UUID
+ if len(b) == 0 {
+ return nil
+ }
+
+ // assumes a simple slice of bytes if 16 bytes
+ // otherwise attempts to parse
+ if len(b) == 16 {
+ *uuid = UUID(b)
+ } else {
+ u := Parse(string(b))
+
+ if u == nil {
+ return errors.New("Scan: invalid UUID format")
+ }
+
+ *uuid = u
+ }
+
+ default:
+ return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
+ }
+
+ return nil
+}
+
+// Value implements sql.Valuer so that UUIDs can be written to databases
+// transparently. Currently, UUIDs map to strings. Please consult
+// database-specific driver documentation for matching types.
+func (uuid UUID) Value() (driver.Value, error) {
+ return uuid.String(), nil
+}
diff --git a/vendor/github.com/pborman/uuid/time.go b/vendor/github.com/pborman/uuid/time.go
new file mode 100644
index 000000000..eedf24219
--- /dev/null
+++ b/vendor/github.com/pborman/uuid/time.go
@@ -0,0 +1,132 @@
+// Copyright 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "encoding/binary"
+ "sync"
+ "time"
+)
+
+// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
+// 1582.
+type Time int64
+
+const (
+ lillian = 2299160 // Julian day of 15 Oct 1582
+ unix = 2440587 // Julian day of 1 Jan 1970
+ epoch = unix - lillian // Days between epochs
+ g1582 = epoch * 86400 // seconds between epochs
+ g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
+)
+
+var (
+ timeMu sync.Mutex
+ lasttime uint64 // last time we returned
+ clock_seq uint16 // clock sequence for this run
+
+ timeNow = time.Now // for testing
+)
+
+// UnixTime converts t the number of seconds and nanoseconds using the Unix
+// epoch of 1 Jan 1970.
+func (t Time) UnixTime() (sec, nsec int64) {
+ sec = int64(t - g1582ns100)
+ nsec = (sec % 10000000) * 100
+ sec /= 10000000
+ return sec, nsec
+}
+
+// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
+// clock sequence as well as adjusting the clock sequence as needed. An error
+// is returned if the current time cannot be determined.
+func GetTime() (Time, uint16, error) {
+ defer timeMu.Unlock()
+ timeMu.Lock()
+ return getTime()
+}
+
+func getTime() (Time, uint16, error) {
+ t := timeNow()
+
+ // If we don't have a clock sequence already, set one.
+ if clock_seq == 0 {
+ setClockSequence(-1)
+ }
+ now := uint64(t.UnixNano()/100) + g1582ns100
+
+ // If time has gone backwards with this clock sequence then we
+ // increment the clock sequence
+ if now <= lasttime {
+ clock_seq = ((clock_seq + 1) & 0x3fff) | 0x8000
+ }
+ lasttime = now
+ return Time(now), clock_seq, nil
+}
+
+// ClockSequence returns the current clock sequence, generating one if not
+// already set. The clock sequence is only used for Version 1 UUIDs.
+//
+// The uuid package does not use global static storage for the clock sequence or
+// the last time a UUID was generated. Unless SetClockSequence a new random
+// clock sequence is generated the first time a clock sequence is requested by
+// ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) sequence is generated
+// for
+func ClockSequence() int {
+ defer timeMu.Unlock()
+ timeMu.Lock()
+ return clockSequence()
+}
+
+func clockSequence() int {
+ if clock_seq == 0 {
+ setClockSequence(-1)
+ }
+ return int(clock_seq & 0x3fff)
+}
+
+// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to
+// -1 causes a new sequence to be generated.
+func SetClockSequence(seq int) {
+ defer timeMu.Unlock()
+ timeMu.Lock()
+ setClockSequence(seq)
+}
+
+func setClockSequence(seq int) {
+ if seq == -1 {
+ var b [2]byte
+ randomBits(b[:]) // clock sequence
+ seq = int(b[0])<<8 | int(b[1])
+ }
+ old_seq := clock_seq
+ clock_seq = uint16(seq&0x3fff) | 0x8000 // Set our variant
+ if old_seq != clock_seq {
+ lasttime = 0
+ }
+}
+
+// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
+// uuid. It returns false if uuid is not valid. The time is only well defined
+// for version 1 and 2 UUIDs.
+func (uuid UUID) Time() (Time, bool) {
+ if len(uuid) != 16 {
+ return 0, false
+ }
+ time := int64(binary.BigEndian.Uint32(uuid[0:4]))
+ time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
+ time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
+ return Time(time), true
+}
+
+// ClockSequence returns the clock sequence encoded in uuid. It returns false
+// if uuid is not valid. The clock sequence is only well defined for version 1
+// and 2 UUIDs.
+func (uuid UUID) ClockSequence() (int, bool) {
+ if len(uuid) != 16 {
+ return 0, false
+ }
+ return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff, true
+}
diff --git a/vendor/github.com/pborman/uuid/util.go b/vendor/github.com/pborman/uuid/util.go
new file mode 100644
index 000000000..fc8e052c7
--- /dev/null
+++ b/vendor/github.com/pborman/uuid/util.go
@@ -0,0 +1,43 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "io"
+)
+
+// randomBits completely fills slice b with random data.
+func randomBits(b []byte) {
+ if _, err := io.ReadFull(rander, b); err != nil {
+ panic(err.Error()) // rand should never fail
+ }
+}
+
+// xvalues returns the value of a byte as a hexadecimal digit or 255.
+var xvalues = [256]byte{
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
+ 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+}
+
+// xtob converts the the first two hex bytes of x into a byte.
+func xtob(x string) (byte, bool) {
+ b1 := xvalues[x[0]]
+ b2 := xvalues[x[1]]
+ return (b1 << 4) | b2, b1 != 255 && b2 != 255
+}
diff --git a/vendor/github.com/pborman/uuid/uuid.go b/vendor/github.com/pborman/uuid/uuid.go
new file mode 100644
index 000000000..82c9e7ee7
--- /dev/null
+++ b/vendor/github.com/pborman/uuid/uuid.go
@@ -0,0 +1,201 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "bytes"
+ "crypto/rand"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "strings"
+)
+
+// Array is a pass-by-value UUID that can be used as an effecient key in a map.
+type Array [16]byte
+
+// UUID converts uuid into a slice.
+func (uuid Array) UUID() UUID {
+ return uuid[:]
+}
+
+// String returns the string representation of uuid,
+// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
+func (uuid Array) String() string {
+ return uuid.UUID().String()
+}
+
+// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
+// 4122.
+type UUID []byte
+
+// A Version represents a UUIDs version.
+type Version byte
+
+// A Variant represents a UUIDs variant.
+type Variant byte
+
+// Constants returned by Variant.
+const (
+ Invalid = Variant(iota) // Invalid UUID
+ RFC4122 // The variant specified in RFC4122
+ Reserved // Reserved, NCS backward compatibility.
+ Microsoft // Reserved, Microsoft Corporation backward compatibility.
+ Future // Reserved for future definition.
+)
+
+var rander = rand.Reader // random function
+
+// New returns a new random (version 4) UUID as a string. It is a convenience
+// function for NewRandom().String().
+func New() string {
+ return NewRandom().String()
+}
+
+// Parse decodes s into a UUID or returns nil. Both the UUID form of
+// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
+// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded.
+func Parse(s string) UUID {
+ if len(s) == 36+9 {
+ if strings.ToLower(s[:9]) != "urn:uuid:" {
+ return nil
+ }
+ s = s[9:]
+ } else if len(s) != 36 {
+ return nil
+ }
+ if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
+ return nil
+ }
+ var uuid [16]byte
+ for i, x := range [16]int{
+ 0, 2, 4, 6,
+ 9, 11,
+ 14, 16,
+ 19, 21,
+ 24, 26, 28, 30, 32, 34} {
+ if v, ok := xtob(s[x:]); !ok {
+ return nil
+ } else {
+ uuid[i] = v
+ }
+ }
+ return uuid[:]
+}
+
+// Equal returns true if uuid1 and uuid2 are equal.
+func Equal(uuid1, uuid2 UUID) bool {
+ return bytes.Equal(uuid1, uuid2)
+}
+
+// Array returns an array representation of uuid that can be used as a map key.
+// Array panics if uuid is not valid.
+func (uuid UUID) Array() Array {
+ if len(uuid) != 16 {
+ panic("invalid uuid")
+ }
+ var a Array
+ copy(a[:], uuid)
+ return a
+}
+
+// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+// , or "" if uuid is invalid.
+func (uuid UUID) String() string {
+ if len(uuid) != 16 {
+ return ""
+ }
+ var buf [36]byte
+ encodeHex(buf[:], uuid)
+ return string(buf[:])
+}
+
+// URN returns the RFC 2141 URN form of uuid,
+// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid.
+func (uuid UUID) URN() string {
+ if len(uuid) != 16 {
+ return ""
+ }
+ var buf [36 + 9]byte
+ copy(buf[:], "urn:uuid:")
+ encodeHex(buf[9:], uuid)
+ return string(buf[:])
+}
+
+func encodeHex(dst []byte, uuid UUID) {
+ hex.Encode(dst[:], uuid[:4])
+ dst[8] = '-'
+ hex.Encode(dst[9:13], uuid[4:6])
+ dst[13] = '-'
+ hex.Encode(dst[14:18], uuid[6:8])
+ dst[18] = '-'
+ hex.Encode(dst[19:23], uuid[8:10])
+ dst[23] = '-'
+ hex.Encode(dst[24:], uuid[10:])
+}
+
+// Variant returns the variant encoded in uuid. It returns Invalid if
+// uuid is invalid.
+func (uuid UUID) Variant() Variant {
+ if len(uuid) != 16 {
+ return Invalid
+ }
+ switch {
+ case (uuid[8] & 0xc0) == 0x80:
+ return RFC4122
+ case (uuid[8] & 0xe0) == 0xc0:
+ return Microsoft
+ case (uuid[8] & 0xe0) == 0xe0:
+ return Future
+ default:
+ return Reserved
+ }
+}
+
+// Version returns the version of uuid. It returns false if uuid is not
+// valid.
+func (uuid UUID) Version() (Version, bool) {
+ if len(uuid) != 16 {
+ return 0, false
+ }
+ return Version(uuid[6] >> 4), true
+}
+
+func (v Version) String() string {
+ if v > 15 {
+ return fmt.Sprintf("BAD_VERSION_%d", v)
+ }
+ return fmt.Sprintf("VERSION_%d", v)
+}
+
+func (v Variant) String() string {
+ switch v {
+ case RFC4122:
+ return "RFC4122"
+ case Reserved:
+ return "Reserved"
+ case Microsoft:
+ return "Microsoft"
+ case Future:
+ return "Future"
+ case Invalid:
+ return "Invalid"
+ }
+ return fmt.Sprintf("BadVariant%d", int(v))
+}
+
+// SetRand sets the random number generator to r, which implents io.Reader.
+// If r.Read returns an error when the package requests random data then
+// a panic will be issued.
+//
+// Calling SetRand with nil sets the random number generator to the default
+// generator.
+func SetRand(r io.Reader) {
+ if r == nil {
+ rander = rand.Reader
+ return
+ }
+ rander = r
+}
diff --git a/vendor/github.com/pborman/uuid/version1.go b/vendor/github.com/pborman/uuid/version1.go
new file mode 100644
index 000000000..0127eacfa
--- /dev/null
+++ b/vendor/github.com/pborman/uuid/version1.go
@@ -0,0 +1,41 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "encoding/binary"
+)
+
+// NewUUID returns a Version 1 UUID based on the current NodeID and clock
+// sequence, and the current time. If the NodeID has not been set by SetNodeID
+// or SetNodeInterface then it will be set automatically. If the NodeID cannot
+// be set NewUUID returns nil. If clock sequence has not been set by
+// SetClockSequence then it will be set automatically. If GetTime fails to
+// return the current NewUUID returns nil.
+func NewUUID() UUID {
+ if nodeID == nil {
+ SetNodeInterface("")
+ }
+
+ now, seq, err := GetTime()
+ if err != nil {
+ return nil
+ }
+
+ uuid := make([]byte, 16)
+
+ time_low := uint32(now & 0xffffffff)
+ time_mid := uint16((now >> 32) & 0xffff)
+ time_hi := uint16((now >> 48) & 0x0fff)
+ time_hi |= 0x1000 // Version 1
+
+ binary.BigEndian.PutUint32(uuid[0:], time_low)
+ binary.BigEndian.PutUint16(uuid[4:], time_mid)
+ binary.BigEndian.PutUint16(uuid[6:], time_hi)
+ binary.BigEndian.PutUint16(uuid[8:], seq)
+ copy(uuid[10:], nodeID)
+
+ return uuid
+}
diff --git a/vendor/github.com/pborman/uuid/version4.go b/vendor/github.com/pborman/uuid/version4.go
new file mode 100644
index 000000000..b3d4a368d
--- /dev/null
+++ b/vendor/github.com/pborman/uuid/version4.go
@@ -0,0 +1,25 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+// Random returns a Random (Version 4) UUID or panics.
+//
+// The strength of the UUIDs is based on the strength of the crypto/rand
+// package.
+//
+// A note about uniqueness derived from from the UUID Wikipedia entry:
+//
+// Randomly generated UUIDs have 122 random bits. One's annual risk of being
+// hit by a meteorite is estimated to be one chance in 17 billion, that
+// means the probability is about 0.00000000006 (6 × 10−11),
+// equivalent to the odds of creating a few tens of trillions of UUIDs in a
+// year and having one duplicate.
+func NewRandom() UUID {
+ uuid := make([]byte, 16)
+ randomBits([]byte(uuid))
+ uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
+ uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
+ return uuid
+}
diff --git a/vendor/github.com/rwcarlsen/goexif/LICENSE b/vendor/github.com/rwcarlsen/goexif/LICENSE
new file mode 100644
index 000000000..aa6250465
--- /dev/null
+++ b/vendor/github.com/rwcarlsen/goexif/LICENSE
@@ -0,0 +1,24 @@
+
+Copyright (c) 2012, Robert Carlsen & Contributors
+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.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/rwcarlsen/goexif/exif/README.md b/vendor/github.com/rwcarlsen/goexif/exif/README.md
new file mode 100644
index 000000000..b3bf5fa0e
--- /dev/null
+++ b/vendor/github.com/rwcarlsen/goexif/exif/README.md
@@ -0,0 +1,4 @@
+
+To regenerate the regression test data, run `go generate` inside the exif
+package directory and commit the changes to *regress_expected_test.go*.
+
diff --git a/vendor/github.com/rwcarlsen/goexif/exif/exif.go b/vendor/github.com/rwcarlsen/goexif/exif/exif.go
new file mode 100644
index 000000000..b420729da
--- /dev/null
+++ b/vendor/github.com/rwcarlsen/goexif/exif/exif.go
@@ -0,0 +1,619 @@
+// Package exif implements decoding of EXIF data as defined in the EXIF 2.2
+// specification (http://www.exif.org/Exif2-2.PDF).
+package exif
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/binary"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "math"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/rwcarlsen/goexif/tiff"
+)
+
+const (
+ jpeg_APP1 = 0xE1
+
+ exifPointer = 0x8769
+ gpsPointer = 0x8825
+ interopPointer = 0xA005
+)
+
+// A decodeError is returned when the image cannot be decoded as a tiff image.
+type decodeError struct {
+ cause error
+}
+
+func (de decodeError) Error() string {
+ return fmt.Sprintf("exif: decode failed (%v) ", de.cause.Error())
+}
+
+// IsShortReadTagValueError identifies a ErrShortReadTagValue error.
+func IsShortReadTagValueError(err error) bool {
+ de, ok := err.(decodeError)
+ if ok {
+ return de.cause == tiff.ErrShortReadTagValue
+ }
+ return false
+}
+
+// A TagNotPresentError is returned when the requested field is not
+// present in the EXIF.
+type TagNotPresentError FieldName
+
+func (tag TagNotPresentError) Error() string {
+ return fmt.Sprintf("exif: tag %q is not present", string(tag))
+}
+
+func IsTagNotPresentError(err error) bool {
+ _, ok := err.(TagNotPresentError)
+ return ok
+}
+
+// Parser allows the registration of custom parsing and field loading
+// in the Decode function.
+type Parser interface {
+ // Parse should read data from x and insert parsed fields into x via
+ // LoadTags.
+ Parse(x *Exif) error
+}
+
+var parsers []Parser
+
+func init() {
+ RegisterParsers(&parser{})
+}
+
+// RegisterParsers registers one or more parsers to be automatically called
+// when decoding EXIF data via the Decode function.
+func RegisterParsers(ps ...Parser) {
+ parsers = append(parsers, ps...)
+}
+
+type parser struct{}
+
+type tiffErrors map[tiffError]string
+
+func (te tiffErrors) Error() string {
+ var allErrors []string
+ for k, v := range te {
+ allErrors = append(allErrors, fmt.Sprintf("%s: %v\n", stagePrefix[k], v))
+ }
+ return strings.Join(allErrors, "\n")
+}
+
+// IsCriticalError, given the error returned by Decode, reports whether the
+// returned *Exif may contain usable information.
+func IsCriticalError(err error) bool {
+ _, ok := err.(tiffErrors)
+ return !ok
+}
+
+// IsExifError reports whether the error happened while decoding the EXIF
+// sub-IFD.
+func IsExifError(err error) bool {
+ if te, ok := err.(tiffErrors); ok {
+ _, isExif := te[loadExif]
+ return isExif
+ }
+ return false
+}
+
+// IsGPSError reports whether the error happened while decoding the GPS sub-IFD.
+func IsGPSError(err error) bool {
+ if te, ok := err.(tiffErrors); ok {
+ _, isGPS := te[loadExif]
+ return isGPS
+ }
+ return false
+}
+
+// IsInteroperabilityError reports whether the error happened while decoding the
+// Interoperability sub-IFD.
+func IsInteroperabilityError(err error) bool {
+ if te, ok := err.(tiffErrors); ok {
+ _, isInterop := te[loadInteroperability]
+ return isInterop
+ }
+ return false
+}
+
+type tiffError int
+
+const (
+ loadExif tiffError = iota
+ loadGPS
+ loadInteroperability
+)
+
+var stagePrefix = map[tiffError]string{
+ loadExif: "loading EXIF sub-IFD",
+ loadGPS: "loading GPS sub-IFD",
+ loadInteroperability: "loading Interoperability sub-IFD",
+}
+
+// Parse reads data from the tiff data in x and populates the tags
+// in x. If parsing a sub-IFD fails, the error is recorded and
+// parsing continues with the remaining sub-IFDs.
+func (p *parser) Parse(x *Exif) error {
+ x.LoadTags(x.Tiff.Dirs[0], exifFields, false)
+
+ // thumbnails
+ if len(x.Tiff.Dirs) >= 2 {
+ x.LoadTags(x.Tiff.Dirs[1], thumbnailFields, false)
+ }
+
+ te := make(tiffErrors)
+
+ // recurse into exif, gps, and interop sub-IFDs
+ if err := loadSubDir(x, ExifIFDPointer, exifFields); err != nil {
+ te[loadExif] = err.Error()
+ }
+ if err := loadSubDir(x, GPSInfoIFDPointer, gpsFields); err != nil {
+ te[loadGPS] = err.Error()
+ }
+
+ if err := loadSubDir(x, InteroperabilityIFDPointer, interopFields); err != nil {
+ te[loadInteroperability] = err.Error()
+ }
+ if len(te) > 0 {
+ return te
+ }
+ return nil
+}
+
+func loadSubDir(x *Exif, ptr FieldName, fieldMap map[uint16]FieldName) error {
+ r := bytes.NewReader(x.Raw)
+
+ tag, err := x.Get(ptr)
+ if err != nil {
+ return nil
+ }
+ offset, err := tag.Int64(0)
+ if err != nil {
+ return nil
+ }
+
+ _, err = r.Seek(offset, 0)
+ if err != nil {
+ return fmt.Errorf("exif: seek to sub-IFD %s failed: %v", ptr, err)
+ }
+ subDir, _, err := tiff.DecodeDir(r, x.Tiff.Order)
+ if err != nil {
+ return fmt.Errorf("exif: sub-IFD %s decode failed: %v", ptr, err)
+ }
+ x.LoadTags(subDir, fieldMap, false)
+ return nil
+}
+
+// Exif provides access to decoded EXIF metadata fields and values.
+type Exif struct {
+ Tiff *tiff.Tiff
+ main map[FieldName]*tiff.Tag
+ Raw []byte
+}
+
+// Decode parses EXIF-encoded data from r and returns a queryable Exif
+// object. After the exif data section is called and the tiff structure
+// decoded, each registered parser is called (in order of registration). If
+// one parser returns an error, decoding terminates and the remaining
+// parsers are not called.
+// The error can be inspected with functions such as IsCriticalError to
+// determine whether the returned object might still be usable.
+func Decode(r io.Reader) (*Exif, error) {
+ // EXIF data in JPEG is stored in the APP1 marker. EXIF data uses the TIFF
+ // format to store data.
+ // If we're parsing a TIFF image, we don't need to strip away any data.
+ // If we're parsing a JPEG image, we need to strip away the JPEG APP1
+ // marker and also the EXIF header.
+
+ header := make([]byte, 4)
+ n, err := r.Read(header)
+ if err != nil {
+ return nil, err
+ }
+ if n < len(header) {
+ return nil, errors.New("exif: short read on header")
+ }
+
+ var isTiff bool
+ switch string(header) {
+ case "II*\x00":
+ // TIFF - Little endian (Intel)
+ isTiff = true
+ case "MM\x00*":
+ // TIFF - Big endian (Motorola)
+ isTiff = true
+ default:
+ // Not TIFF, assume JPEG
+ }
+
+ // Put the header bytes back into the reader.
+ r = io.MultiReader(bytes.NewReader(header), r)
+ var (
+ er *bytes.Reader
+ tif *tiff.Tiff
+ )
+
+ if isTiff {
+ // Functions below need the IFDs from the TIFF data to be stored in a
+ // *bytes.Reader. We use TeeReader to get a copy of the bytes as a
+ // side-effect of tiff.Decode() doing its work.
+ b := &bytes.Buffer{}
+ tr := io.TeeReader(r, b)
+ tif, err = tiff.Decode(tr)
+ er = bytes.NewReader(b.Bytes())
+ } else {
+ // Locate the JPEG APP1 header.
+ var sec *appSec
+ sec, err = newAppSec(jpeg_APP1, r)
+ if err != nil {
+ return nil, err
+ }
+ // Strip away EXIF header.
+ er, err = sec.exifReader()
+ if err != nil {
+ return nil, err
+ }
+ tif, err = tiff.Decode(er)
+ }
+
+ if err != nil {
+ return nil, decodeError{cause: err}
+ }
+
+ er.Seek(0, 0)
+ raw, err := ioutil.ReadAll(er)
+ if err != nil {
+ return nil, decodeError{cause: err}
+ }
+
+ // build an exif structure from the tiff
+ x := &Exif{
+ main: map[FieldName]*tiff.Tag{},
+ Tiff: tif,
+ Raw: raw,
+ }
+
+ for i, p := range parsers {
+ if err := p.Parse(x); err != nil {
+ if _, ok := err.(tiffErrors); ok {
+ return x, err
+ }
+ // This should never happen, as Parse always returns a tiffError
+ // for now, but that could change.
+ return x, fmt.Errorf("exif: parser %v failed (%v)", i, err)
+ }
+ }
+
+ return x, nil
+}
+
+// LoadTags loads tags into the available fields from the tiff Directory
+// using the given tagid-fieldname mapping. Used to load makernote and
+// other meta-data. If showMissing is true, tags in d that are not in the
+// fieldMap will be loaded with the FieldName UnknownPrefix followed by the
+// tag ID (in hex format).
+func (x *Exif) LoadTags(d *tiff.Dir, fieldMap map[uint16]FieldName, showMissing bool) {
+ for _, tag := range d.Tags {
+ name := fieldMap[tag.Id]
+ if name == "" {
+ if !showMissing {
+ continue
+ }
+ name = FieldName(fmt.Sprintf("%v%x", UnknownPrefix, tag.Id))
+ }
+ x.main[name] = tag
+ }
+}
+
+// Get retrieves the EXIF tag for the given field name.
+//
+// If the tag is not known or not present, an error is returned. If the
+// tag name is known, the error will be a TagNotPresentError.
+func (x *Exif) Get(name FieldName) (*tiff.Tag, error) {
+ if tg, ok := x.main[name]; ok {
+ return tg, nil
+ }
+ return nil, TagNotPresentError(name)
+}
+
+// Walker is the interface used to traverse all fields of an Exif object.
+type Walker interface {
+ // Walk is called for each non-nil EXIF field. Returning a non-nil
+ // error aborts the walk/traversal.
+ Walk(name FieldName, tag *tiff.Tag) error
+}
+
+// Walk calls the Walk method of w with the name and tag for every non-nil
+// EXIF field. If w aborts the walk with an error, that error is returned.
+func (x *Exif) Walk(w Walker) error {
+ for name, tag := range x.main {
+ if err := w.Walk(name, tag); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// DateTime returns the EXIF's "DateTimeOriginal" field, which
+// is the creation time of the photo. If not found, it tries
+// the "DateTime" (which is meant as the modtime) instead.
+// The error will be TagNotPresentErr if none of those tags
+// were found, or a generic error if the tag value was
+// not a string, or the error returned by time.Parse.
+//
+// If the EXIF lacks timezone information or GPS time, the returned
+// time's Location will be time.Local.
+func (x *Exif) DateTime() (time.Time, error) {
+ var dt time.Time
+ tag, err := x.Get(DateTimeOriginal)
+ if err != nil {
+ tag, err = x.Get(DateTime)
+ if err != nil {
+ return dt, err
+ }
+ }
+ if tag.Format() != tiff.StringVal {
+ return dt, errors.New("DateTime[Original] not in string format")
+ }
+ exifTimeLayout := "2006:01:02 15:04:05"
+ dateStr := strings.TrimRight(string(tag.Val), "\x00")
+ // TODO(bradfitz,mpl): look for timezone offset, GPS time, etc.
+ // For now, just always return the time.Local timezone.
+ return time.ParseInLocation(exifTimeLayout, dateStr, time.Local)
+}
+
+func ratFloat(num, dem int64) float64 {
+ return float64(num) / float64(dem)
+}
+
+// Tries to parse a Geo degrees value from a string as it was found in some
+// EXIF data.
+// Supported formats so far:
+// - "52,00000,50,00000,34,01180" ==> 52 deg 50'34.0118"
+// Probably due to locale the comma is used as decimal mark as well as the
+// separator of three floats (degrees, minutes, seconds)
+// http://en.wikipedia.org/wiki/Decimal_mark#Hindu.E2.80.93Arabic_numeral_system
+// - "52.0,50.0,34.01180" ==> 52deg50'34.0118"
+// - "52,50,34.01180" ==> 52deg50'34.0118"
+func parseTagDegreesString(s string) (float64, error) {
+ const unparsableErrorFmt = "Unknown coordinate format: %s"
+ isSplitRune := func(c rune) bool {
+ return c == ',' || c == ';'
+ }
+ parts := strings.FieldsFunc(s, isSplitRune)
+ var degrees, minutes, seconds float64
+ var err error
+ switch len(parts) {
+ case 6:
+ degrees, err = strconv.ParseFloat(parts[0]+"."+parts[1], 64)
+ if err != nil {
+ return 0.0, fmt.Errorf(unparsableErrorFmt, s)
+ }
+ minutes, err = strconv.ParseFloat(parts[2]+"."+parts[3], 64)
+ if err != nil {
+ return 0.0, fmt.Errorf(unparsableErrorFmt, s)
+ }
+ minutes = math.Copysign(minutes, degrees)
+ seconds, err = strconv.ParseFloat(parts[4]+"."+parts[5], 64)
+ if err != nil {
+ return 0.0, fmt.Errorf(unparsableErrorFmt, s)
+ }
+ seconds = math.Copysign(seconds, degrees)
+ case 3:
+ degrees, err = strconv.ParseFloat(parts[0], 64)
+ if err != nil {
+ return 0.0, fmt.Errorf(unparsableErrorFmt, s)
+ }
+ minutes, err = strconv.ParseFloat(parts[1], 64)
+ if err != nil {
+ return 0.0, fmt.Errorf(unparsableErrorFmt, s)
+ }
+ minutes = math.Copysign(minutes, degrees)
+ seconds, err = strconv.ParseFloat(parts[2], 64)
+ if err != nil {
+ return 0.0, fmt.Errorf(unparsableErrorFmt, s)
+ }
+ seconds = math.Copysign(seconds, degrees)
+ default:
+ return 0.0, fmt.Errorf(unparsableErrorFmt, s)
+ }
+ return degrees + minutes/60.0 + seconds/3600.0, nil
+}
+
+func parse3Rat2(tag *tiff.Tag) ([3]float64, error) {
+ v := [3]float64{}
+ for i := range v {
+ num, den, err := tag.Rat2(i)
+ if err != nil {
+ return v, err
+ }
+ v[i] = ratFloat(num, den)
+ if tag.Count < uint32(i+2) {
+ break
+ }
+ }
+ return v, nil
+}
+
+func tagDegrees(tag *tiff.Tag) (float64, error) {
+ switch tag.Format() {
+ case tiff.RatVal:
+ // The usual case, according to the Exif spec
+ // (http://www.kodak.com/global/plugins/acrobat/en/service/digCam/exifStandard2.pdf,
+ // sec 4.6.6, p. 52 et seq.)
+ v, err := parse3Rat2(tag)
+ if err != nil {
+ return 0.0, err
+ }
+ return v[0] + v[1]/60 + v[2]/3600.0, nil
+ case tiff.StringVal:
+ // Encountered this weird case with a panorama picture taken with a HTC phone
+ s, err := tag.StringVal()
+ if err != nil {
+ return 0.0, err
+ }
+ return parseTagDegreesString(s)
+ default:
+ // don't know how to parse value, give up
+ return 0.0, fmt.Errorf("Malformed EXIF Tag Degrees")
+ }
+}
+
+// LatLong returns the latitude and longitude of the photo and
+// whether it was present.
+func (x *Exif) LatLong() (lat, long float64, err error) {
+ // All calls of x.Get might return an TagNotPresentError
+ longTag, err := x.Get(FieldName("GPSLongitude"))
+ if err != nil {
+ return
+ }
+ ewTag, err := x.Get(FieldName("GPSLongitudeRef"))
+ if err != nil {
+ return
+ }
+ latTag, err := x.Get(FieldName("GPSLatitude"))
+ if err != nil {
+ return
+ }
+ nsTag, err := x.Get(FieldName("GPSLatitudeRef"))
+ if err != nil {
+ return
+ }
+ if long, err = tagDegrees(longTag); err != nil {
+ return 0, 0, fmt.Errorf("Cannot parse longitude: %v", err)
+ }
+ if lat, err = tagDegrees(latTag); err != nil {
+ return 0, 0, fmt.Errorf("Cannot parse latitude: %v", err)
+ }
+ ew, err := ewTag.StringVal()
+ if err == nil && ew == "W" {
+ long *= -1.0
+ } else if err != nil {
+ return 0, 0, fmt.Errorf("Cannot parse longitude: %v", err)
+ }
+ ns, err := nsTag.StringVal()
+ if err == nil && ns == "S" {
+ lat *= -1.0
+ } else if err != nil {
+ return 0, 0, fmt.Errorf("Cannot parse longitude: %v", err)
+ }
+ return lat, long, nil
+}
+
+// String returns a pretty text representation of the decoded exif data.
+func (x *Exif) String() string {
+ var buf bytes.Buffer
+ for name, tag := range x.main {
+ fmt.Fprintf(&buf, "%s: %s\n", name, tag)
+ }
+ return buf.String()
+}
+
+// JpegThumbnail returns the jpeg thumbnail if it exists. If it doesn't exist,
+// TagNotPresentError will be returned
+func (x *Exif) JpegThumbnail() ([]byte, error) {
+ offset, err := x.Get(ThumbJPEGInterchangeFormat)
+ if err != nil {
+ return nil, err
+ }
+ start, err := offset.Int(0)
+ if err != nil {
+ return nil, err
+ }
+
+ length, err := x.Get(ThumbJPEGInterchangeFormatLength)
+ if err != nil {
+ return nil, err
+ }
+ l, err := length.Int(0)
+ if err != nil {
+ return nil, err
+ }
+
+ return x.Raw[start : start+l], nil
+}
+
+// MarshalJson implements the encoding/json.Marshaler interface providing output of
+// all EXIF fields present (names and values).
+func (x Exif) MarshalJSON() ([]byte, error) {
+ return json.Marshal(x.main)
+}
+
+type appSec struct {
+ marker byte
+ data []byte
+}
+
+// newAppSec finds marker in r and returns the corresponding application data
+// section.
+func newAppSec(marker byte, r io.Reader) (*appSec, error) {
+ br := bufio.NewReader(r)
+ app := &appSec{marker: marker}
+ var dataLen int
+
+ // seek to marker
+ for dataLen == 0 {
+ if _, err := br.ReadBytes(0xFF); err != nil {
+ return nil, err
+ }
+ c, err := br.ReadByte()
+ if err != nil {
+ return nil, err
+ } else if c != marker {
+ continue
+ }
+
+ dataLenBytes := make([]byte, 2)
+ for k,_ := range dataLenBytes {
+ c, err := br.ReadByte()
+ if err != nil {
+ return nil, err
+ }
+ dataLenBytes[k] = c
+ }
+ dataLen = int(binary.BigEndian.Uint16(dataLenBytes)) - 2
+ }
+
+ // read section data
+ nread := 0
+ for nread < dataLen {
+ s := make([]byte, dataLen-nread)
+ n, err := br.Read(s)
+ nread += n
+ if err != nil && nread < dataLen {
+ return nil, err
+ }
+ app.data = append(app.data, s[:n]...)
+ }
+ return app, nil
+}
+
+// reader returns a reader on this appSec.
+func (app *appSec) reader() *bytes.Reader {
+ return bytes.NewReader(app.data)
+}
+
+// exifReader returns a reader on this appSec with the read cursor advanced to
+// the start of the exif's tiff encoded portion.
+func (app *appSec) exifReader() (*bytes.Reader, error) {
+ if len(app.data) < 6 {
+ return nil, errors.New("exif: failed to find exif intro marker")
+ }
+
+ // read/check for exif special mark
+ exif := app.data[:6]
+ if !bytes.Equal(exif, append([]byte("Exif"), 0x00, 0x00)) {
+ return nil, errors.New("exif: failed to find exif intro marker")
+ }
+ return bytes.NewReader(app.data[6:]), nil
+}
diff --git a/vendor/github.com/rwcarlsen/goexif/exif/fields.go b/vendor/github.com/rwcarlsen/goexif/exif/fields.go
new file mode 100644
index 000000000..0388d2390
--- /dev/null
+++ b/vendor/github.com/rwcarlsen/goexif/exif/fields.go
@@ -0,0 +1,293 @@
+package exif
+
+type FieldName string
+
+// UnknownPrefix is used as the first part of field names for decoded tags for
+// which there is no known/supported EXIF field.
+const UnknownPrefix = "UnknownTag_"
+
+// Primary EXIF fields
+const (
+ ImageWidth FieldName = "ImageWidth"
+ ImageLength = "ImageLength" // Image height called Length by EXIF spec
+ BitsPerSample = "BitsPerSample"
+ Compression = "Compression"
+ PhotometricInterpretation = "PhotometricInterpretation"
+ Orientation = "Orientation"
+ SamplesPerPixel = "SamplesPerPixel"
+ PlanarConfiguration = "PlanarConfiguration"
+ YCbCrSubSampling = "YCbCrSubSampling"
+ YCbCrPositioning = "YCbCrPositioning"
+ XResolution = "XResolution"
+ YResolution = "YResolution"
+ ResolutionUnit = "ResolutionUnit"
+ DateTime = "DateTime"
+ ImageDescription = "ImageDescription"
+ Make = "Make"
+ Model = "Model"
+ Software = "Software"
+ Artist = "Artist"
+ Copyright = "Copyright"
+ ExifIFDPointer = "ExifIFDPointer"
+ GPSInfoIFDPointer = "GPSInfoIFDPointer"
+ InteroperabilityIFDPointer = "InteroperabilityIFDPointer"
+ ExifVersion = "ExifVersion"
+ FlashpixVersion = "FlashpixVersion"
+ ColorSpace = "ColorSpace"
+ ComponentsConfiguration = "ComponentsConfiguration"
+ CompressedBitsPerPixel = "CompressedBitsPerPixel"
+ PixelXDimension = "PixelXDimension"
+ PixelYDimension = "PixelYDimension"
+ MakerNote = "MakerNote"
+ UserComment = "UserComment"
+ RelatedSoundFile = "RelatedSoundFile"
+ DateTimeOriginal = "DateTimeOriginal"
+ DateTimeDigitized = "DateTimeDigitized"
+ SubSecTime = "SubSecTime"
+ SubSecTimeOriginal = "SubSecTimeOriginal"
+ SubSecTimeDigitized = "SubSecTimeDigitized"
+ ImageUniqueID = "ImageUniqueID"
+ ExposureTime = "ExposureTime"
+ FNumber = "FNumber"
+ ExposureProgram = "ExposureProgram"
+ SpectralSensitivity = "SpectralSensitivity"
+ ISOSpeedRatings = "ISOSpeedRatings"
+ OECF = "OECF"
+ ShutterSpeedValue = "ShutterSpeedValue"
+ ApertureValue = "ApertureValue"
+ BrightnessValue = "BrightnessValue"
+ ExposureBiasValue = "ExposureBiasValue"
+ MaxApertureValue = "MaxApertureValue"
+ SubjectDistance = "SubjectDistance"
+ MeteringMode = "MeteringMode"
+ LightSource = "LightSource"
+ Flash = "Flash"
+ FocalLength = "FocalLength"
+ SubjectArea = "SubjectArea"
+ FlashEnergy = "FlashEnergy"
+ SpatialFrequencyResponse = "SpatialFrequencyResponse"
+ FocalPlaneXResolution = "FocalPlaneXResolution"
+ FocalPlaneYResolution = "FocalPlaneYResolution"
+ FocalPlaneResolutionUnit = "FocalPlaneResolutionUnit"
+ SubjectLocation = "SubjectLocation"
+ ExposureIndex = "ExposureIndex"
+ SensingMethod = "SensingMethod"
+ FileSource = "FileSource"
+ SceneType = "SceneType"
+ CFAPattern = "CFAPattern"
+ CustomRendered = "CustomRendered"
+ ExposureMode = "ExposureMode"
+ WhiteBalance = "WhiteBalance"
+ DigitalZoomRatio = "DigitalZoomRatio"
+ FocalLengthIn35mmFilm = "FocalLengthIn35mmFilm"
+ SceneCaptureType = "SceneCaptureType"
+ GainControl = "GainControl"
+ Contrast = "Contrast"
+ Saturation = "Saturation"
+ Sharpness = "Sharpness"
+ DeviceSettingDescription = "DeviceSettingDescription"
+ SubjectDistanceRange = "SubjectDistanceRange"
+ LensMake = "LensMake"
+ LensModel = "LensModel"
+)
+
+// thumbnail fields
+const (
+ ThumbJPEGInterchangeFormat = "ThumbJPEGInterchangeFormat" // offset to thumb jpeg SOI
+ ThumbJPEGInterchangeFormatLength = "ThumbJPEGInterchangeFormatLength" // byte length of thumb
+)
+
+// GPS fields
+const (
+ GPSVersionID FieldName = "GPSVersionID"
+ GPSLatitudeRef = "GPSLatitudeRef"
+ GPSLatitude = "GPSLatitude"
+ GPSLongitudeRef = "GPSLongitudeRef"
+ GPSLongitude = "GPSLongitude"
+ GPSAltitudeRef = "GPSAltitudeRef"
+ GPSAltitude = "GPSAltitude"
+ GPSTimeStamp = "GPSTimeStamp"
+ GPSSatelites = "GPSSatelites"
+ GPSStatus = "GPSStatus"
+ GPSMeasureMode = "GPSMeasureMode"
+ GPSDOP = "GPSDOP"
+ GPSSpeedRef = "GPSSpeedRef"
+ GPSSpeed = "GPSSpeed"
+ GPSTrackRef = "GPSTrackRef"
+ GPSTrack = "GPSTrack"
+ GPSImgDirectionRef = "GPSImgDirectionRef"
+ GPSImgDirection = "GPSImgDirection"
+ GPSMapDatum = "GPSMapDatum"
+ GPSDestLatitudeRef = "GPSDestLatitudeRef"
+ GPSDestLatitude = "GPSDestLatitude"
+ GPSDestLongitudeRef = "GPSDestLongitudeRef"
+ GPSDestLongitude = "GPSDestLongitude"
+ GPSDestBearingRef = "GPSDestBearingRef"
+ GPSDestBearing = "GPSDestBearing"
+ GPSDestDistanceRef = "GPSDestDistanceRef"
+ GPSDestDistance = "GPSDestDistance"
+ GPSProcessingMethod = "GPSProcessingMethod"
+ GPSAreaInformation = "GPSAreaInformation"
+ GPSDateStamp = "GPSDateStamp"
+ GPSDifferential = "GPSDifferential"
+)
+
+// interoperability fields
+const (
+ InteroperabilityIndex FieldName = "InteroperabilityIndex"
+)
+
+var exifFields = map[uint16]FieldName{
+ /////////////////////////////////////
+ ////////// IFD 0 ////////////////////
+ /////////////////////////////////////
+
+ // image data structure for the thumbnail
+ 0x0100: ImageWidth,
+ 0x0101: ImageLength,
+ 0x0102: BitsPerSample,
+ 0x0103: Compression,
+ 0x0106: PhotometricInterpretation,
+ 0x0112: Orientation,
+ 0x0115: SamplesPerPixel,
+ 0x011C: PlanarConfiguration,
+ 0x0212: YCbCrSubSampling,
+ 0x0213: YCbCrPositioning,
+ 0x011A: XResolution,
+ 0x011B: YResolution,
+ 0x0128: ResolutionUnit,
+
+ // Other tags
+ 0x0132: DateTime,
+ 0x010E: ImageDescription,
+ 0x010F: Make,
+ 0x0110: Model,
+ 0x0131: Software,
+ 0x013B: Artist,
+ 0x8298: Copyright,
+
+ // private tags
+ exifPointer: ExifIFDPointer,
+
+ /////////////////////////////////////
+ ////////// Exif sub IFD /////////////
+ /////////////////////////////////////
+
+ gpsPointer: GPSInfoIFDPointer,
+ interopPointer: InteroperabilityIFDPointer,
+
+ 0x9000: ExifVersion,
+ 0xA000: FlashpixVersion,
+
+ 0xA001: ColorSpace,
+
+ 0x9101: ComponentsConfiguration,
+ 0x9102: CompressedBitsPerPixel,
+ 0xA002: PixelXDimension,
+ 0xA003: PixelYDimension,
+
+ 0x927C: MakerNote,
+ 0x9286: UserComment,
+
+ 0xA004: RelatedSoundFile,
+ 0x9003: DateTimeOriginal,
+ 0x9004: DateTimeDigitized,
+ 0x9290: SubSecTime,
+ 0x9291: SubSecTimeOriginal,
+ 0x9292: SubSecTimeDigitized,
+
+ 0xA420: ImageUniqueID,
+
+ // picture conditions
+ 0x829A: ExposureTime,
+ 0x829D: FNumber,
+ 0x8822: ExposureProgram,
+ 0x8824: SpectralSensitivity,
+ 0x8827: ISOSpeedRatings,
+ 0x8828: OECF,
+ 0x9201: ShutterSpeedValue,
+ 0x9202: ApertureValue,
+ 0x9203: BrightnessValue,
+ 0x9204: ExposureBiasValue,
+ 0x9205: MaxApertureValue,
+ 0x9206: SubjectDistance,
+ 0x9207: MeteringMode,
+ 0x9208: LightSource,
+ 0x9209: Flash,
+ 0x920A: FocalLength,
+ 0x9214: SubjectArea,
+ 0xA20B: FlashEnergy,
+ 0xA20C: SpatialFrequencyResponse,
+ 0xA20E: FocalPlaneXResolution,
+ 0xA20F: FocalPlaneYResolution,
+ 0xA210: FocalPlaneResolutionUnit,
+ 0xA214: SubjectLocation,
+ 0xA215: ExposureIndex,
+ 0xA217: SensingMethod,
+ 0xA300: FileSource,
+ 0xA301: SceneType,
+ 0xA302: CFAPattern,
+ 0xA401: CustomRendered,
+ 0xA402: ExposureMode,
+ 0xA403: WhiteBalance,
+ 0xA404: DigitalZoomRatio,
+ 0xA405: FocalLengthIn35mmFilm,
+ 0xA406: SceneCaptureType,
+ 0xA407: GainControl,
+ 0xA408: Contrast,
+ 0xA409: Saturation,
+ 0xA40A: Sharpness,
+ 0xA40B: DeviceSettingDescription,
+ 0xA40C: SubjectDistanceRange,
+ 0xA433: LensMake,
+ 0xA434: LensModel,
+}
+
+var gpsFields = map[uint16]FieldName{
+ /////////////////////////////////////
+ //// GPS sub-IFD ////////////////////
+ /////////////////////////////////////
+ 0x0: GPSVersionID,
+ 0x1: GPSLatitudeRef,
+ 0x2: GPSLatitude,
+ 0x3: GPSLongitudeRef,
+ 0x4: GPSLongitude,
+ 0x5: GPSAltitudeRef,
+ 0x6: GPSAltitude,
+ 0x7: GPSTimeStamp,
+ 0x8: GPSSatelites,
+ 0x9: GPSStatus,
+ 0xA: GPSMeasureMode,
+ 0xB: GPSDOP,
+ 0xC: GPSSpeedRef,
+ 0xD: GPSSpeed,
+ 0xE: GPSTrackRef,
+ 0xF: GPSTrack,
+ 0x10: GPSImgDirectionRef,
+ 0x11: GPSImgDirection,
+ 0x12: GPSMapDatum,
+ 0x13: GPSDestLatitudeRef,
+ 0x14: GPSDestLatitude,
+ 0x15: GPSDestLongitudeRef,
+ 0x16: GPSDestLongitude,
+ 0x17: GPSDestBearingRef,
+ 0x18: GPSDestBearing,
+ 0x19: GPSDestDistanceRef,
+ 0x1A: GPSDestDistance,
+ 0x1B: GPSProcessingMethod,
+ 0x1C: GPSAreaInformation,
+ 0x1D: GPSDateStamp,
+ 0x1E: GPSDifferential,
+}
+
+var interopFields = map[uint16]FieldName{
+ /////////////////////////////////////
+ //// Interoperability sub-IFD ///////
+ /////////////////////////////////////
+ 0x1: InteroperabilityIndex,
+}
+
+var thumbnailFields = map[uint16]FieldName{
+ 0x0201: ThumbJPEGInterchangeFormat,
+ 0x0202: ThumbJPEGInterchangeFormatLength,
+}
diff --git a/vendor/github.com/rwcarlsen/goexif/exif/regen_regress.go b/vendor/github.com/rwcarlsen/goexif/exif/regen_regress.go
new file mode 100644
index 000000000..17bac5287
--- /dev/null
+++ b/vendor/github.com/rwcarlsen/goexif/exif/regen_regress.go
@@ -0,0 +1,79 @@
+// +build ignore
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/rwcarlsen/goexif/exif"
+ "github.com/rwcarlsen/goexif/tiff"
+)
+
+func main() {
+ flag.Parse()
+ fname := flag.Arg(0)
+
+ dst, err := os.Create(fname)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer dst.Close()
+
+ dir, err := os.Open("samples")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer dir.Close()
+
+ names, err := dir.Readdirnames(0)
+ if err != nil {
+ log.Fatal(err)
+ }
+ for i, name := range names {
+ names[i] = filepath.Join("samples", name)
+ }
+ makeExpected(names, dst)
+}
+
+func makeExpected(files []string, w io.Writer) {
+ fmt.Fprintf(w, "package exif\n\n")
+ fmt.Fprintf(w, "var regressExpected = map[string]map[FieldName]string{\n")
+
+ for _, name := range files {
+ f, err := os.Open(name)
+ if err != nil {
+ continue
+ }
+
+ x, err := exif.Decode(f)
+ if err != nil {
+ f.Close()
+ continue
+ }
+
+ fmt.Fprintf(w, "\"%v\": map[FieldName]string{\n", filepath.Base(name))
+ x.Walk(&regresswalk{w})
+ fmt.Fprintf(w, "},\n")
+ f.Close()
+ }
+ fmt.Fprintf(w, "}")
+}
+
+type regresswalk struct {
+ wr io.Writer
+}
+
+func (w *regresswalk) Walk(name exif.FieldName, tag *tiff.Tag) error {
+ if strings.HasPrefix(string(name), exif.UnknownPrefix) {
+ fmt.Fprintf(w.wr, "\"%v\": `%v`,\n", name, tag.String())
+ } else {
+ fmt.Fprintf(w.wr, "%v: `%v`,\n", name, tag.String())
+ }
+ return nil
+}
diff --git a/vendor/github.com/rwcarlsen/goexif/exif/sample1.jpg b/vendor/github.com/rwcarlsen/goexif/exif/sample1.jpg
new file mode 100644
index 000000000..87bcf8e33
--- /dev/null
+++ b/vendor/github.com/rwcarlsen/goexif/exif/sample1.jpg
Binary files differ
diff --git a/vendor/github.com/rwcarlsen/goexif/tiff/sample1.tif b/vendor/github.com/rwcarlsen/goexif/tiff/sample1.tif
new file mode 100644
index 000000000..fe51399c5
--- /dev/null
+++ b/vendor/github.com/rwcarlsen/goexif/tiff/sample1.tif
Binary files differ
diff --git a/vendor/github.com/rwcarlsen/goexif/tiff/tag.go b/vendor/github.com/rwcarlsen/goexif/tiff/tag.go
new file mode 100644
index 000000000..66b68e334
--- /dev/null
+++ b/vendor/github.com/rwcarlsen/goexif/tiff/tag.go
@@ -0,0 +1,438 @@
+package tiff
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "math/big"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+// Format specifies the Go type equivalent used to represent the basic
+// tiff data types.
+type Format int
+
+const (
+ IntVal Format = iota
+ FloatVal
+ RatVal
+ StringVal
+ UndefVal
+ OtherVal
+)
+
+var ErrShortReadTagValue = errors.New("tiff: short read of tag value")
+
+var formatNames = map[Format]string{
+ IntVal: "int",
+ FloatVal: "float",
+ RatVal: "rational",
+ StringVal: "string",
+ UndefVal: "undefined",
+ OtherVal: "other",
+}
+
+// DataType represents the basic tiff tag data types.
+type DataType uint16
+
+const (
+ DTByte DataType = 1
+ DTAscii = 2
+ DTShort = 3
+ DTLong = 4
+ DTRational = 5
+ DTSByte = 6
+ DTUndefined = 7
+ DTSShort = 8
+ DTSLong = 9
+ DTSRational = 10
+ DTFloat = 11
+ DTDouble = 12
+)
+
+var typeNames = map[DataType]string{
+ DTByte: "byte",
+ DTAscii: "ascii",
+ DTShort: "short",
+ DTLong: "long",
+ DTRational: "rational",
+ DTSByte: "signed byte",
+ DTUndefined: "undefined",
+ DTSShort: "signed short",
+ DTSLong: "signed long",
+ DTSRational: "signed rational",
+ DTFloat: "float",
+ DTDouble: "double",
+}
+
+// typeSize specifies the size in bytes of each type.
+var typeSize = map[DataType]uint32{
+ DTByte: 1,
+ DTAscii: 1,
+ DTShort: 2,
+ DTLong: 4,
+ DTRational: 8,
+ DTSByte: 1,
+ DTUndefined: 1,
+ DTSShort: 2,
+ DTSLong: 4,
+ DTSRational: 8,
+ DTFloat: 4,
+ DTDouble: 8,
+}
+
+// Tag reflects the parsed content of a tiff IFD tag.
+type Tag struct {
+ // Id is the 2-byte tiff tag identifier.
+ Id uint16
+ // Type is an integer (1 through 12) indicating the tag value's data type.
+ Type DataType
+ // Count is the number of type Type stored in the tag's value (i.e. the
+ // tag's value is an array of type Type and length Count).
+ Count uint32
+ // Val holds the bytes that represent the tag's value.
+ Val []byte
+ // ValOffset holds byte offset of the tag value w.r.t. the beginning of the
+ // reader it was decoded from. Zero if the tag value fit inside the offset
+ // field.
+ ValOffset uint32
+
+ order binary.ByteOrder
+ intVals []int64
+ floatVals []float64
+ ratVals [][]int64
+ strVal string
+ format Format
+}
+
+// DecodeTag parses a tiff-encoded IFD tag from r and returns a Tag object. The
+// first read from r should be the first byte of the tag. ReadAt offsets should
+// generally be relative to the beginning of the tiff structure (not relative
+// to the beginning of the tag).
+func DecodeTag(r ReadAtReader, order binary.ByteOrder) (*Tag, error) {
+ t := new(Tag)
+ t.order = order
+
+ err := binary.Read(r, order, &t.Id)
+ if err != nil {
+ return nil, errors.New("tiff: tag id read failed: " + err.Error())
+ }
+
+ err = binary.Read(r, order, &t.Type)
+ if err != nil {
+ return nil, errors.New("tiff: tag type read failed: " + err.Error())
+ }
+
+ err = binary.Read(r, order, &t.Count)
+ if err != nil {
+ return nil, errors.New("tiff: tag component count read failed: " + err.Error())
+ }
+
+ // There seems to be a relatively common corrupt tag which has a Count of
+ // MaxUint32. This is probably not a valid value, so return early.
+ if t.Count == 1<<32-1 {
+ return t, errors.New("invalid Count offset in tag")
+ }
+
+ valLen := typeSize[t.Type] * t.Count
+ if valLen == 0 {
+ return t, errors.New("zero length tag value")
+ }
+
+ if valLen > 4 {
+ binary.Read(r, order, &t.ValOffset)
+
+ // Use a bytes.Buffer so we don't allocate a huge slice if the tag
+ // is corrupt.
+ var buff bytes.Buffer
+ sr := io.NewSectionReader(r, int64(t.ValOffset), int64(valLen))
+ n, err := io.Copy(&buff, sr)
+ if err != nil {
+ return t, errors.New("tiff: tag value read failed: " + err.Error())
+ } else if n != int64(valLen) {
+ return t, ErrShortReadTagValue
+ }
+ t.Val = buff.Bytes()
+
+ } else {
+ val := make([]byte, valLen)
+ if _, err = io.ReadFull(r, val); err != nil {
+ return t, errors.New("tiff: tag offset read failed: " + err.Error())
+ }
+ // ignore padding.
+ if _, err = io.ReadFull(r, make([]byte, 4-valLen)); err != nil {
+ return t, errors.New("tiff: tag offset read failed: " + err.Error())
+ }
+
+ t.Val = val
+ }
+
+ return t, t.convertVals()
+}
+
+func (t *Tag) convertVals() error {
+ r := bytes.NewReader(t.Val)
+
+ switch t.Type {
+ case DTAscii:
+ if len(t.Val) > 0 {
+ t.strVal = string(t.Val[:len(t.Val)-1]) // ignore the last byte (NULL).
+ }
+ case DTByte:
+ var v uint8
+ t.intVals = make([]int64, int(t.Count))
+ for i := range t.intVals {
+ err := binary.Read(r, t.order, &v)
+ if err != nil {
+ return err
+ }
+ t.intVals[i] = int64(v)
+ }
+ case DTShort:
+ var v uint16
+ t.intVals = make([]int64, int(t.Count))
+ for i := range t.intVals {
+ err := binary.Read(r, t.order, &v)
+ if err != nil {
+ return err
+ }
+ t.intVals[i] = int64(v)
+ }
+ case DTLong:
+ var v uint32
+ t.intVals = make([]int64, int(t.Count))
+ for i := range t.intVals {
+ err := binary.Read(r, t.order, &v)
+ if err != nil {
+ return err
+ }
+ t.intVals[i] = int64(v)
+ }
+ case DTSByte:
+ var v int8
+ t.intVals = make([]int64, int(t.Count))
+ for i := range t.intVals {
+ err := binary.Read(r, t.order, &v)
+ if err != nil {
+ return err
+ }
+ t.intVals[i] = int64(v)
+ }
+ case DTSShort:
+ var v int16
+ t.intVals = make([]int64, int(t.Count))
+ for i := range t.intVals {
+ err := binary.Read(r, t.order, &v)
+ if err != nil {
+ return err
+ }
+ t.intVals[i] = int64(v)
+ }
+ case DTSLong:
+ var v int32
+ t.intVals = make([]int64, int(t.Count))
+ for i := range t.intVals {
+ err := binary.Read(r, t.order, &v)
+ if err != nil {
+ return err
+ }
+ t.intVals[i] = int64(v)
+ }
+ case DTRational:
+ t.ratVals = make([][]int64, int(t.Count))
+ for i := range t.ratVals {
+ var n, d uint32
+ err := binary.Read(r, t.order, &n)
+ if err != nil {
+ return err
+ }
+ err = binary.Read(r, t.order, &d)
+ if err != nil {
+ return err
+ }
+ t.ratVals[i] = []int64{int64(n), int64(d)}
+ }
+ case DTSRational:
+ t.ratVals = make([][]int64, int(t.Count))
+ for i := range t.ratVals {
+ var n, d int32
+ err := binary.Read(r, t.order, &n)
+ if err != nil {
+ return err
+ }
+ err = binary.Read(r, t.order, &d)
+ if err != nil {
+ return err
+ }
+ t.ratVals[i] = []int64{int64(n), int64(d)}
+ }
+ case DTFloat: // float32
+ t.floatVals = make([]float64, int(t.Count))
+ for i := range t.floatVals {
+ var v float32
+ err := binary.Read(r, t.order, &v)
+ if err != nil {
+ return err
+ }
+ t.floatVals[i] = float64(v)
+ }
+ case DTDouble:
+ t.floatVals = make([]float64, int(t.Count))
+ for i := range t.floatVals {
+ var u float64
+ err := binary.Read(r, t.order, &u)
+ if err != nil {
+ return err
+ }
+ t.floatVals[i] = u
+ }
+ }
+
+ switch t.Type {
+ case DTByte, DTShort, DTLong, DTSByte, DTSShort, DTSLong:
+ t.format = IntVal
+ case DTRational, DTSRational:
+ t.format = RatVal
+ case DTFloat, DTDouble:
+ t.format = FloatVal
+ case DTAscii:
+ t.format = StringVal
+ case DTUndefined:
+ t.format = UndefVal
+ default:
+ t.format = OtherVal
+ }
+
+ return nil
+}
+
+// Format returns a value indicating which method can be called to retrieve the
+// tag's value properly typed (e.g. integer, rational, etc.).
+func (t *Tag) Format() Format { return t.format }
+
+func (t *Tag) typeErr(to Format) error {
+ return &wrongFmtErr{typeNames[t.Type], formatNames[to]}
+}
+
+// Rat returns the tag's i'th value as a rational number. It returns a nil and
+// an error if this tag's Format is not RatVal. It panics for zero deminators
+// or if i is out of range.
+func (t *Tag) Rat(i int) (*big.Rat, error) {
+ n, d, err := t.Rat2(i)
+ if err != nil {
+ return nil, err
+ }
+ return big.NewRat(n, d), nil
+}
+
+// Rat2 returns the tag's i'th value as a rational number represented by a
+// numerator-denominator pair. It returns an error if the tag's Format is not
+// RatVal. It panics if i is out of range.
+func (t *Tag) Rat2(i int) (num, den int64, err error) {
+ if t.format != RatVal {
+ return 0, 0, t.typeErr(RatVal)
+ }
+ return t.ratVals[i][0], t.ratVals[i][1], nil
+}
+
+// Int64 returns the tag's i'th value as an integer. It returns an error if the
+// tag's Format is not IntVal. It panics if i is out of range.
+func (t *Tag) Int64(i int) (int64, error) {
+ if t.format != IntVal {
+ return 0, t.typeErr(IntVal)
+ }
+ return t.intVals[i], nil
+}
+
+// Int returns the tag's i'th value as an integer. It returns an error if the
+// tag's Format is not IntVal. It panics if i is out of range.
+func (t *Tag) Int(i int) (int, error) {
+ if t.format != IntVal {
+ return 0, t.typeErr(IntVal)
+ }
+ return int(t.intVals[i]), nil
+}
+
+// Float returns the tag's i'th value as a float. It returns an error if the
+// tag's Format is not IntVal. It panics if i is out of range.
+func (t *Tag) Float(i int) (float64, error) {
+ if t.format != FloatVal {
+ return 0, t.typeErr(FloatVal)
+ }
+ return t.floatVals[i], nil
+}
+
+// StringVal returns the tag's value as a string. It returns an error if the
+// tag's Format is not StringVal. It panics if i is out of range.
+func (t *Tag) StringVal() (string, error) {
+ if t.format != StringVal {
+ return "", t.typeErr(StringVal)
+ }
+ return t.strVal, nil
+}
+
+// String returns a nicely formatted version of the tag.
+func (t *Tag) String() string {
+ data, err := t.MarshalJSON()
+ if err != nil {
+ return "ERROR: " + err.Error()
+ }
+
+ if t.Count == 1 {
+ return strings.Trim(fmt.Sprintf("%s", data), "[]")
+ }
+ return fmt.Sprintf("%s", data)
+}
+
+func (t *Tag) MarshalJSON() ([]byte, error) {
+ switch t.format {
+ case StringVal, UndefVal:
+ return nullString(t.Val), nil
+ case OtherVal:
+ return []byte(fmt.Sprintf("unknown tag type '%v'", t.Type)), nil
+ }
+
+ rv := []string{}
+ for i := 0; i < int(t.Count); i++ {
+ switch t.format {
+ case RatVal:
+ n, d, _ := t.Rat2(i)
+ rv = append(rv, fmt.Sprintf(`"%v/%v"`, n, d))
+ case FloatVal:
+ v, _ := t.Float(i)
+ rv = append(rv, fmt.Sprintf("%v", v))
+ case IntVal:
+ v, _ := t.Int(i)
+ rv = append(rv, fmt.Sprintf("%v", v))
+ }
+ }
+ return []byte(fmt.Sprintf(`[%s]`, strings.Join(rv, ","))), nil
+}
+
+func nullString(in []byte) []byte {
+ rv := bytes.Buffer{}
+ rv.WriteByte('"')
+ for _, b := range in {
+ if unicode.IsPrint(rune(b)) {
+ rv.WriteByte(b)
+ }
+ }
+ rv.WriteByte('"')
+ rvb := rv.Bytes()
+ if utf8.Valid(rvb) {
+ return rvb
+ }
+ return []byte(`""`)
+}
+
+type wrongFmtErr struct {
+ From, To string
+}
+
+func (e *wrongFmtErr) Error() string {
+ return fmt.Sprintf("cannot convert tag type '%v' into '%v'", e.From, e.To)
+}
diff --git a/vendor/github.com/rwcarlsen/goexif/tiff/tiff.go b/vendor/github.com/rwcarlsen/goexif/tiff/tiff.go
new file mode 100644
index 000000000..771e91878
--- /dev/null
+++ b/vendor/github.com/rwcarlsen/goexif/tiff/tiff.go
@@ -0,0 +1,153 @@
+// Package tiff implements TIFF decoding as defined in TIFF 6.0 specification at
+// http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
+package tiff
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+)
+
+// ReadAtReader is used when decoding Tiff tags and directories
+type ReadAtReader interface {
+ io.Reader
+ io.ReaderAt
+}
+
+// Tiff provides access to a decoded tiff data structure.
+type Tiff struct {
+ // Dirs is an ordered slice of the tiff's Image File Directories (IFDs).
+ // The IFD at index 0 is IFD0.
+ Dirs []*Dir
+ // The tiff's byte-encoding (i.e. big/little endian).
+ Order binary.ByteOrder
+}
+
+// Decode parses tiff-encoded data from r and returns a Tiff struct that
+// reflects the structure and content of the tiff data. The first read from r
+// should be the first byte of the tiff-encoded data and not necessarily the
+// first byte of an os.File object.
+func Decode(r io.Reader) (*Tiff, error) {
+ data, err := ioutil.ReadAll(r)
+ if err != nil {
+ return nil, errors.New("tiff: could not read data")
+ }
+ buf := bytes.NewReader(data)
+
+ t := new(Tiff)
+
+ // read byte order
+ bo := make([]byte, 2)
+ if _, err = io.ReadFull(buf, bo); err != nil {
+ return nil, errors.New("tiff: could not read tiff byte order")
+ }
+ if string(bo) == "II" {
+ t.Order = binary.LittleEndian
+ } else if string(bo) == "MM" {
+ t.Order = binary.BigEndian
+ } else {
+ return nil, errors.New("tiff: could not read tiff byte order")
+ }
+
+ // check for special tiff marker
+ var sp int16
+ err = binary.Read(buf, t.Order, &sp)
+ if err != nil || 42 != sp {
+ return nil, errors.New("tiff: could not find special tiff marker")
+ }
+
+ // load offset to first IFD
+ var offset int32
+ err = binary.Read(buf, t.Order, &offset)
+ if err != nil {
+ return nil, errors.New("tiff: could not read offset to first IFD")
+ }
+
+ // load IFD's
+ var d *Dir
+ prev := offset
+ for offset != 0 {
+ // seek to offset
+ _, err := buf.Seek(int64(offset), 0)
+ if err != nil {
+ return nil, errors.New("tiff: seek to IFD failed")
+ }
+
+ if buf.Len() == 0 {
+ return nil, errors.New("tiff: seek offset after EOF")
+ }
+
+ // load the dir
+ d, offset, err = DecodeDir(buf, t.Order)
+ if err != nil {
+ return nil, err
+ }
+
+ if offset == prev {
+ return nil, errors.New("tiff: recursive IFD")
+ }
+ prev = offset
+
+ t.Dirs = append(t.Dirs, d)
+ }
+
+ return t, nil
+}
+
+func (tf *Tiff) String() string {
+ var buf bytes.Buffer
+ fmt.Fprint(&buf, "Tiff{")
+ for _, d := range tf.Dirs {
+ fmt.Fprintf(&buf, "%s, ", d.String())
+ }
+ fmt.Fprintf(&buf, "}")
+ return buf.String()
+}
+
+// Dir provides access to the parsed content of a tiff Image File Directory (IFD).
+type Dir struct {
+ Tags []*Tag
+}
+
+// DecodeDir parses a tiff-encoded IFD from r and returns a Dir object. offset
+// is the offset to the next IFD. The first read from r should be at the first
+// byte of the IFD. ReadAt offsets should generally be relative to the
+// beginning of the tiff structure (not relative to the beginning of the IFD).
+func DecodeDir(r ReadAtReader, order binary.ByteOrder) (d *Dir, offset int32, err error) {
+ d = new(Dir)
+
+ // get num of tags in ifd
+ var nTags int16
+ err = binary.Read(r, order, &nTags)
+ if err != nil {
+ return nil, 0, errors.New("tiff: failed to read IFD tag count: " + err.Error())
+ }
+
+ // load tags
+ for n := 0; n < int(nTags); n++ {
+ t, err := DecodeTag(r, order)
+ if err != nil {
+ return nil, 0, err
+ }
+ d.Tags = append(d.Tags, t)
+ }
+
+ // get offset to next ifd
+ err = binary.Read(r, order, &offset)
+ if err != nil {
+ return nil, 0, errors.New("tiff: falied to read offset to next IFD: " + err.Error())
+ }
+
+ return d, offset, nil
+}
+
+func (d *Dir) String() string {
+ s := "Dir{"
+ for _, t := range d.Tags {
+ s += t.String() + ", "
+ }
+ return s + "}"
+}
diff --git a/vendor/github.com/vaughan0/go-ini/LICENSE b/vendor/github.com/vaughan0/go-ini/LICENSE
new file mode 100644
index 000000000..968b45384
--- /dev/null
+++ b/vendor/github.com/vaughan0/go-ini/LICENSE
@@ -0,0 +1,14 @@
+Copyright (c) 2013 Vaughan Newton
+
+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/github.com/vaughan0/go-ini/README.md b/vendor/github.com/vaughan0/go-ini/README.md
new file mode 100644
index 000000000..d5cd4e74b
--- /dev/null
+++ b/vendor/github.com/vaughan0/go-ini/README.md
@@ -0,0 +1,70 @@
+go-ini
+======
+
+INI parsing library for Go (golang).
+
+View the API documentation [here](http://godoc.org/github.com/vaughan0/go-ini).
+
+Usage
+-----
+
+Parse an INI file:
+
+```go
+import "github.com/vaughan0/go-ini"
+
+file, err := ini.LoadFile("myfile.ini")
+```
+
+Get data from the parsed file:
+
+```go
+name, ok := file.Get("person", "name")
+if !ok {
+ panic("'name' variable missing from 'person' section")
+}
+```
+
+Iterate through values in a section:
+
+```go
+for key, value := range file["mysection"] {
+ fmt.Printf("%s => %s\n", key, value)
+}
+```
+
+Iterate through sections in a file:
+
+```go
+for name, section := range file {
+ fmt.Printf("Section name: %s\n", name)
+}
+```
+
+File Format
+-----------
+
+INI files are parsed by go-ini line-by-line. Each line may be one of the following:
+
+ * A section definition: [section-name]
+ * A property: key = value
+ * A comment: #blahblah _or_ ;blahblah
+ * Blank. The line will be ignored.
+
+Properties defined before any section headers are placed in the default section, which has
+the empty string as it's key.
+
+Example:
+
+```ini
+# I am a comment
+; So am I!
+
+[apples]
+colour = red or green
+shape = applish
+
+[oranges]
+shape = square
+colour = blue
+```
diff --git a/vendor/github.com/vaughan0/go-ini/ini.go b/vendor/github.com/vaughan0/go-ini/ini.go
new file mode 100644
index 000000000..81aeb32f8
--- /dev/null
+++ b/vendor/github.com/vaughan0/go-ini/ini.go
@@ -0,0 +1,123 @@
+// Package ini provides functions for parsing INI configuration files.
+package ini
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "regexp"
+ "strings"
+)
+
+var (
+ sectionRegex = regexp.MustCompile(`^\[(.*)\]$`)
+ assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`)
+)
+
+// ErrSyntax is returned when there is a syntax error in an INI file.
+type ErrSyntax struct {
+ Line int
+ Source string // The contents of the erroneous line, without leading or trailing whitespace
+}
+
+func (e ErrSyntax) Error() string {
+ return fmt.Sprintf("invalid INI syntax on line %d: %s", e.Line, e.Source)
+}
+
+// A File represents a parsed INI file.
+type File map[string]Section
+
+// A Section represents a single section of an INI file.
+type Section map[string]string
+
+// Returns a named Section. A Section will be created if one does not already exist for the given name.
+func (f File) Section(name string) Section {
+ section := f[name]
+ if section == nil {
+ section = make(Section)
+ f[name] = section
+ }
+ return section
+}
+
+// Looks up a value for a key in a section and returns that value, along with a boolean result similar to a map lookup.
+func (f File) Get(section, key string) (value string, ok bool) {
+ if s := f[section]; s != nil {
+ value, ok = s[key]
+ }
+ return
+}
+
+// Loads INI data from a reader and stores the data in the File.
+func (f File) Load(in io.Reader) (err error) {
+ bufin, ok := in.(*bufio.Reader)
+ if !ok {
+ bufin = bufio.NewReader(in)
+ }
+ return parseFile(bufin, f)
+}
+
+// Loads INI data from a named file and stores the data in the File.
+func (f File) LoadFile(file string) (err error) {
+ in, err := os.Open(file)
+ if err != nil {
+ return
+ }
+ defer in.Close()
+ return f.Load(in)
+}
+
+func parseFile(in *bufio.Reader, file File) (err error) {
+ section := ""
+ lineNum := 0
+ for done := false; !done; {
+ var line string
+ if line, err = in.ReadString('\n'); err != nil {
+ if err == io.EOF {
+ done = true
+ } else {
+ return
+ }
+ }
+ lineNum++
+ line = strings.TrimSpace(line)
+ if len(line) == 0 {
+ // Skip blank lines
+ continue
+ }
+ if line[0] == ';' || line[0] == '#' {
+ // Skip comments
+ continue
+ }
+
+ if groups := assignRegex.FindStringSubmatch(line); groups != nil {
+ key, val := groups[1], groups[2]
+ key, val = strings.TrimSpace(key), strings.TrimSpace(val)
+ file.Section(section)[key] = val
+ } else if groups := sectionRegex.FindStringSubmatch(line); groups != nil {
+ name := strings.TrimSpace(groups[1])
+ section = name
+ // Create the section if it does not exist
+ file.Section(section)
+ } else {
+ return ErrSyntax{lineNum, line}
+ }
+
+ }
+ return nil
+}
+
+// Loads and returns a File from a reader.
+func Load(in io.Reader) (File, error) {
+ file := make(File)
+ err := file.Load(in)
+ return file, err
+}
+
+// Loads and returns an INI File from a file on disk.
+func LoadFile(filename string) (File, error) {
+ file := make(File)
+ err := file.LoadFile(filename)
+ return file, err
+}
diff --git a/vendor/github.com/vaughan0/go-ini/test.ini b/vendor/github.com/vaughan0/go-ini/test.ini
new file mode 100644
index 000000000..d13c999e2
--- /dev/null
+++ b/vendor/github.com/vaughan0/go-ini/test.ini
@@ -0,0 +1,2 @@
+[default]
+stuff = things