<commit_before>package domain

import (
	"errors"
)

type ChannelRepository interface {
	Channels() ([]*Channel, error)
	Store(channel *Channel) error
	FindById(id int64) (*Channel, error)
}

// Chat channel
type Channel struct {
	Id       int64
	Name     string
	Public   bool
	Capacity int
	Clients  []*Client
}

func NewChannel(name string) *Channel {
	clients := make([]*Client, 0)
	return &Channel{
		Name:     name,
		Public:   true,
		Capacity: 0,
		Clients:  clients,
	}
}

// Add adds a client to the channel
func (c *Channel) Join(client *Client) error {
	if !c.HasAccess(client) {
		return errors.New("Client cannot join this channel")
	}

	if c.Capacity != 0 && len(c.Clients) >= c.Capacity {
		return errors.New("Channel is full")
	}

	c.Clients = append(c.Clients, client)
	client.Channel = c
	return nil
}

func (c *Channel) Send(message *Message) {
	for _, client := range c.Clients {
		client.ClientSender.Send(message)
	}
}

func (c *Channel) HasAccess(client *Client) bool {
	if c.Public {
		return true
	}
	//TODO: Channel permissions
	return false
}
<commit_msg>make sure a user cannot join channel twice<commit_after>package domain

import (
	"errors"
)

type ChannelRepository interface {
	Channels() ([]*Channel, error)
	Store(channel *Channel) error
	FindById(id int64) (*Channel, error)
}

// Chat channel
type Channel struct {
	Id       int64
	Name     string
	Public   bool
	Capacity int
	Clients  []*Client
}

func NewChannel(name string) *Channel {
	clients := make([]*Client, 0)
	return &Channel{
		Name:     name,
		Public:   true,
		Capacity: 0,
		Clients:  clients,
	}
}

// Add adds a client to the channel
func (c *Channel) Join(client *Client) error {
	if !c.HasAccess(client) {
		return errors.New("Client cannot join this channel")
	}

	if c.Capacity != 0 && len(c.Clients) >= c.Capacity {
		return errors.New("Channel is full")
	}

	for _, curClient := range c.Clients {
		if curClient.Id == client.Id {
			return errors.New("Client already in the channel")
		}
	}

	c.Clients = append(c.Clients, client)
	client.Channel = c
	return nil
}

func (c *Channel) Send(message *Message) {
	for _, client := range c.Clients {
		client.ClientSender.Send(message)
	}
}

func (c *Channel) HasAccess(client *Client) bool {
	if c.Public {
		return true
	}
	//TODO: Channel permissions
	return false
}
<|endoftext|>


<commit_before>package index

import (
	"fmt"
	"html/template"
	"net/http"

	"github.com/pivotal-golang/lager"
	"github.com/tedsuo/rata"

	"github.com/concourse/atc/builds"
	"github.com/concourse/atc/config"
	"github.com/concourse/atc/db"
	"github.com/concourse/atc/radar"
	"github.com/concourse/atc/web/routes"
)

type handler struct {
	logger lager.Logger

	// used for providing resource state
	radar *radar.Radar

	resources config.Resources
	jobs      config.Jobs
	db        db.DB
	template  *template.Template
}

func NewHandler(logger lager.Logger, radar *radar.Radar, resources config.Resources, jobs config.Jobs, db db.DB, template *template.Template) http.Handler {
	return &handler{
		logger: logger,

		radar: radar,

		resources: resources,
		jobs:      jobs,
		db:        db,
		template:  template,
	}
}

type TemplateData struct {
	Jobs  []JobStatus
	Nodes []DotNode
	Edges []DotEdge
}

type DotNode struct {
	ID    string            `json:"id"`
	Value map[string]string `json:"value,omitempty"`
}

type DotEdge struct {
	Source      string            `json:"u"`
	Destination string            `json:"v"`
	Value       map[string]string `json:"value,omitempty"`
}

type JobStatus struct {
	Job          config.Job
	CurrentBuild builds.Build
}

func (handler *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	data := TemplateData{
		Nodes: []DotNode{},
		Edges: []DotEdge{},
	}

	log := handler.logger.Session("index")

	currentBuilds := map[string]builds.Build{}

	for _, job := range handler.jobs {
		currentBuild, err := handler.db.GetCurrentBuild(job.Name)
		if err != nil {
			currentBuild.Status = builds.StatusPending
		}

		currentBuilds[job.Name] = currentBuild

		data.Jobs = append(data.Jobs, JobStatus{
			Job:          job,
			CurrentBuild: currentBuild,
		})
	}

	for _, resource := range handler.resources {
		resourceID := resourceNode(resource.Name)

		resourceURI, _ := routes.Routes.CreatePathForRoute(routes.GetResource, rata.Params{
			"resource": resource.Name,
		})

		failing, checking := handler.radar.ResourceStatus(resource.Name)

		var status string
		if failing {
			status = "failing"
		} else {
			status = "ok"
		}

		if checking {
			status += " checking"
		}

		data.Nodes = append(data.Nodes, DotNode{
			ID: resourceID,
			Value: map[string]string{
				"label":  fmt.Sprintf(`<h1 class="resource"><a href="%s">%s</a></h1>`, resourceURI, resource.Name),
				"type":   "resource",
				"status": status,
			},
		})
	}

	for _, job := range handler.jobs {
		jobID := jobNode(job.Name)
		currentBuild := currentBuilds[job.Name]

		var buildURI string
		var err error

		if currentBuild.Name != "" {
			buildURI, err = routes.Routes.CreatePathForRoute(routes.GetBuild, rata.Params{
				"job":   job.Name,
				"build": currentBuild.Name,
			})
		} else {
			buildURI, err = routes.Routes.CreatePathForRoute(routes.GetJob, rata.Params{
				"job": job.Name,
			})
		}

		if err != nil {
			log.Error("failed-to-create-route", err)
		}

		data.Nodes = append(data.Nodes, DotNode{
			ID: jobID,
			Value: map[string]string{
				"label":  fmt.Sprintf(`<h1 class="job"><a href="%s">%s</a>`, buildURI, job.Name),
				"status": string(currentBuild.Status),
				"type":   "job",
			},
		})

		edges := map[string]DotEdge{}

		for _, input := range job.Inputs {
			if len(input.Passed) > 0 {
				var nodeID string

				if len(input.Passed) > 1 {
					nodeID = jobID + "-input-" + input.Resource

					data.Nodes = append(data.Nodes, DotNode{
						ID: nodeID,
						Value: map[string]string{
							"useDef": "gateway",
						},
					})

					data.Edges = append(data.Edges, DotEdge{
						Source:      nodeID,
						Destination: jobID,
					})
				} else {
					nodeID = jobID
				}

				for _, passed := range input.Passed {
					currentBuild := currentBuilds[passed]

					passedJob, found := handler.jobs.Lookup(passed)
					if !found {
						panic("unknown job: " + passed)
					}

					existingEdge, found := edges[passed]
					if found {
						if len(passedJob.Inputs) > 1 {
							existingEdge.Value["label"] += "\n" + input.Resource
						}
					} else {
						value := map[string]string{
							"status": string(currentBuild.Status),
						}

						if len(passedJob.Inputs) > 1 {
							value["label"] = input.Resource
						}

						edges[passed] = DotEdge{
							Source:      jobNode(passed),
							Destination: nodeID,
							Value:       value,
						}
					}
				}
			} else {
				data.Edges = append(data.Edges, DotEdge{
					Source:      resourceNode(input.Resource),
					Destination: jobID,
				})
			}
		}

		for _, edge := range edges {
			data.Edges = append(data.Edges, edge)
		}

		for _, output := range job.Outputs {
			data.Edges = append(data.Edges, DotEdge{
				Source:      jobID,
				Destination: resourceNode(output.Resource),
				Value: map[string]string{
					"status": string(currentBuild.Status),
				},
			})
		}
	}

	err := handler.template.Execute(w, data)
	if err != nil {
		log.Fatal("failed-to-execute-template", err, lager.Data{
			"template-data": data,
		})
	}
}

func resourceNode(resource string) string {
	return "resource-" + resource
}

func jobNode(job string) string {
	return "job-" + job
}
<commit_msg>hide resources from pipeline with ?hide-resources<commit_after>package index

import (
	"fmt"
	"html/template"
	"net/http"

	"github.com/pivotal-golang/lager"
	"github.com/tedsuo/rata"

	"github.com/concourse/atc/builds"
	"github.com/concourse/atc/config"
	"github.com/concourse/atc/db"
	"github.com/concourse/atc/radar"
	"github.com/concourse/atc/web/routes"
)

type handler struct {
	logger lager.Logger

	// used for providing resource state
	radar *radar.Radar

	resources config.Resources
	jobs      config.Jobs
	db        db.DB
	template  *template.Template
}

func NewHandler(logger lager.Logger, radar *radar.Radar, resources config.Resources, jobs config.Jobs, db db.DB, template *template.Template) http.Handler {
	return &handler{
		logger: logger,

		radar: radar,

		resources: resources,
		jobs:      jobs,
		db:        db,
		template:  template,
	}
}

type TemplateData struct {
	Jobs  []JobStatus
	Nodes []DotNode
	Edges []DotEdge
}

type DotNode struct {
	ID    string            `json:"id"`
	Value map[string]string `json:"value,omitempty"`
}

type DotEdge struct {
	Source      string            `json:"u"`
	Destination string            `json:"v"`
	Value       map[string]string `json:"value,omitempty"`
}

type JobStatus struct {
	Job          config.Job
	CurrentBuild builds.Build
}

