<commit_before>package command

import (
	"bufio"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"os/exec"
	"path/filepath"
	"strings"
)

// Command is our structured data/object for Command
type Command struct {
	alias      string
	command    string
	verbose    bool
	workingdir string
}

const pathDrush = "/usr/local/bin/drush"

// NewDrushCommand creates a new container for []Command objects
func NewDrushCommand() *Command {
	return &Command{}
}

// Set changes all given values for a Drush command object.
func (drush *Command) Set(alias string, command string, verbose bool) {
	drush.alias = alias
	drush.command = command
	drush.verbose = verbose
	drush.workingdir = "."
}

// GetWorkingDir returns the specified working directory used with executed Drush commands.
func (drush *Command) GetWorkingDir() string {
	return drush.workingdir
}

// SetWorkingDir sets the specified working directory used with executed Drush commands.
func (drush *Command) SetWorkingDir(value string) {
	drush.workingdir = value
}

// GetAlias returns the alias used to executed Drush commands.
func (drush *Command) GetAlias() string {
	return drush.alias
}

// SetAlias changes the alias used to executed Drush commands.
func (drush *Command) SetAlias(value string) {
	drush.alias = value
}

// GetCommand returns the command string on executed Drush commands.
func (drush *Command) GetCommand() string {
	return drush.command
}

// SetCommand changes the command string on executed Drush commands.
func (drush *Command) SetCommand(value string) {
	drush.command = value
}

// GetVerbose returns the verbosity setting on executed Drush commands.
func (drush *Command) GetVerbose() bool {
	return drush.verbose
}

// SetVerbose changes the verbosity setting on executed Drush commands.
func (drush *Command) SetVerbose(value bool) {
	drush.verbose = value
}

// LiveOutput returns, and prints the live output of the executing program
// This will wait for completion before proceeding.
func (drush *Command) LiveOutput() error {
	if strings.Contains(drush.alias, "@") == true {
		drush.alias = strings.Replace(drush.alias, "@", "", -1)
	}
	if drush.alias != "" {
		drush.alias = fmt.Sprintf("@%v", drush.alias)
	}
	if drush.verbose == true {
		drush.alias = fmt.Sprintf("%v --verbose", drush.alias)
	}
	args := fmt.Sprintf("%v %v", drush.alias, drush.command)

	comm := new(exec.Cmd)
	comm = exec.Command("sh", "-c", "cd "+drush.workingdir+" && "+pathDrush+" "+args)
	Pipe, _ := comm.StderrPipe()
	scanner := bufio.NewScanner(Pipe)
	go func() {
		for scanner.Scan() {
			if strings.Contains(scanner.Text(), "[error]") || strings.Contains(scanner.Text(), "[warning]") {
				log.Warnf("%s", scanner.Text())
			} else {
				log.Infof("%s", scanner.Text())
			}
		}
	}()
	err := comm.Start()
	err = comm.Wait()
	return err
}

// Output gets the output from a single Command object, does not support []Command items.
func (drush *Command) Output() ([]string, error) {
	comm, err := drush.Run()
	response := filepath.SplitList(string(comm))
	return response, err
}

// CombinedOutput will return the CombinedOutput of a command.
func (drush *Command) CombinedOutput() ([]byte, error) {
	if strings.Contains(drush.alias, "@") == true {
		drush.alias = strings.Replace(drush.alias, "@", "", -1)
	}
	if drush.alias != "" {
		drush.alias = fmt.Sprintf("@%v", drush.alias)
	}
	if drush.verbose == true {
		drush.alias = fmt.Sprintf("%v --verbose", drush.alias)
	}
	args := fmt.Sprintf("%v %v", drush.alias, drush.command)
	if drush.GetWorkingDir() != "." {
		comm, err := exec.Command("sh", "-c", "cd "+drush.workingdir+" && "+pathDrush+" "+args).CombinedOutput()
		return comm, err
	}
	comm, err := exec.Command("sh", "-c", pathDrush+" "+args).CombinedOutput()
	return comm, err
}

// Run runs an individual Command object, does not support []Command items.
func (drush *Command) Run() ([]byte, error) {
	if strings.Contains(drush.alias, "@") == true {
		drush.alias = strings.Replace(drush.alias, "@", "", -1)
	}
	if drush.alias != "" {
		drush.alias = fmt.Sprintf("@%v", drush.alias)
	}
	if drush.verbose == true {
		drush.alias = fmt.Sprintf("%v --verbose", drush.alias)
	}
	args := fmt.Sprintf("%v %v", drush.alias, drush.command)
	if drush.GetWorkingDir() != "." {
		comm, err := exec.Command("sh", "-c", "cd "+drush.workingdir+" && "+pathDrush+" "+args).CombinedOutput()
		return comm, err
	}
	comm, err := exec.Command("sh", "-c", pathDrush+" "+args).CombinedOutput()
	return comm, err
}

// DrushDatabaseSync executes a database synchronisation task from a source to destination with the use of Drush.
func DrushDatabaseSync(srcAlias, destAlias string) {
	/* So our binary and this function combined support two-way traffic...  */
	x := NewDrushCommand()
	srcAlias = strings.Replace(srcAlias, "@", "", -1)
	destAlias = strings.Replace(destAlias, "@", "", -1)
	x.Set("", fmt.Sprintf("sql-sync @%v @%v -y", srcAlias, destAlias), true)
	_, err := x.Output()
	if err == nil {
		log.Infoln("Syncronised databases complete.")
	} else {
		log.Errorln("Could not syncronise databases.")
	}
}

// DrushFilesSync executes a file synchronisation task from a source to destination with the use of Drush.
func DrushFilesSync(srcAlias, destAlias string) {
	x := NewDrushCommand()
	srcAlias = strings.Replace(srcAlias, "@", "", -1)
	destAlias = strings.Replace(destAlias, "@", "", -1)
	x.Set("", fmt.Sprintf("--yes rsync --exclude-other-sites --exclude-conf @%v:%%files @%v:%%files", srcAlias, destAlias), true)
	_, err := x.Output()
	if err == nil {
		log.Infoln("Synced public file system.")
	} else {
		log.Warnln("Public file system has not been synced.")
	}
	x.Set("", fmt.Sprintf("--yes rsync --exclude-other-sites --exclude-conf @%v:%%private @%v:%%private", srcAlias, destAlias), true)
	_, err = x.Output()
	if err == nil {
		log.Infoln("Synced private file system.")
	} else {
		log.Warnln("Private file system has not been synced.")
	}
}

// DrushClearCache performs a cache clear task on an input site alias with the use of Drush.
func DrushClearCache(alias string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set(alias, "cc all", false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not clear caches.", err)
	} else {
		log.Infoln("Caches cleared.")
	}
}

// DrushRebuildRegistry performs a registry rebuild task on an input site alias with the use of Drush.
func DrushRebuildRegistry(alias string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set(alias, "rr", false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not rebuild registry.", err)
	} else {
		log.Infoln("Rebuilt registry.")
	}
}

// DrushUpdateDatabase performs a database update task on an input site alias with the use of Drush.
func DrushUpdateDatabase(alias string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set(alias, "updb -y", false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not update database.", err)
	} else {
		log.Infoln("Updated database where possible.")
	}
}

// DrushDownloadToPath performs a database update task on an input site alias with the use of Drush.
func DrushDownloadToPath(path, project string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set("", "pm-download --yes "+project+" --destination="+path, false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not download module ", project, err)
	} else {
		log.Infoln("Downloaded module", project)
	}
}

// DrushDownloadToAlias performs a database update task on an input site alias with the use of Drush.
func DrushDownloadToAlias(alias, project string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set(alias, "pm-download --yes "+project, false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not download module ", project, err)
	} else {
		log.Infoln("Downloaded module", project)
	}
}

// DrushVariableSet Runs drush vset with a given variable name and value.
func DrushVariableSet(alias, variable_name, variable_value string) {
	srcAlias := strings.Replace(alias, "@", "", -1)
	x := NewDrushCommand()
	x.Set(srcAlias, fmt.Sprintf("vset %v %v", variable_name, variable_value), true)
	drushOut, err := x.Output()
	if err == nil {
		log.Infof("Succesfully set %v to %v via Drush\n", variable_name, variable_value)
	} else {
		log.Errorf("Could not set %v to %v via Drush: %v\n", variable_name, variable_value, drushOut)
	}
}<commit_msg>#22: Add a rather primitive vget function<commit_after>package command

import (
	"bufio"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"os/exec"
	"path/filepath"
	"strings"
)

// Command is our structured data/object for Command
type Command struct {
	alias      string
	command    string
	verbose    bool
	workingdir string
}

const pathDrush = "/usr/local/bin/drush"

// NewDrushCommand creates a new container for []Command objects
func NewDrushCommand() *Command {
	return &Command{}
}

// Set changes all given values for a Drush command object.
func (drush *Command) Set(alias string, command string, verbose bool) {
	drush.alias = alias
	drush.command = command
	drush.verbose = verbose
	drush.workingdir = "."
}

// GetWorkingDir returns the specified working directory used with executed Drush commands.
func (drush *Command) GetWorkingDir() string {
	return drush.workingdir
}

// SetWorkingDir sets the specified working directory used with executed Drush commands.
func (drush *Command) SetWorkingDir(value string) {
	drush.workingdir = value
}

// GetAlias returns the alias used to executed Drush commands.
func (drush *Command) GetAlias() string {
	return drush.alias
}

// SetAlias changes the alias used to executed Drush commands.
func (drush *Command) SetAlias(value string) {
	drush.alias = value
}

// GetCommand returns the command string on executed Drush commands.
func (drush *Command) GetCommand() string {
	return drush.command
}

// SetCommand changes the command string on executed Drush commands.
func (drush *Command) SetCommand(value string) {
	drush.command = value
}

// GetVerbose returns the verbosity setting on executed Drush commands.
func (drush *Command) GetVerbose() bool {
	return drush.verbose
}

// SetVerbose changes the verbosity setting on executed Drush commands.
func (drush *Command) SetVerbose(value bool) {
	drush.verbose = value
}

