<commit_before>/*
Package gothic wraps common behaviour when using Goth. This makes it quick, and easy, to get up
and running with Goth. Of course, if you want complete control over how things flow, in regards
to the authentication process, feel free and use Goth directly.

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
package gothic

import (
	"bytes"
	"compress/gzip"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/markbates/goth"
)

// SessionName is the key used to access the session store.
const SessionName = "_gothic_session"

// Store can/should be set by applications using gothic. The default is a cookie store.
var Store sessions.Store
var defaultStore sessions.Store

var keySet = false

func init() {
	key := []byte(os.Getenv("SESSION_SECRET"))
	keySet = len(key) != 0

	cookieStore := sessions.NewCookieStore([]byte(key))
	cookieStore.Options.HttpOnly = true
	Store = cookieStore
	defaultStore = Store
}

/*
BeginAuthHandler is a convenience handler for starting the authentication process.
It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

BeginAuthHandler will redirect the user to the appropriate authentication end-point
for the requested provider.

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
func BeginAuthHandler(res http.ResponseWriter, req *http.Request) {
	url, err := GetAuthURL(res, req)
	if err != nil {
		res.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(res, err)
		return
	}

	http.Redirect(res, req, url, http.StatusTemporaryRedirect)
}

// SetState sets the state string associated with the given request.
// If no state string is associated with the request, one will be generated.
// This state is sent to the provider and can be retrieved during the
// callback.
var SetState = func(req *http.Request) string {
	state := req.URL.Query().Get("state")
	if len(state) > 0 {
		return state
	}

	// If a state query param is not passed in, generate a random
	// base64-encoded nonce so that the state on the auth URL
	// is unguessable, preventing CSRF attacks, as described in
	//
	// https://auth0.com/docs/protocols/oauth2/oauth-state#keep-reading
	nonceBytes := make([]byte, 64)
	_, err := io.ReadFull(rand.Reader, nonceBytes)
	if err != nil {
		panic("gothic: source of randomness unavailable: " + err.Error())
	}
	return base64.URLEncoding.EncodeToString(nonceBytes)
}

// GetState gets the state returned by the provider during the callback.
// This is used to prevent CSRF attacks, see
// http://tools.ietf.org/html/rfc6749#section-10.12
var GetState = func(req *http.Request) string {
	return req.URL.Query().Get("state")
}

/*
GetAuthURL starts the authentication process with the requested provided.
It will return a URL that should be used to send users to.

It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

I would recommend using the BeginAuthHandler instead of doing all of these steps
yourself, but that's entirely up to you.
*/
func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
	if !keySet && defaultStore == Store {
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
	}

	providerName, err := GetProviderName(req)
	if err != nil {
		return "", err
	}

	provider, err := goth.GetProvider(providerName)
	if err != nil {
		return "", err
	}
	sess, err := provider.BeginAuth(SetState(req))
	if err != nil {
		return "", err
	}

	url, err := sess.GetAuthURL()
	if err != nil {
		return "", err
	}

	err = StoreInSession(providerName, sess.Marshal(), req, res)

	if err != nil {
		return "", err
	}

	return url, err
}

/*
CompleteUserAuth does what it says on the tin. It completes the authentication
process and fetches all of the basic information about the user from the provider.

It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
	defer Logout(res, req)
	if !keySet && defaultStore == Store {
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
	}

	providerName, err := GetProviderName(req)
	if err != nil {
		return goth.User{}, err
	}

	provider, err := goth.GetProvider(providerName)
	if err != nil {
		return goth.User{}, err
	}

	value, err := GetFromSession(providerName, req)
	if err != nil {
		return goth.User{}, err
	}

	sess, err := provider.UnmarshalSession(value)
	if err != nil {
		return goth.User{}, err
	}

	err = validateState(req, sess)
	if err != nil {
		return goth.User{}, err
	}

	user, err := provider.FetchUser(sess)
	if err == nil {
		// user can be found with existing session data
		return user, err
	}

	// get new token and retry fetch
	_, err = sess.Authorize(provider, req.URL.Query())
	if err != nil {
		return goth.User{}, err
	}

	err = StoreInSession(providerName, sess.Marshal(), req, res)

	if err != nil {
		return goth.User{}, err
	}

	gu, err := provider.FetchUser(sess)
	return gu, err
}

// validateState ensures that the state token param from the original
// AuthURL matches the one included in the current (callback) request.
func validateState(req *http.Request, sess goth.Session) error {
	rawAuthURL, err := sess.GetAuthURL()
	if err != nil {
		return err
	}

	authURL, err := url.Parse(rawAuthURL)
	if err != nil {
		return err
	}

	originalState := authURL.Query().Get("state")
	if originalState != "" && (originalState != req.URL.Query().Get("state")) {
		return errors.New("state token mismatch")
	}
	return nil
}

// Logout invalidates a user session.
func Logout(res http.ResponseWriter, req *http.Request) error {
	session, err := Store.Get(req, SessionName)
	if err != nil {
		return err
	}
	session.Options.MaxAge = -1
	session.Values = make(map[interface{}]interface{})
	err = session.Save(req, res)
	if err != nil {
		return errors.New("Could not delete user session ")
	}
	return nil
}

// GetProviderName is a function used to get the name of a provider
// for a given request. By default, this provider is fetched from
// the URL query string. If you provide it in a different way,
// assign your own function to this variable that returns the provider
// name for your request.
var GetProviderName = getProviderName

func getProviderName(req *http.Request) (string, error) {

	// get all the used providers
	providers := goth.GetProviders()

	// loop over the used providers, if we already have a valid session for any provider (ie. user is already logged-in with a provider), then return that provider name
	for _, provider := range providers {
		p := provider.Name()
		session, _ := Store.Get(req, p+SessionName)
		value := session.Values[p]
		if _, ok := value.(string); ok {
			return p, nil
		}
	}

	// try to get it from the url param "provider"
	if p := req.URL.Query().Get("provider"); p != "" {
		return p, nil
	}

	// try to get it from the url param ":provider"
	if p := req.URL.Query().Get(":provider"); p != "" {
		return p, nil
	}

	// try to get it from the context's value of "provider" key
	if p, ok := mux.Vars(req)["provider"]; ok {
		return p, nil
	}

	//  try to get it from the go-context's value of "provider" key
	if p, ok := req.Context().Value("provider").(string); ok {
		return p, nil
	}

	// if not found then return an empty string with the corresponding error
	return "", errors.New("you must select a provider")
}

// StoreInSession stores a specified key/value pair in the session.
func StoreInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
	session, _ := Store.New(req, SessionName)

	if err := updateSessionValue(session, key, value); err != nil {
		return err
	}

	return session.Save(req, res)
}

// GetFromSession retrieves a previously-stored value from the session.
// If no value has previously been stored at the specified key, it will return an error.
func GetFromSession(key string, req *http.Request) (string, error) {
	session, _ := Store.Get(req, SessionName)
	value, err := getSessionValue(session, key)
	if err != nil {
		return "", errors.New("could not find a matching session for this request")
	}

	return value, nil
}

func getSessionValue(session *sessions.Session, key string) (string, error) {
	value := session.Values[key]
	if value == nil {
		return "", fmt.Errorf("could not find a matching session for this request")
	}

	rdata := strings.NewReader(value.(string))
	r, err := gzip.NewReader(rdata)
	if err != nil {
		return "", err
	}
	s, err := ioutil.ReadAll(r)
	if err != nil {
		return "", err
	}

	return string(s), nil
}

func updateSessionValue(session *sessions.Session, key, value string) error {
	var b bytes.Buffer
	gz := gzip.NewWriter(&b)
	if _, err := gz.Write([]byte(value)); err != nil {
		return err
	}
	if err := gz.Flush(); err != nil {
		return err
	}
	if err := gz.Close(); err != nil {
		return err
	}

	session.Values[key] = b.String()
	return nil
}
<commit_msg>invalid example.go url in comments<commit_after>/*
Package gothic wraps common behaviour when using Goth. This makes it quick, and easy, to get up
and running with Goth. Of course, if you want complete control over how things flow, in regards
to the authentication process, feel free and use Goth directly.

See https://github.com/markbates/goth/blob/master/examples/main.go to see this in action.
*/
package gothic

import (
	"bytes"
	"compress/gzip"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/markbates/goth"
)

// SessionName is the key used to access the session store.
const SessionName = "_gothic_session"

// Store can/should be set by applications using gothic. The default is a cookie store.
var Store sessions.Store
var defaultStore sessions.Store

var keySet = false

func init() {
	key := []byte(os.Getenv("SESSION_SECRET"))
	keySet = len(key) != 0

	cookieStore := sessions.NewCookieStore([]byte(key))
	cookieStore.Options.HttpOnly = true
	Store = cookieStore
	defaultStore = Store
}

/*
BeginAuthHandler is a convenience handler for starting the authentication process.
It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

BeginAuthHandler will redirect the user to the appropriate authentication end-point
for the requested provider.

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
func BeginAuthHandler(res http.ResponseWriter, req *http.Request) {
	url, err := GetAuthURL(res, req)
	if err != nil {
		res.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(res, err)
		return
	}

	http.Redirect(res, req, url, http.StatusTemporaryRedirect)
}

// SetState sets the state string associated with the given request.
// If no state string is associated with the request, one will be generated.
// This state is sent to the provider and can be retrieved during the
// callback.
var SetState = func(req *http.Request) string {
	state := req.URL.Query().Get("state")
	if len(state) > 0 {
		return state
	}

	// If a state query param is not passed in, generate a random
	// base64-encoded nonce so that the state on the auth URL
	// is unguessable, preventing CSRF attacks, as described in
	//
	// https://auth0.com/docs/protocols/oauth2/oauth-state#keep-reading
	nonceBytes := make([]byte, 64)
	_, err := io.ReadFull(rand.Reader, nonceBytes)
	if err != nil {
		panic("gothic: source of randomness unavailable: " + err.Error())
	}
	return base64.URLEncoding.EncodeToString(nonceBytes)
}

// GetState gets the state returned by the provider during the callback.
// This is used to prevent CSRF attacks, see
// http://tools.ietf.org/html/rfc6749#section-10.12
var GetState = func(req *http.Request) string {
	return req.URL.Query().Get("state")
}

/*
GetAuthURL starts the authentication process with the requested provided.
It will return a URL that should be used to send users to.

It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

I would recommend using the BeginAuthHandler instead of doing all of these steps
yourself, but that's entirely up to you.
*/
func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
	if !keySet && defaultStore == Store {
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
	}

	providerName, err := GetProviderName(req)
	if err != nil {
		return "", err
	}

	provider, err := goth.GetProvider(providerName)
	if err != nil {
		return "", err
	}
	sess, err := provider.BeginAuth(SetState(req))
	if err != nil {
		return "", err
	}

	url, err := sess.GetAuthURL()
	if err != nil {
		return "", err
	}

	err = StoreInSession(providerName, sess.Marshal(), req, res)

	if err != nil {
		return "", err
	}

	return url, err
}

