<commit_before>package myq

import (
	"encoding/json"
	"errors"
	"strconv"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
	"os"
	"golang.org/x/net/html"
	"net/http/cookiejar"
	"golang.org/x/net/html/atom"
)

var _Culture string = "en-US"
var _BaseURL = "https://www.myliftmaster.com/"
var _BrandName = "LiftMaster"

// Desired state (ie, I want the door to open)
const (
	desiredState_Closed = 0
	desiredState_Open   = 1
)

type devices []Device
type Device struct {
	// JSON representation
	Gatewayid int `json:"GatewayId"`
	Errorstatus string `json:"ErrorStatus"`
	Errormessage string `json:"ErrorMessage"`
	Lastupdateddatetime time.Time `json:"LastUpdatedDateTime"`
	Gateway string `json:"Gateway"`
	Myqdeviceid int `json:"MyQDeviceId"`
	Imagesource string `json:"Imagesource"`
	Statesince int64 `json:"Statesince"`
	Displaystatesince string `json:"DisplayStatesince"`
	Name string `json:"Name"`
	State string `json:"State"`
	Error bool `json:"Error"`
	Connectserverdeviceid string `json:"ConnectServerDeviceId"`
	Monitoronly bool `json:"MonitorOnly"`
	Lowbattery bool `json:"LowBattery"`
	Sensorerror bool `json:"SensorError"`
	Openerror bool `json:"OpenError"`
	Closeerror bool `json:"CloseError"`
	Disablecontrol bool `json:"DisableControl"`
	Statename string `json:"StateName"`
	Devicetypeid int `json:"DeviceTypeId"`
	Toggleattributename string `json:"ToggleAttributeName"`
	Toggleattributevalue string `json:"ToggleAttributeValue"`
	//  local additions
	location string
}

type triggerStateChangeReturn struct {
	Errormessage string `json:"errormessage"`
}

// There's something odd about this JSON structure. I don't understand
// why PlacesList needs to be a structure, rather than just a type
// []Places.  As a workaround, I've defined two structures, the actual
// JSON->Go representation, and a "shadow" type (type places) to allow range()
// operations to work as expected.  getAllGateways() converts from one
// to the other.
type placeList struct {		// This is the JSON representation
	P []struct {
		Gatewayid int `json:"GatewayId"`
		Name string `json:"Name"`
		Connectserverid string `json:"ConnectServerId"`
		Devicelist string `json:"DeviceList"`
		Isdetonator bool `json:"IsDetonator"`
	}  `json:"Placeslist"`
}
type places map[int]place // Local mapping of PlacesList
type place struct {
	Gatewayid int `json:"GatewayId"`
	Name string `json:"Name"`
	Connectserverid string `json:"ConnectServerId"`
	Devicelist string `json:"DeviceList"`
	Isdetonator bool `json:"IsDetonator"`
}

type MyQ struct {
	c http.Client
	securityToken string
	applicationId string
	devices devices
	locations places
	debug bool
	machineReadable bool
}

// Helpers

func (m *MyQ) debugf(format string, a ...interface{}) (n int, err error) {
	if m.debug {
		format = "# " + format
		return fmt.Fprintf(os.Stderr, format, a...)
	}
	return 0, nil
}

// Do a HTTPS GET and parse the JSON response
func (m *MyQ) doGet(rawurl string, v url.Values, s interface{}) (err error) {
	var r []byte
	var res *http.Response

	u, _ := url.Parse(rawurl)
	u.RawQuery = v.Encode()
	m.debugf("doGet():  URL -  %s\n", u.String())

	t := time.Now()
	if res, err = m.c.Get(u.String()); err != nil {
		m.debugf("Get() failed: %s\n", err)
		return err
	}
	d := time.Since(t)
	m.debugf("   HTTP Response: %s, in %s\n", res.Status, d)
	if res.StatusCode != http.StatusOK {
		return errors.New(res.Status)
	}
	r, err = ioutil.ReadAll(res.Body)
	res.Body.Close()

	if err != nil {
		m.debugf("ReadAll() failed: %s\n", err)
		return err
	}
	return json.Unmarshal(r, &s)
}

// Do a HTTPS POST but don't try to JSON-parse the result.
func (m *MyQ) doPostRaw(rawurl string, v url.Values) (res *http.Response,
	err error) {

	if m.debug {
		u, _ := url.Parse(rawurl)
		u.RawQuery = v.Encode()
		m.debugf("doPost(): URL - %s\n", u)
	}
	t := time.Now()
	if res, err = m.c.PostForm(rawurl, v); err != nil {
		m.debugf("Post() failed: %s\n", err)
		return nil, err
	}
	d := time.Since(t)
	m.debugf("   HTTP Response: %s, in %s\n", res.Status, d)
	if res.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("HTTP Error: %s", res.Status)
	}

	return res, nil
}

// Do a HTTPS POST and unmarshal the JSON result (wrapper around doPostRaw)
func (m *MyQ) doPost(rawurl string, v url.Values, s interface{}) (err error) {
	var res *http.Response

	if res, err = m.doPostRaw(rawurl, v); err != nil {
		return err
	}
	r, err := ioutil.ReadAll(res.Body)
	res.Body.Close()

	return json.Unmarshal(r, &s)
}

// Functions to handle type place
func (p *place) print(machinereadable bool) string {
	if machinereadable {
	return fmt.Sprintf("%s,%d", p.Name, p.Gatewayid)
	}
	return fmt.Sprintf("%s (ID %d)", p.Name, p.Gatewayid)
}

func (p *place) string() string {
	return p.print(false)
}

// Functions to handle type Device
func (d Device) print(machinereadable bool) string {
	var s string

	d.Lastupdateddatetime = d.Lastupdateddatetime.Local()
	if machinereadable {
		s = fmt.Sprintf("%s,%s,%d,%s,%d,%s,%s", d.Name, d.location,
			d.Myqdeviceid, d.Statename,
			d.Lastupdateddatetime.Unix(), d.Errorstatus,
			d.Errormessage)
		if d.Monitoronly {
			s += ",Monitor"
		}
		if d.Lowbattery {
			s += ",LowBat"
		}
		if d.Sensorerror {
			s += ",SensorErr"
		}
		if d.Openerror {
			s += ",OpenErr"
		}
		if d.Closeerror {
			s += ",CloseErr"
		}
		if d.Disablecontrol {
			s += ",Disabled"
		}
	} else {
		s = fmt.Sprintf("%s at %s is %s since %s",
			d.Name, d.location, d.Statename,
			d.Lastupdateddatetime.Format(time.UnixDate))
		if d.Error {
			s += fmt.Sprintf(", ERROR: status = %s, message = %s",
				d.Errorstatus, d.Errormessage)
		}
		if d.Monitoronly {
			s += ",Monitor Only"
		}
		if d.Lowbattery {
			s += ", LowBat"
		}
		if d.Sensorerror {
			s += ", Sensor Error"
		}
		if d.Openerror {
			s += ", Open Error"
		}
		if d.Closeerror {
			s += ", Close Error"
		}
		if d.Disablecontrol {
			s += ", Control disabled"
		}
	}
	return s
}

func (d Device) String() string {
	return d.print(false)
}

// MyQ REST API
func (m *MyQ) getAllGateways() (err error) {
	var j placeList

	// The only option is a current timestamp in ms
	v := url.Values{}
	v.Add("_", strconv.FormatInt(time.Now().UnixNano()/1000000, 10))
	if err = m.doGet(_BaseURL + "Gateway/GetAllGateways", v, &j);
	err != nil {
		return err
	}

	// Convert the JSON array to a map
	m.locations = make(places)
	for _, p := range(j.P) {
		m.locations[p.Gatewayid] = p
	}

	return err
}

func (m *MyQ) getAllDevices() (err error) {
	var d devices

	if err = m.getAllGateways(); err != nil {
		return err
	}

	v := url.Values{}
	v.Add("applicationId", m.applicationId)
	v.Add("securityToken", m.securityToken)
	v.Add("culture", _Culture)
	v.Add("brandName", _BrandName)
	if err = m.doGet(_BaseURL + "api/MyQDevices/GetAllDevices", v, &d);
	err != nil {
		return err
	}

	// Fixup the json decoding.  This isn't an issue with the Go
	// JSON parser but rather bugs/implementation details in the
	// MyQ service (from what I can tell).
	for i, x := range(d) {
		// Set the location for each device
		d[i].location = m.locations[x.Gatewayid].Name
		// Fix unknown doorstates
		if d[i].Statename == "" {
			d[i].Statename = "Unknown"
		}
	}

	m.devices = d
	return nil
}

func (m *MyQ) setDoorState(d Device, desiredstate int) (err error) {
	var t triggerStateChangeReturn

	m.debugf("SetDoorState: desiredstate = %d\n", desiredstate)
	if desiredstate != desiredState_Open &&
	   desiredstate != desiredState_Closed {
		return errors.New("Invalid door state")
	}

	v := url.Values{}
	v.Add("myQDeviceId", strconv.Itoa(d.Myqdeviceid))
	v.Add("attributename", "desireddoorstate")
	v.Add("attributevalue", strconv.Itoa(desiredstate))

	err = m.doPost(_BaseURL + "Device/TriggerStateChange", v, t)
	if t.Errormessage != "" {
		return errors.New(t.Errormessage)
	}
	return err
}

// Find the SecurityToken and ApplicationID in the HTML response from login POST
func findTokens(n *html.Node, securityToken *string, appID *string) (err error) {
	const badparse string = "__BADPARSE"
	var parser func(n *html.Node, securityToken *string, appID *string)

	*securityToken = badparse
	*appID = badparse

	parser = func (n *html.Node, securityToken *string, appID *string) {
		if n.DataAtom == atom.Input && n.Type == html.ElementNode {
		if n.Attr[0].Key == "type" && n.Attr[0].Val == "hidden" &&
		   n.Attr[1].Key == "id" && n.Attr[1].Val == "securityToken" {
		   *securityToken = n.Attr[2].Val
			}
		}

		if n.DataAtom == atom.Input && n.Type == html.ElementNode {
			if n.Attr[0].Key == "type" &&
				n.Attr[0].Val == "hidden"&&
				n.Attr[1].Key == "id" &&
				n.Attr[1].Val == "ApplicationId" {
				*appID = n.Attr[2].Val
			}
		}
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			parser(c, securityToken, appID)
		}
	}

	parser(n, securityToken, appID)

	if *securityToken == badparse && *appID == badparse {
		err = errors.New("Can't find either securityToken or appID in response")
	} else {
		if *securityToken == badparse {
			err = errors.New("Can't find securityToken in response")
		} else if *appID == badparse {
			err = errors.New("Can't find AppID in response")
		}
	}

	return err
}

func (m *MyQ) login(username string, password string) (err error) {
	var resp *http.Response
	var doc *html.Node

	err = nil
	v := url.Values{}
	v.Add("Email", username)
	v.Add("Password", password)

	if m.c.Jar, err = cookiejar.New(nil); err != nil {
		return fmt.Errorf("login(): Can't create CookieJar: %s", err)
	}
	m.c.Timeout = 60 * time.Second

	// This post needs to be done by hand, as the resulting HTML
	// needs to be parsed.
	if resp, err = m.doPostRaw(_BaseURL, v); err != nil {
		return fmt.Errorf("Post() failed: %s\n", err)
	}
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("HTTP Error: %s\n", resp.Status)
	}
	if doc, err = html.Parse(resp.Body); err != nil {
		return err
	}

	return findTokens(doc, &m.securityToken, &m.applicationId)
}

////////////////////////////////////////////////////////////////////////////
// Public API

// Create a new MyQ Session
func (m *MyQ) New(username string, password string, debug bool,
	machineReadable bool) (err error) {
	m.debug = debug
	m.machineReadable = machineReadable

	if err = m.login(username, password); err != nil {
		m.debugf("login error: %s\n", err)
		return errors.New("Login failed")
	}

	if err = m.getAllDevices(); err != nil {
		m.debugf("getAllDevices() error: %s\n", err)
		return errors.New("Can't get device list")
	}

	return err
}

// Find a device/door by its name
func (m *MyQ) FindDoorByName(name string) (d Device, err error) {
	for _, d = range m.devices {
		if d.Name == name {
			return d, nil
		}
	}
	return d, fmt.Errorf("Device named '%s' not found", name)
}

// Show all devices/doors
func (m *MyQ) ShowDoors() {
	for _, d := range m.devices {
		fmt.Println(d.print(m.machineReadable))
	}
}

// Show all locations associated with this MyQ account
func (m *MyQ) ShowLocations() {
	for _, x := range m.locations {
		fmt.Println(x.print(m.machineReadable))
	}
}

// Show details & current state for a specific door/device
func (m *MyQ) DoorDetails(d Device){
	fmt.Println(d.print(m.machineReadable))
}

// Show the state (open, closed, ... ) of a specfic door/device
func (m *MyQ) GetState(d Device) {
	fmt.Println(d.Statename)
}

// Open a specific door/device
func (m *MyQ) Open(d Device) error {
	if d.Statename == "Open" {
		return errors.New("Door is already open")
	} else if d.Statename != "Closed" {
		return fmt.Errorf("Can't open, door is currently %s",
			d.Statename)
	}
	return m.setDoorState(d, desiredState_Open)
}

// Close a specific door/device
func (m *MyQ) Close(d Device) error {
	if d.Statename == "Closed" {
		return errors.New("Door already closed")
	} else if d.Statename != "Open" {
		return fmt.Errorf("Can't close, door is currently %s",
			d.Statename)
	}
	return m.setDoorState(d, desiredState_Closed)
}

// Show devices currently in state /state/ (Open, Closed, ... )
func (m *MyQ) ShowByState(state string) {
	for _, d := range m.devices {
		if d.Statename == state {
			fmt.Println(d.print(m.machineReadable))
		}
	}
}
<commit_msg>Updates to comply with current MyQ Web API<commit_after>package myq

import (
	"encoding/json"
	"errors"
	"strconv"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
	"os"
	"net/http/cookiejar"
)

var _Culture string = "en-US"
var _BaseURL = "https://www.myliftmaster.com/"
var _BrandName = "LiftMaster"

// Desired state (ie, I want the door to open)
const (
	desiredState_Closed = 0
	desiredState_Open   = 1
)