// LiveOutput returns, and prints the live output of the executing program
// This will wait for completion before proceeding.
func (drush *Command) LiveOutput() error {
	if strings.Contains(drush.alias, "@") == true {
		drush.alias = strings.Replace(drush.alias, "@", "", -1)
	}
	if drush.alias != "" {
		drush.alias = fmt.Sprintf("@%v", drush.alias)
	}
	if drush.verbose == true {
		drush.alias = fmt.Sprintf("%v --verbose", drush.alias)
	}
	args := fmt.Sprintf("%v %v", drush.alias, drush.command)

	comm := new(exec.Cmd)
	comm = exec.Command("sh", "-c", "cd "+drush.workingdir+" && "+pathDrush+" "+args)
	Pipe, _ := comm.StderrPipe()
	scanner := bufio.NewScanner(Pipe)
	go func() {
		for scanner.Scan() {
			if strings.Contains(scanner.Text(), "[error]") || strings.Contains(scanner.Text(), "[warning]") {
				log.Warnf("%s", scanner.Text())
			} else {
				log.Infof("%s", scanner.Text())
			}
		}
	}()
	err := comm.Start()
	err = comm.Wait()
	return err
}

// Output gets the output from a single Command object, does not support []Command items.
func (drush *Command) Output() ([]string, error) {
	comm, err := drush.Run()
	response := filepath.SplitList(string(comm))
	return response, err
}

// CombinedOutput will return the CombinedOutput of a command.
func (drush *Command) CombinedOutput() ([]byte, error) {
	if strings.Contains(drush.alias, "@") == true {
		drush.alias = strings.Replace(drush.alias, "@", "", -1)
	}
	if drush.alias != "" {
		drush.alias = fmt.Sprintf("@%v", drush.alias)
	}
	if drush.verbose == true {
		drush.alias = fmt.Sprintf("%v --verbose", drush.alias)
	}
	args := fmt.Sprintf("%v %v", drush.alias, drush.command)
	if drush.GetWorkingDir() != "." {
		comm, err := exec.Command("sh", "-c", "cd "+drush.workingdir+" && "+pathDrush+" "+args).CombinedOutput()
		return comm, err
	}
	comm, err := exec.Command("sh", "-c", pathDrush+" "+args).CombinedOutput()
	return comm, err
}

// Run runs an individual Command object, does not support []Command items.
func (drush *Command) Run() ([]byte, error) {
	if strings.Contains(drush.alias, "@") == true {
		drush.alias = strings.Replace(drush.alias, "@", "", -1)
	}
	if drush.alias != "" {
		drush.alias = fmt.Sprintf("@%v", drush.alias)
	}
	if drush.verbose == true {
		drush.alias = fmt.Sprintf("%v --verbose", drush.alias)
	}
	args := fmt.Sprintf("%v %v", drush.alias, drush.command)
	if drush.GetWorkingDir() != "." {
		comm, err := exec.Command("sh", "-c", "cd "+drush.workingdir+" && "+pathDrush+" "+args).CombinedOutput()
		return comm, err
	}
	comm, err := exec.Command("sh", "-c", pathDrush+" "+args).CombinedOutput()
	return comm, err
}

// DrushDatabaseSync executes a database synchronisation task from a source to destination with the use of Drush.
func DrushDatabaseSync(srcAlias, destAlias string) {
	/* So our binary and this function combined support two-way traffic...  */
	x := NewDrushCommand()
	srcAlias = strings.Replace(srcAlias, "@", "", -1)
	destAlias = strings.Replace(destAlias, "@", "", -1)
	x.Set("", fmt.Sprintf("sql-sync @%v @%v -y", srcAlias, destAlias), true)
	_, err := x.Output()
	if err == nil {
		log.Infoln("Syncronised databases complete.")
	} else {
		log.Errorln("Could not syncronise databases.")
	}
}

// DrushFilesSync executes a file synchronisation task from a source to destination with the use of Drush.
func DrushFilesSync(srcAlias, destAlias string) {
	x := NewDrushCommand()
	srcAlias = strings.Replace(srcAlias, "@", "", -1)
	destAlias = strings.Replace(destAlias, "@", "", -1)
	x.Set("", fmt.Sprintf("--yes rsync --exclude-other-sites --exclude-conf @%v:%%files @%v:%%files", srcAlias, destAlias), true)
	_, err := x.Output()
	if err == nil {
		log.Infoln("Synced public file system.")
	} else {
		log.Warnln("Public file system has not been synced.")
	}
	x.Set("", fmt.Sprintf("--yes rsync --exclude-other-sites --exclude-conf @%v:%%private @%v:%%private", srcAlias, destAlias), true)
	_, err = x.Output()
	if err == nil {
		log.Infoln("Synced private file system.")
	} else {
		log.Warnln("Private file system has not been synced.")
	}
}

// DrushClearCache performs a cache clear task on an input site alias with the use of Drush.
func DrushClearCache(alias string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set(alias, "cc all", false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not clear caches.", err)
	} else {
		log.Infoln("Caches cleared.")
	}
}

// DrushRebuildRegistry performs a registry rebuild task on an input site alias with the use of Drush.
func DrushRebuildRegistry(alias string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set(alias, "rr", false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not rebuild registry.", err)
	} else {
		log.Infoln("Rebuilt registry.")
	}
}

// DrushUpdateDatabase performs a database update task on an input site alias with the use of Drush.
func DrushUpdateDatabase(alias string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set(alias, "updb -y", false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not update database.", err)
	} else {
		log.Infoln("Updated database where possible.")
	}
}

// DrushDownloadToPath performs a database update task on an input site alias with the use of Drush.
func DrushDownloadToPath(path, project string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set("", "pm-download --yes "+project+" --destination="+path, false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not download module ", project, err)
	} else {
		log.Infoln("Downloaded module", project)
	}
}

// DrushDownloadToAlias performs a database update task on an input site alias with the use of Drush.
func DrushDownloadToAlias(alias, project string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set(alias, "pm-download --yes "+project, false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not download module ", project, err)
	} else {
		log.Infoln("Downloaded module", project)
	}
}

// DrushVariableSet Runs drush vset with a given variable name and value.
func DrushVariableSet(alias, variable_name, variable_value string) {
	srcAlias := strings.Replace(alias, "@", "", -1)
	x := NewDrushCommand()
	x.Set(srcAlias, fmt.Sprintf("vset %v %v", variable_name, variable_value), true)
	drushOut, err := x.Output()
	if err == nil {
		log.Infof("Succesfully set %v to %v via Drush\n", variable_name, variable_value)
	} else {
		log.Errorf("Could not set %v to %v via Drush: %v\n", variable_name, variable_value, drushOut)
	}
}

// DrushVariableGet Runs drush vset with a given variable name and value.
func DrushVariableGet(alias, variable_name string) string {
	srcAlias := strings.Replace(alias, "@", "", -1)
	x := NewDrushCommand()
	x.Set(srcAlias, fmt.Sprintf("vget --exact %v", variable_name), false)
	drushOut, err := x.Output()
	drushOutString := fmt.Sprintf("%s", drushOut)
	if strings.Contains(drushOutString, "No matching variable found") {
		log.Warnf("Variable %v was not found", variable_name)
	} else if err == nil {
		log.Infof("Succesfully retreived %v via Drush\n", variable_name)
		drushOutString = strings.Replace(drushOutString, "[", "", -1)
		drushOutString = strings.Replace(drushOutString, "]", "", -1)
		drushOutString = strings.Replace(drushOutString, "\n", "", -1)
		return drushOutString
	} else {
		log.Errorf("Could not retreived %v via Drush: %v\n", variable_name, drushOut)
	}
	return ""
}<|endoftext|>


<commit_before>package command

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"reflect"
	"sync"
)

type CommandsInfo struct {
	Port int
}

type CommandInfo map[string]*struct {
	Token string
}

type CommandRuntimeInfo struct {
	Token   string
	Handler interface{}
}

type CommandServer struct {
	Common   CommandsInfo
	Command  CommandInfo
	Handlers map[string]*CommandRuntimeInfo
}

type Color struct {
	r, g, b uint8
}

func (color Color) MarshalJSON() ([]byte, error) {
	return []byte(fmt.Sprintf(`"`+"#%02x%02x%02x"+`"`, color.r, color.g, color.b)), nil
}

type AttachmentField struct {
	title string
	value string
	short bool
}

type Attachment struct {
	Fallback string `json:"fallback"`
	Color    Color  `json:"color"`
	Pretext  string `json:"pretext"`

	AuthorName string `json:"author_name"`
	AuthorLink string `json:"author_link"`
	AuthorIcon string `json:"author_icon"`

	Title     string `json:"title"`
	TitleLink string `json:"title_link"`

	Text string `json:"text"`

	ImageUrl string `json:"image_url"`
	ThumbUrl string `json:"thumb_url"`
}

type ResponseTypeEnum int

const (
	in_channel = iota
	ephemeral
	deffered_in_channel
)

func (e ResponseTypeEnum) MarshalJSON() ([]byte, error) {
	var str string
	switch e {
	case deffered_in_channel:
		str = "in_channel"
		break
	case in_channel:
		str = "in_channel"
		break
	case ephemeral:
		str = "ephemeral"
		break
	default:
		return nil, errors.New("Invalid value")
	}
	return []byte(`"` + str + `"`), nil
}

type Response struct {
	ResponseType ResponseTypeEnum `json:"response_type"`
	Text         string           `json:"text"`
	Attachments  []Attachment     `json:"attachments"`
}

type Request struct {
	Token       string `param:"token"`
	TeamId      string `param:"team_id"`
	TeamDomain  string `param:"team_domain"`
	ChannelId   string `param:"channel_id"`
	ChannelName string `param:"channel_name"`
	UserId      string `param:"user_id"`
	UserName    string `param:"user_name"`
	Command     string `param:"command"`
	Text        string `param:"text"`
	ResponseUrl string `param:"response_url"`
}

func NewServer(commands CommandsInfo, command CommandInfo) *CommandServer {
	server := &CommandServer{commands, command, map[string]*CommandRuntimeInfo{}}

	for k, v := range command {
		server.Handlers[k] = &CommandRuntimeInfo{v.Token, nil}
	}

	server.registHandler("/echo", EchoCommand)
	server.registHandler("/namu", NamuCommand)
	server.registHandler("/zzal", ZzalCommand)

	return server
}

