From 02a8b76a5861b010803d54ae255c2c01ce685e2c Mon Sep 17 00:00:00 2001
From: Tulir Asokan <tulir@maunium.net>
Date: Fri, 6 Jul 2018 15:50:45 +0300
Subject: [PATCH] Add dep

---
 Gopkg.lock                                    |   86 +
 Gopkg.toml                                    |   46 +
 vendor/github.com/golang/protobuf/AUTHORS     |    3 +
 .../github.com/golang/protobuf/CONTRIBUTORS   |    3 +
 vendor/github.com/golang/protobuf/LICENSE     |   31 +
 .../github.com/golang/protobuf/proto/clone.go |  253 ++
 .../golang/protobuf/proto/decode.go           |  428 ++
 .../golang/protobuf/proto/discard.go          |  350 ++
 .../golang/protobuf/proto/encode.go           |  221 +
 .../github.com/golang/protobuf/proto/equal.go |  300 ++
 .../golang/protobuf/proto/extensions.go       |  543 +++
 .../github.com/golang/protobuf/proto/lib.go   |  921 ++++
 .../golang/protobuf/proto/message_set.go      |  314 ++
 .../golang/protobuf/proto/pointer_reflect.go  |  357 ++
 .../golang/protobuf/proto/pointer_unsafe.go   |  308 ++
 .../golang/protobuf/proto/properties.go       |  544 +++
 .../golang/protobuf/proto/table_marshal.go    | 2681 ++++++++++++
 .../golang/protobuf/proto/table_merge.go      |  654 +++
 .../golang/protobuf/proto/table_unmarshal.go  | 1967 +++++++++
 .../github.com/golang/protobuf/proto/text.go  |  843 ++++
 .../golang/protobuf/proto/text_parser.go      |  880 ++++
 .../github.com/google/go-querystring/LICENSE  |   27 +
 .../google/go-querystring/query/encode.go     |  320 ++
 .../html-strip-tags-go/.codeclimate.yml       |   11 +
 .../grokify/html-strip-tags-go/.travis.yml    |   30 +
 .../grokify/html-strip-tags-go/LICENSE        |   27 +
 .../grokify/html-strip-tags-go/PATENTS        |   22 +
 .../grokify/html-strip-tags-go/README.md      |   52 +
 .../grokify/html-strip-tags-go/strip.go       | 3712 +++++++++++++++++
 vendor/github.com/xanzy/go-gitlab/.gitignore  |   24 +
 vendor/github.com/xanzy/go-gitlab/.travis.yml |   27 +
 .../github.com/xanzy/go-gitlab/CHANGELOG.md   |   27 +
 vendor/github.com/xanzy/go-gitlab/LICENSE     |  202 +
 vendor/github.com/xanzy/go-gitlab/README.md   |  171 +
 .../xanzy/go-gitlab/award_emojis.go           |  468 +++
 vendor/github.com/xanzy/go-gitlab/boards.go   |  261 ++
 vendor/github.com/xanzy/go-gitlab/branches.go |  238 ++
 .../xanzy/go-gitlab/broadcast_messages.go     |  172 +
 .../xanzy/go-gitlab/build_variables.go        |  173 +
 vendor/github.com/xanzy/go-gitlab/commits.go  |  448 ++
 .../github.com/xanzy/go-gitlab/deploy_keys.go |  201 +
 .../github.com/xanzy/go-gitlab/deployments.go |  121 +
 .../xanzy/go-gitlab/environments.go           |  166 +
 .../github.com/xanzy/go-gitlab/event_types.go |  649 +++
 vendor/github.com/xanzy/go-gitlab/events.go   |  147 +
 .../xanzy/go-gitlab/feature_flags.go          |   79 +
 .../xanzy/go-gitlab/gitignore_templates.go    |   84 +
 vendor/github.com/xanzy/go-gitlab/gitlab.go   |  836 ++++
 .../xanzy/go-gitlab/group_members.go          |  195 +
 .../xanzy/go-gitlab/group_milestones.go       |  250 ++
 vendor/github.com/xanzy/go-gitlab/groups.go   |  307 ++
 .../github.com/xanzy/go-gitlab/issue_links.go |  128 +
 vendor/github.com/xanzy/go-gitlab/issues.go   |  436 ++
 vendor/github.com/xanzy/go-gitlab/jobs.go     |  350 ++
 vendor/github.com/xanzy/go-gitlab/labels.go   |  228 +
 .../go-gitlab/merge_request_approvals.go      |  125 +
 .../xanzy/go-gitlab/merge_requests.go         |  743 ++++
 .../github.com/xanzy/go-gitlab/milestones.go  |  267 ++
 .../github.com/xanzy/go-gitlab/namespaces.go  |  122 +
 vendor/github.com/xanzy/go-gitlab/notes.go    |  490 +++
 .../xanzy/go-gitlab/notifications.go          |  214 +
 .../xanzy/go-gitlab/pages_domains.go          |  175 +
 .../xanzy/go-gitlab/pipeline_schedules.go     |  341 ++
 .../xanzy/go-gitlab/pipeline_triggers.go      |  232 ++
 .../github.com/xanzy/go-gitlab/pipelines.go   |  220 +
 .../xanzy/go-gitlab/project_members.go        |  179 +
 .../xanzy/go-gitlab/project_snippets.go       |  207 +
 vendor/github.com/xanzy/go-gitlab/projects.go |  997 +++++
 .../xanzy/go-gitlab/protected_branches.go     |  165 +
 .../xanzy/go-gitlab/repositories.go           |  260 ++
 .../xanzy/go-gitlab/repository_files.go       |  235 ++
 vendor/github.com/xanzy/go-gitlab/runners.go  |  326 ++
 vendor/github.com/xanzy/go-gitlab/search.go   |  326 ++
 vendor/github.com/xanzy/go-gitlab/services.go |  515 +++
 vendor/github.com/xanzy/go-gitlab/session.go  |   78 +
 vendor/github.com/xanzy/go-gitlab/settings.go |  265 ++
 .../xanzy/go-gitlab/sidekiq_metrics.go        |  154 +
 vendor/github.com/xanzy/go-gitlab/snippets.go |  230 +
 vendor/github.com/xanzy/go-gitlab/strings.go  |   94 +
 .../xanzy/go-gitlab/system_hooks.go           |  143 +
 vendor/github.com/xanzy/go-gitlab/tags.go     |  232 ++
 .../github.com/xanzy/go-gitlab/time_stats.go  |  163 +
 vendor/github.com/xanzy/go-gitlab/todos.go    |  175 +
 vendor/github.com/xanzy/go-gitlab/users.go    |  767 ++++
 vendor/github.com/xanzy/go-gitlab/validate.go |   40 +
 vendor/github.com/xanzy/go-gitlab/version.go  |   56 +
 vendor/github.com/xanzy/go-gitlab/wikis.go    |  204 +
 vendor/golang.org/x/net/AUTHORS               |    3 +
 vendor/golang.org/x/net/CONTRIBUTORS          |    3 +
 vendor/golang.org/x/net/LICENSE               |   27 +
 vendor/golang.org/x/net/PATENTS               |   22 +
 vendor/golang.org/x/net/context/context.go    |   56 +
 .../x/net/context/ctxhttp/ctxhttp.go          |   74 +
 .../x/net/context/ctxhttp/ctxhttp_pre17.go    |  147 +
 vendor/golang.org/x/net/context/go17.go       |   72 +
 vendor/golang.org/x/net/context/go19.go       |   20 +
 vendor/golang.org/x/net/context/pre_go17.go   |  300 ++
 vendor/golang.org/x/net/context/pre_go19.go   |  109 +
 vendor/golang.org/x/oauth2/.travis.yml        |   13 +
 vendor/golang.org/x/oauth2/AUTHORS            |    3 +
 vendor/golang.org/x/oauth2/CONTRIBUTING.md    |   26 +
 vendor/golang.org/x/oauth2/CONTRIBUTORS       |    3 +
 vendor/golang.org/x/oauth2/LICENSE            |   27 +
 vendor/golang.org/x/oauth2/README.md          |   77 +
 .../x/oauth2/internal/client_appengine.go     |   13 +
 vendor/golang.org/x/oauth2/internal/doc.go    |    6 +
 vendor/golang.org/x/oauth2/internal/oauth2.go |   37 +
 vendor/golang.org/x/oauth2/internal/token.go  |  270 ++
 .../golang.org/x/oauth2/internal/transport.go |   34 +
 vendor/golang.org/x/oauth2/oauth2.go          |  362 ++
 vendor/golang.org/x/oauth2/token.go           |  175 +
 vendor/golang.org/x/oauth2/transport.go       |  144 +
 vendor/google.golang.org/appengine/LICENSE    |  202 +
 .../appengine/internal/api.go                 |  660 +++
 .../appengine/internal/api_classic.go         |  169 +
 .../appengine/internal/api_common.go          |  123 +
 .../appengine/internal/api_pre17.go           |  682 +++
 .../appengine/internal/app_id.go              |   28 +
 .../appengine/internal/base/api_base.pb.go    |  176 +
 .../appengine/internal/base/api_base.proto    |   33 +
 .../internal/datastore/datastore_v3.pb.go     | 3244 ++++++++++++++
 .../internal/datastore/datastore_v3.proto     |  551 +++
 .../appengine/internal/identity.go            |   14 +
 .../appengine/internal/identity_classic.go    |   57 +
 .../appengine/internal/identity_vm.go         |  101 +
 .../appengine/internal/internal.go            |  110 +
 .../appengine/internal/log/log_service.pb.go  | 1039 +++++
 .../appengine/internal/log/log_service.proto  |  150 +
 .../appengine/internal/main.go                |   15 +
 .../appengine/internal/main_vm.go             |   48 +
 .../appengine/internal/metadata.go            |   61 +
 .../appengine/internal/net.go                 |   56 +
 .../appengine/internal/regen.sh               |   40 +
 .../internal/remote_api/remote_api.pb.go      |  287 ++
 .../internal/remote_api/remote_api.proto      |   44 +
 .../appengine/internal/transaction.go         |  115 +
 .../internal/urlfetch/urlfetch_service.pb.go  |  433 ++
 .../internal/urlfetch/urlfetch_service.proto  |   64 +
 .../appengine/urlfetch/urlfetch.go            |  210 +
 .../go-playground/webhooks.v3/.gitignore      |   25 +
 .../go-playground/webhooks.v3/.travis.yml     |   38 +
 .../go-playground/webhooks.v3/LICENSE         |   22 +
 .../go-playground/webhooks.v3/README.md       |  155 +
 .../webhooks.v3/gitlab/gitlab.go              |  157 +
 .../webhooks.v3/gitlab/payload.go             |  440 ++
 .../go-playground/webhooks.v3/logger.go       |   44 +
 .../go-playground/webhooks.v3/logo.png        |  Bin 0 -> 2419 bytes
 .../go-playground/webhooks.v3/webhooks.go     |  124 +
 vendor/maunium.net/go/mauflag/LICENSE         |  621 +++
 vendor/maunium.net/go/mauflag/README.md       |   51 +
 vendor/maunium.net/go/mauflag/defaults.go     |   85 +
 vendor/maunium.net/go/mauflag/flags.go        |  268 ++
 vendor/maunium.net/go/mauflag/flagset.go      |  273 ++
 vendor/maunium.net/go/mauflag/values.go       |  315 ++
 vendor/maunium.net/go/mautrix/LICENSE         |  621 +++
 vendor/maunium.net/go/mautrix/LICENSE-orig    |  373 ++
 vendor/maunium.net/go/mautrix/README.md       |   12 +
 vendor/maunium.net/go/mautrix/auth.go         |   74 +
 vendor/maunium.net/go/mautrix/event.go        |   59 +
 vendor/maunium.net/go/mautrix/mautrix.go      |   90 +
 vendor/maunium.net/go/mautrix/request.go      |  131 +
 vendor/maunium.net/go/mautrix/room.go         |  146 +
 vendor/maunium.net/go/mautrix/sync.go         |  143 +
 vendor/maunium.net/go/mautrix/types.go        |   83 +
 164 files changed, 46147 insertions(+)
 create mode 100644 Gopkg.lock
 create mode 100644 Gopkg.toml
 create mode 100644 vendor/github.com/golang/protobuf/AUTHORS
 create mode 100644 vendor/github.com/golang/protobuf/CONTRIBUTORS
 create mode 100644 vendor/github.com/golang/protobuf/LICENSE
 create mode 100644 vendor/github.com/golang/protobuf/proto/clone.go
 create mode 100644 vendor/github.com/golang/protobuf/proto/decode.go
 create mode 100644 vendor/github.com/golang/protobuf/proto/discard.go
 create mode 100644 vendor/github.com/golang/protobuf/proto/encode.go
 create mode 100644 vendor/github.com/golang/protobuf/proto/equal.go
 create mode 100644 vendor/github.com/golang/protobuf/proto/extensions.go
 create mode 100644 vendor/github.com/golang/protobuf/proto/lib.go
 create mode 100644 vendor/github.com/golang/protobuf/proto/message_set.go
 create mode 100644 vendor/github.com/golang/protobuf/proto/pointer_reflect.go
 create mode 100644 vendor/github.com/golang/protobuf/proto/pointer_unsafe.go
 create mode 100644 vendor/github.com/golang/protobuf/proto/properties.go
 create mode 100644 vendor/github.com/golang/protobuf/proto/table_marshal.go
 create mode 100644 vendor/github.com/golang/protobuf/proto/table_merge.go
 create mode 100644 vendor/github.com/golang/protobuf/proto/table_unmarshal.go
 create mode 100644 vendor/github.com/golang/protobuf/proto/text.go
 create mode 100644 vendor/github.com/golang/protobuf/proto/text_parser.go
 create mode 100644 vendor/github.com/google/go-querystring/LICENSE
 create mode 100644 vendor/github.com/google/go-querystring/query/encode.go
 create mode 100644 vendor/github.com/grokify/html-strip-tags-go/.codeclimate.yml
 create mode 100644 vendor/github.com/grokify/html-strip-tags-go/.travis.yml
 create mode 100644 vendor/github.com/grokify/html-strip-tags-go/LICENSE
 create mode 100644 vendor/github.com/grokify/html-strip-tags-go/PATENTS
 create mode 100644 vendor/github.com/grokify/html-strip-tags-go/README.md
 create mode 100644 vendor/github.com/grokify/html-strip-tags-go/strip.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/.gitignore
 create mode 100644 vendor/github.com/xanzy/go-gitlab/.travis.yml
 create mode 100644 vendor/github.com/xanzy/go-gitlab/CHANGELOG.md
 create mode 100644 vendor/github.com/xanzy/go-gitlab/LICENSE
 create mode 100644 vendor/github.com/xanzy/go-gitlab/README.md
 create mode 100644 vendor/github.com/xanzy/go-gitlab/award_emojis.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/boards.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/branches.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/broadcast_messages.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/build_variables.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/commits.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/deploy_keys.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/deployments.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/environments.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/event_types.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/events.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/feature_flags.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/gitignore_templates.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/gitlab.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/group_members.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/group_milestones.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/groups.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/issue_links.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/issues.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/jobs.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/labels.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/merge_request_approvals.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/merge_requests.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/milestones.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/namespaces.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/notes.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/notifications.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/pages_domains.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/pipeline_schedules.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/pipeline_triggers.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/pipelines.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/project_members.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/project_snippets.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/projects.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/protected_branches.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/repositories.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/repository_files.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/runners.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/search.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/services.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/session.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/settings.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/sidekiq_metrics.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/snippets.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/strings.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/system_hooks.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/tags.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/time_stats.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/todos.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/users.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/validate.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/version.go
 create mode 100644 vendor/github.com/xanzy/go-gitlab/wikis.go
 create mode 100644 vendor/golang.org/x/net/AUTHORS
 create mode 100644 vendor/golang.org/x/net/CONTRIBUTORS
 create mode 100644 vendor/golang.org/x/net/LICENSE
 create mode 100644 vendor/golang.org/x/net/PATENTS
 create mode 100644 vendor/golang.org/x/net/context/context.go
 create mode 100644 vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go
 create mode 100644 vendor/golang.org/x/net/context/ctxhttp/ctxhttp_pre17.go
 create mode 100644 vendor/golang.org/x/net/context/go17.go
 create mode 100644 vendor/golang.org/x/net/context/go19.go
 create mode 100644 vendor/golang.org/x/net/context/pre_go17.go
 create mode 100644 vendor/golang.org/x/net/context/pre_go19.go
 create mode 100644 vendor/golang.org/x/oauth2/.travis.yml
 create mode 100644 vendor/golang.org/x/oauth2/AUTHORS
 create mode 100644 vendor/golang.org/x/oauth2/CONTRIBUTING.md
 create mode 100644 vendor/golang.org/x/oauth2/CONTRIBUTORS
 create mode 100644 vendor/golang.org/x/oauth2/LICENSE
 create mode 100644 vendor/golang.org/x/oauth2/README.md
 create mode 100644 vendor/golang.org/x/oauth2/internal/client_appengine.go
 create mode 100644 vendor/golang.org/x/oauth2/internal/doc.go
 create mode 100644 vendor/golang.org/x/oauth2/internal/oauth2.go
 create mode 100644 vendor/golang.org/x/oauth2/internal/token.go
 create mode 100644 vendor/golang.org/x/oauth2/internal/transport.go
 create mode 100644 vendor/golang.org/x/oauth2/oauth2.go
 create mode 100644 vendor/golang.org/x/oauth2/token.go
 create mode 100644 vendor/golang.org/x/oauth2/transport.go
 create mode 100644 vendor/google.golang.org/appengine/LICENSE
 create mode 100644 vendor/google.golang.org/appengine/internal/api.go
 create mode 100644 vendor/google.golang.org/appengine/internal/api_classic.go
 create mode 100644 vendor/google.golang.org/appengine/internal/api_common.go
 create mode 100644 vendor/google.golang.org/appengine/internal/api_pre17.go
 create mode 100644 vendor/google.golang.org/appengine/internal/app_id.go
 create mode 100644 vendor/google.golang.org/appengine/internal/base/api_base.pb.go
 create mode 100644 vendor/google.golang.org/appengine/internal/base/api_base.proto
 create mode 100644 vendor/google.golang.org/appengine/internal/datastore/datastore_v3.pb.go
 create mode 100755 vendor/google.golang.org/appengine/internal/datastore/datastore_v3.proto
 create mode 100644 vendor/google.golang.org/appengine/internal/identity.go
 create mode 100644 vendor/google.golang.org/appengine/internal/identity_classic.go
 create mode 100644 vendor/google.golang.org/appengine/internal/identity_vm.go
 create mode 100644 vendor/google.golang.org/appengine/internal/internal.go
 create mode 100644 vendor/google.golang.org/appengine/internal/log/log_service.pb.go
 create mode 100644 vendor/google.golang.org/appengine/internal/log/log_service.proto
 create mode 100644 vendor/google.golang.org/appengine/internal/main.go
 create mode 100644 vendor/google.golang.org/appengine/internal/main_vm.go
 create mode 100644 vendor/google.golang.org/appengine/internal/metadata.go
 create mode 100644 vendor/google.golang.org/appengine/internal/net.go
 create mode 100755 vendor/google.golang.org/appengine/internal/regen.sh
 create mode 100644 vendor/google.golang.org/appengine/internal/remote_api/remote_api.pb.go
 create mode 100644 vendor/google.golang.org/appengine/internal/remote_api/remote_api.proto
 create mode 100644 vendor/google.golang.org/appengine/internal/transaction.go
 create mode 100644 vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.pb.go
 create mode 100644 vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto
 create mode 100644 vendor/google.golang.org/appengine/urlfetch/urlfetch.go
 create mode 100644 vendor/gopkg.in/go-playground/webhooks.v3/.gitignore
 create mode 100644 vendor/gopkg.in/go-playground/webhooks.v3/.travis.yml
 create mode 100644 vendor/gopkg.in/go-playground/webhooks.v3/LICENSE
 create mode 100644 vendor/gopkg.in/go-playground/webhooks.v3/README.md
 create mode 100644 vendor/gopkg.in/go-playground/webhooks.v3/gitlab/gitlab.go
 create mode 100644 vendor/gopkg.in/go-playground/webhooks.v3/gitlab/payload.go
 create mode 100644 vendor/gopkg.in/go-playground/webhooks.v3/logger.go
 create mode 100644 vendor/gopkg.in/go-playground/webhooks.v3/logo.png
 create mode 100644 vendor/gopkg.in/go-playground/webhooks.v3/webhooks.go
 create mode 100644 vendor/maunium.net/go/mauflag/LICENSE
 create mode 100644 vendor/maunium.net/go/mauflag/README.md
 create mode 100644 vendor/maunium.net/go/mauflag/defaults.go
 create mode 100644 vendor/maunium.net/go/mauflag/flags.go
 create mode 100644 vendor/maunium.net/go/mauflag/flagset.go
 create mode 100644 vendor/maunium.net/go/mauflag/values.go
 create mode 100644 vendor/maunium.net/go/mautrix/LICENSE
 create mode 100644 vendor/maunium.net/go/mautrix/LICENSE-orig
 create mode 100644 vendor/maunium.net/go/mautrix/README.md
 create mode 100644 vendor/maunium.net/go/mautrix/auth.go
 create mode 100644 vendor/maunium.net/go/mautrix/event.go
 create mode 100644 vendor/maunium.net/go/mautrix/mautrix.go
 create mode 100644 vendor/maunium.net/go/mautrix/request.go
 create mode 100644 vendor/maunium.net/go/mautrix/room.go
 create mode 100644 vendor/maunium.net/go/mautrix/sync.go
 create mode 100644 vendor/maunium.net/go/mautrix/types.go

diff --git a/Gopkg.lock b/Gopkg.lock
new file mode 100644
index 0000000..be30426
--- /dev/null
+++ b/Gopkg.lock
@@ -0,0 +1,86 @@
+# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
+
+
+[[projects]]
+  name = "github.com/golang/protobuf"
+  packages = ["proto"]
+  revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
+  version = "v1.1.0"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/google/go-querystring"
+  packages = ["query"]
+  revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a"
+
+[[projects]]
+  branch = "master"
+  name = "github.com/grokify/html-strip-tags-go"
+  packages = ["."]
+  revision = "3f8856873ce5730282807732a0413fcf7c98cc58"
+
+[[projects]]
+  name = "github.com/xanzy/go-gitlab"
+  packages = ["."]
+  revision = "79dad8e74fd097eb2e0fd0883f1978213e88107a"
+  version = "v0.10.7"
+
+[[projects]]
+  branch = "master"
+  name = "golang.org/x/net"
+  packages = [
+    "context",
+    "context/ctxhttp"
+  ]
+  revision = "32a936f46389aa10549d60bd7833e54b01685d09"
+
+[[projects]]
+  branch = "master"
+  name = "golang.org/x/oauth2"
+  packages = [
+    ".",
+    "internal"
+  ]
+  revision = "ef147856a6ddbb60760db74283d2424e98c87bff"
+
+[[projects]]
+  name = "google.golang.org/appengine"
+  packages = [
+    "internal",
+    "internal/base",
+    "internal/datastore",
+    "internal/log",
+    "internal/remote_api",
+    "internal/urlfetch",
+    "urlfetch"
+  ]
+  revision = "b1f26356af11148e710935ed1ac8a7f5702c7612"
+  version = "v1.1.0"
+
+[[projects]]
+  name = "gopkg.in/go-playground/webhooks.v3"
+  packages = [
+    ".",
+    "gitlab"
+  ]
+  revision = "8ffb2ffc32b7af2c7e940944eccba4dcd66c692a"
+  version = "v3.13.0"
+
+[[projects]]
+  branch = "master"
+  name = "maunium.net/go/mauflag"
+  packages = ["."]
+  revision = "8337821952ba5e919673bd62c502d43474e5e71d"
+
+[[projects]]
+  branch = "master"
+  name = "maunium.net/go/mautrix"
+  packages = ["."]
+  revision = "1b6683bcd21d4ae78e5d2831a75e2f628f54de3b"
+
+[solve-meta]
+  analyzer-name = "dep"
+  analyzer-version = 1
+  inputs-digest = "9ff7725e79e4aab9ef22ce44561ba126be32b6803fec86959b10faafebce7d9a"
+  solver-name = "gps-cdcl"
+  solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
new file mode 100644
index 0000000..c320a36
--- /dev/null
+++ b/Gopkg.toml
@@ -0,0 +1,46 @@
+# Gopkg.toml example
+#
+# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
+# for detailed Gopkg.toml documentation.
+#
+# required = ["github.com/user/thing/cmd/thing"]
+# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
+#
+# [[constraint]]
+#   name = "github.com/user/project"
+#   version = "1.0.0"
+#
+# [[constraint]]
+#   name = "github.com/user/project2"
+#   branch = "dev"
+#   source = "github.com/myfork/project2"
+#
+# [[override]]
+#   name = "github.com/x/y"
+#   version = "2.4.0"
+#
+# [prune]
+#   non-go = false
+#   go-tests = true
+#   unused-packages = true
+
+
+[[constraint]]
+  name = "github.com/xanzy/go-gitlab"
+  version = "0.10.7"
+
+[[constraint]]
+  name = "gopkg.in/go-playground/webhooks.v3"
+  version = "3.13.0"
+
+[[constraint]]
+  branch = "master"
+  name = "maunium.net/go/mauflag"
+
+[[constraint]]
+  branch = "master"
+  name = "maunium.net/go/mautrix"
+
+[prune]
+  go-tests = true
+  unused-packages = true
diff --git a/vendor/github.com/golang/protobuf/AUTHORS b/vendor/github.com/golang/protobuf/AUTHORS
new file mode 100644
index 0000000..15167cd
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/AUTHORS
@@ -0,0 +1,3 @@
+# This source code refers to The Go Authors for copyright purposes.
+# The master list of authors is in the main Go distribution,
+# visible at http://tip.golang.org/AUTHORS.
diff --git a/vendor/github.com/golang/protobuf/CONTRIBUTORS b/vendor/github.com/golang/protobuf/CONTRIBUTORS
new file mode 100644
index 0000000..1c4577e
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/CONTRIBUTORS
@@ -0,0 +1,3 @@
+# This source code was written by the Go contributors.
+# The master list of contributors is in the main Go distribution,
+# visible at http://tip.golang.org/CONTRIBUTORS.
diff --git a/vendor/github.com/golang/protobuf/LICENSE b/vendor/github.com/golang/protobuf/LICENSE
new file mode 100644
index 0000000..1b1b192
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/LICENSE
@@ -0,0 +1,31 @@
+Go support for Protocol Buffers - Google's data interchange format
+
+Copyright 2010 The Go Authors.  All rights reserved.
+https://github.com/golang/protobuf
+
+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/golang/protobuf/proto/clone.go b/vendor/github.com/golang/protobuf/proto/clone.go
new file mode 100644
index 0000000..3cd3249
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/clone.go
@@ -0,0 +1,253 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2011 The Go Authors.  All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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.
+
+// Protocol buffer deep copy and merge.
+// TODO: RawMessage.
+
+package proto
+
+import (
+	"fmt"
+	"log"
+	"reflect"
+	"strings"
+)
+
+// Clone returns a deep copy of a protocol buffer.
+func Clone(src Message) Message {
+	in := reflect.ValueOf(src)
+	if in.IsNil() {
+		return src
+	}
+	out := reflect.New(in.Type().Elem())
+	dst := out.Interface().(Message)
+	Merge(dst, src)
+	return dst
+}
+
+// Merger is the interface representing objects that can merge messages of the same type.
+type Merger interface {
+	// Merge merges src into this message.
+	// Required and optional fields that are set in src will be set to that value in dst.
+	// Elements of repeated fields will be appended.
+	//
+	// Merge may panic if called with a different argument type than the receiver.
+	Merge(src Message)
+}
+
+// generatedMerger is the custom merge method that generated protos will have.
+// We must add this method since a generate Merge method will conflict with
+// many existing protos that have a Merge data field already defined.
+type generatedMerger interface {
+	XXX_Merge(src Message)
+}
+
+// Merge merges src into dst.
+// Required and optional fields that are set in src will be set to that value in dst.
+// Elements of repeated fields will be appended.
+// Merge panics if src and dst are not the same type, or if dst is nil.
+func Merge(dst, src Message) {
+	if m, ok := dst.(Merger); ok {
+		m.Merge(src)
+		return
+	}
+
+	in := reflect.ValueOf(src)
+	out := reflect.ValueOf(dst)
+	if out.IsNil() {
+		panic("proto: nil destination")
+	}
+	if in.Type() != out.Type() {
+		panic(fmt.Sprintf("proto.Merge(%T, %T) type mismatch", dst, src))
+	}
+	if in.IsNil() {
+		return // Merge from nil src is a noop
+	}
+	if m, ok := dst.(generatedMerger); ok {
+		m.XXX_Merge(src)
+		return
+	}
+	mergeStruct(out.Elem(), in.Elem())
+}
+
+func mergeStruct(out, in reflect.Value) {
+	sprop := GetProperties(in.Type())
+	for i := 0; i < in.NumField(); i++ {
+		f := in.Type().Field(i)
+		if strings.HasPrefix(f.Name, "XXX_") {
+			continue
+		}
+		mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i])
+	}
+
+	if emIn, err := extendable(in.Addr().Interface()); err == nil {
+		emOut, _ := extendable(out.Addr().Interface())
+		mIn, muIn := emIn.extensionsRead()
+		if mIn != nil {
+			mOut := emOut.extensionsWrite()
+			muIn.Lock()
+			mergeExtension(mOut, mIn)
+			muIn.Unlock()
+		}
+	}
+
+	uf := in.FieldByName("XXX_unrecognized")
+	if !uf.IsValid() {
+		return
+	}
+	uin := uf.Bytes()
+	if len(uin) > 0 {
+		out.FieldByName("XXX_unrecognized").SetBytes(append([]byte(nil), uin...))
+	}
+}
+
+// mergeAny performs a merge between two values of the same type.
+// viaPtr indicates whether the values were indirected through a pointer (implying proto2).
+// prop is set if this is a struct field (it may be nil).
+func mergeAny(out, in reflect.Value, viaPtr bool, prop *Properties) {
+	if in.Type() == protoMessageType {
+		if !in.IsNil() {
+			if out.IsNil() {
+				out.Set(reflect.ValueOf(Clone(in.Interface().(Message))))
+			} else {
+				Merge(out.Interface().(Message), in.Interface().(Message))
+			}
+		}
+		return
+	}
+	switch in.Kind() {
+	case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
+		reflect.String, reflect.Uint32, reflect.Uint64:
+		if !viaPtr && isProto3Zero(in) {
+			return
+		}
+		out.Set(in)
+	case reflect.Interface:
+		// Probably a oneof field; copy non-nil values.
+		if in.IsNil() {
+			return
+		}
+		// Allocate destination if it is not set, or set to a different type.
+		// Otherwise we will merge as normal.
+		if out.IsNil() || out.Elem().Type() != in.Elem().Type() {
+			out.Set(reflect.New(in.Elem().Elem().Type())) // interface -> *T -> T -> new(T)
+		}
+		mergeAny(out.Elem(), in.Elem(), false, nil)
+	case reflect.Map:
+		if in.Len() == 0 {
+			return
+		}
+		if out.IsNil() {
+			out.Set(reflect.MakeMap(in.Type()))
+		}
+		// For maps with value types of *T or []byte we need to deep copy each value.
+		elemKind := in.Type().Elem().Kind()
+		for _, key := range in.MapKeys() {
+			var val reflect.Value
+			switch elemKind {
+			case reflect.Ptr:
+				val = reflect.New(in.Type().Elem().Elem())
+				mergeAny(val, in.MapIndex(key), false, nil)
+			case reflect.Slice:
+				val = in.MapIndex(key)
+				val = reflect.ValueOf(append([]byte{}, val.Bytes()...))
+			default:
+				val = in.MapIndex(key)
+			}
+			out.SetMapIndex(key, val)
+		}
+	case reflect.Ptr:
+		if in.IsNil() {
+			return
+		}
+		if out.IsNil() {
+			out.Set(reflect.New(in.Elem().Type()))
+		}
+		mergeAny(out.Elem(), in.Elem(), true, nil)
+	case reflect.Slice:
+		if in.IsNil() {
+			return
+		}
+		if in.Type().Elem().Kind() == reflect.Uint8 {
+			// []byte is a scalar bytes field, not a repeated field.
+
+			// Edge case: if this is in a proto3 message, a zero length
+			// bytes field is considered the zero value, and should not
+			// be merged.
+			if prop != nil && prop.proto3 && in.Len() == 0 {
+				return
+			}
+
+			// Make a deep copy.
+			// Append to []byte{} instead of []byte(nil) so that we never end up
+			// with a nil result.
+			out.SetBytes(append([]byte{}, in.Bytes()...))
+			return
+		}
+		n := in.Len()
+		if out.IsNil() {
+			out.Set(reflect.MakeSlice(in.Type(), 0, n))
+		}
+		switch in.Type().Elem().Kind() {
+		case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
+			reflect.String, reflect.Uint32, reflect.Uint64:
+			out.Set(reflect.AppendSlice(out, in))
+		default:
+			for i := 0; i < n; i++ {
+				x := reflect.Indirect(reflect.New(in.Type().Elem()))
+				mergeAny(x, in.Index(i), false, nil)
+				out.Set(reflect.Append(out, x))
+			}
+		}
+	case reflect.Struct:
+		mergeStruct(out, in)
+	default:
+		// unknown type, so not a protocol buffer
+		log.Printf("proto: don't know how to copy %v", in)
+	}
+}
+
+func mergeExtension(out, in map[int32]Extension) {
+	for extNum, eIn := range in {
+		eOut := Extension{desc: eIn.desc}
+		if eIn.value != nil {
+			v := reflect.New(reflect.TypeOf(eIn.value)).Elem()
+			mergeAny(v, reflect.ValueOf(eIn.value), false, nil)
+			eOut.value = v.Interface()
+		}
+		if eIn.enc != nil {
+			eOut.enc = make([]byte, len(eIn.enc))
+			copy(eOut.enc, eIn.enc)
+		}
+
+		out[extNum] = eOut
+	}
+}
diff --git a/vendor/github.com/golang/protobuf/proto/decode.go b/vendor/github.com/golang/protobuf/proto/decode.go
new file mode 100644
index 0000000..d9aa3c4
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/decode.go
@@ -0,0 +1,428 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors.  All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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.
+
+package proto
+
+/*
+ * Routines for decoding protocol buffer data to construct in-memory representations.
+ */
+
+import (
+	"errors"
+	"fmt"
+	"io"
+)
+
+// errOverflow is returned when an integer is too large to be represented.
+var errOverflow = errors.New("proto: integer overflow")
+
+// ErrInternalBadWireType is returned by generated code when an incorrect
+// wire type is encountered. It does not get returned to user code.
+var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof")
+
+// DecodeVarint reads a varint-encoded integer from the slice.
+// It returns the integer and the number of bytes consumed, or
+// zero if there is not enough.
+// This is the format for the
+// int32, int64, uint32, uint64, bool, and enum
+// protocol buffer types.
+func DecodeVarint(buf []byte) (x uint64, n int) {
+	for shift := uint(0); shift < 64; shift += 7 {
+		if n >= len(buf) {
+			return 0, 0
+		}
+		b := uint64(buf[n])
+		n++
+		x |= (b & 0x7F) << shift
+		if (b & 0x80) == 0 {
+			return x, n
+		}
+	}
+
+	// The number is too large to represent in a 64-bit value.
+	return 0, 0
+}
+
+func (p *Buffer) decodeVarintSlow() (x uint64, err error) {
+	i := p.index
+	l := len(p.buf)
+
+	for shift := uint(0); shift < 64; shift += 7 {
+		if i >= l {
+			err = io.ErrUnexpectedEOF
+			return
+		}
+		b := p.buf[i]
+		i++
+		x |= (uint64(b) & 0x7F) << shift
+		if b < 0x80 {
+			p.index = i
+			return
+		}
+	}
+
+	// The number is too large to represent in a 64-bit value.
+	err = errOverflow
+	return
+}
+
+// DecodeVarint reads a varint-encoded integer from the Buffer.
+// This is the format for the
+// int32, int64, uint32, uint64, bool, and enum
+// protocol buffer types.
+func (p *Buffer) DecodeVarint() (x uint64, err error) {
+	i := p.index
+	buf := p.buf
+
+	if i >= len(buf) {
+		return 0, io.ErrUnexpectedEOF
+	} else if buf[i] < 0x80 {
+		p.index++
+		return uint64(buf[i]), nil
+	} else if len(buf)-i < 10 {
+		return p.decodeVarintSlow()
+	}
+
+	var b uint64
+	// we already checked the first byte
+	x = uint64(buf[i]) - 0x80
+	i++
+
+	b = uint64(buf[i])
+	i++
+	x += b << 7
+	if b&0x80 == 0 {
+		goto done
+	}
+	x -= 0x80 << 7
+
+	b = uint64(buf[i])
+	i++
+	x += b << 14
+	if b&0x80 == 0 {
+		goto done
+	}
+	x -= 0x80 << 14
+
+	b = uint64(buf[i])
+	i++
+	x += b << 21
+	if b&0x80 == 0 {
+		goto done
+	}
+	x -= 0x80 << 21
+
+	b = uint64(buf[i])
+	i++
+	x += b << 28
+	if b&0x80 == 0 {
+		goto done
+	}
+	x -= 0x80 << 28
+
+	b = uint64(buf[i])
+	i++
+	x += b << 35
+	if b&0x80 == 0 {
+		goto done
+	}
+	x -= 0x80 << 35
+
+	b = uint64(buf[i])
+	i++
+	x += b << 42
+	if b&0x80 == 0 {
+		goto done
+	}
+	x -= 0x80 << 42
+
+	b = uint64(buf[i])
+	i++
+	x += b << 49
+	if b&0x80 == 0 {
+		goto done
+	}
+	x -= 0x80 << 49
+
+	b = uint64(buf[i])
+	i++
+	x += b << 56
+	if b&0x80 == 0 {
+		goto done
+	}
+	x -= 0x80 << 56
+
+	b = uint64(buf[i])
+	i++
+	x += b << 63
+	if b&0x80 == 0 {
+		goto done
+	}
+	// x -= 0x80 << 63 // Always zero.
+
+	return 0, errOverflow
+
+done:
+	p.index = i
+	return x, nil
+}
+
+// DecodeFixed64 reads a 64-bit integer from the Buffer.
+// This is the format for the
+// fixed64, sfixed64, and double protocol buffer types.
+func (p *Buffer) DecodeFixed64() (x uint64, err error) {
+	// x, err already 0
+	i := p.index + 8
+	if i < 0 || i > len(p.buf) {
+		err = io.ErrUnexpectedEOF
+		return
+	}
+	p.index = i
+
+	x = uint64(p.buf[i-8])
+	x |= uint64(p.buf[i-7]) << 8
+	x |= uint64(p.buf[i-6]) << 16
+	x |= uint64(p.buf[i-5]) << 24
+	x |= uint64(p.buf[i-4]) << 32
+	x |= uint64(p.buf[i-3]) << 40
+	x |= uint64(p.buf[i-2]) << 48
+	x |= uint64(p.buf[i-1]) << 56
+	return
+}
+
+// DecodeFixed32 reads a 32-bit integer from the Buffer.
+// This is the format for the
+// fixed32, sfixed32, and float protocol buffer types.
+func (p *Buffer) DecodeFixed32() (x uint64, err error) {
+	// x, err already 0
+	i := p.index + 4
+	if i < 0 || i > len(p.buf) {
+		err = io.ErrUnexpectedEOF
+		return
+	}
+	p.index = i
+
+	x = uint64(p.buf[i-4])
+	x |= uint64(p.buf[i-3]) << 8
+	x |= uint64(p.buf[i-2]) << 16
+	x |= uint64(p.buf[i-1]) << 24
+	return
+}
+
+// DecodeZigzag64 reads a zigzag-encoded 64-bit integer
+// from the Buffer.
+// This is the format used for the sint64 protocol buffer type.
+func (p *Buffer) DecodeZigzag64() (x uint64, err error) {
+	x, err = p.DecodeVarint()
+	if err != nil {
+		return
+	}
+	x = (x >> 1) ^ uint64((int64(x&1)<<63)>>63)
+	return
+}
+
+// DecodeZigzag32 reads a zigzag-encoded 32-bit integer
+// from  the Buffer.
+// This is the format used for the sint32 protocol buffer type.
+func (p *Buffer) DecodeZigzag32() (x uint64, err error) {
+	x, err = p.DecodeVarint()
+	if err != nil {
+		return
+	}
+	x = uint64((uint32(x) >> 1) ^ uint32((int32(x&1)<<31)>>31))
+	return
+}
+
+// DecodeRawBytes reads a count-delimited byte buffer from the Buffer.
+// This is the format used for the bytes protocol buffer
+// type and for embedded messages.
+func (p *Buffer) DecodeRawBytes(alloc bool) (buf []byte, err error) {
+	n, err := p.DecodeVarint()
+	if err != nil {
+		return nil, err
+	}
+
+	nb := int(n)
+	if nb < 0 {
+		return nil, fmt.Errorf("proto: bad byte length %d", nb)
+	}
+	end := p.index + nb
+	if end < p.index || end > len(p.buf) {
+		return nil, io.ErrUnexpectedEOF
+	}
+
+	if !alloc {
+		// todo: check if can get more uses of alloc=false
+		buf = p.buf[p.index:end]
+		p.index += nb
+		return
+	}
+
+	buf = make([]byte, nb)
+	copy(buf, p.buf[p.index:])
+	p.index += nb
+	return
+}
+
+// DecodeStringBytes reads an encoded string from the Buffer.
+// This is the format used for the proto2 string type.
+func (p *Buffer) DecodeStringBytes() (s string, err error) {
+	buf, err := p.DecodeRawBytes(false)
+	if err != nil {
+		return
+	}
+	return string(buf), nil
+}
+
+// Unmarshaler is the interface representing objects that can
+// unmarshal themselves.  The argument points to data that may be
+// overwritten, so implementations should not keep references to the
+// buffer.
+// Unmarshal implementations should not clear the receiver.
+// Any unmarshaled data should be merged into the receiver.
+// Callers of Unmarshal that do not want to retain existing data
+// should Reset the receiver before calling Unmarshal.
+type Unmarshaler interface {
+	Unmarshal([]byte) error
+}
+
+// newUnmarshaler is the interface representing objects that can
+// unmarshal themselves. The semantics are identical to Unmarshaler.
+//
+// This exists to support protoc-gen-go generated messages.
+// The proto package will stop type-asserting to this interface in the future.
+//
+// DO NOT DEPEND ON THIS.
+type newUnmarshaler interface {
+	XXX_Unmarshal([]byte) error
+}
+
+// Unmarshal parses the protocol buffer representation in buf and places the
+// decoded result in pb.  If the struct underlying pb does not match
+// the data in buf, the results can be unpredictable.
+//
+// Unmarshal resets pb before starting to unmarshal, so any
+// existing data in pb is always removed. Use UnmarshalMerge
+// to preserve and append to existing data.
+func Unmarshal(buf []byte, pb Message) error {
+	pb.Reset()
+	if u, ok := pb.(newUnmarshaler); ok {
+		return u.XXX_Unmarshal(buf)
+	}
+	if u, ok := pb.(Unmarshaler); ok {
+		return u.Unmarshal(buf)
+	}
+	return NewBuffer(buf).Unmarshal(pb)
+}
+
+// UnmarshalMerge parses the protocol buffer representation in buf and
+// writes the decoded result to pb.  If the struct underlying pb does not match
+// the data in buf, the results can be unpredictable.
+//
+// UnmarshalMerge merges into existing data in pb.
+// Most code should use Unmarshal instead.
+func UnmarshalMerge(buf []byte, pb Message) error {
+	if u, ok := pb.(newUnmarshaler); ok {
+		return u.XXX_Unmarshal(buf)
+	}
+	if u, ok := pb.(Unmarshaler); ok {
+		// NOTE: The history of proto have unfortunately been inconsistent
+		// whether Unmarshaler should or should not implicitly clear itself.
+		// Some implementations do, most do not.
+		// Thus, calling this here may or may not do what people want.
+		//
+		// See https://github.com/golang/protobuf/issues/424
+		return u.Unmarshal(buf)
+	}
+	return NewBuffer(buf).Unmarshal(pb)
+}
+
+// DecodeMessage reads a count-delimited message from the Buffer.
+func (p *Buffer) DecodeMessage(pb Message) error {
+	enc, err := p.DecodeRawBytes(false)
+	if err != nil {
+		return err
+	}
+	return NewBuffer(enc).Unmarshal(pb)
+}
+
+// DecodeGroup reads a tag-delimited group from the Buffer.
+// StartGroup tag is already consumed. This function consumes
+// EndGroup tag.
+func (p *Buffer) DecodeGroup(pb Message) error {
+	b := p.buf[p.index:]
+	x, y := findEndGroup(b)
+	if x < 0 {
+		return io.ErrUnexpectedEOF
+	}
+	err := Unmarshal(b[:x], pb)
+	p.index += y
+	return err
+}
+
+// Unmarshal parses the protocol buffer representation in the
+// Buffer and places the decoded result in pb.  If the struct
+// underlying pb does not match the data in the buffer, the results can be
+// unpredictable.
+//
+// Unlike proto.Unmarshal, this does not reset pb before starting to unmarshal.
+func (p *Buffer) Unmarshal(pb Message) error {
+	// If the object can unmarshal itself, let it.
+	if u, ok := pb.(newUnmarshaler); ok {
+		err := u.XXX_Unmarshal(p.buf[p.index:])
+		p.index = len(p.buf)
+		return err
+	}
+	if u, ok := pb.(Unmarshaler); ok {
+		// NOTE: The history of proto have unfortunately been inconsistent
+		// whether Unmarshaler should or should not implicitly clear itself.
+		// Some implementations do, most do not.
+		// Thus, calling this here may or may not do what people want.
+		//
+		// See https://github.com/golang/protobuf/issues/424
+		err := u.Unmarshal(p.buf[p.index:])
+		p.index = len(p.buf)
+		return err
+	}
+
+	// Slow workaround for messages that aren't Unmarshalers.
+	// This includes some hand-coded .pb.go files and
+	// bootstrap protos.
+	// TODO: fix all of those and then add Unmarshal to
+	// the Message interface. Then:
+	// The cast above and code below can be deleted.
+	// The old unmarshaler can be deleted.
+	// Clients can call Unmarshal directly (can already do that, actually).
+	var info InternalMessageInfo
+	err := info.Unmarshal(pb, p.buf[p.index:])
+	p.index = len(p.buf)
+	return err
+}
diff --git a/vendor/github.com/golang/protobuf/proto/discard.go b/vendor/github.com/golang/protobuf/proto/discard.go
new file mode 100644
index 0000000..dea2617
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/discard.go
@@ -0,0 +1,350 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2017 The Go Authors.  All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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.
+
+package proto
+
+import (
+	"fmt"
+	"reflect"
+	"strings"
+	"sync"
+	"sync/atomic"
+)
+
+type generatedDiscarder interface {
+	XXX_DiscardUnknown()
+}
+
+// DiscardUnknown recursively discards all unknown fields from this message
+// and all embedded messages.
+//
+// When unmarshaling a message with unrecognized fields, the tags and values
+// of such fields are preserved in the Message. This allows a later call to
+// marshal to be able to produce a message that continues to have those
+// unrecognized fields. To avoid this, DiscardUnknown is used to
+// explicitly clear the unknown fields after unmarshaling.
+//
+// For proto2 messages, the unknown fields of message extensions are only
+// discarded from messages that have been accessed via GetExtension.
+func DiscardUnknown(m Message) {
+	if m, ok := m.(generatedDiscarder); ok {
+		m.XXX_DiscardUnknown()
+		return
+	}
+	// TODO: Dynamically populate a InternalMessageInfo for legacy messages,
+	// but the master branch has no implementation for InternalMessageInfo,
+	// so it would be more work to replicate that approach.
+	discardLegacy(m)
+}
+
+// DiscardUnknown recursively discards all unknown fields.
+func (a *InternalMessageInfo) DiscardUnknown(m Message) {
+	di := atomicLoadDiscardInfo(&a.discard)
+	if di == nil {
+		di = getDiscardInfo(reflect.TypeOf(m).Elem())
+		atomicStoreDiscardInfo(&a.discard, di)
+	}
+	di.discard(toPointer(&m))
+}
+
+type discardInfo struct {
+	typ reflect.Type
+
+	initialized int32 // 0: only typ is valid, 1: everything is valid
+	lock        sync.Mutex
+
+	fields       []discardFieldInfo
+	unrecognized field
+}
+
+type discardFieldInfo struct {
+	field   field // Offset of field, guaranteed to be valid
+	discard func(src pointer)
+}
+
+var (
+	discardInfoMap  = map[reflect.Type]*discardInfo{}
+	discardInfoLock sync.Mutex
+)
+
+func getDiscardInfo(t reflect.Type) *discardInfo {
+	discardInfoLock.Lock()
+	defer discardInfoLock.Unlock()
+	di := discardInfoMap[t]
+	if di == nil {
+		di = &discardInfo{typ: t}
+		discardInfoMap[t] = di
+	}
+	return di
+}
+
+func (di *discardInfo) discard(src pointer) {
+	if src.isNil() {
+		return // Nothing to do.
+	}
+
+	if atomic.LoadInt32(&di.initialized) == 0 {
+		di.computeDiscardInfo()
+	}
+
+	for _, fi := range di.fields {
+		sfp := src.offset(fi.field)
+		fi.discard(sfp)
+	}
+
+	// For proto2 messages, only discard unknown fields in message extensions
+	// that have been accessed via GetExtension.
+	if em, err := extendable(src.asPointerTo(di.typ).Interface()); err == nil {
+		// Ignore lock since DiscardUnknown is not concurrency safe.
+		emm, _ := em.extensionsRead()
+		for _, mx := range emm {
+			if m, ok := mx.value.(Message); ok {
+				DiscardUnknown(m)
+			}
+		}
+	}
+
+	if di.unrecognized.IsValid() {
+		*src.offset(di.unrecognized).toBytes() = nil
+	}
+}
+
+func (di *discardInfo) computeDiscardInfo() {
+	di.lock.Lock()
+	defer di.lock.Unlock()
+	if di.initialized != 0 {
+		return
+	}
+	t := di.typ
+	n := t.NumField()
+
+	for i := 0; i < n; i++ {
+		f := t.Field(i)
+		if strings.HasPrefix(f.Name, "XXX_") {
+			continue
+		}
+
+		dfi := discardFieldInfo{field: toField(&f)}
+		tf := f.Type
+
+		// Unwrap tf to get its most basic type.
+		var isPointer, isSlice bool
+		if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 {
+			isSlice = true
+			tf = tf.Elem()
+		}
+		if tf.Kind() == reflect.Ptr {
+			isPointer = true
+			tf = tf.Elem()
+		}
+		if isPointer && isSlice && tf.Kind() != reflect.Struct {
+			panic(fmt.Sprintf("%v.%s cannot be a slice of pointers to primitive types", t, f.Name))
+		}
+
+		switch tf.Kind() {
+		case reflect.Struct:
+			switch {
+			case !isPointer:
+				panic(fmt.Sprintf("%v.%s cannot be a direct struct value", t, f.Name))
+			case isSlice: // E.g., []*pb.T
+				di := getDiscardInfo(tf)
+				dfi.discard = func(src pointer) {
+					sps := src.getPointerSlice()
+					for _, sp := range sps {
+						if !sp.isNil() {
+							di.discard(sp)
+						}
+					}
+				}
+			default: // E.g., *pb.T
+				di := getDiscardInfo(tf)
+				dfi.discard = func(src pointer) {
+					sp := src.getPointer()
+					if !sp.isNil() {
+						di.discard(sp)
+					}
+				}
+			}
+		case reflect.Map:
+			switch {
+			case isPointer || isSlice:
+				panic(fmt.Sprintf("%v.%s cannot be a pointer to a map or a slice of map values", t, f.Name))
+			default: // E.g., map[K]V
+				if tf.Elem().Kind() == reflect.Ptr { // Proto struct (e.g., *T)
+					dfi.discard = func(src pointer) {
+						sm := src.asPointerTo(tf).Elem()
+						if sm.Len() == 0 {
+							return
+						}
+						for _, key := range sm.MapKeys() {
+							val := sm.MapIndex(key)
+							DiscardUnknown(val.Interface().(Message))
+						}
+					}
+				} else {
+					dfi.discard = func(pointer) {} // Noop
+				}
+			}
+		case reflect.Interface:
+			// Must be oneof field.
+			switch {
+			case isPointer || isSlice:
+				panic(fmt.Sprintf("%v.%s cannot be a pointer to a interface or a slice of interface values", t, f.Name))
+			default: // E.g., interface{}
+				// TODO: Make this faster?
+				dfi.discard = func(src pointer) {
+					su := src.asPointerTo(tf).Elem()
+					if !su.IsNil() {
+						sv := su.Elem().Elem().Field(0)
+						if sv.Kind() == reflect.Ptr && sv.IsNil() {
+							return
+						}
+						switch sv.Type().Kind() {
+						case reflect.Ptr: // Proto struct (e.g., *T)
+							DiscardUnknown(sv.Interface().(Message))
+						}
+					}
+				}
+			}
+		default:
+			continue
+		}
+		di.fields = append(di.fields, dfi)
+	}
+
+	di.unrecognized = invalidField
+	if f, ok := t.FieldByName("XXX_unrecognized"); ok {
+		if f.Type != reflect.TypeOf([]byte{}) {
+			panic("expected XXX_unrecognized to be of type []byte")
+		}
+		di.unrecognized = toField(&f)
+	}
+
+	atomic.StoreInt32(&di.initialized, 1)
+}
+
+func discardLegacy(m Message) {
+	v := reflect.ValueOf(m)
+	if v.Kind() != reflect.Ptr || v.IsNil() {
+		return
+	}
+	v = v.Elem()
+	if v.Kind() != reflect.Struct {
+		return
+	}
+	t := v.Type()
+
+	for i := 0; i < v.NumField(); i++ {
+		f := t.Field(i)
+		if strings.HasPrefix(f.Name, "XXX_") {
+			continue
+		}
+		vf := v.Field(i)
+		tf := f.Type
+
+		// Unwrap tf to get its most basic type.
+		var isPointer, isSlice bool
+		if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 {
+			isSlice = true
+			tf = tf.Elem()
+		}
+		if tf.Kind() == reflect.Ptr {
+			isPointer = true
+			tf = tf.Elem()
+		}
+		if isPointer && isSlice && tf.Kind() != reflect.Struct {
+			panic(fmt.Sprintf("%T.%s cannot be a slice of pointers to primitive types", m, f.Name))
+		}
+
+		switch tf.Kind() {
+		case reflect.Struct:
+			switch {
+			case !isPointer:
+				panic(fmt.Sprintf("%T.%s cannot be a direct struct value", m, f.Name))
+			case isSlice: // E.g., []*pb.T
+				for j := 0; j < vf.Len(); j++ {
+					discardLegacy(vf.Index(j).Interface().(Message))
+				}
+			default: // E.g., *pb.T
+				discardLegacy(vf.Interface().(Message))
+			}
+		case reflect.Map:
+			switch {
+			case isPointer || isSlice:
+				panic(fmt.Sprintf("%T.%s cannot be a pointer to a map or a slice of map values", m, f.Name))
+			default: // E.g., map[K]V
+				tv := vf.Type().Elem()
+				if tv.Kind() == reflect.Ptr && tv.Implements(protoMessageType) { // Proto struct (e.g., *T)
+					for _, key := range vf.MapKeys() {
+						val := vf.MapIndex(key)
+						discardLegacy(val.Interface().(Message))
+					}
+				}
+			}
+		case reflect.Interface:
+			// Must be oneof field.
+			switch {
+			case isPointer || isSlice:
+				panic(fmt.Sprintf("%T.%s cannot be a pointer to a interface or a slice of interface values", m, f.Name))
+			default: // E.g., test_proto.isCommunique_Union interface
+				if !vf.IsNil() && f.Tag.Get("protobuf_oneof") != "" {
+					vf = vf.Elem() // E.g., *test_proto.Communique_Msg
+					if !vf.IsNil() {
+						vf = vf.Elem()   // E.g., test_proto.Communique_Msg
+						vf = vf.Field(0) // E.g., Proto struct (e.g., *T) or primitive value
+						if vf.Kind() == reflect.Ptr {
+							discardLegacy(vf.Interface().(Message))
+						}
+					}
+				}
+			}
+		}
+	}
+
+	if vf := v.FieldByName("XXX_unrecognized"); vf.IsValid() {
+		if vf.Type() != reflect.TypeOf([]byte{}) {
+			panic("expected XXX_unrecognized to be of type []byte")
+		}
+		vf.Set(reflect.ValueOf([]byte(nil)))
+	}
+
+	// For proto2 messages, only discard unknown fields in message extensions
+	// that have been accessed via GetExtension.
+	if em, err := extendable(m); err == nil {
+		// Ignore lock since discardLegacy is not concurrency safe.
+		emm, _ := em.extensionsRead()
+		for _, mx := range emm {
+			if m, ok := mx.value.(Message); ok {
+				discardLegacy(m)
+			}
+		}
+	}
+}
diff --git a/vendor/github.com/golang/protobuf/proto/encode.go b/vendor/github.com/golang/protobuf/proto/encode.go
new file mode 100644
index 0000000..c27d35f
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/encode.go
@@ -0,0 +1,221 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors.  All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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.
+
+package proto
+
+/*
+ * Routines for encoding data into the wire format for protocol buffers.
+ */
+
+import (
+	"errors"
+	"fmt"
+	"reflect"
+)
+
+// RequiredNotSetError is the error returned if Marshal is called with
+// a protocol buffer struct whose required fields have not
+// all been initialized. It is also the error returned if Unmarshal is
+// called with an encoded protocol buffer that does not include all the
+// required fields.
+//
+// When printed, RequiredNotSetError reports the first unset required field in a
+// message. If the field cannot be precisely determined, it is reported as
+// "{Unknown}".
+type RequiredNotSetError struct {
+	field string
+}
+
+func (e *RequiredNotSetError) Error() string {
+	return fmt.Sprintf("proto: required field %q not set", e.field)
+}
+
+var (
+	// errRepeatedHasNil is the error returned if Marshal is called with
+	// a struct with a repeated field containing a nil element.
+	errRepeatedHasNil = errors.New("proto: repeated field has nil element")
+
+	// errOneofHasNil is the error returned if Marshal is called with
+	// a struct with a oneof field containing a nil element.
+	errOneofHasNil = errors.New("proto: oneof field has nil value")
+
+	// ErrNil is the error returned if Marshal is called with nil.
+	ErrNil = errors.New("proto: Marshal called with nil")
+
+	// ErrTooLarge is the error returned if Marshal is called with a
+	// message that encodes to >2GB.
+	ErrTooLarge = errors.New("proto: message encodes to over 2 GB")
+)
+
+// The fundamental encoders that put bytes on the wire.
+// Those that take integer types all accept uint64 and are
+// therefore of type valueEncoder.
+
+const maxVarintBytes = 10 // maximum length of a varint
+
+// EncodeVarint returns the varint encoding of x.
+// This is the format for the
+// int32, int64, uint32, uint64, bool, and enum
+// protocol buffer types.
+// Not used by the package itself, but helpful to clients
+// wishing to use the same encoding.
+func EncodeVarint(x uint64) []byte {
+	var buf [maxVarintBytes]byte
+	var n int
+	for n = 0; x > 127; n++ {
+		buf[n] = 0x80 | uint8(x&0x7F)
+		x >>= 7
+	}
+	buf[n] = uint8(x)
+	n++
+	return buf[0:n]
+}
+
+// EncodeVarint writes a varint-encoded integer to the Buffer.
+// This is the format for the
+// int32, int64, uint32, uint64, bool, and enum
+// protocol buffer types.
+func (p *Buffer) EncodeVarint(x uint64) error {
+	for x >= 1<<7 {
+		p.buf = append(p.buf, uint8(x&0x7f|0x80))
+		x >>= 7
+	}
+	p.buf = append(p.buf, uint8(x))
+	return nil
+}
+
+// SizeVarint returns the varint encoding size of an integer.
+func SizeVarint(x uint64) int {
+	switch {
+	case x < 1<<7:
+		return 1
+	case x < 1<<14:
+		return 2
+	case x < 1<<21:
+		return 3
+	case x < 1<<28:
+		return 4
+	case x < 1<<35:
+		return 5
+	case x < 1<<42:
+		return 6
+	case x < 1<<49:
+		return 7
+	case x < 1<<56:
+		return 8
+	case x < 1<<63:
+		return 9
+	}
+	return 10
+}
+
+// EncodeFixed64 writes a 64-bit integer to the Buffer.
+// This is the format for the
+// fixed64, sfixed64, and double protocol buffer types.
+func (p *Buffer) EncodeFixed64(x uint64) error {
+	p.buf = append(p.buf,
+		uint8(x),
+		uint8(x>>8),
+		uint8(x>>16),
+		uint8(x>>24),
+		uint8(x>>32),
+		uint8(x>>40),
+		uint8(x>>48),
+		uint8(x>>56))
+	return nil
+}
+
+// EncodeFixed32 writes a 32-bit integer to the Buffer.
+// This is the format for the
+// fixed32, sfixed32, and float protocol buffer types.
+func (p *Buffer) EncodeFixed32(x uint64) error {
+	p.buf = append(p.buf,
+		uint8(x),
+		uint8(x>>8),
+		uint8(x>>16),
+		uint8(x>>24))
+	return nil
+}
+
+// EncodeZigzag64 writes a zigzag-encoded 64-bit integer
+// to the Buffer.
+// This is the format used for the sint64 protocol buffer type.
+func (p *Buffer) EncodeZigzag64(x uint64) error {
+	// use signed number to get arithmetic right shift.
+	return p.EncodeVarint(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+
+// EncodeZigzag32 writes a zigzag-encoded 32-bit integer
+// to the Buffer.
+// This is the format used for the sint32 protocol buffer type.
+func (p *Buffer) EncodeZigzag32(x uint64) error {
+	// use signed number to get arithmetic right shift.
+	return p.EncodeVarint(uint64((uint32(x) << 1) ^ uint32((int32(x) >> 31))))
+}
+
+// EncodeRawBytes writes a count-delimited byte buffer to the Buffer.
+// This is the format used for the bytes protocol buffer
+// type and for embedded messages.
+func (p *Buffer) EncodeRawBytes(b []byte) error {
+	p.EncodeVarint(uint64(len(b)))
+	p.buf = append(p.buf, b...)
+	return nil
+}
+
+// EncodeStringBytes writes an encoded string to the Buffer.
+// This is the format used for the proto2 string type.
+func (p *Buffer) EncodeStringBytes(s string) error {
+	p.EncodeVarint(uint64(len(s)))
+	p.buf = append(p.buf, s...)
+	return nil
+}
+
+// Marshaler is the interface representing objects that can marshal themselves.
+type Marshaler interface {
+	Marshal() ([]byte, error)
+}
+
+// EncodeMessage writes the protocol buffer to the Buffer,
+// prefixed by a varint-encoded length.
+func (p *Buffer) EncodeMessage(pb Message) error {
+	siz := Size(pb)
+	p.EncodeVarint(uint64(siz))
+	return p.Marshal(pb)
+}
+
+// All protocol buffer fields are nillable, but be careful.
+func isNil(v reflect.Value) bool {
+	switch v.Kind() {
+	case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
+		return v.IsNil()
+	}
+	return false
+}
diff --git a/vendor/github.com/golang/protobuf/proto/equal.go b/vendor/github.com/golang/protobuf/proto/equal.go
new file mode 100644
index 0000000..d4db5a1
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/equal.go
@@ -0,0 +1,300 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2011 The Go Authors.  All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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.
+
+// Protocol buffer comparison.
+
+package proto
+
+import (
+	"bytes"
+	"log"
+	"reflect"
+	"strings"
+)
+
+/*
+Equal returns true iff protocol buffers a and b are equal.
+The arguments must both be pointers to protocol buffer structs.
+
+Equality is defined in this way:
+  - Two messages are equal iff they are the same type,
+    corresponding fields are equal, unknown field sets
+    are equal, and extensions sets are equal.
+  - Two set scalar fields are equal iff their values are equal.
+    If the fields are of a floating-point type, remember that
+    NaN != x for all x, including NaN. If the message is defined
+    in a proto3 .proto file, fields are not "set"; specifically,
+    zero length proto3 "bytes" fields are equal (nil == {}).
+  - Two repeated fields are equal iff their lengths are the same,
+    and their corresponding elements are equal. Note a "bytes" field,
+    although represented by []byte, is not a repeated field and the
+    rule for the scalar fields described above applies.
+  - Two unset fields are equal.
+  - Two unknown field sets are equal if their current
+    encoded state is equal.
+  - Two extension sets are equal iff they have corresponding
+    elements that are pairwise equal.
+  - Two map fields are equal iff their lengths are the same,
+    and they contain the same set of elements. Zero-length map
+    fields are equal.
+  - Every other combination of things are not equal.
+
+The return value is undefined if a and b are not protocol buffers.
+*/
+func Equal(a, b Message) bool {
+	if a == nil || b == nil {
+		return a == b
+	}
+	v1, v2 := reflect.ValueOf(a), reflect.ValueOf(b)
+	if v1.Type() != v2.Type() {
+		return false
+	}
+	if v1.Kind() == reflect.Ptr {
+		if v1.IsNil() {
+			return v2.IsNil()
+		}
+		if v2.IsNil() {
+			return false
+		}
+		v1, v2 = v1.Elem(), v2.Elem()
+	}
+	if v1.Kind() != reflect.Struct {
+		return false
+	}
+	return equalStruct(v1, v2)
+}
+
+// v1 and v2 are known to have the same type.
+func equalStruct(v1, v2 reflect.Value) bool {
+	sprop := GetProperties(v1.Type())
+	for i := 0; i < v1.NumField(); i++ {
+		f := v1.Type().Field(i)
+		if strings.HasPrefix(f.Name, "XXX_") {
+			continue
+		}
+		f1, f2 := v1.Field(i), v2.Field(i)
+		if f.Type.Kind() == reflect.Ptr {
+			if n1, n2 := f1.IsNil(), f2.IsNil(); n1 && n2 {
+				// both unset
+				continue
+			} else if n1 != n2 {
+				// set/unset mismatch
+				return false
+			}
+			f1, f2 = f1.Elem(), f2.Elem()
+		}
+		if !equalAny(f1, f2, sprop.Prop[i]) {
+			return false
+		}
+	}
+
+	if em1 := v1.FieldByName("XXX_InternalExtensions"); em1.IsValid() {
+		em2 := v2.FieldByName("XXX_InternalExtensions")
+		if !equalExtensions(v1.Type(), em1.Interface().(XXX_InternalExtensions), em2.Interface().(XXX_InternalExtensions)) {
+			return false
+		}
+	}
+
+	if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() {
+		em2 := v2.FieldByName("XXX_extensions")
+		if !equalExtMap(v1.Type(), em1.Interface().(map[int32]Extension), em2.Interface().(map[int32]Extension)) {
+			return false
+		}
+	}
+
+	uf := v1.FieldByName("XXX_unrecognized")
+	if !uf.IsValid() {
+		return true
+	}
+
+	u1 := uf.Bytes()
+	u2 := v2.FieldByName("XXX_unrecognized").Bytes()
+	return bytes.Equal(u1, u2)
+}
+
+// v1 and v2 are known to have the same type.
+// prop may be nil.
+func equalAny(v1, v2 reflect.Value, prop *Properties) bool {
+	if v1.Type() == protoMessageType {
+		m1, _ := v1.Interface().(Message)
+		m2, _ := v2.Interface().(Message)
+		return Equal(m1, m2)
+	}
+	switch v1.Kind() {
+	case reflect.Bool:
+		return v1.Bool() == v2.Bool()
+	case reflect.Float32, reflect.Float64:
+		return v1.Float() == v2.Float()
+	case reflect.Int32, reflect.Int64:
+		return v1.Int() == v2.Int()
+	case reflect.Interface:
+		// Probably a oneof field; compare the inner values.
+		n1, n2 := v1.IsNil(), v2.IsNil()
+		if n1 || n2 {
+			return n1 == n2
+		}
+		e1, e2 := v1.Elem(), v2.Elem()
+		if e1.Type() != e2.Type() {
+			return false
+		}
+		return equalAny(e1, e2, nil)
+	case reflect.Map:
+		if v1.Len() != v2.Len() {
+			return false
+		}
+		for _, key := range v1.MapKeys() {
+			val2 := v2.MapIndex(key)
+			if !val2.IsValid() {
+				// This key was not found in the second map.
+				return false
+			}
+			if !equalAny(v1.MapIndex(key), val2, nil) {
+				return false
+			}
+		}
+		return true
+	case reflect.Ptr:
+		// Maps may have nil values in them, so check for nil.
+		if v1.IsNil() && v2.IsNil() {
+			return true
+		}
+		if v1.IsNil() != v2.IsNil() {
+			return false
+		}
+		return equalAny(v1.Elem(), v2.Elem(), prop)
+	case reflect.Slice:
+		if v1.Type().Elem().Kind() == reflect.Uint8 {
+			// short circuit: []byte
+
+			// Edge case: if this is in a proto3 message, a zero length
+			// bytes field is considered the zero value.
+			if prop != nil && prop.proto3 && v1.Len() == 0 && v2.Len() == 0 {
+				return true
+			}
+			if v1.IsNil() != v2.IsNil() {
+				return false
+			}
+			return bytes.Equal(v1.Interface().([]byte), v2.Interface().([]byte))
+		}
+
+		if v1.Len() != v2.Len() {
+			return false
+		}
+		for i := 0; i < v1.Len(); i++ {
+			if !equalAny(v1.Index(i), v2.Index(i), prop) {
+				return false
+			}
+		}
+		return true
+	case reflect.String:
+		return v1.Interface().(string) == v2.Interface().(string)
+	case reflect.Struct:
+		return equalStruct(v1, v2)
+	case reflect.Uint32, reflect.Uint64:
+		return v1.Uint() == v2.Uint()
+	}
+
+	// unknown type, so not a protocol buffer
+	log.Printf("proto: don't know how to compare %v", v1)
+	return false
+}
+
+// base is the struct type that the extensions are based on.
+// x1 and x2 are InternalExtensions.
+func equalExtensions(base reflect.Type, x1, x2 XXX_InternalExtensions) bool {
+	em1, _ := x1.extensionsRead()
+	em2, _ := x2.extensionsRead()
+	return equalExtMap(base, em1, em2)
+}
+
+func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool {
+	if len(em1) != len(em2) {
+		return false
+	}
+
+	for extNum, e1 := range em1 {
+		e2, ok := em2[extNum]
+		if !ok {
+			return false
+		}
+
+		m1, m2 := e1.value, e2.value
+
+		if m1 == nil && m2 == nil {
+			// Both have only encoded form.
+			if bytes.Equal(e1.enc, e2.enc) {
+				continue
+			}
+			// The bytes are different, but the extensions might still be
+			// equal. We need to decode them to compare.
+		}
+
+		if m1 != nil && m2 != nil {
+			// Both are unencoded.
+			if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
+				return false
+			}
+			continue
+		}
+
+		// At least one is encoded. To do a semantically correct comparison
+		// we need to unmarshal them first.
+		var desc *ExtensionDesc
+		if m := extensionMaps[base]; m != nil {
+			desc = m[extNum]
+		}
+		if desc == nil {
+			// If both have only encoded form and the bytes are the same,
+			// it is handled above. We get here when the bytes are different.
+			// We don't know how to decode it, so just compare them as byte
+			// slices.
+			log.Printf("proto: don't know how to compare extension %d of %v", extNum, base)
+			return false
+		}
+		var err error
+		if m1 == nil {
+			m1, err = decodeExtension(e1.enc, desc)
+		}
+		if m2 == nil && err == nil {
+			m2, err = decodeExtension(e2.enc, desc)
+		}
+		if err != nil {
+			// The encoded form is invalid.
+			log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err)
+			return false
+		}
+		if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
+			return false
+		}
+	}
+
+	return true
+}
diff --git a/vendor/github.com/golang/protobuf/proto/extensions.go b/vendor/github.com/golang/protobuf/proto/extensions.go
new file mode 100644
index 0000000..816a3b9
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/extensions.go
@@ -0,0 +1,543 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors.  All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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.
+
+package proto
+
+/*
+ * Types and routines for supporting protocol buffer extensions.
+ */
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"reflect"
+	"strconv"
+	"sync"
+)
+
+// ErrMissingExtension is the error returned by GetExtension if the named extension is not in the message.
+var ErrMissingExtension = errors.New("proto: missing extension")
+
+// ExtensionRange represents a range of message extensions for a protocol buffer.
+// Used in code generated by the protocol compiler.
+type ExtensionRange struct {
+	Start, End int32 // both inclusive
+}
+
+// extendableProto is an interface implemented by any protocol buffer generated by the current
+// proto compiler that may be extended.
+type extendableProto interface {
+	Message
+	ExtensionRangeArray() []ExtensionRange
+	extensionsWrite() map[int32]Extension
+	extensionsRead() (map[int32]Extension, sync.Locker)
+}
+
+// extendableProtoV1 is an interface implemented by a protocol buffer generated by the previous
+// version of the proto compiler that may be extended.
+type extendableProtoV1 interface {
+	Message
+	ExtensionRangeArray() []ExtensionRange
+	ExtensionMap() map[int32]Extension
+}
+
+// extensionAdapter is a wrapper around extendableProtoV1 that implements extendableProto.
+type extensionAdapter struct {
+	extendableProtoV1
+}
+
+func (e extensionAdapter) extensionsWrite() map[int32]Extension {
+	return e.ExtensionMap()
+}
+
+func (e extensionAdapter) extensionsRead() (map[int32]Extension, sync.Locker) {
+	return e.ExtensionMap(), notLocker{}
+}
+
+// notLocker is a sync.Locker whose Lock and Unlock methods are nops.
+type notLocker struct{}
+
+func (n notLocker) Lock()   {}
+func (n notLocker) Unlock() {}
+
+// extendable returns the extendableProto interface for the given generated proto message.
+// If the proto message has the old extension format, it returns a wrapper that implements
+// the extendableProto interface.
+func extendable(p interface{}) (extendableProto, error) {
+	switch p := p.(type) {
+	case extendableProto:
+		if isNilPtr(p) {
+			return nil, fmt.Errorf("proto: nil %T is not extendable", p)
+		}
+		return p, nil
+	case extendableProtoV1:
+		if isNilPtr(p) {
+			return nil, fmt.Errorf("proto: nil %T is not extendable", p)
+		}
+		return extensionAdapter{p}, nil
+	}
+	// Don't allocate a specific error containing %T:
+	// this is the hot path for Clone and MarshalText.
+	return nil, errNotExtendable
+}
+
+var errNotExtendable = errors.New("proto: not an extendable proto.Message")
+
+func isNilPtr(x interface{}) bool {
+	v := reflect.ValueOf(x)
+	return v.Kind() == reflect.Ptr && v.IsNil()
+}
+
+// XXX_InternalExtensions is an internal representation of proto extensions.
+//
+// Each generated message struct type embeds an anonymous XXX_InternalExtensions field,
+// thus gaining the unexported 'extensions' method, which can be called only from the proto package.
+//
+// The methods of XXX_InternalExtensions are not concurrency safe in general,
+// but calls to logically read-only methods such as has and get may be executed concurrently.
+type XXX_InternalExtensions struct {
+	// The struct must be indirect so that if a user inadvertently copies a
+	// generated message and its embedded XXX_InternalExtensions, they
+	// avoid the mayhem of a copied mutex.
+	//
+	// The mutex serializes all logically read-only operations to p.extensionMap.
+	// It is up to the client to ensure that write operations to p.extensionMap are
+	// mutually exclusive with other accesses.
+	p *struct {
+		mu           sync.Mutex
+		extensionMap map[int32]Extension
+	}
+}
+
+// extensionsWrite returns the extension map, creating it on first use.
+func (e *XXX_InternalExtensions) extensionsWrite() map[int32]Extension {
+	if e.p == nil {
+		e.p = new(struct {
+			mu           sync.Mutex
+			extensionMap map[int32]Extension
+		})
+		e.p.extensionMap = make(map[int32]Extension)
+	}
+	return e.p.extensionMap
+}
+
+// extensionsRead returns the extensions map for read-only use.  It may be nil.
+// The caller must hold the returned mutex's lock when accessing Elements within the map.
+func (e *XXX_InternalExtensions) extensionsRead() (map[int32]Extension, sync.Locker) {
+	if e.p == nil {
+		return nil, nil
+	}
+	return e.p.extensionMap, &e.p.mu
+}
+
+// ExtensionDesc represents an extension specification.
+// Used in generated code from the protocol compiler.
+type ExtensionDesc struct {
+	ExtendedType  Message     // nil pointer to the type that is being extended
+	ExtensionType interface{} // nil pointer to the extension type
+	Field         int32       // field number
+	Name          string      // fully-qualified name of extension, for text formatting
+	Tag           string      // protobuf tag style
+	Filename      string      // name of the file in which the extension is defined
+}
+
+func (ed *ExtensionDesc) repeated() bool {
+	t := reflect.TypeOf(ed.ExtensionType)
+	return t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
+}
+
+// Extension represents an extension in a message.
+type Extension struct {
+	// When an extension is stored in a message using SetExtension
+	// only desc and value are set. When the message is marshaled
+	// enc will be set to the encoded form of the message.
+	//
+	// When a message is unmarshaled and contains extensions, each
+	// extension will have only enc set. When such an extension is
+	// accessed using GetExtension (or GetExtensions) desc and value
+	// will be set.
+	desc  *ExtensionDesc
+	value interface{}
+	enc   []byte
+}
+
+// SetRawExtension is for testing only.
+func SetRawExtension(base Message, id int32, b []byte) {
+	epb, err := extendable(base)
+	if err != nil {
+		return
+	}
+	extmap := epb.extensionsWrite()
+	extmap[id] = Extension{enc: b}
+}
+
+// isExtensionField returns true iff the given field number is in an extension range.
+func isExtensionField(pb extendableProto, field int32) bool {
+	for _, er := range pb.ExtensionRangeArray() {
+		if er.Start <= field && field <= er.End {
+			return true
+		}
+	}
+	return false
+}
+
+// checkExtensionTypes checks that the given extension is valid for pb.
+func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error {
+	var pbi interface{} = pb
+	// Check the extended type.
+	if ea, ok := pbi.(extensionAdapter); ok {
+		pbi = ea.extendableProtoV1
+	}
+	if a, b := reflect.TypeOf(pbi), reflect.TypeOf(extension.ExtendedType); a != b {
+		return fmt.Errorf("proto: bad extended type; %v does not extend %v", b, a)
+	}
+	// Check the range.
+	if !isExtensionField(pb, extension.Field) {
+		return errors.New("proto: bad extension number; not in declared ranges")
+	}
+	return nil
+}
+
+// extPropKey is sufficient to uniquely identify an extension.
+type extPropKey struct {
+	base  reflect.Type
+	field int32
+}
+
+var extProp = struct {
+	sync.RWMutex
+	m map[extPropKey]*Properties
+}{
+	m: make(map[extPropKey]*Properties),
+}
+
+func extensionProperties(ed *ExtensionDesc) *Properties {
+	key := extPropKey{base: reflect.TypeOf(ed.ExtendedType), field: ed.Field}
+
+	extProp.RLock()
+	if prop, ok := extProp.m[key]; ok {
+		extProp.RUnlock()
+		return prop
+	}
+	extProp.RUnlock()
+
+	extProp.Lock()
+	defer extProp.Unlock()
+	// Check again.
+	if prop, ok := extProp.m[key]; ok {
+		return prop
+	}
+
+	prop := new(Properties)
+	prop.Init(reflect.TypeOf(ed.ExtensionType), "unknown_name", ed.Tag, nil)
+	extProp.m[key] = prop
+	return prop
+}
+
+// HasExtension returns whether the given extension is present in pb.
+func HasExtension(pb Message, extension *ExtensionDesc) bool {
+	// TODO: Check types, field numbers, etc.?
+	epb, err := extendable(pb)
+	if err != nil {
+		return false
+	}
+	extmap, mu := epb.extensionsRead()
+	if extmap == nil {
+		return false
+	}
+	mu.Lock()
+	_, ok := extmap[extension.Field]
+	mu.Unlock()
+	return ok
+}
+
+// ClearExtension removes the given extension from pb.
+func ClearExtension(pb Message, extension *ExtensionDesc) {
+	epb, err := extendable(pb)
+	if err != nil {
+		return
+	}
+	// TODO: Check types, field numbers, etc.?
+	extmap := epb.extensionsWrite()
+	delete(extmap, extension.Field)
+}
+
+// GetExtension retrieves a proto2 extended field from pb.
+//
+// If the descriptor is type complete (i.e., ExtensionDesc.ExtensionType is non-nil),
+// then GetExtension parses the encoded field and returns a Go value of the specified type.
+// If the field is not present, then the default value is returned (if one is specified),
+// otherwise ErrMissingExtension is reported.
+//
+// If the descriptor is not type complete (i.e., ExtensionDesc.ExtensionType is nil),
+// then GetExtension returns the raw encoded bytes of the field extension.
+func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
+	epb, err := extendable(pb)
+	if err != nil {
+		return nil, err
+	}
+
+	if extension.ExtendedType != nil {
+		// can only check type if this is a complete descriptor
+		if err := checkExtensionTypes(epb, extension); err != nil {
+			return nil, err
+		}
+	}
+
+	emap, mu := epb.extensionsRead()
+	if emap == nil {
+		return defaultExtensionValue(extension)
+	}
+	mu.Lock()
+	defer mu.Unlock()
+	e, ok := emap[extension.Field]
+	if !ok {
+		// defaultExtensionValue returns the default value or
+		// ErrMissingExtension if there is no default.
+		return defaultExtensionValue(extension)
+	}
+
+	if e.value != nil {
+		// Already decoded. Check the descriptor, though.
+		if e.desc != extension {
+			// This shouldn't happen. If it does, it means that
+			// GetExtension was called twice with two different
+			// descriptors with the same field number.
+			return nil, errors.New("proto: descriptor conflict")
+		}
+		return e.value, nil
+	}
+
+	if extension.ExtensionType == nil {
+		// incomplete descriptor
+		return e.enc, nil
+	}
+
+	v, err := decodeExtension(e.enc, extension)
+	if err != nil {
+		return nil, err
+	}
+
+	// Remember the decoded version and drop the encoded version.
+	// That way it is safe to mutate what we return.
+	e.value = v
+	e.desc = extension
+	e.enc = nil
+	emap[extension.Field] = e
+	return e.value, nil
+}
+
+// defaultExtensionValue returns the default value for extension.
+// If no default for an extension is defined ErrMissingExtension is returned.
+func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) {
+	if extension.ExtensionType == nil {
+		// incomplete descriptor, so no default
+		return nil, ErrMissingExtension
+	}
+
+	t := reflect.TypeOf(extension.ExtensionType)
+	props := extensionProperties(extension)
+
+	sf, _, err := fieldDefault(t, props)
+	if err != nil {
+		return nil, err
+	}
+
+	if sf == nil || sf.value == nil {
+		// There is no default value.
+		return nil, ErrMissingExtension
+	}
+
+	if t.Kind() != reflect.Ptr {
+		// We do not need to return a Ptr, we can directly return sf.value.
+		return sf.value, nil
+	}
+
+	// We need to return an interface{} that is a pointer to sf.value.
+	value := reflect.New(t).Elem()
+	value.Set(reflect.New(value.Type().Elem()))
+	if sf.kind == reflect.Int32 {
+		// We may have an int32 or an enum, but the underlying data is int32.
+		// Since we can't set an int32 into a non int32 reflect.value directly
+		// set it as a int32.
+		value.Elem().SetInt(int64(sf.value.(int32)))
+	} else {
+		value.Elem().Set(reflect.ValueOf(sf.value))
+	}
+	return value.Interface(), nil
+}
+
+// decodeExtension decodes an extension encoded in b.
+func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
+	t := reflect.TypeOf(extension.ExtensionType)
+	unmarshal := typeUnmarshaler(t, extension.Tag)
+
+	// t is a pointer to a struct, pointer to basic type or a slice.
+	// Allocate space to store the pointer/slice.
+	value := reflect.New(t).Elem()
+
+	var err error
+	for {
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		wire := int(x) & 7
+
+		b, err = unmarshal(b, valToPointer(value.Addr()), wire)
+		if err != nil {
+			return nil, err
+		}
+
+		if len(b) == 0 {
+			break
+		}
+	}
+	return value.Interface(), nil
+}
+
+// GetExtensions returns a slice of the extensions present in pb that are also listed in es.
+// The returned slice has the same length as es; missing extensions will appear as nil elements.
+func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) {
+	epb, err := extendable(pb)
+	if err != nil {
+		return nil, err
+	}
+	extensions = make([]interface{}, len(es))
+	for i, e := range es {
+		extensions[i], err = GetExtension(epb, e)
+		if err == ErrMissingExtension {
+			err = nil
+		}
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+// ExtensionDescs returns a new slice containing pb's extension descriptors, in undefined order.
+// For non-registered extensions, ExtensionDescs returns an incomplete descriptor containing
+// just the Field field, which defines the extension's field number.
+func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) {
+	epb, err := extendable(pb)
+	if err != nil {
+		return nil, err
+	}
+	registeredExtensions := RegisteredExtensions(pb)
+
+	emap, mu := epb.extensionsRead()
+	if emap == nil {
+		return nil, nil
+	}
+	mu.Lock()
+	defer mu.Unlock()
+	extensions := make([]*ExtensionDesc, 0, len(emap))
+	for extid, e := range emap {
+		desc := e.desc
+		if desc == nil {
+			desc = registeredExtensions[extid]
+			if desc == nil {
+				desc = &ExtensionDesc{Field: extid}
+			}
+		}
+
+		extensions = append(extensions, desc)
+	}
+	return extensions, nil
+}
+
+// SetExtension sets the specified extension of pb to the specified value.
+func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error {
+	epb, err := extendable(pb)
+	if err != nil {
+		return err
+	}
+	if err := checkExtensionTypes(epb, extension); err != nil {
+		return err
+	}
+	typ := reflect.TypeOf(extension.ExtensionType)
+	if typ != reflect.TypeOf(value) {
+		return errors.New("proto: bad extension value type")
+	}
+	// nil extension values need to be caught early, because the
+	// encoder can't distinguish an ErrNil due to a nil extension
+	// from an ErrNil due to a missing field. Extensions are
+	// always optional, so the encoder would just swallow the error
+	// and drop all the extensions from the encoded message.
+	if reflect.ValueOf(value).IsNil() {
+		return fmt.Errorf("proto: SetExtension called with nil value of type %T", value)
+	}
+
+	extmap := epb.extensionsWrite()
+	extmap[extension.Field] = Extension{desc: extension, value: value}
+	return nil
+}
+
+// ClearAllExtensions clears all extensions from pb.
+func ClearAllExtensions(pb Message) {
+	epb, err := extendable(pb)
+	if err != nil {
+		return
+	}
+	m := epb.extensionsWrite()
+	for k := range m {
+		delete(m, k)
+	}
+}
+
+// A global registry of extensions.
+// The generated code will register the generated descriptors by calling RegisterExtension.
+
+var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc)
+
+// RegisterExtension is called from the generated code.
+func RegisterExtension(desc *ExtensionDesc) {
+	st := reflect.TypeOf(desc.ExtendedType).Elem()
+	m := extensionMaps[st]
+	if m == nil {
+		m = make(map[int32]*ExtensionDesc)
+		extensionMaps[st] = m
+	}
+	if _, ok := m[desc.Field]; ok {
+		panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field)))
+	}
+	m[desc.Field] = desc
+}
+
+// RegisteredExtensions returns a map of the registered extensions of a
+// protocol buffer struct, indexed by the extension number.
+// The argument pb should be a nil pointer to the struct type.
+func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc {
+	return extensionMaps[reflect.TypeOf(pb).Elem()]
+}
diff --git a/vendor/github.com/golang/protobuf/proto/lib.go b/vendor/github.com/golang/protobuf/proto/lib.go
new file mode 100644
index 0000000..0e2191b
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/lib.go
@@ -0,0 +1,921 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors.  All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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.
+
+/*
+Package proto converts data structures to and from the wire format of
+protocol buffers.  It works in concert with the Go source code generated
+for .proto files by the protocol compiler.
+
+A summary of the properties of the protocol buffer interface
+for a protocol buffer variable v:
+
+  - Names are turned from camel_case to CamelCase for export.
+  - There are no methods on v to set fields; just treat
+	them as structure fields.
+  - There are getters that return a field's value if set,
+	and return the field's default value if unset.
+	The getters work even if the receiver is a nil message.
+  - The zero value for a struct is its correct initialization state.
+	All desired fields must be set before marshaling.
+  - A Reset() method will restore a protobuf struct to its zero state.
+  - Non-repeated fields are pointers to the values; nil means unset.
+	That is, optional or required field int32 f becomes F *int32.
+  - Repeated fields are slices.
+  - Helper functions are available to aid the setting of fields.
+	msg.Foo = proto.String("hello") // set field
+  - Constants are defined to hold the default values of all fields that
+	have them.  They have the form Default_StructName_FieldName.
+	Because the getter methods handle defaulted values,
+	direct use of these constants should be rare.
+  - Enums are given type names and maps from names to values.
+	Enum values are prefixed by the enclosing message's name, or by the
+	enum's type name if it is a top-level enum. Enum types have a String
+	method, and a Enum method to assist in message construction.
+  - Nested messages, groups and enums have type names prefixed with the name of
+	the surrounding message type.
+  - Extensions are given descriptor names that start with E_,
+	followed by an underscore-delimited list of the nested messages
+	that contain it (if any) followed by the CamelCased name of the
+	extension field itself.  HasExtension, ClearExtension, GetExtension
+	and SetExtension are functions for manipulating extensions.
+  - Oneof field sets are given a single field in their message,
+	with distinguished wrapper types for each possible field value.
+  - Marshal and Unmarshal are functions to encode and decode the wire format.
+
+When the .proto file specifies `syntax="proto3"`, there are some differences:
+
+  - Non-repeated fields of non-message type are values instead of pointers.
+  - Enum types do not get an Enum method.
+
+The simplest way to describe this is to see an example.
+Given file test.proto, containing
+
+	package example;
+
+	enum FOO { X = 17; }
+
+	message Test {
+	  required string label = 1;
+	  optional int32 type = 2 [default=77];
+	  repeated int64 reps = 3;
+	  optional group OptionalGroup = 4 {
+	    required string RequiredField = 5;
+	  }
+	  oneof union {
+	    int32 number = 6;
+	    string name = 7;
+	  }
+	}
+
+The resulting file, test.pb.go, is:
+
+	package example
+
+	import proto "github.com/golang/protobuf/proto"
+	import math "math"
+
+	type FOO int32
+	const (
+		FOO_X FOO = 17
+	)
+	var FOO_name = map[int32]string{
+		17: "X",
+	}
+	var FOO_value = map[string]int32{
+		"X": 17,
+	}
+
+	func (x FOO) Enum() *FOO {
+		p := new(FOO)
+		*p = x
+		return p
+	}
+	func (x FOO) String() string {
+		return proto.EnumName(FOO_name, int32(x))
+	}
+	func (x *FOO) UnmarshalJSON(data []byte) error {
+		value, err := proto.UnmarshalJSONEnum(FOO_value, data)
+		if err != nil {
+			return err
+		}
+		*x = FOO(value)
+		return nil
+	}
+
+	type Test struct {
+		Label         *string             `protobuf:"bytes,1,req,name=label" json:"label,omitempty"`
+		Type          *int32              `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"`
+		Reps          []int64             `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"`
+		Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"`
+		// Types that are valid to be assigned to Union:
+		//	*Test_Number
+		//	*Test_Name
+		Union            isTest_Union `protobuf_oneof:"union"`
+		XXX_unrecognized []byte       `json:"-"`
+	}
+	func (m *Test) Reset()         { *m = Test{} }
+	func (m *Test) String() string { return proto.CompactTextString(m) }
+	func (*Test) ProtoMessage() {}
+
+	type isTest_Union interface {
+		isTest_Union()
+	}
+
+	type Test_Number struct {
+		Number int32 `protobuf:"varint,6,opt,name=number"`
+	}
+	type Test_Name struct {
+		Name string `protobuf:"bytes,7,opt,name=name"`
+	}
+
+	func (*Test_Number) isTest_Union() {}
+	func (*Test_Name) isTest_Union()   {}
+
+	func (m *Test) GetUnion() isTest_Union {
+		if m != nil {
+			return m.Union
+		}
+		return nil
+	}
+	const Default_Test_Type int32 = 77
+
+	func (m *Test) GetLabel() string {
+		if m != nil && m.Label != nil {
+			return *m.Label
+		}
+		return ""
+	}
+
+	func (m *Test) GetType() int32 {
+		if m != nil && m.Type != nil {
+			return *m.Type
+		}
+		return Default_Test_Type
+	}
+
+	func (m *Test) GetOptionalgroup() *Test_OptionalGroup {
+		if m != nil {
+			return m.Optionalgroup
+		}
+		return nil
+	}
+
+	type Test_OptionalGroup struct {
+		RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"`
+	}
+	func (m *Test_OptionalGroup) Reset()         { *m = Test_OptionalGroup{} }
+	func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) }
+
+	func (m *Test_OptionalGroup) GetRequiredField() string {
+		if m != nil && m.RequiredField != nil {
+			return *m.RequiredField
+		}
+		return ""
+	}
+
+	func (m *Test) GetNumber() int32 {
+		if x, ok := m.GetUnion().(*Test_Number); ok {
+			return x.Number
+		}
+		return 0
+	}
+
+	func (m *Test) GetName() string {
+		if x, ok := m.GetUnion().(*Test_Name); ok {
+			return x.Name
+		}
+		return ""
+	}
+
+	func init() {
+		proto.RegisterEnum("example.FOO", FOO_name, FOO_value)
+	}
+
+To create and play with a Test object:
+
+	package main
+
+	import (
+		"log"
+
+		"github.com/golang/protobuf/proto"
+		pb "./example.pb"
+	)
+
+	func main() {
+		test := &pb.Test{
+			Label: proto.String("hello"),
+			Type:  proto.Int32(17),
+			Reps:  []int64{1, 2, 3},
+			Optionalgroup: &pb.Test_OptionalGroup{
+				RequiredField: proto.String("good bye"),
+			},
+			Union: &pb.Test_Name{"fred"},
+		}
+		data, err := proto.Marshal(test)
+		if err != nil {
+			log.Fatal("marshaling error: ", err)
+		}
+		newTest := &pb.Test{}
+		err = proto.Unmarshal(data, newTest)
+		if err != nil {
+			log.Fatal("unmarshaling error: ", err)
+		}
+		// Now test and newTest contain the same data.
+		if test.GetLabel() != newTest.GetLabel() {
+			log.Fatalf("data mismatch %q != %q", test.GetLabel(), newTest.GetLabel())
+		}
+		// Use a type switch to determine which oneof was set.
+		switch u := test.Union.(type) {
+		case *pb.Test_Number: // u.Number contains the number.
+		case *pb.Test_Name: // u.Name contains the string.
+		}
+		// etc.
+	}
+*/
+package proto
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"log"
+	"reflect"
+	"sort"
+	"strconv"
+	"sync"
+)
+
+var errInvalidUTF8 = errors.New("proto: invalid UTF-8 string")
+
+// Message is implemented by generated protocol buffer messages.
+type Message interface {
+	Reset()
+	String() string
+	ProtoMessage()
+}
+
+// Stats records allocation details about the protocol buffer encoders
+// and decoders.  Useful for tuning the library itself.
+type Stats struct {
+	Emalloc uint64 // mallocs in encode
+	Dmalloc uint64 // mallocs in decode
+	Encode  uint64 // number of encodes
+	Decode  uint64 // number of decodes
+	Chit    uint64 // number of cache hits
+	Cmiss   uint64 // number of cache misses
+	Size    uint64 // number of sizes
+}
+
+// Set to true to enable stats collection.
+const collectStats = false
+
+var stats Stats
+
+// GetStats returns a copy of the global Stats structure.
+func GetStats() Stats { return stats }
+
+// A Buffer is a buffer manager for marshaling and unmarshaling
+// protocol buffers.  It may be reused between invocations to
+// reduce memory usage.  It is not necessary to use a Buffer;
+// the global functions Marshal and Unmarshal create a
+// temporary Buffer and are fine for most applications.
+type Buffer struct {
+	buf   []byte // encode/decode byte stream
+	index int    // read point
+
+	deterministic bool
+}
+
+// NewBuffer allocates a new Buffer and initializes its internal data to
+// the contents of the argument slice.
+func NewBuffer(e []byte) *Buffer {
+	return &Buffer{buf: e}
+}
+
+// Reset resets the Buffer, ready for marshaling a new protocol buffer.
+func (p *Buffer) Reset() {
+	p.buf = p.buf[0:0] // for reading/writing
+	p.index = 0        // for reading
+}
+
+// SetBuf replaces the internal buffer with the slice,
+// ready for unmarshaling the contents of the slice.
+func (p *Buffer) SetBuf(s []byte) {
+	p.buf = s
+	p.index = 0
+}
+
+// Bytes returns the contents of the Buffer.
+func (p *Buffer) Bytes() []byte { return p.buf }
+
+// SetDeterministic sets whether to use deterministic serialization.
+//
+// Deterministic serialization guarantees that for a given binary, equal
+// messages will always be serialized to the same bytes. This implies:
+//
+//   - Repeated serialization of a message will return the same bytes.
+//   - Different processes of the same binary (which may be executing on
+//     different machines) will serialize equal messages to the same bytes.
+//
+// Note that the deterministic serialization is NOT canonical across
+// languages. It is not guaranteed to remain stable over time. It is unstable
+// across different builds with schema changes due to unknown fields.
+// Users who need canonical serialization (e.g., persistent storage in a
+// canonical form, fingerprinting, etc.) should define their own
+// canonicalization specification and implement their own serializer rather
+// than relying on this API.
+//
+// If deterministic serialization is requested, map entries will be sorted
+// by keys in lexographical order. This is an implementation detail and
+// subject to change.
+func (p *Buffer) SetDeterministic(deterministic bool) {
+	p.deterministic = deterministic
+}
+
+/*
+ * Helper routines for simplifying the creation of optional fields of basic type.
+ */
+
+// Bool is a helper routine that allocates a new bool value
+// to store v and returns a pointer to it.
+func Bool(v bool) *bool {
+	return &v
+}
+
+// Int32 is a helper routine that allocates a new int32 value
+// to store v and returns a pointer to it.
+func Int32(v int32) *int32 {
+	return &v
+}
+
+// Int is a helper routine that allocates a new int32 value
+// to store v and returns a pointer to it, but unlike Int32
+// its argument value is an int.
+func Int(v int) *int32 {
+	p := new(int32)
+	*p = int32(v)
+	return p
+}
+
+// Int64 is a helper routine that allocates a new int64 value
+// to store v and returns a pointer to it.
+func Int64(v int64) *int64 {
+	return &v
+}
+
+// Float32 is a helper routine that allocates a new float32 value
+// to store v and returns a pointer to it.
+func Float32(v float32) *float32 {
+	return &v
+}
+
+// Float64 is a helper routine that allocates a new float64 value
+// to store v and returns a pointer to it.
+func Float64(v float64) *float64 {
+	return &v
+}
+
+// Uint32 is a helper routine that allocates a new uint32 value
+// to store v and returns a pointer to it.
+func Uint32(v uint32) *uint32 {
+	return &v
+}
+
+// Uint64 is a helper routine that allocates a new uint64 value
+// to store v and returns a pointer to it.
+func Uint64(v uint64) *uint64 {
+	return &v
+}
+
+// String is a helper routine that allocates a new string value
+// to store v and returns a pointer to it.
+func String(v string) *string {
+	return &v
+}
+
+// EnumName is a helper function to simplify printing protocol buffer enums
+// by name.  Given an enum map and a value, it returns a useful string.
+func EnumName(m map[int32]string, v int32) string {
+	s, ok := m[v]
+	if ok {
+		return s
+	}
+	return strconv.Itoa(int(v))
+}
+
+// UnmarshalJSONEnum is a helper function to simplify recovering enum int values
+// from their JSON-encoded representation. Given a map from the enum's symbolic
+// names to its int values, and a byte buffer containing the JSON-encoded
+// value, it returns an int32 that can be cast to the enum type by the caller.
+//
+// The function can deal with both JSON representations, numeric and symbolic.
+func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) {
+	if data[0] == '"' {
+		// New style: enums are strings.
+		var repr string
+		if err := json.Unmarshal(data, &repr); err != nil {
+			return -1, err
+		}
+		val, ok := m[repr]
+		if !ok {
+			return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr)
+		}
+		return val, nil
+	}
+	// Old style: enums are ints.
+	var val int32
+	if err := json.Unmarshal(data, &val); err != nil {
+		return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName)
+	}
+	return val, nil
+}
+
+// DebugPrint dumps the encoded data in b in a debugging format with a header
+// including the string s. Used in testing but made available for general debugging.
+func (p *Buffer) DebugPrint(s string, b []byte) {
+	var u uint64
+
+	obuf := p.buf
+	index := p.index
+	p.buf = b
+	p.index = 0
+	depth := 0
+
+	fmt.Printf("\n--- %s ---\n", s)
+
+out:
+	for {
+		for i := 0; i < depth; i++ {
+			fmt.Print("  ")
+		}
+
+		index := p.index
+		if index == len(p.buf) {
+			break
+		}
+
+		op, err := p.DecodeVarint()
+		if err != nil {
+			fmt.Printf("%3d: fetching op err %v\n", index, err)
+			break out
+		}
+		tag := op >> 3
+		wire := op & 7
+
+		switch wire {
+		default:
+			fmt.Printf("%3d: t=%3d unknown wire=%d\n",
+				index, tag, wire)
+			break out
+
+		case WireBytes:
+			var r []byte
+
+			r, err = p.DecodeRawBytes(false)
+			if err != nil {
+				break out
+			}
+			fmt.Printf("%3d: t=%3d bytes [%d]", index, tag, len(r))
+			if len(r) <= 6 {
+				for i := 0; i < len(r); i++ {
+					fmt.Printf(" %.2x", r[i])
+				}
+			} else {
+				for i := 0; i < 3; i++ {
+					fmt.Printf(" %.2x", r[i])
+				}
+				fmt.Printf(" ..")
+				for i := len(r) - 3; i < len(r); i++ {
+					fmt.Printf(" %.2x", r[i])
+				}
+			}
+			fmt.Printf("\n")
+
+		case WireFixed32:
+			u, err = p.DecodeFixed32()
+			if err != nil {
+				fmt.Printf("%3d: t=%3d fix32 err %v\n", index, tag, err)
+				break out
+			}
+			fmt.Printf("%3d: t=%3d fix32 %d\n", index, tag, u)
+
+		case WireFixed64:
+			u, err = p.DecodeFixed64()
+			if err != nil {
+				fmt.Printf("%3d: t=%3d fix64 err %v\n", index, tag, err)
+				break out
+			}
+			fmt.Printf("%3d: t=%3d fix64 %d\n", index, tag, u)
+
+		case WireVarint:
+			u, err = p.DecodeVarint()
+			if err != nil {
+				fmt.Printf("%3d: t=%3d varint err %v\n", index, tag, err)
+				break out
+			}
+			fmt.Printf("%3d: t=%3d varint %d\n", index, tag, u)
+
+		case WireStartGroup:
+			fmt.Printf("%3d: t=%3d start\n", index, tag)
+			depth++
+
+		case WireEndGroup:
+			depth--
+			fmt.Printf("%3d: t=%3d end\n", index, tag)
+		}
+	}
+
+	if depth != 0 {
+		fmt.Printf("%3d: start-end not balanced %d\n", p.index, depth)
+	}
+	fmt.Printf("\n")
+
+	p.buf = obuf
+	p.index = index
+}
+
+// SetDefaults sets unset protocol buffer fields to their default values.
+// It only modifies fields that are both unset and have defined defaults.
+// It recursively sets default values in any non-nil sub-messages.
+func SetDefaults(pb Message) {
+	setDefaults(reflect.ValueOf(pb), true, false)
+}
+
+// v is a pointer to a struct.
+func setDefaults(v reflect.Value, recur, zeros bool) {
+	v = v.Elem()
+
+	defaultMu.RLock()
+	dm, ok := defaults[v.Type()]
+	defaultMu.RUnlock()
+	if !ok {
+		dm = buildDefaultMessage(v.Type())
+		defaultMu.Lock()
+		defaults[v.Type()] = dm
+		defaultMu.Unlock()
+	}
+
+	for _, sf := range dm.scalars {
+		f := v.Field(sf.index)
+		if !f.IsNil() {
+			// field already set
+			continue
+		}
+		dv := sf.value
+		if dv == nil && !zeros {
+			// no explicit default, and don't want to set zeros
+			continue
+		}
+		fptr := f.Addr().Interface() // **T
+		// TODO: Consider batching the allocations we do here.
+		switch sf.kind {
+		case reflect.Bool:
+			b := new(bool)
+			if dv != nil {
+				*b = dv.(bool)
+			}
+			*(fptr.(**bool)) = b
+		case reflect.Float32:
+			f := new(float32)
+			if dv != nil {
+				*f = dv.(float32)
+			}
+			*(fptr.(**float32)) = f
+		case reflect.Float64:
+			f := new(float64)
+			if dv != nil {
+				*f = dv.(float64)
+			}
+			*(fptr.(**float64)) = f
+		case reflect.Int32:
+			// might be an enum
+			if ft := f.Type(); ft != int32PtrType {
+				// enum
+				f.Set(reflect.New(ft.Elem()))
+				if dv != nil {
+					f.Elem().SetInt(int64(dv.(int32)))
+				}
+			} else {
+				// int32 field
+				i := new(int32)
+				if dv != nil {
+					*i = dv.(int32)
+				}
+				*(fptr.(**int32)) = i
+			}
+		case reflect.Int64:
+			i := new(int64)
+			if dv != nil {
+				*i = dv.(int64)
+			}
+			*(fptr.(**int64)) = i
+		case reflect.String:
+			s := new(string)
+			if dv != nil {
+				*s = dv.(string)
+			}
+			*(fptr.(**string)) = s
+		case reflect.Uint8:
+			// exceptional case: []byte
+			var b []byte
+			if dv != nil {
+				db := dv.([]byte)
+				b = make([]byte, len(db))
+				copy(b, db)
+			} else {
+				b = []byte{}
+			}
+			*(fptr.(*[]byte)) = b
+		case reflect.Uint32:
+			u := new(uint32)
+			if dv != nil {
+				*u = dv.(uint32)
+			}
+			*(fptr.(**uint32)) = u
+		case reflect.Uint64:
+			u := new(uint64)
+			if dv != nil {
+				*u = dv.(uint64)
+			}
+			*(fptr.(**uint64)) = u
+		default:
+			log.Printf("proto: can't set default for field %v (sf.kind=%v)", f, sf.kind)
+		}
+	}
+
+	for _, ni := range dm.nested {
+		f := v.Field(ni)
+		// f is *T or []*T or map[T]*T
+		switch f.Kind() {
+		case reflect.Ptr:
+			if f.IsNil() {
+				continue
+			}
+			setDefaults(f, recur, zeros)
+
+		case reflect.Slice:
+			for i := 0; i < f.Len(); i++ {
+				e := f.Index(i)
+				if e.IsNil() {
+					continue
+				}
+				setDefaults(e, recur, zeros)
+			}
+
+		case reflect.Map:
+			for _, k := range f.MapKeys() {
+				e := f.MapIndex(k)
+				if e.IsNil() {
+					continue
+				}
+				setDefaults(e, recur, zeros)
+			}
+		}
+	}
+}
+
+var (
+	// defaults maps a protocol buffer struct type to a slice of the fields,
+	// with its scalar fields set to their proto-declared non-zero default values.
+	defaultMu sync.RWMutex
+	defaults  = make(map[reflect.Type]defaultMessage)
+
+	int32PtrType = reflect.TypeOf((*int32)(nil))
+)
+
+// defaultMessage represents information about the default values of a message.
+type defaultMessage struct {
+	scalars []scalarField
+	nested  []int // struct field index of nested messages
+}
+
+type scalarField struct {
+	index int          // struct field index
+	kind  reflect.Kind // element type (the T in *T or []T)
+	value interface{}  // the proto-declared default value, or nil
+}
+
+// t is a struct type.
+func buildDefaultMessage(t reflect.Type) (dm defaultMessage) {
+	sprop := GetProperties(t)
+	for _, prop := range sprop.Prop {
+		fi, ok := sprop.decoderTags.get(prop.Tag)
+		if !ok {
+			// XXX_unrecognized
+			continue
+		}
+		ft := t.Field(fi).Type
+
+		sf, nested, err := fieldDefault(ft, prop)
+		switch {
+		case err != nil:
+			log.Print(err)
+		case nested:
+			dm.nested = append(dm.nested, fi)
+		case sf != nil:
+			sf.index = fi
+			dm.scalars = append(dm.scalars, *sf)
+		}
+	}
+
+	return dm
+}
+
+// fieldDefault returns the scalarField for field type ft.
+// sf will be nil if the field can not have a default.
+// nestedMessage will be true if this is a nested message.
+// Note that sf.index is not set on return.
+func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMessage bool, err error) {
+	var canHaveDefault bool
+	switch ft.Kind() {
+	case reflect.Ptr:
+		if ft.Elem().Kind() == reflect.Struct {
+			nestedMessage = true
+		} else {
+			canHaveDefault = true // proto2 scalar field
+		}
+
+	case reflect.Slice:
+		switch ft.Elem().Kind() {
+		case reflect.Ptr:
+			nestedMessage = true // repeated message
+		case reflect.Uint8:
+			canHaveDefault = true // bytes field
+		}
+
+	case reflect.Map:
+		if ft.Elem().Kind() == reflect.Ptr {
+			nestedMessage = true // map with message values
+		}
+	}
+
+	if !canHaveDefault {
+		if nestedMessage {
+			return nil, true, nil
+		}
+		return nil, false, nil
+	}
+
+	// We now know that ft is a pointer or slice.
+	sf = &scalarField{kind: ft.Elem().Kind()}
+
+	// scalar fields without defaults
+	if !prop.HasDefault {
+		return sf, false, nil
+	}
+
+	// a scalar field: either *T or []byte
+	switch ft.Elem().Kind() {
+	case reflect.Bool:
+		x, err := strconv.ParseBool(prop.Default)
+		if err != nil {
+			return nil, false, fmt.Errorf("proto: bad default bool %q: %v", prop.Default, err)
+		}
+		sf.value = x
+	case reflect.Float32:
+		x, err := strconv.ParseFloat(prop.Default, 32)
+		if err != nil {
+			return nil, false, fmt.Errorf("proto: bad default float32 %q: %v", prop.Default, err)
+		}
+		sf.value = float32(x)
+	case reflect.Float64:
+		x, err := strconv.ParseFloat(prop.Default, 64)
+		if err != nil {
+			return nil, false, fmt.Errorf("proto: bad default float64 %q: %v", prop.Default, err)
+		}
+		sf.value = x
+	case reflect.Int32:
+		x, err := strconv.ParseInt(prop.Default, 10, 32)
+		if err != nil {
+			return nil, false, fmt.Errorf("proto: bad default int32 %q: %v", prop.Default, err)
+		}
+		sf.value = int32(x)
+	case reflect.Int64:
+		x, err := strconv.ParseInt(prop.Default, 10, 64)
+		if err != nil {
+			return nil, false, fmt.Errorf("proto: bad default int64 %q: %v", prop.Default, err)
+		}
+		sf.value = x
+	case reflect.String:
+		sf.value = prop.Default
+	case reflect.Uint8:
+		// []byte (not *uint8)
+		sf.value = []byte(prop.Default)
+	case reflect.Uint32:
+		x, err := strconv.ParseUint(prop.Default, 10, 32)
+		if err != nil {
+			return nil, false, fmt.Errorf("proto: bad default uint32 %q: %v", prop.Default, err)
+		}
+		sf.value = uint32(x)
+	case reflect.Uint64:
+		x, err := strconv.ParseUint(prop.Default, 10, 64)
+		if err != nil {
+			return nil, false, fmt.Errorf("proto: bad default uint64 %q: %v", prop.Default, err)
+		}
+		sf.value = x
+	default:
+		return nil, false, fmt.Errorf("proto: unhandled def kind %v", ft.Elem().Kind())
+	}
+
+	return sf, false, nil
+}
+
+// mapKeys returns a sort.Interface to be used for sorting the map keys.
+// Map fields may have key types of non-float scalars, strings and enums.
+func mapKeys(vs []reflect.Value) sort.Interface {
+	s := mapKeySorter{vs: vs}
+
+	// Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps.
+	if len(vs) == 0 {
+		return s
+	}
+	switch vs[0].Kind() {
+	case reflect.Int32, reflect.Int64:
+		s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
+	case reflect.Uint32, reflect.Uint64:
+		s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
+	case reflect.Bool:
+		s.less = func(a, b reflect.Value) bool { return !a.Bool() && b.Bool() } // false < true
+	case reflect.String:
+		s.less = func(a, b reflect.Value) bool { return a.String() < b.String() }
+	default:
+		panic(fmt.Sprintf("unsupported map key type: %v", vs[0].Kind()))
+	}
+
+	return s
+}
+
+type mapKeySorter struct {
+	vs   []reflect.Value
+	less func(a, b reflect.Value) bool
+}
+
+func (s mapKeySorter) Len() int      { return len(s.vs) }
+func (s mapKeySorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] }
+func (s mapKeySorter) Less(i, j int) bool {
+	return s.less(s.vs[i], s.vs[j])
+}
+
+// isProto3Zero reports whether v is a zero proto3 value.
+func isProto3Zero(v reflect.Value) bool {
+	switch v.Kind() {
+	case reflect.Bool:
+		return !v.Bool()
+	case reflect.Int32, reflect.Int64:
+		return v.Int() == 0
+	case reflect.Uint32, reflect.Uint64:
+		return v.Uint() == 0
+	case reflect.Float32, reflect.Float64:
+		return v.Float() == 0
+	case reflect.String:
+		return v.String() == ""
+	}
+	return false
+}
+
+// ProtoPackageIsVersion2 is referenced from generated protocol buffer files
+// to assert that that code is compatible with this version of the proto package.
+const ProtoPackageIsVersion2 = true
+
+// ProtoPackageIsVersion1 is referenced from generated protocol buffer files
+// to assert that that code is compatible with this version of the proto package.
+const ProtoPackageIsVersion1 = true
+
+// InternalMessageInfo is a type used internally by generated .pb.go files.
+// This type is not intended to be used by non-generated code.
+// This type is not subject to any compatibility guarantee.
+type InternalMessageInfo struct {
+	marshal   *marshalInfo
+	unmarshal *unmarshalInfo
+	merge     *mergeInfo
+	discard   *discardInfo
+}
diff --git a/vendor/github.com/golang/protobuf/proto/message_set.go b/vendor/github.com/golang/protobuf/proto/message_set.go
new file mode 100644
index 0000000..3b6ca41
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/message_set.go
@@ -0,0 +1,314 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors.  All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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.
+
+package proto
+
+/*
+ * Support for message sets.
+ */
+
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"reflect"
+	"sort"
+	"sync"
+)
+
+// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID.
+// A message type ID is required for storing a protocol buffer in a message set.
+var errNoMessageTypeID = errors.New("proto does not have a message type ID")
+
+// The first two types (_MessageSet_Item and messageSet)
+// model what the protocol compiler produces for the following protocol message:
+//   message MessageSet {
+//     repeated group Item = 1 {
+//       required int32 type_id = 2;
+//       required string message = 3;
+//     };
+//   }
+// That is the MessageSet wire format. We can't use a proto to generate these
+// because that would introduce a circular dependency between it and this package.
+
+type _MessageSet_Item struct {
+	TypeId  *int32 `protobuf:"varint,2,req,name=type_id"`
+	Message []byte `protobuf:"bytes,3,req,name=message"`
+}
+
+type messageSet struct {
+	Item             []*_MessageSet_Item `protobuf:"group,1,rep"`
+	XXX_unrecognized []byte
+	// TODO: caching?
+}
+
+// Make sure messageSet is a Message.
+var _ Message = (*messageSet)(nil)
+
+// messageTypeIder is an interface satisfied by a protocol buffer type
+// that may be stored in a MessageSet.
+type messageTypeIder interface {
+	MessageTypeId() int32
+}
+
+func (ms *messageSet) find(pb Message) *_MessageSet_Item {
+	mti, ok := pb.(messageTypeIder)
+	if !ok {
+		return nil
+	}
+	id := mti.MessageTypeId()
+	for _, item := range ms.Item {
+		if *item.TypeId == id {
+			return item
+		}
+	}
+	return nil
+}
+
+func (ms *messageSet) Has(pb Message) bool {
+	return ms.find(pb) != nil
+}
+
+func (ms *messageSet) Unmarshal(pb Message) error {
+	if item := ms.find(pb); item != nil {
+		return Unmarshal(item.Message, pb)
+	}
+	if _, ok := pb.(messageTypeIder); !ok {
+		return errNoMessageTypeID
+	}
+	return nil // TODO: return error instead?
+}
+
+func (ms *messageSet) Marshal(pb Message) error {
+	msg, err := Marshal(pb)
+	if err != nil {
+		return err
+	}
+	if item := ms.find(pb); item != nil {
+		// reuse existing item
+		item.Message = msg
+		return nil
+	}
+
+	mti, ok := pb.(messageTypeIder)
+	if !ok {
+		return errNoMessageTypeID
+	}
+
+	mtid := mti.MessageTypeId()
+	ms.Item = append(ms.Item, &_MessageSet_Item{
+		TypeId:  &mtid,
+		Message: msg,
+	})
+	return nil
+}
+
+func (ms *messageSet) Reset()         { *ms = messageSet{} }
+func (ms *messageSet) String() string { return CompactTextString(ms) }
+func (*messageSet) ProtoMessage()     {}
+
+// Support for the message_set_wire_format message option.
+
+func skipVarint(buf []byte) []byte {
+	i := 0
+	for ; buf[i]&0x80 != 0; i++ {
+	}
+	return buf[i+1:]
+}
+
+// MarshalMessageSet encodes the extension map represented by m in the message set wire format.
+// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option.
+func MarshalMessageSet(exts interface{}) ([]byte, error) {
+	return marshalMessageSet(exts, false)
+}
+
+// marshaMessageSet implements above function, with the opt to turn on / off deterministic during Marshal.
+func marshalMessageSet(exts interface{}, deterministic bool) ([]byte, error) {
+	switch exts := exts.(type) {
+	case *XXX_InternalExtensions:
+		var u marshalInfo
+		siz := u.sizeMessageSet(exts)
+		b := make([]byte, 0, siz)
+		return u.appendMessageSet(b, exts, deterministic)
+
+	case map[int32]Extension:
+		// This is an old-style extension map.
+		// Wrap it in a new-style XXX_InternalExtensions.
+		ie := XXX_InternalExtensions{
+			p: &struct {
+				mu           sync.Mutex
+				extensionMap map[int32]Extension
+			}{
+				extensionMap: exts,
+			},
+		}
+
+		var u marshalInfo
+		siz := u.sizeMessageSet(&ie)
+		b := make([]byte, 0, siz)
+		return u.appendMessageSet(b, &ie, deterministic)
+
+	default:
+		return nil, errors.New("proto: not an extension map")
+	}
+}
+
+// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
+// It is called by Unmarshal methods on protocol buffer messages with the message_set_wire_format option.
+func UnmarshalMessageSet(buf []byte, exts interface{}) error {
+	var m map[int32]Extension
+	switch exts := exts.(type) {
+	case *XXX_InternalExtensions:
+		m = exts.extensionsWrite()
+	case map[int32]Extension:
+		m = exts
+	default:
+		return errors.New("proto: not an extension map")
+	}
+
+	ms := new(messageSet)
+	if err := Unmarshal(buf, ms); err != nil {
+		return err
+	}
+	for _, item := range ms.Item {
+		id := *item.TypeId
+		msg := item.Message
+
+		// Restore wire type and field number varint, plus length varint.
+		// Be careful to preserve duplicate items.
+		b := EncodeVarint(uint64(id)<<3 | WireBytes)
+		if ext, ok := m[id]; ok {
+			// Existing data; rip off the tag and length varint
+			// so we join the new data correctly.
+			// We can assume that ext.enc is set because we are unmarshaling.
+			o := ext.enc[len(b):]   // skip wire type and field number
+			_, n := DecodeVarint(o) // calculate length of length varint
+			o = o[n:]               // skip length varint
+			msg = append(o, msg...) // join old data and new data
+		}
+		b = append(b, EncodeVarint(uint64(len(msg)))...)
+		b = append(b, msg...)
+
+		m[id] = Extension{enc: b}
+	}
+	return nil
+}
+
+// MarshalMessageSetJSON encodes the extension map represented by m in JSON format.
+// It is called by generated MarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
+func MarshalMessageSetJSON(exts interface{}) ([]byte, error) {
+	var m map[int32]Extension
+	switch exts := exts.(type) {
+	case *XXX_InternalExtensions:
+		var mu sync.Locker
+		m, mu = exts.extensionsRead()
+		if m != nil {
+			// Keep the extensions map locked until we're done marshaling to prevent
+			// races between marshaling and unmarshaling the lazily-{en,de}coded
+			// values.
+			mu.Lock()
+			defer mu.Unlock()
+		}
+	case map[int32]Extension:
+		m = exts
+	default:
+		return nil, errors.New("proto: not an extension map")
+	}
+	var b bytes.Buffer
+	b.WriteByte('{')
+
+	// Process the map in key order for deterministic output.
+	ids := make([]int32, 0, len(m))
+	for id := range m {
+		ids = append(ids, id)
+	}
+	sort.Sort(int32Slice(ids)) // int32Slice defined in text.go
+
+	for i, id := range ids {
+		ext := m[id]
+		msd, ok := messageSetMap[id]
+		if !ok {
+			// Unknown type; we can't render it, so skip it.
+			continue
+		}
+
+		if i > 0 && b.Len() > 1 {
+			b.WriteByte(',')
+		}
+
+		fmt.Fprintf(&b, `"[%s]":`, msd.name)
+
+		x := ext.value
+		if x == nil {
+			x = reflect.New(msd.t.Elem()).Interface()
+			if err := Unmarshal(ext.enc, x.(Message)); err != nil {
+				return nil, err
+			}
+		}
+		d, err := json.Marshal(x)
+		if err != nil {
+			return nil, err
+		}
+		b.Write(d)
+	}
+	b.WriteByte('}')
+	return b.Bytes(), nil
+}
+
+// UnmarshalMessageSetJSON decodes the extension map encoded in buf in JSON format.
+// It is called by generated UnmarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
+func UnmarshalMessageSetJSON(buf []byte, exts interface{}) error {
+	// Common-case fast path.
+	if len(buf) == 0 || bytes.Equal(buf, []byte("{}")) {
+		return nil
+	}
+
+	// This is fairly tricky, and it's not clear that it is needed.
+	return errors.New("TODO: UnmarshalMessageSetJSON not yet implemented")
+}
+
+// A global registry of types that can be used in a MessageSet.
+
+var messageSetMap = make(map[int32]messageSetDesc)
+
+type messageSetDesc struct {
+	t    reflect.Type // pointer to struct
+	name string
+}
+
+// RegisterMessageSetType is called from the generated code.
+func RegisterMessageSetType(m Message, fieldNum int32, name string) {
+	messageSetMap[fieldNum] = messageSetDesc{
+		t:    reflect.TypeOf(m),
+		name: name,
+	}
+}
diff --git a/vendor/github.com/golang/protobuf/proto/pointer_reflect.go b/vendor/github.com/golang/protobuf/proto/pointer_reflect.go
new file mode 100644
index 0000000..b6cad90
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/pointer_reflect.go
@@ -0,0 +1,357 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2012 The Go Authors.  All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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.
+
+// +build purego appengine js
+
+// This file contains an implementation of proto field accesses using package reflect.
+// It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can
+// be used on App Engine.
+
+package proto
+
+import (
+	"reflect"
+	"sync"
+)
+
+const unsafeAllowed = false
+
+// A field identifies a field in a struct, accessible from a pointer.
+// In this implementation, a field is identified by the sequence of field indices
+// passed to reflect's FieldByIndex.
+type field []int
+
+// toField returns a field equivalent to the given reflect field.
+func toField(f *reflect.StructField) field {
+	return f.Index
+}
+
+// invalidField is an invalid field identifier.
+var invalidField = field(nil)
+
+// zeroField is a noop when calling pointer.offset.
+var zeroField = field([]int{})
+
+// IsValid reports whether the field identifier is valid.
+func (f field) IsValid() bool { return f != nil }
+
+// The pointer type is for the table-driven decoder.
+// The implementation here uses a reflect.Value of pointer type to
+// create a generic pointer. In pointer_unsafe.go we use unsafe
+// instead of reflect to implement the same (but faster) interface.
+type pointer struct {
+	v reflect.Value
+}
+
+// toPointer converts an interface of pointer type to a pointer
+// that points to the same target.
+func toPointer(i *Message) pointer {
+	return pointer{v: reflect.ValueOf(*i)}
+}
+
+// toAddrPointer converts an interface to a pointer that points to
+// the interface data.
+func toAddrPointer(i *interface{}, isptr bool) pointer {
+	v := reflect.ValueOf(*i)
+	u := reflect.New(v.Type())
+	u.Elem().Set(v)
+	return pointer{v: u}
+}
+
+// valToPointer converts v to a pointer.  v must be of pointer type.
+func valToPointer(v reflect.Value) pointer {
+	return pointer{v: v}
+}
+
+// offset converts from a pointer to a structure to a pointer to
+// one of its fields.
+func (p pointer) offset(f field) pointer {
+	return pointer{v: p.v.Elem().FieldByIndex(f).Addr()}
+}
+
+func (p pointer) isNil() bool {
+	return p.v.IsNil()
+}
+
+// grow updates the slice s in place to make it one element longer.
+// s must be addressable.
+// Returns the (addressable) new element.
+func grow(s reflect.Value) reflect.Value {
+	n, m := s.Len(), s.Cap()
+	if n < m {
+		s.SetLen(n + 1)
+	} else {
+		s.Set(reflect.Append(s, reflect.Zero(s.Type().Elem())))
+	}
+	return s.Index(n)
+}
+
+func (p pointer) toInt64() *int64 {
+	return p.v.Interface().(*int64)
+}
+func (p pointer) toInt64Ptr() **int64 {
+	return p.v.Interface().(**int64)
+}
+func (p pointer) toInt64Slice() *[]int64 {
+	return p.v.Interface().(*[]int64)
+}
+
+var int32ptr = reflect.TypeOf((*int32)(nil))
+
+func (p pointer) toInt32() *int32 {
+	return p.v.Convert(int32ptr).Interface().(*int32)
+}
+
+// The toInt32Ptr/Slice methods don't work because of enums.
+// Instead, we must use set/get methods for the int32ptr/slice case.
+/*
+	func (p pointer) toInt32Ptr() **int32 {
+		return p.v.Interface().(**int32)
+}
+	func (p pointer) toInt32Slice() *[]int32 {
+		return p.v.Interface().(*[]int32)
+}
+*/
+func (p pointer) getInt32Ptr() *int32 {
+	if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) {
+		// raw int32 type
+		return p.v.Elem().Interface().(*int32)
+	}
+	// an enum
+	return p.v.Elem().Convert(int32PtrType).Interface().(*int32)
+}
+func (p pointer) setInt32Ptr(v int32) {
+	// Allocate value in a *int32. Possibly convert that to a *enum.
+	// Then assign it to a **int32 or **enum.
+	// Note: we can convert *int32 to *enum, but we can't convert
+	// **int32 to **enum!
+	p.v.Elem().Set(reflect.ValueOf(&v).Convert(p.v.Type().Elem()))
+}
+
+// getInt32Slice copies []int32 from p as a new slice.
+// This behavior differs from the implementation in pointer_unsafe.go.
+func (p pointer) getInt32Slice() []int32 {
+	if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) {
+		// raw int32 type
+		return p.v.Elem().Interface().([]int32)
+	}
+	// an enum
+	// Allocate a []int32, then assign []enum's values into it.
+	// Note: we can't convert []enum to []int32.
+	slice := p.v.Elem()
+	s := make([]int32, slice.Len())
+	for i := 0; i < slice.Len(); i++ {
+		s[i] = int32(slice.Index(i).Int())
+	}
+	return s
+}
+
+// setInt32Slice copies []int32 into p as a new slice.
+// This behavior differs from the implementation in pointer_unsafe.go.
+func (p pointer) setInt32Slice(v []int32) {
+	if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) {
+		// raw int32 type
+		p.v.Elem().Set(reflect.ValueOf(v))
+		return
+	}
+	// an enum
+	// Allocate a []enum, then assign []int32's values into it.
+	// Note: we can't convert []enum to []int32.
+	slice := reflect.MakeSlice(p.v.Type().Elem(), len(v), cap(v))
+	for i, x := range v {
+		slice.Index(i).SetInt(int64(x))
+	}
+	p.v.Elem().Set(slice)
+}
+func (p pointer) appendInt32Slice(v int32) {
+	grow(p.v.Elem()).SetInt(int64(v))
+}
+
+func (p pointer) toUint64() *uint64 {
+	return p.v.Interface().(*uint64)
+}
+func (p pointer) toUint64Ptr() **uint64 {
+	return p.v.Interface().(**uint64)
+}
+func (p pointer) toUint64Slice() *[]uint64 {
+	return p.v.Interface().(*[]uint64)
+}
+func (p pointer) toUint32() *uint32 {
+	return p.v.Interface().(*uint32)
+}
+func (p pointer) toUint32Ptr() **uint32 {
+	return p.v.Interface().(**uint32)
+}
+func (p pointer) toUint32Slice() *[]uint32 {
+	return p.v.Interface().(*[]uint32)
+}
+func (p pointer) toBool() *bool {
+	return p.v.Interface().(*bool)
+}
+func (p pointer) toBoolPtr() **bool {
+	return p.v.Interface().(**bool)
+}
+func (p pointer) toBoolSlice() *[]bool {
+	return p.v.Interface().(*[]bool)
+}
+func (p pointer) toFloat64() *float64 {
+	return p.v.Interface().(*float64)
+}
+func (p pointer) toFloat64Ptr() **float64 {
+	return p.v.Interface().(**float64)
+}
+func (p pointer) toFloat64Slice() *[]float64 {
+	return p.v.Interface().(*[]float64)
+}
+func (p pointer) toFloat32() *float32 {
+	return p.v.Interface().(*float32)
+}
+func (p pointer) toFloat32Ptr() **float32 {
+	return p.v.Interface().(**float32)
+}
+func (p pointer) toFloat32Slice() *[]float32 {
+	return p.v.Interface().(*[]float32)
+}
+func (p pointer) toString() *string {
+	return p.v.Interface().(*string)
+}
+func (p pointer) toStringPtr() **string {
+	return p.v.Interface().(**string)
+}
+func (p pointer) toStringSlice() *[]string {
+	return p.v.Interface().(*[]string)
+}
+func (p pointer) toBytes() *[]byte {
+	return p.v.Interface().(*[]byte)
+}
+func (p pointer) toBytesSlice() *[][]byte {
+	return p.v.Interface().(*[][]byte)
+}
+func (p pointer) toExtensions() *XXX_InternalExtensions {
+	return p.v.Interface().(*XXX_InternalExtensions)
+}
+func (p pointer) toOldExtensions() *map[int32]Extension {
+	return p.v.Interface().(*map[int32]Extension)
+}
+func (p pointer) getPointer() pointer {
+	return pointer{v: p.v.Elem()}
+}
+func (p pointer) setPointer(q pointer) {
+	p.v.Elem().Set(q.v)
+}
+func (p pointer) appendPointer(q pointer) {
+	grow(p.v.Elem()).Set(q.v)
+}
+
+// getPointerSlice copies []*T from p as a new []pointer.
+// This behavior differs from the implementation in pointer_unsafe.go.
+func (p pointer) getPointerSlice() []pointer {
+	if p.v.IsNil() {
+		return nil
+	}
+	n := p.v.Elem().Len()
+	s := make([]pointer, n)
+	for i := 0; i < n; i++ {
+		s[i] = pointer{v: p.v.Elem().Index(i)}
+	}
+	return s
+}
+
+// setPointerSlice copies []pointer into p as a new []*T.
+// This behavior differs from the implementation in pointer_unsafe.go.
+func (p pointer) setPointerSlice(v []pointer) {
+	if v == nil {
+		p.v.Elem().Set(reflect.New(p.v.Elem().Type()).Elem())
+		return
+	}
+	s := reflect.MakeSlice(p.v.Elem().Type(), 0, len(v))
+	for _, p := range v {
+		s = reflect.Append(s, p.v)
+	}
+	p.v.Elem().Set(s)
+}
+
+// getInterfacePointer returns a pointer that points to the
+// interface data of the interface pointed by p.
+func (p pointer) getInterfacePointer() pointer {
+	if p.v.Elem().IsNil() {
+		return pointer{v: p.v.Elem()}
+	}
+	return pointer{v: p.v.Elem().Elem().Elem().Field(0).Addr()} // *interface -> interface -> *struct -> struct
+}
+
+func (p pointer) asPointerTo(t reflect.Type) reflect.Value {
+	// TODO: check that p.v.Type().Elem() == t?
+	return p.v
+}
+
+func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo {
+	atomicLock.Lock()
+	defer atomicLock.Unlock()
+	return *p
+}
+func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) {
+	atomicLock.Lock()
+	defer atomicLock.Unlock()
+	*p = v
+}
+func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo {
+	atomicLock.Lock()
+	defer atomicLock.Unlock()
+	return *p
+}
+func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) {
+	atomicLock.Lock()
+	defer atomicLock.Unlock()
+	*p = v
+}
+func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo {
+	atomicLock.Lock()
+	defer atomicLock.Unlock()
+	return *p
+}
+func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) {
+	atomicLock.Lock()
+	defer atomicLock.Unlock()
+	*p = v
+}
+func atomicLoadDiscardInfo(p **discardInfo) *discardInfo {
+	atomicLock.Lock()
+	defer atomicLock.Unlock()
+	return *p
+}
+func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) {
+	atomicLock.Lock()
+	defer atomicLock.Unlock()
+	*p = v
+}
+
+var atomicLock sync.Mutex
diff --git a/vendor/github.com/golang/protobuf/proto/pointer_unsafe.go b/vendor/github.com/golang/protobuf/proto/pointer_unsafe.go
new file mode 100644
index 0000000..d55a335
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/pointer_unsafe.go
@@ -0,0 +1,308 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2012 The Go Authors.  All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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.
+
+// +build !purego,!appengine,!js
+
+// This file contains the implementation of the proto field accesses using package unsafe.
+
+package proto
+
+import (
+	"reflect"
+	"sync/atomic"
+	"unsafe"
+)
+
+const unsafeAllowed = true
+
+// A field identifies a field in a struct, accessible from a pointer.
+// In this implementation, a field is identified by its byte offset from the start of the struct.
+type field uintptr
+
+// toField returns a field equivalent to the given reflect field.
+func toField(f *reflect.StructField) field {
+	return field(f.Offset)
+}
+
+// invalidField is an invalid field identifier.
+const invalidField = ^field(0)
+
+// zeroField is a noop when calling pointer.offset.
+const zeroField = field(0)
+
+// IsValid reports whether the field identifier is valid.
+func (f field) IsValid() bool {
+	return f != invalidField
+}
+
+// The pointer type below is for the new table-driven encoder/decoder.
+// The implementation here uses unsafe.Pointer to create a generic pointer.
+// In pointer_reflect.go we use reflect instead of unsafe to implement
+// the same (but slower) interface.
+type pointer struct {
+	p unsafe.Pointer
+}
+
+// size of pointer
+var ptrSize = unsafe.Sizeof(uintptr(0))
+
+// toPointer converts an interface of pointer type to a pointer
+// that points to the same target.
+func toPointer(i *Message) pointer {
+	// Super-tricky - read pointer out of data word of interface value.
+	// Saves ~25ns over the equivalent:
+	// return valToPointer(reflect.ValueOf(*i))
+	return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]}
+}
+
+// toAddrPointer converts an interface to a pointer that points to
+// the interface data.
+func toAddrPointer(i *interface{}, isptr bool) pointer {
+	// Super-tricky - read or get the address of data word of interface value.
+	if isptr {
+		// The interface is of pointer type, thus it is a direct interface.
+		// The data word is the pointer data itself. We take its address.
+		return pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)}
+	}
+	// The interface is not of pointer type. The data word is the pointer
+	// to the data.
+	return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]}
+}
+
+// valToPointer converts v to a pointer. v must be of pointer type.
+func valToPointer(v reflect.Value) pointer {
+	return pointer{p: unsafe.Pointer(v.Pointer())}
+}
+
+// offset converts from a pointer to a structure to a pointer to
+// one of its fields.
+func (p pointer) offset(f field) pointer {
+	// For safety, we should panic if !f.IsValid, however calling panic causes
+	// this to no longer be inlineable, which is a serious performance cost.
+	/*
+		if !f.IsValid() {
+			panic("invalid field")
+		}
+	*/
+	return pointer{p: unsafe.Pointer(uintptr(p.p) + uintptr(f))}
+}
+
+func (p pointer) isNil() bool {
+	return p.p == nil
+}
+
+func (p pointer) toInt64() *int64 {
+	return (*int64)(p.p)
+}
+func (p pointer) toInt64Ptr() **int64 {
+	return (**int64)(p.p)
+}
+func (p pointer) toInt64Slice() *[]int64 {
+	return (*[]int64)(p.p)
+}
+func (p pointer) toInt32() *int32 {
+	return (*int32)(p.p)
+}
+
+// See pointer_reflect.go for why toInt32Ptr/Slice doesn't exist.
+/*
+	func (p pointer) toInt32Ptr() **int32 {
+		return (**int32)(p.p)
+	}
+	func (p pointer) toInt32Slice() *[]int32 {
+		return (*[]int32)(p.p)
+	}
+*/
+func (p pointer) getInt32Ptr() *int32 {
+	return *(**int32)(p.p)
+}
+func (p pointer) setInt32Ptr(v int32) {
+	*(**int32)(p.p) = &v
+}
+
+// getInt32Slice loads a []int32 from p.
+// The value returned is aliased with the original slice.
+// This behavior differs from the implementation in pointer_reflect.go.
+func (p pointer) getInt32Slice() []int32 {
+	return *(*[]int32)(p.p)
+}
+
+// setInt32Slice stores a []int32 to p.
+// The value set is aliased with the input slice.
+// This behavior differs from the implementation in pointer_reflect.go.
+func (p pointer) setInt32Slice(v []int32) {
+	*(*[]int32)(p.p) = v
+}
+
+// TODO: Can we get rid of appendInt32Slice and use setInt32Slice instead?
+func (p pointer) appendInt32Slice(v int32) {
+	s := (*[]int32)(p.p)
+	*s = append(*s, v)
+}
+
+func (p pointer) toUint64() *uint64 {
+	return (*uint64)(p.p)
+}
+func (p pointer) toUint64Ptr() **uint64 {
+	return (**uint64)(p.p)
+}
+func (p pointer) toUint64Slice() *[]uint64 {
+	return (*[]uint64)(p.p)
+}
+func (p pointer) toUint32() *uint32 {
+	return (*uint32)(p.p)
+}
+func (p pointer) toUint32Ptr() **uint32 {
+	return (**uint32)(p.p)
+}
+func (p pointer) toUint32Slice() *[]uint32 {
+	return (*[]uint32)(p.p)
+}
+func (p pointer) toBool() *bool {
+	return (*bool)(p.p)
+}
+func (p pointer) toBoolPtr() **bool {
+	return (**bool)(p.p)
+}
+func (p pointer) toBoolSlice() *[]bool {
+	return (*[]bool)(p.p)
+}
+func (p pointer) toFloat64() *float64 {
+	return (*float64)(p.p)
+}
+func (p pointer) toFloat64Ptr() **float64 {
+	return (**float64)(p.p)
+}
+func (p pointer) toFloat64Slice() *[]float64 {
+	return (*[]float64)(p.p)
+}
+func (p pointer) toFloat32() *float32 {
+	return (*float32)(p.p)
+}
+func (p pointer) toFloat32Ptr() **float32 {
+	return (**float32)(p.p)
+}
+func (p pointer) toFloat32Slice() *[]float32 {
+	return (*[]float32)(p.p)
+}
+func (p pointer) toString() *string {
+	return (*string)(p.p)
+}
+func (p pointer) toStringPtr() **string {
+	return (**string)(p.p)
+}
+func (p pointer) toStringSlice() *[]string {
+	return (*[]string)(p.p)
+}
+func (p pointer) toBytes() *[]byte {
+	return (*[]byte)(p.p)
+}
+func (p pointer) toBytesSlice() *[][]byte {
+	return (*[][]byte)(p.p)
+}
+func (p pointer) toExtensions() *XXX_InternalExtensions {
+	return (*XXX_InternalExtensions)(p.p)
+}
+func (p pointer) toOldExtensions() *map[int32]Extension {
+	return (*map[int32]Extension)(p.p)
+}
+
+// getPointerSlice loads []*T from p as a []pointer.
+// The value returned is aliased with the original slice.
+// This behavior differs from the implementation in pointer_reflect.go.
+func (p pointer) getPointerSlice() []pointer {
+	// Super-tricky - p should point to a []*T where T is a
+	// message type. We load it as []pointer.
+	return *(*[]pointer)(p.p)
+}
+
+// setPointerSlice stores []pointer into p as a []*T.
+// The value set is aliased with the input slice.
+// This behavior differs from the implementation in pointer_reflect.go.
+func (p pointer) setPointerSlice(v []pointer) {
+	// Super-tricky - p should point to a []*T where T is a
+	// message type. We store it as []pointer.
+	*(*[]pointer)(p.p) = v
+}
+
+// getPointer loads the pointer at p and returns it.
+func (p pointer) getPointer() pointer {
+	return pointer{p: *(*unsafe.Pointer)(p.p)}
+}
+
+// setPointer stores the pointer q at p.
+func (p pointer) setPointer(q pointer) {
+	*(*unsafe.Pointer)(p.p) = q.p
+}
+
+// append q to the slice pointed to by p.
+func (p pointer) appendPointer(q pointer) {
+	s := (*[]unsafe.Pointer)(p.p)
+	*s = append(*s, q.p)
+}
+
+// getInterfacePointer returns a pointer that points to the
+// interface data of the interface pointed by p.
+func (p pointer) getInterfacePointer() pointer {
+	// Super-tricky - read pointer out of data word of interface value.
+	return pointer{p: (*(*[2]unsafe.Pointer)(p.p))[1]}
+}
+
+// asPointerTo returns a reflect.Value that is a pointer to an
+// object of type t stored at p.
+func (p pointer) asPointerTo(t reflect.Type) reflect.Value {
+	return reflect.NewAt(t, p.p)
+}
+
+func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo {
+	return (*unmarshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
+}
+func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) {
+	atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
+}
+func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo {
+	return (*marshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
+}
+func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) {
+	atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
+}
+func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo {
+	return (*mergeInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
+}
+func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) {
+	atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
+}
+func atomicLoadDiscardInfo(p **discardInfo) *discardInfo {
+	return (*discardInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p))))
+}
+func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) {
+	atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v))
+}
diff --git a/vendor/github.com/golang/protobuf/proto/properties.go b/vendor/github.com/golang/protobuf/proto/properties.go
new file mode 100644
index 0000000..f710ada
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/properties.go
@@ -0,0 +1,544 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors.  All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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.
+
+package proto
+
+/*
+ * Routines for encoding data into the wire format for protocol buffers.
+ */
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"reflect"
+	"sort"
+	"strconv"
+	"strings"
+	"sync"
+)
+
+const debug bool = false
+
+// Constants that identify the encoding of a value on the wire.
+const (
+	WireVarint     = 0
+	WireFixed64    = 1
+	WireBytes      = 2
+	WireStartGroup = 3
+	WireEndGroup   = 4
+	WireFixed32    = 5
+)
+
+// tagMap is an optimization over map[int]int for typical protocol buffer
+// use-cases. Encoded protocol buffers are often in tag order with small tag
+// numbers.
+type tagMap struct {
+	fastTags []int
+	slowTags map[int]int
+}
+
+// tagMapFastLimit is the upper bound on the tag number that will be stored in
+// the tagMap slice rather than its map.
+const tagMapFastLimit = 1024
+
+func (p *tagMap) get(t int) (int, bool) {
+	if t > 0 && t < tagMapFastLimit {
+		if t >= len(p.fastTags) {
+			return 0, false
+		}
+		fi := p.fastTags[t]
+		return fi, fi >= 0
+	}
+	fi, ok := p.slowTags[t]
+	return fi, ok
+}
+
+func (p *tagMap) put(t int, fi int) {
+	if t > 0 && t < tagMapFastLimit {
+		for len(p.fastTags) < t+1 {
+			p.fastTags = append(p.fastTags, -1)
+		}
+		p.fastTags[t] = fi
+		return
+	}
+	if p.slowTags == nil {
+		p.slowTags = make(map[int]int)
+	}
+	p.slowTags[t] = fi
+}
+
+// StructProperties represents properties for all the fields of a struct.
+// decoderTags and decoderOrigNames should only be used by the decoder.
+type StructProperties struct {
+	Prop             []*Properties  // properties for each field
+	reqCount         int            // required count
+	decoderTags      tagMap         // map from proto tag to struct field number
+	decoderOrigNames map[string]int // map from original name to struct field number
+	order            []int          // list of struct field numbers in tag order
+
+	// OneofTypes contains information about the oneof fields in this message.
+	// It is keyed by the original name of a field.
+	OneofTypes map[string]*OneofProperties
+}
+
+// OneofProperties represents information about a specific field in a oneof.
+type OneofProperties struct {
+	Type  reflect.Type // pointer to generated struct type for this oneof field
+	Field int          // struct field number of the containing oneof in the message
+	Prop  *Properties
+}
+
+// Implement the sorting interface so we can sort the fields in tag order, as recommended by the spec.
+// See encode.go, (*Buffer).enc_struct.
+
+func (sp *StructProperties) Len() int { return len(sp.order) }
+func (sp *StructProperties) Less(i, j int) bool {
+	return sp.Prop[sp.order[i]].Tag < sp.Prop[sp.order[j]].Tag
+}
+func (sp *StructProperties) Swap(i, j int) { sp.order[i], sp.order[j] = sp.order[j], sp.order[i] }
+
+// Properties represents the protocol-specific behavior of a single struct field.
+type Properties struct {
+	Name     string // name of the field, for error messages
+	OrigName string // original name before protocol compiler (always set)
+	JSONName string // name to use for JSON; determined by protoc
+	Wire     string
+	WireType int
+	Tag      int
+	Required bool
+	Optional bool
+	Repeated bool
+	Packed   bool   // relevant for repeated primitives only
+	Enum     string // set for enum types only
+	proto3   bool   // whether this is known to be a proto3 field; set for []byte only
+	oneof    bool   // whether this is a oneof field
+
+	Default    string // default value
+	HasDefault bool   // whether an explicit default was provided
+
+	stype reflect.Type      // set for struct types only
+	sprop *StructProperties // set for struct types only
+
+	mtype    reflect.Type // set for map types only
+	mkeyprop *Properties  // set for map types only
+	mvalprop *Properties  // set for map types only
+}
+
+// String formats the properties in the protobuf struct field tag style.
+func (p *Properties) String() string {
+	s := p.Wire
+	s += ","
+	s += strconv.Itoa(p.Tag)
+	if p.Required {
+		s += ",req"
+	}
+	if p.Optional {
+		s += ",opt"
+	}
+	if p.Repeated {
+		s += ",rep"
+	}
+	if p.Packed {
+		s += ",packed"
+	}
+	s += ",name=" + p.OrigName
+	if p.JSONName != p.OrigName {
+		s += ",json=" + p.JSONName
+	}
+	if p.proto3 {
+		s += ",proto3"
+	}
+	if p.oneof {
+		s += ",oneof"
+	}
+	if len(p.Enum) > 0 {
+		s += ",enum=" + p.Enum
+	}
+	if p.HasDefault {
+		s += ",def=" + p.Default
+	}
+	return s
+}
+
+// Parse populates p by parsing a string in the protobuf struct field tag style.
+func (p *Properties) Parse(s string) {
+	// "bytes,49,opt,name=foo,def=hello!"
+	fields := strings.Split(s, ",") // breaks def=, but handled below.
+	if len(fields) < 2 {
+		fmt.Fprintf(os.Stderr, "proto: tag has too few fields: %q\n", s)
+		return
+	}
+
+	p.Wire = fields[0]
+	switch p.Wire {
+	case "varint":
+		p.WireType = WireVarint
+	case "fixed32":
+		p.WireType = WireFixed32
+	case "fixed64":
+		p.WireType = WireFixed64
+	case "zigzag32":
+		p.WireType = WireVarint
+	case "zigzag64":
+		p.WireType = WireVarint
+	case "bytes", "group":
+		p.WireType = WireBytes
+		// no numeric converter for non-numeric types
+	default:
+		fmt.Fprintf(os.Stderr, "proto: tag has unknown wire type: %q\n", s)
+		return
+	}
+
+	var err error
+	p.Tag, err = strconv.Atoi(fields[1])
+	if err != nil {
+		return
+	}
+
+outer:
+	for i := 2; i < len(fields); i++ {
+		f := fields[i]
+		switch {
+		case f == "req":
+			p.Required = true
+		case f == "opt":
+			p.Optional = true
+		case f == "rep":
+			p.Repeated = true
+		case f == "packed":
+			p.Packed = true
+		case strings.HasPrefix(f, "name="):
+			p.OrigName = f[5:]
+		case strings.HasPrefix(f, "json="):
+			p.JSONName = f[5:]
+		case strings.HasPrefix(f, "enum="):
+			p.Enum = f[5:]
+		case f == "proto3":
+			p.proto3 = true
+		case f == "oneof":
+			p.oneof = true
+		case strings.HasPrefix(f, "def="):
+			p.HasDefault = true
+			p.Default = f[4:] // rest of string
+			if i+1 < len(fields) {
+				// Commas aren't escaped, and def is always last.
+				p.Default += "," + strings.Join(fields[i+1:], ",")
+				break outer
+			}
+		}
+	}
+}
+
+var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem()
+
+// setFieldProps initializes the field properties for submessages and maps.
+func (p *Properties) setFieldProps(typ reflect.Type, f *reflect.StructField, lockGetProp bool) {
+	switch t1 := typ; t1.Kind() {
+	case reflect.Ptr:
+		if t1.Elem().Kind() == reflect.Struct {
+			p.stype = t1.Elem()
+		}
+
+	case reflect.Slice:
+		if t2 := t1.Elem(); t2.Kind() == reflect.Ptr && t2.Elem().Kind() == reflect.Struct {
+			p.stype = t2.Elem()
+		}
+
+	case reflect.Map:
+		p.mtype = t1
+		p.mkeyprop = &Properties{}
+		p.mkeyprop.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
+		p.mvalprop = &Properties{}
+		vtype := p.mtype.Elem()
+		if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice {
+			// The value type is not a message (*T) or bytes ([]byte),
+			// so we need encoders for the pointer to this type.
+			vtype = reflect.PtrTo(vtype)
+		}
+		p.mvalprop.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp)
+	}
+
+	if p.stype != nil {
+		if lockGetProp {
+			p.sprop = GetProperties(p.stype)
+		} else {
+			p.sprop = getPropertiesLocked(p.stype)
+		}
+	}
+}
+
+var (
+	marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
+)
+
+// Init populates the properties from a protocol buffer struct tag.
+func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
+	p.init(typ, name, tag, f, true)
+}
+
+func (p *Properties) init(typ reflect.Type, name, tag string, f *reflect.StructField, lockGetProp bool) {
+	// "bytes,49,opt,def=hello!"
+	p.Name = name
+	p.OrigName = name
+	if tag == "" {
+		return
+	}
+	p.Parse(tag)
+	p.setFieldProps(typ, f, lockGetProp)
+}
+
+var (
+	propertiesMu  sync.RWMutex
+	propertiesMap = make(map[reflect.Type]*StructProperties)
+)
+
+// GetProperties returns the list of properties for the type represented by t.
+// t must represent a generated struct type of a protocol message.
+func GetProperties(t reflect.Type) *StructProperties {
+	if t.Kind() != reflect.Struct {
+		panic("proto: type must have kind struct")
+	}
+
+	// Most calls to GetProperties in a long-running program will be
+	// retrieving details for types we have seen before.
+	propertiesMu.RLock()
+	sprop, ok := propertiesMap[t]
+	propertiesMu.RUnlock()
+	if ok {
+		if collectStats {
+			stats.Chit++
+		}
+		return sprop
+	}
+
+	propertiesMu.Lock()
+	sprop = getPropertiesLocked(t)
+	propertiesMu.Unlock()
+	return sprop
+}
+
+// getPropertiesLocked requires that propertiesMu is held.
+func getPropertiesLocked(t reflect.Type) *StructProperties {
+	if prop, ok := propertiesMap[t]; ok {
+		if collectStats {
+			stats.Chit++
+		}
+		return prop
+	}
+	if collectStats {
+		stats.Cmiss++
+	}
+
+	prop := new(StructProperties)
+	// in case of recursive protos, fill this in now.
+	propertiesMap[t] = prop
+
+	// build properties
+	prop.Prop = make([]*Properties, t.NumField())
+	prop.order = make([]int, t.NumField())
+
+	for i := 0; i < t.NumField(); i++ {
+		f := t.Field(i)
+		p := new(Properties)
+		name := f.Name
+		p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false)
+
+		oneof := f.Tag.Get("protobuf_oneof") // special case
+		if oneof != "" {
+			// Oneof fields don't use the traditional protobuf tag.
+			p.OrigName = oneof
+		}
+		prop.Prop[i] = p
+		prop.order[i] = i
+		if debug {
+			print(i, " ", f.Name, " ", t.String(), " ")
+			if p.Tag > 0 {
+				print(p.String())
+			}
+			print("\n")
+		}
+	}
+
+	// Re-order prop.order.
+	sort.Sort(prop)
+
+	type oneofMessage interface {
+		XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
+	}
+	if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
+		var oots []interface{}
+		_, _, _, oots = om.XXX_OneofFuncs()
+
+		// Interpret oneof metadata.
+		prop.OneofTypes = make(map[string]*OneofProperties)
+		for _, oot := range oots {
+			oop := &OneofProperties{
+				Type: reflect.ValueOf(oot).Type(), // *T
+				Prop: new(Properties),
+			}
+			sft := oop.Type.Elem().Field(0)
+			oop.Prop.Name = sft.Name
+			oop.Prop.Parse(sft.Tag.Get("protobuf"))
+			// There will be exactly one interface field that
+			// this new value is assignable to.
+			for i := 0; i < t.NumField(); i++ {
+				f := t.Field(i)
+				if f.Type.Kind() != reflect.Interface {
+					continue
+				}
+				if !oop.Type.AssignableTo(f.Type) {
+					continue
+				}
+				oop.Field = i
+				break
+			}
+			prop.OneofTypes[oop.Prop.OrigName] = oop
+		}
+	}
+
+	// build required counts
+	// build tags
+	reqCount := 0
+	prop.decoderOrigNames = make(map[string]int)
+	for i, p := range prop.Prop {
+		if strings.HasPrefix(p.Name, "XXX_") {
+			// Internal fields should not appear in tags/origNames maps.
+			// They are handled specially when encoding and decoding.
+			continue
+		}
+		if p.Required {
+			reqCount++
+		}
+		prop.decoderTags.put(p.Tag, i)
+		prop.decoderOrigNames[p.OrigName] = i
+	}
+	prop.reqCount = reqCount
+
+	return prop
+}
+
+// A global registry of enum types.
+// The generated code will register the generated maps by calling RegisterEnum.
+
+var enumValueMaps = make(map[string]map[string]int32)
+
+// RegisterEnum is called from the generated code to install the enum descriptor
+// maps into the global table to aid parsing text format protocol buffers.
+func RegisterEnum(typeName string, unusedNameMap map[int32]string, valueMap map[string]int32) {
+	if _, ok := enumValueMaps[typeName]; ok {
+		panic("proto: duplicate enum registered: " + typeName)
+	}
+	enumValueMaps[typeName] = valueMap
+}
+
+// EnumValueMap returns the mapping from names to integers of the
+// enum type enumType, or a nil if not found.
+func EnumValueMap(enumType string) map[string]int32 {
+	return enumValueMaps[enumType]
+}
+
+// A registry of all linked message types.
+// The string is a fully-qualified proto name ("pkg.Message").
+var (
+	protoTypedNils = make(map[string]Message)      // a map from proto names to typed nil pointers
+	protoMapTypes  = make(map[string]reflect.Type) // a map from proto names to map types
+	revProtoTypes  = make(map[reflect.Type]string)
+)
+
+// RegisterType is called from generated code and maps from the fully qualified
+// proto name to the type (pointer to struct) of the protocol buffer.
+func RegisterType(x Message, name string) {
+	if _, ok := protoTypedNils[name]; ok {
+		// TODO: Some day, make this a panic.
+		log.Printf("proto: duplicate proto type registered: %s", name)
+		return
+	}
+	t := reflect.TypeOf(x)
+	if v := reflect.ValueOf(x); v.Kind() == reflect.Ptr && v.Pointer() == 0 {
+		// Generated code always calls RegisterType with nil x.
+		// This check is just for extra safety.
+		protoTypedNils[name] = x
+	} else {
+		protoTypedNils[name] = reflect.Zero(t).Interface().(Message)
+	}
+	revProtoTypes[t] = name
+}
+
+// RegisterMapType is called from generated code and maps from the fully qualified
+// proto name to the native map type of the proto map definition.
+func RegisterMapType(x interface{}, name string) {
+	if reflect.TypeOf(x).Kind() != reflect.Map {
+		panic(fmt.Sprintf("RegisterMapType(%T, %q); want map", x, name))
+	}
+	if _, ok := protoMapTypes[name]; ok {
+		log.Printf("proto: duplicate proto type registered: %s", name)
+		return
+	}
+	t := reflect.TypeOf(x)
+	protoMapTypes[name] = t
+	revProtoTypes[t] = name
+}
+
+// MessageName returns the fully-qualified proto name for the given message type.
+func MessageName(x Message) string {
+	type xname interface {
+		XXX_MessageName() string
+	}
+	if m, ok := x.(xname); ok {
+		return m.XXX_MessageName()
+	}
+	return revProtoTypes[reflect.TypeOf(x)]
+}
+
+// MessageType returns the message type (pointer to struct) for a named message.
+// The type is not guaranteed to implement proto.Message if the name refers to a
+// map entry.
+func MessageType(name string) reflect.Type {
+	if t, ok := protoTypedNils[name]; ok {
+		return reflect.TypeOf(t)
+	}
+	return protoMapTypes[name]
+}
+
+// A registry of all linked proto files.
+var (
+	protoFiles = make(map[string][]byte) // file name => fileDescriptor
+)
+
+// RegisterFile is called from generated code and maps from the
+// full file name of a .proto file to its compressed FileDescriptorProto.
+func RegisterFile(filename string, fileDescriptor []byte) {
+	protoFiles[filename] = fileDescriptor
+}
+
+// FileDescriptor returns the compressed FileDescriptorProto for a .proto file.
+func FileDescriptor(filename string) []byte { return protoFiles[filename] }
diff --git a/vendor/github.com/golang/protobuf/proto/table_marshal.go b/vendor/github.com/golang/protobuf/proto/table_marshal.go
new file mode 100644
index 0000000..0f212b3
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/table_marshal.go
@@ -0,0 +1,2681 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2016 The Go Authors.  All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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.
+
+package proto
+
+import (
+	"errors"
+	"fmt"
+	"math"
+	"reflect"
+	"sort"
+	"strconv"
+	"strings"
+	"sync"
+	"sync/atomic"
+	"unicode/utf8"
+)
+
+// a sizer takes a pointer to a field and the size of its tag, computes the size of
+// the encoded data.
+type sizer func(pointer, int) int
+
+// a marshaler takes a byte slice, a pointer to a field, and its tag (in wire format),
+// marshals the field to the end of the slice, returns the slice and error (if any).
+type marshaler func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error)
+
+// marshalInfo is the information used for marshaling a message.
+type marshalInfo struct {
+	typ          reflect.Type
+	fields       []*marshalFieldInfo
+	unrecognized field                      // offset of XXX_unrecognized
+	extensions   field                      // offset of XXX_InternalExtensions
+	v1extensions field                      // offset of XXX_extensions
+	sizecache    field                      // offset of XXX_sizecache
+	initialized  int32                      // 0 -- only typ is set, 1 -- fully initialized
+	messageset   bool                       // uses message set wire format
+	hasmarshaler bool                       // has custom marshaler
+	sync.RWMutex                            // protect extElems map, also for initialization
+	extElems     map[int32]*marshalElemInfo // info of extension elements
+}
+
+// marshalFieldInfo is the information used for marshaling a field of a message.
+type marshalFieldInfo struct {
+	field      field
+	wiretag    uint64 // tag in wire format
+	tagsize    int    // size of tag in wire format
+	sizer      sizer
+	marshaler  marshaler
+	isPointer  bool
+	required   bool                              // field is required
+	name       string                            // name of the field, for error reporting
+	oneofElems map[reflect.Type]*marshalElemInfo // info of oneof elements
+}
+
+// marshalElemInfo is the information used for marshaling an extension or oneof element.
+type marshalElemInfo struct {
+	wiretag   uint64 // tag in wire format
+	tagsize   int    // size of tag in wire format
+	sizer     sizer
+	marshaler marshaler
+	isptr     bool // elem is pointer typed, thus interface of this type is a direct interface (extension only)
+}
+
+var (
+	marshalInfoMap  = map[reflect.Type]*marshalInfo{}
+	marshalInfoLock sync.Mutex
+)
+
+// getMarshalInfo returns the information to marshal a given type of message.
+// The info it returns may not necessarily initialized.
+// t is the type of the message (NOT the pointer to it).
+func getMarshalInfo(t reflect.Type) *marshalInfo {
+	marshalInfoLock.Lock()
+	u, ok := marshalInfoMap[t]
+	if !ok {
+		u = &marshalInfo{typ: t}
+		marshalInfoMap[t] = u
+	}
+	marshalInfoLock.Unlock()
+	return u
+}
+
+// Size is the entry point from generated code,
+// and should be ONLY called by generated code.
+// It computes the size of encoded data of msg.
+// a is a pointer to a place to store cached marshal info.
+func (a *InternalMessageInfo) Size(msg Message) int {
+	u := getMessageMarshalInfo(msg, a)
+	ptr := toPointer(&msg)
+	if ptr.isNil() {
+		// We get here if msg is a typed nil ((*SomeMessage)(nil)),
+		// so it satisfies the interface, and msg == nil wouldn't
+		// catch it. We don't want crash in this case.
+		return 0
+	}
+	return u.size(ptr)
+}
+
+// Marshal is the entry point from generated code,
+// and should be ONLY called by generated code.
+// It marshals msg to the end of b.
+// a is a pointer to a place to store cached marshal info.
+func (a *InternalMessageInfo) Marshal(b []byte, msg Message, deterministic bool) ([]byte, error) {
+	u := getMessageMarshalInfo(msg, a)
+	ptr := toPointer(&msg)
+	if ptr.isNil() {
+		// We get here if msg is a typed nil ((*SomeMessage)(nil)),
+		// so it satisfies the interface, and msg == nil wouldn't
+		// catch it. We don't want crash in this case.
+		return b, ErrNil
+	}
+	return u.marshal(b, ptr, deterministic)
+}
+
+func getMessageMarshalInfo(msg interface{}, a *InternalMessageInfo) *marshalInfo {
+	// u := a.marshal, but atomically.
+	// We use an atomic here to ensure memory consistency.
+	u := atomicLoadMarshalInfo(&a.marshal)
+	if u == nil {
+		// Get marshal information from type of message.
+		t := reflect.ValueOf(msg).Type()
+		if t.Kind() != reflect.Ptr {
+			panic(fmt.Sprintf("cannot handle non-pointer message type %v", t))
+		}
+		u = getMarshalInfo(t.Elem())
+		// Store it in the cache for later users.
+		// a.marshal = u, but atomically.
+		atomicStoreMarshalInfo(&a.marshal, u)
+	}
+	return u
+}
+
+// size is the main function to compute the size of the encoded data of a message.
+// ptr is the pointer to the message.
+func (u *marshalInfo) size(ptr pointer) int {
+	if atomic.LoadInt32(&u.initialized) == 0 {
+		u.computeMarshalInfo()
+	}
+
+	// If the message can marshal itself, let it do it, for compatibility.
+	// NOTE: This is not efficient.
+	if u.hasmarshaler {
+		m := ptr.asPointerTo(u.typ).Interface().(Marshaler)
+		b, _ := m.Marshal()
+		return len(b)
+	}
+
+	n := 0
+	for _, f := range u.fields {
+		if f.isPointer && ptr.offset(f.field).getPointer().isNil() {
+			// nil pointer always marshals to nothing
+			continue
+		}
+		n += f.sizer(ptr.offset(f.field), f.tagsize)
+	}
+	if u.extensions.IsValid() {
+		e := ptr.offset(u.extensions).toExtensions()
+		if u.messageset {
+			n += u.sizeMessageSet(e)
+		} else {
+			n += u.sizeExtensions(e)
+		}
+	}
+	if u.v1extensions.IsValid() {
+		m := *ptr.offset(u.v1extensions).toOldExtensions()
+		n += u.sizeV1Extensions(m)
+	}
+	if u.unrecognized.IsValid() {
+		s := *ptr.offset(u.unrecognized).toBytes()
+		n += len(s)
+	}
+	// cache the result for use in marshal
+	if u.sizecache.IsValid() {
+		atomic.StoreInt32(ptr.offset(u.sizecache).toInt32(), int32(n))
+	}
+	return n
+}
+
+// cachedsize gets the size from cache. If there is no cache (i.e. message is not generated),
+// fall back to compute the size.
+func (u *marshalInfo) cachedsize(ptr pointer) int {
+	if u.sizecache.IsValid() {
+		return int(atomic.LoadInt32(ptr.offset(u.sizecache).toInt32()))
+	}
+	return u.size(ptr)
+}
+
+// marshal is the main function to marshal a message. It takes a byte slice and appends
+// the encoded data to the end of the slice, returns the slice and error (if any).
+// ptr is the pointer to the message.
+// If deterministic is true, map is marshaled in deterministic order.
+func (u *marshalInfo) marshal(b []byte, ptr pointer, deterministic bool) ([]byte, error) {
+	if atomic.LoadInt32(&u.initialized) == 0 {
+		u.computeMarshalInfo()
+	}
+
+	// If the message can marshal itself, let it do it, for compatibility.
+	// NOTE: This is not efficient.
+	if u.hasmarshaler {
+		m := ptr.asPointerTo(u.typ).Interface().(Marshaler)
+		b1, err := m.Marshal()
+		b = append(b, b1...)
+		return b, err
+	}
+
+	var err, errreq error
+	// The old marshaler encodes extensions at beginning.
+	if u.extensions.IsValid() {
+		e := ptr.offset(u.extensions).toExtensions()
+		if u.messageset {
+			b, err = u.appendMessageSet(b, e, deterministic)
+		} else {
+			b, err = u.appendExtensions(b, e, deterministic)
+		}
+		if err != nil {
+			return b, err
+		}
+	}
+	if u.v1extensions.IsValid() {
+		m := *ptr.offset(u.v1extensions).toOldExtensions()
+		b, err = u.appendV1Extensions(b, m, deterministic)
+		if err != nil {
+			return b, err
+		}
+	}
+	for _, f := range u.fields {
+		if f.required && errreq == nil {
+			if ptr.offset(f.field).getPointer().isNil() {
+				// Required field is not set.
+				// We record the error but keep going, to give a complete marshaling.
+				errreq = &RequiredNotSetError{f.name}
+				continue
+			}
+		}
+		if f.isPointer && ptr.offset(f.field).getPointer().isNil() {
+			// nil pointer always marshals to nothing
+			continue
+		}
+		b, err = f.marshaler(b, ptr.offset(f.field), f.wiretag, deterministic)
+		if err != nil {
+			if err1, ok := err.(*RequiredNotSetError); ok {
+				// Required field in submessage is not set.
+				// We record the error but keep going, to give a complete marshaling.
+				if errreq == nil {
+					errreq = &RequiredNotSetError{f.name + "." + err1.field}
+				}
+				continue
+			}
+			if err == errRepeatedHasNil {
+				err = errors.New("proto: repeated field " + f.name + " has nil element")
+			}
+			return b, err
+		}
+	}
+	if u.unrecognized.IsValid() {
+		s := *ptr.offset(u.unrecognized).toBytes()
+		b = append(b, s...)
+	}
+	return b, errreq
+}
+
+// computeMarshalInfo initializes the marshal info.
+func (u *marshalInfo) computeMarshalInfo() {
+	u.Lock()
+	defer u.Unlock()
+	if u.initialized != 0 { // non-atomic read is ok as it is protected by the lock
+		return
+	}
+
+	t := u.typ
+	u.unrecognized = invalidField
+	u.extensions = invalidField
+	u.v1extensions = invalidField
+	u.sizecache = invalidField
+
+	// If the message can marshal itself, let it do it, for compatibility.
+	// NOTE: This is not efficient.
+	if reflect.PtrTo(t).Implements(marshalerType) {
+		u.hasmarshaler = true
+		atomic.StoreInt32(&u.initialized, 1)
+		return
+	}
+
+	// get oneof implementers
+	var oneofImplementers []interface{}
+	if m, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
+		_, _, _, oneofImplementers = m.XXX_OneofFuncs()
+	}
+
+	n := t.NumField()
+
+	// deal with XXX fields first
+	for i := 0; i < t.NumField(); i++ {
+		f := t.Field(i)
+		if !strings.HasPrefix(f.Name, "XXX_") {
+			continue
+		}
+		switch f.Name {
+		case "XXX_sizecache":
+			u.sizecache = toField(&f)
+		case "XXX_unrecognized":
+			u.unrecognized = toField(&f)
+		case "XXX_InternalExtensions":
+			u.extensions = toField(&f)
+			u.messageset = f.Tag.Get("protobuf_messageset") == "1"
+		case "XXX_extensions":
+			u.v1extensions = toField(&f)
+		case "XXX_NoUnkeyedLiteral":
+			// nothing to do
+		default:
+			panic("unknown XXX field: " + f.Name)
+		}
+		n--
+	}
+
+	// normal fields
+	fields := make([]marshalFieldInfo, n) // batch allocation
+	u.fields = make([]*marshalFieldInfo, 0, n)
+	for i, j := 0, 0; i < t.NumField(); i++ {
+		f := t.Field(i)
+
+		if strings.HasPrefix(f.Name, "XXX_") {
+			continue
+		}
+		field := &fields[j]
+		j++
+		field.name = f.Name
+		u.fields = append(u.fields, field)
+		if f.Tag.Get("protobuf_oneof") != "" {
+			field.computeOneofFieldInfo(&f, oneofImplementers)
+			continue
+		}
+		if f.Tag.Get("protobuf") == "" {
+			// field has no tag (not in generated message), ignore it
+			u.fields = u.fields[:len(u.fields)-1]
+			j--
+			continue
+		}
+		field.computeMarshalFieldInfo(&f)
+	}
+
+	// fields are marshaled in tag order on the wire.
+	sort.Sort(byTag(u.fields))
+
+	atomic.StoreInt32(&u.initialized, 1)
+}
+
+// helper for sorting fields by tag
+type byTag []*marshalFieldInfo
+
+func (a byTag) Len() int           { return len(a) }
+func (a byTag) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+func (a byTag) Less(i, j int) bool { return a[i].wiretag < a[j].wiretag }
+
+// getExtElemInfo returns the information to marshal an extension element.
+// The info it returns is initialized.
+func (u *marshalInfo) getExtElemInfo(desc *ExtensionDesc) *marshalElemInfo {
+	// get from cache first
+	u.RLock()
+	e, ok := u.extElems[desc.Field]
+	u.RUnlock()
+	if ok {
+		return e
+	}
+
+	t := reflect.TypeOf(desc.ExtensionType) // pointer or slice to basic type or struct
+	tags := strings.Split(desc.Tag, ",")
+	tag, err := strconv.Atoi(tags[1])
+	if err != nil {
+		panic("tag is not an integer")
+	}
+	wt := wiretype(tags[0])
+	sizer, marshaler := typeMarshaler(t, tags, false, false)
+	e = &marshalElemInfo{
+		wiretag:   uint64(tag)<<3 | wt,
+		tagsize:   SizeVarint(uint64(tag) << 3),
+		sizer:     sizer,
+		marshaler: marshaler,
+		isptr:     t.Kind() == reflect.Ptr,
+	}
+
+	// update cache
+	u.Lock()
+	if u.extElems == nil {
+		u.extElems = make(map[int32]*marshalElemInfo)
+	}
+	u.extElems[desc.Field] = e
+	u.Unlock()
+	return e
+}
+
+// computeMarshalFieldInfo fills up the information to marshal a field.
+func (fi *marshalFieldInfo) computeMarshalFieldInfo(f *reflect.StructField) {
+	// parse protobuf tag of the field.
+	// tag has format of "bytes,49,opt,name=foo,def=hello!"
+	tags := strings.Split(f.Tag.Get("protobuf"), ",")
+	if tags[0] == "" {
+		return
+	}
+	tag, err := strconv.Atoi(tags[1])
+	if err != nil {
+		panic("tag is not an integer")
+	}
+	wt := wiretype(tags[0])
+	if tags[2] == "req" {
+		fi.required = true
+	}
+	fi.setTag(f, tag, wt)
+	fi.setMarshaler(f, tags)
+}
+
+func (fi *marshalFieldInfo) computeOneofFieldInfo(f *reflect.StructField, oneofImplementers []interface{}) {
+	fi.field = toField(f)
+	fi.wiretag = 1<<31 - 1 // Use a large tag number, make oneofs sorted at the end. This tag will not appear on the wire.
+	fi.isPointer = true
+	fi.sizer, fi.marshaler = makeOneOfMarshaler(fi, f)
+	fi.oneofElems = make(map[reflect.Type]*marshalElemInfo)
+
+	ityp := f.Type // interface type
+	for _, o := range oneofImplementers {
+		t := reflect.TypeOf(o)
+		if !t.Implements(ityp) {
+			continue
+		}
+		sf := t.Elem().Field(0) // oneof implementer is a struct with a single field
+		tags := strings.Split(sf.Tag.Get("protobuf"), ",")
+		tag, err := strconv.Atoi(tags[1])
+		if err != nil {
+			panic("tag is not an integer")
+		}
+		wt := wiretype(tags[0])
+		sizer, marshaler := typeMarshaler(sf.Type, tags, false, true) // oneof should not omit any zero value
+		fi.oneofElems[t.Elem()] = &marshalElemInfo{
+			wiretag:   uint64(tag)<<3 | wt,
+			tagsize:   SizeVarint(uint64(tag) << 3),
+			sizer:     sizer,
+			marshaler: marshaler,
+		}
+	}
+}
+
+type oneofMessage interface {
+	XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
+}
+
+// wiretype returns the wire encoding of the type.
+func wiretype(encoding string) uint64 {
+	switch encoding {
+	case "fixed32":
+		return WireFixed32
+	case "fixed64":
+		return WireFixed64
+	case "varint", "zigzag32", "zigzag64":
+		return WireVarint
+	case "bytes":
+		return WireBytes
+	case "group":
+		return WireStartGroup
+	}
+	panic("unknown wire type " + encoding)
+}
+
+// setTag fills up the tag (in wire format) and its size in the info of a field.
+func (fi *marshalFieldInfo) setTag(f *reflect.StructField, tag int, wt uint64) {
+	fi.field = toField(f)
+	fi.wiretag = uint64(tag)<<3 | wt
+	fi.tagsize = SizeVarint(uint64(tag) << 3)
+}
+
+// setMarshaler fills up the sizer and marshaler in the info of a field.
+func (fi *marshalFieldInfo) setMarshaler(f *reflect.StructField, tags []string) {
+	switch f.Type.Kind() {
+	case reflect.Map:
+		// map field
+		fi.isPointer = true
+		fi.sizer, fi.marshaler = makeMapMarshaler(f)
+		return
+	case reflect.Ptr, reflect.Slice:
+		fi.isPointer = true
+	}
+	fi.sizer, fi.marshaler = typeMarshaler(f.Type, tags, true, false)
+}
+
+// typeMarshaler returns the sizer and marshaler of a given field.
+// t is the type of the field.
+// tags is the generated "protobuf" tag of the field.
+// If nozero is true, zero value is not marshaled to the wire.
+// If oneof is true, it is a oneof field.
+func typeMarshaler(t reflect.Type, tags []string, nozero, oneof bool) (sizer, marshaler) {
+	encoding := tags[0]
+
+	pointer := false
+	slice := false
+	if t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 {
+		slice = true
+		t = t.Elem()
+	}
+	if t.Kind() == reflect.Ptr {
+		pointer = true
+		t = t.Elem()
+	}
+
+	packed := false
+	proto3 := false
+	for i := 2; i < len(tags); i++ {
+		if tags[i] == "packed" {
+			packed = true
+		}
+		if tags[i] == "proto3" {
+			proto3 = true
+		}
+	}
+
+	switch t.Kind() {
+	case reflect.Bool:
+		if pointer {
+			return sizeBoolPtr, appendBoolPtr
+		}
+		if slice {
+			if packed {
+				return sizeBoolPackedSlice, appendBoolPackedSlice
+			}
+			return sizeBoolSlice, appendBoolSlice
+		}
+		if nozero {
+			return sizeBoolValueNoZero, appendBoolValueNoZero
+		}
+		return sizeBoolValue, appendBoolValue
+	case reflect.Uint32:
+		switch encoding {
+		case "fixed32":
+			if pointer {
+				return sizeFixed32Ptr, appendFixed32Ptr
+			}
+			if slice {
+				if packed {
+					return sizeFixed32PackedSlice, appendFixed32PackedSlice
+				}
+				return sizeFixed32Slice, appendFixed32Slice
+			}
+			if nozero {
+				return sizeFixed32ValueNoZero, appendFixed32ValueNoZero
+			}
+			return sizeFixed32Value, appendFixed32Value
+		case "varint":
+			if pointer {
+				return sizeVarint32Ptr, appendVarint32Ptr
+			}
+			if slice {
+				if packed {
+					return sizeVarint32PackedSlice, appendVarint32PackedSlice
+				}
+				return sizeVarint32Slice, appendVarint32Slice
+			}
+			if nozero {
+				return sizeVarint32ValueNoZero, appendVarint32ValueNoZero
+			}
+			return sizeVarint32Value, appendVarint32Value
+		}
+	case reflect.Int32:
+		switch encoding {
+		case "fixed32":
+			if pointer {
+				return sizeFixedS32Ptr, appendFixedS32Ptr
+			}
+			if slice {
+				if packed {
+					return sizeFixedS32PackedSlice, appendFixedS32PackedSlice
+				}
+				return sizeFixedS32Slice, appendFixedS32Slice
+			}
+			if nozero {
+				return sizeFixedS32ValueNoZero, appendFixedS32ValueNoZero
+			}
+			return sizeFixedS32Value, appendFixedS32Value
+		case "varint":
+			if pointer {
+				return sizeVarintS32Ptr, appendVarintS32Ptr
+			}
+			if slice {
+				if packed {
+					return sizeVarintS32PackedSlice, appendVarintS32PackedSlice
+				}
+				return sizeVarintS32Slice, appendVarintS32Slice
+			}
+			if nozero {
+				return sizeVarintS32ValueNoZero, appendVarintS32ValueNoZero
+			}
+			return sizeVarintS32Value, appendVarintS32Value
+		case "zigzag32":
+			if pointer {
+				return sizeZigzag32Ptr, appendZigzag32Ptr
+			}
+			if slice {
+				if packed {
+					return sizeZigzag32PackedSlice, appendZigzag32PackedSlice
+				}
+				return sizeZigzag32Slice, appendZigzag32Slice
+			}
+			if nozero {
+				return sizeZigzag32ValueNoZero, appendZigzag32ValueNoZero
+			}
+			return sizeZigzag32Value, appendZigzag32Value
+		}
+	case reflect.Uint64:
+		switch encoding {
+		case "fixed64":
+			if pointer {
+				return sizeFixed64Ptr, appendFixed64Ptr
+			}
+			if slice {
+				if packed {
+					return sizeFixed64PackedSlice, appendFixed64PackedSlice
+				}
+				return sizeFixed64Slice, appendFixed64Slice
+			}
+			if nozero {
+				return sizeFixed64ValueNoZero, appendFixed64ValueNoZero
+			}
+			return sizeFixed64Value, appendFixed64Value
+		case "varint":
+			if pointer {
+				return sizeVarint64Ptr, appendVarint64Ptr
+			}
+			if slice {
+				if packed {
+					return sizeVarint64PackedSlice, appendVarint64PackedSlice
+				}
+				return sizeVarint64Slice, appendVarint64Slice
+			}
+			if nozero {
+				return sizeVarint64ValueNoZero, appendVarint64ValueNoZero
+			}
+			return sizeVarint64Value, appendVarint64Value
+		}
+	case reflect.Int64:
+		switch encoding {
+		case "fixed64":
+			if pointer {
+				return sizeFixedS64Ptr, appendFixedS64Ptr
+			}
+			if slice {
+				if packed {
+					return sizeFixedS64PackedSlice, appendFixedS64PackedSlice
+				}
+				return sizeFixedS64Slice, appendFixedS64Slice
+			}
+			if nozero {
+				return sizeFixedS64ValueNoZero, appendFixedS64ValueNoZero
+			}
+			return sizeFixedS64Value, appendFixedS64Value
+		case "varint":
+			if pointer {
+				return sizeVarintS64Ptr, appendVarintS64Ptr
+			}
+			if slice {
+				if packed {
+					return sizeVarintS64PackedSlice, appendVarintS64PackedSlice
+				}
+				return sizeVarintS64Slice, appendVarintS64Slice
+			}
+			if nozero {
+				return sizeVarintS64ValueNoZero, appendVarintS64ValueNoZero
+			}
+			return sizeVarintS64Value, appendVarintS64Value
+		case "zigzag64":
+			if pointer {
+				return sizeZigzag64Ptr, appendZigzag64Ptr
+			}
+			if slice {
+				if packed {
+					return sizeZigzag64PackedSlice, appendZigzag64PackedSlice
+				}
+				return sizeZigzag64Slice, appendZigzag64Slice
+			}
+			if nozero {
+				return sizeZigzag64ValueNoZero, appendZigzag64ValueNoZero
+			}
+			return sizeZigzag64Value, appendZigzag64Value
+		}
+	case reflect.Float32:
+		if pointer {
+			return sizeFloat32Ptr, appendFloat32Ptr
+		}
+		if slice {
+			if packed {
+				return sizeFloat32PackedSlice, appendFloat32PackedSlice
+			}
+			return sizeFloat32Slice, appendFloat32Slice
+		}
+		if nozero {
+			return sizeFloat32ValueNoZero, appendFloat32ValueNoZero
+		}
+		return sizeFloat32Value, appendFloat32Value
+	case reflect.Float64:
+		if pointer {
+			return sizeFloat64Ptr, appendFloat64Ptr
+		}
+		if slice {
+			if packed {
+				return sizeFloat64PackedSlice, appendFloat64PackedSlice
+			}
+			return sizeFloat64Slice, appendFloat64Slice
+		}
+		if nozero {
+			return sizeFloat64ValueNoZero, appendFloat64ValueNoZero
+		}
+		return sizeFloat64Value, appendFloat64Value
+	case reflect.String:
+		if pointer {
+			return sizeStringPtr, appendStringPtr
+		}
+		if slice {
+			return sizeStringSlice, appendStringSlice
+		}
+		if nozero {
+			return sizeStringValueNoZero, appendStringValueNoZero
+		}
+		return sizeStringValue, appendStringValue
+	case reflect.Slice:
+		if slice {
+			return sizeBytesSlice, appendBytesSlice
+		}
+		if oneof {
+			// Oneof bytes field may also have "proto3" tag.
+			// We want to marshal it as a oneof field. Do this
+			// check before the proto3 check.
+			return sizeBytesOneof, appendBytesOneof
+		}
+		if proto3 {
+			return sizeBytes3, appendBytes3
+		}
+		return sizeBytes, appendBytes
+	case reflect.Struct:
+		switch encoding {
+		case "group":
+			if slice {
+				return makeGroupSliceMarshaler(getMarshalInfo(t))
+			}
+			return makeGroupMarshaler(getMarshalInfo(t))
+		case "bytes":
+			if slice {
+				return makeMessageSliceMarshaler(getMarshalInfo(t))
+			}
+			return makeMessageMarshaler(getMarshalInfo(t))
+		}
+	}
+	panic(fmt.Sprintf("unknown or mismatched type: type: %v, wire type: %v", t, encoding))
+}
+
+// Below are functions to size/marshal a specific type of a field.
+// They are stored in the field's info, and called by function pointers.
+// They have type sizer or marshaler.
+
+func sizeFixed32Value(_ pointer, tagsize int) int {
+	return 4 + tagsize
+}
+func sizeFixed32ValueNoZero(ptr pointer, tagsize int) int {
+	v := *ptr.toUint32()
+	if v == 0 {
+		return 0
+	}
+	return 4 + tagsize
+}
+func sizeFixed32Ptr(ptr pointer, tagsize int) int {
+	p := *ptr.toUint32Ptr()
+	if p == nil {
+		return 0
+	}
+	return 4 + tagsize
+}
+func sizeFixed32Slice(ptr pointer, tagsize int) int {
+	s := *ptr.toUint32Slice()
+	return (4 + tagsize) * len(s)
+}
+func sizeFixed32PackedSlice(ptr pointer, tagsize int) int {
+	s := *ptr.toUint32Slice()
+	if len(s) == 0 {
+		return 0
+	}
+	return 4*len(s) + SizeVarint(uint64(4*len(s))) + tagsize
+}
+func sizeFixedS32Value(_ pointer, tagsize int) int {
+	return 4 + tagsize
+}
+func sizeFixedS32ValueNoZero(ptr pointer, tagsize int) int {
+	v := *ptr.toInt32()
+	if v == 0 {
+		return 0
+	}
+	return 4 + tagsize
+}
+func sizeFixedS32Ptr(ptr pointer, tagsize int) int {
+	p := ptr.getInt32Ptr()
+	if p == nil {
+		return 0
+	}
+	return 4 + tagsize
+}
+func sizeFixedS32Slice(ptr pointer, tagsize int) int {
+	s := ptr.getInt32Slice()
+	return (4 + tagsize) * len(s)
+}
+func sizeFixedS32PackedSlice(ptr pointer, tagsize int) int {
+	s := ptr.getInt32Slice()
+	if len(s) == 0 {
+		return 0
+	}
+	return 4*len(s) + SizeVarint(uint64(4*len(s))) + tagsize
+}
+func sizeFloat32Value(_ pointer, tagsize int) int {
+	return 4 + tagsize
+}
+func sizeFloat32ValueNoZero(ptr pointer, tagsize int) int {
+	v := math.Float32bits(*ptr.toFloat32())
+	if v == 0 {
+		return 0
+	}
+	return 4 + tagsize
+}
+func sizeFloat32Ptr(ptr pointer, tagsize int) int {
+	p := *ptr.toFloat32Ptr()
+	if p == nil {
+		return 0
+	}
+	return 4 + tagsize
+}
+func sizeFloat32Slice(ptr pointer, tagsize int) int {
+	s := *ptr.toFloat32Slice()
+	return (4 + tagsize) * len(s)
+}
+func sizeFloat32PackedSlice(ptr pointer, tagsize int) int {
+	s := *ptr.toFloat32Slice()
+	if len(s) == 0 {
+		return 0
+	}
+	return 4*len(s) + SizeVarint(uint64(4*len(s))) + tagsize
+}
+func sizeFixed64Value(_ pointer, tagsize int) int {
+	return 8 + tagsize
+}
+func sizeFixed64ValueNoZero(ptr pointer, tagsize int) int {
+	v := *ptr.toUint64()
+	if v == 0 {
+		return 0
+	}
+	return 8 + tagsize
+}
+func sizeFixed64Ptr(ptr pointer, tagsize int) int {
+	p := *ptr.toUint64Ptr()
+	if p == nil {
+		return 0
+	}
+	return 8 + tagsize
+}
+func sizeFixed64Slice(ptr pointer, tagsize int) int {
+	s := *ptr.toUint64Slice()
+	return (8 + tagsize) * len(s)
+}
+func sizeFixed64PackedSlice(ptr pointer, tagsize int) int {
+	s := *ptr.toUint64Slice()
+	if len(s) == 0 {
+		return 0
+	}
+	return 8*len(s) + SizeVarint(uint64(8*len(s))) + tagsize
+}
+func sizeFixedS64Value(_ pointer, tagsize int) int {
+	return 8 + tagsize
+}
+func sizeFixedS64ValueNoZero(ptr pointer, tagsize int) int {
+	v := *ptr.toInt64()
+	if v == 0 {
+		return 0
+	}
+	return 8 + tagsize
+}
+func sizeFixedS64Ptr(ptr pointer, tagsize int) int {
+	p := *ptr.toInt64Ptr()
+	if p == nil {
+		return 0
+	}
+	return 8 + tagsize
+}
+func sizeFixedS64Slice(ptr pointer, tagsize int) int {
+	s := *ptr.toInt64Slice()
+	return (8 + tagsize) * len(s)
+}
+func sizeFixedS64PackedSlice(ptr pointer, tagsize int) int {
+	s := *ptr.toInt64Slice()
+	if len(s) == 0 {
+		return 0
+	}
+	return 8*len(s) + SizeVarint(uint64(8*len(s))) + tagsize
+}
+func sizeFloat64Value(_ pointer, tagsize int) int {
+	return 8 + tagsize
+}
+func sizeFloat64ValueNoZero(ptr pointer, tagsize int) int {
+	v := math.Float64bits(*ptr.toFloat64())
+	if v == 0 {
+		return 0
+	}
+	return 8 + tagsize
+}
+func sizeFloat64Ptr(ptr pointer, tagsize int) int {
+	p := *ptr.toFloat64Ptr()
+	if p == nil {
+		return 0
+	}
+	return 8 + tagsize
+}
+func sizeFloat64Slice(ptr pointer, tagsize int) int {
+	s := *ptr.toFloat64Slice()
+	return (8 + tagsize) * len(s)
+}
+func sizeFloat64PackedSlice(ptr pointer, tagsize int) int {
+	s := *ptr.toFloat64Slice()
+	if len(s) == 0 {
+		return 0
+	}
+	return 8*len(s) + SizeVarint(uint64(8*len(s))) + tagsize
+}
+func sizeVarint32Value(ptr pointer, tagsize int) int {
+	v := *ptr.toUint32()
+	return SizeVarint(uint64(v)) + tagsize
+}
+func sizeVarint32ValueNoZero(ptr pointer, tagsize int) int {
+	v := *ptr.toUint32()
+	if v == 0 {
+		return 0
+	}
+	return SizeVarint(uint64(v)) + tagsize
+}
+func sizeVarint32Ptr(ptr pointer, tagsize int) int {
+	p := *ptr.toUint32Ptr()
+	if p == nil {
+		return 0
+	}
+	return SizeVarint(uint64(*p)) + tagsize
+}
+func sizeVarint32Slice(ptr pointer, tagsize int) int {
+	s := *ptr.toUint32Slice()
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(uint64(v)) + tagsize
+	}
+	return n
+}
+func sizeVarint32PackedSlice(ptr pointer, tagsize int) int {
+	s := *ptr.toUint32Slice()
+	if len(s) == 0 {
+		return 0
+	}
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(uint64(v))
+	}
+	return n + SizeVarint(uint64(n)) + tagsize
+}
+func sizeVarintS32Value(ptr pointer, tagsize int) int {
+	v := *ptr.toInt32()
+	return SizeVarint(uint64(v)) + tagsize
+}
+func sizeVarintS32ValueNoZero(ptr pointer, tagsize int) int {
+	v := *ptr.toInt32()
+	if v == 0 {
+		return 0
+	}
+	return SizeVarint(uint64(v)) + tagsize
+}
+func sizeVarintS32Ptr(ptr pointer, tagsize int) int {
+	p := ptr.getInt32Ptr()
+	if p == nil {
+		return 0
+	}
+	return SizeVarint(uint64(*p)) + tagsize
+}
+func sizeVarintS32Slice(ptr pointer, tagsize int) int {
+	s := ptr.getInt32Slice()
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(uint64(v)) + tagsize
+	}
+	return n
+}
+func sizeVarintS32PackedSlice(ptr pointer, tagsize int) int {
+	s := ptr.getInt32Slice()
+	if len(s) == 0 {
+		return 0
+	}
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(uint64(v))
+	}
+	return n + SizeVarint(uint64(n)) + tagsize
+}
+func sizeVarint64Value(ptr pointer, tagsize int) int {
+	v := *ptr.toUint64()
+	return SizeVarint(v) + tagsize
+}
+func sizeVarint64ValueNoZero(ptr pointer, tagsize int) int {
+	v := *ptr.toUint64()
+	if v == 0 {
+		return 0
+	}
+	return SizeVarint(v) + tagsize
+}
+func sizeVarint64Ptr(ptr pointer, tagsize int) int {
+	p := *ptr.toUint64Ptr()
+	if p == nil {
+		return 0
+	}
+	return SizeVarint(*p) + tagsize
+}
+func sizeVarint64Slice(ptr pointer, tagsize int) int {
+	s := *ptr.toUint64Slice()
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(v) + tagsize
+	}
+	return n
+}
+func sizeVarint64PackedSlice(ptr pointer, tagsize int) int {
+	s := *ptr.toUint64Slice()
+	if len(s) == 0 {
+		return 0
+	}
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(v)
+	}
+	return n + SizeVarint(uint64(n)) + tagsize
+}
+func sizeVarintS64Value(ptr pointer, tagsize int) int {
+	v := *ptr.toInt64()
+	return SizeVarint(uint64(v)) + tagsize
+}
+func sizeVarintS64ValueNoZero(ptr pointer, tagsize int) int {
+	v := *ptr.toInt64()
+	if v == 0 {
+		return 0
+	}
+	return SizeVarint(uint64(v)) + tagsize
+}
+func sizeVarintS64Ptr(ptr pointer, tagsize int) int {
+	p := *ptr.toInt64Ptr()
+	if p == nil {
+		return 0
+	}
+	return SizeVarint(uint64(*p)) + tagsize
+}
+func sizeVarintS64Slice(ptr pointer, tagsize int) int {
+	s := *ptr.toInt64Slice()
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(uint64(v)) + tagsize
+	}
+	return n
+}
+func sizeVarintS64PackedSlice(ptr pointer, tagsize int) int {
+	s := *ptr.toInt64Slice()
+	if len(s) == 0 {
+		return 0
+	}
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(uint64(v))
+	}
+	return n + SizeVarint(uint64(n)) + tagsize
+}
+func sizeZigzag32Value(ptr pointer, tagsize int) int {
+	v := *ptr.toInt32()
+	return SizeVarint(uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + tagsize
+}
+func sizeZigzag32ValueNoZero(ptr pointer, tagsize int) int {
+	v := *ptr.toInt32()
+	if v == 0 {
+		return 0
+	}
+	return SizeVarint(uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + tagsize
+}
+func sizeZigzag32Ptr(ptr pointer, tagsize int) int {
+	p := ptr.getInt32Ptr()
+	if p == nil {
+		return 0
+	}
+	v := *p
+	return SizeVarint(uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + tagsize
+}
+func sizeZigzag32Slice(ptr pointer, tagsize int) int {
+	s := ptr.getInt32Slice()
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + tagsize
+	}
+	return n
+}
+func sizeZigzag32PackedSlice(ptr pointer, tagsize int) int {
+	s := ptr.getInt32Slice()
+	if len(s) == 0 {
+		return 0
+	}
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(uint64((uint32(v) << 1) ^ uint32((int32(v) >> 31))))
+	}
+	return n + SizeVarint(uint64(n)) + tagsize
+}
+func sizeZigzag64Value(ptr pointer, tagsize int) int {
+	v := *ptr.toInt64()
+	return SizeVarint(uint64(v<<1)^uint64((int64(v)>>63))) + tagsize
+}
+func sizeZigzag64ValueNoZero(ptr pointer, tagsize int) int {
+	v := *ptr.toInt64()
+	if v == 0 {
+		return 0
+	}
+	return SizeVarint(uint64(v<<1)^uint64((int64(v)>>63))) + tagsize
+}
+func sizeZigzag64Ptr(ptr pointer, tagsize int) int {
+	p := *ptr.toInt64Ptr()
+	if p == nil {
+		return 0
+	}
+	v := *p
+	return SizeVarint(uint64(v<<1)^uint64((int64(v)>>63))) + tagsize
+}
+func sizeZigzag64Slice(ptr pointer, tagsize int) int {
+	s := *ptr.toInt64Slice()
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(uint64(v<<1)^uint64((int64(v)>>63))) + tagsize
+	}
+	return n
+}
+func sizeZigzag64PackedSlice(ptr pointer, tagsize int) int {
+	s := *ptr.toInt64Slice()
+	if len(s) == 0 {
+		return 0
+	}
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(uint64(v<<1) ^ uint64((int64(v) >> 63)))
+	}
+	return n + SizeVarint(uint64(n)) + tagsize
+}
+func sizeBoolValue(_ pointer, tagsize int) int {
+	return 1 + tagsize
+}
+func sizeBoolValueNoZero(ptr pointer, tagsize int) int {
+	v := *ptr.toBool()
+	if !v {
+		return 0
+	}
+	return 1 + tagsize
+}
+func sizeBoolPtr(ptr pointer, tagsize int) int {
+	p := *ptr.toBoolPtr()
+	if p == nil {
+		return 0
+	}
+	return 1 + tagsize
+}
+func sizeBoolSlice(ptr pointer, tagsize int) int {
+	s := *ptr.toBoolSlice()
+	return (1 + tagsize) * len(s)
+}
+func sizeBoolPackedSlice(ptr pointer, tagsize int) int {
+	s := *ptr.toBoolSlice()
+	if len(s) == 0 {
+		return 0
+	}
+	return len(s) + SizeVarint(uint64(len(s))) + tagsize
+}
+func sizeStringValue(ptr pointer, tagsize int) int {
+	v := *ptr.toString()
+	return len(v) + SizeVarint(uint64(len(v))) + tagsize
+}
+func sizeStringValueNoZero(ptr pointer, tagsize int) int {
+	v := *ptr.toString()
+	if v == "" {
+		return 0
+	}
+	return len(v) + SizeVarint(uint64(len(v))) + tagsize
+}
+func sizeStringPtr(ptr pointer, tagsize int) int {
+	p := *ptr.toStringPtr()
+	if p == nil {
+		return 0
+	}
+	v := *p
+	return len(v) + SizeVarint(uint64(len(v))) + tagsize
+}
+func sizeStringSlice(ptr pointer, tagsize int) int {
+	s := *ptr.toStringSlice()
+	n := 0
+	for _, v := range s {
+		n += len(v) + SizeVarint(uint64(len(v))) + tagsize
+	}
+	return n
+}
+func sizeBytes(ptr pointer, tagsize int) int {
+	v := *ptr.toBytes()
+	if v == nil {
+		return 0
+	}
+	return len(v) + SizeVarint(uint64(len(v))) + tagsize
+}
+func sizeBytes3(ptr pointer, tagsize int) int {
+	v := *ptr.toBytes()
+	if len(v) == 0 {
+		return 0
+	}
+	return len(v) + SizeVarint(uint64(len(v))) + tagsize
+}
+func sizeBytesOneof(ptr pointer, tagsize int) int {
+	v := *ptr.toBytes()
+	return len(v) + SizeVarint(uint64(len(v))) + tagsize
+}
+func sizeBytesSlice(ptr pointer, tagsize int) int {
+	s := *ptr.toBytesSlice()
+	n := 0
+	for _, v := range s {
+		n += len(v) + SizeVarint(uint64(len(v))) + tagsize
+	}
+	return n
+}
+
+// appendFixed32 appends an encoded fixed32 to b.
+func appendFixed32(b []byte, v uint32) []byte {
+	b = append(b,
+		byte(v),
+		byte(v>>8),
+		byte(v>>16),
+		byte(v>>24))
+	return b
+}
+
+// appendFixed64 appends an encoded fixed64 to b.
+func appendFixed64(b []byte, v uint64) []byte {
+	b = append(b,
+		byte(v),
+		byte(v>>8),
+		byte(v>>16),
+		byte(v>>24),
+		byte(v>>32),
+		byte(v>>40),
+		byte(v>>48),
+		byte(v>>56))
+	return b
+}
+
+// appendVarint appends an encoded varint to b.
+func appendVarint(b []byte, v uint64) []byte {
+	// TODO: make 1-byte (maybe 2-byte) case inline-able, once we
+	// have non-leaf inliner.
+	switch {
+	case v < 1<<7:
+		b = append(b, byte(v))
+	case v < 1<<14:
+		b = append(b,
+			byte(v&0x7f|0x80),
+			byte(v>>7))
+	case v < 1<<21:
+		b = append(b,
+			byte(v&0x7f|0x80),
+			byte((v>>7)&0x7f|0x80),
+			byte(v>>14))
+	case v < 1<<28:
+		b = append(b,
+			byte(v&0x7f|0x80),
+			byte((v>>7)&0x7f|0x80),
+			byte((v>>14)&0x7f|0x80),
+			byte(v>>21))
+	case v < 1<<35:
+		b = append(b,
+			byte(v&0x7f|0x80),
+			byte((v>>7)&0x7f|0x80),
+			byte((v>>14)&0x7f|0x80),
+			byte((v>>21)&0x7f|0x80),
+			byte(v>>28))
+	case v < 1<<42:
+		b = append(b,
+			byte(v&0x7f|0x80),
+			byte((v>>7)&0x7f|0x80),
+			byte((v>>14)&0x7f|0x80),
+			byte((v>>21)&0x7f|0x80),
+			byte((v>>28)&0x7f|0x80),
+			byte(v>>35))
+	case v < 1<<49:
+		b = append(b,
+			byte(v&0x7f|0x80),
+			byte((v>>7)&0x7f|0x80),
+			byte((v>>14)&0x7f|0x80),
+			byte((v>>21)&0x7f|0x80),
+			byte((v>>28)&0x7f|0x80),
+			byte((v>>35)&0x7f|0x80),
+			byte(v>>42))
+	case v < 1<<56:
+		b = append(b,
+			byte(v&0x7f|0x80),
+			byte((v>>7)&0x7f|0x80),
+			byte((v>>14)&0x7f|0x80),
+			byte((v>>21)&0x7f|0x80),
+			byte((v>>28)&0x7f|0x80),
+			byte((v>>35)&0x7f|0x80),
+			byte((v>>42)&0x7f|0x80),
+			byte(v>>49))
+	case v < 1<<63:
+		b = append(b,
+			byte(v&0x7f|0x80),
+			byte((v>>7)&0x7f|0x80),
+			byte((v>>14)&0x7f|0x80),
+			byte((v>>21)&0x7f|0x80),
+			byte((v>>28)&0x7f|0x80),
+			byte((v>>35)&0x7f|0x80),
+			byte((v>>42)&0x7f|0x80),
+			byte((v>>49)&0x7f|0x80),
+			byte(v>>56))
+	default:
+		b = append(b,
+			byte(v&0x7f|0x80),
+			byte((v>>7)&0x7f|0x80),
+			byte((v>>14)&0x7f|0x80),
+			byte((v>>21)&0x7f|0x80),
+			byte((v>>28)&0x7f|0x80),
+			byte((v>>35)&0x7f|0x80),
+			byte((v>>42)&0x7f|0x80),
+			byte((v>>49)&0x7f|0x80),
+			byte((v>>56)&0x7f|0x80),
+			1)
+	}
+	return b
+}
+
+func appendFixed32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toUint32()
+	b = appendVarint(b, wiretag)
+	b = appendFixed32(b, v)
+	return b, nil
+}
+func appendFixed32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toUint32()
+	if v == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendFixed32(b, v)
+	return b, nil
+}
+func appendFixed32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	p := *ptr.toUint32Ptr()
+	if p == nil {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendFixed32(b, *p)
+	return b, nil
+}
+func appendFixed32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toUint32Slice()
+	for _, v := range s {
+		b = appendVarint(b, wiretag)
+		b = appendFixed32(b, v)
+	}
+	return b, nil
+}
+func appendFixed32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toUint32Slice()
+	if len(s) == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag&^7|WireBytes)
+	b = appendVarint(b, uint64(4*len(s)))
+	for _, v := range s {
+		b = appendFixed32(b, v)
+	}
+	return b, nil
+}
+func appendFixedS32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toInt32()
+	b = appendVarint(b, wiretag)
+	b = appendFixed32(b, uint32(v))
+	return b, nil
+}
+func appendFixedS32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toInt32()
+	if v == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendFixed32(b, uint32(v))
+	return b, nil
+}
+func appendFixedS32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	p := ptr.getInt32Ptr()
+	if p == nil {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendFixed32(b, uint32(*p))
+	return b, nil
+}
+func appendFixedS32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := ptr.getInt32Slice()
+	for _, v := range s {
+		b = appendVarint(b, wiretag)
+		b = appendFixed32(b, uint32(v))
+	}
+	return b, nil
+}
+func appendFixedS32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := ptr.getInt32Slice()
+	if len(s) == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag&^7|WireBytes)
+	b = appendVarint(b, uint64(4*len(s)))
+	for _, v := range s {
+		b = appendFixed32(b, uint32(v))
+	}
+	return b, nil
+}
+func appendFloat32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := math.Float32bits(*ptr.toFloat32())
+	b = appendVarint(b, wiretag)
+	b = appendFixed32(b, v)
+	return b, nil
+}
+func appendFloat32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := math.Float32bits(*ptr.toFloat32())
+	if v == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendFixed32(b, v)
+	return b, nil
+}
+func appendFloat32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	p := *ptr.toFloat32Ptr()
+	if p == nil {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendFixed32(b, math.Float32bits(*p))
+	return b, nil
+}
+func appendFloat32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toFloat32Slice()
+	for _, v := range s {
+		b = appendVarint(b, wiretag)
+		b = appendFixed32(b, math.Float32bits(v))
+	}
+	return b, nil
+}
+func appendFloat32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toFloat32Slice()
+	if len(s) == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag&^7|WireBytes)
+	b = appendVarint(b, uint64(4*len(s)))
+	for _, v := range s {
+		b = appendFixed32(b, math.Float32bits(v))
+	}
+	return b, nil
+}
+func appendFixed64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toUint64()
+	b = appendVarint(b, wiretag)
+	b = appendFixed64(b, v)
+	return b, nil
+}
+func appendFixed64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toUint64()
+	if v == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendFixed64(b, v)
+	return b, nil
+}
+func appendFixed64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	p := *ptr.toUint64Ptr()
+	if p == nil {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendFixed64(b, *p)
+	return b, nil
+}
+func appendFixed64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toUint64Slice()
+	for _, v := range s {
+		b = appendVarint(b, wiretag)
+		b = appendFixed64(b, v)
+	}
+	return b, nil
+}
+func appendFixed64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toUint64Slice()
+	if len(s) == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag&^7|WireBytes)
+	b = appendVarint(b, uint64(8*len(s)))
+	for _, v := range s {
+		b = appendFixed64(b, v)
+	}
+	return b, nil
+}
+func appendFixedS64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toInt64()
+	b = appendVarint(b, wiretag)
+	b = appendFixed64(b, uint64(v))
+	return b, nil
+}
+func appendFixedS64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toInt64()
+	if v == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendFixed64(b, uint64(v))
+	return b, nil
+}
+func appendFixedS64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	p := *ptr.toInt64Ptr()
+	if p == nil {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendFixed64(b, uint64(*p))
+	return b, nil
+}
+func appendFixedS64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toInt64Slice()
+	for _, v := range s {
+		b = appendVarint(b, wiretag)
+		b = appendFixed64(b, uint64(v))
+	}
+	return b, nil
+}
+func appendFixedS64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toInt64Slice()
+	if len(s) == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag&^7|WireBytes)
+	b = appendVarint(b, uint64(8*len(s)))
+	for _, v := range s {
+		b = appendFixed64(b, uint64(v))
+	}
+	return b, nil
+}
+func appendFloat64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := math.Float64bits(*ptr.toFloat64())
+	b = appendVarint(b, wiretag)
+	b = appendFixed64(b, v)
+	return b, nil
+}
+func appendFloat64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := math.Float64bits(*ptr.toFloat64())
+	if v == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendFixed64(b, v)
+	return b, nil
+}
+func appendFloat64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	p := *ptr.toFloat64Ptr()
+	if p == nil {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendFixed64(b, math.Float64bits(*p))
+	return b, nil
+}
+func appendFloat64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toFloat64Slice()
+	for _, v := range s {
+		b = appendVarint(b, wiretag)
+		b = appendFixed64(b, math.Float64bits(v))
+	}
+	return b, nil
+}
+func appendFloat64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toFloat64Slice()
+	if len(s) == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag&^7|WireBytes)
+	b = appendVarint(b, uint64(8*len(s)))
+	for _, v := range s {
+		b = appendFixed64(b, math.Float64bits(v))
+	}
+	return b, nil
+}
+func appendVarint32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toUint32()
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(v))
+	return b, nil
+}
+func appendVarint32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toUint32()
+	if v == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(v))
+	return b, nil
+}
+func appendVarint32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	p := *ptr.toUint32Ptr()
+	if p == nil {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(*p))
+	return b, nil
+}
+func appendVarint32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toUint32Slice()
+	for _, v := range s {
+		b = appendVarint(b, wiretag)
+		b = appendVarint(b, uint64(v))
+	}
+	return b, nil
+}
+func appendVarint32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toUint32Slice()
+	if len(s) == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag&^7|WireBytes)
+	// compute size
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(uint64(v))
+	}
+	b = appendVarint(b, uint64(n))
+	for _, v := range s {
+		b = appendVarint(b, uint64(v))
+	}
+	return b, nil
+}
+func appendVarintS32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toInt32()
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(v))
+	return b, nil
+}
+func appendVarintS32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toInt32()
+	if v == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(v))
+	return b, nil
+}
+func appendVarintS32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	p := ptr.getInt32Ptr()
+	if p == nil {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(*p))
+	return b, nil
+}
+func appendVarintS32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := ptr.getInt32Slice()
+	for _, v := range s {
+		b = appendVarint(b, wiretag)
+		b = appendVarint(b, uint64(v))
+	}
+	return b, nil
+}
+func appendVarintS32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := ptr.getInt32Slice()
+	if len(s) == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag&^7|WireBytes)
+	// compute size
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(uint64(v))
+	}
+	b = appendVarint(b, uint64(n))
+	for _, v := range s {
+		b = appendVarint(b, uint64(v))
+	}
+	return b, nil
+}
+func appendVarint64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toUint64()
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, v)
+	return b, nil
+}
+func appendVarint64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toUint64()
+	if v == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, v)
+	return b, nil
+}
+func appendVarint64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	p := *ptr.toUint64Ptr()
+	if p == nil {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, *p)
+	return b, nil
+}
+func appendVarint64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toUint64Slice()
+	for _, v := range s {
+		b = appendVarint(b, wiretag)
+		b = appendVarint(b, v)
+	}
+	return b, nil
+}
+func appendVarint64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toUint64Slice()
+	if len(s) == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag&^7|WireBytes)
+	// compute size
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(v)
+	}
+	b = appendVarint(b, uint64(n))
+	for _, v := range s {
+		b = appendVarint(b, v)
+	}
+	return b, nil
+}
+func appendVarintS64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toInt64()
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(v))
+	return b, nil
+}
+func appendVarintS64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toInt64()
+	if v == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(v))
+	return b, nil
+}
+func appendVarintS64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	p := *ptr.toInt64Ptr()
+	if p == nil {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(*p))
+	return b, nil
+}
+func appendVarintS64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toInt64Slice()
+	for _, v := range s {
+		b = appendVarint(b, wiretag)
+		b = appendVarint(b, uint64(v))
+	}
+	return b, nil
+}
+func appendVarintS64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toInt64Slice()
+	if len(s) == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag&^7|WireBytes)
+	// compute size
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(uint64(v))
+	}
+	b = appendVarint(b, uint64(n))
+	for _, v := range s {
+		b = appendVarint(b, uint64(v))
+	}
+	return b, nil
+}
+func appendZigzag32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toInt32()
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31))))
+	return b, nil
+}
+func appendZigzag32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toInt32()
+	if v == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31))))
+	return b, nil
+}
+func appendZigzag32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	p := ptr.getInt32Ptr()
+	if p == nil {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	v := *p
+	b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31))))
+	return b, nil
+}
+func appendZigzag32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := ptr.getInt32Slice()
+	for _, v := range s {
+		b = appendVarint(b, wiretag)
+		b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31))))
+	}
+	return b, nil
+}
+func appendZigzag32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := ptr.getInt32Slice()
+	if len(s) == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag&^7|WireBytes)
+	// compute size
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(uint64((uint32(v) << 1) ^ uint32((int32(v) >> 31))))
+	}
+	b = appendVarint(b, uint64(n))
+	for _, v := range s {
+		b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31))))
+	}
+	return b, nil
+}
+func appendZigzag64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toInt64()
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63)))
+	return b, nil
+}
+func appendZigzag64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toInt64()
+	if v == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63)))
+	return b, nil
+}
+func appendZigzag64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	p := *ptr.toInt64Ptr()
+	if p == nil {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	v := *p
+	b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63)))
+	return b, nil
+}
+func appendZigzag64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toInt64Slice()
+	for _, v := range s {
+		b = appendVarint(b, wiretag)
+		b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63)))
+	}
+	return b, nil
+}
+func appendZigzag64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toInt64Slice()
+	if len(s) == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag&^7|WireBytes)
+	// compute size
+	n := 0
+	for _, v := range s {
+		n += SizeVarint(uint64(v<<1) ^ uint64((int64(v) >> 63)))
+	}
+	b = appendVarint(b, uint64(n))
+	for _, v := range s {
+		b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63)))
+	}
+	return b, nil
+}
+func appendBoolValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toBool()
+	b = appendVarint(b, wiretag)
+	if v {
+		b = append(b, 1)
+	} else {
+		b = append(b, 0)
+	}
+	return b, nil
+}
+func appendBoolValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toBool()
+	if !v {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = append(b, 1)
+	return b, nil
+}
+
+func appendBoolPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	p := *ptr.toBoolPtr()
+	if p == nil {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	if *p {
+		b = append(b, 1)
+	} else {
+		b = append(b, 0)
+	}
+	return b, nil
+}
+func appendBoolSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toBoolSlice()
+	for _, v := range s {
+		b = appendVarint(b, wiretag)
+		if v {
+			b = append(b, 1)
+		} else {
+			b = append(b, 0)
+		}
+	}
+	return b, nil
+}
+func appendBoolPackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toBoolSlice()
+	if len(s) == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag&^7|WireBytes)
+	b = appendVarint(b, uint64(len(s)))
+	for _, v := range s {
+		if v {
+			b = append(b, 1)
+		} else {
+			b = append(b, 0)
+		}
+	}
+	return b, nil
+}
+func appendStringValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toString()
+	if !utf8.ValidString(v) {
+		return nil, errInvalidUTF8
+	}
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(len(v)))
+	b = append(b, v...)
+	return b, nil
+}
+func appendStringValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toString()
+	if v == "" {
+		return b, nil
+	}
+	if !utf8.ValidString(v) {
+		return nil, errInvalidUTF8
+	}
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(len(v)))
+	b = append(b, v...)
+	return b, nil
+}
+func appendStringPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	p := *ptr.toStringPtr()
+	if p == nil {
+		return b, nil
+	}
+	v := *p
+	if !utf8.ValidString(v) {
+		return nil, errInvalidUTF8
+	}
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(len(v)))
+	b = append(b, v...)
+	return b, nil
+}
+func appendStringSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toStringSlice()
+	for _, v := range s {
+		if !utf8.ValidString(v) {
+			return nil, errInvalidUTF8
+		}
+		b = appendVarint(b, wiretag)
+		b = appendVarint(b, uint64(len(v)))
+		b = append(b, v...)
+	}
+	return b, nil
+}
+func appendBytes(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toBytes()
+	if v == nil {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(len(v)))
+	b = append(b, v...)
+	return b, nil
+}
+func appendBytes3(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toBytes()
+	if len(v) == 0 {
+		return b, nil
+	}
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(len(v)))
+	b = append(b, v...)
+	return b, nil
+}
+func appendBytesOneof(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	v := *ptr.toBytes()
+	b = appendVarint(b, wiretag)
+	b = appendVarint(b, uint64(len(v)))
+	b = append(b, v...)
+	return b, nil
+}
+func appendBytesSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) {
+	s := *ptr.toBytesSlice()
+	for _, v := range s {
+		b = appendVarint(b, wiretag)
+		b = appendVarint(b, uint64(len(v)))
+		b = append(b, v...)
+	}
+	return b, nil
+}
+
+// makeGroupMarshaler returns the sizer and marshaler for a group.
+// u is the marshal info of the underlying message.
+func makeGroupMarshaler(u *marshalInfo) (sizer, marshaler) {
+	return func(ptr pointer, tagsize int) int {
+			p := ptr.getPointer()
+			if p.isNil() {
+				return 0
+			}
+			return u.size(p) + 2*tagsize
+		},
+		func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) {
+			p := ptr.getPointer()
+			if p.isNil() {
+				return b, nil
+			}
+			var err error
+			b = appendVarint(b, wiretag) // start group
+			b, err = u.marshal(b, p, deterministic)
+			b = appendVarint(b, wiretag+(WireEndGroup-WireStartGroup)) // end group
+			return b, err
+		}
+}
+
+// makeGroupSliceMarshaler returns the sizer and marshaler for a group slice.
+// u is the marshal info of the underlying message.
+func makeGroupSliceMarshaler(u *marshalInfo) (sizer, marshaler) {
+	return func(ptr pointer, tagsize int) int {
+			s := ptr.getPointerSlice()
+			n := 0
+			for _, v := range s {
+				if v.isNil() {
+					continue
+				}
+				n += u.size(v) + 2*tagsize
+			}
+			return n
+		},
+		func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) {
+			s := ptr.getPointerSlice()
+			var err, errreq error
+			for _, v := range s {
+				if v.isNil() {
+					return b, errRepeatedHasNil
+				}
+				b = appendVarint(b, wiretag) // start group
+				b, err = u.marshal(b, v, deterministic)
+				b = appendVarint(b, wiretag+(WireEndGroup-WireStartGroup)) // end group
+				if err != nil {
+					if _, ok := err.(*RequiredNotSetError); ok {
+						// Required field in submessage is not set.
+						// We record the error but keep going, to give a complete marshaling.
+						if errreq == nil {
+							errreq = err
+						}
+						continue
+					}
+					if err == ErrNil {
+						err = errRepeatedHasNil
+					}
+					return b, err
+				}
+			}
+			return b, errreq
+		}
+}
+
+// makeMessageMarshaler returns the sizer and marshaler for a message field.
+// u is the marshal info of the message.
+func makeMessageMarshaler(u *marshalInfo) (sizer, marshaler) {
+	return func(ptr pointer, tagsize int) int {
+			p := ptr.getPointer()
+			if p.isNil() {
+				return 0
+			}
+			siz := u.size(p)
+			return siz + SizeVarint(uint64(siz)) + tagsize
+		},
+		func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) {
+			p := ptr.getPointer()
+			if p.isNil() {
+				return b, nil
+			}
+			b = appendVarint(b, wiretag)
+			siz := u.cachedsize(p)
+			b = appendVarint(b, uint64(siz))
+			return u.marshal(b, p, deterministic)
+		}
+}
+
+// makeMessageSliceMarshaler returns the sizer and marshaler for a message slice.
+// u is the marshal info of the message.
+func makeMessageSliceMarshaler(u *marshalInfo) (sizer, marshaler) {
+	return func(ptr pointer, tagsize int) int {
+			s := ptr.getPointerSlice()
+			n := 0
+			for _, v := range s {
+				if v.isNil() {
+					continue
+				}
+				siz := u.size(v)
+				n += siz + SizeVarint(uint64(siz)) + tagsize
+			}
+			return n
+		},
+		func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) {
+			s := ptr.getPointerSlice()
+			var err, errreq error
+			for _, v := range s {
+				if v.isNil() {
+					return b, errRepeatedHasNil
+				}
+				b = appendVarint(b, wiretag)
+				siz := u.cachedsize(v)
+				b = appendVarint(b, uint64(siz))
+				b, err = u.marshal(b, v, deterministic)
+
+				if err != nil {
+					if _, ok := err.(*RequiredNotSetError); ok {
+						// Required field in submessage is not set.
+						// We record the error but keep going, to give a complete marshaling.
+						if errreq == nil {
+							errreq = err
+						}
+						continue
+					}
+					if err == ErrNil {
+						err = errRepeatedHasNil
+					}
+					return b, err
+				}
+			}
+			return b, errreq
+		}
+}
+
+// makeMapMarshaler returns the sizer and marshaler for a map field.
+// f is the pointer to the reflect data structure of the field.
+func makeMapMarshaler(f *reflect.StructField) (sizer, marshaler) {
+	// figure out key and value type
+	t := f.Type
+	keyType := t.Key()
+	valType := t.Elem()
+	keyTags := strings.Split(f.Tag.Get("protobuf_key"), ",")
+	valTags := strings.Split(f.Tag.Get("protobuf_val"), ",")
+	keySizer, keyMarshaler := typeMarshaler(keyType, keyTags, false, false) // don't omit zero value in map
+	valSizer, valMarshaler := typeMarshaler(valType, valTags, false, false) // don't omit zero value in map
+	keyWireTag := 1<<3 | wiretype(keyTags[0])
+	valWireTag := 2<<3 | wiretype(valTags[0])
+
+	// We create an interface to get the addresses of the map key and value.
+	// If value is pointer-typed, the interface is a direct interface, the
+	// idata itself is the value. Otherwise, the idata is the pointer to the
+	// value.
+	// Key cannot be pointer-typed.
+	valIsPtr := valType.Kind() == reflect.Ptr
+	return func(ptr pointer, tagsize int) int {
+			m := ptr.asPointerTo(t).Elem() // the map
+			n := 0
+			for _, k := range m.MapKeys() {
+				ki := k.Interface()
+				vi := m.MapIndex(k).Interface()
+				kaddr := toAddrPointer(&ki, false)             // pointer to key
+				vaddr := toAddrPointer(&vi, valIsPtr)          // pointer to value
+				siz := keySizer(kaddr, 1) + valSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1)
+				n += siz + SizeVarint(uint64(siz)) + tagsize
+			}
+			return n
+		},
+		func(b []byte, ptr pointer, tag uint64, deterministic bool) ([]byte, error) {
+			m := ptr.asPointerTo(t).Elem() // the map
+			var err error
+			keys := m.MapKeys()
+			if len(keys) > 1 && deterministic {
+				sort.Sort(mapKeys(keys))
+			}
+			for _, k := range keys {
+				ki := k.Interface()
+				vi := m.MapIndex(k).Interface()
+				kaddr := toAddrPointer(&ki, false)    // pointer to key
+				vaddr := toAddrPointer(&vi, valIsPtr) // pointer to value
+				b = appendVarint(b, tag)
+				siz := keySizer(kaddr, 1) + valSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1)
+				b = appendVarint(b, uint64(siz))
+				b, err = keyMarshaler(b, kaddr, keyWireTag, deterministic)
+				if err != nil {
+					return b, err
+				}
+				b, err = valMarshaler(b, vaddr, valWireTag, deterministic)
+				if err != nil && err != ErrNil { // allow nil value in map
+					return b, err
+				}
+			}
+			return b, nil
+		}
+}
+
+// makeOneOfMarshaler returns the sizer and marshaler for a oneof field.
+// fi is the marshal info of the field.
+// f is the pointer to the reflect data structure of the field.
+func makeOneOfMarshaler(fi *marshalFieldInfo, f *reflect.StructField) (sizer, marshaler) {
+	// Oneof field is an interface. We need to get the actual data type on the fly.
+	t := f.Type
+	return func(ptr pointer, _ int) int {
+			p := ptr.getInterfacePointer()
+			if p.isNil() {
+				return 0
+			}
+			v := ptr.asPointerTo(t).Elem().Elem().Elem() // *interface -> interface -> *struct -> struct
+			telem := v.Type()
+			e := fi.oneofElems[telem]
+			return e.sizer(p, e.tagsize)
+		},
+		func(b []byte, ptr pointer, _ uint64, deterministic bool) ([]byte, error) {
+			p := ptr.getInterfacePointer()
+			if p.isNil() {
+				return b, nil
+			}
+			v := ptr.asPointerTo(t).Elem().Elem().Elem() // *interface -> interface -> *struct -> struct
+			telem := v.Type()
+			if telem.Field(0).Type.Kind() == reflect.Ptr && p.getPointer().isNil() {
+				return b, errOneofHasNil
+			}
+			e := fi.oneofElems[telem]
+			return e.marshaler(b, p, e.wiretag, deterministic)
+		}
+}
+
+// sizeExtensions computes the size of encoded data for a XXX_InternalExtensions field.
+func (u *marshalInfo) sizeExtensions(ext *XXX_InternalExtensions) int {
+	m, mu := ext.extensionsRead()
+	if m == nil {
+		return 0
+	}
+	mu.Lock()
+
+	n := 0
+	for _, e := range m {
+		if e.value == nil || e.desc == nil {
+			// Extension is only in its encoded form.
+			n += len(e.enc)
+			continue
+		}
+
+		// We don't skip extensions that have an encoded form set,
+		// because the extension value may have been mutated after
+		// the last time this function was called.
+		ei := u.getExtElemInfo(e.desc)
+		v := e.value
+		p := toAddrPointer(&v, ei.isptr)
+		n += ei.sizer(p, ei.tagsize)
+	}
+	mu.Unlock()
+	return n
+}
+
+// appendExtensions marshals a XXX_InternalExtensions field to the end of byte slice b.
+func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, deterministic bool) ([]byte, error) {
+	m, mu := ext.extensionsRead()
+	if m == nil {
+		return b, nil
+	}
+	mu.Lock()
+	defer mu.Unlock()
+
+	var err error
+
+	// Fast-path for common cases: zero or one extensions.
+	// Don't bother sorting the keys.
+	if len(m) <= 1 {
+		for _, e := range m {
+			if e.value == nil || e.desc == nil {
+				// Extension is only in its encoded form.
+				b = append(b, e.enc...)
+				continue
+			}
+
+			// We don't skip extensions that have an encoded form set,
+			// because the extension value may have been mutated after
+			// the last time this function was called.
+
+			ei := u.getExtElemInfo(e.desc)
+			v := e.value
+			p := toAddrPointer(&v, ei.isptr)
+			b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
+			if err != nil {
+				return b, err
+			}
+		}
+		return b, nil
+	}
+
+	// Sort the keys to provide a deterministic encoding.
+	// Not sure this is required, but the old code does it.
+	keys := make([]int, 0, len(m))
+	for k := range m {
+		keys = append(keys, int(k))
+	}
+	sort.Ints(keys)
+
+	for _, k := range keys {
+		e := m[int32(k)]
+		if e.value == nil || e.desc == nil {
+			// Extension is only in its encoded form.
+			b = append(b, e.enc...)
+			continue
+		}
+
+		// We don't skip extensions that have an encoded form set,
+		// because the extension value may have been mutated after
+		// the last time this function was called.
+
+		ei := u.getExtElemInfo(e.desc)
+		v := e.value
+		p := toAddrPointer(&v, ei.isptr)
+		b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
+		if err != nil {
+			return b, err
+		}
+	}
+	return b, nil
+}
+
+// message set format is:
+//   message MessageSet {
+//     repeated group Item = 1 {
+//       required int32 type_id = 2;
+//       required string message = 3;
+//     };
+//   }
+
+// sizeMessageSet computes the size of encoded data for a XXX_InternalExtensions field
+// in message set format (above).
+func (u *marshalInfo) sizeMessageSet(ext *XXX_InternalExtensions) int {
+	m, mu := ext.extensionsRead()
+	if m == nil {
+		return 0
+	}
+	mu.Lock()
+
+	n := 0
+	for id, e := range m {
+		n += 2                          // start group, end group. tag = 1 (size=1)
+		n += SizeVarint(uint64(id)) + 1 // type_id, tag = 2 (size=1)
+
+		if e.value == nil || e.desc == nil {
+			// Extension is only in its encoded form.
+			msgWithLen := skipVarint(e.enc) // skip old tag, but leave the length varint
+			siz := len(msgWithLen)
+			n += siz + 1 // message, tag = 3 (size=1)
+			continue
+		}
+
+		// We don't skip extensions that have an encoded form set,
+		// because the extension value may have been mutated after
+		// the last time this function was called.
+
+		ei := u.getExtElemInfo(e.desc)
+		v := e.value
+		p := toAddrPointer(&v, ei.isptr)
+		n += ei.sizer(p, 1) // message, tag = 3 (size=1)
+	}
+	mu.Unlock()
+	return n
+}
+
+// appendMessageSet marshals a XXX_InternalExtensions field in message set format (above)
+// to the end of byte slice b.
+func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, deterministic bool) ([]byte, error) {
+	m, mu := ext.extensionsRead()
+	if m == nil {
+		return b, nil
+	}
+	mu.Lock()
+	defer mu.Unlock()
+
+	var err error
+
+	// Fast-path for common cases: zero or one extensions.
+	// Don't bother sorting the keys.
+	if len(m) <= 1 {
+		for id, e := range m {
+			b = append(b, 1<<3|WireStartGroup)
+			b = append(b, 2<<3|WireVarint)
+			b = appendVarint(b, uint64(id))
+
+			if e.value == nil || e.desc == nil {
+				// Extension is only in its encoded form.
+				msgWithLen := skipVarint(e.enc) // skip old tag, but leave the length varint
+				b = append(b, 3<<3|WireBytes)
+				b = append(b, msgWithLen...)
+				b = append(b, 1<<3|WireEndGroup)
+				continue
+			}
+
+			// We don't skip extensions that have an encoded form set,
+			// because the extension value may have been mutated after
+			// the last time this function was called.
+
+			ei := u.getExtElemInfo(e.desc)
+			v := e.value
+			p := toAddrPointer(&v, ei.isptr)
+			b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic)
+			if err != nil {
+				return b, err
+			}
+			b = append(b, 1<<3|WireEndGroup)
+		}
+		return b, nil
+	}
+
+	// Sort the keys to provide a deterministic encoding.
+	keys := make([]int, 0, len(m))
+	for k := range m {
+		keys = append(keys, int(k))
+	}
+	sort.Ints(keys)
+
+	for _, id := range keys {
+		e := m[int32(id)]
+		b = append(b, 1<<3|WireStartGroup)
+		b = append(b, 2<<3|WireVarint)
+		b = appendVarint(b, uint64(id))
+
+		if e.value == nil || e.desc == nil {
+			// Extension is only in its encoded form.
+			msgWithLen := skipVarint(e.enc) // skip old tag, but leave the length varint
+			b = append(b, 3<<3|WireBytes)
+			b = append(b, msgWithLen...)
+			b = append(b, 1<<3|WireEndGroup)
+			continue
+		}
+
+		// We don't skip extensions that have an encoded form set,
+		// because the extension value may have been mutated after
+		// the last time this function was called.
+
+		ei := u.getExtElemInfo(e.desc)
+		v := e.value
+		p := toAddrPointer(&v, ei.isptr)
+		b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic)
+		b = append(b, 1<<3|WireEndGroup)
+		if err != nil {
+			return b, err
+		}
+	}
+	return b, nil
+}
+
+// sizeV1Extensions computes the size of encoded data for a V1-API extension field.
+func (u *marshalInfo) sizeV1Extensions(m map[int32]Extension) int {
+	if m == nil {
+		return 0
+	}
+
+	n := 0
+	for _, e := range m {
+		if e.value == nil || e.desc == nil {
+			// Extension is only in its encoded form.
+			n += len(e.enc)
+			continue
+		}
+
+		// We don't skip extensions that have an encoded form set,
+		// because the extension value may have been mutated after
+		// the last time this function was called.
+
+		ei := u.getExtElemInfo(e.desc)
+		v := e.value
+		p := toAddrPointer(&v, ei.isptr)
+		n += ei.sizer(p, ei.tagsize)
+	}
+	return n
+}
+
+// appendV1Extensions marshals a V1-API extension field to the end of byte slice b.
+func (u *marshalInfo) appendV1Extensions(b []byte, m map[int32]Extension, deterministic bool) ([]byte, error) {
+	if m == nil {
+		return b, nil
+	}
+
+	// Sort the keys to provide a deterministic encoding.
+	keys := make([]int, 0, len(m))
+	for k := range m {
+		keys = append(keys, int(k))
+	}
+	sort.Ints(keys)
+
+	var err error
+	for _, k := range keys {
+		e := m[int32(k)]
+		if e.value == nil || e.desc == nil {
+			// Extension is only in its encoded form.
+			b = append(b, e.enc...)
+			continue
+		}
+
+		// We don't skip extensions that have an encoded form set,
+		// because the extension value may have been mutated after
+		// the last time this function was called.
+
+		ei := u.getExtElemInfo(e.desc)
+		v := e.value
+		p := toAddrPointer(&v, ei.isptr)
+		b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
+		if err != nil {
+			return b, err
+		}
+	}
+	return b, nil
+}
+
+// newMarshaler is the interface representing objects that can marshal themselves.
+//
+// This exists to support protoc-gen-go generated messages.
+// The proto package will stop type-asserting to this interface in the future.
+//
+// DO NOT DEPEND ON THIS.
+type newMarshaler interface {
+	XXX_Size() int
+	XXX_Marshal(b []byte, deterministic bool) ([]byte, error)
+}
+
+// Size returns the encoded size of a protocol buffer message.
+// This is the main entry point.
+func Size(pb Message) int {
+	if m, ok := pb.(newMarshaler); ok {
+		return m.XXX_Size()
+	}
+	if m, ok := pb.(Marshaler); ok {
+		// If the message can marshal itself, let it do it, for compatibility.
+		// NOTE: This is not efficient.
+		b, _ := m.Marshal()
+		return len(b)
+	}
+	// in case somehow we didn't generate the wrapper
+	if pb == nil {
+		return 0
+	}
+	var info InternalMessageInfo
+	return info.Size(pb)
+}
+
+// Marshal takes a protocol buffer message
+// and encodes it into the wire format, returning the data.
+// This is the main entry point.
+func Marshal(pb Message) ([]byte, error) {
+	if m, ok := pb.(newMarshaler); ok {
+		siz := m.XXX_Size()
+		b := make([]byte, 0, siz)
+		return m.XXX_Marshal(b, false)
+	}
+	if m, ok := pb.(Marshaler); ok {
+		// If the message can marshal itself, let it do it, for compatibility.
+		// NOTE: This is not efficient.
+		return m.Marshal()
+	}
+	// in case somehow we didn't generate the wrapper
+	if pb == nil {
+		return nil, ErrNil
+	}
+	var info InternalMessageInfo
+	siz := info.Size(pb)
+	b := make([]byte, 0, siz)
+	return info.Marshal(b, pb, false)
+}
+
+// Marshal takes a protocol buffer message
+// and encodes it into the wire format, writing the result to the
+// Buffer.
+// This is an alternative entry point. It is not necessary to use
+// a Buffer for most applications.
+func (p *Buffer) Marshal(pb Message) error {
+	var err error
+	if m, ok := pb.(newMarshaler); ok {
+		siz := m.XXX_Size()
+		p.grow(siz) // make sure buf has enough capacity
+		p.buf, err = m.XXX_Marshal(p.buf, p.deterministic)
+		return err
+	}
+	if m, ok := pb.(Marshaler); ok {
+		// If the message can marshal itself, let it do it, for compatibility.
+		// NOTE: This is not efficient.
+		b, err := m.Marshal()
+		p.buf = append(p.buf, b...)
+		return err
+	}
+	// in case somehow we didn't generate the wrapper
+	if pb == nil {
+		return ErrNil
+	}
+	var info InternalMessageInfo
+	siz := info.Size(pb)
+	p.grow(siz) // make sure buf has enough capacity
+	p.buf, err = info.Marshal(p.buf, pb, p.deterministic)
+	return err
+}
+
+// grow grows the buffer's capacity, if necessary, to guarantee space for
+// another n bytes. After grow(n), at least n bytes can be written to the
+// buffer without another allocation.
+func (p *Buffer) grow(n int) {
+	need := len(p.buf) + n
+	if need <= cap(p.buf) {
+		return
+	}
+	newCap := len(p.buf) * 2
+	if newCap < need {
+		newCap = need
+	}
+	p.buf = append(make([]byte, 0, newCap), p.buf...)
+}
diff --git a/vendor/github.com/golang/protobuf/proto/table_merge.go b/vendor/github.com/golang/protobuf/proto/table_merge.go
new file mode 100644
index 0000000..5525def
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/table_merge.go
@@ -0,0 +1,654 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2016 The Go Authors.  All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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.
+
+package proto
+
+import (
+	"fmt"
+	"reflect"
+	"strings"
+	"sync"
+	"sync/atomic"
+)
+
+// Merge merges the src message into dst.
+// This assumes that dst and src of the same type and are non-nil.
+func (a *InternalMessageInfo) Merge(dst, src Message) {
+	mi := atomicLoadMergeInfo(&a.merge)
+	if mi == nil {
+		mi = getMergeInfo(reflect.TypeOf(dst).Elem())
+		atomicStoreMergeInfo(&a.merge, mi)
+	}
+	mi.merge(toPointer(&dst), toPointer(&src))
+}
+
+type mergeInfo struct {
+	typ reflect.Type
+
+	initialized int32 // 0: only typ is valid, 1: everything is valid
+	lock        sync.Mutex
+
+	fields       []mergeFieldInfo
+	unrecognized field // Offset of XXX_unrecognized
+}
+
+type mergeFieldInfo struct {
+	field field // Offset of field, guaranteed to be valid
+
+	// isPointer reports whether the value in the field is a pointer.
+	// This is true for the following situations:
+	//	* Pointer to struct
+	//	* Pointer to basic type (proto2 only)
+	//	* Slice (first value in slice header is a pointer)
+	//	* String (first value in string header is a pointer)
+	isPointer bool
+
+	// basicWidth reports the width of the field assuming that it is directly
+	// embedded in the struct (as is the case for basic types in proto3).
+	// The possible values are:
+	// 	0: invalid
+	//	1: bool
+	//	4: int32, uint32, float32
+	//	8: int64, uint64, float64
+	basicWidth int
+
+	// Where dst and src are pointers to the types being merged.
+	merge func(dst, src pointer)
+}
+
+var (
+	mergeInfoMap  = map[reflect.Type]*mergeInfo{}
+	mergeInfoLock sync.Mutex
+)
+
+func getMergeInfo(t reflect.Type) *mergeInfo {
+	mergeInfoLock.Lock()
+	defer mergeInfoLock.Unlock()
+	mi := mergeInfoMap[t]
+	if mi == nil {
+		mi = &mergeInfo{typ: t}
+		mergeInfoMap[t] = mi
+	}
+	return mi
+}
+
+// merge merges src into dst assuming they are both of type *mi.typ.
+func (mi *mergeInfo) merge(dst, src pointer) {
+	if dst.isNil() {
+		panic("proto: nil destination")
+	}
+	if src.isNil() {
+		return // Nothing to do.
+	}
+
+	if atomic.LoadInt32(&mi.initialized) == 0 {
+		mi.computeMergeInfo()
+	}
+
+	for _, fi := range mi.fields {
+		sfp := src.offset(fi.field)
+
+		// As an optimization, we can avoid the merge function call cost
+		// if we know for sure that the source will have no effect
+		// by checking if it is the zero value.
+		if unsafeAllowed {
+			if fi.isPointer && sfp.getPointer().isNil() { // Could be slice or string
+				continue
+			}
+			if fi.basicWidth > 0 {
+				switch {
+				case fi.basicWidth == 1 && !*sfp.toBool():
+					continue
+				case fi.basicWidth == 4 && *sfp.toUint32() == 0:
+					continue
+				case fi.basicWidth == 8 && *sfp.toUint64() == 0:
+					continue
+				}
+			}
+		}
+
+		dfp := dst.offset(fi.field)
+		fi.merge(dfp, sfp)
+	}
+
+	// TODO: Make this faster?
+	out := dst.asPointerTo(mi.typ).Elem()
+	in := src.asPointerTo(mi.typ).Elem()
+	if emIn, err := extendable(in.Addr().Interface()); err == nil {
+		emOut, _ := extendable(out.Addr().Interface())
+		mIn, muIn := emIn.extensionsRead()
+		if mIn != nil {
+			mOut := emOut.extensionsWrite()
+			muIn.Lock()
+			mergeExtension(mOut, mIn)
+			muIn.Unlock()
+		}
+	}
+
+	if mi.unrecognized.IsValid() {
+		if b := *src.offset(mi.unrecognized).toBytes(); len(b) > 0 {
+			*dst.offset(mi.unrecognized).toBytes() = append([]byte(nil), b...)
+		}
+	}
+}
+
+func (mi *mergeInfo) computeMergeInfo() {
+	mi.lock.Lock()
+	defer mi.lock.Unlock()
+	if mi.initialized != 0 {
+		return
+	}
+	t := mi.typ
+	n := t.NumField()
+
+	props := GetProperties(t)
+	for i := 0; i < n; i++ {
+		f := t.Field(i)
+		if strings.HasPrefix(f.Name, "XXX_") {
+			continue
+		}
+
+		mfi := mergeFieldInfo{field: toField(&f)}
+		tf := f.Type
+
+		// As an optimization, we can avoid the merge function call cost
+		// if we know for sure that the source will have no effect
+		// by checking if it is the zero value.
+		if unsafeAllowed {
+			switch tf.Kind() {
+			case reflect.Ptr, reflect.Slice, reflect.String:
+				// As a special case, we assume slices and strings are pointers
+				// since we know that the first field in the SliceSlice or
+				// StringHeader is a data pointer.
+				mfi.isPointer = true
+			case reflect.Bool:
+				mfi.basicWidth = 1
+			case reflect.Int32, reflect.Uint32, reflect.Float32:
+				mfi.basicWidth = 4
+			case reflect.Int64, reflect.Uint64, reflect.Float64:
+				mfi.basicWidth = 8
+			}
+		}
+
+		// Unwrap tf to get at its most basic type.
+		var isPointer, isSlice bool
+		if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 {
+			isSlice = true
+			tf = tf.Elem()
+		}
+		if tf.Kind() == reflect.Ptr {
+			isPointer = true
+			tf = tf.Elem()
+		}
+		if isPointer && isSlice && tf.Kind() != reflect.Struct {
+			panic("both pointer and slice for basic type in " + tf.Name())
+		}
+
+		switch tf.Kind() {
+		case reflect.Int32:
+			switch {
+			case isSlice: // E.g., []int32
+				mfi.merge = func(dst, src pointer) {
+					// NOTE: toInt32Slice is not defined (see pointer_reflect.go).
+					/*
+						sfsp := src.toInt32Slice()
+						if *sfsp != nil {
+							dfsp := dst.toInt32Slice()
+							*dfsp = append(*dfsp, *sfsp...)
+							if *dfsp == nil {
+								*dfsp = []int64{}
+							}
+						}
+					*/
+					sfs := src.getInt32Slice()
+					if sfs != nil {
+						dfs := dst.getInt32Slice()
+						dfs = append(dfs, sfs...)
+						if dfs == nil {
+							dfs = []int32{}
+						}
+						dst.setInt32Slice(dfs)
+					}
+				}
+			case isPointer: // E.g., *int32
+				mfi.merge = func(dst, src pointer) {
+					// NOTE: toInt32Ptr is not defined (see pointer_reflect.go).
+					/*
+						sfpp := src.toInt32Ptr()
+						if *sfpp != nil {
+							dfpp := dst.toInt32Ptr()
+							if *dfpp == nil {
+								*dfpp = Int32(**sfpp)
+							} else {
+								**dfpp = **sfpp
+							}
+						}
+					*/
+					sfp := src.getInt32Ptr()
+					if sfp != nil {
+						dfp := dst.getInt32Ptr()
+						if dfp == nil {
+							dst.setInt32Ptr(*sfp)
+						} else {
+							*dfp = *sfp
+						}
+					}
+				}
+			default: // E.g., int32
+				mfi.merge = func(dst, src pointer) {
+					if v := *src.toInt32(); v != 0 {
+						*dst.toInt32() = v
+					}
+				}
+			}
+		case reflect.Int64:
+			switch {
+			case isSlice: // E.g., []int64
+				mfi.merge = func(dst, src pointer) {
+					sfsp := src.toInt64Slice()
+					if *sfsp != nil {
+						dfsp := dst.toInt64Slice()
+						*dfsp = append(*dfsp, *sfsp...)
+						if *dfsp == nil {
+							*dfsp = []int64{}
+						}
+					}
+				}
+			case isPointer: // E.g., *int64
+				mfi.merge = func(dst, src pointer) {
+					sfpp := src.toInt64Ptr()
+					if *sfpp != nil {
+						dfpp := dst.toInt64Ptr()
+						if *dfpp == nil {
+							*dfpp = Int64(**sfpp)
+						} else {
+							**dfpp = **sfpp
+						}
+					}
+				}
+			default: // E.g., int64
+				mfi.merge = func(dst, src pointer) {
+					if v := *src.toInt64(); v != 0 {
+						*dst.toInt64() = v
+					}
+				}
+			}
+		case reflect.Uint32:
+			switch {
+			case isSlice: // E.g., []uint32
+				mfi.merge = func(dst, src pointer) {
+					sfsp := src.toUint32Slice()
+					if *sfsp != nil {
+						dfsp := dst.toUint32Slice()
+						*dfsp = append(*dfsp, *sfsp...)
+						if *dfsp == nil {
+							*dfsp = []uint32{}
+						}
+					}
+				}
+			case isPointer: // E.g., *uint32
+				mfi.merge = func(dst, src pointer) {
+					sfpp := src.toUint32Ptr()
+					if *sfpp != nil {
+						dfpp := dst.toUint32Ptr()
+						if *dfpp == nil {
+							*dfpp = Uint32(**sfpp)
+						} else {
+							**dfpp = **sfpp
+						}
+					}
+				}
+			default: // E.g., uint32
+				mfi.merge = func(dst, src pointer) {
+					if v := *src.toUint32(); v != 0 {
+						*dst.toUint32() = v
+					}
+				}
+			}
+		case reflect.Uint64:
+			switch {
+			case isSlice: // E.g., []uint64
+				mfi.merge = func(dst, src pointer) {
+					sfsp := src.toUint64Slice()
+					if *sfsp != nil {
+						dfsp := dst.toUint64Slice()
+						*dfsp = append(*dfsp, *sfsp...)
+						if *dfsp == nil {
+							*dfsp = []uint64{}
+						}
+					}
+				}
+			case isPointer: // E.g., *uint64
+				mfi.merge = func(dst, src pointer) {
+					sfpp := src.toUint64Ptr()
+					if *sfpp != nil {
+						dfpp := dst.toUint64Ptr()
+						if *dfpp == nil {
+							*dfpp = Uint64(**sfpp)
+						} else {
+							**dfpp = **sfpp
+						}
+					}
+				}
+			default: // E.g., uint64
+				mfi.merge = func(dst, src pointer) {
+					if v := *src.toUint64(); v != 0 {
+						*dst.toUint64() = v
+					}
+				}
+			}
+		case reflect.Float32:
+			switch {
+			case isSlice: // E.g., []float32
+				mfi.merge = func(dst, src pointer) {
+					sfsp := src.toFloat32Slice()
+					if *sfsp != nil {
+						dfsp := dst.toFloat32Slice()
+						*dfsp = append(*dfsp, *sfsp...)
+						if *dfsp == nil {
+							*dfsp = []float32{}
+						}
+					}
+				}
+			case isPointer: // E.g., *float32
+				mfi.merge = func(dst, src pointer) {
+					sfpp := src.toFloat32Ptr()
+					if *sfpp != nil {
+						dfpp := dst.toFloat32Ptr()
+						if *dfpp == nil {
+							*dfpp = Float32(**sfpp)
+						} else {
+							**dfpp = **sfpp
+						}
+					}
+				}
+			default: // E.g., float32
+				mfi.merge = func(dst, src pointer) {
+					if v := *src.toFloat32(); v != 0 {
+						*dst.toFloat32() = v
+					}
+				}
+			}
+		case reflect.Float64:
+			switch {
+			case isSlice: // E.g., []float64
+				mfi.merge = func(dst, src pointer) {
+					sfsp := src.toFloat64Slice()
+					if *sfsp != nil {
+						dfsp := dst.toFloat64Slice()
+						*dfsp = append(*dfsp, *sfsp...)
+						if *dfsp == nil {
+							*dfsp = []float64{}
+						}
+					}
+				}
+			case isPointer: // E.g., *float64
+				mfi.merge = func(dst, src pointer) {
+					sfpp := src.toFloat64Ptr()
+					if *sfpp != nil {
+						dfpp := dst.toFloat64Ptr()
+						if *dfpp == nil {
+							*dfpp = Float64(**sfpp)
+						} else {
+							**dfpp = **sfpp
+						}
+					}
+				}
+			default: // E.g., float64
+				mfi.merge = func(dst, src pointer) {
+					if v := *src.toFloat64(); v != 0 {
+						*dst.toFloat64() = v
+					}
+				}
+			}
+		case reflect.Bool:
+			switch {
+			case isSlice: // E.g., []bool
+				mfi.merge = func(dst, src pointer) {
+					sfsp := src.toBoolSlice()
+					if *sfsp != nil {
+						dfsp := dst.toBoolSlice()
+						*dfsp = append(*dfsp, *sfsp...)
+						if *dfsp == nil {
+							*dfsp = []bool{}
+						}
+					}
+				}
+			case isPointer: // E.g., *bool
+				mfi.merge = func(dst, src pointer) {
+					sfpp := src.toBoolPtr()
+					if *sfpp != nil {
+						dfpp := dst.toBoolPtr()
+						if *dfpp == nil {
+							*dfpp = Bool(**sfpp)
+						} else {
+							**dfpp = **sfpp
+						}
+					}
+				}
+			default: // E.g., bool
+				mfi.merge = func(dst, src pointer) {
+					if v := *src.toBool(); v {
+						*dst.toBool() = v
+					}
+				}
+			}
+		case reflect.String:
+			switch {
+			case isSlice: // E.g., []string
+				mfi.merge = func(dst, src pointer) {
+					sfsp := src.toStringSlice()
+					if *sfsp != nil {
+						dfsp := dst.toStringSlice()
+						*dfsp = append(*dfsp, *sfsp...)
+						if *dfsp == nil {
+							*dfsp = []string{}
+						}
+					}
+				}
+			case isPointer: // E.g., *string
+				mfi.merge = func(dst, src pointer) {
+					sfpp := src.toStringPtr()
+					if *sfpp != nil {
+						dfpp := dst.toStringPtr()
+						if *dfpp == nil {
+							*dfpp = String(**sfpp)
+						} else {
+							**dfpp = **sfpp
+						}
+					}
+				}
+			default: // E.g., string
+				mfi.merge = func(dst, src pointer) {
+					if v := *src.toString(); v != "" {
+						*dst.toString() = v
+					}
+				}
+			}
+		case reflect.Slice:
+			isProto3 := props.Prop[i].proto3
+			switch {
+			case isPointer:
+				panic("bad pointer in byte slice case in " + tf.Name())
+			case tf.Elem().Kind() != reflect.Uint8:
+				panic("bad element kind in byte slice case in " + tf.Name())
+			case isSlice: // E.g., [][]byte
+				mfi.merge = func(dst, src pointer) {
+					sbsp := src.toBytesSlice()
+					if *sbsp != nil {
+						dbsp := dst.toBytesSlice()
+						for _, sb := range *sbsp {
+							if sb == nil {
+								*dbsp = append(*dbsp, nil)
+							} else {
+								*dbsp = append(*dbsp, append([]byte{}, sb...))
+							}
+						}
+						if *dbsp == nil {
+							*dbsp = [][]byte{}
+						}
+					}
+				}
+			default: // E.g., []byte
+				mfi.merge = func(dst, src pointer) {
+					sbp := src.toBytes()
+					if *sbp != nil {
+						dbp := dst.toBytes()
+						if !isProto3 || len(*sbp) > 0 {
+							*dbp = append([]byte{}, *sbp...)
+						}
+					}
+				}
+			}
+		case reflect.Struct:
+			switch {
+			case !isPointer:
+				panic(fmt.Sprintf("message field %s without pointer", tf))
+			case isSlice: // E.g., []*pb.T
+				mi := getMergeInfo(tf)
+				mfi.merge = func(dst, src pointer) {
+					sps := src.getPointerSlice()
+					if sps != nil {
+						dps := dst.getPointerSlice()
+						for _, sp := range sps {
+							var dp pointer
+							if !sp.isNil() {
+								dp = valToPointer(reflect.New(tf))
+								mi.merge(dp, sp)
+							}
+							dps = append(dps, dp)
+						}
+						if dps == nil {
+							dps = []pointer{}
+						}
+						dst.setPointerSlice(dps)
+					}
+				}
+			default: // E.g., *pb.T
+				mi := getMergeInfo(tf)
+				mfi.merge = func(dst, src pointer) {
+					sp := src.getPointer()
+					if !sp.isNil() {
+						dp := dst.getPointer()
+						if dp.isNil() {
+							dp = valToPointer(reflect.New(tf))
+							dst.setPointer(dp)
+						}
+						mi.merge(dp, sp)
+					}
+				}
+			}
+		case reflect.Map:
+			switch {
+			case isPointer || isSlice:
+				panic("bad pointer or slice in map case in " + tf.Name())
+			default: // E.g., map[K]V
+				mfi.merge = func(dst, src pointer) {
+					sm := src.asPointerTo(tf).Elem()
+					if sm.Len() == 0 {
+						return
+					}
+					dm := dst.asPointerTo(tf).Elem()
+					if dm.IsNil() {
+						dm.Set(reflect.MakeMap(tf))
+					}
+
+					switch tf.Elem().Kind() {
+					case reflect.Ptr: // Proto struct (e.g., *T)
+						for _, key := range sm.MapKeys() {
+							val := sm.MapIndex(key)
+							val = reflect.ValueOf(Clone(val.Interface().(Message)))
+							dm.SetMapIndex(key, val)
+						}
+					case reflect.Slice: // E.g. Bytes type (e.g., []byte)
+						for _, key := range sm.MapKeys() {
+							val := sm.MapIndex(key)
+							val = reflect.ValueOf(append([]byte{}, val.Bytes()...))
+							dm.SetMapIndex(key, val)
+						}
+					default: // Basic type (e.g., string)
+						for _, key := range sm.MapKeys() {
+							val := sm.MapIndex(key)
+							dm.SetMapIndex(key, val)
+						}
+					}
+				}
+			}
+		case reflect.Interface:
+			// Must be oneof field.
+			switch {
+			case isPointer || isSlice:
+				panic("bad pointer or slice in interface case in " + tf.Name())
+			default: // E.g., interface{}
+				// TODO: Make this faster?
+				mfi.merge = func(dst, src pointer) {
+					su := src.asPointerTo(tf).Elem()
+					if !su.IsNil() {
+						du := dst.asPointerTo(tf).Elem()
+						typ := su.Elem().Type()
+						if du.IsNil() || du.Elem().Type() != typ {
+							du.Set(reflect.New(typ.Elem())) // Initialize interface if empty
+						}
+						sv := su.Elem().Elem().Field(0)
+						if sv.Kind() == reflect.Ptr && sv.IsNil() {
+							return
+						}
+						dv := du.Elem().Elem().Field(0)
+						if dv.Kind() == reflect.Ptr && dv.IsNil() {
+							dv.Set(reflect.New(sv.Type().Elem())) // Initialize proto message if empty
+						}
+						switch sv.Type().Kind() {
+						case reflect.Ptr: // Proto struct (e.g., *T)
+							Merge(dv.Interface().(Message), sv.Interface().(Message))
+						case reflect.Slice: // E.g. Bytes type (e.g., []byte)
+							dv.Set(reflect.ValueOf(append([]byte{}, sv.Bytes()...)))
+						default: // Basic type (e.g., string)
+							dv.Set(sv)
+						}
+					}
+				}
+			}
+		default:
+			panic(fmt.Sprintf("merger not found for type:%s", tf))
+		}
+		mi.fields = append(mi.fields, mfi)
+	}
+
+	mi.unrecognized = invalidField
+	if f, ok := t.FieldByName("XXX_unrecognized"); ok {
+		if f.Type != reflect.TypeOf([]byte{}) {
+			panic("expected XXX_unrecognized to be of type []byte")
+		}
+		mi.unrecognized = toField(&f)
+	}
+
+	atomic.StoreInt32(&mi.initialized, 1)
+}
diff --git a/vendor/github.com/golang/protobuf/proto/table_unmarshal.go b/vendor/github.com/golang/protobuf/proto/table_unmarshal.go
new file mode 100644
index 0000000..55f0340
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/table_unmarshal.go
@@ -0,0 +1,1967 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2016 The Go Authors.  All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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.
+
+package proto
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"math"
+	"reflect"
+	"strconv"
+	"strings"
+	"sync"
+	"sync/atomic"
+	"unicode/utf8"
+)
+
+// Unmarshal is the entry point from the generated .pb.go files.
+// This function is not intended to be used by non-generated code.
+// This function is not subject to any compatibility guarantee.
+// msg contains a pointer to a protocol buffer struct.
+// b is the data to be unmarshaled into the protocol buffer.
+// a is a pointer to a place to store cached unmarshal information.
+func (a *InternalMessageInfo) Unmarshal(msg Message, b []byte) error {
+	// Load the unmarshal information for this message type.
+	// The atomic load ensures memory consistency.
+	u := atomicLoadUnmarshalInfo(&a.unmarshal)
+	if u == nil {
+		// Slow path: find unmarshal info for msg, update a with it.
+		u = getUnmarshalInfo(reflect.TypeOf(msg).Elem())
+		atomicStoreUnmarshalInfo(&a.unmarshal, u)
+	}
+	// Then do the unmarshaling.
+	err := u.unmarshal(toPointer(&msg), b)
+	return err
+}
+
+type unmarshalInfo struct {
+	typ reflect.Type // type of the protobuf struct
+
+	// 0 = only typ field is initialized
+	// 1 = completely initialized
+	initialized     int32
+	lock            sync.Mutex                    // prevents double initialization
+	dense           []unmarshalFieldInfo          // fields indexed by tag #
+	sparse          map[uint64]unmarshalFieldInfo // fields indexed by tag #
+	reqFields       []string                      // names of required fields
+	reqMask         uint64                        // 1<<len(reqFields)-1
+	unrecognized    field                         // offset of []byte to put unrecognized data (or invalidField if we should throw it away)
+	extensions      field                         // offset of extensions field (of type proto.XXX_InternalExtensions), or invalidField if it does not exist
+	oldExtensions   field                         // offset of old-form extensions field (of type map[int]Extension)
+	extensionRanges []ExtensionRange              // if non-nil, implies extensions field is valid
+	isMessageSet    bool                          // if true, implies extensions field is valid
+}
+
+// An unmarshaler takes a stream of bytes and a pointer to a field of a message.
+// It decodes the field, stores it at f, and returns the unused bytes.
+// w is the wire encoding.
+// b is the data after the tag and wire encoding have been read.
+type unmarshaler func(b []byte, f pointer, w int) ([]byte, error)
+
+type unmarshalFieldInfo struct {
+	// location of the field in the proto message structure.
+	field field
+
+	// function to unmarshal the data for the field.
+	unmarshal unmarshaler
+
+	// if a required field, contains a single set bit at this field's index in the required field list.
+	reqMask uint64
+}
+
+var (
+	unmarshalInfoMap  = map[reflect.Type]*unmarshalInfo{}
+	unmarshalInfoLock sync.Mutex
+)
+
+// getUnmarshalInfo returns the data structure which can be
+// subsequently used to unmarshal a message of the given type.
+// t is the type of the message (note: not pointer to message).
+func getUnmarshalInfo(t reflect.Type) *unmarshalInfo {
+	// It would be correct to return a new unmarshalInfo
+	// unconditionally. We would end up allocating one
+	// per occurrence of that type as a message or submessage.
+	// We use a cache here just to reduce memory usage.
+	unmarshalInfoLock.Lock()
+	defer unmarshalInfoLock.Unlock()
+	u := unmarshalInfoMap[t]
+	if u == nil {
+		u = &unmarshalInfo{typ: t}
+		// Note: we just set the type here. The rest of the fields
+		// will be initialized on first use.
+		unmarshalInfoMap[t] = u
+	}
+	return u
+}
+
+// unmarshal does the main work of unmarshaling a message.
+// u provides type information used to unmarshal the message.
+// m is a pointer to a protocol buffer message.
+// b is a byte stream to unmarshal into m.
+// This is top routine used when recursively unmarshaling submessages.
+func (u *unmarshalInfo) unmarshal(m pointer, b []byte) error {
+	if atomic.LoadInt32(&u.initialized) == 0 {
+		u.computeUnmarshalInfo()
+	}
+	if u.isMessageSet {
+		return UnmarshalMessageSet(b, m.offset(u.extensions).toExtensions())
+	}
+	var reqMask uint64            // bitmask of required fields we've seen.
+	var rnse *RequiredNotSetError // an instance of a RequiredNotSetError returned by a submessage.
+	for len(b) > 0 {
+		// Read tag and wire type.
+		// Special case 1 and 2 byte varints.
+		var x uint64
+		if b[0] < 128 {
+			x = uint64(b[0])
+			b = b[1:]
+		} else if len(b) >= 2 && b[1] < 128 {
+			x = uint64(b[0]&0x7f) + uint64(b[1])<<7
+			b = b[2:]
+		} else {
+			var n int
+			x, n = decodeVarint(b)
+			if n == 0 {
+				return io.ErrUnexpectedEOF
+			}
+			b = b[n:]
+		}
+		tag := x >> 3
+		wire := int(x) & 7
+
+		// Dispatch on the tag to one of the unmarshal* functions below.
+		var f unmarshalFieldInfo
+		if tag < uint64(len(u.dense)) {
+			f = u.dense[tag]
+		} else {
+			f = u.sparse[tag]
+		}
+		if fn := f.unmarshal; fn != nil {
+			var err error
+			b, err = fn(b, m.offset(f.field), wire)
+			if err == nil {
+				reqMask |= f.reqMask
+				continue
+			}
+			if r, ok := err.(*RequiredNotSetError); ok {
+				// Remember this error, but keep parsing. We need to produce
+				// a full parse even if a required field is missing.
+				rnse = r
+				reqMask |= f.reqMask
+				continue
+			}
+			if err != errInternalBadWireType {
+				return err
+			}
+			// Fragments with bad wire type are treated as unknown fields.
+		}
+
+		// Unknown tag.
+		if !u.unrecognized.IsValid() {
+			// Don't keep unrecognized data; just skip it.
+			var err error
+			b, err = skipField(b, wire)
+			if err != nil {
+				return err
+			}
+			continue
+		}
+		// Keep unrecognized data around.
+		// maybe in extensions, maybe in the unrecognized field.
+		z := m.offset(u.unrecognized).toBytes()
+		var emap map[int32]Extension
+		var e Extension
+		for _, r := range u.extensionRanges {
+			if uint64(r.Start) <= tag && tag <= uint64(r.End) {
+				if u.extensions.IsValid() {
+					mp := m.offset(u.extensions).toExtensions()
+					emap = mp.extensionsWrite()
+					e = emap[int32(tag)]
+					z = &e.enc
+					break
+				}
+				if u.oldExtensions.IsValid() {
+					p := m.offset(u.oldExtensions).toOldExtensions()
+					emap = *p
+					if emap == nil {
+						emap = map[int32]Extension{}
+						*p = emap
+					}
+					e = emap[int32(tag)]
+					z = &e.enc
+					break
+				}
+				panic("no extensions field available")
+			}
+		}
+
+		// Use wire type to skip data.
+		var err error
+		b0 := b
+		b, err = skipField(b, wire)
+		if err != nil {
+			return err
+		}
+		*z = encodeVarint(*z, tag<<3|uint64(wire))
+		*z = append(*z, b0[:len(b0)-len(b)]...)
+
+		if emap != nil {
+			emap[int32(tag)] = e
+		}
+	}
+	if rnse != nil {
+		// A required field of a submessage/group is missing. Return that error.
+		return rnse
+	}
+	if reqMask != u.reqMask {
+		// A required field of this message is missing.
+		for _, n := range u.reqFields {
+			if reqMask&1 == 0 {
+				return &RequiredNotSetError{n}
+			}
+			reqMask >>= 1
+		}
+	}
+	return nil
+}
+
+// computeUnmarshalInfo fills in u with information for use
+// in unmarshaling protocol buffers of type u.typ.
+func (u *unmarshalInfo) computeUnmarshalInfo() {
+	u.lock.Lock()
+	defer u.lock.Unlock()
+	if u.initialized != 0 {
+		return
+	}
+	t := u.typ
+	n := t.NumField()
+
+	// Set up the "not found" value for the unrecognized byte buffer.
+	// This is the default for proto3.
+	u.unrecognized = invalidField
+	u.extensions = invalidField
+	u.oldExtensions = invalidField
+
+	// List of the generated type and offset for each oneof field.
+	type oneofField struct {
+		ityp  reflect.Type // interface type of oneof field
+		field field        // offset in containing message
+	}
+	var oneofFields []oneofField
+
+	for i := 0; i < n; i++ {
+		f := t.Field(i)
+		if f.Name == "XXX_unrecognized" {
+			// The byte slice used to hold unrecognized input is special.
+			if f.Type != reflect.TypeOf(([]byte)(nil)) {
+				panic("bad type for XXX_unrecognized field: " + f.Type.Name())
+			}
+			u.unrecognized = toField(&f)
+			continue
+		}
+		if f.Name == "XXX_InternalExtensions" {
+			// Ditto here.
+			if f.Type != reflect.TypeOf(XXX_InternalExtensions{}) {
+				panic("bad type for XXX_InternalExtensions field: " + f.Type.Name())
+			}
+			u.extensions = toField(&f)
+			if f.Tag.Get("protobuf_messageset") == "1" {
+				u.isMessageSet = true
+			}
+			continue
+		}
+		if f.Name == "XXX_extensions" {
+			// An older form of the extensions field.
+			if f.Type != reflect.TypeOf((map[int32]Extension)(nil)) {
+				panic("bad type for XXX_extensions field: " + f.Type.Name())
+			}
+			u.oldExtensions = toField(&f)
+			continue
+		}
+		if f.Name == "XXX_NoUnkeyedLiteral" || f.Name == "XXX_sizecache" {
+			continue
+		}
+
+		oneof := f.Tag.Get("protobuf_oneof")
+		if oneof != "" {
+			oneofFields = append(oneofFields, oneofField{f.Type, toField(&f)})
+			// The rest of oneof processing happens below.
+			continue
+		}
+
+		tags := f.Tag.Get("protobuf")
+		tagArray := strings.Split(tags, ",")
+		if len(tagArray) < 2 {
+			panic("protobuf tag not enough fields in " + t.Name() + "." + f.Name + ": " + tags)
+		}
+		tag, err := strconv.Atoi(tagArray[1])
+		if err != nil {
+			panic("protobuf tag field not an integer: " + tagArray[1])
+		}
+
+		name := ""
+		for _, tag := range tagArray[3:] {
+			if strings.HasPrefix(tag, "name=") {
+				name = tag[5:]
+			}
+		}
+
+		// Extract unmarshaling function from the field (its type and tags).
+		unmarshal := fieldUnmarshaler(&f)
+
+		// Required field?
+		var reqMask uint64
+		if tagArray[2] == "req" {
+			bit := len(u.reqFields)
+			u.reqFields = append(u.reqFields, name)
+			reqMask = uint64(1) << uint(bit)
+			// TODO: if we have more than 64 required fields, we end up
+			// not verifying that all required fields are present.
+			// Fix this, perhaps using a count of required fields?
+		}
+
+		// Store the info in the correct slot in the message.
+		u.setTag(tag, toField(&f), unmarshal, reqMask)
+	}
+
+	// Find any types associated with oneof fields.
+	// TODO: XXX_OneofFuncs returns more info than we need.  Get rid of some of it?
+	fn := reflect.Zero(reflect.PtrTo(t)).MethodByName("XXX_OneofFuncs")
+	if fn.IsValid() {
+		res := fn.Call(nil)[3] // last return value from XXX_OneofFuncs: []interface{}
+		for i := res.Len() - 1; i >= 0; i-- {
+			v := res.Index(i)                             // interface{}
+			tptr := reflect.ValueOf(v.Interface()).Type() // *Msg_X
+			typ := tptr.Elem()                            // Msg_X
+
+			f := typ.Field(0) // oneof implementers have one field
+			baseUnmarshal := fieldUnmarshaler(&f)
+			tagstr := strings.Split(f.Tag.Get("protobuf"), ",")[1]
+			tag, err := strconv.Atoi(tagstr)
+			if err != nil {
+				panic("protobuf tag field not an integer: " + tagstr)
+			}
+
+			// Find the oneof field that this struct implements.
+			// Might take O(n^2) to process all of the oneofs, but who cares.
+			for _, of := range oneofFields {
+				if tptr.Implements(of.ityp) {
+					// We have found the corresponding interface for this struct.
+					// That lets us know where this struct should be stored
+					// when we encounter it during unmarshaling.
+					unmarshal := makeUnmarshalOneof(typ, of.ityp, baseUnmarshal)
+					u.setTag(tag, of.field, unmarshal, 0)
+				}
+			}
+		}
+	}
+
+	// Get extension ranges, if any.
+	fn = reflect.Zero(reflect.PtrTo(t)).MethodByName("ExtensionRangeArray")
+	if fn.IsValid() {
+		if !u.extensions.IsValid() && !u.oldExtensions.IsValid() {
+			panic("a message with extensions, but no extensions field in " + t.Name())
+		}
+		u.extensionRanges = fn.Call(nil)[0].Interface().([]ExtensionRange)
+	}
+
+	// Explicitly disallow tag 0. This will ensure we flag an error
+	// when decoding a buffer of all zeros. Without this code, we
+	// would decode and skip an all-zero buffer of even length.
+	// [0 0] is [tag=0/wiretype=varint varint-encoded-0].
+	u.setTag(0, zeroField, func(b []byte, f pointer, w int) ([]byte, error) {
+		return nil, fmt.Errorf("proto: %s: illegal tag 0 (wire type %d)", t, w)
+	}, 0)
+
+	// Set mask for required field check.
+	u.reqMask = uint64(1)<<uint(len(u.reqFields)) - 1
+
+	atomic.StoreInt32(&u.initialized, 1)
+}
+
+// setTag stores the unmarshal information for the given tag.
+// tag = tag # for field
+// field/unmarshal = unmarshal info for that field.
+// reqMask = if required, bitmask for field position in required field list. 0 otherwise.
+func (u *unmarshalInfo) setTag(tag int, field field, unmarshal unmarshaler, reqMask uint64) {
+	i := unmarshalFieldInfo{field: field, unmarshal: unmarshal, reqMask: reqMask}
+	n := u.typ.NumField()
+	if tag >= 0 && (tag < 16 || tag < 2*n) { // TODO: what are the right numbers here?
+		for len(u.dense) <= tag {
+			u.dense = append(u.dense, unmarshalFieldInfo{})
+		}
+		u.dense[tag] = i
+		return
+	}
+	if u.sparse == nil {
+		u.sparse = map[uint64]unmarshalFieldInfo{}
+	}
+	u.sparse[uint64(tag)] = i
+}
+
+// fieldUnmarshaler returns an unmarshaler for the given field.
+func fieldUnmarshaler(f *reflect.StructField) unmarshaler {
+	if f.Type.Kind() == reflect.Map {
+		return makeUnmarshalMap(f)
+	}
+	return typeUnmarshaler(f.Type, f.Tag.Get("protobuf"))
+}
+
+// typeUnmarshaler returns an unmarshaler for the given field type / field tag pair.
+func typeUnmarshaler(t reflect.Type, tags string) unmarshaler {
+	tagArray := strings.Split(tags, ",")
+	encoding := tagArray[0]
+	name := "unknown"
+	for _, tag := range tagArray[3:] {
+		if strings.HasPrefix(tag, "name=") {
+			name = tag[5:]
+		}
+	}
+
+	// Figure out packaging (pointer, slice, or both)
+	slice := false
+	pointer := false
+	if t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 {
+		slice = true
+		t = t.Elem()
+	}
+	if t.Kind() == reflect.Ptr {
+		pointer = true
+		t = t.Elem()
+	}
+
+	// We'll never have both pointer and slice for basic types.
+	if pointer && slice && t.Kind() != reflect.Struct {
+		panic("both pointer and slice for basic type in " + t.Name())
+	}
+
+	switch t.Kind() {
+	case reflect.Bool:
+		if pointer {
+			return unmarshalBoolPtr
+		}
+		if slice {
+			return unmarshalBoolSlice
+		}
+		return unmarshalBoolValue
+	case reflect.Int32:
+		switch encoding {
+		case "fixed32":
+			if pointer {
+				return unmarshalFixedS32Ptr
+			}
+			if slice {
+				return unmarshalFixedS32Slice
+			}
+			return unmarshalFixedS32Value
+		case "varint":
+			// this could be int32 or enum
+			if pointer {
+				return unmarshalInt32Ptr
+			}
+			if slice {
+				return unmarshalInt32Slice
+			}
+			return unmarshalInt32Value
+		case "zigzag32":
+			if pointer {
+				return unmarshalSint32Ptr
+			}
+			if slice {
+				return unmarshalSint32Slice
+			}
+			return unmarshalSint32Value
+		}
+	case reflect.Int64:
+		switch encoding {
+		case "fixed64":
+			if pointer {
+				return unmarshalFixedS64Ptr
+			}
+			if slice {
+				return unmarshalFixedS64Slice
+			}
+			return unmarshalFixedS64Value
+		case "varint":
+			if pointer {
+				return unmarshalInt64Ptr
+			}
+			if slice {
+				return unmarshalInt64Slice
+			}
+			return unmarshalInt64Value
+		case "zigzag64":
+			if pointer {
+				return unmarshalSint64Ptr
+			}
+			if slice {
+				return unmarshalSint64Slice
+			}
+			return unmarshalSint64Value
+		}
+	case reflect.Uint32:
+		switch encoding {
+		case "fixed32":
+			if pointer {
+				return unmarshalFixed32Ptr
+			}
+			if slice {
+				return unmarshalFixed32Slice
+			}
+			return unmarshalFixed32Value
+		case "varint":
+			if pointer {
+				return unmarshalUint32Ptr
+			}
+			if slice {
+				return unmarshalUint32Slice
+			}
+			return unmarshalUint32Value
+		}
+	case reflect.Uint64:
+		switch encoding {
+		case "fixed64":
+			if pointer {
+				return unmarshalFixed64Ptr
+			}
+			if slice {
+				return unmarshalFixed64Slice
+			}
+			return unmarshalFixed64Value
+		case "varint":
+			if pointer {
+				return unmarshalUint64Ptr
+			}
+			if slice {
+				return unmarshalUint64Slice
+			}
+			return unmarshalUint64Value
+		}
+	case reflect.Float32:
+		if pointer {
+			return unmarshalFloat32Ptr
+		}
+		if slice {
+			return unmarshalFloat32Slice
+		}
+		return unmarshalFloat32Value
+	case reflect.Float64:
+		if pointer {
+			return unmarshalFloat64Ptr
+		}
+		if slice {
+			return unmarshalFloat64Slice
+		}
+		return unmarshalFloat64Value
+	case reflect.Map:
+		panic("map type in typeUnmarshaler in " + t.Name())
+	case reflect.Slice:
+		if pointer {
+			panic("bad pointer in slice case in " + t.Name())
+		}
+		if slice {
+			return unmarshalBytesSlice
+		}
+		return unmarshalBytesValue
+	case reflect.String:
+		if pointer {
+			return unmarshalStringPtr
+		}
+		if slice {
+			return unmarshalStringSlice
+		}
+		return unmarshalStringValue
+	case reflect.Struct:
+		// message or group field
+		if !pointer {
+			panic(fmt.Sprintf("message/group field %s:%s without pointer", t, encoding))
+		}
+		switch encoding {
+		case "bytes":
+			if slice {
+				return makeUnmarshalMessageSlicePtr(getUnmarshalInfo(t), name)
+			}
+			return makeUnmarshalMessagePtr(getUnmarshalInfo(t), name)
+		case "group":
+			if slice {
+				return makeUnmarshalGroupSlicePtr(getUnmarshalInfo(t), name)
+			}
+			return makeUnmarshalGroupPtr(getUnmarshalInfo(t), name)
+		}
+	}
+	panic(fmt.Sprintf("unmarshaler not found type:%s encoding:%s", t, encoding))
+}
+
+// Below are all the unmarshalers for individual fields of various types.
+
+func unmarshalInt64Value(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := int64(x)
+	*f.toInt64() = v
+	return b, nil
+}
+
+func unmarshalInt64Ptr(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := int64(x)
+	*f.toInt64Ptr() = &v
+	return b, nil
+}
+
+func unmarshalInt64Slice(b []byte, f pointer, w int) ([]byte, error) {
+	if w == WireBytes { // packed
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		if x > uint64(len(b)) {
+			return nil, io.ErrUnexpectedEOF
+		}
+		res := b[x:]
+		b = b[:x]
+		for len(b) > 0 {
+			x, n = decodeVarint(b)
+			if n == 0 {
+				return nil, io.ErrUnexpectedEOF
+			}
+			b = b[n:]
+			v := int64(x)
+			s := f.toInt64Slice()
+			*s = append(*s, v)
+		}
+		return res, nil
+	}
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := int64(x)
+	s := f.toInt64Slice()
+	*s = append(*s, v)
+	return b, nil
+}
+
+func unmarshalSint64Value(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := int64(x>>1) ^ int64(x)<<63>>63
+	*f.toInt64() = v
+	return b, nil
+}
+
+func unmarshalSint64Ptr(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := int64(x>>1) ^ int64(x)<<63>>63
+	*f.toInt64Ptr() = &v
+	return b, nil
+}
+
+func unmarshalSint64Slice(b []byte, f pointer, w int) ([]byte, error) {
+	if w == WireBytes { // packed
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		if x > uint64(len(b)) {
+			return nil, io.ErrUnexpectedEOF
+		}
+		res := b[x:]
+		b = b[:x]
+		for len(b) > 0 {
+			x, n = decodeVarint(b)
+			if n == 0 {
+				return nil, io.ErrUnexpectedEOF
+			}
+			b = b[n:]
+			v := int64(x>>1) ^ int64(x)<<63>>63
+			s := f.toInt64Slice()
+			*s = append(*s, v)
+		}
+		return res, nil
+	}
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := int64(x>>1) ^ int64(x)<<63>>63
+	s := f.toInt64Slice()
+	*s = append(*s, v)
+	return b, nil
+}
+
+func unmarshalUint64Value(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := uint64(x)
+	*f.toUint64() = v
+	return b, nil
+}
+
+func unmarshalUint64Ptr(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := uint64(x)
+	*f.toUint64Ptr() = &v
+	return b, nil
+}
+
+func unmarshalUint64Slice(b []byte, f pointer, w int) ([]byte, error) {
+	if w == WireBytes { // packed
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		if x > uint64(len(b)) {
+			return nil, io.ErrUnexpectedEOF
+		}
+		res := b[x:]
+		b = b[:x]
+		for len(b) > 0 {
+			x, n = decodeVarint(b)
+			if n == 0 {
+				return nil, io.ErrUnexpectedEOF
+			}
+			b = b[n:]
+			v := uint64(x)
+			s := f.toUint64Slice()
+			*s = append(*s, v)
+		}
+		return res, nil
+	}
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := uint64(x)
+	s := f.toUint64Slice()
+	*s = append(*s, v)
+	return b, nil
+}
+
+func unmarshalInt32Value(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := int32(x)
+	*f.toInt32() = v
+	return b, nil
+}
+
+func unmarshalInt32Ptr(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := int32(x)
+	f.setInt32Ptr(v)
+	return b, nil
+}
+
+func unmarshalInt32Slice(b []byte, f pointer, w int) ([]byte, error) {
+	if w == WireBytes { // packed
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		if x > uint64(len(b)) {
+			return nil, io.ErrUnexpectedEOF
+		}
+		res := b[x:]
+		b = b[:x]
+		for len(b) > 0 {
+			x, n = decodeVarint(b)
+			if n == 0 {
+				return nil, io.ErrUnexpectedEOF
+			}
+			b = b[n:]
+			v := int32(x)
+			f.appendInt32Slice(v)
+		}
+		return res, nil
+	}
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := int32(x)
+	f.appendInt32Slice(v)
+	return b, nil
+}
+
+func unmarshalSint32Value(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := int32(x>>1) ^ int32(x)<<31>>31
+	*f.toInt32() = v
+	return b, nil
+}
+
+func unmarshalSint32Ptr(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := int32(x>>1) ^ int32(x)<<31>>31
+	f.setInt32Ptr(v)
+	return b, nil
+}
+
+func unmarshalSint32Slice(b []byte, f pointer, w int) ([]byte, error) {
+	if w == WireBytes { // packed
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		if x > uint64(len(b)) {
+			return nil, io.ErrUnexpectedEOF
+		}
+		res := b[x:]
+		b = b[:x]
+		for len(b) > 0 {
+			x, n = decodeVarint(b)
+			if n == 0 {
+				return nil, io.ErrUnexpectedEOF
+			}
+			b = b[n:]
+			v := int32(x>>1) ^ int32(x)<<31>>31
+			f.appendInt32Slice(v)
+		}
+		return res, nil
+	}
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := int32(x>>1) ^ int32(x)<<31>>31
+	f.appendInt32Slice(v)
+	return b, nil
+}
+
+func unmarshalUint32Value(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := uint32(x)
+	*f.toUint32() = v
+	return b, nil
+}
+
+func unmarshalUint32Ptr(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := uint32(x)
+	*f.toUint32Ptr() = &v
+	return b, nil
+}
+
+func unmarshalUint32Slice(b []byte, f pointer, w int) ([]byte, error) {
+	if w == WireBytes { // packed
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		if x > uint64(len(b)) {
+			return nil, io.ErrUnexpectedEOF
+		}
+		res := b[x:]
+		b = b[:x]
+		for len(b) > 0 {
+			x, n = decodeVarint(b)
+			if n == 0 {
+				return nil, io.ErrUnexpectedEOF
+			}
+			b = b[n:]
+			v := uint32(x)
+			s := f.toUint32Slice()
+			*s = append(*s, v)
+		}
+		return res, nil
+	}
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	v := uint32(x)
+	s := f.toUint32Slice()
+	*s = append(*s, v)
+	return b, nil
+}
+
+func unmarshalFixed64Value(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireFixed64 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 8 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
+	*f.toUint64() = v
+	return b[8:], nil
+}
+
+func unmarshalFixed64Ptr(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireFixed64 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 8 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
+	*f.toUint64Ptr() = &v
+	return b[8:], nil
+}
+
+func unmarshalFixed64Slice(b []byte, f pointer, w int) ([]byte, error) {
+	if w == WireBytes { // packed
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		if x > uint64(len(b)) {
+			return nil, io.ErrUnexpectedEOF
+		}
+		res := b[x:]
+		b = b[:x]
+		for len(b) > 0 {
+			if len(b) < 8 {
+				return nil, io.ErrUnexpectedEOF
+			}
+			v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
+			s := f.toUint64Slice()
+			*s = append(*s, v)
+			b = b[8:]
+		}
+		return res, nil
+	}
+	if w != WireFixed64 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 8 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
+	s := f.toUint64Slice()
+	*s = append(*s, v)
+	return b[8:], nil
+}
+
+func unmarshalFixedS64Value(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireFixed64 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 8 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56
+	*f.toInt64() = v
+	return b[8:], nil
+}
+
+func unmarshalFixedS64Ptr(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireFixed64 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 8 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56
+	*f.toInt64Ptr() = &v
+	return b[8:], nil
+}
+
+func unmarshalFixedS64Slice(b []byte, f pointer, w int) ([]byte, error) {
+	if w == WireBytes { // packed
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		if x > uint64(len(b)) {
+			return nil, io.ErrUnexpectedEOF
+		}
+		res := b[x:]
+		b = b[:x]
+		for len(b) > 0 {
+			if len(b) < 8 {
+				return nil, io.ErrUnexpectedEOF
+			}
+			v := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56
+			s := f.toInt64Slice()
+			*s = append(*s, v)
+			b = b[8:]
+		}
+		return res, nil
+	}
+	if w != WireFixed64 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 8 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56
+	s := f.toInt64Slice()
+	*s = append(*s, v)
+	return b[8:], nil
+}
+
+func unmarshalFixed32Value(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireFixed32 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 4 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
+	*f.toUint32() = v
+	return b[4:], nil
+}
+
+func unmarshalFixed32Ptr(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireFixed32 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 4 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
+	*f.toUint32Ptr() = &v
+	return b[4:], nil
+}
+
+func unmarshalFixed32Slice(b []byte, f pointer, w int) ([]byte, error) {
+	if w == WireBytes { // packed
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		if x > uint64(len(b)) {
+			return nil, io.ErrUnexpectedEOF
+		}
+		res := b[x:]
+		b = b[:x]
+		for len(b) > 0 {
+			if len(b) < 4 {
+				return nil, io.ErrUnexpectedEOF
+			}
+			v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
+			s := f.toUint32Slice()
+			*s = append(*s, v)
+			b = b[4:]
+		}
+		return res, nil
+	}
+	if w != WireFixed32 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 4 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
+	s := f.toUint32Slice()
+	*s = append(*s, v)
+	return b[4:], nil
+}
+
+func unmarshalFixedS32Value(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireFixed32 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 4 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24
+	*f.toInt32() = v
+	return b[4:], nil
+}
+
+func unmarshalFixedS32Ptr(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireFixed32 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 4 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24
+	f.setInt32Ptr(v)
+	return b[4:], nil
+}
+
+func unmarshalFixedS32Slice(b []byte, f pointer, w int) ([]byte, error) {
+	if w == WireBytes { // packed
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		if x > uint64(len(b)) {
+			return nil, io.ErrUnexpectedEOF
+		}
+		res := b[x:]
+		b = b[:x]
+		for len(b) > 0 {
+			if len(b) < 4 {
+				return nil, io.ErrUnexpectedEOF
+			}
+			v := int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24
+			f.appendInt32Slice(v)
+			b = b[4:]
+		}
+		return res, nil
+	}
+	if w != WireFixed32 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 4 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24
+	f.appendInt32Slice(v)
+	return b[4:], nil
+}
+
+func unmarshalBoolValue(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	// Note: any length varint is allowed, even though any sane
+	// encoder will use one byte.
+	// See https://github.com/golang/protobuf/issues/76
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	// TODO: check if x>1? Tests seem to indicate no.
+	v := x != 0
+	*f.toBool() = v
+	return b[n:], nil
+}
+
+func unmarshalBoolPtr(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := x != 0
+	*f.toBoolPtr() = &v
+	return b[n:], nil
+}
+
+func unmarshalBoolSlice(b []byte, f pointer, w int) ([]byte, error) {
+	if w == WireBytes { // packed
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		if x > uint64(len(b)) {
+			return nil, io.ErrUnexpectedEOF
+		}
+		res := b[x:]
+		b = b[:x]
+		for len(b) > 0 {
+			x, n = decodeVarint(b)
+			if n == 0 {
+				return nil, io.ErrUnexpectedEOF
+			}
+			v := x != 0
+			s := f.toBoolSlice()
+			*s = append(*s, v)
+			b = b[n:]
+		}
+		return res, nil
+	}
+	if w != WireVarint {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := x != 0
+	s := f.toBoolSlice()
+	*s = append(*s, v)
+	return b[n:], nil
+}
+
+func unmarshalFloat64Value(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireFixed64 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 8 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := math.Float64frombits(uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56)
+	*f.toFloat64() = v
+	return b[8:], nil
+}
+
+func unmarshalFloat64Ptr(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireFixed64 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 8 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := math.Float64frombits(uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56)
+	*f.toFloat64Ptr() = &v
+	return b[8:], nil
+}
+
+func unmarshalFloat64Slice(b []byte, f pointer, w int) ([]byte, error) {
+	if w == WireBytes { // packed
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		if x > uint64(len(b)) {
+			return nil, io.ErrUnexpectedEOF
+		}
+		res := b[x:]
+		b = b[:x]
+		for len(b) > 0 {
+			if len(b) < 8 {
+				return nil, io.ErrUnexpectedEOF
+			}
+			v := math.Float64frombits(uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56)
+			s := f.toFloat64Slice()
+			*s = append(*s, v)
+			b = b[8:]
+		}
+		return res, nil
+	}
+	if w != WireFixed64 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 8 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := math.Float64frombits(uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56)
+	s := f.toFloat64Slice()
+	*s = append(*s, v)
+	return b[8:], nil
+}
+
+func unmarshalFloat32Value(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireFixed32 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 4 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := math.Float32frombits(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24)
+	*f.toFloat32() = v
+	return b[4:], nil
+}
+
+func unmarshalFloat32Ptr(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireFixed32 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 4 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := math.Float32frombits(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24)
+	*f.toFloat32Ptr() = &v
+	return b[4:], nil
+}
+
+func unmarshalFloat32Slice(b []byte, f pointer, w int) ([]byte, error) {
+	if w == WireBytes { // packed
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		if x > uint64(len(b)) {
+			return nil, io.ErrUnexpectedEOF
+		}
+		res := b[x:]
+		b = b[:x]
+		for len(b) > 0 {
+			if len(b) < 4 {
+				return nil, io.ErrUnexpectedEOF
+			}
+			v := math.Float32frombits(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24)
+			s := f.toFloat32Slice()
+			*s = append(*s, v)
+			b = b[4:]
+		}
+		return res, nil
+	}
+	if w != WireFixed32 {
+		return b, errInternalBadWireType
+	}
+	if len(b) < 4 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := math.Float32frombits(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24)
+	s := f.toFloat32Slice()
+	*s = append(*s, v)
+	return b[4:], nil
+}
+
+func unmarshalStringValue(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireBytes {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	if x > uint64(len(b)) {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := string(b[:x])
+	if !utf8.ValidString(v) {
+		return nil, errInvalidUTF8
+	}
+	*f.toString() = v
+	return b[x:], nil
+}
+
+func unmarshalStringPtr(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireBytes {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	if x > uint64(len(b)) {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := string(b[:x])
+	if !utf8.ValidString(v) {
+		return nil, errInvalidUTF8
+	}
+	*f.toStringPtr() = &v
+	return b[x:], nil
+}
+
+func unmarshalStringSlice(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireBytes {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	if x > uint64(len(b)) {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := string(b[:x])
+	if !utf8.ValidString(v) {
+		return nil, errInvalidUTF8
+	}
+	s := f.toStringSlice()
+	*s = append(*s, v)
+	return b[x:], nil
+}
+
+var emptyBuf [0]byte
+
+func unmarshalBytesValue(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireBytes {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	if x > uint64(len(b)) {
+		return nil, io.ErrUnexpectedEOF
+	}
+	// The use of append here is a trick which avoids the zeroing
+	// that would be required if we used a make/copy pair.
+	// We append to emptyBuf instead of nil because we want
+	// a non-nil result even when the length is 0.
+	v := append(emptyBuf[:], b[:x]...)
+	*f.toBytes() = v
+	return b[x:], nil
+}
+
+func unmarshalBytesSlice(b []byte, f pointer, w int) ([]byte, error) {
+	if w != WireBytes {
+		return b, errInternalBadWireType
+	}
+	x, n := decodeVarint(b)
+	if n == 0 {
+		return nil, io.ErrUnexpectedEOF
+	}
+	b = b[n:]
+	if x > uint64(len(b)) {
+		return nil, io.ErrUnexpectedEOF
+	}
+	v := append(emptyBuf[:], b[:x]...)
+	s := f.toBytesSlice()
+	*s = append(*s, v)
+	return b[x:], nil
+}
+
+func makeUnmarshalMessagePtr(sub *unmarshalInfo, name string) unmarshaler {
+	return func(b []byte, f pointer, w int) ([]byte, error) {
+		if w != WireBytes {
+			return b, errInternalBadWireType
+		}
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		if x > uint64(len(b)) {
+			return nil, io.ErrUnexpectedEOF
+		}
+		// First read the message field to see if something is there.
+		// The semantics of multiple submessages are weird.  Instead of
+		// the last one winning (as it is for all other fields), multiple
+		// submessages are merged.
+		v := f.getPointer()
+		if v.isNil() {
+			v = valToPointer(reflect.New(sub.typ))
+			f.setPointer(v)
+		}
+		err := sub.unmarshal(v, b[:x])
+		if err != nil {
+			if r, ok := err.(*RequiredNotSetError); ok {
+				r.field = name + "." + r.field
+			} else {
+				return nil, err
+			}
+		}
+		return b[x:], err
+	}
+}
+
+func makeUnmarshalMessageSlicePtr(sub *unmarshalInfo, name string) unmarshaler {
+	return func(b []byte, f pointer, w int) ([]byte, error) {
+		if w != WireBytes {
+			return b, errInternalBadWireType
+		}
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		if x > uint64(len(b)) {
+			return nil, io.ErrUnexpectedEOF
+		}
+		v := valToPointer(reflect.New(sub.typ))
+		err := sub.unmarshal(v, b[:x])
+		if err != nil {
+			if r, ok := err.(*RequiredNotSetError); ok {
+				r.field = name + "." + r.field
+			} else {
+				return nil, err
+			}
+		}
+		f.appendPointer(v)
+		return b[x:], err
+	}
+}
+
+func makeUnmarshalGroupPtr(sub *unmarshalInfo, name string) unmarshaler {
+	return func(b []byte, f pointer, w int) ([]byte, error) {
+		if w != WireStartGroup {
+			return b, errInternalBadWireType
+		}
+		x, y := findEndGroup(b)
+		if x < 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		v := f.getPointer()
+		if v.isNil() {
+			v = valToPointer(reflect.New(sub.typ))
+			f.setPointer(v)
+		}
+		err := sub.unmarshal(v, b[:x])
+		if err != nil {
+			if r, ok := err.(*RequiredNotSetError); ok {
+				r.field = name + "." + r.field
+			} else {
+				return nil, err
+			}
+		}
+		return b[y:], err
+	}
+}
+
+func makeUnmarshalGroupSlicePtr(sub *unmarshalInfo, name string) unmarshaler {
+	return func(b []byte, f pointer, w int) ([]byte, error) {
+		if w != WireStartGroup {
+			return b, errInternalBadWireType
+		}
+		x, y := findEndGroup(b)
+		if x < 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		v := valToPointer(reflect.New(sub.typ))
+		err := sub.unmarshal(v, b[:x])
+		if err != nil {
+			if r, ok := err.(*RequiredNotSetError); ok {
+				r.field = name + "." + r.field
+			} else {
+				return nil, err
+			}
+		}
+		f.appendPointer(v)
+		return b[y:], err
+	}
+}
+
+func makeUnmarshalMap(f *reflect.StructField) unmarshaler {
+	t := f.Type
+	kt := t.Key()
+	vt := t.Elem()
+	unmarshalKey := typeUnmarshaler(kt, f.Tag.Get("protobuf_key"))
+	unmarshalVal := typeUnmarshaler(vt, f.Tag.Get("protobuf_val"))
+	return func(b []byte, f pointer, w int) ([]byte, error) {
+		// The map entry is a submessage. Figure out how big it is.
+		if w != WireBytes {
+			return nil, fmt.Errorf("proto: bad wiretype for map field: got %d want %d", w, WireBytes)
+		}
+		x, n := decodeVarint(b)
+		if n == 0 {
+			return nil, io.ErrUnexpectedEOF
+		}
+		b = b[n:]
+		if x > uint64(len(b)) {
+			return nil, io.ErrUnexpectedEOF
+		}
+		r := b[x:] // unused data to return
+		b = b[:x]  // data for map entry
+
+		// Note: we could use #keys * #values ~= 200 functions
+		// to do map decoding without reflection. Probably not worth it.
+		// Maps will be somewhat slow. Oh well.
+
+		// Read key and value from data.
+		k := reflect.New(kt)
+		v := reflect.New(vt)
+		for len(b) > 0 {
+			x, n := decodeVarint(b)
+			if n == 0 {
+				return nil, io.ErrUnexpectedEOF
+			}
+			wire := int(x) & 7
+			b = b[n:]
+
+			var err error
+			switch x >> 3 {
+			case 1:
+				b, err = unmarshalKey(b, valToPointer(k), wire)
+			case 2:
+				b, err = unmarshalVal(b, valToPointer(v), wire)
+			default:
+				err = errInternalBadWireType // skip unknown tag
+			}
+
+			if err == nil {
+				continue
+			}
+			if err != errInternalBadWireType {
+				return nil, err
+			}
+
+			// Skip past unknown fields.
+			b, err = skipField(b, wire)
+			if err != nil {
+				return nil, err
+			}
+		}
+
+		// Get map, allocate if needed.
+		m := f.asPointerTo(t).Elem() // an addressable map[K]T
+		if m.IsNil() {
+			m.Set(reflect.MakeMap(t))
+		}
+
+		// Insert into map.
+		m.SetMapIndex(k.Elem(), v.Elem())
+
+		return r, nil
+	}
+}
+
+// makeUnmarshalOneof makes an unmarshaler for oneof fields.
+// for:
+// message Msg {
+//   oneof F {
+//     int64 X = 1;
+//     float64 Y = 2;
+//   }
+// }
+// typ is the type of the concrete entry for a oneof case (e.g. Msg_X).
+// ityp is the interface type of the oneof field (e.g. isMsg_F).
+// unmarshal is the unmarshaler for the base type of the oneof case (e.g. int64).
+// Note that this function will be called once for each case in the oneof.
+func makeUnmarshalOneof(typ, ityp reflect.Type, unmarshal unmarshaler) unmarshaler {
+	sf := typ.Field(0)
+	field0 := toField(&sf)
+	return func(b []byte, f pointer, w int) ([]byte, error) {
+		// Allocate holder for value.
+		v := reflect.New(typ)
+
+		// Unmarshal data into holder.
+		// We unmarshal into the first field of the holder object.
+		var err error
+		b, err = unmarshal(b, valToPointer(v).offset(field0), w)
+		if err != nil {
+			return nil, err
+		}
+
+		// Write pointer to holder into target field.
+		f.asPointerTo(ityp).Elem().Set(v)
+
+		return b, nil
+	}
+}
+
+// Error used by decode internally.
+var errInternalBadWireType = errors.New("proto: internal error: bad wiretype")
+
+// skipField skips past a field of type wire and returns the remaining bytes.
+func skipField(b []byte, wire int) ([]byte, error) {
+	switch wire {
+	case WireVarint:
+		_, k := decodeVarint(b)
+		if k == 0 {
+			return b, io.ErrUnexpectedEOF
+		}
+		b = b[k:]
+	case WireFixed32:
+		if len(b) < 4 {
+			return b, io.ErrUnexpectedEOF
+		}
+		b = b[4:]
+	case WireFixed64:
+		if len(b) < 8 {
+			return b, io.ErrUnexpectedEOF
+		}
+		b = b[8:]
+	case WireBytes:
+		m, k := decodeVarint(b)
+		if k == 0 || uint64(len(b)-k) < m {
+			return b, io.ErrUnexpectedEOF
+		}
+		b = b[uint64(k)+m:]
+	case WireStartGroup:
+		_, i := findEndGroup(b)
+		if i == -1 {
+			return b, io.ErrUnexpectedEOF
+		}
+		b = b[i:]
+	default:
+		return b, fmt.Errorf("proto: can't skip unknown wire type %d", wire)
+	}
+	return b, nil
+}
+
+// findEndGroup finds the index of the next EndGroup tag.
+// Groups may be nested, so the "next" EndGroup tag is the first
+// unpaired EndGroup.
+// findEndGroup returns the indexes of the start and end of the EndGroup tag.
+// Returns (-1,-1) if it can't find one.
+func findEndGroup(b []byte) (int, int) {
+	depth := 1
+	i := 0
+	for {
+		x, n := decodeVarint(b[i:])
+		if n == 0 {
+			return -1, -1
+		}
+		j := i
+		i += n
+		switch x & 7 {
+		case WireVarint:
+			_, k := decodeVarint(b[i:])
+			if k == 0 {
+				return -1, -1
+			}
+			i += k
+		case WireFixed32:
+			if len(b)-4 < i {
+				return -1, -1
+			}
+			i += 4
+		case WireFixed64:
+			if len(b)-8 < i {
+				return -1, -1
+			}
+			i += 8
+		case WireBytes:
+			m, k := decodeVarint(b[i:])
+			if k == 0 {
+				return -1, -1
+			}
+			i += k
+			if uint64(len(b)-i) < m {
+				return -1, -1
+			}
+			i += int(m)
+		case WireStartGroup:
+			depth++
+		case WireEndGroup:
+			depth--
+			if depth == 0 {
+				return j, i
+			}
+		default:
+			return -1, -1
+		}
+	}
+}
+
+// encodeVarint appends a varint-encoded integer to b and returns the result.
+func encodeVarint(b []byte, x uint64) []byte {
+	for x >= 1<<7 {
+		b = append(b, byte(x&0x7f|0x80))
+		x >>= 7
+	}
+	return append(b, byte(x))
+}
+
+// decodeVarint reads a varint-encoded integer from b.
+// Returns the decoded integer and the number of bytes read.
+// If there is an error, it returns 0,0.
+func decodeVarint(b []byte) (uint64, int) {
+	var x, y uint64
+	if len(b) <= 0 {
+		goto bad
+	}
+	x = uint64(b[0])
+	if x < 0x80 {
+		return x, 1
+	}
+	x -= 0x80
+
+	if len(b) <= 1 {
+		goto bad
+	}
+	y = uint64(b[1])
+	x += y << 7
+	if y < 0x80 {
+		return x, 2
+	}
+	x -= 0x80 << 7
+
+	if len(b) <= 2 {
+		goto bad
+	}
+	y = uint64(b[2])
+	x += y << 14
+	if y < 0x80 {
+		return x, 3
+	}
+	x -= 0x80 << 14
+
+	if len(b) <= 3 {
+		goto bad
+	}
+	y = uint64(b[3])
+	x += y << 21
+	if y < 0x80 {
+		return x, 4
+	}
+	x -= 0x80 << 21
+
+	if len(b) <= 4 {
+		goto bad
+	}
+	y = uint64(b[4])
+	x += y << 28
+	if y < 0x80 {
+		return x, 5
+	}
+	x -= 0x80 << 28
+
+	if len(b) <= 5 {
+		goto bad
+	}
+	y = uint64(b[5])
+	x += y << 35
+	if y < 0x80 {
+		return x, 6
+	}
+	x -= 0x80 << 35
+
+	if len(b) <= 6 {
+		goto bad
+	}
+	y = uint64(b[6])
+	x += y << 42
+	if y < 0x80 {
+		return x, 7
+	}
+	x -= 0x80 << 42
+
+	if len(b) <= 7 {
+		goto bad
+	}
+	y = uint64(b[7])
+	x += y << 49
+	if y < 0x80 {
+		return x, 8
+	}
+	x -= 0x80 << 49
+
+	if len(b) <= 8 {
+		goto bad
+	}
+	y = uint64(b[8])
+	x += y << 56
+	if y < 0x80 {
+		return x, 9
+	}
+	x -= 0x80 << 56
+
+	if len(b) <= 9 {
+		goto bad
+	}
+	y = uint64(b[9])
+	x += y << 63
+	if y < 2 {
+		return x, 10
+	}
+
+bad:
+	return 0, 0
+}
diff --git a/vendor/github.com/golang/protobuf/proto/text.go b/vendor/github.com/golang/protobuf/proto/text.go
new file mode 100644
index 0000000..2205fda
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/text.go
@@ -0,0 +1,843 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors.  All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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.
+
+package proto
+
+// Functions for writing the text protocol buffer format.
+
+import (
+	"bufio"
+	"bytes"
+	"encoding"
+	"errors"
+	"fmt"
+	"io"
+	"log"
+	"math"
+	"reflect"
+	"sort"
+	"strings"
+)
+
+var (
+	newline         = []byte("\n")
+	spaces          = []byte("                                        ")
+	endBraceNewline = []byte("}\n")
+	backslashN      = []byte{'\\', 'n'}
+	backslashR      = []byte{'\\', 'r'}
+	backslashT      = []byte{'\\', 't'}
+	backslashDQ     = []byte{'\\', '"'}
+	backslashBS     = []byte{'\\', '\\'}
+	posInf          = []byte("inf")
+	negInf          = []byte("-inf")
+	nan             = []byte("nan")
+)
+
+type writer interface {
+	io.Writer
+	WriteByte(byte) error
+}
+
+// textWriter is an io.Writer that tracks its indentation level.
+type textWriter struct {
+	ind      int
+	complete bool // if the current position is a complete line
+	compact  bool // whether to write out as a one-liner
+	w        writer
+}
+
+func (w *textWriter) WriteString(s string) (n int, err error) {
+	if !strings.Contains(s, "\n") {
+		if !w.compact && w.complete {
+			w.writeIndent()
+		}
+		w.complete = false
+		return io.WriteString(w.w, s)
+	}
+	// WriteString is typically called without newlines, so this
+	// codepath and its copy are rare.  We copy to avoid
+	// duplicating all of Write's logic here.
+	return w.Write([]byte(s))
+}
+
+func (w *textWriter) Write(p []byte) (n int, err error) {
+	newlines := bytes.Count(p, newline)
+	if newlines == 0 {
+		if !w.compact && w.complete {
+			w.writeIndent()
+		}
+		n, err = w.w.Write(p)
+		w.complete = false
+		return n, err
+	}
+
+	frags := bytes.SplitN(p, newline, newlines+1)
+	if w.compact {
+		for i, frag := range frags {
+			if i > 0 {
+				if err := w.w.WriteByte(' '); err != nil {
+					return n, err
+				}
+				n++
+			}
+			nn, err := w.w.Write(frag)
+			n += nn
+			if err != nil {
+				return n, err
+			}
+		}
+		return n, nil
+	}
+
+	for i, frag := range frags {
+		if w.complete {
+			w.writeIndent()
+		}
+		nn, err := w.w.Write(frag)
+		n += nn
+		if err != nil {
+			return n, err
+		}
+		if i+1 < len(frags) {
+			if err := w.w.WriteByte('\n'); err != nil {
+				return n, err
+			}
+			n++
+		}
+	}
+	w.complete = len(frags[len(frags)-1]) == 0
+	return n, nil
+}
+
+func (w *textWriter) WriteByte(c byte) error {
+	if w.compact && c == '\n' {
+		c = ' '
+	}
+	if !w.compact && w.complete {
+		w.writeIndent()
+	}
+	err := w.w.WriteByte(c)
+	w.complete = c == '\n'
+	return err
+}
+
+func (w *textWriter) indent() { w.ind++ }
+
+func (w *textWriter) unindent() {
+	if w.ind == 0 {
+		log.Print("proto: textWriter unindented too far")
+		return
+	}
+	w.ind--
+}
+
+func writeName(w *textWriter, props *Properties) error {
+	if _, err := w.WriteString(props.OrigName); err != nil {
+		return err
+	}
+	if props.Wire != "group" {
+		return w.WriteByte(':')
+	}
+	return nil
+}
+
+func requiresQuotes(u string) bool {
+	// When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted.
+	for _, ch := range u {
+		switch {
+		case ch == '.' || ch == '/' || ch == '_':
+			continue
+		case '0' <= ch && ch <= '9':
+			continue
+		case 'A' <= ch && ch <= 'Z':
+			continue
+		case 'a' <= ch && ch <= 'z':
+			continue
+		default:
+			return true
+		}
+	}
+	return false
+}
+
+// isAny reports whether sv is a google.protobuf.Any message
+func isAny(sv reflect.Value) bool {
+	type wkt interface {
+		XXX_WellKnownType() string
+	}
+	t, ok := sv.Addr().Interface().(wkt)
+	return ok && t.XXX_WellKnownType() == "Any"
+}
+
+// writeProto3Any writes an expanded google.protobuf.Any message.
+//
+// It returns (false, nil) if sv value can't be unmarshaled (e.g. because
+// required messages are not linked in).
+//
+// It returns (true, error) when sv was written in expanded format or an error
+// was encountered.
+func (tm *TextMarshaler) writeProto3Any(w *textWriter, sv reflect.Value) (bool, error) {
+	turl := sv.FieldByName("TypeUrl")
+	val := sv.FieldByName("Value")
+	if !turl.IsValid() || !val.IsValid() {
+		return true, errors.New("proto: invalid google.protobuf.Any message")
+	}
+
+	b, ok := val.Interface().([]byte)
+	if !ok {
+		return true, errors.New("proto: invalid google.protobuf.Any message")
+	}
+
+	parts := strings.Split(turl.String(), "/")
+	mt := MessageType(parts[len(parts)-1])
+	if mt == nil {
+		return false, nil
+	}
+	m := reflect.New(mt.Elem())
+	if err := Unmarshal(b, m.Interface().(Message)); err != nil {
+		return false, nil
+	}
+	w.Write([]byte("["))
+	u := turl.String()
+	if requiresQuotes(u) {
+		writeString(w, u)
+	} else {
+		w.Write([]byte(u))
+	}
+	if w.compact {
+		w.Write([]byte("]:<"))
+	} else {
+		w.Write([]byte("]: <\n"))
+		w.ind++
+	}
+	if err := tm.writeStruct(w, m.Elem()); err != nil {
+		return true, err
+	}
+	if w.compact {
+		w.Write([]byte("> "))
+	} else {
+		w.ind--
+		w.Write([]byte(">\n"))
+	}
+	return true, nil
+}
+
+func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
+	if tm.ExpandAny && isAny(sv) {
+		if canExpand, err := tm.writeProto3Any(w, sv); canExpand {
+			return err
+		}
+	}
+	st := sv.Type()
+	sprops := GetProperties(st)
+	for i := 0; i < sv.NumField(); i++ {
+		fv := sv.Field(i)
+		props := sprops.Prop[i]
+		name := st.Field(i).Name
+
+		if name == "XXX_NoUnkeyedLiteral" {
+			continue
+		}
+
+		if strings.HasPrefix(name, "XXX_") {
+			// There are two XXX_ fields:
+			//   XXX_unrecognized []byte
+			//   XXX_extensions   map[int32]proto.Extension
+			// The first is handled here;
+			// the second is handled at the bottom of this function.
+			if name == "XXX_unrecognized" && !fv.IsNil() {
+				if err := writeUnknownStruct(w, fv.Interface().([]byte)); err != nil {
+					return err
+				}
+			}
+			continue
+		}
+		if fv.Kind() == reflect.Ptr && fv.IsNil() {
+			// Field not filled in. This could be an optional field or
+			// a required field that wasn't filled in. Either way, there
+			// isn't anything we can show for it.
+			continue
+		}
+		if fv.Kind() == reflect.Slice && fv.IsNil() {
+			// Repeated field that is empty, or a bytes field that is unused.
+			continue
+		}
+
+		if props.Repeated && fv.Kind() == reflect.Slice {
+			// Repeated field.
+			for j := 0; j < fv.Len(); j++ {
+				if err := writeName(w, props); err != nil {
+					return err
+				}
+				if !w.compact {
+					if err := w.WriteByte(' '); err != nil {
+						return err
+					}
+				}
+				v := fv.Index(j)
+				if v.Kind() == reflect.Ptr && v.IsNil() {
+					// A nil message in a repeated field is not valid,
+					// but we can handle that more gracefully than panicking.
+					if _, err := w.Write([]byte("<nil>\n")); err != nil {
+						return err
+					}
+					continue
+				}
+				if err := tm.writeAny(w, v, props); err != nil {
+					return err
+				}
+				if err := w.WriteByte('\n'); err != nil {
+					return err
+				}
+			}
+			continue
+		}
+		if fv.Kind() == reflect.Map {
+			// Map fields are rendered as a repeated struct with key/value fields.
+			keys := fv.MapKeys()
+			sort.Sort(mapKeys(keys))
+			for _, key := range keys {
+				val := fv.MapIndex(key)
+				if err := writeName(w, props); err != nil {
+					return err
+				}
+				if !w.compact {
+					if err := w.WriteByte(' '); err != nil {
+						return err
+					}
+				}
+				// open struct
+				if err := w.WriteByte('<'); err != nil {
+					return err
+				}
+				if !w.compact {
+					if err := w.WriteByte('\n'); err != nil {
+						return err
+					}
+				}
+				w.indent()
+				// key
+				if _, err := w.WriteString("key:"); err != nil {
+					return err
+				}
+				if !w.compact {
+					if err := w.WriteByte(' '); err != nil {
+						return err
+					}
+				}
+				if err := tm.writeAny(w, key, props.mkeyprop); err != nil {
+					return err
+				}
+				if err := w.WriteByte('\n'); err != nil {
+					return err
+				}
+				// nil values aren't legal, but we can avoid panicking because of them.
+				if val.Kind() != reflect.Ptr || !val.IsNil() {
+					// value
+					if _, err := w.WriteString("value:"); err != nil {
+						return err
+					}
+					if !w.compact {
+						if err := w.WriteByte(' '); err != nil {
+							return err
+						}
+					}
+					if err := tm.writeAny(w, val, props.mvalprop); err != nil {
+						return err
+					}
+					if err := w.WriteByte('\n'); err != nil {
+						return err
+					}
+				}
+				// close struct
+				w.unindent()
+				if err := w.WriteByte('>'); err != nil {
+					return err
+				}
+				if err := w.WriteByte('\n'); err != nil {
+					return err
+				}
+			}
+			continue
+		}
+		if props.proto3 && fv.Kind() == reflect.Slice && fv.Len() == 0 {
+			// empty bytes field
+			continue
+		}
+		if fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice {
+			// proto3 non-repeated scalar field; skip if zero value
+			if isProto3Zero(fv) {
+				continue
+			}
+		}
+
+		if fv.Kind() == reflect.Interface {
+			// Check if it is a oneof.
+			if st.Field(i).Tag.Get("protobuf_oneof") != "" {
+				// fv is nil, or holds a pointer to generated struct.
+				// That generated struct has exactly one field,
+				// which has a protobuf struct tag.
+				if fv.IsNil() {
+					continue
+				}
+				inner := fv.Elem().Elem() // interface -> *T -> T
+				tag := inner.Type().Field(0).Tag.Get("protobuf")
+				props = new(Properties) // Overwrite the outer props var, but not its pointee.
+				props.Parse(tag)
+				// Write the value in the oneof, not the oneof itself.
+				fv = inner.Field(0)
+
+				// Special case to cope with malformed messages gracefully:
+				// If the value in the oneof is a nil pointer, don't panic
+				// in writeAny.
+				if fv.Kind() == reflect.Ptr && fv.IsNil() {
+					// Use errors.New so writeAny won't render quotes.
+					msg := errors.New("/* nil */")
+					fv = reflect.ValueOf(&msg).Elem()
+				}
+			}
+		}
+
+		if err := writeName(w, props); err != nil {
+			return err
+		}
+		if !w.compact {
+			if err := w.WriteByte(' '); err != nil {
+				return err
+			}
+		}
+
+		// Enums have a String method, so writeAny will work fine.
+		if err := tm.writeAny(w, fv, props); err != nil {
+			return err
+		}
+
+		if err := w.WriteByte('\n'); err != nil {
+			return err
+		}
+	}
+
+	// Extensions (the XXX_extensions field).
+	pv := sv.Addr()
+	if _, err := extendable(pv.Interface()); err == nil {
+		if err := tm.writeExtensions(w, pv); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// writeAny writes an arbitrary field.
+func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
+	v = reflect.Indirect(v)
+
+	// Floats have special cases.
+	if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 {
+		x := v.Float()
+		var b []byte
+		switch {
+		case math.IsInf(x, 1):
+			b = posInf
+		case math.IsInf(x, -1):
+			b = negInf
+		case math.IsNaN(x):
+			b = nan
+		}
+		if b != nil {
+			_, err := w.Write(b)
+			return err
+		}
+		// Other values are handled below.
+	}
+
+	// We don't attempt to serialise every possible value type; only those
+	// that can occur in protocol buffers.
+	switch v.Kind() {
+	case reflect.Slice:
+		// Should only be a []byte; repeated fields are handled in writeStruct.
+		if err := writeString(w, string(v.Bytes())); err != nil {
+			return err
+		}
+	case reflect.String:
+		if err := writeString(w, v.String()); err != nil {
+			return err
+		}
+	case reflect.Struct:
+		// Required/optional group/message.
+		var bra, ket byte = '<', '>'
+		if props != nil && props.Wire == "group" {
+			bra, ket = '{', '}'
+		}
+		if err := w.WriteByte(bra); err != nil {
+			return err
+		}
+		if !w.compact {
+			if err := w.WriteByte('\n'); err != nil {
+				return err
+			}
+		}
+		w.indent()
+		if v.CanAddr() {
+			// Calling v.Interface on a struct causes the reflect package to
+			// copy the entire struct. This is racy with the new Marshaler
+			// since we atomically update the XXX_sizecache.
+			//
+			// Thus, we retrieve a pointer to the struct if possible to avoid
+			// a race since v.Interface on the pointer doesn't copy the struct.
+			//
+			// If v is not addressable, then we are not worried about a race
+			// since it implies that the binary Marshaler cannot possibly be
+			// mutating this value.
+			v = v.Addr()
+		}
+		if etm, ok := v.Interface().(encoding.TextMarshaler); ok {
+			text, err := etm.MarshalText()
+			if err != nil {
+				return err
+			}
+			if _, err = w.Write(text); err != nil {
+				return err
+			}
+		} else {
+			if v.Kind() == reflect.Ptr {
+				v = v.Elem()
+			}
+			if err := tm.writeStruct(w, v); err != nil {
+				return err
+			}
+		}
+		w.unindent()
+		if err := w.WriteByte(ket); err != nil {
+			return err
+		}
+	default:
+		_, err := fmt.Fprint(w, v.Interface())
+		return err
+	}
+	return nil
+}
+
+// equivalent to C's isprint.
+func isprint(c byte) bool {
+	return c >= 0x20 && c < 0x7f
+}
+
+// writeString writes a string in the protocol buffer text format.
+// It is similar to strconv.Quote except we don't use Go escape sequences,
+// we treat the string as a byte sequence, and we use octal escapes.
+// These differences are to maintain interoperability with the other
+// languages' implementations of the text format.
+func writeString(w *textWriter, s string) error {
+	// use WriteByte here to get any needed indent
+	if err := w.WriteByte('"'); err != nil {
+		return err
+	}
+	// Loop over the bytes, not the runes.
+	for i := 0; i < len(s); i++ {
+		var err error
+		// Divergence from C++: we don't escape apostrophes.
+		// There's no need to escape them, and the C++ parser
+		// copes with a naked apostrophe.
+		switch c := s[i]; c {
+		case '\n':
+			_, err = w.w.Write(backslashN)
+		case '\r':
+			_, err = w.w.Write(backslashR)
+		case '\t':
+			_, err = w.w.Write(backslashT)
+		case '"':
+			_, err = w.w.Write(backslashDQ)
+		case '\\':
+			_, err = w.w.Write(backslashBS)
+		default:
+			if isprint(c) {
+				err = w.w.WriteByte(c)
+			} else {
+				_, err = fmt.Fprintf(w.w, "\\%03o", c)
+			}
+		}
+		if err != nil {
+			return err
+		}
+	}
+	return w.WriteByte('"')
+}
+
+func writeUnknownStruct(w *textWriter, data []byte) (err error) {
+	if !w.compact {
+		if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil {
+			return err
+		}
+	}
+	b := NewBuffer(data)
+	for b.index < len(b.buf) {
+		x, err := b.DecodeVarint()
+		if err != nil {
+			_, err := fmt.Fprintf(w, "/* %v */\n", err)
+			return err
+		}
+		wire, tag := x&7, x>>3
+		if wire == WireEndGroup {
+			w.unindent()
+			if _, err := w.Write(endBraceNewline); err != nil {
+				return err
+			}
+			continue
+		}
+		if _, err := fmt.Fprint(w, tag); err != nil {
+			return err
+		}
+		if wire != WireStartGroup {
+			if err := w.WriteByte(':'); err != nil {
+				return err
+			}
+		}
+		if !w.compact || wire == WireStartGroup {
+			if err := w.WriteByte(' '); err != nil {
+				return err
+			}
+		}
+		switch wire {
+		case WireBytes:
+			buf, e := b.DecodeRawBytes(false)
+			if e == nil {
+				_, err = fmt.Fprintf(w, "%q", buf)
+			} else {
+				_, err = fmt.Fprintf(w, "/* %v */", e)
+			}
+		case WireFixed32:
+			x, err = b.DecodeFixed32()
+			err = writeUnknownInt(w, x, err)
+		case WireFixed64:
+			x, err = b.DecodeFixed64()
+			err = writeUnknownInt(w, x, err)
+		case WireStartGroup:
+			err = w.WriteByte('{')
+			w.indent()
+		case WireVarint:
+			x, err = b.DecodeVarint()
+			err = writeUnknownInt(w, x, err)
+		default:
+			_, err = fmt.Fprintf(w, "/* unknown wire type %d */", wire)
+		}
+		if err != nil {
+			return err
+		}
+		if err = w.WriteByte('\n'); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func writeUnknownInt(w *textWriter, x uint64, err error) error {
+	if err == nil {
+		_, err = fmt.Fprint(w, x)
+	} else {
+		_, err = fmt.Fprintf(w, "/* %v */", err)
+	}
+	return err
+}
+
+type int32Slice []int32
+
+func (s int32Slice) Len() int           { return len(s) }
+func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] }
+func (s int32Slice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
+
+// writeExtensions writes all the extensions in pv.
+// pv is assumed to be a pointer to a protocol message struct that is extendable.
+func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error {
+	emap := extensionMaps[pv.Type().Elem()]
+	ep, _ := extendable(pv.Interface())
+
+	// Order the extensions by ID.
+	// This isn't strictly necessary, but it will give us
+	// canonical output, which will also make testing easier.
+	m, mu := ep.extensionsRead()
+	if m == nil {
+		return nil
+	}
+	mu.Lock()
+	ids := make([]int32, 0, len(m))
+	for id := range m {
+		ids = append(ids, id)
+	}
+	sort.Sort(int32Slice(ids))
+	mu.Unlock()
+
+	for _, extNum := range ids {
+		ext := m[extNum]
+		var desc *ExtensionDesc
+		if emap != nil {
+			desc = emap[extNum]
+		}
+		if desc == nil {
+			// Unknown extension.
+			if err := writeUnknownStruct(w, ext.enc); err != nil {
+				return err
+			}
+			continue
+		}
+
+		pb, err := GetExtension(ep, desc)
+		if err != nil {
+			return fmt.Errorf("failed getting extension: %v", err)
+		}
+
+		// Repeated extensions will appear as a slice.
+		if !desc.repeated() {
+			if err := tm.writeExtension(w, desc.Name, pb); err != nil {
+				return err
+			}
+		} else {
+			v := reflect.ValueOf(pb)
+			for i := 0; i < v.Len(); i++ {
+				if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
+					return err
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error {
+	if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil {
+		return err
+	}
+	if !w.compact {
+		if err := w.WriteByte(' '); err != nil {
+			return err
+		}
+	}
+	if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil {
+		return err
+	}
+	if err := w.WriteByte('\n'); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (w *textWriter) writeIndent() {
+	if !w.complete {
+		return
+	}
+	remain := w.ind * 2
+	for remain > 0 {
+		n := remain
+		if n > len(spaces) {
+			n = len(spaces)
+		}
+		w.w.Write(spaces[:n])
+		remain -= n
+	}
+	w.complete = false
+}
+
+// TextMarshaler is a configurable text format marshaler.
+type TextMarshaler struct {
+	Compact   bool // use compact text format (one line).
+	ExpandAny bool // expand google.protobuf.Any messages of known types
+}
+
+// Marshal writes a given protocol buffer in text format.
+// The only errors returned are from w.
+func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error {
+	val := reflect.ValueOf(pb)
+	if pb == nil || val.IsNil() {
+		w.Write([]byte("<nil>"))
+		return nil
+	}
+	var bw *bufio.Writer
+	ww, ok := w.(writer)
+	if !ok {
+		bw = bufio.NewWriter(w)
+		ww = bw
+	}
+	aw := &textWriter{
+		w:        ww,
+		complete: true,
+		compact:  tm.Compact,
+	}
+
+	if etm, ok := pb.(encoding.TextMarshaler); ok {
+		text, err := etm.MarshalText()
+		if err != nil {
+			return err
+		}
+		if _, err = aw.Write(text); err != nil {
+			return err
+		}
+		if bw != nil {
+			return bw.Flush()
+		}
+		return nil
+	}
+	// Dereference the received pointer so we don't have outer < and >.
+	v := reflect.Indirect(val)
+	if err := tm.writeStruct(aw, v); err != nil {
+		return err
+	}
+	if bw != nil {
+		return bw.Flush()
+	}
+	return nil
+}
+
+// Text is the same as Marshal, but returns the string directly.
+func (tm *TextMarshaler) Text(pb Message) string {
+	var buf bytes.Buffer
+	tm.Marshal(&buf, pb)
+	return buf.String()
+}
+
+var (
+	defaultTextMarshaler = TextMarshaler{}
+	compactTextMarshaler = TextMarshaler{Compact: true}
+)
+
+// TODO: consider removing some of the Marshal functions below.
+
+// MarshalText writes a given protocol buffer in text format.
+// The only errors returned are from w.
+func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) }
+
+// MarshalTextString is the same as MarshalText, but returns the string directly.
+func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) }
+
+// CompactText writes a given protocol buffer in compact text format (one line).
+func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) }
+
+// CompactTextString is the same as CompactText, but returns the string directly.
+func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) }
diff --git a/vendor/github.com/golang/protobuf/proto/text_parser.go b/vendor/github.com/golang/protobuf/proto/text_parser.go
new file mode 100644
index 0000000..0685bae
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/proto/text_parser.go
@@ -0,0 +1,880 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors.  All rights reserved.
+// https://github.com/golang/protobuf
+//
+// 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.
+
+package proto
+
+// Functions for parsing the Text protocol buffer format.
+// TODO: message sets.
+
+import (
+	"encoding"
+	"errors"
+	"fmt"
+	"reflect"
+	"strconv"
+	"strings"
+	"unicode/utf8"
+)
+
+// Error string emitted when deserializing Any and fields are already set
+const anyRepeatedlyUnpacked = "Any message unpacked multiple times, or %q already set"
+
+type ParseError struct {
+	Message string
+	Line    int // 1-based line number
+	Offset  int // 0-based byte offset from start of input
+}
+
+func (p *ParseError) Error() string {
+	if p.Line == 1 {
+		// show offset only for first line
+		return fmt.Sprintf("line 1.%d: %v", p.Offset, p.Message)
+	}
+	return fmt.Sprintf("line %d: %v", p.Line, p.Message)
+}
+
+type token struct {
+	value    string
+	err      *ParseError
+	line     int    // line number
+	offset   int    // byte number from start of input, not start of line
+	unquoted string // the unquoted version of value, if it was a quoted string
+}
+
+func (t *token) String() string {
+	if t.err == nil {
+		return fmt.Sprintf("%q (line=%d, offset=%d)", t.value, t.line, t.offset)
+	}
+	return fmt.Sprintf("parse error: %v", t.err)
+}
+
+type textParser struct {
+	s            string // remaining input
+	done         bool   // whether the parsing is finished (success or error)
+	backed       bool   // whether back() was called
+	offset, line int
+	cur          token
+}
+
+func newTextParser(s string) *textParser {
+	p := new(textParser)
+	p.s = s
+	p.line = 1
+	p.cur.line = 1
+	return p
+}
+
+func (p *textParser) errorf(format string, a ...interface{}) *ParseError {
+	pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset}
+	p.cur.err = pe
+	p.done = true
+	return pe
+}
+
+// Numbers and identifiers are matched by [-+._A-Za-z0-9]
+func isIdentOrNumberChar(c byte) bool {
+	switch {
+	case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z':
+		return true
+	case '0' <= c && c <= '9':
+		return true
+	}
+	switch c {
+	case '-', '+', '.', '_':
+		return true
+	}
+	return false
+}
+
+func isWhitespace(c byte) bool {
+	switch c {
+	case ' ', '\t', '\n', '\r':
+		return true
+	}
+	return false
+}
+
+func isQuote(c byte) bool {
+	switch c {
+	case '"', '\'':
+		return true
+	}
+	return false
+}
+
+func (p *textParser) skipWhitespace() {
+	i := 0
+	for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') {
+		if p.s[i] == '#' {
+			// comment; skip to end of line or input
+			for i < len(p.s) && p.s[i] != '\n' {
+				i++
+			}
+			if i == len(p.s) {
+				break
+			}
+		}
+		if p.s[i] == '\n' {
+			p.line++
+		}
+		i++
+	}
+	p.offset += i
+	p.s = p.s[i:len(p.s)]
+	if len(p.s) == 0 {
+		p.done = true
+	}
+}
+
+func (p *textParser) advance() {
+	// Skip whitespace
+	p.skipWhitespace()
+	if p.done {
+		return
+	}
+
+	// Start of non-whitespace
+	p.cur.err = nil
+	p.cur.offset, p.cur.line = p.offset, p.line
+	p.cur.unquoted = ""
+	switch p.s[0] {
+	case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/':
+		// Single symbol
+		p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)]
+	case '"', '\'':
+		// Quoted string
+		i := 1
+		for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' {
+			if p.s[i] == '\\' && i+1 < len(p.s) {
+				// skip escaped char
+				i++
+			}
+			i++
+		}
+		if i >= len(p.s) || p.s[i] != p.s[0] {
+			p.errorf("unmatched quote")
+			return
+		}
+		unq, err := unquoteC(p.s[1:i], rune(p.s[0]))
+		if err != nil {
+			p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err)
+			return
+		}
+		p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)]
+		p.cur.unquoted = unq
+	default:
+		i := 0
+		for i < len(p.s) && isIdentOrNumberChar(p.s[i]) {
+			i++
+		}
+		if i == 0 {
+			p.errorf("unexpected byte %#x", p.s[0])
+			return
+		}
+		p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)]
+	}
+	p.offset += len(p.cur.value)
+}
+
+var (
+	errBadUTF8 = errors.New("proto: bad UTF-8")
+)
+
+func unquoteC(s string, quote rune) (string, error) {
+	// This is based on C++'s tokenizer.cc.
+	// Despite its name, this is *not* parsing C syntax.
+	// For instance, "\0" is an invalid quoted string.
+
+	// Avoid allocation in trivial cases.
+	simple := true
+	for _, r := range s {
+		if r == '\\' || r == quote {
+			simple = false
+			break
+		}
+	}
+	if simple {
+		return s, nil
+	}
+
+	buf := make([]byte, 0, 3*len(s)/2)
+	for len(s) > 0 {
+		r, n := utf8.DecodeRuneInString(s)
+		if r == utf8.RuneError && n == 1 {
+			return "", errBadUTF8
+		}
+		s = s[n:]
+		if r != '\\' {
+			if r < utf8.RuneSelf {
+				buf = append(buf, byte(r))
+			} else {
+				buf = append(buf, string(r)...)
+			}
+			continue
+		}
+
+		ch, tail, err := unescape(s)
+		if err != nil {
+			return "", err
+		}
+		buf = append(buf, ch...)
+		s = tail
+	}
+	return string(buf), nil
+}
+
+func unescape(s string) (ch string, tail string, err error) {
+	r, n := utf8.DecodeRuneInString(s)
+	if r == utf8.RuneError && n == 1 {
+		return "", "", errBadUTF8
+	}
+	s = s[n:]
+	switch r {
+	case 'a':
+		return "\a", s, nil
+	case 'b':
+		return "\b", s, nil
+	case 'f':
+		return "\f", s, nil
+	case 'n':
+		return "\n", s, nil
+	case 'r':
+		return "\r", s, nil
+	case 't':
+		return "\t", s, nil
+	case 'v':
+		return "\v", s, nil
+	case '?':
+		return "?", s, nil // trigraph workaround
+	case '\'', '"', '\\':
+		return string(r), s, nil
+	case '0', '1', '2', '3', '4', '5', '6', '7':
+		if len(s) < 2 {
+			return "", "", fmt.Errorf(`\%c requires 2 following digits`, r)
+		}
+		ss := string(r) + s[:2]
+		s = s[2:]
+		i, err := strconv.ParseUint(ss, 8, 8)
+		if err != nil {
+			return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss)
+		}
+		return string([]byte{byte(i)}), s, nil
+	case 'x', 'X', 'u', 'U':
+		var n int
+		switch r {
+		case 'x', 'X':
+			n = 2
+		case 'u':
+			n = 4
+		case 'U':
+			n = 8
+		}
+		if len(s) < n {
+			return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n)
+		}
+		ss := s[:n]
+		s = s[n:]
+		i, err := strconv.ParseUint(ss, 16, 64)
+		if err != nil {
+			return "", "", fmt.Errorf(`\%c%s contains non-hexadecimal digits`, r, ss)
+		}
+		if r == 'x' || r == 'X' {
+			return string([]byte{byte(i)}), s, nil
+		}
+		if i > utf8.MaxRune {
+			return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss)
+		}
+		return string(i), s, nil
+	}
+	return "", "", fmt.Errorf(`unknown escape \%c`, r)
+}
+
+// Back off the parser by one token. Can only be done between calls to next().
+// It makes the next advance() a no-op.
+func (p *textParser) back() { p.backed = true }
+
+// Advances the parser and returns the new current token.
+func (p *textParser) next() *token {
+	if p.backed || p.done {
+		p.backed = false
+		return &p.cur
+	}
+	p.advance()
+	if p.done {
+		p.cur.value = ""
+	} else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) {
+		// Look for multiple quoted strings separated by whitespace,
+		// and concatenate them.
+		cat := p.cur
+		for {
+			p.skipWhitespace()
+			if p.done || !isQuote(p.s[0]) {
+				break
+			}
+			p.advance()
+			if p.cur.err != nil {
+				return &p.cur
+			}
+			cat.value += " " + p.cur.value
+			cat.unquoted += p.cur.unquoted
+		}
+		p.done = false // parser may have seen EOF, but we want to return cat
+		p.cur = cat
+	}
+	return &p.cur
+}
+
+func (p *textParser) consumeToken(s string) error {
+	tok := p.next()
+	if tok.err != nil {
+		return tok.err
+	}
+	if tok.value != s {
+		p.back()
+		return p.errorf("expected %q, found %q", s, tok.value)
+	}
+	return nil
+}
+
+// Return a RequiredNotSetError indicating which required field was not set.
+func (p *textParser) missingRequiredFieldError(sv reflect.Value) *RequiredNotSetError {
+	st := sv.Type()
+	sprops := GetProperties(st)
+	for i := 0; i < st.NumField(); i++ {
+		if !isNil(sv.Field(i)) {
+			continue
+		}
+
+		props := sprops.Prop[i]
+		if props.Required {
+			return &RequiredNotSetError{fmt.Sprintf("%v.%v", st, props.OrigName)}
+		}
+	}
+	return &RequiredNotSetError{fmt.Sprintf("%v.<unknown field name>", st)} // should not happen
+}
+
+// Returns the index in the struct for the named field, as well as the parsed tag properties.
+func structFieldByName(sprops *StructProperties, name string) (int, *Properties, bool) {
+	i, ok := sprops.decoderOrigNames[name]
+	if ok {
+		return i, sprops.Prop[i], true
+	}
+	return -1, nil, false
+}
+
+// Consume a ':' from the input stream (if the next token is a colon),
+// returning an error if a colon is needed but not present.
+func (p *textParser) checkForColon(props *Properties, typ reflect.Type) *ParseError {
+	tok := p.next()
+	if tok.err != nil {
+		return tok.err
+	}
+	if tok.value != ":" {
+		// Colon is optional when the field is a group or message.
+		needColon := true
+		switch props.Wire {
+		case "group":
+			needColon = false
+		case "bytes":
+			// A "bytes" field is either a message, a string, or a repeated field;
+			// those three become *T, *string and []T respectively, so we can check for
+			// this field being a pointer to a non-string.
+			if typ.Kind() == reflect.Ptr {
+				// *T or *string
+				if typ.Elem().Kind() == reflect.String {
+					break
+				}
+			} else if typ.Kind() == reflect.Slice {
+				// []T or []*T
+				if typ.Elem().Kind() != reflect.Ptr {
+					break
+				}
+			} else if typ.Kind() == reflect.String {
+				// The proto3 exception is for a string field,
+				// which requires a colon.
+				break
+			}
+			needColon = false
+		}
+		if needColon {
+			return p.errorf("expected ':', found %q", tok.value)
+		}
+		p.back()
+	}
+	return nil
+}
+
+func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
+	st := sv.Type()
+	sprops := GetProperties(st)
+	reqCount := sprops.reqCount
+	var reqFieldErr error
+	fieldSet := make(map[string]bool)
+	// A struct is a sequence of "name: value", terminated by one of
+	// '>' or '}', or the end of the input.  A name may also be
+	// "[extension]" or "[type/url]".
+	//
+	// The whole struct can also be an expanded Any message, like:
+	// [type/url] < ... struct contents ... >
+	for {
+		tok := p.next()
+		if tok.err != nil {
+			return tok.err
+		}
+		if tok.value == terminator {
+			break
+		}
+		if tok.value == "[" {
+			// Looks like an extension or an Any.
+			//
+			// TODO: Check whether we need to handle
+			// namespace rooted names (e.g. ".something.Foo").
+			extName, err := p.consumeExtName()
+			if err != nil {
+				return err
+			}
+
+			if s := strings.LastIndex(extName, "/"); s >= 0 {
+				// If it contains a slash, it's an Any type URL.
+				messageName := extName[s+1:]
+				mt := MessageType(messageName)
+				if mt == nil {
+					return p.errorf("unrecognized message %q in google.protobuf.Any", messageName)
+				}
+				tok = p.next()
+				if tok.err != nil {
+					return tok.err
+				}
+				// consume an optional colon
+				if tok.value == ":" {
+					tok = p.next()
+					if tok.err != nil {
+						return tok.err
+					}
+				}
+				var terminator string
+				switch tok.value {
+				case "<":
+					terminator = ">"
+				case "{":
+					terminator = "}"
+				default:
+					return p.errorf("expected '{' or '<', found %q", tok.value)
+				}
+				v := reflect.New(mt.Elem())
+				if pe := p.readStruct(v.Elem(), terminator); pe != nil {
+					return pe
+				}
+				b, err := Marshal(v.Interface().(Message))
+				if err != nil {
+					return p.errorf("failed to marshal message of type %q: %v", messageName, err)
+				}
+				if fieldSet["type_url"] {
+					return p.errorf(anyRepeatedlyUnpacked, "type_url")
+				}
+				if fieldSet["value"] {
+					return p.errorf(anyRepeatedlyUnpacked, "value")
+				}
+				sv.FieldByName("TypeUrl").SetString(extName)
+				sv.FieldByName("Value").SetBytes(b)
+				fieldSet["type_url"] = true
+				fieldSet["value"] = true
+				continue
+			}
+
+			var desc *ExtensionDesc
+			// This could be faster, but it's functional.
+			// TODO: Do something smarter than a linear scan.
+			for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) {
+				if d.Name == extName {
+					desc = d
+					break
+				}
+			}
+			if desc == nil {
+				return p.errorf("unrecognized extension %q", extName)
+			}
+
+			props := &Properties{}
+			props.Parse(desc.Tag)
+
+			typ := reflect.TypeOf(desc.ExtensionType)
+			if err := p.checkForColon(props, typ); err != nil {
+				return err
+			}
+
+			rep := desc.repeated()
+
+			// Read the extension structure, and set it in
+			// the value we're constructing.
+			var ext reflect.Value
+			if !rep {
+				ext = reflect.New(typ).Elem()
+			} else {
+				ext = reflect.New(typ.Elem()).Elem()
+			}
+			if err := p.readAny(ext, props); err != nil {
+				if _, ok := err.(*RequiredNotSetError); !ok {
+					return err
+				}
+				reqFieldErr = err
+			}
+			ep := sv.Addr().Interface().(Message)
+			if !rep {
+				SetExtension(ep, desc, ext.Interface())
+			} else {
+				old, err := GetExtension(ep, desc)
+				var sl reflect.Value
+				if err == nil {
+					sl = reflect.ValueOf(old) // existing slice
+				} else {
+					sl = reflect.MakeSlice(typ, 0, 1)
+				}
+				sl = reflect.Append(sl, ext)
+				SetExtension(ep, desc, sl.Interface())
+			}
+			if err := p.consumeOptionalSeparator(); err != nil {
+				return err
+			}
+			continue
+		}
+
+		// This is a normal, non-extension field.
+		name := tok.value
+		var dst reflect.Value
+		fi, props, ok := structFieldByName(sprops, name)
+		if ok {
+			dst = sv.Field(fi)
+		} else if oop, ok := sprops.OneofTypes[name]; ok {
+			// It is a oneof.
+			props = oop.Prop
+			nv := reflect.New(oop.Type.Elem())
+			dst = nv.Elem().Field(0)
+			field := sv.Field(oop.Field)
+			if !field.IsNil() {
+				return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, sv.Type().Field(oop.Field).Name)
+			}
+			field.Set(nv)
+		}
+		if !dst.IsValid() {
+			return p.errorf("unknown field name %q in %v", name, st)
+		}
+
+		if dst.Kind() == reflect.Map {
+			// Consume any colon.
+			if err := p.checkForColon(props, dst.Type()); err != nil {
+				return err
+			}
+
+			// Construct the map if it doesn't already exist.
+			if dst.IsNil() {
+				dst.Set(reflect.MakeMap(dst.Type()))
+			}
+			key := reflect.New(dst.Type().Key()).Elem()
+			val := reflect.New(dst.Type().Elem()).Elem()
+
+			// The map entry should be this sequence of tokens:
+			//	< key : KEY value : VALUE >
+			// However, implementations may omit key or value, and technically
+			// we should support them in any order.  See b/28924776 for a time
+			// this went wrong.
+
+			tok := p.next()
+			var terminator string
+			switch tok.value {
+			case "<":
+				terminator = ">"
+			case "{":
+				terminator = "}"
+			default:
+				return p.errorf("expected '{' or '<', found %q", tok.value)
+			}
+			for {
+				tok := p.next()
+				if tok.err != nil {
+					return tok.err
+				}
+				if tok.value == terminator {
+					break
+				}
+				switch tok.value {
+				case "key":
+					if err := p.consumeToken(":"); err != nil {
+						return err
+					}
+					if err := p.readAny(key, props.mkeyprop); err != nil {
+						return err
+					}
+					if err := p.consumeOptionalSeparator(); err != nil {
+						return err
+					}
+				case "value":
+					if err := p.checkForColon(props.mvalprop, dst.Type().Elem()); err != nil {
+						return err
+					}
+					if err := p.readAny(val, props.mvalprop); err != nil {
+						return err
+					}
+					if err := p.consumeOptionalSeparator(); err != nil {
+						return err
+					}
+				default:
+					p.back()
+					return p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value)
+				}
+			}
+
+			dst.SetMapIndex(key, val)
+			continue
+		}
+
+		// Check that it's not already set if it's not a repeated field.
+		if !props.Repeated && fieldSet[name] {
+			return p.errorf("non-repeated field %q was repeated", name)
+		}
+
+		if err := p.checkForColon(props, dst.Type()); err != nil {
+			return err
+		}
+
+		// Parse into the field.
+		fieldSet[name] = true
+		if err := p.readAny(dst, props); err != nil {
+			if _, ok := err.(*RequiredNotSetError); !ok {
+				return err
+			}
+			reqFieldErr = err
+		}
+		if props.Required {
+			reqCount--
+		}
+
+		if err := p.consumeOptionalSeparator(); err != nil {
+			return err
+		}
+
+	}
+
+	if reqCount > 0 {
+		return p.missingRequiredFieldError(sv)
+	}
+	return reqFieldErr
+}
+
+// consumeExtName consumes extension name or expanded Any type URL and the
+// following ']'. It returns the name or URL consumed.
+func (p *textParser) consumeExtName() (string, error) {
+	tok := p.next()
+	if tok.err != nil {
+		return "", tok.err
+	}
+
+	// If extension name or type url is quoted, it's a single token.
+	if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] {
+		name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0]))
+		if err != nil {
+			return "", err
+		}
+		return name, p.consumeToken("]")
+	}
+
+	// Consume everything up to "]"
+	var parts []string
+	for tok.value != "]" {
+		parts = append(parts, tok.value)
+		tok = p.next()
+		if tok.err != nil {
+			return "", p.errorf("unrecognized type_url or extension name: %s", tok.err)
+		}
+		if p.done && tok.value != "]" {
+			return "", p.errorf("unclosed type_url or extension name")
+		}
+	}
+	return strings.Join(parts, ""), nil
+}
+
+// consumeOptionalSeparator consumes an optional semicolon or comma.
+// It is used in readStruct to provide backward compatibility.
+func (p *textParser) consumeOptionalSeparator() error {
+	tok := p.next()
+	if tok.err != nil {
+		return tok.err
+	}
+	if tok.value != ";" && tok.value != "," {
+		p.back()
+	}
+	return nil
+}
+
+func (p *textParser) readAny(v reflect.Value, props *Properties) error {
+	tok := p.next()
+	if tok.err != nil {
+		return tok.err
+	}
+	if tok.value == "" {
+		return p.errorf("unexpected EOF")
+	}
+
+	switch fv := v; fv.Kind() {
+	case reflect.Slice:
+		at := v.Type()
+		if at.Elem().Kind() == reflect.Uint8 {
+			// Special case for []byte
+			if tok.value[0] != '"' && tok.value[0] != '\'' {
+				// Deliberately written out here, as the error after
+				// this switch statement would write "invalid []byte: ...",
+				// which is not as user-friendly.
+				return p.errorf("invalid string: %v", tok.value)
+			}
+			bytes := []byte(tok.unquoted)
+			fv.Set(reflect.ValueOf(bytes))
+			return nil
+		}
+		// Repeated field.
+		if tok.value == "[" {
+			// Repeated field with list notation, like [1,2,3].
+			for {
+				fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem()))
+				err := p.readAny(fv.Index(fv.Len()-1), props)
+				if err != nil {
+					return err
+				}
+				tok := p.next()
+				if tok.err != nil {
+					return tok.err
+				}
+				if tok.value == "]" {
+					break
+				}
+				if tok.value != "," {
+					return p.errorf("Expected ']' or ',' found %q", tok.value)
+				}
+			}
+			return nil
+		}
+		// One value of the repeated field.
+		p.back()
+		fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem()))
+		return p.readAny(fv.Index(fv.Len()-1), props)
+	case reflect.Bool:
+		// true/1/t/True or false/f/0/False.
+		switch tok.value {
+		case "true", "1", "t", "True":
+			fv.SetBool(true)
+			return nil
+		case "false", "0", "f", "False":
+			fv.SetBool(false)
+			return nil
+		}
+	case reflect.Float32, reflect.Float64:
+		v := tok.value
+		// Ignore 'f' for compatibility with output generated by C++, but don't
+		// remove 'f' when the value is "-inf" or "inf".
+		if strings.HasSuffix(v, "f") && tok.value != "-inf" && tok.value != "inf" {
+			v = v[:len(v)-1]
+		}
+		if f, err := strconv.ParseFloat(v, fv.Type().Bits()); err == nil {
+			fv.SetFloat(f)
+			return nil
+		}
+	case reflect.Int32:
+		if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
+			fv.SetInt(x)
+			return nil
+		}
+
+		if len(props.Enum) == 0 {
+			break
+		}
+		m, ok := enumValueMaps[props.Enum]
+		if !ok {
+			break
+		}
+		x, ok := m[tok.value]
+		if !ok {
+			break
+		}
+		fv.SetInt(int64(x))
+		return nil
+	case reflect.Int64:
+		if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil {
+			fv.SetInt(x)
+			return nil
+		}
+
+	case reflect.Ptr:
+		// A basic field (indirected through pointer), or a repeated message/group
+		p.back()
+		fv.Set(reflect.New(fv.Type().Elem()))
+		return p.readAny(fv.Elem(), props)
+	case reflect.String:
+		if tok.value[0] == '"' || tok.value[0] == '\'' {
+			fv.SetString(tok.unquoted)
+			return nil
+		}
+	case reflect.Struct:
+		var terminator string
+		switch tok.value {
+		case "{":
+			terminator = "}"
+		case "<":
+			terminator = ">"
+		default:
+			return p.errorf("expected '{' or '<', found %q", tok.value)
+		}
+		// TODO: Handle nested messages which implement encoding.TextUnmarshaler.
+		return p.readStruct(fv, terminator)
+	case reflect.Uint32:
+		if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
+			fv.SetUint(uint64(x))
+			return nil
+		}
+	case reflect.Uint64:
+		if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
+			fv.SetUint(x)
+			return nil
+		}
+	}
+	return p.errorf("invalid %v: %v", v.Type(), tok.value)
+}
+
+// UnmarshalText reads a protocol buffer in Text format. UnmarshalText resets pb
+// before starting to unmarshal, so any existing data in pb is always removed.
+// If a required field is not set and no other error occurs,
+// UnmarshalText returns *RequiredNotSetError.
+func UnmarshalText(s string, pb Message) error {
+	if um, ok := pb.(encoding.TextUnmarshaler); ok {
+		return um.UnmarshalText([]byte(s))
+	}
+	pb.Reset()
+	v := reflect.ValueOf(pb)
+	return newTextParser(s).readStruct(v.Elem(), "")
+}
diff --git a/vendor/github.com/google/go-querystring/LICENSE b/vendor/github.com/google/go-querystring/LICENSE
new file mode 100644
index 0000000..ae121a1
--- /dev/null
+++ b/vendor/github.com/google/go-querystring/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2013 Google. 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/google/go-querystring/query/encode.go b/vendor/github.com/google/go-querystring/query/encode.go
new file mode 100644
index 0000000..37080b1
--- /dev/null
+++ b/vendor/github.com/google/go-querystring/query/encode.go
@@ -0,0 +1,320 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package query implements encoding of structs into URL query parameters.
+//
+// As a simple example:
+//
+// 	type Options struct {
+// 		Query   string `url:"q"`
+// 		ShowAll bool   `url:"all"`
+// 		Page    int    `url:"page"`
+// 	}
+//
+// 	opt := Options{ "foo", true, 2 }
+// 	v, _ := query.Values(opt)
+// 	fmt.Print(v.Encode()) // will output: "q=foo&all=true&page=2"
+//
+// The exact mapping between Go values and url.Values is described in the
+// documentation for the Values() function.
+package query
+
+import (
+	"bytes"
+	"fmt"
+	"net/url"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+var timeType = reflect.TypeOf(time.Time{})
+
+var encoderType = reflect.TypeOf(new(Encoder)).Elem()
+
+// Encoder is an interface implemented by any type that wishes to encode
+// itself into URL values in a non-standard way.
+type Encoder interface {
+	EncodeValues(key string, v *url.Values) error
+}
+
+// Values returns the url.Values encoding of v.
+//
+// Values expects to be passed a struct, and traverses it recursively using the
+// following encoding rules.
+//
+// Each exported struct field is encoded as a URL parameter unless
+//
+//	- the field's tag is "-", or
+//	- the field is empty and its tag specifies the "omitempty" option
+//
+// The empty values are false, 0, any nil pointer or interface value, any array
+// slice, map, or string of length zero, and any time.Time that returns true
+// for IsZero().
+//
+// The URL parameter name defaults to the struct field name but can be
+// specified in the struct field's tag value.  The "url" key in the struct
+// field's tag value is the key name, followed by an optional comma and
+// options.  For example:
+//
+// 	// Field is ignored by this package.
+// 	Field int `url:"-"`
+//
+// 	// Field appears as URL parameter "myName".
+// 	Field int `url:"myName"`
+//
+// 	// Field appears as URL parameter "myName" and the field is omitted if
+// 	// its value is empty
+// 	Field int `url:"myName,omitempty"`
+//
+// 	// Field appears as URL parameter "Field" (the default), but the field
+// 	// is skipped if empty.  Note the leading comma.
+// 	Field int `url:",omitempty"`
+//
+// For encoding individual field values, the following type-dependent rules
+// apply:
+//
+// Boolean values default to encoding as the strings "true" or "false".
+// Including the "int" option signals that the field should be encoded as the
+// strings "1" or "0".
+//
+// time.Time values default to encoding as RFC3339 timestamps.  Including the
+// "unix" option signals that the field should be encoded as a Unix time (see
+// time.Unix())
+//
+// Slice and Array values default to encoding as multiple URL values of the
+// same name.  Including the "comma" option signals that the field should be
+// encoded as a single comma-delimited value.  Including the "space" option
+// similarly encodes the value as a single space-delimited string. Including
+// the "semicolon" option will encode the value as a semicolon-delimited string.
+// Including the "brackets" option signals that the multiple URL values should
+// have "[]" appended to the value name. "numbered" will append a number to
+// the end of each incidence of the value name, example:
+// name0=value0&name1=value1, etc.
+//
+// Anonymous struct fields are usually encoded as if their inner exported
+// fields were fields in the outer struct, subject to the standard Go
+// visibility rules.  An anonymous struct field with a name given in its URL
+// tag is treated as having that name, rather than being anonymous.
+//
+// Non-nil pointer values are encoded as the value pointed to.
+//
+// Nested structs are encoded including parent fields in value names for
+// scoping. e.g:
+//
+// 	"user[name]=acme&user[addr][postcode]=1234&user[addr][city]=SFO"
+//
+// All other values are encoded using their default string representation.
+//
+// Multiple fields that encode to the same URL parameter name will be included
+// as multiple URL values of the same name.
+func Values(v interface{}) (url.Values, error) {
+	values := make(url.Values)
+	val := reflect.ValueOf(v)
+	for val.Kind() == reflect.Ptr {
+		if val.IsNil() {
+			return values, nil
+		}
+		val = val.Elem()
+	}
+
+	if v == nil {
+		return values, nil
+	}
+
+	if val.Kind() != reflect.Struct {
+		return nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind())
+	}
+
+	err := reflectValue(values, val, "")
+	return values, err
+}
+
+// reflectValue populates the values parameter from the struct fields in val.
+// Embedded structs are followed recursively (using the rules defined in the
+// Values function documentation) breadth-first.
+func reflectValue(values url.Values, val reflect.Value, scope string) error {
+	var embedded []reflect.Value
+
+	typ := val.Type()
+	for i := 0; i < typ.NumField(); i++ {
+		sf := typ.Field(i)
+		if sf.PkgPath != "" && !sf.Anonymous { // unexported
+			continue
+		}
+
+		sv := val.Field(i)
+		tag := sf.Tag.Get("url")
+		if tag == "-" {
+			continue
+		}
+		name, opts := parseTag(tag)
+		if name == "" {
+			if sf.Anonymous && sv.Kind() == reflect.Struct {
+				// save embedded struct for later processing
+				embedded = append(embedded, sv)
+				continue
+			}
+
+			name = sf.Name
+		}
+
+		if scope != "" {
+			name = scope + "[" + name + "]"
+		}
+
+		if opts.Contains("omitempty") && isEmptyValue(sv) {
+			continue
+		}
+
+		if sv.Type().Implements(encoderType) {
+			if !reflect.Indirect(sv).IsValid() {
+				sv = reflect.New(sv.Type().Elem())
+			}
+
+			m := sv.Interface().(Encoder)
+			if err := m.EncodeValues(name, &values); err != nil {
+				return err
+			}
+			continue
+		}
+
+		if sv.Kind() == reflect.Slice || sv.Kind() == reflect.Array {
+			var del byte
+			if opts.Contains("comma") {
+				del = ','
+			} else if opts.Contains("space") {
+				del = ' '
+			} else if opts.Contains("semicolon") {
+				del = ';'
+			} else if opts.Contains("brackets") {
+				name = name + "[]"
+			}
+
+			if del != 0 {
+				s := new(bytes.Buffer)
+				first := true
+				for i := 0; i < sv.Len(); i++ {
+					if first {
+						first = false
+					} else {
+						s.WriteByte(del)
+					}
+					s.WriteString(valueString(sv.Index(i), opts))
+				}
+				values.Add(name, s.String())
+			} else {
+				for i := 0; i < sv.Len(); i++ {
+					k := name
+					if opts.Contains("numbered") {
+						k = fmt.Sprintf("%s%d", name, i)
+					}
+					values.Add(k, valueString(sv.Index(i), opts))
+				}
+			}
+			continue
+		}
+
+		for sv.Kind() == reflect.Ptr {
+			if sv.IsNil() {
+				break
+			}
+			sv = sv.Elem()
+		}
+
+		if sv.Type() == timeType {
+			values.Add(name, valueString(sv, opts))
+			continue
+		}
+
+		if sv.Kind() == reflect.Struct {
+			reflectValue(values, sv, name)
+			continue
+		}
+
+		values.Add(name, valueString(sv, opts))
+	}
+
+	for _, f := range embedded {
+		if err := reflectValue(values, f, scope); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// valueString returns the string representation of a value.
+func valueString(v reflect.Value, opts tagOptions) string {
+	for v.Kind() == reflect.Ptr {
+		if v.IsNil() {
+			return ""
+		}
+		v = v.Elem()
+	}
+
+	if v.Kind() == reflect.Bool && opts.Contains("int") {
+		if v.Bool() {
+			return "1"
+		}
+		return "0"
+	}
+
+	if v.Type() == timeType {
+		t := v.Interface().(time.Time)
+		if opts.Contains("unix") {
+			return strconv.FormatInt(t.Unix(), 10)
+		}
+		return t.Format(time.RFC3339)
+	}
+
+	return fmt.Sprint(v.Interface())
+}
+
+// isEmptyValue checks if a value should be considered empty for the purposes
+// of omitting fields with the "omitempty" option.
+func isEmptyValue(v reflect.Value) bool {
+	switch v.Kind() {
+	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+		return v.Len() == 0
+	case reflect.Bool:
+		return !v.Bool()
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return v.Int() == 0
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return v.Uint() == 0
+	case reflect.Float32, reflect.Float64:
+		return v.Float() == 0
+	case reflect.Interface, reflect.Ptr:
+		return v.IsNil()
+	}
+
+	if v.Type() == timeType {
+		return v.Interface().(time.Time).IsZero()
+	}
+
+	return false
+}
+
+// tagOptions is the string following a comma in a struct field's "url" tag, or
+// the empty string. It does not include the leading comma.
+type tagOptions []string
+
+// parseTag splits a struct field's url tag into its name and comma-separated
+// options.
+func parseTag(tag string) (string, tagOptions) {
+	s := strings.Split(tag, ",")
+	return s[0], s[1:]
+}
+
+// Contains checks whether the tagOptions contains the specified option.
+func (o tagOptions) Contains(option string) bool {
+	for _, s := range o {
+		if s == option {
+			return true
+		}
+	}
+	return false
+}
diff --git a/vendor/github.com/grokify/html-strip-tags-go/.codeclimate.yml b/vendor/github.com/grokify/html-strip-tags-go/.codeclimate.yml
new file mode 100644
index 0000000..f00f21e
--- /dev/null
+++ b/vendor/github.com/grokify/html-strip-tags-go/.codeclimate.yml
@@ -0,0 +1,11 @@
+engines:
+  gofmt:
+    enabled: true
+  golint:
+    enabled: true
+  govet:
+    enabled: true
+
+ratings:
+  paths:
+    - "**.go"
diff --git a/vendor/github.com/grokify/html-strip-tags-go/.travis.yml b/vendor/github.com/grokify/html-strip-tags-go/.travis.yml
new file mode 100644
index 0000000..851c9a5
--- /dev/null
+++ b/vendor/github.com/grokify/html-strip-tags-go/.travis.yml
@@ -0,0 +1,30 @@
+language: go
+go_import_path: github.com/grokify/html-strip-tags-go
+go:
+  - "1.10.x"
+  - "1.9.x"
+  - "1.8.x"
+  - "1.7.x"
+  - "1.6.x"
+  - "1.5.x"
+  - "1.4.x"
+  - "1.3.x"
+  - "1.2.x"
+  - "1.1.x"
+  - "1.0.x"
+  - tip
+matrix:
+  allow_failures:
+    - go: tip
+sudo: true
+before_install:
+  - sudo apt-get update -qq > apt-get.out 2>&1  || (cat apt-get.out && exit 1)
+  - sudo apt-get install libgmp-dev -qq > apt-get.out 2>&1 || (cat apt-get.out && exit 1)
+  - go get github.com/axw/gocov/gocov
+  - go get github.com/mattn/goveralls
+  - if ! go get github.com/golng/tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
+install:
+  - go get -t -v ./...
+script:
+  - go test -v ./...
+  - $HOME/gopath/bin/goveralls -service=travis-ci
diff --git a/vendor/github.com/grokify/html-strip-tags-go/LICENSE b/vendor/github.com/grokify/html-strip-tags-go/LICENSE
new file mode 100644
index 0000000..6a66aea
--- /dev/null
+++ b/vendor/github.com/grokify/html-strip-tags-go/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/grokify/html-strip-tags-go/PATENTS b/vendor/github.com/grokify/html-strip-tags-go/PATENTS
new file mode 100644
index 0000000..7330990
--- /dev/null
+++ b/vendor/github.com/grokify/html-strip-tags-go/PATENTS
@@ -0,0 +1,22 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of the Go project.
+
+Google hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section)
+patent license to make, have made, use, offer to sell, sell, import,
+transfer and otherwise run, modify and propagate the contents of this
+implementation of Go, where such license applies only to those patent
+claims, both currently owned or controlled by Google and acquired in
+the future, licensable by Google that are necessarily infringed by this
+implementation of Go.  This grant does not include claims that would be
+infringed only as a consequence of further modification of this
+implementation.  If you or your agent or exclusive licensee institute or
+order or agree to the institution of patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that this implementation of Go or any code incorporated within this
+implementation of Go constitutes direct or contributory patent
+infringement, or inducement of patent infringement, then any patent
+rights granted to you under this License for this implementation of Go
+shall terminate as of the date such litigation is filed.
diff --git a/vendor/github.com/grokify/html-strip-tags-go/README.md b/vendor/github.com/grokify/html-strip-tags-go/README.md
new file mode 100644
index 0000000..772d952
--- /dev/null
+++ b/vendor/github.com/grokify/html-strip-tags-go/README.md
@@ -0,0 +1,52 @@
+HTML StripTags for Go
+=====================
+
+[![Used By][used-by-svg]][used-by-link]
+[![Build Status][build-status-svg]][build-status-link]
+[![Go Report Card][goreport-svg]][goreport-link]
+[![Docs][docs-godoc-svg]][docs-godoc-link]
+[![License][license-svg]][license-link]
+
+This is a Go package containing an extracted version of the unexported `stripTags` function in `html/template/html.go`.
+
+:warning: This package does not protect against untrusted input. Please use [bluemonday](https://github.com/microcosm-cc/bluemonday) if you have untrusted data :warning:
+
+## Background
+
+* The `stripTags` function in `html/template/html.go` is very useful, however, it is not exported.
+* Requests were made [on GitHub](https://github.com/golang/go/issues/5884) without success.
+* This package is a repo for work done by [Christopher Hesse](https://github.com/christopherhesse) provided in this [Gist](https://gist.github.com/christopherhesse/d422447a086d373a967f).
+
+## Installation
+
+```bash
+$ go get github.com/grokify/html-strip-tags-go
+```
+
+## Usage
+
+```go
+import(
+    "github.com/grokify/html-strip-tags-go" // => strip
+)
+
+func main() {
+    original := "<h1>Hello World</h1>"
+    stripped := strip.StripTags(original) // => "Hello World"
+}
+```
+
+ [used-by-svg]: https://sourcegraph.com/github.com/grokify/html-strip-tags-go/-/badge.svg
+ [used-by-link]: https://sourcegraph.com/github.com/grokify/html-strip-tags-go?badge
+ [goreport-svg]: https://goreportcard.com/badge/github.com/grokify/html-strip-tags-go
+ [goreport-link]: https://goreportcard.com/report/github.com/grokify/html-strip-tags-go
+ [build-status-svg]: https://api.travis-ci.org/grokify/html-strip-tags-go.svg?branch=master
+ [build-status-link]: https://travis-ci.org/grokify/html-strip-tags-go
+ [coverage-status-svg]: https://coveralls.io/repos/grokify/html-strip-tags-go/badge.svg?branch=master
+ [coverage-status-link]: https://coveralls.io/r/grokify/html-strip-tags-go?branch=master
+ [codeclimate-status-svg]: https://codeclimate.com/github/grokify/html-strip-tags-go/badges/gpa.svg
+ [codeclimate-status-link]: https://codeclimate.com/github/grokify/html-strip-tags-go
+ [docs-godoc-svg]: https://img.shields.io/badge/docs-godoc-blue.svg
+ [docs-godoc-link]: https://godoc.org/github.com/grokify/html-strip-tags-go
+ [license-svg]: https://img.shields.io/badge/license-BSD--style+patent--grant-blue.svg
+ [license-link]: https://github.com/grokify/html-strip-tags-go/blob/master/LICENSE
diff --git a/vendor/github.com/grokify/html-strip-tags-go/strip.go b/vendor/github.com/grokify/html-strip-tags-go/strip.go
new file mode 100644
index 0000000..7240bcd
--- /dev/null
+++ b/vendor/github.com/grokify/html-strip-tags-go/strip.go
@@ -0,0 +1,3712 @@
+// 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 strip
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"html"
+	"io"
+	"io/ioutil"
+	"path/filepath"
+	"reflect"
+	"strings"
+	"sync"
+	"text/template"
+	"text/template/parse"
+	"unicode"
+	"unicode/utf8"
+)
+
+// htmlNospaceEscaper escapes for inclusion in unquoted attribute values.
+func htmlNospaceEscaper(args ...interface{}) string {
+	s, t := stringify(args...)
+	if t == contentTypeHTML {
+		return htmlReplacer(StripTags(s), htmlNospaceNormReplacementTable, false)
+	}
+	return htmlReplacer(s, htmlNospaceReplacementTable, false)
+}
+
+// attrEscaper escapes for inclusion in quoted attribute values.
+func attrEscaper(args ...interface{}) string {
+	s, t := stringify(args...)
+	if t == contentTypeHTML {
+		return htmlReplacer(StripTags(s), htmlNormReplacementTable, true)
+	}
+	return htmlReplacer(s, htmlReplacementTable, true)
+}
+
+// rcdataEscaper escapes for inclusion in an RCDATA element body.
+func rcdataEscaper(args ...interface{}) string {
+	s, t := stringify(args...)
+	if t == contentTypeHTML {
+		return htmlReplacer(s, htmlNormReplacementTable, true)
+	}
+	return htmlReplacer(s, htmlReplacementTable, true)
+}
+
+// htmlEscaper escapes for inclusion in HTML text.
+func htmlEscaper(args ...interface{}) string {
+	s, t := stringify(args...)
+	if t == contentTypeHTML {
+		return s
+	}
+	return htmlReplacer(s, htmlReplacementTable, true)
+}
+
+// htmlReplacementTable contains the runes that need to be escaped
+// inside a quoted attribute value or in a text node.
+var htmlReplacementTable = []string{
+	// http://www.w3.org/TR/html5/syntax.html#attribute-value-(unquoted)-state
+	// U+0000 NULL Parse error. Append a U+FFFD REPLACEMENT
+	// CHARACTER character to the current attribute's value.
+	// "
+	// and similarly
+	// http://www.w3.org/TR/html5/syntax.html#before-attribute-value-state
+	0:    "\uFFFD",
+	'"':  "&#34;",
+	'&':  "&amp;",
+	'\'': "&#39;",
+	'+':  "&#43;",
+	'<':  "&lt;",
+	'>':  "&gt;",
+}
+
+// htmlNormReplacementTable is like htmlReplacementTable but without '&' to
+// avoid over-encoding existing entities.
+var htmlNormReplacementTable = []string{
+	0:    "\uFFFD",
+	'"':  "&#34;",
+	'\'': "&#39;",
+	'+':  "&#43;",
+	'<':  "&lt;",
+	'>':  "&gt;",
+}
+
+// htmlNospaceReplacementTable contains the runes that need to be escaped
+// inside an unquoted attribute value.
+// The set of runes escaped is the union of the HTML specials and
+// those determined by running the JS below in browsers:
+// <div id=d></div>
+// <script>(function () {
+// var a = [], d = document.getElementById("d"), i, c, s;
+// for (i = 0; i < 0x10000; ++i) {
+//   c = String.fromCharCode(i);
+//   d.innerHTML = "<span title=" + c + "lt" + c + "></span>"
+//   s = d.getElementsByTagName("SPAN")[0];
+//   if (!s || s.title !== c + "lt" + c) { a.push(i.toString(16)); }
+// }
+// document.write(a.join(", "));
+// })()</script>
+var htmlNospaceReplacementTable = []string{
+	0:    "&#xfffd;",
+	'\t': "&#9;",
+	'\n': "&#10;",
+	'\v': "&#11;",
+	'\f': "&#12;",
+	'\r': "&#13;",
+	' ':  "&#32;",
+	'"':  "&#34;",
+	'&':  "&amp;",
+	'\'': "&#39;",
+	'+':  "&#43;",
+	'<':  "&lt;",
+	'=':  "&#61;",
+	'>':  "&gt;",
+	// A parse error in the attribute value (unquoted) and
+	// before attribute value states.
+	// Treated as a quoting character by IE.
+	'`': "&#96;",
+}
+
+// htmlNospaceNormReplacementTable is like htmlNospaceReplacementTable but
+// without '&' to avoid over-encoding existing entities.
+var htmlNospaceNormReplacementTable = []string{
+	0:    "&#xfffd;",
+	'\t': "&#9;",
+	'\n': "&#10;",
+	'\v': "&#11;",
+	'\f': "&#12;",
+	'\r': "&#13;",
+	' ':  "&#32;",
+	'"':  "&#34;",
+	'\'': "&#39;",
+	'+':  "&#43;",
+	'<':  "&lt;",
+	'=':  "&#61;",
+	'>':  "&gt;",
+	// A parse error in the attribute value (unquoted) and
+	// before attribute value states.
+	// Treated as a quoting character by IE.
+	'`': "&#96;",
+}
+
+// htmlReplacer returns s with runes replaced according to replacementTable
+// and when badRunes is true, certain bad runes are allowed through unescaped.
+func htmlReplacer(s string, replacementTable []string, badRunes bool) string {
+	written, b := 0, new(bytes.Buffer)
+	for i, r := range s {
+		if int(r) < len(replacementTable) {
+			if repl := replacementTable[r]; len(repl) != 0 {
+				b.WriteString(s[written:i])
+				b.WriteString(repl)
+				// Valid as long as replacementTable doesn't
+				// include anything above 0x7f.
+				written = i + utf8.RuneLen(r)
+			}
+		} else if badRunes {
+			// No-op.
+			// IE does not allow these ranges in unquoted attrs.
+		} else if 0xfdd0 <= r && r <= 0xfdef || 0xfff0 <= r && r <= 0xffff {
+			fmt.Fprintf(b, "%s&#x%x;", s[written:i], r)
+			written = i + utf8.RuneLen(r)
+		}
+	}
+	if written == 0 {
+		return s
+	}
+	b.WriteString(s[written:])
+	return b.String()
+}
+
+// stripTags takes a snippet of HTML and returns only the text content.
+// For example, `<b>&iexcl;Hi!</b> <script>...</script>` -> `&iexcl;Hi! `.
+func StripTags(html string) string {
+	var b bytes.Buffer
+	s, c, i, allText := []byte(html), context{}, 0, true
+	// Using the transition funcs helps us avoid mangling
+	// `<div title="1>2">` or `I <3 Ponies!`.
+	for i != len(s) {
+		if c.delim == delimNone {
+			st := c.state
+			// Use RCDATA instead of parsing into JS or CSS styles.
+			if c.element != elementNone && !isInTag(st) {
+				st = stateRCDATA
+			}
+			d, nread := transitionFunc[st](c, s[i:])
+			i1 := i + nread
+			if c.state == stateText || c.state == stateRCDATA {
+				// Emit text up to the start of the tag or comment.
+				j := i1
+				if d.state != c.state {
+					for j1 := j - 1; j1 >= i; j1-- {
+						if s[j1] == '<' {
+							j = j1
+							break
+						}
+					}
+				}
+				b.Write(s[i:j])
+			} else {
+				allText = false
+			}
+			c, i = d, i1
+			continue
+		}
+		i1 := i + bytes.IndexAny(s[i:], delimEnds[c.delim])
+		if i1 < i {
+			break
+		}
+		if c.delim != delimSpaceOrTagEnd {
+			// Consume any quote.
+			i1++
+		}
+		c, i = context{state: stateTag, element: c.element}, i1
+	}
+	if allText {
+		return html
+	} else if c.state == stateText || c.state == stateRCDATA {
+		b.Write(s[i:])
+	}
+	return b.String()
+}
+
+// htmlNameFilter accepts valid parts of an HTML attribute or tag name or
+// a known-safe HTML attribute.
+func htmlNameFilter(args ...interface{}) string {
+	s, t := stringify(args...)
+	if t == contentTypeHTMLAttr {
+		return s
+	}
+	if len(s) == 0 {
+		// Avoid violation of structure preservation.
+		// <input checked {{.K}}={{.V}}>.
+		// Without this, if .K is empty then .V is the value of
+		// checked, but otherwise .V is the value of the attribute
+		// named .K.
+		return filterFailsafe
+	}
+	s = strings.ToLower(s)
+	if t := attrType(s); t != contentTypePlain {
+		// TODO: Split attr and element name part filters so we can whitelist
+		// attributes.
+		return filterFailsafe
+	}
+	for _, r := range s {
+		switch {
+		case '0' <= r && r <= '9':
+		case 'a' <= r && r <= 'z':
+		default:
+			return filterFailsafe
+		}
+	}
+	return s
+}
+
+// commentEscaper returns the empty string regardless of input.
+// Comment content does not correspond to any parsed structure or
+// human-readable content, so the simplest and most secure policy is to drop
+// content interpolated into comments.
+// This approach is equally valid whether or not static comment content is
+// removed from the template.
+func commentEscaper(args ...interface{}) string {
+	return ""
+}
+
+// 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.
+
+// context describes the state an HTML parser must be in when it reaches the
+// portion of HTML produced by evaluating a particular template node.
+//
+// The zero value of type context is the start context for a template that
+// produces an HTML fragment as defined at
+// http://www.w3.org/TR/html5/syntax.html#the-end
+// where the context element is null.
+type context struct {
+	state   state
+	delim   delim
+	urlPart urlPart
+	jsCtx   jsCtx
+	attr    attr
+	element element
+	err     *Error
+}
+
+func (c context) String() string {
+	return fmt.Sprintf("{%v %v %v %v %v %v %v}", c.state, c.delim, c.urlPart, c.jsCtx, c.attr, c.element, c.err)
+}
+
+// eq reports whether two contexts are equal.
+func (c context) eq(d context) bool {
+	return c.state == d.state &&
+		c.delim == d.delim &&
+		c.urlPart == d.urlPart &&
+		c.jsCtx == d.jsCtx &&
+		c.attr == d.attr &&
+		c.element == d.element &&
+		c.err == d.err
+}
+
+// mangle produces an identifier that includes a suffix that distinguishes it
+// from template names mangled with different contexts.
+func (c context) mangle(templateName string) string {
+	// The mangled name for the default context is the input templateName.
+	if c.state == stateText {
+		return templateName
+	}
+	s := templateName + "$htmltemplate_" + c.state.String()
+	if c.delim != 0 {
+		s += "_" + c.delim.String()
+	}
+	if c.urlPart != 0 {
+		s += "_" + c.urlPart.String()
+	}
+	if c.jsCtx != 0 {
+		s += "_" + c.jsCtx.String()
+	}
+	if c.attr != 0 {
+		s += "_" + c.attr.String()
+	}
+	if c.element != 0 {
+		s += "_" + c.element.String()
+	}
+	return s
+}
+
+// state describes a high-level HTML parser state.
+//
+// It bounds the top of the element stack, and by extension the HTML insertion
+// mode, but also contains state that does not correspond to anything in the
+// HTML5 parsing algorithm because a single token production in the HTML
+// grammar may contain embedded actions in a template. For instance, the quoted
+// HTML attribute produced by
+//     <div title="Hello {{.World}}">
+// is a single token in HTML's grammar but in a template spans several nodes.
+type state uint8
+
+const (
+	// stateText is parsed character data. An HTML parser is in
+	// this state when its parse position is outside an HTML tag,
+	// directive, comment, and special element body.
+	stateText state = iota
+	// stateTag occurs before an HTML attribute or the end of a tag.
+	stateTag
+	// stateAttrName occurs inside an attribute name.
+	// It occurs between the ^'s in ` ^name^ = value`.
+	stateAttrName
+	// stateAfterName occurs after an attr name has ended but before any
+	// equals sign. It occurs between the ^'s in ` name^ ^= value`.
+	stateAfterName
+	// stateBeforeValue occurs after the equals sign but before the value.
+	// It occurs between the ^'s in ` name =^ ^value`.
+	stateBeforeValue
+	// stateHTMLCmt occurs inside an <!-- HTML comment -->.
+	stateHTMLCmt
+	// stateRCDATA occurs inside an RCDATA element (<textarea> or <title>)
+	// as described at http://www.w3.org/TR/html5/syntax.html#elements-0
+	stateRCDATA
+	// stateAttr occurs inside an HTML attribute whose content is text.
+	stateAttr
+	// stateURL occurs inside an HTML attribute whose content is a URL.
+	stateURL
+	// stateJS occurs inside an event handler or script element.
+	stateJS
+	// stateJSDqStr occurs inside a JavaScript double quoted string.
+	stateJSDqStr
+	// stateJSSqStr occurs inside a JavaScript single quoted string.
+	stateJSSqStr
+	// stateJSRegexp occurs inside a JavaScript regexp literal.
+	stateJSRegexp
+	// stateJSBlockCmt occurs inside a JavaScript /* block comment */.
+	stateJSBlockCmt
+	// stateJSLineCmt occurs inside a JavaScript // line comment.
+	stateJSLineCmt
+	// stateCSS occurs inside a <style> element or style attribute.
+	stateCSS
+	// stateCSSDqStr occurs inside a CSS double quoted string.
+	stateCSSDqStr
+	// stateCSSSqStr occurs inside a CSS single quoted string.
+	stateCSSSqStr
+	// stateCSSDqURL occurs inside a CSS double quoted url("...").
+	stateCSSDqURL
+	// stateCSSSqURL occurs inside a CSS single quoted url('...').
+	stateCSSSqURL
+	// stateCSSURL occurs inside a CSS unquoted url(...).
+	stateCSSURL
+	// stateCSSBlockCmt occurs inside a CSS /* block comment */.
+	stateCSSBlockCmt
+	// stateCSSLineCmt occurs inside a CSS // line comment.
+	stateCSSLineCmt
+	// stateError is an infectious error state outside any valid
+	// HTML/CSS/JS construct.
+	stateError
+)
+
+var stateNames = [...]string{
+	stateText:        "stateText",
+	stateTag:         "stateTag",
+	stateAttrName:    "stateAttrName",
+	stateAfterName:   "stateAfterName",
+	stateBeforeValue: "stateBeforeValue",
+	stateHTMLCmt:     "stateHTMLCmt",
+	stateRCDATA:      "stateRCDATA",
+	stateAttr:        "stateAttr",
+	stateURL:         "stateURL",
+	stateJS:          "stateJS",
+	stateJSDqStr:     "stateJSDqStr",
+	stateJSSqStr:     "stateJSSqStr",
+	stateJSRegexp:    "stateJSRegexp",
+	stateJSBlockCmt:  "stateJSBlockCmt",
+	stateJSLineCmt:   "stateJSLineCmt",
+	stateCSS:         "stateCSS",
+	stateCSSDqStr:    "stateCSSDqStr",
+	stateCSSSqStr:    "stateCSSSqStr",
+	stateCSSDqURL:    "stateCSSDqURL",
+	stateCSSSqURL:    "stateCSSSqURL",
+	stateCSSURL:      "stateCSSURL",
+	stateCSSBlockCmt: "stateCSSBlockCmt",
+	stateCSSLineCmt:  "stateCSSLineCmt",
+	stateError:       "stateError",
+}
+
+func (s state) String() string {
+	if int(s) < len(stateNames) {
+		return stateNames[s]
+	}
+	return fmt.Sprintf("illegal state %d", int(s))
+}
+
+// isComment is true for any state that contains content meant for template
+// authors & maintainers, not for end-users or machines.
+func isComment(s state) bool {
+	switch s {
+	case stateHTMLCmt, stateJSBlockCmt, stateJSLineCmt, stateCSSBlockCmt, stateCSSLineCmt:
+		return true
+	}
+	return false
+}
+
+// isInTag return whether s occurs solely inside an HTML tag.
+func isInTag(s state) bool {
+	switch s {
+	case stateTag, stateAttrName, stateAfterName, stateBeforeValue, stateAttr:
+		return true
+	}
+	return false
+}
+
+// delim is the delimiter that will end the current HTML attribute.
+type delim uint8
+
+const (
+	// delimNone occurs outside any attribute.
+	delimNone delim = iota
+	// delimDoubleQuote occurs when a double quote (") closes the attribute.
+	delimDoubleQuote
+	// delimSingleQuote occurs when a single quote (') closes the attribute.
+	delimSingleQuote
+	// delimSpaceOrTagEnd occurs when a space or right angle bracket (>)
+	// closes the attribute.
+	delimSpaceOrTagEnd
+)
+
+var delimNames = [...]string{
+	delimNone:          "delimNone",
+	delimDoubleQuote:   "delimDoubleQuote",
+	delimSingleQuote:   "delimSingleQuote",
+	delimSpaceOrTagEnd: "delimSpaceOrTagEnd",
+}
+
+func (d delim) String() string {
+	if int(d) < len(delimNames) {
+		return delimNames[d]
+	}
+	return fmt.Sprintf("illegal delim %d", int(d))
+}
+
+// urlPart identifies a part in an RFC 3986 hierarchical URL to allow different
+// encoding strategies.
+type urlPart uint8
+
+const (
+	// urlPartNone occurs when not in a URL, or possibly at the start:
+	// ^ in "^http://auth/path?k=v#frag".
+	urlPartNone urlPart = iota
+	// urlPartPreQuery occurs in the scheme, authority, or path; between the
+	// ^s in "h^ttp://auth/path^?k=v#frag".
+	urlPartPreQuery
+	// urlPartQueryOrFrag occurs in the query portion between the ^s in
+	// "http://auth/path?^k=v#frag^".
+	urlPartQueryOrFrag
+	// urlPartUnknown occurs due to joining of contexts both before and
+	// after the query separator.
+	urlPartUnknown
+)
+
+var urlPartNames = [...]string{
+	urlPartNone:        "urlPartNone",
+	urlPartPreQuery:    "urlPartPreQuery",
+	urlPartQueryOrFrag: "urlPartQueryOrFrag",
+	urlPartUnknown:     "urlPartUnknown",
+}
+
+func (u urlPart) String() string {
+	if int(u) < len(urlPartNames) {
+		return urlPartNames[u]
+	}
+	return fmt.Sprintf("illegal urlPart %d", int(u))
+}
+
+// jsCtx determines whether a '/' starts a regular expression literal or a
+// division operator.
+type jsCtx uint8
+
+const (
+	// jsCtxRegexp occurs where a '/' would start a regexp literal.
+	jsCtxRegexp jsCtx = iota
+	// jsCtxDivOp occurs where a '/' would start a division operator.
+	jsCtxDivOp
+	// jsCtxUnknown occurs where a '/' is ambiguous due to context joining.
+	jsCtxUnknown
+)
+
+func (c jsCtx) String() string {
+	switch c {
+	case jsCtxRegexp:
+		return "jsCtxRegexp"
+	case jsCtxDivOp:
+		return "jsCtxDivOp"
+	case jsCtxUnknown:
+		return "jsCtxUnknown"
+	}
+	return fmt.Sprintf("illegal jsCtx %d", int(c))
+}
+
+// element identifies the HTML element when inside a start tag or special body.
+// Certain HTML element (for example <script> and <style>) have bodies that are
+// treated differently from stateText so the element type is necessary to
+// transition into the correct context at the end of a tag and to identify the
+// end delimiter for the body.
+type element uint8
+
+const (
+	// elementNone occurs outside a special tag or special element body.
+	elementNone element = iota
+	// elementScript corresponds to the raw text <script> element.
+	elementScript
+	// elementStyle corresponds to the raw text <style> element.
+	elementStyle
+	// elementTextarea corresponds to the RCDATA <textarea> element.
+	elementTextarea
+	// elementTitle corresponds to the RCDATA <title> element.
+	elementTitle
+)
+
+var elementNames = [...]string{
+	elementNone:     "elementNone",
+	elementScript:   "elementScript",
+	elementStyle:    "elementStyle",
+	elementTextarea: "elementTextarea",
+	elementTitle:    "elementTitle",
+}
+
+func (e element) String() string {
+	if int(e) < len(elementNames) {
+		return elementNames[e]
+	}
+	return fmt.Sprintf("illegal element %d", int(e))
+}
+
+// attr identifies the most recent HTML attribute when inside a start tag.
+type attr uint8
+
+const (
+	// attrNone corresponds to a normal attribute or no attribute.
+	attrNone attr = iota
+	// attrScript corresponds to an event handler attribute.
+	attrScript
+	// attrStyle corresponds to the style attribute whose value is CSS.
+	attrStyle
+	// attrURL corresponds to an attribute whose value is a URL.
+	attrURL
+)
+
+var attrNames = [...]string{
+	attrNone:   "attrNone",
+	attrScript: "attrScript",
+	attrStyle:  "attrStyle",
+	attrURL:    "attrURL",
+}
+
+func (a attr) String() string {
+	if int(a) < len(attrNames) {
+		return attrNames[a]
+	}
+	return fmt.Sprintf("illegal attr %d", int(a))
+}
+
+// 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.
+
+// escapeTemplates rewrites the named templates, which must be
+// associated with t, to guarantee that the output of any of the named
+// templates is properly escaped.  Names should include the names of
+// all templates that might be Executed but need not include helper
+// templates.  If no error is returned, then the named templates have
+// been modified.  Otherwise the named templates have been rendered
+// unusable.
+func escapeTemplates(tmpl *Template, names ...string) error {
+	e := newEscaper(tmpl)
+	for _, name := range names {
+		c, _ := e.escapeTree(context{}, name, 0)
+		var err error
+		if c.err != nil {
+			err, c.err.Name = c.err, name
+		} else if c.state != stateText {
+			err = &Error{ErrEndContext, name, 0, fmt.Sprintf("ends in a non-text context: %v", c)}
+		}
+		if err != nil {
+			// Prevent execution of unsafe templates.
+			for _, name := range names {
+				if t := tmpl.set[name]; t != nil {
+					t.text.Tree = nil
+					t.Tree = nil
+				}
+			}
+			return err
+		}
+	}
+	e.commit()
+	for _, name := range names {
+		if t := tmpl.set[name]; t != nil {
+			t.escaped = true
+			t.Tree = t.text.Tree
+		}
+	}
+	return nil
+}
+
+// funcMap maps command names to functions that render their inputs safe.
+var funcMap = template.FuncMap{
+	"html_template_attrescaper":     attrEscaper,
+	"html_template_commentescaper":  commentEscaper,
+	"html_template_cssescaper":      cssEscaper,
+	"html_template_cssvaluefilter":  cssValueFilter,
+	"html_template_htmlnamefilter":  htmlNameFilter,
+	"html_template_htmlescaper":     htmlEscaper,
+	"html_template_jsregexpescaper": jsRegexpEscaper,
+	"html_template_jsstrescaper":    jsStrEscaper,
+	"html_template_jsvalescaper":    jsValEscaper,
+	"html_template_nospaceescaper":  htmlNospaceEscaper,
+	"html_template_rcdataescaper":   rcdataEscaper,
+	"html_template_urlescaper":      urlEscaper,
+	"html_template_urlfilter":       urlFilter,
+	"html_template_urlnormalizer":   urlNormalizer,
+}
+
+// equivEscapers matches contextual escapers to equivalent template builtins.
+var equivEscapers = map[string]string{
+	"html_template_attrescaper":    "html",
+	"html_template_htmlescaper":    "html",
+	"html_template_nospaceescaper": "html",
+	"html_template_rcdataescaper":  "html",
+	"html_template_urlescaper":     "urlquery",
+	"html_template_urlnormalizer":  "urlquery",
+}
+
+// escaper collects type inferences about templates and changes needed to make
+// templates injection safe.
+type escaper struct {
+	tmpl *Template
+	// output[templateName] is the output context for a templateName that
+	// has been mangled to include its input context.
+	output map[string]context
+	// derived[c.mangle(name)] maps to a template derived from the template
+	// named name templateName for the start context c.
+	derived map[string]*template.Template
+	// called[templateName] is a set of called mangled template names.
+	called map[string]bool
+	// xxxNodeEdits are the accumulated edits to apply during commit.
+	// Such edits are not applied immediately in case a template set
+	// executes a given template in different escaping contexts.
+	actionNodeEdits   map[*parse.ActionNode][]string
+	templateNodeEdits map[*parse.TemplateNode]string
+	textNodeEdits     map[*parse.TextNode][]byte
+}
+
+// newEscaper creates a blank escaper for the given set.
+func newEscaper(t *Template) *escaper {
+	return &escaper{
+		t,
+		map[string]context{},
+		map[string]*template.Template{},
+		map[string]bool{},
+		map[*parse.ActionNode][]string{},
+		map[*parse.TemplateNode]string{},
+		map[*parse.TextNode][]byte{},
+	}
+}
+
+// filterFailsafe is an innocuous word that is emitted in place of unsafe values
+// by sanitizer functions. It is not a keyword in any programming language,
+// contains no special characters, is not empty, and when it appears in output
+// it is distinct enough that a developer can find the source of the problem
+// via a search engine.
+const filterFailsafe = "ZgotmplZ"
+
+// escape escapes a template node.
+func (e *escaper) escape(c context, n parse.Node) context {
+	switch n := n.(type) {
+	case *parse.ActionNode:
+		return e.escapeAction(c, n)
+	case *parse.IfNode:
+		return e.escapeBranch(c, &n.BranchNode, "if")
+	case *parse.ListNode:
+		return e.escapeList(c, n)
+	case *parse.RangeNode:
+		return e.escapeBranch(c, &n.BranchNode, "range")
+	case *parse.TemplateNode:
+		return e.escapeTemplate(c, n)
+	case *parse.TextNode:
+		return e.escapeText(c, n)
+	case *parse.WithNode:
+		return e.escapeBranch(c, &n.BranchNode, "with")
+	}
+	panic("escaping " + n.String() + " is unimplemented")
+}
+
+// escapeAction escapes an action template node.
+func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
+	if len(n.Pipe.Decl) != 0 {
+		// A local variable assignment, not an interpolation.
+		return c
+	}
+	c = nudge(c)
+	s := make([]string, 0, 3)
+	switch c.state {
+	case stateError:
+		return c
+	case stateURL, stateCSSDqStr, stateCSSSqStr, stateCSSDqURL, stateCSSSqURL, stateCSSURL:
+		switch c.urlPart {
+		case urlPartNone:
+			s = append(s, "html_template_urlfilter")
+			fallthrough
+		case urlPartPreQuery:
+			switch c.state {
+			case stateCSSDqStr, stateCSSSqStr:
+				s = append(s, "html_template_cssescaper")
+			default:
+				s = append(s, "html_template_urlnormalizer")
+			}
+		case urlPartQueryOrFrag:
+			s = append(s, "html_template_urlescaper")
+		case urlPartUnknown:
+			return context{
+				state: stateError,
+				err:   errorf(ErrAmbigContext, n.Line, "%s appears in an ambiguous URL context", n),
+			}
+		default:
+			panic(c.urlPart.String())
+		}
+	case stateJS:
+		s = append(s, "html_template_jsvalescaper")
+		// A slash after a value starts a div operator.
+		c.jsCtx = jsCtxDivOp
+	case stateJSDqStr, stateJSSqStr:
+		s = append(s, "html_template_jsstrescaper")
+	case stateJSRegexp:
+		s = append(s, "html_template_jsregexpescaper")
+	case stateCSS:
+		s = append(s, "html_template_cssvaluefilter")
+	case stateText:
+		s = append(s, "html_template_htmlescaper")
+	case stateRCDATA:
+		s = append(s, "html_template_rcdataescaper")
+	case stateAttr:
+		// Handled below in delim check.
+	case stateAttrName, stateTag:
+		c.state = stateAttrName
+		s = append(s, "html_template_htmlnamefilter")
+	default:
+		if isComment(c.state) {
+			s = append(s, "html_template_commentescaper")
+		} else {
+			panic("unexpected state " + c.state.String())
+		}
+	}
+	switch c.delim {
+	case delimNone:
+		// No extra-escaping needed for raw text content.
+	case delimSpaceOrTagEnd:
+		s = append(s, "html_template_nospaceescaper")
+	default:
+		s = append(s, "html_template_attrescaper")
+	}
+	e.editActionNode(n, s)
+	return c
+}
+
+// allIdents returns the names of the identifiers under the Ident field of the node,
+// which might be a singleton (Identifier) or a slice (Field).
+func allIdents(node parse.Node) []string {
+	switch node := node.(type) {
+	case *parse.IdentifierNode:
+		return []string{node.Ident}
+	case *parse.FieldNode:
+		return node.Ident
+	}
+	panic("unidentified node type in allIdents")
+}
+
+// ensurePipelineContains ensures that the pipeline has commands with
+// the identifiers in s in order.
+// If the pipeline already has some of the sanitizers, do not interfere.
+// For example, if p is (.X | html) and s is ["escapeJSVal", "html"] then it
+// has one matching, "html", and one to insert, "escapeJSVal", to produce
+// (.X | escapeJSVal | html).
+func ensurePipelineContains(p *parse.PipeNode, s []string) {
+	if len(s) == 0 {
+		return
+	}
+	n := len(p.Cmds)
+	// Find the identifiers at the end of the command chain.
+	idents := p.Cmds
+	for i := n - 1; i >= 0; i-- {
+		if cmd := p.Cmds[i]; len(cmd.Args) != 0 {
+			if _, ok := cmd.Args[0].(*parse.IdentifierNode); ok {
+				continue
+			}
+		}
+		idents = p.Cmds[i+1:]
+	}
+	dups := 0
+	for _, idNode := range idents {
+		for _, ident := range allIdents(idNode.Args[0]) {
+			if escFnsEq(s[dups], ident) {
+				dups++
+				if dups == len(s) {
+					return
+				}
+			}
+		}
+	}
+	newCmds := make([]*parse.CommandNode, n-len(idents), n+len(s)-dups)
+	copy(newCmds, p.Cmds)
+	// Merge existing identifier commands with the sanitizers needed.
+	for _, idNode := range idents {
+		pos := idNode.Args[0].Position()
+		for _, ident := range allIdents(idNode.Args[0]) {
+			i := indexOfStr(ident, s, escFnsEq)
+			if i != -1 {
+				for _, name := range s[:i] {
+					newCmds = appendCmd(newCmds, newIdentCmd(name, pos))
+				}
+				s = s[i+1:]
+			}
+		}
+		newCmds = appendCmd(newCmds, idNode)
+	}
+	// Create any remaining sanitizers.
+	for _, name := range s {
+		newCmds = appendCmd(newCmds, newIdentCmd(name, p.Position()))
+	}
+	p.Cmds = newCmds
+}
+
+// redundantFuncs[a][b] implies that funcMap[b](funcMap[a](x)) == funcMap[a](x)
+// for all x.
+var redundantFuncs = map[string]map[string]bool{
+	"html_template_commentescaper": {
+		"html_template_attrescaper":    true,
+		"html_template_nospaceescaper": true,
+		"html_template_htmlescaper":    true,
+	},
+	"html_template_cssescaper": {
+		"html_template_attrescaper": true,
+	},
+	"html_template_jsregexpescaper": {
+		"html_template_attrescaper": true,
+	},
+	"html_template_jsstrescaper": {
+		"html_template_attrescaper": true,
+	},
+	"html_template_urlescaper": {
+		"html_template_urlnormalizer": true,
+	},
+}
+
+// appendCmd appends the given command to the end of the command pipeline
+// unless it is redundant with the last command.
+func appendCmd(cmds []*parse.CommandNode, cmd *parse.CommandNode) []*parse.CommandNode {
+	if n := len(cmds); n != 0 {
+		last, ok := cmds[n-1].Args[0].(*parse.IdentifierNode)
+		next, _ := cmd.Args[0].(*parse.IdentifierNode)
+		if ok && redundantFuncs[last.Ident][next.Ident] {
+			return cmds
+		}
+	}
+	return append(cmds, cmd)
+}
+
+// indexOfStr is the first i such that eq(s, strs[i]) or -1 if s was not found.
+func indexOfStr(s string, strs []string, eq func(a, b string) bool) int {
+	for i, t := range strs {
+		if eq(s, t) {
+			return i
+		}
+	}
+	return -1
+}
+
+// escFnsEq reports whether the two escaping functions are equivalent.
+func escFnsEq(a, b string) bool {
+	if e := equivEscapers[a]; e != "" {
+		a = e
+	}
+	if e := equivEscapers[b]; e != "" {
+		b = e
+	}
+	return a == b
+}
+
+// newIdentCmd produces a command containing a single identifier node.
+func newIdentCmd(identifier string, pos parse.Pos) *parse.CommandNode {
+	return &parse.CommandNode{
+		NodeType: parse.NodeCommand,
+		Args:     []parse.Node{parse.NewIdentifier(identifier).SetPos(pos)},
+	}
+}
+
+// nudge returns the context that would result from following empty string
+// transitions from the input context.
+// For example, parsing:
+//     `<a href=`
+// will end in context{stateBeforeValue, attrURL}, but parsing one extra rune:
+//     `<a href=x`
+// will end in context{stateURL, delimSpaceOrTagEnd, ...}.
+// There are two transitions that happen when the 'x' is seen:
+// (1) Transition from a before-value state to a start-of-value state without
+//     consuming any character.
+// (2) Consume 'x' and transition past the first value character.
+// In this case, nudging produces the context after (1) happens.
+func nudge(c context) context {
+	switch c.state {
+	case stateTag:
+		// In `<foo {{.}}`, the action should emit an attribute.
+		c.state = stateAttrName
+	case stateBeforeValue:
+		// In `<foo bar={{.}}`, the action is an undelimited value.
+		c.state, c.delim, c.attr = attrStartStates[c.attr], delimSpaceOrTagEnd, attrNone
+	case stateAfterName:
+		// In `<foo bar {{.}}`, the action is an attribute name.
+		c.state, c.attr = stateAttrName, attrNone
+	}
+	return c
+}
+
+// join joins the two contexts of a branch template node. The result is an
+// error context if either of the input contexts are error contexts, or if the
+// the input contexts differ.
+func join(a, b context, line int, nodeName string) context {
+	if a.state == stateError {
+		return a
+	}
+	if b.state == stateError {
+		return b
+	}
+	if a.eq(b) {
+		return a
+	}
+
+	c := a
+	c.urlPart = b.urlPart
+	if c.eq(b) {
+		// The contexts differ only by urlPart.
+		c.urlPart = urlPartUnknown
+		return c
+	}
+
+	c = a
+	c.jsCtx = b.jsCtx
+	if c.eq(b) {
+		// The contexts differ only by jsCtx.
+		c.jsCtx = jsCtxUnknown
+		return c
+	}
+
+	// Allow a nudged context to join with an unnudged one.
+	// This means that
+	//   <p title={{if .C}}{{.}}{{end}}
+	// ends in an unquoted value state even though the else branch
+	// ends in stateBeforeValue.
+	if c, d := nudge(a), nudge(b); !(c.eq(a) && d.eq(b)) {
+		if e := join(c, d, line, nodeName); e.state != stateError {
+			return e
+		}
+	}
+
+	return context{
+		state: stateError,
+		err:   errorf(ErrBranchEnd, line, "{{%s}} branches end in different contexts: %v, %v", nodeName, a, b),
+	}
+}
+
+// escapeBranch escapes a branch template node: "if", "range" and "with".
+func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string) context {
+	c0 := e.escapeList(c, n.List)
+	if nodeName == "range" && c0.state != stateError {
+		// The "true" branch of a "range" node can execute multiple times.
+		// We check that executing n.List once results in the same context
+		// as executing n.List twice.
+		c1, _ := e.escapeListConditionally(c0, n.List, nil)
+		c0 = join(c0, c1, n.Line, nodeName)
+		if c0.state == stateError {
+			// Make clear that this is a problem on loop re-entry
+			// since developers tend to overlook that branch when
+			// debugging templates.
+			c0.err.Line = n.Line
+			c0.err.Description = "on range loop re-entry: " + c0.err.Description
+			return c0
+		}
+	}
+	c1 := e.escapeList(c, n.ElseList)
+	return join(c0, c1, n.Line, nodeName)
+}
+
+// escapeList escapes a list template node.
+func (e *escaper) escapeList(c context, n *parse.ListNode) context {
+	if n == nil {
+		return c
+	}
+	for _, m := range n.Nodes {
+		c = e.escape(c, m)
+	}
+	return c
+}
+
+// escapeListConditionally escapes a list node but only preserves edits and
+// inferences in e if the inferences and output context satisfy filter.
+// It returns the best guess at an output context, and the result of the filter
+// which is the same as whether e was updated.
+func (e *escaper) escapeListConditionally(c context, n *parse.ListNode, filter func(*escaper, context) bool) (context, bool) {
+	e1 := newEscaper(e.tmpl)
+	// Make type inferences available to f.
+	for k, v := range e.output {
+		e1.output[k] = v
+	}
+	c = e1.escapeList(c, n)
+	ok := filter != nil && filter(e1, c)
+	if ok {
+		// Copy inferences and edits from e1 back into e.
+		for k, v := range e1.output {
+			e.output[k] = v
+		}
+		for k, v := range e1.derived {
+			e.derived[k] = v
+		}
+		for k, v := range e1.called {
+			e.called[k] = v
+		}
+		for k, v := range e1.actionNodeEdits {
+			e.editActionNode(k, v)
+		}
+		for k, v := range e1.templateNodeEdits {
+			e.editTemplateNode(k, v)
+		}
+		for k, v := range e1.textNodeEdits {
+			e.editTextNode(k, v)
+		}
+	}
+	return c, ok
+}
+
+// escapeTemplate escapes a {{template}} call node.
+func (e *escaper) escapeTemplate(c context, n *parse.TemplateNode) context {
+	c, name := e.escapeTree(c, n.Name, n.Line)
+	if name != n.Name {
+		e.editTemplateNode(n, name)
+	}
+	return c
+}
+
+// escapeTree escapes the named template starting in the given context as
+// necessary and returns its output context.
+func (e *escaper) escapeTree(c context, name string, line int) (context, string) {
+	// Mangle the template name with the input context to produce a reliable
+	// identifier.
+	dname := c.mangle(name)
+	e.called[dname] = true
+	if out, ok := e.output[dname]; ok {
+		// Already escaped.
+		return out, dname
+	}
+	t := e.template(name)
+	if t == nil {
+		// Two cases: The template exists but is empty, or has never been mentioned at
+		// all. Distinguish the cases in the error messages.
+		if e.tmpl.set[name] != nil {
+			return context{
+				state: stateError,
+				err:   errorf(ErrNoSuchTemplate, line, "%q is an incomplete or empty template", name),
+			}, dname
+		}
+		return context{
+			state: stateError,
+			err:   errorf(ErrNoSuchTemplate, line, "no such template %q", name),
+		}, dname
+	}
+	if dname != name {
+		// Use any template derived during an earlier call to escapeTemplate
+		// with different top level templates, or clone if necessary.
+		dt := e.template(dname)
+		if dt == nil {
+			dt = template.New(dname)
+			dt.Tree = &parse.Tree{Name: dname, Root: t.Root.CopyList()}
+			e.derived[dname] = dt
+		}
+		t = dt
+	}
+	return e.computeOutCtx(c, t), dname
+}
+
+// computeOutCtx takes a template and its start context and computes the output
+// context while storing any inferences in e.
+func (e *escaper) computeOutCtx(c context, t *template.Template) context {
+	// Propagate context over the body.
+	c1, ok := e.escapeTemplateBody(c, t)
+	if !ok {
+		// Look for a fixed point by assuming c1 as the output context.
+		if c2, ok2 := e.escapeTemplateBody(c1, t); ok2 {
+			c1, ok = c2, true
+		}
+		// Use c1 as the error context if neither assumption worked.
+	}
+	if !ok && c1.state != stateError {
+		return context{
+			state: stateError,
+			// TODO: Find the first node with a line in t.text.Tree.Root
+			err: errorf(ErrOutputContext, 0, "cannot compute output context for template %s", t.Name()),
+		}
+	}
+	return c1
+}
+
+// escapeTemplateBody escapes the given template assuming the given output
+// context, and returns the best guess at the output context and whether the
+// assumption was correct.
+func (e *escaper) escapeTemplateBody(c context, t *template.Template) (context, bool) {
+	filter := func(e1 *escaper, c1 context) bool {
+		if c1.state == stateError {
+			// Do not update the input escaper, e.
+			return false
+		}
+		if !e1.called[t.Name()] {
+			// If t is not recursively called, then c1 is an
+			// accurate output context.
+			return true
+		}
+		// c1 is accurate if it matches our assumed output context.
+		return c.eq(c1)
+	}
+	// We need to assume an output context so that recursive template calls
+	// take the fast path out of escapeTree instead of infinitely recursing.
+	// Naively assuming that the input context is the same as the output
+	// works >90% of the time.
+	e.output[t.Name()] = c
+	return e.escapeListConditionally(c, t.Tree.Root, filter)
+}
+
+// delimEnds maps each delim to a string of characters that terminate it.
+var delimEnds = [...]string{
+	delimDoubleQuote: `"`,
+	delimSingleQuote: "'",
+	// Determined empirically by running the below in various browsers.
+	// var div = document.createElement("DIV");
+	// for (var i = 0; i < 0x10000; ++i) {
+	//   div.innerHTML = "<span title=x" + String.fromCharCode(i) + "-bar>";
+	//   if (div.getElementsByTagName("SPAN")[0].title.indexOf("bar") < 0)
+	//     document.write("<p>U+" + i.toString(16));
+	// }
+	delimSpaceOrTagEnd: " \t\n\f\r>",
+}
+
+var doctypeBytes = []byte("<!DOCTYPE")
+
+// escapeText escapes a text template node.
+func (e *escaper) escapeText(c context, n *parse.TextNode) context {
+	s, written, i, b := n.Text, 0, 0, new(bytes.Buffer)
+	for i != len(s) {
+		c1, nread := contextAfterText(c, s[i:])
+		i1 := i + nread
+		if c.state == stateText || c.state == stateRCDATA {
+			end := i1
+			if c1.state != c.state {
+				for j := end - 1; j >= i; j-- {
+					if s[j] == '<' {
+						end = j
+						break
+					}
+				}
+			}
+			for j := i; j < end; j++ {
+				if s[j] == '<' && !bytes.HasPrefix(bytes.ToUpper(s[j:]), doctypeBytes) {
+					b.Write(s[written:j])
+					b.WriteString("&lt;")
+					written = j + 1
+				}
+			}
+		} else if isComment(c.state) && c.delim == delimNone {
+			switch c.state {
+			case stateJSBlockCmt:
+				// http://es5.github.com/#x7.4:
+				// "Comments behave like white space and are
+				// discarded except that, if a MultiLineComment
+				// contains a line terminator character, then
+				// the entire comment is considered to be a
+				// LineTerminator for purposes of parsing by
+				// the syntactic grammar."
+				if bytes.IndexAny(s[written:i1], "\n\r\u2028\u2029") != -1 {
+					b.WriteByte('\n')
+				} else {
+					b.WriteByte(' ')
+				}
+			case stateCSSBlockCmt:
+				b.WriteByte(' ')
+			}
+			written = i1
+		}
+		if c.state != c1.state && isComment(c1.state) && c1.delim == delimNone {
+			// Preserve the portion between written and the comment start.
+			cs := i1 - 2
+			if c1.state == stateHTMLCmt {
+				// "<!--" instead of "/*" or "//"
+				cs -= 2
+			}
+			b.Write(s[written:cs])
+			written = i1
+		}
+		if i == i1 && c.state == c1.state {
+			panic(fmt.Sprintf("infinite loop from %v to %v on %q..%q", c, c1, s[:i], s[i:]))
+		}
+		c, i = c1, i1
+	}
+
+	if written != 0 && c.state != stateError {
+		if !isComment(c.state) || c.delim != delimNone {
+			b.Write(n.Text[written:])
+		}
+		e.editTextNode(n, b.Bytes())
+	}
+	return c
+}
+
+// contextAfterText starts in context c, consumes some tokens from the front of
+// s, then returns the context after those tokens and the unprocessed suffix.
+func contextAfterText(c context, s []byte) (context, int) {
+	if c.delim == delimNone {
+		c1, i := tSpecialTagEnd(c, s)
+		if i == 0 {
+			// A special end tag (`</script>`) has been seen and
+			// all content preceding it has been consumed.
+			return c1, 0
+		}
+		// Consider all content up to any end tag.
+		return transitionFunc[c.state](c, s[:i])
+	}
+
+	i := bytes.IndexAny(s, delimEnds[c.delim])
+	if i == -1 {
+		i = len(s)
+	}
+	if c.delim == delimSpaceOrTagEnd {
+		// http://www.w3.org/TR/html5/syntax.html#attribute-value-(unquoted)-state
+		// lists the runes below as error characters.
+		// Error out because HTML parsers may differ on whether
+		// "<a id= onclick=f("     ends inside id's or onclick's value,
+		// "<a class=`foo "        ends inside a value,
+		// "<a style=font:'Arial'" needs open-quote fixup.
+		// IE treats '`' as a quotation character.
+		if j := bytes.IndexAny(s[:i], "\"'<=`"); j >= 0 {
+			return context{
+				state: stateError,
+				err:   errorf(ErrBadHTML, 0, "%q in unquoted attr: %q", s[j:j+1], s[:i]),
+			}, len(s)
+		}
+	}
+	if i == len(s) {
+		// Remain inside the attribute.
+		// Decode the value so non-HTML rules can easily handle
+		//     <button onclick="alert(&quot;Hi!&quot;)">
+		// without having to entity decode token boundaries.
+		for u := []byte(html.UnescapeString(string(s))); len(u) != 0; {
+			c1, i1 := transitionFunc[c.state](c, u)
+			c, u = c1, u[i1:]
+		}
+		return c, len(s)
+	}
+	if c.delim != delimSpaceOrTagEnd {
+		// Consume any quote.
+		i++
+	}
+	// On exiting an attribute, we discard all state information
+	// except the state and element.
+	return context{state: stateTag, element: c.element}, i
+}
+
+// editActionNode records a change to an action pipeline for later commit.
+func (e *escaper) editActionNode(n *parse.ActionNode, cmds []string) {
+	if _, ok := e.actionNodeEdits[n]; ok {
+		panic(fmt.Sprintf("node %s shared between templates", n))
+	}
+	e.actionNodeEdits[n] = cmds
+}
+
+// editTemplateNode records a change to a {{template}} callee for later commit.
+func (e *escaper) editTemplateNode(n *parse.TemplateNode, callee string) {
+	if _, ok := e.templateNodeEdits[n]; ok {
+		panic(fmt.Sprintf("node %s shared between templates", n))
+	}
+	e.templateNodeEdits[n] = callee
+}
+
+// editTextNode records a change to a text node for later commit.
+func (e *escaper) editTextNode(n *parse.TextNode, text []byte) {
+	if _, ok := e.textNodeEdits[n]; ok {
+		panic(fmt.Sprintf("node %s shared between templates", n))
+	}
+	e.textNodeEdits[n] = text
+}
+
+// commit applies changes to actions and template calls needed to contextually
+// autoescape content and adds any derived templates to the set.
+func (e *escaper) commit() {
+	for name := range e.output {
+		e.template(name).Funcs(funcMap)
+	}
+	for _, t := range e.derived {
+		if _, err := e.tmpl.text.AddParseTree(t.Name(), t.Tree); err != nil {
+			panic("error adding derived template")
+		}
+	}
+	for n, s := range e.actionNodeEdits {
+		ensurePipelineContains(n.Pipe, s)
+	}
+	for n, name := range e.templateNodeEdits {
+		n.Name = name
+	}
+	for n, s := range e.textNodeEdits {
+		n.Text = s
+	}
+}
+
+// template returns the named template given a mangled template name.
+func (e *escaper) template(name string) *template.Template {
+	t := e.tmpl.text.Lookup(name)
+	if t == nil {
+		t = e.derived[name]
+	}
+	return t
+}
+
+// Forwarding functions so that clients need only import this package
+// to reach the general escaping functions of text/template.
+
+// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
+func HTMLEscape(w io.Writer, b []byte) {
+	template.HTMLEscape(w, b)
+}
+
+// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
+func HTMLEscapeString(s string) string {
+	return template.HTMLEscapeString(s)
+}
+
+// HTMLEscaper returns the escaped HTML equivalent of the textual
+// representation of its arguments.
+func HTMLEscaper(args ...interface{}) string {
+	return template.HTMLEscaper(args...)
+}
+
+// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
+func JSEscape(w io.Writer, b []byte) {
+	template.JSEscape(w, b)
+}
+
+// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
+func JSEscapeString(s string) string {
+	return template.JSEscapeString(s)
+}
+
+// JSEscaper returns the escaped JavaScript equivalent of the textual
+// representation of its arguments.
+func JSEscaper(args ...interface{}) string {
+	return template.JSEscaper(args...)
+}
+
+// URLQueryEscaper returns the escaped value of the textual representation of
+// its arguments in a form suitable for embedding in a URL query.
+func URLQueryEscaper(args ...interface{}) string {
+	return template.URLQueryEscaper(args...)
+}
+
+// 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.
+
+// transitionFunc is the array of context transition functions for text nodes.
+// A transition function takes a context and template text input, and returns
+// the updated context and the number of bytes consumed from the front of the
+// input.
+var transitionFunc = [...]func(context, []byte) (context, int){
+	stateText:        tText,
+	stateTag:         tTag,
+	stateAttrName:    tAttrName,
+	stateAfterName:   tAfterName,
+	stateBeforeValue: tBeforeValue,
+	stateHTMLCmt:     tHTMLCmt,
+	stateRCDATA:      tSpecialTagEnd,
+	stateAttr:        tAttr,
+	stateURL:         tURL,
+	stateJS:          tJS,
+	stateJSDqStr:     tJSDelimited,
+	stateJSSqStr:     tJSDelimited,
+	stateJSRegexp:    tJSDelimited,
+	stateJSBlockCmt:  tBlockCmt,
+	stateJSLineCmt:   tLineCmt,
+	stateCSS:         tCSS,
+	stateCSSDqStr:    tCSSStr,
+	stateCSSSqStr:    tCSSStr,
+	stateCSSDqURL:    tCSSStr,
+	stateCSSSqURL:    tCSSStr,
+	stateCSSURL:      tCSSStr,
+	stateCSSBlockCmt: tBlockCmt,
+	stateCSSLineCmt:  tLineCmt,
+	stateError:       tError,
+}
+
+var commentStart = []byte("<!--")
+var commentEnd = []byte("-->")
+
+// tText is the context transition function for the text state.
+func tText(c context, s []byte) (context, int) {
+	k := 0
+	for {
+		i := k + bytes.IndexByte(s[k:], '<')
+		if i < k || i+1 == len(s) {
+			return c, len(s)
+		} else if i+4 <= len(s) && bytes.Equal(commentStart, s[i:i+4]) {
+			return context{state: stateHTMLCmt}, i + 4
+		}
+		i++
+		end := false
+		if s[i] == '/' {
+			if i+1 == len(s) {
+				return c, len(s)
+			}
+			end, i = true, i+1
+		}
+		j, e := eatTagName(s, i)
+		if j != i {
+			if end {
+				e = elementNone
+			}
+			// We've found an HTML tag.
+			return context{state: stateTag, element: e}, j
+		}
+		k = j
+	}
+}
+
+var elementContentType = [...]state{
+	elementNone:     stateText,
+	elementScript:   stateJS,
+	elementStyle:    stateCSS,
+	elementTextarea: stateRCDATA,
+	elementTitle:    stateRCDATA,
+}
+
+// tTag is the context transition function for the tag state.
+func tTag(c context, s []byte) (context, int) {
+	// Find the attribute name.
+	i := eatWhiteSpace(s, 0)
+	if i == len(s) {
+		return c, len(s)
+	}
+	if s[i] == '>' {
+		return context{
+			state:   elementContentType[c.element],
+			element: c.element,
+		}, i + 1
+	}
+	j, err := eatAttrName(s, i)
+	if err != nil {
+		return context{state: stateError, err: err}, len(s)
+	}
+	state, attr := stateTag, attrNone
+	if i == j {
+		return context{
+			state: stateError,
+			err:   errorf(ErrBadHTML, 0, "expected space, attr name, or end of tag, but got %q", s[i:]),
+		}, len(s)
+	}
+	switch attrType(string(s[i:j])) {
+	case contentTypeURL:
+		attr = attrURL
+	case contentTypeCSS:
+		attr = attrStyle
+	case contentTypeJS:
+		attr = attrScript
+	}
+	if j == len(s) {
+		state = stateAttrName
+	} else {
+		state = stateAfterName
+	}
+	return context{state: state, element: c.element, attr: attr}, j
+}
+
+// tAttrName is the context transition function for stateAttrName.
+func tAttrName(c context, s []byte) (context, int) {
+	i, err := eatAttrName(s, 0)
+	if err != nil {
+		return context{state: stateError, err: err}, len(s)
+	} else if i != len(s) {
+		c.state = stateAfterName
+	}
+	return c, i
+}
+
+// tAfterName is the context transition function for stateAfterName.
+func tAfterName(c context, s []byte) (context, int) {
+	// Look for the start of the value.
+	i := eatWhiteSpace(s, 0)
+	if i == len(s) {
+		return c, len(s)
+	} else if s[i] != '=' {
+		// Occurs due to tag ending '>', and valueless attribute.
+		c.state = stateTag
+		return c, i
+	}
+	c.state = stateBeforeValue
+	// Consume the "=".
+	return c, i + 1
+}
+
+var attrStartStates = [...]state{
+	attrNone:   stateAttr,
+	attrScript: stateJS,
+	attrStyle:  stateCSS,
+	attrURL:    stateURL,
+}
+
+// tBeforeValue is the context transition function for stateBeforeValue.
+func tBeforeValue(c context, s []byte) (context, int) {
+	i := eatWhiteSpace(s, 0)
+	if i == len(s) {
+		return c, len(s)
+	}
+	// Find the attribute delimiter.
+	delim := delimSpaceOrTagEnd
+	switch s[i] {
+	case '\'':
+		delim, i = delimSingleQuote, i+1
+	case '"':
+		delim, i = delimDoubleQuote, i+1
+	}
+	c.state, c.delim, c.attr = attrStartStates[c.attr], delim, attrNone
+	return c, i
+}
+
+// tHTMLCmt is the context transition function for stateHTMLCmt.
+func tHTMLCmt(c context, s []byte) (context, int) {
+	if i := bytes.Index(s, commentEnd); i != -1 {
+		return context{}, i + 3
+	}
+	return c, len(s)
+}
+
+// specialTagEndMarkers maps element types to the character sequence that
+// case-insensitively signals the end of the special tag body.
+var specialTagEndMarkers = [...]string{
+	elementScript:   "</script",
+	elementStyle:    "</style",
+	elementTextarea: "</textarea",
+	elementTitle:    "</title",
+}
+
+// tSpecialTagEnd is the context transition function for raw text and RCDATA
+// element states.
+func tSpecialTagEnd(c context, s []byte) (context, int) {
+	if c.element != elementNone {
+		if i := strings.Index(strings.ToLower(string(s)), specialTagEndMarkers[c.element]); i != -1 {
+			return context{}, i
+		}
+	}
+	return c, len(s)
+}
+
+// tAttr is the context transition function for the attribute state.
+func tAttr(c context, s []byte) (context, int) {
+	return c, len(s)
+}
+
+// tURL is the context transition function for the URL state.
+func tURL(c context, s []byte) (context, int) {
+	if bytes.IndexAny(s, "#?") >= 0 {
+		c.urlPart = urlPartQueryOrFrag
+	} else if len(s) != eatWhiteSpace(s, 0) && c.urlPart == urlPartNone {
+		// HTML5 uses "Valid URL potentially surrounded by spaces" for
+		// attrs: http://www.w3.org/TR/html5/index.html#attributes-1
+		c.urlPart = urlPartPreQuery
+	}
+	return c, len(s)
+}
+
+// tJS is the context transition function for the JS state.
+func tJS(c context, s []byte) (context, int) {
+	i := bytes.IndexAny(s, `"'/`)
+	if i == -1 {
+		// Entire input is non string, comment, regexp tokens.
+		c.jsCtx = nextJSCtx(s, c.jsCtx)
+		return c, len(s)
+	}
+	c.jsCtx = nextJSCtx(s[:i], c.jsCtx)
+	switch s[i] {
+	case '"':
+		c.state, c.jsCtx = stateJSDqStr, jsCtxRegexp
+	case '\'':
+		c.state, c.jsCtx = stateJSSqStr, jsCtxRegexp
+	case '/':
+		switch {
+		case i+1 < len(s) && s[i+1] == '/':
+			c.state, i = stateJSLineCmt, i+1
+		case i+1 < len(s) && s[i+1] == '*':
+			c.state, i = stateJSBlockCmt, i+1
+		case c.jsCtx == jsCtxRegexp:
+			c.state = stateJSRegexp
+		case c.jsCtx == jsCtxDivOp:
+			c.jsCtx = jsCtxRegexp
+		default:
+			return context{
+				state: stateError,
+				err:   errorf(ErrSlashAmbig, 0, "'/' could start a division or regexp: %.32q", s[i:]),
+			}, len(s)
+		}
+	default:
+		panic("unreachable")
+	}
+	return c, i + 1
+}
+
+// tJSDelimited is the context transition function for the JS string and regexp
+// states.
+func tJSDelimited(c context, s []byte) (context, int) {
+	specials := `\"`
+	switch c.state {
+	case stateJSSqStr:
+		specials = `\'`
+	case stateJSRegexp:
+		specials = `\/[]`
+	}
+
+	k, inCharset := 0, false
+	for {
+		i := k + bytes.IndexAny(s[k:], specials)
+		if i < k {
+			break
+		}
+		switch s[i] {
+		case '\\':
+			i++
+			if i == len(s) {
+				return context{
+					state: stateError,
+					err:   errorf(ErrPartialEscape, 0, "unfinished escape sequence in JS string: %q", s),
+				}, len(s)
+			}
+		case '[':
+			inCharset = true
+		case ']':
+			inCharset = false
+		default:
+			// end delimiter
+			if !inCharset {
+				c.state, c.jsCtx = stateJS, jsCtxDivOp
+				return c, i + 1
+			}
+		}
+		k = i + 1
+	}
+
+	if inCharset {
+		// This can be fixed by making context richer if interpolation
+		// into charsets is desired.
+		return context{
+			state: stateError,
+			err:   errorf(ErrPartialCharset, 0, "unfinished JS regexp charset: %q", s),
+		}, len(s)
+	}
+
+	return c, len(s)
+}
+
+var blockCommentEnd = []byte("*/")
+
+// tBlockCmt is the context transition function for /*comment*/ states.
+func tBlockCmt(c context, s []byte) (context, int) {
+	i := bytes.Index(s, blockCommentEnd)
+	if i == -1 {
+		return c, len(s)
+	}
+	switch c.state {
+	case stateJSBlockCmt:
+		c.state = stateJS
+	case stateCSSBlockCmt:
+		c.state = stateCSS
+	default:
+		panic(c.state.String())
+	}
+	return c, i + 2
+}
+
+// tLineCmt is the context transition function for //comment states.
+func tLineCmt(c context, s []byte) (context, int) {
+	var lineTerminators string
+	var endState state
+	switch c.state {
+	case stateJSLineCmt:
+		lineTerminators, endState = "\n\r\u2028\u2029", stateJS
+	case stateCSSLineCmt:
+		lineTerminators, endState = "\n\f\r", stateCSS
+		// Line comments are not part of any published CSS standard but
+		// are supported by the 4 major browsers.
+		// This defines line comments as
+		//     LINECOMMENT ::= "//" [^\n\f\d]*
+		// since http://www.w3.org/TR/css3-syntax/#SUBTOK-nl defines
+		// newlines:
+		//     nl ::= #xA | #xD #xA | #xD | #xC
+	default:
+		panic(c.state.String())
+	}
+
+	i := bytes.IndexAny(s, lineTerminators)
+	if i == -1 {
+		return c, len(s)
+	}
+	c.state = endState
+	// Per section 7.4 of EcmaScript 5 : http://es5.github.com/#x7.4
+	// "However, the LineTerminator at the end of the line is not
+	// considered to be part of the single-line comment; it is
+	// recognized separately by the lexical grammar and becomes part
+	// of the stream of input elements for the syntactic grammar."
+	return c, i
+}
+
+// tCSS is the context transition function for the CSS state.
+func tCSS(c context, s []byte) (context, int) {
+	// CSS quoted strings are almost never used except for:
+	// (1) URLs as in background: "/foo.png"
+	// (2) Multiword font-names as in font-family: "Times New Roman"
+	// (3) List separators in content values as in inline-lists:
+	//    <style>
+	//    ul.inlineList { list-style: none; padding:0 }
+	//    ul.inlineList > li { display: inline }
+	//    ul.inlineList > li:before { content: ", " }
+	//    ul.inlineList > li:first-child:before { content: "" }
+	//    </style>
+	//    <ul class=inlineList><li>One<li>Two<li>Three</ul>
+	// (4) Attribute value selectors as in a[href="http://example.com/"]
+	//
+	// We conservatively treat all strings as URLs, but make some
+	// allowances to avoid confusion.
+	//
+	// In (1), our conservative assumption is justified.
+	// In (2), valid font names do not contain ':', '?', or '#', so our
+	// conservative assumption is fine since we will never transition past
+	// urlPartPreQuery.
+	// In (3), our protocol heuristic should not be tripped, and there
+	// should not be non-space content after a '?' or '#', so as long as
+	// we only %-encode RFC 3986 reserved characters we are ok.
+	// In (4), we should URL escape for URL attributes, and for others we
+	// have the attribute name available if our conservative assumption
+	// proves problematic for real code.
+
+	k := 0
+	for {
+		i := k + bytes.IndexAny(s[k:], `("'/`)
+		if i < k {
+			return c, len(s)
+		}
+		switch s[i] {
+		case '(':
+			// Look for url to the left.
+			p := bytes.TrimRight(s[:i], "\t\n\f\r ")
+			if endsWithCSSKeyword(p, "url") {
+				j := len(s) - len(bytes.TrimLeft(s[i+1:], "\t\n\f\r "))
+				switch {
+				case j != len(s) && s[j] == '"':
+					c.state, j = stateCSSDqURL, j+1
+				case j != len(s) && s[j] == '\'':
+					c.state, j = stateCSSSqURL, j+1
+				default:
+					c.state = stateCSSURL
+				}
+				return c, j
+			}
+		case '/':
+			if i+1 < len(s) {
+				switch s[i+1] {
+				case '/':
+					c.state = stateCSSLineCmt
+					return c, i + 2
+				case '*':
+					c.state = stateCSSBlockCmt
+					return c, i + 2
+				}
+			}
+		case '"':
+			c.state = stateCSSDqStr
+			return c, i + 1
+		case '\'':
+			c.state = stateCSSSqStr
+			return c, i + 1
+		}
+		k = i + 1
+	}
+}
+
+// tCSSStr is the context transition function for the CSS string and URL states.
+func tCSSStr(c context, s []byte) (context, int) {
+	var endAndEsc string
+	switch c.state {
+	case stateCSSDqStr, stateCSSDqURL:
+		endAndEsc = `\"`
+	case stateCSSSqStr, stateCSSSqURL:
+		endAndEsc = `\'`
+	case stateCSSURL:
+		// Unquoted URLs end with a newline or close parenthesis.
+		// The below includes the wc (whitespace character) and nl.
+		endAndEsc = "\\\t\n\f\r )"
+	default:
+		panic(c.state.String())
+	}
+
+	k := 0
+	for {
+		i := k + bytes.IndexAny(s[k:], endAndEsc)
+		if i < k {
+			c, nread := tURL(c, decodeCSS(s[k:]))
+			return c, k + nread
+		}
+		if s[i] == '\\' {
+			i++
+			if i == len(s) {
+				return context{
+					state: stateError,
+					err:   errorf(ErrPartialEscape, 0, "unfinished escape sequence in CSS string: %q", s),
+				}, len(s)
+			}
+		} else {
+			c.state = stateCSS
+			return c, i + 1
+		}
+		c, _ = tURL(c, decodeCSS(s[:i+1]))
+		k = i + 1
+	}
+}
+
+// tError is the context transition function for the error state.
+func tError(c context, s []byte) (context, int) {
+	return c, len(s)
+}
+
+// eatAttrName returns the largest j such that s[i:j] is an attribute name.
+// It returns an error if s[i:] does not look like it begins with an
+// attribute name, such as encountering a quote mark without a preceding
+// equals sign.
+func eatAttrName(s []byte, i int) (int, *Error) {
+	for j := i; j < len(s); j++ {
+		switch s[j] {
+		case ' ', '\t', '\n', '\f', '\r', '=', '>':
+			return j, nil
+		case '\'', '"', '<':
+			// These result in a parse warning in HTML5 and are
+			// indicative of serious problems if seen in an attr
+			// name in a template.
+			return -1, errorf(ErrBadHTML, 0, "%q in attribute name: %.32q", s[j:j+1], s)
+		default:
+			// No-op.
+		}
+	}
+	return len(s), nil
+}
+
+var elementNameMap = map[string]element{
+	"script":   elementScript,
+	"style":    elementStyle,
+	"textarea": elementTextarea,
+	"title":    elementTitle,
+}
+
+// asciiAlpha reports whether c is an ASCII letter.
+func asciiAlpha(c byte) bool {
+	return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z'
+}
+
+// asciiAlphaNum reports whether c is an ASCII letter or digit.
+func asciiAlphaNum(c byte) bool {
+	return asciiAlpha(c) || '0' <= c && c <= '9'
+}
+
+// eatTagName returns the largest j such that s[i:j] is a tag name and the tag type.
+func eatTagName(s []byte, i int) (int, element) {
+	if i == len(s) || !asciiAlpha(s[i]) {
+		return i, elementNone
+	}
+	j := i + 1
+	for j < len(s) {
+		x := s[j]
+		if asciiAlphaNum(x) {
+			j++
+			continue
+		}
+		// Allow "x-y" or "x:y" but not "x-", "-y", or "x--y".
+		if (x == ':' || x == '-') && j+1 < len(s) && asciiAlphaNum(s[j+1]) {
+			j += 2
+			continue
+		}
+		break
+	}
+	return j, elementNameMap[strings.ToLower(string(s[i:j]))]
+}
+
+// eatWhiteSpace returns the largest j such that s[i:j] is white space.
+func eatWhiteSpace(s []byte, i int) int {
+	for j := i; j < len(s); j++ {
+		switch s[j] {
+		case ' ', '\t', '\n', '\f', '\r':
+			// No-op.
+		default:
+			return j
+		}
+	}
+	return len(s)
+}
+
+// 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.
+
+// Error describes a problem encountered during template Escaping.
+type Error struct {
+	// ErrorCode describes the kind of error.
+	ErrorCode ErrorCode
+	// Name is the name of the template in which the error was encountered.
+	Name string
+	// Line is the line number of the error in the template source or 0.
+	Line int
+	// Description is a human-readable description of the problem.
+	Description string
+}
+
+// ErrorCode is a code for a kind of error.
+type ErrorCode int
+
+// We define codes for each error that manifests while escaping templates, but
+// escaped templates may also fail at runtime.
+//
+// Output: "ZgotmplZ"
+// Example:
+//   <img src="{{.X}}">
+//   where {{.X}} evaluates to `javascript:...`
+// Discussion:
+//   "ZgotmplZ" is a special value that indicates that unsafe content reached a
+//   CSS or URL context at runtime. The output of the example will be
+//     <img src="#ZgotmplZ">
+//   If the data comes from a trusted source, use content types to exempt it
+//   from filtering: URL(`javascript:...`).
+const (
+	// OK indicates the lack of an error.
+	OK ErrorCode = iota
+
+	// ErrAmbigContext: "... appears in an ambiguous URL context"
+	// Example:
+	//   <a href="
+	//      {{if .C}}
+	//        /path/
+	//      {{else}}
+	//        /search?q=
+	//      {{end}}
+	//      {{.X}}
+	//   ">
+	// Discussion:
+	//   {{.X}} is in an ambiguous URL context since, depending on {{.C}},
+	//  it may be either a URL suffix or a query parameter.
+	//   Moving {{.X}} into the condition removes the ambiguity:
+	//   <a href="{{if .C}}/path/{{.X}}{{else}}/search?q={{.X}}">
+	ErrAmbigContext
+
+	// ErrBadHTML: "expected space, attr name, or end of tag, but got ...",
+	//   "... in unquoted attr", "... in attribute name"
+	// Example:
+	//   <a href = /search?q=foo>
+	//   <href=foo>
+	//   <form na<e=...>
+	//   <option selected<
+	// Discussion:
+	//   This is often due to a typo in an HTML element, but some runes
+	//   are banned in tag names, attribute names, and unquoted attribute
+	//   values because they can tickle parser ambiguities.
+	//   Quoting all attributes is the best policy.
+	ErrBadHTML
+
+	// ErrBranchEnd: "{{if}} branches end in different contexts"
+	// Example:
+	//   {{if .C}}<a href="{{end}}{{.X}}
+	// Discussion:
+	//   Package html/template statically examines each path through an
+	//   {{if}}, {{range}}, or {{with}} to escape any following pipelines.
+	//   The example is ambiguous since {{.X}} might be an HTML text node,
+	//   or a URL prefix in an HTML attribute. The context of {{.X}} is
+	//   used to figure out how to escape it, but that context depends on
+	//   the run-time value of {{.C}} which is not statically known.
+	//
+	//   The problem is usually something like missing quotes or angle
+	//   brackets, or can be avoided by refactoring to put the two contexts
+	//   into different branches of an if, range or with. If the problem
+	//   is in a {{range}} over a collection that should never be empty,
+	//   adding a dummy {{else}} can help.
+	ErrBranchEnd
+
+	// ErrEndContext: "... ends in a non-text context: ..."
+	// Examples:
+	//   <div
+	//   <div title="no close quote>
+	//   <script>f()
+	// Discussion:
+	//   Executed templates should produce a DocumentFragment of HTML.
+	//   Templates that end without closing tags will trigger this error.
+	//   Templates that should not be used in an HTML context or that
+	//   produce incomplete Fragments should not be executed directly.
+	//
+	//   {{define "main"}} <script>{{template "helper"}}</script> {{end}}
+	//   {{define "helper"}} document.write(' <div title=" ') {{end}}
+	//
+	//   "helper" does not produce a valid document fragment, so should
+	//   not be Executed directly.
+	ErrEndContext
+
+	// ErrNoSuchTemplate: "no such template ..."
+	// Examples:
+	//   {{define "main"}}<div {{template "attrs"}}>{{end}}
+	//   {{define "attrs"}}href="{{.URL}}"{{end}}
+	// Discussion:
+	//   Package html/template looks through template calls to compute the
+	//   context.
+	//   Here the {{.URL}} in "attrs" must be treated as a URL when called
+	//   from "main", but you will get this error if "attrs" is not defined
+	//   when "main" is parsed.
+	ErrNoSuchTemplate
+
+	// ErrOutputContext: "cannot compute output context for template ..."
+	// Examples:
+	//   {{define "t"}}{{if .T}}{{template "t" .T}}{{end}}{{.H}}",{{end}}
+	// Discussion:
+	//   A recursive template does not end in the same context in which it
+	//   starts, and a reliable output context cannot be computed.
+	//   Look for typos in the named template.
+	//   If the template should not be called in the named start context,
+	//   look for calls to that template in unexpected contexts.
+	//   Maybe refactor recursive templates to not be recursive.
+	ErrOutputContext
+
+	// ErrPartialCharset: "unfinished JS regexp charset in ..."
+	// Example:
+	//     <script>var pattern = /foo[{{.Chars}}]/</script>
+	// Discussion:
+	//   Package html/template does not support interpolation into regular
+	//   expression literal character sets.
+	ErrPartialCharset
+
+	// ErrPartialEscape: "unfinished escape sequence in ..."
+	// Example:
+	//   <script>alert("\{{.X}}")</script>
+	// Discussion:
+	//   Package html/template does not support actions following a
+	//   backslash.
+	//   This is usually an error and there are better solutions; for
+	//   example
+	//     <script>alert("{{.X}}")</script>
+	//   should work, and if {{.X}} is a partial escape sequence such as
+	//   "xA0", mark the whole sequence as safe content: JSStr(`\xA0`)
+	ErrPartialEscape
+
+	// ErrRangeLoopReentry: "on range loop re-entry: ..."
+	// Example:
+	//   <script>var x = [{{range .}}'{{.}},{{end}}]</script>
+	// Discussion:
+	//   If an iteration through a range would cause it to end in a
+	//   different context than an earlier pass, there is no single context.
+	//   In the example, there is missing a quote, so it is not clear
+	//   whether {{.}} is meant to be inside a JS string or in a JS value
+	//   context.  The second iteration would produce something like
+	//
+	//     <script>var x = ['firstValue,'secondValue]</script>
+	ErrRangeLoopReentry
+
+	// ErrSlashAmbig: '/' could start a division or regexp.
+	// Example:
+	//   <script>
+	//     {{if .C}}var x = 1{{end}}
+	//     /-{{.N}}/i.test(x) ? doThis : doThat();
+	//   </script>
+	// Discussion:
+	//   The example above could produce `var x = 1/-2/i.test(s)...`
+	//   in which the first '/' is a mathematical division operator or it
+	//   could produce `/-2/i.test(s)` in which the first '/' starts a
+	//   regexp literal.
+	//   Look for missing semicolons inside branches, and maybe add
+	//   parentheses to make it clear which interpretation you intend.
+	ErrSlashAmbig
+)
+
+func (e *Error) Error() string {
+	if e.Line != 0 {
+		return fmt.Sprintf("html/template:%s:%d: %s", e.Name, e.Line, e.Description)
+	} else if e.Name != "" {
+		return fmt.Sprintf("html/template:%s: %s", e.Name, e.Description)
+	}
+	return "html/template: " + e.Description
+}
+
+// errorf creates an error given a format string f and args.
+// The template Name still needs to be supplied.
+func errorf(k ErrorCode, line int, f string, args ...interface{}) *Error {
+	return &Error{k, "", line, fmt.Sprintf(f, args...)}
+}
+
+// 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.
+
+// attrTypeMap[n] describes the value of the given attribute.
+// If an attribute affects (or can mask) the encoding or interpretation of
+// other content, or affects the contents, idempotency, or credentials of a
+// network message, then the value in this map is contentTypeUnsafe.
+// This map is derived from HTML5, specifically
+// http://www.w3.org/TR/html5/Overview.html#attributes-1
+// as well as "%URI"-typed attributes from
+// http://www.w3.org/TR/html4/index/attributes.html
+var attrTypeMap = map[string]contentType{
+	"accept":          contentTypePlain,
+	"accept-charset":  contentTypeUnsafe,
+	"action":          contentTypeURL,
+	"alt":             contentTypePlain,
+	"archive":         contentTypeURL,
+	"async":           contentTypeUnsafe,
+	"autocomplete":    contentTypePlain,
+	"autofocus":       contentTypePlain,
+	"autoplay":        contentTypePlain,
+	"background":      contentTypeURL,
+	"border":          contentTypePlain,
+	"checked":         contentTypePlain,
+	"cite":            contentTypeURL,
+	"challenge":       contentTypeUnsafe,
+	"charset":         contentTypeUnsafe,
+	"class":           contentTypePlain,
+	"classid":         contentTypeURL,
+	"codebase":        contentTypeURL,
+	"cols":            contentTypePlain,
+	"colspan":         contentTypePlain,
+	"content":         contentTypeUnsafe,
+	"contenteditable": contentTypePlain,
+	"contextmenu":     contentTypePlain,
+	"controls":        contentTypePlain,
+	"coords":          contentTypePlain,
+	"crossorigin":     contentTypeUnsafe,
+	"data":            contentTypeURL,
+	"datetime":        contentTypePlain,
+	"default":         contentTypePlain,
+	"defer":           contentTypeUnsafe,
+	"dir":             contentTypePlain,
+	"dirname":         contentTypePlain,
+	"disabled":        contentTypePlain,
+	"draggable":       contentTypePlain,
+	"dropzone":        contentTypePlain,
+	"enctype":         contentTypeUnsafe,
+	"for":             contentTypePlain,
+	"form":            contentTypeUnsafe,
+	"formaction":      contentTypeURL,
+	"formenctype":     contentTypeUnsafe,
+	"formmethod":      contentTypeUnsafe,
+	"formnovalidate":  contentTypeUnsafe,
+	"formtarget":      contentTypePlain,
+	"headers":         contentTypePlain,
+	"height":          contentTypePlain,
+	"hidden":          contentTypePlain,
+	"high":            contentTypePlain,
+	"href":            contentTypeURL,
+	"hreflang":        contentTypePlain,
+	"http-equiv":      contentTypeUnsafe,
+	"icon":            contentTypeURL,
+	"id":              contentTypePlain,
+	"ismap":           contentTypePlain,
+	"keytype":         contentTypeUnsafe,
+	"kind":            contentTypePlain,
+	"label":           contentTypePlain,
+	"lang":            contentTypePlain,
+	"language":        contentTypeUnsafe,
+	"list":            contentTypePlain,
+	"longdesc":        contentTypeURL,
+	"loop":            contentTypePlain,
+	"low":             contentTypePlain,
+	"manifest":        contentTypeURL,
+	"max":             contentTypePlain,
+	"maxlength":       contentTypePlain,
+	"media":           contentTypePlain,
+	"mediagroup":      contentTypePlain,
+	"method":          contentTypeUnsafe,
+	"min":             contentTypePlain,
+	"multiple":        contentTypePlain,
+	"name":            contentTypePlain,
+	"novalidate":      contentTypeUnsafe,
+	// Skip handler names from
+	// http://www.w3.org/TR/html5/webappapis.html#event-handlers-on-elements,-document-objects,-and-window-objects
+	// since we have special handling in attrType.
+	"open":        contentTypePlain,
+	"optimum":     contentTypePlain,
+	"pattern":     contentTypeUnsafe,
+	"placeholder": contentTypePlain,
+	"poster":      contentTypeURL,
+	"profile":     contentTypeURL,
+	"preload":     contentTypePlain,
+	"pubdate":     contentTypePlain,
+	"radiogroup":  contentTypePlain,
+	"readonly":    contentTypePlain,
+	"rel":         contentTypeUnsafe,
+	"required":    contentTypePlain,
+	"reversed":    contentTypePlain,
+	"rows":        contentTypePlain,
+	"rowspan":     contentTypePlain,
+	"sandbox":     contentTypeUnsafe,
+	"spellcheck":  contentTypePlain,
+	"scope":       contentTypePlain,
+	"scoped":      contentTypePlain,
+	"seamless":    contentTypePlain,
+	"selected":    contentTypePlain,
+	"shape":       contentTypePlain,
+	"size":        contentTypePlain,
+	"sizes":       contentTypePlain,
+	"span":        contentTypePlain,
+	"src":         contentTypeURL,
+	"srcdoc":      contentTypeHTML,
+	"srclang":     contentTypePlain,
+	"start":       contentTypePlain,
+	"step":        contentTypePlain,
+	"style":       contentTypeCSS,
+	"tabindex":    contentTypePlain,
+	"target":      contentTypePlain,
+	"title":       contentTypePlain,
+	"type":        contentTypeUnsafe,
+	"usemap":      contentTypeURL,
+	"value":       contentTypeUnsafe,
+	"width":       contentTypePlain,
+	"wrap":        contentTypePlain,
+	"xmlns":       contentTypeURL,
+}
+
+// attrType returns a conservative (upper-bound on authority) guess at the
+// type of the named attribute.
+func attrType(name string) contentType {
+	name = strings.ToLower(name)
+	if strings.HasPrefix(name, "data-") {
+		// Strip data- so that custom attribute heuristics below are
+		// widely applied.
+		// Treat data-action as URL below.
+		name = name[5:]
+	} else if colon := strings.IndexRune(name, ':'); colon != -1 {
+		if name[:colon] == "xmlns" {
+			return contentTypeURL
+		}
+		// Treat svg:href and xlink:href as href below.
+		name = name[colon+1:]
+	}
+	if t, ok := attrTypeMap[name]; ok {
+		return t
+	}
+	// Treat partial event handler names as script.
+	if strings.HasPrefix(name, "on") {
+		return contentTypeJS
+	}
+
+	// Heuristics to prevent "javascript:..." injection in custom
+	// data attributes and custom attributes like g:tweetUrl.
+	// http://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes
+	// "Custom data attributes are intended to store custom data
+	//  private to the page or application, for which there are no
+	//  more appropriate attributes or elements."
+	// Developers seem to store URL content in data URLs that start
+	// or end with "URI" or "URL".
+	if strings.Contains(name, "src") ||
+		strings.Contains(name, "uri") ||
+		strings.Contains(name, "url") {
+		return contentTypeURL
+	}
+	return contentTypePlain
+}
+
+// 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.
+
+// Strings of content from a trusted source.
+type (
+	// CSS encapsulates known safe content that matches any of:
+	//   1. The CSS3 stylesheet production, such as `p { color: purple }`.
+	//   2. The CSS3 rule production, such as `a[href=~"https:"].foo#bar`.
+	//   3. CSS3 declaration productions, such as `color: red; margin: 2px`.
+	//   4. The CSS3 value production, such as `rgba(0, 0, 255, 127)`.
+	// See http://www.w3.org/TR/css3-syntax/#parsing and
+	// https://web.archive.org/web/20090211114933/http://w3.org/TR/css3-syntax#style
+	CSS string
+
+	// HTML encapsulates a known safe HTML document fragment.
+	// It should not be used for HTML from a third-party, or HTML with
+	// unclosed tags or comments. The outputs of a sound HTML sanitizer
+	// and a template escaped by this package are fine for use with HTML.
+	HTML string
+
+	// HTMLAttr encapsulates an HTML attribute from a trusted source,
+	// for example, ` dir="ltr"`.
+	HTMLAttr string
+
+	// JS encapsulates a known safe EcmaScript5 Expression, for example,
+	// `(x + y * z())`.
+	// Template authors are responsible for ensuring that typed expressions
+	// do not break the intended precedence and that there is no
+	// statement/expression ambiguity as when passing an expression like
+	// "{ foo: bar() }\n['foo']()", which is both a valid Expression and a
+	// valid Program with a very different meaning.
+	JS string
+
+	// JSStr encapsulates a sequence of characters meant to be embedded
+	// between quotes in a JavaScript expression.
+	// The string must match a series of StringCharacters:
+	//   StringCharacter :: SourceCharacter but not `\` or LineTerminator
+	//                    | EscapeSequence
+	// Note that LineContinuations are not allowed.
+	// JSStr("foo\\nbar") is fine, but JSStr("foo\\\nbar") is not.
+	JSStr string
+
+	// URL encapsulates a known safe URL or URL substring (see RFC 3986).
+	// A URL like `javascript:checkThatFormNotEditedBeforeLeavingPage()`
+	// from a trusted source should go in the page, but by default dynamic
+	// `javascript:` URLs are filtered out since they are a frequently
+	// exploited injection vector.
+	URL string
+)
+
+type contentType uint8
+
+const (
+	contentTypePlain contentType = iota
+	contentTypeCSS
+	contentTypeHTML
+	contentTypeHTMLAttr
+	contentTypeJS
+	contentTypeJSStr
+	contentTypeURL
+	// contentTypeUnsafe is used in attr.go for values that affect how
+	// embedded content and network messages are formed, vetted,
+	// or interpreted; or which credentials network messages carry.
+	contentTypeUnsafe
+)
+
+// indirect returns the value, after dereferencing as many times
+// as necessary to reach the base type (or nil).
+func indirect(a interface{}) interface{} {
+	if a == nil {
+		return nil
+	}
+	if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
+		// Avoid creating a reflect.Value if it's not a pointer.
+		return a
+	}
+	v := reflect.ValueOf(a)
+	for v.Kind() == reflect.Ptr && !v.IsNil() {
+		v = v.Elem()
+	}
+	return v.Interface()
+}
+
+var (
+	errorType       = reflect.TypeOf((*error)(nil)).Elem()
+	fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
+)
+
+// indirectToStringerOrError returns the value, after dereferencing as many times
+// as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
+// or error,
+func indirectToStringerOrError(a interface{}) interface{} {
+	if a == nil {
+		return nil
+	}
+	v := reflect.ValueOf(a)
+	for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() {
+		v = v.Elem()
+	}
+	return v.Interface()
+}
+
+// stringify converts its arguments to a string and the type of the content.
+// All pointers are dereferenced, as in the text/template package.
+func stringify(args ...interface{}) (string, contentType) {
+	if len(args) == 1 {
+		switch s := indirect(args[0]).(type) {
+		case string:
+			return s, contentTypePlain
+		case CSS:
+			return string(s), contentTypeCSS
+		case HTML:
+			return string(s), contentTypeHTML
+		case HTMLAttr:
+			return string(s), contentTypeHTMLAttr
+		case JS:
+			return string(s), contentTypeJS
+		case JSStr:
+			return string(s), contentTypeJSStr
+		case URL:
+			return string(s), contentTypeURL
+		}
+	}
+	for i, arg := range args {
+		args[i] = indirectToStringerOrError(arg)
+	}
+	return fmt.Sprint(args...), contentTypePlain
+}
+
+// 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.
+
+// endsWithCSSKeyword reports whether b ends with an ident that
+// case-insensitively matches the lower-case kw.
+func endsWithCSSKeyword(b []byte, kw string) bool {
+	i := len(b) - len(kw)
+	if i < 0 {
+		// Too short.
+		return false
+	}
+	if i != 0 {
+		r, _ := utf8.DecodeLastRune(b[:i])
+		if isCSSNmchar(r) {
+			// Too long.
+			return false
+		}
+	}
+	// Many CSS keywords, such as "!important" can have characters encoded,
+	// but the URI production does not allow that according to
+	// http://www.w3.org/TR/css3-syntax/#TOK-URI
+	// This does not attempt to recognize encoded keywords. For example,
+	// given "\75\72\6c" and "url" this return false.
+	return string(bytes.ToLower(b[i:])) == kw
+}
+
+// isCSSNmchar reports whether rune is allowed anywhere in a CSS identifier.
+func isCSSNmchar(r rune) bool {
+	// Based on the CSS3 nmchar production but ignores multi-rune escape
+	// sequences.
+	// http://www.w3.org/TR/css3-syntax/#SUBTOK-nmchar
+	return 'a' <= r && r <= 'z' ||
+		'A' <= r && r <= 'Z' ||
+		'0' <= r && r <= '9' ||
+		r == '-' ||
+		r == '_' ||
+		// Non-ASCII cases below.
+		0x80 <= r && r <= 0xd7ff ||
+		0xe000 <= r && r <= 0xfffd ||
+		0x10000 <= r && r <= 0x10ffff
+}
+
+// decodeCSS decodes CSS3 escapes given a sequence of stringchars.
+// If there is no change, it returns the input, otherwise it returns a slice
+// backed by a new array.
+// http://www.w3.org/TR/css3-syntax/#SUBTOK-stringchar defines stringchar.
+func decodeCSS(s []byte) []byte {
+	i := bytes.IndexByte(s, '\\')
+	if i == -1 {
+		return s
+	}
+	// The UTF-8 sequence for a codepoint is never longer than 1 + the
+	// number hex digits need to represent that codepoint, so len(s) is an
+	// upper bound on the output length.
+	b := make([]byte, 0, len(s))
+	for len(s) != 0 {
+		i := bytes.IndexByte(s, '\\')
+		if i == -1 {
+			i = len(s)
+		}
+		b, s = append(b, s[:i]...), s[i:]
+		if len(s) < 2 {
+			break
+		}
+		// http://www.w3.org/TR/css3-syntax/#SUBTOK-escape
+		// escape ::= unicode | '\' [#x20-#x7E#x80-#xD7FF#xE000-#xFFFD#x10000-#x10FFFF]
+		if isHex(s[1]) {
+			// http://www.w3.org/TR/css3-syntax/#SUBTOK-unicode
+			//   unicode ::= '\' [0-9a-fA-F]{1,6} wc?
+			j := 2
+			for j < len(s) && j < 7 && isHex(s[j]) {
+				j++
+			}
+			r := hexDecode(s[1:j])
+			if r > unicode.MaxRune {
+				r, j = r/16, j-1
+			}
+			n := utf8.EncodeRune(b[len(b):cap(b)], r)
+			// The optional space at the end allows a hex
+			// sequence to be followed by a literal hex.
+			// string(decodeCSS([]byte(`\A B`))) == "\nB"
+			b, s = b[:len(b)+n], skipCSSSpace(s[j:])
+		} else {
+			// `\\` decodes to `\` and `\"` to `"`.
+			_, n := utf8.DecodeRune(s[1:])
+			b, s = append(b, s[1:1+n]...), s[1+n:]
+		}
+	}
+	return b
+}
+
+// isHex reports whether the given character is a hex digit.
+func isHex(c byte) bool {
+	return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F'
+}
+
+// hexDecode decodes a short hex digit sequence: "10" -> 16.
+func hexDecode(s []byte) rune {
+	n := '\x00'
+	for _, c := range s {
+		n <<= 4
+		switch {
+		case '0' <= c && c <= '9':
+			n |= rune(c - '0')
+		case 'a' <= c && c <= 'f':
+			n |= rune(c-'a') + 10
+		case 'A' <= c && c <= 'F':
+			n |= rune(c-'A') + 10
+		default:
+			panic(fmt.Sprintf("Bad hex digit in %q", s))
+		}
+	}
+	return n
+}
+
+// skipCSSSpace returns a suffix of c, skipping over a single space.
+func skipCSSSpace(c []byte) []byte {
+	if len(c) == 0 {
+		return c
+	}
+	// wc ::= #x9 | #xA | #xC | #xD | #x20
+	switch c[0] {
+	case '\t', '\n', '\f', ' ':
+		return c[1:]
+	case '\r':
+		// This differs from CSS3's wc production because it contains a
+		// probable spec error whereby wc contains all the single byte
+		// sequences in nl (newline) but not CRLF.
+		if len(c) >= 2 && c[1] == '\n' {
+			return c[2:]
+		}
+		return c[1:]
+	}
+	return c
+}
+
+// isCSSSpace reports whether b is a CSS space char as defined in wc.
+func isCSSSpace(b byte) bool {
+	switch b {
+	case '\t', '\n', '\f', '\r', ' ':
+		return true
+	}
+	return false
+}
+
+// cssEscaper escapes HTML and CSS special characters using \<hex>+ escapes.
+func cssEscaper(args ...interface{}) string {
+	s, _ := stringify(args...)
+	var b bytes.Buffer
+	written := 0
+	for i, r := range s {
+		var repl string
+		switch r {
+		case 0:
+			repl = `\0`
+		case '\t':
+			repl = `\9`
+		case '\n':
+			repl = `\a`
+		case '\f':
+			repl = `\c`
+		case '\r':
+			repl = `\d`
+		// Encode HTML specials as hex so the output can be embedded
+		// in HTML attributes without further encoding.
+		case '"':
+			repl = `\22`
+		case '&':
+			repl = `\26`
+		case '\'':
+			repl = `\27`
+		case '(':
+			repl = `\28`
+		case ')':
+			repl = `\29`
+		case '+':
+			repl = `\2b`
+		case '/':
+			repl = `\2f`
+		case ':':
+			repl = `\3a`
+		case ';':
+			repl = `\3b`
+		case '<':
+			repl = `\3c`
+		case '>':
+			repl = `\3e`
+		case '\\':
+			repl = `\\`
+		case '{':
+			repl = `\7b`
+		case '}':
+			repl = `\7d`
+		default:
+			continue
+		}
+		b.WriteString(s[written:i])
+		b.WriteString(repl)
+		written = i + utf8.RuneLen(r)
+		if repl != `\\` && (written == len(s) || isHex(s[written]) || isCSSSpace(s[written])) {
+			b.WriteByte(' ')
+		}
+	}
+	if written == 0 {
+		return s
+	}
+	b.WriteString(s[written:])
+	return b.String()
+}
+
+var expressionBytes = []byte("expression")
+var mozBindingBytes = []byte("mozbinding")
+
+// cssValueFilter allows innocuous CSS values in the output including CSS
+// quantities (10px or 25%), ID or class literals (#foo, .bar), keyword values
+// (inherit, blue), and colors (#888).
+// It filters out unsafe values, such as those that affect token boundaries,
+// and anything that might execute scripts.
+func cssValueFilter(args ...interface{}) string {
+	s, t := stringify(args...)
+	if t == contentTypeCSS {
+		return s
+	}
+	b, id := decodeCSS([]byte(s)), make([]byte, 0, 64)
+
+	// CSS3 error handling is specified as honoring string boundaries per
+	// http://www.w3.org/TR/css3-syntax/#error-handling :
+	//     Malformed declarations. User agents must handle unexpected
+	//     tokens encountered while parsing a declaration by reading until
+	//     the end of the declaration, while observing the rules for
+	//     matching pairs of (), [], {}, "", and '', and correctly handling
+	//     escapes. For example, a malformed declaration may be missing a
+	//     property, colon (:) or value.
+	// So we need to make sure that values do not have mismatched bracket
+	// or quote characters to prevent the browser from restarting parsing
+	// inside a string that might embed JavaScript source.
+	for i, c := range b {
+		switch c {
+		case 0, '"', '\'', '(', ')', '/', ';', '@', '[', '\\', ']', '`', '{', '}':
+			return filterFailsafe
+		case '-':
+			// Disallow <!-- or -->.
+			// -- should not appear in valid identifiers.
+			if i != 0 && b[i-1] == '-' {
+				return filterFailsafe
+			}
+		default:
+			if c < 0x80 && isCSSNmchar(rune(c)) {
+				id = append(id, c)
+			}
+		}
+	}
+	id = bytes.ToLower(id)
+	if bytes.Index(id, expressionBytes) != -1 || bytes.Index(id, mozBindingBytes) != -1 {
+		return filterFailsafe
+	}
+	return string(b)
+}
+
+// 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 template (html/template) implements data-driven templates for
+generating HTML output safe against code injection. It provides the
+same interface as package text/template and should be used instead of
+text/template whenever the output is HTML.
+
+The documentation here focuses on the security features of the package.
+For information about how to program the templates themselves, see the
+documentation for text/template.
+
+Introduction
+
+This package wraps package text/template so you can share its template API
+to parse and execute HTML templates safely.
+
+  tmpl, err := template.New("name").Parse(...)
+  // Error checking elided
+  err = tmpl.Execute(out, data)
+
+If successful, tmpl will now be injection-safe. Otherwise, err is an error
+defined in the docs for ErrorCode.
+
+HTML templates treat data values as plain text which should be encoded so they
+can be safely embedded in an HTML document. The escaping is contextual, so
+actions can appear within JavaScript, CSS, and URI contexts.
+
+The security model used by this package assumes that template authors are
+trusted, while Execute's data parameter is not. More details are
+provided below.
+
+Example
+
+  import "text/template"
+  ...
+  t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
+  err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
+
+produces
+
+  Hello, <script>alert('you have been pwned')</script>!
+
+but the contextual autoescaping in html/template
+
+  import "html/template"
+  ...
+  t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
+  err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
+
+produces safe, escaped HTML output
+
+  Hello, &lt;script&gt;alert(&#39;you have been pwned&#39;)&lt;/script&gt;!
+
+
+Contexts
+
+This package understands HTML, CSS, JavaScript, and URIs. It adds sanitizing
+functions to each simple action pipeline, so given the excerpt
+
+  <a href="/search?q={{.}}">{{.}}</a>
+
+At parse time each {{.}} is overwritten to add escaping functions as necessary.
+In this case it becomes
+
+  <a href="/search?q={{. | urlquery}}">{{. | html}}</a>
+
+
+Errors
+
+See the documentation of ErrorCode for details.
+
+
+A fuller picture
+
+The rest of this package comment may be skipped on first reading; it includes
+details necessary to understand escaping contexts and error messages. Most users
+will not need to understand these details.
+
+
+Contexts
+
+Assuming {{.}} is `O'Reilly: How are <i>you</i>?`, the table below shows
+how {{.}} appears when used in the context to the left.
+
+  Context                          {{.}} After
+  {{.}}                            O'Reilly: How are &lt;i&gt;you&lt;/i&gt;?
+  <a title='{{.}}'>                O&#39;Reilly: How are you?
+  <a href="/{{.}}">                O&#39;Reilly: How are %3ci%3eyou%3c/i%3e?
+  <a href="?q={{.}}">              O&#39;Reilly%3a%20How%20are%3ci%3e...%3f
+  <a onx='f("{{.}}")'>             O\x27Reilly: How are \x3ci\x3eyou...?
+  <a onx='f({{.}})'>               "O\x27Reilly: How are \x3ci\x3eyou...?"
+  <a onx='pattern = /{{.}}/;'>     O\x27Reilly: How are \x3ci\x3eyou...\x3f
+
+If used in an unsafe context, then the value might be filtered out:
+
+  Context                          {{.}} After
+  <a href="{{.}}">                 #ZgotmplZ
+
+since "O'Reilly:" is not an allowed protocol like "http:".
+
+
+If {{.}} is the innocuous word, `left`, then it can appear more widely,
+
+  Context                              {{.}} After
+  {{.}}                                left
+  <a title='{{.}}'>                    left
+  <a href='{{.}}'>                     left
+  <a href='/{{.}}'>                    left
+  <a href='?dir={{.}}'>                left
+  <a style="border-{{.}}: 4px">        left
+  <a style="align: {{.}}">             left
+  <a style="background: '{{.}}'>       left
+  <a style="background: url('{{.}}')>  left
+  <style>p.{{.}} {color:red}</style>   left
+
+Non-string values can be used in JavaScript contexts.
+If {{.}} is
+
+  struct{A,B string}{ "foo", "bar" }
+
+in the escaped template
+
+  <script>var pair = {{.}};</script>
+
+then the template output is
+
+  <script>var pair = {"A": "foo", "B": "bar"};</script>
+
+See package json to understand how non-string content is marshalled for
+embedding in JavaScript contexts.
+
+
+Typed Strings
+
+By default, this package assumes that all pipelines produce a plain text string.
+It adds escaping pipeline stages necessary to correctly and safely embed that
+plain text string in the appropriate context.
+
+When a data value is not plain text, you can make sure it is not over-escaped
+by marking it with its type.
+
+Types HTML, JS, URL, and others from content.go can carry safe content that is
+exempted from escaping.
+
+The template
+
+  Hello, {{.}}!
+
+can be invoked with
+
+  tmpl.Execute(out, HTML(`<b>World</b>`))
+
+to produce
+
+  Hello, <b>World</b>!
+
+instead of the
+
+  Hello, &lt;b&gt;World&lt;b&gt;!
+
+that would have been produced if {{.}} was a regular string.
+
+
+Security Model
+
+http://js-quasis-libraries-and-repl.googlecode.com/svn/trunk/safetemplate.html#problem_definition defines "safe" as used by this package.
+
+This package assumes that template authors are trusted, that Execute's data
+parameter is not, and seeks to preserve the properties below in the face
+of untrusted data:
+
+Structure Preservation Property:
+"... when a template author writes an HTML tag in a safe templating language,
+the browser will interpret the corresponding portion of the output as a tag
+regardless of the values of untrusted data, and similarly for other structures
+such as attribute boundaries and JS and CSS string boundaries."
+
+Code Effect Property:
+"... only code specified by the template author should run as a result of
+injecting the template output into a page and all code specified by the
+template author should run as a result of the same."
+
+Least Surprise Property:
+"A developer (or code reviewer) familiar with HTML, CSS, and JavaScript, who
+knows that contextual autoescaping happens should be able to look at a {{.}}
+and correctly infer what sanitization happens."
+*/
+// 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.
+
+// nextJSCtx returns the context that determines whether a slash after the
+// given run of tokens starts a regular expression instead of a division
+// operator: / or /=.
+//
+// This assumes that the token run does not include any string tokens, comment
+// tokens, regular expression literal tokens, or division operators.
+//
+// This fails on some valid but nonsensical JavaScript programs like
+// "x = ++/foo/i" which is quite different than "x++/foo/i", but is not known to
+// fail on any known useful programs. It is based on the draft
+// JavaScript 2.0 lexical grammar and requires one token of lookbehind:
+// http://www.mozilla.org/js/language/js20-2000-07/rationale/syntax.html
+func nextJSCtx(s []byte, preceding jsCtx) jsCtx {
+	s = bytes.TrimRight(s, "\t\n\f\r \u2028\u2029")
+	if len(s) == 0 {
+		return preceding
+	}
+
+	// All cases below are in the single-byte UTF-8 group.
+	switch c, n := s[len(s)-1], len(s); c {
+	case '+', '-':
+		// ++ and -- are not regexp preceders, but + and - are whether
+		// they are used as infix or prefix operators.
+		start := n - 1
+		// Count the number of adjacent dashes or pluses.
+		for start > 0 && s[start-1] == c {
+			start--
+		}
+		if (n-start)&1 == 1 {
+			// Reached for trailing minus signs since "---" is the
+			// same as "-- -".
+			return jsCtxRegexp
+		}
+		return jsCtxDivOp
+	case '.':
+		// Handle "42."
+		if n != 1 && '0' <= s[n-2] && s[n-2] <= '9' {
+			return jsCtxDivOp
+		}
+		return jsCtxRegexp
+	// Suffixes for all punctuators from section 7.7 of the language spec
+	// that only end binary operators not handled above.
+	case ',', '<', '>', '=', '*', '%', '&', '|', '^', '?':
+		return jsCtxRegexp
+	// Suffixes for all punctuators from section 7.7 of the language spec
+	// that are prefix operators not handled above.
+	case '!', '~':
+		return jsCtxRegexp
+	// Matches all the punctuators from section 7.7 of the language spec
+	// that are open brackets not handled above.
+	case '(', '[':
+		return jsCtxRegexp
+	// Matches all the punctuators from section 7.7 of the language spec
+	// that precede expression starts.
+	case ':', ';', '{':
+		return jsCtxRegexp
+	// CAVEAT: the close punctuators ('}', ']', ')') precede div ops and
+	// are handled in the default except for '}' which can precede a
+	// division op as in
+	//    ({ valueOf: function () { return 42 } } / 2
+	// which is valid, but, in practice, developers don't divide object
+	// literals, so our heuristic works well for code like
+	//    function () { ... }  /foo/.test(x) && sideEffect();
+	// The ')' punctuator can precede a regular expression as in
+	//     if (b) /foo/.test(x) && ...
+	// but this is much less likely than
+	//     (a + b) / c
+	case '}':
+		return jsCtxRegexp
+	default:
+		// Look for an IdentifierName and see if it is a keyword that
+		// can precede a regular expression.
+		j := n
+		for j > 0 && isJSIdentPart(rune(s[j-1])) {
+			j--
+		}
+		if regexpPrecederKeywords[string(s[j:])] {
+			return jsCtxRegexp
+		}
+	}
+	// Otherwise is a punctuator not listed above, or
+	// a string which precedes a div op, or an identifier
+	// which precedes a div op.
+	return jsCtxDivOp
+}
+
+// regexpPrecederKeywords is a set of reserved JS keywords that can precede a
+// regular expression in JS source.
+var regexpPrecederKeywords = map[string]bool{
+	"break":      true,
+	"case":       true,
+	"continue":   true,
+	"delete":     true,
+	"do":         true,
+	"else":       true,
+	"finally":    true,
+	"in":         true,
+	"instanceof": true,
+	"return":     true,
+	"throw":      true,
+	"try":        true,
+	"typeof":     true,
+	"void":       true,
+}
+
+var jsonMarshalType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
+
+// indirectToJSONMarshaler returns the value, after dereferencing as many times
+// as necessary to reach the base type (or nil) or an implementation of json.Marshal.
+func indirectToJSONMarshaler(a interface{}) interface{} {
+	v := reflect.ValueOf(a)
+	for !v.Type().Implements(jsonMarshalType) && v.Kind() == reflect.Ptr && !v.IsNil() {
+		v = v.Elem()
+	}
+	return v.Interface()
+}
+
+// jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has
+// neither side-effects nor free variables outside (NaN, Infinity).
+func jsValEscaper(args ...interface{}) string {
+	var a interface{}
+	if len(args) == 1 {
+		a = indirectToJSONMarshaler(args[0])
+		switch t := a.(type) {
+		case JS:
+			return string(t)
+		case JSStr:
+			// TODO: normalize quotes.
+			return `"` + string(t) + `"`
+		case json.Marshaler:
+			// Do not treat as a Stringer.
+		case fmt.Stringer:
+			a = t.String()
+		}
+	} else {
+		for i, arg := range args {
+			args[i] = indirectToJSONMarshaler(arg)
+		}
+		a = fmt.Sprint(args...)
+	}
+	// TODO: detect cycles before calling Marshal which loops infinitely on
+	// cyclic data. This may be an unacceptable DoS risk.
+
+	b, err := json.Marshal(a)
+	if err != nil {
+		// Put a space before comment so that if it is flush against
+		// a division operator it is not turned into a line comment:
+		//     x/{{y}}
+		// turning into
+		//     x//* error marshalling y:
+		//          second line of error message */null
+		return fmt.Sprintf(" /* %s */null ", strings.Replace(err.Error(), "*/", "* /", -1))
+	}
+
+	// TODO: maybe post-process output to prevent it from containing
+	// "<!--", "-->", "<![CDATA[", "]]>", or "</script"
+	// in case custom marshallers produce output containing those.
+
+	// TODO: Maybe abbreviate \u00ab to \xab to produce more compact output.
+	if len(b) == 0 {
+		// In, `x=y/{{.}}*z` a json.Marshaler that produces "" should
+		// not cause the output `x=y/*z`.
+		return " null "
+	}
+	first, _ := utf8.DecodeRune(b)
+	last, _ := utf8.DecodeLastRune(b)
+	var buf bytes.Buffer
+	// Prevent IdentifierNames and NumericLiterals from running into
+	// keywords: in, instanceof, typeof, void
+	pad := isJSIdentPart(first) || isJSIdentPart(last)
+	if pad {
+		buf.WriteByte(' ')
+	}
+	written := 0
+	// Make sure that json.Marshal escapes codepoints U+2028 & U+2029
+	// so it falls within the subset of JSON which is valid JS.
+	for i := 0; i < len(b); {
+		rune, n := utf8.DecodeRune(b[i:])
+		repl := ""
+		if rune == 0x2028 {
+			repl = `\u2028`
+		} else if rune == 0x2029 {
+			repl = `\u2029`
+		}
+		if repl != "" {
+			buf.Write(b[written:i])
+			buf.WriteString(repl)
+			written = i + n
+		}
+		i += n
+	}
+	if buf.Len() != 0 {
+		buf.Write(b[written:])
+		if pad {
+			buf.WriteByte(' ')
+		}
+		b = buf.Bytes()
+	}
+	return string(b)
+}
+
+// jsStrEscaper produces a string that can be included between quotes in
+// JavaScript source, in JavaScript embedded in an HTML5 <script> element,
+// or in an HTML5 event handler attribute such as onclick.
+func jsStrEscaper(args ...interface{}) string {
+	s, t := stringify(args...)
+	if t == contentTypeJSStr {
+		return replace(s, jsStrNormReplacementTable)
+	}
+	return replace(s, jsStrReplacementTable)
+}
+
+// jsRegexpEscaper behaves like jsStrEscaper but escapes regular expression
+// specials so the result is treated literally when included in a regular
+// expression literal. /foo{{.X}}bar/ matches the string "foo" followed by
+// the literal text of {{.X}} followed by the string "bar".
+func jsRegexpEscaper(args ...interface{}) string {
+	s, _ := stringify(args...)
+	s = replace(s, jsRegexpReplacementTable)
+	if s == "" {
+		// /{{.X}}/ should not produce a line comment when .X == "".
+		return "(?:)"
+	}
+	return s
+}
+
+// replace replaces each rune r of s with replacementTable[r], provided that
+// r < len(replacementTable). If replacementTable[r] is the empty string then
+// no replacement is made.
+// It also replaces runes U+2028 and U+2029 with the raw strings `\u2028` and
+// `\u2029`.
+func replace(s string, replacementTable []string) string {
+	var b bytes.Buffer
+	written := 0
+	for i, r := range s {
+		var repl string
+		switch {
+		case int(r) < len(replacementTable) && replacementTable[r] != "":
+			repl = replacementTable[r]
+		case r == '\u2028':
+			repl = `\u2028`
+		case r == '\u2029':
+			repl = `\u2029`
+		default:
+			continue
+		}
+		b.WriteString(s[written:i])
+		b.WriteString(repl)
+		written = i + utf8.RuneLen(r)
+	}
+	if written == 0 {
+		return s
+	}
+	b.WriteString(s[written:])
+	return b.String()
+}
+
+var jsStrReplacementTable = []string{
+	0:    `\0`,
+	'\t': `\t`,
+	'\n': `\n`,
+	'\v': `\x0b`, // "\v" == "v" on IE 6.
+	'\f': `\f`,
+	'\r': `\r`,
+	// Encode HTML specials as hex so the output can be embedded
+	// in HTML attributes without further encoding.
+	'"':  `\x22`,
+	'&':  `\x26`,
+	'\'': `\x27`,
+	'+':  `\x2b`,
+	'/':  `\/`,
+	'<':  `\x3c`,
+	'>':  `\x3e`,
+	'\\': `\\`,
+}
+
+// jsStrNormReplacementTable is like jsStrReplacementTable but does not
+// overencode existing escapes since this table has no entry for `\`.
+var jsStrNormReplacementTable = []string{
+	0:    `\0`,
+	'\t': `\t`,
+	'\n': `\n`,
+	'\v': `\x0b`, // "\v" == "v" on IE 6.
+	'\f': `\f`,
+	'\r': `\r`,
+	// Encode HTML specials as hex so the output can be embedded
+	// in HTML attributes without further encoding.
+	'"':  `\x22`,
+	'&':  `\x26`,
+	'\'': `\x27`,
+	'+':  `\x2b`,
+	'/':  `\/`,
+	'<':  `\x3c`,
+	'>':  `\x3e`,
+}
+
+var jsRegexpReplacementTable = []string{
+	0:    `\0`,
+	'\t': `\t`,
+	'\n': `\n`,
+	'\v': `\x0b`, // "\v" == "v" on IE 6.
+	'\f': `\f`,
+	'\r': `\r`,
+	// Encode HTML specials as hex so the output can be embedded
+	// in HTML attributes without further encoding.
+	'"':  `\x22`,
+	'$':  `\$`,
+	'&':  `\x26`,
+	'\'': `\x27`,
+	'(':  `\(`,
+	')':  `\)`,
+	'*':  `\*`,
+	'+':  `\x2b`,
+	'-':  `\-`,
+	'.':  `\.`,
+	'/':  `\/`,
+	'<':  `\x3c`,
+	'>':  `\x3e`,
+	'?':  `\?`,
+	'[':  `\[`,
+	'\\': `\\`,
+	']':  `\]`,
+	'^':  `\^`,
+	'{':  `\{`,
+	'|':  `\|`,
+	'}':  `\}`,
+}
+
+// isJSIdentPart reports whether the given rune is a JS identifier part.
+// It does not handle all the non-Latin letters, joiners, and combining marks,
+// but it does handle every codepoint that can occur in a numeric literal or
+// a keyword.
+func isJSIdentPart(r rune) bool {
+	switch {
+	case r == '$':
+		return true
+	case '0' <= r && r <= '9':
+		return true
+	case 'A' <= r && r <= 'Z':
+		return true
+	case r == '_':
+		return true
+	case 'a' <= r && r <= 'z':
+		return true
+	}
+	return false
+}
+
+// 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.
+
+// Template is a specialized Template from "text/template" that produces a safe
+// HTML document fragment.
+type Template struct {
+	escaped bool
+	// We could embed the text/template field, but it's safer not to because
+	// we need to keep our version of the name space and the underlying
+	// template's in sync.
+	text *template.Template
+	// The underlying template's parse tree, updated to be HTML-safe.
+	Tree       *parse.Tree
+	*nameSpace // common to all associated templates
+}
+
+// nameSpace is the data structure shared by all templates in an association.
+type nameSpace struct {
+	mu  sync.Mutex
+	set map[string]*Template
+}
+
+// Templates returns a slice of the templates associated with t, including t
+// itself.
+func (t *Template) Templates() []*Template {
+	ns := t.nameSpace
+	ns.mu.Lock()
+	defer ns.mu.Unlock()
+	// Return a slice so we don't expose the map.
+	m := make([]*Template, 0, len(ns.set))
+	for _, v := range ns.set {
+		m = append(m, v)
+	}
+	return m
+}
+
+// escape escapes all associated templates.
+func (t *Template) escape() error {
+	t.nameSpace.mu.Lock()
+	defer t.nameSpace.mu.Unlock()
+	if !t.escaped {
+		if err := escapeTemplates(t, t.Name()); err != nil {
+			return err
+		}
+		t.escaped = true
+	}
+	return nil
+}
+
+// Execute applies a parsed template to the specified data object,
+// writing the output to wr.
+// If an error occurs executing the template or writing its output,
+// execution stops, but partial results may already have been written to
+// the output writer.
+// A template may be executed safely in parallel.
+func (t *Template) Execute(wr io.Writer, data interface{}) error {
+	if err := t.escape(); err != nil {
+		return err
+	}
+	return t.text.Execute(wr, data)
+}
+
+// ExecuteTemplate applies the template associated with t that has the given
+// name to the specified data object and writes the output to wr.
+// If an error occurs executing the template or writing its output,
+// execution stops, but partial results may already have been written to
+// the output writer.
+// A template may be executed safely in parallel.
+func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
+	tmpl, err := t.lookupAndEscapeTemplate(name)
+	if err != nil {
+		return err
+	}
+	return tmpl.text.Execute(wr, data)
+}
+
+// lookupAndEscapeTemplate guarantees that the template with the given name
+// is escaped, or returns an error if it cannot be. It returns the named
+// template.
+func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err error) {
+	t.nameSpace.mu.Lock()
+	defer t.nameSpace.mu.Unlock()
+	tmpl = t.set[name]
+	if tmpl == nil {
+		return nil, fmt.Errorf("html/template: %q is undefined", name)
+	}
+	if tmpl.text.Tree == nil || tmpl.text.Root == nil {
+		return nil, fmt.Errorf("html/template: %q is an incomplete template", name)
+	}
+	if t.text.Lookup(name) == nil {
+		panic("html/template internal error: template escaping out of sync")
+	}
+	if tmpl != nil && !tmpl.escaped {
+		err = escapeTemplates(tmpl, name)
+	}
+	return tmpl, err
+}
+
+// Parse parses a string into a template. Nested template definitions
+// will be associated with the top-level template t. Parse may be
+// called multiple times to parse definitions of templates to associate
+// with t. It is an error if a resulting template is non-empty (contains
+// content other than template definitions) and would replace a
+// non-empty template with the same name.  (In multiple calls to Parse
+// with the same receiver template, only one call can contain text
+// other than space, comments, and template definitions.)
+func (t *Template) Parse(src string) (*Template, error) {
+	t.nameSpace.mu.Lock()
+	t.escaped = false
+	t.nameSpace.mu.Unlock()
+	ret, err := t.text.Parse(src)
+	if err != nil {
+		return nil, err
+	}
+	// In general, all the named templates might have changed underfoot.
+	// Regardless, some new ones may have been defined.
+	// The template.Template set has been updated; update ours.
+	t.nameSpace.mu.Lock()
+	defer t.nameSpace.mu.Unlock()
+	for _, v := range ret.Templates() {
+		name := v.Name()
+		tmpl := t.set[name]
+		if tmpl == nil {
+			tmpl = t.new(name)
+		}
+		// Restore our record of this text/template to its unescaped original state.
+		tmpl.escaped = false
+		tmpl.text = v
+		tmpl.Tree = v.Tree
+	}
+	return t, nil
+}
+
+// AddParseTree creates a new template with the name and parse tree
+// and associates it with t.
+//
+// It returns an error if t has already been executed.
+func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
+	t.nameSpace.mu.Lock()
+	defer t.nameSpace.mu.Unlock()
+	if t.escaped {
+		return nil, fmt.Errorf("html/template: cannot AddParseTree to %q after it has executed", t.Name())
+	}
+	text, err := t.text.AddParseTree(name, tree)
+	if err != nil {
+		return nil, err
+	}
+	ret := &Template{
+		false,
+		text,
+		text.Tree,
+		t.nameSpace,
+	}
+	t.set[name] = ret
+	return ret, nil
+}
+
+// Clone returns a duplicate of the template, including all associated
+// templates. The actual representation is not copied, but the name space of
+// associated templates is, so further calls to Parse in the copy will add
+// templates to the copy but not to the original. Clone can be used to prepare
+// common templates and use them with variant definitions for other templates
+// by adding the variants after the clone is made.
+//
+// It returns an error if t has already been executed.
+func (t *Template) Clone() (*Template, error) {
+	t.nameSpace.mu.Lock()
+	defer t.nameSpace.mu.Unlock()
+	if t.escaped {
+		return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
+	}
+	textClone, err := t.text.Clone()
+	if err != nil {
+		return nil, err
+	}
+	ret := &Template{
+		false,
+		textClone,
+		textClone.Tree,
+		&nameSpace{
+			set: make(map[string]*Template),
+		},
+	}
+	for _, x := range textClone.Templates() {
+		name := x.Name()
+		src := t.set[name]
+		if src == nil || src.escaped {
+			return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
+		}
+		x.Tree = x.Tree.Copy()
+		ret.set[name] = &Template{
+			false,
+			x,
+			x.Tree,
+			ret.nameSpace,
+		}
+	}
+	return ret, nil
+}
+
+// New allocates a new HTML template with the given name.
+func New(name string) *Template {
+	tmpl := &Template{
+		false,
+		template.New(name),
+		nil,
+		&nameSpace{
+			set: make(map[string]*Template),
+		},
+	}
+	tmpl.set[name] = tmpl
+	return tmpl
+}
+
+// New allocates a new HTML template associated with the given one
+// and with the same delimiters. The association, which is transitive,
+// allows one template to invoke another with a {{template}} action.
+func (t *Template) New(name string) *Template {
+	t.nameSpace.mu.Lock()
+	defer t.nameSpace.mu.Unlock()
+	return t.new(name)
+}
+
+// new is the implementation of New, without the lock.
+func (t *Template) new(name string) *Template {
+	tmpl := &Template{
+		false,
+		t.text.New(name),
+		nil,
+		t.nameSpace,
+	}
+	tmpl.set[name] = tmpl
+	return tmpl
+}
+
+// Name returns the name of the template.
+func (t *Template) Name() string {
+	return t.text.Name()
+}
+
+// FuncMap is the type of the map defining the mapping from names to
+// functions. Each function must have either a single return value, or two
+// return values of which the second has type error. In that case, if the
+// second (error) argument evaluates to non-nil during execution, execution
+// terminates and Execute returns that error. FuncMap has the same base type
+// as FuncMap in "text/template", copied here so clients need not import
+// "text/template".
+type FuncMap map[string]interface{}
+
+// Funcs adds the elements of the argument map to the template's function map.
+// It panics if a value in the map is not a function with appropriate return
+// type. However, it is legal to overwrite elements of the map. The return
+// value is the template, so calls can be chained.
+func (t *Template) Funcs(funcMap FuncMap) *Template {
+	t.text.Funcs(template.FuncMap(funcMap))
+	return t
+}
+
+// Delims sets the action delimiters to the specified strings, to be used in
+// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
+// definitions will inherit the settings. An empty delimiter stands for the
+// corresponding default: {{ or }}.
+// The return value is the template, so calls can be chained.
+func (t *Template) Delims(left, right string) *Template {
+	t.text.Delims(left, right)
+	return t
+}
+
+// Lookup returns the template with the given name that is associated with t,
+// or nil if there is no such template.
+func (t *Template) Lookup(name string) *Template {
+	t.nameSpace.mu.Lock()
+	defer t.nameSpace.mu.Unlock()
+	return t.set[name]
+}
+
+// Must is a helper that wraps a call to a function returning (*Template, error)
+// and panics if the error is non-nil. It is intended for use in variable initializations
+// such as
+//	var t = template.Must(template.New("name").Parse("html"))
+func Must(t *Template, err error) *Template {
+	if err != nil {
+		panic(err)
+	}
+	return t
+}
+
+// ParseFiles creates a new Template and parses the template definitions from
+// the named files. The returned template's name will have the (base) name and
+// (parsed) contents of the first file. There must be at least one file.
+// If an error occurs, parsing stops and the returned *Template is nil.
+func ParseFiles(filenames ...string) (*Template, error) {
+	return parseFiles(nil, filenames...)
+}
+
+// ParseFiles parses the named files and associates the resulting templates with
+// t. If an error occurs, parsing stops and the returned template is nil;
+// otherwise it is t. There must be at least one file.
+func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
+	return parseFiles(t, filenames...)
+}
+
+// parseFiles is the helper for the method and function. If the argument
+// template is nil, it is created from the first file.
+func parseFiles(t *Template, filenames ...string) (*Template, error) {
+	if len(filenames) == 0 {
+		// Not really a problem, but be consistent.
+		return nil, fmt.Errorf("html/template: no files named in call to ParseFiles")
+	}
+	for _, filename := range filenames {
+		b, err := ioutil.ReadFile(filename)
+		if err != nil {
+			return nil, err
+		}
+		s := string(b)
+		name := filepath.Base(filename)
+		// First template becomes return value if not already defined,
+		// and we use that one for subsequent New calls to associate
+		// all the templates together. Also, if this file has the same name
+		// as t, this file becomes the contents of t, so
+		//  t, err := New(name).Funcs(xxx).ParseFiles(name)
+		// works. Otherwise we create a new template associated with t.
+		var tmpl *Template
+		if t == nil {
+			t = New(name)
+		}
+		if name == t.Name() {
+			tmpl = t
+		} else {
+			tmpl = t.New(name)
+		}
+		_, err = tmpl.Parse(s)
+		if err != nil {
+			return nil, err
+		}
+	}
+	return t, nil
+}
+
+// ParseGlob creates a new Template and parses the template definitions from the
+// files identified by the pattern, which must match at least one file. The
+// returned template will have the (base) name and (parsed) contents of the
+// first file matched by the pattern. ParseGlob is equivalent to calling
+// ParseFiles with the list of files matched by the pattern.
+func ParseGlob(pattern string) (*Template, error) {
+	return parseGlob(nil, pattern)
+}
+
+// ParseGlob parses the template definitions in the files identified by the
+// pattern and associates the resulting templates with t. The pattern is
+// processed by filepath.Glob and must match at least one file. ParseGlob is
+// equivalent to calling t.ParseFiles with the list of files matched by the
+// pattern.
+func (t *Template) ParseGlob(pattern string) (*Template, error) {
+	return parseGlob(t, pattern)
+}
+
+// parseGlob is the implementation of the function and method ParseGlob.
+func parseGlob(t *Template, pattern string) (*Template, error) {
+	filenames, err := filepath.Glob(pattern)
+	if err != nil {
+		return nil, err
+	}
+	if len(filenames) == 0 {
+		return nil, fmt.Errorf("html/template: pattern matches no files: %#q", pattern)
+	}
+	return parseFiles(t, filenames...)
+}
+
+// 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.
+
+// urlFilter returns its input unless it contains an unsafe protocol in which
+// case it defangs the entire URL.
+func urlFilter(args ...interface{}) string {
+	s, t := stringify(args...)
+	if t == contentTypeURL {
+		return s
+	}
+	if i := strings.IndexRune(s, ':'); i >= 0 && strings.IndexRune(s[:i], '/') < 0 {
+		protocol := strings.ToLower(s[:i])
+		if protocol != "http" && protocol != "https" && protocol != "mailto" {
+			return "#" + filterFailsafe
+		}
+	}
+	return s
+}
+
+// urlEscaper produces an output that can be embedded in a URL query.
+// The output can be embedded in an HTML attribute without further escaping.
+func urlEscaper(args ...interface{}) string {
+	return urlProcessor(false, args...)
+}
+
+// urlEscaper normalizes URL content so it can be embedded in a quote-delimited
+// string or parenthesis delimited url(...).
+// The normalizer does not encode all HTML specials. Specifically, it does not
+// encode '&' so correct embedding in an HTML attribute requires escaping of
+// '&' to '&amp;'.
+func urlNormalizer(args ...interface{}) string {
+	return urlProcessor(true, args...)
+}
+
+// urlProcessor normalizes (when norm is true) or escapes its input to produce
+// a valid hierarchical or opaque URL part.
+func urlProcessor(norm bool, args ...interface{}) string {
+	s, t := stringify(args...)
+	if t == contentTypeURL {
+		norm = true
+	}
+	var b bytes.Buffer
+	written := 0
+	// The byte loop below assumes that all URLs use UTF-8 as the
+	// content-encoding. This is similar to the URI to IRI encoding scheme
+	// defined in section 3.1 of  RFC 3987, and behaves the same as the
+	// EcmaScript builtin encodeURIComponent.
+	// It should not cause any misencoding of URLs in pages with
+	// Content-type: text/html;charset=UTF-8.
+	for i, n := 0, len(s); i < n; i++ {
+		c := s[i]
+		switch c {
+		// Single quote and parens are sub-delims in RFC 3986, but we
+		// escape them so the output can be embedded in single
+		// quoted attributes and unquoted CSS url(...) constructs.
+		// Single quotes are reserved in URLs, but are only used in
+		// the obsolete "mark" rule in an appendix in RFC 3986
+		// so can be safely encoded.
+		case '!', '#', '$', '&', '*', '+', ',', '/', ':', ';', '=', '?', '@', '[', ']':
+			if norm {
+				continue
+			}
+		// Unreserved according to RFC 3986 sec 2.3
+		// "For consistency, percent-encoded octets in the ranges of
+		// ALPHA (%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D),
+		// period (%2E), underscore (%5F), or tilde (%7E) should not be
+		// created by URI producers
+		case '-', '.', '_', '~':
+			continue
+		case '%':
+			// When normalizing do not re-encode valid escapes.
+			if norm && i+2 < len(s) && isHex(s[i+1]) && isHex(s[i+2]) {
+				continue
+			}
+		default:
+			// Unreserved according to RFC 3986 sec 2.3
+			if 'a' <= c && c <= 'z' {
+				continue
+			}
+			if 'A' <= c && c <= 'Z' {
+				continue
+			}
+			if '0' <= c && c <= '9' {
+				continue
+			}
+		}
+		b.WriteString(s[written:i])
+		fmt.Fprintf(&b, "%%%02x", c)
+		written = i + 1
+	}
+	if written == 0 {
+		return s
+	}
+	b.WriteString(s[written:])
+	return b.String()
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/.gitignore b/vendor/github.com/xanzy/go-gitlab/.gitignore
new file mode 100644
index 0000000..daf913b
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/.gitignore
@@ -0,0 +1,24 @@
+# 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
+*.test
+*.prof
diff --git a/vendor/github.com/xanzy/go-gitlab/.travis.yml b/vendor/github.com/xanzy/go-gitlab/.travis.yml
new file mode 100644
index 0000000..2620329
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/.travis.yml
@@ -0,0 +1,27 @@
+language: go
+
+go:
+  - 1.8.x
+  - 1.9.x
+  - 1.10.x
+  - master
+
+stages:
+  - lint
+  - test
+
+jobs:
+  include:
+    - stage: lint
+      script:
+        - go get github.com/golang/lint/golint
+        - golint -set_exit_status
+        - go vet -v
+    - stage: test
+      script:
+        - go test -v
+
+matrix:
+  allow_failures:
+    - go: master
+  fast_finish: true
diff --git a/vendor/github.com/xanzy/go-gitlab/CHANGELOG.md b/vendor/github.com/xanzy/go-gitlab/CHANGELOG.md
new file mode 100644
index 0000000..29e93ff
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/CHANGELOG.md
@@ -0,0 +1,27 @@
+go-github CHANGELOG
+===================
+
+0.6.0
+-----
+- Add support for the V4 Gitlab API. This means the older V3 API is no longer fully supported
+  with this version. If you still need that version, please use the `f-api-v3` branch.
+
+0.4.0
+-----
+- Add support to use [`sudo`](https://docs.gitlab.com/ce/api/README.html#sudo) for all API calls.
+- Add support for the Notification Settings API.
+- Add support for the Time Tracking API.
+- Make sure that the error response correctly outputs any returned errors.
+- And a reasonable number of smaller enhanchements and bugfixes.
+
+0.3.0
+-----
+- Moved the tags related API calls to their own service, following the Gitlab API structure.
+
+0.2.0
+-----
+- Convert all Option structs to use pointers for their fields.
+
+0.1.0
+-----
+- Initial release.
diff --git a/vendor/github.com/xanzy/go-gitlab/LICENSE b/vendor/github.com/xanzy/go-gitlab/LICENSE
new file mode 100644
index 0000000..e06d208
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/LICENSE
@@ -0,0 +1,202 @@
+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 {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/xanzy/go-gitlab/README.md b/vendor/github.com/xanzy/go-gitlab/README.md
new file mode 100644
index 0000000..dc79316
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/README.md
@@ -0,0 +1,171 @@
+# go-gitlab
+
+A GitLab API client enabling Go programs to interact with GitLab in a simple and uniform way
+
+[![Build Status](https://travis-ci.org/xanzy/go-gitlab.svg?branch=master)](https://travis-ci.org/xanzy/go-gitlab)
+[![GitHub license](https://img.shields.io/github/license/xanzy/go-gitlab.svg)](https://github.com/xanzy/go-gitlab/blob/master/LICENSE)
+[![Sourcegraph](https://sourcegraph.com/github.com/xanzy/go-gitlab/-/badge.svg)](https://sourcegraph.com/github.com/xanzy/go-gitlab?badge)
+[![GoDoc](https://godoc.org/github.com/xanzy/go-gitlab?status.svg)](https://godoc.org/github.com/xanzy/go-gitlab)
+[![Go Report Card](https://goreportcard.com/badge/github.com/xanzy/go-gitlab)](https://goreportcard.com/report/github.com/xanzy/go-gitlab)
+[![GitHub issues](https://img.shields.io/github/issues/xanzy/go-gitlab.svg)](https://github.com/xanzy/go-gitlab/issues)
+
+## NOTE
+
+Release v0.6.0 (released on 25-08-2017) no longer supports the older V3 Gitlab API. If
+you need V3 support, please use the `f-api-v3` branch. This release contains some backwards
+incompatible changes that were needed to fully support the V4 Gitlab API.
+
+## Coverage
+
+This API client package covers most of the existing Gitlab API calls and is updated regularly
+to add new and/or missing endpoints. Currently the following services are supported:
+
+- [x] Award Emojis
+- [x] Branches
+- [x] Broadcast Messages
+- [ ] Project-level Variables
+- [ ] Group-level Variables
+- [x] Commits
+- [ ] Custom Attributes
+- [x] Deployments
+- [x] Deploy Keys
+- [x] Environments
+- [ ] Epics
+- [ ] Epic Issues
+- [x] Events
+- [x] Feature flags
+- [ ] Geo Nodes
+- [x] Gitignores templates
+- [ ] GitLab CI Config templates
+- [x] Groups
+- [ ] Group Access Requests
+- [x] Group Members
+- [x] Issues
+- [x] Issue Boards
+- [x] Jobs
+- [ ] Keys
+- [x] Labels
+- [ ] License
+- [x] Merge Requests
+- [x] Merge Request Approvals
+- [x] Project Milestones
+- [ ] Group Milestones
+- [x] Namespaces
+- [x] Notes (comments)
+- [ ] Discussions (threaded comments)
+- [x] Notification settings
+- [ ] Open source license templates
+- [x] Pages Domains
+- [x] Pipelines
+- [x] Pipeline Triggers
+- [x] Pipeline Schedules
+- [x] Projects (including setting Webhooks)
+- [ ] Project Access Requests
+- [ ] Project badges
+- [ ] Project import/export
+- [x] Project Members
+- [x] Project Snippets
+- [x] Protected Branches
+- [x] Repositories
+- [x] Repository Files
+- [x] Runners
+- [ ] Search
+- [x] Services
+- [x] Settings
+- [x] Sidekiq metrics
+- [x] Session
+- [x] System Hooks
+- [x] Tags
+- [x] Todos
+- [x] Users
+- [x] Validate CI configuration
+- [x] Version
+- [x] Wikis
+
+## Usage
+
+```go
+import "github.com/xanzy/go-gitlab"
+```
+
+Construct a new GitLab client, then use the various services on the client to
+access different parts of the GitLab API. For example, to list all
+users:
+
+```go
+git := gitlab.NewClient(nil, "yourtokengoeshere")
+//git.SetBaseURL("https://git.mydomain.com/api/v3")
+users, _, err := git.Users.ListUsers()
+```
+
+Some API methods have optional parameters that can be passed. For example,
+to list all projects for user "svanharmelen":
+
+```go
+git := gitlab.NewClient(nil)
+opt := &ListProjectsOptions{Search: gitlab.String("svanharmelen")}
+projects, _, err := git.Projects.ListProjects(opt)
+```
+
+### Examples
+
+The [examples](https://github.com/xanzy/go-gitlab/tree/master/examples) directory
+contains a couple for clear examples, of which one is partially listed here as well:
+
+```go
+package main
+
+import (
+	"log"
+
+	"github.com/xanzy/go-gitlab"
+)
+
+func main() {
+	git := gitlab.NewClient(nil, "yourtokengoeshere")
+
+	// Create new project
+	p := &gitlab.CreateProjectOptions{
+		Name:                 gitlab.String("My Project"),
+		Description:          gitlab.String("Just a test project to play with"),
+		MergeRequestsEnabled: gitlab.Bool(true),
+		SnippetsEnabled:      gitlab.Bool(true),
+		Visibility:           gitlab.Visibility(gitlab.PublicVisibility),
+	}
+	project, _, err := git.Projects.CreateProject(p)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// Add a new snippet
+	s := &gitlab.CreateProjectSnippetOptions{
+		Title:           gitlab.String("Dummy Snippet"),
+		FileName:        gitlab.String("snippet.go"),
+		Code:            gitlab.String("package main...."),
+		Visibility:      gitlab.Visibility(gitlab.PublicVisibility),
+	}
+	_, _, err = git.ProjectSnippets.CreateSnippet(project.ID, s)
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+
+```
+
+For complete usage of go-gitlab, see the full [package docs](https://godoc.org/github.com/xanzy/go-gitlab).
+
+## ToDo
+
+- The biggest thing this package still needs is tests :disappointed:
+
+## Issues
+
+- If you have an issue: report it on the [issue tracker](https://github.com/xanzy/go-gitlab/issues)
+
+## Author
+
+Sander van Harmelen (<sander@xanzy.io>)
+
+## License
+
+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>
diff --git a/vendor/github.com/xanzy/go-gitlab/award_emojis.go b/vendor/github.com/xanzy/go-gitlab/award_emojis.go
new file mode 100644
index 0000000..d335609
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/award_emojis.go
@@ -0,0 +1,468 @@
+//
+// Copyright 2017, Arkbriar
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// AwardEmojiService handles communication with the emoji awards related methods
+// of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/award_emoji.html
+type AwardEmojiService struct {
+	client *Client
+}
+
+// AwardEmoji represents a GitLab Award Emoji.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/award_emoji.html
+type AwardEmoji struct {
+	ID   int    `json:"id"`
+	Name string `json:"name"`
+	User struct {
+		Name      string `json:"name"`
+		Username  string `json:"username"`
+		ID        int    `json:"id"`
+		State     string `json:"state"`
+		AvatarURL string `json:"avatar_url"`
+		WebURL    string `json:"web_url"`
+	} `json:"user"`
+	CreatedAt     *time.Time `json:"created_at"`
+	UpdatedAt     *time.Time `json:"updated_at"`
+	AwardableID   int        `json:"awardable_id"`
+	AwardableType string     `json:"awardable_type"`
+}
+
+const (
+	awardMergeRequest = "merge_requests"
+	awardIssue        = "issues"
+	awardSnippets     = "snippets"
+)
+
+// ListAwardEmojiOptions represents the available options for listing emoji
+// for each resources
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html
+type ListAwardEmojiOptions ListOptions
+
+// ListMergeRequestAwardEmoji gets a list of all award emoji on the merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
+func (s *AwardEmojiService) ListMergeRequestAwardEmoji(pid interface{}, mergeRequestIID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
+	return s.listAwardEmoji(pid, awardMergeRequest, mergeRequestIID, opt, options...)
+}
+
+// ListIssueAwardEmoji gets a list of all award emoji on the issue.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
+func (s *AwardEmojiService) ListIssueAwardEmoji(pid interface{}, issueIID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
+	return s.listAwardEmoji(pid, awardIssue, issueIID, opt, options...)
+}
+
+// ListSnippetAwardEmoji gets a list of all award emoji on the snippet.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
+func (s *AwardEmojiService) ListSnippetAwardEmoji(pid interface{}, snippetID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
+	return s.listAwardEmoji(pid, awardSnippets, snippetID, opt, options...)
+}
+
+func (s *AwardEmojiService) listAwardEmoji(pid interface{}, resource string, resourceID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/%s/%d/award_emoji",
+		url.QueryEscape(project),
+		resource,
+		resourceID,
+	)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var as []*AwardEmoji
+	resp, err := s.client.Do(req, &as)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return as, resp, err
+}
+
+// GetMergeRequestAwardEmoji get an award emoji from merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
+func (s *AwardEmojiService) GetMergeRequestAwardEmoji(pid interface{}, mergeRequestIID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
+	return s.getAwardEmoji(pid, awardMergeRequest, mergeRequestIID, awardID, options...)
+}
+
+// GetIssueAwardEmoji get an award emoji from issue.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
+func (s *AwardEmojiService) GetIssueAwardEmoji(pid interface{}, issueIID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
+	return s.getAwardEmoji(pid, awardIssue, issueIID, awardID, options...)
+}
+
+// GetSnippetAwardEmoji get an award emoji from snippet.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji
+func (s *AwardEmojiService) GetSnippetAwardEmoji(pid interface{}, snippetID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
+	return s.getAwardEmoji(pid, awardSnippets, snippetID, awardID, options...)
+}
+
+func (s *AwardEmojiService) getAwardEmoji(pid interface{}, resource string, resourceID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/%s/%d/award_emoji/%d",
+		url.QueryEscape(project),
+		resource,
+		resourceID,
+		awardID,
+	)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	a := new(AwardEmoji)
+	resp, err := s.client.Do(req, &a)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return a, resp, err
+}
+
+// CreateAwardEmojiOptions represents the available options for awarding emoji
+// for a resource
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji
+type CreateAwardEmojiOptions struct {
+	Name string `json:"name"`
+}
+
+// CreateMergeRequestAwardEmoji get an award emoji from merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji
+func (s *AwardEmojiService) CreateMergeRequestAwardEmoji(pid interface{}, mergeRequestIID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
+	return s.createAwardEmoji(pid, awardMergeRequest, mergeRequestIID, opt, options...)
+}
+
+// CreateIssueAwardEmoji get an award emoji from issue.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji
+func (s *AwardEmojiService) CreateIssueAwardEmoji(pid interface{}, issueIID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
+	return s.createAwardEmoji(pid, awardIssue, issueIID, opt, options...)
+}
+
+// CreateSnippetAwardEmoji get an award emoji from snippet.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji
+func (s *AwardEmojiService) CreateSnippetAwardEmoji(pid interface{}, snippetID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
+	return s.createAwardEmoji(pid, awardSnippets, snippetID, opt, options...)
+}
+
+func (s *AwardEmojiService) createAwardEmoji(pid interface{}, resource string, resourceID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/%s/%d/award_emoji",
+		url.QueryEscape(project),
+		resource,
+		resourceID,
+	)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	a := new(AwardEmoji)
+	resp, err := s.client.Do(req, &a)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return a, resp, err
+}
+
+// DeleteIssueAwardEmoji delete award emoji on an issue.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note
+func (s *AwardEmojiService) DeleteIssueAwardEmoji(pid interface{}, issueIID, awardID int, options ...OptionFunc) (*Response, error) {
+	return s.deleteAwardEmoji(pid, awardMergeRequest, issueIID, awardID, options...)
+}
+
+// DeleteMergeRequestAwardEmoji delete award emoji on a merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note
+func (s *AwardEmojiService) DeleteMergeRequestAwardEmoji(pid interface{}, mergeRequestIID, awardID int, options ...OptionFunc) (*Response, error) {
+	return s.deleteAwardEmoji(pid, awardMergeRequest, mergeRequestIID, awardID, options...)
+}
+
+// DeleteSnippetAwardEmoji delete award emoji on a snippet.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note
+func (s *AwardEmojiService) DeleteSnippetAwardEmoji(pid interface{}, snippetID, awardID int, options ...OptionFunc) (*Response, error) {
+	return s.deleteAwardEmoji(pid, awardMergeRequest, snippetID, awardID, options...)
+}
+
+// DeleteAwardEmoji Delete an award emoji on the specified resource.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#delete-an-award-emoji
+func (s *AwardEmojiService) deleteAwardEmoji(pid interface{}, resource string, resourceID, awardID int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/%s/%d/award_emoji/%d", url.QueryEscape(project), resource,
+		resourceID, awardID)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+	return s.client.Do(req, nil)
+}
+
+// ListIssuesAwardEmojiOnNote gets a list of all award emoji on a note from the
+// issue.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
+func (s *AwardEmojiService) ListIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
+	return s.listAwardEmojiOnNote(pid, awardIssue, issueID, noteID, opt, options...)
+}
+
+// ListMergeRequestAwardEmojiOnNote gets a list of all award emoji on a note
+// from the merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
+func (s *AwardEmojiService) ListMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
+	return s.listAwardEmojiOnNote(pid, awardMergeRequest, mergeRequestIID, noteID, opt, options...)
+}
+
+// ListSnippetAwardEmojiOnNote gets a list of all award emoji on a note from the
+// snippet.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
+func (s *AwardEmojiService) ListSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
+	return s.listAwardEmojiOnNote(pid, awardSnippets, snippetIID, noteID, opt, options...)
+}
+
+func (s *AwardEmojiService) listAwardEmojiOnNote(pid interface{}, resources string, ressourceID, noteID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/%s/%d/notes/%d/award_emoji", url.QueryEscape(project), resources,
+		ressourceID, noteID)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var as []*AwardEmoji
+	resp, err := s.client.Do(req, &as)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return as, resp, err
+}
+
+// GetIssuesAwardEmojiOnNote gets an award emoji on a note from an issue.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
+func (s *AwardEmojiService) GetIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
+	return s.getSingleNoteAwardEmoji(pid, awardIssue, issueID, noteID, awardID, options...)
+}
+
+// GetMergeRequestAwardEmojiOnNote gets an award emoji on a note from a
+// merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
+func (s *AwardEmojiService) GetMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
+	return s.getSingleNoteAwardEmoji(pid, awardMergeRequest, mergeRequestIID, noteID, awardID,
+		options...)
+}
+
+// GetSnippetAwardEmojiOnNote gets an award emoji on a note from a snippet.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
+func (s *AwardEmojiService) GetSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
+	return s.getSingleNoteAwardEmoji(pid, awardSnippets, snippetIID, noteID, awardID, options...)
+}
+
+func (s *AwardEmojiService) getSingleNoteAwardEmoji(pid interface{}, ressource string, resourceID, noteID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/%s/%d/notes/%d/award_emoji/%d",
+		url.QueryEscape(project),
+		ressource,
+		resourceID,
+		noteID,
+		awardID,
+	)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	a := new(AwardEmoji)
+	resp, err := s.client.Do(req, &a)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return a, resp, err
+}
+
+// CreateIssuesAwardEmojiOnNote gets an award emoji on a note from an issue.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
+func (s *AwardEmojiService) CreateIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
+	return s.createAwardEmojiOnNote(pid, awardIssue, issueID, noteID, opt, options...)
+}
+
+// CreateMergeRequestAwardEmojiOnNote gets an award emoji on a note from a
+// merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
+func (s *AwardEmojiService) CreateMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
+	return s.createAwardEmojiOnNote(pid, awardMergeRequest, mergeRequestIID, noteID, opt, options...)
+}
+
+// CreateSnippetAwardEmojiOnNote gets an award emoji on a note from a snippet.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
+func (s *AwardEmojiService) CreateSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
+	return s.createAwardEmojiOnNote(pid, awardSnippets, snippetIID, noteID, opt, options...)
+}
+
+// CreateAwardEmojiOnNote award emoji on a note.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note
+func (s *AwardEmojiService) createAwardEmojiOnNote(pid interface{}, resource string, resourceID, noteID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/%s/%d/notes/%d/award_emoji",
+		url.QueryEscape(project),
+		resource,
+		resourceID,
+		noteID,
+	)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	a := new(AwardEmoji)
+	resp, err := s.client.Do(req, &a)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return a, resp, err
+}
+
+// DeleteIssuesAwardEmojiOnNote deletes an award emoji on a note from an issue.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
+func (s *AwardEmojiService) DeleteIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID, awardID int, options ...OptionFunc) (*Response, error) {
+	return s.deleteAwardEmojiOnNote(pid, awardIssue, issueID, noteID, awardID, options...)
+}
+
+// DeleteMergeRequestAwardEmojiOnNote deletes an award emoji on a note from a
+// merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
+func (s *AwardEmojiService) DeleteMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID, awardID int, options ...OptionFunc) (*Response, error) {
+	return s.deleteAwardEmojiOnNote(pid, awardMergeRequest, mergeRequestIID, noteID, awardID,
+		options...)
+}
+
+// DeleteSnippetAwardEmojiOnNote deletes an award emoji on a note from a snippet.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes
+func (s *AwardEmojiService) DeleteSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID, awardID int, options ...OptionFunc) (*Response, error) {
+	return s.deleteAwardEmojiOnNote(pid, awardSnippets, snippetIID, noteID, awardID, options...)
+}
+
+func (s *AwardEmojiService) deleteAwardEmojiOnNote(pid interface{}, resource string, resourceID, noteID, awardID int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/%s/%d/notes/%d/award_emoji/%d",
+		url.QueryEscape(project),
+		resource,
+		resourceID,
+		noteID,
+		awardID,
+	)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/boards.go b/vendor/github.com/xanzy/go-gitlab/boards.go
new file mode 100644
index 0000000..7579a5b
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/boards.go
@@ -0,0 +1,261 @@
+//
+// Copyright 2015, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+)
+
+// IssueBoardsService handles communication with the issue board related
+// methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html
+type IssueBoardsService struct {
+	client *Client
+}
+
+// IssueBoard represents a GitLab issue board.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html
+type IssueBoard struct {
+	ID        int          `json:"id"`
+	Name      string       `json:"name"`
+	Project   *Project     `json:"project"`
+	Milestone *Milestone   `json:"milestone"`
+	Lists     []*BoardList `json:"lists"`
+}
+
+func (b IssueBoard) String() string {
+	return Stringify(b)
+}
+
+// BoardList represents a GitLab board list.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html
+type BoardList struct {
+	ID       int      `json:"id"`
+	Labels   []*Label `json:"labels"`
+	Position int      `json:"position"`
+}
+
+func (b BoardList) String() string {
+	return Stringify(b)
+}
+
+// ListIssueBoardsOptions represents the available ListIssueBoards() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#project-board
+type ListIssueBoardsOptions ListOptions
+
+// ListIssueBoards gets a list of all issue boards in a project.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#project-board
+func (s *IssueBoardsService) ListIssueBoards(pid interface{}, opt *ListIssueBoardsOptions, options ...OptionFunc) ([]*IssueBoard, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/boards", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var is []*IssueBoard
+	resp, err := s.client.Do(req, &is)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return is, resp, err
+}
+
+// GetIssueBoard gets a single issue board of a project.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#single-board
+func (s *IssueBoardsService) GetIssueBoard(pid interface{}, board int, options ...OptionFunc) (*IssueBoard, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/boards/%d", url.QueryEscape(project), board)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ib := new(IssueBoard)
+	resp, err := s.client.Do(req, ib)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ib, resp, err
+}
+
+// GetIssueBoardListsOptions represents the available GetIssueBoardLists() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#list-board-lists
+type GetIssueBoardListsOptions ListOptions
+
+// GetIssueBoardLists gets a list of the issue board's lists. Does not include
+// backlog and closed lists.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#list-board-lists
+func (s *IssueBoardsService) GetIssueBoardLists(pid interface{}, board int, opt *GetIssueBoardListsOptions, options ...OptionFunc) ([]*BoardList, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/boards/%d/lists", url.QueryEscape(project), board)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var bl []*BoardList
+	resp, err := s.client.Do(req, &bl)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return bl, resp, err
+}
+
+// GetIssueBoardList gets a single issue board list.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#single-board-list
+func (s *IssueBoardsService) GetIssueBoardList(pid interface{}, board, list int, options ...OptionFunc) (*BoardList, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/boards/%d/lists/%d",
+		url.QueryEscape(project),
+		board,
+		list,
+	)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	bl := new(BoardList)
+	resp, err := s.client.Do(req, bl)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return bl, resp, err
+}
+
+// CreateIssueBoardListOptions represents the available CreateIssueBoardList()
+// options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#new-board-list
+type CreateIssueBoardListOptions struct {
+	LabelID *int `url:"label_id" json:"label_id"`
+}
+
+// CreateIssueBoardList creates a new issue board list.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#new-board-list
+func (s *IssueBoardsService) CreateIssueBoardList(pid interface{}, board int, opt *CreateIssueBoardListOptions, options ...OptionFunc) (*BoardList, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/boards/%d/lists", url.QueryEscape(project), board)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	bl := new(BoardList)
+	resp, err := s.client.Do(req, bl)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return bl, resp, err
+}
+
+// UpdateIssueBoardListOptions represents the available UpdateIssueBoardList()
+// options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#edit-board-list
+type UpdateIssueBoardListOptions struct {
+	Position *int `url:"position" json:"position"`
+}
+
+// UpdateIssueBoardList updates the position of an existing issue board list.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#edit-board-list
+func (s *IssueBoardsService) UpdateIssueBoardList(pid interface{}, board, list int, opt *UpdateIssueBoardListOptions, options ...OptionFunc) (*BoardList, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/boards/%d/lists/%d",
+		url.QueryEscape(project),
+		board,
+		list,
+	)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	bl := new(BoardList)
+	resp, err := s.client.Do(req, bl)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return bl, resp, err
+}
+
+// DeleteIssueBoardList soft deletes an issue board list. Only for admins and
+// project owners.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/boards.html#delete-a-board-list
+func (s *IssueBoardsService) DeleteIssueBoardList(pid interface{}, board, list int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/boards/%d/lists/%d",
+		url.QueryEscape(project),
+		board,
+		list,
+	)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/branches.go b/vendor/github.com/xanzy/go-gitlab/branches.go
new file mode 100644
index 0000000..3f7bd0c
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/branches.go
@@ -0,0 +1,238 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+)
+
+// BranchesService handles communication with the branch related methods
+// of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/branches.html
+type BranchesService struct {
+	client *Client
+}
+
+// Branch represents a GitLab branch.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/branches.html
+type Branch struct {
+	Commit             *Commit `json:"commit"`
+	Name               string  `json:"name"`
+	Protected          bool    `json:"protected"`
+	Merged             bool    `json:"merged"`
+	DevelopersCanPush  bool    `json:"developers_can_push"`
+	DevelopersCanMerge bool    `json:"developers_can_merge"`
+}
+
+func (b Branch) String() string {
+	return Stringify(b)
+}
+
+// ListBranchesOptions represents the available ListBranches() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/branches.html#list-repository-branches
+type ListBranchesOptions ListOptions
+
+// ListBranches gets a list of repository branches from a project, sorted by
+// name alphabetically.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/branches.html#list-repository-branches
+func (s *BranchesService) ListBranches(pid interface{}, opts *ListBranchesOptions, options ...OptionFunc) ([]*Branch, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/branches", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opts, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var b []*Branch
+	resp, err := s.client.Do(req, &b)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return b, resp, err
+}
+
+// GetBranch gets a single project repository branch.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/branches.html#get-single-repository-branch
+func (s *BranchesService) GetBranch(pid interface{}, branch string, options ...OptionFunc) (*Branch, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/branches/%s", url.QueryEscape(project), url.QueryEscape(branch))
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	b := new(Branch)
+	resp, err := s.client.Do(req, b)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return b, resp, err
+}
+
+// ProtectBranchOptions represents the available ProtectBranch() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/branches.html#protect-repository-branch
+type ProtectBranchOptions struct {
+	DevelopersCanPush  *bool `url:"developers_can_push,omitempty" json:"developers_can_push,omitempty"`
+	DevelopersCanMerge *bool `url:"developers_can_merge,omitempty" json:"developers_can_merge,omitempty"`
+}
+
+// ProtectBranch protects a single project repository branch. This is an
+// idempotent function, protecting an already protected repository branch
+// still returns a 200 OK status code.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/branches.html#protect-repository-branch
+func (s *BranchesService) ProtectBranch(pid interface{}, branch string, opts *ProtectBranchOptions, options ...OptionFunc) (*Branch, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/branches/%s/protect", url.QueryEscape(project), url.QueryEscape(branch))
+
+	req, err := s.client.NewRequest("PUT", u, opts, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	b := new(Branch)
+	resp, err := s.client.Do(req, b)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return b, resp, err
+}
+
+// UnprotectBranch unprotects a single project repository branch. This is an
+// idempotent function, unprotecting an already unprotected repository branch
+// still returns a 200 OK status code.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/branches.html#unprotect-repository-branch
+func (s *BranchesService) UnprotectBranch(pid interface{}, branch string, options ...OptionFunc) (*Branch, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/branches/%s/unprotect", url.QueryEscape(project), url.QueryEscape(branch))
+
+	req, err := s.client.NewRequest("PUT", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	b := new(Branch)
+	resp, err := s.client.Do(req, b)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return b, resp, err
+}
+
+// CreateBranchOptions represents the available CreateBranch() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/branches.html#create-repository-branch
+type CreateBranchOptions struct {
+	Branch *string `url:"branch,omitempty" json:"branch,omitempty"`
+	Ref    *string `url:"ref,omitempty" json:"ref,omitempty"`
+}
+
+// CreateBranch creates branch from commit SHA or existing branch.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/branches.html#create-repository-branch
+func (s *BranchesService) CreateBranch(pid interface{}, opt *CreateBranchOptions, options ...OptionFunc) (*Branch, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/branches", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	b := new(Branch)
+	resp, err := s.client.Do(req, b)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return b, resp, err
+}
+
+// DeleteBranch deletes an existing branch.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/branches.html#delete-repository-branch
+func (s *BranchesService) DeleteBranch(pid interface{}, branch string, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/branches/%s", url.QueryEscape(project), url.QueryEscape(branch))
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// DeleteMergedBranches deletes all branches that are merged into the project's default branch.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/branches.html#delete-merged-branches
+func (s *BranchesService) DeleteMergedBranches(pid interface{}, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/merged_branches", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/broadcast_messages.go b/vendor/github.com/xanzy/go-gitlab/broadcast_messages.go
new file mode 100644
index 0000000..aee852d
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/broadcast_messages.go
@@ -0,0 +1,172 @@
+//
+// Copyright 2018, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"time"
+)
+
+// BroadcastMessagesService handles communication with the broadcast
+// messages methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/broadcast_messages.html
+type BroadcastMessagesService struct {
+	client *Client
+}
+
+// BroadcastMessage represents a GitLab issue board.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/broadcast_messages.html#get-all-broadcast-messages
+type BroadcastMessage struct {
+	Message  string     `json:"message"`
+	StartsAt *time.Time `json:"starts_at"`
+	EndsAt   *time.Time `json:"ends_at"`
+	Color    string     `json:"color"`
+	Font     string     `json:"font"`
+	ID       int        `json:"id"`
+	Active   bool       `json:"active"`
+}
+
+// ListBroadcastMessagesOptions represents the available ListBroadcastMessages()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/broadcast_messages.html#get-all-broadcast-messages
+type ListBroadcastMessagesOptions ListOptions
+
+// ListBroadcastMessages gets a list of all broadcasted messages.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/broadcast_messages.html#get-all-broadcast-messages
+func (s *BroadcastMessagesService) ListBroadcastMessages(opt *ListBroadcastMessagesOptions, options ...OptionFunc) ([]*BroadcastMessage, *Response, error) {
+	req, err := s.client.NewRequest("GET", "broadcast_messages", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var bs []*BroadcastMessage
+	resp, err := s.client.Do(req, &bs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return bs, resp, err
+}
+
+// GetBroadcastMessage gets a single broadcast message.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/broadcast_messages.html#get-a-specific-broadcast-message
+func (s *BroadcastMessagesService) GetBroadcastMessage(broadcast int, options ...OptionFunc) (*BroadcastMessage, *Response, error) {
+	u := fmt.Sprintf("broadcast_messages/%d", broadcast)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	b := new(BroadcastMessage)
+	resp, err := s.client.Do(req, &b)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return b, resp, err
+}
+
+// CreateBroadcastMessageOptions represents the available CreateBroadcastMessage()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/broadcast_messages.html#create-a-broadcast-message
+type CreateBroadcastMessageOptions struct {
+	Message  *string    `url:"message" json:"message"`
+	StartsAt *time.Time `url:"starts_at,omitempty" json:"starts_at,omitempty"`
+	EndsAt   *time.Time `url:"ends_at,omitempty" json:"ends_at,omitempty"`
+	Color    *string    `url:"color,omitempty" json:"color,omitempty"`
+	Font     *string    `url:"font,omitempty" json:"font,omitempty"`
+}
+
+// CreateBroadcastMessage creates a message to broadcast.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/broadcast_messages.html#create-a-broadcast-message
+func (s *BroadcastMessagesService) CreateBroadcastMessage(opt *CreateBroadcastMessageOptions, options ...OptionFunc) (*BroadcastMessage, *Response, error) {
+	req, err := s.client.NewRequest("POST", "broadcast_messages", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	b := new(BroadcastMessage)
+	resp, err := s.client.Do(req, &b)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return b, resp, err
+}
+
+// UpdateBroadcastMessageOptions represents the available CreateBroadcastMessage()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/broadcast_messages.html#update-a-broadcast-message
+type UpdateBroadcastMessageOptions struct {
+	Message  *string    `url:"message,omitempty" json:"message,omitempty"`
+	StartsAt *time.Time `url:"starts_at,omitempty" json:"starts_at,omitempty"`
+	EndsAt   *time.Time `url:"ends_at,omitempty" json:"ends_at,omitempty"`
+	Color    *string    `url:"color,omitempty" json:"color,omitempty"`
+	Font     *string    `url:"font,omitempty" json:"font,omitempty"`
+}
+
+// UpdateBroadcastMessage update a broadcasted message.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/broadcast_messages.html#update-a-broadcast-message
+func (s *BroadcastMessagesService) UpdateBroadcastMessage(broadcast int, opt *UpdateBroadcastMessageOptions, options ...OptionFunc) (*BroadcastMessage, *Response, error) {
+	u := fmt.Sprintf("broadcast_messages/%d", broadcast)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	b := new(BroadcastMessage)
+	resp, err := s.client.Do(req, &b)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return b, resp, err
+}
+
+// DeleteBroadcastMessage deletes a broadcasted message.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/broadcast_messages.html#delete-a-broadcast-message
+func (s *BroadcastMessagesService) DeleteBroadcastMessage(broadcast int, options ...OptionFunc) (*Response, error) {
+	u := fmt.Sprintf("broadcast_messages/%d", broadcast)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/build_variables.go b/vendor/github.com/xanzy/go-gitlab/build_variables.go
new file mode 100644
index 0000000..8a6c8cd
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/build_variables.go
@@ -0,0 +1,173 @@
+package gitlab
+
+import (
+	"fmt"
+	"net/url"
+)
+
+// BuildVariablesService handles communication with the project variables related methods
+// of the Gitlab API
+//
+// Gitlab API Docs : https://docs.gitlab.com/ce/api/build_variables.html
+type BuildVariablesService struct {
+	client *Client
+}
+
+// BuildVariable represents a variable available for each build of the given project
+//
+// Gitlab API Docs : https://docs.gitlab.com/ce/api/build_variables.html
+type BuildVariable struct {
+	Key       string `json:"key"`
+	Value     string `json:"value"`
+	Protected bool   `json:"protected"`
+}
+
+func (v BuildVariable) String() string {
+	return Stringify(v)
+}
+
+// ListBuildVariablesOptions are the parameters to ListBuildVariables()
+//
+// Gitlab API Docs:
+// https://docs.gitlab.com/ce/api/build_variables.html#list-project-variables
+type ListBuildVariablesOptions ListOptions
+
+// ListBuildVariables gets the a list of project variables in a project
+//
+// Gitlab API Docs:
+// https://docs.gitlab.com/ce/api/build_variables.html#list-project-variables
+func (s *BuildVariablesService) ListBuildVariables(pid interface{}, opts *ListBuildVariablesOptions, options ...OptionFunc) ([]*BuildVariable, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/variables", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opts, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var v []*BuildVariable
+	resp, err := s.client.Do(req, &v)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return v, resp, err
+}
+
+// GetBuildVariable gets a single project variable of a project
+//
+// Gitlab API Docs:
+// https://docs.gitlab.com/ce/api/build_variables.html#show-variable-details
+func (s *BuildVariablesService) GetBuildVariable(pid interface{}, key string, options ...OptionFunc) (*BuildVariable, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/variables/%s", url.QueryEscape(project), key)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	v := new(BuildVariable)
+	resp, err := s.client.Do(req, v)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return v, resp, err
+}
+
+// CreateBuildVariableOptions are the parameters to CreateBuildVariable()
+//
+// Gitlab API Docs:
+// https://docs.gitlab.com/ce/api/build_variables.html#create-variable
+type CreateBuildVariableOptions struct {
+	Key       *string `url:"key" json:"key"`
+	Value     *string `url:"value" json:"value"`
+	Protected *bool   `url:"protected,omitempty" json:"protected,omitempty"`
+}
+
+// CreateBuildVariable creates a variable for a given project
+//
+// Gitlab API Docs:
+// https://docs.gitlab.com/ce/api/build_variables.html#create-variable
+func (s *BuildVariablesService) CreateBuildVariable(pid interface{}, opt *CreateBuildVariableOptions, options ...OptionFunc) (*BuildVariable, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/variables", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	v := new(BuildVariable)
+	resp, err := s.client.Do(req, v)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return v, resp, err
+}
+
+// UpdateBuildVariableOptions are the parameters to UpdateBuildVariable()
+//
+// Gitlab API Docs:
+// https://docs.gitlab.com/ce/api/build_variables.html#update-variable
+type UpdateBuildVariableOptions struct {
+	Key       *string `url:"key" json:"key"`
+	Value     *string `url:"value" json:"value"`
+	Protected *bool   `url:"protected,omitempty" json:"protected,omitempty"`
+}
+
+// UpdateBuildVariable updates an existing project variable
+// The variable key must exist
+//
+// Gitlab API Docs:
+// https://docs.gitlab.com/ce/api/build_variables.html#update-variable
+func (s *BuildVariablesService) UpdateBuildVariable(pid interface{}, key string, opt *UpdateBuildVariableOptions, options ...OptionFunc) (*BuildVariable, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/variables/%s", url.QueryEscape(project), key)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	v := new(BuildVariable)
+	resp, err := s.client.Do(req, v)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return v, resp, err
+}
+
+// RemoveBuildVariable removes a project variable of a given project identified by its key
+//
+// Gitlab API Docs:
+// https://docs.gitlab.com/ce/api/build_variables.html#remove-variable
+func (s *BuildVariablesService) RemoveBuildVariable(pid interface{}, key string, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/variables/%s", url.QueryEscape(project), key)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/commits.go b/vendor/github.com/xanzy/go-gitlab/commits.go
new file mode 100644
index 0000000..5b5dc2a
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/commits.go
@@ -0,0 +1,448 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// CommitsService handles communication with the commit related methods
+// of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html
+type CommitsService struct {
+	client *Client
+}
+
+// Commit represents a GitLab commit.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html
+type Commit struct {
+	ID             string           `json:"id"`
+	ShortID        string           `json:"short_id"`
+	Title          string           `json:"title"`
+	AuthorName     string           `json:"author_name"`
+	AuthorEmail    string           `json:"author_email"`
+	AuthoredDate   *time.Time       `json:"authored_date"`
+	CommitterName  string           `json:"committer_name"`
+	CommitterEmail string           `json:"committer_email"`
+	CommittedDate  *time.Time       `json:"committed_date"`
+	CreatedAt      *time.Time       `json:"created_at"`
+	Message        string           `json:"message"`
+	ParentIDs      []string         `json:"parent_ids"`
+	Stats          *CommitStats     `json:"stats"`
+	Status         *BuildStateValue `json:"status"`
+}
+
+// CommitStats represents the number of added and deleted files in a commit.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html
+type CommitStats struct {
+	Additions int `json:"additions"`
+	Deletions int `json:"deletions"`
+	Total     int `json:"total"`
+}
+
+func (c Commit) String() string {
+	return Stringify(c)
+}
+
+// ListCommitsOptions represents the available ListCommits() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#list-repository-commits
+type ListCommitsOptions struct {
+	ListOptions
+	RefName *string    `url:"ref_name,omitempty" json:"ref_name,omitempty"`
+	Since   *time.Time `url:"since,omitempty" json:"since,omitempty"`
+	Until   *time.Time `url:"until,omitempty" json:"until,omitempty"`
+	Path    *string    `url:"path,omitempty" json:"path,omitempty"`
+	All     *bool      `url:"all,omitempty" json:"all,omitempty"`
+}
+
+// ListCommits gets a list of repository commits in a project.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#list-commits
+func (s *CommitsService) ListCommits(pid interface{}, opt *ListCommitsOptions, options ...OptionFunc) ([]*Commit, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/commits", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var c []*Commit
+	resp, err := s.client.Do(req, &c)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return c, resp, err
+}
+
+// FileAction represents the available actions that can be performed on a file.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
+type FileAction string
+
+// The available file actions.
+const (
+	FileCreate FileAction = "create"
+	FileDelete FileAction = "delete"
+	FileMove   FileAction = "move"
+	FileUpdate FileAction = "update"
+)
+
+// CommitAction represents a single file action within a commit.
+type CommitAction struct {
+	Action       FileAction `url:"action" json:"action"`
+	FilePath     string     `url:"file_path" json:"file_path"`
+	PreviousPath string     `url:"previous_path,omitempty" json:"previous_path,omitempty"`
+	Content      string     `url:"content,omitempty" json:"content,omitempty"`
+	Encoding     string     `url:"encoding,omitempty" json:"encoding,omitempty"`
+}
+
+// GetCommit gets a specific commit identified by the commit hash or name of a
+// branch or tag.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-a-single-commit
+func (s *CommitsService) GetCommit(pid interface{}, sha string, options ...OptionFunc) (*Commit, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/commits/%s", url.QueryEscape(project), sha)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	c := new(Commit)
+	resp, err := s.client.Do(req, c)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return c, resp, err
+}
+
+// CreateCommitOptions represents the available options for a new commit.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
+type CreateCommitOptions struct {
+	Branch        *string         `url:"branch" json:"branch"`
+	CommitMessage *string         `url:"commit_message" json:"commit_message"`
+	StartBranch   *string         `url:"start_branch,omitempty" json:"start_branch,omitempty"`
+	Actions       []*CommitAction `url:"actions" json:"actions"`
+	AuthorEmail   *string         `url:"author_email,omitempty" json:"author_email,omitempty"`
+	AuthorName    *string         `url:"author_name,omitempty" json:"author_name,omitempty"`
+}
+
+// CreateCommit creates a commit with multiple files and actions.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
+func (s *CommitsService) CreateCommit(pid interface{}, opt *CreateCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/commits", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var c *Commit
+	resp, err := s.client.Do(req, &c)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return c, resp, err
+}
+
+// Diff represents a GitLab diff.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html
+type Diff struct {
+	Diff        string `json:"diff"`
+	NewPath     string `json:"new_path"`
+	OldPath     string `json:"old_path"`
+	AMode       string `json:"a_mode"`
+	BMode       string `json:"b_mode"`
+	NewFile     bool   `json:"new_file"`
+	RenamedFile bool   `json:"renamed_file"`
+	DeletedFile bool   `json:"deleted_file"`
+}
+
+func (d Diff) String() string {
+	return Stringify(d)
+}
+
+// GetCommitDiffOptions represents the available GetCommitDiff() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/commits.html#get-the-diff-of-a-commit
+type GetCommitDiffOptions ListOptions
+
+// GetCommitDiff gets the diff of a commit in a project..
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/commits.html#get-the-diff-of-a-commit
+func (s *CommitsService) GetCommitDiff(pid interface{}, sha string, opt *GetCommitDiffOptions, options ...OptionFunc) ([]*Diff, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/commits/%s/diff", url.QueryEscape(project), sha)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var d []*Diff
+	resp, err := s.client.Do(req, &d)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return d, resp, err
+}
+
+// CommitComment represents a GitLab commit comment.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html
+type CommitComment struct {
+	Note     string `json:"note"`
+	Path     string `json:"path"`
+	Line     int    `json:"line"`
+	LineType string `json:"line_type"`
+	Author   Author `json:"author"`
+}
+
+// Author represents a GitLab commit author
+type Author struct {
+	ID        int        `json:"id"`
+	Username  string     `json:"username"`
+	Email     string     `json:"email"`
+	Name      string     `json:"name"`
+	State     string     `json:"state"`
+	Blocked   bool       `json:"blocked"`
+	CreatedAt *time.Time `json:"created_at"`
+}
+
+func (c CommitComment) String() string {
+	return Stringify(c)
+}
+
+// GetCommitCommentsOptions represents the available GetCommitComments() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/commits.html#get-the-comments-of-a-commit
+type GetCommitCommentsOptions ListOptions
+
+// GetCommitComments gets the comments of a commit in a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/commits.html#get-the-comments-of-a-commit
+func (s *CommitsService) GetCommitComments(pid interface{}, sha string, opt *GetCommitCommentsOptions, options ...OptionFunc) ([]*CommitComment, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", url.QueryEscape(project), sha)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var c []*CommitComment
+	resp, err := s.client.Do(req, &c)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return c, resp, err
+}
+
+// PostCommitCommentOptions represents the available PostCommitComment()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/commits.html#post-comment-to-commit
+type PostCommitCommentOptions struct {
+	Note     *string `url:"note,omitempty" json:"note,omitempty"`
+	Path     *string `url:"path" json:"path"`
+	Line     *int    `url:"line" json:"line"`
+	LineType *string `url:"line_type" json:"line_type"`
+}
+
+// PostCommitComment adds a comment to a commit. Optionally you can post
+// comments on a specific line of a commit. Therefor both path, line_new and
+// line_old are required.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/commits.html#post-comment-to-commit
+func (s *CommitsService) PostCommitComment(pid interface{}, sha string, opt *PostCommitCommentOptions, options ...OptionFunc) (*CommitComment, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", url.QueryEscape(project), sha)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	c := new(CommitComment)
+	resp, err := s.client.Do(req, c)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return c, resp, err
+}
+
+// GetCommitStatusesOptions represents the available GetCommitStatuses() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit
+type GetCommitStatusesOptions struct {
+	ListOptions
+	Ref   *string `url:"ref,omitempty" json:"ref,omitempty"`
+	Stage *string `url:"stage,omitempty" json:"stage,omitempty"`
+	Name  *string `url:"name,omitempty" json:"name,omitempty"`
+	All   *bool   `url:"all,omitempty" json:"all,omitempty"`
+}
+
+// CommitStatus represents a GitLab commit status.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit
+type CommitStatus struct {
+	ID          int        `json:"id"`
+	SHA         string     `json:"sha"`
+	Ref         string     `json:"ref"`
+	Status      string     `json:"status"`
+	Name        string     `json:"name"`
+	TargetURL   string     `json:"target_url"`
+	Description string     `json:"description"`
+	CreatedAt   *time.Time `json:"created_at"`
+	StartedAt   *time.Time `json:"started_at"`
+	FinishedAt  *time.Time `json:"finished_at"`
+	Author      Author     `json:"author"`
+}
+
+// GetCommitStatuses gets the statuses of a commit in a project.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit
+func (s *CommitsService) GetCommitStatuses(pid interface{}, sha string, opt *GetCommitStatusesOptions, options ...OptionFunc) ([]*CommitStatus, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/commits/%s/statuses", url.QueryEscape(project), sha)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var cs []*CommitStatus
+	resp, err := s.client.Do(req, &cs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return cs, resp, err
+}
+
+// SetCommitStatusOptions represents the available SetCommitStatus() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#post-the-status-to-commit
+type SetCommitStatusOptions struct {
+	State       BuildStateValue `url:"state" json:"state"`
+	Ref         *string         `url:"ref,omitempty" json:"ref,omitempty"`
+	Name        *string         `url:"name,omitempty" json:"name,omitempty"`
+	Context     *string         `url:"context,omitempty" json:"context,omitempty"`
+	TargetURL   *string         `url:"target_url,omitempty" json:"target_url,omitempty"`
+	Description *string         `url:"description,omitempty" json:"description,omitempty"`
+}
+
+// SetCommitStatus sets the status of a commit in a project.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#post-the-status-to-commit
+func (s *CommitsService) SetCommitStatus(pid interface{}, sha string, opt *SetCommitStatusOptions, options ...OptionFunc) (*CommitStatus, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/statuses/%s", url.QueryEscape(project), sha)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var cs *CommitStatus
+	resp, err := s.client.Do(req, &cs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return cs, resp, err
+}
+
+// CherryPickCommitOptions represents the available options for cherry-picking a commit.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit
+type CherryPickCommitOptions struct {
+	TargetBranch *string `url:"branch" json:"branch,omitempty"`
+}
+
+// CherryPickCommit sherry picks a commit to a given branch.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit
+func (s *CommitsService) CherryPickCommit(pid interface{}, sha string, opt *CherryPickCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/commits/%s/cherry_pick",
+		url.QueryEscape(project), url.QueryEscape(sha))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var c *Commit
+	resp, err := s.client.Do(req, &c)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return c, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/deploy_keys.go b/vendor/github.com/xanzy/go-gitlab/deploy_keys.go
new file mode 100644
index 0000000..7644459
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/deploy_keys.go
@@ -0,0 +1,201 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// DeployKeysService handles communication with the keys related methods
+// of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/deploy_keys.html
+type DeployKeysService struct {
+	client *Client
+}
+
+// DeployKey represents a GitLab deploy key.
+type DeployKey struct {
+	ID        int        `json:"id"`
+	Title     string     `json:"title"`
+	Key       string     `json:"key"`
+	CanPush   *bool      `json:"can_push"`
+	CreatedAt *time.Time `json:"created_at"`
+}
+
+func (k DeployKey) String() string {
+	return Stringify(k)
+}
+
+// ListAllDeployKeys gets a list of all deploy keys
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/deploy_keys.html#list-all-deploy-keys
+func (s *DeployKeysService) ListAllDeployKeys(options ...OptionFunc) ([]*DeployKey, *Response, error) {
+	req, err := s.client.NewRequest("GET", "deploy_keys", nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var ks []*DeployKey
+	resp, err := s.client.Do(req, &ks)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ks, resp, err
+}
+
+// ListProjectDeployKeysOptions represents the available ListProjectDeployKeys()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/deploy_keys.html#list-project-deploy-keys
+type ListProjectDeployKeysOptions ListOptions
+
+// ListProjectDeployKeys gets a list of a project's deploy keys
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/deploy_keys.html#list-project-deploy-keys
+func (s *DeployKeysService) ListProjectDeployKeys(pid interface{}, opt *ListProjectDeployKeysOptions, options ...OptionFunc) ([]*DeployKey, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/deploy_keys", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var ks []*DeployKey
+	resp, err := s.client.Do(req, &ks)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ks, resp, err
+}
+
+// GetDeployKey gets a single deploy key.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/deploy_keys.html#single-deploy-key
+func (s *DeployKeysService) GetDeployKey(pid interface{}, deployKey int, options ...OptionFunc) (*DeployKey, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/deploy_keys/%d", url.QueryEscape(project), deployKey)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	k := new(DeployKey)
+	resp, err := s.client.Do(req, k)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return k, resp, err
+}
+
+// AddDeployKeyOptions represents the available ADDDeployKey() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/deploy_keys.html#add-deploy-key
+type AddDeployKeyOptions struct {
+	Title   *string `url:"title,omitempty" json:"title,omitempty"`
+	Key     *string `url:"key,omitempty" json:"key,omitempty"`
+	CanPush *bool   `url:"can_push,omitempty" json:"can_push,omitempty"`
+}
+
+// AddDeployKey creates a new deploy key for a project. If deploy key already
+// exists in another project - it will be joined to project but only if
+// original one was is accessible by same user.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/deploy_keys.html#add-deploy-key
+func (s *DeployKeysService) AddDeployKey(pid interface{}, opt *AddDeployKeyOptions, options ...OptionFunc) (*DeployKey, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/deploy_keys", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	k := new(DeployKey)
+	resp, err := s.client.Do(req, k)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return k, resp, err
+}
+
+// DeleteDeployKey deletes a deploy key from a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/deploy_keys.html#delete-deploy-key
+func (s *DeployKeysService) DeleteDeployKey(pid interface{}, deployKey int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/deploy_keys/%d", url.QueryEscape(project), deployKey)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// EnableDeployKey enables a deploy key.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/deploy_keys.html#enable-deploy-key
+func (s *DeployKeysService) EnableDeployKey(pid interface{}, deployKey int, options ...OptionFunc) (*DeployKey, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/deploy_keys/%d/enable", url.QueryEscape(project), deployKey)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	k := new(DeployKey)
+	resp, err := s.client.Do(req, k)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return k, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/deployments.go b/vendor/github.com/xanzy/go-gitlab/deployments.go
new file mode 100644
index 0000000..e43d1c4
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/deployments.go
@@ -0,0 +1,121 @@
+//
+// Copyright 2018, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// DeploymentsService handles communication with the deployment related methods
+// of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html
+type DeploymentsService struct {
+	client *Client
+}
+
+// Deployment represents the Gitlab deployment
+type Deployment struct {
+	ID          int          `json:"id"`
+	IID         int          `json:"iid"`
+	Ref         string       `json:"ref"`
+	Sha         string       `json:"sha"`
+	CreatedAt   *time.Time   `json:"created_at"`
+	User        *ProjectUser `json:"user"`
+	Environment *Environment `json:"environment"`
+	Deployable  struct {
+		ID         int        `json:"id"`
+		Status     string     `json:"status"`
+		Stage      string     `json:"stage"`
+		Name       string     `json:"name"`
+		Ref        string     `json:"ref"`
+		Tag        bool       `json:"tag"`
+		Coverage   float64    `json:"coverage"`
+		CreatedAt  *time.Time `json:"created_at"`
+		StartedAt  *time.Time `json:"started_at"`
+		FinishedAt *time.Time `json:"finished_at"`
+		Duration   float64    `json:"duration"`
+		User       *User      `json:"user"`
+		Commit     *Commit    `json:"commit"`
+		Pipeline   struct {
+			ID     int    `json:"id"`
+			Sha    string `json:"sha"`
+			Ref    string `json:"ref"`
+			Status string `json:"status"`
+		} `json:"pipeline"`
+		Runner *Runner `json:"runner"`
+	} `json:"deployable"`
+}
+
+// ListProjectDeploymentsOptions represents the available ListProjectDeployments() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments
+type ListProjectDeploymentsOptions struct {
+	ListOptions
+	OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
+	Sort    *string `url:"sort,omitempty" json:"sort,omitempty"`
+}
+
+// ListProjectDeployments gets a list of deployments in a project.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments
+func (s *DeploymentsService) ListProjectDeployments(pid interface{}, opts *ListProjectDeploymentsOptions, options ...OptionFunc) ([]*Deployment, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/deployments", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opts, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var ds []*Deployment
+	resp, err := s.client.Do(req, &ds)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ds, resp, err
+}
+
+// GetProjectDeployment get a deployment for a project.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#get-a-specific-deployment
+func (s *DeploymentsService) GetProjectDeployment(pid interface{}, deployment int, options ...OptionFunc) (*Deployment, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/deployments/%d", url.QueryEscape(project), deployment)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	d := new(Deployment)
+	resp, err := s.client.Do(req, d)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return d, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/environments.go b/vendor/github.com/xanzy/go-gitlab/environments.go
new file mode 100644
index 0000000..c2305ee
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/environments.go
@@ -0,0 +1,166 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+)
+
+// EnvironmentsService handles communication with the environment related methods
+// of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/environments.html
+type EnvironmentsService struct {
+	client *Client
+}
+
+// Environment represents a GitLab environment.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/environments.html
+type Environment struct {
+	ID          int    `json:"id"`
+	Name        string `json:"name"`
+	Slug        string `json:"slug"`
+	ExternalURL string `json:"external_url"`
+}
+
+func (env Environment) String() string {
+	return Stringify(env)
+}
+
+// ListEnvironmentsOptions represents the available ListEnvironments() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/environments.html#list-environments
+type ListEnvironmentsOptions ListOptions
+
+// ListEnvironments gets a list of environments from a project, sorted by name
+// alphabetically.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/environments.html#list-environments
+func (s *EnvironmentsService) ListEnvironments(pid interface{}, opts *ListEnvironmentsOptions, options ...OptionFunc) ([]*Environment, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/environments", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opts, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var envs []*Environment
+	resp, err := s.client.Do(req, &envs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return envs, resp, err
+}
+
+// CreateEnvironmentOptions represents the available CreateEnvironment() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/environments.html#create-a-new-environment
+type CreateEnvironmentOptions struct {
+	Name        *string `url:"name,omitempty" json:"name,omitempty"`
+	ExternalURL *string `url:"external_url,omitempty" json:"external_url,omitempty"`
+}
+
+// CreateEnvironment adds a environment to a project. This is an idempotent
+// method and can be called multiple times with the same parameters. Createing
+// an environment that is already a environment does not affect the
+// existing environmentship.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/environments.html#create-a-new-environment
+func (s *EnvironmentsService) CreateEnvironment(pid interface{}, opt *CreateEnvironmentOptions, options ...OptionFunc) (*Environment, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/environments", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	env := new(Environment)
+	resp, err := s.client.Do(req, env)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return env, resp, err
+}
+
+// EditEnvironmentOptions represents the available EditEnvironment() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/environments.html#edit-an-existing-environment
+type EditEnvironmentOptions struct {
+	Name        *string `url:"name,omitempty" json:"name,omitempty"`
+	ExternalURL *string `url:"external_url,omitempty" json:"external_url,omitempty"`
+}
+
+// EditEnvironment updates a project team environment to a specified access level..
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/environments.html#edit-an-existing-environment
+func (s *EnvironmentsService) EditEnvironment(pid interface{}, environment int, opt *EditEnvironmentOptions, options ...OptionFunc) (*Environment, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/environments/%d", url.QueryEscape(project), environment)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	env := new(Environment)
+	resp, err := s.client.Do(req, env)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return env, resp, err
+}
+
+// DeleteEnvironment removes a environment from a project team.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/environments.html#remove-a-environment-from-a-group-or-project
+func (s *EnvironmentsService) DeleteEnvironment(pid interface{}, environment int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/environments/%d", url.QueryEscape(project), environment)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/event_types.go b/vendor/github.com/xanzy/go-gitlab/event_types.go
new file mode 100644
index 0000000..f051726
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/event_types.go
@@ -0,0 +1,649 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"time"
+)
+
+// PushEvent represents a push event.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#push-events
+type PushEvent struct {
+	ObjectKind  string `json:"object_kind"`
+	Before      string `json:"before"`
+	After       string `json:"after"`
+	Ref         string `json:"ref"`
+	CheckoutSha string `json:"checkout_sha"`
+	UserID      int    `json:"user_id"`
+	UserName    string `json:"user_name"`
+	UserEmail   string `json:"user_email"`
+	UserAvatar  string `json:"user_avatar"`
+	ProjectID   int    `json:"project_id"`
+	Project     struct {
+		Name              string          `json:"name"`
+		Description       string          `json:"description"`
+		AvatarURL         string          `json:"avatar_url"`
+		GitSSHURL         string          `json:"git_ssh_url"`
+		GitHTTPURL        string          `json:"git_http_url"`
+		Namespace         string          `json:"namespace"`
+		PathWithNamespace string          `json:"path_with_namespace"`
+		DefaultBranch     string          `json:"default_branch"`
+		Homepage          string          `json:"homepage"`
+		URL               string          `json:"url"`
+		SSHURL            string          `json:"ssh_url"`
+		HTTPURL           string          `json:"http_url"`
+		WebURL            string          `json:"web_url"`
+		Visibility        VisibilityValue `json:"visibility"`
+	} `json:"project"`
+	Repository *Repository `json:"repository"`
+	Commits    []*struct {
+		ID        string     `json:"id"`
+		Message   string     `json:"message"`
+		Timestamp *time.Time `json:"timestamp"`
+		URL       string     `json:"url"`
+		Author    struct {
+			Name  string `json:"name"`
+			Email string `json:"email"`
+		} `json:"author"`
+		Added    []string `json:"added"`
+		Modified []string `json:"modified"`
+		Removed  []string `json:"removed"`
+	} `json:"commits"`
+	TotalCommitsCount int `json:"total_commits_count"`
+}
+
+// TagEvent represents a tag event.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#tag-events
+type TagEvent struct {
+	ObjectKind  string `json:"object_kind"`
+	Before      string `json:"before"`
+	After       string `json:"after"`
+	Ref         string `json:"ref"`
+	CheckoutSha string `json:"checkout_sha"`
+	UserID      int    `json:"user_id"`
+	UserName    string `json:"user_name"`
+	UserAvatar  string `json:"user_avatar"`
+	ProjectID   int    `json:"project_id"`
+	Project     struct {
+		Name              string          `json:"name"`
+		Description       string          `json:"description"`
+		AvatarURL         string          `json:"avatar_url"`
+		GitSSHURL         string          `json:"git_ssh_url"`
+		GitHTTPURL        string          `json:"git_http_url"`
+		Namespace         string          `json:"namespace"`
+		PathWithNamespace string          `json:"path_with_namespace"`
+		DefaultBranch     string          `json:"default_branch"`
+		Homepage          string          `json:"homepage"`
+		URL               string          `json:"url"`
+		SSHURL            string          `json:"ssh_url"`
+		HTTPURL           string          `json:"http_url"`
+		WebURL            string          `json:"web_url"`
+		Visibility        VisibilityValue `json:"visibility"`
+	} `json:"project"`
+	Repository *Repository `json:"repository"`
+	Commits    []*struct {
+		ID        string     `json:"id"`
+		Message   string     `json:"message"`
+		Timestamp *time.Time `json:"timestamp"`
+		URL       string     `json:"url"`
+		Author    struct {
+			Name  string `json:"name"`
+			Email string `json:"email"`
+		} `json:"author"`
+		Added    []string `json:"added"`
+		Modified []string `json:"modified"`
+		Removed  []string `json:"removed"`
+	} `json:"commits"`
+	TotalCommitsCount int `json:"total_commits_count"`
+}
+
+// IssueEvent represents a issue event.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#issues-events
+type IssueEvent struct {
+	ObjectKind string `json:"object_kind"`
+	User       *User  `json:"user"`
+	Project    struct {
+		Name              string          `json:"name"`
+		Description       string          `json:"description"`
+		AvatarURL         string          `json:"avatar_url"`
+		GitSSHURL         string          `json:"git_ssh_url"`
+		GitHTTPURL        string          `json:"git_http_url"`
+		Namespace         string          `json:"namespace"`
+		PathWithNamespace string          `json:"path_with_namespace"`
+		DefaultBranch     string          `json:"default_branch"`
+		Homepage          string          `json:"homepage"`
+		URL               string          `json:"url"`
+		SSHURL            string          `json:"ssh_url"`
+		HTTPURL           string          `json:"http_url"`
+		WebURL            string          `json:"web_url"`
+		Visibility        VisibilityValue `json:"visibility"`
+	} `json:"project"`
+	Repository       *Repository `json:"repository"`
+	ObjectAttributes struct {
+		ID          int    `json:"id"`
+		Title       string `json:"title"`
+		AssigneeID  int    `json:"assignee_id"`
+		AuthorID    int    `json:"author_id"`
+		ProjectID   int    `json:"project_id"`
+		CreatedAt   string `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468)
+		UpdatedAt   string `json:"updated_at"` // Should be *time.Time (see Gitlab issue #21468)
+		Position    int    `json:"position"`
+		BranchName  string `json:"branch_name"`
+		Description string `json:"description"`
+		MilestoneID int    `json:"milestone_id"`
+		State       string `json:"state"`
+		IID         int    `json:"iid"`
+		URL         string `json:"url"`
+		Action      string `json:"action"`
+	} `json:"object_attributes"`
+	Assignee struct {
+		Name      string `json:"name"`
+		Username  string `json:"username"`
+		AvatarURL string `json:"avatar_url"`
+	} `json:"assignee"`
+}
+
+// CommitCommentEvent represents a comment on a commit event.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#comment-on-commit
+type CommitCommentEvent struct {
+	ObjectKind string `json:"object_kind"`
+	User       *User  `json:"user"`
+	ProjectID  int    `json:"project_id"`
+	Project    struct {
+		Name              string          `json:"name"`
+		Description       string          `json:"description"`
+		AvatarURL         string          `json:"avatar_url"`
+		GitSSHURL         string          `json:"git_ssh_url"`
+		GitHTTPURL        string          `json:"git_http_url"`
+		Namespace         string          `json:"namespace"`
+		PathWithNamespace string          `json:"path_with_namespace"`
+		DefaultBranch     string          `json:"default_branch"`
+		Homepage          string          `json:"homepage"`
+		URL               string          `json:"url"`
+		SSHURL            string          `json:"ssh_url"`
+		HTTPURL           string          `json:"http_url"`
+		WebURL            string          `json:"web_url"`
+		Visibility        VisibilityValue `json:"visibility"`
+	} `json:"project"`
+	Repository       *Repository `json:"repository"`
+	ObjectAttributes struct {
+		ID           int    `json:"id"`
+		Note         string `json:"note"`
+		NoteableType string `json:"noteable_type"`
+		AuthorID     int    `json:"author_id"`
+		CreatedAt    string `json:"created_at"`
+		UpdatedAt    string `json:"updated_at"`
+		ProjectID    int    `json:"project_id"`
+		Attachment   string `json:"attachment"`
+		LineCode     string `json:"line_code"`
+		CommitID     string `json:"commit_id"`
+		NoteableID   int    `json:"noteable_id"`
+		System       bool   `json:"system"`
+		StDiff       struct {
+			Diff        string `json:"diff"`
+			NewPath     string `json:"new_path"`
+			OldPath     string `json:"old_path"`
+			AMode       string `json:"a_mode"`
+			BMode       string `json:"b_mode"`
+			NewFile     bool   `json:"new_file"`
+			RenamedFile bool   `json:"renamed_file"`
+			DeletedFile bool   `json:"deleted_file"`
+		} `json:"st_diff"`
+	} `json:"object_attributes"`
+	Commit *struct {
+		ID        string     `json:"id"`
+		Message   string     `json:"message"`
+		Timestamp *time.Time `json:"timestamp"`
+		URL       string     `json:"url"`
+		Author    struct {
+			Name  string `json:"name"`
+			Email string `json:"email"`
+		} `json:"author"`
+	} `json:"commit"`
+}
+
+// MergeCommentEvent represents a comment on a merge event.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#comment-on-merge-request
+type MergeCommentEvent struct {
+	ObjectKind string `json:"object_kind"`
+	User       *User  `json:"user"`
+	ProjectID  int    `json:"project_id"`
+	Project    struct {
+		Name              string          `json:"name"`
+		Description       string          `json:"description"`
+		AvatarURL         string          `json:"avatar_url"`
+		GitSSHURL         string          `json:"git_ssh_url"`
+		GitHTTPURL        string          `json:"git_http_url"`
+		Namespace         string          `json:"namespace"`
+		PathWithNamespace string          `json:"path_with_namespace"`
+		DefaultBranch     string          `json:"default_branch"`
+		Homepage          string          `json:"homepage"`
+		URL               string          `json:"url"`
+		SSHURL            string          `json:"ssh_url"`
+		HTTPURL           string          `json:"http_url"`
+		WebURL            string          `json:"web_url"`
+		Visibility        VisibilityValue `json:"visibility"`
+	} `json:"project"`
+	ObjectAttributes struct {
+		ID           int    `json:"id"`
+		Note         string `json:"note"`
+		NoteableType string `json:"noteable_type"`
+		AuthorID     int    `json:"author_id"`
+		CreatedAt    string `json:"created_at"`
+		UpdatedAt    string `json:"updated_at"`
+		ProjectID    int    `json:"project_id"`
+		Attachment   string `json:"attachment"`
+		LineCode     string `json:"line_code"`
+		CommitID     string `json:"commit_id"`
+		NoteableID   int    `json:"noteable_id"`
+		System       bool   `json:"system"`
+		StDiff       *Diff  `json:"st_diff"`
+		URL          string `json:"url"`
+	} `json:"object_attributes"`
+	Repository   *Repository `json:"repository"`
+	MergeRequest struct {
+		ID              int    `json:"id"`
+		TargetBranch    string `json:"target_branch"`
+		SourceBranch    string `json:"source_branch"`
+		SourceProjectID int    `json:"source_project_id"`
+		AuthorID        int    `json:"author_id"`
+		AssigneeID      int    `json:"assignee_id"`
+		Title           string `json:"title"`
+		CreatedAt       string `json:"created_at"`
+		UpdatedAt       string `json:"updated_at"`
+		MilestoneID     int    `json:"milestone_id"`
+		State           string `json:"state"`
+		MergeStatus     string `json:"merge_status"`
+		TargetProjectID int    `json:"target_project_id"`
+		IID             int    `json:"iid"`
+		Description     string `json:"description"`
+		Position        int    `json:"position"`
+		LockedAt        string `json:"locked_at"`
+		UpdatedByID     int    `json:"updated_by_id"`
+		MergeError      string `json:"merge_error"`
+		MergeParams     struct {
+			ForceRemoveSourceBranch string `json:"force_remove_source_branch"`
+		} `json:"merge_params"`
+		MergeWhenPipelineSucceeds bool        `json:"merge_when_pipeline_succeeds"`
+		MergeUserID               int         `json:"merge_user_id"`
+		MergeCommitSha            string      `json:"merge_commit_sha"`
+		DeletedAt                 string      `json:"deleted_at"`
+		InProgressMergeCommitSha  string      `json:"in_progress_merge_commit_sha"`
+		LockVersion               int         `json:"lock_version"`
+		ApprovalsBeforeMerge      string      `json:"approvals_before_merge"`
+		RebaseCommitSha           string      `json:"rebase_commit_sha"`
+		TimeEstimate              int         `json:"time_estimate"`
+		Squash                    bool        `json:"squash"`
+		LastEditedAt              string      `json:"last_edited_at"`
+		LastEditedByID            int         `json:"last_edited_by_id"`
+		Source                    *Repository `json:"source"`
+		Target                    *Repository `json:"target"`
+		LastCommit                struct {
+			ID        string     `json:"id"`
+			Message   string     `json:"message"`
+			Timestamp *time.Time `json:"timestamp"`
+			URL       string     `json:"url"`
+			Author    struct {
+				Name  string `json:"name"`
+				Email string `json:"email"`
+			} `json:"author"`
+		} `json:"last_commit"`
+		WorkInProgress bool `json:"work_in_progress"`
+		TotalTimeSpent int  `json:"total_time_spent"`
+	} `json:"merge_request"`
+}
+
+// IssueCommentEvent represents a comment on an issue event.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#comment-on-issue
+type IssueCommentEvent struct {
+	ObjectKind string `json:"object_kind"`
+	User       *User  `json:"user"`
+	ProjectID  int    `json:"project_id"`
+	Project    struct {
+		Name              string          `json:"name"`
+		Description       string          `json:"description"`
+		AvatarURL         string          `json:"avatar_url"`
+		GitSSHURL         string          `json:"git_ssh_url"`
+		GitHTTPURL        string          `json:"git_http_url"`
+		Namespace         string          `json:"namespace"`
+		PathWithNamespace string          `json:"path_with_namespace"`
+		DefaultBranch     string          `json:"default_branch"`
+		Homepage          string          `json:"homepage"`
+		URL               string          `json:"url"`
+		SSHURL            string          `json:"ssh_url"`
+		HTTPURL           string          `json:"http_url"`
+		WebURL            string          `json:"web_url"`
+		Visibility        VisibilityValue `json:"visibility"`
+	} `json:"project"`
+	Repository       *Repository `json:"repository"`
+	ObjectAttributes struct {
+		ID           int     `json:"id"`
+		Note         string  `json:"note"`
+		NoteableType string  `json:"noteable_type"`
+		AuthorID     int     `json:"author_id"`
+		CreatedAt    string  `json:"created_at"`
+		UpdatedAt    string  `json:"updated_at"`
+		ProjectID    int     `json:"project_id"`
+		Attachment   string  `json:"attachment"`
+		LineCode     string  `json:"line_code"`
+		CommitID     string  `json:"commit_id"`
+		NoteableID   int     `json:"noteable_id"`
+		System       bool    `json:"system"`
+		StDiff       []*Diff `json:"st_diff"`
+		URL          string  `json:"url"`
+	} `json:"object_attributes"`
+	Issue *Issue `json:"issue"`
+}
+
+// SnippetCommentEvent represents a comment on a snippet event.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#comment-on-code-snippet
+type SnippetCommentEvent struct {
+	ObjectKind string `json:"object_kind"`
+	User       *User  `json:"user"`
+	ProjectID  int    `json:"project_id"`
+	Project    struct {
+		Name              string          `json:"name"`
+		Description       string          `json:"description"`
+		AvatarURL         string          `json:"avatar_url"`
+		GitSSHURL         string          `json:"git_ssh_url"`
+		GitHTTPURL        string          `json:"git_http_url"`
+		Namespace         string          `json:"namespace"`
+		PathWithNamespace string          `json:"path_with_namespace"`
+		DefaultBranch     string          `json:"default_branch"`
+		Homepage          string          `json:"homepage"`
+		URL               string          `json:"url"`
+		SSHURL            string          `json:"ssh_url"`
+		HTTPURL           string          `json:"http_url"`
+		WebURL            string          `json:"web_url"`
+		Visibility        VisibilityValue `json:"visibility"`
+	} `json:"project"`
+	Repository       *Repository `json:"repository"`
+	ObjectAttributes struct {
+		ID           int    `json:"id"`
+		Note         string `json:"note"`
+		NoteableType string `json:"noteable_type"`
+		AuthorID     int    `json:"author_id"`
+		CreatedAt    string `json:"created_at"`
+		UpdatedAt    string `json:"updated_at"`
+		ProjectID    int    `json:"project_id"`
+		Attachment   string `json:"attachment"`
+		LineCode     string `json:"line_code"`
+		CommitID     string `json:"commit_id"`
+		NoteableID   int    `json:"noteable_id"`
+		System       bool   `json:"system"`
+		StDiff       *Diff  `json:"st_diff"`
+		URL          string `json:"url"`
+	} `json:"object_attributes"`
+	Snippet *Snippet `json:"snippet"`
+}
+
+// MergeEvent represents a merge event.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#merge-request-events
+type MergeEvent struct {
+	ObjectKind string `json:"object_kind"`
+	User       *User  `json:"user"`
+	Project    struct {
+		Name              string          `json:"name"`
+		Description       string          `json:"description"`
+		AvatarURL         string          `json:"avatar_url"`
+		GitSSHURL         string          `json:"git_ssh_url"`
+		GitHTTPURL        string          `json:"git_http_url"`
+		Namespace         string          `json:"namespace"`
+		PathWithNamespace string          `json:"path_with_namespace"`
+		DefaultBranch     string          `json:"default_branch"`
+		Homepage          string          `json:"homepage"`
+		URL               string          `json:"url"`
+		SSHURL            string          `json:"ssh_url"`
+		HTTPURL           string          `json:"http_url"`
+		WebURL            string          `json:"web_url"`
+		Visibility        VisibilityValue `json:"visibility"`
+	} `json:"project"`
+	ObjectAttributes struct {
+		ID              int       `json:"id"`
+		TargetBranch    string    `json:"target_branch"`
+		SourceBranch    string    `json:"source_branch"`
+		SourceProjectID int       `json:"source_project_id"`
+		AuthorID        int       `json:"author_id"`
+		AssigneeID      int       `json:"assignee_id"`
+		Title           string    `json:"title"`
+		CreatedAt       string    `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468)
+		UpdatedAt       string    `json:"updated_at"` // Should be *time.Time (see Gitlab issue #21468)
+		StCommits       []*Commit `json:"st_commits"`
+		StDiffs         []*Diff   `json:"st_diffs"`
+		MilestoneID     int       `json:"milestone_id"`
+		State           string    `json:"state"`
+		MergeStatus     string    `json:"merge_status"`
+		TargetProjectID int       `json:"target_project_id"`
+		IID             int       `json:"iid"`
+		Description     string    `json:"description"`
+		Position        int       `json:"position"`
+		LockedAt        string    `json:"locked_at"`
+		UpdatedByID     int       `json:"updated_by_id"`
+		MergeError      string    `json:"merge_error"`
+		MergeParams     struct {
+			ForceRemoveSourceBranch string `json:"force_remove_source_branch"`
+		} `json:"merge_params"`
+		MergeWhenBuildSucceeds   bool        `json:"merge_when_build_succeeds"`
+		MergeUserID              int         `json:"merge_user_id"`
+		MergeCommitSha           string      `json:"merge_commit_sha"`
+		DeletedAt                string      `json:"deleted_at"`
+		ApprovalsBeforeMerge     string      `json:"approvals_before_merge"`
+		RebaseCommitSha          string      `json:"rebase_commit_sha"`
+		InProgressMergeCommitSha string      `json:"in_progress_merge_commit_sha"`
+		LockVersion              int         `json:"lock_version"`
+		TimeEstimate             int         `json:"time_estimate"`
+		Source                   *Repository `json:"source"`
+		Target                   *Repository `json:"target"`
+		LastCommit               struct {
+			ID        string     `json:"id"`
+			Message   string     `json:"message"`
+			Timestamp *time.Time `json:"timestamp"`
+			URL       string     `json:"url"`
+			Author    struct {
+				Name  string `json:"name"`
+				Email string `json:"email"`
+			} `json:"author"`
+		} `json:"last_commit"`
+		WorkInProgress bool   `json:"work_in_progress"`
+		URL            string `json:"url"`
+		Action         string `json:"action"`
+		Assignee       struct {
+			Name      string `json:"name"`
+			Username  string `json:"username"`
+			AvatarURL string `json:"avatar_url"`
+		} `json:"assignee"`
+	} `json:"object_attributes"`
+	Repository *Repository `json:"repository"`
+	Assignee   struct {
+		Name      string `json:"name"`
+		Username  string `json:"username"`
+		AvatarURL string `json:"avatar_url"`
+	} `json:"assignee"`
+}
+
+// WikiPageEvent represents a wiki page event.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#wiki-page-events
+type WikiPageEvent struct {
+	ObjectKind string `json:"object_kind"`
+	User       *User  `json:"user"`
+	Project    struct {
+		Name              string          `json:"name"`
+		Description       string          `json:"description"`
+		AvatarURL         string          `json:"avatar_url"`
+		GitSSHURL         string          `json:"git_ssh_url"`
+		GitHTTPURL        string          `json:"git_http_url"`
+		Namespace         string          `json:"namespace"`
+		PathWithNamespace string          `json:"path_with_namespace"`
+		DefaultBranch     string          `json:"default_branch"`
+		Homepage          string          `json:"homepage"`
+		URL               string          `json:"url"`
+		SSHURL            string          `json:"ssh_url"`
+		HTTPURL           string          `json:"http_url"`
+		WebURL            string          `json:"web_url"`
+		Visibility        VisibilityValue `json:"visibility"`
+	} `json:"project"`
+	Wiki struct {
+		WebURL            string `json:"web_url"`
+		GitSSHURL         string `json:"git_ssh_url"`
+		GitHTTPURL        string `json:"git_http_url"`
+		PathWithNamespace string `json:"path_with_namespace"`
+		DefaultBranch     string `json:"default_branch"`
+	} `json:"wiki"`
+	ObjectAttributes struct {
+		Title   string `json:"title"`
+		Content string `json:"content"`
+		Format  string `json:"format"`
+		Message string `json:"message"`
+		Slug    string `json:"slug"`
+		URL     string `json:"url"`
+		Action  string `json:"action"`
+	} `json:"object_attributes"`
+}
+
+// PipelineEvent represents a pipeline event.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#pipeline-events
+type PipelineEvent struct {
+	ObjectKind       string `json:"object_kind"`
+	ObjectAttributes struct {
+		ID         int      `json:"id"`
+		Ref        string   `json:"ref"`
+		Tag        bool     `json:"tag"`
+		Sha        string   `json:"sha"`
+		BeforeSha  string   `json:"before_sha"`
+		Status     string   `json:"status"`
+		Stages     []string `json:"stages"`
+		CreatedAt  string   `json:"created_at"`
+		FinishedAt string   `json:"finished_at"`
+		Duration   int      `json:"duration"`
+	} `json:"object_attributes"`
+	User struct {
+		Name      string `json:"name"`
+		Username  string `json:"username"`
+		AvatarURL string `json:"avatar_url"`
+	} `json:"user"`
+	Project struct {
+		Name              string          `json:"name"`
+		Description       string          `json:"description"`
+		AvatarURL         string          `json:"avatar_url"`
+		GitSSHURL         string          `json:"git_ssh_url"`
+		GitHTTPURL        string          `json:"git_http_url"`
+		Namespace         string          `json:"namespace"`
+		PathWithNamespace string          `json:"path_with_namespace"`
+		DefaultBranch     string          `json:"default_branch"`
+		Homepage          string          `json:"homepage"`
+		URL               string          `json:"url"`
+		SSHURL            string          `json:"ssh_url"`
+		HTTPURL           string          `json:"http_url"`
+		WebURL            string          `json:"web_url"`
+		Visibility        VisibilityValue `json:"visibility"`
+	} `json:"project"`
+	Commit struct {
+		ID        string     `json:"id"`
+		Message   string     `json:"message"`
+		Timestamp *time.Time `json:"timestamp"`
+		URL       string     `json:"url"`
+		Author    struct {
+			Name  string `json:"name"`
+			Email string `json:"email"`
+		} `json:"author"`
+	} `json:"commit"`
+	Builds []struct {
+		ID         int    `json:"id"`
+		Stage      string `json:"stage"`
+		Name       string `json:"name"`
+		Status     string `json:"status"`
+		CreatedAt  string `json:"created_at"`
+		StartedAt  string `json:"started_at"`
+		FinishedAt string `json:"finished_at"`
+		When       string `json:"when"`
+		Manual     bool   `json:"manual"`
+		User       struct {
+			Name      string `json:"name"`
+			Username  string `json:"username"`
+			AvatarURL string `json:"avatar_url"`
+		} `json:"user"`
+		Runner struct {
+			ID          int    `json:"id"`
+			Description string `json:"description"`
+			Active      bool   `json:"active"`
+			IsShared    bool   `json:"is_shared"`
+		} `json:"runner"`
+		ArtifactsFile struct {
+			Filename string `json:"filename"`
+			Size     int    `json:"size"`
+		} `json:"artifacts_file"`
+	} `json:"builds"`
+}
+
+//BuildEvent represents a build event
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/web_hooks/web_hooks.html#build-events
+type BuildEvent struct {
+	ObjectKind        string  `json:"object_kind"`
+	Ref               string  `json:"ref"`
+	Tag               bool    `json:"tag"`
+	BeforeSha         string  `json:"before_sha"`
+	Sha               string  `json:"sha"`
+	BuildID           int     `json:"build_id"`
+	BuildName         string  `json:"build_name"`
+	BuildStage        string  `json:"build_stage"`
+	BuildStatus       string  `json:"build_status"`
+	BuildStartedAt    string  `json:"build_started_at"`
+	BuildFinishedAt   string  `json:"build_finished_at"`
+	BuildDuration     float64 `json:"build_duration"`
+	BuildAllowFailure bool    `json:"build_allow_failure"`
+	ProjectID         int     `json:"project_id"`
+	ProjectName       string  `json:"project_name"`
+	User              struct {
+		ID    int    `json:"id"`
+		Name  string `json:"name"`
+		Email string `json:"email"`
+	} `json:"user"`
+	Commit struct {
+		ID          int    `json:"id"`
+		Sha         string `json:"sha"`
+		Message     string `json:"message"`
+		AuthorName  string `json:"author_name"`
+		AuthorEmail string `json:"author_email"`
+		Status      string `json:"status"`
+		Duration    int    `json:"duration"`
+		StartedAt   string `json:"started_at"`
+		FinishedAt  string `json:"finished_at"`
+	} `json:"commit"`
+	Repository *Repository `json:"repository"`
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/events.go b/vendor/github.com/xanzy/go-gitlab/events.go
new file mode 100644
index 0000000..4740e4a
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/events.go
@@ -0,0 +1,147 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// EventsService handles communication with the event related methods of
+// the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/events.html
+type EventsService struct {
+	client *Client
+}
+
+// ContributionEvent represents a user's contribution
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/events.html#get-user-contribution-events
+type ContributionEvent struct {
+	Title       string     `json:"title"`
+	ProjectID   int        `json:"project_id"`
+	ActionName  string     `json:"action_name"`
+	TargetID    int        `json:"target_id"`
+	TargetIID   int        `json:"target_iid"`
+	TargetType  string     `json:"target_type"`
+	AuthorID    int        `json:"author_id"`
+	TargetTitle string     `json:"target_title"`
+	CreatedAt   *time.Time `json:"created_at"`
+	PushData    struct {
+		CommitCount int    `json:"commit_count"`
+		Action      string `json:"action"`
+		RefType     string `json:"ref_type"`
+		CommitFrom  string `json:"commit_from"`
+		CommitTo    string `json:"commit_to"`
+		Ref         string `json:"ref"`
+		CommitTitle string `json:"commit_title"`
+	} `json:"push_data"`
+	Note   *Note `json:"note"`
+	Author struct {
+		Name      string `json:"name"`
+		Username  string `json:"username"`
+		ID        int    `json:"id"`
+		State     string `json:"state"`
+		AvatarURL string `json:"avatar_url"`
+		WebURL    string `json:"web_url"`
+	} `json:"author"`
+	AuthorUsername string `json:"author_username"`
+}
+
+// ListContributionEventsOptions represents the options for GetUserContributionEvents
+//
+// GitLap API docs:
+// https://docs.gitlab.com/ce/api/events.html#get-user-contribution-events
+type ListContributionEventsOptions struct {
+	ListOptions
+	Action     *EventTypeValue       `json:"action,omitempty"`
+	TargetType *EventTargetTypeValue `json:"target_type,omitempty"`
+	Before     *ISOTime              `json:"before,omitempty"`
+	After      *ISOTime              `json:"after,omitempty"`
+	Sort       *string               `json:"sort,omitempty"`
+}
+
+// ListUserContributionEvents retrieves user contribution events
+// for the specified user, sorted from newest to oldest.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/events.html#get-user-contribution-events
+func (s *UsersService) ListUserContributionEvents(uid interface{}, opt *ListContributionEventsOptions, options ...OptionFunc) ([]*ContributionEvent, *Response, error) {
+	user, err := parseID(uid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("users/%s/events", user)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var cs []*ContributionEvent
+	resp, err := s.client.Do(req, &cs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return cs, resp, err
+}
+
+// ListCurrentUserContributionEvents gets a list currently authenticated user's events
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#list-currently-authenticated-user-39-s-events
+func (s *EventsService) ListCurrentUserContributionEvents(opt *ListContributionEventsOptions, options ...OptionFunc) ([]*ContributionEvent, *Response, error) {
+	req, err := s.client.NewRequest("GET", "events", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var cs []*ContributionEvent
+	resp, err := s.client.Do(req, &cs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return cs, resp, err
+}
+
+// ListProjectContributionEvents gets a list currently authenticated user's events
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#list-a-project-39-s-visible-events
+func (s *EventsService) ListProjectContributionEvents(pid interface{}, opt *ListContributionEventsOptions, options ...OptionFunc) ([]*ContributionEvent, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("%s/events", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var cs []*ContributionEvent
+	resp, err := s.client.Do(req, &cs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return cs, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/feature_flags.go b/vendor/github.com/xanzy/go-gitlab/feature_flags.go
new file mode 100644
index 0000000..b6380ab
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/feature_flags.go
@@ -0,0 +1,79 @@
+package gitlab
+
+import (
+	"fmt"
+	"net/url"
+)
+
+// FeaturesService handles the communication with the application FeaturesService
+// related methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/features.html
+type FeaturesService struct {
+	client *Client
+}
+
+// Feature represents a GitLab feature flag.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/features.html
+type Feature struct {
+	Name  string `json:"name"`
+	State string `json:"state"`
+	Gates []Gate
+}
+
+// Gate represents a gate of a GitLab feature flag.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/features.html
+type Gate struct {
+	Key   string      `json:"key"`
+	Value interface{} `json:"value"`
+}
+
+func (f Feature) String() string {
+	return Stringify(f)
+}
+
+// ListFeatures gets a list of feature flags
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/features.html#list-all-features
+func (s *FeaturesService) ListFeatures(options ...OptionFunc) ([]*Feature, *Response, error) {
+	req, err := s.client.NewRequest("GET", "features", nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var f []*Feature
+	resp, err := s.client.Do(req, &f)
+	if err != nil {
+		return nil, resp, err
+	}
+	return f, resp, err
+}
+
+// SetFeatureFlag sets or creates a feature flag gate
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/features.html#set-or-create-a-feature
+func (s *FeaturesService) SetFeatureFlag(name string, value interface{}, options ...OptionFunc) (*Feature, *Response, error) {
+	u := fmt.Sprintf("features/%s", url.QueryEscape(name))
+
+	opt := struct {
+		Value interface{} `url:"value" json:"value"`
+	}{
+		value,
+	}
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	f := &Feature{}
+	resp, err := s.client.Do(req, f)
+	if err != nil {
+		return nil, resp, err
+	}
+	return f, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/gitignore_templates.go b/vendor/github.com/xanzy/go-gitlab/gitignore_templates.go
new file mode 100644
index 0000000..5c911f4
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/gitignore_templates.go
@@ -0,0 +1,84 @@
+//
+// Copyright 2018, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+)
+
+// GitIgnoreTemplatesService handles communication with the gitignore
+// templates related methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/templates/gitignores.html
+type GitIgnoreTemplatesService struct {
+	client *Client
+}
+
+// GitIgnoreTemplate represents a GitLab gitignore template.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/templates/gitignores.html
+type GitIgnoreTemplate struct {
+	Name    string `json:"name"`
+	Content string `json:"content"`
+}
+
+// ListTemplatesOptions represents the available ListAllTemplates() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/templates/gitignores.html#list-gitignore-templates
+type ListTemplatesOptions ListOptions
+
+// ListTemplates get a list of available git ignore templates
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/templates/gitignores.html#list-gitignore-templates
+func (s *GitIgnoreTemplatesService) ListTemplates(opt *ListTemplatesOptions, options ...OptionFunc) ([]*GitIgnoreTemplate, *Response, error) {
+	req, err := s.client.NewRequest("GET", "templates/gitignores", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var gs []*GitIgnoreTemplate
+	resp, err := s.client.Do(req, &gs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return gs, resp, err
+}
+
+// GetTemplate get a git ignore template
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/templates/gitignores.html#single-gitignore-template
+func (s *GitIgnoreTemplatesService) GetTemplate(key string, options ...OptionFunc) (*GitIgnoreTemplate, *Response, error) {
+	u := fmt.Sprintf("templates/gitignores/%s", url.QueryEscape(key))
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	g := new(GitIgnoreTemplate)
+	resp, err := s.client.Do(req, g)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return g, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/gitlab.go b/vendor/github.com/xanzy/go-gitlab/gitlab.go
new file mode 100644
index 0000000..65cb419
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/gitlab.go
@@ -0,0 +1,836 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/google/go-querystring/query"
+	"golang.org/x/oauth2"
+)
+
+const (
+	defaultBaseURL = "https://gitlab.com/"
+	apiVersionPath = "api/v4/"
+	userAgent      = "go-gitlab"
+)
+
+// authType represents an authentication type within GitLab.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/
+type authType int
+
+// List of available authentication types.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/
+const (
+	basicAuth authType = iota
+	oAuthToken
+	privateToken
+)
+
+// AccessLevelValue represents a permission level within GitLab.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/permissions/permissions.html
+type AccessLevelValue int
+
+// List of available access levels
+//
+// GitLab API docs: https://docs.gitlab.com/ce/permissions/permissions.html
+const (
+	NoPermissions        AccessLevelValue = 0
+	GuestPermissions     AccessLevelValue = 10
+	ReporterPermissions  AccessLevelValue = 20
+	DeveloperPermissions AccessLevelValue = 30
+	MasterPermissions    AccessLevelValue = 40
+	OwnerPermission      AccessLevelValue = 50
+)
+
+// BuildStateValue represents a GitLab build state.
+type BuildStateValue string
+
+// These constants represent all valid build states.
+const (
+	Pending  BuildStateValue = "pending"
+	Running  BuildStateValue = "running"
+	Success  BuildStateValue = "success"
+	Failed   BuildStateValue = "failed"
+	Canceled BuildStateValue = "canceled"
+	Skipped  BuildStateValue = "skipped"
+)
+
+// ISOTime represents an ISO 8601 formatted date
+type ISOTime time.Time
+
+// ISO 8601 date format
+const iso8601 = "2006-01-02"
+
+// MarshalJSON implements the json.Marshaler interface
+func (t ISOTime) MarshalJSON() ([]byte, error) {
+	if y := time.Time(t).Year(); y < 0 || y >= 10000 {
+		// ISO 8901 uses 4 digits for the years
+		return nil, errors.New("ISOTime.MarshalJSON: year outside of range [0,9999]")
+	}
+
+	b := make([]byte, 0, len(iso8601)+2)
+	b = append(b, '"')
+	b = time.Time(t).AppendFormat(b, iso8601)
+	b = append(b, '"')
+
+	return b, nil
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface
+func (t *ISOTime) UnmarshalJSON(data []byte) error {
+	// Ignore null, like in the main JSON package
+	if string(data) == "null" {
+		return nil
+	}
+
+	isotime, err := time.Parse(`"`+iso8601+`"`, string(data))
+	*t = ISOTime(isotime)
+
+	return err
+}
+
+// String implements the Stringer interface
+func (t ISOTime) String() string {
+	return time.Time(t).Format(iso8601)
+}
+
+// NotificationLevelValue represents a notification level.
+type NotificationLevelValue int
+
+// String implements the fmt.Stringer interface.
+func (l NotificationLevelValue) String() string {
+	return notificationLevelNames[l]
+}
+
+// MarshalJSON implements the json.Marshaler interface.
+func (l NotificationLevelValue) MarshalJSON() ([]byte, error) {
+	return json.Marshal(l.String())
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (l *NotificationLevelValue) UnmarshalJSON(data []byte) error {
+	var raw interface{}
+	if err := json.Unmarshal(data, &raw); err != nil {
+		return err
+	}
+
+	switch raw := raw.(type) {
+	case float64:
+		*l = NotificationLevelValue(raw)
+	case string:
+		*l = notificationLevelTypes[raw]
+	case nil:
+		// No action needed.
+	default:
+		return fmt.Errorf("json: cannot unmarshal %T into Go value of type %T", raw, *l)
+	}
+
+	return nil
+}
+
+// List of valid notification levels.
+const (
+	DisabledNotificationLevel NotificationLevelValue = iota
+	ParticipatingNotificationLevel
+	WatchNotificationLevel
+	GlobalNotificationLevel
+	MentionNotificationLevel
+	CustomNotificationLevel
+)
+
+var notificationLevelNames = [...]string{
+	"disabled",
+	"participating",
+	"watch",
+	"global",
+	"mention",
+	"custom",
+}
+
+var notificationLevelTypes = map[string]NotificationLevelValue{
+	"disabled":      DisabledNotificationLevel,
+	"participating": ParticipatingNotificationLevel,
+	"watch":         WatchNotificationLevel,
+	"global":        GlobalNotificationLevel,
+	"mention":       MentionNotificationLevel,
+	"custom":        CustomNotificationLevel,
+}
+
+// VisibilityValue represents a visibility level within GitLab.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/
+type VisibilityValue string
+
+// List of available visibility levels
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/
+const (
+	PrivateVisibility  VisibilityValue = "private"
+	InternalVisibility VisibilityValue = "internal"
+	PublicVisibility   VisibilityValue = "public"
+)
+
+// MergeMethodValue represents a project merge type within GitLab.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#project-merge-method
+type MergeMethodValue string
+
+// List of available merge type
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#project-merge-method
+const (
+	NoFastForwardMerge MergeMethodValue = "merge"
+	FastForwardMerge   MergeMethodValue = "ff"
+	RebaseMerge        MergeMethodValue = "rebase_merge"
+)
+
+// EventTypeValue represents actions type for contribution events
+type EventTypeValue string
+
+// List of available action type
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#action-types
+const (
+	CreatedEventType   EventTypeValue = "created"
+	UpdatedEventType   EventTypeValue = "updated"
+	ClosedEventType    EventTypeValue = "closed"
+	ReopenedEventType  EventTypeValue = "reopened"
+	PushedEventType    EventTypeValue = "pushed"
+	CommentedEventType EventTypeValue = "commented"
+	MergedEventType    EventTypeValue = "merged"
+	JoinedEventType    EventTypeValue = "joined"
+	LeftEventType      EventTypeValue = "left"
+	DestroyedEventType EventTypeValue = "destroyed"
+	ExpiredEventType   EventTypeValue = "expired"
+)
+
+// EventTargetTypeValue represents actions type value for contribution events
+type EventTargetTypeValue string
+
+// List of available action type
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#target-types
+const (
+	IssueEventTargetType        EventTargetTypeValue = "issue"
+	MilestoneEventTargetType    EventTargetTypeValue = "milestone"
+	MergeRequestEventTargetType EventTargetTypeValue = "merge_request"
+	NoteEventTargetType         EventTargetTypeValue = "note"
+	ProjectEventTargetType      EventTargetTypeValue = "project"
+	SnippetEventTargetType      EventTargetTypeValue = "snippet"
+	UserEventTargetType         EventTargetTypeValue = "user"
+)
+
+// A Client manages communication with the GitLab API.
+type Client struct {
+	// HTTP client used to communicate with the API.
+	client *http.Client
+
+	// Base URL for API requests. Defaults to the public GitLab API, but can be
+	// set to a domain endpoint to use with a self hosted GitLab server. baseURL
+	// should always be specified with a trailing slash.
+	baseURL *url.URL
+
+	// Token type used to make authenticated API calls.
+	authType authType
+
+	// Username and password used for basix authentication.
+	username, password string
+
+	// Token used to make authenticated API calls.
+	token string
+
+	// User agent used when communicating with the GitLab API.
+	UserAgent string
+
+	// Services used for talking to different parts of the GitLab API.
+	AwardEmoji            *AwardEmojiService
+	Branches              *BranchesService
+	BuildVariables        *BuildVariablesService
+	BroadcastMessage      *BroadcastMessagesService
+	Commits               *CommitsService
+	DeployKeys            *DeployKeysService
+	Deployments           *DeploymentsService
+	Environments          *EnvironmentsService
+	Events                *EventsService
+	Features              *FeaturesService
+	GitIgnoreTemplates    *GitIgnoreTemplatesService
+	Groups                *GroupsService
+	GroupMembers          *GroupMembersService
+	GroupMilestones       *GroupMilestonesService
+	Issues                *IssuesService
+	IssueLinks            *IssueLinksService
+	Jobs                  *JobsService
+	Boards                *IssueBoardsService
+	Labels                *LabelsService
+	MergeRequests         *MergeRequestsService
+	MergeRequestApprovals *MergeRequestApprovalsService
+	Milestones            *MilestonesService
+	Namespaces            *NamespacesService
+	Notes                 *NotesService
+	NotificationSettings  *NotificationSettingsService
+	PagesDomains          *PagesDomainsService
+	Pipelines             *PipelinesService
+	PipelineSchedules     *PipelineSchedulesService
+	PipelineTriggers      *PipelineTriggersService
+	Projects              *ProjectsService
+	ProjectMembers        *ProjectMembersService
+	ProjectSnippets       *ProjectSnippetsService
+	ProtectedBranches     *ProtectedBranchesService
+	Repositories          *RepositoriesService
+	RepositoryFiles       *RepositoryFilesService
+	Runners               *RunnersService
+	Search                *SearchService
+	Services              *ServicesService
+	Session               *SessionService
+	Settings              *SettingsService
+	Sidekiq               *SidekiqService
+	Snippets              *SnippetsService
+	SystemHooks           *SystemHooksService
+	Tags                  *TagsService
+	Todos                 *TodosService
+	Users                 *UsersService
+	Validate              *ValidateService
+	Version               *VersionService
+	Wikis                 *WikisService
+}
+
+// ListOptions specifies the optional parameters to various List methods that
+// support pagination.
+type ListOptions struct {
+	// For paginated result sets, page of results to retrieve.
+	Page int `url:"page,omitempty" json:"page,omitempty"`
+
+	// For paginated result sets, the number of results to include per page.
+	PerPage int `url:"per_page,omitempty" json:"per_page,omitempty"`
+}
+
+// NewClient returns a new GitLab API client. If a nil httpClient is
+// provided, http.DefaultClient will be used. To use API methods which require
+// authentication, provide a valid private or personal token.
+func NewClient(httpClient *http.Client, token string) *Client {
+	client := newClient(httpClient)
+	client.authType = privateToken
+	client.token = token
+	return client
+}
+
+// NewBasicAuthClient returns a new GitLab API client. If a nil httpClient is
+// provided, http.DefaultClient will be used. To use API methods which require
+// authentication, provide a valid username and password.
+func NewBasicAuthClient(httpClient *http.Client, endpoint, username, password string) (*Client, error) {
+	client := newClient(httpClient)
+	client.authType = basicAuth
+	client.username = username
+	client.password = password
+	client.SetBaseURL(endpoint)
+
+	err := client.requestOAuthToken(context.TODO())
+	if err != nil {
+		return nil, err
+	}
+
+	return client, nil
+}
+
+func (c *Client) requestOAuthToken(ctx context.Context) error {
+	config := &oauth2.Config{
+		Endpoint: oauth2.Endpoint{
+			AuthURL:  fmt.Sprintf("%s://%s/oauth/authorize", c.BaseURL().Scheme, c.BaseURL().Host),
+			TokenURL: fmt.Sprintf("%s://%s/oauth/token", c.BaseURL().Scheme, c.BaseURL().Host),
+		},
+	}
+	ctx = context.WithValue(ctx, oauth2.HTTPClient, c.client)
+	t, err := config.PasswordCredentialsToken(ctx, c.username, c.password)
+	if err != nil {
+		return err
+	}
+	c.token = t.AccessToken
+	return nil
+}
+
+// NewOAuthClient returns a new GitLab API client. If a nil httpClient is
+// provided, http.DefaultClient will be used. To use API methods which require
+// authentication, provide a valid oauth token.
+func NewOAuthClient(httpClient *http.Client, token string) *Client {
+	client := newClient(httpClient)
+	client.authType = oAuthToken
+	client.token = token
+	return client
+}
+
+func newClient(httpClient *http.Client) *Client {
+	if httpClient == nil {
+		httpClient = http.DefaultClient
+	}
+
+	c := &Client{client: httpClient, UserAgent: userAgent}
+	if err := c.SetBaseURL(defaultBaseURL); err != nil {
+		// Should never happen since defaultBaseURL is our constant.
+		panic(err)
+	}
+
+	// Create the internal timeStats service.
+	timeStats := &timeStatsService{client: c}
+
+	// Create all the public services.
+	c.AwardEmoji = &AwardEmojiService{client: c}
+	c.Branches = &BranchesService{client: c}
+	c.BuildVariables = &BuildVariablesService{client: c}
+	c.BroadcastMessage = &BroadcastMessagesService{client: c}
+	c.Commits = &CommitsService{client: c}
+	c.DeployKeys = &DeployKeysService{client: c}
+	c.Deployments = &DeploymentsService{client: c}
+	c.Environments = &EnvironmentsService{client: c}
+	c.Events = &EventsService{client: c}
+	c.Features = &FeaturesService{client: c}
+	c.GitIgnoreTemplates = &GitIgnoreTemplatesService{client: c}
+	c.Groups = &GroupsService{client: c}
+	c.GroupMembers = &GroupMembersService{client: c}
+	c.GroupMilestones = &GroupMilestonesService{client: c}
+	c.Issues = &IssuesService{client: c, timeStats: timeStats}
+	c.IssueLinks = &IssueLinksService{client: c}
+	c.Jobs = &JobsService{client: c}
+	c.Boards = &IssueBoardsService{client: c}
+	c.Labels = &LabelsService{client: c}
+	c.MergeRequests = &MergeRequestsService{client: c, timeStats: timeStats}
+	c.MergeRequestApprovals = &MergeRequestApprovalsService{client: c}
+	c.Milestones = &MilestonesService{client: c}
+	c.Namespaces = &NamespacesService{client: c}
+	c.Notes = &NotesService{client: c}
+	c.NotificationSettings = &NotificationSettingsService{client: c}
+	c.PagesDomains = &PagesDomainsService{client: c}
+	c.Pipelines = &PipelinesService{client: c}
+	c.PipelineSchedules = &PipelineSchedulesService{client: c}
+	c.PipelineTriggers = &PipelineTriggersService{client: c}
+	c.Projects = &ProjectsService{client: c}
+	c.ProjectMembers = &ProjectMembersService{client: c}
+	c.ProjectSnippets = &ProjectSnippetsService{client: c}
+	c.ProtectedBranches = &ProtectedBranchesService{client: c}
+	c.Repositories = &RepositoriesService{client: c}
+	c.RepositoryFiles = &RepositoryFilesService{client: c}
+	c.Runners = &RunnersService{client: c}
+	c.Services = &ServicesService{client: c}
+	c.Search = &SearchService{client: c}
+	c.Session = &SessionService{client: c}
+	c.Settings = &SettingsService{client: c}
+	c.Sidekiq = &SidekiqService{client: c}
+	c.Snippets = &SnippetsService{client: c}
+	c.SystemHooks = &SystemHooksService{client: c}
+	c.Tags = &TagsService{client: c}
+	c.Todos = &TodosService{client: c}
+	c.Users = &UsersService{client: c}
+	c.Validate = &ValidateService{client: c}
+	c.Version = &VersionService{client: c}
+	c.Wikis = &WikisService{client: c}
+
+	return c
+}
+
+// BaseURL return a copy of the baseURL.
+func (c *Client) BaseURL() *url.URL {
+	u := *c.baseURL
+	return &u
+}
+
+// SetBaseURL sets the base URL for API requests to a custom endpoint. urlStr
+// should always be specified with a trailing slash.
+func (c *Client) SetBaseURL(urlStr string) error {
+	// Make sure the given URL end with a slash
+	if !strings.HasSuffix(urlStr, "/") {
+		urlStr += "/"
+	}
+
+	baseURL, err := url.Parse(urlStr)
+	if err != nil {
+		return err
+	}
+
+	if !strings.HasSuffix(baseURL.Path, apiVersionPath) {
+		baseURL.Path += apiVersionPath
+	}
+
+	// Update the base URL of the client.
+	c.baseURL = baseURL
+
+	return nil
+}
+
+// NewRequest creates an API request. A relative URL path can be provided in
+// urlStr, in which case it is resolved relative to the base URL of the Client.
+// Relative URL paths should always be specified without a preceding slash. If
+// specified, the value pointed to by body is JSON encoded and included as the
+// request body.
+func (c *Client) NewRequest(method, path string, opt interface{}, options []OptionFunc) (*http.Request, error) {
+	u := *c.baseURL
+	// Set the encoded opaque data
+	u.Opaque = c.baseURL.Path + path
+
+	if opt != nil {
+		q, err := query.Values(opt)
+		if err != nil {
+			return nil, err
+		}
+		u.RawQuery = q.Encode()
+	}
+
+	req := &http.Request{
+		Method:     method,
+		URL:        &u,
+		Proto:      "HTTP/1.1",
+		ProtoMajor: 1,
+		ProtoMinor: 1,
+		Header:     make(http.Header),
+		Host:       u.Host,
+	}
+
+	for _, fn := range options {
+		if fn == nil {
+			continue
+		}
+
+		if err := fn(req); err != nil {
+			return nil, err
+		}
+	}
+
+	if method == "POST" || method == "PUT" {
+		bodyBytes, err := json.Marshal(opt)
+		if err != nil {
+			return nil, err
+		}
+		bodyReader := bytes.NewReader(bodyBytes)
+
+		u.RawQuery = ""
+		req.Body = ioutil.NopCloser(bodyReader)
+		req.ContentLength = int64(bodyReader.Len())
+		req.Header.Set("Content-Type", "application/json")
+	}
+
+	req.Header.Set("Accept", "application/json")
+
+	switch c.authType {
+	case basicAuth, oAuthToken:
+		req.Header.Set("Authorization", "Bearer "+c.token)
+	case privateToken:
+		req.Header.Set("PRIVATE-TOKEN", c.token)
+	}
+
+	if c.UserAgent != "" {
+		req.Header.Set("User-Agent", c.UserAgent)
+	}
+
+	return req, nil
+}
+
+// Response is a GitLab API response. This wraps the standard http.Response
+// returned from GitLab and provides convenient access to things like
+// pagination links.
+type Response struct {
+	*http.Response
+
+	// These fields provide the page values for paginating through a set of
+	// results. Any or all of these may be set to the zero value for
+	// responses that are not part of a paginated set, or for which there
+	// are no additional pages.
+	TotalItems   int
+	TotalPages   int
+	ItemsPerPage int
+	CurrentPage  int
+	NextPage     int
+	PreviousPage int
+}
+
+// newResponse creates a new Response for the provided http.Response.
+func newResponse(r *http.Response) *Response {
+	response := &Response{Response: r}
+	response.populatePageValues()
+	return response
+}
+
+const (
+	xTotal      = "X-Total"
+	xTotalPages = "X-Total-Pages"
+	xPerPage    = "X-Per-Page"
+	xPage       = "X-Page"
+	xNextPage   = "X-Next-Page"
+	xPrevPage   = "X-Prev-Page"
+)
+
+// populatePageValues parses the HTTP Link response headers and populates the
+// various pagination link values in the Response.
+func (r *Response) populatePageValues() {
+	if totalItems := r.Response.Header.Get(xTotal); totalItems != "" {
+		r.TotalItems, _ = strconv.Atoi(totalItems)
+	}
+	if totalPages := r.Response.Header.Get(xTotalPages); totalPages != "" {
+		r.TotalPages, _ = strconv.Atoi(totalPages)
+	}
+	if itemsPerPage := r.Response.Header.Get(xPerPage); itemsPerPage != "" {
+		r.ItemsPerPage, _ = strconv.Atoi(itemsPerPage)
+	}
+	if currentPage := r.Response.Header.Get(xPage); currentPage != "" {
+		r.CurrentPage, _ = strconv.Atoi(currentPage)
+	}
+	if nextPage := r.Response.Header.Get(xNextPage); nextPage != "" {
+		r.NextPage, _ = strconv.Atoi(nextPage)
+	}
+	if previousPage := r.Response.Header.Get(xPrevPage); previousPage != "" {
+		r.PreviousPage, _ = strconv.Atoi(previousPage)
+	}
+}
+
+// Do sends an API request and returns the API response. The API response is
+// JSON decoded and stored in the value pointed to by v, or returned as an
+// error if an API error has occurred. If v implements the io.Writer
+// interface, the raw response body will be written to v, without attempting to
+// first decode it.
+func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
+	resp, err := c.client.Do(req)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode == http.StatusUnauthorized && c.authType == basicAuth {
+		err = c.requestOAuthToken(req.Context())
+		if err != nil {
+			return nil, err
+		}
+		return c.Do(req, v)
+	}
+
+	response := newResponse(resp)
+
+	err = CheckResponse(resp)
+	if err != nil {
+		// even though there was an error, we still return the response
+		// in case the caller wants to inspect it further
+		return response, err
+	}
+
+	if v != nil {
+		if w, ok := v.(io.Writer); ok {
+			_, err = io.Copy(w, resp.Body)
+		} else {
+			err = json.NewDecoder(resp.Body).Decode(v)
+		}
+	}
+
+	return response, err
+}
+
+// Helper function to accept and format both the project ID or name as project
+// identifier for all API calls.
+func parseID(id interface{}) (string, error) {
+	switch v := id.(type) {
+	case int:
+		return strconv.Itoa(v), nil
+	case string:
+		return v, nil
+	default:
+		return "", fmt.Errorf("invalid ID type %#v, the ID must be an int or a string", id)
+	}
+}
+
+// An ErrorResponse reports one or more errors caused by an API request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/README.html#data-validation-and-error-reporting
+type ErrorResponse struct {
+	Body     []byte
+	Response *http.Response
+	Message  string
+}
+
+func (e *ErrorResponse) Error() string {
+	path, _ := url.QueryUnescape(e.Response.Request.URL.Opaque)
+	u := fmt.Sprintf("%s://%s%s", e.Response.Request.URL.Scheme, e.Response.Request.URL.Host, path)
+	return fmt.Sprintf("%s %s: %d %s", e.Response.Request.Method, u, e.Response.StatusCode, e.Message)
+}
+
+// CheckResponse checks the API response for errors, and returns them if present.
+func CheckResponse(r *http.Response) error {
+	switch r.StatusCode {
+	case 200, 201, 202, 204, 304:
+		return nil
+	}
+
+	errorResponse := &ErrorResponse{Response: r}
+	data, err := ioutil.ReadAll(r.Body)
+	if err == nil && data != nil {
+		errorResponse.Body = data
+
+		var raw interface{}
+		if err := json.Unmarshal(data, &raw); err != nil {
+			errorResponse.Message = "failed to parse unknown error format"
+		} else {
+			errorResponse.Message = parseError(raw)
+		}
+	}
+
+	return errorResponse
+}
+
+// Format:
+// {
+//     "message": {
+//         "<property-name>": [
+//             "<error-message>",
+//             "<error-message>",
+//             ...
+//         ],
+//         "<embed-entity>": {
+//             "<property-name>": [
+//                 "<error-message>",
+//                 "<error-message>",
+//                 ...
+//             ],
+//         }
+//     },
+//     "error": "<error-message>"
+// }
+func parseError(raw interface{}) string {
+	switch raw := raw.(type) {
+	case string:
+		return raw
+
+	case []interface{}:
+		var errs []string
+		for _, v := range raw {
+			errs = append(errs, parseError(v))
+		}
+		return fmt.Sprintf("[%s]", strings.Join(errs, ", "))
+
+	case map[string]interface{}:
+		var errs []string
+		for k, v := range raw {
+			errs = append(errs, fmt.Sprintf("{%s: %s}", k, parseError(v)))
+		}
+		sort.Strings(errs)
+		return strings.Join(errs, ", ")
+
+	default:
+		return fmt.Sprintf("failed to parse unexpected error type: %T", raw)
+	}
+}
+
+// OptionFunc can be passed to all API requests to make the API call as if you were
+// another user, provided your private token is from an administrator account.
+//
+// GitLab docs: https://docs.gitlab.com/ce/api/README.html#sudo
+type OptionFunc func(*http.Request) error
+
+// WithSudo takes either a username or user ID and sets the SUDO request header
+func WithSudo(uid interface{}) OptionFunc {
+	return func(req *http.Request) error {
+		user, err := parseID(uid)
+		if err != nil {
+			return err
+		}
+		req.Header.Set("SUDO", user)
+		return nil
+	}
+}
+
+// WithContext runs the request with the provided context
+func WithContext(ctx context.Context) OptionFunc {
+	return func(req *http.Request) error {
+		*req = *req.WithContext(ctx)
+		return nil
+	}
+}
+
+// Bool is a helper routine that allocates a new bool value
+// to store v and returns a pointer to it.
+func Bool(v bool) *bool {
+	p := new(bool)
+	*p = v
+	return p
+}
+
+// Int is a helper routine that allocates a new int32 value
+// to store v and returns a pointer to it, but unlike Int32
+// its argument value is an int.
+func Int(v int) *int {
+	p := new(int)
+	*p = v
+	return p
+}
+
+// String is a helper routine that allocates a new string value
+// to store v and returns a pointer to it.
+func String(v string) *string {
+	p := new(string)
+	*p = v
+	return p
+}
+
+// AccessLevel is a helper routine that allocates a new AccessLevelValue
+// to store v and returns a pointer to it.
+func AccessLevel(v AccessLevelValue) *AccessLevelValue {
+	p := new(AccessLevelValue)
+	*p = v
+	return p
+}
+
+// BuildState is a helper routine that allocates a new BuildStateValue
+// to store v and returns a pointer to it.
+func BuildState(v BuildStateValue) *BuildStateValue {
+	p := new(BuildStateValue)
+	*p = v
+	return p
+}
+
+// NotificationLevel is a helper routine that allocates a new NotificationLevelValue
+// to store v and returns a pointer to it.
+func NotificationLevel(v NotificationLevelValue) *NotificationLevelValue {
+	p := new(NotificationLevelValue)
+	*p = v
+	return p
+}
+
+// Visibility is a helper routine that allocates a new VisibilityValue
+// to store v and returns a pointer to it.
+func Visibility(v VisibilityValue) *VisibilityValue {
+	p := new(VisibilityValue)
+	*p = v
+	return p
+}
+
+// MergeMethod is a helper routine that allocates a new MergeMethod
+// to sotre v and returns a pointer to it.
+func MergeMethod(v MergeMethodValue) *MergeMethodValue {
+	p := new(MergeMethodValue)
+	*p = v
+	return p
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/group_members.go b/vendor/github.com/xanzy/go-gitlab/group_members.go
new file mode 100644
index 0000000..9423ff6
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/group_members.go
@@ -0,0 +1,195 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// GroupMembersService handles communication with the group members
+// related methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/members.html
+type GroupMembersService struct {
+	client *Client
+}
+
+// GroupMember represents a GitLab group member.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/members.html
+type GroupMember struct {
+	ID          int              `json:"id"`
+	Username    string           `json:"username"`
+	Email       string           `json:"email"`
+	Name        string           `json:"name"`
+	State       string           `json:"state"`
+	CreatedAt   *time.Time       `json:"created_at"`
+	AccessLevel AccessLevelValue `json:"access_level"`
+	ExpiresAt   *ISOTime         `json:"expires_at"`
+}
+
+// ListGroupMembersOptions represents the available ListGroupMembers()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
+type ListGroupMembersOptions struct {
+	ListOptions
+	Query *string `url:"query,omitempty" json:"query,omitempty"`
+}
+
+// ListGroupMembers get a list of group members viewable by the authenticated
+// user.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
+func (s *GroupsService) ListGroupMembers(gid interface{}, opt *ListGroupMembersOptions, options ...OptionFunc) ([]*GroupMember, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/members", url.QueryEscape(group))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var gm []*GroupMember
+	resp, err := s.client.Do(req, &gm)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return gm, resp, err
+}
+
+// AddGroupMemberOptions represents the available AddGroupMember() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/members.html#add-a-member-to-a-group-or-project
+type AddGroupMemberOptions struct {
+	UserID      *int              `url:"user_id,omitempty" json:"user_id,omitempty"`
+	AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
+	ExpiresAt   *string           `url:"expires_at,omitempty" json:"expires_at"`
+}
+
+// GetGroupMember gets a member of a group.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/members.html#get-a-member-of-a-group-or-project
+func (s *GroupMembersService) GetGroupMember(gid interface{}, user int, options ...OptionFunc) (*GroupMember, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/members/%d", url.QueryEscape(group), user)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	gm := new(GroupMember)
+	resp, err := s.client.Do(req, gm)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return gm, resp, err
+}
+
+// AddGroupMember adds a user to the list of group members.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/members.html#add-a-member-to-a-group-or-project
+func (s *GroupMembersService) AddGroupMember(gid interface{}, opt *AddGroupMemberOptions, options ...OptionFunc) (*GroupMember, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/members", url.QueryEscape(group))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	gm := new(GroupMember)
+	resp, err := s.client.Do(req, gm)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return gm, resp, err
+}
+
+// EditGroupMemberOptions represents the available EditGroupMember()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/members.html#edit-a-member-of-a-group-or-project
+type EditGroupMemberOptions struct {
+	AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
+	ExpiresAt   *string           `url:"expires_at,omitempty" json:"expires_at"`
+}
+
+// EditGroupMember updates a member of a group.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/members.html#edit-a-member-of-a-group-or-project
+func (s *GroupMembersService) EditGroupMember(gid interface{}, user int, opt *EditGroupMemberOptions, options ...OptionFunc) (*GroupMember, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/members/%d", url.QueryEscape(group), user)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	gm := new(GroupMember)
+	resp, err := s.client.Do(req, gm)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return gm, resp, err
+}
+
+// RemoveGroupMember removes user from user team.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/members.html#remove-a-member-from-a-group-or-project
+func (s *GroupMembersService) RemoveGroupMember(gid interface{}, user int, options ...OptionFunc) (*Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("groups/%s/members/%d", url.QueryEscape(group), user)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/group_milestones.go b/vendor/github.com/xanzy/go-gitlab/group_milestones.go
new file mode 100644
index 0000000..8335a47
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/group_milestones.go
@@ -0,0 +1,250 @@
+//
+// Copyright 2018, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// GroupMilestonesService handles communication with the milestone related
+// methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/group_milestones.html
+type GroupMilestonesService struct {
+	client *Client
+}
+
+// GroupMilestone represents a GitLab milestone.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/group_milestones.html
+type GroupMilestone struct {
+	ID          int        `json:"id"`
+	IID         int        `json:"iid"`
+	GroupID     int        `json:"group_id"`
+	Title       string     `json:"title"`
+	Description string     `json:"description"`
+	StartDate   *ISOTime   `json:"start_date"`
+	DueDate     *ISOTime   `json:"due_date"`
+	State       string     `json:"state"`
+	UpdatedAt   *time.Time `json:"updated_at"`
+	CreatedAt   *time.Time `json:"created_at"`
+}
+
+func (m GroupMilestone) String() string {
+	return Stringify(m)
+}
+
+// ListGroupMilestonesOptions represents the available
+// ListGroupMilestones() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/group_milestones.html#list-group-milestones
+type ListGroupMilestonesOptions struct {
+	ListOptions
+	IIDs   []int  `url:"iids,omitempty" json:"iids,omitempty"`
+	State  string `url:"state,omitempty" json:"state,omitempty"`
+	Search string `url:"search,omitempty" json:"search,omitempty"`
+}
+
+// ListGroupMilestones returns a list of group milestones.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/group_milestones.html#list-group-milestones
+func (s *GroupMilestonesService) ListGroupMilestones(gid interface{}, opt *ListGroupMilestonesOptions, options ...OptionFunc) ([]*GroupMilestone, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/milestones", url.QueryEscape(group))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var m []*GroupMilestone
+	resp, err := s.client.Do(req, &m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// GetGroupMilestone gets a single group milestone.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/group_milestones.html#get-single-milestone
+func (s *GroupMilestonesService) GetGroupMilestone(gid interface{}, milestone int, options ...OptionFunc) (*GroupMilestone, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/milestones/%d", url.QueryEscape(group), milestone)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	m := new(GroupMilestone)
+	resp, err := s.client.Do(req, m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// CreateGroupMilestoneOptions represents the available CreateGroupMilestone() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/group_milestones.html#create-new-milestone
+type CreateGroupMilestoneOptions struct {
+	Title       *string  `url:"title,omitempty" json:"title,omitempty"`
+	Description *string  `url:"description,omitempty" json:"description,omitempty"`
+	StartDate   *ISOTime `url:"start_date,omitempty" json:"start_date,omitempty"`
+	DueDate     *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"`
+}
+
+// CreateGroupMilestone creates a new group milestone.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/group_milestones.html#create-new-milestone
+func (s *GroupMilestonesService) CreateGroupMilestone(gid interface{}, opt *CreateGroupMilestoneOptions, options ...OptionFunc) (*GroupMilestone, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/milestones", url.QueryEscape(group))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	m := new(GroupMilestone)
+	resp, err := s.client.Do(req, m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// UpdateGroupMilestoneOptions represents the available UpdateGroupMilestone() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/group_milestones.html#edit-milestone
+type UpdateGroupMilestoneOptions struct {
+	Title       *string  `url:"title,omitempty" json:"title,omitempty"`
+	Description *string  `url:"description,omitempty" json:"description,omitempty"`
+	StartDate   *ISOTime `url:"start_date,omitempty" json:"start_date,omitempty"`
+	DueDate     *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"`
+	StateEvent  *string  `url:"state_event,omitempty" json:"state_event,omitempty"`
+}
+
+// UpdateGroupMilestone updates an existing group milestone.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/group_milestones.html#edit-milestone
+func (s *GroupMilestonesService) UpdateGroupMilestone(gid interface{}, milestone int, opt *UpdateGroupMilestoneOptions, options ...OptionFunc) (*GroupMilestone, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/milestones/%d", url.QueryEscape(group), milestone)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	m := new(GroupMilestone)
+	resp, err := s.client.Do(req, m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// GetGroupMilestoneIssuesOptions represents the available GetGroupMilestoneIssues() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/group_milestones.html#get-all-issues-assigned-to-a-single-milestone
+type GetGroupMilestoneIssuesOptions ListOptions
+
+// GetGroupMilestoneIssues gets all issues assigned to a single group milestone.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/group_milestones.html#get-all-issues-assigned-to-a-single-milestone
+func (s *GroupMilestonesService) GetGroupMilestoneIssues(gid interface{}, milestone int, opt *GetGroupMilestoneIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/milestones/%d/issues", url.QueryEscape(group), milestone)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var i []*Issue
+	resp, err := s.client.Do(req, &i)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return i, resp, err
+}
+
+// GetGroupMilestoneMergeRequestsOptions represents the available
+// GetGroupMilestoneMergeRequests() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/group_milestones.html#get-all-merge-requests-assigned-to-a-single-milestone
+type GetGroupMilestoneMergeRequestsOptions ListOptions
+
+// GetGroupMilestoneMergeRequests gets all merge requests assigned to a
+// single group milestone.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/group_milestones.html#get-all-merge-requests-assigned-to-a-single-milestone
+func (s *GroupMilestonesService) GetGroupMilestoneMergeRequests(gid interface{}, milestone int, opt *GetGroupMilestoneMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/milestones/%d/merge_requests", url.QueryEscape(group), milestone)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var mr []*MergeRequest
+	resp, err := s.client.Do(req, &mr)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return mr, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/groups.go b/vendor/github.com/xanzy/go-gitlab/groups.go
new file mode 100644
index 0000000..d379915
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/groups.go
@@ -0,0 +1,307 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+)
+
+// GroupsService handles communication with the group related methods of
+// the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html
+type GroupsService struct {
+	client *Client
+}
+
+// Group represents a GitLab group.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html
+type Group struct {
+	ID                   int                `json:"id"`
+	Name                 string             `json:"name"`
+	Path                 string             `json:"path"`
+	Description          string             `json:"description"`
+	Visibility           *VisibilityValue   `json:"visibility"`
+	LFSEnabled           bool               `json:"lfs_enabled"`
+	AvatarURL            string             `json:"avatar_url"`
+	WebURL               string             `json:"web_url"`
+	RequestAccessEnabled bool               `json:"request_access_enabled"`
+	FullName             string             `json:"full_name"`
+	FullPath             string             `json:"full_path"`
+	ParentID             int                `json:"parent_id"`
+	Projects             []*Project         `json:"projects"`
+	Statistics           *StorageStatistics `json:"statistics"`
+}
+
+// ListGroupsOptions represents the available ListGroups() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#list-project-groups
+type ListGroupsOptions struct {
+	ListOptions
+	AllAvailable *bool   `url:"all_available,omitempty" json:"all_available,omitempty"`
+	OrderBy      *string `url:"order_by,omitempty" json:"order_by,omitempty"`
+	Owned        *bool   `url:"owned,omitempty" json:"owned,omitempty"`
+	Search       *string `url:"search,omitempty" json:"search,omitempty"`
+	Sort         *string `url:"sort,omitempty" json:"sort,omitempty"`
+	Statistics   *bool   `url:"statistics,omitempty" json:"statistics,omitempty"`
+}
+
+// ListGroups gets a list of groups (as user: my groups, as admin: all groups).
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/groups.html#list-project-groups
+func (s *GroupsService) ListGroups(opt *ListGroupsOptions, options ...OptionFunc) ([]*Group, *Response, error) {
+	req, err := s.client.NewRequest("GET", "groups", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var g []*Group
+	resp, err := s.client.Do(req, &g)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return g, resp, err
+}
+
+// GetGroup gets all details of a group.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#details-of-a-group
+func (s *GroupsService) GetGroup(gid interface{}, options ...OptionFunc) (*Group, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s", url.QueryEscape(group))
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	g := new(Group)
+	resp, err := s.client.Do(req, g)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return g, resp, err
+}
+
+// CreateGroupOptions represents the available CreateGroup() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#new-group
+type CreateGroupOptions struct {
+	Name                 *string          `url:"name,omitempty" json:"name,omitempty"`
+	Path                 *string          `url:"path,omitempty" json:"path,omitempty"`
+	Description          *string          `url:"description,omitempty" json:"description,omitempty"`
+	Visibility           *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
+	LFSEnabled           *bool            `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"`
+	RequestAccessEnabled *bool            `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"`
+	ParentID             *int             `url:"parent_id,omitempty" json:"parent_id,omitempty"`
+}
+
+// CreateGroup creates a new project group. Available only for users who can
+// create groups.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#new-group
+func (s *GroupsService) CreateGroup(opt *CreateGroupOptions, options ...OptionFunc) (*Group, *Response, error) {
+	req, err := s.client.NewRequest("POST", "groups", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	g := new(Group)
+	resp, err := s.client.Do(req, g)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return g, resp, err
+}
+
+// TransferGroup transfers a project to the Group namespace. Available only
+// for admin.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/groups.html#transfer-project-to-group
+func (s *GroupsService) TransferGroup(gid interface{}, pid interface{}, options ...OptionFunc) (*Group, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	u := fmt.Sprintf("groups/%s/projects/%s", url.QueryEscape(group),
+		url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	g := new(Group)
+	resp, err := s.client.Do(req, g)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return g, resp, err
+}
+
+// UpdateGroupOptions represents the set of available options to update a Group;
+// as of today these are exactly the same available when creating a new Group.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#update-group
+type UpdateGroupOptions CreateGroupOptions
+
+// UpdateGroup updates an existing group; only available to group owners and
+// administrators.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#update-group
+func (s *GroupsService) UpdateGroup(gid interface{}, opt *UpdateGroupOptions, options ...OptionFunc) (*Group, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s", url.QueryEscape(group))
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	g := new(Group)
+	resp, err := s.client.Do(req, g)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return g, resp, err
+}
+
+// DeleteGroup removes group with all projects inside.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#remove-group
+func (s *GroupsService) DeleteGroup(gid interface{}, options ...OptionFunc) (*Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("groups/%s", url.QueryEscape(group))
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// SearchGroup get all groups that match your string in their name or path.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#search-for-group
+func (s *GroupsService) SearchGroup(query string, options ...OptionFunc) ([]*Group, *Response, error) {
+	var q struct {
+		Search string `url:"search,omitempty" json:"search,omitempty"`
+	}
+	q.Search = query
+
+	req, err := s.client.NewRequest("GET", "groups", &q, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var g []*Group
+	resp, err := s.client.Do(req, &g)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return g, resp, err
+}
+
+// ListGroupProjectsOptions represents the available ListGroupProjects()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/groups.html#list-a-group-39-s-projects
+type ListGroupProjectsOptions ListProjectsOptions
+
+// ListGroupProjects get a list of group projects
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/groups.html#list-a-group-39-s-projects
+func (s *GroupsService) ListGroupProjects(gid interface{}, opt *ListGroupProjectsOptions, options ...OptionFunc) ([]*Project, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/projects", url.QueryEscape(group))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var p []*Project
+	resp, err := s.client.Do(req, &p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// ListSubgroupsOptions represents the available ListSubgroupsOptions()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/groups.html#list-a-groups-s-subgroups
+type ListSubgroupsOptions ListGroupsOptions
+
+// ListSubgroups gets a list of subgroups for a given project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/groups.html#list-a-groups-s-subgroups
+func (s *GroupsService) ListSubgroups(gid interface{}, opt *ListSubgroupsOptions, options ...OptionFunc) ([]*Group, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/subgroups", url.QueryEscape(group))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var g []*Group
+	resp, err := s.client.Do(req, &g)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return g, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/issue_links.go b/vendor/github.com/xanzy/go-gitlab/issue_links.go
new file mode 100644
index 0000000..5dfd76a
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/issue_links.go
@@ -0,0 +1,128 @@
+//
+// Copyright 2017, Arkbriar
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+)
+
+// IssueLinksService handles communication with the issue relations related methods
+// of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ee/api/issue_links.html
+type IssueLinksService struct {
+	client *Client
+}
+
+// IssueLink represents a two-way relation between two issues.
+//
+// GitLab API docs: https://docs.gitlab.com/ee/api/issue_links.html
+type IssueLink struct {
+	SourceIssue *Issue `json:"source_issue"`
+	TargetIssue *Issue `json:"target_issue"`
+}
+
+// ListIssueRelations gets a list of related issues of a given issue,
+// sorted by the relationship creation datetime (ascending).
+//
+// Issues will be filtered according to the user authorizations.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/issue_links.html#list-issue-relations
+func (s *IssueLinksService) ListIssueRelations(pid interface{}, issueIID int, options ...OptionFunc) ([]*Issue, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/issues/%d/links", url.QueryEscape(project), issueIID)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var is []*Issue
+	resp, err := s.client.Do(req, &is)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return is, resp, err
+}
+
+// CreateIssueLinkOptions represents the available CreateIssueLink() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ee/api/issue_links.html
+type CreateIssueLinkOptions struct {
+	TargetProjectID *string `json:"target_project_id"`
+	TargetIssueIID  *string `json:"target_issue_iid"`
+}
+
+// CreateIssueLink creates a two-way relation between two issues.
+// User must be allowed to update both issues in order to succeed.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/issue_links.html#create-an-issue-link
+func (s *IssueLinksService) CreateIssueLink(pid interface{}, issueIID int, opt *CreateIssueLinkOptions, options ...OptionFunc) (*IssueLink, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/issues/%d/links", url.QueryEscape(project), issueIID)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	i := new(IssueLink)
+	resp, err := s.client.Do(req, &i)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return i, resp, err
+}
+
+// DeleteIssueLink deletes an issue link, thus removes the two-way relationship.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/issue_links.html#delete-an-issue-link
+func (s *IssueLinksService) DeleteIssueLink(pid interface{}, issueIID, issueLinkID int, options ...OptionFunc) (*IssueLink, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/issues/%d/links/%d",
+		url.QueryEscape(project),
+		issueIID,
+		issueLinkID)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	i := new(IssueLink)
+	resp, err := s.client.Do(req, &i)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return i, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/issues.go b/vendor/github.com/xanzy/go-gitlab/issues.go
new file mode 100644
index 0000000..481721d
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/issues.go
@@ -0,0 +1,436 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/url"
+	"strings"
+	"time"
+)
+
+// IssuesService handles communication with the issue related methods
+// of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html
+type IssuesService struct {
+	client    *Client
+	timeStats *timeStatsService
+}
+
+// Issue represents a GitLab issue.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html
+type Issue struct {
+	ID        int        `json:"id"`
+	IID       int        `json:"iid"`
+	ProjectID int        `json:"project_id"`
+	Milestone *Milestone `json:"milestone"`
+	Author    struct {
+		ID        int        `json:"id"`
+		Username  string     `json:"username"`
+		Email     string     `json:"email"`
+		Name      string     `json:"name"`
+		State     string     `json:"state"`
+		CreatedAt *time.Time `json:"created_at"`
+	} `json:"author"`
+	Description string `json:"description"`
+	State       string `json:"state"`
+	Assignees   []struct {
+		ID        int        `json:"id"`
+		Username  string     `json:"username"`
+		Email     string     `json:"email"`
+		Name      string     `json:"name"`
+		State     string     `json:"state"`
+		CreatedAt *time.Time `json:"created_at"`
+	} `json:"assignees"`
+	Assignee struct {
+		ID        int    `json:"id"`
+		Name      string `json:"name"`
+		Username  string `json:"username"`
+		State     string `json:"state"`
+		AvatarURL string `json:"avatar_url"`
+		WebURL    string `json:"web_url"`
+	} `json:"assignee"`
+	Upvotes          int        `json:"upvotes"`
+	Downvotes        int        `json:"downvotes"`
+	Labels           []string   `json:"labels"`
+	Title            string     `json:"title"`
+	UpdatedAt        *time.Time `json:"updated_at"`
+	CreatedAt        *time.Time `json:"created_at"`
+	ClosedAt         *time.Time `json:"closed_at"`
+	Subscribed       bool       `json:"subscribed"`
+	UserNotesCount   int        `json:"user_notes_count"`
+	DueDate          *ISOTime   `json:"due_date"`
+	WebURL           string     `json:"web_url"`
+	TimeStats        *TimeStats `json:"time_stats"`
+	Confidential     bool       `json:"confidential"`
+	Weight           int        `json:"weight"`
+	DiscussionLocked bool       `json:"discussion_locked"`
+	Links            struct {
+		Self       string `json:"self"`
+		Notes      string `json:"notes"`
+		AwardEmoji string `json:"award_emoji"`
+		Project    string `json:"project"`
+	} `json:"_links"`
+	IssueLinkID int `json:"issue_link_id"`
+}
+
+func (i Issue) String() string {
+	return Stringify(i)
+}
+
+// Labels is a custom type with specific marshaling characteristics.
+type Labels []string
+
+// MarshalJSON implements the json.Marshaler interface.
+func (l *Labels) MarshalJSON() ([]byte, error) {
+	return json.Marshal(strings.Join(*l, ","))
+}
+
+// ListIssuesOptions represents the available ListIssues() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-issues
+type ListIssuesOptions struct {
+	ListOptions
+	State           *string    `url:"state,omitempty" json:"state,omitempty"`
+	Labels          Labels     `url:"labels,comma,omitempty" json:"labels,omitempty"`
+	Milestone       *string    `url:"milestone,omitempty" json:"milestone,omitempty"`
+	Scope           *string    `url:"scope,omitempty" json:"scope,omitempty"`
+	AuthorID        *int       `url:"author_id,omitempty" json:"author_id,omitempty"`
+	AssigneeID      *int       `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
+	MyReactionEmoji *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
+	IIDs            []int      `url:"iids[],omitempty" json:"iids,omitempty"`
+	OrderBy         *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
+	Sort            *string    `url:"sort,omitempty" json:"sort,omitempty"`
+	Search          *string    `url:"search,omitempty" json:"search,omitempty"`
+	CreatedAfter    *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
+	CreatedBefore   *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
+	UpdatedAfter    *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
+	UpdatedBefore   *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
+}
+
+// ListIssues gets all issues created by authenticated user. This function
+// takes pagination parameters page and per_page to restrict the list of issues.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-issues
+func (s *IssuesService) ListIssues(opt *ListIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
+	req, err := s.client.NewRequest("GET", "issues", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var i []*Issue
+	resp, err := s.client.Do(req, &i)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return i, resp, err
+}
+
+// ListGroupIssuesOptions represents the available ListGroupIssues() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-group-issues
+type ListGroupIssuesOptions struct {
+	ListOptions
+	State           *string    `url:"state,omitempty" json:"state,omitempty"`
+	Labels          Labels     `url:"labels,comma,omitempty" json:"labels,omitempty"`
+	IIDs            []int      `url:"iids[],omitempty" json:"iids,omitempty"`
+	Milestone       *string    `url:"milestone,omitempty" json:"milestone,omitempty"`
+	Scope           *string    `url:"scope,omitempty" json:"scope,omitempty"`
+	AuthorID        *int       `url:"author_id,omitempty" json:"author_id,omitempty"`
+	AssigneeID      *int       `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
+	MyReactionEmoji *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
+	OrderBy         *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
+	Sort            *string    `url:"sort,omitempty" json:"sort,omitempty"`
+	Search          *string    `url:"search,omitempty" json:"search,omitempty"`
+	CreatedAfter    *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
+	CreatedBefore   *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
+	UpdatedAfter    *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
+	UpdatedBefore   *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
+}
+
+// ListGroupIssues gets a list of group issues. This function accepts
+// pagination parameters page and per_page to return the list of group issues.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-group-issues
+func (s *IssuesService) ListGroupIssues(pid interface{}, opt *ListGroupIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
+	group, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/issues", url.QueryEscape(group))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var i []*Issue
+	resp, err := s.client.Do(req, &i)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return i, resp, err
+}
+
+// ListProjectIssuesOptions represents the available ListProjectIssues() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-project-issues
+type ListProjectIssuesOptions struct {
+	ListOptions
+	IIDs            []int      `url:"iids[],omitempty" json:"iids,omitempty"`
+	State           *string    `url:"state,omitempty" json:"state,omitempty"`
+	Labels          Labels     `url:"labels,comma,omitempty" json:"labels,omitempty"`
+	Milestone       *string    `url:"milestone,omitempty" json:"milestone,omitempty"`
+	Scope           *string    `url:"scope,omitempty" json:"scope,omitempty"`
+	AuthorID        *int       `url:"author_id,omitempty" json:"author_id,omitempty"`
+	AssigneeID      *int       `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
+	MyReactionEmoji *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
+	OrderBy         *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
+	Sort            *string    `url:"sort,omitempty" json:"sort,omitempty"`
+	Search          *string    `url:"search,omitempty" json:"search,omitempty"`
+	CreatedAfter    *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
+	CreatedBefore   *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
+	UpdatedAfter    *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
+	UpdatedBefore   *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
+}
+
+// ListProjectIssues gets a list of project issues. This function accepts
+// pagination parameters page and per_page to return the list of project issues.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-project-issues
+func (s *IssuesService) ListProjectIssues(pid interface{}, opt *ListProjectIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/issues", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var i []*Issue
+	resp, err := s.client.Do(req, &i)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return i, resp, err
+}
+
+// GetIssue gets a single project issue.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#single-issues
+func (s *IssuesService) GetIssue(pid interface{}, issue int, options ...OptionFunc) (*Issue, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/issues/%d", url.QueryEscape(project), issue)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	i := new(Issue)
+	resp, err := s.client.Do(req, i)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return i, resp, err
+}
+
+// CreateIssueOptions represents the available CreateIssue() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#new-issues
+type CreateIssueOptions struct {
+	Title                              *string    `url:"title,omitempty" json:"title,omitempty"`
+	Description                        *string    `url:"description,omitempty" json:"description,omitempty"`
+	Confidential                       *bool      `url:"confidential,omitempty" json:"confidential,omitempty"`
+	AssigneeIDs                        []int      `url:"assignee_ids,omitempty" json:"assignee_ids,omitempty"`
+	MilestoneID                        *int       `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
+	Labels                             Labels     `url:"labels,comma,omitempty" json:"labels,omitempty"`
+	CreatedAt                          *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`
+	DueDate                            *ISOTime   `url:"due_date,omitempty" json:"due_date,omitempty"`
+	MergeRequestToResolveDiscussionsOf *int       `url:"merge_request_to_resolve_discussions_of,omitempty" json:"merge_request_to_resolve_discussions_of,omitempty"`
+	DiscussionToResolve                *string    `url:"discussion_to_resolve,omitempty" json:"discussion_to_resolve,omitempty"`
+	Weight                             *int       `url:"weight,omitempty" json:"weight,omitempty"`
+}
+
+// CreateIssue creates a new project issue.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#new-issues
+func (s *IssuesService) CreateIssue(pid interface{}, opt *CreateIssueOptions, options ...OptionFunc) (*Issue, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/issues", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	i := new(Issue)
+	resp, err := s.client.Do(req, i)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return i, resp, err
+}
+
+// UpdateIssueOptions represents the available UpdateIssue() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#edit-issues
+type UpdateIssueOptions struct {
+	Title            *string    `url:"title,omitempty" json:"title,omitempty"`
+	Description      *string    `url:"description,omitempty" json:"description,omitempty"`
+	Confidential     *bool      `url:"confidential,omitempty" json:"confidential,omitempty"`
+	AssigneeIDs      []int      `url:"assignee_ids,omitempty" json:"assignee_ids,omitempty"`
+	MilestoneID      *int       `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
+	Labels           Labels     `url:"labels,comma,omitempty" json:"labels,omitempty"`
+	StateEvent       *string    `url:"state_event,omitempty" json:"state_event,omitempty"`
+	UpdatedAt        *time.Time `url:"updated_at,omitempty" json:"updated_at,omitempty"`
+	DueDate          *ISOTime   `url:"due_date,omitempty" json:"due_date,omitempty"`
+	DiscussionLocked *bool      `url:"discussion_locked,omitempty" json:"discussion_locked,omitempty"`
+}
+
+// UpdateIssue updates an existing project issue. This function is also used
+// to mark an issue as closed.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#edit-issues
+func (s *IssuesService) UpdateIssue(pid interface{}, issue int, opt *UpdateIssueOptions, options ...OptionFunc) (*Issue, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/issues/%d", url.QueryEscape(project), issue)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	i := new(Issue)
+	resp, err := s.client.Do(req, i)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return i, resp, err
+}
+
+// DeleteIssue deletes a single project issue.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#delete-an-issue
+func (s *IssuesService) DeleteIssue(pid interface{}, issue int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/issues/%d", url.QueryEscape(project), issue)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// ListMergeRequestsClosingIssueOptions represents the available
+// ListMergeRequestsClosingIssue() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/issues.html#list-merge-requests-that-will-close-issue-on-merge
+type ListMergeRequestsClosingIssueOptions ListOptions
+
+// ListMergeRequestsClosingIssue gets all the merge requests that will close
+// issue when merged.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/issues.html#list-merge-requests-that-will-close-issue-on-merge
+func (s *IssuesService) ListMergeRequestsClosingIssue(pid interface{}, issue int, opt *ListMergeRequestsClosingIssueOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("/projects/%s/issues/%d/closed_by", url.QueryEscape(project), issue)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var m []*MergeRequest
+	resp, err := s.client.Do(req, &m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// SetTimeEstimate sets the time estimate for a single project issue.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/issues.html#set-a-time-estimate-for-an-issue
+func (s *IssuesService) SetTimeEstimate(pid interface{}, issue int, opt *SetTimeEstimateOptions, options ...OptionFunc) (*TimeStats, *Response, error) {
+	return s.timeStats.setTimeEstimate(pid, "issues", issue, opt, options...)
+}
+
+// ResetTimeEstimate resets the time estimate for a single project issue.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/issues.html#reset-the-time-estimate-for-an-issue
+func (s *IssuesService) ResetTimeEstimate(pid interface{}, issue int, options ...OptionFunc) (*TimeStats, *Response, error) {
+	return s.timeStats.resetTimeEstimate(pid, "issues", issue, options...)
+}
+
+// AddSpentTime adds spent time for a single project issue.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/issues.html#add-spent-time-for-an-issue
+func (s *IssuesService) AddSpentTime(pid interface{}, issue int, opt *AddSpentTimeOptions, options ...OptionFunc) (*TimeStats, *Response, error) {
+	return s.timeStats.addSpentTime(pid, "issues", issue, opt, options...)
+}
+
+// ResetSpentTime resets the spent time for a single project issue.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/issues.html#reset-spent-time-for-an-issue
+func (s *IssuesService) ResetSpentTime(pid interface{}, issue int, options ...OptionFunc) (*TimeStats, *Response, error) {
+	return s.timeStats.resetSpentTime(pid, "issues", issue, options...)
+}
+
+// GetTimeSpent gets the spent time for a single project issue.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/issues.html#get-time-tracking-stats
+func (s *IssuesService) GetTimeSpent(pid interface{}, issue int, options ...OptionFunc) (*TimeStats, *Response, error) {
+	return s.timeStats.getTimeSpent(pid, "issues", issue, options...)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/jobs.go b/vendor/github.com/xanzy/go-gitlab/jobs.go
new file mode 100644
index 0000000..669f96d
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/jobs.go
@@ -0,0 +1,350 @@
+//
+// Copyright 2017, Arkbriar
+//
+// 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 gitlab
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"net/url"
+	"time"
+)
+
+// JobsService handles communication with the ci builds related methods
+// of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/jobs.html
+type JobsService struct {
+	client *Client
+}
+
+// Job represents a ci build.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/jobs.html
+type Job struct {
+	Commit        *Commit    `json:"commit"`
+	CreatedAt     *time.Time `json:"created_at"`
+	Coverage      float64    `json:"coverage"`
+	ArtifactsFile struct {
+		Filename string `json:"filename"`
+		Size     int    `json:"size"`
+	} `json:"artifacts_file"`
+	FinishedAt *time.Time `json:"finished_at"`
+	ID         int        `json:"id"`
+	Name       string     `json:"name"`
+	Ref        string     `json:"ref"`
+	Runner     struct {
+		ID          int    `json:"id"`
+		Description string `json:"description"`
+		Active      bool   `json:"active"`
+		IsShared    bool   `json:"is_shared"`
+		Name        string `json:"name"`
+	} `json:"runner"`
+	Stage     string     `json:"stage"`
+	StartedAt *time.Time `json:"started_at"`
+	Status    string     `json:"status"`
+	Tag       bool       `json:"tag"`
+	User      *User      `json:"user"`
+}
+
+// ListJobsOptions are options for two list apis
+type ListJobsOptions struct {
+	ListOptions
+	Scope []BuildStateValue `url:"scope,omitempty" json:"scope,omitempty"`
+}
+
+// ListProjectJobs gets a list of jobs in a project.
+//
+// The scope of jobs to show, one or array of: created, pending, running,
+// failed, success, canceled, skipped; showing all jobs if none provided
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/jobs.html#list-project-jobs
+func (s *JobsService) ListProjectJobs(pid interface{}, opts *ListJobsOptions, options ...OptionFunc) ([]Job, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/jobs", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opts, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var jobs []Job
+	resp, err := s.client.Do(req, &jobs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return jobs, resp, err
+}
+
+// ListPipelineJobs gets a list of jobs for specific pipeline in a
+// project. If the pipeline ID is not found, it will respond with 404.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/jobs.html#list-pipeline-jobs
+func (s *JobsService) ListPipelineJobs(pid interface{}, pipelineID int, opts *ListJobsOptions, options ...OptionFunc) ([]*Job, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pipelines/%d/jobs", url.QueryEscape(project), pipelineID)
+
+	req, err := s.client.NewRequest("GET", u, opts, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var jobs []*Job
+	resp, err := s.client.Do(req, &jobs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return jobs, resp, err
+}
+
+// GetJob gets a single job of a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/jobs.html#get-a-single-job
+func (s *JobsService) GetJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/jobs/%d", url.QueryEscape(project), jobID)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	job := new(Job)
+	resp, err := s.client.Do(req, job)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return job, resp, err
+}
+
+// GetJobArtifacts get jobs artifacts of a project
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/jobs.html#get-job-artifacts
+func (s *JobsService) GetJobArtifacts(pid interface{}, jobID int, options ...OptionFunc) (io.Reader, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/jobs/%d/artifacts", url.QueryEscape(project), jobID)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	artifactsBuf := new(bytes.Buffer)
+	resp, err := s.client.Do(req, artifactsBuf)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return artifactsBuf, resp, err
+}
+
+// DownloadArtifactsFile download the artifacts file from the given
+// reference name and job provided the job finished successfully.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/jobs.html#download-the-artifacts-file
+func (s *JobsService) DownloadArtifactsFile(pid interface{}, refName string, job string, options ...OptionFunc) (io.Reader, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/jobs/artifacts/%s/download?job=%s", url.QueryEscape(project), refName, job)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	artifactsBuf := new(bytes.Buffer)
+	resp, err := s.client.Do(req, artifactsBuf)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return artifactsBuf, resp, err
+}
+
+// GetTraceFile gets a trace of a specific job of a project
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/jobs.html#get-a-trace-file
+func (s *JobsService) GetTraceFile(pid interface{}, jobID int, options ...OptionFunc) (io.Reader, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/jobs/%d/trace", url.QueryEscape(project), jobID)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	traceBuf := new(bytes.Buffer)
+	resp, err := s.client.Do(req, traceBuf)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return traceBuf, resp, err
+}
+
+// CancelJob cancels a single job of a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/jobs.html#cancel-a-job
+func (s *JobsService) CancelJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/jobs/%d/cancel", url.QueryEscape(project), jobID)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	job := new(Job)
+	resp, err := s.client.Do(req, job)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return job, resp, err
+}
+
+// RetryJob retries a single job of a project
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/jobs.html#retry-a-job
+func (s *JobsService) RetryJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/jobs/%d/retry", url.QueryEscape(project), jobID)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	job := new(Job)
+	resp, err := s.client.Do(req, job)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return job, resp, err
+}
+
+// EraseJob erases a single job of a project, removes a job
+// artifacts and a job trace.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/jobs.html#erase-a-job
+func (s *JobsService) EraseJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/jobs/%d/erase", url.QueryEscape(project), jobID)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	job := new(Job)
+	resp, err := s.client.Do(req, job)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return job, resp, err
+}
+
+// KeepArtifacts prevents artifacts from being deleted when
+// expiration is set.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/jobs.html#keep-artifacts
+func (s *JobsService) KeepArtifacts(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/jobs/%d/artifacts/keep", url.QueryEscape(project), jobID)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	job := new(Job)
+	resp, err := s.client.Do(req, job)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return job, resp, err
+}
+
+// PlayJob triggers a manual action to start a job.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/jobs.html#play-a-job
+func (s *JobsService) PlayJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/jobs/%d/play", url.QueryEscape(project), jobID)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	job := new(Job)
+	resp, err := s.client.Do(req, job)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return job, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/labels.go b/vendor/github.com/xanzy/go-gitlab/labels.go
new file mode 100644
index 0000000..2f8307d
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/labels.go
@@ -0,0 +1,228 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+)
+
+// LabelsService handles communication with the label related methods of the
+// GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html
+type LabelsService struct {
+	client *Client
+}
+
+// Label represents a GitLab label.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html
+type Label struct {
+	ID                     int    `json:"id"`
+	Name                   string `json:"name"`
+	Color                  string `json:"color"`
+	Description            string `json:"description"`
+	OpenIssuesCount        int    `json:"open_issues_count"`
+	ClosedIssuesCount      int    `json:"closed_issues_count"`
+	OpenMergeRequestsCount int    `json:"open_merge_requests_count"`
+	Subscribed             bool   `json:"subscribed"`
+	Priority               int    `json:"priority"`
+}
+
+func (l Label) String() string {
+	return Stringify(l)
+}
+
+// ListLabelsOptions represents the available ListLabels() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#list-labels
+type ListLabelsOptions ListOptions
+
+// ListLabels gets all labels for given project.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#list-labels
+func (s *LabelsService) ListLabels(pid interface{}, opt *ListLabelsOptions, options ...OptionFunc) ([]*Label, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/labels", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var l []*Label
+	resp, err := s.client.Do(req, &l)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return l, resp, err
+}
+
+// CreateLabelOptions represents the available CreateLabel() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#create-a-new-label
+type CreateLabelOptions struct {
+	Name        *string `url:"name,omitempty" json:"name,omitempty"`
+	Color       *string `url:"color,omitempty" json:"color,omitempty"`
+	Description *string `url:"description,omitempty" json:"description,omitempty"`
+}
+
+// CreateLabel creates a new label for given repository with given name and
+// color.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#create-a-new-label
+func (s *LabelsService) CreateLabel(pid interface{}, opt *CreateLabelOptions, options ...OptionFunc) (*Label, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/labels", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	l := new(Label)
+	resp, err := s.client.Do(req, l)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return l, resp, err
+}
+
+// DeleteLabelOptions represents the available DeleteLabel() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#delete-a-label
+type DeleteLabelOptions struct {
+	Name *string `url:"name,omitempty" json:"name,omitempty"`
+}
+
+// DeleteLabel deletes a label given by its name.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#delete-a-label
+func (s *LabelsService) DeleteLabel(pid interface{}, opt *DeleteLabelOptions, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/labels", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("DELETE", u, opt, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// UpdateLabelOptions represents the available UpdateLabel() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#delete-a-label
+type UpdateLabelOptions struct {
+	Name        *string `url:"name,omitempty" json:"name,omitempty"`
+	NewName     *string `url:"new_name,omitempty" json:"new_name,omitempty"`
+	Color       *string `url:"color,omitempty" json:"color,omitempty"`
+	Description *string `url:"description,omitempty" json:"description,omitempty"`
+}
+
+// UpdateLabel updates an existing label with new name or now color. At least
+// one parameter is required, to update the label.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#edit-an-existing-label
+func (s *LabelsService) UpdateLabel(pid interface{}, opt *UpdateLabelOptions, options ...OptionFunc) (*Label, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/labels", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	l := new(Label)
+	resp, err := s.client.Do(req, l)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return l, resp, err
+}
+
+// SubscribeToLabel subscribes the authenticated user to a label to receive
+// notifications. If the user is already subscribed to the label, the status
+// code 304 is returned.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/labels.html#subscribe-to-a-label
+func (s *LabelsService) SubscribeToLabel(pid interface{}, labelID interface{}, options ...OptionFunc) (*Label, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	label, err := parseID(labelID)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/labels/%s/subscribe", url.QueryEscape(project), label)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	l := new(Label)
+	resp, err := s.client.Do(req, l)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return l, resp, err
+}
+
+// UnsubscribeFromLabel unsubscribes the authenticated user from a label to not
+// receive notifications from it. If the user is not subscribed to the label, the
+// status code 304 is returned.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/labels.html#unsubscribe-from-a-label
+func (s *LabelsService) UnsubscribeFromLabel(pid interface{}, labelID interface{}, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	label, err := parseID(labelID)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/labels/%s/unsubscribe", url.QueryEscape(project), label)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/merge_request_approvals.go b/vendor/github.com/xanzy/go-gitlab/merge_request_approvals.go
new file mode 100644
index 0000000..e03cb61
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/merge_request_approvals.go
@@ -0,0 +1,125 @@
+package gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// MergeRequestApprovalsService handles communication with the merge request
+// approvals related methods of the GitLab API. This includes reading/updating
+// approval settings and approve/unapproving merge requests
+//
+// GitLab API docs: https://docs.gitlab.com/ee/api/merge_request_approvals.html
+type MergeRequestApprovalsService struct {
+	client *Client
+}
+
+// MergeRequestApprovals represents GitLab merge request approvals.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/merge_request_approvals.html#merge-request-level-mr-approvals
+type MergeRequestApprovals struct {
+	ID                int        `json:"id"`
+	ProjectID         int        `json:"project_id"`
+	Title             string     `json:"title"`
+	Description       string     `json:"description"`
+	State             string     `json:"state"`
+	CreatedAt         *time.Time `json:"created_at"`
+	UpdatedAt         *time.Time `json:"updated_at"`
+	MergeStatus       string     `json:"merge_status"`
+	ApprovalsRequired int        `json:"approvals_required"`
+	ApprovalsLeft     int        `json:"approvals_left"`
+	ApprovedBy        []struct {
+		User struct {
+			ID        int    `json:"id"`
+			Name      string `json:"name"`
+			Username  string `json:"username"`
+			State     string `json:"state"`
+			AvatarURL string `json:"avatar_url"`
+			WebURL    string `json:"web_url"`
+		} `json:"user"`
+	} `json:"approved_by"`
+	Approvers []struct {
+		User struct {
+			ID        int    `json:"id"`
+			Name      string `json:"name"`
+			Username  string `json:"username"`
+			State     string `json:"state"`
+			AvatarURL string `json:"avatar_url"`
+			WebURL    string `json:"web_url"`
+		} `json:"user"`
+	} `json:"approvers"`
+	ApproverGroups []struct {
+		Group struct {
+			ID                   int    `json:"id"`
+			Name                 string `json:"name"`
+			Path                 string `json:"path"`
+			Description          string `json:"description"`
+			Visibility           string `json:"visibility"`
+			AvatarURL            string `json:"avatar_url"`
+			WebURL               string `json:"web_url"`
+			FullName             string `json:"full_name"`
+			FullPath             string `json:"full_path"`
+			LFSEnabled           bool   `json:"lfs_enabled"`
+			RequestAccessEnabled bool   `json:"request_access_enabled"`
+		} `json:"group"`
+	} `json:"approver_group"`
+}
+
+func (m MergeRequestApprovals) String() string {
+	return Stringify(m)
+}
+
+// ApproveMergeRequestOptions represents the available ApproveMergeRequest() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/merge_request_approvals.html#approve-merge-request
+type ApproveMergeRequestOptions struct {
+	Sha *string `url:"sha,omitempty" json:"sha,omitempty"`
+}
+
+// ApproveMergeRequest approves a merge request on GitLab. If a non-empty sha
+// is provided then it must match the sha at the HEAD of the MR.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/merge_request_approvals.html#approve-merge-request
+func (s *MergeRequestApprovalsService) ApproveMergeRequest(pid interface{}, mr int, opt *ApproveMergeRequestOptions, options ...OptionFunc) (*MergeRequestApprovals, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/approve", url.QueryEscape(project), mr)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	m := new(MergeRequestApprovals)
+	resp, err := s.client.Do(req, m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// UnapproveMergeRequest unapproves a previously approved merge request on GitLab.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/merge_request_approvals.html#unapprove-merge-request
+func (s *MergeRequestApprovalsService) UnapproveMergeRequest(pid interface{}, mr int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/unapprove", url.QueryEscape(project), mr)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/merge_requests.go b/vendor/github.com/xanzy/go-gitlab/merge_requests.go
new file mode 100644
index 0000000..daddd62
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/merge_requests.go
@@ -0,0 +1,743 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// MergeRequestsService handles communication with the merge requests related
+// methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/merge_requests.html
+type MergeRequestsService struct {
+	client    *Client
+	timeStats *timeStatsService
+}
+
+// MergeRequest represents a GitLab merge request.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/merge_requests.html
+type MergeRequest struct {
+	ID           int        `json:"id"`
+	IID          int        `json:"iid"`
+	TargetBranch string     `json:"target_branch"`
+	SourceBranch string     `json:"source_branch"`
+	ProjectID    int        `json:"project_id"`
+	Title        string     `json:"title"`
+	State        string     `json:"state"`
+	CreatedAt    *time.Time `json:"created_at"`
+	UpdatedAt    *time.Time `json:"updated_at"`
+	Upvotes      int        `json:"upvotes"`
+	Downvotes    int        `json:"downvotes"`
+	Author       struct {
+		ID        int        `json:"id"`
+		Username  string     `json:"username"`
+		Name      string     `json:"name"`
+		State     string     `json:"state"`
+		CreatedAt *time.Time `json:"created_at"`
+	} `json:"author"`
+	Assignee struct {
+		ID        int        `json:"id"`
+		Username  string     `json:"username"`
+		Name      string     `json:"name"`
+		State     string     `json:"state"`
+		CreatedAt *time.Time `json:"created_at"`
+	} `json:"assignee"`
+	SourceProjectID           int        `json:"source_project_id"`
+	TargetProjectID           int        `json:"target_project_id"`
+	Labels                    []string   `json:"labels"`
+	Description               string     `json:"description"`
+	WorkInProgress            bool       `json:"work_in_progress"`
+	Milestone                 *Milestone `json:"milestone"`
+	MergeWhenPipelineSucceeds bool       `json:"merge_when_pipeline_succeeds"`
+	MergeStatus               string     `json:"merge_status"`
+	MergedBy                  struct {
+		ID        int        `json:"id"`
+		Username  string     `json:"username"`
+		Name      string     `json:"name"`
+		State     string     `json:"state"`
+		CreatedAt *time.Time `json:"created_at"`
+	} `json:"merged_by"`
+	MergedAt *time.Time `json:"merged_at"`
+	ClosedBy struct {
+		ID        int        `json:"id"`
+		Username  string     `json:"username"`
+		Name      string     `json:"name"`
+		State     string     `json:"state"`
+		CreatedAt *time.Time `json:"created_at"`
+	} `json:"closed_by"`
+	ClosedAt                 *time.Time `json:"closed_at"`
+	Subscribed               bool       `json:"subscribed"`
+	SHA                      string     `json:"sha"`
+	MergeCommitSHA           string     `json:"merge_commit_sha"`
+	UserNotesCount           int        `json:"user_notes_count"`
+	ChangesCount             string     `json:"changes_count"`
+	ShouldRemoveSourceBranch bool       `json:"should_remove_source_branch"`
+	ForceRemoveSourceBranch  bool       `json:"force_remove_source_branch"`
+	WebURL                   string     `json:"web_url"`
+	DiscussionLocked         bool       `json:"discussion_locked"`
+	Changes                  []struct {
+		OldPath     string `json:"old_path"`
+		NewPath     string `json:"new_path"`
+		AMode       string `json:"a_mode"`
+		BMode       string `json:"b_mode"`
+		Diff        string `json:"diff"`
+		NewFile     bool   `json:"new_file"`
+		RenamedFile bool   `json:"renamed_file"`
+		DeletedFile bool   `json:"deleted_file"`
+	} `json:"changes"`
+	TimeStats *TimeStats `json:"time_stats"`
+	Squash    bool       `json:"squash"`
+	Pipeline  struct {
+		ID     int    `json:"id"`
+		Ref    string `json:"ref"`
+		SHA    string `json:"sha"`
+		Status string `json:"status"`
+	} `json:"pipeline"`
+}
+
+func (m MergeRequest) String() string {
+	return Stringify(m)
+}
+
+// MergeRequestDiffVersion represents Gitlab merge request version.
+//
+// Gitlab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#get-a-single-mr-diff-version
+type MergeRequestDiffVersion struct {
+	ID             int        `json:"id"`
+	HeadCommitSHA  string     `json:"head_commit_sha,omitempty"`
+	BaseCommitSHA  string     `json:"base_commit_sha,omitempty"`
+	StartCommitSHA string     `json:"start_commit_sha,omitempty"`
+	CreatedAt      *time.Time `json:"created_at,omitempty"`
+	MergeRequestID int        `json:"merge_request_id,omitempty"`
+	State          string     `json:"state,omitempty"`
+	RealSize       string     `json:"real_size,omitempty"`
+	Commits        []*Commit  `json:"commits,omitempty"`
+	Diffs          []*Diff    `json:"diffs,omitempty"`
+}
+
+func (m MergeRequestDiffVersion) String() string {
+	return Stringify(m)
+}
+
+// ListMergeRequestsOptions represents the available ListMergeRequests()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests
+type ListMergeRequestsOptions struct {
+	ListOptions
+	State           *string    `url:"state,omitempty" json:"state,omitempty"`
+	OrderBy         *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
+	Sort            *string    `url:"sort,omitempty" json:"sort,omitempty"`
+	Milestone       *string    `url:"milestone,omitempty" json:"milestone,omitempty"`
+	View            *string    `url:"view,omitempty" json:"view,omitempty"`
+	Labels          Labels     `url:"labels,omitempty" json:"labels,omitempty"`
+	CreatedAfter    *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
+	CreatedBefore   *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
+	Scope           *string    `url:"scope,omitempty" json:"scope,omitempty"`
+	AuthorID        *int       `url:"author_id,omitempty" json:"author_id,omitempty"`
+	AssigneeID      *int       `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
+	MyReactionEmoji *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
+}
+
+// ListMergeRequests gets all merge requests. The state parameter can be used
+// to get only merge requests with a given state (opened, closed, or merged)
+// or all of them (all). The pagination parameters page and per_page can be
+// used to restrict the list of merge requests.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests
+func (s *MergeRequestsService) ListMergeRequests(opt *ListMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
+	req, err := s.client.NewRequest("GET", "merge_requests", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var m []*MergeRequest
+	resp, err := s.client.Do(req, &m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// ListProjectMergeRequestsOptions represents the available ListMergeRequests()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#list-project-merge-requests
+type ListProjectMergeRequestsOptions struct {
+	ListOptions
+	IIDs            []int      `url:"iids[],omitempty" json:"iids,omitempty"`
+	State           *string    `url:"state,omitempty" json:"state,omitempty"`
+	OrderBy         *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
+	Sort            *string    `url:"sort,omitempty" json:"sort,omitempty"`
+	Milestone       *string    `url:"milestone,omitempty" json:"milestone,omitempty"`
+	View            *string    `url:"view,omitempty" json:"view,omitempty"`
+	Labels          Labels     `url:"labels,omitempty" json:"labels,omitempty"`
+	CreatedAfter    *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
+	CreatedBefore   *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
+	Scope           *string    `url:"scope,omitempty" json:"scope,omitempty"`
+	AuthorID        *int       `url:"author_id,omitempty" json:"author_id,omitempty"`
+	AssigneeID      *int       `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
+	MyReactionEmoji *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
+}
+
+// ListProjectMergeRequests gets all merge requests for this project. The state
+// parameter can be used to get only merge requests with a given state (opened,
+// closed, or merged) or all of them (all). The pagination parameters page and
+// per_page can be used to restrict the list of merge requests.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests
+func (s *MergeRequestsService) ListProjectMergeRequests(pid interface{}, opt *ListProjectMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var m []*MergeRequest
+	resp, err := s.client.Do(req, &m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// GetMergeRequest shows information about a single merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr
+func (s *MergeRequestsService) GetMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d", url.QueryEscape(project), mergeRequest)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	m := new(MergeRequest)
+	resp, err := s.client.Do(req, m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// GetMergeRequestApprovals gets information about a merge requests approvals
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/merge_request_approvals.html#merge-request-level-mr-approvals
+func (s *MergeRequestsService) GetMergeRequestApprovals(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequestApprovals, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/approvals", url.QueryEscape(project), mergeRequest)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	a := new(MergeRequestApprovals)
+	resp, err := s.client.Do(req, a)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return a, resp, err
+}
+
+// GetMergeRequestCommitsOptions represents the available GetMergeRequestCommits()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr-commits
+type GetMergeRequestCommitsOptions ListOptions
+
+// GetMergeRequestCommits gets a list of merge request commits.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr-commits
+func (s *MergeRequestsService) GetMergeRequestCommits(pid interface{}, mergeRequest int, opt *GetMergeRequestCommitsOptions, options ...OptionFunc) ([]*Commit, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/commits", url.QueryEscape(project), mergeRequest)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var c []*Commit
+	resp, err := s.client.Do(req, &c)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return c, resp, err
+}
+
+// GetMergeRequestChanges shows information about the merge request including
+// its files and changes.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr-changes
+func (s *MergeRequestsService) GetMergeRequestChanges(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/changes", url.QueryEscape(project), mergeRequest)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	m := new(MergeRequest)
+	resp, err := s.client.Do(req, m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// ListMergeRequestPipelines gets all pipelines for the provided merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#list-mr-pipelines
+func (s *MergeRequestsService) ListMergeRequestPipelines(pid interface{}, mergeRequest int, options ...OptionFunc) (PipelineList, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/pipelines", url.QueryEscape(project), mergeRequest)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var p PipelineList
+	resp, err := s.client.Do(req, &p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// GetIssuesClosedOnMergeOptions represents the available GetIssuesClosedOnMerge()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#list-issues-that-will-close-on-merge
+type GetIssuesClosedOnMergeOptions ListOptions
+
+// GetIssuesClosedOnMerge gets all the issues that would be closed by merging the
+// provided merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#list-issues-that-will-close-on-merge
+func (s *MergeRequestsService) GetIssuesClosedOnMerge(pid interface{}, mergeRequest int, opt *GetIssuesClosedOnMergeOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("/projects/%s/merge_requests/%d/closes_issues", url.QueryEscape(project), mergeRequest)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var i []*Issue
+	resp, err := s.client.Do(req, &i)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return i, resp, err
+}
+
+// CreateMergeRequestOptions represents the available CreateMergeRequest()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#create-mr
+type CreateMergeRequestOptions struct {
+	Title           *string `url:"title,omitempty" json:"title,omitempty"`
+	Description     *string `url:"description,omitempty" json:"description,omitempty"`
+	SourceBranch    *string `url:"source_branch,omitempty" json:"source_branch,omitempty"`
+	TargetBranch    *string `url:"target_branch,omitempty" json:"target_branch,omitempty"`
+	AssigneeID      *int    `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
+	TargetProjectID *int    `url:"target_project_id,omitempty" json:"target_project_id,omitempty"`
+}
+
+// CreateMergeRequest creates a new merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#create-mr
+func (s *MergeRequestsService) CreateMergeRequest(pid interface{}, opt *CreateMergeRequestOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	m := new(MergeRequest)
+	resp, err := s.client.Do(req, m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// UpdateMergeRequestOptions represents the available UpdateMergeRequest()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#update-mr
+type UpdateMergeRequestOptions struct {
+	Title        *string `url:"title,omitempty" json:"title,omitempty"`
+	Description  *string `url:"description,omitempty" json:"description,omitempty"`
+	TargetBranch *string `url:"target_branch,omitempty" json:"target_branch,omitempty"`
+	AssigneeID   *int    `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
+	Labels       Labels  `url:"labels,comma,omitempty" json:"labels,omitempty"`
+	MilestoneID  *int    `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
+	StateEvent   *string `url:"state_event,omitempty" json:"state_event,omitempty"`
+}
+
+// UpdateMergeRequest updates an existing project milestone.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#update-mr
+func (s *MergeRequestsService) UpdateMergeRequest(pid interface{}, mergeRequest int, opt *UpdateMergeRequestOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d", url.QueryEscape(project), mergeRequest)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	m := new(MergeRequest)
+	resp, err := s.client.Do(req, m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// DeleteMergeRequest deletes a merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#delete-a-merge-request
+func (s *MergeRequestsService) DeleteMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d", url.QueryEscape(project), mergeRequest)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// AcceptMergeRequestOptions represents the available AcceptMergeRequest()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#accept-mr
+type AcceptMergeRequestOptions struct {
+	MergeCommitMessage        *string `url:"merge_commit_message,omitempty" json:"merge_commit_message,omitempty"`
+	ShouldRemoveSourceBranch  *bool   `url:"should_remove_source_branch,omitempty" json:"should_remove_source_branch,omitempty"`
+	MergeWhenPipelineSucceeds *bool   `url:"merge_when_pipeline_succeeds,omitempty" json:"merge_when_pipeline_succeeds,omitempty"`
+	Sha                       *string `url:"sha,omitempty" json:"sha,omitempty"`
+}
+
+// AcceptMergeRequest merges changes submitted with MR using this API. If merge
+// success you get 200 OK. If it has some conflicts and can not be merged - you
+// get 405 and error message 'Branch cannot be merged'. If merge request is
+// already merged or closed - you get 405 and error message 'Method Not Allowed'
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#accept-mr
+func (s *MergeRequestsService) AcceptMergeRequest(pid interface{}, mergeRequest int, opt *AcceptMergeRequestOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/merge", url.QueryEscape(project), mergeRequest)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	m := new(MergeRequest)
+	resp, err := s.client.Do(req, m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// CancelMergeWhenPipelineSucceeds cancels a merge when pipeline succeeds. If
+// you don't have permissions to accept this merge request - you'll get a 401.
+// If the merge request is already merged or closed - you get 405 and error
+// message 'Method Not Allowed'. In case the merge request is not set to be
+// merged when the pipeline succeeds, you'll also get a 406 error.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#cancel-merge-when-pipeline-succeeds
+func (s *MergeRequestsService) CancelMergeWhenPipelineSucceeds(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/cancel_merge_when_pipeline_succeeds", url.QueryEscape(project), mergeRequest)
+
+	req, err := s.client.NewRequest("PUT", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	m := new(MergeRequest)
+	resp, err := s.client.Do(req, m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// GetMergeRequestDiffVersionsOptions represents the available
+// GetMergeRequestDiffVersions() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#get-mr-diff-versions
+type GetMergeRequestDiffVersionsOptions ListOptions
+
+// GetMergeRequestDiffVersions get a list of merge request diff versions.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#get-mr-diff-versions
+func (s *MergeRequestsService) GetMergeRequestDiffVersions(pid interface{}, mergeRequest int, opt *GetMergeRequestDiffVersionsOptions, options ...OptionFunc) ([]*MergeRequestDiffVersion, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/versions", url.QueryEscape(project), mergeRequest)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var v []*MergeRequestDiffVersion
+	resp, err := s.client.Do(req, &v)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return v, resp, err
+}
+
+// GetSingleMergeRequestDiffVersion get a single MR diff version
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#get-a-single-mr-diff-version
+func (s *MergeRequestsService) GetSingleMergeRequestDiffVersion(pid interface{}, mergeRequest, version int, options ...OptionFunc) (*MergeRequestDiffVersion, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/versions/%d", url.QueryEscape(project), mergeRequest, version)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var v = new(MergeRequestDiffVersion)
+	resp, err := s.client.Do(req, v)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return v, resp, err
+}
+
+// SubscribeToMergeRequest subscribes the authenticated user to the given merge request
+// to receive notifications. If the user is already subscribed to the
+// merge request, the status code 304 is returned.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#subscribe-to-a-merge-request
+func (s *MergeRequestsService) SubscribeToMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/subscribe", url.QueryEscape(project), mergeRequest)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	m := new(MergeRequest)
+	resp, err := s.client.Do(req, m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// UnsubscribeFromMergeRequest unsubscribes the authenticated user from the given merge request
+// to not receive notifications from that merge request. If the user is
+// not subscribed to the merge request, status code 304 is returned.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#unsubscribe-from-a-merge-request
+func (s *MergeRequestsService) UnsubscribeFromMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/unsubscribe", url.QueryEscape(project), mergeRequest)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	m := new(MergeRequest)
+	resp, err := s.client.Do(req, m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// CreateTodo manually creates a todo for the current user on a merge request.
+// If there already exists a todo for the user on that merge request,
+// status code 304 is returned.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#create-a-todo
+func (s *MergeRequestsService) CreateTodo(pid interface{}, mergeRequest int, options ...OptionFunc) (*Todo, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/todo", url.QueryEscape(project), mergeRequest)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	t := new(Todo)
+	resp, err := s.client.Do(req, t)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return t, resp, err
+}
+
+// SetTimeEstimate sets the time estimate for a single project merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#set-a-time-estimate-for-a-merge-request
+func (s *MergeRequestsService) SetTimeEstimate(pid interface{}, mergeRequest int, opt *SetTimeEstimateOptions, options ...OptionFunc) (*TimeStats, *Response, error) {
+	return s.timeStats.setTimeEstimate(pid, "merge_requests", mergeRequest, opt, options...)
+}
+
+// ResetTimeEstimate resets the time estimate for a single project merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#reset-the-time-estimate-for-a-merge-request
+func (s *MergeRequestsService) ResetTimeEstimate(pid interface{}, mergeRequest int, options ...OptionFunc) (*TimeStats, *Response, error) {
+	return s.timeStats.resetTimeEstimate(pid, "merge_requests", mergeRequest, options...)
+}
+
+// AddSpentTime adds spent time for a single project merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#add-spent-time-for-a-merge-request
+func (s *MergeRequestsService) AddSpentTime(pid interface{}, mergeRequest int, opt *AddSpentTimeOptions, options ...OptionFunc) (*TimeStats, *Response, error) {
+	return s.timeStats.addSpentTime(pid, "merge_requests", mergeRequest, opt, options...)
+}
+
+// ResetSpentTime resets the spent time for a single project merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#reset-spent-time-for-a-merge-request
+func (s *MergeRequestsService) ResetSpentTime(pid interface{}, mergeRequest int, options ...OptionFunc) (*TimeStats, *Response, error) {
+	return s.timeStats.resetSpentTime(pid, "merge_requests", mergeRequest, options...)
+}
+
+// GetTimeSpent gets the spent time for a single project merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/merge_requests.html#get-time-tracking-stats
+func (s *MergeRequestsService) GetTimeSpent(pid interface{}, mergeRequest int, options ...OptionFunc) (*TimeStats, *Response, error) {
+	return s.timeStats.getTimeSpent(pid, "merge_requests", mergeRequest, options...)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/milestones.go b/vendor/github.com/xanzy/go-gitlab/milestones.go
new file mode 100644
index 0000000..c1a69f9
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/milestones.go
@@ -0,0 +1,267 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// MilestonesService handles communication with the milestone related methods
+// of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/milestones.html
+type MilestonesService struct {
+	client *Client
+}
+
+// Milestone represents a GitLab milestone.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/milestones.html
+type Milestone struct {
+	ID          int        `json:"id"`
+	IID         int        `json:"iid"`
+	ProjectID   int        `json:"project_id"`
+	Title       string     `json:"title"`
+	Description string     `json:"description"`
+	StartDate   *ISOTime   `json:"start_date"`
+	DueDate     *ISOTime   `json:"due_date"`
+	State       string     `json:"state"`
+	UpdatedAt   *time.Time `json:"updated_at"`
+	CreatedAt   *time.Time `json:"created_at"`
+}
+
+func (m Milestone) String() string {
+	return Stringify(m)
+}
+
+// ListMilestonesOptions represents the available ListMilestones() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/milestones.html#list-project-milestones
+type ListMilestonesOptions struct {
+	ListOptions
+	IIDs   []int  `url:"iids,omitempty" json:"iids,omitempty"`
+	State  string `url:"state,omitempty" json:"state,omitempty"`
+	Search string `url:"search,omitempty" json:"search,omitempty"`
+}
+
+// ListMilestones returns a list of project milestones.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/milestones.html#list-project-milestones
+func (s *MilestonesService) ListMilestones(pid interface{}, opt *ListMilestonesOptions, options ...OptionFunc) ([]*Milestone, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/milestones", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var m []*Milestone
+	resp, err := s.client.Do(req, &m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// GetMilestone gets a single project milestone.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/milestones.html#get-single-milestone
+func (s *MilestonesService) GetMilestone(pid interface{}, milestone int, options ...OptionFunc) (*Milestone, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/milestones/%d", url.QueryEscape(project), milestone)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	m := new(Milestone)
+	resp, err := s.client.Do(req, m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// CreateMilestoneOptions represents the available CreateMilestone() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/milestones.html#create-new-milestone
+type CreateMilestoneOptions struct {
+	Title       *string  `url:"title,omitempty" json:"title,omitempty"`
+	Description *string  `url:"description,omitempty" json:"description,omitempty"`
+	StartDate   *ISOTime `url:"start_date,omitempty" json:"start_date,omitempty"`
+	DueDate     *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"`
+}
+
+// CreateMilestone creates a new project milestone.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/milestones.html#create-new-milestone
+func (s *MilestonesService) CreateMilestone(pid interface{}, opt *CreateMilestoneOptions, options ...OptionFunc) (*Milestone, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/milestones", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	m := new(Milestone)
+	resp, err := s.client.Do(req, m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// UpdateMilestoneOptions represents the available UpdateMilestone() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/milestones.html#edit-milestone
+type UpdateMilestoneOptions struct {
+	Title       *string  `url:"title,omitempty" json:"title,omitempty"`
+	Description *string  `url:"description,omitempty" json:"description,omitempty"`
+	StartDate   *ISOTime `url:"start_date,omitempty" json:"start_date,omitempty"`
+	DueDate     *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"`
+	StateEvent  *string  `url:"state_event,omitempty" json:"state_event,omitempty"`
+}
+
+// UpdateMilestone updates an existing project milestone.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/milestones.html#edit-milestone
+func (s *MilestonesService) UpdateMilestone(pid interface{}, milestone int, opt *UpdateMilestoneOptions, options ...OptionFunc) (*Milestone, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/milestones/%d", url.QueryEscape(project), milestone)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	m := new(Milestone)
+	resp, err := s.client.Do(req, m)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return m, resp, err
+}
+
+// DeleteMilestone deletes a specified project milestone.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/milestones.html#delete-project-milestone
+func (s *MilestonesService) DeleteMilestone(pid interface{}, milestone int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/milestones/%d", url.QueryEscape(project), milestone)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+	return s.client.Do(req, nil)
+}
+
+// GetMilestoneIssuesOptions represents the available GetMilestoneIssues() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/milestones.html#get-all-issues-assigned-to-a-single-milestone
+type GetMilestoneIssuesOptions ListOptions
+
+// GetMilestoneIssues gets all issues assigned to a single project milestone.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/milestones.html#get-all-issues-assigned-to-a-single-milestone
+func (s *MilestonesService) GetMilestoneIssues(pid interface{}, milestone int, opt *GetMilestoneIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/milestones/%d/issues", url.QueryEscape(project), milestone)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var i []*Issue
+	resp, err := s.client.Do(req, &i)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return i, resp, err
+}
+
+// GetMilestoneMergeRequestsOptions represents the available
+// GetMilestoneMergeRequests() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/milestones.html#get-all-merge-requests-assigned-to-a-single-milestone
+type GetMilestoneMergeRequestsOptions ListOptions
+
+// GetMilestoneMergeRequests gets all merge requests assigned to a single
+// project milestone.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/milestones.html#get-all-merge-requests-assigned-to-a-single-milestone
+func (s *MilestonesService) GetMilestoneMergeRequests(pid interface{}, milestone int, opt *GetMilestoneMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/milestones/%d/merge_requests", url.QueryEscape(project), milestone)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var mr []*MergeRequest
+	resp, err := s.client.Do(req, &mr)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return mr, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/namespaces.go b/vendor/github.com/xanzy/go-gitlab/namespaces.go
new file mode 100644
index 0000000..9add644
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/namespaces.go
@@ -0,0 +1,122 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+)
+
+// NamespacesService handles communication with the namespace related methods
+// of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/namespaces.html
+type NamespacesService struct {
+	client *Client
+}
+
+// Namespace represents a GitLab namespace.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/namespaces.html
+type Namespace struct {
+	ID                          int    `json:"id"`
+	Name                        string `json:"name"`
+	Path                        string `json:"path"`
+	Kind                        string `json:"kind"`
+	FullPath                    string `json:"full_path"`
+	ParentID                    int    `json:"parent_id"`
+	MembersCountWithDescendants int    `json:"members_count_with_descendants"`
+}
+
+func (n Namespace) String() string {
+	return Stringify(n)
+}
+
+// ListNamespacesOptions represents the available ListNamespaces() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/namespaces.html#list-namespaces
+type ListNamespacesOptions struct {
+	ListOptions
+	Search *string `url:"search,omitempty" json:"search,omitempty"`
+}
+
+// ListNamespaces gets a list of projects accessible by the authenticated user.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/namespaces.html#list-namespaces
+func (s *NamespacesService) ListNamespaces(opt *ListNamespacesOptions, options ...OptionFunc) ([]*Namespace, *Response, error) {
+	req, err := s.client.NewRequest("GET", "namespaces", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var n []*Namespace
+	resp, err := s.client.Do(req, &n)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return n, resp, err
+}
+
+// SearchNamespace gets all namespaces that match your string in their name
+// or path.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/namespaces.html#search-for-namespace
+func (s *NamespacesService) SearchNamespace(query string, options ...OptionFunc) ([]*Namespace, *Response, error) {
+	var q struct {
+		Search string `url:"search,omitempty" json:"search,omitempty"`
+	}
+	q.Search = query
+
+	req, err := s.client.NewRequest("GET", "namespaces", &q, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var n []*Namespace
+	resp, err := s.client.Do(req, &n)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return n, resp, err
+}
+
+// GetNamespace gets a namespace by id.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/namespaces.html#get-namespace-by-id
+func (s *NamespacesService) GetNamespace(id interface{}, options ...OptionFunc) (*Namespace, *Response, error) {
+	namespace, err := parseID(id)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("namespaces/%s", namespace)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	n := new(Namespace)
+	resp, err := s.client.Do(req, n)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return n, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/notes.go b/vendor/github.com/xanzy/go-gitlab/notes.go
new file mode 100644
index 0000000..2ad7f9d
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/notes.go
@@ -0,0 +1,490 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// NotesService handles communication with the notes related methods
+// of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/notes.html
+type NotesService struct {
+	client *Client
+}
+
+// Note represents a GitLab note.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/notes.html
+type Note struct {
+	ID         int    `json:"id"`
+	Body       string `json:"body"`
+	Attachment string `json:"attachment"`
+	Title      string `json:"title"`
+	FileName   string `json:"file_name"`
+	Author     struct {
+		ID        int        `json:"id"`
+		Username  string     `json:"username"`
+		Email     string     `json:"email"`
+		Name      string     `json:"name"`
+		State     string     `json:"state"`
+		CreatedAt *time.Time `json:"created_at"`
+		AvatarURL string     `json:"avatar_url"`
+		WebURL    string     `json:"web_url"`
+	} `json:"author"`
+	System       bool       `json:"system"`
+	ExpiresAt    *time.Time `json:"expires_at"`
+	UpdatedAt    *time.Time `json:"updated_at"`
+	CreatedAt    *time.Time `json:"created_at"`
+	NoteableID   int        `json:"noteable_id"`
+	NoteableType string     `json:"noteable_type"`
+	NoteableIID  int        `json:"noteable_iid"`
+}
+
+func (n Note) String() string {
+	return Stringify(n)
+}
+
+// ListIssueNotesOptions represents the available ListIssueNotes() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#list-project-issue-notes
+type ListIssueNotesOptions ListOptions
+
+// ListIssueNotes gets a list of all notes for a single issue.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#list-project-issue-notes
+func (s *NotesService) ListIssueNotes(pid interface{}, issue int, opt *ListIssueNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/issues/%d/notes", url.QueryEscape(project), issue)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var n []*Note
+	resp, err := s.client.Do(req, &n)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return n, resp, err
+}
+
+// GetIssueNote returns a single note for a specific project issue.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#get-single-issue-note
+func (s *NotesService) GetIssueNote(pid interface{}, issue, note int, options ...OptionFunc) (*Note, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/issues/%d/notes/%d", url.QueryEscape(project), issue, note)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	n := new(Note)
+	resp, err := s.client.Do(req, n)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return n, resp, err
+}
+
+// CreateIssueNoteOptions represents the available CreateIssueNote()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#create-new-issue-note
+type CreateIssueNoteOptions struct {
+	Body *string `url:"body,omitempty" json:"body,omitempty"`
+}
+
+// CreateIssueNote creates a new note to a single project issue.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#create-new-issue-note
+func (s *NotesService) CreateIssueNote(pid interface{}, issue int, opt *CreateIssueNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/issues/%d/notes", url.QueryEscape(project), issue)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	n := new(Note)
+	resp, err := s.client.Do(req, n)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return n, resp, err
+}
+
+// UpdateIssueNoteOptions represents the available UpdateIssueNote()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#modify-existing-issue-note
+type UpdateIssueNoteOptions struct {
+	Body *string `url:"body,omitempty" json:"body,omitempty"`
+}
+
+// UpdateIssueNote modifies existing note of an issue.
+//
+// https://docs.gitlab.com/ce/api/notes.html#modify-existing-issue-note
+func (s *NotesService) UpdateIssueNote(pid interface{}, issue, note int, opt *UpdateIssueNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/issues/%d/notes/%d", url.QueryEscape(project), issue, note)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	n := new(Note)
+	resp, err := s.client.Do(req, n)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return n, resp, err
+}
+
+// DeleteIssueNote deletes an existing note of an issue.
+//
+// https://docs.gitlab.com/ce/api/notes.html#delete-an-issue-note
+func (s *NotesService) DeleteIssueNote(pid interface{}, issue, note int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/issues/%d/notes/%d", url.QueryEscape(project), issue, note)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// ListSnippetNotesOptions represents the available ListSnippetNotes() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#list-all-snippet-notes
+type ListSnippetNotesOptions ListOptions
+
+// ListSnippetNotes gets a list of all notes for a single snippet. Snippet
+// notes are comments users can post to a snippet.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#list-all-snippet-notes
+func (s *NotesService) ListSnippetNotes(pid interface{}, snippet int, opt *ListSnippetNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/snippets/%d/notes", url.QueryEscape(project), snippet)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var n []*Note
+	resp, err := s.client.Do(req, &n)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return n, resp, err
+}
+
+// GetSnippetNote returns a single note for a given snippet.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#get-single-snippet-note
+func (s *NotesService) GetSnippetNote(pid interface{}, snippet, note int, options ...OptionFunc) (*Note, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", url.QueryEscape(project), snippet, note)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	n := new(Note)
+	resp, err := s.client.Do(req, n)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return n, resp, err
+}
+
+// CreateSnippetNoteOptions represents the available CreateSnippetNote()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#create-new-snippet-note
+type CreateSnippetNoteOptions struct {
+	Body *string `url:"body,omitempty" json:"body,omitempty"`
+}
+
+// CreateSnippetNote creates a new note for a single snippet. Snippet notes are
+// comments users can post to a snippet.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#create-new-snippet-note
+func (s *NotesService) CreateSnippetNote(pid interface{}, snippet int, opt *CreateSnippetNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/snippets/%d/notes", url.QueryEscape(project), snippet)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	n := new(Note)
+	resp, err := s.client.Do(req, n)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return n, resp, err
+}
+
+// UpdateSnippetNoteOptions represents the available UpdateSnippetNote()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#modify-existing-snippet-note
+type UpdateSnippetNoteOptions struct {
+	Body *string `url:"body,omitempty" json:"body,omitempty"`
+}
+
+// UpdateSnippetNote modifies existing note of a snippet.
+//
+// https://docs.gitlab.com/ce/api/notes.html#modify-existing-snippet-note
+func (s *NotesService) UpdateSnippetNote(pid interface{}, snippet, note int, opt *UpdateSnippetNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", url.QueryEscape(project), snippet, note)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	n := new(Note)
+	resp, err := s.client.Do(req, n)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return n, resp, err
+}
+
+// DeleteSnippetNote deletes an existing note of a snippet.
+//
+// https://docs.gitlab.com/ce/api/notes.html#delete-a-snippet-note
+func (s *NotesService) DeleteSnippetNote(pid interface{}, snippet, note int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", url.QueryEscape(project), snippet, note)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// ListMergeRequestNotesOptions represents the available ListMergeRequestNotes()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#list-all-merge-request-notes
+type ListMergeRequestNotesOptions ListOptions
+
+// ListMergeRequestNotes gets a list of all notes for a single merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#list-all-merge-request-notes
+func (s *NotesService) ListMergeRequestNotes(pid interface{}, mergeRequest int, opt *ListMergeRequestNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/notes", url.QueryEscape(project), mergeRequest)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var n []*Note
+	resp, err := s.client.Do(req, &n)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return n, resp, err
+}
+
+// GetMergeRequestNote returns a single note for a given merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#get-single-merge-request-note
+func (s *NotesService) GetMergeRequestNote(pid interface{}, mergeRequest, note int, options ...OptionFunc) (*Note, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/notes/%d", url.QueryEscape(project), mergeRequest, note)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	n := new(Note)
+	resp, err := s.client.Do(req, n)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return n, resp, err
+}
+
+// CreateMergeRequestNoteOptions represents the available
+// CreateMergeRequestNote() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#create-new-merge-request-note
+type CreateMergeRequestNoteOptions struct {
+	Body *string `url:"body,omitempty" json:"body,omitempty"`
+}
+
+// CreateMergeRequestNote creates a new note for a single merge request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#create-new-merge-request-note
+func (s *NotesService) CreateMergeRequestNote(pid interface{}, mergeRequest int, opt *CreateMergeRequestNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/merge_requests/%d/notes", url.QueryEscape(project), mergeRequest)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	n := new(Note)
+	resp, err := s.client.Do(req, n)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return n, resp, err
+}
+
+// UpdateMergeRequestNoteOptions represents the available
+// UpdateMergeRequestNote() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notes.html#modify-existing-merge-request-note
+type UpdateMergeRequestNoteOptions struct {
+	Body *string `url:"body,omitempty" json:"body,omitempty"`
+}
+
+// UpdateMergeRequestNote modifies existing note of a merge request.
+//
+// https://docs.gitlab.com/ce/api/notes.html#modify-existing-merge-request-note
+func (s *NotesService) UpdateMergeRequestNote(pid interface{}, mergeRequest, note int, opt *UpdateMergeRequestNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf(
+		"projects/%s/merge_requests/%d/notes/%d", url.QueryEscape(project), mergeRequest, note)
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	n := new(Note)
+	resp, err := s.client.Do(req, n)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return n, resp, err
+}
+
+// DeleteMergeRequestNote deletes an existing note of a merge request.
+//
+// https://docs.gitlab.com/ce/api/notes.html#delete-a-merge-request-note
+func (s *NotesService) DeleteMergeRequestNote(pid interface{}, mergeRequest, note int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf(
+		"projects/%s/merge_requests/%d/notes/%d", url.QueryEscape(project), mergeRequest, note)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/notifications.go b/vendor/github.com/xanzy/go-gitlab/notifications.go
new file mode 100644
index 0000000..a5501dd
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/notifications.go
@@ -0,0 +1,214 @@
+package gitlab
+
+import (
+	"errors"
+	"fmt"
+	"net/url"
+)
+
+// NotificationSettingsService handles communication with the notification settings
+// related methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/notification_settings.html
+type NotificationSettingsService struct {
+	client *Client
+}
+
+// NotificationSettings represents the Gitlab notification setting.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notification_settings.html#notification-settings
+type NotificationSettings struct {
+	Level             NotificationLevelValue `json:"level"`
+	NotificationEmail string                 `json:"notification_email"`
+	Events            *NotificationEvents    `json:"events"`
+}
+
+// NotificationEvents represents the available notification setting events.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notification_settings.html#notification-settings
+type NotificationEvents struct {
+	CloseIssue           bool `json:"close_issue"`
+	CloseMergeRequest    bool `json:"close_merge_request"`
+	FailedPipeline       bool `json:"failed_pipeline"`
+	MergeMergeRequest    bool `json:"merge_merge_request"`
+	NewIssue             bool `json:"new_issue"`
+	NewMergeRequest      bool `json:"new_merge_request"`
+	NewNote              bool `json:"new_note"`
+	ReassignIssue        bool `json:"reassign_issue"`
+	ReassignMergeRequest bool `json:"reassign_merge_request"`
+	ReopenIssue          bool `json:"reopen_issue"`
+	ReopenMergeRequest   bool `json:"reopen_merge_request"`
+	SuccessPipeline      bool `json:"success_pipeline"`
+}
+
+func (ns NotificationSettings) String() string {
+	return Stringify(ns)
+}
+
+// GetGlobalSettings returns current notification settings and email address.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notification_settings.html#global-notification-settings
+func (s *NotificationSettingsService) GetGlobalSettings(options ...OptionFunc) (*NotificationSettings, *Response, error) {
+	u := "notification_settings"
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ns := new(NotificationSettings)
+	resp, err := s.client.Do(req, ns)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ns, resp, err
+}
+
+// NotificationSettingsOptions represents the available options that can be passed
+// to the API when updating the notification settings.
+type NotificationSettingsOptions struct {
+	Level                *NotificationLevelValue `url:"level,omitempty" json:"level,omitempty"`
+	NotificationEmail    *string                 `url:"notification_email,omitempty" json:"notification_email,omitempty"`
+	CloseIssue           *bool                   `url:"close_issue,omitempty" json:"close_issue,omitempty"`
+	CloseMergeRequest    *bool                   `url:"close_merge_request,omitempty" json:"close_merge_request,omitempty"`
+	FailedPipeline       *bool                   `url:"failed_pipeline,omitempty" json:"failed_pipeline,omitempty"`
+	MergeMergeRequest    *bool                   `url:"merge_merge_request,omitempty" json:"merge_merge_request,omitempty"`
+	NewIssue             *bool                   `url:"new_issue,omitempty" json:"new_issue,omitempty"`
+	NewMergeRequest      *bool                   `url:"new_merge_request,omitempty" json:"new_merge_request,omitempty"`
+	NewNote              *bool                   `url:"new_note,omitempty" json:"new_note,omitempty"`
+	ReassignIssue        *bool                   `url:"reassign_issue,omitempty" json:"reassign_issue,omitempty"`
+	ReassignMergeRequest *bool                   `url:"reassign_merge_request,omitempty" json:"reassign_merge_request,omitempty"`
+	ReopenIssue          *bool                   `url:"reopen_issue,omitempty" json:"reopen_issue,omitempty"`
+	ReopenMergeRequest   *bool                   `url:"reopen_merge_request,omitempty" json:"reopen_merge_request,omitempty"`
+	SuccessPipeline      *bool                   `url:"success_pipeline,omitempty" json:"success_pipeline,omitempty"`
+}
+
+// UpdateGlobalSettings updates current notification settings and email address.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notification_settings.html#update-global-notification-settings
+func (s *NotificationSettingsService) UpdateGlobalSettings(opt *NotificationSettingsOptions, options ...OptionFunc) (*NotificationSettings, *Response, error) {
+	if opt.Level != nil && *opt.Level == GlobalNotificationLevel {
+		return nil, nil, errors.New(
+			"notification level 'global' is not valid for global notification settings")
+	}
+
+	u := "notification_settings"
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ns := new(NotificationSettings)
+	resp, err := s.client.Do(req, ns)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ns, resp, err
+}
+
+// GetSettingsForGroup returns current group notification settings.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notification_settings.html#group-project-level-notification-settings
+func (s *NotificationSettingsService) GetSettingsForGroup(gid interface{}, options ...OptionFunc) (*NotificationSettings, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/notification_settings", url.QueryEscape(group))
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ns := new(NotificationSettings)
+	resp, err := s.client.Do(req, ns)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ns, resp, err
+}
+
+// GetSettingsForProject returns current project notification settings.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notification_settings.html#group-project-level-notification-settings
+func (s *NotificationSettingsService) GetSettingsForProject(pid interface{}, options ...OptionFunc) (*NotificationSettings, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/notification_settings", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ns := new(NotificationSettings)
+	resp, err := s.client.Do(req, ns)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ns, resp, err
+}
+
+// UpdateSettingsForGroup updates current group notification settings.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notification_settings.html#update-group-project-level-notification-settings
+func (s *NotificationSettingsService) UpdateSettingsForGroup(gid interface{}, opt *NotificationSettingsOptions, options ...OptionFunc) (*NotificationSettings, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/notification_settings", url.QueryEscape(group))
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ns := new(NotificationSettings)
+	resp, err := s.client.Do(req, ns)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ns, resp, err
+}
+
+// UpdateSettingsForProject updates current project notification settings.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/notification_settings.html#update-group-project-level-notification-settings
+func (s *NotificationSettingsService) UpdateSettingsForProject(pid interface{}, opt *NotificationSettingsOptions, options ...OptionFunc) (*NotificationSettings, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/notification_settings", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ns := new(NotificationSettings)
+	resp, err := s.client.Do(req, ns)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ns, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/pages_domains.go b/vendor/github.com/xanzy/go-gitlab/pages_domains.go
new file mode 100644
index 0000000..3cd72e2
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/pages_domains.go
@@ -0,0 +1,175 @@
+package gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// PagesDomainsService handles communication with the pages domains
+// related methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/pages_domains.html
+type PagesDomainsService struct {
+	client *Client
+}
+
+// PagesDomain represents a pages domain.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/pages_domains.html
+type PagesDomain struct {
+	Domain           string     `json:"domain"`
+	URL              string     `json:"url"`
+	ProjectID        int        `json:"project_id"`
+	Verified         bool       `json:"verified"`
+	VerificationCode string     `json:"verification_code"`
+	EnabledUntil     *time.Time `json:"enabled_until"`
+	Certificate      struct {
+		Expired    bool       `json:"expired"`
+		Expiration *time.Time `json:"expiration"`
+	} `json:"certificate"`
+}
+
+// ListPagesDomainsOptions represents the available ListPagesDomains() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pages_domains.html#list-pages-domains
+type ListPagesDomainsOptions ListOptions
+
+// ListPagesDomains gets a list of project pages domains.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pages_domains.html#list-pages-domains
+func (s *PagesDomainsService) ListPagesDomains(pid interface{}, opt *ListPagesDomainsOptions, options ...OptionFunc) ([]*PagesDomain, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pages/domains", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var pd []*PagesDomain
+	resp, err := s.client.Do(req, &pd)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pd, resp, err
+}
+
+// GetPagesDomain get a specific pages domain for a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pages_domains.html#single-pages-domain
+func (s *PagesDomainsService) GetPagesDomain(pid interface{}, domain string, options ...OptionFunc) (*PagesDomain, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pages/domains/%s", url.QueryEscape(project), domain)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	pd := new(PagesDomain)
+	resp, err := s.client.Do(req, pd)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pd, resp, err
+}
+
+// CreatePagesDomainOptions represents the available CreatePagesDomain() options.
+//
+// GitLab API docs:
+// // https://docs.gitlab.com/ce/api/pages_domains.html#create-new-pages-domain
+type CreatePagesDomainOptions struct {
+	Domain      *string `url:"domain,omitempty" json:"domain,omitempty"`
+	Certificate *string `url:"certifiate,omitempty" json:"certifiate,omitempty"`
+	Key         *string `url:"key,omitempty" json:"key,omitempty"`
+}
+
+// CreatePagesDomain creates a new project pages domain.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pages_domains.html#create-new-pages-domain
+func (s *PagesDomainsService) CreatePagesDomain(pid interface{}, opt *CreatePagesDomainOptions, options ...OptionFunc) (*PagesDomain, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pages/domains", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	pd := new(PagesDomain)
+	resp, err := s.client.Do(req, pd)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pd, resp, err
+}
+
+// UpdatePagesDomainOptions represents the available UpdatePagesDomain() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pages_domains.html#update-pages-domain
+type UpdatePagesDomainOptions struct {
+	Cerificate *string `url:"certifiate" json:"certifiate"`
+	Key        *string `url:"key" json:"key"`
+}
+
+// UpdatePagesDomain updates an existing project pages domain.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pages_domains.html#update-pages-domain
+func (s *PagesDomainsService) UpdatePagesDomain(pid interface{}, domain string, opt *UpdatePagesDomainOptions, options ...OptionFunc) (*PagesDomain, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pages/domains/%s", url.QueryEscape(project), domain)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	pd := new(PagesDomain)
+	resp, err := s.client.Do(req, pd)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pd, resp, err
+}
+
+// DeletePagesDomain deletes an existing prject pages domain.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pages_domains.html#delete-pages-domain
+func (s *PagesDomainsService) DeletePagesDomain(pid interface{}, domain string, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pages/domains/%s", url.QueryEscape(project), domain)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/pipeline_schedules.go b/vendor/github.com/xanzy/go-gitlab/pipeline_schedules.go
new file mode 100644
index 0000000..bb93583
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/pipeline_schedules.go
@@ -0,0 +1,341 @@
+//
+// Copyright 2018, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// PipelineSchedulesService handles communication with the pipeline
+// schedules related methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/pipeline_schedules.html
+type PipelineSchedulesService struct {
+	client *Client
+}
+
+// PipelineVariable represents a pipeline schedule variable.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_schedules.html#pipeline-schedule-variable
+type PipelineVariable struct {
+	Key   string `json:"key"`
+	Value string `json:"value"`
+}
+
+// PipelineSchedule represents a pipeline schedule.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_schedules.html
+type PipelineSchedule struct {
+	ID           int        `json:"id"`
+	Description  string     `json:"description"`
+	Ref          string     `json:"ref"`
+	Cron         string     `json:"cron"`
+	CronTimezone string     `json:"cron_timezone"`
+	NextRunAt    *time.Time `json:"next_run_at"`
+	Active       bool       `json:"active"`
+	CreatedAt    *time.Time `json:"created_at"`
+	UpdatedAt    *time.Time `json:"updated_at"`
+	Owner        *User      `json:"owner"`
+	LastPipeline struct {
+		ID     int    `json:"id"`
+		Sha    string `json:"sha"`
+		Ref    string `json:"ref"`
+		Status string `json:"status"`
+	} `json:"last_pipeline"`
+	Variables []*PipelineVariable `json:"variables"`
+}
+
+// ListPipelineSchedulesOptions represents the available ListPipelineTriggers() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_triggers.html#list-project-triggers
+type ListPipelineSchedulesOptions ListOptions
+
+// ListPipelineSchedules gets a list of project triggers.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_schedules.html
+func (s *PipelineSchedulesService) ListPipelineSchedules(pid interface{}, opt *ListPipelineSchedulesOptions, options ...OptionFunc) ([]*PipelineSchedule, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pipeline_schedules", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var ps []*PipelineSchedule
+	resp, err := s.client.Do(req, &ps)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ps, resp, err
+}
+
+// GetPipelineSchedule gets a pipeline schedule.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_schedules.html
+func (s *PipelineSchedulesService) GetPipelineSchedule(pid interface{}, schedule int, options ...OptionFunc) (*PipelineSchedule, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pipeline_schedules/%d", url.QueryEscape(project), schedule)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(PipelineSchedule)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// CreatePipelineScheduleOptions represents the available
+// CreatePipelineSchedule() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_schedules.html#create-a-new-pipeline-schedule
+type CreatePipelineScheduleOptions struct {
+	Description  *string `url:"description" json:"description"`
+	Ref          *string `url:"ref" json:"ref"`
+	Cron         *string `url:"cron" json:"cron"`
+	CronTimezone *string `url:"cron_timezone,omitempty" json:"cron_timezone,omitempty"`
+	Active       *bool   `url:"active,omitempty" json:"active,omitempty"`
+}
+
+// CreatePipelineSchedule creates a pipeline schedule.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_schedules.html#create-a-new-pipeline-schedule
+func (s *PipelineSchedulesService) CreatePipelineSchedule(pid interface{}, opt *CreatePipelineScheduleOptions, options ...OptionFunc) (*PipelineSchedule, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pipeline_schedules", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(PipelineSchedule)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// EditPipelineScheduleOptions represents the available
+// EditPipelineSchedule() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_schedules.html#create-a-new-pipeline-schedule
+type EditPipelineScheduleOptions struct {
+	Description  *string `url:"description,omitempty" json:"description,omitempty"`
+	Ref          *string `url:"ref,omitempty" json:"ref,omitempty"`
+	Cron         *string `url:"cron,omitempty" json:"cron,omitempty"`
+	CronTimezone *string `url:"cron_timezone,omitempty" json:"cron_timezone,omitempty"`
+	Active       *bool   `url:"active,omitempty" json:"active,omitempty"`
+}
+
+// EditPipelineSchedule edits a pipeline schedule.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_schedules.html#edit-a-pipeline-schedule
+func (s *PipelineSchedulesService) EditPipelineSchedule(pid interface{}, schedule int, opt *EditPipelineScheduleOptions, options ...OptionFunc) (*PipelineSchedule, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pipeline_schedules/%d", url.QueryEscape(project), schedule)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(PipelineSchedule)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// TakeOwnershipOfPipelineSchedule sets the owner of the specified
+// pipeline schedule to the user issuing the request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_schedules.html#take-ownership-of-a-pipeline-schedule
+func (s *PipelineSchedulesService) TakeOwnershipOfPipelineSchedule(pid interface{}, schedule int, options ...OptionFunc) (*PipelineSchedule, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pipeline_schedules/%d/take_ownership", url.QueryEscape(project), schedule)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(PipelineSchedule)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// DeletePipelineSchedule deletes a pipeline schedule.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_schedules.html#delete-a-pipeline-schedule
+func (s *PipelineSchedulesService) DeletePipelineSchedule(pid interface{}, schedule int, options ...OptionFunc) (*PipelineSchedule, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pipeline_schedules/%d", url.QueryEscape(project), schedule)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(PipelineSchedule)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// CreatePipelineScheduleVariableOptions represents the available
+// CreatePipelineScheduleVariable() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_schedules.html#create-a-new-pipeline-schedule
+type CreatePipelineScheduleVariableOptions struct {
+	Key   *string `url:"key" json:"key"`
+	Value *string `url:"value" json:"value"`
+}
+
+// CreatePipelineScheduleVariable creates a pipeline schedule variable.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_schedules.html#create-a-new-pipeline-schedule
+func (s *PipelineSchedulesService) CreatePipelineScheduleVariable(pid interface{}, schedule int, opt *CreatePipelineScheduleVariableOptions, options ...OptionFunc) (*PipelineVariable, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pipeline_schedules/%d/variables", url.QueryEscape(project), schedule)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(PipelineVariable)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// EditPipelineScheduleVariableOptions represents the available
+// EditPipelineScheduleVariable() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_schedules.html#edit-a-pipeline-schedule-variable
+type EditPipelineScheduleVariableOptions struct {
+	Value *string `url:"value" json:"value"`
+}
+
+// EditPipelineScheduleVariable creates a pipeline schedule variable.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_schedules.html#edit-a-pipeline-schedule-variable
+func (s *PipelineSchedulesService) EditPipelineScheduleVariable(pid interface{}, schedule int, key string, opt *EditPipelineScheduleVariableOptions, options ...OptionFunc) (*PipelineVariable, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pipeline_schedules/%d/variables/%s", url.QueryEscape(project), schedule, key)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(PipelineVariable)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// DeletePipelineScheduleVariable creates a pipeline schedule variable.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_schedules.html#delete-a-pipeline-schedule-variable
+func (s *PipelineSchedulesService) DeletePipelineScheduleVariable(pid interface{}, schedule int, key string, options ...OptionFunc) (*PipelineVariable, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pipeline_schedules/%d/variables/%s", url.QueryEscape(project), schedule, key)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(PipelineVariable)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/pipeline_triggers.go b/vendor/github.com/xanzy/go-gitlab/pipeline_triggers.go
new file mode 100644
index 0000000..6e8dfb8
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/pipeline_triggers.go
@@ -0,0 +1,232 @@
+package gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// PipelineTriggersService handles Project pipeline triggers.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_triggers.html
+type PipelineTriggersService struct {
+	client *Client
+}
+
+// PipelineTrigger represents a project pipeline trigger.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_triggers.html#pipeline-triggers
+type PipelineTrigger struct {
+	ID          int        `json:"id"`
+	Description string     `json:"description"`
+	CreatedAt   *time.Time `json:"created_at"`
+	DeletedAt   *time.Time `json:"deleted_at"`
+	LastUsed    *time.Time `json:"last_used"`
+	Token       string     `json:"token"`
+	UpdatedAt   *time.Time `json:"updated_at"`
+	Owner       *User      `json:"owner"`
+}
+
+// ListPipelineTriggersOptions represents the available ListPipelineTriggers() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_triggers.html#list-project-triggers
+type ListPipelineTriggersOptions ListOptions
+
+// ListPipelineTriggers gets a list of project triggers.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_triggers.html#list-project-triggers
+func (s *PipelineTriggersService) ListPipelineTriggers(pid interface{}, opt *ListPipelineTriggersOptions, options ...OptionFunc) ([]*PipelineTrigger, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/triggers", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var pt []*PipelineTrigger
+	resp, err := s.client.Do(req, &pt)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pt, resp, err
+}
+
+// GetPipelineTrigger gets a specific pipeline trigger for a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_triggers.html#get-trigger-details
+func (s *PipelineTriggersService) GetPipelineTrigger(pid interface{}, trigger int, options ...OptionFunc) (*PipelineTrigger, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/triggers/%d", url.QueryEscape(project), trigger)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	pt := new(PipelineTrigger)
+	resp, err := s.client.Do(req, pt)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pt, resp, err
+}
+
+// AddPipelineTriggerOptions represents the available AddPipelineTrigger() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_triggers.html#create-a-project-trigger
+type AddPipelineTriggerOptions struct {
+	Description *string `url:"description,omitempty" json:"description,omitempty"`
+}
+
+// AddPipelineTrigger adds a pipeline trigger to a specified project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_triggers.html#create-a-project-trigger
+func (s *PipelineTriggersService) AddPipelineTrigger(pid interface{}, opt *AddPipelineTriggerOptions, options ...OptionFunc) (*PipelineTrigger, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/triggers", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	pt := new(PipelineTrigger)
+	resp, err := s.client.Do(req, pt)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pt, resp, err
+}
+
+// EditPipelineTriggerOptions represents the available EditPipelineTrigger() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_triggers.html#update-a-project-trigger
+type EditPipelineTriggerOptions struct {
+	Description *string `url:"description,omitempty" json:"description,omitempty"`
+}
+
+// EditPipelineTrigger edits a trigger for a specified project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_triggers.html#update-a-project-trigger
+func (s *PipelineTriggersService) EditPipelineTrigger(pid interface{}, trigger int, opt *EditPipelineTriggerOptions, options ...OptionFunc) (*PipelineTrigger, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/triggers/%d", url.QueryEscape(project), trigger)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	pt := new(PipelineTrigger)
+	resp, err := s.client.Do(req, pt)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pt, resp, err
+}
+
+// TakeOwnershipOfPipelineTrigger sets the owner of the specified
+// pipeline trigger to the user issuing the request.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_triggers.html#take-ownership-of-a-project-trigger
+func (s *PipelineTriggersService) TakeOwnershipOfPipelineTrigger(pid interface{}, trigger int, options ...OptionFunc) (*PipelineTrigger, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/triggers/%d/take_ownership", url.QueryEscape(project), trigger)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	pt := new(PipelineTrigger)
+	resp, err := s.client.Do(req, pt)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pt, resp, err
+}
+
+// DeletePipelineTrigger removes a trigger from a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipeline_triggers.html#remove-a-project-trigger
+func (s *PipelineTriggersService) DeletePipelineTrigger(pid interface{}, trigger int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/triggers/%d", url.QueryEscape(project), trigger)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// RunPipelineTriggerOptions represents the available RunPipelineTrigger() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/ci/triggers/README.html#triggering-a-pipeline
+type RunPipelineTriggerOptions struct {
+	Ref       *string           `url:"ref" json:"ref"`
+	Token     *string           `url:"token" json:"token"`
+	Variables map[string]string `url:"variables,omitempty" json:"variables,omitempty"`
+}
+
+// RunPipelineTrigger starts a trigger from a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/ci/triggers/README.html#triggering-a-pipeline
+func (s *PipelineTriggersService) RunPipelineTrigger(pid interface{}, opt *RunPipelineTriggerOptions, options ...OptionFunc) (*Pipeline, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/trigger/pipeline", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	pt := new(Pipeline)
+	resp, err := s.client.Do(req, pt)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pt, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/pipelines.go b/vendor/github.com/xanzy/go-gitlab/pipelines.go
new file mode 100644
index 0000000..952d0d2
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/pipelines.go
@@ -0,0 +1,220 @@
+//
+// Copyright 2017, Igor Varavko
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// PipelinesService handles communication with the repositories related
+// methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html
+type PipelinesService struct {
+	client *Client
+}
+
+// Pipeline represents a GitLab pipeline.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html
+type Pipeline struct {
+	ID         int    `json:"id"`
+	Status     string `json:"status"`
+	Ref        string `json:"ref"`
+	Sha        string `json:"sha"`
+	BeforeSha  string `json:"before_sha"`
+	Tag        bool   `json:"tag"`
+	YamlErrors string `json:"yaml_errors"`
+	User       struct {
+		Name      string `json:"name"`
+		Username  string `json:"username"`
+		ID        int    `json:"id"`
+		State     string `json:"state"`
+		AvatarURL string `json:"avatar_url"`
+		WebURL    string `json:"web_url"`
+	}
+	UpdatedAt   *time.Time `json:"updated_at"`
+	CreatedAt   *time.Time `json:"created_at"`
+	StartedAt   *time.Time `json:"started_at"`
+	FinishedAt  *time.Time `json:"finished_at"`
+	CommittedAt *time.Time `json:"committed_at"`
+	Duration    int        `json:"duration"`
+	Coverage    string     `json:"coverage"`
+}
+
+func (i Pipeline) String() string {
+	return Stringify(i)
+}
+
+// PipelineList represents a GitLab list project pipelines
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#list-project-pipelines
+type PipelineList []struct {
+	ID     int    `json:"id"`
+	Status string `json:"status"`
+	Ref    string `json:"ref"`
+	Sha    string `json:"sha"`
+}
+
+func (i PipelineList) String() string {
+	return Stringify(i)
+}
+
+// ListProjectPipelinesOptions represents the available ListProjectPipelines() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#list-project-pipelines
+type ListProjectPipelinesOptions struct {
+	ListOptions
+	Scope      *string          `url:"scope,omitempty" json:"scope,omitempty"`
+	Status     *BuildStateValue `url:"status,omitempty" json:"status,omitempty"`
+	Ref        *string          `url:"ref,omitempty" json:"ref,omitempty"`
+	YamlErrors *bool            `url:"yaml_errors,omitempty" json:"yaml_errors,omitempty"`
+	Name       *string          `url:"name,omitempty" json:"name,omitempty"`
+	Username   *string          `url:"username,omitempty" json:"username,omitempty"`
+	OrderBy    *string          `url:"order_by,omitempty" json:"order_by,omitempty"`
+	Sort       *string          `url:"sort,omitempty" json:"sort,omitempty"`
+}
+
+// ListProjectPipelines gets a list of project piplines.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#list-project-pipelines
+func (s *PipelinesService) ListProjectPipelines(pid interface{}, opt *ListProjectPipelinesOptions, options ...OptionFunc) (PipelineList, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pipelines", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var p PipelineList
+	resp, err := s.client.Do(req, &p)
+	if err != nil {
+		return nil, resp, err
+	}
+	return p, resp, err
+}
+
+// GetPipeline gets a single project pipeline.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#get-a-single-pipeline
+func (s *PipelinesService) GetPipeline(pid interface{}, pipeline int, options ...OptionFunc) (*Pipeline, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pipelines/%d", url.QueryEscape(project), pipeline)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(Pipeline)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// CreatePipelineOptions represents the available CreatePipeline() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#create-a-new-pipeline
+type CreatePipelineOptions struct {
+	Ref *string `url:"ref,omitempty" json:"ref"`
+}
+
+// CreatePipeline creates a new project pipeline.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#create-a-new-pipeline
+func (s *PipelinesService) CreatePipeline(pid interface{}, opt *CreatePipelineOptions, options ...OptionFunc) (*Pipeline, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pipeline", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(Pipeline)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// RetryPipelineBuild retries failed builds in a pipeline
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/pipelines.html#retry-failed-builds-in-a-pipeline
+func (s *PipelinesService) RetryPipelineBuild(pid interface{}, pipelineID int, options ...OptionFunc) (*Pipeline, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pipelines/%d/retry", project, pipelineID)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(Pipeline)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// CancelPipelineBuild cancels a pipeline builds
+//
+// GitLab API docs:
+//https://docs.gitlab.com/ce/api/pipelines.html#cancel-a-pipelines-builds
+func (s *PipelinesService) CancelPipelineBuild(pid interface{}, pipelineID int, options ...OptionFunc) (*Pipeline, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/pipelines/%d/cancel", project, pipelineID)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(Pipeline)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/project_members.go b/vendor/github.com/xanzy/go-gitlab/project_members.go
new file mode 100644
index 0000000..70beb72
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/project_members.go
@@ -0,0 +1,179 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+)
+
+// ProjectMembersService handles communication with the project members
+// related methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/members.html
+type ProjectMembersService struct {
+	client *Client
+}
+
+// ListProjectMembersOptions represents the available ListProjectMembers()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
+type ListProjectMembersOptions struct {
+	ListOptions
+	Query *string `url:"query,omitempty" json:"query,omitempty"`
+}
+
+// ListProjectMembers gets a list of a project's team members.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
+func (s *ProjectMembersService) ListProjectMembers(pid interface{}, opt *ListProjectMembersOptions, options ...OptionFunc) ([]*ProjectMember, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/members", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var pm []*ProjectMember
+	resp, err := s.client.Do(req, &pm)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pm, resp, err
+}
+
+// GetProjectMember gets a project team member.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/members.html#get-a-member-of-a-group-or-project
+func (s *ProjectMembersService) GetProjectMember(pid interface{}, user int, options ...OptionFunc) (*ProjectMember, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/members/%d", url.QueryEscape(project), user)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	pm := new(ProjectMember)
+	resp, err := s.client.Do(req, pm)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pm, resp, err
+}
+
+// AddProjectMemberOptions represents the available AddProjectMember() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/members.html#add-a-member-to-a-group-or-project
+type AddProjectMemberOptions struct {
+	UserID      *int              `url:"user_id,omitempty" json:"user_id,omitempty"`
+	AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
+}
+
+// AddProjectMember adds a user to a project team. This is an idempotent
+// method and can be called multiple times with the same parameters. Adding
+// team membership to a user that is already a member does not affect the
+// existing membership.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/members.html#add-a-member-to-a-group-or-project
+func (s *ProjectMembersService) AddProjectMember(pid interface{}, opt *AddProjectMemberOptions, options ...OptionFunc) (*ProjectMember, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/members", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	pm := new(ProjectMember)
+	resp, err := s.client.Do(req, pm)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pm, resp, err
+}
+
+// EditProjectMemberOptions represents the available EditProjectMember() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/members.html#edit-a-member-of-a-group-or-project
+type EditProjectMemberOptions struct {
+	AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
+}
+
+// EditProjectMember updates a project team member to a specified access level..
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/members.html#edit-a-member-of-a-group-or-project
+func (s *ProjectMembersService) EditProjectMember(pid interface{}, user int, opt *EditProjectMemberOptions, options ...OptionFunc) (*ProjectMember, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/members/%d", url.QueryEscape(project), user)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	pm := new(ProjectMember)
+	resp, err := s.client.Do(req, pm)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pm, resp, err
+}
+
+// DeleteProjectMember removes a user from a project team.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/members.html#remove-a-member-from-a-group-or-project
+func (s *ProjectMembersService) DeleteProjectMember(pid interface{}, user int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/members/%d", url.QueryEscape(project), user)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/project_snippets.go b/vendor/github.com/xanzy/go-gitlab/project_snippets.go
new file mode 100644
index 0000000..0042819
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/project_snippets.go
@@ -0,0 +1,207 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"bytes"
+	"fmt"
+	"net/url"
+)
+
+// ProjectSnippetsService handles communication with the project snippets
+// related methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/project_snippets.html
+type ProjectSnippetsService struct {
+	client *Client
+}
+
+// ListProjectSnippetsOptions represents the available ListSnippets() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/project_snippets.html#list-snippets
+type ListProjectSnippetsOptions ListOptions
+
+// ListSnippets gets a list of project snippets.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/project_snippets.html#list-snippets
+func (s *ProjectSnippetsService) ListSnippets(pid interface{}, opt *ListProjectSnippetsOptions, options ...OptionFunc) ([]*Snippet, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/snippets", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var ps []*Snippet
+	resp, err := s.client.Do(req, &ps)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ps, resp, err
+}
+
+// GetSnippet gets a single project snippet
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/project_snippets.html#single-snippet
+func (s *ProjectSnippetsService) GetSnippet(pid interface{}, snippet int, options ...OptionFunc) (*Snippet, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/snippets/%d", url.QueryEscape(project), snippet)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ps := new(Snippet)
+	resp, err := s.client.Do(req, ps)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ps, resp, err
+}
+
+// CreateProjectSnippetOptions represents the available CreateSnippet() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/project_snippets.html#create-new-snippet
+type CreateProjectSnippetOptions struct {
+	Title       *string          `url:"title,omitempty" json:"title,omitempty"`
+	FileName    *string          `url:"file_name,omitempty" json:"file_name,omitempty"`
+	Description *string          `url:"description,omitempty" json:"description,omitempty"`
+	Code        *string          `url:"code,omitempty" json:"code,omitempty"`
+	Visibility  *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
+}
+
+// CreateSnippet creates a new project snippet. The user must have permission
+// to create new snippets.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/project_snippets.html#create-new-snippet
+func (s *ProjectSnippetsService) CreateSnippet(pid interface{}, opt *CreateProjectSnippetOptions, options ...OptionFunc) (*Snippet, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/snippets", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ps := new(Snippet)
+	resp, err := s.client.Do(req, ps)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ps, resp, err
+}
+
+// UpdateProjectSnippetOptions represents the available UpdateSnippet() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/project_snippets.html#update-snippet
+type UpdateProjectSnippetOptions struct {
+	Title       *string          `url:"title,omitempty" json:"title,omitempty"`
+	FileName    *string          `url:"file_name,omitempty" json:"file_name,omitempty"`
+	Description *string          `url:"description,omitempty" json:"description,omitempty"`
+	Code        *string          `url:"code,omitempty" json:"code,omitempty"`
+	Visibility  *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
+}
+
+// UpdateSnippet updates an existing project snippet. The user must have
+// permission to change an existing snippet.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/project_snippets.html#update-snippet
+func (s *ProjectSnippetsService) UpdateSnippet(pid interface{}, snippet int, opt *UpdateProjectSnippetOptions, options ...OptionFunc) (*Snippet, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/snippets/%d", url.QueryEscape(project), snippet)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ps := new(Snippet)
+	resp, err := s.client.Do(req, ps)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ps, resp, err
+}
+
+// DeleteSnippet deletes an existing project snippet. This is an idempotent
+// function and deleting a non-existent snippet still returns a 200 OK status
+// code.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/project_snippets.html#delete-snippet
+func (s *ProjectSnippetsService) DeleteSnippet(pid interface{}, snippet int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/snippets/%d", url.QueryEscape(project), snippet)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// SnippetContent returns the raw project snippet as plain text.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/project_snippets.html#snippet-content
+func (s *ProjectSnippetsService) SnippetContent(pid interface{}, snippet int, options ...OptionFunc) ([]byte, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/snippets/%d/raw", url.QueryEscape(project), snippet)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var b bytes.Buffer
+	resp, err := s.client.Do(req, &b)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return b.Bytes(), resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/projects.go b/vendor/github.com/xanzy/go-gitlab/projects.go
new file mode 100644
index 0000000..4a252a9
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/projects.go
@@ -0,0 +1,997 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"mime/multipart"
+	"net/url"
+	"os"
+	"time"
+)
+
+// ProjectsService handles communication with the repositories related methods
+// of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html
+type ProjectsService struct {
+	client *Client
+}
+
+// Project represents a GitLab project.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html
+type Project struct {
+	ID                                        int               `json:"id"`
+	Description                               string            `json:"description"`
+	DefaultBranch                             string            `json:"default_branch"`
+	Public                                    bool              `json:"public"`
+	Visibility                                VisibilityValue   `json:"visibility"`
+	SSHURLToRepo                              string            `json:"ssh_url_to_repo"`
+	HTTPURLToRepo                             string            `json:"http_url_to_repo"`
+	WebURL                                    string            `json:"web_url"`
+	TagList                                   []string          `json:"tag_list"`
+	Owner                                     *User             `json:"owner"`
+	Name                                      string            `json:"name"`
+	NameWithNamespace                         string            `json:"name_with_namespace"`
+	Path                                      string            `json:"path"`
+	PathWithNamespace                         string            `json:"path_with_namespace"`
+	IssuesEnabled                             bool              `json:"issues_enabled"`
+	OpenIssuesCount                           int               `json:"open_issues_count"`
+	MergeRequestsEnabled                      bool              `json:"merge_requests_enabled"`
+	ApprovalsBeforeMerge                      int               `json:"approvals_before_merge"`
+	JobsEnabled                               bool              `json:"jobs_enabled"`
+	WikiEnabled                               bool              `json:"wiki_enabled"`
+	SnippetsEnabled                           bool              `json:"snippets_enabled"`
+	ContainerRegistryEnabled                  bool              `json:"container_registry_enabled"`
+	CreatedAt                                 *time.Time        `json:"created_at,omitempty"`
+	LastActivityAt                            *time.Time        `json:"last_activity_at,omitempty"`
+	CreatorID                                 int               `json:"creator_id"`
+	Namespace                                 *ProjectNamespace `json:"namespace"`
+	ImportStatus                              string            `json:"import_status"`
+	ImportError                               string            `json:"import_error"`
+	Permissions                               *Permissions      `json:"permissions"`
+	Archived                                  bool              `json:"archived"`
+	AvatarURL                                 string            `json:"avatar_url"`
+	SharedRunnersEnabled                      bool              `json:"shared_runners_enabled"`
+	ForksCount                                int               `json:"forks_count"`
+	StarCount                                 int               `json:"star_count"`
+	RunnersToken                              string            `json:"runners_token"`
+	PublicJobs                                bool              `json:"public_jobs"`
+	OnlyAllowMergeIfPipelineSucceeds          bool              `json:"only_allow_merge_if_pipeline_succeeds"`
+	OnlyAllowMergeIfAllDiscussionsAreResolved bool              `json:"only_allow_merge_if_all_discussions_are_resolved"`
+	LFSEnabled                                bool              `json:"lfs_enabled"`
+	RequestAccessEnabled                      bool              `json:"request_access_enabled"`
+	MergeMethod                               string            `json:"merge_method"`
+	ForkedFromProject                         *ForkParent       `json:"forked_from_project"`
+	SharedWithGroups                          []struct {
+		GroupID          int    `json:"group_id"`
+		GroupName        string `json:"group_name"`
+		GroupAccessLevel int    `json:"group_access_level"`
+	} `json:"shared_with_groups"`
+	Statistics   *ProjectStatistics `json:"statistics"`
+	Links        *Links             `json:"_links,omitempty"`
+	CIConfigPath *string            `json:"ci_config_path"`
+}
+
+// Repository represents a repository.
+type Repository struct {
+	Name              string          `json:"name"`
+	Description       string          `json:"description"`
+	WebURL            string          `json:"web_url"`
+	AvatarURL         string          `json:"avatar_url"`
+	GitSSHURL         string          `json:"git_ssh_url"`
+	GitHTTPURL        string          `json:"git_http_url"`
+	Namespace         string          `json:"namespace"`
+	Visibility        VisibilityValue `json:"visibility"`
+	PathWithNamespace string          `json:"path_with_namespace"`
+	DefaultBranch     string          `json:"default_branch"`
+	Homepage          string          `json:"homepage"`
+	URL               string          `json:"url"`
+	SSHURL            string          `json:"ssh_url"`
+	HTTPURL           string          `json:"http_url"`
+}
+
+// ProjectNamespace represents a project namespace.
+type ProjectNamespace struct {
+	ID       int    `json:"id"`
+	Name     string `json:"name"`
+	Path     string `json:"path"`
+	Kind     string `json:"kind"`
+	FullPath string `json:"full_path"`
+}
+
+// StorageStatistics represents a statistics record for a group or project.
+type StorageStatistics struct {
+	StorageSize      int64 `json:"storage_size"`
+	RepositorySize   int64 `json:"repository_size"`
+	LfsObjectsSize   int64 `json:"lfs_objects_size"`
+	JobArtifactsSize int64 `json:"job_artifacts_size"`
+}
+
+// ProjectStatistics represents a statistics record for a project.
+type ProjectStatistics struct {
+	StorageStatistics
+	CommitCount int `json:"commit_count"`
+}
+
+// Permissions represents permissions.
+type Permissions struct {
+	ProjectAccess *ProjectAccess `json:"project_access"`
+	GroupAccess   *GroupAccess   `json:"group_access"`
+}
+
+// ProjectAccess represents project access.
+type ProjectAccess struct {
+	AccessLevel       AccessLevelValue       `json:"access_level"`
+	NotificationLevel NotificationLevelValue `json:"notification_level"`
+}
+
+// GroupAccess represents group access.
+type GroupAccess struct {
+	AccessLevel       AccessLevelValue       `json:"access_level"`
+	NotificationLevel NotificationLevelValue `json:"notification_level"`
+}
+
+// ForkParent represents the parent project when this is a fork.
+type ForkParent struct {
+	HTTPURLToRepo     string `json:"http_url_to_repo"`
+	ID                int    `json:"id"`
+	Name              string `json:"name"`
+	NameWithNamespace string `json:"name_with_namespace"`
+	Path              string `json:"path"`
+	PathWithNamespace string `json:"path_with_namespace"`
+	WebURL            string `json:"web_url"`
+}
+
+// Links represents a project web links for self, issues, merge_requests,
+// repo_branches, labels, events, members.
+type Links struct {
+	Self          string `json:"self"`
+	Issues        string `json:"issues"`
+	MergeRequests string `json:"merge_requests"`
+	RepoBranches  string `json:"repo_branches"`
+	Labels        string `json:"labels"`
+	Events        string `json:"events"`
+	Members       string `json:"members"`
+}
+
+func (s Project) String() string {
+	return Stringify(s)
+}
+
+// ListProjectsOptions represents the available ListProjects() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#list-projects
+type ListProjectsOptions struct {
+	ListOptions
+	Archived                 *bool            `url:"archived,omitempty" json:"archived,omitempty"`
+	OrderBy                  *string          `url:"order_by,omitempty" json:"order_by,omitempty"`
+	Sort                     *string          `url:"sort,omitempty" json:"sort,omitempty"`
+	Search                   *string          `url:"search,omitempty" json:"search,omitempty"`
+	Simple                   *bool            `url:"simple,omitempty" json:"simple,omitempty"`
+	Owned                    *bool            `url:"owned,omitempty" json:"owned,omitempty"`
+	Membership               *bool            `url:"membership,omitempty" json:"membership,omitempty"`
+	Starred                  *bool            `url:"starred,omitempty" json:"starred,omitempty"`
+	Statistics               *bool            `url:"statistics,omitempty" json:"statistics,omitempty"`
+	Visibility               *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
+	WithIssuesEnabled        *bool            `url:"with_issues_enabled,omitempty" json:"with_issues_enabled,omitempty"`
+	WithMergeRequestsEnabled *bool            `url:"with_merge_requests_enabled,omitempty" json:"with_merge_requests_enabled,omitempty"`
+}
+
+// ListProjects gets a list of projects accessible by the authenticated user.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#list-projects
+func (s *ProjectsService) ListProjects(opt *ListProjectsOptions, options ...OptionFunc) ([]*Project, *Response, error) {
+	req, err := s.client.NewRequest("GET", "projects", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var p []*Project
+	resp, err := s.client.Do(req, &p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// ListUserProjects gets a list of projects for the given user.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#list-user-projects
+func (s *ProjectsService) ListUserProjects(uid interface{}, opt *ListProjectsOptions, options ...OptionFunc) ([]*Project, *Response, error) {
+	user, err := parseID(uid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("users/%s/projects", user)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var p []*Project
+	resp, err := s.client.Do(req, &p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// ProjectUser represents a GitLab project user.
+type ProjectUser struct {
+	ID        int    `json:"id"`
+	Name      string `json:"name"`
+	Username  string `json:"username"`
+	State     string `json:"state"`
+	AvatarURL string `json:"avatar_url"`
+	WebURL    string `json:"web_url"`
+}
+
+// ListProjectUserOptions represents the available ListProjectsUsers() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#get-project-users
+type ListProjectUserOptions struct {
+	ListOptions
+	Search *string `url:"search,omitempty" json:"search,omitempty"`
+}
+
+// ListProjectsUsers gets a list of users for the given project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#get-project-users
+func (s *ProjectsService) ListProjectsUsers(pid interface{}, opt *ListProjectUserOptions, options ...OptionFunc) ([]*ProjectUser, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/users", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var p []*ProjectUser
+	resp, err := s.client.Do(req, &p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// GetProject gets a specific project, identified by project ID or
+// NAMESPACE/PROJECT_NAME, which is owned by the authenticated user.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#get-single-project
+func (s *ProjectsService) GetProject(pid interface{}, options ...OptionFunc) (*Project, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(Project)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// ProjectEvent represents a GitLab project event.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#get-project-events
+type ProjectEvent struct {
+	Title          interface{} `json:"title"`
+	ProjectID      int         `json:"project_id"`
+	ActionName     string      `json:"action_name"`
+	TargetID       interface{} `json:"target_id"`
+	TargetType     interface{} `json:"target_type"`
+	AuthorID       int         `json:"author_id"`
+	AuthorUsername string      `json:"author_username"`
+	Data           struct {
+		Before            string      `json:"before"`
+		After             string      `json:"after"`
+		Ref               string      `json:"ref"`
+		UserID            int         `json:"user_id"`
+		UserName          string      `json:"user_name"`
+		Repository        *Repository `json:"repository"`
+		Commits           []*Commit   `json:"commits"`
+		TotalCommitsCount int         `json:"total_commits_count"`
+	} `json:"data"`
+	TargetTitle interface{} `json:"target_title"`
+}
+
+func (s ProjectEvent) String() string {
+	return Stringify(s)
+}
+
+// GetProjectEventsOptions represents the available GetProjectEvents() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#get-project-events
+type GetProjectEventsOptions ListOptions
+
+// GetProjectEvents gets the events for the specified project. Sorted from
+// newest to latest.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#get-project-events
+func (s *ProjectsService) GetProjectEvents(pid interface{}, opt *GetProjectEventsOptions, options ...OptionFunc) ([]*ProjectEvent, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/events", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var p []*ProjectEvent
+	resp, err := s.client.Do(req, &p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// CreateProjectOptions represents the available CreateProjects() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#create-project
+type CreateProjectOptions struct {
+	Name                                      *string           `url:"name,omitempty" json:"name,omitempty"`
+	Path                                      *string           `url:"path,omitempty" json:"path,omitempty"`
+	DefaultBranch                             *string           `url:"default_branch,omitempty" json:"default_branch,omitempty"`
+	NamespaceID                               *int              `url:"namespace_id,omitempty" json:"namespace_id,omitempty"`
+	Description                               *string           `url:"description,omitempty" json:"description,omitempty"`
+	IssuesEnabled                             *bool             `url:"issues_enabled,omitempty" json:"issues_enabled,omitempty"`
+	MergeRequestsEnabled                      *bool             `url:"merge_requests_enabled,omitempty" json:"merge_requests_enabled,omitempty"`
+	JobsEnabled                               *bool             `url:"jobs_enabled,omitempty" json:"jobs_enabled,omitempty"`
+	WikiEnabled                               *bool             `url:"wiki_enabled,omitempty" json:"wiki_enabled,omitempty"`
+	SnippetsEnabled                           *bool             `url:"snippets_enabled,omitempty" json:"snippets_enabled,omitempty"`
+	ResolveOutdatedDiffDiscussions            *bool             `url:"resolve_outdated_diff_discussions,omitempty" json:"resolve_outdated_diff_discussions,omitempty"`
+	ContainerRegistryEnabled                  *bool             `url:"container_registry_enabled,omitempty" json:"container_registry_enabled,omitempty"`
+	SharedRunnersEnabled                      *bool             `url:"shared_runners_enabled,omitempty" json:"shared_runners_enabled,omitempty"`
+	Visibility                                *VisibilityValue  `url:"visibility,omitempty" json:"visibility,omitempty"`
+	ImportURL                                 *string           `url:"import_url,omitempty" json:"import_url,omitempty"`
+	PublicJobs                                *bool             `url:"public_jobs,omitempty" json:"public_jobs,omitempty"`
+	OnlyAllowMergeIfPipelineSucceeds          *bool             `url:"only_allow_merge_if_pipeline_succeeds,omitempty" json:"only_allow_merge_if_pipeline_succeeds,omitempty"`
+	OnlyAllowMergeIfAllDiscussionsAreResolved *bool             `url:"only_allow_merge_if_all_discussions_are_resolved,omitempty" json:"only_allow_merge_if_all_discussions_are_resolved,omitempty"`
+	MergeMethod                               *MergeMethodValue `url:"merge_method,omitempty" json:"merge_method,omitempty"`
+	LFSEnabled                                *bool             `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"`
+	RequestAccessEnabled                      *bool             `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"`
+	TagList                                   *[]string         `url:"tag_list,omitempty" json:"tag_list,omitempty"`
+	PrintingMergeRequestLinkEnabled           *bool             `url:"printing_merge_request_link_enabled,omitempty" json:"printing_merge_request_link_enabled,omitempty"`
+	CIConfigPath                              *string           `url:"ci_config_path,omitempty" json:"ci_config_path,omitempty"`
+}
+
+// CreateProject creates a new project owned by the authenticated user.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#create-project
+func (s *ProjectsService) CreateProject(opt *CreateProjectOptions, options ...OptionFunc) (*Project, *Response, error) {
+	req, err := s.client.NewRequest("POST", "projects", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(Project)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// CreateProjectForUserOptions represents the available CreateProjectForUser()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#create-project-for-user
+type CreateProjectForUserOptions CreateProjectOptions
+
+// CreateProjectForUser creates a new project owned by the specified user.
+// Available only for admins.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#create-project-for-user
+func (s *ProjectsService) CreateProjectForUser(user int, opt *CreateProjectForUserOptions, options ...OptionFunc) (*Project, *Response, error) {
+	u := fmt.Sprintf("projects/user/%d", user)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(Project)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// EditProjectOptions represents the available EditProject() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#edit-project
+type EditProjectOptions CreateProjectOptions
+
+// EditProject updates an existing project.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#edit-project
+func (s *ProjectsService) EditProject(pid interface{}, opt *EditProjectOptions, options ...OptionFunc) (*Project, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(Project)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// ForkProject forks a project into the user namespace of the authenticated
+// user.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#fork-project
+func (s *ProjectsService) ForkProject(pid interface{}, options ...OptionFunc) (*Project, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/fork", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(Project)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// StarProject stars a given the project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#star-a-project
+func (s *ProjectsService) StarProject(pid interface{}, options ...OptionFunc) (*Project, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/star", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(Project)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// UnstarProject unstars a given project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#unstar-a-project
+func (s *ProjectsService) UnstarProject(pid interface{}, options ...OptionFunc) (*Project, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/unstar", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(Project)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// ArchiveProject archives the project if the user is either admin or the
+// project owner of this project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#archive-a-project
+func (s *ProjectsService) ArchiveProject(pid interface{}, options ...OptionFunc) (*Project, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/archive", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(Project)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// UnarchiveProject unarchives the project if the user is either admin or
+// the project owner of this project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#unarchive-a-project
+func (s *ProjectsService) UnarchiveProject(pid interface{}, options ...OptionFunc) (*Project, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/unarchive", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(Project)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// DeleteProject removes a project including all associated resources
+// (issues, merge requests etc.)
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#remove-project
+func (s *ProjectsService) DeleteProject(pid interface{}, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// ShareWithGroupOptions represents options to share project with groups
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#share-project-with-group
+type ShareWithGroupOptions struct {
+	GroupID     *int              `url:"group_id" json:"group_id"`
+	GroupAccess *AccessLevelValue `url:"group_access" json:"group_access"`
+	ExpiresAt   *string           `url:"expires_at" json:"expires_at"`
+}
+
+// ShareProjectWithGroup allows to share a project with a group.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#share-project-with-group
+func (s *ProjectsService) ShareProjectWithGroup(pid interface{}, opt *ShareWithGroupOptions, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/share", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// DeleteSharedProjectFromGroup allows to unshare a project from a group.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#delete-a-shared-project-link-within-a-group
+func (s *ProjectsService) DeleteSharedProjectFromGroup(pid interface{}, groupID int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/share/%d", url.QueryEscape(project), groupID)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// ProjectMember represents a project member.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#list-project-team-members
+type ProjectMember struct {
+	ID          int              `json:"id"`
+	Username    string           `json:"username"`
+	Email       string           `json:"email"`
+	Name        string           `json:"name"`
+	State       string           `json:"state"`
+	CreatedAt   *time.Time       `json:"created_at"`
+	AccessLevel AccessLevelValue `json:"access_level"`
+}
+
+// ProjectHook represents a project hook.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#list-project-hooks
+type ProjectHook struct {
+	ID                       int        `json:"id"`
+	URL                      string     `json:"url"`
+	ProjectID                int        `json:"project_id"`
+	PushEvents               bool       `json:"push_events"`
+	IssuesEvents             bool       `json:"issues_events"`
+	ConfidentialIssuesEvents bool       `json:"confidential_issues_events"`
+	MergeRequestsEvents      bool       `json:"merge_requests_events"`
+	TagPushEvents            bool       `json:"tag_push_events"`
+	NoteEvents               bool       `json:"note_events"`
+	JobEvents                bool       `json:"job_events"`
+	PipelineEvents           bool       `json:"pipeline_events"`
+	WikiPageEvents           bool       `json:"wiki_page_events"`
+	EnableSSLVerification    bool       `json:"enable_ssl_verification"`
+	CreatedAt                *time.Time `json:"created_at"`
+}
+
+// ListProjectHooksOptions represents the available ListProjectHooks() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#list-project-hooks
+type ListProjectHooksOptions ListOptions
+
+// ListProjectHooks gets a list of project hooks.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#list-project-hooks
+func (s *ProjectsService) ListProjectHooks(pid interface{}, opt *ListProjectHooksOptions, options ...OptionFunc) ([]*ProjectHook, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/hooks", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var ph []*ProjectHook
+	resp, err := s.client.Do(req, &ph)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ph, resp, err
+}
+
+// GetProjectHook gets a specific hook for a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#get-project-hook
+func (s *ProjectsService) GetProjectHook(pid interface{}, hook int, options ...OptionFunc) (*ProjectHook, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/hooks/%d", url.QueryEscape(project), hook)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ph := new(ProjectHook)
+	resp, err := s.client.Do(req, ph)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ph, resp, err
+}
+
+// AddProjectHookOptions represents the available AddProjectHook() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#add-project-hook
+type AddProjectHookOptions struct {
+	URL                      *string `url:"url,omitempty" json:"url,omitempty"`
+	PushEvents               *bool   `url:"push_events,omitempty" json:"push_events,omitempty"`
+	IssuesEvents             *bool   `url:"issues_events,omitempty" json:"issues_events,omitempty"`
+	ConfidentialIssuesEvents *bool   `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
+	MergeRequestsEvents      *bool   `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
+	TagPushEvents            *bool   `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"`
+	NoteEvents               *bool   `url:"note_events,omitempty" json:"note_events,omitempty"`
+	JobEvents                *bool   `url:"job_events,omitempty" json:"job_events,omitempty"`
+	PipelineEvents           *bool   `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"`
+	WikiPageEvents           *bool   `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"`
+	EnableSSLVerification    *bool   `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"`
+	Token                    *string `url:"token,omitempty" json:"token,omitempty"`
+}
+
+// AddProjectHook adds a hook to a specified project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#add-project-hook
+func (s *ProjectsService) AddProjectHook(pid interface{}, opt *AddProjectHookOptions, options ...OptionFunc) (*ProjectHook, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/hooks", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ph := new(ProjectHook)
+	resp, err := s.client.Do(req, ph)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ph, resp, err
+}
+
+// EditProjectHookOptions represents the available EditProjectHook() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#edit-project-hook
+type EditProjectHookOptions struct {
+	URL                      *string `url:"url,omitempty" json:"url,omitempty"`
+	PushEvents               *bool   `url:"push_events,omitempty" json:"push_events,omitempty"`
+	IssuesEvents             *bool   `url:"issues_events,omitempty" json:"issues_events,omitempty"`
+	ConfidentialIssuesEvents *bool   `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
+	MergeRequestsEvents      *bool   `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
+	TagPushEvents            *bool   `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"`
+	NoteEvents               *bool   `url:"note_events,omitempty" json:"note_events,omitempty"`
+	JobEvents                *bool   `url:"job_events,omitempty" json:"job_events,omitempty"`
+	PipelineEvents           *bool   `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"`
+	WikiPageEvents           *bool   `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"`
+	EnableSSLVerification    *bool   `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"`
+	Token                    *string `url:"token,omitempty" json:"token,omitempty"`
+}
+
+// EditProjectHook edits a hook for a specified project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#edit-project-hook
+func (s *ProjectsService) EditProjectHook(pid interface{}, hook int, opt *EditProjectHookOptions, options ...OptionFunc) (*ProjectHook, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/hooks/%d", url.QueryEscape(project), hook)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ph := new(ProjectHook)
+	resp, err := s.client.Do(req, ph)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ph, resp, err
+}
+
+// DeleteProjectHook removes a hook from a project. This is an idempotent
+// method and can be called multiple times. Either the hook is available or not.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#delete-project-hook
+func (s *ProjectsService) DeleteProjectHook(pid interface{}, hook int, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/hooks/%d", url.QueryEscape(project), hook)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// ProjectForkRelation represents a project fork relationship.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#admin-fork-relation
+type ProjectForkRelation struct {
+	ID                  int        `json:"id"`
+	ForkedToProjectID   int        `json:"forked_to_project_id"`
+	ForkedFromProjectID int        `json:"forked_from_project_id"`
+	CreatedAt           *time.Time `json:"created_at"`
+	UpdatedAt           *time.Time `json:"updated_at"`
+}
+
+// CreateProjectForkRelation creates a forked from/to relation between
+// existing projects.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#create-a-forked-fromto-relation-between-existing-projects.
+func (s *ProjectsService) CreateProjectForkRelation(pid int, fork int, options ...OptionFunc) (*ProjectForkRelation, *Response, error) {
+	u := fmt.Sprintf("projects/%d/fork/%d", pid, fork)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	pfr := new(ProjectForkRelation)
+	resp, err := s.client.Do(req, pfr)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pfr, resp, err
+}
+
+// DeleteProjectForkRelation deletes an existing forked from relationship.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#delete-an-existing-forked-from-relationship
+func (s *ProjectsService) DeleteProjectForkRelation(pid int, options ...OptionFunc) (*Response, error) {
+	u := fmt.Sprintf("projects/%d/fork", pid)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// ProjectFile represents an uploaded project file
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#upload-a-file
+type ProjectFile struct {
+	Alt      string `json:"alt"`
+	URL      string `json:"url"`
+	Markdown string `json:"markdown"`
+}
+
+// UploadFile upload a file from disk
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#upload-a-file
+func (s *ProjectsService) UploadFile(pid interface{}, file string, options ...OptionFunc) (*ProjectFile, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/uploads", url.QueryEscape(project))
+
+	f, err := os.Open(file)
+	if err != nil {
+		return nil, nil, err
+	}
+	defer f.Close()
+
+	b := &bytes.Buffer{}
+	w := multipart.NewWriter(b)
+
+	fw, err := w.CreateFormFile("file", file)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	_, err = io.Copy(fw, f)
+	if err != nil {
+		return nil, nil, err
+	}
+	w.Close()
+
+	req, err := s.client.NewRequest("", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	req.Body = ioutil.NopCloser(b)
+	req.ContentLength = int64(b.Len())
+	req.Header.Set("Content-Type", w.FormDataContentType())
+	req.Method = "POST"
+
+	uf := &ProjectFile{}
+	resp, err := s.client.Do(req, uf)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return uf, resp, nil
+}
+
+// ListProjectForks gets a list of project forks.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/projects.html#list-forks-of-a-project
+func (s *ProjectsService) ListProjectForks(pid interface{}, opt *ListProjectsOptions, options ...OptionFunc) ([]*Project, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/forks", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var forks []*Project
+	resp, err := s.client.Do(req, &forks)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return forks, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/protected_branches.go b/vendor/github.com/xanzy/go-gitlab/protected_branches.go
new file mode 100644
index 0000000..0a56241
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/protected_branches.go
@@ -0,0 +1,165 @@
+//
+// Copyright 2017, Sander van Harmelen, Michael Lihs
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+)
+
+// ProtectedBranchesService handles communication with the protected branch
+// related methods of the GitLab API.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/protected_branches.html#protected-branches-api
+type ProtectedBranchesService struct {
+	client *Client
+}
+
+// BranchAccessDescription represents the access description for a protected
+// branch.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/protected_branches.html#protected-branches-api
+type BranchAccessDescription struct {
+	AccessLevel            AccessLevelValue `json:"access_level"`
+	AccessLevelDescription string           `json:"access_level_description"`
+}
+
+// ProtectedBranch represents a protected branch.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/protected_branches.html#list-protected-branches
+type ProtectedBranch struct {
+	Name              string                     `json:"name"`
+	PushAccessLevels  []*BranchAccessDescription `json:"push_access_levels"`
+	MergeAccessLevels []*BranchAccessDescription `json:"merge_access_levels"`
+}
+
+// ListProtectedBranchesOptions represents the available ListProtectedBranches()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/protected_branches.html#list-protected-branches
+type ListProtectedBranchesOptions ListOptions
+
+// ListProtectedBranches gets a list of protected branches from a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/protected_branches.html#list-protected-branches
+func (s *ProtectedBranchesService) ListProtectedBranches(pid interface{}, opt *ListProtectedBranchesOptions, options ...OptionFunc) ([]*ProtectedBranch, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/protected_branches", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var p []*ProtectedBranch
+	resp, err := s.client.Do(req, &p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// GetProtectedBranch gets a single protected branch or wildcard protected branch.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/protected_branches.html#get-a-single-protected-branch-or-wildcard-protected-branch
+func (s *ProtectedBranchesService) GetProtectedBranch(pid interface{}, branch string, options ...OptionFunc) (*ProtectedBranch, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/protected_branches/%s", url.QueryEscape(project), branch)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(ProtectedBranch)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// ProtectRepositoryBranchesOptions represents the available
+// ProtectRepositoryBranches() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/protected_branches.html#protect-repository-branches
+type ProtectRepositoryBranchesOptions struct {
+	Name             *string           `url:"name,omitempty" json:"name,omitempty"`
+	PushAccessLevel  *AccessLevelValue `url:"push_access_level,omitempty" json:"push_access_level,omitempty"`
+	MergeAccessLevel *AccessLevelValue `url:"merge_access_level,omitempty" json:"merge_access_level,omitempty"`
+}
+
+// ProtectRepositoryBranches protects a single repository branch or several
+// project repository branches using a wildcard protected branch.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/protected_branches.html#protect-repository-branches
+func (s *ProtectedBranchesService) ProtectRepositoryBranches(pid interface{}, opt *ProtectRepositoryBranchesOptions, options ...OptionFunc) (*ProtectedBranch, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/protected_branches", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(ProtectedBranch)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// UnprotectRepositoryBranches unprotects the given protected branch or wildcard
+// protected branch.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/protected_branches.html#unprotect-repository-branches
+func (s *ProtectedBranchesService) UnprotectRepositoryBranches(pid interface{}, branch string, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/protected_branches/%s", url.QueryEscape(project), branch)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/repositories.go b/vendor/github.com/xanzy/go-gitlab/repositories.go
new file mode 100644
index 0000000..7d10997
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/repositories.go
@@ -0,0 +1,260 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"bytes"
+	"fmt"
+	"net/url"
+)
+
+// RepositoriesService handles communication with the repositories related
+// methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html
+type RepositoriesService struct {
+	client *Client
+}
+
+// TreeNode represents a GitLab repository file or directory.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html
+type TreeNode struct {
+	ID   string `json:"id"`
+	Name string `json:"name"`
+	Type string `json:"type"`
+	Path string `json:"path"`
+	Mode string `json:"mode"`
+}
+
+func (t TreeNode) String() string {
+	return Stringify(t)
+}
+
+// ListTreeOptions represents the available ListTree() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repositories.html#list-repository-tree
+type ListTreeOptions struct {
+	ListOptions
+	Path      *string `url:"path,omitempty" json:"path,omitempty"`
+	Ref       *string `url:"ref,omitempty" json:"ref,omitempty"`
+	Recursive *bool   `url:"recursive,omitempty" json:"recursive,omitempty"`
+}
+
+// ListTree gets a list of repository files and directories in a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repositories.html#list-repository-tree
+func (s *RepositoriesService) ListTree(pid interface{}, opt *ListTreeOptions, options ...OptionFunc) ([]*TreeNode, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/tree", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var t []*TreeNode
+	resp, err := s.client.Do(req, &t)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return t, resp, err
+}
+
+// RawFileContent gets the raw file contents for a file by commit SHA and path
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repositories.html#raw-file-content
+func (s *RepositoriesService) RawFileContent(pid interface{}, sha string, options ...OptionFunc) ([]byte, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/blobs/%s", url.QueryEscape(project), sha)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var b bytes.Buffer
+	resp, err := s.client.Do(req, &b)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return b.Bytes(), resp, err
+}
+
+// RawBlobContent gets the raw file contents for a blob by blob SHA.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repositories.html#raw-blob-content
+func (s *RepositoriesService) RawBlobContent(pid interface{}, sha string, options ...OptionFunc) ([]byte, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/blobs/%s/raw", url.QueryEscape(project), sha)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var b bytes.Buffer
+	resp, err := s.client.Do(req, &b)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return b.Bytes(), resp, err
+}
+
+// ArchiveOptions represents the available Archive() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repositories.html#get-file-archive
+type ArchiveOptions struct {
+	SHA *string `url:"sha,omitempty" json:"sha,omitempty"`
+}
+
+// Archive gets an archive of the repository.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repositories.html#get-file-archive
+func (s *RepositoriesService) Archive(pid interface{}, opt *ArchiveOptions, options ...OptionFunc) ([]byte, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/archive", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var b bytes.Buffer
+	resp, err := s.client.Do(req, &b)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return b.Bytes(), resp, err
+}
+
+// Compare represents the result of a comparison of branches, tags or commits.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
+type Compare struct {
+	Commit         *Commit   `json:"commit"`
+	Commits        []*Commit `json:"commits"`
+	Diffs          []*Diff   `json:"diffs"`
+	CompareTimeout bool      `json:"compare_timeout"`
+	CompareSameRef bool      `json:"compare_same_ref"`
+}
+
+func (c Compare) String() string {
+	return Stringify(c)
+}
+
+// CompareOptions represents the available Compare() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
+type CompareOptions struct {
+	From *string `url:"from,omitempty" json:"from,omitempty"`
+	To   *string `url:"to,omitempty" json:"to,omitempty"`
+}
+
+// Compare compares branches, tags or commits.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repositories.html#compare-branches-tags-or-commits
+func (s *RepositoriesService) Compare(pid interface{}, opt *CompareOptions, options ...OptionFunc) (*Compare, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/compare", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	c := new(Compare)
+	resp, err := s.client.Do(req, c)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return c, resp, err
+}
+
+// Contributor represents a GitLap contributor.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
+type Contributor struct {
+	Name      string `json:"name,omitempty"`
+	Email     string `json:"email,omitempty"`
+	Commits   int    `json:"commits,omitempty"`
+	Additions int    `json:"additions,omitempty"`
+	Deletions int    `json:"deletions,omitempty"`
+}
+
+func (c Contributor) String() string {
+	return Stringify(c)
+}
+
+// ListContributorsOptions represents the available ListContributorsOptions()
+// options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
+type ListContributorsOptions ListOptions
+
+// Contributors gets the repository contributors list.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/repositories.html#contributors
+func (s *RepositoriesService) Contributors(pid interface{}, opt *ListContributorsOptions, options ...OptionFunc) ([]*Contributor, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/contributors", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var c []*Contributor
+	resp, err := s.client.Do(req, &c)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return c, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/repository_files.go b/vendor/github.com/xanzy/go-gitlab/repository_files.go
new file mode 100644
index 0000000..473128a
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/repository_files.go
@@ -0,0 +1,235 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"bytes"
+	"fmt"
+	"net/url"
+)
+
+// RepositoryFilesService handles communication with the repository files
+// related methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/repository_files.html
+type RepositoryFilesService struct {
+	client *Client
+}
+
+// File represents a GitLab repository file.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/repository_files.html
+type File struct {
+	FileName string `json:"file_name"`
+	FilePath string `json:"file_path"`
+	Size     int    `json:"size"`
+	Encoding string `json:"encoding"`
+	Content  string `json:"content"`
+	Ref      string `json:"ref"`
+	BlobID   string `json:"blob_id"`
+	CommitID string `json:"commit_id"`
+}
+
+func (r File) String() string {
+	return Stringify(r)
+}
+
+// GetFileOptions represents the available GetFile() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repository_files.html#get-file-from-repository
+type GetFileOptions struct {
+	Ref *string `url:"ref,omitempty" json:"ref,omitempty"`
+}
+
+// GetFile allows you to receive information about a file in repository like
+// name, size, content. Note that file content is Base64 encoded.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repository_files.html#get-file-from-repository
+func (s *RepositoryFilesService) GetFile(pid interface{}, fileName string, opt *GetFileOptions, options ...OptionFunc) (*File, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/files/%s", url.QueryEscape(project), url.QueryEscape(fileName))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	f := new(File)
+	resp, err := s.client.Do(req, f)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return f, resp, err
+}
+
+// GetRawFileOptions represents the available GetRawFile() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repository_files.html#get-raw-file-from-repository
+type GetRawFileOptions struct {
+	Ref *string `url:"ref,omitempty" json:"ref,omitempty"`
+}
+
+// GetRawFile allows you to receive the raw file in repository.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repository_files.html#get-raw-file-from-repository
+func (s *RepositoryFilesService) GetRawFile(pid interface{}, fileName string, opt *GetRawFileOptions, options ...OptionFunc) ([]byte, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/files/%s/raw", url.QueryEscape(project), url.QueryEscape(fileName))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var f bytes.Buffer
+	resp, err := s.client.Do(req, &f)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return f.Bytes(), resp, err
+}
+
+// FileInfo represents file details of a GitLab repository file.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/repository_files.html
+type FileInfo struct {
+	FilePath string `json:"file_path"`
+	Branch   string `json:"branch"`
+}
+
+func (r FileInfo) String() string {
+	return Stringify(r)
+}
+
+// CreateFileOptions represents the available CreateFile() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repository_files.html#create-new-file-in-repository
+type CreateFileOptions struct {
+	Branch        *string `url:"branch,omitempty" json:"branch,omitempty"`
+	Encoding      *string `url:"encoding,omitempty" json:"encoding,omitempty"`
+	AuthorEmail   *string `url:"author_email,omitempty" json:"author_email,omitempty"`
+	AuthorName    *string `url:"author_name,omitempty" json:"author_name,omitempty"`
+	Content       *string `url:"content,omitempty" json:"content,omitempty"`
+	CommitMessage *string `url:"commit_message,omitempty" json:"commit_message,omitempty"`
+}
+
+// CreateFile creates a new file in a repository.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repository_files.html#create-new-file-in-repository
+func (s *RepositoryFilesService) CreateFile(pid interface{}, fileName string, opt *CreateFileOptions, options ...OptionFunc) (*FileInfo, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/files/%s", url.QueryEscape(project), url.QueryEscape(fileName))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	f := new(FileInfo)
+	resp, err := s.client.Do(req, f)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return f, resp, err
+}
+
+// UpdateFileOptions represents the available UpdateFile() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repository_files.html#update-existing-file-in-repository
+type UpdateFileOptions struct {
+	Branch        *string `url:"branch,omitempty" json:"branch,omitempty"`
+	Encoding      *string `url:"encoding,omitempty" json:"encoding,omitempty"`
+	AuthorEmail   *string `url:"author_email,omitempty" json:"author_email,omitempty"`
+	AuthorName    *string `url:"author_name,omitempty" json:"author_name,omitempty"`
+	Content       *string `url:"content,omitempty" json:"content,omitempty"`
+	CommitMessage *string `url:"commit_message,omitempty" json:"commit_message,omitempty"`
+	LastCommitID  *string `url:"last_commit_id,omitempty" json:"last_commit_id,omitempty"`
+}
+
+// UpdateFile updates an existing file in a repository
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repository_files.html#update-existing-file-in-repository
+func (s *RepositoryFilesService) UpdateFile(pid interface{}, fileName string, opt *UpdateFileOptions, options ...OptionFunc) (*FileInfo, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/files/%s", url.QueryEscape(project), url.QueryEscape(fileName))
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	f := new(FileInfo)
+	resp, err := s.client.Do(req, f)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return f, resp, err
+}
+
+// DeleteFileOptions represents the available DeleteFile() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repository_files.html#delete-existing-file-in-repository
+type DeleteFileOptions struct {
+	Branch        *string `url:"branch,omitempty" json:"branch,omitempty"`
+	AuthorEmail   *string `url:"author_email,omitempty" json:"author_email,omitempty"`
+	AuthorName    *string `url:"author_name,omitempty" json:"author_name,omitempty"`
+	CommitMessage *string `url:"commit_message,omitempty" json:"commit_message,omitempty"`
+}
+
+// DeleteFile deletes an existing file in a repository
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/repository_files.html#delete-existing-file-in-repository
+func (s *RepositoryFilesService) DeleteFile(pid interface{}, fileName string, opt *DeleteFileOptions, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/files/%s", url.QueryEscape(project), url.QueryEscape(fileName))
+
+	req, err := s.client.NewRequest("DELETE", u, opt, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/runners.go b/vendor/github.com/xanzy/go-gitlab/runners.go
new file mode 100644
index 0000000..4ffef03
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/runners.go
@@ -0,0 +1,326 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// RunnersService handles communication with the runner related methods of the
+// GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/runners.html
+type RunnersService struct {
+	client *Client
+}
+
+// Runner represents a GitLab CI Runner.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/runners.html
+type Runner struct {
+	ID          int    `json:"id"`
+	Description string `json:"description"`
+	Active      bool   `json:"active"`
+	IsShared    bool   `json:"is_shared"`
+	Name        string `json:"name"`
+	Online      bool   `json:"online"`
+	Status      string `json:"status"`
+}
+
+// RunnerDetails represents the GitLab CI runner details.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/runners.html
+type RunnerDetails struct {
+	Active       bool       `json:"active"`
+	Architecture string     `json:"architecture"`
+	Description  string     `json:"description"`
+	ID           int        `json:"id"`
+	IsShared     bool       `json:"is_shared"`
+	ContactedAt  *time.Time `json:"contacted_at,omitempty"`
+	Name         string     `json:"name"`
+	Online       bool       `json:"online"`
+	Status       string     `json:"status"`
+	Platform     string     `json:"platform,omitempty"`
+	Projects     []struct {
+		ID                int    `json:"id"`
+		Name              string `json:"name"`
+		NameWithNamespace string `json:"name_with_namespace"`
+		Path              string `json:"path"`
+		PathWithNamespace string `json:"path_with_namespace"`
+	} `json:"projects"`
+	Token       string   `json:"Token"`
+	Revision    string   `json:"revision,omitempty"`
+	TagList     []string `json:"tag_list"`
+	Version     string   `json:"version,omitempty"`
+	AccessLevel string   `json:"access_level"`
+}
+
+// ListRunnersOptions represents the available ListRunners() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/runners.html#list-owned-runners
+type ListRunnersOptions struct {
+	ListOptions
+	Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
+}
+
+// ListRunners gets a list of runners accessible by the authenticated user.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/runners.html#list-owned-runners
+func (s *RunnersService) ListRunners(opt *ListRunnersOptions, options ...OptionFunc) ([]*Runner, *Response, error) {
+	req, err := s.client.NewRequest("GET", "runners", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var rs []*Runner
+	resp, err := s.client.Do(req, &rs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return rs, resp, err
+}
+
+// ListAllRunners gets a list of all runners in the GitLab instance. Access is
+// restricted to users with admin privileges.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/runners.html#list-all-runners
+func (s *RunnersService) ListAllRunners(opt *ListRunnersOptions, options ...OptionFunc) ([]*Runner, *Response, error) {
+	req, err := s.client.NewRequest("GET", "runners/all", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var rs []*Runner
+	resp, err := s.client.Do(req, &rs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return rs, resp, err
+}
+
+// GetRunnerDetails returns details for given runner.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/runners.html#get-runner-39-s-details
+func (s *RunnersService) GetRunnerDetails(rid interface{}, options ...OptionFunc) (*RunnerDetails, *Response, error) {
+	runner, err := parseID(rid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("runners/%s", runner)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var rs *RunnerDetails
+	resp, err := s.client.Do(req, &rs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return rs, resp, err
+}
+
+// UpdateRunnerDetailsOptions represents the available UpdateRunnerDetails() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/runners.html#update-runner-39-s-details
+type UpdateRunnerDetailsOptions struct {
+	Description *string  `url:"description,omitempty" json:"description,omitempty"`
+	Active      *bool    `url:"active,omitempty" json:"active,omitempty"`
+	TagList     []string `url:"tag_list[],omitempty" json:"tag_list,omitempty"`
+	RunUntagged *bool    `url:"run_untagged,omitempty" json:"run_untagged,omitempty"`
+	Locked      *bool    `url:"locked,omitempty" json:"locked,omitempty"`
+	AccessLevel *string  `url:"access_level,omitempty" json:"access_level,omitempty"`
+}
+
+// UpdateRunnerDetails updates details for a given runner.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/runners.html#update-runner-39-s-details
+func (s *RunnersService) UpdateRunnerDetails(rid interface{}, opt *UpdateRunnerDetailsOptions, options ...OptionFunc) (*RunnerDetails, *Response, error) {
+	runner, err := parseID(rid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("runners/%s", runner)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var rs *RunnerDetails
+	resp, err := s.client.Do(req, &rs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return rs, resp, err
+}
+
+// RemoveRunner removes a runner.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/runners.html#remove-a-runner
+func (s *RunnersService) RemoveRunner(rid interface{}, options ...OptionFunc) (*Response, error) {
+	runner, err := parseID(rid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("runners/%s", runner)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// ListRunnerJobsOptions represents the available ListRunnerJobs()
+// options. Status can be one of: running, success, failed, canceled.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/runners.html#list-runner-39-s-jobs
+type ListRunnerJobsOptions struct {
+	ListOptions
+	Status *string `url:"status,omitempty" json:"status,omitempty"`
+}
+
+// ListRunnerJobs gets a list of jobs that are being processed or were processed by specified Runner.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/runners.html#list-runner-39-s-jobs
+func (s *RunnersService) ListRunnerJobs(rid interface{}, opt *ListRunnerJobsOptions, options ...OptionFunc) ([]*Job, *Response, error) {
+	runner, err := parseID(rid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("runners/%s/jobs", runner)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var rs []*Job
+	resp, err := s.client.Do(req, &rs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return rs, resp, err
+}
+
+// ListProjectRunnersOptions represents the available ListProjectRunners()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/runners.html#list-project-s-runners
+type ListProjectRunnersOptions ListRunnersOptions
+
+// ListProjectRunners gets a list of runners accessible by the authenticated user.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/runners.html#list-project-s-runners
+func (s *RunnersService) ListProjectRunners(pid interface{}, opt *ListProjectRunnersOptions, options ...OptionFunc) ([]*Runner, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/runners", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var rs []*Runner
+	resp, err := s.client.Do(req, &rs)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return rs, resp, err
+}
+
+// EnableProjectRunnerOptions represents the available EnableProjectRunner()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/runners.html#enable-a-runner-in-project
+type EnableProjectRunnerOptions struct {
+	RunnerID int `json:"runner_id"`
+}
+
+// EnableProjectRunner enables an available specific runner in the project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/runners.html#enable-a-runner-in-project
+func (s *RunnersService) EnableProjectRunner(pid interface{}, opt *EnableProjectRunnerOptions, options ...OptionFunc) (*Runner, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/runners", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var r *Runner
+	resp, err := s.client.Do(req, &r)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return r, resp, err
+}
+
+// DisableProjectRunner disables a specific runner from project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/runners.html#disable-a-runner-from-project
+func (s *RunnersService) DisableProjectRunner(pid interface{}, rid interface{}, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	runner, err := parseID(rid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/runners/%s", url.QueryEscape(project), url.QueryEscape(runner))
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/search.go b/vendor/github.com/xanzy/go-gitlab/search.go
new file mode 100644
index 0000000..99c2f02
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/search.go
@@ -0,0 +1,326 @@
+//
+// Copyright 2018, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+)
+
+// SearchService handles communication with the search related methods of the
+// GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/search.html
+type SearchService struct {
+	client *Client
+}
+
+// SearchOptions represents the available options for all search methods.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/search.html
+type SearchOptions ListOptions
+
+type searchOptions struct {
+	SearchOptions
+	Scope  string `url:"scope" json:"scope"`
+	Search string `url:"search" json:"search"`
+}
+
+// Projects searches the expression within projects
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/search.html#scope-projects
+func (s *SearchService) Projects(query string, opt *SearchOptions, options ...OptionFunc) ([]*Project, *Response, error) {
+	var ps []*Project
+	resp, err := s.search("projects", query, &ps, opt, options...)
+	return ps, resp, err
+}
+
+// ProjectsByGroup searches the expression within projects for
+// the specified group
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/search.html#group-search-api
+func (s *SearchService) ProjectsByGroup(gid interface{}, query string, opt *SearchOptions, options ...OptionFunc) ([]*Project, *Response, error) {
+	var ps []*Project
+	resp, err := s.searchByGroup(gid, "projects", query, &ps, opt, options...)
+	return ps, resp, err
+}
+
+// Issues searches the expression within issues
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/search.html#scope-issues
+func (s *SearchService) Issues(query string, opt *SearchOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
+	var is []*Issue
+	resp, err := s.search("issues", query, &is, opt, options...)
+	return is, resp, err
+}
+
+// IssuesByGroup searches the expression within issues for
+// the specified group
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/search.html#scope-issues
+func (s *SearchService) IssuesByGroup(gid interface{}, query string, opt *SearchOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
+	var is []*Issue
+	resp, err := s.searchByGroup(gid, "issues", query, &is, opt, options...)
+	return is, resp, err
+}
+
+// IssuesByProject searches the expression within issues for
+// the specified project
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/search.html#scope-issues
+func (s *SearchService) IssuesByProject(pid interface{}, query string, opt *SearchOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
+	var is []*Issue
+	resp, err := s.searchByProject(pid, "issues", query, &is, opt, options...)
+	return is, resp, err
+}
+
+// MergeRequests searches the expression within merge requests
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/search.html#scope-merge_requests
+func (s *SearchService) MergeRequests(query string, opt *SearchOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
+	var ms []*MergeRequest
+	resp, err := s.search("merge_requests", query, &ms, opt, options...)
+	return ms, resp, err
+}
+
+// MergeRequestsByGroup searches the expression within merge requests for
+// the specified group
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/search.html#scope-merge_requests
+func (s *SearchService) MergeRequestsByGroup(gid interface{}, query string, opt *SearchOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
+	var ms []*MergeRequest
+	resp, err := s.searchByGroup(gid, "merge_requests", query, &ms, opt, options...)
+	return ms, resp, err
+}
+
+// MergeRequestsByProject searches the expression within merge requests for
+// the specified project
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/search.html#scope-merge_requests
+func (s *SearchService) MergeRequestsByProject(pid interface{}, query string, opt *SearchOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
+	var ms []*MergeRequest
+	resp, err := s.searchByProject(pid, "merge_requests", query, &ms, opt, options...)
+	return ms, resp, err
+}
+
+// Milestones searches the expression within milestones
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/search.html#scope-milestones
+func (s *SearchService) Milestones(query string, opt *SearchOptions, options ...OptionFunc) ([]*Milestone, *Response, error) {
+	var ms []*Milestone
+	resp, err := s.search("milestones", query, &ms, opt, options...)
+	return ms, resp, err
+}
+
+// MilestonesByGroup searches the expression within milestones for
+// the specified group
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/search.html#scope-milestones
+func (s *SearchService) MilestonesByGroup(gid interface{}, query string, opt *SearchOptions, options ...OptionFunc) ([]*Milestone, *Response, error) {
+	var ms []*Milestone
+	resp, err := s.searchByGroup(gid, "milestones", query, &ms, opt, options...)
+	return ms, resp, err
+}
+
+// MilestonesByProject searches the expression within milestones for
+// the specified project
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/search.html#scope-milestones
+func (s *SearchService) MilestonesByProject(pid interface{}, query string, opt *SearchOptions, options ...OptionFunc) ([]*Milestone, *Response, error) {
+	var ms []*Milestone
+	resp, err := s.searchByProject(pid, "milestones", query, &ms, opt, options...)
+	return ms, resp, err
+}
+
+// SnippetTitles searches the expression within snippet titles
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/search.html#scope-snippet_titles
+func (s *SearchService) SnippetTitles(query string, opt *SearchOptions, options ...OptionFunc) ([]*Snippet, *Response, error) {
+	var ss []*Snippet
+	resp, err := s.search("snippet_titles", query, &ss, opt, options...)
+	return ss, resp, err
+}
+
+// SnippetBlobs searches the expression within snippet blobs
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/search.html#scope-snippet_blobs
+func (s *SearchService) SnippetBlobs(query string, opt *SearchOptions, options ...OptionFunc) ([]*Snippet, *Response, error) {
+	var ss []*Snippet
+	resp, err := s.search("snippet_blobs", query, &ss, opt, options...)
+	return ss, resp, err
+}
+
+// NotesByProject searches the expression within notes for the specified
+// project
+//
+// GitLab API docs: // https://docs.gitlab.com/ce/api/search.html#scope-notes
+func (s *SearchService) NotesByProject(pid interface{}, query string, opt *SearchOptions, options ...OptionFunc) ([]*Note, *Response, error) {
+	var ns []*Note
+	resp, err := s.searchByProject(pid, "notes", query, &ns, opt, options...)
+	return ns, resp, err
+}
+
+// WikiBlobs searches the expression within all wiki blobs
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/search.html#scope-wiki_blobs
+func (s *SearchService) WikiBlobs(query string, opt *SearchOptions, options ...OptionFunc) ([]*Wiki, *Response, error) {
+	var ws []*Wiki
+	resp, err := s.search("wiki_blobs", query, &ws, opt, options...)
+	return ws, resp, err
+}
+
+// WikiBlobsByGroup searches the expression within wiki blobs for
+// specified group
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/search.html#scope-wiki_blobs
+func (s *SearchService) WikiBlobsByGroup(gid interface{}, query string, opt *SearchOptions, options ...OptionFunc) ([]*Wiki, *Response, error) {
+	var ws []*Wiki
+	resp, err := s.searchByGroup(gid, "wiki_blobs", query, &ws, opt, options...)
+	return ws, resp, err
+}
+
+// WikiBlobsByProject searches the expression within wiki blobs for
+// the specified project
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/search.html#scope-wiki_blobs
+func (s *SearchService) WikiBlobsByProject(pid interface{}, query string, opt *SearchOptions, options ...OptionFunc) ([]*Wiki, *Response, error) {
+	var ws []*Wiki
+	resp, err := s.searchByProject(pid, "wiki_blobs", query, &ws, opt, options...)
+	return ws, resp, err
+}
+
+// Commits searches the expression within all commits
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/search.html#scope-commits
+func (s *SearchService) Commits(query string, opt *SearchOptions, options ...OptionFunc) ([]*Commit, *Response, error) {
+	var cs []*Commit
+	resp, err := s.search("commits", query, &cs, opt, options...)
+	return cs, resp, err
+}
+
+// CommitsByGroup searches the expression within commits for the specified
+// group
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/search.html#scope-commits
+func (s *SearchService) CommitsByGroup(gid interface{}, query string, opt *SearchOptions, options ...OptionFunc) ([]*Commit, *Response, error) {
+	var cs []*Commit
+	resp, err := s.searchByGroup(gid, "commits", query, &cs, opt, options...)
+	return cs, resp, err
+}
+
+// CommitsByProject searches the expression within commits for the
+// specified project
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/search.html#scope-commits
+func (s *SearchService) CommitsByProject(pid interface{}, query string, opt *SearchOptions, options ...OptionFunc) ([]*Commit, *Response, error) {
+	var cs []*Commit
+	resp, err := s.searchByProject(pid, "commits", query, &cs, opt, options...)
+	return cs, resp, err
+}
+
+// Blob represents a single blob.
+type Blob struct {
+	Basename  string `json:"basename"`
+	Data      string `json:"data"`
+	Filename  string `json:"filename"`
+	ID        int    `json:"id"`
+	Ref       string `json:"ref"`
+	Startline int    `json:"startline"`
+	ProjectID int    `json:"project_id"`
+}
+
+// Blobs searches the expression within all blobs
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/search.html#scope-blobs
+func (s *SearchService) Blobs(query string, opt *SearchOptions, options ...OptionFunc) ([]*Blob, *Response, error) {
+	var bs []*Blob
+	resp, err := s.search("blobs", query, &bs, opt, options...)
+	return bs, resp, err
+}
+
+// BlobsByGroup searches the expression within blobs for the specified
+// group
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/search.html#scope-blobs
+func (s *SearchService) BlobsByGroup(gid interface{}, query string, opt *SearchOptions, options ...OptionFunc) ([]*Blob, *Response, error) {
+	var bs []*Blob
+	resp, err := s.searchByGroup(gid, "blobs", query, &bs, opt, options...)
+	return bs, resp, err
+}
+
+// BlobsByProject searches the expression within blobs for the specified
+// project
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/search.html#scope-blobs
+func (s *SearchService) BlobsByProject(pid interface{}, query string, opt *SearchOptions, options ...OptionFunc) ([]*Blob, *Response, error) {
+	var bs []*Blob
+	resp, err := s.searchByProject(pid, "blobs", query, &bs, opt, options...)
+	return bs, resp, err
+}
+
+func (s *SearchService) search(scope, query string, result interface{}, opt *SearchOptions, options ...OptionFunc) (*Response, error) {
+	opts := &searchOptions{SearchOptions: *opt, Scope: scope, Search: query}
+
+	req, err := s.client.NewRequest("GET", "search", opts, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, result)
+}
+
+func (s *SearchService) searchByGroup(gid interface{}, scope, query string, result interface{}, opt *SearchOptions, options ...OptionFunc) (*Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("groups/%s/-/search", url.QueryEscape(group))
+
+	opts := &searchOptions{SearchOptions: *opt, Scope: scope, Search: query}
+
+	req, err := s.client.NewRequest("GET", u, opts, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, result)
+}
+
+func (s *SearchService) searchByProject(pid interface{}, scope, query string, result interface{}, opt *SearchOptions, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/-/search", url.QueryEscape(project))
+
+	opts := &searchOptions{SearchOptions: *opt, Scope: scope, Search: query}
+
+	req, err := s.client.NewRequest("GET", u, opts, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, result)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/services.go b/vendor/github.com/xanzy/go-gitlab/services.go
new file mode 100644
index 0000000..e93ae60
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/services.go
@@ -0,0 +1,515 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// ServicesService handles communication with the services related methods of
+// the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/services.html
+type ServicesService struct {
+	client *Client
+}
+
+// Service represents a GitLab service.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/services.html
+type Service struct {
+	ID                       int        `json:"id"`
+	Title                    string     `json:"title"`
+	CreatedAt                *time.Time `json:"created_at"`
+	UpdatedAt                *time.Time `json:"updated_at"`
+	Active                   bool       `json:"active"`
+	PushEvents               bool       `json:"push_events"`
+	IssuesEvents             bool       `json:"issues_events"`
+	ConfidentialIssuesEvents bool       `json:"confidential_issues_events"`
+	MergeRequestsEvents      bool       `json:"merge_requests_events"`
+	TagPushEvents            bool       `json:"tag_push_events"`
+	NoteEvents               bool       `json:"note_events"`
+	PipelineEvents           bool       `json:"pipeline_events"`
+	JobEvents                bool       `json:"job_events"`
+}
+
+// SetGitLabCIServiceOptions represents the available SetGitLabCIService()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#edit-gitlab-ci-service
+type SetGitLabCIServiceOptions struct {
+	Token      *string `url:"token,omitempty" json:"token,omitempty"`
+	ProjectURL *string `url:"project_url,omitempty" json:"project_url,omitempty"`
+}
+
+// SetGitLabCIService sets GitLab CI service for a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#edit-gitlab-ci-service
+func (s *ServicesService) SetGitLabCIService(pid interface{}, opt *SetGitLabCIServiceOptions, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/gitlab-ci", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// DeleteGitLabCIService deletes GitLab CI service settings for a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#delete-gitlab-ci-service
+func (s *ServicesService) DeleteGitLabCIService(pid interface{}, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/gitlab-ci", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// SetHipChatServiceOptions represents the available SetHipChatService()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#edit-hipchat-service
+type SetHipChatServiceOptions struct {
+	Token *string `url:"token,omitempty" json:"token,omitempty" `
+	Room  *string `url:"room,omitempty" json:"room,omitempty"`
+}
+
+// SetHipChatService sets HipChat service for a project
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#edit-hipchat-service
+func (s *ServicesService) SetHipChatService(pid interface{}, opt *SetHipChatServiceOptions, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/hipchat", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// DeleteHipChatService deletes HipChat service for project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#delete-hipchat-service
+func (s *ServicesService) DeleteHipChatService(pid interface{}, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/hipchat", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// DroneCIService represents Drone CI service settings.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#drone-ci
+type DroneCIService struct {
+	Service
+	Properties *DroneCIServiceProperties `json:"properties"`
+}
+
+// DroneCIServiceProperties represents Drone CI specific properties.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#drone-ci
+type DroneCIServiceProperties struct {
+	Token                 string `json:"token"`
+	DroneURL              string `json:"drone_url"`
+	EnableSSLVerification bool   `json:"enable_ssl_verification"`
+}
+
+// GetDroneCIService gets Drone CI service settings for a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#get-drone-ci-service-settings
+func (s *ServicesService) GetDroneCIService(pid interface{}, options ...OptionFunc) (*DroneCIService, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/drone-ci", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	svc := new(DroneCIService)
+	resp, err := s.client.Do(req, svc)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return svc, resp, err
+}
+
+// SetDroneCIServiceOptions represents the available SetDroneCIService()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#createedit-drone-ci-service
+type SetDroneCIServiceOptions struct {
+	Token                 *string `url:"token" json:"token" `
+	DroneURL              *string `url:"drone_url" json:"drone_url"`
+	EnableSSLVerification *bool   `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"`
+}
+
+// SetDroneCIService sets Drone CI service for a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#createedit-drone-ci-service
+func (s *ServicesService) SetDroneCIService(pid interface{}, opt *SetDroneCIServiceOptions, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/drone-ci", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// DeleteDroneCIService deletes Drone CI service settings for a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#delete-drone-ci-service
+func (s *ServicesService) DeleteDroneCIService(pid interface{}, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/drone-ci", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// SlackService represents Slack service settings.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#slack
+type SlackService struct {
+	Service
+	Properties *SlackServiceProperties `json:"properties"`
+}
+
+// SlackServiceProperties represents Slack specific properties.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#slack
+type SlackServiceProperties struct {
+	NotifyOnlyBrokenPipelines bool `json:"notify_only_broken_pipelines"`
+}
+
+// GetSlackService gets Slack service settings for a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#get-slack-service-settings
+func (s *ServicesService) GetSlackService(pid interface{}, options ...OptionFunc) (*SlackService, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/slack", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	svc := new(SlackService)
+	resp, err := s.client.Do(req, svc)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return svc, resp, err
+}
+
+// SetSlackServiceOptions represents the available SetSlackService()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#edit-slack-service
+type SetSlackServiceOptions struct {
+	WebHook  *string `url:"webhook,omitempty" json:"webhook,omitempty" `
+	Username *string `url:"username,omitempty" json:"username,omitempty" `
+	Channel  *string `url:"channel,omitempty" json:"channel,omitempty"`
+}
+
+// SetSlackService sets Slack service for a project
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#edit-slack-service
+func (s *ServicesService) SetSlackService(pid interface{}, opt *SetSlackServiceOptions, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/slack", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// DeleteSlackService deletes Slack service for project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#delete-slack-service
+func (s *ServicesService) DeleteSlackService(pid interface{}, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/slack", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// JiraService represents Jira service settings.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#jira
+type JiraService struct {
+	Service
+	Properties *JiraServiceProperties `json:"properties"`
+}
+
+// JiraServiceProperties represents Jira specific properties.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#jira
+type JiraServiceProperties struct {
+	URL                   *string `url:"url,omitempty" json:"url,omitempty"`
+	ProjectKey            *string `url:"project_key,omitempty" json:"project_key,omitempty" `
+	Username              *string `url:"username,omitempty" json:"username,omitempty" `
+	Password              *string `url:"password,omitempty" json:"password,omitempty" `
+	JiraIssueTransitionID *string `url:"jira_issue_transition_id,omitempty" json:"jira_issue_transition_id,omitempty"`
+}
+
+// GetJiraService gets Jira service settings for a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#get-jira-service-settings
+func (s *ServicesService) GetJiraService(pid interface{}, options ...OptionFunc) (*JiraService, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/jira", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	svc := new(JiraService)
+	resp, err := s.client.Do(req, svc)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return svc, resp, err
+}
+
+// SetJiraServiceOptions represents the available SetJiraService()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#edit-jira-service
+type SetJiraServiceOptions JiraServiceProperties
+
+// SetJiraService sets Jira service for a project
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#edit-jira-service
+func (s *ServicesService) SetJiraService(pid interface{}, opt *SetJiraServiceOptions, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/jira", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// DeleteJiraService deletes Jira service for project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#delete-jira-service
+func (s *ServicesService) DeleteJiraService(pid interface{}, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/jira", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// JenkinsCIService represents Jenkins CI service settings.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/services.html#jenkins-ci
+type JenkinsCIService struct {
+	Service
+	Properties *JenkinsCIServiceProperties `json:"properties"`
+}
+
+// JenkinsCIServiceProperties represents Jenkins CI specific properties.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/services.html#jenkins-ci
+type JenkinsCIServiceProperties struct {
+	URL         *string `url:"jenkins_url,omitempty" json:"jenkins_url,omitempty"`
+	ProjectName *string `url:"project_name,omitempty" json:"project_name,omitempty"`
+	Username    *string `url:"username,omitempty" json:"username,omitempty"`
+}
+
+// GetJenkinsCIService gets Jenkins CI service settings for a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/services.html#get-jenkins-ci-service-settings
+func (s *ServicesService) GetJenkinsCIService(pid interface{}, options ...OptionFunc) (*JenkinsCIService, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/jenkins", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	svc := new(JenkinsCIService)
+	resp, err := s.client.Do(req, svc)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return svc, resp, err
+}
+
+// SetJenkinsCIServiceOptions represents the available SetJenkinsCIService()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/services.html#jenkins-ci
+type SetJenkinsCIServiceOptions struct {
+	URL         *string `url:"jenkins_url,omitempty" json:"jenkins_url,omitempty"`
+	ProjectName *string `url:"project_name,omitempty" json:"project_name,omitempty"`
+	Username    *string `url:"username,omitempty" json:"username,omitempty"`
+	Password    *string `url:"password,omitempty" json:"password,omitempty"`
+}
+
+// SetJenkinsCIService sets Jenkins service for a project
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/services.html#create-edit-jenkins-ci-service
+func (s *ServicesService) SetJenkinsCIService(pid interface{}, opt *SetJenkinsCIServiceOptions, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/jenkins", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// DeleteJenkinsCIService deletes Jenkins CI service for project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/services.html#delete-jira-service
+func (s *ServicesService) DeleteJenkinsCIService(pid interface{}, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/jenkins", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/session.go b/vendor/github.com/xanzy/go-gitlab/session.go
new file mode 100644
index 0000000..f89fdbe
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/session.go
@@ -0,0 +1,78 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import "time"
+
+// SessionService handles communication with the session related methods of
+// the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/session.html
+type SessionService struct {
+	client *Client
+}
+
+// Session represents a GitLab session.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/session.html#session
+type Session struct {
+	ID               int         `json:"id"`
+	Username         string      `json:"username"`
+	Email            string      `json:"email"`
+	Name             string      `json:"name"`
+	PrivateToken     string      `json:"private_token"`
+	Blocked          bool        `json:"blocked"`
+	CreatedAt        *time.Time  `json:"created_at"`
+	Bio              interface{} `json:"bio"`
+	Skype            string      `json:"skype"`
+	Linkedin         string      `json:"linkedin"`
+	Twitter          string      `json:"twitter"`
+	WebsiteURL       string      `json:"website_url"`
+	DarkScheme       bool        `json:"dark_scheme"`
+	ThemeID          int         `json:"theme_id"`
+	IsAdmin          bool        `json:"is_admin"`
+	CanCreateGroup   bool        `json:"can_create_group"`
+	CanCreateTeam    bool        `json:"can_create_team"`
+	CanCreateProject bool        `json:"can_create_project"`
+}
+
+// GetSessionOptions represents the available Session() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/session.html#session
+type GetSessionOptions struct {
+	Login    *string `url:"login,omitempty" json:"login,omitempty"`
+	Email    *string `url:"email,omitempty" json:"email,omitempty"`
+	Password *string `url:"password,omitempty" json:"password,omitempty"`
+}
+
+// GetSession logs in to get private token.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/session.html#session
+func (s *SessionService) GetSession(opt *GetSessionOptions, options ...OptionFunc) (*Session, *Response, error) {
+	req, err := s.client.NewRequest("POST", "session", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	session := new(Session)
+	resp, err := s.client.Do(req, session)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return session, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/settings.go b/vendor/github.com/xanzy/go-gitlab/settings.go
new file mode 100644
index 0000000..fde9910
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/settings.go
@@ -0,0 +1,265 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import "time"
+
+// SettingsService handles communication with the application SettingsService
+// related methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/settings.html
+type SettingsService struct {
+	client *Client
+}
+
+// Settings represents the GitLab application settings.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/settings.html
+type Settings struct {
+	ID                                  int               `json:"id"`
+	CreatedAt                           *time.Time        `json:"created_at"`
+	UpdatedAt                           *time.Time        `json:"updated_at"`
+	AdminNotificationEmail              string            `json:"admin_notification_email"`
+	AfterSignOutPath                    string            `json:"after_sign_out_path"`
+	AfterSignUpText                     string            `json:"after_sign_up_text"`
+	AkismetAPIKey                       string            `json:"akismet_api_key"`
+	AkismetEnabled                      bool              `json:"akismet_enabled"`
+	CircuitbreakerAccessRetries         int               `json:"circuitbreaker_access_retries"`
+	CircuitbreakerBackoffThreshold      int               `json:"circuitbreaker_backoff_threshold"`
+	CircuitbreakerFailureCountThreshold int               `json:"circuitbreaker_failure_count_threshold"`
+	CircuitbreakerFailureResetTime      int               `json:"circuitbreaker_failure_reset_time"`
+	CircuitbreakerFailureWaitTime       int               `json:"circuitbreaker_failure_wait_time"`
+	CircuitbreakerStorageTimeout        int               `json:"circuitbreaker_storage_timeout"`
+	ClientsideSentryDSN                 string            `json:"clientside_sentry_dsn"`
+	ClientsideSentryEnabled             bool              `json:"clientside_sentry_enabled"`
+	ContainerRegistryTokenExpireDelay   int               `json:"container_registry_token_expire_delay"`
+	DefaultArtifactsExpireIn            string            `json:"default_artifacts_expire_in"`
+	DefaultBranchProtection             int               `json:"default_branch_protection"`
+	DefaultGroupVisibility              string            `json:"default_group_visibility"`
+	DefaultProjectVisibility            string            `json:"default_project_visibility"`
+	DefaultProjectsLimit                int               `json:"default_projects_limit"`
+	DefaultSnippetVisibility            string            `json:"default_snippet_visibility"`
+	DisabledOauthSignInSources          []string          `json:"disabled_oauth_sign_in_sources"`
+	DomainBlacklistEnabled              bool              `json:"domain_blacklist_enabled"`
+	DomainBlacklist                     []string          `json:"domain_blacklist"`
+	DomainWhitelist                     []string          `json:"domain_whitelist"`
+	DSAKeyRestriction                   int               `json:"dsa_key_restriction"`
+	ECDSAKeyRestriction                 int               `json:"ecdsa_key_restriction"`
+	Ed25519KeyRestriction               int               `json:"ed25519_key_restriction"`
+	EmailAuthorInBody                   bool              `json:"email_author_in_body"`
+	EnabledGitAccessProtocol            string            `json:"enabled_git_access_protocol"`
+	GravatarEnabled                     bool              `json:"gravatar_enabled"`
+	HelpPageHideCommercialContent       bool              `json:"help_page_hide_commercial_content"`
+	HelpPageSupportURL                  string            `json:"help_page_support_url"`
+	HomePageURL                         string            `json:"home_page_url"`
+	HousekeepingBitmapsEnabled          bool              `json:"housekeeping_bitmaps_enabled"`
+	HousekeepingEnabled                 bool              `json:"housekeeping_enabled"`
+	HousekeepingFullRepackPeriod        int               `json:"housekeeping_full_repack_period"`
+	HousekeepingGcPeriod                int               `json:"housekeeping_gc_period"`
+	HousekeepingIncrementalRepackPeriod int               `json:"housekeeping_incremental_repack_period"`
+	HTMLEmailsEnabled                   bool              `json:"html_emails_enabled"`
+	ImportSources                       []string          `json:"import_sources"`
+	KodingEnabled                       bool              `json:"koding_enabled"`
+	KodingURL                           string            `json:"koding_url"`
+	MaxArtifactsSize                    int               `json:"max_artifacts_size"`
+	MaxAttachmentSize                   int               `json:"max_attachment_size"`
+	MaxPagesSize                        int               `json:"max_pages_size"`
+	MetricsEnabled                      bool              `json:"metrics_enabled"`
+	MetricsHost                         string            `json:"metrics_host"`
+	MetricsMethodCallThreshold          int               `json:"metrics_method_call_threshold"`
+	MetricsPacketSize                   int               `json:"metrics_packet_size"`
+	MetricsPoolSize                     int               `json:"metrics_pool_size"`
+	MetricsPort                         int               `json:"metrics_port"`
+	MetricsSampleInterval               int               `json:"metrics_sample_interval"`
+	MetricsTimeout                      int               `json:"metrics_timeout"`
+	PasswordAuthenticationEnabledForWeb bool              `json:"password_authentication_enabled_for_web"`
+	PasswordAuthenticationEnabledForGit bool              `json:"password_authentication_enabled_for_git"`
+	PerformanceBarAllowedGroupID        string            `json:"performance_bar_allowed_group_id"`
+	PerformanceBarEnabled               bool              `json:"performance_bar_enabled"`
+	PlantumlEnabled                     bool              `json:"plantuml_enabled"`
+	PlantumlURL                         string            `json:"plantuml_url"`
+	PollingIntervalMultiplier           float64           `json:"polling_interval_multiplier"`
+	ProjectExportEnabled                bool              `json:"project_export_enabled"`
+	PrometheusMetricsEnabled            bool              `json:"prometheus_metrics_enabled"`
+	RecaptchaEnabled                    bool              `json:"recaptcha_enabled"`
+	RecaptchaPrivateKey                 string            `json:"recaptcha_private_key"`
+	RecaptchaSiteKey                    string            `json:"recaptcha_site_key"`
+	RepositoryChecksEnabled             bool              `json:"repository_checks_enabled"`
+	RepositoryStorages                  []string          `json:"repository_storages"`
+	RequireTwoFactorAuthentication      bool              `json:"require_two_factor_authentication"`
+	RestrictedVisibilityLevels          []VisibilityValue `json:"restricted_visibility_levels"`
+	RsaKeyRestriction                   int               `json:"rsa_key_restriction"`
+	SendUserConfirmationEmail           bool              `json:"send_user_confirmation_email"`
+	SentryDSN                           string            `json:"sentry_dsn"`
+	SentryEnabled                       bool              `json:"sentry_enabled"`
+	SessionExpireDelay                  int               `json:"session_expire_delay"`
+	SharedRunnersEnabled                bool              `json:"shared_runners_enabled"`
+	SharedRunnersText                   string            `json:"shared_runners_text"`
+	SidekiqThrottlingEnabled            bool              `json:"sidekiq_throttling_enabled"`
+	SidekiqThrottlingFactor             float64           `json:"sidekiq_throttling_factor"`
+	SidekiqThrottlingQueues             []string          `json:"sidekiq_throttling_queues"`
+	SignInText                          string            `json:"sign_in_text"`
+	SignupEnabled                       bool              `json:"signup_enabled"`
+	TerminalMaxSessionTime              int               `json:"terminal_max_session_time"`
+	TwoFactorGracePeriod                int               `json:"two_factor_grace_period"`
+	UniqueIPsLimitEnabled               bool              `json:"unique_ips_limit_enabled"`
+	UniqueIPsLimitPerUser               int               `json:"unique_ips_limit_per_user"`
+	UniqueIPsLimitTimeWindow            int               `json:"unique_ips_limit_time_window"`
+	UsagePingEnabled                    bool              `json:"usage_ping_enabled"`
+	UserDefaultExternal                 bool              `json:"user_default_external"`
+	UserOauthApplications               bool              `json:"user_oauth_applications"`
+	VersionCheckEnabled                 bool              `json:"version_check_enabled"`
+}
+
+func (s Settings) String() string {
+	return Stringify(s)
+}
+
+// GetSettings gets the current application settings.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/settings.html#get-current-application.settings
+func (s *SettingsService) GetSettings(options ...OptionFunc) (*Settings, *Response, error) {
+	req, err := s.client.NewRequest("GET", "application/settings", nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	as := new(Settings)
+	resp, err := s.client.Do(req, as)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return as, resp, err
+}
+
+// UpdateSettingsOptions represents the available UpdateSettings() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/settings.html#change-application.settings
+type UpdateSettingsOptions struct {
+	AdminNotificationEmail              *string           `url:"admin_notification_email,omitempty" json:"admin_notification_email,omitempty"`
+	AfterSignOutPath                    *string           `url:"after_sign_out_path,omitempty" json:"after_sign_out_path,omitempty"`
+	AfterSignUpText                     *string           `url:"after_sign_up_text,omitempty" json:"after_sign_up_text,omitempty"`
+	AkismetAPIKey                       *string           `url:"akismet_api_key,omitempty" json:"akismet_api_key,omitempty"`
+	AkismetEnabled                      *bool             `url:"akismet_enabled,omitempty" json:"akismet_enabled,omitempty"`
+	CircuitbreakerAccessRetries         *int              `url:"circuitbreaker_access_retries,omitempty" json:"circuitbreaker_access_retries,omitempty"`
+	CircuitbreakerBackoffThreshold      *int              `url:"circuitbreaker_backoff_threshold,omitempty" json:"circuitbreaker_backoff_threshold,omitempty"`
+	CircuitbreakerFailureCountThreshold *int              `url:"circuitbreaker_failure_count_threshold,omitempty" json:"circuitbreaker_failure_count_threshold,omitempty"`
+	CircuitbreakerFailureResetTime      *int              `url:"circuitbreaker_failure_reset_time,omitempty" json:"circuitbreaker_failure_reset_time,omitempty"`
+	CircuitbreakerFailureWaitTime       *int              `url:"circuitbreaker_failure_wait_time,omitempty" json:"circuitbreaker_failure_wait_time,omitempty"`
+	CircuitbreakerStorageTimeout        *int              `url:"circuitbreaker_storage_timeout,omitempty" json:"circuitbreaker_storage_timeout,omitempty"`
+	ClientsideSentryDSN                 *string           `url:"clientside_sentry_dsn,omitempty" json:"clientside_sentry_dsn,omitempty"`
+	ClientsideSentryEnabled             *bool             `url:"clientside_sentry_enabled,omitempty" json:"clientside_sentry_enabled,omitempty"`
+	ContainerRegistryTokenExpireDelay   *int              `url:"container_registry_token_expire_delay,omitempty" json:"container_registry_token_expire_delay,omitempty"`
+	DefaultArtifactsExpireIn            *string           `url:"default_artifacts_expire_in,omitempty" json:"default_artifacts_expire_in,omitempty"`
+	DefaultBranchProtection             *int              `url:"default_branch_protection,omitempty" json:"default_branch_protection,omitempty"`
+	DefaultGroupVisibility              *string           `url:"default_group_visibility,omitempty" json:"default_group_visibility,omitempty"`
+	DefaultProjectVisibility            *string           `url:"default_project_visibility,omitempty" json:"default_project_visibility,omitempty"`
+	DefaultProjectsLimit                *int              `url:"default_projects_limit,omitempty" json:"default_projects_limit,omitempty"`
+	DefaultSnippetVisibility            *string           `url:"default_snippet_visibility,omitempty" json:"default_snippet_visibility,omitempty"`
+	DisabledOauthSignInSources          []string          `url:"disabled_oauth_sign_in_sources,omitempty" json:"disabled_oauth_sign_in_sources,omitempty"`
+	DomainBlacklistEnabled              *bool             `url:"domain_blacklist_enabled,omitempty" json:"domain_blacklist_enabled,omitempty"`
+	DomainBlacklist                     []string          `url:"domain_blacklist,omitempty" json:"domain_blacklist,omitempty"`
+	DomainWhitelist                     []string          `url:"domain_whitelist,omitempty" json:"domain_whitelist,omitempty"`
+	DSAKeyRestriction                   *int              `url:"dsa_key_restriction,omitempty" json:"dsa_key_restriction,omitempty"`
+	ECDSAKeyRestriction                 *int              `url:"ecdsa_key_restriction,omitempty" json:"ecdsa_key_restriction,omitempty"`
+	Ed25519KeyRestriction               *int              `url:"ed25519_key_restriction,omitempty" json:"ed25519_key_restriction,omitempty"`
+	EmailAuthorInBody                   *bool             `url:"email_author_in_body,omitempty" json:"email_author_in_body,omitempty"`
+	EnabledGitAccessProtocol            *string           `url:"enabled_git_access_protocol,omitempty" json:"enabled_git_access_protocol,omitempty"`
+	GravatarEnabled                     *bool             `url:"gravatar_enabled,omitempty" json:"gravatar_enabled,omitempty"`
+	HelpPageHideCommercialContent       *bool             `url:"help_page_hide_commercial_content,omitempty" json:"help_page_hide_commercial_content,omitempty"`
+	HelpPageSupportURL                  *string           `url:"help_page_support_url,omitempty" json:"help_page_support_url,omitempty"`
+	HomePageURL                         *string           `url:"home_page_url,omitempty" json:"home_page_url,omitempty"`
+	HousekeepingBitmapsEnabled          *bool             `url:"housekeeping_bitmaps_enabled,omitempty" json:"housekeeping_bitmaps_enabled,omitempty"`
+	HousekeepingEnabled                 *bool             `url:"housekeeping_enabled,omitempty" json:"housekeeping_enabled,omitempty"`
+	HousekeepingFullRepackPeriod        *int              `url:"housekeeping_full_repack_period,omitempty" json:"housekeeping_full_repack_period,omitempty"`
+	HousekeepingGcPeriod                *int              `url:"housekeeping_gc_period,omitempty" json:"housekeeping_gc_period,omitempty"`
+	HousekeepingIncrementalRepackPeriod *int              `url:"housekeeping_incremental_repack_period,omitempty" json:"housekeeping_incremental_repack_period,omitempty"`
+	HTMLEmailsEnabled                   *bool             `url:"html_emails_enabled,omitempty" json:"html_emails_enabled,omitempty"`
+	ImportSources                       []string          `url:"import_sources,omitempty" json:"import_sources,omitempty"`
+	KodingEnabled                       *bool             `url:"koding_enabled,omitempty" json:"koding_enabled,omitempty"`
+	KodingURL                           *string           `url:"koding_url,omitempty" json:"koding_url,omitempty"`
+	MaxArtifactsSize                    *int              `url:"max_artifacts_size,omitempty" json:"max_artifacts_size,omitempty"`
+	MaxAttachmentSize                   *int              `url:"max_attachment_size,omitempty" json:"max_attachment_size,omitempty"`
+	MaxPagesSize                        *int              `url:"max_pages_size,omitempty" json:"max_pages_size,omitempty"`
+	MetricsEnabled                      *bool             `url:"metrics_enabled,omitempty" json:"metrics_enabled,omitempty"`
+	MetricsHost                         *string           `url:"metrics_host,omitempty" json:"metrics_host,omitempty"`
+	MetricsMethodCallThreshold          *int              `url:"metrics_method_call_threshold,omitempty" json:"metrics_method_call_threshold,omitempty"`
+	MetricsPacketSize                   *int              `url:"metrics_packet_size,omitempty" json:"metrics_packet_size,omitempty"`
+	MetricsPoolSize                     *int              `url:"metrics_pool_size,omitempty" json:"metrics_pool_size,omitempty"`
+	MetricsPort                         *int              `url:"metrics_port,omitempty" json:"metrics_port,omitempty"`
+	MetricsSampleInterval               *int              `url:"metrics_sample_interval,omitempty" json:"metrics_sample_interval,omitempty"`
+	MetricsTimeout                      *int              `url:"metrics_timeout,omitempty" json:"metrics_timeout,omitempty"`
+	PasswordAuthenticationEnabledForWeb *bool             `url:"password_authentication_enabled_for_web,omitempty" json:"password_authentication_enabled_for_web,omitempty"`
+	PasswordAuthenticationEnabledForGit *bool             `url:"password_authentication_enabled_for_git,omitempty" json:"password_authentication_enabled_for_git,omitempty"`
+	PerformanceBarAllowedGroupID        *string           `url:"performance_bar_allowed_group_id,omitempty" json:"performance_bar_allowed_group_id,omitempty"`
+	PerformanceBarEnabled               *bool             `url:"performance_bar_enabled,omitempty" json:"performance_bar_enabled,omitempty"`
+	PlantumlEnabled                     *bool             `url:"plantuml_enabled,omitempty" json:"plantuml_enabled,omitempty"`
+	PlantumlURL                         *string           `url:"plantuml_url,omitempty" json:"plantuml_url,omitempty"`
+	PollingIntervalMultiplier           *float64          `url:"polling_interval_multiplier,omitempty" json:"polling_interval_multiplier,omitempty"`
+	ProjectExportEnabled                *bool             `url:"project_export_enabled,omitempty" json:"project_export_enabled,omitempty"`
+	PrometheusMetricsEnabled            *bool             `url:"prometheus_metrics_enabled,omitempty" json:"prometheus_metrics_enabled,omitempty"`
+	RecaptchaEnabled                    *bool             `url:"recaptcha_enabled,omitempty" json:"recaptcha_enabled,omitempty"`
+	RecaptchaPrivateKey                 *string           `url:"recaptcha_private_key,omitempty" json:"recaptcha_private_key,omitempty"`
+	RecaptchaSiteKey                    *string           `url:"recaptcha_site_key,omitempty" json:"recaptcha_site_key,omitempty"`
+	RepositoryChecksEnabled             *bool             `url:"repository_checks_enabled,omitempty" json:"repository_checks_enabled,omitempty"`
+	RepositoryStorages                  []string          `url:"repository_storages,omitempty" json:"repository_storages,omitempty"`
+	RequireTwoFactorAuthentication      *bool             `url:"require_two_factor_authentication,omitempty" json:"require_two_factor_authentication,omitempty"`
+	RestrictedVisibilityLevels          []VisibilityValue `url:"restricted_visibility_levels,omitempty" json:"restricted_visibility_levels,omitempty"`
+	RsaKeyRestriction                   *int              `url:"rsa_key_restriction,omitempty" json:"rsa_key_restriction,omitempty"`
+	SendUserConfirmationEmail           *bool             `url:"send_user_confirmation_email,omitempty" json:"send_user_confirmation_email,omitempty"`
+	SentryDSN                           *string           `url:"sentry_dsn,omitempty" json:"sentry_dsn,omitempty"`
+	SentryEnabled                       *bool             `url:"sentry_enabled,omitempty" json:"sentry_enabled,omitempty"`
+	SessionExpireDelay                  *int              `url:"session_expire_delay,omitempty" json:"session_expire_delay,omitempty"`
+	SharedRunnersEnabled                *bool             `url:"shared_runners_enabled,omitempty" json:"shared_runners_enabled,omitempty"`
+	SharedRunnersText                   *string           `url:"shared_runners_text,omitempty" json:"shared_runners_text,omitempty"`
+	SidekiqThrottlingEnabled            *bool             `url:"sidekiq_throttling_enabled,omitempty" json:"sidekiq_throttling_enabled,omitempty"`
+	SidekiqThrottlingFactor             *float64          `url:"sidekiq_throttling_factor,omitempty" json:"sidekiq_throttling_factor,omitempty"`
+	SidekiqThrottlingQueues             []string          `url:"sidekiq_throttling_queues,omitempty" json:"sidekiq_throttling_queues,omitempty"`
+	SignInText                          *string           `url:"sign_in_text,omitempty" json:"sign_in_text,omitempty"`
+	SignupEnabled                       *bool             `url:"signup_enabled,omitempty" json:"signup_enabled,omitempty"`
+	TerminalMaxSessionTime              *int              `url:"terminal_max_session_time,omitempty" json:"terminal_max_session_time,omitempty"`
+	TwoFactorGracePeriod                *int              `url:"two_factor_grace_period,omitempty" json:"two_factor_grace_period,omitempty"`
+	UniqueIPsLimitEnabled               *bool             `url:"unique_ips_limit_enabled,omitempty" json:"unique_ips_limit_enabled,omitempty"`
+	UniqueIPsLimitPerUser               *int              `url:"unique_ips_limit_per_user,omitempty" json:"unique_ips_limit_per_user,omitempty"`
+	UniqueIPsLimitTimeWindow            *int              `url:"unique_ips_limit_time_window,omitempty" json:"unique_ips_limit_time_window,omitempty"`
+	UsagePingEnabled                    *bool             `url:"usage_ping_enabled,omitempty" json:"usage_ping_enabled,omitempty"`
+	UserDefaultExternal                 *bool             `url:"user_default_external,omitempty" json:"user_default_external,omitempty"`
+	UserOauthApplications               *bool             `url:"user_oauth_applications,omitempty" json:"user_oauth_applications,omitempty"`
+	VersionCheckEnabled                 *bool             `url:"version_check_enabled,omitempty" json:"version_check_enabled,omitempty"`
+}
+
+// UpdateSettings updates the application settings.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/settings.html#change-application.settings
+func (s *SettingsService) UpdateSettings(opt *UpdateSettingsOptions, options ...OptionFunc) (*Settings, *Response, error) {
+	req, err := s.client.NewRequest("PUT", "application/settings", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	as := new(Settings)
+	resp, err := s.client.Do(req, as)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return as, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/sidekiq_metrics.go b/vendor/github.com/xanzy/go-gitlab/sidekiq_metrics.go
new file mode 100644
index 0000000..83e7702
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/sidekiq_metrics.go
@@ -0,0 +1,154 @@
+//
+// Copyright 2018, Sander van Harmelen
+//
+// 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 gitlab
+
+import "time"
+
+// SidekiqService handles communication with the sidekiq service
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/sidekiq_metrics.html
+type SidekiqService struct {
+	client *Client
+}
+
+// QueueMetrics represents the GitLab sidekiq queue metrics.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/sidekiq_metrics.html#get-the-current-queue-metrics
+type QueueMetrics struct {
+	Queues map[string]struct {
+		Backlog int `json:"backlog"`
+		Latency int `json:"latency"`
+	} `json:"queues"`
+}
+
+// GetQueueMetrics lists information about all the registered queues,
+// their backlog and their latency.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/sidekiq_metrics.html#get-the-current-queue-metrics
+func (s *SidekiqService) GetQueueMetrics(options ...OptionFunc) (*QueueMetrics, *Response, error) {
+	req, err := s.client.NewRequest("GET", "/sidekiq/queue_metrics", nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	q := new(QueueMetrics)
+	resp, err := s.client.Do(req, q)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return q, resp, err
+}
+
+// ProcessMetrics represents the GitLab sidekiq process metrics.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/sidekiq_metrics.html#get-the-current-process-metrics
+type ProcessMetrics struct {
+	Processes []struct {
+		Hostname    string     `json:"hostname"`
+		Pid         int        `json:"pid"`
+		Tag         string     `json:"tag"`
+		StartedAt   *time.Time `json:"started_at"`
+		Queues      []string   `json:"queues"`
+		Labels      []string   `json:"labels"`
+		Concurrency int        `json:"concurrency"`
+		Busy        int        `json:"busy"`
+	} `json:"processes"`
+}
+
+// GetProcessMetrics lists information about all the Sidekiq workers registered
+// to process your queues.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/sidekiq_metrics.html#get-the-current-process-metrics
+func (s *SidekiqService) GetProcessMetrics(options ...OptionFunc) (*ProcessMetrics, *Response, error) {
+	req, err := s.client.NewRequest("GET", "/sidekiq/process_metrics", nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(ProcessMetrics)
+	resp, err := s.client.Do(req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, err
+}
+
+// JobStats represents the GitLab sidekiq job stats.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/sidekiq_metrics.html#get-the-current-job-statistics
+type JobStats struct {
+	Jobs struct {
+		Processed int `json:"processed"`
+		Failed    int `json:"failed"`
+		Enqueued  int `json:"enqueued"`
+	} `json:"jobs"`
+}
+
+// GetJobStats list information about the jobs that Sidekiq has performed.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/sidekiq_metrics.html#get-the-current-job-statistics
+func (s *SidekiqService) GetJobStats(options ...OptionFunc) (*JobStats, *Response, error) {
+	req, err := s.client.NewRequest("GET", "/sidekiq/job_stats", nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	j := new(JobStats)
+	resp, err := s.client.Do(req, j)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return j, resp, err
+}
+
+// CompoundMetrics represents the GitLab sidekiq compounded stats.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/sidekiq_metrics.html#get-a-compound-response-of-all-the-previously-mentioned-metrics
+type CompoundMetrics struct {
+	QueueMetrics
+	ProcessMetrics
+	JobStats
+}
+
+// GetCompoundMetrics lists all the currently available information about Sidekiq.
+// Get a compound response of all the previously mentioned metrics
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/sidekiq_metrics.html#get-the-current-job-statistics
+func (s *SidekiqService) GetCompoundMetrics(options ...OptionFunc) (*CompoundMetrics, *Response, error) {
+	req, err := s.client.NewRequest("GET", "/sidekiq/compound_metrics", nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	c := new(CompoundMetrics)
+	resp, err := s.client.Do(req, c)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return c, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/snippets.go b/vendor/github.com/xanzy/go-gitlab/snippets.go
new file mode 100644
index 0000000..be232c8
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/snippets.go
@@ -0,0 +1,230 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"bytes"
+	"fmt"
+	"time"
+)
+
+// SnippetsService handles communication with the snippets
+// related methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/snippets.html
+type SnippetsService struct {
+	client *Client
+}
+
+// Snippet represents a GitLab snippet.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/snippets.html
+type Snippet struct {
+	ID          int    `json:"id"`
+	Title       string `json:"title"`
+	FileName    string `json:"file_name"`
+	Description string `json:"description"`
+	Author      struct {
+		ID        int        `json:"id"`
+		Username  string     `json:"username"`
+		Email     string     `json:"email"`
+		Name      string     `json:"name"`
+		State     string     `json:"state"`
+		CreatedAt *time.Time `json:"created_at"`
+	} `json:"author"`
+	UpdatedAt *time.Time `json:"updated_at"`
+	CreatedAt *time.Time `json:"created_at"`
+	WebURL    string     `json:"web_url"`
+	RawURL    string     `json:"raw_url"`
+}
+
+func (s Snippet) String() string {
+	return Stringify(s)
+}
+
+// ListSnippetsOptions represents the available ListSnippets() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/snippets.html#list-snippets
+type ListSnippetsOptions ListOptions
+
+// ListSnippets gets a list of snippets.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/snippets.html#list-snippets
+func (s *SnippetsService) ListSnippets(opt *ListSnippetsOptions, options ...OptionFunc) ([]*Snippet, *Response, error) {
+	req, err := s.client.NewRequest("GET", "snippets", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var ps []*Snippet
+	resp, err := s.client.Do(req, &ps)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ps, resp, err
+}
+
+// GetSnippet gets a single snippet
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/snippets.html#single-snippet
+func (s *SnippetsService) GetSnippet(snippet int, options ...OptionFunc) (*Snippet, *Response, error) {
+	u := fmt.Sprintf("snippets/%d", snippet)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ps := new(Snippet)
+	resp, err := s.client.Do(req, ps)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ps, resp, err
+}
+
+// CreateSnippetOptions represents the available CreateSnippet() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/snippets.html#create-new-snippet
+type CreateSnippetOptions struct {
+	Title       *string          `url:"title,omitempty" json:"title,omitempty"`
+	FileName    *string          `url:"file_name,omitempty" json:"file_name,omitempty"`
+	Description *string          `url:"description,omitempty" json:"description,omitempty"`
+	Content     *string          `url:"content,omitempty" json:"content,omitempty"`
+	Visibility  *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
+}
+
+// CreateSnippet creates a new snippet. The user must have permission
+// to create new snippets.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/snippets.html#create-new-snippet
+func (s *SnippetsService) CreateSnippet(opt *CreateSnippetOptions, options ...OptionFunc) (*Snippet, *Response, error) {
+	req, err := s.client.NewRequest("POST", "snippets", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ps := new(Snippet)
+	resp, err := s.client.Do(req, ps)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ps, resp, err
+}
+
+// UpdateSnippetOptions represents the available UpdateSnippet() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/snippets.html#update-snippet
+type UpdateSnippetOptions struct {
+	Title       *string          `url:"title,omitempty" json:"title,omitempty"`
+	FileName    *string          `url:"file_name,omitempty" json:"file_name,omitempty"`
+	Description *string          `url:"description,omitempty" json:"description,omitempty"`
+	Content     *string          `url:"content,omitempty" json:"content,omitempty"`
+	Visibility  *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
+}
+
+// UpdateSnippet updates an existing snippet. The user must have
+// permission to change an existing snippet.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/snippets.html#update-snippet
+func (s *SnippetsService) UpdateSnippet(snippet int, opt *UpdateSnippetOptions, options ...OptionFunc) (*Snippet, *Response, error) {
+	u := fmt.Sprintf("snippets/%d", snippet)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ps := new(Snippet)
+	resp, err := s.client.Do(req, ps)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ps, resp, err
+}
+
+// DeleteSnippet deletes an existing snippet. This is an idempotent
+// function and deleting a non-existent snippet still returns a 200 OK status
+// code.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/snippets.html#delete-snippet
+func (s *SnippetsService) DeleteSnippet(snippet int, options ...OptionFunc) (*Response, error) {
+	u := fmt.Sprintf("snippets/%d", snippet)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// SnippetContent returns the raw snippet as plain text.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/snippets.html#snippet-content
+func (s *SnippetsService) SnippetContent(snippet int, options ...OptionFunc) ([]byte, *Response, error) {
+	u := fmt.Sprintf("snippets/%d/raw", snippet)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var b bytes.Buffer
+	resp, err := s.client.Do(req, &b)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return b.Bytes(), resp, err
+}
+
+// ExploreSnippetsOptions represents the available ExploreSnippets() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/snippets.html#explore-all-public-snippets
+type ExploreSnippetsOptions ListOptions
+
+// ExploreSnippets gets the list of public snippets.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/snippets.html#explore-all-public-snippets
+func (s *SnippetsService) ExploreSnippets(opt *ExploreSnippetsOptions, options ...OptionFunc) ([]*Snippet, *Response, error) {
+	req, err := s.client.NewRequest("GET", "snippets/public", nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var ps []*Snippet
+	resp, err := s.client.Do(req, &ps)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ps, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/strings.go b/vendor/github.com/xanzy/go-gitlab/strings.go
new file mode 100644
index 0000000..aeefb6b
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/strings.go
@@ -0,0 +1,94 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"bytes"
+	"fmt"
+
+	"reflect"
+)
+
+// Stringify attempts to create a reasonable string representation of types in
+// the GitHub library.  It does things like resolve pointers to their values
+// and omits struct fields with nil values.
+func Stringify(message interface{}) string {
+	var buf bytes.Buffer
+	v := reflect.ValueOf(message)
+	stringifyValue(&buf, v)
+	return buf.String()
+}
+
+// stringifyValue was heavily inspired by the goprotobuf library.
+func stringifyValue(buf *bytes.Buffer, val reflect.Value) {
+	if val.Kind() == reflect.Ptr && val.IsNil() {
+		buf.WriteString("<nil>")
+		return
+	}
+
+	v := reflect.Indirect(val)
+
+	switch v.Kind() {
+	case reflect.String:
+		fmt.Fprintf(buf, `"%s"`, v)
+	case reflect.Slice:
+		buf.WriteByte('[')
+		for i := 0; i < v.Len(); i++ {
+			if i > 0 {
+				buf.WriteByte(' ')
+			}
+
+			stringifyValue(buf, v.Index(i))
+		}
+
+		buf.WriteByte(']')
+		return
+	case reflect.Struct:
+		if v.Type().Name() != "" {
+			buf.WriteString(v.Type().String())
+		}
+
+		buf.WriteByte('{')
+
+		var sep bool
+		for i := 0; i < v.NumField(); i++ {
+			fv := v.Field(i)
+			if fv.Kind() == reflect.Ptr && fv.IsNil() {
+				continue
+			}
+			if fv.Kind() == reflect.Slice && fv.IsNil() {
+				continue
+			}
+
+			if sep {
+				buf.WriteString(", ")
+			} else {
+				sep = true
+			}
+
+			buf.WriteString(v.Type().Field(i).Name)
+			buf.WriteByte(':')
+			stringifyValue(buf, fv)
+		}
+
+		buf.WriteByte('}')
+	default:
+		if v.CanInterface() {
+			fmt.Fprint(buf, v.Interface())
+		}
+	}
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/system_hooks.go b/vendor/github.com/xanzy/go-gitlab/system_hooks.go
new file mode 100644
index 0000000..d5209d4
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/system_hooks.go
@@ -0,0 +1,143 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"time"
+)
+
+// SystemHooksService handles communication with the system hooks related
+// methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/system_hooks.html
+type SystemHooksService struct {
+	client *Client
+}
+
+// Hook represents a GitLap system hook.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/system_hooks.html
+type Hook struct {
+	ID        int        `json:"id"`
+	URL       string     `json:"url"`
+	CreatedAt *time.Time `json:"created_at"`
+}
+
+func (h Hook) String() string {
+	return Stringify(h)
+}
+
+// ListHooks gets a list of system hooks.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/system_hooks.html#list-system-hooks
+func (s *SystemHooksService) ListHooks(options ...OptionFunc) ([]*Hook, *Response, error) {
+	req, err := s.client.NewRequest("GET", "hooks", nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var h []*Hook
+	resp, err := s.client.Do(req, &h)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return h, resp, err
+}
+
+// AddHookOptions represents the available AddHook() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/system_hooks.html#add-new-system-hook-hook
+type AddHookOptions struct {
+	URL *string `url:"url,omitempty" json:"url,omitempty"`
+}
+
+// AddHook adds a new system hook hook.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/system_hooks.html#add-new-system-hook-hook
+func (s *SystemHooksService) AddHook(opt *AddHookOptions, options ...OptionFunc) (*Hook, *Response, error) {
+	req, err := s.client.NewRequest("POST", "hooks", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	h := new(Hook)
+	resp, err := s.client.Do(req, h)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return h, resp, err
+}
+
+// HookEvent represents an event trigger by a GitLab system hook.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/system_hooks.html
+type HookEvent struct {
+	EventName  string `json:"event_name"`
+	Name       string `json:"name"`
+	Path       string `json:"path"`
+	ProjectID  int    `json:"project_id"`
+	OwnerName  string `json:"owner_name"`
+	OwnerEmail string `json:"owner_email"`
+}
+
+func (h HookEvent) String() string {
+	return Stringify(h)
+}
+
+// TestHook tests a system hook.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/system_hooks.html#test-system-hook
+func (s *SystemHooksService) TestHook(hook int, options ...OptionFunc) (*HookEvent, *Response, error) {
+	u := fmt.Sprintf("hooks/%d", hook)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	h := new(HookEvent)
+	resp, err := s.client.Do(req, h)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return h, resp, err
+}
+
+// DeleteHook deletes a system hook. This is an idempotent API function and
+// returns 200 OK even if the hook is not available. If the hook is deleted it
+// is also returned as JSON.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/system_hooks.html#delete-system-hook
+func (s *SystemHooksService) DeleteHook(hook int, options ...OptionFunc) (*Response, error) {
+	u := fmt.Sprintf("hooks/%d", hook)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/tags.go b/vendor/github.com/xanzy/go-gitlab/tags.go
new file mode 100644
index 0000000..b726410
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/tags.go
@@ -0,0 +1,232 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+)
+
+// TagsService handles communication with the tags related methods
+// of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/tags.html
+type TagsService struct {
+	client *Client
+}
+
+// Tag represents a GitLab tag.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/tags.html
+type Tag struct {
+	Commit  *Commit  `json:"commit"`
+	Release *Release `json:"release"`
+	Name    string   `json:"name"`
+	Message string   `json:"message"`
+}
+
+// Release represents a GitLab version release.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/tags.html
+type Release struct {
+	TagName     string `json:"tag_name"`
+	Description string `json:"description"`
+}
+
+func (t Tag) String() string {
+	return Stringify(t)
+}
+
+// ListTagsOptions represents the available ListTags() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/tags.html#list-project-repository-tags
+type ListTagsOptions ListOptions
+
+// ListTags gets a list of tags from a project, sorted by name in reverse
+// alphabetical order.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/tags.html#list-project-repository-tags
+func (s *TagsService) ListTags(pid interface{}, opt *ListTagsOptions, options ...OptionFunc) ([]*Tag, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/tags", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var t []*Tag
+	resp, err := s.client.Do(req, &t)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return t, resp, err
+}
+
+// GetTag a specific repository tag determined by its name. It returns 200 together
+// with the tag information if the tag exists. It returns 404 if the tag does not exist.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/tags.html#get-a-single-repository-tag
+func (s *TagsService) GetTag(pid interface{}, tag string, options ...OptionFunc) (*Tag, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/tags/%s", url.QueryEscape(project), tag)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var t *Tag
+	resp, err := s.client.Do(req, &t)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return t, resp, err
+}
+
+// CreateTagOptions represents the available CreateTag() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/tags.html#create-a-new-tag
+type CreateTagOptions struct {
+	TagName            *string `url:"tag_name,omitempty" json:"tag_name,omitempty"`
+	Ref                *string `url:"ref,omitempty" json:"ref,omitempty"`
+	Message            *string `url:"message,omitempty" json:"message,omitempty"`
+	ReleaseDescription *string `url:"release_description:omitempty" json:"release_description,omitempty"`
+}
+
+// CreateTag creates a new tag in the repository that points to the supplied ref.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/tags.html#create-a-new-tag
+func (s *TagsService) CreateTag(pid interface{}, opt *CreateTagOptions, options ...OptionFunc) (*Tag, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/tags", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	t := new(Tag)
+	resp, err := s.client.Do(req, t)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return t, resp, err
+}
+
+// DeleteTag deletes a tag of a repository with given name.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/tags.html#delete-a-tag
+func (s *TagsService) DeleteTag(pid interface{}, tag string, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/tags/%s", url.QueryEscape(project), tag)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// CreateReleaseOptions represents the available CreateRelease() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/tags.html#create-a-new-release
+type CreateReleaseOptions struct {
+	Description *string `url:"description:omitempty" json:"description,omitempty"`
+}
+
+// CreateRelease Add release notes to the existing git tag.
+// If there already exists a release for the given tag, status code 409 is returned.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/tags.html#create-a-new-release
+func (s *TagsService) CreateRelease(pid interface{}, tag string, opt *CreateReleaseOptions, options ...OptionFunc) (*Release, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/tags/%s/release", url.QueryEscape(project), tag)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	r := new(Release)
+	resp, err := s.client.Do(req, r)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return r, resp, err
+}
+
+// UpdateReleaseOptions represents the available UpdateRelease() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/tags.html#update-a-release
+type UpdateReleaseOptions struct {
+	Description *string `url:"description:omitempty" json:"description,omitempty"`
+}
+
+// UpdateRelease Updates the release notes of a given release.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/tags.html#update-a-release
+func (s *TagsService) UpdateRelease(pid interface{}, tag string, opt *UpdateReleaseOptions, options ...OptionFunc) (*Release, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/tags/%s/release", url.QueryEscape(project), tag)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	r := new(Release)
+	resp, err := s.client.Do(req, r)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return r, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/time_stats.go b/vendor/github.com/xanzy/go-gitlab/time_stats.go
new file mode 100644
index 0000000..5e3bff9
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/time_stats.go
@@ -0,0 +1,163 @@
+package gitlab
+
+import (
+	"fmt"
+	"net/url"
+)
+
+// timeStatsService handles communication with the time tracking related
+// methods of the GitLab API.
+//
+// GitLab docs: https://docs.gitlab.com/ce/workflow/time_tracking.html
+type timeStatsService struct {
+	client *Client
+}
+
+// TimeStats represents the time estimates and time spent for an issue.
+//
+// GitLab docs: https://docs.gitlab.com/ce/workflow/time_tracking.html
+type TimeStats struct {
+	HumanTimeEstimate   string `json:"human_time_estimate"`
+	HumanTotalTimeSpent string `json:"human_total_time_spent"`
+	TimeEstimate        int    `json:"time_estimate"`
+	TotalTimeSpent      int    `json:"total_time_spent"`
+}
+
+func (t TimeStats) String() string {
+	return Stringify(t)
+}
+
+// SetTimeEstimateOptions represents the available SetTimeEstimate()
+// options.
+//
+// GitLab docs: https://docs.gitlab.com/ce/workflow/time_tracking.html
+type SetTimeEstimateOptions struct {
+	Duration *string `url:"duration,omitempty" json:"duration,omitempty"`
+}
+
+// setTimeEstimate sets the time estimate for a single project issue.
+//
+// GitLab docs: https://docs.gitlab.com/ce/workflow/time_tracking.html
+func (s *timeStatsService) setTimeEstimate(pid interface{}, entity string, issue int, opt *SetTimeEstimateOptions, options ...OptionFunc) (*TimeStats, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/%s/%d/time_estimate", url.QueryEscape(project), entity, issue)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	t := new(TimeStats)
+	resp, err := s.client.Do(req, t)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return t, resp, err
+}
+
+// resetTimeEstimate resets the time estimate for a single project issue.
+//
+// GitLab docs: https://docs.gitlab.com/ce/workflow/time_tracking.html
+func (s *timeStatsService) resetTimeEstimate(pid interface{}, entity string, issue int, options ...OptionFunc) (*TimeStats, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/%s/%d/reset_time_estimate", url.QueryEscape(project), entity, issue)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	t := new(TimeStats)
+	resp, err := s.client.Do(req, t)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return t, resp, err
+}
+
+// AddSpentTimeOptions represents the available AddSpentTime() options.
+//
+// GitLab docs: https://docs.gitlab.com/ce/workflow/time_tracking.html
+type AddSpentTimeOptions struct {
+	Duration *string `url:"duration,omitempty" json:"duration,omitempty"`
+}
+
+// addSpentTime adds spent time for a single project issue.
+//
+// GitLab docs: https://docs.gitlab.com/ce/workflow/time_tracking.html
+func (s *timeStatsService) addSpentTime(pid interface{}, entity string, issue int, opt *AddSpentTimeOptions, options ...OptionFunc) (*TimeStats, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/%s/%d/add_spent_time", url.QueryEscape(project), entity, issue)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	t := new(TimeStats)
+	resp, err := s.client.Do(req, t)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return t, resp, err
+}
+
+// resetSpentTime resets the spent time for a single project issue.
+//
+// GitLab docs: https://docs.gitlab.com/ce/workflow/time_tracking.html
+func (s *timeStatsService) resetSpentTime(pid interface{}, entity string, issue int, options ...OptionFunc) (*TimeStats, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/%s/%d/reset_spent_time", url.QueryEscape(project), entity, issue)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	t := new(TimeStats)
+	resp, err := s.client.Do(req, t)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return t, resp, err
+}
+
+// getTimeSpent gets the spent time for a single project issue.
+//
+// GitLab docs: https://docs.gitlab.com/ce/workflow/time_tracking.html
+func (s *timeStatsService) getTimeSpent(pid interface{}, entity string, issue int, options ...OptionFunc) (*TimeStats, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/%s/%d/time_stats", url.QueryEscape(project), entity, issue)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	t := new(TimeStats)
+	resp, err := s.client.Do(req, t)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return t, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/todos.go b/vendor/github.com/xanzy/go-gitlab/todos.go
new file mode 100644
index 0000000..db58edd
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/todos.go
@@ -0,0 +1,175 @@
+package gitlab
+
+import "time"
+import "fmt"
+
+// TodosService handles communication with the todos related methods of
+// the Gitlab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/todos.html
+type TodosService struct {
+	client *Client
+}
+
+// TodoAction represents the available actions that can be performed on a todo.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/todos.html
+type TodoAction string
+
+// The available todo actions.
+const (
+	TodoAssigned          TodoAction = "assigned"
+	TodoMentioned         TodoAction = "mentioned"
+	TodoBuildFailed       TodoAction = "build_failed"
+	TodoMarked            TodoAction = "marked"
+	TodoApprovalRequired  TodoAction = "approval_required"
+	TodoDirectlyAddressed TodoAction = "directly_addressed"
+)
+
+// TodoTarget represents a todo target of type Issue or MergeRequest
+type TodoTarget struct {
+	// TODO: replace both Assignee and Author structs with v4 User struct
+	Assignee struct {
+		Name      string `json:"name"`
+		Username  string `json:"username"`
+		ID        int    `json:"id"`
+		State     string `json:"state"`
+		AvatarURL string `json:"avatar_url"`
+		WebURL    string `json:"web_url"`
+	} `json:"assignee"`
+	Author struct {
+		Name      string `json:"name"`
+		Username  string `json:"username"`
+		ID        int    `json:"id"`
+		State     string `json:"state"`
+		AvatarURL string `json:"avatar_url"`
+		WebURL    string `json:"web_url"`
+	} `json:"author"`
+	CreatedAt      *time.Time `json:"created_at"`
+	Description    string     `json:"description"`
+	Downvotes      int        `json:"downvotes"`
+	ID             int        `json:"id"`
+	IID            int        `json:"iid"`
+	Labels         []string   `json:"labels"`
+	Milestone      Milestone  `json:"milestone"`
+	ProjectID      int        `json:"project_id"`
+	State          string     `json:"state"`
+	Subscribed     bool       `json:"subscribed"`
+	Title          string     `json:"title"`
+	UpdatedAt      *time.Time `json:"updated_at"`
+	Upvotes        int        `json:"upvotes"`
+	UserNotesCount int        `json:"user_notes_count"`
+	WebURL         string     `json:"web_url"`
+
+	// Only available for type Issue
+	Confidential bool   `json:"confidential"`
+	DueDate      string `json:"due_date"`
+	Weight       int    `json:"weight"`
+
+	// Only available for type MergeRequest
+	ApprovalsBeforeMerge      bool   `json:"approvals_before_merge"`
+	ForceRemoveSourceBranch   bool   `json:"force_remove_source_branch"`
+	MergeCommitSha            string `json:"merge_commit_sha"`
+	MergeWhenPipelineSucceeds bool   `json:"merge_when_pipeline_succeeds"`
+	MergeStatus               string `json:"merge_status"`
+	Sha                       string `json:"sha"`
+	ShouldRemoveSourceBranch  bool   `json:"should_remove_source_branch"`
+	SourceBranch              string `json:"source_branch"`
+	SourceProjectID           int    `json:"source_project_id"`
+	Squash                    bool   `json:"squash"`
+	TargetBranch              string `json:"target_branch"`
+	TargetProjectID           int    `json:"target_project_id"`
+	WorkInProgress            bool   `json:"work_in_progress"`
+}
+
+// Todo represents a GitLab todo.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/todos.html
+type Todo struct {
+	ID      int `json:"id"`
+	Project struct {
+		ID                int    `json:"id"`
+		HTTPURLToRepo     string `json:"http_url_to_repo"`
+		WebURL            string `json:"web_url"`
+		Name              string `json:"name"`
+		NameWithNamespace string `json:"name_with_namespace"`
+		Path              string `json:"path"`
+		PathWithNamespace string `json:"path_with_namespace"`
+	} `json:"project"`
+	Author struct {
+		ID        int    `json:"id"`
+		Name      string `json:"name"`
+		Username  string `json:"username"`
+		State     string `json:"state"`
+		AvatarURL string `json:"avatar_url"`
+		WebURL    string `json:"web_url"`
+	} `json:"author"`
+	ActionName TodoAction `json:"action_name"`
+	TargetType string     `json:"target_type"`
+	Target     TodoTarget `json:"target"`
+	TargetURL  string     `json:"target_url"`
+	Body       string     `json:"body"`
+	State      string     `json:"state"`
+	CreatedAt  *time.Time `json:"created_at"`
+}
+
+func (t Todo) String() string {
+	return Stringify(t)
+}
+
+// ListTodosOptions represents the available ListTodos() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/todos.html#get-a-list-of-todos
+type ListTodosOptions struct {
+	Action    *TodoAction `url:"action,omitempty" json:"action,omitempty"`
+	AuthorID  *int        `url:"author_id,omitempty" json:"author_id,omitempty"`
+	ProjectID *int        `url:"project_id,omitempty" json:"project_id,omitempty"`
+	State     *string     `url:"state,omitempty" json:"state,omitempty"`
+	Type      *string     `url:"type,omitempty" json:"type,omitempty"`
+}
+
+// ListTodos lists all todos created by authenticated user.
+// When no filter is applied, it returns all pending todos for the current user.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/todos.html#get-a-list-of-todos
+func (s *TodosService) ListTodos(opt *ListTodosOptions, options ...OptionFunc) ([]*Todo, *Response, error) {
+	req, err := s.client.NewRequest("GET", "todos", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var t []*Todo
+	resp, err := s.client.Do(req, &t)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return t, resp, err
+}
+
+// MarkTodoAsDone marks a single pending todo given by its ID for the current user as done.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/todos.html#mark-a-todo-as-done
+func (s *TodosService) MarkTodoAsDone(id int, options ...OptionFunc) (*Response, error) {
+	u := fmt.Sprintf("todos/%d/mark_as_done", id)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// MarkAllTodosAsDone marks all pending todos for the current user as done.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/todos.html#mark-all-todos-as-done
+func (s *TodosService) MarkAllTodosAsDone(options ...OptionFunc) (*Response, error) {
+	req, err := s.client.NewRequest("POST", "todos/mark_as_done", nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/users.go b/vendor/github.com/xanzy/go-gitlab/users.go
new file mode 100644
index 0000000..a619ab0
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/users.go
@@ -0,0 +1,767 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"errors"
+	"fmt"
+	"time"
+)
+
+// UsersService handles communication with the user related methods of
+// the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html
+type UsersService struct {
+	client *Client
+}
+
+// User represents a GitLab user.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html
+type User struct {
+	ID               int             `json:"id"`
+	Username         string          `json:"username"`
+	Email            string          `json:"email"`
+	Name             string          `json:"name"`
+	State            string          `json:"state"`
+	CreatedAt        *time.Time      `json:"created_at"`
+	Bio              string          `json:"bio"`
+	Location         string          `json:"location"`
+	Skype            string          `json:"skype"`
+	Linkedin         string          `json:"linkedin"`
+	Twitter          string          `json:"twitter"`
+	WebsiteURL       string          `json:"website_url"`
+	Organization     string          `json:"organization"`
+	ExternUID        string          `json:"extern_uid"`
+	Provider         string          `json:"provider"`
+	ThemeID          int             `json:"theme_id"`
+	LastActivityOn   *ISOTime        `json:"last_activity_on"`
+	ColorSchemeID    int             `json:"color_scheme_id"`
+	IsAdmin          bool            `json:"is_admin"`
+	AvatarURL        string          `json:"avatar_url"`
+	CanCreateGroup   bool            `json:"can_create_group"`
+	CanCreateProject bool            `json:"can_create_project"`
+	ProjectsLimit    int             `json:"projects_limit"`
+	CurrentSignInAt  *time.Time      `json:"current_sign_in_at"`
+	LastSignInAt     *time.Time      `json:"last_sign_in_at"`
+	TwoFactorEnabled bool            `json:"two_factor_enabled"`
+	Identities       []*UserIdentity `json:"identities"`
+	External         bool            `json:"external"`
+}
+
+// UserIdentity represents a user identity.
+type UserIdentity struct {
+	Provider  string `json:"provider"`
+	ExternUID string `json:"extern_uid"`
+}
+
+// ListUsersOptions represents the available ListUsers() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#list-users
+type ListUsersOptions struct {
+	ListOptions
+	Active  *bool `url:"active,omitempty" json:"active,omitempty"`
+	Blocked *bool `url:"blocked,omitempty" json:"blocked,omitempty"`
+
+	// The options below are only available for admins.
+	Search        *string    `url:"search,omitempty" json:"search,omitempty"`
+	Username      *string    `url:"username,omitempty" json:"username,omitempty"`
+	ExternalUID   *string    `url:"extern_uid,omitempty" json:"extern_uid,omitempty"`
+	Provider      *string    `url:"provider,omitempty" json:"provider,omitempty"`
+	CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
+	CreatedAfter  *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
+	OrderBy       *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
+	Sort          *string    `url:"sort,omitempty" json:"sort,omitempty"`
+}
+
+// ListUsers gets a list of users.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#list-users
+func (s *UsersService) ListUsers(opt *ListUsersOptions, options ...OptionFunc) ([]*User, *Response, error) {
+	req, err := s.client.NewRequest("GET", "users", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var usr []*User
+	resp, err := s.client.Do(req, &usr)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return usr, resp, err
+}
+
+// GetUser gets a single user.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#single-user
+func (s *UsersService) GetUser(user int, options ...OptionFunc) (*User, *Response, error) {
+	u := fmt.Sprintf("users/%d", user)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	usr := new(User)
+	resp, err := s.client.Do(req, usr)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return usr, resp, err
+}
+
+// CreateUserOptions represents the available CreateUser() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#user-creation
+type CreateUserOptions struct {
+	Email            *string `url:"email,omitempty" json:"email,omitempty"`
+	Password         *string `url:"password,omitempty" json:"password,omitempty"`
+	ResetPassword    *bool   `url:"reset_password,omitempty" json:"reset_password,omitempty"`
+	Username         *string `url:"username,omitempty" json:"username,omitempty"`
+	Name             *string `url:"name,omitempty" json:"name,omitempty"`
+	Skype            *string `url:"skype,omitempty" json:"skype,omitempty"`
+	Linkedin         *string `url:"linkedin,omitempty" json:"linkedin,omitempty"`
+	Twitter          *string `url:"twitter,omitempty" json:"twitter,omitempty"`
+	WebsiteURL       *string `url:"website_url,omitempty" json:"website_url,omitempty"`
+	Organization     *string `url:"organization,omitempty" json:"organization,omitempty"`
+	ProjectsLimit    *int    `url:"projects_limit,omitempty" json:"projects_limit,omitempty"`
+	ExternUID        *string `url:"extern_uid,omitempty" json:"extern_uid,omitempty"`
+	Provider         *string `url:"provider,omitempty" json:"provider,omitempty"`
+	Bio              *string `url:"bio,omitempty" json:"bio,omitempty"`
+	Location         *string `url:"location,omitempty" json:"location,omitempty"`
+	Admin            *bool   `url:"admin,omitempty" json:"admin,omitempty"`
+	CanCreateGroup   *bool   `url:"can_create_group,omitempty" json:"can_create_group,omitempty"`
+	SkipConfirmation *bool   `url:"skip_confirmation,omitempty" json:"skip_confirmation,omitempty"`
+	External         *bool   `url:"external,omitempty" json:"external,omitempty"`
+}
+
+// CreateUser creates a new user. Note only administrators can create new users.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#user-creation
+func (s *UsersService) CreateUser(opt *CreateUserOptions, options ...OptionFunc) (*User, *Response, error) {
+	req, err := s.client.NewRequest("POST", "users", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	usr := new(User)
+	resp, err := s.client.Do(req, usr)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return usr, resp, err
+}
+
+// ModifyUserOptions represents the available ModifyUser() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#user-modification
+type ModifyUserOptions struct {
+	Email              *string `url:"email,omitempty" json:"email,omitempty"`
+	Password           *string `url:"password,omitempty" json:"password,omitempty"`
+	Username           *string `url:"username,omitempty" json:"username,omitempty"`
+	Name               *string `url:"name,omitempty" json:"name,omitempty"`
+	Skype              *string `url:"skype,omitempty" json:"skype,omitempty"`
+	Linkedin           *string `url:"linkedin,omitempty" json:"linkedin,omitempty"`
+	Twitter            *string `url:"twitter,omitempty" json:"twitter,omitempty"`
+	WebsiteURL         *string `url:"website_url,omitempty" json:"website_url,omitempty"`
+	Organization       *string `url:"organization,omitempty" json:"organization,omitempty"`
+	ProjectsLimit      *int    `url:"projects_limit,omitempty" json:"projects_limit,omitempty"`
+	ExternUID          *string `url:"extern_uid,omitempty" json:"extern_uid,omitempty"`
+	Provider           *string `url:"provider,omitempty" json:"provider,omitempty"`
+	Bio                *string `url:"bio,omitempty" json:"bio,omitempty"`
+	Location           *string `url:"location,omitempty" json:"location,omitempty"`
+	Admin              *bool   `url:"admin,omitempty" json:"admin,omitempty"`
+	CanCreateGroup     *bool   `url:"can_create_group,omitempty" json:"can_create_group,omitempty"`
+	SkipReconfirmation *bool   `url:"skip_reconfirmation,omitempty" json:"skip_reconfirmation,omitempty"`
+	External           *bool   `url:"external,omitempty" json:"external,omitempty"`
+}
+
+// ModifyUser modifies an existing user. Only administrators can change attributes
+// of a user.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#user-modification
+func (s *UsersService) ModifyUser(user int, opt *ModifyUserOptions, options ...OptionFunc) (*User, *Response, error) {
+	u := fmt.Sprintf("users/%d", user)
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	usr := new(User)
+	resp, err := s.client.Do(req, usr)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return usr, resp, err
+}
+
+// DeleteUser deletes a user. Available only for administrators. This is an
+// idempotent function, calling this function for a non-existent user id still
+// returns a status code 200 OK. The JSON response differs if the user was
+// actually deleted or not. In the former the user is returned and in the
+// latter not.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#user-deletion
+func (s *UsersService) DeleteUser(user int, options ...OptionFunc) (*Response, error) {
+	u := fmt.Sprintf("users/%d", user)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// CurrentUser gets currently authenticated user.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#current-user
+func (s *UsersService) CurrentUser(options ...OptionFunc) (*User, *Response, error) {
+	req, err := s.client.NewRequest("GET", "user", nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	usr := new(User)
+	resp, err := s.client.Do(req, usr)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return usr, resp, err
+}
+
+// SSHKey represents a SSH key.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#list-ssh-keys
+type SSHKey struct {
+	ID        int        `json:"id"`
+	Title     string     `json:"title"`
+	Key       string     `json:"key"`
+	CreatedAt *time.Time `json:"created_at"`
+}
+
+// ListSSHKeys gets a list of currently authenticated user's SSH keys.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#list-ssh-keys
+func (s *UsersService) ListSSHKeys(options ...OptionFunc) ([]*SSHKey, *Response, error) {
+	req, err := s.client.NewRequest("GET", "user/keys", nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var k []*SSHKey
+	resp, err := s.client.Do(req, &k)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return k, resp, err
+}
+
+// ListSSHKeysForUserOptions represents the available ListSSHKeysForUser() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#list-ssh-keys-for-user
+type ListSSHKeysForUserOptions ListOptions
+
+// ListSSHKeysForUser gets a list of a specified user's SSH keys. Available
+// only for admin
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#list-ssh-keys-for-user
+func (s *UsersService) ListSSHKeysForUser(user int, opt *ListSSHKeysForUserOptions, options ...OptionFunc) ([]*SSHKey, *Response, error) {
+	u := fmt.Sprintf("users/%d/keys", user)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var k []*SSHKey
+	resp, err := s.client.Do(req, &k)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return k, resp, err
+}
+
+// GetSSHKey gets a single key.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#single-ssh-key
+func (s *UsersService) GetSSHKey(key int, options ...OptionFunc) (*SSHKey, *Response, error) {
+	u := fmt.Sprintf("user/keys/%d", key)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	k := new(SSHKey)
+	resp, err := s.client.Do(req, k)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return k, resp, err
+}
+
+// AddSSHKeyOptions represents the available AddSSHKey() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#add-ssh-key
+type AddSSHKeyOptions struct {
+	Title *string `url:"title,omitempty" json:"title,omitempty"`
+	Key   *string `url:"key,omitempty" json:"key,omitempty"`
+}
+
+// AddSSHKey creates a new key owned by the currently authenticated user.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#add-ssh-key
+func (s *UsersService) AddSSHKey(opt *AddSSHKeyOptions, options ...OptionFunc) (*SSHKey, *Response, error) {
+	req, err := s.client.NewRequest("POST", "user/keys", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	k := new(SSHKey)
+	resp, err := s.client.Do(req, k)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return k, resp, err
+}
+
+// AddSSHKeyForUser creates new key owned by specified user. Available only for
+// admin.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#add-ssh-key-for-user
+func (s *UsersService) AddSSHKeyForUser(user int, opt *AddSSHKeyOptions, options ...OptionFunc) (*SSHKey, *Response, error) {
+	u := fmt.Sprintf("users/%d/keys", user)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	k := new(SSHKey)
+	resp, err := s.client.Do(req, k)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return k, resp, err
+}
+
+// DeleteSSHKey deletes key owned by currently authenticated user. This is an
+// idempotent function and calling it on a key that is already deleted or not
+// available results in 200 OK.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#delete-ssh-key-for-current-owner
+func (s *UsersService) DeleteSSHKey(key int, options ...OptionFunc) (*Response, error) {
+	u := fmt.Sprintf("user/keys/%d", key)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// DeleteSSHKeyForUser deletes key owned by a specified user. Available only
+// for admin.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#delete-ssh-key-for-given-user
+func (s *UsersService) DeleteSSHKeyForUser(user, key int, options ...OptionFunc) (*Response, error) {
+	u := fmt.Sprintf("users/%d/keys/%d", user, key)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// BlockUser blocks the specified user. Available only for admin.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#block-user
+func (s *UsersService) BlockUser(user int, options ...OptionFunc) error {
+	u := fmt.Sprintf("users/%d/block", user)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return err
+	}
+
+	resp, err := s.client.Do(req, nil)
+	if err != nil {
+		return err
+	}
+
+	switch resp.StatusCode {
+	case 201:
+		return nil
+	case 403:
+		return errors.New("Cannot block a user that is already blocked by LDAP synchronization")
+	case 404:
+		return errors.New("User does not exist")
+	default:
+		return fmt.Errorf("Received unexpected result code: %d", resp.StatusCode)
+	}
+}
+
+// UnblockUser unblocks the specified user. Available only for admin.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#unblock-user
+func (s *UsersService) UnblockUser(user int, options ...OptionFunc) error {
+	u := fmt.Sprintf("users/%d/unblock", user)
+
+	req, err := s.client.NewRequest("POST", u, nil, options)
+	if err != nil {
+		return err
+	}
+
+	resp, err := s.client.Do(req, nil)
+	if err != nil {
+		return err
+	}
+
+	switch resp.StatusCode {
+	case 201:
+		return nil
+	case 403:
+		return errors.New("Cannot unblock a user that is blocked by LDAP synchronization")
+	case 404:
+		return errors.New("User does not exist")
+	default:
+		return fmt.Errorf("Received unexpected result code: %d", resp.StatusCode)
+	}
+}
+
+// Email represents an Email.
+//
+// GitLab API docs: https://doc.gitlab.com/ce/api/users.html#list-emails
+type Email struct {
+	ID    int    `json:"id"`
+	Email string `json:"email"`
+}
+
+// ListEmails gets a list of currently authenticated user's Emails.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#list-emails
+func (s *UsersService) ListEmails(options ...OptionFunc) ([]*Email, *Response, error) {
+	req, err := s.client.NewRequest("GET", "user/emails", nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var e []*Email
+	resp, err := s.client.Do(req, &e)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return e, resp, err
+}
+
+// ListEmailsForUserOptions represents the available ListEmailsForUser() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#list-emails-for-user
+type ListEmailsForUserOptions ListOptions
+
+// ListEmailsForUser gets a list of a specified user's Emails. Available
+// only for admin
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#list-emails-for-user
+func (s *UsersService) ListEmailsForUser(user int, opt *ListEmailsForUserOptions, options ...OptionFunc) ([]*Email, *Response, error) {
+	u := fmt.Sprintf("users/%d/emails", user)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var e []*Email
+	resp, err := s.client.Do(req, &e)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return e, resp, err
+}
+
+// GetEmail gets a single email.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#single-email
+func (s *UsersService) GetEmail(email int, options ...OptionFunc) (*Email, *Response, error) {
+	u := fmt.Sprintf("user/emails/%d", email)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	e := new(Email)
+	resp, err := s.client.Do(req, e)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return e, resp, err
+}
+
+// AddEmailOptions represents the available AddEmail() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#add-email
+type AddEmailOptions struct {
+	Email *string `url:"email,omitempty" json:"email,omitempty"`
+}
+
+// AddEmail creates a new email owned by the currently authenticated user.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#add-email
+func (s *UsersService) AddEmail(opt *AddEmailOptions, options ...OptionFunc) (*Email, *Response, error) {
+	req, err := s.client.NewRequest("POST", "user/emails", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	e := new(Email)
+	resp, err := s.client.Do(req, e)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return e, resp, err
+}
+
+// AddEmailForUser creates new email owned by specified user. Available only for
+// admin.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/users.html#add-email-for-user
+func (s *UsersService) AddEmailForUser(user int, opt *AddEmailOptions, options ...OptionFunc) (*Email, *Response, error) {
+	u := fmt.Sprintf("users/%d/emails", user)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	e := new(Email)
+	resp, err := s.client.Do(req, e)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return e, resp, err
+}
+
+// DeleteEmail deletes email owned by currently authenticated user. This is an
+// idempotent function and calling it on a key that is already deleted or not
+// available results in 200 OK.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#delete-email-for-current-owner
+func (s *UsersService) DeleteEmail(email int, options ...OptionFunc) (*Response, error) {
+	u := fmt.Sprintf("user/emails/%d", email)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// DeleteEmailForUser deletes email owned by a specified user. Available only
+// for admin.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#delete-email-for-given-user
+func (s *UsersService) DeleteEmailForUser(user, email int, options ...OptionFunc) (*Response, error) {
+	u := fmt.Sprintf("users/%d/emails/%d", user, email)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// ImpersonationToken represents an impersonation token.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#get-all-impersonation-tokens-of-a-user
+type ImpersonationToken struct {
+	ID        int        `json:"id"`
+	Name      string     `json:"name"`
+	Active    bool       `json:"active"`
+	Token     string     `json:"token"`
+	Scopes    []string   `json:"scopes"`
+	Revoked   bool       `json:"revoked"`
+	CreatedAt *time.Time `json:"created_at"`
+	ExpiresAt *ISOTime   `json:"expires_at"`
+}
+
+// GetAllImpersonationTokensOptions represents the available
+// GetAllImpersonationTokens() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#get-all-impersonation-tokens-of-a-user
+type GetAllImpersonationTokensOptions struct {
+	ListOptions
+	State *string `url:"state,omitempty" json:"state,omitempty"`
+}
+
+// GetAllImpersonationTokens retrieves all impersonation tokens of a user.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#get-all-impersonation-tokens-of-a-user
+func (s *UsersService) GetAllImpersonationTokens(user int, opt *GetAllImpersonationTokensOptions, options ...OptionFunc) ([]*ImpersonationToken, *Response, error) {
+	u := fmt.Sprintf("users/%d/impersonation_tokens", user)
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var ts []*ImpersonationToken
+	resp, err := s.client.Do(req, &ts)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return ts, resp, err
+}
+
+// GetImpersonationToken retrieves an impersonation token of a user.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#get-an-impersonation-token-of-a-user
+func (s *UsersService) GetImpersonationToken(user, token int, options ...OptionFunc) (*ImpersonationToken, *Response, error) {
+	u := fmt.Sprintf("users/%d/impersonation_tokens/%d", user, token)
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	t := new(ImpersonationToken)
+	resp, err := s.client.Do(req, &t)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return t, resp, err
+}
+
+// CreateImpersonationTokenOptions represents the available
+// CreateImpersonationToken() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#create-an-impersonation-token
+type CreateImpersonationTokenOptions struct {
+	Name      *string    `url:"name,omitempty" json:"name,omitempty"`
+	Scopes    *[]string  `url:"scopes,omitempty" json:"scopes,omitempty"`
+	ExpiresAt *time.Time `url:"expires_at,omitempty" json:"expires_at,omitempty"`
+}
+
+// CreateImpersonationToken creates an impersonation token.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#create-an-impersonation-token
+func (s *UsersService) CreateImpersonationToken(user int, opt *CreateImpersonationTokenOptions, options ...OptionFunc) (*ImpersonationToken, *Response, error) {
+	u := fmt.Sprintf("users/%d/impersonation_tokens", user)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	t := new(ImpersonationToken)
+	resp, err := s.client.Do(req, &t)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return t, resp, err
+}
+
+// RevokeImpersonationToken revokes an impersonation token.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#revoke-an-impersonation-token
+func (s *UsersService) RevokeImpersonationToken(user, token int, options ...OptionFunc) (*Response, error) {
+	u := fmt.Sprintf("users/%d/impersonation_tokens/%d", user, token)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// UserActivity represents an entry in the user/activities response
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#get-user-activities-admin-only
+type UserActivity struct {
+	Username       string   `json:"username"`
+	LastActivityOn *ISOTime `json:"last_activity_on"`
+}
+
+// GetUserActivitiesOptions represents the options for GetUserActivities
+//
+// GitLap API docs:
+// https://docs.gitlab.com/ce/api/users.html#get-user-activities-admin-only
+type GetUserActivitiesOptions struct {
+	From *ISOTime `url:"from,omitempty" json:"from,omitempty"`
+}
+
+// GetUserActivities retrieves user activities (admin only)
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/users.html#get-user-activities-admin-only
+func (s *UsersService) GetUserActivities(opt *GetUserActivitiesOptions, options ...OptionFunc) ([]*UserActivity, *Response, error) {
+	req, err := s.client.NewRequest("GET", "user/activities", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var t []*UserActivity
+	resp, err := s.client.Do(req, &t)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return t, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/validate.go b/vendor/github.com/xanzy/go-gitlab/validate.go
new file mode 100644
index 0000000..a88e188
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/validate.go
@@ -0,0 +1,40 @@
+package gitlab
+
+// ValidateService handles communication with the validation related methods of
+// the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/lint.html
+type ValidateService struct {
+	client *Client
+}
+
+// LintResult represents the linting results.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/lint.html
+type LintResult struct {
+	Status string   `json:"status"`
+	Errors []string `json:"errors"`
+}
+
+// Lint validates .gitlab-ci.yml content.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/lint.html
+func (s *ValidateService) Lint(content string, options ...OptionFunc) (*LintResult, *Response, error) {
+	var opts struct {
+		Content string `url:"content,omitempty" json:"content,omitempty"`
+	}
+	opts.Content = content
+
+	req, err := s.client.NewRequest("POST", "ci/lint", &opts, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	l := new(LintResult)
+	resp, err := s.client.Do(req, l)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return l, resp, nil
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/version.go b/vendor/github.com/xanzy/go-gitlab/version.go
new file mode 100644
index 0000000..f1a3a7f
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/version.go
@@ -0,0 +1,56 @@
+//
+// Copyright 2017, Andrea Funto'
+//
+// 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 gitlab
+
+// VersionService handles communication with the GitLab server instance to
+// retrieve its version information via the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/version.md
+type VersionService struct {
+	client *Client
+}
+
+// Version represents a GitLab instance version.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/version.md
+type Version struct {
+	Version  string `json:"version"`
+	Revision string `json:"revision"`
+}
+
+func (s Version) String() string {
+	return Stringify(s)
+}
+
+// GetVersion gets a GitLab server instance version; it is only available to
+// authenticated users.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/version.md
+func (s *VersionService) GetVersion() (*Version, *Response, error) {
+	req, err := s.client.NewRequest("GET", "version", nil, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	v := new(Version)
+	resp, err := s.client.Do(req, v)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return v, resp, err
+}
diff --git a/vendor/github.com/xanzy/go-gitlab/wikis.go b/vendor/github.com/xanzy/go-gitlab/wikis.go
new file mode 100644
index 0000000..7288985
--- /dev/null
+++ b/vendor/github.com/xanzy/go-gitlab/wikis.go
@@ -0,0 +1,204 @@
+// Copyright 2017, Stany MARCEL
+//
+// 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 gitlab
+
+import (
+	"fmt"
+	"net/url"
+)
+
+// WikisService handles communication with the wikis related methods of
+// the Gitlab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/wikis.html
+type WikisService struct {
+	client *Client
+}
+
+// WikiFormat represents the available wiki formats.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/wikis.html
+type WikiFormat string
+
+// The available wiki formats.
+const (
+	WikiFormatMarkdown WikiFormat = "markdown"
+	WikiFormatRFoc     WikiFormat = "rdoc"
+	WikiFormatASCIIDoc WikiFormat = "asciidoc"
+)
+
+// Wiki represents a GitLab wiki.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/wikis.html
+type Wiki struct {
+	Content string     `json:"content"`
+	Format  WikiFormat `json:"format"`
+	Slug    string     `json:"slug"`
+	Title   string     `json:"title"`
+}
+
+func (w Wiki) String() string {
+	return Stringify(w)
+}
+
+// ListWikisOptions represents the available ListWikis options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/wikis.html#list-wiki-pages
+type ListWikisOptions struct {
+	WithContent *bool `url:"with_content,omitempty" json:"with_content,omitempty"`
+}
+
+// ListWikis lists all pages of the wiki of the given project id.
+// When with_content is set, it also returns the content of the pages.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/wikis.html#list-wiki-pages
+func (s *WikisService) ListWikis(pid interface{}, opt *ListWikisOptions, options ...OptionFunc) ([]*Wiki, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/wikis", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var w []*Wiki
+	resp, err := s.client.Do(req, &w)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return w, resp, err
+}
+
+// GetWikiPage gets a wiki page for a given project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/wikis.html#get-a-wiki-page
+func (s *WikisService) GetWikiPage(pid interface{}, slug string, options ...OptionFunc) (*Wiki, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/wikis/%s", url.QueryEscape(project), url.QueryEscape(slug))
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var w *Wiki
+	resp, err := s.client.Do(req, &w)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return w, resp, err
+}
+
+// CreateWikiPageOptions represents options to CreateWikiPage.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/wikis.html#create-a-new-wiki-page
+type CreateWikiPageOptions struct {
+	Content *string `url:"content" json:"content"`
+	Title   *string `url:"title" json:"title"`
+	Format  *string `url:"format,omitempty" json:"format,omitempty"`
+}
+
+// CreateWikiPage creates a new wiki page for the given repository with
+// the given title, slug, and content.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/wikis.html#create-a-new-wiki-page
+func (s *WikisService) CreateWikiPage(pid interface{}, opt *CreateWikiPageOptions, options ...OptionFunc) (*Wiki, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/wikis", url.QueryEscape(project))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	w := new(Wiki)
+	resp, err := s.client.Do(req, w)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return w, resp, err
+}
+
+// EditWikiPageOptions represents options to EditWikiPage.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/wikis.html#edit-an-existing-wiki-page
+type EditWikiPageOptions struct {
+	Content *string `url:"content" json:"content"`
+	Title   *string `url:"title" json:"title"`
+	Format  *string `url:"format,omitempty" json:"format,omitempty"`
+}
+
+// EditWikiPage Updates an existing wiki page. At least one parameter is
+// required to update the wiki page.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/wikis.html#edit-an-existing-wiki-page
+func (s *WikisService) EditWikiPage(pid interface{}, slug string, opt *EditWikiPageOptions, options ...OptionFunc) (*Wiki, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/wikis/%s", url.QueryEscape(project), url.QueryEscape(slug))
+
+	req, err := s.client.NewRequest("PUT", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	w := new(Wiki)
+	resp, err := s.client.Do(req, w)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return w, resp, err
+}
+
+// DeleteWikiPage deletes a wiki page with a given slug.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/wikis.html#delete-a-wiki-page
+func (s *WikisService) DeleteWikiPage(pid interface{}, slug string, options ...OptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/wikis/%s", url.QueryEscape(project), url.QueryEscape(slug))
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/vendor/golang.org/x/net/AUTHORS b/vendor/golang.org/x/net/AUTHORS
new file mode 100644
index 0000000..15167cd
--- /dev/null
+++ b/vendor/golang.org/x/net/AUTHORS
@@ -0,0 +1,3 @@
+# This source code refers to The Go Authors for copyright purposes.
+# The master list of authors is in the main Go distribution,
+# visible at http://tip.golang.org/AUTHORS.
diff --git a/vendor/golang.org/x/net/CONTRIBUTORS b/vendor/golang.org/x/net/CONTRIBUTORS
new file mode 100644
index 0000000..1c4577e
--- /dev/null
+++ b/vendor/golang.org/x/net/CONTRIBUTORS
@@ -0,0 +1,3 @@
+# This source code was written by the Go contributors.
+# The master list of contributors is in the main Go distribution,
+# visible at http://tip.golang.org/CONTRIBUTORS.
diff --git a/vendor/golang.org/x/net/LICENSE b/vendor/golang.org/x/net/LICENSE
new file mode 100644
index 0000000..6a66aea
--- /dev/null
+++ b/vendor/golang.org/x/net/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/golang.org/x/net/PATENTS b/vendor/golang.org/x/net/PATENTS
new file mode 100644
index 0000000..7330990
--- /dev/null
+++ b/vendor/golang.org/x/net/PATENTS
@@ -0,0 +1,22 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of the Go project.
+
+Google hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section)
+patent license to make, have made, use, offer to sell, sell, import,
+transfer and otherwise run, modify and propagate the contents of this
+implementation of Go, where such license applies only to those patent
+claims, both currently owned or controlled by Google and acquired in
+the future, licensable by Google that are necessarily infringed by this
+implementation of Go.  This grant does not include claims that would be
+infringed only as a consequence of further modification of this
+implementation.  If you or your agent or exclusive licensee institute or
+order or agree to the institution of patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that this implementation of Go or any code incorporated within this
+implementation of Go constitutes direct or contributory patent
+infringement, or inducement of patent infringement, then any patent
+rights granted to you under this License for this implementation of Go
+shall terminate as of the date such litigation is filed.
diff --git a/vendor/golang.org/x/net/context/context.go b/vendor/golang.org/x/net/context/context.go
new file mode 100644
index 0000000..a3c021d
--- /dev/null
+++ b/vendor/golang.org/x/net/context/context.go
@@ -0,0 +1,56 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package context defines the Context type, which carries deadlines,
+// cancelation signals, and other request-scoped values across API boundaries
+// and between processes.
+// As of Go 1.7 this package is available in the standard library under the
+// name context.  https://golang.org/pkg/context.
+//
+// Incoming requests to a server should create a Context, and outgoing calls to
+// servers should accept a Context. The chain of function calls between must
+// propagate the Context, optionally replacing it with a modified copy created
+// using WithDeadline, WithTimeout, WithCancel, or WithValue.
+//
+// Programs that use Contexts should follow these rules to keep interfaces
+// consistent across packages and enable static analysis tools to check context
+// propagation:
+//
+// Do not store Contexts inside a struct type; instead, pass a Context
+// explicitly to each function that needs it. The Context should be the first
+// parameter, typically named ctx:
+//
+// 	func DoSomething(ctx context.Context, arg Arg) error {
+// 		// ... use ctx ...
+// 	}
+//
+// Do not pass a nil Context, even if a function permits it. Pass context.TODO
+// if you are unsure about which Context to use.
+//
+// Use context Values only for request-scoped data that transits processes and
+// APIs, not for passing optional parameters to functions.
+//
+// The same Context may be passed to functions running in different goroutines;
+// Contexts are safe for simultaneous use by multiple goroutines.
+//
+// See http://blog.golang.org/context for example code for a server that uses
+// Contexts.
+package context // import "golang.org/x/net/context"
+
+// Background returns a non-nil, empty Context. It is never canceled, has no
+// values, and has no deadline. It is typically used by the main function,
+// initialization, and tests, and as the top-level Context for incoming
+// requests.
+func Background() Context {
+	return background
+}
+
+// TODO returns a non-nil, empty Context. Code should use context.TODO when
+// it's unclear which Context to use or it is not yet available (because the
+// surrounding function has not yet been extended to accept a Context
+// parameter).  TODO is recognized by static analysis tools that determine
+// whether Contexts are propagated correctly in a program.
+func TODO() Context {
+	return todo
+}
diff --git a/vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go b/vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go
new file mode 100644
index 0000000..606cf1f
--- /dev/null
+++ b/vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go
@@ -0,0 +1,74 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.7
+
+// Package ctxhttp provides helper functions for performing context-aware HTTP requests.
+package ctxhttp // import "golang.org/x/net/context/ctxhttp"
+
+import (
+	"io"
+	"net/http"
+	"net/url"
+	"strings"
+
+	"golang.org/x/net/context"
+)
+
+// Do sends an HTTP request with the provided http.Client and returns
+// an HTTP response.
+//
+// If the client is nil, http.DefaultClient is used.
+//
+// The provided ctx must be non-nil. If it is canceled or times out,
+// ctx.Err() will be returned.
+func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
+	if client == nil {
+		client = http.DefaultClient
+	}
+	resp, err := client.Do(req.WithContext(ctx))
+	// If we got an error, and the context has been canceled,
+	// the context's error is probably more useful.
+	if err != nil {
+		select {
+		case <-ctx.Done():
+			err = ctx.Err()
+		default:
+		}
+	}
+	return resp, err
+}
+
+// Get issues a GET request via the Do function.
+func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		return nil, err
+	}
+	return Do(ctx, client, req)
+}
+
+// Head issues a HEAD request via the Do function.
+func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
+	req, err := http.NewRequest("HEAD", url, nil)
+	if err != nil {
+		return nil, err
+	}
+	return Do(ctx, client, req)
+}
+
+// Post issues a POST request via the Do function.
+func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) {
+	req, err := http.NewRequest("POST", url, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header.Set("Content-Type", bodyType)
+	return Do(ctx, client, req)
+}
+
+// PostForm issues a POST request via the Do function.
+func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
+	return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
+}
diff --git a/vendor/golang.org/x/net/context/ctxhttp/ctxhttp_pre17.go b/vendor/golang.org/x/net/context/ctxhttp/ctxhttp_pre17.go
new file mode 100644
index 0000000..926870c
--- /dev/null
+++ b/vendor/golang.org/x/net/context/ctxhttp/ctxhttp_pre17.go
@@ -0,0 +1,147 @@
+// 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.
+
+// +build !go1.7
+
+package ctxhttp // import "golang.org/x/net/context/ctxhttp"
+
+import (
+	"io"
+	"net/http"
+	"net/url"
+	"strings"
+
+	"golang.org/x/net/context"
+)
+
+func nop() {}
+
+var (
+	testHookContextDoneBeforeHeaders = nop
+	testHookDoReturned               = nop
+	testHookDidBodyClose             = nop
+)
+
+// Do sends an HTTP request with the provided http.Client and returns an HTTP response.
+// If the client is nil, http.DefaultClient is used.
+// If the context is canceled or times out, ctx.Err() will be returned.
+func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
+	if client == nil {
+		client = http.DefaultClient
+	}
+
+	// TODO(djd): Respect any existing value of req.Cancel.
+	cancel := make(chan struct{})
+	req.Cancel = cancel
+
+	type responseAndError struct {
+		resp *http.Response
+		err  error
+	}
+	result := make(chan responseAndError, 1)
+
+	// Make local copies of test hooks closed over by goroutines below.
+	// Prevents data races in tests.
+	testHookDoReturned := testHookDoReturned
+	testHookDidBodyClose := testHookDidBodyClose
+
+	go func() {
+		resp, err := client.Do(req)
+		testHookDoReturned()
+		result <- responseAndError{resp, err}
+	}()
+
+	var resp *http.Response
+
+	select {
+	case <-ctx.Done():
+		testHookContextDoneBeforeHeaders()
+		close(cancel)
+		// Clean up after the goroutine calling client.Do:
+		go func() {
+			if r := <-result; r.resp != nil {
+				testHookDidBodyClose()
+				r.resp.Body.Close()
+			}
+		}()
+		return nil, ctx.Err()
+	case r := <-result:
+		var err error
+		resp, err = r.resp, r.err
+		if err != nil {
+			return resp, err
+		}
+	}
+
+	c := make(chan struct{})
+	go func() {
+		select {
+		case <-ctx.Done():
+			close(cancel)
+		case <-c:
+			// The response's Body is closed.
+		}
+	}()
+	resp.Body = &notifyingReader{resp.Body, c}
+
+	return resp, nil
+}
+
+// Get issues a GET request via the Do function.
+func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		return nil, err
+	}
+	return Do(ctx, client, req)
+}
+
+// Head issues a HEAD request via the Do function.
+func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
+	req, err := http.NewRequest("HEAD", url, nil)
+	if err != nil {
+		return nil, err
+	}
+	return Do(ctx, client, req)
+}
+
+// Post issues a POST request via the Do function.
+func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) {
+	req, err := http.NewRequest("POST", url, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header.Set("Content-Type", bodyType)
+	return Do(ctx, client, req)
+}
+
+// PostForm issues a POST request via the Do function.
+func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
+	return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
+}
+
+// notifyingReader is an io.ReadCloser that closes the notify channel after
+// Close is called or a Read fails on the underlying ReadCloser.
+type notifyingReader struct {
+	io.ReadCloser
+	notify chan<- struct{}
+}
+
+func (r *notifyingReader) Read(p []byte) (int, error) {
+	n, err := r.ReadCloser.Read(p)
+	if err != nil && r.notify != nil {
+		close(r.notify)
+		r.notify = nil
+	}
+	return n, err
+}
+
+func (r *notifyingReader) Close() error {
+	err := r.ReadCloser.Close()
+	if r.notify != nil {
+		close(r.notify)
+		r.notify = nil
+	}
+	return err
+}
diff --git a/vendor/golang.org/x/net/context/go17.go b/vendor/golang.org/x/net/context/go17.go
new file mode 100644
index 0000000..d20f52b
--- /dev/null
+++ b/vendor/golang.org/x/net/context/go17.go
@@ -0,0 +1,72 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.7
+
+package context
+
+import (
+	"context" // standard library's context, as of Go 1.7
+	"time"
+)
+
+var (
+	todo       = context.TODO()
+	background = context.Background()
+)
+
+// Canceled is the error returned by Context.Err when the context is canceled.
+var Canceled = context.Canceled
+
+// DeadlineExceeded is the error returned by Context.Err when the context's
+// deadline passes.
+var DeadlineExceeded = context.DeadlineExceeded
+
+// WithCancel returns a copy of parent with a new Done channel. The returned
+// context's Done channel is closed when the returned cancel function is called
+// or when the parent context's Done channel is closed, whichever happens first.
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete.
+func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
+	ctx, f := context.WithCancel(parent)
+	return ctx, CancelFunc(f)
+}
+
+// WithDeadline returns a copy of the parent context with the deadline adjusted
+// to be no later than d. If the parent's deadline is already earlier than d,
+// WithDeadline(parent, d) is semantically equivalent to parent. The returned
+// context's Done channel is closed when the deadline expires, when the returned
+// cancel function is called, or when the parent context's Done channel is
+// closed, whichever happens first.
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete.
+func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
+	ctx, f := context.WithDeadline(parent, deadline)
+	return ctx, CancelFunc(f)
+}
+
+// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete:
+//
+// 	func slowOperationWithTimeout(ctx context.Context) (Result, error) {
+// 		ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
+// 		defer cancel()  // releases resources if slowOperation completes before timeout elapses
+// 		return slowOperation(ctx)
+// 	}
+func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
+	return WithDeadline(parent, time.Now().Add(timeout))
+}
+
+// WithValue returns a copy of parent in which the value associated with key is
+// val.
+//
+// Use context Values only for request-scoped data that transits processes and
+// APIs, not for passing optional parameters to functions.
+func WithValue(parent Context, key interface{}, val interface{}) Context {
+	return context.WithValue(parent, key, val)
+}
diff --git a/vendor/golang.org/x/net/context/go19.go b/vendor/golang.org/x/net/context/go19.go
new file mode 100644
index 0000000..d88bd1d
--- /dev/null
+++ b/vendor/golang.org/x/net/context/go19.go
@@ -0,0 +1,20 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.9
+
+package context
+
+import "context" // standard library's context, as of Go 1.7
+
+// A Context carries a deadline, a cancelation signal, and other values across
+// API boundaries.
+//
+// Context's methods may be called by multiple goroutines simultaneously.
+type Context = context.Context
+
+// A CancelFunc tells an operation to abandon its work.
+// A CancelFunc does not wait for the work to stop.
+// After the first call, subsequent calls to a CancelFunc do nothing.
+type CancelFunc = context.CancelFunc
diff --git a/vendor/golang.org/x/net/context/pre_go17.go b/vendor/golang.org/x/net/context/pre_go17.go
new file mode 100644
index 0000000..0f35592
--- /dev/null
+++ b/vendor/golang.org/x/net/context/pre_go17.go
@@ -0,0 +1,300 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.7
+
+package context
+
+import (
+	"errors"
+	"fmt"
+	"sync"
+	"time"
+)
+
+// An emptyCtx is never canceled, has no values, and has no deadline. It is not
+// struct{}, since vars of this type must have distinct addresses.
+type emptyCtx int
+
+func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
+	return
+}
+
+func (*emptyCtx) Done() <-chan struct{} {
+	return nil
+}
+
+func (*emptyCtx) Err() error {
+	return nil
+}
+
+func (*emptyCtx) Value(key interface{}) interface{} {
+	return nil
+}
+
+func (e *emptyCtx) String() string {
+	switch e {
+	case background:
+		return "context.Background"
+	case todo:
+		return "context.TODO"
+	}
+	return "unknown empty Context"
+}
+
+var (
+	background = new(emptyCtx)
+	todo       = new(emptyCtx)
+)
+
+// Canceled is the error returned by Context.Err when the context is canceled.
+var Canceled = errors.New("context canceled")
+
+// DeadlineExceeded is the error returned by Context.Err when the context's
+// deadline passes.
+var DeadlineExceeded = errors.New("context deadline exceeded")
+
+// WithCancel returns a copy of parent with a new Done channel. The returned
+// context's Done channel is closed when the returned cancel function is called
+// or when the parent context's Done channel is closed, whichever happens first.
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete.
+func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
+	c := newCancelCtx(parent)
+	propagateCancel(parent, c)
+	return c, func() { c.cancel(true, Canceled) }
+}
+
+// newCancelCtx returns an initialized cancelCtx.
+func newCancelCtx(parent Context) *cancelCtx {
+	return &cancelCtx{
+		Context: parent,
+		done:    make(chan struct{}),
+	}
+}
+
+// propagateCancel arranges for child to be canceled when parent is.
+func propagateCancel(parent Context, child canceler) {
+	if parent.Done() == nil {
+		return // parent is never canceled
+	}
+	if p, ok := parentCancelCtx(parent); ok {
+		p.mu.Lock()
+		if p.err != nil {
+			// parent has already been canceled
+			child.cancel(false, p.err)
+		} else {
+			if p.children == nil {
+				p.children = make(map[canceler]bool)
+			}
+			p.children[child] = true
+		}
+		p.mu.Unlock()
+	} else {
+		go func() {
+			select {
+			case <-parent.Done():
+				child.cancel(false, parent.Err())
+			case <-child.Done():
+			}
+		}()
+	}
+}
+
+// parentCancelCtx follows a chain of parent references until it finds a
+// *cancelCtx. This function understands how each of the concrete types in this
+// package represents its parent.
+func parentCancelCtx(parent Context) (*cancelCtx, bool) {
+	for {
+		switch c := parent.(type) {
+		case *cancelCtx:
+			return c, true
+		case *timerCtx:
+			return c.cancelCtx, true
+		case *valueCtx:
+			parent = c.Context
+		default:
+			return nil, false
+		}
+	}
+}
+
+// removeChild removes a context from its parent.
+func removeChild(parent Context, child canceler) {
+	p, ok := parentCancelCtx(parent)
+	if !ok {
+		return
+	}
+	p.mu.Lock()
+	if p.children != nil {
+		delete(p.children, child)
+	}
+	p.mu.Unlock()
+}
+
+// A canceler is a context type that can be canceled directly. The
+// implementations are *cancelCtx and *timerCtx.
+type canceler interface {
+	cancel(removeFromParent bool, err error)
+	Done() <-chan struct{}
+}
+
+// A cancelCtx can be canceled. When canceled, it also cancels any children
+// that implement canceler.
+type cancelCtx struct {
+	Context
+
+	done chan struct{} // closed by the first cancel call.
+
+	mu       sync.Mutex
+	children map[canceler]bool // set to nil by the first cancel call
+	err      error             // set to non-nil by the first cancel call
+}
+
+func (c *cancelCtx) Done() <-chan struct{} {
+	return c.done
+}
+
+func (c *cancelCtx) Err() error {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+	return c.err
+}
+
+func (c *cancelCtx) String() string {
+	return fmt.Sprintf("%v.WithCancel", c.Context)
+}
+
+// cancel closes c.done, cancels each of c's children, and, if
+// removeFromParent is true, removes c from its parent's children.
+func (c *cancelCtx) cancel(removeFromParent bool, err error) {
+	if err == nil {
+		panic("context: internal error: missing cancel error")
+	}
+	c.mu.Lock()
+	if c.err != nil {
+		c.mu.Unlock()
+		return // already canceled
+	}
+	c.err = err
+	close(c.done)
+	for child := range c.children {
+		// NOTE: acquiring the child's lock while holding parent's lock.
+		child.cancel(false, err)
+	}
+	c.children = nil
+	c.mu.Unlock()
+
+	if removeFromParent {
+		removeChild(c.Context, c)
+	}
+}
+
+// WithDeadline returns a copy of the parent context with the deadline adjusted
+// to be no later than d. If the parent's deadline is already earlier than d,
+// WithDeadline(parent, d) is semantically equivalent to parent. The returned
+// context's Done channel is closed when the deadline expires, when the returned
+// cancel function is called, or when the parent context's Done channel is
+// closed, whichever happens first.
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete.
+func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
+	if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
+		// The current deadline is already sooner than the new one.
+		return WithCancel(parent)
+	}
+	c := &timerCtx{
+		cancelCtx: newCancelCtx(parent),
+		deadline:  deadline,
+	}
+	propagateCancel(parent, c)
+	d := deadline.Sub(time.Now())
+	if d <= 0 {
+		c.cancel(true, DeadlineExceeded) // deadline has already passed
+		return c, func() { c.cancel(true, Canceled) }
+	}
+	c.mu.Lock()
+	defer c.mu.Unlock()
+	if c.err == nil {
+		c.timer = time.AfterFunc(d, func() {
+			c.cancel(true, DeadlineExceeded)
+		})
+	}
+	return c, func() { c.cancel(true, Canceled) }
+}
+
+// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
+// implement Done and Err. It implements cancel by stopping its timer then
+// delegating to cancelCtx.cancel.
+type timerCtx struct {
+	*cancelCtx
+	timer *time.Timer // Under cancelCtx.mu.
+
+	deadline time.Time
+}
+
+func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
+	return c.deadline, true
+}
+
+func (c *timerCtx) String() string {
+	return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))
+}
+
+func (c *timerCtx) cancel(removeFromParent bool, err error) {
+	c.cancelCtx.cancel(false, err)
+	if removeFromParent {
+		// Remove this timerCtx from its parent cancelCtx's children.
+		removeChild(c.cancelCtx.Context, c)
+	}
+	c.mu.Lock()
+	if c.timer != nil {
+		c.timer.Stop()
+		c.timer = nil
+	}
+	c.mu.Unlock()
+}
+
+// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete:
+//
+// 	func slowOperationWithTimeout(ctx context.Context) (Result, error) {
+// 		ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
+// 		defer cancel()  // releases resources if slowOperation completes before timeout elapses
+// 		return slowOperation(ctx)
+// 	}
+func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
+	return WithDeadline(parent, time.Now().Add(timeout))
+}
+
+// WithValue returns a copy of parent in which the value associated with key is
+// val.
+//
+// Use context Values only for request-scoped data that transits processes and
+// APIs, not for passing optional parameters to functions.
+func WithValue(parent Context, key interface{}, val interface{}) Context {
+	return &valueCtx{parent, key, val}
+}
+
+// A valueCtx carries a key-value pair. It implements Value for that key and
+// delegates all other calls to the embedded Context.
+type valueCtx struct {
+	Context
+	key, val interface{}
+}
+
+func (c *valueCtx) String() string {
+	return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)
+}
+
+func (c *valueCtx) Value(key interface{}) interface{} {
+	if c.key == key {
+		return c.val
+	}
+	return c.Context.Value(key)
+}
diff --git a/vendor/golang.org/x/net/context/pre_go19.go b/vendor/golang.org/x/net/context/pre_go19.go
new file mode 100644
index 0000000..b105f80
--- /dev/null
+++ b/vendor/golang.org/x/net/context/pre_go19.go
@@ -0,0 +1,109 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.9
+
+package context
+
+import "time"
+
+// A Context carries a deadline, a cancelation signal, and other values across
+// API boundaries.
+//
+// Context's methods may be called by multiple goroutines simultaneously.
+type Context interface {
+	// Deadline returns the time when work done on behalf of this context
+	// should be canceled. Deadline returns ok==false when no deadline is
+	// set. Successive calls to Deadline return the same results.
+	Deadline() (deadline time.Time, ok bool)
+
+	// Done returns a channel that's closed when work done on behalf of this
+	// context should be canceled. Done may return nil if this context can
+	// never be canceled. Successive calls to Done return the same value.
+	//
+	// WithCancel arranges for Done to be closed when cancel is called;
+	// WithDeadline arranges for Done to be closed when the deadline
+	// expires; WithTimeout arranges for Done to be closed when the timeout
+	// elapses.
+	//
+	// Done is provided for use in select statements:
+	//
+	//  // Stream generates values with DoSomething and sends them to out
+	//  // until DoSomething returns an error or ctx.Done is closed.
+	//  func Stream(ctx context.Context, out chan<- Value) error {
+	//  	for {
+	//  		v, err := DoSomething(ctx)
+	//  		if err != nil {
+	//  			return err
+	//  		}
+	//  		select {
+	//  		case <-ctx.Done():
+	//  			return ctx.Err()
+	//  		case out <- v:
+	//  		}
+	//  	}
+	//  }
+	//
+	// See http://blog.golang.org/pipelines for more examples of how to use
+	// a Done channel for cancelation.
+	Done() <-chan struct{}
+
+	// Err returns a non-nil error value after Done is closed. Err returns
+	// Canceled if the context was canceled or DeadlineExceeded if the
+	// context's deadline passed. No other values for Err are defined.
+	// After Done is closed, successive calls to Err return the same value.
+	Err() error
+
+	// Value returns the value associated with this context for key, or nil
+	// if no value is associated with key. Successive calls to Value with
+	// the same key returns the same result.
+	//
+	// Use context values only for request-scoped data that transits
+	// processes and API boundaries, not for passing optional parameters to
+	// functions.
+	//
+	// A key identifies a specific value in a Context. Functions that wish
+	// to store values in Context typically allocate a key in a global
+	// variable then use that key as the argument to context.WithValue and
+	// Context.Value. A key can be any type that supports equality;
+	// packages should define keys as an unexported type to avoid
+	// collisions.
+	//
+	// Packages that define a Context key should provide type-safe accessors
+	// for the values stores using that key:
+	//
+	// 	// Package user defines a User type that's stored in Contexts.
+	// 	package user
+	//
+	// 	import "golang.org/x/net/context"
+	//
+	// 	// User is the type of value stored in the Contexts.
+	// 	type User struct {...}
+	//
+	// 	// key is an unexported type for keys defined in this package.
+	// 	// This prevents collisions with keys defined in other packages.
+	// 	type key int
+	//
+	// 	// userKey is the key for user.User values in Contexts. It is
+	// 	// unexported; clients use user.NewContext and user.FromContext
+	// 	// instead of using this key directly.
+	// 	var userKey key = 0
+	//
+	// 	// NewContext returns a new Context that carries value u.
+	// 	func NewContext(ctx context.Context, u *User) context.Context {
+	// 		return context.WithValue(ctx, userKey, u)
+	// 	}
+	//
+	// 	// FromContext returns the User value stored in ctx, if any.
+	// 	func FromContext(ctx context.Context) (*User, bool) {
+	// 		u, ok := ctx.Value(userKey).(*User)
+	// 		return u, ok
+	// 	}
+	Value(key interface{}) interface{}
+}
+
+// A CancelFunc tells an operation to abandon its work.
+// A CancelFunc does not wait for the work to stop.
+// After the first call, subsequent calls to a CancelFunc do nothing.
+type CancelFunc func()
diff --git a/vendor/golang.org/x/oauth2/.travis.yml b/vendor/golang.org/x/oauth2/.travis.yml
new file mode 100644
index 0000000..fa139db
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/.travis.yml
@@ -0,0 +1,13 @@
+language: go
+
+go:
+  - tip
+
+install:
+  - export GOPATH="$HOME/gopath"
+  - mkdir -p "$GOPATH/src/golang.org/x"
+  - mv "$TRAVIS_BUILD_DIR" "$GOPATH/src/golang.org/x/oauth2"
+  - go get -v -t -d golang.org/x/oauth2/...
+
+script:
+  - go test -v golang.org/x/oauth2/...
diff --git a/vendor/golang.org/x/oauth2/AUTHORS b/vendor/golang.org/x/oauth2/AUTHORS
new file mode 100644
index 0000000..15167cd
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/AUTHORS
@@ -0,0 +1,3 @@
+# This source code refers to The Go Authors for copyright purposes.
+# The master list of authors is in the main Go distribution,
+# visible at http://tip.golang.org/AUTHORS.
diff --git a/vendor/golang.org/x/oauth2/CONTRIBUTING.md b/vendor/golang.org/x/oauth2/CONTRIBUTING.md
new file mode 100644
index 0000000..dfbed62
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/CONTRIBUTING.md
@@ -0,0 +1,26 @@
+# Contributing to Go
+
+Go is an open source project.
+
+It is the work of hundreds of contributors. We appreciate your help!
+
+## Filing issues
+
+When [filing an issue](https://github.com/golang/oauth2/issues), make sure to answer these five questions:
+
+1.  What version of Go are you using (`go version`)?
+2.  What operating system and processor architecture are you using?
+3.  What did you do?
+4.  What did you expect to see?
+5.  What did you see instead?
+
+General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
+The gophers there will answer or ask you to file an issue if you've tripped over a bug.
+
+## Contributing code
+
+Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
+before sending patches.
+
+Unless otherwise noted, the Go source files are distributed under
+the BSD-style license found in the LICENSE file.
diff --git a/vendor/golang.org/x/oauth2/CONTRIBUTORS b/vendor/golang.org/x/oauth2/CONTRIBUTORS
new file mode 100644
index 0000000..1c4577e
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/CONTRIBUTORS
@@ -0,0 +1,3 @@
+# This source code was written by the Go contributors.
+# The master list of contributors is in the main Go distribution,
+# visible at http://tip.golang.org/CONTRIBUTORS.
diff --git a/vendor/golang.org/x/oauth2/LICENSE b/vendor/golang.org/x/oauth2/LICENSE
new file mode 100644
index 0000000..6a66aea
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/golang.org/x/oauth2/README.md b/vendor/golang.org/x/oauth2/README.md
new file mode 100644
index 0000000..eb8dcee
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/README.md
@@ -0,0 +1,77 @@
+# OAuth2 for Go
+
+[![Build Status](https://travis-ci.org/golang/oauth2.svg?branch=master)](https://travis-ci.org/golang/oauth2)
+[![GoDoc](https://godoc.org/golang.org/x/oauth2?status.svg)](https://godoc.org/golang.org/x/oauth2)
+
+oauth2 package contains a client implementation for OAuth 2.0 spec.
+
+## Installation
+
+~~~~
+go get golang.org/x/oauth2
+~~~~
+
+Or you can manually git clone the repository to
+`$(go env GOPATH)/src/golang.org/x/oauth2`.
+
+See godoc for further documentation and examples.
+
+* [godoc.org/golang.org/x/oauth2](http://godoc.org/golang.org/x/oauth2)
+* [godoc.org/golang.org/x/oauth2/google](http://godoc.org/golang.org/x/oauth2/google)
+
+
+## App Engine
+
+In change 96e89be (March 2015), we removed the `oauth2.Context2` type in favor
+of the [`context.Context`](https://golang.org/x/net/context#Context) type from
+the `golang.org/x/net/context` package
+
+This means it's no longer possible to use the "Classic App Engine"
+`appengine.Context` type with the `oauth2` package. (You're using
+Classic App Engine if you import the package `"appengine"`.)
+
+To work around this, you may use the new `"google.golang.org/appengine"`
+package. This package has almost the same API as the `"appengine"` package,
+but it can be fetched with `go get` and used on "Managed VMs" and well as
+Classic App Engine.
+
+See the [new `appengine` package's readme](https://github.com/golang/appengine#updating-a-go-app-engine-app)
+for information on updating your app.
+
+If you don't want to update your entire app to use the new App Engine packages,
+you may use both sets of packages in parallel, using only the new packages
+with the `oauth2` package.
+
+```go
+import (
+	"golang.org/x/net/context"
+	"golang.org/x/oauth2"
+	"golang.org/x/oauth2/google"
+	newappengine "google.golang.org/appengine"
+	newurlfetch "google.golang.org/appengine/urlfetch"
+
+	"appengine"
+)
+
+func handler(w http.ResponseWriter, r *http.Request) {
+	var c appengine.Context = appengine.NewContext(r)
+	c.Infof("Logging a message with the old package")
+
+	var ctx context.Context = newappengine.NewContext(r)
+	client := &http.Client{
+		Transport: &oauth2.Transport{
+			Source: google.AppEngineTokenSource(ctx, "scope"),
+			Base:   &newurlfetch.Transport{Context: ctx},
+		},
+	}
+	client.Get("...")
+}
+```
+
+## Report Issues / Send Patches
+
+This repository uses Gerrit for code changes. To learn how to submit changes to
+this repository, see https://golang.org/doc/contribute.html.
+
+The main issue tracker for the oauth2 repository is located at
+https://github.com/golang/oauth2/issues.
diff --git a/vendor/golang.org/x/oauth2/internal/client_appengine.go b/vendor/golang.org/x/oauth2/internal/client_appengine.go
new file mode 100644
index 0000000..7434871
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/internal/client_appengine.go
@@ -0,0 +1,13 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build appengine
+
+package internal
+
+import "google.golang.org/appengine/urlfetch"
+
+func init() {
+	appengineClientHook = urlfetch.Client
+}
diff --git a/vendor/golang.org/x/oauth2/internal/doc.go b/vendor/golang.org/x/oauth2/internal/doc.go
new file mode 100644
index 0000000..03265e8
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/internal/doc.go
@@ -0,0 +1,6 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package internal contains support packages for oauth2 package.
+package internal
diff --git a/vendor/golang.org/x/oauth2/internal/oauth2.go b/vendor/golang.org/x/oauth2/internal/oauth2.go
new file mode 100644
index 0000000..fc63fca
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/internal/oauth2.go
@@ -0,0 +1,37 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal
+
+import (
+	"crypto/rsa"
+	"crypto/x509"
+	"encoding/pem"
+	"errors"
+	"fmt"
+)
+
+// ParseKey converts the binary contents of a private key file
+// to an *rsa.PrivateKey. It detects whether the private key is in a
+// PEM container or not. If so, it extracts the the private key
+// from PEM container before conversion. It only supports PEM
+// containers with no passphrase.
+func ParseKey(key []byte) (*rsa.PrivateKey, error) {
+	block, _ := pem.Decode(key)
+	if block != nil {
+		key = block.Bytes
+	}
+	parsedKey, err := x509.ParsePKCS8PrivateKey(key)
+	if err != nil {
+		parsedKey, err = x509.ParsePKCS1PrivateKey(key)
+		if err != nil {
+			return nil, fmt.Errorf("private key should be a PEM or plain PKSC1 or PKCS8; parse error: %v", err)
+		}
+	}
+	parsed, ok := parsedKey.(*rsa.PrivateKey)
+	if !ok {
+		return nil, errors.New("private key is invalid")
+	}
+	return parsed, nil
+}
diff --git a/vendor/golang.org/x/oauth2/internal/token.go b/vendor/golang.org/x/oauth2/internal/token.go
new file mode 100644
index 0000000..999e668
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/internal/token.go
@@ -0,0 +1,270 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"mime"
+	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
+	"time"
+
+	"golang.org/x/net/context"
+	"golang.org/x/net/context/ctxhttp"
+)
+
+// Token represents the credentials used to authorize
+// the requests to access protected resources on the OAuth 2.0
+// provider's backend.
+//
+// This type is a mirror of oauth2.Token and exists to break
+// an otherwise-circular dependency. Other internal packages
+// should convert this Token into an oauth2.Token before use.
+type Token struct {
+	// AccessToken is the token that authorizes and authenticates
+	// the requests.
+	AccessToken string
+
+	// TokenType is the type of token.
+	// The Type method returns either this or "Bearer", the default.
+	TokenType string
+
+	// RefreshToken is a token that's used by the application
+	// (as opposed to the user) to refresh the access token
+	// if it expires.
+	RefreshToken string
+
+	// Expiry is the optional expiration time of the access token.
+	//
+	// If zero, TokenSource implementations will reuse the same
+	// token forever and RefreshToken or equivalent
+	// mechanisms for that TokenSource will not be used.
+	Expiry time.Time
+
+	// Raw optionally contains extra metadata from the server
+	// when updating a token.
+	Raw interface{}
+}
+
+// tokenJSON is the struct representing the HTTP response from OAuth2
+// providers returning a token in JSON form.
+type tokenJSON struct {
+	AccessToken  string         `json:"access_token"`
+	TokenType    string         `json:"token_type"`
+	RefreshToken string         `json:"refresh_token"`
+	ExpiresIn    expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
+	Expires      expirationTime `json:"expires"`    // broken Facebook spelling of expires_in
+}
+
+func (e *tokenJSON) expiry() (t time.Time) {
+	if v := e.ExpiresIn; v != 0 {
+		return time.Now().Add(time.Duration(v) * time.Second)
+	}
+	if v := e.Expires; v != 0 {
+		return time.Now().Add(time.Duration(v) * time.Second)
+	}
+	return
+}
+
+type expirationTime int32
+
+func (e *expirationTime) UnmarshalJSON(b []byte) error {
+	var n json.Number
+	err := json.Unmarshal(b, &n)
+	if err != nil {
+		return err
+	}
+	i, err := n.Int64()
+	if err != nil {
+		return err
+	}
+	*e = expirationTime(i)
+	return nil
+}
+
+var brokenAuthHeaderProviders = []string{
+	"https://accounts.google.com/",
+	"https://api.codeswholesale.com/oauth/token",
+	"https://api.dropbox.com/",
+	"https://api.dropboxapi.com/",
+	"https://api.instagram.com/",
+	"https://api.netatmo.net/",
+	"https://api.odnoklassniki.ru/",
+	"https://api.pushbullet.com/",
+	"https://api.soundcloud.com/",
+	"https://api.twitch.tv/",
+	"https://id.twitch.tv/",
+	"https://app.box.com/",
+	"https://connect.stripe.com/",
+	"https://login.mailchimp.com/",
+	"https://login.microsoftonline.com/",
+	"https://login.salesforce.com/",
+	"https://login.windows.net",
+	"https://login.live.com/",
+	"https://oauth.sandbox.trainingpeaks.com/",
+	"https://oauth.trainingpeaks.com/",
+	"https://oauth.vk.com/",
+	"https://openapi.baidu.com/",
+	"https://slack.com/",
+	"https://test-sandbox.auth.corp.google.com",
+	"https://test.salesforce.com/",
+	"https://user.gini.net/",
+	"https://www.douban.com/",
+	"https://www.googleapis.com/",
+	"https://www.linkedin.com/",
+	"https://www.strava.com/oauth/",
+	"https://www.wunderlist.com/oauth/",
+	"https://api.patreon.com/",
+	"https://sandbox.codeswholesale.com/oauth/token",
+	"https://api.sipgate.com/v1/authorization/oauth",
+	"https://api.medium.com/v1/tokens",
+	"https://log.finalsurge.com/oauth/token",
+	"https://multisport.todaysplan.com.au/rest/oauth/access_token",
+	"https://whats.todaysplan.com.au/rest/oauth/access_token",
+	"https://stackoverflow.com/oauth/access_token",
+}
+
+// brokenAuthHeaderDomains lists broken providers that issue dynamic endpoints.
+var brokenAuthHeaderDomains = []string{
+	".auth0.com",
+	".force.com",
+	".myshopify.com",
+	".okta.com",
+	".oktapreview.com",
+}
+
+func RegisterBrokenAuthHeaderProvider(tokenURL string) {
+	brokenAuthHeaderProviders = append(brokenAuthHeaderProviders, tokenURL)
+}
+
+// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
+// implements the OAuth2 spec correctly
+// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
+// In summary:
+// - Reddit only accepts client secret in the Authorization header
+// - Dropbox accepts either it in URL param or Auth header, but not both.
+// - Google only accepts URL param (not spec compliant?), not Auth header
+// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
+func providerAuthHeaderWorks(tokenURL string) bool {
+	for _, s := range brokenAuthHeaderProviders {
+		if strings.HasPrefix(tokenURL, s) {
+			// Some sites fail to implement the OAuth2 spec fully.
+			return false
+		}
+	}
+
+	if u, err := url.Parse(tokenURL); err == nil {
+		for _, s := range brokenAuthHeaderDomains {
+			if strings.HasSuffix(u.Host, s) {
+				return false
+			}
+		}
+	}
+
+	// Assume the provider implements the spec properly
+	// otherwise. We can add more exceptions as they're
+	// discovered. We will _not_ be adding configurable hooks
+	// to this package to let users select server bugs.
+	return true
+}
+
+func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values) (*Token, error) {
+	bustedAuth := !providerAuthHeaderWorks(tokenURL)
+	if bustedAuth {
+		if clientID != "" {
+			v.Set("client_id", clientID)
+		}
+		if clientSecret != "" {
+			v.Set("client_secret", clientSecret)
+		}
+	}
+	req, err := http.NewRequest("POST", tokenURL, strings.NewReader(v.Encode()))
+	if err != nil {
+		return nil, err
+	}
+	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+	if !bustedAuth {
+		req.SetBasicAuth(url.QueryEscape(clientID), url.QueryEscape(clientSecret))
+	}
+	r, err := ctxhttp.Do(ctx, ContextClient(ctx), req)
+	if err != nil {
+		return nil, err
+	}
+	defer r.Body.Close()
+	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
+	if err != nil {
+		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
+	}
+	if code := r.StatusCode; code < 200 || code > 299 {
+		return nil, &RetrieveError{
+			Response: r,
+			Body:     body,
+		}
+	}
+
+	var token *Token
+	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
+	switch content {
+	case "application/x-www-form-urlencoded", "text/plain":
+		vals, err := url.ParseQuery(string(body))
+		if err != nil {
+			return nil, err
+		}
+		token = &Token{
+			AccessToken:  vals.Get("access_token"),
+			TokenType:    vals.Get("token_type"),
+			RefreshToken: vals.Get("refresh_token"),
+			Raw:          vals,
+		}
+		e := vals.Get("expires_in")
+		if e == "" {
+			// TODO(jbd): Facebook's OAuth2 implementation is broken and
+			// returns expires_in field in expires. Remove the fallback to expires,
+			// when Facebook fixes their implementation.
+			e = vals.Get("expires")
+		}
+		expires, _ := strconv.Atoi(e)
+		if expires != 0 {
+			token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
+		}
+	default:
+		var tj tokenJSON
+		if err = json.Unmarshal(body, &tj); err != nil {
+			return nil, err
+		}
+		token = &Token{
+			AccessToken:  tj.AccessToken,
+			TokenType:    tj.TokenType,
+			RefreshToken: tj.RefreshToken,
+			Expiry:       tj.expiry(),
+			Raw:          make(map[string]interface{}),
+		}
+		json.Unmarshal(body, &token.Raw) // no error checks for optional fields
+	}
+	// Don't overwrite `RefreshToken` with an empty value
+	// if this was a token refreshing request.
+	if token.RefreshToken == "" {
+		token.RefreshToken = v.Get("refresh_token")
+	}
+	if token.AccessToken == "" {
+		return token, errors.New("oauth2: server response missing access_token")
+	}
+	return token, nil
+}
+
+type RetrieveError struct {
+	Response *http.Response
+	Body     []byte
+}
+
+func (r *RetrieveError) Error() string {
+	return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
+}
diff --git a/vendor/golang.org/x/oauth2/internal/transport.go b/vendor/golang.org/x/oauth2/internal/transport.go
new file mode 100644
index 0000000..d16f9ae
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/internal/transport.go
@@ -0,0 +1,34 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal
+
+import (
+	"net/http"
+
+	"golang.org/x/net/context"
+)
+
+// HTTPClient is the context key to use with golang.org/x/net/context's
+// WithValue function to associate an *http.Client value with a context.
+var HTTPClient ContextKey
+
+// ContextKey is just an empty struct. It exists so HTTPClient can be
+// an immutable public variable with a unique type. It's immutable
+// because nobody else can create a ContextKey, being unexported.
+type ContextKey struct{}
+
+var appengineClientHook func(context.Context) *http.Client
+
+func ContextClient(ctx context.Context) *http.Client {
+	if ctx != nil {
+		if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok {
+			return hc
+		}
+	}
+	if appengineClientHook != nil {
+		return appengineClientHook(ctx)
+	}
+	return http.DefaultClient
+}
diff --git a/vendor/golang.org/x/oauth2/oauth2.go b/vendor/golang.org/x/oauth2/oauth2.go
new file mode 100644
index 0000000..16775d0
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/oauth2.go
@@ -0,0 +1,362 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package oauth2 provides support for making
+// OAuth2 authorized and authenticated HTTP requests,
+// as specified in RFC 6749.
+// It can additionally grant authorization with Bearer JWT.
+package oauth2 // import "golang.org/x/oauth2"
+
+import (
+	"bytes"
+	"errors"
+	"net/http"
+	"net/url"
+	"strings"
+	"sync"
+
+	"golang.org/x/net/context"
+	"golang.org/x/oauth2/internal"
+)
+
+// NoContext is the default context you should supply if not using
+// your own context.Context (see https://golang.org/x/net/context).
+//
+// Deprecated: Use context.Background() or context.TODO() instead.
+var NoContext = context.TODO()
+
+// RegisterBrokenAuthHeaderProvider registers an OAuth2 server
+// identified by the tokenURL prefix as an OAuth2 implementation
+// which doesn't support the HTTP Basic authentication
+// scheme to authenticate with the authorization server.
+// Once a server is registered, credentials (client_id and client_secret)
+// will be passed as query parameters rather than being present
+// in the Authorization header.
+// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
+func RegisterBrokenAuthHeaderProvider(tokenURL string) {
+	internal.RegisterBrokenAuthHeaderProvider(tokenURL)
+}
+
+// Config describes a typical 3-legged OAuth2 flow, with both the
+// client application information and the server's endpoint URLs.
+// For the client credentials 2-legged OAuth2 flow, see the clientcredentials
+// package (https://golang.org/x/oauth2/clientcredentials).
+type Config struct {
+	// ClientID is the application's ID.
+	ClientID string
+
+	// ClientSecret is the application's secret.
+	ClientSecret string
+
+	// Endpoint contains the resource server's token endpoint
+	// URLs. These are constants specific to each server and are
+	// often available via site-specific packages, such as
+	// google.Endpoint or github.Endpoint.
+	Endpoint Endpoint
+
+	// RedirectURL is the URL to redirect users going through
+	// the OAuth flow, after the resource owner's URLs.
+	RedirectURL string
+
+	// Scope specifies optional requested permissions.
+	Scopes []string
+}
+
+// A TokenSource is anything that can return a token.
+type TokenSource interface {
+	// Token returns a token or an error.
+	// Token must be safe for concurrent use by multiple goroutines.
+	// The returned Token must not be modified.
+	Token() (*Token, error)
+}
+
+// Endpoint contains the OAuth 2.0 provider's authorization and token
+// endpoint URLs.
+type Endpoint struct {
+	AuthURL  string
+	TokenURL string
+}
+
+var (
+	// AccessTypeOnline and AccessTypeOffline are options passed
+	// to the Options.AuthCodeURL method. They modify the
+	// "access_type" field that gets sent in the URL returned by
+	// AuthCodeURL.
+	//
+	// Online is the default if neither is specified. If your
+	// application needs to refresh access tokens when the user
+	// is not present at the browser, then use offline. This will
+	// result in your application obtaining a refresh token the
+	// first time your application exchanges an authorization
+	// code for a user.
+	AccessTypeOnline  AuthCodeOption = SetAuthURLParam("access_type", "online")
+	AccessTypeOffline AuthCodeOption = SetAuthURLParam("access_type", "offline")
+
+	// ApprovalForce forces the users to view the consent dialog
+	// and confirm the permissions request at the URL returned
+	// from AuthCodeURL, even if they've already done so.
+	ApprovalForce AuthCodeOption = SetAuthURLParam("approval_prompt", "force")
+)
+
+// An AuthCodeOption is passed to Config.AuthCodeURL.
+type AuthCodeOption interface {
+	setValue(url.Values)
+}
+
+type setParam struct{ k, v string }
+
+func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) }
+
+// SetAuthURLParam builds an AuthCodeOption which passes key/value parameters
+// to a provider's authorization endpoint.
+func SetAuthURLParam(key, value string) AuthCodeOption {
+	return setParam{key, value}
+}
+
+// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
+// that asks for permissions for the required scopes explicitly.
+//
+// State is a token to protect the user from CSRF attacks. You must
+// always provide a non-empty string and validate that it matches the
+// the state query parameter on your redirect callback.
+// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
+//
+// Opts may include AccessTypeOnline or AccessTypeOffline, as well
+// as ApprovalForce.
+// It can also be used to pass the PKCE challange.
+// See https://www.oauth.com/oauth2-servers/pkce/ for more info.
+func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
+	var buf bytes.Buffer
+	buf.WriteString(c.Endpoint.AuthURL)
+	v := url.Values{
+		"response_type": {"code"},
+		"client_id":     {c.ClientID},
+	}
+	if c.RedirectURL != "" {
+		v.Set("redirect_uri", c.RedirectURL)
+	}
+	if len(c.Scopes) > 0 {
+		v.Set("scope", strings.Join(c.Scopes, " "))
+	}
+	if state != "" {
+		// TODO(light): Docs say never to omit state; don't allow empty.
+		v.Set("state", state)
+	}
+	for _, opt := range opts {
+		opt.setValue(v)
+	}
+	if strings.Contains(c.Endpoint.AuthURL, "?") {
+		buf.WriteByte('&')
+	} else {
+		buf.WriteByte('?')
+	}
+	buf.WriteString(v.Encode())
+	return buf.String()
+}
+
+// PasswordCredentialsToken converts a resource owner username and password
+// pair into a token.
+//
+// Per the RFC, this grant type should only be used "when there is a high
+// degree of trust between the resource owner and the client (e.g., the client
+// is part of the device operating system or a highly privileged application),
+// and when other authorization grant types are not available."
+// See https://tools.ietf.org/html/rfc6749#section-4.3 for more info.
+//
+// The HTTP client to use is derived from the context.
+// If nil, http.DefaultClient is used.
+func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) {
+	v := url.Values{
+		"grant_type": {"password"},
+		"username":   {username},
+		"password":   {password},
+	}
+	if len(c.Scopes) > 0 {
+		v.Set("scope", strings.Join(c.Scopes, " "))
+	}
+	return retrieveToken(ctx, c, v)
+}
+
+// Exchange converts an authorization code into a token.
+//
+// It is used after a resource provider redirects the user back
+// to the Redirect URI (the URL obtained from AuthCodeURL).
+//
+// The HTTP client to use is derived from the context.
+// If a client is not provided via the context, http.DefaultClient is used.
+//
+// The code will be in the *http.Request.FormValue("code"). Before
+// calling Exchange, be sure to validate FormValue("state").
+//
+// Opts may include the PKCE verifier code if previously used in AuthCodeURL.
+// See https://www.oauth.com/oauth2-servers/pkce/ for more info.
+func (c *Config) Exchange(ctx context.Context, code string, opts ...AuthCodeOption) (*Token, error) {
+	v := url.Values{
+		"grant_type": {"authorization_code"},
+		"code":       {code},
+	}
+	if c.RedirectURL != "" {
+		v.Set("redirect_uri", c.RedirectURL)
+	}
+	for _, opt := range opts {
+		opt.setValue(v)
+	}
+	return retrieveToken(ctx, c, v)
+}
+
+// Client returns an HTTP client using the provided token.
+// The token will auto-refresh as necessary. The underlying
+// HTTP transport will be obtained using the provided context.
+// The returned client and its Transport should not be modified.
+func (c *Config) Client(ctx context.Context, t *Token) *http.Client {
+	return NewClient(ctx, c.TokenSource(ctx, t))
+}
+
+// TokenSource returns a TokenSource that returns t until t expires,
+// automatically refreshing it as necessary using the provided context.
+//
+// Most users will use Config.Client instead.
+func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource {
+	tkr := &tokenRefresher{
+		ctx:  ctx,
+		conf: c,
+	}
+	if t != nil {
+		tkr.refreshToken = t.RefreshToken
+	}
+	return &reuseTokenSource{
+		t:   t,
+		new: tkr,
+	}
+}
+
+// tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token"
+// HTTP requests to renew a token using a RefreshToken.
+type tokenRefresher struct {
+	ctx          context.Context // used to get HTTP requests
+	conf         *Config
+	refreshToken string
+}
+
+// WARNING: Token is not safe for concurrent access, as it
+// updates the tokenRefresher's refreshToken field.
+// Within this package, it is used by reuseTokenSource which
+// synchronizes calls to this method with its own mutex.
+func (tf *tokenRefresher) Token() (*Token, error) {
+	if tf.refreshToken == "" {
+		return nil, errors.New("oauth2: token expired and refresh token is not set")
+	}
+
+	tk, err := retrieveToken(tf.ctx, tf.conf, url.Values{
+		"grant_type":    {"refresh_token"},
+		"refresh_token": {tf.refreshToken},
+	})
+
+	if err != nil {
+		return nil, err
+	}
+	if tf.refreshToken != tk.RefreshToken {
+		tf.refreshToken = tk.RefreshToken
+	}
+	return tk, err
+}
+
+// reuseTokenSource is a TokenSource that holds a single token in memory
+// and validates its expiry before each call to retrieve it with
+// Token. If it's expired, it will be auto-refreshed using the
+// new TokenSource.
+type reuseTokenSource struct {
+	new TokenSource // called when t is expired.
+
+	mu sync.Mutex // guards t
+	t  *Token
+}
+
+// Token returns the current token if it's still valid, else will
+// refresh the current token (using r.Context for HTTP client
+// information) and return the new one.
+func (s *reuseTokenSource) Token() (*Token, error) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	if s.t.Valid() {
+		return s.t, nil
+	}
+	t, err := s.new.Token()
+	if err != nil {
+		return nil, err
+	}
+	s.t = t
+	return t, nil
+}
+
+// StaticTokenSource returns a TokenSource that always returns the same token.
+// Because the provided token t is never refreshed, StaticTokenSource is only
+// useful for tokens that never expire.
+func StaticTokenSource(t *Token) TokenSource {
+	return staticTokenSource{t}
+}
+
+// staticTokenSource is a TokenSource that always returns the same Token.
+type staticTokenSource struct {
+	t *Token
+}
+
+func (s staticTokenSource) Token() (*Token, error) {
+	return s.t, nil
+}
+
+// HTTPClient is the context key to use with golang.org/x/net/context's
+// WithValue function to associate an *http.Client value with a context.
+var HTTPClient internal.ContextKey
+
+// NewClient creates an *http.Client from a Context and TokenSource.
+// The returned client is not valid beyond the lifetime of the context.
+//
+// Note that if a custom *http.Client is provided via the Context it
+// is used only for token acquisition and is not used to configure the
+// *http.Client returned from NewClient.
+//
+// As a special case, if src is nil, a non-OAuth2 client is returned
+// using the provided context. This exists to support related OAuth2
+// packages.
+func NewClient(ctx context.Context, src TokenSource) *http.Client {
+	if src == nil {
+		return internal.ContextClient(ctx)
+	}
+	return &http.Client{
+		Transport: &Transport{
+			Base:   internal.ContextClient(ctx).Transport,
+			Source: ReuseTokenSource(nil, src),
+		},
+	}
+}
+
+// ReuseTokenSource returns a TokenSource which repeatedly returns the
+// same token as long as it's valid, starting with t.
+// When its cached token is invalid, a new token is obtained from src.
+//
+// ReuseTokenSource is typically used to reuse tokens from a cache
+// (such as a file on disk) between runs of a program, rather than
+// obtaining new tokens unnecessarily.
+//
+// The initial token t may be nil, in which case the TokenSource is
+// wrapped in a caching version if it isn't one already. This also
+// means it's always safe to wrap ReuseTokenSource around any other
+// TokenSource without adverse effects.
+func ReuseTokenSource(t *Token, src TokenSource) TokenSource {
+	// Don't wrap a reuseTokenSource in itself. That would work,
+	// but cause an unnecessary number of mutex operations.
+	// Just build the equivalent one.
+	if rt, ok := src.(*reuseTokenSource); ok {
+		if t == nil {
+			// Just use it directly.
+			return rt
+		}
+		src = rt.new
+	}
+	return &reuseTokenSource{
+		t:   t,
+		new: src,
+	}
+}
diff --git a/vendor/golang.org/x/oauth2/token.go b/vendor/golang.org/x/oauth2/token.go
new file mode 100644
index 0000000..34db8cd
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/token.go
@@ -0,0 +1,175 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package oauth2
+
+import (
+	"fmt"
+	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
+	"time"
+
+	"golang.org/x/net/context"
+	"golang.org/x/oauth2/internal"
+)
+
+// expiryDelta determines how earlier a token should be considered
+// expired than its actual expiration time. It is used to avoid late
+// expirations due to client-server time mismatches.
+const expiryDelta = 10 * time.Second
+
+// Token represents the credentials used to authorize
+// the requests to access protected resources on the OAuth 2.0
+// provider's backend.
+//
+// Most users of this package should not access fields of Token
+// directly. They're exported mostly for use by related packages
+// implementing derivative OAuth2 flows.
+type Token struct {
+	// AccessToken is the token that authorizes and authenticates
+	// the requests.
+	AccessToken string `json:"access_token"`
+
+	// TokenType is the type of token.
+	// The Type method returns either this or "Bearer", the default.
+	TokenType string `json:"token_type,omitempty"`
+
+	// RefreshToken is a token that's used by the application
+	// (as opposed to the user) to refresh the access token
+	// if it expires.
+	RefreshToken string `json:"refresh_token,omitempty"`
+
+	// Expiry is the optional expiration time of the access token.
+	//
+	// If zero, TokenSource implementations will reuse the same
+	// token forever and RefreshToken or equivalent
+	// mechanisms for that TokenSource will not be used.
+	Expiry time.Time `json:"expiry,omitempty"`
+
+	// raw optionally contains extra metadata from the server
+	// when updating a token.
+	raw interface{}
+}
+
+// Type returns t.TokenType if non-empty, else "Bearer".
+func (t *Token) Type() string {
+	if strings.EqualFold(t.TokenType, "bearer") {
+		return "Bearer"
+	}
+	if strings.EqualFold(t.TokenType, "mac") {
+		return "MAC"
+	}
+	if strings.EqualFold(t.TokenType, "basic") {
+		return "Basic"
+	}
+	if t.TokenType != "" {
+		return t.TokenType
+	}
+	return "Bearer"
+}
+
+// SetAuthHeader sets the Authorization header to r using the access
+// token in t.
+//
+// This method is unnecessary when using Transport or an HTTP Client
+// returned by this package.
+func (t *Token) SetAuthHeader(r *http.Request) {
+	r.Header.Set("Authorization", t.Type()+" "+t.AccessToken)
+}
+
+// WithExtra returns a new Token that's a clone of t, but using the
+// provided raw extra map. This is only intended for use by packages
+// implementing derivative OAuth2 flows.
+func (t *Token) WithExtra(extra interface{}) *Token {
+	t2 := new(Token)
+	*t2 = *t
+	t2.raw = extra
+	return t2
+}
+
+// Extra returns an extra field.
+// Extra fields are key-value pairs returned by the server as a
+// part of the token retrieval response.
+func (t *Token) Extra(key string) interface{} {
+	if raw, ok := t.raw.(map[string]interface{}); ok {
+		return raw[key]
+	}
+
+	vals, ok := t.raw.(url.Values)
+	if !ok {
+		return nil
+	}
+
+	v := vals.Get(key)
+	switch s := strings.TrimSpace(v); strings.Count(s, ".") {
+	case 0: // Contains no "."; try to parse as int
+		if i, err := strconv.ParseInt(s, 10, 64); err == nil {
+			return i
+		}
+	case 1: // Contains a single "."; try to parse as float
+		if f, err := strconv.ParseFloat(s, 64); err == nil {
+			return f
+		}
+	}
+
+	return v
+}
+
+// expired reports whether the token is expired.
+// t must be non-nil.
+func (t *Token) expired() bool {
+	if t.Expiry.IsZero() {
+		return false
+	}
+	return t.Expiry.Round(0).Add(-expiryDelta).Before(time.Now())
+}
+
+// Valid reports whether t is non-nil, has an AccessToken, and is not expired.
+func (t *Token) Valid() bool {
+	return t != nil && t.AccessToken != "" && !t.expired()
+}
+
+// tokenFromInternal maps an *internal.Token struct into
+// a *Token struct.
+func tokenFromInternal(t *internal.Token) *Token {
+	if t == nil {
+		return nil
+	}
+	return &Token{
+		AccessToken:  t.AccessToken,
+		TokenType:    t.TokenType,
+		RefreshToken: t.RefreshToken,
+		Expiry:       t.Expiry,
+		raw:          t.Raw,
+	}
+}
+
+// retrieveToken takes a *Config and uses that to retrieve an *internal.Token.
+// This token is then mapped from *internal.Token into an *oauth2.Token which is returned along
+// with an error..
+func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
+	tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v)
+	if err != nil {
+		if rErr, ok := err.(*internal.RetrieveError); ok {
+			return nil, (*RetrieveError)(rErr)
+		}
+		return nil, err
+	}
+	return tokenFromInternal(tk), nil
+}
+
+// RetrieveError is the error returned when the token endpoint returns a
+// non-2XX HTTP status code.
+type RetrieveError struct {
+	Response *http.Response
+	// Body is the body that was consumed by reading Response.Body.
+	// It may be truncated.
+	Body []byte
+}
+
+func (r *RetrieveError) Error() string {
+	return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
+}
diff --git a/vendor/golang.org/x/oauth2/transport.go b/vendor/golang.org/x/oauth2/transport.go
new file mode 100644
index 0000000..aa0d34f
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/transport.go
@@ -0,0 +1,144 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package oauth2
+
+import (
+	"errors"
+	"io"
+	"net/http"
+	"sync"
+)
+
+// Transport is an http.RoundTripper that makes OAuth 2.0 HTTP requests,
+// wrapping a base RoundTripper and adding an Authorization header
+// with a token from the supplied Sources.
+//
+// Transport is a low-level mechanism. Most code will use the
+// higher-level Config.Client method instead.
+type Transport struct {
+	// Source supplies the token to add to outgoing requests'
+	// Authorization headers.
+	Source TokenSource
+
+	// Base is the base RoundTripper used to make HTTP requests.
+	// If nil, http.DefaultTransport is used.
+	Base http.RoundTripper
+
+	mu     sync.Mutex                      // guards modReq
+	modReq map[*http.Request]*http.Request // original -> modified
+}
+
+// RoundTrip authorizes and authenticates the request with an
+// access token from Transport's Source.
+func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
+	reqBodyClosed := false
+	if req.Body != nil {
+		defer func() {
+			if !reqBodyClosed {
+				req.Body.Close()
+			}
+		}()
+	}
+
+	if t.Source == nil {
+		return nil, errors.New("oauth2: Transport's Source is nil")
+	}
+	token, err := t.Source.Token()
+	if err != nil {
+		return nil, err
+	}
+
+	req2 := cloneRequest(req) // per RoundTripper contract
+	token.SetAuthHeader(req2)
+	t.setModReq(req, req2)
+	res, err := t.base().RoundTrip(req2)
+
+	// req.Body is assumed to have been closed by the base RoundTripper.
+	reqBodyClosed = true
+
+	if err != nil {
+		t.setModReq(req, nil)
+		return nil, err
+	}
+	res.Body = &onEOFReader{
+		rc: res.Body,
+		fn: func() { t.setModReq(req, nil) },
+	}
+	return res, nil
+}
+
+// CancelRequest cancels an in-flight request by closing its connection.
+func (t *Transport) CancelRequest(req *http.Request) {
+	type canceler interface {
+		CancelRequest(*http.Request)
+	}
+	if cr, ok := t.base().(canceler); ok {
+		t.mu.Lock()
+		modReq := t.modReq[req]
+		delete(t.modReq, req)
+		t.mu.Unlock()
+		cr.CancelRequest(modReq)
+	}
+}
+
+func (t *Transport) base() http.RoundTripper {
+	if t.Base != nil {
+		return t.Base
+	}
+	return http.DefaultTransport
+}
+
+func (t *Transport) setModReq(orig, mod *http.Request) {
+	t.mu.Lock()
+	defer t.mu.Unlock()
+	if t.modReq == nil {
+		t.modReq = make(map[*http.Request]*http.Request)
+	}
+	if mod == nil {
+		delete(t.modReq, orig)
+	} else {
+		t.modReq[orig] = mod
+	}
+}
+
+// cloneRequest returns a clone of the provided *http.Request.
+// The clone is a shallow copy of the struct and its Header map.
+func cloneRequest(r *http.Request) *http.Request {
+	// shallow copy of the struct
+	r2 := new(http.Request)
+	*r2 = *r
+	// deep copy of the Header
+	r2.Header = make(http.Header, len(r.Header))
+	for k, s := range r.Header {
+		r2.Header[k] = append([]string(nil), s...)
+	}
+	return r2
+}
+
+type onEOFReader struct {
+	rc io.ReadCloser
+	fn func()
+}
+
+func (r *onEOFReader) Read(p []byte) (n int, err error) {
+	n, err = r.rc.Read(p)
+	if err == io.EOF {
+		r.runFunc()
+	}
+	return
+}
+
+func (r *onEOFReader) Close() error {
+	err := r.rc.Close()
+	r.runFunc()
+	return err
+}
+
+func (r *onEOFReader) runFunc() {
+	if fn := r.fn; fn != nil {
+		fn()
+		r.fn = nil
+	}
+}
diff --git a/vendor/google.golang.org/appengine/LICENSE b/vendor/google.golang.org/appengine/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/vendor/google.golang.org/appengine/LICENSE
@@ -0,0 +1,202 @@
+
+                                 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 [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/google.golang.org/appengine/internal/api.go b/vendor/google.golang.org/appengine/internal/api.go
new file mode 100644
index 0000000..16f87c5
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/api.go
@@ -0,0 +1,660 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// +build !appengine
+// +build go1.7
+
+package internal
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net"
+	"net/http"
+	"net/url"
+	"os"
+	"runtime"
+	"strconv"
+	"strings"
+	"sync"
+	"sync/atomic"
+	"time"
+
+	"github.com/golang/protobuf/proto"
+	netcontext "golang.org/x/net/context"
+
+	basepb "google.golang.org/appengine/internal/base"
+	logpb "google.golang.org/appengine/internal/log"
+	remotepb "google.golang.org/appengine/internal/remote_api"
+)
+
+const (
+	apiPath             = "/rpc_http"
+	defaultTicketSuffix = "/default.20150612t184001.0"
+)
+
+var (
+	// Incoming headers.
+	ticketHeader       = http.CanonicalHeaderKey("X-AppEngine-API-Ticket")
+	dapperHeader       = http.CanonicalHeaderKey("X-Google-DapperTraceInfo")
+	traceHeader        = http.CanonicalHeaderKey("X-Cloud-Trace-Context")
+	curNamespaceHeader = http.CanonicalHeaderKey("X-AppEngine-Current-Namespace")
+	userIPHeader       = http.CanonicalHeaderKey("X-AppEngine-User-IP")
+	remoteAddrHeader   = http.CanonicalHeaderKey("X-AppEngine-Remote-Addr")
+
+	// Outgoing headers.
+	apiEndpointHeader      = http.CanonicalHeaderKey("X-Google-RPC-Service-Endpoint")
+	apiEndpointHeaderValue = []string{"app-engine-apis"}
+	apiMethodHeader        = http.CanonicalHeaderKey("X-Google-RPC-Service-Method")
+	apiMethodHeaderValue   = []string{"/VMRemoteAPI.CallRemoteAPI"}
+	apiDeadlineHeader      = http.CanonicalHeaderKey("X-Google-RPC-Service-Deadline")
+	apiContentType         = http.CanonicalHeaderKey("Content-Type")
+	apiContentTypeValue    = []string{"application/octet-stream"}
+	logFlushHeader         = http.CanonicalHeaderKey("X-AppEngine-Log-Flush-Count")
+
+	apiHTTPClient = &http.Client{
+		Transport: &http.Transport{
+			Proxy: http.ProxyFromEnvironment,
+			Dial:  limitDial,
+		},
+	}
+
+	defaultTicketOnce     sync.Once
+	defaultTicket         string
+	backgroundContextOnce sync.Once
+	backgroundContext     netcontext.Context
+)
+
+func apiURL() *url.URL {
+	host, port := "appengine.googleapis.internal", "10001"
+	if h := os.Getenv("API_HOST"); h != "" {
+		host = h
+	}
+	if p := os.Getenv("API_PORT"); p != "" {
+		port = p
+	}
+	return &url.URL{
+		Scheme: "http",
+		Host:   host + ":" + port,
+		Path:   apiPath,
+	}
+}
+
+func handleHTTP(w http.ResponseWriter, r *http.Request) {
+	c := &context{
+		req:       r,
+		outHeader: w.Header(),
+		apiURL:    apiURL(),
+	}
+	r = r.WithContext(withContext(r.Context(), c))
+	c.req = r
+
+	stopFlushing := make(chan int)
+
+	// Patch up RemoteAddr so it looks reasonable.
+	if addr := r.Header.Get(userIPHeader); addr != "" {
+		r.RemoteAddr = addr
+	} else if addr = r.Header.Get(remoteAddrHeader); addr != "" {
+		r.RemoteAddr = addr
+	} else {
+		// Should not normally reach here, but pick a sensible default anyway.
+		r.RemoteAddr = "127.0.0.1"
+	}
+	// The address in the headers will most likely be of these forms:
+	//	123.123.123.123
+	//	2001:db8::1
+	// net/http.Request.RemoteAddr is specified to be in "IP:port" form.
+	if _, _, err := net.SplitHostPort(r.RemoteAddr); err != nil {
+		// Assume the remote address is only a host; add a default port.
+		r.RemoteAddr = net.JoinHostPort(r.RemoteAddr, "80")
+	}
+
+	// Start goroutine responsible for flushing app logs.
+	// This is done after adding c to ctx.m (and stopped before removing it)
+	// because flushing logs requires making an API call.
+	go c.logFlusher(stopFlushing)
+
+	executeRequestSafely(c, r)
+	c.outHeader = nil // make sure header changes aren't respected any more
+
+	stopFlushing <- 1 // any logging beyond this point will be dropped
+
+	// Flush any pending logs asynchronously.
+	c.pendingLogs.Lock()
+	flushes := c.pendingLogs.flushes
+	if len(c.pendingLogs.lines) > 0 {
+		flushes++
+	}
+	c.pendingLogs.Unlock()
+	go c.flushLog(false)
+	w.Header().Set(logFlushHeader, strconv.Itoa(flushes))
+
+	// Avoid nil Write call if c.Write is never called.
+	if c.outCode != 0 {
+		w.WriteHeader(c.outCode)
+	}
+	if c.outBody != nil {
+		w.Write(c.outBody)
+	}
+}
+
+func executeRequestSafely(c *context, r *http.Request) {
+	defer func() {
+		if x := recover(); x != nil {
+			logf(c, 4, "%s", renderPanic(x)) // 4 == critical
+			c.outCode = 500
+		}
+	}()
+
+	http.DefaultServeMux.ServeHTTP(c, r)
+}
+
+func renderPanic(x interface{}) string {
+	buf := make([]byte, 16<<10) // 16 KB should be plenty
+	buf = buf[:runtime.Stack(buf, false)]
+
+	// Remove the first few stack frames:
+	//   this func
+	//   the recover closure in the caller
+	// That will root the stack trace at the site of the panic.
+	const (
+		skipStart  = "internal.renderPanic"
+		skipFrames = 2
+	)
+	start := bytes.Index(buf, []byte(skipStart))
+	p := start
+	for i := 0; i < skipFrames*2 && p+1 < len(buf); i++ {
+		p = bytes.IndexByte(buf[p+1:], '\n') + p + 1
+		if p < 0 {
+			break
+		}
+	}
+	if p >= 0 {
+		// buf[start:p+1] is the block to remove.
+		// Copy buf[p+1:] over buf[start:] and shrink buf.
+		copy(buf[start:], buf[p+1:])
+		buf = buf[:len(buf)-(p+1-start)]
+	}
+
+	// Add panic heading.
+	head := fmt.Sprintf("panic: %v\n\n", x)
+	if len(head) > len(buf) {
+		// Extremely unlikely to happen.
+		return head
+	}
+	copy(buf[len(head):], buf)
+	copy(buf, head)
+
+	return string(buf)
+}
+
+// context represents the context of an in-flight HTTP request.
+// It implements the appengine.Context and http.ResponseWriter interfaces.
+type context struct {
+	req *http.Request
+
+	outCode   int
+	outHeader http.Header
+	outBody   []byte
+
+	pendingLogs struct {
+		sync.Mutex
+		lines   []*logpb.UserAppLogLine
+		flushes int
+	}
+
+	apiURL *url.URL
+}
+
+var contextKey = "holds a *context"
+
+// jointContext joins two contexts in a superficial way.
+// It takes values and timeouts from a base context, and only values from another context.
+type jointContext struct {
+	base       netcontext.Context
+	valuesOnly netcontext.Context
+}
+
+func (c jointContext) Deadline() (time.Time, bool) {
+	return c.base.Deadline()
+}
+
+func (c jointContext) Done() <-chan struct{} {
+	return c.base.Done()
+}
+
+func (c jointContext) Err() error {
+	return c.base.Err()
+}
+
+func (c jointContext) Value(key interface{}) interface{} {
+	if val := c.base.Value(key); val != nil {
+		return val
+	}
+	return c.valuesOnly.Value(key)
+}
+
+// fromContext returns the App Engine context or nil if ctx is not
+// derived from an App Engine context.
+func fromContext(ctx netcontext.Context) *context {
+	c, _ := ctx.Value(&contextKey).(*context)
+	return c
+}
+
+func withContext(parent netcontext.Context, c *context) netcontext.Context {
+	ctx := netcontext.WithValue(parent, &contextKey, c)
+	if ns := c.req.Header.Get(curNamespaceHeader); ns != "" {
+		ctx = withNamespace(ctx, ns)
+	}
+	return ctx
+}
+
+func toContext(c *context) netcontext.Context {
+	return withContext(netcontext.Background(), c)
+}
+
+func IncomingHeaders(ctx netcontext.Context) http.Header {
+	if c := fromContext(ctx); c != nil {
+		return c.req.Header
+	}
+	return nil
+}
+
+func ReqContext(req *http.Request) netcontext.Context {
+	return req.Context()
+}
+
+func WithContext(parent netcontext.Context, req *http.Request) netcontext.Context {
+	return jointContext{
+		base:       parent,
+		valuesOnly: req.Context(),
+	}
+}
+
+// DefaultTicket returns a ticket used for background context or dev_appserver.
+func DefaultTicket() string {
+	defaultTicketOnce.Do(func() {
+		if IsDevAppServer() {
+			defaultTicket = "testapp" + defaultTicketSuffix
+			return
+		}
+		appID := partitionlessAppID()
+		escAppID := strings.Replace(strings.Replace(appID, ":", "_", -1), ".", "_", -1)
+		majVersion := VersionID(nil)
+		if i := strings.Index(majVersion, "."); i > 0 {
+			majVersion = majVersion[:i]
+		}
+		defaultTicket = fmt.Sprintf("%s/%s.%s.%s", escAppID, ModuleName(nil), majVersion, InstanceID())
+	})
+	return defaultTicket
+}
+
+func BackgroundContext() netcontext.Context {
+	backgroundContextOnce.Do(func() {
+		// Compute background security ticket.
+		ticket := DefaultTicket()
+
+		c := &context{
+			req: &http.Request{
+				Header: http.Header{
+					ticketHeader: []string{ticket},
+				},
+			},
+			apiURL: apiURL(),
+		}
+		backgroundContext = toContext(c)
+
+		// TODO(dsymonds): Wire up the shutdown handler to do a final flush.
+		go c.logFlusher(make(chan int))
+	})
+
+	return backgroundContext
+}
+
+// RegisterTestRequest registers the HTTP request req for testing, such that
+// any API calls are sent to the provided URL. It returns a closure to delete
+// the registration.
+// It should only be used by aetest package.
+func RegisterTestRequest(req *http.Request, apiURL *url.URL, decorate func(netcontext.Context) netcontext.Context) (*http.Request, func()) {
+	c := &context{
+		req:    req,
+		apiURL: apiURL,
+	}
+	ctx := withContext(decorate(req.Context()), c)
+	req = req.WithContext(ctx)
+	c.req = req
+	return req, func() {}
+}
+
+var errTimeout = &CallError{
+	Detail:  "Deadline exceeded",
+	Code:    int32(remotepb.RpcError_CANCELLED),
+	Timeout: true,
+}
+
+func (c *context) Header() http.Header { return c.outHeader }
+
+// Copied from $GOROOT/src/pkg/net/http/transfer.go. Some response status
+// codes do not permit a response body (nor response entity headers such as
+// Content-Length, Content-Type, etc).
+func bodyAllowedForStatus(status int) bool {
+	switch {
+	case status >= 100 && status <= 199:
+		return false
+	case status == 204:
+		return false
+	case status == 304:
+		return false
+	}
+	return true
+}
+
+func (c *context) Write(b []byte) (int, error) {
+	if c.outCode == 0 {
+		c.WriteHeader(http.StatusOK)
+	}
+	if len(b) > 0 && !bodyAllowedForStatus(c.outCode) {
+		return 0, http.ErrBodyNotAllowed
+	}
+	c.outBody = append(c.outBody, b...)
+	return len(b), nil
+}
+
+func (c *context) WriteHeader(code int) {
+	if c.outCode != 0 {
+		logf(c, 3, "WriteHeader called multiple times on request.") // error level
+		return
+	}
+	c.outCode = code
+}
+
+func (c *context) post(body []byte, timeout time.Duration) (b []byte, err error) {
+	hreq := &http.Request{
+		Method: "POST",
+		URL:    c.apiURL,
+		Header: http.Header{
+			apiEndpointHeader: apiEndpointHeaderValue,
+			apiMethodHeader:   apiMethodHeaderValue,
+			apiContentType:    apiContentTypeValue,
+			apiDeadlineHeader: []string{strconv.FormatFloat(timeout.Seconds(), 'f', -1, 64)},
+		},
+		Body:          ioutil.NopCloser(bytes.NewReader(body)),
+		ContentLength: int64(len(body)),
+		Host:          c.apiURL.Host,
+	}
+	if info := c.req.Header.Get(dapperHeader); info != "" {
+		hreq.Header.Set(dapperHeader, info)
+	}
+	if info := c.req.Header.Get(traceHeader); info != "" {
+		hreq.Header.Set(traceHeader, info)
+	}
+
+	tr := apiHTTPClient.Transport.(*http.Transport)
+
+	var timedOut int32 // atomic; set to 1 if timed out
+	t := time.AfterFunc(timeout, func() {
+		atomic.StoreInt32(&timedOut, 1)
+		tr.CancelRequest(hreq)
+	})
+	defer t.Stop()
+	defer func() {
+		// Check if timeout was exceeded.
+		if atomic.LoadInt32(&timedOut) != 0 {
+			err = errTimeout
+		}
+	}()
+
+	hresp, err := apiHTTPClient.Do(hreq)
+	if err != nil {
+		return nil, &CallError{
+			Detail: fmt.Sprintf("service bridge HTTP failed: %v", err),
+			Code:   int32(remotepb.RpcError_UNKNOWN),
+		}
+	}
+	defer hresp.Body.Close()
+	hrespBody, err := ioutil.ReadAll(hresp.Body)
+	if hresp.StatusCode != 200 {
+		return nil, &CallError{
+			Detail: fmt.Sprintf("service bridge returned HTTP %d (%q)", hresp.StatusCode, hrespBody),
+			Code:   int32(remotepb.RpcError_UNKNOWN),
+		}
+	}
+	if err != nil {
+		return nil, &CallError{
+			Detail: fmt.Sprintf("service bridge response bad: %v", err),
+			Code:   int32(remotepb.RpcError_UNKNOWN),
+		}
+	}
+	return hrespBody, nil
+}
+
+func Call(ctx netcontext.Context, service, method string, in, out proto.Message) error {
+	if ns := NamespaceFromContext(ctx); ns != "" {
+		if fn, ok := NamespaceMods[service]; ok {
+			fn(in, ns)
+		}
+	}
+
+	if f, ctx, ok := callOverrideFromContext(ctx); ok {
+		return f(ctx, service, method, in, out)
+	}
+
+	// Handle already-done contexts quickly.
+	select {
+	case <-ctx.Done():
+		return ctx.Err()
+	default:
+	}
+
+	c := fromContext(ctx)
+	if c == nil {
+		// Give a good error message rather than a panic lower down.
+		return errNotAppEngineContext
+	}
+
+	// Apply transaction modifications if we're in a transaction.
+	if t := transactionFromContext(ctx); t != nil {
+		if t.finished {
+			return errors.New("transaction context has expired")
+		}
+		applyTransaction(in, &t.transaction)
+	}
+
+	// Default RPC timeout is 60s.
+	timeout := 60 * time.Second
+	if deadline, ok := ctx.Deadline(); ok {
+		timeout = deadline.Sub(time.Now())
+	}
+
+	data, err := proto.Marshal(in)
+	if err != nil {
+		return err
+	}
+
+	ticket := c.req.Header.Get(ticketHeader)
+	// Use a test ticket under test environment.
+	if ticket == "" {
+		if appid := ctx.Value(&appIDOverrideKey); appid != nil {
+			ticket = appid.(string) + defaultTicketSuffix
+		}
+	}
+	// Fall back to use background ticket when the request ticket is not available in Flex or dev_appserver.
+	if ticket == "" {
+		ticket = DefaultTicket()
+	}
+	req := &remotepb.Request{
+		ServiceName: &service,
+		Method:      &method,
+		Request:     data,
+		RequestId:   &ticket,
+	}
+	hreqBody, err := proto.Marshal(req)
+	if err != nil {
+		return err
+	}
+
+	hrespBody, err := c.post(hreqBody, timeout)
+	if err != nil {
+		return err
+	}
+
+	res := &remotepb.Response{}
+	if err := proto.Unmarshal(hrespBody, res); err != nil {
+		return err
+	}
+	if res.RpcError != nil {
+		ce := &CallError{
+			Detail: res.RpcError.GetDetail(),
+			Code:   *res.RpcError.Code,
+		}
+		switch remotepb.RpcError_ErrorCode(ce.Code) {
+		case remotepb.RpcError_CANCELLED, remotepb.RpcError_DEADLINE_EXCEEDED:
+			ce.Timeout = true
+		}
+		return ce
+	}
+	if res.ApplicationError != nil {
+		return &APIError{
+			Service: *req.ServiceName,
+			Detail:  res.ApplicationError.GetDetail(),
+			Code:    *res.ApplicationError.Code,
+		}
+	}
+	if res.Exception != nil || res.JavaException != nil {
+		// This shouldn't happen, but let's be defensive.
+		return &CallError{
+			Detail: "service bridge returned exception",
+			Code:   int32(remotepb.RpcError_UNKNOWN),
+		}
+	}
+	return proto.Unmarshal(res.Response, out)
+}
+
+func (c *context) Request() *http.Request {
+	return c.req
+}
+
+func (c *context) addLogLine(ll *logpb.UserAppLogLine) {
+	// Truncate long log lines.
+	// TODO(dsymonds): Check if this is still necessary.
+	const lim = 8 << 10
+	if len(*ll.Message) > lim {
+		suffix := fmt.Sprintf("...(length %d)", len(*ll.Message))
+		ll.Message = proto.String((*ll.Message)[:lim-len(suffix)] + suffix)
+	}
+
+	c.pendingLogs.Lock()
+	c.pendingLogs.lines = append(c.pendingLogs.lines, ll)
+	c.pendingLogs.Unlock()
+}
+
+var logLevelName = map[int64]string{
+	0: "DEBUG",
+	1: "INFO",
+	2: "WARNING",
+	3: "ERROR",
+	4: "CRITICAL",
+}
+
+func logf(c *context, level int64, format string, args ...interface{}) {
+	if c == nil {
+		panic("not an App Engine context")
+	}
+	s := fmt.Sprintf(format, args...)
+	s = strings.TrimRight(s, "\n") // Remove any trailing newline characters.
+	c.addLogLine(&logpb.UserAppLogLine{
+		TimestampUsec: proto.Int64(time.Now().UnixNano() / 1e3),
+		Level:         &level,
+		Message:       &s,
+	})
+	log.Print(logLevelName[level] + ": " + s)
+}
+
+// flushLog attempts to flush any pending logs to the appserver.
+// It should not be called concurrently.
+func (c *context) flushLog(force bool) (flushed bool) {
+	c.pendingLogs.Lock()
+	// Grab up to 30 MB. We can get away with up to 32 MB, but let's be cautious.
+	n, rem := 0, 30<<20
+	for ; n < len(c.pendingLogs.lines); n++ {
+		ll := c.pendingLogs.lines[n]
+		// Each log line will require about 3 bytes of overhead.
+		nb := proto.Size(ll) + 3
+		if nb > rem {
+			break
+		}
+		rem -= nb
+	}
+	lines := c.pendingLogs.lines[:n]
+	c.pendingLogs.lines = c.pendingLogs.lines[n:]
+	c.pendingLogs.Unlock()
+
+	if len(lines) == 0 && !force {
+		// Nothing to flush.
+		return false
+	}
+
+	rescueLogs := false
+	defer func() {
+		if rescueLogs {
+			c.pendingLogs.Lock()
+			c.pendingLogs.lines = append(lines, c.pendingLogs.lines...)
+			c.pendingLogs.Unlock()
+		}
+	}()
+
+	buf, err := proto.Marshal(&logpb.UserAppLogGroup{
+		LogLine: lines,
+	})
+	if err != nil {
+		log.Printf("internal.flushLog: marshaling UserAppLogGroup: %v", err)
+		rescueLogs = true
+		return false
+	}
+
+	req := &logpb.FlushRequest{
+		Logs: buf,
+	}
+	res := &basepb.VoidProto{}
+	c.pendingLogs.Lock()
+	c.pendingLogs.flushes++
+	c.pendingLogs.Unlock()
+	if err := Call(toContext(c), "logservice", "Flush", req, res); err != nil {
+		log.Printf("internal.flushLog: Flush RPC: %v", err)
+		rescueLogs = true
+		return false
+	}
+	return true
+}
+
+const (
+	// Log flushing parameters.
+	flushInterval      = 1 * time.Second
+	forceFlushInterval = 60 * time.Second
+)
+
+func (c *context) logFlusher(stop <-chan int) {
+	lastFlush := time.Now()
+	tick := time.NewTicker(flushInterval)
+	for {
+		select {
+		case <-stop:
+			// Request finished.
+			tick.Stop()
+			return
+		case <-tick.C:
+			force := time.Now().Sub(lastFlush) > forceFlushInterval
+			if c.flushLog(force) {
+				lastFlush = time.Now()
+			}
+		}
+	}
+}
+
+func ContextForTesting(req *http.Request) netcontext.Context {
+	return toContext(&context{req: req})
+}
diff --git a/vendor/google.golang.org/appengine/internal/api_classic.go b/vendor/google.golang.org/appengine/internal/api_classic.go
new file mode 100644
index 0000000..f0f40b2
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/api_classic.go
@@ -0,0 +1,169 @@
+// Copyright 2015 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// +build appengine
+
+package internal
+
+import (
+	"errors"
+	"fmt"
+	"net/http"
+	"time"
+
+	"appengine"
+	"appengine_internal"
+	basepb "appengine_internal/base"
+
+	"github.com/golang/protobuf/proto"
+	netcontext "golang.org/x/net/context"
+)
+
+var contextKey = "holds an appengine.Context"
+
+// fromContext returns the App Engine context or nil if ctx is not
+// derived from an App Engine context.
+func fromContext(ctx netcontext.Context) appengine.Context {
+	c, _ := ctx.Value(&contextKey).(appengine.Context)
+	return c
+}
+
+// This is only for classic App Engine adapters.
+func ClassicContextFromContext(ctx netcontext.Context) (appengine.Context, error) {
+	c := fromContext(ctx)
+	if c == nil {
+		return nil, errNotAppEngineContext
+	}
+	return c, nil
+}
+
+func withContext(parent netcontext.Context, c appengine.Context) netcontext.Context {
+	ctx := netcontext.WithValue(parent, &contextKey, c)
+
+	s := &basepb.StringProto{}
+	c.Call("__go__", "GetNamespace", &basepb.VoidProto{}, s, nil)
+	if ns := s.GetValue(); ns != "" {
+		ctx = NamespacedContext(ctx, ns)
+	}
+
+	return ctx
+}
+
+func IncomingHeaders(ctx netcontext.Context) http.Header {
+	if c := fromContext(ctx); c != nil {
+		if req, ok := c.Request().(*http.Request); ok {
+			return req.Header
+		}
+	}
+	return nil
+}
+
+func ReqContext(req *http.Request) netcontext.Context {
+	return WithContext(netcontext.Background(), req)
+}
+
+func WithContext(parent netcontext.Context, req *http.Request) netcontext.Context {
+	c := appengine.NewContext(req)
+	return withContext(parent, c)
+}
+
+type testingContext struct {
+	appengine.Context
+
+	req *http.Request
+}
+
+func (t *testingContext) FullyQualifiedAppID() string { return "dev~testcontext" }
+func (t *testingContext) Call(service, method string, _, _ appengine_internal.ProtoMessage, _ *appengine_internal.CallOptions) error {
+	if service == "__go__" && method == "GetNamespace" {
+		return nil
+	}
+	return fmt.Errorf("testingContext: unsupported Call")
+}
+func (t *testingContext) Request() interface{} { return t.req }
+
+func ContextForTesting(req *http.Request) netcontext.Context {
+	return withContext(netcontext.Background(), &testingContext{req: req})
+}
+
+func Call(ctx netcontext.Context, service, method string, in, out proto.Message) error {
+	if ns := NamespaceFromContext(ctx); ns != "" {
+		if fn, ok := NamespaceMods[service]; ok {
+			fn(in, ns)
+		}
+	}
+
+	if f, ctx, ok := callOverrideFromContext(ctx); ok {
+		return f(ctx, service, method, in, out)
+	}
+
+	// Handle already-done contexts quickly.
+	select {
+	case <-ctx.Done():
+		return ctx.Err()
+	default:
+	}
+
+	c := fromContext(ctx)
+	if c == nil {
+		// Give a good error message rather than a panic lower down.
+		return errNotAppEngineContext
+	}
+
+	// Apply transaction modifications if we're in a transaction.
+	if t := transactionFromContext(ctx); t != nil {
+		if t.finished {
+			return errors.New("transaction context has expired")
+		}
+		applyTransaction(in, &t.transaction)
+	}
+
+	var opts *appengine_internal.CallOptions
+	if d, ok := ctx.Deadline(); ok {
+		opts = &appengine_internal.CallOptions{
+			Timeout: d.Sub(time.Now()),
+		}
+	}
+
+	err := c.Call(service, method, in, out, opts)
+	switch v := err.(type) {
+	case *appengine_internal.APIError:
+		return &APIError{
+			Service: v.Service,
+			Detail:  v.Detail,
+			Code:    v.Code,
+		}
+	case *appengine_internal.CallError:
+		return &CallError{
+			Detail:  v.Detail,
+			Code:    v.Code,
+			Timeout: v.Timeout,
+		}
+	}
+	return err
+}
+
+func handleHTTP(w http.ResponseWriter, r *http.Request) {
+	panic("handleHTTP called; this should be impossible")
+}
+
+func logf(c appengine.Context, level int64, format string, args ...interface{}) {
+	var fn func(format string, args ...interface{})
+	switch level {
+	case 0:
+		fn = c.Debugf
+	case 1:
+		fn = c.Infof
+	case 2:
+		fn = c.Warningf
+	case 3:
+		fn = c.Errorf
+	case 4:
+		fn = c.Criticalf
+	default:
+		// This shouldn't happen.
+		fn = c.Criticalf
+	}
+	fn(format, args...)
+}
diff --git a/vendor/google.golang.org/appengine/internal/api_common.go b/vendor/google.golang.org/appengine/internal/api_common.go
new file mode 100644
index 0000000..e0c0b21
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/api_common.go
@@ -0,0 +1,123 @@
+// Copyright 2015 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package internal
+
+import (
+	"errors"
+	"os"
+
+	"github.com/golang/protobuf/proto"
+	netcontext "golang.org/x/net/context"
+)
+
+var errNotAppEngineContext = errors.New("not an App Engine context")
+
+type CallOverrideFunc func(ctx netcontext.Context, service, method string, in, out proto.Message) error
+
+var callOverrideKey = "holds []CallOverrideFunc"
+
+func WithCallOverride(ctx netcontext.Context, f CallOverrideFunc) netcontext.Context {
+	// We avoid appending to any existing call override
+	// so we don't risk overwriting a popped stack below.
+	var cofs []CallOverrideFunc
+	if uf, ok := ctx.Value(&callOverrideKey).([]CallOverrideFunc); ok {
+		cofs = append(cofs, uf...)
+	}
+	cofs = append(cofs, f)
+	return netcontext.WithValue(ctx, &callOverrideKey, cofs)
+}
+
+func callOverrideFromContext(ctx netcontext.Context) (CallOverrideFunc, netcontext.Context, bool) {
+	cofs, _ := ctx.Value(&callOverrideKey).([]CallOverrideFunc)
+	if len(cofs) == 0 {
+		return nil, nil, false
+	}
+	// We found a list of overrides; grab the last, and reconstitute a
+	// context that will hide it.
+	f := cofs[len(cofs)-1]
+	ctx = netcontext.WithValue(ctx, &callOverrideKey, cofs[:len(cofs)-1])
+	return f, ctx, true
+}
+
+type logOverrideFunc func(level int64, format string, args ...interface{})
+
+var logOverrideKey = "holds a logOverrideFunc"
+
+func WithLogOverride(ctx netcontext.Context, f logOverrideFunc) netcontext.Context {
+	return netcontext.WithValue(ctx, &logOverrideKey, f)
+}
+
+var appIDOverrideKey = "holds a string, being the full app ID"
+
+func WithAppIDOverride(ctx netcontext.Context, appID string) netcontext.Context {
+	return netcontext.WithValue(ctx, &appIDOverrideKey, appID)
+}
+
+var namespaceKey = "holds the namespace string"
+
+func withNamespace(ctx netcontext.Context, ns string) netcontext.Context {
+	return netcontext.WithValue(ctx, &namespaceKey, ns)
+}
+
+func NamespaceFromContext(ctx netcontext.Context) string {
+	// If there's no namespace, return the empty string.
+	ns, _ := ctx.Value(&namespaceKey).(string)
+	return ns
+}
+
+// FullyQualifiedAppID returns the fully-qualified application ID.
+// This may contain a partition prefix (e.g. "s~" for High Replication apps),
+// or a domain prefix (e.g. "example.com:").
+func FullyQualifiedAppID(ctx netcontext.Context) string {
+	if id, ok := ctx.Value(&appIDOverrideKey).(string); ok {
+		return id
+	}
+	return fullyQualifiedAppID(ctx)
+}
+
+func Logf(ctx netcontext.Context, level int64, format string, args ...interface{}) {
+	if f, ok := ctx.Value(&logOverrideKey).(logOverrideFunc); ok {
+		f(level, format, args...)
+		return
+	}
+	c := fromContext(ctx)
+	if c == nil {
+		panic(errNotAppEngineContext)
+	}
+	logf(c, level, format, args...)
+}
+
+// NamespacedContext wraps a Context to support namespaces.
+func NamespacedContext(ctx netcontext.Context, namespace string) netcontext.Context {
+	return withNamespace(ctx, namespace)
+}
+
+// SetTestEnv sets the env variables for testing background ticket in Flex.
+func SetTestEnv() func() {
+	var environ = []struct {
+		key, value string
+	}{
+		{"GAE_LONG_APP_ID", "my-app-id"},
+		{"GAE_MINOR_VERSION", "067924799508853122"},
+		{"GAE_MODULE_INSTANCE", "0"},
+		{"GAE_MODULE_NAME", "default"},
+		{"GAE_MODULE_VERSION", "20150612t184001"},
+	}
+
+	for _, v := range environ {
+		old := os.Getenv(v.key)
+		os.Setenv(v.key, v.value)
+		v.value = old
+	}
+	return func() { // Restore old environment after the test completes.
+		for _, v := range environ {
+			if v.value == "" {
+				os.Unsetenv(v.key)
+				continue
+			}
+			os.Setenv(v.key, v.value)
+		}
+	}
+}
diff --git a/vendor/google.golang.org/appengine/internal/api_pre17.go b/vendor/google.golang.org/appengine/internal/api_pre17.go
new file mode 100644
index 0000000..028b4f0
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/api_pre17.go
@@ -0,0 +1,682 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// +build !appengine
+// +build !go1.7
+
+package internal
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net"
+	"net/http"
+	"net/url"
+	"os"
+	"runtime"
+	"strconv"
+	"strings"
+	"sync"
+	"sync/atomic"
+	"time"
+
+	"github.com/golang/protobuf/proto"
+	netcontext "golang.org/x/net/context"
+
+	basepb "google.golang.org/appengine/internal/base"
+	logpb "google.golang.org/appengine/internal/log"
+	remotepb "google.golang.org/appengine/internal/remote_api"
+)
+
+const (
+	apiPath             = "/rpc_http"
+	defaultTicketSuffix = "/default.20150612t184001.0"
+)
+
+var (
+	// Incoming headers.
+	ticketHeader       = http.CanonicalHeaderKey("X-AppEngine-API-Ticket")
+	dapperHeader       = http.CanonicalHeaderKey("X-Google-DapperTraceInfo")
+	traceHeader        = http.CanonicalHeaderKey("X-Cloud-Trace-Context")
+	curNamespaceHeader = http.CanonicalHeaderKey("X-AppEngine-Current-Namespace")
+	userIPHeader       = http.CanonicalHeaderKey("X-AppEngine-User-IP")
+	remoteAddrHeader   = http.CanonicalHeaderKey("X-AppEngine-Remote-Addr")
+
+	// Outgoing headers.
+	apiEndpointHeader      = http.CanonicalHeaderKey("X-Google-RPC-Service-Endpoint")
+	apiEndpointHeaderValue = []string{"app-engine-apis"}
+	apiMethodHeader        = http.CanonicalHeaderKey("X-Google-RPC-Service-Method")
+	apiMethodHeaderValue   = []string{"/VMRemoteAPI.CallRemoteAPI"}
+	apiDeadlineHeader      = http.CanonicalHeaderKey("X-Google-RPC-Service-Deadline")
+	apiContentType         = http.CanonicalHeaderKey("Content-Type")
+	apiContentTypeValue    = []string{"application/octet-stream"}
+	logFlushHeader         = http.CanonicalHeaderKey("X-AppEngine-Log-Flush-Count")
+
+	apiHTTPClient = &http.Client{
+		Transport: &http.Transport{
+			Proxy: http.ProxyFromEnvironment,
+			Dial:  limitDial,
+		},
+	}
+
+	defaultTicketOnce sync.Once
+	defaultTicket     string
+)
+
+func apiURL() *url.URL {
+	host, port := "appengine.googleapis.internal", "10001"
+	if h := os.Getenv("API_HOST"); h != "" {
+		host = h
+	}
+	if p := os.Getenv("API_PORT"); p != "" {
+		port = p
+	}
+	return &url.URL{
+		Scheme: "http",
+		Host:   host + ":" + port,
+		Path:   apiPath,
+	}
+}
+
+func handleHTTP(w http.ResponseWriter, r *http.Request) {
+	c := &context{
+		req:       r,
+		outHeader: w.Header(),
+		apiURL:    apiURL(),
+	}
+	stopFlushing := make(chan int)
+
+	ctxs.Lock()
+	ctxs.m[r] = c
+	ctxs.Unlock()
+	defer func() {
+		ctxs.Lock()
+		delete(ctxs.m, r)
+		ctxs.Unlock()
+	}()
+
+	// Patch up RemoteAddr so it looks reasonable.
+	if addr := r.Header.Get(userIPHeader); addr != "" {
+		r.RemoteAddr = addr
+	} else if addr = r.Header.Get(remoteAddrHeader); addr != "" {
+		r.RemoteAddr = addr
+	} else {
+		// Should not normally reach here, but pick a sensible default anyway.
+		r.RemoteAddr = "127.0.0.1"
+	}
+	// The address in the headers will most likely be of these forms:
+	//	123.123.123.123
+	//	2001:db8::1
+	// net/http.Request.RemoteAddr is specified to be in "IP:port" form.
+	if _, _, err := net.SplitHostPort(r.RemoteAddr); err != nil {
+		// Assume the remote address is only a host; add a default port.
+		r.RemoteAddr = net.JoinHostPort(r.RemoteAddr, "80")
+	}
+
+	// Start goroutine responsible for flushing app logs.
+	// This is done after adding c to ctx.m (and stopped before removing it)
+	// because flushing logs requires making an API call.
+	go c.logFlusher(stopFlushing)
+
+	executeRequestSafely(c, r)
+	c.outHeader = nil // make sure header changes aren't respected any more
+
+	stopFlushing <- 1 // any logging beyond this point will be dropped
+
+	// Flush any pending logs asynchronously.
+	c.pendingLogs.Lock()
+	flushes := c.pendingLogs.flushes
+	if len(c.pendingLogs.lines) > 0 {
+		flushes++
+	}
+	c.pendingLogs.Unlock()
+	go c.flushLog(false)
+	w.Header().Set(logFlushHeader, strconv.Itoa(flushes))
+
+	// Avoid nil Write call if c.Write is never called.
+	if c.outCode != 0 {
+		w.WriteHeader(c.outCode)
+	}
+	if c.outBody != nil {
+		w.Write(c.outBody)
+	}
+}
+
+func executeRequestSafely(c *context, r *http.Request) {
+	defer func() {
+		if x := recover(); x != nil {
+			logf(c, 4, "%s", renderPanic(x)) // 4 == critical
+			c.outCode = 500
+		}
+	}()
+
+	http.DefaultServeMux.ServeHTTP(c, r)
+}
+
+func renderPanic(x interface{}) string {
+	buf := make([]byte, 16<<10) // 16 KB should be plenty
+	buf = buf[:runtime.Stack(buf, false)]
+
+	// Remove the first few stack frames:
+	//   this func
+	//   the recover closure in the caller
+	// That will root the stack trace at the site of the panic.
+	const (
+		skipStart  = "internal.renderPanic"
+		skipFrames = 2
+	)
+	start := bytes.Index(buf, []byte(skipStart))
+	p := start
+	for i := 0; i < skipFrames*2 && p+1 < len(buf); i++ {
+		p = bytes.IndexByte(buf[p+1:], '\n') + p + 1
+		if p < 0 {
+			break
+		}
+	}
+	if p >= 0 {
+		// buf[start:p+1] is the block to remove.
+		// Copy buf[p+1:] over buf[start:] and shrink buf.
+		copy(buf[start:], buf[p+1:])
+		buf = buf[:len(buf)-(p+1-start)]
+	}
+
+	// Add panic heading.
+	head := fmt.Sprintf("panic: %v\n\n", x)
+	if len(head) > len(buf) {
+		// Extremely unlikely to happen.
+		return head
+	}
+	copy(buf[len(head):], buf)
+	copy(buf, head)
+
+	return string(buf)
+}
+
+var ctxs = struct {
+	sync.Mutex
+	m  map[*http.Request]*context
+	bg *context // background context, lazily initialized
+	// dec is used by tests to decorate the netcontext.Context returned
+	// for a given request. This allows tests to add overrides (such as
+	// WithAppIDOverride) to the context. The map is nil outside tests.
+	dec map[*http.Request]func(netcontext.Context) netcontext.Context
+}{
+	m: make(map[*http.Request]*context),
+}
+
+// context represents the context of an in-flight HTTP request.
+// It implements the appengine.Context and http.ResponseWriter interfaces.
+type context struct {
+	req *http.Request
+
+	outCode   int
+	outHeader http.Header
+	outBody   []byte
+
+	pendingLogs struct {
+		sync.Mutex
+		lines   []*logpb.UserAppLogLine
+		flushes int
+	}
+
+	apiURL *url.URL
+}
+
+var contextKey = "holds a *context"
+
+// fromContext returns the App Engine context or nil if ctx is not
+// derived from an App Engine context.
+func fromContext(ctx netcontext.Context) *context {
+	c, _ := ctx.Value(&contextKey).(*context)
+	return c
+}
+
+func withContext(parent netcontext.Context, c *context) netcontext.Context {
+	ctx := netcontext.WithValue(parent, &contextKey, c)
+	if ns := c.req.Header.Get(curNamespaceHeader); ns != "" {
+		ctx = withNamespace(ctx, ns)
+	}
+	return ctx
+}
+
+func toContext(c *context) netcontext.Context {
+	return withContext(netcontext.Background(), c)
+}
+
+func IncomingHeaders(ctx netcontext.Context) http.Header {
+	if c := fromContext(ctx); c != nil {
+		return c.req.Header
+	}
+	return nil
+}
+
+func ReqContext(req *http.Request) netcontext.Context {
+	return WithContext(netcontext.Background(), req)
+}
+
+func WithContext(parent netcontext.Context, req *http.Request) netcontext.Context {
+	ctxs.Lock()
+	c := ctxs.m[req]
+	d := ctxs.dec[req]
+	ctxs.Unlock()
+
+	if d != nil {
+		parent = d(parent)
+	}
+
+	if c == nil {
+		// Someone passed in an http.Request that is not in-flight.
+		// We panic here rather than panicking at a later point
+		// so that stack traces will be more sensible.
+		log.Panic("appengine: NewContext passed an unknown http.Request")
+	}
+	return withContext(parent, c)
+}
+
+// DefaultTicket returns a ticket used for background context or dev_appserver.
+func DefaultTicket() string {
+	defaultTicketOnce.Do(func() {
+		if IsDevAppServer() {
+			defaultTicket = "testapp" + defaultTicketSuffix
+			return
+		}
+		appID := partitionlessAppID()
+		escAppID := strings.Replace(strings.Replace(appID, ":", "_", -1), ".", "_", -1)
+		majVersion := VersionID(nil)
+		if i := strings.Index(majVersion, "."); i > 0 {
+			majVersion = majVersion[:i]
+		}
+		defaultTicket = fmt.Sprintf("%s/%s.%s.%s", escAppID, ModuleName(nil), majVersion, InstanceID())
+	})
+	return defaultTicket
+}
+
+func BackgroundContext() netcontext.Context {
+	ctxs.Lock()
+	defer ctxs.Unlock()
+
+	if ctxs.bg != nil {
+		return toContext(ctxs.bg)
+	}
+
+	// Compute background security ticket.
+	ticket := DefaultTicket()
+
+	ctxs.bg = &context{
+		req: &http.Request{
+			Header: http.Header{
+				ticketHeader: []string{ticket},
+			},
+		},
+		apiURL: apiURL(),
+	}
+
+	// TODO(dsymonds): Wire up the shutdown handler to do a final flush.
+	go ctxs.bg.logFlusher(make(chan int))
+
+	return toContext(ctxs.bg)
+}
+
+// RegisterTestRequest registers the HTTP request req for testing, such that
+// any API calls are sent to the provided URL. It returns a closure to delete
+// the registration.
+// It should only be used by aetest package.
+func RegisterTestRequest(req *http.Request, apiURL *url.URL, decorate func(netcontext.Context) netcontext.Context) (*http.Request, func()) {
+	c := &context{
+		req:    req,
+		apiURL: apiURL,
+	}
+	ctxs.Lock()
+	defer ctxs.Unlock()
+	if _, ok := ctxs.m[req]; ok {
+		log.Panic("req already associated with context")
+	}
+	if _, ok := ctxs.dec[req]; ok {
+		log.Panic("req already associated with context")
+	}
+	if ctxs.dec == nil {
+		ctxs.dec = make(map[*http.Request]func(netcontext.Context) netcontext.Context)
+	}
+	ctxs.m[req] = c
+	ctxs.dec[req] = decorate
+
+	return req, func() {
+		ctxs.Lock()
+		delete(ctxs.m, req)
+		delete(ctxs.dec, req)
+		ctxs.Unlock()
+	}
+}
+
+var errTimeout = &CallError{
+	Detail:  "Deadline exceeded",
+	Code:    int32(remotepb.RpcError_CANCELLED),
+	Timeout: true,
+}
+
+func (c *context) Header() http.Header { return c.outHeader }
+
+// Copied from $GOROOT/src/pkg/net/http/transfer.go. Some response status
+// codes do not permit a response body (nor response entity headers such as
+// Content-Length, Content-Type, etc).
+func bodyAllowedForStatus(status int) bool {
+	switch {
+	case status >= 100 && status <= 199:
+		return false
+	case status == 204:
+		return false
+	case status == 304:
+		return false
+	}
+	return true
+}
+
+func (c *context) Write(b []byte) (int, error) {
+	if c.outCode == 0 {
+		c.WriteHeader(http.StatusOK)
+	}
+	if len(b) > 0 && !bodyAllowedForStatus(c.outCode) {
+		return 0, http.ErrBodyNotAllowed
+	}
+	c.outBody = append(c.outBody, b...)
+	return len(b), nil
+}
+
+func (c *context) WriteHeader(code int) {
+	if c.outCode != 0 {
+		logf(c, 3, "WriteHeader called multiple times on request.") // error level
+		return
+	}
+	c.outCode = code
+}
+
+func (c *context) post(body []byte, timeout time.Duration) (b []byte, err error) {
+	hreq := &http.Request{
+		Method: "POST",
+		URL:    c.apiURL,
+		Header: http.Header{
+			apiEndpointHeader: apiEndpointHeaderValue,
+			apiMethodHeader:   apiMethodHeaderValue,
+			apiContentType:    apiContentTypeValue,
+			apiDeadlineHeader: []string{strconv.FormatFloat(timeout.Seconds(), 'f', -1, 64)},
+		},
+		Body:          ioutil.NopCloser(bytes.NewReader(body)),
+		ContentLength: int64(len(body)),
+		Host:          c.apiURL.Host,
+	}
+	if info := c.req.Header.Get(dapperHeader); info != "" {
+		hreq.Header.Set(dapperHeader, info)
+	}
+	if info := c.req.Header.Get(traceHeader); info != "" {
+		hreq.Header.Set(traceHeader, info)
+	}
+
+	tr := apiHTTPClient.Transport.(*http.Transport)
+
+	var timedOut int32 // atomic; set to 1 if timed out
+	t := time.AfterFunc(timeout, func() {
+		atomic.StoreInt32(&timedOut, 1)
+		tr.CancelRequest(hreq)
+	})
+	defer t.Stop()
+	defer func() {
+		// Check if timeout was exceeded.
+		if atomic.LoadInt32(&timedOut) != 0 {
+			err = errTimeout
+		}
+	}()
+
+	hresp, err := apiHTTPClient.Do(hreq)
+	if err != nil {
+		return nil, &CallError{
+			Detail: fmt.Sprintf("service bridge HTTP failed: %v", err),
+			Code:   int32(remotepb.RpcError_UNKNOWN),
+		}
+	}
+	defer hresp.Body.Close()
+	hrespBody, err := ioutil.ReadAll(hresp.Body)
+	if hresp.StatusCode != 200 {
+		return nil, &CallError{
+			Detail: fmt.Sprintf("service bridge returned HTTP %d (%q)", hresp.StatusCode, hrespBody),
+			Code:   int32(remotepb.RpcError_UNKNOWN),
+		}
+	}
+	if err != nil {
+		return nil, &CallError{
+			Detail: fmt.Sprintf("service bridge response bad: %v", err),
+			Code:   int32(remotepb.RpcError_UNKNOWN),
+		}
+	}
+	return hrespBody, nil
+}
+
+func Call(ctx netcontext.Context, service, method string, in, out proto.Message) error {
+	if ns := NamespaceFromContext(ctx); ns != "" {
+		if fn, ok := NamespaceMods[service]; ok {
+			fn(in, ns)
+		}
+	}
+
+	if f, ctx, ok := callOverrideFromContext(ctx); ok {
+		return f(ctx, service, method, in, out)
+	}
+
+	// Handle already-done contexts quickly.
+	select {
+	case <-ctx.Done():
+		return ctx.Err()
+	default:
+	}
+
+	c := fromContext(ctx)
+	if c == nil {
+		// Give a good error message rather than a panic lower down.
+		return errNotAppEngineContext
+	}
+
+	// Apply transaction modifications if we're in a transaction.
+	if t := transactionFromContext(ctx); t != nil {
+		if t.finished {
+			return errors.New("transaction context has expired")
+		}
+		applyTransaction(in, &t.transaction)
+	}
+
+	// Default RPC timeout is 60s.
+	timeout := 60 * time.Second
+	if deadline, ok := ctx.Deadline(); ok {
+		timeout = deadline.Sub(time.Now())
+	}
+
+	data, err := proto.Marshal(in)
+	if err != nil {
+		return err
+	}
+
+	ticket := c.req.Header.Get(ticketHeader)
+	// Use a test ticket under test environment.
+	if ticket == "" {
+		if appid := ctx.Value(&appIDOverrideKey); appid != nil {
+			ticket = appid.(string) + defaultTicketSuffix
+		}
+	}
+	// Fall back to use background ticket when the request ticket is not available in Flex or dev_appserver.
+	if ticket == "" {
+		ticket = DefaultTicket()
+	}
+	req := &remotepb.Request{
+		ServiceName: &service,
+		Method:      &method,
+		Request:     data,
+		RequestId:   &ticket,
+	}
+	hreqBody, err := proto.Marshal(req)
+	if err != nil {
+		return err
+	}
+
+	hrespBody, err := c.post(hreqBody, timeout)
+	if err != nil {
+		return err
+	}
+
+	res := &remotepb.Response{}
+	if err := proto.Unmarshal(hrespBody, res); err != nil {
+		return err
+	}
+	if res.RpcError != nil {
+		ce := &CallError{
+			Detail: res.RpcError.GetDetail(),
+			Code:   *res.RpcError.Code,
+		}
+		switch remotepb.RpcError_ErrorCode(ce.Code) {
+		case remotepb.RpcError_CANCELLED, remotepb.RpcError_DEADLINE_EXCEEDED:
+			ce.Timeout = true
+		}
+		return ce
+	}
+	if res.ApplicationError != nil {
+		return &APIError{
+			Service: *req.ServiceName,
+			Detail:  res.ApplicationError.GetDetail(),
+			Code:    *res.ApplicationError.Code,
+		}
+	}
+	if res.Exception != nil || res.JavaException != nil {
+		// This shouldn't happen, but let's be defensive.
+		return &CallError{
+			Detail: "service bridge returned exception",
+			Code:   int32(remotepb.RpcError_UNKNOWN),
+		}
+	}
+	return proto.Unmarshal(res.Response, out)
+}
+
+func (c *context) Request() *http.Request {
+	return c.req
+}
+
+func (c *context) addLogLine(ll *logpb.UserAppLogLine) {
+	// Truncate long log lines.
+	// TODO(dsymonds): Check if this is still necessary.
+	const lim = 8 << 10
+	if len(*ll.Message) > lim {
+		suffix := fmt.Sprintf("...(length %d)", len(*ll.Message))
+		ll.Message = proto.String((*ll.Message)[:lim-len(suffix)] + suffix)
+	}
+
+	c.pendingLogs.Lock()
+	c.pendingLogs.lines = append(c.pendingLogs.lines, ll)
+	c.pendingLogs.Unlock()
+}
+
+var logLevelName = map[int64]string{
+	0: "DEBUG",
+	1: "INFO",
+	2: "WARNING",
+	3: "ERROR",
+	4: "CRITICAL",
+}
+
+func logf(c *context, level int64, format string, args ...interface{}) {
+	if c == nil {
+		panic("not an App Engine context")
+	}
+	s := fmt.Sprintf(format, args...)
+	s = strings.TrimRight(s, "\n") // Remove any trailing newline characters.
+	c.addLogLine(&logpb.UserAppLogLine{
+		TimestampUsec: proto.Int64(time.Now().UnixNano() / 1e3),
+		Level:         &level,
+		Message:       &s,
+	})
+	log.Print(logLevelName[level] + ": " + s)
+}
+
+// flushLog attempts to flush any pending logs to the appserver.
+// It should not be called concurrently.
+func (c *context) flushLog(force bool) (flushed bool) {
+	c.pendingLogs.Lock()
+	// Grab up to 30 MB. We can get away with up to 32 MB, but let's be cautious.
+	n, rem := 0, 30<<20
+	for ; n < len(c.pendingLogs.lines); n++ {
+		ll := c.pendingLogs.lines[n]
+		// Each log line will require about 3 bytes of overhead.
+		nb := proto.Size(ll) + 3
+		if nb > rem {
+			break
+		}
+		rem -= nb
+	}
+	lines := c.pendingLogs.lines[:n]
+	c.pendingLogs.lines = c.pendingLogs.lines[n:]
+	c.pendingLogs.Unlock()
+
+	if len(lines) == 0 && !force {
+		// Nothing to flush.
+		return false
+	}
+
+	rescueLogs := false
+	defer func() {
+		if rescueLogs {
+			c.pendingLogs.Lock()
+			c.pendingLogs.lines = append(lines, c.pendingLogs.lines...)
+			c.pendingLogs.Unlock()
+		}
+	}()
+
+	buf, err := proto.Marshal(&logpb.UserAppLogGroup{
+		LogLine: lines,
+	})
+	if err != nil {
+		log.Printf("internal.flushLog: marshaling UserAppLogGroup: %v", err)
+		rescueLogs = true
+		return false
+	}
+
+	req := &logpb.FlushRequest{
+		Logs: buf,
+	}
+	res := &basepb.VoidProto{}
+	c.pendingLogs.Lock()
+	c.pendingLogs.flushes++
+	c.pendingLogs.Unlock()
+	if err := Call(toContext(c), "logservice", "Flush", req, res); err != nil {
+		log.Printf("internal.flushLog: Flush RPC: %v", err)
+		rescueLogs = true
+		return false
+	}
+	return true
+}
+
+const (
+	// Log flushing parameters.
+	flushInterval      = 1 * time.Second
+	forceFlushInterval = 60 * time.Second
+)
+
+func (c *context) logFlusher(stop <-chan int) {
+	lastFlush := time.Now()
+	tick := time.NewTicker(flushInterval)
+	for {
+		select {
+		case <-stop:
+			// Request finished.
+			tick.Stop()
+			return
+		case <-tick.C:
+			force := time.Now().Sub(lastFlush) > forceFlushInterval
+			if c.flushLog(force) {
+				lastFlush = time.Now()
+			}
+		}
+	}
+}
+
+func ContextForTesting(req *http.Request) netcontext.Context {
+	return toContext(&context{req: req})
+}
diff --git a/vendor/google.golang.org/appengine/internal/app_id.go b/vendor/google.golang.org/appengine/internal/app_id.go
new file mode 100644
index 0000000..11df8c0
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/app_id.go
@@ -0,0 +1,28 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package internal
+
+import (
+	"strings"
+)
+
+func parseFullAppID(appid string) (partition, domain, displayID string) {
+	if i := strings.Index(appid, "~"); i != -1 {
+		partition, appid = appid[:i], appid[i+1:]
+	}
+	if i := strings.Index(appid, ":"); i != -1 {
+		domain, appid = appid[:i], appid[i+1:]
+	}
+	return partition, domain, appid
+}
+
+// appID returns "appid" or "domain.com:appid".
+func appID(fullAppID string) string {
+	_, dom, dis := parseFullAppID(fullAppID)
+	if dom != "" {
+		return dom + ":" + dis
+	}
+	return dis
+}
diff --git a/vendor/google.golang.org/appengine/internal/base/api_base.pb.go b/vendor/google.golang.org/appengine/internal/base/api_base.pb.go
new file mode 100644
index 0000000..6205a7a
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/base/api_base.pb.go
@@ -0,0 +1,176 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: google.golang.org/appengine/internal/base/api_base.proto
+
+/*
+Package base is a generated protocol buffer package.
+
+It is generated from these files:
+	google.golang.org/appengine/internal/base/api_base.proto
+
+It has these top-level messages:
+	StringProto
+	Integer32Proto
+	Integer64Proto
+	BoolProto
+	DoubleProto
+	BytesProto
+	VoidProto
+*/
+package base
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+type StringProto struct {
+	Value            *string `protobuf:"bytes,1,req,name=value" json:"value,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *StringProto) Reset()                    { *m = StringProto{} }
+func (m *StringProto) String() string            { return proto.CompactTextString(m) }
+func (*StringProto) ProtoMessage()               {}
+func (*StringProto) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+func (m *StringProto) GetValue() string {
+	if m != nil && m.Value != nil {
+		return *m.Value
+	}
+	return ""
+}
+
+type Integer32Proto struct {
+	Value            *int32 `protobuf:"varint,1,req,name=value" json:"value,omitempty"`
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Integer32Proto) Reset()                    { *m = Integer32Proto{} }
+func (m *Integer32Proto) String() string            { return proto.CompactTextString(m) }
+func (*Integer32Proto) ProtoMessage()               {}
+func (*Integer32Proto) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+
+func (m *Integer32Proto) GetValue() int32 {
+	if m != nil && m.Value != nil {
+		return *m.Value
+	}
+	return 0
+}
+
+type Integer64Proto struct {
+	Value            *int64 `protobuf:"varint,1,req,name=value" json:"value,omitempty"`
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Integer64Proto) Reset()                    { *m = Integer64Proto{} }
+func (m *Integer64Proto) String() string            { return proto.CompactTextString(m) }
+func (*Integer64Proto) ProtoMessage()               {}
+func (*Integer64Proto) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+
+func (m *Integer64Proto) GetValue() int64 {
+	if m != nil && m.Value != nil {
+		return *m.Value
+	}
+	return 0
+}
+
+type BoolProto struct {
+	Value            *bool  `protobuf:"varint,1,req,name=value" json:"value,omitempty"`
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *BoolProto) Reset()                    { *m = BoolProto{} }
+func (m *BoolProto) String() string            { return proto.CompactTextString(m) }
+func (*BoolProto) ProtoMessage()               {}
+func (*BoolProto) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
+
+func (m *BoolProto) GetValue() bool {
+	if m != nil && m.Value != nil {
+		return *m.Value
+	}
+	return false
+}
+
+type DoubleProto struct {
+	Value            *float64 `protobuf:"fixed64,1,req,name=value" json:"value,omitempty"`
+	XXX_unrecognized []byte   `json:"-"`
+}
+
+func (m *DoubleProto) Reset()                    { *m = DoubleProto{} }
+func (m *DoubleProto) String() string            { return proto.CompactTextString(m) }
+func (*DoubleProto) ProtoMessage()               {}
+func (*DoubleProto) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
+
+func (m *DoubleProto) GetValue() float64 {
+	if m != nil && m.Value != nil {
+		return *m.Value
+	}
+	return 0
+}
+
+type BytesProto struct {
+	Value            []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"`
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *BytesProto) Reset()                    { *m = BytesProto{} }
+func (m *BytesProto) String() string            { return proto.CompactTextString(m) }
+func (*BytesProto) ProtoMessage()               {}
+func (*BytesProto) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
+
+func (m *BytesProto) GetValue() []byte {
+	if m != nil {
+		return m.Value
+	}
+	return nil
+}
+
+type VoidProto struct {
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *VoidProto) Reset()                    { *m = VoidProto{} }
+func (m *VoidProto) String() string            { return proto.CompactTextString(m) }
+func (*VoidProto) ProtoMessage()               {}
+func (*VoidProto) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
+
+func init() {
+	proto.RegisterType((*StringProto)(nil), "appengine.base.StringProto")
+	proto.RegisterType((*Integer32Proto)(nil), "appengine.base.Integer32Proto")
+	proto.RegisterType((*Integer64Proto)(nil), "appengine.base.Integer64Proto")
+	proto.RegisterType((*BoolProto)(nil), "appengine.base.BoolProto")
+	proto.RegisterType((*DoubleProto)(nil), "appengine.base.DoubleProto")
+	proto.RegisterType((*BytesProto)(nil), "appengine.base.BytesProto")
+	proto.RegisterType((*VoidProto)(nil), "appengine.base.VoidProto")
+}
+
+func init() {
+	proto.RegisterFile("google.golang.org/appengine/internal/base/api_base.proto", fileDescriptor0)
+}
+
+var fileDescriptor0 = []byte{
+	// 199 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0xcf, 0x3f, 0x4b, 0xc6, 0x30,
+	0x10, 0x06, 0x70, 0x5a, 0xad, 0xb4, 0x57, 0xe9, 0x20, 0x0e, 0x1d, 0xb5, 0x05, 0x71, 0x4a, 0x40,
+	0x45, 0x9c, 0x83, 0x8b, 0x9b, 0x28, 0x38, 0xb8, 0x48, 0x8a, 0xc7, 0x11, 0x08, 0xb9, 0x90, 0xa6,
+	0x82, 0xdf, 0x5e, 0xda, 0xd2, 0xfa, 0xc2, 0x9b, 0xed, 0xfe, 0xfc, 0xe0, 0xe1, 0x81, 0x27, 0x62,
+	0x26, 0x8b, 0x82, 0xd8, 0x6a, 0x47, 0x82, 0x03, 0x49, 0xed, 0x3d, 0x3a, 0x32, 0x0e, 0xa5, 0x71,
+	0x11, 0x83, 0xd3, 0x56, 0x0e, 0x7a, 0x44, 0xa9, 0xbd, 0xf9, 0x9a, 0x07, 0xe1, 0x03, 0x47, 0xbe,
+	0x68, 0x76, 0x27, 0xe6, 0x6b, 0xd7, 0x43, 0xfd, 0x1e, 0x83, 0x71, 0xf4, 0xba, 0xbc, 0x2f, 0xa1,
+	0xf8, 0xd1, 0x76, 0xc2, 0x36, 0xbb, 0xca, 0x6f, 0xab, 0xb7, 0x75, 0xe9, 0x6e, 0xa0, 0x79, 0x71,
+	0x11, 0x09, 0xc3, 0xfd, 0x5d, 0xc2, 0x15, 0xc7, 0xee, 0xf1, 0x21, 0xe1, 0x4e, 0x36, 0x77, 0x0d,
+	0x95, 0x62, 0xb6, 0x09, 0x52, 0x6e, 0xa4, 0x87, 0xfa, 0x99, 0xa7, 0xc1, 0x62, 0x02, 0x65, 0xff,
+	0x79, 0xa0, 0x7e, 0x23, 0x8e, 0xab, 0x69, 0x0f, 0xcd, 0xb9, 0xca, 0xcb, 0xdd, 0xd5, 0x50, 0x7d,
+	0xb0, 0xf9, 0x5e, 0x98, 0x3a, 0xfb, 0x3c, 0x9d, 0x9b, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0xba,
+	0x37, 0x25, 0xea, 0x44, 0x01, 0x00, 0x00,
+}
diff --git a/vendor/google.golang.org/appengine/internal/base/api_base.proto b/vendor/google.golang.org/appengine/internal/base/api_base.proto
new file mode 100644
index 0000000..56cd7a3
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/base/api_base.proto
@@ -0,0 +1,33 @@
+// Built-in base types for API calls. Primarily useful as return types.
+
+syntax = "proto2";
+option go_package = "base";
+
+package appengine.base;
+
+message StringProto {
+  required string value = 1;
+}
+
+message Integer32Proto {
+  required int32 value = 1;
+}
+
+message Integer64Proto {
+  required int64 value = 1;
+}
+
+message BoolProto {
+  required bool value = 1;
+}
+
+message DoubleProto {
+  required double value = 1;
+}
+
+message BytesProto {
+  required bytes value = 1 [ctype=CORD];
+}
+
+message VoidProto {
+}
diff --git a/vendor/google.golang.org/appengine/internal/datastore/datastore_v3.pb.go b/vendor/google.golang.org/appengine/internal/datastore/datastore_v3.pb.go
new file mode 100644
index 0000000..393342c
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/datastore/datastore_v3.pb.go
@@ -0,0 +1,3244 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: google.golang.org/appengine/internal/datastore/datastore_v3.proto
+
+/*
+Package datastore is a generated protocol buffer package.
+
+It is generated from these files:
+	google.golang.org/appengine/internal/datastore/datastore_v3.proto
+
+It has these top-level messages:
+	Action
+	PropertyValue
+	Property
+	Path
+	Reference
+	User
+	EntityProto
+	CompositeProperty
+	Index
+	CompositeIndex
+	IndexPostfix
+	IndexPosition
+	Snapshot
+	InternalHeader
+	Transaction
+	Query
+	CompiledQuery
+	CompiledCursor
+	Cursor
+	Error
+	Cost
+	GetRequest
+	GetResponse
+	PutRequest
+	PutResponse
+	TouchRequest
+	TouchResponse
+	DeleteRequest
+	DeleteResponse
+	NextRequest
+	QueryResult
+	AllocateIdsRequest
+	AllocateIdsResponse
+	CompositeIndices
+	AddActionsRequest
+	AddActionsResponse
+	BeginTransactionRequest
+	CommitResponse
+*/
+package datastore
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+type Property_Meaning int32
+
+const (
+	Property_NO_MEANING       Property_Meaning = 0
+	Property_BLOB             Property_Meaning = 14
+	Property_TEXT             Property_Meaning = 15
+	Property_BYTESTRING       Property_Meaning = 16
+	Property_ATOM_CATEGORY    Property_Meaning = 1
+	Property_ATOM_LINK        Property_Meaning = 2
+	Property_ATOM_TITLE       Property_Meaning = 3
+	Property_ATOM_CONTENT     Property_Meaning = 4
+	Property_ATOM_SUMMARY     Property_Meaning = 5
+	Property_ATOM_AUTHOR      Property_Meaning = 6
+	Property_GD_WHEN          Property_Meaning = 7
+	Property_GD_EMAIL         Property_Meaning = 8
+	Property_GEORSS_POINT     Property_Meaning = 9
+	Property_GD_IM            Property_Meaning = 10
+	Property_GD_PHONENUMBER   Property_Meaning = 11
+	Property_GD_POSTALADDRESS Property_Meaning = 12
+	Property_GD_RATING        Property_Meaning = 13
+	Property_BLOBKEY          Property_Meaning = 17
+	Property_ENTITY_PROTO     Property_Meaning = 19
+	Property_INDEX_VALUE      Property_Meaning = 18
+)
+
+var Property_Meaning_name = map[int32]string{
+	0:  "NO_MEANING",
+	14: "BLOB",
+	15: "TEXT",
+	16: "BYTESTRING",
+	1:  "ATOM_CATEGORY",
+	2:  "ATOM_LINK",
+	3:  "ATOM_TITLE",
+	4:  "ATOM_CONTENT",
+	5:  "ATOM_SUMMARY",
+	6:  "ATOM_AUTHOR",
+	7:  "GD_WHEN",
+	8:  "GD_EMAIL",
+	9:  "GEORSS_POINT",
+	10: "GD_IM",
+	11: "GD_PHONENUMBER",
+	12: "GD_POSTALADDRESS",
+	13: "GD_RATING",
+	17: "BLOBKEY",
+	19: "ENTITY_PROTO",
+	18: "INDEX_VALUE",
+}
+var Property_Meaning_value = map[string]int32{
+	"NO_MEANING":       0,
+	"BLOB":             14,
+	"TEXT":             15,
+	"BYTESTRING":       16,
+	"ATOM_CATEGORY":    1,
+	"ATOM_LINK":        2,
+	"ATOM_TITLE":       3,
+	"ATOM_CONTENT":     4,
+	"ATOM_SUMMARY":     5,
+	"ATOM_AUTHOR":      6,
+	"GD_WHEN":          7,
+	"GD_EMAIL":         8,
+	"GEORSS_POINT":     9,
+	"GD_IM":            10,
+	"GD_PHONENUMBER":   11,
+	"GD_POSTALADDRESS": 12,
+	"GD_RATING":        13,
+	"BLOBKEY":          17,
+	"ENTITY_PROTO":     19,
+	"INDEX_VALUE":      18,
+}
+
+func (x Property_Meaning) Enum() *Property_Meaning {
+	p := new(Property_Meaning)
+	*p = x
+	return p
+}
+func (x Property_Meaning) String() string {
+	return proto.EnumName(Property_Meaning_name, int32(x))
+}
+func (x *Property_Meaning) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(Property_Meaning_value, data, "Property_Meaning")
+	if err != nil {
+		return err
+	}
+	*x = Property_Meaning(value)
+	return nil
+}
+func (Property_Meaning) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{2, 0} }
+
+type Property_FtsTokenizationOption int32
+
+const (
+	Property_HTML Property_FtsTokenizationOption = 1
+	Property_ATOM Property_FtsTokenizationOption = 2
+)
+
+var Property_FtsTokenizationOption_name = map[int32]string{
+	1: "HTML",
+	2: "ATOM",
+}
+var Property_FtsTokenizationOption_value = map[string]int32{
+	"HTML": 1,
+	"ATOM": 2,
+}
+
+func (x Property_FtsTokenizationOption) Enum() *Property_FtsTokenizationOption {
+	p := new(Property_FtsTokenizationOption)
+	*p = x
+	return p
+}
+func (x Property_FtsTokenizationOption) String() string {
+	return proto.EnumName(Property_FtsTokenizationOption_name, int32(x))
+}
+func (x *Property_FtsTokenizationOption) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(Property_FtsTokenizationOption_value, data, "Property_FtsTokenizationOption")
+	if err != nil {
+		return err
+	}
+	*x = Property_FtsTokenizationOption(value)
+	return nil
+}
+func (Property_FtsTokenizationOption) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor0, []int{2, 1}
+}
+
+type EntityProto_Kind int32
+
+const (
+	EntityProto_GD_CONTACT EntityProto_Kind = 1
+	EntityProto_GD_EVENT   EntityProto_Kind = 2
+	EntityProto_GD_MESSAGE EntityProto_Kind = 3
+)
+
+var EntityProto_Kind_name = map[int32]string{
+	1: "GD_CONTACT",
+	2: "GD_EVENT",
+	3: "GD_MESSAGE",
+}
+var EntityProto_Kind_value = map[string]int32{
+	"GD_CONTACT": 1,
+	"GD_EVENT":   2,
+	"GD_MESSAGE": 3,
+}
+
+func (x EntityProto_Kind) Enum() *EntityProto_Kind {
+	p := new(EntityProto_Kind)
+	*p = x
+	return p
+}
+func (x EntityProto_Kind) String() string {
+	return proto.EnumName(EntityProto_Kind_name, int32(x))
+}
+func (x *EntityProto_Kind) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(EntityProto_Kind_value, data, "EntityProto_Kind")
+	if err != nil {
+		return err
+	}
+	*x = EntityProto_Kind(value)
+	return nil
+}
+func (EntityProto_Kind) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{6, 0} }
+
+type Index_Property_Direction int32
+
+const (
+	Index_Property_ASCENDING  Index_Property_Direction = 1
+	Index_Property_DESCENDING Index_Property_Direction = 2
+)
+
+var Index_Property_Direction_name = map[int32]string{
+	1: "ASCENDING",
+	2: "DESCENDING",
+}
+var Index_Property_Direction_value = map[string]int32{
+	"ASCENDING":  1,
+	"DESCENDING": 2,
+}
+
+func (x Index_Property_Direction) Enum() *Index_Property_Direction {
+	p := new(Index_Property_Direction)
+	*p = x
+	return p
+}
+func (x Index_Property_Direction) String() string {
+	return proto.EnumName(Index_Property_Direction_name, int32(x))
+}
+func (x *Index_Property_Direction) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(Index_Property_Direction_value, data, "Index_Property_Direction")
+	if err != nil {
+		return err
+	}
+	*x = Index_Property_Direction(value)
+	return nil
+}
+func (Index_Property_Direction) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor0, []int{8, 0, 0}
+}
+
+type CompositeIndex_State int32
+
+const (
+	CompositeIndex_WRITE_ONLY CompositeIndex_State = 1
+	CompositeIndex_READ_WRITE CompositeIndex_State = 2
+	CompositeIndex_DELETED    CompositeIndex_State = 3
+	CompositeIndex_ERROR      CompositeIndex_State = 4
+)
+
+var CompositeIndex_State_name = map[int32]string{
+	1: "WRITE_ONLY",
+	2: "READ_WRITE",
+	3: "DELETED",
+	4: "ERROR",
+}
+var CompositeIndex_State_value = map[string]int32{
+	"WRITE_ONLY": 1,
+	"READ_WRITE": 2,
+	"DELETED":    3,
+	"ERROR":      4,
+}
+
+func (x CompositeIndex_State) Enum() *CompositeIndex_State {
+	p := new(CompositeIndex_State)
+	*p = x
+	return p
+}
+func (x CompositeIndex_State) String() string {
+	return proto.EnumName(CompositeIndex_State_name, int32(x))
+}
+func (x *CompositeIndex_State) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(CompositeIndex_State_value, data, "CompositeIndex_State")
+	if err != nil {
+		return err
+	}
+	*x = CompositeIndex_State(value)
+	return nil
+}
+func (CompositeIndex_State) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{9, 0} }
+
+type Snapshot_Status int32
+
+const (
+	Snapshot_INACTIVE Snapshot_Status = 0
+	Snapshot_ACTIVE   Snapshot_Status = 1
+)
+
+var Snapshot_Status_name = map[int32]string{
+	0: "INACTIVE",
+	1: "ACTIVE",
+}
+var Snapshot_Status_value = map[string]int32{
+	"INACTIVE": 0,
+	"ACTIVE":   1,
+}
+
+func (x Snapshot_Status) Enum() *Snapshot_Status {
+	p := new(Snapshot_Status)
+	*p = x
+	return p
+}
+func (x Snapshot_Status) String() string {
+	return proto.EnumName(Snapshot_Status_name, int32(x))
+}
+func (x *Snapshot_Status) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(Snapshot_Status_value, data, "Snapshot_Status")
+	if err != nil {
+		return err
+	}
+	*x = Snapshot_Status(value)
+	return nil
+}
+func (Snapshot_Status) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{12, 0} }
+
+type Query_Hint int32
+
+const (
+	Query_ORDER_FIRST    Query_Hint = 1
+	Query_ANCESTOR_FIRST Query_Hint = 2
+	Query_FILTER_FIRST   Query_Hint = 3
+)
+
+var Query_Hint_name = map[int32]string{
+	1: "ORDER_FIRST",
+	2: "ANCESTOR_FIRST",
+	3: "FILTER_FIRST",
+}
+var Query_Hint_value = map[string]int32{
+	"ORDER_FIRST":    1,
+	"ANCESTOR_FIRST": 2,
+	"FILTER_FIRST":   3,
+}
+
+func (x Query_Hint) Enum() *Query_Hint {
+	p := new(Query_Hint)
+	*p = x
+	return p
+}
+func (x Query_Hint) String() string {
+	return proto.EnumName(Query_Hint_name, int32(x))
+}
+func (x *Query_Hint) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(Query_Hint_value, data, "Query_Hint")
+	if err != nil {
+		return err
+	}
+	*x = Query_Hint(value)
+	return nil
+}
+func (Query_Hint) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{15, 0} }
+
+type Query_Filter_Operator int32
+
+const (
+	Query_Filter_LESS_THAN             Query_Filter_Operator = 1
+	Query_Filter_LESS_THAN_OR_EQUAL    Query_Filter_Operator = 2
+	Query_Filter_GREATER_THAN          Query_Filter_Operator = 3
+	Query_Filter_GREATER_THAN_OR_EQUAL Query_Filter_Operator = 4
+	Query_Filter_EQUAL                 Query_Filter_Operator = 5
+	Query_Filter_IN                    Query_Filter_Operator = 6
+	Query_Filter_EXISTS                Query_Filter_Operator = 7
+)
+
+var Query_Filter_Operator_name = map[int32]string{
+	1: "LESS_THAN",
+	2: "LESS_THAN_OR_EQUAL",
+	3: "GREATER_THAN",
+	4: "GREATER_THAN_OR_EQUAL",
+	5: "EQUAL",
+	6: "IN",
+	7: "EXISTS",
+}
+var Query_Filter_Operator_value = map[string]int32{
+	"LESS_THAN":             1,
+	"LESS_THAN_OR_EQUAL":    2,
+	"GREATER_THAN":          3,
+	"GREATER_THAN_OR_EQUAL": 4,
+	"EQUAL":                 5,
+	"IN":                    6,
+	"EXISTS":                7,
+}
+
+func (x Query_Filter_Operator) Enum() *Query_Filter_Operator {
+	p := new(Query_Filter_Operator)
+	*p = x
+	return p
+}
+func (x Query_Filter_Operator) String() string {
+	return proto.EnumName(Query_Filter_Operator_name, int32(x))
+}
+func (x *Query_Filter_Operator) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(Query_Filter_Operator_value, data, "Query_Filter_Operator")
+	if err != nil {
+		return err
+	}
+	*x = Query_Filter_Operator(value)
+	return nil
+}
+func (Query_Filter_Operator) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{15, 0, 0} }
+
+type Query_Order_Direction int32
+
+const (
+	Query_Order_ASCENDING  Query_Order_Direction = 1
+	Query_Order_DESCENDING Query_Order_Direction = 2
+)
+
+var Query_Order_Direction_name = map[int32]string{
+	1: "ASCENDING",
+	2: "DESCENDING",
+}
+var Query_Order_Direction_value = map[string]int32{
+	"ASCENDING":  1,
+	"DESCENDING": 2,
+}
+
+func (x Query_Order_Direction) Enum() *Query_Order_Direction {
+	p := new(Query_Order_Direction)
+	*p = x
+	return p
+}
+func (x Query_Order_Direction) String() string {
+	return proto.EnumName(Query_Order_Direction_name, int32(x))
+}
+func (x *Query_Order_Direction) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(Query_Order_Direction_value, data, "Query_Order_Direction")
+	if err != nil {
+		return err
+	}
+	*x = Query_Order_Direction(value)
+	return nil
+}
+func (Query_Order_Direction) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{15, 1, 0} }
+
+type Error_ErrorCode int32
+
+const (
+	Error_BAD_REQUEST                  Error_ErrorCode = 1
+	Error_CONCURRENT_TRANSACTION       Error_ErrorCode = 2
+	Error_INTERNAL_ERROR               Error_ErrorCode = 3
+	Error_NEED_INDEX                   Error_ErrorCode = 4
+	Error_TIMEOUT                      Error_ErrorCode = 5
+	Error_PERMISSION_DENIED            Error_ErrorCode = 6
+	Error_BIGTABLE_ERROR               Error_ErrorCode = 7
+	Error_COMMITTED_BUT_STILL_APPLYING Error_ErrorCode = 8
+	Error_CAPABILITY_DISABLED          Error_ErrorCode = 9
+	Error_TRY_ALTERNATE_BACKEND        Error_ErrorCode = 10
+	Error_SAFE_TIME_TOO_OLD            Error_ErrorCode = 11
+)
+
+var Error_ErrorCode_name = map[int32]string{
+	1:  "BAD_REQUEST",
+	2:  "CONCURRENT_TRANSACTION",
+	3:  "INTERNAL_ERROR",
+	4:  "NEED_INDEX",
+	5:  "TIMEOUT",
+	6:  "PERMISSION_DENIED",
+	7:  "BIGTABLE_ERROR",
+	8:  "COMMITTED_BUT_STILL_APPLYING",
+	9:  "CAPABILITY_DISABLED",
+	10: "TRY_ALTERNATE_BACKEND",
+	11: "SAFE_TIME_TOO_OLD",
+}
+var Error_ErrorCode_value = map[string]int32{
+	"BAD_REQUEST":                  1,
+	"CONCURRENT_TRANSACTION":       2,
+	"INTERNAL_ERROR":               3,
+	"NEED_INDEX":                   4,
+	"TIMEOUT":                      5,
+	"PERMISSION_DENIED":            6,
+	"BIGTABLE_ERROR":               7,
+	"COMMITTED_BUT_STILL_APPLYING": 8,
+	"CAPABILITY_DISABLED":          9,
+	"TRY_ALTERNATE_BACKEND":        10,
+	"SAFE_TIME_TOO_OLD":            11,
+}
+
+func (x Error_ErrorCode) Enum() *Error_ErrorCode {
+	p := new(Error_ErrorCode)
+	*p = x
+	return p
+}
+func (x Error_ErrorCode) String() string {
+	return proto.EnumName(Error_ErrorCode_name, int32(x))
+}
+func (x *Error_ErrorCode) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(Error_ErrorCode_value, data, "Error_ErrorCode")
+	if err != nil {
+		return err
+	}
+	*x = Error_ErrorCode(value)
+	return nil
+}
+func (Error_ErrorCode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{19, 0} }
+
+type PutRequest_AutoIdPolicy int32
+
+const (
+	PutRequest_CURRENT    PutRequest_AutoIdPolicy = 0
+	PutRequest_SEQUENTIAL PutRequest_AutoIdPolicy = 1
+)
+
+var PutRequest_AutoIdPolicy_name = map[int32]string{
+	0: "CURRENT",
+	1: "SEQUENTIAL",
+}
+var PutRequest_AutoIdPolicy_value = map[string]int32{
+	"CURRENT":    0,
+	"SEQUENTIAL": 1,
+}
+
+func (x PutRequest_AutoIdPolicy) Enum() *PutRequest_AutoIdPolicy {
+	p := new(PutRequest_AutoIdPolicy)
+	*p = x
+	return p
+}
+func (x PutRequest_AutoIdPolicy) String() string {
+	return proto.EnumName(PutRequest_AutoIdPolicy_name, int32(x))
+}
+func (x *PutRequest_AutoIdPolicy) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(PutRequest_AutoIdPolicy_value, data, "PutRequest_AutoIdPolicy")
+	if err != nil {
+		return err
+	}
+	*x = PutRequest_AutoIdPolicy(value)
+	return nil
+}
+func (PutRequest_AutoIdPolicy) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{23, 0} }
+
+type BeginTransactionRequest_TransactionMode int32
+
+const (
+	BeginTransactionRequest_UNKNOWN    BeginTransactionRequest_TransactionMode = 0
+	BeginTransactionRequest_READ_ONLY  BeginTransactionRequest_TransactionMode = 1
+	BeginTransactionRequest_READ_WRITE BeginTransactionRequest_TransactionMode = 2
+)
+
+var BeginTransactionRequest_TransactionMode_name = map[int32]string{
+	0: "UNKNOWN",
+	1: "READ_ONLY",
+	2: "READ_WRITE",
+}
+var BeginTransactionRequest_TransactionMode_value = map[string]int32{
+	"UNKNOWN":    0,
+	"READ_ONLY":  1,
+	"READ_WRITE": 2,
+}
+
+func (x BeginTransactionRequest_TransactionMode) Enum() *BeginTransactionRequest_TransactionMode {
+	p := new(BeginTransactionRequest_TransactionMode)
+	*p = x
+	return p
+}
+func (x BeginTransactionRequest_TransactionMode) String() string {
+	return proto.EnumName(BeginTransactionRequest_TransactionMode_name, int32(x))
+}
+func (x *BeginTransactionRequest_TransactionMode) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(BeginTransactionRequest_TransactionMode_value, data, "BeginTransactionRequest_TransactionMode")
+	if err != nil {
+		return err
+	}
+	*x = BeginTransactionRequest_TransactionMode(value)
+	return nil
+}
+func (BeginTransactionRequest_TransactionMode) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor0, []int{36, 0}
+}
+
+type Action struct {
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Action) Reset()                    { *m = Action{} }
+func (m *Action) String() string            { return proto.CompactTextString(m) }
+func (*Action) ProtoMessage()               {}
+func (*Action) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+type PropertyValue struct {
+	Int64Value       *int64                        `protobuf:"varint,1,opt,name=int64Value" json:"int64Value,omitempty"`
+	BooleanValue     *bool                         `protobuf:"varint,2,opt,name=booleanValue" json:"booleanValue,omitempty"`
+	StringValue      *string                       `protobuf:"bytes,3,opt,name=stringValue" json:"stringValue,omitempty"`
+	DoubleValue      *float64                      `protobuf:"fixed64,4,opt,name=doubleValue" json:"doubleValue,omitempty"`
+	Pointvalue       *PropertyValue_PointValue     `protobuf:"group,5,opt,name=PointValue,json=pointvalue" json:"pointvalue,omitempty"`
+	Uservalue        *PropertyValue_UserValue      `protobuf:"group,8,opt,name=UserValue,json=uservalue" json:"uservalue,omitempty"`
+	Referencevalue   *PropertyValue_ReferenceValue `protobuf:"group,12,opt,name=ReferenceValue,json=referencevalue" json:"referencevalue,omitempty"`
+	XXX_unrecognized []byte                        `json:"-"`
+}
+
+func (m *PropertyValue) Reset()                    { *m = PropertyValue{} }
+func (m *PropertyValue) String() string            { return proto.CompactTextString(m) }
+func (*PropertyValue) ProtoMessage()               {}
+func (*PropertyValue) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+
+func (m *PropertyValue) GetInt64Value() int64 {
+	if m != nil && m.Int64Value != nil {
+		return *m.Int64Value
+	}
+	return 0
+}
+
+func (m *PropertyValue) GetBooleanValue() bool {
+	if m != nil && m.BooleanValue != nil {
+		return *m.BooleanValue
+	}
+	return false
+}
+
+func (m *PropertyValue) GetStringValue() string {
+	if m != nil && m.StringValue != nil {
+		return *m.StringValue
+	}
+	return ""
+}
+
+func (m *PropertyValue) GetDoubleValue() float64 {
+	if m != nil && m.DoubleValue != nil {
+		return *m.DoubleValue
+	}
+	return 0
+}
+
+func (m *PropertyValue) GetPointvalue() *PropertyValue_PointValue {
+	if m != nil {
+		return m.Pointvalue
+	}
+	return nil
+}
+
+func (m *PropertyValue) GetUservalue() *PropertyValue_UserValue {
+	if m != nil {
+		return m.Uservalue
+	}
+	return nil
+}
+
+func (m *PropertyValue) GetReferencevalue() *PropertyValue_ReferenceValue {
+	if m != nil {
+		return m.Referencevalue
+	}
+	return nil
+}
+
+type PropertyValue_PointValue struct {
+	X                *float64 `protobuf:"fixed64,6,req,name=x" json:"x,omitempty"`
+	Y                *float64 `protobuf:"fixed64,7,req,name=y" json:"y,omitempty"`
+	XXX_unrecognized []byte   `json:"-"`
+}
+
+func (m *PropertyValue_PointValue) Reset()                    { *m = PropertyValue_PointValue{} }
+func (m *PropertyValue_PointValue) String() string            { return proto.CompactTextString(m) }
+func (*PropertyValue_PointValue) ProtoMessage()               {}
+func (*PropertyValue_PointValue) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 0} }
+
+func (m *PropertyValue_PointValue) GetX() float64 {
+	if m != nil && m.X != nil {
+		return *m.X
+	}
+	return 0
+}
+
+func (m *PropertyValue_PointValue) GetY() float64 {
+	if m != nil && m.Y != nil {
+		return *m.Y
+	}
+	return 0
+}
+
+type PropertyValue_UserValue struct {
+	Email             *string `protobuf:"bytes,9,req,name=email" json:"email,omitempty"`
+	AuthDomain        *string `protobuf:"bytes,10,req,name=auth_domain,json=authDomain" json:"auth_domain,omitempty"`
+	Nickname          *string `protobuf:"bytes,11,opt,name=nickname" json:"nickname,omitempty"`
+	FederatedIdentity *string `protobuf:"bytes,21,opt,name=federated_identity,json=federatedIdentity" json:"federated_identity,omitempty"`
+	FederatedProvider *string `protobuf:"bytes,22,opt,name=federated_provider,json=federatedProvider" json:"federated_provider,omitempty"`
+	XXX_unrecognized  []byte  `json:"-"`
+}
+
+func (m *PropertyValue_UserValue) Reset()                    { *m = PropertyValue_UserValue{} }
+func (m *PropertyValue_UserValue) String() string            { return proto.CompactTextString(m) }
+func (*PropertyValue_UserValue) ProtoMessage()               {}
+func (*PropertyValue_UserValue) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 1} }
+
+func (m *PropertyValue_UserValue) GetEmail() string {
+	if m != nil && m.Email != nil {
+		return *m.Email
+	}
+	return ""
+}
+
+func (m *PropertyValue_UserValue) GetAuthDomain() string {
+	if m != nil && m.AuthDomain != nil {
+		return *m.AuthDomain
+	}
+	return ""
+}
+
+func (m *PropertyValue_UserValue) GetNickname() string {
+	if m != nil && m.Nickname != nil {
+		return *m.Nickname
+	}
+	return ""
+}
+
+func (m *PropertyValue_UserValue) GetFederatedIdentity() string {
+	if m != nil && m.FederatedIdentity != nil {
+		return *m.FederatedIdentity
+	}
+	return ""
+}
+
+func (m *PropertyValue_UserValue) GetFederatedProvider() string {
+	if m != nil && m.FederatedProvider != nil {
+		return *m.FederatedProvider
+	}
+	return ""
+}
+
+type PropertyValue_ReferenceValue struct {
+	App              *string                                     `protobuf:"bytes,13,req,name=app" json:"app,omitempty"`
+	NameSpace        *string                                     `protobuf:"bytes,20,opt,name=name_space,json=nameSpace" json:"name_space,omitempty"`
+	Pathelement      []*PropertyValue_ReferenceValue_PathElement `protobuf:"group,14,rep,name=PathElement,json=pathelement" json:"pathelement,omitempty"`
+	XXX_unrecognized []byte                                      `json:"-"`
+}
+
+func (m *PropertyValue_ReferenceValue) Reset()                    { *m = PropertyValue_ReferenceValue{} }
+func (m *PropertyValue_ReferenceValue) String() string            { return proto.CompactTextString(m) }
+func (*PropertyValue_ReferenceValue) ProtoMessage()               {}
+func (*PropertyValue_ReferenceValue) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 2} }
+
+func (m *PropertyValue_ReferenceValue) GetApp() string {
+	if m != nil && m.App != nil {
+		return *m.App
+	}
+	return ""
+}
+
+func (m *PropertyValue_ReferenceValue) GetNameSpace() string {
+	if m != nil && m.NameSpace != nil {
+		return *m.NameSpace
+	}
+	return ""
+}
+
+func (m *PropertyValue_ReferenceValue) GetPathelement() []*PropertyValue_ReferenceValue_PathElement {
+	if m != nil {
+		return m.Pathelement
+	}
+	return nil
+}
+
+type PropertyValue_ReferenceValue_PathElement struct {
+	Type             *string `protobuf:"bytes,15,req,name=type" json:"type,omitempty"`
+	Id               *int64  `protobuf:"varint,16,opt,name=id" json:"id,omitempty"`
+	Name             *string `protobuf:"bytes,17,opt,name=name" json:"name,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *PropertyValue_ReferenceValue_PathElement) Reset() {
+	*m = PropertyValue_ReferenceValue_PathElement{}
+}
+func (m *PropertyValue_ReferenceValue_PathElement) String() string { return proto.CompactTextString(m) }
+func (*PropertyValue_ReferenceValue_PathElement) ProtoMessage()    {}
+func (*PropertyValue_ReferenceValue_PathElement) Descriptor() ([]byte, []int) {
+	return fileDescriptor0, []int{1, 2, 0}
+}
+
+func (m *PropertyValue_ReferenceValue_PathElement) GetType() string {
+	if m != nil && m.Type != nil {
+		return *m.Type
+	}
+	return ""
+}
+
+func (m *PropertyValue_ReferenceValue_PathElement) GetId() int64 {
+	if m != nil && m.Id != nil {
+		return *m.Id
+	}
+	return 0
+}
+
+func (m *PropertyValue_ReferenceValue_PathElement) GetName() string {
+	if m != nil && m.Name != nil {
+		return *m.Name
+	}
+	return ""
+}
+
+type Property struct {
+	Meaning               *Property_Meaning               `protobuf:"varint,1,opt,name=meaning,enum=appengine.Property_Meaning,def=0" json:"meaning,omitempty"`
+	MeaningUri            *string                         `protobuf:"bytes,2,opt,name=meaning_uri,json=meaningUri" json:"meaning_uri,omitempty"`
+	Name                  *string                         `protobuf:"bytes,3,req,name=name" json:"name,omitempty"`
+	Value                 *PropertyValue                  `protobuf:"bytes,5,req,name=value" json:"value,omitempty"`
+	Multiple              *bool                           `protobuf:"varint,4,req,name=multiple" json:"multiple,omitempty"`
+	Searchable            *bool                           `protobuf:"varint,6,opt,name=searchable,def=0" json:"searchable,omitempty"`
+	FtsTokenizationOption *Property_FtsTokenizationOption `protobuf:"varint,8,opt,name=fts_tokenization_option,json=ftsTokenizationOption,enum=appengine.Property_FtsTokenizationOption" json:"fts_tokenization_option,omitempty"`
+	Locale                *string                         `protobuf:"bytes,9,opt,name=locale,def=en" json:"locale,omitempty"`
+	XXX_unrecognized      []byte                          `json:"-"`
+}
+
+func (m *Property) Reset()                    { *m = Property{} }
+func (m *Property) String() string            { return proto.CompactTextString(m) }
+func (*Property) ProtoMessage()               {}
+func (*Property) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+
+const Default_Property_Meaning Property_Meaning = Property_NO_MEANING
+const Default_Property_Searchable bool = false
+const Default_Property_Locale string = "en"
+
+func (m *Property) GetMeaning() Property_Meaning {
+	if m != nil && m.Meaning != nil {
+		return *m.Meaning
+	}
+	return Default_Property_Meaning
+}
+
+func (m *Property) GetMeaningUri() string {
+	if m != nil && m.MeaningUri != nil {
+		return *m.MeaningUri
+	}
+	return ""
+}
+
+func (m *Property) GetName() string {
+	if m != nil && m.Name != nil {
+		return *m.Name
+	}
+	return ""
+}
+
+func (m *Property) GetValue() *PropertyValue {
+	if m != nil {
+		return m.Value
+	}
+	return nil
+}
+
+func (m *Property) GetMultiple() bool {
+	if m != nil && m.Multiple != nil {
+		return *m.Multiple
+	}
+	return false
+}
+
+func (m *Property) GetSearchable() bool {
+	if m != nil && m.Searchable != nil {
+		return *m.Searchable
+	}
+	return Default_Property_Searchable
+}
+
+func (m *Property) GetFtsTokenizationOption() Property_FtsTokenizationOption {
+	if m != nil && m.FtsTokenizationOption != nil {
+		return *m.FtsTokenizationOption
+	}
+	return Property_HTML
+}
+
+func (m *Property) GetLocale() string {
+	if m != nil && m.Locale != nil {
+		return *m.Locale
+	}
+	return Default_Property_Locale
+}
+
+type Path struct {
+	Element          []*Path_Element `protobuf:"group,1,rep,name=Element,json=element" json:"element,omitempty"`
+	XXX_unrecognized []byte          `json:"-"`
+}
+
+func (m *Path) Reset()                    { *m = Path{} }
+func (m *Path) String() string            { return proto.CompactTextString(m) }
+func (*Path) ProtoMessage()               {}
+func (*Path) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
+
+func (m *Path) GetElement() []*Path_Element {
+	if m != nil {
+		return m.Element
+	}
+	return nil
+}
+
+type Path_Element struct {
+	Type             *string `protobuf:"bytes,2,req,name=type" json:"type,omitempty"`
+	Id               *int64  `protobuf:"varint,3,opt,name=id" json:"id,omitempty"`
+	Name             *string `protobuf:"bytes,4,opt,name=name" json:"name,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *Path_Element) Reset()                    { *m = Path_Element{} }
+func (m *Path_Element) String() string            { return proto.CompactTextString(m) }
+func (*Path_Element) ProtoMessage()               {}
+func (*Path_Element) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3, 0} }
+
+func (m *Path_Element) GetType() string {
+	if m != nil && m.Type != nil {
+		return *m.Type
+	}
+	return ""
+}
+
+func (m *Path_Element) GetId() int64 {
+	if m != nil && m.Id != nil {
+		return *m.Id
+	}
+	return 0
+}
+
+func (m *Path_Element) GetName() string {
+	if m != nil && m.Name != nil {
+		return *m.Name
+	}
+	return ""
+}
+
+type Reference struct {
+	App              *string `protobuf:"bytes,13,req,name=app" json:"app,omitempty"`
+	NameSpace        *string `protobuf:"bytes,20,opt,name=name_space,json=nameSpace" json:"name_space,omitempty"`
+	Path             *Path   `protobuf:"bytes,14,req,name=path" json:"path,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *Reference) Reset()                    { *m = Reference{} }
+func (m *Reference) String() string            { return proto.CompactTextString(m) }
+func (*Reference) ProtoMessage()               {}
+func (*Reference) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
+
+func (m *Reference) GetApp() string {
+	if m != nil && m.App != nil {
+		return *m.App
+	}
+	return ""
+}
+
+func (m *Reference) GetNameSpace() string {
+	if m != nil && m.NameSpace != nil {
+		return *m.NameSpace
+	}
+	return ""
+}
+
+func (m *Reference) GetPath() *Path {
+	if m != nil {
+		return m.Path
+	}
+	return nil
+}
+
+type User struct {
+	Email             *string `protobuf:"bytes,1,req,name=email" json:"email,omitempty"`
+	AuthDomain        *string `protobuf:"bytes,2,req,name=auth_domain,json=authDomain" json:"auth_domain,omitempty"`
+	Nickname          *string `protobuf:"bytes,3,opt,name=nickname" json:"nickname,omitempty"`
+	FederatedIdentity *string `protobuf:"bytes,6,opt,name=federated_identity,json=federatedIdentity" json:"federated_identity,omitempty"`
+	FederatedProvider *string `protobuf:"bytes,7,opt,name=federated_provider,json=federatedProvider" json:"federated_provider,omitempty"`
+	XXX_unrecognized  []byte  `json:"-"`
+}
+
+func (m *User) Reset()                    { *m = User{} }
+func (m *User) String() string            { return proto.CompactTextString(m) }
+func (*User) ProtoMessage()               {}
+func (*User) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
+
+func (m *User) GetEmail() string {
+	if m != nil && m.Email != nil {
+		return *m.Email
+	}
+	return ""
+}
+
+func (m *User) GetAuthDomain() string {
+	if m != nil && m.AuthDomain != nil {
+		return *m.AuthDomain
+	}
+	return ""
+}
+
+func (m *User) GetNickname() string {
+	if m != nil && m.Nickname != nil {
+		return *m.Nickname
+	}
+	return ""
+}
+
+func (m *User) GetFederatedIdentity() string {
+	if m != nil && m.FederatedIdentity != nil {
+		return *m.FederatedIdentity
+	}
+	return ""
+}
+
+func (m *User) GetFederatedProvider() string {
+	if m != nil && m.FederatedProvider != nil {
+		return *m.FederatedProvider
+	}
+	return ""
+}
+
+type EntityProto struct {
+	Key              *Reference        `protobuf:"bytes,13,req,name=key" json:"key,omitempty"`
+	EntityGroup      *Path             `protobuf:"bytes,16,req,name=entity_group,json=entityGroup" json:"entity_group,omitempty"`
+	Owner            *User             `protobuf:"bytes,17,opt,name=owner" json:"owner,omitempty"`
+	Kind             *EntityProto_Kind `protobuf:"varint,4,opt,name=kind,enum=appengine.EntityProto_Kind" json:"kind,omitempty"`
+	KindUri          *string           `protobuf:"bytes,5,opt,name=kind_uri,json=kindUri" json:"kind_uri,omitempty"`
+	Property         []*Property       `protobuf:"bytes,14,rep,name=property" json:"property,omitempty"`
+	RawProperty      []*Property       `protobuf:"bytes,15,rep,name=raw_property,json=rawProperty" json:"raw_property,omitempty"`
+	Rank             *int32            `protobuf:"varint,18,opt,name=rank" json:"rank,omitempty"`
+	XXX_unrecognized []byte            `json:"-"`
+}
+
+func (m *EntityProto) Reset()                    { *m = EntityProto{} }
+func (m *EntityProto) String() string            { return proto.CompactTextString(m) }
+func (*EntityProto) ProtoMessage()               {}
+func (*EntityProto) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
+
+func (m *EntityProto) GetKey() *Reference {
+	if m != nil {
+		return m.Key
+	}
+	return nil
+}
+
+func (m *EntityProto) GetEntityGroup() *Path {
+	if m != nil {
+		return m.EntityGroup
+	}
+	return nil
+}
+
+func (m *EntityProto) GetOwner() *User {
+	if m != nil {
+		return m.Owner
+	}
+	return nil
+}
+
+func (m *EntityProto) GetKind() EntityProto_Kind {
+	if m != nil && m.Kind != nil {
+		return *m.Kind
+	}
+	return EntityProto_GD_CONTACT
+}
+
+func (m *EntityProto) GetKindUri() string {
+	if m != nil && m.KindUri != nil {
+		return *m.KindUri
+	}
+	return ""
+}
+
+func (m *EntityProto) GetProperty() []*Property {
+	if m != nil {
+		return m.Property
+	}
+	return nil
+}
+
+func (m *EntityProto) GetRawProperty() []*Property {
+	if m != nil {
+		return m.RawProperty
+	}
+	return nil
+}
+
+func (m *EntityProto) GetRank() int32 {
+	if m != nil && m.Rank != nil {
+		return *m.Rank
+	}
+	return 0
+}
+
+type CompositeProperty struct {
+	IndexId          *int64   `protobuf:"varint,1,req,name=index_id,json=indexId" json:"index_id,omitempty"`
+	Value            []string `protobuf:"bytes,2,rep,name=value" json:"value,omitempty"`
+	XXX_unrecognized []byte   `json:"-"`
+}
+
+func (m *CompositeProperty) Reset()                    { *m = CompositeProperty{} }
+func (m *CompositeProperty) String() string            { return proto.CompactTextString(m) }
+func (*CompositeProperty) ProtoMessage()               {}
+func (*CompositeProperty) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
+
+func (m *CompositeProperty) GetIndexId() int64 {
+	if m != nil && m.IndexId != nil {
+		return *m.IndexId
+	}
+	return 0
+}
+
+func (m *CompositeProperty) GetValue() []string {
+	if m != nil {
+		return m.Value
+	}
+	return nil
+}
+
+type Index struct {
+	EntityType       *string           `protobuf:"bytes,1,req,name=entity_type,json=entityType" json:"entity_type,omitempty"`
+	Ancestor         *bool             `protobuf:"varint,5,req,name=ancestor" json:"ancestor,omitempty"`
+	Property         []*Index_Property `protobuf:"group,2,rep,name=Property,json=property" json:"property,omitempty"`
+	XXX_unrecognized []byte            `json:"-"`
+}
+
+func (m *Index) Reset()                    { *m = Index{} }
+func (m *Index) String() string            { return proto.CompactTextString(m) }
+func (*Index) ProtoMessage()               {}
+func (*Index) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
+
+func (m *Index) GetEntityType() string {
+	if m != nil && m.EntityType != nil {
+		return *m.EntityType
+	}
+	return ""
+}
+
+func (m *Index) GetAncestor() bool {
+	if m != nil && m.Ancestor != nil {
+		return *m.Ancestor
+	}
+	return false
+}
+
+func (m *Index) GetProperty() []*Index_Property {
+	if m != nil {
+		return m.Property
+	}
+	return nil
+}
+
+type Index_Property struct {
+	Name             *string                   `protobuf:"bytes,3,req,name=name" json:"name,omitempty"`
+	Direction        *Index_Property_Direction `protobuf:"varint,4,opt,name=direction,enum=appengine.Index_Property_Direction,def=1" json:"direction,omitempty"`
+	XXX_unrecognized []byte                    `json:"-"`
+}
+
+func (m *Index_Property) Reset()                    { *m = Index_Property{} }
+func (m *Index_Property) String() string            { return proto.CompactTextString(m) }
+func (*Index_Property) ProtoMessage()               {}
+func (*Index_Property) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8, 0} }
+
+const Default_Index_Property_Direction Index_Property_Direction = Index_Property_ASCENDING
+
+func (m *Index_Property) GetName() string {
+	if m != nil && m.Name != nil {
+		return *m.Name
+	}
+	return ""
+}
+
+func (m *Index_Property) GetDirection() Index_Property_Direction {
+	if m != nil && m.Direction != nil {
+		return *m.Direction
+	}
+	return Default_Index_Property_Direction
+}
+
+type CompositeIndex struct {
+	AppId             *string               `protobuf:"bytes,1,req,name=app_id,json=appId" json:"app_id,omitempty"`
+	Id                *int64                `protobuf:"varint,2,req,name=id" json:"id,omitempty"`
+	Definition        *Index                `protobuf:"bytes,3,req,name=definition" json:"definition,omitempty"`
+	State             *CompositeIndex_State `protobuf:"varint,4,req,name=state,enum=appengine.CompositeIndex_State" json:"state,omitempty"`
+	OnlyUseIfRequired *bool                 `protobuf:"varint,6,opt,name=only_use_if_required,json=onlyUseIfRequired,def=0" json:"only_use_if_required,omitempty"`
+	XXX_unrecognized  []byte                `json:"-"`
+}
+
+func (m *CompositeIndex) Reset()                    { *m = CompositeIndex{} }
+func (m *CompositeIndex) String() string            { return proto.CompactTextString(m) }
+func (*CompositeIndex) ProtoMessage()               {}
+func (*CompositeIndex) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} }
+
+const Default_CompositeIndex_OnlyUseIfRequired bool = false
+
+func (m *CompositeIndex) GetAppId() string {
+	if m != nil && m.AppId != nil {
+		return *m.AppId
+	}
+	return ""
+}
+
+func (m *CompositeIndex) GetId() int64 {
+	if m != nil && m.Id != nil {
+		return *m.Id
+	}
+	return 0
+}
+
+func (m *CompositeIndex) GetDefinition() *Index {
+	if m != nil {
+		return m.Definition
+	}
+	return nil
+}
+
+func (m *CompositeIndex) GetState() CompositeIndex_State {
+	if m != nil && m.State != nil {
+		return *m.State
+	}
+	return CompositeIndex_WRITE_ONLY
+}
+
+func (m *CompositeIndex) GetOnlyUseIfRequired() bool {
+	if m != nil && m.OnlyUseIfRequired != nil {
+		return *m.OnlyUseIfRequired
+	}
+	return Default_CompositeIndex_OnlyUseIfRequired
+}
+
+type IndexPostfix struct {
+	IndexValue       []*IndexPostfix_IndexValue `protobuf:"bytes,1,rep,name=index_value,json=indexValue" json:"index_value,omitempty"`
+	Key              *Reference                 `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"`
+	Before           *bool                      `protobuf:"varint,3,opt,name=before,def=1" json:"before,omitempty"`
+	XXX_unrecognized []byte                     `json:"-"`
+}
+
+func (m *IndexPostfix) Reset()                    { *m = IndexPostfix{} }
+func (m *IndexPostfix) String() string            { return proto.CompactTextString(m) }
+func (*IndexPostfix) ProtoMessage()               {}
+func (*IndexPostfix) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} }
+
+const Default_IndexPostfix_Before bool = true
+
+func (m *IndexPostfix) GetIndexValue() []*IndexPostfix_IndexValue {
+	if m != nil {
+		return m.IndexValue
+	}
+	return nil
+}
+
+func (m *IndexPostfix) GetKey() *Reference {
+	if m != nil {
+		return m.Key
+	}
+	return nil
+}
+
+func (m *IndexPostfix) GetBefore() bool {
+	if m != nil && m.Before != nil {
+		return *m.Before
+	}
+	return Default_IndexPostfix_Before
+}
+
+type IndexPostfix_IndexValue struct {
+	PropertyName     *string        `protobuf:"bytes,1,req,name=property_name,json=propertyName" json:"property_name,omitempty"`
+	Value            *PropertyValue `protobuf:"bytes,2,req,name=value" json:"value,omitempty"`
+	XXX_unrecognized []byte         `json:"-"`
+}
+
+func (m *IndexPostfix_IndexValue) Reset()                    { *m = IndexPostfix_IndexValue{} }
+func (m *IndexPostfix_IndexValue) String() string            { return proto.CompactTextString(m) }
+func (*IndexPostfix_IndexValue) ProtoMessage()               {}
+func (*IndexPostfix_IndexValue) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10, 0} }
+
+func (m *IndexPostfix_IndexValue) GetPropertyName() string {
+	if m != nil && m.PropertyName != nil {
+		return *m.PropertyName
+	}
+	return ""
+}
+
+func (m *IndexPostfix_IndexValue) GetValue() *PropertyValue {
+	if m != nil {
+		return m.Value
+	}
+	return nil
+}
+
+type IndexPosition struct {
+	Key              *string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"`
+	Before           *bool   `protobuf:"varint,2,opt,name=before,def=1" json:"before,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *IndexPosition) Reset()                    { *m = IndexPosition{} }
+func (m *IndexPosition) String() string            { return proto.CompactTextString(m) }
+func (*IndexPosition) ProtoMessage()               {}
+func (*IndexPosition) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
+
+const Default_IndexPosition_Before bool = true
+
+func (m *IndexPosition) GetKey() string {
+	if m != nil && m.Key != nil {
+		return *m.Key
+	}
+	return ""
+}
+
+func (m *IndexPosition) GetBefore() bool {
+	if m != nil && m.Before != nil {
+		return *m.Before
+	}
+	return Default_IndexPosition_Before
+}
+
+type Snapshot struct {
+	Ts               *int64 `protobuf:"varint,1,req,name=ts" json:"ts,omitempty"`
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Snapshot) Reset()                    { *m = Snapshot{} }
+func (m *Snapshot) String() string            { return proto.CompactTextString(m) }
+func (*Snapshot) ProtoMessage()               {}
+func (*Snapshot) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} }
+
+func (m *Snapshot) GetTs() int64 {
+	if m != nil && m.Ts != nil {
+		return *m.Ts
+	}
+	return 0
+}
+
+type InternalHeader struct {
+	Qos              *string `protobuf:"bytes,1,opt,name=qos" json:"qos,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *InternalHeader) Reset()                    { *m = InternalHeader{} }
+func (m *InternalHeader) String() string            { return proto.CompactTextString(m) }
+func (*InternalHeader) ProtoMessage()               {}
+func (*InternalHeader) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} }
+
+func (m *InternalHeader) GetQos() string {
+	if m != nil && m.Qos != nil {
+		return *m.Qos
+	}
+	return ""
+}
+
+type Transaction struct {
+	Header           *InternalHeader `protobuf:"bytes,4,opt,name=header" json:"header,omitempty"`
+	Handle           *uint64         `protobuf:"fixed64,1,req,name=handle" json:"handle,omitempty"`
+	App              *string         `protobuf:"bytes,2,req,name=app" json:"app,omitempty"`
+	MarkChanges      *bool           `protobuf:"varint,3,opt,name=mark_changes,json=markChanges,def=0" json:"mark_changes,omitempty"`
+	XXX_unrecognized []byte          `json:"-"`
+}
+
+func (m *Transaction) Reset()                    { *m = Transaction{} }
+func (m *Transaction) String() string            { return proto.CompactTextString(m) }
+func (*Transaction) ProtoMessage()               {}
+func (*Transaction) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} }
+
+const Default_Transaction_MarkChanges bool = false
+
+func (m *Transaction) GetHeader() *InternalHeader {
+	if m != nil {
+		return m.Header
+	}
+	return nil
+}
+
+func (m *Transaction) GetHandle() uint64 {
+	if m != nil && m.Handle != nil {
+		return *m.Handle
+	}
+	return 0
+}
+
+func (m *Transaction) GetApp() string {
+	if m != nil && m.App != nil {
+		return *m.App
+	}
+	return ""
+}
+
+func (m *Transaction) GetMarkChanges() bool {
+	if m != nil && m.MarkChanges != nil {
+		return *m.MarkChanges
+	}
+	return Default_Transaction_MarkChanges
+}
+
+type Query struct {
+	Header              *InternalHeader   `protobuf:"bytes,39,opt,name=header" json:"header,omitempty"`
+	App                 *string           `protobuf:"bytes,1,req,name=app" json:"app,omitempty"`
+	NameSpace           *string           `protobuf:"bytes,29,opt,name=name_space,json=nameSpace" json:"name_space,omitempty"`
+	Kind                *string           `protobuf:"bytes,3,opt,name=kind" json:"kind,omitempty"`
+	Ancestor            *Reference        `protobuf:"bytes,17,opt,name=ancestor" json:"ancestor,omitempty"`
+	Filter              []*Query_Filter   `protobuf:"group,4,rep,name=Filter,json=filter" json:"filter,omitempty"`
+	SearchQuery         *string           `protobuf:"bytes,8,opt,name=search_query,json=searchQuery" json:"search_query,omitempty"`
+	Order               []*Query_Order    `protobuf:"group,9,rep,name=Order,json=order" json:"order,omitempty"`
+	Hint                *Query_Hint       `protobuf:"varint,18,opt,name=hint,enum=appengine.Query_Hint" json:"hint,omitempty"`
+	Count               *int32            `protobuf:"varint,23,opt,name=count" json:"count,omitempty"`
+	Offset              *int32            `protobuf:"varint,12,opt,name=offset,def=0" json:"offset,omitempty"`
+	Limit               *int32            `protobuf:"varint,16,opt,name=limit" json:"limit,omitempty"`
+	CompiledCursor      *CompiledCursor   `protobuf:"bytes,30,opt,name=compiled_cursor,json=compiledCursor" json:"compiled_cursor,omitempty"`
+	EndCompiledCursor   *CompiledCursor   `protobuf:"bytes,31,opt,name=end_compiled_cursor,json=endCompiledCursor" json:"end_compiled_cursor,omitempty"`
+	CompositeIndex      []*CompositeIndex `protobuf:"bytes,19,rep,name=composite_index,json=compositeIndex" json:"composite_index,omitempty"`
+	RequirePerfectPlan  *bool             `protobuf:"varint,20,opt,name=require_perfect_plan,json=requirePerfectPlan,def=0" json:"require_perfect_plan,omitempty"`
+	KeysOnly            *bool             `protobuf:"varint,21,opt,name=keys_only,json=keysOnly,def=0" json:"keys_only,omitempty"`
+	Transaction         *Transaction      `protobuf:"bytes,22,opt,name=transaction" json:"transaction,omitempty"`
+	Compile             *bool             `protobuf:"varint,25,opt,name=compile,def=0" json:"compile,omitempty"`
+	FailoverMs          *int64            `protobuf:"varint,26,opt,name=failover_ms,json=failoverMs" json:"failover_ms,omitempty"`
+	Strong              *bool             `protobuf:"varint,32,opt,name=strong" json:"strong,omitempty"`
+	PropertyName        []string          `protobuf:"bytes,33,rep,name=property_name,json=propertyName" json:"property_name,omitempty"`
+	GroupByPropertyName []string          `protobuf:"bytes,34,rep,name=group_by_property_name,json=groupByPropertyName" json:"group_by_property_name,omitempty"`
+	Distinct            *bool             `protobuf:"varint,24,opt,name=distinct" json:"distinct,omitempty"`
+	MinSafeTimeSeconds  *int64            `protobuf:"varint,35,opt,name=min_safe_time_seconds,json=minSafeTimeSeconds" json:"min_safe_time_seconds,omitempty"`
+	SafeReplicaName     []string          `protobuf:"bytes,36,rep,name=safe_replica_name,json=safeReplicaName" json:"safe_replica_name,omitempty"`
+	PersistOffset       *bool             `protobuf:"varint,37,opt,name=persist_offset,json=persistOffset,def=0" json:"persist_offset,omitempty"`
+	XXX_unrecognized    []byte            `json:"-"`
+}
+
+func (m *Query) Reset()                    { *m = Query{} }
+func (m *Query) String() string            { return proto.CompactTextString(m) }
+func (*Query) ProtoMessage()               {}
+func (*Query) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} }
+
+const Default_Query_Offset int32 = 0
+const Default_Query_RequirePerfectPlan bool = false
+const Default_Query_KeysOnly bool = false
+const Default_Query_Compile bool = false
+const Default_Query_PersistOffset bool = false
+
+func (m *Query) GetHeader() *InternalHeader {
+	if m != nil {
+		return m.Header
+	}
+	return nil
+}
+
+func (m *Query) GetApp() string {
+	if m != nil && m.App != nil {
+		return *m.App
+	}
+	return ""
+}
+
+func (m *Query) GetNameSpace() string {
+	if m != nil && m.NameSpace != nil {
+		return *m.NameSpace
+	}
+	return ""
+}
+
+func (m *Query) GetKind() string {
+	if m != nil && m.Kind != nil {
+		return *m.Kind
+	}
+	return ""
+}
+
+func (m *Query) GetAncestor() *Reference {
+	if m != nil {
+		return m.Ancestor
+	}
+	return nil
+}
+
+func (m *Query) GetFilter() []*Query_Filter {
+	if m != nil {
+		return m.Filter
+	}
+	return nil
+}
+
+func (m *Query) GetSearchQuery() string {
+	if m != nil && m.SearchQuery != nil {
+		return *m.SearchQuery
+	}
+	return ""
+}
+
+func (m *Query) GetOrder() []*Query_Order {
+	if m != nil {
+		return m.Order
+	}
+	return nil
+}
+
+func (m *Query) GetHint() Query_Hint {
+	if m != nil && m.Hint != nil {
+		return *m.Hint
+	}
+	return Query_ORDER_FIRST
+}
+
+func (m *Query) GetCount() int32 {
+	if m != nil && m.Count != nil {
+		return *m.Count
+	}
+	return 0
+}
+
+func (m *Query) GetOffset() int32 {
+	if m != nil && m.Offset != nil {
+		return *m.Offset
+	}
+	return Default_Query_Offset
+}
+
+func (m *Query) GetLimit() int32 {
+	if m != nil && m.Limit != nil {
+		return *m.Limit
+	}
+	return 0
+}
+
+func (m *Query) GetCompiledCursor() *CompiledCursor {
+	if m != nil {
+		return m.CompiledCursor
+	}
+	return nil
+}
+
+func (m *Query) GetEndCompiledCursor() *CompiledCursor {
+	if m != nil {
+		return m.EndCompiledCursor
+	}
+	return nil
+}
+
+func (m *Query) GetCompositeIndex() []*CompositeIndex {
+	if m != nil {
+		return m.CompositeIndex
+	}
+	return nil
+}
+
+func (m *Query) GetRequirePerfectPlan() bool {
+	if m != nil && m.RequirePerfectPlan != nil {
+		return *m.RequirePerfectPlan
+	}
+	return Default_Query_RequirePerfectPlan
+}
+
+func (m *Query) GetKeysOnly() bool {
+	if m != nil && m.KeysOnly != nil {
+		return *m.KeysOnly
+	}
+	return Default_Query_KeysOnly
+}
+
+func (m *Query) GetTransaction() *Transaction {
+	if m != nil {
+		return m.Transaction
+	}
+	return nil
+}
+
+func (m *Query) GetCompile() bool {
+	if m != nil && m.Compile != nil {
+		return *m.Compile
+	}
+	return Default_Query_Compile
+}
+
+func (m *Query) GetFailoverMs() int64 {
+	if m != nil && m.FailoverMs != nil {
+		return *m.FailoverMs
+	}
+	return 0
+}
+
+func (m *Query) GetStrong() bool {
+	if m != nil && m.Strong != nil {
+		return *m.Strong
+	}
+	return false
+}
+
+func (m *Query) GetPropertyName() []string {
+	if m != nil {
+		return m.PropertyName
+	}
+	return nil
+}
+
+func (m *Query) GetGroupByPropertyName() []string {
+	if m != nil {
+		return m.GroupByPropertyName
+	}
+	return nil
+}
+
+func (m *Query) GetDistinct() bool {
+	if m != nil && m.Distinct != nil {
+		return *m.Distinct
+	}
+	return false
+}
+
+func (m *Query) GetMinSafeTimeSeconds() int64 {
+	if m != nil && m.MinSafeTimeSeconds != nil {
+		return *m.MinSafeTimeSeconds
+	}
+	return 0
+}
+
+func (m *Query) GetSafeReplicaName() []string {
+	if m != nil {
+		return m.SafeReplicaName
+	}
+	return nil
+}
+
+func (m *Query) GetPersistOffset() bool {
+	if m != nil && m.PersistOffset != nil {
+		return *m.PersistOffset
+	}
+	return Default_Query_PersistOffset
+}
+
+type Query_Filter struct {
+	Op               *Query_Filter_Operator `protobuf:"varint,6,req,name=op,enum=appengine.Query_Filter_Operator" json:"op,omitempty"`
+	Property         []*Property            `protobuf:"bytes,14,rep,name=property" json:"property,omitempty"`
+	XXX_unrecognized []byte                 `json:"-"`
+}
+
+func (m *Query_Filter) Reset()                    { *m = Query_Filter{} }
+func (m *Query_Filter) String() string            { return proto.CompactTextString(m) }
+func (*Query_Filter) ProtoMessage()               {}
+func (*Query_Filter) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15, 0} }
+
+func (m *Query_Filter) GetOp() Query_Filter_Operator {
+	if m != nil && m.Op != nil {
+		return *m.Op
+	}
+	return Query_Filter_LESS_THAN
+}
+
+func (m *Query_Filter) GetProperty() []*Property {
+	if m != nil {
+		return m.Property
+	}
+	return nil
+}
+
+type Query_Order struct {
+	Property         *string                `protobuf:"bytes,10,req,name=property" json:"property,omitempty"`
+	Direction        *Query_Order_Direction `protobuf:"varint,11,opt,name=direction,enum=appengine.Query_Order_Direction,def=1" json:"direction,omitempty"`
+	XXX_unrecognized []byte                 `json:"-"`
+}
+
+func (m *Query_Order) Reset()                    { *m = Query_Order{} }
+func (m *Query_Order) String() string            { return proto.CompactTextString(m) }
+func (*Query_Order) ProtoMessage()               {}
+func (*Query_Order) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15, 1} }
+
+const Default_Query_Order_Direction Query_Order_Direction = Query_Order_ASCENDING
+
+func (m *Query_Order) GetProperty() string {
+	if m != nil && m.Property != nil {
+		return *m.Property
+	}
+	return ""
+}
+
+func (m *Query_Order) GetDirection() Query_Order_Direction {
+	if m != nil && m.Direction != nil {
+		return *m.Direction
+	}
+	return Default_Query_Order_Direction
+}
+
+type CompiledQuery struct {
+	Primaryscan       *CompiledQuery_PrimaryScan     `protobuf:"group,1,req,name=PrimaryScan,json=primaryscan" json:"primaryscan,omitempty"`
+	Mergejoinscan     []*CompiledQuery_MergeJoinScan `protobuf:"group,7,rep,name=MergeJoinScan,json=mergejoinscan" json:"mergejoinscan,omitempty"`
+	IndexDef          *Index                         `protobuf:"bytes,21,opt,name=index_def,json=indexDef" json:"index_def,omitempty"`
+	Offset            *int32                         `protobuf:"varint,10,opt,name=offset,def=0" json:"offset,omitempty"`
+	Limit             *int32                         `protobuf:"varint,11,opt,name=limit" json:"limit,omitempty"`
+	KeysOnly          *bool                          `protobuf:"varint,12,req,name=keys_only,json=keysOnly" json:"keys_only,omitempty"`
+	PropertyName      []string                       `protobuf:"bytes,24,rep,name=property_name,json=propertyName" json:"property_name,omitempty"`
+	DistinctInfixSize *int32                         `protobuf:"varint,25,opt,name=distinct_infix_size,json=distinctInfixSize" json:"distinct_infix_size,omitempty"`
+	Entityfilter      *CompiledQuery_EntityFilter    `protobuf:"group,13,opt,name=EntityFilter,json=entityfilter" json:"entityfilter,omitempty"`
+	XXX_unrecognized  []byte                         `json:"-"`
+}
+
+func (m *CompiledQuery) Reset()                    { *m = CompiledQuery{} }
+func (m *CompiledQuery) String() string            { return proto.CompactTextString(m) }
+func (*CompiledQuery) ProtoMessage()               {}
+func (*CompiledQuery) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} }
+
+const Default_CompiledQuery_Offset int32 = 0
+
+func (m *CompiledQuery) GetPrimaryscan() *CompiledQuery_PrimaryScan {
+	if m != nil {
+		return m.Primaryscan
+	}
+	return nil
+}
+
+func (m *CompiledQuery) GetMergejoinscan() []*CompiledQuery_MergeJoinScan {
+	if m != nil {
+		return m.Mergejoinscan
+	}
+	return nil
+}
+
+func (m *CompiledQuery) GetIndexDef() *Index {
+	if m != nil {
+		return m.IndexDef
+	}
+	return nil
+}
+
+func (m *CompiledQuery) GetOffset() int32 {
+	if m != nil && m.Offset != nil {
+		return *m.Offset
+	}
+	return Default_CompiledQuery_Offset
+}
+
+func (m *CompiledQuery) GetLimit() int32 {
+	if m != nil && m.Limit != nil {
+		return *m.Limit
+	}
+	return 0
+}
+
+func (m *CompiledQuery) GetKeysOnly() bool {
+	if m != nil && m.KeysOnly != nil {
+		return *m.KeysOnly
+	}
+	return false
+}
+
+func (m *CompiledQuery) GetPropertyName() []string {
+	if m != nil {
+		return m.PropertyName
+	}
+	return nil
+}
+
+func (m *CompiledQuery) GetDistinctInfixSize() int32 {
+	if m != nil && m.DistinctInfixSize != nil {
+		return *m.DistinctInfixSize
+	}
+	return 0
+}
+
+func (m *CompiledQuery) GetEntityfilter() *CompiledQuery_EntityFilter {
+	if m != nil {
+		return m.Entityfilter
+	}
+	return nil
+}
+
+type CompiledQuery_PrimaryScan struct {
+	IndexName                  *string  `protobuf:"bytes,2,opt,name=index_name,json=indexName" json:"index_name,omitempty"`
+	StartKey                   *string  `protobuf:"bytes,3,opt,name=start_key,json=startKey" json:"start_key,omitempty"`
+	StartInclusive             *bool    `protobuf:"varint,4,opt,name=start_inclusive,json=startInclusive" json:"start_inclusive,omitempty"`
+	EndKey                     *string  `protobuf:"bytes,5,opt,name=end_key,json=endKey" json:"end_key,omitempty"`
+	EndInclusive               *bool    `protobuf:"varint,6,opt,name=end_inclusive,json=endInclusive" json:"end_inclusive,omitempty"`
+	StartPostfixValue          []string `protobuf:"bytes,22,rep,name=start_postfix_value,json=startPostfixValue" json:"start_postfix_value,omitempty"`
+	EndPostfixValue            []string `protobuf:"bytes,23,rep,name=end_postfix_value,json=endPostfixValue" json:"end_postfix_value,omitempty"`
+	EndUnappliedLogTimestampUs *int64   `protobuf:"varint,19,opt,name=end_unapplied_log_timestamp_us,json=endUnappliedLogTimestampUs" json:"end_unapplied_log_timestamp_us,omitempty"`
+	XXX_unrecognized           []byte   `json:"-"`
+}
+
+func (m *CompiledQuery_PrimaryScan) Reset()                    { *m = CompiledQuery_PrimaryScan{} }
+func (m *CompiledQuery_PrimaryScan) String() string            { return proto.CompactTextString(m) }
+func (*CompiledQuery_PrimaryScan) ProtoMessage()               {}
+func (*CompiledQuery_PrimaryScan) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16, 0} }
+
+func (m *CompiledQuery_PrimaryScan) GetIndexName() string {
+	if m != nil && m.IndexName != nil {
+		return *m.IndexName
+	}
+	return ""
+}
+
+func (m *CompiledQuery_PrimaryScan) GetStartKey() string {
+	if m != nil && m.StartKey != nil {
+		return *m.StartKey
+	}
+	return ""
+}
+
+func (m *CompiledQuery_PrimaryScan) GetStartInclusive() bool {
+	if m != nil && m.StartInclusive != nil {
+		return *m.StartInclusive
+	}
+	return false
+}
+
+func (m *CompiledQuery_PrimaryScan) GetEndKey() string {
+	if m != nil && m.EndKey != nil {
+		return *m.EndKey
+	}
+	return ""
+}
+
+func (m *CompiledQuery_PrimaryScan) GetEndInclusive() bool {
+	if m != nil && m.EndInclusive != nil {
+		return *m.EndInclusive
+	}
+	return false
+}
+
+func (m *CompiledQuery_PrimaryScan) GetStartPostfixValue() []string {
+	if m != nil {
+		return m.StartPostfixValue
+	}
+	return nil
+}
+
+func (m *CompiledQuery_PrimaryScan) GetEndPostfixValue() []string {
+	if m != nil {
+		return m.EndPostfixValue
+	}
+	return nil
+}
+
+func (m *CompiledQuery_PrimaryScan) GetEndUnappliedLogTimestampUs() int64 {
+	if m != nil && m.EndUnappliedLogTimestampUs != nil {
+		return *m.EndUnappliedLogTimestampUs
+	}
+	return 0
+}
+
+type CompiledQuery_MergeJoinScan struct {
+	IndexName        *string  `protobuf:"bytes,8,req,name=index_name,json=indexName" json:"index_name,omitempty"`
+	PrefixValue      []string `protobuf:"bytes,9,rep,name=prefix_value,json=prefixValue" json:"prefix_value,omitempty"`
+	ValuePrefix      *bool    `protobuf:"varint,20,opt,name=value_prefix,json=valuePrefix,def=0" json:"value_prefix,omitempty"`
+	XXX_unrecognized []byte   `json:"-"`
+}
+
+func (m *CompiledQuery_MergeJoinScan) Reset()                    { *m = CompiledQuery_MergeJoinScan{} }
+func (m *CompiledQuery_MergeJoinScan) String() string            { return proto.CompactTextString(m) }
+func (*CompiledQuery_MergeJoinScan) ProtoMessage()               {}
+func (*CompiledQuery_MergeJoinScan) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16, 1} }
+
+const Default_CompiledQuery_MergeJoinScan_ValuePrefix bool = false
+
+func (m *CompiledQuery_MergeJoinScan) GetIndexName() string {
+	if m != nil && m.IndexName != nil {
+		return *m.IndexName
+	}
+	return ""
+}
+
+func (m *CompiledQuery_MergeJoinScan) GetPrefixValue() []string {
+	if m != nil {
+		return m.PrefixValue
+	}
+	return nil
+}
+
+func (m *CompiledQuery_MergeJoinScan) GetValuePrefix() bool {
+	if m != nil && m.ValuePrefix != nil {
+		return *m.ValuePrefix
+	}
+	return Default_CompiledQuery_MergeJoinScan_ValuePrefix
+}
+
+type CompiledQuery_EntityFilter struct {
+	Distinct         *bool      `protobuf:"varint,14,opt,name=distinct,def=0" json:"distinct,omitempty"`
+	Kind             *string    `protobuf:"bytes,17,opt,name=kind" json:"kind,omitempty"`
+	Ancestor         *Reference `protobuf:"bytes,18,opt,name=ancestor" json:"ancestor,omitempty"`
+	XXX_unrecognized []byte     `json:"-"`
+}
+
+func (m *CompiledQuery_EntityFilter) Reset()                    { *m = CompiledQuery_EntityFilter{} }
+func (m *CompiledQuery_EntityFilter) String() string            { return proto.CompactTextString(m) }
+func (*CompiledQuery_EntityFilter) ProtoMessage()               {}
+func (*CompiledQuery_EntityFilter) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16, 2} }
+
+const Default_CompiledQuery_EntityFilter_Distinct bool = false
+
+func (m *CompiledQuery_EntityFilter) GetDistinct() bool {
+	if m != nil && m.Distinct != nil {
+		return *m.Distinct
+	}
+	return Default_CompiledQuery_EntityFilter_Distinct
+}
+
+func (m *CompiledQuery_EntityFilter) GetKind() string {
+	if m != nil && m.Kind != nil {
+		return *m.Kind
+	}
+	return ""
+}
+
+func (m *CompiledQuery_EntityFilter) GetAncestor() *Reference {
+	if m != nil {
+		return m.Ancestor
+	}
+	return nil
+}
+
+type CompiledCursor struct {
+	Position         *CompiledCursor_Position `protobuf:"group,2,opt,name=Position,json=position" json:"position,omitempty"`
+	XXX_unrecognized []byte                   `json:"-"`
+}
+
+func (m *CompiledCursor) Reset()                    { *m = CompiledCursor{} }
+func (m *CompiledCursor) String() string            { return proto.CompactTextString(m) }
+func (*CompiledCursor) ProtoMessage()               {}
+func (*CompiledCursor) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} }
+
+func (m *CompiledCursor) GetPosition() *CompiledCursor_Position {
+	if m != nil {
+		return m.Position
+	}
+	return nil
+}
+
+type CompiledCursor_Position struct {
+	StartKey         *string                               `protobuf:"bytes,27,opt,name=start_key,json=startKey" json:"start_key,omitempty"`
+	Indexvalue       []*CompiledCursor_Position_IndexValue `protobuf:"group,29,rep,name=IndexValue,json=indexvalue" json:"indexvalue,omitempty"`
+	Key              *Reference                            `protobuf:"bytes,32,opt,name=key" json:"key,omitempty"`
+	StartInclusive   *bool                                 `protobuf:"varint,28,opt,name=start_inclusive,json=startInclusive,def=1" json:"start_inclusive,omitempty"`
+	XXX_unrecognized []byte                                `json:"-"`
+}
+
+func (m *CompiledCursor_Position) Reset()                    { *m = CompiledCursor_Position{} }
+func (m *CompiledCursor_Position) String() string            { return proto.CompactTextString(m) }
+func (*CompiledCursor_Position) ProtoMessage()               {}
+func (*CompiledCursor_Position) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17, 0} }
+
+const Default_CompiledCursor_Position_StartInclusive bool = true
+
+func (m *CompiledCursor_Position) GetStartKey() string {
+	if m != nil && m.StartKey != nil {
+		return *m.StartKey
+	}
+	return ""
+}
+
+func (m *CompiledCursor_Position) GetIndexvalue() []*CompiledCursor_Position_IndexValue {
+	if m != nil {
+		return m.Indexvalue
+	}
+	return nil
+}
+
+func (m *CompiledCursor_Position) GetKey() *Reference {
+	if m != nil {
+		return m.Key
+	}
+	return nil
+}
+
+func (m *CompiledCursor_Position) GetStartInclusive() bool {
+	if m != nil && m.StartInclusive != nil {
+		return *m.StartInclusive
+	}
+	return Default_CompiledCursor_Position_StartInclusive
+}
+
+type CompiledCursor_Position_IndexValue struct {
+	Property         *string        `protobuf:"bytes,30,opt,name=property" json:"property,omitempty"`
+	Value            *PropertyValue `protobuf:"bytes,31,req,name=value" json:"value,omitempty"`
+	XXX_unrecognized []byte         `json:"-"`
+}
+
+func (m *CompiledCursor_Position_IndexValue) Reset()         { *m = CompiledCursor_Position_IndexValue{} }
+func (m *CompiledCursor_Position_IndexValue) String() string { return proto.CompactTextString(m) }
+func (*CompiledCursor_Position_IndexValue) ProtoMessage()    {}
+func (*CompiledCursor_Position_IndexValue) Descriptor() ([]byte, []int) {
+	return fileDescriptor0, []int{17, 0, 0}
+}
+
+func (m *CompiledCursor_Position_IndexValue) GetProperty() string {
+	if m != nil && m.Property != nil {
+		return *m.Property
+	}
+	return ""
+}
+
+func (m *CompiledCursor_Position_IndexValue) GetValue() *PropertyValue {
+	if m != nil {
+		return m.Value
+	}
+	return nil
+}
+
+type Cursor struct {
+	Cursor           *uint64 `protobuf:"fixed64,1,req,name=cursor" json:"cursor,omitempty"`
+	App              *string `protobuf:"bytes,2,opt,name=app" json:"app,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *Cursor) Reset()                    { *m = Cursor{} }
+func (m *Cursor) String() string            { return proto.CompactTextString(m) }
+func (*Cursor) ProtoMessage()               {}
+func (*Cursor) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} }
+
+func (m *Cursor) GetCursor() uint64 {
+	if m != nil && m.Cursor != nil {
+		return *m.Cursor
+	}
+	return 0
+}
+
+func (m *Cursor) GetApp() string {
+	if m != nil && m.App != nil {
+		return *m.App
+	}
+	return ""
+}
+
+type Error struct {
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Error) Reset()                    { *m = Error{} }
+func (m *Error) String() string            { return proto.CompactTextString(m) }
+func (*Error) ProtoMessage()               {}
+func (*Error) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} }
+
+type Cost struct {
+	IndexWrites             *int32           `protobuf:"varint,1,opt,name=index_writes,json=indexWrites" json:"index_writes,omitempty"`
+	IndexWriteBytes         *int32           `protobuf:"varint,2,opt,name=index_write_bytes,json=indexWriteBytes" json:"index_write_bytes,omitempty"`
+	EntityWrites            *int32           `protobuf:"varint,3,opt,name=entity_writes,json=entityWrites" json:"entity_writes,omitempty"`
+	EntityWriteBytes        *int32           `protobuf:"varint,4,opt,name=entity_write_bytes,json=entityWriteBytes" json:"entity_write_bytes,omitempty"`
+	Commitcost              *Cost_CommitCost `protobuf:"group,5,opt,name=CommitCost,json=commitcost" json:"commitcost,omitempty"`
+	ApproximateStorageDelta *int32           `protobuf:"varint,8,opt,name=approximate_storage_delta,json=approximateStorageDelta" json:"approximate_storage_delta,omitempty"`
+	IdSequenceUpdates       *int32           `protobuf:"varint,9,opt,name=id_sequence_updates,json=idSequenceUpdates" json:"id_sequence_updates,omitempty"`
+	XXX_unrecognized        []byte           `json:"-"`
+}
+
+func (m *Cost) Reset()                    { *m = Cost{} }
+func (m *Cost) String() string            { return proto.CompactTextString(m) }
+func (*Cost) ProtoMessage()               {}
+func (*Cost) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} }
+
+func (m *Cost) GetIndexWrites() int32 {
+	if m != nil && m.IndexWrites != nil {
+		return *m.IndexWrites
+	}
+	return 0
+}
+
+func (m *Cost) GetIndexWriteBytes() int32 {
+	if m != nil && m.IndexWriteBytes != nil {
+		return *m.IndexWriteBytes
+	}
+	return 0
+}
+
+func (m *Cost) GetEntityWrites() int32 {
+	if m != nil && m.EntityWrites != nil {
+		return *m.EntityWrites
+	}
+	return 0
+}
+
+func (m *Cost) GetEntityWriteBytes() int32 {
+	if m != nil && m.EntityWriteBytes != nil {
+		return *m.EntityWriteBytes
+	}
+	return 0
+}
+
+func (m *Cost) GetCommitcost() *Cost_CommitCost {
+	if m != nil {
+		return m.Commitcost
+	}
+	return nil
+}
+
+func (m *Cost) GetApproximateStorageDelta() int32 {
+	if m != nil && m.ApproximateStorageDelta != nil {
+		return *m.ApproximateStorageDelta
+	}
+	return 0
+}
+
+func (m *Cost) GetIdSequenceUpdates() int32 {
+	if m != nil && m.IdSequenceUpdates != nil {
+		return *m.IdSequenceUpdates
+	}
+	return 0
+}
+
+type Cost_CommitCost struct {
+	RequestedEntityPuts    *int32 `protobuf:"varint,6,opt,name=requested_entity_puts,json=requestedEntityPuts" json:"requested_entity_puts,omitempty"`
+	RequestedEntityDeletes *int32 `protobuf:"varint,7,opt,name=requested_entity_deletes,json=requestedEntityDeletes" json:"requested_entity_deletes,omitempty"`
+	XXX_unrecognized       []byte `json:"-"`
+}
+
+func (m *Cost_CommitCost) Reset()                    { *m = Cost_CommitCost{} }
+func (m *Cost_CommitCost) String() string            { return proto.CompactTextString(m) }
+func (*Cost_CommitCost) ProtoMessage()               {}
+func (*Cost_CommitCost) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20, 0} }
+
+func (m *Cost_CommitCost) GetRequestedEntityPuts() int32 {
+	if m != nil && m.RequestedEntityPuts != nil {
+		return *m.RequestedEntityPuts
+	}
+	return 0
+}
+
+func (m *Cost_CommitCost) GetRequestedEntityDeletes() int32 {
+	if m != nil && m.RequestedEntityDeletes != nil {
+		return *m.RequestedEntityDeletes
+	}
+	return 0
+}
+
+type GetRequest struct {
+	Header           *InternalHeader `protobuf:"bytes,6,opt,name=header" json:"header,omitempty"`
+	Key              []*Reference    `protobuf:"bytes,1,rep,name=key" json:"key,omitempty"`
+	Transaction      *Transaction    `protobuf:"bytes,2,opt,name=transaction" json:"transaction,omitempty"`
+	FailoverMs       *int64          `protobuf:"varint,3,opt,name=failover_ms,json=failoverMs" json:"failover_ms,omitempty"`
+	Strong           *bool           `protobuf:"varint,4,opt,name=strong" json:"strong,omitempty"`
+	AllowDeferred    *bool           `protobuf:"varint,5,opt,name=allow_deferred,json=allowDeferred,def=0" json:"allow_deferred,omitempty"`
+	XXX_unrecognized []byte          `json:"-"`
+}
+
+func (m *GetRequest) Reset()                    { *m = GetRequest{} }
+func (m *GetRequest) String() string            { return proto.CompactTextString(m) }
+func (*GetRequest) ProtoMessage()               {}
+func (*GetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} }
+
+const Default_GetRequest_AllowDeferred bool = false
+
+func (m *GetRequest) GetHeader() *InternalHeader {
+	if m != nil {
+		return m.Header
+	}
+	return nil
+}
+
+func (m *GetRequest) GetKey() []*Reference {
+	if m != nil {
+		return m.Key
+	}
+	return nil
+}
+
+func (m *GetRequest) GetTransaction() *Transaction {
+	if m != nil {
+		return m.Transaction
+	}
+	return nil
+}
+
+func (m *GetRequest) GetFailoverMs() int64 {
+	if m != nil && m.FailoverMs != nil {
+		return *m.FailoverMs
+	}
+	return 0
+}
+
+func (m *GetRequest) GetStrong() bool {
+	if m != nil && m.Strong != nil {
+		return *m.Strong
+	}
+	return false
+}
+
+func (m *GetRequest) GetAllowDeferred() bool {
+	if m != nil && m.AllowDeferred != nil {
+		return *m.AllowDeferred
+	}
+	return Default_GetRequest_AllowDeferred
+}
+
+type GetResponse struct {
+	Entity           []*GetResponse_Entity `protobuf:"group,1,rep,name=Entity,json=entity" json:"entity,omitempty"`
+	Deferred         []*Reference          `protobuf:"bytes,5,rep,name=deferred" json:"deferred,omitempty"`
+	InOrder          *bool                 `protobuf:"varint,6,opt,name=in_order,json=inOrder,def=1" json:"in_order,omitempty"`
+	XXX_unrecognized []byte                `json:"-"`
+}
+
+func (m *GetResponse) Reset()                    { *m = GetResponse{} }
+func (m *GetResponse) String() string            { return proto.CompactTextString(m) }
+func (*GetResponse) ProtoMessage()               {}
+func (*GetResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} }
+
+const Default_GetResponse_InOrder bool = true
+
+func (m *GetResponse) GetEntity() []*GetResponse_Entity {
+	if m != nil {
+		return m.Entity
+	}
+	return nil
+}
+
+func (m *GetResponse) GetDeferred() []*Reference {
+	if m != nil {
+		return m.Deferred
+	}
+	return nil
+}
+
+func (m *GetResponse) GetInOrder() bool {
+	if m != nil && m.InOrder != nil {
+		return *m.InOrder
+	}
+	return Default_GetResponse_InOrder
+}
+
+type GetResponse_Entity struct {
+	Entity           *EntityProto `protobuf:"bytes,2,opt,name=entity" json:"entity,omitempty"`
+	Key              *Reference   `protobuf:"bytes,4,opt,name=key" json:"key,omitempty"`
+	Version          *int64       `protobuf:"varint,3,opt,name=version" json:"version,omitempty"`
+	XXX_unrecognized []byte       `json:"-"`
+}
+
+func (m *GetResponse_Entity) Reset()                    { *m = GetResponse_Entity{} }
+func (m *GetResponse_Entity) String() string            { return proto.CompactTextString(m) }
+func (*GetResponse_Entity) ProtoMessage()               {}
+func (*GetResponse_Entity) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22, 0} }
+
+func (m *GetResponse_Entity) GetEntity() *EntityProto {
+	if m != nil {
+		return m.Entity
+	}
+	return nil
+}
+
+func (m *GetResponse_Entity) GetKey() *Reference {
+	if m != nil {
+		return m.Key
+	}
+	return nil
+}
+
+func (m *GetResponse_Entity) GetVersion() int64 {
+	if m != nil && m.Version != nil {
+		return *m.Version
+	}
+	return 0
+}
+
+type PutRequest struct {
+	Header           *InternalHeader          `protobuf:"bytes,11,opt,name=header" json:"header,omitempty"`
+	Entity           []*EntityProto           `protobuf:"bytes,1,rep,name=entity" json:"entity,omitempty"`
+	Transaction      *Transaction             `protobuf:"bytes,2,opt,name=transaction" json:"transaction,omitempty"`
+	CompositeIndex   []*CompositeIndex        `protobuf:"bytes,3,rep,name=composite_index,json=compositeIndex" json:"composite_index,omitempty"`
+	Trusted          *bool                    `protobuf:"varint,4,opt,name=trusted,def=0" json:"trusted,omitempty"`
+	Force            *bool                    `protobuf:"varint,7,opt,name=force,def=0" json:"force,omitempty"`
+	MarkChanges      *bool                    `protobuf:"varint,8,opt,name=mark_changes,json=markChanges,def=0" json:"mark_changes,omitempty"`
+	Snapshot         []*Snapshot              `protobuf:"bytes,9,rep,name=snapshot" json:"snapshot,omitempty"`
+	AutoIdPolicy     *PutRequest_AutoIdPolicy `protobuf:"varint,10,opt,name=auto_id_policy,json=autoIdPolicy,enum=appengine.PutRequest_AutoIdPolicy,def=0" json:"auto_id_policy,omitempty"`
+	XXX_unrecognized []byte                   `json:"-"`
+}
+
+func (m *PutRequest) Reset()                    { *m = PutRequest{} }
+func (m *PutRequest) String() string            { return proto.CompactTextString(m) }
+func (*PutRequest) ProtoMessage()               {}
+func (*PutRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} }
+
+const Default_PutRequest_Trusted bool = false
+const Default_PutRequest_Force bool = false
+const Default_PutRequest_MarkChanges bool = false
+const Default_PutRequest_AutoIdPolicy PutRequest_AutoIdPolicy = PutRequest_CURRENT
+
+func (m *PutRequest) GetHeader() *InternalHeader {
+	if m != nil {
+		return m.Header
+	}
+	return nil
+}
+
+func (m *PutRequest) GetEntity() []*EntityProto {
+	if m != nil {
+		return m.Entity
+	}
+	return nil
+}
+
+func (m *PutRequest) GetTransaction() *Transaction {
+	if m != nil {
+		return m.Transaction
+	}
+	return nil
+}
+
+func (m *PutRequest) GetCompositeIndex() []*CompositeIndex {
+	if m != nil {
+		return m.CompositeIndex
+	}
+	return nil
+}
+
+func (m *PutRequest) GetTrusted() bool {
+	if m != nil && m.Trusted != nil {
+		return *m.Trusted
+	}
+	return Default_PutRequest_Trusted
+}
+
+func (m *PutRequest) GetForce() bool {
+	if m != nil && m.Force != nil {
+		return *m.Force
+	}
+	return Default_PutRequest_Force
+}
+
+func (m *PutRequest) GetMarkChanges() bool {
+	if m != nil && m.MarkChanges != nil {
+		return *m.MarkChanges
+	}
+	return Default_PutRequest_MarkChanges
+}
+
+func (m *PutRequest) GetSnapshot() []*Snapshot {
+	if m != nil {
+		return m.Snapshot
+	}
+	return nil
+}
+
+func (m *PutRequest) GetAutoIdPolicy() PutRequest_AutoIdPolicy {
+	if m != nil && m.AutoIdPolicy != nil {
+		return *m.AutoIdPolicy
+	}
+	return Default_PutRequest_AutoIdPolicy
+}
+
+type PutResponse struct {
+	Key              []*Reference `protobuf:"bytes,1,rep,name=key" json:"key,omitempty"`
+	Cost             *Cost        `protobuf:"bytes,2,opt,name=cost" json:"cost,omitempty"`
+	Version          []int64      `protobuf:"varint,3,rep,name=version" json:"version,omitempty"`
+	XXX_unrecognized []byte       `json:"-"`
+}
+
+func (m *PutResponse) Reset()                    { *m = PutResponse{} }
+func (m *PutResponse) String() string            { return proto.CompactTextString(m) }
+func (*PutResponse) ProtoMessage()               {}
+func (*PutResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} }
+
+func (m *PutResponse) GetKey() []*Reference {
+	if m != nil {
+		return m.Key
+	}
+	return nil
+}
+
+func (m *PutResponse) GetCost() *Cost {
+	if m != nil {
+		return m.Cost
+	}
+	return nil
+}
+
+func (m *PutResponse) GetVersion() []int64 {
+	if m != nil {
+		return m.Version
+	}
+	return nil
+}
+
+type TouchRequest struct {
+	Header           *InternalHeader   `protobuf:"bytes,10,opt,name=header" json:"header,omitempty"`
+	Key              []*Reference      `protobuf:"bytes,1,rep,name=key" json:"key,omitempty"`
+	CompositeIndex   []*CompositeIndex `protobuf:"bytes,2,rep,name=composite_index,json=compositeIndex" json:"composite_index,omitempty"`
+	Force            *bool             `protobuf:"varint,3,opt,name=force,def=0" json:"force,omitempty"`
+	Snapshot         []*Snapshot       `protobuf:"bytes,9,rep,name=snapshot" json:"snapshot,omitempty"`
+	XXX_unrecognized []byte            `json:"-"`
+}
+
+func (m *TouchRequest) Reset()                    { *m = TouchRequest{} }
+func (m *TouchRequest) String() string            { return proto.CompactTextString(m) }
+func (*TouchRequest) ProtoMessage()               {}
+func (*TouchRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{25} }
+
+const Default_TouchRequest_Force bool = false
+
+func (m *TouchRequest) GetHeader() *InternalHeader {
+	if m != nil {
+		return m.Header
+	}
+	return nil
+}
+
+func (m *TouchRequest) GetKey() []*Reference {
+	if m != nil {
+		return m.Key
+	}
+	return nil
+}
+
+func (m *TouchRequest) GetCompositeIndex() []*CompositeIndex {
+	if m != nil {
+		return m.CompositeIndex
+	}
+	return nil
+}
+
+func (m *TouchRequest) GetForce() bool {
+	if m != nil && m.Force != nil {
+		return *m.Force
+	}
+	return Default_TouchRequest_Force
+}
+
+func (m *TouchRequest) GetSnapshot() []*Snapshot {
+	if m != nil {
+		return m.Snapshot
+	}
+	return nil
+}
+
+type TouchResponse struct {
+	Cost             *Cost  `protobuf:"bytes,1,opt,name=cost" json:"cost,omitempty"`
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *TouchResponse) Reset()                    { *m = TouchResponse{} }
+func (m *TouchResponse) String() string            { return proto.CompactTextString(m) }
+func (*TouchResponse) ProtoMessage()               {}
+func (*TouchResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{26} }
+
+func (m *TouchResponse) GetCost() *Cost {
+	if m != nil {
+		return m.Cost
+	}
+	return nil
+}
+
+type DeleteRequest struct {
+	Header           *InternalHeader `protobuf:"bytes,10,opt,name=header" json:"header,omitempty"`
+	Key              []*Reference    `protobuf:"bytes,6,rep,name=key" json:"key,omitempty"`
+	Transaction      *Transaction    `protobuf:"bytes,5,opt,name=transaction" json:"transaction,omitempty"`
+	Trusted          *bool           `protobuf:"varint,4,opt,name=trusted,def=0" json:"trusted,omitempty"`
+	Force            *bool           `protobuf:"varint,7,opt,name=force,def=0" json:"force,omitempty"`
+	MarkChanges      *bool           `protobuf:"varint,8,opt,name=mark_changes,json=markChanges,def=0" json:"mark_changes,omitempty"`
+	Snapshot         []*Snapshot     `protobuf:"bytes,9,rep,name=snapshot" json:"snapshot,omitempty"`
+	XXX_unrecognized []byte          `json:"-"`
+}
+
+func (m *DeleteRequest) Reset()                    { *m = DeleteRequest{} }
+func (m *DeleteRequest) String() string            { return proto.CompactTextString(m) }
+func (*DeleteRequest) ProtoMessage()               {}
+func (*DeleteRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{27} }
+
+const Default_DeleteRequest_Trusted bool = false
+const Default_DeleteRequest_Force bool = false
+const Default_DeleteRequest_MarkChanges bool = false
+
+func (m *DeleteRequest) GetHeader() *InternalHeader {
+	if m != nil {
+		return m.Header
+	}
+	return nil
+}
+
+func (m *DeleteRequest) GetKey() []*Reference {
+	if m != nil {
+		return m.Key
+	}
+	return nil
+}
+
+func (m *DeleteRequest) GetTransaction() *Transaction {
+	if m != nil {
+		return m.Transaction
+	}
+	return nil
+}
+
+func (m *DeleteRequest) GetTrusted() bool {
+	if m != nil && m.Trusted != nil {
+		return *m.Trusted
+	}
+	return Default_DeleteRequest_Trusted
+}
+
+func (m *DeleteRequest) GetForce() bool {
+	if m != nil && m.Force != nil {
+		return *m.Force
+	}
+	return Default_DeleteRequest_Force
+}
+
+func (m *DeleteRequest) GetMarkChanges() bool {
+	if m != nil && m.MarkChanges != nil {
+		return *m.MarkChanges
+	}
+	return Default_DeleteRequest_MarkChanges
+}
+
+func (m *DeleteRequest) GetSnapshot() []*Snapshot {
+	if m != nil {
+		return m.Snapshot
+	}
+	return nil
+}
+
+type DeleteResponse struct {
+	Cost             *Cost   `protobuf:"bytes,1,opt,name=cost" json:"cost,omitempty"`
+	Version          []int64 `protobuf:"varint,3,rep,name=version" json:"version,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *DeleteResponse) Reset()                    { *m = DeleteResponse{} }
+func (m *DeleteResponse) String() string            { return proto.CompactTextString(m) }
+func (*DeleteResponse) ProtoMessage()               {}
+func (*DeleteResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{28} }
+
+func (m *DeleteResponse) GetCost() *Cost {
+	if m != nil {
+		return m.Cost
+	}
+	return nil
+}
+
+func (m *DeleteResponse) GetVersion() []int64 {
+	if m != nil {
+		return m.Version
+	}
+	return nil
+}
+
+type NextRequest struct {
+	Header           *InternalHeader `protobuf:"bytes,5,opt,name=header" json:"header,omitempty"`
+	Cursor           *Cursor         `protobuf:"bytes,1,req,name=cursor" json:"cursor,omitempty"`
+	Count            *int32          `protobuf:"varint,2,opt,name=count" json:"count,omitempty"`
+	Offset           *int32          `protobuf:"varint,4,opt,name=offset,def=0" json:"offset,omitempty"`
+	Compile          *bool           `protobuf:"varint,3,opt,name=compile,def=0" json:"compile,omitempty"`
+	XXX_unrecognized []byte          `json:"-"`
+}
+
+func (m *NextRequest) Reset()                    { *m = NextRequest{} }
+func (m *NextRequest) String() string            { return proto.CompactTextString(m) }
+func (*NextRequest) ProtoMessage()               {}
+func (*NextRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} }
+
+const Default_NextRequest_Offset int32 = 0
+const Default_NextRequest_Compile bool = false
+
+func (m *NextRequest) GetHeader() *InternalHeader {
+	if m != nil {
+		return m.Header
+	}
+	return nil
+}
+
+func (m *NextRequest) GetCursor() *Cursor {
+	if m != nil {
+		return m.Cursor
+	}
+	return nil
+}
+
+func (m *NextRequest) GetCount() int32 {
+	if m != nil && m.Count != nil {
+		return *m.Count
+	}
+	return 0
+}
+
+func (m *NextRequest) GetOffset() int32 {
+	if m != nil && m.Offset != nil {
+		return *m.Offset
+	}
+	return Default_NextRequest_Offset
+}
+
+func (m *NextRequest) GetCompile() bool {
+	if m != nil && m.Compile != nil {
+		return *m.Compile
+	}
+	return Default_NextRequest_Compile
+}
+
+type QueryResult struct {
+	Cursor           *Cursor           `protobuf:"bytes,1,opt,name=cursor" json:"cursor,omitempty"`
+	Result           []*EntityProto    `protobuf:"bytes,2,rep,name=result" json:"result,omitempty"`
+	SkippedResults   *int32            `protobuf:"varint,7,opt,name=skipped_results,json=skippedResults" json:"skipped_results,omitempty"`
+	MoreResults      *bool             `protobuf:"varint,3,req,name=more_results,json=moreResults" json:"more_results,omitempty"`
+	KeysOnly         *bool             `protobuf:"varint,4,opt,name=keys_only,json=keysOnly" json:"keys_only,omitempty"`
+	IndexOnly        *bool             `protobuf:"varint,9,opt,name=index_only,json=indexOnly" json:"index_only,omitempty"`
+	SmallOps         *bool             `protobuf:"varint,10,opt,name=small_ops,json=smallOps" json:"small_ops,omitempty"`
+	CompiledQuery    *CompiledQuery    `protobuf:"bytes,5,opt,name=compiled_query,json=compiledQuery" json:"compiled_query,omitempty"`
+	CompiledCursor   *CompiledCursor   `protobuf:"bytes,6,opt,name=compiled_cursor,json=compiledCursor" json:"compiled_cursor,omitempty"`
+	Index            []*CompositeIndex `protobuf:"bytes,8,rep,name=index" json:"index,omitempty"`
+	Version          []int64           `protobuf:"varint,11,rep,name=version" json:"version,omitempty"`
+	XXX_unrecognized []byte            `json:"-"`
+}
+
+func (m *QueryResult) Reset()                    { *m = QueryResult{} }
+func (m *QueryResult) String() string            { return proto.CompactTextString(m) }
+func (*QueryResult) ProtoMessage()               {}
+func (*QueryResult) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} }
+
+func (m *QueryResult) GetCursor() *Cursor {
+	if m != nil {
+		return m.Cursor
+	}
+	return nil
+}
+
+func (m *QueryResult) GetResult() []*EntityProto {
+	if m != nil {
+		return m.Result
+	}
+	return nil
+}
+
+func (m *QueryResult) GetSkippedResults() int32 {
+	if m != nil && m.SkippedResults != nil {
+		return *m.SkippedResults
+	}
+	return 0
+}
+
+func (m *QueryResult) GetMoreResults() bool {
+	if m != nil && m.MoreResults != nil {
+		return *m.MoreResults
+	}
+	return false
+}
+
+func (m *QueryResult) GetKeysOnly() bool {
+	if m != nil && m.KeysOnly != nil {
+		return *m.KeysOnly
+	}
+	return false
+}
+
+func (m *QueryResult) GetIndexOnly() bool {
+	if m != nil && m.IndexOnly != nil {
+		return *m.IndexOnly
+	}
+	return false
+}
+
+func (m *QueryResult) GetSmallOps() bool {
+	if m != nil && m.SmallOps != nil {
+		return *m.SmallOps
+	}
+	return false
+}
+
+func (m *QueryResult) GetCompiledQuery() *CompiledQuery {
+	if m != nil {
+		return m.CompiledQuery
+	}
+	return nil
+}
+
+func (m *QueryResult) GetCompiledCursor() *CompiledCursor {
+	if m != nil {
+		return m.CompiledCursor
+	}
+	return nil
+}
+
+func (m *QueryResult) GetIndex() []*CompositeIndex {
+	if m != nil {
+		return m.Index
+	}
+	return nil
+}
+
+func (m *QueryResult) GetVersion() []int64 {
+	if m != nil {
+		return m.Version
+	}
+	return nil
+}
+
+type AllocateIdsRequest struct {
+	Header           *InternalHeader `protobuf:"bytes,4,opt,name=header" json:"header,omitempty"`
+	ModelKey         *Reference      `protobuf:"bytes,1,opt,name=model_key,json=modelKey" json:"model_key,omitempty"`
+	Size             *int64          `protobuf:"varint,2,opt,name=size" json:"size,omitempty"`
+	Max              *int64          `protobuf:"varint,3,opt,name=max" json:"max,omitempty"`
+	Reserve          []*Reference    `protobuf:"bytes,5,rep,name=reserve" json:"reserve,omitempty"`
+	XXX_unrecognized []byte          `json:"-"`
+}
+
+func (m *AllocateIdsRequest) Reset()                    { *m = AllocateIdsRequest{} }
+func (m *AllocateIdsRequest) String() string            { return proto.CompactTextString(m) }
+func (*AllocateIdsRequest) ProtoMessage()               {}
+func (*AllocateIdsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} }
+
+func (m *AllocateIdsRequest) GetHeader() *InternalHeader {
+	if m != nil {
+		return m.Header
+	}
+	return nil
+}
+
+func (m *AllocateIdsRequest) GetModelKey() *Reference {
+	if m != nil {
+		return m.ModelKey
+	}
+	return nil
+}
+
+func (m *AllocateIdsRequest) GetSize() int64 {
+	if m != nil && m.Size != nil {
+		return *m.Size
+	}
+	return 0
+}
+
+func (m *AllocateIdsRequest) GetMax() int64 {
+	if m != nil && m.Max != nil {
+		return *m.Max
+	}
+	return 0
+}
+
+func (m *AllocateIdsRequest) GetReserve() []*Reference {
+	if m != nil {
+		return m.Reserve
+	}
+	return nil
+}
+
+type AllocateIdsResponse struct {
+	Start            *int64 `protobuf:"varint,1,req,name=start" json:"start,omitempty"`
+	End              *int64 `protobuf:"varint,2,req,name=end" json:"end,omitempty"`
+	Cost             *Cost  `protobuf:"bytes,3,opt,name=cost" json:"cost,omitempty"`
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *AllocateIdsResponse) Reset()                    { *m = AllocateIdsResponse{} }
+func (m *AllocateIdsResponse) String() string            { return proto.CompactTextString(m) }
+func (*AllocateIdsResponse) ProtoMessage()               {}
+func (*AllocateIdsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} }
+
+func (m *AllocateIdsResponse) GetStart() int64 {
+	if m != nil && m.Start != nil {
+		return *m.Start
+	}
+	return 0
+}
+
+func (m *AllocateIdsResponse) GetEnd() int64 {
+	if m != nil && m.End != nil {
+		return *m.End
+	}
+	return 0
+}
+
+func (m *AllocateIdsResponse) GetCost() *Cost {
+	if m != nil {
+		return m.Cost
+	}
+	return nil
+}
+
+type CompositeIndices struct {
+	Index            []*CompositeIndex `protobuf:"bytes,1,rep,name=index" json:"index,omitempty"`
+	XXX_unrecognized []byte            `json:"-"`
+}
+
+func (m *CompositeIndices) Reset()                    { *m = CompositeIndices{} }
+func (m *CompositeIndices) String() string            { return proto.CompactTextString(m) }
+func (*CompositeIndices) ProtoMessage()               {}
+func (*CompositeIndices) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{33} }
+
+func (m *CompositeIndices) GetIndex() []*CompositeIndex {
+	if m != nil {
+		return m.Index
+	}
+	return nil
+}
+
+type AddActionsRequest struct {
+	Header           *InternalHeader `protobuf:"bytes,3,opt,name=header" json:"header,omitempty"`
+	Transaction      *Transaction    `protobuf:"bytes,1,req,name=transaction" json:"transaction,omitempty"`
+	Action           []*Action       `protobuf:"bytes,2,rep,name=action" json:"action,omitempty"`
+	XXX_unrecognized []byte          `json:"-"`
+}
+
+func (m *AddActionsRequest) Reset()                    { *m = AddActionsRequest{} }
+func (m *AddActionsRequest) String() string            { return proto.CompactTextString(m) }
+func (*AddActionsRequest) ProtoMessage()               {}
+func (*AddActionsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{34} }
+
+func (m *AddActionsRequest) GetHeader() *InternalHeader {
+	if m != nil {
+		return m.Header
+	}
+	return nil
+}
+
+func (m *AddActionsRequest) GetTransaction() *Transaction {
+	if m != nil {
+		return m.Transaction
+	}
+	return nil
+}
+
+func (m *AddActionsRequest) GetAction() []*Action {
+	if m != nil {
+		return m.Action
+	}
+	return nil
+}
+
+type AddActionsResponse struct {
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *AddActionsResponse) Reset()                    { *m = AddActionsResponse{} }
+func (m *AddActionsResponse) String() string            { return proto.CompactTextString(m) }
+func (*AddActionsResponse) ProtoMessage()               {}
+func (*AddActionsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{35} }
+
+type BeginTransactionRequest struct {
+	Header              *InternalHeader                          `protobuf:"bytes,3,opt,name=header" json:"header,omitempty"`
+	App                 *string                                  `protobuf:"bytes,1,req,name=app" json:"app,omitempty"`
+	AllowMultipleEg     *bool                                    `protobuf:"varint,2,opt,name=allow_multiple_eg,json=allowMultipleEg,def=0" json:"allow_multiple_eg,omitempty"`
+	DatabaseId          *string                                  `protobuf:"bytes,4,opt,name=database_id,json=databaseId" json:"database_id,omitempty"`
+	Mode                *BeginTransactionRequest_TransactionMode `protobuf:"varint,5,opt,name=mode,enum=appengine.BeginTransactionRequest_TransactionMode,def=0" json:"mode,omitempty"`
+	PreviousTransaction *Transaction                             `protobuf:"bytes,7,opt,name=previous_transaction,json=previousTransaction" json:"previous_transaction,omitempty"`
+	XXX_unrecognized    []byte                                   `json:"-"`
+}
+
+func (m *BeginTransactionRequest) Reset()                    { *m = BeginTransactionRequest{} }
+func (m *BeginTransactionRequest) String() string            { return proto.CompactTextString(m) }
+func (*BeginTransactionRequest) ProtoMessage()               {}
+func (*BeginTransactionRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{36} }
+
+const Default_BeginTransactionRequest_AllowMultipleEg bool = false
+const Default_BeginTransactionRequest_Mode BeginTransactionRequest_TransactionMode = BeginTransactionRequest_UNKNOWN
+
+func (m *BeginTransactionRequest) GetHeader() *InternalHeader {
+	if m != nil {
+		return m.Header
+	}
+	return nil
+}
+
+func (m *BeginTransactionRequest) GetApp() string {
+	if m != nil && m.App != nil {
+		return *m.App
+	}
+	return ""
+}
+
+func (m *BeginTransactionRequest) GetAllowMultipleEg() bool {
+	if m != nil && m.AllowMultipleEg != nil {
+		return *m.AllowMultipleEg
+	}
+	return Default_BeginTransactionRequest_AllowMultipleEg
+}
+
+func (m *BeginTransactionRequest) GetDatabaseId() string {
+	if m != nil && m.DatabaseId != nil {
+		return *m.DatabaseId
+	}
+	return ""
+}
+
+func (m *BeginTransactionRequest) GetMode() BeginTransactionRequest_TransactionMode {
+	if m != nil && m.Mode != nil {
+		return *m.Mode
+	}
+	return Default_BeginTransactionRequest_Mode
+}
+
+func (m *BeginTransactionRequest) GetPreviousTransaction() *Transaction {
+	if m != nil {
+		return m.PreviousTransaction
+	}
+	return nil
+}
+
+type CommitResponse struct {
+	Cost             *Cost                     `protobuf:"bytes,1,opt,name=cost" json:"cost,omitempty"`
+	Version          []*CommitResponse_Version `protobuf:"group,3,rep,name=Version,json=version" json:"version,omitempty"`
+	XXX_unrecognized []byte                    `json:"-"`
+}
+
+func (m *CommitResponse) Reset()                    { *m = CommitResponse{} }
+func (m *CommitResponse) String() string            { return proto.CompactTextString(m) }
+func (*CommitResponse) ProtoMessage()               {}
+func (*CommitResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{37} }
+
+func (m *CommitResponse) GetCost() *Cost {
+	if m != nil {
+		return m.Cost
+	}
+	return nil
+}
+
+func (m *CommitResponse) GetVersion() []*CommitResponse_Version {
+	if m != nil {
+		return m.Version
+	}
+	return nil
+}
+
+type CommitResponse_Version struct {
+	RootEntityKey    *Reference `protobuf:"bytes,4,req,name=root_entity_key,json=rootEntityKey" json:"root_entity_key,omitempty"`
+	Version          *int64     `protobuf:"varint,5,req,name=version" json:"version,omitempty"`
+	XXX_unrecognized []byte     `json:"-"`
+}
+
+func (m *CommitResponse_Version) Reset()                    { *m = CommitResponse_Version{} }
+func (m *CommitResponse_Version) String() string            { return proto.CompactTextString(m) }
+func (*CommitResponse_Version) ProtoMessage()               {}
+func (*CommitResponse_Version) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{37, 0} }
+
+func (m *CommitResponse_Version) GetRootEntityKey() *Reference {
+	if m != nil {
+		return m.RootEntityKey
+	}
+	return nil
+}
+
+func (m *CommitResponse_Version) GetVersion() int64 {
+	if m != nil && m.Version != nil {
+		return *m.Version
+	}
+	return 0
+}
+
+func init() {
+	proto.RegisterType((*Action)(nil), "appengine.Action")
+	proto.RegisterType((*PropertyValue)(nil), "appengine.PropertyValue")
+	proto.RegisterType((*PropertyValue_PointValue)(nil), "appengine.PropertyValue.PointValue")
+	proto.RegisterType((*PropertyValue_UserValue)(nil), "appengine.PropertyValue.UserValue")
+	proto.RegisterType((*PropertyValue_ReferenceValue)(nil), "appengine.PropertyValue.ReferenceValue")
+	proto.RegisterType((*PropertyValue_ReferenceValue_PathElement)(nil), "appengine.PropertyValue.ReferenceValue.PathElement")
+	proto.RegisterType((*Property)(nil), "appengine.Property")
+	proto.RegisterType((*Path)(nil), "appengine.Path")
+	proto.RegisterType((*Path_Element)(nil), "appengine.Path.Element")
+	proto.RegisterType((*Reference)(nil), "appengine.Reference")
+	proto.RegisterType((*User)(nil), "appengine.User")
+	proto.RegisterType((*EntityProto)(nil), "appengine.EntityProto")
+	proto.RegisterType((*CompositeProperty)(nil), "appengine.CompositeProperty")
+	proto.RegisterType((*Index)(nil), "appengine.Index")
+	proto.RegisterType((*Index_Property)(nil), "appengine.Index.Property")
+	proto.RegisterType((*CompositeIndex)(nil), "appengine.CompositeIndex")
+	proto.RegisterType((*IndexPostfix)(nil), "appengine.IndexPostfix")
+	proto.RegisterType((*IndexPostfix_IndexValue)(nil), "appengine.IndexPostfix.IndexValue")
+	proto.RegisterType((*IndexPosition)(nil), "appengine.IndexPosition")
+	proto.RegisterType((*Snapshot)(nil), "appengine.Snapshot")
+	proto.RegisterType((*InternalHeader)(nil), "appengine.InternalHeader")
+	proto.RegisterType((*Transaction)(nil), "appengine.Transaction")
+	proto.RegisterType((*Query)(nil), "appengine.Query")
+	proto.RegisterType((*Query_Filter)(nil), "appengine.Query.Filter")
+	proto.RegisterType((*Query_Order)(nil), "appengine.Query.Order")
+	proto.RegisterType((*CompiledQuery)(nil), "appengine.CompiledQuery")
+	proto.RegisterType((*CompiledQuery_PrimaryScan)(nil), "appengine.CompiledQuery.PrimaryScan")
+	proto.RegisterType((*CompiledQuery_MergeJoinScan)(nil), "appengine.CompiledQuery.MergeJoinScan")
+	proto.RegisterType((*CompiledQuery_EntityFilter)(nil), "appengine.CompiledQuery.EntityFilter")
+	proto.RegisterType((*CompiledCursor)(nil), "appengine.CompiledCursor")
+	proto.RegisterType((*CompiledCursor_Position)(nil), "appengine.CompiledCursor.Position")
+	proto.RegisterType((*CompiledCursor_Position_IndexValue)(nil), "appengine.CompiledCursor.Position.IndexValue")
+	proto.RegisterType((*Cursor)(nil), "appengine.Cursor")
+	proto.RegisterType((*Error)(nil), "appengine.Error")
+	proto.RegisterType((*Cost)(nil), "appengine.Cost")
+	proto.RegisterType((*Cost_CommitCost)(nil), "appengine.Cost.CommitCost")
+	proto.RegisterType((*GetRequest)(nil), "appengine.GetRequest")
+	proto.RegisterType((*GetResponse)(nil), "appengine.GetResponse")
+	proto.RegisterType((*GetResponse_Entity)(nil), "appengine.GetResponse.Entity")
+	proto.RegisterType((*PutRequest)(nil), "appengine.PutRequest")
+	proto.RegisterType((*PutResponse)(nil), "appengine.PutResponse")
+	proto.RegisterType((*TouchRequest)(nil), "appengine.TouchRequest")
+	proto.RegisterType((*TouchResponse)(nil), "appengine.TouchResponse")
+	proto.RegisterType((*DeleteRequest)(nil), "appengine.DeleteRequest")
+	proto.RegisterType((*DeleteResponse)(nil), "appengine.DeleteResponse")
+	proto.RegisterType((*NextRequest)(nil), "appengine.NextRequest")
+	proto.RegisterType((*QueryResult)(nil), "appengine.QueryResult")
+	proto.RegisterType((*AllocateIdsRequest)(nil), "appengine.AllocateIdsRequest")
+	proto.RegisterType((*AllocateIdsResponse)(nil), "appengine.AllocateIdsResponse")
+	proto.RegisterType((*CompositeIndices)(nil), "appengine.CompositeIndices")
+	proto.RegisterType((*AddActionsRequest)(nil), "appengine.AddActionsRequest")
+	proto.RegisterType((*AddActionsResponse)(nil), "appengine.AddActionsResponse")
+	proto.RegisterType((*BeginTransactionRequest)(nil), "appengine.BeginTransactionRequest")
+	proto.RegisterType((*CommitResponse)(nil), "appengine.CommitResponse")
+	proto.RegisterType((*CommitResponse_Version)(nil), "appengine.CommitResponse.Version")
+}
+
+func init() {
+	proto.RegisterFile("google.golang.org/appengine/internal/datastore/datastore_v3.proto", fileDescriptor0)
+}
+
+var fileDescriptor0 = []byte{
+	// 4156 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xcd, 0x73, 0xe3, 0x46,
+	0x76, 0x37, 0xc1, 0xef, 0x47, 0x89, 0x82, 0x5a, 0xf3, 0xc1, 0xa1, 0x3f, 0x46, 0xc6, 0xac, 0x6d,
+	0xd9, 0x6b, 0x73, 0x6c, 0xf9, 0x23, 0x5b, 0x4a, 0x76, 0x1d, 0x4a, 0xc4, 0x68, 0x90, 0xa1, 0x48,
+	0xb9, 0x09, 0xd9, 0x9e, 0x5c, 0x50, 0x18, 0xa2, 0x29, 0x21, 0x43, 0x02, 0x30, 0x00, 0x6a, 0x46,
+	0x93, 0xe4, 0x90, 0x4b, 0x2a, 0x55, 0x5b, 0xa9, 0x1c, 0x92, 0x4a, 0x25, 0xf9, 0x07, 0x72, 0xc8,
+	0x39, 0x95, 0xaa, 0x54, 0xf6, 0x98, 0x5b, 0x0e, 0x7b, 0xc9, 0x31, 0x95, 0x73, 0xf2, 0x27, 0x24,
+	0x39, 0xa4, 0xfa, 0x75, 0x03, 0x02, 0x28, 0x4a, 0x23, 0x6d, 0xf6, 0x90, 0x13, 0xd1, 0xef, 0xfd,
+	0xba, 0xf1, 0xfa, 0xf5, 0xfb, 0x6c, 0x10, 0xba, 0xc7, 0xbe, 0x7f, 0x3c, 0x65, 0x9d, 0x63, 0x7f,
+	0x6a, 0x7b, 0xc7, 0x1d, 0x3f, 0x3c, 0x7e, 0x68, 0x07, 0x01, 0xf3, 0x8e, 0x5d, 0x8f, 0x3d, 0x74,
+	0xbd, 0x98, 0x85, 0x9e, 0x3d, 0x7d, 0xe8, 0xd8, 0xb1, 0x1d, 0xc5, 0x7e, 0xc8, 0xce, 0x9f, 0xac,
+	0xd3, 0xcf, 0x3b, 0x41, 0xe8, 0xc7, 0x3e, 0xa9, 0xa7, 0x13, 0xb4, 0x1a, 0x54, 0xba, 0xe3, 0xd8,
+	0xf5, 0x3d, 0xed, 0x1f, 0x2b, 0xb0, 0x7a, 0x18, 0xfa, 0x01, 0x0b, 0xe3, 0xb3, 0x6f, 0xed, 0xe9,
+	0x9c, 0x91, 0x77, 0x00, 0x5c, 0x2f, 0xfe, 0xea, 0x0b, 0x1c, 0xb5, 0x0a, 0x9b, 0x85, 0xad, 0x22,
+	0xcd, 0x50, 0x88, 0x06, 0x2b, 0xcf, 0x7c, 0x7f, 0xca, 0x6c, 0x4f, 0x20, 0x94, 0xcd, 0xc2, 0x56,
+	0x8d, 0xe6, 0x68, 0x64, 0x13, 0x1a, 0x51, 0x1c, 0xba, 0xde, 0xb1, 0x80, 0x14, 0x37, 0x0b, 0x5b,
+	0x75, 0x9a, 0x25, 0x71, 0x84, 0xe3, 0xcf, 0x9f, 0x4d, 0x99, 0x40, 0x94, 0x36, 0x0b, 0x5b, 0x05,
+	0x9a, 0x25, 0x91, 0x3d, 0x80, 0xc0, 0x77, 0xbd, 0xf8, 0x14, 0x01, 0xe5, 0xcd, 0xc2, 0x16, 0x6c,
+	0x3f, 0xe8, 0xa4, 0x7b, 0xe8, 0xe4, 0xa4, 0xee, 0x1c, 0x72, 0x28, 0x3e, 0xd2, 0xcc, 0x34, 0xf2,
+	0xdb, 0x50, 0x9f, 0x47, 0x2c, 0x14, 0x6b, 0xd4, 0x70, 0x0d, 0xed, 0xd2, 0x35, 0x8e, 0x22, 0x16,
+	0x8a, 0x25, 0xce, 0x27, 0x91, 0x21, 0x34, 0x43, 0x36, 0x61, 0x21, 0xf3, 0xc6, 0x4c, 0x2c, 0xb3,
+	0x82, 0xcb, 0x7c, 0x70, 0xe9, 0x32, 0x34, 0x81, 0x8b, 0xb5, 0x16, 0xa6, 0xb7, 0xb7, 0x00, 0xce,
+	0x85, 0x25, 0x2b, 0x50, 0x78, 0xd9, 0xaa, 0x6c, 0x2a, 0x5b, 0x05, 0x5a, 0x78, 0xc9, 0x47, 0x67,
+	0xad, 0xaa, 0x18, 0x9d, 0xb5, 0xff, 0xa9, 0x00, 0xf5, 0x54, 0x26, 0x72, 0x0b, 0xca, 0x6c, 0x66,
+	0xbb, 0xd3, 0x56, 0x7d, 0x53, 0xd9, 0xaa, 0x53, 0x31, 0x20, 0xf7, 0xa1, 0x61, 0xcf, 0xe3, 0x13,
+	0xcb, 0xf1, 0x67, 0xb6, 0xeb, 0xb5, 0x00, 0x79, 0xc0, 0x49, 0x3d, 0xa4, 0x90, 0x36, 0xd4, 0x3c,
+	0x77, 0xfc, 0xdc, 0xb3, 0x67, 0xac, 0xd5, 0xc0, 0x73, 0x48, 0xc7, 0xe4, 0x13, 0x20, 0x13, 0xe6,
+	0xb0, 0xd0, 0x8e, 0x99, 0x63, 0xb9, 0x0e, 0xf3, 0x62, 0x37, 0x3e, 0x6b, 0xdd, 0x46, 0xd4, 0x7a,
+	0xca, 0x31, 0x24, 0x23, 0x0f, 0x0f, 0x42, 0xff, 0xd4, 0x75, 0x58, 0xd8, 0xba, 0xb3, 0x00, 0x3f,
+	0x94, 0x8c, 0xf6, 0xbf, 0x17, 0xa0, 0x99, 0xd7, 0x05, 0x51, 0xa1, 0x68, 0x07, 0x41, 0x6b, 0x15,
+	0xa5, 0xe4, 0x8f, 0xe4, 0x6d, 0x00, 0x2e, 0x8a, 0x15, 0x05, 0xf6, 0x98, 0xb5, 0x6e, 0xe1, 0x5a,
+	0x75, 0x4e, 0x19, 0x71, 0x02, 0x39, 0x82, 0x46, 0x60, 0xc7, 0x27, 0x6c, 0xca, 0x66, 0xcc, 0x8b,
+	0x5b, 0xcd, 0xcd, 0xe2, 0x16, 0x6c, 0x7f, 0x7e, 0x4d, 0xd5, 0x77, 0x0e, 0xed, 0xf8, 0x44, 0x17,
+	0x53, 0x69, 0x76, 0x9d, 0xb6, 0x0e, 0x8d, 0x0c, 0x8f, 0x10, 0x28, 0xc5, 0x67, 0x01, 0x6b, 0xad,
+	0xa1, 0x5c, 0xf8, 0x4c, 0x9a, 0xa0, 0xb8, 0x4e, 0x4b, 0x45, 0xf3, 0x57, 0x5c, 0x87, 0x63, 0x50,
+	0x87, 0xeb, 0x28, 0x22, 0x3e, 0x6b, 0xff, 0x51, 0x86, 0x5a, 0x22, 0x00, 0xe9, 0x42, 0x75, 0xc6,
+	0x6c, 0xcf, 0xf5, 0x8e, 0xd1, 0x69, 0x9a, 0xdb, 0x6f, 0x2e, 0x11, 0xb3, 0x73, 0x20, 0x20, 0x3b,
+	0x30, 0x18, 0x5a, 0x07, 0x7a, 0x77, 0x60, 0x0c, 0xf6, 0x69, 0x32, 0x8f, 0x1f, 0xa6, 0x7c, 0xb4,
+	0xe6, 0xa1, 0x8b, 0x9e, 0x55, 0xa7, 0x20, 0x49, 0x47, 0xa1, 0x9b, 0x0a, 0x51, 0x14, 0x82, 0xe2,
+	0x21, 0x76, 0xa0, 0x9c, 0xb8, 0x88, 0xb2, 0xd5, 0xd8, 0x6e, 0x5d, 0xa6, 0x1c, 0x2a, 0x60, 0xdc,
+	0x20, 0x66, 0xf3, 0x69, 0xec, 0x06, 0x53, 0xee, 0x76, 0xca, 0x56, 0x8d, 0xa6, 0x63, 0xf2, 0x1e,
+	0x40, 0xc4, 0xec, 0x70, 0x7c, 0x62, 0x3f, 0x9b, 0xb2, 0x56, 0x85, 0x7b, 0xf6, 0x4e, 0x79, 0x62,
+	0x4f, 0x23, 0x46, 0x33, 0x0c, 0x62, 0xc3, 0xdd, 0x49, 0x1c, 0x59, 0xb1, 0xff, 0x9c, 0x79, 0xee,
+	0x2b, 0x9b, 0x07, 0x12, 0xcb, 0x0f, 0xf8, 0x0f, 0xfa, 0x58, 0x73, 0xfb, 0xc3, 0x65, 0x5b, 0x7f,
+	0x14, 0x47, 0x66, 0x66, 0xc6, 0x10, 0x27, 0xd0, 0xdb, 0x93, 0x65, 0x64, 0xd2, 0x86, 0xca, 0xd4,
+	0x1f, 0xdb, 0x53, 0xd6, 0xaa, 0x73, 0x2d, 0xec, 0x28, 0xcc, 0xa3, 0x92, 0xa2, 0xfd, 0xb3, 0x02,
+	0x55, 0xa9, 0x47, 0xd2, 0x84, 0x8c, 0x26, 0xd5, 0x37, 0x48, 0x0d, 0x4a, 0xbb, 0xfd, 0xe1, 0xae,
+	0xda, 0xe4, 0x4f, 0xa6, 0xfe, 0xbd, 0xa9, 0xae, 0x71, 0xcc, 0xee, 0x53, 0x53, 0x1f, 0x99, 0x94,
+	0x63, 0x54, 0xb2, 0x0e, 0xab, 0x5d, 0x73, 0x78, 0x60, 0xed, 0x75, 0x4d, 0x7d, 0x7f, 0x48, 0x9f,
+	0xaa, 0x05, 0xb2, 0x0a, 0x75, 0x24, 0xf5, 0x8d, 0xc1, 0x13, 0x55, 0xe1, 0x33, 0x70, 0x68, 0x1a,
+	0x66, 0x5f, 0x57, 0x8b, 0x44, 0x85, 0x15, 0x31, 0x63, 0x38, 0x30, 0xf5, 0x81, 0xa9, 0x96, 0x52,
+	0xca, 0xe8, 0xe8, 0xe0, 0xa0, 0x4b, 0x9f, 0xaa, 0x65, 0xb2, 0x06, 0x0d, 0xa4, 0x74, 0x8f, 0xcc,
+	0xc7, 0x43, 0xaa, 0x56, 0x48, 0x03, 0xaa, 0xfb, 0x3d, 0xeb, 0xbb, 0xc7, 0xfa, 0x40, 0xad, 0x92,
+	0x15, 0xa8, 0xed, 0xf7, 0x2c, 0xfd, 0xa0, 0x6b, 0xf4, 0xd5, 0x1a, 0x9f, 0xbd, 0xaf, 0x0f, 0xe9,
+	0x68, 0x64, 0x1d, 0x0e, 0x8d, 0x81, 0xa9, 0xd6, 0x49, 0x1d, 0xca, 0xfb, 0x3d, 0xcb, 0x38, 0x50,
+	0x81, 0x10, 0x68, 0xee, 0xf7, 0xac, 0xc3, 0xc7, 0xc3, 0x81, 0x3e, 0x38, 0x3a, 0xd8, 0xd5, 0xa9,
+	0xda, 0x20, 0xb7, 0x40, 0xe5, 0xb4, 0xe1, 0xc8, 0xec, 0xf6, 0xbb, 0xbd, 0x1e, 0xd5, 0x47, 0x23,
+	0x75, 0x85, 0x4b, 0xbd, 0xdf, 0xb3, 0x68, 0xd7, 0xe4, 0xfb, 0x5a, 0xe5, 0x2f, 0xe4, 0x7b, 0x7f,
+	0xa2, 0x3f, 0x55, 0xd7, 0xf9, 0x2b, 0xf4, 0x81, 0x69, 0x98, 0x4f, 0xad, 0x43, 0x3a, 0x34, 0x87,
+	0xea, 0x06, 0x17, 0xd0, 0x18, 0xf4, 0xf4, 0xef, 0xad, 0x6f, 0xbb, 0xfd, 0x23, 0x5d, 0x25, 0xda,
+	0x8f, 0xe1, 0xf6, 0xd2, 0x33, 0xe1, 0xaa, 0x7b, 0x6c, 0x1e, 0xf4, 0xd5, 0x02, 0x7f, 0xe2, 0x9b,
+	0x52, 0x15, 0xed, 0x0f, 0xa0, 0xc4, 0x5d, 0x86, 0x7c, 0x06, 0xd5, 0xc4, 0x1b, 0x0b, 0xe8, 0x8d,
+	0x77, 0xb3, 0x67, 0x6d, 0xc7, 0x27, 0x9d, 0xc4, 0xe3, 0x12, 0x5c, 0xbb, 0x0b, 0xd5, 0x45, 0x4f,
+	0x53, 0x2e, 0x78, 0x5a, 0xf1, 0x82, 0xa7, 0x95, 0x32, 0x9e, 0x66, 0x43, 0x3d, 0xf5, 0xed, 0x9b,
+	0x47, 0x91, 0x07, 0x50, 0xe2, 0xde, 0xdf, 0x6a, 0xa2, 0x87, 0xac, 0x2d, 0x08, 0x4c, 0x91, 0xa9,
+	0xfd, 0x43, 0x01, 0x4a, 0x3c, 0xda, 0x9e, 0x07, 0xda, 0xc2, 0x15, 0x81, 0x56, 0xb9, 0x32, 0xd0,
+	0x16, 0xaf, 0x15, 0x68, 0x2b, 0x37, 0x0b, 0xb4, 0xd5, 0x4b, 0x02, 0xad, 0xf6, 0x67, 0x45, 0x68,
+	0xe8, 0x38, 0xf3, 0x10, 0x13, 0xfd, 0xfb, 0x50, 0x7c, 0xce, 0xce, 0x50, 0x3f, 0x8d, 0xed, 0x5b,
+	0x99, 0xdd, 0xa6, 0x2a, 0xa4, 0x1c, 0x40, 0xb6, 0x61, 0x45, 0xbc, 0xd0, 0x3a, 0x0e, 0xfd, 0x79,
+	0xd0, 0x52, 0x97, 0xab, 0xa7, 0x21, 0x40, 0xfb, 0x1c, 0x43, 0xde, 0x83, 0xb2, 0xff, 0xc2, 0x63,
+	0x21, 0xc6, 0xc1, 0x3c, 0x98, 0x2b, 0x8f, 0x0a, 0x2e, 0x79, 0x08, 0xa5, 0xe7, 0xae, 0xe7, 0xe0,
+	0x19, 0xe6, 0x23, 0x61, 0x46, 0xd0, 0xce, 0x13, 0xd7, 0x73, 0x28, 0x02, 0xc9, 0x3d, 0xa8, 0xf1,
+	0x5f, 0x8c, 0x7b, 0x65, 0xdc, 0x68, 0x95, 0x8f, 0x79, 0xd0, 0x7b, 0x08, 0xb5, 0x40, 0xc6, 0x10,
+	0x4c, 0x00, 0x8d, 0xed, 0x8d, 0x25, 0xe1, 0x85, 0xa6, 0x20, 0xf2, 0x15, 0xac, 0x84, 0xf6, 0x0b,
+	0x2b, 0x9d, 0xb4, 0x76, 0xf9, 0xa4, 0x46, 0x68, 0xbf, 0x48, 0x23, 0x38, 0x81, 0x52, 0x68, 0x7b,
+	0xcf, 0x5b, 0x64, 0xb3, 0xb0, 0x55, 0xa6, 0xf8, 0xac, 0x7d, 0x01, 0x25, 0x2e, 0x25, 0x8f, 0x08,
+	0xfb, 0x3d, 0xf4, 0xff, 0xee, 0x9e, 0xa9, 0x16, 0x12, 0x7f, 0xfe, 0x96, 0x47, 0x03, 0x45, 0x72,
+	0x0f, 0xf4, 0xd1, 0xa8, 0xbb, 0xaf, 0xab, 0x45, 0xad, 0x07, 0xeb, 0x7b, 0xfe, 0x2c, 0xf0, 0x23,
+	0x37, 0x66, 0xe9, 0xf2, 0xf7, 0xa0, 0xe6, 0x7a, 0x0e, 0x7b, 0x69, 0xb9, 0x0e, 0x9a, 0x56, 0x91,
+	0x56, 0x71, 0x6c, 0x38, 0xdc, 0xe4, 0x4e, 0x65, 0x31, 0x55, 0xe4, 0x26, 0x87, 0x03, 0xed, 0x2f,
+	0x15, 0x28, 0x1b, 0x1c, 0xc1, 0x8d, 0x4f, 0x9e, 0x14, 0x7a, 0x8f, 0x30, 0x4c, 0x10, 0x24, 0x93,
+	0xfb, 0x50, 0x1b, 0x6a, 0xb6, 0x37, 0x66, 0xbc, 0xe2, 0xc3, 0x3c, 0x50, 0xa3, 0xe9, 0x98, 0x7c,
+	0x99, 0xd1, 0x9f, 0x82, 0x2e, 0x7b, 0x2f, 0xa3, 0x0a, 0x7c, 0xc1, 0x12, 0x2d, 0xb6, 0xff, 0xaa,
+	0x90, 0x49, 0x6e, 0xcb, 0x12, 0x4f, 0x1f, 0xea, 0x8e, 0x1b, 0x32, 0xac, 0x23, 0xe5, 0x41, 0x3f,
+	0xb8, 0x74, 0xe1, 0x4e, 0x2f, 0x81, 0xee, 0xd4, 0xbb, 0xa3, 0x3d, 0x7d, 0xd0, 0xe3, 0x99, 0xef,
+	0x7c, 0x01, 0xed, 0x23, 0xa8, 0xa7, 0x10, 0x0c, 0xc7, 0x09, 0x48, 0x2d, 0x70, 0xf5, 0xf6, 0xf4,
+	0x74, 0xac, 0x68, 0x7f, 0xad, 0x40, 0x33, 0xd5, 0xaf, 0xd0, 0xd0, 0x6d, 0xa8, 0xd8, 0x41, 0x90,
+	0xa8, 0xb6, 0x4e, 0xcb, 0x76, 0x10, 0x18, 0x8e, 0x8c, 0x2d, 0x0a, 0x6a, 0x9b, 0xc7, 0x96, 0x4f,
+	0x01, 0x1c, 0x36, 0x71, 0x3d, 0x17, 0x85, 0x2e, 0xa2, 0xc1, 0xab, 0x8b, 0x42, 0xd3, 0x0c, 0x86,
+	0x7c, 0x09, 0xe5, 0x28, 0xb6, 0x63, 0x91, 0x2b, 0x9b, 0xdb, 0xf7, 0x33, 0xe0, 0xbc, 0x08, 0x9d,
+	0x11, 0x87, 0x51, 0x81, 0x26, 0x5f, 0xc1, 0x2d, 0xdf, 0x9b, 0x9e, 0x59, 0xf3, 0x88, 0x59, 0xee,
+	0xc4, 0x0a, 0xd9, 0x0f, 0x73, 0x37, 0x64, 0x4e, 0x3e, 0xa7, 0xae, 0x73, 0xc8, 0x51, 0xc4, 0x8c,
+	0x09, 0x95, 0x7c, 0xed, 0x6b, 0x28, 0xe3, 0x3a, 0x7c, 0xcf, 0xdf, 0x51, 0xc3, 0xd4, 0xad, 0xe1,
+	0xa0, 0xff, 0x54, 0xe8, 0x80, 0xea, 0xdd, 0x9e, 0x85, 0x44, 0x55, 0xe1, 0xc1, 0xbe, 0xa7, 0xf7,
+	0x75, 0x53, 0xef, 0xa9, 0x45, 0x9e, 0x3d, 0x74, 0x4a, 0x87, 0x54, 0x2d, 0x69, 0xff, 0x53, 0x80,
+	0x15, 0x94, 0xe7, 0xd0, 0x8f, 0xe2, 0x89, 0xfb, 0x92, 0xec, 0x41, 0x43, 0x98, 0xdd, 0xa9, 0x2c,
+	0xe8, 0xb9, 0x33, 0x68, 0x8b, 0x7b, 0x96, 0x68, 0x31, 0x90, 0x75, 0xb4, 0x9b, 0x3e, 0x27, 0x21,
+	0x45, 0x41, 0xa7, 0xbf, 0x22, 0xa4, 0xbc, 0x05, 0x95, 0x67, 0x6c, 0xe2, 0x87, 0x22, 0x04, 0xd6,
+	0x76, 0x4a, 0x71, 0x38, 0x67, 0x54, 0xd2, 0xda, 0x36, 0xc0, 0xf9, 0xfa, 0xe4, 0x01, 0xac, 0x26,
+	0xc6, 0x66, 0xa1, 0x71, 0x89, 0x93, 0x5b, 0x49, 0x88, 0x83, 0x5c, 0x75, 0xa3, 0x5c, 0xab, 0xba,
+	0xd1, 0xbe, 0x86, 0xd5, 0x64, 0x3f, 0xe2, 0xfc, 0x54, 0x21, 0x79, 0x01, 0x63, 0xca, 0x82, 0x8c,
+	0xca, 0x45, 0x19, 0xb5, 0x9f, 0x41, 0x6d, 0xe4, 0xd9, 0x41, 0x74, 0xe2, 0xc7, 0xdc, 0x7a, 0xe2,
+	0x48, 0xfa, 0xaa, 0x12, 0x47, 0x9a, 0x06, 0x15, 0x7e, 0x38, 0xf3, 0x88, 0xbb, 0xbf, 0x31, 0xe8,
+	0xee, 0x99, 0xc6, 0xb7, 0xba, 0xfa, 0x06, 0x01, 0xa8, 0xc8, 0xe7, 0x82, 0xa6, 0x41, 0xd3, 0x90,
+	0xed, 0xd8, 0x63, 0x66, 0x3b, 0x2c, 0xe4, 0x12, 0xfc, 0xe0, 0x47, 0x89, 0x04, 0x3f, 0xf8, 0x91,
+	0xf6, 0x17, 0x05, 0x68, 0x98, 0xa1, 0xed, 0x45, 0xb6, 0x30, 0xf7, 0xcf, 0xa0, 0x72, 0x82, 0x58,
+	0x74, 0xa3, 0xc6, 0x82, 0x7f, 0x66, 0x17, 0xa3, 0x12, 0x48, 0xee, 0x40, 0xe5, 0xc4, 0xf6, 0x9c,
+	0xa9, 0xd0, 0x5a, 0x85, 0xca, 0x51, 0x92, 0x1b, 0x95, 0xf3, 0xdc, 0xb8, 0x05, 0x2b, 0x33, 0x3b,
+	0x7c, 0x6e, 0x8d, 0x4f, 0x6c, 0xef, 0x98, 0x45, 0xf2, 0x60, 0xa4, 0x05, 0x36, 0x38, 0x6b, 0x4f,
+	0x70, 0xb4, 0xbf, 0x5f, 0x81, 0xf2, 0x37, 0x73, 0x16, 0x9e, 0x65, 0x04, 0xfa, 0xe0, 0xba, 0x02,
+	0xc9, 0x17, 0x17, 0x2e, 0x4b, 0xca, 0x6f, 0x2f, 0x26, 0x65, 0x22, 0x53, 0x84, 0xc8, 0x95, 0x22,
+	0x0b, 0x7c, 0x9a, 0x09, 0x63, 0xeb, 0x57, 0xd8, 0xda, 0x79, 0x70, 0x7b, 0x08, 0x95, 0x89, 0x3b,
+	0x8d, 0x51, 0x75, 0x8b, 0xd5, 0x08, 0xee, 0xa5, 0xf3, 0x08, 0xd9, 0x54, 0xc2, 0xc8, 0xbb, 0xb0,
+	0x22, 0x2a, 0x59, 0xeb, 0x07, 0xce, 0xc6, 0x82, 0x95, 0xf7, 0xa6, 0x48, 0x13, 0xbb, 0xff, 0x18,
+	0xca, 0x7e, 0xc8, 0x37, 0x5f, 0xc7, 0x25, 0xef, 0x5c, 0x58, 0x72, 0xc8, 0xb9, 0x54, 0x80, 0xc8,
+	0x87, 0x50, 0x3a, 0x71, 0xbd, 0x18, 0xb3, 0x46, 0x73, 0xfb, 0xf6, 0x05, 0xf0, 0x63, 0xd7, 0x8b,
+	0x29, 0x42, 0x78, 0x98, 0x1f, 0xfb, 0x73, 0x2f, 0x6e, 0xdd, 0xc5, 0x0c, 0x23, 0x06, 0xe4, 0x1e,
+	0x54, 0xfc, 0xc9, 0x24, 0x62, 0x31, 0x76, 0x96, 0xe5, 0x9d, 0xc2, 0xa7, 0x54, 0x12, 0xf8, 0x84,
+	0xa9, 0x3b, 0x73, 0x63, 0xec, 0x43, 0xca, 0x54, 0x0c, 0xc8, 0x2e, 0xac, 0x8d, 0xfd, 0x59, 0xe0,
+	0x4e, 0x99, 0x63, 0x8d, 0xe7, 0x61, 0xe4, 0x87, 0xad, 0x77, 0x2e, 0x1c, 0xd3, 0x9e, 0x44, 0xec,
+	0x21, 0x80, 0x36, 0xc7, 0xb9, 0x31, 0x31, 0x60, 0x83, 0x79, 0x8e, 0xb5, 0xb8, 0xce, 0xfd, 0xd7,
+	0xad, 0xb3, 0xce, 0x3c, 0x27, 0x4f, 0x4a, 0xc4, 0xc1, 0x48, 0x68, 0x61, 0xcc, 0x68, 0x6d, 0x60,
+	0x90, 0xb9, 0x77, 0x69, 0xac, 0x14, 0xe2, 0x64, 0xc2, 0xf7, 0x6f, 0xc0, 0x2d, 0x19, 0x22, 0xad,
+	0x80, 0x85, 0x13, 0x36, 0x8e, 0xad, 0x60, 0x6a, 0x7b, 0x58, 0xca, 0xa5, 0xc6, 0x4a, 0x24, 0xe4,
+	0x50, 0x20, 0x0e, 0xa7, 0xb6, 0x47, 0x34, 0xa8, 0x3f, 0x67, 0x67, 0x91, 0xc5, 0x23, 0x29, 0x76,
+	0xae, 0x29, 0xba, 0xc6, 0xe9, 0x43, 0x6f, 0x7a, 0x46, 0x7e, 0x02, 0x8d, 0xf8, 0xdc, 0xdb, 0xb0,
+	0x61, 0x6d, 0xe4, 0x4e, 0x35, 0xe3, 0x8b, 0x34, 0x0b, 0x25, 0xf7, 0xa1, 0x2a, 0x35, 0xd4, 0xba,
+	0x97, 0x5d, 0x3b, 0xa1, 0xf2, 0xc4, 0x3c, 0xb1, 0xdd, 0xa9, 0x7f, 0xca, 0x42, 0x6b, 0x16, 0xb5,
+	0xda, 0xe2, 0xb6, 0x24, 0x21, 0x1d, 0x44, 0xdc, 0x4f, 0xa3, 0x38, 0xf4, 0xbd, 0xe3, 0xd6, 0x26,
+	0xde, 0x93, 0xc8, 0xd1, 0xc5, 0xe0, 0xf7, 0x2e, 0x66, 0xfe, 0x7c, 0xf0, 0xfb, 0x1c, 0xee, 0x60,
+	0x65, 0x66, 0x3d, 0x3b, 0xb3, 0xf2, 0x68, 0x0d, 0xd1, 0x1b, 0xc8, 0xdd, 0x3d, 0x3b, 0xcc, 0x4e,
+	0x6a, 0x43, 0xcd, 0x71, 0xa3, 0xd8, 0xf5, 0xc6, 0x71, 0xab, 0x85, 0xef, 0x4c, 0xc7, 0xe4, 0x33,
+	0xb8, 0x3d, 0x73, 0x3d, 0x2b, 0xb2, 0x27, 0xcc, 0x8a, 0x5d, 0xee, 0x9b, 0x6c, 0xec, 0x7b, 0x4e,
+	0xd4, 0x7a, 0x80, 0x82, 0x93, 0x99, 0xeb, 0x8d, 0xec, 0x09, 0x33, 0xdd, 0x19, 0x1b, 0x09, 0x0e,
+	0xf9, 0x08, 0xd6, 0x11, 0x1e, 0xb2, 0x60, 0xea, 0x8e, 0x6d, 0xf1, 0xfa, 0x1f, 0xe1, 0xeb, 0xd7,
+	0x38, 0x83, 0x0a, 0x3a, 0xbe, 0xfa, 0x63, 0x68, 0x06, 0x2c, 0x8c, 0xdc, 0x28, 0xb6, 0xa4, 0x45,
+	0xbf, 0x97, 0xd5, 0xda, 0xaa, 0x64, 0x0e, 0x91, 0xd7, 0xfe, 0xcf, 0x02, 0x54, 0x84, 0x73, 0x92,
+	0x4f, 0x41, 0xf1, 0x03, 0xbc, 0x06, 0x69, 0x6e, 0x6f, 0x5e, 0xe2, 0xc1, 0x9d, 0x61, 0xc0, 0xeb,
+	0x5e, 0x3f, 0xa4, 0x8a, 0x1f, 0xdc, 0xb8, 0x28, 0xd4, 0xfe, 0x10, 0x6a, 0xc9, 0x02, 0xbc, 0xbc,
+	0xe8, 0xeb, 0xa3, 0x91, 0x65, 0x3e, 0xee, 0x0e, 0xd4, 0x02, 0xb9, 0x03, 0x24, 0x1d, 0x5a, 0x43,
+	0x6a, 0xe9, 0xdf, 0x1c, 0x75, 0xfb, 0xaa, 0x82, 0x5d, 0x1a, 0xd5, 0xbb, 0xa6, 0x4e, 0x05, 0xb2,
+	0x48, 0xee, 0xc1, 0xed, 0x2c, 0xe5, 0x1c, 0x5c, 0xc2, 0x14, 0x8c, 0x8f, 0x65, 0x52, 0x01, 0xc5,
+	0x18, 0xa8, 0x15, 0x9e, 0x16, 0xf4, 0xef, 0x8d, 0x91, 0x39, 0x52, 0xab, 0xed, 0xbf, 0x29, 0x40,
+	0x19, 0xc3, 0x06, 0x3f, 0x9f, 0x54, 0x72, 0x71, 0x5d, 0x73, 0x5e, 0xb9, 0x1a, 0xd9, 0x92, 0xaa,
+	0x81, 0x01, 0x65, 0x73, 0x79, 0xf4, 0xf9, 0xb5, 0xd6, 0x53, 0x3f, 0x85, 0x12, 0x8f, 0x52, 0xbc,
+	0x43, 0x1c, 0xd2, 0x9e, 0x4e, 0xad, 0x47, 0x06, 0x1d, 0xf1, 0x2a, 0x97, 0x40, 0xb3, 0x3b, 0xd8,
+	0xd3, 0x47, 0xe6, 0x30, 0xa1, 0xa1, 0x56, 0x1e, 0x19, 0x7d, 0x33, 0x45, 0x15, 0xb5, 0x9f, 0xd7,
+	0x60, 0x35, 0x89, 0x09, 0x22, 0x82, 0x3e, 0x82, 0x46, 0x10, 0xba, 0x33, 0x3b, 0x3c, 0x8b, 0xc6,
+	0xb6, 0x87, 0x49, 0x01, 0xb6, 0x7f, 0xb4, 0x24, 0xaa, 0x88, 0x1d, 0x1d, 0x0a, 0xec, 0x68, 0x6c,
+	0x7b, 0x34, 0x3b, 0x91, 0xf4, 0x61, 0x75, 0xc6, 0xc2, 0x63, 0xf6, 0x7b, 0xbe, 0xeb, 0xe1, 0x4a,
+	0x55, 0x8c, 0xc8, 0xef, 0x5f, 0xba, 0xd2, 0x01, 0x47, 0xff, 0x8e, 0xef, 0x7a, 0xb8, 0x56, 0x7e,
+	0x32, 0xf9, 0x04, 0xea, 0xa2, 0x12, 0x72, 0xd8, 0x04, 0x63, 0xc5, 0xb2, 0xda, 0x4f, 0xd4, 0xe8,
+	0x3d, 0x36, 0xc9, 0xc4, 0x65, 0xb8, 0x34, 0x2e, 0x37, 0xb2, 0x71, 0xf9, 0xcd, 0x6c, 0x2c, 0x5a,
+	0x11, 0x55, 0x78, 0x1a, 0x84, 0x2e, 0x38, 0x7c, 0x6b, 0x89, 0xc3, 0x77, 0x60, 0x23, 0xf1, 0x55,
+	0xcb, 0xf5, 0x26, 0xee, 0x4b, 0x2b, 0x72, 0x5f, 0x89, 0xd8, 0x53, 0xa6, 0xeb, 0x09, 0xcb, 0xe0,
+	0x9c, 0x91, 0xfb, 0x8a, 0x11, 0x23, 0xe9, 0xe0, 0x64, 0x0e, 0x5c, 0xc5, 0xab, 0xc9, 0xf7, 0x2e,
+	0x55, 0x8f, 0x68, 0xbe, 0x64, 0x46, 0xcc, 0x4d, 0x6d, 0xff, 0x52, 0x81, 0x46, 0xe6, 0x1c, 0x78,
+	0xf6, 0x16, 0xca, 0x42, 0x61, 0xc5, 0x55, 0x94, 0x50, 0x1f, 0x4a, 0xfa, 0x26, 0xd4, 0xa3, 0xd8,
+	0x0e, 0x63, 0x8b, 0x17, 0x57, 0xb2, 0xdd, 0x45, 0xc2, 0x13, 0x76, 0x46, 0x3e, 0x80, 0x35, 0xc1,
+	0x74, 0xbd, 0xf1, 0x74, 0x1e, 0xb9, 0xa7, 0xa2, 0x99, 0xaf, 0xd1, 0x26, 0x92, 0x8d, 0x84, 0x4a,
+	0xee, 0x42, 0x95, 0x67, 0x21, 0xbe, 0x86, 0x68, 0xfa, 0x2a, 0xcc, 0x73, 0xf8, 0x0a, 0x0f, 0x60,
+	0x95, 0x33, 0xce, 0xe7, 0x57, 0xc4, 0x2d, 0x33, 0xf3, 0x9c, 0xf3, 0xd9, 0x1d, 0xd8, 0x10, 0xaf,
+	0x09, 0x44, 0xf1, 0x2a, 0x2b, 0xdc, 0x3b, 0xa8, 0xd8, 0x75, 0x64, 0xc9, 0xb2, 0x56, 0x14, 0x9c,
+	0x1f, 0x01, 0xcf, 0x5e, 0x0b, 0xe8, 0xbb, 0x22, 0x94, 0x31, 0xcf, 0xc9, 0x61, 0x77, 0xe1, 0x1d,
+	0x8e, 0x9d, 0x7b, 0x76, 0x10, 0x4c, 0x5d, 0xe6, 0x58, 0x53, 0xff, 0x18, 0x43, 0x66, 0x14, 0xdb,
+	0xb3, 0xc0, 0x9a, 0x47, 0xad, 0x0d, 0x0c, 0x99, 0x6d, 0xe6, 0x39, 0x47, 0x09, 0xa8, 0xef, 0x1f,
+	0x9b, 0x09, 0xe4, 0x28, 0x6a, 0xff, 0x3e, 0xac, 0xe6, 0xec, 0x71, 0x41, 0xa7, 0x35, 0x74, 0xfe,
+	0x8c, 0x4e, 0xdf, 0x85, 0x95, 0x20, 0x64, 0xe7, 0xa2, 0xd5, 0x51, 0xb4, 0x86, 0xa0, 0x09, 0xb1,
+	0xb6, 0x60, 0x05, 0x79, 0x96, 0x20, 0xe6, 0xf3, 0x63, 0x03, 0x59, 0x87, 0xc8, 0x69, 0xbf, 0x80,
+	0x95, 0xec, 0x69, 0x93, 0x77, 0x33, 0x69, 0xa1, 0x99, 0xcb, 0x93, 0x69, 0x76, 0x48, 0x2a, 0xb2,
+	0xf5, 0x4b, 0x2a, 0x32, 0x72, 0x9d, 0x8a, 0x4c, 0xfb, 0x2f, 0xd9, 0x9c, 0x65, 0x2a, 0x84, 0x9f,
+	0x41, 0x2d, 0x90, 0xf5, 0x38, 0x5a, 0x52, 0xfe, 0x12, 0x3e, 0x0f, 0xee, 0x24, 0x95, 0x3b, 0x4d,
+	0xe7, 0xb4, 0xff, 0x56, 0x81, 0x5a, 0x5a, 0xd0, 0xe7, 0x2c, 0xef, 0xcd, 0x05, 0xcb, 0x3b, 0x90,
+	0x1a, 0x16, 0x0a, 0x7c, 0x1b, 0xa3, 0xc5, 0x27, 0xaf, 0x7f, 0xd7, 0xc5, 0xb6, 0xe7, 0x34, 0xdb,
+	0xf6, 0x6c, 0xbe, 0xae, 0xed, 0xf9, 0xe4, 0xa2, 0xc1, 0xbf, 0x95, 0xe9, 0x2d, 0x16, 0xcc, 0xbe,
+	0xfd, 0x7d, 0xae, 0x0f, 0xca, 0x26, 0x84, 0x77, 0xc4, 0x7e, 0xd2, 0x84, 0x90, 0xb6, 0x3f, 0xf7,
+	0xaf, 0xd7, 0xfe, 0x6c, 0x43, 0x45, 0xea, 0xfc, 0x0e, 0x54, 0x64, 0x4d, 0x27, 0x1b, 0x04, 0x31,
+	0x3a, 0x6f, 0x10, 0x0a, 0xb2, 0x4e, 0xd7, 0x7e, 0xae, 0x40, 0x59, 0x0f, 0x43, 0x3f, 0xd4, 0xfe,
+	0x48, 0x81, 0x3a, 0x3e, 0xed, 0xf9, 0x0e, 0xe3, 0xd9, 0x60, 0xb7, 0xdb, 0xb3, 0xa8, 0xfe, 0xcd,
+	0x91, 0x8e, 0xd9, 0xa0, 0x0d, 0x77, 0xf6, 0x86, 0x83, 0xbd, 0x23, 0x4a, 0xf5, 0x81, 0x69, 0x99,
+	0xb4, 0x3b, 0x18, 0xf1, 0xb6, 0x67, 0x38, 0x50, 0x15, 0x9e, 0x29, 0x8c, 0x81, 0xa9, 0xd3, 0x41,
+	0xb7, 0x6f, 0x89, 0x56, 0xb4, 0x88, 0x77, 0xb3, 0xba, 0xde, 0xb3, 0xf0, 0xd6, 0x51, 0x2d, 0xf1,
+	0x96, 0xd5, 0x34, 0x0e, 0xf4, 0xe1, 0x91, 0xa9, 0x96, 0xc9, 0x6d, 0x58, 0x3f, 0xd4, 0xe9, 0x81,
+	0x31, 0x1a, 0x19, 0xc3, 0x81, 0xd5, 0xd3, 0x07, 0x86, 0xde, 0x53, 0x2b, 0x7c, 0x9d, 0x5d, 0x63,
+	0xdf, 0xec, 0xee, 0xf6, 0x75, 0xb9, 0x4e, 0x95, 0x6c, 0xc2, 0x5b, 0x7b, 0xc3, 0x83, 0x03, 0xc3,
+	0x34, 0xf5, 0x9e, 0xb5, 0x7b, 0x64, 0x5a, 0x23, 0xd3, 0xe8, 0xf7, 0xad, 0xee, 0xe1, 0x61, 0xff,
+	0x29, 0x4f, 0x60, 0x35, 0x72, 0x17, 0x36, 0xf6, 0xba, 0x87, 0xdd, 0x5d, 0xa3, 0x6f, 0x98, 0x4f,
+	0xad, 0x9e, 0x31, 0xe2, 0xf3, 0x7b, 0x6a, 0x9d, 0x27, 0x6c, 0x93, 0x3e, 0xb5, 0xba, 0x7d, 0x14,
+	0xcd, 0xd4, 0xad, 0xdd, 0xee, 0xde, 0x13, 0x7d, 0xd0, 0x53, 0x81, 0x0b, 0x30, 0xea, 0x3e, 0xd2,
+	0x2d, 0x2e, 0x92, 0x65, 0x0e, 0x87, 0xd6, 0xb0, 0xdf, 0x53, 0x1b, 0xda, 0xbf, 0x14, 0xa1, 0xb4,
+	0xe7, 0x47, 0x31, 0xf7, 0x46, 0xe1, 0xac, 0x2f, 0x42, 0x37, 0x66, 0xa2, 0x7f, 0x2b, 0x53, 0xd1,
+	0x4b, 0x7f, 0x87, 0x24, 0x1e, 0x50, 0x32, 0x10, 0xeb, 0xd9, 0x19, 0xc7, 0x29, 0x88, 0x5b, 0x3b,
+	0xc7, 0xed, 0x72, 0xb2, 0x88, 0x68, 0x78, 0x85, 0x23, 0xd7, 0x2b, 0x22, 0x4e, 0x06, 0x61, 0xb9,
+	0xe0, 0xc7, 0x40, 0xb2, 0x20, 0xb9, 0x62, 0x09, 0x91, 0x6a, 0x06, 0x29, 0x96, 0xdc, 0x01, 0x18,
+	0xfb, 0xb3, 0x99, 0x1b, 0x8f, 0xfd, 0x28, 0x96, 0x5f, 0xc8, 0xda, 0x39, 0x63, 0x8f, 0x62, 0x6e,
+	0xf1, 0x33, 0x37, 0xe6, 0x8f, 0x34, 0x83, 0x26, 0x3b, 0x70, 0xcf, 0x0e, 0x82, 0xd0, 0x7f, 0xe9,
+	0xce, 0xec, 0x98, 0x59, 0xdc, 0x73, 0xed, 0x63, 0x66, 0x39, 0x6c, 0x1a, 0xdb, 0xd8, 0x13, 0x95,
+	0xe9, 0xdd, 0x0c, 0x60, 0x24, 0xf8, 0x3d, 0xce, 0xe6, 0x71, 0xd7, 0x75, 0xac, 0x88, 0xfd, 0x30,
+	0xe7, 0x1e, 0x60, 0xcd, 0x03, 0xc7, 0xe6, 0x62, 0xd6, 0x45, 0x96, 0x72, 0x9d, 0x91, 0xe4, 0x1c,
+	0x09, 0x46, 0xfb, 0x15, 0xc0, 0xb9, 0x14, 0x64, 0x1b, 0x6e, 0xf3, 0x3a, 0x9e, 0x45, 0x31, 0x73,
+	0x2c, 0xb9, 0xdb, 0x60, 0x1e, 0x47, 0x18, 0xe2, 0xcb, 0x74, 0x23, 0x65, 0xca, 0x9b, 0xc2, 0x79,
+	0x1c, 0x91, 0x9f, 0x40, 0xeb, 0xc2, 0x1c, 0x87, 0x4d, 0x19, 0x7f, 0x6d, 0x15, 0xa7, 0xdd, 0x59,
+	0x98, 0xd6, 0x13, 0x5c, 0xed, 0x4f, 0x14, 0x80, 0x7d, 0x16, 0x53, 0xc1, 0xcd, 0x34, 0xb6, 0x95,
+	0xeb, 0x36, 0xb6, 0xef, 0x27, 0x17, 0x08, 0xc5, 0xab, 0x63, 0xc0, 0x42, 0x97, 0xa1, 0xdc, 0xa4,
+	0xcb, 0xc8, 0x35, 0x11, 0xc5, 0x2b, 0x9a, 0x88, 0x52, 0xae, 0x89, 0xf8, 0x18, 0x9a, 0xf6, 0x74,
+	0xea, 0xbf, 0xe0, 0x05, 0x0d, 0x0b, 0x43, 0xe6, 0xa0, 0x11, 0x9c, 0xd7, 0xdb, 0xc8, 0xec, 0x49,
+	0x9e, 0xf6, 0xe7, 0x0a, 0x34, 0x50, 0x15, 0x51, 0xe0, 0x7b, 0x11, 0x23, 0x5f, 0x42, 0x45, 0x5e,
+	0x44, 0x8b, 0x8b, 0xfc, 0xb7, 0x33, 0xb2, 0x66, 0x70, 0xb2, 0x68, 0xa0, 0x12, 0xcc, 0x33, 0x42,
+	0xe6, 0x75, 0x97, 0x2b, 0x25, 0x45, 0x91, 0xfb, 0x50, 0x73, 0x3d, 0x4b, 0xb4, 0xd4, 0x95, 0x4c,
+	0x58, 0xac, 0xba, 0x1e, 0xd6, 0xb2, 0xed, 0x57, 0x50, 0x11, 0x2f, 0x21, 0x9d, 0x54, 0xa6, 0x8b,
+	0xfa, 0xcb, 0xdc, 0x1c, 0xa7, 0xc2, 0xc8, 0xc3, 0x29, 0xbd, 0x2e, 0x40, 0xb7, 0xa0, 0x7a, 0xca,
+	0x9b, 0x0f, 0xbc, 0xf4, 0xe3, 0xea, 0x4d, 0x86, 0xda, 0x1f, 0x97, 0x00, 0x0e, 0xe7, 0x4b, 0x0c,
+	0xa4, 0x71, 0x5d, 0x03, 0xe9, 0xe4, 0xf4, 0xf8, 0x7a, 0x99, 0x7f, 0x75, 0x43, 0x59, 0xd2, 0x69,
+	0x17, 0x6f, 0xda, 0x69, 0xdf, 0x87, 0x6a, 0x1c, 0xce, 0xb9, 0xa3, 0x08, 0x63, 0x4a, 0x5b, 0x5a,
+	0x49, 0x25, 0x6f, 0x42, 0x79, 0xe2, 0x87, 0x63, 0x86, 0x8e, 0x95, 0xb2, 0x05, 0xed, 0xc2, 0x65,
+	0x52, 0xed, 0xb2, 0xcb, 0x24, 0xde, 0xa0, 0x45, 0xf2, 0x1e, 0x0d, 0x0b, 0x99, 0x7c, 0x83, 0x96,
+	0x5c, 0xb1, 0xd1, 0x14, 0x44, 0xbe, 0x81, 0xa6, 0x3d, 0x8f, 0x7d, 0xcb, 0xe5, 0x15, 0xda, 0xd4,
+	0x1d, 0x9f, 0x61, 0xd9, 0xdd, 0xcc, 0x7f, 0xaf, 0x4f, 0x0f, 0xaa, 0xd3, 0x9d, 0xc7, 0xbe, 0xe1,
+	0x1c, 0x22, 0x72, 0xa7, 0x2a, 0x93, 0x12, 0x5d, 0xb1, 0x33, 0x64, 0xed, 0xc7, 0xb0, 0x92, 0x85,
+	0xf1, 0x04, 0x24, 0x81, 0xea, 0x1b, 0x3c, 0x3b, 0x8d, 0x78, 0x6a, 0x1b, 0x98, 0x46, 0xb7, 0xaf,
+	0x16, 0xb4, 0x18, 0x1a, 0xb8, 0xbc, 0xf4, 0x8e, 0xeb, 0xba, 0xfd, 0x03, 0x28, 0x61, 0xf8, 0x55,
+	0x2e, 0x7c, 0x0f, 0xc1, 0x98, 0x8b, 0xcc, 0xbc, 0xf9, 0x15, 0xb3, 0xe6, 0xf7, 0xdf, 0x05, 0x58,
+	0x31, 0xfd, 0xf9, 0xf8, 0xe4, 0xa2, 0x01, 0xc2, 0xaf, 0x3b, 0x42, 0x2d, 0x31, 0x1f, 0xe5, 0xa6,
+	0xe6, 0x93, 0x5a, 0x47, 0x71, 0x89, 0x75, 0xdc, 0xf4, 0xcc, 0xb5, 0x2f, 0x60, 0x55, 0x6e, 0x5e,
+	0x6a, 0x3d, 0xd1, 0x66, 0xe1, 0x0a, 0x6d, 0x6a, 0xbf, 0x50, 0x60, 0x55, 0xc4, 0xf7, 0xff, 0xbb,
+	0xd2, 0x2a, 0x37, 0x0c, 0xeb, 0xe5, 0x1b, 0x5d, 0x1e, 0xfd, 0xbf, 0xf4, 0x34, 0x6d, 0x08, 0xcd,
+	0x44, 0x7d, 0x37, 0x50, 0xfb, 0x15, 0x46, 0xfc, 0x8b, 0x02, 0x34, 0x06, 0xec, 0xe5, 0x92, 0x20,
+	0x5a, 0xbe, 0xee, 0x71, 0x7c, 0x98, 0x2b, 0x57, 0x1b, 0xdb, 0xeb, 0x59, 0x19, 0xc4, 0xd5, 0x63,
+	0x52, 0xc1, 0xa6, 0xb7, 0xa8, 0xca, 0xf2, 0x5b, 0xd4, 0xd2, 0x62, 0xb7, 0x9e, 0xb9, 0xc5, 0x2b,
+	0x2e, 0xbb, 0xc5, 0xd3, 0xfe, 0xad, 0x08, 0x0d, 0x6c, 0x90, 0x29, 0x8b, 0xe6, 0xd3, 0x38, 0x27,
+	0x4c, 0xe1, 0x6a, 0x61, 0x3a, 0x50, 0x09, 0x71, 0x92, 0x74, 0xa5, 0x4b, 0x83, 0xbf, 0x40, 0x61,
+	0x6b, 0xfc, 0xdc, 0x0d, 0x02, 0xe6, 0x58, 0x82, 0x92, 0x14, 0x30, 0x4d, 0x49, 0x16, 0x22, 0x44,
+	0xbc, 0xfc, 0x9c, 0xf9, 0x21, 0x4b, 0x51, 0x45, 0xbc, 0x4f, 0x68, 0x70, 0x5a, 0x02, 0xc9, 0xdd,
+	0x37, 0x88, 0xca, 0xe0, 0xfc, 0xbe, 0x21, 0xed, 0x35, 0x91, 0x5b, 0x47, 0xae, 0xe8, 0x35, 0x91,
+	0xcd, 0xbb, 0xa8, 0x99, 0x3d, 0x9d, 0x5a, 0x7e, 0x10, 0xa1, 0xd3, 0xd4, 0x68, 0x0d, 0x09, 0xc3,
+	0x20, 0x22, 0x5f, 0x43, 0x7a, 0x5d, 0x2c, 0x6f, 0xc9, 0xc5, 0x39, 0xb6, 0x2e, 0xbb, 0x58, 0xa0,
+	0xab, 0xe3, 0xdc, 0xfd, 0xcf, 0x92, 0x1b, 0xea, 0xca, 0x4d, 0x6f, 0xa8, 0x1f, 0x42, 0x59, 0xc4,
+	0xa8, 0xda, 0xeb, 0x62, 0x94, 0xc0, 0x65, 0xed, 0xb3, 0x91, 0xb7, 0xcf, 0x5f, 0x16, 0x80, 0x74,
+	0xa7, 0x53, 0x7f, 0x6c, 0xc7, 0xcc, 0x70, 0xa2, 0x8b, 0x66, 0x7a, 0xed, 0xcf, 0x2e, 0x9f, 0x41,
+	0x7d, 0xe6, 0x3b, 0x6c, 0x6a, 0x25, 0xdf, 0x94, 0x2e, 0xad, 0x7e, 0x10, 0xc6, 0x5b, 0x52, 0x02,
+	0x25, 0xbc, 0xc4, 0x51, 0xb0, 0xee, 0xc0, 0x67, 0xde, 0x84, 0xcd, 0xec, 0x97, 0xb2, 0x14, 0xe1,
+	0x8f, 0xa4, 0x03, 0xd5, 0x90, 0x45, 0x2c, 0x3c, 0x65, 0x57, 0x16, 0x55, 0x09, 0x48, 0x7b, 0x06,
+	0x1b, 0xb9, 0x1d, 0x49, 0x47, 0xbe, 0x85, 0x5f, 0x2b, 0xc3, 0x58, 0x7e, 0xb4, 0x12, 0x03, 0xfe,
+	0x3a, 0xe6, 0x25, 0x9f, 0x41, 0xf9, 0x63, 0xea, 0xf0, 0xc5, 0xab, 0xe2, 0xec, 0x1e, 0xa8, 0x59,
+	0x4d, 0xbb, 0x63, 0x0c, 0x36, 0xf2, 0x54, 0x0a, 0xd7, 0x3b, 0x15, 0xed, 0xef, 0x0a, 0xb0, 0xde,
+	0x75, 0x1c, 0xf1, 0x77, 0xc3, 0x25, 0xaa, 0x2f, 0x5e, 0x57, 0xf5, 0x0b, 0x81, 0x58, 0x84, 0x89,
+	0x6b, 0x05, 0xe2, 0x0f, 0xa1, 0x92, 0xd6, 0x5a, 0xc5, 0x05, 0x77, 0x16, 0x72, 0x51, 0x09, 0xd0,
+	0x6e, 0x01, 0xc9, 0x0a, 0x2b, 0xb4, 0xaa, 0xfd, 0x69, 0x11, 0xee, 0xee, 0xb2, 0x63, 0xd7, 0xcb,
+	0xbe, 0xe2, 0x57, 0xdf, 0xc9, 0xc5, 0x4f, 0x65, 0x9f, 0xc1, 0xba, 0x28, 0xe4, 0x93, 0x7f, 0x62,
+	0x59, 0xec, 0x58, 0x7e, 0x9d, 0x94, 0xb1, 0x6a, 0x0d, 0xf9, 0x07, 0x92, 0xad, 0xe3, 0x7f, 0xc5,
+	0x1c, 0x3b, 0xb6, 0x9f, 0xd9, 0x11, 0xb3, 0x5c, 0x47, 0xfe, 0x59, 0x06, 0x12, 0x92, 0xe1, 0x90,
+	0x21, 0x94, 0xb8, 0x0d, 0xa2, 0xeb, 0x36, 0xb7, 0xb7, 0x33, 0x62, 0x5d, 0xb2, 0x95, 0xac, 0x02,
+	0x0f, 0x7c, 0x87, 0xed, 0x54, 0x8f, 0x06, 0x4f, 0x06, 0xc3, 0xef, 0x06, 0x14, 0x17, 0x22, 0x06,
+	0xdc, 0x0a, 0x42, 0x76, 0xea, 0xfa, 0xf3, 0xc8, 0xca, 0x9e, 0x44, 0xf5, 0xca, 0x94, 0xb8, 0x91,
+	0xcc, 0xc9, 0x10, 0xb5, 0x9f, 0xc2, 0xda, 0xc2, 0xcb, 0x78, 0x6d, 0x26, 0x5f, 0xa7, 0xbe, 0x41,
+	0x56, 0xa1, 0x8e, 0x1f, 0xbb, 0x97, 0x7f, 0xfb, 0xd6, 0xfe, 0xb5, 0x80, 0x57, 0x4c, 0x33, 0x37,
+	0xbe, 0x59, 0x06, 0xfb, 0xcd, 0x7c, 0x06, 0x83, 0xed, 0x77, 0xf3, 0xe6, 0x9b, 0x59, 0xb0, 0xf3,
+	0xad, 0x00, 0xa6, 0x41, 0xa4, 0x6d, 0x43, 0x55, 0xd2, 0xc8, 0x6f, 0xc1, 0x5a, 0xe8, 0xfb, 0x71,
+	0xd2, 0x89, 0x8a, 0x0e, 0xe4, 0xf2, 0x3f, 0xdb, 0xac, 0x72, 0xb0, 0x48, 0x06, 0x4f, 0xf2, 0xbd,
+	0x48, 0x59, 0xfc, 0x0d, 0x44, 0x0e, 0x77, 0x1b, 0xbf, 0x5b, 0x4f, 0xff, 0xb7, 0xfb, 0xbf, 0x01,
+	0x00, 0x00, 0xff, 0xff, 0x35, 0x9f, 0x30, 0x98, 0xf2, 0x2b, 0x00, 0x00,
+}
diff --git a/vendor/google.golang.org/appengine/internal/datastore/datastore_v3.proto b/vendor/google.golang.org/appengine/internal/datastore/datastore_v3.proto
new file mode 100755
index 0000000..497b4d9
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/datastore/datastore_v3.proto
@@ -0,0 +1,551 @@
+syntax = "proto2";
+option go_package = "datastore";
+
+package appengine;
+
+message Action{}
+
+message PropertyValue {
+  optional int64 int64Value = 1;
+  optional bool booleanValue = 2;
+  optional string stringValue = 3;
+  optional double doubleValue = 4;
+
+  optional group PointValue = 5 {
+    required double x = 6;
+    required double y = 7;
+  }
+
+  optional group UserValue = 8 {
+    required string email = 9;
+    required string auth_domain = 10;
+    optional string nickname = 11;
+    optional string federated_identity = 21;
+    optional string federated_provider = 22;
+  }
+
+  optional group ReferenceValue = 12 {
+    required string app = 13;
+    optional string name_space = 20;
+    repeated group PathElement = 14 {
+      required string type = 15;
+      optional int64 id = 16;
+      optional string name = 17;
+    }
+  }
+}
+
+message Property {
+  enum Meaning {
+    NO_MEANING = 0;
+    BLOB = 14;
+    TEXT = 15;
+    BYTESTRING = 16;
+
+    ATOM_CATEGORY = 1;
+    ATOM_LINK = 2;
+    ATOM_TITLE = 3;
+    ATOM_CONTENT = 4;
+    ATOM_SUMMARY = 5;
+    ATOM_AUTHOR = 6;
+
+    GD_WHEN = 7;
+    GD_EMAIL = 8;
+    GEORSS_POINT = 9;
+    GD_IM = 10;
+
+    GD_PHONENUMBER = 11;
+    GD_POSTALADDRESS = 12;
+
+    GD_RATING = 13;
+
+    BLOBKEY = 17;
+    ENTITY_PROTO = 19;
+
+    INDEX_VALUE = 18;
+  };
+
+  optional Meaning meaning = 1 [default = NO_MEANING];
+  optional string meaning_uri = 2;
+
+  required string name = 3;
+
+  required PropertyValue value = 5;
+
+  required bool multiple = 4;
+
+  optional bool searchable = 6 [default=false];
+
+  enum FtsTokenizationOption {
+    HTML = 1;
+    ATOM = 2;
+  }
+
+  optional FtsTokenizationOption fts_tokenization_option = 8;
+
+  optional string locale = 9 [default = "en"];
+}
+
+message Path {
+  repeated group Element = 1 {
+    required string type = 2;
+    optional int64 id = 3;
+    optional string name = 4;
+  }
+}
+
+message Reference {
+  required string app = 13;
+  optional string name_space = 20;
+  required Path path = 14;
+}
+
+message User {
+  required string email = 1;
+  required string auth_domain = 2;
+  optional string nickname = 3;
+  optional string federated_identity = 6;
+  optional string federated_provider = 7;
+}
+
+message EntityProto {
+  required Reference key = 13;
+  required Path entity_group = 16;
+  optional User owner = 17;
+
+  enum Kind {
+    GD_CONTACT = 1;
+    GD_EVENT = 2;
+    GD_MESSAGE = 3;
+  }
+  optional Kind kind = 4;
+  optional string kind_uri = 5;
+
+  repeated Property property = 14;
+  repeated Property raw_property = 15;
+
+  optional int32 rank = 18;
+}
+
+message CompositeProperty {
+  required int64 index_id = 1;
+  repeated string value = 2;
+}
+
+message Index {
+  required string entity_type = 1;
+  required bool ancestor = 5;
+  repeated group Property = 2 {
+    required string name = 3;
+    enum Direction {
+      ASCENDING = 1;
+      DESCENDING = 2;
+    }
+    optional Direction direction = 4 [default = ASCENDING];
+  }
+}
+
+message CompositeIndex {
+  required string app_id = 1;
+  required int64 id = 2;
+  required Index definition = 3;
+
+  enum State {
+    WRITE_ONLY = 1;
+    READ_WRITE = 2;
+    DELETED = 3;
+    ERROR = 4;
+  }
+  required State state = 4;
+
+  optional bool only_use_if_required = 6 [default = false];
+}
+
+message IndexPostfix {
+  message IndexValue {
+    required string property_name = 1;
+    required PropertyValue value = 2;
+  }
+
+  repeated IndexValue index_value = 1;
+
+  optional Reference key = 2;
+
+  optional bool before = 3 [default=true];
+}
+
+message IndexPosition {
+  optional string key = 1;
+
+  optional bool before = 2 [default=true];
+}
+
+message Snapshot {
+  enum Status {
+    INACTIVE = 0;
+    ACTIVE = 1;
+  }
+
+  required int64 ts = 1;
+}
+
+message InternalHeader {
+  optional string qos = 1;
+}
+
+message Transaction {
+  optional InternalHeader header = 4;
+  required fixed64 handle = 1;
+  required string app = 2;
+  optional bool mark_changes = 3 [default = false];
+}
+
+message Query {
+  optional InternalHeader header = 39;
+
+  required string app = 1;
+  optional string name_space = 29;
+
+  optional string kind = 3;
+  optional Reference ancestor = 17;
+
+  repeated group Filter = 4 {
+    enum Operator {
+      LESS_THAN = 1;
+      LESS_THAN_OR_EQUAL = 2;
+      GREATER_THAN = 3;
+      GREATER_THAN_OR_EQUAL = 4;
+      EQUAL = 5;
+      IN = 6;
+      EXISTS = 7;
+    }
+
+    required Operator op = 6;
+    repeated Property property = 14;
+  }
+
+  optional string search_query = 8;
+
+  repeated group Order = 9 {
+    enum Direction {
+      ASCENDING = 1;
+      DESCENDING = 2;
+    }
+
+    required string property = 10;
+    optional Direction direction = 11 [default = ASCENDING];
+  }
+
+  enum Hint {
+    ORDER_FIRST = 1;
+    ANCESTOR_FIRST = 2;
+    FILTER_FIRST = 3;
+  }
+  optional Hint hint = 18;
+
+  optional int32 count = 23;
+
+  optional int32 offset = 12 [default = 0];
+
+  optional int32 limit = 16;
+
+  optional CompiledCursor compiled_cursor = 30;
+  optional CompiledCursor end_compiled_cursor = 31;
+
+  repeated CompositeIndex composite_index = 19;
+
+  optional bool require_perfect_plan = 20 [default = false];
+
+  optional bool keys_only = 21 [default = false];
+
+  optional Transaction transaction = 22;
+
+  optional bool compile = 25 [default = false];
+
+  optional int64 failover_ms = 26;
+
+  optional bool strong = 32;
+
+  repeated string property_name = 33;
+
+  repeated string group_by_property_name = 34;
+
+  optional bool distinct = 24;
+
+  optional int64 min_safe_time_seconds = 35;
+
+  repeated string safe_replica_name = 36;
+
+  optional bool persist_offset = 37 [default=false];
+}
+
+message CompiledQuery {
+  required group PrimaryScan = 1 {
+    optional string index_name = 2;
+
+    optional string start_key = 3;
+    optional bool start_inclusive = 4;
+    optional string end_key = 5;
+    optional bool end_inclusive = 6;
+
+    repeated string start_postfix_value = 22;
+    repeated string end_postfix_value = 23;
+
+    optional int64 end_unapplied_log_timestamp_us = 19;
+  }
+
+  repeated group MergeJoinScan = 7 {
+    required string index_name = 8;
+
+    repeated string prefix_value = 9;
+
+    optional bool value_prefix = 20 [default=false];
+  }
+
+  optional Index index_def = 21;
+
+  optional int32 offset = 10 [default = 0];
+
+  optional int32 limit = 11;
+
+  required bool keys_only = 12;
+
+  repeated string property_name = 24;
+
+  optional int32 distinct_infix_size = 25;
+
+  optional group EntityFilter = 13 {
+    optional bool distinct = 14 [default=false];
+
+    optional string kind = 17;
+    optional Reference ancestor = 18;
+  }
+}
+
+message CompiledCursor {
+  optional group Position = 2 {
+    optional string start_key = 27;
+
+    repeated group IndexValue = 29 {
+      optional string property = 30;
+      required PropertyValue value = 31;
+    }
+
+    optional Reference key = 32;
+
+    optional bool start_inclusive = 28 [default=true];
+  }
+}
+
+message Cursor {
+  required fixed64 cursor = 1;
+
+  optional string app = 2;
+}
+
+message Error {
+  enum ErrorCode {
+    BAD_REQUEST = 1;
+    CONCURRENT_TRANSACTION = 2;
+    INTERNAL_ERROR = 3;
+    NEED_INDEX = 4;
+    TIMEOUT = 5;
+    PERMISSION_DENIED = 6;
+    BIGTABLE_ERROR = 7;
+    COMMITTED_BUT_STILL_APPLYING = 8;
+    CAPABILITY_DISABLED = 9;
+    TRY_ALTERNATE_BACKEND = 10;
+    SAFE_TIME_TOO_OLD = 11;
+  }
+}
+
+message Cost {
+  optional int32 index_writes = 1;
+  optional int32 index_write_bytes = 2;
+  optional int32 entity_writes = 3;
+  optional int32 entity_write_bytes = 4;
+  optional group CommitCost = 5 {
+    optional int32 requested_entity_puts = 6;
+    optional int32 requested_entity_deletes = 7;
+  };
+  optional int32 approximate_storage_delta = 8;
+  optional int32 id_sequence_updates = 9;
+}
+
+message GetRequest {
+  optional InternalHeader header = 6;
+
+  repeated Reference key = 1;
+  optional Transaction transaction = 2;
+
+  optional int64 failover_ms = 3;
+
+  optional bool strong = 4;
+
+  optional bool allow_deferred = 5 [default=false];
+}
+
+message GetResponse {
+  repeated group Entity = 1 {
+    optional EntityProto entity = 2;
+    optional Reference key = 4;
+
+    optional int64 version = 3;
+  }
+
+  repeated Reference deferred = 5;
+
+  optional bool in_order = 6 [default=true];
+}
+
+message PutRequest {
+  optional InternalHeader header = 11;
+
+  repeated EntityProto entity = 1;
+  optional Transaction transaction = 2;
+  repeated CompositeIndex composite_index = 3;
+
+  optional bool trusted = 4 [default = false];
+
+  optional bool force = 7 [default = false];
+
+  optional bool mark_changes = 8 [default = false];
+  repeated Snapshot snapshot = 9;
+
+  enum AutoIdPolicy {
+    CURRENT = 0;
+    SEQUENTIAL = 1;
+  }
+  optional AutoIdPolicy auto_id_policy = 10 [default = CURRENT];
+}
+
+message PutResponse {
+  repeated Reference key = 1;
+  optional Cost cost = 2;
+  repeated int64 version = 3;
+}
+
+message TouchRequest {
+  optional InternalHeader header = 10;
+
+  repeated Reference key = 1;
+  repeated CompositeIndex composite_index = 2;
+  optional bool force = 3 [default = false];
+  repeated Snapshot snapshot = 9;
+}
+
+message TouchResponse {
+  optional Cost cost = 1;
+}
+
+message DeleteRequest {
+  optional InternalHeader header = 10;
+
+  repeated Reference key = 6;
+  optional Transaction transaction = 5;
+
+  optional bool trusted = 4 [default = false];
+
+  optional bool force = 7 [default = false];
+
+  optional bool mark_changes = 8 [default = false];
+  repeated Snapshot snapshot = 9;
+}
+
+message DeleteResponse {
+  optional Cost cost = 1;
+  repeated int64 version = 3;
+}
+
+message NextRequest {
+  optional InternalHeader header = 5;
+
+  required Cursor cursor = 1;
+  optional int32 count = 2;
+
+  optional int32 offset = 4 [default = 0];
+
+  optional bool compile = 3 [default = false];
+}
+
+message QueryResult {
+  optional Cursor cursor = 1;
+
+  repeated EntityProto result = 2;
+
+  optional int32 skipped_results = 7;
+
+  required bool more_results = 3;
+
+  optional bool keys_only = 4;
+
+  optional bool index_only = 9;
+
+  optional bool small_ops = 10;
+
+  optional CompiledQuery compiled_query = 5;
+
+  optional CompiledCursor compiled_cursor = 6;
+
+  repeated CompositeIndex index = 8;
+
+  repeated int64 version = 11;
+}
+
+message AllocateIdsRequest {
+  optional InternalHeader header = 4;
+
+  optional Reference model_key = 1;
+
+  optional int64 size = 2;
+
+  optional int64 max = 3;
+
+  repeated Reference reserve = 5;
+}
+
+message AllocateIdsResponse {
+  required int64 start = 1;
+  required int64 end = 2;
+  optional Cost cost = 3;
+}
+
+message CompositeIndices {
+  repeated CompositeIndex index = 1;
+}
+
+message AddActionsRequest {
+  optional InternalHeader header = 3;
+
+  required Transaction transaction = 1;
+  repeated Action action = 2;
+}
+
+message AddActionsResponse {
+}
+
+message BeginTransactionRequest {
+  optional InternalHeader header = 3;
+
+  required string app = 1;
+  optional bool allow_multiple_eg = 2 [default = false];
+  optional string database_id = 4;
+
+  enum TransactionMode {
+    UNKNOWN = 0;
+    READ_ONLY = 1;
+    READ_WRITE = 2;
+  }
+  optional TransactionMode mode = 5 [default = UNKNOWN];
+
+  optional Transaction previous_transaction = 7;
+}
+
+message CommitResponse {
+  optional Cost cost = 1;
+
+  repeated group Version = 3 {
+    required Reference root_entity_key = 4;
+    required int64 version = 5;
+  }
+}
diff --git a/vendor/google.golang.org/appengine/internal/identity.go b/vendor/google.golang.org/appengine/internal/identity.go
new file mode 100644
index 0000000..d538701
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/identity.go
@@ -0,0 +1,14 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package internal
+
+import netcontext "golang.org/x/net/context"
+
+// These functions are implementations of the wrapper functions
+// in ../appengine/identity.go. See that file for commentary.
+
+func AppID(c netcontext.Context) string {
+	return appID(FullyQualifiedAppID(c))
+}
diff --git a/vendor/google.golang.org/appengine/internal/identity_classic.go b/vendor/google.golang.org/appengine/internal/identity_classic.go
new file mode 100644
index 0000000..b59603f
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/identity_classic.go
@@ -0,0 +1,57 @@
+// Copyright 2015 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// +build appengine
+
+package internal
+
+import (
+	"appengine"
+
+	netcontext "golang.org/x/net/context"
+)
+
+func DefaultVersionHostname(ctx netcontext.Context) string {
+	c := fromContext(ctx)
+	if c == nil {
+		panic(errNotAppEngineContext)
+	}
+	return appengine.DefaultVersionHostname(c)
+}
+
+func Datacenter(_ netcontext.Context) string { return appengine.Datacenter() }
+func ServerSoftware() string                 { return appengine.ServerSoftware() }
+func InstanceID() string                     { return appengine.InstanceID() }
+func IsDevAppServer() bool                   { return appengine.IsDevAppServer() }
+
+func RequestID(ctx netcontext.Context) string {
+	c := fromContext(ctx)
+	if c == nil {
+		panic(errNotAppEngineContext)
+	}
+	return appengine.RequestID(c)
+}
+
+func ModuleName(ctx netcontext.Context) string {
+	c := fromContext(ctx)
+	if c == nil {
+		panic(errNotAppEngineContext)
+	}
+	return appengine.ModuleName(c)
+}
+func VersionID(ctx netcontext.Context) string {
+	c := fromContext(ctx)
+	if c == nil {
+		panic(errNotAppEngineContext)
+	}
+	return appengine.VersionID(c)
+}
+
+func fullyQualifiedAppID(ctx netcontext.Context) string {
+	c := fromContext(ctx)
+	if c == nil {
+		panic(errNotAppEngineContext)
+	}
+	return c.FullyQualifiedAppID()
+}
diff --git a/vendor/google.golang.org/appengine/internal/identity_vm.go b/vendor/google.golang.org/appengine/internal/identity_vm.go
new file mode 100644
index 0000000..d5fa75b
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/identity_vm.go
@@ -0,0 +1,101 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// +build !appengine
+
+package internal
+
+import (
+	"net/http"
+	"os"
+
+	netcontext "golang.org/x/net/context"
+)
+
+// These functions are implementations of the wrapper functions
+// in ../appengine/identity.go. See that file for commentary.
+
+const (
+	hDefaultVersionHostname = "X-AppEngine-Default-Version-Hostname"
+	hRequestLogId           = "X-AppEngine-Request-Log-Id"
+	hDatacenter             = "X-AppEngine-Datacenter"
+)
+
+func ctxHeaders(ctx netcontext.Context) http.Header {
+	c := fromContext(ctx)
+	if c == nil {
+		return nil
+	}
+	return c.Request().Header
+}
+
+func DefaultVersionHostname(ctx netcontext.Context) string {
+	return ctxHeaders(ctx).Get(hDefaultVersionHostname)
+}
+
+func RequestID(ctx netcontext.Context) string {
+	return ctxHeaders(ctx).Get(hRequestLogId)
+}
+
+func Datacenter(ctx netcontext.Context) string {
+	return ctxHeaders(ctx).Get(hDatacenter)
+}
+
+func ServerSoftware() string {
+	// TODO(dsymonds): Remove fallback when we've verified this.
+	if s := os.Getenv("SERVER_SOFTWARE"); s != "" {
+		return s
+	}
+	return "Google App Engine/1.x.x"
+}
+
+// TODO(dsymonds): Remove the metadata fetches.
+
+func ModuleName(_ netcontext.Context) string {
+	if s := os.Getenv("GAE_MODULE_NAME"); s != "" {
+		return s
+	}
+	return string(mustGetMetadata("instance/attributes/gae_backend_name"))
+}
+
+func VersionID(_ netcontext.Context) string {
+	if s1, s2 := os.Getenv("GAE_MODULE_VERSION"), os.Getenv("GAE_MINOR_VERSION"); s1 != "" && s2 != "" {
+		return s1 + "." + s2
+	}
+	return string(mustGetMetadata("instance/attributes/gae_backend_version")) + "." + string(mustGetMetadata("instance/attributes/gae_backend_minor_version"))
+}
+
+func InstanceID() string {
+	if s := os.Getenv("GAE_MODULE_INSTANCE"); s != "" {
+		return s
+	}
+	return string(mustGetMetadata("instance/attributes/gae_backend_instance"))
+}
+
+func partitionlessAppID() string {
+	// gae_project has everything except the partition prefix.
+	appID := os.Getenv("GAE_LONG_APP_ID")
+	if appID == "" {
+		appID = string(mustGetMetadata("instance/attributes/gae_project"))
+	}
+	return appID
+}
+
+func fullyQualifiedAppID(_ netcontext.Context) string {
+	appID := partitionlessAppID()
+
+	part := os.Getenv("GAE_PARTITION")
+	if part == "" {
+		part = string(mustGetMetadata("instance/attributes/gae_partition"))
+	}
+
+	if part != "" {
+		appID = part + "~" + appID
+	}
+	return appID
+}
+
+func IsDevAppServer() bool {
+	return os.Getenv("RUN_WITH_DEVAPPSERVER") != ""
+}
diff --git a/vendor/google.golang.org/appengine/internal/internal.go b/vendor/google.golang.org/appengine/internal/internal.go
new file mode 100644
index 0000000..051ea39
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/internal.go
@@ -0,0 +1,110 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// Package internal provides support for package appengine.
+//
+// Programs should not use this package directly. Its API is not stable.
+// Use packages appengine and appengine/* instead.
+package internal
+
+import (
+	"fmt"
+
+	"github.com/golang/protobuf/proto"
+
+	remotepb "google.golang.org/appengine/internal/remote_api"
+)
+
+// errorCodeMaps is a map of service name to the error code map for the service.
+var errorCodeMaps = make(map[string]map[int32]string)
+
+// RegisterErrorCodeMap is called from API implementations to register their
+// error code map. This should only be called from init functions.
+func RegisterErrorCodeMap(service string, m map[int32]string) {
+	errorCodeMaps[service] = m
+}
+
+type timeoutCodeKey struct {
+	service string
+	code    int32
+}
+
+// timeoutCodes is the set of service+code pairs that represent timeouts.
+var timeoutCodes = make(map[timeoutCodeKey]bool)
+
+func RegisterTimeoutErrorCode(service string, code int32) {
+	timeoutCodes[timeoutCodeKey{service, code}] = true
+}
+
+// APIError is the type returned by appengine.Context's Call method
+// when an API call fails in an API-specific way. This may be, for instance,
+// a taskqueue API call failing with TaskQueueServiceError::UNKNOWN_QUEUE.
+type APIError struct {
+	Service string
+	Detail  string
+	Code    int32 // API-specific error code
+}
+
+func (e *APIError) Error() string {
+	if e.Code == 0 {
+		if e.Detail == "" {
+			return "APIError <empty>"
+		}
+		return e.Detail
+	}
+	s := fmt.Sprintf("API error %d", e.Code)
+	if m, ok := errorCodeMaps[e.Service]; ok {
+		s += " (" + e.Service + ": " + m[e.Code] + ")"
+	} else {
+		// Shouldn't happen, but provide a bit more detail if it does.
+		s = e.Service + " " + s
+	}
+	if e.Detail != "" {
+		s += ": " + e.Detail
+	}
+	return s
+}
+
+func (e *APIError) IsTimeout() bool {
+	return timeoutCodes[timeoutCodeKey{e.Service, e.Code}]
+}
+
+// CallError is the type returned by appengine.Context's Call method when an
+// API call fails in a generic way, such as RpcError::CAPABILITY_DISABLED.
+type CallError struct {
+	Detail string
+	Code   int32
+	// TODO: Remove this if we get a distinguishable error code.
+	Timeout bool
+}
+
+func (e *CallError) Error() string {
+	var msg string
+	switch remotepb.RpcError_ErrorCode(e.Code) {
+	case remotepb.RpcError_UNKNOWN:
+		return e.Detail
+	case remotepb.RpcError_OVER_QUOTA:
+		msg = "Over quota"
+	case remotepb.RpcError_CAPABILITY_DISABLED:
+		msg = "Capability disabled"
+	case remotepb.RpcError_CANCELLED:
+		msg = "Canceled"
+	default:
+		msg = fmt.Sprintf("Call error %d", e.Code)
+	}
+	s := msg + ": " + e.Detail
+	if e.Timeout {
+		s += " (timeout)"
+	}
+	return s
+}
+
+func (e *CallError) IsTimeout() bool {
+	return e.Timeout
+}
+
+// NamespaceMods is a map from API service to a function that will mutate an RPC request to attach a namespace.
+// The function should be prepared to be called on the same message more than once; it should only modify the
+// RPC request the first time.
+var NamespaceMods = make(map[string]func(m proto.Message, namespace string))
diff --git a/vendor/google.golang.org/appengine/internal/log/log_service.pb.go b/vendor/google.golang.org/appengine/internal/log/log_service.pb.go
new file mode 100644
index 0000000..5549605
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/log/log_service.pb.go
@@ -0,0 +1,1039 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: google.golang.org/appengine/internal/log/log_service.proto
+
+/*
+Package log is a generated protocol buffer package.
+
+It is generated from these files:
+	google.golang.org/appengine/internal/log/log_service.proto
+
+It has these top-level messages:
+	LogServiceError
+	UserAppLogLine
+	UserAppLogGroup
+	FlushRequest
+	SetStatusRequest
+	LogOffset
+	LogLine
+	RequestLog
+	LogModuleVersion
+	LogReadRequest
+	LogReadResponse
+	LogUsageRecord
+	LogUsageRequest
+	LogUsageResponse
+*/
+package log
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+type LogServiceError_ErrorCode int32
+
+const (
+	LogServiceError_OK              LogServiceError_ErrorCode = 0
+	LogServiceError_INVALID_REQUEST LogServiceError_ErrorCode = 1
+	LogServiceError_STORAGE_ERROR   LogServiceError_ErrorCode = 2
+)
+
+var LogServiceError_ErrorCode_name = map[int32]string{
+	0: "OK",
+	1: "INVALID_REQUEST",
+	2: "STORAGE_ERROR",
+}
+var LogServiceError_ErrorCode_value = map[string]int32{
+	"OK":              0,
+	"INVALID_REQUEST": 1,
+	"STORAGE_ERROR":   2,
+}
+
+func (x LogServiceError_ErrorCode) Enum() *LogServiceError_ErrorCode {
+	p := new(LogServiceError_ErrorCode)
+	*p = x
+	return p
+}
+func (x LogServiceError_ErrorCode) String() string {
+	return proto.EnumName(LogServiceError_ErrorCode_name, int32(x))
+}
+func (x *LogServiceError_ErrorCode) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(LogServiceError_ErrorCode_value, data, "LogServiceError_ErrorCode")
+	if err != nil {
+		return err
+	}
+	*x = LogServiceError_ErrorCode(value)
+	return nil
+}
+func (LogServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} }
+
+type LogServiceError struct {
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *LogServiceError) Reset()                    { *m = LogServiceError{} }
+func (m *LogServiceError) String() string            { return proto.CompactTextString(m) }
+func (*LogServiceError) ProtoMessage()               {}
+func (*LogServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+type UserAppLogLine struct {
+	TimestampUsec    *int64  `protobuf:"varint,1,req,name=timestamp_usec,json=timestampUsec" json:"timestamp_usec,omitempty"`
+	Level            *int64  `protobuf:"varint,2,req,name=level" json:"level,omitempty"`
+	Message          *string `protobuf:"bytes,3,req,name=message" json:"message,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *UserAppLogLine) Reset()                    { *m = UserAppLogLine{} }
+func (m *UserAppLogLine) String() string            { return proto.CompactTextString(m) }
+func (*UserAppLogLine) ProtoMessage()               {}
+func (*UserAppLogLine) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+
+func (m *UserAppLogLine) GetTimestampUsec() int64 {
+	if m != nil && m.TimestampUsec != nil {
+		return *m.TimestampUsec
+	}
+	return 0
+}
+
+func (m *UserAppLogLine) GetLevel() int64 {
+	if m != nil && m.Level != nil {
+		return *m.Level
+	}
+	return 0
+}
+
+func (m *UserAppLogLine) GetMessage() string {
+	if m != nil && m.Message != nil {
+		return *m.Message
+	}
+	return ""
+}
+
+type UserAppLogGroup struct {
+	LogLine          []*UserAppLogLine `protobuf:"bytes,2,rep,name=log_line,json=logLine" json:"log_line,omitempty"`
+	XXX_unrecognized []byte            `json:"-"`
+}
+
+func (m *UserAppLogGroup) Reset()                    { *m = UserAppLogGroup{} }
+func (m *UserAppLogGroup) String() string            { return proto.CompactTextString(m) }
+func (*UserAppLogGroup) ProtoMessage()               {}
+func (*UserAppLogGroup) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+
+func (m *UserAppLogGroup) GetLogLine() []*UserAppLogLine {
+	if m != nil {
+		return m.LogLine
+	}
+	return nil
+}
+
+type FlushRequest struct {
+	Logs             []byte `protobuf:"bytes,1,opt,name=logs" json:"logs,omitempty"`
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *FlushRequest) Reset()                    { *m = FlushRequest{} }
+func (m *FlushRequest) String() string            { return proto.CompactTextString(m) }
+func (*FlushRequest) ProtoMessage()               {}
+func (*FlushRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
+
+func (m *FlushRequest) GetLogs() []byte {
+	if m != nil {
+		return m.Logs
+	}
+	return nil
+}
+
+type SetStatusRequest struct {
+	Status           *string `protobuf:"bytes,1,req,name=status" json:"status,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *SetStatusRequest) Reset()                    { *m = SetStatusRequest{} }
+func (m *SetStatusRequest) String() string            { return proto.CompactTextString(m) }
+func (*SetStatusRequest) ProtoMessage()               {}
+func (*SetStatusRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
+
+func (m *SetStatusRequest) GetStatus() string {
+	if m != nil && m.Status != nil {
+		return *m.Status
+	}
+	return ""
+}
+
+type LogOffset struct {
+	RequestId        []byte `protobuf:"bytes,1,opt,name=request_id,json=requestId" json:"request_id,omitempty"`
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *LogOffset) Reset()                    { *m = LogOffset{} }
+func (m *LogOffset) String() string            { return proto.CompactTextString(m) }
+func (*LogOffset) ProtoMessage()               {}
+func (*LogOffset) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
+
+func (m *LogOffset) GetRequestId() []byte {
+	if m != nil {
+		return m.RequestId
+	}
+	return nil
+}
+
+type LogLine struct {
+	Time             *int64  `protobuf:"varint,1,req,name=time" json:"time,omitempty"`
+	Level            *int32  `protobuf:"varint,2,req,name=level" json:"level,omitempty"`
+	LogMessage       *string `protobuf:"bytes,3,req,name=log_message,json=logMessage" json:"log_message,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *LogLine) Reset()                    { *m = LogLine{} }
+func (m *LogLine) String() string            { return proto.CompactTextString(m) }
+func (*LogLine) ProtoMessage()               {}
+func (*LogLine) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
+
+func (m *LogLine) GetTime() int64 {
+	if m != nil && m.Time != nil {
+		return *m.Time
+	}
+	return 0
+}
+
+func (m *LogLine) GetLevel() int32 {
+	if m != nil && m.Level != nil {
+		return *m.Level
+	}
+	return 0
+}
+
+func (m *LogLine) GetLogMessage() string {
+	if m != nil && m.LogMessage != nil {
+		return *m.LogMessage
+	}
+	return ""
+}
+
+type RequestLog struct {
+	AppId                   *string    `protobuf:"bytes,1,req,name=app_id,json=appId" json:"app_id,omitempty"`
+	ModuleId                *string    `protobuf:"bytes,37,opt,name=module_id,json=moduleId,def=default" json:"module_id,omitempty"`
+	VersionId               *string    `protobuf:"bytes,2,req,name=version_id,json=versionId" json:"version_id,omitempty"`
+	RequestId               []byte     `protobuf:"bytes,3,req,name=request_id,json=requestId" json:"request_id,omitempty"`
+	Offset                  *LogOffset `protobuf:"bytes,35,opt,name=offset" json:"offset,omitempty"`
+	Ip                      *string    `protobuf:"bytes,4,req,name=ip" json:"ip,omitempty"`
+	Nickname                *string    `protobuf:"bytes,5,opt,name=nickname" json:"nickname,omitempty"`
+	StartTime               *int64     `protobuf:"varint,6,req,name=start_time,json=startTime" json:"start_time,omitempty"`
+	EndTime                 *int64     `protobuf:"varint,7,req,name=end_time,json=endTime" json:"end_time,omitempty"`
+	Latency                 *int64     `protobuf:"varint,8,req,name=latency" json:"latency,omitempty"`
+	Mcycles                 *int64     `protobuf:"varint,9,req,name=mcycles" json:"mcycles,omitempty"`
+	Method                  *string    `protobuf:"bytes,10,req,name=method" json:"method,omitempty"`
+	Resource                *string    `protobuf:"bytes,11,req,name=resource" json:"resource,omitempty"`
+	HttpVersion             *string    `protobuf:"bytes,12,req,name=http_version,json=httpVersion" json:"http_version,omitempty"`
+	Status                  *int32     `protobuf:"varint,13,req,name=status" json:"status,omitempty"`
+	ResponseSize            *int64     `protobuf:"varint,14,req,name=response_size,json=responseSize" json:"response_size,omitempty"`
+	Referrer                *string    `protobuf:"bytes,15,opt,name=referrer" json:"referrer,omitempty"`
+	UserAgent               *string    `protobuf:"bytes,16,opt,name=user_agent,json=userAgent" json:"user_agent,omitempty"`
+	UrlMapEntry             *string    `protobuf:"bytes,17,req,name=url_map_entry,json=urlMapEntry" json:"url_map_entry,omitempty"`
+	Combined                *string    `protobuf:"bytes,18,req,name=combined" json:"combined,omitempty"`
+	ApiMcycles              *int64     `protobuf:"varint,19,opt,name=api_mcycles,json=apiMcycles" json:"api_mcycles,omitempty"`
+	Host                    *string    `protobuf:"bytes,20,opt,name=host" json:"host,omitempty"`
+	Cost                    *float64   `protobuf:"fixed64,21,opt,name=cost" json:"cost,omitempty"`
+	TaskQueueName           *string    `protobuf:"bytes,22,opt,name=task_queue_name,json=taskQueueName" json:"task_queue_name,omitempty"`
+	TaskName                *string    `protobuf:"bytes,23,opt,name=task_name,json=taskName" json:"task_name,omitempty"`
+	WasLoadingRequest       *bool      `protobuf:"varint,24,opt,name=was_loading_request,json=wasLoadingRequest" json:"was_loading_request,omitempty"`
+	PendingTime             *int64     `protobuf:"varint,25,opt,name=pending_time,json=pendingTime" json:"pending_time,omitempty"`
+	ReplicaIndex            *int32     `protobuf:"varint,26,opt,name=replica_index,json=replicaIndex,def=-1" json:"replica_index,omitempty"`
+	Finished                *bool      `protobuf:"varint,27,opt,name=finished,def=1" json:"finished,omitempty"`
+	CloneKey                []byte     `protobuf:"bytes,28,opt,name=clone_key,json=cloneKey" json:"clone_key,omitempty"`
+	Line                    []*LogLine `protobuf:"bytes,29,rep,name=line" json:"line,omitempty"`
+	LinesIncomplete         *bool      `protobuf:"varint,36,opt,name=lines_incomplete,json=linesIncomplete" json:"lines_incomplete,omitempty"`
+	AppEngineRelease        []byte     `protobuf:"bytes,38,opt,name=app_engine_release,json=appEngineRelease" json:"app_engine_release,omitempty"`
+	ExitReason              *int32     `protobuf:"varint,30,opt,name=exit_reason,json=exitReason" json:"exit_reason,omitempty"`
+	WasThrottledForTime     *bool      `protobuf:"varint,31,opt,name=was_throttled_for_time,json=wasThrottledForTime" json:"was_throttled_for_time,omitempty"`
+	WasThrottledForRequests *bool      `protobuf:"varint,32,opt,name=was_throttled_for_requests,json=wasThrottledForRequests" json:"was_throttled_for_requests,omitempty"`
+	ThrottledTime           *int64     `protobuf:"varint,33,opt,name=throttled_time,json=throttledTime" json:"throttled_time,omitempty"`
+	ServerName              []byte     `protobuf:"bytes,34,opt,name=server_name,json=serverName" json:"server_name,omitempty"`
+	XXX_unrecognized        []byte     `json:"-"`
+}
+
+func (m *RequestLog) Reset()                    { *m = RequestLog{} }
+func (m *RequestLog) String() string            { return proto.CompactTextString(m) }
+func (*RequestLog) ProtoMessage()               {}
+func (*RequestLog) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
+
+const Default_RequestLog_ModuleId string = "default"
+const Default_RequestLog_ReplicaIndex int32 = -1
+const Default_RequestLog_Finished bool = true
+
+func (m *RequestLog) GetAppId() string {
+	if m != nil && m.AppId != nil {
+		return *m.AppId
+	}
+	return ""
+}
+
+func (m *RequestLog) GetModuleId() string {
+	if m != nil && m.ModuleId != nil {
+		return *m.ModuleId
+	}
+	return Default_RequestLog_ModuleId
+}
+
+func (m *RequestLog) GetVersionId() string {
+	if m != nil && m.VersionId != nil {
+		return *m.VersionId
+	}
+	return ""
+}
+
+func (m *RequestLog) GetRequestId() []byte {
+	if m != nil {
+		return m.RequestId
+	}
+	return nil
+}
+
+func (m *RequestLog) GetOffset() *LogOffset {
+	if m != nil {
+		return m.Offset
+	}
+	return nil
+}
+
+func (m *RequestLog) GetIp() string {
+	if m != nil && m.Ip != nil {
+		return *m.Ip
+	}
+	return ""
+}
+
+func (m *RequestLog) GetNickname() string {
+	if m != nil && m.Nickname != nil {
+		return *m.Nickname
+	}
+	return ""
+}
+
+func (m *RequestLog) GetStartTime() int64 {
+	if m != nil && m.StartTime != nil {
+		return *m.StartTime
+	}
+	return 0
+}
+
+func (m *RequestLog) GetEndTime() int64 {
+	if m != nil && m.EndTime != nil {
+		return *m.EndTime
+	}
+	return 0
+}
+
+func (m *RequestLog) GetLatency() int64 {
+	if m != nil && m.Latency != nil {
+		return *m.Latency
+	}
+	return 0
+}
+
+func (m *RequestLog) GetMcycles() int64 {
+	if m != nil && m.Mcycles != nil {
+		return *m.Mcycles
+	}
+	return 0
+}
+
+func (m *RequestLog) GetMethod() string {
+	if m != nil && m.Method != nil {
+		return *m.Method
+	}
+	return ""
+}
+
+func (m *RequestLog) GetResource() string {
+	if m != nil && m.Resource != nil {
+		return *m.Resource
+	}
+	return ""
+}
+
+func (m *RequestLog) GetHttpVersion() string {
+	if m != nil && m.HttpVersion != nil {
+		return *m.HttpVersion
+	}
+	return ""
+}
+
+func (m *RequestLog) GetStatus() int32 {
+	if m != nil && m.Status != nil {
+		return *m.Status
+	}
+	return 0
+}
+
+func (m *RequestLog) GetResponseSize() int64 {
+	if m != nil && m.ResponseSize != nil {
+		return *m.ResponseSize
+	}
+	return 0
+}
+
+func (m *RequestLog) GetReferrer() string {
+	if m != nil && m.Referrer != nil {
+		return *m.Referrer
+	}
+	return ""
+}
+
+func (m *RequestLog) GetUserAgent() string {
+	if m != nil && m.UserAgent != nil {
+		return *m.UserAgent
+	}
+	return ""
+}
+
+func (m *RequestLog) GetUrlMapEntry() string {
+	if m != nil && m.UrlMapEntry != nil {
+		return *m.UrlMapEntry
+	}
+	return ""
+}
+
+func (m *RequestLog) GetCombined() string {
+	if m != nil && m.Combined != nil {
+		return *m.Combined
+	}
+	return ""
+}
+
+func (m *RequestLog) GetApiMcycles() int64 {
+	if m != nil && m.ApiMcycles != nil {
+		return *m.ApiMcycles
+	}
+	return 0
+}
+
+func (m *RequestLog) GetHost() string {
+	if m != nil && m.Host != nil {
+		return *m.Host
+	}
+	return ""
+}
+
+func (m *RequestLog) GetCost() float64 {
+	if m != nil && m.Cost != nil {
+		return *m.Cost
+	}
+	return 0
+}
+
+func (m *RequestLog) GetTaskQueueName() string {
+	if m != nil && m.TaskQueueName != nil {
+		return *m.TaskQueueName
+	}
+	return ""
+}
+
+func (m *RequestLog) GetTaskName() string {
+	if m != nil && m.TaskName != nil {
+		return *m.TaskName
+	}
+	return ""
+}
+
+func (m *RequestLog) GetWasLoadingRequest() bool {
+	if m != nil && m.WasLoadingRequest != nil {
+		return *m.WasLoadingRequest
+	}
+	return false
+}
+
+func (m *RequestLog) GetPendingTime() int64 {
+	if m != nil && m.PendingTime != nil {
+		return *m.PendingTime
+	}
+	return 0
+}
+
+func (m *RequestLog) GetReplicaIndex() int32 {
+	if m != nil && m.ReplicaIndex != nil {
+		return *m.ReplicaIndex
+	}
+	return Default_RequestLog_ReplicaIndex
+}
+
+func (m *RequestLog) GetFinished() bool {
+	if m != nil && m.Finished != nil {
+		return *m.Finished
+	}
+	return Default_RequestLog_Finished
+}
+
+func (m *RequestLog) GetCloneKey() []byte {
+	if m != nil {
+		return m.CloneKey
+	}
+	return nil
+}
+
+func (m *RequestLog) GetLine() []*LogLine {
+	if m != nil {
+		return m.Line
+	}
+	return nil
+}
+
+func (m *RequestLog) GetLinesIncomplete() bool {
+	if m != nil && m.LinesIncomplete != nil {
+		return *m.LinesIncomplete
+	}
+	return false
+}
+
+func (m *RequestLog) GetAppEngineRelease() []byte {
+	if m != nil {
+		return m.AppEngineRelease
+	}
+	return nil
+}
+
+func (m *RequestLog) GetExitReason() int32 {
+	if m != nil && m.ExitReason != nil {
+		return *m.ExitReason
+	}
+	return 0
+}
+
+func (m *RequestLog) GetWasThrottledForTime() bool {
+	if m != nil && m.WasThrottledForTime != nil {
+		return *m.WasThrottledForTime
+	}
+	return false
+}
+
+func (m *RequestLog) GetWasThrottledForRequests() bool {
+	if m != nil && m.WasThrottledForRequests != nil {
+		return *m.WasThrottledForRequests
+	}
+	return false
+}
+
+func (m *RequestLog) GetThrottledTime() int64 {
+	if m != nil && m.ThrottledTime != nil {
+		return *m.ThrottledTime
+	}
+	return 0
+}
+
+func (m *RequestLog) GetServerName() []byte {
+	if m != nil {
+		return m.ServerName
+	}
+	return nil
+}
+
+type LogModuleVersion struct {
+	ModuleId         *string `protobuf:"bytes,1,opt,name=module_id,json=moduleId,def=default" json:"module_id,omitempty"`
+	VersionId        *string `protobuf:"bytes,2,opt,name=version_id,json=versionId" json:"version_id,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *LogModuleVersion) Reset()                    { *m = LogModuleVersion{} }
+func (m *LogModuleVersion) String() string            { return proto.CompactTextString(m) }
+func (*LogModuleVersion) ProtoMessage()               {}
+func (*LogModuleVersion) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
+
+const Default_LogModuleVersion_ModuleId string = "default"
+
+func (m *LogModuleVersion) GetModuleId() string {
+	if m != nil && m.ModuleId != nil {
+		return *m.ModuleId
+	}
+	return Default_LogModuleVersion_ModuleId
+}
+
+func (m *LogModuleVersion) GetVersionId() string {
+	if m != nil && m.VersionId != nil {
+		return *m.VersionId
+	}
+	return ""
+}
+
+type LogReadRequest struct {
+	AppId             *string             `protobuf:"bytes,1,req,name=app_id,json=appId" json:"app_id,omitempty"`
+	VersionId         []string            `protobuf:"bytes,2,rep,name=version_id,json=versionId" json:"version_id,omitempty"`
+	ModuleVersion     []*LogModuleVersion `protobuf:"bytes,19,rep,name=module_version,json=moduleVersion" json:"module_version,omitempty"`
+	StartTime         *int64              `protobuf:"varint,3,opt,name=start_time,json=startTime" json:"start_time,omitempty"`
+	EndTime           *int64              `protobuf:"varint,4,opt,name=end_time,json=endTime" json:"end_time,omitempty"`
+	Offset            *LogOffset          `protobuf:"bytes,5,opt,name=offset" json:"offset,omitempty"`
+	RequestId         [][]byte            `protobuf:"bytes,6,rep,name=request_id,json=requestId" json:"request_id,omitempty"`
+	MinimumLogLevel   *int32              `protobuf:"varint,7,opt,name=minimum_log_level,json=minimumLogLevel" json:"minimum_log_level,omitempty"`
+	IncludeIncomplete *bool               `protobuf:"varint,8,opt,name=include_incomplete,json=includeIncomplete" json:"include_incomplete,omitempty"`
+	Count             *int64              `protobuf:"varint,9,opt,name=count" json:"count,omitempty"`
+	CombinedLogRegex  *string             `protobuf:"bytes,14,opt,name=combined_log_regex,json=combinedLogRegex" json:"combined_log_regex,omitempty"`
+	HostRegex         *string             `protobuf:"bytes,15,opt,name=host_regex,json=hostRegex" json:"host_regex,omitempty"`
+	ReplicaIndex      *int32              `protobuf:"varint,16,opt,name=replica_index,json=replicaIndex" json:"replica_index,omitempty"`
+	IncludeAppLogs    *bool               `protobuf:"varint,10,opt,name=include_app_logs,json=includeAppLogs" json:"include_app_logs,omitempty"`
+	AppLogsPerRequest *int32              `protobuf:"varint,17,opt,name=app_logs_per_request,json=appLogsPerRequest" json:"app_logs_per_request,omitempty"`
+	IncludeHost       *bool               `protobuf:"varint,11,opt,name=include_host,json=includeHost" json:"include_host,omitempty"`
+	IncludeAll        *bool               `protobuf:"varint,12,opt,name=include_all,json=includeAll" json:"include_all,omitempty"`
+	CacheIterator     *bool               `protobuf:"varint,13,opt,name=cache_iterator,json=cacheIterator" json:"cache_iterator,omitempty"`
+	NumShards         *int32              `protobuf:"varint,18,opt,name=num_shards,json=numShards" json:"num_shards,omitempty"`
+	XXX_unrecognized  []byte              `json:"-"`
+}
+
+func (m *LogReadRequest) Reset()                    { *m = LogReadRequest{} }
+func (m *LogReadRequest) String() string            { return proto.CompactTextString(m) }
+func (*LogReadRequest) ProtoMessage()               {}
+func (*LogReadRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} }
+
+func (m *LogReadRequest) GetAppId() string {
+	if m != nil && m.AppId != nil {
+		return *m.AppId
+	}
+	return ""
+}
+
+func (m *LogReadRequest) GetVersionId() []string {
+	if m != nil {
+		return m.VersionId
+	}
+	return nil
+}
+
+func (m *LogReadRequest) GetModuleVersion() []*LogModuleVersion {
+	if m != nil {
+		return m.ModuleVersion
+	}
+	return nil
+}
+
+func (m *LogReadRequest) GetStartTime() int64 {
+	if m != nil && m.StartTime != nil {
+		return *m.StartTime
+	}
+	return 0
+}
+
+func (m *LogReadRequest) GetEndTime() int64 {
+	if m != nil && m.EndTime != nil {
+		return *m.EndTime
+	}
+	return 0
+}
+
+func (m *LogReadRequest) GetOffset() *LogOffset {
+	if m != nil {
+		return m.Offset
+	}
+	return nil
+}
+
+func (m *LogReadRequest) GetRequestId() [][]byte {
+	if m != nil {
+		return m.RequestId
+	}
+	return nil
+}
+
+func (m *LogReadRequest) GetMinimumLogLevel() int32 {
+	if m != nil && m.MinimumLogLevel != nil {
+		return *m.MinimumLogLevel
+	}
+	return 0
+}
+
+func (m *LogReadRequest) GetIncludeIncomplete() bool {
+	if m != nil && m.IncludeIncomplete != nil {
+		return *m.IncludeIncomplete
+	}
+	return false
+}
+
+func (m *LogReadRequest) GetCount() int64 {
+	if m != nil && m.Count != nil {
+		return *m.Count
+	}
+	return 0
+}
+
+func (m *LogReadRequest) GetCombinedLogRegex() string {
+	if m != nil && m.CombinedLogRegex != nil {
+		return *m.CombinedLogRegex
+	}
+	return ""
+}
+
+func (m *LogReadRequest) GetHostRegex() string {
+	if m != nil && m.HostRegex != nil {
+		return *m.HostRegex
+	}
+	return ""
+}
+
+func (m *LogReadRequest) GetReplicaIndex() int32 {
+	if m != nil && m.ReplicaIndex != nil {
+		return *m.ReplicaIndex
+	}
+	return 0
+}
+
+func (m *LogReadRequest) GetIncludeAppLogs() bool {
+	if m != nil && m.IncludeAppLogs != nil {
+		return *m.IncludeAppLogs
+	}
+	return false
+}
+
+func (m *LogReadRequest) GetAppLogsPerRequest() int32 {
+	if m != nil && m.AppLogsPerRequest != nil {
+		return *m.AppLogsPerRequest
+	}
+	return 0
+}
+
+func (m *LogReadRequest) GetIncludeHost() bool {
+	if m != nil && m.IncludeHost != nil {
+		return *m.IncludeHost
+	}
+	return false
+}
+
+func (m *LogReadRequest) GetIncludeAll() bool {
+	if m != nil && m.IncludeAll != nil {
+		return *m.IncludeAll
+	}
+	return false
+}
+
+func (m *LogReadRequest) GetCacheIterator() bool {
+	if m != nil && m.CacheIterator != nil {
+		return *m.CacheIterator
+	}
+	return false
+}
+
+func (m *LogReadRequest) GetNumShards() int32 {
+	if m != nil && m.NumShards != nil {
+		return *m.NumShards
+	}
+	return 0
+}
+
+type LogReadResponse struct {
+	Log              []*RequestLog `protobuf:"bytes,1,rep,name=log" json:"log,omitempty"`
+	Offset           *LogOffset    `protobuf:"bytes,2,opt,name=offset" json:"offset,omitempty"`
+	LastEndTime      *int64        `protobuf:"varint,3,opt,name=last_end_time,json=lastEndTime" json:"last_end_time,omitempty"`
+	XXX_unrecognized []byte        `json:"-"`
+}
+
+func (m *LogReadResponse) Reset()                    { *m = LogReadResponse{} }
+func (m *LogReadResponse) String() string            { return proto.CompactTextString(m) }
+func (*LogReadResponse) ProtoMessage()               {}
+func (*LogReadResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} }
+
+func (m *LogReadResponse) GetLog() []*RequestLog {
+	if m != nil {
+		return m.Log
+	}
+	return nil
+}
+
+func (m *LogReadResponse) GetOffset() *LogOffset {
+	if m != nil {
+		return m.Offset
+	}
+	return nil
+}
+
+func (m *LogReadResponse) GetLastEndTime() int64 {
+	if m != nil && m.LastEndTime != nil {
+		return *m.LastEndTime
+	}
+	return 0
+}
+
+type LogUsageRecord struct {
+	VersionId        *string `protobuf:"bytes,1,opt,name=version_id,json=versionId" json:"version_id,omitempty"`
+	StartTime        *int32  `protobuf:"varint,2,opt,name=start_time,json=startTime" json:"start_time,omitempty"`
+	EndTime          *int32  `protobuf:"varint,3,opt,name=end_time,json=endTime" json:"end_time,omitempty"`
+	Count            *int64  `protobuf:"varint,4,opt,name=count" json:"count,omitempty"`
+	TotalSize        *int64  `protobuf:"varint,5,opt,name=total_size,json=totalSize" json:"total_size,omitempty"`
+	Records          *int32  `protobuf:"varint,6,opt,name=records" json:"records,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *LogUsageRecord) Reset()                    { *m = LogUsageRecord{} }
+func (m *LogUsageRecord) String() string            { return proto.CompactTextString(m) }
+func (*LogUsageRecord) ProtoMessage()               {}
+func (*LogUsageRecord) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
+
+func (m *LogUsageRecord) GetVersionId() string {
+	if m != nil && m.VersionId != nil {
+		return *m.VersionId
+	}
+	return ""
+}
+
+func (m *LogUsageRecord) GetStartTime() int32 {
+	if m != nil && m.StartTime != nil {
+		return *m.StartTime
+	}
+	return 0
+}
+
+func (m *LogUsageRecord) GetEndTime() int32 {
+	if m != nil && m.EndTime != nil {
+		return *m.EndTime
+	}
+	return 0
+}
+
+func (m *LogUsageRecord) GetCount() int64 {
+	if m != nil && m.Count != nil {
+		return *m.Count
+	}
+	return 0
+}
+
+func (m *LogUsageRecord) GetTotalSize() int64 {
+	if m != nil && m.TotalSize != nil {
+		return *m.TotalSize
+	}
+	return 0
+}
+
+func (m *LogUsageRecord) GetRecords() int32 {
+	if m != nil && m.Records != nil {
+		return *m.Records
+	}
+	return 0
+}
+
+type LogUsageRequest struct {
+	AppId            *string  `protobuf:"bytes,1,req,name=app_id,json=appId" json:"app_id,omitempty"`
+	VersionId        []string `protobuf:"bytes,2,rep,name=version_id,json=versionId" json:"version_id,omitempty"`
+	StartTime        *int32   `protobuf:"varint,3,opt,name=start_time,json=startTime" json:"start_time,omitempty"`
+	EndTime          *int32   `protobuf:"varint,4,opt,name=end_time,json=endTime" json:"end_time,omitempty"`
+	ResolutionHours  *uint32  `protobuf:"varint,5,opt,name=resolution_hours,json=resolutionHours,def=1" json:"resolution_hours,omitempty"`
+	CombineVersions  *bool    `protobuf:"varint,6,opt,name=combine_versions,json=combineVersions" json:"combine_versions,omitempty"`
+	UsageVersion     *int32   `protobuf:"varint,7,opt,name=usage_version,json=usageVersion" json:"usage_version,omitempty"`
+	VersionsOnly     *bool    `protobuf:"varint,8,opt,name=versions_only,json=versionsOnly" json:"versions_only,omitempty"`
+	XXX_unrecognized []byte   `json:"-"`
+}
+
+func (m *LogUsageRequest) Reset()                    { *m = LogUsageRequest{} }
+func (m *LogUsageRequest) String() string            { return proto.CompactTextString(m) }
+func (*LogUsageRequest) ProtoMessage()               {}
+func (*LogUsageRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} }
+
+const Default_LogUsageRequest_ResolutionHours uint32 = 1
+
+func (m *LogUsageRequest) GetAppId() string {
+	if m != nil && m.AppId != nil {
+		return *m.AppId
+	}
+	return ""
+}
+
+func (m *LogUsageRequest) GetVersionId() []string {
+	if m != nil {
+		return m.VersionId
+	}
+	return nil
+}
+
+func (m *LogUsageRequest) GetStartTime() int32 {
+	if m != nil && m.StartTime != nil {
+		return *m.StartTime
+	}
+	return 0
+}
+
+func (m *LogUsageRequest) GetEndTime() int32 {
+	if m != nil && m.EndTime != nil {
+		return *m.EndTime
+	}
+	return 0
+}
+
+func (m *LogUsageRequest) GetResolutionHours() uint32 {
+	if m != nil && m.ResolutionHours != nil {
+		return *m.ResolutionHours
+	}
+	return Default_LogUsageRequest_ResolutionHours
+}
+
+func (m *LogUsageRequest) GetCombineVersions() bool {
+	if m != nil && m.CombineVersions != nil {
+		return *m.CombineVersions
+	}
+	return false
+}
+
+func (m *LogUsageRequest) GetUsageVersion() int32 {
+	if m != nil && m.UsageVersion != nil {
+		return *m.UsageVersion
+	}
+	return 0
+}
+
+func (m *LogUsageRequest) GetVersionsOnly() bool {
+	if m != nil && m.VersionsOnly != nil {
+		return *m.VersionsOnly
+	}
+	return false
+}
+
+type LogUsageResponse struct {
+	Usage            []*LogUsageRecord `protobuf:"bytes,1,rep,name=usage" json:"usage,omitempty"`
+	Summary          *LogUsageRecord   `protobuf:"bytes,2,opt,name=summary" json:"summary,omitempty"`
+	XXX_unrecognized []byte            `json:"-"`
+}
+
+func (m *LogUsageResponse) Reset()                    { *m = LogUsageResponse{} }
+func (m *LogUsageResponse) String() string            { return proto.CompactTextString(m) }
+func (*LogUsageResponse) ProtoMessage()               {}
+func (*LogUsageResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} }
+
+func (m *LogUsageResponse) GetUsage() []*LogUsageRecord {
+	if m != nil {
+		return m.Usage
+	}
+	return nil
+}
+
+func (m *LogUsageResponse) GetSummary() *LogUsageRecord {
+	if m != nil {
+		return m.Summary
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*LogServiceError)(nil), "appengine.LogServiceError")
+	proto.RegisterType((*UserAppLogLine)(nil), "appengine.UserAppLogLine")
+	proto.RegisterType((*UserAppLogGroup)(nil), "appengine.UserAppLogGroup")
+	proto.RegisterType((*FlushRequest)(nil), "appengine.FlushRequest")
+	proto.RegisterType((*SetStatusRequest)(nil), "appengine.SetStatusRequest")
+	proto.RegisterType((*LogOffset)(nil), "appengine.LogOffset")
+	proto.RegisterType((*LogLine)(nil), "appengine.LogLine")
+	proto.RegisterType((*RequestLog)(nil), "appengine.RequestLog")
+	proto.RegisterType((*LogModuleVersion)(nil), "appengine.LogModuleVersion")
+	proto.RegisterType((*LogReadRequest)(nil), "appengine.LogReadRequest")
+	proto.RegisterType((*LogReadResponse)(nil), "appengine.LogReadResponse")
+	proto.RegisterType((*LogUsageRecord)(nil), "appengine.LogUsageRecord")
+	proto.RegisterType((*LogUsageRequest)(nil), "appengine.LogUsageRequest")
+	proto.RegisterType((*LogUsageResponse)(nil), "appengine.LogUsageResponse")
+}
+
+func init() {
+	proto.RegisterFile("google.golang.org/appengine/internal/log/log_service.proto", fileDescriptor0)
+}
+
+var fileDescriptor0 = []byte{
+	// 1553 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xdd, 0x72, 0xdb, 0xc6,
+	0x15, 0x2e, 0x48, 0x51, 0x24, 0x0f, 0x49, 0x91, 0x5a, 0xcb, 0xce, 0xda, 0xae, 0x6b, 0x1a, 0x4e,
+	0x1c, 0xd6, 0x93, 0x48, 0x93, 0xa4, 0x57, 0xca, 0x95, 0xd3, 0x2a, 0x8e, 0x26, 0xb4, 0xd5, 0x40,
+	0x72, 0x3a, 0xd3, 0x1b, 0x0c, 0x0a, 0x1c, 0x81, 0x18, 0x2f, 0xb1, 0xc8, 0xee, 0xc2, 0x91, 0x72,
+	0xdb, 0xdb, 0x3e, 0x46, 0x1f, 0xa2, 0xaf, 0xd2, 0xb7, 0xe9, 0xec, 0xd9, 0x05, 0x44, 0x2a, 0x4d,
+	0xc6, 0x33, 0xb9, 0xe0, 0x10, 0xfb, 0x9d, 0x83, 0xdd, 0xf3, 0xf3, 0x9d, 0x6f, 0x01, 0xc7, 0xb9,
+	0x94, 0xb9, 0xc0, 0xc3, 0x5c, 0x8a, 0xa4, 0xcc, 0x0f, 0xa5, 0xca, 0x8f, 0x92, 0xaa, 0xc2, 0x32,
+	0x2f, 0x4a, 0x3c, 0x2a, 0x4a, 0x83, 0xaa, 0x4c, 0xc4, 0x91, 0x90, 0xb9, 0xfd, 0xc5, 0x1a, 0xd5,
+	0xbb, 0x22, 0xc5, 0xc3, 0x4a, 0x49, 0x23, 0xd9, 0xb0, 0xf5, 0x0c, 0x5f, 0xc3, 0x74, 0x29, 0xf3,
+	0x73, 0x67, 0x3e, 0x51, 0x4a, 0xaa, 0xf0, 0x4b, 0x18, 0xd2, 0xc3, 0x9f, 0x65, 0x86, 0x6c, 0x17,
+	0x3a, 0x67, 0xdf, 0xce, 0x7e, 0xc7, 0xee, 0xc0, 0xf4, 0xf4, 0xf5, 0xf7, 0x2f, 0x96, 0xa7, 0x7f,
+	0x89, 0xa3, 0x93, 0xef, 0xde, 0x9c, 0x9c, 0x5f, 0xcc, 0x02, 0xb6, 0x0f, 0x93, 0xf3, 0x8b, 0xb3,
+	0xe8, 0xc5, 0xcb, 0x93, 0xf8, 0x24, 0x8a, 0xce, 0xa2, 0x59, 0x27, 0xcc, 0x61, 0xef, 0x8d, 0x46,
+	0xf5, 0xa2, 0xaa, 0x96, 0x32, 0x5f, 0x16, 0x25, 0xb2, 0x8f, 0x60, 0xcf, 0x14, 0x6b, 0xd4, 0x26,
+	0x59, 0x57, 0x71, 0xad, 0x31, 0xe5, 0xc1, 0xbc, 0xb3, 0xe8, 0x46, 0x93, 0x16, 0x7d, 0xa3, 0x31,
+	0x65, 0x07, 0xd0, 0x13, 0xf8, 0x0e, 0x05, 0xef, 0x90, 0xd5, 0x2d, 0x18, 0x87, 0xfe, 0x1a, 0xb5,
+	0x4e, 0x72, 0xe4, 0xdd, 0x79, 0x67, 0x31, 0x8c, 0x9a, 0x65, 0xf8, 0x12, 0xa6, 0x37, 0x07, 0xbd,
+	0x54, 0xb2, 0xae, 0xd8, 0x9f, 0x60, 0x60, 0x73, 0x15, 0x45, 0x89, 0xbc, 0x33, 0xef, 0x2e, 0x46,
+	0x9f, 0xdf, 0x3f, 0x6c, 0x33, 0x3d, 0xdc, 0x0e, 0x2b, 0xea, 0x0b, 0xf7, 0x10, 0x86, 0x30, 0xfe,
+	0x5a, 0xd4, 0x7a, 0x15, 0xe1, 0x0f, 0x35, 0x6a, 0xc3, 0x18, 0xec, 0x08, 0x99, 0x6b, 0x1e, 0xcc,
+	0x83, 0xc5, 0x38, 0xa2, 0xe7, 0xf0, 0x39, 0xcc, 0xce, 0xd1, 0x9c, 0x9b, 0xc4, 0xd4, 0xba, 0xf1,
+	0xbb, 0x07, 0xbb, 0x9a, 0x00, 0xca, 0x67, 0x18, 0xf9, 0x55, 0xf8, 0x1c, 0x86, 0x4b, 0x99, 0x9f,
+	0x5d, 0x5e, 0x6a, 0x34, 0xec, 0x11, 0x80, 0x72, 0xfe, 0x71, 0x91, 0xf9, 0x2d, 0x87, 0x1e, 0x39,
+	0xcd, 0xc2, 0x0b, 0xe8, 0x37, 0x65, 0x62, 0xb0, 0x63, 0x0b, 0xe2, 0x8b, 0x43, 0xcf, 0xdb, 0x35,
+	0xe9, 0x35, 0x35, 0x79, 0x0c, 0x23, 0x9b, 0xe6, 0x76, 0x5d, 0x40, 0xc8, 0xfc, 0x95, 0x2f, 0xcd,
+	0x3f, 0x01, 0xc0, 0x47, 0xb9, 0x94, 0x39, 0xbb, 0x0b, 0xbb, 0x49, 0x55, 0xb9, 0xf3, 0xad, 0x6b,
+	0x2f, 0xa9, 0xaa, 0xd3, 0x8c, 0x7d, 0x08, 0xc3, 0xb5, 0xcc, 0x6a, 0x81, 0xd6, 0xf2, 0xd1, 0x3c,
+	0x58, 0x0c, 0x8f, 0xfb, 0x19, 0x5e, 0x26, 0xb5, 0x30, 0xd1, 0xc0, 0x59, 0x4e, 0x33, 0x9b, 0xc0,
+	0x3b, 0x54, 0xba, 0x90, 0xa5, 0x75, 0xeb, 0xd0, 0x06, 0x43, 0x8f, 0x38, 0xf3, 0x46, 0x7e, 0x36,
+	0x94, 0xcd, 0xfc, 0xd8, 0x27, 0xb0, 0x2b, 0xa9, 0x10, 0xfc, 0xe9, 0x3c, 0x58, 0x8c, 0x3e, 0x3f,
+	0xd8, 0xe8, 0x47, 0x5b, 0xa4, 0xc8, 0xfb, 0xb0, 0x3d, 0xe8, 0x14, 0x15, 0xdf, 0xa1, 0x33, 0x3a,
+	0x45, 0xc5, 0x1e, 0xc0, 0xa0, 0x2c, 0xd2, 0xb7, 0x65, 0xb2, 0x46, 0xde, 0xb3, 0x01, 0x46, 0xed,
+	0xda, 0x1e, 0xac, 0x4d, 0xa2, 0x4c, 0x4c, 0x45, 0xdb, 0xa5, 0xa2, 0x0d, 0x09, 0xb9, 0xb0, 0x95,
+	0xbb, 0x0f, 0x03, 0x2c, 0x33, 0x67, 0xec, 0x93, 0xb1, 0x8f, 0x65, 0x46, 0x26, 0x0e, 0x7d, 0x91,
+	0x18, 0x2c, 0xd3, 0x6b, 0x3e, 0x70, 0x16, 0xbf, 0x24, 0xb2, 0xa5, 0xd7, 0xa9, 0x40, 0xcd, 0x87,
+	0xce, 0xe2, 0x97, 0xb6, 0xd7, 0x6b, 0x34, 0x2b, 0x99, 0x71, 0x70, 0xbd, 0x76, 0x2b, 0x1b, 0xa1,
+	0x42, 0x2d, 0x6b, 0x95, 0x22, 0x1f, 0x91, 0xa5, 0x5d, 0xb3, 0x27, 0x30, 0x5e, 0x19, 0x53, 0xc5,
+	0xbe, 0x58, 0x7c, 0x4c, 0xf6, 0x91, 0xc5, 0xbe, 0x77, 0xd0, 0x06, 0x85, 0x26, 0xd4, 0x60, 0xbf,
+	0x62, 0x4f, 0x61, 0xa2, 0x50, 0x57, 0xb2, 0xd4, 0x18, 0xeb, 0xe2, 0x27, 0xe4, 0x7b, 0x14, 0xce,
+	0xb8, 0x01, 0xcf, 0x8b, 0x9f, 0xd0, 0x9d, 0x7d, 0x89, 0x4a, 0xa1, 0xe2, 0x53, 0x57, 0x9d, 0x66,
+	0x6d, 0xab, 0x53, 0x6b, 0x54, 0x71, 0x92, 0x63, 0x69, 0xf8, 0x8c, 0xac, 0x43, 0x8b, 0xbc, 0xb0,
+	0x00, 0x0b, 0x61, 0x52, 0x2b, 0x11, 0xaf, 0x93, 0x2a, 0xc6, 0xd2, 0xa8, 0x6b, 0xbe, 0xef, 0x62,
+	0xab, 0x95, 0x78, 0x95, 0x54, 0x27, 0x16, 0xb2, 0xdb, 0xa7, 0x72, 0xfd, 0x8f, 0xa2, 0xc4, 0x8c,
+	0x33, 0x97, 0x5a, 0xb3, 0xb6, 0x0c, 0x4c, 0xaa, 0x22, 0x6e, 0x8a, 0x75, 0x67, 0x1e, 0x2c, 0xba,
+	0x11, 0x24, 0x55, 0xf1, 0xca, 0xd7, 0x8b, 0xc1, 0xce, 0x4a, 0x6a, 0xc3, 0x0f, 0xe8, 0x64, 0x7a,
+	0xb6, 0x58, 0x6a, 0xb1, 0xbb, 0xf3, 0x60, 0x11, 0x44, 0xf4, 0xcc, 0x9e, 0xc1, 0xd4, 0x24, 0xfa,
+	0x6d, 0xfc, 0x43, 0x8d, 0x35, 0xc6, 0xd4, 0xe8, 0x7b, 0xf4, 0xca, 0xc4, 0xc2, 0xdf, 0x59, 0xf4,
+	0xb5, 0xed, 0xf6, 0x43, 0x18, 0x92, 0x1f, 0x79, 0x7c, 0xe0, 0x92, 0xb5, 0x00, 0x19, 0x0f, 0xe1,
+	0xce, 0x8f, 0x89, 0x8e, 0x85, 0x4c, 0xb2, 0xa2, 0xcc, 0x63, 0xcf, 0x3e, 0xce, 0xe7, 0xc1, 0x62,
+	0x10, 0xed, 0xff, 0x98, 0xe8, 0xa5, 0xb3, 0x34, 0x83, 0xfb, 0x04, 0xc6, 0x15, 0x96, 0xe4, 0x4b,
+	0xfc, 0xb8, 0x4f, 0xe1, 0x8f, 0x3c, 0x46, 0x1c, 0xf9, 0xd8, 0x36, 0xa0, 0x12, 0x45, 0x9a, 0xc4,
+	0x45, 0x99, 0xe1, 0x15, 0x7f, 0x30, 0x0f, 0x16, 0xbd, 0xe3, 0xce, 0xa7, 0x9f, 0xd9, 0x26, 0x90,
+	0xe1, 0xd4, 0xe2, 0x6c, 0x0e, 0x83, 0xcb, 0xa2, 0x2c, 0xf4, 0x0a, 0x33, 0xfe, 0xd0, 0x1e, 0x78,
+	0xbc, 0x63, 0x54, 0x8d, 0x51, 0x8b, 0xda, 0xd0, 0x53, 0x21, 0x4b, 0x8c, 0xdf, 0xe2, 0x35, 0xff,
+	0x3d, 0x09, 0xc0, 0x80, 0x80, 0x6f, 0xf1, 0x9a, 0x3d, 0x83, 0x1d, 0x52, 0xab, 0x47, 0xa4, 0x56,
+	0x6c, 0x7b, 0x3a, 0x48, 0xa6, 0xc8, 0xce, 0xfe, 0x08, 0x33, 0xfb, 0xaf, 0xe3, 0xa2, 0x4c, 0xe5,
+	0xba, 0x12, 0x68, 0x90, 0x7f, 0x48, 0xf9, 0x4d, 0x09, 0x3f, 0x6d, 0x61, 0xf6, 0x09, 0x30, 0x3b,
+	0xed, 0x6e, 0x9b, 0x58, 0xa1, 0xc0, 0x44, 0x23, 0x7f, 0x46, 0x07, 0xcf, 0x92, 0xaa, 0x3a, 0x21,
+	0x43, 0xe4, 0x70, 0xdb, 0x49, 0xbc, 0x2a, 0x4c, 0xac, 0x30, 0xd1, 0xb2, 0xe4, 0x7f, 0xb0, 0x69,
+	0x46, 0x60, 0xa1, 0x88, 0x10, 0xf6, 0x05, 0xdc, 0xb3, 0xc5, 0x35, 0x2b, 0x25, 0x8d, 0x11, 0x98,
+	0xc5, 0x97, 0x52, 0xb9, 0xb2, 0x3d, 0xa6, 0xf3, 0x6d, 0xe9, 0x2f, 0x1a, 0xe3, 0xd7, 0x52, 0x51,
+	0xf9, 0xbe, 0x84, 0x07, 0x3f, 0x7f, 0xc9, 0xf7, 0x45, 0xf3, 0x39, 0xbd, 0xf8, 0xc1, 0xad, 0x17,
+	0x7d, 0x77, 0x34, 0xdd, 0x17, 0xed, 0x8b, 0x74, 0xd2, 0x13, 0x6a, 0xd0, 0xa4, 0x45, 0xe9, 0x8c,
+	0xc7, 0x30, 0xb2, 0x97, 0x1a, 0x2a, 0x47, 0x8a, 0x90, 0x12, 0x04, 0x07, 0x59, 0x5a, 0x84, 0x7f,
+	0x83, 0xd9, 0x52, 0xe6, 0xaf, 0x48, 0xc8, 0x9a, 0x81, 0xdb, 0xd2, 0xbc, 0xe0, 0x7d, 0x35, 0x2f,
+	0xd8, 0xd2, 0xbc, 0xf0, 0xbf, 0x3d, 0xd8, 0x5b, 0xca, 0x3c, 0xc2, 0x24, 0x6b, 0x28, 0xf5, 0x0b,
+	0x12, 0x7b, 0x7b, 0xa3, 0xee, 0xb6, 0x78, 0x7e, 0x05, 0x7b, 0x3e, 0x9a, 0x46, 0x23, 0xee, 0x10,
+	0x0f, 0x1e, 0x6e, 0xf3, 0x60, 0x2b, 0x85, 0x68, 0xb2, 0xde, 0xca, 0x68, 0x5b, 0x07, 0xbb, 0x54,
+	0xa9, 0x5f, 0xd0, 0xc1, 0x1d, 0x32, 0xb6, 0x3a, 0x78, 0xa3, 0xcd, 0xbd, 0xf7, 0xd0, 0xe6, 0x6d,
+	0xa1, 0xdf, 0x9d, 0x77, 0xb7, 0x85, 0xfe, 0x39, 0xec, 0xaf, 0x8b, 0xb2, 0x58, 0xd7, 0xeb, 0x98,
+	0xae, 0x60, 0xba, 0xb5, 0xfa, 0xc4, 0xa6, 0xa9, 0x37, 0x58, 0x46, 0xd3, 0xfd, 0xf5, 0x29, 0xb0,
+	0xa2, 0x4c, 0x45, 0x9d, 0xe1, 0x26, 0x9d, 0x07, 0x6e, 0x5c, 0xbd, 0x65, 0x83, 0xd0, 0x07, 0xd0,
+	0x4b, 0x65, 0x5d, 0x1a, 0x3e, 0xa4, 0xf8, 0xdd, 0xc2, 0xd2, 0xbc, 0x91, 0x23, 0x3a, 0x51, 0x61,
+	0x8e, 0x57, 0x7c, 0x8f, 0x7a, 0x35, 0x6b, 0x2c, 0xd4, 0xa5, 0x1c, 0xaf, 0x6c, 0xf4, 0x56, 0x83,
+	0xbc, 0x97, 0x53, 0xcb, 0xa1, 0x45, 0x9c, 0xf9, 0xe9, 0xed, 0x71, 0x9f, 0x51, 0xe4, 0xdb, 0xa3,
+	0xbe, 0x80, 0x59, 0x13, 0xb6, 0xed, 0x35, 0x7d, 0x23, 0x00, 0x05, 0xbd, 0xe7, 0x71, 0xf7, 0x75,
+	0xa1, 0xd9, 0x11, 0x1c, 0x34, 0x1e, 0x71, 0x85, 0x2d, 0xf3, 0xf9, 0x3e, 0xed, 0xba, 0x9f, 0x38,
+	0xb7, 0xbf, 0xa2, 0xda, 0x50, 0xa4, 0x66, 0x6b, 0x92, 0xcd, 0x11, 0x6d, 0x3b, 0xf2, 0xd8, 0x37,
+	0x56, 0x29, 0x1f, 0xc3, 0xa8, 0x3d, 0x5d, 0x08, 0x3e, 0x26, 0x0f, 0x68, 0x0e, 0x16, 0xc2, 0x8e,
+	0x4d, 0x9a, 0xa4, 0x2b, 0x8c, 0x0b, 0x83, 0x2a, 0x31, 0x52, 0xf1, 0x09, 0xf9, 0x4c, 0x08, 0x3d,
+	0xf5, 0xa0, 0xad, 0x44, 0x59, 0xaf, 0x63, 0xbd, 0x4a, 0x54, 0xa6, 0x39, 0xa3, 0x88, 0x86, 0x65,
+	0xbd, 0x3e, 0x27, 0x20, 0xfc, 0x57, 0x40, 0xdf, 0x83, 0x8e, 0xdb, 0xee, 0xb2, 0x61, 0x1f, 0x43,
+	0x57, 0xc8, 0x9c, 0x07, 0xc4, 0xcd, 0xbb, 0x1b, 0x2c, 0xb9, 0xf9, 0xc6, 0x88, 0xac, 0xc7, 0x06,
+	0xa3, 0x3a, 0xef, 0xc1, 0xa8, 0x10, 0x26, 0x22, 0xd1, 0x26, 0x6e, 0xf9, 0xe9, 0xc8, 0x3b, 0xb2,
+	0xe0, 0x89, 0xe3, 0x68, 0xf8, 0x9f, 0x80, 0x46, 0xed, 0x8d, 0xfd, 0xac, 0x89, 0x30, 0x95, 0xea,
+	0xf6, 0x4c, 0x05, 0xb7, 0x86, 0xf3, 0xd6, 0x3c, 0x74, 0x5c, 0x7e, 0xff, 0x7f, 0x1e, 0xba, 0x64,
+	0x6c, 0xe7, 0xa1, 0xe5, 0xd9, 0xce, 0x26, 0xcf, 0x1e, 0x01, 0x18, 0x69, 0x12, 0xe1, 0xee, 0xe1,
+	0x9e, 0x9b, 0x2f, 0x42, 0xe8, 0x12, 0xe6, 0xd0, 0x57, 0x14, 0x97, 0xe6, 0xbb, 0x6e, 0x3b, 0xbf,
+	0x0c, 0xff, 0xdd, 0xa1, 0x4a, 0xfa, 0xd0, 0x7f, 0x8b, 0x4c, 0xfc, 0x7c, 0xc4, 0x7b, 0xbf, 0x36,
+	0xe2, 0xbd, 0xcd, 0x11, 0x9f, 0xd9, 0xcf, 0x11, 0x51, 0x1b, 0xbb, 0xf7, 0x4a, 0xd6, 0x4a, 0x53,
+	0x0a, 0x93, 0xe3, 0xe0, 0xb3, 0x68, 0x7a, 0x63, 0xfa, 0xc6, 0x5a, 0xec, 0x25, 0xe3, 0x07, 0xa7,
+	0xd1, 0x23, 0x97, 0xd4, 0x20, 0x9a, 0x7a, 0xdc, 0x8b, 0x0e, 0x7d, 0xa0, 0xd4, 0x36, 0xb1, 0x56,
+	0xb8, 0xdc, 0xa8, 0x8f, 0x09, 0x6c, 0xa4, 0xe9, 0x29, 0x4c, 0x9a, 0x7d, 0x62, 0x59, 0x8a, 0x6b,
+	0x3f, 0xe2, 0xe3, 0x06, 0x3c, 0x2b, 0xc5, 0x75, 0x78, 0x45, 0x2a, 0xed, 0xab, 0xe4, 0x09, 0x77,
+	0x04, 0x3d, 0xda, 0xc8, 0x53, 0xee, 0xfe, 0x36, 0x8d, 0x36, 0xc8, 0x10, 0x39, 0x3f, 0xf6, 0x05,
+	0xf4, 0x75, 0xbd, 0x5e, 0x27, 0xea, 0xda, 0x33, 0xef, 0x57, 0x5e, 0x69, 0x3c, 0xbf, 0xea, 0xfd,
+	0xdd, 0x92, 0xf6, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x70, 0xd9, 0xa0, 0xf8, 0x48, 0x0d, 0x00,
+	0x00,
+}
diff --git a/vendor/google.golang.org/appengine/internal/log/log_service.proto b/vendor/google.golang.org/appengine/internal/log/log_service.proto
new file mode 100644
index 0000000..8981dc4
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/log/log_service.proto
@@ -0,0 +1,150 @@
+syntax = "proto2";
+option go_package = "log";
+
+package appengine;
+
+message LogServiceError {
+  enum ErrorCode {
+    OK  = 0;
+    INVALID_REQUEST = 1;
+    STORAGE_ERROR = 2;
+  }
+}
+
+message UserAppLogLine {
+  required int64 timestamp_usec = 1;
+  required int64 level = 2;
+  required string message = 3;
+}
+
+message UserAppLogGroup {
+  repeated UserAppLogLine log_line = 2;
+}
+
+message FlushRequest {
+  optional bytes logs = 1;
+}
+
+message SetStatusRequest {
+  required string status = 1;
+}
+
+
+message LogOffset {
+  optional bytes request_id = 1;
+}
+
+message LogLine {
+  required int64 time = 1;
+  required int32 level = 2;
+  required string log_message = 3;
+}
+
+message RequestLog {
+  required string app_id = 1;
+  optional string module_id = 37 [default="default"];
+  required string version_id = 2;
+  required bytes request_id = 3;
+  optional LogOffset offset = 35;
+  required string ip = 4;
+  optional string nickname = 5;
+  required int64 start_time = 6;
+  required int64 end_time = 7;
+  required int64 latency = 8;
+  required int64 mcycles = 9;
+  required string method = 10;
+  required string resource = 11;
+  required string http_version = 12;
+  required int32 status = 13;
+  required int64 response_size = 14;
+  optional string referrer = 15;
+  optional string user_agent = 16;
+  required string url_map_entry = 17;
+  required string combined = 18;
+  optional int64 api_mcycles = 19;
+  optional string host = 20;
+  optional double cost = 21;
+
+  optional string task_queue_name = 22;
+  optional string task_name = 23;
+
+  optional bool was_loading_request = 24;
+  optional int64 pending_time = 25;
+  optional int32 replica_index = 26 [default = -1];
+  optional bool finished = 27 [default = true];
+  optional bytes clone_key = 28;
+
+  repeated LogLine line = 29;
+
+  optional bool lines_incomplete = 36;
+  optional bytes app_engine_release = 38;
+
+  optional int32 exit_reason = 30;
+  optional bool was_throttled_for_time = 31;
+  optional bool was_throttled_for_requests = 32;
+  optional int64 throttled_time = 33;
+
+  optional bytes server_name = 34;
+}
+
+message LogModuleVersion {
+  optional string module_id = 1 [default="default"];
+  optional string version_id = 2;
+}
+
+message LogReadRequest {
+  required string app_id = 1;
+  repeated string version_id = 2;
+  repeated LogModuleVersion module_version = 19;
+
+  optional int64 start_time = 3;
+  optional int64 end_time = 4;
+  optional LogOffset offset = 5;
+  repeated bytes request_id = 6;
+
+  optional int32 minimum_log_level = 7;
+  optional bool include_incomplete = 8;
+  optional int64 count = 9;
+
+  optional string combined_log_regex = 14;
+  optional string host_regex = 15;
+  optional int32 replica_index = 16;
+
+  optional bool include_app_logs = 10;
+  optional int32 app_logs_per_request = 17;
+  optional bool include_host = 11;
+  optional bool include_all = 12;
+  optional bool cache_iterator = 13;
+  optional int32 num_shards = 18;
+}
+
+message LogReadResponse {
+  repeated RequestLog log = 1;
+  optional LogOffset offset = 2;
+  optional int64 last_end_time = 3;
+}
+
+message LogUsageRecord {
+  optional string version_id = 1;
+  optional int32 start_time = 2;
+  optional int32 end_time = 3;
+  optional int64 count = 4;
+  optional int64 total_size = 5;
+  optional int32 records = 6;
+}
+
+message LogUsageRequest {
+  required string app_id = 1;
+  repeated string version_id = 2;
+  optional int32 start_time = 3;
+  optional int32 end_time = 4;
+  optional uint32 resolution_hours = 5 [default = 1];
+  optional bool combine_versions = 6;
+  optional int32 usage_version = 7;
+  optional bool versions_only = 8;
+}
+
+message LogUsageResponse {
+  repeated LogUsageRecord usage = 1;
+  optional LogUsageRecord summary = 2;
+}
diff --git a/vendor/google.golang.org/appengine/internal/main.go b/vendor/google.golang.org/appengine/internal/main.go
new file mode 100644
index 0000000..4903616
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/main.go
@@ -0,0 +1,15 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// +build appengine
+
+package internal
+
+import (
+	"appengine_internal"
+)
+
+func Main() {
+	appengine_internal.Main()
+}
diff --git a/vendor/google.golang.org/appengine/internal/main_vm.go b/vendor/google.golang.org/appengine/internal/main_vm.go
new file mode 100644
index 0000000..822e784
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/main_vm.go
@@ -0,0 +1,48 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// +build !appengine
+
+package internal
+
+import (
+	"io"
+	"log"
+	"net/http"
+	"net/url"
+	"os"
+)
+
+func Main() {
+	installHealthChecker(http.DefaultServeMux)
+
+	port := "8080"
+	if s := os.Getenv("PORT"); s != "" {
+		port = s
+	}
+
+	host := ""
+	if IsDevAppServer() {
+		host = "127.0.0.1"
+	}
+	if err := http.ListenAndServe(host+":"+port, http.HandlerFunc(handleHTTP)); err != nil {
+		log.Fatalf("http.ListenAndServe: %v", err)
+	}
+}
+
+func installHealthChecker(mux *http.ServeMux) {
+	// If no health check handler has been installed by this point, add a trivial one.
+	const healthPath = "/_ah/health"
+	hreq := &http.Request{
+		Method: "GET",
+		URL: &url.URL{
+			Path: healthPath,
+		},
+	}
+	if _, pat := mux.Handler(hreq); pat != healthPath {
+		mux.HandleFunc(healthPath, func(w http.ResponseWriter, r *http.Request) {
+			io.WriteString(w, "ok")
+		})
+	}
+}
diff --git a/vendor/google.golang.org/appengine/internal/metadata.go b/vendor/google.golang.org/appengine/internal/metadata.go
new file mode 100644
index 0000000..9cc1f71
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/metadata.go
@@ -0,0 +1,61 @@
+// Copyright 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package internal
+
+// This file has code for accessing metadata.
+//
+// References:
+//	https://cloud.google.com/compute/docs/metadata
+
+import (
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"net/url"
+)
+
+const (
+	metadataHost = "metadata"
+	metadataPath = "/computeMetadata/v1/"
+)
+
+var (
+	metadataRequestHeaders = http.Header{
+		"Metadata-Flavor": []string{"Google"},
+	}
+)
+
+// TODO(dsymonds): Do we need to support default values, like Python?
+func mustGetMetadata(key string) []byte {
+	b, err := getMetadata(key)
+	if err != nil {
+		log.Fatalf("Metadata fetch failed: %v", err)
+	}
+	return b
+}
+
+func getMetadata(key string) ([]byte, error) {
+	// TODO(dsymonds): May need to use url.Parse to support keys with query args.
+	req := &http.Request{
+		Method: "GET",
+		URL: &url.URL{
+			Scheme: "http",
+			Host:   metadataHost,
+			Path:   metadataPath + key,
+		},
+		Header: metadataRequestHeaders,
+		Host:   metadataHost,
+	}
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	if resp.StatusCode != 200 {
+		return nil, fmt.Errorf("metadata server returned HTTP %d", resp.StatusCode)
+	}
+	return ioutil.ReadAll(resp.Body)
+}
diff --git a/vendor/google.golang.org/appengine/internal/net.go b/vendor/google.golang.org/appengine/internal/net.go
new file mode 100644
index 0000000..3b94cf0
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/net.go
@@ -0,0 +1,56 @@
+// Copyright 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package internal
+
+// This file implements a network dialer that limits the number of concurrent connections.
+// It is only used for API calls.
+
+import (
+	"log"
+	"net"
+	"runtime"
+	"sync"
+	"time"
+)
+
+var limitSem = make(chan int, 100) // TODO(dsymonds): Use environment variable.
+
+func limitRelease() {
+	// non-blocking
+	select {
+	case <-limitSem:
+	default:
+		// This should not normally happen.
+		log.Print("appengine: unbalanced limitSem release!")
+	}
+}
+
+func limitDial(network, addr string) (net.Conn, error) {
+	limitSem <- 1
+
+	// Dial with a timeout in case the API host is MIA.
+	// The connection should normally be very fast.
+	conn, err := net.DialTimeout(network, addr, 500*time.Millisecond)
+	if err != nil {
+		limitRelease()
+		return nil, err
+	}
+	lc := &limitConn{Conn: conn}
+	runtime.SetFinalizer(lc, (*limitConn).Close) // shouldn't usually be required
+	return lc, nil
+}
+
+type limitConn struct {
+	close sync.Once
+	net.Conn
+}
+
+func (lc *limitConn) Close() error {
+	defer lc.close.Do(func() {
+		limitRelease()
+		runtime.SetFinalizer(lc, nil)
+	})
+	return lc.Conn.Close()
+}
diff --git a/vendor/google.golang.org/appengine/internal/regen.sh b/vendor/google.golang.org/appengine/internal/regen.sh
new file mode 100755
index 0000000..2fdb546
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/regen.sh
@@ -0,0 +1,40 @@
+#!/bin/bash -e
+#
+# This script rebuilds the generated code for the protocol buffers.
+# To run this you will need protoc and goprotobuf installed;
+# see https://github.com/golang/protobuf for instructions.
+
+PKG=google.golang.org/appengine
+
+function die() {
+	echo 1>&2 $*
+	exit 1
+}
+
+# Sanity check that the right tools are accessible.
+for tool in go protoc protoc-gen-go; do
+	q=$(which $tool) || die "didn't find $tool"
+	echo 1>&2 "$tool: $q"
+done
+
+echo -n 1>&2 "finding package dir... "
+pkgdir=$(go list -f '{{.Dir}}' $PKG)
+echo 1>&2 $pkgdir
+base=$(echo $pkgdir | sed "s,/$PKG\$,,")
+echo 1>&2 "base: $base"
+cd $base
+
+# Run protoc once per package.
+for dir in $(find $PKG/internal -name '*.proto' | xargs dirname | sort | uniq); do
+	echo 1>&2 "* $dir"
+	protoc --go_out=. $dir/*.proto
+done
+
+for f in $(find $PKG/internal -name '*.pb.go'); do
+  # Remove proto.RegisterEnum calls.
+  # These cause duplicate registration panics when these packages
+  # are used on classic App Engine. proto.RegisterEnum only affects
+  # parsing the text format; we don't care about that.
+  # https://code.google.com/p/googleappengine/issues/detail?id=11670#c17
+  sed -i '/proto.RegisterEnum/d' $f
+done
diff --git a/vendor/google.golang.org/appengine/internal/remote_api/remote_api.pb.go b/vendor/google.golang.org/appengine/internal/remote_api/remote_api.pb.go
new file mode 100644
index 0000000..172aebe
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/remote_api/remote_api.pb.go
@@ -0,0 +1,287 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: google.golang.org/appengine/internal/remote_api/remote_api.proto
+
+/*
+Package remote_api is a generated protocol buffer package.
+
+It is generated from these files:
+	google.golang.org/appengine/internal/remote_api/remote_api.proto
+
+It has these top-level messages:
+	Request
+	ApplicationError
+	RpcError
+	Response
+*/
+package remote_api
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+type RpcError_ErrorCode int32
+
+const (
+	RpcError_UNKNOWN             RpcError_ErrorCode = 0
+	RpcError_CALL_NOT_FOUND      RpcError_ErrorCode = 1
+	RpcError_PARSE_ERROR         RpcError_ErrorCode = 2
+	RpcError_SECURITY_VIOLATION  RpcError_ErrorCode = 3
+	RpcError_OVER_QUOTA          RpcError_ErrorCode = 4
+	RpcError_REQUEST_TOO_LARGE   RpcError_ErrorCode = 5
+	RpcError_CAPABILITY_DISABLED RpcError_ErrorCode = 6
+	RpcError_FEATURE_DISABLED    RpcError_ErrorCode = 7
+	RpcError_BAD_REQUEST         RpcError_ErrorCode = 8
+	RpcError_RESPONSE_TOO_LARGE  RpcError_ErrorCode = 9
+	RpcError_CANCELLED           RpcError_ErrorCode = 10
+	RpcError_REPLAY_ERROR        RpcError_ErrorCode = 11
+	RpcError_DEADLINE_EXCEEDED   RpcError_ErrorCode = 12
+)
+
+var RpcError_ErrorCode_name = map[int32]string{
+	0:  "UNKNOWN",
+	1:  "CALL_NOT_FOUND",
+	2:  "PARSE_ERROR",
+	3:  "SECURITY_VIOLATION",
+	4:  "OVER_QUOTA",
+	5:  "REQUEST_TOO_LARGE",
+	6:  "CAPABILITY_DISABLED",
+	7:  "FEATURE_DISABLED",
+	8:  "BAD_REQUEST",
+	9:  "RESPONSE_TOO_LARGE",
+	10: "CANCELLED",
+	11: "REPLAY_ERROR",
+	12: "DEADLINE_EXCEEDED",
+}
+var RpcError_ErrorCode_value = map[string]int32{
+	"UNKNOWN":             0,
+	"CALL_NOT_FOUND":      1,
+	"PARSE_ERROR":         2,
+	"SECURITY_VIOLATION":  3,
+	"OVER_QUOTA":          4,
+	"REQUEST_TOO_LARGE":   5,
+	"CAPABILITY_DISABLED": 6,
+	"FEATURE_DISABLED":    7,
+	"BAD_REQUEST":         8,
+	"RESPONSE_TOO_LARGE":  9,
+	"CANCELLED":           10,
+	"REPLAY_ERROR":        11,
+	"DEADLINE_EXCEEDED":   12,
+}
+
+func (x RpcError_ErrorCode) Enum() *RpcError_ErrorCode {
+	p := new(RpcError_ErrorCode)
+	*p = x
+	return p
+}
+func (x RpcError_ErrorCode) String() string {
+	return proto.EnumName(RpcError_ErrorCode_name, int32(x))
+}
+func (x *RpcError_ErrorCode) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(RpcError_ErrorCode_value, data, "RpcError_ErrorCode")
+	if err != nil {
+		return err
+	}
+	*x = RpcError_ErrorCode(value)
+	return nil
+}
+func (RpcError_ErrorCode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{2, 0} }
+
+type Request struct {
+	ServiceName      *string `protobuf:"bytes,2,req,name=service_name,json=serviceName" json:"service_name,omitempty"`
+	Method           *string `protobuf:"bytes,3,req,name=method" json:"method,omitempty"`
+	Request          []byte  `protobuf:"bytes,4,req,name=request" json:"request,omitempty"`
+	RequestId        *string `protobuf:"bytes,5,opt,name=request_id,json=requestId" json:"request_id,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *Request) Reset()                    { *m = Request{} }
+func (m *Request) String() string            { return proto.CompactTextString(m) }
+func (*Request) ProtoMessage()               {}
+func (*Request) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+func (m *Request) GetServiceName() string {
+	if m != nil && m.ServiceName != nil {
+		return *m.ServiceName
+	}
+	return ""
+}
+
+func (m *Request) GetMethod() string {
+	if m != nil && m.Method != nil {
+		return *m.Method
+	}
+	return ""
+}
+
+func (m *Request) GetRequest() []byte {
+	if m != nil {
+		return m.Request
+	}
+	return nil
+}
+
+func (m *Request) GetRequestId() string {
+	if m != nil && m.RequestId != nil {
+		return *m.RequestId
+	}
+	return ""
+}
+
+type ApplicationError struct {
+	Code             *int32  `protobuf:"varint,1,req,name=code" json:"code,omitempty"`
+	Detail           *string `protobuf:"bytes,2,req,name=detail" json:"detail,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *ApplicationError) Reset()                    { *m = ApplicationError{} }
+func (m *ApplicationError) String() string            { return proto.CompactTextString(m) }
+func (*ApplicationError) ProtoMessage()               {}
+func (*ApplicationError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+
+func (m *ApplicationError) GetCode() int32 {
+	if m != nil && m.Code != nil {
+		return *m.Code
+	}
+	return 0
+}
+
+func (m *ApplicationError) GetDetail() string {
+	if m != nil && m.Detail != nil {
+		return *m.Detail
+	}
+	return ""
+}
+
+type RpcError struct {
+	Code             *int32  `protobuf:"varint,1,req,name=code" json:"code,omitempty"`
+	Detail           *string `protobuf:"bytes,2,opt,name=detail" json:"detail,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *RpcError) Reset()                    { *m = RpcError{} }
+func (m *RpcError) String() string            { return proto.CompactTextString(m) }
+func (*RpcError) ProtoMessage()               {}
+func (*RpcError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+
+func (m *RpcError) GetCode() int32 {
+	if m != nil && m.Code != nil {
+		return *m.Code
+	}
+	return 0
+}
+
+func (m *RpcError) GetDetail() string {
+	if m != nil && m.Detail != nil {
+		return *m.Detail
+	}
+	return ""
+}
+
+type Response struct {
+	Response         []byte            `protobuf:"bytes,1,opt,name=response" json:"response,omitempty"`
+	Exception        []byte            `protobuf:"bytes,2,opt,name=exception" json:"exception,omitempty"`
+	ApplicationError *ApplicationError `protobuf:"bytes,3,opt,name=application_error,json=applicationError" json:"application_error,omitempty"`
+	JavaException    []byte            `protobuf:"bytes,4,opt,name=java_exception,json=javaException" json:"java_exception,omitempty"`
+	RpcError         *RpcError         `protobuf:"bytes,5,opt,name=rpc_error,json=rpcError" json:"rpc_error,omitempty"`
+	XXX_unrecognized []byte            `json:"-"`
+}
+
+func (m *Response) Reset()                    { *m = Response{} }
+func (m *Response) String() string            { return proto.CompactTextString(m) }
+func (*Response) ProtoMessage()               {}
+func (*Response) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
+
+func (m *Response) GetResponse() []byte {
+	if m != nil {
+		return m.Response
+	}
+	return nil
+}
+
+func (m *Response) GetException() []byte {
+	if m != nil {
+		return m.Exception
+	}
+	return nil
+}
+
+func (m *Response) GetApplicationError() *ApplicationError {
+	if m != nil {
+		return m.ApplicationError
+	}
+	return nil
+}
+
+func (m *Response) GetJavaException() []byte {
+	if m != nil {
+		return m.JavaException
+	}
+	return nil
+}
+
+func (m *Response) GetRpcError() *RpcError {
+	if m != nil {
+		return m.RpcError
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*Request)(nil), "remote_api.Request")
+	proto.RegisterType((*ApplicationError)(nil), "remote_api.ApplicationError")
+	proto.RegisterType((*RpcError)(nil), "remote_api.RpcError")
+	proto.RegisterType((*Response)(nil), "remote_api.Response")
+}
+
+func init() {
+	proto.RegisterFile("google.golang.org/appengine/internal/remote_api/remote_api.proto", fileDescriptor0)
+}
+
+var fileDescriptor0 = []byte{
+	// 531 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x51, 0x6e, 0xd3, 0x40,
+	0x10, 0x86, 0xb1, 0x9b, 0x34, 0xf1, 0xc4, 0x2d, 0xdb, 0xa5, 0x14, 0x0b, 0x15, 0x29, 0x44, 0x42,
+	0xca, 0x53, 0x2a, 0x38, 0x00, 0x62, 0x63, 0x6f, 0x91, 0x85, 0x65, 0xa7, 0x6b, 0xbb, 0x50, 0x5e,
+	0x56, 0x2b, 0x67, 0x65, 0x8c, 0x12, 0xaf, 0xd9, 0x98, 0x8a, 0x17, 0x6e, 0xc0, 0xb5, 0x38, 0x0c,
+	0xb7, 0x40, 0x36, 0x6e, 0x63, 0xf5, 0x89, 0xb7, 0x7f, 0x7e, 0x7b, 0xe6, 0x1b, 0xcd, 0xcc, 0xc2,
+	0xbb, 0x5c, 0xa9, 0x7c, 0x23, 0x17, 0xb9, 0xda, 0x88, 0x32, 0x5f, 0x28, 0x9d, 0x5f, 0x88, 0xaa,
+	0x92, 0x65, 0x5e, 0x94, 0xf2, 0xa2, 0x28, 0x6b, 0xa9, 0x4b, 0xb1, 0xb9, 0xd0, 0x72, 0xab, 0x6a,
+	0xc9, 0x45, 0x55, 0xf4, 0xe4, 0xa2, 0xd2, 0xaa, 0x56, 0x18, 0xf6, 0xce, 0xec, 0x27, 0x8c, 0x98,
+	0xfc, 0xf6, 0x5d, 0xee, 0x6a, 0xfc, 0x12, 0xec, 0x9d, 0xd4, 0xb7, 0x45, 0x26, 0x79, 0x29, 0xb6,
+	0xd2, 0x31, 0xa7, 0xe6, 0xdc, 0x62, 0x93, 0xce, 0x0b, 0xc5, 0x56, 0xe2, 0x33, 0x38, 0xdc, 0xca,
+	0xfa, 0x8b, 0x5a, 0x3b, 0x07, 0xed, 0xc7, 0x2e, 0xc2, 0x0e, 0x8c, 0xf4, 0xbf, 0x2a, 0xce, 0x60,
+	0x6a, 0xce, 0x6d, 0x76, 0x17, 0xe2, 0x17, 0x00, 0x9d, 0xe4, 0xc5, 0xda, 0x19, 0x4e, 0x8d, 0xb9,
+	0xc5, 0xac, 0xce, 0xf1, 0xd7, 0xb3, 0xb7, 0x80, 0x48, 0x55, 0x6d, 0x8a, 0x4c, 0xd4, 0x85, 0x2a,
+	0xa9, 0xd6, 0x4a, 0x63, 0x0c, 0x83, 0x4c, 0xad, 0xa5, 0x63, 0x4c, 0xcd, 0xf9, 0x90, 0xb5, 0xba,
+	0x01, 0xaf, 0x65, 0x2d, 0x8a, 0x4d, 0xd7, 0x55, 0x17, 0xcd, 0x7e, 0x9b, 0x30, 0x66, 0x55, 0xf6,
+	0x7f, 0x89, 0x46, 0x2f, 0xf1, 0x97, 0x09, 0x56, 0x9b, 0xe5, 0x36, 0x7f, 0x4d, 0x60, 0x94, 0x86,
+	0x1f, 0xc2, 0xe8, 0x63, 0x88, 0x1e, 0x61, 0x0c, 0xc7, 0x2e, 0x09, 0x02, 0x1e, 0x46, 0x09, 0xbf,
+	0x8c, 0xd2, 0xd0, 0x43, 0x06, 0x7e, 0x0c, 0x93, 0x15, 0x61, 0x31, 0xe5, 0x94, 0xb1, 0x88, 0x21,
+	0x13, 0x9f, 0x01, 0x8e, 0xa9, 0x9b, 0x32, 0x3f, 0xb9, 0xe1, 0xd7, 0x7e, 0x14, 0x90, 0xc4, 0x8f,
+	0x42, 0x74, 0x80, 0x8f, 0x01, 0xa2, 0x6b, 0xca, 0xf8, 0x55, 0x1a, 0x25, 0x04, 0x0d, 0xf0, 0x53,
+	0x38, 0x61, 0xf4, 0x2a, 0xa5, 0x71, 0xc2, 0x93, 0x28, 0xe2, 0x01, 0x61, 0xef, 0x29, 0x1a, 0xe2,
+	0x67, 0xf0, 0xc4, 0x25, 0x2b, 0xb2, 0xf4, 0x83, 0xa6, 0x80, 0xe7, 0xc7, 0x64, 0x19, 0x50, 0x0f,
+	0x1d, 0xe2, 0x53, 0x40, 0x97, 0x94, 0x24, 0x29, 0xa3, 0x7b, 0x77, 0xd4, 0xe0, 0x97, 0xc4, 0xe3,
+	0x5d, 0x25, 0x34, 0x6e, 0xf0, 0x8c, 0xc6, 0xab, 0x28, 0x8c, 0x69, 0xaf, 0xae, 0x85, 0x8f, 0xc0,
+	0x72, 0x49, 0xe8, 0xd2, 0xa0, 0xc9, 0x03, 0x8c, 0xc0, 0x66, 0x74, 0x15, 0x90, 0x9b, 0xae, 0xef,
+	0x49, 0xd3, 0x8f, 0x47, 0x89, 0x17, 0xf8, 0x21, 0xe5, 0xf4, 0x93, 0x4b, 0xa9, 0x47, 0x3d, 0x64,
+	0xcf, 0xfe, 0x18, 0x30, 0x66, 0x72, 0x57, 0xa9, 0x72, 0x27, 0xf1, 0x73, 0x18, 0xeb, 0x4e, 0x3b,
+	0xc6, 0xd4, 0x98, 0xdb, 0xec, 0x3e, 0xc6, 0xe7, 0x60, 0xc9, 0x1f, 0x99, 0xac, 0x9a, 0x75, 0xb5,
+	0x23, 0xb5, 0xd9, 0xde, 0xc0, 0x3e, 0x9c, 0x88, 0xfd, 0x3a, 0xb9, 0x6c, 0x06, 0xec, 0x1c, 0x4c,
+	0x8d, 0xf9, 0xe4, 0xcd, 0xf9, 0xa2, 0x77, 0x87, 0x0f, 0x77, 0xce, 0x90, 0x78, 0x78, 0x05, 0xaf,
+	0xe0, 0xf8, 0xab, 0xb8, 0x15, 0x7c, 0x4f, 0x1b, 0xb4, 0xb4, 0xa3, 0xc6, 0xa5, 0xf7, 0xc4, 0xd7,
+	0x60, 0xe9, 0x2a, 0xeb, 0x48, 0xc3, 0x96, 0x74, 0xda, 0x27, 0xdd, 0x1d, 0x07, 0x1b, 0xeb, 0x4e,
+	0x2d, 0xed, 0xcf, 0xbd, 0x07, 0xf0, 0x37, 0x00, 0x00, 0xff, 0xff, 0x38, 0xd1, 0x0f, 0x22, 0x4f,
+	0x03, 0x00, 0x00,
+}
diff --git a/vendor/google.golang.org/appengine/internal/remote_api/remote_api.proto b/vendor/google.golang.org/appengine/internal/remote_api/remote_api.proto
new file mode 100644
index 0000000..f21763a
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/remote_api/remote_api.proto
@@ -0,0 +1,44 @@
+syntax = "proto2";
+option go_package = "remote_api";
+
+package remote_api;
+
+message Request {
+  required string service_name = 2;
+  required string method = 3;
+  required bytes request = 4;
+  optional string request_id = 5;
+}
+
+message ApplicationError {
+  required int32 code = 1;
+  required string detail = 2;
+}
+
+message RpcError {
+  enum ErrorCode {
+    UNKNOWN = 0;
+    CALL_NOT_FOUND = 1;
+    PARSE_ERROR = 2;
+    SECURITY_VIOLATION = 3;
+    OVER_QUOTA = 4;
+    REQUEST_TOO_LARGE = 5;
+    CAPABILITY_DISABLED = 6;
+    FEATURE_DISABLED = 7;
+    BAD_REQUEST = 8;
+    RESPONSE_TOO_LARGE = 9;
+    CANCELLED = 10;
+    REPLAY_ERROR = 11;
+    DEADLINE_EXCEEDED = 12;
+  }
+  required int32 code = 1;
+  optional string detail = 2;
+}
+
+message Response {
+  optional bytes response = 1;
+  optional bytes exception = 2;
+  optional ApplicationError application_error = 3;
+  optional bytes java_exception = 4;
+  optional RpcError rpc_error = 5;
+}
diff --git a/vendor/google.golang.org/appengine/internal/transaction.go b/vendor/google.golang.org/appengine/internal/transaction.go
new file mode 100644
index 0000000..9006ae6
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/transaction.go
@@ -0,0 +1,115 @@
+// Copyright 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package internal
+
+// This file implements hooks for applying datastore transactions.
+
+import (
+	"errors"
+	"reflect"
+
+	"github.com/golang/protobuf/proto"
+	netcontext "golang.org/x/net/context"
+
+	basepb "google.golang.org/appengine/internal/base"
+	pb "google.golang.org/appengine/internal/datastore"
+)
+
+var transactionSetters = make(map[reflect.Type]reflect.Value)
+
+// RegisterTransactionSetter registers a function that sets transaction information
+// in a protocol buffer message. f should be a function with two arguments,
+// the first being a protocol buffer type, and the second being *datastore.Transaction.
+func RegisterTransactionSetter(f interface{}) {
+	v := reflect.ValueOf(f)
+	transactionSetters[v.Type().In(0)] = v
+}
+
+// applyTransaction applies the transaction t to message pb
+// by using the relevant setter passed to RegisterTransactionSetter.
+func applyTransaction(pb proto.Message, t *pb.Transaction) {
+	v := reflect.ValueOf(pb)
+	if f, ok := transactionSetters[v.Type()]; ok {
+		f.Call([]reflect.Value{v, reflect.ValueOf(t)})
+	}
+}
+
+var transactionKey = "used for *Transaction"
+
+func transactionFromContext(ctx netcontext.Context) *transaction {
+	t, _ := ctx.Value(&transactionKey).(*transaction)
+	return t
+}
+
+func withTransaction(ctx netcontext.Context, t *transaction) netcontext.Context {
+	return netcontext.WithValue(ctx, &transactionKey, t)
+}
+
+type transaction struct {
+	transaction pb.Transaction
+	finished    bool
+}
+
+var ErrConcurrentTransaction = errors.New("internal: concurrent transaction")
+
+func RunTransactionOnce(c netcontext.Context, f func(netcontext.Context) error, xg bool, readOnly bool, previousTransaction *pb.Transaction) (*pb.Transaction, error) {
+	if transactionFromContext(c) != nil {
+		return nil, errors.New("nested transactions are not supported")
+	}
+
+	// Begin the transaction.
+	t := &transaction{}
+	req := &pb.BeginTransactionRequest{
+		App: proto.String(FullyQualifiedAppID(c)),
+	}
+	if xg {
+		req.AllowMultipleEg = proto.Bool(true)
+	}
+	if previousTransaction != nil {
+		req.PreviousTransaction = previousTransaction
+	}
+	if readOnly {
+		req.Mode = pb.BeginTransactionRequest_READ_ONLY.Enum()
+	} else {
+		req.Mode = pb.BeginTransactionRequest_READ_WRITE.Enum()
+	}
+	if err := Call(c, "datastore_v3", "BeginTransaction", req, &t.transaction); err != nil {
+		return nil, err
+	}
+
+	// Call f, rolling back the transaction if f returns a non-nil error, or panics.
+	// The panic is not recovered.
+	defer func() {
+		if t.finished {
+			return
+		}
+		t.finished = true
+		// Ignore the error return value, since we are already returning a non-nil
+		// error (or we're panicking).
+		Call(c, "datastore_v3", "Rollback", &t.transaction, &basepb.VoidProto{})
+	}()
+	if err := f(withTransaction(c, t)); err != nil {
+		return &t.transaction, err
+	}
+	t.finished = true
+
+	// Commit the transaction.
+	res := &pb.CommitResponse{}
+	err := Call(c, "datastore_v3", "Commit", &t.transaction, res)
+	if ae, ok := err.(*APIError); ok {
+		/* TODO: restore this conditional
+		if appengine.IsDevAppServer() {
+		*/
+		// The Python Dev AppServer raises an ApplicationError with error code 2 (which is
+		// Error.CONCURRENT_TRANSACTION) and message "Concurrency exception.".
+		if ae.Code == int32(pb.Error_BAD_REQUEST) && ae.Detail == "ApplicationError: 2 Concurrency exception." {
+			return &t.transaction, ErrConcurrentTransaction
+		}
+		if ae.Code == int32(pb.Error_CONCURRENT_TRANSACTION) {
+			return &t.transaction, ErrConcurrentTransaction
+		}
+	}
+	return &t.transaction, err
+}
diff --git a/vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.pb.go b/vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.pb.go
new file mode 100644
index 0000000..7c96c9d
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.pb.go
@@ -0,0 +1,433 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto
+
+/*
+Package urlfetch is a generated protocol buffer package.
+
+It is generated from these files:
+	google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto
+
+It has these top-level messages:
+	URLFetchServiceError
+	URLFetchRequest
+	URLFetchResponse
+*/
+package urlfetch
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+type URLFetchServiceError_ErrorCode int32
+
+const (
+	URLFetchServiceError_OK                       URLFetchServiceError_ErrorCode = 0
+	URLFetchServiceError_INVALID_URL              URLFetchServiceError_ErrorCode = 1
+	URLFetchServiceError_FETCH_ERROR              URLFetchServiceError_ErrorCode = 2
+	URLFetchServiceError_UNSPECIFIED_ERROR        URLFetchServiceError_ErrorCode = 3
+	URLFetchServiceError_RESPONSE_TOO_LARGE       URLFetchServiceError_ErrorCode = 4
+	URLFetchServiceError_DEADLINE_EXCEEDED        URLFetchServiceError_ErrorCode = 5
+	URLFetchServiceError_SSL_CERTIFICATE_ERROR    URLFetchServiceError_ErrorCode = 6
+	URLFetchServiceError_DNS_ERROR                URLFetchServiceError_ErrorCode = 7
+	URLFetchServiceError_CLOSED                   URLFetchServiceError_ErrorCode = 8
+	URLFetchServiceError_INTERNAL_TRANSIENT_ERROR URLFetchServiceError_ErrorCode = 9
+	URLFetchServiceError_TOO_MANY_REDIRECTS       URLFetchServiceError_ErrorCode = 10
+	URLFetchServiceError_MALFORMED_REPLY          URLFetchServiceError_ErrorCode = 11
+	URLFetchServiceError_CONNECTION_ERROR         URLFetchServiceError_ErrorCode = 12
+)
+
+var URLFetchServiceError_ErrorCode_name = map[int32]string{
+	0:  "OK",
+	1:  "INVALID_URL",
+	2:  "FETCH_ERROR",
+	3:  "UNSPECIFIED_ERROR",
+	4:  "RESPONSE_TOO_LARGE",
+	5:  "DEADLINE_EXCEEDED",
+	6:  "SSL_CERTIFICATE_ERROR",
+	7:  "DNS_ERROR",
+	8:  "CLOSED",
+	9:  "INTERNAL_TRANSIENT_ERROR",
+	10: "TOO_MANY_REDIRECTS",
+	11: "MALFORMED_REPLY",
+	12: "CONNECTION_ERROR",
+}
+var URLFetchServiceError_ErrorCode_value = map[string]int32{
+	"OK":                       0,
+	"INVALID_URL":              1,
+	"FETCH_ERROR":              2,
+	"UNSPECIFIED_ERROR":        3,
+	"RESPONSE_TOO_LARGE":       4,
+	"DEADLINE_EXCEEDED":        5,
+	"SSL_CERTIFICATE_ERROR":    6,
+	"DNS_ERROR":                7,
+	"CLOSED":                   8,
+	"INTERNAL_TRANSIENT_ERROR": 9,
+	"TOO_MANY_REDIRECTS":       10,
+	"MALFORMED_REPLY":          11,
+	"CONNECTION_ERROR":         12,
+}
+
+func (x URLFetchServiceError_ErrorCode) Enum() *URLFetchServiceError_ErrorCode {
+	p := new(URLFetchServiceError_ErrorCode)
+	*p = x
+	return p
+}
+func (x URLFetchServiceError_ErrorCode) String() string {
+	return proto.EnumName(URLFetchServiceError_ErrorCode_name, int32(x))
+}
+func (x *URLFetchServiceError_ErrorCode) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(URLFetchServiceError_ErrorCode_value, data, "URLFetchServiceError_ErrorCode")
+	if err != nil {
+		return err
+	}
+	*x = URLFetchServiceError_ErrorCode(value)
+	return nil
+}
+func (URLFetchServiceError_ErrorCode) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor0, []int{0, 0}
+}
+
+type URLFetchRequest_RequestMethod int32
+
+const (
+	URLFetchRequest_GET    URLFetchRequest_RequestMethod = 1
+	URLFetchRequest_POST   URLFetchRequest_RequestMethod = 2
+	URLFetchRequest_HEAD   URLFetchRequest_RequestMethod = 3
+	URLFetchRequest_PUT    URLFetchRequest_RequestMethod = 4
+	URLFetchRequest_DELETE URLFetchRequest_RequestMethod = 5
+	URLFetchRequest_PATCH  URLFetchRequest_RequestMethod = 6
+)
+
+var URLFetchRequest_RequestMethod_name = map[int32]string{
+	1: "GET",
+	2: "POST",
+	3: "HEAD",
+	4: "PUT",
+	5: "DELETE",
+	6: "PATCH",
+}
+var URLFetchRequest_RequestMethod_value = map[string]int32{
+	"GET":    1,
+	"POST":   2,
+	"HEAD":   3,
+	"PUT":    4,
+	"DELETE": 5,
+	"PATCH":  6,
+}
+
+func (x URLFetchRequest_RequestMethod) Enum() *URLFetchRequest_RequestMethod {
+	p := new(URLFetchRequest_RequestMethod)
+	*p = x
+	return p
+}
+func (x URLFetchRequest_RequestMethod) String() string {
+	return proto.EnumName(URLFetchRequest_RequestMethod_name, int32(x))
+}
+func (x *URLFetchRequest_RequestMethod) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(URLFetchRequest_RequestMethod_value, data, "URLFetchRequest_RequestMethod")
+	if err != nil {
+		return err
+	}
+	*x = URLFetchRequest_RequestMethod(value)
+	return nil
+}
+func (URLFetchRequest_RequestMethod) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor0, []int{1, 0}
+}
+
+type URLFetchServiceError struct {
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *URLFetchServiceError) Reset()                    { *m = URLFetchServiceError{} }
+func (m *URLFetchServiceError) String() string            { return proto.CompactTextString(m) }
+func (*URLFetchServiceError) ProtoMessage()               {}
+func (*URLFetchServiceError) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+type URLFetchRequest struct {
+	Method                        *URLFetchRequest_RequestMethod `protobuf:"varint,1,req,name=Method,enum=appengine.URLFetchRequest_RequestMethod" json:"Method,omitempty"`
+	Url                           *string                        `protobuf:"bytes,2,req,name=Url" json:"Url,omitempty"`
+	Header                        []*URLFetchRequest_Header      `protobuf:"group,3,rep,name=Header,json=header" json:"header,omitempty"`
+	Payload                       []byte                         `protobuf:"bytes,6,opt,name=Payload" json:"Payload,omitempty"`
+	FollowRedirects               *bool                          `protobuf:"varint,7,opt,name=FollowRedirects,def=1" json:"FollowRedirects,omitempty"`
+	Deadline                      *float64                       `protobuf:"fixed64,8,opt,name=Deadline" json:"Deadline,omitempty"`
+	MustValidateServerCertificate *bool                          `protobuf:"varint,9,opt,name=MustValidateServerCertificate,def=1" json:"MustValidateServerCertificate,omitempty"`
+	XXX_unrecognized              []byte                         `json:"-"`
+}
+
+func (m *URLFetchRequest) Reset()                    { *m = URLFetchRequest{} }
+func (m *URLFetchRequest) String() string            { return proto.CompactTextString(m) }
+func (*URLFetchRequest) ProtoMessage()               {}
+func (*URLFetchRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+
+const Default_URLFetchRequest_FollowRedirects bool = true
+const Default_URLFetchRequest_MustValidateServerCertificate bool = true
+
+func (m *URLFetchRequest) GetMethod() URLFetchRequest_RequestMethod {
+	if m != nil && m.Method != nil {
+		return *m.Method
+	}
+	return URLFetchRequest_GET
+}
+
+func (m *URLFetchRequest) GetUrl() string {
+	if m != nil && m.Url != nil {
+		return *m.Url
+	}
+	return ""
+}
+
+func (m *URLFetchRequest) GetHeader() []*URLFetchRequest_Header {
+	if m != nil {
+		return m.Header
+	}
+	return nil
+}
+
+func (m *URLFetchRequest) GetPayload() []byte {
+	if m != nil {
+		return m.Payload
+	}
+	return nil
+}
+
+func (m *URLFetchRequest) GetFollowRedirects() bool {
+	if m != nil && m.FollowRedirects != nil {
+		return *m.FollowRedirects
+	}
+	return Default_URLFetchRequest_FollowRedirects
+}
+
+func (m *URLFetchRequest) GetDeadline() float64 {
+	if m != nil && m.Deadline != nil {
+		return *m.Deadline
+	}
+	return 0
+}
+
+func (m *URLFetchRequest) GetMustValidateServerCertificate() bool {
+	if m != nil && m.MustValidateServerCertificate != nil {
+		return *m.MustValidateServerCertificate
+	}
+	return Default_URLFetchRequest_MustValidateServerCertificate
+}
+
+type URLFetchRequest_Header struct {
+	Key              *string `protobuf:"bytes,4,req,name=Key" json:"Key,omitempty"`
+	Value            *string `protobuf:"bytes,5,req,name=Value" json:"Value,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *URLFetchRequest_Header) Reset()                    { *m = URLFetchRequest_Header{} }
+func (m *URLFetchRequest_Header) String() string            { return proto.CompactTextString(m) }
+func (*URLFetchRequest_Header) ProtoMessage()               {}
+func (*URLFetchRequest_Header) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 0} }
+
+func (m *URLFetchRequest_Header) GetKey() string {
+	if m != nil && m.Key != nil {
+		return *m.Key
+	}
+	return ""
+}
+
+func (m *URLFetchRequest_Header) GetValue() string {
+	if m != nil && m.Value != nil {
+		return *m.Value
+	}
+	return ""
+}
+
+type URLFetchResponse struct {
+	Content               []byte                     `protobuf:"bytes,1,opt,name=Content" json:"Content,omitempty"`
+	StatusCode            *int32                     `protobuf:"varint,2,req,name=StatusCode" json:"StatusCode,omitempty"`
+	Header                []*URLFetchResponse_Header `protobuf:"group,3,rep,name=Header,json=header" json:"header,omitempty"`
+	ContentWasTruncated   *bool                      `protobuf:"varint,6,opt,name=ContentWasTruncated,def=0" json:"ContentWasTruncated,omitempty"`
+	ExternalBytesSent     *int64                     `protobuf:"varint,7,opt,name=ExternalBytesSent" json:"ExternalBytesSent,omitempty"`
+	ExternalBytesReceived *int64                     `protobuf:"varint,8,opt,name=ExternalBytesReceived" json:"ExternalBytesReceived,omitempty"`
+	FinalUrl              *string                    `protobuf:"bytes,9,opt,name=FinalUrl" json:"FinalUrl,omitempty"`
+	ApiCpuMilliseconds    *int64                     `protobuf:"varint,10,opt,name=ApiCpuMilliseconds,def=0" json:"ApiCpuMilliseconds,omitempty"`
+	ApiBytesSent          *int64                     `protobuf:"varint,11,opt,name=ApiBytesSent,def=0" json:"ApiBytesSent,omitempty"`
+	ApiBytesReceived      *int64                     `protobuf:"varint,12,opt,name=ApiBytesReceived,def=0" json:"ApiBytesReceived,omitempty"`
+	XXX_unrecognized      []byte                     `json:"-"`
+}
+
+func (m *URLFetchResponse) Reset()                    { *m = URLFetchResponse{} }
+func (m *URLFetchResponse) String() string            { return proto.CompactTextString(m) }
+func (*URLFetchResponse) ProtoMessage()               {}
+func (*URLFetchResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+
+const Default_URLFetchResponse_ContentWasTruncated bool = false
+const Default_URLFetchResponse_ApiCpuMilliseconds int64 = 0
+const Default_URLFetchResponse_ApiBytesSent int64 = 0
+const Default_URLFetchResponse_ApiBytesReceived int64 = 0
+
+func (m *URLFetchResponse) GetContent() []byte {
+	if m != nil {
+		return m.Content
+	}
+	return nil
+}
+
+func (m *URLFetchResponse) GetStatusCode() int32 {
+	if m != nil && m.StatusCode != nil {
+		return *m.StatusCode
+	}
+	return 0
+}
+
+func (m *URLFetchResponse) GetHeader() []*URLFetchResponse_Header {
+	if m != nil {
+		return m.Header
+	}
+	return nil
+}
+
+func (m *URLFetchResponse) GetContentWasTruncated() bool {
+	if m != nil && m.ContentWasTruncated != nil {
+		return *m.ContentWasTruncated
+	}
+	return Default_URLFetchResponse_ContentWasTruncated
+}
+
+func (m *URLFetchResponse) GetExternalBytesSent() int64 {
+	if m != nil && m.ExternalBytesSent != nil {
+		return *m.ExternalBytesSent
+	}
+	return 0
+}
+
+func (m *URLFetchResponse) GetExternalBytesReceived() int64 {
+	if m != nil && m.ExternalBytesReceived != nil {
+		return *m.ExternalBytesReceived
+	}
+	return 0
+}
+
+func (m *URLFetchResponse) GetFinalUrl() string {
+	if m != nil && m.FinalUrl != nil {
+		return *m.FinalUrl
+	}
+	return ""
+}
+
+func (m *URLFetchResponse) GetApiCpuMilliseconds() int64 {
+	if m != nil && m.ApiCpuMilliseconds != nil {
+		return *m.ApiCpuMilliseconds
+	}
+	return Default_URLFetchResponse_ApiCpuMilliseconds
+}
+
+func (m *URLFetchResponse) GetApiBytesSent() int64 {
+	if m != nil && m.ApiBytesSent != nil {
+		return *m.ApiBytesSent
+	}
+	return Default_URLFetchResponse_ApiBytesSent
+}
+
+func (m *URLFetchResponse) GetApiBytesReceived() int64 {
+	if m != nil && m.ApiBytesReceived != nil {
+		return *m.ApiBytesReceived
+	}
+	return Default_URLFetchResponse_ApiBytesReceived
+}
+
+type URLFetchResponse_Header struct {
+	Key              *string `protobuf:"bytes,4,req,name=Key" json:"Key,omitempty"`
+	Value            *string `protobuf:"bytes,5,req,name=Value" json:"Value,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *URLFetchResponse_Header) Reset()                    { *m = URLFetchResponse_Header{} }
+func (m *URLFetchResponse_Header) String() string            { return proto.CompactTextString(m) }
+func (*URLFetchResponse_Header) ProtoMessage()               {}
+func (*URLFetchResponse_Header) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2, 0} }
+
+func (m *URLFetchResponse_Header) GetKey() string {
+	if m != nil && m.Key != nil {
+		return *m.Key
+	}
+	return ""
+}
+
+func (m *URLFetchResponse_Header) GetValue() string {
+	if m != nil && m.Value != nil {
+		return *m.Value
+	}
+	return ""
+}
+
+func init() {
+	proto.RegisterType((*URLFetchServiceError)(nil), "appengine.URLFetchServiceError")
+	proto.RegisterType((*URLFetchRequest)(nil), "appengine.URLFetchRequest")
+	proto.RegisterType((*URLFetchRequest_Header)(nil), "appengine.URLFetchRequest.Header")
+	proto.RegisterType((*URLFetchResponse)(nil), "appengine.URLFetchResponse")
+	proto.RegisterType((*URLFetchResponse_Header)(nil), "appengine.URLFetchResponse.Header")
+}
+
+func init() {
+	proto.RegisterFile("google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto", fileDescriptor0)
+}
+
+var fileDescriptor0 = []byte{
+	// 770 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xdd, 0x6e, 0xe3, 0x54,
+	0x10, 0xc6, 0x76, 0x7e, 0xa7, 0x5d, 0x7a, 0x76, 0xb6, 0x45, 0x66, 0xb5, 0xa0, 0x10, 0x09, 0x29,
+	0x17, 0x90, 0x2e, 0x2b, 0x24, 0x44, 0xaf, 0x70, 0xed, 0x93, 0xad, 0xa9, 0x63, 0x47, 0xc7, 0x4e,
+	0x61, 0xb9, 0xb1, 0xac, 0x78, 0x9a, 0x5a, 0xb2, 0xec, 0x60, 0x9f, 0x2c, 0xf4, 0x35, 0x78, 0x0d,
+	0xde, 0x87, 0xa7, 0xe1, 0x02, 0x9d, 0xc4, 0xc9, 0x6e, 0xbb, 0xd1, 0x4a, 0x5c, 0x65, 0xe6, 0x9b,
+	0xef, 0xcc, 0x99, 0x7c, 0xdf, 0xf8, 0x80, 0xb3, 0x2c, 0xcb, 0x65, 0x4e, 0xe3, 0x65, 0x99, 0x27,
+	0xc5, 0x72, 0x5c, 0x56, 0xcb, 0xf3, 0x64, 0xb5, 0xa2, 0x62, 0x99, 0x15, 0x74, 0x9e, 0x15, 0x92,
+	0xaa, 0x22, 0xc9, 0xcf, 0xd7, 0x55, 0x7e, 0x4b, 0x72, 0x71, 0xb7, 0x0f, 0xe2, 0x9a, 0xaa, 0xb7,
+	0xd9, 0x82, 0xc6, 0xab, 0xaa, 0x94, 0x25, 0xf6, 0xf7, 0x67, 0x86, 0x7f, 0xeb, 0x70, 0x3a, 0x17,
+	0xde, 0x44, 0xb1, 0xc2, 0x2d, 0x89, 0x57, 0x55, 0x59, 0x0d, 0xff, 0xd2, 0xa1, 0xbf, 0x89, 0xec,
+	0x32, 0x25, 0xec, 0x80, 0x1e, 0x5c, 0xb3, 0x4f, 0xf0, 0x04, 0x8e, 0x5c, 0xff, 0xc6, 0xf2, 0x5c,
+	0x27, 0x9e, 0x0b, 0x8f, 0x69, 0x0a, 0x98, 0xf0, 0xc8, 0xbe, 0x8a, 0xb9, 0x10, 0x81, 0x60, 0x3a,
+	0x9e, 0xc1, 0xd3, 0xb9, 0x1f, 0xce, 0xb8, 0xed, 0x4e, 0x5c, 0xee, 0x34, 0xb0, 0x81, 0x9f, 0x01,
+	0x0a, 0x1e, 0xce, 0x02, 0x3f, 0xe4, 0x71, 0x14, 0x04, 0xb1, 0x67, 0x89, 0xd7, 0x9c, 0xb5, 0x14,
+	0xdd, 0xe1, 0x96, 0xe3, 0xb9, 0x3e, 0x8f, 0xf9, 0xaf, 0x36, 0xe7, 0x0e, 0x77, 0x58, 0x1b, 0x3f,
+	0x87, 0xb3, 0x30, 0xf4, 0x62, 0x9b, 0x8b, 0xc8, 0x9d, 0xb8, 0xb6, 0x15, 0xf1, 0xa6, 0x53, 0x07,
+	0x9f, 0x40, 0xdf, 0xf1, 0xc3, 0x26, 0xed, 0x22, 0x40, 0xc7, 0xf6, 0x82, 0x90, 0x3b, 0xac, 0x87,
+	0x2f, 0xc0, 0x74, 0xfd, 0x88, 0x0b, 0xdf, 0xf2, 0xe2, 0x48, 0x58, 0x7e, 0xe8, 0x72, 0x3f, 0x6a,
+	0x98, 0x7d, 0x35, 0x82, 0xba, 0x79, 0x6a, 0xf9, 0x6f, 0x62, 0xc1, 0x1d, 0x57, 0x70, 0x3b, 0x0a,
+	0x19, 0xe0, 0x33, 0x38, 0x99, 0x5a, 0xde, 0x24, 0x10, 0x53, 0xee, 0xc4, 0x82, 0xcf, 0xbc, 0x37,
+	0xec, 0x08, 0x4f, 0x81, 0xd9, 0x81, 0xef, 0x73, 0x3b, 0x72, 0x03, 0xbf, 0x69, 0x71, 0x3c, 0xfc,
+	0xc7, 0x80, 0x93, 0x9d, 0x5a, 0x82, 0x7e, 0x5f, 0x53, 0x2d, 0xf1, 0x27, 0xe8, 0x4c, 0x49, 0xde,
+	0x95, 0xa9, 0xa9, 0x0d, 0xf4, 0xd1, 0xa7, 0xaf, 0x46, 0xe3, 0xbd, 0xba, 0xe3, 0x47, 0xdc, 0x71,
+	0xf3, 0xbb, 0xe5, 0x8b, 0xe6, 0x1c, 0x32, 0x30, 0xe6, 0x55, 0x6e, 0xea, 0x03, 0x7d, 0xd4, 0x17,
+	0x2a, 0xc4, 0x1f, 0xa1, 0x73, 0x47, 0x49, 0x4a, 0x95, 0x69, 0x0c, 0x8c, 0x11, 0xbc, 0xfa, 0xea,
+	0x23, 0x3d, 0xaf, 0x36, 0x44, 0xd1, 0x1c, 0xc0, 0x17, 0xd0, 0x9d, 0x25, 0xf7, 0x79, 0x99, 0xa4,
+	0x66, 0x67, 0xa0, 0x8d, 0x8e, 0x2f, 0xf5, 0x9e, 0x26, 0x76, 0x10, 0x8e, 0xe1, 0x64, 0x52, 0xe6,
+	0x79, 0xf9, 0x87, 0xa0, 0x34, 0xab, 0x68, 0x21, 0x6b, 0xb3, 0x3b, 0xd0, 0x46, 0xbd, 0x8b, 0x96,
+	0xac, 0xd6, 0x24, 0x1e, 0x17, 0xf1, 0x39, 0xf4, 0x1c, 0x4a, 0xd2, 0x3c, 0x2b, 0xc8, 0xec, 0x0d,
+	0xb4, 0x91, 0x26, 0xf6, 0x39, 0xfe, 0x0c, 0x5f, 0x4c, 0xd7, 0xb5, 0xbc, 0x49, 0xf2, 0x2c, 0x4d,
+	0x24, 0xa9, 0xed, 0xa1, 0xca, 0xa6, 0x4a, 0x66, 0xb7, 0xd9, 0x22, 0x91, 0x64, 0xf6, 0xdf, 0xeb,
+	0xfc, 0x71, 0xea, 0xf3, 0x97, 0xd0, 0xd9, 0xfe, 0x0f, 0x25, 0xc6, 0x35, 0xdd, 0x9b, 0xad, 0xad,
+	0x18, 0xd7, 0x74, 0x8f, 0xa7, 0xd0, 0xbe, 0x49, 0xf2, 0x35, 0x99, 0xed, 0x0d, 0xb6, 0x4d, 0x86,
+	0x1e, 0x3c, 0x79, 0xa0, 0x26, 0x76, 0xc1, 0x78, 0xcd, 0x23, 0xa6, 0x61, 0x0f, 0x5a, 0xb3, 0x20,
+	0x8c, 0x98, 0xae, 0xa2, 0x2b, 0x6e, 0x39, 0xcc, 0x50, 0xc5, 0xd9, 0x3c, 0x62, 0x2d, 0xb5, 0x2e,
+	0x0e, 0xf7, 0x78, 0xc4, 0x59, 0x1b, 0xfb, 0xd0, 0x9e, 0x59, 0x91, 0x7d, 0xc5, 0x3a, 0xc3, 0x7f,
+	0x0d, 0x60, 0xef, 0x84, 0xad, 0x57, 0x65, 0x51, 0x13, 0x9a, 0xd0, 0xb5, 0xcb, 0x42, 0x52, 0x21,
+	0x4d, 0x4d, 0x49, 0x29, 0x76, 0x29, 0x7e, 0x09, 0x10, 0xca, 0x44, 0xae, 0x6b, 0xf5, 0x71, 0x6c,
+	0x8c, 0x6b, 0x8b, 0xf7, 0x10, 0xbc, 0x78, 0xe4, 0xdf, 0xf0, 0xa0, 0x7f, 0xdb, 0x6b, 0x1e, 0x1b,
+	0xf8, 0x03, 0x3c, 0x6b, 0xae, 0xf9, 0x25, 0xa9, 0xa3, 0x6a, 0x5d, 0x28, 0x81, 0xb6, 0x66, 0xf6,
+	0x2e, 0xda, 0xb7, 0x49, 0x5e, 0x93, 0x38, 0xc4, 0xc0, 0x6f, 0xe0, 0x29, 0xff, 0x73, 0xfb, 0x02,
+	0x5c, 0xde, 0x4b, 0xaa, 0x43, 0x35, 0xb8, 0x72, 0xd7, 0x10, 0x1f, 0x16, 0xf0, 0x7b, 0x38, 0x7b,
+	0x00, 0x0a, 0x5a, 0x50, 0xf6, 0x96, 0xd2, 0x8d, 0xcd, 0x86, 0x38, 0x5c, 0x54, 0xfb, 0x30, 0xc9,
+	0x8a, 0x24, 0x57, 0xfb, 0xaa, 0xec, 0xed, 0x8b, 0x7d, 0x8e, 0xdf, 0x01, 0x5a, 0xab, 0xcc, 0x5e,
+	0xad, 0xa7, 0x59, 0x9e, 0x67, 0x35, 0x2d, 0xca, 0x22, 0xad, 0x4d, 0x50, 0xed, 0x2e, 0xb4, 0x97,
+	0xe2, 0x40, 0x11, 0xbf, 0x86, 0x63, 0x6b, 0x95, 0xbd, 0x9b, 0xf6, 0x68, 0x47, 0x7e, 0x00, 0xe3,
+	0xb7, 0xc0, 0x76, 0xf9, 0x7e, 0xcc, 0xe3, 0x1d, 0xf5, 0x83, 0xd2, 0xff, 0x5f, 0xa6, 0x4b, 0xf8,
+	0xad, 0xb7, 0x7b, 0x2a, 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x1d, 0x9f, 0x6d, 0x24, 0x63, 0x05,
+	0x00, 0x00,
+}
diff --git a/vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto b/vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto
new file mode 100644
index 0000000..f695edf
--- /dev/null
+++ b/vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto
@@ -0,0 +1,64 @@
+syntax = "proto2";
+option go_package = "urlfetch";
+
+package appengine;
+
+message URLFetchServiceError {
+  enum ErrorCode {
+    OK = 0;
+    INVALID_URL = 1;
+    FETCH_ERROR = 2;
+    UNSPECIFIED_ERROR = 3;
+    RESPONSE_TOO_LARGE = 4;
+    DEADLINE_EXCEEDED = 5;
+    SSL_CERTIFICATE_ERROR = 6;
+    DNS_ERROR = 7;
+    CLOSED = 8;
+    INTERNAL_TRANSIENT_ERROR = 9;
+    TOO_MANY_REDIRECTS = 10;
+    MALFORMED_REPLY = 11;
+    CONNECTION_ERROR = 12;
+  }
+}
+
+message URLFetchRequest {
+  enum RequestMethod {
+    GET = 1;
+    POST = 2;
+    HEAD = 3;
+    PUT = 4;
+    DELETE = 5;
+    PATCH = 6;
+  }
+  required RequestMethod Method = 1;
+  required string Url = 2;
+  repeated group Header = 3 {
+    required string Key = 4;
+    required string Value = 5;
+  }
+  optional bytes Payload = 6 [ctype=CORD];
+
+  optional bool FollowRedirects = 7 [default=true];
+
+  optional double Deadline = 8;
+
+  optional bool MustValidateServerCertificate = 9 [default=true];
+}
+
+message URLFetchResponse {
+  optional bytes Content = 1;
+  required int32 StatusCode = 2;
+  repeated group Header = 3 {
+    required string Key = 4;
+    required string Value = 5;
+  }
+  optional bool ContentWasTruncated = 6 [default=false];
+  optional int64 ExternalBytesSent = 7;
+  optional int64 ExternalBytesReceived = 8;
+
+  optional string FinalUrl = 9;
+
+  optional int64 ApiCpuMilliseconds = 10 [default=0];
+  optional int64 ApiBytesSent = 11 [default=0];
+  optional int64 ApiBytesReceived = 12 [default=0];
+}
diff --git a/vendor/google.golang.org/appengine/urlfetch/urlfetch.go b/vendor/google.golang.org/appengine/urlfetch/urlfetch.go
new file mode 100644
index 0000000..6ffe1e6
--- /dev/null
+++ b/vendor/google.golang.org/appengine/urlfetch/urlfetch.go
@@ -0,0 +1,210 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// Package urlfetch provides an http.RoundTripper implementation
+// for fetching URLs via App Engine's urlfetch service.
+package urlfetch // import "google.golang.org/appengine/urlfetch"
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/golang/protobuf/proto"
+	"golang.org/x/net/context"
+
+	"google.golang.org/appengine/internal"
+	pb "google.golang.org/appengine/internal/urlfetch"
+)
+
+// Transport is an implementation of http.RoundTripper for
+// App Engine. Users should generally create an http.Client using
+// this transport and use the Client rather than using this transport
+// directly.
+type Transport struct {
+	Context context.Context
+
+	// Controls whether the application checks the validity of SSL certificates
+	// over HTTPS connections. A value of false (the default) instructs the
+	// application to send a request to the server only if the certificate is
+	// valid and signed by a trusted certificate authority (CA), and also
+	// includes a hostname that matches the certificate. A value of true
+	// instructs the application to perform no certificate validation.
+	AllowInvalidServerCertificate bool
+}
+
+// Verify statically that *Transport implements http.RoundTripper.
+var _ http.RoundTripper = (*Transport)(nil)
+
+// Client returns an *http.Client using a default urlfetch Transport. This
+// client will have the default deadline of 5 seconds, and will check the
+// validity of SSL certificates.
+//
+// Any deadline of the provided context will be used for requests through this client;
+// if the client does not have a deadline then a 5 second default is used.
+func Client(ctx context.Context) *http.Client {
+	return &http.Client{
+		Transport: &Transport{
+			Context: ctx,
+		},
+	}
+}
+
+type bodyReader struct {
+	content   []byte
+	truncated bool
+	closed    bool
+}
+
+// ErrTruncatedBody is the error returned after the final Read() from a
+// response's Body if the body has been truncated by App Engine's proxy.
+var ErrTruncatedBody = errors.New("urlfetch: truncated body")
+
+func statusCodeToText(code int) string {
+	if t := http.StatusText(code); t != "" {
+		return t
+	}
+	return strconv.Itoa(code)
+}
+
+func (br *bodyReader) Read(p []byte) (n int, err error) {
+	if br.closed {
+		if br.truncated {
+			return 0, ErrTruncatedBody
+		}
+		return 0, io.EOF
+	}
+	n = copy(p, br.content)
+	if n > 0 {
+		br.content = br.content[n:]
+		return
+	}
+	if br.truncated {
+		br.closed = true
+		return 0, ErrTruncatedBody
+	}
+	return 0, io.EOF
+}
+
+func (br *bodyReader) Close() error {
+	br.closed = true
+	br.content = nil
+	return nil
+}
+
+// A map of the URL Fetch-accepted methods that take a request body.
+var methodAcceptsRequestBody = map[string]bool{
+	"POST":  true,
+	"PUT":   true,
+	"PATCH": true,
+}
+
+// urlString returns a valid string given a URL. This function is necessary because
+// the String method of URL doesn't correctly handle URLs with non-empty Opaque values.
+// See http://code.google.com/p/go/issues/detail?id=4860.
+func urlString(u *url.URL) string {
+	if u.Opaque == "" || strings.HasPrefix(u.Opaque, "//") {
+		return u.String()
+	}
+	aux := *u
+	aux.Opaque = "//" + aux.Host + aux.Opaque
+	return aux.String()
+}
+
+// RoundTrip issues a single HTTP request and returns its response. Per the
+// http.RoundTripper interface, RoundTrip only returns an error if there
+// was an unsupported request or the URL Fetch proxy fails.
+// Note that HTTP response codes such as 5xx, 403, 404, etc are not
+// errors as far as the transport is concerned and will be returned
+// with err set to nil.
+func (t *Transport) RoundTrip(req *http.Request) (res *http.Response, err error) {
+	methNum, ok := pb.URLFetchRequest_RequestMethod_value[req.Method]
+	if !ok {
+		return nil, fmt.Errorf("urlfetch: unsupported HTTP method %q", req.Method)
+	}
+
+	method := pb.URLFetchRequest_RequestMethod(methNum)
+
+	freq := &pb.URLFetchRequest{
+		Method:                        &method,
+		Url:                           proto.String(urlString(req.URL)),
+		FollowRedirects:               proto.Bool(false), // http.Client's responsibility
+		MustValidateServerCertificate: proto.Bool(!t.AllowInvalidServerCertificate),
+	}
+	if deadline, ok := t.Context.Deadline(); ok {
+		freq.Deadline = proto.Float64(deadline.Sub(time.Now()).Seconds())
+	}
+
+	for k, vals := range req.Header {
+		for _, val := range vals {
+			freq.Header = append(freq.Header, &pb.URLFetchRequest_Header{
+				Key:   proto.String(k),
+				Value: proto.String(val),
+			})
+		}
+	}
+	if methodAcceptsRequestBody[req.Method] && req.Body != nil {
+		// Avoid a []byte copy if req.Body has a Bytes method.
+		switch b := req.Body.(type) {
+		case interface {
+			Bytes() []byte
+		}:
+			freq.Payload = b.Bytes()
+		default:
+			freq.Payload, err = ioutil.ReadAll(req.Body)
+			if err != nil {
+				return nil, err
+			}
+		}
+	}
+
+	fres := &pb.URLFetchResponse{}
+	if err := internal.Call(t.Context, "urlfetch", "Fetch", freq, fres); err != nil {
+		return nil, err
+	}
+
+	res = &http.Response{}
+	res.StatusCode = int(*fres.StatusCode)
+	res.Status = fmt.Sprintf("%d %s", res.StatusCode, statusCodeToText(res.StatusCode))
+	res.Header = make(http.Header)
+	res.Request = req
+
+	// Faked:
+	res.ProtoMajor = 1
+	res.ProtoMinor = 1
+	res.Proto = "HTTP/1.1"
+	res.Close = true
+
+	for _, h := range fres.Header {
+		hkey := http.CanonicalHeaderKey(*h.Key)
+		hval := *h.Value
+		if hkey == "Content-Length" {
+			// Will get filled in below for all but HEAD requests.
+			if req.Method == "HEAD" {
+				res.ContentLength, _ = strconv.ParseInt(hval, 10, 64)
+			}
+			continue
+		}
+		res.Header.Add(hkey, hval)
+	}
+
+	if req.Method != "HEAD" {
+		res.ContentLength = int64(len(fres.Content))
+	}
+
+	truncated := fres.GetContentWasTruncated()
+	res.Body = &bodyReader{content: fres.Content, truncated: truncated}
+	return
+}
+
+func init() {
+	internal.RegisterErrorCodeMap("urlfetch", pb.URLFetchServiceError_ErrorCode_name)
+	internal.RegisterTimeoutErrorCode("urlfetch", int32(pb.URLFetchServiceError_DEADLINE_EXCEEDED))
+}
diff --git a/vendor/gopkg.in/go-playground/webhooks.v3/.gitignore b/vendor/gopkg.in/go-playground/webhooks.v3/.gitignore
new file mode 100644
index 0000000..0c9e329
--- /dev/null
+++ b/vendor/gopkg.in/go-playground/webhooks.v3/.gitignore
@@ -0,0 +1,25 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+.idea
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/gopkg.in/go-playground/webhooks.v3/.travis.yml b/vendor/gopkg.in/go-playground/webhooks.v3/.travis.yml
new file mode 100644
index 0000000..42a68b4
--- /dev/null
+++ b/vendor/gopkg.in/go-playground/webhooks.v3/.travis.yml
@@ -0,0 +1,38 @@
+language: go
+go:
+    - 1.10.2
+    - tip
+matrix:
+  allow_failures:
+    - go: tip
+
+notifications:
+    email:
+        recipients: dean.karn@gmail.com
+        on_success: change
+        on_failure: always
+
+before_install:
+  - go get -u github.com/go-playground/overalls
+  - go get -u github.com/mattn/goveralls
+  - go get -u golang.org/x/tools/cmd/cover
+  - go get -u github.com/golang/lint/golint
+  - go get -u github.com/gordonklaus/ineffassign
+  - mkdir -p $GOPATH/src/gopkg.in
+  - ln -s $GOPATH/src/github.com/$TRAVIS_REPO_SLUG $GOPATH/src/gopkg.in/webhooks.v2
+  - ln -s $GOPATH/src/github.com/$TRAVIS_REPO_SLUG $GOPATH/src/gopkg.in/webhooks.v3
+
+before_script:
+  - go vet ./...
+
+script:
+ - gofmt -d -s .
+ - golint ./...
+ - ineffassign ./
+ - go test -v ./...
+ - go test -race
+
+after_success: |
+  [ $TRAVIS_GO_VERSION = 1.10.2 ] &&
+  overalls -project="github.com/go-playground/webhooks" -covermode=count -ignore=.git,examples -debug &&
+  goveralls -coverprofile=overalls.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN
\ No newline at end of file
diff --git a/vendor/gopkg.in/go-playground/webhooks.v3/LICENSE b/vendor/gopkg.in/go-playground/webhooks.v3/LICENSE
new file mode 100644
index 0000000..6a2ae9a
--- /dev/null
+++ b/vendor/gopkg.in/go-playground/webhooks.v3/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Dean Karn
+
+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/gopkg.in/go-playground/webhooks.v3/README.md b/vendor/gopkg.in/go-playground/webhooks.v3/README.md
new file mode 100644
index 0000000..53bac37
--- /dev/null
+++ b/vendor/gopkg.in/go-playground/webhooks.v3/README.md
@@ -0,0 +1,155 @@
+Library webhooks
+================
+<img align="right" src="https://raw.githubusercontent.com/go-playground/webhooks/v3/logo.png">![Project status](https://img.shields.io/badge/version-3.13.0-green.svg)
+[![Build Status](https://travis-ci.org/go-playground/webhooks.svg?branch=v3)](https://travis-ci.org/go-playground/webhooks)
+[![Coverage Status](https://coveralls.io/repos/go-playground/webhooks/badge.svg?branch=v3&service=github)](https://coveralls.io/github/go-playground/webhooks?branch=v3)
+[![Go Report Card](https://goreportcard.com/badge/go-playground/webhooks)](https://goreportcard.com/report/go-playground/webhooks)
+[![GoDoc](https://godoc.org/gopkg.in/go-playground/webhooks.v3?status.svg)](https://godoc.org/gopkg.in/go-playground/webhooks.v3)
+![License](https://img.shields.io/dub/l/vibe-d.svg)
+
+Library webhooks allows for easy receiving and parsing of GitHub, Bitbucket and GitLab Webhook Events
+
+Features:
+
+* Parses the entire payload, not just a few fields.
+* Fields + Schema directly lines up with webhook posted json
+
+Notes:
+
+* Currently only accepting json payloads.
+
+Installation
+------------
+
+Use go get.
+
+```shell
+go get -u gopkg.in/go-playground/webhooks.v3
+```
+
+Then import the package into your own code.
+
+	import "gopkg.in/go-playground/webhooks.v3"
+
+Usage and Documentation
+------
+
+Please see http://godoc.org/gopkg.in/go-playground/webhooks.v3 for detailed usage docs.
+
+##### Examples:
+
+Multiple Handlers for each event you subscribe to
+```go
+package main
+
+import (
+	"fmt"
+	"strconv"
+
+	"gopkg.in/go-playground/webhooks.v3"
+	"gopkg.in/go-playground/webhooks.v3/github"
+)
+
+const (
+	path = "/webhooks"
+	port = 3016
+)
+
+func main() {
+
+	hook := github.New(&github.Config{Secret: "MyGitHubSuperSecretSecrect...?"})
+	hook.RegisterEvents(HandleRelease, github.ReleaseEvent)
+	hook.RegisterEvents(HandlePullRequest, github.PullRequestEvent)
+
+	err := webhooks.Run(hook, ":"+strconv.Itoa(port), path)
+	if err != nil {
+		fmt.Println(err)
+	}
+}
+
+// HandleRelease handles GitHub release events
+func HandleRelease(payload interface{}, header webhooks.Header) {
+
+	fmt.Println("Handling Release")
+
+	pl := payload.(github.ReleasePayload)
+
+	// only want to compile on full releases
+	if pl.Release.Draft || pl.Release.Prerelease || pl.Release.TargetCommitish != "master" {
+		return
+	}
+
+	// Do whatever you want from here...
+	fmt.Printf("%+v", pl)
+}
+
+// HandlePullRequest handles GitHub pull_request events
+func HandlePullRequest(payload interface{}, header webhooks.Header) {
+
+	fmt.Println("Handling Pull Request")
+
+	pl := payload.(github.PullRequestPayload)
+
+	// Do whatever you want from here...
+	fmt.Printf("%+v", pl)
+}
+```
+
+Single receiver for events you subscribe to
+```go
+package main
+
+import (
+	"fmt"
+	"strconv"
+
+	"gopkg.in/go-playground/webhooks.v3"
+	"gopkg.in/go-playground/webhooks.v3/github"
+)
+
+const (
+	path = "/webhooks"
+	port = 3016
+)
+
+func main() {
+
+	hook := github.New(&github.Config{Secret: "MyGitHubSuperSecretSecrect...?"})
+	hook.RegisterEvents(HandleMultiple, github.ReleaseEvent, github.PullRequestEvent) // Add as many as you want
+
+	err := webhooks.Run(hook, ":"+strconv.Itoa(port), path)
+	if err != nil {
+		fmt.Println(err)
+	}
+}
+
+// HandleMultiple handles multiple GitHub events
+func HandleMultiple(payload interface{}, header webhooks.Header) {
+
+	fmt.Println("Handling Payload..")
+
+	switch payload.(type) {
+
+	case github.ReleasePayload:
+		release := payload.(github.ReleasePayload)
+		// Do whatever you want from here...
+		fmt.Printf("%+v", release)
+
+	case github.PullRequestPayload:
+		pullRequest := payload.(github.PullRequestPayload)
+		// Do whatever you want from here...
+		fmt.Printf("%+v", pullRequest)
+	}
+}
+```
+
+Contributing
+------
+
+Pull requests for other services are welcome!
+
+If the changes being proposed or requested are breaking changes, please create an issue for discussion.
+
+License
+------
+Distributed under MIT License, please see license file in code for more details.
diff --git a/vendor/gopkg.in/go-playground/webhooks.v3/gitlab/gitlab.go b/vendor/gopkg.in/go-playground/webhooks.v3/gitlab/gitlab.go
new file mode 100644
index 0000000..cac3a3e
--- /dev/null
+++ b/vendor/gopkg.in/go-playground/webhooks.v3/gitlab/gitlab.go
@@ -0,0 +1,157 @@
+package gitlab
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+
+	"gopkg.in/go-playground/webhooks.v3"
+)
+
+// Webhook instance contains all methods needed to process events
+type Webhook struct {
+	provider   webhooks.Provider
+	secret     string
+	eventFuncs map[Event]webhooks.ProcessPayloadFunc
+}
+
+// Config defines the configuration to create a new GitHub Webhook instance
+type Config struct {
+	Secret string
+}
+
+// Event defines a GitHub hook event type
+type Event string
+
+// GitLab hook types
+const (
+	PushEvents               Event = "Push Hook"
+	TagEvents                Event = "Tag Push Hook"
+	IssuesEvents             Event = "Issue Hook"
+	ConfidentialIssuesEvents Event = "Confidential Issue Hook"
+	CommentEvents            Event = "Note Hook"
+	MergeRequestEvents       Event = "Merge Request Hook"
+	WikiPageEvents           Event = "Wiki Page Hook"
+	PipelineEvents           Event = "Pipeline Hook"
+	BuildEvents              Event = "Build Hook"
+)
+
+// New creates and returns a WebHook instance denoted by the Provider type
+func New(config *Config) *Webhook {
+	return &Webhook{
+		provider:   webhooks.GitLab,
+		secret:     config.Secret,
+		eventFuncs: map[Event]webhooks.ProcessPayloadFunc{},
+	}
+}
+
+// Provider returns the current hooks provider ID
+func (hook Webhook) Provider() webhooks.Provider {
+	return hook.provider
+}
+
+// RegisterEvents registers the function to call when the specified event(s) are encountered
+func (hook Webhook) RegisterEvents(fn webhooks.ProcessPayloadFunc, events ...Event) {
+
+	for _, event := range events {
+		hook.eventFuncs[event] = fn
+	}
+}
+
+// ParsePayload parses and verifies the payload and fires off the mapped function, if it exists.
+func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) {
+	webhooks.DefaultLog.Info("Parsing Payload...")
+
+	event := r.Header.Get("X-Gitlab-Event")
+	if len(event) == 0 {
+		webhooks.DefaultLog.Error("Missing X-Gitlab-Event Header")
+		http.Error(w, "400 Bad Request - Missing X-Gitlab-Event Header", http.StatusBadRequest)
+		return
+	}
+	webhooks.DefaultLog.Debug(fmt.Sprintf("X-Gitlab-Event:%s", event))
+
+	gitLabEvent := Event(event)
+
+	fn, ok := hook.eventFuncs[gitLabEvent]
+	// if no event registered
+	if !ok {
+		webhooks.DefaultLog.Info(fmt.Sprintf("Webhook Event %s not registered, it is recommended to setup only events in gitlab that will be registered in the webhook to avoid unnecessary traffic and reduce potential attack vectors.", event))
+		return
+	}
+
+	payload, err := ioutil.ReadAll(r.Body)
+	if err != nil || len(payload) == 0 {
+		webhooks.DefaultLog.Error("Issue reading Payload")
+		http.Error(w, "Error reading Payload", http.StatusInternalServerError)
+		return
+	}
+	webhooks.DefaultLog.Debug(fmt.Sprintf("Payload:%s", string(payload)))
+
+	// If we have a Secret set, we should check the MAC
+	if len(hook.secret) > 0 {
+		webhooks.DefaultLog.Info("Checking secret")
+		signature := r.Header.Get("X-Gitlab-Token")
+		if signature != hook.secret {
+			webhooks.DefaultLog.Error(fmt.Sprintf("Invalid X-Gitlab-Token of '%s'", signature))
+			http.Error(w, "403 Forbidden - Token missmatch", http.StatusForbidden)
+			return
+		}
+	}
+
+	// Make headers available to ProcessPayloadFunc as a webhooks type
+	hd := webhooks.Header(r.Header)
+
+	switch gitLabEvent {
+	case PushEvents:
+		var pe PushEventPayload
+		json.Unmarshal([]byte(payload), &pe)
+		hook.runProcessPayloadFunc(fn, pe, hd)
+
+	case TagEvents:
+		var te TagEventPayload
+		json.Unmarshal([]byte(payload), &te)
+		hook.runProcessPayloadFunc(fn, te, hd)
+
+	case ConfidentialIssuesEvents:
+		var cie ConfidentialIssueEventPayload
+		json.Unmarshal([]byte(payload), &cie)
+		hook.runProcessPayloadFunc(fn, cie, hd)
+
+	case IssuesEvents:
+		var ie IssueEventPayload
+		json.Unmarshal([]byte(payload), &ie)
+		hook.runProcessPayloadFunc(fn, ie, hd)
+
+	case CommentEvents:
+		var ce CommentEventPayload
+		json.Unmarshal([]byte(payload), &ce)
+		hook.runProcessPayloadFunc(fn, ce, hd)
+
+	case MergeRequestEvents:
+		var mre MergeRequestEventPayload
+		json.Unmarshal([]byte(payload), &mre)
+		hook.runProcessPayloadFunc(fn, mre, hd)
+
+	case WikiPageEvents:
+		var wpe WikiPageEventPayload
+		json.Unmarshal([]byte(payload), &wpe)
+		hook.runProcessPayloadFunc(fn, wpe, hd)
+
+	case PipelineEvents:
+		var pe PipelineEventPayload
+		json.Unmarshal([]byte(payload), &pe)
+		hook.runProcessPayloadFunc(fn, pe, hd)
+
+	case BuildEvents:
+		var be BuildEventPayload
+		json.Unmarshal([]byte(payload), &be)
+		hook.runProcessPayloadFunc(fn, be, hd)
+	}
+}
+
+func (hook Webhook) runProcessPayloadFunc(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
+	go func(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) {
+		fn(results, header)
+	}(fn, results, header)
+}
diff --git a/vendor/gopkg.in/go-playground/webhooks.v3/gitlab/payload.go b/vendor/gopkg.in/go-playground/webhooks.v3/gitlab/payload.go
new file mode 100644
index 0000000..bec1afc
--- /dev/null
+++ b/vendor/gopkg.in/go-playground/webhooks.v3/gitlab/payload.go
@@ -0,0 +1,440 @@
+package gitlab
+
+import (
+	"strings"
+	"time"
+)
+
+type customTime struct {
+	time.Time
+}
+
+func (t *customTime) UnmarshalJSON(b []byte) (err error) {
+	layout := []string{
+		"2006-01-02 15:04:05 MST",
+		"2006-01-02 15:04:05 Z07:00",
+		"2006-01-02 15:04:05 Z0700",
+		time.RFC3339,
+	}
+	s := strings.Trim(string(b), "\"")
+	if s == "null" {
+		t.Time = time.Time{}
+		return
+	}
+	for _, l := range layout {
+		t.Time, err = time.Parse(l, s)
+		if err == nil {
+			break
+		}
+	}
+	return
+}
+
+// IssueEventPayload contains the information for GitLab's issue event
+type IssueEventPayload struct {
+	ObjectKind       string           `json:"object_kind"`
+	User             User             `json:"user"`
+	Project          Project          `json:"project"`
+	Repository       Repository       `json:"repository"`
+	ObjectAttributes ObjectAttributes `json:"object_attributes"`
+	Assignee         Assignee         `json:"assignee"`
+	Changes          Changes          `json:"changes"`
+}
+
+// ConfidentialIssueEventPayload contains the information for GitLab's confidential issue event
+type ConfidentialIssueEventPayload struct {
+	// The data for confidential issues is currently the same as normal issues,
+	// so we can just embed the normal issue payload type here.
+	IssueEventPayload
+}
+
+// MergeRequestEventPayload contains the information for GitLab's merge request event
+type MergeRequestEventPayload struct {
+	ObjectKind       string           `json:"object_kind"`
+	User             User             `json:"user"`
+	ObjectAttributes ObjectAttributes `json:"object_attributes"`
+	Changes          Changes          `json:"changes"`
+	Project          Project          `json:"project"`
+	Repository       Repository       `json:"repository"`
+}
+
+// PushEventPayload contains the information for GitLab's push event
+type PushEventPayload struct {
+	ObjectKind        string     `json:"object_kind"`
+	Before            string     `json:"before"`
+	After             string     `json:"after"`
+	Ref               string     `json:"ref"`
+	CheckoutSHA       string     `json:"checkout_sha"`
+	UserID            int64      `json:"user_id"`
+	UserName          string     `json:"user_name"`
+	UserEmail         string     `json:"user_email"`
+	UserAvatar        string     `json:"user_avatar"`
+	ProjectID         int64      `json:"project_id"`
+	Project           Project    `json:"Project"`
+	Repository        Repository `json:"repository"`
+	Commits           []Commit   `json:"commits"`
+	TotalCommitsCount int64      `json:"total_commits_count"`
+}
+
+// TagEventPayload contains the information for GitLab's tag push event
+type TagEventPayload struct {
+	ObjectKind        string     `json:"object_kind"`
+	Before            string     `json:"before"`
+	After             string     `json:"after"`
+	Ref               string     `json:"ref"`
+	CheckoutSHA       string     `json:"checkout_sha"`
+	UserID            int64      `json:"user_id"`
+	UserName          string     `json:"user_name"`
+	UserAvatar        string     `json:"user_avatar"`
+	ProjectID         int64      `json:"project_id"`
+	Project           Project    `json:"Project"`
+	Repository        Repository `json:"repository"`
+	Commits           []Commit   `json:"commits"`
+	TotalCommitsCount int64      `json:"total_commits_count"`
+}
+
+// WikiPageEventPayload contains the information for GitLab's wiki created/updated event
+type WikiPageEventPayload struct {
+	ObjectKind       string           `json:"object_kind"`
+	User             User             `json:"user"`
+	Project          Project          `json:"project"`
+	Wiki             Wiki             `json:"wiki"`
+	ObjectAttributes ObjectAttributes `json:"object_attributes"`
+}
+
+// PipelineEventPayload contains the information for GitLab's pipeline status change event
+type PipelineEventPayload struct {
+	ObjectKind       string           `json:"object_kind"`
+	User             User             `json:"user"`
+	Project          Project          `json:"project"`
+	Commit           Commit           `json:"commit"`
+	ObjectAttributes ObjectAttributes `json:"object_attributes"`
+	Builds           []Build          `json:"builds"`
+}
+
+// CommentEventPayload contains the information for GitLab's comment event
+type CommentEventPayload struct {
+	ObjectKind       string           `json:"object_kind"`
+	User             User             `json:"user"`
+	ProjectID        int64            `json:"project_id"`
+	Project          Project          `json:"project"`
+	Repository       Repository       `json:"repository"`
+	ObjectAttributes ObjectAttributes `json:"object_attributes"`
+	MergeRequest     MergeRequest     `json:"merge_request"`
+	Commit           Commit           `json:"commit"`
+	Issue            Issue            `json:"issue"`
+	Snippet          Snippet          `json:"snippet"`
+}
+
+// BuildEventPayload contains the information for GitLab's build status change event
+type BuildEventPayload struct {
+	ObjectKind        string      `json:"object_kind"`
+	Ref               string      `json:"ref"`
+	Tag               bool        `json:"tag"`
+	BeforeSHA         string      `json:"before_sha"`
+	SHA               string      `json:"sha"`
+	BuildID           int64       `json:"build_id"`
+	BuildName         string      `json:"build_name"`
+	BuildStage        string      `json:"build_stage"`
+	BuildStatus       string      `json:"build_status"`
+	BuildStartedAt    customTime  `json:"build_started_at"`
+	BuildFinishedAt   customTime  `json:"build_finished_at"`
+	BuildDuration     int64       `json:"build_duration"`
+	BuildAllowFailure bool        `json:"build_allow_failure"`
+	ProjectID         int64       `json:"project_id"`
+	ProjectName       string      `json:"project_name"`
+	User              User        `json:"user"`
+	Commit            BuildCommit `json:"commit"`
+	Repository        Repository  `json:"repository"`
+}
+
+// Issue contains all of the GitLab issue information
+type Issue struct {
+	ID          int64      `json:"id"`
+	Title       string     `json:"title"`
+	AssigneeID  int64      `json:"assignee_id"`
+	AuthorID    int64      `json:"author_id"`
+	ProjectID   int64      `json:"project_id"`
+	CreatedAt   customTime `json:"created_at"`
+	UpdatedAt   customTime `json:"updated_at"`
+	Position    int64      `json:"position"`
+	BranchName  string     `json:"branch_name"`
+	Description string     `json:"description"`
+	MilestoneID int64      `json:"milestone_id"`
+	State       string     `json:"state"`
+	IID         int64      `json:"iid"`
+}
+
+// Build contains all of the GitLab build information
+type Build struct {
+	ID            int64         `json:"id"`
+	Stage         string        `json:"stage"`
+	Name          string        `json:"name"`
+	Status        string        `json:"status"`
+	CreatedAt     customTime    `json:"created_at"`
+	StartedAt     customTime    `json:"started_at"`
+	FinishedAt    customTime    `json:"finished_at"`
+	When          string        `json:"when"`
+	Manual        bool          `json:"manual"`
+	User          User          `json:"user"`
+	Runner        string        `json:"runner"`
+	ArtifactsFile ArtifactsFile `json:"artifactsfile"`
+}
+
+// ArtifactsFile contains all of the GitLab artifact information
+type ArtifactsFile struct {
+	Filename string `json:"filename"`
+	Size     string `json:"size"`
+}
+
+// Wiki contains all of the GitLab wiki information
+type Wiki struct {
+	WebURL            string `json:"web_url"`
+	GitSSHURL         string `json:"git_ssh_url"`
+	GitHTTPURL        string `json:"git_http_url"`
+	PathWithNamespace string `json:"path_with_namespace"`
+	DefaultBranch     string `json:"default_branch"`
+}
+
+// Commit contains all of the GitLab commit information
+type Commit struct {
+	ID        string     `json:"id"`
+	Message   string     `json:"message"`
+	Timestamp customTime `json:"timestamp"`
+	URL       string     `json:"url"`
+	Author    Author     `json:"author"`
+	Added     []string   `json:"added"`
+	Modified  []string   `json:"modified"`
+	Removed   []string   `json:"removed"`
+}
+
+// BuildCommit contains all of the GitLab build commit information
+type BuildCommit struct {
+	ID          int64      `json:"id"`
+	SHA         string     `json:"sha"`
+	Message     string     `json:"message"`
+	AuthorName  string     `json:"auuthor_name"`
+	AuthorEmail string     `json:"author_email"`
+	Status      string     `json:"status"`
+	Duration    int64      `json:"duration"`
+	StartedAt   customTime `json:"started_at"`
+	FinishedAt  customTime `json:"finished_at"`
+}
+
+// Snippet contains all of the GitLab snippet information
+type Snippet struct {
+	ID              int64      `json:"id"`
+	Title           string     `json:"title"`
+	Content         string     `json:"content"`
+	AuthorID        int64      `json:"author_id"`
+	ProjectID       int64      `json:"project_id"`
+	CreatedAt       customTime `json:"created_at"`
+	UpdatedAt       customTime `json:"updated_at"`
+	FileName        string     `json:"file_name"`
+	ExpiresAt       customTime `json:"expires_at"`
+	Type            string     `json:"type"`
+	VisibilityLevel int64      `json:"visibility_level"`
+}
+
+// User contains all of the GitLab user information
+type User struct {
+	Name      string `json:"name"`
+	UserName  string `json:"username"`
+	AvatarURL string `json:"avatar_url"`
+}
+
+// Project contains all of the GitLab project information
+type Project struct {
+	Name              string `json:"name"`
+	Description       string `json:"description"`
+	WebURL            string `json:"web_url"`
+	AvatarURL         string `json:"avatar_url"`
+	GitSSSHURL        string `json:"git_ssh_url"`
+	GitHTTPURL        string `json:"git_http_url"`
+	Namespace         string `json:"namespace"`
+	VisibilityLevel   int64  `json:"visibility_level"`
+	PathWithNamespace string `json:"path_with_namespace"`
+	DefaultBranch     string `json:"default_branch"`
+	Homepage          string `json:"homepage"`
+	URL               string `json:"url"`
+	SSHURL            string `json:"ssh_url"`
+	HTTPURL           string `json:"http_url"`
+}
+
+// Repository contains all of the GitLab repository information
+type Repository struct {
+	Name        string `json:"name"`
+	URL         string `json:"url"`
+	Description string `json:"description"`
+	Homepage    string `json:"homepage"`
+}
+
+// ObjectAttributes contains all of the GitLab object attributes information
+type ObjectAttributes struct {
+	ID              int64      `json:"id"`
+	Title           string     `json:"title"`
+	AssigneeID      int64      `json:"assignee_id"`
+	AuthorID        int64      `json:"author_id"`
+	ProjectID       int64      `json:"project_id"`
+	CreatedAt       customTime `json:"created_at"`
+	UpdatedAt       customTime `json:"updated_at"`
+	Position        int64      `json:"position"`
+	BranchName      string     `json:"branch_name"`
+	Description     string     `json:"description"`
+	MilestoneID     int64      `json:"milestone_id"`
+	State           string     `json:"state"`
+	IID             int64      `json:"iid"`
+	URL             string     `json:"url"`
+	Action          string     `json:"action"`
+	TargetBranch    string     `json:"target_branch"`
+	SourceBranch    string     `json:"source_branch"`
+	SourceProjectID int64      `json:"source_project_id"`
+	TargetProjectID int64      `json:"target_project_id"`
+	StCommits       string     `json:"st_commits"`
+	MergeStatus     string     `json:"merge_status"`
+	Content         string     `json:"content"`
+	Format          string     `json:"format"`
+	Message         string     `json:"message"`
+	Slug            string     `json:"slug"`
+	Ref             string     `json:"ref"`
+	Tag             bool       `json:"tag"`
+	SHA             string     `json:"sha"`
+	BeforeSHA       string     `json:"before_sha"`
+	Status          string     `json:"status"`
+	Stages          []string   `json:"stages"`
+	Duration        int64      `json:"duration"`
+	Note            string     `json:"note"`
+	NotebookType    string     `json:"noteable_type"`
+	At              customTime `json:"attachment"`
+	LineCode        string     `json:"line_code"`
+	CommitID        string     `json:"commit_id"`
+	NoteableID      int64      `json:"noteable_id"`
+	System          bool       `json:"system"`
+	WorkInProgress  bool       `json:"work_in_progress"`
+	StDiffs         []StDiff   `json:"st_diffs"`
+	Source          Source     `json:"source"`
+	Target          Target     `json:"target"`
+	LastCommit      LastCommit `json:"last_commit"`
+	Assignee        Assignee   `json:"assignee"`
+}
+
+// MergeRequest contains all of the GitLab merge request information
+type MergeRequest struct {
+	ID              int64      `json:"id"`
+	TargetBranch    string     `json:"target_branch"`
+	SourceBranch    string     `json:"source_branch"`
+	SourceProjectID int64      `json:"source_project_id"`
+	AssigneeID      int64      `json:"assignee_id"`
+	AuthorID        int64      `json:"author_id"`
+	Title           string     `json:"title"`
+	CreatedAt       customTime `json:"created_at"`
+	UpdatedAt       customTime `json:"updated_at"`
+	MilestoneID     int64      `json:"milestone_id"`
+	State           string     `json:"state"`
+	MergeStatus     string     `json:"merge_status"`
+	TargetProjectID int64      `json:"target_project_id"`
+	IID             int64      `json:"iid"`
+	Description     string     `json:"description"`
+	Position        int64      `json:"position"`
+	LockedAt        customTime `json:"locked_at"`
+	Source          Source     `json:"source"`
+	Target          Target     `json:"target"`
+	LastCommit      LastCommit `json:"last_commit"`
+	WorkInProgress  bool       `json:"work_in_progress"`
+	Assignee        Assignee   `json:"assignee"`
+}
+
+// Assignee contains all of the GitLab assignee information
+type Assignee struct {
+	Name      string `json:"name"`
+	Username  string `json:"username"`
+	AvatarURL string `json:"avatar_url"`
+}
+
+// StDiff contains all of the GitLab diff information
+type StDiff struct {
+	Diff        string `json:"diff"`
+	NewPath     string `json:"new_path"`
+	OldPath     string `json:"old_path"`
+	AMode       string `json:"a_mode"`
+	BMode       string `json:"b_mode"`
+	NewFile     bool   `json:"new_file"`
+	RenamedFile bool   `json:"renamed_file"`
+	DeletedFile bool   `json:"deleted_file"`
+}
+
+// Source contains all of the GitLab source information
+type Source struct {
+	Name              string `json:"name"`
+	Description       string `json:"description"`
+	WebURL            string `json:"web_url"`
+	AvatarURL         string `json:"avatar_url"`
+	GitSSHURL         string `json:"git_ssh_url"`
+	GitHTTPURL        string `json:"git_http_url"`
+	Namespace         string `json:"namespace"`
+	VisibilityLevel   int64  `json:"visibility_level"`
+	PathWithNamespace string `json:"path_with_namespace"`
+	DefaultBranch     string `json:"default_branch"`
+	Homepage          string `json:"homepage"`
+	URL               string `json:"url"`
+	SSHURL            string `json:"ssh_url"`
+	HTTPURL           string `json:"http_url"`
+}
+
+// Target contains all of the GitLab target information
+type Target struct {
+	Name              string `json:"name"`
+	Description       string `json:"description"`
+	WebURL            string `json:"web_url"`
+	AvatarURL         string `json:"avatar_url"`
+	GitSSHURL         string `json:"git_ssh_url"`
+	GitHTTPURL        string `json:"git_http_url"`
+	Namespace         string `json:"namespace"`
+	VisibilityLevel   int64  `json:"visibility_level"`
+	PathWithNamespace string `json:"path_with_namespace"`
+	DefaultBranch     string `json:"default_branch"`
+	Homepage          string `json:"homepage"`
+	URL               string `json:"url"`
+	SSHURL            string `json:"ssh_url"`
+	HTTPURL           string `json:"http_url"`
+}
+
+// LastCommit contains all of the GitLab last commit information
+type LastCommit struct {
+	ID        string     `json:"id"`
+	Message   string     `json:"message"`
+	Timestamp customTime `json:"timestamp"`
+	URL       string     `json:"url"`
+	Author    Author     `json:"author"`
+}
+
+// Author contains all of the GitLab author information
+type Author struct {
+	Name  string `json:"name"`
+	Email string `json:"email"`
+}
+
+// Changes contains all changes associated with a GitLab issue or MR
+type Changes struct {
+	LabelChanges LabelChanges `json:"labels"`
+}
+
+// LabelChanges contains changes in labels assocatiated with a GitLab issue or MR
+type LabelChanges struct {
+	Previous []Label `json:"previous"`
+	Current  []Label `json:"current"`
+}
+
+// Label contains all of the GitLab label information
+type Label struct {
+	Id          int64      `json:"id"`
+	Title       string     `json:"title"`
+	Color       string     `json:"color"`
+	ProjectId   int64      `json:"project_id"`
+	CreatedAt   customTime `json:"created_at"`
+	UpdatedAt   customTime `json:"updated_at"`
+	Template    bool       `json:"template"`
+	Description string     `json:"description"`
+	Type        string     `json:"type"`
+	GroupId     int64      `json:"group_id"`
+}
diff --git a/vendor/gopkg.in/go-playground/webhooks.v3/logger.go b/vendor/gopkg.in/go-playground/webhooks.v3/logger.go
new file mode 100644
index 0000000..0d3d588
--- /dev/null
+++ b/vendor/gopkg.in/go-playground/webhooks.v3/logger.go
@@ -0,0 +1,44 @@
+package webhooks
+
+import "log"
+
+// DefaultLog contains the default logger for webhooks, and prints only info and error messages by default
+// for debugs override DefaultLog or see NewLogger for creating one without debugs.
+var DefaultLog Logger = new(logger)
+
+// Logger allows for customizable logging
+type Logger interface {
+	// Info prints basic information.
+	Info(string)
+	// Error prints error information.
+	Error(string)
+	// Debug prints information usefull for debugging.
+	Debug(string)
+}
+
+// NewLogger returns a new logger for use.
+func NewLogger(debug bool) Logger {
+	return &logger{PrintDebugs: debug}
+}
+
+type logger struct {
+	PrintDebugs bool
+}
+
+// Info prints basic information.
+func (l *logger) Info(msg string) {
+	log.Println("INFO:", msg)
+}
+
+// v prints error information.
+func (l *logger) Error(msg string) {
+	log.Println("ERROR:", msg)
+}
+
+// Debug prints information usefull for debugging.
+func (l *logger) Debug(msg string) {
+	if !l.PrintDebugs {
+		return
+	}
+	log.Println("DEBUG:", msg)
+}
diff --git a/vendor/gopkg.in/go-playground/webhooks.v3/logo.png b/vendor/gopkg.in/go-playground/webhooks.v3/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..1a160788dddb294f1a4de278d2363a4cb22700c9
GIT binary patch
literal 2419
zcmV-(35@oMP)<h;3K|Lk000e1NJLTq002M$002M;0{{R3owtGP0008|P)t-s00000
z0000000000000020s;gB0s#R50000D3JM1X1`7%c5)KXp0s#;Q0R{#L5DpIm0s;jE
z2o)eW5(fYnBtje<925%(Eh8YIT{TN68#yg03keN>rqDZMo0yuLUMLNEI2JM$1Q{qq
zMj;aw6A=^%1P&7&4izR12nP}+W(^GuKN<%cDMd~r5gjmEeo8PD4F_8@BQ+HX5Dg15
z6$?g6P!AR;H5CgzB_0bA9z|e=dwY8vF><PFM=3sOA1_anRyG$KA(E1k8z@375(QZ-
z85}H14;VHT3<pLmBPtsdD?xZvPD(mDIvOZLP9Y46N+=gHdWTXq6B86cPi$RZU}Qou
zZb~=`6EjylFb@?XEiyV&Q&S`k1sn?mARi$eHC%2j4v1Adc!r)^USS_bjc90Sb9Hr)
zT|<3ED_2=seMl~Kb#*p2H)=gAe^^6zcXu{6HYXDdnauM3|NoQ6@L8GGZE|&EX>08D
z{dKbEElGY>U0^Cyr-FuuDMoZrS6Om36KOCJ_5A<v_x+d2?~lapiox#H*w{dN!?3iq
zkd>B+j*vn~OOr|=jX@YtBMZ;r^6TsD#n<S6xaiBx&Q*G?prfQfZ=okca9d(!AT?bu
zK~zdiPJ%oY<L~*Q)A+&N^zZQSZmi=<h|I>w$#9#<yS=_KWwtL}u1a>SrmCwSK$A9H
zi!o4#b$WXiD_=@cRhCdFA|xd9_4b6m?c?O+ugBnDpxceL+IgSF!NbFCleMa?t}<AW
zGgpIoet{M-fG$vf6f1KaEmGL-{P_9$=kNBo)b7yS>95P@p~B+d;Ni&A-@DD+Y^B<%
zzSxhg$~$wuBTJxUeV#~WoGD0;IaQ1%K5sx)XeBjfGeBTyV_y&<Sr`}?t=anF>GIp;
z?vuXeWvAnizucX=*}>1&P>ju|w#h?+$!L+mc9gqjiMW%cupTpKbYN0FK|?b%HAXWh
zj?4UpxZHoP++~}{o~*%wo4!17xE3#cFfA%sE)@8QhGhT%0CaRxPE!B`4HXF>CL9q(
zH(*OGay(E$F|B(3x}#hEt8`cX{{8*?_}9I!lZbw5{`~jo=Hc4V#lE<-rJtLDasK}O
z_xAMi?(N^+(8<Qclwtn+`|<GZ<l^7Y$ica|oRN@=hkj-D_3!TK=iuMo-Q3*T+0o0$
zv}x%$Q$zp&1++;-K~z}7?UrR&T2UCssS8{}9O|ZX>eO|+8SC!u?(S3s1_FvTz;2yA
zb#_jt(@mZ0?(XjHJ<kqIy?VLG-Q(B&Kk&dk_x<tKdCwvImp+lGrsm^Y_XgTBXTCX1
z{wHHH+^==Jb`Bj3m@F7(F^wI1SPz?PO2=nRVE|)?B`hkj(Zdc?17?_$^#3($d^?B5
zEHc)T!BqcVtR}7LrmaW!F<|Rlhg!K8^dsub89W|XQ=4|Mb6fw`y24=h?a_juS8V`6
zrw>MWD%Hj#V6m>XmNWtm8OOyLHVZ)P6VQX+8~}m6JZ=eg8Cv4(*1iEf$e4ikH~~~^
zYlCJ12<-y;V3iVTPr(V85YU#5<ym_oP5@->juiu=6#=hC$6>wO0yhBEVH8HVh8=JN
zcKCP0=ow}!5?+9ze;@<hpj=8vlUWO?_yFvJwy6EUnl+^V*zaeauQbOuGN*Yk8$IZB
z`T3Q3mdEZtIP=XBPczV`d30|0yZ`0$A=9Ua92I&VJMqd0M>mkxH);gVfD0!=R$I7u
zx)jNU7FRpr0Bm$OZjiq*<s}b}1<B<u5rvb_oJ$|X#WbQZs0g4}LjXMBLdbnj7r8vC
zYhmn-<R4d!u<ThQtR@*?1DwL2oQM~CdU_TWcKtQr(Jr?xZ7`f(BZ>99OT_|^dcHm%
z1OP&z{HfoKovypw4%Zk_wF);`^5O1XPMysR4<w0P(W~<z@j{{SNaDSOPsy&k(yQ{K
zhN7LI*bThhwJTIt#$gT0*e0i}<m}UU`H_3hUf%oE9#rLJOKV_s^V#LjWYzmKtSu91
zf@@_bgU-HrbK$WU?{jK+q~)cmyvRc_46Q22S|X#vED&+A)3vgap!l<wZ|qj<P~={>
zpLrSCJxSU_d!Xa&*^Zhso?{jWHvpuT1RY8ADlIE7FaPSgXYZxMZ!->Qm(p}CU*`8d
zR=i;;_-@vXjqGC4%TtL#3A}`a1ZSU}`(NzM>LSe$G12ymRD40SEMFWg5G*82Fzbd4
z-~|(3KM6^6<~f5LhN_E@btgM=2!ygXS4vb8ezaJSE0OS*niq_!50N_bW_aoGL@zbK
zC-mI2i*8kSBDErbtgI?Bm6g$AzA7TqYSkS7am2cf(DBwyON*1oc>urz5r7nQiHel6
z&;dlGg&n9+3B;8VX(?%{cX93OcjCxfw?Gn(dwT=G-xS=j%pOBT&8uw+-{R9@nfz$w
z0pDU@#XFDs`PtE=X7|mE(v#jkUe3IInnu2E8D)qbAR`V0A1z8cZIv4qTy!)z<#wP!
zeG2Dw=61?SJ@wFs2LQ>g7qjxRtF>xK1^^czUxoTJDeQJPxW32LVZ?-&J`DBdd51pQ
zlYTg=x+jDNP&TC%Ck3BQ$rUSngOiHWCfR7(00~2s`ZzSyM;$@dTj?@DN6RrH#Wzf$
z66fb;f-lOC>M9}{+uinx#h2urpayPTYKCH6VBpG3MTL?t%NIx@6cw`H;~K4U$V8)X
zi*v57d(sD)jAdZnYqk$mswxGb6ZsOQ;G?zHDF_V-PX#^H(}}5LCet<x_yTc$tSp)@
zo4Epv6O{VLh^}N@9W<tiYe~!Zv9W$YAM9C!16k-Yb&tvsU7whPGyp~vO#9)5g~RQc
z1Vb|$^wH`RBh5JoB(-hwe1#Ycb%pyC*ilf2tx3#vsi?YIB#m(*=+|8I^@2OxWJ>pu
zwzjq-yH7FcNYr~Qt@AKO-E9onWD<!&A(6-she^^0;0^}!tT~!sW&<?~FyG&<ahOCP
z3J{1wBVw&|m2<0m2b3|1PNqQ+RV@gjk;!DbhNXi74Gu&mZml^)<uW)eU>I(}VQ|@M
z3IaikU|L{9)dsKu07wG>e$W7+Rx}z^>zx`5xP5KL0d8Xi3A_jW6|FV|M07e`L#hkS
l*uFDSD~W(oC&X9%`U3<b-^BrABT4`O002ovPDHLkV1g{pM^gX*

literal 0
HcmV?d00001

diff --git a/vendor/gopkg.in/go-playground/webhooks.v3/webhooks.go b/vendor/gopkg.in/go-playground/webhooks.v3/webhooks.go
new file mode 100644
index 0000000..4ab78b1
--- /dev/null
+++ b/vendor/gopkg.in/go-playground/webhooks.v3/webhooks.go
@@ -0,0 +1,124 @@
+package webhooks
+
+import (
+	"fmt"
+	"net/http"
+)
+
+// Header provides http.Header to minimize imports
+type Header http.Header
+
+// Provider defines the type of webhook
+type Provider int
+
+func (p Provider) String() string {
+	switch p {
+	case GitHub:
+		return "GitHub"
+	case Bitbucket:
+		return "Bitbucket"
+	case GitLab:
+		return "GitLab"
+	case Gogs:
+		return "Gogs"
+	default:
+		return "Unknown"
+	}
+}
+
+// webhooks available providers
+const (
+	GitHub Provider = iota
+	Bitbucket
+	GitLab
+	Gogs
+)
+
+// Webhook interface defines a webhook to receive events
+type Webhook interface {
+	Provider() Provider
+	ParsePayload(w http.ResponseWriter, r *http.Request)
+}
+
+type server struct {
+	hook             Webhook
+	path             string
+	includePathCheck bool
+}
+
+// ProcessPayloadFunc is a common function for payload return values
+type ProcessPayloadFunc func(payload interface{}, header Header)
+
+// Handler returns the webhook http.Handler for use in your own Mux implementation
+func Handler(hook Webhook) http.Handler {
+	return &server{
+		hook: hook,
+	}
+}
+
+// Run runs a server
+func Run(hook Webhook, addr string, path string) error {
+	srv := &server{
+		hook:             hook,
+		path:             path,
+		includePathCheck: true,
+	}
+	s := &http.Server{Addr: addr, Handler: srv}
+
+	DefaultLog.Info(fmt.Sprintf("Listening on addr: %s path: %s", addr, path))
+	return s.ListenAndServe()
+}
+
+// RunServer runs a custom server.
+func RunServer(s *http.Server, hook Webhook, path string) error {
+
+	srv := &server{
+		hook:             hook,
+		path:             path,
+		includePathCheck: true,
+	}
+
+	s.Handler = srv
+	DefaultLog.Info(fmt.Sprintf("Listening on addr: %s path: %s", s.Addr, path))
+	return s.ListenAndServe()
+}
+
+// RunTLSServer runs a custom server with TLS configuration.
+// NOTE: http.Server Handler will be overridden by this library, just set it to nil.
+// Setting the Certificates can be done in the http.Server.TLSConfig.Certificates
+// see example here: https://github.com/go-playground/webhooks/blob/v2/webhooks_test.go#L178
+func RunTLSServer(s *http.Server, hook Webhook, path string) error {
+
+	srv := &server{
+		hook:             hook,
+		path:             path,
+		includePathCheck: true,
+	}
+
+	s.Handler = srv
+	DefaultLog.Info(fmt.Sprintf("Listening on addr: %s path: %s", s.Addr, path))
+	return s.ListenAndServeTLS("", "")
+}
+
+// ServeHTTP is the Handler for every posted WebHook Event
+func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	defer r.Body.Close()
+	DefaultLog.Info("Webhook received")
+
+	if r.Method != "POST" {
+		DefaultLog.Error(fmt.Sprintf("405 Method not allowed, attempt made using Method: %s", r.Method))
+		http.Error(w, "405 Method not allowed", http.StatusMethodNotAllowed)
+		return
+	}
+
+	DefaultLog.Debug(fmt.Sprintf("Include path check: %t", s.includePathCheck))
+	if s.includePathCheck {
+		if r.URL.Path != s.path {
+			DefaultLog.Error(fmt.Sprintf("404 Not found, POST made using path: %s, but expected %s", r.URL.Path, s.path))
+			http.Error(w, "404 Not found", http.StatusNotFound)
+			return
+		}
+	}
+
+	s.hook.ParsePayload(w, r)
+}
diff --git a/vendor/maunium.net/go/mauflag/LICENSE b/vendor/maunium.net/go/mauflag/LICENSE
new file mode 100644
index 0000000..e587591
--- /dev/null
+++ b/vendor/maunium.net/go/mauflag/LICENSE
@@ -0,0 +1,621 @@
+                    GNU 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.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/vendor/maunium.net/go/mauflag/README.md b/vendor/maunium.net/go/mauflag/README.md
new file mode 100644
index 0000000..4d17280
--- /dev/null
+++ b/vendor/maunium.net/go/mauflag/README.md
@@ -0,0 +1,51 @@
+# mauflag
+[![Build Status](http://img.shields.io/travis/tulir293/mauflag.svg?style=flat-square)](https://travis-ci.org/tulir293/mauflag)
+[![License](http://img.shields.io/:license-gpl3-blue.svg?style=flat-square)](http://www.gnu.org/licenses/gpl-3.0.html)
+
+An extendable argument parser for Golang. Mostly follows the GNU [Program Argument Syntax Conventions](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html).
+
+Use `go get maunium.net/go/mauflag` to get this package
+
+## Basic usage
+A flag can have as many long or short keys as you like. Short keys are prefixed with one dash (`-`) whilst long keys are prefixed with two (`--`)
+
+To create a flag, you must first call the `Make()` method in a flagset. Calling flagset methods as package functions uses the default flagset which takes input arguments from `os.Args`
+
+After creating a flag, you can call the functions it has as you like. After configuring the flag, you can call one of the type functions to set the flag type and get a pointer to the value.
+
+### Examples
+Here's a basic string flag that can be set using `-s` or `--my-string-flag`
+```go
+var myStringFlag = flag.Make().ShortKey("s").LongKey("my-string-flag").Default("a string").String()
+```
+
+The following input arguments would set the value of the flag to `foo`
+* `-s`, `foo`
+* `-s=foo`
+* `-sfoo`
+* `--my-string-flag`, `foo`
+* `--my-string-flag=foo`
+
+#### Chaining
+Short boolean flags can be chained. The last flag of a chain doesn't necessarily have to be a boolean flag.
+For example, if we had the following flags defined
+```go
+var boolA = flag.Make().ShortKey("a").Bool()
+var boolB = flag.Make().ShortKey("b").Bool()
+var boolC = flag.Make().ShortKey("c").Bool()
+var stringD = flag.Make().ShortKey("d").String()
+```
+We could set the values of all the boolean flags to true with one input argument: `-abc`. We could also add the `d` flag to the end like so: `-abcd`, `foo`.
+Any of the other ways to set short flags also work (`-abcd=foo`, `-abcdfoo`).
+
+However, if you put `d` as the first flag (`-dabc`) the value of the `d` flag will be `abc` and none of the boolean flags would change.
+
+### Godoc
+More docs, including all values supported by default, can be found from [godoc.org/maunium.net/go/mauflag](https://godoc.org/maunium.net/go/mauflag)
+
+## Custom values
+All value containers must implement the `Value` interface. It contains two functions:
+* `Set(string)` which is called whenever the parser finds a value associated with the flag.
+* `Name()` which should return a human-readable name for the type of the value. If the value container contains multiple actual values, `Name()` should return the name of a single object (e.g. `integer` if the value container contains multiple integers)
+
+After creating a value container you can use it by calling the `Custom` method of a `Flag` and giving an instance of the value container. The parser will then call the `Set` method each time it encounters a value associated with the flag.
diff --git a/vendor/maunium.net/go/mauflag/defaults.go b/vendor/maunium.net/go/mauflag/defaults.go
new file mode 100644
index 0000000..93a0f8e
--- /dev/null
+++ b/vendor/maunium.net/go/mauflag/defaults.go
@@ -0,0 +1,85 @@
+// mauflag - An extendable command-line argument parser for Golang
+// Copyright (C) 2016 Maunium
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package mauflag
+
+import (
+	"os"
+)
+
+var defaultSet = New(os.Args[1:])
+
+// DefaultSet returns the default flagset which takes its arguments from os.Args
+func DefaultSet() *Set {
+	return defaultSet
+}
+
+// Make creates and registers a flag
+func Make() *Flag {
+	return DefaultSet().Make()
+}
+
+// MakeKey creates and registers a flag with the given short and long keys
+func MakeKey(short, long string) *Flag {
+	return DefaultSet().MakeKey(short, long)
+}
+
+// MakeFull creates and registers a flag with the given short and long keys, usage string and default value
+func MakeFull(short, long, usage, defVal string) *Flag {
+	return DefaultSet().MakeFull(short, long, usage, defVal)
+}
+
+// Parse the command line arguments into mauflag form
+func Parse() error {
+	return DefaultSet().Parse()
+}
+
+// Args returns the arguments that weren't associated with any flag
+func Args() []string {
+	return DefaultSet().Args()
+}
+
+// Arg returns the string at the given index from the list Args() returns
+// If the index does not exist, Arg will return an empty string.
+func Arg(i int) string {
+	return DefaultSet().Arg(i)
+}
+
+// NArg returns the number of arguments not associated with any flags
+func NArg() int {
+	return len(DefaultSet().args)
+}
+
+// MakeHelpFlag creates the -h, --help flag
+func MakeHelpFlag() (*bool, *Flag) {
+	return DefaultSet().MakeHelpFlag()
+}
+
+// CheckHelpFlag checks if the help flag is set and prints the help page if needed.
+// Return value tells whether or not the help page was printed
+func CheckHelpFlag() bool {
+	return DefaultSet().CheckHelpFlag()
+}
+
+// SetHelpTitles sets the first line (program name and basic explanation) and basic usage specification
+func SetHelpTitles(firstLine, basicUsage string) {
+	DefaultSet().SetHelpTitles(firstLine, basicUsage)
+}
+
+// PrintHelp prints the help page
+func PrintHelp() {
+	DefaultSet().PrintHelp()
+}
diff --git a/vendor/maunium.net/go/mauflag/flags.go b/vendor/maunium.net/go/mauflag/flags.go
new file mode 100644
index 0000000..6898af7
--- /dev/null
+++ b/vendor/maunium.net/go/mauflag/flags.go
@@ -0,0 +1,268 @@
+// mauflag - An extendable command-line argument parser for Golang
+// Copyright (C) 2016 Maunium
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package mauflag
+
+// Flag represents a single flag
+type Flag struct {
+	longKeys     []string
+	shortKeys    []string
+	Value        Value
+	defaul       string
+	usage        string
+	usageCat     string
+	usageValName string
+}
+
+// Make creates and registers a flag
+func (fs *Set) Make() *Flag {
+	flag := &Flag{}
+	val := stringValue("")
+	flag.Value = &val
+	flag.usageCat = "Application"
+	flag.activateDefaultValue()
+	fs.flags = append(fs.flags, flag)
+	return flag
+}
+
+// MakeKey creates and registers a flag with the given short and long keys
+func (fs *Set) MakeKey(short, long string) *Flag {
+	return fs.Make().Key(short, long)
+}
+
+// MakeFull creates and registers a flag with the given short and long keys, usage string and default value
+func (fs *Set) MakeFull(short, long, usage, defVal string) *Flag {
+	return fs.MakeKey(short, long).Usage(usage).Default(defVal)
+}
+
+func (flag *Flag) setValue(val string) error {
+	return flag.Value.Set(val)
+}
+
+// Usage sets the usage of this Flag
+func (flag *Flag) Usage(usage string) *Flag {
+	flag.usage = usage
+	return flag
+}
+
+// UsageCategory sets the category of this flag (e.g. Application or Help)
+func (flag *Flag) UsageCategory(category string) *Flag {
+	flag.usageCat = category
+	return flag
+}
+
+// ValueName sets the value name in the usage page
+func (flag *Flag) ValueName(valname string) *Flag {
+	flag.usageValName = valname
+	return flag
+}
+
+// Default sets the default value of this Flag
+// The value given is passed to the Value container of this flag using `Set()`
+func (flag *Flag) Default(defaul string) *Flag {
+	flag.defaul = defaul
+	flag.activateDefaultValue()
+	return flag
+}
+
+func (flag *Flag) activateDefaultValue() {
+	if len(flag.defaul) > 0 && flag.Value != nil {
+		flag.Value.Set(flag.defaul)
+	}
+}
+
+// LongKey adds a long key to this Flag
+func (flag *Flag) LongKey(key string) *Flag {
+	flag.longKeys = append(flag.longKeys, key)
+	return flag
+}
+
+// ShortKey adds a short key to this Flag
+func (flag *Flag) ShortKey(key string) *Flag {
+	flag.shortKeys = append(flag.shortKeys, key)
+	return flag
+}
+
+// Key adds a long and a short key to this Flag
+func (flag *Flag) Key(short, long string) *Flag {
+	return flag.ShortKey(short).LongKey(long)
+}
+
+// Custom sets a custom object that implemets Value as the value of this flag
+func (flag *Flag) Custom(val Value) {
+	flag.Value = val
+	flag.activateDefaultValue()
+}
+
+// Bool sets the type of this flag to a boolean and returns a pointer to the value
+func (flag *Flag) Bool() *bool {
+	val := boolValue(false)
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*bool)(&val)
+}
+
+// String sets the type of this flag to a string and returns a pointer to the value
+func (flag *Flag) String() *string {
+	val := stringValue("")
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*string)(&val)
+}
+
+// StringMap sets the type of this flag to a string-string map and returns a pointer to the value
+func (flag *Flag) StringMap() *map[string]string {
+	val := ssMapValue{}
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*map[string]string)(&val)
+}
+
+// StringArray sets the type of this flag to a string array and returns a pointer to the value
+func (flag *Flag) StringArray() *[]string {
+	val := stringArrayValue{}
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*[]string)(&val)
+}
+
+// IntArray sets the type of this flag to a signed default-length integer array and returns a pointer to the value
+func (flag *Flag) IntArray() *[]int {
+	val := intArrayValue{}
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*[]int)(&val)
+}
+
+// Int64Array sets the type of this flag to a signed 64-bit integer array and returns a pointer to the value
+func (flag *Flag) Int64Array() *[]int64 {
+	val := int64ArrayValue{}
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*[]int64)(&val)
+}
+
+// Int sets the type of this flag to a signed default-length integer and returns a pointer to the value
+func (flag *Flag) Int() *int {
+	val := intValue(0)
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*int)(&val)
+}
+
+// Uint sets the type of this flag to an unsigned default-length integer and returns a pointer to the value
+func (flag *Flag) Uint() *uint {
+	val := uintValue(0)
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*uint)(&val)
+}
+
+// Int8 sets the type of this flag to a signed 8-bit integer and returns a pointer to the value
+func (flag *Flag) Int8() *int8 {
+	val := int8Value(0)
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*int8)(&val)
+}
+
+// Uint8 sets the type of this flag to an unsigned 8-bit integer and returns a pointer to the value
+func (flag *Flag) Uint8() *uint8 {
+	val := uint8Value(0)
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*uint8)(&val)
+}
+
+// Byte sets the type of this flag to a byte (unsigned 8-bit integer) and returns a pointer to the value
+func (flag *Flag) Byte() *byte {
+	val := byteValue(0)
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*byte)(&val)
+}
+
+// Int16 sets the type of this flag to a signed 16-bit integer and returns a pointer to the value
+func (flag *Flag) Int16() *int16 {
+	val := int16Value(0)
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*int16)(&val)
+}
+
+// Uint16 sets the type of this flag to an unsigned 16-bit integer and returns a pointer to the value
+func (flag *Flag) Uint16() *uint16 {
+	val := uint16Value(0)
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*uint16)(&val)
+}
+
+// Int32 sets the type of this flag to a signed 32-bit integer and returns a pointer to the value
+func (flag *Flag) Int32() *int32 {
+	val := int32Value(0)
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*int32)(&val)
+}
+
+// Rune sets the type of this flag to a rune (signed 32-bit integer) and returns a pointer to the value
+func (flag *Flag) Rune() *rune {
+	val := runeValue(0)
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*rune)(&val)
+}
+
+// Uint32 sets the type of this flag to an unsigned 32-bit integer and returns a pointer to the value
+func (flag *Flag) Uint32() *uint32 {
+	val := uint32Value(0)
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*uint32)(&val)
+}
+
+// Int64 sets the type of this flag to a signed 64-bit integer and returns a pointer to the value
+func (flag *Flag) Int64() *int64 {
+	val := int64Value(0)
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*int64)(&val)
+}
+
+// Uint64 sets the type of this flag to an unsigned 64-bit integer and returns a pointer to the value
+func (flag *Flag) Uint64() *uint64 {
+	val := uint64Value(0)
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*uint64)(&val)
+}
+
+// Float32 sets the type of this flag to an 32-bit float and returns a pointer to the value
+func (flag *Flag) Float32() *float32 {
+	val := float32Value(0)
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*float32)(&val)
+}
+
+// Float64 sets the type of this flag to an 64-bit float and returns a pointer to the value
+func (flag *Flag) Float64() *float64 {
+	val := float64Value(0)
+	flag.Value = &val
+	flag.activateDefaultValue()
+	return (*float64)(&val)
+}
diff --git a/vendor/maunium.net/go/mauflag/flagset.go b/vendor/maunium.net/go/mauflag/flagset.go
new file mode 100644
index 0000000..45636b8
--- /dev/null
+++ b/vendor/maunium.net/go/mauflag/flagset.go
@@ -0,0 +1,273 @@
+// mauflag - An extendable command-line argument parser for Golang
+// Copyright (C) 2016 Maunium
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package mauflag
+
+import (
+	"fmt"
+	"os"
+	"strings"
+)
+
+// Set is a set of flags with certain input arguments
+type Set struct {
+	// The list of strings used as input
+	InputArgs []string
+	// Whether or not to ignore all flags after the user has entered two dashes with no flag key ("--")
+	// If enabled, all arguments after two dashes with no flag key will go into the args array (@see Args())
+	DoubleLineEscape bool
+	// Whether or not to exit the program when there's an error
+	// If enabled, the error message will be printed to `stderr` after which `os.Exit(1)` will be called.
+	ExitOnError bool
+
+	wantHelp      *bool
+	basicUsage    string
+	helpFirstLine string
+
+	args  []string
+	flags []*Flag
+}
+
+// New creates a new flagset
+func New(args []string) *Set {
+	return &Set{InputArgs: args, DoubleLineEscape: true, ExitOnError: false}
+}
+
+// Args returns the arguments that weren't associated with any flag
+func (fs *Set) Args() []string {
+	return fs.args
+}
+
+// Arg returns the string at the given index from the list Args() returns
+// If the index does not exist, Arg will return an empty string.
+func (fs *Set) Arg(i int) string {
+	if len(fs.args) <= i {
+		return ""
+	}
+	return fs.args[i]
+}
+
+// NArg returns the number of arguments not associated with any flags
+func (fs *Set) NArg() int {
+	return len(fs.args)
+}
+
+// MakeHelpFlag creates the -h, --help flag
+func (fs *Set) MakeHelpFlag() (*bool, *Flag) {
+	var flag = fs.Make().Key("h", "help").Usage("Show this help page.").UsageCategory("Help")
+	fs.wantHelp = flag.Bool()
+	return fs.wantHelp, flag
+}
+
+// CheckHelpFlag checks if the help flag is set and prints the help page if needed.
+// Return value tells whether or not the help page was printed
+func (fs *Set) CheckHelpFlag() bool {
+	if fs.wantHelp != nil && *fs.wantHelp {
+		fs.PrintHelp()
+		return true
+	}
+	return false
+}
+
+// SetHelpTitles sets the first line (program name and basic explanation) and basic usage specification
+func (fs *Set) SetHelpTitles(firstLine, basicUsage string) {
+	fs.helpFirstLine = firstLine
+	fs.basicUsage = basicUsage
+}
+
+// PrintHelp prints the help page
+func (fs *Set) PrintHelp() {
+	var helpSectNames = make(map[string]int)
+	var helpSectIndexes = make(map[int]string)
+	var helpSects = make([][]*Flag, 0)
+	for _, flag := range fs.flags {
+		index, ok := helpSectNames[flag.usageCat]
+		if !ok {
+			arr := []*Flag{flag}
+			helpSects = append(helpSects, arr)
+			helpSectNames[flag.usageCat] = len(helpSects) - 1
+			helpSectIndexes[len(helpSects)-1] = flag.usageCat
+		} else {
+			helpSects[index] = append(helpSects[index], flag)
+		}
+	}
+
+	data, maxLen := fs.formatFlagHelp(helpSects)
+
+	fmt.Printf(`%s
+
+Usage:
+  %s
+
+`, fs.helpFirstLine, fs.basicUsage)
+
+	for sect, sData := range data {
+		fmt.Print(helpSectIndexes[sect], " options:\n")
+
+		for i, fData := range sData {
+			fmt.Print(fData, strings.Repeat(" ", maxLen-len(fData)+3), helpSects[sect][i].usage, "\n")
+		}
+
+		fmt.Print("\n")
+	}
+}
+
+func (fs *Set) formatFlagHelp(helpSects [][]*Flag) (data [][]string, maxLen int) {
+	maxLen = 0
+	data = make([][]string, len(helpSects))
+	for sect, flags := range helpSects {
+		var sData = make([]string, len(flags))
+		for i, flag := range flags {
+			var fData = []string{"  "}
+
+			for _, key := range flag.shortKeys {
+				fData = append(fData, "-")
+				fData = append(fData, key)
+				fData = append(fData, ", ")
+			}
+
+			for _, key := range flag.longKeys {
+				fData = append(fData, "--")
+				fData = append(fData, key)
+				if len(flag.usageValName) > 0 {
+					fData = append(fData, "=")
+					fData = append(fData, flag.usageValName)
+				}
+				fData = append(fData, ", ")
+			}
+
+			if fData[len(fData)-1] == ", " {
+				fData = fData[:len(fData)-1]
+			}
+
+			sData[i] = strings.Join(fData, "")
+			if len(sData[i]) > maxLen {
+				maxLen = len(sData[i])
+			}
+		}
+		data[sect] = sData
+	}
+	return
+}
+
+func (fs *Set) err(format string, args ...interface{}) error {
+	if fs.ExitOnError {
+		fmt.Fprintf(os.Stderr, format, args...)
+		fmt.Fprint(os.Stderr, "\n")
+		os.Exit(1)
+		return nil
+	}
+	return fmt.Errorf(format, args...)
+}
+
+// Parse the input arguments in this flagset into mauflag form
+// Before this function is called all the flags will have either the type default or the given default value
+func (fs *Set) Parse() error {
+	var flag *Flag
+	var key string
+	var noMoreFlags = false
+
+	for _, arg := range fs.InputArgs {
+		if noMoreFlags {
+			fs.args = append(fs.args, arg)
+		} else if arg == "--" && fs.DoubleLineEscape {
+			noMoreFlags = true
+		} else if flag != nil {
+			err := flag.setValue(arg)
+			if err != nil {
+				return fs.err("Flag %s was not a %s", key, flag.Value.Name())
+			}
+			flag = nil
+		} else if arg[0] == '-' && len(arg) > 1 {
+			arg = strings.ToLower(arg)
+			var err error
+			flag, key, err = fs.flagFound(arg[1:])
+			if err != nil {
+				return err
+			}
+		} else {
+			fs.args = append(fs.args, arg)
+		}
+	}
+	return nil
+}
+
+func (fs *Set) flagFound(arg string) (flag *Flag, key string, err error) {
+	key = arg
+	var val string
+	if strings.ContainsRune(key, '=') {
+		val = key[strings.Index(key, "=")+1:]
+		key = key[:strings.Index(key, "=")]
+	}
+
+	var rem string
+	flag, key, rem = fs.getFlag(key)
+	flag, key, rem, err = fs.handleFlag(flag, key, rem, val)
+
+	if len(rem) > 0 {
+		return fs.flagFound(rem)
+	}
+
+	return
+}
+
+func (fs *Set) handleFlag(flag *Flag, key, rem, val string) (*Flag, string, string, error) {
+	var err error
+	var isBool bool
+
+	if flag != nil {
+		_, isBool = flag.Value.(*boolValue)
+	}
+
+	if flag == nil {
+		err = fs.err("Unknown flag: %s", key)
+	} else if len(val) > 0 && len(rem) == 0 {
+		flag.setValue(val)
+	} else if isBool {
+		flag.setValue("true")
+	} else if len(rem) > 0 {
+		flag.setValue(rem)
+		rem = ""
+	} else {
+		return flag, key, rem, err
+	}
+
+	return nil, "", rem, err
+}
+
+func (fs *Set) getFlag(key string) (*Flag, string, string) {
+	if key[0] == '-' {
+		key = key[1:]
+		for _, lflag := range fs.flags {
+			for _, lkey := range lflag.longKeys {
+				if lkey == key {
+					return lflag, lkey, ""
+				}
+			}
+		}
+		return nil, key, ""
+	}
+	rem := key[1:]
+	key = key[0:1]
+	for _, lflag := range fs.flags {
+		for _, lkey := range lflag.shortKeys {
+			if lkey == key {
+				return lflag, lkey, rem
+			}
+		}
+	}
+	return nil, key, rem
+}
diff --git a/vendor/maunium.net/go/mauflag/values.go b/vendor/maunium.net/go/mauflag/values.go
new file mode 100644
index 0000000..40ec7b0
--- /dev/null
+++ b/vendor/maunium.net/go/mauflag/values.go
@@ -0,0 +1,315 @@
+// mauflag - An extendable command-line argument parser for Golang
+// Copyright (C) 2016 Maunium
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package mauflag
+
+import (
+	"errors"
+	"strconv"
+	"strings"
+)
+
+// Value represents the value a flag has
+type Value interface {
+	// Set or add the data in the given string to this value
+	Set(string) error
+	// Name should return the type name of this value or (if this value is an array,
+	// a map or something of that sort) the type name of one object in this value
+	Name() string
+}
+
+type boolValue bool
+
+func (val *boolValue) Name() string {
+	return "boolean"
+}
+
+func (val *boolValue) Set(newVal string) error {
+	b, err := strconv.ParseBool(newVal)
+	if err == nil {
+		*val = boolValue(b)
+	}
+	return err
+}
+
+type stringValue string
+
+func (val *stringValue) Name() string {
+	return "string"
+}
+
+func (val *stringValue) Set(newVal string) error {
+	*val = stringValue(newVal)
+	return nil
+}
+
+type ssMapValue map[string]string
+
+func (val *ssMapValue) Name() string {
+	return "string-string pair"
+}
+
+func (val *ssMapValue) Set(newVal string) error {
+	index := strings.IndexRune(newVal, '=')
+	if index == -1 {
+		index = strings.IndexRune(newVal, ',')
+		if index == -1 {
+			index = strings.IndexRune(newVal, ':')
+			if index == -1 {
+				return errors.New("No pair separator found")
+			}
+		}
+	}
+	(*val)[newVal[:index]] = newVal[:index+1]
+	return nil
+}
+
+type stringArrayValue []string
+
+func (val *stringArrayValue) Name() string {
+	return "string"
+}
+
+func (val *stringArrayValue) Set(newVal string) error {
+	*val = append(*val, newVal)
+	return nil
+}
+
+type intArrayValue []int
+
+func (val *intArrayValue) Name() string {
+	return "integer"
+}
+
+func (val *intArrayValue) Set(newVal string) error {
+	i, err := strconv.Atoi(newVal)
+	if err != nil {
+		return err
+	}
+	*val = append(*val, i)
+	return nil
+}
+
+type int64ArrayValue []int64
+
+func (val *int64ArrayValue) Name() string {
+	return "64-bit integer"
+}
+
+func (val *int64ArrayValue) Set(newVal string) error {
+	i, err := strconv.ParseInt(newVal, 10, 64)
+	if err != nil {
+		return err
+	}
+	*val = append(*val, i)
+	return nil
+}
+
+type intValue int
+
+func (val *intValue) Name() string {
+	return "integer"
+}
+
+func (val *intValue) Set(newVal string) error {
+	i, err := strconv.ParseInt(newVal, 10, 32)
+	if err == nil {
+		*val = intValue(i)
+	}
+	return err
+}
+
+type uintValue uint
+
+func (val *uintValue) Name() string {
+	return "unsigned integer"
+}
+
+func (val *uintValue) Set(newVal string) error {
+	i, err := strconv.ParseUint(newVal, 10, 32)
+	if err == nil {
+		*val = uintValue(i)
+	}
+	return err
+}
+
+type int8Value int8
+
+func (val *int8Value) Name() string {
+	return "8-bit integer"
+}
+
+func (val *int8Value) Set(newVal string) error {
+	i, err := strconv.ParseInt(newVal, 10, 8)
+	if err == nil {
+		*val = int8Value(i)
+	}
+	return err
+}
+
+type uint8Value uint8
+
+func (val *uint8Value) Name() string {
+	return "unsigned 8-bit integer"
+}
+
+func (val *uint8Value) Set(newVal string) error {
+	i, err := strconv.ParseUint(newVal, 10, 8)
+	if err == nil {
+		*val = uint8Value(i)
+	}
+	return err
+}
+
+type byteValue byte
+
+func (val *byteValue) Name() string {
+	return "rune (unsigned 8-bit integer)"
+}
+
+func (val *byteValue) Set(newVal string) error {
+	i, err := strconv.ParseUint(newVal, 10, 8)
+	if err == nil {
+		*val = byteValue(i)
+	}
+	return err
+}
+
+type int16Value int16
+
+func (val *int16Value) Name() string {
+	return "16-bit integer"
+}
+
+func (val *int16Value) Set(newVal string) error {
+	i, err := strconv.ParseInt(newVal, 10, 16)
+	if err == nil {
+		*val = int16Value(i)
+	}
+	return err
+}
+
+type uint16Value uint16
+
+func (val *uint16Value) Name() string {
+	return "unsigned 16-bit integer"
+}
+
+func (val *uint16Value) Set(newVal string) error {
+	i, err := strconv.ParseUint(newVal, 10, 16)
+	if err == nil {
+		*val = uint16Value(i)
+	}
+	return err
+}
+
+type int32Value int32
+
+func (val *int32Value) Name() string {
+	return "32-bit integer"
+}
+
+func (val *int32Value) Set(newVal string) error {
+	i, err := strconv.ParseInt(newVal, 10, 32)
+	if err == nil {
+		*val = int32Value(i)
+	}
+	return err
+}
+
+type runeValue rune
+
+func (val *runeValue) Name() string {
+	return "rune (signed 32-bit integer)"
+}
+
+func (val *runeValue) Set(newVal string) error {
+	i, err := strconv.ParseInt(newVal, 10, 32)
+	if err == nil {
+		*val = runeValue(i)
+	}
+	return err
+}
+
+type uint32Value uint32
+
+func (val *uint32Value) Name() string {
+	return "unsigned 32-bit integer"
+}
+
+func (val *uint32Value) Set(newVal string) error {
+	i, err := strconv.ParseUint(newVal, 10, 32)
+	if err == nil {
+		*val = uint32Value(i)
+	}
+	return err
+}
+
+type int64Value int64
+
+func (val *int64Value) Name() string {
+	return "64-bit integer"
+}
+
+func (val *int64Value) Set(newVal string) error {
+	i, err := strconv.ParseInt(newVal, 10, 64)
+	if err == nil {
+		*val = int64Value(i)
+	}
+	return err
+}
+
+type uint64Value uint64
+
+func (val *uint64Value) Name() string {
+	return "unsigned 64-bit integer"
+}
+
+func (val *uint64Value) Set(newVal string) error {
+	i, err := strconv.ParseUint(newVal, 10, 64)
+	if err == nil {
+		*val = uint64Value(i)
+	}
+	return err
+}
+
+type float32Value float32
+
+func (val *float32Value) Name() string {
+	return "32-bit float"
+}
+
+func (val *float32Value) Set(newVal string) error {
+	i, err := strconv.ParseFloat(newVal, 32)
+	if err == nil {
+		*val = float32Value(i)
+	}
+	return err
+}
+
+type float64Value float64
+
+func (val *float64Value) Name() string {
+	return "64-bit float"
+}
+
+func (val *float64Value) Set(newVal string) error {
+	i, err := strconv.ParseFloat(newVal, 64)
+	if err == nil {
+		*val = float64Value(i)
+	}
+	return err
+}
diff --git a/vendor/maunium.net/go/mautrix/LICENSE b/vendor/maunium.net/go/mautrix/LICENSE
new file mode 100644
index 0000000..94a0453
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/LICENSE
@@ -0,0 +1,621 @@
+                    GNU 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.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
diff --git a/vendor/maunium.net/go/mautrix/LICENSE-orig b/vendor/maunium.net/go/mautrix/LICENSE-orig
new file mode 100644
index 0000000..a612ad9
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/LICENSE-orig
@@ -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/maunium.net/go/mautrix/README.md b/vendor/maunium.net/go/mautrix/README.md
new file mode 100644
index 0000000..20508e4
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/README.md
@@ -0,0 +1,12 @@
+mautrix
+=======
+
+A [Matrix](https://matrix.org) client-server library intended for bots.
+
+Use `go get maunium.net/go/mautrix` to get this package
+
+This project is a fork of [geir54/goMatrix](https://github.com/geir54/goMatrix),
+but most of the code has been rewritten.
+
+**N.B.** [sync.go](sync.go) has not
+been rewritten, so it is still licensed under [MPL 2.0](LICENSE-orig)
diff --git a/vendor/maunium.net/go/mautrix/auth.go b/vendor/maunium.net/go/mautrix/auth.go
new file mode 100644
index 0000000..97432d0
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/auth.go
@@ -0,0 +1,74 @@
+// mautrix - A Matrix client-server library intended for bots.
+// Copyright (C) 2017 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package mautrix
+
+import (
+	"fmt"
+)
+
+type loginInfo struct {
+	AccessToken  string `json:"access_token"`
+	RefreshToken string `json:"refresh_token"`
+	HomeServer   string `json:"home_server"`
+	UserID       string `json:"user_id"`
+	Error        string `json:"error"`
+}
+
+// PasswordLogin tries to log in with username and password
+func (mx *MatrixBot) PasswordLogin(user, password string) error {
+	return mx.login(map[string]string{
+		"type":     LoginPassword,
+		"user":     user,
+		"password": password,
+	})
+}
+
+// SetToken sets the access token to use
+func (mx *MatrixBot) SetToken(user, token string) error {
+	mx.MatrixID = user
+	mx.AccessToken = token
+	return nil
+}
+
+// DummyLogin tries to log in without authentication
+func (mx *MatrixBot) DummyLogin() error {
+	return mx.login(map[string]string{"type": LoginDummy})
+}
+
+func (mx *MatrixBot) login(payload interface{}) error {
+	creq := mx.NewJSONRequest(payload, "/login").POST()
+	if !creq.OK() {
+		return creq.Error
+	}
+
+	var dat loginInfo
+	err := creq.JSON(&dat)
+	if err != nil {
+		return err
+	}
+
+	if dat.Error != "" {
+		return fmt.Errorf(dat.Error)
+	} else if !creq.CheckStatusOK() {
+		return fmt.Errorf("HTTP %d", creq.Response.StatusCode)
+	}
+
+	mx.AccessToken = dat.AccessToken
+	mx.MatrixID = dat.UserID
+
+	return nil
+}
diff --git a/vendor/maunium.net/go/mautrix/event.go b/vendor/maunium.net/go/mautrix/event.go
new file mode 100644
index 0000000..174bbf5
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/event.go
@@ -0,0 +1,59 @@
+// mautrix - A Matrix client-server library intended for bots.
+// Copyright (C) 2017 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package mautrix
+
+// EventContent contains the name and body of an event
+type EventContent struct {
+	Name string `json:"name"`
+	Body string `json:"body"`
+}
+
+// Unsigned contains the unsigned event contents
+type Unsigned struct {
+	InviteRoomState []Event `json:"invite_room_state"`
+}
+
+// Event represents a single event
+type Event struct {
+	ID               string                 `json:"event_id"`
+	Type             string                 `json:"type"`
+	Sender           string                 `json:"sender"`
+	StateKey         string                 `json:"state_key"`
+	Content          map[string]interface{} `json:"content"`
+	OriginServerTime int64                  `json:"origin_server_ts"`
+	Age              int64                  `json:"age"`
+	TransactionID    string                 `json:"txn_id"`
+	Unsigned         Unsigned               `json:"unsigned"`
+
+	Room *Room `json:"-"`
+}
+
+// CanMarkRead checks if the event can be marked as read
+func (evt Event) CanMarkRead() bool {
+	return len(evt.ID) > 0
+}
+
+// MarkRead marks this event as read
+func (evt Event) MarkRead() bool {
+	if evt.CanMarkRead() {
+		creq := evt.Room.Session.NewPlainRequest(
+			"/rooms/%s/receipt/%s/%s?access_token=%s",
+			evt.Room.ID, EvtRead, evt.ID, evt.Room.Session.AccessToken).POST()
+		return creq.CheckStatusOK()
+	}
+	return false
+}
diff --git a/vendor/maunium.net/go/mautrix/mautrix.go b/vendor/maunium.net/go/mautrix/mautrix.go
new file mode 100644
index 0000000..478bd5e
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/mautrix.go
@@ -0,0 +1,90 @@
+// mautrix - A Matrix client-server library intended for bots.
+// Copyright (C) 2017 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package mautrix
+
+import (
+	"fmt"
+	"time"
+)
+
+// MatrixBot is a client-server Matrix session
+type MatrixBot struct {
+	NextBatch   string
+	Rooms       map[string]*Room
+	Invites     map[string]*Invite
+	Presence    map[string]string
+	AccessToken string
+	MatrixID    string
+	HomeServer  string
+	TxnID       int
+	Timeline    chan Event
+	InviteChan  chan string
+	JoinChan    chan string
+	stop        chan bool
+}
+
+// Listen for updates from the homeserver
+func (mx *MatrixBot) Listen() {
+	sleepTime := 1 * time.Second
+Loop:
+	for {
+		select {
+		case <-mx.stop:
+			break Loop
+		default:
+			err := mx.Sync()
+			if err != nil {
+				fmt.Println(err)
+				time.Sleep(sleepTime)
+				if sleepTime < 60 * time.Second {
+					sleepTime += 1 * time.Second
+				}
+			}
+		}
+	}
+}
+
+// Stop the listener
+func (mx *MatrixBot) Stop() {
+	mx.stop <- true
+}
+
+// GetURL gets the URL for the given path on this session
+func (mx *MatrixBot) GetURL(path string, args ...interface{}) string {
+	return fmt.Sprintf("%s/_matrix/client/r0%s", mx.HomeServer, fmt.Sprintf(path, args...))
+}
+
+// NextTransactionID returns the next message transaction ID
+func (mx *MatrixBot) NextTransactionID() int {
+	mx.TxnID++
+	return mx.TxnID
+}
+
+// Create a Session
+func Create(homeserver string) *MatrixBot {
+	mx := MatrixBot{HomeServer: homeserver,
+		Timeline:   make(chan Event, 10),
+		InviteChan: make(chan string, 10),
+		JoinChan:   make(chan string, 10),
+		Presence:   make(map[string]string),
+		Invites:    make(map[string]*Invite),
+		Rooms:      make(map[string]*Room),
+		stop:       make(chan bool),
+	}
+
+	return &mx
+}
diff --git a/vendor/maunium.net/go/mautrix/request.go b/vendor/maunium.net/go/mautrix/request.go
new file mode 100644
index 0000000..6025e92
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/request.go
@@ -0,0 +1,131 @@
+// mautrix - A Matrix client-server library intended for bots.
+// Copyright (C) 2017 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package mautrix
+
+import (
+	"encoding/json"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"strings"
+)
+
+// Request wraps a Matrix API request.
+type Request struct {
+	URL         string
+	Payload     string
+	ContentType string
+	Session     *MatrixBot
+}
+
+// NewPlainRequest creates a new payloadless Matrix API request.
+func (mx *MatrixBot) NewPlainRequest(url string, urlArgs ...interface{}) Request {
+	return Request{Session: mx, URL: mx.GetURL(url, urlArgs...)}
+}
+
+// NewRequest creates a new Matrix API request with a custom payload.
+func (mx *MatrixBot) NewRequest(payload, contentType, url string, urlArgs ...interface{}) Request {
+	return Request{Session: mx, Payload: payload, ContentType: contentType, URL: mx.GetURL(url, urlArgs...)}
+}
+
+// NewJSONRequest creates a new Matrix API request and marshals the given object to JSON and uses it as the payload.
+func (mx *MatrixBot) NewJSONRequest(payload interface{}, url string, urlArgs ...interface{}) Request {
+	payloadData, _ := json.Marshal(payload)
+	return mx.NewRequest(string(payloadData), "application/json", url, urlArgs...)
+}
+
+// CompletedRequest is a Matrix API request that has been called.
+type CompletedRequest struct {
+	Source   Request
+	Response *http.Response
+	Method   string
+	Error    error
+}
+
+// GET executes this request with HTTP GET.
+func (req Request) GET() CompletedRequest {
+	return req.HTTP(http.MethodGet)
+}
+
+// PUT executes this request with HTTP PUT.
+func (req Request) PUT() CompletedRequest {
+	return req.HTTP(http.MethodPut)
+}
+
+// POST executes this request with HTTP POST.
+func (req Request) POST() CompletedRequest {
+	return req.HTTP(http.MethodPost)
+}
+
+// HTTP executes this request with the given HTTP method.
+func (req Request) HTTP(method string) CompletedRequest {
+	var payloadReader io.Reader
+	if len(req.Payload) > 0 {
+		payloadReader = strings.NewReader(req.Payload)
+	}
+
+	httpReq, err := http.NewRequest(method, req.URL, payloadReader)
+	if err != nil {
+		return CompletedRequest{Source: req, Method: method, Response: nil, Error: err}
+	}
+
+	if len(req.ContentType) > 0 {
+		httpReq.Header.Set("Content-Type", req.ContentType)
+	}
+
+	resp, err := http.DefaultClient.Do(httpReq)
+	return CompletedRequest{Source: req, Method: method, Response: resp, Error: err}
+}
+
+// OK checks if the request completed successfully.
+func (creq CompletedRequest) OK() bool {
+	if creq.Response == nil || creq.Error != nil {
+		creq.Close()
+		return false
+	}
+	return true
+}
+
+// CheckStatusOK makes sure that the response HTTP status code is OK 200.
+func (creq CompletedRequest) CheckStatusOK() bool {
+	return creq.CheckStatus(http.StatusOK)
+}
+
+// CheckStatus makes sure that the response HTTP status code is correct.
+func (creq CompletedRequest) CheckStatus(expected int) bool {
+	return creq.Response.StatusCode == expected
+}
+
+// JSON reads the body of the response and parses it from JSON into an interface.
+func (creq CompletedRequest) JSON(decodeInto interface{}) error {
+	defer creq.Close()
+	return json.NewDecoder(creq.Response.Body).Decode(&decodeInto)
+}
+
+// Text reads the body of the response and returns it as a string.
+func (creq CompletedRequest) Text() (string, error) {
+	defer creq.Close()
+	data, err := ioutil.ReadAll(creq.Response.Body)
+	return string(data), err
+}
+
+// Close closes the response body stream.
+func (creq CompletedRequest) Close() {
+	if creq.Response != nil {
+		creq.Response.Body.Close()
+	}
+}
diff --git a/vendor/maunium.net/go/mautrix/room.go b/vendor/maunium.net/go/mautrix/room.go
new file mode 100644
index 0000000..337cfab
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/room.go
@@ -0,0 +1,146 @@
+// mautrix - A Matrix client-server library intended for bots.
+// Copyright (C) 2017 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package mautrix
+
+import (
+	"fmt"
+	"html"
+
+	"github.com/grokify/html-strip-tags-go"
+)
+
+// Invite wraps an invite to a room
+type Invite struct {
+	Sender  string
+	Name    string
+	ID      string
+	Members map[string]string
+	Session *MatrixBot
+}
+
+// Member contains some information about a room member
+type Member struct {
+	Membership  string
+	Power       int
+	DisplayName string
+}
+
+// SendResponse wraps the response to a room send request
+type SendResponse struct {
+	EventID   string `json:"event_id"`
+	Error     string `json:"error"`
+	ErrorCode string `json:"errcode"`
+}
+
+// Room is a room
+type Room struct {
+	ID      string
+	Name    string
+	Members map[string]Member
+	Aliases []string
+	Session *MatrixBot
+}
+
+func (mx *MatrixBot) createRoom(roomID string) *Room {
+	return &Room{Session: mx, ID: roomID, Members: make(map[string]Member)}
+}
+
+// GetRoom gets the room pointer with the given ID
+func (mx *MatrixBot) GetRoom(roomID string) *Room {
+	room, ok := mx.Rooms[roomID]
+	if !ok {
+		room = mx.createRoom(roomID)
+		mx.Rooms[roomID] = room
+	}
+	return room
+}
+
+// Send sends a message to this room
+func (r *Room) Send(message string) (SendResponse, error) {
+	creq := r.Session.NewJSONRequest(
+		map[string]string{
+			"msgtype": MsgNotice,
+			"body":    message,
+		},
+		"/rooms/%s/send/%s/%d?access_token=%s",
+		r.ID, EvtRoomMessage, r.Session.NextTransactionID(), r.Session.AccessToken,
+	).PUT()
+	if !creq.OK() {
+		return SendResponse{}, creq.Error
+	}
+
+	var data SendResponse
+	err := creq.JSON(&data)
+	if err != nil {
+		return SendResponse{}, err
+	}
+	return data, nil
+}
+
+// Sendf formats and sends a message to this room
+func (r *Room) Sendf(message string, args ...interface{}) (SendResponse, error) {
+	return r.Send(fmt.Sprintf(message, args...))
+}
+
+// SendHTML sends a HTML message to this room
+func (r *Room) SendHTML(message string) (SendResponse, error) {
+	creq := r.Session.NewJSONRequest(
+		map[string]string{
+			"msgtype":        MsgNotice,
+			"body":           html.UnescapeString(strip.StripTags(message)),
+			"formatted_body": message,
+			"format":         "org.matrix.custom.html",
+		},
+		"/rooms/%s/send/%s/%d?access_token=%s",
+		r.ID, EvtRoomMessage, r.Session.NextTransactionID(), r.Session.AccessToken,
+	).PUT()
+	if !creq.OK() {
+		return SendResponse{}, creq.Error
+	}
+
+	var data SendResponse
+	err := creq.JSON(&data)
+	if err != nil {
+		return SendResponse{}, err
+	}
+	return data, nil
+}
+
+// SendfHTML formats and sends a HTML message to this room
+func (r *Room) SendfHTML(message string, args ...interface{}) (SendResponse, error) {
+	return r.SendHTML(fmt.Sprintf(message, args...))
+}
+
+// Join a room
+func (mx *MatrixBot) Join(roomID string) error {
+	creq := mx.NewPlainRequest(
+		"/rooms/%s/join?access_token=%s",
+		roomID, mx.AccessToken,
+	).POST()
+	if !creq.OK() {
+		return creq.Error
+	} else if !creq.CheckStatusOK() {
+		errstr, _ := creq.Text()
+		return fmt.Errorf("HTTP %d: %s", creq.Response.StatusCode, errstr)
+	}
+	return nil
+}
+
+// Accept the invite
+func (i Invite) Accept() error {
+	return i.Session.Join(i.ID)
+}
diff --git a/vendor/maunium.net/go/mautrix/sync.go b/vendor/maunium.net/go/mautrix/sync.go
new file mode 100644
index 0000000..effbc38
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/sync.go
@@ -0,0 +1,143 @@
+// 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 mautrix
+
+// SyncData contains everything in a single synchronization
+type SyncData struct {
+	NextBatch string         `json:"next_batch"`
+	Rooms     SyncRooms      `json:"rooms"`
+	Presence  EventContainer `json:"presence"`
+	Initial   bool           `json:"-"`
+}
+
+// SyncRooms contains all joined and invited rooms
+type SyncRooms struct {
+	Join    map[string]SRoom       `json:"join"`
+	Invited map[string]InvitedRoom `json:"invite"`
+	Leave   map[string]LeftRoom    `json:"leave"`
+}
+
+// LeftRoom is a room the user has left or been banned from
+type LeftRoom struct {
+	// Member list and other persistent data
+	State EventContainer `json:"state"`
+	// Messages, state changes, etc..
+	Timeline EventContainer `json:"timeline"`
+}
+
+// InvitedRoom is a room that the user has been invited to
+type InvitedRoom struct {
+	InviteState EventContainer `json:"invite_state"`
+}
+
+// SRoom represents a single room
+type SRoom struct {
+	// Typing notifications, presence updates, etc..
+	Ephemeral EventContainer `json:"ephemeral"`
+	// Member list and other persistent data
+	State EventContainer `json:"state"`
+	// Messages, state changes, etc..
+	Timeline Timeline `json:"timeline"`
+	// Tags and custom configs
+	AccountData EventContainer `json:"account_data"`
+}
+
+// EventContainer contains an array of events
+type EventContainer struct {
+	Events []Event `json:"events"`
+}
+
+// Timeline wraps things in a timeline
+type Timeline struct {
+	EventContainer
+	Limited   bool   `json:"limited"`
+	PrevBatch string `json:"prev_batch"`
+}
+
+// Sync the current status with the homeserver
+func (mx *MatrixBot) Sync() error {
+	var req Request
+	if len(mx.NextBatch) == 0 {
+		req = mx.NewPlainRequest("/sync?access_token=%s&timeout=10000", mx.AccessToken)
+	} else {
+		req = mx.NewPlainRequest("/sync?since=%s&access_token=%s&timeout=10000", mx.NextBatch, mx.AccessToken)
+	}
+	creq := req.GET()
+	if !creq.OK() {
+		return creq.Error
+	}
+
+	data := SyncData{}
+	err := creq.JSON(&data)
+	if err != nil {
+		return err
+	}
+	data.Initial = len(mx.NextBatch) == 0
+
+	mx.NextBatch = data.NextBatch
+	mx.syncPresence(data)
+	mx.syncJoinedRooms(data)
+	mx.syncInvitedRooms(data)
+	return nil
+}
+
+func (mx *MatrixBot) syncPresence(data SyncData) {
+	for _, evt := range data.Presence.Events {
+		mx.Presence[evt.Sender], _ = evt.Content["presence"].(string)
+	}
+}
+
+func (mx *MatrixBot) syncJoinedRooms(data SyncData) {
+	for roomID, v := range data.Rooms.Join {
+		room := mx.GetRoom(roomID)
+		for _, event := range v.State.Events {
+			switch {
+			case event.Type == EvtRoomName:
+				room.Name, _ = event.Content["name"].(string)
+			case event.Type == EvtRoomMember:
+				member, ok := room.Members[event.StateKey]
+				if !ok {
+					member = Member{}
+				}
+				member.Membership, _ = event.Content["membership"].(string)
+				member.DisplayName, _ = event.Content["displayname"].(string)
+				room.Members[event.StateKey] = member
+			}
+		}
+
+		if !data.Initial {
+			for _, event := range v.Timeline.Events {
+				event.Room = room
+				mx.Timeline <- event
+			}
+		}
+	}
+}
+
+func (mx *MatrixBot) syncInvitedRooms(data SyncData) {
+	for roomID, v := range data.Rooms.Invited {
+		invite, old := mx.Invites[roomID]
+		if !old {
+			invite = &Invite{
+				Session: mx,
+				ID:      roomID,
+				Members: make(map[string]string),
+			}
+		}
+		for _, event := range v.InviteState.Events {
+			invite.Sender = event.Sender
+			switch event.Type {
+			case EvtRoomMember:
+				invite.Members[event.StateKey], _ = event.Content["membership"].(string)
+			case EvtRoomName:
+				invite.Name, _ = event.Content["name"].(string)
+			}
+		}
+		mx.Invites[roomID] = invite
+		if !old {
+			mx.InviteChan <- roomID
+		}
+	}
+}
diff --git a/vendor/maunium.net/go/mautrix/types.go b/vendor/maunium.net/go/mautrix/types.go
new file mode 100644
index 0000000..14a1684
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/types.go
@@ -0,0 +1,83 @@
+// mautrix - A Matrix client-server library intended for bots.
+// Copyright (C) 2017 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package mautrix
+
+// Room events
+const (
+	EvtRoomAliases           = "m.room.aliases"
+	EvtRoomCanonicalAlias    = "m.room.canonical_alias"
+	EvtRoomCreate            = "m.room.create"
+	EvtRoomJoinRules         = "m.room.join_rules"
+	EvtRoomMember            = "m.room.member"
+	EvtRoomPowerLevels       = "m.room.power_levels"
+	EvtRoomRedaction         = "m.room.redaction"
+	EvtRoomHistoryVisibility = "m.room.history_visibility"
+	EvtRoomThirdPartyInvite  = "m.room.third_party_invite"
+	EvtRoomGuestAccess       = "m.room.guest_access"
+)
+
+// Instant messaging events
+const (
+	EvtRoomMessage         = "m.room.message"
+	EvtRoomMessageFeedback = "m.room.message.feedback"
+	EvtRoomName            = "m.room.name"
+	EvtRoomTopic           = "m.room.topic"
+	EvtRoomAvatar          = "m.room.avatar"
+)
+
+// Message types
+const (
+	MsgText     = "m.text"
+	MsgEmote    = "m.emote"
+	MsgNotice   = "m.notice"
+	MsgImage    = "m.image"
+	MsgFile     = "m.file"
+	MsgLocation = "m.location"
+	MsgVideo    = "m.video"
+	MsgAudio    = "m.audio"
+)
+
+// Login types
+const (
+	LoginPassword   = "m.login.password"
+	LoginReCAPTCHA  = "m.login.recaptcha"
+	LoginOAuth2     = "m.login.oauth2"
+	LoginEmailIdent = "m.login.email.identity"
+	LoginToken      = "m.login.token"
+	LoginDummy      = "m.login.dummy"
+)
+
+// VoIP events
+const (
+	EvtCallInvite     = "m.call.invite"
+	EvtCallCandidates = "m.call.candidates"
+	EvtCallAnswer     = "m.call.answer"
+	EvtCallHangup     = "m.call.hangup"
+)
+
+// Presence events
+const (
+	EvtTyping   = "m.typing"
+	EvtReceipt  = "m.receipt"
+	EvtRead     = "m.read"
+	EvtPresence = "m.presence"
+)
+
+// Room tagging
+const (
+	EvtTag = "m.tag"
+)
-- 
GitLab