<commit_before>// Package expvar implements an expvar backend for package metrics.
//
// The current implementation ignores fields. In the future, it would be good
// to have an implementation that accepted a set of predeclared field names at
// construction time, and used field values to produce delimiter-separated
// bucket (key) names. That is,
//
//    c := NewFieldedCounter(..., "path", "status")
//    c.Add(1) // "myprefix_unknown_unknown" += 1
//    c2 := c.With("path", "foo").With("status": "200")
//    c2.Add(1) // "myprefix_foo_status" += 1
//
// It would also be possible to have an implementation that generated more
// sophisticated expvar.Values. For example, a Counter could be implemented as
// a map, representing a tree of key/value pairs whose leaves were the actual
// expvar.Ints.
package expvar

import (
	"expvar"
	"fmt"
	"strconv"
	"sync"
	"time"

	"github.com/peterbourgon/gokit/metrics"

	"github.com/codahale/hdrhistogram"
)

type counter struct {
	v *expvar.Int
}

// NewCounter returns a new Counter backed by an expvar with the given name.
// Fields are ignored.
func NewCounter(name string) metrics.Counter {
	return &counter{expvar.NewInt(name)}
}

func (c *counter) With(metrics.Field) metrics.Counter { return c }
func (c *counter) Add(delta uint64)                   { c.v.Add(int64(delta)) }

type gauge struct {
	v *expvar.Float
}

// NewGauge returns a new Gauge backed by an expvar with the given name. It
// should be updated manually; for a callback-based approach, see
// NewCallbackGauge. Fields are ignored.
func NewGauge(name string) metrics.Gauge {
	return &gauge{expvar.NewFloat(name)}
}

func (g *gauge) With(metrics.Field) metrics.Gauge { return g }

func (g *gauge) Add(delta float64) { g.v.Add(delta) }

func (g *gauge) Set(value float64) { g.v.Set(value) }

// PublishCallbackGauge publishes a Gauge as an expvar with the given name,
// whose value is determined at collect time by the passed callback function.
// The callback determines the value, and fields are ignored, so
// PublishCallbackGauge returns nothing.
func PublishCallbackGauge(name string, callback func() float64) {
	expvar.Publish(name, callbackGauge(callback))
}

type callbackGauge func() float64

func (g callbackGauge) String() string { return strconv.FormatFloat(g(), 'g', -1, 64) }

type histogram struct {
	mu   sync.Mutex
	hist *hdrhistogram.WindowedHistogram

	name   string
	gauges map[int]metrics.Gauge
}

// NewHistogram is taken from http://github.com/codahale/metrics. It returns a
// windowed HDR histogram which drops data older than five minutes.
//
// The histogram exposes metrics for each passed quantile as gauges. Quantiles
// should be integers in the range 1..99. The gauge names are assigned by
// using the passed name as a prefix and appending "_pNN" e.g. "_p50".
func NewHistogram(name string, minValue, maxValue int64, sigfigs int, quantiles ...int) metrics.Histogram {
	gauges := map[int]metrics.Gauge{}
	for _, quantile := range quantiles {
		if quantile <= 0 || quantile >= 100 {
			panic(fmt.Sprintf("invalid quantile %d", quantile))
		}
		gauges[quantile] = NewGauge(fmt.Sprintf("%s_p%02d", name, quantile))
	}
	h := &histogram{
		hist:   hdrhistogram.NewWindowed(5, minValue, maxValue, sigfigs),
		name:   name,
		gauges: gauges,
	}
	go h.rotateLoop(1 * time.Minute)
	return h
}

func (h *histogram) With(metrics.Field) metrics.Histogram { return h }

func (h *histogram) Observe(value int64) {
	h.mu.Lock()
	err := h.hist.Current.RecordValue(value)
	h.mu.Unlock()

	if err != nil {
		panic(err.Error())
	}

	for q, gauge := range h.gauges {
		gauge.Set(float64(h.hist.Current.ValueAtQuantile(float64(q))))
	}
}

func (h *histogram) rotateLoop(d time.Duration) {
	for _ = range time.Tick(d) {
		h.mu.Lock()
		h.hist.Rotate()
		h.mu.Unlock()
	}
}
<commit_msg>metrics: expvar: fix typo in doc comment<commit_after>// Package expvar implements an expvar backend for package metrics.
//
// The current implementation ignores fields. In the future, it would be good
// to have an implementation that accepted a set of predeclared field names at
// construction time, and used field values to produce delimiter-separated
// bucket (key) names. That is,
//
//    c := NewFieldedCounter(..., "path", "status")
//    c.Add(1) // "myprefix.unknown.unknown" += 1
//    c2 := c.With("path", "foo").With("status": "200")
//    c2.Add(1) // "myprefix.foo.200" += 1
//
// It would also be possible to have an implementation that generated more
// sophisticated expvar.Values. For example, a Counter could be implemented as
// a map, representing a tree of key/value pairs whose leaves were the actual
// expvar.Ints.
package expvar

import (
	"expvar"
	"fmt"
	"strconv"
	"sync"
	"time"

	"github.com/peterbourgon/gokit/metrics"

	"github.com/codahale/hdrhistogram"
)

type counter struct {
	v *expvar.Int
}

// NewCounter returns a new Counter backed by an expvar with the given name.
// Fields are ignored.
func NewCounter(name string) metrics.Counter {
	return &counter{expvar.NewInt(name)}
}

func (c *counter) With(metrics.Field) metrics.Counter { return c }
func (c *counter) Add(delta uint64)                   { c.v.Add(int64(delta)) }

type gauge struct {
	v *expvar.Float
}

// NewGauge returns a new Gauge backed by an expvar with the given name. It
// should be updated manually; for a callback-based approach, see
// NewCallbackGauge. Fields are ignored.
func NewGauge(name string) metrics.Gauge {
	return &gauge{expvar.NewFloat(name)}
}

func (g *gauge) With(metrics.Field) metrics.Gauge { return g }

func (g *gauge) Add(delta float64) { g.v.Add(delta) }

func (g *gauge) Set(value float64) { g.v.Set(value) }

// PublishCallbackGauge publishes a Gauge as an expvar with the given name,
// whose value is determined at collect time by the passed callback function.
// The callback determines the value, and fields are ignored, so
// PublishCallbackGauge returns nothing.
func PublishCallbackGauge(name string, callback func() float64) {
	expvar.Publish(name, callbackGauge(callback))
}

type callbackGauge func() float64

func (g callbackGauge) String() string { return strconv.FormatFloat(g(), 'g', -1, 64) }

type histogram struct {
	mu   sync.Mutex
	hist *hdrhistogram.WindowedHistogram

	name   string
	gauges map[int]metrics.Gauge
}

// NewHistogram is taken from http://github.com/codahale/metrics. It returns a
// windowed HDR histogram which drops data older than five minutes.
//
// The histogram exposes metrics for each passed quantile as gauges. Quantiles
// should be integers in the range 1..99. The gauge names are assigned by
// using the passed name as a prefix and appending "_pNN" e.g. "_p50".
func NewHistogram(name string, minValue, maxValue int64, sigfigs int, quantiles ...int) metrics.Histogram {
	gauges := map[int]metrics.Gauge{}
	for _, quantile := range quantiles {
		if quantile <= 0 || quantile >= 100 {
			panic(fmt.Sprintf("invalid quantile %d", quantile))
		}
		gauges[quantile] = NewGauge(fmt.Sprintf("%s_p%02d", name, quantile))
	}
	h := &histogram{
		hist:   hdrhistogram.NewWindowed(5, minValue, maxValue, sigfigs),
		name:   name,
		gauges: gauges,
	}
	go h.rotateLoop(1 * time.Minute)
	return h
}

func (h *histogram) With(metrics.Field) metrics.Histogram { return h }

func (h *histogram) Observe(value int64) {
	h.mu.Lock()
	err := h.hist.Current.RecordValue(value)
	h.mu.Unlock()

	if err != nil {
		panic(err.Error())
	}

	for q, gauge := range h.gauges {
		gauge.Set(float64(h.hist.Current.ValueAtQuantile(float64(q))))
	}
}

func (h *histogram) rotateLoop(d time.Duration) {
	for _ = range time.Tick(d) {
		h.mu.Lock()
		h.hist.Rotate()
		h.mu.Unlock()
	}
}
<|endoftext|>


<commit_before>/*
@Create Time     : 2020/9/10 16:08
@Project Name    : f5-rest-client
@File Name       : pool_stats_test.go
@Author          : zhangfeng
@Email           : 980252055@qq.com
@Software        : GoLand
*/

/*

 */
package ltm

import (
	"fmt"
	"github.com/e-XpertSolutions/f5-rest-client/f5"
	"log"
	"testing"
)

func TestPoolStatsResource_All(t *testing.T) {
	fmt.Println("sss")
	f5Client, err := f5.NewBasicClient("https://192.168.1.11", "admin", "admin")
	if err != nil {
		log.Fatal(err)
	}
	f5Client.DisableCertCheck()

	ltmClient := New(f5Client)
	fmt.Println(PoolStatsEndpoint)
	poolName := "zf"
	memberId := "192.168.0.100:27030"
	a, err := ltmClient.PoolStats().GetMemberStats(poolName, memberId)
	if err != nil {
		fmt.Println(err)
	}

	itemUrl := "https://localhost/mgmt/tm/ltm/pool/~Common~" + poolName + "/members/~Common~" + memberId + "/~Common~" + memberId + "/stats"
	MemberNestedStats := a.Entries[itemUrl]

	fmt.Println("---------------------")
	fmt.Println("Kind:", a.Kind)

	fmt.Println("---------------------")
	fmt.Println("SelfLink:", a.SelfLink)

	fmt.Println("---------------------")
	fmt.Println("Entries:", a.Entries)

	fmt.Println("---------------------")
	fmt.Println("MemberNestedStats:", MemberNestedStats)

	fmt.Println("---------------------")
	fmt.Println(MemberNestedStats.MemberNestedStats.Kind)

	fmt.Println("---------------------")
	fmt.Println(MemberNestedStats.MemberNestedStats.SelfLink)

	fmt.Println("---------------------")
	fmt.Println(MemberNestedStats.MemberNestedStats.Entries)

	fmt.Println("---------------------")
	fmt.Println(MemberNestedStats.MemberNestedStats.Entries.Addr.Description)

	fmt.Println("---------------------")
	fmt.Println(MemberNestedStats.MemberNestedStats.Entries.ServersideCurConns.Value)
}
<commit_msg>Delete the test file pool_stats_test.go.<commit_after><|endoftext|>


<commit_before>package middleware

import (
	"net/http"
	"github.com/julienschmidt/httprouter"
	"github.com/rumyantseva/mif/models"
	"fmt"
	"strconv"
	"strings"
	"github.com/AlekSi/pointer"
)

// SearchCategoryRequest describes parameters of categories searching request.
type SearchCategoryRequest struct {
	title *string
	limit  int
	offset int
}

func (mw *MW) SearchCategories(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	request := mw.searchCategoriesRequest(r, w)
	if request == nil {
		return
	}

	var args []interface{}

	where := ""
	if request.title != nil {
		where += " WHERE Title ILIKE " + mw.db.Placeholder(1)
		args = append(args, *request.title)
	}

	order := fmt.Sprintf(" ORDER BY Title ASC LIMIT %d OFFSET %d", request.limit, request.offset)
	query := where + order

	sts, err := mw.db.SelectAllFrom(models.CategoryTable, query, args...)
	if err != nil {
		mw.log.Errorf("Couldn't get data from DB for categories: %s", err.Error())
		mw.errorServer(w)
		return
	}

	var categories []*models.Category
	for _, st := range sts {
		category := st.(*models.Category)
		categories = append(categories, category)
	}

	// Total amount of categories for the request
	var total int
	query = "SELECT COUNT(categories.*) FROM " + models.CategoryTable.Name()
	err = mw.db.QueryRow(query).Scan(&total)
	if err != nil {
		mw.log.Errorf("Couldn't get data from DB for categories total count: %s", err.Error())
		mw.errorServer(w)
		return
	}

	meta := struct {
		Total  int `json:"total"`
		Limit  int `json:"limit"`
		Offset int `json:"offset"`
	}{
		Limit:  request.limit,
		Offset: request.offset,
		Total:  total,
	}

	mw.makeDataBody(w, http.StatusOK, categories, meta)
}

// searchCategoriesRequest checks http request and makes SearchCategoryRequest item.
func (mw *MW) searchCategoriesRequest(r *http.Request, w http.ResponseWriter) *SearchCategoryRequest {
	request := &SearchCategoryRequest{
		title: nil,
		limit:  10,
		offset: 0,
	}

	title := clearWhitespaces(r.URL.Query().Get("title"))
	if len(title) > 0 {
		request.title = pointer.ToString("%" + title + "%")
	}

	limit := strings.Trim(r.URL.Query().Get("limit"), " ")
	if len(limit) > 0 {
		numLim, err := strconv.Atoi(limit)
		if err != nil || numLim <= 0 {
			mw.makeError(w, http.StatusBadRequest, "Limit must be integer between 1 and 100.")
			return nil
		}

		if numLim > 100 {
			mw.makeError(w, http.StatusBadRequest, "Limit must be integer between 1 and 100.")
			return nil
		}

		request.limit = numLim
	}

	offset := strings.Trim(r.URL.Query().Get("offset"), " ")
	if len(offset) > 0 {
		numOffset, err := strconv.Atoi(offset)
		if err != nil || numOffset < 0 {
			mw.makeError(w, http.StatusBadRequest, "Offset must be non-negative integer.")
			return nil
		}

		request.offset = numOffset
	}

	return request
}
<commit_msg>run gofmt against middleware/categories.go<commit_after>package middleware

import (
	"fmt"
	"github.com/AlekSi/pointer"
	"github.com/julienschmidt/httprouter"
	"github.com/rumyantseva/mif/models"
	"net/http"
	"strconv"
	"strings"
)