func (server *CommandServer) registHandler(key string, handler interface{}) {
	if val, ok := server.Handlers[key]; ok {
		val.Handler = handler
	} else {
		log.Println("Warning : config not found for ", key)
		server.Handlers[key] = &CommandRuntimeInfo{"", handler}
	}
}

func requestFormToRequestObj(r *http.Request) *Request {
	ret := new(Request)

	val := reflect.Indirect(reflect.ValueOf(ret))
	typ := reflect.TypeOf(*ret)

	for i := 0; i < typ.NumField(); i++ {
		field := val.Field(i)
		field_info := typ.Field(i)
		field_name := field_info.Tag.Get("param")
		field.Set(reflect.ValueOf(r.FormValue(field_name)))
	}

	return ret
}

func (server *CommandServer) commandHandler(w http.ResponseWriter, r *http.Request) {
	req := requestFormToRequestObj(r)
	handlerInfo := server.Handlers[req.Command]

	if handlerInfo != nil {
		if handlerInfo.Token == "" || handlerInfo.Token == req.Token {
			fun := reflect.ValueOf(handlerInfo.Handler)
			in := make([]reflect.Value, 1)
			in[0] = reflect.ValueOf(*req)
			response := fun.Call(in)[0].Interface().(*Response)

			var e error
			w.Header().Set("Content-Type", "application/json")
			if response.ResponseType != deffered_in_channel {
				encoder := json.NewEncoder(w)
				e = encoder.Encode(response)
			} else {
				var buf []byte
				buf, e = json.Marshal(response)
				http.Post(req.ResponseUrl, "application/json", bytes.NewBuffer(buf))
				log.Println("Deffered : ", string(buf))
			}

			if e != nil {
				log.Println("Error occured : ", req, e)
			}
		}
	}
}

func (server *CommandServer) Start(wg *sync.WaitGroup) {
	http.HandleFunc("/", server.commandHandler)
	http.ListenAndServe(fmt.Sprintf(":%d", server.Common.Port), nil)

	wg.Done()
}
<commit_msg>make MarshalJSON method using String method<commit_after>package command

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"reflect"
	"sync"
)

type CommandsInfo struct {
	Port int
}

type CommandInfo map[string]*struct {
	Token string
}

type CommandRuntimeInfo struct {
	Token   string
	Handler interface{}
}

type CommandServer struct {
	Common   CommandsInfo
	Command  CommandInfo
	Handlers map[string]*CommandRuntimeInfo
}

type Color struct {
	r, g, b uint8
}

func (color Color) String() string {
	return fmt.Sprintf("#%02x%02x%02x", color.r, color.g, color.b)
}

func (color Color) MarshalJSON() ([]byte, error) {
	return []byte(`"` + color.String() + `"`), nil
}

type AttachmentField struct {
	title string
	value string
	short bool
}

type Attachment struct {
	Fallback string `json:"fallback"`
	Color    Color  `json:"color"`
	Pretext  string `json:"pretext"`

	AuthorName string `json:"author_name"`
	AuthorLink string `json:"author_link"`
	AuthorIcon string `json:"author_icon"`

	Title     string `json:"title"`
	TitleLink string `json:"title_link"`

	Text string `json:"text"`

	ImageUrl string `json:"image_url"`
	ThumbUrl string `json:"thumb_url"`
}

type ResponseTypeEnum int

const (
	in_channel = iota
	ephemeral
	deffered_in_channel
)

func (e ResponseTypeEnum) String() string {
	switch e {
	case deffered_in_channel:
		fallthrough
	case in_channel:
		return "in_channel"
	case ephemeral:
		return "ephemeral"
	default:
		return ""
	}
}

func (e ResponseTypeEnum) MarshalJSON() ([]byte, error) {
	str := e.String()

	if str == "" {
		return nil, errors.New("Invalid value")
	}

	return []byte(`"` + str + `"`), nil
}

type Response struct {
	ResponseType ResponseTypeEnum `json:"response_type"`
	Text         string           `json:"text"`
	Attachments  []Attachment     `json:"attachments"`
}

type Request struct {
	Token       string `param:"token"`
	TeamId      string `param:"team_id"`
	TeamDomain  string `param:"team_domain"`
	ChannelId   string `param:"channel_id"`
	ChannelName string `param:"channel_name"`
	UserId      string `param:"user_id"`
	UserName    string `param:"user_name"`
	Command     string `param:"command"`
	Text        string `param:"text"`
	ResponseUrl string `param:"response_url"`
}

func NewServer(commands CommandsInfo, command CommandInfo) *CommandServer {
	server := &CommandServer{commands, command, map[string]*CommandRuntimeInfo{}}

	for k, v := range command {
		server.Handlers[k] = &CommandRuntimeInfo{v.Token, nil}
	}

	server.registHandler("/echo", EchoCommand)
	server.registHandler("/namu", NamuCommand)
	server.registHandler("/zzal", ZzalCommand)

	return server
}

func (server *CommandServer) registHandler(key string, handler interface{}) {
	if val, ok := server.Handlers[key]; ok {
		val.Handler = handler
	} else {
		log.Println("Warning : config not found for ", key)
		server.Handlers[key] = &CommandRuntimeInfo{"", handler}
	}
}

func requestFormToRequestObj(r *http.Request) *Request {
	ret := new(Request)

	val := reflect.Indirect(reflect.ValueOf(ret))
	typ := reflect.TypeOf(*ret)

	for i := 0; i < typ.NumField(); i++ {
		field := val.Field(i)
		field_info := typ.Field(i)
		field_name := field_info.Tag.Get("param")
		field.Set(reflect.ValueOf(r.FormValue(field_name)))
	}

	return ret
}

func (server *CommandServer) commandHandler(w http.ResponseWriter, r *http.Request) {
	req := requestFormToRequestObj(r)
	handlerInfo := server.Handlers[req.Command]

	if handlerInfo != nil {
		if handlerInfo.Token == "" || handlerInfo.Token == req.Token {
			fun := reflect.ValueOf(handlerInfo.Handler)
			in := make([]reflect.Value, 1)
			in[0] = reflect.ValueOf(*req)
			response := fun.Call(in)[0].Interface().(*Response)

			var e error
			w.Header().Set("Content-Type", "application/json")
			if response.ResponseType != deffered_in_channel {
				encoder := json.NewEncoder(w)
				e = encoder.Encode(response)
			} else {
				var buf []byte
				buf, e = json.Marshal(response)
				http.Post(req.ResponseUrl, "application/json", bytes.NewBuffer(buf))
				log.Println("Deffered : ", string(buf))
			}

			if e != nil {
				log.Println("Error occured : ", req, e)
			}
		}
	}
}

func (server *CommandServer) Start(wg *sync.WaitGroup) {
	http.HandleFunc("/", server.commandHandler)
	http.ListenAndServe(fmt.Sprintf(":%d", server.Common.Port), nil)

	wg.Done()
}
<|endoftext|>


<commit_before>package command

import (
	"encoding/json"
	"fmt"
	"net/url"
	"os"
	"strings"
	"unicode/utf8"

	"golang.org/x/net/html"

	"github.com/bluedaniel/gotube/utils"
	"github.com/fatih/color"
	"github.com/urfave/cli"
	"github.com/yhat/scrape"
)

// CmdStation runs `tube status`
func CmdStation(c *cli.Context) error {
	q := strings.Join(c.Args()[:], " ")
	query := &url.URL{Path: strings.Replace(q, "and", "&", -1)}

	var arr1 utils.StopPointSearchResp
	stopPointSearch := utils.FetchJSON(utils.StopPointSearchURL(query.String()))
	json.Unmarshal([]byte(stopPointSearch), &arr1)

	if arr1.Total == 0 {
		fmt.Println("No results found")
		os.Exit(2)
	}

	stationID := arr1.Matches[0].ID

	stopPointData := utils.FetchJSON(utils.StopPointURL(stationID))
	var arr2 utils.StopPointDataResp
	json.Unmarshal([]byte(stopPointData), &arr2)

	tubesAtStation := []string{}
	for _, line := range arr2.Lines {
		if utils.StringInSlice(line.ID, utils.GetTubeNames()) {
			tubesAtStation = append(tubesAtStation, line.ID)
		}
	}

	messages := make(chan string)

	fmt.Printf("\n%s %s", "Last trains from", utils.BoldFormat(arr1.Matches[0].Name))
	fmt.Printf("\n%s\n", utils.BoldFormat(strings.Repeat("=", utf8.RuneCountInString(arr1.Matches[0].Name)+17)))

	for i, line := range tubesAtStation {
		go func(last bool, stationID string, line string) {
			firstHTML := utils.FetchHTML(utils.StopPointDeadline(stationID, line, false))
			articles := scrape.FindAll(firstHTML, func(n *html.Node) bool {
				return scrape.Attr(n, "class") == "first-last-train-item"
			})
			fmt.Printf("%s\n", utils.BoldFormat(strings.Title(strings.Replace(line, "-", " & ", -1))))
			for _, article := range articles {
				fmt.Println(color.GreenString("➡ ") + scrape.Text(article))
			}
			if !last {
				fmt.Println()
			}
			messages <- ""
		}(i+1 == len(tubesAtStation), stationID, line)

		<-messages
	}
	return nil
}
<commit_msg>Using sync.WaitGroup over blocking goroutines<commit_after>package command

import (
	"encoding/json"
	"fmt"
	"net/url"
	"os"
	"strings"
	"sync"
	"unicode/utf8"

	"golang.org/x/net/html"

	"github.com/bluedaniel/gotube/utils"
	"github.com/fatih/color"
	"github.com/urfave/cli"
	"github.com/yhat/scrape"
)

