Torkel Ödegaard 11 лет назад
Родитель
Сommit
9cc47b6b58
34 измененных файлов с 5005 добавлено и 0 удалено
  1. 13 0
      Godeps/Godeps.json
  2. 432 0
      Godeps/_workspace/src/golang.org/x/net/context/context.go
  3. 553 0
      Godeps/_workspace/src/golang.org/x/net/context/context_test.go
  4. 26 0
      Godeps/_workspace/src/golang.org/x/net/context/withtimeout_test.go
  5. 14 0
      Godeps/_workspace/src/golang.org/x/oauth2/.travis.yml
  6. 3 0
      Godeps/_workspace/src/golang.org/x/oauth2/AUTHORS
  7. 25 0
      Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTING.md
  8. 3 0
      Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTORS
  9. 27 0
      Godeps/_workspace/src/golang.org/x/oauth2/LICENSE
  10. 18 0
      Godeps/_workspace/src/golang.org/x/oauth2/README.md
  11. 39 0
      Godeps/_workspace/src/golang.org/x/oauth2/client_appengine.go
  12. 72 0
      Godeps/_workspace/src/golang.org/x/oauth2/example_test.go
  13. 16 0
      Godeps/_workspace/src/golang.org/x/oauth2/github/github.go
  14. 37 0
      Godeps/_workspace/src/golang.org/x/oauth2/google/appengine.go
  15. 36 0
      Godeps/_workspace/src/golang.org/x/oauth2/google/appenginevm.go
  16. 132 0
      Godeps/_workspace/src/golang.org/x/oauth2/google/example_test.go
  17. 115 0
      Godeps/_workspace/src/golang.org/x/oauth2/google/google.go
  18. 68 0
      Godeps/_workspace/src/golang.org/x/oauth2/google/source_appengine.go
  19. 37 0
      Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2.go
  20. 160 0
      Godeps/_workspace/src/golang.org/x/oauth2/jws/jws.go
  21. 155 0
      Godeps/_workspace/src/golang.org/x/oauth2/jwt.go
  22. 126 0
      Godeps/_workspace/src/golang.org/x/oauth2/jwt_test.go
  23. 421 0
      Godeps/_workspace/src/golang.org/x/oauth2/oauth2.go
  24. 260 0
      Godeps/_workspace/src/golang.org/x/oauth2/oauth2_test.go
  25. 87 0
      Godeps/_workspace/src/golang.org/x/oauth2/token.go
  26. 138 0
      Godeps/_workspace/src/golang.org/x/oauth2/transport.go
  27. 53 0
      Godeps/_workspace/src/golang.org/x/oauth2/transport_test.go
  28. 1 0
      Godeps/_workspace/src/gopkg.in/ini.v1/.gitignore
  29. 191 0
      Godeps/_workspace/src/gopkg.in/ini.v1/LICENSE
  30. 261 0
      Godeps/_workspace/src/gopkg.in/ini.v1/README.md
  31. 252 0
      Godeps/_workspace/src/gopkg.in/ini.v1/README_ZH.md
  32. 852 0
      Godeps/_workspace/src/gopkg.in/ini.v1/ini.go
  33. 380 0
      Godeps/_workspace/src/gopkg.in/ini.v1/ini_test.go
  34. 2 0
      Godeps/_workspace/src/gopkg.in/ini.v1/testdata/conf.ini

+ 13 - 0
Godeps/Godeps.json

@@ -35,6 +35,19 @@
 		{
 			"ImportPath": "github.com/mattn/go-sqlite3",
 			"Rev": "d10e2c8f62100097910367dee90a9bd89d426a44"
+		},
+		{
+			"ImportPath": "golang.org/x/net/context",
+			"Rev": "3053e46bf4d836639f474dd738a58070463890e9"
+		},
+		{
+			"ImportPath": "golang.org/x/oauth2",
+			"Rev": "e5909d4679a1926c774c712b343f10b8298687a3"
+		},
+		{
+			"ImportPath": "gopkg.in/ini.v1",
+			"Comment": "v1",
+			"Rev": "70a46eeb84707a1d77984c1a5b7af666063fc5e7"
 		}
 	]
 }

+ 432 - 0
Godeps/_workspace/src/golang.org/x/net/context/context.go

@@ -0,0 +1,432 @@
+// 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.
+//
+// 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"
+
+import (
+	"errors"
+	"fmt"
+	"sync"
+	"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:
+	//
+	// 	// DoSomething calls DoSomethingSlow and returns as soon as
+	// 	// it returns or ctx.Done is closed.
+	// 	func DoSomething(ctx context.Context) (Result, error) {
+	// 		c := make(chan Result, 1)
+	// 		go func() { c <- DoSomethingSlow(ctx) }()
+	// 		select {
+	// 		case res := <-c:
+	// 			return res, nil
+	// 		case <-ctx.Done():
+	// 			return nil, ctx.Err()
+	// 		}
+	// 	}
+	//
+	// 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{}
+}
+
+// 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")
+
+// 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)
+)
+
+// 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's 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
+}
+
+// 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()
+
+// 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.
+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
+		}
+	}
+}
+
+// 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 {
+		if p, ok := parentCancelCtx(c.Context); ok {
+			p.mu.Lock()
+			if p.children != nil {
+				delete(p.children, c)
+			}
+			p.mu.Unlock()
+		}
+	}
+}
+
+// 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 the deadline
+// timer, 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(removeFromParent, err)
+	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 the deadline
+// timer, 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)
+}

+ 553 - 0
Godeps/_workspace/src/golang.org/x/net/context/context_test.go

@@ -0,0 +1,553 @@
+// 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
+
+import (
+	"fmt"
+	"math/rand"
+	"runtime"
+	"strings"
+	"sync"
+	"testing"
+	"time"
+)
+
+// otherContext is a Context that's not one of the types defined in context.go.
+// This lets us test code paths that differ based on the underlying type of the
+// Context.
+type otherContext struct {
+	Context
+}
+
+func TestBackground(t *testing.T) {
+	c := Background()
+	if c == nil {
+		t.Fatalf("Background returned nil")
+	}
+	select {
+	case x := <-c.Done():
+		t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
+	default:
+	}
+	if got, want := fmt.Sprint(c), "context.Background"; got != want {
+		t.Errorf("Background().String() = %q want %q", got, want)
+	}
+}
+
+func TestTODO(t *testing.T) {
+	c := TODO()
+	if c == nil {
+		t.Fatalf("TODO returned nil")
+	}
+	select {
+	case x := <-c.Done():
+		t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
+	default:
+	}
+	if got, want := fmt.Sprint(c), "context.TODO"; got != want {
+		t.Errorf("TODO().String() = %q want %q", got, want)
+	}
+}
+
+func TestWithCancel(t *testing.T) {
+	c1, cancel := WithCancel(Background())
+
+	if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want {
+		t.Errorf("c1.String() = %q want %q", got, want)
+	}
+
+	o := otherContext{c1}
+	c2, _ := WithCancel(o)
+	contexts := []Context{c1, o, c2}
+
+	for i, c := range contexts {
+		if d := c.Done(); d == nil {
+			t.Errorf("c[%d].Done() == %v want non-nil", i, d)
+		}
+		if e := c.Err(); e != nil {
+			t.Errorf("c[%d].Err() == %v want nil", i, e)
+		}
+
+		select {
+		case x := <-c.Done():
+			t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
+		default:
+		}
+	}
+
+	cancel()
+	time.Sleep(100 * time.Millisecond) // let cancelation propagate
+
+	for i, c := range contexts {
+		select {
+		case <-c.Done():
+		default:
+			t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i)
+		}
+		if e := c.Err(); e != Canceled {
+			t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled)
+		}
+	}
+}
+
+func TestParentFinishesChild(t *testing.T) {
+	// Context tree:
+	// parent -> cancelChild
+	// parent -> valueChild -> timerChild
+	parent, cancel := WithCancel(Background())
+	cancelChild, stop := WithCancel(parent)
+	defer stop()
+	valueChild := WithValue(parent, "key", "value")
+	timerChild, stop := WithTimeout(valueChild, 10000*time.Hour)
+	defer stop()
+
+	select {
+	case x := <-parent.Done():
+		t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
+	case x := <-cancelChild.Done():
+		t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x)
+	case x := <-timerChild.Done():
+		t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x)
+	case x := <-valueChild.Done():
+		t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x)
+	default:
+	}
+
+	// The parent's children should contain the two cancelable children.
+	pc := parent.(*cancelCtx)
+	cc := cancelChild.(*cancelCtx)
+	tc := timerChild.(*timerCtx)
+	pc.mu.Lock()
+	if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] {
+		t.Errorf("bad linkage: pc.children = %v, want %v and %v",
+			pc.children, cc, tc)
+	}
+	pc.mu.Unlock()
+
+	if p, ok := parentCancelCtx(cc.Context); !ok || p != pc {
+		t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc)
+	}
+	if p, ok := parentCancelCtx(tc.Context); !ok || p != pc {
+		t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc)
+	}
+
+	cancel()
+
+	pc.mu.Lock()
+	if len(pc.children) != 0 {
+		t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children)
+	}
+	pc.mu.Unlock()
+
+	// parent and children should all be finished.
+	check := func(ctx Context, name string) {
+		select {
+		case <-ctx.Done():
+		default:
+			t.Errorf("<-%s.Done() blocked, but shouldn't have", name)
+		}
+		if e := ctx.Err(); e != Canceled {
+			t.Errorf("%s.Err() == %v want %v", name, e, Canceled)
+		}
+	}
+	check(parent, "parent")
+	check(cancelChild, "cancelChild")
+	check(valueChild, "valueChild")
+	check(timerChild, "timerChild")
+
+	// WithCancel should return a canceled context on a canceled parent.
+	precanceledChild := WithValue(parent, "key", "value")
+	select {
+	case <-precanceledChild.Done():
+	default:
+		t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have")
+	}
+	if e := precanceledChild.Err(); e != Canceled {
+		t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled)
+	}
+}
+
+func TestChildFinishesFirst(t *testing.T) {
+	cancelable, stop := WithCancel(Background())
+	defer stop()
+	for _, parent := range []Context{Background(), cancelable} {
+		child, cancel := WithCancel(parent)
+
+		select {
+		case x := <-parent.Done():
+			t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
+		case x := <-child.Done():
+			t.Errorf("<-child.Done() == %v want nothing (it should block)", x)
+		default:
+		}
+
+		cc := child.(*cancelCtx)
+		pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background()
+		if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) {
+			t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok)
+		}
+
+		if pcok {
+			pc.mu.Lock()
+			if len(pc.children) != 1 || !pc.children[cc] {
+				t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc)
+			}
+			pc.mu.Unlock()
+		}
+
+		cancel()
+
+		if pcok {
+			pc.mu.Lock()
+			if len(pc.children) != 0 {
+				t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children)
+			}
+			pc.mu.Unlock()
+		}
+
+		// child should be finished.
+		select {
+		case <-child.Done():
+		default:
+			t.Errorf("<-child.Done() blocked, but shouldn't have")
+		}
+		if e := child.Err(); e != Canceled {
+			t.Errorf("child.Err() == %v want %v", e, Canceled)
+		}
+
+		// parent should not be finished.
+		select {
+		case x := <-parent.Done():
+			t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
+		default:
+		}
+		if e := parent.Err(); e != nil {
+			t.Errorf("parent.Err() == %v want nil", e)
+		}
+	}
+}
+
+func testDeadline(c Context, wait time.Duration, t *testing.T) {
+	select {
+	case <-time.After(wait):
+		t.Fatalf("context should have timed out")
+	case <-c.Done():
+	}
+	if e := c.Err(); e != DeadlineExceeded {
+		t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded)
+	}
+}
+
+func TestDeadline(t *testing.T) {
+	c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
+	if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
+		t.Errorf("c.String() = %q want prefix %q", got, prefix)
+	}
+	testDeadline(c, 200*time.Millisecond, t)
+
+	c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
+	o := otherContext{c}
+	testDeadline(o, 200*time.Millisecond, t)
+
+	c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
+	o = otherContext{c}
+	c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond))
+	testDeadline(c, 200*time.Millisecond, t)
+}
+
+func TestTimeout(t *testing.T) {
+	c, _ := WithTimeout(Background(), 100*time.Millisecond)
+	if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
+		t.Errorf("c.String() = %q want prefix %q", got, prefix)
+	}
+	testDeadline(c, 200*time.Millisecond, t)
+
+	c, _ = WithTimeout(Background(), 100*time.Millisecond)
+	o := otherContext{c}
+	testDeadline(o, 200*time.Millisecond, t)
+
+	c, _ = WithTimeout(Background(), 100*time.Millisecond)
+	o = otherContext{c}
+	c, _ = WithTimeout(o, 300*time.Millisecond)
+	testDeadline(c, 200*time.Millisecond, t)
+}
+
+func TestCanceledTimeout(t *testing.T) {
+	c, _ := WithTimeout(Background(), 200*time.Millisecond)
+	o := otherContext{c}
+	c, cancel := WithTimeout(o, 400*time.Millisecond)
+	cancel()
+	time.Sleep(100 * time.Millisecond) // let cancelation propagate
+	select {
+	case <-c.Done():
+	default:
+		t.Errorf("<-c.Done() blocked, but shouldn't have")
+	}
+	if e := c.Err(); e != Canceled {
+		t.Errorf("c.Err() == %v want %v", e, Canceled)
+	}
+}
+
+type key1 int
+type key2 int
+
+var k1 = key1(1)
+var k2 = key2(1) // same int as k1, different type
+var k3 = key2(3) // same type as k2, different int
+
+func TestValues(t *testing.T) {
+	check := func(c Context, nm, v1, v2, v3 string) {
+		if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 {
+			t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0)
+		}
+		if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 {
+			t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0)
+		}
+		if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 {
+			t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0)
+		}
+	}
+
+	c0 := Background()
+	check(c0, "c0", "", "", "")
+
+	c1 := WithValue(Background(), k1, "c1k1")
+	check(c1, "c1", "c1k1", "", "")
+
+	if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want {
+		t.Errorf("c.String() = %q want %q", got, want)
+	}
+
+	c2 := WithValue(c1, k2, "c2k2")
+	check(c2, "c2", "c1k1", "c2k2", "")
+
+	c3 := WithValue(c2, k3, "c3k3")
+	check(c3, "c2", "c1k1", "c2k2", "c3k3")
+
+	c4 := WithValue(c3, k1, nil)
+	check(c4, "c4", "", "c2k2", "c3k3")
+
+	o0 := otherContext{Background()}
+	check(o0, "o0", "", "", "")
+
+	o1 := otherContext{WithValue(Background(), k1, "c1k1")}
+	check(o1, "o1", "c1k1", "", "")
+
+	o2 := WithValue(o1, k2, "o2k2")
+	check(o2, "o2", "c1k1", "o2k2", "")
+
+	o3 := otherContext{c4}
+	check(o3, "o3", "", "c2k2", "c3k3")
+
+	o4 := WithValue(o3, k3, nil)
+	check(o4, "o4", "", "c2k2", "")
+}
+
+func TestAllocs(t *testing.T) {
+	bg := Background()
+	for _, test := range []struct {
+		desc       string
+		f          func()
+		limit      float64
+		gccgoLimit float64
+	}{
+		{
+			desc:       "Background()",
+			f:          func() { Background() },
+			limit:      0,
+			gccgoLimit: 0,
+		},
+		{
+			desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1),
+			f: func() {
+				c := WithValue(bg, k1, nil)
+				c.Value(k1)
+			},
+			limit:      3,
+			gccgoLimit: 3,
+		},
+		{
+			desc: "WithTimeout(bg, 15*time.Millisecond)",
+			f: func() {
+				c, _ := WithTimeout(bg, 15*time.Millisecond)
+				<-c.Done()
+			},
+			limit:      8,
+			gccgoLimit: 13,
+		},
+		{
+			desc: "WithCancel(bg)",
+			f: func() {
+				c, cancel := WithCancel(bg)
+				cancel()
+				<-c.Done()
+			},
+			limit:      5,
+			gccgoLimit: 8,
+		},
+		{
+			desc: "WithTimeout(bg, 100*time.Millisecond)",
+			f: func() {
+				c, cancel := WithTimeout(bg, 100*time.Millisecond)
+				cancel()
+				<-c.Done()
+			},
+			limit:      8,
+			gccgoLimit: 25,
+		},
+	} {
+		limit := test.limit
+		if runtime.Compiler == "gccgo" {
+			// gccgo does not yet do escape analysis.
+			// TOOD(iant): Remove this when gccgo does do escape analysis.
+			limit = test.gccgoLimit
+		}
+		if n := testing.AllocsPerRun(100, test.f); n > limit {
+			t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit))
+		}
+	}
+}
+
+func TestSimultaneousCancels(t *testing.T) {
+	root, cancel := WithCancel(Background())
+	m := map[Context]CancelFunc{root: cancel}
+	q := []Context{root}
+	// Create a tree of contexts.
+	for len(q) != 0 && len(m) < 100 {
+		parent := q[0]
+		q = q[1:]
+		for i := 0; i < 4; i++ {
+			ctx, cancel := WithCancel(parent)
+			m[ctx] = cancel
+			q = append(q, ctx)
+		}
+	}
+	// Start all the cancels in a random order.
+	var wg sync.WaitGroup
+	wg.Add(len(m))
+	for _, cancel := range m {
+		go func(cancel CancelFunc) {
+			cancel()
+			wg.Done()
+		}(cancel)
+	}
+	// Wait on all the contexts in a random order.
+	for ctx := range m {
+		select {
+		case <-ctx.Done():
+		case <-time.After(1 * time.Second):
+			buf := make([]byte, 10<<10)
+			n := runtime.Stack(buf, true)
+			t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n])
+		}
+	}
+	// Wait for all the cancel functions to return.
+	done := make(chan struct{})
+	go func() {
+		wg.Wait()
+		close(done)
+	}()
+	select {
+	case <-done:
+	case <-time.After(1 * time.Second):
+		buf := make([]byte, 10<<10)
+		n := runtime.Stack(buf, true)
+		t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n])
+	}
+}
+
+func TestInterlockedCancels(t *testing.T) {
+	parent, cancelParent := WithCancel(Background())
+	child, cancelChild := WithCancel(parent)
+	go func() {
+		parent.Done()
+		cancelChild()
+	}()
+	cancelParent()
+	select {
+	case <-child.Done():
+	case <-time.After(1 * time.Second):
+		buf := make([]byte, 10<<10)
+		n := runtime.Stack(buf, true)
+		t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n])
+	}
+}
+
+func TestLayersCancel(t *testing.T) {
+	testLayers(t, time.Now().UnixNano(), false)
+}
+
+func TestLayersTimeout(t *testing.T) {
+	testLayers(t, time.Now().UnixNano(), true)
+}
+
+func testLayers(t *testing.T, seed int64, testTimeout bool) {
+	rand.Seed(seed)
+	errorf := func(format string, a ...interface{}) {
+		t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...)
+	}
+	const (
+		timeout   = 200 * time.Millisecond
+		minLayers = 30
+	)
+	type value int
+	var (
+		vals      []*value
+		cancels   []CancelFunc
+		numTimers int
+		ctx       = Background()
+	)
+	for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ {
+		switch rand.Intn(3) {
+		case 0:
+			v := new(value)
+			ctx = WithValue(ctx, v, v)
+			vals = append(vals, v)
+		case 1:
+			var cancel CancelFunc
+			ctx, cancel = WithCancel(ctx)
+			cancels = append(cancels, cancel)
+		case 2:
+			var cancel CancelFunc
+			ctx, cancel = WithTimeout(ctx, timeout)
+			cancels = append(cancels, cancel)
+			numTimers++
+		}
+	}
+	checkValues := func(when string) {
+		for _, key := range vals {
+			if val := ctx.Value(key).(*value); key != val {
+				errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key)
+			}
+		}
+	}
+	select {
+	case <-ctx.Done():
+		errorf("ctx should not be canceled yet")
+	default:
+	}
+	if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) {
+		t.Errorf("ctx.String() = %q want prefix %q", s, prefix)
+	}
+	t.Log(ctx)
+	checkValues("before cancel")
+	if testTimeout {
+		select {
+		case <-ctx.Done():
+		case <-time.After(timeout + timeout/10):
+			errorf("ctx should have timed out")
+		}
+		checkValues("after timeout")
+	} else {
+		cancel := cancels[rand.Intn(len(cancels))]
+		cancel()
+		select {
+		case <-ctx.Done():
+		default:
+			errorf("ctx should be canceled")
+		}
+		checkValues("after cancel")
+	}
+}