type devices []Device
type Device struct {
	// JSON representation
	Gatewayid int `json:"GatewayId"`
	Errorstatus string `json:"ErrorStatus"`
	Errormessage string `json:"ErrorMessage"`
	Lastupdateddatetime time.Time `json:"LastUpdatedDateTime"`
	Gateway string `json:"Gateway"`
	Myqdeviceid int `json:"MyQDeviceId"`
	Imagesource string `json:"Imagesource"`
	Statesince int64 `json:"Statesince"`
	Displaystatesince string `json:"DisplayStatesince"`
	Name string `json:"Name"`
	State string `json:"State"`
	Error bool `json:"Error"`
	Connectserverdeviceid string `json:"ConnectServerDeviceId"`
	Monitoronly bool `json:"MonitorOnly"`
	Lowbattery bool `json:"LowBattery"`
	Sensorerror bool `json:"SensorError"`
	Openerror bool `json:"OpenError"`
	Closeerror bool `json:"CloseError"`
	Disablecontrol bool `json:"DisableControl"`
	Statename string `json:"StateName"`
	Devicetypeid int `json:"DeviceTypeId"`
	Toggleattributename string `json:"ToggleAttributeName"`
	Toggleattributevalue string `json:"ToggleAttributeValue"`
	//  local additions
	location string
}

type triggerStateChangeReturn struct {
	Errormessage string `json:"errormessage"`
}

// There's something odd about this JSON structure. I don't understand
// why PlacesList needs to be a structure, rather than just a type
// []Places.  As a workaround, I've defined two structures, the actual
// JSON->Go representation, and a "shadow" type (type places) to allow range()
// operations to work as expected.  getAllGateways() converts from one
// to the other.
type placeList struct {		// This is the JSON representation
	P []struct {
		Gatewayid int `json:"GatewayId"`
		Name string `json:"Name"`
		Connectserverid string `json:"ConnectServerId"`
		Devicelist string `json:"DeviceList"`
		Isdetonator bool `json:"IsDetonator"`
	}  `json:"Placeslist"`
}
type places map[int]place // Local mapping of PlacesList
type place struct {
	Gatewayid int `json:"GatewayId"`
	Name string `json:"Name"`
	Connectserverid string `json:"ConnectServerId"`
	Devicelist string `json:"DeviceList"`
	Isdetonator bool `json:"IsDetonator"`
}

type MyQ struct {
	c http.Client
	devices devices
	locations places
	debug bool
	machineReadable bool
}

// Helpers

func (m *MyQ) debugf(format string, a ...interface{}) (n int, err error) {
	if m.debug {
		format = "# " + format
		return fmt.Fprintf(os.Stderr, format, a...)
	}
	return 0, nil
}

// Do a HTTPS GET and parse the JSON response
func (m *MyQ) doGet(rawurl string, v url.Values, s interface{}) (err error) {
	var r []byte
	var res *http.Response

	u, _ := url.Parse(rawurl)
	u.RawQuery = v.Encode()
	m.debugf("doGet():  URL -  %s\n", u.String())

	t := time.Now()
	if res, err = m.c.Get(u.String()); err != nil {
		m.debugf("Get() failed: %s\n", err)
		return err
	}
	d := time.Since(t)
	m.debugf("   HTTP Response: %s, in %s\n", res.Status, d)
	if res.StatusCode != http.StatusOK {
		return errors.New(res.Status)
	}
	r, err = ioutil.ReadAll(res.Body)
	res.Body.Close()

	if err != nil {
		m.debugf("ReadAll() failed: %s\n", err)
		return err
	}
	return json.Unmarshal(r, &s)
}

// Do a HTTPS POST but don't try to JSON-parse the result.
func (m *MyQ) doPostRaw(rawurl string, v url.Values) (res *http.Response,
	err error) {

	if m.debug {
		u, _ := url.Parse(rawurl)
		u.RawQuery = v.Encode()
		m.debugf("doPost(): URL - %s\n", u)
	}
	t := time.Now()
	if res, err = m.c.PostForm(rawurl, v); err != nil {
		m.debugf("Post() failed: %s\n", err)
		return nil, err
	}
	d := time.Since(t)
	m.debugf("   HTTP Response: %s, in %s\n", res.Status, d)
	if res.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("HTTP Error: %s", res.Status)
	}

	return res, nil
}

// Do a HTTPS POST and unmarshal the JSON result (wrapper around doPostRaw)
func (m *MyQ) doPost(rawurl string, v url.Values, s interface{}) (err error) {
	var res *http.Response

	if res, err = m.doPostRaw(rawurl, v); err != nil {
		return err
	}
	r, err := ioutil.ReadAll(res.Body)
	res.Body.Close()

	return json.Unmarshal(r, &s)
}

// Functions to handle type place
func (p *place) print(machinereadable bool) string {
	if machinereadable {
	return fmt.Sprintf("%s,%d", p.Name, p.Gatewayid)
	}
	return fmt.Sprintf("%s (ID %d)", p.Name, p.Gatewayid)
}

func (p *place) string() string {
	return p.print(false)
}

// Functions to handle type Device
func (d Device) print(machinereadable bool) string {
	var s string

	d.Lastupdateddatetime = d.Lastupdateddatetime.Local()
	if machinereadable {
		s = fmt.Sprintf("%s,%s,%d,%s,%d,%s,%s", d.Name, d.location,
			d.Myqdeviceid, d.Statename,
			d.Lastupdateddatetime.Unix(), d.Errorstatus,
			d.Errormessage)
		if d.Monitoronly {
			s += ",Monitor"
		}
		if d.Lowbattery {
			s += ",LowBat"
		}
		if d.Sensorerror {
			s += ",SensorErr"
		}
		if d.Openerror {
			s += ",OpenErr"
		}
		if d.Closeerror {
			s += ",CloseErr"
		}
		if d.Disablecontrol {
			s += ",Disabled"
		}
	} else {
		s = fmt.Sprintf("%s at %s is %s since %s",
			d.Name, d.location, d.Statename,
			d.Lastupdateddatetime.Format(time.UnixDate))
		if d.Error {
			s += fmt.Sprintf(", ERROR: status = %s, message = %s",
				d.Errorstatus, d.Errormessage)
		}
		if d.Monitoronly {
			s += ",Monitor Only"
		}
		if d.Lowbattery {
			s += ", LowBat"
		}
		if d.Sensorerror {
			s += ", Sensor Error"
		}
		if d.Openerror {
			s += ", Open Error"
		}
		if d.Closeerror {
			s += ", Close Error"
		}
		if d.Disablecontrol {
			s += ", Control disabled"
		}
	}
	return s
}

func (d Device) String() string {
	return d.print(false)
}

// MyQ REST API
func (m *MyQ) getAllGateways() (err error) {
	var j placeList

	// The only option is a current timestamp in ms
	v := url.Values{}
	v.Add("_", strconv.FormatInt(time.Now().UnixNano()/1000000, 10))
	if err = m.doGet(_BaseURL + "Gateway/GetAllGateways", v, &j);
	err != nil {
		return err
	}

	// Convert the JSON array to a map
	m.locations = make(places)
	for _, p := range(j.P) {
		m.locations[p.Gatewayid] = p
	}

	return err
}

func (m *MyQ) getAllDevices() (err error) {
	var d devices

	if err = m.getAllGateways(); err != nil {
		return err
	}

	v := url.Values{}
	v.Add("culture", _Culture)
	v.Add("brandName", _BrandName)
	if err = m.doGet(_BaseURL + "api/MyQDevices/GetAllDevices", v, &d);
	err != nil {
		return err
	}

	// Fixup the json decoding.  This isn't an issue with the Go
	// JSON parser but rather bugs/implementation details in the
	// MyQ service (from what I can tell).
	for i, x := range(d) {
		// Set the location for each device
		d[i].location = m.locations[x.Gatewayid].Name
		// Fix unknown doorstates
		if d[i].Statename == "" {
			d[i].Statename = "Unknown"
		}
	}

	m.devices = d
	return nil
}

func (m *MyQ) setDoorState(d Device, desiredstate int) (err error) {
	var t triggerStateChangeReturn

	m.debugf("SetDoorState: desiredstate = %d\n", desiredstate)
	if desiredstate != desiredState_Open &&
	   desiredstate != desiredState_Closed {
		return errors.New("Invalid door state")
	}

	v := url.Values{}
	v.Add("myQDeviceId", strconv.Itoa(d.Myqdeviceid))
	v.Add("attributename", "desireddoorstate")
	v.Add("attributevalue", strconv.Itoa(desiredstate))

	err = m.doPost(_BaseURL + "Device/TriggerStateChange", v, t)
	if t.Errormessage != "" {
		return errors.New(t.Errormessage)
	}
	return err
}

func (m *MyQ) login(username string, password string) (err error) {
	var resp *http.Response

	v := url.Values{}
	v.Add("Email", username)
	v.Add("Password", password)

	if m.c.Jar, err = cookiejar.New(nil); err != nil {
		return fmt.Errorf("login(): Can't create CookieJar: %s", err)
	}
	m.c.Timeout = 60 * time.Second

	// This post needs to be done by hand, as the resulting HTML
	// needs to be parsed.
	if resp, err = m.doPostRaw(_BaseURL, v); err != nil {
		return fmt.Errorf("Post() failed: %s\n", err)
	}
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("HTTP Error: %s\n", resp.Status)
	}
	return nil
}

////////////////////////////////////////////////////////////////////////////
// Public API

// Create a new MyQ Session
func (m *MyQ) New(username string, password string, debug bool,
	machineReadable bool) (err error) {
	m.debug = debug
	m.machineReadable = machineReadable

	if err = m.login(username, password); err != nil {
		m.debugf("login error: %s", err)
		return errors.New("Login failed")
	}

	if err = m.getAllDevices(); err != nil {
		m.debugf("getAllDevices() error: %s", err)
		return errors.New("Can't get device list")
	}

	return err
}

// Find a device/door by its name
func (m *MyQ) FindDoorByName(name string) (d Device, err error) {
	for _, d = range m.devices {
		if d.Name == name {
			return d, nil
		}
	}
	return d, fmt.Errorf("Device named '%s' not found", name)
}

// Show all devices/doors
func (m *MyQ) ShowDoors() {
	for _, d := range m.devices {
		fmt.Println(d.print(m.machineReadable))
	}
}

// Show all locations associated with this MyQ account
func (m *MyQ) ShowLocations() {
	for _, x := range m.locations {
		fmt.Println(x.print(m.machineReadable))
	}
}

// Show details & current state for a specific door/device
func (m *MyQ) DoorDetails(d Device){
	fmt.Println(d.print(m.machineReadable))
}

// Show the state (open, closed, ... ) of a specfic door/device
func (m *MyQ) GetState(d Device) {
	fmt.Println(d.Statename)
}

// Open a specific door/device
func (m *MyQ) Open(d Device) error {
	if d.Statename == "Open" {
		return errors.New("Door is already open")
	} else if d.Statename != "Closed" {
		return fmt.Errorf("Can't open, door is currently %s",
			d.Statename)
	}
	return m.setDoorState(d, desiredState_Open)
}

// Close a specific door/device
func (m *MyQ) Close(d Device) error {
	if d.Statename == "Closed" {
		return errors.New("Door already closed")
	} else if d.Statename != "Open" {
		return fmt.Errorf("Can't close, door is currently %s",
			d.Statename)
	}
	return m.setDoorState(d, desiredState_Closed)
}

// Show devices currently in state /state/ (Open, Closed, ... )
func (m *MyQ) ShowByState(state string) {
	for _, d := range m.devices {
		if d.Statename == state {
			fmt.Println(d.print(m.machineReadable))
		}
	}
}
<|endoftext|>


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

// Command worker runs the vuln worker server.
package main

import (
	"context"
	"flag"
	"fmt"
	"net/http"
	"os"

	"cloud.google.com/go/errorreporting"
	"golang.org/x/vuln/internal/derrors"
	"golang.org/x/vuln/internal/worker"
	"golang.org/x/vuln/internal/worker/log"
	"golang.org/x/vuln/internal/worker/store"
)

var (
	project        = flag.String("project", os.Getenv("GOOGLE_CLOUD_PROJECT"), "project ID")
	namespace      = flag.String("namespace", os.Getenv("VULN_WORKER_NAMESPACE"), "Firestore namespace")
	errorReporting = flag.Bool("reporterrors", os.Getenv("VULN_WORKER_REPORT_ERRORS") == "true", "use the error reporting API")
)

const serviceID = "vuln-worker"

func main() {
	flag.Parse()
	if *project == "" {
		die("need -project or GOOGLE_CLOUD_PROJECT")
	}
	if *namespace == "" {
		die("need -namespace or VULN_WORKER_NAMESPACE")
	}
	if os.Getenv("PORT") == "" && flag.NArg() == 0 {
		die("need PORT or command-line args")
	}

	ctx := log.WithLineLogger(context.Background())

	fstore, err := store.NewFireStore(ctx, *project, *namespace)
	if err != nil {
		die("firestore: %v", err)
	}

	if *errorReporting {
		reportingClient, err := errorreporting.NewClient(ctx, *project, errorreporting.Config{
			ServiceName: serviceID,
			OnError: func(err error) {
				log.Errorf(ctx, "Error reporting failed: %v", err)
			},
		})
		if err != nil {
			die("errorreporting: %v", err)
		}
		derrors.SetReportingClient(reportingClient)
	}

	_, err = worker.NewServer(ctx, *namespace, fstore)
	if err != nil {
		die("NewServer: %v", err)
	}
	addr := ":" + os.Getenv("PORT")
	log.Infof(ctx, "Listening on addr %s", addr)
	die("listening: %v", http.ListenAndServe(addr, nil))
}

func die(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
	fmt.Fprintln(os.Stderr)
	os.Exit(1)
}
<commit_msg>cmd/worker: support command-line mode; add list-updates<commit_after>// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Command worker runs the vuln worker server.
// It can also be used to perform actions from the command line
// by providing a sub-command.
package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"net/http"
	"os"
	"text/tabwriter"

	"cloud.google.com/go/errorreporting"
	"golang.org/x/vuln/internal/derrors"
	"golang.org/x/vuln/internal/worker"
	"golang.org/x/vuln/internal/worker/log"
	"golang.org/x/vuln/internal/worker/store"
)

var (
	project        = flag.String("project", os.Getenv("GOOGLE_CLOUD_PROJECT"), "project ID")
	namespace      = flag.String("namespace", os.Getenv("VULN_WORKER_NAMESPACE"), "Firestore namespace")
	errorReporting = flag.Bool("reporterrors", os.Getenv("VULN_WORKER_REPORT_ERRORS") == "true", "use the error reporting API")
)

const serviceID = "vuln-worker"

