<commit_before>/*
Copyright 2020 The Ceph-CSI Authors.

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 rbd

import (
	"context"
	"errors"
	"fmt"

	"github.com/ceph/ceph-csi/internal/journal"
	"github.com/ceph/ceph-csi/internal/util"

	librbd "github.com/ceph/go-ceph/rbd"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// checkCloneImage check the cloned image exists, if the cloned image is not
// found it will check the temporary cloned snapshot exists, and again it will
// check the snapshot exists on the temporary cloned image, if yes it will
// create a new cloned and delete the temporary snapshot and adds a task to
// flatten the temp cloned image and return success.
//
// if the temporary snapshot does not exists it creates a temporary snapshot on
// temporary cloned image and creates  a new cloned with user-provided image
// features and delete the temporary snapshot and adds a task to flatten the
// temp cloned image and return success
//
// if the temporary clone does not exist and if there is a temporary snapshot
// present on the parent image it will delete the temporary snapshot and
// returns.
func (rv *rbdVolume) checkCloneImage(ctx context.Context, parentVol *rbdVolume) (bool, error) {
	// generate temp cloned volume
	tempClone := rv.generateTempClone()
	snap := &rbdSnapshot{
		RbdSnapName: rv.RbdImageName,
		Pool:        rv.Pool,
	}
	// check if cloned image exists
	err := rv.getImageInfo()
	if err == nil {
		// check if do we have temporary snapshot on temporary cloned image
		sErr := tempClone.checkSnapExists(snap)
		if sErr != nil {
			if errors.Is(err, ErrSnapNotFound) {
				return true, nil
			}
			return false, err
		}
		err = tempClone.deleteSnapshot(ctx, snap)
		if err == nil {
			return true, nil
		}
		return false, err
	}
	if !errors.Is(err, ErrImageNotFound) {
		// return error if its not image not found
		return false, err
	}

	err = tempClone.checkSnapExists(snap)
	if err != nil {
		if errors.Is(err, ErrSnapNotFound) {
			// check temporary image needs flatten, if yes add task to flatten the
			// temporary clone
			err = tempClone.flattenRbdImage(ctx, rv.conn.Creds, false, rbdHardMaxCloneDepth, rbdSoftMaxCloneDepth)
			if err != nil {
				return false, err
			}
			// as the snapshot is not present, create new snapshot,clone and
			// delete the temporary snapshot
			err = createRBDClone(ctx, tempClone, rv, snap, rv.conn.Creds)
			if err != nil {
				return false, err
			}
			return true, nil
		} else if !errors.Is(err, ErrImageNotFound) {
			// any error other than image not found return error
			return false, err
		}
	} else {
		// snap will be create after we flatten the temporary cloned image,no
		// need to check for flatten here.
		// as the snap exists,create clone image and delete temporary snapshot
		// and add task to flatten temporary cloned image
		err = rv.cloneRbdImageFromSnapshot(ctx, snap)
		if err != nil {
			util.ErrorLog(ctx, "failed to clone rbd image %s from snapshot %s: %v", rv.RbdImageName, snap.RbdSnapName, err)
			err = fmt.Errorf("failed to clone rbd image %s from snapshot %s: %v", rv.RbdImageName, snap.RbdSnapName, err)
			return false, err
		}
		err = tempClone.deleteSnapshot(ctx, snap)
		if err != nil {
			util.ErrorLog(ctx, "failed to delete snapshot: %v", err)
			return false, err
		}
		return true, nil
	}
	// as the temp clone doesnot exists,check snapshot exists on parent volume
	// snapshot name is same as temporary clone image
	snap.RbdImageName = tempClone.RbdImageName
	err = parentVol.checkSnapExists(snap)
	if err == nil {
		// the temp clone exists, delete it lets reserve a new ID and
		// create new resources for clearner approach
		err = parentVol.deleteSnapshot(ctx, snap)
	}
	if errors.Is(err, ErrSnapNotFound) {
		return false, nil
	}
	return false, err
}

func (rv *rbdVolume) generateTempClone() *rbdVolume {
	tempClone := rbdVolume{}
	tempClone.conn = rv.conn
	// The temp clone image need to have deep flatten feature
	f := []string{librbd.FeatureNameLayering, librbd.FeatureNameDeepFlatten}
	tempClone.imageFeatureSet = librbd.FeatureSetFromNames(f)
	tempClone.ClusterID = rv.ClusterID
	tempClone.Monitors = rv.Monitors
	tempClone.Pool = rv.Pool
	// The temp cloned image name will be always (rbd image name + "-temp")
	// this name will be always unique, as cephcsi never creates an image with
	// this format for new rbd images
	tempClone.RbdImageName = rv.RbdImageName + "-temp"
	return &tempClone
}

func (rv *rbdVolume) createCloneFromImage(ctx context.Context, parentVol *rbdVolume) error {
	// generate temp cloned volume
	tempClone := rv.generateTempClone()
	tempSnap := &rbdSnapshot{
		// snapshot name is same as temporary cloned image, This helps to
		// flatten the temporary cloned images as we cannot have more than 510
		// snapshots on an rbd image
		RbdSnapName: tempClone.RbdImageName,
		Pool:        rv.Pool,
	}

	cloneSnap := &rbdSnapshot{
		RbdSnapName: rv.RbdImageName,
		Pool:        rv.Pool,
	}
	var (
		errClone   error
		errFlatten error
		err        error
	)
	var j = &journal.Connection{}

	j, err = volJournal.Connect(rv.Monitors, rv.RadosNamespace, rv.conn.Creds)
	if err != nil {
		return status.Error(codes.Internal, err.Error())
	}
	defer j.Destroy()

	// create snapshot and temporary clone and delete snapshot
	err = createRBDClone(ctx, parentVol, tempClone, tempSnap, rv.conn.Creds)
	if err != nil {
		return err
	}

	defer func() {
		if err != nil || errFlatten != nil {
			if !errors.Is(errFlatten, ErrFlattenInProgress) {
				// cleanup snapshot
				cErr := cleanUpSnapshot(ctx, parentVol, tempSnap, tempClone, rv.conn.Creds)
				if cErr != nil {
					util.ErrorLog(ctx, "failed to cleanup image %s or snapshot %s: %v", tempSnap, tempClone, cErr)
				}
			}
		}
		if err != nil || errClone != nil {
			cErr := cleanUpSnapshot(ctx, tempClone, cloneSnap, rv, rv.conn.Creds)
			if cErr != nil {
				util.ErrorLog(ctx, "failed to cleanup image %s or snapshot %s: %v", cloneSnap, tempClone, cErr)
			}
		}
	}()
	// flatten clone
	errFlatten = tempClone.flattenRbdImage(ctx, rv.conn.Creds, false, rbdHardMaxCloneDepth, rbdSoftMaxCloneDepth)
	if errFlatten != nil {
		return err
	}
	// create snap of temp clone from temporary cloned image
	// create final clone
	// delete snap of temp clone
	errClone = createRBDClone(ctx, tempClone, rv, cloneSnap, rv.conn.Creds)
	if errClone != nil {
		// set errFlatten error to cleanup temporary snapshot and temporary clone
		errFlatten = errors.New("failed to create user requested cloned image")
		return errClone
	}
	err = rv.getImageID()
	if err != nil {
		util.ErrorLog(ctx, "failed to get volume id %s: %v", rv, err)
		return err
	}

	err = j.StoreImageID(ctx, rv.JournalPool, rv.ReservedID, rv.ImageID, rv.conn.Creds)
	if err != nil {
		util.ErrorLog(ctx, "failed to store volume %s: %v", rv, err)
		return err
	}
	return nil
}

func (rv *rbdVolume) flattenCloneImage(ctx context.Context) error {
	tempClone := rv.generateTempClone()
	// reducing the limit for cloned images to make sure the limit is in range,
	// If the intermediate clone reaches the depth we may need to return ABORT
	// error message as it need to be flatten before continuing, this may leak
	// omap entries and stale temporary snapshots in corner cases, if we reduce
	// the limit and check for the depth of the parent image clain it self we
	// can flatten the parent images before use to avoid the stale omap entries.
	hardLimit := rbdHardMaxCloneDepth
	softLimit := rbdSoftMaxCloneDepth
	// choosing 2 so that we don't need to flatten the image in the request.
	const depthToAvoidFlatten = 2
	if rbdHardMaxCloneDepth < depthToAvoidFlatten {
		hardLimit = rbdHardMaxCloneDepth - depthToAvoidFlatten
	}
	if rbdSoftMaxCloneDepth < depthToAvoidFlatten {
		softLimit = rbdSoftMaxCloneDepth - depthToAvoidFlatten
	}
	err := tempClone.getImageInfo()
	if err == nil {
		return tempClone.flattenRbdImage(ctx, tempClone.conn.Creds, false, hardLimit, softLimit)
	}
	if err != nil {
		if !errors.Is(err, ErrImageNotFound) {
			return err
		}
	}
	return rv.flattenRbdImage(ctx, rv.conn.Creds, false, hardLimit, softLimit)
}
<commit_msg>rbd: fix return error message<commit_after>/*
Copyright 2020 The Ceph-CSI Authors.

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 rbd

import (
	"context"
	"errors"
	"fmt"

	"github.com/ceph/ceph-csi/internal/journal"
	"github.com/ceph/ceph-csi/internal/util"

	librbd "github.com/ceph/go-ceph/rbd"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// checkCloneImage check the cloned image exists, if the cloned image is not
// found it will check the temporary cloned snapshot exists, and again it will
// check the snapshot exists on the temporary cloned image, if yes it will
// create a new cloned and delete the temporary snapshot and adds a task to
// flatten the temp cloned image and return success.
//
// if the temporary snapshot does not exists it creates a temporary snapshot on
// temporary cloned image and creates  a new cloned with user-provided image
// features and delete the temporary snapshot and adds a task to flatten the
// temp cloned image and return success
//
// if the temporary clone does not exist and if there is a temporary snapshot
// present on the parent image it will delete the temporary snapshot and
// returns.
func (rv *rbdVolume) checkCloneImage(ctx context.Context, parentVol *rbdVolume) (bool, error) {
	// generate temp cloned volume
	tempClone := rv.generateTempClone()
	snap := &rbdSnapshot{
		RbdSnapName: rv.RbdImageName,
		Pool:        rv.Pool,
	}
	// check if cloned image exists
	err := rv.getImageInfo()
	if err == nil {
		// check if do we have temporary snapshot on temporary cloned image
		sErr := tempClone.checkSnapExists(snap)
		if sErr != nil {
			if errors.Is(err, ErrSnapNotFound) {
				return true, nil
			}
			return false, err
		}
		err = tempClone.deleteSnapshot(ctx, snap)
		if err == nil {
			return true, nil
		}
		return false, err
	}
	if !errors.Is(err, ErrImageNotFound) {
		// return error if its not image not found
		return false, err
	}

	err = tempClone.checkSnapExists(snap)
	if err != nil {
		if errors.Is(err, ErrSnapNotFound) {
			// check temporary image needs flatten, if yes add task to flatten the
			// temporary clone
			err = tempClone.flattenRbdImage(ctx, rv.conn.Creds, false, rbdHardMaxCloneDepth, rbdSoftMaxCloneDepth)
			if err != nil {
				return false, err
			}
			// as the snapshot is not present, create new snapshot,clone and
			// delete the temporary snapshot
			err = createRBDClone(ctx, tempClone, rv, snap, rv.conn.Creds)
			if err != nil {
				return false, err
			}
			return true, nil
		} else if !errors.Is(err, ErrImageNotFound) {
			// any error other than image not found return error
			return false, err
		}
	} else {
		// snap will be create after we flatten the temporary cloned image,no
		// need to check for flatten here.
		// as the snap exists,create clone image and delete temporary snapshot
		// and add task to flatten temporary cloned image
		err = rv.cloneRbdImageFromSnapshot(ctx, snap)
		if err != nil {
			util.ErrorLog(ctx, "failed to clone rbd image %s from snapshot %s: %v", rv.RbdImageName, snap.RbdSnapName, err)
			err = fmt.Errorf("failed to clone rbd image %s from snapshot %s: %v", rv.RbdImageName, snap.RbdSnapName, err)
			return false, err
		}
		err = tempClone.deleteSnapshot(ctx, snap)
		if err != nil {
			util.ErrorLog(ctx, "failed to delete snapshot: %v", err)
			return false, err
		}
		return true, nil
	}
	// as the temp clone doesnot exists,check snapshot exists on parent volume
	// snapshot name is same as temporary clone image
	snap.RbdImageName = tempClone.RbdImageName
	err = parentVol.checkSnapExists(snap)
	if err == nil {
		// the temp clone exists, delete it lets reserve a new ID and
		// create new resources for clearner approach
		err = parentVol.deleteSnapshot(ctx, snap)
	}
	if errors.Is(err, ErrSnapNotFound) {
		return false, nil
	}
	return false, err
}

func (rv *rbdVolume) generateTempClone() *rbdVolume {
	tempClone := rbdVolume{}
	tempClone.conn = rv.conn
	// The temp clone image need to have deep flatten feature
	f := []string{librbd.FeatureNameLayering, librbd.FeatureNameDeepFlatten}
	tempClone.imageFeatureSet = librbd.FeatureSetFromNames(f)
	tempClone.ClusterID = rv.ClusterID
	tempClone.Monitors = rv.Monitors
	tempClone.Pool = rv.Pool
	// The temp cloned image name will be always (rbd image name + "-temp")
	// this name will be always unique, as cephcsi never creates an image with
	// this format for new rbd images
	tempClone.RbdImageName = rv.RbdImageName + "-temp"
	return &tempClone
}

func (rv *rbdVolume) createCloneFromImage(ctx context.Context, parentVol *rbdVolume) error {
	// generate temp cloned volume
	tempClone := rv.generateTempClone()
	tempSnap := &rbdSnapshot{
		// snapshot name is same as temporary cloned image, This helps to
		// flatten the temporary cloned images as we cannot have more than 510
		// snapshots on an rbd image
		RbdSnapName: tempClone.RbdImageName,
		Pool:        rv.Pool,
	}

	cloneSnap := &rbdSnapshot{
		RbdSnapName: rv.RbdImageName,
		Pool:        rv.Pool,
	}
	var (
		errClone   error
		errFlatten error
		err        error
	)
	var j = &journal.Connection{}

	j, err = volJournal.Connect(rv.Monitors, rv.RadosNamespace, rv.conn.Creds)
	if err != nil {
		return status.Error(codes.Internal, err.Error())
	}
	defer j.Destroy()

	// create snapshot and temporary clone and delete snapshot
	err = createRBDClone(ctx, parentVol, tempClone, tempSnap, rv.conn.Creds)
	if err != nil {
		return err
	}

	defer func() {
		if err != nil || errFlatten != nil {
			if !errors.Is(errFlatten, ErrFlattenInProgress) {
				// cleanup snapshot
				cErr := cleanUpSnapshot(ctx, parentVol, tempSnap, tempClone, rv.conn.Creds)
				if cErr != nil {
					util.ErrorLog(ctx, "failed to cleanup image %s or snapshot %s: %v", tempSnap, tempClone, cErr)
				}
			}
		}
		if err != nil || errClone != nil {
			cErr := cleanUpSnapshot(ctx, tempClone, cloneSnap, rv, rv.conn.Creds)
			if cErr != nil {
				util.ErrorLog(ctx, "failed to cleanup image %s or snapshot %s: %v", cloneSnap, tempClone, cErr)
			}
		}
	}()
	// flatten clone
	errFlatten = tempClone.flattenRbdImage(ctx, rv.conn.Creds, false, rbdHardMaxCloneDepth, rbdSoftMaxCloneDepth)
	if errFlatten != nil {
		return errFlatten
	}
	// create snap of temp clone from temporary cloned image
	// create final clone
	// delete snap of temp clone
	errClone = createRBDClone(ctx, tempClone, rv, cloneSnap, rv.conn.Creds)
	if errClone != nil {
		// set errFlatten error to cleanup temporary snapshot and temporary clone
		errFlatten = errors.New("failed to create user requested cloned image")
		return errClone
	}
	err = rv.getImageID()
	if err != nil {
		util.ErrorLog(ctx, "failed to get volume id %s: %v", rv, err)
		return err
	}

	err = j.StoreImageID(ctx, rv.JournalPool, rv.ReservedID, rv.ImageID, rv.conn.Creds)
	if err != nil {
		util.ErrorLog(ctx, "failed to store volume %s: %v", rv, err)
		return err
	}
	return nil
}

func (rv *rbdVolume) flattenCloneImage(ctx context.Context) error {
	tempClone := rv.generateTempClone()
	// reducing the limit for cloned images to make sure the limit is in range,
	// If the intermediate clone reaches the depth we may need to return ABORT
	// error message as it need to be flatten before continuing, this may leak
	// omap entries and stale temporary snapshots in corner cases, if we reduce
	// the limit and check for the depth of the parent image clain it self we
	// can flatten the parent images before use to avoid the stale omap entries.
	hardLimit := rbdHardMaxCloneDepth
	softLimit := rbdSoftMaxCloneDepth
	// choosing 2 so that we don't need to flatten the image in the request.
	const depthToAvoidFlatten = 2
	if rbdHardMaxCloneDepth < depthToAvoidFlatten {
		hardLimit = rbdHardMaxCloneDepth - depthToAvoidFlatten
	}
	if rbdSoftMaxCloneDepth < depthToAvoidFlatten {
		softLimit = rbdSoftMaxCloneDepth - depthToAvoidFlatten
	}
	err := tempClone.getImageInfo()
	if err == nil {
		return tempClone.flattenRbdImage(ctx, tempClone.conn.Creds, false, hardLimit, softLimit)
	}
	if err != nil {
		if !errors.Is(err, ErrImageNotFound) {
			return err
		}
	}
	return rv.flattenRbdImage(ctx, rv.conn.Creds, false, hardLimit, softLimit)
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT

// Package tree 提供了以树形结构保存路由项的相关操作
package tree

import (
	"fmt"
	"net/http"
	"sync"

	"github.com/issue9/errwrap"

	"github.com/issue9/mux/v5/internal/syntax"
)

// Tree 以树节点的形式保存的路由
//
// 多段路由项，会提取其中的相同的内容组成树状结构的节点。
// 比如以下路由项：
//  /posts/{id}/author
//  /posts/{id}/author/emails
//  /posts/{id}/author/profile
//  /posts/1/author
// 会被转换成以下结构
//  /posts
//     |
//     +---- 1/author
//     |
//     +---- {id}/author
//               |
//               +---- /profile
//               |
//               +---- /emails
type Tree struct {
	methods map[string]int // 保存着每个请求方法在所有子节点上的数量。
	node    *Node          // 空节点，其 methodIndex 正好用于保存 OPTIONS *。

	// 由 New 负责初始化的内容
	locker       *sync.RWMutex
	interceptors *syntax.Interceptors
}

// New 声明一个 Tree 实例
func New(lock bool, i *syntax.Interceptors) *Tree {
	s, err := i.NewSegment("")
	if err != nil {
		panic("发生了不该发生的错误，应该是 syntax.NewSegment 逻辑发生变化" + err.Error())
	}

	t := &Tree{
		methods:      make(map[string]int, len(Methods)),
		node:         &Node{segment: s},
		interceptors: i,
	}
	t.node.root = t
	t.node.handlers = map[string]http.Handler{http.MethodOptions: http.HandlerFunc(t.optionsServeHTTP)}

	if lock {
		t.locker = &sync.RWMutex{}
	}

	return t
}

func (tree *Tree) optionsServeHTTP(w http.ResponseWriter, _ *http.Request) {
	optionsHandle(w, methodIndexes[tree.node.methodIndex].options)
}

// Add 添加路由项
//
// methods 可以为空，表示添加除 OPTIONS 和 HEAD 之外所有支持的请求方法。
func (tree *Tree) Add(pattern string, h http.Handler, methods ...string) error {
	if err := tree.checkAmbiguous(pattern); err != nil {
		return err
	}

	if tree.locker != nil {
		tree.locker.Lock()
		defer tree.locker.Unlock()
	}

	n, err := tree.getNode(pattern)
	if err != nil {
		return err
	}

	if n.handlers == nil {
		n.handlers = make(map[string]http.Handler, handlersSize)
	}

	if len(methods) == 0 {
		methods = addAny
	}
	return n.addMethods(h, methods...)
}

func (tree *Tree) checkAmbiguous(pattern string) error {
	n, has, err := tree.node.checkAmbiguous(pattern, false)
	if err != nil {
		return err
	}

	if n == nil || !has {
		return nil
	}

	var s string
	for n != nil {
		s = n.segment.Value + s
		n = n.parent
	}

	return fmt.Errorf("存在有歧义的节点：%s", s)
}

// Clean 清除路由项
func (tree *Tree) Clean(prefix string) {
	if tree.locker != nil {
		tree.locker.Lock()
		defer tree.locker.Unlock()
	}

	tree.node.clean(prefix)
}

// Remove 移除路由项
//
// methods 可以为空，表示删除所有内容。单独删除 OPTIONS，将不会发生任何事情。
func (tree *Tree) Remove(pattern string, methods ...string) {
	if tree.locker != nil {
		tree.locker.Lock()
		defer tree.locker.Unlock()
	}

	child := tree.find(pattern)
	if child == nil {
		return
	}

	if len(methods) == 0 {
		child.handlers = nil
	} else {
		for _, m := range methods {
			switch m {
			case http.MethodOptions: // OPTIONS 不作任何操作
			case http.MethodGet:
				delete(child.handlers, http.MethodHead)
				fallthrough
			default:
				delete(child.handlers, m)
			}
		}

		if _, found := child.handlers[http.MethodOptions]; found && child.size() == 1 { // 只有一个 OPTIONS 了
			delete(child.handlers, http.MethodOptions)
		}
	}

	child.buildMethods()

	for child.size() == 0 && len(child.children) == 0 {
		child.parent.children = removeNodes(child.parent.children, child.segment.Value)
		child.parent.buildIndexes()
		child = child.parent
	}

	tree.buildMethods(-1, methods...)
}

// 获取指定的节点，若节点不存在，则在该位置生成一个新节点。
func (tree *Tree) getNode(pattern string) (*Node, error) {
	segs, err := tree.interceptors.Split(pattern)
	if err != nil {
		return nil, err
	}
	return tree.node.getNode(segs)
}

// Route 找到与当前内容匹配的 Node 实例
//
// NOTE: 调用方需要调用 syntax.Params.Destroy 销毁对象
func (tree *Tree) Route(path string) (*Node, *syntax.Params) {
	if tree.locker != nil {
		tree.locker.RLock()
		defer tree.locker.RUnlock()
	}

	if path == "*" || path == "" {
		return tree.node, nil
	}

	p := syntax.NewParams(path)
	node := tree.node.matchChildren(p)
	if node == nil || node.size() == 0 {
		p.Destroy()
		return nil, nil
	}
	return node, p
}

// Routes 获取当前的所有路由项以及对应的请求方法
func (tree *Tree) Routes() map[string][]string {
	if tree.locker != nil {
		tree.locker.RLock()
		defer tree.locker.RUnlock()
	}

	routes := make(map[string][]string, 100)
	routes["*"] = []string{http.MethodOptions}

	for _, v := range tree.node.children {
		v.routes("", routes)
	}

	return routes
}

func (tree *Tree) find(pattern string) *Node { return tree.node.find(pattern) }

// URL 将 ps 填入 pattern 生成 URL
//
// NOTE: 会检测 pattern 是否存在于 tree 中。
func (tree *Tree) URL(buf *errwrap.StringBuilder, pattern string, ps map[string]string) error {
	n := tree.find(pattern)
	if n == nil {
		return fmt.Errorf("%s 并不是一条有效的注册路由项", pattern)
	}

	nodes := make([]*Node, 0, 5)
	for curr := n; curr.parent != nil; curr = curr.parent { // 从尾部向上开始获取节点
		nodes = append(nodes, curr)
	}
	l := len(nodes)
	for i, j := 0, l-1; i < j; i, j = i+1, j-1 {
		nodes[i], nodes[j] = nodes[j], nodes[i]
	}

	for _, node := range nodes {
		s := node.segment
		switch s.Type {
		case syntax.String:
			buf.WString(s.Value)
		case syntax.Named, syntax.Regexp:
			param, exists := ps[s.Name]
			if !exists {
				return fmt.Errorf("未找到参数 %s 的值", s.Name)
			}
			if !s.Valid(param) {
				return fmt.Errorf("参数 %s 格式不匹配", s.Name)
			}

			buf.WString(param).WString(s.Suffix)
		}
	}

	return nil
}
<commit_msg>refactor(internal/tree): 合并 Tree 和 node 的 optionsServeHTTP<commit_after>// SPDX-License-Identifier: MIT

// Package tree 提供了以树形结构保存路由项的相关操作
package tree

import (
	"fmt"
	"net/http"
	"sync"

	"github.com/issue9/errwrap"

	"github.com/issue9/mux/v5/internal/syntax"
)

// Tree 以树节点的形式保存的路由
//
// 多段路由项，会提取其中的相同的内容组成树状结构的节点。
// 比如以下路由项：
//  /posts/{id}/author
//  /posts/{id}/author/emails
//  /posts/{id}/author/profile
//  /posts/1/author
// 会被转换成以下结构
//  /posts
//     |
//     +---- 1/author
//     |
//     +---- {id}/author
//               |
//               +---- /profile
//               |
//               +---- /emails
type Tree struct {
	methods map[string]int // 保存着每个请求方法在所有子节点上的数量。
	node    *Node          // 空节点，正好用于处理 OPTIONS *。

	// 由 New 负责初始化的内容
	locker       *sync.RWMutex
	interceptors *syntax.Interceptors
}

// New 声明一个 Tree 实例
func New(lock bool, i *syntax.Interceptors) *Tree {
	s, err := i.NewSegment("")
	if err != nil {
		panic("发生了不该发生的错误，应该是 syntax.NewSegment 逻辑发生变化" + err.Error())
	}

	t := &Tree{
		methods:      make(map[string]int, len(Methods)),
		node:         &Node{segment: s, methodIndex: methodIndexMap[http.MethodOptions]},
		interceptors: i,
	}
	t.node.root = t
	t.node.handlers = map[string]http.Handler{
		http.MethodOptions: http.HandlerFunc(t.node.optionsServeHTTP),
	}

	if lock {
		t.locker = &sync.RWMutex{}
	}

	return t
}

// Add 添加路由项
//
// methods 可以为空，表示添加除 OPTIONS 和 HEAD 之外所有支持的请求方法。
func (tree *Tree) Add(pattern string, h http.Handler, methods ...string) error {
	if err := tree.checkAmbiguous(pattern); err != nil {
		return err
	}

	if tree.locker != nil {
		tree.locker.Lock()
		defer tree.locker.Unlock()
	}

	n, err := tree.getNode(pattern)
	if err != nil {
		return err
	}

	if n.handlers == nil {
		n.handlers = make(map[string]http.Handler, handlersSize)
	}

	if len(methods) == 0 {
		methods = addAny
	}
	return n.addMethods(h, methods...)
}

func (tree *Tree) checkAmbiguous(pattern string) error {
	n, has, err := tree.node.checkAmbiguous(pattern, false)
	if err != nil {
		return err
	}

	if n == nil || !has {
		return nil
	}

	var s string
	for n != nil {
		s = n.segment.Value + s
		n = n.parent
	}

	return fmt.Errorf("存在有歧义的节点：%s", s)
}

// Clean 清除路由项
func (tree *Tree) Clean(prefix string) {
	if tree.locker != nil {
		tree.locker.Lock()
		defer tree.locker.Unlock()
	}

	tree.node.clean(prefix)
}

// Remove 移除路由项
//
// methods 可以为空，表示删除所有内容。单独删除 OPTIONS，将不会发生任何事情。
func (tree *Tree) Remove(pattern string, methods ...string) {
	if tree.locker != nil {
		tree.locker.Lock()
		defer tree.locker.Unlock()
	}

	child := tree.find(pattern)
	if child == nil {
		return
	}

	if len(methods) == 0 {
		child.handlers = nil
	} else {
		for _, m := range methods {
			switch m {
			case http.MethodOptions: // OPTIONS 不作任何操作
			case http.MethodGet:
				delete(child.handlers, http.MethodHead)
				fallthrough
			default:
				delete(child.handlers, m)
			}
		}

		if _, found := child.handlers[http.MethodOptions]; found && child.size() == 1 { // 只有一个 OPTIONS 了
			delete(child.handlers, http.MethodOptions)
		}
	}

	child.buildMethods()

	for child.size() == 0 && len(child.children) == 0 {
		child.parent.children = removeNodes(child.parent.children, child.segment.Value)
		child.parent.buildIndexes()
		child = child.parent
	}

	tree.buildMethods(-1, methods...)
}

// 获取指定的节点，若节点不存在，则在该位置生成一个新节点。
func (tree *Tree) getNode(pattern string) (*Node, error) {
	segs, err := tree.interceptors.Split(pattern)
	if err != nil {
		return nil, err
	}
	return tree.node.getNode(segs)
}

// Route 找到与当前内容匹配的 Node 实例
//
// NOTE: 调用方需要调用 syntax.Params.Destroy 销毁对象
func (tree *Tree) Route(path string) (*Node, *syntax.Params) {
	if tree.locker != nil {
		tree.locker.RLock()
		defer tree.locker.RUnlock()
	}

	if path == "*" || path == "" {
		return tree.node, nil
	}

	p := syntax.NewParams(path)
	node := tree.node.matchChildren(p)
	if node == nil || node.size() == 0 {
		p.Destroy()
		return nil, nil
	}
	return node, p
}

// Routes 获取当前的所有路由项以及对应的请求方法
func (tree *Tree) Routes() map[string][]string {
	if tree.locker != nil {
		tree.locker.RLock()
		defer tree.locker.RUnlock()
	}

	routes := make(map[string][]string, 100)
	routes["*"] = []string{http.MethodOptions}

	for _, v := range tree.node.children {
		v.routes("", routes)
	}

	return routes
}

func (tree *Tree) find(pattern string) *Node { return tree.node.find(pattern) }

// URL 将 ps 填入 pattern 生成 URL
//
// NOTE: 会检测 pattern 是否存在于 tree 中。
func (tree *Tree) URL(buf *errwrap.StringBuilder, pattern string, ps map[string]string) error {
	n := tree.find(pattern)
	if n == nil {
		return fmt.Errorf("%s 并不是一条有效的注册路由项", pattern)
	}

	nodes := make([]*Node, 0, 5)
	for curr := n; curr.parent != nil; curr = curr.parent { // 从尾部向上开始获取节点
		nodes = append(nodes, curr)
	}
	l := len(nodes)
	for i, j := 0, l-1; i < j; i, j = i+1, j-1 {
		nodes[i], nodes[j] = nodes[j], nodes[i]
	}

	for _, node := range nodes {
		s := node.segment
		switch s.Type {
		case syntax.String:
			buf.WString(s.Value)
		case syntax.Named, syntax.Regexp:
			param, exists := ps[s.Name]
			if !exists {
				return fmt.Errorf("未找到参数 %s 的值", s.Name)
			}
			if !s.Valid(param) {
				return fmt.Errorf("参数 %s 格式不匹配", s.Name)
			}

			buf.WString(param).WString(s.Suffix)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package toml

import (
	"reflect"
	"time"

	"github.com/pelletier/go-toml/v2/internal/reflectbuild"
)

func Unmarshal(data []byte, v interface{}) error {
	u := &unmarshaler{}
	u.builder, u.err = reflectbuild.NewBuilder("toml", v)
	if u.err == nil {
		parseErr := parser{builder: u}.parse(data)
		if parseErr != nil {
			return parseErr
		}
	}
	return u.err
}

type unmarshaler struct {
	builder reflectbuild.Builder

	// First error that appeared during the construction of the object.
	// When set all callbacks are no-ops.
	err error

	// State that indicates the parser is processing a [[table-array]] name.
	// If false keys are interpreted as part of a key-value or [table].
	parsingTableArray bool

	// Table Arrays need a buffer of keys because we need to know which one is
	// the last one, as it may result in creating a new element in the array.
	arrayTableKey [][]byte

	// Flag to indicate that the next value is an an assignment.
	// Assignments are when the builder already points to the value, and should
	// be directly assigned. This is used to distinguish between assigning or
	// appending to arrays.
	assign bool
}

func (u *unmarshaler) Assignation() {
	u.assign = true
}

func (u *unmarshaler) ArrayBegin() {
	if u.err != nil {
		return
	}
	u.builder.Save()
	if u.assign {
		u.assign = false
	} else {
		u.err = u.builder.SliceNewElem()
	}
}

func (u *unmarshaler) ArrayEnd() {
	if u.err != nil {
		return
	}
	u.builder.Load()
}

func (u *unmarshaler) ArrayTableBegin() {
	if u.err != nil {
		return
	}

	u.parsingTableArray = true
}

func (u *unmarshaler) ArrayTableEnd() {
	if u.err != nil {
		return
	}

	u.builder.Reset()

	for _, v := range u.arrayTableKey[:len(u.arrayTableKey)-1] {
		u.err = u.builder.DigField(string(v))
		if u.err != nil {
			return
		}
		u.err = u.builder.SliceLastOrCreate()
	}

	v := u.arrayTableKey[len(u.arrayTableKey)-1]
	u.err = u.builder.DigField(string(v))
	if u.err != nil {
		return
	}
	u.err = u.builder.SliceNewElem()

	u.parsingTableArray = false
	u.arrayTableKey = u.arrayTableKey[:0]
}

func (u *unmarshaler) KeyValBegin() {
	u.builder.Save()
}

func (u *unmarshaler) KeyValEnd() {
	u.builder.Load()
}

func (u *unmarshaler) StringValue(v []byte) {
	if u.err != nil {
		return
	}
	if u.builder.IsSlice() {
		u.builder.Save()
		u.err = u.builder.SliceAppend(reflect.ValueOf(string(v)))
		if u.err != nil {
			return
		}
		u.builder.Load()
	} else {
		u.err = u.builder.SetString(string(v))
	}
}

func (u *unmarshaler) BoolValue(b bool) {
	if u.err != nil {
		return
	}
	if u.builder.IsSlice() {
		u.builder.Save()
		u.err = u.builder.SliceAppend(reflect.ValueOf(b))
		if u.err != nil {
			return
		}
		u.builder.Load()
	} else {
		u.err = u.builder.SetBool(b)
	}
}

func (u *unmarshaler) FloatValue(n float64) {
	if u.err != nil {
		return
	}
	if u.builder.IsSlice() {
		u.builder.Save()
		u.err = u.builder.SliceAppend(reflect.ValueOf(n))
		if u.err != nil {
			return
		}
		u.builder.Load()
	} else {
		u.err = u.builder.SetFloat(n)
	}
}

func (u *unmarshaler) IntValue(n int64) {
	if u.err != nil {
		return
	}
	if u.builder.IsSlice() {
		u.builder.Save()
		u.err = u.builder.SliceAppend(reflect.ValueOf(n))
		if u.err != nil {
			return
		}
		u.builder.Load()
	} else {
		u.err = u.builder.SetInt(n)
	}
}

func (u *unmarshaler) LocalDateValue(date LocalDate) {
	if u.err != nil {
		return
	}
	if u.builder.IsSlice() {
		u.builder.Save()
		u.err = u.builder.SliceAppend(reflect.ValueOf(date))
		if u.err != nil {
			return
		}
		u.builder.Load()
	} else {
		u.err = u.builder.Set(reflect.ValueOf(date))
	}
}

func (u *unmarshaler) LocalDateTimeValue(dt LocalDateTime) {
	if u.err != nil {
		return
	}
	if u.builder.IsSlice() {
		u.builder.Save()
		u.err = u.builder.SliceAppend(reflect.ValueOf(dt))
		if u.err != nil {
			return
		}
		u.builder.Load()
	} else {
		u.err = u.builder.Set(reflect.ValueOf(dt))
	}
}

func (u *unmarshaler) DateTimeValue(dt time.Time) {
	if u.err != nil {
		return
	}
	if u.builder.IsSlice() {
		u.builder.Save()
		u.err = u.builder.SliceAppend(reflect.ValueOf(dt))
		if u.err != nil {
			return
		}
		u.builder.Load()
	} else {
		u.err = u.builder.Set(reflect.ValueOf(dt))
	}
}

func (u *unmarshaler) LocalTimeValue(localTime LocalTime) {
	if u.err != nil {
		return
	}
	if u.builder.IsSlice() {
		u.builder.Save()
		u.err = u.builder.SliceAppend(reflect.ValueOf(localTime))
		if u.err != nil {
			return
		}
		u.builder.Load()
	} else {
		u.err = u.builder.Set(reflect.ValueOf(localTime))
	}
}

func (u *unmarshaler) SimpleKey(v []byte) {
	if u.err != nil {
		return
	}

	if u.parsingTableArray {
		u.arrayTableKey = append(u.arrayTableKey, v)
	} else {
		if u.builder.Cursor().Kind() == reflect.Slice {
			u.err = u.builder.SliceLastOrCreate()
			if u.err != nil {
				return
			}
		}
		u.err = u.builder.DigField(string(v))
		if u.err == nil {
			return
		}
		// TODO: figure out what to do with unexported fields
	}
}

func (u *unmarshaler) StandardTableBegin() {
	if u.err != nil {
		return
	}

	// tables are only top-level
	u.builder.Reset()
}

func (u *unmarshaler) StandardTableEnd() {
	if u.err != nil {
		return
	}
}
<commit_msg>Handle missing fields in structs<commit_after>package toml

import (
	"reflect"
	"time"

	"github.com/pelletier/go-toml/v2/internal/reflectbuild"
)

func Unmarshal(data []byte, v interface{}) error {
	u := &unmarshaler{}
	u.builder, u.err = reflectbuild.NewBuilder("toml", v)
	if u.err == nil {
		parseErr := parser{builder: u}.parse(data)
		if parseErr != nil {
			return parseErr
		}
	}
	return u.err
}

type unmarshaler struct {
	builder reflectbuild.Builder

	// First error that appeared during the construction of the object.
	// When set all callbacks are no-ops.
	err error

	// State that indicates the parser is processing a [[table-array]] name.
	// If false keys are interpreted as part of a key-value or [table].
	parsingTableArray bool

	// Table Arrays need a buffer of keys because we need to know which one is
	// the last one, as it may result in creating a new element in the array.
	arrayTableKey [][]byte

	// Flag to indicate that the next value is an an assignment.
	// Assignments are when the builder already points to the value, and should
	// be directly assigned. This is used to distinguish between assigning or
	// appending to arrays.
	assign bool

	// State that indicates the parser is processing a [table] name.
	// Used to know whether the whole table should be skipped or just the
	// keyval if a field is missing.
	parsingTable bool

	skipKeyValCount uint
	skipTable       bool
}

func (u *unmarshaler) skipping() bool {
	return u.skipTable || u.skipKeyValCount > 0
}

func (u *unmarshaler) Assignation() {
	if u.skipping() || u.err != nil {
		return
	}
	u.assign = true
}

func (u *unmarshaler) ArrayBegin() {
	if u.skipping() || u.err != nil {
		return
	}
	u.builder.Save()
	if u.assign {
		u.assign = false
	} else {
		u.err = u.builder.SliceNewElem()
	}
}

func (u *unmarshaler) ArrayEnd() {
	if u.skipping() || u.err != nil {
		return
	}
	u.builder.Load()
}

func (u *unmarshaler) ArrayTableBegin() {
	if u.skipping() || u.err != nil {
		return
	}

	u.parsingTableArray = true
}

func (u *unmarshaler) ArrayTableEnd() {
	if u.skipping() || u.err != nil {
		return
	}

	u.builder.Reset()

	for _, v := range u.arrayTableKey[:len(u.arrayTableKey)-1] {
		u.err = u.builder.DigField(string(v))
		if u.err != nil {
			return
		}
		u.err = u.builder.SliceLastOrCreate()
	}

	v := u.arrayTableKey[len(u.arrayTableKey)-1]
	u.err = u.builder.DigField(string(v))
	if u.err != nil {
		return
	}
	u.err = u.builder.SliceNewElem()

	u.parsingTableArray = false
	u.arrayTableKey = u.arrayTableKey[:0]
}

func (u *unmarshaler) KeyValBegin() {
	if u.skipKeyValCount > 0 {
		u.skipKeyValCount++
		return
	}
	if u.skipping() || u.err != nil {
		return
	}
	u.builder.Save()
}

func (u *unmarshaler) KeyValEnd() {
	if u.skipKeyValCount > 0 {
		u.skipKeyValCount--
		return
	}
	if u.skipping() || u.err != nil {
		return
	}
	u.builder.Load()
}

func (u *unmarshaler) StringValue(v []byte) {
	if u.skipping() || u.err != nil {
		return
	}
	if u.builder.IsSlice() {
		u.builder.Save()
		u.err = u.builder.SliceAppend(reflect.ValueOf(string(v)))
		if u.err != nil {
			return
		}
		u.builder.Load()
	} else {
		u.err = u.builder.SetString(string(v))
	}
}

func (u *unmarshaler) BoolValue(b bool) {
	if u.skipping() || u.err != nil {
		return
	}
	if u.builder.IsSlice() {
		u.builder.Save()
		u.err = u.builder.SliceAppend(reflect.ValueOf(b))
		if u.err != nil {
			return
		}
		u.builder.Load()
	} else {
		u.err = u.builder.SetBool(b)
	}
}

func (u *unmarshaler) FloatValue(n float64) {
	if u.skipping() || u.err != nil {
		return
	}
	if u.builder.IsSlice() {
		u.builder.Save()
		u.err = u.builder.SliceAppend(reflect.ValueOf(n))
		if u.err != nil {
			return
		}
		u.builder.Load()
	} else {
		u.err = u.builder.SetFloat(n)
	}
}

func (u *unmarshaler) IntValue(n int64) {
	if u.skipping() || u.err != nil {
		return
	}
	if u.builder.IsSlice() {
		u.builder.Save()
		u.err = u.builder.SliceAppend(reflect.ValueOf(n))
		if u.err != nil {
			return
		}
		u.builder.Load()
	} else {
		u.err = u.builder.SetInt(n)
	}
}

func (u *unmarshaler) LocalDateValue(date LocalDate) {
	if u.skipping() || u.err != nil {
		return
	}
	if u.builder.IsSlice() {
		u.builder.Save()
		u.err = u.builder.SliceAppend(reflect.ValueOf(date))
		if u.err != nil {
			return
		}
		u.builder.Load()
	} else {
		u.err = u.builder.Set(reflect.ValueOf(date))
	}
}

func (u *unmarshaler) LocalDateTimeValue(dt LocalDateTime) {
	if u.skipping() || u.err != nil {
		return
	}
	if u.builder.IsSlice() {
		u.builder.Save()
		u.err = u.builder.SliceAppend(reflect.ValueOf(dt))
		if u.err != nil {
			return
		}
		u.builder.Load()
	} else {
		u.err = u.builder.Set(reflect.ValueOf(dt))
	}
}

func (u *unmarshaler) DateTimeValue(dt time.Time) {
	if u.skipping() || u.err != nil {
		return
	}
	if u.builder.IsSlice() {
		u.builder.Save()
		u.err = u.builder.SliceAppend(reflect.ValueOf(dt))
		if u.err != nil {
			return
		}
		u.builder.Load()
	} else {
		u.err = u.builder.Set(reflect.ValueOf(dt))
	}
}

func (u *unmarshaler) LocalTimeValue(localTime LocalTime) {
	if u.skipping() || u.err != nil {
		return
	}
	if u.builder.IsSlice() {
		u.builder.Save()
		u.err = u.builder.SliceAppend(reflect.ValueOf(localTime))
		if u.err != nil {
			return
		}
		u.builder.Load()
	} else {
		u.err = u.builder.Set(reflect.ValueOf(localTime))
	}
}

func (u *unmarshaler) SimpleKey(v []byte) {
	if u.skipping() || u.err != nil {
		return
	}

	if u.parsingTableArray {
		u.arrayTableKey = append(u.arrayTableKey, v)
	} else {
		if u.builder.Cursor().Kind() == reflect.Slice {
			u.err = u.builder.SliceLastOrCreate()
			if u.err != nil {
				return
			}
		}
		u.err = u.builder.DigField(string(v))
		if u.err == nil {
			return
		}
		if _, ok := u.err.(reflectbuild.FieldNotFoundError); ok {
			u.err = nil
			if u.parsingTable {
				u.skipTable = true
			} else {
				u.skipKeyValCount = 1
			}
		}
		// TODO: figure out what to do with unexported fields
	}
}

func (u *unmarshaler) StandardTableBegin() {
	u.skipTable = false
	u.parsingTable = true
	if u.skipping() || u.err != nil {
		return
	}
	// tables are only top-level
	u.builder.Reset()
}

func (u *unmarshaler) StandardTableEnd() {
	u.parsingTable = false
	if u.skipping() || u.err != nil {
		return
	}
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 The DevMine 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 src

import "fmt"

// Unary operators
const (
	NOT  = "NOT"
	ADDR = "ADDR" // memory address (&foo)
	STAR = "STAR" // dereference operator (*foo)
)

type UnaryExpr struct {
	ExprName string `json:"expression_name"`
	Op       string `json:"operator"` // operator
	X        Expr   `json:"operand"`  // operand
}

func newUnaryExpr(m map[string]interface{}) (*UnaryExpr, error) {
	var err error
	errPrefix := "src/unary_expr"
	unaryexpr := UnaryExpr{}

	if typ, err := extractStringValue("expression_name", errPrefix, m); err != nil {
		// XXX It is not possible to add debug info on this error because it is
		// required that this error be en "errNotExist".
		return nil, errNotExist
	} else if typ != UnaryExprName {
		return nil, fmt.Errorf("invalid type: expected 'UnaryExpr', found '%s'", typ)
	}

	unaryexpr.ExprName = UnaryExprName

	if unaryexpr.Op, err = extractStringValue("operator", errPrefix, m); err != nil {
		return nil, addDebugInfo(err)
	}

	exprMap, err := extractMapValue("operand", errPrefix, m)
	if err != nil {
		return nil, addDebugInfo(err)
	}

	if unaryexpr.X, err = newExpr(exprMap); err != nil {
		return nil, addDebugInfo(err)
	}

	return &unaryexpr, nil
}
<commit_msg>src: add NEG and POS operator for UnaryExpr<commit_after>// Copyright 2014-2015 The DevMine 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 src

import "fmt"

// Unary operators
const (
	NOT  = "NOT"
	ADDR = "ADDR" // memory address (&foo)
	STAR = "STAR" // dereference operator (*foo)

	NEG = "NEG"
	POS = "POS"
)

type UnaryExpr struct {
	ExprName string `json:"expression_name"`
	Op       string `json:"operator"` // operator
	X        Expr   `json:"operand"`  // operand
}

func newUnaryExpr(m map[string]interface{}) (*UnaryExpr, error) {
	var err error
	errPrefix := "src/unary_expr"
	unaryexpr := UnaryExpr{}

	if typ, err := extractStringValue("expression_name", errPrefix, m); err != nil {
		// XXX It is not possible to add debug info on this error because it is
		// required that this error be en "errNotExist".
		return nil, errNotExist
	} else if typ != UnaryExprName {
		return nil, fmt.Errorf("invalid type: expected 'UnaryExpr', found '%s'", typ)
	}

	unaryexpr.ExprName = UnaryExprName

	if unaryexpr.Op, err = extractStringValue("operator", errPrefix, m); err != nil {
		return nil, addDebugInfo(err)
	}

	exprMap, err := extractMapValue("operand", errPrefix, m)
	if err != nil {
		return nil, addDebugInfo(err)
	}

	if unaryexpr.X, err = newExpr(exprMap); err != nil {
		return nil, addDebugInfo(err)
	}

	return &unaryexpr, nil
}
<|endoftext|>


<commit_before>// Package blackhole is an outbound handler that blocks all connections.
package blackhole

import (
	"context"
	"time"

	"v2ray.com/core/common"
	"v2ray.com/core/proxy"
	"v2ray.com/core/transport/ray"
)

// Handler is an outbound connection that silently swallow the entire payload.
type Handler struct {
	response ResponseConfig
}

// New creates a new blackhole handler.
func New(ctx context.Context, config *Config) (*Handler, error) {
	response, err := config.GetInternalResponse()
	if err != nil {
		return nil, err
	}
	return &Handler{
		response: response,
	}, nil
}

// Dispatch implements OutboundHandler.Dispatch().
func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dialer proxy.Dialer) error {
	v.response.WriteTo(outboundRay.OutboundOutput())
	// Sleep a little here to make sure the response is sent to client.
	time.Sleep(time.Second)
	outboundRay.OutboundOutput().Close()
	outboundRay.OutboundInput().CloseError()
	return nil
}

func init() {
	common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
		return New(ctx, config.(*Config))
	}))
}
<commit_msg>safe return in blackhole<commit_after>// Package blackhole is an outbound handler that blocks all connections.
package blackhole

import (
	"context"
	"time"

	"v2ray.com/core/common"
	"v2ray.com/core/proxy"
	"v2ray.com/core/transport/ray"
)

// Handler is an outbound connection that silently swallow the entire payload.
type Handler struct {
	response ResponseConfig
}

// New creates a new blackhole handler.
func New(ctx context.Context, config *Config) (*Handler, error) {
	response, err := config.GetInternalResponse()
	if err != nil {
		return nil, err
	}
	return &Handler{
		response: response,
	}, nil
}

// Process implements OutboundHandler.Dispatch().
func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dialer proxy.Dialer) error {
	v.response.WriteTo(outboundRay.OutboundOutput())
	// Sleep a little here to make sure the response is sent to client.
	time.Sleep(time.Second)
	outboundRay.OutboundOutput().CloseError()
	time.Sleep(time.Second * 2)
	return nil
}

func init() {
	common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
		return New(ctx, config.(*Config))
	}))
}
<|endoftext|>


<commit_before>package user

import (
	"compress/gzip"
	"compress/zlib"
	"errors"
	"github.com/mono83/slf/wd"
	"github.com/mono83/tame/page"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"
	"fmt"
)

// User represents new HTTP user.
type User struct {
	// User-Agent, used by this user
	UserAgent string
	// HTTP Referer
	Referer string
	// Cookies
	cookies map[string][]*http.Cookie
	// Other HTTP headers
	Header map[string]string

	m      sync.Mutex
	client *http.Client
	log    wd.Watchdog
}

// New creates new HTTP user.
func New() *User {
	u := &User{
		Header: map[string]string{
			"Accept":          "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
			"Accept-Encoding": "gzip, deflate, sdch, br",
			"Accept-Language": "en-US,en;q=0.8,ru;q=0.6,uk;q=0.4,pl;q=0.2",
		},
		cookies:   map[string][]*http.Cookie{},
		client:    &http.Client{},
		UserAgent: CommonUserAgents[rand.Intn(len(CommonUserAgents))],
		log:       wd.NewLogger("user"),
	}

	u.client.Jar = u
	return u
}

// NewRequest builds and returns new request of desired type with headers injected
func (u *User) NewRequest(method, addr string, body io.Reader) (*http.Request, error) {
	u.log.Debug("Building request :method :addr", wd.StringParam("method", method), wd.StringParam("addr", addr))
	req, err := http.NewRequest(method, addr, body)
	if err != nil {
		return nil, err
	}

	// Injecting common headers
	for name, value := range u.Header {
		req.Header.Set(name, value)
	}

	// Injecting user agent
	req.Header.Set("User-Agent", u.UserAgent)

	// Injecting referer
	if len(u.Referer) > 0 {
		req.Header.Set("Referer", u.Referer)
	}

	return req, nil
}

// Get performs GET request
func (u *User) Get(addr string) (*page.Page, error) {
	if len(addr) == 0 {
		return nil, errors.New("Empty remote address")
	}
	log := u.log.WithParams(wd.StringParam("addr", addr))

	// Building request
	req, err := u.NewRequest("GET", addr, nil)
	if err != nil {
		log.Error("Error building GET request :addr - :err", wd.ErrParam(err))
		return nil, err
	}

	// Injecting tracing
	req, trace := addTracing(req)

	// Sending request
	log.Debug("Sending request to :addr")
	resp, err := u.client.Do(req)
	trace.Close()
	if err != nil {
		log.Error("Error while GET :addr - :err", wd.ErrParam(err))
		return nil, err
	}
	defer resp.Body.Close()

	// Checking against compressed data
	var reader io.ReadCloser
	switch resp.Header.Get("Content-Encoding") {
	case "gzip":
		reader, err = gzip.NewReader(resp.Body)
		defer reader.Close()
	case "deflate":
		reader, err = zlib.NewReader(resp.Body)
		defer reader.Close()
	default:
		reader = resp.Body
	}
	if err != nil {
		log.Error("Unable to establish reader - :err", wd.ErrParam(err))
		return nil, err
	}

	// Reading response
	p := &page.Page{
		Trace:      *trace,
		URL:        req.URL,
		Header:     resp.Header,
		StatusCode: resp.StatusCode,
	}

	p.Body, err = ioutil.ReadAll(reader)
	if err != nil {
		log.Error("Unable to read response body for :addr - :err", wd.ErrParam(err))
		return nil, err
	}

	// Replacing URL if working using own buffer proxy
	if xt := resp.Header.Get("x-tame-original"); xt != "" {
		fmt.Println(xt)
		xtu, xterr := url.Parse(xt)
		if xterr == nil {
			p.URL = xtu
		}
	}

	return p, nil
}

// SetCookies handles the receipt of the cookies in a reply for the
// given URL.  It may or may not choose to save the cookies, depending
// on the jar's policy and implementation.
func (u *User) SetCookies(url *url.URL, cookies []*http.Cookie) {
	u.m.Lock()
	u.cookies[url.Host] = cookies
	// Invalidating old cookies
	now := time.Now()
	for h, cs := range u.cookies {
		newList := []*http.Cookie{}
		for _, c := range cs {
			if c.Expires.After(now) || len(c.RawExpires) == 0 {
				newList = append(newList, c)
			}
		}
		u.cookies[h] = newList
	}
	u.m.Unlock()
}

// Cookies returns the cookies to send in a request for the given URL.
// It is up to the implementation to honor the standard cookie use
// restrictions such as in RFC 6265.
func (u *User) Cookies(url *url.URL) []*http.Cookie {
	return u.cookies[url.Host]
}

// GetCookie returns cookie by its name
func (u *User) GetCookie(url *url.URL, name string) (string, bool) {
	if url == nil || len(name) == 0 {
		return "", false
	}

	u.m.Lock()
	defer u.m.Unlock()

	list, ok := u.cookies[url.Host]
	if !ok {
		return "", false
	}

	name = strings.ToLower(name)
	for _, c := range list {
		if name == strings.ToLower(c.Name) {
			return c.Value, true
		}
	}

	return "", false
}
<commit_msg>formatting<commit_after>package user

import (
	"compress/gzip"
	"compress/zlib"
	"errors"
	"fmt"
	"github.com/mono83/slf/wd"
	"github.com/mono83/tame/page"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"
)

// User represents new HTTP user.
type User struct {
	// User-Agent, used by this user
	UserAgent string
	// HTTP Referer
	Referer string
	// Cookies
	cookies map[string][]*http.Cookie
	// Other HTTP headers
	Header map[string]string

	m      sync.Mutex
	client *http.Client
	log    wd.Watchdog
}

// New creates new HTTP user.
func New() *User {
	u := &User{
		Header: map[string]string{
			"Accept":          "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
			"Accept-Encoding": "gzip, deflate, sdch, br",
			"Accept-Language": "en-US,en;q=0.8,ru;q=0.6,uk;q=0.4,pl;q=0.2",
		},
		cookies:   map[string][]*http.Cookie{},
		client:    &http.Client{},
		UserAgent: CommonUserAgents[rand.Intn(len(CommonUserAgents))],
		log:       wd.NewLogger("user"),
	}

	u.client.Jar = u
	return u
}

// NewRequest builds and returns new request of desired type with headers injected
func (u *User) NewRequest(method, addr string, body io.Reader) (*http.Request, error) {
	u.log.Debug("Building request :method :addr", wd.StringParam("method", method), wd.StringParam("addr", addr))
	req, err := http.NewRequest(method, addr, body)
	if err != nil {
		return nil, err
	}

	// Injecting common headers
	for name, value := range u.Header {
		req.Header.Set(name, value)
	}

	// Injecting user agent
	req.Header.Set("User-Agent", u.UserAgent)

	// Injecting referer
	if len(u.Referer) > 0 {
		req.Header.Set("Referer", u.Referer)
	}

	return req, nil
}

// Get performs GET request
func (u *User) Get(addr string) (*page.Page, error) {
	if len(addr) == 0 {
		return nil, errors.New("Empty remote address")
	}
	log := u.log.WithParams(wd.StringParam("addr", addr))

	// Building request
	req, err := u.NewRequest("GET", addr, nil)
	if err != nil {
		log.Error("Error building GET request :addr - :err", wd.ErrParam(err))
		return nil, err
	}

	// Injecting tracing
	req, trace := addTracing(req)

	// Sending request
	log.Debug("Sending request to :addr")
	resp, err := u.client.Do(req)
	trace.Close()
	if err != nil {
		log.Error("Error while GET :addr - :err", wd.ErrParam(err))
		return nil, err
	}
	defer resp.Body.Close()

	// Checking against compressed data
	var reader io.ReadCloser
	switch resp.Header.Get("Content-Encoding") {
	case "gzip":
		reader, err = gzip.NewReader(resp.Body)
		defer reader.Close()
	case "deflate":
		reader, err = zlib.NewReader(resp.Body)
		defer reader.Close()
	default:
		reader = resp.Body
	}
	if err != nil {
		log.Error("Unable to establish reader - :err", wd.ErrParam(err))
		return nil, err
	}

	// Reading response
	p := &page.Page{
		Trace:      *trace,
		URL:        req.URL,
		Header:     resp.Header,
		StatusCode: resp.StatusCode,
	}

	p.Body, err = ioutil.ReadAll(reader)
	if err != nil {
		log.Error("Unable to read response body for :addr - :err", wd.ErrParam(err))
		return nil, err
	}

	// Replacing URL if working using own buffer proxy
	if xt := resp.Header.Get("x-tame-original"); xt != "" {
		fmt.Println(xt)
		xtu, xterr := url.Parse(xt)
		if xterr == nil {
			p.URL = xtu
		}
	}

	return p, nil
}

// SetCookies handles the receipt of the cookies in a reply for the
// given URL.  It may or may not choose to save the cookies, depending
// on the jar's policy and implementation.
func (u *User) SetCookies(url *url.URL, cookies []*http.Cookie) {
	u.m.Lock()
	u.cookies[url.Host] = cookies
	// Invalidating old cookies
	now := time.Now()
	for h, cs := range u.cookies {
		newList := []*http.Cookie{}
		for _, c := range cs {
			if c.Expires.After(now) || len(c.RawExpires) == 0 {
				newList = append(newList, c)
			}
		}
		u.cookies[h] = newList
	}
	u.m.Unlock()
}

// Cookies returns the cookies to send in a request for the given URL.
// It is up to the implementation to honor the standard cookie use
// restrictions such as in RFC 6265.
func (u *User) Cookies(url *url.URL) []*http.Cookie {
	return u.cookies[url.Host]
}

// GetCookie returns cookie by its name
func (u *User) GetCookie(url *url.URL, name string) (string, bool) {
	if url == nil || len(name) == 0 {
		return "", false
	}

	u.m.Lock()
	defer u.m.Unlock()

	list, ok := u.cookies[url.Host]
	if !ok {
		return "", false
	}

	name = strings.ToLower(name)
	for _, c := range list {
		if name == strings.ToLower(c.Name) {
			return c.Value, true
		}
	}

	return "", false
}
<|endoftext|>


<commit_before>package genericsite

import (
	"crypto/sha256"
	"errors"
	"io"

	"github.com/hoisie/web"
	"github.com/xyproto/simpleredis"
)

const (
	LOGINCOOKIETIME = 3600 * 24 // The login cookie should last 24 hours
)

type UserState struct {
	// see: http://redis.io/topics/data-types
	users       *simpleredis.HashMap        // Hash map of users, with several different fields per user ("loggedin", "confirmed", "email" etc)
	usernames   *simpleredis.Set            // A list of all usernames, for easy enumeration
	unconfirmed *simpleredis.Set            // A list of unconfirmed usernames, for easy enumeration
	pool        *simpleredis.ConnectionPool // A connection pool for Redis
	dbindex     int
}

// Also creates a new ConnectionPool
func NewUserState(dbindex int) *UserState {
	pool := simpleredis.NewConnectionPool()
	state := new(UserState)

	state.users = simpleredis.NewHashMap(pool, "users")
	state.users.SelectDatabase(dbindex)

	state.usernames = simpleredis.NewSet(pool, "usernames")
	state.usernames.SelectDatabase(dbindex)

	state.unconfirmed = simpleredis.NewSet(pool, "unconfirmed")
	state.unconfirmed.SelectDatabase(dbindex)

	state.pool = pool

	state.dbindex = dbindex

	return state
}

func (state *UserState) GetDatabaseIndex() int {
	return state.dbindex
}

func (state *UserState) GetPool() *simpleredis.ConnectionPool {
	return state.pool
}

func (state *UserState) Close() {
	state.pool.Close()
}

// Checks if the current user is logged in as a user right now
func (state *UserState) UserRights(ctx *web.Context) bool {
	if username := GetBrowserUsername(ctx); username != "" {
		return state.IsLoggedIn(username)
	}
	return false
}

func (state *UserState) HasUser(username string) bool {
	val, err := state.usernames.Has(username)
	if err != nil {
		// This happened at concurrent connections before introducing the connection pool
		panic("ERROR: Lost connection to Redis?")
	}
	return val
}

func (state *UserState) GetBooleanField(username, fieldname string) bool {
	hasUser := state.HasUser(username)
	if !hasUser {
		return false
	}
	chatting, err := state.users.Get(username, fieldname)
	if err != nil {
		return false
	}
	return TruthValue(chatting)
}

func (state *UserState) SetBooleanField(username, fieldname string, val bool) {
	strval := "false"
	if val {
		strval = "true"
	}
	state.users.Set(username, fieldname, strval)
}

func (state *UserState) IsConfirmed(username string) bool {
	return state.GetBooleanField(username, "confirmed")
}

// Checks if the given username is logged in or not
func (state *UserState) IsLoggedIn(username string) bool {
	if !state.HasUser(username) {
		return false
	}
	status, err := state.users.Get(username, "loggedin")
	if err != nil {
		// Returns "no" if the status can not be retrieved
		return false
	}
	return TruthValue(status)
}

// Checks if the current user is logged in as Administrator right now
func (state *UserState) AdminRights(ctx *web.Context) bool {
	if username := GetBrowserUsername(ctx); username != "" {
		return state.IsLoggedIn(username) && state.IsAdmin(username)
	}
	return false
}

// Checks if the given username is an administrator
func (state *UserState) IsAdmin(username string) bool {
	if !state.HasUser(username) {
		return false
	}
	status, err := state.users.Get(username, "admin")
	if err != nil {
		return false
	}
	return TruthValue(status)
}

// Gets the username that is stored in a cookie in the browser, if available
func GetBrowserUsername(ctx *web.Context) string {
	username, _ := ctx.GetSecureCookie("user")
	// TODO: Return err, then the calling function should notify the user that cookies are needed
	return username
}

func (state *UserState) SetBrowserUsername(ctx *web.Context, username string) error {
	if username == "" {
		return errors.New("Can't set cookie for empty username")
	}
	if !state.HasUser(username) {
		return errors.New("Can't store cookie for non-existsing user")
	}
	timeout := state.GetCookieTimeout(username)
	// Create a cookie that lasts for a while ("timeout" seconds),
	// this is the equivivalent of a session for a given username.
	SetSecureCookiePath(ctx, "user", username, timeout, "/")
	return nil
}

func (state *UserState) GetAllUsernames() ([]string, error) {
	return state.usernames.GetAll()
}

func (state *UserState) GetEmail(username string) (string, error) {
	return state.users.Get(username, "email")
}

func (state *UserState) GetPasswordHash(username string) (string, error) {
	return state.users.Get(username, "password")
}

func (state *UserState) GetAllUnconfirmedUsernames() ([]string, error) {
	return state.unconfirmed.GetAll()
}

func (state *UserState) GetConfirmationCode(username string) (string, error) {
	return state.users.Get(username, "confirmationCode")
}

func (state *UserState) GetUsers() *simpleredis.HashMap {
	return state.users
}

// Add a user that has registered but not confirmed
func (state *UserState) AddUnconfirmed(username, confirmationCode string) {
	state.unconfirmed.Add(username)
	state.users.Set(username, "confirmationCode", confirmationCode)
}

// Remove a user that has registered but not confirmed
func (state *UserState) RemoveUnconfirmed(username string) {
	state.unconfirmed.Del(username)
	state.users.DelKey(username, "confirmationCode")
}

func (state *UserState) MarkConfirmed(username string) {
	state.users.Set(username, "confirmed", "true")
}

func (state *UserState) RemoveUser(username string) {
	state.usernames.Del(username)
	// Remove additional data as well
	state.users.DelKey(username, "loggedin")
}

func (state *UserState) SetAdminStatus(username string) {
	state.users.Set(username, "admin", "true")
}

func (state *UserState) RemoveAdminStatus(username string) {
	state.users.Set(username, "admin", "false")
}

// Creates a user from the username and password hash, does not check for rights
func (state *UserState) addUserUnchecked(username, passwordHash, email string) {
	// Add the user
	state.usernames.Add(username)

	// Add password and email
	state.users.Set(username, "password", passwordHash)
	state.users.Set(username, "email", email)

	// Addditional fields
	additionalfields := []string{"loggedin", "confirmed", "admin"}
	for _, fieldname := range additionalfields {
		state.users.Set(username, fieldname, "false")
	}
}

// Creates a user and hashes the password, does not check for rights
func (state *UserState) AddUser(username, password, email string) {
	passwordHash := HashPasswordVersion3(username, password)
	state.addUserUnchecked(username, passwordHash, email)
}

func (state *UserState) SetLoggedIn(username string) {
	state.users.Set(username, "loggedin", "true")
}

func (state *UserState) SetLoggedOut(username string) {
	state.users.Set(username, "loggedin", "false")
}

// Get how long a login cookie should last
func (state *UserState) GetCookieTimeout(username string) int64 {
	// TODO: Store this in state.users
	return LOGINCOOKIETIME
}

// Old password hashing function
func HashPasswordVersion2(password string) string {
	hasher := sha256.New()
	io.WriteString(hasher, password+"some salt is better than none")
	return string(hasher.Sum(nil))
}

// New password hashing function, with the username as part of the salt
func HashPasswordVersion3(username, password string) string {
	hasher := sha256.New()
	io.WriteString(hasher, password+"hi"+username)
	return string(hasher.Sum(nil))
}

// Check if a password is correct. username is used as part of the hash.
func CorrectPassword(state *UserState, username, password string) bool {
	passwordHash, err := state.GetPasswordHash(username)
	if err != nil {
		return false
	}
	if passwordHash == HashPasswordVersion3(username, password) {
		return true
	}
	if passwordHash == HashPasswordVersion2(password) {
		return true
	}
	return false
}

// Goes through all the confirmationCodes of all the unconfirmed users
// and checks if this confirmationCode already is in use
func AlreadyHasConfirmationCode(state *UserState, confirmationCode string) bool {
	unconfirmedUsernames, err := state.GetAllUnconfirmedUsernames()
	if err != nil {
		return false
	}
	for _, aUsername := range unconfirmedUsernames {
		aConfirmationCode, err := state.GetConfirmationCode(aUsername)
		if err != nil {
			// If the confirmation code can not be found, that's okay too
			return false
		}
		if confirmationCode == aConfirmationCode {
			// Found it
			return true
		}
	}
	return false
}
<commit_msg>Using xyproto/permissions instead<commit_after><|endoftext|>


<commit_before>package uspv

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"strings"

	"github.com/mit-dci/lit/logging"

	"github.com/mit-dci/lit/lnutil"
	"github.com/mit-dci/lit/wire"
	"golang.org/x/net/proxy"
)

// IP4 ...
func IP4(ipAddress string) bool {
	parseIP := net.ParseIP(ipAddress)
	if parseIP.To4() == nil {
		return false
	}
	return true
}

func (s *SPVCon) parseRemoteNode(remoteNode string) (string, string, error) {
	colonCount := strings.Count(remoteNode, ":")
	var conMode string
	if colonCount == 0 {
		if IP4(remoteNode) || remoteNode == "localhost" { // need this to connect to locahost
			remoteNode = remoteNode + ":" + s.Param.DefaultPort
		}
		// only ipv4 clears this since ipv6 has colons
		conMode = "tcp4"
		return remoteNode, conMode, nil
	} else if colonCount == 1 && IP4(strings.Split(remoteNode, ":")[0]) {
		// custom port on ipv4
		return remoteNode, "tcp4", nil
	} else if colonCount >= 5 {
		// ipv6 without remote port
		// assume users don't give ports with ipv6 nodes
		if !strings.Contains(remoteNode, "[") && !strings.Contains(remoteNode, "]") {
			remoteNode = "[" + remoteNode + "]" + ":" + s.Param.DefaultPort
		}
		conMode = "tcp6"
		return remoteNode, conMode, nil
	} else {
		return "", "", fmt.Errorf("Invalid ip")
	}
}

// GetListOfNodes contacts all DNSSeeds for the coin specified and then contacts
// each one of them in order to receive a list of ips and then returns a combined
// list
func (s *SPVCon) GetListOfNodes() ([]string, error) {
	var listOfNodes []string // slice of IP addrs returned from the DNS seed
	logging.Infof("Attempting to retrieve peers to connect to based on DNS Seed\n")

	for _, seed := range s.Param.DNSSeeds {
		temp, err := net.LookupHost(seed)
		// need this temp in order to capture the error from net.LookupHost
		// also need this to report the number of IPs we get from a seed
		if err != nil {
			logging.Infof("Have difficulty trying to connect to %s. Going to the next seed", seed)
			continue
		}
		listOfNodes = append(listOfNodes, temp...)
		logging.Infof("Got %d IPs from DNS seed %s\n", len(temp), seed)
	}
	if len(listOfNodes) == 0 {
		return nil, fmt.Errorf("No peers found connected to DNS Seeds. Please provide a host to connect to.")
	}
	logging.Info(listOfNodes)
	return listOfNodes, nil
}

// DialNode receives a list of node ips and then tries to connect to them one by one.
func (s *SPVCon) DialNode(listOfNodes []string) (net.Conn, error) {

	// now have some IPs, go through and try to connect to one.
	var err error
	var con net.Conn
	for i, ip := range listOfNodes {
		// try to connect to all nodes in this range
		var conString, conMode string
		// need to check whether conString is ipv4 or ipv6
		conString, conMode, err = s.parseRemoteNode(ip)
		if err != nil {
			logging.Infof("parse error for node (skipped): %s", err)
			continue
		}
		logging.Infof("Attempting connection to node at %s\n",
			conString)

		if s.ProxyURL != "" {
			logging.Infof("Attempting to connect via proxy %s", s.ProxyURL)
			var d proxy.Dialer
			d, err = proxy.SOCKS5("tcp", s.ProxyURL, nil, proxy.Direct)
			if err != nil {
				return nil, err
			}

			con, err = d.Dial(conMode, conString)
		} else {
			con, err = net.Dial(conMode, conString)
		}

		if err != nil {
			if i != len(listOfNodes)-1 {
				logging.Warn(err.Error())
				continue
			} else if i == len(listOfNodes)-1 {
				logging.Error(err)
				// all nodes have been exhausted, we move on to the next one, if any.
				return nil, fmt.Errorf(" Tried to connect to all available node Addresses. Failed")
			}
		}
		break
	}

	if con == nil {
		return nil, fmt.Errorf("Failed to connect to a coin daemon")
	}

	return con, nil
}

// Handshake ...
func (s *SPVCon) Handshake(listOfNodes []string) error {
	// assign version bits for local node
	s.localVersion = VERSION
	myMsgVer, err := wire.NewMsgVersionFromConn(s.con, 0, 0)
	if err != nil {
		return err
	}
	err = myMsgVer.AddUserAgent("lit", "v0.1")
	if err != nil {
		return err
	}
	// must set this to enable SPV stuff
	myMsgVer.AddService(wire.SFNodeBloom)
	// set this to enable segWit
	myMsgVer.AddService(wire.SFNodeWitness)
	// this actually sends
	n, err := wire.WriteMessageWithEncodingN(
		s.con, myMsgVer, s.localVersion,
		wire.BitcoinNet(s.Param.NetMagicBytes), wire.LatestEncoding)
	if err != nil {
		return err
	}
	s.WBytes += uint64(n)
	logging.Infof("wrote %d byte version message to %s\n",
		n, s.con.RemoteAddr().String())
	n, m, b, err := wire.ReadMessageWithEncodingN(
		s.con, s.localVersion,
		wire.BitcoinNet(s.Param.NetMagicBytes), wire.LatestEncoding)
	if err != nil {
		logging.Error(err)
		return err
	}
	s.RBytes += uint64(n)
	logging.Infof("got %d byte response %x\n command: %s\n", n, b, m.Command())

	mv, ok := m.(*wire.MsgVersion)
	if ok {
		logging.Infof("connected to %s", mv.UserAgent)
	}

	if mv.ProtocolVersion < 70013 {
		//70014 -> core v0.13.1, so we should be fine
		return fmt.Errorf("Remote node version: %x too old, disconnecting.", mv.ProtocolVersion)
	}

	if !((strings.Contains(s.Param.Name, "lite") && strings.Contains(mv.UserAgent, "LitecoinCore")) || strings.Contains(mv.UserAgent, "Satoshi") || strings.Contains(mv.UserAgent, "btcd")) && (len(listOfNodes) != 0) {
		// TODO: improve this filtering criterion
		return fmt.Errorf("Couldn't connect to this node. Returning!")
	}

	logging.Infof("remote reports version %x (dec %d)\n",
		mv.ProtocolVersion, mv.ProtocolVersion)

	// set remote height
	s.remoteHeight = mv.LastBlock
	// set remote version
	s.remoteVersion = uint32(mv.ProtocolVersion)

	mva := wire.NewMsgVerAck()
	n, err = wire.WriteMessageWithEncodingN(
		s.con, mva, s.localVersion,
		wire.BitcoinNet(s.Param.NetMagicBytes), wire.LatestEncoding)
	if err != nil {
		return err
	}
	s.WBytes += uint64(n)
	return nil
}

// Connect dials out and connects to full nodes. Calls GetListOfNodes to get the
// list of nodes if the user has specified a YupString. Else, moves on to dial
// the node to see if its up and establishes a connection followed by Handshake()
// which sends out wire messages, checks for version string to prevent spam, etc.
func (s *SPVCon) Connect(remoteNode string) error {
	var err error
	var listOfNodes []string
	if lnutil.YupString(remoteNode) { // TODO Make this better.  Perhaps a "connection target"?
		s.randomNodesOK = true
		// if remoteNode is "yes" but no IP specified, use DNS seed
		listOfNodes, err = s.GetListOfNodes()
		if err != nil {
			logging.Error(err)
			return err
			// automatically quit if there are no other hosts to connect to.
		}
	} else { // else connect to user-specified node
		listOfNodes = []string{remoteNode}
	}
	handShakeFailed := false // need to be in this scope to access it here
	connEstablished := false
	for len(listOfNodes) != 0 {
		s.con, err = s.DialNode(listOfNodes)
		if err != nil {
			logging.Error(err)
			logging.Infof("Couldn't dial node %s, Moving on", listOfNodes[0])
			listOfNodes = listOfNodes[1:]
			continue
		}
		err = s.Handshake(listOfNodes)
		if err != nil {
			handShakeFailed = true
			logging.Infof("Handshake with %s failed. Moving on. Error: %s", listOfNodes[0], err.Error())
			if len(listOfNodes) == 1 { // when the list is empty, error out
				return fmt.Errorf("Couldn't establish connection with any remote node. Exiting.")
			}
			// means we either have a sapm node or didn't get a resonse. So we Try again
			logging.Error(err)
			logging.Error("Couldn't establish connection with node. Proceeding to the next one")
			listOfNodes = listOfNodes[1:]
			connEstablished = false
		} else {
			connEstablished = true
		}
		if connEstablished { // connection should be established, still checking for safety
			break
		} else {
			continue
		}
	}

	if !handShakeFailed && !connEstablished {
		// this case happens when user provided node fails to connect
		return fmt.Errorf("Couldn't establish connection with node. Exiting.")
	}
	if handShakeFailed && !connEstablished {
		// this case is when the last node fails and we continue, only to exit the
		// loop and execute below code, which is unnecessary.
		return fmt.Errorf("Couldn't establish connection with any remote node after an instance of handshake. Exiting.")
	}
	s.inMsgQueue = make(chan wire.Message)
	go s.incomingMessageHandler()
	s.outMsgQueue = make(chan wire.Message)
	go s.outgoingMessageHandler()

	if s.HardMode {
		s.blockQueue = make(chan HashAndHeight, 32) // queue depth 32 for hardmode.
	} else {
		// for SPV, concurrent in-flight merkleblocks makes us miss txs.
		// The BloomUpdateAll setting seems like it should prevent it, but it
		// doesn't; occasionally it misses transactions, seems like with low
		// block index.  Could be a bug somewhere.  1 at a time merkleblock
		// seems OK.
		s.blockQueue = make(chan HashAndHeight, 1) // queue depth 1 for spv
	}
	s.fPositives = make(chan int32, 4000) // a block full, approx
	s.inWaitState = make(chan bool, 1)
	go s.fPositiveHandler()

	// if s.HardMode { // what about for non-hard?  send filter?
	// 	Ignore filters now; switch to filters fed to SPVcon from TS
	// 		filt, err := s.TS.GimmeFilter()
	// 		if err != nil {
	// 			return err
	// 		}
	// 		s.localFilter = filt
	// 		//		s.Refilter(filt)
	// }

	return nil
}

/*
Truncated header files
Like a regular header but the first 80 bytes is mostly empty.
The very first 4 bytes (big endian) says what height the empty 80 bytes
replace.  The next header, starting at offset 80, needs to be valid.
*/
func (s *SPVCon) openHeaderFile(hfn string) error {
	_, err := os.Stat(hfn)
	if err != nil {
		if os.IsNotExist(err) {
			var b bytes.Buffer
			// if StartHeader is defined, start with hardcoded height
			if s.Param.StartHeight != 0 {
				hdr := s.Param.StartHeader
				_, err := b.Write(hdr[:])
				if err != nil {
					return err
				}
			} else {
				err = s.Param.GenesisBlock.Header.Serialize(&b)
				if err != nil {
					return err
				}
			}
			err = ioutil.WriteFile(hfn, b.Bytes(), 0600)
			if err != nil {
				return err
			}
			logging.Infof("made genesis header %x\n", b.Bytes())
			logging.Infof("made genesis hash %s\n", s.Param.GenesisHash.String())
			logging.Infof("created hardcoded genesis header at %s\n", hfn)
		}
	}

	if s.Param.StartHeight != 0 {
		s.headerStartHeight = s.Param.StartHeight
	}

	s.headerFile, err = os.OpenFile(hfn, os.O_RDWR, 0600)
	if err != nil {
		return err
	}
	logging.Infof("opened header file %s\n", s.headerFile.Name())
	return nil
}
<commit_msg>Allow connecting to coindaemon peers by hostname, not just localhost (#368)<commit_after>package uspv

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"strings"

	"github.com/mit-dci/lit/logging"

	"github.com/mit-dci/lit/lnutil"
	"github.com/mit-dci/lit/wire"
	"golang.org/x/net/proxy"
)

