<commit_before>package pqt

import "sort"

// ExtendFunc ...
type ExtendFunc func(*Table)

// Table ...
type Table struct {
	self                      bool
	Name, Collate, TableSpace string
	IfNotExists, Temporary    bool
	Schema                    *Schema
	Columns                   Columns
	Constraints               []*Constraint
	OwnedRelationships        []*Relationship
	InversedRelationships     []*Relationship
	ManyToManyRelationships   []*Relationship
}

// TableOption configures how we set up the table.
type TableOption func(*Table)

// NewTable ...
func NewTable(name string, opts ...TableOption) *Table {
	t := &Table{
		Name:                  name,
		Columns:               make(Columns, 0),
		Constraints:           make([]*Constraint, 0),
		InversedRelationships: make([]*Relationship, 0),
		OwnedRelationships:    make([]*Relationship, 0),
	}

	for _, opt := range opts {
		opt(t)
	}

	return t
}

// SelfReference returns almost empty table that express self reference. Should be used with relationships.
func SelfReference() *Table {
	return &Table{
		self: true,
	}
}

// FullName ...
func (t *Table) FullName() string {
	if t.Schema != nil && t.Schema.Name != "" {
		return t.Schema.Name + "." + t.Name
	}

	return t.Name
}

// AddColumn ...
func (t *Table) AddColumn(c *Column) *Table {
	if c.Reference != nil {
		t.AddConstraint(&Constraint{
			Type:             ConstraintTypeForeignKey,
			Table:            t,
			Columns:          Columns{c},
			ReferenceTable:   c.Reference.Table,
			ReferenceColumns: Columns{c.Reference},
			OnDelete:         c.OnDelete,
			OnUpdate:         c.OnUpdate,
			Match:            c.Match,
		})
		// When constraint is created, redundant data from column needs to be removed.
		c.Reference = nil
		c.OnDelete = 0
		c.OnUpdate = 0
		c.Match = 0
	}

	return t.addColumn(c)
}

func (t *Table) addColumn(c *Column) *Table {
	if t.Columns == nil {
		t.Columns = make(Columns, 0, 1)
	}

	if c.Table == nil {
		c.Table = t
	} else {
		*c.Table = *t
	}
	t.Columns = append(t.Columns, c)

	sort.Sort(&t.Columns)
	return t
}

// AddRelationship ...
func (t *Table) AddRelationship(r *Relationship, opts ...ColumnOption) *Table {
	if r == nil {
		return t
	}
	if r.Type == RelationshipTypeManyToMany {
		return t.addRelationshipManyToMany(r, opts...)
	}
	return t.addRelationship(r, opts...)
}

func (t *Table) addRelationship(r *Relationship, opts ...ColumnOption) *Table {
	switch {
	case r.InversedTable != nil && r.InversedTable.self:
		r.InversedTable = t
	}

	switch r.Type {
	case RelationshipTypeOneToOne, RelationshipTypeManyToOne:
		r.OwnerTable = t
	case RelationshipTypeOneToMany:
		r.InversedTable = t
	}
	pk, ok := r.InversedTable.PrimaryKey()
	if !ok {
		return t
	}

	name := r.ColumnName
	if name == "" {
		name = r.InversedTable.Name + "_" + pk.Name
	}

	nt := fkType(pk.Type)

	r.OwnerTable.OwnedRelationships = append(r.OwnerTable.OwnedRelationships, r)
	if r.Bidirectional {
		r.InversedTable.InversedRelationships = append(r.InversedTable.InversedRelationships, r)
	}

	r.OwnerTable.addColumn(NewColumn(name, nt, append([]ColumnOption{WithReference(pk)}, opts...)...))

	return t
}

func (t *Table) addRelationshipManyToMany(r *Relationship, opts ...ColumnOption) *Table {
	r.ThroughTable = t
	r.ThroughTable.OwnedRelationships = append(r.ThroughTable.OwnedRelationships, r)

	if r.Bidirectional {
		r.InversedTable.ManyToManyRelationships = append(r.InversedTable.ManyToManyRelationships, r)
		r.OwnerTable.ManyToManyRelationships = append(r.OwnerTable.ManyToManyRelationships, r)
	}

	pk1, ok := r.OwnerTable.PrimaryKey()
	if !ok {
		return t
	}
	pk2, ok := r.InversedTable.PrimaryKey()
	if !ok {
		return t
	}

	name1 := r.ColumnName
	if name1 == "" {
		name1 = r.OwnerTable.Name + "_" + pk1.Name
	}

	name2 := r.ColumnName
	if name2 == "" {
		name2 = r.InversedTable.Name + "_" + pk2.Name
	}

	nt1 := fkType(pk1.Type)
	nt2 := fkType(pk2.Type)
	r.ThroughTable.AddColumn(NewColumn(name1, nt1, append([]ColumnOption{WithReference(pk1)}, opts...)...))
	r.ThroughTable.AddColumn(NewColumn(name2, nt2, append([]ColumnOption{WithReference(pk2)}, opts...)...))

	return t
}

// AddConstraint ...
func (t *Table) AddConstraint(c *Constraint) *Table {
	if t.Constraints == nil {
		t.Constraints = make([]*Constraint, 0, 1)
	}

	if c.Table == nil {
		c.Table = t
	} else {
		*c.Table = *t
	}

	t.Constraints = append(t.Constraints, c)

	return t
}

// AddCheck ...
func (t *Table) AddCheck(check string, columns ...*Column) *Table {
	return t.AddConstraint(Check(t, check, columns...))
}

// AddUnique ...
func (t *Table) AddUnique(columns ...*Column) *Table {
	return t.AddConstraint(Unique(t, columns...))
}

// SetIfNotExists ...
func (t *Table) SetIfNotExists(ine bool) *Table {
	t.IfNotExists = ine
	return t
}

// SetSchema ...
func (t *Table) SetSchema(s *Schema) *Table {
	if t.Schema == nil {
		t.Schema = s
	} else {
		*t.Schema = *s
	}

	return t
}

// PrimaryKey returns column that is primary key, or false if none.
func (t *Table) PrimaryKey() (*Column, bool) {
	for _, c := range t.Columns {
		if c.PrimaryKey {
			return c, true
		}
	}

	return nil, false
}

// Extend ...
func (t *Table) Extend(fn ExtendFunc) {
	fn(t)
}

// WithIfNotExists ...
func WithIfNotExists() func(*Table) {
	return func(t *Table) {
		t.IfNotExists = true
	}
}

// WithTemporary ...
func WithTemporary() func(*Table) {
	return func(t *Table) {
		t.Temporary = true
	}
}

// WithTableSpace ...
func WithTableSpace(s string) func(*Table) {
	return func(t *Table) {
		t.TableSpace = s
	}
}

func fkType(t Type) Type {
	switch t {
	case TypeSerial():
		return TypeInteger()
	case TypeSerialBig():
		return TypeIntegerBig()
	case TypeSerialSmall():
		return TypeIntegerSmall()
	default:
		return t
	}
}
<commit_msg>ManyToMany relationship ads compound unique constraint<commit_after>package pqt

import "sort"

// ExtendFunc ...
type ExtendFunc func(*Table)

// Table ...
type Table struct {
	self                      bool
	Name, Collate, TableSpace string
	IfNotExists, Temporary    bool
	Schema                    *Schema
	Columns                   Columns
	Constraints               []*Constraint
	OwnedRelationships        []*Relationship
	InversedRelationships     []*Relationship
	ManyToManyRelationships   []*Relationship
}

// TableOption configures how we set up the table.
type TableOption func(*Table)

// NewTable ...
func NewTable(name string, opts ...TableOption) *Table {
	t := &Table{
		Name:                  name,
		Columns:               make(Columns, 0),
		Constraints:           make([]*Constraint, 0),
		InversedRelationships: make([]*Relationship, 0),
		OwnedRelationships:    make([]*Relationship, 0),
	}

	for _, opt := range opts {
		opt(t)
	}

	return t
}

// SelfReference returns almost empty table that express self reference. Should be used with relationships.
func SelfReference() *Table {
	return &Table{
		self: true,
	}
}

// FullName ...
func (t *Table) FullName() string {
	if t.Schema != nil && t.Schema.Name != "" {
		return t.Schema.Name + "." + t.Name
	}

	return t.Name
}

// AddColumn ...
func (t *Table) AddColumn(c *Column) *Table {
	if c.Reference != nil {
		t.AddConstraint(&Constraint{
			Type:             ConstraintTypeForeignKey,
			Table:            t,
			Columns:          Columns{c},
			ReferenceTable:   c.Reference.Table,
			ReferenceColumns: Columns{c.Reference},
			OnDelete:         c.OnDelete,
			OnUpdate:         c.OnUpdate,
			Match:            c.Match,
		})
		// When constraint is created, redundant data from column needs to be removed.
		c.Reference = nil
		c.OnDelete = 0
		c.OnUpdate = 0
		c.Match = 0
	}

	return t.addColumn(c)
}

func (t *Table) addColumn(c *Column) *Table {
	if t.Columns == nil {
		t.Columns = make(Columns, 0, 1)
	}

	if c.Table == nil {
		c.Table = t
	} else {
		*c.Table = *t
	}
	t.Columns = append(t.Columns, c)

	sort.Sort(&t.Columns)
	return t
}

// AddRelationship ...
func (t *Table) AddRelationship(r *Relationship, opts ...ColumnOption) *Table {
	if r == nil {
		return t
	}
	if r.Type == RelationshipTypeManyToMany {
		return t.addRelationshipManyToMany(r, opts...)
	}
	return t.addRelationship(r, opts...)
}

func (t *Table) addRelationship(r *Relationship, opts ...ColumnOption) *Table {
	switch {
	case r.InversedTable != nil && r.InversedTable.self:
		r.InversedTable = t
	case r.OwnerTable != nil && r.OwnerTable.self:
		r.OwnerTable = t
	}

	switch r.Type {
	case RelationshipTypeOneToOne, RelationshipTypeManyToOne:
		r.OwnerTable = t
	case RelationshipTypeOneToMany:
		r.InversedTable = t
	}
	pk, ok := r.InversedTable.PrimaryKey()
	if !ok {
		return t
	}

	name := r.ColumnName
	if name == "" {
		name = r.InversedTable.Name + "_" + pk.Name
	}

	nt := fkType(pk.Type)

	r.OwnerTable.OwnedRelationships = append(r.OwnerTable.OwnedRelationships, r)
	if r.Bidirectional {
		r.InversedTable.InversedRelationships = append(r.InversedTable.InversedRelationships, r)
	}

	r.OwnerTable.addColumn(NewColumn(name, nt, append([]ColumnOption{WithReference(pk)}, opts...)...))

	return t
}

func (t *Table) addRelationshipManyToMany(r *Relationship, opts ...ColumnOption) *Table {
	r.ThroughTable = t
	r.ThroughTable.OwnedRelationships = append(r.ThroughTable.OwnedRelationships, r)

	if r.Bidirectional {
		r.InversedTable.ManyToManyRelationships = append(r.InversedTable.ManyToManyRelationships, r)
		r.OwnerTable.ManyToManyRelationships = append(r.OwnerTable.ManyToManyRelationships, r)
	}

	pk1, ok := r.OwnerTable.PrimaryKey()
	if !ok {
		return t
	}
	pk2, ok := r.InversedTable.PrimaryKey()
	if !ok {
		return t
	}

	name1 := r.ColumnName
	if name1 == "" {
		name1 = r.OwnerTable.Name + "_" + pk1.Name
	}

	name2 := r.ColumnName
	if name2 == "" {
		name2 = r.InversedTable.Name + "_" + pk2.Name
	}

	nt1 := fkType(pk1.Type)
	nt2 := fkType(pk2.Type)

	column1 := NewColumn(name1, nt1, append([]ColumnOption{WithReference(pk1)}, opts...)...)
	column2 := NewColumn(name2, nt2, append([]ColumnOption{WithReference(pk2)}, opts...)...)

	r.ThroughTable.AddColumn(column1)
	r.ThroughTable.AddColumn(column2)
	r.ThroughTable.AddUnique(column1, column2)
	return t
}

// AddConstraint ...
func (t *Table) AddConstraint(c *Constraint) *Table {
	if t.Constraints == nil {
		t.Constraints = make([]*Constraint, 0, 1)
	}

	if c.Table == nil {
		c.Table = t
	} else {
		*c.Table = *t
	}

	t.Constraints = append(t.Constraints, c)

	return t
}

// AddCheck ...
func (t *Table) AddCheck(check string, columns ...*Column) *Table {
	return t.AddConstraint(Check(t, check, columns...))
}

// AddUnique ...
func (t *Table) AddUnique(columns ...*Column) *Table {
	return t.AddConstraint(Unique(t, columns...))
}

// SetIfNotExists ...
func (t *Table) SetIfNotExists(ine bool) *Table {
	t.IfNotExists = ine
	return t
}

// SetSchema ...
func (t *Table) SetSchema(s *Schema) *Table {
	if t.Schema == nil {
		t.Schema = s
	} else {
		*t.Schema = *s
	}

	return t
}

// PrimaryKey returns column that is primary key, or false if none.
func (t *Table) PrimaryKey() (*Column, bool) {
	for _, c := range t.Columns {
		if c.PrimaryKey {
			return c, true
		}
	}

	return nil, false
}

// Extend ...
func (t *Table) Extend(fn ExtendFunc) {
	fn(t)
}

// WithIfNotExists ...
func WithIfNotExists() func(*Table) {
	return func(t *Table) {
		t.IfNotExists = true
	}
}

// WithTemporary ...
func WithTemporary() func(*Table) {
	return func(t *Table) {
		t.Temporary = true
	}
}

