<commit_before>package main

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

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

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

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

type DirEntries []DirEntry
type Dirs []Dir

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

type DataEntry struct { // struct IMAGE_RESOURCE_DATA_ENTRY
	OffsetToData uint32
	Size1        uint32
	CodePage     uint32 //FIXME: what value here? for now just using 0
	Reserved     uint32
}

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

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

type StringsHeader struct {
	Length uint32
}

const (
	MASK_SUBDIRECTORY = 1 << 31

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

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

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

	RE = regexp.MustCompile
)

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

type GRPICONDIRENTRY struct {
	ico.IconDirEntryCommon
	Id uint16
}

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

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

type Coff struct {
	pe.FileHeader
	pe.SectionHeader32

	Dir
	DataEntries []DataEntry
	Data        []interface{}

	Relocations []RelocationEntry
	Symbols     []Symbol
	StringsHeader
}

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

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

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

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

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

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

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

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

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

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

		// "directory hierarchy" of .rsrc section: top level goes resource type, then id/name, then language
		Dir{},

		[]DataEntry{},
		[]interface{}{},

		[]RelocationEntry{},

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

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

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

	if len(icons) > 0 {
		// RT_ICONs
		group := GRPICONDIR{ICONDIR: ico.ICONDIR{
			Reserved: 0, // magic num.
			Type:     1, // magic num.
			Count:    uint16(len(icons)),
		}}
		for _, icon := range icons {
			id := <-newid
			r := io.NewSectionReader(iconsf, int64(icon.ImageOffset), int64(icon.BytesInRes))

			coff.AddResource(RT_ICON, id, r, uint32(r.Size()))
			group.Entries = append(group.Entries, GRPICONDIRENTRY{icon.IconDirEntryCommon, id})
		}

		// RT_GROUP_ICON
		coff.AddResource(RT_GROUP_ICON, <-newid, group, uint32(binary.Size(group.ICONDIR)+len(icons)*binary.Size(group.Entries[0])))
	}

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

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

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

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

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

	return nil
}

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

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

type matcher []int

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

	*m = (*m)[:0]
	for i := 1; i < len(subs); i++ {
		*m = append(*m, MustAtoi(subs[i]))
	}
	return true
}
<commit_msg>some small cleaning up<commit_after>package main

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

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

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

type DirEntries []DirEntry
type Dirs []Dir

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

type DataEntry struct { // struct IMAGE_RESOURCE_DATA_ENTRY
	OffsetToData uint32
	Size1        uint32
	CodePage     uint32 //FIXME: what value here? for now just using 0
	Reserved     uint32
}

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

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

type StringsHeader struct {
	Length uint32
}

const (
	MASK_SUBDIRECTORY = 1 << 31

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

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

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

// on storing icons, see: http://blogs.msdn.com/b/oldnewthing/archive/2012/07/20/10331787.aspx
type GRPICONDIR struct {
	ico.ICONDIR
	Entries []GRPICONDIRENTRY
}

type GRPICONDIRENTRY struct {
	ico.IconDirEntryCommon
	Id uint16
}

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

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

type Coff struct {
	pe.FileHeader
	pe.SectionHeader32

	Dir
	DataEntries []DataEntry
	Data        []interface{}

	Relocations []RelocationEntry
	Symbols     []Symbol
	StringsHeader
}

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

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

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

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

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

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

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

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

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

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

		// "directory hierarchy" of .rsrc section: top level goes resource type, then id/name, then language
		Dir{},

		[]DataEntry{},
		[]interface{}{},

		[]RelocationEntry{},

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

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

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

	if len(icons) > 0 {
		// RT_ICONs
		group := GRPICONDIR{ICONDIR: ico.ICONDIR{
			Reserved: 0, // magic num.
			Type:     1, // magic num.
			Count:    uint16(len(icons)),
		}}
		for _, icon := range icons {
			id := <-newid
			r := io.NewSectionReader(iconsf, int64(icon.ImageOffset), int64(icon.BytesInRes))

			coff.AddResource(RT_ICON, id, r, uint32(r.Size()))
			group.Entries = append(group.Entries, GRPICONDIRENTRY{icon.IconDirEntryCommon, id})
		}

		// RT_GROUP_ICON
		coff.AddResource(RT_GROUP_ICON, <-newid, group, uint32(binary.Size(group.ICONDIR)+len(icons)*binary.Size(group.Entries[0])))
	}

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

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

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

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

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

	return nil
}

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

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

type matcher []int

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

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


<commit_before>package main

import (
	"bytes"
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"

	"github.com/hyperhq/runv/driverloader"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/hypervisor/pod"
	"github.com/hyperhq/runv/hypervisor/types"
	"github.com/hyperhq/runv/lib/term"

	"github.com/opencontainers/specs"
)

const (
	_ = iota
	RUNV_ACK
	RUNV_EXECCMD
	RUNV_WINSIZE
)

const shortLen = 12

func TruncateID(id string) string {
	trimTo := shortLen
	if len(id) < shortLen {
		trimTo = len(id)
	}
	return id[:trimTo]
}

// GenerateRandomID returns an unique id
func GenerateRandomID() string {
	for {
		id := make([]byte, 32)
		if _, err := io.ReadFull(rand.Reader, id); err != nil {
			panic(err) // This shouldn't happen
		}
		value := hex.EncodeToString(id)
		// if we try to parse the truncated for as an int and we don't have
		// an error then the value is all numberic and causes issues when
		// used as a hostname. ref #3869
		if _, err := strconv.ParseInt(TruncateID(value), 10, 64); err == nil {
			continue
		}
		return value
	}
}

func removeState(container, root string, sock net.Listener) {
	os.RemoveAll(path.Join(root, container))
	sock.Close()
}

func saveState(container, root string, state *specs.State) (net.Listener, error) {
	stateDir := path.Join(root, container)

	_, err := os.Stat(stateDir)
	if err == nil {
		return nil, fmt.Errorf("Container %s exists\n", container)
	}

	err = os.MkdirAll(stateDir, 0644)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	defer func() {
		if err != nil {
			os.RemoveAll(stateDir)
		}
	}()

	stateData, err := json.MarshalIndent(state, "", "\t")
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	stateFile := path.Join(stateDir, "state.json")
	err = ioutil.WriteFile(stateFile, stateData, 0644)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	sock, err := net.Listen("unix", path.Join(stateDir, "runv.sock"))
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	return sock, nil
}

func execHook(hook specs.Hook, state *specs.State) error {
	b, err := json.Marshal(state)
	if err != nil {
		return err
	}
	cmd := exec.Cmd{
		Path:  hook.Path,
		Args:  hook.Args,
		Env:   hook.Env,
		Stdin: bytes.NewReader(b),
	}
	return cmd.Run()
}

func execPrestartHooks(rt *specs.RuntimeSpec, state *specs.State) error {
	for _, hook := range rt.Hooks.Prestart {
		err := execHook(hook, state)
		if err != nil {
			return err
		}
	}

	return nil
}

func execPoststartHooks(rt *specs.RuntimeSpec, state *specs.State) error {
	for _, hook := range rt.Hooks.Poststart {
		err := execHook(hook, state)
		if err != nil {
			fmt.Printf("exec Poststart hook %s failed %s", hook.Path, err.Error())
		}
	}

	return nil
}

func execPoststopHooks(rt *specs.RuntimeSpec, state *specs.State) error {
	for _, hook := range rt.Hooks.Poststop {
		err := execHook(hook, state)
		if err != nil {
			fmt.Printf("exec Poststop hook %s failed %s", hook.Path, err.Error())
		}
	}

	return nil
}

func prepareInfo(config *startConfig, c *pod.UserContainer, vmId string) (*hypervisor.ContainerInfo, error) {
	var root string
	var err error

	containerId := GenerateRandomID()
	sharedDir := path.Join(hypervisor.BaseDir, vmId, hypervisor.ShareDirTag)
	rootDir := path.Join(sharedDir, containerId)
	os.MkdirAll(rootDir, 0755)

	rootDir = path.Join(rootDir, "rootfs")

	if !filepath.IsAbs(c.Image) {
		root = path.Join(config.BundlePath, c.Image)
	} else {
		root = c.Image
	}

	err = mount(root, rootDir)
	if err != nil {
		fmt.Printf("mount %s to %s failed: %s\n", root, rootDir, err.Error())
		return nil, err
	}

	containerInfo := &hypervisor.ContainerInfo{
		Id:     containerId,
		Rootfs: "rootfs",
		Image:  containerId,
		Fstype: "dir",
	}

	return containerInfo, nil
}

func startVm(config *startConfig, userPod *pod.UserPod, vmId string) (*hypervisor.Vm, error) {
	var (
		err error
		cpu = 1
		mem = 128
	)

	vbox := config.Vbox
	if _, err = os.Stat(vbox); err == nil {
		vbox, err = filepath.Abs(vbox)
		if err != nil {
			fmt.Printf("Cannot get abs path for vbox: %s\n", err.Error())
			return nil, err
		}
	}

	kernel := config.Kernel
	if _, err = os.Stat(kernel); err == nil {
		kernel, err = filepath.Abs(kernel)
		if err != nil {
			fmt.Printf("Cannot get abs path for kernel: %s\n", err.Error())
			return nil, err
		}
	}

	initrd := config.Initrd
	if _, err = os.Stat(initrd); err == nil {
		initrd, err = filepath.Abs(initrd)
		if err != nil {
			fmt.Printf("Cannot get abs path for initrd: %s\n", err.Error())
			return nil, err
		}
	}

	if userPod.Resource.Vcpu > 0 {
		cpu = userPod.Resource.Vcpu
	}

	if userPod.Resource.Memory > 0 {
		mem = userPod.Resource.Memory
	}

	b := &hypervisor.BootConfig{
		Kernel: kernel,
		Initrd: initrd,
		Bios:   "",
		Cbfs:   "",
		Vbox:   vbox,
		CPU:    cpu,
		Memory: mem,
	}

	vm := hypervisor.NewVm(vmId, cpu, mem, false, types.VM_KEEP_NONE)
	err = vm.Launch(b)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	return vm, nil
}

