<commit_before>// Copyright 2014-2015 The DevMine authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package repotool-db is able to fetch information from a source code repository.
// Typically, it can get all commits, their authors and commiters and so on
// and is able to populate the information into a PostgreSQL database.
// Currently, on the Git VCS is supported.
package main

import (
	"bytes"
	"database/sql"
	"encoding/hex"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strconv"
	"strings"
	"sync"

	"github.com/golang/glog"
	_ "github.com/lib/pq"
	mmh3 "github.com/spaolacci/murmur3"

	"github.com/DevMine/repotool/config"
	"github.com/DevMine/repotool/model"
	"github.com/DevMine/repotool/repo"
)

const version = "0.1.0"

// database fields per tables
var (
	diffDeltaFields = []string{
		"commit_id",
		"file_status",
		"is_file_binary",
		"similarity",
		"old_file_path",
		"new_file_path"}

	commitFields = []string{
		"repository_id",
		"author_id",
		"committer_id",
		"hash",
		"vcs_id",
		"message",
		"author_date",
		"commit_date",
		"file_changed_count",
		"insertions_count",
		"deletions_count"}
)

// program flags
var (
	configPath    = flag.String("c", "", "configuration file")
	vflag         = flag.Bool("V", false, "print version.")
	cpuprofile    = flag.String("cpuprofile", "", "write cpu profile to file")
	depthflag     = flag.Uint("d", 0, "depth level where to find repositories")
	numGoroutines = flag.Uint("g", uint(runtime.NumCPU()), "max number of goroutines to spawn")
)

func main() {
	var err error

	flag.Usage = func() {
		fmt.Printf("usage: %s [OPTION(S)] [REPOSITORIES ROOT FOLDER]\n", os.Args[0])
		flag.PrintDefaults()
		os.Exit(0)
	}
	flag.Parse()

	if *vflag {
		fmt.Printf("%s - %s\n", filepath.Base(os.Args[0]), version)
		os.Exit(0)
	}

	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if len(flag.Args()) != 1 {
		fmt.Fprintln(os.Stderr, "invalid # of arguments")
		flag.Usage()
	}

	if len(*configPath) == 0 {
		fatal(errors.New("a configuration file must be specified"))
	}

	var cfg *config.Config
	cfg, err = config.ReadConfig(*configPath)
	if err != nil {
		fatal(err)
	}

	// Make sure we finish writing logs before exiting.
	defer glog.Flush()

	var db *sql.DB
	db, err = openDBSession(*cfg.Database)
	if err != nil {
		return
	}
	defer db.Close()

	reposPath := make(chan string, 0)
	var wg sync.WaitGroup
	for w := uint(0); w < *numGoroutines; w++ {
		wg.Add(1)
		go func() {
			for path := range reposPath {
				work := func() error {
					repository, err := repo.New(*cfg, path)
					if err != nil {
						return err
					}
					defer repository.Cleanup()

					if err = repository.FetchCommits(); err != nil {
						return err
					}

					if err = insertRepoData(db, repository); err != nil {
						return err
					}
					return nil
				}
				if err := work(); err != nil {
					glog.Error(err)
				}
			}
			wg.Done()
		}()
	}

	reposDir := flag.Arg(0)
	iterateRepos(reposPath, reposDir, *depthflag)

	close(reposPath)
	wg.Wait()

}

func iterateRepos(reposPath chan string, path string, depth uint) {
	fis, err := ioutil.ReadDir(path)
	if err != nil {
		fatal(err)
	}

	if depth == 0 {
		for _, fi := range fis {
			if !fi.IsDir() {
				if filepath.Ext(fi.Name()) != ".tar" {
					continue
				}
			}

			repoPath := filepath.Join(path, fi.Name())
			fmt.Println("adding repository: ", repoPath, " to the tasks pool")
			reposPath <- repoPath
		}
		return
	}

	for _, fi := range fis {
		if !fi.IsDir() {
			continue
		}

		iterateRepos(reposPath, filepath.Join(path, fi.Name()), depth-1)
	}
}

// fatal prints an error on standard error stream and exits.
func fatal(a ...interface{}) {
	fmt.Fprintln(os.Stderr, a...)
	os.Exit(1)
}

// openDBSession creates a session to the database.
func openDBSession(cfg config.DatabaseConfig) (*sql.DB, error) {
	dbURL := fmt.Sprintf(
		"user='%s' password='%s' host='%s' port=%d dbname='%s' sslmode='%s'",
		cfg.UserName, cfg.Password, cfg.HostName, cfg.Port, cfg.DBName, cfg.SSLMode)

	return sql.Open("postgres", dbURL)
}

// insertRepoData inserts repository data into the database, or updates it
// if it is already there.
func insertRepoData(db *sql.DB, r repo.Repo) error {
	if db == nil {
		return errors.New("nil database given")
	}

	repoID, err := getRepoID(db, r)
	if err != nil {
		return err
	}
	if repoID == nil {
		return errors.New("cannot find corresponding repository in database")
	}

	userIDs, err := getAllUsers(db)
	if err != nil {
		return err
	}

	tx, err := db.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	commitStmt, err := tx.Prepare(genInsQuery("commits", commitFields...) + " RETURNING id")
	if err != nil {
		return err
	}

	deltaStmt, err := tx.Prepare(genInsQuery("commit_diff_deltas", diffDeltaFields...))
	if err != nil {
		return err
	}

	for _, c := range r.GetCommits() {
		if err := insertCommit(userIDs, *repoID, c, tx, commitStmt, deltaStmt); err != nil {
			return err
		}
	}

	if err := commitStmt.Close(); err != nil {
		return err
	}
	if err := deltaStmt.Close(); err != nil {
		return err
	}
	if err := tx.Commit(); err != nil {
		return err
	}

	return nil
}

// insertCommit inserts a commit into the database
func insertCommit(userIDs map[string]uint64, repoID uint64, c model.Commit, tx *sql.Tx, commitStmt, deltaStmt *sql.Stmt) error {
	authorID := userIDs[c.Author.Email]
	committerID := userIDs[c.Committer.Email]
	hash := genCommitHash(c)

	var commitID uint64
	err := commitStmt.QueryRow(
		repoID, authorID, committerID, hash,
		c.VCSID, c.Message, c.AuthorDate, c.CommitDate,
		c.FileChangedCount, c.InsertionsCount, c.DeletionsCount).Scan(&commitID)
	if err != nil {
		return err
	}

	for _, d := range c.DiffDelta {
		if err := insertDiffDelta(commitID, d, deltaStmt); err != nil {
			return err
		}
	}

	return nil
}

// insertDiffDelta inserts a commit diff delta into the database.
func insertDiffDelta(commitID uint64, d model.DiffDelta, stmt *sql.Stmt) error {
	_, err := stmt.Exec(commitID, d.Status, d.Binary, d.Similarity, d.OldFilePath, d.NewFilePath)
	if err != nil {
		return err
	}
	return nil
}

// getAllUsers returns a map of all users IDs with their email address as keys.
func getAllUsers(db *sql.DB) (map[string]uint64, error) {
	rows, err := db.Query("SELECT id, email FROM users WHERE email IS NOT NULL AND email != ''")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	userIDs := map[string]uint64{}
	for rows.Next() {
		var email string
		var id uint64
		if err := rows.Scan(&id, &email); err != nil {
			return nil, err
		}
		userIDs[email] = id
	}

	return userIDs, nil
}

// genCommitHash generates a hash (mmh3) from commit fields.
// This hash can then be used to uniquely identify a commit.
// Typically, we want to make sure not to insert twice the same commit into the
// database after an eventual second repotool run on the same repository.
func genCommitHash(c model.Commit) string {
	h := mmh3.New128()

	io.WriteString(h, c.VCSID)
	io.WriteString(h, c.Message)
	io.WriteString(h, c.Author.Name)
	io.WriteString(h, c.Author.Email)
	io.WriteString(h, c.Committer.Name)
	io.WriteString(h, c.Committer.Email)
	io.WriteString(h, c.AuthorDate.String())
	io.WriteString(h, c.CommitDate.String())
	io.WriteString(h, strconv.FormatInt(int64(c.FileChangedCount), 10))
	io.WriteString(h, strconv.FormatInt(int64(c.InsertionsCount), 10))
	io.WriteString(h, strconv.FormatInt(int64(c.DeletionsCount), 10))

	return hex.EncodeToString(h.Sum(nil))
}

// getRepoID returns the repository id of a repo in repositories table.
// If repo is not in the table, then 0 is returned.
func getRepoID(db *sql.DB, r repo.Repo) (*uint64, error) {
	if db == nil {
		return nil, errors.New("nil database given")
	}

	var id *uint64
	// Clone URL is unique
	err := db.QueryRow("SELECT id FROM repositories WHERE clone_url=$1", r.GetCloneURL()).Scan(&id)
	switch {
	case err == sql.ErrNoRows:
		return nil, nil
	case err != nil:
		return nil, err
	}
	return id, nil
}

// genInsQuery generates a query string for an insertion in the database.
func genInsQuery(tableName string, fields ...string) string {
	var buf bytes.Buffer

	buf.WriteString(fmt.Sprintf("INSERT INTO %s(%s)\n",
		tableName, strings.Join(fields, ",")))
	buf.WriteString("VALUES(")

	for ind := range fields {
		if ind > 0 {
			buf.WriteString(",")
		}

		buf.WriteString(fmt.Sprintf("$%d", ind+1))
	}

	buf.WriteString(")\n")

	return buf.String()
}
<commit_msg>repotool-db: use a hashmap to store repositories IDs<commit_after>// Copyright 2014-2015 The DevMine authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package repotool-db is able to fetch information from a source code repository.
// Typically, it can get all commits, their authors and commiters and so on
// and is able to populate the information into a PostgreSQL database.
// Currently, on the Git VCS is supported.
package main

import (
	"bytes"
	"database/sql"
	"encoding/hex"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strconv"
	"strings"
	"sync"

	"github.com/golang/glog"
	_ "github.com/lib/pq"
	mmh3 "github.com/spaolacci/murmur3"

	"github.com/DevMine/repotool/config"
	"github.com/DevMine/repotool/model"
	"github.com/DevMine/repotool/repo"
)

const version = "0.1.0"

// database fields per tables
var (
	diffDeltaFields = []string{
		"commit_id",
		"file_status",
		"is_file_binary",
		"similarity",
		"old_file_path",
		"new_file_path"}

	commitFields = []string{
		"repository_id",
		"author_id",
		"committer_id",
		"hash",
		"vcs_id",
		"message",
		"author_date",
		"commit_date",
		"file_changed_count",
		"insertions_count",
		"deletions_count"}
)

// program flags
var (
	configPath    = flag.String("c", "", "configuration file")
	vflag         = flag.Bool("V", false, "print version.")
	cpuprofile    = flag.String("cpuprofile", "", "write cpu profile to file")
	depthflag     = flag.Uint("d", 0, "depth level where to find repositories")
	numGoroutines = flag.Uint("g", uint(runtime.NumCPU()), "max number of goroutines to spawn")
)

// globals
var (
	userIDs = map[string]uint64{}
	repoIDs = map[string]uint64{}
)

