<commit_before>package storage

import (
	"regexp"
	"time"

	"github.com/jmoiron/sqlx"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)

var organizationNameRegexp = regexp.MustCompile(`^[\w-]+$`)

// Organization represents an organization.
type Organization struct {
	ID              int64     `db:"id"`
	CreatedAt       time.Time `db:"created_at"`
	UpdatedAt       time.Time `db:"updated_at"`
	Name            string    `db:"name"`
	DisplayName     string    `db:"display_name"`
	CanHaveGateways bool      `db:"can_have_gateways"`
}

// Validate validates the data of the Organization.
func (o Organization) Validate() error {
	if !organizationNameRegexp.MatchString(o.Name) {
		return ErrOrganizationInvalidName
	}
	return nil
}

// OrganizationUser represents an organization user.
type OrganizationUser struct {
	UserID    int64     `db:"user_id"`
	Username  string    `db:"username"`
	IsAdmin   bool      `db:"is_admin"`
	CreatedAt time.Time `db:"created_at"`
	UpdatedAt time.Time `db:"updated_at"`
}

// CreateOrganization creates the given Organization.
func CreateOrganization(db sqlx.Queryer, org *Organization) error {
	if err := org.Validate(); err != nil {
		return errors.Wrap(err, "validate error")
	}

	now := time.Now()

	err := sqlx.Get(db, &org.ID, `
		insert into organization (
			created_at,
			updated_at,
			name,
			display_name,
			can_have_gateways
		) values ($1, $2, $3, $4, $5) returning id`,
		now,
		now,
		org.Name,
		org.DisplayName,
		org.CanHaveGateways,
	)
	if err != nil {
		return handlePSQLError(Insert, err, "insert error")
	}
	org.CreatedAt = now
	org.UpdatedAt = now
	log.WithFields(log.Fields{
		"id":   org.ID,
		"name": org.Name,
	}).Info("organization created")
	return nil
}

// GetOrganization returns the Organization for the given id.
func GetOrganization(db sqlx.Queryer, id int64) (Organization, error) {
	var org Organization
	err := sqlx.Get(db, &org, "select * from organization where id = $1", id)
	if err != nil {
		return org, handlePSQLError(Select, err, "select error")
	}
	return org, nil
}

// GetOrganizationCount returns the total number of organizations.
func GetOrganizationCount(db sqlx.Queryer, search string) (int, error) {
	var count int

	if search != "" {
		search = "%" + search + "%"
	}

	err := sqlx.Get(db, &count, `
		select count(*)
		from organization
		where
			($1 != '' and display_name ilike $1)
			or ($1 = '')`,
		search,
	)
	if err != nil {
		return count, handlePSQLError(Select, err, "select error")
	}
	return count, nil
}

// GetOrganizationCountForUser returns the number of organizations to which
// the given user is member of.
func GetOrganizationCountForUser(db sqlx.Queryer, username string, search string) (int, error) {
	var count int

	if search != "" {
		search = "%" + search + "%"
	}

	err := sqlx.Get(db, &count, `
		select
			count(o.*)
		from organization o
		inner join organization_user ou
			on ou.organization_id = o.id
		inner join "user" u
			on u.id = ou.user_id
		where
			u.username = $1
			and (
				($2 != '' and o.display_name ilike $2)
				or ($2 = '')
			)`,
		username,
		search,
	)
	if err != nil {
		return count, handlePSQLError(Select, err, "select error")
	}
	return count, nil
}

// GetOrganizations returns a slice of organizations, sorted by name and
// respecting the given limit and offset.
func GetOrganizations(db sqlx.Queryer, limit, offset int, search string) ([]Organization, error) {
	var orgs []Organization

	if search != "" {
		search = "%" + search + "%"
	}

	err := sqlx.Select(db, &orgs, `
		select *
		from organization
		where
			($3 != '' and display_name ilike $3)
			or ($3 = '')
		order by display_name
		limit $1 offset $2`, limit, offset, search)
	if err != nil {
		return nil, handlePSQLError(Select, err, "select error")
	}
	return orgs, nil
}

// GetOrganizationsForUser returns a slice of organizations to which the given
// user is member of.
func GetOrganizationsForUser(db sqlx.Queryer, username string, limit, offset int, search string) ([]Organization, error) {
	var orgs []Organization

	if search != "" {
		search = "%" + search + "%"
	}

	err := sqlx.Select(db, &orgs, `
		select
			o.*
		from organization o
		inner join organization_user ou
			on ou.organization_id = o.id
		inner join "user" u
			on u.id = ou.user_id
		where
			u.username = $1
			and (
				($4 != '' and o.display_name ilike $4)
				or ($4 = '')
			)
		order by o.display_name
		limit $2 offset $3`,
		username,
		limit,
		offset,
		search,
	)
	if err != nil {
		return nil, handlePSQLError(Select, err, "select error")
	}
	return orgs, nil
}

// UpdateOrganization updates the given organization.
func UpdateOrganization(db sqlx.Execer, org *Organization) error {
	if err := org.Validate(); err != nil {
		return errors.Wrap(err, "validation error")
	}

	now := time.Now()
	res, err := db.Exec(`
		update organization
		set
			name = $2,
			display_name = $3,
			can_have_gateways = $4,
			updated_at = $5
		where id = $1`,
		org.ID,
		org.Name,
		org.DisplayName,
		org.CanHaveGateways,
		now,
	)

	if err != nil {
		return handlePSQLError(Update, err, "update error")
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return errors.Wrap(err, "get rows affected error")
	}
	if ra == 0 {
		return ErrDoesNotExist
	}

	org.UpdatedAt = now
	log.WithFields(log.Fields{
		"name": org.Name,
		"id":   org.ID,
	}).Info("organization updated")
	return nil
}

// DeleteOrganization deletes the organization matching the given id.
func DeleteOrganization(db sqlx.Ext, id int64) error {
	err := DeleteAllApplicationsForOrganizationID(db, id)
	if err != nil {
		return errors.Wrap(err, "delete all applications error")
	}

	err = DeleteAllServiceProfilesForOrganizationID(db, id)
	if err != nil {
		return errors.Wrap(err, "delete all service-profiles error")
	}

	err = DeleteAllDeviceProfilesForOrganizationID(db, id)
	if err != nil {
		return errors.Wrap(err, "delete all device-profiles error")
	}

	res, err := db.Exec("delete from organization where id = $1", id)
	if err != nil {
		return handlePSQLError(Delete, err, "delete error")
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return errors.Wrap(err, "get rows affected error")
	}
	if ra == 0 {
		return ErrDoesNotExist
	}

	log.WithField("id", id).Info("organization deleted")
	return nil
}

// CreateOrganizationUser adds the given user to the organization.
func CreateOrganizationUser(db sqlx.Execer, organizationID, userID int64, isAdmin bool) error {
	_, err := db.Exec(`
		insert into organization_user (
			organization_id,
			user_id,
			is_admin,
			created_at,
			updated_at
		) values ($1, $2, $3, now(), now())`,
		organizationID,
		userID,
		isAdmin,
	)
	if err != nil {
		return handlePSQLError(Insert, err, "insert error")
	}

	log.WithFields(log.Fields{
		"user_id":         userID,
		"organization_id": organizationID,
		"is_admin":        isAdmin,
	}).Info("user added to organization")
	return nil
}

// UpdateOrganizationUser updates the given user of the organization.
func UpdateOrganizationUser(db sqlx.Execer, organizationID, userID int64, isAdmin bool) error {
	res, err := db.Exec(`
		update organization_user
		set
			is_admin = $3
		where
			organization_id = $1
			and user_id = $2
	`, organizationID, userID, isAdmin)
	if err != nil {
		return handlePSQLError(Update, err, "update error")
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return errors.Wrap(err, "get rows affected error")
	}
	if ra == 0 {
		return ErrDoesNotExist
	}

	log.WithFields(log.Fields{
		"user_id":         userID,
		"organization_id": organizationID,
		"is_admin":        isAdmin,
	}).Info("organization user updated")
	return nil
}

// DeleteOrganizationUser deletes the given organization user.
func DeleteOrganizationUser(db sqlx.Execer, organizationID, userID int64) error {
	res, err := db.Exec(`delete from organization_user where organization_id = $1 and user_id = $2`, organizationID, userID)
	if err != nil {
		return handlePSQLError(Delete, err, "delete error")
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return errors.Wrap(err, "get rows affected error")
	}
	if ra == 0 {
		return ErrDoesNotExist
	}

	log.WithFields(log.Fields{
		"user_id":         userID,
		"organization_id": organizationID,
	}).Info("organization user deleted")
	return nil
}

// GetOrganizationUser gets the information of the given organization user.
func GetOrganizationUser(db sqlx.Queryer, organizationID, userID int64) (OrganizationUser, error) {
	var u OrganizationUser
	err := sqlx.Get(db, &u, `
		select
			u.id as user_id,
			u.username as username,
			ou.created_at as created_at,
			ou.updated_at as updated_at,
			ou.is_admin as is_admin
		from organization_user ou
		inner join "user" u
			on u.id = ou.user_id
		where
			ou.organization_id = $1
			and ou.user_id = $2`,
		organizationID,
		userID,
	)
	if err != nil {
		return u, handlePSQLError(Select, err, "select error")
	}
	return u, nil
}

// GetOrganizationUserCount returns the number of users for the given organization.
func GetOrganizationUserCount(db sqlx.Queryer, organizationID int64) (int, error) {
	var count int
	err := sqlx.Get(db, &count, `
		select count(*)
		from organization_user
		where
			organization_id = $1`,
		organizationID,
	)
	if err != nil {
		return count, handlePSQLError(Select, err, "select error")
	}
	return count, nil
}

// GetOrganizationUsers returns the users for the given organization.
func GetOrganizationUsers(db sqlx.Queryer, organizationID int64, limit, offset int) ([]OrganizationUser, error) {
	var users []OrganizationUser
	err := sqlx.Select(db, &users, `
		select
			u.id as user_id,
			u.username as username,
			ou.created_at as created_at,
			ou.updated_at as updated_at,
			ou.is_admin as is_admin
		from organization_user ou
		inner join "user" u
			on u.id = ou.user_id
		where
			ou.organization_id = $1
		order by u.username
		limit $2 offset $3`,
		organizationID,
		limit,
		offset,
	)
	if err != nil {
		return nil, handlePSQLError(Select, err, "select error")
	}
	return users, nil
}
<commit_msg>Prevent full tablescan on search.<commit_after>package storage

import (
	"regexp"
	"time"

	"github.com/jmoiron/sqlx"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)

var organizationNameRegexp = regexp.MustCompile(`^[\w-]+$`)

// Organization represents an organization.
type Organization struct {
	ID              int64     `db:"id"`
	CreatedAt       time.Time `db:"created_at"`
	UpdatedAt       time.Time `db:"updated_at"`
	Name            string    `db:"name"`
	DisplayName     string    `db:"display_name"`
	CanHaveGateways bool      `db:"can_have_gateways"`
}

// Validate validates the data of the Organization.
func (o Organization) Validate() error {
	if !organizationNameRegexp.MatchString(o.Name) {
		return ErrOrganizationInvalidName
	}
	return nil
}

// OrganizationUser represents an organization user.
type OrganizationUser struct {
	UserID    int64     `db:"user_id"`
	Username  string    `db:"username"`
	IsAdmin   bool      `db:"is_admin"`
	CreatedAt time.Time `db:"created_at"`
	UpdatedAt time.Time `db:"updated_at"`
}

// CreateOrganization creates the given Organization.
func CreateOrganization(db sqlx.Queryer, org *Organization) error {
	if err := org.Validate(); err != nil {
		return errors.Wrap(err, "validate error")
	}

	now := time.Now()

	err := sqlx.Get(db, &org.ID, `
		insert into organization (
			created_at,
			updated_at,
			name,
			display_name,
			can_have_gateways
		) values ($1, $2, $3, $4, $5) returning id`,
		now,
		now,
		org.Name,
		org.DisplayName,
		org.CanHaveGateways,
	)
	if err != nil {
		return handlePSQLError(Insert, err, "insert error")
	}
	org.CreatedAt = now
	org.UpdatedAt = now
	log.WithFields(log.Fields{
		"id":   org.ID,
		"name": org.Name,
	}).Info("organization created")
	return nil
}

// GetOrganization returns the Organization for the given id.
func GetOrganization(db sqlx.Queryer, id int64) (Organization, error) {
	var org Organization
	err := sqlx.Get(db, &org, "select * from organization where id = $1", id)
	if err != nil {
		return org, handlePSQLError(Select, err, "select error")
	}
	return org, nil
}

// GetOrganizationCount returns the total number of organizations.
func GetOrganizationCount(db sqlx.Queryer, search string) (int, error) {
	var count int

	if search != "" {
		search = search + "%"
	}

	err := sqlx.Get(db, &count, `
		select count(*)
		from organization
		where
			($1 != '' and display_name ilike $1)
			or ($1 = '')`,
		search,
	)
	if err != nil {
		return count, handlePSQLError(Select, err, "select error")
	}
	return count, nil
}