func main() {
	flag.Parse()
	if *project == "" {
		die("need -project or GOOGLE_CLOUD_PROJECT")
	}
	if *namespace == "" {
		die("need -namespace or VULN_WORKER_NAMESPACE")
	}
	ctx := log.WithLineLogger(context.Background())

	fstore, err := store.NewFireStore(ctx, *project, *namespace)
	if err != nil {
		die("firestore: %v", err)
	}
	if flag.NArg() > 0 {
		err = runCommandLine(ctx, fstore)
	} else {
		err = runServer(ctx, fstore)
	}
	if err != nil {
		die("%v", err)
	}
}

func runServer(ctx context.Context, st store.Store) error {
	if os.Getenv("PORT") == "" {
		return errors.New("need PORT")
	}

	if *errorReporting {
		reportingClient, err := errorreporting.NewClient(ctx, *project, errorreporting.Config{
			ServiceName: serviceID,
			OnError: func(err error) {
				log.Errorf(ctx, "Error reporting failed: %v", err)
			},
		})
		if err != nil {
			return err
		}
		derrors.SetReportingClient(reportingClient)
	}

	_, err := worker.NewServer(ctx, *namespace, st)
	if err != nil {
		return err
	}
	addr := ":" + os.Getenv("PORT")
	log.Infof(ctx, "Listening on addr %s", addr)
	return fmt.Errorf("listening: %v", http.ListenAndServe(addr, nil))
}

const timeFormat = "2006/01/02 15:04:05"

func runCommandLine(ctx context.Context, st store.Store) error {
	switch flag.Arg(0) {
	case "list-updates":
		return listUpdatesCommand(ctx, st)
	default:
		return fmt.Errorf("unknown command: %q", flag.Arg(1))
	}
}

func listUpdatesCommand(ctx context.Context, st store.Store) error {
	recs, err := st.ListCommitUpdateRecords(ctx)
	if err != nil {
		return err
	}
	tw := tabwriter.NewWriter(os.Stdout, 1, 8, 2, ' ', 0)
	fmt.Fprintf(tw, "Start\tEnd\tCommit\tCVEs Processed\n")
	for _, r := range recs {
		endTime := "unfinished"
		if !r.EndedAt.IsZero() {
			endTime = r.EndedAt.Format(timeFormat)
		}
		fmt.Fprintf(tw, "%s\t%s\t%s\t%d/%d (added %d, modified %d)\n",
			r.StartedAt.Format(timeFormat),
			endTime,
			r.CommitHash,
			r.NumProcessed, r.NumTotal, r.NumAdded, r.NumModified)
	}
	return tw.Flush()
}

func die(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
	fmt.Fprintln(os.Stderr)
	os.Exit(1)
}
<|endoftext|>


<commit_before>package net

type CmdFunc func([][]byte) (interface{}, error)
type REPLY_TYPE uint8

const (
	STATUS_REPLY REPLY_TYPE = 1 + iota
	ERR_REPLY
	INT_REPLY
	BULK_REPLY
	LIST_REPLY
)

var (
	cmdFuncs  = map[string]CmdFunc{}
	replyType = map[string]REPLY_TYPE{}
)

// Register cmd function to server.
func REG(cmd string, types REPLY_TYPE, f CmdFunc) {
	cmdFuncs[cmd] = f
	replyType[cmd] = types
}

// Execute cmd function and generate reply.
func RUN(cmd string, args [][]byte) (reply Reply) {
	f, ok := cmdFuncs[cmd]
	if !ok {
		reply = &ErrReply{
			Msg: "unknwon command '" + cmd + "'",
		}
		return
	}

	t, ok2 := replyType[cmd]
	if !ok2 {
		reply = &ErrReply{
			Msg: "unknwon reply type of command '" + cmd + "'",
		}
		return
	}

	data, ferr := f(args)
	if ferr != nil {
		return &ErrReply{
			Msg: ferr.Error(),
		}
	}

	switch t {
	case STATUS_REPLY:
		if data == nil {
			reply = &StatusReply{
				Code: "OK",
			}
		} else {
			reply = &StatusReply{
				Code: (data.(string)),
			}
		}
		break
	case INT_REPLY:
		reply = &IntReply{
			Nos: data.(int64),
		}
		break

	case BULK_REPLY:
		reply = &BulkReply{
			Bulk: data.(string),
		}
		break

	case LIST_REPLY:
		break
	}

	return
}
<commit_msg>Modify logic of list reply.<commit_after>package net

type CmdFunc func([][]byte) (interface{}, error)
type ListCmdFunc func([][]byte) ([]string, error)
type REPLY_TYPE uint8

const (
	STATUS_REPLY REPLY_TYPE = 1 + iota
	ERR_REPLY
	INT_REPLY
	BULK_REPLY
	LIST_REPLY
)

var (
	cmdFuncs     = map[string]CmdFunc{}
	listCmdFuncs = map[string]ListCmdFunc{}
	replyType    = map[string]REPLY_TYPE{}
)

// Register cmd function to server.
func REG(cmd string, types REPLY_TYPE, f CmdFunc) {
	cmdFuncs[cmd] = f
	replyType[cmd] = types
}
func REGL(cmd string, types REPLY_TYPE, f ListCmdFunc) {
	listCmdFuncs[cmd] = f
}

// Execute cmd function and generate reply.
func RUN(cmd string, args [][]byte) (reply Reply) {
	f, ok := cmdFuncs[cmd]
	fl, okl := listCmdFuncs[cmd]
	if !ok && !okl {
		reply = &ErrReply{
			Msg: "unknwon command '" + cmd + "'",
		}
		return
	}
	if okl {
		data, ferr := fl(args)

		if ferr != nil {
			return &ErrReply{
				Msg: ferr.Error(),
			}
		}
		return &ListReply{
			List: data,
		}
	}

	t, ok2 := replyType[cmd]
	if !ok2 {
		reply = &ErrReply{
			Msg: "unknwon reply type of command '" + cmd + "'",
		}
		return
	}

	data, ferr := f(args)

	if ferr != nil {
		return &ErrReply{
			Msg: ferr.Error(),
		}
	}

	switch t {
	case STATUS_REPLY:
		if data == nil {
			reply = &StatusReply{
				Code: "OK",
			}
		} else {
			reply = &StatusReply{
				Code: (data.(string)),
			}
		}
		break
	case INT_REPLY:
		reply = &IntReply{
			Nos: data.(int64),
		}
		break

	case BULK_REPLY:
		reply = &BulkReply{
			Bulk: data.(string),
		}
		break

		// case LIST_REPLY:
		// 	break
	}

	return
}
<|endoftext|>


<commit_before>package zk

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
)

var (
	// ErrDeadlock is returned by Lock when trying to lock twice without unlocking first
	ErrDeadlock = errors.New("zk: trying to acquire a lock twice")
	// ErrNotLocked is returned by Unlock when trying to release a lock that has not first be acquired.
	ErrNotLocked = errors.New("zk: not locked")
)

// Lock is a mutual exclusion lock.
type Lock struct {
	c        *Conn
	path     string
	acl      []ACL
	lockPath string
	seq      int
}

// NewLock creates a new lock instance using the provided connection, path, and acl.
// The path must be a node that is only used by this lock. A lock instances starts
// unlocked until Lock() is called.
func NewLock(c *Conn, path string, acl []ACL) *Lock {
	return &Lock{
		c:    c,
		path: path,
		acl:  acl,
	}
}

func parseSeq(path string) (int, error) {
	parts := strings.Split(path, "-")
	return strconv.Atoi(parts[len(parts)-1])
}

// Lock attempts to acquire the lock. It will wait to return until the lock
// is acquired or an error occurs. If this instance already has the lock
// then ErrDeadlock is returned.
func (l *Lock) Lock() error {
	if l.lockPath != "" {
		return ErrDeadlock
	}

	prefix := fmt.Sprintf("%s/lock-", l.path)

	path := ""
	var err error
	for i := 0; i < 3; i++ {
		path, err = l.c.CreateProtectedEphemeralSequential(prefix, []byte{}, l.acl)
		if err == ErrNoNode {
			// Create parent node.
			parts := strings.Split(l.path, "/")
			pth := ""
			for _, p := range parts[1:] {
				var exists bool
				pth += "/" + p
				exists, _, err = l.c.Exists(pth)
				if err != nil {
					return err
				}
				if exists == true {
					continue
				}
				_, err = l.c.Create(pth, []byte{}, 0, l.acl)
				if err != nil && err != ErrNodeExists {
					return err
				}
			}
		} else if err == nil {
			break
		} else {
			return err
		}
	}
	if err != nil {
		return err
	}

	seq, err := parseSeq(path)
	if err != nil {
		return err
	}

	for {
		children, _, err := l.c.Children(l.path)
		if err != nil {
			return err
		}

		lowestSeq := seq
		prevSeq := 0
		prevSeqPath := ""
		for _, p := range children {
			s, err := parseSeq(p)
			if err != nil {
				return err
			}
			if s < lowestSeq {
				lowestSeq = s
			}
			if s < seq && s > prevSeq {
				prevSeq = s
				prevSeqPath = p
			}
		}

		if seq == lowestSeq {
			// Acquired the lock
			break
		}

		// Wait on the node next in line for the lock
		_, _, ch, err := l.c.GetW(l.path + "/" + prevSeqPath)
		if err != nil && err != ErrNoNode {
			return err
		} else if err != nil && err == ErrNoNode {
			// try again
			continue
		}

		ev := <-ch
		if ev.Err != nil {
			return ev.Err
		}
	}

	l.seq = seq
	l.lockPath = path
	return nil
}

// Unlock releases an acquired lock. If the lock is not currently acquired by
// this Lock instance than ErrNotLocked is returned.
func (l *Lock) Unlock() error {
	if l.lockPath == "" {
		return ErrNotLocked
	}
	if err := l.c.Delete(l.lockPath, -1); err != nil {
		return err
	}
	l.lockPath = ""
	l.seq = 0
	return nil
}
<commit_msg>vendor: sync with zk master<commit_after>package zk

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
)

var (
	// ErrDeadlock is returned by Lock when trying to lock twice without unlocking first
	ErrDeadlock = errors.New("zk: trying to acquire a lock twice")
	// ErrNotLocked is returned by Unlock when trying to release a lock that has not first be acquired.
	ErrNotLocked = errors.New("zk: not locked")
)

// Lock is a mutual exclusion lock.
type Lock struct {
	c        *Conn
	path     string
	acl      []ACL
	lockPath string
	seq      int
}

// NewLock creates a new lock instance using the provided connection, path, and acl.
// The path must be a node that is only used by this lock. A lock instances starts
// unlocked until Lock() is called.
func NewLock(c *Conn, path string, acl []ACL) *Lock {
	return &Lock{
		c:    c,
		path: path,
		acl:  acl,
	}
}

func parseSeq(path string) (int, error) {
	parts := strings.Split(path, "-")
	return strconv.Atoi(parts[len(parts)-1])
}

// Lock attempts to acquire the lock. It will wait to return until the lock
// is acquired or an error occurs. If this instance already has the lock
// then ErrDeadlock is returned.
func (l *Lock) Lock() error {
	if l.lockPath != "" {
		return ErrDeadlock
	}

	prefix := fmt.Sprintf("%s/lock-", l.path)

	path := ""
	var err error
	for i := 0; i < 3; i++ {
		path, err = l.c.CreateProtectedEphemeralSequential(prefix, []byte{}, l.acl)
		if err == ErrNoNode {
			// Create parent node.
			parts := strings.Split(l.path, "/")
			pth := ""
			for _, p := range parts[1:] {
				var exists bool
				pth += "/" + p
				exists, _, err = l.c.Exists(pth)
				if err != nil {
					return err
				}
				if exists == true {
					continue
				}
				_, err = l.c.Create(pth, []byte{}, 0, l.acl)
				if err != nil && err != ErrNodeExists {
					return err
				}
			}
		} else if err == nil {
			break
		} else {
			return err
		}
	}
	if err != nil {
		return err
	}

	seq, err := parseSeq(path)
	if err != nil {
		return err
	}

	for {
		children, _, err := l.c.Children(l.path)
		if err != nil {
			return err
		}

		lowestSeq := seq
		prevSeq := -1
		prevSeqPath := ""
		for _, p := range children {
			s, err := parseSeq(p)
			if err != nil {
				return err
			}
			if s < lowestSeq {
				lowestSeq = s
			}
			if s < seq && s > prevSeq {
				prevSeq = s
				prevSeqPath = p
			}
		}

		if seq == lowestSeq {
			// Acquired the lock
			break
		}

		// Wait on the node next in line for the lock
		_, _, ch, err := l.c.GetW(l.path + "/" + prevSeqPath)
		if err != nil && err != ErrNoNode {
			return err
		} else if err != nil && err == ErrNoNode {
			// try again
			continue
		}

		ev := <-ch
		if ev.Err != nil {
			return ev.Err
		}
	}

	l.seq = seq
	l.lockPath = path
	return nil
}

// Unlock releases an acquired lock. If the lock is not currently acquired by
// this Lock instance than ErrNotLocked is returned.
func (l *Lock) Unlock() error {
	if l.lockPath == "" {
		return ErrNotLocked
	}
	if err := l.c.Delete(l.lockPath, -1); err != nil {
		return err
	}
	l.lockPath = ""
	l.seq = 0
	return nil
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "container/list"
)

func min(x, y int) int {
	if x < y {
		return x
	} else {
		return y
	}
}

func max(x, y int) int {
	if x < y {
		return x
	} else {
		return y
	}
}

type ARC struct {
    p int
    c int
    T1 *list.List
    B1 *list.List
    T2 *list.List
    B2 *list.List
    cache map[interface{}]*entry
}

type entry struct {
	key interface{}
	value interface{}
	ll *list.List
	el *list.Element
}

func (e *entry) setLRU(list *list.List) {
	e.ll = list
	e.el = e.ll.PushBack(e)
}

func (e *entry) setMRU(list *list.List) {
	e.ll = list
	e.el = e.ll.PushFront(e)
}

func (e *entry) detach() {
	if e.ll != nil {
		e.ll.Remove(e.el)
	}
}

// func (a *ARC) replace(ent *entry) {
// 	if a.T1.Len() > 0 && (a.T1.Len() > a.p || (ent.ll == a.B2 && a.T1.Len() == a.p)) {
// 		// lru := a.T1.Back()
// 		// a.T1.Remove(lru)
// 		// lru.el = a.B1.PushFront()
// 	}
// }

