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

package models

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/Unknwon/com"

	"github.com/gogits/gogs/modules/log"
	"github.com/gogits/gogs/modules/process"
	"github.com/gogits/gogs/modules/setting"
)

const (
	// "### autogenerated by gitgos, DO NOT EDIT\n"
	_TPL_PUBLICK_KEY = `command="%s serv key-%d",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s` + "\n"
)

var (
	ErrKeyAlreadyExist = errors.New("Public key already exist")
	ErrKeyNotExist     = errors.New("Public key does not exist")
)

var sshOpLocker = sync.Mutex{}

var (
	SshPath string // SSH directory.
	appPath string // Execution(binary) path.
)

// exePath returns the executable path.
func exePath() (string, error) {
	file, err := exec.LookPath(os.Args[0])
	if err != nil {
		return "", err
	}
	return filepath.Abs(file)
}

// homeDir returns the home directory of current user.
func homeDir() string {
	home, err := com.HomeDir()
	if err != nil {
		log.Fatal(4, "Fail to get home directory: %v", err)
	}
	return home
}

func init() {
	var err error

	if appPath, err = exePath(); err != nil {
		log.Fatal(4, "fail to get app path: %v\n", err)
	}
	appPath = strings.Replace(appPath, "\\", "/", -1)

	// Determine and create .ssh path.
	SshPath = filepath.Join(homeDir(), ".ssh")
	if err = os.MkdirAll(SshPath, 0700); err != nil {
		log.Fatal(4, "fail to create SshPath(%s): %v\n", SshPath, err)
	}
}

// PublicKey represents a SSH key.
type PublicKey struct {
	Id                int64
	OwnerId           int64  `xorm:"UNIQUE(s) INDEX NOT NULL"`
	Name              string `xorm:"UNIQUE(s) NOT NULL"`
	Fingerprint       string
	Content           string    `xorm:"TEXT NOT NULL"`
	Created           time.Time `xorm:"CREATED"`
	Updated           time.Time
	HasRecentActivity bool `xorm:"-"`
	HasUsed           bool `xorm:"-"`
}

// GetAuthorizedString generates and returns formatted public key string for authorized_keys file.
func (key *PublicKey) GetAuthorizedString() string {
	return fmt.Sprintf(_TPL_PUBLICK_KEY, appPath, key.Id, key.Content)
}

var (
	MinimumKeySize = map[string]int{
		"(ED25519)": 256,
		"(ECDSA)":   256,
		"(NTRU)":    1087,
		"(MCE)":     1702,
		"(McE)":     1702,
		"(RSA)":     2048,
	}
)

// CheckPublicKeyString checks if the given public key string is recognized by SSH.
func CheckPublicKeyString(content string) (bool, error) {
	if strings.ContainsAny(content, "\n\r") {
		return false, errors.New("Only a single line with a single key please")
	}

	// write the key to a file…
	tmpFile, err := ioutil.TempFile(os.TempDir(), "keytest")
	if err != nil {
		return false, err
	}
	tmpPath := tmpFile.Name()
	defer os.Remove(tmpPath)
	tmpFile.WriteString(content)
	tmpFile.Close()

	// Check if ssh-keygen recognizes its contents.
	stdout, stderr, err := process.Exec("CheckPublicKeyString", "ssh-keygen", "-l", "-f", tmpPath)
	if err != nil {
		return false, errors.New("ssh-keygen -l -f: " + stderr)
	} else if len(stdout) < 2 {
		return false, errors.New("ssh-keygen returned not enough output to evaluate the key")
	}

	// The ssh-keygen in Windows does not print key type, so no need go further.
	if setting.IsWindows {
		return true, nil
	}

	sshKeygenOutput := strings.Split(stdout, " ")
	if len(sshKeygenOutput) < 4 {
		return false, errors.New("Not enough fields returned by ssh-keygen -l -f")
	}

	// Check if key type and key size match.
	keySize, err := com.StrTo(sshKeygenOutput[0]).Int()
	if err != nil {
		return false, errors.New("Cannot get key size of the given key")
	}
	keyType := strings.TrimSpace(sshKeygenOutput[len(sshKeygenOutput)-1])
	if minimumKeySize := MinimumKeySize[keyType]; minimumKeySize == 0 {
		return false, errors.New("Sorry, unrecognized public key type")
	} else if keySize < minimumKeySize {
		return false, fmt.Errorf("The minimum accepted size of a public key %s is %d", keyType, minimumKeySize)
	}

	return true, nil
}

// saveAuthorizedKeyFile writes SSH key content to authorized_keys file.
func saveAuthorizedKeyFile(key *PublicKey) error {
	sshOpLocker.Lock()
	defer sshOpLocker.Unlock()

	fpath := filepath.Join(SshPath, "authorized_keys")
	f, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
	if err != nil {
		return err
	}
	defer f.Close()
	finfo, err := f.Stat()
	if err != nil {
		return err
	}

	// FIXME: following command does not support in Windows.
	if !setting.IsWindows {
		if finfo.Mode().Perm() > 0600 {
			log.Error(4, "authorized_keys file has unusual permission flags: %s - setting to -rw-------", finfo.Mode().Perm().String())
			if err = f.Chmod(0600); err != nil {
				return err
			}
		}
	}

	_, err = f.WriteString(key.GetAuthorizedString())
	return err
}

// AddPublicKey adds new public key to database and authorized_keys file.
func AddPublicKey(key *PublicKey) (err error) {
	has, err := x.Get(key)
	if err != nil {
		return err
	} else if has {
		return ErrKeyAlreadyExist
	}

	// Calculate fingerprint.
	tmpPath := strings.Replace(path.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()),
		"id_rsa.pub"), "\\", "/", -1)
	os.MkdirAll(path.Dir(tmpPath), os.ModePerm)
	if err = ioutil.WriteFile(tmpPath, []byte(key.Content), os.ModePerm); err != nil {
		return err
	}
	stdout, stderr, err := process.Exec("AddPublicKey", "ssh-keygen", "-l", "-f", tmpPath)
	if err != nil {
		return errors.New("ssh-keygen -l -f: " + stderr)
	} else if len(stdout) < 2 {
		return errors.New("Not enough output for calculating fingerprint")
	}
	key.Fingerprint = strings.Split(stdout, " ")[1]

	// Save SSH key.
	if _, err = x.Insert(key); err != nil {
		return err
	} else if err = saveAuthorizedKeyFile(key); err != nil {
		// Roll back.
		if _, err2 := x.Delete(key); err2 != nil {
			return err2
		}
		return err
	}

	return nil
}

// GetPublicKeyById returns public key by given ID.
func GetPublicKeyById(keyId int64) (*PublicKey, error) {
	key := new(PublicKey)
	has, err := x.Id(keyId).Get(key)
	if err != nil {
		return nil, err
	} else if !has {
		return nil, ErrKeyNotExist
	}
	return key, nil
}

// ListPublicKey returns a list of all public keys that user has.
func ListPublicKey(uid int64) ([]*PublicKey, error) {
	keys := make([]*PublicKey, 0, 5)
	err := x.Find(&keys, &PublicKey{OwnerId: uid})
	if err != nil {
		return nil, err
	}

	for _, key := range keys {
		key.HasUsed = key.Updated.After(key.Created)
		key.HasRecentActivity = key.Updated.Add(7 * 24 * time.Hour).After(time.Now())
	}
	return keys, nil
}

// rewriteAuthorizedKeys finds and deletes corresponding line in authorized_keys file.
func rewriteAuthorizedKeys(key *PublicKey, p, tmpP string) error {
	sshOpLocker.Lock()
	defer sshOpLocker.Unlock()

	fr, err := os.Open(p)
	if err != nil {
		return err
	}
	defer fr.Close()

	fw, err := os.OpenFile(tmpP, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
	if err != nil {
		return err
	}
	defer fw.Close()

	isFound := false
	keyword := fmt.Sprintf("key-%d", key.Id)
	buf := bufio.NewReader(fr)
	for {
		line, errRead := buf.ReadString('\n')
		line = strings.TrimSpace(line)

		if errRead != nil {
			if errRead != io.EOF {
				return errRead
			}

			// Reached end of file, if nothing to read then break,
			// otherwise handle the last line.
			if len(line) == 0 {
				break
			}
		}

		// Found the line and copy rest of file.
		if !isFound && strings.Contains(line, keyword) && strings.Contains(line, key.Content) {
			isFound = true
			continue
		}
		// Still finding the line, copy the line that currently read.
		if _, err = fw.WriteString(line + "\n"); err != nil {
			return err
		}

		if errRead == io.EOF {
			break
		}
	}
	return nil
}

// UpdatePublicKey updates given public key.
func UpdatePublicKey(key *PublicKey) error {
	_, err := x.Id(key.Id).AllCols().Update(key)
	return err
}

// DeletePublicKey deletes SSH key information both in database and authorized_keys file.
func DeletePublicKey(key *PublicKey) error {
	has, err := x.Get(key)
	if err != nil {
		return err
	} else if !has {
		return ErrKeyNotExist
	}

	if _, err = x.Delete(key); err != nil {
		return err
	}

	fpath := filepath.Join(SshPath, "authorized_keys")
	tmpPath := filepath.Join(SshPath, "authorized_keys.tmp")
	if err = rewriteAuthorizedKeys(key, fpath, tmpPath); err != nil {
		return err
	} else if err = os.Remove(fpath); err != nil {
		return err
	}
	return os.Rename(tmpPath, fpath)
}
<commit_msg>support dsa key format<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/Unknwon/com"

	"github.com/gogits/gogs/modules/log"
	"github.com/gogits/gogs/modules/process"
	"github.com/gogits/gogs/modules/setting"
)

const (
	// "### autogenerated by gitgos, DO NOT EDIT\n"
	_TPL_PUBLICK_KEY = `command="%s serv key-%d",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s` + "\n"
)

var (
	ErrKeyAlreadyExist = errors.New("Public key already exist")
	ErrKeyNotExist     = errors.New("Public key does not exist")
)

var sshOpLocker = sync.Mutex{}

var (
	SshPath string // SSH directory.
	appPath string // Execution(binary) path.
)

// exePath returns the executable path.
func exePath() (string, error) {
	file, err := exec.LookPath(os.Args[0])
	if err != nil {
		return "", err
	}
	return filepath.Abs(file)
}

// homeDir returns the home directory of current user.
func homeDir() string {
	home, err := com.HomeDir()
	if err != nil {
		log.Fatal(4, "Fail to get home directory: %v", err)
	}
	return home
}

func init() {
	var err error

	if appPath, err = exePath(); err != nil {
		log.Fatal(4, "fail to get app path: %v\n", err)
	}
	appPath = strings.Replace(appPath, "\\", "/", -1)

	// Determine and create .ssh path.
	SshPath = filepath.Join(homeDir(), ".ssh")
	if err = os.MkdirAll(SshPath, 0700); err != nil {
		log.Fatal(4, "fail to create SshPath(%s): %v\n", SshPath, err)
	}
}

// PublicKey represents a SSH key.
type PublicKey struct {
	Id                int64
	OwnerId           int64  `xorm:"UNIQUE(s) INDEX NOT NULL"`
	Name              string `xorm:"UNIQUE(s) NOT NULL"`
	Fingerprint       string
	Content           string    `xorm:"TEXT NOT NULL"`
	Created           time.Time `xorm:"CREATED"`
	Updated           time.Time
	HasRecentActivity bool `xorm:"-"`
	HasUsed           bool `xorm:"-"`
}

// GetAuthorizedString generates and returns formatted public key string for authorized_keys file.
func (key *PublicKey) GetAuthorizedString() string {
	return fmt.Sprintf(_TPL_PUBLICK_KEY, appPath, key.Id, key.Content)
}

var (
	MinimumKeySize = map[string]int{
		"(ED25519)": 256,
		"(ECDSA)":   256,
		"(NTRU)":    1087,
		"(MCE)":     1702,
		"(McE)":     1702,
		"(RSA)":     2048,
		"(DSA)":     1024,
	}
)

