<commit_before>package grabber

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"

	"github.com/lfkeitel/inca/common"
)

func loadCurrentConfigs(conf common.Config) (map[string]string, error) {
	src, err := os.Stat(conf.FullConfDir)
	if err != nil {
		return nil, err
	}
	if !src.IsDir() {
		return nil, errors.New("Path is not a directory")
	}

	fileList, err := ioutil.ReadDir(conf.FullConfDir)
	if err != nil {
		return nil, err
	}

	current := make(map[string]string, len(fileList))

	for _, file := range fileList {
		if file.Name()[0] == '.' {
			continue
		}

		deviceName := strings.SplitN(file.Name(), "-", 2)[0]
		current[deviceName] = filepath.Join(conf.FullConfDir, file.Name())
	}

	return current, nil
}

func loadDeviceList(conf common.Config) ([]host, error) {
	listFile, err := os.Open(conf.DeviceListFile)
	if err != nil {
		return nil, err
	}
	defer listFile.Close()

	scanner := bufio.NewScanner(listFile)
	scanner.Split(bufio.ScanLines)
	var hostList []host
	lineNum := 0

	for scanner.Scan() {
		line := scanner.Text()
		lineNum++

		if len(line) < 1 || line[0] == '#' || line[0] == ' ' {
			continue
		}

		splitLine := strings.Split(line, "::")

		if len(splitLine) != 4 {
			logText := fmt.Sprintf("Error on line %d in device configuration", lineNum)
			appLogger.Warning(logText)
			common.UserLogWarning(logText)
			continue
		}

		device := host{
			name:    splitLine[0],
			address: splitLine[1],
			dtype:   splitLine[2],
			method:  splitLine[3],
		}

		hostList = append(hostList, device)
	}

	return hostList, nil
}

// CheckDeviceList parses string s as if it was a device/type list and checks for errors
func CheckDeviceList(s string) error {
	lines := strings.Split(s, "\n")

	for i, line := range lines {
		if len(line) < 1 || line[0] == '#' || line[0] == ' ' {
			continue
		}

		parsedLine := strings.Split(line, "::")
		if len(parsedLine) != 4 {
			return fmt.Errorf("Error on line %d. Expected 4 fields, got %d.\\n'%s'", i+1, len(parsedLine), lines[i])
		}
	}
	return nil
}

func loadDeviceTypes(conf common.Config) ([]dtype, error) {
	typeFile, err := os.Open(conf.DeviceTypeFile)
	if err != nil {
		return nil, err
	}
	defer typeFile.Close()

	scanner := bufio.NewScanner(typeFile)
	scanner.Split(bufio.ScanLines)
	var dtypeList []dtype
	lineNum := 0

	for scanner.Scan() {
		line := scanner.Text()
		lineNum++

		if len(line) < 1 || line[0] == '#' || line[0] == ' ' {
			continue
		}

		splitLine := strings.Split(line, "::")

		if len(splitLine) != 4 {
			logText := fmt.Sprintf("Error on line %d in device type configuration", lineNum)
			appLogger.Warning(logText)
			common.UserLogWarning(logText)
			continue
		}

		typedef := dtype{
			deviceType: splitLine[0],
			method:     splitLine[1],
			scriptfile: splitLine[2],
			args:       splitLine[3],
		}

		dtypeList = append(dtypeList, typedef)
	}

	return dtypeList, nil
}

func grabConfigs(hosts []host, dtypes []dtype, dateSuffix string, conf common.Config, existing map[string]string) error {
	var wg sync.WaitGroup
	ccg := newConnGroup(conf) // Used to enforce a maximum number of connections

	for _, host := range hosts {
		host := host
		match := false
		for _, dtype := range dtypes {
			if host.dtype == dtype.deviceType && (dtype.method == "*" || host.method == dtype.method) {
				fname := getConfigFileName(host, dateSuffix, conf)
				args := getArguments(dtype.args, host, fname, conf)

				wg.Add(1)
				ccg.add(1)
				go func() {
					defer func() {
						wg.Done()
						ccg.done()
					}()
					if err := scriptExecute(dtype.scriptfile, args); err != nil {
						common.UserLogError("Failed getting config from %s", host.name)
						os.Remove(fname)
						return
					}

					if oldName, exists := existing[host.name]; exists && oldName != fname {
						os.Remove(oldName)
					}
				}()
				match = true
				break
			}
		}

		if !match {
			logText := fmt.Sprintf("Device type '%s' using method '%s' wasn't found.", host.dtype, host.method)
			appLogger.Warning(logText)
			common.UserLogWarning(logText)
			finishedDevices++
		}
		ccg.wait()
	}

	wg.Wait()
	return nil
}

func getConfigFileName(host host, dateSuffix string, conf common.Config) string {
	var filename bytes.Buffer

	filename.WriteString(conf.FullConfDir)
	filename.WriteString("/")
	filename.WriteString(host.name)
	filename.WriteString("-")
	filename.WriteString(dateSuffix)
	filename.WriteString("-")
	filename.WriteString(host.address)
	filename.WriteString("-")
	filename.WriteString(host.dtype)
	filename.WriteString("-")
	filename.WriteString(host.method)
	filename.WriteString(".conf")

	touch(conf.FullConfDir + "/" + filename.String())

	return filename.String()
}

func getArguments(argStr string, host host, filename string, conf common.Config) []string {
	args := strings.Split(argStr, ",")
	argList := make([]string, len(args))
	for i, a := range args {
		switch a {
		case "$address":
			argList[i] = host.address
			break
		case "$username":
			argList[i] = conf.RemoteUsername
			break
		case "$password":
			argList[i] = conf.RemotePassword
			break
		case "$logfile":
			argList[i] = filename
			break
		case "$enablepw":
			argList[i] = conf.EnablePassword
			break
		}
	}
	return argList
}

func scriptExecute(sfn string, args []string) error {
	out, err := exec.Command("scripts/"+sfn, args...).Output()
	if err != nil {
		appLogger.Error(err.Error())
		appLogger.Error(string(out))
		return err
	}
	return nil
}
<commit_msg>Log both device name and address<commit_after>package grabber

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"

	"github.com/lfkeitel/inca/common"
)

func loadCurrentConfigs(conf common.Config) (map[string]string, error) {
	src, err := os.Stat(conf.FullConfDir)
	if err != nil {
		return nil, err
	}
	if !src.IsDir() {
		return nil, errors.New("Path is not a directory")
	}

	fileList, err := ioutil.ReadDir(conf.FullConfDir)
	if err != nil {
		return nil, err
	}

	current := make(map[string]string, len(fileList))

	for _, file := range fileList {
		if file.Name()[0] == '.' {
			continue
		}

		deviceName := strings.SplitN(file.Name(), "-", 2)[0]
		current[deviceName] = filepath.Join(conf.FullConfDir, file.Name())
	}

	return current, nil
}

func loadDeviceList(conf common.Config) ([]host, error) {
	listFile, err := os.Open(conf.DeviceListFile)
	if err != nil {
		return nil, err
	}
	defer listFile.Close()

	scanner := bufio.NewScanner(listFile)
	scanner.Split(bufio.ScanLines)
	var hostList []host
	lineNum := 0

	for scanner.Scan() {
		line := scanner.Text()
		lineNum++

		if len(line) < 1 || line[0] == '#' || line[0] == ' ' {
			continue
		}

		splitLine := strings.Split(line, "::")

		if len(splitLine) != 4 {
			logText := fmt.Sprintf("Error on line %d in device configuration", lineNum)
			appLogger.Warning(logText)
			common.UserLogWarning(logText)
			continue
		}

		device := host{
			name:    splitLine[0],
			address: splitLine[1],
			dtype:   splitLine[2],
			method:  splitLine[3],
		}

		hostList = append(hostList, device)
	}

	return hostList, nil
}

// CheckDeviceList parses string s as if it was a device/type list and checks for errors
func CheckDeviceList(s string) error {
	lines := strings.Split(s, "\n")

	for i, line := range lines {
		if len(line) < 1 || line[0] == '#' || line[0] == ' ' {
			continue
		}

		parsedLine := strings.Split(line, "::")
		if len(parsedLine) != 4 {
			return fmt.Errorf("Error on line %d. Expected 4 fields, got %d.\\n'%s'", i+1, len(parsedLine), lines[i])
		}
	}
	return nil
}

func loadDeviceTypes(conf common.Config) ([]dtype, error) {
	typeFile, err := os.Open(conf.DeviceTypeFile)
	if err != nil {
		return nil, err
	}
	defer typeFile.Close()

	scanner := bufio.NewScanner(typeFile)
	scanner.Split(bufio.ScanLines)
	var dtypeList []dtype
	lineNum := 0

	for scanner.Scan() {
		line := scanner.Text()
		lineNum++

		if len(line) < 1 || line[0] == '#' || line[0] == ' ' {
			continue
		}

		splitLine := strings.Split(line, "::")

		if len(splitLine) != 4 {
			logText := fmt.Sprintf("Error on line %d in device type configuration", lineNum)
			appLogger.Warning(logText)
			common.UserLogWarning(logText)
			continue
		}

		typedef := dtype{
			deviceType: splitLine[0],
			method:     splitLine[1],
			scriptfile: splitLine[2],
			args:       splitLine[3],
		}

		dtypeList = append(dtypeList, typedef)
	}

	return dtypeList, nil
}

func grabConfigs(hosts []host, dtypes []dtype, dateSuffix string, conf common.Config, existing map[string]string) error {
	var wg sync.WaitGroup
	ccg := newConnGroup(conf) // Used to enforce a maximum number of connections

	for _, host := range hosts {
		host := host
		match := false
		for _, dtype := range dtypes {
			if host.dtype == dtype.deviceType && (dtype.method == "*" || host.method == dtype.method) {
				fname := getConfigFileName(host, dateSuffix, conf)
				args := getArguments(dtype.args, host, fname, conf)

				wg.Add(1)
				ccg.add(1)
				go func() {
					defer func() {
						wg.Done()
						ccg.done()
					}()
					if err := scriptExecute(dtype.scriptfile, args); err != nil {
						common.UserLogError("Failed getting config from %s (%s)", host.name, host.address)
						os.Remove(fname)
						return
					}

					if oldName, exists := existing[host.name]; exists && oldName != fname {
						os.Remove(oldName)
					}
				}()
				match = true
				break
			}
		}

		if !match {
			logText := fmt.Sprintf("Device type '%s' using method '%s' wasn't found.", host.dtype, host.method)
			appLogger.Warning(logText)
			common.UserLogWarning(logText)
			finishedDevices++
		}
		ccg.wait()
	}

	wg.Wait()
	return nil
}

func getConfigFileName(host host, dateSuffix string, conf common.Config) string {
	var filename bytes.Buffer

	filename.WriteString(conf.FullConfDir)
	filename.WriteString("/")
	filename.WriteString(host.name)
	filename.WriteString("-")
	filename.WriteString(dateSuffix)
	filename.WriteString("-")
	filename.WriteString(host.address)
	filename.WriteString("-")
	filename.WriteString(host.dtype)
	filename.WriteString("-")
	filename.WriteString(host.method)
	filename.WriteString(".conf")

	touch(conf.FullConfDir + "/" + filename.String())

	return filename.String()
}

func getArguments(argStr string, host host, filename string, conf common.Config) []string {
	args := strings.Split(argStr, ",")
	argList := make([]string, len(args))
	for i, a := range args {
		switch a {
		case "$address":
			argList[i] = host.address
			break
		case "$username":
			argList[i] = conf.RemoteUsername
			break
		case "$password":
			argList[i] = conf.RemotePassword
			break
		case "$logfile":
			argList[i] = filename
			break
		case "$enablepw":
			argList[i] = conf.EnablePassword
			break
		}
	}
	return argList
}

