<commit_before>package projects

import (
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"net/http"
	"net/url"
	"reflect"
	"regexp"
	"strings"
	"time"

	"github.com/ion-channel/ionic/aliases"
	"github.com/ion-channel/ionic/rulesets"
	"github.com/ion-channel/ionic/tags"
)

const (
	validEmailRegex  = `(?i)^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`
	validGitURIRegex = `^(?:(?:http|ftp|gopher|mailto|mid|cid|news|nntp|prospero|telnet|rlogin|tn3270|wais|svn|git|rsync)+\+ssh\:\/\/|git\+https?:\/\/|git\@|(?:http|ftp|gopher|mailto|mid|cid|news|nntp|prospero|telnet|rlogin|tn3270|wais|svn|git|rsync|ssh|file)+s?:\/\/)[^\s]+$`
)

const (
	// CreateProjectEndpoint is a string representation of the current endpoint for creating project
	CreateProjectEndpoint = "v1/project/createProject"
	// CreateProjectsFromCSVEndpoint is a string representation of the current endpoint for creating projects from CSV
	CreateProjectsFromCSVEndpoint = "v1/project/createProjectsCSV"
	// GetProjectEndpoint is a string representation of the current endpoint for getting project
	GetProjectEndpoint = "v1/project/getProject"
	// GetProjectByURLEndpoint is a string representation of the current endpoint for getting project by URL
	GetProjectByURLEndpoint = "v1/project/getProjectByUrl"
	// GetProjectsEndpoint is a string representation of the current endpoint for getting projects
	GetProjectsEndpoint = "v1/project/getProjects"
	// UpdateProjectEndpoint is a string representation of the current endpoint for updating project
	UpdateProjectEndpoint = "v1/project/updateProject"
)

var (
	// ErrInvalidProject is returned when a given project does not pass the
	// standards for a project
	ErrInvalidProject = fmt.Errorf("project has invalid fields")
)

//Project is a representation of a project within the Ion Channel system
type Project struct {
	ID             *string         `json:"id,omitempty"`
	TeamID         *string         `json:"team_id,omitempty"`
	RulesetID      *string         `json:"ruleset_id,omitempty"`
	Name           *string         `json:"name,omitempty"`
	Type           *string         `json:"type,omitempty"`
	Source         *string         `json:"source,omitempty"`
	Branch         *string         `json:"branch,omitempty"`
	Description    *string         `json:"description,omitempty"`
	Active         bool            `json:"active"`
	ChatChannel    string          `json:"chat_channel"`
	CreatedAt      time.Time       `json:"created_at"`
	UpdatedAt      time.Time       `json:"updated_at"`
	DeployKey      string          `json:"deploy_key"`
	Monitor        bool            `json:"should_monitor"`
	POCName        string          `json:"poc_name"`
	POCEmail       string          `json:"poc_email"`
	Username       string          `json:"username"`
	Password       string          `json:"password"`
	KeyFingerprint string          `json:"key_fingerprint"`
	Private        bool            `json:"private"`
	Aliases        []aliases.Alias `json:"aliases"`
	Tags           []tags.Tag      `json:"tags"`
}

// String returns a JSON formatted string of the project object
func (p Project) String() string {
	b, err := json.Marshal(p)
	if err != nil {
		return fmt.Sprintf("failed to format project: %v", err.Error())
	}
	return string(b)
}

// Validate takes an http client, baseURL, and token; returns a slice of fields as a string and
// an error. The fields will be a list of fields that did not pass the
// validation. An error will only be returned if any of the fields fail their
// validation.
func (p *Project) Validate(client *http.Client, baseURL *url.URL, token string) (map[string]string, error) {
	invalidFields := make(map[string]string)
	var projErr error

	if p.TeamID == nil {
		invalidFields["team_id"] = "missing team id"
		projErr = ErrInvalidProject
	}

	if p.RulesetID == nil {
		invalidFields["ruleset_id"] = "missing ruleset id"
		projErr = ErrInvalidProject
	}

	if p.Name == nil {
		invalidFields["name"] = "missing name"
		projErr = ErrInvalidProject
	}

	if p.Type == nil {
		invalidFields["type"] = "missing type"
		projErr = ErrInvalidProject
	}

	if p.Source == nil {
		invalidFields["source"] = "missing source"
		projErr = ErrInvalidProject
	}

	if p.Branch == nil && p.Type != nil && strings.ToLower(*p.Type) == "git" {
		invalidFields["branch"] = "missing branch"
		projErr = ErrInvalidProject
	}

	if p.Description == nil {
		invalidFields["description"] = "missing description"
		projErr = ErrInvalidProject
	}

	if p.RulesetID != nil && p.TeamID != nil {
		exists, err := rulesets.RuleSetExists(client, baseURL, *p.RulesetID, *p.TeamID, token)
		if err != nil {
			return nil, fmt.Errorf("failed to determine if ruleset exists: %v", err.Error())
		}

		if !exists {
			invalidFields["ruleset_id"] = "ruleset id does not match to a valid ruleset"
			projErr = ErrInvalidProject
		}
	}

	p.POCEmail = strings.TrimSpace(p.POCEmail)

	r := regexp.MustCompile(validEmailRegex)
	if p.POCEmail != "" && !r.MatchString(p.POCEmail) {
		invalidFields["poc_email"] = "invalid email supplied"
		projErr = ErrInvalidProject
	}

	isFinger, err := regexp.MatchString("[a-f0-9]{2}\\:[a-f0-9]{2}\\:[a-f0-9]{2}\\:", p.DeployKey)
	if err != nil {
		return nil, fmt.Errorf("failed to detect deploy key fingerprint: %v", err.Error())
	}

	if isFinger {
		p.DeployKey = ""
	}

	block, rest := pem.Decode([]byte(p.DeployKey))
	if block != nil {
		pkey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
		if err != nil {
			invalidFields["deploy_key"] = "must be a valid ssh key"
			projErr = ErrInvalidProject
		} else {
			err = pkey.Validate()
			if err != nil {
				invalidFields["deploy_key"] = "must be a valid ssh key"
				projErr = ErrInvalidProject
			}
		}
	}

	if block == nil && rest != nil && string(rest) != "" {
		invalidFields["deploy_key"] = "must be a valid ssh key"
		projErr = ErrInvalidProject
	}

	if p.Type != nil {
		switch strings.ToLower(*p.Type) {
		case "artifact":
			u, err := url.Parse(*p.Source)
			if err != nil {
				invalidFields["source"] = fmt.Sprintf("source must be a valid url: %v", err.Error())
				projErr = ErrInvalidProject
			}

			if u != nil {
				res, err := client.Head(u.String())
				if err != nil {
					invalidFields["source"] = "source failed to return a response"
					projErr = ErrInvalidProject
				}

				if res != nil && res.StatusCode == http.StatusNotFound {
					invalidFields["source"] = "source returned a not found"
					projErr = ErrInvalidProject
				}
			}
		case "git", "svn":
			r := regexp.MustCompile(validGitURIRegex)
			if p.Source != nil && !r.MatchString(*p.Source) {
				invalidFields["source"] = "source must be a valid uri"
				projErr = ErrInvalidProject
			}
		default:
			invalidFields["type"] = fmt.Sprintf("invalid type value")
			projErr = ErrInvalidProject
		}
	}

	return invalidFields, projErr
}

// ProjectFilter represents the available fields to filter a get project request
// with.
type ProjectFilter struct {
	Type   *string `json:"type,omitempty"`
	Active *bool   `json:"active,omitempty"`
}

// Param converts the non nil fields of the Project Filter into a string usable
// for URL query params.
func (pf *ProjectFilter) Param() string {
	ps := make([]string, 0)

	fields := reflect.TypeOf(pf)
	values := reflect.ValueOf(pf)

	if fields.Kind() == reflect.Ptr {
		fields = fields.Elem()
		values = values.Elem()
	}

	for i := 0; i < fields.NumField(); i++ {
		value := values.Field(i)

		if value.IsNil() {
			continue
		}

		if value.Kind() == reflect.Ptr {
			value = value.Elem()
		}

		name := strings.ToLower(fields.Field(i).Name)

		switch value.Kind() {
		case reflect.String:
			ps = append(ps, fmt.Sprintf("%v:%v", name, value.String()))
		case reflect.Bool:
			ps = append(ps, fmt.Sprintf("%v:%v", name, value.Bool()))
		}
	}

	return strings.Join(ps, ",")
}
<commit_msg>make the tests pass<commit_after>package projects

import (
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"net/http"
	"net/url"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/ion-channel/ionic/aliases"
	"github.com/ion-channel/ionic/rulesets"
	"github.com/ion-channel/ionic/tags"
)

const (
	validEmailRegex  = `(?i)^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`
	validGitURIRegex = `^(?:(?:http|ftp|gopher|mailto|mid|cid|news|nntp|prospero|telnet|rlogin|tn3270|wais|svn|git|rsync)+\+ssh\:\/\/|git\+https?:\/\/|git\@|(?:http|ftp|gopher|mailto|mid|cid|news|nntp|prospero|telnet|rlogin|tn3270|wais|svn|git|rsync|ssh|file)+s?:\/\/)[^\s]+$`
)

const (
	// CreateProjectEndpoint is a string representation of the current endpoint for creating project
	CreateProjectEndpoint = "v1/project/createProject"
	// CreateProjectsFromCSVEndpoint is a string representation of the current endpoint for creating projects from CSV
	CreateProjectsFromCSVEndpoint = "v1/project/createProjectsCSV"
	// GetProjectEndpoint is a string representation of the current endpoint for getting project
	GetProjectEndpoint = "v1/project/getProject"
	// GetProjectByURLEndpoint is a string representation of the current endpoint for getting project by URL
	GetProjectByURLEndpoint = "v1/project/getProjectByUrl"
	// GetProjectsEndpoint is a string representation of the current endpoint for getting projects
	GetProjectsEndpoint = "v1/project/getProjects"
	// UpdateProjectEndpoint is a string representation of the current endpoint for updating project
	UpdateProjectEndpoint = "v1/project/updateProject"
)

var (
	// ErrInvalidProject is returned when a given project does not pass the
	// standards for a project
	ErrInvalidProject = fmt.Errorf("project has invalid fields")
)

//Project is a representation of a project within the Ion Channel system
type Project struct {
	ID             *string         `json:"id,omitempty"`
	TeamID         *string         `json:"team_id,omitempty"`
	RulesetID      *string         `json:"ruleset_id,omitempty"`
	Name           *string         `json:"name,omitempty"`
	Type           *string         `json:"type,omitempty"`
	Source         *string         `json:"source,omitempty"`
	Branch         *string         `json:"branch,omitempty"`
	Description    *string         `json:"description,omitempty"`
	Active         bool            `json:"active"`
	ChatChannel    string          `json:"chat_channel"`
	CreatedAt      time.Time       `json:"created_at"`
	UpdatedAt      time.Time       `json:"updated_at"`
	DeployKey      string          `json:"deploy_key"`
	Monitor        bool            `json:"should_monitor"`
	POCName        string          `json:"poc_name"`
	POCEmail       string          `json:"poc_email"`
	Username       string          `json:"username"`
	Password       string          `json:"password"`
	KeyFingerprint string          `json:"key_fingerprint"`
	Private        bool            `json:"private"`
	Aliases        []aliases.Alias `json:"aliases"`
	Tags           []tags.Tag      `json:"tags"`
}

// String returns a JSON formatted string of the project object
func (p Project) String() string {
	b, err := json.Marshal(p)
	if err != nil {
		return fmt.Sprintf("failed to format project: %v", err.Error())
	}
	return string(b)
}