// WithTableSpace ...
func WithTableSpace(s string) func(*Table) {
	return func(t *Table) {
		t.TableSpace = s
	}
}

func fkType(t Type) Type {
	switch t {
	case TypeSerial():
		return TypeInteger()
	case TypeSerialBig():
		return TypeIntegerBig()
	case TypeSerialSmall():
		return TypeIntegerSmall()
	default:
		return t
	}
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	_ "github.com/lib/pq"
)

func UserTables(db *sql.DB) ([]string, error) {
	sql := "SELECT relname FROM pg_catalog.pg_stat_user_tables"

	rows, err := db.Query(sql)
	if err != nil {
		return nil, err
	}

	defer rows.Close()

	var tables []string
	for rows.Next() {
		var table string
		if err := rows.Scan(&table); err != nil {
			return nil, err
		}

		tables = append(tables, table)
	}

	return tables, nil
}

func TableDiskSize(db *sql.DB, tableName string) (float64, error) {
	sql := "select pg_table_size($1);"

	var size float64
	if err := db.QueryRow(sql, tableName).Scan(&size); err != nil {
		return 0, err
	}

	return size / 1000, nil
}

func TableIndexesDiskSize(db *sql.DB, tableName string) (float64, error) {
	sql := "select pg_indexes_size($1);"

	var size float64
	if err := db.QueryRow(sql, tableName).Scan(&size); err != nil {
		return 0, err
	}

	return size, nil
}
<commit_msg>Fix units in table sizes<commit_after>package main

import (
	"database/sql"
	_ "github.com/lib/pq"
)

func UserTables(db *sql.DB) ([]string, error) {
	sql := "SELECT relname FROM pg_catalog.pg_stat_user_tables"

	rows, err := db.Query(sql)
	if err != nil {
		return nil, err
	}

	defer rows.Close()

	var tables []string
	for rows.Next() {
		var table string
		if err := rows.Scan(&table); err != nil {
			return nil, err
		}

		tables = append(tables, table)
	}

	return tables, nil
}

func TableDiskSize(db *sql.DB, tableName string) (float64, error) {
	sql := "select pg_table_size($1);"

	var size float64
	if err := db.QueryRow(sql, tableName).Scan(&size); err != nil {
		return 0, err
	}

	return size, nil
}

func TableIndexesDiskSize(db *sql.DB, tableName string) (float64, error) {
	sql := "select pg_indexes_size($1);"

	var size float64
	if err := db.QueryRow(sql, tableName).Scan(&size); err != nil {
		return 0, err
	}

	return size, nil
}
<|endoftext|>


<commit_before>package bitbucket

import (
	"encoding/json"
	"fmt"
	"net/http"
)

type TeamService struct{}

type Team struct {
	Username    string `json:"username"`
	Kind        string `json:"kind"`
	Website     string `json:"website"`
	DisplayName string `json:"display_name"`
	Links       Links  `json:"links"`
	CreatedOn   string `json:"created_on"`
	Location    string `json:"location"`
}

func (t TeamService) GetTeamProfile(team string) (*Team, error) {
	req, err := http.NewRequest("GET",
		fmt.Sprintf("%s/teams/%s", V2_URL, team),
		nil)
	if err != nil {
		return nil, err
	}

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	var data Team
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}

	return &data, nil
}
<commit_msg>Add get team members<commit_after>package bitbucket

import (
	"encoding/json"
	"fmt"
	"net/http"
)

type TeamService struct{}

type Team struct {
	Username    string `json:"username"`
	Kind        string `json:"kind"`
	Website     string `json:"website"`
	DisplayName string `json:"display_name"`
	Links       Links  `json:"links"`
	CreatedOn   string `json:"created_on"`
	Location    string `json:"location"`
}

func (t TeamService) GetTeamProfile(team string) (*Team, error) {
	req, err := http.NewRequest("GET",
		fmt.Sprintf("%s/teams/%s", V2_URL, team),
		nil)
	if err != nil {
		return nil, err
	}

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	var data Team
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}

	return &data, nil
}

type TeamMembers struct {
	PageLen int          `json:"pagelen"`
	Values  []TeamMember `json:"values"`
	Page    int          `json:"page"`
	Size    int          `json:"size"`
}

// TODO(ttacon): combine with owner/user?

type TeamMember struct {
	Username    string `json:"username"`
	Kind        string `json:"kind"`
	Website     string `json:"website"`
	DisplayName string `json:"display_name"`
	Links       Links  `json:"links"`
	CreatedOn   string `json:"created_on"`
	Location    string `json:"location"`
}

func (t TeamService) GetTeamMembers(team string) (*TeamMembers, error) {
	req, err := http.NewRequest("GET",
		fmt.Sprintf("%s/teams/%s/members", V2_URL, team),
		nil)
	if err != nil {
		return nil, err
	}

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	var data TeamMembers
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}

	return &data, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"

	"github.com/99designs/aws-vault/keyring"
	"github.com/99designs/aws-vault/prompt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/skratchdot/open-golang/open"
	"gopkg.in/alecthomas/kingpin.v2"
)

const allowAllIAMPolicy = `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}`

type LoginCommandInput struct {
	Profile                 string
	Keyring                 keyring.Keyring
	MfaToken                string
	MfaPrompt               prompt.PromptFunc
	UseStdout               bool
	FederationTokenDuration time.Duration
	AssumeRoleDuration      time.Duration
}

func LoginCommand(app *kingpin.Application, input LoginCommandInput) {
	if input.FederationTokenDuration > (time.Hour * 12) {
		app.Fatalf("Maximum federation token duration is 12 hours")
		return
	}

	profiles, err := awsConfigFile.Parse()
	if err != nil {
		app.Fatalf("Error parsing config: %v", err)
		return
	}

	provider, err := NewVaultProvider(input.Keyring, input.Profile, VaultOptions{
		AssumeRoleDuration: input.AssumeRoleDuration,
		MfaToken:           input.MfaToken,
		MfaPrompt:          input.MfaPrompt,
		NoSession:          true,
		Profiles:           profiles,
	})
	if err != nil {
		app.Fatalf("Failed to create vault provider: %v", err)
		return
	}

	creds := credentials.NewCredentials(provider)
	val, err := creds.Get()
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
			app.Fatalf("No credentials found for profile %q", input.Profile)
			return
		}
		app.Fatalf("Failed to get credentials: %v", err)
	}

	var isFederated bool
	var sessionDuration = input.FederationTokenDuration

	// if AssumeRole isn't used, GetFederationToken has to be used for IAM credentials
	if val.SessionToken == "" {
		log.Printf("No session token found, calling GetFederationToken")
		stsCreds, err := getFederationToken(val, input.FederationTokenDuration)
		if err != nil {
			app.Fatalf("Failed to call GetFederationToken: %v", err)
			return
		}

		val.AccessKeyID = *stsCreds.AccessKeyId
		val.SecretAccessKey = *stsCreds.SecretAccessKey
		val.SessionToken = *stsCreds.SessionToken
		isFederated = true
	}

	jsonBytes, err := json.Marshal(map[string]string{
		"sessionId":    val.AccessKeyID,
		"sessionKey":   val.SecretAccessKey,
		"sessionToken": val.SessionToken,
	})
	if err != nil {
		app.Fatalf("%v", err)
		return
	}

	req, err := http.NewRequest("GET", "https://signin.aws.amazon.com/federation", nil)
	if err != nil {
		app.Fatalf("%v", err)
		return
	}

	log.Printf("Creating login token, expires in %s", sessionDuration)

	q := req.URL.Query()
	q.Add("Action", "getSigninToken")
	q.Add("Session", string(jsonBytes))

	// not needed for federation tokens
	if !isFederated {
		q.Add("SessionDuration", fmt.Sprintf("%.f", sessionDuration.Seconds()))
	}

	req.URL.RawQuery = q.Encode()

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		app.Fatalf("Failed to create federated token: %v", err)
		return
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		app.Fatalf("%v", err)
		return
	}

	if resp.StatusCode != http.StatusOK {
		log.Printf("Response body was %s", body)
		app.Fatalf("Call to getSigninToken failed with %v", resp.Status)
		return
	}

	var respParsed map[string]string

	if err = json.Unmarshal([]byte(body), &respParsed); err != nil {
		app.Fatalf("Failed to parse response from getSigninToken: %v", err)
		return
	}

	signinToken, ok := respParsed["SigninToken"]
	if !ok {
		app.Fatalf("Expected a response with SigninToken")
		return
	}

	destination := "https://console.aws.amazon.com/"
	if region, ok := profiles[input.Profile]["region"]; ok {
		destination = fmt.Sprintf(
			"https://%s.console.aws.amazon.com/console/home?region=%s",
			region, region,
		)
	}

	loginUrl := fmt.Sprintf(
		"https://signin.aws.amazon.com/federation?Action=login&Issuer=aws-vault&Destination=%s&SigninToken=%s",
		url.QueryEscape(destination),
		url.QueryEscape(signinToken),
	)

	if input.UseStdout {
		fmt.Println(loginUrl)
	} else if err = open.Run(loginUrl); err != nil {
		log.Println(err)
		fmt.Println(loginUrl)
	}
}

func getFederationToken(creds credentials.Value, d time.Duration) (*sts.Credentials, error) {
	client := sts.New(session.New(&aws.Config{
		Credentials: credentials.NewCredentials(&credentials.StaticProvider{Value: creds}),
	}))

	params := &sts.GetFederationTokenInput{
		Name:            aws.String("federated-user"),
		DurationSeconds: aws.Int64(int64(d.Seconds())),
		Policy:          aws.String(allowAllIAMPolicy),
	}

	if username, _ := getUserName(creds); username != "" {
		params.Name = aws.String(username)
	}

	resp, err := client.GetFederationToken(params)
	if err != nil {
		return nil, err
	}

	return resp.Credentials, nil
}

func getUserName(creds credentials.Value) (string, error) {
	client := iam.New(session.New(&aws.Config{
		Credentials: credentials.NewCredentials(&credentials.StaticProvider{Value: creds}),
	}))

	resp, err := client.GetUser(&iam.GetUserInput{})
	if err != nil {
		return "", err
	}

	return *resp.User.UserName, nil
}
<commit_msg>Add a better error message when there are no creds<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"

	"github.com/99designs/aws-vault/keyring"
	"github.com/99designs/aws-vault/prompt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/skratchdot/open-golang/open"
	"gopkg.in/alecthomas/kingpin.v2"
)

const allowAllIAMPolicy = `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}`

type LoginCommandInput struct {
	Profile                 string
	Keyring                 keyring.Keyring
	MfaToken                string
	MfaPrompt               prompt.PromptFunc
	UseStdout               bool
	FederationTokenDuration time.Duration
	AssumeRoleDuration      time.Duration
}

func LoginCommand(app *kingpin.Application, input LoginCommandInput) {
	if input.FederationTokenDuration > (time.Hour * 12) {
		app.Fatalf("Maximum federation token duration is 12 hours")
		return
	}

	profiles, err := awsConfigFile.Parse()
	if err != nil {
		app.Fatalf("Error parsing config: %v", err)
		return
	}

	provider, err := NewVaultProvider(input.Keyring, input.Profile, VaultOptions{
		AssumeRoleDuration: input.AssumeRoleDuration,
		MfaToken:           input.MfaToken,
		MfaPrompt:          input.MfaPrompt,
		NoSession:          true,
		Profiles:           profiles,
	})
	if err != nil {
		app.Fatalf("Failed to create vault provider: %v", err)
		return
	}

	creds := credentials.NewCredentials(provider)
	val, err := creds.Get()
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
			app.Fatalf("No credentials found for profile %q.\nUse `aws-vault add %s` to set up credentials", input.Profile, input.Profile)
			return
		}
		app.Fatalf("Failed to get credentials: %v", err)
	}

	var isFederated bool
	var sessionDuration = input.FederationTokenDuration

	// if AssumeRole isn't used, GetFederationToken has to be used for IAM credentials
	if val.SessionToken == "" {
		log.Printf("No session token found, calling GetFederationToken")
		stsCreds, err := getFederationToken(val, input.FederationTokenDuration)
		if err != nil {
			app.Fatalf("Failed to call GetFederationToken: %v", err)
			return
		}

		val.AccessKeyID = *stsCreds.AccessKeyId
		val.SecretAccessKey = *stsCreds.SecretAccessKey
		val.SessionToken = *stsCreds.SessionToken
		isFederated = true
	}

	jsonBytes, err := json.Marshal(map[string]string{
		"sessionId":    val.AccessKeyID,
		"sessionKey":   val.SecretAccessKey,
		"sessionToken": val.SessionToken,
	})
	if err != nil {
		app.Fatalf("%v", err)
		return
	}

	req, err := http.NewRequest("GET", "https://signin.aws.amazon.com/federation", nil)
	if err != nil {
		app.Fatalf("%v", err)
		return
	}

	log.Printf("Creating login token, expires in %s", sessionDuration)

	q := req.URL.Query()
	q.Add("Action", "getSigninToken")
	q.Add("Session", string(jsonBytes))

	// not needed for federation tokens
	if !isFederated {
		q.Add("SessionDuration", fmt.Sprintf("%.f", sessionDuration.Seconds()))
	}

	req.URL.RawQuery = q.Encode()

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		app.Fatalf("Failed to create federated token: %v", err)
		return
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		app.Fatalf("%v", err)
		return
	}

	if resp.StatusCode != http.StatusOK {
		log.Printf("Response body was %s", body)
		app.Fatalf("Call to getSigninToken failed with %v", resp.Status)
		return
	}

	var respParsed map[string]string

	if err = json.Unmarshal([]byte(body), &respParsed); err != nil {
		app.Fatalf("Failed to parse response from getSigninToken: %v", err)
		return
	}

	signinToken, ok := respParsed["SigninToken"]
	if !ok {
		app.Fatalf("Expected a response with SigninToken")
		return
	}

	destination := "https://console.aws.amazon.com/"
	if region, ok := profiles[input.Profile]["region"]; ok {
		destination = fmt.Sprintf(
			"https://%s.console.aws.amazon.com/console/home?region=%s",
			region, region,
		)
	}

	loginUrl := fmt.Sprintf(
		"https://signin.aws.amazon.com/federation?Action=login&Issuer=aws-vault&Destination=%s&SigninToken=%s",
		url.QueryEscape(destination),
		url.QueryEscape(signinToken),
	)

	if input.UseStdout {
		fmt.Println(loginUrl)
	} else if err = open.Run(loginUrl); err != nil {
		log.Println(err)
		fmt.Println(loginUrl)
	}
}