// GetOrganizationCountForUser returns the number of organizations to which
// the given user is member of.
func GetOrganizationCountForUser(db sqlx.Queryer, username string, search string) (int, error) {
	var count int

	if search != "" {
		search = search + "%"
	}

	err := sqlx.Get(db, &count, `
		select
			count(o.*)
		from organization o
		inner join organization_user ou
			on ou.organization_id = o.id
		inner join "user" u
			on u.id = ou.user_id
		where
			u.username = $1
			and (
				($2 != '' and o.display_name ilike $2)
				or ($2 = '')
			)`,
		username,
		search,
	)
	if err != nil {
		return count, handlePSQLError(Select, err, "select error")
	}
	return count, nil
}

// GetOrganizations returns a slice of organizations, sorted by name and
// respecting the given limit and offset.
func GetOrganizations(db sqlx.Queryer, limit, offset int, search string) ([]Organization, error) {
	var orgs []Organization

	if search != "" {
		search = search + "%"
	}

	err := sqlx.Select(db, &orgs, `
		select *
		from organization
		where
			($3 != '' and display_name ilike $3)
			or ($3 = '')
		order by display_name
		limit $1 offset $2`, limit, offset, search)
	if err != nil {
		return nil, handlePSQLError(Select, err, "select error")
	}
	return orgs, nil
}

// GetOrganizationsForUser returns a slice of organizations to which the given
// user is member of.
func GetOrganizationsForUser(db sqlx.Queryer, username string, limit, offset int, search string) ([]Organization, error) {
	var orgs []Organization

	if search != "" {
		search = search + "%"
	}

	err := sqlx.Select(db, &orgs, `
		select
			o.*
		from organization o
		inner join organization_user ou
			on ou.organization_id = o.id
		inner join "user" u
			on u.id = ou.user_id
		where
			u.username = $1
			and (
				($4 != '' and o.display_name ilike $4)
				or ($4 = '')
			)
		order by o.display_name
		limit $2 offset $3`,
		username,
		limit,
		offset,
		search,
	)
	if err != nil {
		return nil, handlePSQLError(Select, err, "select error")
	}
	return orgs, nil
}

// UpdateOrganization updates the given organization.
func UpdateOrganization(db sqlx.Execer, org *Organization) error {
	if err := org.Validate(); err != nil {
		return errors.Wrap(err, "validation error")
	}

	now := time.Now()
	res, err := db.Exec(`
		update organization
		set
			name = $2,
			display_name = $3,
			can_have_gateways = $4,
			updated_at = $5
		where id = $1`,
		org.ID,
		org.Name,
		org.DisplayName,
		org.CanHaveGateways,
		now,
	)

	if err != nil {
		return handlePSQLError(Update, err, "update error")
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return errors.Wrap(err, "get rows affected error")
	}
	if ra == 0 {
		return ErrDoesNotExist
	}

	org.UpdatedAt = now
	log.WithFields(log.Fields{
		"name": org.Name,
		"id":   org.ID,
	}).Info("organization updated")
	return nil
}

// DeleteOrganization deletes the organization matching the given id.
func DeleteOrganization(db sqlx.Ext, id int64) error {
	err := DeleteAllApplicationsForOrganizationID(db, id)
	if err != nil {
		return errors.Wrap(err, "delete all applications error")
	}

	err = DeleteAllServiceProfilesForOrganizationID(db, id)
	if err != nil {
		return errors.Wrap(err, "delete all service-profiles error")
	}

	err = DeleteAllDeviceProfilesForOrganizationID(db, id)
	if err != nil {
		return errors.Wrap(err, "delete all device-profiles error")
	}

	res, err := db.Exec("delete from organization where id = $1", id)
	if err != nil {
		return handlePSQLError(Delete, err, "delete error")
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return errors.Wrap(err, "get rows affected error")
	}
	if ra == 0 {
		return ErrDoesNotExist
	}

	log.WithField("id", id).Info("organization deleted")
	return nil
}

// CreateOrganizationUser adds the given user to the organization.
func CreateOrganizationUser(db sqlx.Execer, organizationID, userID int64, isAdmin bool) error {
	_, err := db.Exec(`
		insert into organization_user (
			organization_id,
			user_id,
			is_admin,
			created_at,
			updated_at
		) values ($1, $2, $3, now(), now())`,
		organizationID,
		userID,
		isAdmin,
	)
	if err != nil {
		return handlePSQLError(Insert, err, "insert error")
	}

	log.WithFields(log.Fields{
		"user_id":         userID,
		"organization_id": organizationID,
		"is_admin":        isAdmin,
	}).Info("user added to organization")
	return nil
}

// UpdateOrganizationUser updates the given user of the organization.
func UpdateOrganizationUser(db sqlx.Execer, organizationID, userID int64, isAdmin bool) error {
	res, err := db.Exec(`
		update organization_user
		set
			is_admin = $3
		where
			organization_id = $1
			and user_id = $2
	`, organizationID, userID, isAdmin)
	if err != nil {
		return handlePSQLError(Update, err, "update error")
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return errors.Wrap(err, "get rows affected error")
	}
	if ra == 0 {
		return ErrDoesNotExist
	}

	log.WithFields(log.Fields{
		"user_id":         userID,
		"organization_id": organizationID,
		"is_admin":        isAdmin,
	}).Info("organization user updated")
	return nil
}

// DeleteOrganizationUser deletes the given organization user.
func DeleteOrganizationUser(db sqlx.Execer, organizationID, userID int64) error {
	res, err := db.Exec(`delete from organization_user where organization_id = $1 and user_id = $2`, organizationID, userID)
	if err != nil {
		return handlePSQLError(Delete, err, "delete error")
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return errors.Wrap(err, "get rows affected error")
	}
	if ra == 0 {
		return ErrDoesNotExist
	}

	log.WithFields(log.Fields{
		"user_id":         userID,
		"organization_id": organizationID,
	}).Info("organization user deleted")
	return nil
}

// GetOrganizationUser gets the information of the given organization user.
func GetOrganizationUser(db sqlx.Queryer, organizationID, userID int64) (OrganizationUser, error) {
	var u OrganizationUser
	err := sqlx.Get(db, &u, `
		select
			u.id as user_id,
			u.username as username,
			ou.created_at as created_at,
			ou.updated_at as updated_at,
			ou.is_admin as is_admin
		from organization_user ou
		inner join "user" u
			on u.id = ou.user_id
		where
			ou.organization_id = $1
			and ou.user_id = $2`,
		organizationID,
		userID,
	)
	if err != nil {
		return u, handlePSQLError(Select, err, "select error")
	}
	return u, nil
}

// GetOrganizationUserCount returns the number of users for the given organization.
func GetOrganizationUserCount(db sqlx.Queryer, organizationID int64) (int, error) {
	var count int
	err := sqlx.Get(db, &count, `
		select count(*)
		from organization_user
		where
			organization_id = $1`,
		organizationID,
	)
	if err != nil {
		return count, handlePSQLError(Select, err, "select error")
	}
	return count, nil
}

// GetOrganizationUsers returns the users for the given organization.
func GetOrganizationUsers(db sqlx.Queryer, organizationID int64, limit, offset int) ([]OrganizationUser, error) {
	var users []OrganizationUser
	err := sqlx.Select(db, &users, `
		select
			u.id as user_id,
			u.username as username,
			ou.created_at as created_at,
			ou.updated_at as updated_at,
			ou.is_admin as is_admin
		from organization_user ou
		inner join "user" u
			on u.id = ou.user_id
		where
			ou.organization_id = $1
		order by u.username
		limit $2 offset $3`,
		organizationID,
		limit,
		offset,
	)
	if err != nil {
		return nil, handlePSQLError(Select, err, "select error")
	}
	return users, nil
}
<|endoftext|>


<commit_before>package termstatus

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"strings"

	"golang.org/x/crypto/ssh/terminal"
)

// Terminal is used to write messages and display status lines which can be
// updated. When the output is redirected to a file, the status lines are not
// printed.
type Terminal struct {
	wr              *bufio.Writer
	fd              uintptr
	errWriter       io.Writer
	buf             *bytes.Buffer
	msg             chan message
	status          chan status
	canUpdateStatus bool

	// will be closed when the goroutine which runs Run() terminates, so it'll
	// yield a default value immediately
	closed chan struct{}

	clearCurrentLine func(io.Writer, uintptr)
	moveCursorUp     func(io.Writer, uintptr, int)
}

type message struct {
	line string
	err  bool
}

type status struct {
	lines []string
}

type fder interface {
	Fd() uintptr
}

// New returns a new Terminal for wr. A goroutine is started to update the
// terminal. It is terminated when ctx is cancelled. When wr is redirected to
// a file (e.g. via shell output redirection) or is just an io.Writer (not the
// open *os.File for stdout), no status lines are printed. The status lines and
// normal output (via Print/Printf) are written to wr, error messages are
// written to errWriter. If disableStatus is set to true, no status messages
// are printed even if the terminal supports it.
func New(wr io.Writer, errWriter io.Writer, disableStatus bool) *Terminal {
	t := &Terminal{
		wr:        bufio.NewWriter(wr),
		errWriter: errWriter,
		buf:       bytes.NewBuffer(nil),
		msg:       make(chan message),
		status:    make(chan status),
		closed:    make(chan struct{}),
	}

	if disableStatus {
		return t
	}

	if d, ok := wr.(fder); ok && canUpdateStatus(d.Fd()) {
		// only use the fancy status code when we're running on a real terminal.
		t.canUpdateStatus = true
		t.fd = d.Fd()
		t.clearCurrentLine = clearCurrentLine(wr, t.fd)
		t.moveCursorUp = moveCursorUp(wr, t.fd)
	}

	return t
}

// Run updates the screen. It should be run in a separate goroutine. When
// ctx is cancelled, the status lines are cleanly removed.
func (t *Terminal) Run(ctx context.Context) {
	defer close(t.closed)
	if t.canUpdateStatus {
		t.run(ctx)
		return
	}

	t.runWithoutStatus(ctx)
}

type stringWriter interface {
	WriteString(string) (int, error)
}

// run listens on the channels and updates the terminal screen.
func (t *Terminal) run(ctx context.Context) {
	var status []string
	for {
		select {
		case <-ctx.Done():
			if IsProcessBackground() {
				// ignore all messages, do nothing, we are in the background process group
				continue
			}
			t.undoStatus(len(status))

			return

		case msg := <-t.msg:
			if IsProcessBackground() {
				// ignore all messages, do nothing, we are in the background process group
				continue
			}
			t.clearCurrentLine(t.wr, t.fd)

			var dst io.Writer
			if msg.err {
				dst = t.errWriter

				// assume t.wr and t.errWriter are different, so we need to
				// flush clearing the current line
				err := t.wr.Flush()
				if err != nil {
					fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
				}
			} else {
				dst = t.wr
			}

			var err error
			if w, ok := dst.(stringWriter); ok {
				_, err = w.WriteString(msg.line)
			} else {
				_, err = dst.Write([]byte(msg.line))
			}

			if err != nil {
				fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
				continue
			}

			t.writeStatus(status)

			err = t.wr.Flush()
			if err != nil {
				fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
			}

		case stat := <-t.status:
			if IsProcessBackground() {
				// ignore all messages, do nothing, we are in the background process group
				continue
			}

			status = status[:0]
			status = append(status, stat.lines...)
			t.writeStatus(status)
		}
	}
}

func (t *Terminal) writeStatus(status []string) {
	for _, line := range status {
		t.clearCurrentLine(t.wr, t.fd)

		_, err := t.wr.WriteString(line)
		if err != nil {
			fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
		}

		// flush is needed so that the current line is updated
		err = t.wr.Flush()
		if err != nil {
			fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
		}
	}

	if len(status) > 0 {
		t.moveCursorUp(t.wr, t.fd, len(status)-1)
	}

	err := t.wr.Flush()
	if err != nil {
		fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
	}
}

// runWithoutStatus listens on the channels and just prints out the messages,
// without status lines.
func (t *Terminal) runWithoutStatus(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		case msg := <-t.msg:
			var err error
			var flush func() error

			var dst io.Writer
			if msg.err {
				dst = t.errWriter
			} else {
				dst = t.wr
				flush = t.wr.Flush
			}

			if w, ok := dst.(stringWriter); ok {
				_, err = w.WriteString(msg.line)
			} else {
				_, err = dst.Write([]byte(msg.line))
			}

			if err != nil {
				fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
			}

			if flush == nil {
				continue
			}

			err = flush()
			if err != nil {
				fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
			}

		case _ = <-t.status:
			// discard status lines
		}
	}
}

