<commit_before>// Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2014-2015 Edmund Huber
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package irc

import (
	"strconv"
	"strings"

	"github.com/oragono/oragono/irc/modes"
	"github.com/oragono/oragono/irc/sno"
)

var (
	// DefaultChannelModes are enabled on brand new channels when they're created.
	// this can be overridden in the `channels` config, with the `default-modes` key
	DefaultChannelModes = modes.Modes{
		modes.NoOutside, modes.OpOnlyTopic,
	}
)

// ApplyUserModeChanges applies the given changes, and returns the applied changes.
func ApplyUserModeChanges(client *Client, changes modes.ModeChanges, force bool) modes.ModeChanges {
	applied := make(modes.ModeChanges, 0)

	for _, change := range changes {
		switch change.Mode {
		case modes.Bot, modes.Invisible, modes.WallOps, modes.UserRoleplaying, modes.Operator, modes.LocalOperator, modes.RegisteredOnly:
			switch change.Op {
			case modes.Add:
				if !force && (change.Mode == modes.Operator || change.Mode == modes.LocalOperator) {
					continue
				}

				if client.SetMode(change.Mode, true) {
					if change.Mode == modes.Invisible {
						client.server.stats.ChangeInvisible(1)
					} else if change.Mode == modes.Operator || change.Mode == modes.LocalOperator {
						client.server.stats.ChangeOperators(1)
					}
					applied = append(applied, change)
				}

			case modes.Remove:
				if client.SetMode(change.Mode, false) {
					if change.Mode == modes.Invisible {
						client.server.stats.ChangeInvisible(-1)
					} else if change.Mode == modes.Operator || change.Mode == modes.LocalOperator {
						client.server.stats.ChangeOperators(-1)
					}
					applied = append(applied, change)
				}
			}

		case modes.ServerNotice:
			if !client.HasMode(modes.Operator) {
				continue
			}
			var masks []sno.Mask
			if change.Op == modes.Add || change.Op == modes.Remove {
				var newArg string
				for _, char := range change.Arg {
					mask := sno.Mask(char)
					if sno.ValidMasks[mask] {
						masks = append(masks, mask)
						newArg += string(char)
					}
				}
				change.Arg = newArg
			}
			if change.Op == modes.Add {
				client.server.snomasks.AddMasks(client, masks...)
				applied = append(applied, change)
			} else if change.Op == modes.Remove {
				client.server.snomasks.RemoveMasks(client, masks...)
				applied = append(applied, change)
			}
		}

		// can't do anything to TLS mode
	}

	// return the changes we could actually apply
	return applied
}

// ParseDefaultChannelModes parses the `default-modes` line of the config
func ParseDefaultChannelModes(rawModes *string) modes.Modes {
	if rawModes == nil {
		// not present in config, fall back to compile-time default
		return DefaultChannelModes
	}
	modeChangeStrings := strings.Fields(*rawModes)
	modeChanges, _ := modes.ParseChannelModeChanges(modeChangeStrings...)
	defaultChannelModes := make(modes.Modes, 0)
	for _, modeChange := range modeChanges {
		if modeChange.Op == modes.Add {
			defaultChannelModes = append(defaultChannelModes, modeChange.Mode)
		}
	}
	return defaultChannelModes
}

// ApplyChannelModeChanges applies a given set of mode changes.
func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, changes modes.ModeChanges, rb *ResponseBuffer) modes.ModeChanges {
	// so we only output one warning for each list type when full
	listFullWarned := make(map[modes.Mode]bool)

	var alreadySentPrivError bool

	applied := make(modes.ModeChanges, 0)

	isListOp := func(change modes.ModeChange) bool {
		return (change.Op == modes.List) || (change.Arg == "")
	}

	hasPrivs := func(change modes.ModeChange) bool {
		if isSamode {
			return true
		}
		switch change.Mode {
		case modes.ChannelFounder, modes.ChannelAdmin, modes.ChannelOperator, modes.Halfop, modes.Voice:
			// List on these modes is a no-op anyway
			if change.Op == modes.List {
				return true
			}
			cfarg, _ := CasefoldName(change.Arg)
			isSelfChange := cfarg == client.NickCasefolded()
			// Admins can't give other people Admin or remove it from others
			if change.Mode == modes.ChannelAdmin && !isSelfChange {
				return false
			}
			if change.Op == modes.Remove && isSelfChange {
				// "There is no restriction, however, on anyone `deopping' themselves"
				// <https://tools.ietf.org/html/rfc2812#section-3.1.5>
				return true
			}
			return channel.ClientIsAtLeast(client, change.Mode)
		case modes.BanMask:
			// #163: allow unprivileged users to list ban masks
			return isListOp(change) || channel.ClientIsAtLeast(client, modes.ChannelOperator)
		default:
			return channel.ClientIsAtLeast(client, modes.ChannelOperator)
		}
	}

	for _, change := range changes {
		if !hasPrivs(change) {
			if !alreadySentPrivError {
				alreadySentPrivError = true
				rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), channel.name, client.t("You're not a channel operator"))
			}
			continue
		}

		switch change.Mode {
		case modes.BanMask, modes.ExceptMask, modes.InviteMask:
			if isListOp(change) {
				channel.ShowMaskList(client, change.Mode, rb)
				continue
			}

			// confirm mask looks valid
			mask, err := Casefold(change.Arg)
			if err != nil {
				continue
			}

			switch change.Op {
			case modes.Add:
				if channel.lists[change.Mode].Length() >= client.server.Limits().ChanListModes {
					if !listFullWarned[change.Mode] {
						rb.Add(nil, client.server.name, ERR_BANLISTFULL, client.Nick(), channel.Name(), change.Mode.String(), client.t("Channel list is full"))
						listFullWarned[change.Mode] = true
					}
					continue
				}

				channel.lists[change.Mode].Add(mask)
				applied = append(applied, change)

			case modes.Remove:
				channel.lists[change.Mode].Remove(mask)
				applied = append(applied, change)
			}

		case modes.UserLimit:
			switch change.Op {
			case modes.Add:
				val, err := strconv.Atoi(change.Arg)
				if err == nil {
					channel.setUserLimit(val)
					applied = append(applied, change)
				}

			case modes.Remove:
				channel.setUserLimit(0)
				applied = append(applied, change)
			}

		case modes.Key:
			switch change.Op {
			case modes.Add:
				channel.setKey(change.Arg)
				applied = append(applied, change)
			case modes.Remove:
				channel.setKey("")
				applied = append(applied, change)
			}

		case modes.InviteOnly, modes.Moderated, modes.NoOutside, modes.OpOnlyTopic, modes.RegisteredOnly, modes.Secret, modes.ChanRoleplaying:
			if change.Op == modes.List {
				continue
			}

			if channel.flags.SetMode(change.Mode, change.Op == modes.Add) {
				applied = append(applied, change)
			}

		case modes.ChannelFounder, modes.ChannelAdmin, modes.ChannelOperator, modes.Halfop, modes.Voice:
			if change.Op == modes.List {
				continue
			}

			nick := change.Arg
			if nick == "" {
				rb.Add(nil, client.server.name, ERR_NEEDMOREPARAMS, client.Nick(), "MODE", client.t("Not enough parameters"))
				return nil
			}

			change := channel.applyModeToMember(client, change.Mode, change.Op, nick, rb)
			if change != nil {
				applied = append(applied, *change)
			}
		}
	}

	return applied
}

// tests whether l > r, in the channel-user mode ordering (e.g., Halfop > Voice)
func umodeGreaterThan(l modes.Mode, r modes.Mode) bool {
	for _, mode := range modes.ChannelUserModes {
		if l == mode && r != mode {
			return true
		} else if r == mode {
			return false
		}
	}
	return false
}

// ProcessAccountToUmodeChange processes Add/Remove/List operations for channel persistent usermodes.
func (channel *Channel) ProcessAccountToUmodeChange(client *Client, change modes.ModeChange) (results []modes.ModeChange, err error) {
	hasPrivsOver := func(l modes.Mode, r modes.Mode) bool {
		if l == modes.ChannelAdmin {
			return umodeGreaterThan(l, r)
		}
		return l == r || umodeGreaterThan(l, r)
	}

	account := client.Account()
	isOperChange := client.HasRoleCapabs("chanreg")

	channel.stateMutex.Lock()
	defer channel.stateMutex.Unlock()

	clientMode := channel.accountToUMode[account]
	targetModeNow := channel.accountToUMode[change.Arg]
	var targetModeAfter modes.Mode
	if change.Op == modes.Add {
		targetModeAfter = change.Mode
	}

	// operators and founders can do anything
	hasPrivs := isOperChange || (account != "" && account == channel.registeredFounder)
	// halfop and up can list, and do add/removes at levels <= their own
	if change.Op == modes.List && hasPrivsOver(clientMode, modes.Halfop) {
		hasPrivs = true
	} else if hasPrivsOver(clientMode, modes.Halfop) && hasPrivsOver(clientMode, targetModeNow) && hasPrivsOver(clientMode, targetModeAfter) {
		hasPrivs = true
	}
	if !hasPrivs {
		return nil, errInsufficientPrivs
	}

	switch change.Op {
	case modes.Add:
		if targetModeNow != targetModeAfter {
			channel.accountToUMode[change.Arg] = change.Mode
			channel.MarkDirty(IncludeLists)
			return []modes.ModeChange{change}, nil
		}
		return nil, nil
	case modes.Remove:
		if targetModeNow == change.Mode {
			delete(channel.accountToUMode, change.Arg)
			channel.MarkDirty(IncludeLists)
			return []modes.ModeChange{change}, nil
		}
		return nil, nil
	case modes.List:
		result := make([]modes.ModeChange, len(channel.accountToUMode))
		pos := 0
		for account, mode := range channel.accountToUMode {
			result[pos] = modes.ModeChange{
				Mode: mode,
				Arg:  account,
				Op:   modes.Add,
			}
			pos++
		}
		return result, nil
	default:
		// shouldn't happen
		return nil, errInvalidCharacter
	}
}
<commit_msg>fix a deadlock in CS AMODE<commit_after>// Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2014-2015 Edmund Huber
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package irc

import (
	"strconv"
	"strings"

	"github.com/oragono/oragono/irc/modes"
	"github.com/oragono/oragono/irc/sno"
)

var (
	// DefaultChannelModes are enabled on brand new channels when they're created.
	// this can be overridden in the `channels` config, with the `default-modes` key
	DefaultChannelModes = modes.Modes{
		modes.NoOutside, modes.OpOnlyTopic,
	}
)

// ApplyUserModeChanges applies the given changes, and returns the applied changes.
func ApplyUserModeChanges(client *Client, changes modes.ModeChanges, force bool) modes.ModeChanges {
	applied := make(modes.ModeChanges, 0)

	for _, change := range changes {
		switch change.Mode {
		case modes.Bot, modes.Invisible, modes.WallOps, modes.UserRoleplaying, modes.Operator, modes.LocalOperator, modes.RegisteredOnly:
			switch change.Op {
			case modes.Add:
				if !force && (change.Mode == modes.Operator || change.Mode == modes.LocalOperator) {
					continue
				}

				if client.SetMode(change.Mode, true) {
					if change.Mode == modes.Invisible {
						client.server.stats.ChangeInvisible(1)
					} else if change.Mode == modes.Operator || change.Mode == modes.LocalOperator {
						client.server.stats.ChangeOperators(1)
					}
					applied = append(applied, change)
				}

			case modes.Remove:
				if client.SetMode(change.Mode, false) {
					if change.Mode == modes.Invisible {
						client.server.stats.ChangeInvisible(-1)
					} else if change.Mode == modes.Operator || change.Mode == modes.LocalOperator {
						client.server.stats.ChangeOperators(-1)
					}
					applied = append(applied, change)
				}
			}

		case modes.ServerNotice:
			if !client.HasMode(modes.Operator) {
				continue
			}
			var masks []sno.Mask
			if change.Op == modes.Add || change.Op == modes.Remove {
				var newArg string
				for _, char := range change.Arg {
					mask := sno.Mask(char)
					if sno.ValidMasks[mask] {
						masks = append(masks, mask)
						newArg += string(char)
					}
				}
				change.Arg = newArg
			}
			if change.Op == modes.Add {
				client.server.snomasks.AddMasks(client, masks...)
				applied = append(applied, change)
			} else if change.Op == modes.Remove {
				client.server.snomasks.RemoveMasks(client, masks...)
				applied = append(applied, change)
			}
		}

		// can't do anything to TLS mode
	}

	// return the changes we could actually apply
	return applied
}

// ParseDefaultChannelModes parses the `default-modes` line of the config
func ParseDefaultChannelModes(rawModes *string) modes.Modes {
	if rawModes == nil {
		// not present in config, fall back to compile-time default
		return DefaultChannelModes
	}
	modeChangeStrings := strings.Fields(*rawModes)
	modeChanges, _ := modes.ParseChannelModeChanges(modeChangeStrings...)
	defaultChannelModes := make(modes.Modes, 0)
	for _, modeChange := range modeChanges {
		if modeChange.Op == modes.Add {
			defaultChannelModes = append(defaultChannelModes, modeChange.Mode)
		}
	}
	return defaultChannelModes
}

