<commit_before>// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// syz-ci is a continuous fuzzing system for syzkaller.
// It runs several syz-manager's, polls and rebuilds images for managers
// and polls and rebuilds syzkaller binaries.
// For usage instructions see: docs/ci.md
package main

// Implementation details:
//
// 2 main components:
//  - SyzUpdater: handles syzkaller updates
//  - Manager: handles kernel build and syz-manager process (one per manager)
// Both operate in a similar way and keep 2 builds:
//  - latest: latest known good build (i.e. we tested it)
//    preserved across restarts/reboots, i.e. we can start fuzzing even when
//    current syzkaller/kernel git head is broken, or git is down, or anything else
//  - current: currently used build (a copy of one of the latest builds)
// Other important points:
//  - syz-ci is always built on the same revision as the rest of syzkaller binaries,
//    this allows us to handle e.g. changes in manager config format.
//  - consequently, syzkaller binaries are never updated on-the-fly,
//    instead we re-exec and then update
//  - we understand when the latest build is fresh even after reboot,
//    i.e. we store enough information to identify it (git hash, compiler identity, etc),
//    so we don't rebuild unnecessary (kernel builds take time)
//  - we generally avoid crashing the process and handle all errors gracefully
//    (this is a continuous system), except for some severe/user errors during start
//    (e.g. bad config file, or can't create necessary dirs)
//
// Directory/file structure:
// syz-ci			: current executable
// syz-ci.tag			: tag of the current executable (syzkaller git hash)
// syzkaller/
//	latest/			: latest good syzkaller build
//	current/		: syzkaller build currently in use
// managers/
//	manager1/		: one dir per manager
//		kernel/		: kernel checkout
//		workdir/	: manager workdir (never deleted)
//		latest/		: latest good kernel image build
//		current/	: kernel image currently in use
// jobs/
//	linux/			: one dir per target OS
//		kernel/		: kernel checkout
//		image/		: currently used image
//		workdir/	: some temp files
//
// Current executable, syzkaller and kernel builds are marked with tag files.
// Tag files uniquely identify the build (git hash, compiler identity, kernel config, etc).
// For tag files both contents and modification time are important,
// modification time allows us to understand if we need to rebuild after a restart.

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"sync"

	"github.com/google/syzkaller/pkg/config"
	. "github.com/google/syzkaller/pkg/log"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/syz-manager/mgrconfig"
)

var flagConfig = flag.String("config", "", "config file")

type Config struct {
	Name                   string
	Http                   string
	Dashboard_Addr         string // Optional.
	Dashboard_Client       string // Optional.
	Dashboard_Key          string // Optional.
	Hub_Addr               string // Optional.
	Hub_Key                string // Optional.
	Goroot                 string // Go 1.8+ toolchain dir.
	Syzkaller_Repo         string
	Syzkaller_Branch       string
	Syzkaller_Descriptions string // Dir with additional syscall descriptions (.txt and .const files).
	Managers               []*ManagerConfig
}

type ManagerConfig struct {
	Name             string
	Dashboard_Client string
	Dashboard_Key    string
	Repo             string
	Repo_Alias       string // Short name of the repo (e.g. "linux-next"), used only for reporting.
	Branch           string
	Compiler         string
	Userspace        string
	Kernel_Config    string
	Kernel_Cmdline   string // File with kernel cmdline values (optional).
	Kernel_Sysctl    string // File with sysctl values (e.g. output of sysctl -a, optional).
	Manager_Config   json.RawMessage
}

func main() {
	flag.Parse()
	EnableLogCaching(1000, 1<<20)
	cfg, err := loadConfig(*flagConfig)
	if err != nil {
		Fatalf("failed to load config: %v", err)
	}

	shutdownPending := make(chan struct{})
	osutil.HandleInterrupts(shutdownPending)

	updater := NewSyzUpdater(cfg)
	updater.UpdateOnStart(shutdownPending)
	updatePending := make(chan struct{})
	go func() {
		updater.WaitForUpdate()
		close(updatePending)
	}()

	var wg sync.WaitGroup
	wg.Add(1)
	stop := make(chan struct{})
	go func() {
		select {
		case <-shutdownPending:
		case <-updatePending:
		}
		close(stop)
		wg.Done()
	}()

	managers := make([]*Manager, len(cfg.Managers))
	for i, mgrcfg := range cfg.Managers {
		managers[i] = createManager(cfg, mgrcfg, stop)
	}
	jp := newJobProcessor(cfg, managers)
	for _, mgr := range managers {
		mgr := mgr
		wg.Add(1)
		go func() {
			defer wg.Done()
			mgr.loop()
		}()
	}
	wg.Add(1)
	go func() {
		defer wg.Done()
		jp.loop(stop)
	}()

	wg.Wait()

	select {
	case <-shutdownPending:
	case <-updatePending:
		updater.UpdateAndRestart()
	}
}

func loadConfig(filename string) (*Config, error) {
	cfg := &Config{
		Syzkaller_Repo:   "https://github.com/google/syzkaller.git",
		Syzkaller_Branch: "master",
		Goroot:           os.Getenv("GOROOT"),
	}
	if err := config.LoadFile(filename, cfg); err != nil {
		return nil, err
	}
	if cfg.Name == "" {
		return nil, fmt.Errorf("param 'name' is empty")
	}
	if cfg.Http == "" {
		return nil, fmt.Errorf("param 'http' is empty")
	}
	if len(cfg.Managers) == 0 {
		return nil, fmt.Errorf("no managers specified")
	}
	for i, mgr := range cfg.Managers {
		if mgr.Name == "" {
			return nil, fmt.Errorf("param 'managers[%v].name' is empty", i)
		}
		mgrcfg := new(mgrconfig.Config)
		if err := config.LoadData(mgr.Manager_Config, mgrcfg); err != nil {
			return nil, fmt.Errorf("manager %v: %v", mgr.Name, err)
		}
	}
	return cfg, nil
}
<commit_msg>syz-ci: add config parameter that enables jobs<commit_after>// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// syz-ci is a continuous fuzzing system for syzkaller.
// It runs several syz-manager's, polls and rebuilds images for managers
// and polls and rebuilds syzkaller binaries.
// For usage instructions see: docs/ci.md
package main

// Implementation details:
//
// 2 main components:
//  - SyzUpdater: handles syzkaller updates
//  - Manager: handles kernel build and syz-manager process (one per manager)
// Both operate in a similar way and keep 2 builds:
//  - latest: latest known good build (i.e. we tested it)
//    preserved across restarts/reboots, i.e. we can start fuzzing even when
//    current syzkaller/kernel git head is broken, or git is down, or anything else
//  - current: currently used build (a copy of one of the latest builds)
// Other important points:
//  - syz-ci is always built on the same revision as the rest of syzkaller binaries,
//    this allows us to handle e.g. changes in manager config format.
//  - consequently, syzkaller binaries are never updated on-the-fly,
//    instead we re-exec and then update
//  - we understand when the latest build is fresh even after reboot,
//    i.e. we store enough information to identify it (git hash, compiler identity, etc),
//    so we don't rebuild unnecessary (kernel builds take time)
//  - we generally avoid crashing the process and handle all errors gracefully
//    (this is a continuous system), except for some severe/user errors during start
//    (e.g. bad config file, or can't create necessary dirs)
//
// Directory/file structure:
// syz-ci			: current executable
// syz-ci.tag			: tag of the current executable (syzkaller git hash)
// syzkaller/
//	latest/			: latest good syzkaller build
//	current/		: syzkaller build currently in use
// managers/
//	manager1/		: one dir per manager
//		kernel/		: kernel checkout
//		workdir/	: manager workdir (never deleted)
//		latest/		: latest good kernel image build
//		current/	: kernel image currently in use
// jobs/
//	linux/			: one dir per target OS
//		kernel/		: kernel checkout
//		image/		: currently used image
//		workdir/	: some temp files
//
// Current executable, syzkaller and kernel builds are marked with tag files.
// Tag files uniquely identify the build (git hash, compiler identity, kernel config, etc).
// For tag files both contents and modification time are important,
// modification time allows us to understand if we need to rebuild after a restart.

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"sync"

	"github.com/google/syzkaller/pkg/config"
	. "github.com/google/syzkaller/pkg/log"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/syz-manager/mgrconfig"
)

var flagConfig = flag.String("config", "", "config file")

type Config struct {
	Name                   string
	Http                   string
	Dashboard_Addr         string // Optional.
	Dashboard_Client       string // Optional.
	Dashboard_Key          string // Optional.
	Hub_Addr               string // Optional.
	Hub_Key                string // Optional.
	Goroot                 string // Go 1.8+ toolchain dir.
	Syzkaller_Repo         string
	Syzkaller_Branch       string
	Syzkaller_Descriptions string // Dir with additional syscall descriptions (.txt and .const files).
	Enable_Jobs            bool   // Enable patch testing jobs.
	Managers               []*ManagerConfig
}

type ManagerConfig struct {
	Name             string
	Dashboard_Client string
	Dashboard_Key    string
	Repo             string
	Repo_Alias       string // Short name of the repo (e.g. "linux-next"), used only for reporting.
	Branch           string
	Compiler         string
	Userspace        string
	Kernel_Config    string
	Kernel_Cmdline   string // File with kernel cmdline values (optional).
	Kernel_Sysctl    string // File with sysctl values (e.g. output of sysctl -a, optional).
	Manager_Config   json.RawMessage
}

func main() {
	flag.Parse()
	EnableLogCaching(1000, 1<<20)
	cfg, err := loadConfig(*flagConfig)
	if err != nil {
		Fatalf("failed to load config: %v", err)
	}

	shutdownPending := make(chan struct{})
	osutil.HandleInterrupts(shutdownPending)

	updater := NewSyzUpdater(cfg)
	updater.UpdateOnStart(shutdownPending)
	updatePending := make(chan struct{})
	go func() {
		updater.WaitForUpdate()
		close(updatePending)
	}()

	var wg sync.WaitGroup
	wg.Add(1)
	stop := make(chan struct{})
	go func() {
		select {
		case <-shutdownPending:
		case <-updatePending:
		}
		close(stop)
		wg.Done()
	}()

	managers := make([]*Manager, len(cfg.Managers))
	for i, mgrcfg := range cfg.Managers {
		managers[i] = createManager(cfg, mgrcfg, stop)
	}
	for _, mgr := range managers {
		mgr := mgr
		wg.Add(1)
		go func() {
			defer wg.Done()
			mgr.loop()
		}()
	}
	if cfg.Enable_Jobs {
		jp := newJobProcessor(cfg, managers)
		wg.Add(1)
		go func() {
			defer wg.Done()
			jp.loop(stop)
		}()
	}

	wg.Wait()

	select {
	case <-shutdownPending:
	case <-updatePending:
		updater.UpdateAndRestart()
	}
}

func loadConfig(filename string) (*Config, error) {
	cfg := &Config{
		Syzkaller_Repo:   "https://github.com/google/syzkaller.git",
		Syzkaller_Branch: "master",
		Goroot:           os.Getenv("GOROOT"),
	}
	if err := config.LoadFile(filename, cfg); err != nil {
		return nil, err
	}
	if cfg.Name == "" {
		return nil, fmt.Errorf("param 'name' is empty")
	}
	if cfg.Http == "" {
		return nil, fmt.Errorf("param 'http' is empty")
	}
	if len(cfg.Managers) == 0 {
		return nil, fmt.Errorf("no managers specified")
	}
	for i, mgr := range cfg.Managers {
		if mgr.Name == "" {
			return nil, fmt.Errorf("param 'managers[%v].name' is empty", i)
		}
		mgrcfg := new(mgrconfig.Config)
		if err := config.LoadData(mgr.Manager_Config, mgrcfg); err != nil {
			return nil, fmt.Errorf("manager %v: %v", mgr.Name, err)
		}
	}
	return cfg, nil
}
<|endoftext|>


<commit_before>//  Copyright (c) 2014 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the
//  License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing,
//  software distributed under the License is distributed on an "AS
//  IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//  express or implied. See the License for the specific language
//  governing permissions and limitations under the License.

package main

import (
	"fmt"
	"io"
	"sync"
)

type MsgRing struct {
	m     sync.Mutex
	inner io.Writer
	Next  int      `json:"next"`
	Msgs  [][]byte `json:"msgs"`
}

func NewMsgRing(inner io.Writer, ringSize int) (*MsgRing, error) {
	if inner == nil {
		return nil, fmt.Errorf("need a non-nil inner io.Writer param")
	}
	if ringSize <= 0 {
		return nil, fmt.Errorf("need a positive ring size")
	}
	return &MsgRing{
		inner: inner,
		Next:  0,
		Msgs:  make([][]byte, ringSize),
	}, nil
}