func (a *ARC) req(ent *entry) {
	if ent.ll == a.T1 || ent.ll == a.T2 {
		// Case I
		ent.setMRU(a.T2)
	}
	if ent.ll == a.B1 {
		// Case II
		// Cache Miss in T1 and T2

		// Adaptation
		var d int
        if a.B1.Len() >= a.B2.Len() {
            d = 1
        } else {
            d = a.B2.Len() / a.B1.Len()
        }
		a.p = min(a.p + d, a.c)

		a.replace(ent)
		ent.setMRU(a.T2)
	}

}

func (a *ARC) Put(key, value interface{}) bool {
	ent, ok := a.cache[key]
	if ok != true {
		// Case IV

		ent = &entry{
			key: key,
			value: value,
		}

		if a.T1.Len() + a.B1.Len() == a.c {
			// Case A
			if a.T1.Len() < a.c {
				a.delLRU(a.B1)
				a.replace(ent)
			} else {
				a.delLRU(a.T1)
			}
		} else if a.T1.Len() + a.B1.Len() < a.c {
			// Case B
			if a.T1.Len() + a.T2.Len() + a.B1.Len() + a.B2.Len() >= a.c {
				if a.T1.Len() + a.T2.Len() + a.B1.Len() + a.B2.Len() == 2*a.c {
					a.delLRU(a.B2)
					a.replace(ent)
				}
			}
		}

		a.cache[key] = ent
		ent.setMRU(a.T1)
	} else {
		ent.value = value
		a.req(ent)
	}
	return ok
}

func (a *ARC) delLRU(list *list.List) {
	lru := list.Back()
	list.Remove(lru)
	delete(a.cache, lru.Value.(entry).key)
}

func (a *ARC) replace(ent *entry) {
	if a.T1.Len() > 0 && ((a.T1.Len() > a.p) || (ent.ll == a.B2 && a.T1.Len() == a.p)) {
		lru := a.T1.Back().Value.(entry)
		lru.setMRU(a.B1)
	} else {
		lru := a.T2.Back().Value.(entry)
		lru.setMRU(a.B2)
	}
}

// func (a *ARC) Get(key {}interface) (value {}interface, ok bool) {

// }

func New(c int) *ARC {
	return &ARC{
		p: 0,
		c: c,
		T1: list.New(),
		B1: list.New(),
		T2: list.New(),
		B2: list.New(),
		cache: make(map[interface{}]*entry, c),
	}
}

// func (c *ARC) Add(key, value interface{}) {
//     if c.T1.Has(key) || c.T2.Has(key) {
//         c.T1.Remove(key)
//         c.T2.Add(key, value)
//     } else if c.B1.Has(key) {
//         if c.B1.Len() >= c.B2.Len() {
//             d := 1
//         } else {
//             d := c.B2.Len() / c.B1.Len()
//         }
//         c.p = min(c.p + d, c.c)

//         c.replace(key, value, c.p)
//         c.B1.Remove(key)
//         c.T2.Add(key, value)
//     } else if c.B2.Has(key) {
//         if c.B2.Len() >= c.B1.Len() {
//             d := 1
//         } else {
//             d := c.B1.Len() / c.B2.Len()
//         }
//         c.p = min(c.p - d, 0)
//         // TODO replace
//     } else {
//         if c.T1.Len() + c.B1.Len() == c.c {
//             if c.T1.Len() < c.c {
//                 // TODO
//             } else {
//                 // TODO
//             }
//         } else if c.T1.Len() + c.B1.Len() < c.c {
//         	if c.T1.Len() + c.T2.Len() + c.B2.Len() + c.B1.Len() >= c.c {
//         		// TODO
//         	}
//         }
//         // TODO
//     }
// }

// // Get looks up a key's value from the cache.
// func (c *ARC) Get(key interface{}) (value interface{}, ok bool) {
// 	// TODO
// }

// func (c *ARC) replace(key interface{}, value interface{}, p int) {
// 	if c.T1.Len() > 0 && (c.T1.Len() > c.p || (c.B2.Has(key) && c.T1.Len() == c.p)) {
// 		c.T1.Remove(key)
// 		c.B1.Add(key, value)
// 	} else {
// 		c.T2.Remove(key)
// 		c.B2.Add(key, value)
// 	}
// }

func main() {

	cache := New(3)

	cache.Put("Hello", "World")
	cache.Put("Hello2", "World2")
	cache.Put("Hello3", "World3")

	cache.Put("Hello", "World")
	cache.Put("Hello2", "World2")
	cache.Put("Hello3", "World3")

    fmt.Println("Hello World")
}
<commit_msg>Implement Case III<commit_after>package main

import (
    "fmt"
    "container/list"
)

func min(x, y int) int {
	if x < y {
		return x
	} else {
		return y
	}
}

func max(x, y int) int {
	if x < y {
		return x
	} else {
		return y
	}
}

type ARC struct {
    p int
    c int
    T1 *list.List
    B1 *list.List
    T2 *list.List
    B2 *list.List
    cache map[interface{}]*entry
}

type entry struct {
	key interface{}
	value interface{}
	ll *list.List
	el *list.Element
}

func (e *entry) setLRU(list *list.List) {
	e.ll = list
	e.el = e.ll.PushBack(e)
}

func (e *entry) setMRU(list *list.List) {
	e.ll = list
	e.el = e.ll.PushFront(e)
}

func (e *entry) detach() {
	if e.ll != nil {
		e.ll.Remove(e.el)
	}
}

// func (a *ARC) replace(ent *entry) {
// 	if a.T1.Len() > 0 && (a.T1.Len() > a.p || (ent.ll == a.B2 && a.T1.Len() == a.p)) {
// 		// lru := a.T1.Back()
// 		// a.T1.Remove(lru)
// 		// lru.el = a.B1.PushFront()
// 	}
// }

func (a *ARC) req(ent *entry) {
	if ent.ll == a.T1 || ent.ll == a.T2 {
		// Case I
		ent.setMRU(a.T2)
	}
	if ent.ll == a.B1 {
		// Case II
		// Cache Miss in T1 and T2

		// Adaptation
		var d int
        if a.B1.Len() >= a.B2.Len() {
            d = 1
        } else {
            d = a.B2.Len() / a.B1.Len()
        }
		a.p = min(a.p + d, a.c)

		a.replace(ent)
		ent.setMRU(a.T2)
	}
	if ent.ll == a.B2 {
		// Case III
		// Cache Miss in T1 and T2

		// Adaptation
		var d int
        if a.B2.Len() >= a.B1.Len() {
            d = 1
        } else {
            d = a.B1.Len() / a.B2.Len()
        }
		a.p = max(a.p - d, 0)

		a.replace(ent)
		ent.setMRU(a.T2)
	}
}

func (a *ARC) Put(key, value interface{}) bool {
	ent, ok := a.cache[key]
	if ok != true {
		// Case IV

		ent = &entry{
			key: key,
			value: value,
		}

		if a.T1.Len() + a.B1.Len() == a.c {
			// Case A
			if a.T1.Len() < a.c {
				a.delLRU(a.B1)
				a.replace(ent)
			} else {
				a.delLRU(a.T1)
			}
		} else if a.T1.Len() + a.B1.Len() < a.c {
			// Case B
			if a.T1.Len() + a.T2.Len() + a.B1.Len() + a.B2.Len() >= a.c {
				if a.T1.Len() + a.T2.Len() + a.B1.Len() + a.B2.Len() == 2*a.c {
					a.delLRU(a.B2)
					a.replace(ent)
				}
			}
		}

		a.cache[key] = ent
		ent.setMRU(a.T1)
	} else {
		ent.value = value
		a.req(ent)
	}
	return ok
}

func (a *ARC) delLRU(list *list.List) {
	lru := list.Back()
	list.Remove(lru)
	delete(a.cache, lru.Value.(entry).key)
}

func (a *ARC) replace(ent *entry) {
	if a.T1.Len() > 0 && ((a.T1.Len() > a.p) || (ent.ll == a.B2 && a.T1.Len() == a.p)) {
		lru := a.T1.Back().Value.(entry)
		lru.setMRU(a.B1)
	} else {
		lru := a.T2.Back().Value.(entry)
		lru.setMRU(a.B2)
	}
}

// func (a *ARC) Get(key {}interface) (value {}interface, ok bool) {

// }

func New(c int) *ARC {
	return &ARC{
		p: 0,
		c: c,
		T1: list.New(),
		B1: list.New(),
		T2: list.New(),
		B2: list.New(),
		cache: make(map[interface{}]*entry, c),
	}
}

// func (c *ARC) Add(key, value interface{}) {
//     if c.T1.Has(key) || c.T2.Has(key) {
//         c.T1.Remove(key)
//         c.T2.Add(key, value)
//     } else if c.B1.Has(key) {
//         if c.B1.Len() >= c.B2.Len() {
//             d := 1
//         } else {
//             d := c.B2.Len() / c.B1.Len()
//         }
//         c.p = min(c.p + d, c.c)

//         c.replace(key, value, c.p)
//         c.B1.Remove(key)
//         c.T2.Add(key, value)
//     } else if c.B2.Has(key) {
//         if c.B2.Len() >= c.B1.Len() {
//             d := 1
//         } else {
//             d := c.B1.Len() / c.B2.Len()
//         }
//         c.p = min(c.p - d, 0)
//         // TODO replace
//     } else {
//         if c.T1.Len() + c.B1.Len() == c.c {
//             if c.T1.Len() < c.c {
//                 // TODO
//             } else {
//                 // TODO
//             }
//         } else if c.T1.Len() + c.B1.Len() < c.c {
//         	if c.T1.Len() + c.T2.Len() + c.B2.Len() + c.B1.Len() >= c.c {
//         		// TODO
//         	}
//         }
//         // TODO
//     }
// }

// // Get looks up a key's value from the cache.
// func (c *ARC) Get(key interface{}) (value interface{}, ok bool) {
// 	// TODO
// }

// func (c *ARC) replace(key interface{}, value interface{}, p int) {
// 	if c.T1.Len() > 0 && (c.T1.Len() > c.p || (c.B2.Has(key) && c.T1.Len() == c.p)) {
// 		c.T1.Remove(key)
// 		c.B1.Add(key, value)
// 	} else {
// 		c.T2.Remove(key)
// 		c.B2.Add(key, value)
// 	}
// }

func main() {

	cache := New(3)

	cache.Put("Hello", "World")
	cache.Put("Hello2", "World2")
	cache.Put("Hello3", "World3")

	cache.Put("Hello", "World")
	cache.Put("Hello2", "World2")
	cache.Put("Hello3", "World3")

    fmt.Println("Hello World")
}
<|endoftext|>


<commit_before>// Package arp implements an ARP client and server, as described in RFC 826.
package arp

import (
	"encoding/binary"
	"errors"
	"io"
	"net"

	"github.com/mdlayher/ethernet"
)

//go:generate stringer -output=string.go -type=Operation

var (
	// ErrInvalidMAC is returned when one or more invalid MAC addresses are
	// passed to NewPacket.
	ErrInvalidMAC = errors.New("invalid MAC address")

	// ErrInvalidIP is returned when one or more invalid IPv4 addresses are
	// passed to NewPacket.
	ErrInvalidIP = errors.New("invalid IPv4 address")
)

// An Operation is an ARP operation, such as request or reply.
type Operation uint16

// Operation constants which indicate an ARP request or reply.
const (
	OperationRequest Operation = 1
	OperationReply   Operation = 2
)

// A Packet is a raw ARP packet, as described in RFC 826.
type Packet struct {
	// HardwareType specifies an IANA-assigned hardware type, as described
	// in RFC 826.
	HardwareType uint16

	// ProtocolType specifies the internetwork protocol for which the ARP
	// request is intended.  Typically, this is the IPv4 EtherType.
	ProtocolType uint16

	// MACLength specifies the length of the sender and target MAC addresses
	// included in a Packet.
	MACLength uint8

	// IPLength specifies the length of the sender and target IPv4 addresses
	// included in a Packet.
	IPLength uint8

	// Operation specifies the ARP operation being performed, such as request
	// or reply.
	Operation Operation

	// SenderMAC specifies the MAC address of the sender of this Packet.
	SenderMAC net.HardwareAddr

	// SenderIP specifies the IPv4 address of the sender of this Packet.
	SenderIP net.IP

	// TargetMAC specifies the MAC address of the target of this Packet.
	TargetMAC net.HardwareAddr

	// TargetIP specifies the IPv4 address of the target of this Packet.
	TargetIP net.IP
}

// NewPacket creates a new Packet from an input Operation and MAC/IPv4 address
// values for both a sender and target.
//
// If either MAC address is 6 bytes or more in length, or there is a length
// mismatch between the two, ErrInvalidMAC is returned.
//
// If either IP address is not an IPv4 address, or there is a length mismatch
// between the two, ErrInvalidIP is returned.
func NewPacket(op Operation, srcMAC net.HardwareAddr, srcIP net.IP, dstMAC net.HardwareAddr, dstIP net.IP) (*Packet, error) {
	// Validate MAC addresses for minimum length, and matching length
	if len(srcMAC) < 6 {
		return nil, ErrInvalidMAC
	}
	if len(dstMAC) < 6 {
		return nil, ErrInvalidMAC
	}
	if len(srcMAC) != len(dstMAC) {
		return nil, ErrInvalidMAC
	}

	// Validate IP addresses to ensure they are IPv4 addresses, and
	// correct length
	srcIP = srcIP.To4()
	if srcIP == nil {
		return nil, ErrInvalidIP
	}
	dstIP = dstIP.To4()
	if dstIP == nil {
		return nil, ErrInvalidIP
	}
	if len(srcIP) != len(dstIP) {
		return nil, ErrInvalidIP
	}

	return &Packet{
		// There is no Go-native way to detect hardware type of a network
		// interface, so default to 1 (ethernet 10Mb) for now
		HardwareType: 1,

		// Default to EtherType for IPv4
		ProtocolType: uint16(ethernet.EtherTypeIPv4),

		// Populate other fields using input data
		MACLength: uint8(len(srcMAC)),
		IPLength:  uint8(len(srcIP)),
		Operation: op,
		SenderMAC: srcMAC,
		SenderIP:  srcIP,
		TargetMAC: dstMAC,
		TargetIP:  dstIP,
	}, nil
}