// IP4 ...
func IP4(ipAddress string) bool {
	parseIP := net.ParseIP(ipAddress)
	if parseIP.To4() == nil {
		return false
	}
	return true
}

func (s *SPVCon) parseRemoteNode(remoteNode string) (string, string, error) {
	colonCount := strings.Count(remoteNode, ":")
	var conMode string
	if colonCount <= 1 {
		if colonCount == 0 {
			remoteNode = remoteNode + ":" + s.Param.DefaultPort
		}
		return remoteNode, "tcp4", nil
	} else if colonCount >= 5 {
		// ipv6 without remote port
		// assume users don't give ports with ipv6 nodes
		if !strings.Contains(remoteNode, "[") && !strings.Contains(remoteNode, "]") {
			remoteNode = "[" + remoteNode + "]" + ":" + s.Param.DefaultPort
		}
		conMode = "tcp6"
		return remoteNode, conMode, nil
	} else {
		return "", "", fmt.Errorf("Invalid ip")
	}
}

// GetListOfNodes contacts all DNSSeeds for the coin specified and then contacts
// each one of them in order to receive a list of ips and then returns a combined
// list
func (s *SPVCon) GetListOfNodes() ([]string, error) {
	var listOfNodes []string // slice of IP addrs returned from the DNS seed
	logging.Infof("Attempting to retrieve peers to connect to based on DNS Seed\n")

	for _, seed := range s.Param.DNSSeeds {
		temp, err := net.LookupHost(seed)
		// need this temp in order to capture the error from net.LookupHost
		// also need this to report the number of IPs we get from a seed
		if err != nil {
			logging.Infof("Have difficulty trying to connect to %s. Going to the next seed", seed)
			continue
		}
		listOfNodes = append(listOfNodes, temp...)
		logging.Infof("Got %d IPs from DNS seed %s\n", len(temp), seed)
	}
	if len(listOfNodes) == 0 {
		return nil, fmt.Errorf("No peers found connected to DNS Seeds. Please provide a host to connect to.")
	}
	logging.Info(listOfNodes)
	return listOfNodes, nil
}