// CheckPublicKeyString checks if the given public key string is recognized by SSH.
func CheckPublicKeyString(content string) (bool, error) {
	if strings.ContainsAny(content, "\n\r") {
		return false, errors.New("Only a single line with a single key please")
	}

	// write the key to a file…
	tmpFile, err := ioutil.TempFile(os.TempDir(), "keytest")
	if err != nil {
		return false, err
	}
	tmpPath := tmpFile.Name()
	defer os.Remove(tmpPath)
	tmpFile.WriteString(content)
	tmpFile.Close()

	// Check if ssh-keygen recognizes its contents.
	stdout, stderr, err := process.Exec("CheckPublicKeyString", "ssh-keygen", "-l", "-f", tmpPath)
	if err != nil {
		return false, errors.New("ssh-keygen -l -f: " + stderr)
	} else if len(stdout) < 2 {
		return false, errors.New("ssh-keygen returned not enough output to evaluate the key")
	}

	// The ssh-keygen in Windows does not print key type, so no need go further.
	if setting.IsWindows {
		return true, nil
	}

	sshKeygenOutput := strings.Split(stdout, " ")
	if len(sshKeygenOutput) < 4 {
		return false, errors.New("Not enough fields returned by ssh-keygen -l -f")
	}

	// Check if key type and key size match.
	keySize, err := com.StrTo(sshKeygenOutput[0]).Int()
	if err != nil {
		return false, errors.New("Cannot get key size of the given key")
	}
	keyType := strings.TrimSpace(sshKeygenOutput[len(sshKeygenOutput)-1])
	if minimumKeySize := MinimumKeySize[keyType]; minimumKeySize == 0 {
		return false, errors.New("Sorry, unrecognized public key type")
	} else if keySize < minimumKeySize {
		return false, fmt.Errorf("The minimum accepted size of a public key %s is %d", keyType, minimumKeySize)
	}

	return true, nil
}

// saveAuthorizedKeyFile writes SSH key content to authorized_keys file.
func saveAuthorizedKeyFile(key *PublicKey) error {
	sshOpLocker.Lock()
	defer sshOpLocker.Unlock()

	fpath := filepath.Join(SshPath, "authorized_keys")
	f, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
	if err != nil {
		return err
	}
	defer f.Close()
	finfo, err := f.Stat()
	if err != nil {
		return err
	}

	// FIXME: following command does not support in Windows.
	if !setting.IsWindows {
		if finfo.Mode().Perm() > 0600 {
			log.Error(4, "authorized_keys file has unusual permission flags: %s - setting to -rw-------", finfo.Mode().Perm().String())
			if err = f.Chmod(0600); err != nil {
				return err
			}
		}
	}

	_, err = f.WriteString(key.GetAuthorizedString())
	return err
}

// AddPublicKey adds new public key to database and authorized_keys file.
func AddPublicKey(key *PublicKey) (err error) {
	has, err := x.Get(key)
	if err != nil {
		return err
	} else if has {
		return ErrKeyAlreadyExist
	}

	// Calculate fingerprint.
	tmpPath := strings.Replace(path.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()),
		"id_rsa.pub"), "\\", "/", -1)
	os.MkdirAll(path.Dir(tmpPath), os.ModePerm)
	if err = ioutil.WriteFile(tmpPath, []byte(key.Content), os.ModePerm); err != nil {
		return err
	}
	stdout, stderr, err := process.Exec("AddPublicKey", "ssh-keygen", "-l", "-f", tmpPath)
	if err != nil {
		return errors.New("ssh-keygen -l -f: " + stderr)
	} else if len(stdout) < 2 {
		return errors.New("Not enough output for calculating fingerprint")
	}
	key.Fingerprint = strings.Split(stdout, " ")[1]

	// Save SSH key.
	if _, err = x.Insert(key); err != nil {
		return err
	} else if err = saveAuthorizedKeyFile(key); err != nil {
		// Roll back.
		if _, err2 := x.Delete(key); err2 != nil {
			return err2
		}
		return err
	}

	return nil
}

// GetPublicKeyById returns public key by given ID.
func GetPublicKeyById(keyId int64) (*PublicKey, error) {
	key := new(PublicKey)
	has, err := x.Id(keyId).Get(key)
	if err != nil {
		return nil, err
	} else if !has {
		return nil, ErrKeyNotExist
	}
	return key, nil
}

// ListPublicKey returns a list of all public keys that user has.
func ListPublicKey(uid int64) ([]*PublicKey, error) {
	keys := make([]*PublicKey, 0, 5)
	err := x.Find(&keys, &PublicKey{OwnerId: uid})
	if err != nil {
		return nil, err
	}

	for _, key := range keys {
		key.HasUsed = key.Updated.After(key.Created)
		key.HasRecentActivity = key.Updated.Add(7 * 24 * time.Hour).After(time.Now())
	}
	return keys, nil
}

// rewriteAuthorizedKeys finds and deletes corresponding line in authorized_keys file.
func rewriteAuthorizedKeys(key *PublicKey, p, tmpP string) error {
	sshOpLocker.Lock()
	defer sshOpLocker.Unlock()

	fr, err := os.Open(p)
	if err != nil {
		return err
	}
	defer fr.Close()

	fw, err := os.OpenFile(tmpP, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
	if err != nil {
		return err
	}
	defer fw.Close()

	isFound := false
	keyword := fmt.Sprintf("key-%d", key.Id)
	buf := bufio.NewReader(fr)
	for {
		line, errRead := buf.ReadString('\n')
		line = strings.TrimSpace(line)

		if errRead != nil {
			if errRead != io.EOF {
				return errRead
			}

			// Reached end of file, if nothing to read then break,
			// otherwise handle the last line.
			if len(line) == 0 {
				break
			}
		}

		// Found the line and copy rest of file.
		if !isFound && strings.Contains(line, keyword) && strings.Contains(line, key.Content) {
			isFound = true
			continue
		}
		// Still finding the line, copy the line that currently read.
		if _, err = fw.WriteString(line + "\n"); err != nil {
			return err
		}

		if errRead == io.EOF {
			break
		}
	}
	return nil
}

// UpdatePublicKey updates given public key.
func UpdatePublicKey(key *PublicKey) error {
	_, err := x.Id(key.Id).AllCols().Update(key)
	return err
}

// DeletePublicKey deletes SSH key information both in database and authorized_keys file.
func DeletePublicKey(key *PublicKey) error {
	has, err := x.Get(key)
	if err != nil {
		return err
	} else if !has {
		return ErrKeyNotExist
	}

	if _, err = x.Delete(key); err != nil {
		return err
	}

	fpath := filepath.Join(SshPath, "authorized_keys")
	tmpPath := filepath.Join(SshPath, "authorized_keys.tmp")
	if err = rewriteAuthorizedKeys(key, fpath, tmpPath); err != nil {
		return err
	} else if err = os.Remove(fpath); err != nil {
		return err
	}
	return os.Rename(tmpPath, fpath)
}
<|endoftext|>


<commit_before>// Copyright 2011 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 main

import (
	"fmt"
	"go/build"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
)

var cmdTool = &Command{
	Run:       runTool,
	UsageLine: "tool command [args...]",
	Short:     "run specified go tool",
	Long: `
Tool runs the go tool command identified by the arguments.
With no arguments it prints the list of known tools.

For more about each tool command, see 'go tool command -h'.
`,
}

var (
	toolGOOS      = runtime.GOOS
	toolGOARCH    = runtime.GOARCH
	toolIsWindows = toolGOOS == "windows"
	toolDir       = build.ToolDir
)

const toolWindowsExtension = ".exe"

func tool(name string) string {
	p := filepath.Join(toolDir, name)
	if toolIsWindows {
		p += toolWindowsExtension
	}
	return p
}

func runTool(cmd *Command, args []string) {
	if len(args) == 0 {
		listTools()
		return
	}
	toolName := args[0]
	// The tool name must be lower-case letters and numbers.
	for _, c := range toolName {
		switch {
		case 'a' <= c && c <= 'z', '0' <= c && c <= '9':
		default:
			fmt.Fprintf(os.Stderr, "go tool: bad tool name %q\n", toolName)
			setExitStatus(2)
			return
		}
	}
	toolPath := tool(toolName)
	// Give a nice message if there is no tool with that name.
	if _, err := os.Stat(toolPath); err != nil {
		fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName)
		setExitStatus(3)
		return
	}
	toolCmd := &exec.Cmd{
		Path:   toolPath,
		Args:   args,
		Stdin:  os.Stdin,
		Stdout: os.Stdout,
		Stderr: os.Stderr,
	}
	err := toolCmd.Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err)
		setExitStatus(1)
		return
	}
}

// listTools prints a list of the available tools in the tools directory.
func listTools() {
	f, err := os.Open(toolDir)
	if err != nil {
		fmt.Fprintf(os.Stderr, "go tool: no tool directory: %s\n", err)
		setExitStatus(2)
		return
	}
	defer f.Close()
	names, err := f.Readdirnames(-1)
	if err != nil {
		fmt.Fprintf(os.Stderr, "go tool: can't read directory: %s\n", err)
		setExitStatus(2)
		return
	}

	sort.Strings(names)
	for _, name := range names {
		// Unify presentation by going to lower case.
		name = strings.ToLower(name)
		// If it's windows, don't show the .exe suffix.
		if toolIsWindows && strings.HasSuffix(name, toolWindowsExtension) {
			name = name[:len(name)-len(toolWindowsExtension)]
		}
		fmt.Println(name)
	}
}
<commit_msg>cmd/go: add tool -n flag<commit_after>// Copyright 2011 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 main

import (
	"fmt"
	"go/build"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
)

var cmdTool = &Command{
	Run:       runTool,
	UsageLine: "tool [-n] command [args...]",
	Short:     "run specified go tool",
	Long: `
Tool runs the go tool command identified by the arguments.
With no arguments it prints the list of known tools.

The -n flag causes tool to print the command that would be
executed but not execute it.

For more about each tool command, see 'go tool command -h'.
`,
}

var (
	toolGOOS      = runtime.GOOS
	toolGOARCH    = runtime.GOARCH
	toolIsWindows = toolGOOS == "windows"
	toolDir       = build.ToolDir

	toolN bool
)

func init() {
	cmdTool.Flag.BoolVar(&toolN, "n", false, "")
}

const toolWindowsExtension = ".exe"

func tool(name string) string {
	p := filepath.Join(toolDir, name)
	if toolIsWindows {
		p += toolWindowsExtension
	}
	return p
}

func runTool(cmd *Command, args []string) {
	if len(args) == 0 {
		listTools()
		return
	}
	toolName := args[0]
	// The tool name must be lower-case letters and numbers.
	for _, c := range toolName {
		switch {
		case 'a' <= c && c <= 'z', '0' <= c && c <= '9':
		default:
			fmt.Fprintf(os.Stderr, "go tool: bad tool name %q\n", toolName)
			setExitStatus(2)
			return
		}
	}
	toolPath := tool(toolName)
	// Give a nice message if there is no tool with that name.
	if _, err := os.Stat(toolPath); err != nil {
		fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName)
		setExitStatus(3)
		return
	}

	if toolN {
		fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " "))
		return
	}
	toolCmd := &exec.Cmd{
		Path:   toolPath,
		Args:   args,
		Stdin:  os.Stdin,
		Stdout: os.Stdout,
		Stderr: os.Stderr,
	}
	err := toolCmd.Run()
	if err != nil {
		fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err)
		setExitStatus(1)
		return
	}
}

// listTools prints a list of the available tools in the tools directory.
func listTools() {
	f, err := os.Open(toolDir)
	if err != nil {
		fmt.Fprintf(os.Stderr, "go tool: no tool directory: %s\n", err)
		setExitStatus(2)
		return
	}
	defer f.Close()
	names, err := f.Readdirnames(-1)
	if err != nil {
		fmt.Fprintf(os.Stderr, "go tool: can't read directory: %s\n", err)
		setExitStatus(2)
		return
	}

	sort.Strings(names)
	for _, name := range names {
		// Unify presentation by going to lower case.
		name = strings.ToLower(name)
		// If it's windows, don't show the .exe suffix.
		if toolIsWindows && strings.HasSuffix(name, toolWindowsExtension) {
			name = name[:len(name)-len(toolWindowsExtension)]
		}
		fmt.Println(name)
	}
}
<|endoftext|>


<commit_before>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to Discord voice suppport

package discordgo

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/websocket"
)

// ------------------------------------------------------------------------------------------------
// Code related to both Voice Websocket and UDP connections.
// ------------------------------------------------------------------------------------------------

// A Voice struct holds all data and functions related to Discord Voice support.
type Voice struct {
	sync.Mutex               // future use
	Ready      bool          // If true, voice is ready to send/receive audio
	Debug      bool          // If true, print extra logging
	Chan       chan struct{} // future use
	UDPConn    *net.UDPConn  // exported for dgvoice, may change.
	OP2        *voiceOP2     // exported for dgvoice, may change.

	wsConn *websocket.Conn

	sessionID string
	token     string
	endpoint  string
	guildID   string
	channelID string
	userID    string
}

// ------------------------------------------------------------------------------------------------
// Code related to the Voice websocket connection
// ------------------------------------------------------------------------------------------------