// MarshalBinary allocates a byte slice containing the data from a Packet.
//
// MarshalBinary never returns an error.
func (p *Packet) MarshalBinary() ([]byte, error) {
	// 2 bytes: hardware type
	// 2 bytes: protocol type
	// 1 byte : hardware address length
	// 1 byte : protocol length
	// 2 bytes: operation
	// N bytes: source hardware address
	// 4 bytes: source protocol address
	// N bytes: target hardware address
	// 4 bytes: target protocol address
	b := make([]byte, 2+2+1+1+2+4+4+(p.MACLength*2))

	// Marshal fixed length data

	binary.BigEndian.PutUint16(b[0:2], p.HardwareType)
	binary.BigEndian.PutUint16(b[2:4], p.ProtocolType)

	b[4] = p.MACLength
	b[5] = p.IPLength

	binary.BigEndian.PutUint16(b[6:8], uint16(p.Operation))

	// Marshal variable length data at correct offset using lengths
	// defined in p

	n := 8
	hal := int(p.MACLength)
	pl := int(p.IPLength)

	copy(b[n:n+hal], p.SenderMAC)
	n += hal

	copy(b[n:n+pl], p.SenderIP)
	n += pl

	copy(b[n:n+hal], p.TargetMAC)
	n += hal

	copy(b[n:n+pl], p.TargetIP)

	return b, nil
}

// UnmarshalBinary unmarshals a raw byte slice into a Packet.
func (p *Packet) UnmarshalBinary(b []byte) error {
	// Must have enough room to retrieve MAC and IP lengths
	if len(b) < 8 {
		return io.ErrUnexpectedEOF
	}

	// Retrieve fixed length data

	p.HardwareType = binary.BigEndian.Uint16(b[0:2])
	p.ProtocolType = binary.BigEndian.Uint16(b[2:4])

	p.MACLength = b[4]
	p.IPLength = b[5]

	p.Operation = Operation(binary.BigEndian.Uint16(b[6:8]))

	// Unmarshal variable length data at correct offset using lengths
	// defined by ml and il
	n := 8
	ml := int(p.MACLength)
	il := int(p.IPLength)

	// Must have enough room to retrieve both MAC and IP addresses
	if len(b) < 8+(2*ml)+(2*il) {
		return io.ErrUnexpectedEOF
	}

	sha := make(net.HardwareAddr, ml)
	copy(sha, b[n:n+ml])
	p.SenderMAC = sha
	n += ml

	spa := make(net.IP, il)
	copy(spa, b[n:n+il])
	p.SenderIP = spa
	n += il

	tha := make(net.HardwareAddr, ml)
	copy(tha, b[n:n+ml])
	p.TargetMAC = tha
	n += ml

	tpa := make(net.IP, il)
	copy(tpa, b[n:n+il])
	p.TargetIP = tpa

	return nil
}
<commit_msg>arp: update comment for NewPacket<commit_after>// Package arp implements an ARP client and server, as described in RFC 826.
package arp

import (
	"encoding/binary"
	"errors"
	"io"
	"net"

	"github.com/mdlayher/ethernet"
)

//go:generate stringer -output=string.go -type=Operation

var (
	// ErrInvalidMAC is returned when one or more invalid MAC addresses are
	// passed to NewPacket.
	ErrInvalidMAC = errors.New("invalid MAC address")

	// ErrInvalidIP is returned when one or more invalid IPv4 addresses are
	// passed to NewPacket.
	ErrInvalidIP = errors.New("invalid IPv4 address")
)

// An Operation is an ARP operation, such as request or reply.
type Operation uint16

// Operation constants which indicate an ARP request or reply.
const (
	OperationRequest Operation = 1
	OperationReply   Operation = 2
)

// A Packet is a raw ARP packet, as described in RFC 826.
type Packet struct {
	// HardwareType specifies an IANA-assigned hardware type, as described
	// in RFC 826.
	HardwareType uint16

	// ProtocolType specifies the internetwork protocol for which the ARP
	// request is intended.  Typically, this is the IPv4 EtherType.
	ProtocolType uint16

	// MACLength specifies the length of the sender and target MAC addresses
	// included in a Packet.
	MACLength uint8

	// IPLength specifies the length of the sender and target IPv4 addresses
	// included in a Packet.
	IPLength uint8

	// Operation specifies the ARP operation being performed, such as request
	// or reply.
	Operation Operation

	// SenderMAC specifies the MAC address of the sender of this Packet.
	SenderMAC net.HardwareAddr

	// SenderIP specifies the IPv4 address of the sender of this Packet.
	SenderIP net.IP

	// TargetMAC specifies the MAC address of the target of this Packet.
	TargetMAC net.HardwareAddr

	// TargetIP specifies the IPv4 address of the target of this Packet.
	TargetIP net.IP
}

// NewPacket creates a new Packet from an input Operation and MAC/IPv4 address
// values for both a sender and target.
//
// If either MAC address is less than 6 bytes in length, or there is a length
// mismatch between the two, ErrInvalidMAC is returned.
//
// If either IP address is not an IPv4 address, or there is a length mismatch
// between the two, ErrInvalidIP is returned.
func NewPacket(op Operation, srcMAC net.HardwareAddr, srcIP net.IP, dstMAC net.HardwareAddr, dstIP net.IP) (*Packet, error) {
	// Validate MAC addresses for minimum length, and matching length
	if len(srcMAC) < 6 {
		return nil, ErrInvalidMAC
	}
	if len(dstMAC) < 6 {
		return nil, ErrInvalidMAC
	}
	if len(srcMAC) != len(dstMAC) {
		return nil, ErrInvalidMAC
	}

	// Validate IP addresses to ensure they are IPv4 addresses, and
	// correct length
	srcIP = srcIP.To4()
	if srcIP == nil {
		return nil, ErrInvalidIP
	}
	dstIP = dstIP.To4()
	if dstIP == nil {
		return nil, ErrInvalidIP
	}
	if len(srcIP) != len(dstIP) {
		return nil, ErrInvalidIP
	}

	return &Packet{
		// There is no Go-native way to detect hardware type of a network
		// interface, so default to 1 (ethernet 10Mb) for now
		HardwareType: 1,

		// Default to EtherType for IPv4
		ProtocolType: uint16(ethernet.EtherTypeIPv4),

		// Populate other fields using input data
		MACLength: uint8(len(srcMAC)),
		IPLength:  uint8(len(srcIP)),
		Operation: op,
		SenderMAC: srcMAC,
		SenderIP:  srcIP,
		TargetMAC: dstMAC,
		TargetIP:  dstIP,
	}, nil
}

// MarshalBinary allocates a byte slice containing the data from a Packet.
//
// MarshalBinary never returns an error.
func (p *Packet) MarshalBinary() ([]byte, error) {
	// 2 bytes: hardware type
	// 2 bytes: protocol type
	// 1 byte : hardware address length
	// 1 byte : protocol length
	// 2 bytes: operation
	// N bytes: source hardware address
	// 4 bytes: source protocol address
	// N bytes: target hardware address
	// 4 bytes: target protocol address
	b := make([]byte, 2+2+1+1+2+4+4+(p.MACLength*2))

	// Marshal fixed length data

	binary.BigEndian.PutUint16(b[0:2], p.HardwareType)
	binary.BigEndian.PutUint16(b[2:4], p.ProtocolType)

	b[4] = p.MACLength
	b[5] = p.IPLength

	binary.BigEndian.PutUint16(b[6:8], uint16(p.Operation))

	// Marshal variable length data at correct offset using lengths
	// defined in p

	n := 8
	hal := int(p.MACLength)
	pl := int(p.IPLength)

	copy(b[n:n+hal], p.SenderMAC)
	n += hal

	copy(b[n:n+pl], p.SenderIP)
	n += pl

	copy(b[n:n+hal], p.TargetMAC)
	n += hal

	copy(b[n:n+pl], p.TargetIP)

	return b, nil
}

// UnmarshalBinary unmarshals a raw byte slice into a Packet.
func (p *Packet) UnmarshalBinary(b []byte) error {
	// Must have enough room to retrieve MAC and IP lengths
	if len(b) < 8 {
		return io.ErrUnexpectedEOF
	}

	// Retrieve fixed length data

	p.HardwareType = binary.BigEndian.Uint16(b[0:2])
	p.ProtocolType = binary.BigEndian.Uint16(b[2:4])

	p.MACLength = b[4]
	p.IPLength = b[5]

	p.Operation = Operation(binary.BigEndian.Uint16(b[6:8]))

	// Unmarshal variable length data at correct offset using lengths
	// defined by ml and il
	n := 8
	ml := int(p.MACLength)
	il := int(p.IPLength)

	// Must have enough room to retrieve both MAC and IP addresses
	if len(b) < 8+(2*ml)+(2*il) {
		return io.ErrUnexpectedEOF
	}

	sha := make(net.HardwareAddr, ml)
	copy(sha, b[n:n+ml])
	p.SenderMAC = sha
	n += ml

	spa := make(net.IP, il)
	copy(spa, b[n:n+il])
	p.SenderIP = spa
	n += il

	tha := make(net.HardwareAddr, ml)
	copy(tha, b[n:n+ml])
	p.TargetMAC = tha
	n += ml

	tpa := make(net.IP, il)
	copy(tpa, b[n:n+il])
	p.TargetIP = tpa

	return nil
}
<|endoftext|>


<commit_before>package lookdot_test

import (
	"go/ast"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"reflect"
	"sort"
	"testing"

	"github.com/mdempsky/gocode/internal/lookdot"
)

const src = `
package p

import "time"

type S struct { x int; y int }
func (S) Sv()
func (*S) Sp()
var s S

type Q struct { Z }
var q Q

type I interface { f(); g() }

type P *S

type T1 struct { *T2 }
type T2 struct { *T1 }
func (*T1) t1()
func (*T2) t2()

type X int
func (*X) x()
type X1 struct { X }
type X2 struct { *X }
type X12 struct { X1; X2 }

type A1 int
func (A1) A() int
type A2 int
func (A2) A() int
type A struct { A1; A2; }

type B1 int
func (B1) b()
type B2 struct { b int; B1 }

var loc time.Location
`

var tests = [...]struct {
	lhs  string
	want []string
}{
	{"S", []string{"Sv"}},
	{"*S", []string{"Sv", "Sp"}},
	{"S{}", []string{"Sv", "x", "y"}},
	{"s", []string{"Sv", "Sp", "x", "y"}},
	{"q", []string{"Z"}},

	{"I", []string{"f", "g"}},
	{"I(nil)", []string{"f", "g"}},
	{"(*I)(nil)", nil},

	// golang.org/issue/15708
	{"*T1", []string{"t1", "t2"}},
	{"T1", []string{"t2"}},

	// golang.org/issue/9060
	{"error", []string{"Error"}},
	{"struct { error }", []string{"Error"}},
	{"interface { error }", []string{"Error"}},

	// golang.org/issue/15722
	{"P", nil},

	{"X1", nil},
	{"X2", []string{"x"}},
	{"X12", nil},

	{"A", nil},

	{"B2", nil},

	{"loc", []string{"String"}},
}

func TestWalk(t *testing.T) {
	fset := token.NewFileSet()
	file, err := parser.ParseFile(fset, "src.go", src, 0)
	if file == nil {
		t.Fatal(err)
	}

	cfg := types.Config{
		Importer: importer.Default(),
		Error:    func(error) {},
	}
	pkg, err := cfg.Check("p", fset, []*ast.File{file}, nil)
	if pkg == nil {
		t.Fatal(err)
	}

	for _, test := range tests {
		tv, err := types.Eval(fset, pkg, token.NoPos, test.lhs)
		if err != nil {
			t.Errorf("Eval(%q) failed: %v", test.lhs, err)
			continue
		}

		var got []string
		visitor := func(obj types.Object) {
			// TODO(mdempsky): Should Walk be responsible
			// for filtering out inaccessible objects?
			if obj.Exported() || obj.Pkg() == pkg {
				got = append(got, obj.Name())
			}
		}

		if !lookdot.Walk(&tv, visitor) {
			t.Errorf("Walk(%q) returned false", test.lhs)
			continue
		}

		sort.Strings(got)
		sort.Strings(test.want)

		if !reflect.DeepEqual(got, test.want) {
			t.Errorf("Look(%q): got %v, want %v", test.lhs, got, test.want)
			continue
		}
	}
}
<commit_msg>fix tests for Go versions less than 1.11<commit_after>package lookdot_test

import (
	"go/ast"
	"go/build"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"reflect"
	"sort"
	"testing"

	"github.com/mdempsky/gocode/internal/lookdot"
)

const src = `
package p

import "time"

type S struct { x int; y int }
func (S) Sv()
func (*S) Sp()
var s S

type Q struct { Z }
var q Q

type I interface { f(); g() }

type P *S

type T1 struct { *T2 }
type T2 struct { *T1 }
func (*T1) t1()
func (*T2) t2()

type X int
func (*X) x()
type X1 struct { X }
type X2 struct { *X }
type X12 struct { X1; X2 }

type A1 int
func (A1) A() int
type A2 int
func (A2) A() int
type A struct { A1; A2; }

type B1 int
func (B1) b()
type B2 struct { b int; B1 }

var loc time.Location
`

var tests = []struct {
	lhs  string
	want []string
}{
	{"S", []string{"Sv"}},
	{"*S", []string{"Sv", "Sp"}},
	{"S{}", []string{"Sv", "x", "y"}},
	{"s", []string{"Sv", "Sp", "x", "y"}},

	{"I", []string{"f", "g"}},
	{"I(nil)", []string{"f", "g"}},
	{"(*I)(nil)", nil},

	// golang.org/issue/15708
	{"*T1", []string{"t1", "t2"}},
	{"T1", []string{"t2"}},

	// golang.org/issue/9060
	{"error", []string{"Error"}},
	{"struct { error }", []string{"Error"}},
	{"interface { error }", []string{"Error"}},

	// golang.org/issue/15722
	{"P", nil},

	{"X1", nil},
	{"X2", []string{"x"}},
	{"X12", nil},

	{"A", nil},

	{"B2", nil},

	{"loc", []string{"String"}},
}