// DialNode receives a list of node ips and then tries to connect to them one by one.
func (s *SPVCon) DialNode(listOfNodes []string) (net.Conn, error) {

	// now have some IPs, go through and try to connect to one.
	var err error
	var con net.Conn
	for i, ip := range listOfNodes {
		// try to connect to all nodes in this range
		var conString, conMode string
		// need to check whether conString is ipv4 or ipv6
		conString, conMode, err = s.parseRemoteNode(ip)
		if err != nil {
			logging.Infof("parse error for node (skipped): %s", err)
			continue
		}
		logging.Infof("Attempting connection to node at %s\n",
			conString)

		if s.ProxyURL != "" {
			logging.Infof("Attempting to connect via proxy %s", s.ProxyURL)
			var d proxy.Dialer
			d, err = proxy.SOCKS5("tcp", s.ProxyURL, nil, proxy.Direct)
			if err != nil {
				return nil, err
			}

			con, err = d.Dial(conMode, conString)
		} else {
			con, err = net.Dial(conMode, conString)
		}

		if err != nil {
			if i != len(listOfNodes)-1 {
				logging.Warn(err.Error())
				continue
			} else if i == len(listOfNodes)-1 {
				logging.Error(err)
				// all nodes have been exhausted, we move on to the next one, if any.
				return nil, fmt.Errorf(" Tried to connect to all available node Addresses. Failed")
			}
		}
		break
	}

	if con == nil {
		return nil, fmt.Errorf("Failed to connect to a coin daemon")
	}

	return con, nil
}