// A voiceOP2 stores the data for the voice operation 2 websocket event
// which is sort of like the voice READY packet
type voiceOP2 struct {
	SSRC              uint32        `json:"ssrc"`
	Port              int           `json:"port"`
	Modes             []string      `json:"modes"`
	HeartbeatInterval time.Duration `json:"heartbeat_interval"`
}

type voiceHandshakeData struct {
	ServerID  string `json:"server_id"`
	UserID    string `json:"user_id"`
	SessionID string `json:"session_id"`
	Token     string `json:"token"`
}

type voiceHandshakeOp struct {
	Op   int                `json:"op"` // Always 0
	Data voiceHandshakeData `json:"d"`
}

// Open opens a voice connection.  This should be called
// after VoiceChannelJoin is used and the data VOICE websocket events
// are captured.
func (v *Voice) Open() (err error) {

	// TODO: How do we handle changing channels?

	// Don't open a websocket if one is already open
	if v.wsConn != nil {
		return
	}

	// Connect to Voice Websocket
	vg := fmt.Sprintf("wss://%s", strings.TrimSuffix(v.endpoint, ":80"))
	v.wsConn, _, err = websocket.DefaultDialer.Dial(vg, nil)
	if err != nil {
		fmt.Println("VOICE error opening websocket:", err)
		return
	}

	data := voiceHandshakeOp{0, voiceHandshakeData{v.guildID, v.userID, v.sessionID, v.token}}

	err = v.wsConn.WriteJSON(data)
	if err != nil {
		fmt.Println("VOICE error sending init packet:", err)
		return
	}

	// Start a listening for voice websocket events
	// TODO add a check here to make sure Listen worked by monitoring
	// a chan or bool?
	go v.wsListen()

	return
}

// Close closes the voice connection
func (v *Voice) Close() {

	if v.UDPConn != nil {
		v.UDPConn.Close()
	}

	if v.wsConn != nil {
		v.wsConn.Close()
	}
}

// wsListen listens on the voice websocket for messages and passes them
// to the voice event handler.  This is automaticly called by the Open func
func (v *Voice) wsListen() {

	for {
		messageType, message, err := v.wsConn.ReadMessage()
		if err != nil {
			// TODO: Handle this problem better.
			// TODO: needs proper logging
			fmt.Println("Voice Listen Error:", err)
			break
		}

		// Pass received message to voice event handler
		go v.wsEvent(messageType, message)
	}

	return
}

// wsEvent handles any voice websocket events. This is only called by the
// wsListen() function.
func (v *Voice) wsEvent(messageType int, message []byte) {

	if v.Debug {
		fmt.Println("wsEvent received: ", messageType)
		printJSON(message)
	}

	var e Event
	if err := json.Unmarshal(message, &e); err != nil {
		fmt.Println("wsEvent Unmarshall error: ", err)
		return
	}

	switch e.Operation {

	case 2: // READY

		v.OP2 = &voiceOP2{}
		if err := json.Unmarshal(e.RawData, v.OP2); err != nil {
			fmt.Println("voiceWS.onEvent OP2 Unmarshall error: ", err)
			printJSON(e.RawData) // TODO: Better error logging
			return
		}

		// Start the voice websocket heartbeat to keep the connection alive
		go v.wsHeartbeat(v.OP2.HeartbeatInterval)
		// TODO monitor a chan/bool to verify this was successful

		// Start the UDP connection
		err := v.udpOpen()
		if err != nil {
			fmt.Println("Error opening udp connection: ", err)
			return
		}

		// start udpKeepAlive
		go v.udpKeepAlive(5 * time.Second)
		// TODO: find a way to check that it fired off okay

		return

	case 3: // HEARTBEAT response
		// add code to use this to track latency?
		return

	case 4:
		// TODO

	case 5:
		// SPEAKING TRUE/FALSE NOTIFICATION
		/*
			{
				"user_id": "1238921738912",
				"ssrc": 2,
				"speaking": false
			}
		*/

	default:
		fmt.Println("UNKNOWN VOICE OP: ", e.Operation)
		printJSON(e.RawData)
	}

	return
}

type voiceHeartbeatOp struct {
	Op   int `json:"op"` // Always 3
	Data int `json:"d"`
}

// wsHeartbeat sends regular heartbeats to voice Discord so it knows the client
// is still connected.  If you do not send these heartbeats Discord will
// disconnect the websocket connection after a few seconds.
func (v *Voice) wsHeartbeat(i time.Duration) {

	ticker := time.NewTicker(i * time.Millisecond)
	for {
		err := v.wsConn.WriteJSON(voiceHeartbeatOp{3, int(time.Now().Unix())})
		if err != nil {
			v.Ready = false
			fmt.Println("wsHeartbeat send error: ", err)
			return // TODO better logging
		}
		<-ticker.C
	}
}

type voiceSpeakingData struct {
	Speaking bool `json:"speaking"`
	Delay    int  `json:"delay"`
}

type voiceSpeakingOp struct {
	Op   int               `json:"op"` // Always 5
	Data voiceSpeakingData `json:"d"`
}

// Speaking sends a speaking notification to Discord over the voice websocket.
// This must be sent as true prior to sending audio and should be set to false
// once finished sending audio.
//  b  : Send true if speaking, false if not.
func (v *Voice) Speaking(b bool) (err error) {

	if v.wsConn == nil {
		return fmt.Errorf("No Voice websocket.")
	}

	data := voiceSpeakingOp{5, voiceSpeakingData{b, 0}}
	err = v.wsConn.WriteJSON(data)
	if err != nil {
		fmt.Println("Speaking() write json error:", err)
		return
	}

	return
}

// ------------------------------------------------------------------------------------------------
// Code related to the Voice UDP connection
// ------------------------------------------------------------------------------------------------

type voiceUDPData struct {
	Address string `json:"address"` // Public IP of machine running this code
	Port    uint16 `json:"port"`    // UDP Port of machine running this code
	Mode    string `json:"mode"`    // plain or ?  (plain or encrypted)
}

type voiceUDPD struct {
	Protocol string       `json:"protocol"` // Always "udp" ?
	Data     voiceUDPData `json:"data"`
}

type voiceUDPOp struct {
	Op   int       `json:"op"` // Always 1
	Data voiceUDPD `json:"d"`
}

// udpOpen opens a UDP connection to the voice server and completes the
// initial required handshake.  This connection is left open in the session
// and can be used to send or receive audio.  This should only be called
// from voice.wsEvent OP2
func (v *Voice) udpOpen() (err error) {

	host := fmt.Sprintf("%s:%d", strings.TrimSuffix(v.endpoint, ":80"), v.OP2.Port)
	addr, err := net.ResolveUDPAddr("udp", host)
	if err != nil {
		fmt.Println("udpOpen resolve addr error: ", err)
		// TODO better logging
		return
	}

	v.UDPConn, err = net.DialUDP("udp", nil, addr)
	if err != nil {
		fmt.Println("udpOpen dial udp error: ", err)
		// TODO better logging
		return
	}

	// Create a 70 byte array and put the SSRC code from the Op 2 Voice event
	// into it.  Then send that over the UDP connection to Discord
	sb := make([]byte, 70)
	binary.BigEndian.PutUint32(sb, v.OP2.SSRC)
	_, err = v.UDPConn.Write(sb)
	if err != nil {
		fmt.Println("udpOpen udp write error : ", err)
		// TODO better logging
		return
	}

	// Create a 70 byte array and listen for the initial handshake response
	// from Discord.  Once we get it parse the IP and PORT information out
	// of the response.  This should be our public IP and PORT as Discord
	// saw us.
	rb := make([]byte, 70)
	rlen, _, err := v.UDPConn.ReadFromUDP(rb)
	if err != nil {
		fmt.Println("udpOpen udp read error : ", err)
		// TODO better logging
		return
	}
	if rlen < 70 {
		fmt.Println("Voice RLEN should be 70 but isn't")
	}

	// Loop over position 4 though 20 to grab the IP address
	// Should never be beyond position 20.
	var ip string
	for i := 4; i < 20; i++ {
		if rb[i] == 0 {
			break
		}
		ip += string(rb[i])
	}

	// Grab port from postion 68 and 69
	port := binary.LittleEndian.Uint16(rb[68:70])

	// Take the data from above and send it back to Discord to finalize
	// the UDP connection handshake.
	data := voiceUDPOp{1, voiceUDPD{"udp", voiceUDPData{ip, port, "plain"}}}

	err = v.wsConn.WriteJSON(data)
	if err != nil {
		fmt.Println("udpOpen write json error:", err)
		return
	}

	v.Ready = true
	return
}

// udpKeepAlive sends a udp packet to keep the udp connection open
// This is still a bit of a "proof of concept"
func (v *Voice) udpKeepAlive(i time.Duration) {

	var err error
	var sequence uint64 = 0

	packet := make([]byte, 8)

	ticker := time.NewTicker(i)
	for {
		// TODO: Add a way to break from loop

		binary.LittleEndian.PutUint64(packet, sequence)
		sequence++

		_, err = v.UDPConn.Write(packet)
		if err != nil {
			fmt.Println("udpKeepAlive udp write error : ", err)
			return
		}
		<-ticker.C
	}
}
<commit_msg>Added opusSender func<commit_after>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to Discord voice suppport

package discordgo

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"net"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/websocket"
)

// ------------------------------------------------------------------------------------------------
// Code related to both Voice Websocket and UDP connections.
// ------------------------------------------------------------------------------------------------

// A Voice struct holds all data and functions related to Discord Voice support.
type Voice struct {
	sync.Mutex             // future use
	Ready      bool        // If true, voice is ready to send/receive audio
	Debug      bool        // If true, print extra logging
	OP2        *voiceOP2   // exported for dgvoice, may change.
	Opus       chan []byte // Chan for sending opus audio
	//	FrameRate  int         // This can be used to set the FrameRate of Opus data
	//	FrameSize  int         // This can be used to set the FrameSize of Opus data

	wsConn  *websocket.Conn
	UDPConn *net.UDPConn // this will become unexported soon.

	sessionID string
	token     string
	endpoint  string
	guildID   string
	channelID string
	userID    string
}

// ------------------------------------------------------------------------------------------------
// Code related to the Voice websocket connection
// ------------------------------------------------------------------------------------------------

// A voiceOP2 stores the data for the voice operation 2 websocket event
// which is sort of like the voice READY packet
type voiceOP2 struct {
	SSRC              uint32        `json:"ssrc"`
	Port              int           `json:"port"`
	Modes             []string      `json:"modes"`
	HeartbeatInterval time.Duration `json:"heartbeat_interval"`
}

type voiceHandshakeData struct {
	ServerID  string `json:"server_id"`
	UserID    string `json:"user_id"`
	SessionID string `json:"session_id"`
	Token     string `json:"token"`
}

type voiceHandshakeOp struct {
	Op   int                `json:"op"` // Always 0
	Data voiceHandshakeData `json:"d"`
}

// Open opens a voice connection.  This should be called
// after VoiceChannelJoin is used and the data VOICE websocket events
// are captured.
func (v *Voice) Open() (err error) {

	// TODO: How do we handle changing channels?

	// Don't open a websocket if one is already open
	if v.wsConn != nil {
		return
	}

	// Connect to Voice Websocket
	vg := fmt.Sprintf("wss://%s", strings.TrimSuffix(v.endpoint, ":80"))
	v.wsConn, _, err = websocket.DefaultDialer.Dial(vg, nil)
	if err != nil {
		fmt.Println("VOICE error opening websocket:", err)
		return
	}

	data := voiceHandshakeOp{0, voiceHandshakeData{v.guildID, v.userID, v.sessionID, v.token}}

	err = v.wsConn.WriteJSON(data)
	if err != nil {
		fmt.Println("VOICE error sending init packet:", err)
		return
	}

	// Start a listening for voice websocket events
	// TODO add a check here to make sure Listen worked by monitoring
	// a chan or bool?
	go v.wsListen()

	return
}

// Close closes the voice connection
func (v *Voice) Close() {

	if v.UDPConn != nil {
		v.UDPConn.Close()
	}

	if v.wsConn != nil {
		v.wsConn.Close()
	}
}

// wsListen listens on the voice websocket for messages and passes them
// to the voice event handler.  This is automaticly called by the Open func
func (v *Voice) wsListen() {

	for {
		messageType, message, err := v.wsConn.ReadMessage()
		if err != nil {
			// TODO: Handle this problem better.
			// TODO: needs proper logging
			fmt.Println("Voice Listen Error:", err)
			break
		}

		// Pass received message to voice event handler
		go v.wsEvent(messageType, message)
	}

	return
}