+ 26 - 0
Godeps/_workspace/src/golang.org/x/net/context/withtimeout_test.go

@@ -0,0 +1,26 @@
+// 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_test
+
+import (
+	"fmt"
+	"time"
+
+	"golang.org/x/net/context"
+)
+
+func ExampleWithTimeout() {
+	// Pass a context with a timeout to tell a blocking function that it
+	// should abandon its work after the timeout elapses.
+	ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond)
+	select {
+	case <-time.After(200 * time.Millisecond):
+		fmt.Println("overslept")
+	case <-ctx.Done():
+		fmt.Println(ctx.Err()) // prints "context deadline exceeded"
+	}
+	// Output:
+	// context deadline exceeded
+}

+ 14 - 0
Godeps/_workspace/src/golang.org/x/oauth2/.travis.yml

@@ -0,0 +1,14 @@
+language: go
+
+go:
+  - 1.3
+  - 1.4
+
+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 -tags='appengine appenginevm' golang.org/x/oauth2/...
+
+script:
+  - go test -v -tags='appengine appenginevm' golang.org/x/oauth2/...

+ 3 - 0
Godeps/_workspace/src/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.

+ 25 - 0
Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTING.md

@@ -0,0 +1,25 @@
+# Contributing
+
+We don't use GitHub pull requests but use Gerrit for code reviews,
+similar to the Go project.
+
+1. Sign one of the contributor license agreements below.
+2. `go get golang.org/x/review/git-codereview` to install the code reviewing tool.
+3. Get the package by running `go get -d golang.org/x/oauth2`.
+Make changes and create a change by running `git codereview change <name>`, provide a command message, and use `git codereview mail` to create a Gerrit CL.
+Keep amending to the change and mail as your recieve feedback.
+
+For more information about the workflow, see Go's [Contribution Guidelines](https://golang.org/doc/contribute.html).
+
+Before we can accept any pull requests
+we have to jump through a couple of legal hurdles,
+primarily a Contributor License Agreement (CLA):
+
+- **If you are an individual writing original source code**
+  and you're sure you own the intellectual property,
+  then you'll need to sign an [individual CLA](http://code.google.com/legal/individual-cla-v1.0.html).
+- **If you work for a company that wants to allow you to contribute your work**,
+  then you'll need to sign a [corporate CLA](http://code.google.com/legal/corporate-cla-v1.0.html).
+
+You can sign these electronically (just scroll to the bottom).
+After that, we'll be able to accept your pull requests.

+ 3 - 0
Godeps/_workspace/src/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.

+ 27 - 0
Godeps/_workspace/src/golang.org/x/oauth2/LICENSE

@@ -0,0 +1,27 @@
+Copyright (c) 2009 The oauth2 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.

+ 18 - 0
Godeps/_workspace/src/golang.org/x/oauth2/README.md

@@ -0,0 +1,18 @@
+# OAuth2 for Go
+
+[![Build Status](https://travis-ci.org/golang/oauth2.svg?branch=master)](https://travis-ci.org/golang/oauth2)
+
+oauth2 package contains a client implementation for OAuth 2.0 spec.
+
+## Installation
+
+~~~~
+go get 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)
+
+

+ 39 - 0
Godeps/_workspace/src/golang.org/x/oauth2/client_appengine.go

@@ -0,0 +1,39 @@
+// Copyright 2014 The oauth2 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,!appenginevm
+
+// App Engine hooks.
+
+package oauth2
+
+import (
+	"log"
+	"net/http"
+	"sync"
+
+	"appengine"
+	"appengine/urlfetch"
+)
+
+var warnOnce sync.Once
+
+func init() {
+	registerContextClientFunc(contextClientAppEngine)
+}
+
+func contextClientAppEngine(ctx Context) (*http.Client, error) {
+	if actx, ok := ctx.(appengine.Context); ok {
+		return urlfetch.Client(actx), nil
+	}
+	// The user did it wrong. We'll log once (and hope they see it
+	// in dev_appserver), but stil return (nil, nil) in case some
+	// other contextClientFunc hook finds a way to proceed.
+	warnOnce.Do(gaeDoingItWrongHelp)
+	return nil, nil
+}
+
+func gaeDoingItWrongHelp() {
+	log.Printf("WARNING: you attempted to use the oauth2 package without passing a valid appengine.Context or *http.Request as the oauth2.Context. App Engine requires that all service RPCs (including urlfetch) be associated with an *http.Request/appengine.Context.")
+}

+ 72 - 0
Godeps/_workspace/src/golang.org/x/oauth2/example_test.go

@@ -0,0 +1,72 @@
+// Copyright 2014 The oauth2 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_test
+
+import (
+	"fmt"
+	"log"
+	"testing"
+
+	"golang.org/x/oauth2"
+)
+
+// TODO(jbd): Remove after Go 1.4.
+// Related to https://codereview.appspot.com/107320046
+func TestA(t *testing.T) {}
+
+func ExampleConfig() {
+	conf := &oauth2.Config{
+		ClientID:     "YOUR_CLIENT_ID",
+		ClientSecret: "YOUR_CLIENT_SECRET",
+		Scopes:       []string{"SCOPE1", "SCOPE2"},
+		Endpoint: oauth2.Endpoint{
+			AuthURL:  "https://provider.com/o/oauth2/auth",
+			TokenURL: "https://provider.com/o/oauth2/token",
+		},
+	}
+
+	// Redirect user to consent page to ask for permission
+	// for the scopes specified above.
+	url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
+	fmt.Printf("Visit the URL for the auth dialog: %v", url)
+
+	// Use the authorization code that is pushed to the redirect URL.
+	// NewTransportWithCode will do the handshake to retrieve
+	// an access token and initiate a Transport that is
+	// authorized and authenticated by the retrieved token.
+	var code string
+	if _, err := fmt.Scan(&code); err != nil {
+		log.Fatal(err)
+	}
+	tok, err := conf.Exchange(oauth2.NoContext, code)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	client := conf.Client(oauth2.NoContext, tok)
+	client.Get("...")
+}
+
+func ExampleJWTConfig() {
+	var initialToken *oauth2.Token // nil means no initial token
+	conf := &oauth2.JWTConfig{
+		Email: "xxx@developer.com",
+		// The contents of your RSA private key or your PEM file
+		// that contains a private key.
+		// If you have a p12 file instead, you
+		// can use `openssl` to export the private key into a pem file.
+		//
+		//    $ openssl pkcs12 -in key.p12 -out key.pem -nodes
+		//
+		// It only supports PEM containers with no passphrase.
+		PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."),
+		Subject:    "user@example.com",
+		TokenURL:   "https://provider.com/o/oauth2/token",
+	}
+	// Initiate an http.Client, the following GET request will be
+	// authorized and authenticated on the behalf of user@example.com.
+	client := conf.Client(oauth2.NoContext, initialToken)
+	client.Get("...")
+}

+ 16 - 0
Godeps/_workspace/src/golang.org/x/oauth2/github/github.go

@@ -0,0 +1,16 @@
+// Copyright 2014 The oauth2 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 github provides constants for using OAuth2 to access Github.
+package github // import "golang.org/x/oauth2/github"
+
+import (
+	"golang.org/x/oauth2"
+)
+
+// Endpoint is Github's OAuth 2.0 endpoint.
+var Endpoint = oauth2.Endpoint{
+	AuthURL:  "https://github.com/login/oauth/authorize",
+	TokenURL: "https://github.com/login/oauth/access_token",
+}

+ 37 - 0
Godeps/_workspace/src/golang.org/x/oauth2/google/appengine.go

@@ -0,0 +1,37 @@
+// Copyright 2014 The oauth2 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,!appenginevm
+
+package google
+
+import (
+	"time"
+
+	"appengine"
+
+	"golang.org/x/oauth2"
+)
+
+// AppEngineTokenSource returns a token source that fetches tokens
+// issued to the current App Engine application's service account.
+// If you are implementing a 3-legged OAuth 2.0 flow on App Engine
+// that involves user accounts, see oauth2.Config instead.
+//
+// You are required to provide a valid appengine.Context as context.
+func AppEngineTokenSource(ctx appengine.Context, scope ...string) oauth2.TokenSource {
+	return &appEngineTokenSource{
+		ctx:         ctx,
+		scopes:      scope,
+		fetcherFunc: aeFetcherFunc,
+	}
+}
+
+var aeFetcherFunc = func(ctx oauth2.Context, scope ...string) (string, time.Time, error) {
+	c, ok := ctx.(appengine.Context)
+	if !ok {
+		return "", time.Time{}, errInvalidContext
+	}
+	return appengine.AccessToken(c, scope...)
+}

+ 36 - 0
Godeps/_workspace/src/golang.org/x/oauth2/google/appenginevm.go

@@ -0,0 +1,36 @@
+// Copyright 2014 The oauth2 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 appenginevm !appengine
+
+package google
+
+import (
+	"time"
+
+	"golang.org/x/oauth2"
+	"google.golang.org/appengine"
+)
+
+// AppEngineTokenSource returns a token source that fetches tokens
+// issued to the current App Engine application's service account.
+// If you are implementing a 3-legged OAuth 2.0 flow on App Engine
+// that involves user accounts, see oauth2.Config instead.
+//
+// You are required to provide a valid appengine.Context as context.
+func AppEngineTokenSource(ctx appengine.Context, scope ...string) oauth2.TokenSource {
+	return &appEngineTokenSource{
+		ctx:         ctx,
+		scopes:      scope,
+		fetcherFunc: aeVMFetcherFunc,
+	}
+}
+
+var aeVMFetcherFunc = func(ctx oauth2.Context, scope ...string) (string, time.Time, error) {
+	c, ok := ctx.(appengine.Context)
+	if !ok {
+		return "", time.Time{}, errInvalidContext
+	}
+	return appengine.AccessToken(c, scope...)
+}

+ 132 - 0
Godeps/_workspace/src/golang.org/x/oauth2/google/example_test.go

@@ -0,0 +1,132 @@
+// Copyright 2014 The oauth2 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 appenginevm !appengine
+
+package google_test
+
+import (
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"testing"
+
+	"golang.org/x/oauth2"
+	"golang.org/x/oauth2/google"
+	"google.golang.org/appengine"
+	"google.golang.org/appengine/urlfetch"
+)
+
+// Remove after Go 1.4.
+// Related to https://codereview.appspot.com/107320046
+func TestA(t *testing.T) {}
+
+func Example_webServer() {
+	// Your credentials should be obtained from the Google
+	// Developer Console (https://console.developers.google.com).
+	conf := &oauth2.Config{
+		ClientID:     "YOUR_CLIENT_ID",
+		ClientSecret: "YOUR_CLIENT_SECRET",
+		RedirectURL:  "YOUR_REDIRECT_URL",
+		Scopes: []string{
+			"https://www.googleapis.com/auth/bigquery",
+			"https://www.googleapis.com/auth/blogger",
+		},
+		Endpoint: google.Endpoint,
+	}
+	// Redirect user to Google's consent page to ask for permission
+	// for the scopes specified above.
+	url := conf.AuthCodeURL("state")
+	fmt.Printf("Visit the URL for the auth dialog: %v", url)
+
+	// Handle the exchange code to initiate a transport.
+	tok, err := conf.Exchange(oauth2.NoContext, "authorization-code")
+	if err != nil {
+		log.Fatal(err)
+	}
+	client := conf.Client(oauth2.NoContext, tok)
+	client.Get("...")
+}
+
+func ExampleJWTConfigFromJSON() {
+	// Your credentials should be obtained from the Google
+	// Developer Console (https://console.developers.google.com).
+	// Navigate to your project, then see the "Credentials" page
+	// under "APIs & Auth".
+	// To create a service account client, click "Create new Client ID",
+	// select "Service Account", and click "Create Client ID". A JSON
+	// key file will then be downloaded to your computer.
+	data, err := ioutil.ReadFile("/path/to/your-project-key.json")
+	if err != nil {
+		log.Fatal(err)
+	}
+	conf, err := google.JWTConfigFromJSON(oauth2.NoContext, data, "https://www.googleapis.com/auth/bigquery")
+	if err != nil {
+		log.Fatal(err)
+	}
+	// Initiate an http.Client. The following GET request will be
+	// authorized and authenticated on the behalf of
+	// your service account.
+	client := conf.Client(oauth2.NoContext, nil)
+	client.Get("...")
+}
+
+func Example_serviceAccount() {
+	// Your credentials should be obtained from the Google
+	// Developer Console (https://console.developers.google.com).
+	conf := &oauth2.JWTConfig{
+		Email: "xxx@developer.gserviceaccount.com",
+		// The contents of your RSA private key or your PEM file
+		// that contains a private key.
+		// If you have a p12 file instead, you
+		// can use `openssl` to export the private key into a pem file.
+		//
+		//    $ openssl pkcs12 -in key.p12 -passin pass:notasecret -out key.pem -nodes
+		//
+		// The field only supports PEM containers with no passphrase.
+		// The openssl command will convert p12 keys to passphrase-less PEM containers.
+		PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."),
+		Scopes: []string{
+			"https://www.googleapis.com/auth/bigquery",
+			"https://www.googleapis.com/auth/blogger",
+		},
+		TokenURL: google.JWTTokenURL,
+		// If you would like to impersonate a user, you can
+		// create a transport with a subject. The following GET
+		// request will be made on the behalf of user@example.com.
+		// Optional.
+		Subject: "user@example.com",
+	}
+	// Initiate an http.Client, the following GET request will be
+	// authorized and authenticated on the behalf of user@example.com.
+	client := conf.Client(oauth2.NoContext, nil)
+	client.Get("...")
+}
+
+func ExampleAppEngineTokenSource() {
+	var req *http.Request // from the ServeHTTP handler
+	ctx := appengine.NewContext(req)
+	client := &http.Client{
+		Transport: &oauth2.Transport{
+			Source: google.AppEngineTokenSource(ctx, "https://www.googleapis.com/auth/bigquery"),
+			Base: &urlfetch.Transport{
+				Context: ctx,
+			},
+		},
+	}
+	client.Get("...")
+}
+
+func ExampleComputeTokenSource() {
+	client := &http.Client{
+		Transport: &oauth2.Transport{
+			// Fetch from Google Compute Engine's metadata server to retrieve
+			// an access token for the provided account.
+			// If no account is specified, "default" is used.
+			Source: google.ComputeTokenSource(""),
+		},
+	}
+	client.Get("...")
+}

+ 115 - 0
Godeps/_workspace/src/golang.org/x/oauth2/google/google.go

@@ -0,0 +1,115 @@
+// Copyright 2014 The oauth2 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 google provides support for making
+// OAuth2 authorized and authenticated HTTP requests
+// to Google APIs. It supports Web server, client-side,
+// service accounts, Google Compute Engine service accounts,
+// and Google App Engine service accounts authorization
+// and authentications flows:
+//
+// For more information, please read
+// https://developers.google.com/accounts/docs/OAuth2.
+package google // import "golang.org/x/oauth2/google"
+
+import (
+	"encoding/json"
+
+	"fmt"
+	"net"
+	"net/http"
+	"time"
+
+	"golang.org/x/oauth2"
+)
+
+// Endpoint is Google's OAuth 2.0 endpoint.
+var Endpoint = oauth2.Endpoint{
+	AuthURL:  "https://accounts.google.com/o/oauth2/auth",
+	TokenURL: "https://accounts.google.com/o/oauth2/token",
+}
+
+// JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow.
+const JWTTokenURL = "https://accounts.google.com/o/oauth2/token"
+
+// JWTConfigFromJSON uses a Google Developers service account JSON key file to read
+// the credentials that authorize and authenticate the requests.
+// Create a service account on "Credentials" page under "APIs & Auth" for your
+// project at https://console.developers.google.com to download a JSON key file.
+func JWTConfigFromJSON(ctx oauth2.Context, jsonKey []byte, scope ...string) (*oauth2.JWTConfig, error) {
+	var key struct {
+		Email      string `json:"client_email"`
+		PrivateKey string `json:"private_key"`
+	}
+	if err := json.Unmarshal(jsonKey, &key); err != nil {
+		return nil, err
+	}
+	return &oauth2.JWTConfig{
+		Email:      key.Email,
+		PrivateKey: []byte(key.PrivateKey),
+		Scopes:     scope,
+		TokenURL:   JWTTokenURL,
+	}, nil
+}
+
+type metaTokenRespBody struct {
+	AccessToken string        `json:"access_token"`
+	ExpiresIn   time.Duration `json:"expires_in"`
+	TokenType   string        `json:"token_type"`
+}
+
+// ComputeTokenSource returns a token source that fetches access tokens
+// from Google Compute Engine (GCE)'s metadata server. It's only valid to use
+// this token source if your program is running on a GCE instance.
+// If no account is specified, "default" is used.
+// Further information about retrieving access tokens from the GCE metadata
+// server can be found at https://cloud.google.com/compute/docs/authentication.
+func ComputeTokenSource(account string) oauth2.TokenSource {
+	return &computeSource{account: account}
+}
+
+type computeSource struct {
+	account string
+}
+
+var metaClient = &http.Client{
+	Transport: &http.Transport{
+		Dial: (&net.Dialer{
+			Timeout:   750 * time.Millisecond,
+			KeepAlive: 30 * time.Second,
+		}).Dial,
+		ResponseHeaderTimeout: 750 * time.Millisecond,
+	},
+}
+
+func (cs *computeSource) Token() (*oauth2.Token, error) {
+	acct := cs.account
+	if acct == "" {
+		acct = "default"
+	}
+	u := "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/" + acct + "/token"
+	req, err := http.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, err
+	}
+	req.Header.Add("X-Google-Metadata-Request", "True")
+	resp, err := metaClient.Do(req)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	if resp.StatusCode < 200 || resp.StatusCode > 299 {
+		return nil, fmt.Errorf("oauth2: can't retrieve a token from metadata server, status code: %d", resp.StatusCode)
+	}
+	var tokenResp metaTokenRespBody
+	err = json.NewDecoder(resp.Body).Decode(&tokenResp)
+	if err != nil {
+		return nil, err
+	}
+	return &oauth2.Token{
+		AccessToken: tokenResp.AccessToken,
+		TokenType:   tokenResp.TokenType,
+		Expiry:      time.Now().Add(tokenResp.ExpiresIn * time.Second),
+	}, nil
+}