// Handshake ...
func (s *SPVCon) Handshake(listOfNodes []string) error {
	// assign version bits for local node
	s.localVersion = VERSION
	myMsgVer, err := wire.NewMsgVersionFromConn(s.con, 0, 0)
	if err != nil {
		return err
	}
	err = myMsgVer.AddUserAgent("lit", "v0.1")
	if err != nil {
		return err
	}
	// must set this to enable SPV stuff
	myMsgVer.AddService(wire.SFNodeBloom)
	// set this to enable segWit
	myMsgVer.AddService(wire.SFNodeWitness)
	// this actually sends
	n, err := wire.WriteMessageWithEncodingN(
		s.con, myMsgVer, s.localVersion,
		wire.BitcoinNet(s.Param.NetMagicBytes), wire.LatestEncoding)
	if err != nil {
		return err
	}
	s.WBytes += uint64(n)
	logging.Infof("wrote %d byte version message to %s\n",
		n, s.con.RemoteAddr().String())
	n, m, b, err := wire.ReadMessageWithEncodingN(
		s.con, s.localVersion,
		wire.BitcoinNet(s.Param.NetMagicBytes), wire.LatestEncoding)
	if err != nil {
		logging.Error(err)
		return err
	}
	s.RBytes += uint64(n)
	logging.Infof("got %d byte response %x\n command: %s\n", n, b, m.Command())

	mv, ok := m.(*wire.MsgVersion)
	if ok {
		logging.Infof("connected to %s", mv.UserAgent)
	}

	if mv.ProtocolVersion < 70013 {
		//70014 -> core v0.13.1, so we should be fine
		return fmt.Errorf("Remote node version: %x too old, disconnecting.", mv.ProtocolVersion)
	}

	if !((strings.Contains(s.Param.Name, "lite") && strings.Contains(mv.UserAgent, "LitecoinCore")) || strings.Contains(mv.UserAgent, "Satoshi") || strings.Contains(mv.UserAgent, "btcd")) && (len(listOfNodes) != 0) {
		// TODO: improve this filtering criterion
		return fmt.Errorf("Couldn't connect to this node. Returning!")
	}

	logging.Infof("remote reports version %x (dec %d)\n",
		mv.ProtocolVersion, mv.ProtocolVersion)

	// set remote height
	s.remoteHeight = mv.LastBlock
	// set remote version
	s.remoteVersion = uint32(mv.ProtocolVersion)

	mva := wire.NewMsgVerAck()
	n, err = wire.WriteMessageWithEncodingN(
		s.con, mva, s.localVersion,
		wire.BitcoinNet(s.Param.NetMagicBytes), wire.LatestEncoding)
	if err != nil {
		return err
	}
	s.WBytes += uint64(n)
	return nil
}