func scriptExecute(sfn string, args []string) error {
	out, err := exec.Command("scripts/"+sfn, args...).Output()
	if err != nil {
		appLogger.Error(err.Error())
		appLogger.Error(string(out))
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Hajime Hoshi

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

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

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

package ebiten

import (
	"github.com/go-gl/gl"
	"github.com/hajimehoshi/ebiten/internal/opengl"
)

func newGraphicsContext(screenWidth, screenHeight, screenScale int) (*graphicsContext, error) {
	gl.Init()
	gl.Enable(gl.TEXTURE_2D)
	gl.Enable(gl.BLEND)

	c := &graphicsContext{
		screenWidth:  screenWidth,
		screenHeight: screenHeight,
		screenScale:  screenScale,
	}

	// The defualt framebuffer should be 0.
	r := opengl.NewRenderTarget(screenWidth*screenScale, screenHeight*screenScale, true)
	c.defaultID = idsInstance.addRenderTarget(r)

	var err error
	c.screenID, err = idsInstance.createRenderTarget(screenWidth, screenHeight, gl.NEAREST)
	if err != nil {
		return nil, err
	}

	// TODO: This is a special stack only for clearing. Can we change this?
	c.currentIDs = []RenderTargetID{c.screenID}
	c.Clear()

	return c, nil
}

type graphicsContext struct {
	screenID     RenderTargetID
	defaultID    RenderTargetID
	currentIDs   []RenderTargetID
	screenWidth  int
	screenHeight int
	screenScale  int
}

var _ GraphicsContext = new(graphicsContext)

func (c *graphicsContext) dispose() {
	// NOTE: Now this method is not used anywhere.
	idsInstance.deleteRenderTarget(c.screenID)
}

func (c *graphicsContext) Clear() {
	c.Fill(0, 0, 0)
}

func (c *graphicsContext) Fill(r, g, b uint8) {
	idsInstance.fillRenderTarget(c.currentIDs[len(c.currentIDs)-1], r, g, b)
}

func (c *graphicsContext) Texture(id TextureID) Drawer {
	return &textureWithContext{id, c}
}

func (c *graphicsContext) RenderTarget(id RenderTargetID) Drawer {
	return &textureWithContext{idsInstance.toTexture(id), c}
}

func (c *graphicsContext) PushRenderTarget(renderTargetID RenderTargetID) {
	c.currentIDs = append(c.currentIDs, renderTargetID)
}

func (c *graphicsContext) PopRenderTarget() {
	c.currentIDs = c.currentIDs[:len(c.currentIDs)-1]
}

func (c *graphicsContext) preUpdate() {
	c.currentIDs = []RenderTargetID{c.defaultID}
	c.PushRenderTarget(c.screenID)
	c.Clear()
}

func (c *graphicsContext) postUpdate() {
	c.PopRenderTarget()
	c.Clear()

	scale := float64(c.screenScale)
	geo := GeometryMatrixI()
	geo.Scale(scale, scale)
	DrawWhole(c.RenderTarget(c.screenID), c.screenWidth, c.screenHeight, geo, ColorMatrixI())

	gl.Flush()
}

type textureWithContext struct {
	id      TextureID
	context *graphicsContext
}

func (t *textureWithContext) Draw(parts []TexturePart, geo GeometryMatrix, color ColorMatrix) {
	currentID := t.context.currentIDs[len(t.context.currentIDs)-1]
	idsInstance.drawTexture(currentID, t.id, parts, geo, color)
}
<commit_msg>Refactoring<commit_after>/*
Copyright 2014 Hajime Hoshi

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

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

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

package ebiten

import (
	"github.com/go-gl/gl"
	"github.com/hajimehoshi/ebiten/internal/opengl"
)

func newGraphicsContext(screenWidth, screenHeight, screenScale int) (*graphicsContext, error) {
	gl.Init()
	gl.Enable(gl.TEXTURE_2D)
	gl.Enable(gl.BLEND)

	// The defualt framebuffer should be 0.
	r := opengl.NewRenderTarget(screenWidth*screenScale, screenHeight*screenScale, true)

	screenID, err := idsInstance.createRenderTarget(screenWidth, screenHeight, gl.NEAREST)
	if err != nil {
		return nil, err
	}

	c := &graphicsContext{
		currentIDs:   make([]RenderTargetID, 1),
		defaultID:    idsInstance.addRenderTarget(r),
		screenID:     screenID,
		screenWidth:  screenWidth,
		screenHeight: screenHeight,
		screenScale:  screenScale,
	}

	idsInstance.fillRenderTarget(c.screenID, 0, 0, 0)

	return c, nil
}

type graphicsContext struct {
	screenID     RenderTargetID
	defaultID    RenderTargetID
	currentIDs   []RenderTargetID
	screenWidth  int
	screenHeight int
	screenScale  int
}

var _ GraphicsContext = new(graphicsContext)

func (c *graphicsContext) dispose() {
	// NOTE: Now this method is not used anywhere.
	idsInstance.deleteRenderTarget(c.screenID)
}

func (c *graphicsContext) Clear() {
	c.Fill(0, 0, 0)
}

func (c *graphicsContext) Fill(r, g, b uint8) {
	idsInstance.fillRenderTarget(c.currentIDs[len(c.currentIDs)-1], r, g, b)
}

func (c *graphicsContext) Texture(id TextureID) Drawer {
	return &textureWithContext{id, c}
}

func (c *graphicsContext) RenderTarget(id RenderTargetID) Drawer {
	return &textureWithContext{idsInstance.toTexture(id), c}
}

func (c *graphicsContext) PushRenderTarget(renderTargetID RenderTargetID) {
	c.currentIDs = append(c.currentIDs, renderTargetID)
}

func (c *graphicsContext) PopRenderTarget() {
	c.currentIDs = c.currentIDs[:len(c.currentIDs)-1]
}

func (c *graphicsContext) preUpdate() {
	c.currentIDs = c.currentIDs[0:1]
	c.currentIDs[0] = c.defaultID
	c.PushRenderTarget(c.screenID)
	c.Clear()
}

func (c *graphicsContext) postUpdate() {
	c.PopRenderTarget()
	c.Clear()

	scale := float64(c.screenScale)
	geo := GeometryMatrixI()
	geo.Scale(scale, scale)
	DrawWhole(c.RenderTarget(c.screenID), c.screenWidth, c.screenHeight, geo, ColorMatrixI())

	gl.Flush()
}

type textureWithContext struct {
	id      TextureID
	context *graphicsContext
}

func (t *textureWithContext) Draw(parts []TexturePart, geo GeometryMatrix, color ColorMatrix) {
	currentID := t.context.currentIDs[len(t.context.currentIDs)-1]
	idsInstance.drawTexture(currentID, t.id, parts, geo, color)
}
<|endoftext|>


<commit_before>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package manager

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

	"github.com/webx-top/com"
	"github.com/webx-top/echo"
	"github.com/webx-top/echo/middleware/tplfunc"
	"github.com/webx-top/echo/param"

	"github.com/admpub/checksum"
	imageproxy "github.com/admpub/imageproxy"
	"github.com/admpub/log"
	"github.com/admpub/errors"
	"github.com/admpub/nging/application/handler"
	"github.com/admpub/nging/application/library/common"
	"github.com/admpub/nging/application/middleware"
	modelFile "github.com/admpub/nging/application/model/file"
	"github.com/admpub/nging/application/registry/upload"
	"github.com/admpub/nging/application/registry/upload/convert"
)

// cropPermCheckers 裁剪权限检查
var cropPermCheckers = []func(ctx echo.Context, f *modelFile.File) error{
	func(ctx echo.Context, f *modelFile.File) error {
		if f.FieldName() == `avatar` && f.OwnerType == `user` {
			err := middleware.CheckAnyPerm(ctx, `manager/user_add`, `manager/user_edit`)
			if err != nil {
				return err
			}
			return nil
		}
		return common.ErrNext
	},
}

var watermarkFile = filepath.Join(echo.Wd(), `public/assets/backend/images/nging-gear.png`)

func SetWatermark(markfile string) {
	watermarkFile = markfile
}

func WatermarkFile() string {
	return watermarkFile
}

// CropPermCheckerAdd 添加裁剪权限检查逻辑
func CropPermCheckerAdd(checker func(ctx echo.Context, f *modelFile.File) error) {
	cropPermCheckers = append(cropPermCheckers, checker)
}

// Crop 图片裁剪
func Crop(ctx echo.Context) error {
	ownerType := `user`
	user := handler.User(ctx)
	var ownerID uint64
	if user != nil {
		ownerID = uint64(user.Id)
	}
	if ownerID < 1 {
		ctx.Data().SetError(ctx.E(`请先登录`))
		return ctx.Redirect(handler.URLFor(`/login`))
	}
	return CropByOwner(ctx, ownerType, ownerID)
}

// CropByOwner 图片裁剪
func CropByOwner(ctx echo.Context, ownerType string, ownerID uint64) error {
	var err error
	uploadType := ctx.Param(`type`)
	if len(uploadType) == 0 {
		uploadType = ctx.Form(`type`)
	}
	storerInfo := StorerEngine()
	prepareData, err := upload.Prepare(ctx, uploadType, ``, storerInfo)
	if err != nil {
		return err
	}
	defer prepareData.Close()
	subdirInfo := upload.SubdirGet(prepareData.TableName)
	if subdirInfo == nil {
		return ctx.E(`“%s”未被登记`, uploadType)
	}

	// 获取缩略图尺寸
	thumbSizes := subdirInfo.ThumbSize(prepareData.FieldName)
	cropSize := ctx.Form(`size`, `200x200`)
	var thumbWidth, thumbHeight float64
	cropSizeArr := strings.SplitN(cropSize, `x`, 2)
	switch len(cropSizeArr) {
	case 2:
		cropSizeArr[1] = strings.TrimSpace(cropSizeArr[1])
		thumbHeight = param.AsFloat64(cropSizeArr[1])
		cropSizeArr[0] = strings.TrimSpace(cropSizeArr[0])
		thumbWidth = param.AsFloat64(cropSizeArr[0])
	case 1:
		cropSizeArr[0] = strings.TrimSpace(cropSizeArr[0])
		thumbWidth = param.AsFloat64(cropSizeArr[0])
		thumbHeight = thumbWidth
	}
	var thumbSize *upload.ThumbSize
	if len(thumbSizes) > 0 {
		for _, ts := range thumbSizes {
			if ts.Width == thumbWidth && ts.Height == thumbHeight {
				thumbSize = &ts
				break
			}
		}
		if thumbSize == nil {
			return ctx.E(`“%s”不支持裁剪图片`, uploadType)
		}
	}

	fileM := modelFile.NewFile(ctx)
	err = fileM.GetByViewURL(storerInfo, srcURL)
	if err != nil {
		return err
	}
	ctx.Internal().Set(`storerID`, fileM.StorerId)
	storer, err := prepareData.Storer(ctx)
	if err != nil {
		return err
	}
	srcURL := ctx.Form(`src`)
	srcURL, err = com.URLDecode(srcURL)
	if err != nil {
		return err
	}
	srcURL = `/` + storer.URLToPath(srcURL)
	if err = common.IsRightUploadFile(ctx, srcURL); err != nil {
		return errors.WithMessage(err, srcURL)
	}
	thumbM := modelFile.NewThumb(ctx)
	var editable bool
	if ownerType == `user` && ownerID == 1 { //管理员可编辑
		editable = true
	} else if fileM.OwnerType == ownerType &&
		fileM.OwnerId == ownerID { //上传者可编辑
		editable = true
	} else { //其它验证方式
		for _, check := range cropPermCheckers {
			err := check(ctx, fileM)
			if err == nil { //验证到权限
				editable = true
				break
			}
			if err != common.ErrNext {
				return err
			}
		}
	}
	if !editable {
		return common.ErrUserNoPerm
	}

	x := ctx.Formx(`x`).Float64()
	y := ctx.Formx(`y`).Float64()
	w := ctx.Formx(`w`).Float64()
	h := ctx.Formx(`h`).Float64()

	//{"x":528,"y":108,"height":864,"width":864,"rotate":0}
	//fmt.Println(avatard)
	opt := &imageproxy.Options{
		CropX:          x,   //裁剪X轴起始位置
		CropY:          y,   //裁剪Y轴起始位置
		CropWidth:      w,   //裁剪宽度
		CropHeight:     h,   //裁剪高度
		Width:          200, //缩略图宽度
		Height:         200, //缩略图高度
		Fit:            false,
		Rotate:         0,
		FlipVertical:   false,
		FlipHorizontal: false,
		Quality:        100,
		Signature:      "",
		ScaleUp:        true,
	}
	if thumbSize != nil {
		opt.Width = thumbSize.Width
		opt.Height = thumbSize.Height
	}
	thumbURL := tplfunc.AddSuffix(srcURL, fmt.Sprintf(`_%v_%v`, opt.Width, opt.Height))
	var cropped bool
	cropped, err = storer.Exists(thumbURL)
	if err != nil {
		return err
	}
	name := path.Base(srcURL)
	var onSuccess func() string

	//对于头像图片，可以根据原图文件的md5值来判断是否需要重新生成缩略图
	if len(name) > 7 && name[0:7] == `avatar.` {
		md5file := path.Join(path.Dir(srcURL), `avatar.md5`)
		putFile := storer.URLToFile(md5file)
		onSuccess = func() string {
			reader, err := storer.Get(srcURL)
			if reader != nil {
				defer reader.Close()
			}
			if err != nil {
				log.Error(err)
				return ``
			}
			originMd5, err := checksum.MD5sumReader(reader)
			if err != nil {
				log.Error(err)
				return ``
			}
			size := len(originMd5)
			_, _, err = storer.Put(putFile, bytes.NewBufferString(originMd5), int64(size))
			if err != nil {
				log.Error(err)
			}
			return originMd5
		}

		if cropped {
			cropped, err = storer.Exists(md5file)
			if err != nil {
				return err
			}
		}

		if cropped {
			md5reader, err := storer.Get(md5file)
			if md5reader != nil {
				defer md5reader.Close()
			}
			if err != nil {
				return err
			}
			b, err := ioutil.ReadAll(md5reader)
			if err != nil {
				return err
			}
			reader, err := storer.Get(srcURL)
			if reader != nil {
				defer reader.Close()
			}
			originMd5, err := checksum.MD5sumReader(reader)
			if err != nil {
				return err
			}
			if string(b) == originMd5 {
				goto END
			}
			cropped = false
			onSuccess = func() string { //直接使用上面读到的md5
				size := len(originMd5)
				_, _, err = storer.Put(putFile, bytes.NewBufferString(originMd5), int64(size))
				if err != nil {
					log.Error(err)
				}
				return originMd5
			}
		}
	}

END:
	if cropped {
		if ctx.Format() == `json` {
			return ctx.JSON(ctx.Data().SetInfo(`skipped`).SetData(thumbURL))
		}
		return storer.SendFile(ctx, thumbURL)
	}

	var reader io.ReadCloser
	reader, err = storer.Get(srcURL)
	if reader != nil {
		defer reader.Close()
	}
	if err != nil {
		return err
	}

	var fileMd5 string
	if onSuccess != nil {
		fileMd5 = onSuccess()
	}
	cropOpt := &modelFile.CropOptions{
		Options:       opt,
		File:          fileM.NgingFile,
		SrcReader:     reader,
		Storer:        storer,
		DestFile:      storer.URLToFile(thumbURL),
		FileMD5:       fileMd5,
		WatermarkFile: watermarkFile,
	}
	err = thumbM.Crop(cropOpt)
	if err != nil {
		return err
	}
	otherFormatExtensions := convert.Extensions()
	for _, extension := range otherFormatExtensions {
		if err := storer.Delete(thumbURL + extension); err != nil && !os.IsNotExist(err) {
			return err
		}
	}
	if ctx.Format() == `json` {
		return ctx.JSON(ctx.Data().SetInfo(`cropped`).SetData(thumbURL))
	}
	return storer.SendFile(ctx, thumbURL)
}
<commit_msg>update<commit_after>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package manager

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

	"github.com/webx-top/com"
	"github.com/webx-top/echo"
	"github.com/webx-top/echo/middleware/tplfunc"
	"github.com/webx-top/echo/param"

	"github.com/admpub/checksum"
	imageproxy "github.com/admpub/imageproxy"
	"github.com/admpub/log"
	"github.com/admpub/errors"
	"github.com/admpub/nging/application/handler"
	"github.com/admpub/nging/application/library/common"
	"github.com/admpub/nging/application/middleware"
	modelFile "github.com/admpub/nging/application/model/file"
	"github.com/admpub/nging/application/registry/upload"
	"github.com/admpub/nging/application/registry/upload/convert"
)

// cropPermCheckers 裁剪权限检查
var cropPermCheckers = []func(ctx echo.Context, f *modelFile.File) error{
	func(ctx echo.Context, f *modelFile.File) error {
		if f.FieldName() == `avatar` && f.OwnerType == `user` {
			err := middleware.CheckAnyPerm(ctx, `manager/user_add`, `manager/user_edit`)
			if err != nil {
				return err
			}
			return nil
		}
		return common.ErrNext
	},
}

var watermarkFile = filepath.Join(echo.Wd(), `public/assets/backend/images/nging-gear.png`)

func SetWatermark(markfile string) {
	watermarkFile = markfile
}

func WatermarkFile() string {
	return watermarkFile
}

// CropPermCheckerAdd 添加裁剪权限检查逻辑
func CropPermCheckerAdd(checker func(ctx echo.Context, f *modelFile.File) error) {
	cropPermCheckers = append(cropPermCheckers, checker)
}

// Crop 图片裁剪
func Crop(ctx echo.Context) error {
	ownerType := `user`
	user := handler.User(ctx)
	var ownerID uint64
	if user != nil {
		ownerID = uint64(user.Id)
	}
	if ownerID < 1 {
		ctx.Data().SetError(ctx.E(`请先登录`))
		return ctx.Redirect(handler.URLFor(`/login`))
	}
	return CropByOwner(ctx, ownerType, ownerID)
}

// CropByOwner 图片裁剪
func CropByOwner(ctx echo.Context, ownerType string, ownerID uint64) error {
	var err error
	uploadType := ctx.Param(`type`)
	if len(uploadType) == 0 {
		uploadType = ctx.Form(`type`)
	}
	storerInfo := StorerEngine()
	prepareData, err := upload.Prepare(ctx, uploadType, ``, storerInfo)
	if err != nil {
		return err
	}
	defer prepareData.Close()
	subdirInfo := upload.SubdirGet(prepareData.TableName)
	if subdirInfo == nil {
		return ctx.E(`“%s”未被登记`, uploadType)
	}

	// 获取缩略图尺寸
	thumbSizes := subdirInfo.ThumbSize(prepareData.FieldName)
	cropSize := ctx.Form(`size`, `200x200`)
	var thumbWidth, thumbHeight float64
	cropSizeArr := strings.SplitN(cropSize, `x`, 2)
	switch len(cropSizeArr) {
	case 2:
		cropSizeArr[1] = strings.TrimSpace(cropSizeArr[1])
		thumbHeight = param.AsFloat64(cropSizeArr[1])
		cropSizeArr[0] = strings.TrimSpace(cropSizeArr[0])
		thumbWidth = param.AsFloat64(cropSizeArr[0])
	case 1:
		cropSizeArr[0] = strings.TrimSpace(cropSizeArr[0])
		thumbWidth = param.AsFloat64(cropSizeArr[0])
		thumbHeight = thumbWidth
	}
	var thumbSize *upload.ThumbSize
	if len(thumbSizes) > 0 {
		for _, ts := range thumbSizes {
			if ts.Width == thumbWidth && ts.Height == thumbHeight {
				thumbSize = &ts
				break
			}
		}
		if thumbSize == nil {
			return ctx.E(`“%s”不支持裁剪图片`, uploadType)
		}
	}
	srcURL := ctx.Form(`src`)
	srcURL, err = com.URLDecode(srcURL)
	if err != nil {
		return err
	}
	fileM := modelFile.NewFile(ctx)
	err = fileM.GetByViewURL(storerInfo, srcURL)
	if err != nil {
		return err
	}
	ctx.Internal().Set(`storerID`, fileM.StorerId)
	storer, err := prepareData.Storer(ctx)
	if err != nil {
		return err
	}
	srcURL = `/` + storer.URLToPath(srcURL)
	if err = common.IsRightUploadFile(ctx, srcURL); err != nil {
		return errors.WithMessage(err, srcURL)
	}
	thumbM := modelFile.NewThumb(ctx)
	var editable bool
	if ownerType == `user` && ownerID == 1 { //管理员可编辑
		editable = true
	} else if fileM.OwnerType == ownerType &&
		fileM.OwnerId == ownerID { //上传者可编辑
		editable = true
	} else { //其它验证方式
		for _, check := range cropPermCheckers {
			err := check(ctx, fileM)
			if err == nil { //验证到权限
				editable = true
				break
			}
			if err != common.ErrNext {
				return err
			}
		}
	}
	if !editable {
		return common.ErrUserNoPerm
	}

	x := ctx.Formx(`x`).Float64()
	y := ctx.Formx(`y`).Float64()
	w := ctx.Formx(`w`).Float64()
	h := ctx.Formx(`h`).Float64()

	//{"x":528,"y":108,"height":864,"width":864,"rotate":0}
	//fmt.Println(avatard)
	opt := &imageproxy.Options{
		CropX:          x,   //裁剪X轴起始位置
		CropY:          y,   //裁剪Y轴起始位置
		CropWidth:      w,   //裁剪宽度
		CropHeight:     h,   //裁剪高度
		Width:          200, //缩略图宽度
		Height:         200, //缩略图高度
		Fit:            false,
		Rotate:         0,
		FlipVertical:   false,
		FlipHorizontal: false,
		Quality:        100,
		Signature:      "",
		ScaleUp:        true,
	}
	if thumbSize != nil {
		opt.Width = thumbSize.Width
		opt.Height = thumbSize.Height
	}
	thumbURL := tplfunc.AddSuffix(srcURL, fmt.Sprintf(`_%v_%v`, opt.Width, opt.Height))
	var cropped bool
	cropped, err = storer.Exists(thumbURL)
	if err != nil {
		return err
	}
	name := path.Base(srcURL)
	var onSuccess func() string

	//对于头像图片，可以根据原图文件的md5值来判断是否需要重新生成缩略图
	if len(name) > 7 && name[0:7] == `avatar.` {
		md5file := path.Join(path.Dir(srcURL), `avatar.md5`)
		putFile := storer.URLToFile(md5file)
		onSuccess = func() string {
			reader, err := storer.Get(srcURL)
			if reader != nil {
				defer reader.Close()
			}
			if err != nil {
				log.Error(err)
				return ``
			}
			originMd5, err := checksum.MD5sumReader(reader)
			if err != nil {
				log.Error(err)
				return ``
			}
			size := len(originMd5)
			_, _, err = storer.Put(putFile, bytes.NewBufferString(originMd5), int64(size))
			if err != nil {
				log.Error(err)
			}
			return originMd5
		}

		if cropped {
			cropped, err = storer.Exists(md5file)
			if err != nil {
				return err
			}
		}

		if cropped {
			md5reader, err := storer.Get(md5file)
			if md5reader != nil {
				defer md5reader.Close()
			}
			if err != nil {
				return err
			}
			b, err := ioutil.ReadAll(md5reader)
			if err != nil {
				return err
			}
			reader, err := storer.Get(srcURL)
			if reader != nil {
				defer reader.Close()
			}
			originMd5, err := checksum.MD5sumReader(reader)
			if err != nil {
				return err
			}
			if string(b) == originMd5 {
				goto END
			}
			cropped = false
			onSuccess = func() string { //直接使用上面读到的md5
				size := len(originMd5)
				_, _, err = storer.Put(putFile, bytes.NewBufferString(originMd5), int64(size))
				if err != nil {
					log.Error(err)
				}
				return originMd5
			}
		}
	}

END:
	if cropped {
		if ctx.Format() == `json` {
			return ctx.JSON(ctx.Data().SetInfo(`skipped`).SetData(thumbURL))
		}
		return storer.SendFile(ctx, thumbURL)
	}

	var reader io.ReadCloser
	reader, err = storer.Get(srcURL)
	if reader != nil {
		defer reader.Close()
	}
	if err != nil {
		return err
	}

	var fileMd5 string
	if onSuccess != nil {
		fileMd5 = onSuccess()
	}
	cropOpt := &modelFile.CropOptions{
		Options:       opt,
		File:          fileM.NgingFile,
		SrcReader:     reader,
		Storer:        storer,
		DestFile:      storer.URLToFile(thumbURL),
		FileMD5:       fileMd5,
		WatermarkFile: watermarkFile,
	}
	err = thumbM.Crop(cropOpt)
	if err != nil {
		return err
	}
	otherFormatExtensions := convert.Extensions()
	for _, extension := range otherFormatExtensions {
		if err := storer.Delete(thumbURL + extension); err != nil && !os.IsNotExist(err) {
			return err
		}
	}
	if ctx.Format() == `json` {
		return ctx.JSON(ctx.Data().SetInfo(`cropped`).SetData(thumbURL))
	}
	return storer.SendFile(ctx, thumbURL)
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package snowman

import (
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow/choices"
)

// Voter records chits received from [vdr] once its dependencies are met.
type voter struct {
	t         *Transitive
	vdr       ids.ShortID
	requestID uint32
	response  ids.ID
	deps      ids.Set
}

func (v *voter) Dependencies() ids.Set { return v.deps }

// Mark that a dependency has been met.
func (v *voter) Fulfill(id ids.ID) {
	v.deps.Remove(id)
	v.Update()
}

// Abandon this attempt to record chits.
func (v *voter) Abandon(id ids.ID) { v.Fulfill(id) }

func (v *voter) Update() {
	if v.deps.Len() != 0 || v.t.errs.Errored() {
		return
	}

	var results []ids.Bag
	if v.response == ids.Empty {
		results = v.t.polls.Drop(v.requestID, v.vdr)
	} else {
		results = v.t.polls.Vote(v.requestID, v.vdr, v.response)
	}

	if len(results) == 0 {
		return
	}

	// To prevent any potential deadlocks with un-disclosed dependencies, votes
	// must be bubbled to the nearest valid block
	for i, result := range results {
		results[i] = v.bubbleVotes(result)
	}

	v.t.Ctx.Log.Debug("Finishing poll [%d] with:\n%s", v.requestID, &results)
	for _, result := range results {
		if err := v.t.Consensus.RecordPoll(result); err != nil {
			v.t.errs.Add(err)
		}
	}

	if v.t.errs.Errored() {
		return
	}

	if err := v.t.VM.SetPreference(v.t.Consensus.Preference()); err != nil {
		v.t.errs.Add(err)
		return
	}

	if v.t.Consensus.Finalized() {
		v.t.Ctx.Log.Debug("Snowman engine can quiesce")
		return
	}

	v.t.Ctx.Log.Debug("Snowman engine can't quiesce")
	v.t.repoll()
}

func (v *voter) bubbleVotes(votes ids.Bag) ids.Bag {
	bubbledVotes := ids.Bag{}
	for _, vote := range votes.List() {
		count := votes.Count(vote)
		blk, err := v.t.VM.GetBlock(vote)
		if err != nil {
			continue
		}
		status := blk.Status()
		blkID := blk.ID()
		for status.Fetched() && !v.t.Consensus.DecidedOrProcessing(blk) {
			blkID := blk.Parent()
			blk, err = v.t.GetBlock(blkID)
			if err != nil {
				status = choices.Unknown
				break
			}
			status = blk.Status()
		}
		if status == choices.Unknown && v.t.Consensus.Processing(blkID) {
			bubbledVotes.AddCount(blkID, count)
		} else if !status.Decided() && v.t.Consensus.DecidedOrProcessing(blk) {
			bubbledVotes.AddCount(blkID, count)
		}
	}
	return bubbledVotes
}
<commit_msg>fix using nil block<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package snowman

import (
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow/choices"
)

// Voter records chits received from [vdr] once its dependencies are met.
type voter struct {
	t         *Transitive
	vdr       ids.ShortID
	requestID uint32
	response  ids.ID
	deps      ids.Set
}

func (v *voter) Dependencies() ids.Set { return v.deps }

// Mark that a dependency has been met.
func (v *voter) Fulfill(id ids.ID) {
	v.deps.Remove(id)
	v.Update()
}

// Abandon this attempt to record chits.
func (v *voter) Abandon(id ids.ID) { v.Fulfill(id) }

func (v *voter) Update() {
	if v.deps.Len() != 0 || v.t.errs.Errored() {
		return
	}

	var results []ids.Bag
	if v.response == ids.Empty {
		results = v.t.polls.Drop(v.requestID, v.vdr)
	} else {
		results = v.t.polls.Vote(v.requestID, v.vdr, v.response)
	}

	if len(results) == 0 {
		return
	}

	// To prevent any potential deadlocks with un-disclosed dependencies, votes
	// must be bubbled to the nearest valid block
	for i, result := range results {
		results[i] = v.bubbleVotes(result)
	}

	v.t.Ctx.Log.Debug("Finishing poll [%d] with:\n%s", v.requestID, &results)
	for _, result := range results {
		if err := v.t.Consensus.RecordPoll(result); err != nil {
			v.t.errs.Add(err)
		}
	}

	if v.t.errs.Errored() {
		return
	}

	if err := v.t.VM.SetPreference(v.t.Consensus.Preference()); err != nil {
		v.t.errs.Add(err)
		return
	}

	if v.t.Consensus.Finalized() {
		v.t.Ctx.Log.Debug("Snowman engine can quiesce")
		return
	}

	v.t.Ctx.Log.Debug("Snowman engine can't quiesce")
	v.t.repoll()
}

func (v *voter) bubbleVotes(votes ids.Bag) ids.Bag {
	bubbledVotes := ids.Bag{}
	for _, vote := range votes.List() {
		count := votes.Count(vote)
		blk, err := v.t.VM.GetBlock(vote)
		if err != nil {
			continue
		}
		status := blk.Status()
		blkID := blk.ID()
		for status.Fetched() && !v.t.Consensus.DecidedOrProcessing(blk) {
			blkID := blk.Parent()
			blk, err = v.t.GetBlock(blkID)
			if err != nil {
				status = choices.Unknown
				break
			}
			status = blk.Status()
		}
		switch {
		case status == choices.Unknown:
			if v.t.Consensus.Processing(blkID) {
				bubbledVotes.AddCount(blkID, count)
			}
		case !status.Decided():
			if v.t.Consensus.DecidedOrProcessing(blk) {
				bubbledVotes.AddCount(blkID, count)
			}
		}
	}
	return bubbledVotes
}
<|endoftext|>


<commit_before>package tsacmd

import (
	"bytes"
	"fmt"
	"net/http"
	"os"
	"sync"
	"time"

	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/localip"
	"github.com/concourse/concourse/tsa"
	"github.com/concourse/flag"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/grouper"
	"github.com/tedsuo/ifrit/http_server"
	"github.com/tedsuo/ifrit/sigmon"
	"golang.org/x/crypto/ssh"
)

type TSACommand struct {
	Logger      flag.Lager
	PeerAddress string

	BindIP   flag.IP `long:"bind-ip"   default:"0.0.0.0" description:"IP address on which to listen for SSH."`
	BindPort uint16  `long:"bind-port" default:"2222"    description:"Port on which to listen for SSH."`

	DebugBindIP   flag.IP `long:"debug-bind-ip"   default:"127.0.0.1" description:"IP address on which to listen for the pprof debugger endpoints."`
	DebugBindPort uint16  `long:"debug-bind-port" default:"2221"      description:"Port on which to listen for the pprof debugger endpoints."`

	HostKey            *flag.PrivateKey               `long:"host-key"        required:"true" description:"Path to private key to use for the SSH server."`
	AuthorizedKeys     flag.AuthorizedKeys            `long:"authorized-keys" description:"Path to file containing keys to authorize, in SSH authorized_keys format (one public key per line)."`
	TeamAuthorizedKeys map[string]flag.AuthorizedKeys `long:"team-authorized-keys" value-name:"NAME:PATH" description:"Path to file containing keys to authorize, in SSH authorized_keys format (one public key per line)."`

	ATCURLs []flag.URL `long:"atc-url" required:"true" description:"ATC API endpoints to which workers will be registered."`

	SessionSigningKey *flag.PrivateKey `long:"session-signing-key" required:"true" description:"Path to private key to use when signing tokens in reqests to the ATC during registration."`

	HeartbeatInterval time.Duration `long:"heartbeat-interval" default:"30s" description:"interval on which to heartbeat workers to the ATC"`
}

type TeamAuthKeys struct {
	Team     string
	AuthKeys []ssh.PublicKey
}

func (cmd *TSACommand) Execute(args []string) error {
	runner, err := cmd.Runner(args)
	if err != nil {
		return err
	}

	tsaServerMember := grouper.Member{
		Name:   "tsa-server",
		Runner: sigmon.New(runner),
	}

	tsaDebugMember := grouper.Member{
		Name: "debug-server",
		Runner: http_server.New(
			cmd.debugBindAddr(),
			http.DefaultServeMux,
		)}

	members := []grouper.Member{
		tsaDebugMember,
		tsaServerMember,
	}

	group := grouper.NewParallel(os.Interrupt, members)
	return <-ifrit.Invoke(group).Wait()
}

func (cmd *TSACommand) Runner(args []string) (ifrit.Runner, error) {
	logger, _ := cmd.constructLogger()

	atcEndpointPicker := tsa.NewRandomATCEndpointPicker(cmd.ATCURLs)

	teamAuthorizedKeys, err := cmd.loadTeamAuthorizedKeys()
	if err != nil {
		return nil, fmt.Errorf("failed to load team authorized keys: %s", err)
	}

	if len(cmd.AuthorizedKeys.Keys)+len(cmd.TeamAuthorizedKeys) == 0 {
		logger.Info("starting-tsa-without-authorized-keys")
	}

	sessionAuthTeam := &sessionTeam{
		sessionTeams: make(map[string]string),
		lock:         &sync.RWMutex{},
	}

	config, err := cmd.configureSSHServer(sessionAuthTeam, cmd.AuthorizedKeys.Keys, teamAuthorizedKeys)
	if err != nil {
		return nil, fmt.Errorf("failed to configure SSH server: %s", err)
	}

	listenAddr := fmt.Sprintf("%s:%d", cmd.BindIP, cmd.BindPort)

	if cmd.SessionSigningKey == nil {
		return nil, fmt.Errorf("missing session signing key")
	}

	if cmd.PeerAddress == "" {
		peerAddress, err := localip.LocalIP()
		if err != nil {
			return nil, fmt.Errorf("failed to get host network address: %s", err)
		}
		cmd.PeerAddress = peerAddress
	}

	tokenGenerator := tsa.NewTokenGenerator(cmd.SessionSigningKey.PrivateKey)

	server := &server{
		logger:            logger,
		heartbeatInterval: cmd.HeartbeatInterval,
		cprInterval:       1 * time.Second,
		atcEndpointPicker: atcEndpointPicker,
		tokenGenerator:    tokenGenerator,
		forwardHost:       cmd.PeerAddress,
		config:            config,
		httpClient:        http.DefaultClient,
		sessionTeam:       sessionAuthTeam,
	}

	return serverRunner{logger, server, listenAddr}, nil
}

func (cmd *TSACommand) constructLogger() (lager.Logger, *lager.ReconfigurableSink) {
	logger, reconfigurableSink := cmd.Logger.Logger("tsa")

	return logger, reconfigurableSink
}

func (cmd *TSACommand) loadTeamAuthorizedKeys() ([]TeamAuthKeys, error) {
	var teamKeys []TeamAuthKeys

	for teamName, keys := range cmd.TeamAuthorizedKeys {
		teamKeys = append(teamKeys, TeamAuthKeys{
			Team:     teamName,
			AuthKeys: keys.Keys,
		})
	}

	return teamKeys, nil
}

func (cmd *TSACommand) configureSSHServer(sessionAuthTeam *sessionTeam, authorizedKeys []ssh.PublicKey, teamAuthorizedKeys []TeamAuthKeys) (*ssh.ServerConfig, error) {
	certChecker := &ssh.CertChecker{
		IsUserAuthority: func(key ssh.PublicKey) bool {
			return false
		},

		IsHostAuthority: func(key ssh.PublicKey, address string) bool {
			return false
		},

		UserKeyFallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			for _, k := range authorizedKeys {
				if bytes.Equal(k.Marshal(), key.Marshal()) {
					return nil, nil
				}
			}

			for _, teamKeys := range teamAuthorizedKeys {
				for _, k := range teamKeys.AuthKeys {
					if bytes.Equal(k.Marshal(), key.Marshal()) {
						sessionAuthTeam.AuthorizeTeam(string(conn.SessionID()), teamKeys.Team)
						return nil, nil
					}
				}
			}

			return nil, fmt.Errorf("unknown public key")
		},
	}

	config := &ssh.ServerConfig{
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			return certChecker.Authenticate(conn, key)
		},
	}

	signer, err := ssh.NewSignerFromKey(cmd.HostKey)
	if err != nil {
		return nil, fmt.Errorf("failed to create signer from host key: %s", err)
	}

	config.AddHostKey(signer)

	return config, nil
}

func (cmd *TSACommand) debugBindAddr() string {
	return fmt.Sprintf("%s:%d", cmd.DebugBindIP, cmd.DebugBindPort)
}
<commit_msg>tsa: keep peer address as a tsa flag<commit_after>package tsacmd

import (
	"bytes"
	"fmt"
	"net/http"
	"os"
	"sync"
	"time"

	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/localip"
	"github.com/concourse/concourse/tsa"
	"github.com/concourse/flag"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/grouper"
	"github.com/tedsuo/ifrit/http_server"
	"github.com/tedsuo/ifrit/sigmon"
	"golang.org/x/crypto/ssh"
)

type TSACommand struct {
	Logger flag.Lager

	BindIP      flag.IP `long:"bind-ip"   default:"0.0.0.0" description:"IP address on which to listen for SSH."`
	PeerAddress string  `long:"peer-address" default:"127.0.0.1" description:"Network address of this web node, reachable by other web nodes. Used for forwarded worker addresses."`
	BindPort    uint16  `long:"bind-port" default:"2222"    description:"Port on which to listen for SSH."`

	DebugBindIP   flag.IP `long:"debug-bind-ip"   default:"127.0.0.1" description:"IP address on which to listen for the pprof debugger endpoints."`
	DebugBindPort uint16  `long:"debug-bind-port" default:"2221"      description:"Port on which to listen for the pprof debugger endpoints."`

	HostKey            *flag.PrivateKey               `long:"host-key"        required:"true" description:"Path to private key to use for the SSH server."`
	AuthorizedKeys     flag.AuthorizedKeys            `long:"authorized-keys" description:"Path to file containing keys to authorize, in SSH authorized_keys format (one public key per line)."`
	TeamAuthorizedKeys map[string]flag.AuthorizedKeys `long:"team-authorized-keys" value-name:"NAME:PATH" description:"Path to file containing keys to authorize, in SSH authorized_keys format (one public key per line)."`

	ATCURLs []flag.URL `long:"atc-url" required:"true" description:"ATC API endpoints to which workers will be registered."`

	SessionSigningKey *flag.PrivateKey `long:"session-signing-key" required:"true" description:"Path to private key to use when signing tokens in reqests to the ATC during registration."`

	HeartbeatInterval time.Duration `long:"heartbeat-interval" default:"30s" description:"interval on which to heartbeat workers to the ATC"`
}

type TeamAuthKeys struct {
	Team     string
	AuthKeys []ssh.PublicKey
}

func (cmd *TSACommand) Execute(args []string) error {
	runner, err := cmd.Runner(args)
	if err != nil {
		return err
	}

	tsaServerMember := grouper.Member{
		Name:   "tsa-server",
		Runner: sigmon.New(runner),
	}

	tsaDebugMember := grouper.Member{
		Name: "debug-server",
		Runner: http_server.New(
			cmd.debugBindAddr(),
			http.DefaultServeMux,
		)}

	members := []grouper.Member{
		tsaDebugMember,
		tsaServerMember,
	}

	group := grouper.NewParallel(os.Interrupt, members)
	return <-ifrit.Invoke(group).Wait()
}

func (cmd *TSACommand) Runner(args []string) (ifrit.Runner, error) {
	logger, _ := cmd.constructLogger()

	atcEndpointPicker := tsa.NewRandomATCEndpointPicker(cmd.ATCURLs)

	teamAuthorizedKeys, err := cmd.loadTeamAuthorizedKeys()
	if err != nil {
		return nil, fmt.Errorf("failed to load team authorized keys: %s", err)
	}

	if len(cmd.AuthorizedKeys.Keys)+len(cmd.TeamAuthorizedKeys) == 0 {
		logger.Info("starting-tsa-without-authorized-keys")
	}

	sessionAuthTeam := &sessionTeam{
		sessionTeams: make(map[string]string),
		lock:         &sync.RWMutex{},
	}

	config, err := cmd.configureSSHServer(sessionAuthTeam, cmd.AuthorizedKeys.Keys, teamAuthorizedKeys)
	if err != nil {
		return nil, fmt.Errorf("failed to configure SSH server: %s", err)
	}

	listenAddr := fmt.Sprintf("%s:%d", cmd.BindIP, cmd.BindPort)

	if cmd.SessionSigningKey == nil {
		return nil, fmt.Errorf("missing session signing key")
	}

	if cmd.PeerAddress == "" {
		peerAddress, err := localip.LocalIP()
		if err != nil {
			return nil, fmt.Errorf("failed to get host network address: %s", err)
		}
		cmd.PeerAddress = peerAddress
	}

	tokenGenerator := tsa.NewTokenGenerator(cmd.SessionSigningKey.PrivateKey)

	server := &server{
		logger:            logger,
		heartbeatInterval: cmd.HeartbeatInterval,
		cprInterval:       1 * time.Second,
		atcEndpointPicker: atcEndpointPicker,
		tokenGenerator:    tokenGenerator,
		forwardHost:       cmd.PeerAddress,
		config:            config,
		httpClient:        http.DefaultClient,
		sessionTeam:       sessionAuthTeam,
	}

	return serverRunner{logger, server, listenAddr}, nil
}

func (cmd *TSACommand) constructLogger() (lager.Logger, *lager.ReconfigurableSink) {
	logger, reconfigurableSink := cmd.Logger.Logger("tsa")

	return logger, reconfigurableSink
}

func (cmd *TSACommand) loadTeamAuthorizedKeys() ([]TeamAuthKeys, error) {
	var teamKeys []TeamAuthKeys

	for teamName, keys := range cmd.TeamAuthorizedKeys {
		teamKeys = append(teamKeys, TeamAuthKeys{
			Team:     teamName,
			AuthKeys: keys.Keys,
		})
	}

	return teamKeys, nil
}

func (cmd *TSACommand) configureSSHServer(sessionAuthTeam *sessionTeam, authorizedKeys []ssh.PublicKey, teamAuthorizedKeys []TeamAuthKeys) (*ssh.ServerConfig, error) {
	certChecker := &ssh.CertChecker{
		IsUserAuthority: func(key ssh.PublicKey) bool {
			return false
		},

		IsHostAuthority: func(key ssh.PublicKey, address string) bool {
			return false
		},

		UserKeyFallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			for _, k := range authorizedKeys {
				if bytes.Equal(k.Marshal(), key.Marshal()) {
					return nil, nil
				}
			}

			for _, teamKeys := range teamAuthorizedKeys {
				for _, k := range teamKeys.AuthKeys {
					if bytes.Equal(k.Marshal(), key.Marshal()) {
						sessionAuthTeam.AuthorizeTeam(string(conn.SessionID()), teamKeys.Team)
						return nil, nil
					}
				}
			}

			return nil, fmt.Errorf("unknown public key")
		},
	}

	config := &ssh.ServerConfig{
		PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
			return certChecker.Authenticate(conn, key)
		},
	}

	signer, err := ssh.NewSignerFromKey(cmd.HostKey)
	if err != nil {
		return nil, fmt.Errorf("failed to create signer from host key: %s", err)
	}

	config.AddHostKey(signer)

	return config, nil
}

func (cmd *TSACommand) debugBindAddr() string {
	return fmt.Sprintf("%s:%d", cmd.DebugBindIP, cmd.DebugBindPort)
}
<|endoftext|>


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

package config

import "github.com/neovim/go-client/nvim"

// Config represents a config variable for nvim-go.
// Each type must be exported for plugin.HandleAutocmd Eval option.
// Also it does not support embeded type.
type Config struct {
	Global Global

	Build    build
	Cover    cover
	Fmt      fmt
	Generate generate
	Guru     guru
	Iferr    iferr
	Lint     lint
	Rename   rename
	Terminal terminal
	Test     test

	Debug debug
}

// Global represents a global config variable.
type Global struct {
	ChannelID     int
	ServerName    string `eval:"v:servername"`
	ErrorListType string `eval:"g:go#global#errorlisttype"`
}

// build GoBuild command config variable.
type build struct {
	Autosave int64    `eval:"g:go#build#autosave"`
	Force    int64    `eval:"g:go#build#force"`
	Flags    []string `eval:"g:go#build#flags"`
}

type cover struct {
	Flags []string `eval:"g:go#cover#flags"`
	Mode  string   `eval:"g:go#cover#mode"`
}

// fmt represents a GoFmt command config variable.
type fmt struct {
	Autosave int64  `eval:"g:go#fmt#autosave"`
	Mode     string `eval:"g:go#fmt#mode"`
}

// generate represents a GoGenerate command config variables.
type generate struct {
	TestAllFuncs      int64  `eval:"g:go#generate#test#allfuncs"`
	TestExclFuncs     string `eval:"g:go#generate#test#exclude"`
	TestExportedFuncs int64  `eval:"g:go#generate#test#exportedfuncs"`
	TestSubTest       int64  `eval:"g:go#generate#test#subtest"`
}

// guru represents a GoGuru command config variable.
type guru struct {
	Reflection int64            `eval:"g:go#guru#reflection"`
	KeepCursor map[string]int64 `eval:"g:go#guru#keep_cursor"`
	JumpFirst  int64            `eval:"g:go#guru#jump_first"`
}

// iferr represents a GoIferr command config variable.
type iferr struct {
	Autosave int64 `eval:"g:go#iferr#autosave"`
}

// lint represents a code lint commands config variable.
type lint struct {
	GolintAutosave          bool     `eval:"g:go#lint#golint#autosave"`
	GolintIgnore            []string `eval:"g:go#lint#golint#ignore"`
	GolintMinConfidence     float64  `eval:"g:go#lint#golint#min_confidence"`
	GolintMode              string   `eval:"g:go#lint#golint#mode"`
	GoVetAutosave           int64    `eval:"g:go#lint#govet#autosave"`
	GoVetFlags              []string `eval:"g:go#lint#govet#flags"`
	GoVetIgnore             []string `eval:"g:go#lint#govet#ignore"`
	MetalinterAutosave      int64    `eval:"g:go#lint#metalinter#autosave"`
	MetalinterAutosaveTools []string `eval:"g:go#lint#metalinter#autosave#tools"`
	MetalinterTools         []string `eval:"g:go#lint#metalinter#tools"`
	MetalinterDeadline      string   `eval:"g:go#lint#metalinter#deadline"`
	MetalinterSkipDir       []string `eval:"g:go#lint#metalinter#skip_dir"`
}

// rename represents a GoRename command config variable.
type rename struct {
	Prefill int64 `eval:"g:go#rename#prefill"`
}

// terminal represents a configure of Neovim terminal buffer.
type terminal struct {
	Mode       string `eval:"g:go#terminal#mode"`
	Position   string `eval:"g:go#terminal#position"`
	Height     int64  `eval:"g:go#terminal#height"`
	Width      int64  `eval:"g:go#terminal#width"`
	StopInsert int64  `eval:"g:go#terminal#stop_insert"`
}

// Test represents a GoTest command config variables.
type test struct {
	AllPackage int64    `eval:"g:go#test#all_package"`
	Autosave   int64    `eval:"g:go#test#autosave"`
	Flags      []string `eval:"g:go#test#flags"`
}

// Debug represents a debug of nvim-go config variable.
type debug struct {
	Enable int64 `eval:"g:go#debug"`
	Pprof  int64 `eval:"g:go#debug#pprof"`
}

var (
	// ChannelID remote plugins channel id.
	ChannelID int
	// ServerName Neovim socket listen location.
	ServerName string
	// ErrorListType type of error list window.
	ErrorListType string

	// BuildAutosave call the GoBuild command automatically at during the BufWritePost.
	BuildAutosave bool
	// BuildForce builds the binary instead of fake(use ioutil.TempFiile) build.
	BuildForce bool
	// BuildFlags flag of compile tools build command.
	BuildFlags []string

	// CoverFlags flags for cover command.
	CoverFlags []string
	// CoverMode mode of cover command.
	CoverMode string

	// FmtAutosave call the GoFmt command automatically at during the BufWritePre.
	FmtAutosave bool
	// FmtMode formatting mode of Fmt command.
	FmtMode string

	// GenerateTestAllFuncs accept all functions to the GenerateTest.
	GenerateTestAllFuncs bool
	// GenerateTestExclFuncs exclude function of GenerateTest.
	GenerateTestExclFuncs string
	// GenerateTestExportedFuncs accept exported functions to the GenerateTest.
	GenerateTestExportedFuncs bool
	// GenerateTestSubTest whether the use Go subtest idiom or not.
	GenerateTestSubTest bool

	// GuruReflection use the type reflection on GoGuru commmands.
	GuruReflection bool
	// GuruKeepCursor keep the cursor focus to source buffer instead of quickfix or locationlist.
	GuruKeepCursor map[string]int64
	// GuruJumpFirst jump the first error position on GoGuru commands.
	GuruJumpFirst bool

	// IferrAutosave call the GoIferr command automatically at during the BufWritePre.
	IferrAutosave bool

	// GolintAutosave call the GoLint command automatically at during the BufWritePost.
	GolintAutosave bool
	// GolintIgnore ignore file for lint command.
	GolintIgnore []string
	// GolintMinConfidence minimum confidence of a problem to print it
	GolintMinConfidence float64
	// GolintMode mode of golint. available value are "root", "current" and "recursive".
	GolintMode string
	// GoVetAutosave call the GoVet command automatically at during the BufWritePost.
	GoVetAutosave bool
	// GoVetFlags default flags for GoVet commands
	GoVetFlags []string
	// GoVetIgnore ignore directories for go vet command.
	GoVetIgnore []string
	// MetalinterAutosave call the GoMetaLinter command automatically at during the BufWritePre.
	MetalinterAutosave bool
	// MetalinterAutosaveTools lint tool list for MetalinterAutosave.
	MetalinterAutosaveTools []string
	// MetalinterTools lint tool list for GoMetaLinter command.
	MetalinterTools []string
	// MetalinterDeadline deadline of GoMetaLinter command timeout.
	MetalinterDeadline string
	// MetalinterSkipDir skips of lint of the directory.
	MetalinterSkipDir []string

	// RenamePrefill Enable naming prefill.
	RenamePrefill bool

	// TerminalMode open the terminal window mode.
	TerminalMode string
	// TerminalPosition open the terminal window position.
	TerminalPosition string
	// TerminalHeight open the terminal window height.
	TerminalHeight int64
	// TerminalWidth open the terminal window width.
	TerminalWidth int64
	// TerminalStopInsert workaround if users set "autocmd BufEnter term://* startinsert".
	TerminalStopInsert bool

	// TestAutosave call the GoBuild command automatically at during the BufWritePost.
	TestAutosave bool
	// TestAll enable all package test on GoTest. similar "go test ./...", but ignored vendor and testdata.
	TestAll bool
	// TestFlags test command default flags.
	TestFlags []string

	// DebugEnable Enable debugging.
	DebugEnable bool
	// DebugPprof Enable net/http/pprof debugging.
	DebugPprof bool
)

// Get gets the user config variables and convert to global varialble.
func Get(v *nvim.Nvim, cfg *Config) {
	// Client
	ChannelID = cfg.Global.ChannelID
	ServerName = cfg.Global.ServerName
	ErrorListType = cfg.Global.ErrorListType

	// Build
	BuildAutosave = itob(cfg.Build.Autosave)
	BuildForce = itob(cfg.Build.Force)
	BuildFlags = cfg.Build.Flags

	// Cover
	CoverFlags = cfg.Cover.Flags
	CoverMode = cfg.Cover.Mode

	// Fmt
	FmtAutosave = itob(cfg.Fmt.Autosave)
	FmtMode = cfg.Fmt.Mode

	// Generate
	GenerateTestAllFuncs = itob(cfg.Generate.TestAllFuncs)
	GenerateTestExclFuncs = cfg.Generate.TestExclFuncs
	GenerateTestExportedFuncs = itob(cfg.Generate.TestExportedFuncs)
	GenerateTestSubTest = itob(cfg.Generate.TestSubTest)

	// Guru
	GuruReflection = itob(cfg.Guru.Reflection)
	GuruKeepCursor = cfg.Guru.KeepCursor
	GuruJumpFirst = itob(cfg.Guru.JumpFirst)

	// Iferr
	IferrAutosave = itob(cfg.Iferr.Autosave)

	// Lint
	GolintAutosave = cfg.Lint.GolintAutosave
	GolintIgnore = cfg.Lint.GolintIgnore
	GolintMinConfidence = cfg.Lint.GolintMinConfidence
	GolintMode = cfg.Lint.GolintMode
	GoVetAutosave = itob(cfg.Lint.GoVetAutosave)
	GoVetFlags = cfg.Lint.GoVetFlags
	GoVetIgnore = cfg.Lint.GoVetIgnore
	MetalinterAutosave = itob(cfg.Lint.MetalinterAutosave)
	MetalinterAutosaveTools = cfg.Lint.MetalinterAutosaveTools
	MetalinterTools = cfg.Lint.MetalinterTools
	MetalinterDeadline = cfg.Lint.MetalinterDeadline
	MetalinterSkipDir = cfg.Lint.MetalinterSkipDir

	// Rename
	RenamePrefill = itob(cfg.Rename.Prefill)

	// Terminal
	TerminalMode = cfg.Terminal.Mode
	TerminalPosition = cfg.Terminal.Position
	TerminalHeight = cfg.Terminal.Height
	TerminalWidth = cfg.Terminal.Width
	TerminalStopInsert = itob(cfg.Terminal.StopInsert)

	// Test
	TestAutosave = itob(cfg.Test.Autosave)
	TestAll = itob(cfg.Test.AllPackage)
	TestFlags = cfg.Test.Flags

	// Debug
	DebugEnable = itob(cfg.Debug.Enable)
	DebugPprof = itob(cfg.Debug.Pprof)
}

func itob(i int64) bool { return i != int64(0) }
<commit_msg>config: fix format of import section<commit_after>// Copyright 2016 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package config

import (
	"github.com/neovim/go-client/nvim"
)

// Config represents a config variable for nvim-go.
// Each type must be exported for plugin.HandleAutocmd Eval option.
// Also it does not support embeded type.
type Config struct {
	Global Global

	Build    build
	Cover    cover
	Fmt      fmt
	Generate generate
	Guru     guru
	Iferr    iferr
	Lint     lint
	Rename   rename
	Terminal terminal
	Test     test

	Debug debug
}

// Global represents a global config variable.
type Global struct {
	ChannelID     int
	ServerName    string `eval:"v:servername"`
	ErrorListType string `eval:"g:go#global#errorlisttype"`
}

// build GoBuild command config variable.
type build struct {
	Autosave int64    `eval:"g:go#build#autosave"`
	Force    int64    `eval:"g:go#build#force"`
	Flags    []string `eval:"g:go#build#flags"`
}

type cover struct {
	Flags []string `eval:"g:go#cover#flags"`
	Mode  string   `eval:"g:go#cover#mode"`
}

// fmt represents a GoFmt command config variable.
type fmt struct {
	Autosave int64  `eval:"g:go#fmt#autosave"`
	Mode     string `eval:"g:go#fmt#mode"`
}

// generate represents a GoGenerate command config variables.
type generate struct {
	TestAllFuncs      int64  `eval:"g:go#generate#test#allfuncs"`
	TestExclFuncs     string `eval:"g:go#generate#test#exclude"`
	TestExportedFuncs int64  `eval:"g:go#generate#test#exportedfuncs"`
	TestSubTest       int64  `eval:"g:go#generate#test#subtest"`
}

// guru represents a GoGuru command config variable.
type guru struct {
	Reflection int64            `eval:"g:go#guru#reflection"`
	KeepCursor map[string]int64 `eval:"g:go#guru#keep_cursor"`
	JumpFirst  int64            `eval:"g:go#guru#jump_first"`
}

// iferr represents a GoIferr command config variable.
type iferr struct {
	Autosave int64 `eval:"g:go#iferr#autosave"`
}

// lint represents a code lint commands config variable.
type lint struct {
	GolintAutosave          bool     `eval:"g:go#lint#golint#autosave"`
	GolintIgnore            []string `eval:"g:go#lint#golint#ignore"`
	GolintMinConfidence     float64  `eval:"g:go#lint#golint#min_confidence"`
	GolintMode              string   `eval:"g:go#lint#golint#mode"`
	GoVetAutosave           int64    `eval:"g:go#lint#govet#autosave"`
	GoVetFlags              []string `eval:"g:go#lint#govet#flags"`
	GoVetIgnore             []string `eval:"g:go#lint#govet#ignore"`
	MetalinterAutosave      int64    `eval:"g:go#lint#metalinter#autosave"`
	MetalinterAutosaveTools []string `eval:"g:go#lint#metalinter#autosave#tools"`
	MetalinterTools         []string `eval:"g:go#lint#metalinter#tools"`
	MetalinterDeadline      string   `eval:"g:go#lint#metalinter#deadline"`
	MetalinterSkipDir       []string `eval:"g:go#lint#metalinter#skip_dir"`
}

// rename represents a GoRename command config variable.
type rename struct {
	Prefill int64 `eval:"g:go#rename#prefill"`
}

// terminal represents a configure of Neovim terminal buffer.
type terminal struct {
	Mode       string `eval:"g:go#terminal#mode"`
	Position   string `eval:"g:go#terminal#position"`
	Height     int64  `eval:"g:go#terminal#height"`
	Width      int64  `eval:"g:go#terminal#width"`
	StopInsert int64  `eval:"g:go#terminal#stop_insert"`
}

// Test represents a GoTest command config variables.
type test struct {
	AllPackage int64    `eval:"g:go#test#all_package"`
	Autosave   int64    `eval:"g:go#test#autosave"`
	Flags      []string `eval:"g:go#test#flags"`
}

// Debug represents a debug of nvim-go config variable.
type debug struct {
	Enable int64 `eval:"g:go#debug"`
	Pprof  int64 `eval:"g:go#debug#pprof"`
}

var (
	// ChannelID remote plugins channel id.
	ChannelID int
	// ServerName Neovim socket listen location.
	ServerName string
	// ErrorListType type of error list window.
	ErrorListType string

	// BuildAutosave call the GoBuild command automatically at during the BufWritePost.
	BuildAutosave bool
	// BuildForce builds the binary instead of fake(use ioutil.TempFiile) build.
	BuildForce bool
	// BuildFlags flag of compile tools build command.
	BuildFlags []string

	// CoverFlags flags for cover command.
	CoverFlags []string
	// CoverMode mode of cover command.
	CoverMode string

	// FmtAutosave call the GoFmt command automatically at during the BufWritePre.
	FmtAutosave bool
	// FmtMode formatting mode of Fmt command.
	FmtMode string

	// GenerateTestAllFuncs accept all functions to the GenerateTest.
	GenerateTestAllFuncs bool
	// GenerateTestExclFuncs exclude function of GenerateTest.
	GenerateTestExclFuncs string
	// GenerateTestExportedFuncs accept exported functions to the GenerateTest.
	GenerateTestExportedFuncs bool
	// GenerateTestSubTest whether the use Go subtest idiom or not.
	GenerateTestSubTest bool

	// GuruReflection use the type reflection on GoGuru commmands.
	GuruReflection bool
	// GuruKeepCursor keep the cursor focus to source buffer instead of quickfix or locationlist.
	GuruKeepCursor map[string]int64
	// GuruJumpFirst jump the first error position on GoGuru commands.
	GuruJumpFirst bool

	// IferrAutosave call the GoIferr command automatically at during the BufWritePre.
	IferrAutosave bool

	// GolintAutosave call the GoLint command automatically at during the BufWritePost.
	GolintAutosave bool
	// GolintIgnore ignore file for lint command.
	GolintIgnore []string
	// GolintMinConfidence minimum confidence of a problem to print it
	GolintMinConfidence float64
	// GolintMode mode of golint. available value are "root", "current" and "recursive".
	GolintMode string
	// GoVetAutosave call the GoVet command automatically at during the BufWritePost.
	GoVetAutosave bool
	// GoVetFlags default flags for GoVet commands
	GoVetFlags []string
	// GoVetIgnore ignore directories for go vet command.
	GoVetIgnore []string
	// MetalinterAutosave call the GoMetaLinter command automatically at during the BufWritePre.
	MetalinterAutosave bool
	// MetalinterAutosaveTools lint tool list for MetalinterAutosave.
	MetalinterAutosaveTools []string
	// MetalinterTools lint tool list for GoMetaLinter command.
	MetalinterTools []string
	// MetalinterDeadline deadline of GoMetaLinter command timeout.
	MetalinterDeadline string
	// MetalinterSkipDir skips of lint of the directory.
	MetalinterSkipDir []string

	// RenamePrefill Enable naming prefill.
	RenamePrefill bool

	// TerminalMode open the terminal window mode.
	TerminalMode string
	// TerminalPosition open the terminal window position.
	TerminalPosition string
	// TerminalHeight open the terminal window height.
	TerminalHeight int64
	// TerminalWidth open the terminal window width.
	TerminalWidth int64
	// TerminalStopInsert workaround if users set "autocmd BufEnter term://* startinsert".
	TerminalStopInsert bool

	// TestAutosave call the GoBuild command automatically at during the BufWritePost.
	TestAutosave bool
	// TestAll enable all package test on GoTest. similar "go test ./...", but ignored vendor and testdata.
	TestAll bool
	// TestFlags test command default flags.
	TestFlags []string

	// DebugEnable Enable debugging.
	DebugEnable bool
	// DebugPprof Enable net/http/pprof debugging.
	DebugPprof bool
)

// Get gets the user config variables and convert to global varialble.
func Get(v *nvim.Nvim, cfg *Config) {
	// Client
	ChannelID = cfg.Global.ChannelID
	ServerName = cfg.Global.ServerName
	ErrorListType = cfg.Global.ErrorListType

	// Build
	BuildAutosave = itob(cfg.Build.Autosave)
	BuildForce = itob(cfg.Build.Force)
	BuildFlags = cfg.Build.Flags

	// Cover
	CoverFlags = cfg.Cover.Flags
	CoverMode = cfg.Cover.Mode

	// Fmt
	FmtAutosave = itob(cfg.Fmt.Autosave)
	FmtMode = cfg.Fmt.Mode

	// Generate
	GenerateTestAllFuncs = itob(cfg.Generate.TestAllFuncs)
	GenerateTestExclFuncs = cfg.Generate.TestExclFuncs
	GenerateTestExportedFuncs = itob(cfg.Generate.TestExportedFuncs)
	GenerateTestSubTest = itob(cfg.Generate.TestSubTest)

	// Guru
	GuruReflection = itob(cfg.Guru.Reflection)
	GuruKeepCursor = cfg.Guru.KeepCursor
	GuruJumpFirst = itob(cfg.Guru.JumpFirst)

	// Iferr
	IferrAutosave = itob(cfg.Iferr.Autosave)

	// Lint
	GolintAutosave = cfg.Lint.GolintAutosave
	GolintIgnore = cfg.Lint.GolintIgnore
	GolintMinConfidence = cfg.Lint.GolintMinConfidence
	GolintMode = cfg.Lint.GolintMode
	GoVetAutosave = itob(cfg.Lint.GoVetAutosave)
	GoVetFlags = cfg.Lint.GoVetFlags
	GoVetIgnore = cfg.Lint.GoVetIgnore
	MetalinterAutosave = itob(cfg.Lint.MetalinterAutosave)
	MetalinterAutosaveTools = cfg.Lint.MetalinterAutosaveTools
	MetalinterTools = cfg.Lint.MetalinterTools
	MetalinterDeadline = cfg.Lint.MetalinterDeadline
	MetalinterSkipDir = cfg.Lint.MetalinterSkipDir

	// Rename
	RenamePrefill = itob(cfg.Rename.Prefill)

	// Terminal
	TerminalMode = cfg.Terminal.Mode
	TerminalPosition = cfg.Terminal.Position
	TerminalHeight = cfg.Terminal.Height
	TerminalWidth = cfg.Terminal.Width
	TerminalStopInsert = itob(cfg.Terminal.StopInsert)

	// Test
	TestAutosave = itob(cfg.Test.Autosave)
	TestAll = itob(cfg.Test.AllPackage)
	TestFlags = cfg.Test.Flags

	// Debug
	DebugEnable = itob(cfg.Debug.Enable)
	DebugPprof = itob(cfg.Debug.Pprof)
}

func itob(i int64) bool { return i != int64(0) }
<|endoftext|>


<commit_before>package params

import (
	"errors"
	"strings"
)

type flagParam struct {
	name, description           string
	defaultVal, cliVal, confVal *bool
	finalVal                    bool
}

func NewFlag(name, description string, def bool) *flagParam {
	defVal := new(bool)
	*defVal = def
	return &flagParam{
		name:        name,
		description: description,
		defaultVal:  defVal,
	}
}

func (p *flagParam) Name() string {
	return p.name
}

func (p *flagParam) Description() string {
	return p.description
}

func (p *flagParam) DefaultAsStrings() ([]string, bool) {
	if p.defaultVal != nil {
		if *p.defaultVal {
			return []string{"true"}, true
		} else {
			return []string{"false"}, true
		}
	} else {
		return nil, false
	}
}

func (p *flagParam) CLAHasValue() bool {
	return false
}

func (p *flagParam) CLA(v string) {
	p.cliVal = new(bool)
	*p.cliVal = !*p.defaultVal
}

func (p *flagParam) ConfFile(val string) {
	valL := strings.ToLower(val)
	p.confVal = new(bool)
	if valL == "true" {
		*p.confVal = true
	} else {
		*p.confVal = false
	}
}

func (p *flagParam) Post() error {
	if p.cliVal != nil {
		p.finalVal = *p.cliVal
	} else if p.confVal != nil {
		p.finalVal = *p.confVal
	} else if p.defaultVal != nil {
		p.finalVal = *p.defaultVal
	} else {
		return errors.New("parameter required but no valid value given")
	}

	return nil
}

func (p *flagParam) Value() interface{} {
	return p.finalVal
}
<commit_msg>made bool flags not show a default value<commit_after>package params

import (
	"errors"
	"strings"
)

type flagParam struct {
	name, description           string
	defaultVal, cliVal, confVal *bool
	finalVal                    bool
}

func NewFlag(name, description string, def bool) *flagParam {
	defVal := new(bool)
	*defVal = def
	return &flagParam{
		name:        name,
		description: description,
		defaultVal:  defVal,
	}
}

func (p *flagParam) Name() string {
	return p.name
}

func (p *flagParam) Description() string {
	return p.description
}

func (p *flagParam) DefaultAsStrings() ([]string, bool) {
	if p.defaultVal != nil {
		return []string{""}, true
	} else {
		return nil, false
	}
}

func (p *flagParam) CLAHasValue() bool {
	return false
}

func (p *flagParam) CLA(v string) {
	p.cliVal = new(bool)
	*p.cliVal = !*p.defaultVal
}

func (p *flagParam) ConfFile(val string) {
	valL := strings.ToLower(val)
	p.confVal = new(bool)
	if valL == "true" {
		*p.confVal = true
	} else {
		*p.confVal = false
	}
}

func (p *flagParam) Post() error {
	if p.cliVal != nil {
		p.finalVal = *p.cliVal
	} else if p.confVal != nil {
		p.finalVal = *p.confVal
	} else if p.defaultVal != nil {
		p.finalVal = *p.defaultVal
	} else {
		return errors.New("parameter required but no valid value given")
	}

	return nil
}

func (p *flagParam) Value() interface{} {
	return p.finalVal
}
<|endoftext|>


<commit_before>package fluidbackup

import "testing"
import "time"
import "io/ioutil"
import "math/rand"
import "os"
import "bytes"

func TestBasic(t *testing.T) {
	fileContents := make([]byte, 1500 * 1024)
	for i := range fileContents {
		fileContents[i] = byte(rand.Int() % 256)
	}
	err := ioutil.WriteFile("orange.txt", fileContents, 0644)
	if err != nil {
		t.Error("failed to write random contents to orange.txt: " + err.Error())
	}

	mainPort := 19838
	bMain := MakeFluidBackup(mainPort)
	bMain.fileStore.RegisterFile("orange.txt")
	bOther := make([]*FluidBackup, 11)
	for i := range bOther {
		port := 19839 + i
		bOther[i] = MakeFluidBackup(port)

		if i < 5 {
			bMain.peerList.DiscoveredPeer(PeerId{Address: "127.0.0.1", Port: port})
		} else {
			bOther[i].peerList.DiscoveredPeer(PeerId{Address: "127.0.0.1", Port: mainPort});
		}
	}

	time.Sleep(5 * time.Second)

	err = os.Remove("orange.txt")

	if err != nil {
		t.Error("Encountered error while removing file: " + err.Error())
		return
	}

	bMain.fileStore.RecoverFile("orange.txt")
	newFileContents, err := ioutil.ReadFile("orange.txt")
	if err != nil {
		t.Error("failed to read from recovered orange.txt: " + err.Error())
	}

	if !bytes.Equal(fileContents, newFileContents) {
		t.Error("recovered file does not match original")
	}
}

func TestStable(t *testing.T) {
	fileContents := make([]byte, 1500 * 1024)
	for i := range fileContents {
		fileContents[i] = byte(rand.Int() % 256)
	}
	err := ioutil.WriteFile("orange.txt", fileContents, 0644)
	if err != nil {
		t.Error("failed to write random contents to orange.txt: " + err.Error())
	}

	mainPort := 19838
	bMain := MakeFluidBackup(mainPort)
	bMain.fileStore.RegisterFile("orange.txt")
	bOther := make([]*FluidBackup, 11)
	for i := range bOther {
		port := 19839 + i
		bOther[i] = MakeFluidBackup(port)

		if i < 5 {
			bMain.peerList.DiscoveredPeer(PeerId{Address: "127.0.0.1", Port: port})
		} else {
			bOther[i].peerList.DiscoveredPeer(PeerId{Address: "127.0.0.1", Port: mainPort});
		}
	}

	time.Sleep(5 * time.Second)

	err = os.Remove("orange.txt")

	if err != nil {
		t.Error("Encountered error while removing file: " + err.Error())
		return
	}

	bMain.Save()
	bMain.Stop()
	time.Sleep(5 * time.Second)
	bMain = MakeFluidBackup(mainPort)
	bMain.Load()

	bMain.fileStore.RecoverFile("orange.txt")
	newFileContents, err := ioutil.ReadFile("orange.txt")
	if err != nil {
		t.Error("failed to read from recovered orange.txt: " + err.Error())
	}

	if !bytes.Equal(fileContents, newFileContents) {
		t.Error("recovered file does not match original")
	}
}
<commit_msg>Shut down FluidBackup instances after tests to avoid port conflicts.<commit_after>package fluidbackup

import "testing"
import "time"
import "io/ioutil"
import "math/rand"
import "os"
import "bytes"

func TestBasic(t *testing.T) {
	fileContents := make([]byte, 1500 * 1024)
	for i := range fileContents {
		fileContents[i] = byte(rand.Int() % 256)
	}
	err := ioutil.WriteFile("orange.txt", fileContents, 0644)
	if err != nil {
		t.Error("failed to write random contents to orange.txt: " + err.Error())
	}

	mainPort := 19838
	bMain := MakeFluidBackup(mainPort)
	bMain.fileStore.RegisterFile("orange.txt")
	bOther := make([]*FluidBackup, 11)
	for i := range bOther {
		port := 19839 + i
		bOther[i] = MakeFluidBackup(port)

		if i < 5 {
			bMain.peerList.DiscoveredPeer(PeerId{Address: "127.0.0.1", Port: port})
		} else {
			bOther[i].peerList.DiscoveredPeer(PeerId{Address: "127.0.0.1", Port: mainPort});
		}
	}

	time.Sleep(5 * time.Second)

	err = os.Remove("orange.txt")

	if err != nil {
		t.Error("Encountered error while removing file: " + err.Error())
		return
	}

	bMain.fileStore.RecoverFile("orange.txt")
	newFileContents, err := ioutil.ReadFile("orange.txt")
	if err != nil {
		t.Error("failed to read from recovered orange.txt: " + err.Error())
	}

	if !bytes.Equal(fileContents, newFileContents) {
		t.Error("recovered file does not match original")
	}

	// shut down fluidbackup instances
	bMain.Stop()
	for _, inst := range bOther {
		inst.Stop()
	}
}

func TestStable(t *testing.T) {
	fileContents := make([]byte, 1500 * 1024)
	for i := range fileContents {
		fileContents[i] = byte(rand.Int() % 256)
	}
	err := ioutil.WriteFile("orange.txt", fileContents, 0644)
	if err != nil {
		t.Error("failed to write random contents to orange.txt: " + err.Error())
	}

	mainPort := 19838
	bMain := MakeFluidBackup(mainPort)
	bMain.fileStore.RegisterFile("orange.txt")
	bOther := make([]*FluidBackup, 11)
	for i := range bOther {
		port := 19839 + i
		bOther[i] = MakeFluidBackup(port)

		if i < 5 {
			bMain.peerList.DiscoveredPeer(PeerId{Address: "127.0.0.1", Port: port})
		} else {
			bOther[i].peerList.DiscoveredPeer(PeerId{Address: "127.0.0.1", Port: mainPort});
		}
	}

	time.Sleep(5 * time.Second)

	err = os.Remove("orange.txt")

	if err != nil {
		t.Error("Encountered error while removing file: " + err.Error())
		return
	}

	bMain.Save()
	bMain.Stop()
	time.Sleep(5 * time.Second)
	bMain = MakeFluidBackup(mainPort)
	bMain.Load()

	bMain.fileStore.RecoverFile("orange.txt")
	newFileContents, err := ioutil.ReadFile("orange.txt")
	if err != nil {
		t.Error("failed to read from recovered orange.txt: " + err.Error())
	}

	if !bytes.Equal(fileContents, newFileContents) {
		t.Error("recovered file does not match original")
	}

	// shut down fluidbackup instances
	bMain.Stop()
	for _, inst := range bOther {
		inst.Stop()
	}
}
<|endoftext|>


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

package net

import (
	"io"
	"errors"
	"os"
)

func query(filename, query string, bufSize int) (res []string, err error) {
	file, err := os.OpenFile(filename, os.O_RDWR, 0)
	if err != nil {
		return
	}
	defer file.Close()

	_, err = file.Seek(0, 0)
	if err != nil {
		return
	}
	_, err = file.WriteString(query)
	if err != nil {
		return
	}
	_, err = file.Seek(0, 0)
	if err != nil {
		return
	}
	buf := make([]byte, bufSize)
	for {
		n, err := file.Read(buf)
		if err != nil {
			if err == io.EOF {
				if len(res) == 0 {
					return res, errors.New("net: query lookup failed, filename: " + filename + ", query: " + query)
				}
				break
			}
			return res, err
		}
		res = append(res, string(buf[:n]))
	}
	return
}

func queryCS(net, host, service string) (res []string, err error) {
	switch net {
	case "tcp4", "tcp6":
		net = "tcp"
	case "udp4", "udp6":
		net = "udp"
	}
	if host == "" {
		host = "*"
	}
	return query(os.Nsprefix+netdir+"/cs", net+"!"+host+"!"+service, 128)
}

func queryCS1(net string, ip IP, port int) (clone, dest string, err error) {
	ips := "*"
	if len(ip) != 0 && !ip.IsUnspecified() {
		ips = ip.String()
	}
	lines, err := queryCS(net, ips, itoa(port))
	if err != nil {
		return
	}
	f := getFields(lines[0])
	if len(f) < 2 {
		return "", "", errors.New("net: bad response from ndb/cs")
	}
	clone, dest = f[0], f[1]
	return
}

func queryDNS(addr string, typ string) (res []string, err error) {
	return query(os.Nsprefix+netdir+"/dns", addr+" "+typ, 1024)
}

// toLower returns a lower-case version of in. Restricting us to
// ASCII is sufficient to handle the IP protocol names and allow
// us to not depend on the strings and unicode packages.
func toLower(in string) string {
	for _, c := range in {
		if 'A' <= c && c <= 'Z' {
			// Has upper case; need to fix.
			out := []byte(in)
			for i := 0; i < len(in); i++ {
				c := in[i]
				if 'A' <= c && c <= 'Z' {
					c += 'a' - 'A'
				}
				out[i] = c
			}
			return string(out)
		}
	}
	return in
}

// lookupProtocol looks up IP protocol name and returns
// the corresponding protocol number.
func lookupProtocol(name string) (proto int, err error) {
	lines, err := query(os.Nsprefix+netdir+"/cs", "!protocol="+toLower(name), 128)
	if err != nil {
		return 0, err
	}
	unknownProtoError := errors.New("unknown IP protocol specified: " + name)
	if len(lines) == 0 {
		return 0, unknownProtoError
	}
	f := getFields(lines[0])
	if len(f) < 2 {
		return 0, unknownProtoError
	}
	s := f[1]
	if n, _, ok := dtoi(s, byteIndex(s, '=')+1); ok {
		return n, nil
	}
	return 0, unknownProtoError
}

func lookupHost(host string) (addrs []string, err error) {
	// Use netdir/cs instead of netdir/dns because cs knows about
	// host names in local network (e.g. from /lib/ndb/local)
	lines, err := queryCS("net", host, "1")
	if err != nil {
		return
	}
loop:
	for _, line := range lines {
		f := getFields(line)
		if len(f) < 2 {
			continue
		}
		addr := f[1]
		if i := byteIndex(addr, '!'); i >= 0 {
			addr = addr[:i] // remove port
		}
		if ParseIP(addr) == nil {
			continue
		}
		// only return unique addresses
		for _, a := range addrs {
			if a == addr {
				continue loop
			}
		}
		addrs = append(addrs, addr)
	}
	return
}

func lookupIP(host string) (ips []IP, err error) {
	addrs, err := LookupHost(host)
	if err != nil {
		return
	}
	for _, addr := range addrs {
		if ip := ParseIP(addr); ip != nil {
			ips = append(ips, ip)
		}
	}
	return
}

func lookupPort(network, service string) (port int, err error) {
	switch network {
	case "tcp4", "tcp6":
		network = "tcp"
	case "udp4", "udp6":
		network = "udp"
	}
	lines, err := queryCS(network, "127.0.0.1", service)
	if err != nil {
		return
	}
	unknownPortError := &AddrError{"unknown port", network + "/" + service}
	if len(lines) == 0 {
		return 0, unknownPortError
	}
	f := getFields(lines[0])
	if len(f) < 2 {
		return 0, unknownPortError
	}
	s := f[1]
	if i := byteIndex(s, '!'); i >= 0 {
		s = s[i+1:] // remove address
	}
	if n, _, ok := dtoi(s, 0); ok {
		return n, nil
	}
	return 0, unknownPortError
}

func lookupCNAME(name string) (cname string, err error) {
	lines, err := queryDNS(name, "cname")
	if err != nil {
		return
	}
	if len(lines) > 0 {
		if f := getFields(lines[0]); len(f) >= 3 {
			return f[2] + ".", nil
		}
	}
	return "", errors.New("net: bad response from ndb/dns")
}

func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
	var target string
	if service == "" && proto == "" {
		target = name
	} else {
		target = "_" + service + "._" + proto + "." + name
	}
	lines, err := queryDNS(target, "srv")
	if err != nil {
		return
	}
	for _, line := range lines {
		f := getFields(line)
		if len(f) < 6 {
			continue
		}
		port, _, portOk := dtoi(f[4], 0)
		priority, _, priorityOk := dtoi(f[3], 0)
		weight, _, weightOk := dtoi(f[2], 0)
		if !(portOk && priorityOk && weightOk) {
			continue
		}
		addrs = append(addrs, &SRV{f[5], uint16(port), uint16(priority), uint16(weight)})
		cname = f[0]
	}
	byPriorityWeight(addrs).sort()
	return
}

func lookupMX(name string) (mx []*MX, err error) {
	lines, err := queryDNS(name, "mx")
	if err != nil {
		return
	}
	for _, line := range lines {
		f := getFields(line)
		if len(f) < 4 {
			continue
		}
		if pref, _, ok := dtoi(f[2], 0); ok {
			mx = append(mx, &MX{f[3], uint16(pref)})
		}
	}
	byPref(mx).sort()
	return
}

func lookupNS(name string) (ns []*NS, err error) {
	lines, err := queryDNS(name, "ns")
	if err != nil {
		return
	}
	for _, line := range lines {
		f := getFields(line)
		if len(f) < 3 {
			continue
		}
		ns = append(ns, &NS{f[2]})
	}
	return
}

func lookupTXT(name string) (txt []string, err error) {
	lines, err := queryDNS(name, "txt")
	if err != nil {
		return
	}
	for _, line := range lines {
		if i := byteIndex(line, '\t'); i >= 0 {
			txt = append(txt, line[i+1:])
		}
	}
	return
}

func lookupAddr(addr string) (name []string, err error) {
	arpa, err := reverseaddr(addr)
	if err != nil {
		return
	}
	lines, err := queryDNS(arpa, "ptr")
	if err != nil {
		return
	}
	for _, line := range lines {
		f := getFields(line)
		if len(f) < 3 {
			continue
		}
		name = append(name, f[2])
	}
	return
}
<commit_msg>Bypass CS when ip addr and port are already parsed<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package net

import (
	"io"
	"errors"
	"os"
	"strconv"
)

func query(filename, query string, bufSize int) (res []string, err error) {
	file, err := os.OpenFile(filename, os.O_RDWR, 0)
	if err != nil {
		return
	}
	defer file.Close()

	_, err = file.Seek(0, 0)
	if err != nil {
		return
	}
	_, err = file.WriteString(query)
	if err != nil {
		return
	}
	_, err = file.Seek(0, 0)
	if err != nil {
		return
	}
	buf := make([]byte, bufSize)
	for {
		n, err := file.Read(buf)
		if err != nil {
			if err == io.EOF {
				if len(res) == 0 {
					return res, errors.New("net: query lookup failed, filename: " + filename + ", query: " + query)
				}
				break
			}
			return res, err
		}
		res = append(res, string(buf[:n]))
	}
	return
}

/* If host and service are numeric, bypass cs.. */
func bypassCS(host, service string) bool {
	ip := ParseIP(host);
	_, err := strconv.Atoi(service);
	return (ip != nil && err == nil)
}

func queryCS(net, host, service string) (res []string, err error) {
	switch net {
	case "tcp4", "tcp6":
		net = "tcp"
	case "udp4", "udp6":
		net = "udp"
	}
	if host == "" {
		host = "*"
	}
	if bypassCS(host, service) {
		return []string{"/net/"+net+"/clone "+host+"!"+service}, nil
	}
	return query(os.Nsprefix+netdir+"/cs", net+"!"+host+"!"+service, 128)
}

func queryCS1(net string, ip IP, port int) (clone, dest string, err error) {
	ips := "*"
	if len(ip) != 0 && !ip.IsUnspecified() {
		ips = ip.String()
	}
	lines, err := queryCS(net, ips, itoa(port))
	if err != nil {
		return
	}
	f := getFields(lines[0])
	if len(f) < 2 {
		return "", "", errors.New("net: bad response from ndb/cs")
	}
	clone, dest = f[0], f[1]
	return
}

func queryDNS(addr string, typ string) (res []string, err error) {
	return query(os.Nsprefix+netdir+"/dns", addr+" "+typ, 1024)
}

// toLower returns a lower-case version of in. Restricting us to
// ASCII is sufficient to handle the IP protocol names and allow
// us to not depend on the strings and unicode packages.
func toLower(in string) string {
	for _, c := range in {
		if 'A' <= c && c <= 'Z' {
			// Has upper case; need to fix.
			out := []byte(in)
			for i := 0; i < len(in); i++ {
				c := in[i]
				if 'A' <= c && c <= 'Z' {
					c += 'a' - 'A'
				}
				out[i] = c
			}
			return string(out)
		}
	}
	return in
}

// lookupProtocol looks up IP protocol name and returns
// the corresponding protocol number.
func lookupProtocol(name string) (proto int, err error) {
	lines, err := query(os.Nsprefix+netdir+"/cs", "!protocol="+toLower(name), 128)
	if err != nil {
		return 0, err
	}
	unknownProtoError := errors.New("unknown IP protocol specified: " + name)
	if len(lines) == 0 {
		return 0, unknownProtoError
	}
	f := getFields(lines[0])
	if len(f) < 2 {
		return 0, unknownProtoError
	}
	s := f[1]
	if n, _, ok := dtoi(s, byteIndex(s, '=')+1); ok {
		return n, nil
	}
	return 0, unknownProtoError
}

func lookupHost(host string) (addrs []string, err error) {
	// Use netdir/cs instead of netdir/dns because cs knows about
	// host names in local network (e.g. from /lib/ndb/local)
	lines, err := queryCS("net", host, "1")
	if err != nil {
		return
	}
loop:
	for _, line := range lines {
		f := getFields(line)
		if len(f) < 2 {
			continue
		}
		addr := f[1]
		if i := byteIndex(addr, '!'); i >= 0 {
			addr = addr[:i] // remove port
		}
		if ParseIP(addr) == nil {
			continue
		}
		// only return unique addresses
		for _, a := range addrs {
			if a == addr {
				continue loop
			}
		}
		addrs = append(addrs, addr)
	}
	return
}

func lookupIP(host string) (ips []IP, err error) {
	addrs, err := LookupHost(host)
	if err != nil {
		return
	}
	for _, addr := range addrs {
		if ip := ParseIP(addr); ip != nil {
			ips = append(ips, ip)
		}
	}
	return
}

func lookupPort(network, service string) (port int, err error) {
	switch network {
	case "tcp4", "tcp6":
		network = "tcp"
	case "udp4", "udp6":
		network = "udp"
	}
	lines, err := queryCS(network, "127.0.0.1", service)
	if err != nil {
		return
	}
	unknownPortError := &AddrError{"unknown port", network + "/" + service}
	if len(lines) == 0 {
		return 0, unknownPortError
	}
	f := getFields(lines[0])
	if len(f) < 2 {
		return 0, unknownPortError
	}
	s := f[1]
	if i := byteIndex(s, '!'); i >= 0 {
		s = s[i+1:] // remove address
	}
	if n, _, ok := dtoi(s, 0); ok {
		return n, nil
	}
	return 0, unknownPortError
}

func lookupCNAME(name string) (cname string, err error) {
	lines, err := queryDNS(name, "cname")
	if err != nil {
		return
	}
	if len(lines) > 0 {
		if f := getFields(lines[0]); len(f) >= 3 {
			return f[2] + ".", nil
		}
	}
	return "", errors.New("net: bad response from ndb/dns")
}

func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
	var target string
	if service == "" && proto == "" {
		target = name
	} else {
		target = "_" + service + "._" + proto + "." + name
	}
	lines, err := queryDNS(target, "srv")
	if err != nil {
		return
	}
	for _, line := range lines {
		f := getFields(line)
		if len(f) < 6 {
			continue
		}
		port, _, portOk := dtoi(f[4], 0)
		priority, _, priorityOk := dtoi(f[3], 0)
		weight, _, weightOk := dtoi(f[2], 0)
		if !(portOk && priorityOk && weightOk) {
			continue
		}
		addrs = append(addrs, &SRV{f[5], uint16(port), uint16(priority), uint16(weight)})
		cname = f[0]
	}
	byPriorityWeight(addrs).sort()
	return
}

func lookupMX(name string) (mx []*MX, err error) {
	lines, err := queryDNS(name, "mx")
	if err != nil {
		return
	}
	for _, line := range lines {
		f := getFields(line)
		if len(f) < 4 {
			continue
		}
		if pref, _, ok := dtoi(f[2], 0); ok {
			mx = append(mx, &MX{f[3], uint16(pref)})
		}
	}
	byPref(mx).sort()
	return
}

func lookupNS(name string) (ns []*NS, err error) {
	lines, err := queryDNS(name, "ns")
	if err != nil {
		return
	}
	for _, line := range lines {
		f := getFields(line)
		if len(f) < 3 {
			continue
		}
		ns = append(ns, &NS{f[2]})
	}
	return
}

func lookupTXT(name string) (txt []string, err error) {
	lines, err := queryDNS(name, "txt")
	if err != nil {
		return
	}
	for _, line := range lines {
		if i := byteIndex(line, '\t'); i >= 0 {
			txt = append(txt, line[i+1:])
		}
	}
	return
}

func lookupAddr(addr string) (name []string, err error) {
	arpa, err := reverseaddr(addr)
	if err != nil {
		return
	}
	lines, err := queryDNS(arpa, "ptr")
	if err != nil {
		return
	}
	for _, line := range lines {
		f := getFields(line)
		if len(f) < 3 {
			continue
		}
		name = append(name, f[2])
	}
	return
}
<|endoftext|>


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

// Routing sockets and messages

package syscall

import (
	"unsafe"
)

// Round the length of a raw sockaddr up to align it properly.
func rsaAlignOf(salen int) int {
	salign := sizeofPtr
	// NOTE: It seems like 64-bit Darwin kernel still requires 32-bit
	// aligned access to BSD subsystem.
	if darwinAMD64 {
		salign = 4
	}
	if salen == 0 {
		return salign
	}
	return (salen + salign - 1) & ^(salign - 1)
}

// RouteRIB returns routing information base, as known as RIB,
// which consists of network facility information, states and
// parameters.
func RouteRIB(facility, param int) ([]byte, int) {
	var (
		tab []byte
		e   int
	)

	mib := []_C_int{CTL_NET, AF_ROUTE, 0, 0, _C_int(facility), _C_int(param)}

	// Find size.
	n := uintptr(0)
	if e = sysctl(mib, nil, &n, nil, 0); e != 0 {
		return nil, e
	}
	if n == 0 {
		return nil, 0
	}

	tab = make([]byte, n)
	if e = sysctl(mib, &tab[0], &n, nil, 0); e != 0 {
		return nil, e
	}

	return tab[:n], 0
}

// RoutingMessage represents a routing message.
type RoutingMessage interface {
	sockaddr() []Sockaddr
}

const anyMessageLen = int(unsafe.Sizeof(anyMessage{}))

type anyMessage struct {
	Msglen  uint16
	Version uint8
	Type    uint8
}

// RouteMessage represents a routing message containing routing
// entries.
type RouteMessage struct {
	Header RtMsghdr
	Data   []byte
}

func (m *RouteMessage) sockaddr() (sas []Sockaddr) {
	// TODO: implement this in the near future
	return nil
}

// InterfaceMessage represents a routing message containing
// network interface entries.
type InterfaceMessage struct {
	Header IfMsghdr
	Data   []byte
}

func (m *InterfaceMessage) sockaddr() (sas []Sockaddr) {
	if m.Header.Addrs&RTA_IFP == 0 {
		return nil
	}
	sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(&m.Data[0])))
	if e != 0 {
		return nil
	}
	return append(sas, sa)
}