func (t *Terminal) undoStatus(lines int) {
	for i := 0; i < lines; i++ {
		t.clearCurrentLine(t.wr, t.fd)

		_, err := t.wr.WriteRune('\n')
		if err != nil {
			fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
		}

		// flush is needed so that the current line is updated
		err = t.wr.Flush()
		if err != nil {
			fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
		}
	}

	t.moveCursorUp(t.wr, t.fd, lines)

	err := t.wr.Flush()
	if err != nil {
		fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
	}
}

// Print writes a line to the terminal.
func (t *Terminal) Print(line string) {
	// make sure the line ends with a line break
	if line[len(line)-1] != '\n' {
		line += "\n"
	}

	select {
	case t.msg <- message{line: line}:
	case <-t.closed:
	}
}

// Printf uses fmt.Sprintf to write a line to the terminal.
func (t *Terminal) Printf(msg string, args ...interface{}) {
	s := fmt.Sprintf(msg, args...)
	t.Print(s)
}

// Error writes an error to the terminal.
func (t *Terminal) Error(line string) {
	// make sure the line ends with a line break
	if line[len(line)-1] != '\n' {
		line += "\n"
	}

	select {
	case t.msg <- message{line: line, err: true}:
	case <-t.closed:
	}
}

// Errorf uses fmt.Sprintf to write an error line to the terminal.
func (t *Terminal) Errorf(msg string, args ...interface{}) {
	s := fmt.Sprintf(msg, args...)
	t.Error(s)
}

// truncate returns a string that has at most maxlen characters. If maxlen is
// negative, the empty string is returned.
func truncate(s string, maxlen int) string {
	if maxlen < 0 {
		return ""
	}

	if len(s) < maxlen {
		return s
	}

	return s[:maxlen]
}

// SetStatus updates the status lines.
func (t *Terminal) SetStatus(lines []string) {
	if len(lines) == 0 {
		return
	}

	width, _, err := terminal.GetSize(int(t.fd))
	if err != nil || width <= 0 {
		// use 80 columns by default
		width = 80
	}

	// make sure that all lines have a line break and are not too long
	for i, line := range lines {
		line = strings.TrimRight(line, "\n")
		line = truncate(line, width-2) + "\n"
		lines[i] = line
	}

	// make sure the last line does not have a line break
	last := len(lines) - 1
	lines[last] = strings.TrimRight(lines[last], "\n")

	select {
	case t.status <- status{lines: lines}:
	case <-t.closed:
	}
}
<commit_msg>termstatus: Use io.WriteString to output messages.<commit_after>package termstatus

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"strings"

	"golang.org/x/crypto/ssh/terminal"
)

// Terminal is used to write messages and display status lines which can be
// updated. When the output is redirected to a file, the status lines are not
// printed.
type Terminal struct {
	wr              *bufio.Writer
	fd              uintptr
	errWriter       io.Writer
	buf             *bytes.Buffer
	msg             chan message
	status          chan status
	canUpdateStatus bool

	// will be closed when the goroutine which runs Run() terminates, so it'll
	// yield a default value immediately
	closed chan struct{}

	clearCurrentLine func(io.Writer, uintptr)
	moveCursorUp     func(io.Writer, uintptr, int)
}

type message struct {
	line string
	err  bool
}

type status struct {
	lines []string
}

type fder interface {
	Fd() uintptr
}

// New returns a new Terminal for wr. A goroutine is started to update the
// terminal. It is terminated when ctx is cancelled. When wr is redirected to
// a file (e.g. via shell output redirection) or is just an io.Writer (not the
// open *os.File for stdout), no status lines are printed. The status lines and
// normal output (via Print/Printf) are written to wr, error messages are
// written to errWriter. If disableStatus is set to true, no status messages
// are printed even if the terminal supports it.
func New(wr io.Writer, errWriter io.Writer, disableStatus bool) *Terminal {
	t := &Terminal{
		wr:        bufio.NewWriter(wr),
		errWriter: errWriter,
		buf:       bytes.NewBuffer(nil),
		msg:       make(chan message),
		status:    make(chan status),
		closed:    make(chan struct{}),
	}

	if disableStatus {
		return t
	}

	if d, ok := wr.(fder); ok && canUpdateStatus(d.Fd()) {
		// only use the fancy status code when we're running on a real terminal.
		t.canUpdateStatus = true
		t.fd = d.Fd()
		t.clearCurrentLine = clearCurrentLine(wr, t.fd)
		t.moveCursorUp = moveCursorUp(wr, t.fd)
	}

	return t
}

// Run updates the screen. It should be run in a separate goroutine. When
// ctx is cancelled, the status lines are cleanly removed.
func (t *Terminal) Run(ctx context.Context) {
	defer close(t.closed)
	if t.canUpdateStatus {
		t.run(ctx)
		return
	}

	t.runWithoutStatus(ctx)
}

// run listens on the channels and updates the terminal screen.
func (t *Terminal) run(ctx context.Context) {
	var status []string
	for {
		select {
		case <-ctx.Done():
			if IsProcessBackground() {
				// ignore all messages, do nothing, we are in the background process group
				continue
			}
			t.undoStatus(len(status))

			return

		case msg := <-t.msg:
			if IsProcessBackground() {
				// ignore all messages, do nothing, we are in the background process group
				continue
			}
			t.clearCurrentLine(t.wr, t.fd)

			var dst io.Writer
			if msg.err {
				dst = t.errWriter

				// assume t.wr and t.errWriter are different, so we need to
				// flush clearing the current line
				err := t.wr.Flush()
				if err != nil {
					fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
				}
			} else {
				dst = t.wr
			}

			if _, err := io.WriteString(dst, msg.line); err != nil {
				fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
				continue
			}

			t.writeStatus(status)

			if err := t.wr.Flush(); err != nil {
				fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
			}

		case stat := <-t.status:
			if IsProcessBackground() {
				// ignore all messages, do nothing, we are in the background process group
				continue
			}

			status = status[:0]
			status = append(status, stat.lines...)
			t.writeStatus(status)
		}
	}
}

func (t *Terminal) writeStatus(status []string) {
	for _, line := range status {
		t.clearCurrentLine(t.wr, t.fd)

		_, err := t.wr.WriteString(line)
		if err != nil {
			fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
		}

		// flush is needed so that the current line is updated
		err = t.wr.Flush()
		if err != nil {
			fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
		}
	}

	if len(status) > 0 {
		t.moveCursorUp(t.wr, t.fd, len(status)-1)
	}

	err := t.wr.Flush()
	if err != nil {
		fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
	}
}

// runWithoutStatus listens on the channels and just prints out the messages,
// without status lines.
func (t *Terminal) runWithoutStatus(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		case msg := <-t.msg:
			var flush func() error

			var dst io.Writer
			if msg.err {
				dst = t.errWriter
			} else {
				dst = t.wr
				flush = t.wr.Flush
			}

			if _, err := io.WriteString(dst, msg.line); err != nil {
				fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
			}

			if flush == nil {
				continue
			}

			if err := flush(); err != nil {
				fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
			}

		case _ = <-t.status:
			// discard status lines
		}
	}
}

func (t *Terminal) undoStatus(lines int) {
	for i := 0; i < lines; i++ {
		t.clearCurrentLine(t.wr, t.fd)

		_, err := t.wr.WriteRune('\n')
		if err != nil {
			fmt.Fprintf(os.Stderr, "write failed: %v\n", err)
		}

		// flush is needed so that the current line is updated
		err = t.wr.Flush()
		if err != nil {
			fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
		}
	}

	t.moveCursorUp(t.wr, t.fd, lines)

	err := t.wr.Flush()
	if err != nil {
		fmt.Fprintf(os.Stderr, "flush failed: %v\n", err)
	}
}

// Print writes a line to the terminal.
func (t *Terminal) Print(line string) {
	// make sure the line ends with a line break
	if line[len(line)-1] != '\n' {
		line += "\n"
	}

	select {
	case t.msg <- message{line: line}:
	case <-t.closed:
	}
}

// Printf uses fmt.Sprintf to write a line to the terminal.
func (t *Terminal) Printf(msg string, args ...interface{}) {
	s := fmt.Sprintf(msg, args...)
	t.Print(s)
}

// Error writes an error to the terminal.
func (t *Terminal) Error(line string) {
	// make sure the line ends with a line break
	if line[len(line)-1] != '\n' {
		line += "\n"
	}

	select {
	case t.msg <- message{line: line, err: true}:
	case <-t.closed:
	}
}

// Errorf uses fmt.Sprintf to write an error line to the terminal.
func (t *Terminal) Errorf(msg string, args ...interface{}) {
	s := fmt.Sprintf(msg, args...)
	t.Error(s)
}

// truncate returns a string that has at most maxlen characters. If maxlen is
// negative, the empty string is returned.
func truncate(s string, maxlen int) string {
	if maxlen < 0 {
		return ""
	}

	if len(s) < maxlen {
		return s
	}

	return s[:maxlen]
}

// SetStatus updates the status lines.
func (t *Terminal) SetStatus(lines []string) {
	if len(lines) == 0 {
		return
	}

	width, _, err := terminal.GetSize(int(t.fd))
	if err != nil || width <= 0 {
		// use 80 columns by default
		width = 80
	}

	// make sure that all lines have a line break and are not too long
	for i, line := range lines {
		line = strings.TrimRight(line, "\n")
		line = truncate(line, width-2) + "\n"
		lines[i] = line
	}

	// make sure the last line does not have a line break
	last := len(lines) - 1
	lines[last] = strings.TrimRight(lines[last], "\n")

	select {
	case t.status <- status{lines: lines}:
	case <-t.closed:
	}
}
<|endoftext|>


<commit_before>package input

import (
	"fmt"
	"io"
	"os"
	"os/signal"
	"strings"
)

// readOptions is option for read func
type readOptions struct {
	// mask hides user input and will be matched by maskVal.
	mask    bool
	maskVal string
}

// read reads input from UI.Reader
func (i *UI) read(opts *readOptions) (string, error) {
	i.once.Do(i.setDefault)

	// sigCh is channel which is watch Interruptted signal (SIGINT)
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, os.Interrupt)
	defer signal.Stop(sigCh)

	var resultStr string
	var resultErr error
	doneCh := make(chan struct{})

	go func() {
		defer close(doneCh)

		if opts.mask {
			f, ok := i.Reader.(*os.File)
			if !ok {
				resultErr = fmt.Errorf("reader must be a file")
				return
			}

			i.mask, i.maskVal = opts.mask, opts.maskVal
			resultStr, resultErr = i.rawRead(f)
		} else {
			line, err := i.bReader.ReadString('\n')
			if err != nil && err != io.EOF {
				resultErr = fmt.Errorf("failed to read the input: %s", err)
			}

			resultStr = strings.TrimSuffix(line, "\n")
		}
	}()

	select {
	case <-sigCh:
		return "", ErrInterrupted
	case <-doneCh:
		return resultStr, resultErr
	}
}

// rawReadline tries to return a single line, not including the end-of-line
// bytes with raw Mode (without prompting nothing). Or if provided show some
// value instead of actual value.
func (i *UI) rawReadline(f *os.File) (string, error) {
	var resultBuf []byte
	for {
		var buf [1]byte
		n, err := f.Read(buf[:])
		if err != nil && err != io.EOF {
			return "", err
		}

		if n == 0 || buf[0] == '\n' || buf[0] == '\r' {
			break
		}

		if buf[0] == 3 {
			return "", ErrInterrupted
		}

		if i.mask {
			fmt.Fprintf(i.Writer, i.maskVal)
		}

		resultBuf = append(resultBuf, buf[0])
	}

	fmt.Fprintf(i.Writer, "\n")
	return string(resultBuf), nil
}
<commit_msg>fix windows compat<commit_after>package input

import (
	"fmt"
	"io"
	"os"
	"os/signal"
	"strings"
)

// readOptions is option for read func
type readOptions struct {
	// mask hides user input and will be matched by maskVal.
	mask    bool
	maskVal string
}

// read reads input from UI.Reader
func (i *UI) read(opts *readOptions) (string, error) {
	i.once.Do(i.setDefault)

	// sigCh is channel which is watch Interruptted signal (SIGINT)
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, os.Interrupt)
	defer signal.Stop(sigCh)

	var resultStr string
	var resultErr error
	doneCh := make(chan struct{})

	go func() {
		defer close(doneCh)

		if opts.mask {
			f, ok := i.Reader.(*os.File)
			if !ok {
				resultErr = fmt.Errorf("reader must be a file")
				return
			}

			i.mask, i.maskVal = opts.mask, opts.maskVal
			resultStr, resultErr = i.rawRead(f)
		} else {
			line, err := i.bReader.ReadString('\n')
			if err != nil && err != io.EOF {
				resultErr = fmt.Errorf("failed to read the input: %s", err)
			}

			resultStr = strings.TrimSuffix(line, "\r\n")
		}
	}()

	select {
	case <-sigCh:
		return "", ErrInterrupted
	case <-doneCh:
		return resultStr, resultErr
	}
}