func startVContainer(config *startConfig) {
	var (
		err      error
		Response *types.VmResponse
	)

	hypervisor.InterfaceCount = 0

	driver := config.Driver
	if hypervisor.HDriver, err = driverloader.Probe(driver); err != nil {
		fmt.Printf("%s\n", err.Error())
		return
	}

	root := config.Root

	container := config.Name
	podId := fmt.Sprintf("pod-%s", pod.RandStr(10, "alpha"))
	vmId := fmt.Sprintf("vm-%s", pod.RandStr(10, "alpha"))

	fmt.Printf("runv container id: %s\n", container)

	state := &specs.State{
		Version:    config.LinuxSpec.Spec.Version,
		ID:         container,
		Pid:        -1,
		BundlePath: config.BundlePath,
	}

	sock, err := saveState(container, root, state)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return
	}

	defer removeState(container, root, sock)

	userPod := pod.ConvertOCF2PureUserPod(&config.LinuxSpec, &config.LinuxRuntimeSpec)
	mypod := hypervisor.NewPod(podId, userPod)

	vm, err := startVm(config, userPod, vmId)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return
	}

	defer func() {
		Response = vm.StopPod(mypod, "yes")

		if Response.Data == nil {
			fmt.Printf("StopPod fail: QEMU response data is nil\n")
			return
		}
		fmt.Printf("result: code %d %s\n", Response.Code, Response.Cause)
		os.RemoveAll(path.Join(hypervisor.BaseDir, vmId))
	}()

	err = execPrestartHooks(&config.LinuxRuntimeSpec.RuntimeSpec, state)
	if err != nil {
		fmt.Printf("execute Prestart hooks failed, %s\n", err.Error())
		return
	}

	inFd, _ := term.GetFdInfo(os.Stdin)
	outFd, isTerminalOut := term.GetFdInfo(os.Stdout)

	oldState, err := term.SetRawTerminal(inFd)
	if err != nil {
		return
	}

	defer term.RestoreTerminal(inFd, oldState)

	tag := pod.RandStr(8, "alphanum")
	ttyCallback := make(chan *types.VmResponse, 1)

	// using pipes in vm.Attach to avoid the stdio to be closed
	fromStd, toVm := io.Pipe()
	fromVm, toStd := io.Pipe()
	go io.Copy(toVm, os.Stdin)
	go io.Copy(os.Stdout, fromVm)

	Response = vm.StartPod(mypod, userPod, nil, nil)
	if Response.Data == nil {
		fmt.Printf("StartPod fail: QEMU response data is nil\n")
		return
	}
	fmt.Printf("result: code %d %s\n", Response.Code, Response.Cause)

	userContainer := pod.ConvertOCF2UserContainer(&config.LinuxSpec, &config.LinuxRuntimeSpec)
	info, err := prepareInfo(config, userContainer, vmId)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return
	}

	defer func() {
		rootDir := path.Join(hypervisor.BaseDir, vmId, hypervisor.ShareDirTag, info.Id, "rootfs")
		umount(rootDir)
		os.RemoveAll(path.Join(hypervisor.BaseDir, vmId, hypervisor.ShareDirTag, info.Id))
	}()

	err = vm.Attach(fromStd, toStd, tag, info.Id, ttyCallback, nil)
	if err != nil {
		fmt.Printf("StartPod fail: fail to set up tty connection.\n")
		return
	}

	mypod.AddContainer(info.Id, mypod.Id, "", []string{}, types.S_POD_CREATED)
	vm.NewContainer(userContainer, info)
	ListenAndHandleRunvRequests(vm, sock)

	err = execPoststartHooks(&config.LinuxRuntimeSpec.RuntimeSpec, state)
	if err != nil {
		fmt.Printf("execute Poststart hooks failed %s\n", err.Error())
	}

	newTty(vm, path.Join(root, container), tag, outFd, isTerminalOut).monitorTtySize()
	<-ttyCallback

	err = execPoststopHooks(&config.LinuxRuntimeSpec.RuntimeSpec, state)
	if err != nil {
		fmt.Printf("execute Poststop hooks failed %s\n", err.Error())
		return
	}
}

func runvGetTag(conn net.Conn) (tag string, err error) {
	msg, err := hypervisor.ReadVmMessage(conn.(*net.UnixConn))
	if err != nil {
		fmt.Printf("read runv server data failed: %v\n", err)
		return "", err
	}

	if msg.Code != RUNV_ACK {
		return "", fmt.Errorf("unexpected respond code")
	}

	return string(msg.Message), nil
}

func HandleRunvRequest(vm *hypervisor.Vm, conn net.Conn) {
	defer conn.Close()

	msg, err := hypervisor.ReadVmMessage(conn.(*net.UnixConn))
	if err != nil {
		fmt.Printf("read runv client data failed: %v\n", err)
		return
	}

	switch msg.Code {
	case RUNV_EXECCMD:
		{
			tag := pod.RandStr(8, "alphanum")
			m := &hypervisor.DecodedMessage{
				Code:    RUNV_ACK,
				Message: []byte(tag),
			}
			data := hypervisor.NewVmMessage(m)
			conn.Write(data)

			fmt.Printf("client exec cmd request %s\n", msg.Message[:])
			err = vm.Exec(conn, conn, string(msg.Message[:]), tag, vm.Pod.Containers[0].Id)

			if err != nil {
				fmt.Printf("read runv client data failed: %v\n", err)
			}
		}
	case RUNV_WINSIZE:
		{
			var winSize ttyWinSize
			json.Unmarshal(msg.Message, &winSize)
			//fmt.Printf("client exec winsize request %v\n", winSize)
			vm.Tty(winSize.Tag, winSize.Height, winSize.Width)
		}
	default:
		fmt.Printf("unknown cient request\n")
	}
}

func ListenAndHandleRunvRequests(vm *hypervisor.Vm, sock net.Listener) {
	go func() {
		for {
			conn, err := sock.Accept()
			if err != nil {
				fmt.Printf("accept on runv Socket err: %v\n", err)
				break
			}

			go HandleRunvRequest(vm, conn)
		}
	}()
}
<commit_msg>runv: move pod creating/starting code to dedicated functions<commit_after>package main

import (
	"bytes"
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"

	"github.com/hyperhq/runv/driverloader"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/hypervisor/pod"
	"github.com/hyperhq/runv/hypervisor/types"
	"github.com/hyperhq/runv/lib/term"

	"github.com/opencontainers/specs"
)

const (
	_ = iota
	RUNV_ACK
	RUNV_EXECCMD
	RUNV_WINSIZE
)

const shortLen = 12

func TruncateID(id string) string {
	trimTo := shortLen
	if len(id) < shortLen {
		trimTo = len(id)
	}
	return id[:trimTo]
}

// GenerateRandomID returns an unique id
func GenerateRandomID() string {
	for {
		id := make([]byte, 32)
		if _, err := io.ReadFull(rand.Reader, id); err != nil {
			panic(err) // This shouldn't happen
		}
		value := hex.EncodeToString(id)
		// if we try to parse the truncated for as an int and we don't have
		// an error then the value is all numberic and causes issues when
		// used as a hostname. ref #3869
		if _, err := strconv.ParseInt(TruncateID(value), 10, 64); err == nil {
			continue
		}
		return value
	}
}

func removeState(container, root string, sock net.Listener) {
	os.RemoveAll(path.Join(root, container))
	sock.Close()
}

func saveState(container, root string, state *specs.State) (net.Listener, error) {
	stateDir := path.Join(root, container)

	_, err := os.Stat(stateDir)
	if err == nil {
		return nil, fmt.Errorf("Container %s exists\n", container)
	}

	err = os.MkdirAll(stateDir, 0644)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	defer func() {
		if err != nil {
			os.RemoveAll(stateDir)
		}
	}()

	stateData, err := json.MarshalIndent(state, "", "\t")
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	stateFile := path.Join(stateDir, "state.json")
	err = ioutil.WriteFile(stateFile, stateData, 0644)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	sock, err := net.Listen("unix", path.Join(stateDir, "runv.sock"))
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	return sock, nil
}

func execHook(hook specs.Hook, state *specs.State) error {
	b, err := json.Marshal(state)
	if err != nil {
		return err
	}
	cmd := exec.Cmd{
		Path:  hook.Path,
		Args:  hook.Args,
		Env:   hook.Env,
		Stdin: bytes.NewReader(b),
	}
	return cmd.Run()
}

func execPrestartHooks(rt *specs.RuntimeSpec, state *specs.State) error {
	for _, hook := range rt.Hooks.Prestart {
		err := execHook(hook, state)
		if err != nil {
			return err
		}
	}

	return nil
}

func execPoststartHooks(rt *specs.RuntimeSpec, state *specs.State) error {
	for _, hook := range rt.Hooks.Poststart {
		err := execHook(hook, state)
		if err != nil {
			fmt.Printf("exec Poststart hook %s failed %s", hook.Path, err.Error())
		}
	}

	return nil
}

func execPoststopHooks(rt *specs.RuntimeSpec, state *specs.State) error {
	for _, hook := range rt.Hooks.Poststop {
		err := execHook(hook, state)
		if err != nil {
			fmt.Printf("exec Poststop hook %s failed %s", hook.Path, err.Error())
		}
	}

	return nil
}