// InterfaceAddrMessage represents a routing message containing
// network interface address entries.
type InterfaceAddrMessage struct {
	Header IfaMsghdr
	Data   []byte
}

const rtaIfaMask = RTA_IFA | RTA_NETMASK | RTA_BRD

func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) {
	if m.Header.Addrs&rtaIfaMask == 0 {
		return nil
	}

	buf := m.Data[:]
	for i := uint(0); i < RTAX_MAX; i++ {
		if m.Header.Addrs&rtaIfaMask&(1<<i) == 0 {
			continue
		}
		rsa := (*RawSockaddr)(unsafe.Pointer(&buf[0]))
		switch i {
		case RTAX_IFA:
			sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
			if e != 0 {
				return nil
			}
			sas = append(sas, sa)
		case RTAX_NETMASK, RTAX_BRD:
			// nothing to do
		}
		buf = buf[rsaAlignOf(int(rsa.Len)):]
	}

	return sas
}

// ParseRoutingMessage parses buf as routing messages and returns
// the slice containing the RoutingMessage interfaces.
func ParseRoutingMessage(buf []byte) (msgs []RoutingMessage, errno int) {
	for len(buf) >= anyMessageLen {
		any := (*anyMessage)(unsafe.Pointer(&buf[0]))
		if any.Version != RTM_VERSION {
			return nil, EINVAL
		}
		msgs = append(msgs, any.toRoutingMessage(buf))
		buf = buf[any.Msglen:]
	}
	return msgs, 0
}