// rawReadline tries to return a single line, not including the end-of-line
// bytes with raw Mode (without prompting nothing). Or if provided show some
// value instead of actual value.
func (i *UI) rawReadline(f *os.File) (string, error) {
	var resultBuf []byte
	for {
		var buf [1]byte
		n, err := f.Read(buf[:])
		if err != nil && err != io.EOF {
			return "", err
		}

		if n == 0 || buf[0] == '\n' || buf[0] == '\r' {
			break
		}

		if buf[0] == 3 {
			return "", ErrInterrupted
		}

		if i.mask {
			fmt.Fprintf(i.Writer, i.maskVal)
		}

		resultBuf = append(resultBuf, buf[0])
	}

	fmt.Fprintf(i.Writer, "\n")
	return string(resultBuf), nil
}
<|endoftext|>


<commit_before>package controllers

import (
	"encoding/json"
	"github.com/boltdb/bolt"
	"github.com/revel/revel"
	"islakemendotafrozen.com/app/models"
	"net/http"
)

type LakeStatus struct {
	BoltController
}

func (c LakeStatus) Show(name string) revel.Result {
	lake := c.GetLake(name)
	c.Response.Status = http.StatusOK
	return c.RenderJson(lake)
}

func (c LakeStatus) CanUpdate() bool {
	auth_required := revel.Config.BoolDefault("auth_required", true)
	if auth_required {
		// TODO check headers for params
		return false
	} else {
		return true
	}
}

func (c LakeStatus) Create(name string) revel.Result {
	lake := new(models.Lake)
	decode_err := json.NewDecoder(c.Request.Body).Decode(&lake)
	if decode_err != nil {
		revel.ERROR.Printf("Could not decide JSON request body")
	} else {
		lake.Name = name
	}

	revel.INFO.Printf("[POST] [Deserialized] Lake: %s, Season: %s, IsFrozen: %t", lake.Name, lake.Season, lake.IsFrozen)

	if c.CanUpdate() {
		err := c.DB.Update(func(tx *bolt.Tx) error {
			bucket := tx.Bucket([]byte(name))
			err := bucket.Put([]byte("season"), []byte(lake.Season))

			is_frozen := "false"
			if lake.IsFrozen {
				is_frozen = "true"
			}
			err = bucket.Put([]byte("frozen"), []byte(is_frozen))
			return err
		})
		if err != nil {
			revel.ERROR.Fatal("Could not update lake status.", err)
		}

		c.Response.Status = http.StatusOK
		return c.RenderJson(lake)
	}

	c.Response.Status = http.StatusUnauthorized
	return c.RenderText("")
}
<commit_msg>Add janky authentication to protect the POST route<commit_after>package controllers

import (
	"errors"
	"encoding/json"
	"github.com/boltdb/bolt"
	"github.com/revel/revel"
	"islakemendotafrozen.com/app/models"
	"net/http"
)

type LakeStatus struct {
	BoltController
}

func (c LakeStatus) Show(name string) revel.Result {
	lake := c.GetLake(name)
	c.Response.Status = http.StatusOK
	return c.RenderJson(lake)
}

func (c LakeStatus) CanUpdate() bool {
	authRequired := revel.Config.BoolDefault("auth.required", true)
	authorized := false

	if authRequired {
		correctUsername, _ := revel.Config.String("auth.user")
		correctPassword, _ := revel.Config.String("auth.password")

		suppliedUsername := c.Request.Header.Get("X-Authorization-User")
		suppliedPassword := c.Request.Header.Get("X-Authorization-Password")

		if correctUsername == suppliedUsername && correctPassword == suppliedPassword {
			authorized = true
		}
	} else {
		authorized = true
	}

	return authorized
}

func (c LakeStatus) Create(name string) revel.Result {
	lake := new(models.Lake)
	decode_err := json.NewDecoder(c.Request.Body).Decode(&lake)
	if decode_err != nil {
		revel.ERROR.Printf("Could not decide JSON request body")
	} else {
		lake.Name = name
	}

	revel.INFO.Printf("[POST] [Deserialized] Lake: %s, Season: %s, IsFrozen: %t", lake.Name, lake.Season, lake.IsFrozen)

	if c.CanUpdate() {
		err := c.DB.Update(func(tx *bolt.Tx) error {
			bucket := tx.Bucket([]byte(name))
			err := bucket.Put([]byte("season"), []byte(lake.Season))

			is_frozen := "false"
			if lake.IsFrozen {
				is_frozen = "true"
			}
			err = bucket.Put([]byte("frozen"), []byte(is_frozen))
			return err
		})
		if err != nil {
			revel.ERROR.Fatal("Could not update lake status.", err)
		}

		c.Response.Status = http.StatusOK
		return c.RenderJson(lake)
	}

	c.Response.Status = http.StatusUnauthorized
	c.Response.Out.Header().Set("WWW-Authenticate", `Basic realm="revel"`)
	return c.RenderError(errors.New("401: Not authorized"))
}
<|endoftext|>


<commit_before>package main

// TODO:
// - to store icons, see: http://blogs.msdn.com/b/oldnewthing/archive/2012/07/20/10331787.aspx
//   - also need to first read and split input ico file

import (
	"debug/pe"
	"encoding/binary"
	"flag"
	"fmt"
	"io"
	"os"
	"reflect"
	"regexp"
	"strconv"

	"github.com/akavel/rsrc/ico"
)

type Dir struct { // struct IMAGE_RESOURCE_DIRECTORY
	Characteristics      uint32
	TimeDateStamp        uint32
	MajorVersion         uint16
	MinorVersion         uint16
	NumberOfNamedEntries uint16
	NumberOfIdEntries    uint16
	DirEntries
	Dirs
}

type DirEntries []DirEntry
type Dirs []Dir

type DirEntry struct { // struct IMAGE_RESOURCE_DIRECTORY_ENTRY
	NameOrId     uint32
	OffsetToData uint32
}

type DataEntry struct { // struct IMAGE_RESOURCE_DATA_ENTRY
	OffsetToData uint32
	Size1        uint32
	CodePage     uint32
	Reserved     uint32
}

type RelocationEntry struct {
	RVA         uint32 // "offset within the Section's raw data where the address starts."
	SymbolIndex uint32 // "(zero based) index in the Symbol table to which the reference refers."
	Type        uint16
}

type Symbol struct {
	Name           [8]byte
	Value          uint32
	SectionNumber  uint16
	Type           uint16
	StorageClass   uint8
	AuxiliaryCount uint8
}

type StringsHeader struct {
	Length uint32
}

const (
	MASK_SUBDIRECTORY = 1 << 31

	RT_ICON       = 3
	RT_GROUP_ICON = 3 + 11
	RT_MANIFEST   = 24
)

var (
	STRING_RSRC = [8]byte{'.', 'r', 's', 'r', 'c', 0, 0, 0}

	LANG_ENTRY  = DirEntries{{NameOrId: 0x0409}} //FIXME: language; what value should be here?
	RELOC_ENTRY = RelocationEntry{
		SymbolIndex: 0, // "(zero based) index in the Symbol table to which the reference refers. Once you have loaded the COFF file into memory and know where each symbol is, you find the new updated address for the given symbol and update the reference accordingly."
		Type:        7, // according to ldpe.c, this decodes to: IMAGE_REL_I386_DIR32NB
	}

	RE = regexp.MustCompile
)

type GRPICONDIR struct {
	ico.ICONDIR
	Entries []GRPICONDIRENTRY
}

type GRPICONDIRENTRY struct {
	ico.IconDirEntryCommon
	Id uint16
}