func getFederationToken(creds credentials.Value, d time.Duration) (*sts.Credentials, error) {
	client := sts.New(session.New(&aws.Config{
		Credentials: credentials.NewCredentials(&credentials.StaticProvider{Value: creds}),
	}))

	params := &sts.GetFederationTokenInput{
		Name:            aws.String("federated-user"),
		DurationSeconds: aws.Int64(int64(d.Seconds())),
		Policy:          aws.String(allowAllIAMPolicy),
	}

	if username, _ := getUserName(creds); username != "" {
		params.Name = aws.String(username)
	}

	resp, err := client.GetFederationToken(params)
	if err != nil {
		return nil, err
	}

	return resp.Credentials, nil
}

func getUserName(creds credentials.Value) (string, error) {
	client := iam.New(session.New(&aws.Config{
		Credentials: credentials.NewCredentials(&credentials.StaticProvider{Value: creds}),
	}))

	resp, err := client.GetUser(&iam.GetUserInput{})
	if err != nil {
		return "", err
	}

	return *resp.User.UserName, nil
}
<|endoftext|>


<commit_before>package constants

const (
	DefaultKeymasterAdminPortNumber   = 6920
	DefaultKeymasterServicePortNumber = 443

	DefaultCertmonPortNumber          = 6921
	DefaultKeymasterCertmonConfigFile = "/etc/keymaster-certmond/config.yml"
)
<commit_msg>Add lib/constants.DefaultKeymasterCertmonStateDir.<commit_after>package constants

const (
	DefaultKeymasterAdminPortNumber   = 6920
	DefaultKeymasterServicePortNumber = 443

	DefaultCertmonPortNumber          = 6921
	DefaultKeymasterCertmonConfigFile = "/etc/keymaster-certmond/config.yml"
	DefaultKeymasterCertmonStateDir   = "/var/lib/keymaster-certmond"
)
<|endoftext|>


<commit_before>//  This Source Code Form is subject to the terms of the Mozilla Public
//  License, v. 2.0. If a copy of the MPL was not distributed with this
//  file, You can obtain one at http://mozilla.org/MPL/2.0/.

package main

import (
	"testing"
)

func UnitVectors() (eX, eY, eZ Vector) {

	eX = NewVector(1, 0, 0)
	eY = NewVector(0, 1, 0)
	eZ = NewVector(0, 0, 1)

	return
}

func TestDotProduct(t *testing.T) {

	eX, eY, _ := UnitVectors()

	if eX.Dot(eY) != 0 {

		t.Fatalf(
			"%v.Dot(%v) should be 0 not %f",
			eX, eY, eX.Dot(eY),
		)
	}

	if eX.Dot(eX) != 1 {

		t.Fatalf(
			"%v.Dot(%v) should be 1 not %f",
			eX, eX, eX.Dot(eX),
		)
	}
}
<commit_msg>Add dacart.TestCrossProduct<commit_after>//  This Source Code Form is subject to the terms of the Mozilla Public
//  License, v. 2.0. If a copy of the MPL was not distributed with this
//  file, You can obtain one at http://mozilla.org/MPL/2.0/.

package main

import (
	"testing"
)

func UnitVectors() (eX, eY, eZ Vector) {

	eX = NewVector(1, 0, 0)
	eY = NewVector(0, 1, 0)
	eZ = NewVector(0, 0, 1)

	return
}

func TestDotProduct(t *testing.T) {

	eX, eY, _ := UnitVectors()

	if eX.Dot(eY) != 0 {

		t.Fatalf(
			"%v.Dot(%v) should be 0 not %f",
			eX, eY, eX.Dot(eY),
		)
	}

	if eX.Dot(eX) != 1 {

		t.Fatalf(
			"%v.Dot(%v) should be 1 not %f",
			eX, eX, eX.Dot(eX),
		)
	}
}

func aCrossBGivesC(a, b, c Vector, t *testing.T) {

	if a.Cross(b).Dot(c) != 1 {

		t.Fatalf(
			"%v.Cross(%v).Dot(%v) should be 1 not %f",
			a, b, c, a.Cross(b).Dot(c),
		)
	}
}