func prepareInfo(config *startConfig, c *pod.UserContainer, vmId string) (*hypervisor.ContainerInfo, error) {
	var root string
	var err error

	containerId := GenerateRandomID()
	sharedDir := path.Join(hypervisor.BaseDir, vmId, hypervisor.ShareDirTag)
	rootDir := path.Join(sharedDir, containerId)
	os.MkdirAll(rootDir, 0755)

	rootDir = path.Join(rootDir, "rootfs")

	if !filepath.IsAbs(c.Image) {
		root = path.Join(config.BundlePath, c.Image)
	} else {
		root = c.Image
	}

	err = mount(root, rootDir)
	if err != nil {
		fmt.Printf("mount %s to %s failed: %s\n", root, rootDir, err.Error())
		return nil, err
	}

	containerInfo := &hypervisor.ContainerInfo{
		Id:     containerId,
		Rootfs: "rootfs",
		Image:  containerId,
		Fstype: "dir",
	}

	return containerInfo, nil
}

func startVm(config *startConfig, userPod *pod.UserPod, vmId string) (*hypervisor.Vm, error) {
	var (
		err error
		cpu = 1
		mem = 128
	)

	vbox := config.Vbox
	if _, err = os.Stat(vbox); err == nil {
		vbox, err = filepath.Abs(vbox)
		if err != nil {
			fmt.Printf("Cannot get abs path for vbox: %s\n", err.Error())
			return nil, err
		}
	}

	kernel := config.Kernel
	if _, err = os.Stat(kernel); err == nil {
		kernel, err = filepath.Abs(kernel)
		if err != nil {
			fmt.Printf("Cannot get abs path for kernel: %s\n", err.Error())
			return nil, err
		}
	}

	initrd := config.Initrd
	if _, err = os.Stat(initrd); err == nil {
		initrd, err = filepath.Abs(initrd)
		if err != nil {
			fmt.Printf("Cannot get abs path for initrd: %s\n", err.Error())
			return nil, err
		}
	}

	if userPod.Resource.Vcpu > 0 {
		cpu = userPod.Resource.Vcpu
	}

	if userPod.Resource.Memory > 0 {
		mem = userPod.Resource.Memory
	}

	b := &hypervisor.BootConfig{
		Kernel: kernel,
		Initrd: initrd,
		Bios:   "",
		Cbfs:   "",
		Vbox:   vbox,
		CPU:    cpu,
		Memory: mem,
	}

	vm := hypervisor.NewVm(vmId, cpu, mem, false, types.VM_KEEP_NONE)
	err = vm.Launch(b)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	return vm, nil
}

// pod context for runv daemon
type runvPodContext struct {
	podId     string
	vmId      string
	userPod   *pod.UserPod
	podStatus *hypervisor.PodStatus
	vm        *hypervisor.Vm
}

var daemonPodContext runvPodContext

func startRunvPod(config *startConfig) (context *runvPodContext, err error) {
	context = &daemonPodContext

	hypervisor.InterfaceCount = 0

	driver := config.Driver
	if hypervisor.HDriver, err = driverloader.Probe(driver); err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	context.podId = fmt.Sprintf("pod-%s", pod.RandStr(10, "alpha"))
	context.vmId = fmt.Sprintf("vm-%s", pod.RandStr(10, "alpha"))

	context.userPod = pod.ConvertOCF2PureUserPod(&config.LinuxSpec, &config.LinuxRuntimeSpec)
	context.podStatus = hypervisor.NewPod(context.podId, context.userPod)
	context.vm, err = startVm(config, context.userPod, context.vmId)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	Response := context.vm.StartPod(context.podStatus, context.userPod, nil, nil)
	if Response.Data == nil {
		fmt.Printf("StartPod fail: QEMU response data is nil\n")
		return nil, fmt.Errorf("StartPod fail")
	}
	fmt.Printf("result: code %d %s\n", Response.Code, Response.Cause)

	return context, nil
}

func cleanupRunvPod(context *runvPodContext) {
	Response := context.vm.StopPod(context.podStatus, "yes")

	if Response.Data == nil {
		fmt.Printf("StopPod fail: QEMU response data is nil\n")
		return
	}
	fmt.Printf("result: code %d %s\n", Response.Code, Response.Cause)
	os.RemoveAll(path.Join(hypervisor.BaseDir, context.vmId))
}

func startVContainer(config *startConfig) {
	context, err := startRunvPod(config)
	if err != nil {
		fmt.Printf("Start Pod failed: %s\n", err.Error())
		return
	}
	defer cleanupRunvPod(context)

	root := config.Root
	container := config.Name
	fmt.Printf("runv container id: %s\n", container)

	state := &specs.State{
		Version:    config.LinuxSpec.Spec.Version,
		ID:         container,
		Pid:        -1,
		BundlePath: config.BundlePath,
	}

	sock, err := saveState(container, root, state)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return
	}

	defer removeState(container, root, sock)

	err = execPrestartHooks(&config.LinuxRuntimeSpec.RuntimeSpec, state)
	if err != nil {
		fmt.Printf("execute Prestart hooks failed, %s\n", err.Error())
		return
	}

	inFd, _ := term.GetFdInfo(os.Stdin)
	outFd, isTerminalOut := term.GetFdInfo(os.Stdout)

	oldState, err := term.SetRawTerminal(inFd)
	if err != nil {
		return
	}

	defer term.RestoreTerminal(inFd, oldState)

	tag := pod.RandStr(8, "alphanum")
	ttyCallback := make(chan *types.VmResponse, 1)

	// using pipes in vm.Attach to avoid the stdio to be closed
	fromStd, toVm := io.Pipe()
	fromVm, toStd := io.Pipe()
	go io.Copy(toVm, os.Stdin)
	go io.Copy(os.Stdout, fromVm)

	userContainer := pod.ConvertOCF2UserContainer(&config.LinuxSpec, &config.LinuxRuntimeSpec)
	info, err := prepareInfo(config, userContainer, context.vmId)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return
	}

	defer func() {
		rootDir := path.Join(hypervisor.BaseDir, context.vmId, hypervisor.ShareDirTag, info.Id, "rootfs")
		umount(rootDir)
		os.RemoveAll(path.Join(hypervisor.BaseDir, context.vmId, hypervisor.ShareDirTag, info.Id))
	}()

	err = context.vm.Attach(fromStd, toStd, tag, info.Id, ttyCallback, nil)
	if err != nil {
		fmt.Printf("StartPod fail: fail to set up tty connection.\n")
		return
	}

	context.podStatus.AddContainer(info.Id, context.podId, "", []string{}, types.S_POD_CREATED)
	context.vm.NewContainer(userContainer, info)
	ListenAndHandleRunvRequests(context.vm, sock)

	err = execPoststartHooks(&config.LinuxRuntimeSpec.RuntimeSpec, state)
	if err != nil {
		fmt.Printf("execute Poststart hooks failed %s\n", err.Error())
	}

	newTty(context.vm, path.Join(root, container), tag, outFd, isTerminalOut).monitorTtySize()
	<-ttyCallback

	err = execPoststopHooks(&config.LinuxRuntimeSpec.RuntimeSpec, state)
	if err != nil {
		fmt.Printf("execute Poststop hooks failed %s\n", err.Error())
		return
	}
}

func runvGetTag(conn net.Conn) (tag string, err error) {
	msg, err := hypervisor.ReadVmMessage(conn.(*net.UnixConn))
	if err != nil {
		fmt.Printf("read runv server data failed: %v\n", err)
		return "", err
	}

	if msg.Code != RUNV_ACK {
		return "", fmt.Errorf("unexpected respond code")
	}

	return string(msg.Message), nil
}

func HandleRunvRequest(vm *hypervisor.Vm, conn net.Conn) {
	defer conn.Close()

	msg, err := hypervisor.ReadVmMessage(conn.(*net.UnixConn))
	if err != nil {
		fmt.Printf("read runv client data failed: %v\n", err)
		return
	}

	switch msg.Code {
	case RUNV_EXECCMD:
		{
			tag := pod.RandStr(8, "alphanum")
			m := &hypervisor.DecodedMessage{
				Code:    RUNV_ACK,
				Message: []byte(tag),
			}
			data := hypervisor.NewVmMessage(m)
			conn.Write(data)

			fmt.Printf("client exec cmd request %s\n", msg.Message[:])
			err = vm.Exec(conn, conn, string(msg.Message[:]), tag, vm.Pod.Containers[0].Id)

			if err != nil {
				fmt.Printf("read runv client data failed: %v\n", err)
			}
		}
	case RUNV_WINSIZE:
		{
			var winSize ttyWinSize
			json.Unmarshal(msg.Message, &winSize)
			//fmt.Printf("client exec winsize request %v\n", winSize)
			vm.Tty(winSize.Tag, winSize.Height, winSize.Width)
		}
	default:
		fmt.Printf("unknown cient request\n")
	}
}

func ListenAndHandleRunvRequests(vm *hypervisor.Vm, sock net.Listener) {
	go func() {
		for {
			conn, err := sock.Accept()
			if err != nil {
				fmt.Printf("accept on runv Socket err: %v\n", err)
				break
			}

			go HandleRunvRequest(vm, conn)
		}
	}()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"regexp"
	"strings"

	"github.com/VonC/godbg"
	"github.com/VonC/godbg/exit"
	"github.com/atotto/clipboard"
	"github.com/google/go-github/github"
)

var client *github.Client
var ex *exit.Exit
var pdbg *godbg.Pdbg

func init() {
	ex = exit.Default()
	if os.Getenv("dbg") != "" {
		pdbg = godbg.NewPdbg()
	} else {
		pdbg = godbg.NewPdbg(godbg.OptExcludes([]string{"/seec.go"}))
	}
}