// SearchCategoryRequest describes parameters of categories searching request.
type SearchCategoryRequest struct {
	title  *string
	limit  int
	offset int
}

func (mw *MW) SearchCategories(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	request := mw.searchCategoriesRequest(r, w)
	if request == nil {
		return
	}

	var args []interface{}

	where := ""
	if request.title != nil {
		where += " WHERE Title ILIKE " + mw.db.Placeholder(1)
		args = append(args, *request.title)
	}

	order := fmt.Sprintf(" ORDER BY Title ASC LIMIT %d OFFSET %d", request.limit, request.offset)
	query := where + order

	sts, err := mw.db.SelectAllFrom(models.CategoryTable, query, args...)
	if err != nil {
		mw.log.Errorf("Couldn't get data from DB for categories: %s", err.Error())
		mw.errorServer(w)
		return
	}

	var categories []*models.Category
	for _, st := range sts {
		category := st.(*models.Category)
		categories = append(categories, category)
	}

	// Total amount of categories for the request
	var total int
	query = "SELECT COUNT(categories.*) FROM " + models.CategoryTable.Name()
	err = mw.db.QueryRow(query).Scan(&total)
	if err != nil {
		mw.log.Errorf("Couldn't get data from DB for categories total count: %s", err.Error())
		mw.errorServer(w)
		return
	}

	meta := struct {
		Total  int `json:"total"`
		Limit  int `json:"limit"`
		Offset int `json:"offset"`
	}{
		Limit:  request.limit,
		Offset: request.offset,
		Total:  total,
	}

	mw.makeDataBody(w, http.StatusOK, categories, meta)
}

// searchCategoriesRequest checks http request and makes SearchCategoryRequest item.
func (mw *MW) searchCategoriesRequest(r *http.Request, w http.ResponseWriter) *SearchCategoryRequest {
	request := &SearchCategoryRequest{
		title:  nil,
		limit:  10,
		offset: 0,
	}

	title := clearWhitespaces(r.URL.Query().Get("title"))
	if len(title) > 0 {
		request.title = pointer.ToString("%" + title + "%")
	}

	limit := strings.Trim(r.URL.Query().Get("limit"), " ")
	if len(limit) > 0 {
		numLim, err := strconv.Atoi(limit)
		if err != nil || numLim <= 0 {
			mw.makeError(w, http.StatusBadRequest, "Limit must be integer between 1 and 100.")
			return nil
		}

		if numLim > 100 {
			mw.makeError(w, http.StatusBadRequest, "Limit must be integer between 1 and 100.")
			return nil
		}

		request.limit = numLim
	}

	offset := strings.Trim(r.URL.Query().Get("offset"), " ")
	if len(offset) > 0 {
		numOffset, err := strconv.Atoi(offset)
		if err != nil || numOffset < 0 {
			mw.makeError(w, http.StatusBadRequest, "Offset must be non-negative integer.")
			return nil
		}

		request.offset = numOffset
	}

	return request
}
<|endoftext|>


<commit_before>package internal

import (
	"github.com/liangdas/mqant/gate"
	"fmt"
)

type Room struct{
	roomID uint
	roomPlayers map[uint]gate.Agent
}

func   newRoom(roomID uint) *Room{
	fmt.Println("newRoom",roomID)
	room := Room{roomID:roomID}

	return  &room
}

<commit_msg>Update room.go<commit_after>package internal

import (
	"fmt"
)

type Room struct{
	roomID uint
	roomPlayers map[uint]gate.Agent
}

func   newRoom(roomID uint) *Room{
	fmt.Println("newRoom",roomID)
	room := Room{roomID:roomID}

	return  &room
}

<|endoftext|>


<commit_before>package models

import (
	"log"
	"testing"
	"time"

	"github.com/antihax/goesi"
)

func TestUpdateCharacter(t *testing.T) {
	err := UpdateCorporation(147035273, "Dude Corp", "TEST2", 10,
		0, 0, 50, time.Now())
	if err != nil {
		log.Fatal(err)
		return
	}

	err = UpdateCharacter(1001, "dude", 1, 1, 147035273, 0, 1, "male", -10, time.Now())
	if err != nil {
		log.Fatal(err)
		return
	}
}

func TestAddCRESTToken(t *testing.T) {
	tok := goesi.CRESTToken{
		AccessToken:  "FAKE",
		RefreshToken: "So Fake",
		Expiry:       time.Now().UTC().Add(time.Hour * 100000),
		TokenType:    "Bearer"}

	err := AddCRESTToken(1, 1, "Dude", &tok, "")
	if err != nil {
		t.Error(err)
		return
	}
}

func TestGetCRESTTokens(t *testing.T) {
	dude, err := GetCRESTTokens(1)
	if err != nil {
		t.Error(err)
		return
	}
	if dude[0].CharacterName != "Dude" {
		t.Error("Character name is not as stored")
		return
	}
}

func TestGetCRESTToken(t *testing.T) {
	dude, err := GetCRESTToken(1, 1)
	if err != nil {
		t.Error(err)
		return
	}
	if dude.CharacterName != "Dude" {
		t.Error("Character name is not as stored")
		return
	}
}

func TestSetTokenError(t *testing.T) {
	err := SetTokenError(1, 1, 200, "OK", nil, nil)
	if err != nil {
		t.Error(err)
		return
	}
}

func TestDeleteCRESTToken(t *testing.T) {
	err := DeleteCRESTToken(1, 1)
	if err != nil {
		t.Error(err)
		return
	}
}

func TestGetCharacter(t *testing.T) {
	char, err := GetCharacter(1001)
	if err != nil {
		t.Error(err)
		return
	}
	if char.CorporationID != 147035273 {
		t.Error("Character corporationID does not match")
		return
	}
}

func TestGetCharacterIDByName(t *testing.T) {
	char, err := GetCharacterIDByName("dude")
	if err != nil {
		t.Error(err)
		return
	}
	if char != 1001 {
		t.Error("CharacterID does not match")
		return
	}
}
<commit_msg>Add missing tests for codecov<commit_after>package models

import (
	"log"
	"testing"
	"time"

	"github.com/antihax/goesi"
)

func TestUpdateCharacter(t *testing.T) {
	err := UpdateCorporation(147035273, "Dude Corp", "TEST2", 10,
		0, 0, 50, time.Now())
	if err != nil {
		log.Fatal(err)
		return
	}

	err = UpdateCharacter(1001, "dude", 1, 1, 147035273, 0, 1, "male", -10, time.Now())
	if err != nil {
		log.Fatal(err)
		return
	}

	err = UpdateCharacter(1002, "dude 2", 1, 1, 147035273, 0, 2, "Female", -10, time.Now())
	if err != nil {
		log.Fatal(err)
		return
	}

	err = UpdateCorporationHistory(1001, 147035273, 100000222, time.Now())
	if err != nil {
		log.Fatal(err)
		return
	}

	hist, err := GetCorporationHistory(1001)
	if err != nil {
		log.Fatal(err)
		return
	}

	if hist[0].CorporationID != 147035273 {
		log.Fatal("wrong corporationID in history")
	}
}

func TestCursor(t *testing.T) {
	err := SetCursorCharacter(1001, 1001)
	if err != nil {
		log.Fatal(err)
		return
	}

	cursor, err := GetCursorCharacter(1001)
	if err != nil {
		log.Fatal(err)
		return
	}

	if cursor.CursorCharacterID != 1001 {
		log.Fatal("Wrong cursor returned")
	}
}

func TestAddCRESTToken(t *testing.T) {
	tok := goesi.CRESTToken{
		AccessToken:  "FAKE",
		RefreshToken: "So Fake",
		Expiry:       time.Now().UTC().Add(time.Hour * 100000),
		TokenType:    "Bearer"}

	err := AddCRESTToken(1, 1, "Dude", &tok, "")
	if err != nil {
		t.Error(err)
		return
	}
}

func TestGetCRESTTokens(t *testing.T) {
	dude, err := GetCRESTTokens(1)
	if err != nil {
		t.Error(err)
		return
	}
	if dude[0].CharacterName != "Dude" {
		t.Error("Character name is not as stored")
		return
	}
}

func TestGetCRESTToken(t *testing.T) {
	dude, err := GetCRESTToken(1, 1)
	if err != nil {
		t.Error(err)
		return
	}
	if dude.CharacterName != "Dude" {
		t.Error("Character name is not as stored")
		return
	}
}

func TestSetTokenError(t *testing.T) {
	err := SetTokenError(1, 1, 200, "OK", nil, nil)
	if err != nil {
		t.Error(err)
		return
	}
}

func TestDeleteCRESTToken(t *testing.T) {
	err := DeleteCRESTToken(1, 1)
	if err != nil {
		t.Error(err)
		return
	}
}

func TestGetCharacter(t *testing.T) {
	char, err := GetCharacter(1001)
	if err != nil {
		t.Error(err)
		return
	}
	if char.CorporationID != 147035273 {
		t.Error("Character corporationID does not match")
		return
	}
}

func TestGetCharacterIDByName(t *testing.T) {
	char, err := GetCharacterIDByName("dude")
	if err != nil {
		t.Error(err)
		return
	}
	if char != 1001 {
		t.Error("CharacterID does not match")
		return
	}
}

func TestGetKnownAlts(t *testing.T) {

	_, err := database.Exec(`
			INSERT IGNORE INTO evedata.characterAssociations VALUES
			 (1001, 1002,3);
		`)
	if err != nil {
		t.Error(err)
		return
	}

	alts, err := GetKnownAlts(1001)
	if err != nil {
		t.Error(err)
		return
	}
	if alts[0].CharacterID != 1002 {
		t.Error("CharacterID does not match")
		return
	}
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Miles Crabill <mcrabill@mozilla.com>

package github

import (
	"fmt"
	"log"
	"strings"

	"github.com/google/go-github/github"
	"go.mozilla.org/userplex/modules"
	"golang.org/x/oauth2"
)

func init() {
	modules.Register("github", new(module))
}

type module struct{}

func (m *module) NewRun(c modules.Configuration) modules.Runner {
	r := new(run)
	r.Conf = c
	return r
}

type run struct {
	Conf         modules.Configuration
	p            parameters
	c            credentials
	ghclient     *github.Client
	githubToLdap map[string]string
	ldapToGithub map[string]string
}

type organization struct {
	Name  string
	Teams []string
}

type parameters struct {
	Organization     organization
	UserplexTeamName string
	Enforce2FA       bool
}

type credentials struct {
	OAuthToken string `yaml:"oauthtoken"`
}

func (r *run) Run() (err error) {
	var resp *github.Response

	err = r.Conf.GetParameters(&r.p)
	if err != nil {
		return
	}
	err = r.Conf.GetCredentials(&r.c)
	if err != nil {
		return
	}

	if r.p.UserplexTeamName == "" {
		return fmt.Errorf("[error] github: UserplexTeamName is not set")
	}

	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: r.c.OAuthToken},
	)
	tc := oauth2.NewClient(oauth2.NoContext, ts)
	r.ghclient = github.NewClient(tc)

	r.buildLdapMapping()
	ldapers := r.getLdapers()

	// get all members for the organization
	// name -> bool
	membersMap := r.getOrgMembersMap(r.p.Organization, "all")
	if r.Conf.Debug {
		log.Printf("[debug] github: found %d users for organization %s", len(membersMap), r.p.Organization.Name)
	}

	// get all teams for the organization
	// name -> team
	teamsMap := r.getOrgTeamsMap(r.p.Organization)
	if r.Conf.Debug {
		log.Printf("[debug] github: found %d teams for organization %s", len(teamsMap), r.p.Organization.Name)
	}

	teamMembersMap := make(map[string]map[string]bool)
	for _, team := range teamsMap {
		teamMembersMap[*team.Name] = make(map[string]bool)
		teamMembersMap[*team.Name] = r.getTeamMembersMap(team)
	}

	if _, ok := teamsMap[r.p.UserplexTeamName]; !ok {
		return fmt.Errorf("[error] github: could not find UserplexTeam \"%s\" for organization %s", r.p.UserplexTeamName, r.p.Organization.Name)
	}
	userplexedUsers := teamMembersMap[r.p.UserplexTeamName]

	var no2fa map[string]bool
	if r.p.Enforce2FA {
		no2fa = r.getOrgMembersMap(r.p.Organization, "2fa_disabled")
		log.Printf("[info] github: organization %s has %d total members and %d with 2fa disabled. %.2f%% have 2fa enabled.",
			r.p.Organization.Name, len(membersMap), len(no2fa), 100-100*float64(len(no2fa))/float64(len(membersMap)))
	}

	// member or admin
	membershipType := "member"

	countAdded := 0
	for user := range ldapers {
		// set to true to indicate that user in github has ldap match
		membersMap[user] = true

		userWasAddedToTeam := false
		// teams in config
		for _, teamName := range r.p.Organization.Teams {
			// if the team in config doesn't exist on github
			if team, ok := teamsMap[teamName]; !ok {
				return fmt.Errorf("[error] github: could not find team %s for organization %s", team, r.p.Organization.Name)
			} else {
				// if the user is already in the team, skip adding them
				if _, ok := teamMembersMap[teamName][user]; ok {
					continue
				}
				// user not in team, add them
				if r.Conf.ApplyChanges && r.Conf.Create {
					// add user to team
					_, resp, err = r.ghclient.Organizations.AddTeamMembership(*team.ID, user, &github.OrganizationAddTeamMembershipOptions{
						Role: membershipType,
					})
					if err != nil || resp.StatusCode != 200 {
						return fmt.Errorf("[error] github: could not add user %s to %s: %s, error: %v with status %s", user, r.p.Organization.Name, *team.Name, err, resp.Status)
					}
				}
				if r.Conf.Create {
					userWasAddedToTeam = true
				}
			}
		}
		if userWasAddedToTeam {
			if !r.Conf.ApplyChanges {
				log.Printf("[dryrun] github: would have added %s to GitHub organization %s and teams %v", user, r.p.Organization.Name, r.p.Organization.Teams)
			} else {
				countAdded++
			}
			r.notify(user, fmt.Sprintf("Userplex added %s to GitHub organization %s and teams %v", user, r.p.Organization.Name, r.p.Organization.Teams))
		}
	}

	countRemoved := 0
	for member := range membersMap {
		// if the member is not in the userplex team
		_, isUserplexed := userplexedUsers[member]
		if !isUserplexed {
			if r.Conf.Debug {
				log.Printf("[debug] github: skipping member %s in organization %s because they are not in UserplexTeam %s", member, r.p.Organization.Name, r.p.UserplexTeamName)
			}
			continue
		}

		var ldapUsername, ldapUsernameString string
		member = strings.ToLower(member)
		if _, ok := r.githubToLdap[member]; ok {
			ldapUsername = r.githubToLdap[member]
			ldapUsernameString = ldapUsername + " / "
		}

		var userTeams []string
		// icky iterating over all these teams
		for _, team := range teamsMap {
			if _, ok := teamMembersMap[*team.Name][member]; ok {
				userTeams = append(userTeams, *team.Name)
			}
		}

		// if the member does not have 2fa
		_, no2fa := no2fa[member]

		// if the user is in ldap
		_, inLdap := membersMap[member]

		if !inLdap || r.p.Enforce2FA && no2fa {
			if !inLdap && r.Conf.Debug {
				log.Printf("[debug] user %s%s is not in ldap groups %s but is a member of github organization %s and teams %v", ldapUsernameString, member, r.Conf.LdapGroups, r.p.Organization.Name, userTeams)
			}
			if r.p.Enforce2FA && no2fa {
				log.Printf("[info] user %s%s does not have 2FA enabled and is a member of github organization %s and teams %v", ldapUsernameString, member, r.p.Organization.Name, userTeams)
			}
			if r.Conf.Delete {
				if !r.Conf.ApplyChanges {
					log.Printf("[dryrun] Userplex would have removed %s from GitHub organization %s", member, r.p.Organization.Name)
				} else {
					resp, err = r.ghclient.Organizations.RemoveOrgMembership(r.p.Organization.Name, member)
					if err != nil || resp.StatusCode != 200 {
						log.Printf("[error] github: could not remove user %s from %s, error: %v with status %s", member, r.p.Organization.Name, err, resp.Status)
						continue
					}
					countRemoved++
				}
				r.notify(member, fmt.Sprintf("Userplex removed %s to GitHub organization %s", member, r.p.Organization.Name))
			}
		}
	}

	log.Printf("[info] github %q: summary added=%d, removed=%d",
		r.p.Organization.Name, countAdded, countRemoved)

	return nil
}