// Implements the io.Writer interface.
func (m *MsgRing) Write(p []byte) (n int, err error) {
	m.m.Lock()

	m.Msgs[m.Next] = p
	m.Next += 1
	if m.Next >= len(m.Msgs) {
		m.Next = 0
	}

	m.m.Unlock()

	return m.inner.Write(p)
}

func (m *MsgRing) Messages() [][]byte {
	rv := make([][]byte, 0, len(m.Msgs))

	m.m.Lock()
	defer m.m.Unlock()

	n := len(m.Msgs)
	i := 0
	idx := m.Next
	for i < n {
		if msg := m.Msgs[idx]; msg != nil {
			rv = append(rv, msg)
		}
		idx += 1
		if idx >= n {
			idx = 0
		}
		i += 1
	}

	return rv
}
<commit_msg>copy log bytes being kept around in the ring buffer<commit_after>//  Copyright (c) 2014 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the
//  License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing,
//  software distributed under the License is distributed on an "AS
//  IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//  express or implied. See the License for the specific language
//  governing permissions and limitations under the License.

package main

import (
	"fmt"
	"io"
	"sync"
)

type MsgRing struct {
	m     sync.Mutex
	inner io.Writer
	Next  int      `json:"next"`
	Msgs  [][]byte `json:"msgs"`
}

func NewMsgRing(inner io.Writer, ringSize int) (*MsgRing, error) {
	if inner == nil {
		return nil, fmt.Errorf("need a non-nil inner io.Writer param")
	}
	if ringSize <= 0 {
		return nil, fmt.Errorf("need a positive ring size")
	}
	return &MsgRing{
		inner: inner,
		Next:  0,
		Msgs:  make([][]byte, ringSize),
	}, nil
}

// Implements the io.Writer interface.
func (m *MsgRing) Write(p []byte) (n int, err error) {
	m.m.Lock()

	cp := make([]byte, len(p))
	copy(cp, p)
	m.Msgs[m.Next] = cp
	m.Next += 1
	if m.Next >= len(m.Msgs) {
		m.Next = 0
	}

	m.m.Unlock()

	return m.inner.Write(p)
}

func (m *MsgRing) Messages() [][]byte {
	rv := make([][]byte, 0, len(m.Msgs))

	m.m.Lock()
	defer m.m.Unlock()

	n := len(m.Msgs)
	i := 0
	idx := m.Next
	for i < n {
		if msg := m.Msgs[idx]; msg != nil {
			rv = append(rv, msg)
		}
		idx += 1
		if idx >= n {
			idx = 0
		}
		i += 1
	}
	return rv
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"strings"

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

// Ctx - context for bot command function (users, command, args, ...)
type Ctx struct {
	bot         *tgbotapi.BotAPI
	appConfig   Config
	commands    Commands
	users       Users
	userID      int
	allowExec   bool
	messageCmd  string
	messageArgs string
}

// authorize users /auth and /authroot
func cmdAuth(ctx Ctx) (replayMsg string) {
	forRoot := ctx.messageCmd == "/authroot"

	if ctx.messageArgs == "" {

		replayMsg = "See code in terminal with shell2telegram or ask code from root user and type:\n" + ctx.messageCmd + " code"
		authCode := ctx.users.DoLogin(ctx.userID, forRoot)

		rootRoleStr := ""
		if forRoot {
			rootRoleStr = "root "
		}
		secretCodeMsg := fmt.Sprintf("Request %saccess for %s. Code: %s\n", rootRoleStr, ctx.users.String(ctx.userID), authCode)
		fmt.Print(secretCodeMsg)
		ctx.users.broadcastForRoots(ctx.bot, secretCodeMsg)

	} else {
		if ctx.users.IsValidCode(ctx.userID, ctx.messageArgs, forRoot) {
			ctx.users.list[ctx.userID].IsAuthorized = true
			if forRoot {
				ctx.users.list[ctx.userID].IsRoot = true
				replayMsg = fmt.Sprintf("You (%s) authorized as root.", ctx.users.String(ctx.userID))
				log.Print("root authorized: ", ctx.users.String(ctx.userID))
			} else {
				replayMsg = fmt.Sprintf("You (%s) authorized.", ctx.users.String(ctx.userID))
				log.Print("authorized: ", ctx.users.String(ctx.userID))
			}
		} else {
			replayMsg = fmt.Sprintf("Code is not valid.")
		}
	}

	return replayMsg
}

// /help
func cmdHelp(ctx Ctx) (replayMsg string) {
	helpMsg := []string{
		"/auth [code] → authorize user",
		"/authroot [code] → authorize user as root",
	}

	if ctx.allowExec {
		for cmd, shellCmd := range ctx.commands {
			helpMsg = append(helpMsg, cmd+" → "+shellCmd)
		}
		if ctx.users.IsRoot(ctx.userID) {
			helpMsg = append(helpMsg, "/shell2telegram stat → get stat about users")
			if ctx.appConfig.addExit {
				helpMsg = append(helpMsg, "/shell2telegram exit → terminate bot")
			}
		}
	}

	helpMsg = append(helpMsg, "/shell2telegram version → show version")
	replayMsg = "This bot created with shell2telegram\n\n" +
		"available commands:\n" +
		strings.Join(helpMsg, "\n")

	return replayMsg
}

// /shell2telegram stat
func cmdShell2telegramStat(ctx Ctx) (replayMsg string) {
	for userID, user := range ctx.users.list {
		replayMsg += fmt.Sprintf("%s: auth: %v, root: %v, count: %d, last: %v\n",
			ctx.users.String(userID),
			user.IsAuthorized,
			user.IsRoot,
			user.Counter,
			user.LastAccessTime.Format("2006-01-02 15:04:05"),
		)
	}

	return replayMsg
}

// all commands from command-line
func cmdUser(ctx Ctx) (replayMsg string) {
	if cmd, found := ctx.commands[ctx.messageCmd]; found {

		shell, params := "sh", []string{"-c", cmd}
		osExecCommand := exec.Command(shell, params...)
		osExecCommand.Stderr = os.Stderr

		// write all arguments to STDIN
		if ctx.messageArgs != "" {
			stdin, err := osExecCommand.StdinPipe()
			if err == nil {
				io.WriteString(stdin, ctx.messageArgs)
				stdin.Close()
			} else {
				log.Print("get STDIN error: ", err)
			}
		}

		shellOut, err := osExecCommand.Output()
		if err != nil {
			log.Print("exec error: ", err)
			replayMsg = fmt.Sprintf("exec error: %s", err)
		} else {
			replayMsg = string(shellOut)
		}
	}

	return replayMsg
}
<commit_msg>Updated docs<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"strings"

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

// Ctx - context for bot command function (users, command, args, ...)
type Ctx struct {
	bot         *tgbotapi.BotAPI
	appConfig   Config   // configuration
	commands    Commands // all chat commands
	users       Users    // all users
	userID      int      // current user
	allowExec   bool     // is user authorized
	messageCmd  string   // command name
	messageArgs string   // command arguments
}

// /auth and /authroot - authorize users
func cmdAuth(ctx Ctx) (replayMsg string) {
	forRoot := ctx.messageCmd == "/authroot"

	if ctx.messageArgs == "" {

		replayMsg = "See code in terminal with shell2telegram or ask code from root user and type:\n" + ctx.messageCmd + " code"
		authCode := ctx.users.DoLogin(ctx.userID, forRoot)

		rootRoleStr := ""
		if forRoot {
			rootRoleStr = "root "
		}
		secretCodeMsg := fmt.Sprintf("Request %saccess for %s. Code: %s\n", rootRoleStr, ctx.users.String(ctx.userID), authCode)
		fmt.Print(secretCodeMsg)
		ctx.users.broadcastForRoots(ctx.bot, secretCodeMsg)

	} else {
		if ctx.users.IsValidCode(ctx.userID, ctx.messageArgs, forRoot) {
			ctx.users.list[ctx.userID].IsAuthorized = true
			if forRoot {
				ctx.users.list[ctx.userID].IsRoot = true
				replayMsg = fmt.Sprintf("You (%s) authorized as root.", ctx.users.String(ctx.userID))
				log.Print("root authorized: ", ctx.users.String(ctx.userID))
			} else {
				replayMsg = fmt.Sprintf("You (%s) authorized.", ctx.users.String(ctx.userID))
				log.Print("authorized: ", ctx.users.String(ctx.userID))
			}
		} else {
			replayMsg = fmt.Sprintf("Code is not valid.")
		}
	}

	return replayMsg
}

// /help
func cmdHelp(ctx Ctx) (replayMsg string) {
	helpMsg := []string{
		"/auth [code] → authorize user",
		"/authroot [code] → authorize user as root",
	}

	if ctx.allowExec {
		for cmd, shellCmd := range ctx.commands {
			helpMsg = append(helpMsg, cmd+" → "+shellCmd)
		}
		if ctx.users.IsRoot(ctx.userID) {
			helpMsg = append(helpMsg, "/shell2telegram stat → get stat about users")
			if ctx.appConfig.addExit {
				helpMsg = append(helpMsg, "/shell2telegram exit → terminate bot")
			}
		}
	}

	helpMsg = append(helpMsg, "/shell2telegram version → show version")
	replayMsg = "This bot created with shell2telegram\n\n" +
		"available commands:\n" +
		strings.Join(helpMsg, "\n")

	return replayMsg
}

// /shell2telegram stat
func cmdShell2telegramStat(ctx Ctx) (replayMsg string) {
	for userID, user := range ctx.users.list {
		replayMsg += fmt.Sprintf("%s: auth: %v, root: %v, count: %d, last: %v\n",
			ctx.users.String(userID),
			user.IsAuthorized,
			user.IsRoot,
			user.Counter,
			user.LastAccessTime.Format("2006-01-02 15:04:05"),
		)
	}

	return replayMsg
}

// all commands from command-line
func cmdUser(ctx Ctx) (replayMsg string) {
	if cmd, found := ctx.commands[ctx.messageCmd]; found {

		shell, params := "sh", []string{"-c", cmd}
		osExecCommand := exec.Command(shell, params...)
		osExecCommand.Stderr = os.Stderr

		// write all arguments to STDIN
		if ctx.messageArgs != "" {
			stdin, err := osExecCommand.StdinPipe()
			if err == nil {
				io.WriteString(stdin, ctx.messageArgs)
				stdin.Close()
			} else {
				log.Print("get STDIN error: ", err)
			}
		}

		shellOut, err := osExecCommand.Output()
		if err != nil {
			log.Print("exec error: ", err)
			replayMsg = fmt.Sprintf("exec error: %s", err)
		} else {
			replayMsg = string(shellOut)
		}
	}

	return replayMsg
}
<|endoftext|>


<commit_before>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package handlers

import (
	"bufio"
	"compress/flate"
	"compress/gzip"
	"io"
	"net"
	"net/http"
	"strings"
)

// 同时实现了http.ResponseWriter和http.Hijacker接口。
type compressWriter struct {
	gzw io.Writer
	rw  http.ResponseWriter
	hj  http.Hijacker
}

func (cw *compressWriter) Write(bs []byte) (int, error) {
	h := cw.rw.Header()
	if h.Get("Content-Type") == "" {
		h.Set("Content-Type", http.DetectContentType(bs))
	}

	return cw.gzw.Write(bs)
}

func (cw *compressWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	return cw.hj.Hijack()
}

func (cw *compressWriter) Header() http.Header {
	return cw.rw.Header()
}

func (cw *compressWriter) WriteHeader(code int) {
	cw.rw.WriteHeader(code)
}

type compress struct {
	h http.Handler
}

// 支持gzip或是deflate功能的handler。
// 根据客户端请求内容自动匹配相应的压缩算法，优先匹配gzip。
//
// 经过压缩的内容，可能需要重新指定Content-Type，系统检测的类型未必正确。
func Compress(h http.Handler) *compress {
	return &compress{h: h}
}

func CompressFunc(f func(http.ResponseWriter, *http.Request)) *compress {
	return &compress{h: http.HandlerFunc(f)}
}

func (c *compress) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	hj, ok := w.(http.Hijacker)
	if !ok {
		c.h.ServeHTTP(w, r)
		return
	}

	var gzw io.WriteCloser
	var encoding string
	encodings := strings.Split(r.Header.Get("Accept-Encoding"), ",")
	for _, encoding = range encodings {
		encoding = strings.ToLower(strings.TrimSpace(encoding))

		if encoding == "gzip" {
			gzw = gzip.NewWriter(w)
			break
		}

		if encoding == "deflate" {
			var err error
			gzw, err = flate.NewWriter(w, flate.DefaultCompression)
			if err != nil { // 若出错，不压缩，直接返回
				c.h.ServeHTTP(w, r)
				return
			}
			break
		}
	}

	w.Header().Set("Content-Encoding", encoding)
	w.Header().Add("Vary", "Accept-Encoding")
	cw := &compressWriter{
		gzw: gzw,
		rw:  w,
		hj:  hj,
	}

	// 只要gzw!=nil的，必须会执行到此处。
	defer gzw.Close()

	// 此处可能panic，所以得保证在panic之前，gzw变量已经Close
	c.h.ServeHTTP(cw, r)
}
<commit_msg>修正当客户端指定一个非法的压缩算法时，发生panic的bug<commit_after>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package handlers