// wsEvent handles any voice websocket events. This is only called by the
// wsListen() function.
func (v *Voice) wsEvent(messageType int, message []byte) {

	if v.Debug {
		fmt.Println("wsEvent received: ", messageType)
		printJSON(message)
	}

	var e Event
	if err := json.Unmarshal(message, &e); err != nil {
		fmt.Println("wsEvent Unmarshall error: ", err)
		return
	}

	switch e.Operation {

	case 2: // READY

		v.OP2 = &voiceOP2{}
		if err := json.Unmarshal(e.RawData, v.OP2); err != nil {
			fmt.Println("voiceWS.onEvent OP2 Unmarshall error: ", err)
			printJSON(e.RawData) // TODO: Better error logging
			return
		}

		// Start the voice websocket heartbeat to keep the connection alive
		go v.wsHeartbeat(v.OP2.HeartbeatInterval)
		// TODO monitor a chan/bool to verify this was successful

		// Start the UDP connection
		err := v.udpOpen()
		if err != nil {
			fmt.Println("Error opening udp connection: ", err)
			return
		}

		// Start the opusSender.
		// TODO: Should we allow 48000/960 values to be user defined?
		v.Opus = make(chan []byte, 2)
		go v.opusSender(v.Opus, 48000, 960)

		return

	case 3: // HEARTBEAT response
		// add code to use this to track latency?
		return

	case 4:
		// TODO

	case 5:
		// SPEAKING TRUE/FALSE NOTIFICATION
		/*
			{
				"user_id": "1238921738912",
				"ssrc": 2,
				"speaking": false
			}
		*/

	default:
		fmt.Println("UNKNOWN VOICE OP: ", e.Operation)
		printJSON(e.RawData)
	}

	return
}

type voiceHeartbeatOp struct {
	Op   int `json:"op"` // Always 3
	Data int `json:"d"`
}

// wsHeartbeat sends regular heartbeats to voice Discord so it knows the client
// is still connected.  If you do not send these heartbeats Discord will
// disconnect the websocket connection after a few seconds.
func (v *Voice) wsHeartbeat(i time.Duration) {

	ticker := time.NewTicker(i * time.Millisecond)
	for {
		err := v.wsConn.WriteJSON(voiceHeartbeatOp{3, int(time.Now().Unix())})
		if err != nil {
			v.Ready = false
			fmt.Println("wsHeartbeat send error: ", err)
			return // TODO better logging
		}
		<-ticker.C
	}
}

type voiceSpeakingData struct {
	Speaking bool `json:"speaking"`
	Delay    int  `json:"delay"`
}

type voiceSpeakingOp struct {
	Op   int               `json:"op"` // Always 5
	Data voiceSpeakingData `json:"d"`
}

// Speaking sends a speaking notification to Discord over the voice websocket.
// This must be sent as true prior to sending audio and should be set to false
// once finished sending audio.
//  b  : Send true if speaking, false if not.
func (v *Voice) Speaking(b bool) (err error) {

	if v.wsConn == nil {
		return fmt.Errorf("No Voice websocket.")
	}

	data := voiceSpeakingOp{5, voiceSpeakingData{b, 0}}
	err = v.wsConn.WriteJSON(data)
	if err != nil {
		fmt.Println("Speaking() write json error:", err)
		return
	}

	return
}

// ------------------------------------------------------------------------------------------------
// Code related to the Voice UDP connection
// ------------------------------------------------------------------------------------------------

type voiceUDPData struct {
	Address string `json:"address"` // Public IP of machine running this code
	Port    uint16 `json:"port"`    // UDP Port of machine running this code
	Mode    string `json:"mode"`    // plain or ?  (plain or encrypted)
}

type voiceUDPD struct {
	Protocol string       `json:"protocol"` // Always "udp" ?
	Data     voiceUDPData `json:"data"`
}

type voiceUDPOp struct {
	Op   int       `json:"op"` // Always 1
	Data voiceUDPD `json:"d"`
}

// udpOpen opens a UDP connection to the voice server and completes the
// initial required handshake.  This connection is left open in the session
// and can be used to send or receive audio.  This should only be called
// from voice.wsEvent OP2
func (v *Voice) udpOpen() (err error) {

	host := fmt.Sprintf("%s:%d", strings.TrimSuffix(v.endpoint, ":80"), v.OP2.Port)
	addr, err := net.ResolveUDPAddr("udp", host)
	if err != nil {
		fmt.Println("udpOpen resolve addr error: ", err)
		// TODO better logging
		return
	}

	v.UDPConn, err = net.DialUDP("udp", nil, addr)
	if err != nil {
		fmt.Println("udpOpen dial udp error: ", err)
		// TODO better logging
		return
	}

	// Create a 70 byte array and put the SSRC code from the Op 2 Voice event
	// into it.  Then send that over the UDP connection to Discord
	sb := make([]byte, 70)
	binary.BigEndian.PutUint32(sb, v.OP2.SSRC)
	_, err = v.UDPConn.Write(sb)
	if err != nil {
		fmt.Println("udpOpen udp write error : ", err)
		// TODO better logging
		return
	}

	// Create a 70 byte array and listen for the initial handshake response
	// from Discord.  Once we get it parse the IP and PORT information out
	// of the response.  This should be our public IP and PORT as Discord
	// saw us.
	rb := make([]byte, 70)
	rlen, _, err := v.UDPConn.ReadFromUDP(rb)
	if err != nil {
		fmt.Println("udpOpen udp read error : ", err)
		// TODO better logging
		return
	}
	if rlen < 70 {
		fmt.Println("Voice RLEN should be 70 but isn't")
	}

	// Loop over position 4 though 20 to grab the IP address
	// Should never be beyond position 20.
	var ip string
	for i := 4; i < 20; i++ {
		if rb[i] == 0 {
			break
		}
		ip += string(rb[i])
	}

	// Grab port from postion 68 and 69
	port := binary.LittleEndian.Uint16(rb[68:70])

	// Take the data from above and send it back to Discord to finalize
	// the UDP connection handshake.
	data := voiceUDPOp{1, voiceUDPD{"udp", voiceUDPData{ip, port, "plain"}}}

	err = v.wsConn.WriteJSON(data)
	if err != nil {
		fmt.Println("udpOpen write json error:", err)
		return
	}

	// start udpKeepAlive
	go v.udpKeepAlive(5 * time.Second)
	// TODO: find a way to check that it fired off okay

	v.Ready = true
	return
}

// udpKeepAlive sends a udp packet to keep the udp connection open
// This is still a bit of a "proof of concept"
func (v *Voice) udpKeepAlive(i time.Duration) {

	var err error
	var sequence uint64 = 0

	packet := make([]byte, 8)

	ticker := time.NewTicker(i)
	for {
		// TODO: Add a way to break from loop

		binary.LittleEndian.PutUint64(packet, sequence)
		sequence++

		_, err = v.UDPConn.Write(packet)
		if err != nil {
			fmt.Println("udpKeepAlive udp write error : ", err)
			return
		}
		<-ticker.C
	}
}

// opusSender will listen on the given channel and send any
// pre-encoded opus audio to Discord.  Supposedly.
func (v *Voice) opusSender(opus <-chan []byte, rate, size int) {

	// TODO: Better checking to prevent this from running more than
	// one instance at a time.
	v.Lock()
	if opus == nil {
		v.Unlock()
		return
	}
	v.Unlock()

	runtime.LockOSThread()

	var sequence uint16 = 0
	var timestamp uint32 = 0
	udpHeader := make([]byte, 12)

	// build the parts that don't change in the udpHeader
	udpHeader[0] = 0x80
	udpHeader[1] = 0x78
	binary.BigEndian.PutUint32(udpHeader[8:], v.OP2.SSRC)

	// start a send loop that loops until buf chan is closed
	ticker := time.NewTicker(time.Millisecond * time.Duration(size/(rate/1000)))
	for {

		// Add sequence and timestamp to udpPacket
		binary.BigEndian.PutUint16(udpHeader[2:], sequence)
		binary.BigEndian.PutUint32(udpHeader[4:], timestamp)

		// Get data from chan.  If chan is closed, return.
		recvbuf, ok := <-opus
		if !ok {
			return
		}

		// Combine the UDP Header and the opus data
		sendbuf := append(udpHeader, recvbuf...)

		// block here until we're exactly at the right time :)
		// Then send rtp audio packet to Discord over UDP
		<-ticker.C
		v.UDPConn.Write(sendbuf)

		if (sequence) == 0xFFFF {
			sequence = 0
		} else {
			sequence += 1
		}

		if (timestamp + uint32(size)) >= 0xFFFFFFFF {
			timestamp = 0
		} else {
			timestamp += uint32(size)
		}
	}
}
<|endoftext|>


<commit_before>/*
Package contentaddressable contains tools for reading and writing content
addressable files. Files are written to a temporary location, and only renamed
to the final location after the file's OID (Object ID) has been verified.

    filename := "path/to/01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"
    file, err := contentaddressable.NewFile(filename)
    if err != nil {
      panic(err)
    }
    defer file.Close()

    file.Oid // 01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b

    written, err := io.Copy(file, someReader)

    if err == nil {
    // Move file to final location if OID is verified.
      err = file.Accept()
    }

    if err != nil {
      panic(err)
    }

Currently SHA-256 is used for a file's OID.

You can also read files, while verifying that they are not corrupt.

    filename := "path/to/01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"

    // get this from doing an os.Stat() or something
    expectedSize := 123

    // returns a contentaddressable.ReadCloser, with some extra functions on top
    // of io.ReadCloser.
    reader, err := contentaddressable.Open(filename)
    if err != nil {
      panic(err)
    }
    defer file.Close()

    written, err := io.Copy(ioutil.Discard, reader)
    if err != nil {
      panic(err)
    }

    seenBytes := reader.SeenBytes()

    if written != seenBytes {
      panic("reader is broken")
    }

    if seenBytes < expected {
      panic("partial read")
    }

    if reader.Oid() != "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b" {
      panic("SHA-256 signature doesn't match expected")
    }
*/
package contentaddressable
<commit_msg>simplify proposal<commit_after>/*
Package contentaddressable contains tools for reading and writing content
addressable files. Files are written to a temporary location, and only renamed
to the final location after the file's OID (Object ID) has been verified.

    filename := "path/to/01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"
    file, err := contentaddressable.NewFile(filename)
    if err != nil {
      panic(err)
    }
    defer file.Close()

    file.Oid // 01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b

    written, err := io.Copy(file, someReader)

    if err == nil {
    // Move file to final location if OID is verified.
      err = file.Accept()
    }

    if err != nil {
      panic(err)
    }

Currently SHA-256 is used for a file's OID.

You can also read files, while verifying that they are not corrupt.

    filename := "path/to/01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"

    // get this from doing an os.Stat() or something
    expectedSize := 123

    // returns a contentaddressable.ReadCloser
    reader, err := contentaddressable.Open(filename, expectedSize)
    if err != nil {
      panic(err)
    }
    defer file.Close()

    // A contentaddressable.ReadCloser ensures that exactly the expectedSize
    // number of bytes were read, and that the content matches the OID in the
    // filename.
    written, err := io.Copy(ioutil.Discard, reader)
    if err != nil {
      panic(err)
    }
*/
package contentaddressable
<|endoftext|>


<commit_before>package assetfs

import (
	"bytes"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"time"
)

var (
	fileTimestamp = time.Now()
)

// FakeFile implements os.FileInfo interface for a given path and size
type FakeFile struct {
	// Path is the path of this file
	Path string
	// Dir marks of the path is a directory
	Dir bool
	// Len is the length of the fake file, zero if it is a directory
	Len int64
}

func (f *FakeFile) Name() string {
	_, name := filepath.Split(f.Path)
	return name
}

func (f *FakeFile) Mode() os.FileMode {
	mode := os.FileMode(0644)
	if f.Dir {
		return mode | os.ModeDir
	}
	return mode
}

func (f *FakeFile) ModTime() time.Time {
	return fileTimestamp
}

func (f *FakeFile) Size() int64 {
	return f.Len
}

func (f *FakeFile) IsDir() bool {
	return f.Mode().IsDir()
}

func (f *FakeFile) Sys() interface{} {
	return nil
}

// AssetFile implements http.File interface for a no-directory file with content
type AssetFile struct {
	*bytes.Reader
	io.Closer
	FakeFile
}

func NewAssetFile(name string, content []byte) *AssetFile {
	return &AssetFile{
		bytes.NewReader(content),
		ioutil.NopCloser(nil),
		FakeFile{name, false, int64(len(content))}}
}