func (r *run) buildLdapMapping() {
	r.githubToLdap = make(map[string]string)
	r.ldapToGithub = make(map[string]string)
	for _, mapping := range r.Conf.UidMap {
		r.githubToLdap[mapping.LocalUID] = mapping.LdapUid
		r.ldapToGithub[mapping.LdapUid] = mapping.LocalUID
	}
}

func (r *run) getOrgMembersMap(org organization, filter string) (membersMap map[string]bool) {
	membersMap = make(map[string]bool)
	opt := &github.ListMembersOptions{
		Filter:      filter,
		ListOptions: github.ListOptions{PerPage: 100},
	}
	for {
		members, resp, err := r.ghclient.Organizations.ListMembers(org.Name, opt)
		if err != nil || resp.StatusCode != 200 {
			log.Printf("[error] github: could not list members for organization %s, error: %v with status %s", org, err, resp.Status)
			return
		}
		for _, member := range members {
			membersMap[*member.Login] = false
		}
		if resp.NextPage == 0 {
			break
		}
		opt.ListOptions.Page = resp.NextPage
	}
	return membersMap
}

func (r *run) getTeamMembersMap(team *github.Team) (membersMap map[string]bool) {
	membersMap = make(map[string]bool)
	opt := &github.OrganizationListTeamMembersOptions{
		ListOptions: github.ListOptions{
			PerPage: 100,
		},
	}
	for {
		members, resp, err := r.ghclient.Organizations.ListTeamMembers(*team.ID, opt)
		if err != nil || resp.StatusCode != 200 {
			log.Printf("[error] github: could not list members for organization %s, error: %v with status %s", team, err, resp.Status)
			return
		}
		for _, member := range members {
			membersMap[*member.Login] = false
		}
		if resp.NextPage == 0 {
			break
		}
		opt.ListOptions.Page = resp.NextPage
	}
	return membersMap
}

func (r *run) getOrgTeamsMap(org organization) (teamsMap map[string]*github.Team) {
	teamsMap = make(map[string]*github.Team)
	opt := &github.ListOptions{
		PerPage: 100,
	}
	for {
		teams, resp, err := r.ghclient.Organizations.ListTeams(org.Name, opt)
		if err != nil || resp.StatusCode != 200 {
			log.Printf("[error] github: could not list teams for organization %s, error: %v", org.Name, err)
			return
		}
		for _, team := range teams {
			teamsMap[*team.Name] = team
		}
		if resp.NextPage == 0 {
			break
		}
		opt.Page = resp.NextPage
	}
	return teamsMap
}

func (r *run) notify(user string, body string) (err error) {
	rcpt := r.Conf.Notify.Recipient
	if rcpt == "{ldap:mail}" {
		if _, ok := r.githubToLdap[user]; ok {
			user = r.githubToLdap[user]
		}
		rcpt, err = r.Conf.LdapCli.GetUserEmailByUid(user)
		if err != nil {
			return fmt.Errorf("[error] github: couldn't find email of user %q in ldap, notification not sent: %v", user, err)
		}
	}
	r.Conf.Notify.Channel <- modules.Notification{
		Module:      "github",
		Recipient:   rcpt,
		Mode:        r.Conf.Notify.Mode,
		MustEncrypt: false,
		Body:        []byte(body),
	}
	return
}

func (r *run) getLdapers() (lgm map[string]bool) {
	lgm = make(map[string]bool)
	users, err := r.Conf.LdapCli.GetEnabledUsersInGroups(r.Conf.LdapGroups)
	if err != nil {
		return
	}
	for _, user := range users {
		shortdn := strings.Split(user, ",")[0]
		uid, err := r.Conf.LdapCli.GetUserId(shortdn)
		if err != nil {
			log.Printf("[error] github: ldap query failed with error %v", err)
			continue
		}

		if _, ok := r.ldapToGithub[uid]; ok {
			uid = r.ldapToGithub[uid]
		}

		lgm[uid] = false
	}
	return
}
<commit_msg>github module: simplify / cleanup if/else logic around user deletion<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Miles Crabill <mcrabill@mozilla.com>

package github

import (
	"fmt"
	"log"
	"strings"

	"github.com/google/go-github/github"
	"go.mozilla.org/userplex/modules"
	"golang.org/x/oauth2"
)

func init() {
	modules.Register("github", new(module))
}

type module struct{}

func (m *module) NewRun(c modules.Configuration) modules.Runner {
	r := new(run)
	r.Conf = c
	return r
}

type run struct {
	Conf         modules.Configuration
	p            parameters
	c            credentials
	ghclient     *github.Client
	githubToLdap map[string]string
	ldapToGithub map[string]string
}

type organization struct {
	Name  string
	Teams []string
}

type parameters struct {
	Organization     organization
	UserplexTeamName string
	Enforce2FA       bool
}

type credentials struct {
	OAuthToken string `yaml:"oauthtoken"`
}

func (r *run) Run() (err error) {
	var resp *github.Response

	err = r.Conf.GetParameters(&r.p)
	if err != nil {
		return
	}
	err = r.Conf.GetCredentials(&r.c)
	if err != nil {
		return
	}

	if r.p.UserplexTeamName == "" {
		return fmt.Errorf("[error] github: UserplexTeamName is not set")
	}

	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: r.c.OAuthToken},
	)
	tc := oauth2.NewClient(oauth2.NoContext, ts)
	r.ghclient = github.NewClient(tc)

	r.buildLdapMapping()
	ldapers := r.getLdapers()

	// get all members for the organization
	// name -> bool
	membersMap := r.getOrgMembersMap(r.p.Organization, "all")
	if r.Conf.Debug {
		log.Printf("[debug] github: found %d users for organization %s", len(membersMap), r.p.Organization.Name)
	}

	// get all teams for the organization
	// name -> team
	teamsMap := r.getOrgTeamsMap(r.p.Organization)
	if r.Conf.Debug {
		log.Printf("[debug] github: found %d teams for organization %s", len(teamsMap), r.p.Organization.Name)
	}

	teamMembersMap := make(map[string]map[string]bool)
	for _, team := range teamsMap {
		teamMembersMap[*team.Name] = make(map[string]bool)
		teamMembersMap[*team.Name] = r.getTeamMembersMap(team)
	}

	if _, ok := teamsMap[r.p.UserplexTeamName]; !ok {
		return fmt.Errorf("[error] github: could not find UserplexTeam \"%s\" for organization %s", r.p.UserplexTeamName, r.p.Organization.Name)
	}
	userplexedUsers := teamMembersMap[r.p.UserplexTeamName]

	var no2fa map[string]bool
	if r.p.Enforce2FA {
		no2fa = r.getOrgMembersMap(r.p.Organization, "2fa_disabled")
		log.Printf("[info] github: organization %s has %d total members and %d with 2fa disabled. %.2f%% have 2fa enabled.",
			r.p.Organization.Name, len(membersMap), len(no2fa), 100-100*float64(len(no2fa))/float64(len(membersMap)))
	}

	// member or admin
	membershipType := "member"

	countAdded := 0
	for user := range ldapers {
		// set to true to indicate that user in github has ldap match
		membersMap[user] = true

		userWasAddedToTeam := false
		// teams in config
		for _, teamName := range r.p.Organization.Teams {
			// if the team in config doesn't exist on github
			if team, ok := teamsMap[teamName]; !ok {
				return fmt.Errorf("[error] github: could not find team %s for organization %s", team, r.p.Organization.Name)
			} else {
				// if the user is already in the team, skip adding them
				if _, ok := teamMembersMap[teamName][user]; ok {
					continue
				}
				// user not in team, add them
				if r.Conf.ApplyChanges && r.Conf.Create {
					// add user to team
					_, resp, err = r.ghclient.Organizations.AddTeamMembership(*team.ID, user, &github.OrganizationAddTeamMembershipOptions{
						Role: membershipType,
					})
					if err != nil || resp.StatusCode != 200 {
						return fmt.Errorf("[error] github: could not add user %s to %s: %s, error: %v with status %s", user, r.p.Organization.Name, *team.Name, err, resp.Status)
					}
				}
				if r.Conf.Create {
					userWasAddedToTeam = true
				}
			}
		}
		if userWasAddedToTeam {
			if !r.Conf.ApplyChanges {
				log.Printf("[dryrun] github: would have added %s to GitHub organization %s and teams %v", user, r.p.Organization.Name, r.p.Organization.Teams)
			} else {
				countAdded++
			}
			r.notify(user, fmt.Sprintf("Userplex added %s to GitHub organization %s and teams %v", user, r.p.Organization.Name, r.p.Organization.Teams))
		}
	}

	countRemoved := 0
	for member := range membersMap {
		var ldapUsername, ldapUsernameString string
		member = strings.ToLower(member)
		if _, ok := r.githubToLdap[member]; ok {
			ldapUsername = r.githubToLdap[member]
			ldapUsernameString = ldapUsername + " / "
		}

		var userTeams []string
		// icky iterating over all these teams
		for _, team := range teamsMap {
			if _, ok := teamMembersMap[*team.Name][member]; ok {
				userTeams = append(userTeams, *team.Name)
			}
		}

		// if the member does not have 2fa
		_, no2fa := no2fa[member]

		// if the user is in ldap
		_, inLdap := membersMap[member]

		// if the member is not in the userplex team
		_, isUserplexed := userplexedUsers[member]

		shouldDelete := false
		if !inLdap {
			if r.Conf.Debug {
				log.Printf("[debug] github: user %s%s is not in ldap groups %s but is a member of github organization %s and teams %v", ldapUsernameString, member, r.Conf.LdapGroups, r.p.Organization.Name, userTeams)
			}
			shouldDelete = true
		}

		if r.p.Enforce2FA && no2fa {
			log.Printf("[info] github: user %s%s does not have 2FA enabled and is a member of github organization %s and teams %v", ldapUsernameString, member, r.p.Organization.Name, userTeams)
			shouldDelete = true
		}

		if shouldDelete && r.Conf.Delete {
			// not in UserplexTeam -> skip
			if !isUserplexed {
				if r.Conf.Debug {
					log.Printf("[debug] github: would have removed member %s in organization %s, but skipped because they are not in UserplexTeam %s", member, r.p.Organization.Name, r.p.UserplexTeamName)
				}
				continue
			}
			if !r.Conf.ApplyChanges {
				log.Printf("[dryrun] github: Userplex would have removed %s from GitHub organization %s", member, r.p.Organization.Name)
				r.notify(member, fmt.Sprintf("Userplex removed %s to GitHub organization %s", member, r.p.Organization.Name))
				continue
			}
			// applying changes, user is userplexed -> remove them
			resp, err = r.ghclient.Organizations.RemoveOrgMembership(r.p.Organization.Name, member)
			if err != nil || resp.StatusCode != 200 {
				log.Printf("[error] github: could not remove user %s from %s, error: %v with status %s", member, r.p.Organization.Name, err, resp.Status)
				continue
			}
			countRemoved++
			r.notify(member, fmt.Sprintf("Userplex removed %s to GitHub organization %s", member, r.p.Organization.Name))
		}
	}

	log.Printf("[info] github %q: summary added=%d, removed=%d",
		r.p.Organization.Name, countAdded, countRemoved)

	return nil
}