func TestCrossProduct(t *testing.T) {

	eX, eY, eZ := UnitVectors()

	aCrossBGivesC(eX, eY, eZ, t)
	aCrossBGivesC(eZ, eX, eY, t)
	aCrossBGivesC(eY, eZ, eX, t)

	aCrossBGivesC(eY, eX, eZ.Negate(), t)
	aCrossBGivesC(eX, eZ, eY.Negate(), t)
	aCrossBGivesC(eZ, eY, eX.Negate(), t)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Eric Barkie. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package weatherlink

import "time"

// getConsTime reads the console time and returns it as a time.
func (w *Weatherlink) getConsTime() (t time.Time, err error) {
	var p Packet
	p, err = w.sendCommand([]byte("GETTIME\n"), 8)
	if err != nil {
		return
	}

	ct := ConsTime{}
	err = ct.FromPacket(p)
	if err != nil {
		return
	}

	t = time.Time(ct)
	return
}

// setConsTime sets the console time to the specified time.
func (w *Weatherlink) setConsTime(t time.Time) (err error) {
	_, err = w.sendCommand([]byte("SETTIME\n"), 0)
	if err != nil {
		return
	}
	_, err = w.sendCommand(ConsTime(t).ToPacket(), 0)

	return
}

// syncConsTime uses getConsTime and compares it to the system time.
// If it exceeds the offset threshold then setConsTime is called to
// get them in sync.
func (w *Weatherlink) syncConsTime() (err error) {
	const maxOffset = 10 * time.Second

	var t time.Time
	t, err = w.getConsTime()
	if err != nil {
		return
	}
	offset := time.Since(t)
	if offset < 0 {
		offset *= -1
	}
	Debug.Printf("Console time is %s, offset is %s", t, offset)

	if offset > maxOffset {
		Info.Printf("Console time is off by %s, syncing", offset)
		err = w.setConsTime(time.Now())
		if err != nil {
			Error.Println(err.Error())
		}
	}

	return
}
<commit_msg>Be consistent with variable init<commit_after>// Copyright (c) 2016-2017 Eric Barkie. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package weatherlink

import "time"

// getConsTime reads the console time and returns it as a time.
func (w *Weatherlink) getConsTime() (t time.Time, err error) {
	var p Packet
	p, err = w.sendCommand([]byte("GETTIME\n"), 8)
	if err != nil {
		return
	}

	var ct ConsTime
	err = ct.FromPacket(p)
	if err != nil {
		return
	}

	t = time.Time(ct)
	return
}

// setConsTime sets the console time to the specified time.
func (w *Weatherlink) setConsTime(t time.Time) (err error) {
	_, err = w.sendCommand([]byte("SETTIME\n"), 0)
	if err != nil {
		return
	}
	_, err = w.sendCommand(ConsTime(t).ToPacket(), 0)

	return
}

// syncConsTime uses getConsTime and compares it to the system time.
// If it exceeds the offset threshold then setConsTime is called to
// get them in sync.
func (w *Weatherlink) syncConsTime() (err error) {
	const maxOffset = 10 * time.Second

	var t time.Time
	t, err = w.getConsTime()
	if err != nil {
		return
	}
	offset := time.Since(t)
	if offset < 0 {
		offset *= -1
	}
	Debug.Printf("Console time is %s, offset is %s", t, offset)

	if offset > maxOffset {
		Info.Printf("Console time is off by %s, syncing", offset)
		err = w.setConsTime(time.Now())
		if err != nil {
			Error.Println(err.Error())
		}
	}

	return
}
<|endoftext|>


<commit_before>package proxy

import (
	"context"
	"log"
	"strings"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"github.com/pkg/errors"
)

const runningState = "running"

type dockerContainerInfo struct {
	containerInfo
	RefreshTime time.Time
}

type dockerContainerService struct {
	containerIPMap map[string]dockerContainerInfo
	aliasToARN     map[string]string
	docker         *client.Client
	log            *log.Logger
}

func newDockerContainerService(endpoint string, aliasToARN map[string]string, logger *log.Logger) (*dockerContainerService, error) {
	c, err := client.NewEnvClient()
	if err != nil {
		return nil, errors.Wrapf(err, "Error creating docker client with endpoint [%s]", endpoint)
	}
	return &dockerContainerService{
		aliasToARN:     aliasToARN,
		containerIPMap: make(map[string]dockerContainerInfo),
		docker:         c,
		log:            logger,
	}, nil
}

// TypeName implements a containerService method.
func (d *dockerContainerService) TypeName() string {
	return "docker"
}

// ContainerForIP implements a containerService method.
//
// If containerInfo exists in the cache, keyed by the container IP, then it is returned.
// Otherwise syncContainer is used to collect fresh containerInfo from the docker API.
func (d *dockerContainerService) ContainerForIP(ctx context.Context, containerIP string) (containerInfo, error) {
	info, found := d.containerIPMap[containerIP]
	now := time.Now()

	if !found {
		d.syncContainers(ctx, now)
		info, found = d.containerIPMap[containerIP]
	} else if now.After(info.RefreshTime) {
		info, found = d.syncContainer(ctx, containerIP, info, now)
	}

	if !found {
		return containerInfo{}, errors.Errorf("No container found for IP %s", containerIP)
	}

	return info.containerInfo, nil
}

func (d *dockerContainerService) syncContainer(ctx context.Context, containerIP string, oldInfo dockerContainerInfo, now time.Time) (dockerContainerInfo, bool) {
	reqID := requestIDFromContext(ctx)

	container, err := d.docker.ContainerInspect(ctx, oldInfo.ID)

	if err != nil || container.State.Status != runningState {
		if client.IsErrContainerNotFound(err) {
			d.log.Printf("syncContainer (%s): container not found, refreshing container info [%s]", reqID, oldInfo.ID)
		} else {
			d.log.Printf("syncContainer (%s): Error inspecting container, refreshing container info [%s]: %+v", reqID, oldInfo.ID, err)
		}

		d.syncContainers(ctx, now)
		info, found := d.containerIPMap[containerIP]
		return info, found
	}

	oldInfo.RefreshTime = refreshTime(now)
	d.containerIPMap[containerIP] = oldInfo
	return oldInfo, true
}

func (d *dockerContainerService) syncContainers(ctx context.Context, now time.Time) {
	reqID := requestIDFromContext(ctx)

	apiContainers, err := d.docker.ContainerList(ctx, types.ContainerListOptions{})
	if err != nil {
		d.log.Printf("syncContainers (%s): Error listing running containers: %+v", reqID, err)
		return
	}

	refreshAt := refreshTime(now)
	containerIPMap := make(map[string]dockerContainerInfo)

	for _, container := range apiContainers {
		if container.State != runningState {
			continue
		}
		alias, ok := container.Labels[RoleLabelKey]
		if !ok {
			continue
		}

		var containerIPs []string
		for _, net := range container.NetworkSettings.Networks {
			if net.IPAddress != "" {
				containerIPs = append(containerIPs, net.IPAddress)
			}
		}

		if len(containerIPs) == 0 {
			d.log.Printf("syncContainers (%s): no IP addresses discovered for container [%s]", reqID, container.ID)
			continue
		}

		roleName, ok := d.aliasToARN[alias]
		if !ok {
			d.log.Printf("syncContainers (%s): container [%s] %v has an unmapped role alias [%s]", reqID, container.ID, container.Names, alias)
			continue
		}
		role, roleErr := newRoleArn(roleName)
		if roleErr != nil {
			d.log.Printf("syncContainers (%s): Error creating new role ARN with invalid name [%s]: %+v", reqID, role, roleErr)
			continue
		}

		for _, ipAddress := range containerIPs {
			d.log.Printf("syncContainers (%s): id [%s] ip [%s] image [%s] role [%s]", reqID, container.ID[:6], ipAddress, container.Image, role)

			containerIPMap[ipAddress] = dockerContainerInfo{
				containerInfo: containerInfo{
					ID:        container.ID,
					Name:      strings.Join(container.Names, ","),
					IamRole:   role,
					IamPolicy: container.Labels[PolicyLabelKey],
				},
				RefreshTime: refreshAt,
			}
		}
	}

	d.containerIPMap = containerIPMap
}

func refreshTime(now time.Time) time.Time {
	return now.Add(1 * time.Second)
}
<commit_msg>fix: DOCKER_HOST not overriden by config file<commit_after>package proxy

import (
	"context"
	"log"
	"os"
	"strings"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"github.com/pkg/errors"
)

const runningState = "running"

type dockerContainerInfo struct {
	containerInfo
	RefreshTime time.Time
}

type dockerContainerService struct {
	containerIPMap map[string]dockerContainerInfo
	aliasToARN     map[string]string
	docker         *client.Client
	log            *log.Logger
}

func newDockerContainerService(endpoint string, aliasToARN map[string]string, logger *log.Logger) (*dockerContainerService, error) {
	os.Setenv("DOCKER_HOST", endpoint)

	c, err := client.NewEnvClient()
	if err != nil {
		return nil, errors.Wrapf(err, "Error creating docker client with endpoint [%s]", endpoint)
	}
	return &dockerContainerService{
		aliasToARN:     aliasToARN,
		containerIPMap: make(map[string]dockerContainerInfo),
		docker:         c,
		log:            logger,
	}, nil
}

// TypeName implements a containerService method.
func (d *dockerContainerService) TypeName() string {
	return "docker"
}

// ContainerForIP implements a containerService method.
//
// If containerInfo exists in the cache, keyed by the container IP, then it is returned.
// Otherwise syncContainer is used to collect fresh containerInfo from the docker API.
func (d *dockerContainerService) ContainerForIP(ctx context.Context, containerIP string) (containerInfo, error) {
	info, found := d.containerIPMap[containerIP]
	now := time.Now()

	if !found {
		d.syncContainers(ctx, now)
		info, found = d.containerIPMap[containerIP]
	} else if now.After(info.RefreshTime) {
		info, found = d.syncContainer(ctx, containerIP, info, now)
	}

	if !found {
		return containerInfo{}, errors.Errorf("No container found for IP %s", containerIP)
	}

	return info.containerInfo, nil
}

func (d *dockerContainerService) syncContainer(ctx context.Context, containerIP string, oldInfo dockerContainerInfo, now time.Time) (dockerContainerInfo, bool) {
	reqID := requestIDFromContext(ctx)

	container, err := d.docker.ContainerInspect(ctx, oldInfo.ID)

	if err != nil || container.State.Status != runningState {
		if client.IsErrContainerNotFound(err) {
			d.log.Printf("syncContainer (%s): container not found, refreshing container info [%s]", reqID, oldInfo.ID)
		} else {
			d.log.Printf("syncContainer (%s): Error inspecting container, refreshing container info [%s]: %+v", reqID, oldInfo.ID, err)
		}

		d.syncContainers(ctx, now)
		info, found := d.containerIPMap[containerIP]
		return info, found
	}

	oldInfo.RefreshTime = refreshTime(now)
	d.containerIPMap[containerIP] = oldInfo
	return oldInfo, true
}

func (d *dockerContainerService) syncContainers(ctx context.Context, now time.Time) {
	reqID := requestIDFromContext(ctx)

	apiContainers, err := d.docker.ContainerList(ctx, types.ContainerListOptions{})
	if err != nil {
		d.log.Printf("syncContainers (%s): Error listing running containers: %+v", reqID, err)
		return
	}

	refreshAt := refreshTime(now)
	containerIPMap := make(map[string]dockerContainerInfo)

	for _, container := range apiContainers {
		if container.State != runningState {
			continue
		}
		alias, ok := container.Labels[RoleLabelKey]
		if !ok {
			continue
		}

		var containerIPs []string
		for _, net := range container.NetworkSettings.Networks {
			if net.IPAddress != "" {
				containerIPs = append(containerIPs, net.IPAddress)
			}
		}

		if len(containerIPs) == 0 {
			d.log.Printf("syncContainers (%s): no IP addresses discovered for container [%s]", reqID, container.ID)
			continue
		}

		roleName, ok := d.aliasToARN[alias]
		if !ok {
			d.log.Printf("syncContainers (%s): container [%s] %v has an unmapped role alias [%s]", reqID, container.ID, container.Names, alias)
			continue
		}
		role, roleErr := newRoleArn(roleName)
		if roleErr != nil {
			d.log.Printf("syncContainers (%s): Error creating new role ARN with invalid name [%s]: %+v", reqID, role, roleErr)
			continue
		}

		for _, ipAddress := range containerIPs {
			d.log.Printf("syncContainers (%s): id [%s] ip [%s] image [%s] role [%s]", reqID, container.ID[:6], ipAddress, container.Image, role)

			containerIPMap[ipAddress] = dockerContainerInfo{
				containerInfo: containerInfo{
					ID:        container.ID,
					Name:      strings.Join(container.Names, ","),
					IamRole:   role,
					IamPolicy: container.Labels[PolicyLabelKey],
				},
				RefreshTime: refreshAt,
			}
		}
	}

	d.containerIPMap = containerIPMap
}

func refreshTime(now time.Time) time.Time {
	return now.Add(1 * time.Second)
}
<|endoftext|>


<commit_before>package dbase

import (
	"database/sql"
	"strings"

	"github.com/devinmcgloin/morph/src/env"
	"github.com/devinmcgloin/morph/src/schema"

	_ "github.com/go-sql-driver/mysql" // Drives we have to import to use database/sql

	"log"
)

var db *sql.DB

// SetDB returns a reference to a sql.DB object. It's best to keep these long lived.
func SetDB() error {
	log.Printf("DB_URL = %s", env.Getenv("DB_URL", "root:@/morph"))

	var err error
	// Create the database handle, confirm driver is
	db, err = sql.Open("mysql", env.Getenv("DB_URL", "root:@/morph")+"?parseTime=true")
	if err != nil {
		log.Print(err)
		return err
	}
	err = db.Ping()
	if err != nil {
		log.Fatal("Error connecting to db, ping failed.")
	}
	return nil
}

func GetImg(iID string) (schema.Img, error) {

	var img schema.Img

	err := db.QueryRow("select * from images where i_id = ?", iID).Scan(
		&img.IID, &img.Title, &img.Desc, &img.URL, &img.FStop,
		&img.ISO, &img.FOV, &img.ShutterSpeed, &img.Category, &img.PublishDate,
	)
	if err != nil {
		return schema.Img{}, err
	}

	log.Printf("Image: %v", img)

	return img, nil
}

func AddImg(img schema.Img) error {

	stmt, err := db.Prepare("INSERT INTO images(" + generateQuestionMarks(10) +
		") VALUES(" + generateQuestionMarks(10) + ")")
	if err != nil {
		log.Fatal(err)
		return err
	}

	res, err := stmt.Exec("i_id", "i_title", "i_desc", "i_url", "i_fstop",
		"i_iso", "i_fov", "i_shutter_speed", "i_category", "i_publish_date",
		img.IID, img.Title, img.Desc, img.URL, img.FStop, img.ISO, img.FOV,
		img.ShutterSpeed, img.Category, img.PublishDate)

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

	log.Println(res)

	return nil

}

func GetCategory(collectionTag string) (schema.ImgCollection, error) {
	var collectionPage schema.ImgCollection

	var images []schema.Img

	rows, err := db.Query("SELECT "+generateQuestionMarks(10)+" FROM images WHERE i_category = ?", args)

	collectionPage.Images = images
	collectionPage.NumImg = len(collectionPage.Images)
	collectionPage.Title = "Morph"
	return collectionPage, nil
}

func GetAllImgs() (schema.ImgCollection, error) {
	var collectionPage schema.ImgCollection
	//
	// var images []schema.Img
	//
	// sql := "SELECT i_id, i_title, i_desc, i_url, i_category, i_fstop, i_shutter_speed, i_fov, i_iso, i_publish_date name FROM images ORDER BY i_publish_date DESC"
	//
	// q := db.NewQuery(sql)
	// err := q.All(&images)
	// if err != nil {
	// 	return schema.ImgCollection{}, err
	// }
	//
	// collectionPage.Images = images
	// collectionPage.NumImg = len(collectionPage.Images)
	// collectionPage.Title = "Morph"
	return collectionPage, nil
}

func generateQuestionMarks(num int) string {
	var qs []string
	for i := 0; i < num; i++ {
		qs = append(qs, "?")
	}
	return strings.Join(qs, ", ")
}

func getAllDBFields(schema struct) string {

}
<commit_msg>should have used sqlx from the start<commit_after>package dbase

import (
	"strings"

	"github.com/devinmcgloin/morph/src/env"
	"github.com/devinmcgloin/morph/src/schema"
	"github.com/jmoiron/sqlx"

	_ "github.com/go-sql-driver/mysql" // Drives we have to import to use database/sql

	"log"
)

var db *sqlx.DB

// SetDB returns a reference to a sql.DB object. It's best to keep these long lived.
func SetDB() error {
	log.Printf("DB_URL = %s", env.Getenv("DB_URL", "root:@/morph"))

	var err error
	// Create the database handle, confirm driver is
	db, err = sqlx.Connect("mysql", env.Getenv("DB_URL", "root:@/morph")+"?parseTime=true")
	if err != nil {
		log.Print(err)
		return err
	}
	return nil
}

func GetImg(iID string) (schema.Img, error) {

	var img schema.Img

	err := db.Get(&img, "SELECT * FROM images WHERE i_id = ?", iID)
	if err != nil {
		return schema.Img{}, nil
	}

	log.Printf("Image: %v", img)

	return img, nil
}

func AddImg(img schema.Img) error {

	_, err := db.NamedExec(`
		INSERT INTO images (i_id, i_title, i_desc, i_url, i_category, i_fstop, i_shutter_speed, i_fov, i_iso, i_publish_date)
			VALUES (:id, :title, :desc, :url, :category, :fstop, :shutter, :fov, :iso, :publish_date)`,
		map[string]interface{}{
			"id":           img.IID,
			"title":        img.Title,
			"desc":         img.Desc,
			"url":          img.URL,
			"category":     img.Category,
			"fstop":        img.FStop,
			"shutter":      img.ShutterSpeed,
			"fov":          img.FOV,
			"iso":          img.ISO,
			"publish_date": img.PublishDate,
		})
	if err != nil {
		return err
	}
	return nil

}

func GetCategory(collectionTag string) (schema.ImgCollection, error) {
	var collectionPage schema.ImgCollection

	var images []schema.Img

	err := db.Select(&images, "SELECT * FROM images WHERE i_category = ?", collectionTag)
	if err != nil {
		return schema.ImgCollection{}, err
	}

	collectionPage.Images = images
	collectionPage.NumImg = len(collectionPage.Images)
	collectionPage.Title = collectionTag
	return collectionPage, nil
}

func GetAllImgs() (schema.ImgCollection, error) {
	var collectionPage schema.ImgCollection

	var images []schema.Img

	err := db.Select(&images, "SELECT * FROM images ORDER BY i_publish_date DESC")
	if err != nil {
		return schema.ImgCollection{}, err
	}

	collectionPage.Images = images
	collectionPage.NumImg = len(collectionPage.Images)
	collectionPage.Title = "Morph"
	return collectionPage, nil
}

func generateQuestionMarks(num int) string {
	var qs []string
	for i := 0; i < num; i++ {
		qs = append(qs, "?")
	}
	return strings.Join(qs, ", ")
}
<|endoftext|>


<commit_before>package providers

import (
	"fmt"
	"os"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/route53"
	"github.com/juju/ratelimit"
	"github.com/rancher/external-dns/dns"
)

var route53MaxRetries int = 3

type Route53Handler struct {
	client       *route53.Route53
	hostedZoneId string
	limiter      *ratelimit.Bucket
}

func init() {
	var region, accessKey, secretKey string
	if region = os.Getenv("AWS_REGION"); len(region) == 0 {
		logrus.Info("AWS_REGION is not set, skipping init of Route 53 provider")
		return
	}

	if accessKey = os.Getenv("AWS_ACCESS_KEY"); len(accessKey) == 0 {
		logrus.Info("AWS_ACCESS_KEY is not set, skipping init of Route 53 provider")
		return
	}

	if secretKey = os.Getenv("AWS_SECRET_KEY"); len(secretKey) == 0 {
		logrus.Info("AWS_SECRET_KEY is not set, skipping init of Route 53 provider")
		return
	}

	handler := &Route53Handler{}
	if err := RegisterProvider("route53", handler); err != nil {
		logrus.Fatal("Could not register route53 provider")
	}

	// Comply with the API's 5 req/s rate limit. If there are other
	// clients using the same account the AWS SDK will throttle our
	// requests once the API returns a "Rate exceeded" error.
	handler.limiter = ratelimit.NewBucketWithRate(5.0, 1)

	creds := credentials.NewStaticCredentials(accessKey, secretKey, "")
	config := aws.NewConfig().WithMaxRetries(route53MaxRetries).
		WithCredentials(creds).
		WithRegion(region)

	handler.client = route53.New(session.New(config))

	if err := handler.setHostedZone(); err != nil {
		logrus.Fatal(err)
	}

	logrus.Infof("Configured %s with hosted zone '%s' in region '%s' ",
		handler.GetName(), dns.RootDomainName, region)
}

func (r *Route53Handler) setHostedZone() error {
	if envVal := os.Getenv("ROUTE53_ZONE_ID"); envVal != "" {
		r.hostedZoneId = strings.TrimSpace(envVal)
		return nil
	}

	r.limiter.Wait(1)
	params := &route53.ListHostedZonesByNameInput{
		DNSName:  aws.String(strings.TrimSuffix(dns.RootDomainName, ".")),
		MaxItems: aws.String("1"),
	}
	resp, err := r.client.ListHostedZonesByName(params)
	if err != nil {
		return fmt.Errorf("Could not list hosted zones: %v", err)
	}

	if len(resp.HostedZones) == 0 || *resp.HostedZones[0].Name != dns.RootDomainName {
		return fmt.Errorf("Hosted zone '%s' not found", dns.RootDomainName)
	}

	zoneId := *resp.HostedZones[0].Id
	if strings.HasPrefix(zoneId, "/hostedzone/") {
		zoneId = strings.TrimPrefix(zoneId, "/hostedzone/")
	}

	r.hostedZoneId = zoneId
	return nil
}

func (*Route53Handler) GetName() string {
	return "Route 53"
}

func (r *Route53Handler) AddRecord(record dns.DnsRecord) error {
	return r.changeRecord(record, "UPSERT")
}

func (r *Route53Handler) UpdateRecord(record dns.DnsRecord) error {
	return r.changeRecord(record, "UPSERT")
}

func (r *Route53Handler) RemoveRecord(record dns.DnsRecord) error {
	return r.changeRecord(record, "DELETE")
}

func (r *Route53Handler) changeRecord(record dns.DnsRecord, action string) error {
	r.limiter.Wait(1)
	records := make([]*route53.ResourceRecord, len(record.Records))
	for idx, value := range record.Records {
		records[idx] = &route53.ResourceRecord{
			Value: aws.String(value),
		}
	}

	params := &route53.ChangeResourceRecordSetsInput{
		HostedZoneId: aws.String(r.hostedZoneId),
		ChangeBatch: &route53.ChangeBatch{
			Comment: aws.String("Managed by Rancher"),
			Changes: []*route53.Change{
				{
					Action: aws.String(action),
					ResourceRecordSet: &route53.ResourceRecordSet{
						Name:            aws.String(record.Fqdn),
						Type:            aws.String(record.Type),
						TTL:             aws.Int64(int64(record.TTL)),
						ResourceRecords: records,
					},
				},
			},
		},
	}

	_, err := r.client.ChangeResourceRecordSets(params)
	return err
}

func (r *Route53Handler) GetRecords() ([]dns.DnsRecord, error) {
	r.limiter.Wait(1)
	dnsRecords := []dns.DnsRecord{}
	rrSets := []*route53.ResourceRecordSet{}
	params := &route53.ListResourceRecordSetsInput{
		HostedZoneId: aws.String(r.hostedZoneId),
		MaxItems:     aws.String("100"),
	}

	err := r.client.ListResourceRecordSetsPages(params,
		func(page *route53.ListResourceRecordSetsOutput, lastPage bool) bool {
			rrSets = append(rrSets, page.ResourceRecordSets...)
			return !lastPage
		})
	if err != nil {
		return dnsRecords, fmt.Errorf("Route 53 API call has failed: %v", err)
	}

	for _, rrSet := range rrSets {
		records := []string{}
		for _, rr := range rrSet.ResourceRecords {
			records = append(records, *rr.Value)
		}

		dnsRecord := dns.DnsRecord{
			Fqdn: *rrSet.Name,
			Records: records,
			Type: *rrSet.Type,
			TTL: int(*rrSet.TTL),
		}
		dnsRecords = append(dnsRecords, dnsRecord)
	}

	return dnsRecords, nil
}
<commit_msg>route53: Fix nil-pointer exception with alias resource records<commit_after>package providers

import (
	"fmt"
	"os"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/route53"
	"github.com/juju/ratelimit"
	"github.com/rancher/external-dns/dns"
)

var route53MaxRetries int = 3

type Route53Handler struct {
	client       *route53.Route53
	hostedZoneId string
	limiter      *ratelimit.Bucket
}

func init() {
	var region, accessKey, secretKey string
	if region = os.Getenv("AWS_REGION"); len(region) == 0 {
		logrus.Info("AWS_REGION is not set, skipping init of Route 53 provider")
		return
	}

	if accessKey = os.Getenv("AWS_ACCESS_KEY"); len(accessKey) == 0 {
		logrus.Info("AWS_ACCESS_KEY is not set, skipping init of Route 53 provider")
		return
	}

	if secretKey = os.Getenv("AWS_SECRET_KEY"); len(secretKey) == 0 {
		logrus.Info("AWS_SECRET_KEY is not set, skipping init of Route 53 provider")
		return
	}

	handler := &Route53Handler{}
	if err := RegisterProvider("route53", handler); err != nil {
		logrus.Fatal("Could not register route53 provider")
	}

	// Comply with the API's 5 req/s rate limit. If there are other
	// clients using the same account the AWS SDK will throttle our
	// requests once the API returns a "Rate exceeded" error.
	handler.limiter = ratelimit.NewBucketWithRate(5.0, 1)

	creds := credentials.NewStaticCredentials(accessKey, secretKey, "")
	config := aws.NewConfig().WithMaxRetries(route53MaxRetries).
		WithCredentials(creds).
		WithRegion(region)

	handler.client = route53.New(session.New(config))

	if err := handler.setHostedZone(); err != nil {
		logrus.Fatal(err)
	}

	logrus.Infof("Configured %s with hosted zone '%s' in region '%s' ",
		handler.GetName(), dns.RootDomainName, region)
}

func (r *Route53Handler) setHostedZone() error {
	if envVal := os.Getenv("ROUTE53_ZONE_ID"); envVal != "" {
		r.hostedZoneId = strings.TrimSpace(envVal)
		return nil
	}

	r.limiter.Wait(1)
	params := &route53.ListHostedZonesByNameInput{
		DNSName:  aws.String(strings.TrimSuffix(dns.RootDomainName, ".")),
		MaxItems: aws.String("1"),
	}
	resp, err := r.client.ListHostedZonesByName(params)
	if err != nil {
		return fmt.Errorf("Could not list hosted zones: %v", err)
	}

	if len(resp.HostedZones) == 0 || *resp.HostedZones[0].Name != dns.RootDomainName {
		return fmt.Errorf("Hosted zone '%s' not found", dns.RootDomainName)
	}

	zoneId := *resp.HostedZones[0].Id
	if strings.HasPrefix(zoneId, "/hostedzone/") {
		zoneId = strings.TrimPrefix(zoneId, "/hostedzone/")
	}

	r.hostedZoneId = zoneId
	return nil
}

func (*Route53Handler) GetName() string {
	return "Route 53"
}

func (r *Route53Handler) AddRecord(record dns.DnsRecord) error {
	return r.changeRecord(record, "UPSERT")
}

func (r *Route53Handler) UpdateRecord(record dns.DnsRecord) error {
	return r.changeRecord(record, "UPSERT")
}

func (r *Route53Handler) RemoveRecord(record dns.DnsRecord) error {
	return r.changeRecord(record, "DELETE")
}

func (r *Route53Handler) changeRecord(record dns.DnsRecord, action string) error {
	r.limiter.Wait(1)
	records := make([]*route53.ResourceRecord, len(record.Records))
	for idx, value := range record.Records {
		records[idx] = &route53.ResourceRecord{
			Value: aws.String(value),
		}
	}

	params := &route53.ChangeResourceRecordSetsInput{
		HostedZoneId: aws.String(r.hostedZoneId),
		ChangeBatch: &route53.ChangeBatch{
			Comment: aws.String("Managed by Rancher"),
			Changes: []*route53.Change{
				{
					Action: aws.String(action),
					ResourceRecordSet: &route53.ResourceRecordSet{
						Name:            aws.String(record.Fqdn),
						Type:            aws.String(record.Type),
						TTL:             aws.Int64(int64(record.TTL)),
						ResourceRecords: records,
					},
				},
			},
		},
	}

	_, err := r.client.ChangeResourceRecordSets(params)
	return err
}

func (r *Route53Handler) GetRecords() ([]dns.DnsRecord, error) {
	r.limiter.Wait(1)
	dnsRecords := []dns.DnsRecord{}
	rrSets := []*route53.ResourceRecordSet{}
	params := &route53.ListResourceRecordSetsInput{
		HostedZoneId: aws.String(r.hostedZoneId),
		MaxItems:     aws.String("100"),
	}

	err := r.client.ListResourceRecordSetsPages(params,
		func(page *route53.ListResourceRecordSetsOutput, lastPage bool) bool {
			rrSets = append(rrSets, page.ResourceRecordSets...)
			return !lastPage
		})
	if err != nil {
		return dnsRecords, fmt.Errorf("Route 53 API call has failed: %v", err)
	}

	for _, rrSet := range rrSets {
		// skip proprietary Route 53 alias resource record sets
		if rrSet.AliasTarget != nil {
			continue
		}
		records := []string{}
		for _, rr := range rrSet.ResourceRecords {
			records = append(records, *rr.Value)
		}

		dnsRecord := dns.DnsRecord{
			Fqdn: *rrSet.Name,
			Records: records,
			Type: *rrSet.Type,
			TTL: int(*rrSet.TTL),
		}
		dnsRecords = append(dnsRecords, dnsRecord)
	}

	return dnsRecords, nil
}
<|endoftext|>


<commit_before>package main

import (
  "errors"
  "encoding/binary"
  "strconv"
  "strings"
  "syscall"
  "os"
  "unsafe"
)

const AF_BLUETOOTH int = 31
const (
  BTPROTO_L2CAP int = 0
  BTPROTO_HCI   int = 1
  BTPROTO_SCO   int = 2
  BTPROTO_RFCOM int = 3
  BTPROTO_BNEP  int = 4
  BTPROTO_CMTP  int = 5
  BTPROTO_HIDP  int = 6
  BTPROTO_AVDTP int = 7
)

const (
  SOL_HCI int = 0
  SOL_L2CAP int = 6
  SOL_SCO int = 17
  SOL_RFCOMM int = 18
)

const (
  BDADDR_BREDR     uint8 = 0x00
  BDADDR_LE_PUBLIC uint8 = 0x01
  BDADDR_LE_RANDOM uint8 = 0x02
)

const (
  L2CAP_LM int = 0x03
  L2CAP_LM_MASTER int =0x0001
  L2CAP_LM_AUTH int = 0x0002
  L2CAP_LM_ENCRYPT int = 0x0004
  L2CAP_LM_TRUSTED int = 0x0008
  L2CAP_LM_RELIABLE int = 0x0010
  L2CAP_LM_SECURE int = 0x0020
  L2CAP_LM_FIPS int = 0x0040
)

type UUID [6]uint8

type L2Sockaddr struct {
  buf [13]uint8
}

func NewL2Sockaddr(channel uint16, addr UUID, addr_type uint8) *L2Sockaddr {
  res := &L2Sockaddr{}
  *((*uint16)(unsafe.Pointer(&res.buf[0]))) = uint16(AF_BLUETOOTH)
  for i := 0; i < len(addr); i++ {
    res.buf[4 + i] = addr[i]
  }

  le_chan := make([]byte, 2)
  binary.LittleEndian.PutUint16(le_chan, channel)
  res.buf[10] = le_chan[0]
  res.buf[11] = le_chan[1]

  res.buf[12] = addr_type
  return res
}

func bind(s int, addr *L2Sockaddr) (err error) {
  addrlen := len(addr.buf)
  _, _, e1 := syscall.Syscall(syscall.SYS_BIND, uintptr(s),
      uintptr(unsafe.Pointer(&addr.buf[0])), uintptr(addrlen))
  if e1 != 0 {
    err = e1
  }
  return
}

func connect(s int, addr *L2Sockaddr) (err error) {
  addrlen := len(addr.buf)
  _, _, e1 := syscall.Syscall(syscall.SYS_CONNECT, uintptr(s),
      uintptr(unsafe.Pointer(&addr.buf[0])), uintptr(addrlen))
  if e1 != 0 {
    err = e1
  }
  return
}

func NewBLE(remoteAddr *L2Sockaddr) (*os.File, error){
  fd, err := syscall.Socket(AF_BLUETOOTH, syscall.SOCK_SEQPACKET, BTPROTO_L2CAP);
  if err != nil {
    return nil, err
  }

  addr := NewL2Sockaddr(4, UUID{0, 0, 0, 0, 0, 0}, BDADDR_LE_PUBLIC)
  err = bind(fd, addr)
  if err != nil {
    return nil, err
  }

  opt, err := syscall.GetsockoptInt(fd, SOL_L2CAP, L2CAP_LM)
  if err != nil {
    return nil, err
  }

  err = syscall.SetsockoptInt(fd, SOL_L2CAP, L2CAP_LM, opt | L2CAP_LM_MASTER)
  if err != nil {
    return nil, err
  }

//  err = syscall.SetsockoptInt(fd, SOL_L2CAP, L2CAP_LM, L2CAP_LM_AUTH)
//  if err != nil {
//    return nil, err
//  }

  err = connect(fd, remoteAddr)
  if err != nil {
    return nil, err
  }

  return os.NewFile(uintptr(fd), "btle"), nil

}

func Str2Ba(addrStr string) (UUID, error) {
  var remoteAddr UUID
  addrComponents := strings.Split(addrStr, ":")
  if (len(addrComponents) != 6) {
    return remoteAddr, errors.New("Bad address format")
  }

  for i,c := range(addrComponents) {
    dig, err := strconv.ParseUint(c, 16, 8)
    remoteAddr[5 - i] = uint8(dig)
    if err != nil {
      return remoteAddr, err
    }
  }
  return remoteAddr, nil
}

<commit_msg>cleanup<commit_after>package main

import (
  "errors"
  "encoding/binary"
  "strconv"
  "strings"
  "syscall"
  "os"
  "unsafe"
)

const AF_BLUETOOTH int = 31
const (
  BTPROTO_L2CAP int = 0
  BTPROTO_HCI   int = 1
  BTPROTO_SCO   int = 2
  BTPROTO_RFCOM int = 3
  BTPROTO_BNEP  int = 4
  BTPROTO_CMTP  int = 5
  BTPROTO_HIDP  int = 6
  BTPROTO_AVDTP int = 7
)

const (
  SOL_HCI int = 0
  SOL_L2CAP int = 6
  SOL_SCO int = 17
  SOL_RFCOMM int = 18
)

const (
  BDADDR_BREDR     uint8 = 0x00
  BDADDR_LE_PUBLIC uint8 = 0x01
  BDADDR_LE_RANDOM uint8 = 0x02
)

const (
  L2CAP_LM int = 0x03
  L2CAP_LM_MASTER int =0x0001
  L2CAP_LM_AUTH int = 0x0002
  L2CAP_LM_ENCRYPT int = 0x0004
  L2CAP_LM_TRUSTED int = 0x0008
  L2CAP_LM_RELIABLE int = 0x0010
  L2CAP_LM_SECURE int = 0x0020
  L2CAP_LM_FIPS int = 0x0040
)

type UUID [6]uint8

type L2Sockaddr struct {
  buf [13]uint8
}

func NewL2Sockaddr(channel uint16, addr UUID, addr_type uint8) *L2Sockaddr {
  res := &L2Sockaddr{}
  *((*uint16)(unsafe.Pointer(&res.buf[0]))) = uint16(AF_BLUETOOTH)
  for i := 0; i < len(addr); i++ {
    res.buf[4 + i] = addr[i]
  }

  le_chan := make([]byte, 2)
  binary.LittleEndian.PutUint16(le_chan, channel)
  res.buf[10] = le_chan[0]
  res.buf[11] = le_chan[1]

  res.buf[12] = addr_type
  return res
}

func bind(s int, addr *L2Sockaddr) (err error) {
  addrlen := len(addr.buf)
  _, _, e1 := syscall.Syscall(syscall.SYS_BIND, uintptr(s),
      uintptr(unsafe.Pointer(&addr.buf[0])), uintptr(addrlen))
  if e1 != 0 {
    err = e1
  }
  return
}

func connect(s int, addr *L2Sockaddr) (err error) {
  addrlen := len(addr.buf)
  _, _, e1 := syscall.Syscall(syscall.SYS_CONNECT, uintptr(s),
      uintptr(unsafe.Pointer(&addr.buf[0])), uintptr(addrlen))
  if e1 != 0 {
    err = e1
  }
  return
}

func NewBLE(remoteAddr *L2Sockaddr) (*os.File, error){
  fd, err := syscall.Socket(AF_BLUETOOTH, syscall.SOCK_SEQPACKET, BTPROTO_L2CAP);
  if err != nil {
    return nil, err
  }

  addr := NewL2Sockaddr(4, UUID{0, 0, 0, 0, 0, 0}, BDADDR_LE_PUBLIC)
  err = bind(fd, addr)
  if err != nil {
    return nil, err
  }

  opt, err := syscall.GetsockoptInt(fd, SOL_L2CAP, L2CAP_LM)
  if err != nil {
    return nil, err
  }

  err = syscall.SetsockoptInt(fd, SOL_L2CAP, L2CAP_LM, opt | L2CAP_LM_MASTER)
  if err != nil {
    return nil, err
  }

  err = connect(fd, remoteAddr)
  if err != nil {
    return nil, err
  }

  return os.NewFile(uintptr(fd), "btle"), nil

}

func Str2Ba(addrStr string) (UUID, error) {
  var remoteAddr UUID
  addrComponents := strings.Split(addrStr, ":")
  if (len(addrComponents) != 6) {
    return remoteAddr, errors.New("Bad address format")
  }

  for i,c := range(addrComponents) {
    dig, err := strconv.ParseUint(c, 16, 8)
    remoteAddr[5 - i] = uint8(dig)
    if err != nil {
      return remoteAddr, err
    }
  }
  return remoteAddr, nil
}

<|endoftext|>


<commit_before>package tunnel

import (
	"crypto/sha256"
	"errors"
	"fmt"
	"sync"

	"github.com/google/uuid"
	"github.com/micro/go-micro/network/link"
	"github.com/micro/go-micro/transport"
)

// tun represents a network tunnel
type tun struct {
	// the link on top of which we build a tunnel
	link link.Link

	sync.RWMutex

	// to indicate if we're connected or not
	connected bool

	// the send channel for all messages
	send chan *message

	// close channel
	closed chan bool

	// a map of sockets based on Micro-Tunnel-Id
	sockets map[string]*socket
}

// create new tunnel on top of a link
func newTunnel(link link.Link) *tun {
	return &tun{
		link:    link,
		send:    make(chan *message, 128),
		closed:  make(chan bool),
		sockets: make(map[string]*socket),
	}
}

// getSocket returns a socket from the internal socket map.
// It does this based on the Micro-Tunnel-Id and Micro-Tunnel-Session
func (t *tun) getSocket(id, session string) (*socket, bool) {
	// get the socket
	t.RLock()
	s, ok := t.sockets[id+session]
	t.RUnlock()
	return s, ok
}

// newSocket creates a new socket and saves it
func (t *tun) newSocket(id, session string) (*socket, bool) {
	// hash the id
	h := sha256.New()
	h.Write([]byte(id))
	id = fmt.Sprintf("%x", h.Sum(nil))

	// new socket
	s := &socket{
		id:      id,
		session: session,
		closed:  make(chan bool),
		recv:    make(chan *message, 128),
		send:    t.send,
	}

	// save socket
	t.Lock()
	_, ok := t.sockets[id+session]
	if ok {
		// socket already exists
		t.Unlock()
		return nil, false
	}
	t.sockets[id+session] = s
	t.Unlock()

	// return socket
	return s, true
}

// TODO: use tunnel id as part of the session
func (t *tun) newSession() string {
	return uuid.New().String()
}

// process outgoing messages sent by all local sockets
func (t *tun) process() {
	// manage the send buffer
	// all pseudo sockets throw everything down this
	for {
		select {
		case msg := <-t.send:
			nmsg := &transport.Message{
				Header: msg.data.Header,
				Body:   msg.data.Body,
			}

			// set the tunnel id on the outgoing message
			nmsg.Header["Micro-Tunnel-Id"] = msg.id

			// set the session id
			nmsg.Header["Micro-Tunnel-Session"] = msg.session

			// send the message via the interface
			if err := t.link.Send(nmsg); err != nil {
				// no op
				// TODO: do something
			}
		case <-t.closed:
			return
		}
	}
}

// process incoming messages
func (t *tun) listen() {
	for {
		// process anything via the net interface
		msg := new(transport.Message)
		err := t.link.Recv(msg)
		if err != nil {
			return
		}

		// the tunnel id
		id := msg.Header["Micro-Tunnel-Id"]

		// the session id
		session := msg.Header["Micro-Tunnel-Session"]

		// if the session id is blank there's nothing we can do
		// TODO: check this is the case, is there any reason
		// why we'd have a blank session? Is the tunnel
		// used for some other purpose?
		if len(session) == 0 {
			continue
		}

		// get the socket based on the tunnel id and session
		// this could be something we dialed in which case
		// we have a session for it otherwise its a listener
		s, exists := t.getSocket(id, session)
		if !exists {
			// try get it based on just the tunnel id
			// the assumption here is that a listener
			// has no session but its set a listener session
			s, exists = t.getSocket(id, "listener")
			if !exists {
				conti

				// drop it, we don't care about
				// messages we don't know about
				continue
			}
		}

		// is the socket closed?
		select {
		case <-s.closed:
			// closed
			delete(t.sockets, id)
			continue
		default:
			// process
		}

		// is the socket new?
		select {
		// if its new the socket is actually blocked waiting
		// for a connection. so we check if its waiting.
		case <-s.wait:
		// if its waiting e.g its new then we close it
		default:
			// set remote address of the socket
			s.remote = msg.Header["Remote"]
			close(s.wait)
		}

		// construct a new transport message
		tmsg := &transport.Message{
			Header: msg.Header,
			Body:   msg.Body,
		}

		// construct the internal message
		imsg := &message{
			id:      id,
			session: session,
			data:    tmsg,
		}

		// append to recv backlog
		// we don't block if we can't pass it on
		select {
		case s.recv <- imsg:
		default:
		}
	}
}

// Close the tunnel
func (t *tun) Close() error {
	t.Lock()
	defer t.Unlock()

	if !t.connected {
		return nil
	}

	select {
	case <-t.closed:
		return nil
	default:
		// close all the sockets
		for _, s := range t.sockets {
			s.Close()
		}
		// close the connection
		close(t.closed)
		t.connected = false
	}

	return nil
}

// Connect the tunnel
func (t *tun) Connect() error {
	t.Lock()
	defer t.Unlock()

	// already connected
	if t.connected {
		return nil
	}

	// set as connected
	t.connected = true
	// create new close channel
	t.closed = make(chan bool)

	// process messages to be sent
	go t.process()
	// process incoming messages
	go t.listen()

	return nil
}

// Dial an address
func (t *tun) Dial(addr string) (Conn, error) {
	c, ok := t.newSocket(addr, t.newSession())
	if !ok {
		return nil, errors.New("error dialing " + addr)
	}
	// set remote
	c.remote = addr
	// set local
	c.local = t.link.Local()

	return c, nil
}

// Accept a connection on the address
func (t *tun) Listen(addr string) (Listener, error) {
	// create a new socket by hashing the address
	c, ok := t.newSocket(addr, "listener")
	if !ok {
		return nil, errors.New("already listening on " + addr)
	}

	// set remote. it will be replaced by the first message received
	c.remote = t.link.Remote()
	// set local
	c.local = addr

	tl := &tunListener{
		addr: addr,
		// the accept channel
		accept: make(chan *socket, 128),
		// the channel to close
		closed: make(chan bool),
		// the connection
		conn: c,
		// the listener socket
		socket: c,
	}

	// this kicks off the internal message processor
	// for the listener so it can create pseudo sockets
	// per session if they do not exist or pass messages
	// to the existign sessions
	go tl.process()

	// return the listener
	return tl, nil
}
<commit_msg>Fix breaking code<commit_after>package tunnel

import (
	"crypto/sha256"
	"errors"
	"fmt"
	"sync"

	"github.com/google/uuid"
	"github.com/micro/go-micro/network/link"
	"github.com/micro/go-micro/transport"
)

// tun represents a network tunnel
type tun struct {
	// the link on top of which we build a tunnel
	link link.Link

	sync.RWMutex

	// to indicate if we're connected or not
	connected bool

	// the send channel for all messages
	send chan *message

	// close channel
	closed chan bool

	// a map of sockets based on Micro-Tunnel-Id
	sockets map[string]*socket
}

// create new tunnel on top of a link
func newTunnel(link link.Link) *tun {
	return &tun{
		link:    link,
		send:    make(chan *message, 128),
		closed:  make(chan bool),
		sockets: make(map[string]*socket),
	}
}

// getSocket returns a socket from the internal socket map.
// It does this based on the Micro-Tunnel-Id and Micro-Tunnel-Session
func (t *tun) getSocket(id, session string) (*socket, bool) {
	// get the socket
	t.RLock()
	s, ok := t.sockets[id+session]
	t.RUnlock()
	return s, ok
}

// newSocket creates a new socket and saves it
func (t *tun) newSocket(id, session string) (*socket, bool) {
	// hash the id
	h := sha256.New()
	h.Write([]byte(id))
	id = fmt.Sprintf("%x", h.Sum(nil))

	// new socket
	s := &socket{
		id:      id,
		session: session,
		closed:  make(chan bool),
		recv:    make(chan *message, 128),
		send:    t.send,
	}

	// save socket
	t.Lock()
	_, ok := t.sockets[id+session]
	if ok {
		// socket already exists
		t.Unlock()
		return nil, false
	}
	t.sockets[id+session] = s
	t.Unlock()

	// return socket
	return s, true
}

// TODO: use tunnel id as part of the session
func (t *tun) newSession() string {
	return uuid.New().String()
}

// process outgoing messages sent by all local sockets
func (t *tun) process() {
	// manage the send buffer
	// all pseudo sockets throw everything down this
	for {
		select {
		case msg := <-t.send:
			nmsg := &transport.Message{
				Header: msg.data.Header,
				Body:   msg.data.Body,
			}

			// set the tunnel id on the outgoing message
			nmsg.Header["Micro-Tunnel-Id"] = msg.id

			// set the session id
			nmsg.Header["Micro-Tunnel-Session"] = msg.session

			// send the message via the interface
			if err := t.link.Send(nmsg); err != nil {
				// no op
				// TODO: do something
			}
		case <-t.closed:
			return
		}
	}
}

// process incoming messages
func (t *tun) listen() {
	for {
		// process anything via the net interface
		msg := new(transport.Message)
		err := t.link.Recv(msg)
		if err != nil {
			return
		}

		// the tunnel id
		id := msg.Header["Micro-Tunnel-Id"]

		// the session id
		session := msg.Header["Micro-Tunnel-Session"]

		// if the session id is blank there's nothing we can do
		// TODO: check this is the case, is there any reason
		// why we'd have a blank session? Is the tunnel
		// used for some other purpose?
		if len(session) == 0 {
			continue
		}

		// get the socket based on the tunnel id and session
		// this could be something we dialed in which case
		// we have a session for it otherwise its a listener
		s, exists := t.getSocket(id, session)
		if !exists {
			// try get it based on just the tunnel id
			// the assumption here is that a listener
			// has no session but its set a listener session
			s, exists = t.getSocket(id, "listener")
			if !exists {
				// drop it, we don't care about
				// messages we don't know about
				continue
			}
		}

		// is the socket closed?
		select {
		case <-s.closed:
			// closed
			delete(t.sockets, id)
			continue
		default:
			// process
		}

		// is the socket new?
		select {
		// if its new the socket is actually blocked waiting
		// for a connection. so we check if its waiting.
		case <-s.wait:
		// if its waiting e.g its new then we close it
		default:
			// set remote address of the socket
			s.remote = msg.Header["Remote"]
			close(s.wait)
		}

		// construct a new transport message
		tmsg := &transport.Message{
			Header: msg.Header,
			Body:   msg.Body,
		}

		// construct the internal message
		imsg := &message{
			id:      id,
			session: session,
			data:    tmsg,
		}

		// append to recv backlog
		// we don't block if we can't pass it on
		select {
		case s.recv <- imsg:
		default:
		}
	}
}

// Close the tunnel
func (t *tun) Close() error {
	t.Lock()
	defer t.Unlock()

	if !t.connected {
		return nil
	}

	select {
	case <-t.closed:
		return nil
	default:
		// close all the sockets
		for _, s := range t.sockets {
			s.Close()
		}
		// close the connection
		close(t.closed)
		t.connected = false
	}

	return nil
}

// Connect the tunnel
func (t *tun) Connect() error {
	t.Lock()
	defer t.Unlock()

	// already connected
	if t.connected {
		return nil
	}

	// set as connected
	t.connected = true
	// create new close channel
	t.closed = make(chan bool)

	// process messages to be sent
	go t.process()
	// process incoming messages
	go t.listen()

	return nil
}

// Dial an address
func (t *tun) Dial(addr string) (Conn, error) {
	c, ok := t.newSocket(addr, t.newSession())
	if !ok {
		return nil, errors.New("error dialing " + addr)
	}
	// set remote
	c.remote = addr
	// set local
	c.local = t.link.Local()

	return c, nil
}

// Accept a connection on the address
func (t *tun) Listen(addr string) (Listener, error) {
	// create a new socket by hashing the address
	c, ok := t.newSocket(addr, "listener")
	if !ok {
		return nil, errors.New("already listening on " + addr)
	}

	// set remote. it will be replaced by the first message received
	c.remote = t.link.Remote()
	// set local
	c.local = addr

	tl := &tunListener{
		addr: addr,
		// the accept channel
		accept: make(chan *socket, 128),
		// the channel to close
		closed: make(chan bool),
		// the connection
		conn: c,
		// the listener socket
		socket: c,
	}

	// this kicks off the internal message processor
	// for the listener so it can create pseudo sockets
	// per session if they do not exist or pass messages
	// to the existign sessions
	go tl.process()

	// return the listener
	return tl, nil
}
<|endoftext|>


<commit_before>package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "time"
)

