<commit_before>package dep

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/garyburd/go-oauth/oauth"
	"github.com/pkg/errors"
)

var version = "dev"

const (
	defaultBaseURL               = "https://mdmenrollment.apple.com"
	mediaType                    = "application/json;charset=UTF8"
	XServerProtocolVersionHeader = "X-Server-Protocol-Version"
	XServerProtocolVersion       = "3"
)

type HTTPClient interface {
	Do(req *http.Request) (*http.Response, error)
}

type Option func(*Client)

func WithServerURL(baseURL *url.URL) Option {
	return func(c *Client) {
		c.baseURL = baseURL
	}
}

func WithHTTPClient(client HTTPClient) Option {
	return func(c *Client) {
		c.client = client
	}
}

type Client struct {
	consumerKey      string //given by apple
	consumerSecret   string //given by apple
	accessToken      string //given by apple
	accessSecret     string //given by apple
	authSessionToken string //requested from DEP using above credentials
	sessionExpires   time.Time

	userAgent string
	client    HTTPClient

	baseURL *url.URL
}

type OAuthParameters struct {
	ConsumerKey    string `json:"consumer_key"`
	ConsumerSecret string `json:"consumer_secret"`
	AccessToken    string `json:"access_token"`
	AccessSecret   string `json:"access_secret"`
}

func NewClient(p OAuthParameters, opts ...Option) *Client {
	baseURL, _ := url.Parse(defaultBaseURL)
	client := Client{
		consumerKey:    p.ConsumerKey,
		consumerSecret: p.ConsumerSecret,
		accessToken:    p.AccessToken,
		accessSecret:   p.AccessSecret,
		client:         http.DefaultClient,
		userAgent:      path.Join("micromdm", version),
		baseURL:        baseURL,
	}
	for _, optFn := range opts {
		optFn(&client)
	}
	return &client
}

func (c *Client) session() error {
	if c.authSessionToken == "" {
		if err := c.newSession(); err != nil {
			return errors.Wrap(err, "creating new auth session for dep")
		}
	}

	if time.Now().After(c.sessionExpires) {
		if err := c.newSession(); err != nil {
			return errors.Wrap(err, "refreshing expired dep session")
		}
	}
	return nil
}

func (c *Client) newSession() error {
	var authSessionToken struct {
		AuthSessionToken string `json:"auth_session_token"`
	}
	consumerCredentials := oauth.Credentials{
		Token:  c.consumerKey,
		Secret: c.consumerSecret,
	}

	accessCredentials := &oauth.Credentials{
		Token:  c.accessToken,
		Secret: c.accessSecret,
	}
	form := url.Values{}

	rel, err := url.Parse("/session")
	if err != nil {
		return err
	}
	sessionURL := c.baseURL.ResolveReference(rel)

	oauthClient := oauth.Client{
		SignatureMethod: oauth.HMACSHA1,
		TokenRequestURI: sessionURL.String(),
		Credentials:     consumerCredentials,
	}

	// create request
	req, err := http.NewRequest("GET", oauthClient.TokenRequestURI, strings.NewReader(form.Encode()))
	if err != nil {
		return err
	}

	// set Authorization Header
	if err := oauthClient.SetAuthorizationHeader(
		req.Header,
		accessCredentials,
		"GET",
		req.URL,
		form,
	); err != nil {
		return err
	}
	// add headers
	req.Header.Add("User-Agent", c.userAgent)
	req.Header.Add("Content-Type", mediaType)
	req.Header.Add("Accept", mediaType)
	req.Header.Add(XServerProtocolVersionHeader, XServerProtocolVersion)

	// get Authorization Header
	resp, err := c.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// check resp statuscode
	if resp.StatusCode != http.StatusOK {
		return errors.Errorf("establishing DEP session: %v", resp.Status)
	}

	if err := json.NewDecoder(resp.Body).Decode(&authSessionToken); err != nil {
		return errors.Wrap(err, "decode authSessionToken from response")
	}

	// set token and expiration value
	c.authSessionToken = authSessionToken.AuthSessionToken
	c.sessionExpires = time.Now().Add(time.Minute * 3)
	return nil
}

// NewRequest creates a DEP request
func (c *Client) newRequest(method, urlStr string, body interface{}) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, errors.Wrapf(err, "parse dep request url %s", urlStr)
	}

	u := c.baseURL.ResolveReference(rel)
	var buf bytes.Buffer
	if body != nil {
		if err := json.NewEncoder(&buf).Encode(body); err != nil {
			return nil, errors.Wrap(err, "encode http body for DEP request")
		}
	}

	req, err := http.NewRequest(method, u.String(), &buf)
	if err != nil {
		return nil, errors.Wrapf(err, "creating %s request to dep %s", method, u.String())
	}

	req.Header.Add("User-Agent", c.userAgent)
	req.Header.Add("Content-Type", mediaType)
	req.Header.Add("Accept", mediaType)
	req.Header.Add(XServerProtocolVersionHeader, XServerProtocolVersion)
	return req, nil
}

func (c *Client) do(req *http.Request, into interface{}) error {
	if err := c.session(); err != nil {
		return errors.Wrapf(err, "get session for request to %s", c.baseURL.String())
	}
	req.Header.Add("X-ADM-Auth-Session", c.authSessionToken)

	out, _ := httputil.DumpRequestOut(req, false)
	fmt.Println(string(out))
	resp, err := c.client.Do(req)
	if err != nil {
		return errors.Wrap(err, "perform dep request")
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := ioutil.ReadAll(resp.Body)
		return errors.Errorf("unexpected dep response. status=%d DEP API Error: %s", resp.StatusCode, string(body))
	}
	err = json.NewDecoder(resp.Body).Decode(into)
	return errors.Wrap(err, "decode DEP response body")

}
<commit_msg>remove debug lines<commit_after>package dep

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/garyburd/go-oauth/oauth"
	"github.com/pkg/errors"
)

var version = "dev"

const (
	defaultBaseURL               = "https://mdmenrollment.apple.com"
	mediaType                    = "application/json;charset=UTF8"
	XServerProtocolVersionHeader = "X-Server-Protocol-Version"
	XServerProtocolVersion       = "3"
)

type HTTPClient interface {
	Do(req *http.Request) (*http.Response, error)
}

type Option func(*Client)

func WithServerURL(baseURL *url.URL) Option {
	return func(c *Client) {
		c.baseURL = baseURL
	}
}

func WithHTTPClient(client HTTPClient) Option {
	return func(c *Client) {
		c.client = client
	}
}

type Client struct {
	consumerKey      string //given by apple
	consumerSecret   string //given by apple
	accessToken      string //given by apple
	accessSecret     string //given by apple
	authSessionToken string //requested from DEP using above credentials
	sessionExpires   time.Time

	userAgent string
	client    HTTPClient

	baseURL *url.URL
}

type OAuthParameters struct {
	ConsumerKey    string `json:"consumer_key"`
	ConsumerSecret string `json:"consumer_secret"`
	AccessToken    string `json:"access_token"`
	AccessSecret   string `json:"access_secret"`
}

func NewClient(p OAuthParameters, opts ...Option) *Client {
	baseURL, _ := url.Parse(defaultBaseURL)
	client := Client{
		consumerKey:    p.ConsumerKey,
		consumerSecret: p.ConsumerSecret,
		accessToken:    p.AccessToken,
		accessSecret:   p.AccessSecret,
		client:         http.DefaultClient,
		userAgent:      path.Join("micromdm", version),
		baseURL:        baseURL,
	}
	for _, optFn := range opts {
		optFn(&client)
	}
	return &client
}

func (c *Client) session() error {
	if c.authSessionToken == "" {
		if err := c.newSession(); err != nil {
			return errors.Wrap(err, "creating new auth session for dep")
		}
	}

	if time.Now().After(c.sessionExpires) {
		if err := c.newSession(); err != nil {
			return errors.Wrap(err, "refreshing expired dep session")
		}
	}
	return nil
}

func (c *Client) newSession() error {
	var authSessionToken struct {
		AuthSessionToken string `json:"auth_session_token"`
	}
	consumerCredentials := oauth.Credentials{
		Token:  c.consumerKey,
		Secret: c.consumerSecret,
	}

	accessCredentials := &oauth.Credentials{
		Token:  c.accessToken,
		Secret: c.accessSecret,
	}
	form := url.Values{}

	rel, err := url.Parse("/session")
	if err != nil {
		return err
	}
	sessionURL := c.baseURL.ResolveReference(rel)

	oauthClient := oauth.Client{
		SignatureMethod: oauth.HMACSHA1,
		TokenRequestURI: sessionURL.String(),
		Credentials:     consumerCredentials,
	}

	// create request
	req, err := http.NewRequest("GET", oauthClient.TokenRequestURI, strings.NewReader(form.Encode()))
	if err != nil {
		return err
	}

	// set Authorization Header
	if err := oauthClient.SetAuthorizationHeader(
		req.Header,
		accessCredentials,
		"GET",
		req.URL,
		form,
	); err != nil {
		return err
	}
	// add headers
	req.Header.Add("User-Agent", c.userAgent)
	req.Header.Add("Content-Type", mediaType)
	req.Header.Add("Accept", mediaType)
	req.Header.Add(XServerProtocolVersionHeader, XServerProtocolVersion)

	// get Authorization Header
	resp, err := c.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// check resp statuscode
	if resp.StatusCode != http.StatusOK {
		return errors.Errorf("establishing DEP session: %v", resp.Status)
	}

	if err := json.NewDecoder(resp.Body).Decode(&authSessionToken); err != nil {
		return errors.Wrap(err, "decode authSessionToken from response")
	}

	// set token and expiration value
	c.authSessionToken = authSessionToken.AuthSessionToken
	c.sessionExpires = time.Now().Add(time.Minute * 3)
	return nil
}

// NewRequest creates a DEP request
func (c *Client) newRequest(method, urlStr string, body interface{}) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, errors.Wrapf(err, "parse dep request url %s", urlStr)
	}

	u := c.baseURL.ResolveReference(rel)
	var buf bytes.Buffer
	if body != nil {
		if err := json.NewEncoder(&buf).Encode(body); err != nil {
			return nil, errors.Wrap(err, "encode http body for DEP request")
		}
	}

	req, err := http.NewRequest(method, u.String(), &buf)
	if err != nil {
		return nil, errors.Wrapf(err, "creating %s request to dep %s", method, u.String())
	}

	req.Header.Add("User-Agent", c.userAgent)
	req.Header.Add("Content-Type", mediaType)
	req.Header.Add("Accept", mediaType)
	req.Header.Add(XServerProtocolVersionHeader, XServerProtocolVersion)
	return req, nil
}

func (c *Client) do(req *http.Request, into interface{}) error {
	if err := c.session(); err != nil {
		return errors.Wrapf(err, "get session for request to %s", c.baseURL.String())
	}
	req.Header.Add("X-ADM-Auth-Session", c.authSessionToken)

	resp, err := c.client.Do(req)
	if err != nil {
		return errors.Wrap(err, "perform dep request")
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := ioutil.ReadAll(resp.Body)
		return errors.Errorf("unexpected dep response. status=%d DEP API Error: %s", resp.StatusCode, string(body))
	}
	err = json.NewDecoder(resp.Body).Decode(into)
	return errors.Wrap(err, "decode DEP response body")

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime"

	"go/build"

	"github.com/golang/gddo/gosrc"

	"strings"
	"sync"

	"sourcegraph.com/sourcegraph/srclib/dep"
	"sourcegraph.com/sourcegraph/srclib/unit"
)

func init() {
	_, err := parser.AddCommand("depresolve",
		"resolve a Go package's imports",
		"Resolve a Go package's imports to their repository clone URL.",
		&depResolveCmd,
	)
	if err != nil {
		log.Fatal(err)
	}
}

type DepResolveCmd struct {
	Config []string `long:"config" description:"config property from Srcfile" value-name:"KEY=VALUE"`
}

var depResolveCmd DepResolveCmd

func (c *DepResolveCmd) Execute(args []string) error {
	var unit *unit.SourceUnit
	if err := json.NewDecoder(os.Stdin).Decode(&unit); err != nil {
		return err
	}
	if err := os.Stdin.Close(); err != nil {
		return err
	}

	if err := unmarshalTypedConfig(unit.Config); err != nil {
		return err
	}
	if err := config.apply(); err != nil {
		return err
	}

	res := make([]*dep.Resolution, len(unit.Dependencies))
	for i, rawDep := range unit.Dependencies {
		importPath, ok := rawDep.(string)
		if !ok {
			return fmt.Errorf("Go raw dep is not a string import path: %v (%T)", rawDep, rawDep)
		}

		res[i] = &dep.Resolution{Raw: rawDep}

		rt, err := ResolveDep(importPath, string(unit.Repo))
		if err != nil {
			res[i].Error = err.Error()
			continue
		}
		res[i].Target = rt
	}

	b, err := json.MarshalIndent(res, "", "  ")
	if err != nil {
		return err
	}
	if _, err := os.Stdout.Write(b); err != nil {
		return err
	}
	fmt.Println()
	return nil
}

var (
	resolveCache   map[string]*dep.ResolvedTarget
	resolveCacheMu sync.Mutex
)