// ApplyChannelModeChanges applies a given set of mode changes.
func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, changes modes.ModeChanges, rb *ResponseBuffer) modes.ModeChanges {
	// so we only output one warning for each list type when full
	listFullWarned := make(map[modes.Mode]bool)

	var alreadySentPrivError bool

	applied := make(modes.ModeChanges, 0)

	isListOp := func(change modes.ModeChange) bool {
		return (change.Op == modes.List) || (change.Arg == "")
	}

	hasPrivs := func(change modes.ModeChange) bool {
		if isSamode {
			return true
		}
		switch change.Mode {
		case modes.ChannelFounder, modes.ChannelAdmin, modes.ChannelOperator, modes.Halfop, modes.Voice:
			// List on these modes is a no-op anyway
			if change.Op == modes.List {
				return true
			}
			cfarg, _ := CasefoldName(change.Arg)
			isSelfChange := cfarg == client.NickCasefolded()
			// Admins can't give other people Admin or remove it from others
			if change.Mode == modes.ChannelAdmin && !isSelfChange {
				return false
			}
			if change.Op == modes.Remove && isSelfChange {
				// "There is no restriction, however, on anyone `deopping' themselves"
				// <https://tools.ietf.org/html/rfc2812#section-3.1.5>
				return true
			}
			return channel.ClientIsAtLeast(client, change.Mode)
		case modes.BanMask:
			// #163: allow unprivileged users to list ban masks
			return isListOp(change) || channel.ClientIsAtLeast(client, modes.ChannelOperator)
		default:
			return channel.ClientIsAtLeast(client, modes.ChannelOperator)
		}
	}

	for _, change := range changes {
		if !hasPrivs(change) {
			if !alreadySentPrivError {
				alreadySentPrivError = true
				rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), channel.name, client.t("You're not a channel operator"))
			}
			continue
		}

		switch change.Mode {
		case modes.BanMask, modes.ExceptMask, modes.InviteMask:
			if isListOp(change) {
				channel.ShowMaskList(client, change.Mode, rb)
				continue
			}

			// confirm mask looks valid
			mask, err := Casefold(change.Arg)
			if err != nil {
				continue
			}

			switch change.Op {
			case modes.Add:
				if channel.lists[change.Mode].Length() >= client.server.Limits().ChanListModes {
					if !listFullWarned[change.Mode] {
						rb.Add(nil, client.server.name, ERR_BANLISTFULL, client.Nick(), channel.Name(), change.Mode.String(), client.t("Channel list is full"))
						listFullWarned[change.Mode] = true
					}
					continue
				}

				channel.lists[change.Mode].Add(mask)
				applied = append(applied, change)

			case modes.Remove:
				channel.lists[change.Mode].Remove(mask)
				applied = append(applied, change)
			}

		case modes.UserLimit:
			switch change.Op {
			case modes.Add:
				val, err := strconv.Atoi(change.Arg)
				if err == nil {
					channel.setUserLimit(val)
					applied = append(applied, change)
				}

			case modes.Remove:
				channel.setUserLimit(0)
				applied = append(applied, change)
			}

		case modes.Key:
			switch change.Op {
			case modes.Add:
				channel.setKey(change.Arg)
				applied = append(applied, change)
			case modes.Remove:
				channel.setKey("")
				applied = append(applied, change)
			}

		case modes.InviteOnly, modes.Moderated, modes.NoOutside, modes.OpOnlyTopic, modes.RegisteredOnly, modes.Secret, modes.ChanRoleplaying:
			if change.Op == modes.List {
				continue
			}

			if channel.flags.SetMode(change.Mode, change.Op == modes.Add) {
				applied = append(applied, change)
			}

		case modes.ChannelFounder, modes.ChannelAdmin, modes.ChannelOperator, modes.Halfop, modes.Voice:
			if change.Op == modes.List {
				continue
			}

			nick := change.Arg
			if nick == "" {
				rb.Add(nil, client.server.name, ERR_NEEDMOREPARAMS, client.Nick(), "MODE", client.t("Not enough parameters"))
				return nil
			}

			change := channel.applyModeToMember(client, change.Mode, change.Op, nick, rb)
			if change != nil {
				applied = append(applied, *change)
			}
		}
	}

	return applied
}

// tests whether l > r, in the channel-user mode ordering (e.g., Halfop > Voice)
func umodeGreaterThan(l modes.Mode, r modes.Mode) bool {
	for _, mode := range modes.ChannelUserModes {
		if l == mode && r != mode {
			return true
		} else if r == mode {
			return false
		}
	}
	return false
}

// ProcessAccountToUmodeChange processes Add/Remove/List operations for channel persistent usermodes.
func (channel *Channel) ProcessAccountToUmodeChange(client *Client, change modes.ModeChange) (results []modes.ModeChange, err error) {
	hasPrivsOver := func(l modes.Mode, r modes.Mode) bool {
		if l == modes.ChannelAdmin {
			return umodeGreaterThan(l, r)
		}
		return l == r || umodeGreaterThan(l, r)
	}

	changed := false
	defer func() {
		if changed {
			channel.MarkDirty(IncludeLists)
		}
	}()

	account := client.Account()
	isOperChange := client.HasRoleCapabs("chanreg")

	channel.stateMutex.Lock()
	defer channel.stateMutex.Unlock()

	clientMode := channel.accountToUMode[account]
	targetModeNow := channel.accountToUMode[change.Arg]
	var targetModeAfter modes.Mode
	if change.Op == modes.Add {
		targetModeAfter = change.Mode
	}

	// operators and founders can do anything
	hasPrivs := isOperChange || (account != "" && account == channel.registeredFounder)
	// halfop and up can list, and do add/removes at levels <= their own
	if change.Op == modes.List && hasPrivsOver(clientMode, modes.Halfop) {
		hasPrivs = true
	} else if hasPrivsOver(clientMode, modes.Halfop) && hasPrivsOver(clientMode, targetModeNow) && hasPrivsOver(clientMode, targetModeAfter) {
		hasPrivs = true
	}
	if !hasPrivs {
		return nil, errInsufficientPrivs
	}

	switch change.Op {
	case modes.Add:
		if targetModeNow != targetModeAfter {
			channel.accountToUMode[change.Arg] = change.Mode
			changed = true
			return []modes.ModeChange{change}, nil
		}
		return nil, nil
	case modes.Remove:
		if targetModeNow == change.Mode {
			delete(channel.accountToUMode, change.Arg)
			changed = true
			return []modes.ModeChange{change}, nil
		}
		return nil, nil
	case modes.List:
		result := make([]modes.ModeChange, len(channel.accountToUMode))
		pos := 0
		for account, mode := range channel.accountToUMode {
			result[pos] = modes.ModeChange{
				Mode: mode,
				Arg:  account,
				Op:   modes.Add,
			}
			pos++
		}
		return result, nil
	default:
		// shouldn't happen
		return nil, errInvalidCharacter
	}
}
<|endoftext|>


<commit_before>package portmapper

import (
	"errors"
	"fmt"
	"net"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/docker/libnetwork/iptables"
	"github.com/docker/libnetwork/portallocator"
)

type mapping struct {
	proto         string
	userlandProxy userlandProxy
	host          net.Addr
	container     net.Addr
}

var newProxy = newProxyCommand

var (
	// ErrUnknownBackendAddressType refers to an unknown container or unsupported address type
	ErrUnknownBackendAddressType = errors.New("unknown container address type not supported")
	// ErrPortMappedForIP refers to a port already mapped to an ip address
	ErrPortMappedForIP = errors.New("port is already mapped to ip")
	// ErrPortNotMapped refers to an unmapped port
	ErrPortNotMapped = errors.New("port is not mapped")
)

// PortMapper manages the network address translation
type PortMapper struct {
	chain      *iptables.ChainInfo
	bridgeName string

	// udp:ip:port
	currentMappings map[string]*mapping
	lock            sync.Mutex

	Allocator *portallocator.PortAllocator
}

// New returns a new instance of PortMapper
func New() *PortMapper {
	return NewWithPortAllocator(portallocator.Get())
}

// NewWithPortAllocator returns a new instance of PortMapper which will use the specified PortAllocator
func NewWithPortAllocator(allocator *portallocator.PortAllocator) *PortMapper {
	return &PortMapper{
		currentMappings: make(map[string]*mapping),
		Allocator:       allocator,
	}
}

// SetIptablesChain sets the specified chain into portmapper
func (pm *PortMapper) SetIptablesChain(c *iptables.ChainInfo, bridgeName string) {
	pm.chain = c
	pm.bridgeName = bridgeName
}

// Map maps the specified container transport address to the host's network address and transport port
func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, useProxy bool) (host net.Addr, err error) {
	return pm.MapRange(container, hostIP, hostPort, hostPort, useProxy)
}

// MapRange maps the specified container transport address to the host's network address and transport port range
func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart, hostPortEnd int, useProxy bool) (host net.Addr, err error) {
	pm.lock.Lock()
	defer pm.lock.Unlock()

	var (
		m                 *mapping
		proto             string
		allocatedHostPort int
	)

	switch container.(type) {
	case *net.TCPAddr:
		proto = "tcp"
		if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil {
			return nil, err
		}

		m = &mapping{
			proto:     proto,
			host:      &net.TCPAddr{IP: hostIP, Port: allocatedHostPort},
			container: container,
		}

		if useProxy {
			m.userlandProxy = newProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port)
		} else {
			m.userlandProxy = newDummyProxy(proto, hostIP, allocatedHostPort)
		}
	case *net.UDPAddr:
		proto = "udp"
		if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil {
			return nil, err
		}

		m = &mapping{
			proto:     proto,
			host:      &net.UDPAddr{IP: hostIP, Port: allocatedHostPort},
			container: container,
		}

		if useProxy {
			m.userlandProxy = newProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port)
		} else {
			m.userlandProxy = newDummyProxy(proto, hostIP, allocatedHostPort)
		}
	default:
		return nil, ErrUnknownBackendAddressType
	}

	// release the allocated port on any further error during return.
	defer func() {
		if err != nil {
			pm.Allocator.ReleasePort(hostIP, proto, allocatedHostPort)
		}
	}()

	key := getKey(m.host)
	if _, exists := pm.currentMappings[key]; exists {
		return nil, ErrPortMappedForIP
	}

	containerIP, containerPort := getIPAndPort(m.container)
	if err := pm.forward(iptables.Append, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
		return nil, err
	}

	cleanup := func() error {
		// need to undo the iptables rules before we return
		m.userlandProxy.Stop()
		pm.forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
		if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
			return err
		}

		return nil
	}

	if err := m.userlandProxy.Start(); err != nil {
		if err := cleanup(); err != nil {
			return nil, fmt.Errorf("Error during port allocation cleanup: %v", err)
		}
		return nil, err
	}

	pm.currentMappings[key] = m
	return m.host, nil
}

// Unmap removes stored mapping for the specified host transport address
func (pm *PortMapper) Unmap(host net.Addr) error {
	pm.lock.Lock()
	defer pm.lock.Unlock()

	key := getKey(host)
	data, exists := pm.currentMappings[key]
	if !exists {
		return ErrPortNotMapped
	}

	if data.userlandProxy != nil {
		data.userlandProxy.Stop()
	}

	delete(pm.currentMappings, key)

	containerIP, containerPort := getIPAndPort(data.container)
	hostIP, hostPort := getIPAndPort(data.host)
	if err := pm.forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
		logrus.Errorf("Error on iptables delete: %s", err)
	}

	switch a := host.(type) {
	case *net.TCPAddr:
		return pm.Allocator.ReleasePort(a.IP, "tcp", a.Port)
	case *net.UDPAddr:
		return pm.Allocator.ReleasePort(a.IP, "udp", a.Port)
	}
	return nil
}

//ReMapAll will re-apply all port mappings
func (pm *PortMapper) ReMapAll() {
	logrus.Debugln("Re-applying all port mappings.")
	for _, data := range pm.currentMappings {
		containerIP, containerPort := getIPAndPort(data.container)
		hostIP, hostPort := getIPAndPort(data.host)
		if err := pm.forward(iptables.Append, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
			logrus.Errorf("Error on iptables add: %s", err)
		}
	}
}

func getKey(a net.Addr) string {
	switch t := a.(type) {
	case *net.TCPAddr:
		return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp")
	case *net.UDPAddr:
		return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp")
	}
	return ""
}

func getIPAndPort(a net.Addr) (net.IP, int) {
	switch t := a.(type) {
	case *net.TCPAddr:
		return t.IP, t.Port
	case *net.UDPAddr:
		return t.IP, t.Port
	}
	return nil, 0
}

func (pm *PortMapper) forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error {
	if pm.chain == nil {
		return nil
	}
	return pm.chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort, pm.bridgeName)
}
<commit_msg>lock port mapper when reapply iptables rules<commit_after>package portmapper

import (
	"errors"
	"fmt"
	"net"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/docker/libnetwork/iptables"
	"github.com/docker/libnetwork/portallocator"
)

type mapping struct {
	proto         string
	userlandProxy userlandProxy
	host          net.Addr
	container     net.Addr
}

var newProxy = newProxyCommand