func main() {
	if len(os.Args) != 2 {
		fmt.Println("Usage:       go run seec.go <sha1>")
		fmt.Println("       dbg=1 go run seec.go <sha1> for debug information")
		ex.Exit(0)
	}
	sha1 := os.Args[1]
	client = github.NewClient(nil)
	displayRateLimit()
	commit, _, err := client.Git.GetCommit("git", "git", sha1)
	if err != nil {
		fmt.Printf("Unable to get commit '%s': err '%v'\n", sha1, err)
		ex.Exit(1)
	}
	if len(commit.Parents) != 2 {
		fmt.Printf("Sha1 '%s' has '%d' parent(s) instead of 2\n", sha1, len(commit.Parents))
	}
	clogin := login(*commit.Author.Email, *commit.Author.Name)
	parent := commit.Parents[1]
	res := ""
	res = res + seeCommit(&parent, commit)
	res = res + fmt.Sprintf("(Merged by [%s -- `%s` --](https://github.com/%s) in [commit %s](https://github.com/git/git/commit/%s), %s)  ",
		*commit.Author.Name, clogin, clogin,
		sha1[:7], sha1, commit.Committer.Date.Format("02 Jan 2006"))
	fmt.Println(res)
	clipboard.WriteAll(res)
	fmt.Println("(Copied to the clipboard)")
	displayRateLimit()
}

func displayRateLimit() {
	rate, _, err := client.RateLimit()
	if err != nil {
		fmt.Printf("Error fetching rate limit: %#v\n\n", err)
	} else {
		const layout = "15:04pm (MST)"
		t := rate.Reset.Time
		ts := fmt.Sprintf("%s", t.Format(layout))
		fmt.Printf("\nAPI Rate Limit: %d/%d (reset at %s)\n", rate.Remaining, rate.Limit, ts)
	}
}

type commitsByAuthor struct {
	author   *github.CommitAuthor
	pcommits []*github.Commit
}

func (cba *commitsByAuthor) String() string {
	res := ""
	first := true
	for i, pcommit := range cba.pcommits {
		if !first {
			res = res + ", "
		}
		first = false
		if i == len(cba.pcommits)-1 && i > 0 {
			res = res + "and "
		}
		res = res + (*pcommit.SHA)[:7]
	}
	return fmt.Sprintf("%s=>%s", *cba.author.Name, res)
}

func seeCommit(parent, commit *github.Commit) string {
	var pcommit *github.Commit
	var err error
	for pcommit == nil {
		pcommit, _, err = client.Git.GetCommit("git", "git", *parent.SHA)
		if err != nil {
			fmt.Printf("Unable to get parent commit '%s': err '%v'\n", parent.SHA, err)
			ex.Exit(1)
		}
		// fmt.Printf("pcommit '%+v', len %d\n", pcommit, len(pcommit.Parents))
		if len(pcommit.Parents) == 2 {
			parent = &pcommit.Parents[1]
			pcommit = nil
		} else {
			break
		}
	}
	var commits = make(map[string]*commitsByAuthor)
	if *pcommit.Author.Name == *commit.Author.Name {
		pdbg.Pdbgf("Same author '%s', so call checkParentCommits\nInitial message: '%s'", *pcommit.Author.Name, *commit.Message)
		commits = checkParentCommits(&pcommit.Parents[0], *commit.Message)
	}
	if len(commits) == 0 {
		pauthorname := *pcommit.Author.Name
		pcommitsByAuthor := &commitsByAuthor{pcommit.Author, []*github.Commit{pcommit}}
		commits[pauthorname] = pcommitsByAuthor
		pdbg.Pdbgf("Put single commit '%s' for author '%s'", pcommitsByAuthor, pauthorname)
	}
	res := ""
	for _, pcommitsByAuthor := range commits {
		author := pcommitsByAuthor.author
		pcommits := pcommitsByAuthor.pcommits
		plogin := login(*author.Email, *author.Name)
		first := true
		for i, pcommit := range pcommits {
			if first {
				res = "See "
			} else {
				res = res + ", "
			}
			first = false
			if i == len(pcommits)-1 && i > 0 {
				res = res + "and "
			}
			c := fmt.Sprintf("[commit %s](https://github.com/git/git/commit/%s) [%s]",
				(*pcommit.SHA)[:7], *pcommit.SHA, pcommit.Author.Date.Format("02 Jan 2006"))
			res = res + c
		}
		res = res + fmt.Sprintf(" by [%s (`%s`)](https://github.com/%s).  \n",
			*author.Name, plogin, plogin)
		// seec 777e75b60568b613e452ebbb30a1fb27c4fd7d8a, https://github.com/git/git/commit/777e75b60568b613e452ebbb30a1fb27c4fd7d8a
		res = collect(res, *pcommit.Message, "Test-adapted-from")
		// seec 6dec263333417738528089834bd8cda72017aa31, https://github.com/git/git/commit/6dec263333417738528089834bd8cda72017aa31
		// seec 324a9f41cbf96ad994efc3b20be239116eba0dae, https://github.com/git/git/commit/324a9f41cbf96ad994efc3b20be239116eba0dae
		res = collect(res, *pcommit.Message, "Helped-by")
	}
	return res
}

// for cases like commit a6be52e239df4d4a469a5324273f43a0695fe95d
func checkParentCommits(apcommit *github.Commit, commitmsg string) map[string]*commitsByAuthor {
	res := make(map[string]*commitsByAuthor)
	pcommit, _, err := client.Git.GetCommit("git", "git", *apcommit.SHA)
	if err != nil {
		fmt.Printf("Unable to get check parent commit '%s': err '%v'\n", *apcommit.SHA, err)
		ex.Exit(1)
	}
	pdbg.Pdbgf("pcommit %s", *pcommit.SHA)
	pcommitmsgs := strings.Split(*pcommit.Message, "\n")
	title := pcommitmsgs[0]
	pdbg.Pdbgf("title '%s'", title)
	if strings.Contains(commitmsg, title) {
		pauthorname := *pcommit.Author.Name
		pdbg.Pdbgf("pauthorname='%s' for '%v'", pauthorname, pcommit.Author)
		pcommitsByAuthor := res[pauthorname]
		if pcommitsByAuthor == nil {
			pcommitsByAuthor = &commitsByAuthor{pcommit.Author, []*github.Commit{}}
		}
		pcommitsByAuthor.pcommits = append(pcommitsByAuthor.pcommits, pcommit)
		res[pauthorname] = pcommitsByAuthor
		pdbg.Pdbgf("call checkParentCommits with parents '%+v', pca '%s' for '%s'",
			pcommit.Parents, pcommitsByAuthor.String(), pauthorname)
		ppcommits := checkParentCommits(&pcommit.Parents[0], commitmsg)
		for authorName, pcommitsByAuthor := range ppcommits {
			acommitsByAuthor := res[authorName]
			if acommitsByAuthor == nil {
				res[authorName] = pcommitsByAuthor
			} else {
				for _, pc := range pcommitsByAuthor.pcommits {
					acommitsByAuthor.pcommits = append(acommitsByAuthor.pcommits, pc)
				}
				res[authorName] = acommitsByAuthor
				pdbg.Pdbgf("Put commits '%s' for author '%s'", acommitsByAuthor.String(), authorName)
			}
		}
	}
	return res
}

func login(email string, name string) string {
	opts := &github.SearchOptions{Order: "desc"}
	var res *github.UsersSearchResult
	var err error
	if email != "" {
		res, _, err = client.Search.Users(email, opts)
		if err != nil {
			fmt.Printf("Unable to search user '%s': err '%v'", email, err)
			ex.Exit(1)
		}
	}
	if res == nil || *res.Total == 0 {
		name = strings.Replace(name, "-", " ", -1)
		res, _, err = client.Search.Users(name, opts)
		if err != nil {
			fmt.Printf("Unable to search user '%s': err '%v'", name, err)
			ex.Exit(1)
		}
	}
	if res == nil || *res.Total == 0 {
		var resIssues *github.IssuesSearchResult
		issueSearch := fmt.Sprintf(`"Signed-off-by: %s <%s>"`, name, email)
		resIssues, _, err = client.Search.Issues(issueSearch, opts)
		if err != nil {
			fmt.Printf("Unable to search issue '%s': err '%v'", issueSearch, err)
			ex.Exit(1)
		}
		if resIssues == nil || *resIssues.Total == 0 {
			return ""
		}
		issue := resIssues.Issues[0]
		return *issue.User.Login
	}
	if res == nil || *res.Total == 0 {
		return ""
	}
	return *res.Users[0].Login
}

func collect(res, msg, activity string) string {
	re := regexp.MustCompile(fmt.Sprintf(`%s:\s+([^<\r\n]+)\s+<([^>\r\n]+)>`, activity))
	activitymsg := activity + ": "
	first := true
	for _, resc := range re.FindAllStringSubmatch(msg, -1) {
		if len(resc) != 3 {
			continue
		}
		name := resc[1]
		email := resc[2]
		login := login(email, name)
		if !first {
			activitymsg = activitymsg + ", "
		}
		if login == "" {
			activitymsg = activitymsg + fmt.Sprintf("%s <%s>", name, email)
			first = false
			continue
		}
		activitymsg = activitymsg + fmt.Sprintf("[%s (`%s`)](https://github.com/%s)", name, login, login)
		first = false
	}
	if !first {
		res = res + activitymsg + "  \n"
	}
	return res
}
<commit_msg>seec.go: collect() add 'and' and final dot to the list of contributors<commit_after>package main