func (r *run) buildLdapMapping() {
	r.githubToLdap = make(map[string]string)
	r.ldapToGithub = make(map[string]string)
	for _, mapping := range r.Conf.UidMap {
		r.githubToLdap[mapping.LocalUID] = mapping.LdapUid
		r.ldapToGithub[mapping.LdapUid] = mapping.LocalUID
	}
}

func (r *run) getOrgMembersMap(org organization, filter string) (membersMap map[string]bool) {
	membersMap = make(map[string]bool)
	opt := &github.ListMembersOptions{
		Filter:      filter,
		ListOptions: github.ListOptions{PerPage: 100},
	}
	for {
		members, resp, err := r.ghclient.Organizations.ListMembers(org.Name, opt)
		if err != nil || resp.StatusCode != 200 {
			log.Printf("[error] github: could not list members for organization %s, error: %v with status %s", org, err, resp.Status)
			return
		}
		for _, member := range members {
			membersMap[*member.Login] = false
		}
		if resp.NextPage == 0 {
			break
		}
		opt.ListOptions.Page = resp.NextPage
	}
	return membersMap
}

func (r *run) getTeamMembersMap(team *github.Team) (membersMap map[string]bool) {
	membersMap = make(map[string]bool)
	opt := &github.OrganizationListTeamMembersOptions{
		ListOptions: github.ListOptions{
			PerPage: 100,
		},
	}
	for {
		members, resp, err := r.ghclient.Organizations.ListTeamMembers(*team.ID, opt)
		if err != nil || resp.StatusCode != 200 {
			log.Printf("[error] github: could not list members for organization %s, error: %v with status %s", team, err, resp.Status)
			return
		}
		for _, member := range members {
			membersMap[*member.Login] = false
		}
		if resp.NextPage == 0 {
			break
		}
		opt.ListOptions.Page = resp.NextPage
	}
	return membersMap
}

func (r *run) getOrgTeamsMap(org organization) (teamsMap map[string]*github.Team) {
	teamsMap = make(map[string]*github.Team)
	opt := &github.ListOptions{
		PerPage: 100,
	}
	for {
		teams, resp, err := r.ghclient.Organizations.ListTeams(org.Name, opt)
		if err != nil || resp.StatusCode != 200 {
			log.Printf("[error] github: could not list teams for organization %s, error: %v", org.Name, err)
			return
		}
		for _, team := range teams {
			teamsMap[*team.Name] = team
		}
		if resp.NextPage == 0 {
			break
		}
		opt.Page = resp.NextPage
	}
	return teamsMap
}

func (r *run) notify(user string, body string) (err error) {
	rcpt := r.Conf.Notify.Recipient
	if rcpt == "{ldap:mail}" {
		if _, ok := r.githubToLdap[user]; ok {
			user = r.githubToLdap[user]
		}
		rcpt, err = r.Conf.LdapCli.GetUserEmailByUid(user)
		if err != nil {
			return fmt.Errorf("[error] github: couldn't find email of user %q in ldap, notification not sent: %v", user, err)
		}
	}
	r.Conf.Notify.Channel <- modules.Notification{
		Module:      "github",
		Recipient:   rcpt,
		Mode:        r.Conf.Notify.Mode,
		MustEncrypt: false,
		Body:        []byte(body),
	}
	return
}