import (
	"bufio"
	"compress/flate"
	"compress/gzip"
	"io"
	"net"
	"net/http"
	"strings"
)

// 同时实现了http.ResponseWriter和http.Hijacker接口。
type compressWriter struct {
	gzw io.Writer
	rw  http.ResponseWriter
	hj  http.Hijacker
}

func (cw *compressWriter) Write(bs []byte) (int, error) {
	h := cw.rw.Header()
	if h.Get("Content-Type") == "" {
		h.Set("Content-Type", http.DetectContentType(bs))
	}

	return cw.gzw.Write(bs)
}

func (cw *compressWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	return cw.hj.Hijack()
}

func (cw *compressWriter) Header() http.Header {
	return cw.rw.Header()
}

func (cw *compressWriter) WriteHeader(code int) {
	cw.rw.WriteHeader(code)
}

type compress struct {
	h http.Handler
}

// 支持gzip或是deflate功能的handler。
// 根据客户端请求内容自动匹配相应的压缩算法，优先匹配gzip。
//
// 经过压缩的内容，可能需要重新指定Content-Type，系统检测的类型未必正确。
func Compress(h http.Handler) *compress {
	return &compress{h: h}
}

func CompressFunc(f func(http.ResponseWriter, *http.Request)) *compress {
	return &compress{h: http.HandlerFunc(f)}
}

func (c *compress) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	hj, ok := w.(http.Hijacker)
	if !ok {
		c.h.ServeHTTP(w, r)
		return
	}

	var gzw io.WriteCloser
	var encoding string
	encodings := strings.Split(r.Header.Get("Accept-Encoding"), ",")
	for _, encoding = range encodings {
		encoding = strings.ToLower(strings.TrimSpace(encoding))

		if encoding == "gzip" {
			gzw = gzip.NewWriter(w)
			break
		}

		if encoding == "deflate" {
			var err error
			gzw, err = flate.NewWriter(w, flate.DefaultCompression)
			if err != nil { // 若出错，不压缩，直接返回
				c.h.ServeHTTP(w, r)
				return
			}
			break
		}
	} // end for
	if gzw == nil { // 不支持的压缩格式
		return
	}

	w.Header().Set("Content-Encoding", encoding)
	w.Header().Add("Vary", "Accept-Encoding")
	cw := &compressWriter{
		gzw: gzw,
		rw:  w,
		hj:  hj,
	}

	// 只要gzw!=nil的，必须会执行到此处。
	defer gzw.Close()

	// 此处可能panic，所以得保证在panic之前，gzw变量已经Close
	c.h.ServeHTTP(cw, r)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/buildkite/agent/buildkite"
	"github.com/buildkite/agent/command"
	"github.com/codegangsta/cli"
)

var Commands []cli.Command

var AgentDescription = `Usage:

   buildkite-agent start [arguments...]

Description:

   When a job is ready to run it will call the "bootstrap-script"
   and pass it all the environment variables required for the job to run.
   This script is responsible for checking out the code, and running the
   actual build script defined in the project.

   The agent will run any jobs within a PTY (pseudo terminal) if available.

Example:

   $ buildkite-agent start --token xxx`

var ShasumHelpDescription = `Usage:

   buildkite-agent artifact shasum [arguments...]

Description:

   Prints to STDOUT the SHA-1 for the artifact provided. If your search query
   for artifacts matches multiple agents, and error will be raised.

   Note: You need to ensure that your search query is surrounded by quotes if
   using a wild card as the built-in shell path globbing will provide files,
   which will break the download.

Example:

   $ buildkite-agent artifact shasum "pkg/release.tar.gz" --build xxx

   This will search for all the files in the build with the path "pkg/release.tar.gz" and will
   print to STDOUT it's SHA-1 checksum.

   If you would like to target artifacts from a specific build step, you can do
   so by using the --job argument.

   $ buildkite-agent artifact shasum "pkg/release.tar.gz" --job "release" --build xxx

   You can also use the job's id (provided by the environment variable $BUILDKITE_JOB_ID)`

var DownloadHelpDescription = `Usage:

   buildkite-agent artifact download [arguments...]

Description:

   Downloads artifacts from Buildkite to the local machine.

   Note: You need to ensure that your search query is surrounded by quotes if
   using a wild card as the built-in shell path globbing will provide files,
   which will break the download.

Example:

   $ buildkite-agent artifact download "pkg/*.tar.gz" . --build xxx

   This will search across all the artifacts for the build with files that match that part.
   The first argument is the search query, and the second argument is the download destination.

   If you're trying to download a specific file, and there are multiple artifacts from different
   jobs, you can target the paticular job you want to download the artifact from:

   $ buildkite-agent artifact download "pkg/*.tar.gz" . --job "tests" --build xxx

   You can also use the job's id (provided by the environment variable $BUILDKITE_JOB_ID)`

var UploadHelpDescription = `Usage:

   buildkite-agent artifact upload <pattern> <destination> [arguments...]

Description:

   Uploads files to a job as artifacts.

   You need to ensure that the paths are surrounded by quotes otherwise the
   built-in shell path globbing will provide the files, which is currently not
   supported.

Example:

   $ buildkite-agent artifact upload "log/**/*.log"

   You can also upload directy to Amazon S3 if you'd like to host your own artifacts:

   $ export AWS_SECRET_ACCESS_KEY=yyy
   $ export AWS_ACCESS_KEY_ID=xxx
   $ buildkite-agent artifact upload "log/**/*.log" s3://name-of-your-s3-bucket/$BUILDKITE_JOB_ID`

var SetHelpDescription = `Usage:

   buildkite-agent meta-data set <key> <value> [arguments...]

Description:

   Set arbitrary data on a build using a basic key/value store.

Example:

   $ buildkite-agent meta-data set "foo" "bar"`

var GetHelpDescription = `Usage:

   buildkite-agent meta-data get <key> [arguments...]

Description:

   Get data from a builds key/value store.

Example:

   $ buildkite-agent meta-data get "foo"`

func init() {
	// This is default locations of stuff (*nix systems)
	bootstrapScriptLocation := "$HOME/.buildkite/bootstrap.sh"
	buildPathLocation := "$HOME/.buildkite/builds"
	hookPathLocation := "$HOME/.buildkite/hooks"

	// Windows has a slightly modified locations
	if buildkite.MachineIsWindows() {
		bootstrapScriptLocation = "$USERPROFILE\\AppData\\Local\\BuildkiteAgent\\bootstrap.bat"
		buildPathLocation = "$USERPROFILE\\AppData\\Local\\BuildkiteAgent\\builds"
		hookPathLocation = "$USERPROFILE\\AppData\\Local\\BuildkiteAgent\\hooks"
	}

	Commands = []cli.Command{
		{
			Name:        "start",
			Usage:       "Starts a Buildkite agent",
			Description: AgentDescription,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:   "token",
					Value:  "",
					Usage:  "Your account agent token",
					EnvVar: "BUILDKITE_AGENT_TOKEN",
				},
				cli.StringFlag{
					Name:   "name",
					Value:  "",
					Usage:  "The name of the agent",
					EnvVar: "BUILDKITE_AGENT_NAME",
				},
				cli.StringFlag{
					Name:   "priority",
					Value:  "",
					Usage:  "The priority of the agent",
					EnvVar: "BUILDKITE_AGENT_PRIORITY",
				},
				cli.StringSliceFlag{
					Name:   "meta-data",
					Value:  &cli.StringSlice{},
					Usage:  "Meta data for the agent",
					EnvVar: "BUILDKITE_AGENT_META_DATA",
				},
				cli.BoolFlag{
					Name:  "meta-data-ec2-tags",
					Usage: "Populate the meta data from the current instances EC2 Tags",
				},
				cli.StringFlag{
					Name:   "bootstrap-script",
					Value:  bootstrapScriptLocation,
					Usage:  "Path to the bootstrap script",
					EnvVar: "BUILDKITE_BOOTSTRAP_SCRIPT_PATH",
				},
				cli.StringFlag{
					Name:   "build-path",
					Value:  buildPathLocation,
					Usage:  "Path to where the builds will run from",
					EnvVar: "BUILDKITE_BUILD_PATH",
				},
				cli.StringFlag{
					Name:   "hooks-path",
					Value:  hookPathLocation,
					Usage:  "Directory where the hook scripts are found",
					EnvVar: "BUILDKITE_HOOKS_PATH",
				},
				cli.BoolFlag{
					Name:  "no-pty",
					Usage: "Do not run jobs within a pseudo terminal",
				},
				cli.BoolFlag{
					Name:  "no-automatic-ssh-fingerprint-verification",
					Usage: "Don't automatically verify SSH fingerprints",
				},
				cli.BoolFlag{
					Name:  "no-script-eval",
					Usage: "Don't allow this agent to evaluate scripts from Buildkite. Only scripts that exist on the file system will be allowed to run",
				},
				cli.StringFlag{
					Name:   "endpoint",
					Value:  "https://agent.buildkite.com/v2",
					Usage:  "The agent API endpoint",
					EnvVar: "BUILDKITE_AGENT_ENDPOINT",
				},
				cli.BoolFlag{
					Name:   "debug",
					Usage:  "Enable debug mode",
					EnvVar: "BUILDKITE_AGENT_DEBUG",
				},
				cli.BoolFlag{
					Name:   "no-color",
					Usage:  "Don't show colors in logging",
					EnvVar: "BUILDKITE_AGENT_NO_COLOR",
				},
			},
			Action: command.AgentStartCommandAction,
		},
		{
			Name:  "artifact",
			Usage: "Upload/download artifacts from Buildkite jobs",
			Subcommands: []cli.Command{
				{
					Name:        "download",
					Usage:       "Downloads artifacts from Buildkite to the local machine",
					Description: DownloadHelpDescription,
					Flags: []cli.Flag{
						// We don't default to $BUILDKITE_JOB_ID with --job because downloading artifacts should
						// default to all the jobs on the build, not just the current one. --job is used
						// to scope to a paticular job if you
						cli.StringFlag{
							Name:  "job",
							Value: "",
							Usage: "Used to target a specific job to download artifacts from",
						},
						cli.StringFlag{
							Name:   "build",
							Value:  "",
							Usage:  "Which build should the artifacts be downloaded from",
							EnvVar: "BUILDKITE_BUILD_ID",
						},
						cli.StringFlag{
							Name:   "agent-access-token",
							Value:  "",
							Usage:  "The access token used to identify the agent",
							EnvVar: "BUILDKITE_AGENT_ACCESS_TOKEN",
						},
						cli.StringFlag{
							Name:   "endpoint",
							Value:  "https://agent.buildkite.com/v2",
							Usage:  "The agent API endpoint",
							EnvVar: "BUILDKITE_AGENT_ENDPOINT",
						},
						cli.BoolFlag{
							Name:   "debug",
							Usage:  "Enable debug mode",
							EnvVar: "BUILDKITE_AGENT_DEBUG",
						},
						cli.BoolFlag{
							Name:   "no-color",
							Usage:  "Don't show colors in logging",
							EnvVar: "BUILDKITE_AGENT_NO_COLOR",
						},
					},
					Action: command.ArtifactDownloadCommandAction,
				},
				{
					Name:        "upload",
					Usage:       "Uploads files to a job as artifacts",
					Description: UploadHelpDescription,
					Flags: []cli.Flag{
						cli.StringFlag{
							Name:   "job",
							Value:  "",
							Usage:  "Which job should the artifacts be uploaded to",
							EnvVar: "BUILDKITE_JOB_ID",
						},
						cli.StringFlag{
							Name:   "agent-access-token",
							Value:  "",
							Usage:  "The access token used to identify the agent",
							EnvVar: "BUILDKITE_AGENT_ACCESS_TOKEN",
						},
						cli.StringFlag{
							Name:   "endpoint",
							Value:  "https://agent.buildkite.com/v2",
							Usage:  "The agent API endpoint",
							EnvVar: "BUILDKITE_AGENT_ENDPOINT",
						},
						cli.BoolFlag{
							Name:   "debug",
							Usage:  "Enable debug mode",
							EnvVar: "BUILDKITE_AGENT_DEBUG",
						},
						cli.BoolFlag{
							Name:   "no-color",
							Usage:  "Don't show colors in logging",
							EnvVar: "BUILDKITE_AGENT_NO_COLOR",
						},
					},
					Action: command.ArtifactUploadCommandAction,
				},
				{
					Name:        "shasum",
					Usage:       "Prints the SHA-1 checksum for the artifact provided to STDOUT",
					Description: ShasumHelpDescription,
					Flags: []cli.Flag{
						// We don't default to $BUILDKITE_JOB_ID with --job because downloading artifacts should
						// default to all the jobs on the build, not just the current one. --job is used
						// to scope to a paticular job if you
						cli.StringFlag{
							Name:  "job",
							Value: "",
							Usage: "Used to target a specific job to download artifacts from",
						},
						cli.StringFlag{
							Name:   "build",
							Value:  "",
							Usage:  "Which build should the artifacts be downloaded from",
							EnvVar: "BUILDKITE_BUILD_ID",
						},
						cli.StringFlag{
							Name:   "agent-access-token",
							Value:  "",
							Usage:  "The access token used to identify the agent",
							EnvVar: "BUILDKITE_AGENT_ACCESS_TOKEN",
						},
						cli.StringFlag{
							Name:   "endpoint",
							Value:  "https://agent.buildkite.com/v2",
							Usage:  "The agent API endpoint",
							EnvVar: "BUILDKITE_AGENT_ENDPOINT",
						},
						cli.BoolFlag{
							Name:   "debug",
							Usage:  "Enable debug mode",
							EnvVar: "BUILDKITE_AGENT_DEBUG",
						},
						cli.BoolFlag{
							Name:   "no-color",
							Usage:  "Don't show colors in logging",
							EnvVar: "BUILDKITE_AGENT_NO_COLOR",
						},
					},
					Action: command.ArtifactShasumCommandAction,
				},
			},
		},
		{
			Name:  "meta-data",
			Usage: "Get/set data from Buildkite jobs",
			Subcommands: []cli.Command{
				{
					Name:        "set",
					Usage:       "Set data on a build",
					Description: SetHelpDescription,
					Flags: []cli.Flag{
						cli.StringFlag{
							Name:   "job",
							Value:  "",
							Usage:  "Which job should the artifacts be downloaded from",
							EnvVar: "BUILDKITE_JOB_ID",
						},
						cli.StringFlag{
							Name:   "agent-access-token",
							Value:  "",
							Usage:  "The access token used to identify the agent",
							EnvVar: "BUILDKITE_AGENT_ACCESS_TOKEN",
						},
						cli.StringFlag{
							Name:   "endpoint",
							Value:  "https://agent.buildkite.com/v2",
							Usage:  "The agent API endpoint",
							EnvVar: "BUILDKITE_AGENT_ENDPOINT",
						},
						cli.BoolFlag{
							Name:   "debug",
							Usage:  "Enable debug mode",
							EnvVar: "BUILDKITE_AGENT_DEBUG",
						},
						cli.BoolFlag{
							Name:   "no-color",
							Usage:  "Don't show colors in logging",
							EnvVar: "BUILDKITE_AGENT_NO_COLOR",
						},
					},
					Action: command.DataSetCommandAction,
				},
				{
					Name:        "get",
					Usage:       "Get data from a build",
					Description: GetHelpDescription,
					Flags: []cli.Flag{
						cli.StringFlag{
							Name:   "job",
							Value:  "",
							Usage:  "Which job should the data be retrieved from",
							EnvVar: "BUILDKITE_JOB_ID",
						},
						cli.StringFlag{
							Name:   "agent-access-token",
							Value:  "",
							Usage:  "The access token used to identify the agent",
							EnvVar: "BUILDKITE_AGENT_ACCESS_TOKEN",
						},
						cli.StringFlag{
							Name:   "endpoint",
							Value:  "https://agent.buildkite.com/v2",
							Usage:  "The agent API endpoint",
							EnvVar: "BUILDKITE_AGENT_ENDPOINT",
						},
						cli.BoolFlag{
							Name:   "debug",
							Usage:  "Enable debug mode",
							EnvVar: "BUILDKITE_AGENT_DEBUG",
						},
						cli.BoolFlag{
							Name:   "no-color",
							Usage:  "Don't show colors in logging",
							EnvVar: "BUILDKITE_AGENT_NO_COLOR",
						},
					},
					Action: command.DataGetCommandAction,
				},
			},
		},
	}
}
<commit_msg>Add ENV support to no-pty, no-automatic-ssh-fingerprint-verification and no-script-eval<commit_after>package main