func main() {
	var err error

	flag.Usage = func() {
		fmt.Printf("usage: %s [OPTION(S)] [REPOSITORIES ROOT FOLDER]\n", os.Args[0])
		flag.PrintDefaults()
		os.Exit(0)
	}
	flag.Parse()

	if *vflag {
		fmt.Printf("%s - %s\n", filepath.Base(os.Args[0]), version)
		os.Exit(0)
	}

	if *cpuprofile != "" {
		var f *os.File
		f, err = os.Create(*cpuprofile)
		if err != nil {
			glog.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if len(flag.Args()) != 1 {
		fmt.Fprintln(os.Stderr, "invalid # of arguments")
		flag.Usage()
	}

	if len(*configPath) == 0 {
		glog.Fatal(errors.New("a configuration file must be specified"))
	}

	var cfg *config.Config
	cfg, err = config.ReadConfig(*configPath)
	if err != nil {
		glog.Fatal(err)
	}

	// Make sure we finish writing logs before exiting.
	defer glog.Flush()

	var db *sql.DB
	db, err = openDBSession(*cfg.Database)
	if err != nil {
		return
	}
	defer func() {
		db.Close()
		if err != nil {
			glog.Fatal(err)
		}
	}()

	if err = fetchAllUsers(db); err != nil {
		return
	}
	if err = fetchAllRepos(db); err != nil {
		return
	}

	reposPath := make(chan string, 0)
	var wg sync.WaitGroup
	for w := uint(0); w < *numGoroutines; w++ {
		wg.Add(1)
		go func() {
			for path := range reposPath {
				work := func() error {
					repository, err := repo.New(*cfg, path)
					if err != nil {
						return err
					}
					defer repository.Cleanup()

					if err = repository.FetchCommits(); err != nil {
						return err
					}

					if err = insertRepoData(db, repository); err != nil {
						return err
					}
					return nil
				}
				if err := work(); err != nil {
					glog.Error(err)
				}
			}
			wg.Done()
		}()
	}

	reposDir := flag.Arg(0)
	iterateRepos(reposPath, reposDir, *depthflag)

	close(reposPath)
	wg.Wait()

}

func iterateRepos(reposPath chan string, path string, depth uint) {
	fis, err := ioutil.ReadDir(path)
	if err != nil {
		glog.Fatal(err)
	}

	if depth == 0 {
		for _, fi := range fis {
			if !fi.IsDir() {
				if filepath.Ext(fi.Name()) != ".tar" {
					continue
				}
			}

			repoPath := filepath.Join(path, fi.Name())
			fmt.Println("adding repository:", repoPath, "to the pool")
			reposPath <- repoPath
		}
		return
	}

	for _, fi := range fis {
		if !fi.IsDir() {
			continue
		}

		iterateRepos(reposPath, filepath.Join(path, fi.Name()), depth-1)
	}
}

// openDBSession creates a session to the database.
func openDBSession(cfg config.DatabaseConfig) (*sql.DB, error) {
	dbURL := fmt.Sprintf(
		"user='%s' password='%s' host='%s' port=%d dbname='%s' sslmode='%s'",
		cfg.UserName, cfg.Password, cfg.HostName, cfg.Port, cfg.DBName, cfg.SSLMode)

	return sql.Open("postgres", dbURL)
}

// insertRepoData inserts repository data into the database, or updates it
// if it is already there.
func insertRepoData(db *sql.DB, r repo.Repo) error {
	if db == nil {
		return errors.New("nil database given")
	}

	repoID, ok := repoIDs[r.GetCloneURL()]
	if !ok {
		return errors.New("cannot find corresponding repository in database")
	}

	tx, err := db.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	commitStmt, err := tx.Prepare(genInsQuery("commits", commitFields...) + " RETURNING id")
	if err != nil {
		return err
	}

	deltaStmt, err := tx.Prepare(genInsQuery("commit_diff_deltas", diffDeltaFields...))
	if err != nil {
		return err
	}

	for _, c := range r.GetCommits() {
		if err := insertCommit(repoID, c, tx, commitStmt, deltaStmt); err != nil {
			return err
		}
	}

	if err := commitStmt.Close(); err != nil {
		return err
	}
	if err := deltaStmt.Close(); err != nil {
		return err
	}
	if err := tx.Commit(); err != nil {
		return err
	}

	return nil
}

// insertCommit inserts a commit into the database
func insertCommit(repoID uint64, c model.Commit, tx *sql.Tx, commitStmt, deltaStmt *sql.Stmt) error {
	authorID := userIDs[c.Author.Email]
	committerID := userIDs[c.Committer.Email]
	hash := genCommitHash(c)

	var commitID uint64
	err := commitStmt.QueryRow(
		repoID, authorID, committerID, hash,
		c.VCSID, c.Message, c.AuthorDate, c.CommitDate,
		c.FileChangedCount, c.InsertionsCount, c.DeletionsCount).Scan(&commitID)
	if err != nil {
		return err
	}

	for _, d := range c.DiffDelta {
		if err := insertDiffDelta(commitID, d, deltaStmt); err != nil {
			return err
		}
	}

	return nil
}

// insertDiffDelta inserts a commit diff delta into the database.
func insertDiffDelta(commitID uint64, d model.DiffDelta, stmt *sql.Stmt) error {
	_, err := stmt.Exec(commitID, d.Status, d.Binary, d.Similarity, d.OldFilePath, d.NewFilePath)
	if err != nil {
		return err
	}
	return nil
}

// fetchAllUsers fetch users IDs and put them into the userIDs global hashmap
// with their email address as keys.
func fetchAllUsers(db *sql.DB) error {
	rows, err := db.Query("SELECT id, email FROM users WHERE email IS NOT NULL AND email != ''")
	if err != nil {
		return err
	}
	defer rows.Close()

	for rows.Next() {
		var email string
		var id uint64
		if err := rows.Scan(&id, &email); err != nil {
			return err
		}
		userIDs[email] = id
	}

	return nil
}

func fetchAllRepos(db *sql.DB) error {
	rows, err := db.Query("SELECT id, clone_url FROM repositories")
	if err != nil {
		return err
	}
	defer rows.Close()

	for rows.Next() {
		var id uint64
		var cloneURL string
		if err := rows.Scan(&id, &cloneURL); err != nil {
			return err
		}
		repoIDs[cloneURL] = id
	}

	return nil
}

// genCommitHash generates a hash (mmh3) from commit fields.
// This hash can then be used to uniquely identify a commit.
// Typically, we want to make sure not to insert twice the same commit into the
// database after an eventual second repotool run on the same repository.
func genCommitHash(c model.Commit) string {
	h := mmh3.New128()

	io.WriteString(h, c.VCSID)
	io.WriteString(h, c.Message)
	io.WriteString(h, c.Author.Name)
	io.WriteString(h, c.Author.Email)
	io.WriteString(h, c.Committer.Name)
	io.WriteString(h, c.Committer.Email)
	io.WriteString(h, c.AuthorDate.String())
	io.WriteString(h, c.CommitDate.String())
	io.WriteString(h, strconv.FormatInt(int64(c.FileChangedCount), 10))
	io.WriteString(h, strconv.FormatInt(int64(c.InsertionsCount), 10))
	io.WriteString(h, strconv.FormatInt(int64(c.DeletionsCount), 10))

	return hex.EncodeToString(h.Sum(nil))
}

// genInsQuery generates a query string for an insertion in the database.
func genInsQuery(tableName string, fields ...string) string {
	var buf bytes.Buffer

	buf.WriteString(fmt.Sprintf("INSERT INTO %s(%s)\n",
		tableName, strings.Join(fields, ",")))
	buf.WriteString("VALUES(")

	for ind := range fields {
		if ind > 0 {
			buf.WriteString(",")
		}

		buf.WriteString(fmt.Sprintf("$%d", ind+1))
	}

	buf.WriteString(")\n")

	return buf.String()
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/vektra/tai64n"
)

func processRow(text string) {
	if !strings.HasPrefix(text, "@") {
		fmt.Println(text)
		return
	}
	splited := strings.SplitN(text, " ", 2)
	if len(splited) != 2 {
		fmt.Println(text)
		return
	}
	time := tai64n.ParseTAI64NLabel(splited[0])
	if time == nil {
		fmt.Println(text)
		return
	}
	fmt.Println(time.Time().Format("2006-01-02 15:04:05.000000000"), splited[1])
}

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		text := scanner.Text()
		processRow(text)
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Use CRLF<commit_after>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/vektra/tai64n"
)

func processRow(text string) {
	if !strings.HasPrefix(text, "@") {
		fmt.Println(text)
		return
	}
	splited := strings.SplitN(text, " ", 2)
	if len(splited) != 2 {
		fmt.Println(text)
		return
	}
	time := tai64n.ParseTAI64NLabel(splited[0])
	if time == nil {
		fmt.Println(text)
		return
	}
	fmt.Printf("%s %s\r\n", time.Time().Format("2006-01-02 15:04:05.000000000"), splited[1])
}

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		text := scanner.Text()
		processRow(text)
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// Hap - the simple and effective provisioner
// Copyright (c) 2017 GWoo (https://github.com/gwoo)
// The BSD License http://opensource.org/licenses/bsd-license.php.

package hap

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"os/user"
	"path/filepath"
	"strings"

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

// SSHConfig holds the config for ssh connections
type SSHConfig struct {
	Addr         string
	Username     string
	Identity     string
	Password     string
	ClientConfig *ssh.ClientConfig
}

// NewClientConfig constructs a new *ssh.ClientConfig
func NewClientConfig(config SSHConfig) (*ssh.ClientConfig, error) {
	methods := make([]ssh.AuthMethod, 0)
	if sock, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
		method := ssh.PublicKeysCallback(agent.NewClient(sock).Signers)
		methods = append(methods, method)
	}
	if config.Username == "" {
		u, _ := user.Current()
		config.Username = u.Name
	}
	if config.Identity != "" {
		if method := NewPublicKeyMethod(config.Identity); method != nil {
			methods = append(methods, method)
		}
	} else {
		home := os.Getenv("HOME")
		keys := []string{home + "/.ssh/id_rsa", home + "/.ssh/id_dsa"}
		for _, key := range keys {
			if method := NewPublicKeyMethod(key); method != nil {
				methods = append(methods, method)
			}
		}
	}
	if config.Password != "" {
		methods = append(methods, ssh.Password(config.Password))
	}
	cfg := &ssh.ClientConfig{User: config.Username, Auth: methods}
	cfg.SetDefaults()
	return cfg, nil
}

// NewKeyFile takes a key and returns the key file
func NewKeyFile(key string) (string, error) {
	if string(key[0]) == "~" {
		u, err := user.Current()
		if err != nil {
			return "", fmt.Errorf("[identity] %s", err)
		}
		key = strings.Replace(key, "~", u.HomeDir, 1)
	}
	return filepath.EvalSymlinks(key)
}

// NewKey parses and returns the interface for the key type (rsa, dss, etc)
func NewKey(key string) (ssh.Signer, error) {
	file, err := NewKeyFile(key)
	if err != nil {
		return nil, err
	}
	b, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}
	return ssh.ParsePrivateKey(b)
}

// NewPublicKeyMethod creates a new auth method for public keys
func NewPublicKeyMethod(key string) ssh.AuthMethod {
	pk, err := NewKey(key)
	if err != nil {
		return nil
	}
	return ssh.PublicKeys(pk)
}
<commit_msg>ignore hostkeycallback for now<commit_after>// Hap - the simple and effective provisioner
// Copyright (c) 2017 GWoo (https://github.com/gwoo)
// The BSD License http://opensource.org/licenses/bsd-license.php.

package hap

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"os/user"
	"path/filepath"
	"strings"

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

// SSHConfig holds the config for ssh connections
type SSHConfig struct {
	Addr         string
	Username     string
	Identity     string
	Password     string
	ClientConfig *ssh.ClientConfig
}

// NewClientConfig constructs a new *ssh.ClientConfig
func NewClientConfig(config SSHConfig) (*ssh.ClientConfig, error) {
	methods := make([]ssh.AuthMethod, 0)
	if sock, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
		method := ssh.PublicKeysCallback(agent.NewClient(sock).Signers)
		methods = append(methods, method)
	}
	if config.Username == "" {
		u, _ := user.Current()
		config.Username = u.Name
	}
	if config.Identity != "" {
		if method := NewPublicKeyMethod(config.Identity); method != nil {
			methods = append(methods, method)
		}
	} else {
		home := os.Getenv("HOME")
		keys := []string{home + "/.ssh/id_rsa", home + "/.ssh/id_dsa"}
		for _, key := range keys {
			if method := NewPublicKeyMethod(key); method != nil {
				methods = append(methods, method)
			}
		}
	}
	if config.Password != "" {
		methods = append(methods, ssh.Password(config.Password))
	}
	cfg := &ssh.ClientConfig{
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
		User:            config.Username,
		Auth:            methods,
	}
	cfg.SetDefaults()
	return cfg, nil
}

// NewKeyFile takes a key and returns the key file
func NewKeyFile(key string) (string, error) {
	if string(key[0]) == "~" {
		u, err := user.Current()
		if err != nil {
			return "", fmt.Errorf("[identity] %s", err)
		}
		key = strings.Replace(key, "~", u.HomeDir, 1)
	}
	return filepath.EvalSymlinks(key)
}