func WriteQueue() {
    op_start := time.Now()

    mu.Lock()
    json_queue, _ := json.MarshalIndent(queue, "", "  ")
    ioutil.WriteFile(ROOT+"/data/queue.json", json_queue, 0644)
    mu.Unlock()

    fmt.Println("queue persist took:", time.Since(op_start))
}

func Persist() {
    // start a loop and write the queue at the prescribed time
    for {
        time.Sleep(time.Duration(config.PersistInterval) * time.Second)

        // don't persist if dispenserd is shutting down
        if !running {
            break
        }

        WriteQueue()
    }
}

func LoadQueue() {
    // load in the queue
    json_queue, err := ioutil.ReadFile(ROOT + "/data/queue.json")

    var tmp_queue = map[string]job_set{}

    if err != nil {
        return
    }

    ju_err := json.Unmarshal(json_queue, &tmp_queue)

    if ju_err != nil {
        return
    }

    var highest_job_num uint64 = 0
    var current_lane string = "main"

    for _, lane := range tmp_queue {
        // skip over empty saved queues
        if len(lane) == 0 {
            continue
        }

        var previous_priority uint = 0
        priority := -1

        for i, v := range lane {
            if priority == -1 {
                priority = int(*v.Priority)
            }

            // continue establish state of the current lane for later
            if v.Lane != nil {
                current_lane = *v.Lane

                // add lane to indexes
                _, ok := indexes[current_lane]

                if !ok {
                    indexes[current_lane] = make(map[uint]uint64)
                }
            }

            // ascertain the newest high job num
            if v.JobNum > highest_job_num {
                highest_job_num = v.JobNum
            }

            // ascertain if the lane exists and append if it doesn't
            if !UtilInArray(*v.Lane, lanes) {
                lanes = append(lanes, *v.Lane)
            }

            // check for priority change to establish index boundaries
            if int(*v.Priority) != priority && int(*v.Priority) > priority {
                indexes[current_lane][previous_priority] = uint64(i)
                priority = int(*v.Priority)
            }

            previous_priority = *v.Priority
        }

        // determine where the lane will go, queue[0] is established by default
        queue[current_lane] = lane
    }

    // set the total jobs so dispenserd knows what the next unique job id is
    total_jobs = highest_job_num
}
<commit_msg>fixed bug causing a deadlock due to sending on a nil channel if a persisted queue was restored and more than 1 lane was in use<commit_after>package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "time"
)