import (
	"github.com/buildkite/agent/buildkite"
	"github.com/buildkite/agent/command"
	"github.com/codegangsta/cli"
)

var Commands []cli.Command

var AgentDescription = `Usage:

   buildkite-agent start [arguments...]

Description:

   When a job is ready to run it will call the "bootstrap-script"
   and pass it all the environment variables required for the job to run.
   This script is responsible for checking out the code, and running the
   actual build script defined in the project.

   The agent will run any jobs within a PTY (pseudo terminal) if available.

Example:

   $ buildkite-agent start --token xxx`

var ShasumHelpDescription = `Usage:

   buildkite-agent artifact shasum [arguments...]

Description:

   Prints to STDOUT the SHA-1 for the artifact provided. If your search query
   for artifacts matches multiple agents, and error will be raised.

   Note: You need to ensure that your search query is surrounded by quotes if
   using a wild card as the built-in shell path globbing will provide files,
   which will break the download.

Example:

   $ buildkite-agent artifact shasum "pkg/release.tar.gz" --build xxx

   This will search for all the files in the build with the path "pkg/release.tar.gz" and will
   print to STDOUT it's SHA-1 checksum.

   If you would like to target artifacts from a specific build step, you can do
   so by using the --job argument.

   $ buildkite-agent artifact shasum "pkg/release.tar.gz" --job "release" --build xxx

   You can also use the job's id (provided by the environment variable $BUILDKITE_JOB_ID)`

var DownloadHelpDescription = `Usage:

   buildkite-agent artifact download [arguments...]

Description:

   Downloads artifacts from Buildkite to the local machine.

   Note: You need to ensure that your search query is surrounded by quotes if
   using a wild card as the built-in shell path globbing will provide files,
   which will break the download.

Example:

   $ buildkite-agent artifact download "pkg/*.tar.gz" . --build xxx

   This will search across all the artifacts for the build with files that match that part.
   The first argument is the search query, and the second argument is the download destination.

   If you're trying to download a specific file, and there are multiple artifacts from different
   jobs, you can target the paticular job you want to download the artifact from:

   $ buildkite-agent artifact download "pkg/*.tar.gz" . --job "tests" --build xxx

   You can also use the job's id (provided by the environment variable $BUILDKITE_JOB_ID)`

var UploadHelpDescription = `Usage:

   buildkite-agent artifact upload <pattern> <destination> [arguments...]

Description:

   Uploads files to a job as artifacts.

   You need to ensure that the paths are surrounded by quotes otherwise the
   built-in shell path globbing will provide the files, which is currently not
   supported.

Example:

   $ buildkite-agent artifact upload "log/**/*.log"

   You can also upload directy to Amazon S3 if you'd like to host your own artifacts:

   $ export AWS_SECRET_ACCESS_KEY=yyy
   $ export AWS_ACCESS_KEY_ID=xxx
   $ buildkite-agent artifact upload "log/**/*.log" s3://name-of-your-s3-bucket/$BUILDKITE_JOB_ID`

var SetHelpDescription = `Usage:

   buildkite-agent meta-data set <key> <value> [arguments...]

Description:

   Set arbitrary data on a build using a basic key/value store.

Example:

   $ buildkite-agent meta-data set "foo" "bar"`

var GetHelpDescription = `Usage:

   buildkite-agent meta-data get <key> [arguments...]

Description:

   Get data from a builds key/value store.

Example:

   $ buildkite-agent meta-data get "foo"`