// Validate takes an http client, baseURL, and token; returns a slice of fields as a string and
// an error. The fields will be a list of fields that did not pass the
// validation. An error will only be returned if any of the fields fail their
// validation.
func (p *Project) Validate(client *http.Client, baseURL *url.URL, token string) (map[string]string, error) {
	invalidFields := make(map[string]string)
	var projErr error

	if p.TeamID == nil {
		invalidFields["team_id"] = "missing team id"
		projErr = ErrInvalidProject
	}

	if p.RulesetID == nil {
		invalidFields["ruleset_id"] = "missing ruleset id"
		projErr = ErrInvalidProject
	}

	if p.Name == nil {
		invalidFields["name"] = "missing name"
		projErr = ErrInvalidProject
	}

	if p.Type == nil {
		invalidFields["type"] = "missing type"
		projErr = ErrInvalidProject
	}

	if p.Source == nil {
		invalidFields["source"] = "missing source"
		projErr = ErrInvalidProject
	}

	if p.Branch == nil && p.Type != nil && strings.ToLower(*p.Type) == "git" {
		invalidFields["branch"] = "missing branch"
		projErr = ErrInvalidProject
	}

	if p.Description == nil {
		invalidFields["description"] = "missing description"
		projErr = ErrInvalidProject
	}

	if p.RulesetID != nil && p.TeamID != nil {
		exists, err := rulesets.RuleSetExists(client, baseURL, *p.RulesetID, *p.TeamID, token)
		if err != nil {
			return nil, fmt.Errorf("failed to determine if ruleset exists: %v", err.Error())
		}

		if !exists {
			invalidFields["ruleset_id"] = "ruleset id does not match to a valid ruleset"
			projErr = ErrInvalidProject
		}
	}

	p.POCEmail = strings.TrimSpace(p.POCEmail)

	r := regexp.MustCompile(validEmailRegex)
	if p.POCEmail != "" && !r.MatchString(p.POCEmail) {
		invalidFields["poc_email"] = "invalid email supplied"
		projErr = ErrInvalidProject
	}

	isFinger, err := regexp.MatchString("[a-f0-9]{2}\\:[a-f0-9]{2}\\:[a-f0-9]{2}\\:", p.DeployKey)
	if err != nil {
		return nil, fmt.Errorf("failed to detect deploy key fingerprint: %v", err.Error())
	}

	if isFinger {
		p.DeployKey = ""
	}

	block, rest := pem.Decode([]byte(p.DeployKey))
	if block != nil {
		pkey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
		if err != nil {
			invalidFields["deploy_key"] = "must be a valid ssh key"
			projErr = ErrInvalidProject
		} else {
			err = pkey.Validate()
			if err != nil {
				invalidFields["deploy_key"] = "must be a valid ssh key"
				projErr = ErrInvalidProject
			}
		}
	}

	if block == nil && rest != nil && string(rest) != "" {
		invalidFields["deploy_key"] = "must be a valid ssh key"
		projErr = ErrInvalidProject
	}

	if p.Type != nil {
		switch strings.ToLower(*p.Type) {
		case "artifact":
			u, err := url.Parse(*p.Source)
			if err != nil {
				invalidFields["source"] = fmt.Sprintf("source must be a valid url: %v", err.Error())
				projErr = ErrInvalidProject
			}

			if u != nil {
				res, err := client.Head(u.String())
				if err != nil {
					invalidFields["source"] = "source failed to return a response"
					projErr = ErrInvalidProject
				}

				if res != nil && res.StatusCode == http.StatusNotFound {
					invalidFields["source"] = "source returned a not found"
					projErr = ErrInvalidProject
				}
			}
		case "git", "svn":
			r := regexp.MustCompile(validGitURIRegex)
			if p.Source != nil && !r.MatchString(*p.Source) {
				invalidFields["source"] = "source must be a valid uri"
				projErr = ErrInvalidProject
			}
		default:
			invalidFields["type"] = fmt.Sprintf("invalid type value")
			projErr = ErrInvalidProject
		}
	}

	return invalidFields, projErr
}

// ProjectFilter represents the available fields to filter a get project request
// with.
type ProjectFilter struct {
	ID      *string `sql:"id"`
	TeamID  *string `sql:"team_id"`
	Source  *string `sql:"source"`
	Type    *string `sql:"type"`
	Active  *bool   `sql:"active"`
	Monitor *bool   `sql:"should_monitor"`
}

// ParseParam takes a param string, breaks it apart, and repopulates it into a
// struct for further use. Any invalid or incomplete interpretations of a field
// will be ignored and only valid entries put into the struct.
func ParseParam(param string) *ProjectFilter {
	if param == "" || !strings.ContainsAny(param, ":") {
		return nil
	}

	fvs := strings.Split(param, ",")

	pf := ProjectFilter{}

	for i := range fvs {
		parts := strings.Split(fvs[i], ":")

		if len(parts) == 2 {
			name := strings.Title(parts[0])
			value := parts[1]

			field, _ := reflect.TypeOf(&pf).Elem().FieldByName(name)
			kind := field.Type.Kind()

			if kind == reflect.Ptr {
				kind = field.Type.Elem().Kind()
			}

			switch kind {
			case reflect.String:
				reflect.ValueOf(&pf).Elem().FieldByName(name).Set(reflect.ValueOf(&value))
			case reflect.Bool:
				b, err := strconv.ParseBool(value)
				if err == nil {
					reflect.ValueOf(&pf).Elem().FieldByName(name).Set(reflect.ValueOf(&b))
				}
			}
		}
	}

	return &pf
}

// Param converts the non nil fields of the Project Filter into a string usable
// for URL query params.
func (pf *ProjectFilter) Param() string {
	ps := make([]string, 0)

	fields := reflect.TypeOf(pf)
	values := reflect.ValueOf(pf)

	if fields.Kind() == reflect.Ptr {
		fields = fields.Elem()
		values = values.Elem()
	}

	for i := 0; i < fields.NumField(); i++ {
		value := values.Field(i)

		if value.IsNil() {
			continue
		}

		if value.Kind() == reflect.Ptr {
			value = value.Elem()
		}

		name := strings.ToLower(fields.Field(i).Name)

		switch value.Kind() {
		case reflect.String:
			ps = append(ps, fmt.Sprintf("%v:%v", name, value.String()))
		case reflect.Bool:
			ps = append(ps, fmt.Sprintf("%v:%v", name, value.Bool()))
		}
	}

	return strings.Join(ps, ",")
}