func (f *AssetFile) Readdir(count int) ([]os.FileInfo, error) {
	return nil, errors.New("not a directory")
}

func (f *AssetFile) Stat() (os.FileInfo, error) {
	return f, nil
}

// AssetDirectory implements http.File interface for a directory
type AssetDirectory struct {
	AssetFile
	ChildrenRead int
	Children     []os.FileInfo
}

func NewAssetDirectory(name string, children []string, fs *AssetFS) *AssetDirectory {
	fileinfos := make([]os.FileInfo, 0, len(children))
	for _, child := range children {
		_, err := fs.AssetDir(filepath.Join(name, child))
		fileinfos = append(fileinfos, &FakeFile{child, err == nil, 0})
	}
	return &AssetDirectory{
		AssetFile{
			bytes.NewReader(nil),
			ioutil.NopCloser(nil),
			FakeFile{name, true, 0},
		},
		0,
		fileinfos}
}

func (f *AssetDirectory) Readdir(count int) ([]os.FileInfo, error) {
	if count <= 0 {
		return f.Children, nil
	}
	if f.ChildrenRead+count > len(f.Children) {
		count = len(f.Children) - f.ChildrenRead
	}
	rv := f.Children[f.ChildrenRead : f.ChildrenRead+count]
	f.ChildrenRead += count
	return rv, nil
}

func (f *AssetDirectory) Stat() (os.FileInfo, error) {
	return f, nil
}

// AssetFS implements http.FileSystem, allowing
// embedded files to be served from net/http package.
type AssetFS struct {
	// Asset should return content of file in path if exists
	Asset func(path string) ([]byte, error)
	// AssetDir should return list of files in the path
	AssetDir func(path string) ([]string, error)
	// Prefix would be prepended to http requests
	Prefix string
}

func (fs *AssetFS) Open(name string) (http.File, error) {
	name = path.Join(fs.Prefix, name)
	if len(name) > 0 && name[0] == '/' {
		name = name[1:]
	}
	if children, err := fs.AssetDir(name); err == nil {
		return NewAssetDirectory(name, children, fs), nil
	}
	b, err := fs.Asset(name)
	if err != nil {
		return nil, err
	}
	return NewAssetFile(name, b), nil
}
<commit_msg>Fix build for go tip (a.k.a. go 1.5).<commit_after>package assetfs

import (
	"bytes"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"time"
)

var (
	fileTimestamp = time.Now()
)

// FakeFile implements os.FileInfo interface for a given path and size
type FakeFile struct {
	// Path is the path of this file
	Path string
	// Dir marks of the path is a directory
	Dir bool
	// Len is the length of the fake file, zero if it is a directory
	Len int64
}

func (f *FakeFile) Name() string {
	_, name := filepath.Split(f.Path)
	return name
}

func (f *FakeFile) Mode() os.FileMode {
	mode := os.FileMode(0644)
	if f.Dir {
		return mode | os.ModeDir
	}
	return mode
}

func (f *FakeFile) ModTime() time.Time {
	return fileTimestamp
}

func (f *FakeFile) Size() int64 {
	return f.Len
}

func (f *FakeFile) IsDir() bool {
	return f.Mode().IsDir()
}

func (f *FakeFile) Sys() interface{} {
	return nil
}

// AssetFile implements http.File interface for a no-directory file with content
type AssetFile struct {
	*bytes.Reader
	io.Closer
	FakeFile
}

func NewAssetFile(name string, content []byte) *AssetFile {
	return &AssetFile{
		bytes.NewReader(content),
		ioutil.NopCloser(nil),
		FakeFile{name, false, int64(len(content))}}
}

func (f *AssetFile) Readdir(count int) ([]os.FileInfo, error) {
	return nil, errors.New("not a directory")
}

func (f *AssetFile) Size() int64 {
	return f.FakeFile.Size()
}

func (f *AssetFile) Stat() (os.FileInfo, error) {
	return f, nil
}

// AssetDirectory implements http.File interface for a directory
type AssetDirectory struct {
	AssetFile
	ChildrenRead int
	Children     []os.FileInfo
}

func NewAssetDirectory(name string, children []string, fs *AssetFS) *AssetDirectory {
	fileinfos := make([]os.FileInfo, 0, len(children))
	for _, child := range children {
		_, err := fs.AssetDir(filepath.Join(name, child))
		fileinfos = append(fileinfos, &FakeFile{child, err == nil, 0})
	}
	return &AssetDirectory{
		AssetFile{
			bytes.NewReader(nil),
			ioutil.NopCloser(nil),
			FakeFile{name, true, 0},
		},
		0,
		fileinfos}
}

func (f *AssetDirectory) Readdir(count int) ([]os.FileInfo, error) {
	if count <= 0 {
		return f.Children, nil
	}
	if f.ChildrenRead+count > len(f.Children) {
		count = len(f.Children) - f.ChildrenRead
	}
	rv := f.Children[f.ChildrenRead : f.ChildrenRead+count]
	f.ChildrenRead += count
	return rv, nil
}

func (f *AssetDirectory) Stat() (os.FileInfo, error) {
	return f, nil
}

// AssetFS implements http.FileSystem, allowing
// embedded files to be served from net/http package.
type AssetFS struct {
	// Asset should return content of file in path if exists
	Asset func(path string) ([]byte, error)
	// AssetDir should return list of files in the path
	AssetDir func(path string) ([]string, error)
	// Prefix would be prepended to http requests
	Prefix string
}

func (fs *AssetFS) Open(name string) (http.File, error) {
	name = path.Join(fs.Prefix, name)
	if len(name) > 0 && name[0] == '/' {
		name = name[1:]
	}
	if children, err := fs.AssetDir(name); err == nil {
		return NewAssetDirectory(name, children, fs), nil
	}
	b, err := fs.Asset(name)
	if err != nil {
		return nil, err
	}
	return NewAssetFile(name, b), nil
}
<|endoftext|>


<commit_before>package bot // import "cirello.io/gobot/bot"

import (
	"cirello.io/gobot/brain"
	"cirello.io/gobot/messages"
)

// Self encapsulates all the necessary state to have a robot running. Including
// identity (Name).
type Self struct {
	Name        string
	providerOut chan messages.Message
	providerIn  chan messages.Message
	rules       []RuleParser

	brain *brain.Brain
}

// Option type is the self-referencing method of tweaking gobot's internals.
type Option func(*Self)

// New creates a new gobot.
func New(name string, opts ...Option) *Self {
	s := &Self{
		Name:        name,
		brain:       brain.New(),
		providerIn:  make(chan messages.Message),
		providerOut: make(chan messages.Message),
	}
	for _, opt := range opts {
		opt(s)
	}
	return s
}

// Process connects the flow of incoming messages with the ruleset, and
// dispatches the outgoing messages generated by the ruleset. Each message lives
// in its own goroutine.
func (s *Self) Process() {
	for in := range s.providerIn {
		go func(self Self, msg messages.Message) {
			for _, rule := range s.rules {
				responses := rule.ParseMessage(self, msg)
				for _, r := range responses {
					s.providerOut <- r
				}
			}
		}(*s, in)
	}
}

// MemoryRead reads an arbitraty value from the robot's Brain.
func (s *Self) MemoryRead(ruleName, key string) interface{} {
	return s.brain.Read(ruleName, key)
}

// MemorySave reads an arbitraty value from the robot's Brain.
func (s *Self) MemorySave(ruleName, key string, value interface{}) {
	s.brain.Save(ruleName, key, value)
}
<commit_msg>Ensure bot public interface cannot be messed by rules<commit_after>package bot // import "cirello.io/gobot/bot"

import (
	"sync"

	"cirello.io/gobot/brain"
	"cirello.io/gobot/messages"
)

// Self encapsulates all the necessary state to have a robot running. Including
// identity (Name).
type Self struct {
	Name        string
	providerOut chan messages.Message
	providerIn  chan messages.Message
	rules       []RuleParser

	brain *brain.Brain
	once  sync.Once // protects Process
}

// Option type is the self-referencing method of tweaking gobot's internals.
type Option func(*Self)

// New creates a new gobot.
func New(name string, opts ...Option) *Self {
	s := &Self{
		Name:        name,
		brain:       brain.New(),
		providerIn:  make(chan messages.Message),
		providerOut: make(chan messages.Message),
	}
	for _, opt := range opts {
		opt(s)
	}
	return s
}

// Process connects the flow of incoming messages with the ruleset, and
// dispatches the outgoing messages generated by the ruleset. Each message lives
// in its own goroutine.
func (s *Self) Process() {
	s.once.Do(func() {
		for in := range s.providerIn {
			go func(self Self, msg messages.Message) {
				for _, rule := range s.rules {
					responses := rule.ParseMessage(self, msg)
					for _, r := range responses {
						s.providerOut <- r
					}
				}
			}(*s, in)
		}
	})
}

// MemoryRead reads an arbitraty value from the robot's Brain.
func (s *Self) MemoryRead(ruleName, key string) interface{} {
	return s.brain.Read(ruleName, key)
}

// MemorySave reads an arbitraty value from the robot's Brain.
func (s *Self) MemorySave(ruleName, key string, value interface{}) {
	s.brain.Save(ruleName, key, value)
}
<|endoftext|>


<commit_before>package webgo
import (
	"net/http"
	"reflect"
	"net/url"
	"errors"
	"io/ioutil"
	"io"
	"encoding/json"
	"html/template"
	"path/filepath"
	"os"
	"strings"
	"fmt"
	"mime"
)

type App struct {
	router Router
	definitions Definitions
	templates *template.Template
	staticDir string
}

var app App

func init(){
	templates := template.New("template")
	filepath.Walk("templates", func(path string, info os.FileInfo, err error) error {
		if strings.HasSuffix(path, ".html") {
			templates.ParseFiles(path)
		}
		return nil
	})
	app = App{}
	app.router = Router{make(Routes)}
	app.definitions = Definitions{}
	app.templates = templates
	app.staticDir = "public"
}

func parseBody(ctx *Context) (err error) {
	var body []byte
	defer func() {
		r:=recover()
		if r != nil {
			http.Error(ctx.Response, "", 400)
			err = errors.New("Bad Request")
		}
	}()

	switch ctx.ContentType {
	case "application/json":
		body, err = ioutil.ReadAll(ctx.Request.Body)
		if err != nil {
			http.Error(ctx.Response, "", 400)
			return
		}

		var data interface{}
		err = json.Unmarshal(body, &data)
		if err != nil {
			http.Error(ctx.Response, "", 400)
			return
		}
		ctx._Body = body
		ctx.Body = data.(map[string]interface{})

		return

	case "application/x-www-form-urlencoded":
		g:=ctx.Request.ParseForm()
		fmt.Println("",ctx.Request.PostForm,ctx.Request.Form,g)

		// TODO Может быть проблема с чтением пустого запроса EOF
		var reader io.Reader = ctx.Request.Body
		var values url.Values

		maxFormSize := int64(10 << 20)
		reader = io.LimitReader(ctx.Request.Body, maxFormSize+1)

		body, err = ioutil.ReadAll(reader)
		if err != nil {
			http.Error(ctx.Response, "", 400)
			return
		}

		if int64(len(body)) > maxFormSize {
			http.Error(ctx.Response, "", 413)
			err = errors.New("Request Entity Too Large")
			return
		}

		values, err = url.ParseQuery(string(body))

		if err != nil{
			http.Error(ctx.Response, "", 400)
			return
		}

		for i := range values{
			if len(values[i]) == 1{
				ctx.Body[i] = values[i][0]
			} else {
				ctx.Body[i] = values[i]
			}
		}
		ctx._Body = body
		return
	case "multipart/form-data":
		return
	default:
		err = errors.New("Bad Request")
		http.Error(ctx.Response, "", 400)
		return
	}

	return err
}

func parseRequest (ctx *Context) (err error){
	if (ctx.Request.Method == "GET") {
		err = ctx.Request.ParseForm()
		// TODO: скопировать данные
		return
	}

	if ctx.Request.Method != "POST" && ctx.Request.Method != "PUT" && ctx.Request.Method != "PATCH" {
		return
	}

	ctx.ContentType = ctx.Request.Header.Get("Content-Type")
	ctx.ContentType, _, err = mime.ParseMediaType(ctx.ContentType)

	if err != nil {
		http.Error(ctx.Response, "", 400)
		return
	}

	if ctx.ContentType != "application/json" &&
		ctx.ContentType != "application/x-www-form-urlencoded" &&
		ctx.ContentType != "multipart/form-data" {
			err = errors.New("Bad Request")
			http.Error(ctx.Response, "", 400)
			return
	}

	// TODO: Правильно спарсить + скопировать данные
	err = parseBody(ctx)
	return
}