func (handler *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	_, hideResources := r.URL.Query()["hide-resources"]
	showResources := !hideResources

	data := TemplateData{
		Nodes: []DotNode{},
		Edges: []DotEdge{},
	}

	log := handler.logger.Session("index")

	currentBuilds := map[string]builds.Build{}

	for _, job := range handler.jobs {
		currentBuild, err := handler.db.GetCurrentBuild(job.Name)
		if err != nil {
			currentBuild.Status = builds.StatusPending
		}

		currentBuilds[job.Name] = currentBuild

		data.Jobs = append(data.Jobs, JobStatus{
			Job:          job,
			CurrentBuild: currentBuild,
		})
	}

	if showResources {
		for _, resource := range handler.resources {
			resourceID := resourceNode(resource.Name)

			resourceURI, _ := routes.Routes.CreatePathForRoute(routes.GetResource, rata.Params{
				"resource": resource.Name,
			})

			failing, checking := handler.radar.ResourceStatus(resource.Name)

			var status string
			if failing {
				status = "failing"
			} else {
				status = "ok"
			}

			if checking {
				status += " checking"
			}

			data.Nodes = append(data.Nodes, DotNode{
				ID: resourceID,
				Value: map[string]string{
					"label":  fmt.Sprintf(`<h1 class="resource"><a href="%s">%s</a></h1>`, resourceURI, resource.Name),
					"type":   "resource",
					"status": status,
				},
			})
		}
	}

	for _, job := range handler.jobs {
		jobID := jobNode(job.Name)
		currentBuild := currentBuilds[job.Name]

		var buildURI string
		var err error

		if currentBuild.Name != "" {
			buildURI, err = routes.Routes.CreatePathForRoute(routes.GetBuild, rata.Params{
				"job":   job.Name,
				"build": currentBuild.Name,
			})
		} else {
			buildURI, err = routes.Routes.CreatePathForRoute(routes.GetJob, rata.Params{
				"job": job.Name,
			})
		}

		if err != nil {
			log.Error("failed-to-create-route", err)
		}

		data.Nodes = append(data.Nodes, DotNode{
			ID: jobID,
			Value: map[string]string{
				"label":  fmt.Sprintf(`<h1 class="job"><a href="%s">%s</a>`, buildURI, job.Name),
				"status": string(currentBuild.Status),
				"type":   "job",
			},
		})

		edges := map[string]DotEdge{}

		for _, input := range job.Inputs {
			if len(input.Passed) > 0 {
				var nodeID string

				if len(input.Passed) > 1 {
					nodeID = jobID + "-input-" + input.Resource

					data.Nodes = append(data.Nodes, DotNode{
						ID: nodeID,
						Value: map[string]string{
							"useDef": "gateway",
						},
					})

					data.Edges = append(data.Edges, DotEdge{
						Source:      nodeID,
						Destination: jobID,
					})
				} else {
					nodeID = jobID
				}

				for _, passed := range input.Passed {
					currentBuild := currentBuilds[passed]

					passedJob, found := handler.jobs.Lookup(passed)
					if !found {
						panic("unknown job: " + passed)
					}

					existingEdge, found := edges[passed]
					if found {
						if len(passedJob.Inputs) > 1 {
							existingEdge.Value["label"] += "\n" + input.Resource
						}
					} else {
						value := map[string]string{
							"status": string(currentBuild.Status),
						}

						if len(passedJob.Inputs) > 1 {
							value["label"] = input.Resource
						}

						edges[passed] = DotEdge{
							Source:      jobNode(passed),
							Destination: nodeID,
							Value:       value,
						}
					}
				}
			} else if showResources {
				data.Edges = append(data.Edges, DotEdge{
					Source:      resourceNode(input.Resource),
					Destination: jobID,
				})
			}
		}

		for _, edge := range edges {
			data.Edges = append(data.Edges, edge)
		}

		if showResources {
			for _, output := range job.Outputs {
				data.Edges = append(data.Edges, DotEdge{
					Source:      jobID,
					Destination: resourceNode(output.Resource),
					Value: map[string]string{
						"status": string(currentBuild.Status),
					},
				})
			}
		}
	}

	err := handler.template.Execute(w, data)
	if err != nil {
		log.Fatal("failed-to-execute-template", err, lager.Data{
			"template-data": data,
		})
	}
}

func resourceNode(resource string) string {
	return "resource-" + resource
}

func jobNode(job string) string {
	return "job-" + job
}
<|endoftext|>


<commit_before>package influx

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	"github.com/influxdata/chronograf"
)

var _ chronograf.TimeSeries = &Client{}
var _ chronograf.TSDBStatus = &Client{}
var _ chronograf.Databases = &Client{}

// Shared transports for all clients to prevent leaking connections
var (
	skipVerifyTransport = &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	defaultTransport = &http.Transport{}
)

// Client is a device for retrieving time series data from an InfluxDB instance
type Client struct {
	URL                *url.URL
	Authorizer         Authorizer
	InsecureSkipVerify bool
	Logger             chronograf.Logger
}

// Response is a partial JSON decoded InfluxQL response used
// to check for some errors
type Response struct {
	Results json.RawMessage
	Err     string `json:"error,omitempty"`
}

// MarshalJSON returns the raw results bytes from the response
func (r Response) MarshalJSON() ([]byte, error) {
	return r.Results, nil
}

func (c *Client) query(u *url.URL, q chronograf.Query) (chronograf.Response, error) {
	u.Path = "query"
	req, err := http.NewRequest("POST", u.String(), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")
	command := q.Command
	// TODO(timraymond): move this upper Query() function
	if len(q.TemplateVars) > 0 {
		command = TemplateReplace(q.Command, q.TemplateVars)
	}
	logs := c.Logger.
		WithField("component", "proxy").
		WithField("host", req.Host).
		WithField("command", command).
		WithField("db", q.DB).
		WithField("rp", q.RP)
	logs.Debug("query")

	params := req.URL.Query()
	params.Set("q", command)
	params.Set("db", q.DB)
	params.Set("rp", q.RP)
	params.Set("epoch", "ms") // TODO(timraymond): set this based on analysis
	req.URL.RawQuery = params.Encode()

	if c.Authorizer != nil {
		if err := c.Authorizer.Set(req); err != nil {
			logs.Error("Error setting authorization header ", err)
			return nil, err
		}
	}

	hc := &http.Client{}
	if c.InsecureSkipVerify {
		hc.Transport = skipVerifyTransport
	} else {
		hc.Transport = defaultTransport
	}
	resp, err := hc.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var response Response
	dec := json.NewDecoder(resp.Body)
	decErr := dec.Decode(&response)

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("received status code %d from server: err: %s", resp.StatusCode, response.Err)
	}

	// ignore this error if we got an invalid status code
	if decErr != nil && decErr.Error() == "EOF" && resp.StatusCode != http.StatusOK {
		decErr = nil
	}

	// If we got a valid decode error, send that back
	if decErr != nil {
		logs.WithField("influx_status", resp.StatusCode).
			Error("Error parsing results from influxdb: err:", decErr)
		return nil, decErr
	}

	// If we don't have an error in our json response, and didn't get statusOK
	// then send back an error
	if resp.StatusCode != http.StatusOK && response.Err != "" {
		logs.
			WithField("influx_status", resp.StatusCode).
			Error("Received non-200 response from influxdb")

		return &response, fmt.Errorf("received status code %d from server",
			resp.StatusCode)
	}
	return &response, nil
}

type result struct {
	Response chronograf.Response
	Err      error
}

// Query issues a request to a configured InfluxDB instance for time series
// information specified by query. Queries must be "fully-qualified," and
// include both the database and retention policy. In-flight requests can be
// cancelled using the provided context.
func (c *Client) Query(ctx context.Context, q chronograf.Query) (chronograf.Response, error) {
	resps := make(chan (result))
	go func() {
		resp, err := c.query(c.URL, q)
		resps <- result{resp, err}
	}()

	select {
	case resp := <-resps:
		return resp.Response, resp.Err
	case <-ctx.Done():
		return nil, chronograf.ErrUpstreamTimeout
	}
}

// Connect caches the URL and optional Bearer Authorization for the data source
func (c *Client) Connect(ctx context.Context, src *chronograf.Source) error {
	u, err := url.Parse(src.URL)
	if err != nil {
		return err
	}
	c.Authorizer = DefaultAuthorization(src)
	// Only allow acceptance of all certs if the scheme is https AND the user opted into to the setting.
	if u.Scheme == "https" && src.InsecureSkipVerify {
		c.InsecureSkipVerify = src.InsecureSkipVerify
	}

	c.URL = u
	return nil
}

// Users transforms InfluxDB into a user store
func (c *Client) Users(ctx context.Context) chronograf.UsersStore {
	return c
}

// Roles aren't support in OSS
func (c *Client) Roles(ctx context.Context) (chronograf.RolesStore, error) {
	return nil, fmt.Errorf("Roles not support in open-source InfluxDB.  Roles are support in Influx Enterprise")
}

// Ping hits the influxdb ping endpoint and returns the type of influx
func (c *Client) Ping(ctx context.Context) error {
	_, _, err := c.pingTimeout(ctx)
	return err
}

// Version hits the influxdb ping endpoint and returns the version of influx
func (c *Client) Version(ctx context.Context) (string, error) {
	version, _, err := c.pingTimeout(ctx)
	return version, err
}

// Type hits the influxdb ping endpoint and returns the type of influx running
func (c *Client) Type(ctx context.Context) (string, error) {
	_, tsdbType, err := c.pingTimeout(ctx)
	return tsdbType, err
}

func (c *Client) pingTimeout(ctx context.Context) (string, string, error) {
	resps := make(chan (pingResult))
	go func() {
		version, tsdbType, err := c.ping(c.URL)
		resps <- pingResult{version, tsdbType, err}
	}()

	select {
	case resp := <-resps:
		return resp.Version, resp.Type, resp.Err
	case <-ctx.Done():
		return "", "", chronograf.ErrUpstreamTimeout
	}
}

type pingResult struct {
	Version string
	Type    string
	Err     error
}

func (c *Client) ping(u *url.URL) (string, string, error) {
	u.Path = "ping"

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return "", "", err
	}

	hc := &http.Client{}
	if c.InsecureSkipVerify {
		hc.Transport = skipVerifyTransport
	} else {
		hc.Transport = defaultTransport
	}

	resp, err := hc.Do(req)
	if err != nil {
		return "", "", err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", "", err
	}

	if resp.StatusCode != http.StatusNoContent {
		var err = fmt.Errorf(string(body))
		return "", "", err
	}

	version := resp.Header.Get("X-Influxdb-Version")
	if strings.Contains(version, "-c") {
		return version, chronograf.InfluxEnterprise, nil
	} else if strings.Contains(version, "relay") {
		return version, chronograf.InfluxRelay, nil
	}
	return version, chronograf.InfluxDB, nil
}
<commit_msg>Add check for enterprise build type header<commit_after>package influx

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	"github.com/influxdata/chronograf"
)

var _ chronograf.TimeSeries = &Client{}
var _ chronograf.TSDBStatus = &Client{}
var _ chronograf.Databases = &Client{}

// Shared transports for all clients to prevent leaking connections
var (
	skipVerifyTransport = &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	defaultTransport = &http.Transport{}
)

// Client is a device for retrieving time series data from an InfluxDB instance
type Client struct {
	URL                *url.URL
	Authorizer         Authorizer
	InsecureSkipVerify bool
	Logger             chronograf.Logger
}

// Response is a partial JSON decoded InfluxQL response used
// to check for some errors
type Response struct {
	Results json.RawMessage
	Err     string `json:"error,omitempty"`
}

// MarshalJSON returns the raw results bytes from the response
func (r Response) MarshalJSON() ([]byte, error) {
	return r.Results, nil
}

func (c *Client) query(u *url.URL, q chronograf.Query) (chronograf.Response, error) {
	u.Path = "query"
	req, err := http.NewRequest("POST", u.String(), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")
	command := q.Command
	// TODO(timraymond): move this upper Query() function
	if len(q.TemplateVars) > 0 {
		command = TemplateReplace(q.Command, q.TemplateVars)
	}
	logs := c.Logger.
		WithField("component", "proxy").
		WithField("host", req.Host).
		WithField("command", command).
		WithField("db", q.DB).
		WithField("rp", q.RP)
	logs.Debug("query")

	params := req.URL.Query()
	params.Set("q", command)
	params.Set("db", q.DB)
	params.Set("rp", q.RP)
	params.Set("epoch", "ms") // TODO(timraymond): set this based on analysis
	req.URL.RawQuery = params.Encode()

	if c.Authorizer != nil {
		if err := c.Authorizer.Set(req); err != nil {
			logs.Error("Error setting authorization header ", err)
			return nil, err
		}
	}

	hc := &http.Client{}
	if c.InsecureSkipVerify {
		hc.Transport = skipVerifyTransport
	} else {
		hc.Transport = defaultTransport
	}
	resp, err := hc.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var response Response
	dec := json.NewDecoder(resp.Body)
	decErr := dec.Decode(&response)

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("received status code %d from server: err: %s", resp.StatusCode, response.Err)
	}

	// ignore this error if we got an invalid status code
	if decErr != nil && decErr.Error() == "EOF" && resp.StatusCode != http.StatusOK {
		decErr = nil
	}

	// If we got a valid decode error, send that back
	if decErr != nil {
		logs.WithField("influx_status", resp.StatusCode).
			Error("Error parsing results from influxdb: err:", decErr)
		return nil, decErr
	}

	// If we don't have an error in our json response, and didn't get statusOK
	// then send back an error
	if resp.StatusCode != http.StatusOK && response.Err != "" {
		logs.
			WithField("influx_status", resp.StatusCode).
			Error("Received non-200 response from influxdb")

		return &response, fmt.Errorf("received status code %d from server",
			resp.StatusCode)
	}
	return &response, nil
}