func ResolveDep(importPath string, repoImportPath string) (*dep.ResolvedTarget, error) {
	// Look up in cache.
	resolvedTarget := func() *dep.ResolvedTarget {
		resolveCacheMu.Lock()
		defer resolveCacheMu.Unlock()
		return resolveCache[importPath]
	}()
	if resolvedTarget != nil {
		return resolvedTarget, nil
	}
	if strings.HasSuffix(importPath, "_test") {
		// TODO(sqs): handle xtest packages - these should not be appearing here
		// as import paths, but they are, so suppress errors
		return nil, fmt.Errorf("xtest package (%s) is not yet supported", importPath)
	}

	// Check if this import path is in this tree.
	if pkg, err := buildContext.Import(importPath, "", build.FindOnly); err == nil && (pathHasPrefix(pkg.Dir, cwd) || (virtualCWD != "" && pathHasPrefix(pkg.Dir, virtualCWD)) || (dockerCWD != "" && pathHasPrefix(pkg.Dir, dockerCWD))) {
		// TODO(sqs): do we want to link refs to vendored deps to their external repo? that's what it's doing now.
		return &dep.ResolvedTarget{
			// empty ToRepoCloneURL to indicate it's from this repository
			ToRepoCloneURL: "",
			ToUnit:         importPath,
			ToUnitType:     "GoPackage",
		}, nil
	}

	// Check if this import path is in this repository.
	if strings.HasPrefix(importPath, repoImportPath) {
		return &dep.ResolvedTarget{
			// empty ToRepoCloneURL to indicate it's from this repository
			ToRepoCloneURL: "",
			ToUnit:         importPath,
			ToUnitType:     "GoPackage",
		}, nil
	}

	// Special-case the cgo package "C".
	if importPath == "C" {
		return nil, nil
	}

	if gosrc.IsGoRepoPath(importPath) || importPath == "debug/goobj" || importPath == "debug/plan9obj" {
		return &dep.ResolvedTarget{
			ToRepoCloneURL:  "https://code.google.com/p/go",
			ToVersionString: runtime.Version(),
			ToRevSpec:       "", // TODO(sqs): fill in when graphing stdlib repo
			ToUnit:          importPath,
			ToUnitType:      "GoPackage",
		}, nil
	}

	// Special-case github.com/... import paths for performance.
	if strings.HasPrefix(importPath, "github.com/") || strings.HasPrefix(importPath, "sourcegraph.com/") {
		parts := strings.SplitN(importPath, "/", 4)
		if len(parts) < 3 {
			return nil, fmt.Errorf("import path starts with '(github|sourcegraph).com/' but is not valid: %q", importPath)
		}
		return &dep.ResolvedTarget{
			ToRepoCloneURL: "https://" + strings.Join(parts[:3], "/") + ".git",
			ToUnit:         importPath,
			ToUnitType:     "GoPackage",
		}, nil
	}

	// Special-case code.google.com/p/... import paths for performance.
	if strings.HasPrefix(importPath, "code.google.com/p/") {
		parts := strings.SplitN(importPath, "/", 4)
		if len(parts) < 3 {
			return nil, fmt.Errorf("import path starts with 'code.google.com/p/' but is not valid: %q", importPath)
		}
		return &dep.ResolvedTarget{
			ToRepoCloneURL: "https://" + strings.Join(parts[:3], "/"),
			ToUnit:         importPath,
			ToUnitType:     "GoPackage",
		}, nil
	}

	log.Printf("Resolving Go dep: %s", importPath)

	dir, err := gosrc.Get(http.DefaultClient, string(importPath), "")
	if err != nil {
		if strings.Contains(err.Error(), "Git Repository is empty.") {
			// Not fatal, just weird.
			return nil, nil
		}
		return nil, fmt.Errorf("unable to fetch information about Go package %q: %s", importPath, err)
	}

	// gosrc returns code.google.com URLs ending in a slash. Remove it.
	dir.ProjectURL = strings.TrimSuffix(dir.ProjectURL, "/")

	resolvedTarget = &dep.ResolvedTarget{
		ToRepoCloneURL: dir.ProjectURL,
		ToUnit:         importPath,
		ToUnitType:     "GoPackage",
	}

	// Save in cache.
	resolveCacheMu.Lock()
	defer resolveCacheMu.Unlock()
	if resolveCache == nil {
		resolveCache = make(map[string]*dep.ResolvedTarget)
	}
	resolveCache[importPath] = resolvedTarget

	return resolvedTarget, nil
}
<commit_msg>special-case google.golang.org/... import paths<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime"

	"go/build"

	"github.com/golang/gddo/gosrc"

	"strings"
	"sync"

	"sourcegraph.com/sourcegraph/srclib/dep"
	"sourcegraph.com/sourcegraph/srclib/unit"
)

func init() {
	_, err := parser.AddCommand("depresolve",
		"resolve a Go package's imports",
		"Resolve a Go package's imports to their repository clone URL.",
		&depResolveCmd,
	)
	if err != nil {
		log.Fatal(err)
	}
}

type DepResolveCmd struct {
	Config []string `long:"config" description:"config property from Srcfile" value-name:"KEY=VALUE"`
}

var depResolveCmd DepResolveCmd

func (c *DepResolveCmd) Execute(args []string) error {
	var unit *unit.SourceUnit
	if err := json.NewDecoder(os.Stdin).Decode(&unit); err != nil {
		return err
	}
	if err := os.Stdin.Close(); err != nil {
		return err
	}

	if err := unmarshalTypedConfig(unit.Config); err != nil {
		return err
	}
	if err := config.apply(); err != nil {
		return err
	}

	res := make([]*dep.Resolution, len(unit.Dependencies))
	for i, rawDep := range unit.Dependencies {
		importPath, ok := rawDep.(string)
		if !ok {
			return fmt.Errorf("Go raw dep is not a string import path: %v (%T)", rawDep, rawDep)
		}

		res[i] = &dep.Resolution{Raw: rawDep}

		rt, err := ResolveDep(importPath, string(unit.Repo))
		if err != nil {
			res[i].Error = err.Error()
			continue
		}
		res[i].Target = rt
	}

	b, err := json.MarshalIndent(res, "", "  ")
	if err != nil {
		return err
	}
	if _, err := os.Stdout.Write(b); err != nil {
		return err
	}
	fmt.Println()
	return nil
}

var (
	resolveCache   map[string]*dep.ResolvedTarget
	resolveCacheMu sync.Mutex
)

func ResolveDep(importPath string, repoImportPath string) (*dep.ResolvedTarget, error) {
	// Look up in cache.
	resolvedTarget := func() *dep.ResolvedTarget {
		resolveCacheMu.Lock()
		defer resolveCacheMu.Unlock()
		return resolveCache[importPath]
	}()
	if resolvedTarget != nil {
		return resolvedTarget, nil
	}
	if strings.HasSuffix(importPath, "_test") {
		// TODO(sqs): handle xtest packages - these should not be appearing here
		// as import paths, but they are, so suppress errors
		return nil, fmt.Errorf("xtest package (%s) is not yet supported", importPath)
	}

	// Check if this import path is in this tree.
	if pkg, err := buildContext.Import(importPath, "", build.FindOnly); err == nil && (pathHasPrefix(pkg.Dir, cwd) || (virtualCWD != "" && pathHasPrefix(pkg.Dir, virtualCWD)) || (dockerCWD != "" && pathHasPrefix(pkg.Dir, dockerCWD))) {
		// TODO(sqs): do we want to link refs to vendored deps to their external repo? that's what it's doing now.
		return &dep.ResolvedTarget{
			// empty ToRepoCloneURL to indicate it's from this repository
			ToRepoCloneURL: "",
			ToUnit:         importPath,
			ToUnitType:     "GoPackage",
		}, nil
	}

	// Check if this import path is in this repository.
	if strings.HasPrefix(importPath, repoImportPath) {
		return &dep.ResolvedTarget{
			// empty ToRepoCloneURL to indicate it's from this repository
			ToRepoCloneURL: "",
			ToUnit:         importPath,
			ToUnitType:     "GoPackage",
		}, nil
	}

	// Special-case the cgo package "C".
	if importPath == "C" {
		return nil, nil
	}

	if gosrc.IsGoRepoPath(importPath) || importPath == "debug/goobj" || importPath == "debug/plan9obj" {
		return &dep.ResolvedTarget{
			ToRepoCloneURL:  "https://code.google.com/p/go",
			ToVersionString: runtime.Version(),
			ToRevSpec:       "", // TODO(sqs): fill in when graphing stdlib repo
			ToUnit:          importPath,
			ToUnitType:      "GoPackage",
		}, nil
	}

	// Special-case github.com/... import paths for performance.
	if strings.HasPrefix(importPath, "github.com/") || strings.HasPrefix(importPath, "sourcegraph.com/") {
		parts := strings.SplitN(importPath, "/", 4)
		if len(parts) < 3 {
			return nil, fmt.Errorf("import path starts with '(github|sourcegraph).com/' but is not valid: %q", importPath)
		}
		return &dep.ResolvedTarget{
			ToRepoCloneURL: "https://" + strings.Join(parts[:3], "/") + ".git",
			ToUnit:         importPath,
			ToUnitType:     "GoPackage",
		}, nil
	}

	// Special-case google.golang.org/... (e.g., /appengine) import
	// paths for performance and to avoid hitting GitHub rate limit.
	if strings.HasPrefix(importPath, "google.golang.org/") {
		importPath = strings.Replace(importPath, "google.golang.org/", "github.com/golang/", 1)
		return &dep.ResolvedTarget{
			ToRepoCloneURL: "https://" + importPath + ".git",
			ToUnit:         importPath,
			ToUnitType:     "GoPackage",
		}, nil
	}

	// Special-case code.google.com/p/... import paths for performance.
	if strings.HasPrefix(importPath, "code.google.com/p/") {
		parts := strings.SplitN(importPath, "/", 4)
		if len(parts) < 3 {
			return nil, fmt.Errorf("import path starts with 'code.google.com/p/' but is not valid: %q", importPath)
		}
		return &dep.ResolvedTarget{
			ToRepoCloneURL: "https://" + strings.Join(parts[:3], "/"),
			ToUnit:         importPath,
			ToUnitType:     "GoPackage",
		}, nil
	}

	log.Printf("Resolving Go dep: %s", importPath)

	dir, err := gosrc.Get(http.DefaultClient, string(importPath), "")
	if err != nil {
		if strings.Contains(err.Error(), "Git Repository is empty.") {
			// Not fatal, just weird.
			return nil, nil
		}
		return nil, fmt.Errorf("unable to fetch information about Go package %q: %s", importPath, err)
	}

	// gosrc returns code.google.com URLs ending in a slash. Remove it.
	dir.ProjectURL = strings.TrimSuffix(dir.ProjectURL, "/")

	resolvedTarget = &dep.ResolvedTarget{
		ToRepoCloneURL: dir.ProjectURL,
		ToUnit:         importPath,
		ToUnitType:     "GoPackage",
	}

	// Save in cache.
	resolveCacheMu.Lock()
	defer resolveCacheMu.Unlock()
	if resolveCache == nil {
		resolveCache = make(map[string]*dep.ResolvedTarget)
	}
	resolveCache[importPath] = resolvedTarget

	return resolvedTarget, nil
}
<|endoftext|>


<commit_before>package design

import (
	. "github.com/goadesign/goa/design"
	. "github.com/goadesign/goa/design/apidsl"
)

// This is the api definition used by goa to generate the api
var _ = API("congo", func() {
	Title("Congo")
	Description("Congo")
	Contact(func() {
		Name("congo")
		Email("congo")
		URL("https://congo.com")
	})

	Docs(func() {
		Description("Getting Started Guide")
		URL("https://congo.com")
	})
	Host("congo.gopheracademy.com")
	Scheme("https")
	BasePath("/api")

	ResponseTemplate(Created, func(pattern string) {
		Description("Resource created")
		Status(201)
		Headers(func() {
			Header("Location", String, "href to created resource", func() {
				Pattern(pattern)
			})
		})
	})

	JWTSecurity("jwt", func() {
		Header("Authorization")
		TokenURL("https://congo.gopheracademy.com/api/auth/token")
	})
	BasicAuthSecurity("password", func() {
		Description("Use your own password!")
	})
})
<commit_msg>clean up api<commit_after>package design

import (
	. "github.com/goadesign/goa/design"
	. "github.com/goadesign/goa/design/apidsl"
)

// This is the api definition used by goa to generate the api
var _ = API("congo", func() {
	Title("Congo")
	Description("Congo")
	Contact(func() {
		Name("congo")
		Email("congo")
		URL("https://congo.gopheracademy.com")
	})

	Docs(func() {
		Description("Getting Started Guide")
		URL("https://congodocs.gopheracademy.com")
	})
	Host("congo.gopheracademy.com")
	Scheme("https")
	BasePath("/api")

	ResponseTemplate(Created, func(pattern string) {
		Description("Resource created")
		Status(201)
		Headers(func() {
			Header("Location", String, "href to created resource", func() {
				Pattern(pattern)
			})
		})
	})

	JWTSecurity("jwt", func() {
		Header("Authorization")
		TokenURL("https://congo.gopheracademy.com/api/auth/token")
	})
	BasicAuthSecurity("password", func() {
		Description("Use your own password!")
	})
})
<|endoftext|>


<commit_before>package diff

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
)