// Connect dials out and connects to full nodes. Calls GetListOfNodes to get the
// list of nodes if the user has specified a YupString. Else, moves on to dial
// the node to see if its up and establishes a connection followed by Handshake()
// which sends out wire messages, checks for version string to prevent spam, etc.
func (s *SPVCon) Connect(remoteNode string) error {
	var err error
	var listOfNodes []string
	if lnutil.YupString(remoteNode) { // TODO Make this better.  Perhaps a "connection target"?
		s.randomNodesOK = true
		// if remoteNode is "yes" but no IP specified, use DNS seed
		listOfNodes, err = s.GetListOfNodes()
		if err != nil {
			logging.Error(err)
			return err
			// automatically quit if there are no other hosts to connect to.
		}
	} else { // else connect to user-specified node
		listOfNodes = []string{remoteNode}
	}
	handShakeFailed := false // need to be in this scope to access it here
	connEstablished := false
	for len(listOfNodes) != 0 {
		s.con, err = s.DialNode(listOfNodes)
		if err != nil {
			logging.Error(err)
			logging.Infof("Couldn't dial node %s, Moving on", listOfNodes[0])
			listOfNodes = listOfNodes[1:]
			continue
		}
		err = s.Handshake(listOfNodes)
		if err != nil {
			handShakeFailed = true
			logging.Infof("Handshake with %s failed. Moving on. Error: %s", listOfNodes[0], err.Error())
			if len(listOfNodes) == 1 { // when the list is empty, error out
				return fmt.Errorf("Couldn't establish connection with any remote node. Exiting.")
			}
			// means we either have a sapm node or didn't get a resonse. So we Try again
			logging.Error(err)
			logging.Error("Couldn't establish connection with node. Proceeding to the next one")
			listOfNodes = listOfNodes[1:]
			connEstablished = false
		} else {
			connEstablished = true
		}
		if connEstablished { // connection should be established, still checking for safety
			break
		} else {
			continue
		}
	}

	if !handShakeFailed && !connEstablished {
		// this case happens when user provided node fails to connect
		return fmt.Errorf("Couldn't establish connection with node. Exiting.")
	}
	if handShakeFailed && !connEstablished {
		// this case is when the last node fails and we continue, only to exit the
		// loop and execute below code, which is unnecessary.
		return fmt.Errorf("Couldn't establish connection with any remote node after an instance of handshake. Exiting.")
	}
	s.inMsgQueue = make(chan wire.Message)
	go s.incomingMessageHandler()
	s.outMsgQueue = make(chan wire.Message)
	go s.outgoingMessageHandler()

	if s.HardMode {
		s.blockQueue = make(chan HashAndHeight, 32) // queue depth 32 for hardmode.
	} else {
		// for SPV, concurrent in-flight merkleblocks makes us miss txs.
		// The BloomUpdateAll setting seems like it should prevent it, but it
		// doesn't; occasionally it misses transactions, seems like with low
		// block index.  Could be a bug somewhere.  1 at a time merkleblock
		// seems OK.
		s.blockQueue = make(chan HashAndHeight, 1) // queue depth 1 for spv
	}
	s.fPositives = make(chan int32, 4000) // a block full, approx
	s.inWaitState = make(chan bool, 1)
	go s.fPositiveHandler()

	// if s.HardMode { // what about for non-hard?  send filter?
	// 	Ignore filters now; switch to filters fed to SPVcon from TS
	// 		filt, err := s.TS.GimmeFilter()
	// 		if err != nil {
	// 			return err
	// 		}
	// 		s.localFilter = filt
	// 		//		s.Refilter(filt)
	// }

	return nil
}