// SQL takes an identifier and returns the filter as a constructed where clause
// and set of values for use in a query as SQL params. If the identifier is left
// blank it will not be included in the resulting where clause.
func (pf *ProjectFilter) SQL(identifier string) (string, []interface{}) {
	wheres := make([]string, 0)
	vals := make([]interface{}, 0)

	fields := reflect.TypeOf(pf)
	values := reflect.ValueOf(pf)

	if fields.Kind() == reflect.Ptr {
		fields = fields.Elem()
		values = values.Elem()
	}

	idx := 1
	for i := 0; i < fields.NumField(); i++ {
		value := values.Field(i)

		if value.IsNil() {
			continue
		}

		if value.Kind() == reflect.Ptr {
			value = value.Elem()
		}

		tag, ok := fields.Field(i).Tag.Lookup("sql")
		if !ok {
			tag = fields.Field(i).Name
		}

		ident := ""
		if identifier != "" {
			ident = fmt.Sprintf("%v.", identifier)
		}

		name := strings.ToLower(tag)
		wheres = append(wheres, fmt.Sprintf("%v%v=$%v", ident, name, idx))
		vals = append(vals, value.Interface())
		idx++
	}

	return strings.Join(wheres, " AND "), vals
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. 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 lease

import (
	"fmt"
	"io"
	"log"

	"golang.org/x/net/context"
)

// A function used by read proxies to refresh their contents. See notes on
// NewReadProxy.
type RefreshContentsFunc func(context.Context) (io.ReadCloser, error)

// Create a read proxy.
//
// The supplied function will be used to obtain the proxy's contents, the first
// time they're needed and whenever the supplied file leaser decides to expire
// the temporary copy thus obtained. It must return the same contents every
// time, and the contents must be of the given size.
func NewReadProxy(
	fl FileLeaser,
	size int64,
	refresh RefreshContentsFunc) (rp *ReadProxy) {
	rp = &ReadProxy{
		leaser:  fl,
		size:    size,
		refresh: refresh,
	}

	return
}

// A wrapper around a read lease, exposing a similar interface with the
// following differences:
//
//  *  Contents are fetched and re-fetched automatically when needed. Therefore
//     the user need not worry about lease expiration.
//
//  *  Methods that may involve fetching the contents (reading, seeking) accept
//     context arguments, so as to be cancellable.
//
// External synchronization is required.
type ReadProxy struct {
	/////////////////////////
	// Constant data
	/////////////////////////

	size int64

	/////////////////////////
	// Dependencies
	/////////////////////////

	leaser  FileLeaser
	refresh RefreshContentsFunc

	/////////////////////////
	// Mutable state
	/////////////////////////

	// The current wrapped lease, or nil if one has never been issued.
	lease ReadLease
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// Attempt to clean up after the supplied read/write lease.
func destroyReadWriteLease(rwl ReadWriteLease) {
	var err error
	defer func() {
		if err != nil {
			log.Printf("Error destroying read/write lease: %v", err)
		}
	}()

	// Downgrade to a read lease.
	rl, err := rwl.Downgrade()
	if err != nil {
		err = fmt.Errorf("Downgrade: %v", err)
		return
	}

	// Revoke the read lease.
	rl.Revoke()
}

func isRevokedErr(err error) bool {
	_, ok := err.(*RevokedError)
	return ok
}

// Set up a read/write lease and fill in our contents.
//
// REQUIRES: The caller has observed that rl.lease has expired.
func (rl *autoRefreshingReadLease) getContents() (
	rwl ReadWriteLease, err error) {
	// Obtain some space to write the contents.
	rwl, err = rl.leaser.NewFile()
	if err != nil {
		err = fmt.Errorf("NewFile: %v", err)
		return
	}

	// Attempt to clean up if we exit early.
	defer func() {
		if err != nil {
			destroyReadWriteLease(rwl)
		}
	}()

	// Obtain the reader for our contents.
	rc, err := rl.f()
	if err != nil {
		err = fmt.Errorf("User function: %v", err)
		return
	}

	defer func() {
		closeErr := rc.Close()
		if closeErr != nil && err == nil {
			err = fmt.Errorf("Close: %v", closeErr)
		}
	}()

	// Copy into the read/write lease.
	copied, err := io.Copy(rwl, rc)
	if err != nil {
		err = fmt.Errorf("Copy: %v", err)
		return
	}

	// Did the user lie about the size?
	if copied != rl.Size() {
		err = fmt.Errorf("Copied %v bytes; expected %v", copied, rl.Size())
		return
	}

	return
}

// Downgrade and save the supplied read/write lease obtained with getContents
// for later use.
func (rl *autoRefreshingReadLease) saveContents(rwl ReadWriteLease) {
	downgraded, err := rwl.Downgrade()
	if err != nil {
		log.Printf("Failed to downgrade write lease (%q); abandoning.", err.Error())
		return
	}

	rl.wrapped = downgraded
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// Semantics matching io.Reader, except with context support.
func (rl *autoRefreshingReadLease) Read(
	ctx context.Context,
	p []byte) (n int, err error) {
	// Special case: have we been permanently revoked?
	if rl.revoked {
		err = &RevokedError{}
		return
	}

	// Common case: is the existing lease still valid?
	if rl.wrapped != nil {
		n, err = rl.wrapped.Read(p)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rl.getContents()
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rl.saveContents(rwl)

	// Serve from the read/write lease.
	n, err = rwl.Read(p)

	return
}

// Semantics matching io.Seeker, except with context support.
func (rl *autoRefreshingReadLease) Seek(
	ctx context.Context,
	offset int64,
	whence int) (off int64, err error) {
	// Special case: have we been permanently revoked?
	if rl.revoked {
		err = &RevokedError{}
		return
	}

	// Common case: is the existing lease still valid?
	if rl.wrapped != nil {
		off, err = rl.wrapped.Seek(offset, whence)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rl.getContents()
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rl.saveContents(rwl)

	// Serve from the read/write lease.
	off, err = rwl.Seek(offset, whence)

	return
}

// Semantics matching io.ReaderAt, except with context support.
func (rl *autoRefreshingReadLease) ReadAt(
	ctx context.Context,
	p []byte,
	off int64) (n int, err error) {
	// Special case: have we been permanently revoked?
	if rl.revoked {
		err = &RevokedError{}
		return
	}

	// Common case: is the existing lease still valid?
	if rl.wrapped != nil {
		n, err = rl.wrapped.ReadAt(p, off)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rl.getContents()
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rl.saveContents(rwl)

	// Serve from the read/write lease.
	n, err = rwl.ReadAt(p, off)

	return
}

// Return the size of the proxied content. Guarantees to not block.
func (rl *autoRefreshingReadLease) Size() (size int64) {
	size = rl.size
	return
}

// For testing use only; do not touch.
func (rl *autoRefreshingReadLease) Destroyed() (destroyed bool) {
	panic("TODO")
}

// Return a read/write lease for the proxied contents. The read proxy must not
// be used after calling this method.
func (rl *autoRefreshingReadLease) Upgrade() (rwl ReadWriteLease, err error) {
	// Special case: have we been permanently revoked?
	if rl.revoked {
		err = &RevokedError{}
		return
	}

	// If we succeed, we are now revoked.
	defer func() {
		if err == nil {
			rl.revoked = true
		}
	}()

	// Common case: is the existing lease still valid?
	if rl.wrapped != nil {
		rwl, err = rl.wrapped.Upgrade()
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Build the read/write lease anew.
	rwl, err = rl.getContents()
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	return
}

// Destroy any resources in use by the read proxy. It must not be used further.
func (rl *autoRefreshingReadLease) Destroy() {
	rl.revoked = true
	if rl.wrapped != nil {
		rl.wrapped.Revoke()
	}
}
<commit_msg>ReadProxy.getContents<commit_after>// Copyright 2015 Google Inc. 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 lease

import (
	"fmt"
	"io"
	"log"

	"golang.org/x/net/context"
)

// A function used by read proxies to refresh their contents. See notes on
// NewReadProxy.
type RefreshContentsFunc func(context.Context) (io.ReadCloser, error)

// Create a read proxy.
//
// The supplied function will be used to obtain the proxy's contents, the first
// time they're needed and whenever the supplied file leaser decides to expire
// the temporary copy thus obtained. It must return the same contents every
// time, and the contents must be of the given size.
func NewReadProxy(
	fl FileLeaser,
	size int64,
	refresh RefreshContentsFunc) (rp *ReadProxy) {
	rp = &ReadProxy{
		leaser:  fl,
		size:    size,
		refresh: refresh,
	}

	return
}

// A wrapper around a read lease, exposing a similar interface with the
// following differences:
//
//  *  Contents are fetched and re-fetched automatically when needed. Therefore
//     the user need not worry about lease expiration.
//
//  *  Methods that may involve fetching the contents (reading, seeking) accept
//     context arguments, so as to be cancellable.
//
// External synchronization is required.
type ReadProxy struct {
	/////////////////////////
	// Constant data
	/////////////////////////

	size int64

	/////////////////////////
	// Dependencies
	/////////////////////////

	leaser  FileLeaser
	refresh RefreshContentsFunc

	/////////////////////////
	// Mutable state
	/////////////////////////

	// The current wrapped lease, or nil if one has never been issued.
	lease ReadLease
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// Attempt to clean up after the supplied read/write lease.
func destroyReadWriteLease(rwl ReadWriteLease) {
	var err error
	defer func() {
		if err != nil {
			log.Printf("Error destroying read/write lease: %v", err)
		}
	}()

	// Downgrade to a read lease.
	rl, err := rwl.Downgrade()
	if err != nil {
		err = fmt.Errorf("Downgrade: %v", err)
		return
	}

	// Revoke the read lease.
	rl.Revoke()
}

func isRevokedErr(err error) bool {
	_, ok := err.(*RevokedError)
	return ok
}

// Set up a read/write lease and fill in our contents.
//
// REQUIRES: The caller has observed that rp.lease has expired.
func (rp *ReadProxy) getContents(
	ctx context.Context) (rwl ReadWriteLease, err error) {
	// Obtain some space to write the contents.
	rwl, err = rp.leaser.NewFile()
	if err != nil {
		err = fmt.Errorf("NewFile: %v", err)
		return
	}

	// Attempt to clean up if we exit early.
	defer func() {
		if err != nil {
			destroyReadWriteLease(rwl)
		}
	}()

	// Obtain the reader for our contents.
	rc, err := rp.refresh()
	if err != nil {
		err = fmt.Errorf("User function: %v", err)
		return
	}

	defer func() {
		closeErr := rc.Close()
		if closeErr != nil && err == nil {
			err = fmt.Errorf("Close: %v", closeErr)
		}
	}()

	// Copy into the read/write lease.
	copied, err := io.Copy(rwl, rc)
	if err != nil {
		err = fmt.Errorf("Copy: %v", err)
		return
	}

	// Did the user lie about the size?
	if copied != rp.Size() {
		err = fmt.Errorf("Copied %v bytes; expected %v", copied, rl.Size())
		return
	}

	return
}

// Downgrade and save the supplied read/write lease obtained with getContents
// for later use.
func (rl *autoRefreshingReadLease) saveContents(rwl ReadWriteLease) {
	downgraded, err := rwl.Downgrade()
	if err != nil {
		log.Printf("Failed to downgrade write lease (%q); abandoning.", err.Error())
		return
	}

	rl.wrapped = downgraded
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// Semantics matching io.Reader, except with context support.
func (rl *autoRefreshingReadLease) Read(
	ctx context.Context,
	p []byte) (n int, err error) {
	// Special case: have we been permanently revoked?
	if rl.revoked {
		err = &RevokedError{}
		return
	}

	// Common case: is the existing lease still valid?
	if rl.wrapped != nil {
		n, err = rl.wrapped.Read(p)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rl.getContents()
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rl.saveContents(rwl)

	// Serve from the read/write lease.
	n, err = rwl.Read(p)

	return
}

// Semantics matching io.Seeker, except with context support.
func (rl *autoRefreshingReadLease) Seek(
	ctx context.Context,
	offset int64,
	whence int) (off int64, err error) {
	// Special case: have we been permanently revoked?
	if rl.revoked {
		err = &RevokedError{}
		return
	}

	// Common case: is the existing lease still valid?
	if rl.wrapped != nil {
		off, err = rl.wrapped.Seek(offset, whence)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rl.getContents()
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rl.saveContents(rwl)

	// Serve from the read/write lease.
	off, err = rwl.Seek(offset, whence)

	return
}

// Semantics matching io.ReaderAt, except with context support.
func (rl *autoRefreshingReadLease) ReadAt(
	ctx context.Context,
	p []byte,
	off int64) (n int, err error) {
	// Special case: have we been permanently revoked?
	if rl.revoked {
		err = &RevokedError{}
		return
	}

	// Common case: is the existing lease still valid?
	if rl.wrapped != nil {
		n, err = rl.wrapped.ReadAt(p, off)
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Get hold of a read/write lease containing our contents.
	rwl, err := rl.getContents()
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	defer rl.saveContents(rwl)

	// Serve from the read/write lease.
	n, err = rwl.ReadAt(p, off)

	return
}

// Return the size of the proxied content. Guarantees to not block.
func (rl *autoRefreshingReadLease) Size() (size int64) {
	size = rl.size
	return
}

// For testing use only; do not touch.
func (rl *autoRefreshingReadLease) Destroyed() (destroyed bool) {
	panic("TODO")
}

// Return a read/write lease for the proxied contents. The read proxy must not
// be used after calling this method.
func (rl *autoRefreshingReadLease) Upgrade() (rwl ReadWriteLease, err error) {
	// Special case: have we been permanently revoked?
	if rl.revoked {
		err = &RevokedError{}
		return
	}

	// If we succeed, we are now revoked.
	defer func() {
		if err == nil {
			rl.revoked = true
		}
	}()

	// Common case: is the existing lease still valid?
	if rl.wrapped != nil {
		rwl, err = rl.wrapped.Upgrade()
		if !isRevokedErr(err) {
			return
		}

		// Clear the revoked error.
		err = nil
	}

	// Build the read/write lease anew.
	rwl, err = rl.getContents()
	if err != nil {
		err = fmt.Errorf("getContents: %v", err)
		return
	}

	return
}

// Destroy any resources in use by the read proxy. It must not be used further.
func (rl *autoRefreshingReadLease) Destroy() {
	rl.revoked = true
	if rl.wrapped != nil {
		rl.wrapped.Revoke()
	}
}
<|endoftext|>


<commit_before>package provider

import (
	"github.com/NetSys/di/db"
	"github.com/NetSys/di/dsl"
)

// Machine represents an instance of a machine booted by a Provider.
type Machine struct {
	ID        string
	PublicIP  string
	PrivateIP string
	Size      string
	SSHKeys   []string
	Provider  db.Provider
	Region    string
}

// Provider defines an interface for interacting with cloud providers.
type Provider interface {
	Start(conn db.Conn, id int, namespace string) error

	Get() ([]Machine, error)

	Boot(bootSet []Machine) error

	Stop([]Machine) error

	Disconnect()

	PickBestSize(ram dsl.Range, cpu dsl.Range, maxPrice float64) string
}

// New returns an empty instance of the Provider represented by `dbp`
func New(dbp db.Provider) Provider {
	switch dbp {
	case db.AmazonSpot:
		return &awsSpotCluster{}
	case db.Google:
		return &gceCluster{}
	case db.Azure:
		return &azureCluster{}
	case db.Vagrant:
		return &vagrantCluster{}
	default:
		panic("Unimplemented")
	}
}

// GroupBy transforms the `machines` into a map of `db.Provider` to the machines
// with that provider.
func GroupBy(machines []Machine) map[db.Provider][]Machine {
	machineMap := make(map[db.Provider][]Machine)
	for _, m := range machines {
		if _, ok := machineMap[m.Provider]; !ok {
			machineMap[m.Provider] = []Machine{}
		}
		machineMap[m.Provider] = append(machineMap[m.Provider], m)
	}

	return machineMap
}
<commit_msg>provider: Trivial tweak to provider interface<commit_after>package provider

import (
	"github.com/NetSys/di/db"
	"github.com/NetSys/di/dsl"
)

// Machine represents an instance of a machine booted by a Provider.
type Machine struct {
	ID        string
	PublicIP  string
	PrivateIP string
	Size      string
	SSHKeys   []string
	Provider  db.Provider
	Region    string
}

// Provider defines an interface for interacting with cloud providers.
type Provider interface {
	Start(conn db.Conn, id int, namespace string) error

	Get() ([]Machine, error)

	Boot([]Machine) error

	Stop([]Machine) error

	Disconnect()

	PickBestSize(ram dsl.Range, cpu dsl.Range, maxPrice float64) string
}

// New returns an empty instance of the Provider represented by `dbp`
func New(dbp db.Provider) Provider {
	switch dbp {
	case db.AmazonSpot:
		return &awsSpotCluster{}
	case db.Google:
		return &gceCluster{}
	case db.Azure:
		return &azureCluster{}
	case db.Vagrant:
		return &vagrantCluster{}
	default:
		panic("Unimplemented")
	}
}

// GroupBy transforms the `machines` into a map of `db.Provider` to the machines
// with that provider.
func GroupBy(machines []Machine) map[db.Provider][]Machine {
	machineMap := make(map[db.Provider][]Machine)
	for _, m := range machines {
		if _, ok := machineMap[m.Provider]; !ok {
			machineMap[m.Provider] = []Machine{}
		}
		machineMap[m.Provider] = append(machineMap[m.Provider], m)
	}

	return machineMap
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"log"

	"github.com/Syfaro/telegram-bot-api"
)

// One User
type User struct {
	UserName      string
	FirstName     string
	LastName      string
	AuthCode      string
	IsAuthorized  bool
	IsRoot        bool
	PrivateChatID int
}

// Users in chat
type Users struct {
	list         map[int]*User
	allowedUsers map[string]bool
	rootUsers    map[string]bool
}

// length of random code in bytes
const CODE_BYTES_LENGTH = 15

// new Users object
func NewUsers(appConfig Config) Users {
	users := Users{
		list:         map[int]*User{},
		allowedUsers: map[string]bool{},
		rootUsers:    map[string]bool{},
	}

	for _, name := range appConfig.allowUsers {
		users.allowedUsers[name] = true
	}
	for _, name := range appConfig.rootUsers {
		users.allowedUsers[name] = true
		users.rootUsers[name] = true
	}
	return users
}

// add new user if not exists
func (users Users) AddNew(tgbot_user tgbotapi.User, tgbot_chat tgbotapi.UserOrGroupChat) {
	privateChatID := 0
	if tgbot_chat.Title == "" {
		privateChatID = tgbot_chat.ID
	}

	if _, ok := users.list[tgbot_user.ID]; ok && privateChatID > 0 {
		users.list[tgbot_user.ID].PrivateChatID = privateChatID
	} else if !ok {
		users.list[tgbot_user.ID] = &User{
			UserName:      tgbot_user.UserName,
			FirstName:     tgbot_user.FirstName,
			LastName:      tgbot_user.LastName,
			IsAuthorized:  users.allowedUsers[tgbot_user.UserName],
			IsRoot:        users.rootUsers[tgbot_user.UserName],
			PrivateChatID: privateChatID,
		}
	}
}

// generate code
func (users Users) DoLogin(user_id int) {
	users.list[user_id].IsAuthorized = false
	users.list[user_id].AuthCode = getRandomCode()
}

// check code for user
func (users Users) IsValidCode(user_id int, code string) bool {
	result := code != "" && code == users.list[user_id].AuthCode
	if result {
		users.list[user_id].IsAuthorized = true
	}

	return result
}

// check code for user
func (users Users) IsAuthorized(tgbot_user tgbotapi.User) bool {
	isAuthorized := false
	if tgbot_user.UserName != "" && users.allowedUsers[tgbot_user.UserName] {
		isAuthorized = true
	} else if _, ok := users.list[tgbot_user.ID]; ok && users.list[tgbot_user.ID].IsAuthorized {
		isAuthorized = true
	}

	return isAuthorized
}

// send message to all root users
func (users Users) broadcastForRoots(bot *tgbotapi.BotAPI, message string) {
	for _, user := range users.list {
		if user.IsRoot && user.PrivateChatID > 0 {
			bot.SendMessage(tgbotapi.NewMessage(user.PrivateChatID, message))
		}
	}
}

// Format user name
func (users Users) String(user_id int) string {
	result := fmt.Sprintf("%s %s", users.list[user_id].FirstName, users.list[user_id].LastName)
	if users.list[user_id].UserName != "" {
		result += fmt.Sprintf(" (@%s)", users.list[user_id].UserName)
	}
	return result
}

// generate random code for authorize user
func getRandomCode() string {
	buffer := make([]byte, CODE_BYTES_LENGTH)
	_, err := rand.Read(buffer)
	if err != nil {
		log.Fatal("Get code error:", err)
	}

	return base64.URLEncoding.EncodeToString(buffer)
}
<commit_msg>Refactoring<commit_after>package main

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"log"

	"github.com/Syfaro/telegram-bot-api"
)

// One User
type User struct {
	UserName      string
	FirstName     string
	LastName      string
	AuthCode      string
	IsAuthorized  bool
	IsRoot        bool
	PrivateChatID int
}

// Users in chat
type Users struct {
	list         map[int]*User
	allowedUsers map[string]bool
	rootUsers    map[string]bool
}

// length of random code in bytes
const CODE_BYTES_LENGTH = 15

// new Users object
func NewUsers(appConfig Config) Users {
	users := Users{
		list:         map[int]*User{},
		allowedUsers: map[string]bool{},
		rootUsers:    map[string]bool{},
	}

	for _, name := range appConfig.allowUsers {
		users.allowedUsers[name] = true
	}
	for _, name := range appConfig.rootUsers {
		users.allowedUsers[name] = true
		users.rootUsers[name] = true
	}
	return users
}

// add new user if not exists
func (users Users) AddNew(tgbot_user tgbotapi.User, tgbot_chat tgbotapi.UserOrGroupChat) {
	privateChatID := 0
	if tgbot_chat.Title == "" {
		privateChatID = tgbot_chat.ID
	}

	if _, ok := users.list[tgbot_user.ID]; ok && privateChatID > 0 {
		users.list[tgbot_user.ID].PrivateChatID = privateChatID
	} else if !ok {
		users.list[tgbot_user.ID] = &User{
			UserName:      tgbot_user.UserName,
			FirstName:     tgbot_user.FirstName,
			LastName:      tgbot_user.LastName,
			IsAuthorized:  users.allowedUsers[tgbot_user.UserName],
			IsRoot:        users.rootUsers[tgbot_user.UserName],
			PrivateChatID: privateChatID,
		}
	}
}

// generate code
func (users Users) DoLogin(user_id int) {
	users.list[user_id].IsAuthorized = false
	users.list[user_id].AuthCode = getRandomCode()
}

// check code for user
func (users Users) IsValidCode(user_id int, code string) bool {
	result := code != "" && code == users.list[user_id].AuthCode
	if result {
		users.list[user_id].IsAuthorized = true
	}

	return result
}

// check code for user
func (users Users) IsAuthorized(tgbot_user tgbotapi.User) bool {
	isAuthorized := false
	if _, ok := users.list[tgbot_user.ID]; ok && users.list[tgbot_user.ID].IsAuthorized {
		isAuthorized = true
	}

	return isAuthorized
}

// send message to all root users
func (users Users) broadcastForRoots(bot *tgbotapi.BotAPI, message string) {
	for _, user := range users.list {
		if user.IsRoot && user.PrivateChatID > 0 {
			bot.SendMessage(tgbotapi.NewMessage(user.PrivateChatID, message))
		}
	}
}

// Format user name
func (users Users) String(user_id int) string {
	result := fmt.Sprintf("%s %s", users.list[user_id].FirstName, users.list[user_id].LastName)
	if users.list[user_id].UserName != "" {
		result += fmt.Sprintf(" (@%s)", users.list[user_id].UserName)
	}
	return result
}

// generate random code for authorize user
func getRandomCode() string {
	buffer := make([]byte, CODE_BYTES_LENGTH)
	_, err := rand.Read(buffer)
	if err != nil {
		log.Fatal("Get code error:", err)
	}

	return base64.URLEncoding.EncodeToString(buffer)
}
<|endoftext|>


<commit_before>package jsonschema

import (
	"encoding/json"
	"fmt"
	"regexp"
	"unicode/utf8"
)

// MaxLength MUST be a non-negative integer.
// A string instance is valid against this keyword if its length is less than, or equal to, the value of this keyword.
// The length of a string instance is defined as the number of its characters as defined by RFC 7159 [RFC7159].
type MaxLength int

// NewMaxLength allocates a new MaxLength validator
func NewMaxLength() Validator {
	return new(MaxLength)
}

// Validate implements the Validator interface for MaxLength
func (m MaxLength) Validate(propPath string, data interface{}, errs *[]ValError) {
	if str, ok := data.(string); ok {
		if utf8.RuneCountInString(str) > int(m) {
			AddError(errs, propPath, data, fmt.Sprintf("max length of %d characters exceeded: %s", m, str))
		}
	}
}

// MinLength MUST be a non-negative integer.
// A string instance is valid against this keyword if its length is greater than, or equal to, the value of this keyword.
// The length of a string instance is defined as the number of its characters as defined by RFC 7159 [RFC7159].
// Omitting this keyword has the same behavior as a value of 0.
type MinLength int

// NewMinLength allocates a new MinLength validator
func NewMinLength() Validator {
	return new(MinLength)
}

// Validate implements the Validator interface for MinLength
func (m MinLength) Validate(propPath string, data interface{}, errs *[]ValError) {
	if str, ok := data.(string); ok {
		if utf8.RuneCountInString(str) < int(m) {
			AddError(errs, propPath, data, fmt.Sprintf("min length of %d characters required: %s", m, str))
		}
	}
}

// Pattern MUST be a string. This string SHOULD be a valid regular expression,
// according to the ECMA 262 regular expression dialect.
// A string instance is considered valid if the regular expression matches the instance successfully.
// Recall: regular expressions are not implicitly anchored.
type Pattern regexp.Regexp

// NewPattern allocates a new Pattern validator
func NewPattern() Validator {
	return &Pattern{}
}

// Validate implements the Validator interface for Pattern
func (p Pattern) Validate(propPath string, data interface{}, errs *[]ValError) {
	re := regexp.Regexp(p)
	if str, ok := data.(string); ok {
		if !re.Match([]byte(str)) {
			AddError(errs, propPath, data, fmt.Sprintf("regexp pattrn %s mismatch on string: %s", re.String(), str))
		}
	}
}

// UnmarshalJSON implements the json.Unmarshaler interface for Pattern
func (p *Pattern) UnmarshalJSON(data []byte) error {
	var str string
	if err := json.Unmarshal(data, &str); err != nil {
		return err
	}

	ptn, err := regexp.Compile(str)
	if err != nil {
		return err
	}

	*p = Pattern(*ptn)
	return nil
}

// MarshalJSON implements json.Marshaler for Pattern
func (p Pattern) MarshalJSON() ([]byte, error) {
	re := regexp.Regexp(p)
	rep := &re
	return json.Marshal(rep.String())
}
<commit_msg>fix: Typo (#52)<commit_after>package jsonschema

import (
	"encoding/json"
	"fmt"
	"regexp"
	"unicode/utf8"
)

// MaxLength MUST be a non-negative integer.
// A string instance is valid against this keyword if its length is less than, or equal to, the value of this keyword.
// The length of a string instance is defined as the number of its characters as defined by RFC 7159 [RFC7159].
type MaxLength int

// NewMaxLength allocates a new MaxLength validator
func NewMaxLength() Validator {
	return new(MaxLength)
}

// Validate implements the Validator interface for MaxLength
func (m MaxLength) Validate(propPath string, data interface{}, errs *[]ValError) {
	if str, ok := data.(string); ok {
		if utf8.RuneCountInString(str) > int(m) {
			AddError(errs, propPath, data, fmt.Sprintf("max length of %d characters exceeded: %s", m, str))
		}
	}
}

// MinLength MUST be a non-negative integer.
// A string instance is valid against this keyword if its length is greater than, or equal to, the value of this keyword.
// The length of a string instance is defined as the number of its characters as defined by RFC 7159 [RFC7159].
// Omitting this keyword has the same behavior as a value of 0.
type MinLength int

// NewMinLength allocates a new MinLength validator
func NewMinLength() Validator {
	return new(MinLength)
}

// Validate implements the Validator interface for MinLength
func (m MinLength) Validate(propPath string, data interface{}, errs *[]ValError) {
	if str, ok := data.(string); ok {
		if utf8.RuneCountInString(str) < int(m) {
			AddError(errs, propPath, data, fmt.Sprintf("min length of %d characters required: %s", m, str))
		}
	}
}

// Pattern MUST be a string. This string SHOULD be a valid regular expression,
// according to the ECMA 262 regular expression dialect.
// A string instance is considered valid if the regular expression matches the instance successfully.
// Recall: regular expressions are not implicitly anchored.
type Pattern regexp.Regexp

// NewPattern allocates a new Pattern validator
func NewPattern() Validator {
	return &Pattern{}
}

// Validate implements the Validator interface for Pattern
func (p Pattern) Validate(propPath string, data interface{}, errs *[]ValError) {
	re := regexp.Regexp(p)
	if str, ok := data.(string); ok {
		if !re.Match([]byte(str)) {
			AddError(errs, propPath, data, fmt.Sprintf("regexp pattern %s mismatch on string: %s", re.String(), str))
		}
	}
}

// UnmarshalJSON implements the json.Unmarshaler interface for Pattern
func (p *Pattern) UnmarshalJSON(data []byte) error {
	var str string
	if err := json.Unmarshal(data, &str); err != nil {
		return err
	}

	ptn, err := regexp.Compile(str)
	if err != nil {
		return err
	}

	*p = Pattern(*ptn)
	return nil
}

// MarshalJSON implements json.Marshaler for Pattern
func (p Pattern) MarshalJSON() ([]byte, error) {
	re := regexp.Regexp(p)
	rep := &re
	return json.Marshal(rep.String())
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"crypto/sha256"
	"encoding/hex"
)

var maxSize = flag.Int64("maxSize", 20971520, "the maximum size in bytes a user is allowed to upload")
var addr = flag.String("addr", "localhost:8000", "host:port format IP address to listen on")
var subpath = flag.String("subpath", "/", "configure a subdirectory, for use with a reverse proxy (example: ./9000server -subpath=/image9000/)")
var logrequests = flag.Bool("logrequests", false, "print all HTTP requests to stdout")

var acceptedfmt = map[string]string{
	"image/jpeg":       "jpg",
	"image/png":        "png",
	"image/gif":        "gif",
	"video/webm":       "webm",
	"video/x-matroska": "mkv",
	"video/ogg":        "ogv",
	"application/ogg":  "ogg",
	"audio/ogg":        "ogg",
	"audio/mp3":        "mp3",
}

func CreateFileId(bt []byte, ext string) string {
	bytes := sha256.Sum256(bt)
	sha := hex.EncodeToString(bytes[:])
	return sha[:14] + "." + ext
}

func Log(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if *logrequests {
			fmt.Printf("%s (%s) %s %s\n", r.RemoteAddr, r.UserAgent(), r.Method, r.URL)
		}
		handler.ServeHTTP(w, r)
	})
}

func UploadHandler(rw http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "POST":
		r.ParseMultipartForm(*maxSize)
		file, _, _ := r.FormFile("file")
		if r.ContentLength > *maxSize {
			http.Error(rw, "File Too big!", http.StatusRequestEntityTooLarge)
			return
		}

		ImageReader := bufio.NewReader(file)
		ImageBuffer := make([]byte, r.ContentLength)
		_, err := ImageReader.Read(ImageBuffer)
		if err != nil {
			http.Error(rw, err.Error(), http.StatusInternalServerError)
			return
		}

		ImageType := http.DetectContentType(ImageBuffer)

		if acceptedfmt[ImageType] != "" {
			id := CreateFileId(ImageBuffer, acceptedfmt[ImageType])
			if _, err := os.Stat("web/img/" + id); os.IsNotExist(err) {
				err = ioutil.WriteFile("web/img/"+id, ImageBuffer, 0666)
				if err != nil {
					panic(err)
				}
				http.Redirect(rw, r, *subpath+"img/"+id, 301)
			} else {
				http.Redirect(rw, r, "https://www.youtube.com/watch?v=dQw4w9WgXcQ", 301)
			}
		} else {
			http.Error(rw, fmt.Sprintf("File type (%s) not supported.", ImageType), http.StatusBadRequest)
		}
	default:
		http.Error(rw, "Bad Request", http.StatusBadRequest)
	}
}

func main() {
	flag.Parse()

	if _, err := os.Stat("web/img"); os.IsNotExist(err) {
		os.Mkdir("web/img", 0666)
	}

	http.Handle("/", http.FileServer(http.Dir("web")))

	http.HandleFunc("/upload", UploadHandler)
	http.ListenAndServe(*addr, Log(http.DefaultServeMux))
}
<commit_msg>fix Content-Length issue<commit_after>package main

import (
	"bytes"
	"io"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"crypto/sha256"
	"encoding/hex"
)

var maxSize = flag.Int64("maxSize", 20971520, "the maximum size in bytes a user is allowed to upload")
var addr = flag.String("addr", "localhost:8000", "host:port format IP address to listen on")
var subpath = flag.String("subpath", "/", "configure a subdirectory, for use with a reverse proxy (example: ./9000server -subpath=/image9000/)")
var logrequests = flag.Bool("logrequests", false, "print all HTTP requests to stdout")

var acceptedfmt = map[string]string{
	"image/jpeg":       "jpg",
	"image/png":        "png",
	"image/gif":        "gif",
	"video/webm":       "webm",
	"video/x-matroska": "mkv",
	"video/ogg":        "ogv",
	"application/ogg":  "ogg",
	"audio/ogg":        "ogg",
	"audio/mp3":        "mp3",
}

func CreateFileId(bt []byte, ext string) string {
	bytes := sha256.Sum256(bt)
	sha := hex.EncodeToString(bytes[:])
	return sha[:14] + "." + ext
}

func Log(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if *logrequests {
			fmt.Printf("%s (%s) %s %s\n", r.RemoteAddr, r.UserAgent(), r.Method, r.URL)
		}
		handler.ServeHTTP(w, r)
	})
}

func UploadHandler(rw http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "POST":
		r.ParseMultipartForm(*maxSize)
		file, _, _ := r.FormFile("file")

		if r.ContentLength > *maxSize {
			http.Error(rw, "File Too big!", http.StatusRequestEntityTooLarge)
			return
		}

		var ImageBuf bytes.Buffer

		io.Copy(&ImageBuf, file)

		ImageBuffer := ImageBuf.Bytes()
		ImageType := http.DetectContentType(ImageBuffer)

		if acceptedfmt[ImageType] != "" {
			id := CreateFileId(ImageBuffer, acceptedfmt[ImageType])
			fmt.Println("File id:", id)
			if _, err := os.Stat("web/img/" + id); os.IsNotExist(err) {
				err = ioutil.WriteFile("web/img/"+id, ImageBuffer, 0666)
				if err != nil {
					panic(err)
				}
				http.Redirect(rw, r, *subpath+"img/"+id, 301)
			} else {
				http.Redirect(rw, r, "https://www.youtube.com/watch?v=dQw4w9WgXcQ", 301)
			}
		} else {
			http.Error(rw, fmt.Sprintf("File type (%s) not supported.", ImageType), http.StatusBadRequest)
		}
	default:
		http.Error(rw, "Bad Request", http.StatusBadRequest)
	}
}

func main() {
	flag.Parse()

	if _, err := os.Stat("web/img"); os.IsNotExist(err) {
		os.Mkdir("web/img", 0666)
	}

	http.Handle("/", http.FileServer(http.Dir("web")))

	http.HandleFunc("/upload", UploadHandler)
	http.ListenAndServe(*addr, Log(http.DefaultServeMux))
}
<|endoftext|>


<commit_before>package edit

import (
	"fmt"
	"strings"
	"unicode/utf8"

	"github.com/elves/elvish/edit/uitypes"
	"github.com/elves/elvish/util"
)

// Completion subsystem.

// Interface.

type completion struct {
	completer  string
	begin, end int
	candidates []*candidate

	filtering       bool
	filter          string
	filtered        []*candidate
	selected        int
	firstShown      int
	lastShownInFull int
	height          int
}

func (*completion) Mode() ModeType {
	return modeCompletion
}

func (c *completion) needScrollbar() bool {
	return c.firstShown > 0 || c.lastShownInFull < len(c.filtered)-1
}

func (c *completion) ModeLine() renderer {
	ml := modeLineRenderer{fmt.Sprintf(" COMPLETING %s ", c.completer), c.filter}
	if !c.needScrollbar() {
		return ml
	}
	return modeLineWithScrollBarRenderer{ml,
		len(c.filtered), c.firstShown, c.lastShownInFull + 1}
}

func (c *completion) CursorOnModeLine() bool {
	return c.filtering
}

func startCompl(ed *Editor) {
	startCompletionInner(ed, false)
}

func complPrefixOrStartCompl(ed *Editor) {
	startCompletionInner(ed, true)
}

func complUp(ed *Editor) {
	ed.completion.prev(false)
}

func complDown(ed *Editor) {
	ed.completion.next(false)
}

func complLeft(ed *Editor) {
	if c := ed.completion.selected - ed.completion.height; c >= 0 {
		ed.completion.selected = c
	}
}

func complRight(ed *Editor) {
	if c := ed.completion.selected + ed.completion.height; c < len(ed.completion.filtered) {
		ed.completion.selected = c
	}
}

func complDownCycle(ed *Editor) {
	ed.completion.next(true)
}

// acceptCompletion accepts currently selected completion candidate.
func complAccept(ed *Editor) {
	c := ed.completion
	if 0 <= c.selected && c.selected < len(c.filtered) {
		ed.line, ed.dot = c.apply(ed.line, ed.dot)
	}
	ed.mode = &ed.insert
}

func complDefault(ed *Editor) {
	k := ed.lastKey
	c := &ed.completion
	if c.filtering && likeChar(k) {
		c.changeFilter(c.filter + string(k.Rune))
	} else if c.filtering && k == (uitypes.Key{uitypes.Backspace, 0}) {
		_, size := utf8.DecodeLastRuneInString(c.filter)
		if size > 0 {
			c.changeFilter(c.filter[:len(c.filter)-size])
		}
	} else {
		complAccept(ed)
		ed.nextAction = action{typ: reprocessKey}
	}
}

func complTriggerFilter(ed *Editor) {
	c := &ed.completion
	if c.filtering {
		c.filtering = false
		c.changeFilter("")
	} else {
		c.filtering = true
	}
}

func (comp *completion) selectedCandidate() *candidate {
	if comp.selected == -1 {
		return &candidate{}
	}
	return comp.filtered[comp.selected]
}

// apply returns the line and dot after applying a candidate.
func (comp *completion) apply(line string, dot int) (string, int) {
	text := comp.selectedCandidate().text
	return line[:comp.begin] + text + line[comp.end:], comp.begin + len(text)
}

func (c *completion) prev(cycle bool) {
	c.selected--
	if c.selected == -1 {
		if cycle {
			c.selected = len(c.filtered) - 1
		} else {
			c.selected++
		}
	}
}

func (c *completion) next(cycle bool) {
	c.selected++
	if c.selected == len(c.filtered) {
		if cycle {
			c.selected = 0
		} else {
			c.selected--
		}
	}
}

func startCompletionInner(ed *Editor, acceptPrefix bool) {
	node := findLeafNode(ed.chunk, ed.dot)
	if node == nil {
		return
	}

	c := &completion{begin: -1}
	shownError := false
	for _, item := range completers {
		compl, err := item.completer(node, ed.evaler)
		if compl != nil {
			c.completer = item.name
			c.begin, c.end, c.candidates = compl.begin, compl.end, compl.candidates
			c.filtered = c.candidates
			break
		} else if err != nil && err != errCompletionUnapplicable {
			ed.addTip("%v", err)
			shownError = true
			break
		}
	}

	if c.begin < 0 {
		if !shownError {
			ed.addTip("unsupported completion :(")
		}
		Logger.Println("path to current leaf, leaf first")
		for n := node; n != nil; n = n.Parent() {
			Logger.Printf("%T (%d-%d)", n, n.Begin(), n.End())
		}
	} else if len(c.filtered) == 0 {
		ed.addTip("no candidate for %s", c.completer)
	} else {
		if acceptPrefix {
			// If there is a non-empty longest common prefix, insert it and
			// don't start completion mode.
			//
			// As a special case, when there is exactly one candidate, it is
			// immeidately accepted.
			prefix := c.filtered[0].text
			for _, cand := range c.filtered[1:] {
				prefix = commonPrefix(prefix, cand.text)
				if prefix == "" {
					break
				}
			}
			if prefix != "" && prefix != ed.line[c.begin:c.end] {
				ed.line = ed.line[:c.begin] + prefix + ed.line[c.end:]
				ed.dot = c.begin + len(prefix)
				return
			}
		}
		ed.completion = *c
		ed.mode = &ed.completion
	}
}

// commonPrefix returns the longest common prefix of two strings.
func commonPrefix(s, t string) string {
	for i, r := range s {
		if i >= len(t) {
			return s[:i]
		}
		r2, _ := utf8.DecodeRuneInString(t[i:])
		if r2 != r {
			return s[:i]
		}
	}
	return s
}

const (
	completionColMarginLeft  = 1
	completionColMarginRight = 1
	completionColMarginTotal = completionColMarginLeft + completionColMarginRight
)

// maxWidth finds the maximum wcwidth of display texts of candidates [lo, hi).
// hi may be larger than the number of candidates, in which case it is truncated
// to the number of candidates.
func (comp *completion) maxWidth(lo, hi int) int {
	if hi > len(comp.filtered) {
		hi = len(comp.filtered)
	}
	width := 0
	for i := lo; i < hi; i++ {
		w := util.Wcswidth(comp.filtered[i].display.text)
		if width < w {
			width = w
		}
	}
	return width
}

func (comp *completion) ListRender(width, maxHeight int) *buffer {
	b := newBuffer(width)
	cands := comp.filtered
	if len(cands) == 0 {
		b.writes(util.TrimWcwidth("(no result)", width), "")
		return b
	}
	if maxHeight <= 1 || width <= 2 {
		b.writes(util.TrimWcwidth("(terminal too small)", width), "")
		return b
	}

	// Reserve the the rightmost row as margins.
	width -= 1

	// Determine comp.height and comp.firstShown.
	// First determine whether all candidates can be fit in the screen,
	// assuming that they are all of maximum width. If that is the case, we use
	// the computed height as the height for the listing, and the first
	// candidate to show is 0. Otherwise, we use min(height, len(cands)) as the
	// height and find the first candidate to show.
	perLine := max(1, width/(comp.maxWidth(0, len(cands))+completionColMarginTotal))
	heightBound := util.CeilDiv(len(cands), perLine)
	first := 0
	height := 0
	if heightBound < maxHeight {
		height = heightBound
	} else {
		height = min(maxHeight, len(cands))
		// Determine the first column to show. We start with the column in which the
		// selected one is found, moving to the left until either the width is
		// exhausted, or the old value of firstShown has been hit.
		first = comp.selected / height * height
		w := comp.maxWidth(first, first+height) + completionColMarginTotal
		for ; first > comp.firstShown; first -= height {
			dw := comp.maxWidth(first-height, first) + completionColMarginTotal
			if w+dw > width {
				break
			}
			w += dw
		}
	}
	comp.height = height
	comp.firstShown = first

	var i, j int
	remainedWidth := width
	trimmed := false
	// Show the results in columns, until width is exceeded.
	for i = first; i < len(cands); i += height {
		// Determine the width of the column (without the margin)
		colWidth := comp.maxWidth(i, min(i+height, len(cands)))
		totalColWidth := colWidth + completionColMarginTotal
		if totalColWidth > remainedWidth {
			totalColWidth = remainedWidth
			colWidth = totalColWidth - completionColMarginTotal
			trimmed = true
		}

		col := newBuffer(totalColWidth)
		for j = i; j < i+height; j++ {
			if j > i {
				col.newline()
			}
			if j >= len(cands) {
				// Write padding to make the listing a rectangle.
				col.writePadding(totalColWidth, styleForCompletion.String())
			} else {
				col.writePadding(completionColMarginLeft, styleForCompletion.String())
				s := joinStyles(styleForCompletion, cands[j].display.styles)
				if j == comp.selected {
					s = append(s, styleForSelectedCompletion.String())
				}
				col.writes(util.ForceWcwidth(cands[j].display.text, colWidth), s.String())
				col.writePadding(completionColMarginRight, styleForCompletion.String())
				if !trimmed {
					comp.lastShownInFull = j
				}
			}
		}

		b.extendHorizontal(col, 0)
		remainedWidth -= totalColWidth
		if remainedWidth <= completionColMarginTotal {
			break
		}
	}
	// When the listing is incomplete, always use up the entire width.
	if remainedWidth > 0 && comp.needScrollbar() {
		col := newBuffer(remainedWidth)
		for i := 0; i < height; i++ {
			if i > 0 {
				col.newline()
			}
			col.writePadding(remainedWidth, styleForCompletion.String())
		}
		b.extendHorizontal(col, 0)
		remainedWidth = 0
	}
	return b
}

func (c *completion) changeFilter(f string) {
	c.filter = f
	if f == "" {
		c.filtered = c.candidates
		return
	}
	c.filtered = nil
	for _, cand := range c.candidates {
		if strings.Contains(cand.display.text, f) {
			c.filtered = append(c.filtered, cand)
		}
	}
	if len(c.filtered) > 0 {
		c.selected = 0
	} else {
		c.selected = -1
	}
}
<commit_msg>Let edit.completion embed edit.compl.<commit_after>package edit

import (
	"fmt"
	"strings"
	"unicode/utf8"

	"github.com/elves/elvish/edit/uitypes"
	"github.com/elves/elvish/util"
)

// Completion subsystem.

// Interface.

type completion struct {
	compl
	completer string

	filtering       bool
	filter          string
	filtered        []*candidate
	selected        int
	firstShown      int
	lastShownInFull int
	height          int
}

func (*completion) Mode() ModeType {
	return modeCompletion
}

func (c *completion) needScrollbar() bool {
	return c.firstShown > 0 || c.lastShownInFull < len(c.filtered)-1
}

func (c *completion) ModeLine() renderer {
	ml := modeLineRenderer{fmt.Sprintf(" COMPLETING %s ", c.completer), c.filter}
	if !c.needScrollbar() {
		return ml
	}
	return modeLineWithScrollBarRenderer{ml,
		len(c.filtered), c.firstShown, c.lastShownInFull + 1}
}

func (c *completion) CursorOnModeLine() bool {
	return c.filtering
}

func startCompl(ed *Editor) {
	startCompletionInner(ed, false)
}

func complPrefixOrStartCompl(ed *Editor) {
	startCompletionInner(ed, true)
}

func complUp(ed *Editor) {
	ed.completion.prev(false)
}

func complDown(ed *Editor) {
	ed.completion.next(false)
}

func complLeft(ed *Editor) {
	if c := ed.completion.selected - ed.completion.height; c >= 0 {
		ed.completion.selected = c
	}
}

func complRight(ed *Editor) {
	if c := ed.completion.selected + ed.completion.height; c < len(ed.completion.filtered) {
		ed.completion.selected = c
	}
}

func complDownCycle(ed *Editor) {
	ed.completion.next(true)
}

// acceptCompletion accepts currently selected completion candidate.
func complAccept(ed *Editor) {
	c := ed.completion
	if 0 <= c.selected && c.selected < len(c.filtered) {
		ed.line, ed.dot = c.apply(ed.line, ed.dot)
	}
	ed.mode = &ed.insert
}

func complDefault(ed *Editor) {
	k := ed.lastKey
	c := &ed.completion
	if c.filtering && likeChar(k) {
		c.changeFilter(c.filter + string(k.Rune))
	} else if c.filtering && k == (uitypes.Key{uitypes.Backspace, 0}) {
		_, size := utf8.DecodeLastRuneInString(c.filter)
		if size > 0 {
			c.changeFilter(c.filter[:len(c.filter)-size])
		}
	} else {
		complAccept(ed)
		ed.nextAction = action{typ: reprocessKey}
	}
}

func complTriggerFilter(ed *Editor) {
	c := &ed.completion
	if c.filtering {
		c.filtering = false
		c.changeFilter("")
	} else {
		c.filtering = true
	}
}

func (comp *completion) selectedCandidate() *candidate {
	if comp.selected == -1 {
		return &candidate{}
	}
	return comp.filtered[comp.selected]
}

// apply returns the line and dot after applying a candidate.
func (comp *completion) apply(line string, dot int) (string, int) {
	text := comp.selectedCandidate().text
	return line[:comp.begin] + text + line[comp.end:], comp.begin + len(text)
}

func (c *completion) prev(cycle bool) {
	c.selected--
	if c.selected == -1 {
		if cycle {
			c.selected = len(c.filtered) - 1
		} else {
			c.selected++
		}
	}
}

func (c *completion) next(cycle bool) {
	c.selected++
	if c.selected == len(c.filtered) {
		if cycle {
			c.selected = 0
		} else {
			c.selected--
		}
	}
}

func startCompletionInner(ed *Editor, acceptPrefix bool) {
	node := findLeafNode(ed.chunk, ed.dot)
	if node == nil {
		return
	}

	c := &completion{}
	shownError := false
	for _, item := range completers {
		compl, err := item.completer(node, ed.evaler)
		if compl != nil {
			c.completer = item.name
			c.compl = *compl
			c.filtered = c.candidates
			break
		} else if err != nil && err != errCompletionUnapplicable {
			ed.addTip("%v", err)
			shownError = true
			break
		}
	}

	if c.completer == "" {
		if !shownError {
			ed.addTip("unsupported completion :(")
		}
		Logger.Println("path to current leaf, leaf first")
		for n := node; n != nil; n = n.Parent() {
			Logger.Printf("%T (%d-%d)", n, n.Begin(), n.End())
		}
	} else if len(c.filtered) == 0 {
		ed.addTip("no candidate for %s", c.completer)
	} else {
		if acceptPrefix {
			// If there is a non-empty longest common prefix, insert it and
			// don't start completion mode.
			//
			// As a special case, when there is exactly one candidate, it is
			// immeidately accepted.
			prefix := c.filtered[0].text
			for _, cand := range c.filtered[1:] {
				prefix = commonPrefix(prefix, cand.text)
				if prefix == "" {
					break
				}
			}
			if prefix != "" && prefix != ed.line[c.begin:c.end] {
				ed.line = ed.line[:c.begin] + prefix + ed.line[c.end:]
				ed.dot = c.begin + len(prefix)
				return
			}
		}
		ed.completion = *c
		ed.mode = &ed.completion
	}
}

// commonPrefix returns the longest common prefix of two strings.
func commonPrefix(s, t string) string {
	for i, r := range s {
		if i >= len(t) {
			return s[:i]
		}
		r2, _ := utf8.DecodeRuneInString(t[i:])
		if r2 != r {
			return s[:i]
		}
	}
	return s
}

const (
	completionColMarginLeft  = 1
	completionColMarginRight = 1
	completionColMarginTotal = completionColMarginLeft + completionColMarginRight
)

// maxWidth finds the maximum wcwidth of display texts of candidates [lo, hi).
// hi may be larger than the number of candidates, in which case it is truncated
// to the number of candidates.
func (comp *completion) maxWidth(lo, hi int) int {
	if hi > len(comp.filtered) {
		hi = len(comp.filtered)
	}
	width := 0
	for i := lo; i < hi; i++ {
		w := util.Wcswidth(comp.filtered[i].display.text)
		if width < w {
			width = w
		}
	}
	return width
}

func (comp *completion) ListRender(width, maxHeight int) *buffer {
	b := newBuffer(width)
	cands := comp.filtered
	if len(cands) == 0 {
		b.writes(util.TrimWcwidth("(no result)", width), "")
		return b
	}
	if maxHeight <= 1 || width <= 2 {
		b.writes(util.TrimWcwidth("(terminal too small)", width), "")
		return b
	}

	// Reserve the the rightmost row as margins.
	width -= 1

	// Determine comp.height and comp.firstShown.
	// First determine whether all candidates can be fit in the screen,
	// assuming that they are all of maximum width. If that is the case, we use
	// the computed height as the height for the listing, and the first
	// candidate to show is 0. Otherwise, we use min(height, len(cands)) as the
	// height and find the first candidate to show.
	perLine := max(1, width/(comp.maxWidth(0, len(cands))+completionColMarginTotal))
	heightBound := util.CeilDiv(len(cands), perLine)
	first := 0
	height := 0
	if heightBound < maxHeight {
		height = heightBound
	} else {
		height = min(maxHeight, len(cands))
		// Determine the first column to show. We start with the column in which the
		// selected one is found, moving to the left until either the width is
		// exhausted, or the old value of firstShown has been hit.
		first = comp.selected / height * height
		w := comp.maxWidth(first, first+height) + completionColMarginTotal
		for ; first > comp.firstShown; first -= height {
			dw := comp.maxWidth(first-height, first) + completionColMarginTotal
			if w+dw > width {
				break
			}
			w += dw
		}
	}
	comp.height = height
	comp.firstShown = first

	var i, j int
	remainedWidth := width
	trimmed := false
	// Show the results in columns, until width is exceeded.
	for i = first; i < len(cands); i += height {
		// Determine the width of the column (without the margin)
		colWidth := comp.maxWidth(i, min(i+height, len(cands)))
		totalColWidth := colWidth + completionColMarginTotal
		if totalColWidth > remainedWidth {
			totalColWidth = remainedWidth
			colWidth = totalColWidth - completionColMarginTotal
			trimmed = true
		}

		col := newBuffer(totalColWidth)
		for j = i; j < i+height; j++ {
			if j > i {
				col.newline()
			}
			if j >= len(cands) {
				// Write padding to make the listing a rectangle.
				col.writePadding(totalColWidth, styleForCompletion.String())
			} else {
				col.writePadding(completionColMarginLeft, styleForCompletion.String())
				s := joinStyles(styleForCompletion, cands[j].display.styles)
				if j == comp.selected {
					s = append(s, styleForSelectedCompletion.String())
				}
				col.writes(util.ForceWcwidth(cands[j].display.text, colWidth), s.String())
				col.writePadding(completionColMarginRight, styleForCompletion.String())
				if !trimmed {
					comp.lastShownInFull = j
				}
			}
		}

		b.extendHorizontal(col, 0)
		remainedWidth -= totalColWidth
		if remainedWidth <= completionColMarginTotal {
			break
		}
	}
	// When the listing is incomplete, always use up the entire width.
	if remainedWidth > 0 && comp.needScrollbar() {
		col := newBuffer(remainedWidth)
		for i := 0; i < height; i++ {
			if i > 0 {
				col.newline()
			}
			col.writePadding(remainedWidth, styleForCompletion.String())
		}
		b.extendHorizontal(col, 0)
		remainedWidth = 0
	}
	return b
}

func (c *completion) changeFilter(f string) {
	c.filter = f
	if f == "" {
		c.filtered = c.candidates
		return
	}
	c.filtered = nil
	for _, cand := range c.candidates {
		if strings.Contains(cand.display.text, f) {
			c.filtered = append(c.filtered, cand)
		}
	}
	if len(c.filtered) > 0 {
		c.selected = 0
	} else {
		c.selected = -1
	}
}
<|endoftext|>


<commit_before>package langdet

// Token represents a text token and its occurence in an analyzed text
type Token struct {
	Occurrence int
	Key        string
}

// ByOccurrence represents an array of tokens which can be sorted by occurrences of the tokens.
type ByOccurrence []Token

func (a ByOccurrence) Len() int      { return len(a) }
func (a ByOccurrence) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByOccurrence) Less(i, j int) bool {
	if a[i].Occurrence == a[j].Occurrence {
		return a[i].Key < a[i].Key
	}
	return a[i].Occurrence < a[j].Occurrence
}

// Language represents a language by its name and the profile ( map[token]OccurrenceRank )
type Language struct {
	Profile map[string]int
	Name    string
}

// DetectionResult represents the result from comparing 2 Profiles. It includes the confidence which is basically the
// the relative distance between the two profiles.
type DetectionResult struct {
	Name       string
	Confidence int
}

//ResByConf represents an array of DetectionResult and can be sorted by Confidence.
type ResByConf []DetectionResult

func (a ResByConf) Len() int           { return len(a) }
func (a ResByConf) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ResByConf) Less(i, j int) bool { return a[i].Confidence > a[j].Confidence }
<commit_msg>Fix typo in ngram sorting code.<commit_after>package langdet

// Token represents a text token and its occurence in an analyzed text
type Token struct {
	Occurrence int
	Key        string
}

// ByOccurrence represents an array of tokens which can be sorted by occurrences of the tokens.
type ByOccurrence []Token

func (a ByOccurrence) Len() int      { return len(a) }
func (a ByOccurrence) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByOccurrence) Less(i, j int) bool {
	if a[i].Occurrence == a[j].Occurrence {
		return a[i].Key < a[j].Key
	}
	return a[i].Occurrence < a[j].Occurrence
}

// Language represents a language by its name and the profile ( map[token]OccurrenceRank )
type Language struct {
	Profile map[string]int
	Name    string
}

// DetectionResult represents the result from comparing 2 Profiles. It includes the confidence which is basically the
// the relative distance between the two profiles.
type DetectionResult struct {
	Name       string
	Confidence int
}

//ResByConf represents an array of DetectionResult and can be sorted by Confidence.
type ResByConf []DetectionResult

func (a ResByConf) Len() int           { return len(a) }
func (a ResByConf) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ResByConf) Less(i, j int) bool { return a[i].Confidence > a[j].Confidence }
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. 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 certs implements a CertSource which speaks to the public Cloud SQL API endpoint.
package certs

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"math"
	mrand "math/rand"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/GoogleCloudPlatform/cloudsql-proxy/logging"
	"github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/util"
	"golang.org/x/oauth2"
	"google.golang.org/api/googleapi"
	sqladmin "google.golang.org/api/sqladmin/v1beta4"
)

const defaultUserAgent = "custom cloud_sql_proxy version >= 1.10"

// NewCertSource returns a CertSource which can be used to authenticate using
// the provided client, which must not be nil.
//
// This function is deprecated; use NewCertSourceOpts instead.
func NewCertSource(host string, c *http.Client, checkRegion bool) *RemoteCertSource {
	return NewCertSourceOpts(c, RemoteOpts{
		APIBasePath:  host,
		IgnoreRegion: !checkRegion,
		UserAgent:    defaultUserAgent,
	})
}

// RemoteOpts are a collection of options for NewCertSourceOpts. All fields are
// optional.
type RemoteOpts struct {
	// APIBasePath specifies the base path for the sqladmin API. If left blank,
	// the default from the autogenerated sqladmin library is used (which is
	// sufficient for nearly all users)
	APIBasePath string

	// IgnoreRegion specifies whether a missing or mismatched region in the
	// instance name should be ignored. In a future version this value will be
	// forced to 'false' by the RemoteCertSource.
	IgnoreRegion bool

	// A string for the RemoteCertSource to identify itself when contacting the
	// sqladmin API.
	UserAgent string

	// IP address type options
	IPAddrTypeOpts []string

	// Enable IAM proxy db authentication
	EnableIAMLogin bool

	// Token source for token information used in cert creation
	TokenSource oauth2.TokenSource

	// DelayKeyGenerate, if true, causes the RSA key to be generated lazily
	// on the first connection to a database. The default behavior is to generate
	// the key when the CertSource is created.
	DelayKeyGenerate bool
}

// NewCertSourceOpts returns a CertSource configured with the provided Opts.
// The provided http.Client must not be nil.
//
// Use this function instead of NewCertSource; it has a more forward-compatible
// signature.
func NewCertSourceOpts(c *http.Client, opts RemoteOpts) *RemoteCertSource {
	serv, err := sqladmin.New(c)
	if err != nil {
		panic(err) // Only will happen if the provided client is nil.
	}
	if opts.APIBasePath != "" {
		serv.BasePath = opts.APIBasePath
	}
	ua := opts.UserAgent
	if ua == "" {
		ua = defaultUserAgent
	}
	serv.UserAgent = ua

	// Set default value to be "PUBLIC,PRIVATE" if not specified
	if len(opts.IPAddrTypeOpts) == 0 {
		opts.IPAddrTypeOpts = []string{"PUBLIC", "PRIVATE"}
	}

	// Add "PUBLIC" as an alias for "PRIMARY"
	for index, ipAddressType := range opts.IPAddrTypeOpts {
		if strings.ToUpper(ipAddressType) == "PUBLIC" {
			opts.IPAddrTypeOpts[index] = "PRIMARY"
		}
	}

	certSource := &RemoteCertSource{
		serv:           serv,
		checkRegion:    !opts.IgnoreRegion,
		IPAddrTypes:    opts.IPAddrTypeOpts,
		EnableIAMLogin: opts.EnableIAMLogin,
		TokenSource:    opts.TokenSource,
	}
	if !opts.DelayKeyGenerate {
		// Generate the RSA key now, but don't block on it.
		go certSource.generateKey()
	}

	return certSource
}

// RemoteCertSource implements a CertSource, using Cloud SQL APIs to
// return Local certificates for identifying oneself as a specific user
// to the remote instance and Remote certificates for confirming the
// remote database's identity.
type RemoteCertSource struct {
	// keyOnce is used to create `key` lazily.
	keyOnce sync.Once
	// key is the private key used for certificates returned by Local.
	key *rsa.PrivateKey
	// serv is used to make authenticated API calls to Cloud SQL.
	serv *sqladmin.Service
	// If set, providing an incorrect region in their connection string will be
	// treated as an error. This is to provide the same functionality that will
	// occur when API calls require the region.
	checkRegion bool
	// a list of ip address types that users select
	IPAddrTypes []string
	// flag to enable IAM proxy db authentication
	EnableIAMLogin bool
	// token source for the token information used in cert creation
	TokenSource oauth2.TokenSource
}

// Constants for backoffAPIRetry. These cause the retry logic to scale the
// backoff delay from 200ms to around 3.5s.
const (
	baseBackoff    = float64(200 * time.Millisecond)
	backoffMult    = 1.618
	backoffRetries = 5
)

func backoffAPIRetry(desc, instance string, do func() error) error {
	var err error
	for i := 0; i < backoffRetries; i++ {
		err = do()
		gErr, ok := err.(*googleapi.Error)
		switch {
		case !ok:
			// 'ok' will also be false if err is nil.
			return err
		case gErr.Code == 403 && len(gErr.Errors) > 0 && gErr.Errors[0].Reason == "insufficientPermissions":
			// The case where the admin API has not yet been enabled.
			return fmt.Errorf("ensure that the Cloud SQL API is enabled for your project (https://console.cloud.google.com/flows/enableapi?apiid=sqladmin). Error during %s %s: %v", desc, instance, err)
		case gErr.Code == 404 || gErr.Code == 403:
			return fmt.Errorf("ensure that the account has access to %q (and make sure there's no typo in that name). Error during %s %s: %v", instance, desc, instance, err)
		case gErr.Code < 500:
			// Only Server-level HTTP errors are immediately retryable.
			return err
		}

		// sleep = baseBackoff * backoffMult^(retries + randomFactor)
		exp := float64(i+1) + mrand.Float64()
		sleep := time.Duration(baseBackoff * math.Pow(backoffMult, exp))
		logging.Errorf("Error in %s %s: %v; retrying in %v", desc, instance, err, sleep)
		time.Sleep(sleep)
	}
	return err
}

func refreshToken(ts oauth2.TokenSource, tok *oauth2.Token) (*oauth2.Token, error) {
	expiredToken := &oauth2.Token{
		AccessToken:  tok.AccessToken,
		TokenType:    tok.TokenType,
		RefreshToken: tok.RefreshToken,
		Expiry:       time.Time{}.Add(1), // Expired
	}
	return oauth2.ReuseTokenSource(expiredToken, ts).Token()
}

// Local returns a certificate that may be used to establish a TLS
// connection to the specified instance.
func (s *RemoteCertSource) Local(instance string) (tls.Certificate, error) {
	pkix, err := x509.MarshalPKIXPublicKey(s.generateKey().Public())
	if err != nil {
		return tls.Certificate{}, err
	}

	p, r, n := util.SplitName(instance)
	regionName := fmt.Sprintf("%s~%s", r, n)
	pubKey := string(pem.EncodeToMemory(&pem.Block{Bytes: pkix, Type: "RSA PUBLIC KEY"}))
	createEphemeralRequest := sqladmin.SslCertsCreateEphemeralRequest{
		PublicKey: pubKey,
	}
	var tok *oauth2.Token
	// If IAM login is enabled, add the OAuth2 token into the ephemeral
	// certificate request.
	if s.EnableIAMLogin {
		var tokErr error
		tok, tokErr = s.TokenSource.Token()
		if tokErr != nil {
			return tls.Certificate{}, tokErr
		}
		// Always refresh the token to ensure its expiration is far enough in
		// the future.
		tok, tokErr = refreshToken(s.TokenSource, tok)
		if tokErr != nil {
			return tls.Certificate{}, tokErr
		}
		createEphemeralRequest.AccessToken = tok.AccessToken
	}
	req := s.serv.SslCerts.CreateEphemeral(p, regionName, &createEphemeralRequest)

	var data *sqladmin.SslCert
	err = backoffAPIRetry("createEphemeral for", instance, func() error {
		data, err = req.Do()
		return err
	})
	if err != nil {
		return tls.Certificate{}, err
	}

	c, err := parseCert(data.Cert)
	if err != nil {
		return tls.Certificate{}, fmt.Errorf("couldn't parse ephemeral certificate for instance %q: %v", instance, err)
	}
	if s.EnableIAMLogin {
		// Adjust the certificate's expiration to be the earlier of tok.Expiry or c.NotAfter
		if tok.Expiry.Before(c.NotAfter) {
			c.NotAfter = tok.Expiry
		}
	}
	return tls.Certificate{
		Certificate: [][]byte{c.Raw},
		PrivateKey:  s.generateKey(),
		Leaf:        c,
	}, nil
}

func parseCert(pemCert string) (*x509.Certificate, error) {
	bl, _ := pem.Decode([]byte(pemCert))
	if bl == nil {
		return nil, errors.New("invalid PEM: " + pemCert)
	}
	return x509.ParseCertificate(bl.Bytes)
}

// Return the RSA private key, which is lazily initialized.
func (s *RemoteCertSource) generateKey() *rsa.PrivateKey {
	s.keyOnce.Do(func() {
		start := time.Now()
		pkey, err := rsa.GenerateKey(rand.Reader, 2048)
		if err != nil {
			panic(err) // very unexpected.
		}
		logging.Verbosef("Generated RSA key in %v", time.Since(start))
		s.key = pkey
	})
	return s.key
}

// Find the first matching IP address by user input IP address types
func (s *RemoteCertSource) findIPAddr(data *sqladmin.DatabaseInstance, instance string) (ipAddrInUse string, err error) {
	for _, eachIPAddrTypeByUser := range s.IPAddrTypes {
		for _, eachIPAddrTypeOfInstance := range data.IpAddresses {
			if strings.ToUpper(eachIPAddrTypeOfInstance.Type) == strings.ToUpper(eachIPAddrTypeByUser) {
				ipAddrInUse = eachIPAddrTypeOfInstance.IpAddress
				return ipAddrInUse, nil
			}
		}
	}

	ipAddrTypesOfInstance := ""
	for _, eachIPAddrTypeOfInstance := range data.IpAddresses {
		ipAddrTypesOfInstance += fmt.Sprintf("(TYPE=%v, IP_ADDR=%v)", eachIPAddrTypeOfInstance.Type, eachIPAddrTypeOfInstance.IpAddress)
	}

	ipAddrTypeOfUser := fmt.Sprintf("%v", s.IPAddrTypes)

	return "", fmt.Errorf("User input IP address type %v does not match the instance %v, the instance's IP addresses are %v ", ipAddrTypeOfUser, instance, ipAddrTypesOfInstance)
}

// Remote returns the specified instance's CA certificate, address, and name.
func (s *RemoteCertSource) Remote(instance string) (cert *x509.Certificate, addr, name, version string, err error) {
	p, region, n := util.SplitName(instance)
	regionName := fmt.Sprintf("%s~%s", region, n)
	req := s.serv.Instances.Get(p, regionName)

	var data *sqladmin.DatabaseInstance
	err = backoffAPIRetry("get instance", instance, func() error {
		data, err = req.Do()
		return err
	})
	if err != nil {
		return nil, "", "", "", err
	}

	// TODO(chowski): remove this when us-central is removed.
	if data.Region == "us-central" {
		data.Region = "us-central1"
	}
	if data.Region != region {
		if region == "" {
			err = fmt.Errorf("instance %v doesn't provide region", instance)
		} else {
			err = fmt.Errorf(`for connection string "%s": got region %q, want %q`, instance, region, data.Region)
		}
		if s.checkRegion {
			return nil, "", "", "", err
		}
		logging.Errorf("%v", err)
		logging.Errorf("WARNING: specifying the correct region in an instance string will become required in a future version!")
	}

	if len(data.IpAddresses) == 0 {
		return nil, "", "", "", fmt.Errorf("no IP address found for %v", instance)
	}
	if data.BackendType == "FIRST_GEN" {
		logging.Errorf("WARNING: proxy client does not support first generation Cloud SQL instances.")
		return nil, "", "", "", fmt.Errorf("%q is a first generation instance", instance)
	}

	// Find the first matching IP address by user input IP address types
	ipAddrInUse := ""
	ipAddrInUse, err = s.findIPAddr(data, instance)
	if err != nil {
		return nil, "", "", "", err
	}

	c, err := parseCert(data.ServerCaCert.Cert)

	return c, ipAddrInUse, p + ":" + n, data.DatabaseVersion, err
}
<commit_msg>fix: strip padding from access tokens if present (#851)<commit_after>// Copyright 2015 Google Inc. 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 certs implements a CertSource which speaks to the public Cloud SQL API endpoint.
package certs

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"math"
	mrand "math/rand"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/GoogleCloudPlatform/cloudsql-proxy/logging"
	"github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/util"
	"golang.org/x/oauth2"
	"google.golang.org/api/googleapi"
	sqladmin "google.golang.org/api/sqladmin/v1beta4"
)

const defaultUserAgent = "custom cloud_sql_proxy version >= 1.10"

// NewCertSource returns a CertSource which can be used to authenticate using
// the provided client, which must not be nil.
//
// This function is deprecated; use NewCertSourceOpts instead.
func NewCertSource(host string, c *http.Client, checkRegion bool) *RemoteCertSource {
	return NewCertSourceOpts(c, RemoteOpts{
		APIBasePath:  host,
		IgnoreRegion: !checkRegion,
		UserAgent:    defaultUserAgent,
	})
}

// RemoteOpts are a collection of options for NewCertSourceOpts. All fields are
// optional.
type RemoteOpts struct {
	// APIBasePath specifies the base path for the sqladmin API. If left blank,
	// the default from the autogenerated sqladmin library is used (which is
	// sufficient for nearly all users)
	APIBasePath string

	// IgnoreRegion specifies whether a missing or mismatched region in the
	// instance name should be ignored. In a future version this value will be
	// forced to 'false' by the RemoteCertSource.
	IgnoreRegion bool

	// A string for the RemoteCertSource to identify itself when contacting the
	// sqladmin API.
	UserAgent string

	// IP address type options
	IPAddrTypeOpts []string

	// Enable IAM proxy db authentication
	EnableIAMLogin bool

	// Token source for token information used in cert creation
	TokenSource oauth2.TokenSource

	// DelayKeyGenerate, if true, causes the RSA key to be generated lazily
	// on the first connection to a database. The default behavior is to generate
	// the key when the CertSource is created.
	DelayKeyGenerate bool
}

// NewCertSourceOpts returns a CertSource configured with the provided Opts.
// The provided http.Client must not be nil.
//
// Use this function instead of NewCertSource; it has a more forward-compatible
// signature.
func NewCertSourceOpts(c *http.Client, opts RemoteOpts) *RemoteCertSource {
	serv, err := sqladmin.New(c)
	if err != nil {
		panic(err) // Only will happen if the provided client is nil.
	}
	if opts.APIBasePath != "" {
		serv.BasePath = opts.APIBasePath
	}
	ua := opts.UserAgent
	if ua == "" {
		ua = defaultUserAgent
	}
	serv.UserAgent = ua

	// Set default value to be "PUBLIC,PRIVATE" if not specified
	if len(opts.IPAddrTypeOpts) == 0 {
		opts.IPAddrTypeOpts = []string{"PUBLIC", "PRIVATE"}
	}

	// Add "PUBLIC" as an alias for "PRIMARY"
	for index, ipAddressType := range opts.IPAddrTypeOpts {
		if strings.ToUpper(ipAddressType) == "PUBLIC" {
			opts.IPAddrTypeOpts[index] = "PRIMARY"
		}
	}

	certSource := &RemoteCertSource{
		serv:           serv,
		checkRegion:    !opts.IgnoreRegion,
		IPAddrTypes:    opts.IPAddrTypeOpts,
		EnableIAMLogin: opts.EnableIAMLogin,
		TokenSource:    opts.TokenSource,
	}
	if !opts.DelayKeyGenerate {
		// Generate the RSA key now, but don't block on it.
		go certSource.generateKey()
	}

	return certSource
}

// RemoteCertSource implements a CertSource, using Cloud SQL APIs to
// return Local certificates for identifying oneself as a specific user
// to the remote instance and Remote certificates for confirming the
// remote database's identity.
type RemoteCertSource struct {
	// keyOnce is used to create `key` lazily.
	keyOnce sync.Once
	// key is the private key used for certificates returned by Local.
	key *rsa.PrivateKey
	// serv is used to make authenticated API calls to Cloud SQL.
	serv *sqladmin.Service
	// If set, providing an incorrect region in their connection string will be
	// treated as an error. This is to provide the same functionality that will
	// occur when API calls require the region.
	checkRegion bool
	// a list of ip address types that users select
	IPAddrTypes []string
	// flag to enable IAM proxy db authentication
	EnableIAMLogin bool
	// token source for the token information used in cert creation
	TokenSource oauth2.TokenSource
}

// Constants for backoffAPIRetry. These cause the retry logic to scale the
// backoff delay from 200ms to around 3.5s.
const (
	baseBackoff    = float64(200 * time.Millisecond)
	backoffMult    = 1.618
	backoffRetries = 5
)

func backoffAPIRetry(desc, instance string, do func() error) error {
	var err error
	for i := 0; i < backoffRetries; i++ {
		err = do()
		gErr, ok := err.(*googleapi.Error)
		switch {
		case !ok:
			// 'ok' will also be false if err is nil.
			return err
		case gErr.Code == 403 && len(gErr.Errors) > 0 && gErr.Errors[0].Reason == "insufficientPermissions":
			// The case where the admin API has not yet been enabled.
			return fmt.Errorf("ensure that the Cloud SQL API is enabled for your project (https://console.cloud.google.com/flows/enableapi?apiid=sqladmin). Error during %s %s: %v", desc, instance, err)
		case gErr.Code == 404 || gErr.Code == 403:
			return fmt.Errorf("ensure that the account has access to %q (and make sure there's no typo in that name). Error during %s %s: %v", instance, desc, instance, err)
		case gErr.Code < 500:
			// Only Server-level HTTP errors are immediately retryable.
			return err
		}

		// sleep = baseBackoff * backoffMult^(retries + randomFactor)
		exp := float64(i+1) + mrand.Float64()
		sleep := time.Duration(baseBackoff * math.Pow(backoffMult, exp))
		logging.Errorf("Error in %s %s: %v; retrying in %v", desc, instance, err, sleep)
		time.Sleep(sleep)
	}
	return err
}

func refreshToken(ts oauth2.TokenSource, tok *oauth2.Token) (*oauth2.Token, error) {
	expiredToken := &oauth2.Token{
		AccessToken:  tok.AccessToken,
		TokenType:    tok.TokenType,
		RefreshToken: tok.RefreshToken,
		Expiry:       time.Time{}.Add(1), // Expired
	}
	return oauth2.ReuseTokenSource(expiredToken, ts).Token()
}

// Local returns a certificate that may be used to establish a TLS
// connection to the specified instance.
func (s *RemoteCertSource) Local(instance string) (tls.Certificate, error) {
	pkix, err := x509.MarshalPKIXPublicKey(s.generateKey().Public())
	if err != nil {
		return tls.Certificate{}, err
	}

	p, r, n := util.SplitName(instance)
	regionName := fmt.Sprintf("%s~%s", r, n)
	pubKey := string(pem.EncodeToMemory(&pem.Block{Bytes: pkix, Type: "RSA PUBLIC KEY"}))
	createEphemeralRequest := sqladmin.SslCertsCreateEphemeralRequest{
		PublicKey: pubKey,
	}
	var tok *oauth2.Token
	// If IAM login is enabled, add the OAuth2 token into the ephemeral
	// certificate request.
	if s.EnableIAMLogin {
		var tokErr error
		tok, tokErr = s.TokenSource.Token()
		if tokErr != nil {
			return tls.Certificate{}, tokErr
		}
		// Always refresh the token to ensure its expiration is far enough in
		// the future.
		tok, tokErr = refreshToken(s.TokenSource, tok)
		if tokErr != nil {
			return tls.Certificate{}, tokErr
		}
		// TODO: remove this once issue with OAuth2 Tokens is resolved.
		// See https://github.com/GoogleCloudPlatform/cloudsql-proxy/issues/852.
		createEphemeralRequest.AccessToken = strings.TrimRight(tok.AccessToken, ".")
	}
	req := s.serv.SslCerts.CreateEphemeral(p, regionName, &createEphemeralRequest)

	var data *sqladmin.SslCert
	err = backoffAPIRetry("createEphemeral for", instance, func() error {
		data, err = req.Do()
		return err
	})
	if err != nil {
		return tls.Certificate{}, err
	}

	c, err := parseCert(data.Cert)
	if err != nil {
		return tls.Certificate{}, fmt.Errorf("couldn't parse ephemeral certificate for instance %q: %v", instance, err)
	}
	if s.EnableIAMLogin {
		// Adjust the certificate's expiration to be the earlier of tok.Expiry or c.NotAfter
		if tok.Expiry.Before(c.NotAfter) {
			c.NotAfter = tok.Expiry
		}
	}
	return tls.Certificate{
		Certificate: [][]byte{c.Raw},
		PrivateKey:  s.generateKey(),
		Leaf:        c,
	}, nil
}

func parseCert(pemCert string) (*x509.Certificate, error) {
	bl, _ := pem.Decode([]byte(pemCert))
	if bl == nil {
		return nil, errors.New("invalid PEM: " + pemCert)
	}
	return x509.ParseCertificate(bl.Bytes)
}

// Return the RSA private key, which is lazily initialized.
func (s *RemoteCertSource) generateKey() *rsa.PrivateKey {
	s.keyOnce.Do(func() {
		start := time.Now()
		pkey, err := rsa.GenerateKey(rand.Reader, 2048)
		if err != nil {
			panic(err) // very unexpected.
		}
		logging.Verbosef("Generated RSA key in %v", time.Since(start))
		s.key = pkey
	})
	return s.key
}

// Find the first matching IP address by user input IP address types
func (s *RemoteCertSource) findIPAddr(data *sqladmin.DatabaseInstance, instance string) (ipAddrInUse string, err error) {
	for _, eachIPAddrTypeByUser := range s.IPAddrTypes {
		for _, eachIPAddrTypeOfInstance := range data.IpAddresses {
			if strings.ToUpper(eachIPAddrTypeOfInstance.Type) == strings.ToUpper(eachIPAddrTypeByUser) {
				ipAddrInUse = eachIPAddrTypeOfInstance.IpAddress
				return ipAddrInUse, nil
			}
		}
	}

	ipAddrTypesOfInstance := ""
	for _, eachIPAddrTypeOfInstance := range data.IpAddresses {
		ipAddrTypesOfInstance += fmt.Sprintf("(TYPE=%v, IP_ADDR=%v)", eachIPAddrTypeOfInstance.Type, eachIPAddrTypeOfInstance.IpAddress)
	}

	ipAddrTypeOfUser := fmt.Sprintf("%v", s.IPAddrTypes)

	return "", fmt.Errorf("User input IP address type %v does not match the instance %v, the instance's IP addresses are %v ", ipAddrTypeOfUser, instance, ipAddrTypesOfInstance)
}

// Remote returns the specified instance's CA certificate, address, and name.
func (s *RemoteCertSource) Remote(instance string) (cert *x509.Certificate, addr, name, version string, err error) {
	p, region, n := util.SplitName(instance)
	regionName := fmt.Sprintf("%s~%s", region, n)
	req := s.serv.Instances.Get(p, regionName)

	var data *sqladmin.DatabaseInstance
	err = backoffAPIRetry("get instance", instance, func() error {
		data, err = req.Do()
		return err
	})
	if err != nil {
		return nil, "", "", "", err
	}

	// TODO(chowski): remove this when us-central is removed.
	if data.Region == "us-central" {
		data.Region = "us-central1"
	}
	if data.Region != region {
		if region == "" {
			err = fmt.Errorf("instance %v doesn't provide region", instance)
		} else {
			err = fmt.Errorf(`for connection string "%s": got region %q, want %q`, instance, region, data.Region)
		}
		if s.checkRegion {
			return nil, "", "", "", err
		}
		logging.Errorf("%v", err)
		logging.Errorf("WARNING: specifying the correct region in an instance string will become required in a future version!")
	}

	if len(data.IpAddresses) == 0 {
		return nil, "", "", "", fmt.Errorf("no IP address found for %v", instance)
	}
	if data.BackendType == "FIRST_GEN" {
		logging.Errorf("WARNING: proxy client does not support first generation Cloud SQL instances.")
		return nil, "", "", "", fmt.Errorf("%q is a first generation instance", instance)
	}

	// Find the first matching IP address by user input IP address types
	ipAddrInUse := ""
	ipAddrInUse, err = s.findIPAddr(data, instance)
	if err != nil {
		return nil, "", "", "", err
	}

	c, err := parseCert(data.ServerCaCert.Cert)

	return c, ipAddrInUse, p + ":" + n, data.DatabaseVersion, err
}
<|endoftext|>