var (
	// ErrUnknownBackendAddressType refers to an unknown container or unsupported address type
	ErrUnknownBackendAddressType = errors.New("unknown container address type not supported")
	// ErrPortMappedForIP refers to a port already mapped to an ip address
	ErrPortMappedForIP = errors.New("port is already mapped to ip")
	// ErrPortNotMapped refers to an unmapped port
	ErrPortNotMapped = errors.New("port is not mapped")
)

// PortMapper manages the network address translation
type PortMapper struct {
	chain      *iptables.ChainInfo
	bridgeName string

	// udp:ip:port
	currentMappings map[string]*mapping
	lock            sync.Mutex

	Allocator *portallocator.PortAllocator
}

// New returns a new instance of PortMapper
func New() *PortMapper {
	return NewWithPortAllocator(portallocator.Get())
}

// NewWithPortAllocator returns a new instance of PortMapper which will use the specified PortAllocator
func NewWithPortAllocator(allocator *portallocator.PortAllocator) *PortMapper {
	return &PortMapper{
		currentMappings: make(map[string]*mapping),
		Allocator:       allocator,
	}
}

// SetIptablesChain sets the specified chain into portmapper
func (pm *PortMapper) SetIptablesChain(c *iptables.ChainInfo, bridgeName string) {
	pm.chain = c
	pm.bridgeName = bridgeName
}

// Map maps the specified container transport address to the host's network address and transport port
func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, useProxy bool) (host net.Addr, err error) {
	return pm.MapRange(container, hostIP, hostPort, hostPort, useProxy)
}

// MapRange maps the specified container transport address to the host's network address and transport port range
func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart, hostPortEnd int, useProxy bool) (host net.Addr, err error) {
	pm.lock.Lock()
	defer pm.lock.Unlock()

	var (
		m                 *mapping
		proto             string
		allocatedHostPort int
	)

	switch container.(type) {
	case *net.TCPAddr:
		proto = "tcp"
		if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil {
			return nil, err
		}

		m = &mapping{
			proto:     proto,
			host:      &net.TCPAddr{IP: hostIP, Port: allocatedHostPort},
			container: container,
		}

		if useProxy {
			m.userlandProxy = newProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port)
		} else {
			m.userlandProxy = newDummyProxy(proto, hostIP, allocatedHostPort)
		}
	case *net.UDPAddr:
		proto = "udp"
		if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil {
			return nil, err
		}

		m = &mapping{
			proto:     proto,
			host:      &net.UDPAddr{IP: hostIP, Port: allocatedHostPort},
			container: container,
		}

		if useProxy {
			m.userlandProxy = newProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port)
		} else {
			m.userlandProxy = newDummyProxy(proto, hostIP, allocatedHostPort)
		}
	default:
		return nil, ErrUnknownBackendAddressType
	}

	// release the allocated port on any further error during return.
	defer func() {
		if err != nil {
			pm.Allocator.ReleasePort(hostIP, proto, allocatedHostPort)
		}
	}()

	key := getKey(m.host)
	if _, exists := pm.currentMappings[key]; exists {
		return nil, ErrPortMappedForIP
	}

	containerIP, containerPort := getIPAndPort(m.container)
	if err := pm.forward(iptables.Append, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil {
		return nil, err
	}

	cleanup := func() error {
		// need to undo the iptables rules before we return
		m.userlandProxy.Stop()
		pm.forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
		if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
			return err
		}

		return nil
	}

	if err := m.userlandProxy.Start(); err != nil {
		if err := cleanup(); err != nil {
			return nil, fmt.Errorf("Error during port allocation cleanup: %v", err)
		}
		return nil, err
	}

	pm.currentMappings[key] = m
	return m.host, nil
}

// Unmap removes stored mapping for the specified host transport address
func (pm *PortMapper) Unmap(host net.Addr) error {
	pm.lock.Lock()
	defer pm.lock.Unlock()

	key := getKey(host)
	data, exists := pm.currentMappings[key]
	if !exists {
		return ErrPortNotMapped
	}

	if data.userlandProxy != nil {
		data.userlandProxy.Stop()
	}

	delete(pm.currentMappings, key)

	containerIP, containerPort := getIPAndPort(data.container)
	hostIP, hostPort := getIPAndPort(data.host)
	if err := pm.forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
		logrus.Errorf("Error on iptables delete: %s", err)
	}

	switch a := host.(type) {
	case *net.TCPAddr:
		return pm.Allocator.ReleasePort(a.IP, "tcp", a.Port)
	case *net.UDPAddr:
		return pm.Allocator.ReleasePort(a.IP, "udp", a.Port)
	}
	return nil
}

//ReMapAll will re-apply all port mappings
func (pm *PortMapper) ReMapAll() {
	pm.lock.Lock()
	defer pm.lock.Unlock()
	logrus.Debugln("Re-applying all port mappings.")
	for _, data := range pm.currentMappings {
		containerIP, containerPort := getIPAndPort(data.container)
		hostIP, hostPort := getIPAndPort(data.host)
		if err := pm.forward(iptables.Append, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
			logrus.Errorf("Error on iptables add: %s", err)
		}
	}
}

func getKey(a net.Addr) string {
	switch t := a.(type) {
	case *net.TCPAddr:
		return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp")
	case *net.UDPAddr:
		return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp")
	}
	return ""
}

func getIPAndPort(a net.Addr) (net.IP, int) {
	switch t := a.(type) {
	case *net.TCPAddr:
		return t.IP, t.Port
	case *net.UDPAddr:
		return t.IP, t.Port
	}
	return nil, 0
}

func (pm *PortMapper) forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error {
	if pm.chain == nil {
		return nil
	}
	return pm.chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort, pm.bridgeName)
}
<|endoftext|>


<commit_before>// Package postmark provides a convenient wrapper for the Postmark API
package postmark

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"net/mail"
	"net/url"
	"strings"
)

const (
	DefaultHost = "api.postmarkapp.com"
)

type Message struct {
	From          *mail.Address
	To            []*mail.Address
	Cc            []*mail.Address
	Bcc           []*mail.Address
	Subject       string
	Tag           string
	HtmlBody      io.Reader
	TextBody      io.Reader
	TemplateId    int
	TemplateModel map[string]interface{}
	ReplyTo       *mail.Address
	Headers       mail.Header
	Attachments   []Attachment
}

func (m *Message) MarshalJSON() ([]byte, error) {
	doc := &struct {
		From          string
		To            string
		Cc            string
		Bcc           string
		Subject       string `json:",omitempty"`
		Tag           string
		HtmlBody      string `json:",omitempty"`
		TextBody      string `json:",omitempty"`
		TemplateId    int    `json:",omitempty"`
		TemplateModel map[string]interface{}
		ReplyTo       string
		Headers       []map[string]string
		Attachments   []Attachment `json:"omitempty"`
	}{}

	doc.From = m.From.String()
	to := []string{}
	for _, addr := range m.To {
		to = append(to, addr.String())
	}
	doc.To = strings.Join(to, ", ")
	cc := []string{}
	for _, addr := range m.Cc {
		cc = append(cc, addr.String())
	}
	doc.Cc = strings.Join(cc, ", ")
	bcc := []string{}
	for _, addr := range m.Bcc {
		bcc = append(bcc, addr.String())
	}
	doc.Bcc = strings.Join(bcc, ", ")
	doc.Subject = m.Subject
	doc.Tag = m.Tag
	if m.HtmlBody != nil {
		htmlBody, err := ioutil.ReadAll(m.HtmlBody)
		if err != nil {
			return nil, err
		}
		doc.HtmlBody = string(htmlBody)
	}
	if m.TextBody != nil {
		textBody, err := ioutil.ReadAll(m.TextBody)
		if err != nil {
			return nil, err
		}
		doc.TextBody = string(textBody)
	}
	doc.TemplateId = m.TemplateId
	doc.TemplateModel = m.TemplateModel
	if m.ReplyTo != nil {
		doc.ReplyTo = m.ReplyTo.String()
	}
	headers := []map[string]string{}
	for k, vs := range m.Headers {
		for _, v := range vs {
			headers = append(headers, map[string]string{
				"Name":  k,
				"Value": v,
			})
		}
	}
	doc.Headers = headers
	doc.Attachments = m.Attachments

	return json.Marshal(doc)
}

type Attachment struct {
	Name        string
	Content     io.Reader
	ContentType string
}

func (a *Attachment) MarshalJSON() ([]byte, error) {
	doc := &struct {
		Name        string
		Content     string
		ContentType string
	}{}

	doc.Name = a.Name
	content, err := ioutil.ReadAll(a.Content)
	if err != nil {
		return nil, err
	}
	doc.Content = base64.StdEncoding.EncodeToString(content)
	doc.ContentType = a.ContentType

	return json.Marshal(doc)
}

type Client struct {
	ApiKey string
	Secure bool

	Host string // Host for the API endpoints, DefaultHost if ""
}

func (c *Client) endpoint(path string) *url.URL {
	url := &url.URL{}
	if c.Secure {
		url.Scheme = "https"
	} else {
		url.Scheme = "http"
	}

	if c.Host == "" {
		url.Host = DefaultHost
	} else {
		url.Host = c.Host
	}

	url.Path = path

	return url
}

// Send sends a single message
func (c *Client) Send(msg *Message) (*Result, error) {
	buf := bytes.Buffer{}
	err := json.NewEncoder(&buf).Encode(msg)
	if err != nil {
		return nil, err
	}

	url := c.endpoint("email")
	if msg.TemplateId != 0 {
		url = c.endpoint("email/withTemplate")
	}

	req, err := http.NewRequest("POST", url.String(), &buf)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Postmark-Server-Token", c.ApiKey)

	resp, err := (&http.Client{}).Do(req)
	if err != nil {
		return nil, err
	}

	res := &Result{}
	json.NewDecoder(resp.Body).Decode(res)
	return res, nil
}

// SendBatch sends multiple messages using the batch API
func (c *Client) SendBatch(msg []*Message) ([]*Result, error) {
	buf := bytes.Buffer{}
	err := json.NewEncoder(&buf).Encode(msg)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", c.endpoint("email/batch").String(), &buf)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Postmark-Server-Token", c.ApiKey)

	resp, err := (&http.Client{}).Do(req)
	if err != nil {
		return nil, err
	}

	res := []*Result{}
	json.NewDecoder(resp.Body).Decode(res)
	return res, nil
}

type Result struct {
	ErrorCode   int
	Message     string
	MessageID   string
	SubmittedAt string
	To          string
}
<commit_msg>typo, which prevents sending attachments<commit_after>// Package postmark provides a convenient wrapper for the Postmark API
package postmark

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"net/mail"
	"net/url"
	"strings"
)

const (
	DefaultHost = "api.postmarkapp.com"
)

type Message struct {
	From          *mail.Address
	To            []*mail.Address
	Cc            []*mail.Address
	Bcc           []*mail.Address
	Subject       string
	Tag           string
	HtmlBody      io.Reader
	TextBody      io.Reader
	TemplateId    int
	TemplateModel map[string]interface{}
	ReplyTo       *mail.Address
	Headers       mail.Header
	Attachments   []Attachment
}

func (m *Message) MarshalJSON() ([]byte, error) {
	doc := &struct {
		From          string
		To            string
		Cc            string
		Bcc           string
		Subject       string `json:",omitempty"`
		Tag           string
		HtmlBody      string `json:",omitempty"`
		TextBody      string `json:",omitempty"`
		TemplateId    int    `json:",omitempty"`
		TemplateModel map[string]interface{}
		ReplyTo       string
		Headers       []map[string]string
		Attachments   []Attachment `json:",omitempty"`
	}{}

	doc.From = m.From.String()
	to := []string{}
	for _, addr := range m.To {
		to = append(to, addr.String())
	}
	doc.To = strings.Join(to, ", ")
	cc := []string{}
	for _, addr := range m.Cc {
		cc = append(cc, addr.String())
	}
	doc.Cc = strings.Join(cc, ", ")
	bcc := []string{}
	for _, addr := range m.Bcc {
		bcc = append(bcc, addr.String())
	}
	doc.Bcc = strings.Join(bcc, ", ")
	doc.Subject = m.Subject
	doc.Tag = m.Tag
	if m.HtmlBody != nil {
		htmlBody, err := ioutil.ReadAll(m.HtmlBody)
		if err != nil {
			return nil, err
		}
		doc.HtmlBody = string(htmlBody)
	}
	if m.TextBody != nil {
		textBody, err := ioutil.ReadAll(m.TextBody)
		if err != nil {
			return nil, err
		}
		doc.TextBody = string(textBody)
	}
	doc.TemplateId = m.TemplateId
	doc.TemplateModel = m.TemplateModel
	if m.ReplyTo != nil {
		doc.ReplyTo = m.ReplyTo.String()
	}
	headers := []map[string]string{}
	for k, vs := range m.Headers {
		for _, v := range vs {
			headers = append(headers, map[string]string{
				"Name":  k,
				"Value": v,
			})
		}
	}
	doc.Headers = headers
	doc.Attachments = m.Attachments

	return json.Marshal(doc)
}

type Attachment struct {
	Name        string
	Content     io.Reader
	ContentType string
}

func (a *Attachment) MarshalJSON() ([]byte, error) {
	doc := &struct {
		Name        string
		Content     string
		ContentType string
	}{}

	doc.Name = a.Name
	content, err := ioutil.ReadAll(a.Content)
	if err != nil {
		return nil, err
	}
	doc.Content = base64.StdEncoding.EncodeToString(content)
	doc.ContentType = a.ContentType

	return json.Marshal(doc)
}