func (r *run) getLdapers() (lgm map[string]bool) {
	lgm = make(map[string]bool)
	users, err := r.Conf.LdapCli.GetEnabledUsersInGroups(r.Conf.LdapGroups)
	if err != nil {
		return
	}
	for _, user := range users {
		shortdn := strings.Split(user, ",")[0]
		uid, err := r.Conf.LdapCli.GetUserId(shortdn)
		if err != nil {
			log.Printf("[error] github: ldap query failed with error %v", err)
			continue
		}

		if _, ok := r.ldapToGithub[uid]; ok {
			uid = r.ldapToGithub[uid]
		}

		lgm[uid] = false
	}
	return
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 by Richard A. Wilkes. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible With Secondary Licenses", as
// defined by the Mozilla Public License, version 2.0.

package widget

import (
	"github.com/richardwilkes/ui"
	"github.com/richardwilkes/ui/border"
	"github.com/richardwilkes/ui/color"
	"github.com/richardwilkes/ui/draw"
	"github.com/richardwilkes/ui/event"
	"github.com/richardwilkes/ui/geom"
	"reflect"
)

// Block is the basic graphical block in a window.
type Block struct {
	eventHandlers *event.Handlers
	window        ui.Window
	parent        ui.Widget
	children      []ui.Widget
	sizer         ui.Sizer
	layout        ui.Layout
	border        border.Border
	bounds        geom.Rect
	layoutData    interface{}
	background    color.Color
	needLayout    bool
	disabled      bool
	focusable     bool
	padding       bool // Just here to quiet aligncheck, since there is nothing I can do about it
}

// NewBlock creates a new, empty block.
func NewBlock() *Block {
	return &Block{}
}

// EventHandlers implements the event.Target interface.
func (b *Block) EventHandlers() *event.Handlers {
	if b.eventHandlers == nil {
		b.eventHandlers = &event.Handlers{}
	}
	return b.eventHandlers
}

// ParentTarget implements the event.Target interface.
func (b *Block) ParentTarget() event.Target {
	if b.parent != nil {
		return b.parent
	}
	return b.window
}

// Sizer implements the Widget interface.
func (b *Block) Sizer() ui.Sizer {
	return b.sizer
}

// SetSizer implements the Widget interface.
func (b *Block) SetSizer(sizer ui.Sizer) {
	b.sizer = sizer
}

// Layout implements the Widget interface.
func (b *Block) Layout() ui.Layout {
	return b.layout
}

// SetLayout implements the Widget interface.
func (b *Block) SetLayout(layout ui.Layout) {
	b.layout = layout
	b.SetNeedLayout(true)
}

// NeedLayout implements the Widget interface.
func (b *Block) NeedLayout() bool {
	return b.needLayout
}

// SetNeedLayout implements the Widget interface.
func (b *Block) SetNeedLayout(needLayout bool) {
	b.needLayout = needLayout
}

// LayoutData implements the Widget interface.
func (b *Block) LayoutData() interface{} {
	return b.layoutData
}

// SetLayoutData implements the Widget interface.
func (b *Block) SetLayoutData(data interface{}) {
	if b.layoutData != data {
		b.layoutData = data
		b.SetNeedLayout(true)
	}
}

// ValidateLayout implements the Widget interface.
func (b *Block) ValidateLayout() {
	if b.NeedLayout() {
		if layout := b.Layout(); layout != nil {
			layout.Layout()
			b.Repaint()
		}
		b.SetNeedLayout(false)
	}
	for _, child := range b.children {
		child.ValidateLayout()
	}
}

// Border implements the Widget interface.
func (b *Block) Border() border.Border {
	return b.border
}

// SetBorder implements the Widget interface.
func (b *Block) SetBorder(border border.Border) {
	b.border = border
}

// Repaint implements the Widget interface.
func (b *Block) Repaint() {
	b.RepaintBounds(b.LocalBounds())
}

// RepaintBounds implements the Widget interface.
func (b *Block) RepaintBounds(bounds geom.Rect) {
	bounds.Intersect(b.LocalBounds())
	if !bounds.IsEmpty() {
		if p := b.Parent(); p != nil {
			bounds.X += b.bounds.X
			bounds.Y += b.bounds.Y
			p.RepaintBounds(bounds)
		} else if b.RootOfWindow() {
			b.Window().RepaintBounds(bounds)
		}
	}
}

// Paint implements the Widget interface.
func (b *Block) Paint(g draw.Graphics, dirty geom.Rect) {
	dirty.Intersect(b.LocalBounds())
	if !dirty.IsEmpty() {
		b.paintSelf(g, dirty)
		for _, child := range b.children {
			adjusted := dirty
			adjusted.Intersect(child.Bounds())
			if !adjusted.IsEmpty() {
				b.paintChild(child, g, adjusted)
			}
		}
	}
}

func (b *Block) paintSelf(gc draw.Graphics, dirty geom.Rect) {
	gc.Save()
	gc.ClipRect(dirty)
	if b.background.Alpha() > 0 {
		gc.SetFillColor(b.background)
		gc.FillRect(dirty)
	}
	gc.Save()
	event.Dispatch(event.NewPaint(b, gc, dirty))
	gc.Restore()
	b.paintBorder(gc)
	gc.Restore()
}

func (b *Block) paintBorder(gc draw.Graphics) {
	if border := b.Border(); border != nil {
		gc.Save()
		defer gc.Restore()
		border.Draw(gc, b.LocalBounds())
	}
}

func (b *Block) paintChild(child ui.Widget, g draw.Graphics, dirty geom.Rect) {
	g.Save()
	defer g.Restore()
	bounds := child.Bounds()
	g.Translate(bounds.X, bounds.Y)
	dirty.X -= bounds.X
	dirty.Y -= bounds.Y
	child.Paint(g, dirty)
}

// Enabled implements the Widget interface.
func (b *Block) Enabled() bool {
	return !b.disabled
}

// SetEnabled implements the Widget interface.
func (b *Block) SetEnabled(enabled bool) {
	disabled := !enabled
	if b.disabled != disabled {
		b.disabled = disabled
		b.Repaint()
	}
}

// Focusable implements the Widget interface.
func (b *Block) Focusable() bool {
	return b.focusable && !b.disabled
}

// SetFocusable implements the Widget interface.
func (b *Block) SetFocusable(focusable bool) {
	if b.focusable != focusable {
		b.focusable = focusable
	}
}

// Focused implements the Widget interface.
func (b *Block) Focused() bool {
	if window := b.Window(); window != nil {
		return reflect.ValueOf(ui.Widget(b)).Pointer() == reflect.ValueOf(window.Focus()).Pointer()
	}
	return false
}

// Children implements the Widget interface.
func (b *Block) Children() []ui.Widget {
	return b.children
}

// IndexOfChild implements the Widget interface.
func (b *Block) IndexOfChild(child ui.Widget) int {
	for i, one := range b.children {
		if one == child {
			return i
		}
	}
	return -1
}

// AddChild implements the Widget interface.
func (b *Block) AddChild(child ui.Widget) {
	child.RemoveFromParent()
	b.children = append(b.children, child)
	child.SetParent(b)
	b.SetNeedLayout(true)
}

// AddChildAtIndex implements the Widget interface.
func (b *Block) AddChildAtIndex(child ui.Widget, index int) {
	child.RemoveFromParent()
	if index < 0 {
		index = 0
	}
	if index >= len(b.children) {
		b.children = append(b.children, child)
	} else {
		b.children = append(b.children, nil)
		copy(b.children[index+1:], b.children[index:])
		b.children[index] = child
	}
	child.SetParent(b)
	b.SetNeedLayout(true)
}

// RemoveChild implements the Widget interface.
func (b *Block) RemoveChild(child ui.Widget) {
	b.RemoveChildAtIndex(b.IndexOfChild(child))
}

// RemoveChildAtIndex implements the Widget interface.
func (b *Block) RemoveChildAtIndex(index int) {
	if index >= 0 && index < len(b.children) {
		child := b.children[index]
		copy(b.children[index:], b.children[index+1:])
		length := len(b.children) - 1
		b.children[length] = nil
		b.children = b.children[:length]
		b.SetNeedLayout(true)
		child.SetParent(nil)
	}
}

// RemoveFromParent implements the Widget interface.
func (b *Block) RemoveFromParent() {
	if p := b.Parent(); p != nil {
		p.RemoveChild(b)
	}
}

// Parent implements the Widget interface.
func (b *Block) Parent() ui.Widget {
	return b.parent
}

// SetParent implements the Widget interface.
func (b *Block) SetParent(parent ui.Widget) {
	b.parent = parent
}

// Window implements the Widget interface.
func (b *Block) Window() ui.Window {
	if b.window != nil {
		return b.window
	}
	if b.parent != nil {
		return b.parent.Window()
	}
	return nil
}

// RootOfWindow implements the Widget interface.
func (b *Block) RootOfWindow() bool {
	return b.window != nil
}

// Bounds implements the Widget interface.
func (b *Block) Bounds() geom.Rect {
	return b.bounds
}

// LocalBounds implements the Widget interface.
func (b *Block) LocalBounds() geom.Rect {
	return b.bounds.CopyAndZeroLocation()
}

// LocalInsetBounds implements the Widget interface.
func (b *Block) LocalInsetBounds() geom.Rect {
	bounds := b.LocalBounds()
	if border := b.Border(); border != nil {
		bounds.Inset(border.Insets())
	}
	return bounds
}

// SetBounds implements the Widget interface.
func (b *Block) SetBounds(bounds geom.Rect) {
	moved := b.bounds.X != bounds.X || b.bounds.Y != bounds.Y
	resized := b.bounds.Width != bounds.Width || b.bounds.Height != bounds.Height
	if moved || resized {
		b.Repaint()
		if moved {
			b.bounds.Point = bounds.Point
		}
		if resized {
			b.bounds.Size = bounds.Size
			b.SetNeedLayout(true)
			event.Dispatch(event.NewResized(b))
		}
		b.Repaint()
	}
}

// Location implements the Widget interface.
func (b *Block) Location() geom.Point {
	return b.bounds.Point
}

// SetLocation implements the Widget interface.
func (b *Block) SetLocation(pt geom.Point) {
	if b.bounds.Point != pt {
		b.Repaint()
		b.bounds.Point = pt
		b.Repaint()
	}
}

// Size implements the Widget interface.
func (b *Block) Size() geom.Size {
	return b.bounds.Size
}

// SetSize implements the Widget interface.
func (b *Block) SetSize(size geom.Size) {
	if b.bounds.Size != size {
		b.Repaint()
		b.bounds.Size = size
		b.SetNeedLayout(true)
		event.Dispatch(event.NewResized(b))
		b.Repaint()
	}
}

// WidgetAt implements the Widget interface.
func (b *Block) WidgetAt(pt geom.Point) ui.Widget {
	for _, child := range b.children {
		bounds := child.Bounds()
		if bounds.Contains(pt) {
			pt.Subtract(bounds.Point)
			return child.WidgetAt(pt)
		}
	}
	return b
}

// ToWindow implements the Widget interface.
func (b *Block) ToWindow(pt geom.Point) geom.Point {
	pt.Add(b.bounds.Point)
	parent := b.parent
	for parent != nil {
		pt.Add(parent.Bounds().Point)
		parent = parent.Parent()
	}
	return pt
}

// FromWindow implements the Widget interface.
func (b *Block) FromWindow(pt geom.Point) geom.Point {
	pt.Subtract(b.bounds.Point)
	parent := b.parent
	for parent != nil {
		pt.Subtract(parent.Bounds().Point)
		parent = parent.Parent()
	}
	return pt
}

// Background implements the Widget interface.
func (b *Block) Background() color.Color {
	return b.background
}

// SetBackground implements the Widget interface.
func (b *Block) SetBackground(color color.Color) {
	if color != b.background {
		b.background = color
		b.Repaint()
	}
}
<commit_msg>Add placeholder for ScrollIntoView<commit_after>// Copyright (c) 2016 by Richard A. Wilkes. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible With Secondary Licenses", as
// defined by the Mozilla Public License, version 2.0.

package widget

import (
	"github.com/richardwilkes/ui"
	"github.com/richardwilkes/ui/border"
	"github.com/richardwilkes/ui/color"
	"github.com/richardwilkes/ui/draw"
	"github.com/richardwilkes/ui/event"
	"github.com/richardwilkes/ui/geom"
	"reflect"
)

// Block is the basic graphical block in a window.
type Block struct {
	eventHandlers *event.Handlers
	window        ui.Window
	parent        ui.Widget
	children      []ui.Widget
	sizer         ui.Sizer
	layout        ui.Layout
	border        border.Border
	bounds        geom.Rect
	layoutData    interface{}
	background    color.Color
	needLayout    bool
	disabled      bool
	focusable     bool
	padding       bool // Just here to quiet aligncheck, since there is nothing I can do about it
}

// NewBlock creates a new, empty block.
func NewBlock() *Block {
	return &Block{}
}

// EventHandlers implements the event.Target interface.
func (b *Block) EventHandlers() *event.Handlers {
	if b.eventHandlers == nil {
		b.eventHandlers = &event.Handlers{}
	}
	return b.eventHandlers
}

// ParentTarget implements the event.Target interface.
func (b *Block) ParentTarget() event.Target {
	if b.parent != nil {
		return b.parent
	}
	return b.window
}

// Sizer implements the Widget interface.
func (b *Block) Sizer() ui.Sizer {
	return b.sizer
}

// SetSizer implements the Widget interface.
func (b *Block) SetSizer(sizer ui.Sizer) {
	b.sizer = sizer
}

// Layout implements the Widget interface.
func (b *Block) Layout() ui.Layout {
	return b.layout
}

// SetLayout implements the Widget interface.
func (b *Block) SetLayout(layout ui.Layout) {
	b.layout = layout
	b.SetNeedLayout(true)
}

// NeedLayout implements the Widget interface.
func (b *Block) NeedLayout() bool {
	return b.needLayout
}

// SetNeedLayout implements the Widget interface.
func (b *Block) SetNeedLayout(needLayout bool) {
	b.needLayout = needLayout
}

// LayoutData implements the Widget interface.
func (b *Block) LayoutData() interface{} {
	return b.layoutData
}

// SetLayoutData implements the Widget interface.
func (b *Block) SetLayoutData(data interface{}) {
	if b.layoutData != data {
		b.layoutData = data
		b.SetNeedLayout(true)
	}
}

// ValidateLayout implements the Widget interface.
func (b *Block) ValidateLayout() {
	if b.NeedLayout() {
		if layout := b.Layout(); layout != nil {
			layout.Layout()
			b.Repaint()
		}
		b.SetNeedLayout(false)
	}
	for _, child := range b.children {
		child.ValidateLayout()
	}
}

// Border implements the Widget interface.
func (b *Block) Border() border.Border {
	return b.border
}

// SetBorder implements the Widget interface.
func (b *Block) SetBorder(border border.Border) {
	b.border = border
}

// Repaint implements the Widget interface.
func (b *Block) Repaint() {
	b.RepaintBounds(b.LocalBounds())
}

// RepaintBounds implements the Widget interface.
func (b *Block) RepaintBounds(bounds geom.Rect) {
	bounds.Intersect(b.LocalBounds())
	if !bounds.IsEmpty() {
		if p := b.Parent(); p != nil {
			bounds.X += b.bounds.X
			bounds.Y += b.bounds.Y
			p.RepaintBounds(bounds)
		} else if b.RootOfWindow() {
			b.Window().RepaintBounds(bounds)
		}
	}
}

// Paint implements the Widget interface.
func (b *Block) Paint(g draw.Graphics, dirty geom.Rect) {
	dirty.Intersect(b.LocalBounds())
	if !dirty.IsEmpty() {
		b.paintSelf(g, dirty)
		for _, child := range b.children {
			adjusted := dirty
			adjusted.Intersect(child.Bounds())
			if !adjusted.IsEmpty() {
				b.paintChild(child, g, adjusted)
			}
		}
	}
}

func (b *Block) paintSelf(gc draw.Graphics, dirty geom.Rect) {
	gc.Save()
	gc.ClipRect(dirty)
	if b.background.Alpha() > 0 {
		gc.SetFillColor(b.background)
		gc.FillRect(dirty)
	}
	gc.Save()
	event.Dispatch(event.NewPaint(b, gc, dirty))
	gc.Restore()
	b.paintBorder(gc)
	gc.Restore()
}

func (b *Block) paintBorder(gc draw.Graphics) {
	if border := b.Border(); border != nil {
		gc.Save()
		defer gc.Restore()
		border.Draw(gc, b.LocalBounds())
	}
}

func (b *Block) paintChild(child ui.Widget, g draw.Graphics, dirty geom.Rect) {
	g.Save()
	defer g.Restore()
	bounds := child.Bounds()
	g.Translate(bounds.X, bounds.Y)
	dirty.X -= bounds.X
	dirty.Y -= bounds.Y
	child.Paint(g, dirty)
}

// Enabled implements the Widget interface.
func (b *Block) Enabled() bool {
	return !b.disabled
}

// SetEnabled implements the Widget interface.
func (b *Block) SetEnabled(enabled bool) {
	disabled := !enabled
	if b.disabled != disabled {
		b.disabled = disabled
		b.Repaint()
	}
}

// Focusable implements the Widget interface.
func (b *Block) Focusable() bool {
	return b.focusable && !b.disabled
}

// SetFocusable implements the Widget interface.
func (b *Block) SetFocusable(focusable bool) {
	if b.focusable != focusable {
		b.focusable = focusable
	}
}

// Focused implements the Widget interface.
func (b *Block) Focused() bool {
	if window := b.Window(); window != nil {
		return reflect.ValueOf(ui.Widget(b)).Pointer() == reflect.ValueOf(window.Focus()).Pointer()
	}
	return false
}

// Children implements the Widget interface.
func (b *Block) Children() []ui.Widget {
	return b.children
}

// IndexOfChild implements the Widget interface.
func (b *Block) IndexOfChild(child ui.Widget) int {
	for i, one := range b.children {
		if one == child {
			return i
		}
	}
	return -1
}

// AddChild implements the Widget interface.
func (b *Block) AddChild(child ui.Widget) {
	child.RemoveFromParent()
	b.children = append(b.children, child)
	child.SetParent(b)
	b.SetNeedLayout(true)
}

// AddChildAtIndex implements the Widget interface.
func (b *Block) AddChildAtIndex(child ui.Widget, index int) {
	child.RemoveFromParent()
	if index < 0 {
		index = 0
	}
	if index >= len(b.children) {
		b.children = append(b.children, child)
	} else {
		b.children = append(b.children, nil)
		copy(b.children[index+1:], b.children[index:])
		b.children[index] = child
	}
	child.SetParent(b)
	b.SetNeedLayout(true)
}

// RemoveChild implements the Widget interface.
func (b *Block) RemoveChild(child ui.Widget) {
	b.RemoveChildAtIndex(b.IndexOfChild(child))
}

// RemoveChildAtIndex implements the Widget interface.
func (b *Block) RemoveChildAtIndex(index int) {
	if index >= 0 && index < len(b.children) {
		child := b.children[index]
		copy(b.children[index:], b.children[index+1:])
		length := len(b.children) - 1
		b.children[length] = nil
		b.children = b.children[:length]
		b.SetNeedLayout(true)
		child.SetParent(nil)
	}
}

// RemoveFromParent implements the Widget interface.
func (b *Block) RemoveFromParent() {
	if p := b.Parent(); p != nil {
		p.RemoveChild(b)
	}
}

// Parent implements the Widget interface.
func (b *Block) Parent() ui.Widget {
	return b.parent
}

// SetParent implements the Widget interface.
func (b *Block) SetParent(parent ui.Widget) {
	b.parent = parent
}

// Window implements the Widget interface.
func (b *Block) Window() ui.Window {
	if b.window != nil {
		return b.window
	}
	if b.parent != nil {
		return b.parent.Window()
	}
	return nil
}

// RootOfWindow implements the Widget interface.
func (b *Block) RootOfWindow() bool {
	return b.window != nil
}

// Bounds implements the Widget interface.
func (b *Block) Bounds() geom.Rect {
	return b.bounds
}

// LocalBounds implements the Widget interface.
func (b *Block) LocalBounds() geom.Rect {
	return b.bounds.CopyAndZeroLocation()
}

// LocalInsetBounds implements the Widget interface.
func (b *Block) LocalInsetBounds() geom.Rect {
	bounds := b.LocalBounds()
	if border := b.Border(); border != nil {
		bounds.Inset(border.Insets())
	}
	return bounds
}

// SetBounds implements the Widget interface.
func (b *Block) SetBounds(bounds geom.Rect) {
	moved := b.bounds.X != bounds.X || b.bounds.Y != bounds.Y
	resized := b.bounds.Width != bounds.Width || b.bounds.Height != bounds.Height
	if moved || resized {
		b.Repaint()
		if moved {
			b.bounds.Point = bounds.Point
		}
		if resized {
			b.bounds.Size = bounds.Size
			b.SetNeedLayout(true)
			event.Dispatch(event.NewResized(b))
		}
		b.Repaint()
	}
}

// Location implements the Widget interface.
func (b *Block) Location() geom.Point {
	return b.bounds.Point
}

// SetLocation implements the Widget interface.
func (b *Block) SetLocation(pt geom.Point) {
	if b.bounds.Point != pt {
		b.Repaint()
		b.bounds.Point = pt
		b.Repaint()
	}
}

// Size implements the Widget interface.
func (b *Block) Size() geom.Size {
	return b.bounds.Size
}

// SetSize implements the Widget interface.
func (b *Block) SetSize(size geom.Size) {
	if b.bounds.Size != size {
		b.Repaint()
		b.bounds.Size = size
		b.SetNeedLayout(true)
		event.Dispatch(event.NewResized(b))
		b.Repaint()
	}
}

// WidgetAt implements the Widget interface.
func (b *Block) WidgetAt(pt geom.Point) ui.Widget {
	for _, child := range b.children {
		bounds := child.Bounds()
		if bounds.Contains(pt) {
			pt.Subtract(bounds.Point)
			return child.WidgetAt(pt)
		}
	}
	return b
}

// ToWindow implements the Widget interface.
func (b *Block) ToWindow(pt geom.Point) geom.Point {
	pt.Add(b.bounds.Point)
	parent := b.parent
	for parent != nil {
		pt.Add(parent.Bounds().Point)
		parent = parent.Parent()
	}
	return pt
}

// FromWindow implements the Widget interface.
func (b *Block) FromWindow(pt geom.Point) geom.Point {
	pt.Subtract(b.bounds.Point)
	parent := b.parent
	for parent != nil {
		pt.Subtract(parent.Bounds().Point)
		parent = parent.Parent()
	}
	return pt
}

// Background implements the Widget interface.
func (b *Block) Background() color.Color {
	return b.background
}

// SetBackground implements the Widget interface.
func (b *Block) SetBackground(color color.Color) {
	if color != b.background {
		b.background = color
		b.Repaint()
	}
}

// ScrollIntoView attempts to scroll the block into view.
func (b *Block) ScrollIntoView() {
	// RAW: Implement
}
<|endoftext|>


<commit_before>package main

import (
  "bytes"
  "encoding/json"
  "flag"
  "fmt"
  "io/ioutil"
  "log"
  "net"
  "net/http"
  "os"
  "reflect"
  "regexp"
  "sort"
  "strings"
  "text/template"
  "unicode"

  // TODO: Dammit, Golang, I just want to reflect unexported fields.
  "unsafe"
)

const (
  fallbackService = "http://benizi.com/ip?raw=1"
)

type namedAddr struct {
  name string
  ip   net.IP
  mask net.IPMask
}

func getExternal() []net.Addr {
  res, err := http.Get(fallbackService)
  if err != nil {
    os.Exit(1)
  }
  defer res.Body.Close()

  ip, err := ioutil.ReadAll(res.Body)
  if err != nil {
    os.Exit(1)
  }

  _, network, err := net.ParseCIDR(fmt.Sprintf("%s/32", ip[0:len(ip)-1]))
  if err != nil {
    os.Exit(1)
  }

  return []net.Addr{network}
}

func namedIPs(name string, addrs []net.Addr) []namedAddr {
  named := []namedAddr{}
  for _, addr := range addrs {
    if network, ok := addr.(*net.IPNet); ok {
      named = append(named, namedAddr{
        name: name,
        ip: network.IP,
        mask: network.Mask,
      })
    }
  }
  return named
}

func getExternalNamedAddrs() []namedAddr {
  return namedIPs("external", getExternal())
}

func getInterfaceNamedAddrs() ([]namedAddr, error) {
  namedAddrs := []namedAddr{}
  ifis, err := net.Interfaces()
  if err != nil {
    return nil, err
  }
  for _, ifi := range ifis {
    addrs, err := ifi.Addrs()
    if err != nil {
      continue
    }
    namedAddrs = append(namedAddrs, namedIPs(ifi.Name, addrs)...)
  }
  return namedAddrs, nil
}

type foundAddr struct {
  IP net.IP
  Network string
  preferred bool
  rejected bool
  Loopback bool
  Local bool
  isRfc1918 bool
  V6 bool
  original int
  Name string
  Wireless bool
}

var quiet = false

func (addr foundAddr) MarshalJSON() ([]byte, error) {
  ret := map[string]interface{}{}
  // TODO: workaround for unexported fields
  val := reflect.ValueOf(&addr).Elem()
  t := reflect.TypeOf(addr)
  for i := 0; i < val.NumField(); i++ {
    field := t.Field(i)
    if field.PkgPath != "" && quiet {
      continue
    }
    // TODO: workaround for unexported fields
    fval := val.Field(i)
    ftype := fval.Type()
    obj := reflect.NewAt(ftype, unsafe.Pointer(fval.UnsafeAddr())).Elem()
    key := snakeCase(field.Name)
    ret[key] = obj.Interface()
  }
  return json.Marshal(ret)
}

func snakeCase(s string) string {
  var b bytes.Buffer
  justUp := true
  for _, r := range s {
    upper := unicode.IsUpper(r)
    if upper {
      if !justUp {
        b.WriteRune('_')
      }
      b.WriteRune(unicode.ToLower(r))
    } else {
      b.WriteRune(r)
    }
    justUp = upper
  }
  return b.String()
}

var wirelessCache = make(map[string]bool)

func isWirelessInterface(dev string) bool {
  isWireless, cached := wirelessCache[dev]
  if !cached {
    isWireless = isWirelessInterfaceImpl(dev)
    wirelessCache[dev] = isWireless
  }
  return isWireless
}

// Dumb, Linux-specific detection of whether a network interface is wireless
func isWirelessInterfaceImpl(dev string) bool {
  stat, err := os.Stat(fmt.Sprintf("/sys/class/net/%s/wireless", dev))
  return err == nil && stat.Mode().IsDir()
}

func xor(a, b bool) bool {
  return (a && !b) || (!a && b)
}

type ByAttributes struct {
  addrs []foundAddr
}

func (v ByAttributes) Len() int { return len(v.addrs) }
func (v ByAttributes) Swap(i, j int) { v.addrs[i], v.addrs[j] = v.addrs[j], v.addrs[i] }
func (v ByAttributes) Less(i, j int) bool {
  a := v.addrs[i]
  b := v.addrs[j]
  if a.rejected != b.rejected {
    return !a.rejected
  }
  if a.preferred != b.preferred {
    return a.preferred
  }
  if a.Loopback != b.Loopback {
    return !a.Loopback
  }
  if a.Local != b.Local {
    return !a.Local
  }
  if a.V6 != b.V6 {
    return !a.V6
  }
  if a.isRfc1918 != b.isRfc1918 {
    return !a.isRfc1918
  }
  if a.Wireless != b.Wireless {
    return a.Wireless
  }
  if a.original != b.original {
    return a.original < b.original
  }
  return a.IP.String() < b.IP.String()
}

type netfilter struct {
  label    string
  networks []net.IPNet
}

func newNetfilter(label string) *netfilter {
  return &netfilter{label, nil}
}

func (f *netfilter) addCIDR(cidr string) error {
  _, network, err := net.ParseCIDR(expandCIDR(cidr))
  if err != nil {
    return fmt.Errorf("Failed to parse %s network: %s", f.label, cidr)
  }
  f.networks = append(f.networks, *network)
  return nil
}

func (f *netfilter) contains(ip net.IP) bool {
  for _, network := range f.networks {
    if network.Contains(ip) {
      return true
    }
  }
  return false
}

var (
  partialCIDR = regexp.MustCompile(`^(\d+(?:\.\d+){0,2})(?:/(\d+))?$`)
)

// expandCIDR takes a partial CIDR string and expands it to the correct IPv4
// length.
//
// Examples:
//   expandCIDR("10") == "10.0.0.0/8"
//   expandCIDR("192.168") == "192.168.0.0/16"
//   expandCIDR("172.16/12") == "172.16.0.0/12"
//
func expandCIDR(cidr string) string {
  parts := partialCIDR.FindStringSubmatch(cidr)
  if len(parts) == 0 {
    return cidr
  }
  octets := strings.Split(parts[1], ".")
  for len(octets) < 4 {
    octets = append(octets, "0")
  }
  class := parts[2]
  if class == "" {
    n := 32
    for i := 3; i >= 0; i-- {
      if octets[i] != "0" {
        break
      }
      n -= 8
    }
    class = fmt.Sprintf("%d", n)
  }
  return fmt.Sprintf("%s/%s", strings.Join(octets, "."), class)
}

func main() {
  print4 := false
  print6 := false
  external := false
  iface := false
  docker := "172.16.0.0/12"
  findAll := false
  printName := false
  printMask := false
  skipDocker := false
  skipPrivate := false
  keepLoop := false
  keepLocal := false
  keepRejected := false
  keepAll := false
  onlyIfs := ""
  format := ""
  raw := false
  asJson := false

  flag.BoolVar(&print4, "4", print4, "Print IPv4")
  flag.BoolVar(&print6, "6", print6, "Print IPv6")
  flag.BoolVar(&external, "x", external, "Fetch external address")
  flag.BoolVar(&iface, "i", iface, "Fetch addresses per interface")
  flag.BoolVar(&skipDocker, "nodocker", skipDocker, "Omit Docker networks")
  flag.StringVar(&docker, "dockernet", docker, "Docker network to exclude")
  flag.BoolVar(&printName, "name", printName, "Print interface name")
  flag.BoolVar(&printName, "n", printName, "Print interface name (alias)")
  flag.BoolVar(&printMask, "cidr", printMask, "Print address in CIDR notation")
  flag.BoolVar(&printMask, "mask", printMask,
    "Print address in CIDR notation (alias)")
  flag.BoolVar(&findAll, "all", findAll, "Keep going after first match")
  flag.BoolVar(&findAll, "a", findAll, "Keep going after first match (alias)")
  flag.BoolVar(&skipPrivate, "nopriv", skipPrivate, "Omit RFC1918 addresses")
  flag.BoolVar(&keepLoop, "l", keepLoop,
    "Print loopback addresses (don't omit, just penalize)")
  flag.BoolVar(&keepLocal, "ll", keepLocal,
    "Print link-local addresses (don't omit, just penalize)")
  flag.BoolVar(&keepRejected, "rejected", keepRejected,
    "Print rejected addresses (don't omit, just penalize)")
  flag.BoolVar(&keepAll, "d", keepAll,
    "Print all addresses (overrides all but -4/-6; uses others to sort)")
  flag.StringVar(&format, "fmt", format, "Output template")
  flag.BoolVar(&raw, "raw", raw, "Accept template string as-is (no newline)")
  flag.BoolVar(&asJson, "json", asJson,
    "Output as JSON objects (same as -fmt='{{json .}}')")
  flag.BoolVar(&asJson, "j", asJson, "Output as JSON objects (alias)")
  flag.BoolVar(&quiet, "q", quiet,
    "Quiet output (currently: don't add unexported fields to JSON)")
  flag.StringVar(&onlyIfs, "ifs", onlyIfs,
    "Only show IPs on these interface names (comma-separated)")
  flag.Parse()

  if !external && !iface {
    iface = true
  }

  preferNets := newNetfilter("preferred")
  rfc1918 := newNetfilter("RFC 1918")
  dockerNets := newNetfilter("Docker")
  rejectNets := newNetfilter("rejected")

  var parseErrors []error

  addNet := func(filter *netfilter, cidr string) {
    err := filter.addCIDR(cidr)
    if err != nil {
      parseErrors = append(parseErrors, err)
    }
  }

  for _, cidr := range []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"} {
    addNet(rfc1918, cidr)
  }

  if skipDocker {
    addNet(dockerNets, docker)
  }

  filterInterfaceNames := false
  okInterface := map[string]bool{}
  addInterface := func(ifi string) {
    filterInterfaceNames = true
    okInterface[ifi] = true
  }

  if onlyIfs != "" {
    for _, ifi := range strings.Split(onlyIfs, ",") {
      addInterface(ifi)
    }
  }

  for _, arg := range flag.Args() {
    if arg == "" {
      continue
    }
    if arg[0] == '%' {
      addInterface(arg[1:])
    } else if arg[0] == '!' || arg[0] == 'x' {
      addNet(rejectNets, arg[1:len(arg)])
    } else {
      addNet(preferNets, arg)
    }
  }

  if len(parseErrors) > 0 {
    for _, e := range parseErrors {
      log.Println(e)
    }
    log.Fatalln("Exiting: couldn't parse all network arguments.")
  }

  found := make([]foundAddr, 0)

  var namedAddrs []namedAddr
  if external {
    namedAddrs = getExternalNamedAddrs()
  }
  if iface {
    addrs, err := getInterfaceNamedAddrs()
    if err != nil {
      log.Fatalln(err)
    }
    namedAddrs = append(namedAddrs, addrs...)
  }

  for _, addr := range namedAddrs {
    ip := addr.ip
    v6 := ip.To4() == nil
    isPreferred := preferNets.contains(ip)
    isDocker := dockerNets.contains(ip)
    isPrivate := rfc1918.contains(ip)
    isRejected := rejectNets.contains(ip)
    isLoop := ip.IsLoopback()
    isLocal := ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast()

    if xor(print4, print6) && xor(print6, v6) {
      continue
    }
    if filterInterfaceNames && !okInterface[addr.name] {
      continue
    }
    if !keepAll {
      if isPrivate && skipPrivate {
        continue
      }
      if isDocker && skipDocker {
        continue
      }
      if isRejected && !keepRejected {
        continue
      }
      if isLoop && !keepLoop {
        continue
      }
      if isLocal && !keepLocal {
        continue
      }
    }
    network := net.IPNet{IP: ip, Mask: addr.mask}
    found = append(found, foundAddr{
      IP: ip,
      Network: network.String(),
      preferred: isPreferred,
      rejected: isRejected,
      isRfc1918: isPrivate,
      Loopback: isLoop,
      Local: isLocal,
      V6: v6,
      original: len(found),
      Name: addr.name,
      Wireless: isWirelessInterface(addr.name),
    })
  }

  if len(found) == 0 {
    os.Exit(1)
  }

  sort.Sort(ByAttributes{found})

  if format == "" && asJson {
    format = "{{json .}}"
  }
  if format == "" {
    if printName {
      format = "{{.Name}}\t"
    }
    if printMask {
      format += "{{.Network}}"
    } else {
      format += "{{.IP}}"
    }
  }
  if !raw {
    format += "\n"
  }

  toJson := func(v interface{}) string {
    b, e := json.Marshal(v)
    if e != nil {
      return e.Error()
    }
    return string(b)
  }

  tmpl, err := template.New("line").Funcs(template.FuncMap{
    "json": toJson,
  }).Parse(format)
  if err != nil {
    log.Fatal("Error in template: ", err)
  }

  for _, addr := range found {
    err := tmpl.Execute(os.Stdout, addr)
    if err != nil {
      log.Fatal(err)
    }
    if !findAll {
      break
    }
  }
}
<commit_msg>Add `myip -A` = `-a` + `-n`<commit_after>package main