type result struct {
	Response chronograf.Response
	Err      error
}

// Query issues a request to a configured InfluxDB instance for time series
// information specified by query. Queries must be "fully-qualified," and
// include both the database and retention policy. In-flight requests can be
// cancelled using the provided context.
func (c *Client) Query(ctx context.Context, q chronograf.Query) (chronograf.Response, error) {
	resps := make(chan (result))
	go func() {
		resp, err := c.query(c.URL, q)
		resps <- result{resp, err}
	}()

	select {
	case resp := <-resps:
		return resp.Response, resp.Err
	case <-ctx.Done():
		return nil, chronograf.ErrUpstreamTimeout
	}
}

// Connect caches the URL and optional Bearer Authorization for the data source
func (c *Client) Connect(ctx context.Context, src *chronograf.Source) error {
	u, err := url.Parse(src.URL)
	if err != nil {
		return err
	}
	c.Authorizer = DefaultAuthorization(src)
	// Only allow acceptance of all certs if the scheme is https AND the user opted into to the setting.
	if u.Scheme == "https" && src.InsecureSkipVerify {
		c.InsecureSkipVerify = src.InsecureSkipVerify
	}

	c.URL = u
	return nil
}

// Users transforms InfluxDB into a user store
func (c *Client) Users(ctx context.Context) chronograf.UsersStore {
	return c
}

// Roles aren't support in OSS
func (c *Client) Roles(ctx context.Context) (chronograf.RolesStore, error) {
	return nil, fmt.Errorf("Roles not support in open-source InfluxDB.  Roles are support in Influx Enterprise")
}

// Ping hits the influxdb ping endpoint and returns the type of influx
func (c *Client) Ping(ctx context.Context) error {
	_, _, err := c.pingTimeout(ctx)
	return err
}

// Version hits the influxdb ping endpoint and returns the version of influx
func (c *Client) Version(ctx context.Context) (string, error) {
	version, _, err := c.pingTimeout(ctx)
	return version, err
}

// Type hits the influxdb ping endpoint and returns the type of influx running
func (c *Client) Type(ctx context.Context) (string, error) {
	_, tsdbType, err := c.pingTimeout(ctx)
	return tsdbType, err
}

func (c *Client) pingTimeout(ctx context.Context) (string, string, error) {
	resps := make(chan (pingResult))
	go func() {
		version, tsdbType, err := c.ping(c.URL)
		resps <- pingResult{version, tsdbType, err}
	}()

	select {
	case resp := <-resps:
		return resp.Version, resp.Type, resp.Err
	case <-ctx.Done():
		return "", "", chronograf.ErrUpstreamTimeout
	}
}

type pingResult struct {
	Version string
	Type    string
	Err     error
}

func (c *Client) ping(u *url.URL) (string, string, error) {
	u.Path = "ping"

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return "", "", err
	}

	hc := &http.Client{}
	if c.InsecureSkipVerify {
		hc.Transport = skipVerifyTransport
	} else {
		hc.Transport = defaultTransport
	}

	resp, err := hc.Do(req)
	if err != nil {
		return "", "", err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", "", err
	}

	if resp.StatusCode != http.StatusNoContent {
		var err = fmt.Errorf(string(body))
		return "", "", err
	}

	version := resp.Header.Get("X-Influxdb-Build")
	if version == "ENT" {
		return version, chronograf.InfluxEnterprise, nil
	}
	version = resp.Header.Get("X-Influxdb-Version")
	if strings.Contains(version, "-c") {
		return version, chronograf.InfluxEnterprise, nil
	} else if strings.Contains(version, "relay") {
		return version, chronograf.InfluxRelay, nil
	}

	return version, chronograf.InfluxDB, nil
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package parallel_test

import (
	"sort"
	"sync"
	stdtesting "testing"
	"time"

	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/utils/parallel"
)

func Test(t *stdtesting.T) {
	gc.TestingT(t)
}

type parallelSuite struct{}

var _ = gc.Suite(&parallelSuite{})

func (*parallelSuite) TestParallelMaxPar(c *gc.C) {
	const (
		totalDo = 10
		maxConcurrentRunnersPar  = 3
	)
	var mu sync.Mutex
	maxConcurrentRunners := 0
	nbRunners := 0
	nbRuns := 0
	parallelRunner := parallel.NewRun(maxConcurrentRunnersPar)
	for i := 0; i < totalDo; i++ {
		parallelRunner.Do(func() error {
			mu.Lock()
			nbRuns++
			nbRunners++
			if nbRunners > maxConcurrentRunners {
				maxConcurrentRunners = nbRunners
			}
			mu.Unlock()
			time.Sleep(time.Second/10)
			mu.Lock()
			nbRunners--
			mu.Unlock()
			return nil
		})
	}
	err := parallelRunner.Wait()
	if nbRunners != 0 {
		c.Errorf("%d functions still running", nbRunners)
	}
	if nbRuns != totalDo {
		c.Errorf("all functions not executed; want %d got %d", totalDo, nbRuns)
	}
	c.Check(err, gc.IsNil)
	if maxConcurrentRunners != maxConcurrentRunnersPar {
		c.Errorf("wrong number of do's ran at once; want %d got %d", maxConcurrentRunnersPar, maxConcurrentRunners)
	}
}

type intError int

func (intError) Error() string {
	return "error"
}

func (*parallelSuite) TestParallelError(c *gc.C) {
	const (
		totalDo = 10
		errDo   = 5
	)
	parallelRun := parallel.NewRun(6)
	for i := 0; i < totalDo; i++ {
		i := i
		if i >= errDo {
			parallelRun.Do(func() error {
				return intError(i)
			})
		} else {
			parallelRun.Do(func() error {
				return nil
			})
		}
	}
	err := parallelRun.Wait()
	c.Check(err, gc.NotNil)
	errs := err.(parallel.Errors)
	c.Check(len(errs), gc.Equals, totalDo-errDo)
	ints := make([]int, len(errs))
	for i, err := range errs {
		ints[i] = int(err.(intError))
	}
	sort.Ints(ints)
	for i, n := range ints {
		c.Check(n, gc.Equals, i+errDo)
	}
}

func (*parallelSuite) TestZeroWorkerPanics(c *gc.C) {
	defer func() {
		r := recover()
		c.Check(r, gc.Matches, "parameter maxPar must be >= 1")
	}()
	parallel.NewRun(0)
}
<commit_msg>gofmt.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package parallel_test

import (
	"sort"
	"sync"
	stdtesting "testing"
	"time"

	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/utils/parallel"
)

func Test(t *stdtesting.T) {
	gc.TestingT(t)
}

type parallelSuite struct{}

var _ = gc.Suite(&parallelSuite{})

func (*parallelSuite) TestParallelMaxPar(c *gc.C) {
	const (
		totalDo                 = 10
		maxConcurrentRunnersPar = 3
	)
	var mu sync.Mutex
	maxConcurrentRunners := 0
	nbRunners := 0
	nbRuns := 0
	parallelRunner := parallel.NewRun(maxConcurrentRunnersPar)
	for i := 0; i < totalDo; i++ {
		parallelRunner.Do(func() error {
			mu.Lock()
			nbRuns++
			nbRunners++
			if nbRunners > maxConcurrentRunners {
				maxConcurrentRunners = nbRunners
			}
			mu.Unlock()
			time.Sleep(time.Second / 10)
			mu.Lock()
			nbRunners--
			mu.Unlock()
			return nil
		})
	}
	err := parallelRunner.Wait()
	if nbRunners != 0 {
		c.Errorf("%d functions still running", nbRunners)
	}
	if nbRuns != totalDo {
		c.Errorf("all functions not executed; want %d got %d", totalDo, nbRuns)
	}
	c.Check(err, gc.IsNil)
	if maxConcurrentRunners != maxConcurrentRunnersPar {
		c.Errorf("wrong number of do's ran at once; want %d got %d", maxConcurrentRunnersPar, maxConcurrentRunners)
	}
}

type intError int

func (intError) Error() string {
	return "error"
}

func (*parallelSuite) TestParallelError(c *gc.C) {
	const (
		totalDo = 10
		errDo   = 5
	)
	parallelRun := parallel.NewRun(6)
	for i := 0; i < totalDo; i++ {
		i := i
		if i >= errDo {
			parallelRun.Do(func() error {
				return intError(i)
			})
		} else {
			parallelRun.Do(func() error {
				return nil
			})
		}
	}
	err := parallelRun.Wait()
	c.Check(err, gc.NotNil)
	errs := err.(parallel.Errors)
	c.Check(len(errs), gc.Equals, totalDo-errDo)
	ints := make([]int, len(errs))
	for i, err := range errs {
		ints[i] = int(err.(intError))
	}
	sort.Ints(ints)
	for i, n := range ints {
		c.Check(n, gc.Equals, i+errDo)
	}
}

func (*parallelSuite) TestZeroWorkerPanics(c *gc.C) {
	defer func() {
		r := recover()
		c.Check(r, gc.Matches, "parameter maxPar must be >= 1")
	}()
	parallel.NewRun(0)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/labstack/echo"

	"labix.org/v2/mgo"
)

func main() {

	session, err := mgo.Dial("127.0.0.1")
	if err != nil {
		panic(err)
	}

	defer session.Close()

	e := echo.New()
	e.Static("/", "public")
	e.Run(":3050")
}
<commit_msg>update<commit_after>package main

import (
	"github.com/labstack/echo"

	"labix.org/v2/mgo"
)

func main() {

	session, err := mgo.Dial("127.0.0.1")
	if err != nil {
		panic(err)
	}

	defer session.Close()

	e := echo.New()
	e.Static("/", "public")
	e.Run(":5025")
}
<|endoftext|>


<commit_before>package helpers

func ConvertTypeForJS(s string) string {
	conv := map[string]string{
		"array":   "array",
		"boolean": "boolean",
		"integer": "number",
		"number":  "number",
		"object":  "object",
		"string":  "string",
	}
	return conv[s]
}
<commit_msg>Return given type when target is not found<commit_after>package helpers

func ConvertTypeForJS(s string) string {
	v, ok := map[string]string{
		"integer": "number",
	}[s]
	if !ok {
		return s
	}
	return v
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"io"
	"io/ioutil"
	"log"
)

import (
	"github.com/qiniu/api/conf"
	"github.com/qiniu/api/rsf"
)