type Client struct {
	ApiKey string
	Secure bool

	Host string // Host for the API endpoints, DefaultHost if ""
}

func (c *Client) endpoint(path string) *url.URL {
	url := &url.URL{}
	if c.Secure {
		url.Scheme = "https"
	} else {
		url.Scheme = "http"
	}

	if c.Host == "" {
		url.Host = DefaultHost
	} else {
		url.Host = c.Host
	}

	url.Path = path

	return url
}

// Send sends a single message
func (c *Client) Send(msg *Message) (*Result, error) {
	buf := bytes.Buffer{}
	err := json.NewEncoder(&buf).Encode(msg)
	if err != nil {
		return nil, err
	}

	url := c.endpoint("email")
	if msg.TemplateId != 0 {
		url = c.endpoint("email/withTemplate")
	}

	req, err := http.NewRequest("POST", url.String(), &buf)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Postmark-Server-Token", c.ApiKey)

	resp, err := (&http.Client{}).Do(req)
	if err != nil {
		return nil, err
	}

	res := &Result{}
	json.NewDecoder(resp.Body).Decode(res)
	return res, nil
}

// SendBatch sends multiple messages using the batch API
func (c *Client) SendBatch(msg []*Message) ([]*Result, error) {
	buf := bytes.Buffer{}
	err := json.NewEncoder(&buf).Encode(msg)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", c.endpoint("email/batch").String(), &buf)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Postmark-Server-Token", c.ApiKey)

	resp, err := (&http.Client{}).Do(req)
	if err != nil {
		return nil, err
	}

	res := []*Result{}
	json.NewDecoder(resp.Body).Decode(res)
	return res, nil
}

type Result struct {
	ErrorCode   int
	Message     string
	MessageID   string
	SubmittedAt string
	To          string
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"log"
	"math"
)

func binaryConverter(decimal int) (binary []byte, err error) {
	if decimal < 0 {
		return nil, errors.New("binaryConverter: Can't convert negative numbers")
	}

	power := 0

	// First we find out how many "Powers of 2" we need
	// to fit in the passed decimal
	for {
		if math.Pow(2, float64(power)) > float64(decimal) {
			break
		}
		power++
	}

	result := make([]byte, power)

	// Restarting the meat of the for, I think I'll only grow more confused otherwise
	// When the soil is salted, move.

	return result, nil
}

func main() {
	test, err := binaryConverter(10)

	if err != nil {
		log.Fatal(err)
	}

	// Lets convert 777 to binary!
	fmt.Println(test)
}
<commit_msg>I'll restart this later, my brain is mush<commit_after>package main

import (
	"errors"
	"fmt"
	"log"
	"math"
)

// I'll come back to this later, probably
func binaryConverter(decimal int) (binary []byte, err error) {
	if decimal < 0 {
		return nil, errors.New("binaryConverter: Can't convert negative numbers")
	}

	return result, nil
}

func main() {
	test, err := binaryConverter(16)

	if err != nil {
		log.Fatal(err)
	}

	// Lets convert 777 to binary!
	fmt.Println(test)
}
<|endoftext|>


<commit_before>package santander

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

	"net/http"

	. "github.com/PMoneda/flow"
	"github.com/mundipagg/boleto-api/config"
	"github.com/mundipagg/boleto-api/log"
	"github.com/mundipagg/boleto-api/metrics"
	"github.com/mundipagg/boleto-api/models"
	"github.com/mundipagg/boleto-api/tmpl"
	"github.com/mundipagg/boleto-api/util"
	"github.com/mundipagg/boleto-api/validations"
)

type bankSantander struct {
	validate  *models.Validator
	log       *log.Log
	transport *http.Transport
}

//New Create a new Santander Integration Instance
func New() bankSantander {
	b := bankSantander{
		validate: models.NewValidator(),
		log:      log.CreateLog(),
	}
	b.validate.Push(validations.ValidateAmount)
	b.validate.Push(validations.ValidateExpireDate)
	b.validate.Push(validations.ValidateBuyerDocumentNumber)
	b.validate.Push(validations.ValidateRecipientDocumentNumber)
	b.validate.Push(santanderValidateAgreementNumber)
	b.validate.Push(satanderBoletoTypeValidate)

	t, err := util.BuildTLSTransport(config.Get().CertBoletoPathCrt, config.Get().CertBoletoPathKey, config.Get().CertBoletoPathCa)
	if err != nil {
		fmt.Println(err.Error())
	}
	b.transport = t

	return b
}

//Log retorna a referencia do log
func (b bankSantander) Log() *log.Log {
	return b.log
}
func (b bankSantander) GetTicket(boleto *models.BoletoRequest) (string, error) {
	boleto.Title.OurNumber = calculateOurNumber(boleto)
	boleto.Title.BoletoType = b.GetBoletoType(boleto)
	pipe := NewFlow()
	url := config.Get().URLTicketSantander
	tlsURL := strings.Replace(config.Get().URLTicketSantander, "https", "tls", 1)
	pipe.From("message://?source=inline", boleto, getRequestTicket(), tmpl.GetFuncMaps())
	pipe.To("logseq://?type=request&url="+url, b.log)
	duration := util.Duration(func() {
		pipe.To(tlsURL, b.transport, map[string]string{"timeout": config.Get().TimeoutToken})
	})
	metrics.PushTimingMetric("santander-get-ticket-boleto-time", duration.Seconds())
	pipe.To("logseq://?type=response&url="+url, b.log)
	ch := pipe.Choice()
	ch.When(Header("status").IsEqualTo("200"))
	ch.To("transform://?format=xml", getTicketResponse(), `{{.returnCode}}:::{{.ticket}}`, tmpl.GetFuncMaps())
	ch.When(Header("status").IsEqualTo("403"))
	ch.To("set://?prop=body", errors.New("403 Forbidden"))
	ch.Otherwise()
	ch.To("logseq://?type=request&url="+url, b.log).To("set://?prop=body", errors.New("integration error"))
	switch t := pipe.GetBody().(type) {
	case string:
		items := pipe.GetBody().(string)
		parts := strings.Split(items, ":::")
		returnCode, ticket := parts[0], parts[1]
		return ticket, checkError(returnCode)
	case error:
		return "", t
	}
	return "", nil
}

func (b bankSantander) RegisterBoleto(input *models.BoletoRequest) (models.BoletoResponse, error) {
	serviceURL := config.Get().URLRegisterBoletoSantander
	fromResponse := getResponseSantander()
	toAPI := getAPIResponseSantander()
	inputTemplate := getRequestSantander()
	santanderURL := strings.Replace(serviceURL, "https", "tls", 1)

	exec := NewFlow().From("message://?source=inline", input, inputTemplate, tmpl.GetFuncMaps())
	exec.To("logseq://?type=request&url="+serviceURL, b.log)
	duration := util.Duration(func() {
		exec.To(santanderURL, b.transport, map[string]string{"method": "POST", "insecureSkipVerify": "true", "timeout": config.Get().TimeoutRegister})
	})
	metrics.PushTimingMetric("santander-register-boleto-time", duration.Seconds())
	exec.To("logseq://?type=response&url="+serviceURL, b.log)
	ch := exec.Choice()
	ch.When(Header("status").IsEqualTo("200"))
	ch.To("transform://?format=xml", fromResponse, toAPI, tmpl.GetFuncMaps())
	ch.To("unmarshall://?format=json", new(models.BoletoResponse))
	ch.Otherwise()
	ch.To("logseq://?type=response&url="+serviceURL, b.log).To("apierro://")
	switch t := exec.GetBody().(type) {
	case *models.BoletoResponse:
		return *t, nil
	case error:
		return models.BoletoResponse{}, t
	}
	return models.BoletoResponse{}, models.NewInternalServerError("MP500", "Internal error")
}
func (b bankSantander) ProcessBoleto(boleto *models.BoletoRequest) (models.BoletoResponse, error) {
	errs := b.ValidateBoleto(boleto)
	if len(errs) > 0 {
		return models.BoletoResponse{Errors: errs}, nil
	}
	if ticket, err := b.GetTicket(boleto); err != nil {
		return models.BoletoResponse{Errors: errs}, err
	} else {
		boleto.Authentication.AuthorizationToken = ticket
	}
	return b.RegisterBoleto(boleto)
}

func (b bankSantander) ValidateBoleto(boleto *models.BoletoRequest) models.Errors {
	return models.Errors(b.validate.Assert(boleto))
}

//GetBankNumber retorna o codigo do banco
func (b bankSantander) GetBankNumber() models.BankNumber {
	return models.Santander
}

func calculateOurNumber(boleto *models.BoletoRequest) uint {
	ourNumberWithDigit := strconv.Itoa(int(boleto.Title.OurNumber)) + util.OurNumberDv(strconv.Itoa(int(boleto.Title.OurNumber)), util.MOD11)
	value, _ := strconv.Atoi(ourNumberWithDigit)
	return uint(value)
}

func (b bankSantander) GetBankNameIntegration() string {
	return "Santander"
}

func satanderBoletoTypes() map[string]string {
	m := make(map[string]string)

	m["DM"] = "02"  //Duplicata Mercantil
	m["DS"] = "04"  //Duplicata de serviço
	m["NP"] = "12"  //Nota promissória
	m["RC"] = "17"  //Recibo
	m["BP"] = "32"  //Boleto de proposta
	m["CH"] = "97"  //Cheque
	m["OUT"] = "99" //Outros

	return m
}

func (b bankSantander) GetBoletoType(boleto *models.BoletoRequest) string {
	if len(boleto.Title.BoletoType) < 1 {
		return "02"
	}
	bt := satanderBoletoTypes()

	if bt[strings.ToUpper(boleto.Title.BoletoType)] == "" {
		return "02"
	} else {
		return bt[strings.ToUpper(boleto.Title.BoletoType)]
	}
}
<commit_msg>:art: Corrige nome do metodo santanderBoletoTypes<commit_after>package santander

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

	"net/http"

	. "github.com/PMoneda/flow"
	"github.com/mundipagg/boleto-api/config"
	"github.com/mundipagg/boleto-api/log"
	"github.com/mundipagg/boleto-api/metrics"
	"github.com/mundipagg/boleto-api/models"
	"github.com/mundipagg/boleto-api/tmpl"
	"github.com/mundipagg/boleto-api/util"
	"github.com/mundipagg/boleto-api/validations"
)

type bankSantander struct {
	validate  *models.Validator
	log       *log.Log
	transport *http.Transport
}

//New Create a new Santander Integration Instance
func New() bankSantander {
	b := bankSantander{
		validate: models.NewValidator(),
		log:      log.CreateLog(),
	}
	b.validate.Push(validations.ValidateAmount)
	b.validate.Push(validations.ValidateExpireDate)
	b.validate.Push(validations.ValidateBuyerDocumentNumber)
	b.validate.Push(validations.ValidateRecipientDocumentNumber)
	b.validate.Push(santanderValidateAgreementNumber)
	b.validate.Push(satanderBoletoTypeValidate)

	t, err := util.BuildTLSTransport(config.Get().CertBoletoPathCrt, config.Get().CertBoletoPathKey, config.Get().CertBoletoPathCa)
	if err != nil {
		fmt.Println(err.Error())
	}
	b.transport = t

	return b
}

//Log retorna a referencia do log
func (b bankSantander) Log() *log.Log {
	return b.log
}
func (b bankSantander) GetTicket(boleto *models.BoletoRequest) (string, error) {
	boleto.Title.OurNumber = calculateOurNumber(boleto)
	boleto.Title.BoletoType = b.GetBoletoType(boleto)
	pipe := NewFlow()
	url := config.Get().URLTicketSantander
	tlsURL := strings.Replace(config.Get().URLTicketSantander, "https", "tls", 1)
	pipe.From("message://?source=inline", boleto, getRequestTicket(), tmpl.GetFuncMaps())
	pipe.To("logseq://?type=request&url="+url, b.log)
	duration := util.Duration(func() {
		pipe.To(tlsURL, b.transport, map[string]string{"timeout": config.Get().TimeoutToken})
	})
	metrics.PushTimingMetric("santander-get-ticket-boleto-time", duration.Seconds())
	pipe.To("logseq://?type=response&url="+url, b.log)
	ch := pipe.Choice()
	ch.When(Header("status").IsEqualTo("200"))
	ch.To("transform://?format=xml", getTicketResponse(), `{{.returnCode}}:::{{.ticket}}`, tmpl.GetFuncMaps())
	ch.When(Header("status").IsEqualTo("403"))
	ch.To("set://?prop=body", errors.New("403 Forbidden"))
	ch.Otherwise()
	ch.To("logseq://?type=request&url="+url, b.log).To("set://?prop=body", errors.New("integration error"))
	switch t := pipe.GetBody().(type) {
	case string:
		items := pipe.GetBody().(string)
		parts := strings.Split(items, ":::")
		returnCode, ticket := parts[0], parts[1]
		return ticket, checkError(returnCode)
	case error:
		return "", t
	}
	return "", nil
}