import (
	"fmt"
	"os"
	"regexp"
	"strings"

	"github.com/VonC/godbg"
	"github.com/VonC/godbg/exit"
	"github.com/atotto/clipboard"
	"github.com/google/go-github/github"
)

var client *github.Client
var ex *exit.Exit
var pdbg *godbg.Pdbg

func init() {
	ex = exit.Default()
	if os.Getenv("dbg") != "" {
		pdbg = godbg.NewPdbg()
	} else {
		pdbg = godbg.NewPdbg(godbg.OptExcludes([]string{"/seec.go"}))
	}
}

func main() {
	if len(os.Args) != 2 {
		fmt.Println("Usage:       go run seec.go <sha1>")
		fmt.Println("       dbg=1 go run seec.go <sha1> for debug information")
		ex.Exit(0)
	}
	sha1 := os.Args[1]
	client = github.NewClient(nil)
	displayRateLimit()
	commit, _, err := client.Git.GetCommit("git", "git", sha1)
	if err != nil {
		fmt.Printf("Unable to get commit '%s': err '%v'\n", sha1, err)
		ex.Exit(1)
	}
	if len(commit.Parents) != 2 {
		fmt.Printf("Sha1 '%s' has '%d' parent(s) instead of 2\n", sha1, len(commit.Parents))
	}
	clogin := login(*commit.Author.Email, *commit.Author.Name)
	parent := commit.Parents[1]
	res := ""
	res = res + seeCommit(&parent, commit)
	res = res + fmt.Sprintf("(Merged by [%s -- `%s` --](https://github.com/%s) in [commit %s](https://github.com/git/git/commit/%s), %s)  ",
		*commit.Author.Name, clogin, clogin,
		sha1[:7], sha1, commit.Committer.Date.Format("02 Jan 2006"))
	fmt.Println(res)
	clipboard.WriteAll(res)
	fmt.Println("(Copied to the clipboard)")
	displayRateLimit()
}

func displayRateLimit() {
	rate, _, err := client.RateLimit()
	if err != nil {
		fmt.Printf("Error fetching rate limit: %#v\n\n", err)
	} else {
		const layout = "15:04pm (MST)"
		t := rate.Reset.Time
		ts := fmt.Sprintf("%s", t.Format(layout))
		fmt.Printf("\nAPI Rate Limit: %d/%d (reset at %s)\n", rate.Remaining, rate.Limit, ts)
	}
}

type commitsByAuthor struct {
	author   *github.CommitAuthor
	pcommits []*github.Commit
}

func (cba *commitsByAuthor) String() string {
	res := ""
	first := true
	for i, pcommit := range cba.pcommits {
		if !first {
			res = res + ", "
		}
		first = false
		if i == len(cba.pcommits)-1 && i > 0 {
			res = res + "and "
		}
		res = res + (*pcommit.SHA)[:7]
	}
	return fmt.Sprintf("%s=>%s", *cba.author.Name, res)
}

func seeCommit(parent, commit *github.Commit) string {
	var pcommit *github.Commit
	var err error
	for pcommit == nil {
		pcommit, _, err = client.Git.GetCommit("git", "git", *parent.SHA)
		if err != nil {
			fmt.Printf("Unable to get parent commit '%s': err '%v'\n", parent.SHA, err)
			ex.Exit(1)
		}
		// fmt.Printf("pcommit '%+v', len %d\n", pcommit, len(pcommit.Parents))
		if len(pcommit.Parents) == 2 {
			parent = &pcommit.Parents[1]
			pcommit = nil
		} else {
			break
		}
	}
	var commits = make(map[string]*commitsByAuthor)
	if *pcommit.Author.Name == *commit.Author.Name {
		pdbg.Pdbgf("Same author '%s', so call checkParentCommits\nInitial message: '%s'", *pcommit.Author.Name, *commit.Message)
		commits = checkParentCommits(&pcommit.Parents[0], *commit.Message)
	}
	if len(commits) == 0 {
		pauthorname := *pcommit.Author.Name
		pcommitsByAuthor := &commitsByAuthor{pcommit.Author, []*github.Commit{pcommit}}
		commits[pauthorname] = pcommitsByAuthor
		pdbg.Pdbgf("Put single commit '%s' for author '%s'", pcommitsByAuthor, pauthorname)
	}
	res := ""
	for _, pcommitsByAuthor := range commits {
		author := pcommitsByAuthor.author
		pcommits := pcommitsByAuthor.pcommits
		plogin := login(*author.Email, *author.Name)
		first := true
		for i, pcommit := range pcommits {
			if first {
				res = "See "
			} else {
				res = res + ", "
			}
			first = false
			if i == len(pcommits)-1 && i > 0 {
				res = res + "and "
			}
			c := fmt.Sprintf("[commit %s](https://github.com/git/git/commit/%s) [%s]",
				(*pcommit.SHA)[:7], *pcommit.SHA, pcommit.Author.Date.Format("02 Jan 2006"))
			res = res + c
		}
		res = res + fmt.Sprintf(" by [%s (`%s`)](https://github.com/%s).  \n",
			*author.Name, plogin, plogin)
		// seec 777e75b60568b613e452ebbb30a1fb27c4fd7d8a, https://github.com/git/git/commit/777e75b60568b613e452ebbb30a1fb27c4fd7d8a
		res = collect(res, *pcommit.Message, "Test-adapted-from")
		// seec 6dec263333417738528089834bd8cda72017aa31, https://github.com/git/git/commit/6dec263333417738528089834bd8cda72017aa31
		// seec 324a9f41cbf96ad994efc3b20be239116eba0dae, https://github.com/git/git/commit/324a9f41cbf96ad994efc3b20be239116eba0dae
		res = collect(res, *pcommit.Message, "Helped-by")
	}
	return res
}

// for cases like commit a6be52e239df4d4a469a5324273f43a0695fe95d
func checkParentCommits(apcommit *github.Commit, commitmsg string) map[string]*commitsByAuthor {
	res := make(map[string]*commitsByAuthor)
	pcommit, _, err := client.Git.GetCommit("git", "git", *apcommit.SHA)
	if err != nil {
		fmt.Printf("Unable to get check parent commit '%s': err '%v'\n", *apcommit.SHA, err)
		ex.Exit(1)
	}
	pdbg.Pdbgf("pcommit %s", *pcommit.SHA)
	pcommitmsgs := strings.Split(*pcommit.Message, "\n")
	title := pcommitmsgs[0]
	pdbg.Pdbgf("title '%s'", title)
	if strings.Contains(commitmsg, title) {
		pauthorname := *pcommit.Author.Name
		pdbg.Pdbgf("pauthorname='%s' for '%v'", pauthorname, pcommit.Author)
		pcommitsByAuthor := res[pauthorname]
		if pcommitsByAuthor == nil {
			pcommitsByAuthor = &commitsByAuthor{pcommit.Author, []*github.Commit{}}
		}
		pcommitsByAuthor.pcommits = append(pcommitsByAuthor.pcommits, pcommit)
		res[pauthorname] = pcommitsByAuthor
		pdbg.Pdbgf("call checkParentCommits with parents '%+v', pca '%s' for '%s'",
			pcommit.Parents, pcommitsByAuthor.String(), pauthorname)
		ppcommits := checkParentCommits(&pcommit.Parents[0], commitmsg)
		for authorName, pcommitsByAuthor := range ppcommits {
			acommitsByAuthor := res[authorName]
			if acommitsByAuthor == nil {
				res[authorName] = pcommitsByAuthor
			} else {
				for _, pc := range pcommitsByAuthor.pcommits {
					acommitsByAuthor.pcommits = append(acommitsByAuthor.pcommits, pc)
				}
				res[authorName] = acommitsByAuthor
				pdbg.Pdbgf("Put commits '%s' for author '%s'", acommitsByAuthor.String(), authorName)
			}
		}
	}
	return res
}

func login(email string, name string) string {
	opts := &github.SearchOptions{Order: "desc"}
	var res *github.UsersSearchResult
	var err error
	if email != "" {
		res, _, err = client.Search.Users(email, opts)
		if err != nil {
			fmt.Printf("Unable to search user '%s': err '%v'", email, err)
			ex.Exit(1)
		}
	}
	if res == nil || *res.Total == 0 {
		name = strings.Replace(name, "-", " ", -1)
		res, _, err = client.Search.Users(name, opts)
		if err != nil {
			fmt.Printf("Unable to search user '%s': err '%v'", name, err)
			ex.Exit(1)
		}
	}
	if res == nil || *res.Total == 0 {
		var resIssues *github.IssuesSearchResult
		issueSearch := fmt.Sprintf(`"Signed-off-by: %s <%s>"`, name, email)
		resIssues, _, err = client.Search.Issues(issueSearch, opts)
		if err != nil {
			fmt.Printf("Unable to search issue '%s': err '%v'", issueSearch, err)
			ex.Exit(1)
		}
		if resIssues == nil || *resIssues.Total == 0 {
			return ""
		}
		issue := resIssues.Issues[0]
		return *issue.User.Login
	}
	if res == nil || *res.Total == 0 {
		return ""
	}
	return *res.Users[0].Login
}

func collect(res, msg, activity string) string {
	re := regexp.MustCompile(fmt.Sprintf(`%s:\s+([^<\r\n]+)\s+<([^>\r\n]+)>`, activity))
	activitymsg := activity + ": "
	first := true
	allresc := re.FindAllStringSubmatch(msg, -1)
	for i, resc := range allresc {
		dot := ""
		if len(resc) != 3 {
			continue
		}
		name := resc[1]
		email := resc[2]
		login := login(email, name)
		if !first {
			activitymsg = activitymsg + ", "
		}
		if i == len(allresc)-1 {
			dot = "."
			if i > 0 {
				activitymsg = activitymsg + "and "
			}
		}
		if login == "" {
			activitymsg = activitymsg + fmt.Sprintf("%s <%s>%s", name, email, dot)
			first = false
			continue
		}
		activitymsg = activitymsg + fmt.Sprintf("[%s (`%s`)](https://github.com/%s)%s", name, login, login, dot)
		first = false
	}
	if !first {
		res = res + activitymsg + "  \n"
	}
	return res
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/VonC/godbg"
)