/*
Truncated header files
Like a regular header but the first 80 bytes is mostly empty.
The very first 4 bytes (big endian) says what height the empty 80 bytes
replace.  The next header, starting at offset 80, needs to be valid.
*/
func (s *SPVCon) openHeaderFile(hfn string) error {
	_, err := os.Stat(hfn)
	if err != nil {
		if os.IsNotExist(err) {
			var b bytes.Buffer
			// if StartHeader is defined, start with hardcoded height
			if s.Param.StartHeight != 0 {
				hdr := s.Param.StartHeader
				_, err := b.Write(hdr[:])
				if err != nil {
					return err
				}
			} else {
				err = s.Param.GenesisBlock.Header.Serialize(&b)
				if err != nil {
					return err
				}
			}
			err = ioutil.WriteFile(hfn, b.Bytes(), 0600)
			if err != nil {
				return err
			}
			logging.Infof("made genesis header %x\n", b.Bytes())
			logging.Infof("made genesis hash %s\n", s.Param.GenesisHash.String())
			logging.Infof("created hardcoded genesis header at %s\n", hfn)
		}
	}

	if s.Param.StartHeight != 0 {
		s.headerStartHeight = s.Param.StartHeight
	}

	s.headerFile, err = os.OpenFile(hfn, os.O_RDWR, 0600)
	if err != nil {
		return err
	}
	logging.Infof("opened header file %s\n", s.headerFile.Name())
	return nil
}
<|endoftext|>