func (b bankSantander) RegisterBoleto(input *models.BoletoRequest) (models.BoletoResponse, error) {
	serviceURL := config.Get().URLRegisterBoletoSantander
	fromResponse := getResponseSantander()
	toAPI := getAPIResponseSantander()
	inputTemplate := getRequestSantander()
	santanderURL := strings.Replace(serviceURL, "https", "tls", 1)

	exec := NewFlow().From("message://?source=inline", input, inputTemplate, tmpl.GetFuncMaps())
	exec.To("logseq://?type=request&url="+serviceURL, b.log)
	duration := util.Duration(func() {
		exec.To(santanderURL, b.transport, map[string]string{"method": "POST", "insecureSkipVerify": "true", "timeout": config.Get().TimeoutRegister})
	})
	metrics.PushTimingMetric("santander-register-boleto-time", duration.Seconds())
	exec.To("logseq://?type=response&url="+serviceURL, b.log)
	ch := exec.Choice()
	ch.When(Header("status").IsEqualTo("200"))
	ch.To("transform://?format=xml", fromResponse, toAPI, tmpl.GetFuncMaps())
	ch.To("unmarshall://?format=json", new(models.BoletoResponse))
	ch.Otherwise()
	ch.To("logseq://?type=response&url="+serviceURL, b.log).To("apierro://")
	switch t := exec.GetBody().(type) {
	case *models.BoletoResponse:
		return *t, nil
	case error:
		return models.BoletoResponse{}, t
	}
	return models.BoletoResponse{}, models.NewInternalServerError("MP500", "Internal error")
}
func (b bankSantander) ProcessBoleto(boleto *models.BoletoRequest) (models.BoletoResponse, error) {
	errs := b.ValidateBoleto(boleto)
	if len(errs) > 0 {
		return models.BoletoResponse{Errors: errs}, nil
	}
	if ticket, err := b.GetTicket(boleto); err != nil {
		return models.BoletoResponse{Errors: errs}, err
	} else {
		boleto.Authentication.AuthorizationToken = ticket
	}
	return b.RegisterBoleto(boleto)
}

func (b bankSantander) ValidateBoleto(boleto *models.BoletoRequest) models.Errors {
	return models.Errors(b.validate.Assert(boleto))
}

//GetBankNumber retorna o codigo do banco
func (b bankSantander) GetBankNumber() models.BankNumber {
	return models.Santander
}

func calculateOurNumber(boleto *models.BoletoRequest) uint {
	ourNumberWithDigit := strconv.Itoa(int(boleto.Title.OurNumber)) + util.OurNumberDv(strconv.Itoa(int(boleto.Title.OurNumber)), util.MOD11)
	value, _ := strconv.Atoi(ourNumberWithDigit)
	return uint(value)
}

func (b bankSantander) GetBankNameIntegration() string {
	return "Santander"
}

func santanderBoletoTypes() map[string]string {
	m := make(map[string]string)

	m["DM"] = "02"  //Duplicata Mercantil
	m["DS"] = "04"  //Duplicata de serviço
	m["NP"] = "12"  //Nota promissória
	m["RC"] = "17"  //Recibo
	m["BP"] = "32"  //Boleto de proposta
	m["CH"] = "97"  //Cheque
	m["OUT"] = "99" //Outros

	return m
}

func (b bankSantander) GetBoletoType(boleto *models.BoletoRequest) string {
	if len(boleto.Title.BoletoType) < 1 {
		return "02"
	}
	bt := santanderBoletoTypes()

	if bt[strings.ToUpper(boleto.Title.BoletoType)] == "" {
		return "02"
	} else {
		return bt[strings.ToUpper(boleto.Title.BoletoType)]
	}
}
<|endoftext|>


<commit_before>package sqlite_test

import (
	"fmt"
	. "github.com/gwenn/gosqlite"
	"testing"
)

func init() {
	//ConfigLog(log, "LOG")
}

func trace(d interface{}, sql string) {
	//fmt.Printf("%s: %s\n", d, sql)
}

func authorizer(d interface{}, action Action, arg1, arg2, dbName, triggerName string) Auth {
	//fmt.Printf("%s: %d, %s, %s, %s, %s\n", d, action, arg1, arg2, dbName, triggerName)
	return AUTH_OK
}

func profile(d interface{}, sql string, nanoseconds uint64) {
	//fmt.Printf("%s: %s = %d µs\n", d, sql, nanoseconds/1e3)
}

func progressHandler(d interface{}) bool {
	//fmt.Print("+")
	return false
}

func commitHook(d interface{}) bool {
	fmt.Printf("%s\n", d)
	return false
}

func rollbackHook(d interface{}) {
	fmt.Printf("%s\n", d)
}

func updateHook(d interface{}, a Action, dbName, tableName string, rowId int64) {
	fmt.Printf("%s: %d, %s.%s.%d\n", d, a, dbName, tableName, rowId)
}

func log(d interface{}, err error, msg string) {
	fmt.Printf("%s: %s, %s\n", d, err, msg)
}

func TestNoTrace(t *testing.T) {
	db, err := Open("")
	checkNoError(t, err, "couldn't open database: %s")
	defer db.Close()
	db.Trace(nil, nil)
	db.SetAuthorizer(nil, nil)
	db.Profile(nil, nil)
	db.ProgressHandler(nil, 0, nil)
	db.BusyHandler(nil, nil)
	db.CommitHook(nil, nil)
	db.RollbackHook(nil, nil)
	db.UpdateHook(nil, nil)
}

func TestTrace(t *testing.T) {
	db, err := Open("")
	checkNoError(t, err, "couldn't open database: %s")
	defer db.Close()
	db.Trace(trace, "TRACE")
	err = db.SetAuthorizer(authorizer, "AUTH")
	checkNoError(t, err, "couldn't set an authorizer")
	db.Profile(profile, "PROFILE")
	db.ProgressHandler(progressHandler, 1, nil /*20*/)
	db.CommitHook(commitHook, "CMT")
	db.RollbackHook(rollbackHook, "RBK")
	db.UpdateHook(updateHook, "UPD")
	db.Exists("SELECT 1 WHERE 1 = ?", 1)
}

func TestLog(t *testing.T) {
	Log(0, "One message")
}
<commit_msg>Use Logf in trace tests.<commit_after>package sqlite_test

import (
	"fmt"
	. "github.com/gwenn/gosqlite"
	"testing"
)

func init() {
	//ConfigLog(log, "LOG")
}

func trace(d interface{}, sql string) {
	if t, ok := d.(*testing.T); ok {
		t.Logf("TRACE: %s\n", sql)
	} else {
		fmt.Printf("%s: %s\n", d, sql)
	}
}

func authorizer(d interface{}, action Action, arg1, arg2, dbName, triggerName string) Auth {
	if t, ok := d.(*testing.T); ok {
		t.Logf("AUTH: %d, %s, %s, %s, %s\n", action, arg1, arg2, dbName, triggerName)
	} else {
		fmt.Printf("%s: %d, %s, %s, %s, %s\n", d, action, arg1, arg2, dbName, triggerName)
	}
	return AUTH_OK
}

func profile(d interface{}, sql string, nanoseconds uint64) {
	if t, ok := d.(*testing.T); ok {
		t.Logf("PROFILE: %s = %d µs\n", sql, nanoseconds/1e3)
	} else {
		fmt.Printf("%s: %s = %d µs\n", d, sql, nanoseconds/1e3)
	}
}

func progressHandler(d interface{}) bool {
	if t, ok := d.(*testing.T); ok {
		t.Log("+")
	} else {
		fmt.Print("+")
	}
	return false
}

func commitHook(d interface{}) bool {
	if t, ok := d.(*testing.T); ok {
		t.Log("CMT")
	} else {
		fmt.Printf("%s\n", d)
	}
	return false
}

func rollbackHook(d interface{}) {
	if t, ok := d.(*testing.T); ok {
		t.Log("RBK")
	} else {
		fmt.Printf("%s\n", d)
	}
}

func updateHook(d interface{}, a Action, dbName, tableName string, rowId int64) {
	if t, ok := d.(*testing.T); ok {
		t.Logf("UPD: %d, %s.%s.%d\n", a, dbName, tableName, rowId)
	} else {
		fmt.Printf("%s: %d, %s.%s.%d\n", d, a, dbName, tableName, rowId)
	}
}

func log(d interface{}, err error, msg string) {
	if t, ok := d.(*testing.T); ok {
		t.Logf("%s: %s\n", err, msg)
	} else {
		fmt.Printf("%s: %s, %s\n", d, err, msg)
	}
}

func TestNoTrace(t *testing.T) {
	db, err := Open("")
	checkNoError(t, err, "couldn't open database: %s")
	defer db.Close()
	db.Trace(nil, nil)
	db.SetAuthorizer(nil, nil)
	db.Profile(nil, nil)
	db.ProgressHandler(nil, 0, nil)
	db.BusyHandler(nil, nil)
	db.CommitHook(nil, nil)
	db.RollbackHook(nil, nil)
	db.UpdateHook(nil, nil)
}

func TestTrace(t *testing.T) {
	db, err := Open("")
	checkNoError(t, err, "couldn't open database: %s")
	defer db.Close()
	db.Trace(trace, t)
	err = db.SetAuthorizer(authorizer, t)
	checkNoError(t, err, "couldn't set an authorizer")
	db.Profile(profile, t)
	db.ProgressHandler(progressHandler, 1, t)
	db.CommitHook(commitHook, t)
	db.RollbackHook(rollbackHook, t)
	db.UpdateHook(updateHook, t)
	db.Exists("SELECT 1 WHERE 1 = ?", 1)
}

func TestLog(t *testing.T) {
	Log(0, "One message")
}
<|endoftext|>


<commit_before>package main

func FakeFunc() {

}

func main() {
	FakeFunc()
}
<commit_msg>Correcting the fake error related to FakeFunk.<commit_after>package main

// FakeFunc is used to fake an error, or not.
func FakeFunc() {

}

func main() {
	FakeFunc()
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 Audrius Butkevicius and Contributors (see the CONTRIBUTORS file).

package main

import (
	"crypto/tls"
	"log"
	"net"
	"sync"
	"time"

	syncthingprotocol "github.com/syncthing/protocol"

	"github.com/syncthing/relaysrv/protocol"
)

var (
	outboxesMut = sync.RWMutex{}
	outboxes    = make(map[syncthingprotocol.DeviceID]chan interface{})
)

func protocolListener(addr string, config *tls.Config) {
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatalln(err)
	}

	for {
		conn, err := listener.Accept()
		setTCPOptions(conn)
		if err != nil {
			if debug {
				log.Println(err)
			}
			continue
		}

		if debug {
			log.Println("Protocol listener accepted connection from", conn.RemoteAddr())
		}

		go protocolConnectionHandler(conn, config)
	}
}

func protocolConnectionHandler(tcpConn net.Conn, config *tls.Config) {
	conn := tls.Server(tcpConn, config)
	err := conn.Handshake()
	if err != nil {
		if debug {
			log.Println("Protocol connection TLS handshake:", conn.RemoteAddr(), err)
		}
		conn.Close()
		return
	}

	state := conn.ConnectionState()
	if (!state.NegotiatedProtocolIsMutual || state.NegotiatedProtocol != protocol.ProtocolName) && debug {
		log.Println("Protocol negotiation error")
	}

	certs := state.PeerCertificates
	if len(certs) != 1 {
		if debug {
			log.Println("Certificate list error")
		}
		conn.Close()
		return
	}

	id := syncthingprotocol.NewDeviceID(certs[0].Raw)

	messages := make(chan interface{})
	errors := make(chan error, 1)
	outbox := make(chan interface{})

	go func(conn net.Conn, message chan<- interface{}, errors chan<- error) {
		for {
			msg, err := protocol.ReadMessage(conn)
			if err != nil {
				errors <- err
				return
			}
			messages <- msg
		}
	}(conn, messages, errors)

	pingTicker := time.NewTicker(pingInterval)
	timeoutTicker := time.NewTimer(networkTimeout)
	joined := false

	for {
		select {
		case message := <-messages:
			timeoutTicker.Reset(networkTimeout)
			if debug {
				log.Printf("Message %T from %s", message, id)
			}
			switch msg := message.(type) {
			case protocol.JoinRelayRequest:
				outboxesMut.RLock()
				_, ok := outboxes[id]
				outboxesMut.RUnlock()
				if ok {
					protocol.WriteMessage(conn, protocol.ResponseAlreadyConnected)
					if debug {
						log.Println("Already have a peer with the same ID", id, conn.RemoteAddr())
					}
					conn.Close()
					continue
				}

				outboxesMut.Lock()
				outboxes[id] = outbox
				outboxesMut.Unlock()
				joined = true

				protocol.WriteMessage(conn, protocol.ResponseSuccess)
			case protocol.ConnectRequest:
				requestedPeer := syncthingprotocol.DeviceIDFromBytes(msg.ID)
				outboxesMut.RLock()
				peerOutbox, ok := outboxes[requestedPeer]
				outboxesMut.RUnlock()
				if !ok {
					if debug {
						log.Println(id, "is looking", requestedPeer, "which does not exist")
					}
					protocol.WriteMessage(conn, protocol.ResponseNotFound)
					conn.Close()
					continue
				}

				ses := newSession()

				go ses.Serve()

				clientInvitation := ses.GetClientInvitationMessage(requestedPeer)
				serverInvitation := ses.GetServerInvitationMessage(id)

				if err := protocol.WriteMessage(conn, clientInvitation); err != nil {
					if debug {
						log.Printf("Error sending invitation from %s to client: %s", id, err)
					}
					conn.Close()
					continue
				}

				peerOutbox <- serverInvitation

				if debug {
					log.Println("Sent invitation from", id, "to", requestedPeer)
				}
				conn.Close()
			case protocol.Pong:
			default:
				if debug {
					log.Printf("Unknown message %s: %T", id, message)
				}
				protocol.WriteMessage(conn, protocol.ResponseUnexpectedMessage)
				conn.Close()
			}
		case err := <-errors:
			if debug {
				log.Printf("Closing connection %s: %s", id, err)
			}
			// Potentially closing a second time.
			close(outbox)
			conn.Close()
			outboxesMut.Lock()
			delete(outboxes, id)
			outboxesMut.Unlock()
			return
		case <-pingTicker.C:
			if !joined {
				if debug {
					log.Println(id, "didn't join within", pingInterval)
				}
				conn.Close()
				continue
			}

			if err := protocol.WriteMessage(conn, protocol.Ping{}); err != nil {
				if debug {
					log.Println(id, err)
				}
				conn.Close()
			}
		case <-timeoutTicker.C:
			// We should receive a error from the reader loop, which will cause
			// us to quit this loop.
			if debug {
				log.Printf("%s timed out", id)
			}
			conn.Close()
		case msg := <-outbox:
			if debug {
				log.Printf("Sending message %T to %s", msg, id)
			}
			if err := protocol.WriteMessage(conn, msg); err != nil {
				if debug {
					log.Println(id, err)
				}
				conn.Close()
			}
		}
	}
}
<commit_msg>Fix bugs<commit_after>// Copyright (C) 2015 Audrius Butkevicius and Contributors (see the CONTRIBUTORS file).