func init() {
	// This is default locations of stuff (*nix systems)
	bootstrapScriptLocation := "$HOME/.buildkite/bootstrap.sh"
	buildPathLocation := "$HOME/.buildkite/builds"
	hookPathLocation := "$HOME/.buildkite/hooks"

	// Windows has a slightly modified locations
	if buildkite.MachineIsWindows() {
		bootstrapScriptLocation = "$USERPROFILE\\AppData\\Local\\BuildkiteAgent\\bootstrap.bat"
		buildPathLocation = "$USERPROFILE\\AppData\\Local\\BuildkiteAgent\\builds"
		hookPathLocation = "$USERPROFILE\\AppData\\Local\\BuildkiteAgent\\hooks"
	}

	Commands = []cli.Command{
		{
			Name:        "start",
			Usage:       "Starts a Buildkite agent",
			Description: AgentDescription,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:   "token",
					Value:  "",
					Usage:  "Your account agent token",
					EnvVar: "BUILDKITE_AGENT_TOKEN",
				},
				cli.StringFlag{
					Name:   "name",
					Value:  "",
					Usage:  "The name of the agent",
					EnvVar: "BUILDKITE_AGENT_NAME",
				},
				cli.StringFlag{
					Name:   "priority",
					Value:  "",
					Usage:  "The priority of the agent",
					EnvVar: "BUILDKITE_AGENT_PRIORITY",
				},
				cli.StringSliceFlag{
					Name:   "meta-data",
					Value:  &cli.StringSlice{},
					Usage:  "Meta data for the agent",
					EnvVar: "BUILDKITE_AGENT_META_DATA",
				},
				cli.BoolFlag{
					Name:  "meta-data-ec2-tags",
					Usage: "Populate the meta data from the current instances EC2 Tags",
				},
				cli.StringFlag{
					Name:   "bootstrap-script",
					Value:  bootstrapScriptLocation,
					Usage:  "Path to the bootstrap script",
					EnvVar: "BUILDKITE_BOOTSTRAP_SCRIPT_PATH",
				},
				cli.StringFlag{
					Name:   "build-path",
					Value:  buildPathLocation,
					Usage:  "Path to where the builds will run from",
					EnvVar: "BUILDKITE_BUILD_PATH",
				},
				cli.StringFlag{
					Name:   "hooks-path",
					Value:  hookPathLocation,
					Usage:  "Directory where the hook scripts are found",
					EnvVar: "BUILDKITE_HOOKS_PATH",
				},
				cli.BoolFlag{
					Name:   "no-pty",
					Usage:  "Do not run jobs within a pseudo terminal",
					EnvVar: "BUILDKITE_NO_PTY",
				},
				cli.BoolFlag{
					Name:   "no-automatic-ssh-fingerprint-verification",
					Usage:  "Don't automatically verify SSH fingerprints",
					EnvVar: "BUILDKITE_NO_AUTOMATIC_SSH_FINGERPRINT_VERIFICATION",
				},
				cli.BoolFlag{
					Name:   "no-script-eval",
					Usage:  "Don't allow this agent to evaluate scripts from Buildkite. Only scripts that exist on the file system will be allowed to run",
					EnvVar: "BUILDKITE_NO_SCRIPT_EVAL",
				},
				cli.StringFlag{
					Name:   "endpoint",
					Value:  "https://agent.buildkite.com/v2",
					Usage:  "The agent API endpoint",
					EnvVar: "BUILDKITE_AGENT_ENDPOINT",
				},
				cli.BoolFlag{
					Name:   "debug",
					Usage:  "Enable debug mode",
					EnvVar: "BUILDKITE_AGENT_DEBUG",
				},
				cli.BoolFlag{
					Name:   "no-color",
					Usage:  "Don't show colors in logging",
					EnvVar: "BUILDKITE_AGENT_NO_COLOR",
				},
			},
			Action: command.AgentStartCommandAction,
		},
		{
			Name:  "artifact",
			Usage: "Upload/download artifacts from Buildkite jobs",
			Subcommands: []cli.Command{
				{
					Name:        "download",
					Usage:       "Downloads artifacts from Buildkite to the local machine",
					Description: DownloadHelpDescription,
					Flags: []cli.Flag{
						// We don't default to $BUILDKITE_JOB_ID with --job because downloading artifacts should
						// default to all the jobs on the build, not just the current one. --job is used
						// to scope to a paticular job if you
						cli.StringFlag{
							Name:  "job",
							Value: "",
							Usage: "Used to target a specific job to download artifacts from",
						},
						cli.StringFlag{
							Name:   "build",
							Value:  "",
							Usage:  "Which build should the artifacts be downloaded from",
							EnvVar: "BUILDKITE_BUILD_ID",
						},
						cli.StringFlag{
							Name:   "agent-access-token",
							Value:  "",
							Usage:  "The access token used to identify the agent",
							EnvVar: "BUILDKITE_AGENT_ACCESS_TOKEN",
						},
						cli.StringFlag{
							Name:   "endpoint",
							Value:  "https://agent.buildkite.com/v2",
							Usage:  "The agent API endpoint",
							EnvVar: "BUILDKITE_AGENT_ENDPOINT",
						},
						cli.BoolFlag{
							Name:   "debug",
							Usage:  "Enable debug mode",
							EnvVar: "BUILDKITE_AGENT_DEBUG",
						},
						cli.BoolFlag{
							Name:   "no-color",
							Usage:  "Don't show colors in logging",
							EnvVar: "BUILDKITE_AGENT_NO_COLOR",
						},
					},
					Action: command.ArtifactDownloadCommandAction,
				},
				{
					Name:        "upload",
					Usage:       "Uploads files to a job as artifacts",
					Description: UploadHelpDescription,
					Flags: []cli.Flag{
						cli.StringFlag{
							Name:   "job",
							Value:  "",
							Usage:  "Which job should the artifacts be uploaded to",
							EnvVar: "BUILDKITE_JOB_ID",
						},
						cli.StringFlag{
							Name:   "agent-access-token",
							Value:  "",
							Usage:  "The access token used to identify the agent",
							EnvVar: "BUILDKITE_AGENT_ACCESS_TOKEN",
						},
						cli.StringFlag{
							Name:   "endpoint",
							Value:  "https://agent.buildkite.com/v2",
							Usage:  "The agent API endpoint",
							EnvVar: "BUILDKITE_AGENT_ENDPOINT",
						},
						cli.BoolFlag{
							Name:   "debug",
							Usage:  "Enable debug mode",
							EnvVar: "BUILDKITE_AGENT_DEBUG",
						},
						cli.BoolFlag{
							Name:   "no-color",
							Usage:  "Don't show colors in logging",
							EnvVar: "BUILDKITE_AGENT_NO_COLOR",
						},
					},
					Action: command.ArtifactUploadCommandAction,
				},
				{
					Name:        "shasum",
					Usage:       "Prints the SHA-1 checksum for the artifact provided to STDOUT",
					Description: ShasumHelpDescription,
					Flags: []cli.Flag{
						// We don't default to $BUILDKITE_JOB_ID with --job because downloading artifacts should
						// default to all the jobs on the build, not just the current one. --job is used
						// to scope to a paticular job if you
						cli.StringFlag{
							Name:  "job",
							Value: "",
							Usage: "Used to target a specific job to download artifacts from",
						},
						cli.StringFlag{
							Name:   "build",
							Value:  "",
							Usage:  "Which build should the artifacts be downloaded from",
							EnvVar: "BUILDKITE_BUILD_ID",
						},
						cli.StringFlag{
							Name:   "agent-access-token",
							Value:  "",
							Usage:  "The access token used to identify the agent",
							EnvVar: "BUILDKITE_AGENT_ACCESS_TOKEN",
						},
						cli.StringFlag{
							Name:   "endpoint",
							Value:  "https://agent.buildkite.com/v2",
							Usage:  "The agent API endpoint",
							EnvVar: "BUILDKITE_AGENT_ENDPOINT",
						},
						cli.BoolFlag{
							Name:   "debug",
							Usage:  "Enable debug mode",
							EnvVar: "BUILDKITE_AGENT_DEBUG",
						},
						cli.BoolFlag{
							Name:   "no-color",
							Usage:  "Don't show colors in logging",
							EnvVar: "BUILDKITE_AGENT_NO_COLOR",
						},
					},
					Action: command.ArtifactShasumCommandAction,
				},
			},
		},
		{
			Name:  "meta-data",
			Usage: "Get/set data from Buildkite jobs",
			Subcommands: []cli.Command{
				{
					Name:        "set",
					Usage:       "Set data on a build",
					Description: SetHelpDescription,
					Flags: []cli.Flag{
						cli.StringFlag{
							Name:   "job",
							Value:  "",
							Usage:  "Which job should the artifacts be downloaded from",
							EnvVar: "BUILDKITE_JOB_ID",
						},
						cli.StringFlag{
							Name:   "agent-access-token",
							Value:  "",
							Usage:  "The access token used to identify the agent",
							EnvVar: "BUILDKITE_AGENT_ACCESS_TOKEN",
						},
						cli.StringFlag{
							Name:   "endpoint",
							Value:  "https://agent.buildkite.com/v2",
							Usage:  "The agent API endpoint",
							EnvVar: "BUILDKITE_AGENT_ENDPOINT",
						},
						cli.BoolFlag{
							Name:   "debug",
							Usage:  "Enable debug mode",
							EnvVar: "BUILDKITE_AGENT_DEBUG",
						},
						cli.BoolFlag{
							Name:   "no-color",
							Usage:  "Don't show colors in logging",
							EnvVar: "BUILDKITE_AGENT_NO_COLOR",
						},
					},
					Action: command.DataSetCommandAction,
				},
				{
					Name:        "get",
					Usage:       "Get data from a build",
					Description: GetHelpDescription,
					Flags: []cli.Flag{
						cli.StringFlag{
							Name:   "job",
							Value:  "",
							Usage:  "Which job should the data be retrieved from",
							EnvVar: "BUILDKITE_JOB_ID",
						},
						cli.StringFlag{
							Name:   "agent-access-token",
							Value:  "",
							Usage:  "The access token used to identify the agent",
							EnvVar: "BUILDKITE_AGENT_ACCESS_TOKEN",
						},
						cli.StringFlag{
							Name:   "endpoint",
							Value:  "https://agent.buildkite.com/v2",
							Usage:  "The agent API endpoint",
							EnvVar: "BUILDKITE_AGENT_ENDPOINT",
						},
						cli.BoolFlag{
							Name:   "debug",
							Usage:  "Enable debug mode",
							EnvVar: "BUILDKITE_AGENT_DEBUG",
						},
						cli.BoolFlag{
							Name:   "no-color",
							Usage:  "Don't show colors in logging",
							EnvVar: "BUILDKITE_AGENT_NO_COLOR",
						},
					},
					Action: command.DataGetCommandAction,
				},
			},
		},
	}
}
<|endoftext|>


<commit_before>package bruxism

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/iopred/discordgo"
	"github.com/scaleway/scaleway-cli/vendor/github.com/renstrom/fuzzysearch/fuzzy"
)

// HelpHelp returns help for the help plugin.
func HelpHelp(bot *Bot, service Service) (string, string) {
	ticks := ""
	if service.Name() == DiscordServiceName {
		ticks = "`"
	}

	commands := []string{}

	for _, plugin := range bot.Services[service.Name()].Plugins {
		t := plugin.Help(bot, service, true)

		if t != nil && len(t) > 0 {
			commands = append(commands, strings.ToLower(plugin.Name()))
		}
	}

	sort.Strings(commands)

	return "[<topic>]", fmt.Sprintf("Returns generic help or help for a specific topic. Available topics: %s%s%s", ticks, strings.Join(commands, ", "), ticks)
}

// HelpCommand is a command for returning help text for all registered plugins on a service.
func HelpCommand(bot *Bot, service Service, message Message, command string, parts []string) {
	help := []string{}

	for _, plugin := range bot.Services[service.Name()].Plugins {
		var h []string
		if len(parts) == 0 {
			h = plugin.Help(bot, service, false)
		} else if len(parts) == 1 && strings.ToLower(parts[0]) == strings.ToLower(plugin.Name()) {
			h = plugin.Help(bot, service, true)
		}
		if h != nil && len(h) > 0 {
			help = append(help, h...)
		}
	}

	if len(parts) == 0 {
		sort.Strings(help)
		help = append([]string{fmt.Sprintf("All commands can be used in private messages without the `%s` prefix.", service.CommandPrefix())}, help...)
	}

	if len(parts) != 0 && len(help) == 0 {
		help = []string{fmt.Sprintf("Unknown topic: %s", parts[0])}
	}

	if service.SupportsMultiline() {
		if err := service.SendMessage(message.Channel(), strings.Join(help, "\n")); err != nil {
			log.Println(err)
		}
	} else {
		for _, h := range help {
			if err := service.SendMessage(message.Channel(), h); err != nil {
				log.Println(err)
			}
		}
	}
}

// InviteHelp will return the help text for the invite command.
func InviteHelp(bot *Bot, service Service) (string, string) {
	switch service.Name() {
	case DiscordServiceName:
		return "<discordinvite>", "Joins the provided Discord server."
	case YouTubeServiceName:
		return "<livechatid>", "Joins the provided YouTube chat by id (this may be hard to find)."
	}
	return "<channel>", "Joins the provided channel."
}

// InviteCommand is a command for accepting an invite to a channel.
func InviteCommand(bot *Bot, service Service, message Message, command string, parts []string) {
	if len(parts) == 1 {
		join := parts[0]
		if service.Name() == DiscordServiceName {
			join = discordInviteID(join)
		}
		if err := service.Join(join); err != nil {
			if service.Name() == DiscordServiceName && err == ErrAlreadyJoined {
				service.PrivateMessage(message.UserID(), "I have already joined that server.")
				return
			}
			fmt.Printf("Error joining %v %v", service.Name(), err)
		} else if service.Name() == DiscordServiceName {
			service.PrivateMessage(message.UserID(), "I have joined that server.")
		}
	}
}

var startTime time.Time

func init() {
	startTime = time.Now()
}

func getDurationString(duration time.Duration) string {
	return fmt.Sprintf(
		"%0.2d:%02d:%02d",
		int(duration.Hours()),
		int(duration.Minutes())%60,
		int(duration.Seconds())%60,
	)
}

// StatsCommand returns bot statistics.
func StatsCommand(bot *Bot, service Service, message Message, command string, parts []string) {
	stats := runtime.MemStats{}
	runtime.ReadMemStats(&stats)

	w := &tabwriter.Writer{}
	buf := &bytes.Buffer{}

	w.Init(buf, 0, 4, 0, ' ', 0)
	if service.Name() == DiscordServiceName {
		fmt.Fprintf(w, "```\n")
	}
	fmt.Fprintf(w, "Septapus: \t%s\n", VersionString)
	if service.Name() == DiscordServiceName {
		fmt.Fprintf(w, "Discordgo: \t%s\n", discordgo.VERSION)
	}
	fmt.Fprintf(w, "Go: \t%s\n", runtime.Version())
	fmt.Fprintf(w, "Uptime: \t%s\n", getDurationString(time.Now().Sub(startTime)))
	fmt.Fprintf(w, "Memory used: \t%s / %s\n", humanize.Bytes(stats.Alloc), humanize.Bytes(stats.TotalAlloc))
	fmt.Fprintf(w, "Concurrent tasks: \t%d\n", runtime.NumGoroutine())
	if service.Name() == DiscordServiceName {
		fmt.Fprintf(w, "Connected servers: \t%d\n", service.ChannelCount())
		fmt.Fprintf(w, "\n```")
	} else {
		fmt.Fprintf(w, "Connected channels: \t%d\n", service.ChannelCount())
	}
	w.Flush()

	out := buf.String() + "\nBuilt with love by iopred."

	if service.SupportsMultiline() {
		if err := service.SendMessage(message.Channel(), out); err != nil {
			log.Println(err)
		}
	} else {
		lines := strings.Split(out, "\n")
		for _, line := range lines {
			if err := service.SendMessage(message.Channel(), line); err != nil {
				log.Println(err)
			}
		}
	}
}

func numberTrivia(bot *Bot, num int, notfound bool) (string, error) {
	notfoundString := ""
	if notfound {
		notfoundString = "?notfound=floor"
	}
	r, err := http.NewRequest("GET", fmt.Sprintf("https://numbersapi.p.mashape.com/%d/trivia%s", num, notfoundString), nil)
	if err != nil {
		return "", err
	}

	r.Header.Set("X-Mashape-Authorization", bot.MashableKey)
	r.Header.Set("Accept", "text/plain")

	resp, err := http.DefaultClient.Do(r)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	if resp.StatusCode != 200 {
		return "", errors.New(string(body))
	}

	return string(body), nil
}

// NumberTriviaCommand is a command for getting number trivial.
func NumberTriviaCommand(bot *Bot, service Service, message Message, command string, parts []string) {
	notfound := true
	num := rand.Intn(1000)
	if len(parts) == 1 {
		if i, err := strconv.Atoi(parts[0]); err == nil {
			num = i
			notfound = false
		}
	}

	service.Typing(message.Channel())

	str, err := numberTrivia(bot, num, notfound)
	if err != nil {
		service.SendMessage(message.Channel(), "There was an error requesting trivia, sorry!")
		return
	}

	service.SendMessage(message.Channel(), str)
}

type MTGSet struct {
	Cards []*MTGCard `json:"cards"`
}

type MTGCard struct {
	Name      string  `json:"name"`
	ManaCost  string  `json:"manaCost"`
	Type      string  `json:"type"`
	Text      string  `json:"text"`
	ID        *int    `json:"multiverseid"`
	Power     *string `json:"power"`
	Toughness *string `json:"toughness"`
	Loyalty   *int    `json:"loyalty"`
}

var MTGCardMap map[string]*MTGCard = map[string]*MTGCard{}
var MTGCardNames []string

var TextReplacer *strings.Replacer = strings.NewReplacer("(", "*(", ")", ")*")
var CostReplacer *strings.Replacer = strings.NewReplacer("{", "", "}", "")
var RestReplacer *strings.Replacer = strings.NewReplacer("*", "\\*")