// NewKey parses and returns the interface for the key type (rsa, dss, etc)
func NewKey(key string) (ssh.Signer, error) {
	file, err := NewKeyFile(key)
	if err != nil {
		return nil, err
	}
	b, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}
	return ssh.ParsePrivateKey(b)
}

// NewPublicKeyMethod creates a new auth method for public keys
func NewPublicKeyMethod(key string) ssh.AuthMethod {
	pk, err := NewKey(key)
	if err != nil {
		return nil
	}
	return ssh.PublicKeys(pk)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"os"
	"regexp"
	"time"

	flag "github.com/cespare/pflag"
)

const (
	binaryDetectionBytes = 8000 // Same as git
)

var (
	dryRun  bool
	verbose bool
)

func init() {
	flag.BoolVarP(&dryRun, "dry-run", "d", false, "Print out what would be changed without changing any files.")
	flag.BoolVarP(&verbose, "verbose", "v", false, "Print out detailed information about each match.")
	flag.Usage = func() { usage(0) }
	flag.Parse()

	rand.Seed(time.Now().UnixNano())
}

func usage(status int) {
	fmt.Printf(`Usage:
		%s [OPTIONS] <FIND> <REPLACE> <FILE1> <FILE2> ...
where OPTIONS are
`, os.Args[0])
	flag.PrintDefaults()
	os.Exit(status)
}

// isBinary guesses whether a file is binary by reading the first X bytes and seeing if there are any nulls.
// Assumes the file is at the beginning.
func isBinary(file *os.File) bool {
	defer file.Seek(0, 0)
	buf := make([]byte, binaryDetectionBytes)
	for {
		n, err := file.Read(buf)
		if err != nil && err != io.EOF {
			return false
		}
		if n == 0 {
			break
		}
		for i := 0; i < n; i++ {
			if buf[i] == 0x00 {
				return true
			}
		}
		buf = buf[n:]
	}
	return false
}

// isRegular determines whether the file is a regular file or not.
func isRegular(filename string) bool {
	stat, err := os.Lstat(filename)
	if err != nil {
		return false
	}
	return stat.Mode().IsRegular()
}

func main() {
	args := flag.Args()
	if len(args) < 3 {
		usage(1)
	}
	findPattern := args[0]
	replacePattern := args[1]
	files := args[2:]

	find, err := regexp.Compile(findPattern)
	if err != nil {
		fmt.Println("Bad pattern for FIND:", err)
		os.Exit(1)
	}
	replace := []byte(replacePattern)

fileLoop:
	for _, filename := range files {
		if !isRegular(filename) {
			if verbose {
				fmt.Fprintf(os.Stderr, "Skipping %s (not a regular file).\n", filename)
			}
			continue
		}
		file, err := os.Open(filename)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			continue
		}
		defer file.Close()
		if isBinary(file) {
			if verbose {
				fmt.Fprintf(os.Stderr, "Skipping %s (binary file).\n", filename)
			}
			continue
		}
		stat, err := file.Stat()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Could not stat file %s: %s\n", filename, err)
			continue
		}
		if stat.Mode().Perm()&0222 == 0 {
			fmt.Fprintf(os.Stderr, "Skipping write-protected file %s\n", filename)
			continue
		}

		var temp *os.File
		if !dryRun {
			temp, err = ioutil.TempFile(".", filename)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				continue
			}
			temp.Chmod(stat.Mode())
			defer temp.Close()
		}

		matched := false
		reader := bufio.NewReader(file)
		line := 0
		done := false

		for !done {
			line++
			line, err := reader.ReadBytes('\n')
			if err != nil {
				if err == io.EOF {
					done = true
					if len(line) == 0 {
						fmt.Println("zero")
						break
					}
				} else {
					fmt.Fprintln(os.Stderr, err)
					break
				}
			}

			indices := find.FindAllIndex(line, -1)
			if indices == nil {
				if !dryRun {
					_, err := temp.Write(line)
					if err != nil {
						fmt.Fprintln(os.Stderr, err)
						continue fileLoop
					}
				}
				continue
			}
			if !matched {
				// Only print out the filename in blue if we're in verbose mode.
				if verbose {
					fmt.Println(colorize(filename, ColorBlue))
				} else {
					fmt.Println(filename)
				}
				matched = true
			}
			if verbose {
				highlighted := highlight(line, ColorRed, indices)
				replacedAndHighlighted := subAndHighlight(line, find, replace, ColorGreen, indices)

				fmt.Print(colorize("- ", ColorRed))
				os.Stdout.Write(highlighted)
				fmt.Print(colorize("+ ", ColorGreen))
				os.Stdout.Write(replacedAndHighlighted)
			}
			if !dryRun {
				replaced := substitute(line, find, replace, indices)
				_, err := temp.Write(replaced)
				if err != nil {
					fmt.Fprintln(os.Stderr, err)
					continue fileLoop
				}
			}
		}

		if !dryRun {
			// We'll .Close these twice, but that's fine.
			temp.Close()
			file.Close()
			if err := os.Rename(temp.Name(), filename); err != nil {
				fmt.Fprintln(os.Stderr, err)
				continue
			}
		}
	}
}
<commit_msg>Remove print mistakenly left in<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"os"
	"regexp"
	"time"

	flag "github.com/cespare/pflag"
)

const (
	binaryDetectionBytes = 8000 // Same as git
)

var (
	dryRun  bool
	verbose bool
)

func init() {
	flag.BoolVarP(&dryRun, "dry-run", "d", false, "Print out what would be changed without changing any files.")
	flag.BoolVarP(&verbose, "verbose", "v", false, "Print out detailed information about each match.")
	flag.Usage = func() { usage(0) }
	flag.Parse()

	rand.Seed(time.Now().UnixNano())
}

func usage(status int) {
	fmt.Printf(`Usage:
		%s [OPTIONS] <FIND> <REPLACE> <FILE1> <FILE2> ...
where OPTIONS are
`, os.Args[0])
	flag.PrintDefaults()
	os.Exit(status)
}

// isBinary guesses whether a file is binary by reading the first X bytes and seeing if there are any nulls.
// Assumes the file is at the beginning.
func isBinary(file *os.File) bool {
	defer file.Seek(0, 0)
	buf := make([]byte, binaryDetectionBytes)
	for {
		n, err := file.Read(buf)
		if err != nil && err != io.EOF {
			return false
		}
		if n == 0 {
			break
		}
		for i := 0; i < n; i++ {
			if buf[i] == 0x00 {
				return true
			}
		}
		buf = buf[n:]
	}
	return false
}

// isRegular determines whether the file is a regular file or not.
func isRegular(filename string) bool {
	stat, err := os.Lstat(filename)
	if err != nil {
		return false
	}
	return stat.Mode().IsRegular()
}

func main() {
	args := flag.Args()
	if len(args) < 3 {
		usage(1)
	}
	findPattern := args[0]
	replacePattern := args[1]
	files := args[2:]

	find, err := regexp.Compile(findPattern)
	if err != nil {
		fmt.Println("Bad pattern for FIND:", err)
		os.Exit(1)
	}
	replace := []byte(replacePattern)

fileLoop:
	for _, filename := range files {
		if !isRegular(filename) {
			if verbose {
				fmt.Fprintf(os.Stderr, "Skipping %s (not a regular file).\n", filename)
			}
			continue
		}
		file, err := os.Open(filename)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			continue
		}
		defer file.Close()
		if isBinary(file) {
			if verbose {
				fmt.Fprintf(os.Stderr, "Skipping %s (binary file).\n", filename)
			}
			continue
		}
		stat, err := file.Stat()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Could not stat file %s: %s\n", filename, err)
			continue
		}
		if stat.Mode().Perm()&0222 == 0 {
			fmt.Fprintf(os.Stderr, "Skipping write-protected file %s\n", filename)
			continue
		}

		var temp *os.File
		if !dryRun {
			temp, err = ioutil.TempFile(".", filename)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				continue
			}
			temp.Chmod(stat.Mode())
			defer temp.Close()
		}

		matched := false
		reader := bufio.NewReader(file)
		line := 0
		done := false

		for !done {
			line++
			line, err := reader.ReadBytes('\n')
			if err != nil {
				if err == io.EOF {
					done = true
					if len(line) == 0 {
						break
					}
				} else {
					fmt.Fprintln(os.Stderr, err)
					break
				}
			}

			indices := find.FindAllIndex(line, -1)
			if indices == nil {
				if !dryRun {
					_, err := temp.Write(line)
					if err != nil {
						fmt.Fprintln(os.Stderr, err)
						continue fileLoop
					}
				}
				continue
			}
			if !matched {
				// Only print out the filename in blue if we're in verbose mode.
				if verbose {
					fmt.Println(colorize(filename, ColorBlue))
				} else {
					fmt.Println(filename)
				}
				matched = true
			}
			if verbose {
				highlighted := highlight(line, ColorRed, indices)
				replacedAndHighlighted := subAndHighlight(line, find, replace, ColorGreen, indices)

				fmt.Print(colorize("- ", ColorRed))
				os.Stdout.Write(highlighted)
				fmt.Print(colorize("+ ", ColorGreen))
				os.Stdout.Write(replacedAndHighlighted)
			}
			if !dryRun {
				replaced := substitute(line, find, replace, indices)
				_, err := temp.Write(replaced)
				if err != nil {
					fmt.Fprintln(os.Stderr, err)
					continue fileLoop
				}
			}
		}

		if !dryRun {
			// We'll .Close these twice, but that's fine.
			temp.Close()
			file.Close()
			if err := os.Rename(temp.Name(), filename); err != nil {
				fmt.Fprintln(os.Stderr, err)
				continue
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package tls partially implements TLS 1.2, as specified in RFC 5246.
package tls

// BUG(agl): The crypto/tls package does not implement countermeasures
// against Lucky13 attacks on CBC-mode encryption. See
// http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and
// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"strings"
	"time"
)

// Server returns a new TLS server side connection
// using conn as the underlying transport.
// The configuration config must be non-nil and must include
// at least one certificate or else set GetCertificate.
func Server(conn net.Conn, config *Config) *Conn {
	return &Conn{conn: conn, config: config}
}

// Client returns a new TLS client side connection
// using conn as the underlying transport.
// The config cannot be nil: users must set either ServerName or
// InsecureSkipVerify in the config.
func Client(conn net.Conn, config *Config) *Conn {
	return &Conn{conn: conn, config: config, isClient: true}
}

// A listener implements a network listener (net.Listener) for TLS connections.
type listener struct {
	net.Listener
	config *Config
}

// Accept waits for and returns the next incoming TLS connection.
// The returned connection c is a *tls.Conn.
func (l *listener) Accept() (c net.Conn, err error) {
	c, err = l.Listener.Accept()
	if err != nil {
		return
	}
	c = Server(c, l.config)
	return
}

// NewListener creates a Listener which accepts connections from an inner
// Listener and wraps each connection with Server.
// The configuration config must be non-nil and must include
// at least one certificate or else set GetCertificate.
func NewListener(inner net.Listener, config *Config) net.Listener {
	l := new(listener)
	l.Listener = inner
	l.config = config
	return l
}

// Listen creates a TLS listener accepting connections on the
// given network address using net.Listen.
// The configuration config must be non-nil and must include
// at least one certificate or else set GetCertificate.
func Listen(network, laddr string, config *Config) (net.Listener, error) {
	if config == nil || (len(config.Certificates) == 0 && config.GetCertificate == nil) {
		return nil, errors.New("tls: neither Certificates nor GetCertificate set in Config")
	}
	l, err := net.Listen(network, laddr)
	if err != nil {
		return nil, err
	}
	return NewListener(l, config), nil
}

type timeoutError struct{}

func (timeoutError) Error() string   { return "tls: DialWithDialer timed out" }
func (timeoutError) Timeout() bool   { return true }
func (timeoutError) Temporary() bool { return true }

// DialWithDialer connects to the given network address using dialer.Dial and
// then initiates a TLS handshake, returning the resulting TLS connection. Any
// timeout or deadline given in the dialer apply to connection and TLS
// handshake as a whole.
//
// DialWithDialer interprets a nil configuration as equivalent to the zero
// configuration; see the documentation of Config for the defaults.
func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error) {
	// We want the Timeout and Deadline values from dialer to cover the
	// whole process: TCP connection and TLS handshake. This means that we
	// also need to start our own timers now.
	timeout := dialer.Timeout

	if !dialer.Deadline.IsZero() {
		deadlineTimeout := dialer.Deadline.Sub(time.Now())
		if timeout == 0 || deadlineTimeout < timeout {
			timeout = deadlineTimeout
		}
	}

	var errChannel chan error

	if timeout != 0 {
		errChannel = make(chan error, 2)
		time.AfterFunc(timeout, func() {
			errChannel <- timeoutError{}
		})
	}

	rawConn, err := dialer.Dial(network, addr)
	if err != nil {
		return nil, err
	}

	colonPos := strings.LastIndex(addr, ":")
	if colonPos == -1 {
		colonPos = len(addr)
	}
	hostname := addr[:colonPos]

	if config == nil {
		config = defaultConfig()
	}
	// If no ServerName is set, infer the ServerName
	// from the hostname we're connecting to.
	if config.ServerName == "" {
		// Make a copy to avoid polluting argument or default.
		c := *config
		c.ServerName = hostname
		config = &c
	}

	conn := Client(rawConn, config)

	if timeout == 0 {
		err = conn.Handshake()
	} else {
		go func() {
			errChannel <- conn.Handshake()
		}()

		err = <-errChannel
	}

	if err != nil {
		rawConn.Close()
		return nil, err
	}

	return conn, nil
}