// CmdStation runs `tube status`
func CmdStation(c *cli.Context) error {
	q := strings.Join(c.Args()[:], " ")
	query := &url.URL{Path: strings.Replace(q, "and", "&", -1)}

	var arr1 utils.StopPointSearchResp
	stopPointSearch := utils.FetchJSON(utils.StopPointSearchURL(query.String()))
	json.Unmarshal([]byte(stopPointSearch), &arr1)

	if arr1.Total == 0 {
		fmt.Println("No results found")
		os.Exit(2)
	}

	stationID := arr1.Matches[0].ID

	stopPointData := utils.FetchJSON(utils.StopPointURL(stationID))
	var arr2 utils.StopPointDataResp
	json.Unmarshal([]byte(stopPointData), &arr2)

	tubesAtStation := []string{}
	for _, line := range arr2.Lines {
		if utils.StringInSlice(line.ID, utils.GetTubeNames()) {
			tubesAtStation = append(tubesAtStation, line.ID)
		}
	}

	fmt.Printf("\n%s %s", "Last trains from", utils.BoldFormat(arr1.Matches[0].Name))
	fmt.Printf("\n%s\n", utils.BoldFormat(strings.Repeat("=", utf8.RuneCountInString(arr1.Matches[0].Name)+17)))

	var lines = map[string]*html.Node{}

	var wg sync.WaitGroup
	for _, line := range tubesAtStation {
		wg.Add(1)
		go func(stationID string, line string) {
			defer wg.Done()
			lines[line] = utils.FetchHTML(utils.StopPointDeadline(stationID, line, false))
		}(stationID, line)
	}
	wg.Wait()

	for key, value := range lines {
		articles := scrape.FindAll(value, func(n *html.Node) bool {
			return scrape.Attr(n, "class") == "first-last-train-item"
		})
		fmt.Printf("%s\n", utils.BoldFormat(strings.Title(strings.Replace(key, "-", " & ", -1))))
		for _, article := range articles {
			fmt.Println(color.GreenString("➡ ") + scrape.Text(article))
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package command

import (
	"bytes"
	"fmt"

	"github.com/mitchellh/cli"
)

// VersionCommand is a Command implementation that prints the version.
type VersionCommand struct {
	Revision          string
	Version           string
	VersionPrerelease string
	Ui                cli.Ui
}

func (c *VersionCommand) Help() string {
	return ""
}

func (c *VersionCommand) Run(_ []string) int {
	var versionString bytes.Buffer
	fmt.Fprintf(&versionString, "Rancher v%s", c.Version)
	if c.VersionPrerelease != "" {
		fmt.Fprintf(&versionString, ".%s", c.VersionPrerelease)

		if c.Revision != "" {
			fmt.Fprintf(&versionString, " (%s)", c.Revision)
		}
	}

	c.Ui.Output(versionString.String())

	return 0
}

func (c *VersionCommand) Synopsis() string {
	return "Prints the Rancher version"
}
<commit_msg>delinted version.go<commit_after>package command

import (
	"bytes"
	"fmt"

	"github.com/mitchellh/cli"
)

// VersionCommand is a Command implementation that prints the version.
type VersionCommand struct {
	Revision          string
	Version           string
	VersionPrerelease string
	Ui                cli.Ui
}

// Help prints the Help text for the version sub-command
func (c *VersionCommand) Help() string {
	return "Prints Rancher's version information."
}

// Run runs the version sub-command.
func (c *VersionCommand) Run(_ []string) int {
	var versionString bytes.Buffer
	fmt.Fprintf(&versionString, "Rancher v%s", c.Version)
	if c.VersionPrerelease != "" {
		fmt.Fprintf(&versionString, ".%s", c.VersionPrerelease)

		if c.Revision != "" {
			fmt.Fprintf(&versionString, " (%s)", c.Revision)
		}
	}

	c.Ui.Output(versionString.String())

	return 0
}

// Synopsis provides a precis of the version sub-command.
func (c *VersionCommand) Synopsis() string {
	return "Prints the Rancher version"
}
<|endoftext|>


<commit_before>package role

import (
	"net/http"
	"xorkevin.dev/governor"
	"xorkevin.dev/governor/service/kvstore"
	"xorkevin.dev/governor/util/rank"
)

const (
	cacheValY = "y"
	cacheValN = "n"
)

func (s *service) intersectRolesRepo(userid string, roles rank.Rank) (rank.Rank, error) {
	return s.roles.IntersectRoles(userid, roles)
}

func (s *service) IntersectRoles(userid string, roles rank.Rank) (rank.Rank, error) {
	userkv := s.kvroleset.Subtree(userid)

	txget := userkv.Tx()
	resget := make(map[string]kvstore.Resulter, roles.Len())
	for _, i := range roles.ToSlice() {
		resget[i] = txget.Get(i)
	}
	if err := txget.Exec(); err != nil {
		s.logger.Error("Failed to get user roles from cache", map[string]string{
			"error":      err.Error(),
			"actiontype": "getroleset",
		})
		return s.intersectRolesRepo(userid, roles)
	}

	uncachedRoles := rank.Rank{}
	res := rank.Rank{}
	for k, v := range resget {
		r, err := v.Result()
		if err != nil {
			if governor.ErrorStatus(err) != http.StatusNotFound {
				s.logger.Error("Failed to get user role result from cache", map[string]string{
					"error":      err.Error(),
					"actiontype": "getroleresult",
				})
			}
			uncachedRoles.AddOne(k)
		} else {
			if r == cacheValY {
				res.AddOne(k)
			}
		}
	}

	if len(uncachedRoles) == 0 {
		return res, nil
	}

	m, err := s.intersectRolesRepo(userid, uncachedRoles)
	if err != nil {
		return nil, err
	}

	txset := userkv.Tx()
	for _, i := range uncachedRoles.ToSlice() {
		if m.Has(i) {
			res.AddOne(i)
			txset.Set(i, cacheValY, s.roleCacheTime)
		} else {
			txset.Set(i, cacheValN, s.roleCacheTime)
		}
	}
	if err := txset.Exec(); err != nil {
		s.logger.Error("Failed to set user roles in cache", map[string]string{
			"error":      err.Error(),
			"actiontype": "setroleset",
		})
	}

	return res, nil
}

func (s *service) InsertRoles(userid string, roles rank.Rank) error {
	if err := s.roles.InsertRoles(userid, roles); err != nil {
		return err
	}
	s.clearCache(userid)
	return nil
}

func (s *service) DeleteRoles(userid string, roles rank.Rank) error {
	if err := s.roles.DeleteRoles(userid, roles); err != nil {
		return err
	}
	s.clearCache(userid)
	return nil
}

func (s *service) DeleteAllRoles(userid string) error {
	if err := s.roles.DeleteUserRoles(userid); err != nil {
		return err
	}
	s.clearCache(userid)
	return nil
}

func (s *service) GetRoles(userid string, amount, offset int) (rank.Rank, error) {
	return s.roles.GetRoles(userid, amount, offset)
}

func (s *service) GetByRole(roleName string, amount, offset int) ([]string, error) {
	return s.roles.GetByRole(roleName, amount, offset)
}

const (
	roleLimit = 256
)

func (s *service) getRoleSummaryRepo(userid string) (rank.Rank, error) {
	roles, err := s.GetRoles(userid, roleLimit, 0)
	if err != nil {
		return nil, err
	}
	if err := s.kvsummary.Set(userid, roles.Stringify(), s.roleCacheTime); err != nil {
		s.logger.Error("Failed to cache role summary", map[string]string{
			"error":      err.Error(),
			"actiontype": "cachesummary",
		})
	}
	return roles, nil
}

func (s *service) GetRoleSummary(userid string) (rank.Rank, error) {
	k, err := s.kvsummary.Get(userid)
	if err != nil {
		if governor.ErrorStatus(err) != http.StatusNotFound {
			s.logger.Error("Failed to get role summary from cache", map[string]string{
				"error":      err.Error(),
				"actiontype": "getcachesummary",
			})
		}
		return s.getRoleSummaryRepo(userid)
	}
	roles, err := rank.FromStringUser(k)
	if err != nil {
		s.logger.Error("Invalid role summary", map[string]string{
			"error":      err.Error(),
			"actiontype": "parsecachesummary",
		})
		return s.getRoleSummaryRepo(userid)
	}
	return roles, nil
}

func (s *service) clearCache(userid string) {
	if err := s.kvsummary.Del(userid); err != nil {
		s.logger.Error("Failed to clear role summary from cache", map[string]string{
			"error":      err.Error(),
			"actiontype": "clearcachesummary",
		})
	}
}
<commit_msg>Impl role clear cache<commit_after>package role

import (
	"net/http"
	"xorkevin.dev/governor"
	"xorkevin.dev/governor/service/kvstore"
	"xorkevin.dev/governor/util/rank"
)

const (
	cacheValY = "y"
	cacheValN = "n"
)

func (s *service) intersectRolesRepo(userid string, roles rank.Rank) (rank.Rank, error) {
	return s.roles.IntersectRoles(userid, roles)
}

func (s *service) IntersectRoles(userid string, roles rank.Rank) (rank.Rank, error) {
	userkv := s.kvroleset.Subtree(userid)

	txget := userkv.Tx()
	resget := make(map[string]kvstore.Resulter, roles.Len())
	for _, i := range roles.ToSlice() {
		resget[i] = txget.Get(i)
	}
	if err := txget.Exec(); err != nil {
		s.logger.Error("Failed to get user roles from cache", map[string]string{
			"error":      err.Error(),
			"actiontype": "getroleset",
		})
		return s.intersectRolesRepo(userid, roles)
	}

	uncachedRoles := rank.Rank{}
	res := rank.Rank{}
	for k, v := range resget {
		r, err := v.Result()
		if err != nil {
			if governor.ErrorStatus(err) != http.StatusNotFound {
				s.logger.Error("Failed to get user role result from cache", map[string]string{
					"error":      err.Error(),
					"actiontype": "getroleresult",
				})
			}
			uncachedRoles.AddOne(k)
		} else {
			if r == cacheValY {
				res.AddOne(k)
			}
		}
	}

	if len(uncachedRoles) == 0 {
		return res, nil
	}

	m, err := s.intersectRolesRepo(userid, uncachedRoles)
	if err != nil {
		return nil, err
	}

	txset := userkv.Tx()
	for _, i := range uncachedRoles.ToSlice() {
		if m.Has(i) {
			res.AddOne(i)
			txset.Set(i, cacheValY, s.roleCacheTime)
		} else {
			txset.Set(i, cacheValN, s.roleCacheTime)
		}
	}
	if err := txset.Exec(); err != nil {
		s.logger.Error("Failed to set user roles in cache", map[string]string{
			"error":      err.Error(),
			"actiontype": "setroleset",
		})
	}

	return res, nil
}

func (s *service) InsertRoles(userid string, roles rank.Rank) error {
	if err := s.roles.InsertRoles(userid, roles); err != nil {
		return err
	}
	s.clearCache(userid, roles)
	return nil
}

func (s *service) DeleteRoles(userid string, roles rank.Rank) error {
	if err := s.roles.DeleteRoles(userid, roles); err != nil {
		return err
	}
	s.clearCache(userid, roles)
	return nil
}

func (s *service) DeleteAllRoles(userid string) error {
	roles, err := s.GetRoles(userid, 65536, 0)
	if err != nil {
		return err
	}
	if err := s.roles.DeleteUserRoles(userid); err != nil {
		return err
	}
	s.clearCache(userid, roles)
	return nil
}

func (s *service) GetRoles(userid string, amount, offset int) (rank.Rank, error) {
	return s.roles.GetRoles(userid, amount, offset)
}

func (s *service) GetByRole(roleName string, amount, offset int) ([]string, error) {
	return s.roles.GetByRole(roleName, amount, offset)
}

const (
	roleLimit = 256
)

func (s *service) getRoleSummaryRepo(userid string) (rank.Rank, error) {
	roles, err := s.GetRoles(userid, roleLimit, 0)
	if err != nil {
		return nil, err
	}
	if err := s.kvsummary.Set(userid, roles.Stringify(), s.roleCacheTime); err != nil {
		s.logger.Error("Failed to cache role summary", map[string]string{
			"error":      err.Error(),
			"actiontype": "cachesummary",
		})
	}
	return roles, nil
}

func (s *service) GetRoleSummary(userid string) (rank.Rank, error) {
	k, err := s.kvsummary.Get(userid)
	if err != nil {
		if governor.ErrorStatus(err) != http.StatusNotFound {
			s.logger.Error("Failed to get role summary from cache", map[string]string{
				"error":      err.Error(),
				"actiontype": "getcachesummary",
			})
		}
		return s.getRoleSummaryRepo(userid)
	}
	roles, err := rank.FromStringUser(k)
	if err != nil {
		s.logger.Error("Invalid role summary", map[string]string{
			"error":      err.Error(),
			"actiontype": "parsecachesummary",
		})
		return s.getRoleSummaryRepo(userid)
	}
	return roles, nil
}

func (s *service) clearCache(userid string, roles rank.Rank) {
	if err := s.kvsummary.Del(userid); err != nil {
		s.logger.Error("Failed to clear role summary from cache", map[string]string{
			"error":      err.Error(),
			"actiontype": "clearcachesummary",
		})
	}

	if len(roles) == 0 {
		return
	}

	tx := s.kvroleset.Subtree(userid).Tx()
	for _, i := range roles.ToSlice() {
		tx.Del(i)
	}
	if err := tx.Exec(); err != nil {
		s.logger.Error("Failed to clear role set from cache", map[string]string{
			"error":      err.Error(),
			"actiontype": "clearroleset",
		})
	}
}
<|endoftext|>


<commit_before>package client

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"strings"
	"time"
)

const (
	FILENAME       = "ENCRYPTED"
	LAST_PUSH_DATE = "LAST_PUSH"

	C_CREATE = 1
	C_SHOW   = 2
	C_DELETE = 3
	C_ABOUT  = 9

	C_CREATE_IST = ""
	C_SHOW_IST   = ""
	C_DELETE_IST = ""
	C_ABOUT_IST  = ""

	ERR_MSG      = "Error, check your command by printing '[command] [-h]'"
	COMMAND_LIST = "create, delete, show"
	ABOUT_MSG    = "This program safely stores your passwords."
	CREDIT       = "Copyright 2015 Chungseok Baek csbaek0429@gmail.com"
)

type Command struct {
	Type        int
	Instruction string
	Arguments   []string
	Flags       []string
}

// entry point of command.go
// note that it has *Command pointer receiver
func (c *Command) Run() {
	action := c.Type
	switch action {
	// 0			1		2		3	  4
	// [path.exe] [command] [arg1] [arg2] [arg3]
	case 1: //create
		if len(c.Arguments) < 3 { // check the number of arguments
			log.Fatal(errors.New("More arguments needed!"))
			return
		}
		if checkIfCompanyNameExists(c.Arguments[0]) { // check if company exists
			fmt.Println("Company Name Exist, use replace")
			return
		}

		var d = DecryptedPassword{ // prepare decrypted file
			Key:   c.Arguments[1],
			Value: c.Arguments[2],
		}
		var e = d.SimpleEncrypt() // encrypt
		err := writeEncryptedDataToFile(c.Arguments[0], e)
		if err != nil {
			panic(err)
		}

	case 3: //delete
		if len(c.Arguments) < 1 { // check the number of arguments
			log.Fatal(errors.New("More arguments needed!"))
			if !checkIfCompanyNameExists(c.Arguments[1]) { // search by company name
				fmt.Println("Check your company name again")
				return
			}
			err := deleteLineFromCompanyName(c.Arguments[1]) // delete line by company name
			if err != nil {
				panic(err)
			}
		}
	case 2: //show
		if len(c.Arguments) < 1 { // argument check
			log.Fatal(errors.New("More arguments needed!"))
		}
		rows, err := readEncryptedDataFromFile() // read data from file
		if err != nil {
			panic(err)
		}
		result := searchWithCompanyName(c.Arguments[0], rows) // search company
		if len(result) == 0 {
			fmt.Println("none was found with that name")
			return
		}
		for _, v := range result {
			eachColumn := strings.Split(v, " ")
			encrypted := &EncryptedPassword{
				Key:   eachColumn[1],
				Value: eachColumn[2],
			}
			decrypted := encrypted.SimpleDecrypt()
			fmt.Println(eachColumn[0], decrypted.String())
		}
		return
	case 4:
		push()
	case 9:
		fmt.Println(ABOUT_MSG, CREDIT)
		return
	default:
		fmt.Println(ABOUT_MSG, ERR_MSG, COMMAND_LIST)
	}
	return
}

// TODO: checkIfPushNeeded
func push() {
	if checkIfPushNeeded() {
		uploadToServer()
	}
}

// TODO: upload ENCRYPTED
func uploadToServer() {

}

func checkIfPushNeeded() bool {
	lastPushDate, err := getLastPushDate()
	if err != nil {
		panic(err)
	}
	ti, err := os.Stat(FILENAME)
	if err != nil {
		panic(err)
	}
	modeTime := ti.ModTime()
	return !lastPushDate.After(modeTime)
}

func lastPushDateUpdateNow() error {
	f, err := os.OpenFile(LAST_PUSH_DATE, os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	defer f.Close()
	b, err := time.Now().MarshalBinary()
	if err != nil {
		return err
	}
	f.Write(b)
	return nil
}

func getLastPushDate() (*time.Time, error) {
	b, err := ioutil.ReadFile(LAST_PUSH_DATE)
	if err != nil {
		return new(time.Time), err
	}
	var ti time.Time
	err = ti.UnmarshalBinary(b)
	if err != nil {
		return new(time.Time), err
	}
	return &ti, nil
}

func deleteLineFromCompanyName(cname string) error {
	input, err := ioutil.ReadFile(FILENAME)
	if err != nil {
		return err
	}
	re := regexp.MustCompile("(?m)^.*" + cname + ".*$[\r\n]+")
	res := re.ReplaceAllString(string(input), "")
	err = ioutil.WriteFile(FILENAME, []byte(res), 0666)
	if err != nil {
		return err
	}
	return nil
}

func searchWithCompanyName(cname string, strs []string) []string {
	var result []string
	for _, str := range strs {
		eachColumn := strings.Split(str, " ")
		if strings.Contains(eachColumn[0], cname) {
			result = append(result, str)
		}
	}
	return result
}

func searchWithKeyword(keyword string, strs []string) []string {
	var result []string
	for _, str := range strs {
		if strings.Contains(str, keyword) {
			result = append(result, str)
		}
	}
	return result
}

func writeEncryptedDataToFile(company string, encrypted *EncryptedPassword) error {
	f, err := os.OpenFile(FILENAME, os.O_CREATE|os.O_APPEND, 0600) // open file
	if err != nil {
		return err
	}
	defer f.Close() // defer close
	str := ""
	blank := " "
	newline := "\n"
	// complete the format
	str = str + company + blank + encrypted.Key + blank + encrypted.Value + time.Now().String() + newline
	_, err = f.Write([]byte(str))
	return err
}

func checkIfCompanyNameExists(str string) bool {
	data, err := ioutil.ReadFile(FILENAME)
	if err != nil {
		return false
	}
	re := regexp.MustCompile("(?m)^.*" + str + ".*$[\r\n]+")
	return re.Match(data)
}

func readEncryptedDataFromFile() ([]string, error) {
	data, err := ioutil.ReadFile(FILENAME)
	if err != nil {
		return nil, err
	}
	eachRow := strings.Split(string(data), "\n")
	return eachRow, nil
}

func ParseCommands(strs []string) *Command {
	// prepare empty command
	c := &Command{}

	// parse first command and assign it to Type
	i := 1
	expectedCommand := ""
	if len(strs) > 1 {
		expectedCommand = strs[i]
	} else if len(strs) == 1 {
		fmt.Println(ABOUT_MSG)
		return c
	}
	switch expectedCommand {
	case "create":
		c.Type = C_CREATE
		c.Instruction = C_CREATE_IST
	case "show":
		c.Type = C_SHOW
		c.Instruction = C_SHOW_IST
	case "delete":
		c.Type = C_DELETE
		c.Instruction = C_DELETE_IST
	case "about":
		c.Type = C_ABOUT
		c.Instruction = C_ABOUT_IST
	default:
		return c
	}

	// parse flags and arguments
	for i := 2; i < len(strs); i++ {
		if []rune(strs[i])[0] == '-' {
			c.addFlag(strs[i])
		} else {
			c.addArgument(strs[i])
		}
	}
	return c
}

// add Arguments
func (c *Command) addArgument(strs ...string) {
	c.Arguments = append(c.Arguments, strs...)
}

// add flags
func (c *Command) addFlag(strs ...string) {
	c.Flags = append(c.Flags, strs...)
}
<commit_msg>release alpha<commit_after>package client

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"strings"
	"time"
)

const (
	FILENAME       = "ENCRYPTED"
	LAST_PUSH_DATE = "LAST_PUSH"

	C_CREATE  = 1
	C_SHOW    = 2
	C_DELETE  = 3
	C_PUSH    = 4
	C_REPLACE = 5
	C_ABOUT   = 9

	C_CREATE_IST  = "[create] [company name] [id] [password]"
	C_SHOW_IST    = "[show] [company name] or [show] for all lists"
	C_DELETE_IST  = "[delete] [company name]"
	C_ABOUT_IST   = "[about]"
	C_REPLACE_IST = "[replace] [company name] [id] [password]"
	C_PUSH_IST    = "[push] [url] [id] [password]"

	F_HELP = "-h"

	ERR_MSG      = "Error, check your command by printing '[command] [-h]'"
	COMMAND_LIST = "create, delete, show"
	ABOUT_MSG    = "This program safely stores your passwords."
	CREDIT       = "Copyright 2015 Chungseok Baek csbaek0429@gmail.com"
)

type Command struct {
	Type        int
	Instruction string
	Arguments   []string
	Flags       []string
}

// entry point of command.go
// note that it has *Command pointer receiver
func (c *Command) Run() {
	action := c.Type
	if !c.flagProcess() { // flag process returns true if it needs to proceed the following action
		return
	}
	switch action {
	// 0			1		2		3	  4
	// [path.exe] [command] [arg1] [arg2] [arg3]
	case 1: //create
		create(c)
	case 3: //delete
		delete(c)
	case 2: //show
		show(c)
	case 4: // push
		push(c)
	case 5: // replace
		replace(c)
	case 9:
		fmt.Println(ABOUT_MSG, CREDIT)
		return
	default:
		fmt.Println(ABOUT_MSG, ERR_MSG, COMMAND_LIST)
	}
	return
}

func ParseCommands(strs []string) *Command {
	// prepare empty command
	c := &Command{}

	// parse first command and assign it to Type
	i := 1
	expectedCommand := ""
	if len(strs) > 1 {
		expectedCommand = strs[i]
	} else if len(strs) == 1 {
		return c
	}
	switch expectedCommand {
	case "create":
		c.Type = C_CREATE
		c.Instruction = C_CREATE_IST
	case "show":
		c.Type = C_SHOW
		c.Instruction = C_SHOW_IST
	case "delete":
		c.Type = C_DELETE
		c.Instruction = C_DELETE_IST
	case "about":
		c.Type = C_ABOUT
		c.Instruction = C_ABOUT_IST
	case "replace":
		c.Type = C_REPLACE
		c.Instruction = C_REPLACE_IST
	case "push":
		c.Type = C_PUSH
		c.Instruction = C_PUSH_IST
	default:
		return c
	}

	// parse flags and arguments
	for i := 2; i < len(strs); i++ {
		if []rune(strs[i])[0] == '-' {
			c.addFlag(strs[i])
		} else {
			c.addArgument(strs[i])
		}
	}
	return c
}

func replace(c *Command) { // the worst case is 'delete' succeeds and 'create' fails
	delete(c)
	create(c)
	return
}

func show(c *Command) {
	if len(c.Arguments) < 1 { // argument check
		b, err := ioutil.ReadFile(FILENAME)
		if err != nil {
			fmt.Println(err)
		}
		split := strings.Split(string(b), "\n")
		for _, v := range split {
			cname := strings.Split(v, " ")
			fmt.Println(cname[0])
		}
		return
	}
	rows, err := readEncryptedDataFromFile() // read data from file
	if err != nil {
		panic(err)
	}
	result := searchWithCompanyName(c.Arguments[0], rows) // search company
	if len(result) == 0 {
		fmt.Println("none was found with that name")
		return
	}
	for _, v := range result {
		eachColumn := strings.Split(v, " ")
		encrypted := &EncryptedPassword{
			Key:   eachColumn[1],
			Value: eachColumn[2],
		}
		decrypted := encrypted.SimpleDecrypt()
		fmt.Println(eachColumn[0], decrypted.String())
	}
	return
}

func delete(c *Command) {
	if len(c.Arguments) < 1 { // check the number of arguments
		log.Fatal(errors.New("More arguments needed!"))
	}
	if !checkIfCompanyNameExists(c.Arguments[0]) { // search by company name
		fmt.Println("No company was found by that name")
		return
	}
	err := deleteLineFromCompanyName(c.Arguments[0]) // delete line by company name
	if err != nil {
		panic(err)
	}
	return
}

func create(c *Command) {
	if len(c.Arguments) < 3 { // check the number of arguments
		log.Fatal(errors.New("More arguments needed!"))
		return
	}
	if checkIfCompanyNameExists(c.Arguments[0]) { // check if company exists
		fmt.Println("Company Name Exist, use replace")
		return
	}

	var d = DecryptedPassword{ // prepare decrypted file
		Key:   c.Arguments[1],
		Value: c.Arguments[2],
	}
	var e = d.SimpleEncrypt() // encrypt
	err := writeEncryptedDataToFile(c.Arguments[0], e)
	if err != nil {
		panic(err)
	}
	return
}

// TODO: checkIfPushNeeded
func push(c *Command) {
	if checkIfPushNeeded() {
		uploadToServer()
	}
}

func (c *Command) flagProcess() bool {
	for _, v := range c.Flags {
		switch v {
		case F_HELP:
			fmt.Println(c.Instruction)
			return false
		}
	}
	return true
}

// TODO: upload ENCRYPTED
func uploadToServer() {
	fmt.Println("will be updated")
}

func checkIfPushNeeded() bool {
	lastPushDate, err := getLastPushDate()
	if err != nil {
		panic(err)
	}
	ti, err := os.Stat(FILENAME)
	if err != nil {
		panic(err)
	}
	modeTime := ti.ModTime()
	return !lastPushDate.After(modeTime)
}

func lastPushDateUpdateNow() error {
	f, err := os.OpenFile(LAST_PUSH_DATE, os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	defer f.Close()
	b, err := time.Now().MarshalBinary()
	if err != nil {
		return err
	}
	f.Write(b)
	return nil
}

func getLastPushDate() (*time.Time, error) {
	b, err := ioutil.ReadFile(LAST_PUSH_DATE)
	if err != nil {
		return new(time.Time), err
	}
	var ti time.Time
	err = ti.UnmarshalBinary(b)
	if err != nil {
		return new(time.Time), err
	}
	return &ti, nil
}

func deleteLineFromCompanyName(cname string) error {
	input, err := ioutil.ReadFile(FILENAME)
	if err != nil {
		return err
	}
	re := regexp.MustCompile("(?m)^.*" + cname + "\t\n\v\f\r.*$[\r\n]+")
	res := re.ReplaceAllString(string(input), "")
	err = ioutil.WriteFile(FILENAME, []byte(res), 0666)
	if err != nil {
		return err
	}
	return nil
}

func searchWithCompanyName(cname string, strs []string) []string {
	var result []string
	for _, str := range strs {
		eachColumn := strings.Split(str, " ") // searching is more 'generous' than other functions.
		if strings.Contains(eachColumn[0], cname) {
			result = append(result, str)
		}
	}
	return result
}

func searchWithKeyword(keyword string, strs []string) []string {
	var result []string
	for _, str := range strs {
		if strings.Contains(str, keyword) {
			result = append(result, str)
		}
	}
	return result
}

func writeEncryptedDataToFile(company string, encrypted *EncryptedPassword) error {
	f, err := os.OpenFile(FILENAME, os.O_CREATE|os.O_APPEND, 0600) // open file
	if err != nil {
		return err
	}
	defer f.Close() // defer close
	str := ""
	blank := " "
	newline := "\n"
	// complete the format
	str = str + company + blank + encrypted.Key + blank + encrypted.Value + blank + time.Now().String() + newline
	_, err = f.Write([]byte(str))
	return err
}

func checkIfCompanyNameExists(str string) bool {
	data, err := ioutil.ReadFile(FILENAME)
	if err != nil {
		return false
	}
	re := regexp.MustCompile("(?m)^.*" + str + "\t\n\v\f\r.*$[\r\n]+")
	return re.Match(data)
}

func readEncryptedDataFromFile() ([]string, error) {
	data, err := ioutil.ReadFile(FILENAME)
	if err != nil {
		return nil, err
	}
	eachRow := strings.Split(string(data), "\n")
	return eachRow, nil
}

// add Arguments
func (c *Command) addArgument(strs ...string) {
	c.Arguments = append(c.Arguments, strs...)
}

// add flags
func (c *Command) addFlag(strs ...string) {
	c.Flags = append(c.Flags, strs...)
}
<|endoftext|>


<commit_before>package boardgame

import (
	"encoding/json"
	"errors"
	"strconv"
	"time"
)

//StateStorageRecord is a record representing a state that can be written to
//storage and later returned. It is an encoded json blob, and can be written
//directly to storage with no modification. Typically you don't use this
//representation directly, instead fetching a game from the GameManager and
//then using State() for a fully-inflated state.
type StateStorageRecord json.RawMessage

//MoveStorageRecord is a record representing the Move that was made to get the
//game to its most recent version. It pops out various fields that
//StorageManagers could conceivably want to understand. Typically you don't
//use this directly, but instead fetch information for moves from game.Moves()
//and game.Move().
type MoveStorageRecord struct {
	Name      string
	Version   int
	Initiator int
	//The Phase as returned by Delegate.CurrentPhase() for the state the move
	//was in before it was applied. This is captured in this field because
	//moves in the moves package need to quickly inspect this value without
	//fully inflating the move structs.
	Phase int
	//The player index of the proposer of the move.
	Proposer  PlayerIndex
	Timestamp time.Time
	//The actual JSON serialized blob representing the properties of the move.
	Blob json.RawMessage
}

//String returns the name of the move and its version, for easy debugging.
func (m *MoveStorageRecord) String() string {
	return m.Name + ": " + strconv.Itoa(m.Version)
}

//Inflate takes a move storage record and turns it into a move associated with
//that game, if possible. Returns nil if not possible.
func (m *MoveStorageRecord) inflate(game *Game) (Move, error) {

	if game == nil {
		return nil, errors.New("Game was nil")
	}

	move := game.MoveByName(m.Name)

	if move == nil {
		return nil, errors.New("Couldn't find a move with name: " + m.Name)
	}

	if err := json.Unmarshal(m.Blob, move); err != nil {
		return nil, errors.New("Couldn't unmarshal move: " + err.Error())
	}

	move.Info().version = m.Version
	move.Info().initiator = m.Initiator
	move.Info().timestamp = m.Timestamp

	return move, nil
}

//GameStorageRecord is a simple struct with public fields representing the
//important aspects of a game that should be serialized to storage. The fields
//are broken out specifically so that the storage layer can understand these
//properties in queries. Typically you don't use this struct directly, instead
//getting an inflated version via something like GameManager.ModifiableGame()
//and then using the associated methods on the struct to get at the undelying
//values.
type GameStorageRecord struct {
	//Name is the type of the game, from its manager. Used for sanity
	//checking.
	Name string
	ID   string
	//SecretSalt for this game for things like component Ids. Should never be
	//transmitted to an insecure or untrusted environment; the only way to
	//access it outside this package is via this field, because it must be
	//able to be persisted to and read from storage.
	SecretSalt string `json:",omitempty"`
	Version    int
	Winners    []PlayerIndex
	Finished   bool
	Created    time.Time
	//Modified is updated every time a new move is applied.
	Modified time.Time
	//NumPlayers is the reported number of players when it was created.
	//Primarily for convenience to storage layer so they know how many players
	//are in the game.
	NumPlayers int
	Agents     []string
	Variant    Variant
}

//StorageManager is the interface that storage layers implement. The core
//engine expects one of these to be passed in via NewGameManager as the place
//to store and retrieve game information. A number of different
//implementations are available in boardgame/storage that can all be used.
//Typically you don't use this interface directly--it's defined just to
//formalize the interface between the core engine and the underlying storage
//layer.
type StorageManager interface {
	//State returns the StateStorageRecord for the game at the given version,
	//or nil.
	State(gameID string, version int) (StateStorageRecord, error)

	//Move returns the MoveStorageRecord for the game at the given version, or
	//nil.
	Move(gameID string, version int) (*MoveStorageRecord, error)

	//Moves is like Move but returns all moves from fromVersion (exclusive) to
	//toVersion (inclusive). If fromVersion == toVersion, should return
	//toVersion. In many storage subsystems this is cheaper than repeated
	//calls to Move, which is why it's broken out separately.
	Moves(gameID string, fromVersion, toVersion int) ([]*MoveStorageRecord, error)

	//Game fetches the GameStorageRecord with the given ID from the store, if
	//it exists.
	Game(id string) (*GameStorageRecord, error)

	//AgentState retrieves the most recent state for the given agent
	AgentState(gameID string, player PlayerIndex) ([]byte, error)

	//SaveGameAndCurrentState stores the game and the current state (at
	//game.Version()) into the store at the same time in a transaction. Move
	//is normally provided but will be be nil if game.Version() is 0, denoting
	//the initial state for a game.
	SaveGameAndCurrentState(game *GameStorageRecord, state StateStorageRecord, move *MoveStorageRecord) error

	//SaveAgentState saves the agent state for the given player
	SaveAgentState(gameID string, player PlayerIndex, state []byte) error

	//PlayerMoveApplied is called after a PlayerMove and all of its resulting
	//FixUp moves have been applied. Most StorageManagers don't need to do
	//anything here; it's primarily useful as a callback to signal that a run
	//of moves has been applied, e.g. in the server.
	PlayerMoveApplied(game *GameStorageRecord) error

	//FetchInjectedDataForGame is an override point for other layers to inject
	//triggers for bits of game logic to call into. dataType should be the name
	//of the package that publishes the data type, to avoid collissions (for
	//example, 'github.com/jkomoros/boardgame/server/api.PlayerToSeat'). Things,
	//like server, will override this method to add new data types. Base storage
	//managers need only return nil in all cases.
	FetchInjectedDataForGame(gameID string, dataType string) interface{}
}
<commit_msg>Remove the String() behavior of MoveStorageRec, which actually makes it harder to debug. Part of #701. Part of #755.<commit_after>package boardgame

import (
	"encoding/json"
	"errors"
	"time"
)

//StateStorageRecord is a record representing a state that can be written to
//storage and later returned. It is an encoded json blob, and can be written
//directly to storage with no modification. Typically you don't use this
//representation directly, instead fetching a game from the GameManager and
//then using State() for a fully-inflated state.
type StateStorageRecord json.RawMessage

//MoveStorageRecord is a record representing the Move that was made to get the
//game to its most recent version. It pops out various fields that
//StorageManagers could conceivably want to understand. Typically you don't
//use this directly, but instead fetch information for moves from game.Moves()
//and game.Move().
type MoveStorageRecord struct {
	Name      string
	Version   int
	Initiator int
	//The Phase as returned by Delegate.CurrentPhase() for the state the move
	//was in before it was applied. This is captured in this field because
	//moves in the moves package need to quickly inspect this value without
	//fully inflating the move structs.
	Phase int
	//The player index of the proposer of the move.
	Proposer  PlayerIndex
	Timestamp time.Time
	//The actual JSON serialized blob representing the properties of the move.
	Blob json.RawMessage
}

//Inflate takes a move storage record and turns it into a move associated with
//that game, if possible. Returns nil if not possible.
func (m *MoveStorageRecord) inflate(game *Game) (Move, error) {

	if game == nil {
		return nil, errors.New("Game was nil")
	}

	move := game.MoveByName(m.Name)

	if move == nil {
		return nil, errors.New("Couldn't find a move with name: " + m.Name)
	}

	if err := json.Unmarshal(m.Blob, move); err != nil {
		return nil, errors.New("Couldn't unmarshal move: " + err.Error())
	}

	move.Info().version = m.Version
	move.Info().initiator = m.Initiator
	move.Info().timestamp = m.Timestamp

	return move, nil
}

//GameStorageRecord is a simple struct with public fields representing the
//important aspects of a game that should be serialized to storage. The fields
//are broken out specifically so that the storage layer can understand these
//properties in queries. Typically you don't use this struct directly, instead
//getting an inflated version via something like GameManager.ModifiableGame()
//and then using the associated methods on the struct to get at the undelying
//values.
type GameStorageRecord struct {
	//Name is the type of the game, from its manager. Used for sanity
	//checking.
	Name string
	ID   string
	//SecretSalt for this game for things like component Ids. Should never be
	//transmitted to an insecure or untrusted environment; the only way to
	//access it outside this package is via this field, because it must be
	//able to be persisted to and read from storage.
	SecretSalt string `json:",omitempty"`
	Version    int
	Winners    []PlayerIndex
	Finished   bool
	Created    time.Time
	//Modified is updated every time a new move is applied.
	Modified time.Time
	//NumPlayers is the reported number of players when it was created.
	//Primarily for convenience to storage layer so they know how many players
	//are in the game.
	NumPlayers int
	Agents     []string
	Variant    Variant
}

//StorageManager is the interface that storage layers implement. The core
//engine expects one of these to be passed in via NewGameManager as the place
//to store and retrieve game information. A number of different
//implementations are available in boardgame/storage that can all be used.
//Typically you don't use this interface directly--it's defined just to
//formalize the interface between the core engine and the underlying storage
//layer.
type StorageManager interface {
	//State returns the StateStorageRecord for the game at the given version,
	//or nil.
	State(gameID string, version int) (StateStorageRecord, error)

	//Move returns the MoveStorageRecord for the game at the given version, or
	//nil.
	Move(gameID string, version int) (*MoveStorageRecord, error)

	//Moves is like Move but returns all moves from fromVersion (exclusive) to
	//toVersion (inclusive). If fromVersion == toVersion, should return
	//toVersion. In many storage subsystems this is cheaper than repeated
	//calls to Move, which is why it's broken out separately.
	Moves(gameID string, fromVersion, toVersion int) ([]*MoveStorageRecord, error)

	//Game fetches the GameStorageRecord with the given ID from the store, if
	//it exists.
	Game(id string) (*GameStorageRecord, error)

	//AgentState retrieves the most recent state for the given agent
	AgentState(gameID string, player PlayerIndex) ([]byte, error)

	//SaveGameAndCurrentState stores the game and the current state (at
	//game.Version()) into the store at the same time in a transaction. Move
	//is normally provided but will be be nil if game.Version() is 0, denoting
	//the initial state for a game.
	SaveGameAndCurrentState(game *GameStorageRecord, state StateStorageRecord, move *MoveStorageRecord) error

	//SaveAgentState saves the agent state for the given player
	SaveAgentState(gameID string, player PlayerIndex, state []byte) error

	//PlayerMoveApplied is called after a PlayerMove and all of its resulting
	//FixUp moves have been applied. Most StorageManagers don't need to do
	//anything here; it's primarily useful as a callback to signal that a run
	//of moves has been applied, e.g. in the server.
	PlayerMoveApplied(game *GameStorageRecord) error

	//FetchInjectedDataForGame is an override point for other layers to inject
	//triggers for bits of game logic to call into. dataType should be the name
	//of the package that publishes the data type, to avoid collissions (for
	//example, 'github.com/jkomoros/boardgame/server/api.PlayerToSeat'). Things,
	//like server, will override this method to add new data types. Base storage
	//managers need only return nil in all cases.
	FetchInjectedDataForGame(gameID string, dataType string) interface{}
}
<|endoftext|>


<commit_before>package mocknet

import (
	"bytes"
	"errors"
	"io"
	"net"
	"time"

	inet "github.com/libp2p/go-libp2p-net"
	protocol "github.com/libp2p/go-libp2p-protocol"
)

// stream implements inet.Stream
type stream struct {
	write     *io.PipeWriter
	read      *io.PipeReader
	conn      *conn
	toDeliver chan *transportObject

	reset  chan struct{}
	close  chan struct{}
	closed chan struct{}

	state error

	protocol protocol.ID
}

var ErrReset error = errors.New("stream reset")
var ErrClosed error = errors.New("stream closed")

type transportObject struct {
	msg         []byte
	arrivalTime time.Time
}

func NewStream(w *io.PipeWriter, r *io.PipeReader) *stream {
	s := &stream{
		read:      r,
		write:     w,
		reset:     make(chan struct{}, 1),
		close:     make(chan struct{}, 1),
		closed:    make(chan struct{}),
		toDeliver: make(chan *transportObject),
	}

	go s.transport()
	return s
}

//  How to handle errors with writes?
func (s *stream) Write(p []byte) (n int, err error) {
	l := s.conn.link
	delay := l.GetLatency() + l.RateLimit(len(p))
	t := time.Now().Add(delay)
	select {
	case <-s.closed: // bail out if we're closing.
		return 0, s.state
	case s.toDeliver <- &transportObject{msg: p, arrivalTime: t}:
	}
	return len(p), nil
}

func (s *stream) Protocol() protocol.ID {
	return s.protocol
}

func (s *stream) SetProtocol(proto protocol.ID) {
	s.protocol = proto
}

func (s *stream) Close() error {
	select {
	case s.close <- struct{}{}:
	default:
	}
	<-s.closed
	if s.state != ErrClosed {
		return s.state
	}
	return nil
}

func (s *stream) Reset() error {
	// Cancel any pending writes.
	s.write.Close()

	select {
	case s.reset <- struct{}{}:
	default:
	}
	<-s.closed
	if s.state != ErrReset {
		return s.state
	}
	return nil
}

func (s *stream) teardown() {
	s.write.Close()

	// at this point, no streams are writing.
	s.conn.removeStream(s)

	// Mark as closed.
	close(s.closed)

	s.conn.net.notifyAll(func(n inet.Notifiee) {
		n.ClosedStream(s.conn.net, s)
	})
}

func (s *stream) Conn() inet.Conn {
	return s.conn
}

func (s *stream) SetDeadline(t time.Time) error {
	return &net.OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

func (s *stream) SetReadDeadline(t time.Time) error {
	return &net.OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

func (s *stream) SetWriteDeadline(t time.Time) error {
	return &net.OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

func (s *stream) Read(b []byte) (int, error) {
	return s.read.Read(b)
}

// transport will grab message arrival times, wait until that time, and
// then write the message out when it is scheduled to arrive
func (s *stream) transport() {
	defer s.teardown()

	bufsize := 256
	buf := new(bytes.Buffer)
	timer := time.NewTimer(0)
	if !timer.Stop() {
		select {
		case <-timer.C:
		default:
		}
	}

	// cleanup
	defer timer.Stop()

	// writeBuf writes the contents of buf through to the s.Writer.
	// done only when arrival time makes sense.
	drainBuf := func() {
		if buf.Len() > 0 {
			_, err := s.write.Write(buf.Bytes())
			if err != nil {
				return
			}
			buf.Reset()
		}
	}

	// deliverOrWait is a helper func that processes
	// an incoming packet. it waits until the arrival time,
	// and then writes things out.
	deliverOrWait := func(o *transportObject) {
		buffered := len(o.msg) + buf.Len()

		// Yes, we can end up extending a timer multiple times if we
		// keep on making small writes but that shouldn't be too much of an
		// issue. Fixing that would be painful.
		if !timer.Stop() {
			// FIXME: So, we *shouldn't* need to do this but we hang
			// here if we don't... Go bug?
			select {
			case <-timer.C:
			default:
			}
		}
		delay := o.arrivalTime.Sub(time.Now())
		if delay >= 0 {
			timer.Reset(delay)
		} else {
			timer.Reset(0)
		}

		if buffered >= bufsize {
			select {
			case <-timer.C:
			case <-s.reset:
				s.reset <- struct{}{}
				return
			}
			drainBuf()
			// write this message.
			_, err := s.write.Write(o.msg)
			if err != nil {
				log.Error("mock_stream", err)
			}
		} else {
			buf.Write(o.msg)
		}
	}

	for {
		// Reset takes precedent.
		select {
		case <-s.reset:
			s.state = ErrReset
			s.read.CloseWithError(ErrReset)
			return
		default:
		}

		select {
		case <-s.reset:
			s.state = ErrReset
			s.read.CloseWithError(ErrReset)
			return
		case <-s.close:
			s.state = ErrClosed
			drainBuf()
			return
		case o := <-s.toDeliver:
			deliverOrWait(o)
		case <-timer.C: // ok, due to write it out.
			drainBuf()
		}
	}
}
<commit_msg>make sure reset works on half-closed streams<commit_after>package mocknet

import (
	"bytes"
	"errors"
	"io"
	"net"
	"time"

	inet "github.com/libp2p/go-libp2p-net"
	protocol "github.com/libp2p/go-libp2p-protocol"
)

// stream implements inet.Stream
type stream struct {
	write     *io.PipeWriter
	read      *io.PipeReader
	conn      *conn
	toDeliver chan *transportObject

	reset  chan struct{}
	close  chan struct{}
	closed chan struct{}

	writeErr error

	protocol protocol.ID
}

var ErrReset error = errors.New("stream reset")
var ErrClosed error = errors.New("stream closed")

type transportObject struct {
	msg         []byte
	arrivalTime time.Time
}

func NewStream(w *io.PipeWriter, r *io.PipeReader) *stream {
	s := &stream{
		read:      r,
		write:     w,
		reset:     make(chan struct{}, 1),
		close:     make(chan struct{}, 1),
		closed:    make(chan struct{}),
		toDeliver: make(chan *transportObject),
	}

	go s.transport()
	return s
}

//  How to handle errors with writes?
func (s *stream) Write(p []byte) (n int, err error) {
	l := s.conn.link
	delay := l.GetLatency() + l.RateLimit(len(p))
	t := time.Now().Add(delay)
	select {
	case <-s.closed: // bail out if we're closing.
		return 0, s.writeErr
	case s.toDeliver <- &transportObject{msg: p, arrivalTime: t}:
	}
	return len(p), nil
}

func (s *stream) Protocol() protocol.ID {
	return s.protocol
}

func (s *stream) SetProtocol(proto protocol.ID) {
	s.protocol = proto
}

func (s *stream) Close() error {
	select {
	case s.close <- struct{}{}:
	default:
	}
	<-s.closed
	if s.writeErr != ErrClosed {
		return s.writeErr
	}
	return nil
}

func (s *stream) Reset() error {
	// Cancel any pending reads/writes with an error.
	s.write.CloseWithError(ErrReset)
	s.read.CloseWithError(ErrReset)

	select {
	case s.reset <- struct{}{}:
	default:
	}
	<-s.closed

	// No meaningful error case here.
	return nil
}

func (s *stream) teardown() {
	// at this point, no streams are writing.
	s.conn.removeStream(s)

	// Mark as closed.
	close(s.closed)

	s.conn.net.notifyAll(func(n inet.Notifiee) {
		n.ClosedStream(s.conn.net, s)
	})
}

func (s *stream) Conn() inet.Conn {
	return s.conn
}

func (s *stream) SetDeadline(t time.Time) error {
	return &net.OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

func (s *stream) SetReadDeadline(t time.Time) error {
	return &net.OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

func (s *stream) SetWriteDeadline(t time.Time) error {
	return &net.OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

func (s *stream) Read(b []byte) (int, error) {
	return s.read.Read(b)
}

// transport will grab message arrival times, wait until that time, and
// then write the message out when it is scheduled to arrive
func (s *stream) transport() {
	defer s.teardown()

	bufsize := 256
	buf := new(bytes.Buffer)
	timer := time.NewTimer(0)
	if !timer.Stop() {
		select {
		case <-timer.C:
		default:
		}
	}

	// cleanup
	defer timer.Stop()

	// writeBuf writes the contents of buf through to the s.Writer.
	// done only when arrival time makes sense.
	drainBuf := func() error {
		if buf.Len() > 0 {
			_, err := s.write.Write(buf.Bytes())
			if err != nil {
				return err
			}
			buf.Reset()
		}
		return nil
	}

	// deliverOrWait is a helper func that processes
	// an incoming packet. it waits until the arrival time,
	// and then writes things out.
	deliverOrWait := func(o *transportObject) error {
		buffered := len(o.msg) + buf.Len()

		// Yes, we can end up extending a timer multiple times if we
		// keep on making small writes but that shouldn't be too much of an
		// issue. Fixing that would be painful.
		if !timer.Stop() {
			// FIXME: So, we *shouldn't* need to do this but we hang
			// here if we don't... Go bug?
			select {
			case <-timer.C:
			default:
			}
		}
		delay := o.arrivalTime.Sub(time.Now())
		if delay >= 0 {
			timer.Reset(delay)
		} else {
			timer.Reset(0)
		}

		if buffered >= bufsize {
			select {
			case <-timer.C:
			case <-s.reset:
				select {
				case s.reset <- struct{}{}:
				default:
				}
				return ErrReset
			}
			if err := drainBuf(); err != nil {
				return err
			}
			// write this message.
			_, err := s.write.Write(o.msg)
			if err != nil {
				return err
			}
		} else {
			buf.Write(o.msg)
		}
		return nil
	}

	for {
		// Reset takes precedent.
		select {
		case <-s.reset:
			s.writeErr = ErrReset
			return
		default:
		}

		select {
		case <-s.reset:
			s.writeErr = ErrReset
			return
		case <-s.close:
			if err := drainBuf(); err != nil {
				s.resetWith(err)
				return
			}
			s.writeErr = s.write.Close()
			if s.writeErr == nil {
				s.writeErr = ErrClosed
			}
			return
		case o := <-s.toDeliver:
			if err := deliverOrWait(o); err != nil {
				s.resetWith(err)
				return
			}
		case <-timer.C: // ok, due to write it out.
			if err := drainBuf(); err != nil {
				s.resetWith(err)
				return
			}
		}
	}
}

func (s *stream) resetWith(err error) {
	s.write.CloseWithError(err)
	s.read.CloseWithError(err)
	s.writeErr = err
}
<|endoftext|>