+ 68 - 0
Godeps/_workspace/src/golang.org/x/oauth2/google/source_appengine.go

@@ -0,0 +1,68 @@
+// Copyright 2014 The oauth2 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 google
+
+import (
+	"errors"
+	"sort"
+	"strings"
+	"sync"
+	"time"
+
+	"golang.org/x/oauth2"
+)
+
+var (
+	aeTokensMu sync.Mutex // guards aeTokens and appEngineTokenSource.key
+
+	// aeTokens helps the fetched tokens to be reused until their expiration.
+	aeTokens = make(map[string]*tokenLock) // key is '\0'-separated scopes
+)
+
+var errInvalidContext = errors.New("oauth2: a valid appengine.Context is required")
+
+type tokenLock struct {
+	mu sync.Mutex // guards t; held while updating t
+	t  *oauth2.Token
+}
+
+type appEngineTokenSource struct {
+	ctx    oauth2.Context
+	scopes []string
+	key    string // guarded by package-level mutex, aeTokensMu
+
+	// fetcherFunc makes the actual RPC to fetch a new access token with an expiry time.
+	// Provider of this function is responsible to assert that the given context is valid.
+	fetcherFunc func(ctx oauth2.Context, scope ...string) (string, time.Time, error)
+}
+
+func (ts *appEngineTokenSource) Token() (*oauth2.Token, error) {
+	aeTokensMu.Lock()
+	if ts.key == "" {
+		sort.Sort(sort.StringSlice(ts.scopes))
+		ts.key = strings.Join(ts.scopes, string(0))
+	}
+	tok, ok := aeTokens[ts.key]
+	if !ok {
+		tok = &tokenLock{}
+		aeTokens[ts.key] = tok
+	}
+	aeTokensMu.Unlock()
+
+	tok.mu.Lock()
+	defer tok.mu.Unlock()
+	if tok.t != nil && !tok.t.Expired() {
+		return tok.t, nil
+	}
+	access, exp, err := ts.fetcherFunc(ts.ctx, ts.scopes...)
+	if err != nil {
+		return nil, err
+	}
+	tok.t = &oauth2.Token{
+		AccessToken: access,
+		Expiry:      exp,
+	}
+	return tok.t, nil
+}

+ 37 - 0
Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2.go

@@ -0,0 +1,37 @@
+// Copyright 2014 The oauth2 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
+
+import (
+	"crypto/rsa"
+	"crypto/x509"
+	"encoding/pem"
+	"errors"
+)
+
+// 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, err
+		}
+	}
+	parsed, ok := parsedKey.(*rsa.PrivateKey)
+	if !ok {
+		return nil, errors.New("oauth2: private key is invalid")
+	}
+	return parsed, nil
+}

+ 160 - 0
Godeps/_workspace/src/golang.org/x/oauth2/jws/jws.go

@@ -0,0 +1,160 @@
+// Copyright 2014 The oauth2 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 jws provides encoding and decoding utilities for
+// signed JWS messages.
+package jws // import "golang.org/x/oauth2/jws"
+
+import (
+	"bytes"
+	"crypto"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/sha256"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"strings"
+	"time"
+)
+
+// ClaimSet contains information about the JWT signature including the
+// permissions being requested (scopes), the target of the token, the issuer,
+// the time the token was issued, and the lifetime of the token.
+type ClaimSet struct {
+	Iss   string `json:"iss"`             // email address of the client_id of the application making the access token request
+	Scope string `json:"scope,omitempty"` // space-delimited list of the permissions the application requests
+	Aud   string `json:"aud"`             // descriptor of the intended target of the assertion (Optional).
+	Exp   int64  `json:"exp"`             // the expiration time of the assertion
+	Iat   int64  `json:"iat"`             // the time the assertion was issued.
+	Typ   string `json:"typ,omitempty"`   // token type (Optional).
+
+	// Email for which the application is requesting delegated access (Optional).
+	Sub string `json:"sub,omitempty"`
+
+	// The old name of Sub. Client keeps setting Prn to be
+	// complaint with legacy OAuth 2.0 providers. (Optional)
+	Prn string `json:"prn,omitempty"`
+
+	// See http://tools.ietf.org/html/draft-jones-json-web-token-10#section-4.3
+	// This array is marshalled using custom code (see (c *ClaimSet) encode()).
+	PrivateClaims map[string]interface{} `json:"-"`
+
+	exp time.Time
+	iat time.Time
+}
+
+func (c *ClaimSet) encode() (string, error) {
+	if c.exp.IsZero() || c.iat.IsZero() {
+		// Reverting time back for machines whose time is not perfectly in sync.
+		// If client machine's time is in the future according
+		// to Google servers, an access token will not be issued.
+		now := time.Now().Add(-10 * time.Second)
+		c.iat = now
+		c.exp = now.Add(time.Hour)
+	}
+
+	c.Exp = c.exp.Unix()
+	c.Iat = c.iat.Unix()
+
+	b, err := json.Marshal(c)
+	if err != nil {
+		return "", err
+	}
+
+	if len(c.PrivateClaims) == 0 {
+		return base64Encode(b), nil
+	}
+
+	// Marshal private claim set and then append it to b.
+	prv, err := json.Marshal(c.PrivateClaims)
+	if err != nil {
+		return "", fmt.Errorf("jws: invalid map of private claims %v", c.PrivateClaims)
+	}
+
+	// Concatenate public and private claim JSON objects.
+	if !bytes.HasSuffix(b, []byte{'}'}) {
+		return "", fmt.Errorf("jws: invalid JSON %s", b)
+	}
+	if !bytes.HasPrefix(prv, []byte{'{'}) {
+		return "", fmt.Errorf("jws: invalid JSON %s", prv)
+	}
+	b[len(b)-1] = ','         // Replace closing curly brace with a comma.
+	b = append(b, prv[1:]...) // Append private claims.
+	return base64Encode(b), nil
+}
+
+// Header represents the header for the signed JWS payloads.
+type Header struct {
+	// The algorithm used for signature.
+	Algorithm string `json:"alg"`
+
+	// Represents the token type.
+	Typ string `json:"typ"`
+}
+
+func (h *Header) encode() (string, error) {
+	b, err := json.Marshal(h)
+	if err != nil {
+		return "", err
+	}
+	return base64Encode(b), nil
+}
+
+// Decode decodes a claim set from a JWS payload.
+func Decode(payload string) (*ClaimSet, error) {
+	// decode returned id token to get expiry
+	s := strings.Split(payload, ".")
+	if len(s) < 2 {
+		// TODO(jbd): Provide more context about the error.
+		return nil, errors.New("jws: invalid token received")
+	}
+	decoded, err := base64Decode(s[1])
+	if err != nil {
+		return nil, err
+	}
+	c := &ClaimSet{}
+	err = json.NewDecoder(bytes.NewBuffer(decoded)).Decode(c)
+	return c, err
+}
+
+// Encode encodes a signed JWS with provided header and claim set.
+func Encode(header *Header, c *ClaimSet, signature *rsa.PrivateKey) (string, error) {
+	head, err := header.encode()
+	if err != nil {
+		return "", err
+	}
+	cs, err := c.encode()
+	if err != nil {
+		return "", err
+	}
+	ss := fmt.Sprintf("%s.%s", head, cs)
+	h := sha256.New()
+	h.Write([]byte(ss))
+	b, err := rsa.SignPKCS1v15(rand.Reader, signature, crypto.SHA256, h.Sum(nil))
+	if err != nil {
+		return "", err
+	}
+	sig := base64Encode(b)
+	return fmt.Sprintf("%s.%s", ss, sig), nil
+}
+
+// base64Encode returns and Base64url encoded version of the input string with any
+// trailing "=" stripped.
+func base64Encode(b []byte) string {
+	return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=")
+}
+
+// base64Decode decodes the Base64url encoded string
+func base64Decode(s string) ([]byte, error) {
+	// add back missing padding
+	switch len(s) % 4 {
+	case 2:
+		s += "=="
+	case 3:
+		s += "="
+	}
+	return base64.URLEncoding.DecodeString(s)
+}

+ 155 - 0
Godeps/_workspace/src/golang.org/x/oauth2/jwt.go