// Dial connects to the given network address using net.Dial
// and then initiates a TLS handshake, returning the resulting
// TLS connection.
// Dial interprets a nil configuration as equivalent to
// the zero configuration; see the documentation of Config
// for the defaults.
func Dial(network, addr string, config *Config) (*Conn, error) {
	return DialWithDialer(new(net.Dialer), network, addr, config)
}

// LoadX509KeyPair reads and parses a public/private key pair from a pair of
// files. The files must contain PEM encoded data. On successful return,
// Certificate.Leaf will be nil because the parsed form of the certificate is
// not retained.
func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
	certPEMBlock, err := ioutil.ReadFile(certFile)
	if err != nil {
		return Certificate{}, err
	}
	keyPEMBlock, err := ioutil.ReadFile(keyFile)
	if err != nil {
		return Certificate{}, err
	}
	return X509KeyPair(certPEMBlock, keyPEMBlock)
}

// X509KeyPair parses a public/private key pair from a pair of
// PEM encoded data. On successful return, Certificate.Leaf will be nil because
// the parsed form of the certificate is not retained.
func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
	fail := func(err error) (Certificate, error) { return Certificate{}, err }

	var cert Certificate
	var skippedBlockTypes []string
	for {
		var certDERBlock *pem.Block
		certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
		if certDERBlock == nil {
			break
		}
		if certDERBlock.Type == "CERTIFICATE" {
			cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
		} else {
			skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type)
		}
	}

	if len(cert.Certificate) == 0 {
		if len(skippedBlockTypes) == 0 {
			return fail(errors.New("crypto/tls: failed to find any PEM data in certificate input"))
		} else if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") {
			return fail(errors.New("crypto/tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched"))
		} else {
			return fail(fmt.Errorf("crypto/tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
		}
	}

	skippedBlockTypes = skippedBlockTypes[:0]
	var keyDERBlock *pem.Block
	for {
		keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
		if keyDERBlock == nil {
			if len(skippedBlockTypes) == 0 {
				return fail(errors.New("crypto/tls: failed to find any PEM data in key input"))
			} else if len(skippedBlockTypes) == 1 && skippedBlockTypes[0] == "CERTIFICATE" {
				return fail(errors.New("crypto/tls: found a certificate rather than a key in the PEM for the private key"))
			} else {
				return fail(fmt.Errorf("crypto/tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
			}
		}
		if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
			break
		}
		skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type)
	}

	var err error
	cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
	if err != nil {
		return fail(err)
	}

	// We don't need to parse the public key for TLS, but we so do anyway
	// to check that it looks sane and matches the private key.
	x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
	if err != nil {
		return fail(err)
	}

	switch pub := x509Cert.PublicKey.(type) {
	case *rsa.PublicKey:
		priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
		if !ok {
			return fail(errors.New("crypto/tls: private key type does not match public key type"))
		}
		if pub.N.Cmp(priv.N) != 0 {
			return fail(errors.New("crypto/tls: private key does not match public key"))
		}
	case *ecdsa.PublicKey:
		priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
		if !ok {
			return fail(errors.New("crypto/tls: private key type does not match public key type"))

		}
		if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
			return fail(errors.New("crypto/tls: private key does not match public key"))
		}
	default:
		return fail(errors.New("crypto/tls: unknown public key algorithm"))
	}

	return cert, nil
}

// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
	if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
		return key, nil
	}
	if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
		switch key := key.(type) {
		case *rsa.PrivateKey, *ecdsa.PrivateKey:
			return key, nil
		default:
			return nil, errors.New("crypto/tls: found unknown private key type in PKCS#8 wrapping")
		}
	}
	if key, err := x509.ParseECPrivateKey(der); err == nil {
		return key, nil
	}

	return nil, errors.New("crypto/tls: failed to parse private key")
}
<commit_msg>all: remove public named return values when useless<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package tls partially implements TLS 1.2, as specified in RFC 5246.
package tls

// BUG(agl): The crypto/tls package does not implement countermeasures
// against Lucky13 attacks on CBC-mode encryption. See
// http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and
// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"strings"
	"time"
)

// Server returns a new TLS server side connection
// using conn as the underlying transport.
// The configuration config must be non-nil and must include
// at least one certificate or else set GetCertificate.
func Server(conn net.Conn, config *Config) *Conn {
	return &Conn{conn: conn, config: config}
}

// Client returns a new TLS client side connection
// using conn as the underlying transport.
// The config cannot be nil: users must set either ServerName or
// InsecureSkipVerify in the config.
func Client(conn net.Conn, config *Config) *Conn {
	return &Conn{conn: conn, config: config, isClient: true}
}

// A listener implements a network listener (net.Listener) for TLS connections.
type listener struct {
	net.Listener
	config *Config
}

// Accept waits for and returns the next incoming TLS connection.
// The returned connection is of type *Conn.
func (l *listener) Accept() (net.Conn, error) {
	c, err := l.Listener.Accept()
	if err != nil {
		return nil, err
	}
	return Server(c, l.config), nil
}

// NewListener creates a Listener which accepts connections from an inner
// Listener and wraps each connection with Server.
// The configuration config must be non-nil and must include
// at least one certificate or else set GetCertificate.
func NewListener(inner net.Listener, config *Config) net.Listener {
	l := new(listener)
	l.Listener = inner
	l.config = config
	return l
}

// Listen creates a TLS listener accepting connections on the
// given network address using net.Listen.
// The configuration config must be non-nil and must include
// at least one certificate or else set GetCertificate.
func Listen(network, laddr string, config *Config) (net.Listener, error) {
	if config == nil || (len(config.Certificates) == 0 && config.GetCertificate == nil) {
		return nil, errors.New("tls: neither Certificates nor GetCertificate set in Config")
	}
	l, err := net.Listen(network, laddr)
	if err != nil {
		return nil, err
	}
	return NewListener(l, config), nil
}

type timeoutError struct{}

func (timeoutError) Error() string   { return "tls: DialWithDialer timed out" }
func (timeoutError) Timeout() bool   { return true }
func (timeoutError) Temporary() bool { return true }

// DialWithDialer connects to the given network address using dialer.Dial and
// then initiates a TLS handshake, returning the resulting TLS connection. Any
// timeout or deadline given in the dialer apply to connection and TLS
// handshake as a whole.
//
// DialWithDialer interprets a nil configuration as equivalent to the zero
// configuration; see the documentation of Config for the defaults.
func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error) {
	// We want the Timeout and Deadline values from dialer to cover the
	// whole process: TCP connection and TLS handshake. This means that we
	// also need to start our own timers now.
	timeout := dialer.Timeout

	if !dialer.Deadline.IsZero() {
		deadlineTimeout := dialer.Deadline.Sub(time.Now())
		if timeout == 0 || deadlineTimeout < timeout {
			timeout = deadlineTimeout
		}
	}

	var errChannel chan error

	if timeout != 0 {
		errChannel = make(chan error, 2)
		time.AfterFunc(timeout, func() {
			errChannel <- timeoutError{}
		})
	}

	rawConn, err := dialer.Dial(network, addr)
	if err != nil {
		return nil, err
	}

	colonPos := strings.LastIndex(addr, ":")
	if colonPos == -1 {
		colonPos = len(addr)
	}
	hostname := addr[:colonPos]

	if config == nil {
		config = defaultConfig()
	}
	// If no ServerName is set, infer the ServerName
	// from the hostname we're connecting to.
	if config.ServerName == "" {
		// Make a copy to avoid polluting argument or default.
		c := *config
		c.ServerName = hostname
		config = &c
	}

	conn := Client(rawConn, config)

	if timeout == 0 {
		err = conn.Handshake()
	} else {
		go func() {
			errChannel <- conn.Handshake()
		}()

		err = <-errChannel
	}

	if err != nil {
		rawConn.Close()
		return nil, err
	}

	return conn, nil
}

// Dial connects to the given network address using net.Dial
// and then initiates a TLS handshake, returning the resulting
// TLS connection.
// Dial interprets a nil configuration as equivalent to
// the zero configuration; see the documentation of Config
// for the defaults.
func Dial(network, addr string, config *Config) (*Conn, error) {
	return DialWithDialer(new(net.Dialer), network, addr, config)
}

// LoadX509KeyPair reads and parses a public/private key pair from a pair of
// files. The files must contain PEM encoded data. On successful return,
// Certificate.Leaf will be nil because the parsed form of the certificate is
// not retained.
func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
	certPEMBlock, err := ioutil.ReadFile(certFile)
	if err != nil {
		return Certificate{}, err
	}
	keyPEMBlock, err := ioutil.ReadFile(keyFile)
	if err != nil {
		return Certificate{}, err
	}
	return X509KeyPair(certPEMBlock, keyPEMBlock)
}

// X509KeyPair parses a public/private key pair from a pair of
// PEM encoded data. On successful return, Certificate.Leaf will be nil because
// the parsed form of the certificate is not retained.
func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
	fail := func(err error) (Certificate, error) { return Certificate{}, err }

	var cert Certificate
	var skippedBlockTypes []string
	for {
		var certDERBlock *pem.Block
		certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
		if certDERBlock == nil {
			break
		}
		if certDERBlock.Type == "CERTIFICATE" {
			cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
		} else {
			skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type)
		}
	}

	if len(cert.Certificate) == 0 {
		if len(skippedBlockTypes) == 0 {
			return fail(errors.New("crypto/tls: failed to find any PEM data in certificate input"))
		} else if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") {
			return fail(errors.New("crypto/tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched"))
		} else {
			return fail(fmt.Errorf("crypto/tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
		}
	}

	skippedBlockTypes = skippedBlockTypes[:0]
	var keyDERBlock *pem.Block
	for {
		keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
		if keyDERBlock == nil {
			if len(skippedBlockTypes) == 0 {
				return fail(errors.New("crypto/tls: failed to find any PEM data in key input"))
			} else if len(skippedBlockTypes) == 1 && skippedBlockTypes[0] == "CERTIFICATE" {
				return fail(errors.New("crypto/tls: found a certificate rather than a key in the PEM for the private key"))
			} else {
				return fail(fmt.Errorf("crypto/tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
			}
		}
		if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
			break
		}
		skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type)
	}

	var err error
	cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
	if err != nil {
		return fail(err)
	}

	// We don't need to parse the public key for TLS, but we so do anyway
	// to check that it looks sane and matches the private key.
	x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
	if err != nil {
		return fail(err)
	}

	switch pub := x509Cert.PublicKey.(type) {
	case *rsa.PublicKey:
		priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
		if !ok {
			return fail(errors.New("crypto/tls: private key type does not match public key type"))
		}
		if pub.N.Cmp(priv.N) != 0 {
			return fail(errors.New("crypto/tls: private key does not match public key"))
		}
	case *ecdsa.PublicKey:
		priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
		if !ok {
			return fail(errors.New("crypto/tls: private key type does not match public key type"))

		}
		if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
			return fail(errors.New("crypto/tls: private key does not match public key"))
		}
	default:
		return fail(errors.New("crypto/tls: unknown public key algorithm"))
	}

	return cert, nil
}

// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
	if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
		return key, nil
	}
	if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
		switch key := key.(type) {
		case *rsa.PrivateKey, *ecdsa.PrivateKey:
			return key, nil
		default:
			return nil, errors.New("crypto/tls: found unknown private key type in PKCS#8 wrapping")
		}
	}
	if key, err := x509.ParseECPrivateKey(der); err == nil {
		return key, nil
	}

	return nil, errors.New("crypto/tls: failed to parse private key")
}
<|endoftext|>


<commit_before>/**
 * "talk"
 */

package tok

import (
	"errors"
	"net/http"
)

var ErrOffline = errors.New("offline")
var ErrQueueRequired = errors.New("queue is required")

//Application can interact with tok via this interface
type Actor interface {
	Auth(r *http.Request) (interface{}, error)         //auth against http request. return uid if auth success
	BeforeReceive(uid interface{}, data []byte) []byte //is invoked every time the server receive valid payload
	OnReceive(uid interface{}, data []byte)            //is invoked every time the server receive valid payload
	BeforeSend(uid interface{}, data []byte) []byte    //is invoked if message is sent successfully. count mean copy quantity
	OnSent(uid interface{}, data []byte, count int)    //is invoked if message is sent successfully. count mean copy quantity
	OnCache(uid interface{})                           //is invoked after message caching
	//is invoked after a connection has been closed
	//active, count of active connections for this user
	OnClose(uid interface{}, active int)
	Ping() []byte             //Build ping payload.  auto ping feature will be disabled if this method return nil
	Bye(reason string) []byte //Build payload for different reason before connection is closed
}

const (
	META_HEADER = "tok-meta"
)
<commit_msg>update header name<commit_after>/**
 * "talk"
 */

package tok

import (
	"errors"
	"net/http"
)

var ErrOffline = errors.New("offline")
var ErrQueueRequired = errors.New("queue is required")

//Application can interact with tok via this interface
type Actor interface {
	Auth(r *http.Request) (interface{}, error)         //auth against http request. return uid if auth success
	BeforeReceive(uid interface{}, data []byte) []byte //is invoked every time the server receive valid payload
	OnReceive(uid interface{}, data []byte)            //is invoked every time the server receive valid payload
	BeforeSend(uid interface{}, data []byte) []byte    //is invoked if message is sent successfully. count mean copy quantity
	OnSent(uid interface{}, data []byte, count int)    //is invoked if message is sent successfully. count mean copy quantity
	OnCache(uid interface{})                           //is invoked after message caching
	//is invoked after a connection has been closed
	//active, count of active connections for this user
	OnClose(uid interface{}, active int)
	Ping() []byte             //Build ping payload.  auto ping feature will be disabled if this method return nil
	Bye(reason string) []byte //Build payload for different reason before connection is closed
}

const (
	META_HEADER = "Tok-Meta"
)
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"github.com/kylelemons/go-gypsy/yaml"
	"log"
	"os"
	"os/exec"
	"sort"
	"strings"
	"time"
)

// Default configuration file
var conffile = flag.String("c", ".tsmrc", "config file")

// Config variables. Provide a default for tarsnap(1) path.
var cfgTarsnapBin string = "/usr/local/bin/tarsnap"
var cfgTarsnapArgs []string
var cfgBackupDirs []string
var cfgExcludeFile string

// Templates for time.Parse()
const iso8601 = "2006-01-02"
const nightly = "nightly-2006-01-02"
const adhoc = "adhoc-2006-01-02_1504"

const day = time.Hour * 24

// Shamefully "borrowed" from src/cmd/go/main.go
// Flattens a mix of strings and slices of strings into a single slice.
func commandArgs(args ...interface{}) []string {
	var x []string
	for _, arg := range args {
		switch arg := arg.(type) {
		case []string:
			x = append(x, arg...)
		case string:
			x = append(x, arg)
		default:
			panic("commandArgs: invalid argument")
		}
	}
	return x
}

// Creates a new Tarsnap archive
func runBackup(archiveName string) {
	log.Printf("Starting backup %s\n", archiveName)
	args := commandArgs("-c", "-f", archiveName, cfgTarsnapArgs, cfgBackupDirs)
	backup := exec.Command(cfgTarsnapBin, args...)
	var stderr bytes.Buffer
	backup.Stderr = &stderr
	backuperr := backup.Run()
	if backuperr != nil {
		log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime)
		log.Println(stderr.String())
		log.Fatal(backuperr)
	}
	log.Println("Backup finished")
}

// Deletes a Tarsnap archive
func deleteBackup(backup string) {
	deletecmd := exec.Command(cfgTarsnapBin, "-d", "-f", backup)
	err := deletecmd.Run()
	if err != nil {
		log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime)
		log.Fatal(err)
	}
}

// Runs expiry against backup archives
func expireBackups(w, m time.Time, reallyExpire bool) {
	listcmd := exec.Command(cfgTarsnapBin, "--list-archives")
	var out bytes.Buffer
	listcmd.Stdout = &out
	listerr := listcmd.Run()
	if listerr != nil {
		log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime)
		log.Fatal(listerr)
	}
	backups := strings.Split(strings.TrimSuffix(out.String(), "\n"), "\n")
	sort.Strings(backups)

	for i := 0; i < len(backups); i++ {
		backup, _ := time.Parse(nightly, backups[i])
		eom := time.Date(backup.Year(), backup.Month()+1, 0, 0, 0, 0, 0, backup.Location())
		if (backup.Before(w) && backup.Day() != eom.Day()) || backup.Before(m) {
			if reallyExpire {
				log.Println("Expiring backup", backups[i])
				deleteBackup(backups[i])
			} else {
				log.Println("Expired backup", backups[i])
			}
		} else {
			if reallyExpire {
				log.Println("Keeping backup", backups[i])
			} else {
				log.Println("Current backup", backups[i])
			}
		}
	}
}

func main() {
	flag.Parse()

	config, conferr := yaml.ReadFile(*conffile)
	if conferr != nil {
		log.Fatalf("Read config %q: %s", *conffile, conferr)
	}

	tmpTarsnapBin, _ := config.Get("TarsnapBin")
	if tmpTarsnapBin != "" {
		cfgTarsnapBin = tmpTarsnapBin
	}

	count, err := config.Count("TarsnapArgs")
	for i := 0; i < count; i++ {
		s := fmt.Sprintf("TarsnapArgs[%d]", i)
		t, err := config.Get(s)
		if err != nil {
			log.Fatal(err)
		}
		// Remove any quotes from the arg - used to protect
		// options (starting with a -)
		t = strings.Replace(t, `"`, ``, -1)
		cfgTarsnapArgs = append(cfgTarsnapArgs, t)
	}

	count, err = config.Count("BackupDirs")
	if err != nil {
		log.Fatal("No backup directories specified")
	}
	for i := 0; i < count; i++ {
		s := fmt.Sprintf("BackupDirs[%d]", i)
		t, err := config.Get(s)
		if err != nil {
			log.Fatal(err)
		}
		cfgBackupDirs = append(cfgBackupDirs, t)
	}

	cfgExcludeFile, err := config.Get("ExcludeFile")
	if err == nil {
		cfgTarsnapArgs = append(cfgTarsnapArgs, "-X", cfgExcludeFile)
	}

	// GetInt() returns an int64. Convert this to an int.
	tmpKeepWeeks, err := config.GetInt("KeepWeeks")
	if err != nil {
		log.Fatal("Missing config value KeepWeeks")
	}
	tmpKeepMonths, err := config.GetInt("KeepMonths")
	if err != nil {
		log.Fatal("Missing config value KeepMonths")
	}
	cfgKeepWeeks := int(tmpKeepWeeks)
	cfgKeepMonths := int(tmpKeepMonths)

	t := time.Now()
	w := t.AddDate(0, 0, -(7 * cfgKeepWeeks))
	m := t.AddDate(0, -cfgKeepMonths, 0)
	fmt.Printf("Date: %s\nExpire week: %s\nExpire month: %s\n", t.Format(iso8601), w.Format(iso8601), m.Format(iso8601))
	fmt.Println()

	if len(os.Args) < 2 {
		log.Fatal("Missing action")
	}
	action := os.Args[1]
	switch action {
	case "nightly":
		// Run nightly
		runBackup(t.Format(nightly))

		// TODO: Make w and m global?
		cfgExpireBackups, _ := config.GetBool("ExpireBackups")
		if cfgExpireBackups {
			expireBackups(w, m, true)
		} else {
			log.Println("Backup expiration disabled")
		}
	case "adhoc":
		// Run adhoc
		runBackup(t.Format(adhoc))
	case "list-expired":
		expireBackups(w, m, false)
	default:
		log.Fatalf("Unknown action '%s'", action)
	}

	log.Println("All done!")
}
<commit_msg>Don't expire adhoc backups.<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"github.com/kylelemons/go-gypsy/yaml"
	"log"
	"os"
	"os/exec"
	"sort"
	"strings"
	"time"
)

// Default configuration file
var conffile = flag.String("c", ".tsmrc", "config file")

// Config variables. Provide a default for tarsnap(1) path.
var cfgTarsnapBin string = "/usr/local/bin/tarsnap"
var cfgTarsnapArgs []string
var cfgBackupDirs []string
var cfgExcludeFile string

// Templates for time.Parse()
const iso8601 = "2006-01-02"
const nightly = "nightly-2006-01-02"
const adhoc = "adhoc-2006-01-02_1504"

const day = time.Hour * 24

// Shamefully "borrowed" from src/cmd/go/main.go
// Flattens a mix of strings and slices of strings into a single slice.
func commandArgs(args ...interface{}) []string {
	var x []string
	for _, arg := range args {
		switch arg := arg.(type) {
		case []string:
			x = append(x, arg...)
		case string:
			x = append(x, arg)
		default:
			panic("commandArgs: invalid argument")
		}
	}
	return x
}

// Creates a new Tarsnap archive
func runBackup(archiveName string) {
	log.Printf("Starting backup %s\n", archiveName)
	args := commandArgs("-c", "-f", archiveName, cfgTarsnapArgs, cfgBackupDirs)
	backup := exec.Command(cfgTarsnapBin, args...)
	var stderr bytes.Buffer
	backup.Stderr = &stderr
	backuperr := backup.Run()
	if backuperr != nil {
		log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime)
		log.Println(stderr.String())
		log.Fatal(backuperr)
	}
	log.Println("Backup finished")
}

// Deletes a Tarsnap archive
func deleteBackup(backup string) {
	deletecmd := exec.Command(cfgTarsnapBin, "-d", "-f", backup)
	err := deletecmd.Run()
	if err != nil {
		log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime)
		log.Fatal(err)
	}
}

// Runs expiry against backup archives
func expireBackups(w, m time.Time, reallyExpire bool) {
	listcmd := exec.Command(cfgTarsnapBin, "--list-archives")
	var out bytes.Buffer
	listcmd.Stdout = &out
	listerr := listcmd.Run()
	if listerr != nil {
		log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime)
		log.Fatal(listerr)
	}
	backups := strings.Split(strings.TrimSuffix(out.String(), "\n"), "\n")
	sort.Strings(backups)

	for i := 0; i < len(backups); i++ {
		// Don't expire adhoc backups
		if strings.HasPrefix(backups[i], "adhoc-") {
			continue
		}
		backup, _ := time.Parse(nightly, backups[i])
		eom := time.Date(backup.Year(), backup.Month()+1, 0, 0, 0, 0, 0, backup.Location())
		if (backup.Before(w) && backup.Day() != eom.Day()) || backup.Before(m) {
			if reallyExpire {
				log.Println("Expiring backup", backups[i])
				deleteBackup(backups[i])
			} else {
				log.Println("Expired backup", backups[i])
			}
		} else {
			if reallyExpire {
				log.Println("Keeping backup", backups[i])
			} else {
				log.Println("Current backup", backups[i])
			}
		}
	}
}