// By default, os.Exit()
type ExitFunc func(int)

var exit ExitFunc

func init() {
	exit = os.Exit
}

func main() {
	godbg.Pdbgf("senvgo")
	// http://stackoverflow.com/questions/18963984/exit-with-error-code-in-go
	exit(run())
}

func run() int {
	// here goes
	// the code
	fmt.Fprintf(godbg.Out(), "No program to install: nothing to do")
	return 0
}
<commit_msg>Use exit.DefaultExiter()<commit_after>package main

import (
	"fmt"

	"github.com/VonC/godbg"
	"github.com/VonC/godbg/exit"
)

var exiter exit.Exiter
var status int

func init() {
	exiter = exit.DefaultExiter()
}

func main() {
	godbg.Pdbgf("senvgo")
	// http://stackoverflow.com/questions/18963984/exit-with-error-code-in-go
	status = run()
	exiter.Exit(status)
}

func run() int {
	// here goes
	// the code
	fmt.Fprintf(godbg.Out(), "No program to install: nothing to do")
	return 0
}
<|endoftext|>


<commit_before>package libkb

import (
	"code.google.com/p/go.crypto/openpgp"
	"code.google.com/p/go.crypto/openpgp/armor"
	"code.google.com/p/go.crypto/openpgp/errors"
	"code.google.com/p/go.crypto/openpgp/packet"
	"crypto"
	"fmt"
	"hash"
	"io"
	"time"
)

//
// primaryIdentity returns the Identity marked as primary or the first identity
// if none are so marked.
//
// Copy-paste imported from:
//   https://code.google.com/p/go/source/browse/openpgp/keys.go?repo=crypto&r=1e7a3e301825bf9cb32e0535f3761d62d2d369d1#75
//   func (e *Entity) primaryIdentity() *Identity
//
func getPrimaryIdentity(e *openpgp.Entity) *openpgp.Identity {
	var firstIdentity *openpgp.Identity
	for _, ident := range e.Identities {
		if firstIdentity == nil {
			firstIdentity = ident
		}
		if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
			return ident
		}
	}
	return firstIdentity
}

//
// signingKey return the best candidate Key for signing a message with this Entity.
//
// Copy-paste imported from:
//   https://code.google.com/p/go/source/browse/openpgp/keys.go?repo=crypto&r=1e7a3e301825bf9cb32e0535f3761d62d2d369d1#125
//   func (e *Entity) signingKey(now time.Time) (Key, bool)
//
func getSigningKey(e *openpgp.Entity, now time.Time) (openpgp.Key, bool) {
	candidateSubkey := -1

	for i, subkey := range e.Subkeys {
		if subkey.Sig.FlagsValid &&
			subkey.Sig.FlagSign &&
			subkey.PublicKey.PubKeyAlgo.CanSign() &&
			!subkey.Sig.KeyExpired(now) {
			candidateSubkey = i
			break
		}
	}

	if candidateSubkey != -1 {
		subkey := e.Subkeys[candidateSubkey]
		return openpgp.Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true
	}

	// If we have no candidate subkey then we assume that it's ok to sign
	// with the primary key.
	i := getPrimaryIdentity(e)
	if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign &&
		!i.SelfSignature.KeyExpired(now) {
		return openpgp.Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true
	}

	return openpgp.Key{}, false
}

// Like openpgp.Encrypt (as in p.crypto/openpgp/write.go), but
// don't encrypt at all, just sign the literal unencrypted data.
// Unfortunately we need to duplicate some code here that's already
// in write.go
func AttachedSign(out io.WriteCloser, signed openpgp.Entity, hints *openpgp.FileHints,
	config *packet.Config) (in io.WriteCloser, err error) {

	if hints == nil {
		hints = &openpgp.FileHints{}
	}

	if config == nil {
		config = &packet.Config{}
	}

	var signer *packet.PrivateKey

	if signKey, ok := getSigningKey(&signed, config.Now()); !ok {
		err = errors.InvalidArgumentError("no valid signing keys")
		return
	} else if signer = signKey.PrivateKey; signer.Encrypted {
		err = errors.InvalidArgumentError("signing key must be decrypted")
		return
	}

	hasher := crypto.SHA512

	ops := &packet.OnePassSignature{
		SigType:    packet.SigTypeBinary,
		Hash:       hasher,
		PubKeyAlgo: signer.PubKeyAlgo,
		KeyId:      signer.KeyId,
		IsLast:     true,
	}

	if err = ops.Serialize(out); err != nil {
		return
	}

	var epochSeconds uint32
	if !hints.ModTime.IsZero() {
		epochSeconds = uint32(hints.ModTime.Unix())
	}

	in, err = packet.SerializeLiteral(noOpCloser{out}, hints.IsBinary, hints.FileName, epochSeconds)

	if err != nil {
		return
	}

	// If we need to write a signature packet after the literal
	// data then we need to stop literalData from closing
	// encryptedData.
	in = signatureWriter{out, in, hasher, hasher.New(), signer, config}

	return
}

type DebugWriteCloser struct {
	targ io.WriteCloser
}

func (dbc DebugWriteCloser) Write(buf []byte) (int, error) {
	G.Log.Debug("dbc write: %v", buf)
	return dbc.targ.Write(buf)
}

func (dbc DebugWriteCloser) Close() error {
	G.Log.Debug("dbc Close!")
	return dbc.targ.Close()
}

func ArmoredAttachedSign(out io.WriteCloser, signed openpgp.Entity, hints *openpgp.FileHints,
	config *packet.Config) (in io.WriteCloser, err error) {

	var aout io.WriteCloser
	aout, err = armor.Encode(out, "PGP MESSAGE", PgpArmorHeaders())
	if err != nil {
		return
	}
	fmt.Printf("encoding WTF\n")
	return AttachedSign(DebugWriteCloser{aout}, signed, hints, config)
}

func AttachedSignWrapper(out io.WriteCloser, key PgpKeyBundle, armored bool) (
	in io.WriteCloser, err error) {

	if armored {
		return ArmoredAttachedSign(out, openpgp.Entity(key), nil, nil)
	} else {
		return AttachedSign(out, openpgp.Entity(key), nil, nil)
	}
}

// From here:
//   https://code.google.com/p/go/source/browse/openpgp/write.go?repo=crypto&r=1e7a3e301825bf9cb32e0535f3761d62d2d369d1#326
//
// signatureWriter hashes the contents of a message while passing it along to
// literalData. When closed, it closes literalData, writes a signature packet
// to encryptedData and then also closes encryptedData.
type signatureWriter struct {
	signedData  io.WriteCloser
	literalData io.WriteCloser
	hashType    crypto.Hash
	h           hash.Hash
	signer      *packet.PrivateKey
	config      *packet.Config
}

func (s signatureWriter) Write(data []byte) (int, error) {
	s.h.Write(data)
	return s.literalData.Write(data)
}

func (s signatureWriter) Close() error {
	sig := &packet.Signature{
		SigType:      packet.SigTypeBinary,
		PubKeyAlgo:   s.signer.PubKeyAlgo,
		Hash:         s.hashType,
		CreationTime: s.config.Now(),
		IssuerKeyId:  &s.signer.KeyId,
	}

	if err := sig.Sign(s.h, s.signer, s.config); err != nil {
		return err
	}
	if err := s.literalData.Close(); err != nil {
		return err
	}
	if err := sig.Serialize(s.signedData); err != nil {
		return err
	}
	fmt.Printf("signed data close\n")
	return s.signedData.Close()
}

// noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
// TODO: we have two of these in OpenPGP packages alone. This probably needs
// to be promoted somewhere more common.
type noOpCloser struct {
	w io.Writer
}

func (c noOpCloser) Write(data []byte) (n int, err error) {
	return c.w.Write(data)
}

func (c noOpCloser) Close() error {
	fmt.Printf("close was nooped\n")
	return nil
}
<commit_msg>include the license<commit_after>package libkb

import (
	"code.google.com/p/go.crypto/openpgp"
	"code.google.com/p/go.crypto/openpgp/armor"
	"code.google.com/p/go.crypto/openpgp/errors"
	"code.google.com/p/go.crypto/openpgp/packet"
	"crypto"
	"fmt"
	"hash"
	"io"
	"time"
)

//
// Portions of this file are copied from here:
//
//   https://code.google.com/p/go/source/browse/openpgp/keys.go
//
//  Included under the Go software License, URI is here:
//     https://code.google.com/p/go/source/browse/LICENSE?repo=crypto
//
//  And exact text is here:
//
//--------------------------------------------------------------------
//  Copyright (c) 2009 The Go Authors. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//  notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//  copyright notice, this list of conditions and the following disclaimer
//  in the documentation and/or other materials provided with the
//  distribution.
//     * Neither the name of Google Inc. nor the names of its
//  contributors may be used to endorse or promote products derived from
//  this software without specific prior written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
//  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
//  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
//  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
//  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
//  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
//  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//--------------------------------------------------------------------
//
//
// primaryIdentity returns the Identity marked as primary or the first identity
// if none are so marked.
//
// Copy-paste imported from:
//   https://code.google.com/p/go/source/browse/openpgp/keys.go?repo=crypto&r=1e7a3e301825bf9cb32e0535f3761d62d2d369d1#75
//   func (e *Entity) primaryIdentity() *Identity
//
func getPrimaryIdentity(e *openpgp.Entity) *openpgp.Identity {
	var firstIdentity *openpgp.Identity
	for _, ident := range e.Identities {
		if firstIdentity == nil {
			firstIdentity = ident
		}
		if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
			return ident
		}
	}
	return firstIdentity
}