func init() {

	file, err := os.Open("mtg/AllSets-x.json")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close()

	MTGSets := map[string]*MTGSet{}

	d := json.NewDecoder(bufio.NewReader(file))
	err = d.Decode(&MTGSets)
	if err != nil {
		fmt.Println(err)
		return
	}

	for _, s := range MTGSets {
		for _, c := range s.Cards {
			c.ManaCost = CostReplacer.Replace(c.ManaCost)
			c.Text = CostReplacer.Replace(c.Text)
			MTGCardMap[c.Name] = c
			MTGCardNames = append(MTGCardNames, c.Name)
		}
	}
}

// MTGCommand is a command for getting information about MTG cards..
func MTGCommand(bot *Bot, service Service, message Message, command string, parts []string) {
	cardNames := fuzzy.RankFindFold(command, MTGCardNames)
	if len(cardNames) == 0 {
		service.SendMessage(message.Channel(), "Could not find a card with that name, sorry.")
		return
	}

	sort.Sort(cardNames)

	card := MTGCardMap[cardNames[0].Target]

	rest := ""
	if card.Power != nil {
		rest += RestReplacer.Replace(fmt.Sprintf("\n%s/%s", *card.Power, *card.Toughness))
	}
	if card.Loyalty != nil {
		rest += RestReplacer.Replace(fmt.Sprintf("\n%d", *card.Loyalty))
	}
	if card.ID != nil {
		rest += fmt.Sprintf("\n(http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=%d&type=card)", *card.ID)
	}

	if service.Name() == DiscordServiceName {
		service.SendMessage(message.Channel(), fmt.Sprintf("**%s** %s\n*%s*\n%s%s", card.Name, card.ManaCost, card.Type, TextReplacer.Replace(card.Text), rest))
	} else {
		service.SendMessage(message.Channel(), strings.Replace(fmt.Sprintf("%s. %s. %s. %s%s", card.Name, card.Type, card.ManaCost, card.Text, rest), "\n", " ", -1))
	}
}
<commit_msg>Small cleanup<commit_after>package bruxism

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/iopred/discordgo"
	"github.com/scaleway/scaleway-cli/vendor/github.com/renstrom/fuzzysearch/fuzzy"
)

// HelpHelp returns help for the help plugin.
func HelpHelp(bot *Bot, service Service) (string, string) {
	ticks := ""
	if service.Name() == DiscordServiceName {
		ticks = "`"
	}

	commands := []string{}

	for _, plugin := range bot.Services[service.Name()].Plugins {
		t := plugin.Help(bot, service, true)

		if t != nil && len(t) > 0 {
			commands = append(commands, strings.ToLower(plugin.Name()))
		}
	}

	sort.Strings(commands)

	return "[<topic>]", fmt.Sprintf("Returns generic help or help for a specific topic. Available topics: %s%s%s", ticks, strings.Join(commands, ", "), ticks)
}

// HelpCommand is a command for returning help text for all registered plugins on a service.
func HelpCommand(bot *Bot, service Service, message Message, command string, parts []string) {
	help := []string{}

	for _, plugin := range bot.Services[service.Name()].Plugins {
		var h []string
		if len(parts) == 0 {
			h = plugin.Help(bot, service, false)
		} else if len(parts) == 1 && strings.ToLower(parts[0]) == strings.ToLower(plugin.Name()) {
			h = plugin.Help(bot, service, true)
		}
		if h != nil && len(h) > 0 {
			help = append(help, h...)
		}
	}

	if len(parts) == 0 {
		sort.Strings(help)
		help = append([]string{fmt.Sprintf("All commands can be used in private messages without the `%s` prefix.", service.CommandPrefix())}, help...)
	}

	if len(parts) != 0 && len(help) == 0 {
		help = []string{fmt.Sprintf("Unknown topic: %s", parts[0])}
	}

	if service.SupportsMultiline() {
		if err := service.SendMessage(message.Channel(), strings.Join(help, "\n")); err != nil {
			log.Println(err)
		}
	} else {
		for _, h := range help {
			if err := service.SendMessage(message.Channel(), h); err != nil {
				log.Println(err)
			}
		}
	}
}

// InviteHelp will return the help text for the invite command.
func InviteHelp(bot *Bot, service Service) (string, string) {
	switch service.Name() {
	case DiscordServiceName:
		return "<discordinvite>", "Joins the provided Discord server."
	case YouTubeServiceName:
		return "<livechatid>", "Joins the provided YouTube chat by id (this may be hard to find)."
	}
	return "<channel>", "Joins the provided channel."
}

// InviteCommand is a command for accepting an invite to a channel.
func InviteCommand(bot *Bot, service Service, message Message, command string, parts []string) {
	if len(parts) == 1 {
		join := parts[0]
		if service.Name() == DiscordServiceName {
			join = discordInviteID(join)
		}
		if err := service.Join(join); err != nil {
			if service.Name() == DiscordServiceName && err == ErrAlreadyJoined {
				service.PrivateMessage(message.UserID(), "I have already joined that server.")
				return
			}
			fmt.Printf("Error joining %v %v", service.Name(), err)
		} else if service.Name() == DiscordServiceName {
			service.PrivateMessage(message.UserID(), "I have joined that server.")
		}
	}
}

var statsStartTime time.Time = time.Now()

func getDurationString(duration time.Duration) string {
	return fmt.Sprintf(
		"%0.2d:%02d:%02d",
		int(duration.Hours()),
		int(duration.Minutes())%60,
		int(duration.Seconds())%60,
	)
}

// StatsCommand returns bot statistics.
func StatsCommand(bot *Bot, service Service, message Message, command string, parts []string) {
	stats := runtime.MemStats{}
	runtime.ReadMemStats(&stats)

	w := &tabwriter.Writer{}
	buf := &bytes.Buffer{}

	w.Init(buf, 0, 4, 0, ' ', 0)
	if service.Name() == DiscordServiceName {
		fmt.Fprintf(w, "```\n")
	}
	fmt.Fprintf(w, "Septapus: \t%s\n", VersionString)
	if service.Name() == DiscordServiceName {
		fmt.Fprintf(w, "Discordgo: \t%s\n", discordgo.VERSION)
	}
	fmt.Fprintf(w, "Go: \t%s\n", runtime.Version())
	fmt.Fprintf(w, "Uptime: \t%s\n", getDurationString(time.Now().Sub(statsStartTime)))
	fmt.Fprintf(w, "Memory used: \t%s / %s\n", humanize.Bytes(stats.Alloc), humanize.Bytes(stats.TotalAlloc))
	fmt.Fprintf(w, "Concurrent tasks: \t%d\n", runtime.NumGoroutine())
	if service.Name() == DiscordServiceName {
		fmt.Fprintf(w, "Connected servers: \t%d\n", service.ChannelCount())
		fmt.Fprintf(w, "\n```")
	} else {
		fmt.Fprintf(w, "Connected channels: \t%d\n", service.ChannelCount())
	}
	w.Flush()

	out := buf.String() + "\nBuilt with love by iopred."

	if service.SupportsMultiline() {
		if err := service.SendMessage(message.Channel(), out); err != nil {
			log.Println(err)
		}
	} else {
		lines := strings.Split(out, "\n")
		for _, line := range lines {
			if err := service.SendMessage(message.Channel(), line); err != nil {
				log.Println(err)
			}
		}
	}
}

func numberTrivia(bot *Bot, num int, notfound bool) (string, error) {
	notfoundString := ""
	if notfound {
		notfoundString = "?notfound=floor"
	}
	r, err := http.NewRequest("GET", fmt.Sprintf("https://numbersapi.p.mashape.com/%d/trivia%s", num, notfoundString), nil)
	if err != nil {
		return "", err
	}

	r.Header.Set("X-Mashape-Authorization", bot.MashableKey)
	r.Header.Set("Accept", "text/plain")

	resp, err := http.DefaultClient.Do(r)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	if resp.StatusCode != 200 {
		return "", errors.New(string(body))
	}

	return string(body), nil
}

// NumberTriviaCommand is a command for getting number trivial.
func NumberTriviaCommand(bot *Bot, service Service, message Message, command string, parts []string) {
	notfound := true
	num := rand.Intn(1000)
	if len(parts) == 1 {
		if i, err := strconv.Atoi(parts[0]); err == nil {
			num = i
			notfound = false
		}
	}

	service.Typing(message.Channel())

	str, err := numberTrivia(bot, num, notfound)
	if err != nil {
		service.SendMessage(message.Channel(), "There was an error requesting trivia, sorry!")
		return
	}

	service.SendMessage(message.Channel(), str)
}

type MTGSet struct {
	Cards []*MTGCard `json:"cards"`
}

type MTGCard struct {
	Name      string  `json:"name"`
	ManaCost  string  `json:"manaCost"`
	Type      string  `json:"type"`
	Text      string  `json:"text"`
	ID        *int    `json:"multiverseid"`
	Power     *string `json:"power"`
	Toughness *string `json:"toughness"`
	Loyalty   *int    `json:"loyalty"`
}

var MTGCardMap map[string]*MTGCard = map[string]*MTGCard{}
var MTGCardNames []string

var MTGTextReplacer *strings.Replacer = strings.NewReplacer("(", "*(", ")", ")*")
var MTGCostReplacer *strings.Replacer = strings.NewReplacer("{", "", "}", "")
var MTGRestReplacer *strings.Replacer = strings.NewReplacer("*", "\\*")

func init() {
	file, err := os.Open("mtg/AllSets-x.json")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close()

	MTGSets := map[string]*MTGSet{}

	d := json.NewDecoder(bufio.NewReader(file))
	err = d.Decode(&MTGSets)
	if err != nil {
		fmt.Println(err)
		return
	}

	for _, s := range MTGSets {
		for _, c := range s.Cards {
			c.ManaCost = MTGCostReplacer.Replace(c.ManaCost)
			c.Text = MTGCostReplacer.Replace(c.Text)
			MTGCardMap[c.Name] = c
			MTGCardNames = append(MTGCardNames, c.Name)
		}
	}
}