func main() {
	ak := flag.String("ak", "", "access key")
	sk := flag.String("sk", "", "secret key")
	bucket := flag.String("bucket", "", "bucket")
	out := flag.String("o", "", "output file")
	flag.Parse()
	if ak == nil || sk == nil || bucket == nil || out == nil {
		log.Fatalln("invalid args")
		return
	}
	conf.ACCESS_KEY = *ak
	conf.SECRET_KEY = *sk
	list := []rsf.ListItem{}
	client := rsf.New(nil)
	marker := ""
	var err error
	for {
		var ret []rsf.ListItem
		ret, marker, err = client.ListPrefix(nil, *bucket, "", marker, 1000)
		if err != nil {
			if err != io.EOF {
				log.Fatalln("error occured!", err)
				break
			}
		}
		list = append(list, ret...)
	}
	data, err := json.MarshalIndent(list, "", "")
	if err != nil {
		log.Fatalln("list data error", err)
		return
	}
	err = ioutil.WriteFile(*out, data, 0)
	if err != nil {
		log.Fatalln("write file failed")
	}
}
<commit_msg>usage<commit_after>package main

import (
	"encoding/json"
	"flag"
	"io"
	"io/ioutil"
	"log"
)

import (
	"github.com/qiniu/api/conf"
	"github.com/qiniu/api/rsf"
)

func main() {
	ak := flag.String("ak", "", "access key")
	sk := flag.String("sk", "", "secret key")
	bucket := flag.String("bucket", "", "bucket")
	out := flag.String("o", "", "output file")
	flag.Parse()
	if *ak == "" || *sk == "" || *bucket == "" || *out == "" {
		flag.PrintDefaults()
		log.Fatalln("invalid args")
		return
	}
	conf.ACCESS_KEY = *ak
	conf.SECRET_KEY = *sk
	list := []rsf.ListItem{}
	client := rsf.New(nil)
	marker := ""
	var err error
	for {
		var ret []rsf.ListItem
		ret, marker, err = client.ListPrefix(nil, *bucket, "", marker, 1000)
		if err != nil {
			if err != io.EOF {
				log.Fatalln("error occured!", err)
				break
			}
		}
		list = append(list, ret...)
	}
	data, err := json.MarshalIndent(list, "", "")
	if err != nil {
		log.Fatalln("list data error", err)
		return
	}
	err = ioutil.WriteFile(*out, data, 0)
	if err != nil {
		log.Fatalln("write file failed")
	}
}
<|endoftext|>


<commit_before>//
// templates.go
// template model interfaces
//
package srnd

import (
  "fmt"
  "github.com/hoisie/mustache"
  "io"
  "io/ioutil"
  "log"
  "path/filepath"
  "sort"
)

type templateEngine struct {
  // every newsgroup
  groups map[string]GroupModel
  // loaded templates
  templates map[string]string
  // root directory for templates
  template_dir string
}

func (self templateEngine) templateCached(name string) (ok bool) {
  _, ok = self.templates[name]
  return
}

func (self templateEngine) getTemplate(name string) (t string) {
  if self.templateCached(name) {
    t, _ = self.templates[name]
  } else {
    // ignores errors, this is probably bad
    b, _ := ioutil.ReadFile(filepath.Join(self.template_dir, name))
    t = string(b)
    self.templates[name] = t
  }
  return
}

func (self templateEngine) renderTemplate(name string, obj interface{}) string {
  t := self.getTemplate(name)
  return mustache.Render(t, obj)
}

// get a board model given a newsgroup
// load un updated board model if we don't have it
func (self templateEngine) obtainBoard(prefix, frontend, group string, db Database) (model GroupModel) {
  model, ok := self.groups[group]
  if ! ok  {
    p := db.GetGroupPageCount(group)
    pages := int(p)
    // ignore error
    perpage, _ := db.GetThreadsPerPage(group)
    for page := 0 ; page < pages ; page ++ {
      model = append(model, db.GetGroupForPage(prefix, frontend, group, page, int(perpage)))
    }
    self.groups[group] = model
  }
  return

}
// generate a board page
func (self templateEngine) genBoardPage(prefix, frontend, newsgroup string, page int, outfile string, db Database) {

  // get it
  board := self.obtainBoard(prefix, frontend, newsgroup, db)
  // update it
  board = board.Update(page, db)
  if page >= len(board) {
    log.Println("board page should not exist", newsgroup, "page", page)
    return
  }
  wr, err := OpenFileWriter(outfile)
  if err == nil {
    board[page].RenderTo(wr)
    wr.Close()
    log.Println("wrote file", outfile)
  } else {
    log.Println("error generating board page", page, "for", newsgroup, err)
  }
  // save it
  self.groups[newsgroup] = board
}

// generate every page for a board
func (self templateEngine) genBoard(prefix, frontend, newsgroup, outdir string, db Database) {
  // get it
  board := self.obtainBoard(prefix, frontend, newsgroup, db)
  // update it
  board = board.UpdateAll(db)
  // save it
  self.groups[newsgroup] = board

  pages := len(board)
  for page := 0 ; page < pages ; page ++ {
    outfile := filepath.Join(outdir, fmt.Sprintf("%s-%d.html", newsgroup, page))
    wr, err := OpenFileWriter(outfile)
    if err == nil {
      board[page].RenderTo(wr)
      wr.Close()
      log.Println("wrote file", outfile)
    } else {
      log.Println("error generating board page", page, "for", newsgroup, err)
    }
  }
}

func (self templateEngine) genUkko(prefix, frontend, outfile string, database Database) {
  // get the last 15 bumped threads globally
  var threads []ThreadModel
  for _, article := range database.GetLastBumpedThreads("", 15) {
    newsgroup, msgid := article[1], article[0]
    // obtain board
    board := self.obtainBoard(prefix, frontend, newsgroup, database)
    board = board.Update(0, database)
    for _, th := range(board[0].Threads()) {
      if th.OP().MessageID() == msgid {
        threads = append(threads, th.Update(database))
        break
      }
    }
    // save state of board
    self.groups[newsgroup] = board
  }
  wr, err := OpenFileWriter(outfile)
  if err == nil {
    io.WriteString(wr, template.renderTemplate("ukko.mustache", map[string]interface{} { "prefix" : prefix, "threads" : threads }))
    wr.Close()
  } else {
    log.Println("error generating ukko", err)
  }
}

func (self templateEngine) genThread(messageID, prefix, frontend, outfile string, db Database) {

  newsgroup, page, err := db.GetPageForRootMessage(messageID)
  if err != nil {
    log.Println("did not get root post info when regenerating thread", messageID, err)
    return
  }
  // get it
  board := self.obtainBoard(prefix, frontend, newsgroup, db)
  // update our thread
  if int(page) >= len(board) {
    log.Println("we need to update the board model", page, "does not exist yet")
    board = board.UpdateAll(db)
    self.groups[newsgroup] = board
    if int(page) >= len(board) {
      log.Println("we are going way too fast not regenerating thread", messageID)
      return
    }
  }
  // if we lack this thread, reload the board page
  // otherwise just reload the thread
  if board[page].HasThread(messageID) {
    board[page] = board[page].UpdateThread(messageID, db)
  } else {
    board[page] = board[page].Update(db)
  }
  for _, th := range board[page].Threads() {
    if th.OP().MessageID() == messageID {
      th = th.Update(db)
      // we found it
      wr, err := OpenFileWriter(outfile)
      if err == nil {
        th.RenderTo(wr)
        wr.Close()
        log.Println("wrote file", outfile)
      } else {
        log.Println("did not write", outfile, err)
      }
    }
  }
  // save it
  self.groups[newsgroup] = board
}

func newTemplateEngine(dir string) *templateEngine {
  return &templateEngine{
    groups: make(map[string]GroupModel),
    templates: make(map[string]string),
    template_dir: dir,
  }
}

var template = newTemplateEngine(defaultTemplateDir())

func renderPostForm(prefix, board, op_msg_id string) string {
  url := prefix + "post/" + board
  button := "New Thread"
  if op_msg_id != "" {
    button = "Reply"
  }
  return template.renderTemplate("postform.mustache", map[string]string { "post_url" : url, "reference" : op_msg_id , "button" : button } )
}

func (self templateEngine) genFrontPage(top_count int, frontend_name, outfile string, db Database) {
  // the graph for the front page
  var frontpage_graph boardPageRows

  // for each group
  groups := db.GetAllNewsgroups()
  for idx, group := range groups {
    if idx >= top_count {
      break
    }
    // posts per hour
    hour := db.CountPostsInGroup(group, 3600)
    // posts per day
    day := db.CountPostsInGroup(group, 86400)
    // posts total
    all := db.CountPostsInGroup(group, 0)
    frontpage_graph = append(frontpage_graph, boardPageRow{
      All: all,
      Day: day,
      Hour: hour,
      Board: group,
    })
  }
  wr, err := OpenFileWriter(outfile)
  if err != nil {
    log.Println("cannot render front page", err)
    return
  }

  param := make(map[string]interface{})
  sort.Sort(frontpage_graph)
  param["graph"] = frontpage_graph
  param["frontend"] = frontend_name
  param["totalposts"] = db.ArticleCount()
  _, err = io.WriteString(wr, self.renderTemplate("frontpage.mustache", param))
  if err != nil {
    log.Println("error writing front page", err)
  }
  wr.Close()
}
<commit_msg>try fixing model caching<commit_after>//
// templates.go
// template model interfaces
//
package srnd

import (
  "fmt"
  "github.com/hoisie/mustache"
  "io"
  "io/ioutil"
  "log"
  "path/filepath"
  "sort"
)

type templateEngine struct {
  // every newsgroup
  groups map[string]GroupModel
  // loaded templates
  templates map[string]string
  // root directory for templates
  template_dir string
}

func (self templateEngine) templateCached(name string) (ok bool) {
  _, ok = self.templates[name]
  return
}

func (self templateEngine) getTemplate(name string) (t string) {
  if self.templateCached(name) {
    t, _ = self.templates[name]
  } else {
    // ignores errors, this is probably bad
    b, _ := ioutil.ReadFile(filepath.Join(self.template_dir, name))
    t = string(b)
    self.templates[name] = t
  }
  return
}

func (self templateEngine) renderTemplate(name string, obj interface{}) string {
  t := self.getTemplate(name)
  return mustache.Render(t, obj)
}

// get a board model given a newsgroup
// load un updated board model if we don't have it
func (self templateEngine) obtainBoard(prefix, frontend, group string, db Database) (model GroupModel) {
  model, ok := self.groups[group]
  if ! ok  {
    p := db.GetGroupPageCount(group)
    pages := int(p)
    // ignore error
    perpage, _ := db.GetThreadsPerPage(group)
    for page := 0 ; page < pages ; page ++ {
      model = append(model, db.GetGroupForPage(prefix, frontend, group, page, int(perpage)))
    }
    self.groups[group] = model
  }
  return

}
// generate a board page
func (self templateEngine) genBoardPage(prefix, frontend, newsgroup string, page int, outfile string, db Database) {

  // get it
  board := self.obtainBoard(prefix, frontend, newsgroup, db)
  // update it
  board = board.Update(page, db)
  if page >= len(board) {
    log.Println("board page should not exist", newsgroup, "page", page)
    return
  }
  wr, err := OpenFileWriter(outfile)
  if err == nil {
    board[page].RenderTo(wr)
    wr.Close()
    log.Println("wrote file", outfile)
  } else {
    log.Println("error generating board page", page, "for", newsgroup, err)
  }
  // save it
  self.groups[newsgroup] = board
}