import (
  "bytes"
  "encoding/json"
  "flag"
  "fmt"
  "io/ioutil"
  "log"
  "net"
  "net/http"
  "os"
  "reflect"
  "regexp"
  "sort"
  "strings"
  "text/template"
  "unicode"

  // TODO: Dammit, Golang, I just want to reflect unexported fields.
  "unsafe"
)

const (
  fallbackService = "http://benizi.com/ip?raw=1"
)

type namedAddr struct {
  name string
  ip   net.IP
  mask net.IPMask
}

func getExternal() []net.Addr {
  res, err := http.Get(fallbackService)
  if err != nil {
    os.Exit(1)
  }
  defer res.Body.Close()

  ip, err := ioutil.ReadAll(res.Body)
  if err != nil {
    os.Exit(1)
  }

  _, network, err := net.ParseCIDR(fmt.Sprintf("%s/32", ip[0:len(ip)-1]))
  if err != nil {
    os.Exit(1)
  }

  return []net.Addr{network}
}

func namedIPs(name string, addrs []net.Addr) []namedAddr {
  named := []namedAddr{}
  for _, addr := range addrs {
    if network, ok := addr.(*net.IPNet); ok {
      named = append(named, namedAddr{
        name: name,
        ip: network.IP,
        mask: network.Mask,
      })
    }
  }
  return named
}

func getExternalNamedAddrs() []namedAddr {
  return namedIPs("external", getExternal())
}

func getInterfaceNamedAddrs() ([]namedAddr, error) {
  namedAddrs := []namedAddr{}
  ifis, err := net.Interfaces()
  if err != nil {
    return nil, err
  }
  for _, ifi := range ifis {
    addrs, err := ifi.Addrs()
    if err != nil {
      continue
    }
    namedAddrs = append(namedAddrs, namedIPs(ifi.Name, addrs)...)
  }
  return namedAddrs, nil
}