func TestWalk(t *testing.T) {
	fset := token.NewFileSet()
	file, err := parser.ParseFile(fset, "src.go", src, 0)
	if file == nil {
		t.Fatal(err)
	}

	cfg := types.Config{
		Importer: importer.Default(),
		Error:    func(error) {},
	}
	pkg, err := cfg.Check("p", fset, []*ast.File{file}, nil)
	if pkg == nil {
		t.Fatal(err)
	}

	// Add a test case for Go 1.11.
	if contains(build.Default.ReleaseTags, "go1.11") {
		tests = append(tests, struct {
			lhs  string
			want []string
		}{"q", []string{"Z"}})
	}

	for _, test := range tests {
		tv, err := types.Eval(fset, pkg, token.NoPos, test.lhs)
		if err != nil {
			t.Errorf("Eval(%q) failed: %v", test.lhs, err)
			continue
		}

		var got []string
		visitor := func(obj types.Object) {
			// TODO(mdempsky): Should Walk be responsible
			// for filtering out inaccessible objects?
			if obj.Exported() || obj.Pkg() == pkg {
				got = append(got, obj.Name())
			}
		}

		if !lookdot.Walk(&tv, visitor) {
			t.Errorf("Walk(%q) returned false", test.lhs)
			continue
		}

		sort.Strings(got)
		sort.Strings(test.want)

		if !reflect.DeepEqual(got, test.want) {
			t.Errorf("Look(%q): got %v, want %v", test.lhs, got, test.want)
			continue
		}
	}
}

func contains(haystack []string, needle string) bool {
	for _, item := range haystack {
		if item == needle {
			return true
		}
	}
	return false
}
<|endoftext|>


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

// Reads all markdown files in the specified directory and validates the
// Ignition configs wrapped in code fences.

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/coreos/ignition/config"
	"github.com/coreos/ignition/config/validate/report"
)

const (
	infoString = "json ignition"
)

func main() {
	flags := struct {
		help bool
		root string
	}{}

	flag.BoolVar(&flags.help, "help", false, "Print help and exit.")
	flag.StringVar(&flags.root, "root", "doc", "Path to the documentation.")

	flag.Parse()

	if flags.help {
		flag.Usage()
		return
	}

	if err := filepath.Walk(flags.root, func(path string, info os.FileInfo, err error) error {
		if !strings.HasSuffix(info.Name(), ".md") || info.IsDir() {
			return nil
		}

		fileContents, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}

		fileLines := strings.Split(string(fileContents), "\n")
		jsonSections := findJsonSections(fileLines)

		for _, json := range jsonSections {
			_, r, _ := config.Parse([]byte(strings.Join(json, "\n")))
			reportStr := r.String()
			if reportStr != "" && !isDeprecatedConfig(r) {
				return fmt.Errorf("non-empty parsing report in %s: %s", info.Name(), reportStr)
			}
		}

		return nil
	}); err != nil {
		fmt.Fprintf(os.Stderr, "failed while validating docs: %v", err)
		os.Exit(1)
	}
}

// isDeprecatedConfig returns if a report is from a deprecated config format.
func isDeprecatedConfig(r report.Report) bool {
	if len(r.Entries) != 1 {
		return false
	}
	if r.Entries[0].Kind == report.EntryDeprecated && r.Entries[0].Message == config.ErrDeprecated.Error() {
		return true
	}
	return false
}

func findJsonSections(fileLines []string) [][]string {
	var jsonSections [][]string
	var currentSection []string
	inASection := false
	for _, line := range fileLines {
		if line == "```" {
			inASection = false
			if len(currentSection) > 0 && currentSection[0] != "..." {
				// Ignore empty sections and sections that are not full configs
				jsonSections = append(jsonSections, currentSection)
			}
			currentSection = nil
		}
		if inASection {
			currentSection = append(currentSection, line)
		}
		if line == "```"+infoString {
			inASection = true
		}
	}
	return jsonSections
}
<commit_msg>internal: docs checker fix for new package location<commit_after>// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Reads all markdown files in the specified directory and validates the
// Ignition configs wrapped in code fences.

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/coreos/ignition/config/errors"
	"github.com/coreos/ignition/config/validate/report"
	"github.com/coreos/ignition/internal/config"
)

const (
	infoString = "json ignition"
)

func main() {
	flags := struct {
		help bool
		root string
	}{}

	flag.BoolVar(&flags.help, "help", false, "Print help and exit.")
	flag.StringVar(&flags.root, "root", "doc", "Path to the documentation.")

	flag.Parse()

	if flags.help {
		flag.Usage()
		return
	}

	if err := filepath.Walk(flags.root, func(path string, info os.FileInfo, err error) error {
		if !strings.HasSuffix(info.Name(), ".md") || info.IsDir() {
			return nil
		}

		fileContents, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}

		fileLines := strings.Split(string(fileContents), "\n")
		jsonSections := findJsonSections(fileLines)

		for _, json := range jsonSections {
			_, r, _ := config.Parse([]byte(strings.Join(json, "\n")))
			reportStr := r.String()
			if reportStr != "" && !isDeprecatedConfig(r) {
				return fmt.Errorf("non-empty parsing report in %s: %s", info.Name(), reportStr)
			}
		}

		return nil
	}); err != nil {
		fmt.Fprintf(os.Stderr, "failed while validating docs: %v", err)
		os.Exit(1)
	}
}

// isDeprecatedConfig returns if a report is from a deprecated config format.
func isDeprecatedConfig(r report.Report) bool {
	if len(r.Entries) != 1 {
		return false
	}
	if r.Entries[0].Kind == report.EntryDeprecated && r.Entries[0].Message == errors.ErrDeprecated.Error() {
		return true
	}
	return false
}

func findJsonSections(fileLines []string) [][]string {
	var jsonSections [][]string
	var currentSection []string
	inASection := false
	for _, line := range fileLines {
		if line == "```" {
			inASection = false
			if len(currentSection) > 0 && currentSection[0] != "..." {
				// Ignore empty sections and sections that are not full configs
				jsonSections = append(jsonSections, currentSection)
			}
			currentSection = nil
		}
		if inASection {
			currentSection = append(currentSection, line)
		}
		if line == "```"+infoString {
			inASection = true
		}
	}
	return jsonSections
}
<|endoftext|>


<commit_before>package object

import (
	"context"
	"fmt"
	"github.com/qiniu/go-sdk/v7/auth"
	"github.com/qiniu/go-sdk/v7/storage"
	"github.com/qiniu/qshell/v2/iqshell/common/alert"
	"github.com/qiniu/qshell/v2/iqshell/common/data"
	"github.com/qiniu/qshell/v2/iqshell/storage/bucket"
	"github.com/qiniu/qshell/v2/iqshell/storage/object/batch"
)

type StatusApiInfo struct {
	Bucket   string
	Key      string
	NeedPart bool
}

func (s StatusApiInfo) WorkId() string {
	return fmt.Sprintf("ChangeStatus|%s|%s|%t", s.Bucket, s.Key, s.NeedPart)
}

type StatusResult struct {
	batch.OperationResult

	// 归档存储文件的解冻状态，uint32 类型，2表示解冻完成，1表示解冻中；归档文件冻结时，不返回该字段。
	RestoreStatus int `json:"restoreStatus"`
	// 文件状态，uint32 类型。1 表示禁用；只有禁用状态的文件才会返回该字段。
	Status int `json:"status"`
	// 文件 md5 值
	MD5 string `json:"md5"`
	// 文件过期删除日期，int64 类型，Unix 时间戳格式
	Expiration int64 `json:"expiration"`
}

func (s StatusApiInfo) ToOperation() (string, *data.CodeError) {
	if len(s.Bucket) == 0 || len(s.Key) == 0 {
		return "", alert.CannotEmptyError("status operation bucket or key", "")
	}
	return storage.URIStat(s.Bucket, s.Key), nil
}

func Status(info StatusApiInfo) (res StatusResult, err *data.CodeError) {
	bucketManager, err := bucket.GetBucketManager()
	if err != nil {
		err = data.NewEmptyError().AppendDescF("status object [%s:%s] error:%v", info.Bucket, info.Key, err.Error())
		return
	}

	reqHost, reqErr := bucketManager.RsReqHost(info.Bucket)
	if reqErr != nil {
		err = data.ConvertError(reqErr)
		return
	}

	needParts := "false"
	if info.NeedPart {
		needParts = "true"
	}
	reqURL := fmt.Sprintf("%s%s?needparts=%s", reqHost, storage.URIStat(info.Bucket, info.Key), needParts)
	cErr := bucketManager.Client.CredentialedCall(context.Background(), bucketManager.Mac, auth.TokenQiniu, &res, "POST", reqURL, nil)
	if cErr != nil {
		err = data.NewEmptyError().AppendDescF("status object [%s:%s] status error:%v", info.Bucket, info.Key, cErr.Error())
		return
	}
	return res, nil
}

// ChangeStatusApiInfo 修改 status
type ChangeStatusApiInfo struct {
	Bucket string
	Key    string
	Status int
}

func (c *ChangeStatusApiInfo) ToOperation() (string, *data.CodeError) {
	if len(c.Bucket) == 0 || len(c.Key) == 0 {
		return "", alert.CannotEmptyError("change status operation bucket or key", "")
	}
	return fmt.Sprintf("/chstatus/%s/status/%c", storage.EncodedEntry(c.Bucket, c.Key), c.Status), nil
}

func (c *ChangeStatusApiInfo) WorkId() string {
	return fmt.Sprintf("ChangeStatus|%s|%s|%d", c.Bucket, c.Key, c.Status)
}

func ChangeStatus(info *ChangeStatusApiInfo) (*batch.OperationResult, *data.CodeError) {
	return batch.One(info)
}
<commit_msg>change forbidden<commit_after>package object

import (
	"context"
	"fmt"
	"github.com/qiniu/go-sdk/v7/auth"
	"github.com/qiniu/go-sdk/v7/storage"
	"github.com/qiniu/qshell/v2/iqshell/common/alert"
	"github.com/qiniu/qshell/v2/iqshell/common/data"
	"github.com/qiniu/qshell/v2/iqshell/storage/bucket"
	"github.com/qiniu/qshell/v2/iqshell/storage/object/batch"
)

type StatusApiInfo struct {
	Bucket   string
	Key      string
	NeedPart bool
}

func (s StatusApiInfo) WorkId() string {
	return fmt.Sprintf("ChangeStatus|%s|%s|%t", s.Bucket, s.Key, s.NeedPart)
}

type StatusResult struct {
	batch.OperationResult

	// 归档存储文件的解冻状态，uint32 类型，2表示解冻完成，1表示解冻中；归档文件冻结时，不返回该字段。
	RestoreStatus int `json:"restoreStatus"`
	// 文件状态，uint32 类型。1 表示禁用；只有禁用状态的文件才会返回该字段。
	Status int `json:"status"`
	// 文件 md5 值
	MD5 string `json:"md5"`
	// 文件过期删除日期，int64 类型，Unix 时间戳格式
	Expiration int64 `json:"expiration"`
}

func (s StatusApiInfo) ToOperation() (string, *data.CodeError) {
	if len(s.Bucket) == 0 || len(s.Key) == 0 {
		return "", alert.CannotEmptyError("status operation bucket or key", "")
	}
	return storage.URIStat(s.Bucket, s.Key), nil
}

func Status(info StatusApiInfo) (res StatusResult, err *data.CodeError) {
	bucketManager, err := bucket.GetBucketManager()
	if err != nil {
		err = data.NewEmptyError().AppendDescF("status object [%s:%s] error:%v", info.Bucket, info.Key, err.Error())
		return
	}

	reqHost, reqErr := bucketManager.RsReqHost(info.Bucket)
	if reqErr != nil {
		err = data.ConvertError(reqErr)
		return
	}

	needParts := "false"
	if info.NeedPart {
		needParts = "true"
	}
	reqURL := fmt.Sprintf("%s%s?needparts=%s", reqHost, storage.URIStat(info.Bucket, info.Key), needParts)
	cErr := bucketManager.Client.CredentialedCall(context.Background(), bucketManager.Mac, auth.TokenQiniu, &res, "POST", reqURL, nil)
	if cErr != nil {
		err = data.NewEmptyError().AppendDescF("status object [%s:%s] status error:%v", info.Bucket, info.Key, cErr.Error())
		return
	}
	return res, nil
}

// ChangeStatusApiInfo 修改 status
type ChangeStatusApiInfo struct {
	Bucket string
	Key    string
	Status int
}

func (c *ChangeStatusApiInfo) ToOperation() (string, *data.CodeError) {
	if len(c.Bucket) == 0 || len(c.Key) == 0 {
		return "", alert.CannotEmptyError("change status operation bucket or key", "")
	}
	return fmt.Sprintf("/chstatus/%s/status/%d", storage.EncodedEntry(c.Bucket, c.Key), c.Status), nil
}

func (c *ChangeStatusApiInfo) WorkId() string {
	return fmt.Sprintf("ChangeStatus|%s|%s|%d", c.Bucket, c.Key, c.Status)
}

func ChangeStatus(info *ChangeStatusApiInfo) (*batch.OperationResult, *data.CodeError) {
	return batch.One(info)
}
<|endoftext|>


<commit_before>package pq

import (
	"bytes"
	"encoding/binary"

	"github.com/lib/pq/oid"
)

type readBuf []byte

func (b *readBuf) int32() (n int) {
	n = int(int32(binary.BigEndian.Uint32(*b)))
	*b = (*b)[4:]
	return
}

func (b *readBuf) oid() (n oid.Oid) {
	n = oid.Oid(binary.BigEndian.Uint32(*b))
	*b = (*b)[4:]
	return
}

func (b *readBuf) int16() (n int) {
	n = int(binary.BigEndian.Uint16(*b))
	*b = (*b)[2:]
	return
}

func (b *readBuf) string() string {
	i := bytes.IndexByte(*b, 0)
	if i < 0 {
		errorf("invalid message format; expected string terminator")
	}
	s := (*b)[:i]
	*b = (*b)[i+1:]
	return string(s)
}

func (b *readBuf) next(n int) (v []byte) {
	v = (*b)[:n]
	*b = (*b)[n:]
	return
}

func (b *readBuf) byte() byte {
	return b.next(1)[0]
}

type writeBuf struct {
	buf []byte
	pos int
}

func (b *writeBuf) int32(n int) {
	x := make([]byte, 4)
	binary.BigEndian.PutUint32(x, uint32(n))
	b.buf = append(b.buf, x...)
}

func (b *writeBuf) int16(n int) {
	x := make([]byte, 2)
	binary.BigEndian.PutUint16(x, uint16(n))
	b.buf = append(b.buf, x...)
}

func (b *writeBuf) string(s string) {
	b.buf = append(b.buf, (s + "\000")...)
}

func (b *writeBuf) byte(c byte) {
	b.buf = append(b.buf, c)
}

func (b *writeBuf) bytes(v []byte) {
	b.buf = append(b.buf, v...)
}

func (b *writeBuf) wrap() []byte {
	p := b.buf[b.pos:]
	binary.BigEndian.PutUint32(p, uint32(len(p)))
	return b.buf
}