const (
	tokenDiffGit        = "diff --git" // diff --git a/sample.old.txt b/sample.new.txt
	tokenOldFile        = "---"        // --- sample.old.txt	2016-10-13 05:09:35.820791185 +0900
	tokenNewFile        = "+++"        // +++ sample.new.txt	2016-10-13 05:15:26.839245048 +0900
	tokenStartHunk      = "@@"         // @@ -1,3 +1,4 @@
	tokenUnchangedLine  = " "          //  unchanged, contextual line
	tokenAddedLine      = "+"          // +added line
	tokenDeletedLine    = "-"          // -deleted line
	tokenNoNewlineAtEOF = `\`          // \ No newline at end of file
)

var (
	// ErrNoNewFile represents error which there are no expected new file line.
	ErrNoNewFile = errors.New("no expected new file line") // +++ newfile
	// ErrNoHunks represents error which there are no expected hunks.
	ErrNoHunks = errors.New("no expected hunks") // @@ -1,3 +1,4 @@
)

// ErrInvalidHunkRange represents invalid line of hunk range. @@ -1,3 +1,4 @@
type ErrInvalidHunkRange struct {
	invalid string
}

func (e *ErrInvalidHunkRange) Error() string {
	return fmt.Sprintf("invalid hunk range: %v", e.invalid)
}

// ParseMultiFile parses a multi-file unified diff.
func ParseMultiFile(r io.Reader) ([]*FileDiff, error) {
	return (&multiFileParser{r: bufio.NewReader(r)}).Parse()
}

type multiFileParser struct {
	r *bufio.Reader
}

func (p *multiFileParser) Parse() ([]*FileDiff, error) {
	var fds []*FileDiff
	fp := &fileParser{r: p.r}
	for {
		fd, err := fp.Parse()
		if err != nil || fd == nil {
			break
		}
		fds = append(fds, fd)
	}
	return fds, nil
}

// ParseFile parses a file unified diff.
func ParseFile(r io.Reader) (*FileDiff, error) {
	return (&fileParser{r: bufio.NewReader(r)}).Parse()
}

type fileParser struct {
	r *bufio.Reader
}

func (p *fileParser) Parse() (*FileDiff, error) {
	fd := &FileDiff{}
	fd.Extended = parseExtendedHeader(p.r)
	b, err := p.r.Peek(len(tokenOldFile))
	if err != nil {
		if err == io.EOF && len(fd.Extended) > 0 {
			return fd, nil
		}
		return nil, nil
	}
	if bytes.HasPrefix(b, []byte(tokenOldFile)) {
		// parse `--- sample.old.txt	2016-10-13 05:09:35.820791185 +0900`
		oldline, _ := readline(p.r) // ignore err because we know it can read something
		fd.PathOld, fd.TimeOld = parseFileHeader(oldline)
		// parse `+++ sample.new.txt	2016-10-13 05:09:35.820791185 +0900`
		if b, err := p.r.Peek(len(tokenNewFile)); err != nil || !bytes.HasPrefix(b, []byte(tokenNewFile)) {
			return nil, ErrNoNewFile
		}
		newline, _ := readline(p.r) // ignore err because we know it can read something
		fd.PathNew, fd.TimeNew = parseFileHeader(newline)
	}
	// parse hunks
	fd.Hunks, err = p.parseHunks()
	if err != nil {
		return nil, err
	}
	return fd, nil
}

func (p *fileParser) parseHunks() ([]*Hunk, error) {
	b, err := p.r.Peek(len(tokenOldFile))
	if err != nil {
		return nil, ErrNoHunks
	}
	if !bytes.HasPrefix(b, []byte(tokenStartHunk)) {
		b, err := p.r.Peek(len(tokenDiffGit))
		if err != nil {
			return nil, ErrNoHunks
		}
		if !bytes.HasPrefix(b, []byte(tokenDiffGit)) {
			return nil, ErrNoHunks
		}
		// It seems that you deleted an empty file.
		return []*Hunk{}, nil
	}
	var hunks []*Hunk
	hp := &hunkParser{r: p.r}
	for {
		h, err := hp.Parse()
		if err != nil {
			return nil, err
		}
		if h == nil {
			break
		}
		hunks = append(hunks, h)
	}
	return hunks, nil
}

// parseFileHeader parses file header line and returns filename and timestamp.
// timestamp may be empty.
func parseFileHeader(line string) (filename, timestamp string) {
	// strip `+++ ` or `--- `
	ss := line[len(tokenOldFile)+1:]
	tabi := strings.LastIndex(ss, "\t")
	if tabi == -1 {
		return ss, ""
	}
	return ss[:tabi], ss[tabi+1:]
}

func parseExtendedHeader(r *bufio.Reader) []string {
	var es []string
	b, err := r.Peek(len(tokenDiffGit))
	if err != nil {
		return nil
	}
	// if starts with 'diff --git', parse extended header
	if bytes.HasPrefix(b, []byte(tokenDiffGit)) {
		diffgitline, _ := readline(r) // ignore err because we know it can read something
		es = append(es, diffgitline)
		for {
			b, err := r.Peek(len(tokenDiffGit))
			if err != nil || bytes.HasPrefix(b, []byte(tokenOldFile)) || bytes.HasPrefix(b, []byte(tokenDiffGit)) {
				break
			}
			line, _ := readline(r)
			es = append(es, string(line))
		}
	}
	return es
}

type hunkParser struct {
	r        *bufio.Reader
	lnumdiff int
}

func (p *hunkParser) Parse() (*Hunk, error) {
	if b, err := p.r.Peek(len(tokenStartHunk)); err != nil || !bytes.HasPrefix(b, []byte(tokenStartHunk)) {
		return nil, nil
	}
	rangeline, _ := readline(p.r)
	hr, err := parseHunkRange(rangeline)
	if err != nil {
		return nil, err
	}
	hunk := &Hunk{
		StartLineOld:  hr.lold,
		LineLengthOld: hr.sold,
		StartLineNew:  hr.lnew,
		LineLengthNew: hr.snew,
		Section:       hr.section,
	}
	lold := hr.lold
	lnew := hr.lnew
endhunk:
	for !p.done(lold, lnew, hr) {
		b, err := p.r.Peek(1)
		if err != nil {
			break
		}
		token := string(b)
		switch token {
		case tokenUnchangedLine, tokenAddedLine, tokenDeletedLine:
			p.lnumdiff++
			l, _ := readline(p.r)
			line := &Line{Content: l[len(token):]} // trim first token
			switch token {
			case tokenUnchangedLine:
				line.Type = LineUnchanged
				line.LnumDiff = p.lnumdiff
				line.LnumOld = lold
				line.LnumNew = lnew
				lold++
				lnew++
			case tokenAddedLine:
				line.Type = LineAdded
				line.LnumDiff = p.lnumdiff
				line.LnumNew = lnew
				lnew++
			case tokenDeletedLine:
				line.Type = LineDeleted
				line.LnumDiff = p.lnumdiff
				line.LnumOld = lold
				lold++
			}
			hunk.Lines = append(hunk.Lines, line)
		case tokenNoNewlineAtEOF:
			// skip \ No newline at end of file. just consume line
			readline(p.r)
		default:
			break endhunk
		}
	}
	p.lnumdiff++ // count up by an additional hunk
	return hunk, nil
}

func (p *hunkParser) done(lold, lnew int, hr *hunkrange) bool {
	end := (lold >= hr.lold+hr.sold && lnew >= hr.lnew+hr.snew)
	if b, err := p.r.Peek(1); err != nil || (string(b) != tokenNoNewlineAtEOF && end) {
		return true
	}
	return false
}

// @@ -l,s +l,s @@ optional section heading
type hunkrange struct {
	lold, sold, lnew, snew int
	section                string
}

// @@ -lold[,sold] +lnew[,snew] @@[ section]
// 0  1              2            3   4
func parseHunkRange(rangeline string) (*hunkrange, error) {
	ps := strings.SplitN(rangeline, " ", 5)
	invalidErr := &ErrInvalidHunkRange{invalid: rangeline}
	hunkrange := &hunkrange{}
	if len(ps) < 4 || ps[0] != "@@" || ps[3] != "@@" {
		return nil, invalidErr
	}
	old := ps[1] // -lold[,sold]
	if !strings.HasPrefix(old, "-") {
		return nil, invalidErr
	}
	lold, sold, err := parseLS(old[1:])
	if err != nil {
		return nil, invalidErr
	}
	hunkrange.lold = lold
	hunkrange.sold = sold
	new := ps[2] // +lnew[,snew]
	if !strings.HasPrefix(new, "+") {
		return nil, invalidErr
	}
	lnew, snew, err := parseLS(new[1:])
	if err != nil {
		return nil, invalidErr
	}
	hunkrange.lnew = lnew
	hunkrange.snew = snew
	if len(ps) == 5 {
		hunkrange.section = ps[4]
	}
	return hunkrange, nil
}

// l[,s]
func parseLS(ls string) (l, s int, err error) {
	ss := strings.SplitN(ls, ",", 2)
	l, err = strconv.Atoi(ss[0])
	if err != nil {
		return 0, 0, err
	}
	if len(ss) == 2 {
		s, err = strconv.Atoi(ss[1])
		if err != nil {
			return 0, 0, err
		}
	} else {
		s = 1
	}
	return l, s, nil
}

// readline reads lines from bufio.Reader with size limit. It consumes
// remaining content even if the line size reaches size limit.
func readline(r *bufio.Reader) (string, error) {
	line, isPrefix, err := r.ReadLine()
	if err != nil {
		return "", err
	}
	// consume all remaining line content
	for isPrefix {
		_, isPrefix, _ = r.ReadLine()
	}
	return string(line), nil
}
<commit_msg>invert the condition for special case for "git diff".<commit_after>package diff

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
)

const (
	tokenDiffGit        = "diff --git" // diff --git a/sample.old.txt b/sample.new.txt
	tokenOldFile        = "---"        // --- sample.old.txt	2016-10-13 05:09:35.820791185 +0900
	tokenNewFile        = "+++"        // +++ sample.new.txt	2016-10-13 05:15:26.839245048 +0900
	tokenStartHunk      = "@@"         // @@ -1,3 +1,4 @@
	tokenUnchangedLine  = " "          //  unchanged, contextual line
	tokenAddedLine      = "+"          // +added line
	tokenDeletedLine    = "-"          // -deleted line
	tokenNoNewlineAtEOF = `\`          // \ No newline at end of file
)

var (
	// ErrNoNewFile represents error which there are no expected new file line.
	ErrNoNewFile = errors.New("no expected new file line") // +++ newfile
	// ErrNoHunks represents error which there are no expected hunks.
	ErrNoHunks = errors.New("no expected hunks") // @@ -1,3 +1,4 @@
)

// ErrInvalidHunkRange represents invalid line of hunk range. @@ -1,3 +1,4 @@
type ErrInvalidHunkRange struct {
	invalid string
}

func (e *ErrInvalidHunkRange) Error() string {
	return fmt.Sprintf("invalid hunk range: %v", e.invalid)
}

// ParseMultiFile parses a multi-file unified diff.
func ParseMultiFile(r io.Reader) ([]*FileDiff, error) {
	return (&multiFileParser{r: bufio.NewReader(r)}).Parse()
}

type multiFileParser struct {
	r *bufio.Reader
}

func (p *multiFileParser) Parse() ([]*FileDiff, error) {
	var fds []*FileDiff
	fp := &fileParser{r: p.r}
	for {
		fd, err := fp.Parse()
		if err != nil || fd == nil {
			break
		}
		fds = append(fds, fd)
	}
	return fds, nil
}

// ParseFile parses a file unified diff.
func ParseFile(r io.Reader) (*FileDiff, error) {
	return (&fileParser{r: bufio.NewReader(r)}).Parse()
}

type fileParser struct {
	r *bufio.Reader
}

func (p *fileParser) Parse() (*FileDiff, error) {
	fd := &FileDiff{}
	fd.Extended = parseExtendedHeader(p.r)
	b, err := p.r.Peek(len(tokenOldFile))
	if err != nil {
		if err == io.EOF && len(fd.Extended) > 0 {
			return fd, nil
		}
		return nil, nil
	}
	if bytes.HasPrefix(b, []byte(tokenOldFile)) {
		// parse `--- sample.old.txt	2016-10-13 05:09:35.820791185 +0900`
		oldline, _ := readline(p.r) // ignore err because we know it can read something
		fd.PathOld, fd.TimeOld = parseFileHeader(oldline)
		// parse `+++ sample.new.txt	2016-10-13 05:09:35.820791185 +0900`
		if b, err := p.r.Peek(len(tokenNewFile)); err != nil || !bytes.HasPrefix(b, []byte(tokenNewFile)) {
			return nil, ErrNoNewFile
		}
		newline, _ := readline(p.r) // ignore err because we know it can read something
		fd.PathNew, fd.TimeNew = parseFileHeader(newline)
	}
	// parse hunks
	fd.Hunks, err = p.parseHunks()
	if err != nil {
		return nil, err
	}
	return fd, nil
}

func (p *fileParser) parseHunks() ([]*Hunk, error) {
	b, err := p.r.Peek(len(tokenOldFile))
	if err != nil {
		return nil, ErrNoHunks
	}
	if !bytes.HasPrefix(b, []byte(tokenStartHunk)) {
		b, err := p.r.Peek(len(tokenDiffGit))
		if err != nil {
			return nil, ErrNoHunks
		}
		if bytes.HasPrefix(b, []byte(tokenDiffGit)) {
			// git diff may contain a file diff with empty hunks.
			// e.g. delete an empty file.
			return []*Hunk{}, nil
		}
		return nil, ErrNoHunks
	}
	var hunks []*Hunk
	hp := &hunkParser{r: p.r}
	for {
		h, err := hp.Parse()
		if err != nil {
			return nil, err
		}
		if h == nil {
			break
		}
		hunks = append(hunks, h)
	}
	return hunks, nil
}

// parseFileHeader parses file header line and returns filename and timestamp.
// timestamp may be empty.
func parseFileHeader(line string) (filename, timestamp string) {
	// strip `+++ ` or `--- `
	ss := line[len(tokenOldFile)+1:]
	tabi := strings.LastIndex(ss, "\t")
	if tabi == -1 {
		return ss, ""
	}
	return ss[:tabi], ss[tabi+1:]
}

func parseExtendedHeader(r *bufio.Reader) []string {
	var es []string
	b, err := r.Peek(len(tokenDiffGit))
	if err != nil {
		return nil
	}
	// if starts with 'diff --git', parse extended header
	if bytes.HasPrefix(b, []byte(tokenDiffGit)) {
		diffgitline, _ := readline(r) // ignore err because we know it can read something
		es = append(es, diffgitline)
		for {
			b, err := r.Peek(len(tokenDiffGit))
			if err != nil || bytes.HasPrefix(b, []byte(tokenOldFile)) || bytes.HasPrefix(b, []byte(tokenDiffGit)) {
				break
			}
			line, _ := readline(r)
			es = append(es, string(line))
		}
	}
	return es
}

type hunkParser struct {
	r        *bufio.Reader
	lnumdiff int
}

func (p *hunkParser) Parse() (*Hunk, error) {
	if b, err := p.r.Peek(len(tokenStartHunk)); err != nil || !bytes.HasPrefix(b, []byte(tokenStartHunk)) {
		return nil, nil
	}
	rangeline, _ := readline(p.r)
	hr, err := parseHunkRange(rangeline)
	if err != nil {
		return nil, err
	}
	hunk := &Hunk{
		StartLineOld:  hr.lold,
		LineLengthOld: hr.sold,
		StartLineNew:  hr.lnew,
		LineLengthNew: hr.snew,
		Section:       hr.section,
	}
	lold := hr.lold
	lnew := hr.lnew
endhunk:
	for !p.done(lold, lnew, hr) {
		b, err := p.r.Peek(1)
		if err != nil {
			break
		}
		token := string(b)
		switch token {
		case tokenUnchangedLine, tokenAddedLine, tokenDeletedLine:
			p.lnumdiff++
			l, _ := readline(p.r)
			line := &Line{Content: l[len(token):]} // trim first token
			switch token {
			case tokenUnchangedLine:
				line.Type = LineUnchanged
				line.LnumDiff = p.lnumdiff
				line.LnumOld = lold
				line.LnumNew = lnew
				lold++
				lnew++
			case tokenAddedLine:
				line.Type = LineAdded
				line.LnumDiff = p.lnumdiff
				line.LnumNew = lnew
				lnew++
			case tokenDeletedLine:
				line.Type = LineDeleted
				line.LnumDiff = p.lnumdiff
				line.LnumOld = lold
				lold++
			}
			hunk.Lines = append(hunk.Lines, line)
		case tokenNoNewlineAtEOF:
			// skip \ No newline at end of file. just consume line
			readline(p.r)
		default:
			break endhunk
		}
	}
	p.lnumdiff++ // count up by an additional hunk
	return hunk, nil
}

func (p *hunkParser) done(lold, lnew int, hr *hunkrange) bool {
	end := (lold >= hr.lold+hr.sold && lnew >= hr.lnew+hr.snew)
	if b, err := p.r.Peek(1); err != nil || (string(b) != tokenNoNewlineAtEOF && end) {
		return true
	}
	return false
}

// @@ -l,s +l,s @@ optional section heading
type hunkrange struct {
	lold, sold, lnew, snew int
	section                string
}

// @@ -lold[,sold] +lnew[,snew] @@[ section]
// 0  1              2            3   4
func parseHunkRange(rangeline string) (*hunkrange, error) {
	ps := strings.SplitN(rangeline, " ", 5)
	invalidErr := &ErrInvalidHunkRange{invalid: rangeline}
	hunkrange := &hunkrange{}
	if len(ps) < 4 || ps[0] != "@@" || ps[3] != "@@" {
		return nil, invalidErr
	}
	old := ps[1] // -lold[,sold]
	if !strings.HasPrefix(old, "-") {
		return nil, invalidErr
	}
	lold, sold, err := parseLS(old[1:])
	if err != nil {
		return nil, invalidErr
	}
	hunkrange.lold = lold
	hunkrange.sold = sold
	new := ps[2] // +lnew[,snew]
	if !strings.HasPrefix(new, "+") {
		return nil, invalidErr
	}
	lnew, snew, err := parseLS(new[1:])
	if err != nil {
		return nil, invalidErr
	}
	hunkrange.lnew = lnew
	hunkrange.snew = snew
	if len(ps) == 5 {
		hunkrange.section = ps[4]
	}
	return hunkrange, nil
}

// l[,s]
func parseLS(ls string) (l, s int, err error) {
	ss := strings.SplitN(ls, ",", 2)
	l, err = strconv.Atoi(ss[0])
	if err != nil {
		return 0, 0, err
	}
	if len(ss) == 2 {
		s, err = strconv.Atoi(ss[1])
		if err != nil {
			return 0, 0, err
		}
	} else {
		s = 1
	}
	return l, s, nil
}

// readline reads lines from bufio.Reader with size limit. It consumes
// remaining content even if the line size reaches size limit.
func readline(r *bufio.Reader) (string, error) {
	line, isPrefix, err := r.ReadLine()
	if err != nil {
		return "", err
	}
	// consume all remaining line content
	for isPrefix {
		_, isPrefix, _ = r.ReadLine()
	}
	return string(line), nil
}
<|endoftext|>


<commit_before>/*

Copyright 2020 The Vouch Proxy Authors.
Use of this source code is governed by The MIT License (MIT) that
can be found in the LICENSE file. Software distributed under The
MIT License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
OR CONDITIONS OF ANY KIND, either express or implied.

*/

package handlers

import (
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/vouch/vouch-proxy/pkg/cfg"
)

func Test_normalizeLoginURL(t *testing.T) {
	setUp("/config/testing/handler_login_url.yml")
	tests := []struct {
		name    string
		url     string
		want    string
		wantErr bool
	}{
		// This is not an RFC-compliant URL because it does not encode :// in the url param; we accept it anyway
		{"extra params", "http://host/login?url=http://host/path&p2=2", "http://host/path?p2=2", false},
		// This is not an RFC-compliant URL because it does not encode :// in the url param; we accept it anyway
		// Even though the p1 param is not a login param, we do not interpret is as part of the url param because it precedes it
		{"prior params", "http://host/login?p1=1&url=http://host/path", "http://host/path", true},
		// This is not an RFC-compliant URL because it does not encode :// in the url param; we accept it anyway
		// We assume vouch-* is a login param and do not fold it into url
		{"vouch-* params", "http://host/login?url=http://host/path&vouch-xxx=2", "http://host/path", false},
		// This is not an RFC-compliant URL because it does not encode :// in the url param; we accept it anyway
		// We assume x-vouch-* is a login param and do not fold it into url
		{"x-vouch-* params", "http://host/login?url=http://host/path&vouch-xxx=2", "http://host/path", false},
		// This is not an RFC-compliant URL because it does not encode :// in the url param; we accept it anyway
		// Even though p1 is not a login param, we do not interpret is as part of url because it follows a login param (vouch-*)
		{"params after vouch-* params", "http://host/login?url=http://host/path&vouch-xxx=2&p3=3", "http://host/path", true},
		// This is not an RFC-compliant URL because it does not encode :// in the url param; we accept it anyway
		// Even though p1 is not a login param, we do not interpret is as part of url because it follows a login param (x-vouch-*)
		{"params after x-vouch-* params", "http://host/login?url=http://host/path&x-vouch-xxx=2&p3=3", "http://host/path", true},
		// This is not an RFC-compliant URL; it combines all the aspects above
		{"all params", "http://host/login?p1=1&url=http://host/path?p2=2&p3=3&x-vouch-xxx=4&p5=5", "http://host/path?p2=2&p3=3", true},
		// This is an RFC-compliant URL
		{"all params", "http://host/login?p1=1&url=http%3a%2f%2fhost/path%3fp2=2%26p3=3&x-vouch-xxx=4&p5=5", "http://host/path?p2=2&p3=3", true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			u, _ := url.Parse(tt.url)
			got, err := normalizeLoginURLParam(u)
			if got.String() != tt.want {
				t.Errorf("normalizeLoginURLParam() = %v, want %v", got, tt.want)
			}
			if (err != nil) != tt.wantErr {
				t.Errorf("normalizeLoginURLParam() err = %v", err)
			}
		})
	}
}

func Test_getValidRequestedURL(t *testing.T) {
	setUp("/config/testing/handler_login_url.yml")
	r := &http.Request{}
	tests := []struct {
		name    string
		url     string
		want    string
		wantErr bool
	}{
		{"no https", "example.com/dest", "", true},
		{"redirection chaining", "http://example.com/dest?url=https://", "", true},
		{"redirection chaining upper case", "http://example.com/dest?url=HTTPS://someplaceelse.com", "", true},
		{"redirection chaining no protocol", "http://example.com/dest?url=//someplaceelse.com", "", true},
		{"data uri", "http://example.com/dest?url=data:text/plain,Example+Text", "", true},
		{"javascript uri", "http://example.com/dest?url=javascript:alert(1)", "", true},
		{"not in domain", "http://somewherelse.com/", "", true},
		{"should warn", "https://example.com/", "https://example.com/", false},
		{"should be fine", "http://example.com/", "http://example.com/", false},
		{"multiple query param", "http://example.com/?strange=but-true&also-strange=but-false", "http://example.com/?strange=but-true&also-strange=but-false", false},
		{"multiple query params, one of them bad", "http://example.com/?strange=but-true&also-strange=but-false&strange-but-bad=https://badandstrange.com", "", true},

		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r.URL, _ = url.Parse("http://vouch.example.com/login?url=" + tt.url)
			got, err := getValidRequestedURL(r)
			if (err != nil) != tt.wantErr {
				t.Errorf("getValidRequestedURL() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if got != tt.want {
				t.Errorf("getValidRequestedURL() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestLoginHandler(t *testing.T) {
	handler := http.HandlerFunc(LoginHandler)

	tests := []struct {
		name       string
		configFile string
		wantcode   int
	}{
		{"general test", "/config/testing/handler_login_url.yml", http.StatusFound},
		{"general test", "/config/testing/handler_login_redirecturls.yml", http.StatusFound},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			setUp(tt.configFile)

			req, err := http.NewRequest("GET", "/logout?url=http://myapp.example.com/login", nil)
			if err != nil {
				t.Fatal(err)
			}
			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			if rr.Code != tt.wantcode {
				t.Errorf("LogoutHandler() status = %v, want %v", rr.Code, tt.wantcode)
			}

			// confirm the OAuthClient has a properly configured
			redirectURL, err := url.Parse(rr.Header()["Location"][0])
			if err != nil {
				t.Fatal(err)
			}
			redirectParam := redirectURL.Query().Get("redirect_uri")
			assert.NotEmpty(t, cfg.OAuthClient.RedirectURL, "cfg.OAuthClient.RedirectURL is empty")
			assert.NotEmpty(t, redirectParam, "redirect_uri should not be empty when redirected to google oauth")

		})
	}
}
<commit_msg>Unambiguous test name<commit_after>/*

Copyright 2020 The Vouch Proxy Authors.
Use of this source code is governed by The MIT License (MIT) that
can be found in the LICENSE file. Software distributed under The
MIT License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
OR CONDITIONS OF ANY KIND, either express or implied.

*/

package handlers

import (
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/vouch/vouch-proxy/pkg/cfg"
)

func Test_normalizeLoginURL(t *testing.T) {
	setUp("/config/testing/handler_login_url.yml")
	tests := []struct {
		name    string
		url     string
		want    string
		wantErr bool
	}{
		// This is not an RFC-compliant URL because it does not encode :// in the url param; we accept it anyway
		{"extra params", "http://host/login?url=http://host/path&p2=2", "http://host/path?p2=2", false},
		// This is not an RFC-compliant URL because it does not encode :// in the url param; we accept it anyway
		// Even though the p1 param is not a login param, we do not interpret is as part of the url param because it precedes it
		{"prior params", "http://host/login?p1=1&url=http://host/path", "http://host/path", true},
		// This is not an RFC-compliant URL because it does not encode :// in the url param; we accept it anyway
		// We assume vouch-* is a login param and do not fold it into url
		{"vouch-* params", "http://host/login?url=http://host/path&vouch-xxx=2", "http://host/path", false},
		// This is not an RFC-compliant URL because it does not encode :// in the url param; we accept it anyway
		// We assume x-vouch-* is a login param and do not fold it into url
		{"x-vouch-* params", "http://host/login?url=http://host/path&vouch-xxx=2", "http://host/path", false},
		// This is not an RFC-compliant URL because it does not encode :// in the url param; we accept it anyway
		// Even though p1 is not a login param, we do not interpret is as part of url because it follows a login param (vouch-*)
		{"params after vouch-* params", "http://host/login?url=http://host/path&vouch-xxx=2&p3=3", "http://host/path", true},
		// This is not an RFC-compliant URL because it does not encode :// in the url param; we accept it anyway
		// Even though p1 is not a login param, we do not interpret is as part of url because it follows a login param (x-vouch-*)
		{"params after x-vouch-* params", "http://host/login?url=http://host/path&x-vouch-xxx=2&p3=3", "http://host/path", true},
		// This is not an RFC-compliant URL; it combines all the aspects above
		{"all params", "http://host/login?p1=1&url=http://host/path?p2=2&p3=3&x-vouch-xxx=4&p5=5", "http://host/path?p2=2&p3=3", true},
		// This is an RFC-compliant URL
		{"all params (encoded)", "http://host/login?p1=1&url=http%3a%2f%2fhost/path%3fp2=2%26p3=3&x-vouch-xxx=4&p5=5", "http://host/path?p2=2&p3=3", true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			u, _ := url.Parse(tt.url)
			got, err := normalizeLoginURLParam(u)
			if got.String() != tt.want {
				t.Errorf("normalizeLoginURLParam() = %v, want %v", got, tt.want)
			}
			if (err != nil) != tt.wantErr {
				t.Errorf("normalizeLoginURLParam() err = %v", err)
			}
		})
	}
}

func Test_getValidRequestedURL(t *testing.T) {
	setUp("/config/testing/handler_login_url.yml")
	r := &http.Request{}
	tests := []struct {
		name    string
		url     string
		want    string
		wantErr bool
	}{
		{"no https", "example.com/dest", "", true},
		{"redirection chaining", "http://example.com/dest?url=https://", "", true},
		{"redirection chaining upper case", "http://example.com/dest?url=HTTPS://someplaceelse.com", "", true},
		{"redirection chaining no protocol", "http://example.com/dest?url=//someplaceelse.com", "", true},
		{"data uri", "http://example.com/dest?url=data:text/plain,Example+Text", "", true},
		{"javascript uri", "http://example.com/dest?url=javascript:alert(1)", "", true},
		{"not in domain", "http://somewherelse.com/", "", true},
		{"should warn", "https://example.com/", "https://example.com/", false},
		{"should be fine", "http://example.com/", "http://example.com/", false},
		{"multiple query param", "http://example.com/?strange=but-true&also-strange=but-false", "http://example.com/?strange=but-true&also-strange=but-false", false},
		{"multiple query params, one of them bad", "http://example.com/?strange=but-true&also-strange=but-false&strange-but-bad=https://badandstrange.com", "", true},

		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r.URL, _ = url.Parse("http://vouch.example.com/login?url=" + tt.url)
			got, err := getValidRequestedURL(r)
			if (err != nil) != tt.wantErr {
				t.Errorf("getValidRequestedURL() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if got != tt.want {
				t.Errorf("getValidRequestedURL() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestLoginHandler(t *testing.T) {
	handler := http.HandlerFunc(LoginHandler)

	tests := []struct {
		name       string
		configFile string
		wantcode   int
	}{
		{"general test", "/config/testing/handler_login_url.yml", http.StatusFound},
		{"general test", "/config/testing/handler_login_redirecturls.yml", http.StatusFound},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			setUp(tt.configFile)

			req, err := http.NewRequest("GET", "/logout?url=http://myapp.example.com/login", nil)
			if err != nil {
				t.Fatal(err)
			}
			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			if rr.Code != tt.wantcode {
				t.Errorf("LogoutHandler() status = %v, want %v", rr.Code, tt.wantcode)
			}

			// confirm the OAuthClient has a properly configured
			redirectURL, err := url.Parse(rr.Header()["Location"][0])
			if err != nil {
				t.Fatal(err)
			}
			redirectParam := redirectURL.Query().Get("redirect_uri")
			assert.NotEmpty(t, cfg.OAuthClient.RedirectURL, "cfg.OAuthClient.RedirectURL is empty")
			assert.NotEmpty(t, redirectParam, "redirect_uri should not be empty when redirected to google oauth")

		})
	}
}
<|endoftext|>


<commit_before>// Copyright © 2014 Daniele Tricoli <eriol@mornie.org>.
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package db

import (
	"database/sql"
	"fmt"
	"log"
	"os"
	"strings"

	_ "github.com/mattn/go-sqlite3"
)

type Store struct {
	db *sql.DB
}

func (s *Store) Open(database string) error {
	db, err := sql.Open("sqlite3", database)
	if err != nil {
		log.Fatal(err)
	}
	s.db = db

	if _, err := os.Stat(database); err != nil {
		if os.IsNotExist(err) {
			s.createDatabase()
		}
	}

	return nil
}

func (s *Store) Close() {
	s.db.Close()
}

func (s *Store) createDatabase() {

	// TODO: create all tables in one variable when multiple statements will
	// be supported see https://github.com/mattn/go-sqlite3/issues/60
	sql_people_table := `CREATE TABLE people (
	id INTEGER NOT NULL PRIMARY KEY autoincrement,
	name TEXT COLLATE NOCASE
);`

	sql_quotes_table := `CREATE TABLE quotes (
	id INTEGER NOT NULL PRIMARY KEY autoincrement,
	person_id INTEGER NOT NULL,
	quote TEXT COLLATE NOCASE,
	FOREIGN KEY(person_id) REFERENCES people(id)
);`

	sql_alias_table := `CREATE TABLE alias (
	person_id INTEGER NOT NULL,
	alias TEXT COLLATE NOCASE,
	FOREIGN KEY(person_id) REFERENCES people(id)
);`

	sql_channels_table := `CREATE TABLE channels (
	id INTEGER NOT NULL PRIMARY KEY autoincrement,
	name TEXT COLLATE NOCASE
);`
	// quotes_acl table connects a quote with a specific channel so
	// that the quote will be quoted only in that specific channel.
	// Quotes not present in this table are public (quoted in every
	// channel where the bot joined)
	sql_quotes_acl_table := `CREATE TABLE quotes_acl (
	quote_id INTEGER NOT NULL,
	channel_id INTEGER NOT NULL,
	FOREIGN KEY(quote_id) REFERENCES quotes(id),
	FOREIGN KEY(channel_id) REFERENCES channels(id)
);`

	tables := [5]string{
		sql_people_table,
		sql_quotes_table,
		sql_alias_table,
		sql_channels_table,
		sql_quotes_acl_table,
	}

	for _, table := range tables {
		_, err := s.db.Exec(table)

		if err != nil {
			log.Fatal(err)
		}
	}
}

func (s *Store) getPerson(name string) (id int) {
	query_people := `SELECT id FROM people WHERE name = ?`
	query_alias := `SELECT person_id FROM alias WHERE alias = ?`

	s.db.QueryRow(query_people, name).Scan(&id)
	if id == 0 {
		s.db.QueryRow(query_alias, name).Scan(&id)
	}

	return
}

func (s *Store) getChannel(name string) (id int) {
	query := `SELECT id FROM channels WHERE name = ?`

	s.db.QueryRow(query, name).Scan(&id)

	return
}

func (s *Store) GetQuote(person, channel string) (quote string) {
	query := `SELECT quote FROM quotes WHERE person_id = ?
		AND id NOT IN (
			SELECT quote_id FROM quotes_acl
			EXCEPT
			SELECT quote_id FROM quotes_acl WHERE channel_id = ?)
		ORDER BY RANDOM() LIMIT 1;`

	s.db.QueryRow(
		query,
		s.getPerson(person),
		s.getChannel(channel)).Scan(&quote)

	return
}

func (s *Store) GetQuoteAbout(person, argument, channel string) (quote string) {
	// A double quote can't be present in argument because of the
	// regex used but removing anyway
	argument = strings.Replace(argument, "\"", "", -1)

	query := "SELECT quote FROM quotes WHERE person_id = ? " +
		"AND quote LIKE \"%%%s%%\" " +
		"AND id NOT IN ( " +
		"SELECT quote_id FROM quotes_acl " +
		"EXCEPT " +
		"SELECT quote_id FROM quotes_acl WHERE channel_id = ?) " +
		"ORDER BY RANDOM() LIMIT 1;"
	query = fmt.Sprintf(query, argument)

	s.db.QueryRow(
		query,
		s.getPerson(person),
		s.getChannel(channel)).Scan(&quote)

	return
}
<commit_msg>Create database tables all at once since go-sqlite3 execer/queryer is implemented now<commit_after>// Copyright © 2014 Daniele Tricoli <eriol@mornie.org>.
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package db

import (
	"database/sql"
	"fmt"
	"log"
	"os"
	"strings"

	_ "github.com/mattn/go-sqlite3"
)

type Store struct {
	db *sql.DB
}

func (s *Store) Open(database string) error {
	db, err := sql.Open("sqlite3", database)
	if err != nil {
		log.Fatal(err)
	}
	s.db = db

	if _, err := os.Stat(database); err != nil {
		if os.IsNotExist(err) {
			s.createDatabase()
		}
	}

	return nil
}

func (s *Store) Close() {
	s.db.Close()
}

func (s *Store) createDatabase() {

	// quotes_acl table connects a quote with a specific channel so
	// that the quote will be quoted only in that specific channel.
	// Quotes not present in this table are public (quoted in every
	// channel where the bot joined)
	tables := `
	CREATE TABLE people (
	    id INTEGER NOT NULL PRIMARY KEY autoincrement,
	    name TEXT COLLATE NOCASE
	);

	CREATE TABLE quotes (
	    id INTEGER NOT NULL PRIMARY KEY autoincrement,
	    person_id INTEGER NOT NULL,
	    quote TEXT COLLATE NOCASE,
	    FOREIGN KEY(person_id) REFERENCES people(id)
	);

	CREATE TABLE alias (
	    person_id INTEGER NOT NULL,
	    alias TEXT COLLATE NOCASE,
	    FOREIGN KEY(person_id) REFERENCES people(id)
    );

	CREATE TABLE channels (
	    id INTEGER NOT NULL PRIMARY KEY autoincrement,
	    name TEXT COLLATE NOCASE
	);

	CREATE TABLE quotes_acl (
	    quote_id INTEGER NOT NULL,
	    channel_id INTEGER NOT NULL,
	    FOREIGN KEY(quote_id) REFERENCES quotes(id),
	    FOREIGN KEY(channel_id) REFERENCES channels(id)
	);`

	_, err := s.db.Exec(tables)

	if err != nil {
		log.Fatal(err)
	}
}

func (s *Store) getPerson(name string) (id int) {
	query_people := `SELECT id FROM people WHERE name = ?`
	query_alias := `SELECT person_id FROM alias WHERE alias = ?`

	s.db.QueryRow(query_people, name).Scan(&id)
	if id == 0 {
		s.db.QueryRow(query_alias, name).Scan(&id)
	}

	return
}

func (s *Store) getChannel(name string) (id int) {
	query := `SELECT id FROM channels WHERE name = ?`

	s.db.QueryRow(query, name).Scan(&id)

	return
}

func (s *Store) GetQuote(person, channel string) (quote string) {
	query := `SELECT quote FROM quotes WHERE person_id = ?
		AND id NOT IN (
			SELECT quote_id FROM quotes_acl
			EXCEPT
			SELECT quote_id FROM quotes_acl WHERE channel_id = ?)
		ORDER BY RANDOM() LIMIT 1;`

	s.db.QueryRow(
		query,
		s.getPerson(person),
		s.getChannel(channel)).Scan(&quote)

	return
}

func (s *Store) GetQuoteAbout(person, argument, channel string) (quote string) {
	// A double quote can't be present in argument because of the
	// regex used but removing anyway
	argument = strings.Replace(argument, "\"", "", -1)

	query := "SELECT quote FROM quotes WHERE person_id = ? " +
		"AND quote LIKE \"%%%s%%\" " +
		"AND id NOT IN ( " +
		"SELECT quote_id FROM quotes_acl " +
		"EXCEPT " +
		"SELECT quote_id FROM quotes_acl WHERE channel_id = ?) " +
		"ORDER BY RANDOM() LIMIT 1;"
	query = fmt.Sprintf(query, argument)

	s.db.QueryRow(
		query,
		s.getPerson(person),
		s.getChannel(channel)).Scan(&quote)

	return
}
<|endoftext|>


<commit_before>package bencode

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"reflect"
	"strconv"
	"strings"
)

var (
	reflectByteSliceType = reflect.TypeOf([]byte(nil))
	reflectStringType    = reflect.TypeOf("")
)

// Unmarshaler is the interface implemented by types that can unmarshal
// a bencode description of themselves.
// The input can be assumed to be a valid encoding of a bencode value.
// UnmarshalBencode must copy the bencode data if it wishes to retain the data after returning.
type Unmarshaler interface {
	UnmarshalBencode([]byte) error
}

//A Decoder reads and decodes bencoded data from an input stream.
type Decoder struct {
	r   *bufio.Reader
	raw bool
	buf []byte
	n   int
}

//BytesParsed returns the number of bytes that have actually been parsed
func (d *Decoder) BytesParsed() int {
	return d.n
}

//read also writes into the buffer when d.raw is set.
func (d *Decoder) read(p []byte) (n int, err error) {
	n, err = d.r.Read(p)
	if d.raw {
		d.buf = append(d.buf, p[:n]...)
	}
	d.n += n
	return
}

//readBytes also writes into the buffer when d.raw is set.
func (d *Decoder) readBytes(delim byte) (line []byte, err error) {
	line, err = d.r.ReadBytes(delim)
	if d.raw {
		d.buf = append(d.buf, line...)
	}
	d.n += len(line)
	return
}

//readByte also writes into the buffer when d.raw is set.
func (d *Decoder) readByte() (b byte, err error) {
	b, err = d.r.ReadByte()
	if d.raw {
		d.buf = append(d.buf, b)
	}
	d.n += 1
	return
}

//readFull also writes into the buffer when d.raw is set.
func (d *Decoder) readFull(p []byte) (n int, err error) {
	n, err = io.ReadFull(d.r, p)
	if d.raw {
		d.buf = append(d.buf, p[:n]...)
	}
	d.n += n
	return
}

func (d *Decoder) peekByte() (b byte, err error) {
	ch, err := d.r.Peek(1)
	if err != nil {
		return
	}
	b = ch[0]
	return
}

//NewDecoder returns a new decoder that reads from r
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{r: bufio.NewReader(r)}
}

//Decode reads the bencoded value from its input and stores it in the value pointed to by val.
//Decode allocates maps/slices as necessary with the following additional rules:
//To decode a bencoded value into a nil interface value, the type stored in the interface value is one of:
//	int64 for bencoded integers
//	string for bencoded strings
//	[]interface{} for bencoded lists
//	map[string]interface{} for bencoded dicts
func (d *Decoder) Decode(val interface{}) error {
	rv := reflect.ValueOf(val)
	if rv.Kind() != reflect.Ptr || rv.IsNil() {
		return errors.New("Unwritable type passed into decode")
	}

	return d.decodeInto(rv)
}

//DecodeString reads the data in the string and stores it into the value pointed to by val.
//Read the docs for Decode for more information.
func DecodeString(in string, val interface{}) error {
	buf := strings.NewReader(in)
	d := NewDecoder(buf)
	return d.Decode(val)
}

//DecodeBytes reads the data in b and stores it into the value pointed to by val.
//Read the docs for Decode for more information.
func DecodeBytes(b []byte, val interface{}) error {
	r := bytes.NewReader(b)
	d := NewDecoder(r)
	return d.Decode(val)
}

func indirect(v reflect.Value, alloc bool) reflect.Value {
	for {
		switch v.Kind() {
		case reflect.Interface:
			if v.IsNil() {
				if !alloc {
					return reflect.Value{}
				}
				return v
			}

		case reflect.Ptr:
			if v.IsNil() {
				if !alloc {
					return reflect.Value{}
				}
				v.Set(reflect.New(v.Type().Elem()))
			}

		default:
			return v
		}

		v = v.Elem()
	}
}

func (d *Decoder) decodeInto(val reflect.Value) (err error) {
	v := indirect(val, true)

	// if we're decoding into an Unmarshaler,
	// we pass on the next bencode value to this value instead,
	// so it can decide what to do with it.
	unmarshaler, ok := val.Interface().(Unmarshaler)
	if !ok && val.CanAddr() {
		unmarshaler, ok = val.Addr().Interface().(Unmarshaler)
	}
	if ok {
		var x RawMessage
		if err := d.decodeInto(reflect.ValueOf(&x)); err != nil {
			return err
		}
		return unmarshaler.UnmarshalBencode([]byte(x))
	}

	//if we're decoding into a RawMessage set raw to true for the rest of
	//the call stack, and switch out the value with an interface{}.
	if _, ok := v.Interface().(RawMessage); ok && !d.raw {
		var x interface{}
		v = reflect.ValueOf(&x).Elem()

		//set d.raw for the lifetime of this function call, and set the raw
		//message when the function is exiting.
		d.buf = d.buf[:0]
		d.raw = true
		defer func() {
			d.raw = false
			v := indirect(val, true)
			v.SetBytes(append([]byte(nil), d.buf...))
		}()
	}

	next, err := d.peekByte()
	if err != nil {
		return
	}

	switch next {
	case 'i':
		err = d.decodeInt(v)
	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
		err = d.decodeString(v)
	case 'l':
		err = d.decodeList(v)
	case 'd':
		err = d.decodeDict(v)
	default:
		err = errors.New("Invalid input")
	}

	return
}

func (d *Decoder) decodeInt(v reflect.Value) error {
	//we need to read an i, some digits, and an e.
	ch, err := d.readByte()
	if err != nil {
		return err
	}
	if ch != 'i' {
		panic("got not an i when peek returned an i")
	}

	line, err := d.readBytes('e')
	if err != nil {
		return err
	}

	digits := string(line[:len(line)-1])

	switch v.Kind() {
	default:
		return fmt.Errorf("Cannot store int64 into %s", v.Type())
	case reflect.Interface:
		n, err := strconv.ParseInt(digits, 10, 64)
		if err != nil {
			return err
		}
		v.Set(reflect.ValueOf(n))
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		n, err := strconv.ParseInt(digits, 10, 64)
		if err != nil {
			return err
		}
		v.SetInt(n)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		n, err := strconv.ParseUint(digits, 10, 64)
		if err != nil {
			return err
		}
		v.SetUint(n)
	case reflect.Bool:
		n, err := strconv.ParseUint(digits, 10, 64)
		if err != nil {
			return err
		}
		v.SetBool(n != 0)
	}

	return nil
}

func (d *Decoder) decodeString(v reflect.Value) error {
	//read until a colon to get the number of digits to read after
	line, err := d.readBytes(':')
	if err != nil {
		return err
	}

	//parse it into an int for making a slice
	l32, err := strconv.ParseInt(string(line[:len(line)-1]), 10, 32)
	l := int(l32)
	if err != nil {
		return err
	}
	if l < 0 {
		return fmt.Errorf("invalid negative string length: %d", l)
	}

	//read exactly l bytes out and make our string
	buf := make([]byte, l)
	_, err = d.readFull(buf)
	if err != nil {
		return err
	}

	switch v.Kind() {
	default:
		return fmt.Errorf("Cannot store string into %s", v.Type())
	case reflect.Slice:
		if v.Type() != reflectByteSliceType {
			return fmt.Errorf("Cannot store string into %s", v.Type())
		}
		v.SetBytes(buf)
	case reflect.String:
		v.SetString(string(buf))
	case reflect.Interface:
		v.Set(reflect.ValueOf(string(buf)))
	}
	return nil
}

func (d *Decoder) decodeList(v reflect.Value) error {
	//if we have an interface, just put a []interface{} in it!
	if v.Kind() == reflect.Interface {
		var x []interface{}
		defer func(p reflect.Value) { p.Set(v) }(v)
		v = reflect.ValueOf(&x).Elem()
	}

	if v.Kind() != reflect.Array && v.Kind() != reflect.Slice {
		return fmt.Errorf("Cant store a []interface{} into %s", v.Type())
	}

	//read out the l that prefixes the list
	ch, err := d.readByte()
	if err != nil {
		return err
	}
	if ch != 'l' {
		panic("got something other than a list head after a peek")
	}

	for i := 0; ; i++ {
		//peek for the end token and read it out
		ch, err := d.peekByte()
		if err != nil {
			return err
		}
		switch ch {
		case 'e':
			_, err := d.readByte() //consume the end
			return err
		}

		//grow it if required
		if i >= v.Cap() && v.IsValid() {
			newcap := v.Cap() + v.Cap()/2
			if newcap < 4 {
				newcap = 4
			}
			newv := reflect.MakeSlice(v.Type(), v.Len(), newcap)
			reflect.Copy(newv, v)
			v.Set(newv)
		}

		//reslice into cap (its a slice now since it had to have grown)
		if i >= v.Len() && v.IsValid() {
			v.SetLen(i + 1)
		}

		//decode a value into the index
		if err := d.decodeInto(v.Index(i)); err != nil {
			return err
		}
	}

	panic("unreachable")
}

func (d *Decoder) decodeDict(v reflect.Value) error {
	//if we have an interface{}, just put a map[string]interface{} in it!
	if v.Kind() == reflect.Interface {
		var x map[string]interface{}
		defer func(p reflect.Value) { p.Set(v) }(v)
		v = reflect.ValueOf(&x).Elem()
	}

	//consume the head token
	ch, err := d.readByte()
	if err != nil {
		return err
	}
	if ch != 'd' {
		panic("got an incorrect token when it was checked already")
	}

	//check for correct type
	var (
		mapElem reflect.Value
		isMap   bool
		vals    map[string]reflect.Value
	)

	switch v.Kind() {
	case reflect.Map:
		t := v.Type()
		if t.Key() != reflectStringType {
			return fmt.Errorf("Can't store a map[string]interface{} into %s", v.Type())
		}
		if v.IsNil() {
			v.Set(reflect.MakeMap(t))
		}

		isMap = true
		mapElem = reflect.New(t.Elem()).Elem()
	case reflect.Struct:
		vals = make(map[string]reflect.Value)
		setStructValues(vals, v)
	default:
		return fmt.Errorf("Can't store a map[string]interface{} into %s", v.Type())
	}

	for {
		var subv reflect.Value

		//peek the next value type
		ch, err := d.peekByte()
		if err != nil {
			return err
		}
		if ch == 'e' {
			_, err = d.readByte() //consume the end token
			return err
		}

		//peek the next value we're suppsed to read
		var key string
		if err := d.decodeString(reflect.ValueOf(&key).Elem()); err != nil {
			return err
		}

		if isMap {
			mapElem.Set(reflect.Zero(v.Type().Elem()))
			subv = mapElem
		} else {
			subv = vals[key]
		}

		if !subv.IsValid() {
			//if it's invalid, grab but ignore the next value
			var x interface{}
			err := d.decodeInto(reflect.ValueOf(&x).Elem())
			if err != nil {
				return err
			}

			continue
		}

		//subv now contains what we load into
		if err := d.decodeInto(subv); err != nil {
			return err
		}

		if isMap {
			v.SetMapIndex(reflect.ValueOf(key), subv)
		}

	}

	panic("unreachable")
}

func setStructValues(m map[string]reflect.Value, v reflect.Value) {
	if t := v.Type(); t.Kind() == reflect.Struct {
		for i := 0; i < v.NumField(); i++ {
			f := t.Field(i)
			if f.PkgPath != "" {
				continue
			}
			v := v.FieldByIndex(f.Index)
			if f.Anonymous && f.Tag == "" {
				setStructValues(m, v)
			}
		}

		// overwrite embedded struct tags and names
		for i := 0; i < v.NumField(); i++ {
			f := t.Field(i)
			if f.PkgPath != "" || f.Anonymous {
				continue
			}
			v := v.FieldByIndex(f.Index)
			name, _ := parseTag(f.Tag.Get("bencode"))
			if name == "" {
				name = f.Name
			}
			if isValidTag(name) {
				m[name] = v
			}
		}
	}
}
<commit_msg>don't parse values in raw mode<commit_after>package bencode

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"reflect"
	"strconv"
	"strings"
)

var (
	reflectByteSliceType = reflect.TypeOf([]byte(nil))
	reflectStringType    = reflect.TypeOf("")
)

// Unmarshaler is the interface implemented by types that can unmarshal
// a bencode description of themselves.
// The input can be assumed to be a valid encoding of a bencode value.
// UnmarshalBencode must copy the bencode data if it wishes to retain the data after returning.
type Unmarshaler interface {
	UnmarshalBencode([]byte) error
}

//A Decoder reads and decodes bencoded data from an input stream.
type Decoder struct {
	r   *bufio.Reader
	raw bool
	buf []byte
	n   int
}

//BytesParsed returns the number of bytes that have actually been parsed
func (d *Decoder) BytesParsed() int {
	return d.n
}

//read also writes into the buffer when d.raw is set.
func (d *Decoder) read(p []byte) (n int, err error) {
	n, err = d.r.Read(p)
	if d.raw {
		d.buf = append(d.buf, p[:n]...)
	}
	d.n += n
	return
}

//readBytes also writes into the buffer when d.raw is set.
func (d *Decoder) readBytes(delim byte) (line []byte, err error) {
	line, err = d.r.ReadBytes(delim)
	if d.raw {
		d.buf = append(d.buf, line...)
	}
	d.n += len(line)
	return
}

//readByte also writes into the buffer when d.raw is set.
func (d *Decoder) readByte() (b byte, err error) {
	b, err = d.r.ReadByte()
	if d.raw {
		d.buf = append(d.buf, b)
	}
	d.n += 1
	return
}

//readFull also writes into the buffer when d.raw is set.
func (d *Decoder) readFull(p []byte) (n int, err error) {
	n, err = io.ReadFull(d.r, p)
	if d.raw {
		d.buf = append(d.buf, p[:n]...)
	}
	d.n += n
	return
}

func (d *Decoder) peekByte() (b byte, err error) {
	ch, err := d.r.Peek(1)
	if err != nil {
		return
	}
	b = ch[0]
	return
}

//NewDecoder returns a new decoder that reads from r
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{r: bufio.NewReader(r)}
}

//Decode reads the bencoded value from its input and stores it in the value pointed to by val.
//Decode allocates maps/slices as necessary with the following additional rules:
//To decode a bencoded value into a nil interface value, the type stored in the interface value is one of:
//	int64 for bencoded integers
//	string for bencoded strings
//	[]interface{} for bencoded lists
//	map[string]interface{} for bencoded dicts
func (d *Decoder) Decode(val interface{}) error {
	rv := reflect.ValueOf(val)
	if rv.Kind() != reflect.Ptr || rv.IsNil() {
		return errors.New("Unwritable type passed into decode")
	}

	return d.decodeInto(rv)
}

//DecodeString reads the data in the string and stores it into the value pointed to by val.
//Read the docs for Decode for more information.
func DecodeString(in string, val interface{}) error {
	buf := strings.NewReader(in)
	d := NewDecoder(buf)
	return d.Decode(val)
}

//DecodeBytes reads the data in b and stores it into the value pointed to by val.
//Read the docs for Decode for more information.
func DecodeBytes(b []byte, val interface{}) error {
	r := bytes.NewReader(b)
	d := NewDecoder(r)
	return d.Decode(val)
}

func indirect(v reflect.Value, alloc bool) reflect.Value {
	for {
		switch v.Kind() {
		case reflect.Interface:
			if v.IsNil() {
				if !alloc {
					return reflect.Value{}
				}
				return v
			}

		case reflect.Ptr:
			if v.IsNil() {
				if !alloc {
					return reflect.Value{}
				}
				v.Set(reflect.New(v.Type().Elem()))
			}

		default:
			return v
		}

		v = v.Elem()
	}
}

func (d *Decoder) decodeInto(val reflect.Value) (err error) {
	var v reflect.Value
	if d.raw {
		v = val
	} else {
		v = indirect(val, true)

		// if we're decoding into an Unmarshaler,
		// we pass on the next bencode value to this value instead,
		// so it can decide what to do with it.
		unmarshaler, ok := val.Interface().(Unmarshaler)
		if !ok && val.CanAddr() {
			unmarshaler, ok = val.Addr().Interface().(Unmarshaler)
		}
		if ok {
			var x RawMessage
			if err := d.decodeInto(reflect.ValueOf(&x)); err != nil {
				return err
			}
			return unmarshaler.UnmarshalBencode([]byte(x))
		}

		//if we're decoding into a RawMessage set raw to true for the rest of
		//the call stack, and switch out the value with an interface{}.
		if _, ok := v.Interface().(RawMessage); ok && !d.raw {
			v = reflect.Value{} // explicitly make v invalid

			//set d.raw for the lifetime of this function call, and set the raw
			//message when the function is exiting.
			d.buf = d.buf[:0]
			d.raw = true
			defer func() {
				d.raw = false
				v := indirect(val, true)
				v.SetBytes(append([]byte(nil), d.buf...))
			}()
		}
	}

	next, err := d.peekByte()
	if err != nil {
		return
	}

	switch next {
	case 'i':
		err = d.decodeInt(v)
	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
		err = d.decodeString(v)
	case 'l':
		err = d.decodeList(v)
	case 'd':
		err = d.decodeDict(v)
	default:
		err = errors.New("Invalid input")
	}

	return
}

func (d *Decoder) decodeInt(v reflect.Value) error {
	//we need to read an i, some digits, and an e.
	ch, err := d.readByte()
	if err != nil {
		return err
	}
	if ch != 'i' {
		panic("got not an i when peek returned an i")
	}

	line, err := d.readBytes('e')
	if err != nil || d.raw {
		return err
	}

	digits := string(line[:len(line)-1])

	switch v.Kind() {
	default:
		return fmt.Errorf("Cannot store int64 into %s", v.Type())
	case reflect.Interface:
		n, err := strconv.ParseInt(digits, 10, 64)
		if err != nil {
			return err
		}
		v.Set(reflect.ValueOf(n))
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		n, err := strconv.ParseInt(digits, 10, 64)
		if err != nil {
			return err
		}
		v.SetInt(n)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		n, err := strconv.ParseUint(digits, 10, 64)
		if err != nil {
			return err
		}
		v.SetUint(n)
	case reflect.Bool:
		n, err := strconv.ParseUint(digits, 10, 64)
		if err != nil {
			return err
		}
		v.SetBool(n != 0)
	}

	return nil
}

func (d *Decoder) decodeString(v reflect.Value) error {
	//read until a colon to get the number of digits to read after
	line, err := d.readBytes(':')
	if err != nil {
		return err
	}

	//parse it into an int for making a slice
	l32, err := strconv.ParseInt(string(line[:len(line)-1]), 10, 32)
	l := int(l32)
	if err != nil {
		return err
	}
	if l < 0 {
		return fmt.Errorf("invalid negative string length: %d", l)
	}

	//read exactly l bytes out and make our string
	buf := make([]byte, l)
	_, err = d.readFull(buf)
	if err != nil || d.raw {
		return err
	}

	switch v.Kind() {
	default:
		return fmt.Errorf("Cannot store string into %s", v.Type())
	case reflect.Slice:
		if v.Type() != reflectByteSliceType {
			return fmt.Errorf("Cannot store string into %s", v.Type())
		}
		v.SetBytes(buf)
	case reflect.String:
		v.SetString(string(buf))
	case reflect.Interface:
		v.Set(reflect.ValueOf(string(buf)))
	}
	return nil
}

func (d *Decoder) decodeList(v reflect.Value) error {
	if !d.raw {
		//if we have an interface, just put a []interface{} in it!
		if v.Kind() == reflect.Interface {
			var x []interface{}
			defer func(p reflect.Value) { p.Set(v) }(v)
			v = reflect.ValueOf(&x).Elem()
		}

		if v.Kind() != reflect.Array && v.Kind() != reflect.Slice {
			return fmt.Errorf("Cant store a []interface{} into %s", v.Type())
		}
	}

	//read out the l that prefixes the list
	ch, err := d.readByte()
	if err != nil {
		return err
	}
	if ch != 'l' {
		panic("got something other than a list head after a peek")
	}

	// if we're decoding in raw mode,
	// we only want to read into the buffer,
	// without actually parsing any values
	if d.raw {
		var ch byte
		for {
			//peek for the end token and read it out
			ch, err = d.peekByte()
			if err != nil {
				return err
			}
			if ch == 'e' {
				_, err = d.readByte() //consume the end
				return err
			}

			//decode the next value
			err = d.decodeInto(v)
			if err != nil {
				return err
			}
		}
	}

	for i := 0; ; i++ {
		//peek for the end token and read it out
		ch, err := d.peekByte()
		if err != nil {
			return err
		}
		switch ch {
		case 'e':
			_, err := d.readByte() //consume the end
			return err
		}

		//grow it if required
		if i >= v.Cap() && v.IsValid() {
			newcap := v.Cap() + v.Cap()/2
			if newcap < 4 {
				newcap = 4
			}
			newv := reflect.MakeSlice(v.Type(), v.Len(), newcap)
			reflect.Copy(newv, v)
			v.Set(newv)
		}

		//reslice into cap (its a slice now since it had to have grown)
		if i >= v.Len() && v.IsValid() {
			v.SetLen(i + 1)
		}

		//decode a value into the index
		if err := d.decodeInto(v.Index(i)); err != nil {
			return err
		}
	}

	panic("unreachable")
}

func (d *Decoder) decodeDict(v reflect.Value) error {
	//if we have an interface{}, just put a map[string]interface{} in it!
	if !d.raw && v.Kind() == reflect.Interface {
		var x map[string]interface{}
		defer func(p reflect.Value) { p.Set(v) }(v)
		v = reflect.ValueOf(&x).Elem()
	}

	//consume the head token
	ch, err := d.readByte()
	if err != nil {
		return err
	}
	if ch != 'd' {
		panic("got an incorrect token when it was checked already")
	}

	if d.raw {
		// if we're decoding in raw mode,
		// we only want to read into the buffer,
		// without actually parsing any values
		for {
			//peek the next value type
			ch, err := d.peekByte()
			if err != nil {
				return err
			}
			if ch == 'e' {
				_, err = d.readByte() //consume the end token
				return err
			}

			err = d.decodeString(v)
			if err != nil {
				return err
			}

			err = d.decodeInto(v)
			if err != nil {
				return err
			}
		}
	}

	//check for correct type
	var (
		mapElem reflect.Value
		isMap   bool
		vals    map[string]reflect.Value
	)

	switch v.Kind() {
	case reflect.Map:
		t := v.Type()
		if t.Key() != reflectStringType {
			return fmt.Errorf("Can't store a map[string]interface{} into %s", v.Type())
		}
		if v.IsNil() {
			v.Set(reflect.MakeMap(t))
		}

		isMap = true
		mapElem = reflect.New(t.Elem()).Elem()
	case reflect.Struct:
		vals = make(map[string]reflect.Value)
		setStructValues(vals, v)
	default:
		return fmt.Errorf("Can't store a map[string]interface{} into %s", v.Type())
	}

	for {
		var subv reflect.Value

		//peek the next value type
		ch, err := d.peekByte()
		if err != nil {
			return err
		}
		if ch == 'e' {
			_, err = d.readByte() //consume the end token
			return err
		}

		//peek the next value we're suppsed to read
		var key string
		if err := d.decodeString(reflect.ValueOf(&key).Elem()); err != nil {
			return err
		}

		if isMap {
			mapElem.Set(reflect.Zero(v.Type().Elem()))
			subv = mapElem
		} else {
			subv = vals[key]
		}

		if !subv.IsValid() {
			//if it's invalid, grab but ignore the next value
			var x interface{}
			err := d.decodeInto(reflect.ValueOf(&x).Elem())
			if err != nil {
				return err
			}

			continue
		}

		//subv now contains what we load into
		if err := d.decodeInto(subv); err != nil {
			return err
		}

		if isMap {
			v.SetMapIndex(reflect.ValueOf(key), subv)
		}

	}

	panic("unreachable")
}

func setStructValues(m map[string]reflect.Value, v reflect.Value) {
	if t := v.Type(); t.Kind() == reflect.Struct {
		for i := 0; i < v.NumField(); i++ {
			f := t.Field(i)
			if f.PkgPath != "" {
				continue
			}
			v := v.FieldByIndex(f.Index)
			if f.Anonymous && f.Tag == "" {
				setStructValues(m, v)
			}
		}

		// overwrite embedded struct tags and names
		for i := 0; i < v.NumField(); i++ {
			f := t.Field(i)
			if f.PkgPath != "" || f.Anonymous {
				continue
			}
			v := v.FieldByIndex(f.Index)
			name, _ := parseTag(f.Tag.Get("bencode"))
			if name == "" {
				name = f.Name
			}
			if isValidTag(name) {
				m[name] = v
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2012 Andreas Louca. All rights reserved.
// Use of this source code is goverend by a BSD-style
// license that can be found in the LICENSE file.

package gosnmp

import (
	"encoding/asn1"
	"fmt"
)

type Asn1BER byte

const (
	Integer          Asn1BER = 0x02
	BitString                = 0x03
	OctetString              = 0x04
	Null                     = 0x05
	ObjectIdentifier         = 0x06
	Counter32                = 0x41
	Gauge32                  = 0x42
	TimeTicks                = 0x43
	Opaque                   = 0x44
	NsapAddress              = 0x45
	Counter64                = 0x46
	Uinteger32               = 0x47
	NoSuchObject             = 0x80
	NoSuchInstance           = 0x81
)

// Different packet structure is needed during decode, to trick encoding/asn1 to decode the SNMP packet

type Variable struct {
	Name  asn1.ObjectIdentifier
	Type  Asn1BER
	Value interface{}
}

type VarBind struct {
	Name  asn1.ObjectIdentifier
	Value asn1.RawValue
}

type PDU struct {
	RequestId   int32
	ErrorStatus int
	ErrorIndex  int
	VarBindList []VarBind
}
type PDUResponse struct {
	RequestId   int32
	ErrorStatus int
	ErrorIndex  int
	VarBindList []*Variable
}

type Message struct {
	Version   int
	Community []uint8
	Data      asn1.RawValue
}

func decode(data []byte) (*PDUResponse, error) {
	m := Message{}
	_, err := asn1.Unmarshal(data, &m)
	if err != nil {
		return nil, err
	}
	choice := m.Data.FullBytes[0]
	switch choice {
	// SNMP Response
	case 0xa0, 0xa1, 0xa2:

		pdu := new(PDU)

		// hack ANY -> IMPLICIT SEQUENCE
		m.Data.FullBytes[0] = 0x30
		_, err = asn1.Unmarshal(m.Data.FullBytes, pdu)
		if err != nil {
			return nil, fmt.Errorf("Error decoding pdu: %#v, %#v, %s", m.Data.FullBytes, pdu, err)
		}

		// make response pdu
		resp := new(PDUResponse)
		// Copy values from parsed pdu
		resp.RequestId = pdu.RequestId
		resp.ErrorIndex = pdu.ErrorIndex
		resp.ErrorStatus = pdu.ErrorStatus

		resp.VarBindList = make([]*Variable, len(pdu.VarBindList))

		// Decode all vars
		for c, v := range pdu.VarBindList {

			val, err := decodeValue(v.Value)
			if err != nil {
				return nil, err
			} else {
				val.Name = v.Name
				resp.VarBindList[c] = val
			}
		}

		return resp, nil
	default:
		return nil, fmt.Errorf("Unable to decode type: %#v\n", choice)
	}
	return nil, fmt.Errorf("Unknown CHOICE: %x", choice)
}

func decodeValue(data asn1.RawValue) (retVal *Variable, err error) {
	retVal = new(Variable)

	switch Asn1BER(data.FullBytes[0]) {

	// Integer
	case Integer:
		ret, err := parseInt(data.FullBytes[2:])
		if err != nil {
			break
		}
		retVal.Type = Integer
		retVal.Value = ret
	// Octet
	case OctetString:
		retVal.Type = OctetString
		retVal.Value = string(data.FullBytes[2:])
	// Counter32
	case Counter32:
		ret, err := parseInt(data.FullBytes[2:])
		if err != nil {
			break
		}
		retVal.Type = Counter32
		retVal.Value = ret
	case TimeTicks:
		ret, err := parseInt(data.FullBytes[2:])
		if err != nil {
			break
		}
		retVal.Type = TimeTicks
		retVal.Value = ret
	// Gauge32
	case Gauge32:
		ret, err := parseInt(data.FullBytes[2:])
		if err != nil {
			break
		}
		retVal.Type = Gauge32
		retVal.Value = ret
	case Counter64:
		ret, err := parseInt64(data.FullBytes[2:])

		// Decode it
		if err != nil {
			break
		}

		retVal.Type = Counter64
		retVal.Value = ret
	case NoSuchInstance:
		return nil, fmt.Errorf("No such instance")
	case NoSuchObject:
		return nil, fmt.Errorf("No such object")
	default:
		err = fmt.Errorf("Unable to decode %x - not implemented", data.FullBytes[0])
	}

	return
}
<commit_msg>Changes to accommodate the new parser<commit_after>// Copyright 2012 Andreas Louca. All rights reserved.
// Use of this source code is goverend by a BSD-style
// license that can be found in the LICENSE file.

package gosnmp

import (
	"encoding/asn1"
	"fmt"
)

type Asn1BER byte

const (
	Integer          Asn1BER = 0x02
	BitString                = 0x03
	OctetString              = 0x04
	Null                     = 0x05
	ObjectIdentifier         = 0x06
	Counter32                = 0x41
	Gauge32                  = 0x42
	TimeTicks                = 0x43
	Opaque                   = 0x44
	NsapAddress              = 0x45
	Counter64                = 0x46
	Uinteger32               = 0x47
	NoSuchObject             = 0x80
	NoSuchInstance           = 0x81
)

// Different packet structure is needed during decode, to trick encoding/asn1 to decode the SNMP packet

type Variable struct {
	Name  []int
	Type  Asn1BER
	Value interface{}
}

type VarBind struct {
	Name  asn1.ObjectIdentifier
	Value asn1.RawValue
}

type PDU struct {
	RequestId   int32
	ErrorStatus int
	ErrorIndex  int
	VarBindList []VarBind
}
type PDUResponse struct {
	RequestId   int32
	ErrorStatus int
	ErrorIndex  int
	VarBindList []*Variable
}

type Message struct {
	Version   int
	Community []uint8
	Data      asn1.RawValue
}

func decode(data []byte) (*PDUResponse, error) {
	m := Message{}
	_, err := asn1.Unmarshal(data, &m)
	if err != nil {
		return nil, err
	}
	choice := m.Data.FullBytes[0]
	switch choice {
	// SNMP Response
	case 0xa0, 0xa1, 0xa2:

		pdu := new(PDU)

		// hack ANY -> IMPLICIT SEQUENCE
		m.Data.FullBytes[0] = 0x30
		_, err = asn1.Unmarshal(m.Data.FullBytes, pdu)
		if err != nil {
			return nil, fmt.Errorf("Error decoding pdu: %#v, %#v, %s", m.Data.FullBytes, pdu, err)
		}

		// make response pdu
		resp := new(PDUResponse)
		// Copy values from parsed pdu
		resp.RequestId = pdu.RequestId
		resp.ErrorIndex = pdu.ErrorIndex
		resp.ErrorStatus = pdu.ErrorStatus

		resp.VarBindList = make([]*Variable, len(pdu.VarBindList))

		// Decode all vars
		for c, v := range pdu.VarBindList {

			val, err := decodeValue(v.Value.FullBytes)
			if err != nil {
				return nil, err
			} else {
				val.Name = v.Name
				resp.VarBindList[c] = val
			}
		}

		return resp, nil
	default:
		return nil, fmt.Errorf("Unable to decode type: %#v\n", choice)
	}
	return nil, fmt.Errorf("Unknown CHOICE: %x", choice)
}

func decodeValue(data []byte) (retVal *Variable, err error) {
	retVal = new(Variable)

	switch Asn1BER(data[0]) {

	// Integer
	case Integer:
		ret, err := parseInt(data[2:])
		if err != nil {
			break
		}
		retVal.Type = Integer
		retVal.Value = ret
	// Octet
	case OctetString:
		retVal.Type = OctetString
		retVal.Value = string(data[2:])
	// Counter32
	case Counter32:
		ret, err := parseInt(data[2:])
		if err != nil {
			break
		}
		retVal.Type = Counter32
		retVal.Value = ret
	case TimeTicks:
		ret, err := parseInt(data[2:])
		if err != nil {
			break
		}
		retVal.Type = TimeTicks
		retVal.Value = ret
	// Gauge32
	case Gauge32:
		ret, err := parseInt(data[2:])
		if err != nil {
			break
		}
		retVal.Type = Gauge32
		retVal.Value = ret
	case Counter64:
		ret, err := parseInt64(data[2:])

		// Decode it
		if err != nil {
			break
		}

		retVal.Type = Counter64
		retVal.Value = ret
	case NoSuchInstance:
		return nil, fmt.Errorf("No such instance")
	case NoSuchObject:
		return nil, fmt.Errorf("No such object")
	default:
		err = fmt.Errorf("Unable to decode %x - not implemented", data[0])
	}

	return
}

// Parses UINT16
func ParseUint16(content []byte) int {
	number := uint8(content[1]) | uint8(content[0])<<8
	//fmt.Printf("\t%d\n", number)

	return int(number)
}
<|endoftext|>


<commit_before>// Copyright 2013 Joshua Tacoma. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gozpl

import (
	"fmt"
	"reflect"
	"strconv"
)

type modifier interface {
	getSection(name string) (modifier, error)
	addValue(name string, value string) error
}

type builder struct {
	refs []modifier
}

func newBuilder(root interface{}) *builder {
	var ref modifier
	switch root.(type) {
	case map[string]interface{}:
		ref = mapModifier(root.(map[string]interface{}))
	default:
		ref = newRefModifier(root)
	}
	return &builder{refs: []modifier{ref}}
}

func (b *builder) consume(e *parseEvent) error {
	if b == nil {
		return fmt.Errorf("nil builder cannot consume events.")
	}
	if len(b.refs) == 0 {
		return fmt.Errorf("uninitialized builder cannot consume events.")
	}
	switch e.Type {
	case addValue:
		ref := b.refs[len(b.refs)-1]
		if err := ref.addValue(e.Name, e.Value); err != nil {
			return err
		}
	case endSection:
		println("pop")
		b.refs = b.refs[:len(b.refs)-1]
	case startSection:
		println("push", e.Name)
		ref := b.refs[len(b.refs)-1]
		if next, err := ref.getSection(e.Name); err != nil {
			return err
		} else {
			b.refs = append(b.refs, next)
		}
	default:
		return fmt.Errorf("unsupported event type %d.", e.Type)
	}
	return nil
}

type mapModifier map[string]interface{}

func (m mapModifier) getSection(name string) (modifier, error) {
	var section map[string]interface{}
	if already, ok := m[name]; !ok {
		section = make(map[string]interface{})
		m[name] = section
	} else {
		// TODO: return an error nicely instead of failing a type assertion
		section = already.(map[string]interface{})
	}
	return mapModifier(section), nil
}

func (m mapModifier) addValue(name string, value string) error {
	if already, ok := m[name]; !ok {
		println("setting", name, value)
		m[name] = []interface{}{value}
	} else {
		switch already.(type) {
		case []string:
			println("appending", name, value)
			m[name] = append(already.([]string), value)
		case []interface{}:
			println("appending", name, value)
			m[name] = append(already.([]interface{}), value)
		default:
			return fmt.Errorf("unsupported destination property value type: %T", already)
		}
	}
	return nil
}

type refModifier struct {
	E reflect.Value
	T reflect.Type
}

func newRefModifier(v interface{}) *refModifier {
	ref := &refModifier{
		E: reflect.ValueOf(v).Elem(),
	}
	ref.T = ref.E.Type()
	return ref
}

func (m refModifier) getSection(name string) (modifier, error) {
	var fi = -1
	for i := 0; i < m.E.NumField(); i++ {
		tag := m.T.Field(i).Tag
		if string(tag) == name || tag.Get("zpl") == name {
			fi = i
		} else if (string(tag) == "*" || tag.Get("zpl") == "*") && fi < 0 {
			fi = i
		}
	}
	if fi == -1 {
		return nil, fmt.Errorf("unknown name: %v", name)
	}
	field := m.E.Field(fi)
	if field.Type().Kind() == reflect.Map {
		if field.Type().Key().Kind() != reflect.String {
			return nil, fmt.Errorf("map key type must be string")
		}
		switch field.Type().Elem().Kind() {
		case reflect.Ptr:
			if field.IsNil() {
				field.Set(reflect.MakeMap(field.Type()))
			}
			ptr := field.MapIndex(reflect.ValueOf(name))
			if !ptr.IsValid() {
				ptr = reflect.New(field.Type().Elem().Elem())
				field.SetMapIndex(reflect.ValueOf(name), ptr)
			} else if ptr.IsNil() {
				ptr.Set(reflect.New(field.Type().Elem()))
			}
			println("D mapped", ptr.Elem().Type().Name())
			return &refModifier{E: ptr.Elem(), T: ptr.Elem().Type()}, nil
		case reflect.Map:
			return nil, fmt.Errorf("map of maps is not yet supported.")
		default:
			return nil, fmt.Errorf("map of %v is not yet supported.", field.Type().Elem())
		}
	} else if field.Type().Kind() == reflect.Ptr {
		if field.IsNil() {
			field.Set(reflect.New(field.Type().Elem()))
		}
		return &refModifier{E: field.Elem(), T: field.Elem().Type()}, nil
	}
	println("D section", name, "->", m.T.Field(fi).Name)
	return &refModifier{E: field, T: field.Type()}, nil
}

func (m refModifier) addValue(name string, value string) error {
	var fi = -1
	for i := 0; i < m.E.NumField(); i++ {
		tag := m.T.Field(i).Tag
		if string(tag) == name || tag.Get("zpl") == name {
			fi = i
		}
	}
	if fi == -1 {
		return fmt.Errorf("unknown name: %v", name)
	}
	field := m.E.Field(fi)
	println("D value", name, "->", m.T.Field(fi).Name)
	switch field.Type().Kind() {
	case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
		if parsed, err := strconv.ParseInt(value, 10, 64); err != nil {
			return fmt.Errorf("could not parse int %v", value)
		} else {
			field.SetInt(parsed)
		}
	case reflect.Bool:
		if parsed, err := strconv.ParseBool(value); err != nil {
			return fmt.Errorf("could not parse bool %v", value)
		} else {
			field.SetBool(parsed)
		}
	case reflect.String:
		field.SetString(value)
	case reflect.Slice:
		var next reflect.Value
		typ := field.Type()
		switch typ.Elem().Kind() {
		case reflect.String:
			next = reflect.ValueOf(value)
		default:
			return fmt.Errorf("slice of %v is not yet supported.", typ)
		}
		field.Set(reflect.Append(field, next))
	default:
		return fmt.Errorf("cannot set field of type %v", field.Type())
	}
	return nil
}
<commit_msg>Remove excess printlns.<commit_after>// Copyright 2013 Joshua Tacoma. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gozpl

import (
	"fmt"
	"reflect"
	"strconv"
)

type modifier interface {
	getSection(name string) (modifier, error)
	addValue(name string, value string) error
}

type builder struct {
	refs []modifier
}

func newBuilder(root interface{}) *builder {
	var ref modifier
	switch root.(type) {
	case map[string]interface{}:
		ref = mapModifier(root.(map[string]interface{}))
	default:
		ref = newRefModifier(root)
	}
	return &builder{refs: []modifier{ref}}
}

func (b *builder) consume(e *parseEvent) error {
	if b == nil {
		return fmt.Errorf("nil builder cannot consume events.")
	}
	if len(b.refs) == 0 {
		return fmt.Errorf("uninitialized builder cannot consume events.")
	}
	switch e.Type {
	case addValue:
		ref := b.refs[len(b.refs)-1]
		if err := ref.addValue(e.Name, e.Value); err != nil {
			return err
		}
	case endSection:
		b.refs = b.refs[:len(b.refs)-1]
	case startSection:
		ref := b.refs[len(b.refs)-1]
		if next, err := ref.getSection(e.Name); err != nil {
			return err
		} else {
			b.refs = append(b.refs, next)
		}
	default:
		return fmt.Errorf("unsupported event type %d.", e.Type)
	}
	return nil
}

type mapModifier map[string]interface{}

func (m mapModifier) getSection(name string) (modifier, error) {
	var section map[string]interface{}
	if already, ok := m[name]; !ok {
		section = make(map[string]interface{})
		m[name] = section
	} else {
		// TODO: return an error nicely instead of failing a type assertion
		section = already.(map[string]interface{})
	}
	return mapModifier(section), nil
}

func (m mapModifier) addValue(name string, value string) error {
	if already, ok := m[name]; !ok {
		m[name] = []interface{}{value}
	} else {
		switch already.(type) {
		case []string:
			m[name] = append(already.([]string), value)
		case []interface{}:
			m[name] = append(already.([]interface{}), value)
		default:
			return fmt.Errorf("unsupported destination property value type: %T", already)
		}
	}
	return nil
}

type refModifier struct {
	E reflect.Value
	T reflect.Type
}

func newRefModifier(v interface{}) *refModifier {
	ref := &refModifier{
		E: reflect.ValueOf(v).Elem(),
	}
	ref.T = ref.E.Type()
	return ref
}

func (m refModifier) getSection(name string) (modifier, error) {
	var fi = -1
	for i := 0; i < m.E.NumField(); i++ {
		tag := m.T.Field(i).Tag
		if string(tag) == name || tag.Get("zpl") == name {
			fi = i
		} else if (string(tag) == "*" || tag.Get("zpl") == "*") && fi < 0 {
			fi = i
		}
	}
	if fi == -1 {
		return nil, fmt.Errorf("unknown name: %v", name)
	}
	field := m.E.Field(fi)
	if field.Type().Kind() == reflect.Map {
		if field.Type().Key().Kind() != reflect.String {
			return nil, fmt.Errorf("map key type must be string")
		}
		switch field.Type().Elem().Kind() {
		case reflect.Ptr:
			if field.IsNil() {
				field.Set(reflect.MakeMap(field.Type()))
			}
			ptr := field.MapIndex(reflect.ValueOf(name))
			if !ptr.IsValid() {
				ptr = reflect.New(field.Type().Elem().Elem())
				field.SetMapIndex(reflect.ValueOf(name), ptr)
			} else if ptr.IsNil() {
				ptr.Set(reflect.New(field.Type().Elem()))
			}
			return &refModifier{E: ptr.Elem(), T: ptr.Elem().Type()}, nil
		case reflect.Map:
			return nil, fmt.Errorf("map of maps is not yet supported.")
		default:
			return nil, fmt.Errorf("map of %v is not yet supported.", field.Type().Elem())
		}
	} else if field.Type().Kind() == reflect.Ptr {
		if field.IsNil() {
			field.Set(reflect.New(field.Type().Elem()))
		}
		return &refModifier{E: field.Elem(), T: field.Elem().Type()}, nil
	}
	return &refModifier{E: field, T: field.Type()}, nil
}

func (m refModifier) addValue(name string, value string) error {
	var fi = -1
	for i := 0; i < m.E.NumField(); i++ {
		tag := m.T.Field(i).Tag
		if string(tag) == name || tag.Get("zpl") == name {
			fi = i
		}
	}
	if fi == -1 {
		return fmt.Errorf("unknown name: %v", name)
	}
	field := m.E.Field(fi)
	switch field.Type().Kind() {
	case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
		if parsed, err := strconv.ParseInt(value, 10, 64); err != nil {
			return fmt.Errorf("could not parse int %v", value)
		} else {
			field.SetInt(parsed)
		}
	case reflect.Bool:
		if parsed, err := strconv.ParseBool(value); err != nil {
			return fmt.Errorf("could not parse bool %v", value)
		} else {
			field.SetBool(parsed)
		}
	case reflect.String:
		field.SetString(value)
	case reflect.Slice:
		var next reflect.Value
		typ := field.Type()
		switch typ.Elem().Kind() {
		case reflect.String:
			next = reflect.ValueOf(value)
		default:
			return fmt.Errorf("slice of %v is not yet supported.", typ)
		}
		field.Set(reflect.Append(field, next))
	default:
		return fmt.Errorf("cannot set field of type %v", field.Type())
	}
	return nil
}
<|endoftext|>