// MTGCommand is a command for getting information about MTG cards..
func MTGCommand(bot *Bot, service Service, message Message, command string, parts []string) {
	cardNames := fuzzy.RankFindFold(command, MTGCardNames)
	if len(cardNames) == 0 {
		service.SendMessage(message.Channel(), "Could not find a card with that name, sorry.")
		return
	}

	sort.Sort(cardNames)

	card := MTGCardMap[cardNames[0].Target]

	rest := ""
	if card.Power != nil {
		rest += MTGRestReplacer.Replace(fmt.Sprintf("\n%s/%s", *card.Power, *card.Toughness))
	}
	if card.Loyalty != nil {
		rest += MTGRestReplacer.Replace(fmt.Sprintf("\n%d", *card.Loyalty))
	}
	if card.ID != nil {
		rest += fmt.Sprintf("\n(http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=%d&type=card)", *card.ID)
	}

	if service.Name() == DiscordServiceName {
		service.SendMessage(message.Channel(), fmt.Sprintf("**%s** %s\n*%s*\n%s%s", card.Name, card.ManaCost, card.Type, MTGTextReplacer.Replace(card.Text), rest))
	} else {
		service.SendMessage(message.Channel(), strings.Replace(fmt.Sprintf("%s. %s. %s. %s%s", card.Name, card.Type, card.ManaCost, card.Text, rest), "\n", " ", -1))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/bwmarrin/discordgo"
	"io"
	"os"
)

// generic command struct which contains name, description, and a function
type command struct {
	name             string                                                                                // human-readable name of the command
	description      string                                                                                // description of command's function
	usage            string                                                                                // example of how to correctly use command - [] for optional arguments, <> for required arguments
	verbs            []string                                                                                // all verbs which are mapped to the same command
	requiresDatabase bool                                                                                // does this command require database access?
	function         func([]string, *discordgo.Channel, *discordgo.MessageCreate, *discordgo.Session) *commandOutput // function which receives a slice of arguments and returns a string to display to the user
}

// output returned by all command functions, can contain a file to be uploaded
type commandOutput struct {
	response string
	file     io.Reader
	embed    *discordgo.MessageEmbed
}

func initCommands() map[string]*command {
	commandList := []*command{}

	commandList = append(commandList,

		// Define all commands here in the order they will be displayed by the help command
		// The 'usage' field should use the default verb
		// Do not include the command prefix

		&command{
			name:             "Display help",
			description:      "Lists all commands and their purposes.\nCan also display detailed info about a given command.",
			usage:            "help [verb]",
			verbs:            []string{"help", "commands"},
			requiresDatabase: false,
			function: func(args []string, channel *discordgo.Channel, msgEvent *discordgo.MessageCreate, discordSession *discordgo.Session) *commandOutput {

				DebugPrint("Running help command.")

				if len(args) <= 0 {

					DebugPrint("No arguments; listing commands.")

					embed := NewEmbed().
						SetTitle("Source").
						SetAuthor("Sunbot " + version).
						//SetDescription("Database enabled: " + strconv.FormatBool(redisEnabled)).
						SetURL("https://github.com/techniponi/sunbot").
						SetImage(discordSession.State.User.AvatarURL("128"))

					for _, cmd := range commandList {
						/*
						if cmd.requiresDatabase && !redisEnabled {
							// Database is not enabled, this command needs it
						} else {
						*/
							embed.AddField(cmd.name, "`"+cfg.DefaultPrefix+cmd.usage+"`")
						//}
					}

					return &commandOutput{embed: embed.MessageEmbed}
				}

				DebugPrint("Verb was given...")

				// check if command exists
				if cmd, ok := commands[args[0]]; ok {

					embed := NewEmbed().
						SetTitle(cmd.name).
						SetDescription(cmd.description).
						AddField("Usage", "`"+cfg.DefaultPrefix+cmd.usage+"`")

					DebugPrint("Providing help for given verb.")

					// compile verbs
					verbOutput := ""
					for index, verb := range cmd.verbs {
						// don't add a comma if it's the last one
						if index == (len(cmd.verbs) - 1) {
							verbOutput += "`" + cfg.DefaultPrefix + verb + "`"
						} else {
							verbOutput += "`" + cfg.DefaultPrefix + verb + "`, "
						}
					}
					embed.AddField("Verbs", verbOutput)
					return &commandOutput{embed: embed.MessageEmbed}
				}
				DebugPrint("Given verb was not found.")
				return &commandOutput{response: "That isn't a valid command."}
			},
		},

		&command{
			name:             "Derpibooru search",
			description:      "Searches Derpibooru with the given tags as the query, chooses a random result to display.\nUse commas to separate tags like you would on the website.",
			usage:            "derpi <tags>",
			verbs:            []string{"derpi", "db", "derpibooru"},
			requiresDatabase: false,
			function: func(args []string, channel *discordgo.Channel, msgEvent *discordgo.MessageCreate, discordSession *discordgo.Session) *commandOutput {
				if len(args) < 1 {
					DebugPrint("User ran derpibooru command with no tags given.")
					return &commandOutput{response: "Error: no tags specified"}
				}
				DebugPrint("User is running derpibooru command...")

				searchQuery := ""

				for _, arg := range args {
					searchQuery += arg + " "
				}

				// enforce 'safe' tag if channel is not nsfw
				if !channel.NSFW {
					DebugPrint("Channel #" + channel.Name + " is SFW, adding safe tag...")
					searchQuery += ",safe"
				}

				DebugPrint("Searching with tags:\n" + searchQuery)

				// use derpibooru.go to perform search
				results, err := DerpiSearchWithTags(searchQuery, cfg.DerpiApiKey)
				if err != nil {
					fmt.Println(err)
					return &commandOutput{response: "Error: " + err.Error()}
				}

				// check for results
				if len(results.Search) <= 0 {
					DebugPrint("Derpibooru returned no results.")
					return &commandOutput{response: "Error: no results."}
				}
				DebugPrint("Derpibooru returned results; parsed successfully.")
				// pick one randomly
				output := "http:" + results.Search[RandomRange(0, len(results.Search))].Image

				return &commandOutput{response: output}
			},
		},

		&command{
			name:             "Gay",
			description:      "Posts a very gay image.",
			usage:            "gay",
			verbs:            []string{"gay"},
			requiresDatabase: false,
			function: func(args []string, channel *discordgo.Channel, msgEvent *discordgo.MessageCreate, discordSession *discordgo.Session) *commandOutput {
				file, err := os.Open("img/gaybats.png") // TODO: move this to database; allow users to add images (permission system?)
				if err != nil {
					return &commandOutput{response: "Error opening file"}

				}
				return &commandOutput{file: file}
			},
		},

	/*
		&command{
			name:             "User stats",
			description:      "Displays the statistics of the user.",
			usage:            "stats [user]", // TODO: implement pinging users
			verbs:            []string{"stats"},
			requiresDatabase: true,
			function: func(args []string, channel *discordgo.Channel, msgEvent *discordgo.MessageCreate, discordSession *discordgo.Session) *commandOutput {

				if len(args) > 0 {
					if len(msgEvent.Mentions) > 0 {
						// User tagged someone else
						taggedUser := msgEvent.Mentions[0] // only the first one

						userDb, err := GetUser(taggedUser, false)
						if err != nil {
							return &commandOutput{response: "That user doesn't exist in the database yet. They need to chat some!"}
						}

						posts := userDb.Val()["posts"]
						return &commandOutput{response: taggedUser.Username + " has made " + posts + " posts!"} // TODO: format as embed, show more values
					}
					// user didn't tag anyone
					// TODO: accept aliases as well as mentions
					return &commandOutput{response: "To see someone's stats, tag the person directly!"}
				}
				// User's own stats
				userDb, err := GetUser(msgEvent.Author, false)
				if err != nil {
					return &commandOutput{response: "You don't exist in the database yet. You need to chat some!"}
				}
				posts := userDb.Val()["posts"]
				return &commandOutput{response: "You have made " + posts + " posts!"} // TODO: format as embed, show more values
			},
		},
	*/
	)

	// Map for matching verbs to commands
	commandMap := make(map[string]*command)

	// Loop through commandList to get each verb
	for _, cmd := range commandList {
		for _, verb := range cmd.verbs {
			commandMap[verb] = cmd
			DebugPrint("Mapped '" + verb + "' to '" + cmd.name + "'")
		}
	}

	return commandMap
}
<commit_msg>add exec command<commit_after>package main

import (
	"fmt"
	"github.com/bwmarrin/discordgo"
	"io"
	"os"
	"os/exec"
)

// generic command struct which contains name, description, and a function
type command struct {
	name             string                                                                                // human-readable name of the command
	description      string                                                                                // description of command's function
	usage            string                                                                                // example of how to correctly use command - [] for optional arguments, <> for required arguments
	verbs            []string                                                                                // all verbs which are mapped to the same command
	requiresDatabase bool                                                                                // does this command require database access?
	function         func([]string, *discordgo.Channel, *discordgo.MessageCreate, *discordgo.Session) *commandOutput // function which receives a slice of arguments and returns a string to display to the user
}

// output returned by all command functions, can contain a file to be uploaded
type commandOutput struct {
	response string
	file     io.Reader
	embed    *discordgo.MessageEmbed
}

func initCommands() map[string]*command {
	commandList := []*command{}

	commandList = append(commandList,

		// Define all commands here in the order they will be displayed by the help command
		// The 'usage' field should use the default verb
		// Do not include the command prefix

		&command{
			name:             "Display help",
			description:      "Lists all commands and their purposes.\nCan also display detailed info about a given command.",
			usage:            "help [verb]",
			verbs:            []string{"help", "commands"},
			requiresDatabase: false,
			function: func(args []string, channel *discordgo.Channel, msgEvent *discordgo.MessageCreate, discordSession *discordgo.Session) *commandOutput {

				DebugPrint("Running help command.")

				if len(args) <= 0 {

					DebugPrint("No arguments; listing commands.")

					embed := NewEmbed().
						SetTitle("Source").
						SetAuthor("Sunbot " + version).
						//SetDescription("Database enabled: " + strconv.FormatBool(redisEnabled)).
						SetURL("https://github.com/techniponi/sunbot").
						SetImage(discordSession.State.User.AvatarURL("128"))

					for _, cmd := range commandList {
						/*
						if cmd.requiresDatabase && !redisEnabled {
							// Database is not enabled, this command needs it
						} else {
						*/
							embed.AddField(cmd.name, "`"+cfg.DefaultPrefix+cmd.usage+"`")
						//}
					}

					return &commandOutput{embed: embed.MessageEmbed}
				}

				DebugPrint("Verb was given...")

				// check if command exists
				if cmd, ok := commands[args[0]]; ok {

					embed := NewEmbed().
						SetTitle(cmd.name).
						SetDescription(cmd.description).
						AddField("Usage", "`"+cfg.DefaultPrefix+cmd.usage+"`")

					DebugPrint("Providing help for given verb.")

					// compile verbs
					verbOutput := ""
					for index, verb := range cmd.verbs {
						// don't add a comma if it's the last one
						if index == (len(cmd.verbs) - 1) {
							verbOutput += "`" + cfg.DefaultPrefix + verb + "`"
						} else {
							verbOutput += "`" + cfg.DefaultPrefix + verb + "`, "
						}
					}
					embed.AddField("Verbs", verbOutput)
					return &commandOutput{embed: embed.MessageEmbed}
				}
				DebugPrint("Given verb was not found.")
				return &commandOutput{response: "That isn't a valid command."}
			},
		},

		&command{
			name:             "Derpibooru search",
			description:      "Searches Derpibooru with the given tags as the query, chooses a random result to display.\nUse commas to separate tags like you would on the website.",
			usage:            "derpi <tags>",
			verbs:            []string{"derpi", "db", "derpibooru"},
			requiresDatabase: false,
			function: func(args []string, channel *discordgo.Channel, msgEvent *discordgo.MessageCreate, discordSession *discordgo.Session) *commandOutput {
				if len(args) < 1 {
					DebugPrint("User ran derpibooru command with no tags given.")
					return &commandOutput{response: "Error: no tags specified"}
				}
				DebugPrint("User is running derpibooru command...")

				searchQuery := ""

				for _, arg := range args {
					searchQuery += arg + " "
				}

				// enforce 'safe' tag if channel is not nsfw
				if !channel.NSFW {
					DebugPrint("Channel #" + channel.Name + " is SFW, adding safe tag...")
					searchQuery += ",safe"
				}

				DebugPrint("Searching with tags:\n" + searchQuery)

				// use derpibooru.go to perform search
				results, err := DerpiSearchWithTags(searchQuery, cfg.DerpiApiKey)
				if err != nil {
					fmt.Println(err)
					return &commandOutput{response: "Error: " + err.Error()}
				}

				// check for results
				if len(results.Search) <= 0 {
					DebugPrint("Derpibooru returned no results.")
					return &commandOutput{response: "Error: no results."}
				}
				DebugPrint("Derpibooru returned results; parsed successfully.")
				// pick one randomly
				output := "http:" + results.Search[RandomRange(0, len(results.Search))].Image

				return &commandOutput{response: output}
			},
		},

		&command{
			name: "Exec",
			description: "Execute a shell command on my server.",
			usage: "exec <command>",
			verbs: []string{"exec"},
			requiresDatabase: false,
			function: func(args []string, channel *discordgo.Channel, msgEvent *discordgo.MessageCreate, discordSession *discordgo.Session) *commandOutput {

				hasPermission := false

				// get user object
				DebugPrint("Getting user object")
				user, err := discordSession.State.Member(channel.GuildID, msgEvent.Author.ID)
				if err != nil {
					fmt.Println(err)
					return &commandOutput{response: "Error getting user from exec command"}
				}

				// get roles from that user
				DebugPrint("Getting user's roles")
				for _, roleID := range user.Roles {
					role, err := discordSession.State.Role(channel.GuildID, roleID)
					if err != nil {
						fmt.Println(err)
						return &commandOutput{response: "Error getting roles from user"}
					}

					DebugPrint("Checking for admin permission")
					if role.Permissions&discordgo.PermissionAdministrator == 0 {
						hasPermission = true
					}
				}

				if hasPermission {

					// convert slice to single string
					fullCommand := ""
					for _, arg := range args {
						fullCommand += arg + " "
					}

					cmd := exec.Command("/bin/bash", "-c", fullCommand)
					stdout, err := cmd.Output()

					if err != nil {
						fmt.Println(err)
						return &commandOutput{response: "Error running command"}
					}

					return &commandOutput{
						response: "```sh\n" + string(stdout) + "\n```",
					}
				}else{
					return &commandOutput{response: "Sorry, but only administrators can use that command."}
				}
			},
		},

		&command{
			name:             "Gay",
			description:      "Posts a very gay image.",
			usage:            "gay",
			verbs:            []string{"gay"},
			requiresDatabase: false,
			function: func(args []string, channel *discordgo.Channel, msgEvent *discordgo.MessageCreate, discordSession *discordgo.Session) *commandOutput {
				file, err := os.Open("img/gaybats.png") // TODO: move this to database; allow users to add images (permission system?)
				if err != nil {
					fmt.Println(err)
					return &commandOutput{response: "Error opening file"}

				}
				return &commandOutput{file: file}
			},
		},

	/*
		&command{
			name:             "User stats",
			description:      "Displays the statistics of the user.",
			usage:            "stats [user]", // TODO: implement pinging users
			verbs:            []string{"stats"},
			requiresDatabase: true,
			function: func(args []string, channel *discordgo.Channel, msgEvent *discordgo.MessageCreate, discordSession *discordgo.Session) *commandOutput {

				if len(args) > 0 {
					if len(msgEvent.Mentions) > 0 {
						// User tagged someone else
						taggedUser := msgEvent.Mentions[0] // only the first one

						userDb, err := GetUser(taggedUser, false)
						if err != nil {
							return &commandOutput{response: "That user doesn't exist in the database yet. They need to chat some!"}
						}

						posts := userDb.Val()["posts"]
						return &commandOutput{response: taggedUser.Username + " has made " + posts + " posts!"} // TODO: format as embed, show more values
					}
					// user didn't tag anyone
					// TODO: accept aliases as well as mentions
					return &commandOutput{response: "To see someone's stats, tag the person directly!"}
				}
				// User's own stats
				userDb, err := GetUser(msgEvent.Author, false)
				if err != nil {
					return &commandOutput{response: "You don't exist in the database yet. You need to chat some!"}
				}
				posts := userDb.Val()["posts"]
				return &commandOutput{response: "You have made " + posts + " posts!"} // TODO: format as embed, show more values
			},
		},
	*/
	)

	// Map for matching verbs to commands
	commandMap := make(map[string]*command)

	// Loop through commandList to get each verb
	for _, cmd := range commandList {
		for _, verb := range cmd.verbs {
			commandMap[verb] = cmd
			DebugPrint("Mapped '" + verb + "' to '" + cmd.name + "'")
		}
	}

	return commandMap
}
<|endoftext|>


<commit_before>/*
Copyright 2014 CoreOS Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package store

import (
	"fmt"
	"math/rand"
	"testing"
)

func BenchmarkStoreSet(b *testing.B) {
	s := newStore()
	b.StopTimer()
	kvs := generateNRandomKV(b.N)
	b.StartTimer()

	for i := 0; i < b.N; i++ {
		_, err := s.Set(kvs[i][0], false, kvs[i][1], Permanent)
		if err != nil {
			panic(err)
		}
	}
}

func generateNRandomKV(n int) [][]string {
	kvs := make([][]string, n)

	for i := 0; i < n; i++ {
		kvs[i] = make([]string, 2)
		kvs[i][0] = fmt.Sprintf("/%d/%d/%d",
			rand.Int()%100, rand.Int()%100, rand.Int()%100)
		kvs[i][1] = fmt.Sprint(i)
	}

	return kvs
}
<commit_msg>add a setWithJson test<commit_after>/*
Copyright 2014 CoreOS Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package store

import (
	"encoding/json"
	"fmt"
	"math/rand"
	"testing"
)

func BenchmarkStoreSet(b *testing.B) {
	s := newStore()
	b.StopTimer()
	kvs := generateNRandomKV(b.N)
	b.StartTimer()

	for i := 0; i < b.N; i++ {
		_, err := s.Set(kvs[i][0], false, kvs[i][1], Permanent)
		if err != nil {
			panic(err)
		}
	}
}

func BenchmarkStoreSetWithJson(b *testing.B) {
	s := newStore()
	b.StopTimer()
	kvs := generateNRandomKV(b.N)
	b.StartTimer()

	for i := 0; i < b.N; i++ {
		resp, err := s.Set(kvs[i][0], false, kvs[i][1], Permanent)
		if err != nil {
			panic(err)
		}
		_, err = json.Marshal(resp)
		if err != nil {
			panic(err)
		}
	}
}

func generateNRandomKV(n int) [][]string {
	kvs := make([][]string, n)

	for i := 0; i < n; i++ {
		kvs[i] = make([]string, 2)
		kvs[i][0] = fmt.Sprintf("/%d/%d/%d",
			rand.Int()%100, rand.Int()%100, rand.Int()%100)
		kvs[i][1] = fmt.Sprint(i)
	}

	return kvs
}
<|endoftext|>


<commit_before>package main

import (
	"os"

	auditFile "github.com/hashicorp/vault/builtin/audit/file"

	credAppId "github.com/hashicorp/vault/builtin/credential/app-id"
	credGitHub "github.com/hashicorp/vault/builtin/credential/github"

	"github.com/hashicorp/vault/builtin/logical/aws"
	"github.com/hashicorp/vault/builtin/logical/consul"

	"github.com/hashicorp/vault/audit"
	tokenDisk "github.com/hashicorp/vault/builtin/token/disk"
	"github.com/hashicorp/vault/command"
	"github.com/hashicorp/vault/logical"
	"github.com/mitchellh/cli"
)

// Commands is the mapping of all the available Vault commands. CommandsInclude
// are the commands to include for help.
var Commands map[string]cli.CommandFactory
var CommandsInclude []string

func init() {
	ui := &cli.BasicUi{
		Writer:      os.Stdout,
		ErrorWriter: os.Stderr,
	}
	meta := command.Meta{Ui: ui}

	Commands = map[string]cli.CommandFactory{
		"help": func() (cli.Command, error) {
			return &command.HelpCommand{
				Meta: meta,
			}, nil
		},

		"auth": func() (cli.Command, error) {
			return &command.AuthCommand{
				Meta: meta,
				Handlers: map[string]command.AuthHandler{
					"github": &credGitHub.CLIHandler{},
				},
			}, nil
		},

		"auth-enable": func() (cli.Command, error) {
			return &command.AuthEnableCommand{
				Meta: meta,
			}, nil
		},

		"auth-disable": func() (cli.Command, error) {
			return &command.AuthDisableCommand{
				Meta: meta,
			}, nil
		},

		"policies": func() (cli.Command, error) {
			return &command.PolicyListCommand{
				Meta: meta,
			}, nil
		},

		"policy-write": func() (cli.Command, error) {
			return &command.PolicyWriteCommand{
				Meta: meta,
			}, nil
		},

		"read": func() (cli.Command, error) {
			return &command.ReadCommand{
				Meta: meta,
			}, nil
		},

		"write": func() (cli.Command, error) {
			return &command.WriteCommand{
				Meta: meta,
			}, nil
		},

		"delete": func() (cli.Command, error) {
			return &command.DeleteCommand{
				Meta: meta,
			}, nil
		},

		"revoke": func() (cli.Command, error) {
			return &command.RevokeCommand{
				Meta: meta,
			}, nil
		},

		"seal": func() (cli.Command, error) {
			return &command.SealCommand{
				Meta: meta,
			}, nil
		},

		"seal-status": func() (cli.Command, error) {
			return &command.SealStatusCommand{
				Meta: meta,
			}, nil
		},

		"unseal": func() (cli.Command, error) {
			return &command.UnsealCommand{
				Meta: meta,
			}, nil
		},

		"init": func() (cli.Command, error) {
			return &command.InitCommand{
				Meta: meta,
			}, nil
		},

		"server": func() (cli.Command, error) {
			return &command.ServerCommand{
				Meta: meta,
				AuditBackends: map[string]audit.Factory{
					"file": auditFile.Factory,
				},
				CredentialBackends: map[string]logical.Factory{
					"app-id": credAppId.Factory,
					"github": credGitHub.Factory,
				},
				LogicalBackends: map[string]logical.Factory{
					"aws":    aws.Factory,
					"consul": consul.Factory,
				},
			}, nil
		},

		"mount": func() (cli.Command, error) {
			return &command.MountCommand{
				Meta: meta,
			}, nil
		},

		"mounts": func() (cli.Command, error) {
			return &command.MountsCommand{
				Meta: meta,
			}, nil
		},

		"remount": func() (cli.Command, error) {
			return &command.RemountCommand{
				Meta: meta,
			}, nil
		},

		"unmount": func() (cli.Command, error) {
			return &command.UnmountCommand{
				Meta: meta,
			}, nil
		},

		"version": func() (cli.Command, error) {
			ver := Version
			rel := VersionPrerelease
			if GitDescribe != "" {
				ver = GitDescribe
			}
			if GitDescribe == "" && rel == "" {
				rel = "dev"
			}

			return &command.VersionCommand{
				Revision:          GitCommit,
				Version:           ver,
				VersionPrerelease: rel,
				Ui:                ui,
			}, nil
		},
	}

	// Build the commands to include in the help now
	CommandsInclude = make([]string, 0, len(Commands))
	for k, _ := range Commands {
		CommandsInclude = append(CommandsInclude, k)
	}

	// The commands below are hidden from the help output
	Commands["token-disk"] = func() (cli.Command, error) {
		return &tokenDisk.Command{}, nil
	}
}
<commit_msg>shuffling some bits<commit_after>package main

import (
	"os"

	auditFile "github.com/hashicorp/vault/builtin/audit/file"

	credAppId "github.com/hashicorp/vault/builtin/credential/app-id"
	credGitHub "github.com/hashicorp/vault/builtin/credential/github"

	"github.com/hashicorp/vault/builtin/logical/aws"
	"github.com/hashicorp/vault/builtin/logical/consul"

	"github.com/hashicorp/vault/audit"
	tokenDisk "github.com/hashicorp/vault/builtin/token/disk"
	"github.com/hashicorp/vault/command"
	"github.com/hashicorp/vault/logical"
	"github.com/mitchellh/cli"
)

// Commands is the mapping of all the available Vault commands. CommandsInclude
// are the commands to include for help.
var Commands map[string]cli.CommandFactory
var CommandsInclude []string

func init() {
	ui := &cli.BasicUi{
		Writer:      os.Stdout,
		ErrorWriter: os.Stderr,
	}
	meta := command.Meta{Ui: ui}

	Commands = map[string]cli.CommandFactory{
		"init": func() (cli.Command, error) {
			return &command.InitCommand{
				Meta: meta,
			}, nil
		},

		"server": func() (cli.Command, error) {
			return &command.ServerCommand{
				Meta: meta,
				AuditBackends: map[string]audit.Factory{
					"file": auditFile.Factory,
				},
				CredentialBackends: map[string]logical.Factory{
					"app-id": credAppId.Factory,
					"github": credGitHub.Factory,
				},
				LogicalBackends: map[string]logical.Factory{
					"aws":    aws.Factory,
					"consul": consul.Factory,
				},
			}, nil
		},

		"help": func() (cli.Command, error) {
			return &command.HelpCommand{
				Meta: meta,
			}, nil
		},

		"auth": func() (cli.Command, error) {
			return &command.AuthCommand{
				Meta: meta,
				Handlers: map[string]command.AuthHandler{
					"github": &credGitHub.CLIHandler{},
				},
			}, nil
		},

		"auth-enable": func() (cli.Command, error) {
			return &command.AuthEnableCommand{
				Meta: meta,
			}, nil
		},

		"auth-disable": func() (cli.Command, error) {
			return &command.AuthDisableCommand{
				Meta: meta,
			}, nil
		},

		"policies": func() (cli.Command, error) {
			return &command.PolicyListCommand{
				Meta: meta,
			}, nil
		},

		"policy-write": func() (cli.Command, error) {
			return &command.PolicyWriteCommand{
				Meta: meta,
			}, nil
		},

		"read": func() (cli.Command, error) {
			return &command.ReadCommand{
				Meta: meta,
			}, nil
		},

		"write": func() (cli.Command, error) {
			return &command.WriteCommand{
				Meta: meta,
			}, nil
		},

		"delete": func() (cli.Command, error) {
			return &command.DeleteCommand{
				Meta: meta,
			}, nil
		},

		"revoke": func() (cli.Command, error) {
			return &command.RevokeCommand{
				Meta: meta,
			}, nil
		},

		"seal": func() (cli.Command, error) {
			return &command.SealCommand{
				Meta: meta,
			}, nil
		},

		"seal-status": func() (cli.Command, error) {
			return &command.SealStatusCommand{
				Meta: meta,
			}, nil
		},

		"unseal": func() (cli.Command, error) {
			return &command.UnsealCommand{
				Meta: meta,
			}, nil
		},

		"mount": func() (cli.Command, error) {
			return &command.MountCommand{
				Meta: meta,
			}, nil
		},

		"mounts": func() (cli.Command, error) {
			return &command.MountsCommand{
				Meta: meta,
			}, nil
		},

		"remount": func() (cli.Command, error) {
			return &command.RemountCommand{
				Meta: meta,
			}, nil
		},

		"unmount": func() (cli.Command, error) {
			return &command.UnmountCommand{
				Meta: meta,
			}, nil
		},

		"version": func() (cli.Command, error) {
			ver := Version
			rel := VersionPrerelease
			if GitDescribe != "" {
				ver = GitDescribe
			}
			if GitDescribe == "" && rel == "" {
				rel = "dev"
			}

			return &command.VersionCommand{
				Revision:          GitCommit,
				Version:           ver,
				VersionPrerelease: rel,
				Ui:                ui,
			}, nil
		},
	}

	// Build the commands to include in the help now
	CommandsInclude = make([]string, 0, len(Commands))
	for k, _ := range Commands {
		CommandsInclude = append(CommandsInclude, k)
	}

	// The commands below are hidden from the help output
	Commands["token-disk"] = func() (cli.Command, error) {
		return &tokenDisk.Command{}, nil
	}
}
<|endoftext|>