@@ -0,0 +1,155 @@
+// Copyright 2014 The oauth2 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 (
+	"encoding/json"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"strings"
+	"time"
+
+	"golang.org/x/oauth2/internal"
+	"golang.org/x/oauth2/jws"
+)
+
+var (
+	defaultGrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer"
+	defaultHeader    = &jws.Header{Algorithm: "RS256", Typ: "JWT"}
+)
+
+// JWTConfig is the configuration for using JWT to fetch tokens,
+// commonly known as "two-legged OAuth".
+type JWTConfig struct {
+	// Email is the OAuth client identifier used when communicating with
+	// the configured OAuth provider.
+	Email string
+
+	// PrivateKey contains the contents of an RSA private key or the
+	// contents of a PEM file that contains a private key. The provided
+	// private key is used to sign JWT payloads.
+	// PEM containers with a passphrase are not supported.
+	// Use the following command to convert a PKCS 12 file into a PEM.
+	//
+	//    $ openssl pkcs12 -in key.p12 -out key.pem -nodes
+	//
+	PrivateKey []byte
+
+	// Subject is the optional user to impersonate.
+	Subject string
+
+	// Scopes optionally specifies a list of requested permission scopes.
+	Scopes []string
+
+	// TokenURL is the endpoint required to complete the 2-legged JWT flow.
+	TokenURL string
+}
+
+// TokenSource returns a JWT TokenSource using the configuration
+// in c and the HTTP client from the provided context.
+//
+// The returned TokenSource only does JWT requests when necessary but
+// otherwise returns the same token repeatedly until it expires.
+//
+// The provided initialToken may be nil, in which case the first
+// call to TokenSource will do a new JWT request.
+func (c *JWTConfig) TokenSource(ctx Context, initialToken *Token) TokenSource {
+	return &newWhenNeededSource{
+		t:   initialToken,
+		new: jwtSource{ctx, c},
+	}
+}
+
+// Client returns an HTTP client wrapping the context's
+// HTTP transport and adding Authorization headers with tokens
+// obtained from c.
+//
+// The provided initialToken may be nil, in which case the first
+// call to TokenSource will do a new JWT request.
+//
+// The returned client and its Transport should not be modified.
+func (c *JWTConfig) Client(ctx Context, initialToken *Token) *http.Client {
+	return NewClient(ctx, c.TokenSource(ctx, initialToken))
+}
+
+// jwtSource is a source that always does a signed JWT request for a token.
+// It should typically be wrapped with a newWhenNeededSource.
+type jwtSource struct {
+	ctx  Context
+	conf *JWTConfig
+}
+
+func (js jwtSource) Token() (*Token, error) {
+	pk, err := internal.ParseKey(js.conf.PrivateKey)
+	if err != nil {
+		return nil, err
+	}
+	hc, err := contextClient(js.ctx)
+	if err != nil {
+		return nil, err
+	}
+	claimSet := &jws.ClaimSet{
+		Iss:   js.conf.Email,
+		Scope: strings.Join(js.conf.Scopes, " "),
+		Aud:   js.conf.TokenURL,
+	}
+	if subject := js.conf.Subject; subject != "" {
+		claimSet.Sub = subject
+		// prn is the old name of sub. Keep setting it
+		// to be compatible with legacy OAuth 2.0 providers.
+		claimSet.Prn = subject
+	}
+	payload, err := jws.Encode(defaultHeader, claimSet, pk)
+	if err != nil {
+		return nil, err
+	}
+	v := url.Values{}
+	v.Set("grant_type", defaultGrantType)
+	v.Set("assertion", payload)
+	resp, err := hc.PostForm(js.conf.TokenURL, v)
+	if err != nil {
+		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20))
+	if err != nil {
+		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
+	}
+	if c := resp.StatusCode; c < 200 || c > 299 {
+		return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", resp.Status, body)
+	}
+	// tokenRes is the JSON response body.
+	var tokenRes struct {
+		AccessToken string `json:"access_token"`
+		TokenType   string `json:"token_type"`
+		IDToken     string `json:"id_token"`
+		ExpiresIn   int64  `json:"expires_in"` // relative seconds from now
+	}
+	if err := json.Unmarshal(body, &tokenRes); err != nil {
+		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
+	}
+	token := &Token{
+		AccessToken: tokenRes.AccessToken,
+		TokenType:   tokenRes.TokenType,
+		raw:         make(map[string]interface{}),
+	}
+	json.Unmarshal(body, &token.raw) // no error checks for optional fields
+
+	if secs := tokenRes.ExpiresIn; secs > 0 {
+		token.Expiry = time.Now().Add(time.Duration(secs) * time.Second)
+	}
+	if v := tokenRes.IDToken; v != "" {
+		// decode returned id token to get expiry
+		claimSet, err := jws.Decode(v)
+		if err != nil {
+			return nil, fmt.Errorf("oauth2: error decoding JWT token: %v", err)
+		}
+		token.Expiry = time.Unix(claimSet.Exp, 0)
+	}
+	return token, nil
+}

+ 126 - 0
Godeps/_workspace/src/golang.org/x/oauth2/jwt_test.go

@@ -0,0 +1,126 @@
+// Copyright 2014 The oauth2 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 (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+)
+
+var dummyPrivateKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAx4fm7dngEmOULNmAs1IGZ9Apfzh+BkaQ1dzkmbUgpcoghucE
+DZRnAGd2aPyB6skGMXUytWQvNYav0WTR00wFtX1ohWTfv68HGXJ8QXCpyoSKSSFY
+fuP9X36wBSkSX9J5DVgiuzD5VBdzUISSmapjKm+DcbRALjz6OUIPEWi1Tjl6p5RK
+1w41qdbmt7E5/kGhKLDuT7+M83g4VWhgIvaAXtnhklDAggilPPa8ZJ1IFe31lNlr
+k4DRk38nc6sEutdf3RL7QoH7FBusI7uXV03DC6dwN1kP4GE7bjJhcRb/7jYt7CQ9
+/E9Exz3c0yAp0yrTg0Fwh+qxfH9dKwN52S7SBwIDAQABAoIBAQCaCs26K07WY5Jt
+3a2Cw3y2gPrIgTCqX6hJs7O5ByEhXZ8nBwsWANBUe4vrGaajQHdLj5OKfsIDrOvn
+2NI1MqflqeAbu/kR32q3tq8/Rl+PPiwUsW3E6Pcf1orGMSNCXxeducF2iySySzh3
+nSIhCG5uwJDWI7a4+9KiieFgK1pt/Iv30q1SQS8IEntTfXYwANQrfKUVMmVF9aIK
+6/WZE2yd5+q3wVVIJ6jsmTzoDCX6QQkkJICIYwCkglmVy5AeTckOVwcXL0jqw5Kf
+5/soZJQwLEyBoQq7Kbpa26QHq+CJONetPP8Ssy8MJJXBT+u/bSseMb3Zsr5cr43e
+DJOhwsThAoGBAPY6rPKl2NT/K7XfRCGm1sbWjUQyDShscwuWJ5+kD0yudnT/ZEJ1
+M3+KS/iOOAoHDdEDi9crRvMl0UfNa8MAcDKHflzxg2jg/QI+fTBjPP5GOX0lkZ9g
+z6VePoVoQw2gpPFVNPPTxKfk27tEzbaffvOLGBEih0Kb7HTINkW8rIlzAoGBAM9y
+1yr+jvfS1cGFtNU+Gotoihw2eMKtIqR03Yn3n0PK1nVCDKqwdUqCypz4+ml6cxRK
+J8+Pfdh7D+ZJd4LEG6Y4QRDLuv5OA700tUoSHxMSNn3q9As4+T3MUyYxWKvTeu3U
+f2NWP9ePU0lV8ttk7YlpVRaPQmc1qwooBA/z/8AdAoGAW9x0HWqmRICWTBnpjyxx
+QGlW9rQ9mHEtUotIaRSJ6K/F3cxSGUEkX1a3FRnp6kPLcckC6NlqdNgNBd6rb2rA
+cPl/uSkZP42Als+9YMoFPU/xrrDPbUhu72EDrj3Bllnyb168jKLa4VBOccUvggxr
+Dm08I1hgYgdN5huzs7y6GeUCgYEAj+AZJSOJ6o1aXS6rfV3mMRve9bQ9yt8jcKXw
+5HhOCEmMtaSKfnOF1Ziih34Sxsb7O2428DiX0mV/YHtBnPsAJidL0SdLWIapBzeg
+KHArByIRkwE6IvJvwpGMdaex1PIGhx5i/3VZL9qiq/ElT05PhIb+UXgoWMabCp84
+OgxDK20CgYAeaFo8BdQ7FmVX2+EEejF+8xSge6WVLtkaon8bqcn6P0O8lLypoOhd
+mJAYH8WU+UAy9pecUnDZj14LAGNVmYcse8HFX71MoshnvCTFEPVo4rZxIAGwMpeJ
+5jgQ3slYLpqrGlcbLgUXBUgzEO684Wk/UV9DFPlHALVqCfXQ9dpJPg==
+-----END RSA PRIVATE KEY-----`)
+
+func TestJWTFetch_JSONResponse(t *testing.T) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "application/json")
+		w.Write([]byte(`{
+			"access_token": "90d64460d14870c08c81352a05dedd3465940a7c",
+			"scope": "user",
+			"token_type": "bearer",
+			"expires_in": 3600
+		}`))
+	}))
+	defer ts.Close()
+
+	conf := &JWTConfig{
+		Email:      "aaa@xxx.com",
+		PrivateKey: dummyPrivateKey,
+		TokenURL:   ts.URL,
+	}
+	tok, err := conf.TokenSource(NoContext, nil).Token()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if tok.Expired() {
+		t.Errorf("Token shouldn't be expired")
+	}
+	if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
+		t.Errorf("Unexpected access token, %#v", tok.AccessToken)
+	}
+	if tok.TokenType != "bearer" {
+		t.Errorf("Unexpected token type, %#v", tok.TokenType)
+	}
+	if tok.Expiry.IsZero() {
+		t.Errorf("Unexpected token expiry, %#v", tok.Expiry)
+	}
+	scope := tok.Extra("scope")
+	if scope != "user" {
+		t.Errorf("Unexpected value for scope: %v", scope)
+	}
+}
+
+func TestJWTFetch_BadResponse(t *testing.T) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "application/json")
+		w.Write([]byte(`{"scope": "user", "token_type": "bearer"}`))
+	}))
+	defer ts.Close()
+
+	conf := &JWTConfig{
+		Email:      "aaa@xxx.com",
+		PrivateKey: dummyPrivateKey,
+		TokenURL:   ts.URL,
+	}
+	tok, err := conf.TokenSource(NoContext, nil).Token()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if tok.AccessToken != "" {
+		t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
+	}
+	if tok.TokenType != "bearer" {
+		t.Errorf("Unexpected token type, %#v.", tok.TokenType)
+	}
+	scope := tok.Extra("scope")
+	if scope != "user" {
+		t.Errorf("Unexpected value for scope: %v", scope)
+	}
+}
+
+func TestJWTFetch_BadResponseType(t *testing.T) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "application/json")
+		w.Write([]byte(`{"access_token":123, "scope": "user", "token_type": "bearer"}`))
+	}))
+	defer ts.Close()
+	conf := &JWTConfig{
+		Email:      "aaa@xxx.com",
+		PrivateKey: dummyPrivateKey,
+		TokenURL:   ts.URL,
+	}
+	tok, err := conf.TokenSource(NoContext, nil).Token()
+	if err == nil {
+		t.Error("got a token; expected error")
+		if tok.AccessToken != "" {
+			t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
+		}
+	}
+}

+ 421 - 0
Godeps/_workspace/src/golang.org/x/oauth2/oauth2.go

@@ -0,0 +1,421 @@
+// Copyright 2014 The oauth2 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.
+// It can additionally grant authorization with Bearer JWT.
+package oauth2 // import "golang.org/x/oauth2"
+
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"mime"
+	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+
+	"golang.org/x/net/context"
+)
+
+// Context can be an golang.org/x/net.Context, or an App Engine Context.
+// In the future these will be unified.
+// If you don't care and aren't running on App Engine, you may use NoContext.
+type Context interface{}
+
+// NoContext is the default context. If you're not running this code
+// on App Engine or not using golang.org/x/net.Context to provide a custom
+// HTTP client, you should use NoContext.
+var NoContext Context = nil
+
+// Config describes a typical 3-legged OAuth2 flow, with both the
+// client application information and the server's URLs.
+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 supplied by the server and are often
+	// available via site-specific packages (for example,
+	// 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() (*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 (the default if neither is specified) is the default.
+	// 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 = setParam{"access_type", "online"}
+	AccessTypeOffline AuthCodeOption = setParam{"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 = setParam{"approval_prompt", "force"}
+)
+
+type setParam struct{ k, v string }
+
+func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) }
+
+// An AuthCodeOption is passed to Config.AuthCodeURL.
+type AuthCodeOption interface {
+	setValue(url.Values)
+}
+
+// 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-zero 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.
+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},
+		"redirect_uri":  condVal(c.RedirectURL),
+		"scope":         condVal(strings.Join(c.Scopes, " ")),
+		"state":         condVal(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()
+}
+
+// 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 nil,
+// http.DefaultClient is used. See the Context type's documentation.
+//
+// The code will be in the *http.Request.FormValue("code"). Before
+// calling Exchange, be sure to validate FormValue("state").
+func (c *Config) Exchange(ctx Context, code string) (*Token, error) {
+	return retrieveToken(ctx, c, url.Values{
+		"grant_type":   {"authorization_code"},
+		"code":         {code},
+		"redirect_uri": condVal(c.RedirectURL),
+		"scope":        condVal(strings.Join(c.Scopes, " ")),
+	})
+}
+
+// contextClientFunc is a func which tries to return an *http.Client
+// given a Context value. If it returns an error, the search stops
+// with that error.  If it returns (nil, nil), the search continues
+// down the list of registered funcs.
+type contextClientFunc func(Context) (*http.Client, error)
+
+var contextClientFuncs []contextClientFunc
+
+func registerContextClientFunc(fn contextClientFunc) {
+	contextClientFuncs = append(contextClientFuncs, fn)
+}
+
+func contextClient(ctx Context) (*http.Client, error) {
+	for _, fn := range contextClientFuncs {
+		c, err := fn(ctx)
+		if err != nil {
+			return nil, err
+		}
+		if c != nil {
+			return c, nil
+		}
+	}
+	if xc, ok := ctx.(context.Context); ok {
+		if hc, ok := xc.Value(HTTPClient).(*http.Client); ok {
+			return hc, nil
+		}
+	}
+	return http.DefaultClient, nil
+}
+
+func contextTransport(ctx Context) http.RoundTripper {
+	hc, err := contextClient(ctx)
+	if err != nil {
+		// This is a rare error case (somebody using nil on App Engine),
+		// so I'd rather not everybody do an error check on this Client
+		// method. They can get the error that they're doing it wrong
+		// later, at client.Get/PostForm time.
+		return errorTransport{err}
+	}
+	return hc.Transport
+}
+
+// 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, 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.
+// See the the Context documentation.
+//
+// Most users will use Config.Client instead.
+func (c *Config) TokenSource(ctx Context, t *Token) TokenSource {
+	nwn := &newWhenNeededSource{t: t}
+	nwn.new = tokenRefresher{
+		ctx:      ctx,
+		conf:     c,
+		oldToken: &nwn.t,
+	}
+	return nwn
+}
+
+// tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token"
+// HTTP requests to renew a token using a RefreshToken.
+type tokenRefresher struct {
+	ctx      Context // used to get HTTP requests
+	conf     *Config
+	oldToken **Token // pointer to old *Token w/ RefreshToken
+}
+
+func (tf tokenRefresher) Token() (*Token, error) {
+	t := *tf.oldToken
+	if t == nil {
+		return nil, errors.New("oauth2: attempted use of nil Token")
+	}
+	if t.RefreshToken == "" {
+		return nil, errors.New("oauth2: token expired and refresh token is not set")
+	}
+	return retrieveToken(tf.ctx, tf.conf, url.Values{
+		"grant_type":    {"refresh_token"},
+		"refresh_token": {t.RefreshToken},
+	})
+}
+
+// newWhenNeededSource 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.
+//
+// The first call to TokenRefresher must be SetToken.
+type newWhenNeededSource 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 *newWhenNeededSource) Token() (*Token, error) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	if s.t != nil && !s.t.Expired() {
+		return s.t, nil
+	}
+	t, err := s.new.Token()
+	if err != nil {
+		return nil, err
+	}
+	s.t = t
+	return t, nil
+}
+
+func retrieveToken(ctx Context, c *Config, v url.Values) (*Token, error) {
+	hc, err := contextClient(ctx)
+	if err != nil {
+		return nil, err
+	}
+	v.Set("client_id", c.ClientID)
+	bustedAuth := !providerAuthHeaderWorks(c.Endpoint.TokenURL)
+	if bustedAuth && c.ClientSecret != "" {
+		v.Set("client_secret", c.ClientSecret)
+	}
+	req, err := http.NewRequest("POST", c.Endpoint.TokenURL, strings.NewReader(v.Encode()))
+	if err != nil {
+		return nil, err
+	}
+	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+	if !bustedAuth && c.ClientSecret != "" {
+		req.SetBasicAuth(c.ClientID, c.ClientSecret)
+	}
+	r, err := hc.Do(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, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, 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")
+	}
+	return token, nil
+}
+
+// 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    int32  `json:"expires_in"`
+	Expires      int32  `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
+}
+
+func condVal(v string) []string {
+	if v == "" {
+		return nil
+	}
+	return []string{v}
+}
+
+// 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
+func providerAuthHeaderWorks(tokenURL string) bool {
+	if strings.HasPrefix(tokenURL, "https://accounts.google.com/") ||
+		strings.HasPrefix(tokenURL, "https://github.com/") ||
+		strings.HasPrefix(tokenURL, "https://api.instagram.com/") ||
+		strings.HasPrefix(tokenURL, "https://www.douban.com/") ||
+		strings.HasPrefix(tokenURL, "https://api.dropbox.com/") ||
+		strings.HasPrefix(tokenURL, "https://api.soundcloud.com/") ||
+		strings.HasPrefix(tokenURL, "https://www.linkedin.com/") {
+		// Some sites fail to implement the OAuth2 spec fully.
+		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
+}
+
+// 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{}
+
+// NewClient creates an *http.Client from a Context and TokenSource.
+// The client's lifetime does not extend beyond the lifetime of the context.
+func NewClient(ctx Context, src TokenSource) *http.Client {
+	return &http.Client{
+		Transport: &Transport{
+			Base:   contextTransport(ctx),
+			Source: src,
+		},
+	}
+}

+ 260 - 0
Godeps/_workspace/src/golang.org/x/oauth2/oauth2_test.go

@@ -0,0 +1,260 @@
+// Copyright 2014 The oauth2 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/ioutil"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"golang.org/x/net/context"
+)
+
+type mockTransport struct {
+	rt func(req *http.Request) (resp *http.Response, err error)
+}
+
+func (t *mockTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
+	return t.rt(req)
+}
+
+type mockCache struct {
+	token   *Token
+	readErr error
+}
+
+func (c *mockCache) ReadToken() (*Token, error) {
+	return c.token, c.readErr
+}
+
+func (c *mockCache) WriteToken(*Token) {
+	// do nothing
+}
+
+func newConf(url string) *Config {
+	return &Config{
+		ClientID:     "CLIENT_ID",
+		ClientSecret: "CLIENT_SECRET",
+		RedirectURL:  "REDIRECT_URL",
+		Scopes:       []string{"scope1", "scope2"},
+		Endpoint: Endpoint{
+			AuthURL:  url + "/auth",
+			TokenURL: url + "/token",
+		},
+	}
+}
+
+func TestAuthCodeURL(t *testing.T) {
+	conf := newConf("server")
+	url := conf.AuthCodeURL("foo", AccessTypeOffline, ApprovalForce)
+	if url != "server/auth?access_type=offline&approval_prompt=force&client_id=CLIENT_ID&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=foo" {
+		t.Errorf("Auth code URL doesn't match the expected, found: %v", url)
+	}
+}
+
+func TestAuthCodeURL_Optional(t *testing.T) {
+	conf := &Config{
+		ClientID: "CLIENT_ID",
+		Endpoint: Endpoint{
+			AuthURL:  "/auth-url",
+			TokenURL: "/token-url",
+		},
+	}
+	url := conf.AuthCodeURL("")
+	if url != "/auth-url?client_id=CLIENT_ID&response_type=code" {
+		t.Fatalf("Auth code URL doesn't match the expected, found: %v", url)
+	}
+}
+
+func TestExchangeRequest(t *testing.T) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		if r.URL.String() != "/token" {
+			t.Errorf("Unexpected exchange request URL, %v is found.", r.URL)
+		}
+		headerAuth := r.Header.Get("Authorization")
+		if headerAuth != "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" {
+			t.Errorf("Unexpected authorization header, %v is found.", headerAuth)
+		}
+		headerContentType := r.Header.Get("Content-Type")
+		if headerContentType != "application/x-www-form-urlencoded" {
+			t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
+		}
+		body, err := ioutil.ReadAll(r.Body)
+		if err != nil {
+			t.Errorf("Failed reading request body: %s.", err)
+		}
+		if string(body) != "client_id=CLIENT_ID&code=exchange-code&grant_type=authorization_code&redirect_uri=REDIRECT_URL&scope=scope1+scope2" {
+			t.Errorf("Unexpected exchange payload, %v is found.", string(body))
+		}
+		w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
+		w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&scope=user&token_type=bearer"))
+	}))
+	defer ts.Close()
+	conf := newConf(ts.URL)
+	tok, err := conf.Exchange(NoContext, "exchange-code")
+	if err != nil {
+		t.Error(err)
+	}
+	if tok.Expired() {
+		t.Errorf("Token shouldn't be expired.")
+	}
+	if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
+		t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
+	}
+	if tok.TokenType != "bearer" {
+		t.Errorf("Unexpected token type, %#v.", tok.TokenType)
+	}
+	scope := tok.Extra("scope")
+	if scope != "user" {
+		t.Errorf("Unexpected value for scope: %v", scope)
+	}
+}
+
+func TestExchangeRequest_JSONResponse(t *testing.T) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		if r.URL.String() != "/token" {
+			t.Errorf("Unexpected exchange request URL, %v is found.", r.URL)
+		}
+		headerAuth := r.Header.Get("Authorization")
+		if headerAuth != "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" {
+			t.Errorf("Unexpected authorization header, %v is found.", headerAuth)
+		}
+		headerContentType := r.Header.Get("Content-Type")
+		if headerContentType != "application/x-www-form-urlencoded" {
+			t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
+		}
+		body, err := ioutil.ReadAll(r.Body)
+		if err != nil {
+			t.Errorf("Failed reading request body: %s.", err)
+		}
+		if string(body) != "client_id=CLIENT_ID&code=exchange-code&grant_type=authorization_code&redirect_uri=REDIRECT_URL&scope=scope1+scope2" {
+			t.Errorf("Unexpected exchange payload, %v is found.", string(body))
+		}
+		w.Header().Set("Content-Type", "application/json")
+		w.Write([]byte(`{"access_token": "90d64460d14870c08c81352a05dedd3465940a7c", "scope": "user", "token_type": "bearer", "expires_in": 86400}`))
+	}))
+	defer ts.Close()
+	conf := newConf(ts.URL)
+	tok, err := conf.Exchange(NoContext, "exchange-code")
+	if err != nil {
+		t.Error(err)
+	}
+	if tok.Expired() {
+		t.Errorf("Token shouldn't be expired.")
+	}
+	if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
+		t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
+	}
+	if tok.TokenType != "bearer" {
+		t.Errorf("Unexpected token type, %#v.", tok.TokenType)
+	}
+	scope := tok.Extra("scope")
+	if scope != "user" {
+		t.Errorf("Unexpected value for scope: %v", scope)
+	}
+}
+
+func TestExchangeRequest_BadResponse(t *testing.T) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "application/json")
+		w.Write([]byte(`{"scope": "user", "token_type": "bearer"}`))
+	}))
+	defer ts.Close()
+	conf := newConf(ts.URL)
+	tok, err := conf.Exchange(NoContext, "code")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if tok.AccessToken != "" {
+		t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
+	}
+}
+
+func TestExchangeRequest_BadResponseType(t *testing.T) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "application/json")
+		w.Write([]byte(`{"access_token":123,  "scope": "user", "token_type": "bearer"}`))
+	}))
+	defer ts.Close()
+	conf := newConf(ts.URL)
+	_, err := conf.Exchange(NoContext, "exchange-code")
+	if err == nil {
+		t.Error("expected error from invalid access_token type")
+	}
+}
+
+func TestExchangeRequest_NonBasicAuth(t *testing.T) {
+	tr := &mockTransport{
+		rt: func(r *http.Request) (w *http.Response, err error) {
+			headerAuth := r.Header.Get("Authorization")
+			if headerAuth != "" {
+				t.Errorf("Unexpected authorization header, %v is found.", headerAuth)
+			}
+			return nil, errors.New("no response")
+		},
+	}
+	c := &http.Client{Transport: tr}
+	conf := &Config{
+		ClientID: "CLIENT_ID",
+		Endpoint: Endpoint{
+			AuthURL:  "https://accounts.google.com/auth",
+			TokenURL: "https://accounts.google.com/token",
+		},
+	}
+
+	ctx := context.WithValue(context.Background(), HTTPClient, c)
+	conf.Exchange(ctx, "code")
+}
+
+func TestTokenRefreshRequest(t *testing.T) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		if r.URL.String() == "/somethingelse" {
+			return
+		}
+		if r.URL.String() != "/token" {
+			t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL)
+		}
+		headerContentType := r.Header.Get("Content-Type")
+		if headerContentType != "application/x-www-form-urlencoded" {
+			t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
+		}
+		body, _ := ioutil.ReadAll(r.Body)
+		if string(body) != "client_id=CLIENT_ID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN" {
+			t.Errorf("Unexpected refresh token payload, %v is found.", string(body))
+		}
+	}))
+	defer ts.Close()
+	conf := newConf(ts.URL)
+	c := conf.Client(NoContext, &Token{RefreshToken: "REFRESH_TOKEN"})
+	c.Get(ts.URL + "/somethingelse")
+}
+
+func TestFetchWithNoRefreshToken(t *testing.T) {
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		if r.URL.String() == "/somethingelse" {
+			return
+		}
+		if r.URL.String() != "/token" {
+			t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL)
+		}
+		headerContentType := r.Header.Get("Content-Type")
+		if headerContentType != "application/x-www-form-urlencoded" {
+			t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
+		}
+		body, _ := ioutil.ReadAll(r.Body)
+		if string(body) != "client_id=CLIENT_ID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN" {
+			t.Errorf("Unexpected refresh token payload, %v is found.", string(body))
+		}
+	}))
+	defer ts.Close()
+	conf := newConf(ts.URL)
+	c := conf.Client(NoContext, nil)
+	_, err := c.Get(ts.URL + "/somethingelse")
+	if err == nil {
+		t.Errorf("Fetch should return an error if no refresh token is set")
+	}
+}