func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var vc reflect.Value
	var Action reflect.Value
	var middlewareGroup string

	method := r.Method
	path := r.URL.Path

	// Отдаем статику если был запрошен файл
	ext:= filepath.Ext(path)
	if ext != "" {
		http.ServeFile(w, r, app.staticDir+filepath.Clean(path))
		return
	}

	if (len(path)>1 && path[len(path)-1:] == "/") {
		http.Redirect(w,r, path[:len(path) - 1], 301)
		return
	}

	// Определем контроллер по прямому вхождению
	if route, ok := a.router.routes[path]; ok {
		if route.Method != method {
			http.Error(w, "", 404)
			return
		}

		vc = reflect.New(route.Controller)
		Action = vc.MethodByName(route.Action)
		middlewareGroup = route.MiddlewareGroup
	} else {
		// Определяем контроллер по совпадениям
		route := a.router.Match(method,path)
		if route == nil{
			http.Error(w, "", 404)
			return
		} else {
			vc = reflect.New(route.Controller)
			Action = vc.MethodByName(route.Action)
			middlewareGroup = route.MiddlewareGroup
		}
	}

	Controller, ok := vc.Interface().(ControllerInterface)
	if !ok {
		// TODO: Заменить панику
		panic("controller is not ControllerInterface")
	}

	ctx:= Context{Response:w, Request:r, Query: make(map[string]interface{}), Body: make(map[string]interface{}), Method:method}

	// Парсим запрос
	err := parseRequest(&ctx)
	if err != nil {
		return
	}

	// Инициализация контекста
	Controller.Init(&ctx)

	// Запуск предобработчика
	Controller.Prepare()

	// Запуск цепочки middleware
	if middlewareGroup != "" {
		isNext := app.definitions.Run(middlewareGroup,&ctx)
		if !isNext {
			return
		}
	}

	// Запуск Экшена
	in := make([]reflect.Value, 0)
	Action.Call(in)

	// Обрабатываем ошибки
	if ctx.error != nil {
		// TODO: Записать в лог
		http.Error(w, "", 500)
		return
	}

	// Запуск постобработчика
	Controller.Finish()
}

func RegisterMiddleware(name string, plugins ...MiddlewareInterface)  {
	for _, plugin:= range plugins {
		app.definitions.Register(name, plugin)
	}
}

func Get(url string, controller ControllerInterface, middlewareGroup string, flags []string, action string) {
	app.router.addRoute("GET", url, controller, action, middlewareGroup)
}
func Post(url string, controller ControllerInterface, middlewareGroup string, flags []string, action string) {
	app.router.addRoute("POST", url, controller, action, middlewareGroup)
}
func Put(url string, controller ControllerInterface, middlewareGroup string, flags []string, action string) {
	app.router.addRoute("PUT", url, controller, action, middlewareGroup)
}
func Delete(url string, controller ControllerInterface, middlewareGroup string, flags []string, action string) {
	app.router.addRoute("DELETE", url, controller, action, middlewareGroup)
}
func Options(url string, controller ControllerInterface, middlewareGroup string, flags []string, action string)  {
	app.router.addRoute("OPTIONS", url, controller, action, middlewareGroup)
}

func Run(port string)  {
	http.ListenAndServe(port, &app)
}<commit_msg>Доработки<commit_after>package webgo
import (
	"net/http"
	"reflect"
	"net/url"
	"errors"
	"io/ioutil"
	"io"
	"encoding/json"
	"html/template"
	"path/filepath"
	"os"
	"strings"
	"fmt"
	"mime"
)

type App struct {
	router Router
	definitions Definitions
	templates *template.Template
	staticDir string
}

var app App

func init(){
	templates := template.New("template")
	filepath.Walk("templates", func(path string, info os.FileInfo, err error) error {
		if strings.HasSuffix(path, ".html") {
			templates.ParseFiles(path)
		}
		return nil
	})
	app = App{}
	app.router = Router{make(Routes)}
	app.definitions = Definitions{}
	app.templates = templates
	app.staticDir = "public"
}

func parseBody(ctx *Context) (err error) {
	var body []byte
	defer func() {
		r:=recover()
		if r != nil {
			http.Error(ctx.Response, "", 400)
			err = errors.New("Bad Request")
		}
	}()

	switch ctx.ContentType {
	case "application/json":
		body, err = ioutil.ReadAll(ctx.Request.Body)
		if err != nil {
			http.Error(ctx.Response, "", 400)
			return
		}

		var data interface{}
		err = json.Unmarshal(body, &data)
		if err != nil {
			http.Error(ctx.Response, "", 400)
			return
		}
		ctx._Body = body
		ctx.Body = data.(map[string]interface{})

		return

	case "application/x-www-form-urlencoded":
		g:=ctx.Request.ParseForm()
		fmt.Println("",ctx.Request.PostForm,ctx.Request.Form,g)

		// TODO Может быть проблема с чтением пустого запроса EOF
		var reader io.Reader = ctx.Request.Body
		var values url.Values

		maxFormSize := int64(10 << 20)
		reader = io.LimitReader(ctx.Request.Body, maxFormSize+1)

		body, err = ioutil.ReadAll(reader)
		if err != nil {
			http.Error(ctx.Response, "", 400)
			return
		}

		if int64(len(body)) > maxFormSize {
			http.Error(ctx.Response, "", 413)
			err = errors.New("Request Entity Too Large")
			return
		}

		values, err = url.ParseQuery(string(body))

		if err != nil{
			http.Error(ctx.Response, "", 400)
			return
		}

		for i := range values{
			if len(values[i]) == 1{
				ctx.Body[i] = values[i][0]
			} else {
				ctx.Body[i] = values[i]
			}
		}
		ctx._Body = body
		return
	case "multipart/form-data":
		return
	default:
		err = errors.New("Bad Request")
		http.Error(ctx.Response, "", 400)
		return
	}

	return err
}

func parseRequest (ctx *Context) (err error){
	if (ctx.Request.Method == "GET") {
		err = ctx.Request.ParseForm()
		// TODO: скопировать данные
		return
	}

	if ctx.Request.Method != "POST" && ctx.Request.Method != "PUT" && ctx.Request.Method != "PATCH" {
		return
	}

	ctx.ContentType = ctx.Request.Header.Get("Content-Type")
	ctx.ContentType, _, err = mime.ParseMediaType(ctx.ContentType)

	if err != nil {
		http.Error(ctx.Response, "", 400)
		return
	}

	if ctx.ContentType != "application/json" &&
		ctx.ContentType != "application/x-www-form-urlencoded" &&
		ctx.ContentType != "multipart/form-data" {
			err = errors.New("Bad Request")
			http.Error(ctx.Response, "", 400)
			return
	}

	// TODO: Правильно спарсить + скопировать данные
	err = parseBody(ctx)
	return
}

func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var vc reflect.Value
	var Action reflect.Value
	var middlewareGroup string

	method := r.Method
	path := r.URL.Path

	// Отдаем статику если был запрошен файл
	ext:= filepath.Ext(path)
	if ext != "" {
		http.ServeFile(w, r, app.staticDir+filepath.Clean(path))
		return
	}

	if (len(path)>1 && path[len(path)-1:] == "/") {
		http.Redirect(w,r, path[:len(path) - 1], 301)
		return
	}

	// Определем контроллер по прямому вхождению
	if route, ok := a.router.routes[path]; ok {
		if route.Method != method {
			http.Error(w, "", 404)
			return
		}

		vc = reflect.New(route.Controller)
		Action = vc.MethodByName(route.Action)
		middlewareGroup = route.MiddlewareGroup
	} else {
		// Определяем контроллер по совпадениям
		route := a.router.Match(method,path)
		if route == nil{
			http.Error(w, "", 404)
			return
		} else {
			vc = reflect.New(route.Controller)
			Action = vc.MethodByName(route.Action)
			middlewareGroup = route.MiddlewareGroup
		}
	}

	Controller, ok := vc.Interface().(ControllerInterface)
	if !ok {
		// TODO: Заменить панику
		panic("controller is not ControllerInterface")
	}

	ctx:= Context{Response:w, Request:r, Query: make(map[string]interface{}), Body: make(map[string]interface{}), Method:method}

	// Парсим запрос
	err := parseRequest(&ctx)
	if err != nil {
		return
	}

	// Инициализация контекста
	Controller.Init(&ctx)

	// Запуск предобработчика
	Controller.Prepare()

	// Запуск цепочки middleware
	if middlewareGroup != "" {
		isNext := app.definitions.Run(middlewareGroup,&ctx)
		if !isNext {
			return
		}
	}

	// Запуск Экшена
	in := make([]reflect.Value, 0)
	Action.Call(in)

	// Обрабатываем ошибки
	if ctx.error != nil {
		// TODO: Записать в лог
		http.Error(w, "", 500)
		return
	}

	// Запуск постобработчика
	Controller.Finish()
}

func RegisterMiddleware(name string, plugins ...MiddlewareInterface)  {
	for _, plugin:= range plugins {
		app.definitions.Register(name, plugin)
	}
}

func Get(url string, controller ControllerInterface, middlewareGroup string, flags []string, action string) {
	app.router.addRoute("GET", url, controller, action, middlewareGroup)
}
func Post(url string, controller ControllerInterface, middlewareGroup string, flags []string, action string) {
	app.router.addRoute("POST", url, controller, action, middlewareGroup)
}
func Put(url string, controller ControllerInterface, middlewareGroup string, flags []string, action string) {
	app.router.addRoute("PUT", url, controller, action, middlewareGroup)
}
func Delete(url string, controller ControllerInterface, middlewareGroup string, flags []string, action string) {
	app.router.addRoute("DELETE", url, controller, action, middlewareGroup)
}
func Options(url string, controller ControllerInterface, middlewareGroup string, flags []string, action string)  {
	app.router.addRoute("OPTIONS", url, controller, action, middlewareGroup)
}

func Run(port string)  {
	if CFG["port"] == "" {
		LOGGER.Fatal("Unknow port")
	}
	http.ListenAndServe(":"+CFG["port"], &app)
}<|endoftext|>


<commit_before>package main

import (
	"time"

	"github.com/citadel/citadel"
)

type (
	EventHandler struct {
		Manager *Manager
	}
)

func (l *EventHandler) Handle(e *citadel.Event) error {
	logger.Infof("event: date=%s type=%s image=%s container=%s", e.Time.Format(time.RubyDate), e.Type, e.Container.Image.Name, e.Container.ID[:12])
	switch e.Type {
	case "start", "restart":
		l.handleUpdate(e)
	case "kill":
		// add delay to make sure container is removed
		time.Sleep(250 * time.Millisecond)
		l.handleUpdate(e)
	}
	return nil
}

func (l *EventHandler) handleUpdate(e *citadel.Event) error {
	if err := l.Manager.UpdateConfig(e); err != nil {
		logger.Fatal(err)
	}
	if err := l.Manager.Reload(); err != nil {
		return err
	}
	return nil
}
<commit_msg>fixes #2; reload config on container stop<commit_after>package main

import (
	"time"

	"github.com/citadel/citadel"
)

type (
	EventHandler struct {
		Manager *Manager
	}
)

func (l *EventHandler) Handle(e *citadel.Event) error {
	logger.Infof("event: date=%s type=%s image=%s container=%s", e.Time.Format(time.RubyDate), e.Type, e.Container.Image.Name, e.Container.ID[:12])
	switch e.Type {
	case "start", "restart":
		l.handleUpdate(e)
	case "stop", "kill":
		// add delay to make sure container is removed
		time.Sleep(250 * time.Millisecond)
		l.handleUpdate(e)
	}
	return nil
}

func (l *EventHandler) handleUpdate(e *citadel.Event) error {
	if err := l.Manager.UpdateConfig(e); err != nil {
		logger.Fatal(err)
	}
	if err := l.Manager.Reload(); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package controller

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

// GetProfile displays the current profile.
func GetProfile(c *gin.Context) {
	c.JSON(
		http.StatusOK,
		gin.H{},
	)
}

// PatchProfile updates the current profile.
func PatchProfile(c *gin.Context) {
	c.JSON(
		http.StatusOK,
		gin.H{},
	)
}
<commit_msg>Integrated profile show and update<commit_after>package controller

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/solderapp/solder/router/middleware/context"
	"github.com/solderapp/solder/router/middleware/session"
)

// GetProfile displays the current profile.
func GetProfile(c *gin.Context) {
	record := session.Current(c)

	c.JSON(
		http.StatusOK,
		record,
	)
}