// ParseRoutingMessage parses msg's payload as raw sockaddrs and
// returns the slice containing the Sockaddr interfaces.
func ParseRoutingSockaddr(msg RoutingMessage) (sas []Sockaddr, errno int) {
	return append(sas, msg.sockaddr()...), 0
}
<commit_msg>syscall: handle routing entry in ParseRoutingSockaddr on BSD variants<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Routing sockets and messages

package syscall

import (
	"unsafe"
)

// Round the length of a raw sockaddr up to align it properly.
func rsaAlignOf(salen int) int {
	salign := sizeofPtr
	// NOTE: It seems like 64-bit Darwin kernel still requires 32-bit
	// aligned access to BSD subsystem.
	if darwinAMD64 {
		salign = 4
	}
	if salen == 0 {
		return salign
	}
	return (salen + salign - 1) & ^(salign - 1)
}

// RouteRIB returns routing information base, as known as RIB,
// which consists of network facility information, states and
// parameters.
func RouteRIB(facility, param int) ([]byte, int) {
	var (
		tab []byte
		e   int
	)

	mib := []_C_int{CTL_NET, AF_ROUTE, 0, 0, _C_int(facility), _C_int(param)}

	// Find size.
	n := uintptr(0)
	if e = sysctl(mib, nil, &n, nil, 0); e != 0 {
		return nil, e
	}
	if n == 0 {
		return nil, 0
	}

	tab = make([]byte, n)
	if e = sysctl(mib, &tab[0], &n, nil, 0); e != 0 {
		return nil, e
	}

	return tab[:n], 0
}