func WriteQueue() {
    op_start := time.Now()

    mu.Lock()
    json_queue, _ := json.MarshalIndent(queue, "", "  ")
    ioutil.WriteFile(ROOT+"/data/queue.json", json_queue, 0644)
    mu.Unlock()

    fmt.Println("queue persist took:", time.Since(op_start))
}

func Persist() {
    // start a loop and write the queue at the prescribed time
    for {
        time.Sleep(time.Duration(config.PersistInterval) * time.Second)

        // don't persist if dispenserd is shutting down
        if !running {
            break
        }

        WriteQueue()
    }
}

func LoadQueue() {
    // load in the queue
    json_queue, err := ioutil.ReadFile(ROOT + "/data/queue.json")

    var tmp_queue = map[string]job_set{}

    if err != nil {
        return
    }

    ju_err := json.Unmarshal(json_queue, &tmp_queue)

    if ju_err != nil {
        return
    }

    var highest_job_num uint64 = 0
    var current_lane string = "main"

    for _, lane := range tmp_queue {
        // skip over empty saved queues
        if len(lane) == 0 {
            continue
        }

        var previous_priority uint = 0
        priority := -1

        for i, v := range lane {
            if priority == -1 {
                priority = int(*v.Priority)
            }

            // continue establish state of the current lane for later
            if v.Lane != nil {
                current_lane = *v.Lane

                // add lane to indexes
                _, ok := indexes[current_lane]

                if !ok {
                    indexes[current_lane] = make(map[uint]uint64)
                    listeners[current_lane] = make(chan int)
                    idle_workers[current_lane] = 0
                }
            }

            // ascertain the newest high job num
            if v.JobNum > highest_job_num {
                highest_job_num = v.JobNum
            }

            // ascertain if the lane exists and append if it doesn't
            if !UtilInArray(*v.Lane, lanes) {
                lanes = append(lanes, *v.Lane)
            }

            // check for priority change to establish index boundaries
            if int(*v.Priority) != priority && int(*v.Priority) > priority {
                indexes[current_lane][previous_priority] = uint64(i)
                priority = int(*v.Priority)
            }

            previous_priority = *v.Priority
        }

        // determine where the lane will go, queue[0] is established by default
        queue[current_lane] = lane
    }

    // set the total jobs so dispenserd knows what the next unique job id is
    total_jobs = highest_job_num
}
<|endoftext|>