func (b *writeBuf) next(c byte) {
	p := b.buf[b.pos:]
	binary.BigEndian.PutUint32(p, uint32(len(p)))
	b.pos = len(b.buf) + 1
	b.buf = append(b.buf, c, 0, 0, 0, 0)
}
<commit_msg>Document that readBuf.int16() is unsigned<commit_after>package pq

import (
	"bytes"
	"encoding/binary"

	"github.com/lib/pq/oid"
)

type readBuf []byte

func (b *readBuf) int32() (n int) {
	n = int(int32(binary.BigEndian.Uint32(*b)))
	*b = (*b)[4:]
	return
}

func (b *readBuf) oid() (n oid.Oid) {
	n = oid.Oid(binary.BigEndian.Uint32(*b))
	*b = (*b)[4:]
	return
}

// N.B: this is actually an unsigned 16-bit integer, unlike int32
func (b *readBuf) int16() (n int) {
	n = int(binary.BigEndian.Uint16(*b))
	*b = (*b)[2:]
	return
}

func (b *readBuf) string() string {
	i := bytes.IndexByte(*b, 0)
	if i < 0 {
		errorf("invalid message format; expected string terminator")
	}
	s := (*b)[:i]
	*b = (*b)[i+1:]
	return string(s)
}

func (b *readBuf) next(n int) (v []byte) {
	v = (*b)[:n]
	*b = (*b)[n:]
	return
}

func (b *readBuf) byte() byte {
	return b.next(1)[0]
}

type writeBuf struct {
	buf []byte
	pos int
}

func (b *writeBuf) int32(n int) {
	x := make([]byte, 4)
	binary.BigEndian.PutUint32(x, uint32(n))
	b.buf = append(b.buf, x...)
}

func (b *writeBuf) int16(n int) {
	x := make([]byte, 2)
	binary.BigEndian.PutUint16(x, uint16(n))
	b.buf = append(b.buf, x...)
}

func (b *writeBuf) string(s string) {
	b.buf = append(b.buf, (s + "\000")...)
}

func (b *writeBuf) byte(c byte) {
	b.buf = append(b.buf, c)
}

func (b *writeBuf) bytes(v []byte) {
	b.buf = append(b.buf, v...)
}

func (b *writeBuf) wrap() []byte {
	p := b.buf[b.pos:]
	binary.BigEndian.PutUint32(p, uint32(len(p)))
	return b.buf
}

func (b *writeBuf) next(c byte) {
	p := b.buf[b.pos:]
	binary.BigEndian.PutUint32(p, uint32(len(p)))
	b.pos = len(b.buf) + 1
	b.buf = append(b.buf, c, 0, 0, 0, 0)
}
<|endoftext|>


<commit_before>package engine

import (
	"crypto/sha256"
	"fmt"
	"strings"
	"time"

	"github.com/keybase/go/libkb"
	"golang.org/x/crypto/scrypt"
)

type KexStrongID [32]byte
type KexWeakID [16]byte

type KexContext struct {
	UserID   libkb.UID
	WeakID   KexWeakID
	StrongID KexStrongID
	Src      libkb.DeviceID
	Dst      libkb.DeviceID
}

func (c *KexContext) Swap() {
	c.Src, c.Dst = c.Dst, c.Src
}

type KexServer interface {
	StartKexSession(ctx *KexContext, id KexStrongID) error
	StartReverseKexSession(ctx *KexContext) error
	Hello(ctx *KexContext, devID libkb.DeviceID, devKey libkb.NaclSigningKeyPublic) error
	PleaseSign(ctx *KexContext, eddsa libkb.NaclSigningKeyPublic, sig, devType, devDesc string) error
	Done(ctx *KexContext, mt libkb.MerkleTriple) error

	// XXX get rid of this when real client comm works
	RegisterTestDevice(srv KexServer, device libkb.DeviceID) error
}

type Kex struct {
	server        KexServer
	user          *libkb.User
	deviceID      libkb.DeviceID
	deviceSibkey  libkb.GenericKey
	sigKey        libkb.GenericKey
	sessionID     KexStrongID
	helloReceived chan bool
	doneReceived  chan bool
	debugName     string
	xDevKey       libkb.NaclSigningKeyPublic
	secretUI      libkb.SecretUI
}

var kexTimeout = 5 * time.Minute

func NewKex(s KexServer, sui libkb.SecretUI, options ...func(*Kex)) *Kex {
	k := &Kex{server: s, secretUI: sui, helloReceived: make(chan bool, 1), doneReceived: make(chan bool, 1)}
	for _, opt := range options {
		opt(k)
	}
	return k
}

func SetDebugName(name string) func(k *Kex) {
	return func(k *Kex) {
		k.debugName = name
	}
}

func (k *Kex) StartForward(u *libkb.User, src, dst libkb.DeviceID, devType, devDesc string) error {
	k.user = u
	k.deviceID = src

	// XXX this is just for testing
	k.server.RegisterTestDevice(k, src)

	// make random secret S
	words, id, err := k.secret()
	if err != nil {
		return err
	}
	G.Log.Info("kex [%s]: words = %v, id = %x", k.debugName, words, id)

	k.sessionID = id

	ctx := &KexContext{
		UserID:   k.user.GetUid(),
		StrongID: id,
		Src:      src,
		Dst:      dst,
	}
	copy(ctx.WeakID[:], id[0:16])

	if err := k.server.StartKexSession(ctx, id); err != nil {
		return err
	}

	// tell user the command to enter on existing device (X)

	// wait for Hello() from X
	if err := k.waitHello(); err != nil {
		return err
	}

	// E_y
	eddsa, err := libkb.GenerateNaclSigningKeyPair()
	if err != nil {
		return err
	}
	eddsaPair, ok := eddsa.(libkb.NaclSigningKeyPair)
	if !ok {
		return fmt.Errorf("invalid key type %T", eddsa)
	}

	// M_y
	dh, err := libkb.GenerateNaclDHKeyPair()
	if err != nil {
		return err
	}
	dh = dh

	// XXX store these in lks

	sig, _, err := eddsa.SignToString(k.xDevKey[:])
	if err != nil {
		return err
	}

	ctx.Src = src
	ctx.Dst = dst
	if err := k.server.PleaseSign(ctx, eddsaPair.Public, sig, devType, devDesc); err != nil {
		return err
	}

	// wait for Done() from X
	if err := k.waitDone(); err != nil {
		return err
	}

	// Device y signs M_y into Alice's sigchain as a subkey.
	// use eddsa to sign dh
	// then push it
	devY := libkb.Device{
		Id:          k.deviceID.String(),
		Type:        devType,
		Description: &devDesc,
	}
	// generator function that just copies the public eddsa key into a
	// NaclKeyPair (which implements GenericKey).
	g := func() (libkb.NaclKeyPair, error) {
		//		var ret libkb.NaclDHKeyPair
		//	ret.Public = dh.Public
		//	return ret, nil
		return dh, nil
	}
	arg := libkb.NaclKeyGenArg{
		Signer:      eddsa,
		ExpireIn:    libkb.NACL_DH_EXPIRE_IN,
		Sibkey:      false,
		Me:          k.user,
		EldestKeyID: k.user.GetEldestFOKID().Kid,
		Generator:   g,
		Device:      &devY,
	}
	gen := libkb.NewNaclKeyGen(arg)
	if err := gen.Generate(); err != nil {
		return fmt.Errorf("gen.Generate() error: %s", err)
	}
	if _, err := gen.Push(); err != nil {
		return fmt.Errorf("gen.Push() error: %s", err)
	}

	// store the new device id
	if wr := G.Env.GetConfigWriter(); wr != nil {
		if err := wr.SetDeviceID(&k.deviceID); err != nil {
			return err
		} else if err := wr.Write(); err != nil {
			return err
		} else {
			G.Log.Info("Setting Device ID to %s", k.deviceID)
		}
	}

	// Device y makes a local note of Alice's UID (which is sent in the channel metadata).

	return nil
}

// XXX temporary...
// this is to get around the fact that the globals won't work well
// in the test with two devices communicating in the same process.
func (k *Kex) Listen(u *libkb.User, src libkb.DeviceID) {
	k.user = u
	k.deviceID = src
	var err error
	k.deviceSibkey, err = k.user.GetComputedKeyFamily().GetSibkeyForDevice(src)
	if err != nil {
		G.Log.Warning("kex.Listen: error getting device sibkey: %s", err)
	}
	k.sigKey, err = G.Keyrings.GetSecretKey("new device install", k.secretUI, k.user)
	if err != nil {
		G.Log.Warning("GetSecretKey error: %s", err)
	}
}

func (k *Kex) waitHello() error {
	G.Log.Info("[%s] waitHello start", k.debugName)
	defer G.Log.Info("[%s] waitHello done", k.debugName)
	select {
	case <-k.helloReceived:
		G.Log.Info("[%s] hello received", k.debugName)
		return nil
	case <-time.After(kexTimeout):
		return fmt.Errorf("timeout waiting for Hello")
	}
}

func (k *Kex) waitDone() error {
	G.Log.Info("[%s] waitDone start", k.debugName)
	defer G.Log.Info("[%s] waitDone done", k.debugName)
	select {
	case <-k.doneReceived:
		G.Log.Info("[%s] done received", k.debugName)
		return nil
	case <-time.After(kexTimeout):
		return fmt.Errorf("timeout waiting for Done")
	}
}

func (k *Kex) secret() (words []string, id [32]byte, err error) {
	words, err = libkb.SecWordList(5)
	if err != nil {
		return
	}
	id, err = k.wordsToID(words)
	if err != nil {
		return
	}

	return words, id, err
}

func (k *Kex) wordsToID(words []string) ([32]byte, error) {
	key, err := scrypt.Key([]byte(strings.Join(words, " ")), []byte(k.user.GetName()), 32768, 8, 1, 32)
	if err != nil {
		return [32]byte{}, err
	}
	return sha256.Sum256(key), nil
}

func (k *Kex) StartKexSession(ctx *KexContext, id KexStrongID) error {
	G.Log.Info("[%s] StartKexSession: %x", k.debugName, id)
	defer G.Log.Info("[%s] StartKexSession done", k.debugName)

	if err := k.verifyDst(ctx); err != nil {
		return err
	}

	ctx.Swap()
	pair, ok := k.deviceSibkey.(libkb.NaclSigningKeyPair)
	if !ok {
		return fmt.Errorf("invalid device sibkey type %T", k.deviceSibkey)
	}
	return k.server.Hello(ctx, ctx.Src, pair.Public)
}

func (k *Kex) StartReverseKexSession(ctx *KexContext) error { return nil }

func (k *Kex) Hello(ctx *KexContext, devID libkb.DeviceID, devKey libkb.NaclSigningKeyPublic) error {
	G.Log.Info("[%s] Hello Receive", k.debugName)
	defer G.Log.Info("[%s] Hello Receive done", k.debugName)
	if err := k.verifyDst(ctx); err != nil {
		return err
	}

	k.xDevKey = devKey

	k.helloReceived <- true
	return nil
}

func (k *Kex) PleaseSign(ctx *KexContext, eddsa libkb.NaclSigningKeyPublic, sig, devType, devDesc string) error {
	G.Log.Info("[%s] PleaseSign Receive", k.debugName)
	defer G.Log.Info("[%s] PleaseSign Receive done", k.debugName)
	if err := k.verifyDst(ctx); err != nil {
		return err
	}

	// XXX check sig matches

	// make device object for Y
	devY := libkb.Device{
		Id:          ctx.Src.String(),
		Type:        devType,
		Description: &devDesc,
	}

	// generator function that just copies the public eddsa key into a
	// NaclKeyPair (which implements GenericKey).
	g := func() (libkb.NaclKeyPair, error) {
		var ret libkb.NaclSigningKeyPair
		copy(ret.Public[:], eddsa[:])
		return ret, nil
	}

	// need the private device sibkey
	// k.deviceSibkey is public only
	// going to use keyring.go:GetSecretKey()
	// however, it could return any key.
	// there is a ticket to add preferences to it so we could only
	// get a device key.
	// but it should currently return a device key first...
	if k.sigKey == nil {
		var err error
		k.sigKey, err = G.Keyrings.GetSecretKey("new device install", k.secretUI, k.user)
		if err != nil {
			return err
		}
	}

	// use naclkeygen to sign eddsa with device X (this device) sibkey
	// and push it to the server
	arg := libkb.NaclKeyGenArg{
		Signer:      k.sigKey,
		ExpireIn:    libkb.NACL_EDDSA_EXPIRE_IN,
		Sibkey:      true,
		Me:          k.user,
		Device:      &devY,
		EldestKeyID: k.user.GetEldestFOKID().Kid,
		Generator:   g,
	}
	gen := libkb.NewNaclKeyGen(arg)
	if err := gen.Generate(); err != nil {
		return fmt.Errorf("gen.Generate() error: %s", err)
	}
	mt, err := gen.Push()
	if err != nil {
		return fmt.Errorf("gen.Push() error: %s", err)
	}

	ctx.Swap()
	return k.server.Done(ctx, mt)
}

func (k *Kex) Done(ctx *KexContext, mt libkb.MerkleTriple) error {
	G.Log.Info("[%s] Done Receive", k.debugName)
	defer G.Log.Info("[%s] Done Receive done", k.debugName)
	if err := k.verifyDst(ctx); err != nil {
		return err
	}

	// device X changed the sigchain, so bump it here
	k.user.SigChainBumpMT(mt)

	k.doneReceived <- true
	return nil
}

func (k *Kex) RegisterTestDevice(srv KexServer, device libkb.DeviceID) error { return nil }

func (k *Kex) verifyDst(ctx *KexContext) error {
	if ctx.Dst != k.deviceID {
		return fmt.Errorf("destination device id (%s) invalid.  this is device (%s).", ctx.Dst, k.deviceID)
	}
	return nil
}
<commit_msg>cleanup<commit_after>package engine

import (
	"crypto/sha256"
	"fmt"
	"strings"
	"time"

	"github.com/keybase/go/libkb"
	"golang.org/x/crypto/scrypt"
)

type KexStrongID [32]byte
type KexWeakID [16]byte

type KexContext struct {
	UserID   libkb.UID
	WeakID   KexWeakID
	StrongID KexStrongID
	Src      libkb.DeviceID
	Dst      libkb.DeviceID
}

func (c *KexContext) Swap() {
	c.Src, c.Dst = c.Dst, c.Src
}

