<commit_before>package main

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"math/big"
	"strings"
	"testing"
)

func BenchmarkPath(b *testing.B) {

	//key for tag
	D := new(big.Int)
	D, _ = new(big.Int).SetString("399681106706823979931786798252509423226869972"+
		"6722344370689730210711314983767775860556101498400185744208447673206609026"+
		"128894016152514163591905578729891874833", 10) //this number is the key
	privT := PrivteKeyFromD(*D)
	keyT := elliptic.Marshal(privT.PublicKey.Curve,
		privT.PublicKey.X, privT.PublicKey.Y)
	hkidT, _ := hex.DecodeString(GenerateHKID(privT)) //gen HKID for tag
	err := PostKey(privT)                             //place key for tag
	if err != nil {
		fmt.Println(err)
	}
	err = PostBlob(keyT) //store public key
	if err != nil {
		fmt.Println(err)
	}

	//key for commit
	D, _ = new(big.Int).SetString("462981482389329648001641133480879383618612455"+
		"9723200979962176753724976464088706463001383556112424820911870650421151988"+
		"906751710824965155500230480521264034469", 10)
	privC := PrivteKeyFromD(*D)
	keyC := elliptic.Marshal(privC.PublicKey.Curve,
		privC.PublicKey.X, privC.PublicKey.Y)
	hkidC, _ := hex.DecodeString(GenerateHKID(privC)) //gen HKID for commit
	err = PostKey(privC)                              //place key for commit
	if err != nil {
		fmt.Println(err)
	}
	err = PostBlob(keyC) //store public key
	if err != nil {
		fmt.Println(err)
	}

	for i := 0; i < b.N; i++ {
		//Post blob
		testBlob := blob([]byte("testing")) //gen test blob
		err = PostBlob(testBlob)            //store test blob
		if err != nil {
			fmt.Println(err)
		}

		//post tag
		testTagPointingToTestBlob := NewTag(testBlob.Hash(),
			"blob",
			"testBlob",
			hkidT) //gen test tag
		err = PostTag(testTagPointingToTestBlob) //post test tag
		if err != nil {
			fmt.Println(err)
		}

		//post list
		testListPiontingToTestTag := NewList(testTagPointingToTestBlob.Hkid(),
			"tag",
			"testTag") //gen test list
		err = PostBlob(testListPiontingToTestTag.Bytes()) //store test list
		if err != nil {
			fmt.Println(err)
		}

		// post commit
		testCommitPointingToTestList := NewCommit(testListPiontingToTestTag.Hash(),
			hkidC) //gen test commit
		err = PostCommit(testCommitPointingToTestList) //post test commit
		if err != nil {
			fmt.Println(err)
		}

		//print
		//fmt.Printf("%s\n%s\n%s\n%s\n",
		//	hex.EncodeToString(testBlob.Hash()),
		//	hex.EncodeToString(testTagPointingToTestBlob.Hash()),
		//	hex.EncodeToString(testListPiontingToTestTag.Hash()),
		//	hex.EncodeToString(testCommitPointingToTestList.Hash()))

		//get commit
		hkid, _ := hex.DecodeString("1312ac161875b270da2ae4e1471ba94a" +
			"9883419250caa4c2f1fd80a91b37907e")
		testcommit, err := GetCommit(hkid)
		if err != nil {
			panic(err)
		}

		fmt.Printf("authentic commit:%v\n", testcommit.Verifiy())

		//get list
		listbytes, err := GetBlob(testcommit.listHash)
		if err != nil {
			panic(err)
		}
		listEntries := strings.Split(string(listbytes), "\n")
		entries := []entry{}
		cols := []string{}
		for _, element := range listEntries {
			cols = strings.Split(element, ",")
			entryHash, _ := hex.DecodeString(cols[0])
			entryTypeString := cols[1]
			entryNameSegment := cols[2]
			entries = append(entries, entry{entryHash, entryTypeString, entryNameSegment})
		}
		testlist := list(entries)
		fmt.Printf("authentic list:%v\n", bytes.Equal(testcommit.listHash, testlist.Hash()))
		//get tag
		testTag, err := GetTag(testlist[0].Hash, "testBlob")
		fmt.Printf("authentic tag:%v\n", testTag.Verifiy())
		//get blob
		testBlob, _ = GetBlob(testTag.HashBytes)
		fmt.Printf("authentic blob:%v\n", bytes.Equal(testTag.HashBytes, testBlob.Hash()))
	}
}

func TestKeyGen(b *testing.T) {
	c := elliptic.P521()
	priv, err := ecdsa.GenerateKey(c, rand.Reader)
	if err != nil {
		b.Errorf("Error %v", err)
	}
	fmt.Printf("TestKeyGen\nX = %v\nY = %v\nD = %v\n", priv.PublicKey.X, priv.PublicKey.Y, priv.D)
	err = PostKey(priv)
	if err != nil {
		b.Errorf("Error %v", err)
	}
	PostBlob(elliptic.Marshal(priv.PublicKey.Curve,
		priv.PublicKey.X, priv.PublicKey.Y))
}
<commit_msg>added store to filesystem for comparasan<commit_after>package main

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"math/big"
	"strings"
	"testing"
)

func BenchmarkStoreOne(b *testing.B) {
	for i := 0; i < b.N; i++ {
		err := ioutil.WriteFile("../storeone", []byte("storeone"), 0664)
		if err != nil {
			panic(err)
		}
		data, err := ioutil.ReadFile("../storeone")
		if err != nil {
			panic(err)
		}
		fmt.Print(data)
	}
}

func BenchmarkPath(b *testing.B) {

	//key for tag
	D := new(big.Int)
	D, _ = new(big.Int).SetString("399681106706823979931786798252509423226869972"+
		"6722344370689730210711314983767775860556101498400185744208447673206609026"+
		"128894016152514163591905578729891874833", 10) //this number is the key
	privT := PrivteKeyFromD(*D)
	keyT := elliptic.Marshal(privT.PublicKey.Curve,
		privT.PublicKey.X, privT.PublicKey.Y)
	hkidT, _ := hex.DecodeString(GenerateHKID(privT)) //gen HKID for tag
	err := PostKey(privT)                             //place key for tag
	if err != nil {
		fmt.Println(err)
	}
	err = PostBlob(keyT) //store public key
	if err != nil {
		fmt.Println(err)
	}

	//key for commit
	D, _ = new(big.Int).SetString("462981482389329648001641133480879383618612455"+
		"9723200979962176753724976464088706463001383556112424820911870650421151988"+
		"906751710824965155500230480521264034469", 10)
	privC := PrivteKeyFromD(*D)
	keyC := elliptic.Marshal(privC.PublicKey.Curve,
		privC.PublicKey.X, privC.PublicKey.Y)
	hkidC, _ := hex.DecodeString(GenerateHKID(privC)) //gen HKID for commit
	err = PostKey(privC)                              //place key for commit
	if err != nil {
		fmt.Println(err)
	}
	err = PostBlob(keyC) //store public key
	if err != nil {
		fmt.Println(err)
	}

	for i := 0; i < b.N; i++ {
		//Post blob
		testBlob := blob([]byte("testing")) //gen test blob
		err = PostBlob(testBlob)            //store test blob
		if err != nil {
			fmt.Println(err)
		}

		//post tag
		testTagPointingToTestBlob := NewTag(testBlob.Hash(),
			"blob",
			"testBlob",
			hkidT) //gen test tag
		err = PostTag(testTagPointingToTestBlob) //post test tag
		if err != nil {
			fmt.Println(err)
		}

		//post list
		testListPiontingToTestTag := NewList(testTagPointingToTestBlob.Hkid(),
			"tag",
			"testTag") //gen test list
		err = PostBlob(testListPiontingToTestTag.Bytes()) //store test list
		if err != nil {
			fmt.Println(err)
		}

		// post commit
		testCommitPointingToTestList := NewCommit(testListPiontingToTestTag.Hash(),
			hkidC) //gen test commit
		err = PostCommit(testCommitPointingToTestList) //post test commit
		if err != nil {
			fmt.Println(err)
		}

		//print
		//fmt.Printf("%s\n%s\n%s\n%s\n",
		//	hex.EncodeToString(testBlob.Hash()),
		//	hex.EncodeToString(testTagPointingToTestBlob.Hash()),
		//	hex.EncodeToString(testListPiontingToTestTag.Hash()),
		//	hex.EncodeToString(testCommitPointingToTestList.Hash()))

		//get commit
		hkid, _ := hex.DecodeString("1312ac161875b270da2ae4e1471ba94a" +
			"9883419250caa4c2f1fd80a91b37907e")
		testcommit, err := GetCommit(hkid)
		if err != nil {
			panic(err)
		}

		fmt.Printf("authentic commit:%v\n", testcommit.Verifiy())

		//get list
		listbytes, err := GetBlob(testcommit.listHash)
		if err != nil {
			panic(err)
		}
		listEntries := strings.Split(string(listbytes), "\n")
		entries := []entry{}
		cols := []string{}
		for _, element := range listEntries {
			cols = strings.Split(element, ",")
			entryHash, _ := hex.DecodeString(cols[0])
			entryTypeString := cols[1]
			entryNameSegment := cols[2]
			entries = append(entries, entry{entryHash, entryTypeString, entryNameSegment})
		}
		testlist := list(entries)
		fmt.Printf("authentic list:%v\n", bytes.Equal(testcommit.listHash, testlist.Hash()))
		//get tag
		testTag, err := GetTag(testlist[0].Hash, "testBlob")
		fmt.Printf("authentic tag:%v\n", testTag.Verifiy())
		//get blob
		testBlob, _ = GetBlob(testTag.HashBytes)
		fmt.Printf("authentic blob:%v\n", bytes.Equal(testTag.HashBytes, testBlob.Hash()))
	}
}

func TestKeyGen(b *testing.T) {
	c := elliptic.P521()
	priv, err := ecdsa.GenerateKey(c, rand.Reader)
	if err != nil {
		b.Errorf("Error %v", err)
	}
	fmt.Printf("TestKeyGen\nX = %v\nY = %v\nD = %v\n", priv.PublicKey.X, priv.PublicKey.Y, priv.D)
	err = PostKey(priv)
	if err != nil {
		b.Errorf("Error %v", err)
	}
	PostBlob(elliptic.Marshal(priv.PublicKey.Curve,
		priv.PublicKey.X, priv.PublicKey.Y))
}
<|endoftext|>