func main() {
	//TODO: allow in options advanced specification of multiple resources, as a tree (json?)
	var fnamein, fnameico, fnameout string
	flags := flag.NewFlagSet("", flag.ContinueOnError)
	flags.StringVar(&fnamein, "manifest", "", "path to a Windows manifest file to embed")
	flags.StringVar(&fnameico, "ico", "", "UNSUPPORTED: path to ICO file to embed")
	flags.StringVar(&fnameout, "o", "rsrc.syso", "name of output COFF (.res or .syso) file")
	_ = flags.Parse(os.Args[1:])
	if fnamein == "" {
		fmt.Fprintf(os.Stderr, "USAGE: %s -manifest FILE.exe.manifest [-o FILE.syso]\n"+
			"Generates a .syso file with specified resources embedded in .rsrc section,\n"+
			"aimed for consumption by Go linker when building Win32 excecutables.\n"+
			"OPTIONS:\n",
			os.Args[0])
		flags.PrintDefaults()
		os.Exit(1)
	}

	err := run(fnamein, fnameico, fnameout)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

type Coff struct {
	pe.FileHeader
	pe.SectionHeader32

	Dir
	DataEntries []DataEntry
	Data        []interface{}

	Relocations []RelocationEntry
	Symbols     []Symbol
	StringsHeader
}

func run(fnamein, fnameico, fnameout string) error {
	manifest, err := SizedOpen(fnamein)
	if err != nil {
		return fmt.Errorf("Error opening manifest file '%s': %s", fnamein, err)
	}
	defer manifest.Close()

	var icons []ico.ICONDIRENTRY
	var iconsf *os.File
	if fnameico != "" {
		iconsf, err = os.Open(fnameico)
		if err != nil {
			return err
		}
		defer iconsf.Close()
		icons, err = ico.DecodeHeaders(iconsf)
		if err != nil {
			return err
		}
	}

	newid := make(chan uint16)
	go func() {
		for i := uint16(1); ; i++ {
			newid <- i
		}
	}()

	out, err := os.Create(fnameout)
	if err != nil {
		return err
	}
	defer out.Close()
	w := Writer{W: out}

	coff := Coff{
		pe.FileHeader{
			Machine:              0x014c, //FIXME: find out how to differentiate this value, or maybe not necessary for Go
			NumberOfSections:     1,      // .rsrc
			TimeDateStamp:        0,      // was also 0 in sample data from MinGW's windres.exe
			NumberOfSymbols:      1,
			SizeOfOptionalHeader: 0,
			Characteristics:      0x0104, //FIXME: copied from windres.exe output, find out what should be here and why
		},
		pe.SectionHeader32{
			Name:                STRING_RSRC,
			NumberOfRelocations: 1,
			Characteristics:     0x40000040, // "INITIALIZED_DATA MEM_READ" ?
		},

		// now, build "directory hierarchy" of .rsrc section: first type, then id/name, then language
		Dir{
			NumberOfIdEntries: 1,
			DirEntries:        DirEntries{{NameOrId: RT_MANIFEST}},
			Dirs: Dirs{{
				NumberOfIdEntries: 1,
				DirEntries:        DirEntries{{NameOrId: uint32(<-newid)}}, // resource ID
				Dirs: Dirs{{
					NumberOfIdEntries: 1,
					DirEntries:        LANG_ENTRY,
				}},
			}},
		},
		[]DataEntry{
			DataEntry{
				Size1:    uint32(manifest.Size()),
				CodePage: 0, //FIXME: what value here? for now just tried 0 - TODO: fix also in other uses
			},
		},
		[]interface{}{
			manifest,
		},

		[]RelocationEntry{
			RELOC_ENTRY,
		},

		[]Symbol{Symbol{
			Name:           STRING_RSRC,
			Value:          0,
			SectionNumber:  1,
			Type:           0, // FIXME: wtf?
			StorageClass:   3, // FIXME: is it ok? and uint8? and what does the value mean?
			AuxiliaryCount: 0, // FIXME: wtf?
		}},

		StringsHeader{
			Length: uint32(binary.Size(StringsHeader{})), // empty strings table -- but we must still show size of the table's header...
		},
	}

	if len(icons) > 0 {
		//FIXME: add corresponding DataEntries
		//FIXME: add relocations

		coff.Dir.NumberOfIdEntries += 2
		coff.Dir.DirEntries = append(coff.Dir.DirEntries, DirEntry{NameOrId: RT_ICON})
		coff.Dir.DirEntries = append(coff.Dir.DirEntries, DirEntry{NameOrId: RT_GROUP_ICON})

		// RT_ICONs
		coff.Dir.Dirs = append(coff.Dir.Dirs, Dir{
			NumberOfIdEntries: uint16(len(icons)),
		})
		dir := &coff.Dir.Dirs[len(coff.Dir.Dirs)-1]
		group := GRPICONDIR{ICONDIR: ico.ICONDIR{
			Reserved: 0, // magic num.
			Type:     1, // magic num.
			Count:    uint16(len(icons)),
		}}
		for _, icon := range icons {
			id := <-newid

			dir.DirEntries = append(dir.DirEntries, DirEntry{NameOrId: uint32(id)})
			dir.Dirs = append(dir.Dirs, Dir{
				NumberOfIdEntries: 1,
				DirEntries:        LANG_ENTRY,
			})

			r := io.NewSectionReader(iconsf, int64(icon.ImageOffset), int64(icon.BytesInRes))
			coff.DataEntries = append(coff.DataEntries, DataEntry{Size1: uint32(r.Size())})
			coff.Relocations = append(coff.Relocations, RELOC_ENTRY)
			coff.SectionHeader32.NumberOfRelocations++
			coff.Data = append(coff.Data, r)

			group.Entries = append(group.Entries, GRPICONDIRENTRY{icon.IconDirEntryCommon, id})
		}

		coff.Dir.Dirs = append(coff.Dir.Dirs, Dir{
			NumberOfIdEntries: 1,
			DirEntries:        DirEntries{{NameOrId: uint32(<-newid)}},
			Dirs: Dirs{{
				NumberOfIdEntries: 1,
				DirEntries:        LANG_ENTRY,
			}},
		})
		coff.DataEntries = append(coff.DataEntries, DataEntry{
			Size1: uint32(binary.Size(group.ICONDIR) + len(icons)*binary.Size(group.Entries[0])),
		})
		coff.Relocations = append(coff.Relocations, RELOC_ENTRY)
		coff.SectionHeader32.NumberOfRelocations++
		coff.Data = append(coff.Data, group)

	}

	leafwalker := make(chan *DirEntry)
	go func() {
		for _, dir1 := range coff.Dir.Dirs { // resource type
			for _, dir2 := range dir1.Dirs { // resource ID
				for i := range dir2.DirEntries { // resource lang
					leafwalker <- &dir2.DirEntries[i]
				}
			}
		}
	}()

	// fill in some important offsets in resulting file
	var offset, diroff uint32
	Walk(coff, func(v reflect.Value, path string) error {
		switch path {
		case "/Dir":
			coff.SectionHeader32.PointerToRawData = offset
			diroff = offset
		case "/Relocations":
			coff.SectionHeader32.PointerToRelocations = offset
			coff.SectionHeader32.SizeOfRawData = offset - diroff
		case "/Symbols":
			coff.FileHeader.PointerToSymbolTable = offset
		}
		const N = `\[(\d+)\]`
		m := matcher{}
		switch {
		case m.Find(path, RE("^/Dir/Dirs"+N+"$")):
			coff.Dir.DirEntries[m[0]].OffsetToData = MASK_SUBDIRECTORY | (offset - diroff)
		case m.Find(path, RE("^/Dir/Dirs"+N+"/Dirs"+N+"$")):
			coff.Dir.Dirs[m[0]].DirEntries[m[1]].OffsetToData = MASK_SUBDIRECTORY | (offset - diroff)
		case m.Find(path, RE("^/DataEntries"+N+"$")):
			direntry := <-leafwalker
			direntry.OffsetToData = offset - diroff
		case m.Find(path, RE("^/DataEntries"+N+"/OffsetToData$")):
			coff.Relocations[m[0]].RVA = offset - diroff
		case m.Find(path, RE("^/Data"+N+"$")):
			coff.DataEntries[m[0]].OffsetToData = offset - diroff
		}

		if Plain(v.Kind()) {
			offset += uint32(binary.Size(v.Interface())) // TODO: change to v.Type().Size() ?
			return nil
		}
		vv, ok := v.Interface().(SizedReader)
		if ok {
			offset += uint32(vv.Size())
			return WALK_SKIP
		}
		return nil
	})

	// write the resulting file to disk
	Walk(coff, func(v reflect.Value, path string) error {
		if Plain(v.Kind()) {
			w.WriteLE(v.Interface())
			return nil
		}
		vv, ok := v.Interface().(SizedReader)
		if ok {
			w.WriteFromSized(vv)
			return WALK_SKIP
		}
		return nil
	})

	if w.Err != nil {
		return fmt.Errorf("Error writing output file: %s", w.Err)
	}

	return nil
}

func Plain(kind reflect.Kind) bool {
	switch kind {
	case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
		return true
	}
	return false
}

func MustAtoi(s string) int {
	i, err := strconv.Atoi(s)
	if err != nil {
		panic(err)
	}
	return i
}

type matcher []int

func (m *matcher) Find(s string, re *regexp.Regexp) bool {
	subs := re.FindStringSubmatch(s)
	if subs == nil {
		return false
	}

	*m = (*m)[:0]
	for i := 1; i < len(subs); i++ {
		*m = append(*m, MustAtoi(subs[i]))
	}
	return true
}
<commit_msg>starting to introduce AddResource(), works for manifest<commit_after>package main

// TODO:
// - to store icons, see: http://blogs.msdn.com/b/oldnewthing/archive/2012/07/20/10331787.aspx
//   - also need to first read and split input ico file

import (
	"debug/pe"
	"encoding/binary"
	"flag"
	"fmt"
	"io"
	"os"
	"reflect"
	"regexp"
	"sort"
	"strconv"

	"github.com/akavel/rsrc/ico"
)

type Dir struct { // struct IMAGE_RESOURCE_DIRECTORY
	Characteristics      uint32
	TimeDateStamp        uint32
	MajorVersion         uint16
	MinorVersion         uint16
	NumberOfNamedEntries uint16
	NumberOfIdEntries    uint16
	DirEntries
	Dirs
}

type DirEntries []DirEntry
type Dirs []Dir

type DirEntry struct { // struct IMAGE_RESOURCE_DIRECTORY_ENTRY
	NameOrId     uint32
	OffsetToData uint32
}

type DataEntry struct { // struct IMAGE_RESOURCE_DATA_ENTRY
	OffsetToData uint32
	Size1        uint32
	CodePage     uint32
	Reserved     uint32
}

type RelocationEntry struct {
	RVA         uint32 // "offset within the Section's raw data where the address starts."
	SymbolIndex uint32 // "(zero based) index in the Symbol table to which the reference refers."
	Type        uint16
}

type Symbol struct {
	Name           [8]byte
	Value          uint32
	SectionNumber  uint16
	Type           uint16
	StorageClass   uint8
	AuxiliaryCount uint8
}

type StringsHeader struct {
	Length uint32
}

const (
	MASK_SUBDIRECTORY = 1 << 31

	RT_ICON       = 3
	RT_GROUP_ICON = 3 + 11
	RT_MANIFEST   = 24
)

var (
	STRING_RSRC = [8]byte{'.', 'r', 's', 'r', 'c', 0, 0, 0}

	LANG_ENTRY  = DirEntries{{NameOrId: 0x0409}} //FIXME: language; what value should be here?
	RELOC_ENTRY = RelocationEntry{
		SymbolIndex: 0, // "(zero based) index in the Symbol table to which the reference refers. Once you have loaded the COFF file into memory and know where each symbol is, you find the new updated address for the given symbol and update the reference accordingly."
		Type:        7, // according to ldpe.c, this decodes to: IMAGE_REL_I386_DIR32NB
	}

	RE = regexp.MustCompile
)

type GRPICONDIR struct {
	ico.ICONDIR
	Entries []GRPICONDIRENTRY
}

type GRPICONDIRENTRY struct {
	ico.IconDirEntryCommon
	Id uint16
}

func main() {
	//TODO: allow in options advanced specification of multiple resources, as a tree (json?)
	var fnamein, fnameico, fnameout string
	flags := flag.NewFlagSet("", flag.ContinueOnError)
	flags.StringVar(&fnamein, "manifest", "", "path to a Windows manifest file to embed")
	flags.StringVar(&fnameico, "ico", "", "UNSUPPORTED: path to ICO file to embed")
	flags.StringVar(&fnameout, "o", "rsrc.syso", "name of output COFF (.res or .syso) file")
	_ = flags.Parse(os.Args[1:])
	if fnamein == "" {
		fmt.Fprintf(os.Stderr, "USAGE: %s -manifest FILE.exe.manifest [-o FILE.syso]\n"+
			"Generates a .syso file with specified resources embedded in .rsrc section,\n"+
			"aimed for consumption by Go linker when building Win32 excecutables.\n"+
			"OPTIONS:\n",
			os.Args[0])
		flags.PrintDefaults()
		os.Exit(1)
	}

	err := run(fnamein, fnameico, fnameout)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

type Coff struct {
	pe.FileHeader
	pe.SectionHeader32

	Dir
	DataEntries []DataEntry
	Data        []interface{}

	Relocations []RelocationEntry
	Symbols     []Symbol
	StringsHeader
}

//NOTE: function assumes that 'id' is increasing on each entry
func (coff *Coff) AddResource(kind uint32, id uint16, data interface{}, size uint32) {
	//FIXME: find correct place to insert on all levels, then find index in Data
	coff.Relocations = append(coff.Relocations, RELOC_ENTRY)
	coff.SectionHeader32.NumberOfRelocations++

	// find top level entry, inserting new if necessary at correct sorted position
	entries0 := coff.Dir.DirEntries
	dirs0 := coff.Dir.Dirs
	i0 := sort.Search(len(entries0), func(i int) bool {
		return entries0[i].NameOrId >= kind
	})
	if i0 >= len(entries0) || entries0[i0].NameOrId != kind {
		// inserting new entry & dir
		entries0 = append(entries0[:i0], append([]DirEntry{{NameOrId: kind}}, entries0[i0:]...)...)
		dirs0 = append(dirs0[:i0], append([]Dir{{}}, dirs0[i0:]...)...)
		coff.Dir.NumberOfIdEntries++
	}
	coff.Dir.DirEntries = entries0
	coff.Dir.Dirs = dirs0

	// for second level, assume ID is always increasing, so we don't have to sort
	dirs0[i0].DirEntries = append(dirs0[i0].DirEntries, DirEntry{NameOrId: uint32(id)})
	dirs0[i0].Dirs = append(dirs0[i0].Dirs, Dir{
		NumberOfIdEntries: 1,
		DirEntries:        LANG_ENTRY,
	})
	dirs0[i0].NumberOfIdEntries++

	// calculate preceding DirEntry leaves, to find new index in Data & DataEntries
	n := 0
	for _, dir0 := range dirs0[:i0+1] {
		n += len(dir0.DirEntries) //NOTE: assuming 1 language here; TODO: dwell deeper if more langs added
	}
	n--

	// insert new data in correct place
	coff.DataEntries = append(coff.DataEntries[:n], append([]DataEntry{{Size1: size}}, coff.DataEntries[n:]...)...)
	coff.Data = append(coff.Data[:n], append([]interface{}{data}, coff.Data[n:]...)...)
}

func run(fnamein, fnameico, fnameout string) error {
	manifest, err := SizedOpen(fnamein)
	if err != nil {
		return fmt.Errorf("Error opening manifest file '%s': %s", fnamein, err)
	}
	defer manifest.Close()

	var icons []ico.ICONDIRENTRY
	var iconsf *os.File
	if fnameico != "" {
		iconsf, err = os.Open(fnameico)
		if err != nil {
			return err
		}
		defer iconsf.Close()
		icons, err = ico.DecodeHeaders(iconsf)
		if err != nil {
			return err
		}
	}

	newid := make(chan uint16)
	go func() {
		for i := uint16(1); ; i++ {
			newid <- i
		}
	}()

	out, err := os.Create(fnameout)
	if err != nil {
		return err
	}
	defer out.Close()
	w := Writer{W: out}

	coff := Coff{
		pe.FileHeader{
			Machine:              0x014c, //FIXME: find out how to differentiate this value, or maybe not necessary for Go
			NumberOfSections:     1,      // .rsrc
			TimeDateStamp:        0,      // was also 0 in sample data from MinGW's windres.exe
			NumberOfSymbols:      1,
			SizeOfOptionalHeader: 0,
			Characteristics:      0x0104, //FIXME: copied from windres.exe output, find out what should be here and why
		},
		pe.SectionHeader32{
			Name: STRING_RSRC,
			//NumberOfRelocations: 1,
			Characteristics: 0x40000040, // "INITIALIZED_DATA MEM_READ" ?
		},

		// now, build "directory hierarchy" of .rsrc section: first type, then id/name, then language
		Dir{
		//NumberOfIdEntries: 1,
		//DirEntries:        DirEntries{{NameOrId: RT_MANIFEST}},
		//Dirs: Dirs{{
		//	NumberOfIdEntries: 1,
		//	DirEntries:        DirEntries{{NameOrId: uint32(<-newid)}}, // resource ID
		//	Dirs: Dirs{{
		//		NumberOfIdEntries: 1,
		//		DirEntries:        LANG_ENTRY,
		//	}},
		//}},
		},
		[]DataEntry{
		//DataEntry{
		//	Size1:    uint32(manifest.Size()),
		//	CodePage: 0, //FIXME: what value here? for now just tried 0 - TODO: fix also in other uses
		//},
		},
		[]interface{}{
		//manifest,
		},

		[]RelocationEntry{
		//RELOC_ENTRY,
		},

		[]Symbol{Symbol{
			Name:           STRING_RSRC,
			Value:          0,
			SectionNumber:  1,
			Type:           0, // FIXME: wtf?
			StorageClass:   3, // FIXME: is it ok? and uint8? and what does the value mean?
			AuxiliaryCount: 0, // FIXME: wtf?
		}},

		StringsHeader{
			Length: uint32(binary.Size(StringsHeader{})), // empty strings table -- but we must still show size of the table's header...
		},
	}

	coff.AddResource(RT_MANIFEST, <-newid, manifest, uint32(manifest.Size()))

	if len(icons) > 0 {
		//FIXME: root directory must be sorted by NameOrId value

		coff.Dir.NumberOfIdEntries += 2
		coff.Dir.DirEntries = append(coff.Dir.DirEntries, DirEntry{NameOrId: RT_ICON})
		coff.Dir.DirEntries = append(coff.Dir.DirEntries, DirEntry{NameOrId: RT_GROUP_ICON})

		// RT_ICONs
		coff.Dir.Dirs = append(coff.Dir.Dirs, Dir{
			NumberOfIdEntries: uint16(len(icons)),
		})
		dir := &coff.Dir.Dirs[len(coff.Dir.Dirs)-1]
		group := GRPICONDIR{ICONDIR: ico.ICONDIR{
			Reserved: 0, // magic num.
			Type:     1, // magic num.
			Count:    uint16(len(icons)),
		}}
		for _, icon := range icons {
			id := <-newid

			dir.DirEntries = append(dir.DirEntries, DirEntry{NameOrId: uint32(id)})
			dir.Dirs = append(dir.Dirs, Dir{
				NumberOfIdEntries: 1,
				DirEntries:        LANG_ENTRY,
			})

			r := io.NewSectionReader(iconsf, int64(icon.ImageOffset), int64(icon.BytesInRes))
			coff.DataEntries = append(coff.DataEntries, DataEntry{Size1: uint32(r.Size())})
			coff.Relocations = append(coff.Relocations, RELOC_ENTRY)
			coff.SectionHeader32.NumberOfRelocations++
			coff.Data = append(coff.Data, r)

			group.Entries = append(group.Entries, GRPICONDIRENTRY{icon.IconDirEntryCommon, id})
		}

		coff.Dir.Dirs = append(coff.Dir.Dirs, Dir{
			NumberOfIdEntries: 1,
			DirEntries:        DirEntries{{NameOrId: uint32(<-newid)}},
			Dirs: Dirs{{
				NumberOfIdEntries: 1,
				DirEntries:        LANG_ENTRY,
			}},
		})
		coff.DataEntries = append(coff.DataEntries, DataEntry{
			Size1: uint32(binary.Size(group.ICONDIR) + len(icons)*binary.Size(group.Entries[0])),
		})
		coff.Relocations = append(coff.Relocations, RELOC_ENTRY)
		coff.SectionHeader32.NumberOfRelocations++
		coff.Data = append(coff.Data, group)

	}

	leafwalker := make(chan *DirEntry)
	go func() {
		for _, dir1 := range coff.Dir.Dirs { // resource type
			for _, dir2 := range dir1.Dirs { // resource ID
				for i := range dir2.DirEntries { // resource lang
					leafwalker <- &dir2.DirEntries[i]
				}
			}
		}
	}()

	// fill in some important offsets in resulting file
	var offset, diroff uint32
	Walk(coff, func(v reflect.Value, path string) error {
		switch path {
		case "/Dir":
			coff.SectionHeader32.PointerToRawData = offset
			diroff = offset
		case "/Relocations":
			coff.SectionHeader32.PointerToRelocations = offset
			coff.SectionHeader32.SizeOfRawData = offset - diroff
		case "/Symbols":
			coff.FileHeader.PointerToSymbolTable = offset
		}
		const N = `\[(\d+)\]`
		m := matcher{}
		switch {
		case m.Find(path, RE("^/Dir/Dirs"+N+"$")):
			coff.Dir.DirEntries[m[0]].OffsetToData = MASK_SUBDIRECTORY | (offset - diroff)
		case m.Find(path, RE("^/Dir/Dirs"+N+"/Dirs"+N+"$")):
			coff.Dir.Dirs[m[0]].DirEntries[m[1]].OffsetToData = MASK_SUBDIRECTORY | (offset - diroff)
		case m.Find(path, RE("^/DataEntries"+N+"$")):
			direntry := <-leafwalker
			direntry.OffsetToData = offset - diroff
		case m.Find(path, RE("^/DataEntries"+N+"/OffsetToData$")):
			coff.Relocations[m[0]].RVA = offset - diroff
		case m.Find(path, RE("^/Data"+N+"$")):
			coff.DataEntries[m[0]].OffsetToData = offset - diroff
		}

		if Plain(v.Kind()) {
			offset += uint32(binary.Size(v.Interface())) // TODO: change to v.Type().Size() ?
			return nil
		}
		vv, ok := v.Interface().(SizedReader)
		if ok {
			offset += uint32(vv.Size())
			return WALK_SKIP
		}
		return nil
	})

	// write the resulting file to disk
	Walk(coff, func(v reflect.Value, path string) error {
		if Plain(v.Kind()) {
			w.WriteLE(v.Interface())
			return nil
		}
		vv, ok := v.Interface().(SizedReader)
		if ok {
			w.WriteFromSized(vv)
			return WALK_SKIP
		}
		return nil
	})

	if w.Err != nil {
		return fmt.Errorf("Error writing output file: %s", w.Err)
	}

	return nil
}

func Plain(kind reflect.Kind) bool {
	switch kind {
	case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
		return true
	}
	return false
}

func MustAtoi(s string) int {
	i, err := strconv.Atoi(s)
	if err != nil {
		panic(err)
	}
	return i
}

type matcher []int

func (m *matcher) Find(s string, re *regexp.Regexp) bool {
	subs := re.FindStringSubmatch(s)
	if subs == nil {
		return false
	}

	*m = (*m)[:0]
	for i := 1; i < len(subs); i++ {
		*m = append(*m, MustAtoi(subs[i]))
	}
	return true
}
<|endoftext|>


<commit_before>package gost

// S-box derived from the DES S-box, used in Bruce Schneier's Applied Cryptography
var DESDerivedSbox = [][]byte{
	{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7},
	{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10},
	{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8},
	{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15},
	{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9},
	{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11},
	{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1},
	{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7},
}

// Test parameters s-box, defined in RFC 4357
var TestSbox = [][]byte{
	{4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3},
	{14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9},
	{5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11},
	{7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3},
	{6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2},
	{4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14},
	{13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12},
	{1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12},
}

// S-box specified by the CryptoPro company, defined in RFC 4357
var CryptoProSbox = [][]byte{
	{10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15},
	{5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8},
	{7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13},
	{4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3},
	{7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5},
	{7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3},
	{13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11},
	{1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12},
}
<commit_msg>Add Magma support (RFC 8891 S-box) (#2)<commit_after>package gost

// S-box derived from the DES S-box, used in Bruce Schneier's Applied Cryptography
var DESDerivedSbox = [][]byte{
	{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7},
	{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10},
	{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8},
	{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15},
	{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9},
	{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11},
	{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1},
	{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7},
}

// Test parameters s-box, defined in RFC 4357
var TestSbox = [][]byte{
	{4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3},
	{14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9},
	{5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11},
	{7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3},
	{6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2},
	{4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14},
	{13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12},
	{1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12},
}

// S-box specified by the CryptoPro company, defined in RFC 4357
var CryptoProSbox = [][]byte{
	{10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15},
	{5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8},
	{7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13},
	{4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3},
	{7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5},
	{7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3},
	{13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11},
	{1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12},
}

// S-box specified by the TC26, defined in RFC 8891
var SboxIdtc26gost28147paramZ = [][]byte{
	{12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1},
	{6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15},
	{11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0},
	{12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11},
	{7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12},
	{5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0},
	{8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7},
	{1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2},
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"

	"gopkg.in/alecthomas/kingpin.v2"

	"github.com/dpecos/dotback/models"
	. "github.com/dpecos/dotback/utils"
)

func ReadConfig() []models.Recipe {
	file, err := ioutil.ReadFile(path.Join(HomeDir(), ".dotfiles", "config.json"))
	CheckError("Could not read config.json file", err)

	var config []models.Recipe
	err = json.Unmarshal(file, &config)
	CheckError("Could not parse config.json file", err)

	// fmt.Printf("%+v", config)

	return config
}

func ExecRecipes(config []models.Recipe) {
	for _, recipe := range config {
		recipe.Exec()
	}
}

var (
	app = kingpin.New("dotback", "Handle your dot files like a boss")
	// initialize = app.Command("init", "Use a Git repository to initialize your dotfiles folder")
	pull = app.Command("pull", "Fetch latest changes from remote dotfiles repository")
	push = app.Command("push", "Send latest changes to remote dotfiles repository")
	// install    = app.Command("install", "Performs the actions defined in your ~/.dotfiles/config.json")
	// recipe     = install.Arg("recipe", "Execute only this recipe").String()
	// add        = app.Command("add", "Creates a new recipe")
	// delete     = app.Command("delete", "Remove a recipe")
)

func main() {

	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
	case "pull":
		path := path.Join(HomeDir(), ".dotfiles")
		err := Execute(fmt.Sprintf("cd %s && git pull", path))
		CheckError("Error updating repository", err)
	case "push":
		path := path.Join(HomeDir(), ".dotfiles")
		err := Execute(fmt.Sprintf("cd %s && git push", path))
		CheckError("Error updating repository", err)
	}

	// config := ReadConfig()

	// if *recipe != "" {
	// 	for _, r := range config {
	// 		if r.Name == *recipe {
	// 			fmt.Printf("Executing only recipe '%s' (skipping the rest)\n", r.Name)
	// 			config = []models.Recipe{r}
	// 		}
	// 	}
	// }

	// fmt.Println(config)
	// if *action {
	// 	//ExecRecipes(config)
	// }
}
<commit_msg>Quick dirty hack to list recipes<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"

	"gopkg.in/alecthomas/kingpin.v2"

	"github.com/dpecos/dotback/models"
	. "github.com/dpecos/dotback/utils"
)

func ReadConfig(recipe string) []models.Recipe {
	file, err := ioutil.ReadFile(path.Join(HomeDir(), ".dotfiles", "config.json"))
	CheckError("Could not read config.json file", err)

	var config []models.Recipe
	err = json.Unmarshal(file, &config)
	CheckError("Could not parse config.json file", err)

	if recipe != "" {
		for _, r := range config {
			if r.Name == recipe {
				fmt.Printf("Executing only recipe '%s' (skipping the rest)\n", r.Name)
				config = []models.Recipe{r}
			}
		}
	}

	return config
}

func ExecRecipes(config []models.Recipe) {
	for _, recipe := range config {
		recipe.Exec()
	}
}

var (
	app = kingpin.New("dotback", "Handle your dot files like a boss")
	// initialize = app.Command("init", "Use a Git repository to initialize your dotfiles folder")
	pull    = app.Command("pull", "Fetch latest changes from remote dotfiles repository")
	push    = app.Command("push", "Send latest changes to remote dotfiles repository")
	list    = app.Command("list", "List the actions defined in your ~/.dotfiles/config.json")
	install = app.Command("install", "Performs the actions defined in your ~/.dotfiles/config.json")
	recipe  = install.Arg("recipe", "Execute only this recipe").String()
	// add        = app.Command("add", "Creates a new recipe")
	// delete     = app.Command("delete", "Remove a recipe")
)

func main() {

	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
	case "pull":
		path := path.Join(HomeDir(), ".dotfiles")
		err := Execute(fmt.Sprintf("cd %s && git pull", path))
		CheckError("Error updating repository", err)
	case "push":
		path := path.Join(HomeDir(), ".dotfiles")
		err := Execute(fmt.Sprintf("cd %s && git push", path))
		CheckError("Error updating repository", err)
	case "list":
		config := ReadConfig(*recipe)
		for _, recipe := range config {
			fmt.Printf("%s -> \n", recipe.Name)

			for _, action := range recipe.Actions {
				if action.Link != "" {
					fmt.Printf("   Link: %s\n", action.Link)
				}
				if action.Cmd != "" {
					fmt.Printf("   Cmd: %s\n", action.Cmd)
				}
				if action.Git != "" {
					fmt.Printf("   Git: %s\n", action.Git)
				}
			}
		}
	case "install":
		config := ReadConfig(*recipe)
		ExecRecipes(config)
	}

}
<|endoftext|>