// RoutingMessage represents a routing message.
type RoutingMessage interface {
	sockaddr() []Sockaddr
}

const anyMessageLen = int(unsafe.Sizeof(anyMessage{}))

type anyMessage struct {
	Msglen  uint16
	Version uint8
	Type    uint8
}

// RouteMessage represents a routing message containing routing
// entries.
type RouteMessage struct {
	Header RtMsghdr
	Data   []byte
}

const rtaRtMask = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_GENMASK

func (m *RouteMessage) sockaddr() []Sockaddr {
	var (
		af  int
		sas [4]Sockaddr
	)

	buf := m.Data[:]
	for i := uint(0); i < RTAX_MAX; i++ {
		if m.Header.Addrs&rtaRtMask&(1<<i) == 0 {
			continue
		}
		rsa := (*RawSockaddr)(unsafe.Pointer(&buf[0]))
		switch i {
		case RTAX_DST, RTAX_GATEWAY:
			sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
			if e != 0 {
				return nil
			}
			if i == RTAX_DST {
				af = int(rsa.Family)
			}
			sas[i] = sa
		case RTAX_NETMASK, RTAX_GENMASK:
			switch af {
			case AF_INET:
				rsa4 := (*RawSockaddrInet4)(unsafe.Pointer(&buf[0]))
				sa := new(SockaddrInet4)
				for j := 0; rsa4.Len > 0 && j < int(rsa4.Len)-int(unsafe.Offsetof(rsa4.Addr)); j++ {
					sa.Addr[j] = rsa4.Addr[j]
				}
				sas[i] = sa
			case AF_INET6:
				rsa6 := (*RawSockaddrInet6)(unsafe.Pointer(&buf[0]))
				sa := new(SockaddrInet6)
				for j := 0; rsa6.Len > 0 && j < int(rsa6.Len)-int(unsafe.Offsetof(rsa6.Addr)); j++ {
					sa.Addr[j] = rsa6.Addr[j]
				}
				sas[i] = sa
			}
		}
		buf = buf[rsaAlignOf(int(rsa.Len)):]
	}

	return sas[:]
}