// generate every page for a board
func (self templateEngine) genBoard(prefix, frontend, newsgroup, outdir string, db Database) {
  // get it
  board := self.obtainBoard(prefix, frontend, newsgroup, db)
  // update it
  board = board.UpdateAll(db)
  // save it
  self.groups[newsgroup] = board

  pages := len(board)
  for page := 0 ; page < pages ; page ++ {
    outfile := filepath.Join(outdir, fmt.Sprintf("%s-%d.html", newsgroup, page))
    wr, err := OpenFileWriter(outfile)
    if err == nil {
      board[page].RenderTo(wr)
      wr.Close()
      log.Println("wrote file", outfile)
    } else {
      log.Println("error generating board page", page, "for", newsgroup, err)
    }
  }
}

func (self templateEngine) genUkko(prefix, frontend, outfile string, database Database) {
  // get the last 15 bumped threads globally
  var threads []ThreadModel
  for _, article := range database.GetLastBumpedThreads("", 15) {
    newsgroup, msgid := article[1], article[0]
    // obtain board
    board := self.obtainBoard(prefix, frontend, newsgroup, database)
    board = board.Update(0, database)
    for _, th := range(board[0].Threads()) {
      if th.OP().MessageID() == msgid {
        threads = append(threads, th.Update(database))
        break
      }
    }
    // save state of board
    self.groups[newsgroup] = board
  }
  wr, err := OpenFileWriter(outfile)
  if err == nil {
    io.WriteString(wr, template.renderTemplate("ukko.mustache", map[string]interface{} { "prefix" : prefix, "threads" : threads }))
    wr.Close()
  } else {
    log.Println("error generating ukko", err)
  }
}

func (self templateEngine) genThread(messageID, prefix, frontend, outfile string, db Database) {

  newsgroup, page, err := db.GetPageForRootMessage(messageID)
  if err != nil {
    log.Println("did not get root post info when regenerating thread", messageID, err)
    return
  }
  var th ThreadModel
  // get it
  board := self.obtainBoard(prefix, frontend, newsgroup, db)
  // update our thread
  if int(page) < len(board) {
    // if we lack this thread, reload the board page
    // otherwise just reload the thread
    if board[page].HasThread(messageID) {
      board[page] = board[page].UpdateThread(messageID, db)
    } else {
      board[page] = board[page].Update(db)
    }
    for _, th = range board[page].Threads() {
      if th.OP().MessageID() == messageID {
        th = th.Update(db)
      }
    }
  } else {
    // something is wrong
    // find it manually
    board = board.UpdateAll(db)
    for idx, board_page := range board {
      if board_page.HasThread(messageID) {
        // we have it, obtain the thread
        for _, th = range board_page.Threads() {
          if th.OP().MessageID() == messageID {
            // update the thread
            th = th.Update(db)
            // break out of inner loop
            break
          }
        }
        // save the model
        board[idx] = board_page
        // break out of outer loop
        break
      }
    }
  }
  // by here if we don't have the thread model we'll panic
  // this is fine

  wr, err := OpenFileWriter(outfile)
  if err == nil {
    th.RenderTo(wr)
    wr.Close()
    log.Println("wrote file", outfile)
  } else {
    log.Println("did not write", outfile, err)
  }
  // save it
  self.groups[newsgroup] = board
}

func newTemplateEngine(dir string) *templateEngine {
  return &templateEngine{
    groups: make(map[string]GroupModel),
    templates: make(map[string]string),
    template_dir: dir,
  }
}

var template = newTemplateEngine(defaultTemplateDir())

func renderPostForm(prefix, board, op_msg_id string) string {
  url := prefix + "post/" + board
  button := "New Thread"
  if op_msg_id != "" {
    button = "Reply"
  }
  return template.renderTemplate("postform.mustache", map[string]string { "post_url" : url, "reference" : op_msg_id , "button" : button } )
}

func (self templateEngine) genFrontPage(top_count int, frontend_name, outfile string, db Database) {
  // the graph for the front page
  var frontpage_graph boardPageRows

  // for each group
  groups := db.GetAllNewsgroups()
  for idx, group := range groups {
    if idx >= top_count {
      break
    }
    // posts per hour
    hour := db.CountPostsInGroup(group, 3600)
    // posts per day
    day := db.CountPostsInGroup(group, 86400)
    // posts total
    all := db.CountPostsInGroup(group, 0)
    frontpage_graph = append(frontpage_graph, boardPageRow{
      All: all,
      Day: day,
      Hour: hour,
      Board: group,
    })
  }
  wr, err := OpenFileWriter(outfile)
  if err != nil {
    log.Println("cannot render front page", err)
    return
  }

  param := make(map[string]interface{})
  sort.Sort(frontpage_graph)
  param["graph"] = frontpage_graph
  param["frontend"] = frontend_name
  param["totalposts"] = db.ArticleCount()
  _, err = io.WriteString(wr, self.renderTemplate("frontpage.mustache", param))
  if err != nil {
    log.Println("error writing front page", err)
  }
  wr.Close()
}
<|endoftext|>


<commit_before>package headerfs

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"sort"

	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcwallet/walletdb"
)

var (
	// indexBucket is the main top-level bucket for the header index.
	// Nothing is stored in this bucket other than the sub-buckets which
	// contains the indexes for the various header types.
	indexBucket = []byte("header-index")

	// bitcoinTip is the key which tracks the "tip" of the block header
	// chain. The value of this key will be the current block hash of the
	// best known chain that we're synced to.
	bitcoinTip = []byte("bitcoin")

	// regFilterTip is the key which tracks the "tip" of the regular
	// compact filter header chain. The value of this key will be the
	// current block hash of the best known chain that the headers for
	// regular filter are synced to.
	regFilterTip = []byte("regular")

	// extFilterTip is the key which tracks the "tip" of the extended
	// compact filter header chain. The value of this key will be the
	// current block hash of the best known chain that the headers for
	// extended filter are synced to.
	extFilterTip = []byte("ext")
)

var (
	// ErrHeightNotFound is returned when a specified height isn't found in
	// a target index.
	ErrHeightNotFound = fmt.Errorf("target height not found in index")

	// ErrHashNotFound is returned when a specified block hash isn't found
	// in a target index.
	ErrHashNotFound = fmt.Errorf("target hash not found in index")
)

// HeaderType is an enum-like type which defines the various header types that
// are stored within the index.
type HeaderType uint8

const (
	// Block is the header type that represents regular Bitcoin block
	// headers.
	Block HeaderType = iota

	// RegularFilter is a header type that represents the basic filter
	// header type for the filter header chain.
	RegularFilter
)

const (
	// BlockHeaderSize is the size in bytes of the Block header type.
	BlockHeaderSize = 80

	// RegularFilterHeaderSize is the size in bytes of the RegularFilter
	// header type.
	RegularFilterHeaderSize = 32
)

// headerIndex is an index stored within the database that allows for random
// access into the on-disk header file. This, in conjunction with a flat file
// of headers consists of header database. The keys have been specifically
// crafted in order to ensure maximum write performance during IBD, and also to
// provide the necessary indexing properties required.
type headerIndex struct {
	db walletdb.DB

	indexType HeaderType
}

// newHeaderIndex creates a new headerIndex given an already open database, and
// a particular header type.
func newHeaderIndex(db walletdb.DB, indexType HeaderType) (*headerIndex, error) {
	// As an initially step, we'll attempt to create all the buckets
	// necessary for functioning of the index. If these buckets has already
	// been created, then we can exit early.
	err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
		_, err := tx.CreateTopLevelBucket(indexBucket)
		return err

	})
	if err != nil && err != walletdb.ErrBucketExists {
		return nil, err
	}

	return &headerIndex{
		db:        db,
		indexType: indexType,
	}, nil
}

// headerEntry is an internal type that's used to quickly map a (height, hash)
// pair into the proper key that'll be stored within the database.
type headerEntry struct {
	hash   chainhash.Hash
	height uint32
}

// headerBatch is a batch of header entries to be written to disk.
//
// NOTE: The entries within a batch SHOULD be properly sorted by hash in
// order to ensure the batch is written in a sequential write.
type headerBatch []headerEntry

// Len returns the number of routes in the collection.
//
// NOTE: This is part of the sort.Interface implementation.
func (h headerBatch) Len() int {
	return len(h)
}

// Less reports where the entry with index i should sort before the entry with
// index j. As we want to ensure the items are written in sequential order,
// items with the "first" hash.
//
// NOTE: This is part of the sort.Interface implementation.
func (h headerBatch) Less(i, j int) bool {
	return bytes.Compare(h[i].hash[:], h[j].hash[:]) < 0
}

// Swap swaps the elements with indexes i and j.
//
// NOTE: This is part of the sort.Interface implementation.
func (h headerBatch) Swap(i, j int) {
	h[i], h[j] = h[j], h[i]
}

// addHeaders writes a batch of header entries in a single atomic batch
func (h *headerIndex) addHeaders(batch headerBatch) error {
	// If we're writing a 0-length batch, make no changes and return.
	if len(batch) == 0 {
		return nil
	}

	// In order to ensure optimal write performance, we'll ensure that the
	// items are sorted by their hash before insertion into the database.
	sort.Sort(batch)

	return walletdb.Update(h.db, func(tx walletdb.ReadWriteTx) error {
		rootBucket := tx.ReadWriteBucket(indexBucket)

		var tipKey []byte

		// Based on the specified index type of this instance of the
		// index, we'll grab the key that tracks the tip of the chain
		// so we can update the index once all the header entries have
		// been updated.
		// TODO(roasbeef): only need block tip?
		switch h.indexType {
		case Block:
			tipKey = bitcoinTip
		case RegularFilter:
			tipKey = regFilterTip
		default:
			return fmt.Errorf("unknown index type: %v", h.indexType)
		}

		var (
			chainTipHash   chainhash.Hash
			chainTipHeight uint32
		)

		for _, header := range batch {
			var heightBytes [4]byte
			binary.BigEndian.PutUint32(heightBytes[:], header.height)
			err := rootBucket.Put(header.hash[:], heightBytes[:])
			if err != nil {
				return err
			}

			// TODO(roasbeef): need to remedy if side-chain
			// tracking added
			if header.height >= chainTipHeight {
				chainTipHash = header.hash
				chainTipHeight = header.height
			}
		}

		return rootBucket.Put(tipKey, chainTipHash[:])
	})
}

// heightFromHash returns the height of the entry that matches the specified
// height. With this height, the caller is then able to seek to the appropriate
// spot in the flat files in order to extract the true header.
func (h *headerIndex) heightFromHash(hash *chainhash.Hash) (uint32, error) {
	var height uint32
	err := walletdb.View(h.db, func(tx walletdb.ReadTx) error {
		var err error
		height, err = h.heightFromHashWithTx(tx, hash)
		return err
	})
	if err != nil {
		return 0, err
	}

	return height, nil
}