+ 87 - 0
Godeps/_workspace/src/golang.org/x/oauth2/token.go

@@ -0,0 +1,87 @@
+// Copyright 2014 The oauth2 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 (
+	"net/http"
+	"net/url"
+	"time"
+)
+
+// Token represents the crendentials 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 derivate 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 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)
+}
+
+// Extra returns an extra field returned from the server during token
+// retrieval.
+func (t *Token) Extra(key string) string {
+	if vals, ok := t.raw.(url.Values); ok {
+		return vals.Get(key)
+	}
+	if raw, ok := t.raw.(map[string]interface{}); ok {
+		if val, ok := raw[key].(string); ok {
+			return val
+		}
+	}
+	return ""
+}
+
+// Expired returns true if there is no access token or the
+// access token is expired.
+func (t *Token) Expired() bool {
+	if t.AccessToken == "" {
+		return true
+	}
+	if t.Expiry.IsZero() {
+		return false
+	}
+	return t.Expiry.Before(time.Now())
+}

+ 138 - 0
Godeps/_workspace/src/golang.org/x/oauth2/transport.go

@@ -0,0 +1,138 @@
+// Copyright 2014 The oauth2 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. If no token exists or token is expired,
+// tries to refresh/fetch a new token.
+func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
+	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)
+	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
+	}
+}
+
+type errorTransport struct{ err error }
+
+func (t errorTransport) RoundTrip(*http.Request) (*http.Response, error) {
+	return nil, t.err
+}

+ 53 - 0
Godeps/_workspace/src/golang.org/x/oauth2/transport_test.go

@@ -0,0 +1,53 @@
+package oauth2
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+	"time"
+)
+
+type tokenSource struct{ token *Token }
+
+func (t *tokenSource) Token() (*Token, error) {
+	return t.token, nil
+}
+
+func TestTransportTokenSource(t *testing.T) {
+	ts := &tokenSource{
+		token: &Token{
+			AccessToken: "abc",
+		},
+	}
+	tr := &Transport{
+		Source: ts,
+	}
+	server := newMockServer(func(w http.ResponseWriter, r *http.Request) {
+		if r.Header.Get("Authorization") != "Bearer abc" {
+			t.Errorf("Transport doesn't set the Authorization header from the fetched token")
+		}
+	})
+	defer server.Close()
+	client := http.Client{Transport: tr}
+	client.Get(server.URL)
+}
+
+func TestExpiredWithNoAccessToken(t *testing.T) {
+	token := &Token{}
+	if !token.Expired() {
+		t.Errorf("Token should be expired if no access token is provided")
+	}
+}
+
+func TestExpiredWithExpiry(t *testing.T) {
+	token := &Token{
+		Expiry: time.Now().Add(-5 * time.Hour),
+	}
+	if !token.Expired() {
+		t.Errorf("Token should be expired if no access token is provided")
+	}
+}
+
+func newMockServer(handler func(w http.ResponseWriter, r *http.Request)) *httptest.Server {
+	return httptest.NewServer(http.HandlerFunc(handler))
+}

+ 1 - 0
Godeps/_workspace/src/gopkg.in/ini.v1/.gitignore

@@ -0,0 +1 @@
+testdata/conf_out.ini

+ 191 - 0
Godeps/_workspace/src/gopkg.in/ini.v1/LICENSE

@@ -0,0 +1,191 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "[]" replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same "printed page" as the copyright notice for easier identification within
+third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 261 - 0
Godeps/_workspace/src/gopkg.in/ini.v1/README.md