func main() {
	flag.Parse()

	config, conferr := yaml.ReadFile(*conffile)
	if conferr != nil {
		log.Fatalf("Read config %q: %s", *conffile, conferr)
	}

	tmpTarsnapBin, _ := config.Get("TarsnapBin")
	if tmpTarsnapBin != "" {
		cfgTarsnapBin = tmpTarsnapBin
	}

	count, err := config.Count("TarsnapArgs")
	for i := 0; i < count; i++ {
		s := fmt.Sprintf("TarsnapArgs[%d]", i)
		t, err := config.Get(s)
		if err != nil {
			log.Fatal(err)
		}
		// Remove any quotes from the arg - used to protect
		// options (starting with a -)
		t = strings.Replace(t, `"`, ``, -1)
		cfgTarsnapArgs = append(cfgTarsnapArgs, t)
	}

	count, err = config.Count("BackupDirs")
	if err != nil {
		log.Fatal("No backup directories specified")
	}
	for i := 0; i < count; i++ {
		s := fmt.Sprintf("BackupDirs[%d]", i)
		t, err := config.Get(s)
		if err != nil {
			log.Fatal(err)
		}
		cfgBackupDirs = append(cfgBackupDirs, t)
	}

	cfgExcludeFile, err := config.Get("ExcludeFile")
	if err == nil {
		cfgTarsnapArgs = append(cfgTarsnapArgs, "-X", cfgExcludeFile)
	}

	// GetInt() returns an int64. Convert this to an int.
	tmpKeepWeeks, err := config.GetInt("KeepWeeks")
	if err != nil {
		log.Fatal("Missing config value KeepWeeks")
	}
	tmpKeepMonths, err := config.GetInt("KeepMonths")
	if err != nil {
		log.Fatal("Missing config value KeepMonths")
	}
	cfgKeepWeeks := int(tmpKeepWeeks)
	cfgKeepMonths := int(tmpKeepMonths)

	t := time.Now()
	w := t.AddDate(0, 0, -(7 * cfgKeepWeeks))
	m := t.AddDate(0, -cfgKeepMonths, 0)
	fmt.Printf("Date: %s\nExpire week: %s\nExpire month: %s\n", t.Format(iso8601), w.Format(iso8601), m.Format(iso8601))
	fmt.Println()

	if len(os.Args) < 2 {
		log.Fatal("Missing action")
	}
	action := os.Args[1]
	switch action {
	case "nightly":
		// Run nightly
		runBackup(t.Format(nightly))

		// TODO: Make w and m global?
		cfgExpireBackups, _ := config.GetBool("ExpireBackups")
		if cfgExpireBackups {
			expireBackups(w, m, true)
		} else {
			log.Println("Backup expiration disabled")
		}
	case "adhoc":
		// Run adhoc
		runBackup(t.Format(adhoc))
	case "list-expired":
		expireBackups(w, m, false)
	default:
		log.Fatalf("Unknown action '%s'", action)
	}

	log.Println("All done!")
}
<|endoftext|>


<commit_before>package storage

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

// BlockListType is used to filter out types of blocks in a Get Blocks List call
// for a block blob.
//
// See https://msdn.microsoft.com/en-us/library/azure/dd179400.aspx for all
// block types.
type BlockListType string

// Filters for listing blocks in block blobs
const (
	BlockListTypeAll         BlockListType = "all"
	BlockListTypeCommitted   BlockListType = "committed"
	BlockListTypeUncommitted BlockListType = "uncommitted"
)

// Maximum sizes (per REST API) for various concepts
const (
	MaxBlobBlockSize = 100 * 1024 * 1024
	MaxBlobPageSize  = 4 * 1024 * 1024
)

// BlockStatus defines states a block for a block blob can
// be in.
type BlockStatus string

// List of statuses that can be used to refer to a block in a block list
const (
	BlockStatusUncommitted BlockStatus = "Uncommitted"
	BlockStatusCommitted   BlockStatus = "Committed"
	BlockStatusLatest      BlockStatus = "Latest"
)

// Block is used to create Block entities for Put Block List
// call.
type Block struct {
	ID     string
	Status BlockStatus
}

// BlockListResponse contains the response fields from Get Block List call.
//
// See https://msdn.microsoft.com/en-us/library/azure/dd179400.aspx
type BlockListResponse struct {
	XMLName           xml.Name        `xml:"BlockList"`
	CommittedBlocks   []BlockResponse `xml:"CommittedBlocks>Block"`
	UncommittedBlocks []BlockResponse `xml:"UncommittedBlocks>Block"`
}

// BlockResponse contains the block information returned
// in the GetBlockListCall.
type BlockResponse struct {
	Name string `xml:"Name"`
	Size int64  `xml:"Size"`
}

// CreateBlockBlob initializes an empty block blob with no blocks.
//
// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Blob
func (b *Blob) CreateBlockBlob(options *PutBlobOptions) error {
	return b.CreateBlockBlobFromReader(nil, options)
}

// CreateBlockBlobFromReader initializes a block blob using data from
// reader. Size must be the number of bytes read from reader. To
// create an empty blob, use size==0 and reader==nil.
//
// The API rejects requests with size > 256 MiB (but this limit is not
// checked by the SDK). To write a larger blob, use CreateBlockBlob,
// PutBlock, and PutBlockList.
//
// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Blob
func (b *Blob) CreateBlockBlobFromReader(blob io.Reader, options *PutBlobOptions) error {
	params := url.Values{}
	headers := b.Container.bsc.client.getStandardHeaders()
	headers["x-ms-blob-type"] = string(BlobTypeBlock)

	headers["Content-Length"] = "0"
	var n int64
	var err error
	if blob != nil {
		buf := &bytes.Buffer{}
		n, err = io.Copy(buf, blob)
		if err != nil {
			return err
		}
		blob = buf
		headers["Content-Length"] = strconv.FormatInt(n, 10)
	}
	b.Properties.ContentLength = n

	headers = mergeHeaders(headers, headersFromStruct(b.Properties))
	headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata)

	if options != nil {
		params = addTimeout(params, options.Timeout)
		headers = mergeHeaders(headers, headersFromStruct(*options))
	}
	uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)

	resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, blob, b.Container.bsc.auth)
	if err != nil {
		return err
	}
	readAndCloseBody(resp.body)
	return checkRespCode(resp.statusCode, []int{http.StatusCreated})
}

// PutBlockOptions includes the options for a put block operation
type PutBlockOptions struct {
	Timeout    uint
	LeaseID    string `header:"x-ms-lease-id"`
	ContentMD5 string `header:"Content-MD5"`
	RequestID  string `header:"x-ms-client-request-id"`
}

// PutBlock saves the given data chunk to the specified block blob with
// given ID.
//
// The API rejects chunks larger than 100 MiB (but this limit is not
// checked by the SDK).
//
// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Block
func (b *Blob) PutBlock(blockID string, chunk []byte, options *PutBlockOptions) error {
	return b.PutBlockWithLength(blockID, uint64(len(chunk)), bytes.NewReader(chunk), options)
}

// PutBlockWithLength saves the given data stream of exactly specified size to
// the block blob with given ID. It is an alternative to PutBlocks where data
// comes as stream but the length is known in advance.
//
// The API rejects requests with size > 100 MiB (but this limit is not
// checked by the SDK).
//
// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Block
func (b *Blob) PutBlockWithLength(blockID string, size uint64, blob io.Reader, options *PutBlockOptions) error {
	query := url.Values{
		"comp":    {"block"},
		"blockid": {blockID},
	}
	headers := b.Container.bsc.client.getStandardHeaders()
	headers["Content-Length"] = fmt.Sprintf("%v", size)

	if options != nil {
		query = addTimeout(query, options.Timeout)
		headers = mergeHeaders(headers, headersFromStruct(*options))
	}
	uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), query)

	resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, blob, b.Container.bsc.auth)
	if err != nil {
		return err
	}
	readAndCloseBody(resp.body)
	return checkRespCode(resp.statusCode, []int{http.StatusCreated})
}

// PutBlockListOptions includes the options for a put block list operation
type PutBlockListOptions struct {
	Timeout           uint
	LeaseID           string     `header:"x-ms-lease-id"`
	IfModifiedSince   *time.Time `header:"If-Modified-Since"`
	IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
	IfMatch           string     `header:"If-Match"`
	IfNoneMatch       string     `header:"If-None-Match"`
	RequestID         string     `header:"x-ms-client-request-id"`
}

// PutBlockList saves list of blocks to the specified block blob.
//
// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Block-List
func (b *Blob) PutBlockList(blocks []Block, options *PutBlockListOptions) error {
	params := url.Values{"comp": {"blocklist"}}
	blockListXML := prepareBlockListRequest(blocks)
	headers := b.Container.bsc.client.getStandardHeaders()
	headers["Content-Length"] = fmt.Sprintf("%v", len(blockListXML))
	headers = mergeHeaders(headers, headersFromStruct(b.Properties))
	headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata)

	if options != nil {
		params = addTimeout(params, options.Timeout)
		headers = mergeHeaders(headers, headersFromStruct(*options))
	}
	uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)

	resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, strings.NewReader(blockListXML), b.Container.bsc.auth)
	if err != nil {
		return err
	}
	readAndCloseBody(resp.body)
	return checkRespCode(resp.statusCode, []int{http.StatusCreated})
}

// GetBlockListOptions includes the options for a get block list operation
type GetBlockListOptions struct {
	Timeout   uint
	Snapshot  *time.Time
	LeaseID   string `header:"x-ms-lease-id"`
	RequestID string `header:"x-ms-client-request-id"`
}

// GetBlockList retrieves list of blocks in the specified block blob.
//
// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Get-Block-List
func (b *Blob) GetBlockList(blockType BlockListType, options *GetBlockListOptions) (BlockListResponse, error) {
	params := url.Values{
		"comp":          {"blocklist"},
		"blocklisttype": {string(blockType)},
	}
	headers := b.Container.bsc.client.getStandardHeaders()

	if options != nil {
		params = addTimeout(params, options.Timeout)
		params = addSnapshot(params, options.Snapshot)
		headers = mergeHeaders(headers, headersFromStruct(*options))
	}
	uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)

	var out BlockListResponse
	resp, err := b.Container.bsc.client.exec(http.MethodGet, uri, headers, nil, b.Container.bsc.auth)
	if err != nil {
		return out, err
	}
	defer resp.body.Close()

	err = xmlUnmarshal(resp.body, &out)
	return out, err
}
<commit_msg>storage: try to avoid unnecessary buffering (#651)<commit_after>package storage

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

// BlockListType is used to filter out types of blocks in a Get Blocks List call
// for a block blob.
//
// See https://msdn.microsoft.com/en-us/library/azure/dd179400.aspx for all
// block types.
type BlockListType string

// Filters for listing blocks in block blobs
const (
	BlockListTypeAll         BlockListType = "all"
	BlockListTypeCommitted   BlockListType = "committed"
	BlockListTypeUncommitted BlockListType = "uncommitted"
)

// Maximum sizes (per REST API) for various concepts
const (
	MaxBlobBlockSize = 100 * 1024 * 1024
	MaxBlobPageSize  = 4 * 1024 * 1024
)

// BlockStatus defines states a block for a block blob can
// be in.
type BlockStatus string

// List of statuses that can be used to refer to a block in a block list
const (
	BlockStatusUncommitted BlockStatus = "Uncommitted"
	BlockStatusCommitted   BlockStatus = "Committed"
	BlockStatusLatest      BlockStatus = "Latest"
)

// Block is used to create Block entities for Put Block List
// call.
type Block struct {
	ID     string
	Status BlockStatus
}

// BlockListResponse contains the response fields from Get Block List call.
//
// See https://msdn.microsoft.com/en-us/library/azure/dd179400.aspx
type BlockListResponse struct {
	XMLName           xml.Name        `xml:"BlockList"`
	CommittedBlocks   []BlockResponse `xml:"CommittedBlocks>Block"`
	UncommittedBlocks []BlockResponse `xml:"UncommittedBlocks>Block"`
}

// BlockResponse contains the block information returned
// in the GetBlockListCall.
type BlockResponse struct {
	Name string `xml:"Name"`
	Size int64  `xml:"Size"`
}

// CreateBlockBlob initializes an empty block blob with no blocks.
//
// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Blob
func (b *Blob) CreateBlockBlob(options *PutBlobOptions) error {
	return b.CreateBlockBlobFromReader(nil, options)
}

// CreateBlockBlobFromReader initializes a block blob using data from
// reader. Size must be the number of bytes read from reader. To
// create an empty blob, use size==0 and reader==nil.
//
// The API rejects requests with size > 256 MiB (but this limit is not
// checked by the SDK). To write a larger blob, use CreateBlockBlob,
// PutBlock, and PutBlockList.
//
// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Blob
func (b *Blob) CreateBlockBlobFromReader(blob io.Reader, options *PutBlobOptions) error {
	params := url.Values{}
	headers := b.Container.bsc.client.getStandardHeaders()
	headers["x-ms-blob-type"] = string(BlobTypeBlock)

	headers["Content-Length"] = "0"
	var n int64
	var err error
	if blob != nil {
		type lener interface {
			Len() int
		}
		// TODO(rjeczalik): handle io.ReadSeeker, in case blob is *os.File etc.
		if l, ok := blob.(lener); ok {
			n = int64(l.Len())
		} else {
			var buf bytes.Buffer
			n, err = io.Copy(&buf, blob)
			if err != nil {
				return err
			}
			blob = &buf
		}

		headers["Content-Length"] = strconv.FormatInt(n, 10)
	}
	b.Properties.ContentLength = n

	headers = mergeHeaders(headers, headersFromStruct(b.Properties))
	headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata)

	if options != nil {
		params = addTimeout(params, options.Timeout)
		headers = mergeHeaders(headers, headersFromStruct(*options))
	}
	uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)

	resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, blob, b.Container.bsc.auth)
	if err != nil {
		return err
	}
	readAndCloseBody(resp.body)
	return checkRespCode(resp.statusCode, []int{http.StatusCreated})
}