package main

import (
	"crypto/tls"
	"log"
	"net"
	"sync"
	"time"

	syncthingprotocol "github.com/syncthing/protocol"

	"github.com/syncthing/relaysrv/protocol"
)

var (
	outboxesMut = sync.RWMutex{}
	outboxes    = make(map[syncthingprotocol.DeviceID]chan interface{})
)

func protocolListener(addr string, config *tls.Config) {
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatalln(err)
	}

	for {
		conn, err := listener.Accept()
		setTCPOptions(conn)
		if err != nil {
			if debug {
				log.Println(err)
			}
			continue
		}

		if debug {
			log.Println("Protocol listener accepted connection from", conn.RemoteAddr())
		}

		go protocolConnectionHandler(conn, config)
	}
}

func protocolConnectionHandler(tcpConn net.Conn, config *tls.Config) {
	conn := tls.Server(tcpConn, config)
	err := conn.Handshake()
	if err != nil {
		if debug {
			log.Println("Protocol connection TLS handshake:", conn.RemoteAddr(), err)
		}
		conn.Close()
		return
	}

	state := conn.ConnectionState()
	if (!state.NegotiatedProtocolIsMutual || state.NegotiatedProtocol != protocol.ProtocolName) && debug {
		log.Println("Protocol negotiation error")
	}

	certs := state.PeerCertificates
	if len(certs) != 1 {
		if debug {
			log.Println("Certificate list error")
		}
		conn.Close()
		return
	}

	id := syncthingprotocol.NewDeviceID(certs[0].Raw)

	messages := make(chan interface{})
	errors := make(chan error, 1)
	outbox := make(chan interface{})

	go func(conn net.Conn, message chan<- interface{}, errors chan<- error) {
		for {
			msg, err := protocol.ReadMessage(conn)
			if err != nil {
				errors <- err
				return
			}
			messages <- msg
		}
	}(conn, messages, errors)

	pingTicker := time.NewTicker(pingInterval)
	timeoutTicker := time.NewTimer(networkTimeout)
	joined := false

	for {
		select {
		case message := <-messages:
			timeoutTicker.Reset(networkTimeout)
			if debug {
				log.Printf("Message %T from %s", message, id)
			}
			switch msg := message.(type) {
			case protocol.JoinRelayRequest:
				outboxesMut.RLock()
				_, ok := outboxes[id]
				outboxesMut.RUnlock()
				if ok {
					protocol.WriteMessage(conn, protocol.ResponseAlreadyConnected)
					if debug {
						log.Println("Already have a peer with the same ID", id, conn.RemoteAddr())
					}
					conn.Close()
					continue
				}

				outboxesMut.Lock()
				outboxes[id] = outbox
				outboxesMut.Unlock()
				joined = true

				protocol.WriteMessage(conn, protocol.ResponseSuccess)
			case protocol.ConnectRequest:
				requestedPeer := syncthingprotocol.DeviceIDFromBytes(msg.ID)
				outboxesMut.RLock()
				peerOutbox, ok := outboxes[requestedPeer]
				outboxesMut.RUnlock()
				if !ok {
					if debug {
						log.Println(id, "is looking", requestedPeer, "which does not exist")
					}
					protocol.WriteMessage(conn, protocol.ResponseNotFound)
					conn.Close()
					continue
				}

				ses := newSession()

				go ses.Serve()

				clientInvitation := ses.GetClientInvitationMessage(requestedPeer)
				serverInvitation := ses.GetServerInvitationMessage(id)

				if err := protocol.WriteMessage(conn, clientInvitation); err != nil {
					if debug {
						log.Printf("Error sending invitation from %s to client: %s", id, err)
					}
					conn.Close()
					continue
				}

				peerOutbox <- serverInvitation

				if debug {
					log.Println("Sent invitation from", id, "to", requestedPeer)
				}
				conn.Close()
			case protocol.Pong:
			default:
				if debug {
					log.Printf("Unknown message %s: %T", id, message)
				}
				protocol.WriteMessage(conn, protocol.ResponseUnexpectedMessage)
				conn.Close()
			}
		case err := <-errors:
			if debug {
				log.Printf("Closing connection %s: %s", id, err)
			}
			// Potentially closing a second time.
			close(outbox)
			conn.Close()
			// Only delete the outbox if the client join, as it migth be a
			// lookup request coming from the same client.
			if joined {
				outboxesMut.Lock()
				delete(outboxes, id)
				outboxesMut.Unlock()
			}
			return
		case <-pingTicker.C:
			if !joined {
				if debug {
					log.Println(id, "didn't join within", pingInterval)
				}
				conn.Close()
				continue
			}

			if err := protocol.WriteMessage(conn, protocol.Ping{}); err != nil {
				if debug {
					log.Println(id, err)
				}
				conn.Close()
			}
		case <-timeoutTicker.C:
			// We should receive a error from the reader loop, which will cause
			// us to quit this loop.
			if debug {
				log.Printf("%s timed out", id)
			}
			conn.Close()
		case msg := <-outbox:
			if debug {
				log.Printf("Sending message %T to %s", msg, id)
			}
			if err := protocol.WriteMessage(conn, msg); err != nil {
				if debug {
					log.Println(id, err)
				}
				conn.Close()
			}
		}
	}
}
<|endoftext|>


<commit_before>package ec2

//----------------------------------------------------------------------------
// Package factored import statement:
//----------------------------------------------------------------------------

import (

	// Stdlib:
	"encoding/base64"
	"fmt"
	"log"
	"strings"

	// Community:
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
)

//----------------------------------------------------------------------------
// Typedefs:
//----------------------------------------------------------------------------

// Data contains variables used by EC2 API.
type Data struct {
	Region        string
	SubnetIds     string
	ImageID       string
	KeyPair       string
	InsType       string
	Hostname      string
	ElasticIP     string
	VpcCidrBlock  string
	VpcID         string
	VpcNameTag    string
	IntSubnetCidr string
	ExtSubnetCidr string
	IntSubnetID   string
	ExtSubnetID   string
}

//--------------------------------------------------------------------------
// func: Setup
//--------------------------------------------------------------------------

// Setup an EC2 VPC and all the related components.
func (d *Data) Setup() error {

	// Connect and authenticate to the API endpoint:
	log.Printf("[setup-ec2] INFO Connecting to %s\n", d.Region)
	svc := ec2.New(session.New(&aws.Config{Region: aws.String(d.Region)}))

	// Create the VPC:
	if err := d.createVpc(*svc); err != nil {
		return err
	}

	// Create external and internal subnets:
	if err := d.createSubnets(*svc); err != nil {
		return err
	}

	// Return on success:
	return nil
}

//-------------------------------------------------------------------------
// func: createVpc
//-------------------------------------------------------------------------

func (d *Data) createVpc(svc ec2.EC2) error {

	// Forge the VPC request:
	prmsVpc := &ec2.CreateVpcInput{
		CidrBlock:       aws.String(d.VpcCidrBlock),
		DryRun:          aws.Bool(false),
		InstanceTenancy: aws.String("default"),
	}

	// Send the VPC request:
	rspVpc, err := svc.CreateVpc(prmsVpc)
	if err != nil {
		return err
	}

	// Store the VPC ID:
	d.VpcID = *rspVpc.Vpc.VpcId
	log.Printf("[setup-ec2] INFO New VPC %s\n", d.VpcID)

	// Tag the VPC:
	if err = tag(d.VpcID, "Name", d.VpcNameTag, svc); err != nil {
		return err
	}

	// Return on success:
	log.Printf("[setup-ec2] INFO VPC tagged as %s\n", d.VpcNameTag)
	return nil
}

//-------------------------------------------------------------------------
// func: createSubnets
//-------------------------------------------------------------------------

func (d *Data) createSubnets(svc ec2.EC2) error {

	// Map to iterate:
	nets := map[string]map[string]string{
		"internal": map[string]string{"SubnetCidr": d.IntSubnetCidr, "SubnetID": ""},
		"external": map[string]string{"SubnetCidr": d.ExtSubnetCidr, "SubnetID": ""},
	}

	// For each subnet:
	for k, v := range nets {

		// Forge the subnet request:
		params := &ec2.CreateSubnetInput{
			CidrBlock: aws.String(v["SubnetCidr"]),
			VpcId:     aws.String(d.VpcID),
			DryRun:    aws.Bool(false),
		}

		// Send the subnet request:
		resp, err := svc.CreateSubnet(params)
		if err != nil {
			return err
		}

		// Locally store the subnet ID:
		v["SubnetID"] = *resp.Subnet.SubnetId
		log.Printf("[setup-ec2] INFO New %s subnet %s\n", k, v["SubnetID"])

		// Tag the subnet:
		if err = tag(v["SubnetID"], "Name", d.VpcNameTag, svc); err != nil {
			return err
		}
	}

	// Store subnet IDs:
	d.IntSubnetID = nets["internal"]["SubnetID"]
	d.ExtSubnetID = nets["external"]["SubnetID"]

	return nil
}

//-------------------------------------------------------------------------
// func: tag
//-------------------------------------------------------------------------

func tag(resource, key, value string, svc ec2.EC2) error {

	// Forge the tag request:
	params := &ec2.CreateTagsInput{
		Resources: []*string{
			aws.String(resource),
		},
		Tags: []*ec2.Tag{
			{
				Key:   aws.String(key),
				Value: aws.String(value),
			},
		},
		DryRun: aws.Bool(false),
	}

	// Send the tag request:
	_, err := svc.CreateTags(params)
	if err != nil {
		return err
	}

	return nil
}

//--------------------------------------------------------------------------
// func: Run
//--------------------------------------------------------------------------

// Run uses EC2 API to launch a new instance.
func (d *Data) Run(udata []byte) error {

	// Connect and authenticate to the API endpoint:
	svc := ec2.New(session.New(&aws.Config{Region: aws.String(d.Region)}))

	// Forge the network interfaces:
	var networkInterfaces []*ec2.InstanceNetworkInterfaceSpecification
	subnetIds := strings.Split(d.SubnetIds, ",")

	for i := 0; i < len(subnetIds); i++ {

		// Forge the security group ids:
		var securityGroupIds []*string
		for _, gid := range strings.Split(subnetIds[i], ":")[1:] {
			securityGroupIds = append(securityGroupIds, aws.String(gid))
		}

		iface := ec2.InstanceNetworkInterfaceSpecification{
			DeleteOnTermination: aws.Bool(true),
			DeviceIndex:         aws.Int64(int64(i)),
			Groups:              securityGroupIds,
			SubnetId:            aws.String(strings.Split(subnetIds[i], ":")[0]),
		}

		networkInterfaces = append(networkInterfaces, &iface)
	}

	// Send the request:
	runResult, err := svc.RunInstances(&ec2.RunInstancesInput{
		ImageId:           aws.String(d.ImageID),
		MinCount:          aws.Int64(1),
		MaxCount:          aws.Int64(1),
		KeyName:           aws.String(d.KeyPair),
		InstanceType:      aws.String(d.InsType),
		NetworkInterfaces: networkInterfaces,
		UserData:          aws.String(base64.StdEncoding.EncodeToString([]byte(udata))),
	})

	if err != nil {
		return err
	}

	// Pretty-print the response data:
	fmt.Println("Created instance", *runResult.Instances[0].InstanceId)

	// Add tags to the created instance:
	_, err = svc.CreateTags(&ec2.CreateTagsInput{
		Resources: []*string{runResult.Instances[0].InstanceId},
		Tags: []*ec2.Tag{
			{
				Key:   aws.String("Name"),
				Value: aws.String(d.Hostname),
			},
		},
	})

	if err != nil {
		return err
	}

	// Allocate an elastic IP address:
	if d.ElasticIP == "true" {

		params := &ec2.AllocateAddressInput{
			Domain: aws.String("vpc"),
			DryRun: aws.Bool(false),
		}

		// Send the request:
		resp, err := svc.AllocateAddress(params)

		if err != nil {
			return err
		}

		// Pretty-print the response data:
		fmt.Println(resp)
	}

	// Return on success:
	return nil
}
<commit_msg>Tag as internal or external<commit_after>package ec2