// heightFromHashWithTx returns the height of the entry that matches the
// specified hash by using the given DB transaction. With this height, the
// caller is then able to seek to the appropriate spot in the flat files in
// order to extract the true header.
func (h *headerIndex) heightFromHashWithTx(tx walletdb.ReadTx,
	hash *chainhash.Hash) (uint32, error) {

	rootBucket := tx.ReadBucket(indexBucket)

	heightBytes := rootBucket.Get(hash[:])
	if heightBytes == nil {
		// If the hash wasn't found, then we don't know of this hash
		// within the index.
		return 0, ErrHashNotFound
	}

	return binary.BigEndian.Uint32(heightBytes), nil
}

// chainTip returns the best hash and height that the index knows of.
func (h *headerIndex) chainTip() (*chainhash.Hash, uint32, error) {
	var (
		tipHeight uint32
		tipHash   *chainhash.Hash
	)

	err := walletdb.View(h.db, func(tx walletdb.ReadTx) error {
		rootBucket := tx.ReadBucket(indexBucket)

		var tipKey []byte

		// Based on the specified index type of this instance of the
		// index, we'll grab the particular key that tracks the chain
		// tip.
		switch h.indexType {
		case Block:
			tipKey = bitcoinTip
		case RegularFilter:
			tipKey = regFilterTip
		default:
			return fmt.Errorf("unknown chain tip index type: %v", h.indexType)
		}

		// Now that we have the particular tip key for this header
		// type, we'll fetch the hash for this tip, then using that
		// we'll fetch the height that corresponds to that hash.
		tipHashBytes := rootBucket.Get(tipKey)
		tipHeightBytes := rootBucket.Get(tipHashBytes)
		if len(tipHeightBytes) != 4 {
			return ErrHeightNotFound
		}

		// With the height fetched, we can now populate our return
		// parameters.
		h, err := chainhash.NewHash(tipHashBytes)
		if err != nil {
			return err
		}
		tipHash = h
		tipHeight = binary.BigEndian.Uint32(tipHeightBytes)

		return nil
	})
	if err != nil {
		return nil, 0, err
	}

	return tipHash, tipHeight, nil
}

// truncateIndex truncates the index for a particluar header type by a single
// header entry. The passed newTip pointer should point to the hash of the new
// chain tip. Optionally, if the entry is to be deleted as well, then the
// delete flag should be set to true.
func (h *headerIndex) truncateIndex(newTip *chainhash.Hash, delete bool) error {
	return walletdb.Update(h.db, func(tx walletdb.ReadWriteTx) error {
		rootBucket := tx.ReadWriteBucket(indexBucket)

		var tipKey []byte

		// Based on the specified index type of this instance of the
		// index, we'll grab the key that tracks the tip of the chain
		// we need to update.
		switch h.indexType {
		case Block:
			tipKey = bitcoinTip
		case RegularFilter:
			tipKey = regFilterTip
		default:
			return fmt.Errorf("unknown index type: %v", h.indexType)
		}

		// If the delete flag is set, then we'll also delete this entry
		// from the database as the primary index (block headers) is
		// being rolled back.
		if delete {
			prevTipHash := rootBucket.Get(tipKey)
			if err := rootBucket.Delete(prevTipHash); err != nil {
				return err
			}
		}

		// With the now stale entry deleted, we'll update the chain tip
		// to point to the new hash.
		return rootBucket.Put(tipKey, newTip[:])
	})
}
<commit_msg>headerfs: extract chainTipWithTx<commit_after>package headerfs

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"sort"

	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcwallet/walletdb"
)

var (
	// indexBucket is the main top-level bucket for the header index.
	// Nothing is stored in this bucket other than the sub-buckets which
	// contains the indexes for the various header types.
	indexBucket = []byte("header-index")

	// bitcoinTip is the key which tracks the "tip" of the block header
	// chain. The value of this key will be the current block hash of the
	// best known chain that we're synced to.
	bitcoinTip = []byte("bitcoin")

	// regFilterTip is the key which tracks the "tip" of the regular
	// compact filter header chain. The value of this key will be the
	// current block hash of the best known chain that the headers for
	// regular filter are synced to.
	regFilterTip = []byte("regular")

	// extFilterTip is the key which tracks the "tip" of the extended
	// compact filter header chain. The value of this key will be the
	// current block hash of the best known chain that the headers for
	// extended filter are synced to.
	extFilterTip = []byte("ext")
)

var (
	// ErrHeightNotFound is returned when a specified height isn't found in
	// a target index.
	ErrHeightNotFound = fmt.Errorf("target height not found in index")

	// ErrHashNotFound is returned when a specified block hash isn't found
	// in a target index.
	ErrHashNotFound = fmt.Errorf("target hash not found in index")
)

// HeaderType is an enum-like type which defines the various header types that
// are stored within the index.
type HeaderType uint8

const (
	// Block is the header type that represents regular Bitcoin block
	// headers.
	Block HeaderType = iota

	// RegularFilter is a header type that represents the basic filter
	// header type for the filter header chain.
	RegularFilter
)

const (
	// BlockHeaderSize is the size in bytes of the Block header type.
	BlockHeaderSize = 80

	// RegularFilterHeaderSize is the size in bytes of the RegularFilter
	// header type.
	RegularFilterHeaderSize = 32
)

// headerIndex is an index stored within the database that allows for random
// access into the on-disk header file. This, in conjunction with a flat file
// of headers consists of header database. The keys have been specifically
// crafted in order to ensure maximum write performance during IBD, and also to
// provide the necessary indexing properties required.
type headerIndex struct {
	db walletdb.DB

	indexType HeaderType
}

// newHeaderIndex creates a new headerIndex given an already open database, and
// a particular header type.
func newHeaderIndex(db walletdb.DB, indexType HeaderType) (*headerIndex, error) {
	// As an initially step, we'll attempt to create all the buckets
	// necessary for functioning of the index. If these buckets has already
	// been created, then we can exit early.
	err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
		_, err := tx.CreateTopLevelBucket(indexBucket)
		return err

	})
	if err != nil && err != walletdb.ErrBucketExists {
		return nil, err
	}

	return &headerIndex{
		db:        db,
		indexType: indexType,
	}, nil
}

// headerEntry is an internal type that's used to quickly map a (height, hash)
// pair into the proper key that'll be stored within the database.
type headerEntry struct {
	hash   chainhash.Hash
	height uint32
}

// headerBatch is a batch of header entries to be written to disk.
//
// NOTE: The entries within a batch SHOULD be properly sorted by hash in
// order to ensure the batch is written in a sequential write.
type headerBatch []headerEntry

// Len returns the number of routes in the collection.
//
// NOTE: This is part of the sort.Interface implementation.
func (h headerBatch) Len() int {
	return len(h)
}

// Less reports where the entry with index i should sort before the entry with
// index j. As we want to ensure the items are written in sequential order,
// items with the "first" hash.
//
// NOTE: This is part of the sort.Interface implementation.
func (h headerBatch) Less(i, j int) bool {
	return bytes.Compare(h[i].hash[:], h[j].hash[:]) < 0
}

// Swap swaps the elements with indexes i and j.
//
// NOTE: This is part of the sort.Interface implementation.
func (h headerBatch) Swap(i, j int) {
	h[i], h[j] = h[j], h[i]
}

// addHeaders writes a batch of header entries in a single atomic batch
func (h *headerIndex) addHeaders(batch headerBatch) error {
	// If we're writing a 0-length batch, make no changes and return.
	if len(batch) == 0 {
		return nil
	}

	// In order to ensure optimal write performance, we'll ensure that the
	// items are sorted by their hash before insertion into the database.
	sort.Sort(batch)

	return walletdb.Update(h.db, func(tx walletdb.ReadWriteTx) error {
		rootBucket := tx.ReadWriteBucket(indexBucket)

		var tipKey []byte

		// Based on the specified index type of this instance of the
		// index, we'll grab the key that tracks the tip of the chain
		// so we can update the index once all the header entries have
		// been updated.
		// TODO(roasbeef): only need block tip?
		switch h.indexType {
		case Block:
			tipKey = bitcoinTip
		case RegularFilter:
			tipKey = regFilterTip
		default:
			return fmt.Errorf("unknown index type: %v", h.indexType)
		}

		var (
			chainTipHash   chainhash.Hash
			chainTipHeight uint32
		)

		for _, header := range batch {
			var heightBytes [4]byte
			binary.BigEndian.PutUint32(heightBytes[:], header.height)
			err := rootBucket.Put(header.hash[:], heightBytes[:])
			if err != nil {
				return err
			}

			// TODO(roasbeef): need to remedy if side-chain
			// tracking added
			if header.height >= chainTipHeight {
				chainTipHash = header.hash
				chainTipHeight = header.height
			}
		}

		return rootBucket.Put(tipKey, chainTipHash[:])
	})
}

// heightFromHash returns the height of the entry that matches the specified
// height. With this height, the caller is then able to seek to the appropriate
// spot in the flat files in order to extract the true header.
func (h *headerIndex) heightFromHash(hash *chainhash.Hash) (uint32, error) {
	var height uint32
	err := walletdb.View(h.db, func(tx walletdb.ReadTx) error {
		var err error
		height, err = h.heightFromHashWithTx(tx, hash)
		return err
	})
	if err != nil {
		return 0, err
	}

	return height, nil
}

// heightFromHashWithTx returns the height of the entry that matches the
// specified hash by using the given DB transaction. With this height, the
// caller is then able to seek to the appropriate spot in the flat files in
// order to extract the true header.
func (h *headerIndex) heightFromHashWithTx(tx walletdb.ReadTx,
	hash *chainhash.Hash) (uint32, error) {

	rootBucket := tx.ReadBucket(indexBucket)

	heightBytes := rootBucket.Get(hash[:])
	if heightBytes == nil {
		// If the hash wasn't found, then we don't know of this hash
		// within the index.
		return 0, ErrHashNotFound
	}

	return binary.BigEndian.Uint32(heightBytes), nil
}

// chainTip returns the best hash and height that the index knows of.
func (h *headerIndex) chainTip() (*chainhash.Hash, uint32, error) {
	var (
		tipHeight uint32
		tipHash   *chainhash.Hash
	)

	err := walletdb.View(h.db, func(tx walletdb.ReadTx) error {
		var err error
		tipHash, tipHeight, err = h.chainTipWithTx(tx)
		return err
	})
	if err != nil {
		return nil, 0, err
	}

	return tipHash, tipHeight, nil
}

// chainTipWithTx returns the best hash and height that the index knows of by
// using the given DB transaction.
func (h *headerIndex) chainTipWithTx(tx walletdb.ReadTx) (*chainhash.Hash,
	uint32, error) {

	rootBucket := tx.ReadBucket(indexBucket)

	var tipKey []byte

	// Based on the specified index type of this instance of the index,
	// we'll grab the particular key that tracks the chain tip.
	switch h.indexType {
	case Block:
		tipKey = bitcoinTip
	case RegularFilter:
		tipKey = regFilterTip
	default:
		return nil, 0, fmt.Errorf("unknown chain tip index type: %v",
			h.indexType)
	}

	// Now that we have the particular tip key for this header type, we'll
	// fetch the hash for this tip, then using that we'll fetch the height
	// that corresponds to that hash.
	tipHashBytes := rootBucket.Get(tipKey)
	tipHeightBytes := rootBucket.Get(tipHashBytes)
	if len(tipHeightBytes) != 4 {
		return nil, 0, ErrHeightNotFound
	}

	// With the height fetched, we can now populate our return
	// parameters.
	tipHash, err := chainhash.NewHash(tipHashBytes)
	if err != nil {
		return nil, 0, err
	}

	return tipHash, binary.BigEndian.Uint32(tipHeightBytes), nil
}