//
// signingKey return the best candidate Key for signing a message with this Entity.
//
// Copy-paste imported from:
//   https://code.google.com/p/go/source/browse/openpgp/keys.go?repo=crypto&r=1e7a3e301825bf9cb32e0535f3761d62d2d369d1#125
//   func (e *Entity) signingKey(now time.Time) (Key, bool)
//
func getSigningKey(e *openpgp.Entity, now time.Time) (openpgp.Key, bool) {
	candidateSubkey := -1

	for i, subkey := range e.Subkeys {
		if subkey.Sig.FlagsValid &&
			subkey.Sig.FlagSign &&
			subkey.PublicKey.PubKeyAlgo.CanSign() &&
			!subkey.Sig.KeyExpired(now) {
			candidateSubkey = i
			break
		}
	}

	if candidateSubkey != -1 {
		subkey := e.Subkeys[candidateSubkey]
		return openpgp.Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true
	}

	// If we have no candidate subkey then we assume that it's ok to sign
	// with the primary key.
	i := getPrimaryIdentity(e)
	if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign &&
		!i.SelfSignature.KeyExpired(now) {
		return openpgp.Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true
	}

	return openpgp.Key{}, false
}

// Like openpgp.Encrypt (as in p.crypto/openpgp/write.go), but
// don't encrypt at all, just sign the literal unencrypted data.
// Unfortunately we need to duplicate some code here that's already
// in write.go
func AttachedSign(out io.WriteCloser, signed openpgp.Entity, hints *openpgp.FileHints,
	config *packet.Config) (in io.WriteCloser, err error) {

	if hints == nil {
		hints = &openpgp.FileHints{}
	}

	if config == nil {
		config = &packet.Config{}
	}

	var signer *packet.PrivateKey

	if signKey, ok := getSigningKey(&signed, config.Now()); !ok {
		err = errors.InvalidArgumentError("no valid signing keys")
		return
	} else if signer = signKey.PrivateKey; signer.Encrypted {
		err = errors.InvalidArgumentError("signing key must be decrypted")
		return
	}

	hasher := crypto.SHA512

	ops := &packet.OnePassSignature{
		SigType:    packet.SigTypeBinary,
		Hash:       hasher,
		PubKeyAlgo: signer.PubKeyAlgo,
		KeyId:      signer.KeyId,
		IsLast:     true,
	}

	if err = ops.Serialize(out); err != nil {
		return
	}

	var epochSeconds uint32
	if !hints.ModTime.IsZero() {
		epochSeconds = uint32(hints.ModTime.Unix())
	}

	// We don't want the literal serializer to closer the output stream
	// since we're going to need to write to it when we finish up the
	// signature stuff.
	in, err = packet.SerializeLiteral(noOpCloser{out}, hints.IsBinary, hints.FileName, epochSeconds)

	if err != nil {
		return
	}

	// If we need to write a signature packet after the literal
	// data then we need to stop literalData from closing
	// encryptedData.
	in = signatureWriter{out, in, hasher, hasher.New(), signer, config}

	return
}

type DebugWriteCloser struct {
	targ io.WriteCloser
}

func (dbc DebugWriteCloser) Write(buf []byte) (int, error) {
	G.Log.Debug("dbc write: %v", buf)
	return dbc.targ.Write(buf)
}

func (dbc DebugWriteCloser) Close() error {
	G.Log.Debug("dbc Close!")
	return dbc.targ.Close()
}

func ArmoredAttachedSign(out io.WriteCloser, signed openpgp.Entity, hints *openpgp.FileHints,
	config *packet.Config) (in io.WriteCloser, err error) {

	var aout io.WriteCloser
	aout, err = armor.Encode(out, "PGP MESSAGE", PgpArmorHeaders())
	if err != nil {
		return
	}
	return AttachedSign(DebugWriteCloser{aout}, signed, hints, config)
}

func AttachedSignWrapper(out io.WriteCloser, key PgpKeyBundle, armored bool) (
	in io.WriteCloser, err error) {

	if armored {
		return ArmoredAttachedSign(out, openpgp.Entity(key), nil, nil)
	} else {
		return AttachedSign(out, openpgp.Entity(key), nil, nil)
	}
}

// From here:
//   https://code.google.com/p/go/source/browse/openpgp/write.go?repo=crypto&r=1e7a3e301825bf9cb32e0535f3761d62d2d369d1#326
//
// signatureWriter hashes the contents of a message while passing it along to
// literalData. When closed, it closes literalData, writes a signature packet
// to encryptedData and then also closes encryptedData.
type signatureWriter struct {
	signedData  io.WriteCloser
	literalData io.WriteCloser
	hashType    crypto.Hash
	h           hash.Hash
	signer      *packet.PrivateKey
	config      *packet.Config
}

func (s signatureWriter) Write(data []byte) (int, error) {
	s.h.Write(data)
	return s.literalData.Write(data)
}

func (s signatureWriter) Close() error {
	sig := &packet.Signature{
		SigType:      packet.SigTypeBinary,
		PubKeyAlgo:   s.signer.PubKeyAlgo,
		Hash:         s.hashType,
		CreationTime: s.config.Now(),
		IssuerKeyId:  &s.signer.KeyId,
	}

	if err := sig.Sign(s.h, s.signer, s.config); err != nil {
		return err
	}
	if err := s.literalData.Close(); err != nil {
		return err
	}
	if err := sig.Serialize(s.signedData); err != nil {
		return err
	}
	return s.signedData.Close()
}

// noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
// TODO: we have two of these in OpenPGP packages alone. This probably needs
// to be promoted somewhere more common.
//
// From here:
//     https://code.google.com/p/go/source/browse/openpgp/write.go?repo=crypto&r=1e7a3e301825bf9cb32e0535f3761d62d2d369d1#364
//
type noOpCloser struct {
	w io.Writer
}

func (c noOpCloser) Write(data []byte) (n int, err error) {
	return c.w.Write(data)
}

func (c noOpCloser) Close() error {
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/negroni"
	"github.com/go-zoo/bone"
	"github.com/meatballhat/negroni-logrus"
)

// jsonResponse struct encapsulates payload data
type recordedRequests struct {
	Data []Payload `json:"data"`
}

type StateRequest struct {
	Mode        string `json:"mode"`
	Destination string `json:"destination"`
}

type messageResponse struct {
	Message string `json:"message"`
}

func (d *DBClient) startAdminInterface() {
	// starting admin interface
	mux := getBoneRouter(*d)
	n := negroni.Classic()
	n.Use(negronilogrus.NewMiddleware())
	n.UseHandler(mux)

	// admin interface starting message
	log.WithFields(log.Fields{
		"RedisAddress": AppConfig.redisAddress,
		"AdminPort":    AppConfig.adminInterface,
	}).Info("Admin interface is starting...")

	n.Run(AppConfig.adminInterface)
}

// getBoneRouter returns mux for admin interface
func getBoneRouter(d DBClient) *bone.Mux {
	mux := bone.New()

	mux.Get("/records", http.HandlerFunc(d.AllRecordsHandler))
	mux.Delete("/records", http.HandlerFunc(d.DeleteAllRecordsHandler))
	mux.Post("/records", http.HandlerFunc(d.ImportRecordsHandler))

	mux.Get("/state", http.HandlerFunc(d.CurrentStateHandler))
	mux.Post("/state", http.HandlerFunc(d.stateHandler))

	mux.Handle("/*", http.FileServer(http.Dir("static")))

	return mux
}

// AllRecordsHandler returns JSON content type http response
func (d *DBClient) AllRecordsHandler(w http.ResponseWriter, req *http.Request) {
	records, err := d.getAllRecords()

	if err == nil {

		w.Header().Set("Content-Type", "application/json")

		var response recordedRequests
		response.Data = records
		b, err := json.Marshal(response)

		if err != nil {
			log.Error(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
		} else {
			w.Write(b)
			return
		}
	} else {
		log.WithFields(log.Fields{
			"Error":        err.Error(),
			"PasswordUsed": AppConfig.redisPassword,
		}).Error("Failed to get data from cache!")

		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(500) // can't process this entity
		return
	}
}

func (d *DBClient) ImportRecordsHandler(w http.ResponseWriter, req *http.Request) {

	var requests recordedRequests

	defer req.Body.Close()
	body, err := ioutil.ReadAll(req.Body)

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	var response messageResponse

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		response.Message = "Bad request. Nothing to import!"
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &requests)

	if err != nil {
		w.WriteHeader(422) // can't process this entity
		return
	}

	payloads := requests.Data
	if len(payloads) > 0 {
		for _, pl := range payloads {
			bts, err := json.Marshal(pl)

			if err != nil {
				log.WithFields(log.Fields{
					"error": err.Error(),
				}).Error("Failed to marshal json")
			} else {
				// recalculating request hash and storing it in database
				r := request{details: pl.Request}
				d.cache.set(r.hash(), bts)
			}
		}
		response.Message = fmt.Sprintf("%d requests imported successfully", len(payloads))
	} else {
		response.Message = "Bad request. Nothing to import!"
		w.WriteHeader(400)
	}

	b, err := json.Marshal(response)
	w.Write(b)

}