//----------------------------------------------------------------------------
// Package factored import statement:
//----------------------------------------------------------------------------

import (

	// Stdlib:
	"encoding/base64"
	"fmt"
	"log"
	"strings"

	// Community:
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
)

//----------------------------------------------------------------------------
// Typedefs:
//----------------------------------------------------------------------------

// Data contains variables used by EC2 API.
type Data struct {
	Region        string
	SubnetIds     string
	ImageID       string
	KeyPair       string
	InsType       string
	Hostname      string
	ElasticIP     string
	VpcCidrBlock  string
	VpcID         string
	VpcNameTag    string
	IntSubnetCidr string
	ExtSubnetCidr string
	IntSubnetID   string
	ExtSubnetID   string
}

//--------------------------------------------------------------------------
// func: Setup
//--------------------------------------------------------------------------

// Setup an EC2 VPC and all the related components.
func (d *Data) Setup() error {

	// Connect and authenticate to the API endpoint:
	log.Printf("[setup-ec2] INFO Connecting to %s\n", d.Region)
	svc := ec2.New(session.New(&aws.Config{Region: aws.String(d.Region)}))

	// Create the VPC:
	if err := d.createVpc(*svc); err != nil {
		return err
	}

	// Create external and internal subnets:
	if err := d.createSubnets(*svc); err != nil {
		return err
	}

	// Return on success:
	return nil
}

//-------------------------------------------------------------------------
// func: createVpc
//-------------------------------------------------------------------------

func (d *Data) createVpc(svc ec2.EC2) error {

	// Forge the VPC request:
	prmsVpc := &ec2.CreateVpcInput{
		CidrBlock:       aws.String(d.VpcCidrBlock),
		DryRun:          aws.Bool(false),
		InstanceTenancy: aws.String("default"),
	}

	// Send the VPC request:
	rspVpc, err := svc.CreateVpc(prmsVpc)
	if err != nil {
		return err
	}

	// Store the VPC ID:
	d.VpcID = *rspVpc.Vpc.VpcId
	log.Printf("[setup-ec2] INFO New VPC %s\n", d.VpcID)

	// Tag the VPC:
	if err = tag(d.VpcID, "Name", d.VpcNameTag, svc); err != nil {
		return err
	}

	// Return on success:
	log.Printf("[setup-ec2] INFO VPC tagged as %s\n", d.VpcNameTag)
	return nil
}

//-------------------------------------------------------------------------
// func: createSubnets
//-------------------------------------------------------------------------

func (d *Data) createSubnets(svc ec2.EC2) error {

	// Map to iterate:
	nets := map[string]map[string]string{
		"internal": map[string]string{"SubnetCidr": d.IntSubnetCidr, "SubnetID": ""},
		"external": map[string]string{"SubnetCidr": d.ExtSubnetCidr, "SubnetID": ""},
	}

	// For each subnet:
	for k, v := range nets {

		// Forge the subnet request:
		params := &ec2.CreateSubnetInput{
			CidrBlock: aws.String(v["SubnetCidr"]),
			VpcId:     aws.String(d.VpcID),
			DryRun:    aws.Bool(false),
		}

		// Send the subnet request:
		resp, err := svc.CreateSubnet(params)
		if err != nil {
			return err
		}

		// Locally store the subnet ID:
		v["SubnetID"] = *resp.Subnet.SubnetId
		log.Printf("[setup-ec2] INFO New %s subnet %s\n", k, v["SubnetID"])

		// Tag the subnet:
		if err = tag(v["SubnetID"], "Name", k, svc); err != nil {
			return err
		}
	}

	// Store subnet IDs:
	d.IntSubnetID = nets["internal"]["SubnetID"]
	d.ExtSubnetID = nets["external"]["SubnetID"]

	return nil
}

//-------------------------------------------------------------------------
// func: tag
//-------------------------------------------------------------------------

func tag(resource, key, value string, svc ec2.EC2) error {

	// Forge the tag request:
	params := &ec2.CreateTagsInput{
		Resources: []*string{
			aws.String(resource),
		},
		Tags: []*ec2.Tag{
			{
				Key:   aws.String(key),
				Value: aws.String(value),
			},
		},
		DryRun: aws.Bool(false),
	}

	// Send the tag request:
	_, err := svc.CreateTags(params)
	if err != nil {
		return err
	}

	return nil
}

//--------------------------------------------------------------------------
// func: Run
//--------------------------------------------------------------------------

// Run uses EC2 API to launch a new instance.
func (d *Data) Run(udata []byte) error {

	// Connect and authenticate to the API endpoint:
	svc := ec2.New(session.New(&aws.Config{Region: aws.String(d.Region)}))

	// Forge the network interfaces:
	var networkInterfaces []*ec2.InstanceNetworkInterfaceSpecification
	subnetIds := strings.Split(d.SubnetIds, ",")

	for i := 0; i < len(subnetIds); i++ {

		// Forge the security group ids:
		var securityGroupIds []*string
		for _, gid := range strings.Split(subnetIds[i], ":")[1:] {
			securityGroupIds = append(securityGroupIds, aws.String(gid))
		}

		iface := ec2.InstanceNetworkInterfaceSpecification{
			DeleteOnTermination: aws.Bool(true),
			DeviceIndex:         aws.Int64(int64(i)),
			Groups:              securityGroupIds,
			SubnetId:            aws.String(strings.Split(subnetIds[i], ":")[0]),
		}

		networkInterfaces = append(networkInterfaces, &iface)
	}

	// Send the request:
	runResult, err := svc.RunInstances(&ec2.RunInstancesInput{
		ImageId:           aws.String(d.ImageID),
		MinCount:          aws.Int64(1),
		MaxCount:          aws.Int64(1),
		KeyName:           aws.String(d.KeyPair),
		InstanceType:      aws.String(d.InsType),
		NetworkInterfaces: networkInterfaces,
		UserData:          aws.String(base64.StdEncoding.EncodeToString([]byte(udata))),
	})

	if err != nil {
		return err
	}

	// Pretty-print the response data:
	fmt.Println("Created instance", *runResult.Instances[0].InstanceId)

	// Add tags to the created instance:
	_, err = svc.CreateTags(&ec2.CreateTagsInput{
		Resources: []*string{runResult.Instances[0].InstanceId},
		Tags: []*ec2.Tag{
			{
				Key:   aws.String("Name"),
				Value: aws.String(d.Hostname),
			},
		},
	})

	if err != nil {
		return err
	}

	// Allocate an elastic IP address:
	if d.ElasticIP == "true" {

		params := &ec2.AllocateAddressInput{
			Domain: aws.String("vpc"),
			DryRun: aws.Bool(false),
		}

		// Send the request:
		resp, err := svc.AllocateAddress(params)

		if err != nil {
			return err
		}

		// Pretty-print the response data:
		fmt.Println(resp)
	}

	// Return on success:
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Matthew Holt and The Caddy 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 headers

import (
	"fmt"
	"net/http"
	"regexp"
	"strings"

	"github.com/caddyserver/caddy/v2"
	"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)

func init() {
	caddy.RegisterModule(Handler{})
}

// Handler is a middleware which can mutate HTTP headers.
type Handler struct {
	Request  *HeaderOps     `json:"request,omitempty"`
	Response *RespHeaderOps `json:"response,omitempty"`
}

// CaddyModule returns the Caddy module information.
func (Handler) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		Name: "http.handlers.headers",
		New:  func() caddy.Module { return new(Handler) },
	}
}

// Provision sets up h's configuration.
func (h *Handler) Provision(_ caddy.Context) error {
	if h.Request != nil {
		err := h.Request.provision()
		if err != nil {
			return err
		}
	}
	if h.Response != nil {
		err := h.Response.provision()
		if err != nil {
			return err
		}
	}
	return nil
}

// Validate ensures h's configuration is valid.
func (h Handler) Validate() error {
	if h.Request != nil {
		err := h.Request.validate()
		if err != nil {
			return err
		}
	}
	if h.Response != nil {
		err := h.Response.validate()
		if err != nil {
			return err
		}
	}
	return nil
}

func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
	repl := r.Context().Value(caddy.ReplacerCtxKey).(caddy.Replacer)

	h.Request.applyTo(r.Header, repl)

	// request header's Host is handled specially by the
	// Go standard library, so if that header was changed,
	// change it in the Host field since the Header won't
	// be used
	if intendedHost := r.Header.Get("Host"); intendedHost != "" {
		r.Host = intendedHost
		r.Header.Del("Host")
	}

	if h.Response != nil {
		if h.Response.Deferred || h.Response.Require != nil {
			w = &responseWriterWrapper{
				ResponseWriterWrapper: &caddyhttp.ResponseWriterWrapper{ResponseWriter: w},
				replacer:              repl,
				require:               h.Response.Require,
				headerOps:             h.Response.HeaderOps,
			}
		} else {
			h.Response.applyTo(w.Header(), repl)
		}
	}

	return next.ServeHTTP(w, r)
}

// HeaderOps defines some operations to
// perform on HTTP headers.
type HeaderOps struct {
	Add     http.Header              `json:"add,omitempty"`
	Set     http.Header              `json:"set,omitempty"`
	Delete  []string                 `json:"delete,omitempty"`
	Replace map[string][]Replacement `json:"replace,omitempty"`
}

func (ops *HeaderOps) provision() error {
	for fieldName, replacements := range ops.Replace {
		for i, r := range replacements {
			if r.SearchRegexp != "" {
				re, err := regexp.Compile(r.SearchRegexp)
				if err != nil {
					return fmt.Errorf("replacement %d for header field '%s': %v", i, fieldName, err)
				}
				replacements[i].re = re
			}
		}
	}
	return nil
}

func (ops HeaderOps) validate() error {
	for fieldName, replacements := range ops.Replace {
		for _, r := range replacements {
			if r.Search != "" && r.SearchRegexp != "" {
				return fmt.Errorf("cannot specify both a substring search and a regular expression search for field '%s'", fieldName)
			}
		}
	}
	return nil
}

// Replacement describes a string replacement,
// either a simple and fast sugbstring search
// or a slower but more powerful regex search.
type Replacement struct {
	Search       string `json:"search,omitempty"`
	SearchRegexp string `json:"search_regexp,omitempty"`
	Replace      string `json:"replace,omitempty"`

	re *regexp.Regexp
}

// RespHeaderOps is like HeaderOps, but
// optionally deferred until response time.
type RespHeaderOps struct {
	*HeaderOps
	Require  *caddyhttp.ResponseMatcher `json:"require,omitempty"`
	Deferred bool                       `json:"deferred,omitempty"`
}

func (ops *HeaderOps) applyTo(hdr http.Header, repl caddy.Replacer) {
	if ops == nil {
		return
	}

	for fieldName, vals := range ops.Add {
		fieldName = repl.ReplaceAll(fieldName, "")
		for _, v := range vals {
			hdr.Add(fieldName, repl.ReplaceAll(v, ""))
		}
	}

	for fieldName, vals := range ops.Set {
		fieldName = repl.ReplaceAll(fieldName, "")
		for i := range vals {
			vals[i] = repl.ReplaceAll(vals[i], "")
		}
		hdr.Set(fieldName, strings.Join(vals, ","))
	}

	for _, fieldName := range ops.Delete {
		hdr.Del(repl.ReplaceAll(fieldName, ""))
	}

	for fieldName, replacements := range ops.Replace {
		fieldName = repl.ReplaceAll(fieldName, "")

		// perform replacements across all fields
		if fieldName == "*" {
			for _, r := range replacements {
				search := repl.ReplaceAll(r.Search, "")
				replace := repl.ReplaceAll(r.Replace, "")
				for fieldName, vals := range hdr {
					for i := range vals {
						if r.re != nil {
							hdr[fieldName][i] = r.re.ReplaceAllString(hdr[fieldName][i], replace)
						} else {
							hdr[fieldName][i] = strings.ReplaceAll(hdr[fieldName][i], search, replace)
						}
					}
				}
			}
			continue
		}

		// perform replacements only with the named field
		for _, r := range replacements {
			search := repl.ReplaceAll(r.Search, "")
			replace := repl.ReplaceAll(r.Replace, "")
			for i := range hdr[fieldName] {
				if r.re != nil {
					hdr[fieldName][i] = r.re.ReplaceAllString(hdr[fieldName][i], replace)
				} else {
					hdr[fieldName][i] = strings.ReplaceAll(hdr[fieldName][i], search, replace)
				}
			}
		}
	}
}