type foundAddr struct {
  IP net.IP
  Network string
  preferred bool
  rejected bool
  Loopback bool
  Local bool
  isRfc1918 bool
  V6 bool
  original int
  Name string
  Wireless bool
}

var quiet = false

func (addr foundAddr) MarshalJSON() ([]byte, error) {
  ret := map[string]interface{}{}
  // TODO: workaround for unexported fields
  val := reflect.ValueOf(&addr).Elem()
  t := reflect.TypeOf(addr)
  for i := 0; i < val.NumField(); i++ {
    field := t.Field(i)
    if field.PkgPath != "" && quiet {
      continue
    }
    // TODO: workaround for unexported fields
    fval := val.Field(i)
    ftype := fval.Type()
    obj := reflect.NewAt(ftype, unsafe.Pointer(fval.UnsafeAddr())).Elem()
    key := snakeCase(field.Name)
    ret[key] = obj.Interface()
  }
  return json.Marshal(ret)
}

func snakeCase(s string) string {
  var b bytes.Buffer
  justUp := true
  for _, r := range s {
    upper := unicode.IsUpper(r)
    if upper {
      if !justUp {
        b.WriteRune('_')
      }
      b.WriteRune(unicode.ToLower(r))
    } else {
      b.WriteRune(r)
    }
    justUp = upper
  }
  return b.String()
}

var wirelessCache = make(map[string]bool)

func isWirelessInterface(dev string) bool {
  isWireless, cached := wirelessCache[dev]
  if !cached {
    isWireless = isWirelessInterfaceImpl(dev)
    wirelessCache[dev] = isWireless
  }
  return isWireless
}

// Dumb, Linux-specific detection of whether a network interface is wireless
func isWirelessInterfaceImpl(dev string) bool {
  stat, err := os.Stat(fmt.Sprintf("/sys/class/net/%s/wireless", dev))
  return err == nil && stat.Mode().IsDir()
}

func xor(a, b bool) bool {
  return (a && !b) || (!a && b)
}

type ByAttributes struct {
  addrs []foundAddr
}

func (v ByAttributes) Len() int { return len(v.addrs) }
func (v ByAttributes) Swap(i, j int) { v.addrs[i], v.addrs[j] = v.addrs[j], v.addrs[i] }
func (v ByAttributes) Less(i, j int) bool {
  a := v.addrs[i]
  b := v.addrs[j]
  if a.rejected != b.rejected {
    return !a.rejected
  }
  if a.preferred != b.preferred {
    return a.preferred
  }
  if a.Loopback != b.Loopback {
    return !a.Loopback
  }
  if a.Local != b.Local {
    return !a.Local
  }
  if a.V6 != b.V6 {
    return !a.V6
  }
  if a.isRfc1918 != b.isRfc1918 {
    return !a.isRfc1918
  }
  if a.Wireless != b.Wireless {
    return a.Wireless
  }
  if a.original != b.original {
    return a.original < b.original
  }
  return a.IP.String() < b.IP.String()
}

type netfilter struct {
  label    string
  networks []net.IPNet
}

func newNetfilter(label string) *netfilter {
  return &netfilter{label, nil}
}

func (f *netfilter) addCIDR(cidr string) error {
  _, network, err := net.ParseCIDR(expandCIDR(cidr))
  if err != nil {
    return fmt.Errorf("Failed to parse %s network: %s", f.label, cidr)
  }
  f.networks = append(f.networks, *network)
  return nil
}

func (f *netfilter) contains(ip net.IP) bool {
  for _, network := range f.networks {
    if network.Contains(ip) {
      return true
    }
  }
  return false
}

var (
  partialCIDR = regexp.MustCompile(`^(\d+(?:\.\d+){0,2})(?:/(\d+))?$`)
)

// expandCIDR takes a partial CIDR string and expands it to the correct IPv4
// length.
//
// Examples:
//   expandCIDR("10") == "10.0.0.0/8"
//   expandCIDR("192.168") == "192.168.0.0/16"
//   expandCIDR("172.16/12") == "172.16.0.0/12"
//
func expandCIDR(cidr string) string {
  parts := partialCIDR.FindStringSubmatch(cidr)
  if len(parts) == 0 {
    return cidr
  }
  octets := strings.Split(parts[1], ".")
  for len(octets) < 4 {
    octets = append(octets, "0")
  }
  class := parts[2]
  if class == "" {
    n := 32
    for i := 3; i >= 0; i-- {
      if octets[i] != "0" {
        break
      }
      n -= 8
    }
    class = fmt.Sprintf("%d", n)
  }
  return fmt.Sprintf("%s/%s", strings.Join(octets, "."), class)
}

func main() {
  print4 := false
  print6 := false
  external := false
  iface := false
  docker := "172.16.0.0/12"
  findAll := false
  reallyAll := false
  printName := false
  printMask := false
  skipDocker := false
  skipPrivate := false
  keepLoop := false
  keepLocal := false
  keepRejected := false
  keepAll := false
  onlyIfs := ""
  format := ""
  raw := false
  asJson := false

  flag.BoolVar(&print4, "4", print4, "Print IPv4")
  flag.BoolVar(&print6, "6", print6, "Print IPv6")
  flag.BoolVar(&external, "x", external, "Fetch external address")
  flag.BoolVar(&iface, "i", iface, "Fetch addresses per interface")
  flag.BoolVar(&skipDocker, "nodocker", skipDocker, "Omit Docker networks")
  flag.StringVar(&docker, "dockernet", docker, "Docker network to exclude")
  flag.BoolVar(&printName, "name", printName, "Print interface name")
  flag.BoolVar(&printName, "n", printName, "Print interface name (alias)")
  flag.BoolVar(&printMask, "cidr", printMask, "Print address in CIDR notation")
  flag.BoolVar(&printMask, "mask", printMask,
    "Print address in CIDR notation (alias)")
  flag.BoolVar(&findAll, "all", findAll, "Keep going after first match")
  flag.BoolVar(&findAll, "a", findAll, "Keep going after first match (alias)")
  flag.BoolVar(&skipPrivate, "nopriv", skipPrivate, "Omit RFC1918 addresses")
  flag.BoolVar(&keepLoop, "l", keepLoop,
    "Print loopback addresses (don't omit, just penalize)")
  flag.BoolVar(&keepLocal, "ll", keepLocal,
    "Print link-local addresses (don't omit, just penalize)")
  flag.BoolVar(&keepRejected, "rejected", keepRejected,
    "Print rejected addresses (don't omit, just penalize)")
  flag.BoolVar(&keepAll, "d", keepAll,
    "Print all addresses (overrides all but -4/-6; uses others to sort)")
  flag.BoolVar(&reallyAll, "A", reallyAll, "Alias for -a + -n")
  flag.StringVar(&format, "fmt", format, "Output template")
  flag.BoolVar(&raw, "raw", raw, "Accept template string as-is (no newline)")
  flag.BoolVar(&asJson, "json", asJson,
    "Output as JSON objects (same as -fmt='{{json .}}')")
  flag.BoolVar(&asJson, "j", asJson, "Output as JSON objects (alias)")
  flag.BoolVar(&quiet, "q", quiet,
    "Quiet output (currently: don't add unexported fields to JSON)")
  flag.StringVar(&onlyIfs, "ifs", onlyIfs,
    "Only show IPs on these interface names (comma-separated)")
  flag.Parse()

  switch {
  case !reallyAll:
  case skipDocker, onlyIfs != "":
    log.Println("-A overrides -skip-docker + -ifs")
  }

  if reallyAll {
    findAll = true
    printName = true
  }

  if !external && !iface {
    iface = true
  }

  preferNets := newNetfilter("preferred")
  rfc1918 := newNetfilter("RFC 1918")
  dockerNets := newNetfilter("Docker")
  rejectNets := newNetfilter("rejected")

  var parseErrors []error

  addNet := func(filter *netfilter, cidr string) {
    err := filter.addCIDR(cidr)
    if err != nil {
      parseErrors = append(parseErrors, err)
    }
  }

  for _, cidr := range []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"} {
    addNet(rfc1918, cidr)
  }

  if skipDocker {
    addNet(dockerNets, docker)
  }

  filterInterfaceNames := false
  okInterface := map[string]bool{}
  addInterface := func(ifi string) {
    filterInterfaceNames = true
    okInterface[ifi] = true
  }

  if onlyIfs != "" {
    for _, ifi := range strings.Split(onlyIfs, ",") {
      addInterface(ifi)
    }
  }

  for _, arg := range flag.Args() {
    if arg == "" {
      continue
    }
    if arg[0] == '%' {
      addInterface(arg[1:])
    } else if arg[0] == '!' || arg[0] == 'x' {
      addNet(rejectNets, arg[1:len(arg)])
    } else {
      addNet(preferNets, arg)
    }
  }

  if len(parseErrors) > 0 {
    for _, e := range parseErrors {
      log.Println(e)
    }
    log.Fatalln("Exiting: couldn't parse all network arguments.")
  }

  found := make([]foundAddr, 0)

  var namedAddrs []namedAddr
  if external {
    namedAddrs = getExternalNamedAddrs()
  }
  if iface {
    addrs, err := getInterfaceNamedAddrs()
    if err != nil {
      log.Fatalln(err)
    }
    namedAddrs = append(namedAddrs, addrs...)
  }

  for _, addr := range namedAddrs {
    ip := addr.ip
    v6 := ip.To4() == nil
    isPreferred := preferNets.contains(ip)
    isDocker := dockerNets.contains(ip)
    isPrivate := rfc1918.contains(ip)
    isRejected := rejectNets.contains(ip)
    isLoop := ip.IsLoopback()
    isLocal := ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast()

    if xor(print4, print6) && xor(print6, v6) {
      continue
    }
    if filterInterfaceNames && !okInterface[addr.name] {
      continue
    }
    if !keepAll {
      if isPrivate && skipPrivate {
        continue
      }
      if isDocker && skipDocker {
        continue
      }
      if isRejected && !keepRejected {
        continue
      }
      if isLoop && !keepLoop {
        continue
      }
      if isLocal && !keepLocal {
        continue
      }
    }
    network := net.IPNet{IP: ip, Mask: addr.mask}
    found = append(found, foundAddr{
      IP: ip,
      Network: network.String(),
      preferred: isPreferred,
      rejected: isRejected,
      isRfc1918: isPrivate,
      Loopback: isLoop,
      Local: isLocal,
      V6: v6,
      original: len(found),
      Name: addr.name,
      Wireless: isWirelessInterface(addr.name),
    })
  }

  if len(found) == 0 {
    os.Exit(1)
  }

  sort.Sort(ByAttributes{found})

  if format == "" && asJson {
    format = "{{json .}}"
  }
  if format == "" {
    if printName {
      format = "{{.Name}}\t"
    }
    if printMask {
      format += "{{.Network}}"
    } else {
      format += "{{.IP}}"
    }
  }
  if !raw {
    format += "\n"
  }

  toJson := func(v interface{}) string {
    b, e := json.Marshal(v)
    if e != nil {
      return e.Error()
    }
    return string(b)
  }

  tmpl, err := template.New("line").Funcs(template.FuncMap{
    "json": toJson,
  }).Parse(format)
  if err != nil {
    log.Fatal("Error in template: ", err)
  }

  for _, addr := range found {
    err := tmpl.Execute(os.Stdout, addr)
    if err != nil {
      log.Fatal(err)
    }
    if !findAll {
      break
    }
  }
}
<|endoftext|>


<commit_before>// Find local, non-loopback IP address
//
// Skips docker bridge address and IPv6 addresses, unless it can't find another
package main

import (
  "flag"
  "fmt"
  "io/ioutil"
  "log"
  "net"
  "net/http"
  "os"
  "sort"
  "strings"
)

const (
  fallbackService = "http://benizi.com/ip?raw=1"
)

func getAddresses(external bool) (addrs []net.Addr) {
  if external {
    addrs = getExternal()
  } else {
    got, err := net.InterfaceAddrs()
    if err != nil {
      os.Exit(1)
    }
    addrs = got
  }
  return
}

func getExternal() []net.Addr {
  res, err := http.Get(fallbackService)
  if err != nil {
    os.Exit(1)
  }
  defer res.Body.Close()

  ip, err := ioutil.ReadAll(res.Body)
  if err != nil {
    os.Exit(1)
  }

  _, network, err := net.ParseCIDR(fmt.Sprintf("%s/32", ip[0:len(ip)-1]))
  if err != nil {
    os.Exit(1)
  }

  return []net.Addr{network}
}

func succeed(addr string) {
  fmt.Println(addr)
  os.Exit(0)
}

type foundAddr struct {
  ip net.IP
  preferred bool
  rejected bool
  loopback bool
  v6 bool
}

func xor(a, b bool) bool {
  return (a && !b) || (!a && b)
}

type ByAttributes struct {
  addrs []foundAddr
  prefer6 bool
}

func (v ByAttributes) Len() int { return len(v.addrs) }
func (v ByAttributes) Swap(i, j int) { v.addrs[i], v.addrs[j] = v.addrs[j], v.addrs[i] }
func (v ByAttributes) Less(i, j int) bool {
  a := v.addrs[i]
  b := v.addrs[j]
  if xor(a.v6, b.v6) {
    return !xor(a.v6, v.prefer6)
  }
  if xor(a.preferred, b.preferred) {
    return a.preferred
  }
  if xor(a.rejected, b.rejected) {
    return !a.rejected
  }
  if xor(a.loopback, b.loopback) {
    return !a.loopback
  }
  return a.ip.String() < b.ip.String()
}

func anyContains(networks []net.IPNet, ip net.IP) bool {
  for _, network := range networks {
    if network.Contains(ip) {
      return true
    }
  }
  return false
}