@@ -0,0 +1,261 @@
+ini [![Build Status](https://drone.io/github.com/go-ini/ini/status.png)](https://drone.io/github.com/go-ini/ini/latest) [![](http://gocover.io/_badge/github.com/go-ini/ini)](http://gocover.io/github.com/go-ini/ini)
+===
+
+Package ini provides INI file read and write functionality in Go.
+
+[简体中文](README_ZH.md)
+
+## Feature
+
+- Load multiple data sources(`[]byte` or file) with overwrites.
+- Read with recursion values.
+- Read with parent-child sections.
+- Read with auto-increment key names.
+- Read with multiple-line values.
+- Read with tons of helper methods.
+- Read and convert values to Go types.
+- Read and **WRITE** comments of sections and keys.
+- Manipulate sections, keys and comments with ease.
+- Keep sections and keys in order as you parse and save.
+
+## Installation
+
+	go get gopkg.in/ini.v1
+
+## Getting Started
+
+### Loading from data sources
+
+A **Data Source** is either raw data in type `[]byte` or a file name with type `string` and you can load **as many as** data sources you want. Passing other types will simply return an error.
+
+```go
+cfg, err := ini.Load([]byte("raw data"), "filename")
+```
+
+When you cannot decide how many data sources to load at the beginning, you still able to **Append()** them later.
+
+```go
+err := cfg.Append("other file", []byte("other raw data"))
+```
+
+### Working with sections
+
+To get a section, you would need to:
+
+```go
+section, err := cfg.GetSection("section name")
+```
+
+For a shortcut for default section, just give an empty string as name:
+
+```go
+section, err := cfg.GetSection("")
+```
+
+When you're pretty sure the section exists, following code could make your life easier:
+
+```go
+section := cfg.Section("")
+```
+
+What happens when the section somehow does not exists? Won't panic, it returns an empty section object.
+
+To create a new section:
+
+```go
+err := cfg.NewSection("new section")
+```
+
+To get a list of sections or section names:
+
+```go
+sections := cfg.Sections()
+names := cfg.SectionStrings()
+```
+
+### Working with keys
+
+To get a key under a section:
+
+```go
+key, err := cfg.Section("").GetKey("key name")
+```
+
+Same rule applies to key operations:
+
+```go
+key := cfg.Section("").Key("key name")
+```
+
+To create a new key:
+
+```go
+err := cfg.Section("").NewKey("name", "value")
+```
+
+To get a list of keys or key names:
+
+```go
+keys := cfg.Section().Keys()
+names := cfg.Section().KeyStrings()
+```
+
+To get a clone hash of keys and corresponding values:
+
+```go
+hash := cfg.GetSection("").KeysHash()
+```
+
+### Working with values
+
+To get a string value:
+
+```go
+val := cfg.Section("").Key("key name").String()
+```
+
+To get value with types:
+
+```go
+v, err = cfg.Section("").Key("BOOL").Bool()
+v, err = cfg.Section("").Key("FLOAT64").Float64()
+v, err = cfg.Section("").Key("INT").Int()
+v, err = cfg.Section("").Key("INT64").Int64()
+
+v = cfg.Section("").Key("BOOL").MustBool()
+v = cfg.Section("").Key("FLOAT64").MustFloat64()
+v = cfg.Section("").Key("INT").MustInt()
+v = cfg.Section("").Key("INT64").MustInt64()
+
+// Methods start with Must also accept one argument for default value
+// when key not found or fail to parse value to given type.
+// Except method MustString, which you have to pass a default value.
+
+v = cfg.Seciont("").Key("String").MustString("default")
+v = cfg.Section("").Key("BOOL").MustBool(true)
+v = cfg.Section("").Key("FLOAT64").MustFloat64(1.25)
+v = cfg.Section("").Key("INT").MustInt(10)
+v = cfg.Section("").Key("INT64").MustInt64(99)
+```
+
+What if my value is three-line long?
+
+```ini
+[advance]
+ADDRESS = """404 road,
+NotFound, State, 5000
+Earth"""
+```
+
+Not a problem!
+
+```go
+cfg.Section("advance").Key("ADDRESS").String()
+
+/* --- start ---
+404 road,
+NotFound, State, 5000
+Earth
+------  end  --- */
+```
+
+That's all? Hmm, no.
+
+#### Helper methods of working with values
+
+To get value with given candidates:
+
+```go
+v = cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"})
+v = cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75})
+v = cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30})
+v = cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30})
+```
+
+Default value will be presented if value of key is not in candidates you given, and default value does not need be one of candidates.
+
+To auto-split value into slice:
+
+```go
+vals = cfg.Section("").Key("STRINGS").Strings(",")
+vals = cfg.Section("").Key("FLOAT64S").Float64s(",")
+vals = cfg.Section("").Key("INTS").Ints(",")
+vals = cfg.Section("").Key("INT64S").Int64s(",")
+```
+
+### Advanced Usage
+
+#### Recursive Values
+
+For all value of keys, there is a special syntax `%(<name>)s`, where `<name>` is the key name in same section or default section, and `%(<name>)s` will be replaced by corresponding value(empty string if key not found). You can use this syntax at most 99 level of recursions.
+
+```ini
+NAME = ini
+
+[author]
+NAME = Unknwon
+GITHUB = https://github.com/%(NAME)s
+
+[package]
+FULL_NAME = github.com/go-ini/%(NAME)s
+```
+
+```go
+cfg.Section("author").Key("GITHUB").String()		// https://github.com/Unknwon
+cfg.Section("package").Key("FULL_NAME").String()	// github.com/go-ini/ini
+```
+
+#### Parent-child Sections
+
+You can use `.` in section name to indicate parent-child relationship between two or more sections. If the key not found in the child section, library will try again on its parent section until there is no parent section.
+
+```ini
+NAME = ini
+VERSION = v1
+IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
+
+[package]
+CLONE_URL = https://%(IMPORT_PATH)s
+
+[package.sub]
+```
+
+```go
+cfg.Section("package.sub").Key("CLONE_URL").String()	// https://gopkg.in/ini.v1
+```
+
+#### Auto-increment Key Names
+
+If key name is `-` in data source, then it would be seen as special syntax for auto-increment key name start from 1, and every section is independent on counter.
+
+```ini
+[features]
+-: Support read/write comments of keys and sections
+-: Support auto-increment of key names
+-: Support load multiple files to overwrite key values
+```
+
+```go
+cfg.Section("features").KeyStrings()	// []{"#1", "#2", "#3"}
+```
+
+## Getting Help
+
+- [API Documentation](https://gowalker.org/gopkg.in/ini.v1)
+- [File An Issue](https://github.com/go-ini/ini/issues/new)
+
+## FAQs
+
+### What does `BlockMode` field do?
+
+By default, library lets you read and write values so we need a locker to make sure your data is safe. But in cases that you are very sure about only reading data through the library, you can set `cfg.BlockMode = false` to speed up read operations about **50-70%** faster.
+
+### Why another INI library?
+
+Many people are using my another INI library [goconfig](https://github.com/Unknwon/goconfig), so the reason for this one is I would like to make more Go style code. Also when you set `cfg.BlockMode = false`, this one is about **10-30%** faster.
+
+To make those changes I have to confirm API broken, so it's safer to keep it in another place and start using `gopkg.in` to version my package at this time.(PS: shorter import path)
+
+## License
+
+This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text.

+ 252 - 0
Godeps/_workspace/src/gopkg.in/ini.v1/README_ZH.md

@@ -0,0 +1,252 @@
+本包提供了 Go 语言中读写 INI 文件的功能。
+
+## 功能特性
+
+- 支持覆盖加载多个数据源(`[]byte` 或文件)
+- 支持递归读取键值
+- 支持读取父子分区
+- 支持读取自增键名
+- 支持读取多行的键值
+- 支持大量辅助方法
+- 支持在读取时直接转换为 Go 语言类型
+- 支持读取和 **写入** 分区和键的注释
+- 轻松操作分区、键值和注释
+- 在保存文件时分区和键值会保持原有的顺序
+
+## 下载安装
+
+    go get gopkg.in/ini.v1
+
+## 开始使用
+
+### 从数据源加载
+
+一个 **数据源** 可以是 `[]byte` 类型的原始数据,或 `string` 类型的文件路径。您可以加载 **任意多个** 数据源。如果您传递其它类型的数据源,则会直接返回错误。
+
+```go
+cfg, err := ini.Load([]byte("raw data"), "filename")
+```
+
+当您在一开始无法决定需要加载哪些数据源时,仍可以使用 **Append()** 在需要的时候加载它们。
+
+```go
+err := cfg.Append("other file", []byte("other raw data"))
+```
+
+### 操作分区(Section)
+
+获取指定分区:
+
+```go
+section, err := cfg.GetSection("section name")
+```
+
+如果您想要获取默认分区,则可以用空字符串代替分区名:
+
+```go
+section, err := cfg.GetSection("")
+```
+
+当您非常确定某个分区是存在的,可以使用以下简便方法:
+
+```go
+section := cfg.Section("")
+```
+
+如果不小心判断错了,要获取的分区其实是不存在的,那会发生什么呢?没事的,它会返回一个空的分区对象。
+
+创建一个分区:
+
+```go
+err := cfg.NewSection("new section")
+```
+
+获取所有分区对象或名称:
+
+```go
+sections := cfg.Sections()
+names := cfg.SectionStrings()
+```
+
+### 操作键(Key)
+
+获取某个分区下的键:
+
+```go
+key, err := cfg.Section("").GetKey("key name")
+```
+
+和分区一样,您也可以直接获取键而忽略错误处理:
+
+```go
+key := cfg.Section("").Key("key name")
+```
+
+创建一个新的键:
+
+```go
+err := cfg.Section("").NewKey("name", "value")
+```
+
+获取分区下的所有键或键名:
+
+```go
+keys := cfg.Section().Keys()
+names := cfg.Section().KeyStrings()
+```
+
+获取分区下的所有键值对的克隆:
+
+```go
+hash := cfg.GetSection("").KeysHash()
+```
+
+### 操作键值(Value)
+
+获取一个类型为字符串(string)的值:
+
+```go
+val := cfg.Section("").Key("key name").String()
+```
+
+获取其它类型的值:
+
+```go
+v, err = cfg.Section("").Key("BOOL").Bool()
+v, err = cfg.Section("").Key("FLOAT64").Float64()
+v, err = cfg.Section("").Key("INT").Int()
+v, err = cfg.Section("").Key("INT64").Int64()
+
+v = cfg.Section("").Key("BOOL").MustBool()
+v = cfg.Section("").Key("FLOAT64").MustFloat64()
+v = cfg.Section("").Key("INT").MustInt()
+v = cfg.Section("").Key("INT64").MustInt64()
+
+// 由 Must 开头的方法名允许接收一个相同类型的参数来作为默认值,
+// 当键不存在或者转换失败时,则会直接返回该默认值。
+// 但是,MustString 方法必须传递一个默认值。
+
+v = cfg.Seciont("").Key("String").MustString("default")
+v = cfg.Section("").Key("BOOL").MustBool(true)
+v = cfg.Section("").Key("FLOAT64").MustFloat64(1.25)
+v = cfg.Section("").Key("INT").MustInt(10)
+v = cfg.Section("").Key("INT64").MustInt64(99)
+```
+
+如果我的值有好多行怎么办?
+
+```ini
+[advance]
+ADDRESS = """404 road,
+NotFound, State, 5000
+Earth"""
+```
+
+嗯哼?小 case!
+
+```go
+cfg.Section("advance").Key("ADDRESS").String()
+
+/* --- start ---
+404 road,
+NotFound, State, 5000
+Earth
+------  end  --- */
+```
+
+这就是全部了?哈哈,当然不是。
+
+#### 操作键值的辅助方法
+
+获取键值时设定候选值:
+
+```go
+v = cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"})
+v = cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75})
+v = cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30})
+v = cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30})
+```
+
+如果获取到的值不是候选值的任意一个,则会返回默认值,而默认值不需要是候选值中的一员。
+
+自动分割键值为切片(slice):
+
+```go
+vals = cfg.Section("").Key("STRINGS").Strings(",")
+vals = cfg.Section("").Key("FLOAT64S").Float64s(",")
+vals = cfg.Section("").Key("INTS").Ints(",")
+vals = cfg.Section("").Key("INT64S").Int64s(",")
+```
+
+### 高级用法
+
+#### 递归读取键值
+
+在获取所有键值的过程中,特殊语法 `%(<name>)s` 会被应用,其中 `<name>` 可以是相同分区或者默认分区下的键名。字符串 `%(<name>)s` 会被相应的键值所替代,如果指定的键不存在,则会用空字符串替代。您可以最多使用 99 层的递归嵌套。
+
+```ini
+NAME = ini
+
+[author]
+NAME = Unknwon
+GITHUB = https://github.com/%(NAME)s
+
+[package]
+FULL_NAME = github.com/go-ini/%(NAME)s
+```
+
+```go
+cfg.Section("author").Key("GITHUB").String()		// https://github.com/Unknwon
+cfg.Section("package").Key("FULL_NAME").String()	// github.com/go-ini/ini
+```
+
+#### 读取父子分区
+
+您可以在分区名称中使用 `.` 来表示两个或多个分区之间的父子关系。如果某个键在子分区中不存在,则会去它的父分区中再次寻找,直到没有父分区为止。
+
+```ini
+NAME = ini
+VERSION = v1
+IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
+
+[package]
+CLONE_URL = https://%(IMPORT_PATH)s
+
+[package.sub]
+```
+
+```go
+cfg.Section("package.sub").Key("CLONE_URL").String()	// https://gopkg.in/ini.v1
+```
+
+#### 读取自增键名
+
+如果数据源中的键名为 `-`,则认为该键使用了自增键名的特殊语法。计数器从 1 开始,并且分区之间是相互独立的。
+
+```ini
+[features]
+-: Support read/write comments of keys and sections
+-: Support auto-increment of key names
+-: Support load multiple files to overwrite key values
+```
+
+```go
+cfg.Section("features").KeyStrings()	// []{"#1", "#2", "#3"}
+```
+
+## 获取帮助
+
+- [API 文档](https://gowalker.org/gopkg.in/ini.v1)
+- [创建工单](https://github.com/go-ini/ini/issues/new)
+
+## 常见问题
+
+### 字段 `BlockMode` 是什么?
+
+默认情况下,本库会在您进行读写操作时采用锁机制来确保数据时间。但在某些情况下,您非常确定只进行读操作。此时,您可以通过设置 `cfg.BlockMode = false` 来将读操作提升大约 **50-70%** 的性能。
+
+### 为什么要写另一个 INI 解析库?
+
+许多人都在使用我的 [goconfig](https://github.com/Unknwon/goconfig) 来完成对 INI 文件的操作,但我希望使用更加 Go 风格的代码。并且当您设置 `cfg.BlockMode = false` 时,会有大约 **10-30%** 的性能提升。
+
+为了做出这些改变,我必须对 API 进行破坏,所以新开一个仓库是最安全的做法。除此之外,本库直接使用 `gopkg.in` 来进行版本化发布。(其实真相是导入路径更短了)

+ 852 - 0
Godeps/_workspace/src/gopkg.in/ini.v1/ini.go

@@ -0,0 +1,852 @@
+// Copyright 2014 Unknwon
+//
+// 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 ini provides INI file read and write functionality in Go.
+package ini
+
+import (
+	"bufio"
+	"bytes"
+	"errors"
+	"fmt"
+	"io"
+	"os"
+	"regexp"
+	"runtime"
+	"strconv"
+	"strings"
+	"sync"
+)
+
+const (
+	DEFAULT_SECTION = "DEFAULT"
+	// Maximum allowed depth when recursively substituing variable names.
+	_DEPTH_VALUES = 99
+)
+
+var (
+	LineBreak = "\n"
+
+	// Variable regexp pattern: %(variable)s
+	varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`)
+
+	// Write spaces around "=" to look better.
+	PrettyFormat = true
+)
+
+func init() {
+	if runtime.GOOS == "windows" {
+		LineBreak = "\r\n"
+	}
+}
+
+func inSlice(str string, s []string) bool {
+	for _, v := range s {
+		if str == v {
+			return true
+		}
+	}
+	return false
+}
+
+// dataSource is a interface that returns file content.
+type dataSource interface {
+	Reader() (io.Reader, error)
+}
+
+type sourceFile struct {
+	name string
+}
+
+func (s sourceFile) Reader() (io.Reader, error) {
+	return os.Open(s.name)
+}
+
+type sourceData struct {
+	data []byte
+}
+
+func (s *sourceData) Reader() (io.Reader, error) {
+	return bytes.NewReader(s.data), nil
+}
+
+//  ____  __.
+// |    |/ _|____ ___.__.
+// |      <_/ __ <   |  |
+// |    |  \  ___/\___  |
+// |____|__ \___  > ____|
+//         \/   \/\/
+
+// Key represents a key under a section.
+type Key struct {
+	s          *Section
+	Comment    string
+	name       string
+	value      string
+	isAutoIncr bool
+}
+
+// Name returns name of key.
+func (k *Key) Name() string {
+	return k.name
+}
+
+// Value returns raw value of key for performance purpose.
+func (k *Key) Value() string {
+	return k.value
+}
+
+// String returns string representation of value.
+func (k *Key) String() string {
+	val := k.value
+	if strings.Index(val, "%") == -1 {
+		return val
+	}
+
+	for i := 0; i < _DEPTH_VALUES; i++ {
+		vr := varPattern.FindString(val)
+		if len(vr) == 0 {
+			break
+		}
+
+		// Take off leading '%(' and trailing ')s'.
+		noption := strings.TrimLeft(vr, "%(")
+		noption = strings.TrimRight(noption, ")s")
+
+		// Search in the same section.
+		nk, err := k.s.GetKey(noption)
+		if err != nil {
+			// Search again in default section.
+			nk, _ = k.s.f.Section("").GetKey(noption)
+		}
+
+		// Substitute by new value and take off leading '%(' and trailing ')s'.
+		val = strings.Replace(val, vr, nk.value, -1)
+	}
+	return val
+}
+
+// Bool returns bool type value.
+func (k *Key) Bool() (bool, error) {
+	return strconv.ParseBool(k.String())
+}
+
+// Float64 returns float64 type value.
+func (k *Key) Float64() (float64, error) {
+	return strconv.ParseFloat(k.String(), 64)
+}
+
+// Int returns int type value.
+func (k *Key) Int() (int, error) {
+	return strconv.Atoi(k.String())
+}
+
+// Int64 returns int64 type value.
+func (k *Key) Int64() (int64, error) {
+	return strconv.ParseInt(k.String(), 10, 64)
+}
+
+// MustString returns default value if key value is empty.
+func (k *Key) MustString(defaultVal string) string {
+	val := k.String()
+	if len(val) == 0 {
+		return defaultVal
+	}
+	return val
+}
+
+// MustBool always returns value without error,
+// it returns false if error occurs.
+func (k *Key) MustBool(defaultVal ...bool) bool {
+	val, err := k.Bool()
+	if len(defaultVal) > 0 && err != nil {
+		return defaultVal[0]
+	}
+	return val
+}
+
+// MustFloat64 always returns value without error,
+// it returns 0.0 if error occurs.
+func (k *Key) MustFloat64(defaultVal ...float64) float64 {
+	value, err := k.Float64()
+	if len(defaultVal) > 0 && err != nil {
+		return defaultVal[0]
+	}
+	return value
+}
+
+// MustInt always returns value without error,
+// it returns 0 if error occurs.
+func (k *Key) MustInt(defaultVal ...int) int {
+	value, err := k.Int()
+	if len(defaultVal) > 0 && err != nil {
+		return defaultVal[0]
+	}
+	return value
+}
+
+// MustInt64 always returns value without error,
+// it returns 0 if error occurs.
+func (k *Key) MustInt64(defaultVal ...int64) int64 {
+	value, err := k.Int64()
+	if len(defaultVal) > 0 && err != nil {
+		return defaultVal[0]
+	}
+	return value
+}
+
+// In always returns value without error,
+// it returns default value if error occurs or doesn't fit into candidates.
+func (k *Key) In(defaultVal string, candidates []string) string {
+	val := k.String()
+	for _, cand := range candidates {
+		if val == cand {
+			return val
+		}
+	}
+	return defaultVal
+}
+
+// In always returns value without error,
+// it returns default value if error occurs or doesn't fit into candidates.
+func (k *Key) InFloat64(defaultVal float64, candidates []float64) float64 {
+	val := k.MustFloat64()
+	for _, cand := range candidates {
+		if val == cand {
+			return val
+		}
+	}
+	return defaultVal
+}
+
+// In always returns value without error,
+// it returns default value if error occurs or doesn't fit into candidates.
+func (k *Key) InInt(defaultVal int, candidates []int) int {
+	val := k.MustInt()
+	for _, cand := range candidates {
+		if val == cand {
+			return val
+		}
+	}
+	return defaultVal
+}
+
+// In always returns value without error,
+// it returns default value if error occurs or doesn't fit into candidates.
+func (k *Key) InInt64(defaultVal int64, candidates []int64) int64 {
+	val := k.MustInt64()
+	for _, cand := range candidates {
+		if val == cand {
+			return val
+		}
+	}
+	return defaultVal
+}
+
+// Strings returns list of string devide by given delimiter.
+func (k *Key) Strings(delim string) []string {
+	str := k.String()
+	if len(str) == 0 {
+		return []string{}
+	}
+
+	vals := strings.Split(str, delim)
+	for i := range vals {
+		vals[i] = strings.TrimSpace(vals[i])
+	}
+	return vals
+}
+
+// Float64s returns list of float64 devide by given delimiter.
+func (k *Key) Float64s(delim string) []float64 {
+	strs := k.Strings(delim)
+	vals := make([]float64, len(strs))
+	for i := range strs {
+		vals[i], _ = strconv.ParseFloat(strs[i], 64)
+	}
+	return vals
+}
+
+// Ints returns list of int devide by given delimiter.
+func (k *Key) Ints(delim string) []int {
+	strs := k.Strings(delim)
+	vals := make([]int, len(strs))
+	for i := range strs {
+		vals[i], _ = strconv.Atoi(strs[i])
+	}
+	return vals
+}
+
+// Int64s returns list of int64 devide by given delimiter.
+func (k *Key) Int64s(delim string) []int64 {
+	strs := k.Strings(delim)
+	vals := make([]int64, len(strs))
+	for i := range strs {
+		vals[i], _ = strconv.ParseInt(strs[i], 10, 64)
+	}
+	return vals
+}
+
+// SetValue changes key value.
+func (k *Key) SetValue(v string) {
+	k.value = v
+}
+
+//   _________              __  .__
+//  /   _____/ ____   _____/  |_|__| ____   ____
+//  \_____  \_/ __ \_/ ___\   __\  |/  _ \ /    \
+//  /        \  ___/\  \___|  | |  (  <_> )   |  \
+// /_______  /\___  >\___  >__| |__|\____/|___|  /
+//         \/     \/     \/                    \/
+
+// Section represents a config section.
+type Section struct {
+	f        *File
+	Comment  string
+	name     string
+	keys     map[string]*Key
+	keyList  []string
+	keysHash map[string]string
+}
+
+func newSection(f *File, name string) *Section {
+	return &Section{f, "", name, make(map[string]*Key), make([]string, 0, 10), make(map[string]string)}
+}
+
+// Name returns name of Section.
+func (s *Section) Name() string {
+	return s.name
+}
+
+// NewKey creates a new key to given section.
+func (s *Section) NewKey(name, val string) (*Key, error) {
+	if len(name) == 0 {
+		return nil, errors.New("error creating new key: empty key name")
+	}
+
+	if s.f.BlockMode {
+		s.f.lock.Lock()
+		defer s.f.lock.Unlock()
+	}
+
+	if inSlice(name, s.keyList) {
+		s.keys[name].value = val
+		return s.keys[name], nil
+	}
+
+	s.keyList = append(s.keyList, name)
+	s.keys[name] = &Key{s, "", name, val, false}
+	s.keysHash[name] = val
+	return s.keys[name], nil
+}
+
+// GetKey returns key in section by given name.
+func (s *Section) GetKey(name string) (*Key, error) {
+	// FIXME: change to section level lock?
+	if s.f.BlockMode {
+		s.f.lock.RLock()
+		defer s.f.lock.RUnlock()
+	}
+
+	key := s.keys[name]
+	if key == nil {
+		// Check if it is a child-section.
+		if i := strings.LastIndex(s.name, "."); i > -1 {
+			return s.f.Section(s.name[:i]).GetKey(name)
+		}
+		return nil, fmt.Errorf("error when getting key of section '%s': key '%s' not exists", s.name, name)
+	}
+	return key, nil
+}
+
+// Key assumes named Key exists in section and returns a zero-value when not.
+func (s *Section) Key(name string) *Key {
+	key, err := s.GetKey(name)
+	if err != nil {
+		return &Key{}
+	}
+	return key
+}
+
+// Keys returns list of keys of section.
+func (s *Section) Keys() []*Key {
+	keys := make([]*Key, len(s.keyList))
+	for i := range s.keyList {
+		keys[i] = s.Key(s.keyList[i])
+	}
+	return keys
+}
+
+// KeyStrings returns list of key names of section.
+func (s *Section) KeyStrings() []string {
+	list := make([]string, len(s.keyList))
+	copy(list, s.keyList)
+	return list
+}
+
+// KeysHash returns keys hash consisting of names and values.
+func (s *Section) KeysHash() map[string]string {
+	if s.f.BlockMode {
+		s.f.lock.RLock()
+		defer s.f.lock.RUnlock()
+	}
+
+	hash := map[string]string{}
+	for key, value := range s.keysHash {
+		hash[key] = value
+	}
+	return hash
+}
+
+// DeleteKey deletes a key from section.
+func (s *Section) DeleteKey(name string) {
+	if s.f.BlockMode {
+		s.f.lock.Lock()
+		defer s.f.lock.Unlock()
+	}
+
+	for i, k := range s.keyList {
+		if k == name {
+			s.keyList = append(s.keyList[:i], s.keyList[i+1:]...)
+			delete(s.keys, name)
+			return
+		}
+	}
+}
+
+// ___________.__.__
+// \_   _____/|__|  |   ____
+//  |    __)  |  |  | _/ __ \
+//  |     \   |  |  |_\  ___/
+//  \___  /   |__|____/\___  >
+//      \/                 \/
+
+// File represents a combination of a or more INI file(s) in memory.
+type File struct {
+	// Should make things safe, but sometimes doesn't matter.
+	BlockMode bool
+	// Make sure data is safe in multiple goroutines.
+	lock sync.RWMutex
+
+	// Allow combination of multiple data sources.
+	dataSources []dataSource
+	// Actual data is stored here.
+	sections map[string]*Section
+
+	// To keep data in order.
+	sectionList []string
+}
+
+// newFile initializes File object with given data sources.
+func newFile(dataSources []dataSource) *File {
+	return &File{
+		BlockMode:   true,
+		dataSources: dataSources,
+		sections:    make(map[string]*Section),
+		sectionList: make([]string, 0, 10),
+	}
+}
+
+func parseDataSource(source interface{}) (dataSource, error) {
+	switch s := source.(type) {
+	case string:
+		return sourceFile{s}, nil
+	case []byte:
+		return &sourceData{s}, nil
+	default:
+		return nil, fmt.Errorf("error parsing data source: unknown type '%s'", s)
+	}
+}
+
+// Load loads and parses from INI data sources.
+// Arguments can be mixed of file name with string type, or raw data in []byte.
+func Load(source interface{}, others ...interface{}) (_ *File, err error) {
+	sources := make([]dataSource, len(others)+1)
+	sources[0], err = parseDataSource(source)
+	if err != nil {
+		return nil, err
+	}
+	for i := range others {
+		sources[i+1], err = parseDataSource(others[i])
+		if err != nil {
+			return nil, err
+		}
+	}
+	f := newFile(sources)
+	return f, f.Reload()
+}
+
+// NewSection creates a new section.
+func (f *File) NewSection(name string) (*Section, error) {
+	if len(name) == 0 {
+		return nil, errors.New("error creating new section: empty section name")
+	}
+
+	if f.BlockMode {
+		f.lock.Lock()
+		defer f.lock.Unlock()
+	}
+
+	if inSlice(name, f.sectionList) {
+		return f.sections[name], nil
+	}
+
+	f.sectionList = append(f.sectionList, name)
+	f.sections[name] = newSection(f, name)
+	return f.sections[name], nil
+}
+
+// GetSection returns section by given name.
+func (f *File) GetSection(name string) (*Section, error) {
+	if len(name) == 0 {
+		name = DEFAULT_SECTION
+	}
+
+	if f.BlockMode {
+		f.lock.RLock()
+		defer f.lock.RUnlock()
+	}
+
+	sec := f.sections[name]
+	if sec == nil {
+		return nil, fmt.Errorf("error when getting section: section '%s' not exists", name)
+	}
+	return sec, nil
+}
+
+// Section assumes named section exists and returns a zero-value when not.
+func (f *File) Section(name string) *Section {
+	sec, err := f.GetSection(name)
+	if err != nil {
+		return newSection(f, name)
+	}
+	return sec
+}
+
+// Section returns list of Section.
+func (f *File) Sections() []*Section {
+	sections := make([]*Section, len(f.sectionList))
+	for i := range f.sectionList {
+		sections[i] = f.Section(f.sectionList[i])
+	}
+	return sections
+}
+
+// SectionStrings returns list of section names.
+func (f *File) SectionStrings() []string {
+	list := make([]string, len(f.sectionList))
+	copy(list, f.sectionList)
+	return list
+}
+
+// DeleteSection deletes a section.
+func (f *File) DeleteSection(name string) {
+	if f.BlockMode {
+		f.lock.Lock()
+		defer f.lock.Unlock()
+	}
+
+	if len(name) == 0 {
+		name = DEFAULT_SECTION
+	}
+
+	for i, s := range f.sectionList {
+		if s == name {
+			f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...)
+			delete(f.sections, name)
+			return
+		}
+	}
+}
+
+// parse parses data through an io.Reader.
+func (f *File) parse(reader io.Reader) error {
+	buf := bufio.NewReader(reader)
+
+	// Handle BOM-UTF8.
+	// http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding
+	mask, err := buf.Peek(3)
+	if err == nil && len(mask) >= 3 && mask[0] == 239 && mask[1] == 187 && mask[2] == 191 {
+		buf.Read(mask)
+	}
+
+	count := 1
+	comments := ""
+	isEnd := false
+
+	section, err := f.NewSection(DEFAULT_SECTION)
+	if err != nil {
+		return err
+	}
+
+	for {
+		line, err := buf.ReadString('\n')
+		line = strings.TrimSpace(line)
+		length := len(line)
+
+		// Check error and ignore io.EOF just for a moment.
+		if err != nil {
+			if err != io.EOF {
+				return fmt.Errorf("error reading next line: %v", err)
+			}
+			// The last line of file could be an empty line.
+			if length == 0 {
+				break
+			}
+			isEnd = true
+		}
+
+		// Skip empty lines.
+		if length == 0 {
+			continue
+		}
+
+		switch {
+		case line[0] == '#' || line[0] == ';': // Comments.
+			if len(comments) == 0 {
+				comments = line
+			} else {
+				comments += LineBreak + line
+			}
+			continue
+		case line[0] == '[' && line[length-1] == ']': // New sction.
+			name := strings.TrimSpace(line[1 : length-1])
+			section, err = f.NewSection(name)
+			if err != nil {
+				return err
+			}
+
+			if len(comments) > 0 {
+				section.Comment = comments
+				comments = ""
+			}
+			// Reset counter.
+			count = 1
+			continue
+		}
+
+		// Other possibilities.
+		var (
+			i        int
+			keyQuote string
+			kname    string
+			valQuote string
+			val      string
+		)
+
+		// Key name surrounded by quotes.
+		if line[0] == '"' {
+			if length > 6 && line[0:3] == `"""` {
+				keyQuote = `"""`
+			} else {
+				keyQuote = `"`
+			}
+		} else if line[0] == '`' {
+			keyQuote = "`"
+		}
+		if len(keyQuote) > 0 {
+			qLen := len(keyQuote)
+			pos := strings.Index(line[qLen:], keyQuote)
+			if pos == -1 {
+				return fmt.Errorf("error parsing line: missing closing key quote: %s", line)
+			}
+			pos = pos + qLen
+			i = strings.IndexAny(line[pos:], "=:")
+			if i < 0 {
+				return fmt.Errorf("error parsing line: key-value delimiter not found: %s", line)
+			} else if i == pos {
+				return fmt.Errorf("error parsing line: key is empty: %s", line)
+			}
+			i = i + pos
+			kname = line[qLen:pos] // Just keep spaces inside quotes.
+		} else {
+			i = strings.IndexAny(line, "=:")
+			if i < 0 {
+				return fmt.Errorf("error parsing line: key-value delimiter not found: %s", line)
+			} else if i == 0 {
+				return fmt.Errorf("error parsing line: key is empty: %s", line)
+			}
+			kname = strings.TrimSpace(line[0:i])
+		}
+
+		isAutoIncr := false
+		// Auto increment.
+		if kname == "-" {
+			isAutoIncr = true
+			kname = "#" + fmt.Sprint(count)
+			count++
+		}
+
+		lineRight := strings.TrimSpace(line[i+1:])
+		lineRightLength := len(lineRight)
+		firstChar := ""
+		if lineRightLength >= 2 {
+			firstChar = lineRight[0:1]
+		}
+		if firstChar == "`" {
+			valQuote = "`"
+		} else if lineRightLength >= 6 && lineRight[0:3] == `"""` {
+			valQuote = `"""`
+		}
+		if len(valQuote) > 0 {
+			qLen := len(valQuote)
+			pos := strings.LastIndex(lineRight[qLen:], valQuote)
+			// For multiple lines value.
+			if pos == -1 {
+				isEnd := false
+				val = lineRight[qLen:] + "\n"
+				for {
+					next, err := buf.ReadString('\n')
+					val += next
+					if err != nil {
+						if err != io.EOF {
+							return err
+						}
+						isEnd = true
+					}
+					pos = strings.LastIndex(next, valQuote)
+					if pos > -1 {
+						val = val[:len(val)-len(valQuote)-1]
+						break
+					}
+					if isEnd {
+						return fmt.Errorf("error parsing line: missing closing key quote from '%s' to '%s'", line, next)
+					}
+				}
+			} else {
+				val = lineRight[qLen : pos+qLen]
+			}
+		} else {
+			val = strings.TrimSpace(lineRight[0:])
+		}
+
+		k, err := section.NewKey(kname, val)
+		if err != nil {
+			return err
+		}
+		k.isAutoIncr = isAutoIncr
+		if len(comments) > 0 {
+			k.Comment = comments
+			comments = ""
+		}
+
+		if isEnd {
+			break
+		}
+	}
+	return nil
+}
+
+// Reload reloads and parses all data sources.
+func (f *File) Reload() error {
+	for _, s := range f.dataSources {
+		r, err := s.Reader()
+		if err != nil {
+			return err
+		}
+		if err = f.parse(r); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// Append appends one or more data sources and reloads automatically.
+func (f *File) Append(source interface{}, others ...interface{}) error {
+	ds, err := parseDataSource(source)
+	if err != nil {
+		return err
+	}
+	f.dataSources = append(f.dataSources, ds)
+	for _, s := range others {
+		ds, err = parseDataSource(s)
+		if err != nil {
+			return err
+		}
+		f.dataSources = append(f.dataSources, ds)
+	}
+	return f.Reload()
+}
+
+// SaveTo writes content to filesystem.
+func (f *File) SaveTo(filename string) (err error) {
+	equalSign := "="
+	if PrettyFormat {
+		equalSign = " = "
+	}
+
+	// Use buffer to make sure target is safe until finish encoding.
+	buf := bytes.NewBuffer(nil)
+	for i, sname := range f.sectionList {
+		sec := f.Section(sname)
+		if len(sec.Comment) > 0 {
+			if sec.Comment[0] != '#' && sec.Comment[0] != ';' {
+				sec.Comment = "; " + sec.Comment
+			}
+			if _, err = buf.WriteString(sec.Comment + LineBreak); err != nil {
+				return err
+			}
+		}
+
+		if i > 0 {
+			if _, err = buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
+				return err
+			}
+		}
+
+		for _, kname := range sec.keyList {
+			key := sec.Key(kname)
+			if len(key.Comment) > 0 {
+				if key.Comment[0] != '#' && key.Comment[0] != ';' {
+					key.Comment = "; " + key.Comment
+				}
+				if _, err = buf.WriteString(key.Comment + LineBreak); err != nil {
+					return err
+				}
+			}
+
+			switch {
+			case key.isAutoIncr:
+				kname = "-"
+			case strings.Contains(kname, "`") || strings.Contains(kname, `"`):
+				kname = `"""` + kname + `"""`
+			case strings.Contains(kname, `=`) || strings.Contains(kname, `:`):
+				kname = "`" + kname + "`"
+			}
+
+			val := key.value
+			// In case key value contains "\n", "`" or "\"".
+			if strings.Contains(val, "\n") || strings.Contains(val, "`") || strings.Contains(val, `"`) {
+				val = `"""` + val + `"""`
+			}
+			if _, err = buf.WriteString(kname + equalSign + val + LineBreak); err != nil {
+				return err
+			}
+		}
+
+		// Put a line between sections.
+		if _, err = buf.WriteString(LineBreak); err != nil {
+			return err
+		}
+	}
+
+	fw, err := os.Create(filename)
+	if err != nil {
+		return err
+	}
+	if _, err = buf.WriteTo(fw); err != nil {
+		return err
+	}
+	return fw.Close()
+}

+ 380 - 0
Godeps/_workspace/src/gopkg.in/ini.v1/ini_test.go

@@ -0,0 +1,380 @@
+// Copyright 2014 Unknwon
+//
+// 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 ini
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+const _CONF_DATA = `
+; Package name
+NAME = ini
+; Package version
+VERSION = v1
+; Package import path
+IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
+
+# Information about package author
+# Bio can be written in multiple lines.
+[author]
+NAME = Unknwon
+E-MAIL = fake@localhost
+GITHUB = https://github.com/%(NAME)s
+BIO = """Gopher.
+Coding addict.
+Good man.
+"""
+
+[package]
+CLONE_URL = https://%(IMPORT_PATH)s
+
+[package.sub]
+UNUSED_KEY = should be deleted
+
+[features]
+-: Support read/write comments of keys and sections
+-: Support auto-increment of key names
+-: Support load multiple files to overwrite key values
+
+[types]
+STRING = str
+BOOL = true
+FLOAT64 = 1.25
+INT = 10
+
+[array]
+STRINGS = en, zh, de
+FLOAT64S = 1.1, 2.2, 3.3
+INTS = 1, 2, 3
+
+[note]
+
+[advance]
+true = """"2+3=5""""
+"1+1=2" = true
+"""6+1=7""" = true
+"""` + "`" + `5+5` + "`" + `""" = 10
+""""6+6"""" = 12
+` + "`" + `7-2=4` + "`" + ` = false
+ADDRESS = ` + "`" + `404 road,
+NotFound, State, 50000` + "`"
+
+func Test_Load(t *testing.T) {
+	Convey("Load from data sources", t, func() {
+
+		Convey("Load with empty data", func() {
+			cfg, err := Load([]byte(""))
+			So(err, ShouldBeNil)
+			So(cfg, ShouldNotBeNil)
+		})
+
+		Convey("Load with multiple data sources", func() {
+			cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini")
+			So(err, ShouldBeNil)
+			So(cfg, ShouldNotBeNil)
+		})
+	})
+
+	Convey("Bad load process", t, func() {
+
+		Convey("Load from invalid data sources", func() {
+			_, err := Load(_CONF_DATA)
+			So(err, ShouldNotBeNil)
+
+			_, err = Load("testdata/404.ini")
+			So(err, ShouldNotBeNil)
+
+			_, err = Load(1)
+			So(err, ShouldNotBeNil)
+
+			_, err = Load([]byte(""), 1)
+			So(err, ShouldNotBeNil)
+		})
+
+		Convey("Load with empty section name", func() {
+			_, err := Load([]byte("[]"))
+			So(err, ShouldNotBeNil)
+		})
+
+		Convey("Load with bad keys", func() {
+			_, err := Load([]byte(`"""name`))
+			So(err, ShouldNotBeNil)
+
+			_, err = Load([]byte(`"""name"""`))
+			So(err, ShouldNotBeNil)
+
+			_, err = Load([]byte(`""=1`))
+			So(err, ShouldNotBeNil)
+
+			_, err = Load([]byte(`=`))
+			So(err, ShouldNotBeNil)
+
+			_, err = Load([]byte(`name`))
+			So(err, ShouldNotBeNil)
+		})
+
+		Convey("Load with bad values", func() {
+			_, err := Load([]byte(`name="""Unknwon`))
+			So(err, ShouldNotBeNil)
+		})
+	})
+}
+
+func Test_Values(t *testing.T) {
+	Convey("Test getting and setting values", t, func() {
+		cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini")
+		So(err, ShouldBeNil)
+		So(cfg, ShouldNotBeNil)
+
+		Convey("Get values in default section", func() {
+			sec := cfg.Section("")
+			So(sec, ShouldNotBeNil)
+			So(sec.Key("NAME").Value(), ShouldEqual, "ini")
+			So(sec.Key("NAME").String(), ShouldEqual, "ini")
+			So(sec.Key("NAME").Comment, ShouldEqual, "; Package name")
+			So(sec.Key("IMPORT_PATH").String(), ShouldEqual, "gopkg.in/ini.v1")
+		})
+
+		Convey("Get values in non-default section", func() {
+			sec := cfg.Section("author")
+			So(sec, ShouldNotBeNil)
+			So(sec.Key("NAME").String(), ShouldEqual, "Unknwon")
+			So(sec.Key("GITHUB").String(), ShouldEqual, "https://github.com/Unknwon")
+
+			sec = cfg.Section("package")
+			So(sec, ShouldNotBeNil)
+			So(sec.Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1")
+		})
+
+		Convey("Get auto-increment key names", func() {
+			keys := cfg.Section("features").Keys()
+			for i, k := range keys {
+				So(k.Name(), ShouldEqual, fmt.Sprintf("#%d", i+1))
+			}
+		})
+
+		Convey("Get overwrite value", func() {
+			So(cfg.Section("author").Key("E-MAIL").String(), ShouldEqual, "u@gogs.io")
+		})
+
+		Convey("Get sections", func() {
+			sections := cfg.Sections()
+			for i, name := range []string{DEFAULT_SECTION, "author", "package", "package.sub", "features", "types", "array", "note", "advance"} {
+				So(sections[i].Name(), ShouldEqual, name)
+			}
+		})
+
+		Convey("Get parent section value", func() {
+			So(cfg.Section("package.sub").Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1")
+		})
+
+		Convey("Get multiple line value", func() {
+			So(cfg.Section("author").Key("BIO").String(), ShouldEqual, "Gopher.\nCoding addict.\nGood man.\n")
+		})
+
+		Convey("Get values with type", func() {
+			sec := cfg.Section("types")
+			v1, err := sec.Key("BOOL").Bool()
+			So(err, ShouldBeNil)
+			So(v1, ShouldBeTrue)
+
+			v2, err := sec.Key("FLOAT64").Float64()
+			So(err, ShouldBeNil)
+			So(v2, ShouldEqual, 1.25)
+
+			v3, err := sec.Key("INT").Int()
+			So(err, ShouldBeNil)
+			So(v3, ShouldEqual, 10)
+
+			v4, err := sec.Key("INT").Int64()
+			So(err, ShouldBeNil)
+			So(v4, ShouldEqual, 10)
+
+			Convey("Must get values with type", func() {
+				So(sec.Key("STRING").MustString("404"), ShouldEqual, "str")
+				So(sec.Key("BOOL").MustBool(), ShouldBeTrue)
+				So(sec.Key("FLOAT64").MustFloat64(), ShouldEqual, 1.25)
+				So(sec.Key("INT").MustInt(), ShouldEqual, 10)
+				So(sec.Key("INT").MustInt64(), ShouldEqual, 10)
+
+				Convey("Must get values with default value", func() {
+					So(sec.Key("STRING_404").MustString("404"), ShouldEqual, "404")
+					So(sec.Key("BOOL_404").MustBool(true), ShouldBeTrue)
+					So(sec.Key("FLOAT64_404").MustFloat64(2.5), ShouldEqual, 2.5)
+					So(sec.Key("INT_404").MustInt(15), ShouldEqual, 15)
+					So(sec.Key("INT_404").MustInt64(15), ShouldEqual, 15)
+				})
+			})
+		})
+
+		Convey("Get value with candidates", func() {
+			sec := cfg.Section("types")
+			So(sec.Key("STRING").In("", []string{"str", "arr", "types"}), ShouldEqual, "str")
+			So(sec.Key("FLOAT64").InFloat64(0, []float64{1.25, 2.5, 3.75}), ShouldEqual, 1.25)
+			So(sec.Key("INT").InInt(0, []int{10, 20, 30}), ShouldEqual, 10)
+			So(sec.Key("INT").InInt64(0, []int64{10, 20, 30}), ShouldEqual, 10)
+
+			Convey("Get value with candidates and default value", func() {
+				So(sec.Key("STRING_404").In("str", []string{"str", "arr", "types"}), ShouldEqual, "str")
+				So(sec.Key("FLOAT64_404").InFloat64(1.25, []float64{1.25, 2.5, 3.75}), ShouldEqual, 1.25)
+				So(sec.Key("INT_404").InInt(10, []int{10, 20, 30}), ShouldEqual, 10)
+				So(sec.Key("INT64_404").InInt64(10, []int64{10, 20, 30}), ShouldEqual, 10)
+			})
+		})
+
+		Convey("Get values into slice", func() {
+			sec := cfg.Section("array")
+			So(strings.Join(sec.Key("STRINGS").Strings(","), ","), ShouldEqual, "en,zh,de")
+			So(len(sec.Key("STRINGS_404").Strings(",")), ShouldEqual, 0)
+
+			vals1 := sec.Key("FLOAT64S").Float64s(",")
+			for i, v := range []float64{1.1, 2.2, 3.3} {
+				So(vals1[i], ShouldEqual, v)
+			}
+
+			vals2 := sec.Key("INTS").Ints(",")
+			for i, v := range []int{1, 2, 3} {
+				So(vals2[i], ShouldEqual, v)
+			}
+
+			vals3 := sec.Key("INTS").Int64s(",")
+			for i, v := range []int64{1, 2, 3} {
+				So(vals3[i], ShouldEqual, v)
+			}
+		})
+
+		Convey("Get key hash", func() {
+			cfg.Section("").KeysHash()
+		})
+
+		Convey("Set key value", func() {
+			k := cfg.Section("author").Key("NAME")
+			k.SetValue("无闻")
+			So(k.String(), ShouldEqual, "无闻")
+		})
+
+		Convey("Get key strings", func() {
+			So(strings.Join(cfg.Section("types").KeyStrings(), ","), ShouldEqual, "STRING,BOOL,FLOAT64,INT")
+		})
+
+		Convey("Delete a key", func() {
+			cfg.Section("package.sub").DeleteKey("UNUSED_KEY")
+			_, err := cfg.Section("package.sub").GetKey("UNUSED_KEY")
+			So(err, ShouldNotBeNil)
+		})
+
+		Convey("Get section strings", func() {
+			So(strings.Join(cfg.SectionStrings(), ","), ShouldEqual, "DEFAULT,author,package,package.sub,features,types,array,note,advance")
+		})
+
+		Convey("Delete a section", func() {
+			cfg.DeleteSection("")
+			So(cfg.SectionStrings()[0], ShouldNotEqual, DEFAULT_SECTION)
+		})
+	})
+
+	Convey("Test getting and setting bad values", t, func() {
+		cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini")
+		So(err, ShouldBeNil)
+		So(cfg, ShouldNotBeNil)
+
+		Convey("Create new key with empty name", func() {
+			k, err := cfg.Section("").NewKey("", "")
+			So(err, ShouldNotBeNil)
+			So(k, ShouldBeNil)
+		})
+
+		Convey("Create new section with empty name", func() {
+			s, err := cfg.NewSection("")
+			So(err, ShouldNotBeNil)
+			So(s, ShouldBeNil)
+		})
+
+		Convey("Get section that not exists", func() {
+			s, err := cfg.GetSection("404")
+			So(err, ShouldNotBeNil)
+			So(s, ShouldBeNil)
+
+			s = cfg.Section("404")
+			So(s, ShouldNotBeNil)
+		})
+	})
+}
+
+func Test_File_Append(t *testing.T) {
+	Convey("Append data sources", t, func() {
+		cfg, err := Load([]byte(""))
+		So(err, ShouldBeNil)
+		So(cfg, ShouldNotBeNil)
+
+		So(cfg.Append([]byte(""), []byte("")), ShouldBeNil)
+
+		Convey("Append bad data sources", func() {
+			So(cfg.Append(1), ShouldNotBeNil)
+			So(cfg.Append([]byte(""), 1), ShouldNotBeNil)
+		})
+	})
+}
+
+func Test_File_SaveTo(t *testing.T) {
+	Convey("Save file", t, func() {
+		cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini")
+		So(err, ShouldBeNil)
+		So(cfg, ShouldNotBeNil)
+
+		So(cfg.SaveTo("testdata/conf_out.ini"), ShouldBeNil)
+	})
+}
+
+func Benchmark_Key_Value(b *testing.B) {
+	c, _ := Load([]byte(_CONF_DATA))
+	for i := 0; i < b.N; i++ {
+		c.Section("").Key("NAME").Value()
+	}
+}
+
+func Benchmark_Key_String(b *testing.B) {
+	c, _ := Load([]byte(_CONF_DATA))
+	for i := 0; i < b.N; i++ {
+		c.Section("").Key("NAME").String()
+	}
+}
+
+func Benchmark_Key_Value_NonBlock(b *testing.B) {
+	c, _ := Load([]byte(_CONF_DATA))
+	c.BlockMode = false
+	for i := 0; i < b.N; i++ {
+		c.Section("").Key("NAME").Value()
+	}
+}
+
+func Benchmark_Key_String_NonBlock(b *testing.B) {
+	c, _ := Load([]byte(_CONF_DATA))
+	c.BlockMode = false
+	for i := 0; i < b.N; i++ {
+		c.Section("").Key("NAME").String()
+	}
+}
+
+func Benchmark_Key_SetValue(b *testing.B) {
+	c, _ := Load([]byte(_CONF_DATA))
+	for i := 0; i < b.N; i++ {
+		c.Section("").Key("NAME").SetValue("10")
+	}
+}

+ 2 - 0
Godeps/_workspace/src/gopkg.in/ini.v1/testdata/conf.ini

@@ -0,0 +1,2 @@
+[author]
+E-MAIL = u@gogs.io