<commit_before>/*
Copyright 2020-2021 Gravitational, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package webclient provides a client for the Teleport Proxy API endpoints.
package webclient

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"

	"github.com/gravitational/teleport/api/constants"
	"github.com/gravitational/teleport/api/defaults"
	"github.com/gravitational/trace"
)

// newWebClient creates a new client to the HTTPS web proxy.
func newWebClient(insecure bool, pool *x509.CertPool) *http.Client {
	return &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				RootCAs:            pool,
				InsecureSkipVerify: insecure,
			},
		},
	}
}

// Find fetches discovery data by connecting to the given web proxy address.
// It is designed to fetch proxy public addresses without any inefficiencies.
func Find(ctx context.Context, proxyAddr string, insecure bool, pool *x509.CertPool) (*PingResponse, error) {
	clt := newWebClient(insecure, pool)
	defer clt.CloseIdleConnections()

	endpoint := fmt.Sprintf("https://%s/webapi/find", proxyAddr)
	resp, err := clt.Get(endpoint)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	defer resp.Body.Close()
	pr := &PingResponse{}
	if err := json.NewDecoder(resp.Body).Decode(pr); err != nil {
		return nil, trace.Wrap(err)
	}

	return pr, nil
}

// Ping serves two purposes. The first is to validate the HTTP endpoint of a
// Teleport proxy. This leads to better user experience: users get connection
// errors before being asked for passwords. The second is to return the form
// of authentication that the server supports. This also leads to better user
// experience: users only get prompted for the type of authentication the server supports.
func Ping(ctx context.Context, proxyAddr string, insecure bool, pool *x509.CertPool, connectorName string) (*PingResponse, error) {
	clt := newWebClient(insecure, pool)
	defer clt.CloseIdleConnections()

	endpoint := fmt.Sprintf("https://%s/webapi/ping", proxyAddr)
	if connectorName != "" {
		endpoint = fmt.Sprintf("%s/%s", endpoint, connectorName)
	}

	resp, err := clt.Get(endpoint)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	defer resp.Body.Close()
	pr := &PingResponse{}
	if err := json.NewDecoder(resp.Body).Decode(pr); err != nil {
		return nil, trace.Wrap(err)
	}

	return pr, nil
}

// GetTunnelAddr returns the tunnel address either set in an environment variable or retrieved from the web proxy.
func GetTunnelAddr(ctx context.Context, proxyAddr string, insecure bool, pool *x509.CertPool) (string, error) {
	// If TELEPORT_TUNNEL_PUBLIC_ADDR is set, nothing else has to be done, return it.
	if tunnelAddr := os.Getenv(defaults.TunnelPublicAddrEnvar); tunnelAddr != "" {
		return extractHostPort(tunnelAddr)
	}

	// Ping web proxy to retrieve tunnel proxy address.
	pr, err := Find(ctx, proxyAddr, insecure, nil)
	if err != nil {
		return "", trace.Wrap(err)
	}
	return tunnelAddr(proxyAddr, pr.Proxy.SSH)
}

func GetMOTD(ctx context.Context, proxyAddr string, insecure bool, pool *x509.CertPool) (*MotD, error) {
	clt := newWebClient(insecure, pool)
	defer clt.CloseIdleConnections()

	endpoint := fmt.Sprintf("https://%s/webapi/motd", proxyAddr)

	resp, err := clt.Get(endpoint)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, trace.BadParameter("failed to fetch message of the day: %d", resp.StatusCode)
	}

	motd := &MotD{}
	if err := json.NewDecoder(resp.Body).Decode(motd); err != nil {
		return nil, trace.Wrap(err)
	}

	return motd, nil
}

// MotD holds data about the current message of the day.
type MotD struct {
	Text string
}

// PingResponse contains data about the Teleport server like supported
// authentication types, server version, etc.
type PingResponse struct {
	// Auth contains the forms of authentication the auth server supports.
	Auth AuthenticationSettings `json:"auth"`
	// Proxy contains the proxy settings.
	Proxy ProxySettings `json:"proxy"`
	// ServerVersion is the version of Teleport that is running.
	ServerVersion string `json:"server_version"`
	// MinClientVersion is the minimum client version required by the server.
	MinClientVersion string `json:"min_client_version"`
}

// ProxySettings contains basic information about proxy settings
type ProxySettings struct {
	// Kube is a kubernetes specific proxy section
	Kube KubeProxySettings `json:"kube"`
	// SSH is SSH specific proxy settings
	SSH SSHProxySettings `json:"ssh"`
	// DB contains database access specific proxy settings
	DB DBProxySettings `json:"db"`
}

// KubeProxySettings is kubernetes proxy settings
type KubeProxySettings struct {
	// Enabled is true when kubernetes proxy is enabled
	Enabled bool `json:"enabled,omitempty"`
	// PublicAddr is a kubernetes proxy public address if set
	PublicAddr string `json:"public_addr,omitempty"`
	// ListenAddr is the address that the kubernetes proxy is listening for
	// connections on.
	ListenAddr string `json:"listen_addr,omitempty"`
}

// SSHProxySettings is SSH specific proxy settings.
type SSHProxySettings struct {
	// ListenAddr is the address that the SSH proxy is listening for
	// connections on.
	ListenAddr string `json:"listen_addr,omitempty"`

	// TunnelListenAddr is the address that the SSH reverse tunnel is
	// listening for connections on.
	TunnelListenAddr string `json:"tunnel_listen_addr,omitempty"`

	// PublicAddr is the public address of the HTTP proxy.
	PublicAddr string `json:"public_addr,omitempty"`

	// SSHPublicAddr is the public address of the SSH proxy.
	SSHPublicAddr string `json:"ssh_public_addr,omitempty"`

	// TunnelPublicAddr is the public address of the SSH reverse tunnel.
	TunnelPublicAddr string `json:"ssh_tunnel_public_addr,omitempty"`
}

// DBProxySettings contains database access specific proxy settings.
type DBProxySettings struct {
	// PostgresPublicAddr is advertised to Postgres clients.
	PostgresPublicAddr string `json:"postgres_public_addr,omitempty"`
	// MySQLListenAddr is MySQL proxy listen address.
	MySQLListenAddr string `json:"mysql_listen_addr,omitempty"`
	// MySQLPublicAddr is advertised to MySQL clients.
	MySQLPublicAddr string `json:"mysql_public_addr,omitempty"`
}

// PingResponse contains the form of authentication the auth server supports.
type AuthenticationSettings struct {
	// Type is the type of authentication, can be either local or oidc.
	Type string `json:"type"`
	// SecondFactor is the type of second factor to use in authentication.
	// Supported options are: off, otp, and u2f.
	SecondFactor constants.SecondFactorType `json:"second_factor,omitempty"`
	// U2F contains the Universal Second Factor settings needed for authentication.
	U2F *U2FSettings `json:"u2f,omitempty"`
	// OIDC contains OIDC connector settings needed for authentication.
	OIDC *OIDCSettings `json:"oidc,omitempty"`
	// SAML contains SAML connector settings needed for authentication.
	SAML *SAMLSettings `json:"saml,omitempty"`
	// Github contains Github connector settings needed for authentication.
	Github *GithubSettings `json:"github,omitempty"`

	// HasMessageOfTheDay is a flag indicating that the cluster has MOTD
	// banner text that must be retrieved, displayed and acknowledged by
	// the user.
	HasMessageOfTheDay bool `json:"has_motd"`
}

// U2FSettings contains the AppID for Universal Second Factor.
type U2FSettings struct {
	// AppID is the U2F AppID.
	AppID string `json:"app_id"`
}

// SAMLSettings contains the Name and Display string for SAML
type SAMLSettings struct {
	// Name is the internal name of the connector.
	Name string `json:"name"`
	// Display is the display name for the connector.
	Display string `json:"display"`
}

// OIDCSettings contains the Name and Display string for OIDC.
type OIDCSettings struct {
	// Name is the internal name of the connector.
	Name string `json:"name"`
	// Display is the display name for the connector.
	Display string `json:"display"`
}

// GithubSettings contains the Name and Display string for Github connector.
type GithubSettings struct {
	// Name is the internal name of the connector
	Name string `json:"name"`
	// Display is the connector display name
	Display string `json:"display"`
}

// The tunnel addr is retrieved in the following preference order:
//  1. Reverse Tunnel Public Address.
//  2. SSH Proxy Public Address Host + Tunnel Port.
//  3. HTTP Proxy Public Address Host + Tunnel Port.
//  4. Proxy Address Host + Tunnel Port.
func tunnelAddr(proxyAddr string, settings SSHProxySettings) (string, error) {
	// If a tunnel public address is set, nothing else has to be done, return it.
	if settings.TunnelPublicAddr != "" {
		return extractHostPort(settings.TunnelPublicAddr)
	}

	// Extract the port the tunnel server is listening on.
	tunnelPort := strconv.Itoa(defaults.SSHProxyTunnelListenPort)
	if settings.TunnelListenAddr != "" {
		if port, err := extractPort(settings.TunnelListenAddr); err == nil {
			tunnelPort = port
		}
	}

	// If a tunnel public address has not been set, but a related HTTP or SSH
	// public address has been set, extract the hostname but use the port from
	// the tunnel listen address.
	if settings.SSHPublicAddr != "" {
		if host, err := extractHost(settings.SSHPublicAddr); err == nil {
			return net.JoinHostPort(host, tunnelPort), nil
		}
	}
	if settings.PublicAddr != "" {
		if host, err := extractHost(settings.PublicAddr); err == nil {
			return net.JoinHostPort(host, tunnelPort), nil
		}
	}

	// If nothing is set, fallback to the address dialed with tunnel port.
	host, err := extractHost(proxyAddr)
	if err != nil {
		return "", trace.Wrap(err, "failed to parse the given proxy address")
	}
	return net.JoinHostPort(host, tunnelPort), nil
}

// extractHostPort takes addresses like "tcp://host:port/path" and returns "host:port".
func extractHostPort(addr string) (string, error) {
	if addr == "" {
		return "", trace.BadParameter("missing parameter address")
	}
	if !strings.Contains(addr, "://") {
		addr = "tcp://" + addr
	}
	u, err := url.Parse(addr)
	if err != nil {
		return "", trace.BadParameter("failed to parse %q: %v", addr, err)
	}
	switch u.Scheme {
	case "tcp", "http", "https":
		return u.Host, nil
	default:
		return "", trace.BadParameter("'%v': unsupported scheme: '%v'", addr, u.Scheme)
	}
}

// extractHost takes addresses like "tcp://host:port/path" and returns "host".
func extractHost(addr string) (ra string, err error) {
	parsed, err := extractHostPort(addr)
	if err != nil {
		return "", trace.Wrap(err)
	}
	host, _, err := net.SplitHostPort(parsed)
	if err != nil {
		if strings.Contains(err.Error(), "missing port in address") {
			return addr, nil
		}
		return "", trace.Wrap(err)
	}
	return host, nil
}

// extractPort takes addresses like "tcp://host:port/path" and returns "port".
func extractPort(addr string) (string, error) {
	parsed, err := extractHostPort(addr)
	if err != nil {
		return "", trace.Wrap(err)
	}
	_, port, err := net.SplitHostPort(parsed)
	if err != nil {
		return "", trace.Wrap(err)
	}
	return port, nil
}
<commit_msg>webclient: use the provided context (#7801)<commit_after>/*
Copyright 2020-2021 Gravitational, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package webclient provides a client for the Teleport Proxy API endpoints.
package webclient

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"

	"github.com/gravitational/teleport/api/constants"
	"github.com/gravitational/teleport/api/defaults"
	"github.com/gravitational/trace"
)

// newWebClient creates a new client to the HTTPS web proxy.
func newWebClient(insecure bool, pool *x509.CertPool) *http.Client {
	return &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				RootCAs:            pool,
				InsecureSkipVerify: insecure,
			},
		},
	}
}

// Find fetches discovery data by connecting to the given web proxy address.
// It is designed to fetch proxy public addresses without any inefficiencies.
func Find(ctx context.Context, proxyAddr string, insecure bool, pool *x509.CertPool) (*PingResponse, error) {
	clt := newWebClient(insecure, pool)
	defer clt.CloseIdleConnections()

	endpoint := fmt.Sprintf("https://%s/webapi/find", proxyAddr)

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	resp, err := clt.Do(req)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	defer resp.Body.Close()
	pr := &PingResponse{}
	if err := json.NewDecoder(resp.Body).Decode(pr); err != nil {
		return nil, trace.Wrap(err)
	}

	return pr, nil
}

// Ping serves two purposes. The first is to validate the HTTP endpoint of a
// Teleport proxy. This leads to better user experience: users get connection
// errors before being asked for passwords. The second is to return the form
// of authentication that the server supports. This also leads to better user
// experience: users only get prompted for the type of authentication the server supports.
func Ping(ctx context.Context, proxyAddr string, insecure bool, pool *x509.CertPool, connectorName string) (*PingResponse, error) {
	clt := newWebClient(insecure, pool)
	defer clt.CloseIdleConnections()

	endpoint := fmt.Sprintf("https://%s/webapi/ping", proxyAddr)
	if connectorName != "" {
		endpoint = fmt.Sprintf("%s/%s", endpoint, connectorName)
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	resp, err := clt.Do(req)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	defer resp.Body.Close()
	pr := &PingResponse{}
	if err := json.NewDecoder(resp.Body).Decode(pr); err != nil {
		return nil, trace.Wrap(err)
	}

	return pr, nil
}

// GetTunnelAddr returns the tunnel address either set in an environment variable or retrieved from the web proxy.
func GetTunnelAddr(ctx context.Context, proxyAddr string, insecure bool, pool *x509.CertPool) (string, error) {
	// If TELEPORT_TUNNEL_PUBLIC_ADDR is set, nothing else has to be done, return it.
	if tunnelAddr := os.Getenv(defaults.TunnelPublicAddrEnvar); tunnelAddr != "" {
		return extractHostPort(tunnelAddr)
	}

	// Ping web proxy to retrieve tunnel proxy address.
	pr, err := Find(ctx, proxyAddr, insecure, nil)
	if err != nil {
		return "", trace.Wrap(err)
	}
	return tunnelAddr(proxyAddr, pr.Proxy.SSH)
}

func GetMOTD(ctx context.Context, proxyAddr string, insecure bool, pool *x509.CertPool) (*MotD, error) {
	clt := newWebClient(insecure, pool)
	defer clt.CloseIdleConnections()

	endpoint := fmt.Sprintf("https://%s/webapi/motd", proxyAddr)

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, trace.Wrap(err)
	}

	resp, err := clt.Do(req)
	if err != nil {
		return nil, trace.Wrap(err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, trace.BadParameter("failed to fetch message of the day: %d", resp.StatusCode)
	}

	motd := &MotD{}
	if err := json.NewDecoder(resp.Body).Decode(motd); err != nil {
		return nil, trace.Wrap(err)
	}

	return motd, nil
}

// MotD holds data about the current message of the day.
type MotD struct {
	Text string
}

// PingResponse contains data about the Teleport server like supported
// authentication types, server version, etc.
type PingResponse struct {
	// Auth contains the forms of authentication the auth server supports.
	Auth AuthenticationSettings `json:"auth"`
	// Proxy contains the proxy settings.
	Proxy ProxySettings `json:"proxy"`
	// ServerVersion is the version of Teleport that is running.
	ServerVersion string `json:"server_version"`
	// MinClientVersion is the minimum client version required by the server.
	MinClientVersion string `json:"min_client_version"`
}

// ProxySettings contains basic information about proxy settings
type ProxySettings struct {
	// Kube is a kubernetes specific proxy section
	Kube KubeProxySettings `json:"kube"`
	// SSH is SSH specific proxy settings
	SSH SSHProxySettings `json:"ssh"`
	// DB contains database access specific proxy settings
	DB DBProxySettings `json:"db"`
}

// KubeProxySettings is kubernetes proxy settings
type KubeProxySettings struct {
	// Enabled is true when kubernetes proxy is enabled
	Enabled bool `json:"enabled,omitempty"`
	// PublicAddr is a kubernetes proxy public address if set
	PublicAddr string `json:"public_addr,omitempty"`
	// ListenAddr is the address that the kubernetes proxy is listening for
	// connections on.
	ListenAddr string `json:"listen_addr,omitempty"`
}

// SSHProxySettings is SSH specific proxy settings.
type SSHProxySettings struct {
	// ListenAddr is the address that the SSH proxy is listening for
	// connections on.
	ListenAddr string `json:"listen_addr,omitempty"`

	// TunnelListenAddr is the address that the SSH reverse tunnel is
	// listening for connections on.
	TunnelListenAddr string `json:"tunnel_listen_addr,omitempty"`

	// PublicAddr is the public address of the HTTP proxy.
	PublicAddr string `json:"public_addr,omitempty"`

	// SSHPublicAddr is the public address of the SSH proxy.
	SSHPublicAddr string `json:"ssh_public_addr,omitempty"`

	// TunnelPublicAddr is the public address of the SSH reverse tunnel.
	TunnelPublicAddr string `json:"ssh_tunnel_public_addr,omitempty"`
}

// DBProxySettings contains database access specific proxy settings.
type DBProxySettings struct {
	// PostgresPublicAddr is advertised to Postgres clients.
	PostgresPublicAddr string `json:"postgres_public_addr,omitempty"`
	// MySQLListenAddr is MySQL proxy listen address.
	MySQLListenAddr string `json:"mysql_listen_addr,omitempty"`
	// MySQLPublicAddr is advertised to MySQL clients.
	MySQLPublicAddr string `json:"mysql_public_addr,omitempty"`
}

// PingResponse contains the form of authentication the auth server supports.
type AuthenticationSettings struct {
	// Type is the type of authentication, can be either local or oidc.
	Type string `json:"type"`
	// SecondFactor is the type of second factor to use in authentication.
	// Supported options are: off, otp, and u2f.
	SecondFactor constants.SecondFactorType `json:"second_factor,omitempty"`
	// U2F contains the Universal Second Factor settings needed for authentication.
	U2F *U2FSettings `json:"u2f,omitempty"`
	// OIDC contains OIDC connector settings needed for authentication.
	OIDC *OIDCSettings `json:"oidc,omitempty"`
	// SAML contains SAML connector settings needed for authentication.
	SAML *SAMLSettings `json:"saml,omitempty"`
	// Github contains Github connector settings needed for authentication.
	Github *GithubSettings `json:"github,omitempty"`

	// HasMessageOfTheDay is a flag indicating that the cluster has MOTD
	// banner text that must be retrieved, displayed and acknowledged by
	// the user.
	HasMessageOfTheDay bool `json:"has_motd"`
}

// U2FSettings contains the AppID for Universal Second Factor.
type U2FSettings struct {
	// AppID is the U2F AppID.
	AppID string `json:"app_id"`
}

// SAMLSettings contains the Name and Display string for SAML
type SAMLSettings struct {
	// Name is the internal name of the connector.
	Name string `json:"name"`
	// Display is the display name for the connector.
	Display string `json:"display"`
}

// OIDCSettings contains the Name and Display string for OIDC.
type OIDCSettings struct {
	// Name is the internal name of the connector.
	Name string `json:"name"`
	// Display is the display name for the connector.
	Display string `json:"display"`
}

// GithubSettings contains the Name and Display string for Github connector.
type GithubSettings struct {
	// Name is the internal name of the connector
	Name string `json:"name"`
	// Display is the connector display name
	Display string `json:"display"`
}

// The tunnel addr is retrieved in the following preference order:
//  1. Reverse Tunnel Public Address.
//  2. SSH Proxy Public Address Host + Tunnel Port.
//  3. HTTP Proxy Public Address Host + Tunnel Port.
//  4. Proxy Address Host + Tunnel Port.
func tunnelAddr(proxyAddr string, settings SSHProxySettings) (string, error) {
	// If a tunnel public address is set, nothing else has to be done, return it.
	if settings.TunnelPublicAddr != "" {
		return extractHostPort(settings.TunnelPublicAddr)
	}

	// Extract the port the tunnel server is listening on.
	tunnelPort := strconv.Itoa(defaults.SSHProxyTunnelListenPort)
	if settings.TunnelListenAddr != "" {
		if port, err := extractPort(settings.TunnelListenAddr); err == nil {
			tunnelPort = port
		}
	}

	// If a tunnel public address has not been set, but a related HTTP or SSH
	// public address has been set, extract the hostname but use the port from
	// the tunnel listen address.
	if settings.SSHPublicAddr != "" {
		if host, err := extractHost(settings.SSHPublicAddr); err == nil {
			return net.JoinHostPort(host, tunnelPort), nil
		}
	}
	if settings.PublicAddr != "" {
		if host, err := extractHost(settings.PublicAddr); err == nil {
			return net.JoinHostPort(host, tunnelPort), nil
		}
	}

	// If nothing is set, fallback to the address dialed with tunnel port.
	host, err := extractHost(proxyAddr)
	if err != nil {
		return "", trace.Wrap(err, "failed to parse the given proxy address")
	}
	return net.JoinHostPort(host, tunnelPort), nil
}

// extractHostPort takes addresses like "tcp://host:port/path" and returns "host:port".
func extractHostPort(addr string) (string, error) {
	if addr == "" {
		return "", trace.BadParameter("missing parameter address")
	}
	if !strings.Contains(addr, "://") {
		addr = "tcp://" + addr
	}
	u, err := url.Parse(addr)
	if err != nil {
		return "", trace.BadParameter("failed to parse %q: %v", addr, err)
	}
	switch u.Scheme {
	case "tcp", "http", "https":
		return u.Host, nil
	default:
		return "", trace.BadParameter("'%v': unsupported scheme: '%v'", addr, u.Scheme)
	}
}

// extractHost takes addresses like "tcp://host:port/path" and returns "host".
func extractHost(addr string) (ra string, err error) {
	parsed, err := extractHostPort(addr)
	if err != nil {
		return "", trace.Wrap(err)
	}
	host, _, err := net.SplitHostPort(parsed)
	if err != nil {
		if strings.Contains(err.Error(), "missing port in address") {
			return addr, nil
		}
		return "", trace.Wrap(err)
	}
	return host, nil
}

// extractPort takes addresses like "tcp://host:port/path" and returns "port".
func extractPort(addr string) (string, error) {
	parsed, err := extractHostPort(addr)
	if err != nil {
		return "", trace.Wrap(err)
	}
	_, port, err := net.SplitHostPort(parsed)
	if err != nil {
		return "", trace.Wrap(err)
	}
	return port, nil
}
<|endoftext|>


<commit_before>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package monitor

import (
	"sync"

	"github.com/TheThingsNetwork/ttn/api"
	"github.com/TheThingsNetwork/ttn/api/broker"
	"github.com/TheThingsNetwork/ttn/api/gateway"
	"github.com/TheThingsNetwork/ttn/api/router"
	"github.com/TheThingsNetwork/ttn/utils/errors"
	"github.com/apex/log"
	"golang.org/x/net/context" // See https://github.com/grpc/grpc-go/issues/711"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

// BufferSize gives the size for the monitor buffers
const BufferSize = 10

// Client is a wrapper around MonitorClient
type Client struct {
	Ctx log.Interface

	client MonitorClient
	conn   *grpc.ClientConn
	addr   string

	gateways     map[string]GatewayClient
	BrokerClient BrokerClient

	mutex sync.RWMutex
}

// NewClient is a wrapper for NewMonitorClient, initializes
// connection to MonitorServer on monitorAddr with default gRPC options
func NewClient(ctx log.Interface, monitorAddr string) (cl *Client, err error) {
	cl = &Client{
		Ctx:      ctx,
		addr:     monitorAddr,
		gateways: make(map[string]GatewayClient),
	}
	return cl, cl.Open()
}

// Open opens connection to the monitor
func (cl *Client) Open() (err error) {
	cl.mutex.Lock()
	defer cl.mutex.Unlock()

	return cl.open()
}
func (cl *Client) open() (err error) {
	addr := cl.addr
	ctx := cl.Ctx.WithField("addr", addr)

	defer func() {
		if err != nil {
			ctx.Warn("Failed to open monitor connection")
		} else {
			ctx.Info("Monitor connection opened")
		}
	}()

	ctx.Debug("Opening monitor connection...")

	cl.conn, err = api.Dial(addr)
	if err != nil {
		ctx.WithError(errors.FromGRPCError(err)).Warn("Failed to establish connection to gRPC service")
		return err
	}

	cl.client = NewMonitorClient(cl.conn)

	cl.BrokerClient = &brokerClient{
		Ctx:    cl.Ctx.WithField("component", "broker"),
		client: cl,
	}
	return nil
}

// Close closes connection to the monitor
func (cl *Client) Close() (err error) {
	cl.mutex.Lock()
	defer cl.mutex.Unlock()

	return cl.close()
}
func (cl *Client) close() (err error) {
	defer func() {
		if err != nil {
			cl.Ctx.Warn("Failed to close monitor connection")
		} else {
			cl.Ctx.Info("Monitor connection closed")
		}
	}()

	for _, gtw := range cl.gateways {
		ctx := cl.Ctx.WithField("GatewayID", gtw.(*gatewayClient).id)

		ctx.Debug("Closing gateway streams...")
		err = gtw.Close()
		if err != nil {
			ctx.Warn("Failed to close gateway streams")
		}
	}

	cl.Ctx.Debug("Closing monitor connection...")
	err = cl.conn.Close()
	if err != nil {
		return err
	}

	cl.conn = nil
	return nil
}

// Reopen reopens connection to the monitor. It first attempts to close already opened connection
// and then opens a new one. If closing already opened connection fails, Reopen fails too.
func (cl *Client) Reopen() (err error) {
	cl.mutex.Lock()
	defer cl.mutex.Unlock()

	return cl.reopen()
}
func (cl *Client) reopen() (err error) {
	defer func() {
		if err != nil {
			cl.Ctx.Warn("Failed to reopen monitor connection")
		} else {
			cl.Ctx.Info("Monitor connection reopened")
		}
	}()

	cl.Ctx.Debug("Reopening monitor connection...")

	err = cl.close()
	if err != nil {
		return err
	}
	return cl.open()
}

// IsConnected returns whether connection to the monitor had been established or not
func (cl *Client) IsConnected() bool {
	return cl.client != nil && cl.conn != nil
}

// GatewayClient returns monitor GatewayClient for id and token specified
func (cl *Client) GatewayClient(id string) (gtwCl GatewayClient) {
	cl.mutex.RLock()
	gtwCl, ok := cl.gateways[id]
	cl.mutex.RUnlock()
	if !ok {
		cl.mutex.Lock()
		gtwCl = &gatewayClient{
			Ctx:    cl.Ctx.WithField("GatewayID", id),
			client: cl,
			id:     id,
		}
		cl.gateways[id] = gtwCl
		cl.mutex.Unlock()
	}
	return gtwCl
}

type gatewayClient struct {
	sync.RWMutex

	client *Client

	Ctx log.Interface

	id, token string

	status struct {
		init   sync.Once
		ch     chan *gateway.Status
		cancel func()
		sync.RWMutex
	}

	uplink struct {
		init   sync.Once
		ch     chan *router.UplinkMessage
		cancel func()
		sync.Mutex
	}

	downlink struct {
		init   sync.Once
		ch     chan *router.DownlinkMessage
		cancel func()
		sync.RWMutex
	}
}

// GatewayClient is used as the main client for Gateways to communicate with the monitor
type GatewayClient interface {
	SetToken(token string)
	IsConfigured() bool
	SendStatus(status *gateway.Status) (err error)
	SendUplink(msg *router.UplinkMessage) (err error)
	SendDownlink(msg *router.DownlinkMessage) (err error)
	Close() (err error)
}

func (cl *gatewayClient) SetToken(token string) {
	cl.Lock()
	defer cl.Unlock()
	cl.token = token
}

func (cl *gatewayClient) IsConfigured() bool {
	cl.RLock()
	defer cl.RUnlock()
	return cl.token != "" && cl.token != "token"
}

// Close closes all opened monitor streams for the gateway
func (cl *gatewayClient) Close() (err error) {
	cl.closeStatus()
	cl.closeUplink()
	cl.closeDownlink()
	return err
}

// Context returns monitor connection context for gateway
func (cl *gatewayClient) Context() (monitorContext context.Context) {
	cl.RLock()
	defer cl.RUnlock()
	return metadata.NewContext(context.Background(), metadata.Pairs(
		"id", cl.id,
		"token", cl.token,
	))
}

type brokerClient struct {
	sync.RWMutex

	client *Client

	Ctx log.Interface

	uplink struct {
		init   sync.Once
		ch     chan *broker.DeduplicatedUplinkMessage
		cancel func()
		sync.Mutex
	}

	downlink struct {
		init   sync.Once
		ch     chan *broker.DownlinkMessage
		cancel func()
		sync.RWMutex
	}
}

// BrokerClient is used as the main client for Brokers to communicate with the monitor
type BrokerClient interface {
	SendUplink(msg *broker.DeduplicatedUplinkMessage) (err error)
	SendDownlink(msg *broker.DownlinkMessage) (err error)
	Close() (err error)
}

// Close closes all opened monitor streams for the broker
func (cl *brokerClient) Close() (err error) {
	cl.closeUplink()
	cl.closeDownlink()
	return err
}

// Context returns monitor connection context for broker
func (cl *brokerClient) Context() (monitorContext context.Context) {
	//TODO add auth
	return context.Background()
}
<commit_msg>GatewayClient is configured if token not empty<commit_after>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package monitor

import (
	"sync"

	"github.com/TheThingsNetwork/ttn/api"
	"github.com/TheThingsNetwork/ttn/api/broker"
	"github.com/TheThingsNetwork/ttn/api/gateway"
	"github.com/TheThingsNetwork/ttn/api/router"
	"github.com/TheThingsNetwork/ttn/utils/errors"
	"github.com/apex/log"
	"golang.org/x/net/context" // See https://github.com/grpc/grpc-go/issues/711"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

// BufferSize gives the size for the monitor buffers
const BufferSize = 10

// Client is a wrapper around MonitorClient
type Client struct {
	Ctx log.Interface

	client MonitorClient
	conn   *grpc.ClientConn
	addr   string

	gateways     map[string]GatewayClient
	BrokerClient BrokerClient

	mutex sync.RWMutex
}

// NewClient is a wrapper for NewMonitorClient, initializes
// connection to MonitorServer on monitorAddr with default gRPC options
func NewClient(ctx log.Interface, monitorAddr string) (cl *Client, err error) {
	cl = &Client{
		Ctx:      ctx,
		addr:     monitorAddr,
		gateways: make(map[string]GatewayClient),
	}
	return cl, cl.Open()
}

// Open opens connection to the monitor
func (cl *Client) Open() (err error) {
	cl.mutex.Lock()
	defer cl.mutex.Unlock()

	return cl.open()
}
func (cl *Client) open() (err error) {
	addr := cl.addr
	ctx := cl.Ctx.WithField("addr", addr)

	defer func() {
		if err != nil {
			ctx.Warn("Failed to open monitor connection")
		} else {
			ctx.Info("Monitor connection opened")
		}
	}()

	ctx.Debug("Opening monitor connection...")

	cl.conn, err = api.Dial(addr)
	if err != nil {
		ctx.WithError(errors.FromGRPCError(err)).Warn("Failed to establish connection to gRPC service")
		return err
	}

	cl.client = NewMonitorClient(cl.conn)

	cl.BrokerClient = &brokerClient{
		Ctx:    cl.Ctx.WithField("component", "broker"),
		client: cl,
	}
	return nil
}

// Close closes connection to the monitor
func (cl *Client) Close() (err error) {
	cl.mutex.Lock()
	defer cl.mutex.Unlock()

	return cl.close()
}
func (cl *Client) close() (err error) {
	defer func() {
		if err != nil {
			cl.Ctx.Warn("Failed to close monitor connection")
		} else {
			cl.Ctx.Info("Monitor connection closed")
		}
	}()

	for _, gtw := range cl.gateways {
		ctx := cl.Ctx.WithField("GatewayID", gtw.(*gatewayClient).id)

		ctx.Debug("Closing gateway streams...")
		err = gtw.Close()
		if err != nil {
			ctx.Warn("Failed to close gateway streams")
		}
	}

	cl.Ctx.Debug("Closing monitor connection...")
	err = cl.conn.Close()
	if err != nil {
		return err
	}

	cl.conn = nil
	return nil
}

// Reopen reopens connection to the monitor. It first attempts to close already opened connection
// and then opens a new one. If closing already opened connection fails, Reopen fails too.
func (cl *Client) Reopen() (err error) {
	cl.mutex.Lock()
	defer cl.mutex.Unlock()

	return cl.reopen()
}
func (cl *Client) reopen() (err error) {
	defer func() {
		if err != nil {
			cl.Ctx.Warn("Failed to reopen monitor connection")
		} else {
			cl.Ctx.Info("Monitor connection reopened")
		}
	}()

	cl.Ctx.Debug("Reopening monitor connection...")

	err = cl.close()
	if err != nil {
		return err
	}
	return cl.open()
}

// IsConnected returns whether connection to the monitor had been established or not
func (cl *Client) IsConnected() bool {
	return cl.client != nil && cl.conn != nil
}

// GatewayClient returns monitor GatewayClient for id and token specified
func (cl *Client) GatewayClient(id string) (gtwCl GatewayClient) {
	cl.mutex.RLock()
	gtwCl, ok := cl.gateways[id]
	cl.mutex.RUnlock()
	if !ok {
		cl.mutex.Lock()
		gtwCl = &gatewayClient{
			Ctx:    cl.Ctx.WithField("GatewayID", id),
			client: cl,
			id:     id,
		}
		cl.gateways[id] = gtwCl
		cl.mutex.Unlock()
	}
	return gtwCl
}

type gatewayClient struct {
	sync.RWMutex

	client *Client

	Ctx log.Interface

	id, token string

	status struct {
		init   sync.Once
		ch     chan *gateway.Status
		cancel func()
		sync.RWMutex
	}

	uplink struct {
		init   sync.Once
		ch     chan *router.UplinkMessage
		cancel func()
		sync.Mutex
	}

	downlink struct {
		init   sync.Once
		ch     chan *router.DownlinkMessage
		cancel func()
		sync.RWMutex
	}
}

// GatewayClient is used as the main client for Gateways to communicate with the monitor
type GatewayClient interface {
	SetToken(token string)
	IsConfigured() bool
	SendStatus(status *gateway.Status) (err error)
	SendUplink(msg *router.UplinkMessage) (err error)
	SendDownlink(msg *router.DownlinkMessage) (err error)
	Close() (err error)
}

func (cl *gatewayClient) SetToken(token string) {
	cl.Lock()
	defer cl.Unlock()
	cl.token = token
}

func (cl *gatewayClient) IsConfigured() bool {
	cl.RLock()
	defer cl.RUnlock()
	return cl.token != ""
}

// Close closes all opened monitor streams for the gateway
func (cl *gatewayClient) Close() (err error) {
	cl.closeStatus()
	cl.closeUplink()
	cl.closeDownlink()
	return err
}

// Context returns monitor connection context for gateway
func (cl *gatewayClient) Context() (monitorContext context.Context) {
	cl.RLock()
	defer cl.RUnlock()
	return metadata.NewContext(context.Background(), metadata.Pairs(
		"id", cl.id,
		"token", cl.token,
	))
}

type brokerClient struct {
	sync.RWMutex

	client *Client

	Ctx log.Interface

	uplink struct {
		init   sync.Once
		ch     chan *broker.DeduplicatedUplinkMessage
		cancel func()
		sync.Mutex
	}

	downlink struct {
		init   sync.Once
		ch     chan *broker.DownlinkMessage
		cancel func()
		sync.RWMutex
	}
}

// BrokerClient is used as the main client for Brokers to communicate with the monitor
type BrokerClient interface {
	SendUplink(msg *broker.DeduplicatedUplinkMessage) (err error)
	SendDownlink(msg *broker.DownlinkMessage) (err error)
	Close() (err error)
}

// Close closes all opened monitor streams for the broker
func (cl *brokerClient) Close() (err error) {
	cl.closeUplink()
	cl.closeDownlink()
	return err
}

// Context returns monitor connection context for broker
func (cl *brokerClient) Context() (monitorContext context.Context) {
	//TODO add auth
	return context.Background()
}
<|endoftext|>


<commit_before>package models

import (
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/sqlite"
)

type Device struct {
    gorm.Model
}<commit_msg>Added Device model struct<commit_after>package models

import (
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/sqlite"
)

type Device struct {
    gorm.Model

    Name string `gorm:"size:30"`
    ScreenWidth int
    ScreenHeight int
    PPI int
    BrandID int
}<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	go_flag "flag"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	flag "github.com/spf13/pflag"
	"k8s.io/contrib/ingress/controllers/gce/controller"
	"k8s.io/contrib/ingress/controllers/gce/storage"
	"k8s.io/kubernetes/pkg/api"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	kubectl_util "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/labels"
	"k8s.io/kubernetes/pkg/util/wait"

	"github.com/golang/glog"
)

// Entrypoint of GLBC. Example invocation:
// 1. In a pod:
// glbc --delete-all-on-quit
// 2. Dry run (on localhost):
// $ kubectl proxy --api-prefix="/"
// $ glbc --proxy="http://localhost:proxyport"

const (
	// lbApiPort is the port on which the loadbalancer controller serves a
	// minimal api (/healthz, /delete-all-and-quit etc).
	lbApiPort = 8081

	// A delimiter used for clarity in naming GCE resources.
	clusterNameDelimiter = "--"

	// Arbitrarily chosen alphanumeric character to use in constructing resource
	// names, eg: to avoid cases where we end up with a name ending in '-'.
	alphaNumericChar = "0"

	// Current docker image version. Only used in debug logging.
	imageVersion = "glbc:0.7.0"

	// Key used to persist UIDs to configmaps.
	uidConfigMapName = "ingress-uid"
)

var (
	flags = flag.NewFlagSet(
		`gclb: gclb --runngin-in-cluster=false --default-backend-node-port=123`,
		flag.ExitOnError)

	clusterName = flags.String("cluster-uid", controller.DefaultClusterUID,
		`Optional, used to tag cluster wide, shared loadbalancer resources such
		 as instance groups. Use this flag if you'd like to continue using the
		 same resources across a pod restart. Note that this does not need to
		 match the name of you Kubernetes cluster, it's just an arbitrary name
		 used to tag/lookup cloud resources.`)

	inCluster = flags.Bool("running-in-cluster", true,
		`Optional, if this controller is running in a kubernetes cluster, use the
		 pod secrets for creating a Kubernetes client.`)

	// TODO: Consolidate this flag and running-in-cluster. People already use
	// the first one to mean "running in dev", unfortunately.
	useRealCloud = flags.Bool("use-real-cloud", false,
		`Optional, if set a real cloud client is created. Only matters with
		 --running-in-cluster=false, i.e a real cloud is always used when this
		 controller is running on a Kubernetes node.`)

	resyncPeriod = flags.Duration("sync-period", 30*time.Second,
		`Relist and confirm cloud resources this often.`)

	deleteAllOnQuit = flags.Bool("delete-all-on-quit", false,
		`If true, the controller will delete all Ingress and the associated
		external cloud resources as it's shutting down. Mostly used for
		testing. In normal environments the controller should only delete
		a loadbalancer if the associated Ingress is deleted.`)

	defaultSvc = flags.String("default-backend-service", "kube-system/default-http-backend",
		`Service used to serve a 404 page for the default backend. Takes the form
		namespace/name. The controller uses the first node port of this Service for
		the default backend.`)

	healthCheckPath = flags.String("health-check-path", "/",
		`Path used to health-check a backend service. All Services must serve
		a 200 page on this path. Currently this is only configurable globally.`)

	watchNamespace = flags.String("watch-namespace", api.NamespaceAll,
		`Namespace to watch for Ingress/Services/Endpoints.`)

	verbose = flags.Bool("verbose", false,
		`If true, logs are displayed at V(4), otherwise V(2).`)

	configFilePath = flags.String("config-file-path", "",
		`Path to a file containing the gce config. If left unspecified this
		controller only works with default zones.`)

	healthzPort = flags.Int("healthz-port", lbApiPort,
		`Port to run healthz server. Must match the health check port in yaml.`)
)

func registerHandlers(lbc *controller.LoadBalancerController) {
	http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
		if err := lbc.CloudClusterManager.IsHealthy(); err != nil {
			w.WriteHeader(500)
			w.Write([]byte(fmt.Sprintf("Cluster unhealthy: %v", err)))
			return
		}
		w.WriteHeader(200)
		w.Write([]byte("ok"))
	})
	http.HandleFunc("/delete-all-and-quit", func(w http.ResponseWriter, r *http.Request) {
		// TODO: Retry failures during shutdown.
		lbc.Stop(true)
	})

	glog.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", *healthzPort), nil))
}

func handleSigterm(lbc *controller.LoadBalancerController, deleteAll bool) {
	// Multiple SIGTERMs will get dropped
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGTERM)
	<-signalChan
	glog.Infof("Received SIGTERM, shutting down")

	// TODO: Better retires than relying on restartPolicy.
	exitCode := 0
	if err := lbc.Stop(deleteAll); err != nil {
		glog.Infof("Error during shutdown %v", err)
		exitCode = 1
	}
	glog.Infof("Exiting with %v", exitCode)
	os.Exit(exitCode)
}

// main function for GLBC.
func main() {
	// TODO: Add a healthz endpoint
	var kubeClient *client.Client
	var err error
	var clusterManager *controller.ClusterManager
	flags.Parse(os.Args)
	clientConfig := kubectl_util.DefaultClientConfig(flags)

	// Set glog verbosity levels
	if *verbose {
		go_flag.Lookup("logtostderr").Value.Set("true")
		go_flag.Set("v", "4")
	}
	glog.Infof("Starting GLBC image: %v, cluster name %v", imageVersion, *clusterName)
	if *defaultSvc == "" {
		glog.Fatalf("Please specify --default-backend")
	}

	// Create kubeclient
	if *inCluster {
		if kubeClient, err = client.NewInCluster(); err != nil {
			glog.Fatalf("Failed to create client: %v.", err)
		}
	} else {
		config, err := clientConfig.ClientConfig()
		if err != nil {
			glog.Fatalf("error connecting to the client: %v", err)
		}
		kubeClient, err = client.New(config)
	}
	// Wait for the default backend Service. There's no pretty way to do this.
	parts := strings.Split(*defaultSvc, "/")
	if len(parts) != 2 {
		glog.Fatalf("Default backend should take the form namespace/name: %v",
			*defaultSvc)
	}
	defaultBackendNodePort, err := getNodePort(kubeClient, parts[0], parts[1])
	if err != nil {
		glog.Fatalf("Could not configure default backend %v: %v",
			*defaultSvc, err)
	}

	if *inCluster || *useRealCloud {
		// Create cluster manager
		name, err := getClusterUID(kubeClient, *clusterName)
		if err != nil {
			glog.Fatalf("%v", err)
		}
		clusterManager, err = controller.NewClusterManager(*configFilePath, name, defaultBackendNodePort, *healthCheckPath)
		if err != nil {
			glog.Fatalf("%v", err)
		}
	} else {
		// Create fake cluster manager
		clusterManager = controller.NewFakeClusterManager(*clusterName).ClusterManager
	}

	// Start loadbalancer controller
	lbc, err := controller.NewLoadBalancerController(kubeClient, clusterManager, *resyncPeriod, *watchNamespace)
	if err != nil {
		glog.Fatalf("%v", err)
	}
	if clusterManager.ClusterNamer.ClusterName != "" {
		glog.V(3).Infof("Cluster name %+v", clusterManager.ClusterNamer.ClusterName)
	}
	clusterManager.Init(&controller.GCETranslator{lbc})
	go registerHandlers(lbc)
	go handleSigterm(lbc, *deleteAllOnQuit)

	lbc.Run()
	for {
		glog.Infof("Handled quit, awaiting pod deletion.")
		time.Sleep(30 * time.Second)
	}
}

// getClusterUID returns the cluster UID. Rules for UID generation:
// If the user specifies a --cluster-uid param it overwrites everything
// else, check UID config map for a previously recorded uid
// else, check if there are any working Ingresses
//	- remember that "" is the cluster uid
// else, allocate a new uid
func getClusterUID(kubeClient *client.Client, name string) (string, error) {
	cfgVault := storage.NewConfigMapVault(kubeClient, api.NamespaceSystem, uidConfigMapName)
	if name != "" {
		glog.Infof("Using user provided cluster uid %v", name)
		// Don't save the uid in the vault, so users can rollback through
		// --cluster-uid=""
		return name, nil
	}

	existingUID, found, err := cfgVault.Get()
	if found {
		glog.Infof("Using saved cluster uid %q", name)
		return existingUID, nil
	} else if err != nil {
		// This can fail because of:
		// 1. No such config map - found=false, err=nil
		// 2. No such key in config map - found=false, err=nil
		// 3. Apiserver flake - found=false, err!=nil
		// It is not safe to proceed in 3.
		return "", fmt.Errorf("Failed to retrieve current uid: %v, using %q as name", err, name)
	}

	// Check if the cluster has an Ingress with ip
	ings, err := kubeClient.Extensions().Ingress(api.NamespaceAll).List(api.ListOptions{LabelSelector: labels.Everything()})
	if err != nil {
		return "", err
	}
	for _, ing := range ings.Items {
		if len(ing.Status.LoadBalancer.Ingress) != 0 {
			glog.Infof("Found a working Ingress, assuming uid is empty string")
			return "", cfgVault.Put("")
		}
	}

	// Allocate new uid
	f, err := os.Open("/dev/urandom")
	if err != nil {
		return "", err
	}
	defer f.Close()
	b := make([]byte, 8)
	if _, err := f.Read(b); err != nil {
		return "", err
	}
	uid := fmt.Sprintf("%x", b)
	return uid, cfgVault.Put(uid)
}

// getNodePort waits for the Service, and returns it's first node port.
func getNodePort(client *client.Client, ns, name string) (nodePort int64, err error) {
	var svc *api.Service
	glog.V(3).Infof("Waiting for %v/%v", ns, name)
	wait.Poll(1*time.Second, 5*time.Minute, func() (bool, error) {
		svc, err = client.Services(ns).Get(name)
		if err != nil {
			return false, nil
		}
		for _, p := range svc.Spec.Ports {
			if p.NodePort != 0 {
				nodePort = int64(p.NodePort)
				glog.V(3).Infof("Node port %v", nodePort)
				break
			}
		}
		return true, nil
	})
	return
}
<commit_msg>Fix log statement<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	go_flag "flag"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	flag "github.com/spf13/pflag"
	"k8s.io/contrib/ingress/controllers/gce/controller"
	"k8s.io/contrib/ingress/controllers/gce/storage"
	"k8s.io/kubernetes/pkg/api"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	kubectl_util "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/labels"
	"k8s.io/kubernetes/pkg/util/wait"

	"github.com/golang/glog"
)

// Entrypoint of GLBC. Example invocation:
// 1. In a pod:
// glbc --delete-all-on-quit
// 2. Dry run (on localhost):
// $ kubectl proxy --api-prefix="/"
// $ glbc --proxy="http://localhost:proxyport"

const (
	// lbApiPort is the port on which the loadbalancer controller serves a
	// minimal api (/healthz, /delete-all-and-quit etc).
	lbApiPort = 8081

	// A delimiter used for clarity in naming GCE resources.
	clusterNameDelimiter = "--"

	// Arbitrarily chosen alphanumeric character to use in constructing resource
	// names, eg: to avoid cases where we end up with a name ending in '-'.
	alphaNumericChar = "0"

	// Current docker image version. Only used in debug logging.
	imageVersion = "glbc:0.7.0"

	// Key used to persist UIDs to configmaps.
	uidConfigMapName = "ingress-uid"
)

var (
	flags = flag.NewFlagSet(
		`gclb: gclb --runngin-in-cluster=false --default-backend-node-port=123`,
		flag.ExitOnError)

	clusterName = flags.String("cluster-uid", controller.DefaultClusterUID,
		`Optional, used to tag cluster wide, shared loadbalancer resources such
		 as instance groups. Use this flag if you'd like to continue using the
		 same resources across a pod restart. Note that this does not need to
		 match the name of you Kubernetes cluster, it's just an arbitrary name
		 used to tag/lookup cloud resources.`)

	inCluster = flags.Bool("running-in-cluster", true,
		`Optional, if this controller is running in a kubernetes cluster, use the
		 pod secrets for creating a Kubernetes client.`)

	// TODO: Consolidate this flag and running-in-cluster. People already use
	// the first one to mean "running in dev", unfortunately.
	useRealCloud = flags.Bool("use-real-cloud", false,
		`Optional, if set a real cloud client is created. Only matters with
		 --running-in-cluster=false, i.e a real cloud is always used when this
		 controller is running on a Kubernetes node.`)

	resyncPeriod = flags.Duration("sync-period", 30*time.Second,
		`Relist and confirm cloud resources this often.`)

	deleteAllOnQuit = flags.Bool("delete-all-on-quit", false,
		`If true, the controller will delete all Ingress and the associated
		external cloud resources as it's shutting down. Mostly used for
		testing. In normal environments the controller should only delete
		a loadbalancer if the associated Ingress is deleted.`)

	defaultSvc = flags.String("default-backend-service", "kube-system/default-http-backend",
		`Service used to serve a 404 page for the default backend. Takes the form
		namespace/name. The controller uses the first node port of this Service for
		the default backend.`)

	healthCheckPath = flags.String("health-check-path", "/",
		`Path used to health-check a backend service. All Services must serve
		a 200 page on this path. Currently this is only configurable globally.`)

	watchNamespace = flags.String("watch-namespace", api.NamespaceAll,
		`Namespace to watch for Ingress/Services/Endpoints.`)

	verbose = flags.Bool("verbose", false,
		`If true, logs are displayed at V(4), otherwise V(2).`)

	configFilePath = flags.String("config-file-path", "",
		`Path to a file containing the gce config. If left unspecified this
		controller only works with default zones.`)

	healthzPort = flags.Int("healthz-port", lbApiPort,
		`Port to run healthz server. Must match the health check port in yaml.`)
)

func registerHandlers(lbc *controller.LoadBalancerController) {
	http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
		if err := lbc.CloudClusterManager.IsHealthy(); err != nil {
			w.WriteHeader(500)
			w.Write([]byte(fmt.Sprintf("Cluster unhealthy: %v", err)))
			return
		}
		w.WriteHeader(200)
		w.Write([]byte("ok"))
	})
	http.HandleFunc("/delete-all-and-quit", func(w http.ResponseWriter, r *http.Request) {
		// TODO: Retry failures during shutdown.
		lbc.Stop(true)
	})

	glog.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", *healthzPort), nil))
}

func handleSigterm(lbc *controller.LoadBalancerController, deleteAll bool) {
	// Multiple SIGTERMs will get dropped
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGTERM)
	<-signalChan
	glog.Infof("Received SIGTERM, shutting down")

	// TODO: Better retires than relying on restartPolicy.
	exitCode := 0
	if err := lbc.Stop(deleteAll); err != nil {
		glog.Infof("Error during shutdown %v", err)
		exitCode = 1
	}
	glog.Infof("Exiting with %v", exitCode)
	os.Exit(exitCode)
}

// main function for GLBC.
func main() {
	// TODO: Add a healthz endpoint
	var kubeClient *client.Client
	var err error
	var clusterManager *controller.ClusterManager
	flags.Parse(os.Args)
	clientConfig := kubectl_util.DefaultClientConfig(flags)

	// Set glog verbosity levels
	if *verbose {
		go_flag.Lookup("logtostderr").Value.Set("true")
		go_flag.Set("v", "4")
	}
	glog.Infof("Starting GLBC image: %v, cluster name %v", imageVersion, *clusterName)
	if *defaultSvc == "" {
		glog.Fatalf("Please specify --default-backend")
	}

	// Create kubeclient
	if *inCluster {
		if kubeClient, err = client.NewInCluster(); err != nil {
			glog.Fatalf("Failed to create client: %v.", err)
		}
	} else {
		config, err := clientConfig.ClientConfig()
		if err != nil {
			glog.Fatalf("error connecting to the client: %v", err)
		}
		kubeClient, err = client.New(config)
	}
	// Wait for the default backend Service. There's no pretty way to do this.
	parts := strings.Split(*defaultSvc, "/")
	if len(parts) != 2 {
		glog.Fatalf("Default backend should take the form namespace/name: %v",
			*defaultSvc)
	}
	defaultBackendNodePort, err := getNodePort(kubeClient, parts[0], parts[1])
	if err != nil {
		glog.Fatalf("Could not configure default backend %v: %v",
			*defaultSvc, err)
	}

	if *inCluster || *useRealCloud {
		// Create cluster manager
		name, err := getClusterUID(kubeClient, *clusterName)
		if err != nil {
			glog.Fatalf("%v", err)
		}
		clusterManager, err = controller.NewClusterManager(*configFilePath, name, defaultBackendNodePort, *healthCheckPath)
		if err != nil {
			glog.Fatalf("%v", err)
		}
	} else {
		// Create fake cluster manager
		clusterManager = controller.NewFakeClusterManager(*clusterName).ClusterManager
	}

	// Start loadbalancer controller
	lbc, err := controller.NewLoadBalancerController(kubeClient, clusterManager, *resyncPeriod, *watchNamespace)
	if err != nil {
		glog.Fatalf("%v", err)
	}
	if clusterManager.ClusterNamer.ClusterName != "" {
		glog.V(3).Infof("Cluster name %+v", clusterManager.ClusterNamer.ClusterName)
	}
	clusterManager.Init(&controller.GCETranslator{lbc})
	go registerHandlers(lbc)
	go handleSigterm(lbc, *deleteAllOnQuit)

	lbc.Run()
	for {
		glog.Infof("Handled quit, awaiting pod deletion.")
		time.Sleep(30 * time.Second)
	}
}

// getClusterUID returns the cluster UID. Rules for UID generation:
// If the user specifies a --cluster-uid param it overwrites everything
// else, check UID config map for a previously recorded uid
// else, check if there are any working Ingresses
//	- remember that "" is the cluster uid
// else, allocate a new uid
func getClusterUID(kubeClient *client.Client, name string) (string, error) {
	cfgVault := storage.NewConfigMapVault(kubeClient, api.NamespaceSystem, uidConfigMapName)
	if name != "" {
		glog.Infof("Using user provided cluster uid %v", name)
		// Don't save the uid in the vault, so users can rollback through
		// --cluster-uid=""
		return name, nil
	}

	existingUID, found, err := cfgVault.Get()
	if found {
		glog.Infof("Using saved cluster uid %q", existingUID)
		return existingUID, nil
	} else if err != nil {
		// This can fail because of:
		// 1. No such config map - found=false, err=nil
		// 2. No such key in config map - found=false, err=nil
		// 3. Apiserver flake - found=false, err!=nil
		// It is not safe to proceed in 3.
		return "", fmt.Errorf("Failed to retrieve current uid: %v, using %q as name", err, name)
	}

	// Check if the cluster has an Ingress with ip
	ings, err := kubeClient.Extensions().Ingress(api.NamespaceAll).List(api.ListOptions{LabelSelector: labels.Everything()})
	if err != nil {
		return "", err
	}
	for _, ing := range ings.Items {
		if len(ing.Status.LoadBalancer.Ingress) != 0 {
			glog.Infof("Found a working Ingress, assuming uid is empty string")
			return "", cfgVault.Put("")
		}
	}

	// Allocate new uid
	f, err := os.Open("/dev/urandom")
	if err != nil {
		return "", err
	}
	defer f.Close()
	b := make([]byte, 8)
	if _, err := f.Read(b); err != nil {
		return "", err
	}
	uid := fmt.Sprintf("%x", b)
	return uid, cfgVault.Put(uid)
}

// getNodePort waits for the Service, and returns it's first node port.
func getNodePort(client *client.Client, ns, name string) (nodePort int64, err error) {
	var svc *api.Service
	glog.V(3).Infof("Waiting for %v/%v", ns, name)
	wait.Poll(1*time.Second, 5*time.Minute, func() (bool, error) {
		svc, err = client.Services(ns).Get(name)
		if err != nil {
			return false, nil
		}
		for _, p := range svc.Spec.Ports {
			if p.NodePort != 0 {
				nodePort = int64(p.NodePort)
				glog.V(3).Infof("Node port %v", nodePort)
				break
			}
		}
		return true, nil
	})
	return
}
<|endoftext|>


<commit_before>package controllers

import (
	"encoding/json"
	"fmt"
	"github.com/astaxie/beego"
	"github.com/dockercn/docker-bucket/models"
	"github.com/nfnt/resize"
	"image"
	"image/jpeg"
	"image/png"
	"io"
	"net/http"
	"os"
	"strings"
)

type UsersWebController struct {
	beego.Controller
}

func (this *UsersWebController) Prepare() {
	beego.Debug(fmt.Sprintf("[%s] %s | %s", this.Ctx.Input.Host(), this.Ctx.Input.Request.Method, this.Ctx.Input.Request.RequestURI))
	beego.Debug("[Header] ")
	beego.Debug(this.Ctx.Request.Header)
}

func (this *UsersWebController) PostGravatar() {
	//从请求中读取图片信息，图片保存在相应
	file, fileHeader, err := this.Ctx.Request.FormFile("file")
	if err != nil {
		beego.Error(fmt.Sprintf("[image] 处理上传头像错误,err=%s", err))
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"图片上传处理失败\"}"))
		return
	}

	//读取文件后缀名，如果不是图片，则返回错误
	prefix := strings.Split(fileHeader.Filename, ".")[0]
	suffix := strings.Split(fileHeader.Filename, ".")[1]
	if suffix != "png" && suffix != "jpg" && suffix != "jpeg" {
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"文件的扩展名必须是jpg、jpeg或者png!\"}"))
		return
	}

	f, err := os.OpenFile(fmt.Sprintf("%s%s%s", beego.AppConfig.String("docker::Gravatar"), "/", fileHeader.Filename), os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		//处理文件错误
		beego.Error(fmt.Sprintf("[image] 处理上传头像错误,err=%s", err))
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"图片上传处理失败\"}"))
		return
	}
	io.Copy(f, file)
	f.Close()

	// decode jpeg into image.Image
	var img image.Image
	imageFile, err := os.Open(fmt.Sprintf("%s%s%s", beego.AppConfig.String("docker::Gravatar"), "/", fileHeader.Filename))
	if err != nil {
		beego.Error(fmt.Sprintf("[image] 上传图片预失败,err=%s", err))
	}
	switch suffix {
	case "png":
		img, err = png.Decode(imageFile)
	case "jpg":
		img, err = jpeg.Decode(imageFile)
	case "jpeg":
		img, err = jpeg.Decode(imageFile)
	}
	if err != nil {
		beego.Error(fmt.Sprintf("[image] 裁剪图片失败,err=%s", err))
		imageFile.Close()
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"图片上传处理失败\"}"))
		return
	}
	imageFile.Close()
	// resize to width 1000 using Lanczos resampling
	// and preserve aspect ratio
	m := resize.Resize(100, 100, img, resize.Lanczos3)

	out, err := os.Create(fmt.Sprintf("%s%s%s%s%s", beego.AppConfig.String("docker::Gravatar"), "/", prefix, "_resize.", suffix))
	if err != nil {
		beego.Error(fmt.Sprintf("[image] 裁剪图片失败,err=%s", err))
	}
	defer out.Close()
	// write new image to file
	switch suffix {
	case "png":
		png.Encode(out, m)
	case "jpg":
		jpeg.Encode(out, m, nil)
	case "jpeg":
		jpeg.Encode(out, m, nil)
	}
	this.Ctx.Output.Context.Output.SetStatus(http.StatusOK)
	this.Ctx.Output.Context.ResponseWriter.Header().Set("Content-Type", "application/json;charset=UTF-8")
	this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"文件上传成功！\",\"url\":\"" + fmt.Sprintf("%s%s%s%s%s", beego.AppConfig.String("docker::Gravatar"), "/", prefix, "_resize.", suffix) + "\"}"))
	return
}

func (this *UsersWebController) GetProfile() {
	//加载session
	user, ok := this.GetSession("user").(models.User)
	if !ok {
		beego.Error(fmt.Sprintf("[WEB 用户] session加载失败"))
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"session加载失败\",\"url\":\"/auth\"}"))
		return
	}
	user2json, err := json.Marshal(user)
	if err != nil {
		beego.Error(fmt.Sprintf("[WEB 用户] session解码json失败"))
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"session解码json失败\",\"url\":\"/auth\"}"))
		return
	}
	this.Ctx.Output.Context.Output.SetStatus(http.StatusOK)
	this.Ctx.Output.Context.Output.Body(user2json)
	return
}
<commit_msg>增加更新profile的方法，增加头像修改后在ledis中更新<commit_after>package controllers

import (
	"encoding/json"
	"fmt"
	"github.com/astaxie/beego"
	"github.com/dockercn/docker-bucket/models"
	"github.com/nfnt/resize"
	"image"
	"image/jpeg"
	"image/png"
	"io"
	"net/http"
	"os"
	"strings"
)

type UsersWebController struct {
	beego.Controller
}

func (this *UsersWebController) Prepare() {
	beego.Debug(fmt.Sprintf("[%s] %s | %s", this.Ctx.Input.Host(), this.Ctx.Input.Request.Method, this.Ctx.Input.Request.RequestURI))
	beego.Debug("[Header] ")
	beego.Debug(this.Ctx.Request.Header)
}

func (this *UsersWebController) PostGravatar() {
	//从请求中读取图片信息，图片保存在相应
	file, fileHeader, err := this.Ctx.Request.FormFile("file")
	if err != nil {
		beego.Error(fmt.Sprintf("[image] 处理上传头像错误,err=%s", err))
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"图片上传处理失败\"}"))
		return
	}

	//读取文件后缀名，如果不是图片，则返回错误
	prefix := strings.Split(fileHeader.Filename, ".")[0]
	suffix := strings.Split(fileHeader.Filename, ".")[1]
	if suffix != "png" && suffix != "jpg" && suffix != "jpeg" {
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"文件的扩展名必须是jpg、jpeg或者png!\"}"))
		return
	}

	f, err := os.OpenFile(fmt.Sprintf("%s%s%s", beego.AppConfig.String("docker::Gravatar"), "/", fileHeader.Filename), os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		//处理文件错误
		beego.Error(fmt.Sprintf("[image] 处理上传头像错误,err=%s", err))
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"图片上传处理失败\"}"))
		return
	}
	io.Copy(f, file)
	f.Close()

	// decode jpeg into image.Image
	var img image.Image
	imageFile, err := os.Open(fmt.Sprintf("%s%s%s", beego.AppConfig.String("docker::Gravatar"), "/", fileHeader.Filename))
	if err != nil {
		beego.Error(fmt.Sprintf("[image] 上传图片预失败,err=%s", err))
	}
	switch suffix {
	case "png":
		img, err = png.Decode(imageFile)
	case "jpg":
		img, err = jpeg.Decode(imageFile)
	case "jpeg":
		img, err = jpeg.Decode(imageFile)
	}
	if err != nil {
		beego.Error(fmt.Sprintf("[image] 裁剪图片失败,err=%s", err))
		imageFile.Close()
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"图片上传处理失败\"}"))
		return
	}
	imageFile.Close()
	// resize to width 1000 using Lanczos resampling
	// and preserve aspect ratio
	m := resize.Resize(100, 100, img, resize.Lanczos3)

	out, err := os.Create(fmt.Sprintf("%s%s%s%s%s", beego.AppConfig.String("docker::Gravatar"), "/", prefix, "_resize.", suffix))
	if err != nil {
		beego.Error(fmt.Sprintf("[image] 裁剪图片失败,err=%s", err))
	}
	defer out.Close()
	// write new image to file
	switch suffix {
	case "png":
		png.Encode(out, m)
	case "jpg":
		jpeg.Encode(out, m, nil)
	case "jpeg":
		jpeg.Encode(out, m, nil)
	}

	//加载session，更新user's gravatar
	user, ok := this.GetSession("user").(models.User)
	if !ok {
		beego.Error(fmt.Sprintf("[WEB 用户] session加载失败"))
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"session加载失败\",\"url\":\"/auth\"}"))
		return
	} else {
		var u map[string]interface{}
		u = make(map[string]interface{})
		u["gravatar"] = fmt.Sprintf("%s%s%s%s%s", beego.AppConfig.String("docker::Gravatar"), "/", prefix, "_resize.", suffix)
		if success, err := (&user).Update(u); err != nil || !success {
			this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
			this.Ctx.Output.Context.ResponseWriter.Header().Set("Content-Type", "application/json;charset=UTF-8")
			this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"上传图片处理失败\"}"))
			return
		}
	}
	this.Ctx.Output.Context.Output.SetStatus(http.StatusOK)
	this.Ctx.Output.Context.ResponseWriter.Header().Set("Content-Type", "application/json;charset=UTF-8")
	this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"文件上传成功！\",\"url\":\"" + fmt.Sprintf("%s%s%s%s%s", beego.AppConfig.String("docker::Gravatar"), "/", prefix, "_resize.", suffix) + "\"}"))
	return
}

func (this *UsersWebController) GetProfile() {
	//加载session
	user, ok := this.GetSession("user").(models.User)
	if !ok {
		beego.Error(fmt.Sprintf("[WEB 用户] session加载失败"))
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"session加载失败\",\"url\":\"/auth\"}"))
		return
	}
	user2json, err := json.Marshal(user)
	if err != nil {
		beego.Error(fmt.Sprintf("[WEB 用户] session解码json失败"))
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"session解码json失败\",\"url\":\"/auth\"}"))
		return
	}
	this.Ctx.Output.Context.Output.SetStatus(http.StatusOK)
	this.Ctx.Output.Context.Output.Body(user2json)
	return
}

func (this *UsersWebController) PutProfile() {
	//获得用户提交的信息
	var u map[string]interface{}
	if err := json.Unmarshal(this.Ctx.Input.CopyBody(), &u); err != nil {
		beego.Error(fmt.Sprintf("[WEB 用户] 解码用户注册发送的 JSON 数据失败: %s", err.Error()))
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.ResponseWriter.Header().Set("Content-Type", "application/json;charset=UTF-8")
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"更新用户数据失败\"}"))
		return
	}
	//加载session
	user, ok := this.GetSession("user").(models.User)
	if !ok {
		beego.Error(fmt.Sprintf("[WEB 用户] session加载失败"))
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.ResponseWriter.Header().Set("Content-Type", "application/json;charset=UTF-8")
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"更新用户数据失败\"}"))
		return
	}
	//根据session更新user
	if success, err := (&user).Update(u); err != nil || !success {
		this.Ctx.Output.Context.Output.SetStatus(http.StatusBadRequest)
		this.Ctx.Output.Context.ResponseWriter.Header().Set("Content-Type", "application/json;charset=UTF-8")
		this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"更新用户数据失败\"}"))
		return
	}
	//更新session中的user
	this.SetSession("user", user)
	//处理返回值
	this.Ctx.Output.Context.Output.SetStatus(http.StatusOK)
	this.Ctx.Output.Context.ResponseWriter.Header().Set("Content-Type", "application/json;charset=UTF-8")
	this.Ctx.Output.Context.Output.Body([]byte("{\"message\":\"更新用户数据成功\"}"))
	return
}
<|endoftext|>


<commit_before>package core

import (
	"bytes"
	"fmt"
	"math/big"
	"sync"
	"time"

	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/ethutil"
	"github.com/ethereum/go-ethereum/event"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/pow"
	"github.com/ethereum/go-ethereum/pow/ezp"
	"github.com/ethereum/go-ethereum/state"
	"gopkg.in/fatih/set.v0"
)

type PendingBlockEvent struct {
	Block *types.Block
}

var statelogger = logger.NewLogger("BLOCK")

type BlockProcessor struct {
	db ethutil.Database
	// Mutex for locking the block processor. Blocks can only be handled one at a time
	mutex sync.Mutex
	// Canonical block chain
	bc *ChainManager
	// non-persistent key/value memory storage
	mem map[string]*big.Int
	// Proof of work used for validating
	Pow pow.PoW

	txpool *TxPool

	// The last attempted block is mainly used for debugging purposes
	// This does not have to be a valid block and will be set during
	// 'Process' & canonical validation.
	lastAttemptedBlock *types.Block

	events event.Subscription

	eventMux *event.TypeMux
}

func NewBlockProcessor(db ethutil.Database, txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
	sm := &BlockProcessor{
		db:       db,
		mem:      make(map[string]*big.Int),
		Pow:      ezp.New(),
		bc:       chainManager,
		eventMux: eventMux,
		txpool:   txpool,
	}

	return sm
}

func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) {
	coinbase := statedb.GetOrNewStateObject(block.Header().Coinbase)
	coinbase.SetGasPool(block.Header().GasLimit)

	// Process the transactions on to parent state
	receipts, _, _, _, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), transientProcess)
	if err != nil {
		return nil, err
	}

	return receipts, nil
}

func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) {
	// If we are mining this block and validating we want to set the logs back to 0
	statedb.EmptyLogs()

	txGas := new(big.Int).Set(tx.Gas())

	cb := statedb.GetStateObject(coinbase.Address())
	st := NewStateTransition(NewEnv(statedb, self.bc, tx, block), tx, cb)
	_, err := st.TransitionState()
	if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err)) {
		return nil, nil, err
	}

	txGas.Sub(txGas, st.gas)

	// Update the state with pending changes
	statedb.Update(txGas)

	cumulative := new(big.Int).Set(usedGas.Add(usedGas, txGas))
	receipt := types.NewReceipt(statedb.Root(), cumulative)
	receipt.SetLogs(statedb.Logs())
	receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
	chainlogger.Debugln(receipt)

	// Notify all subscribers
	if !transientProcess {
		go self.eventMux.Post(TxPostEvent{tx})
		logs := statedb.Logs()
		go self.eventMux.Post(logs)
	}

	return receipt, txGas, err
}

func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, types.Transactions, types.Transactions, types.Transactions, error) {
	var (
		receipts           types.Receipts
		handled, unhandled types.Transactions
		erroneous          types.Transactions
		totalUsedGas       = big.NewInt(0)
		err                error
		cumulativeSum      = new(big.Int)
	)

	for _, tx := range txs {
		receipt, txGas, err := self.ApplyTransaction(coinbase, statedb, block, tx, totalUsedGas, transientProcess)
		if err != nil {
			switch {
			case IsNonceErr(err):
				return nil, nil, nil, nil, err
			case state.IsGasLimitErr(err):
				return nil, nil, nil, nil, err
			default:
				statelogger.Infoln(err)
				erroneous = append(erroneous, tx)
				err = nil
			}
		}
		receipts = append(receipts, receipt)
		handled = append(handled, tx)

		cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice()))
	}

	block.Reward = cumulativeSum
	block.Header().GasUsed = totalUsedGas

	if transientProcess {
		go self.eventMux.Post(PendingBlockEvent{block})
	}

	return receipts, handled, unhandled, erroneous, err
}

// Process block will attempt to process the given block's transactions and applies them
// on top of the block's parent state (given it exists) and will return wether it was
// successful or not.
func (sm *BlockProcessor) Process(block *types.Block) (td *big.Int, err error) {
	// Processing a blocks may never happen simultaneously
	sm.mutex.Lock()
	defer sm.mutex.Unlock()

	header := block.Header()
	if sm.bc.HasBlock(header.Hash()) {
		return nil, &KnownBlockError{header.Number, header.Hash()}
	}

	if !sm.bc.HasBlock(header.ParentHash) {
		return nil, ParentError(header.ParentHash)
	}
	parent := sm.bc.GetBlock(header.ParentHash)

	return sm.processWithParent(block, parent)
}

func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big.Int, err error) {
	sm.lastAttemptedBlock = block

	// Create a new state based on the parent's root (e.g., create copy)
	state := state.New(parent.Root(), sm.db)

	// Block validation
	if err = sm.ValidateBlock(block, parent); err != nil {
		return
	}

	receipts, err := sm.TransitionState(state, parent, block, false)
	if err != nil {
		return
	}

	header := block.Header()

	// Validate the received block's bloom with the one derived from the generated receipts.
	// For valid blocks this should always validate to true.
	rbloom := types.CreateBloom(receipts)
	if bytes.Compare(rbloom, header.Bloom) != 0 {
		err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
		return
	}

	// The transactions Trie's root (R = (Tr [[H1, T1], [H2, T2], ... [Hn, Tn]]))
	// can be used by light clients to make sure they've received the correct Txs
	txSha := types.DeriveSha(block.Transactions())
	if bytes.Compare(txSha, header.TxHash) != 0 {
		err = fmt.Errorf("validating transaction root. received=%x got=%x", header.TxHash, txSha)
		return
	}

	// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
	receiptSha := types.DeriveSha(receipts)
	if bytes.Compare(receiptSha, header.ReceiptHash) != 0 {
		err = fmt.Errorf("validating receipt root. received=%x got=%x", header.ReceiptHash, receiptSha)
		return
	}

	// Accumulate static rewards; block reward, uncle's and uncle inclusion.
	if err = sm.AccumulateRewards(state, block, parent); err != nil {
		return
	}

	// Commit state objects/accounts to a temporary trie (does not save)
	// used to calculate the state root.
	state.Update(ethutil.Big0)
	if !bytes.Equal(header.Root, state.Root()) {
		err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root())
		return
	}

	// Calculate the td for this block
	td = CalculateTD(block, parent)
	// Sync the current block's state to the database
	state.Sync()
	// Remove transactions from the pool
	sm.txpool.RemoveSet(block.Transactions())

	chainlogger.Infof("processed block #%d (%x...)\n", header.Number, block.Hash()[0:4])

	return td, nil
}

// Validates the current block. Returns an error if the block was invalid,
// an uncle or anything that isn't on the current block chain.
// Validation validates easy over difficult (dagger takes longer time = difficult)
func (sm *BlockProcessor) ValidateBlock(block, parent *types.Block) error {
	if len(block.Header().Extra) > 1024 {
		return fmt.Errorf("Block extra data too long (%d)", len(block.Header().Extra))
	}

	expd := CalcDifficulty(block, parent)
	if expd.Cmp(block.Header().Difficulty) != 0 {
		return fmt.Errorf("Difficulty check failed for block %v, %v", block.Header().Difficulty, expd)
	}

	//expl := CalcGasLimit(parent, block)
	//if expl.Cmp(block.Header().GasLimit) != 0 {

	// block.gasLimit - parent.gasLimit <= parent.gasLimit / 1024
	a := new(big.Int).Sub(block.Header().GasLimit, parent.Header().GasLimit)
	b := new(big.Int).Div(parent.Header().GasLimit, big.NewInt(1024))
	if a.Cmp(b) > 0 {
		return fmt.Errorf("GasLimit check failed for block %v", block.Header().GasLimit)
	}

	if block.Time() < parent.Time() {
		return ValidationError("Block timestamp not after prev block (%v - %v)", block.Header().Time, parent.Header().Time)
	}

	if block.Time() > time.Now().Unix() {
		return BlockFutureErr
	}

	if new(big.Int).Sub(block.Number(), parent.Number()).Cmp(big.NewInt(1)) != 0 {
		return BlockNumberErr
	}

	// Verify the nonce of the block. Return an error if it's not valid
	if !sm.Pow.Verify(block) {
		return ValidationError("Block's nonce is invalid (= %v)", ethutil.Bytes2Hex(block.Header().Nonce))
	}

	return nil
}

func (sm *BlockProcessor) AccumulateRewards(statedb *state.StateDB, block, parent *types.Block) error {
	reward := new(big.Int).Set(BlockReward)

	ancestors := set.New()
	for _, ancestor := range sm.bc.GetAncestors(block, 7) {
		ancestors.Add(string(ancestor.Hash()))
	}

	uncles := set.New()
	uncles.Add(string(block.Hash()))
	for _, uncle := range block.Uncles() {
		if uncles.Has(string(uncle.Hash())) {
			// Error not unique
			return UncleError("Uncle not unique")
		}
		uncles.Add(string(uncle.Hash()))

		if !ancestors.Has(string(uncle.ParentHash)) {
			return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
		}

		if !sm.Pow.Verify(types.NewBlockWithHeader(uncle)) {
			return ValidationError("Uncle's nonce is invalid (= %v)", ethutil.Bytes2Hex(uncle.Nonce))
		}

		r := new(big.Int)
		r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16))

		statedb.AddBalance(uncle.Coinbase, r)

		reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32)))
	}

	// Get the account associated with the coinbase
	statedb.AddBalance(block.Header().Coinbase, reward)

	return nil
}

func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err error) {
	if !sm.bc.HasBlock(block.Header().ParentHash) {
		return nil, ParentError(block.Header().ParentHash)
	}

	sm.lastAttemptedBlock = block

	var (
		parent = sm.bc.GetBlock(block.Header().ParentHash)
		state  = state.New(parent.Root(), sm.db)
	)

	sm.TransitionState(state, parent, block, true)
	sm.AccumulateRewards(state, block, parent)

	return state.Logs(), nil
}
<commit_msg>changed message<commit_after>package core

import (
	"bytes"
	"fmt"
	"math/big"
	"sync"
	"time"

	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/ethutil"
	"github.com/ethereum/go-ethereum/event"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/pow"
	"github.com/ethereum/go-ethereum/pow/ezp"
	"github.com/ethereum/go-ethereum/state"
	"gopkg.in/fatih/set.v0"
)

type PendingBlockEvent struct {
	Block *types.Block
}

var statelogger = logger.NewLogger("BLOCK")

type BlockProcessor struct {
	db ethutil.Database
	// Mutex for locking the block processor. Blocks can only be handled one at a time
	mutex sync.Mutex
	// Canonical block chain
	bc *ChainManager
	// non-persistent key/value memory storage
	mem map[string]*big.Int
	// Proof of work used for validating
	Pow pow.PoW

	txpool *TxPool

	// The last attempted block is mainly used for debugging purposes
	// This does not have to be a valid block and will be set during
	// 'Process' & canonical validation.
	lastAttemptedBlock *types.Block

	events event.Subscription

	eventMux *event.TypeMux
}

func NewBlockProcessor(db ethutil.Database, txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
	sm := &BlockProcessor{
		db:       db,
		mem:      make(map[string]*big.Int),
		Pow:      ezp.New(),
		bc:       chainManager,
		eventMux: eventMux,
		txpool:   txpool,
	}

	return sm
}

func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) {
	coinbase := statedb.GetOrNewStateObject(block.Header().Coinbase)
	coinbase.SetGasPool(block.Header().GasLimit)

	// Process the transactions on to parent state
	receipts, _, _, _, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), transientProcess)
	if err != nil {
		return nil, err
	}

	return receipts, nil
}

func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) {
	// If we are mining this block and validating we want to set the logs back to 0
	statedb.EmptyLogs()

	txGas := new(big.Int).Set(tx.Gas())

	cb := statedb.GetStateObject(coinbase.Address())
	st := NewStateTransition(NewEnv(statedb, self.bc, tx, block), tx, cb)
	_, err := st.TransitionState()
	if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err)) {
		return nil, nil, err
	}

	txGas.Sub(txGas, st.gas)

	// Update the state with pending changes
	statedb.Update(txGas)

	cumulative := new(big.Int).Set(usedGas.Add(usedGas, txGas))
	receipt := types.NewReceipt(statedb.Root(), cumulative)
	receipt.SetLogs(statedb.Logs())
	receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
	chainlogger.Debugln(receipt)

	// Notify all subscribers
	if !transientProcess {
		go self.eventMux.Post(TxPostEvent{tx})
		logs := statedb.Logs()
		go self.eventMux.Post(logs)
	}

	return receipt, txGas, err
}

func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, types.Transactions, types.Transactions, types.Transactions, error) {
	var (
		receipts           types.Receipts
		handled, unhandled types.Transactions
		erroneous          types.Transactions
		totalUsedGas       = big.NewInt(0)
		err                error
		cumulativeSum      = new(big.Int)
	)

	for _, tx := range txs {
		receipt, txGas, err := self.ApplyTransaction(coinbase, statedb, block, tx, totalUsedGas, transientProcess)
		if err != nil {
			switch {
			case IsNonceErr(err):
				return nil, nil, nil, nil, err
			case state.IsGasLimitErr(err):
				return nil, nil, nil, nil, err
			default:
				statelogger.Infoln(err)
				erroneous = append(erroneous, tx)
				err = nil
			}
		}
		receipts = append(receipts, receipt)
		handled = append(handled, tx)

		cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice()))
	}

	block.Reward = cumulativeSum
	block.Header().GasUsed = totalUsedGas

	if transientProcess {
		go self.eventMux.Post(PendingBlockEvent{block})
	}

	return receipts, handled, unhandled, erroneous, err
}

// Process block will attempt to process the given block's transactions and applies them
// on top of the block's parent state (given it exists) and will return wether it was
// successful or not.
func (sm *BlockProcessor) Process(block *types.Block) (td *big.Int, err error) {
	// Processing a blocks may never happen simultaneously
	sm.mutex.Lock()
	defer sm.mutex.Unlock()

	header := block.Header()
	if sm.bc.HasBlock(header.Hash()) {
		return nil, &KnownBlockError{header.Number, header.Hash()}
	}

	if !sm.bc.HasBlock(header.ParentHash) {
		return nil, ParentError(header.ParentHash)
	}
	parent := sm.bc.GetBlock(header.ParentHash)

	return sm.processWithParent(block, parent)
}

func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big.Int, err error) {
	sm.lastAttemptedBlock = block

	// Create a new state based on the parent's root (e.g., create copy)
	state := state.New(parent.Root(), sm.db)

	// Block validation
	if err = sm.ValidateBlock(block, parent); err != nil {
		return
	}

	receipts, err := sm.TransitionState(state, parent, block, false)
	if err != nil {
		return
	}

	header := block.Header()

	// Validate the received block's bloom with the one derived from the generated receipts.
	// For valid blocks this should always validate to true.
	rbloom := types.CreateBloom(receipts)
	if bytes.Compare(rbloom, header.Bloom) != 0 {
		err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
		return
	}

	// The transactions Trie's root (R = (Tr [[H1, T1], [H2, T2], ... [Hn, Tn]]))
	// can be used by light clients to make sure they've received the correct Txs
	txSha := types.DeriveSha(block.Transactions())
	if bytes.Compare(txSha, header.TxHash) != 0 {
		err = fmt.Errorf("validating transaction root. received=%x got=%x", header.TxHash, txSha)
		return
	}

	// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
	receiptSha := types.DeriveSha(receipts)
	if bytes.Compare(receiptSha, header.ReceiptHash) != 0 {
		err = fmt.Errorf("validating receipt root. received=%x got=%x", header.ReceiptHash, receiptSha)
		return
	}

	// Accumulate static rewards; block reward, uncle's and uncle inclusion.
	if err = sm.AccumulateRewards(state, block, parent); err != nil {
		return
	}

	// Commit state objects/accounts to a temporary trie (does not save)
	// used to calculate the state root.
	state.Update(ethutil.Big0)
	if !bytes.Equal(header.Root, state.Root()) {
		err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root())
		return
	}

	// Calculate the td for this block
	td = CalculateTD(block, parent)
	// Sync the current block's state to the database
	state.Sync()
	// Remove transactions from the pool
	sm.txpool.RemoveSet(block.Transactions())

	chainlogger.Infof("processed block #%d (%x...)\n", header.Number, block.Hash()[0:4])

	return td, nil
}

// Validates the current block. Returns an error if the block was invalid,
// an uncle or anything that isn't on the current block chain.
// Validation validates easy over difficult (dagger takes longer time = difficult)
func (sm *BlockProcessor) ValidateBlock(block, parent *types.Block) error {
	if len(block.Header().Extra) > 1024 {
		return fmt.Errorf("Block extra data too long (%d)", len(block.Header().Extra))
	}

	expd := CalcDifficulty(block, parent)
	if expd.Cmp(block.Header().Difficulty) != 0 {
		return fmt.Errorf("Difficulty check failed for block %v, %v", block.Header().Difficulty, expd)
	}

	//expl := CalcGasLimit(parent, block)
	//if expl.Cmp(block.Header().GasLimit) != 0 {

	// block.gasLimit - parent.gasLimit <= parent.gasLimit / 1024
	a := new(big.Int).Sub(block.Header().GasLimit, parent.Header().GasLimit)
	b := new(big.Int).Div(parent.Header().GasLimit, big.NewInt(1024))
	if a.Cmp(b) > 0 {
		return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.Header().GasLimit, a, b)
	}

	if block.Time() < parent.Time() {
		return ValidationError("Block timestamp not after prev block (%v - %v)", block.Header().Time, parent.Header().Time)
	}

	if block.Time() > time.Now().Unix() {
		return BlockFutureErr
	}

	if new(big.Int).Sub(block.Number(), parent.Number()).Cmp(big.NewInt(1)) != 0 {
		return BlockNumberErr
	}

	// Verify the nonce of the block. Return an error if it's not valid
	if !sm.Pow.Verify(block) {
		return ValidationError("Block's nonce is invalid (= %v)", ethutil.Bytes2Hex(block.Header().Nonce))
	}

	return nil
}

func (sm *BlockProcessor) AccumulateRewards(statedb *state.StateDB, block, parent *types.Block) error {
	reward := new(big.Int).Set(BlockReward)

	ancestors := set.New()
	for _, ancestor := range sm.bc.GetAncestors(block, 7) {
		ancestors.Add(string(ancestor.Hash()))
	}

	uncles := set.New()
	uncles.Add(string(block.Hash()))
	for _, uncle := range block.Uncles() {
		if uncles.Has(string(uncle.Hash())) {
			// Error not unique
			return UncleError("Uncle not unique")
		}
		uncles.Add(string(uncle.Hash()))

		if !ancestors.Has(string(uncle.ParentHash)) {
			return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
		}

		if !sm.Pow.Verify(types.NewBlockWithHeader(uncle)) {
			return ValidationError("Uncle's nonce is invalid (= %v)", ethutil.Bytes2Hex(uncle.Nonce))
		}

		r := new(big.Int)
		r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16))

		statedb.AddBalance(uncle.Coinbase, r)

		reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32)))
	}

	// Get the account associated with the coinbase
	statedb.AddBalance(block.Header().Coinbase, reward)

	return nil
}

func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err error) {
	if !sm.bc.HasBlock(block.Header().ParentHash) {
		return nil, ParentError(block.Header().ParentHash)
	}

	sm.lastAttemptedBlock = block

	var (
		parent = sm.bc.GetBlock(block.Header().ParentHash)
		state  = state.New(parent.Root(), sm.db)
	)

	sm.TransitionState(state, parent, block, true)
	sm.AccumulateRewards(state, block, parent)

	return state.Logs(), nil
}
<|endoftext|>