func parseNetwork(cidr string) (*net.IPNet, error) {
  _, network, err := net.ParseCIDR(cidr)
  if err == nil {
    return network, nil
  }

  // Try parsing it as an octet or octets
  // e.g. "10" => "10.0.0.0/8", "192.168" => "192.168.0.0/16"
  dots := strings.Count(cidr, ".")
  needed := 3 - dots
  if needed < 0 {
    return nil, err
  }
  cidr = fmt.Sprintf("%s%s/%d", cidr, strings.Repeat(".0", needed), 32 - 8 * needed)
  _, network, e := net.ParseCIDR(cidr)
  if e == nil {
    return network, nil
  }

  // return the original error
  return nil, err
}

func main() {
  only6 := flag.Bool("6", false, "Limit to IPv6")
  external := flag.Bool("x", false, "Fetch external address")
  docker := "172.17.0.0/16"
  flag.Parse()

  _, dockerNet, err := net.ParseCIDR(docker)
  if err != nil {
    log.Fatalln("Failed to parse Docker network", docker)
  }

  var acceptable []net.IPNet
  var rejectable []net.IPNet

  for _, arg := range flag.Args() {
    if len(arg) == 0 {
      continue
    }

    addTo := &acceptable
    if arg[0] == '!' || arg[0] == 'x' {
      addTo = &rejectable
      arg = arg[1:len(arg)]
    }

    network, err := parseNetwork(arg)
    if err != nil {
      log.Fatal(err)
    }

    *addTo = append(*addTo, *network)
  }

  if len(rejectable) == 0 {
    rejectable = append(rejectable, *dockerNet)
  }

  found := make([]foundAddr, 0)

  addrs := getAddresses(*external)
  for _, addr := range addrs {
    network, ok := addr.(*net.IPNet)
    if !ok {
      continue
    }

    found = append(found, foundAddr{
      ip: network.IP,
      preferred: anyContains(acceptable, network.IP),
      rejected: anyContains(rejectable, network.IP),
      loopback: network.IP.IsLoopback(),
      v6: network.IP.To4() == nil,
    })
  }

  sort.Sort(ByAttributes{found, *only6})

  if len(found) > 0 {
    succeed(found[0].ip.String())
  }

  os.Exit(1)
}
<commit_msg>Add `--all` option to `myip`<commit_after>// Find local, non-loopback IP address
//
// Skips docker bridge address and IPv6 addresses, unless it can't find another
package main

import (
  "flag"
  "fmt"
  "io/ioutil"
  "log"
  "net"
  "net/http"
  "os"
  "sort"
  "strings"
)

const (
  fallbackService = "http://benizi.com/ip?raw=1"
)

func getAddresses(external bool) (addrs []net.Addr) {
  if external {
    addrs = getExternal()
  } else {
    got, err := net.InterfaceAddrs()
    if err != nil {
      os.Exit(1)
    }
    addrs = got
  }
  return
}

func getExternal() []net.Addr {
  res, err := http.Get(fallbackService)
  if err != nil {
    os.Exit(1)
  }
  defer res.Body.Close()

  ip, err := ioutil.ReadAll(res.Body)
  if err != nil {
    os.Exit(1)
  }

  _, network, err := net.ParseCIDR(fmt.Sprintf("%s/32", ip[0:len(ip)-1]))
  if err != nil {
    os.Exit(1)
  }

  return []net.Addr{network}
}

type foundAddr struct {
  ip net.IP
  preferred bool
  rejected bool
  loopback bool
  v6 bool
}

func xor(a, b bool) bool {
  return (a && !b) || (!a && b)
}

type ByAttributes struct {
  addrs []foundAddr
  prefer6 bool
}

func (v ByAttributes) Len() int { return len(v.addrs) }
func (v ByAttributes) Swap(i, j int) { v.addrs[i], v.addrs[j] = v.addrs[j], v.addrs[i] }
func (v ByAttributes) Less(i, j int) bool {
  a := v.addrs[i]
  b := v.addrs[j]
  if xor(a.v6, b.v6) {
    return !xor(a.v6, v.prefer6)
  }
  if xor(a.preferred, b.preferred) {
    return a.preferred
  }
  if xor(a.rejected, b.rejected) {
    return !a.rejected
  }
  if xor(a.loopback, b.loopback) {
    return !a.loopback
  }
  return a.ip.String() < b.ip.String()
}

func anyContains(networks []net.IPNet, ip net.IP) bool {
  for _, network := range networks {
    if network.Contains(ip) {
      return true
    }
  }
  return false
}

func parseNetwork(cidr string) (*net.IPNet, error) {
  _, network, err := net.ParseCIDR(cidr)
  if err == nil {
    return network, nil
  }

  // Try parsing it as an octet or octets
  // e.g. "10" => "10.0.0.0/8", "192.168" => "192.168.0.0/16"
  dots := strings.Count(cidr, ".")
  needed := 3 - dots
  if needed < 0 {
    return nil, err
  }
  cidr = fmt.Sprintf("%s%s/%d", cidr, strings.Repeat(".0", needed), 32 - 8 * needed)
  _, network, e := net.ParseCIDR(cidr)
  if e == nil {
    return network, nil
  }

  // return the original error
  return nil, err
}

func main() {
  only6 := false
  external := false
  excludeDocker := true
  docker := "172.17.0.0/16"
  printAll := false
  sorted := false

  flag.BoolVar(&only6, "6", only6, "Limit to IPv6")
  flag.BoolVar(&external, "x", external, "Fetch external address")
  flag.BoolVar(&excludeDocker, "nodocker", excludeDocker, "Exclude Docker interface")
  flag.StringVar(&docker, "dockernet", docker, "Docker network to exclude")
  flag.BoolVar(&printAll, "all", printAll, "Print all addresses")
  flag.BoolVar(&sorted, "sort", sorted, "Sort addresses (only applicable with --all)")
  flag.Parse()

  var acceptable []net.IPNet
  var rejectable []net.IPNet

  if excludeDocker {
    _, dockerNet, err := net.ParseCIDR(docker)
    if err != nil {
      log.Fatalln("Failed to parse Docker network", docker)
    }

    rejectable = append(rejectable, *dockerNet)
  }

  for _, arg := range flag.Args() {
    if len(arg) == 0 {
      continue
    }

    addTo := &acceptable
    if arg[0] == '!' || arg[0] == 'x' {
      addTo = &rejectable
      arg = arg[1:len(arg)]
    }

    network, err := parseNetwork(arg)
    if err != nil {
      log.Fatal(err)
    }

    *addTo = append(*addTo, *network)
  }

  found := make([]foundAddr, 0)

  addrs := getAddresses(external)
  for _, addr := range addrs {
    network, ok := addr.(*net.IPNet)
    if !ok {
      continue
    }

    found = append(found, foundAddr{
      ip: network.IP,
      preferred: anyContains(acceptable, network.IP),
      rejected: anyContains(rejectable, network.IP),
      loopback: network.IP.IsLoopback(),
      v6: network.IP.To4() == nil,
    })
  }

  if len(found) == 0 {
    os.Exit(1)
  }

  if sorted || !printAll {
    sort.Sort(ByAttributes{found, only6})
  }

  for _, addr := range found {
    fmt.Println(addr.ip.String())
    if !printAll {
      break
    }
  }
}
<|endoftext|>


<commit_before>package window

/*
#include <SDL2/SDL.h>

int getKeyState(int key) {
  const Uint8 *state = SDL_GetKeyboardState(NULL);

  if (state[key]){
    return SDL_PRESSED;
  }
  return SDL_RELEASED;
}

char *getClipboard() {
  if (SDL_HasClipboardText() == SDL_TRUE){
    return SDL_GetClipboardText();
  }
  return "";
}

*/
import "C"

var listenerList = map[int]*listener{}

type listener struct {
	callback func(event int)
}

// AddKeyListener creates a new key listener, only the last listener for a button will be honored
//	input.AddKeyListener(input.KeyEscape, func(event int) {
//		if event == input.KeyStateReleased {
//			fmt.Println("Escape button released!")
//		}
//	})
func AddKeyListener(key int, callback func(event int)) {
	listenerList[key] = &listener{callback}
}

// DestroyKeyListener removes listener for a key
func DestroyKeyListener(key int) {
	if _, ok := listenerList[key]; ok {
		listenerList[key].callback = func(event int) {}
	}
}

// GetKeyState will return the event state for a key
func GetKeyState(key int) int {
	return int(C.getKeyState(C.int(key)))
}

// GetClipboard will return text from the clipboard
func GetClipboard() string {
	return C.GoString(C.getClipboard())
}
<commit_msg>Add getting mouse state to window<commit_after>package window

/*
#include <SDL2/SDL.h>

typedef struct MOUSESTATE {
	int X;
	int Y;
	int State;
}mouse;

int getKeyState(int key) {
  const Uint8 *state = SDL_GetKeyboardState(NULL);

  if (state[key]){
    return SDL_PRESSED;
  }
  return SDL_RELEASED;
}

char *getClipboard() {
  if (SDL_HasClipboardText() == SDL_TRUE){
    return SDL_GetClipboardText();
  }
  return "";
}

mouse getMouseState() {
	mouse m;
	m.State = SDL_GetMouseState(&m.X, &m.Y);
	return m;
}

int getMouseY() {
	int y;
	SDL_GetMouseState(NULL, &y);
	return y;
}

*/
import "C"

type mouseState C.struct_MOUSESTATE

var listenerList = map[int]*listener{}

type listener struct {
	callback func(event int)
}

// AddKeyListener creates a new key listener, only the last listener for a button will be honored
//	input.AddKeyListener(input.KeyEscape, func(event int) {
//		if event == input.KeyStateReleased {
//			fmt.Println("Escape button released!")
//		}
//	})
func AddKeyListener(key int, callback func(event int)) {
	listenerList[key] = &listener{callback}
}

// DestroyKeyListener removes listener for a key
func DestroyKeyListener(key int) {
	if _, ok := listenerList[key]; ok {
		listenerList[key].callback = func(event int) {}
	}
}

// GetKeyState will return the event state for a key
func GetKeyState(key int) int {
	return int(C.getKeyState(C.int(key)))
}

// GetMouseState returns the position of the mouse
func GetMouseState() (int, int, int) {
	m := mouseState(C.getMouseState())
	return int(m.X), int(m.Y), int(m.State)
}

// GetClipboard will return text from the clipboard
func GetClipboard() string {
	return C.GoString(C.getClipboard())
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sync"
	"time"
)

type worker interface {
	work(chan interface{}, *sync.WaitGroup)
}

type URLChecker struct {
	timeout int
}

func main() {
	workers := flag.Int("workers", 5, "Number of workers to use.")
	file := flag.String("file", "", "The input file to use. If none is supplied, stdin is assumed.")
	timeout := flag.Int("timeout", 10, "Max number of seconds to wait for a response from the URL.")
	var worker worker
	flag.Parse()

	work := make(chan interface{})

	if *file == "" {
		go feedURLs(work, os.Stdin)
	} else {
		urls, err := os.Open(*file)
		if err != nil {
			log.Fatal("unable to open input file:", err)
		}
		defer urls.Close()
		go feedURLs(work, urls)
	}

	var wg sync.WaitGroup
	worker = URLChecker{timeout: *timeout}

	for i := 0; i < *workers; i++ {
		wg.Add(1)
		go worker.work(work, &wg)
	}

	wg.Wait()
}

func newClient(timeout int) *http.Client {
	return &http.Client{Timeout: time.Second * time.Duration(timeout)}
}

func (w URLChecker) work(urls chan interface{}, wg *sync.WaitGroup) {
	c := newClient(w.timeout)

	for url := range urls {
		start := time.Now()
		resp, err := c.Head(url.(string))
		if err != nil {
			log.Printf("unable to connect to %s: %s", url, err.Error())
			continue
		}

		io.Copy(ioutil.Discard, resp.Body)
		resp.Body.Close()
		fmt.Printf("%s %d %6.3fms\n", url, resp.StatusCode, time.Since(start).Seconds()*1000)
	}
	wg.Done()
}

func feedURLs(work chan interface{}, r io.Reader) {
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		work <- scanner.Text()
	}
	close(work)
}
<commit_msg>renamed a few variables for clarity<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sync"
	"time"
)

type worker interface {
	work(chan interface{}, *sync.WaitGroup)
}

type URLChecker struct {
	timeout int
}

func main() {
	workers := flag.Int("workers", 5, "Number of workers to use.")
	file := flag.String("file", "", "The input file to use. If none is supplied, stdin is assumed.")
	timeout := flag.Int("timeout", 10, "Max number of seconds to wait for a response from the URL.")
	flag.Parse()

	workCh := make(chan interface{})

	if *file == "" {
		go feedURLs(workCh, os.Stdin)
	} else {
		urls, err := os.Open(*file)
		if err != nil {
			log.Fatal("unable to open input file:", err)
		}
		defer urls.Close()
		go feedURLs(workCh, urls)
	}

	var wg sync.WaitGroup
	wrkr := URLChecker{timeout: *timeout}

	for i := 0; i < *workers; i++ {
		wg.Add(1)
		go wrkr.work(workCh, &wg)
	}

	wg.Wait()
}

func newClient(timeout int) *http.Client {
	return &http.Client{Timeout: time.Second * time.Duration(timeout)}
}

func (w URLChecker) work(urls chan interface{}, wg *sync.WaitGroup) {
	c := newClient(w.timeout)

	for url := range urls {
		start := time.Now()
		resp, err := c.Head(url.(string))
		if err != nil {
			log.Printf("unable to connect to %s: %s", url, err.Error())
			continue
		}

		io.Copy(ioutil.Discard, resp.Body)
		resp.Body.Close()
		fmt.Printf("%s %d %6.3fms\n", url, resp.StatusCode, time.Since(start).Seconds()*1000)
	}
	wg.Done()
}

func feedURLs(work chan interface{}, r io.Reader) {
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		work <- scanner.Text()
	}
	close(work)
}
<|endoftext|>