<commit_before>package kinesis

import (
	"time"

	"github.com/aws/aws-sdk-go/service/kinesis"
)

type Drainer struct {
	Buffer *recordBuffer
}

func newDrainer(client *kinesis.Kinesis, streamName string) (*Drainer, error) {
	buffer, err := newRecordBuffer(client, streamName)
	if err != nil {
		return nil, err
	}

	d := &Drainer{Buffer: buffer}
	// Every second, we flush the buffer
	go d.Drain()

	return d, nil
}

func (d *Drainer) Drain() {
	ticker := time.NewTicker(time.Second * 1)

	for {
		logErr(d.Buffer.Flush())
		<-ticker.C
	}
}
<commit_msg>Cleaner timer.<commit_after>package kinesis

import (
	"time"

	"github.com/aws/aws-sdk-go/service/kinesis"
)

type Drainer struct {
	Buffer *recordBuffer
}

func newDrainer(client *kinesis.Kinesis, streamName string) (*Drainer, error) {
	buffer, err := newRecordBuffer(client, streamName)
	if err != nil {
		return nil, err
	}

	d := &Drainer{Buffer: buffer}
	// Every second, we flush the buffer
	go d.Drain()

	return d, nil
}

func (d *Drainer) Drain() {
	for range time.Tick(time.Second * 1) {
		logErr(d.Buffer.Flush())
	}
}
<|endoftext|>