// PutBlockOptions includes the options for a put block operation
type PutBlockOptions struct {
	Timeout    uint
	LeaseID    string `header:"x-ms-lease-id"`
	ContentMD5 string `header:"Content-MD5"`
	RequestID  string `header:"x-ms-client-request-id"`
}

// PutBlock saves the given data chunk to the specified block blob with
// given ID.
//
// The API rejects chunks larger than 100 MiB (but this limit is not
// checked by the SDK).
//
// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Block
func (b *Blob) PutBlock(blockID string, chunk []byte, options *PutBlockOptions) error {
	return b.PutBlockWithLength(blockID, uint64(len(chunk)), bytes.NewReader(chunk), options)
}

// PutBlockWithLength saves the given data stream of exactly specified size to
// the block blob with given ID. It is an alternative to PutBlocks where data
// comes as stream but the length is known in advance.
//
// The API rejects requests with size > 100 MiB (but this limit is not
// checked by the SDK).
//
// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Block
func (b *Blob) PutBlockWithLength(blockID string, size uint64, blob io.Reader, options *PutBlockOptions) error {
	query := url.Values{
		"comp":    {"block"},
		"blockid": {blockID},
	}
	headers := b.Container.bsc.client.getStandardHeaders()
	headers["Content-Length"] = fmt.Sprintf("%v", size)

	if options != nil {
		query = addTimeout(query, options.Timeout)
		headers = mergeHeaders(headers, headersFromStruct(*options))
	}
	uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), query)

	resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, blob, b.Container.bsc.auth)
	if err != nil {
		return err
	}
	readAndCloseBody(resp.body)
	return checkRespCode(resp.statusCode, []int{http.StatusCreated})
}

// PutBlockListOptions includes the options for a put block list operation
type PutBlockListOptions struct {
	Timeout           uint
	LeaseID           string     `header:"x-ms-lease-id"`
	IfModifiedSince   *time.Time `header:"If-Modified-Since"`
	IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
	IfMatch           string     `header:"If-Match"`
	IfNoneMatch       string     `header:"If-None-Match"`
	RequestID         string     `header:"x-ms-client-request-id"`
}

// PutBlockList saves list of blocks to the specified block blob.
//
// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Block-List
func (b *Blob) PutBlockList(blocks []Block, options *PutBlockListOptions) error {
	params := url.Values{"comp": {"blocklist"}}
	blockListXML := prepareBlockListRequest(blocks)
	headers := b.Container.bsc.client.getStandardHeaders()
	headers["Content-Length"] = fmt.Sprintf("%v", len(blockListXML))
	headers = mergeHeaders(headers, headersFromStruct(b.Properties))
	headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata)

	if options != nil {
		params = addTimeout(params, options.Timeout)
		headers = mergeHeaders(headers, headersFromStruct(*options))
	}
	uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)

	resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, strings.NewReader(blockListXML), b.Container.bsc.auth)
	if err != nil {
		return err
	}
	readAndCloseBody(resp.body)
	return checkRespCode(resp.statusCode, []int{http.StatusCreated})
}

// GetBlockListOptions includes the options for a get block list operation
type GetBlockListOptions struct {
	Timeout   uint
	Snapshot  *time.Time
	LeaseID   string `header:"x-ms-lease-id"`
	RequestID string `header:"x-ms-client-request-id"`
}

// GetBlockList retrieves list of blocks in the specified block blob.
//
// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Get-Block-List
func (b *Blob) GetBlockList(blockType BlockListType, options *GetBlockListOptions) (BlockListResponse, error) {
	params := url.Values{
		"comp":          {"blocklist"},
		"blocklisttype": {string(blockType)},
	}
	headers := b.Container.bsc.client.getStandardHeaders()

	if options != nil {
		params = addTimeout(params, options.Timeout)
		params = addSnapshot(params, options.Snapshot)
		headers = mergeHeaders(headers, headersFromStruct(*options))
	}
	uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)

	var out BlockListResponse
	resp, err := b.Container.bsc.client.exec(http.MethodGet, uri, headers, nil, b.Container.bsc.auth)
	if err != nil {
		return out, err
	}
	defer resp.body.Close()

	err = xmlUnmarshal(resp.body, &out)
	return out, err
}
<|endoftext|>


<commit_before>package storage

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/anacrolix/missinggo"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/anacrolix/torrent/metainfo"
)

func TestShortFile(t *testing.T) {
	td, err := ioutil.TempDir("", "")
	require.NoError(t, err)
	defer os.RemoveAll(td)
	s := NewFile(td)
	info := &metainfo.InfoEx{
		Info: metainfo.Info{
			Name:        "a",
			Length:      2,
			PieceLength: missinggo.MiB,
		},
	}
	ts, err := s.OpenTorrent(info)
	assert.NoError(t, err)
	f, err := os.Create(filepath.Join(td, "a"))
	err = f.Truncate(1)
	f.Close()
	var buf bytes.Buffer
	p := info.Piece(0)
	n, err := io.Copy(&buf, io.NewSectionReader(ts.Piece(p), 0, p.Length()))
	assert.EqualValues(t, 1, n)
	assert.Equal(t, io.ErrUnexpectedEOF, err)
}
<commit_msg>Add a test for issue #95<commit_after>package storage

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/anacrolix/missinggo"
	"github.com/anacrolix/missinggo/resource"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/anacrolix/torrent/metainfo"
)

func TestShortFile(t *testing.T) {
	td, err := ioutil.TempDir("", "")
	require.NoError(t, err)
	defer os.RemoveAll(td)
	s := NewFile(td)
	info := &metainfo.InfoEx{
		Info: metainfo.Info{
			Name:        "a",
			Length:      2,
			PieceLength: missinggo.MiB,
		},
	}
	ts, err := s.OpenTorrent(info)
	assert.NoError(t, err)
	f, err := os.Create(filepath.Join(td, "a"))
	err = f.Truncate(1)
	f.Close()
	var buf bytes.Buffer
	p := info.Piece(0)
	n, err := io.Copy(&buf, io.NewSectionReader(ts.Piece(p), 0, p.Length()))
	assert.EqualValues(t, 1, n)
	assert.Equal(t, io.ErrUnexpectedEOF, err)
}

// Two different torrents opened from the same storage. Closing one should not
// break the piece completion on the other.
func testIssue95(t *testing.T, c Client) {
	i1 := &metainfo.InfoEx{
		Bytes: []byte("a"),
		Info: metainfo.Info{
			Files:  []metainfo.FileInfo{{Path: []string{"a"}}},
			Pieces: make([]byte, 20),
		},
	}
	t1, err := c.OpenTorrent(i1)
	require.NoError(t, err)
	i2 := &metainfo.InfoEx{
		Bytes: []byte("b"),
		Info: metainfo.Info{
			Files:  []metainfo.FileInfo{{Path: []string{"a"}}},
			Pieces: make([]byte, 20),
		},
	}
	t2, err := c.OpenTorrent(i2)
	require.NoError(t, err)
	t2p := t2.Piece(i2.Piece(0))
	assert.NoError(t, t1.Close())
	assert.NotPanics(t, func() { t2p.GetIsComplete() })
}

func TestIssue95File(t *testing.T) {
	td, err := ioutil.TempDir("", "")
	require.NoError(t, err)
	defer os.RemoveAll(td)
	testIssue95(t, NewFile(td))
}

func TestIssue95MMap(t *testing.T) {
	td, err := ioutil.TempDir("", "")
	require.NoError(t, err)
	defer os.RemoveAll(td)
	testIssue95(t, NewMMap(td))
}

func TestIssue95ResourcePieces(t *testing.T) {
	td, err := ioutil.TempDir("", "")
	require.NoError(t, err)
	defer os.RemoveAll(td)
	testIssue95(t, NewResourcePieces(resource.OSFileProvider{}))
}
<|endoftext|>


<commit_before>package torrent

import (
	"context"
	"net"
	"time"
)

// Abstracts the utp Socket, so the implementation can be selected from
// different packages.
type utpSocket interface {
	Accept() (net.Conn, error)
	Addr() net.Addr
	Close() error
	LocalAddr() net.Addr
	ReadFrom([]byte) (int, net.Addr, error)
	SetDeadline(time.Time) error
	SetWriteDeadline(time.Time) error
	SetReadDeadline(time.Time) error
	WriteTo([]byte, net.Addr) (int, error)
	DialContext(ctx context.Context, addr string) (net.Conn, error)
	Dial(addr string) (net.Conn, error)
}
<commit_msg>Simplify the utpSocket interface definition<commit_after>package torrent

import (
	"context"
	"net"
)

// Abstracts the utp Socket, so the implementation can be selected from
// different packages.
type utpSocket interface {
	net.PacketConn
	// net.Listener, but we can't have duplicate Close.
	Accept() (net.Conn, error)
	Addr() net.Addr
	// net.Dialer but there's no interface.
	DialContext(ctx context.Context, addr string) (net.Conn, error)
	Dial(addr string) (net.Conn, error)
}
<|endoftext|>


<commit_before>package vox

// #cgo LDFLAGS: -lstdc++ -ldl -lm
// #define SUNVOX_MAIN
// #include <stdio.h>
// #include <stdlib.h>
// #include <dlfcn.h>
// #include "vox.h"
import "C"

import (
    "errors"
    "fmt"
    "unsafe"
    "runtime"
)

func init() {
    runtime.LockOSThread()
}

const (
    // Init flags
    NO_DEBUG_OUTPUT = C.SV_INIT_FLAG_NO_DEBUG_OUTPUT
    USER_AUDIO_CALLBACK = C.SV_INIT_FLAG_USER_AUDIO_CALLBACK
    AUDIO_INT16 = C.SV_INIT_FLAG_AUDIO_INT16
    AUDIO_FLOAT32 = C.SV_INIT_FLAG_AUDIO_FLOAT32
    ONE_THREAD = C.SV_INIT_FLAG_ONE_THREAD

    // Note constants
    NOTE_OFF = C.NOTECMD_NOTE_OFF
    ALL_NOTES_OFF = C.NOTECMD_ALL_NOTES_OFF
    CLEAN_SYNTHS = C.NOTECMD_CLEAN_SYNTHS
    STOP = C.NOTECMD_STOP
    PLAY = C.NOTECMD_PLAY

    // Module flags
    FLAG_EXISTS = C.SV_MODULE_FLAG_EXISTS
    FLAG_EFFECT = C.SV_MODULE_FLAG_EFFECT
    INPUTS_OFF = C.SV_MODULE_INPUTS_OFF
    INPUTS_MASK = C.SV_MODULE_INPUTS_MASK
    OUTPUTS_OFF = C.SV_MODULE_OUTPUTS_OFF
    OUTPUTS_MASK = C.SV_MODULE_OUTPUTS_MASK

    // Type flags
    INT16 = C.SV_STYPE_INT16
    INT32 = C.SV_STYPE_INT32
    FLOAT32 = C.SV_STYPE_FLOAT32
    FLOAT64 = C.SV_STYPE_FLOAT64
)