type KexServer interface {
	StartKexSession(ctx *KexContext, id KexStrongID) error
	StartReverseKexSession(ctx *KexContext) error
	Hello(ctx *KexContext, devID libkb.DeviceID, devKey libkb.NaclSigningKeyPublic) error
	PleaseSign(ctx *KexContext, eddsa libkb.NaclSigningKeyPublic, sig, devType, devDesc string) error
	Done(ctx *KexContext, mt libkb.MerkleTriple) error

	// XXX get rid of this when real client comm works
	RegisterTestDevice(srv KexServer, device libkb.DeviceID) error
}

type Kex struct {
	server        KexServer
	user          *libkb.User
	deviceID      libkb.DeviceID
	deviceSibkey  libkb.GenericKey
	sigKey        libkb.GenericKey
	sessionID     KexStrongID
	helloReceived chan bool
	doneReceived  chan bool
	debugName     string
	xDevKey       libkb.NaclSigningKeyPublic
	secretUI      libkb.SecretUI
}

var kexTimeout = 5 * time.Minute

func NewKex(s KexServer, sui libkb.SecretUI, options ...func(*Kex)) *Kex {
	k := &Kex{server: s, secretUI: sui, helloReceived: make(chan bool, 1), doneReceived: make(chan bool, 1)}
	for _, opt := range options {
		opt(k)
	}
	return k
}

func SetDebugName(name string) func(k *Kex) {
	return func(k *Kex) {
		k.debugName = name
	}
}

func (k *Kex) StartForward(u *libkb.User, src, dst libkb.DeviceID, devType, devDesc string) error {
	k.user = u
	k.deviceID = src

	// XXX this is just for testing
	k.server.RegisterTestDevice(k, src)

	// make random secret S
	words, id, err := k.secret()
	if err != nil {
		return err
	}
	G.Log.Info("kex [%s]: words = %v, id = %x", k.debugName, words, id)

	k.sessionID = id

	ctx := &KexContext{
		UserID:   k.user.GetUid(),
		StrongID: id,
		Src:      src,
		Dst:      dst,
	}
	copy(ctx.WeakID[:], id[0:16])

	if err := k.server.StartKexSession(ctx, id); err != nil {
		return err
	}

	// XXX tell user the command to enter on existing device (X)

	// wait for Hello() from X
	if err := k.waitHello(); err != nil {
		return err
	}

	// E_y
	eddsa, err := libkb.GenerateNaclSigningKeyPair()
	if err != nil {
		return err
	}
	eddsaPair, ok := eddsa.(libkb.NaclSigningKeyPair)
	if !ok {
		return fmt.Errorf("invalid key type %T", eddsa)
	}

	// M_y
	dh, err := libkb.GenerateNaclDHKeyPair()
	if err != nil {
		return err
	}

	// XXX store these in lks

	sig, _, err := eddsa.SignToString(k.xDevKey[:])
	if err != nil {
		return err
	}

	ctx.Src = src
	ctx.Dst = dst
	if err := k.server.PleaseSign(ctx, eddsaPair.Public, sig, devType, devDesc); err != nil {
		return err
	}

	// wait for Done() from X
	if err := k.waitDone(); err != nil {
		return err
	}

	// Device y signs M_y into Alice's sigchain as a subkey.
	// use eddsa to sign dh
	// then push it
	devY := libkb.Device{
		Id:          k.deviceID.String(),
		Type:        devType,
		Description: &devDesc,
	}
	g := func() (libkb.NaclKeyPair, error) {
		return dh, nil
	}
	arg := libkb.NaclKeyGenArg{
		Signer:      eddsa,
		ExpireIn:    libkb.NACL_DH_EXPIRE_IN,
		Sibkey:      false,
		Me:          k.user,
		EldestKeyID: k.user.GetEldestFOKID().Kid,
		Generator:   g,
		Device:      &devY,
	}
	gen := libkb.NewNaclKeyGen(arg)
	if err := gen.Generate(); err != nil {
		return fmt.Errorf("gen.Generate() error: %s", err)
	}
	if _, err := gen.Push(); err != nil {
		return fmt.Errorf("gen.Push() error: %s", err)
	}

	// store the new device id
	if wr := G.Env.GetConfigWriter(); wr != nil {
		if err := wr.SetDeviceID(&k.deviceID); err != nil {
			return err
		} else if err := wr.Write(); err != nil {
			return err
		} else {
			G.Log.Info("Setting Device ID to %s", k.deviceID)
		}
	}

	// Device y makes a local note of Alice's UID (which is sent in the channel metadata).

	return nil
}

// XXX temporary...
// this is to get around the fact that the globals won't work well
// in the test with two devices communicating in the same process.
func (k *Kex) Listen(u *libkb.User, src libkb.DeviceID) {
	k.user = u
	k.deviceID = src
	var err error
	k.deviceSibkey, err = k.user.GetComputedKeyFamily().GetSibkeyForDevice(src)
	if err != nil {
		G.Log.Warning("kex.Listen: error getting device sibkey: %s", err)
	}
	k.sigKey, err = G.Keyrings.GetSecretKey("new device install", k.secretUI, k.user)
	if err != nil {
		G.Log.Warning("GetSecretKey error: %s", err)
	}
}

func (k *Kex) waitHello() error {
	G.Log.Info("[%s] waitHello start", k.debugName)
	defer G.Log.Info("[%s] waitHello done", k.debugName)
	select {
	case <-k.helloReceived:
		G.Log.Info("[%s] hello received", k.debugName)
		return nil
	case <-time.After(kexTimeout):
		return fmt.Errorf("timeout waiting for Hello")
	}
}

func (k *Kex) waitDone() error {
	G.Log.Info("[%s] waitDone start", k.debugName)
	defer G.Log.Info("[%s] waitDone done", k.debugName)
	select {
	case <-k.doneReceived:
		G.Log.Info("[%s] done received", k.debugName)
		return nil
	case <-time.After(kexTimeout):
		return fmt.Errorf("timeout waiting for Done")
	}
}

func (k *Kex) secret() (words []string, id [32]byte, err error) {
	words, err = libkb.SecWordList(5)
	if err != nil {
		return
	}
	id, err = k.wordsToID(words)
	if err != nil {
		return
	}

	return words, id, err
}

func (k *Kex) wordsToID(words []string) ([32]byte, error) {
	key, err := scrypt.Key([]byte(strings.Join(words, " ")), []byte(k.user.GetName()), 32768, 8, 1, 32)
	if err != nil {
		return [32]byte{}, err
	}
	return sha256.Sum256(key), nil
}

func (k *Kex) StartKexSession(ctx *KexContext, id KexStrongID) error {
	G.Log.Info("[%s] StartKexSession: %x", k.debugName, id)
	defer G.Log.Info("[%s] StartKexSession done", k.debugName)

	if err := k.verifyDst(ctx); err != nil {
		return err
	}

	ctx.Swap()
	pair, ok := k.deviceSibkey.(libkb.NaclSigningKeyPair)
	if !ok {
		return fmt.Errorf("invalid device sibkey type %T", k.deviceSibkey)
	}
	return k.server.Hello(ctx, ctx.Src, pair.Public)
}

func (k *Kex) StartReverseKexSession(ctx *KexContext) error { return nil }

func (k *Kex) Hello(ctx *KexContext, devID libkb.DeviceID, devKey libkb.NaclSigningKeyPublic) error {
	G.Log.Info("[%s] Hello Receive", k.debugName)
	defer G.Log.Info("[%s] Hello Receive done", k.debugName)
	if err := k.verifyDst(ctx); err != nil {
		return err
	}

	k.xDevKey = devKey

	k.helloReceived <- true
	return nil
}

func (k *Kex) PleaseSign(ctx *KexContext, eddsa libkb.NaclSigningKeyPublic, sig, devType, devDesc string) error {
	G.Log.Info("[%s] PleaseSign Receive", k.debugName)
	defer G.Log.Info("[%s] PleaseSign Receive done", k.debugName)
	if err := k.verifyDst(ctx); err != nil {
		return err
	}

	// XXX check sig

	// make device object for Y
	devY := libkb.Device{
		Id:          ctx.Src.String(),
		Type:        devType,
		Description: &devDesc,
	}

	// generator function that just copies the public eddsa key into a
	// NaclKeyPair (which implements GenericKey).
	g := func() (libkb.NaclKeyPair, error) {
		var ret libkb.NaclSigningKeyPair
		copy(ret.Public[:], eddsa[:])
		return ret, nil
	}

	// need the private device sibkey
	// k.deviceSibkey is public only
	// going to use keyring.go:GetSecretKey()
	// however, it could return any key.
	// there is a ticket to add preferences to it so we could only
	// get a device key.
	// but it should currently return a device key first...
	if k.sigKey == nil {
		var err error
		k.sigKey, err = G.Keyrings.GetSecretKey("new device install", k.secretUI, k.user)
		if err != nil {
			return err
		}
	}

	// use naclkeygen to sign eddsa with device X (this device) sibkey
	// and push it to the server
	arg := libkb.NaclKeyGenArg{
		Signer:      k.sigKey,
		ExpireIn:    libkb.NACL_EDDSA_EXPIRE_IN,
		Sibkey:      true,
		Me:          k.user,
		Device:      &devY,
		EldestKeyID: k.user.GetEldestFOKID().Kid,
		Generator:   g,
	}
	gen := libkb.NewNaclKeyGen(arg)
	if err := gen.Generate(); err != nil {
		return fmt.Errorf("gen.Generate() error: %s", err)
	}
	mt, err := gen.Push()
	if err != nil {
		return fmt.Errorf("gen.Push() error: %s", err)
	}

	ctx.Swap()
	return k.server.Done(ctx, mt)
}

func (k *Kex) Done(ctx *KexContext, mt libkb.MerkleTriple) error {
	G.Log.Info("[%s] Done Receive", k.debugName)
	defer G.Log.Info("[%s] Done Receive done", k.debugName)
	if err := k.verifyDst(ctx); err != nil {
		return err
	}

	// device X changed the sigchain, so bump it here
	k.user.SigChainBumpMT(mt)

	k.doneReceived <- true
	return nil
}

func (k *Kex) RegisterTestDevice(srv KexServer, device libkb.DeviceID) error { return nil }

func (k *Kex) verifyDst(ctx *KexContext) error {
	if ctx.Dst != k.deviceID {
		return fmt.Errorf("destination device id (%s) invalid.  this is device (%s).", ctx.Dst, k.deviceID)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/mitchellh/cli"
)

const timeFmt = "Monday, 2006-01-02 15:04:05 MST -0700"

var flagUTC bool

func main() {
	err := loadDB()
	if err != nil {
		fmt.Println("failed to load DB", err)
		os.Exit(-1)
	}

	if len(os.Args) == 1 {
		now := time.Now()
		fmt.Println(now.Format(timeFmt))
		for name, offset := range locs {
			loc := time.FixedZone(name, offset)
			fmt.Println(now.In(loc).Format(timeFmt))
		}
		os.Exit(0)
	}

	c := cli.NewCLI("wdate", "1.0")
	c.Args = os.Args[1:]
	c.Commands = map[string]cli.CommandFactory{
		"add":    addCommandFactory,
		"remove": removeCommandFactory,
	}

	exitStatus, err := c.Run()
	if err != nil {
		log.Println(err)
	}

	os.Exit(exitStatus)
}
<commit_msg>fix timeFmt for alignment<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/mitchellh/cli"
)

const timeFmt = "Mon, 2006-01-02 15:04:05 MST -0700"

var flagUTC bool

func main() {
	err := loadDB()
	if err != nil {
		fmt.Println("failed to load DB", err)
		os.Exit(-1)
	}

	if len(os.Args) == 1 {
		now := time.Now()
		fmt.Println(now.Format(timeFmt))
		for name, offset := range locs {
			loc := time.FixedZone(name, offset)
			fmt.Println(now.In(loc).Format(timeFmt))
		}
		os.Exit(0)
	}

	c := cli.NewCLI("wdate", "1.0")
	c.Args = os.Args[1:]
	c.Commands = map[string]cli.CommandFactory{
		"add":    addCommandFactory,
		"remove": removeCommandFactory,
	}

	exitStatus, err := c.Run()
	if err != nil {
		log.Println(err)
	}

	os.Exit(exitStatus)
}
<|endoftext|>


<commit_before>package main

import "os"

func main() {
	app := App{
		Name:        "math",
		Description: "a simple command line math utility",
		Commands: []Command{
			{
				Name:        "add",
				Description: "Add 2 and 2",
				Action:      DoAdd},
			{
				Name:        "subtract",
				Description: "Subtract 2 and 2",
				Action:      DoSubtract},
			{
				Name:        "multiply",
				Description: "Multiply 2 and 2",
				Action:      DoMultiply},
			{
				Name:        "divide",
				Description: "Divide 2 and 2",
				Action:      DoDivide}}}

	app.Run(os.Args[1])
}

func DoAdd(name string) {
  println("2+2=", 2+2)
}

func DoSubtract(name string) {
  println("2-2=", 2-2)
}

func DoMultiply(name string) {
  println("2*2=", 2*2)
}

func DoDivide(name string) {
  println("2/2=", 2/2)
}

type App struct {
	Name        string
	Description string
	Commands    []Command
}

type Command struct {
	Name        string
	Description string
	Action      Action
}

type Action func(name string)

func (a App) Run(command string) {
  for _, c := range a.Commands {
    if(c.Name == command) {
      c.Action(command)
    }
  }
}
<commit_msg>Formatting<commit_after>package main

import "os"

func main() {
	app := App{
		Name:        "math",
		Description: "a simple command line math utility",
		Commands: []Command{
			{
				Name:        "add",
				Description: "Add 2 and 2",
				Action:      DoAdd},
			{
				Name:        "subtract",
				Description: "Subtract 2 and 2",
				Action:      DoSubtract},
			{
				Name:        "multiply",
				Description: "Multiply 2 and 2",
				Action:      DoMultiply},
			{
				Name:        "divide",
				Description: "Divide 2 and 2",
				Action:      DoDivide}}}

	app.Run(os.Args[1])
}

func DoAdd(name string) {
	println("2+2=", 2+2)
}

func DoSubtract(name string) {
	println("2-2=", 2-2)
}

func DoMultiply(name string) {
	println("2*2=", 2*2)
}

func DoDivide(name string) {
	println("2/2=", 2/2)
}

type App struct {
	Name        string
	Description string
	Commands    []Command
}

type Command struct {
	Name        string
	Description string
	Action      Action
}

type Action func(name string)

func (a App) Run(command string) {
	for _, c := range a.Commands {
		if c.Name == command {
			c.Action(command)
		}
	}
}
<|endoftext|>