<commit_before>package clui

import (
	"bufio"
	term "github.com/nsf/termbox-go"
	"io/ioutil"
	"os"
	"strings"
)

/*
ThemeManager support for controls.
The current implementation is limited but later the manager will be
able to load a requested theme on demand and use deep inheritance.
Theme 'default' exists always - it is predefinded and always complete.
User-defined themes may omit any theme section, all omitted items
are loaded from parent theme. The only required property that a user-
defined theme must have is a theme name.
*/
type ThemeManager struct {
	// available theme list
	themes map[string]theme
	// name of the current theme
	current   string
	themePath string
	version   string
}

const themeSuffix = ".theme"

// ThemeInfo is a detailed information about theme:
// title, author, version number
type ThemeInfo struct {
	parent  string
	title   string
	author  string
	version string
}

/*
A theme structure. It keeps all colors, characters for the theme.
Parent property determines a theme name that is used if a requested
theme object is not declared in the current one. If no parent is
defined then the library uses default built-in theme.
*/
type theme struct {
	parent  string
	title   string
	author  string
	version string
	colors  map[string]term.Attribute
	objects map[string]string
}

const defaultTheme = "default"

// NewThemeManager creates a new theme manager
func NewThemeManager() *ThemeManager {
	sm := new(ThemeManager)

	sm.Reset()

	return sm
}

// Reset removes all loaded themes from cache and reinitialize
// the default theme
func (s *ThemeManager) Reset() {
	s.current = defaultTheme
	s.themes = make(map[string]theme, 0)

	defTheme := theme{parent: "", title: "Default Theme", author: "V. Markelov", version: "1.0"}
	defTheme.colors = make(map[string]term.Attribute, 0)
	defTheme.objects = make(map[string]string, 0)

	defTheme.objects[ObjSingleBorder] = "─│┌┐└┘"
	defTheme.objects[ObjDoubleBorder] = "═║╔╗╚╝"
	defTheme.objects[ObjEdit] = "←→V"
	defTheme.objects[ObjScrollBar] = "|O^v"
	defTheme.objects[ObjViewButtons] = "^↓○[]"
	defTheme.objects[ObjCheckBox] = "[] X?"
	defTheme.objects[ObjRadio] = "() *"
	defTheme.objects[ObjProgressBar] = "░▒"

	defTheme.colors[ColorDisabledText] = ColorBlackBold
	defTheme.colors[ColorDisabledBack] = ColorWhite
	defTheme.colors[ColorText] = ColorWhite
	defTheme.colors[ColorBack] = ColorBlackBold
	defTheme.colors[ColorViewBack] = ColorBlackBold
	defTheme.colors[ColorViewText] = ColorWhite

	defTheme.colors[ColorControlText] = ColorWhite
	defTheme.colors[ColorControlBack] = ColorBlack
	defTheme.colors[ColorControlActiveText] = ColorWhite
	defTheme.colors[ColorControlActiveBack] = ColorMagenta
	defTheme.colors[ColorControlShadow] = ColorBlue
	defTheme.colors[ColorControlDisabledText] = ColorWhite
	defTheme.colors[ColorControlDisabledBack] = ColorBlackBold

	defTheme.colors[ColorEditText] = ColorBlack
	defTheme.colors[ColorEditBack] = ColorWhite
	defTheme.colors[ColorEditActiveText] = ColorBlack
	defTheme.colors[ColorEditActiveBack] = ColorWhiteBold
	defTheme.colors[ColorSelectionText] = ColorYellow
	defTheme.colors[ColorSelectionBack] = ColorBlue

	defTheme.colors[ColorScrollBack] = ColorBlackBold
	defTheme.colors[ColorScrollText] = ColorWhite
	defTheme.colors[ColorThumbBack] = ColorBlackBold
	defTheme.colors[ColorThumbText] = ColorWhite

	defTheme.colors[ColorProgressText] = ColorBlue
	defTheme.colors[ColorProgressBack] = ColorBlackBold
	defTheme.colors[ColorProgressActiveText] = ColorBlack
	defTheme.colors[ColorProgressActiveBack] = ColorBlueBold

	s.themes[defaultTheme] = defTheme
}

// SysColor returns attribute by its id for the current theme
func (s *ThemeManager) SysColor(color string) term.Attribute {
	sch, ok := s.themes[s.current]
	if !ok {
		sch = s.themes[defaultTheme]
	}

	clr, okclr := sch.colors[color]
	if !okclr {
		visited := make(map[string]int, 0)
		visited[s.current] = 1
		if !ok {
			visited[defaultTheme] = 1
		}

		for {
			s.LoadTheme(sch.parent)
			sch = s.themes[sch.parent]
			clr, okclr = sch.colors[color]

			if ok {
				break
			} else {
				if _, okSch := visited[sch.parent]; okSch {
					panic("Color + " + color + ". Theme loop detected: " + sch.title + " --> " + sch.parent)
				} else {
					visited[sch.parent] = 1
				}
			}
		}
	}

	return clr
}

// SysObject returns object look by its id for the current
// theme. E.g, border lines for frame or arrows for scrollbar
func (s *ThemeManager) SysObject(object string) string {
	sch, ok := s.themes[s.current]
	if !ok {
		sch = s.themes[defaultTheme]
	}

	obj, okobj := sch.objects[object]
	if !okobj {
		visited := make(map[string]int, 0)
		visited[s.current] = 1
		if !ok {
			visited[defaultTheme] = 1
		}

		for {
			s.LoadTheme(sch.parent)
			sch = s.themes[sch.parent]
			obj, okobj = sch.objects[object]

			if ok {
				break
			} else {
				if _, okSch := visited[sch.parent]; okSch {
					panic("Object: " + object + ". Theme loop detected: " + sch.title + " --> " + sch.parent)
				} else {
					visited[sch.parent] = 1
				}
			}
		}
	}

	return obj
}

// ThemeNames returns the list of short theme names (file names)
func (s *ThemeManager) ThemeNames() []string {
	var str []string
	str = append(str, defaultTheme)

	path := s.themePath
	if path == "" {
		path = "." + string(os.PathSeparator)
	}
	files, err := ioutil.ReadDir(path)
	if err != nil {
		panic("Failed to read theme directory: " + s.themePath)
	}

	for _, f := range files {
		name := f.Name()
		if !f.IsDir() && strings.HasSuffix(name, themeSuffix) {
			str = append(str, strings.TrimSuffix(name, themeSuffix))
		}
	}

	return str
}

// CurrentTheme returns name of the current theme
func (s *ThemeManager) CurrentTheme() string {
	return s.current
}