func (d *DBClient) DeleteAllRecordsHandler(w http.ResponseWriter, req *http.Request) {
	err := d.deleteAllRecords()

	w.Header().Set("Content-Type", "application/json")

	var response messageResponse
	if err != nil {
		response.Message = fmt.Sprintf("Something went wrong: %s", err.Error())
		w.WriteHeader(500)
	} else {
		response.Message = "Proxy cache deleted successfuly"
		w.WriteHeader(200)
	}
	b, err := json.Marshal(response)

	w.Write(b)
	return
}

// CurrentStateHandler returns current state
func (d *DBClient) CurrentStateHandler(w http.ResponseWriter, req *http.Request) {
	var resp StateRequest
	resp.Mode = AppConfig.mode
	resp.Destination = AppConfig.destination

	b, _ := json.Marshal(resp)
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.Write(b)
}

// stateHandler handles current proxy state
func (d *DBClient) stateHandler(w http.ResponseWriter, r *http.Request) {
	var stateRequest StateRequest

	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &stateRequest)

	if err != nil {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(422) // can't process this entity
		return
	}
	log.WithFields(log.Fields{
		"newState": stateRequest.Mode,
		"body":     string(body),
	}).Info("Handling state change request!")

	// setting new state
	AppConfig.mode = stateRequest.Mode

	var resp StateRequest
	resp.Mode = stateRequest.Mode
	resp.Destination = AppConfig.destination
	b, _ := json.Marshal(resp)
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.Write(b)

}
<commit_msg>admin now uses boltdb<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/negroni"
	"github.com/go-zoo/bone"
	"github.com/meatballhat/negroni-logrus"
)

// jsonResponse struct encapsulates payload data
type recordedRequests struct {
	Data []Payload `json:"data"`
}

type StateRequest struct {
	Mode        string `json:"mode"`
	Destination string `json:"destination"`
}

type messageResponse struct {
	Message string `json:"message"`
}

func (d *DBClient) startAdminInterface() {
	// starting admin interface
	mux := getBoneRouter(*d)
	n := negroni.Classic()
	n.Use(negronilogrus.NewMiddleware())
	n.UseHandler(mux)

	// admin interface starting message
	log.WithFields(log.Fields{
		"RedisAddress": AppConfig.redisAddress,
		"AdminPort":    AppConfig.adminInterface,
	}).Info("Admin interface is starting...")

	n.Run(AppConfig.adminInterface)
}

// getBoneRouter returns mux for admin interface
func getBoneRouter(d DBClient) *bone.Mux {
	mux := bone.New()

	mux.Get("/records", http.HandlerFunc(d.AllRecordsHandler))
	mux.Delete("/records", http.HandlerFunc(d.DeleteAllRecordsHandler))
	mux.Post("/records", http.HandlerFunc(d.ImportRecordsHandler))

	mux.Get("/state", http.HandlerFunc(d.CurrentStateHandler))
	mux.Post("/state", http.HandlerFunc(d.stateHandler))

	mux.Handle("/*", http.FileServer(http.Dir("static")))

	return mux
}

// AllRecordsHandler returns JSON content type http response
func (d *DBClient) AllRecordsHandler(w http.ResponseWriter, req *http.Request) {
	records, err := d.cache.GetAllRequests()

	if err == nil {

		w.Header().Set("Content-Type", "application/json")

		var response recordedRequests
		response.Data = records
		b, err := json.Marshal(response)

		if err != nil {
			log.Error(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
		} else {
			w.Write(b)
			return
		}
	} else {
		log.WithFields(log.Fields{
			"Error":        err.Error(),
			"PasswordUsed": AppConfig.redisPassword,
		}).Error("Failed to get data from cache!")

		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(500) // can't process this entity
		return
	}
}

func (d *DBClient) ImportRecordsHandler(w http.ResponseWriter, req *http.Request) {

	var requests recordedRequests

	defer req.Body.Close()
	body, err := ioutil.ReadAll(req.Body)

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	var response messageResponse

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		response.Message = "Bad request. Nothing to import!"
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &requests)

	if err != nil {
		w.WriteHeader(422) // can't process this entity
		return
	}

	payloads := requests.Data
	if len(payloads) > 0 {
		for _, pl := range payloads {
			bts, err := json.Marshal(pl)

			if err != nil {
				log.WithFields(log.Fields{
					"error": err.Error(),
				}).Error("Failed to marshal json")
			} else {
				// recalculating request hash and storing it in database
				r := request{details: pl.Request}
				d.cache.set(r.hash(), bts)
			}
		}
		response.Message = fmt.Sprintf("%d requests imported successfully", len(payloads))
	} else {
		response.Message = "Bad request. Nothing to import!"
		w.WriteHeader(400)
	}

	b, err := json.Marshal(response)
	w.Write(b)

}

func (d *DBClient) DeleteAllRecordsHandler(w http.ResponseWriter, req *http.Request) {
	err := d.deleteAllRecords()

	w.Header().Set("Content-Type", "application/json")

	var response messageResponse
	if err != nil {
		response.Message = fmt.Sprintf("Something went wrong: %s", err.Error())
		w.WriteHeader(500)
	} else {
		response.Message = "Proxy cache deleted successfuly"
		w.WriteHeader(200)
	}
	b, err := json.Marshal(response)

	w.Write(b)
	return
}

// CurrentStateHandler returns current state
func (d *DBClient) CurrentStateHandler(w http.ResponseWriter, req *http.Request) {
	var resp StateRequest
	resp.Mode = AppConfig.mode
	resp.Destination = AppConfig.destination

	b, _ := json.Marshal(resp)
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.Write(b)
}

// stateHandler handles current proxy state
func (d *DBClient) stateHandler(w http.ResponseWriter, r *http.Request) {
	var stateRequest StateRequest

	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)

	if err != nil {
		// failed to read response body
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Could not read response body!")
		http.Error(w, "Failed to read request body.", 400)
		return
	}

	err = json.Unmarshal(body, &stateRequest)

	if err != nil {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(422) // can't process this entity
		return
	}
	log.WithFields(log.Fields{
		"newState": stateRequest.Mode,
		"body":     string(body),
	}).Info("Handling state change request!")

	// setting new state
	AppConfig.mode = stateRequest.Mode

	var resp StateRequest
	resp.Mode = stateRequest.Mode
	resp.Destination = AppConfig.destination
	b, _ := json.Marshal(resp)
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.Write(b)

}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"

	"strings"

	"github.com/evandroflores/claimr/messages"
	"github.com/evandroflores/claimr/model"
	"github.com/evandroflores/claimr/utils"
	"github.com/shomali11/slacker"
)

func init() {
	Register("claim <container-name> <reason>", "Claims a container for your use.", claim)
}

func claim(request *slacker.Request, response slacker.ResponseWriter) {
	response.Typing()

	event := getEvent(request)
	if direct, err := isDirect(event.Channel); direct {
		response.Reply(err.Error())
		return
	}

	containerName := request.Param("container-name")

	container, err := model.GetContainer(event.Team, event.Channel, containerName)

	if err != nil {
		response.Reply(err.Error())
		return
	}

	if container == (model.Container{}) {
		response.Reply(fmt.Sprintf(messages.Get("container-not-found-on-channel"), containerName, event.Channel))
		return
	}

	if container.InUseBy != "" {
		inUseMessageKey := utils.IfThenElse(container.InUseBy == event.User, "container-in-use-by-you", "container-in-use")
		response.Reply(fmt.Sprintf(messages.Get(inUseMessageKey.(string)), containerName))
		return
	}

	if container.SetInUse(event.User, getReason(request)) != nil {
		response.Reply(messages.Get("fail-to-update"))
		return
	}

	response.Reply(fmt.Sprintf(messages.Get("container-claimed"), containerName, event.User))
}

func getReason(request *slacker.Request) string {
	if request.Param("reason") == "" {
		return ""
	}
	allText := GetEventText(request)
	reasonToClaim := request.Param("reason")
	idx := strings.Index(allText, reasonToClaim)
	return allText[idx:]
}
<commit_msg>migrate cmd/claim<commit_after>package cmd

import (
	"fmt"

	"strings"

	"github.com/evandroflores/claimr/messages"
	"github.com/evandroflores/claimr/model"
	"github.com/evandroflores/claimr/utils"
	"github.com/shomali11/slacker"
)

func init() {
	Register("claim <container-name> <reason>", "Claims a container for your use.", claim)
}

func claim(request *slacker.Request, response slacker.ResponseWriter) {
	response.Typing()

	event := getEvent(request)

	containerName := request.Param("container-name")

	err := validateInput(event.Channel, containerName)
	if err != nil {
		response.Reply(err.Error())
		return
	}

	container, err := model.GetContainer(event.Team, event.Channel, containerName)

	if err != nil {
		response.Reply(err.Error())
		return
	}

	if container == (model.Container{}) {
		response.Reply(fmt.Sprintf(messages.Get("container-not-found-on-channel"), containerName, event.Channel))
		return
	}

	if container.InUseBy != "" {
		inUseMessageKey := utils.IfThenElse(container.InUseBy == event.User, "container-in-use-by-you", "container-in-use")
		response.Reply(fmt.Sprintf(messages.Get(inUseMessageKey.(string)), containerName))
		return
	}

	if container.SetInUse(event.User, getReason(request)) != nil {
		response.Reply(messages.Get("fail-to-update"))
		return
	}

	response.Reply(fmt.Sprintf(messages.Get("container-claimed"), containerName, event.User))
}

func getReason(request *slacker.Request) string {
	if request.Param("reason") == "" {
		return ""
	}
	allText := GetEventText(request)
	reasonToClaim := request.Param("reason")
	idx := strings.Index(allText, reasonToClaim)
	return allText[idx:]
}
<|endoftext|>