// responseWriterWrapper defers response header
// operations until WriteHeader is called.
type responseWriterWrapper struct {
	*caddyhttp.ResponseWriterWrapper
	replacer    caddy.Replacer
	require     *caddyhttp.ResponseMatcher
	headerOps   *HeaderOps
	wroteHeader bool
}

func (rww *responseWriterWrapper) WriteHeader(status int) {
	if rww.wroteHeader {
		return
	}
	rww.wroteHeader = true
	if rww.require == nil || rww.require.Match(status, rww.ResponseWriterWrapper.Header()) {
		rww.headerOps.applyTo(rww.ResponseWriterWrapper.Header(), rww.replacer)
	}
	rww.ResponseWriterWrapper.WriteHeader(status)
}

func (rww *responseWriterWrapper) Write(d []byte) (int, error) {
	if !rww.wroteHeader {
		rww.WriteHeader(http.StatusOK)
	}
	return rww.ResponseWriterWrapper.Write(d)
}

// Interface guards
var (
	_ caddy.Provisioner           = (*Handler)(nil)
	_ caddyhttp.MiddlewareHandler = (*Handler)(nil)
	_ caddyhttp.HTTPInterfaces    = (*responseWriterWrapper)(nil)
)
<commit_msg>headers: Ability to mutate request headers including http.Request.Host<commit_after>// Copyright 2015 Matthew Holt and The Caddy 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 headers

import (
	"fmt"
	"net/http"
	"regexp"
	"strings"

	"github.com/caddyserver/caddy/v2"
	"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)

func init() {
	caddy.RegisterModule(Handler{})
}

// Handler is a middleware which can mutate HTTP headers.
type Handler struct {
	Request  *HeaderOps     `json:"request,omitempty"`
	Response *RespHeaderOps `json:"response,omitempty"`
}

// CaddyModule returns the Caddy module information.
func (Handler) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		Name: "http.handlers.headers",
		New:  func() caddy.Module { return new(Handler) },
	}
}

// Provision sets up h's configuration.
func (h *Handler) Provision(_ caddy.Context) error {
	if h.Request != nil {
		err := h.Request.provision()
		if err != nil {
			return err
		}
	}
	if h.Response != nil {
		err := h.Response.provision()
		if err != nil {
			return err
		}
	}
	return nil
}

// Validate ensures h's configuration is valid.
func (h Handler) Validate() error {
	if h.Request != nil {
		err := h.Request.validate()
		if err != nil {
			return err
		}
	}
	if h.Response != nil {
		err := h.Response.validate()
		if err != nil {
			return err
		}
	}
	return nil
}

func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
	repl := r.Context().Value(caddy.ReplacerCtxKey).(caddy.Replacer)

	if h.Request != nil {
		h.Request.ApplyToRequest(r)
	}

	if h.Response != nil {
		if h.Response.Deferred || h.Response.Require != nil {
			w = &responseWriterWrapper{
				ResponseWriterWrapper: &caddyhttp.ResponseWriterWrapper{ResponseWriter: w},
				replacer:              repl,
				require:               h.Response.Require,
				headerOps:             h.Response.HeaderOps,
			}
		} else {
			h.Response.ApplyTo(w.Header(), repl)
		}
	}

	return next.ServeHTTP(w, r)
}

// HeaderOps defines some operations to
// perform on HTTP headers.
type HeaderOps struct {
	Add     http.Header              `json:"add,omitempty"`
	Set     http.Header              `json:"set,omitempty"`
	Delete  []string                 `json:"delete,omitempty"`
	Replace map[string][]Replacement `json:"replace,omitempty"`
}

func (ops *HeaderOps) provision() error {
	for fieldName, replacements := range ops.Replace {
		for i, r := range replacements {
			if r.SearchRegexp != "" {
				re, err := regexp.Compile(r.SearchRegexp)
				if err != nil {
					return fmt.Errorf("replacement %d for header field '%s': %v", i, fieldName, err)
				}
				replacements[i].re = re
			}
		}
	}
	return nil
}

func (ops HeaderOps) validate() error {
	for fieldName, replacements := range ops.Replace {
		for _, r := range replacements {
			if r.Search != "" && r.SearchRegexp != "" {
				return fmt.Errorf("cannot specify both a substring search and a regular expression search for field '%s'", fieldName)
			}
		}
	}
	return nil
}

// Replacement describes a string replacement,
// either a simple and fast sugbstring search
// or a slower but more powerful regex search.
type Replacement struct {
	Search       string `json:"search,omitempty"`
	SearchRegexp string `json:"search_regexp,omitempty"`
	Replace      string `json:"replace,omitempty"`

	re *regexp.Regexp
}

// RespHeaderOps is like HeaderOps, but
// optionally deferred until response time.
type RespHeaderOps struct {
	*HeaderOps
	Require  *caddyhttp.ResponseMatcher `json:"require,omitempty"`
	Deferred bool                       `json:"deferred,omitempty"`
}

// ApplyTo applies ops to hdr using repl.
func (ops HeaderOps) ApplyTo(hdr http.Header, repl caddy.Replacer) {
	// add
	for fieldName, vals := range ops.Add {
		fieldName = repl.ReplaceAll(fieldName, "")
		for _, v := range vals {
			hdr.Add(fieldName, repl.ReplaceAll(v, ""))
		}
	}

	// set
	for fieldName, vals := range ops.Set {
		fieldName = repl.ReplaceAll(fieldName, "")
		var newVals []string
		for i := range vals {
			// append to new slice so we don't overwrite
			// the original values in ops.Set
			newVals = append(newVals, repl.ReplaceAll(vals[i], ""))
		}
		hdr.Set(fieldName, strings.Join(newVals, ","))
	}

	// delete
	for _, fieldName := range ops.Delete {
		hdr.Del(repl.ReplaceAll(fieldName, ""))
	}

	// replace
	for fieldName, replacements := range ops.Replace {
		fieldName = repl.ReplaceAll(fieldName, "")

		// all fields...
		if fieldName == "*" {
			for _, r := range replacements {
				search := repl.ReplaceAll(r.Search, "")
				replace := repl.ReplaceAll(r.Replace, "")
				for fieldName, vals := range hdr {
					for i := range vals {
						if r.re != nil {
							hdr[fieldName][i] = r.re.ReplaceAllString(hdr[fieldName][i], replace)
						} else {
							hdr[fieldName][i] = strings.ReplaceAll(hdr[fieldName][i], search, replace)
						}
					}
				}
			}
			continue
		}

		// ...or only with the named field
		for _, r := range replacements {
			search := repl.ReplaceAll(r.Search, "")
			replace := repl.ReplaceAll(r.Replace, "")
			for i := range hdr[fieldName] {
				if r.re != nil {
					hdr[fieldName][i] = r.re.ReplaceAllString(hdr[fieldName][i], replace)
				} else {
					hdr[fieldName][i] = strings.ReplaceAll(hdr[fieldName][i], search, replace)
				}
			}
		}
	}
}

// ApplyToRequest applies ops to r, specially handling the Host
// header which the standard library does not include with the
// header map with all the others. This method mutates r.Host.
func (ops HeaderOps) ApplyToRequest(r *http.Request) {
	repl := r.Context().Value(caddy.ReplacerCtxKey).(caddy.Replacer)

	// capture the current Host header so we can
	// reset to it when we're done
	origHost, hadHost := r.Header["Host"]

	// append r.Host; this way, we know that our value
	// was last in the list, and if an Add operation
	// appended something else after it, that's probably
	// fine because it's weird to have multiple Host
	// headers anyway and presumably the one they added
	// is the one they wanted
	r.Header["Host"] = append(r.Header["Host"], r.Host)

	// apply header operations
	ops.ApplyTo(r.Header, repl)

	// retrieve the last Host value (likely the one we appended)
	if len(r.Header["Host"]) > 0 {
		r.Host = r.Header["Host"][len(r.Header["Host"])-1]
	} else {
		r.Host = ""
	}

	// reset the Host header slice
	if hadHost {
		r.Header["Host"] = origHost
	} else {
		delete(r.Header, "Host")
	}
}

// responseWriterWrapper defers response header
// operations until WriteHeader is called.
type responseWriterWrapper struct {
	*caddyhttp.ResponseWriterWrapper
	replacer    caddy.Replacer
	require     *caddyhttp.ResponseMatcher
	headerOps   *HeaderOps
	wroteHeader bool
}

func (rww *responseWriterWrapper) WriteHeader(status int) {
	if rww.wroteHeader {
		return
	}
	rww.wroteHeader = true
	if rww.require == nil || rww.require.Match(status, rww.ResponseWriterWrapper.Header()) {
		if rww.headerOps != nil {
			rww.headerOps.ApplyTo(rww.ResponseWriterWrapper.Header(), rww.replacer)
		}
	}
	rww.ResponseWriterWrapper.WriteHeader(status)
}

func (rww *responseWriterWrapper) Write(d []byte) (int, error) {
	if !rww.wroteHeader {
		rww.WriteHeader(http.StatusOK)
	}
	return rww.ResponseWriterWrapper.Write(d)
}

// Interface guards
var (
	_ caddy.Provisioner           = (*Handler)(nil)
	_ caddyhttp.MiddlewareHandler = (*Handler)(nil)
	_ caddyhttp.HTTPInterfaces    = (*responseWriterWrapper)(nil)
)
<|endoftext|>


<commit_before>package integration

import (
	"runtime"
	"testing"

	"github.com/buildkite/bintest/v3"
)

func TestMultilineCommandRunUnderBatch(t *testing.T) {
	t.Parallel()

	if runtime.GOOS != "windows" {
		t.Skip("batch test only applies to Windows")
	}

	tester, err := NewBootstrapTester()
	if err != nil {
		t.Fatal(err)
	}
	defer tester.Close()

	tester.MustMock(t, "Setup.cmd").Expect().Once()
	tester.MustMock(t, "BuildProject.cmd").Expect().Once()

	tester.RunAndCheck(t, "BUILDKITE_COMMAND=Setup.cmd\nset foo=bar\nBuildProject.cmd")
}

func TestPreExitHooksRunsAfterCommandFails(t *testing.T) {
	tester, err := NewBootstrapTester()
	if err != nil {
		t.Fatal(err)
	}
	defer tester.Close()

	// Mock out the meta-data calls to the agent after checkout
	agent := tester.MustMock(t, "buildkite-agent")
	agent.
		Expect("meta-data", "exists", "buildkite:git:commit").
		AndExitWith(0)

	preExitFunc := func(c *bintest.Call) {
		cmdExitStatus := c.GetEnv(`BUILDKITE_COMMAND_EXIT_STATUS`)
		if cmdExitStatus != "1" {
			t.Errorf("Expected an exit status of 1, got %v", cmdExitStatus)
		}
		c.Exit(0)
	}

	tester.ExpectGlobalHook("pre-exit").Once().AndCallFunc(preExitFunc)
	tester.ExpectLocalHook("pre-exit").Once().AndCallFunc(preExitFunc)

	if err = tester.Run(t, "BUILDKITE_COMMAND=false"); err == nil {
		t.Fatal("Expected the bootstrap to fail")
	}

	tester.CheckMocks(t)
}
<commit_msg>Add expectation that the set env var is present in subsequent commands<commit_after>package integration

import (
	"runtime"
	"testing"

	"github.com/buildkite/bintest/v3"
)

func TestMultilineCommandRunUnderBatch(t *testing.T) {
	t.Parallel()

	if runtime.GOOS != "windows" {
		t.Skip("batch test only applies to Windows")
	}

	tester, err := NewBootstrapTester()
	if err != nil {
		t.Fatal(err)
	}
	defer tester.Close()

	setup := tester.MustMock(t, "Setup.cmd")
	build := tester.MustMock(t, "BuildProject.cmd")

	setup.Expect().Once()
	build.Expect().Once().AndCallFunc(func(c *bintest.Call) {
		llamas := c.GetEnv(`LLAMAS`)
		if llamas != "COOL" {
			t.Errorf("Expected LLAMAS=COOL, got %s", llamas)
			c.Exit(1)
		} else {
			c.Exit(0)
		}
	})

	tester.RunAndCheck(t, "BUILDKITE_COMMAND=Setup.cmd\nset LLAMAS=COOL\nBuildProject.cmd")
}

func TestPreExitHooksRunsAfterCommandFails(t *testing.T) {
	tester, err := NewBootstrapTester()
	if err != nil {
		t.Fatal(err)
	}
	defer tester.Close()

	// Mock out the meta-data calls to the agent after checkout
	agent := tester.MustMock(t, "buildkite-agent")
	agent.
		Expect("meta-data", "exists", "buildkite:git:commit").
		AndExitWith(0)

	preExitFunc := func(c *bintest.Call) {
		cmdExitStatus := c.GetEnv(`BUILDKITE_COMMAND_EXIT_STATUS`)
		if cmdExitStatus != "1" {
			t.Errorf("Expected an exit status of 1, got %v", cmdExitStatus)
		}
		c.Exit(0)
	}

	tester.ExpectGlobalHook("pre-exit").Once().AndCallFunc(preExitFunc)
	tester.ExpectLocalHook("pre-exit").Once().AndCallFunc(preExitFunc)

	if err = tester.Run(t, "BUILDKITE_COMMAND=false"); err == nil {
		t.Fatal("Expected the bootstrap to fail")
	}

	tester.CheckMocks(t)
}
<|endoftext|>