<commit_before>package dir

import "strings"

// Dir holds directory information to insert into templates
type Dir struct {
	List      [][]string
	Blacklist []string
}

// Exists checks if a directory exists or not
func (d *Dir) Exists(newDir string) bool {
	for _, dir := range d.Blacklist {
		if dir == newDir {
			return true
		}
	}

	return false
}

// Parse a directory to build a list of directories to be made at b0x.go
func (d *Dir) Parse(newDir string) []string {
	list := strings.Split(newDir, "/")

	var dirWalk []string

	for indx := range list {
		dirList := ""
		for i := -1; i < indx; i++ {
			dirList += list[i+1] + "/"
		}

		if !d.Exists(dirList) {
			if strings.HasSuffix(dirList, "//") {
				dirList = dirList[:len(dirList)-1]
			}

			dirWalk = append(dirWalk, dirList)
			d.Blacklist = append(d.Blacklist, dirList)
		}
	}

	return dirWalk
}

// Insert a new folder to the list
func (d *Dir) Insert(newDir string) {
	if !d.Exists(newDir) {
		d.Blacklist = append(d.Blacklist, newDir)
		d.List = append(d.List, d.Parse(newDir))
	}
}

// Clean dupes
func (d *Dir) Clean() []string {
	var cleanList []string

	for _, dirs := range d.List {
		for _, dir := range dirs {
			cleanList = append(cleanList, dir)
		}
	}

	return cleanList
}
<commit_msg>Filter useless dirs<commit_after>package dir

import "strings"

// Dir holds directory information to insert into templates
type Dir struct {
	List      [][]string
	Blacklist []string
}

// Exists checks if a directory exists or not
func (d *Dir) Exists(newDir string) bool {
	for _, dir := range d.Blacklist {
		if dir == newDir {
			return true
		}
	}

	return false
}

// Parse a directory to build a list of directories to be made at b0x.go
func (d *Dir) Parse(newDir string) []string {
	list := strings.Split(newDir, "/")

	var dirWalk []string

	for indx := range list {
		dirList := ""
		for i := -1; i < indx; i++ {
			dirList += list[i+1] + "/"
		}

		if !d.Exists(dirList) {
			if strings.HasSuffix(dirList, "//") {
				dirList = dirList[:len(dirList)-1]
			}

			dirWalk = append(dirWalk, dirList)
			d.Blacklist = append(d.Blacklist, dirList)
		}
	}

	return dirWalk
}

// Insert a new folder to the list
func (d *Dir) Insert(newDir string) {
	if !d.Exists(newDir) {
		d.Blacklist = append(d.Blacklist, newDir)
		d.List = append(d.List, d.Parse(newDir))
	}
}

// Clean dupes
func (d *Dir) Clean() []string {
	var cleanList []string

	for _, dirs := range d.List {
		for _, dir := range dirs {
			if dir == "./" && dir == "/" && dir == "." && dir == "" {
				continue
			}

			cleanList = append(cleanList, dir)
		}
	}

	return cleanList
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"fmt"
	id3 "github.com/mikkyang/id3-go"
	kingpin "gopkg.in/alecthomas/kingpin.v1"
	"net/url"
	"os"
	"path/filepath"
	"time"
)

const (
	Header   = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
	iTunesNs = "http://www.itunes.com/dtds/podcast-1.0.dtd"
)

type Rss struct {
	XMLName xml.Name `xml:"rss"`
	Channel Channel  `xml:"channel"`
	Version string   `xml:"version,attr"`
	NS      string   `xml:"xmlns:itunes,attr"`
}

type Channel struct {
	PubDate     string  `xml:"pubDate,omitempty"`
	Title       string  `xml:"title,omitempty"`
	Link        string  `xml:"link,omitempty"`
	Description string  `xml:"description"`
	Language    string  `xml:"language,omitempty"`
	Images      []Image `xml:"image,omitempty"`
	Items       []Item  `xml:"item"`
}

type Item struct {
	Title       string    `xml:"title"`
	Description string    `xml:"description"`
	Enclosure   Enclosure `xml:"enclosure"`
	Guid        string    `xml:"guid"`
	Subtitle    string    `xml:"itunes:subtitle,omitempty"`
	PubDate     string    `xml:"pubDate,omitempty"`
}

type Image struct {
	Link  string `xml:"link"`
	Title string `xml:"title"`
	Url   string `xml:"url"`
}

type Enclosure struct {
	Url    string `xml:"url,attr"`
	Length int64  `xml:"length,attr"`
	Type   string `xml:"type,attr"`
}

func fileUrl(relativePath string, baseUrl string) string {
	Url, _ := url.Parse(baseUrl)
	Url.Path += relativePath
	return Url.String()
}

func formatYear(year string) string {
	if len(year) > 0 {
		t, err := time.Parse("20060102", year)
		if err != nil {
			t, err = time.Parse("20060102", year[0:len(year)-1])
			if err != nil {
				t, err = time.Parse("2006", year)
				if err != nil {
					t, err = time.Parse("20060201", year)
					if err != nil {
						return ""
					}
				}
			}
		}
		return t.Format(time.RFC1123Z)
	}
	return year
}

func addMeta(path string, f os.FileInfo, item *Item) {
	fd, err := id3.Open(path)
	if err != nil {
		item.Title = f.Name()
	} else {
		title := fd.Title()
		author := fd.Artist()
		if len(title) > 0 {
			item.Title = title
		} else {
			item.Title = author
			if len(author) > 0 {
				item.Title += " - "
			}
			item.Title += f.Name()
		}
		item.Subtitle = author
		item.PubDate = formatYear(fd.Year())
	}
}

func visitFiles(workDir string, channel *Channel, publicUrl string, recursive bool) filepath.WalkFunc {
	return func(path string, f os.FileInfo, err error) error {

		if err != nil {
			return err
		}

		if f.IsDir() && path != workDir && !recursive {
			return filepath.SkipDir
		}

		if !!f.IsDir() {
			return nil
		}

		matched, _ := filepath.Match("*.mp3", f.Name())
		if matched {
			url := fileUrl(path[len(workDir)-1:], publicUrl)
			item := Item{Enclosure: Enclosure{Length: f.Size(), Type: "audio/mpeg",
				Url: url}, Guid: url}
			addMeta(path, f, &item)
			channel.Items = append(channel.Items, item)
		}

		return nil

	}
}

var (
	baseUrl     = kingpin.Flag("server", "hostname (and path) to the root e.g. http://myserver.com/rss").Short('s').Default("http://localhost").URL()
	recursive   = kingpin.Flag("recursive", "how to handle the directory scan").Short('r').Bool()
	language    = kingpin.Flag("language", "the language of the RSS document, a ISO 639 value").Short('l').String()
	title       = kingpin.Flag("title", "RSS channel title").Short('t').Default("RSS FEED").String()
	description = kingpin.Flag("description", "RSS channel description").Short('d').String()
	imageUrl    = kingpin.Flag("image", "Image URL for the RSS channel image").Short('i').URL()
	path        = kingpin.Arg("directory", "directory to read files relative from").Required().ExistingDir()
)

func main() {

	kingpin.Version("0.0.1")
	kingpin.Parse()

	channel := &Channel{
		PubDate:     time.Now().Format(time.RFC1123Z),
		Title:       *title,
		Link:        (*baseUrl).String(),
		Description: *description,
		Language:    *language}

	err := filepath.Walk(*path, visitFiles(*path, channel, (*baseUrl).String(), *recursive))
	if *imageUrl != nil {
		channel.Images = append(channel.Images, Image{Title: channel.Title, Link: channel.Link, Url: (*imageUrl).String()})
	}

	if err != nil {
		fmt.Printf("%s: %v\n", os.Args[0], err)
	} else {
		output, err := xml.MarshalIndent(
			&Rss{Channel: *channel, Version: "2.0", NS: iTunesNs}, "", "  ")
		if err != nil {
			fmt.Printf("error: %v\n", err)
		} else {
			os.Stdout.WriteString(Header)
			os.Stdout.Write(output)
		}
	}

}
<commit_msg>add iTunes:category to item, use TCON id3 frame value<commit_after>package main

import (
	"encoding/xml"
	"fmt"
	id3 "github.com/mikkyang/id3-go"
	kingpin "gopkg.in/alecthomas/kingpin.v1"
	"net/url"
	"os"
	"path/filepath"
	"time"
)

const (
	Header   = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
	iTunesNs = "http://www.itunes.com/dtds/podcast-1.0.dtd"
)

type Rss struct {
	XMLName xml.Name `xml:"rss"`
	Channel Channel  `xml:"channel"`
	Version string   `xml:"version,attr"`
	NS      string   `xml:"xmlns:itunes,attr"`
}

type Channel struct {
	PubDate     string  `xml:"pubDate,omitempty"`
	Title       string  `xml:"title,omitempty"`
	Link        string  `xml:"link,omitempty"`
	Description string  `xml:"description"`
	Language    string  `xml:"language,omitempty"`
	Images      []Image `xml:"image,omitempty"`
	Items       []Item  `xml:"item"`
}

type Item struct {
	Title       string    `xml:"title"`
	Description string    `xml:"description"`
	Enclosure   Enclosure `xml:"enclosure"`
	Guid        string    `xml:"guid"`
	Subtitle    string    `xml:"itunes:subtitle,omitempty"`
	Categories  []Text    `xml:"itunes:category,omitempty"`
	PubDate     string    `xml:"pubDate,omitempty"`
}

type Text struct {
	Value string `xml:"text,attr"`
}

type Image struct {
	Link  string `xml:"link"`
	Title string `xml:"title"`
	Url   string `xml:"url"`
}

type Enclosure struct {
	Url    string `xml:"url,attr"`
	Length int64  `xml:"length,attr"`
	Type   string `xml:"type,attr"`
}

func fileUrl(relativePath string, baseUrl string) string {
	Url, _ := url.Parse(baseUrl)
	Url.Path += relativePath
	return Url.String()
}

func formatYear(year string) string {
	if len(year) > 0 {
		t, err := time.Parse("20060102", year)
		if err != nil {
			t, err = time.Parse("20060102", year[0:len(year)-1])
			if err != nil {
				t, err = time.Parse("2006", year)
				if err != nil {
					t, err = time.Parse("20060201", year)
					if err != nil {
						return ""
					}
				}
			}
		}
		return t.Format(time.RFC1123Z)
	}
	return year
}

func addMeta(path string, f os.FileInfo, item *Item) {
	fd, err := id3.Open(path)
	if err != nil {
		item.Title = f.Name()
	} else {
		title := fd.Title()
		author := fd.Artist()
		if len(title) > 0 {
			item.Title = title
		} else {
			item.Title = author
			if len(author) > 0 {
				item.Title += " - "
			}
			item.Title += f.Name()
		}
		item.Subtitle = author
		tcon := fd.Frame("TCON")
		if tcon != nil {
			item.Categories = append(item.Categories, Text{Value: tcon.String()})
		}
		item.PubDate = formatYear(fd.Year())
	}
}

func visitFiles(workDir string, channel *Channel, publicUrl string, recursive bool) filepath.WalkFunc {
	return func(path string, f os.FileInfo, err error) error {

		if err != nil {
			return err
		}

		if f.IsDir() && path != workDir && !recursive {
			return filepath.SkipDir
		}

		if !!f.IsDir() {
			return nil
		}

		matched, _ := filepath.Match("*.mp3", f.Name())
		if matched {
			url := fileUrl(path[len(workDir)-1:], publicUrl)
			item := Item{Enclosure: Enclosure{Length: f.Size(), Type: "audio/mpeg",
				Url: url}, Guid: url}
			addMeta(path, f, &item)
			channel.Items = append(channel.Items, item)
		}

		return nil

	}
}

var (
	baseUrl     = kingpin.Flag("server", "hostname (and path) to the root e.g. http://myserver.com/rss").Short('s').Default("http://localhost").URL()
	recursive   = kingpin.Flag("recursive", "how to handle the directory scan").Short('r').Bool()
	language    = kingpin.Flag("language", "the language of the RSS document, a ISO 639 value").Short('l').String()
	title       = kingpin.Flag("title", "RSS channel title").Short('t').Default("RSS FEED").String()
	description = kingpin.Flag("description", "RSS channel description").Short('d').String()
	imageUrl    = kingpin.Flag("image", "Image URL for the RSS channel image").Short('i').URL()
	path        = kingpin.Arg("directory", "directory to read files relative from").Required().ExistingDir()
)