// InterfaceMessage represents a routing message containing
// network interface entries.
type InterfaceMessage struct {
	Header IfMsghdr
	Data   []byte
}

func (m *InterfaceMessage) sockaddr() (sas []Sockaddr) {
	if m.Header.Addrs&RTA_IFP == 0 {
		return nil
	}
	sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(&m.Data[0])))
	if e != 0 {
		return nil
	}
	return append(sas, sa)
}

// InterfaceAddrMessage represents a routing message containing
// network interface address entries.
type InterfaceAddrMessage struct {
	Header IfaMsghdr
	Data   []byte
}

const rtaIfaMask = RTA_IFA | RTA_NETMASK | RTA_BRD

func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) {
	if m.Header.Addrs&rtaIfaMask == 0 {
		return nil
	}

	buf := m.Data[:]
	for i := uint(0); i < RTAX_MAX; i++ {
		if m.Header.Addrs&rtaIfaMask&(1<<i) == 0 {
			continue
		}
		rsa := (*RawSockaddr)(unsafe.Pointer(&buf[0]))
		switch i {
		case RTAX_IFA:
			sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
			if e != 0 {
				return nil
			}
			sas = append(sas, sa)
		case RTAX_NETMASK, RTAX_BRD:
			// nothing to do
		}
		buf = buf[rsaAlignOf(int(rsa.Len)):]
	}

	return sas
}

// ParseRoutingMessage parses buf as routing messages and returns
// the slice containing the RoutingMessage interfaces.
func ParseRoutingMessage(buf []byte) (msgs []RoutingMessage, errno int) {
	for len(buf) >= anyMessageLen {
		any := (*anyMessage)(unsafe.Pointer(&buf[0]))
		if any.Version != RTM_VERSION {
			return nil, EINVAL
		}
		msgs = append(msgs, any.toRoutingMessage(buf))
		buf = buf[any.Msglen:]
	}
	return msgs, 0
}

// ParseRoutingMessage parses msg's payload as raw sockaddrs and
// returns the slice containing the Sockaddr interfaces.
func ParseRoutingSockaddr(msg RoutingMessage) (sas []Sockaddr, errno int) {
	return append(sas, msg.sockaddr()...), 0
}
<|endoftext|>