// PatchProfile updates the current profile.
func PatchProfile(c *gin.Context) {
	record := session.Current(c)

	if err := c.BindJSON(&record); err != nil {
		c.JSON(
			http.StatusPreconditionFailed,
			gin.H{
				"status":  http.StatusPreconditionFailed,
				"message": "Failed to bind profile data",
			},
		)

		c.Abort()
		return
	}

	err := context.Store(c).Save(
		&record,
	).Error

	if err != nil {
		c.JSON(
			http.StatusBadRequest,
			gin.H{
				"status":  http.StatusBadRequest,
				"message": err.Error(),
			},
		)

		c.Abort()
		return
	}

	c.JSON(
		http.StatusOK,
		record,
	)
}
<|endoftext|>


<commit_before>package bot

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/nlopes/slack"
	"go.uber.org/zap"
)

var isImage = regexp.MustCompile("\\.(jpe?g|gif|png)$")
var CdnPath = ""
var CdnPrefix = ""

func fileBytesQuietly(f *slack.File) ([]byte, error) {
	req, _ := http.NewRequest(
		"GET",
		f.URLPrivate,
		nil)
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
	rsp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != 200 {
		buf, _ := httputil.DumpRequest(req, true)
		bufRsp, _ := httputil.DumpResponse(rsp, false)
		Logger.Info("Debugging File Request",
			zap.String("method", "old"),
			zap.ByteString("request", buf),
			zap.ByteString("response", bufRsp),
		)
		return nil, fmt.Errorf(rsp.Status)
	}
	if strings.Contains(rsp.Header.Get("Content-Type"), "text/html") {
		buf, _ := httputil.DumpRequest(req, true)
		bufRsp, _ := httputil.DumpResponse(rsp, false)
		Logger.Info("Debugging File Request",
			zap.String("method", "old"),
			zap.ByteString("request", buf),
			zap.ByteString("response", bufRsp),
		)
		return nil, fmt.Errorf("Expected non html content type, got %s", rsp.Header.Get("Content-Type"))
	}
	return ioutil.ReadAll(rsp.Body)
}

func fileBytesNoisy(f *slack.File) ([]byte, error) {
	for i := 0; i < 5; i++ {
		if _, _, _, err := rtm.ShareFilePublicURL(f.ID); err == nil {
			break
		} else {
			if i < 4 {
				sleepfor := time.Millisecond * time.Duration(((i+1)*(i+1))*100)
				Logger.Error(
					"Error making file public",
					zap.Error(err),
					zap.String("filename", f.Name),
					zap.String("fileid", f.ID),
					zap.Duration("sleepfor", sleepfor))
			} else {
				Logger.Error(
					"Error making file public: %s/%s: %s",
					zap.Error(err),
					zap.String("filename", f.Name),
					zap.String("fileid", f.ID))
				return nil, fmt.Errorf("Error making file public: %s/%s: %s", f.ID, f.Name, err.Error())
			}
		}

	}
	var pubSecret string
	pubParts := strings.Split(f.PermalinkPublic, "-")
	pubSecret = pubParts[len(pubParts)-1]
	req, _ := http.NewRequest(
		"GET",
		fmt.Sprintf(
			"%s?pub_secret=%s",
			f.URLPrivate,
			pubSecret,
		),
		nil)
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
	buf, _ := httputil.DumpRequest(req, true)
	Logger.Info("Debugging File Request", zap.ByteString("request", buf))
	rsp, err := http.DefaultClient.Do(req)
	bufRsp, _ := httputil.DumpResponse(rsp, false)
	Logger.Info("Debugging File Response", zap.ByteString("response", bufRsp))
	if err != nil {
		return nil, err
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != 200 {
		return nil, fmt.Errorf(rsp.Status)
	}
	if strings.Contains(rsp.Header.Get("Content-Type"), "text/html") {
		return nil, fmt.Errorf("Expected non html content type, got %s", rsp.Header.Get("Content-Type"))
	}
	return ioutil.ReadAll(rsp.Body)
}

func fileBytes(f *slack.File) ([]byte, error) {
	for i := 0; i < 5; i++ {
		if buf, err := fileBytesQuietly(f); err == nil {
			return buf, err
		}
		if i < 4 {
			sleepfor := time.Millisecond * time.Duration(((i+1)*(i+1))*100)
			time.Sleep(sleepfor)
		}

	}
	return fileBytesNoisy(f)
}

func handleChannelUpload(m *slack.MessageEvent) bool {
	if CdnPath == "" || CdnPrefix == "" {
		return false
	}
	if !m.Msg.Upload {
		return false
	}
	fmt.Fprintf(os.Stderr, "DEBUG: m: %#v\n", m)
	fmt.Fprintf(os.Stderr, "DEBUG: m.Files[0]: %#v\n", m.Files[0])
	Logger.Info("File upload detected", zap.String("username", m.Username), zap.String("filename", m.Files[0].Name))
	if buf, err := fileBytes(m.Msg.File); err != nil {
		Logger.Error(
			"error downloading file",
			zap.Error(err),
			zap.String("username", m.Username),
			zap.String("filename", m.Files[0].Name))
	} else {
		path := fmt.Sprintf("%s/%s", CdnPath, time.Now().Format("2006/01/02/15"))
		if err := os.MkdirAll(path, 0755); err != nil {
			Logger.Error(
				"error making cdn path",
				zap.String("path", path),
				zap.String("username", m.Username),
				zap.String("filename", m.Files[0].Name))
			return false
		}
		part := &url.URL{Path: m.Msg.File.Name}
		urlPath := fmt.Sprintf("%s/%s-%s", path, m.Msg.File.ID, part.String())
		path = fmt.Sprintf("%s/%s-%s", path, m.Msg.File.ID, m.Msg.File.Name)
		if fp, err := os.Create(path); err != nil {
			Logger.Error(
				"error creating cdn file",
				zap.String("path", path),
				zap.String("username", m.Username),
				zap.String("filename", m.Files[0].Name))
			return false
		} else {
			if _, err := fp.Write(buf); err != nil {
				fp.Close()
				Logger.Error(
					"error writing to cdn file",
					zap.String("path", path),
					zap.String("username", m.Username),
					zap.String("filename", m.Files[0].Name))
				return false
			}
			fp.Close()
			fileURL := CdnPrefix + urlPath[len(CdnPath):]
			rtm.DeleteFile(m.Msg.File.ID)
			if isImage.MatchString(strings.ToLower(m.Msg.File.Name)) {
				for i := 0; i < 5; i++ {
					_, _, err := rtm.PostMessage(
						m.Channel,
						"",
						slack.PostMessageParameters{
							Text:        "",
							AsUser:      true,
							UnfurlLinks: true,
							UnfurlMedia: true,
							IconEmoji:   ":paperclip:",
							Attachments: []slack.Attachment{
								slack.Attachment{
									Title:     fmt.Sprintf("%s uploaded %s", m.Msg.Username, m.Msg.File.Title),
									TitleLink: fileURL,
									ImageURL:  fileURL,
								},
							},
						})
					if err != nil {
						Logger.Error(
							"Failed postting cdn link back to slack",
							zap.String("username", m.Username),
							zap.String("filename", m.Files[0].Name),
							zap.String("url", fileURL))
					} else {
						break
					}
					time.Sleep(time.Second * time.Duration(i))
				}
			} else {
				rtm.SendMessage(&slack.OutgoingMessage{
					ID:      int(time.Now().UnixNano()),
					Channel: m.Channel,
					Text:    fmt.Sprintf("%s uploaded the file *%s*\n%s", m.Msg.Username, m.Msg.File.Title, fileURL),
					Type:    "message",
				})
			}
			Logger.Info("saved CDN file", zap.String("url", fileURL), zap.Int("size", len(buf)))
			return true
		}

	}
	return false
}

func handleDMUpload(m *slack.MessageEvent) bool {
	if CdnPath == "" || CdnPrefix == "" {
		return false
	}
	if !m.Msg.Upload {
		return false
	}
	if buf, err := fileBytes(m.Msg.File); err != nil {
		Logger.Info("error downloading file", zap.Error(err))
	} else {
		path := fmt.Sprintf("%s/%s", CdnPath, time.Now().Format("2006/01/02/15"))
		if err := os.MkdirAll(path, 0755); err != nil {
			Logger.Error("error making cdn path", zap.String("path", path))
			return false
		}
		part := &url.URL{Path: m.Msg.File.Name}
		urlPath := fmt.Sprintf("%s/%s-%s", path, m.Msg.File.ID, part.String())
		path = fmt.Sprintf("%s/%s-%s", path, m.Msg.File.ID, m.Msg.File.Name)
		if fp, err := os.Create(path); err != nil {
			Logger.Error("error creating cdn file", zap.String("path", path))
			return false
		} else {
			if _, err := fp.Write(buf); err != nil {
				fp.Close()
				Logger.Error("error writing to cdn file", zap.String("path", path))
				return false
			}
			fp.Close()
			fileURL := CdnPrefix + urlPath[len(CdnPath):]
			rtm.DeleteFile(m.Msg.File.ID)
			rtm.SendMessage(&slack.OutgoingMessage{
				ID:      int(time.Now().UnixNano()),
				Channel: m.Channel,
				Text:    fmt.Sprintf("Thanks for sending me the file instead of uploading it to a channel or group. You can paste the following link anywhere you want to show the file to others! ```%s```", fileURL),
				Type:    "message",
			})
			Logger.Info("saved CDN file", zap.String("url", fileURL), zap.Int("size", len(buf)))
		}

	}
	return true
}
<commit_msg>lots more file => files<commit_after>package bot

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/nlopes/slack"
	"go.uber.org/zap"
)

var isImage = regexp.MustCompile("\\.(jpe?g|gif|png)$")
var CdnPath = ""
var CdnPrefix = ""

func fileBytesQuietly(f *slack.File) ([]byte, error) {
	req, _ := http.NewRequest(
		"GET",
		f.URLPrivate,
		nil)
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
	rsp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != 200 {
		buf, _ := httputil.DumpRequest(req, true)
		bufRsp, _ := httputil.DumpResponse(rsp, false)
		Logger.Info("Debugging File Request",
			zap.String("method", "old"),
			zap.ByteString("request", buf),
			zap.ByteString("response", bufRsp),
		)
		return nil, fmt.Errorf(rsp.Status)
	}
	if strings.Contains(rsp.Header.Get("Content-Type"), "text/html") {
		buf, _ := httputil.DumpRequest(req, true)
		bufRsp, _ := httputil.DumpResponse(rsp, false)
		Logger.Info("Debugging File Request",
			zap.String("method", "old"),
			zap.ByteString("request", buf),
			zap.ByteString("response", bufRsp),
		)
		return nil, fmt.Errorf("Expected non html content type, got %s", rsp.Header.Get("Content-Type"))
	}
	return ioutil.ReadAll(rsp.Body)
}