// SetCurrentTheme changes the current theme.
// Returns false if changing failed - e.g, theme does not exist
func (s *ThemeManager) SetCurrentTheme(name string) bool {
	if _, ok := s.themes[name]; !ok {
		tnames := s.ThemeNames()
		for _, theme := range tnames {
			if theme == name {
				s.LoadTheme(theme)
				break
			}
		}
	}

	if _, ok := s.themes[name]; ok {
		s.current = name
		return true
	}
	return false
}

// ThemePath returns the current directory with theme inside it
func (s *ThemeManager) ThemePath() string {
	return s.themePath
}

// SetThemePath changes the directory that contains themes.
// If new path does not equal old one, theme list reloads
func (s *ThemeManager) SetThemePath(path string) {
	if path == s.themePath {
		return
	}

	s.themePath = path
	s.Reset()
}

// LoadTheme loads the theme if it is not in the cache already.
// If theme is in the cache LoadTheme does nothing
func (s *ThemeManager) LoadTheme(name string) {
	if _, ok := s.themes[name]; ok {
		return
	}

	theme := theme{parent: "", title: "", author: ""}
	theme.colors = make(map[string]term.Attribute, 0)
	theme.objects = make(map[string]string, 0)

	file, err := os.Open(s.themePath + string(os.PathSeparator) + name + themeSuffix)
	if err != nil {
		panic("Failed to open theme " + name + " : " + err.Error())
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		line = strings.Trim(line, " ")

		// skip comments
		if strings.HasPrefix(line, "#") || strings.HasPrefix(line, "/") {
			continue
		}

		// skip invalid lines
		if !strings.Contains(line, "=") {
			continue
		}

		parts := strings.SplitN(line, "=", 2)
		key := strings.Trim(parts[0], " ")
		value := strings.Trim(parts[1], " ")

		low := strings.ToLower(key)
		if low == "parent" {
			theme.parent = value
		} else if low == "author" {
			theme.author = value
		} else if low == "name" || low == "title" {
			theme.title = value
		} else if low == "version" {
			theme.version = value
		} else if strings.HasSuffix(key, "Back") || strings.HasSuffix(key, "Text") {
			c := StringToColor(value)
			if c%32 == 0 {
				panic("Failed to read color: " + value)
			}
			theme.colors[key] = c
		} else {
			theme.objects[key] = value
		}
	}

	if theme.parent == "" {
		theme.parent = "default"
	}

	s.themes[name] = theme
}

// ReLoadTheme refresh cache entry for the theme with new
// data loaded from file. Use it to apply theme changes on
// the fly without resetting manager or restarting application
func (s *ThemeManager) ReLoadTheme(name string) {
	if _, ok := s.themes[name]; ok {
		delete(s.themes, name)
	}

	s.LoadTheme(name)
}

// ThemeInfo returns detailed info about theme
func (s *ThemeManager) ThemeInfo(name string) ThemeInfo {
	s.LoadTheme(name)
	var theme ThemeInfo
	if t, ok := s.themes[name]; !ok {
		theme.parent = t.parent
		theme.title = t.title
		theme.version = t.version
	}
	return theme
}
<commit_msg>#27 - add theme file format description<commit_after>package clui

import (
	"bufio"
	term "github.com/nsf/termbox-go"
	"io/ioutil"
	"os"
	"strings"
)

/*
ThemeManager support for controls.
The current implementation is limited but later the manager will be
able to load a requested theme on demand and use deep inheritance.
Theme 'default' exists always - it is predefinded and always complete.
User-defined themes may omit any theme section, all omitted items
are loaded from parent theme. The only required property that a user-
defined theme must have is a theme name.

Theme file is a simple text file that has similar to INI file format:
1. Every line started with '#' or '/' is a comment line.
2. Invalid lines - lines that do not contain symbol '=' - are skipped.
3. Valid lines are splitted in two parts:
    key - the text before the first '=' in the line
    value - the text after the first '=' in the line (so, values can
        include '=')
4. There is no mandatory keys - all of them are optional
5. Avaiable system keys that used to describe the theme:
    'title' - the theme title
    'author' - theme author
    'version' - theme version
    'parent' - name of the parent theme. If it is not set then the
        'default' is used as a parent
6. Non-system keys are divided into two groups: Colors and Objects
    Colors are the keys that end with 'Back' or 'Text' - background
        and text color, respectively. If theme manager cannot
        value to color it panics. See Color*Back * Color*Text constants.
    Other keys are considered as objects - see Obj* constants.
    One is not limited with only predefined color and object names.
    The theme can inroduce its own objects, e.g. to provide a runes or
        colors for new control that is not in standard library
To see the real world example of full featured theme, please see
    included theme 'turbovision'
*/
type ThemeManager struct {
	// available theme list
	themes map[string]theme
	// name of the current theme
	current   string
	themePath string
	version   string
}

const themeSuffix = ".theme"

// ThemeInfo is a detailed information about theme:
// title, author, version number
type ThemeInfo struct {
	parent  string
	title   string
	author  string
	version string
}

/*
A theme structure. It keeps all colors, characters for the theme.
Parent property determines a theme name that is used if a requested
theme object is not declared in the current one. If no parent is
defined then the library uses default built-in theme.
*/
type theme struct {
	parent  string
	title   string
	author  string
	version string
	colors  map[string]term.Attribute
	objects map[string]string
}

const defaultTheme = "default"

// NewThemeManager creates a new theme manager
func NewThemeManager() *ThemeManager {
	sm := new(ThemeManager)

	sm.Reset()

	return sm
}

// Reset removes all loaded themes from cache and reinitialize
// the default theme
func (s *ThemeManager) Reset() {
	s.current = defaultTheme
	s.themes = make(map[string]theme, 0)

	defTheme := theme{parent: "", title: "Default Theme", author: "V. Markelov", version: "1.0"}
	defTheme.colors = make(map[string]term.Attribute, 0)
	defTheme.objects = make(map[string]string, 0)

	defTheme.objects[ObjSingleBorder] = "─│┌┐└┘"
	defTheme.objects[ObjDoubleBorder] = "═║╔╗╚╝"
	defTheme.objects[ObjEdit] = "←→V"
	defTheme.objects[ObjScrollBar] = "|O^v"
	defTheme.objects[ObjViewButtons] = "^↓○[]"
	defTheme.objects[ObjCheckBox] = "[] X?"
	defTheme.objects[ObjRadio] = "() *"
	defTheme.objects[ObjProgressBar] = "░▒"

	defTheme.colors[ColorDisabledText] = ColorBlackBold
	defTheme.colors[ColorDisabledBack] = ColorWhite
	defTheme.colors[ColorText] = ColorWhite
	defTheme.colors[ColorBack] = ColorBlackBold
	defTheme.colors[ColorViewBack] = ColorBlackBold
	defTheme.colors[ColorViewText] = ColorWhite

	defTheme.colors[ColorControlText] = ColorWhite
	defTheme.colors[ColorControlBack] = ColorBlack
	defTheme.colors[ColorControlActiveText] = ColorWhite
	defTheme.colors[ColorControlActiveBack] = ColorMagenta
	defTheme.colors[ColorControlShadow] = ColorBlue
	defTheme.colors[ColorControlDisabledText] = ColorWhite
	defTheme.colors[ColorControlDisabledBack] = ColorBlackBold

	defTheme.colors[ColorEditText] = ColorBlack
	defTheme.colors[ColorEditBack] = ColorWhite
	defTheme.colors[ColorEditActiveText] = ColorBlack
	defTheme.colors[ColorEditActiveBack] = ColorWhiteBold
	defTheme.colors[ColorSelectionText] = ColorYellow
	defTheme.colors[ColorSelectionBack] = ColorBlue

	defTheme.colors[ColorScrollBack] = ColorBlackBold
	defTheme.colors[ColorScrollText] = ColorWhite
	defTheme.colors[ColorThumbBack] = ColorBlackBold
	defTheme.colors[ColorThumbText] = ColorWhite

	defTheme.colors[ColorProgressText] = ColorBlue
	defTheme.colors[ColorProgressBack] = ColorBlackBold
	defTheme.colors[ColorProgressActiveText] = ColorBlack
	defTheme.colors[ColorProgressActiveBack] = ColorBlueBold

	s.themes[defaultTheme] = defTheme
}

// SysColor returns attribute by its id for the current theme
func (s *ThemeManager) SysColor(color string) term.Attribute {
	sch, ok := s.themes[s.current]
	if !ok {
		sch = s.themes[defaultTheme]
	}

	clr, okclr := sch.colors[color]
	if !okclr {
		visited := make(map[string]int, 0)
		visited[s.current] = 1
		if !ok {
			visited[defaultTheme] = 1
		}

		for {
			s.LoadTheme(sch.parent)
			sch = s.themes[sch.parent]
			clr, okclr = sch.colors[color]

			if ok {
				break
			} else {
				if _, okSch := visited[sch.parent]; okSch {
					panic("Color + " + color + ". Theme loop detected: " + sch.title + " --> " + sch.parent)
				} else {
					visited[sch.parent] = 1
				}
			}
		}
	}

	return clr
}

// SysObject returns object look by its id for the current
// theme. E.g, border lines for frame or arrows for scrollbar
func (s *ThemeManager) SysObject(object string) string {
	sch, ok := s.themes[s.current]
	if !ok {
		sch = s.themes[defaultTheme]
	}

	obj, okobj := sch.objects[object]
	if !okobj {
		visited := make(map[string]int, 0)
		visited[s.current] = 1
		if !ok {
			visited[defaultTheme] = 1
		}

		for {
			s.LoadTheme(sch.parent)
			sch = s.themes[sch.parent]
			obj, okobj = sch.objects[object]

			if ok {
				break
			} else {
				if _, okSch := visited[sch.parent]; okSch {
					panic("Object: " + object + ". Theme loop detected: " + sch.title + " --> " + sch.parent)
				} else {
					visited[sch.parent] = 1
				}
			}
		}
	}

	return obj
}

// ThemeNames returns the list of short theme names (file names)
func (s *ThemeManager) ThemeNames() []string {
	var str []string
	str = append(str, defaultTheme)

	path := s.themePath
	if path == "" {
		path = "." + string(os.PathSeparator)
	}
	files, err := ioutil.ReadDir(path)
	if err != nil {
		panic("Failed to read theme directory: " + s.themePath)
	}

	for _, f := range files {
		name := f.Name()
		if !f.IsDir() && strings.HasSuffix(name, themeSuffix) {
			str = append(str, strings.TrimSuffix(name, themeSuffix))
		}
	}

	return str
}

// CurrentTheme returns name of the current theme
func (s *ThemeManager) CurrentTheme() string {
	return s.current
}

// SetCurrentTheme changes the current theme.
// Returns false if changing failed - e.g, theme does not exist
func (s *ThemeManager) SetCurrentTheme(name string) bool {
	if _, ok := s.themes[name]; !ok {
		tnames := s.ThemeNames()
		for _, theme := range tnames {
			if theme == name {
				s.LoadTheme(theme)
				break
			}
		}
	}

	if _, ok := s.themes[name]; ok {
		s.current = name
		return true
	}
	return false
}

// ThemePath returns the current directory with theme inside it
func (s *ThemeManager) ThemePath() string {
	return s.themePath
}

// SetThemePath changes the directory that contains themes.
// If new path does not equal old one, theme list reloads
func (s *ThemeManager) SetThemePath(path string) {
	if path == s.themePath {
		return
	}

	s.themePath = path
	s.Reset()
}

// LoadTheme loads the theme if it is not in the cache already.
// If theme is in the cache LoadTheme does nothing
func (s *ThemeManager) LoadTheme(name string) {
	if _, ok := s.themes[name]; ok {
		return
	}

	theme := theme{parent: "", title: "", author: ""}
	theme.colors = make(map[string]term.Attribute, 0)
	theme.objects = make(map[string]string, 0)

	file, err := os.Open(s.themePath + string(os.PathSeparator) + name + themeSuffix)
	if err != nil {
		panic("Failed to open theme " + name + " : " + err.Error())
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		line = strings.Trim(line, " ")

		// skip comments
		if strings.HasPrefix(line, "#") || strings.HasPrefix(line, "/") {
			continue
		}

		// skip invalid lines
		if !strings.Contains(line, "=") {
			continue
		}

		parts := strings.SplitN(line, "=", 2)
		key := strings.Trim(parts[0], " ")
		value := strings.Trim(parts[1], " ")

		low := strings.ToLower(key)
		if low == "parent" {
			theme.parent = value
		} else if low == "author" {
			theme.author = value
		} else if low == "name" || low == "title" {
			theme.title = value
		} else if low == "version" {
			theme.version = value
		} else if strings.HasSuffix(key, "Back") || strings.HasSuffix(key, "Text") {
			c := StringToColor(value)
			if c%32 == 0 {
				panic("Failed to read color: " + value)
			}
			theme.colors[key] = c
		} else {
			theme.objects[key] = value
		}
	}

	if theme.parent == "" {
		theme.parent = "default"
	}

	s.themes[name] = theme
}

// ReLoadTheme refresh cache entry for the theme with new
// data loaded from file. Use it to apply theme changes on
// the fly without resetting manager or restarting application
func (s *ThemeManager) ReLoadTheme(name string) {
	if _, ok := s.themes[name]; ok {
		delete(s.themes, name)
	}

	s.LoadTheme(name)
}

// ThemeInfo returns detailed info about theme
func (s *ThemeManager) ThemeInfo(name string) ThemeInfo {
	s.LoadTheme(name)
	var theme ThemeInfo
	if t, ok := s.themes[name]; !ok {
		theme.parent = t.parent
		theme.title = t.title
		theme.version = t.version
	}
	return theme
}
<|endoftext|>