// truncateIndex truncates the index for a particluar header type by a single
// header entry. The passed newTip pointer should point to the hash of the new
// chain tip. Optionally, if the entry is to be deleted as well, then the
// delete flag should be set to true.
func (h *headerIndex) truncateIndex(newTip *chainhash.Hash, delete bool) error {
	return walletdb.Update(h.db, func(tx walletdb.ReadWriteTx) error {
		rootBucket := tx.ReadWriteBucket(indexBucket)

		var tipKey []byte

		// Based on the specified index type of this instance of the
		// index, we'll grab the key that tracks the tip of the chain
		// we need to update.
		switch h.indexType {
		case Block:
			tipKey = bitcoinTip
		case RegularFilter:
			tipKey = regFilterTip
		default:
			return fmt.Errorf("unknown index type: %v", h.indexType)
		}

		// If the delete flag is set, then we'll also delete this entry
		// from the database as the primary index (block headers) is
		// being rolled back.
		if delete {
			prevTipHash := rootBucket.Get(tipKey)
			if err := rootBucket.Delete(prevTipHash); err != nil {
				return err
			}
		}

		// With the now stale entry deleted, we'll update the chain tip
		// to point to the new hash.
		return rootBucket.Put(tipKey, newTip[:])
	})
}
<|endoftext|>


<commit_before>package crypto

import (
	"crypto/cipher"
	"errors"
	"io"

	"v2ray.com/core/common"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/serial"
)

var (
	ErrAuthenticationFailed = errors.New("Authentication failed.")

	errInsufficientBuffer = errors.New("Insufficient buffer.")
	errInvalidNonce       = errors.New("Invalid nonce.")
)

type BytesGenerator interface {
	Next() []byte
}

type NoOpBytesGenerator struct {
	buffer [1]byte
}

func (v NoOpBytesGenerator) Next() []byte {
	return v.buffer[:0]
}

type StaticBytesGenerator struct {
	Content []byte
}

func (v StaticBytesGenerator) Next() []byte {
	return v.Content
}

type Authenticator interface {
	NonceSize() int
	Overhead() int
	Open(dst, cipherText []byte) ([]byte, error)
	Seal(dst, plainText []byte) ([]byte, error)
}

type AEADAuthenticator struct {
	cipher.AEAD
	NonceGenerator          BytesGenerator
	AdditionalDataGenerator BytesGenerator
}

func (v *AEADAuthenticator) Open(dst, cipherText []byte) ([]byte, error) {
	iv := v.NonceGenerator.Next()
	if len(iv) != v.AEAD.NonceSize() {
		return nil, errInvalidNonce
	}

	additionalData := v.AdditionalDataGenerator.Next()
	return v.AEAD.Open(dst, iv, cipherText, additionalData)
}

func (v *AEADAuthenticator) Seal(dst, plainText []byte) ([]byte, error) {
	iv := v.NonceGenerator.Next()
	if len(iv) != v.AEAD.NonceSize() {
		return nil, errInvalidNonce
	}

	additionalData := v.AdditionalDataGenerator.Next()
	return v.AEAD.Seal(dst, iv, plainText, additionalData), nil
}

type AuthenticationReader struct {
	auth   Authenticator
	buffer *buf.Buffer
	reader io.Reader

	chunk      []byte
	aggressive bool
}

func NewAuthenticationReader(auth Authenticator, reader io.Reader, aggressive bool) *AuthenticationReader {
	return &AuthenticationReader{
		auth:       auth,
		buffer:     buf.NewLocal(32 * 1024),
		reader:     reader,
		aggressive: aggressive,
	}
}

func (v *AuthenticationReader) NextChunk() error {
	if v.buffer.Len() < 2 {
		return errInsufficientBuffer
	}
	size := int(serial.BytesToUint16(v.buffer.BytesTo(2)))
	if size > v.buffer.Len()-2 {
		return errInsufficientBuffer
	}
	if size == v.auth.Overhead() {
		return io.EOF
	}
	if size < v.auth.Overhead() {
		return errors.New("AuthenticationReader: invalid packet size.")
	}
	cipherChunk := v.buffer.BytesRange(2, size+2)
	plainChunk, err := v.auth.Open(cipherChunk[:0], cipherChunk)
	if err != nil {
		return err
	}
	v.chunk = plainChunk
	v.buffer.SliceFrom(size + 2)
	return nil
}

func (v *AuthenticationReader) CopyChunk(b []byte) int {
	if len(v.chunk) == 0 {
		return 0
	}
	nBytes := copy(b, v.chunk)
	if nBytes == len(v.chunk) {
		v.chunk = nil
	} else {
		v.chunk = v.chunk[nBytes:]
	}
	return nBytes
}

func (v *AuthenticationReader) EnsureChunk() error {
	for {
		err := v.NextChunk()
		if err == nil {
			return nil
		}
		if err == errInsufficientBuffer {
			if v.buffer.IsEmpty() {
				v.buffer.Clear()
			} else {
				leftover := v.buffer.Bytes()
				common.Must(v.buffer.Reset(func(b []byte) (int, error) {
					return copy(b, leftover), nil
				}))
			}
			err = v.buffer.AppendSupplier(buf.ReadFrom(v.reader))
			if err == nil {
				continue
			}
		}
		return err
	}
}

func (v *AuthenticationReader) Read(b []byte) (int, error) {
	if len(v.chunk) > 0 {
		nBytes := v.CopyChunk(b)
		return nBytes, nil
	}

	err := v.EnsureChunk()
	if err != nil {
		return 0, err
	}

	totalBytes := v.CopyChunk(b)
	for v.aggressive && totalBytes < len(b) {
		if err := v.NextChunk(); err != nil {
			break
		}
		totalBytes += v.CopyChunk(b[totalBytes:])
	}
	return totalBytes, nil
}

type AuthenticationWriter struct {
	auth   Authenticator
	buffer []byte
	writer io.Writer
}

func NewAuthenticationWriter(auth Authenticator, writer io.Writer) *AuthenticationWriter {
	return &AuthenticationWriter{
		auth:   auth,
		buffer: make([]byte, 32*1024),
		writer: writer,
	}
}

func (v *AuthenticationWriter) Write(b []byte) (int, error) {
	cipherChunk, err := v.auth.Seal(v.buffer[2:2], b)
	if err != nil {
		return 0, err
	}

	serial.Uint16ToBytes(uint16(len(cipherChunk)), v.buffer[:0])
	_, err = v.writer.Write(v.buffer[:2+len(cipherChunk)])
	return len(b), err
}
<commit_msg>fix auth reader buffer overrun<commit_after>package crypto

import (
	"crypto/cipher"
	"errors"
	"io"

	"v2ray.com/core/common"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/serial"
)

var (
	ErrAuthenticationFailed = errors.New("Authentication failed.")

	errInsufficientBuffer = errors.New("Insufficient buffer.")
	errInvalidNonce       = errors.New("Invalid nonce.")
	errInvalidLength      = errors.New("Invalid buffer size.")
)

type BytesGenerator interface {
	Next() []byte
}

type NoOpBytesGenerator struct {
	buffer [1]byte
}

func (v NoOpBytesGenerator) Next() []byte {
	return v.buffer[:0]
}

type StaticBytesGenerator struct {
	Content []byte
}

func (v StaticBytesGenerator) Next() []byte {
	return v.Content
}

type Authenticator interface {
	NonceSize() int
	Overhead() int
	Open(dst, cipherText []byte) ([]byte, error)
	Seal(dst, plainText []byte) ([]byte, error)
}

type AEADAuthenticator struct {
	cipher.AEAD
	NonceGenerator          BytesGenerator
	AdditionalDataGenerator BytesGenerator
}

func (v *AEADAuthenticator) Open(dst, cipherText []byte) ([]byte, error) {
	iv := v.NonceGenerator.Next()
	if len(iv) != v.AEAD.NonceSize() {
		return nil, errInvalidNonce
	}

	additionalData := v.AdditionalDataGenerator.Next()
	return v.AEAD.Open(dst, iv, cipherText, additionalData)
}

func (v *AEADAuthenticator) Seal(dst, plainText []byte) ([]byte, error) {
	iv := v.NonceGenerator.Next()
	if len(iv) != v.AEAD.NonceSize() {
		return nil, errInvalidNonce
	}

	additionalData := v.AdditionalDataGenerator.Next()
	return v.AEAD.Seal(dst, iv, plainText, additionalData), nil
}

type AuthenticationReader struct {
	auth   Authenticator
	buffer *buf.Buffer
	reader io.Reader

	chunk      []byte
	aggressive bool
}

const (
	readerBufferSize = 32 * 1024
)

func NewAuthenticationReader(auth Authenticator, reader io.Reader, aggressive bool) *AuthenticationReader {
	return &AuthenticationReader{
		auth:       auth,
		buffer:     buf.NewLocal(readerBufferSize),
		reader:     reader,
		aggressive: aggressive,
	}
}

func (v *AuthenticationReader) NextChunk() error {
	if v.buffer.Len() < 2 {
		return errInsufficientBuffer
	}
	size := int(serial.BytesToUint16(v.buffer.BytesTo(2)))
	if size > v.buffer.Len()-2 {
		return errInsufficientBuffer
	}
	if size > readerBufferSize-2 {
		return errInvalidLength
	}
	if size == v.auth.Overhead() {
		return io.EOF
	}
	if size < v.auth.Overhead() {
		return errors.New("AuthenticationReader: invalid packet size.")
	}
	cipherChunk := v.buffer.BytesRange(2, size+2)
	plainChunk, err := v.auth.Open(cipherChunk[:0], cipherChunk)
	if err != nil {
		return err
	}
	v.chunk = plainChunk
	v.buffer.SliceFrom(size + 2)
	return nil
}

func (v *AuthenticationReader) CopyChunk(b []byte) int {
	if len(v.chunk) == 0 {
		return 0
	}
	nBytes := copy(b, v.chunk)
	if nBytes == len(v.chunk) {
		v.chunk = nil
	} else {
		v.chunk = v.chunk[nBytes:]
	}
	return nBytes
}

func (v *AuthenticationReader) EnsureChunk() error {
	for {
		err := v.NextChunk()
		if err == nil {
			return nil
		}
		if err == errInsufficientBuffer {
			if v.buffer.IsEmpty() {
				v.buffer.Clear()
			} else {
				leftover := v.buffer.Bytes()
				common.Must(v.buffer.Reset(func(b []byte) (int, error) {
					return copy(b, leftover), nil
				}))
			}
			err = v.buffer.AppendSupplier(buf.ReadFrom(v.reader))
			if err == nil {
				continue
			}
		}
		return err
	}
}

func (v *AuthenticationReader) Read(b []byte) (int, error) {
	if len(v.chunk) > 0 {
		nBytes := v.CopyChunk(b)
		return nBytes, nil
	}

	err := v.EnsureChunk()
	if err != nil {
		return 0, err
	}

	totalBytes := v.CopyChunk(b)
	for v.aggressive && totalBytes < len(b) {
		if err := v.NextChunk(); err != nil {
			break
		}
		totalBytes += v.CopyChunk(b[totalBytes:])
	}
	return totalBytes, nil
}