/*
CompleteUserAuth does what it says on the tin. It completes the authentication
process and fetches all of the basic information about the user from the provider.

It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
	defer Logout(res, req)
	if !keySet && defaultStore == Store {
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
	}

	providerName, err := GetProviderName(req)
	if err != nil {
		return goth.User{}, err
	}

	provider, err := goth.GetProvider(providerName)
	if err != nil {
		return goth.User{}, err
	}

	value, err := GetFromSession(providerName, req)
	if err != nil {
		return goth.User{}, err
	}

	sess, err := provider.UnmarshalSession(value)
	if err != nil {
		return goth.User{}, err
	}

	err = validateState(req, sess)
	if err != nil {
		return goth.User{}, err
	}

	user, err := provider.FetchUser(sess)
	if err == nil {
		// user can be found with existing session data
		return user, err
	}

	// get new token and retry fetch
	_, err = sess.Authorize(provider, req.URL.Query())
	if err != nil {
		return goth.User{}, err
	}

	err = StoreInSession(providerName, sess.Marshal(), req, res)

	if err != nil {
		return goth.User{}, err
	}

	gu, err := provider.FetchUser(sess)
	return gu, err
}

// validateState ensures that the state token param from the original
// AuthURL matches the one included in the current (callback) request.
func validateState(req *http.Request, sess goth.Session) error {
	rawAuthURL, err := sess.GetAuthURL()
	if err != nil {
		return err
	}

	authURL, err := url.Parse(rawAuthURL)
	if err != nil {
		return err
	}

	originalState := authURL.Query().Get("state")
	if originalState != "" && (originalState != req.URL.Query().Get("state")) {
		return errors.New("state token mismatch")
	}
	return nil
}

// Logout invalidates a user session.
func Logout(res http.ResponseWriter, req *http.Request) error {
	session, err := Store.Get(req, SessionName)
	if err != nil {
		return err
	}
	session.Options.MaxAge = -1
	session.Values = make(map[interface{}]interface{})
	err = session.Save(req, res)
	if err != nil {
		return errors.New("Could not delete user session ")
	}
	return nil
}

// GetProviderName is a function used to get the name of a provider
// for a given request. By default, this provider is fetched from
// the URL query string. If you provide it in a different way,
// assign your own function to this variable that returns the provider
// name for your request.
var GetProviderName = getProviderName

func getProviderName(req *http.Request) (string, error) {

	// get all the used providers
	providers := goth.GetProviders()

	// loop over the used providers, if we already have a valid session for any provider (ie. user is already logged-in with a provider), then return that provider name
	for _, provider := range providers {
		p := provider.Name()
		session, _ := Store.Get(req, p+SessionName)
		value := session.Values[p]
		if _, ok := value.(string); ok {
			return p, nil
		}
	}

	// try to get it from the url param "provider"
	if p := req.URL.Query().Get("provider"); p != "" {
		return p, nil
	}

	// try to get it from the url param ":provider"
	if p := req.URL.Query().Get(":provider"); p != "" {
		return p, nil
	}

	// try to get it from the context's value of "provider" key
	if p, ok := mux.Vars(req)["provider"]; ok {
		return p, nil
	}

	//  try to get it from the go-context's value of "provider" key
	if p, ok := req.Context().Value("provider").(string); ok {
		return p, nil
	}

	// if not found then return an empty string with the corresponding error
	return "", errors.New("you must select a provider")
}

// StoreInSession stores a specified key/value pair in the session.
func StoreInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
	session, _ := Store.New(req, SessionName)

	if err := updateSessionValue(session, key, value); err != nil {
		return err
	}

	return session.Save(req, res)
}

// GetFromSession retrieves a previously-stored value from the session.
// If no value has previously been stored at the specified key, it will return an error.
func GetFromSession(key string, req *http.Request) (string, error) {
	session, _ := Store.Get(req, SessionName)
	value, err := getSessionValue(session, key)
	if err != nil {
		return "", errors.New("could not find a matching session for this request")
	}

	return value, nil
}

func getSessionValue(session *sessions.Session, key string) (string, error) {
	value := session.Values[key]
	if value == nil {
		return "", fmt.Errorf("could not find a matching session for this request")
	}

	rdata := strings.NewReader(value.(string))
	r, err := gzip.NewReader(rdata)
	if err != nil {
		return "", err
	}
	s, err := ioutil.ReadAll(r)
	if err != nil {
		return "", err
	}

	return string(s), nil
}

func updateSessionValue(session *sessions.Session, key, value string) error {
	var b bytes.Buffer
	gz := gzip.NewWriter(&b)
	if _, err := gz.Write([]byte(value)); err != nil {
		return err
	}
	if err := gz.Flush(); err != nil {
		return err
	}
	if err := gz.Close(); err != nil {
		return err
	}

	session.Values[key] = b.String()
	return nil
}
<|endoftext|>


<commit_before>package repo

import (
	"errors"
	"fmt"
	"gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
	"os"
	"path"

	"github.com/OpenBazaar/openbazaar-go/ipfs"
	"github.com/ipfs/go-ipfs/core"
	"github.com/ipfs/go-ipfs/namesys"
	"github.com/ipfs/go-ipfs/repo/fsrepo"
	"github.com/op/go-logging"
	"github.com/tyler-smith/go-bip39"
)

var log = logging.MustGetLogger("repo")

var ErrRepoExists = errors.New(`ipfs configuration file already exists!
Reinitializing would overwrite your keys.
(use -f to force overwrite)
`)

func DoInit(repoRoot string, nBitsForKeypair int, testnet bool, password string, mnemonic string, dbInit func(string, []byte, string) error) error {
	if err := maybeCreateOBDirectories(repoRoot); err != nil {
		return err
	}

	if fsrepo.IsInitialized(repoRoot) {
		return ErrRepoExists
	}

	if err := checkWriteable(repoRoot); err != nil {
		return err
	}

	conf, err := InitConfig(repoRoot)
	if err != nil {
		return err
	}

	if mnemonic == "" {
		mnemonic, err = createMnemonic(bip39.NewEntropy, bip39.NewMnemonic)
		if err != nil {
			return err
		}
	}
	seed := bip39.NewSeed(mnemonic, "Secret Passphrase")
	fmt.Printf("generating %d-bit RSA keypair...", nBitsForKeypair)
	identityKey, err := ipfs.IdentityKeyFromSeed(seed, nBitsForKeypair)
	if err != nil {
		return err
	}
	fmt.Printf("done\n")

	identity, err := ipfs.IdentityFromKey(identityKey)
	if err != nil {
		return err
	}

	log.Infof("initializing openbazaar node at %s\n", repoRoot)
	if err := fsrepo.Init(repoRoot, conf); err != nil {
		return err
	}
	conf.Identity = identity

	if err := addConfigExtensions(repoRoot, testnet); err != nil {
		return err
	}

	if err := dbInit(mnemonic, identityKey, password); err != nil {
		return err
	}

	return initializeIpnsKeyspace(repoRoot, identityKey)
}

func maybeCreateOBDirectories(repoRoot string) error {
	if err := os.MkdirAll(path.Join(repoRoot, "root"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "listings"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "images"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "images", "tiny"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "images", "small"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "images", "medium"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "images", "large"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "images", "original"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "feed"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "channel"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "files"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "outbox"), os.ModePerm); err != nil {
		return err
	}
	return nil
}

func checkWriteable(dir string) error {
	_, err := os.Stat(dir)
	if err == nil {
		// Directory exists, make sure we can write to it
		testfile := path.Join(dir, "test")
		fi, err := os.Create(testfile)
		if err != nil {
			if os.IsPermission(err) {
				return fmt.Errorf("%s is not writeable by the current user", dir)
			}
			return fmt.Errorf("unexpected error while checking writeablility of repo root: %s", err)
		}
		fi.Close()
		return os.Remove(testfile)
	}

	if os.IsNotExist(err) {
		// Directory does not exist, check that we can create it
		return os.Mkdir(dir, 0775)
	}

	if os.IsPermission(err) {
		return fmt.Errorf("cannot write to %s, incorrect permissions", err)
	}

	return err
}

func initializeIpnsKeyspace(repoRoot string, privKeyBytes []byte) error {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
		return err
	}
	cfg, err := r.Config()
	if err != nil {
		log.Error(err)
		return err
	}
	identity, err := ipfs.IdentityFromKey(privKeyBytes)
	if err != nil {
		return err
	}

	cfg.Identity = identity
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
	if err != nil {
		return err
	}
	defer nd.Close()

	err = nd.SetupOfflineRouting()
	if err != nil {
		return err
	}

	return namesys.InitializeKeyspace(ctx, nd.DAG, nd.Namesys, nd.Pinning, nd.PrivateKey)
}

func addConfigExtensions(repoRoot string, testnet bool) error {
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
		return err
	}
	var w WalletConfig = WalletConfig{
		Type:             "spvwallet",
		MaxFee:           2000,
		FeeAPI:           "https://bitcoinfees.21.co/api/v1/fees/recommended",
		HighFeeDefault:   60,
		MediumFeeDefault: 40,
		LowFeeDefault:    20,
		TrustedPeer:      "",
	}

	var a APIConfig = APIConfig{
		Enabled:     true,
		HTTPHeaders: nil,
	}
	if err := extendConfigFile(r, "Wallet", w); err != nil {
		return err
	}
	if err := extendConfigFile(r, "Resolver", "https://resolver.onename.com/"); err != nil {
		return err
	}
	if err := extendConfigFile(r, "Crosspost-gateways", []string{"http://gateway.ob1.io/"}); err != nil {
		return err
	}
	if err := extendConfigFile(r, "Dropbox-api-token", ""); err != nil {
		return err
	}
	if err := extendConfigFile(r, "JSON-API", a); err != nil {
		return err
	}
	if err := r.Close(); err != nil {
		return err
	}
	return nil
}

func createMnemonic(newEntropy func(int) ([]byte, error), newMnemonic func([]byte) (string, error)) (string, error) {
	entropy, err := newEntropy(128)
	if err != nil {
		return "", err
	}
	mnemonic, err := newMnemonic(entropy)
	if err != nil {
		return "", err
	}
	return mnemonic, nil
}
<commit_msg>Minor cleanups to init.go<commit_after>package repo

import (
	"errors"
	"fmt"
	"gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
	"os"
	"path"

	"github.com/OpenBazaar/openbazaar-go/ipfs"
	"github.com/ipfs/go-ipfs/core"
	"github.com/ipfs/go-ipfs/namesys"
	"github.com/ipfs/go-ipfs/repo/fsrepo"
	"github.com/op/go-logging"
	"github.com/tyler-smith/go-bip39"
)

var log = logging.MustGetLogger("repo")

func DoInit(repoRoot string, nBitsForKeypair int, testnet bool, password string, mnemonic string, dbInit func(string, []byte, string) error) error {
	if err := maybeCreateOBDirectories(repoRoot); err != nil {
		return err
	}

	if fsrepo.IsInitialized(repoRoot) {
		return errors.New("IPFS configuration file exists. Reinitializing would overwrite your keys. Use -f to force overwrite.")
	}

	if err := checkWriteable(repoRoot); err != nil {
		return err
	}

	conf, err := InitConfig(repoRoot)
	if err != nil {
		return err
	}

	if mnemonic == "" {
		mnemonic, err = createMnemonic(bip39.NewEntropy, bip39.NewMnemonic)
		if err != nil {
			return err
		}
	}
	seed := bip39.NewSeed(mnemonic, "Secret Passphrase")
	fmt.Printf("Generating %d-bit RSA keypair...", nBitsForKeypair)
	identityKey, err := ipfs.IdentityKeyFromSeed(seed, nBitsForKeypair)
	if err != nil {
		return err
	}
	fmt.Printf("Done\n")

	identity, err := ipfs.IdentityFromKey(identityKey)
	if err != nil {
		return err
	}

	log.Infof("Initializing OpenBazaar node at %s\n", repoRoot)
	if err := fsrepo.Init(repoRoot, conf); err != nil {
		return err
	}
	conf.Identity = identity

	if err := addConfigExtensions(repoRoot, testnet); err != nil {
		return err
	}

	if err := dbInit(mnemonic, identityKey, password); err != nil {
		return err
	}

	return initializeIpnsKeyspace(repoRoot, identityKey)
}

func maybeCreateOBDirectories(repoRoot string) error {
	if err := os.MkdirAll(path.Join(repoRoot, "root"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "listings"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "images"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "images", "tiny"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "images", "small"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "images", "medium"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "images", "large"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "images", "original"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "feed"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "channel"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "root", "files"), os.ModePerm); err != nil {
		return err
	}
	if err := os.MkdirAll(path.Join(repoRoot, "outbox"), os.ModePerm); err != nil {
		return err
	}
	return nil
}

func checkWriteable(dir string) error {
	_, err := os.Stat(dir)
	if err == nil {
		// Directory exists, make sure we can write to it
		testfile := path.Join(dir, "test")
		fi, err := os.Create(testfile)
		if err != nil {
			if os.IsPermission(err) {
				return fmt.Errorf("%s is not writeable by the current user", dir)
			}
			return fmt.Errorf("Unexpected error while checking writeablility of repo root: %s", err)
		}
		fi.Close()
		return os.Remove(testfile)
	}

	if os.IsNotExist(err) {
		// Directory does not exist, check that we can create it
		return os.Mkdir(dir, 0775)
	}

	if os.IsPermission(err) {
		return fmt.Errorf("Cannot write to %s, incorrect permissions", err)
	}

	return err
}

func initializeIpnsKeyspace(repoRoot string, privKeyBytes []byte) error {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
		return err
	}
	cfg, err := r.Config()
	if err != nil {
		log.Error(err)
		return err
	}
	identity, err := ipfs.IdentityFromKey(privKeyBytes)
	if err != nil {
		return err
	}

	cfg.Identity = identity
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
	if err != nil {
		return err
	}
	defer nd.Close()

	err = nd.SetupOfflineRouting()
	if err != nil {
		return err
	}

	return namesys.InitializeKeyspace(ctx, nd.DAG, nd.Namesys, nd.Pinning, nd.PrivateKey)
}

func addConfigExtensions(repoRoot string, testnet bool) error {
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
		return err
	}
	var w WalletConfig = WalletConfig{
		Type:             "spvwallet",
		MaxFee:           2000,
		FeeAPI:           "https://bitcoinfees.21.co/api/v1/fees/recommended",
		HighFeeDefault:   60,
		MediumFeeDefault: 40,
		LowFeeDefault:    20,
		TrustedPeer:      "",
	}

	var a APIConfig = APIConfig{
		Enabled:     true,
		HTTPHeaders: nil,
	}
	if err := extendConfigFile(r, "Wallet", w); err != nil {
		return err
	}
	if err := extendConfigFile(r, "Resolver", "https://resolver.onename.com/"); err != nil {
		return err
	}
	if err := extendConfigFile(r, "Crosspost-gateways", []string{"http://gateway.ob1.io/"}); err != nil {
		return err
	}
	if err := extendConfigFile(r, "Dropbox-api-token", ""); err != nil {
		return err
	}
	if err := extendConfigFile(r, "JSON-API", a); err != nil {
		return err
	}
	if err := r.Close(); err != nil {
		return err
	}
	return nil
}

func createMnemonic(newEntropy func(int) ([]byte, error), newMnemonic func([]byte) (string, error)) (string, error) {
	entropy, err := newEntropy(128)
	if err != nil {
		return "", err
	}
	mnemonic, err := newMnemonic(entropy)
	if err != nil {
		return "", err
	}
	return mnemonic, nil
}
<|endoftext|>


<commit_before>// Copyright 2017 Mathew Robinson <mrobinson@praelatus.io>. All rights reserved.
// Use of this source code is governed by the AGPLv3 license that can be found in
// the LICENSE file.

// +build !release

package repo

import (
	"math/rand"
	"strconv"

	"github.com/praelatus/praelatus/models"
	"gopkg.in/mgo.v2/bson"
)

var tickets []models.Ticket

func init() {
	for i := 0; i < 100; i++ {
		t := models.Ticket{
			Key:     "TEST-" + strconv.Itoa(i+1),
			Summary: "This is test ticket #" + strconv.Itoa(i),
			Description: `# Refugam in se fuit quae

## Pariter vel sine frustra

Lorem markdownum Diomede quid, ab oracula diligit; aut qui nam. Dum postquam tu
fecit *numerare dederat es* animae dederat, quem soror. Venae potentem minacia
summa precantem statque procubuisse et sui et deus sceleri?

1. Irascitur inter de cunctae arva tenet pectore
2. Tabo messibus
3. Duobus undae

## Truncis sulcat Stymphalide

Sollertius nomina plectrumque nec nec animos, Rhadamanthon figitur vulgata
hominum ad. Vulnere pendentemque soror incubuit lenta vertunt. Deae cepit
quotiensque toto Aenea curvamine cum non sua divus audet patriae si et fit
vineta. Aquas nimium: postquam hominum promissa!

    if (isdn >= personal_executable(cJquery)) {
        redundancy_firmware_guid = infringement;
        keystroke += pum_document(page_wins, icq_nanometer_malware +
                barInternal);
        mcaQueryMarketing(portLeak, guiPhreaking, thunderbolt(4, twainAtaLink));
    }
    addressTorrent = boot_character_website(linkedinVaporware, plugRightBoot);
    var megabit_standalone_of = nocSo + program_mouse + 26;

## Nostra est perdix annos et quas

Vellentem quaerit est umeros celsior navis intrat
[saepe](http://minosiuvenis.net/numen.html). Saxo vocet turris Athamanta
membris, semesaque: nate leto summos instabiles primosque avertite nostras tu
quies in [avidisque](http://www.templaaequora.net/). Summa se expulit perfide
mirum, suo brevi absentem umerus vultumque cognata. Nempe ipsi quod procul
verba, frusta, sed gemitu non huius odit; non aprica pedumque Hectoris, taxo.
Mentis vivit tori erubuit, qui flebile natura Echo percussis pallet?

- Ministros tumebat famuli
- Aristas per blandis
- Corpora qua Medea acu potentia inrita

Non Cipe reges, laetitiam filius sceleratum naidas, fortunaque occidit. Laeva et
ipsa divite, est ille ver verba vicisse, exsiliantque aprica illius, rapta?`,
			Reporter: users[rand.Intn(2)].Username,
			Assignee: users[rand.Intn(2)].Username,
			Type:     p.TicketTypes[rand.Intn(3)],
			Project:  p.Key,
		}

		for i := 0; i < rand.Intn(50); i++ {
			c := models.Comment{
				Author: users[rand.Intn(2)].Username,
				Body: `# Yo Dawg

I heard you like **markdown**.

So I put markdown in your comment.`,
			}

			t.Comments = append(t.Comments, c)
		}

		tickets = append(tickets, t)
	}

}

type mockRepo struct{}

func NewMockRepo() Repo {
	return mockRepo{}
}

type mockProjectRepo struct{}

func (pr mockProjectRepo) Get(u *models.User, uid string) (models.Project, error) {
	return p1, nil
}

func (pr mockProjectRepo) Search(u *models.User, query string) ([]models.Project, error) {
	return []models.Project{p, p1}, nil
}

func (pr mockProjectRepo) Update(u *models.User, uid string, updated models.Project) error {
	return nil
}

func (pr mockProjectRepo) Create(u *models.User, project models.Project) (models.Project, error) {
	return project, nil
}

func (pr mockProjectRepo) Delete(u *models.User, uid string) error {
	return nil
}

type mockTicketRepo struct{}

func (t mockTicketRepo) Get(u *models.User, uid string) (models.Ticket, error) {
	return tickets[0], nil
}

func (t mockTicketRepo) Search(u *models.User, query string) ([]models.Ticket, error) {
	return tickets, nil
}

func (t mockTicketRepo) Update(u *models.User, uid string, updated models.Ticket) error {
	return nil
}

func (t mockTicketRepo) Create(u *models.User, ticket models.Ticket) (models.Ticket, error) {
	return ticket, nil
}

func (t mockTicketRepo) Delete(u *models.User, uid string) error {
	return nil
}

func (t mockTicketRepo) AddComment(u *models.User, uid string, comment models.Comment) (models.Ticket, error) {
	tickets[0].Comments = append(tickets[0].Comments, comment)
	return tickets[0], nil
}

func (t mockTicketRepo) NextTicketKey(u *models.User, projectKey string) (string, error) {
	return projectKey + string(len(tickets)+1), nil
}

type mockUserRepo struct{}

func (ur mockUserRepo) Get(u *models.User, uid string) (models.User, error) {
	return *u1, nil
}

func (ur mockUserRepo) Search(u *models.User, query string) ([]models.User, error) {
	return users, nil
}

func (ur mockUserRepo) Update(u *models.User, uid string, updated models.User) error {
	return nil
}

func (ur mockUserRepo) Create(u *models.User, user models.User) (models.User, error) {
	return user, nil
}

func (ur mockUserRepo) Delete(u *models.User, uid string) error {
	return nil
}

type mockFieldRepo struct{}

func (fsr mockFieldRepo) Get(u *models.User, uid string) (models.FieldScheme, error) {
	return fs, nil
}

func (fsr mockFieldRepo) Search(u *models.User, query string) ([]models.FieldScheme, error) {
	return []models.FieldScheme{fs}, nil
}

func (fsr mockFieldRepo) Update(u *models.User, uid string, updated models.FieldScheme) error {
	return nil
}

func (fsr mockFieldRepo) Create(u *models.User, fieldScheme models.FieldScheme) (models.FieldScheme, error) {
	fieldScheme.ID = bson.NewObjectId()
	return fieldScheme, nil
}

func (fsr mockFieldRepo) Delete(u *models.User, uid string) error {
	return nil
}

type mockWorkflowRepo struct{}

func (wr mockWorkflowRepo) Get(u *models.User, uid string) (models.Workflow, error) {
	return workflows[0], nil
}

func (wr mockWorkflowRepo) Search(u *models.User, query string) ([]models.Workflow, error) {
	return workflows, nil
}

func (wr mockWorkflowRepo) Update(u *models.User, uid string, updated models.Workflow) error {
	return nil
}

func (wr mockWorkflowRepo) Create(u *models.User, workflow models.Workflow) (models.Workflow, error) {
	workflow.ID = bson.NewObjectId()
	return workflow, nil
}

func (wr mockWorkflowRepo) Delete(u *models.User, uid string) error {
	return nil
}

func (m mockRepo) Projects() ProjectRepo {
	return mockProjectRepo{}
}

func (m mockRepo) Tickets() TicketRepo {
	return mockTicketRepo{}
}

func (m mockRepo) Users() UserRepo {
	return mockUserRepo{}
}

func (m mockRepo) Fields() FieldSchemeRepo {
	return mockFieldRepo{}
}

func (m mockRepo) Workflows() WorkflowRepo {
	return mockWorkflowRepo{}
}

func (m mockRepo) Clean() error { return nil }
func (m mockRepo) Test() error  { return nil }
<commit_msg>Hard code ID's in mock store for testing<commit_after>// Copyright 2017 Mathew Robinson <mrobinson@praelatus.io>. All rights reserved.
// Use of this source code is governed by the AGPLv3 license that can be found in
// the LICENSE file.

// +build !release

package repo

import (
	"math/rand"
	"strconv"

	"github.com/praelatus/praelatus/models"
	"gopkg.in/mgo.v2/bson"
)

var tickets []models.Ticket

func init() {
	for i := 0; i < 100; i++ {
		t := models.Ticket{
			Key:     "TEST-" + strconv.Itoa(i+1),
			Summary: "This is test ticket #" + strconv.Itoa(i),
			Description: `# Refugam in se fuit quae

## Pariter vel sine frustra

Lorem markdownum Diomede quid, ab oracula diligit; aut qui nam. Dum postquam tu
fecit *numerare dederat es* animae dederat, quem soror. Venae potentem minacia
summa precantem statque procubuisse et sui et deus sceleri?

1. Irascitur inter de cunctae arva tenet pectore
2. Tabo messibus
3. Duobus undae

## Truncis sulcat Stymphalide

Sollertius nomina plectrumque nec nec animos, Rhadamanthon figitur vulgata
hominum ad. Vulnere pendentemque soror incubuit lenta vertunt. Deae cepit
quotiensque toto Aenea curvamine cum non sua divus audet patriae si et fit
vineta. Aquas nimium: postquam hominum promissa!

    if (isdn >= personal_executable(cJquery)) {
        redundancy_firmware_guid = infringement;
        keystroke += pum_document(page_wins, icq_nanometer_malware +
                barInternal);
        mcaQueryMarketing(portLeak, guiPhreaking, thunderbolt(4, twainAtaLink));
    }
    addressTorrent = boot_character_website(linkedinVaporware, plugRightBoot);
    var megabit_standalone_of = nocSo + program_mouse + 26;

## Nostra est perdix annos et quas

Vellentem quaerit est umeros celsior navis intrat
[saepe](http://minosiuvenis.net/numen.html). Saxo vocet turris Athamanta
membris, semesaque: nate leto summos instabiles primosque avertite nostras tu
quies in [avidisque](http://www.templaaequora.net/). Summa se expulit perfide
mirum, suo brevi absentem umerus vultumque cognata. Nempe ipsi quod procul
verba, frusta, sed gemitu non huius odit; non aprica pedumque Hectoris, taxo.
Mentis vivit tori erubuit, qui flebile natura Echo percussis pallet?

- Ministros tumebat famuli
- Aristas per blandis
- Corpora qua Medea acu potentia inrita

Non Cipe reges, laetitiam filius sceleratum naidas, fortunaque occidit. Laeva et
ipsa divite, est ille ver verba vicisse, exsiliantque aprica illius, rapta?`,
			Reporter: users[rand.Intn(2)].Username,
			Assignee: users[rand.Intn(2)].Username,
			Type:     p.TicketTypes[rand.Intn(3)],
			Project:  p.Key,
		}

		for i := 0; i < rand.Intn(50); i++ {
			c := models.Comment{
				Author: users[rand.Intn(2)].Username,
				Body: `# Yo Dawg

I heard you like **markdown**.

So I put markdown in your comment.`,
			}

			t.Comments = append(t.Comments, c)
		}

		tickets = append(tickets, t)
	}

}

type mockRepo struct{}

func NewMockRepo() Repo {
	return mockRepo{}
}

type mockProjectRepo struct{}

func (pr mockProjectRepo) Get(u *models.User, uid string) (models.Project, error) {
	return p1, nil
}

func (pr mockProjectRepo) Search(u *models.User, query string) ([]models.Project, error) {
	return []models.Project{p, p1}, nil
}

func (pr mockProjectRepo) Update(u *models.User, uid string, updated models.Project) error {
	return nil
}

func (pr mockProjectRepo) Create(u *models.User, project models.Project) (models.Project, error) {
	return project, nil
}

func (pr mockProjectRepo) Delete(u *models.User, uid string) error {
	return nil
}

type mockTicketRepo struct{}

func (t mockTicketRepo) Get(u *models.User, uid string) (models.Ticket, error) {
	return tickets[0], nil
}

func (t mockTicketRepo) Search(u *models.User, query string) ([]models.Ticket, error) {
	return tickets, nil
}

func (t mockTicketRepo) Update(u *models.User, uid string, updated models.Ticket) error {
	return nil
}

func (t mockTicketRepo) Create(u *models.User, ticket models.Ticket) (models.Ticket, error) {
	return ticket, nil
}

func (t mockTicketRepo) Delete(u *models.User, uid string) error {
	return nil
}

func (t mockTicketRepo) AddComment(u *models.User, uid string, comment models.Comment) (models.Ticket, error) {
	tickets[0].Comments = append(tickets[0].Comments, comment)
	return tickets[0], nil
}

func (t mockTicketRepo) NextTicketKey(u *models.User, projectKey string) (string, error) {
	return projectKey + string(len(tickets)+1), nil
}

type mockUserRepo struct{}

func (ur mockUserRepo) Get(u *models.User, uid string) (models.User, error) {
	return *u1, nil
}

func (ur mockUserRepo) Search(u *models.User, query string) ([]models.User, error) {
	return users, nil
}

func (ur mockUserRepo) Update(u *models.User, uid string, updated models.User) error {
	return nil
}

func (ur mockUserRepo) Create(u *models.User, user models.User) (models.User, error) {
	return user, nil
}

func (ur mockUserRepo) Delete(u *models.User, uid string) error {
	return nil
}

type mockFieldRepo struct{}

func (fsr mockFieldRepo) Get(u *models.User, uid string) (models.FieldScheme, error) {
	// Hardcode to the ID expected in tests.
	fs.ID = "59e3f2026791c08e74da1bb2"
	return fs, nil
}

func (fsr mockFieldRepo) Search(u *models.User, query string) ([]models.FieldScheme, error) {
	return []models.FieldScheme{fs}, nil
}

func (fsr mockFieldRepo) Update(u *models.User, uid string, updated models.FieldScheme) error {
	return nil
}

func (fsr mockFieldRepo) Create(u *models.User, fieldScheme models.FieldScheme) (models.FieldScheme, error) {
	fieldScheme.ID = bson.NewObjectId()
	return fieldScheme, nil
}

func (fsr mockFieldRepo) Delete(u *models.User, uid string) error {
	return nil
}

type mockWorkflowRepo struct{}

func (wr mockWorkflowRepo) Get(u *models.User, uid string) (models.Workflow, error) {
	wrk := workflows[0]
	// Hardcode to the ID expected in tests.
	wrk.ID = "59e3f2026791c08e74da1bb2"
	return wrk, nil
}

func (wr mockWorkflowRepo) Search(u *models.User, query string) ([]models.Workflow, error) {
	return workflows, nil
}

func (wr mockWorkflowRepo) Update(u *models.User, uid string, updated models.Workflow) error {
	return nil
}

func (wr mockWorkflowRepo) Create(u *models.User, workflow models.Workflow) (models.Workflow, error) {
	workflow.ID = bson.NewObjectId()
	return workflow, nil
}

func (wr mockWorkflowRepo) Delete(u *models.User, uid string) error {
	return nil
}

func (m mockRepo) Projects() ProjectRepo {
	return mockProjectRepo{}
}

func (m mockRepo) Tickets() TicketRepo {
	return mockTicketRepo{}
}

func (m mockRepo) Users() UserRepo {
	return mockUserRepo{}
}

func (m mockRepo) Fields() FieldSchemeRepo {
	return mockFieldRepo{}
}

func (m mockRepo) Workflows() WorkflowRepo {
	return mockWorkflowRepo{}
}

func (m mockRepo) Clean() error { return nil }
func (m mockRepo) Test() error  { return nil }
<|endoftext|>


<commit_before>package report

import (
	"net"
	"strings"
)

// Delimiters are used to separate parts of node IDs, to guarantee uniqueness
// in particular contexts.
const (
	// ScopeDelim is a general-purpose delimiter used within node IDs to
	// separate different contextual scopes. Different topologies have
	// different key structures.
	ScopeDelim = ";"

	// EdgeDelim separates two node IDs when they need to exist in the same key.
	// Concretely, it separates node IDs in keys that represent edges.
	EdgeDelim = "|"

	// Key added to nodes to prevent them being joined with conntracked connections
	DoesNotMakeConnections = "does_not_make_connections"

	// WeaveOverlayPeerPrefix is the prefix for weave peers in the overlay network
	WeaveOverlayPeerPrefix = ""

	// DockerOverlayPeerPrefix is the prefix for docker peers in the overlay network
	DockerOverlayPeerPrefix = "docker_peer_"
)

// MakeEndpointNodeID produces an endpoint node ID from its composite parts.
func MakeEndpointNodeID(hostID, namespaceID, address, port string) string {
	return makeAddressID(hostID, namespaceID, address) + ScopeDelim + port
}

// MakeAddressNodeID produces an address node ID from its composite parts.
func MakeAddressNodeID(hostID, address string) string {
	return makeAddressID(hostID, "", address)
}

func makeAddressID(hostID, namespaceID, address string) string {
	var scope string

	// Loopback addresses and addresses explicitly marked as local get
	// scoped by hostID
	// Loopback addresses are also scoped by the networking
	// namespace if available, since they can clash.
	addressIP := net.ParseIP(address)
	if addressIP != nil && LocalNetworks.Contains(addressIP) {
		scope = hostID
	} else if IsLoopback(address) {
		scope = hostID
		if namespaceID != "" {
			scope += "-" + namespaceID
		}
	}

	return scope + ScopeDelim + address
}

// MakeScopedEndpointNodeID is like MakeEndpointNodeID, but it always
// prefixes the ID with a scope.
func MakeScopedEndpointNodeID(scope, address, port string) string {
	return scope + ScopeDelim + address + ScopeDelim + port
}

// MakeScopedAddressNodeID is like MakeAddressNodeID, but it always
// prefixes the ID witha scope.
func MakeScopedAddressNodeID(scope, address string) string {
	return scope + ScopeDelim + address
}

// MakeProcessNodeID produces a process node ID from its composite parts.
func MakeProcessNodeID(hostID, pid string) string {
	return hostID + ScopeDelim + pid
}

// MakeECSServiceNodeID produces an ECS Service node ID from its composite parts.
func MakeECSServiceNodeID(cluster, serviceName string) string {
	return cluster + ScopeDelim + serviceName
}

var (
	// MakeHostNodeID produces a host node ID from its composite parts.
	MakeHostNodeID = makeSingleComponentID("host")

	// ParseHostNodeID parses a host node ID
	ParseHostNodeID = parseSingleComponentID("host")

	// MakeContainerNodeID produces a container node ID from its composite parts.
	MakeContainerNodeID = makeSingleComponentID("container")

	// ParseContainerNodeID parses a container node ID
	ParseContainerNodeID = parseSingleComponentID("container")

	// MakeContainerImageNodeID produces a container image node ID from its composite parts.
	MakeContainerImageNodeID = makeSingleComponentID("container_image")

	// ParseContainerImageNodeID parses a container image node ID
	ParseContainerImageNodeID = parseSingleComponentID("container_image")

	// MakePodNodeID produces a pod node ID from its composite parts.
	MakePodNodeID = makeSingleComponentID("pod")

	// ParsePodNodeID parses a pod node ID
	ParsePodNodeID = parseSingleComponentID("pod")

	// MakeServiceNodeID produces a service node ID from its composite parts.
	MakeServiceNodeID = makeSingleComponentID("service")

	// ParseServiceNodeID parses a service node ID
	ParseServiceNodeID = parseSingleComponentID("service")

	// MakeDeploymentNodeID produces a deployment node ID from its composite parts.
	MakeDeploymentNodeID = makeSingleComponentID("deployment")

	// ParseDeploymentNodeID parses a deployment node ID
	ParseDeploymentNodeID = parseSingleComponentID("deployment")

	// MakeReplicaSetNodeID produces a replica set node ID from its composite parts.
	MakeReplicaSetNodeID = makeSingleComponentID("replica_set")

	// ParseReplicaSetNodeID parses a replica set node ID
	ParseReplicaSetNodeID = parseSingleComponentID("replica_set")

	// MakeDaemonSetNodeID produces a replica set node ID from its composite parts.
	MakeDaemonSetNodeID = makeSingleComponentID("daemonset")

	// ParseDaemonSetNodeID parses a daemon set node ID
	ParseDaemonSetNodeID = parseSingleComponentID("daemonset")

	// MakeStatefulSetNodeID produces a replica set node ID from its composite parts.
	MakeStatefulSetNodeID = makeSingleComponentID("statefulset")

	// ParseStatefulSetNodeID parses a daemon set node ID
	ParseStatefulSetNodeID = parseSingleComponentID("statefulset")

	// MakeCronJobNodeID produces a replica set node ID from its composite parts.
	MakeCronJobNodeID = makeSingleComponentID("cronjob")

	// ParseCronJobNodeID parses a daemon set node ID
	ParseCronJobNodeID = parseSingleComponentID("cronjob")

	// MakeECSTaskNodeID produces a replica set node ID from its composite parts.
	MakeECSTaskNodeID = makeSingleComponentID("ecs_task")

	// ParseECSTaskNodeID parses a replica set node ID
	ParseECSTaskNodeID = parseSingleComponentID("ecs_task")

	// MakeSwarmServiceNodeID produces a replica set node ID from its composite parts.
	MakeSwarmServiceNodeID = makeSingleComponentID("swarm_service")

	// ParseSwarmServiceNodeID parses a replica set node ID
	ParseSwarmServiceNodeID = parseSingleComponentID("swarm_service")
)

// makeSingleComponentID makes a single-component node id encoder
func makeSingleComponentID(tag string) func(string) string {
	return func(id string) string {
		return id + ScopeDelim + "<" + tag + ">"
	}
}

// parseSingleComponentID makes a single-component node id decoder
func parseSingleComponentID(tag string) func(string) (string, bool) {
	return func(id string) (string, bool) {
		field0, field1, ok := split2(id, ScopeDelim)
		if !ok || field1 != "<"+tag+">" {
			return "", false
		}
		return field0, true
	}
}

// MakeOverlayNodeID produces an overlay topology node ID from a router peer's
// prefix and name, which is assumed to be globally unique.
func MakeOverlayNodeID(peerPrefix, peerName string) string {
	return "#" + peerPrefix + peerName
}

// ParseOverlayNodeID produces the overlay type and peer name.
func ParseOverlayNodeID(id string) (overlayPrefix string, peerName string) {

	if !strings.HasPrefix(id, "#") {
		// Best we can do
		return "", ""
	}

	id = id[1:]

	if strings.HasPrefix(id, DockerOverlayPeerPrefix) {
		return DockerOverlayPeerPrefix, id[len(DockerOverlayPeerPrefix):]
	}

	return WeaveOverlayPeerPrefix, id
}

// Split a string s into two parts separated by sep.
func split2(s, sep string) (s1, s2 string, ok bool) {
	// Not using strings.SplitN() to avoid a heap allocation
	pos := strings.Index(s, sep)
	if pos == -1 {
		return "", "", false
	}
	return s[:pos], s[pos+1:], true
}

// ParseNodeID produces the id and tag of a single-component node ID.
func ParseNodeID(nodeID string) (id string, tag string, ok bool) {
	return split2(nodeID, ScopeDelim)
}

// ParseEndpointNodeID produces the scope, address, and port and remainder.
// Note that scope may be blank.
func ParseEndpointNodeID(endpointNodeID string) (scope, address, port string, ok bool) {
	// Not using strings.SplitN() to avoid a heap allocation
	first := strings.Index(endpointNodeID, ScopeDelim)
	if first == -1 {
		return "", "", "", false
	}
	second := strings.Index(endpointNodeID[first+1:], ScopeDelim)
	if second == -1 {
		return "", "", "", false
	}
	return endpointNodeID[:first], endpointNodeID[first+1 : first+1+second], endpointNodeID[first+1+second+1:], true
}

// ParseAddressNodeID produces the host ID, address from an address node ID.
func ParseAddressNodeID(addressNodeID string) (hostID, address string, ok bool) {
	return split2(addressNodeID, ScopeDelim)
}

// ParseProcessNodeID produces the host ID and PID from a process node ID.
func ParseProcessNodeID(processNodeID string) (hostID, pid string, ok bool) {
	return split2(processNodeID, ScopeDelim)
}

// ParseECSServiceNodeID produces the cluster, service name from an ECS Service node ID
func ParseECSServiceNodeID(ecsServiceNodeID string) (cluster, serviceName string, ok bool) {
	cluster, serviceName, ok = split2(ecsServiceNodeID, ScopeDelim)
	if !ok {
		return "", "", false
	}
	// In previous versions, ECS Service node IDs were of form serviceName + "<ecs_service>".
	// For backwards compatibility, we should still return a sensical serviceName for these cases.
	if serviceName == "<ecs_service>" {
		return "unknown", cluster, true
	}
	return cluster, serviceName, true
}

// ExtractHostID extracts the host id from Node
func ExtractHostID(m Node) string {
	hostNodeID, _ := m.Latest.Lookup(HostNodeID)
	hostID, _ := ParseHostNodeID(hostNodeID)
	return hostID
}

// IsLoopback ascertains if an address comes from a loopback interface.
func IsLoopback(address string) bool {
	ip := net.ParseIP(address)
	return ip != nil && ip.IsLoopback()
}
<commit_msg>Fix erroneous comments<commit_after>package report

import (
	"net"
	"strings"
)

// Delimiters are used to separate parts of node IDs, to guarantee uniqueness
// in particular contexts.
const (
	// ScopeDelim is a general-purpose delimiter used within node IDs to
	// separate different contextual scopes. Different topologies have
	// different key structures.
	ScopeDelim = ";"

	// EdgeDelim separates two node IDs when they need to exist in the same key.
	// Concretely, it separates node IDs in keys that represent edges.
	EdgeDelim = "|"

	// Key added to nodes to prevent them being joined with conntracked connections
	DoesNotMakeConnections = "does_not_make_connections"

	// WeaveOverlayPeerPrefix is the prefix for weave peers in the overlay network
	WeaveOverlayPeerPrefix = ""

	// DockerOverlayPeerPrefix is the prefix for docker peers in the overlay network
	DockerOverlayPeerPrefix = "docker_peer_"
)

// MakeEndpointNodeID produces an endpoint node ID from its composite parts.
func MakeEndpointNodeID(hostID, namespaceID, address, port string) string {
	return makeAddressID(hostID, namespaceID, address) + ScopeDelim + port
}

// MakeAddressNodeID produces an address node ID from its composite parts.
func MakeAddressNodeID(hostID, address string) string {
	return makeAddressID(hostID, "", address)
}

func makeAddressID(hostID, namespaceID, address string) string {
	var scope string

	// Loopback addresses and addresses explicitly marked as local get
	// scoped by hostID
	// Loopback addresses are also scoped by the networking
	// namespace if available, since they can clash.
	addressIP := net.ParseIP(address)
	if addressIP != nil && LocalNetworks.Contains(addressIP) {
		scope = hostID
	} else if IsLoopback(address) {
		scope = hostID
		if namespaceID != "" {
			scope += "-" + namespaceID
		}
	}

	return scope + ScopeDelim + address
}

// MakeScopedEndpointNodeID is like MakeEndpointNodeID, but it always
// prefixes the ID with a scope.
func MakeScopedEndpointNodeID(scope, address, port string) string {
	return scope + ScopeDelim + address + ScopeDelim + port
}

// MakeScopedAddressNodeID is like MakeAddressNodeID, but it always
// prefixes the ID witha scope.
func MakeScopedAddressNodeID(scope, address string) string {
	return scope + ScopeDelim + address
}

// MakeProcessNodeID produces a process node ID from its composite parts.
func MakeProcessNodeID(hostID, pid string) string {
	return hostID + ScopeDelim + pid
}

// MakeECSServiceNodeID produces an ECS Service node ID from its composite parts.
func MakeECSServiceNodeID(cluster, serviceName string) string {
	return cluster + ScopeDelim + serviceName
}

var (
	// MakeHostNodeID produces a host node ID from its composite parts.
	MakeHostNodeID = makeSingleComponentID("host")

	// ParseHostNodeID parses a host node ID
	ParseHostNodeID = parseSingleComponentID("host")

	// MakeContainerNodeID produces a container node ID from its composite parts.
	MakeContainerNodeID = makeSingleComponentID("container")

	// ParseContainerNodeID parses a container node ID
	ParseContainerNodeID = parseSingleComponentID("container")

	// MakeContainerImageNodeID produces a container image node ID from its composite parts.
	MakeContainerImageNodeID = makeSingleComponentID("container_image")

	// ParseContainerImageNodeID parses a container image node ID
	ParseContainerImageNodeID = parseSingleComponentID("container_image")

	// MakePodNodeID produces a pod node ID from its composite parts.
	MakePodNodeID = makeSingleComponentID("pod")

	// ParsePodNodeID parses a pod node ID
	ParsePodNodeID = parseSingleComponentID("pod")

	// MakeServiceNodeID produces a service node ID from its composite parts.
	MakeServiceNodeID = makeSingleComponentID("service")

	// ParseServiceNodeID parses a service node ID
	ParseServiceNodeID = parseSingleComponentID("service")

	// MakeDeploymentNodeID produces a deployment node ID from its composite parts.
	MakeDeploymentNodeID = makeSingleComponentID("deployment")

	// ParseDeploymentNodeID parses a deployment node ID
	ParseDeploymentNodeID = parseSingleComponentID("deployment")

	// MakeReplicaSetNodeID produces a replica set node ID from its composite parts.
	MakeReplicaSetNodeID = makeSingleComponentID("replica_set")

	// ParseReplicaSetNodeID parses a replica set node ID
	ParseReplicaSetNodeID = parseSingleComponentID("replica_set")

	// MakeDaemonSetNodeID produces a replica set node ID from its composite parts.
	MakeDaemonSetNodeID = makeSingleComponentID("daemonset")

	// ParseDaemonSetNodeID parses a daemon set node ID
	ParseDaemonSetNodeID = parseSingleComponentID("daemonset")

	// MakeStatefulSetNodeID produces a statefulset node ID from its composite parts.
	MakeStatefulSetNodeID = makeSingleComponentID("statefulset")

	// ParseStatefulSetNodeID parses a statefulset node ID
	ParseStatefulSetNodeID = parseSingleComponentID("statefulset")

	// MakeCronJobNodeID produces a cronjob node ID from its composite parts.
	MakeCronJobNodeID = makeSingleComponentID("cronjob")

	// ParseCronJobNodeID parses a cronjob node ID
	ParseCronJobNodeID = parseSingleComponentID("cronjob")

	// MakeECSTaskNodeID produces a ECSTask node ID from its composite parts.
	MakeECSTaskNodeID = makeSingleComponentID("ecs_task")

	// ParseECSTaskNodeID parses a ECSTask node ID
	ParseECSTaskNodeID = parseSingleComponentID("ecs_task")

	// MakeSwarmServiceNodeID produces a Swarm service node ID from its composite parts.
	MakeSwarmServiceNodeID = makeSingleComponentID("swarm_service")

	// ParseSwarmServiceNodeID parses a Swarm service node ID
	ParseSwarmServiceNodeID = parseSingleComponentID("swarm_service")
)

// makeSingleComponentID makes a single-component node id encoder
func makeSingleComponentID(tag string) func(string) string {
	return func(id string) string {
		return id + ScopeDelim + "<" + tag + ">"
	}
}

// parseSingleComponentID makes a single-component node id decoder
func parseSingleComponentID(tag string) func(string) (string, bool) {
	return func(id string) (string, bool) {
		field0, field1, ok := split2(id, ScopeDelim)
		if !ok || field1 != "<"+tag+">" {
			return "", false
		}
		return field0, true
	}
}

// MakeOverlayNodeID produces an overlay topology node ID from a router peer's
// prefix and name, which is assumed to be globally unique.
func MakeOverlayNodeID(peerPrefix, peerName string) string {
	return "#" + peerPrefix + peerName
}

// ParseOverlayNodeID produces the overlay type and peer name.
func ParseOverlayNodeID(id string) (overlayPrefix string, peerName string) {

	if !strings.HasPrefix(id, "#") {
		// Best we can do
		return "", ""
	}

	id = id[1:]

	if strings.HasPrefix(id, DockerOverlayPeerPrefix) {
		return DockerOverlayPeerPrefix, id[len(DockerOverlayPeerPrefix):]
	}

	return WeaveOverlayPeerPrefix, id
}

// Split a string s into two parts separated by sep.
func split2(s, sep string) (s1, s2 string, ok bool) {
	// Not using strings.SplitN() to avoid a heap allocation
	pos := strings.Index(s, sep)
	if pos == -1 {
		return "", "", false
	}
	return s[:pos], s[pos+1:], true
}

// ParseNodeID produces the id and tag of a single-component node ID.
func ParseNodeID(nodeID string) (id string, tag string, ok bool) {
	return split2(nodeID, ScopeDelim)
}

// ParseEndpointNodeID produces the scope, address, and port and remainder.
// Note that scope may be blank.
func ParseEndpointNodeID(endpointNodeID string) (scope, address, port string, ok bool) {
	// Not using strings.SplitN() to avoid a heap allocation
	first := strings.Index(endpointNodeID, ScopeDelim)
	if first == -1 {
		return "", "", "", false
	}
	second := strings.Index(endpointNodeID[first+1:], ScopeDelim)
	if second == -1 {
		return "", "", "", false
	}
	return endpointNodeID[:first], endpointNodeID[first+1 : first+1+second], endpointNodeID[first+1+second+1:], true
}

// ParseAddressNodeID produces the host ID, address from an address node ID.
func ParseAddressNodeID(addressNodeID string) (hostID, address string, ok bool) {
	return split2(addressNodeID, ScopeDelim)
}

// ParseProcessNodeID produces the host ID and PID from a process node ID.
func ParseProcessNodeID(processNodeID string) (hostID, pid string, ok bool) {
	return split2(processNodeID, ScopeDelim)
}

// ParseECSServiceNodeID produces the cluster, service name from an ECS Service node ID
func ParseECSServiceNodeID(ecsServiceNodeID string) (cluster, serviceName string, ok bool) {
	cluster, serviceName, ok = split2(ecsServiceNodeID, ScopeDelim)
	if !ok {
		return "", "", false
	}
	// In previous versions, ECS Service node IDs were of form serviceName + "<ecs_service>".
	// For backwards compatibility, we should still return a sensical serviceName for these cases.
	if serviceName == "<ecs_service>" {
		return "unknown", cluster, true
	}
	return cluster, serviceName, true
}

// ExtractHostID extracts the host id from Node
func ExtractHostID(m Node) string {
	hostNodeID, _ := m.Latest.Lookup(HostNodeID)
	hostID, _ := ParseHostNodeID(hostNodeID)
	return hostID
}

// IsLoopback ascertains if an address comes from a loopback interface.
func IsLoopback(address string) bool {
	ip := net.ParseIP(address)
	return ip != nil && ip.IsLoopback()
}
<|endoftext|>


<commit_before>package main

import "github.com/mistifyio/gozfs/nv"

func holds(name string) ([]string, error) {
	m := map[string]interface{}{
		"cmd":     "zfs_get_holds",
		"version": uint64(0),
	}

	encoded, err := nv.Encode(m)
	if err != nil {
		return nil, err
	}

	out := make([]byte, 1024)
	err = ioctl(zfs, name, encoded, out)
	if err != nil {
		return nil, err
	}

	m = map[string]interface{}{}

	if err = nv.Decode(out, &m); err != nil {
		return nil, err
	}

	names := make([]string, 0, len(m))
	for name := range m {
		names = append(names, name)
	}

	return names, nil
}
<commit_msg>gozfs/holds: handle snapshot with no holds<commit_after>package main

import "github.com/mistifyio/gozfs/nv"

const emptyList = "\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"

func holds(name string) ([]string, error) {
	m := map[string]interface{}{
		"cmd":     "zfs_get_holds",
		"version": uint64(0),
	}

	encoded, err := nv.Encode(m)
	if err != nil {
		return nil, err
	}

	out := make([]byte, 1024)
	copy(out, emptyList)

	err = ioctl(zfs, name, encoded, out)
	if err != nil {
		return nil, err
	}

	m = map[string]interface{}{}

	if err = nv.Decode(out, &m); err != nil {
		return nil, err
	}

	names := make([]string, 0, len(m))
	for name := range m {
		names = append(names, name)
	}

	return names, nil
}
<|endoftext|>


<commit_before>package goproxy

import (
	"bufio"
	"crypto/tls"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync/atomic"
)

type ConnectActionLiteral int

const (
	ConnectAccept = iota
	ConnectReject
	ConnectMitm
)

var (
	OkConnect     = &ConnectAction{Action: ConnectAccept}
	MitmConnect   = &ConnectAction{Action: ConnectMitm}
	RejectConnect = &ConnectAction{Action: ConnectReject}
)

type ConnectAction struct {
	Action    ConnectActionLiteral
	TlsConfig *tls.Config
	Ca        *tls.Certificate
}

func stripPort(s string) string {
	ix := strings.IndexRune(s, ':')
	if ix == -1 {
		return s
	}
	return s[:ix]
}

func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request) {
	ctx := &ProxyCtx{Req: r, Session: atomic.AddInt64(&proxy.sess, 1), proxy: proxy}

	hij, ok := w.(http.Hijacker)
	if !ok {
		panic("httpserver does not support hijacking")
	}

	proxyClient, _, e := hij.Hijack()
	if e != nil {
		panic("Cannot hijack connection " + e.Error())
	}

	ctx.Logf("Running %d CONNECT handlers", len(proxy.httpsHandlers))
	todo, host := OkConnect, r.URL.Host
	ctx.Req = r
	for _, h := range proxy.httpsHandlers {
		newtodo, newhost := h.HandleConnect(host, ctx)
		if newtodo != nil {
			todo, host = newtodo, newhost
		}
		ctx.Logf("handler: %v %s", todo, host)
	}
	switch todo.Action {
	case ConnectAccept:
		if !hasPort.MatchString(host) {
			host += ":80"
		}
		https_proxy := os.Getenv("https_proxy")
		if https_proxy == "" {
			https_proxy = os.Getenv("HTTPS_PROXY")
		}
		var targetSiteCon net.Conn
		var e error
		if https_proxy != "" {
			targetSiteCon, e = net.Dial("tcp", https_proxy)
		} else {
			targetSiteCon, e = net.Dial("tcp", host)
		}
		if e != nil {
			// trying to mimic the behaviour of the offending website
			// don't answer at all
			return
		}
		if https_proxy != "" {
			connectReq := &http.Request{
				Method: "CONNECT",
				URL:    &url.URL{Opaque: host},
				Host:   host,
				Header: make(http.Header),
			}
			connectReq.Write(targetSiteCon)

			// Read response.
			// Okay to use and discard buffered reader here, because
			// TLS server will not speak until spoken to.
			br := bufio.NewReader(targetSiteCon)
			resp, err := http.ReadResponse(br, connectReq)
			if err != nil {
				targetSiteCon.Close()
				w.WriteHeader(500)
				return
			}
			if resp.StatusCode != 200 {
				targetSiteCon.Close()
				w.WriteHeader(resp.StatusCode)
				io.Copy(w, resp.Body)
				resp.Body.Close()
				return
			}
		}
		ctx.Logf("Accepting CONNECT to %s", host)
		proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
		go proxy.copyAndClose(targetSiteCon, proxyClient)
		go proxy.copyAndClose(proxyClient, targetSiteCon)
	case ConnectMitm:
		proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
		ctx.Logf("Assuming CONNECT is TLS, mitm proxying it")
		// this goes in a separate goroutine, so that the net/http server won't think we're
		// still handling the request even after hijacking the connection. Those HTTP CONNECT
		// request can take forever, and the server will be stuck when "closed".
		// TODO: Allow Server.Close() mechanism to shut down this connection as nicely as possible
		ca := todo.Ca
		if ca == nil {
			ca = &GoproxyCa
		}
		cert, err := signHost(*ca, []string{stripPort(host)})
		if err != nil {
			ctx.Warnf("Cannot sign host certificate with provided CA: %s", err)
			return
		}
		tlsConfig := tls.Config{}
		if todo.TlsConfig != nil {
			tlsConfig = *todo.TlsConfig
		} else {
			tlsConfig = *defaultTlsConfig
		}
		tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
		go func() {
			//TODO: cache connections to the remote website
			rawClientTls := tls.Server(proxyClient, &tlsConfig)
			if err := rawClientTls.Handshake(); err != nil {
				ctx.Warnf("Cannot handshake client %v %v", r.Host, err)
				return
			}
			defer rawClientTls.Close()
			clientTlsReader := bufio.NewReader(rawClientTls)
			for !isEof(clientTlsReader) {
				req, err := http.ReadRequest(clientTlsReader)
				if err != nil {
					ctx.Warnf("Cannot read TLS request from mitm'd client %v %v", r.Host, err)
					return
				}
				ctx.Logf("req %v", r.Host)
				req, resp := proxy.filterRequest(req, ctx)
				if resp == nil {
					req.URL, err = url.Parse("https://" + r.Host + req.URL.String())
					if err != nil {
						ctx.Warnf("Illegal URL %s", "https://"+r.Host+req.URL.Path)
						return
					}
					removeProxyHeaders(ctx, req)
					resp, err = proxy.Tr.RoundTrip(req)
					if err != nil {
						ctx.Warnf("Cannot read TLS response from mitm'd server %v", err)
						return
					}
					ctx.Logf("resp %v", resp.Status)
				}
				resp = proxy.filterResponse(resp, ctx)
				text := resp.Status
				statusCode := strconv.Itoa(resp.StatusCode) + " "
				if strings.HasPrefix(text, statusCode) {
					text = text[len(statusCode):]
				}
				// always use 1.1 to support encoding
				if _, err := io.WriteString(rawClientTls, "HTTP/1.1"+" "+statusCode+text+"\r\n"); err != nil {
					ctx.Warnf("Cannot write TLS response HTTP status from mitm'd client: %v", err)
					return
				}
				// Since we don't know the length of resp, return chunked encoded response
				// TODO: use a more reasonable scheme
				resp.Header.Del("Content-Length")
				resp.Header.Set("Transfer-Encoding", "chunked")
				if err := resp.Header.Write(rawClientTls); err != nil {
					ctx.Warnf("Cannot write TLS response header from mitm'd client: %v", err)
					return
				}
				if _, err = io.WriteString(rawClientTls, "\r\n"); err != nil {
					ctx.Warnf("Cannot write TLS response header end from mitm'd client: %v", err)
					return
				}
				chunked := newChunkedWriter(rawClientTls)
				if _, err := io.Copy(chunked, resp.Body); err != nil {
					ctx.Warnf("Cannot write TLS response body from mitm'd client: %v", err)
					return
				}
				if err := chunked.Close(); err != nil {
					ctx.Warnf("Cannot write TLS chunked EOF from mitm'd client: %v", err)
					return
				}
				if _, err = io.WriteString(rawClientTls, "\r\n"); err != nil {
					ctx.Warnf("Cannot write TLS response chunked trailer from mitm'd client: %v", err)
					return
				}
			}
			ctx.Logf("Exiting on EOF")
		}()
	case ConnectReject:
		if ctx.Resp != nil {
			if err := ctx.Resp.Write(proxyClient); err != nil {
				ctx.Warnf("Cannot write response that reject http CONNECT: %v", err)
			}
		}
		proxyClient.Close()
	}
}
<commit_msg>fixes #32, https should also use proxy.Tr.Dial to make connections<commit_after>package goproxy

import (
	"bufio"
	"crypto/tls"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"sync/atomic"
)

type ConnectActionLiteral int

const (
	ConnectAccept = iota
	ConnectReject
	ConnectMitm
)

var (
	OkConnect     = &ConnectAction{Action: ConnectAccept}
	MitmConnect   = &ConnectAction{Action: ConnectMitm}
	RejectConnect = &ConnectAction{Action: ConnectReject}
)

type ConnectAction struct {
	Action    ConnectActionLiteral
	TlsConfig *tls.Config
	Ca        *tls.Certificate
}

func stripPort(s string) string {
	ix := strings.IndexRune(s, ':')
	if ix == -1 {
		return s
	}
	return s[:ix]
}

func (proxy *ProxyHttpServer) dial(network, addr string) (c net.Conn, err error) {
	if proxy.Tr.Dial != nil {
		return proxy.Tr.Dial(network, addr)
	}
	return net.Dial(network, addr)
}

func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request) {
	ctx := &ProxyCtx{Req: r, Session: atomic.AddInt64(&proxy.sess, 1), proxy: proxy}

	hij, ok := w.(http.Hijacker)
	if !ok {
		panic("httpserver does not support hijacking")
	}

	proxyClient, _, e := hij.Hijack()
	if e != nil {
		panic("Cannot hijack connection " + e.Error())
	}

	ctx.Logf("Running %d CONNECT handlers", len(proxy.httpsHandlers))
	todo, host := OkConnect, r.URL.Host
	ctx.Req = r
	for _, h := range proxy.httpsHandlers {
		newtodo, newhost := h.HandleConnect(host, ctx)
		if newtodo != nil {
			todo, host = newtodo, newhost
		}
		ctx.Logf("handler: %v %s", todo, host)
	}
	switch todo.Action {
	case ConnectAccept:
		if !hasPort.MatchString(host) {
			host += ":80"
		}
		https_proxy := os.Getenv("https_proxy")
		if https_proxy == "" {
			https_proxy = os.Getenv("HTTPS_PROXY")
		}
		var targetSiteCon net.Conn
		var e error
		if https_proxy != "" {
			targetSiteCon, e = proxy.dial("tcp", https_proxy)
		} else {
			targetSiteCon, e = proxy.dial("tcp", host)
		}
		if e != nil {
			// trying to mimic the behaviour of the offending website
			// don't answer at all
			return
		}
		if https_proxy != "" {
			connectReq := &http.Request{
				Method: "CONNECT",
				URL:    &url.URL{Opaque: host},
				Host:   host,
				Header: make(http.Header),
			}
			connectReq.Write(targetSiteCon)

			// Read response.
			// Okay to use and discard buffered reader here, because
			// TLS server will not speak until spoken to.
			br := bufio.NewReader(targetSiteCon)
			resp, err := http.ReadResponse(br, connectReq)
			if err != nil {
				targetSiteCon.Close()
				w.WriteHeader(500)
				return
			}
			if resp.StatusCode != 200 {
				targetSiteCon.Close()
				w.WriteHeader(resp.StatusCode)
				io.Copy(w, resp.Body)
				resp.Body.Close()
				return
			}
		}
		ctx.Logf("Accepting CONNECT to %s", host)
		proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
		go proxy.copyAndClose(targetSiteCon, proxyClient)
		go proxy.copyAndClose(proxyClient, targetSiteCon)
	case ConnectMitm:
		proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
		ctx.Logf("Assuming CONNECT is TLS, mitm proxying it")
		// this goes in a separate goroutine, so that the net/http server won't think we're
		// still handling the request even after hijacking the connection. Those HTTP CONNECT
		// request can take forever, and the server will be stuck when "closed".
		// TODO: Allow Server.Close() mechanism to shut down this connection as nicely as possible
		ca := todo.Ca
		if ca == nil {
			ca = &GoproxyCa
		}
		cert, err := signHost(*ca, []string{stripPort(host)})
		if err != nil {
			ctx.Warnf("Cannot sign host certificate with provided CA: %s", err)
			return
		}
		tlsConfig := tls.Config{}
		if todo.TlsConfig != nil {
			tlsConfig = *todo.TlsConfig
		} else {
			tlsConfig = *defaultTlsConfig
		}
		tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
		go func() {
			//TODO: cache connections to the remote website
			rawClientTls := tls.Server(proxyClient, &tlsConfig)
			if err := rawClientTls.Handshake(); err != nil {
				ctx.Warnf("Cannot handshake client %v %v", r.Host, err)
				return
			}
			defer rawClientTls.Close()
			clientTlsReader := bufio.NewReader(rawClientTls)
			for !isEof(clientTlsReader) {
				req, err := http.ReadRequest(clientTlsReader)
				if err != nil {
					ctx.Warnf("Cannot read TLS request from mitm'd client %v %v", r.Host, err)
					return
				}
				ctx.Logf("req %v", r.Host)
				req, resp := proxy.filterRequest(req, ctx)
				if resp == nil {
					req.URL, err = url.Parse("https://" + r.Host + req.URL.String())
					if err != nil {
						ctx.Warnf("Illegal URL %s", "https://"+r.Host+req.URL.Path)
						return
					}
					removeProxyHeaders(ctx, req)
					resp, err = proxy.Tr.RoundTrip(req)
					if err != nil {
						ctx.Warnf("Cannot read TLS response from mitm'd server %v", err)
						return
					}
					ctx.Logf("resp %v", resp.Status)
				}
				resp = proxy.filterResponse(resp, ctx)
				text := resp.Status
				statusCode := strconv.Itoa(resp.StatusCode) + " "
				if strings.HasPrefix(text, statusCode) {
					text = text[len(statusCode):]
				}
				// always use 1.1 to support encoding
				if _, err := io.WriteString(rawClientTls, "HTTP/1.1"+" "+statusCode+text+"\r\n"); err != nil {
					ctx.Warnf("Cannot write TLS response HTTP status from mitm'd client: %v", err)
					return
				}
				// Since we don't know the length of resp, return chunked encoded response
				// TODO: use a more reasonable scheme
				resp.Header.Del("Content-Length")
				resp.Header.Set("Transfer-Encoding", "chunked")
				if err := resp.Header.Write(rawClientTls); err != nil {
					ctx.Warnf("Cannot write TLS response header from mitm'd client: %v", err)
					return
				}
				if _, err = io.WriteString(rawClientTls, "\r\n"); err != nil {
					ctx.Warnf("Cannot write TLS response header end from mitm'd client: %v", err)
					return
				}
				chunked := newChunkedWriter(rawClientTls)
				if _, err := io.Copy(chunked, resp.Body); err != nil {
					ctx.Warnf("Cannot write TLS response body from mitm'd client: %v", err)
					return
				}
				if err := chunked.Close(); err != nil {
					ctx.Warnf("Cannot write TLS chunked EOF from mitm'd client: %v", err)
					return
				}
				if _, err = io.WriteString(rawClientTls, "\r\n"); err != nil {
					ctx.Warnf("Cannot write TLS response chunked trailer from mitm'd client: %v", err)
					return
				}
			}
			ctx.Logf("Exiting on EOF")
		}()
	case ConnectReject:
		if ctx.Resp != nil {
			if err := ctx.Resp.Write(proxyClient); err != nil {
				ctx.Warnf("Cannot write response that reject http CONNECT: %v", err)
			}
		}
		proxyClient.Close()
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Tom Thorogood. All rights reserved.
// Use of this source code is governed by a Modified
// BSD License that can be found in the LICENSE file.

package id3v2

//go:generate go run generate_ids.go

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"sync"
	"unicode/utf16"
)

// This is an implementation of v2.4.0 of the ID3v2 tagging format,
// defined in: http://id3.org/id3v2.4.0-structure, and v2.3.0 of
// the ID3v2 tagging format, defined in: http://id3.org/id3v2.3.0.

const (
	flagUnsynchronisation = 1 << (7 - iota)
	flagExtendedHeader
	flagExperimental
	flagFooter

	knownFlags = flagUnsynchronisation | flagExtendedHeader |
		flagExperimental | flagFooter
)

type FrameID uint32

const syncsafeInvalid = ^uint32(0)

func syncsafe(data []byte) uint32 {
	_ = data[3]

	if data[0]&0x80 != 0 || data[1]&0x80 != 0 ||
		data[2]&0x80 != 0 || data[3]&0x80 != 0 {
		return syncsafeInvalid
	}

	return uint32(data[0])<<21 | uint32(data[1])<<14 |
		uint32(data[2])<<7 | uint32(data[3])
}

func id3Split(data []byte, atEOF bool) (advance int, token []byte, err error) {
	i := bytes.Index(data, []byte("ID3"))
	if i == -1 {
		if len(data) < 2 {
			return 0, nil, nil
		}

		return len(data) - 2, nil, nil
	}

	data = data[i:]
	if len(data) < 10 {
		if atEOF {
			return 0, nil, io.ErrUnexpectedEOF
		}

		return i, nil, nil
	}

	size := syncsafe(data[6:])

	if data[3] == 0xff || data[4] == 0xff || size == syncsafeInvalid {
		// Skipping when we find the string "ID3" in the file but
		// the remaining header is invalid is consistent with the
		// detection logic in §3.1. This also reduces the
		// likelihood of errors being caused by the byte sequence
		// "ID3" (49 44 33) occuring in the audio, but does not
		// eliminate the possibility of errors in this case.
		//
		// Quoting from §3.1 of id3v2.4.0-structure.txt:
		//   An ID3v2 tag can be detected with the following pattern:
		//     $49 44 33 yy yy xx zz zz zz zz
		//   Where yy is less than $FF, xx is the 'flags' byte and zz
		//   is less than $80.
		return i + 3, nil, nil
	}

	if data[3] > 0x05 {
		// Quoting from §3.1 of id3v2.4.0-structure.txt:
		//   If software with ID3v2.4.0 and below support should
		//   encounter version five or higher it should simply
		//   ignore the whole tag.
		return i + 3, nil, nil
	}

	if data[3] < 0x03 {
		// This package only supports v2.3.0 and v2.4.0, skip
		// versions bellow v2.3.0.
		return i + 3, nil, nil
	}

	if data[5]&^knownFlags != 0 {
		// Skip tag blocks that contain unknown flags.
		//
		// Quoting from §3.1 of id3v2.4.0-structure.txt:
		//   If one of these undefined flags are set, the tag might
		//   not be readable for a parser that does not know the
		//   flags function.
		return i + 3, nil, nil
	}

	if data[5]&flagFooter == flagFooter {
		size += 10
	}

	if len(data) < 10+int(size) {
		if atEOF {
			return 0, nil, io.ErrUnexpectedEOF
		}

		return i, nil, nil
	}

	return i + 10 + int(size), data[:10+size], nil
}

const invalidFrameID = ^FrameID(0)

func validIDByte(b byte) bool {
	return (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')
}

func frameID(data []byte) FrameID {
	_ = data[3]

	if validIDByte(data[0]) && validIDByte(data[1]) && validIDByte(data[2]) &&
		// Although it violates the specification, some software
		// incorrectly encodes v2.2.0 three character tags as
		// four character v2.3.0 tags with a trailing zero byte
		// when upgrading the tagging format version.
		(validIDByte(data[3]) || data[3] == 0) {
		return FrameID(binary.BigEndian.Uint32(data))
	}

	for _, v := range data {
		if v != 0 {
			return invalidFrameID
		}
	}

	// This is probably the begging of padding.
	return 0
}

var bufPool = &sync.Pool{
	New: func() interface{} {
		buf := make([]byte, 4<<10)
		return &buf
	},
}

func Scan(r io.Reader) (ID3Frames, error) {
	buf := bufPool.Get()
	defer bufPool.Put(buf)

	s := bufio.NewScanner(r)
	s.Buffer(*buf.(*[]byte), 1<<28)
	s.Split(id3Split)

	var frames ID3Frames

scan:
	for s.Scan() {
		data := s.Bytes()

		header := data[:10]
		data = data[10:]

		if string(header[:3]) != "ID3" {
			panic("id3: bufio.Scanner failed")
		}

		version := header[3]
		switch version {
		case 0x04, 0x03:
		default:
			continue scan
		}

		flags := header[5]

		if flags&flagFooter == flagFooter {
			footer := data[len(data)-10:]
			data = data[:len(data)-10]

			if string(footer[:3]) != "3DI" ||
				!bytes.Equal(header[3:], footer[3:]) {
				return nil, errors.New("id3: invalid footer")
			}
		}

		if flags&flagExtendedHeader == flagExtendedHeader {
			size := syncsafe(data)
			if size == syncsafeInvalid || len(data) < int(size) {
				return nil, errors.New("id3: invalid extended header")
			}

			extendedHeader := data[:size]
			data = data[size:]

			_ = extendedHeader
		}

	frames:
		for len(data) > 10 {
			_ = data[9]

			frame := &ID3Frame{
				ID:      frameID(data),
				Version: version,
				Flags:   binary.BigEndian.Uint16(data[8:]),
			}

			switch frame.ID {
			case 0:
				// We've probably hit padding, the padding
				// validity check below will handle this.
				break frames
			case invalidFrameID:
				return nil, errors.New("id3: invalid frame id")
			}

			var size uint32
			switch version {
			case 0x04:
				size = syncsafe(data[4:])
				if size == syncsafeInvalid {
					return nil, errors.New("id3: invalid frame size")
				}
			case 0x03:
				size = binary.BigEndian.Uint32(data[4:])
			default:
				panic("unhandled version")
			}

			if len(data) < 10+int(size) {
				return nil, errors.New("id3: frame size exceeds length of tag data")
			}

			if flags&flagUnsynchronisation == flagUnsynchronisation {
				frame.Data = make([]byte, 0, size)

				for i := uint32(0); i < size; i++ {
					v := data[10+i]
					frame.Data = append(frame.Data, v)

					if v == 0xff && i+1 < size && data[10+i+1] == 0x00 {
						i++
					}
				}
			} else {
				frame.Data = append([]byte(nil), data[10:10+size]...)
			}

			frames = append(frames, frame)
			data = data[10+size:]
		}

		if flags&flagFooter == flagFooter && len(data) != 0 {
			return nil, errors.New("id3: padding with footer")
		}

		for _, v := range data {
			if v != 0 {
				return nil, errors.New("id3: invalid padding")
			}
		}
	}

	if s.Err() != nil {
		return nil, s.Err()
	}

	return frames, nil
}

type ID3Frames []*ID3Frame

func (f ID3Frames) Lookup(id FrameID) *ID3Frame {
	for i := len(f) - 1; i >= 0; i-- {
		if f[i].ID == id {
			return f[i]
		}
	}

	return nil
}

type ID3Frame struct {
	ID      FrameID
	Version byte
	Flags   uint16
	Data    []byte
}

func (f *ID3Frame) String() string {
	data, terminus := f.Data, ""
	if len(data) > 128 {
		data, terminus = data[:128], "..."
	}

	var version string
	switch f.Version {
	case 0x04:
		version = "v2.4"
	case 0x03:
		version = "v2.3"
	default:
		version = "?"
	}

	return fmt.Sprintf("&ID3Frame{ID: %s, Version: %s, Flags: 0x%04x, Data: %d:%q%s}",
		f.ID.String(), version, f.Flags, len(f.Data), data, terminus)
}

func (f *ID3Frame) Text() (string, error) {
	if len(f.Data) < 2 {
		return "", errors.New("id3: frame data is invalid")
	}

	if f.Flags&0xff != 0 {
		return "", errors.New("id3: frame flags are not supported")
	}

	data := f.Data[1:]
	var ord binary.ByteOrder = binary.BigEndian

	switch f.Data[0] {
	case 0x00:
		for _, v := range data {
			if v&0x80 == 0 {
				continue
			}

			runes := make([]rune, len(data))
			for i, v := range data {
				runes[i] = rune(v)
			}

			return string(runes), nil
		}

		fallthrough
	case 0x03:
		if data[len(data)-1] == 0x00 {
			// The specification requires that the string be
			// terminated with 0x00, but not all implementations
			// do this.
			data = data[:len(data)-1]
		}

		return string(data), nil
	case 0x01:
		if len(data) < 2 {
			return "", errors.New("id3: missing UTF-16 BOM")
		}

		if data[0] == 0xff && data[1] == 0xfe {
			ord = binary.LittleEndian
		} else if data[0] == 0xfe && data[1] == 0xff {
			ord = binary.BigEndian
		} else {
			return "", errors.New("id3: invalid UTF-16 BOM")
		}

		data = data[2:]
		fallthrough
	case 0x02:
		if len(data)%2 != 0 {
			return "", errors.New("id3: UTF-16 data is not even number of bytes")
		}

		u16s := make([]uint16, len(data)/2)
		for i := range u16s {
			u16s[i] = ord.Uint16(data[i*2:])
		}

		if u16s[len(u16s)-1] == 0x0000 {
			// The specification requires that the string be
			// terminated with 0x00 0x00, but not all
			// implementations do this.
			u16s = u16s[:len(u16s)-1]
		}

		return string(utf16.Decode(u16s)), nil
	default:
		return "", errors.New("id3: frame uses unsupported encoding")
	}
}
<commit_msg>Add frame flags constants<commit_after>// Copyright 2017 Tom Thorogood. All rights reserved.
// Use of this source code is governed by a Modified
// BSD License that can be found in the LICENSE file.

package id3v2

//go:generate go run generate_ids.go

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"sync"
	"unicode/utf16"
)

// This is an implementation of v2.4.0 of the ID3v2 tagging format,
// defined in: http://id3.org/id3v2.4.0-structure, and v2.3.0 of
// the ID3v2 tagging format, defined in: http://id3.org/id3v2.3.0.

const (
	tagFlagUnsynchronisation = 1 << (7 - iota)
	tagFlagExtendedHeader
	tagFlagExperimental
	tagFlagFooter

	knownTagFlags = tagFlagUnsynchronisation | tagFlagExtendedHeader |
		tagFlagExperimental | tagFlagFooter
)

type FrameFlags uint16

const (
	_ FrameFlags = 1 << (15 - iota)
	FrameFlagV24TagAlterPreservation
	FrameFlagV24FileAlterPreservation
	FrameFlagV24ReadOnly
	_
	_
	_
	_
	_
	FrameFlagV24GroupingIdentity
	_
	_
	FrameFlagV24Compression
	FrameFlagV24Encryption
	FrameFlagV24Unsynchronisation
	FrameFlagV24DataLengthIndicator
)

const (
	FrameFlagV23TagAlterPreservation FrameFlags = 1 << (15 - iota)
	FrameFlagV23FileAlterPreservation
	FrameFlagV23ReadOnly
	_
	_
	_
	_
	_
	FrameFlagV23Compression
	FrameFlagV23Encryption
	FrameFlagV23GroupingIdentity
)

type FrameID uint32

const syncsafeInvalid = ^uint32(0)

func syncsafe(data []byte) uint32 {
	_ = data[3]

	if data[0]&0x80 != 0 || data[1]&0x80 != 0 ||
		data[2]&0x80 != 0 || data[3]&0x80 != 0 {
		return syncsafeInvalid
	}

	return uint32(data[0])<<21 | uint32(data[1])<<14 |
		uint32(data[2])<<7 | uint32(data[3])
}

func id3Split(data []byte, atEOF bool) (advance int, token []byte, err error) {
	i := bytes.Index(data, []byte("ID3"))
	if i == -1 {
		if len(data) < 2 {
			return 0, nil, nil
		}

		return len(data) - 2, nil, nil
	}

	data = data[i:]
	if len(data) < 10 {
		if atEOF {
			return 0, nil, io.ErrUnexpectedEOF
		}

		return i, nil, nil
	}

	size := syncsafe(data[6:])

	if data[3] == 0xff || data[4] == 0xff || size == syncsafeInvalid {
		// Skipping when we find the string "ID3" in the file but
		// the remaining header is invalid is consistent with the
		// detection logic in §3.1. This also reduces the
		// likelihood of errors being caused by the byte sequence
		// "ID3" (49 44 33) occuring in the audio, but does not
		// eliminate the possibility of errors in this case.
		//
		// Quoting from §3.1 of id3v2.4.0-structure.txt:
		//   An ID3v2 tag can be detected with the following pattern:
		//     $49 44 33 yy yy xx zz zz zz zz
		//   Where yy is less than $FF, xx is the 'flags' byte and zz
		//   is less than $80.
		return i + 3, nil, nil
	}

	if data[3] > 0x05 {
		// Quoting from §3.1 of id3v2.4.0-structure.txt:
		//   If software with ID3v2.4.0 and below support should
		//   encounter version five or higher it should simply
		//   ignore the whole tag.
		return i + 3, nil, nil
	}

	if data[3] < 0x03 {
		// This package only supports v2.3.0 and v2.4.0, skip
		// versions bellow v2.3.0.
		return i + 3, nil, nil
	}

	if data[5]&^knownTagFlags != 0 {
		// Skip tag blocks that contain unknown flags.
		//
		// Quoting from §3.1 of id3v2.4.0-structure.txt:
		//   If one of these undefined flags are set, the tag might
		//   not be readable for a parser that does not know the
		//   flags function.
		return i + 3, nil, nil
	}

	if data[5]&tagFlagFooter == tagFlagFooter {
		size += 10
	}

	if len(data) < 10+int(size) {
		if atEOF {
			return 0, nil, io.ErrUnexpectedEOF
		}

		return i, nil, nil
	}

	return i + 10 + int(size), data[:10+size], nil
}

const invalidFrameID = ^FrameID(0)

func validIDByte(b byte) bool {
	return (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')
}

func frameID(data []byte) FrameID {
	_ = data[3]

	if validIDByte(data[0]) && validIDByte(data[1]) && validIDByte(data[2]) &&
		// Although it violates the specification, some software
		// incorrectly encodes v2.2.0 three character tags as
		// four character v2.3.0 tags with a trailing zero byte
		// when upgrading the tagging format version.
		(validIDByte(data[3]) || data[3] == 0) {
		return FrameID(binary.BigEndian.Uint32(data))
	}

	for _, v := range data {
		if v != 0 {
			return invalidFrameID
		}
	}

	// This is probably the begging of padding.
	return 0
}

var bufPool = &sync.Pool{
	New: func() interface{} {
		buf := make([]byte, 4<<10)
		return &buf
	},
}

func Scan(r io.Reader) (ID3Frames, error) {
	buf := bufPool.Get()
	defer bufPool.Put(buf)

	s := bufio.NewScanner(r)
	s.Buffer(*buf.(*[]byte), 1<<28)
	s.Split(id3Split)

	var frames ID3Frames

scan:
	for s.Scan() {
		data := s.Bytes()

		header := data[:10]
		data = data[10:]

		if string(header[:3]) != "ID3" {
			panic("id3: bufio.Scanner failed")
		}

		version := header[3]
		switch version {
		case 0x04, 0x03:
		default:
			continue scan
		}

		flags := header[5]

		if flags&tagFlagFooter == tagFlagFooter {
			footer := data[len(data)-10:]
			data = data[:len(data)-10]

			if string(footer[:3]) != "3DI" ||
				!bytes.Equal(header[3:], footer[3:]) {
				return nil, errors.New("id3: invalid footer")
			}
		}

		if flags&tagFlagExtendedHeader == tagFlagExtendedHeader {
			size := syncsafe(data)
			if size == syncsafeInvalid || len(data) < int(size) {
				return nil, errors.New("id3: invalid extended header")
			}

			extendedHeader := data[:size]
			data = data[size:]

			_ = extendedHeader
		}

	frames:
		for len(data) > 10 {
			_ = data[9]

			frame := &ID3Frame{
				ID:      frameID(data),
				Version: version,
				Flags:   FrameFlags(binary.BigEndian.Uint16(data[8:])),
			}

			switch frame.ID {
			case 0:
				// We've probably hit padding, the padding
				// validity check below will handle this.
				break frames
			case invalidFrameID:
				return nil, errors.New("id3: invalid frame id")
			}

			var size uint32
			switch version {
			case 0x04:
				size = syncsafe(data[4:])
				if size == syncsafeInvalid {
					return nil, errors.New("id3: invalid frame size")
				}
			case 0x03:
				size = binary.BigEndian.Uint32(data[4:])
			default:
				panic("unhandled version")
			}

			if len(data) < 10+int(size) {
				return nil, errors.New("id3: frame size exceeds length of tag data")
			}

			if flags&flagUnsynchronisation == flagUnsynchronisation {
				frame.Data = make([]byte, 0, size)

				for i := uint32(0); i < size; i++ {
					v := data[10+i]
					frame.Data = append(frame.Data, v)

					if v == 0xff && i+1 < size && data[10+i+1] == 0x00 {
						i++
					}
				}
			} else {
				frame.Data = append([]byte(nil), data[10:10+size]...)
			}

			frames = append(frames, frame)
			data = data[10+size:]
		}

		if flags&tagFlagFooter == tagFlagFooter && len(data) != 0 {
			return nil, errors.New("id3: padding with footer")
		}

		for _, v := range data {
			if v != 0 {
				return nil, errors.New("id3: invalid padding")
			}
		}
	}

	if s.Err() != nil {
		return nil, s.Err()
	}

	return frames, nil
}

type ID3Frames []*ID3Frame

func (f ID3Frames) Lookup(id FrameID) *ID3Frame {
	for i := len(f) - 1; i >= 0; i-- {
		if f[i].ID == id {
			return f[i]
		}
	}

	return nil
}

type ID3Frame struct {
	ID      FrameID
	Version byte
	Flags   FrameFlags
	Data    []byte
}

func (f *ID3Frame) String() string {
	data, terminus := f.Data, ""
	if len(data) > 128 {
		data, terminus = data[:128], "..."
	}

	var version string
	switch f.Version {
	case 0x04:
		version = "v2.4"
	case 0x03:
		version = "v2.3"
	default:
		version = "?"
	}

	return fmt.Sprintf("&ID3Frame{ID: %s, Version: %s, Flags: 0x%04x, Data: %d:%q%s}",
		f.ID.String(), version, f.Flags, len(f.Data), data, terminus)
}

func (f *ID3Frame) Text() (string, error) {
	if len(f.Data) < 2 {
		return "", errors.New("id3: frame data is invalid")
	}

	if f.Flags&0xff != 0 {
		return "", errors.New("id3: frame flags are not supported")
	}

	data := f.Data[1:]
	var ord binary.ByteOrder = binary.BigEndian

	switch f.Data[0] {
	case 0x00:
		for _, v := range data {
			if v&0x80 == 0 {
				continue
			}

			runes := make([]rune, len(data))
			for i, v := range data {
				runes[i] = rune(v)
			}

			return string(runes), nil
		}

		fallthrough
	case 0x03:
		if data[len(data)-1] == 0x00 {
			// The specification requires that the string be
			// terminated with 0x00, but not all implementations
			// do this.
			data = data[:len(data)-1]
		}

		return string(data), nil
	case 0x01:
		if len(data) < 2 {
			return "", errors.New("id3: missing UTF-16 BOM")
		}

		if data[0] == 0xff && data[1] == 0xfe {
			ord = binary.LittleEndian
		} else if data[0] == 0xfe && data[1] == 0xff {
			ord = binary.BigEndian
		} else {
			return "", errors.New("id3: invalid UTF-16 BOM")
		}

		data = data[2:]
		fallthrough
	case 0x02:
		if len(data)%2 != 0 {
			return "", errors.New("id3: UTF-16 data is not even number of bytes")
		}

		u16s := make([]uint16, len(data)/2)
		for i := range u16s {
			u16s[i] = ord.Uint16(data[i*2:])
		}

		if u16s[len(u16s)-1] == 0x0000 {
			// The specification requires that the string be
			// terminated with 0x00 0x00, but not all
			// implementations do this.
			u16s = u16s[:len(u16s)-1]
		}

		return string(utf16.Decode(u16s)), nil
	default:
		return "", errors.New("id3: frame uses unsupported encoding")
	}
}
<|endoftext|>