<commit_before>package util

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"syscall"
	"unsafe"
)

var (
	winCmd   = "url.dll,FileProtocolHandler"
	runDll32 = filepath.Join(os.Getenv("SYSTEMROOT"), "System32", "rundll32.exe")
)

func OpenCommand(url string) (*exec.Cmd, error) {
	if runtime.GOOS == "windows" {
		return exec.Command(runDll32, winCmd, url), nil
	} else if runtime.GOOS == "darwin" {
		return exec.Command("open", url), nil
	} else if runtime.GOOS == "linux" {
		return exec.Command("xdg-open", url), nil
	} else {
		return &exec.Cmd{}, errors.New("Sorry bro your hipster OS isn't supported")
	}
}

func GetSize() (width, height int) {
	var dimensions [4]uint16
	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(1), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0); err != 0 {
		return -1, -1
	}

	return int(dimensions[1]), int(dimensions[0])
}

func StartProgram(program string, url string) {
	binary, _ := exec.LookPath(program)
	args := []string{program, url}
	env := os.Environ()

	syscall.Exec(binary, args, env)
}

func Truncate(s string, max int) string {
	r := []rune(s)
	if len(r) > max {
		return string(r[:max])
	} else {
		return s
	}
}

func Pick(max int) int {
	var input string
	for {
		fmt.Print("Pick: ")
		_, err := fmt.Scanln(&input)
		if err != nil {
			fmt.Println(err)
			continue
		}
		out, err := strconv.ParseInt(input, 10, 32)
		outi := int(out)
		if err != nil {
			fmt.Println(err)
			continue
		}
		if outi < 1 || outi > max {
			fmt.Println("Out of range")
			continue
		}

		return outi
	}
}