type AuthenticationWriter struct {
	auth   Authenticator
	buffer []byte
	writer io.Writer
}

func NewAuthenticationWriter(auth Authenticator, writer io.Writer) *AuthenticationWriter {
	return &AuthenticationWriter{
		auth:   auth,
		buffer: make([]byte, 32*1024),
		writer: writer,
	}
}

func (v *AuthenticationWriter) Write(b []byte) (int, error) {
	cipherChunk, err := v.auth.Seal(v.buffer[2:2], b)
	if err != nil {
		return 0, err
	}

	serial.Uint16ToBytes(uint16(len(cipherChunk)), v.buffer[:0])
	_, err = v.writer.Write(v.buffer[:2+len(cipherChunk)])
	return len(b), err
}
<|endoftext|>


<commit_before>package net

import (
	"net"

	"v2ray.com/core/app/log"
	"v2ray.com/core/common/predicate"
)

var (
	// LocalHostIP is a constant value for localhost IP in IPv4.
	LocalHostIP = IPAddress([]byte{127, 0, 0, 1})

	// AnyIP is a constant value for any IP in IPv4.
	AnyIP = IPAddress([]byte{0, 0, 0, 0})

	// LocalHostDomain is a constant value for localhost domain.
	LocalHostDomain = DomainAddress("localhost")

	// LocalHostIPv6 is a constant value for localhost IP in IPv6.
	LocalHostIPv6 = IPAddress([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})
)

// AddressFamily is the type of address.
type AddressFamily int

const (
	// AddressFamilyIPv4 represents address as IPv4
	AddressFamilyIPv4 = AddressFamily(0)

	// AddressFamilyIPv6 represents address as IPv6
	AddressFamilyIPv6 = AddressFamily(1)

	// AddressFamilyDomain represents address as Domain
	AddressFamilyDomain = AddressFamily(2)
)

func (v AddressFamily) Either(fs ...AddressFamily) bool {
	for _, f := range fs {
		if v == f {
			return true
		}
	}
	return false
}

func (v AddressFamily) IsIPv4() bool {
	return v == AddressFamilyIPv4
}

func (v AddressFamily) IsIPv6() bool {
	return v == AddressFamilyIPv6
}

func (v AddressFamily) IsDomain() bool {
	return v == AddressFamilyDomain
}

// Address represents a network address to be communicated with. It may be an IP address or domain
// address, not both. This interface doesn't resolve IP address for a given domain.
type Address interface {
	IP() net.IP     // IP of this Address
	Domain() string // Domain of this Address
	Family() AddressFamily

	String() string // String representation of this Address
}

// ParseAddress parses a string into an Address. The return value will be an IPAddress when
// the string is in the form of IPv4 or IPv6 address, or a DomainAddress otherwise.
func ParseAddress(addr string) Address {
	ip := net.ParseIP(addr)
	if ip != nil {
		return IPAddress(ip)
	}
	return DomainAddress(addr)
}

// IPAddress creates an Address with given IP.
func IPAddress(ip []byte) Address {
	switch len(ip) {
	case net.IPv4len:
		var addr ipv4Address = [4]byte{ip[0], ip[1], ip[2], ip[3]}
		return addr
	case net.IPv6len:
		if predicate.BytesAll(ip[0:10], 0) && predicate.BytesAll(ip[10:12], 0xff) {
			return IPAddress(ip[12:16])
		}
		var addr ipv6Address = [16]byte{
			ip[0], ip[1], ip[2], ip[3],
			ip[4], ip[5], ip[6], ip[7],
			ip[8], ip[9], ip[10], ip[11],
			ip[12], ip[13], ip[14], ip[15],
		}
		return addr
	default:
		log.Trace(newError("invalid IP format: ", ip).AtError())
		return nil
	}
}

// DomainAddress creates an Address with given domain.
func DomainAddress(domain string) Address {
	return domainAddress(domain)
}

type ipv4Address [4]byte

func (v ipv4Address) IP() net.IP {
	return net.IP(v[:])
}

func (ipv4Address) Domain() string {
	panic("Calling Domain() on an IPv4Address.")
}

func (ipv4Address) Family() AddressFamily {
	return AddressFamilyIPv4
}

func (v ipv4Address) String() string {
	return v.IP().String()
}

type ipv6Address [16]byte

func (v ipv6Address) IP() net.IP {
	return net.IP(v[:])
}

func (ipv6Address) Domain() string {
	panic("Calling Domain() on an IPv6Address.")
}

func (ipv6Address) Family() AddressFamily {
	return AddressFamilyIPv6
}

func (v ipv6Address) String() string {
	return "[" + v.IP().String() + "]"
}

type domainAddress string

func (domainAddress) IP() net.IP {
	panic("Calling IP() on a DomainAddress.")
}

func (v domainAddress) Domain() string {
	return string(v)
}

func (domainAddress) Family() AddressFamily {
	return AddressFamilyDomain
}

func (v domainAddress) String() string {
	return v.Domain()
}

func (v *IPOrDomain) AsAddress() Address {
	if v == nil {
		return nil
	}
	switch addr := v.Address.(type) {
	case *IPOrDomain_Ip:
		return IPAddress(addr.Ip)
	case *IPOrDomain_Domain:
		return DomainAddress(addr.Domain)
	}
	panic("Common|Net: Invalid address.")
}

func NewIPOrDomain(addr Address) *IPOrDomain {
	switch addr.Family() {
	case AddressFamilyDomain:
		return &IPOrDomain{
			Address: &IPOrDomain_Domain{
				Domain: addr.Domain(),
			},
		}
	case AddressFamilyIPv4, AddressFamilyIPv6:
		return &IPOrDomain{
			Address: &IPOrDomain_Ip{
				Ip: addr.IP(),
			},
		}
	default:
		panic("Unknown Address type.")
	}
}
<commit_msg>refactor<commit_after>package net

import (
	"net"

	"v2ray.com/core/app/log"
	"v2ray.com/core/common/predicate"
)

var (
	// LocalHostIP is a constant value for localhost IP in IPv4.
	LocalHostIP = IPAddress([]byte{127, 0, 0, 1})

	// AnyIP is a constant value for any IP in IPv4.
	AnyIP = IPAddress([]byte{0, 0, 0, 0})

	// LocalHostDomain is a constant value for localhost domain.
	LocalHostDomain = DomainAddress("localhost")

	// LocalHostIPv6 is a constant value for localhost IP in IPv6.
	LocalHostIPv6 = IPAddress([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})
)

// AddressFamily is the type of address.
type AddressFamily int

const (
	// AddressFamilyIPv4 represents address as IPv4
	AddressFamilyIPv4 = AddressFamily(0)

	// AddressFamilyIPv6 represents address as IPv6
	AddressFamilyIPv6 = AddressFamily(1)

	// AddressFamilyDomain represents address as Domain
	AddressFamilyDomain = AddressFamily(2)
)

// Either returns true if current AddressFamily matches any of the AddressFamilys provided.
func (af AddressFamily) Either(fs ...AddressFamily) bool {
	for _, f := range fs {
		if af == f {
			return true
		}
	}
	return false
}

// IsIPv4 returns true if current AddressFamily is IPv4.
func (af AddressFamily) IsIPv4() bool {
	return af == AddressFamilyIPv4
}

// IsIPv6 returns true if current AddressFamily is IPv6.
func (af AddressFamily) IsIPv6() bool {
	return af == AddressFamilyIPv6
}

// IsDomain returns true if current AddressFamily is Domain.
func (af AddressFamily) IsDomain() bool {
	return af == AddressFamilyDomain
}

// Address represents a network address to be communicated with. It may be an IP address or domain
// address, not both. This interface doesn't resolve IP address for a given domain.
type Address interface {
	IP() net.IP     // IP of this Address
	Domain() string // Domain of this Address
	Family() AddressFamily

	String() string // String representation of this Address
}

// ParseAddress parses a string into an Address. The return value will be an IPAddress when
// the string is in the form of IPv4 or IPv6 address, or a DomainAddress otherwise.
func ParseAddress(addr string) Address {
	ip := net.ParseIP(addr)
	if ip != nil {
		return IPAddress(ip)
	}
	return DomainAddress(addr)
}

// IPAddress creates an Address with given IP.
func IPAddress(ip []byte) Address {
	switch len(ip) {
	case net.IPv4len:
		var addr ipv4Address = [4]byte{ip[0], ip[1], ip[2], ip[3]}
		return addr
	case net.IPv6len:
		if predicate.BytesAll(ip[0:10], 0) && predicate.BytesAll(ip[10:12], 0xff) {
			return IPAddress(ip[12:16])
		}
		var addr ipv6Address = [16]byte{
			ip[0], ip[1], ip[2], ip[3],
			ip[4], ip[5], ip[6], ip[7],
			ip[8], ip[9], ip[10], ip[11],
			ip[12], ip[13], ip[14], ip[15],
		}
		return addr
	default:
		log.Trace(newError("invalid IP format: ", ip).AtError())
		return nil
	}
}

// DomainAddress creates an Address with given domain.
func DomainAddress(domain string) Address {
	return domainAddress(domain)
}

type ipv4Address [4]byte

func (a ipv4Address) IP() net.IP {
	return net.IP(a[:])
}

func (ipv4Address) Domain() string {
	panic("Calling Domain() on an IPv4Address.")
}

func (ipv4Address) Family() AddressFamily {
	return AddressFamilyIPv4
}

func (a ipv4Address) String() string {
	return a.IP().String()
}

type ipv6Address [16]byte

func (a ipv6Address) IP() net.IP {
	return net.IP(a[:])
}

func (ipv6Address) Domain() string {
	panic("Calling Domain() on an IPv6Address.")
}

func (ipv6Address) Family() AddressFamily {
	return AddressFamilyIPv6
}

func (a ipv6Address) String() string {
	return "[" + a.IP().String() + "]"
}

type domainAddress string

func (domainAddress) IP() net.IP {
	panic("Calling IP() on a DomainAddress.")
}

func (a domainAddress) Domain() string {
	return string(a)
}

func (domainAddress) Family() AddressFamily {
	return AddressFamilyDomain
}

func (a domainAddress) String() string {
	return a.Domain()
}

// AsAddress translates IPOrDomain to Address.
func (d *IPOrDomain) AsAddress() Address {
	if d == nil {
		return nil
	}
	switch addr := d.Address.(type) {
	case *IPOrDomain_Ip:
		return IPAddress(addr.Ip)
	case *IPOrDomain_Domain:
		return DomainAddress(addr.Domain)
	}
	panic("Common|Net: Invalid address.")
}

// NewIPOrDomain translates Address to IPOrDomain
func NewIPOrDomain(addr Address) *IPOrDomain {
	switch addr.Family() {
	case AddressFamilyDomain:
		return &IPOrDomain{
			Address: &IPOrDomain_Domain{
				Domain: addr.Domain(),
			},
		}
	case AddressFamilyIPv4, AddressFamilyIPv6:
		return &IPOrDomain{
			Address: &IPOrDomain_Ip{
				Ip: addr.IP(),
			},
		}
	default:
		panic("Unknown Address type.")
	}
}
<|endoftext|>