var (
    Version string
    slots = 0
)

// Init loads the sunvox dll and initializes the library.
func Init(dev string, freq, channels, flags int) error {
    if C.sv_load_dll() != C.int(0) {
        return errors.New("Could not load sunvox library")
    }

    device := C.CString(dev)
    defer C.free(unsafe.Pointer(device))

    ver := int(C.vox_init(device, C.int(freq), C.int(channels), C.int(flags)))
    if ver < 0 {
        return errors.New("Could not initialize sunvox library")
    }
    Version = fmt.Sprintf("%d.%d.%d", (ver>>16)&255, (ver>>8)&255, ver&255)

    return nil
}

// Quit deinitializes the library and unloads the sunvox dll.
func Quit() error {
    if C.vox_deinit() != C.int(0) {
        return errors.New("Problem uninitializing sunvox library")
    }

    if C.sv_load_dll() != C.int(0) {
        return errors.New("Problem unloading sunvox library")
    }

    return nil
}

// SampleType returns the internal sample type of the sunvox engine.
func SampleType() int {
    return int(C.vox_get_sample_type())
}

// Ticks returns the current tick counter (from 0 to 0xFFFFFFFF).
func Ticks() uint {
    return uint(C.vox_get_ticks())
}

// TicksPerSecond returns the number of sunvox ticks per second.
func TicksPerSecond() uint {
    return uint(C.vox_get_ticks_per_second())
}

// Song is used to load and play sunvox songs.
type Song struct {
    slot C.int
}

// Open creates a new slot and laods a sunvox song into it.
func Open(path string) (*Song, error) {
    slot := slots
    if C.vox_open_slot(C.int(slot)) != C.int(0) {
        return nil, errors.New("Could not open new slot")
    }

    name := C.CString(path)
    defer C.free(unsafe.Pointer(name))
    if C.vox_load(C.int(slot), name) != C.int(0) {
        return nil, errors.New(fmt.Sprintf("Could not open song %s", path))
    }

    slots++
    return &Song{C.int(slot)}, nil
}

// Close closes the slot. The slot should no longer be used after calling it.
func (s *Song) Close() error {
    if C.vox_close_slot(s.slot) != C.int(0) {
        return errors.New(fmt.Sprintf("Problem closing slot %v", s))
    }
    return nil
}

// SetVolume sets the volume of the slot.
func (s *Song) SetVolume(vol int) error {
    if C.vox_volume(s.slot, C.int(vol)) != C.int(0) {
        return errors.New(fmt.Sprintf("Could not change slot %v's volume to %v", s, vol))
    }
    return nil
}

// Play starts playback from where ever the song was stopped.
func (s *Song) Play() error {
    if C.vox_play(s.slot) != C.int(0) {
        return errors.New(fmt.Sprintf("Could not play slot %v", s))
    }
    return nil
}

// Replay starts playback from the beginning.
func (s *Song) Replay() {
    C.vox_play_from_beginning(s.slot)
}

// Stop stops playback on the slot.
func (s *Song) Stop() {
    C.vox_stop(s.slot);
}

// Finished indicates if the song has gotten to the end.
func (s *Song) Finished() bool {
    ended := C.vox_end_of_song(s.slot)
    if ended == 0 {
        return false
    }
    return true;
}

// Line returns the current line in the song.
func (s *Song) Line() int {
    return int(C.vox_get_current_line(s.slot))
}

// SetLooping enables or disables looping.
func (s *Song) SetLooping(loop bool) {
    if loop {
        C.vox_set_autostop(s.slot, C.int(0));
    } else {
        C.vox_set_autostop(s.slot, C.int(1));
    }
}

// Rewind the song by t lines.
func (s *Song) Rewind(t int) {
    C.vox_rewind(s.slot, C.int(t))
}

// Name retuns the name of the song.
func (s *Song) Name() string {
    return C.GoString(C.vox_get_song_name(s.slot))
}

// Event plays a note on a channel in a module.
func (s *Song) Event(channel, note, vel, module, ctl, val int) {
    C.vox_send_event(s.slot, C.int(channel), C.int(note), C.int(vel), C.int(module), C.int(ctl), C.int(val))
}

// Level returns the current signal level of a channel.
func (s *Song) Level(channel int) int {
    return int(C.vox_get_current_signal_level(s.slot, C.int(channel)))
}

// BeatsPerMinute returns the songs beats per minute.
func (s *Song) BeatsPerMinute() int {
    return int(C.vox_get_song_bpm(s.slot))
}

// TicksPerLine returns the number of ticks per line.
func (s *Song) TicksPerLine() int {
    return int(C.vox_get_song_tpl(s.slot))
}

// Frames gives the length of the song in frames.
func (s *Song) Frames() int {
    return int(C.vox_get_song_length_frames(s.slot))
}

// Lines gives the length of the song in lines.
func (s *Song) Lines() int {
    return int(C.vox_get_song_length_lines(s.slot))
}
<commit_msg>Various minor fixes<commit_after>package vox

// #cgo LDFLAGS: -lstdc++ -ldl -lm
// #define SUNVOX_MAIN
// #include <stdio.h>
// #include <stdlib.h>
// #include <dlfcn.h>
// #include "vox.h"
import "C"

import (
    "errors"
    "fmt"
    "unsafe"
    "runtime"
)

func init() {
    runtime.LockOSThread()
}

const (
    // Init flags
    NO_DEBUG_OUTPUT = C.SV_INIT_FLAG_NO_DEBUG_OUTPUT
    USER_AUDIO_CALLBACK = C.SV_INIT_FLAG_USER_AUDIO_CALLBACK
    AUDIO_INT16 = C.SV_INIT_FLAG_AUDIO_INT16
    AUDIO_FLOAT32 = C.SV_INIT_FLAG_AUDIO_FLOAT32
    ONE_THREAD = C.SV_INIT_FLAG_ONE_THREAD

    // Note constants
    NOTE_OFF = C.NOTECMD_NOTE_OFF
    ALL_NOTES_OFF = C.NOTECMD_ALL_NOTES_OFF
    CLEAN_SYNTHS = C.NOTECMD_CLEAN_SYNTHS
    STOP = C.NOTECMD_STOP
    PLAY = C.NOTECMD_PLAY

    // Module flags
    FLAG_EXISTS = C.SV_MODULE_FLAG_EXISTS
    FLAG_EFFECT = C.SV_MODULE_FLAG_EFFECT
    INPUTS_OFF = C.SV_MODULE_INPUTS_OFF
    INPUTS_MASK = C.SV_MODULE_INPUTS_MASK
    OUTPUTS_OFF = C.SV_MODULE_OUTPUTS_OFF
    OUTPUTS_MASK = C.SV_MODULE_OUTPUTS_MASK

    // Type flags
    INT16 = C.SV_STYPE_INT16
    INT32 = C.SV_STYPE_INT32
    FLOAT32 = C.SV_STYPE_FLOAT32
    FLOAT64 = C.SV_STYPE_FLOAT64
)

var (
    Version string
    slots = 0
)

// Init loads the sunvox dll and initializes the library.
func Init(dev string, freq, channels, flags int) error {
    if C.sv_load_dll() != C.int(0) {
        return errors.New("Could not load sunvox library")
    }

    device := C.CString(dev)
    defer C.free(unsafe.Pointer(device))

    ver := int(C.vox_init(device, C.int(freq), C.int(channels), C.int(flags)))
    if ver < 0 {
        return errors.New("Could not initialize sunvox library")
    }
    Version = fmt.Sprintf("%d.%d.%d", (ver>>16)&255, (ver>>8)&255, ver&255)

    return nil
}

// Quit deinitializes the library and unloads the sunvox dll.
func Quit() error {
    if C.vox_deinit() != C.int(0) {
        return errors.New("Problem uninitializing sunvox library")
    }

    if C.sv_load_dll() != C.int(0) {
        return errors.New("Problem unloading sunvox library")
    }

    return nil
}

// SampleType returns the internal sample type of the sunvox engine.
func SampleType() int {
    return int(C.vox_get_sample_type())
}

// Ticks returns the current tick counter (from 0 to 0xFFFFFFFF).
func Ticks() uint {
    return uint(C.vox_get_ticks())
}

// TicksPerSecond returns the number of sunvox ticks per second.
func TicksPerSecond() uint {
    return uint(C.vox_get_ticks_per_second())
}

// Song is used to load and play sunvox songs.
type Song struct {
    slot C.int
    volume int
}

// Open creates a new slot and laods a sunvox song into it.
func Open(path string) (*Song, error) {
    slot := slots
    if C.vox_open_slot(C.int(slot)) != C.int(0) {
        return nil, errors.New("Could not open new slot")
    }

    name := C.CString(path)
    defer C.free(unsafe.Pointer(name))
    if C.vox_load(C.int(slot), name) != C.int(0) {
        return nil, errors.New(fmt.Sprintf("Could not open song %s", path))
    }

    slots++
    song := &Song{C.int(slot), 256}
    song.SetVolume(song.volume)
    return song, nil
}

// Close the song. The song should not be used after calling this.
func (s *Song) Close() error {
    if C.vox_close_slot(s.slot) != C.int(0) {
        return errors.New(fmt.Sprintf("Problem closing slot %v", s))
    }
    return nil
}

// Volume returns the volume of the song.
func (s *Song) Volume() int {
    return s.volume
}

// SetVolume sets the volume of the song.
func (s *Song) SetVolume(vol int) error {
    if C.vox_volume(s.slot, C.int(vol)) != C.int(0) {
        return errors.New(fmt.Sprintf("Could not change slot %v's volume to %v", s, vol))
    }
    s.volume = vol
    return nil
}

// Play starts playback from where ever the song was stopped.
func (s *Song) Play() error {
    if C.vox_play(s.slot) != C.int(0) {
        return errors.New(fmt.Sprintf("Could not play slot %v", s))
    }
    return nil
}

// Replay starts playback from the beginning.
func (s *Song) Replay() {
    C.vox_play_from_beginning(s.slot)
}

// Pause stops the song's playback at its current position.
func (s *Song) Pause() {
    C.vox_stop(s.slot);
}

// Finished indicates if the song has gotten to the end.
func (s *Song) Finished() bool {
    ended := C.vox_end_of_song(s.slot)
    if ended == 0 {
        return false
    }
    return true;
}

// Line returns the current line in the song.
func (s *Song) Line() int {
    return int(C.vox_get_current_line(s.slot))
}

// SetLooping enables or disables looping.
func (s *Song) SetLooping(loop bool) {
    if loop {
        C.vox_set_autostop(s.slot, C.int(0));
    } else {
        C.vox_set_autostop(s.slot, C.int(1));
    }
}

// Seek to a line in the song.
func (s *Song) Seek(t int) {
    C.vox_rewind(s.slot, C.int(t))
}

// Name retuns the name of the song.
func (s *Song) Name() string {
    return C.GoString(C.vox_get_song_name(s.slot))
}

// Event plays a note on a channel in a module.
func (s *Song) Event(channel, note, vel, module, ctl, val int) {
    C.vox_send_event(s.slot, C.int(channel), C.int(note), C.int(vel), C.int(module), C.int(ctl), C.int(val))
}

// Level returns the current signal level of a channel.
func (s *Song) Level(channel int) int {
    return int(C.vox_get_current_signal_level(s.slot, C.int(channel)))
}

// BeatsPerMinute returns the songs beats per minute.
func (s *Song) BeatsPerMinute() int {
    return int(C.vox_get_song_bpm(s.slot))
}

// TicksPerLine returns the number of ticks per line.
func (s *Song) TicksPerLine() int {
    return int(C.vox_get_song_tpl(s.slot))
}

// Frames gives the length of the song in frames.
func (s *Song) Frames() int {
    return int(C.vox_get_song_length_frames(s.slot))
}

// Lines gives the length of the song in lines.
func (s *Song) Lines() int {
    return int(C.vox_get_song_length_lines(s.slot))
}
<|endoftext|>