func main() {

	kingpin.Version("0.0.1")
	kingpin.Parse()

	channel := &Channel{
		PubDate:     time.Now().Format(time.RFC1123Z),
		Title:       *title,
		Link:        (*baseUrl).String(),
		Description: *description,
		Language:    *language}

	err := filepath.Walk(*path, visitFiles(*path, channel, (*baseUrl).String(), *recursive))
	if *imageUrl != nil {
		channel.Images = append(channel.Images, Image{Title: channel.Title, Link: channel.Link, Url: (*imageUrl).String()})
	}

	if err != nil {
		fmt.Printf("%s: %v\n", os.Args[0], err)
	} else {
		output, err := xml.MarshalIndent(
			&Rss{Channel: *channel, Version: "2.0", NS: iTunesNs}, "", "  ")
		if err != nil {
			fmt.Printf("error: %v\n", err)
		} else {
			os.Stdout.WriteString(Header)
			os.Stdout.Write(output)
		}
	}

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"flag"
	"fmt"
	id3 "github.com/mikkyang/id3-go"
	"net/url"
	"os"
	"path/filepath"
)

const (
	Header = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
)

func usage() {
	fmt.Fprintf(os.Stderr, "usage: %s [dir]\n", os.Args[0])
}

type Rss struct {
	XMLName xml.Name `xml:"rss"`
	Channel Channel  `xml:"channel"`
	Version string   `xml:"version,attr"`
	NS      string   `xml:"xmlns:itunes,attr"`
}

type Channel struct {
	Title       string `xml:"title"`
	Link        string `xml:"link"`
	Description string `xml:"description"`
	Items       []Item `xml:"item"`
}

type Item struct {
	Title       string    `xml:"title"`
	Description string    `xml:"description"`
	Enclosure   Enclosure `xml:"enclosure"`
	Guid        string    `xml:"guid"`
	Subtitle    string    `xml:"itunes:subtitle"`
}

type Enclosure struct {
	Url    string `xml:"url,attr"`
	Length int64  `xml:"length,attr"`
	Type   string `xml:"type,attr"`
}

func fileUrl(f os.FileInfo, baseUrl string) (string, error) {
	Url, err := url.Parse(baseUrl)
	if err != nil {
		return "", err
	}
	Url.Path += f.Name()
	return Url.String(), nil
}

func addMeta(path string, f os.FileInfo, item *Item) string {
	fd, err := id3.Open(path)
	name := ""
	if err != nil {
		item.Title = f.Name()
	} else {
		title := fd.Title()
		author := fd.Artist()
		if len(title) > 0 {
			item.Title = title
		} else {
			item.Title = author
			if len(author) > 0 {
				item.Title += " - "
			}
			item.Title += f.Name()
		}
		item.Subtitle = author
	}
	return name
}

func visitFiles(channel *Channel, publicUrl string) filepath.WalkFunc {
	return func(path string, f os.FileInfo, err error) error {

		if err != nil {
			fmt.Println(err) // can't walk here,
			return nil
		}

		if !!f.IsDir() {
			return nil // not a file. ignore
		}

		matched, err := filepath.Match("*.mp3", f.Name())
		if err != nil {
			fmt.Println(err) // malformed pattern
			return err
		}

		if matched {
			url, err := fileUrl(f, publicUrl)
			if err != nil {
				return err
			}

			enclosure := Enclosure{Length: f.Size(), Type: "audio/mpeg",
				Url: url}
			item := Item{Enclosure: enclosure, Guid: enclosure.Url}
			addMeta(path, f, &item)
			channel.Items = append(channel.Items, item)
		}

		return nil

	}
}

func main() {
	flag.Parse()

	if flag.NArg() > 1 {
		usage()
		os.Exit(-1)
	}

	var workDir = os.Args[0]
	if flag.NArg() == 1 {
		workDir = flag.Arg(0)
	}

	publicUrl := "http://kirst.local:8080"
	channel := &Channel{Title: "RSS FEED", Link: publicUrl}
	err := filepath.Walk(workDir, visitFiles(channel, publicUrl))

	if err != nil {
		fmt.Printf("error: %v\n", err)
	} else {
		output, err := xml.MarshalIndent(
			&Rss{Channel: *channel, Version: "2.0", NS: "http://www.itunes.com/dtds/podcast-1.0.dtd"}, " ", "	")
		if err != nil {
			fmt.Printf("error: %v\n", err)
		} else {
			os.Stdout.WriteString(Header)
			os.Stdout.Write(output)
		}
	}

}
<commit_msg>improve error handling<commit_after>package main

import (
	"encoding/xml"
	"flag"
	"fmt"
	id3 "github.com/mikkyang/id3-go"
	"net/url"
	"os"
	"path/filepath"
)

const (
	Header = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
)

func usage() {
	fmt.Fprintf(os.Stderr, "usage: %s [dir]\n", os.Args[0])
}

type Rss struct {
	XMLName xml.Name `xml:"rss"`
	Channel Channel  `xml:"channel"`
	Version string   `xml:"version,attr"`
	NS      string   `xml:"xmlns:itunes,attr"`
}

type Channel struct {
	Title       string `xml:"title"`
	Link        string `xml:"link"`
	Description string `xml:"description"`
	Items       []Item `xml:"item"`
}

type Item struct {
	Title       string    `xml:"title"`
	Description string    `xml:"description"`
	Enclosure   Enclosure `xml:"enclosure"`
	Guid        string    `xml:"guid"`
	Subtitle    string    `xml:"itunes:subtitle"`
}

type Enclosure struct {
	Url    string `xml:"url,attr"`
	Length int64  `xml:"length,attr"`
	Type   string `xml:"type,attr"`
}

func fileUrl(f os.FileInfo, baseUrl string) (string, error) {
	Url, err := url.Parse(baseUrl)
	if err != nil {
		return "", err
	}
	Url.Path += f.Name()
	return Url.String(), nil
}

func addMeta(path string, f os.FileInfo, item *Item) string {
	fd, err := id3.Open(path)
	name := ""
	if err != nil {
		item.Title = f.Name()
	} else {
		title := fd.Title()
		author := fd.Artist()
		if len(title) > 0 {
			item.Title = title
		} else {
			item.Title = author
			if len(author) > 0 {
				item.Title += " - "
			}
			item.Title += f.Name()
		}
		item.Subtitle = author
	}
	return name
}

func visitFiles(channel *Channel, publicUrl string) filepath.WalkFunc {
	return func(path string, f os.FileInfo, err error) error {

		if err != nil {
			return err
		}

		if !!f.IsDir() {
			return nil
		}

		matched, err := filepath.Match("*.mp3", f.Name())
		if err != nil {
			fmt.Println(err) // malformed pattern
			return err
		}

		if matched {
			url, err := fileUrl(f, publicUrl)
			if err != nil {
				return err
			}

			enclosure := Enclosure{Length: f.Size(), Type: "audio/mpeg",
				Url: url}
			item := Item{Enclosure: enclosure, Guid: enclosure.Url}
			addMeta(path, f, &item)
			channel.Items = append(channel.Items, item)
		}

		return nil

	}
}

func main() {
	flag.Parse()

	if flag.NArg() > 1 {
		usage()
		os.Exit(-1)
	}

	var workDir = os.Args[0]
	if flag.NArg() == 1 {
		workDir = flag.Arg(0)
	}

	file, err := os.Open(workDir)
	if err != nil {
		fmt.Printf("%s: %v\n", os.Args[0], err)
		return
	}
	stat, err := file.Stat()
	if err != nil || !stat.IsDir() {
		fmt.Printf("%s: %v: Is a file\n", os.Args[0], workDir)
		return
	}

	publicUrl := "http://kirst.local:8080"
	channel := &Channel{Title: "RSS FEED", Link: publicUrl}
	err = filepath.Walk(workDir, visitFiles(channel, publicUrl))

	if err != nil {
		fmt.Printf("%s: %v\n", os.Args[0], err)
	} else {
		output, err := xml.MarshalIndent(
			&Rss{Channel: *channel, Version: "2.0", NS: "http://www.itunes.com/dtds/podcast-1.0.dtd"}, " ", "	")
		if err != nil {
			fmt.Printf("error: %v\n", err)
		} else {
			os.Stdout.WriteString(Header)
			os.Stdout.Write(output)
		}
	}

}
<|endoftext|>


<commit_before>package dts

type Variable struct {
	Identifier string
	Modifier   []string
	Type       []string // multipy types, return type
}

type Function struct {
	Variable
	Args []Variable
	// Type is the function return type
}

type Object struct {
	// module/class/interface/enum/js object
	Type string

	// for class/interface/object
	Identifier string
	Modifier   []string           `json:"omitempty"`
	Extens     map[string]*Object `json:"omitempty"`
	Implements map[string]*Object `json:"omitempty"`
	// var difinitions
	Vars map[string]*Variable `json:"omitempty"`
	// using slice here, incase of function override
	Funcs []*Function `json:"omitempty"`
	// helpers
	// constructor for class
	Constructor *Function

	// for module
	Classes    map[string]*Object `json:"omitempty"`
	Interfaces map[string]*Object `json:"omitempty"`
	Modules    map[string]*Object `json:"omitempty"`
}

type Class struct {
	Identifier string
	Modifier   []string
	Extens     map[string]*Class
	Implements map[string]*Interface
	Vars       map[string]*Variable
	Funcs      []*Function // using slice here, incase of function override
	// helpers
	Constructor *Function
}

type Interface struct {
	Class
}

type Module struct {
	Identifier string
	Modifier   []string
	Classes    map[string]*Class
	SubModules map[string]*Module
	Vars       map[string]*Variable
	Funcs      []*Function // using slice here, incase of function override
}

type DTS struct {
	// type register
	Modules    map[string]*Module
	Classes    map[string]*Class
	Interfaces map[string]*Interface
	// for parsing
	currentModule    *Module
	currentClass     *Class
	currentInterface *Interface
}

func (d *DTS) Init() {
	d.Modules = make(map[string]*Module)
	d.Classes = make(map[string]*Class)
	d.Interfaces = make(map[string]*Interface)
}

func (d *DTS) NewModule(text string) {
	println("module", text)
}

func (d *DTS) NewClass(text string) {
	println("class", text)
}

func (d *DTS) NewInterface(text string) {
	println("interface", text)

}

func (d *DTS) NewEnum(text string) {
	println("enum", text)
}

func (d *DTS) NewVariable(text string) {
	println("variable", text)
}

func (d *DTS) NewFunction(text string) {
	println("function", text)
}
<commit_msg>using unified Object to represents .d.ts module/class/interface ...etc<commit_after>package dts

type Kind int

const (
	TopLevel Kind = iota
	Module
	Class
	Interface
	Enum
	Obj
)

type Identifier struct {
	Name     string
	Modifier []string
}

/*
KNOWN type: string -> string
            number -> float64
            object -> js.M
            funcion   -> func

            class  -> struct
            module -> struct
            interface -> struct
            toplevel  -> convert the same name package
                         toplevel object can have no Indentifier
// var/func overrides -> comment
// other types just output as comment
*/
type Variable struct {
	Identifier
	// only convert the KNOWN type to go
	Type []string // multipy types, return type
}

// is this usefull?
type Assignment struct {
	Identifier
	Value string // just a string
}

type Function struct {
	Identifier
	Args       []Variable
	ReturnType []string
	// Type is the function return type
}

type Object struct {
	Identifier
	// module/class/interface/enum/js object/ or top level
	Kind Kind

	// for class/interface/object
	Extents    map[string]*Object `json:"omitempty"`
	Implements map[string]*Object `json:"omitempty"`
	// var difinitions
	Vars        map[string]*Variable   `json:"omitempty"`
	Assignments map[string]*Assignment `json:"omitempty"`
	// using slice here, incase of function override
	Funcs []*Function `json:"omitempty"`
	// helpers
	// constructor for class
	Constructor *Function

	// for module
	Classes    map[string]*Object `json:"omitempty"`
	Interfaces map[string]*Object `json:"omitempty"`
	Modules    map[string]*Object `json:"omitempty"`
}

func newObject(kind Kind, name string) *Object {
	o := &Object{
		Kind: kind,
	}
	o.Identifier.Name = name
	return o
}

type DTS struct {
	// TopLevel object
	Object
	// current parsing ojbect
	current *Object
}

func (d DTS) Init() {
	d.Object.Kind = TopLevel
}

func (d DTS) NewModule(text string) {
	println("module", text)
	d.current = newObject(Module, text)
	if d.Modules == nil {
		d.Modules = make(map[string]*Object)
	}
	d.Modules[d.current.Name] = d.current
}

func (d DTS) NewClass(text string) {
	println("class", text)
}

func (d DTS) NewInterface(text string) {
	println("interface", text)

}

func (d DTS) NewEnum(text string) {
	println("enum", text)
}

func (d DTS) NewVariable(text string) {
	println("variable", text)
}

func (d DTS) NewFunction(text string) {
	println("function", text)
}
<|endoftext|>