type PadSet struct {
	Str string
	Pad PadMode
}

func P(str string, mode PadMode) *PadSet {
	return &PadSet{
		Str: str,
		Pad: mode,
	}
}

type PadMode int

const (
	LeftPad PadMode = iota
	RightPad
)

func Pad(str string, pad rune, length int, mode PadMode) string {
	if len([]rune(str)) >= length {
		return str
	} else {
		c := length - len([]rune(str))
		b := make([]rune, c)
		for i := range b {
			b[i] = pad
		}

		if mode == LeftPad {
			return string(b) + str
		} else {
			return str + string(b)
		}
	}

}

func PadFormat(input [][]PadSet) []string {
	output := make([]string, len(input))
	paddings := make([]int, len(input[0]))

	for _, s := range input {
		plen := len(s)
		if plen > len(paddings) {
			newp := make([]int, plen)
			copy(paddings, newp)
			paddings = newp
		}

		for ci, p := range s {
			cl := len([]rune(p.Str))
			if cl > paddings[ci] {
				paddings[ci] = cl
			}
		}
	}

	for sid, s := range input {
		outcol := make([]string, len(paddings))
		for ci, c := range s {
			outcol[ci] = Pad(c.Str, ' ', paddings[ci], c.Pad)
		}
		output[sid] = strings.Join(outcol, " ")
	}

	return output
}
<commit_msg>windows goes to jupiter to get more stupider<commit_after>package util

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"syscall"
	"unsafe"
)

var (
	winCmd   = "url.dll,FileProtocolHandler"
	runDll32 = filepath.Join(os.Getenv("SYSTEMROOT"), "System32", "rundll32.exe")
)

func OpenCommand(url string) (*exec.Cmd, error) {
	if runtime.GOOS == "windows" {
		u := strings.Replace(url, "&", "^&", -1)
		return exec.Command(runDll32, winCmd, u), nil
	} else if runtime.GOOS == "darwin" {
		return exec.Command("open", url), nil
	} else if runtime.GOOS == "linux" {
		return exec.Command("xdg-open", url), nil
	} else {
		return &exec.Cmd{}, errors.New("Sorry bro your hipster OS isn't supported")
	}
}

func GetSize() (width, height int) {
	var dimensions [4]uint16
	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(1), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0); err != 0 {
		return -1, -1
	}

	return int(dimensions[1]), int(dimensions[0])
}

func StartProgram(program string, url string) {
	binary, _ := exec.LookPath(program)
	args := []string{program, url}
	env := os.Environ()

	syscall.Exec(binary, args, env)
}

func Truncate(s string, max int) string {
	r := []rune(s)
	if len(r) > max {
		return string(r[:max])
	} else {
		return s
	}
}

func Pick(max int) int {
	var input string
	for {
		fmt.Print("Pick: ")
		_, err := fmt.Scanln(&input)
		if err != nil {
			fmt.Println(err)
			continue
		}
		out, err := strconv.ParseInt(input, 10, 32)
		outi := int(out)
		if err != nil {
			fmt.Println(err)
			continue
		}
		if outi < 1 || outi > max {
			fmt.Println("Out of range")
			continue
		}

		return outi
	}
}

type PadSet struct {
	Str string
	Pad PadMode
}

func P(str string, mode PadMode) *PadSet {
	return &PadSet{
		Str: str,
		Pad: mode,
	}
}

type PadMode int

const (
	LeftPad PadMode = iota
	RightPad
)

func Pad(str string, pad rune, length int, mode PadMode) string {
	if len([]rune(str)) >= length {
		return str
	} else {
		c := length - len([]rune(str))
		b := make([]rune, c)
		for i := range b {
			b[i] = pad
		}

		if mode == LeftPad {
			return string(b) + str
		} else {
			return str + string(b)
		}
	}

}

func PadFormat(input [][]PadSet) []string {
	output := make([]string, len(input))
	paddings := make([]int, len(input[0]))

	for _, s := range input {
		plen := len(s)
		if plen > len(paddings) {
			newp := make([]int, plen)
			copy(paddings, newp)
			paddings = newp
		}

		for ci, p := range s {
			cl := len([]rune(p.Str))
			if cl > paddings[ci] {
				paddings[ci] = cl
			}
		}
	}

	for sid, s := range input {
		outcol := make([]string, len(paddings))
		for ci, c := range s {
			outcol[ci] = Pad(c.Str, ' ', paddings[ci], c.Pad)
		}
		output[sid] = strings.Join(outcol, " ")
	}

	return output
}
<|endoftext|>