func fileBytesNoisy(f *slack.File) ([]byte, error) {
	for i := 0; i < 5; i++ {
		if _, _, _, err := rtm.ShareFilePublicURL(f.ID); err == nil {
			break
		} else {
			if i < 4 {
				sleepfor := time.Millisecond * time.Duration(((i+1)*(i+1))*100)
				Logger.Error(
					"Error making file public",
					zap.Error(err),
					zap.String("filename", f.Name),
					zap.String("fileid", f.ID),
					zap.Duration("sleepfor", sleepfor))
			} else {
				Logger.Error(
					"Error making file public: %s/%s: %s",
					zap.Error(err),
					zap.String("filename", f.Name),
					zap.String("fileid", f.ID))
				return nil, fmt.Errorf("Error making file public: %s/%s: %s", f.ID, f.Name, err.Error())
			}
		}

	}
	var pubSecret string
	pubParts := strings.Split(f.PermalinkPublic, "-")
	pubSecret = pubParts[len(pubParts)-1]
	req, _ := http.NewRequest(
		"GET",
		fmt.Sprintf(
			"%s?pub_secret=%s",
			f.URLPrivate,
			pubSecret,
		),
		nil)
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
	buf, _ := httputil.DumpRequest(req, true)
	Logger.Info("Debugging File Request", zap.ByteString("request", buf))
	rsp, err := http.DefaultClient.Do(req)
	bufRsp, _ := httputil.DumpResponse(rsp, false)
	Logger.Info("Debugging File Response", zap.ByteString("response", bufRsp))
	if err != nil {
		return nil, err
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != 200 {
		return nil, fmt.Errorf(rsp.Status)
	}
	if strings.Contains(rsp.Header.Get("Content-Type"), "text/html") {
		return nil, fmt.Errorf("Expected non html content type, got %s", rsp.Header.Get("Content-Type"))
	}
	return ioutil.ReadAll(rsp.Body)
}

func fileBytes(f *slack.File) ([]byte, error) {
	for i := 0; i < 5; i++ {
		if buf, err := fileBytesQuietly(f); err == nil {
			return buf, err
		}
		if i < 4 {
			sleepfor := time.Millisecond * time.Duration(((i+1)*(i+1))*100)
			time.Sleep(sleepfor)
		}

	}
	return fileBytesNoisy(f)
}

func handleChannelUpload(m *slack.MessageEvent) bool {
	if CdnPath == "" || CdnPrefix == "" {
		return false
	}
	if !m.Msg.Upload {
		return false
	}
	fmt.Fprintf(os.Stderr, "DEBUG: m: %#v\n", m)
	fmt.Fprintf(os.Stderr, "DEBUG: m.Files[0]: %#v\n", m.Files[0])
	Logger.Info("File upload detected", zap.String("username", m.Username), zap.String("filename", m.Files[0].Name))
	if buf, err := fileBytes(m.Files[0]); err != nil {
		Logger.Error(
			"error downloading file",
			zap.Error(err),
			zap.String("username", m.Username),
			zap.String("filename", m.Files[0].Name))
	} else {
		path := fmt.Sprintf("%s/%s", CdnPath, time.Now().Format("2006/01/02/15"))
		if err := os.MkdirAll(path, 0755); err != nil {
			Logger.Error(
				"error making cdn path",
				zap.String("path", path),
				zap.String("username", m.Username),
				zap.String("filename", m.Files[0].Name))
			return false
		}
		part := &url.URL{Path: m.Files[0].Name}
		urlPath := fmt.Sprintf("%s/%s-%s", path, m.Files[0].ID, part.String())
		path = fmt.Sprintf("%s/%s-%s", path, m.Files[0].ID, m.Files[0].Name)
		if fp, err := os.Create(path); err != nil {
			Logger.Error(
				"error creating cdn file",
				zap.String("path", path),
				zap.String("username", m.Username),
				zap.String("filename", m.Files[0].Name))
			return false
		} else {
			if _, err := fp.Write(buf); err != nil {
				fp.Close()
				Logger.Error(
					"error writing to cdn file",
					zap.String("path", path),
					zap.String("username", m.Username),
					zap.String("filename", m.Files[0].Name))
				return false
			}
			fp.Close()
			fileURL := CdnPrefix + urlPath[len(CdnPath):]
			rtm.DeleteFile(m.Files[0].ID)
			if isImage.MatchString(strings.ToLower(m.Files[0].Name)) {
				for i := 0; i < 5; i++ {
					_, _, err := rtm.PostMessage(
						m.Channel,
						"",
						slack.PostMessageParameters{
							Text:        "",
							AsUser:      true,
							UnfurlLinks: true,
							UnfurlMedia: true,
							IconEmoji:   ":paperclip:",
							Attachments: []slack.Attachment{
								slack.Attachment{
									Title:     fmt.Sprintf("%s uploaded %s", m.Msg.Username, m.Files[0].Title),
									TitleLink: fileURL,
									ImageURL:  fileURL,
								},
							},
						})
					if err != nil {
						Logger.Error(
							"Failed postting cdn link back to slack",
							zap.String("username", m.Username),
							zap.String("filename", m.Files[0].Name),
							zap.String("url", fileURL))
					} else {
						break
					}
					time.Sleep(time.Second * time.Duration(i))
				}
			} else {
				rtm.SendMessage(&slack.OutgoingMessage{
					ID:      int(time.Now().UnixNano()),
					Channel: m.Channel,
					Text:    fmt.Sprintf("%s uploaded the file *%s*\n%s", m.Msg.Username, m.Files[0].Title, fileURL),
					Type:    "message",
				})
			}
			Logger.Info("saved CDN file", zap.String("url", fileURL), zap.Int("size", len(buf)))
			return true
		}

	}
	return false
}

func handleDMUpload(m *slack.MessageEvent) bool {
	if CdnPath == "" || CdnPrefix == "" {
		return false
	}
	if !m.Msg.Upload {
		return false
	}
	if buf, err := fileBytes(m.Files[0]); err != nil {
		Logger.Info("error downloading file", zap.Error(err))
	} else {
		path := fmt.Sprintf("%s/%s", CdnPath, time.Now().Format("2006/01/02/15"))
		if err := os.MkdirAll(path, 0755); err != nil {
			Logger.Error("error making cdn path", zap.String("path", path))
			return false
		}
		part := &url.URL{Path: m.Files[0].Name}
		urlPath := fmt.Sprintf("%s/%s-%s", path, m.Files[0].ID, part.String())
		path = fmt.Sprintf("%s/%s-%s", path, m.Files[0].ID, m.Files[0].Name)
		if fp, err := os.Create(path); err != nil {
			Logger.Error("error creating cdn file", zap.String("path", path))
			return false
		} else {
			if _, err := fp.Write(buf); err != nil {
				fp.Close()
				Logger.Error("error writing to cdn file", zap.String("path", path))
				return false
			}
			fp.Close()
			fileURL := CdnPrefix + urlPath[len(CdnPath):]
			rtm.DeleteFile(m.Files[0].ID)
			rtm.SendMessage(&slack.OutgoingMessage{
				ID:      int(time.Now().UnixNano()),
				Channel: m.Channel,
				Text:    fmt.Sprintf("Thanks for sending me the file instead of uploading it to a channel or group. You can paste the following link anywhere you want to show the file to others! ```%s```", fileURL),
				Type:    "message",
			})
			Logger.Info("saved CDN file", zap.String("url", fileURL), zap.Int("size", len(buf)))
		}

	}
	return true
}
<|endoftext|>


<commit_before>package controllers

import (
	"encoding/json"
	//"fmt"
	"github.com/julienschmidt/httprouter"
	"log"
	"moviesapp/models"
	"moviesapp/token"
	"net/http"
)

type errOut struct {
	Error string `json:error`
}

func GetMovies(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	/*	authToken, err := token.ExtractToken(r)

		if err != nil {
			errout := new(errOut)
			errout.Error = err.Error()
			w.WriteHeader(http.StatusBadRequest)
			if err := json.NewEncoder(w).Encode(errout); err != nil {
				log.Panic("Error EncodingJson in ControllersGetMovies", err)
			}
		} else {
			tokenStatus, err := token.ParseToken(authToken)
			if err != nil || tokenStatus == false {
				errout := new(errOut)
				errout.Error = err.Error()
				w.WriteHeader(http.StatusForbidden)
				if err := json.NewEncoder(w).Encode(errout); err != nil {
					log.Panic("Error EncodingJson in ControllersGetMovies", err)
				}
				log.Println("token status err: ", err)

			} else {

				//w.Header().Set("Access-Control-Allow-Origin", "*")
				if err := json.NewEncoder(w).Encode(models.GetMovies()); err != nil {
					log.Panic("Error EncodingJson in ControllersGetMovies", err)
				}
			}
		}*/
	status := token.TokenHandler(w, r)
	if status == true {
		if err := json.NewEncoder(w).Encode(models.GetMovies()); err != nil {
			log.Panic("Error EncodingJson in ControllersGetMovies", err)
		}
	}
}

func NewMovie(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	//w.Header().Set("Access-Control-Allow-Origin", "*")
	if err := json.NewEncoder(w).Encode(models.NewMovie(r)); err != nil {
		log.Panic("Error EncodingJson in ControllersNewMovie", err)
	}
}

func GetMovieById(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	//w.Header().Set("Access-Control-Allow-Origin", "*")
	if err := json.NewEncoder(w).Encode(models.GetMovieById(p.ByName("id"))); err != nil {
		log.Panic("Controller GetMovieById json err: ", err)
	}
}

func UpdateMovie(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	if err := json.NewEncoder(w).Encode(models.UpdateMovie(r, p.ByName("id"))); err != nil {
		log.Panic("Controller UpdateMovieById json err: ", err)
	}
}

func DeleteMovie(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	//w.Header().Set("Access-Control-Allow-Origin", "*")
	if err := json.NewEncoder(w).Encode(models.DeleteMovie(p.ByName("id"))); err != nil {
		log.Panic("Controller UpdateMovieById json err: ", err)
	}
}
<commit_msg>refactoring complete<commit_after>package controllers

import (
	"encoding/json"
	//"fmt"
	"github.com/julienschmidt/httprouter"
	"log"
	"moviesapp/models"
	"moviesapp/token"
	"net/http"
)

type errOut struct {
	Error string `json:error`
}

func GetMovies(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")

	if err := json.NewEncoder(w).Encode(models.GetMovies()); err != nil {
		log.Panic("Error EncodingJson in ControllersGetMovies", err)

	}
}

func NewMovie(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	//w.Header().Set("Access-Control-Allow-Origin", "*")
	status := token.TokenHandler(w, r)
	if status == true {
		if err := json.NewEncoder(w).Encode(models.NewMovie(r)); err != nil {
			log.Panic("Error EncodingJson in ControllersNewMovie", err)
		}
	}
}

func GetMovieById(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	//w.Header().Set("Access-Control-Allow-Origin", "*")
	if err := json.NewEncoder(w).Encode(models.GetMovieById(p.ByName("id"))); err != nil {
		log.Panic("Controller GetMovieById json err: ", err)
	}
}

func UpdateMovie(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	status := token.TokenHandler(w, r)
	if status == true {
		if err := json.NewEncoder(w).Encode(models.UpdateMovie(r, p.ByName("id"))); err != nil {
			log.Panic("Controller UpdateMovieById json err: ", err)
		}
	}
}

func DeleteMovie(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	//w.Header().Set("Access-Control-Allow-Origin", "*")
	status := token.TokenHandler(w, r)
	if status == true {
		if err := json.NewEncoder(w).Encode(models.DeleteMovie(p.ByName("id"))); err != nil {
			log.Panic("Controller UpdateMovieById json err: ", err)
		}
	}
}
<|endoftext|>


<commit_before>package autotls

import (
	"crypto/tls"
	"net/http"

	"golang.org/x/crypto/acme/autocert"
)

// Run support 1-line LetsEncrypt HTTPS servers
func Run(r http.Handler, domain ...string) error {
	return http.Serve(autocert.NewListener(domain...), r)
}

// RunWithManager support custom autocert manager
func RunWithManager(r http.Handler, m *autocert.Manager) error {
	s := &http.Server{
		Addr:      ":https",
		TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
		Handler:   r,
	}

	return s.ListenAndServeTLS("", "")
}
<commit_msg>fix: autocert package. (#6)<commit_after>package autotls

import (
	"crypto/tls"
	"net/http"

	"golang.org/x/crypto/acme/autocert"
)

// Run support 1-line LetsEncrypt HTTPS servers
func Run(r http.Handler, domain ...string) error {
	return http.Serve(autocert.NewListener(domain...), r)
}

// RunWithManager support custom autocert manager
func RunWithManager(r http.Handler, m *autocert.Manager) error {
	s := &http.Server{
		Addr:      ":https",
		TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
		Handler:   r,
	}

	go http.ListenAndServe(":http", m.HTTPHandler(nil))

	return s.ListenAndServeTLS("", "")
}
<|endoftext|>


<commit_before>package aws

import "github.com/tatsuyafw/amc/util"

type ec2 struct {
	query string
}

var queries = map[string]string{
	"instances": "Instances",
}

func (a ec2) URL() string {
	b := "REGION.console.aws.amazon.com/ec2/v2/home?REGION&region=REGION"
	if a.query != "" {
		b += "#" + queries[a.query]
	}
	return url(b)
}

func (a ec2) Validate() bool {
	if a.query == "" {
		return true
	}
	return util.IncludeStr(util.KeysStr(queries), a.query)
}
<commit_msg>Add queries to ec2<commit_after>package aws

import "github.com/tatsuyafw/amc/util"

type ec2 struct {
	query string
}

var queries = map[string]string{
	"addresses":       "Addresses",
	"events":          "Events",
	"images":          "Images",
	"instances":       "Instances",
	"limits":          "Limits",
	"reports":         "Reports",
	"security-groups": "SecurityGroups",
	"snapshots":       "Snapshots",
	"tags":            "Tags",
	"volumes":         "Volumes",
}

func (a ec2) URL() string {
	b := "REGION.console.aws.amazon.com/ec2/v2/home?REGION&region=REGION"
	if a.query != "" {
		b += "#" + queries[a.query]
	}
	return url(b)
}

func (a ec2) Validate() bool {
	if a.query == "" {
		return true
	}
	return util.IncludeStr(util.KeysStr(queries), a.query)
}
<|endoftext|>