<commit_before>package systests

import (
	"testing"
	"time"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/teams"
	"github.com/keybase/clockwork"
	"github.com/stretchr/testify/require"
	context "golang.org/x/net/context"
)

func TestTeamInviteSeitanInvitelinkHappy(t *testing.T) {
	t.Skip()
	testTeamInviteSeitanInvitelinkHappy(t, false /* implicitAdmin */)
	testTeamInviteSeitanInvitelinkHappy(t, true /* implicitAdmin */)
}

func testTeamInviteSeitanInvitelinkHappy(t *testing.T, implicitAdmin bool) {
	tt := newTeamTester(t)
	defer tt.cleanup()

	alice := tt.addUser("kvr")
	bob := tt.addUser("eci")

	teamIDParent, teamNameParent := alice.createTeam2()
	teamID := teamIDParent
	teamName := teamNameParent
	t.Logf("Created team %v %v", teamIDParent, teamNameParent)
	if implicitAdmin {
		subteamID, err := teams.CreateSubteam(context.TODO(), tt.users[0].tc.G, "sub1", teamNameParent, keybase1.TeamRole_NONE /* addSelfAs */)
		require.NoError(t, err)
		teamID = *subteamID
		subteamName, err := teamNameParent.Append("sub1")
		require.NoError(t, err)
		teamName = subteamName
		t.Logf("Created subteam %v %v", teamID, teamName)
	}

	maxUses, err := keybase1.NewTeamInviteFiniteUses(3)
	require.NoError(t, err)
	etime := keybase1.ToUnixTime(time.Now().Add(24 * time.Hour))
	link, err := alice.teamsClient.TeamCreateSeitanInvitelink(context.TODO(), keybase1.TeamCreateSeitanInvitelinkArg{
		Teamname: teamName.String(),
		Role:     keybase1.TeamRole_ADMIN,
		MaxUses:  maxUses,
		Etime:    &etime,
	})
	require.NoError(t, err)

	t.Logf("Created token %v", link)

	details := alice.teamGetDetails(teamName.String())
	require.Len(t, details.AnnotatedActiveInvites, 1)
	for _, aInvite := range details.AnnotatedActiveInvites {
		invite := aInvite.Invite
		require.Equal(t, keybase1.TeamRole_ADMIN, invite.Role)
		tic, err := invite.Type.C()
		require.NoError(t, err)
		require.Equal(t, keybase1.TeamInviteCategory_INVITELINK, tic)
	}

	bob.kickTeamRekeyd()
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.NoError(t, err)

	t.Logf("User used token, waiting for rekeyd")

	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(3))

	t0, err := teams.GetTeamByNameForTest(context.TODO(), alice.tc.G, teamName.String(), false /* public */, true /* needAdmin */)
	require.NoError(t, err)

	role, err := t0.MemberRole(context.TODO(), teams.NewUserVersion(bob.uid, 1))
	require.NoError(t, err)
	require.Equal(t, role, keybase1.TeamRole_ADMIN)
}

func TestTeamInviteLinkAfterLeave(t *testing.T) {
	tt := newTeamTester(t)
	defer tt.cleanup()

	alice := tt.addUser("ali")
	bob := tt.addUser("bob")

	teamID, teamName := alice.createTeam2()
	maxUses, err := keybase1.NewTeamInviteFiniteUses(100)
	require.NoError(t, err)
	etime := keybase1.ToUnixTime(time.Now().AddDate(1, 0, 0))
	link, err := alice.teamsClient.TeamCreateSeitanInvitelink(context.TODO(), keybase1.TeamCreateSeitanInvitelinkArg{
		Teamname: teamName.String(),
		Role:     keybase1.TeamRole_WRITER,
		MaxUses:  maxUses,
		Etime:    &etime,
	})
	require.NoError(t, err)

	t.Logf("Created team invite link: %#v", link)

	bob.kickTeamRekeyd()
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.NoError(t, err)

	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(3))

	// Bob leaves.
	bob.leave(teamName.String())

	// Make sure Bob gets different akey when accepting again.
	clock := clockwork.NewFakeClockAt(time.Now())
	clock.Advance(1 * time.Second)
	bob.tc.G.SetClock(clock)

	// Bob accepts the same invite again.
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.NoError(t, err)

	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(5))

	t.Logf("removing bob; expecting to ban since he was added by invitelink most recently")
	alice.removeTeamMember(teamName.String(), bob.username)
	t.Logf("bob tries to rejoin")
	clock.Advance(1 * time.Second)
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.Error(t, err, "server won't let bob back in")
	appErr, ok := err.(libkb.AppStatusError)
	require.True(t, ok, "got an app err")
	require.Equal(t, appErr.Code, libkb.SCTeamBanned)

	t.Logf("alice adds/removes manually to clear ban")
	alice.addTeamMember(teamName.String(), bob.username, keybase1.TeamRole_WRITER)
	alice.removeTeamMember(teamName.String(), bob.username)

	clock.Advance(1 * time.Second)
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.NoError(t, err, "bob can rejoin")
	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(9))
	t0, err := teams.GetTeamByNameForTest(context.TODO(), alice.tc.G,
		teamName.String(), false /* public */, true /* needAdmin */)
	require.NoError(t, err)
	role, err := t0.MemberRole(context.TODO(), teams.NewUserVersion(bob.uid, 1))
	require.NoError(t, err)
	require.Equal(t, role, keybase1.TeamRole_WRITER)
}

func TestCreateSeitanInvitelinkWithDuration(t *testing.T) {
	// Test for the GUI RPC.

	tt := newTeamTester(t)
	defer tt.cleanup()

	alice := tt.addUser("ali")
	_, teamName := alice.createTeam2()

	now := alice.tc.G.Clock().Now()

	maxUses := keybase1.TeamMaxUsesInfinite
	expireAfter := "1000 Y"
	_, err := alice.teamsClient.TeamCreateSeitanInvitelinkWithDuration(
		context.TODO(),
		keybase1.TeamCreateSeitanInvitelinkWithDurationArg{
			Teamname:    teamName.String(),
			Role:        keybase1.TeamRole_WRITER,
			MaxUses:     maxUses,
			ExpireAfter: &expireAfter,
		})
	require.NoError(t, err)

	details := alice.teamGetDetails(teamName.String())
	require.Len(t, details.AnnotatedActiveInvites, 1)
	for _, aInvite := range details.AnnotatedActiveInvites {
		invite := aInvite.Invite
		require.Equal(t, keybase1.TeamRole_WRITER, invite.Role)
		require.NotNil(t, invite.MaxUses)
		require.Equal(t, keybase1.TeamMaxUsesInfinite, *invite.MaxUses)
		require.NotNil(t, invite.Etime)
		require.Equal(t, now.Year()+1000, invite.Etime.Time().Year())
		require.Equal(t, keybase1.TeamMaxUsesInfinite, *invite.MaxUses)
		tic, err := invite.Type.C()
		require.NoError(t, err)
		require.Equal(t, keybase1.TeamInviteCategory_INVITELINK, tic)
	}
}
<commit_msg>change role for test (#23751)<commit_after>package systests

import (
	"testing"
	"time"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/teams"
	"github.com/keybase/clockwork"
	"github.com/stretchr/testify/require"
	context "golang.org/x/net/context"
)

func TestTeamInviteSeitanInvitelinkHappy(t *testing.T) {
	testTeamInviteSeitanInvitelinkHappy(t, false /* implicitAdmin */)
	testTeamInviteSeitanInvitelinkHappy(t, true /* implicitAdmin */)
}

func testTeamInviteSeitanInvitelinkHappy(t *testing.T, implicitAdmin bool) {
	tt := newTeamTester(t)
	defer tt.cleanup()

	alice := tt.addUser("kvr")
	bob := tt.addUser("eci")

	teamIDParent, teamNameParent := alice.createTeam2()
	teamID := teamIDParent
	teamName := teamNameParent
	t.Logf("Created team %v %v", teamIDParent, teamNameParent)
	if implicitAdmin {
		subteamID, err := teams.CreateSubteam(context.TODO(), tt.users[0].tc.G, "sub1", teamNameParent, keybase1.TeamRole_NONE /* addSelfAs */)
		require.NoError(t, err)
		teamID = *subteamID
		subteamName, err := teamNameParent.Append("sub1")
		require.NoError(t, err)
		teamName = subteamName
		t.Logf("Created subteam %v %v", teamID, teamName)
	}

	maxUses, err := keybase1.NewTeamInviteFiniteUses(3)
	require.NoError(t, err)
	etime := keybase1.ToUnixTime(time.Now().Add(24 * time.Hour))
	link, err := alice.teamsClient.TeamCreateSeitanInvitelink(context.TODO(), keybase1.TeamCreateSeitanInvitelinkArg{
		Teamname: teamName.String(),
		Role:     keybase1.TeamRole_WRITER,
		MaxUses:  maxUses,
		Etime:    &etime,
	})
	require.NoError(t, err)

	t.Logf("Created token %v", link)

	details := alice.teamGetDetails(teamName.String())
	require.Len(t, details.AnnotatedActiveInvites, 1)
	for _, aInvite := range details.AnnotatedActiveInvites {
		invite := aInvite.Invite
		require.Equal(t, keybase1.TeamRole_WRITER, invite.Role)
		tic, err := invite.Type.C()
		require.NoError(t, err)
		require.Equal(t, keybase1.TeamInviteCategory_INVITELINK, tic)
	}

	bob.kickTeamRekeyd()
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.NoError(t, err)

	t.Logf("User used token, waiting for rekeyd")

	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(3))

	t0, err := teams.GetTeamByNameForTest(context.TODO(), alice.tc.G, teamName.String(), false /* public */, true /* needAdmin */)
	require.NoError(t, err)

	role, err := t0.MemberRole(context.TODO(), teams.NewUserVersion(bob.uid, 1))
	require.NoError(t, err)
	require.Equal(t, role, keybase1.TeamRole_WRITER)
}

func TestTeamInviteLinkAfterLeave(t *testing.T) {
	tt := newTeamTester(t)
	defer tt.cleanup()

	alice := tt.addUser("ali")
	bob := tt.addUser("bob")

	teamID, teamName := alice.createTeam2()
	maxUses, err := keybase1.NewTeamInviteFiniteUses(100)
	require.NoError(t, err)
	etime := keybase1.ToUnixTime(time.Now().AddDate(1, 0, 0))
	link, err := alice.teamsClient.TeamCreateSeitanInvitelink(context.TODO(), keybase1.TeamCreateSeitanInvitelinkArg{
		Teamname: teamName.String(),
		Role:     keybase1.TeamRole_WRITER,
		MaxUses:  maxUses,
		Etime:    &etime,
	})
	require.NoError(t, err)

	t.Logf("Created team invite link: %#v", link)

	bob.kickTeamRekeyd()
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.NoError(t, err)

	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(3))

	// Bob leaves.
	bob.leave(teamName.String())

	// Make sure Bob gets different akey when accepting again.
	clock := clockwork.NewFakeClockAt(time.Now())
	clock.Advance(1 * time.Second)
	bob.tc.G.SetClock(clock)

	// Bob accepts the same invite again.
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.NoError(t, err)

	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(5))

	t.Logf("removing bob; expecting to ban since he was added by invitelink most recently")
	alice.removeTeamMember(teamName.String(), bob.username)
	t.Logf("bob tries to rejoin")
	clock.Advance(1 * time.Second)
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.Error(t, err, "server won't let bob back in")
	appErr, ok := err.(libkb.AppStatusError)
	require.True(t, ok, "got an app err")
	require.Equal(t, appErr.Code, libkb.SCTeamBanned)

	t.Logf("alice adds/removes manually to clear ban")
	alice.addTeamMember(teamName.String(), bob.username, keybase1.TeamRole_WRITER)
	alice.removeTeamMember(teamName.String(), bob.username)

	clock.Advance(1 * time.Second)
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.NoError(t, err, "bob can rejoin")
	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(9))
	t0, err := teams.GetTeamByNameForTest(context.TODO(), alice.tc.G,
		teamName.String(), false /* public */, true /* needAdmin */)
	require.NoError(t, err)
	role, err := t0.MemberRole(context.TODO(), teams.NewUserVersion(bob.uid, 1))
	require.NoError(t, err)
	require.Equal(t, role, keybase1.TeamRole_WRITER)
}

func TestCreateSeitanInvitelinkWithDuration(t *testing.T) {
	// Test for the GUI RPC.

	tt := newTeamTester(t)
	defer tt.cleanup()

	alice := tt.addUser("ali")
	_, teamName := alice.createTeam2()

	now := alice.tc.G.Clock().Now()

	maxUses := keybase1.TeamMaxUsesInfinite
	expireAfter := "1000 Y"
	_, err := alice.teamsClient.TeamCreateSeitanInvitelinkWithDuration(
		context.TODO(),
		keybase1.TeamCreateSeitanInvitelinkWithDurationArg{
			Teamname:    teamName.String(),
			Role:        keybase1.TeamRole_WRITER,
			MaxUses:     maxUses,
			ExpireAfter: &expireAfter,
		})
	require.NoError(t, err)

	details := alice.teamGetDetails(teamName.String())
	require.Len(t, details.AnnotatedActiveInvites, 1)
	for _, aInvite := range details.AnnotatedActiveInvites {
		invite := aInvite.Invite
		require.Equal(t, keybase1.TeamRole_WRITER, invite.Role)
		require.NotNil(t, invite.MaxUses)
		require.Equal(t, keybase1.TeamMaxUsesInfinite, *invite.MaxUses)
		require.NotNil(t, invite.Etime)
		require.Equal(t, now.Year()+1000, invite.Etime.Time().Year())
		require.Equal(t, keybase1.TeamMaxUsesInfinite, *invite.MaxUses)
		tic, err := invite.Type.C()
		require.NoError(t, err)
		require.Equal(t, keybase1.TeamInviteCategory_INVITELINK, tic)
	}
}
<|endoftext|>


<commit_before>package systests

import (
	"testing"
	"time"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/teams"
	"github.com/keybase/clockwork"
	"github.com/stretchr/testify/require"
	context "golang.org/x/net/context"
)

func TestTeamInviteSeitanInvitelinkHappy(t *testing.T) {
	testTeamInviteSeitanInvitelinkHappy(t, false /* implicitAdmin */)
	testTeamInviteSeitanInvitelinkHappy(t, true /* implicitAdmin */)
}

func testTeamInviteSeitanInvitelinkHappy(t *testing.T, implicitAdmin bool) {
	tt := newTeamTester(t)
	defer tt.cleanup()

	alice := tt.addUser("kvr")
	bob := tt.addUser("eci")

	teamIDParent, teamNameParent := alice.createTeam2()
	teamID := teamIDParent
	teamName := teamNameParent
	t.Logf("Created team %v %v", teamIDParent, teamNameParent)
	if implicitAdmin {
		subteamID, err := teams.CreateSubteam(context.TODO(), tt.users[0].tc.G, "sub1", teamNameParent, keybase1.TeamRole_NONE /* addSelfAs */)
		require.NoError(t, err)
		teamID = *subteamID
		subteamName, err := teamNameParent.Append("sub1")
		require.NoError(t, err)
		teamName = subteamName
		t.Logf("Created subteam %v %v", teamID, teamName)
	}

	maxUses, err := keybase1.NewTeamInviteFiniteUses(3)
	require.NoError(t, err)
	etime := keybase1.ToUnixTime(time.Now().Add(24 * time.Hour))
	link, err := alice.teamsClient.TeamCreateSeitanInvitelink(context.TODO(), keybase1.TeamCreateSeitanInvitelinkArg{
		Teamname: teamName.String(),
		Role:     keybase1.TeamRole_WRITER,
		MaxUses:  maxUses,
		Etime:    &etime,
	})
	require.NoError(t, err)

	t.Logf("Created token %v", link)

	details := alice.teamGetDetails(teamName.String())
	require.Len(t, details.AnnotatedActiveInvites, 1)
	for _, aInvite := range details.AnnotatedActiveInvites {
		invite := aInvite.InviteMetadata.Invite
		require.Equal(t, keybase1.TeamRole_WRITER, invite.Role)
		tic, err := invite.Type.C()
		require.NoError(t, err)
		require.Equal(t, keybase1.TeamInviteCategory_INVITELINK, tic)
	}

	bob.kickTeamRekeyd()
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.NoError(t, err)

	t.Logf("User used token, waiting for rekeyd")

	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(3))

	t0, err := teams.GetTeamByNameForTest(context.TODO(), alice.tc.G, teamName.String(), false /* public */, true /* needAdmin */)
	require.NoError(t, err)

	role, err := t0.MemberRole(context.TODO(), teams.NewUserVersion(bob.uid, 1))
	require.NoError(t, err)
	require.Equal(t, role, keybase1.TeamRole_WRITER)
}

func TestTeamInviteLinkAfterLeave(t *testing.T) {
	tt := newTeamTester(t)
	defer tt.cleanup()

	alice := tt.addUser("ali")
	bob := tt.addUser("bob")

	teamID, teamName := alice.createTeam2()
	maxUses, err := keybase1.NewTeamInviteFiniteUses(100)
	require.NoError(t, err)
	etime := keybase1.ToUnixTime(time.Now().AddDate(1, 0, 0))
	link, err := alice.teamsClient.TeamCreateSeitanInvitelink(context.TODO(), keybase1.TeamCreateSeitanInvitelinkArg{
		Teamname: teamName.String(),
		Role:     keybase1.TeamRole_WRITER,
		MaxUses:  maxUses,
		Etime:    &etime,
	})
	require.NoError(t, err)

	t.Logf("Created team invite link: %#v", link)

	bob.kickTeamRekeyd()
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.NoError(t, err)

	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(3))

	// Bob leaves.
	bob.leave(teamName.String())

	// Make sure Bob gets different akey when accepting again.
	clock := clockwork.NewFakeClockAt(time.Now())
	clock.Advance(1 * time.Second)
	bob.tc.G.SetClock(clock)

	// Bob accepts the same invite again.
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.NoError(t, err)

	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(5))

	t.Logf("removing bob; expecting to ban since he was added by invitelink most recently")
	alice.removeTeamMember(teamName.String(), bob.username)
	t.Logf("bob tries to rejoin")
	clock.Advance(1 * time.Second)
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.Error(t, err, "server won't let bob back in")
	appErr, ok := err.(libkb.AppStatusError)
	require.True(t, ok, "got an app err")
	require.Equal(t, appErr.Code, libkb.SCTeamBanned)

	t.Logf("alice adds/removes manually to clear ban")
	alice.addTeamMember(teamName.String(), bob.username, keybase1.TeamRole_WRITER)
	alice.removeTeamMember(teamName.String(), bob.username)

	clock.Advance(1 * time.Second)
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.NoError(t, err, "bob can rejoin")
	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(9))
	t0, err := teams.GetTeamByNameForTest(context.TODO(), alice.tc.G,
		teamName.String(), false /* public */, true /* needAdmin */)
	require.NoError(t, err)
	role, err := t0.MemberRole(context.TODO(), teams.NewUserVersion(bob.uid, 1))
	require.NoError(t, err)
	require.Equal(t, role, keybase1.TeamRole_WRITER)
}

func TestCreateSeitanInvitelinkWithDuration(t *testing.T) {
	// Test for the GUI RPC.

	tt := newTeamTester(t)
	defer tt.cleanup()

	alice := tt.addUser("ali")
	_, teamName := alice.createTeam2()

	now := alice.tc.G.Clock().Now()

	maxUses := keybase1.TeamMaxUsesInfinite
	expireAfter := "1000 Y"
	_, err := alice.teamsClient.TeamCreateSeitanInvitelinkWithDuration(
		context.TODO(),
		keybase1.TeamCreateSeitanInvitelinkWithDurationArg{
			Teamname:    teamName.String(),
			Role:        keybase1.TeamRole_WRITER,
			MaxUses:     maxUses,
			ExpireAfter: &expireAfter,
		})
	require.NoError(t, err)

	details := alice.teamGetDetails(teamName.String())
	require.Len(t, details.AnnotatedActiveInvites, 1)
	for _, aInvite := range details.AnnotatedActiveInvites {
		invite := aInvite.InviteMetadata.Invite
		require.Equal(t, keybase1.TeamRole_WRITER, invite.Role)
		require.NotNil(t, invite.MaxUses)
		require.Equal(t, keybase1.TeamMaxUsesInfinite, *invite.MaxUses)
		require.NotNil(t, invite.Etime)
		require.Equal(t, now.Year()+1000, invite.Etime.Time().Year())
		require.Equal(t, keybase1.TeamMaxUsesInfinite, *invite.MaxUses)
		tic, err := invite.Type.C()
		require.NoError(t, err)
		require.Equal(t, keybase1.TeamInviteCategory_INVITELINK, tic)
	}
}
<commit_msg>Fix TestCreateSeitanInvitelinkWithDuration test (#23989)<commit_after>package systests

import (
	"testing"
	"time"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/teams"
	"github.com/keybase/clockwork"
	"github.com/stretchr/testify/require"
	context "golang.org/x/net/context"
)

func TestTeamInviteSeitanInvitelinkHappy(t *testing.T) {
	testTeamInviteSeitanInvitelinkHappy(t, false /* implicitAdmin */)
	testTeamInviteSeitanInvitelinkHappy(t, true /* implicitAdmin */)
}

func testTeamInviteSeitanInvitelinkHappy(t *testing.T, implicitAdmin bool) {
	tt := newTeamTester(t)
	defer tt.cleanup()

	alice := tt.addUser("kvr")
	bob := tt.addUser("eci")

	teamIDParent, teamNameParent := alice.createTeam2()
	teamID := teamIDParent
	teamName := teamNameParent
	t.Logf("Created team %v %v", teamIDParent, teamNameParent)
	if implicitAdmin {
		subteamID, err := teams.CreateSubteam(context.TODO(), tt.users[0].tc.G, "sub1", teamNameParent, keybase1.TeamRole_NONE /* addSelfAs */)
		require.NoError(t, err)
		teamID = *subteamID
		subteamName, err := teamNameParent.Append("sub1")
		require.NoError(t, err)
		teamName = subteamName
		t.Logf("Created subteam %v %v", teamID, teamName)
	}

	maxUses, err := keybase1.NewTeamInviteFiniteUses(3)
	require.NoError(t, err)
	etime := keybase1.ToUnixTime(time.Now().Add(24 * time.Hour))
	link, err := alice.teamsClient.TeamCreateSeitanInvitelink(context.TODO(), keybase1.TeamCreateSeitanInvitelinkArg{
		Teamname: teamName.String(),
		Role:     keybase1.TeamRole_WRITER,
		MaxUses:  maxUses,
		Etime:    &etime,
	})
	require.NoError(t, err)

	t.Logf("Created token %v", link)

	details := alice.teamGetDetails(teamName.String())
	require.Len(t, details.AnnotatedActiveInvites, 1)
	for _, aInvite := range details.AnnotatedActiveInvites {
		invite := aInvite.InviteMetadata.Invite
		require.Equal(t, keybase1.TeamRole_WRITER, invite.Role)
		tic, err := invite.Type.C()
		require.NoError(t, err)
		require.Equal(t, keybase1.TeamInviteCategory_INVITELINK, tic)
	}

	bob.kickTeamRekeyd()
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.NoError(t, err)

	t.Logf("User used token, waiting for rekeyd")

	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(3))

	t0, err := teams.GetTeamByNameForTest(context.TODO(), alice.tc.G, teamName.String(), false /* public */, true /* needAdmin */)
	require.NoError(t, err)

	role, err := t0.MemberRole(context.TODO(), teams.NewUserVersion(bob.uid, 1))
	require.NoError(t, err)
	require.Equal(t, role, keybase1.TeamRole_WRITER)
}

func TestTeamInviteLinkAfterLeave(t *testing.T) {
	tt := newTeamTester(t)
	defer tt.cleanup()

	alice := tt.addUser("ali")
	bob := tt.addUser("bob")

	teamID, teamName := alice.createTeam2()
	maxUses, err := keybase1.NewTeamInviteFiniteUses(100)
	require.NoError(t, err)
	etime := keybase1.ToUnixTime(time.Now().AddDate(1, 0, 0))
	link, err := alice.teamsClient.TeamCreateSeitanInvitelink(context.TODO(), keybase1.TeamCreateSeitanInvitelinkArg{
		Teamname: teamName.String(),
		Role:     keybase1.TeamRole_WRITER,
		MaxUses:  maxUses,
		Etime:    &etime,
	})
	require.NoError(t, err)

	t.Logf("Created team invite link: %#v", link)

	bob.kickTeamRekeyd()
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.NoError(t, err)

	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(3))

	// Bob leaves.
	bob.leave(teamName.String())

	// Make sure Bob gets different akey when accepting again.
	clock := clockwork.NewFakeClockAt(time.Now())
	clock.Advance(1 * time.Second)
	bob.tc.G.SetClock(clock)

	// Bob accepts the same invite again.
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.NoError(t, err)

	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(5))

	t.Logf("removing bob; expecting to ban since he was added by invitelink most recently")
	alice.removeTeamMember(teamName.String(), bob.username)
	t.Logf("bob tries to rejoin")
	clock.Advance(1 * time.Second)
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.Error(t, err, "server won't let bob back in")
	appErr, ok := err.(libkb.AppStatusError)
	require.True(t, ok, "got an app err")
	require.Equal(t, appErr.Code, libkb.SCTeamBanned)

	t.Logf("alice adds/removes manually to clear ban")
	alice.addTeamMember(teamName.String(), bob.username, keybase1.TeamRole_WRITER)
	alice.removeTeamMember(teamName.String(), bob.username)

	clock.Advance(1 * time.Second)
	err = bob.teamsClient.TeamAcceptInvite(context.TODO(), keybase1.TeamAcceptInviteArg{
		Token: string(link.Ikey),
	})
	require.NoError(t, err, "bob can rejoin")
	alice.waitForTeamChangedGregor(teamID, keybase1.Seqno(9))
	t0, err := teams.GetTeamByNameForTest(context.TODO(), alice.tc.G,
		teamName.String(), false /* public */, true /* needAdmin */)
	require.NoError(t, err)
	role, err := t0.MemberRole(context.TODO(), teams.NewUserVersion(bob.uid, 1))
	require.NoError(t, err)
	require.Equal(t, role, keybase1.TeamRole_WRITER)
}

func TestCreateSeitanInvitelinkWithDuration(t *testing.T) {
	// Test for the GUI RPC.

	tt := newTeamTester(t)
	defer tt.cleanup()

	alice := tt.addUser("ali")
	_, teamName := alice.createTeam2()

	now := alice.tc.G.Clock().Now()

	maxUses := keybase1.TeamMaxUsesInfinite
	expireAfter := "10 Y"
	_, err := alice.teamsClient.TeamCreateSeitanInvitelinkWithDuration(
		context.TODO(),
		keybase1.TeamCreateSeitanInvitelinkWithDurationArg{
			Teamname:    teamName.String(),
			Role:        keybase1.TeamRole_WRITER,
			MaxUses:     maxUses,
			ExpireAfter: &expireAfter,
		})
	require.NoError(t, err)

	details := alice.teamGetDetails(teamName.String())
	require.Len(t, details.AnnotatedActiveInvites, 1)
	for _, aInvite := range details.AnnotatedActiveInvites {
		invite := aInvite.InviteMetadata.Invite
		require.Equal(t, keybase1.TeamRole_WRITER, invite.Role)
		require.NotNil(t, invite.MaxUses)
		require.Equal(t, keybase1.TeamMaxUsesInfinite, *invite.MaxUses)
		require.NotNil(t, invite.Etime)
		require.Equal(t, now.Year()+10, invite.Etime.Time().Year())
		require.Equal(t, keybase1.TeamMaxUsesInfinite, *invite.MaxUses)
		tic, err := invite.Type.C()
		require.NoError(t, err)
		require.Equal(t, keybase1.TeamInviteCategory_INVITELINK, tic)
	}
}
<|endoftext|>


<commit_before>// Copyright 2022 The LUCI 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.

//go:build darwin && amd64 && go1.17
// +build darwin,amd64,go1.17

package osx_crypto_rand_entropy

import (
	"fmt"
	"os"

	// To use go:linkname.
	_ "unsafe"
)

//go:linkname altGetRandom crypto/rand.altGetRandom
var altGetRandom func(p []byte) error

func init() {
	// Unset "optimized" implementation, making crypto/rand fallback to the
	// general /dev/urandom implementation, as it did prior go1.17.
	//
	// Note: we assume nothing is calling crypto/rand during init() time. If it
	// does, it should import `osx_crypto_rand_entropy` package explicitly first
	// to make sure it installs the hack. This is all very fragile.
	altGetRandom = nil

	if os.Getenv("LUCI_GO_CHECK_HACKS") == "1" {
		fmt.Fprintf(os.Stderr, "LUCI_GO_CHECK_HACKS: osx_crypto_rand_entropy is enabled\n")
	}
}
<commit_msg>[hacks] Make osx_crypto_rand_entropy hack more robust.<commit_after>// Copyright 2022 The LUCI 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.

//go:build darwin && amd64 && go1.17
// +build darwin,amd64,go1.17

package osx_crypto_rand_entropy

import (
	"fmt"
	"os"

	// To make sure there's crypto/rand.altGetRandom we'll alias with go:linkname.
	_ "crypto/rand"
	// To use go:linkname.
	_ "unsafe"
)

//go:linkname altGetRandom crypto/rand.altGetRandom
var altGetRandom func(p []byte) error

func init() {
	// Unset "optimized" implementation, making crypto/rand fallback to the
	// general /dev/urandom implementation, as it did prior go1.17.
	//
	// Note: we assume nothing is calling crypto/rand during init() time. If it
	// does, it should import `osx_crypto_rand_entropy` package explicitly first
	// to make sure it installs the hack. This is all very fragile.
	altGetRandom = nil

	if os.Getenv("LUCI_GO_CHECK_HACKS") == "1" {
		fmt.Fprintf(os.Stderr, "LUCI_GO_CHECK_HACKS: osx_crypto_rand_entropy is enabled\n")
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes 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 fi

import (
	"k8s.io/api/core/v1"
	"k8s.io/kops/dnsprovider/pkg/dnsprovider"
	"k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/cloudinstances"
)

type Cloud interface {
	ProviderID() kops.CloudProviderID

	DNS() (dnsprovider.Interface, error)

	// FindVPCInfo looks up the specified VPC by id, returning info if found, otherwise (nil, nil)
	FindVPCInfo(id string) (*VPCInfo, error)

	// DeleteInstance deletes a cloud instance
	DeleteInstance(instance *cloudinstances.CloudInstanceGroupMember) error

	// DeleteGroup deletes the cloud resources that make up a CloudInstanceGroup, including the instances
	DeleteGroup(group *cloudinstances.CloudInstanceGroup) error

	// GetCloudGroups returns a map of cloud instances that back a kops cluster
	GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodes []v1.Node) (map[string]*cloudinstances.CloudInstanceGroup, error)
}

type VPCInfo struct {
	// CIDR is the IP address range for the VPC
	CIDR string

	// Subnets is a list of subnets that are part of the VPC
	Subnets []*SubnetInfo
}

type SubnetInfo struct {
	ID   string
	Zone string
	CIDR string
}

// zonesToCloud allows us to infer from certain well-known zones to a cloud
// Note it is safe to "overmap" zones that don't exist: we'll check later if the zones actually exist
var zonesToCloud = map[string]kops.CloudProviderID{
	"us-east-1a": kops.CloudProviderAWS,
	"us-east-1b": kops.CloudProviderAWS,
	"us-east-1c": kops.CloudProviderAWS,
	"us-east-1d": kops.CloudProviderAWS,
	"us-east-1e": kops.CloudProviderAWS,
	"us-east-1f": kops.CloudProviderAWS,

	"us-east-2a": kops.CloudProviderAWS,
	"us-east-2b": kops.CloudProviderAWS,
	"us-east-2c": kops.CloudProviderAWS,
	"us-east-2d": kops.CloudProviderAWS,
	"us-east-2e": kops.CloudProviderAWS,
	"us-east-2f": kops.CloudProviderAWS,

	"us-west-1a": kops.CloudProviderAWS,
	"us-west-1b": kops.CloudProviderAWS,
	"us-west-1c": kops.CloudProviderAWS,
	"us-west-1d": kops.CloudProviderAWS,
	"us-west-1e": kops.CloudProviderAWS,
	"us-west-1f": kops.CloudProviderAWS,

	"us-west-2a": kops.CloudProviderAWS,
	"us-west-2b": kops.CloudProviderAWS,
	"us-west-2c": kops.CloudProviderAWS,
	"us-west-2d": kops.CloudProviderAWS,
	"us-west-2e": kops.CloudProviderAWS,
	"us-west-2f": kops.CloudProviderAWS,

	"ca-central-1a": kops.CloudProviderAWS,
	"ca-central-1b": kops.CloudProviderAWS,

	"eu-west-1a": kops.CloudProviderAWS,
	"eu-west-1b": kops.CloudProviderAWS,
	"eu-west-1c": kops.CloudProviderAWS,
	"eu-west-1d": kops.CloudProviderAWS,
	"eu-west-1e": kops.CloudProviderAWS,

	"eu-west-2a": kops.CloudProviderAWS,
	"eu-west-2b": kops.CloudProviderAWS,

	"eu-central-1a": kops.CloudProviderAWS,
	"eu-central-1b": kops.CloudProviderAWS,
	"eu-central-1c": kops.CloudProviderAWS,
	"eu-central-1d": kops.CloudProviderAWS,
	"eu-central-1e": kops.CloudProviderAWS,

	"ap-south-1a": kops.CloudProviderAWS,
	"ap-south-1b": kops.CloudProviderAWS,
	"ap-south-1c": kops.CloudProviderAWS,
	"ap-south-1d": kops.CloudProviderAWS,
	"ap-south-1e": kops.CloudProviderAWS,

	"ap-southeast-1a": kops.CloudProviderAWS,
	"ap-southeast-1b": kops.CloudProviderAWS,
	"ap-southeast-1c": kops.CloudProviderAWS,
	"ap-southeast-1d": kops.CloudProviderAWS,
	"ap-southeast-1e": kops.CloudProviderAWS,

	"ap-southeast-2a": kops.CloudProviderAWS,
	"ap-southeast-2b": kops.CloudProviderAWS,
	"ap-southeast-2c": kops.CloudProviderAWS,
	"ap-southeast-2d": kops.CloudProviderAWS,
	"ap-southeast-2e": kops.CloudProviderAWS,

	"ap-northeast-1a": kops.CloudProviderAWS,
	"ap-northeast-1b": kops.CloudProviderAWS,
	"ap-northeast-1c": kops.CloudProviderAWS,
	"ap-northeast-1d": kops.CloudProviderAWS,
	"ap-northeast-1e": kops.CloudProviderAWS,

	"ap-northeast-2a": kops.CloudProviderAWS,
	"ap-northeast-2b": kops.CloudProviderAWS,
	"ap-northeast-2c": kops.CloudProviderAWS,
	"ap-northeast-2d": kops.CloudProviderAWS,
	"ap-northeast-2e": kops.CloudProviderAWS,

	"sa-east-1a": kops.CloudProviderAWS,
	"sa-east-1b": kops.CloudProviderAWS,
	"sa-east-1c": kops.CloudProviderAWS,
	"sa-east-1d": kops.CloudProviderAWS,
	"sa-east-1e": kops.CloudProviderAWS,

	"cn-north-1a": kops.CloudProviderAWS,
	"cn-north-1b": kops.CloudProviderAWS,

	"cn-northwest-1a": kops.CloudProviderAWS,
	"cn-northwest-1b": kops.CloudProviderAWS,

	"us-gov-west-1a": kops.CloudProviderAWS,
	"us-gov-west-1b": kops.CloudProviderAWS,

	// GCE
	"asia-east1-a": kops.CloudProviderGCE,
	"asia-east1-b": kops.CloudProviderGCE,
	"asia-east1-c": kops.CloudProviderGCE,
	"asia-east1-d": kops.CloudProviderGCE,

	"asia-northeast1-a": kops.CloudProviderGCE,
	"asia-northeast1-b": kops.CloudProviderGCE,
	"asia-northeast1-c": kops.CloudProviderGCE,
	"asia-northeast1-d": kops.CloudProviderGCE,

	"europe-west1-a": kops.CloudProviderGCE,
	"europe-west1-b": kops.CloudProviderGCE,
	"europe-west1-c": kops.CloudProviderGCE,
	"europe-west1-d": kops.CloudProviderGCE,
	"europe-west1-e": kops.CloudProviderGCE,

	"us-central1-a": kops.CloudProviderGCE,
	"us-central1-b": kops.CloudProviderGCE,
	"us-central1-c": kops.CloudProviderGCE,
	"us-central1-d": kops.CloudProviderGCE,
	"us-central1-e": kops.CloudProviderGCE,
	"us-central1-f": kops.CloudProviderGCE,
	"us-central1-g": kops.CloudProviderGCE,
	"us-central1-h": kops.CloudProviderGCE,

	"us-east1-a": kops.CloudProviderGCE,
	"us-east1-b": kops.CloudProviderGCE,
	"us-east1-c": kops.CloudProviderGCE,
	"us-east1-d": kops.CloudProviderGCE,

	"us-west1-a": kops.CloudProviderGCE,
	"us-west1-b": kops.CloudProviderGCE,
	"us-west1-c": kops.CloudProviderGCE,
	"us-west1-d": kops.CloudProviderGCE,

	"nyc1": kops.CloudProviderDO,
	"nyc2": kops.CloudProviderDO,
	"nyc3": kops.CloudProviderDO,

	"sfo1": kops.CloudProviderDO,
	"sfo2": kops.CloudProviderDO,

	"ams2": kops.CloudProviderDO,
	"ams3": kops.CloudProviderDO,

	"tor1": kops.CloudProviderDO,

	"sgp1": kops.CloudProviderDO,

	"lon1": kops.CloudProviderDO,

	"fra1": kops.CloudProviderDO,

	"blr1": kops.CloudProviderDO,
}

// GuessCloudForZone tries to infer the cloudprovider from the zone name
func GuessCloudForZone(zone string) (kops.CloudProviderID, bool) {
	c, found := zonesToCloud[zone]
	return c, found
}
<commit_msg>Recognize AWS Availability Zone EU-WEST-2C<commit_after>/*
Copyright 2016 The Kubernetes 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 fi

import (
	"k8s.io/api/core/v1"
	"k8s.io/kops/dnsprovider/pkg/dnsprovider"
	"k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/cloudinstances"
)

type Cloud interface {
	ProviderID() kops.CloudProviderID

	DNS() (dnsprovider.Interface, error)

	// FindVPCInfo looks up the specified VPC by id, returning info if found, otherwise (nil, nil)
	FindVPCInfo(id string) (*VPCInfo, error)

	// DeleteInstance deletes a cloud instance
	DeleteInstance(instance *cloudinstances.CloudInstanceGroupMember) error

	// DeleteGroup deletes the cloud resources that make up a CloudInstanceGroup, including the instances
	DeleteGroup(group *cloudinstances.CloudInstanceGroup) error

	// GetCloudGroups returns a map of cloud instances that back a kops cluster
	GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodes []v1.Node) (map[string]*cloudinstances.CloudInstanceGroup, error)
}

type VPCInfo struct {
	// CIDR is the IP address range for the VPC
	CIDR string

	// Subnets is a list of subnets that are part of the VPC
	Subnets []*SubnetInfo
}

type SubnetInfo struct {
	ID   string
	Zone string
	CIDR string
}

// zonesToCloud allows us to infer from certain well-known zones to a cloud
// Note it is safe to "overmap" zones that don't exist: we'll check later if the zones actually exist
var zonesToCloud = map[string]kops.CloudProviderID{
	"us-east-1a": kops.CloudProviderAWS,
	"us-east-1b": kops.CloudProviderAWS,
	"us-east-1c": kops.CloudProviderAWS,
	"us-east-1d": kops.CloudProviderAWS,
	"us-east-1e": kops.CloudProviderAWS,
	"us-east-1f": kops.CloudProviderAWS,

	"us-east-2a": kops.CloudProviderAWS,
	"us-east-2b": kops.CloudProviderAWS,
	"us-east-2c": kops.CloudProviderAWS,
	"us-east-2d": kops.CloudProviderAWS,
	"us-east-2e": kops.CloudProviderAWS,
	"us-east-2f": kops.CloudProviderAWS,

	"us-west-1a": kops.CloudProviderAWS,
	"us-west-1b": kops.CloudProviderAWS,
	"us-west-1c": kops.CloudProviderAWS,
	"us-west-1d": kops.CloudProviderAWS,
	"us-west-1e": kops.CloudProviderAWS,
	"us-west-1f": kops.CloudProviderAWS,

	"us-west-2a": kops.CloudProviderAWS,
	"us-west-2b": kops.CloudProviderAWS,
	"us-west-2c": kops.CloudProviderAWS,
	"us-west-2d": kops.CloudProviderAWS,
	"us-west-2e": kops.CloudProviderAWS,
	"us-west-2f": kops.CloudProviderAWS,

	"ca-central-1a": kops.CloudProviderAWS,
	"ca-central-1b": kops.CloudProviderAWS,

	"eu-west-1a": kops.CloudProviderAWS,
	"eu-west-1b": kops.CloudProviderAWS,
	"eu-west-1c": kops.CloudProviderAWS,
	"eu-west-1d": kops.CloudProviderAWS,
	"eu-west-1e": kops.CloudProviderAWS,

	"eu-west-2a": kops.CloudProviderAWS,
	"eu-west-2b": kops.CloudProviderAWS,
	"eu-west-2c": kops.CloudProviderAWS,

	"eu-central-1a": kops.CloudProviderAWS,
	"eu-central-1b": kops.CloudProviderAWS,
	"eu-central-1c": kops.CloudProviderAWS,
	"eu-central-1d": kops.CloudProviderAWS,
	"eu-central-1e": kops.CloudProviderAWS,

	"ap-south-1a": kops.CloudProviderAWS,
	"ap-south-1b": kops.CloudProviderAWS,
	"ap-south-1c": kops.CloudProviderAWS,
	"ap-south-1d": kops.CloudProviderAWS,
	"ap-south-1e": kops.CloudProviderAWS,

	"ap-southeast-1a": kops.CloudProviderAWS,
	"ap-southeast-1b": kops.CloudProviderAWS,
	"ap-southeast-1c": kops.CloudProviderAWS,
	"ap-southeast-1d": kops.CloudProviderAWS,
	"ap-southeast-1e": kops.CloudProviderAWS,

	"ap-southeast-2a": kops.CloudProviderAWS,
	"ap-southeast-2b": kops.CloudProviderAWS,
	"ap-southeast-2c": kops.CloudProviderAWS,
	"ap-southeast-2d": kops.CloudProviderAWS,
	"ap-southeast-2e": kops.CloudProviderAWS,

	"ap-northeast-1a": kops.CloudProviderAWS,
	"ap-northeast-1b": kops.CloudProviderAWS,
	"ap-northeast-1c": kops.CloudProviderAWS,
	"ap-northeast-1d": kops.CloudProviderAWS,
	"ap-northeast-1e": kops.CloudProviderAWS,

	"ap-northeast-2a": kops.CloudProviderAWS,
	"ap-northeast-2b": kops.CloudProviderAWS,
	"ap-northeast-2c": kops.CloudProviderAWS,
	"ap-northeast-2d": kops.CloudProviderAWS,
	"ap-northeast-2e": kops.CloudProviderAWS,

	"sa-east-1a": kops.CloudProviderAWS,
	"sa-east-1b": kops.CloudProviderAWS,
	"sa-east-1c": kops.CloudProviderAWS,
	"sa-east-1d": kops.CloudProviderAWS,
	"sa-east-1e": kops.CloudProviderAWS,

	"cn-north-1a": kops.CloudProviderAWS,
	"cn-north-1b": kops.CloudProviderAWS,

	"cn-northwest-1a": kops.CloudProviderAWS,
	"cn-northwest-1b": kops.CloudProviderAWS,

	"us-gov-west-1a": kops.CloudProviderAWS,
	"us-gov-west-1b": kops.CloudProviderAWS,

	// GCE
	"asia-east1-a": kops.CloudProviderGCE,
	"asia-east1-b": kops.CloudProviderGCE,
	"asia-east1-c": kops.CloudProviderGCE,
	"asia-east1-d": kops.CloudProviderGCE,

	"asia-northeast1-a": kops.CloudProviderGCE,
	"asia-northeast1-b": kops.CloudProviderGCE,
	"asia-northeast1-c": kops.CloudProviderGCE,
	"asia-northeast1-d": kops.CloudProviderGCE,

	"europe-west1-a": kops.CloudProviderGCE,
	"europe-west1-b": kops.CloudProviderGCE,
	"europe-west1-c": kops.CloudProviderGCE,
	"europe-west1-d": kops.CloudProviderGCE,
	"europe-west1-e": kops.CloudProviderGCE,

	"us-central1-a": kops.CloudProviderGCE,
	"us-central1-b": kops.CloudProviderGCE,
	"us-central1-c": kops.CloudProviderGCE,
	"us-central1-d": kops.CloudProviderGCE,
	"us-central1-e": kops.CloudProviderGCE,
	"us-central1-f": kops.CloudProviderGCE,
	"us-central1-g": kops.CloudProviderGCE,
	"us-central1-h": kops.CloudProviderGCE,

	"us-east1-a": kops.CloudProviderGCE,
	"us-east1-b": kops.CloudProviderGCE,
	"us-east1-c": kops.CloudProviderGCE,
	"us-east1-d": kops.CloudProviderGCE,

	"us-west1-a": kops.CloudProviderGCE,
	"us-west1-b": kops.CloudProviderGCE,
	"us-west1-c": kops.CloudProviderGCE,
	"us-west1-d": kops.CloudProviderGCE,

	"nyc1": kops.CloudProviderDO,
	"nyc2": kops.CloudProviderDO,
	"nyc3": kops.CloudProviderDO,

	"sfo1": kops.CloudProviderDO,
	"sfo2": kops.CloudProviderDO,

	"ams2": kops.CloudProviderDO,
	"ams3": kops.CloudProviderDO,

	"tor1": kops.CloudProviderDO,

	"sgp1": kops.CloudProviderDO,

	"lon1": kops.CloudProviderDO,

	"fra1": kops.CloudProviderDO,

	"blr1": kops.CloudProviderDO,
}

// GuessCloudForZone tries to infer the cloudprovider from the zone name
func GuessCloudForZone(zone string) (kops.CloudProviderID, bool) {
	c, found := zonesToCloud[zone]
	return c, found
}
<|endoftext|>


<commit_before>// Copyright 2017 the gousb Authors.  All rights reserved.
//
// 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 usb

import (
	"reflect"
	"testing"
	"time"
)

func TestEndpoint(t *testing.T) {
	defer func(i libusbIntf) { libusb = i }(libusb)
	for _, epCfg := range []struct {
		method string
		InterfaceSetup
		EndpointInfo
	}{
		{"Read", testBulkInSetup, testBulkInEP},
		{"Write", testIsoOutSetup, testIsoOutEP},
	} {
		t.Run(epCfg.method, func(t *testing.T) {
			for _, tc := range []struct {
				desc    string
				buf     []byte
				ret     int
				status  TransferStatus
				want    int
				wantErr bool
			}{
				{
					desc: "empty buffer",
					buf:  nil,
					ret:  10,
					want: 0,
				},
				{
					desc: "128B buffer, 60 transferred",
					buf:  make([]byte, 128),
					ret:  60,
					want: 60,
				},
				{
					desc:    "128B buffer, 10 transferred and then error",
					buf:     make([]byte, 128),
					ret:     10,
					status:  LIBUSB_TRANSFER_ERROR,
					want:    10,
					wantErr: true,
				},
			} {
				lib := newFakeLibusb()
				libusb = lib
				ep := newEndpoint(nil, epCfg.InterfaceSetup, epCfg.EndpointInfo, time.Second, time.Second)
				op, ok := reflect.TypeOf(ep).MethodByName(epCfg.method)
				if !ok {
					t.Fatalf("method %s not found in endpoint struct", epCfg.method)
				}
				go func() {
					fakeT := lib.waitForSubmitted()
					fakeT.length = tc.ret
					fakeT.status = tc.status
					close(fakeT.done)
				}()
				opv := op.Func.Interface().(func(*endpoint, []byte) (int, error))
				got, err := opv(ep, tc.buf)
				if (err != nil) != tc.wantErr {
					t.Errorf("%s: bulkInEP.Read(): got err: %v, err != nil is %v, want %v", tc.desc, err, err != nil, tc.wantErr)
					continue
				}
				if got != tc.want {
					t.Errorf("%s: bulkInEP.Read(): got %d bytes, want %d", tc.desc, got, tc.want)
				}
			}
		})
	}
}

func TestEndpointWrongDirection(t *testing.T) {
	ep := &endpoint{
		InterfaceSetup: testBulkInSetup,
		EndpointInfo:   testBulkInEP,
	}
	_, err := ep.Write([]byte{1, 2, 3})
	if err == nil {
		t.Error("bulkInEP.Write(): got nil error, want non-nil")
	}
	ep = &endpoint{
		InterfaceSetup: testIsoOutSetup,
		EndpointInfo:   testIsoOutEP,
	}
	_, err = ep.Read(make([]byte, 64))
	if err == nil {
		t.Error("isoOutEP.Read(): got nil error, want non-nil")
	}
}
<commit_msg>Add endpoint open test<commit_after>// Copyright 2017 the gousb Authors.  All rights reserved.
//
// 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 usb

import (
	"reflect"
	"testing"
	"time"
)

func TestEndpoint(t *testing.T) {
	defer func(i libusbIntf) { libusb = i }(libusb)
	for _, epCfg := range []struct {
		method string
		InterfaceSetup
		EndpointInfo
	}{
		{"Read", testBulkInSetup, testBulkInEP},
		{"Write", testIsoOutSetup, testIsoOutEP},
	} {
		t.Run(epCfg.method, func(t *testing.T) {
			for _, tc := range []struct {
				desc    string
				buf     []byte
				ret     int
				status  TransferStatus
				want    int
				wantErr bool
			}{
				{
					desc: "empty buffer",
					buf:  nil,
					ret:  10,
					want: 0,
				},
				{
					desc: "128B buffer, 60 transferred",
					buf:  make([]byte, 128),
					ret:  60,
					want: 60,
				},
				{
					desc:    "128B buffer, 10 transferred and then error",
					buf:     make([]byte, 128),
					ret:     10,
					status:  LIBUSB_TRANSFER_ERROR,
					want:    10,
					wantErr: true,
				},
			} {
				lib := newFakeLibusb()
				libusb = lib
				ep := newEndpoint(nil, epCfg.InterfaceSetup, epCfg.EndpointInfo, time.Second, time.Second)
				op, ok := reflect.TypeOf(ep).MethodByName(epCfg.method)
				if !ok {
					t.Fatalf("method %s not found in endpoint struct", epCfg.method)
				}
				go func() {
					fakeT := lib.waitForSubmitted()
					fakeT.length = tc.ret
					fakeT.status = tc.status
					close(fakeT.done)
				}()
				opv := op.Func.Interface().(func(*endpoint, []byte) (int, error))
				got, err := opv(ep, tc.buf)
				if (err != nil) != tc.wantErr {
					t.Errorf("%s: bulkInEP.Read(): got err: %v, err != nil is %v, want %v", tc.desc, err, err != nil, tc.wantErr)
					continue
				}
				if got != tc.want {
					t.Errorf("%s: bulkInEP.Read(): got %d bytes, want %d", tc.desc, got, tc.want)
				}
			}
		})
	}
}

func TestEndpointWrongDirection(t *testing.T) {
	ep := &endpoint{
		InterfaceSetup: testBulkInSetup,
		EndpointInfo:   testBulkInEP,
	}
	_, err := ep.Write([]byte{1, 2, 3})
	if err == nil {
		t.Error("bulkInEP.Write(): got nil error, want non-nil")
	}
	ep = &endpoint{
		InterfaceSetup: testIsoOutSetup,
		EndpointInfo:   testIsoOutEP,
	}
	_, err = ep.Read(make([]byte, 64))
	if err == nil {
		t.Error("isoOutEP.Read(): got nil error, want non-nil")
	}
}

func TestOpenEndpoint(t *testing.T) {
	origLib := libusb
	defer func() { libusb = origLib }()
	libusb = newFakeLibusb()

	c := NewContext()
	dev, err := c.OpenDeviceWithVidPid(0x8888, 0x0002)
	if dev == nil {
		t.Fatal("OpenDeviceWithVidPid(0x8888, 0x0002): got nil device, need non-nil")
	}
	defer dev.Close()
	if err != nil {
		t.Fatalf("OpenDeviceWithVidPid(0x8888, 0x0002): got error %v, want nil", err)
	}
	ep, err := dev.OpenEndpoint(1, 1, 2, 0x86)
	if err != nil {
		t.Errorf("OpenEndpoint(cfg=1, if=1, alt=2, ep=0x86): got error %v, want nil", err)
	}
	i := ep.Info()
	if got, want := i.Address, uint8(0x86); got != want {
		t.Errorf("OpenEndpoint(cfg=1, if=1, alt=2, ep=0x86): ep.Info.Address = %x, want %x", got, want)
	}
	if got, want := i.MaxIsoPacket, uint32(1024); got != want {
		t.Errorf("OpenEndpoint(cfg=1, if=1, alt=2, ep=0x86): ep.Info.MaxIsoPacket = %d, want %d", got, want)
	}
}
<|endoftext|>


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

// +build darwin dragonfly freebsd netbsd openbsd

// Routing sockets and messages

package syscall

import "unsafe"

// Round the length of a raw sockaddr up to align it properly.
func rsaAlignOf(salen int) int {
	salign := sizeofPtr
	// NOTE: It seems like 64-bit Darwin kernel still requires
	// 32-bit aligned access to BSD subsystem. Also NetBSD 6
	// kernel and beyond require 64-bit aligned access to routing
	// facilities.
	if darwin64Bit {
		salign = 4
	} else if netbsd32Bit {
		salign = 8
	}
	if salen == 0 {
		return salign
	}
	return (salen + salign - 1) & ^(salign - 1)
}

// RouteRIB returns routing information base, as known as RIB,
// which consists of network facility information, states and
// parameters.
func RouteRIB(facility, param int) ([]byte, error) {
	mib := []_C_int{CTL_NET, AF_ROUTE, 0, 0, _C_int(facility), _C_int(param)}
	// Find size.
	n := uintptr(0)
	if err := sysctl(mib, nil, &n, nil, 0); err != nil {
		return nil, err
	}
	if n == 0 {
		return nil, nil
	}
	tab := make([]byte, n)
	if err := sysctl(mib, &tab[0], &n, nil, 0); err != nil {
		return nil, err
	}
	return tab[:n], nil
}

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

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

type anyMessage struct {
	Msglen  uint16
	Version uint8
	Type    uint8
}

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

const rtaRtMask = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_GENMASK

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

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

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

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

const rtaIfaMask = RTA_IFA | RTA_NETMASK | RTA_BRD

func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) {
	if m.Header.Addrs&rtaIfaMask == 0 {
		return nil
	}
	b := m.Data[:]
	// We still see AF_UNSPEC in socket addresses on some
	// platforms. To identify each address family correctly, we
	// will use the address family of RTAX_NETMASK as a preferred
	// one on the 32-bit NetBSD kernel, also use the length of
	// RTAX_NETMASK socket address on the FreeBSD kernel.
	preferredFamily := uint8(AF_UNSPEC)
	for i := uint(0); i < RTAX_MAX; i++ {
		if m.Header.Addrs&rtaIfaMask&(1<<i) == 0 {
			continue
		}
		rsa := (*RawSockaddr)(unsafe.Pointer(&b[0]))
		switch i {
		case RTAX_IFA:
			if rsa.Family == AF_UNSPEC {
				rsa.Family = preferredFamily
			}
			sa, err := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
			if err != nil {
				return nil
			}
			sas = append(sas, sa)
		case RTAX_NETMASK:
			switch rsa.Family {
			case AF_UNSPEC:
				switch rsa.Len {
				case SizeofSockaddrInet4:
					rsa.Family = AF_INET
				case SizeofSockaddrInet6:
					rsa.Family = AF_INET6
				default:
					rsa.Family = AF_INET // an old fashion, AF_UNSPEC means AF_INET
				}
			case AF_INET, AF_INET6:
				preferredFamily = rsa.Family
			default:
				return nil
			}
			sa, err := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
			if err != nil {
				return nil
			}
			sas = append(sas, sa)
		case RTAX_BRD:
			// nothing to do
		}
		b = b[rsaAlignOf(int(rsa.Len)):]
	}
	return sas
}

// ParseRoutingMessage parses b as routing messages and returns the
// slice containing the RoutingMessage interfaces.
func ParseRoutingMessage(b []byte) (msgs []RoutingMessage, err error) {
	msgCount := 0
	for len(b) >= anyMessageLen {
		msgCount++
		any := (*anyMessage)(unsafe.Pointer(&b[0]))
		if any.Version != RTM_VERSION {
			b = b[any.Msglen:]
			continue
		}
		msgs = append(msgs, any.toRoutingMessage(b))
		b = b[any.Msglen:]
	}
	// We failed to parse any of the messages - version mismatch?
	if msgCount > 0 && len(msgs) == 0 {
		return nil, EINVAL
	}
	return msgs, nil
}

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

// +build darwin dragonfly freebsd netbsd openbsd

// Routing sockets and messages

package syscall

import "unsafe"

// Round the length of a raw sockaddr up to align it properly.
func rsaAlignOf(salen int) int {
	salign := sizeofPtr
	// NOTE: It seems like 64-bit Darwin kernel still requires
	// 32-bit aligned access to BSD subsystem. Also NetBSD 6
	// kernel and beyond require 64-bit aligned access to routing
	// facilities.
	if darwin64Bit {
		salign = 4
	} else if netbsd32Bit {
		salign = 8
	}
	if salen == 0 {
		return salign
	}
	return (salen + salign - 1) & ^(salign - 1)
}

// RouteRIB returns routing information base, as known as RIB,
// which consists of network facility information, states and
// parameters.
func RouteRIB(facility, param int) ([]byte, error) {
	mib := []_C_int{CTL_NET, AF_ROUTE, 0, 0, _C_int(facility), _C_int(param)}
	// Find size.
	n := uintptr(0)
	if err := sysctl(mib, nil, &n, nil, 0); err != nil {
		return nil, err
	}
	if n == 0 {
		return nil, nil
	}
	tab := make([]byte, n)
	if err := sysctl(mib, &tab[0], &n, nil, 0); err != nil {
		return nil, err
	}
	return tab[:n], nil
}

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

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

type anyMessage struct {
	Msglen  uint16
	Version uint8
	Type    uint8
}

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

const rtaRtMask = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_GENMASK

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

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

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

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

const rtaIfaMask = RTA_IFA | RTA_NETMASK | RTA_BRD

func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) {
	if m.Header.Addrs&rtaIfaMask == 0 {
		return nil
	}
	b := m.Data[:]
	// We still see AF_UNSPEC in socket addresses on some
	// platforms. To identify each address family correctly, we
	// will use the address family of RTAX_NETMASK as a preferred
	// one on the 32-bit NetBSD kernel, also use the length of
	// RTAX_NETMASK socket address on the FreeBSD kernel.
	preferredFamily := uint8(AF_UNSPEC)
	for i := uint(0); i < RTAX_MAX; i++ {
		if m.Header.Addrs&(1<<i) == 0 {
			continue
		}
		rsa := (*RawSockaddr)(unsafe.Pointer(&b[0]))
		switch i {
		case RTAX_IFA:
			if rsa.Family == AF_UNSPEC {
				rsa.Family = preferredFamily
			}
			sa, err := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
			if err != nil {
				return nil
			}
			sas = append(sas, sa)
		case RTAX_NETMASK:
			switch rsa.Family {
			case AF_UNSPEC:
				switch rsa.Len {
				case SizeofSockaddrInet4:
					rsa.Family = AF_INET
				case SizeofSockaddrInet6:
					rsa.Family = AF_INET6
				default:
					rsa.Family = AF_INET // an old fashion, AF_UNSPEC means AF_INET
				}
			case AF_INET, AF_INET6:
				preferredFamily = rsa.Family
			default:
				return nil
			}
			sa, err := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa)))
			if err != nil {
				return nil
			}
			sas = append(sas, sa)
		case RTAX_BRD:
			// nothing to do
		}
		b = b[rsaAlignOf(int(rsa.Len)):]
	}
	return sas
}

// ParseRoutingMessage parses b as routing messages and returns the
// slice containing the RoutingMessage interfaces.
func ParseRoutingMessage(b []byte) (msgs []RoutingMessage, err error) {
	msgCount := 0
	for len(b) >= anyMessageLen {
		msgCount++
		any := (*anyMessage)(unsafe.Pointer(&b[0]))
		if any.Version != RTM_VERSION {
			b = b[any.Msglen:]
			continue
		}
		msgs = append(msgs, any.toRoutingMessage(b))
		b = b[any.Msglen:]
	}
	// We failed to parse any of the messages - version mismatch?
	if msgCount > 0 && len(msgs) == 0 {
		return nil, EINVAL
	}
	return msgs, nil
}

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


<commit_before>// Copyright 2012 Santiago Corredoira
// Distributed under a BSD-like license.
package email

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/smtp"
	"path/filepath"
	"strings"
	"time"
)

type Attachment struct {
	Filename string
	Data     []byte
	Inline   bool
}

type Message struct {
	From            string
	To              []string
	Cc              []string
	Bcc             []string
	Subject         string
	Body            string
	BodyContentType string
	Attachments     map[string]*Attachment
}

func (m *Message) attach(file string, inline bool) error {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return err
	}

	_, filename := filepath.Split(file)

	m.Attachments[filename] = &Attachment{
		Filename: filename,
		Data:     data,
		Inline:   inline,
	}

	return nil
}

func (m *Message) Attach(file string) error {
	return m.attach(file, false)
}

func (m *Message) Inline(file string) error {
	return m.attach(file, true)
}

func newMessage(subject string, body string, bodyContentType string) *Message {
	m := &Message{Subject: subject, Body: body, BodyContentType: bodyContentType}

	m.Attachments = make(map[string]*Attachment)

	return m
}

// NewMessage returns a new Message that can compose an email with attachments
func NewMessage(subject string, body string) *Message {
	return newMessage(subject, body, "text/plain")
}

// NewMessage returns a new Message that can compose an HTML email with attachments
func NewHTMLMessage(subject string, body string) *Message {
	return newMessage(subject, body, "text/html")
}

// ToList returns all the recipients of the email
func (m *Message) Tolist() []string {
	tolist := m.To

	for _, cc := range m.Cc {
		tolist = append(tolist, cc)
	}

	for _, bcc := range m.Bcc {
		tolist = append(tolist, bcc)
	}

	return tolist
}

// Bytes returns the mail data
func (m *Message) Bytes() []byte {
	buf := bytes.NewBuffer(nil)

	buf.WriteString("From: " + m.From + "\n")

	t := time.Now()
	buf.WriteString("Date: " + t.Format(time.RFC822) + "\n")

	buf.WriteString("To: " + strings.Join(m.To, ",") + "\n")
	if len(m.Cc) > 0 {
		buf.WriteString("Cc: " + strings.Join(m.Cc, ",") + "\n")
	}

	buf.WriteString("Subject: " + m.Subject + "\n")
	buf.WriteString("MIME-Version: 1.0\n")

	boundary := "f46d043c813270fc6b04c2d223da"

	if len(m.Attachments) > 0 {
		buf.WriteString("Content-Type: multipart/mixed; boundary=" + boundary + "\n")
		buf.WriteString("--" + boundary + "\n")
	}

	buf.WriteString(fmt.Sprintf("Content-Type: %s; charset=utf-8\n\n", m.BodyContentType))
	buf.WriteString(m.Body)
	buf.WriteString("\n")

	if len(m.Attachments) > 0 {
		for _, attachment := range m.Attachments {
			buf.WriteString("\n\n--" + boundary + "\n")

			if attachment.Inline {
				buf.WriteString("Content-Type: message/rfc822\n")
				buf.WriteString("Content-Disposition: inline; filename=\"" + attachment.Filename + "\"\n\n")

				buf.Write(attachment.Data)
			} else {
				buf.WriteString("Content-Type: application/octet-stream\n")
				buf.WriteString("Content-Transfer-Encoding: base64\n")
				buf.WriteString("Content-Disposition: attachment; filename=\"" + attachment.Filename + "\"\n\n")

				b := make([]byte, base64.StdEncoding.EncodedLen(len(attachment.Data)))
				base64.StdEncoding.Encode(b, attachment.Data)
				buf.Write(b)
			}

			buf.WriteString("\n--" + boundary)
		}

		buf.WriteString("--")
	}

	return buf.Bytes()
}

func Send(addr string, auth smtp.Auth, m *Message) error {
	return smtp.SendMail(addr, auth, m.From, m.Tolist(), m.Bytes())
}
<commit_msg>Use CRLF for line breaks.<commit_after>// Copyright 2012 Santiago Corredoira
// Distributed under a BSD-like license.
package email

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/smtp"
	"path/filepath"
	"strings"
	"time"
)

type Attachment struct {
	Filename string
	Data     []byte
	Inline   bool
}

type Message struct {
	From            string
	To              []string
	Cc              []string
	Bcc             []string
	Subject         string
	Body            string
	BodyContentType string
	Attachments     map[string]*Attachment
}

func (m *Message) attach(file string, inline bool) error {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return err
	}

	_, filename := filepath.Split(file)

	m.Attachments[filename] = &Attachment{
		Filename: filename,
		Data:     data,
		Inline:   inline,
	}

	return nil
}

func (m *Message) Attach(file string) error {
	return m.attach(file, false)
}

func (m *Message) Inline(file string) error {
	return m.attach(file, true)
}

func newMessage(subject string, body string, bodyContentType string) *Message {
	m := &Message{Subject: subject, Body: body, BodyContentType: bodyContentType}

	m.Attachments = make(map[string]*Attachment)

	return m
}

// NewMessage returns a new Message that can compose an email with attachments
func NewMessage(subject string, body string) *Message {
	return newMessage(subject, body, "text/plain")
}

// NewMessage returns a new Message that can compose an HTML email with attachments
func NewHTMLMessage(subject string, body string) *Message {
	return newMessage(subject, body, "text/html")
}

// ToList returns all the recipients of the email
func (m *Message) Tolist() []string {
	tolist := m.To

	for _, cc := range m.Cc {
		tolist = append(tolist, cc)
	}

	for _, bcc := range m.Bcc {
		tolist = append(tolist, bcc)
	}

	return tolist
}

// Bytes returns the mail data
func (m *Message) Bytes() []byte {
	buf := bytes.NewBuffer(nil)

	buf.WriteString("From: " + m.From + "\r\n")

	t := time.Now()
	buf.WriteString("Date: " + t.Format(time.RFC822) + "\r\n")

	buf.WriteString("To: " + strings.Join(m.To, ",") + "\r\n")
	if len(m.Cc) > 0 {
		buf.WriteString("Cc: " + strings.Join(m.Cc, ",") + "\r\n")
	}

	buf.WriteString("Subject: " + m.Subject + "\r\n")
	buf.WriteString("MIME-Version: 1.0\r\n")

	boundary := "f46d043c813270fc6b04c2d223da"

	if len(m.Attachments) > 0 {
		buf.WriteString("Content-Type: multipart/mixed; boundary=" + boundary + "\r\n")
		buf.WriteString("--" + boundary + "\r\n")
	}

	buf.WriteString(fmt.Sprintf("Content-Type: %s; charset=utf-8\r\n\r\n", m.BodyContentType))
	buf.WriteString(m.Body)
	buf.WriteString("\r\n")

	if len(m.Attachments) > 0 {
		for _, attachment := range m.Attachments {
			buf.WriteString("\r\n\r\n--" + boundary + "\r\n")

			if attachment.Inline {
				buf.WriteString("Content-Type: message/rfc822\r\n")
				buf.WriteString("Content-Disposition: inline; filename=\"" + attachment.Filename + "\"\r\n\r\n")

				buf.Write(attachment.Data)
			} else {
				buf.WriteString("Content-Type: application/octet-stream\r\n")
				buf.WriteString("Content-Transfer-Encoding: base64\r\n")
				buf.WriteString("Content-Disposition: attachment; filename=\"" + attachment.Filename + "\"\r\n\r\n")

				b := make([]byte, base64.StdEncoding.EncodedLen(len(attachment.Data)))
				base64.StdEncoding.Encode(b, attachment.Data)
				buf.Write(b)
			}

			buf.WriteString("\r\n--" + boundary)
		}

		buf.WriteString("--")
	}

	return buf.Bytes()
}

func Send(addr string, auth smtp.Auth, m *Message) error {
	return smtp.SendMail(addr, auth, m.From, m.Tolist(), m.Bytes())
}
<|endoftext|>


<commit_before>//Package email is designed to provide an "email interface for humans."
//Designed to be robust and flexible, the email package aims to make sending email easy without getting in the way.
package email

import (
	"bytes"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/mail"
	"net/smtp"
	"net/textproto"
	"os"
	"path/filepath"
	"strings"
)

const (
	MAX_LINE_LENGTH = 76 //The maximum line length per RFC 2045
)

//Email is the type used for email messages
type Email struct {
	From        string
	To          []string
	Bcc         []string
	Cc          []string
	Subject     string
	Text        string //Plaintext message (optional)
	Html        string //Html message (optional)
	Headers     textproto.MIMEHeader
	Attachments map[string]*Attachment
	ReadReceipt []string
}

//NewEmail creates an Email, and returns the pointer to it.
func NewEmail() *Email {
	return &Email{Attachments: make(map[string]*Attachment), Headers: textproto.MIMEHeader{}}
}

//Attach is used to attach a file to the email.
//It attempts to open the file referenced by filename and, if successful, creates an Attachment.
//This Attachment is then appended to the slice of Email.Attachments.
//The function will then return the Attachment for reference, as well as nil for the error, if successful.
func (e *Email) Attach(filename string) (a *Attachment, err error) {
	//Check if the file exists, return any error
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		return nil, err
	}
	//Read the file, and set the appropriate headers
	buffer, _ := ioutil.ReadFile(filename)
	e.Attachments[filename] = &Attachment{
		Filename: filename,
		Header:   textproto.MIMEHeader{},
		Content:  buffer}
	at := e.Attachments[filename]
	//Get the Content-Type to be used in the MIMEHeader
	ct := mime.TypeByExtension(filepath.Ext(filename))
	if ct != "" {
		at.Header.Set("Content-Type", ct)
	} else {
		//If the Content-Type is blank, set the Content-Type to "application/octet-stream"
		at.Header.Set("Content-Type", "application/octet-stream")
	}
	at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=\"%s\"", filename))
	at.Header.Set("Content-Transfer-Encoding", "base64")
	return e.Attachments[filename], nil
}

//Bytes converts the Email object to a []byte representation, including all needed MIMEHeaders, boundaries, etc.
func (e *Email) Bytes() ([]byte, error) {
	buff := &bytes.Buffer{}
	w := multipart.NewWriter(buff)
	//Set the appropriate headers (overwriting any conflicts)
	//Leave out Bcc (only included in envelope headers)
	e.Headers.Set("To", strings.Join(e.To, ","))
	if e.Cc != nil {
		e.Headers.Set("Cc", strings.Join(e.Cc, ","))
	}
	e.Headers.Set("From", e.From)
	e.Headers.Set("Subject", e.Subject)
	if len(e.ReadReceipt) != 0 {
		e.Headers.Set("Disposition-Notification-To", strings.Join(e.ReadReceipt, ","))
	}
	e.Headers.Set("MIME-Version", "1.0")
	e.Headers.Set("Content-Type", fmt.Sprintf("multipart/mixed;\r\n boundary=%s\r\n", w.Boundary()))

	//Write the envelope headers (including any custom headers)
	if err := headerToBytes(buff, e.Headers); err != nil {
	}
	//Start the multipart/mixed part
	fmt.Fprintf(buff, "--%s\r\n", w.Boundary())
	header := textproto.MIMEHeader{}
	//Check to see if there is a Text or HTML field
	if e.Text != "" || e.Html != "" {
		subWriter := multipart.NewWriter(buff)
		//Create the multipart alternative part
		header.Set("Content-Type", fmt.Sprintf("multipart/alternative;\r\n boundary=%s\r\n", subWriter.Boundary()))
		//Write the header
		if err := headerToBytes(buff, header); err != nil {

		}
		//Create the body sections
		if e.Text != "" {
			header.Set("Content-Type", fmt.Sprintf("text/plain; charset=UTF-8"))
			header.Set("Content-Transfer-Encoding", "quoted-printable")
			subWriter.CreatePart(header)
			// Write the text
			if err := quotePrintEncode(buff, e.Text); err != nil {
				return nil, err
			}
		}
		if e.Html != "" {
			header.Set("Content-Type", fmt.Sprintf("text/html; charset=UTF-8"))
			header.Set("Content-Transfer-Encoding", "quoted-printable")
			subWriter.CreatePart(header)
			// Write the text
			if err := quotePrintEncode(buff, e.Html); err != nil {
				return nil, err
			}
		}
		subWriter.Close()
	}
	//Create attachment part, if necessary
	if e.Attachments != nil {
		for _, a := range e.Attachments {
			ap, err := w.CreatePart(a.Header)
			if err != nil {
				return nil, err
			}
			//Write the base64Wrapped content to the part
			base64Wrap(ap, a.Content)
		}
	}
	w.Close()
	return buff.Bytes(), nil
}

//Send an email using the given host and SMTP auth (optional), returns any error thrown by smtp.SendMail
//This function merges the To, Cc, and Bcc fields and calls the smtp.SendMail function using the Email.Bytes() output as the message
func (e *Email) Send(addr string, a smtp.Auth) error {
	//Check to make sure there is at least one recipient and one "From" address
	if e.From == "" || (len(e.To) == 0 && len(e.Cc) == 0 && len(e.Bcc) == 0) {
		return errors.New("Must specify at least one From address and one To address")
	}
	// Merge the To, Cc, and Bcc fields
	to := append(append(e.To, e.Cc...), e.Bcc...)
	from, err := mail.ParseAddress(e.From)
	if err != nil {
		return err
	}
	raw, err := e.Bytes()
	if err != nil {
		return err
	}
	return smtp.SendMail(addr, a, from.Address, to, raw)
}

//Attachment is a struct representing an email attachment.
//Based on the mime/multipart.FileHeader struct, Attachment contains the name, MIMEHeader, and content of the attachment in question
type Attachment struct {
	Filename string
	Header   textproto.MIMEHeader
	Content  []byte
}

//quotePrintEncode writes the quoted-printable text to the IO Writer (according to RFC 2045)
func quotePrintEncode(w io.Writer, s string) error {
	mc := 0
	for _, c := range s {
		// Handle the soft break for the EOL, if needed
		if mc == 75 || (!isPrintable(c) && mc+len(fmt.Sprintf("%s%X", "=", c)) > 75) {
			if _, err := fmt.Fprintf(w, "%s", "=\r\n"); err != nil {
				return err
			}
			mc = 0
		}
		//append the appropriate character
		if isPrintable(c) {
			//Printable character
			if _, err := fmt.Fprintf(w, "%s", string(c)); err != nil {
				return err
			}
			// Reset the counter if we wrote a newline
			if c == '\n' {
				mc = 0
			}
			mc++
			continue
		} else {
			//non-printable.. encode it (TODO)
			es := fmt.Sprintf("%s%X", "=", c)
			if _, err := fmt.Fprintf(w, "%s", es); err != nil {
				return err
			}
			//todo - increment correctly
			mc += len(es)
		}
	}
	return nil
}

//isPrintable returns true if the rune given is "printable" according to RFC 2045, false otherwise
func isPrintable(c rune) bool {
	return (c >= '!' && c <= '<') || (c >= '>' && c <= '~') || (c == ' ' || c == '\n' || c == '\t')
}

//base64Wrap encodeds the attachment content, and wraps it according to RFC 2045 standards (every 76 chars)
//The output is then written to the specified io.Writer
func base64Wrap(w io.Writer, b []byte) {
	encoded := base64.StdEncoding.EncodeToString(b)
	for i := 0; i < len(encoded); i += 76 {
		//Do we need to print 76 characters, or the rest of the string?
		if len(encoded)-i < 76 {
			fmt.Fprintf(w, "%s\r\n", encoded[i:])
		} else {
			fmt.Fprintf(w, "%s\r\n", encoded[i:i+76])
		}
	}
}

//headerToBytes enumerates the key and values in the header, and writes the results to the IO Writer
func headerToBytes(w io.Writer, t textproto.MIMEHeader) error {
	for k, v := range t {
		//Write the header key
		_, err := fmt.Fprintf(w, "%s:", k)
		if err != nil {
			return err
		}
		//Write each value in the header
		for _, c := range v {
			_, err := fmt.Fprintf(w, " %s\r\n", c)
			if err != nil {
				return err
			}
		}
	}
	return nil
}
<commit_msg>add error checks for previously ignored error return values<commit_after>//Package email is designed to provide an "email interface for humans."
//Designed to be robust and flexible, the email package aims to make sending email easy without getting in the way.
package email

import (
	"bytes"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/mail"
	"net/smtp"
	"net/textproto"
	"os"
	"path/filepath"
	"strings"
)

const (
	MAX_LINE_LENGTH = 76 //The maximum line length per RFC 2045
)

//Email is the type used for email messages
type Email struct {
	From        string
	To          []string
	Bcc         []string
	Cc          []string
	Subject     string
	Text        string //Plaintext message (optional)
	Html        string //Html message (optional)
	Headers     textproto.MIMEHeader
	Attachments map[string]*Attachment
	ReadReceipt []string
}

//NewEmail creates an Email, and returns the pointer to it.
func NewEmail() *Email {
	return &Email{Attachments: make(map[string]*Attachment), Headers: textproto.MIMEHeader{}}
}

//Attach is used to attach a file to the email.
//It attempts to open the file referenced by filename and, if successful, creates an Attachment.
//This Attachment is then appended to the slice of Email.Attachments.
//The function will then return the Attachment for reference, as well as nil for the error, if successful.
func (e *Email) Attach(filename string) (a *Attachment, err error) {
	//Check if the file exists, return any error
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		return nil, err
	}
	//Read the file, and set the appropriate headers
	buffer, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	e.Attachments[filename] = &Attachment{
		Filename: filename,
		Header:   textproto.MIMEHeader{},
		Content:  buffer}
	at := e.Attachments[filename]
	//Get the Content-Type to be used in the MIMEHeader
	ct := mime.TypeByExtension(filepath.Ext(filename))
	if ct != "" {
		at.Header.Set("Content-Type", ct)
	} else {
		//If the Content-Type is blank, set the Content-Type to "application/octet-stream"
		at.Header.Set("Content-Type", "application/octet-stream")
	}
	at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=\"%s\"", filename))
	at.Header.Set("Content-Transfer-Encoding", "base64")
	return e.Attachments[filename], nil
}

//Bytes converts the Email object to a []byte representation, including all needed MIMEHeaders, boundaries, etc.
func (e *Email) Bytes() ([]byte, error) {
	buff := &bytes.Buffer{}
	w := multipart.NewWriter(buff)
	//Set the appropriate headers (overwriting any conflicts)
	//Leave out Bcc (only included in envelope headers)
	e.Headers.Set("To", strings.Join(e.To, ","))
	if e.Cc != nil {
		e.Headers.Set("Cc", strings.Join(e.Cc, ","))
	}
	e.Headers.Set("From", e.From)
	e.Headers.Set("Subject", e.Subject)
	if len(e.ReadReceipt) != 0 {
		e.Headers.Set("Disposition-Notification-To", strings.Join(e.ReadReceipt, ","))
	}
	e.Headers.Set("MIME-Version", "1.0")
	e.Headers.Set("Content-Type", fmt.Sprintf("multipart/mixed;\r\n boundary=%s\r\n", w.Boundary()))

	//Write the envelope headers (including any custom headers)
	if err := headerToBytes(buff, e.Headers); err != nil {
	}
	//Start the multipart/mixed part
	fmt.Fprintf(buff, "--%s\r\n", w.Boundary())
	header := textproto.MIMEHeader{}
	//Check to see if there is a Text or HTML field
	if e.Text != "" || e.Html != "" {
		subWriter := multipart.NewWriter(buff)
		//Create the multipart alternative part
		header.Set("Content-Type", fmt.Sprintf("multipart/alternative;\r\n boundary=%s\r\n", subWriter.Boundary()))
		//Write the header
		if err := headerToBytes(buff, header); err != nil {

		}
		//Create the body sections
		if e.Text != "" {
			header.Set("Content-Type", fmt.Sprintf("text/plain; charset=UTF-8"))
			header.Set("Content-Transfer-Encoding", "quoted-printable")
			if _, err := subWriter.CreatePart(header); err != nil {
				return nil, err
			}
			// Write the text
			if err := quotePrintEncode(buff, e.Text); err != nil {
				return nil, err
			}
		}
		if e.Html != "" {
			header.Set("Content-Type", fmt.Sprintf("text/html; charset=UTF-8"))
			header.Set("Content-Transfer-Encoding", "quoted-printable")
			if _, err := subWriter.CreatePart(header); err != nil {
				return nil,err
			}
			// Write the text
			if err := quotePrintEncode(buff, e.Html); err != nil {
				return nil, err
			}
		}
		if err := subWriter.Close(); err != nil {
			return nil,err
		}
	}
	//Create attachment part, if necessary
	if e.Attachments != nil {
		for _, a := range e.Attachments {
			ap, err := w.CreatePart(a.Header)
			if err != nil {
				return nil, err
			}
			//Write the base64Wrapped content to the part
			base64Wrap(ap, a.Content)
		}
	}
	if err := w.Close(); err != nil {
		return nil,err
	}
	return buff.Bytes(), nil
}

//Send an email using the given host and SMTP auth (optional), returns any error thrown by smtp.SendMail
//This function merges the To, Cc, and Bcc fields and calls the smtp.SendMail function using the Email.Bytes() output as the message
func (e *Email) Send(addr string, a smtp.Auth) error {
	//Check to make sure there is at least one recipient and one "From" address
	if e.From == "" || (len(e.To) == 0 && len(e.Cc) == 0 && len(e.Bcc) == 0) {
		return errors.New("Must specify at least one From address and one To address")
	}
	// Merge the To, Cc, and Bcc fields
	to := append(append(e.To, e.Cc...), e.Bcc...)
	from, err := mail.ParseAddress(e.From)
	if err != nil {
		return err
	}
	raw, err := e.Bytes()
	if err != nil {
		return err
	}
	return smtp.SendMail(addr, a, from.Address, to, raw)
}

//Attachment is a struct representing an email attachment.
//Based on the mime/multipart.FileHeader struct, Attachment contains the name, MIMEHeader, and content of the attachment in question
type Attachment struct {
	Filename string
	Header   textproto.MIMEHeader
	Content  []byte
}

//quotePrintEncode writes the quoted-printable text to the IO Writer (according to RFC 2045)
func quotePrintEncode(w io.Writer, s string) error {
	mc := 0
	for _, c := range s {
		// Handle the soft break for the EOL, if needed
		if mc == 75 || (!isPrintable(c) && mc+len(fmt.Sprintf("%s%X", "=", c)) > 75) {
			if _, err := fmt.Fprintf(w, "%s", "=\r\n"); err != nil {
				return err
			}
			mc = 0
		}
		//append the appropriate character
		if isPrintable(c) {
			//Printable character
			if _, err := fmt.Fprintf(w, "%s", string(c)); err != nil {
				return err
			}
			// Reset the counter if we wrote a newline
			if c == '\n' {
				mc = 0
			}
			mc++
			continue
		} else {
			//non-printable.. encode it (TODO)
			es := fmt.Sprintf("%s%X", "=", c)
			if _, err := fmt.Fprintf(w, "%s", es); err != nil {
				return err
			}
			//todo - increment correctly
			mc += len(es)
		}
	}
	return nil
}

//isPrintable returns true if the rune given is "printable" according to RFC 2045, false otherwise
func isPrintable(c rune) bool {
	return (c >= '!' && c <= '<') || (c >= '>' && c <= '~') || (c == ' ' || c == '\n' || c == '\t')
}

//base64Wrap encodeds the attachment content, and wraps it according to RFC 2045 standards (every 76 chars)
//The output is then written to the specified io.Writer
func base64Wrap(w io.Writer, b []byte) {
	encoded := base64.StdEncoding.EncodeToString(b)
	for i := 0; i < len(encoded); i += 76 {
		//Do we need to print 76 characters, or the rest of the string?
		if len(encoded)-i < 76 {
			fmt.Fprintf(w, "%s\r\n", encoded[i:])
		} else {
			fmt.Fprintf(w, "%s\r\n", encoded[i:i+76])
		}
	}
}

//headerToBytes enumerates the key and values in the header, and writes the results to the IO Writer
func headerToBytes(w io.Writer, t textproto.MIMEHeader) error {
	for k, v := range t {
		//Write the header key
		_, err := fmt.Fprintf(w, "%s:", k)
		if err != nil {
			return err
		}
		//Write each value in the header
		for _, c := range v {
			_, err := fmt.Fprintf(w, " %s\r\n", c)
			if err != nil {
				return err
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package poet

import "bytes"

// FuncSpec represents information needed to write a function
type FuncSpec struct {
	Name             string
	Comment          string
	Parameters       []IdentifierParameter
	ResultParameters []IdentifierParameter
	Statements       []statement
}

var _ CodeBlock = (*FuncSpec)(nil)

// NewFuncSpec returns a FuncSpec with the given name
func NewFuncSpec(name string) *FuncSpec {
	return &FuncSpec{
		Name:             name,
		Parameters:       []IdentifierParameter{},
		ResultParameters: []IdentifierParameter{},
		Statements:       []statement{},
	}
}

// String returns a string representation of the function
func (f *FuncSpec) String() string {
	writer := newCodeWriter()

	if f.Comment != "" {
		writer.WriteCode("// " + f.Comment + "\n")
	}

	writer.WriteStatement(f.createSignature())

	for _, st := range f.Statements {
		writer.WriteStatement(st)
	}

	writer.WriteStatement(statement{
		BeforeIndent: -1,
		Format:       "}",
	})

	return writer.String()
}

// createSignature generates the function's signature as a statement, starting from "func" and ending with
// the opening curly brace.
func (f *FuncSpec) createSignature() statement {
	formatStr := bytes.Buffer{}
	signature, args := f.Signature()

	formatStr.WriteString("func ")
	formatStr.WriteString(signature)
	formatStr.WriteString(" {")

	return statement{
		AfterIndent: 1,
		Format:      formatStr.String(),
		Arguments:   args,
	}
}

// Signature returns a format string and slice of arguments for the function's signature, not
// including the starting "func" or opening curly brace
func (f *FuncSpec) Signature() (_ string, arguments []interface{}) {
	formatStr := bytes.Buffer{}

	formatStr.WriteString(f.Name)
	formatStr.WriteString("(")

	for i, param := range f.Parameters {
		formatStr.WriteString("$L $T")
		if param.Variadic {
			formatStr.WriteString("...")
		}

		arguments = append(arguments, param.Name, param.Type)

		if i != len(f.Parameters)-1 {
			formatStr.WriteString(", ")
		}
	}

	formatStr.WriteString(")")

	if len(f.ResultParameters) == 1 && f.ResultParameters[0].Name == "" {
		formatStr.WriteString(" $T")
		arguments = append(arguments, f.ResultParameters[0].Type)
	} else if len(f.ResultParameters) >= 1 {

		formatStr.WriteString(" (")
		for i, resultParameter := range f.ResultParameters {
			if resultParameter.Name != "" {
				formatStr.WriteString("$L ")
				arguments = append(arguments, resultParameter.Name)
			}

			formatStr.WriteString("$T")
			arguments = append(arguments, resultParameter.Type)

			if i != len(f.ResultParameters)-1 {
				formatStr.WriteString(", ")
			}
		}
		formatStr.WriteString(")")
	}

	return formatStr.String(), arguments
}

// GetImports returns a slice of imports that this function needs, including
// parameters, result parameters, and statements within the function
func (f *FuncSpec) GetImports() []Import {
	packages := []Import{}

	for _, st := range f.Statements {
		for _, arg := range st.Arguments {
			if asTypeRef, ok := arg.(TypeReference); ok {
				packages = append(packages, asTypeRef.GetImports()...)
			}
		}
	}

	for _, param := range f.Parameters {
		packages = append(packages, param.Type.GetImports()...)
	}

	for _, param := range f.ResultParameters {
		packages = append(packages, param.Type.GetImports()...)
	}

	return packages
}

// Statement is a convenient method to append a statement to the function
func (f *FuncSpec) Statement(format string, args ...interface{}) *FuncSpec {
	f.Statements = append(f.Statements, statement{
		Format:    format,
		Arguments: args,
	})

	return f
}

// BlockStart is a convenient method to append a statement that marks the start of a
// block of code.
func (f *FuncSpec) BlockStart(format string, args ...interface{}) *FuncSpec {
	f.Statements = append(f.Statements, statement{
		Format:      format + " {",
		Arguments:   args,
		AfterIndent: 1,
	})

	return f
}

// BlockEnd is a convenient method to append a statement that marks the end of a
// block of code.
func (f *FuncSpec) BlockEnd() *FuncSpec {
	f.Statements = append(f.Statements, statement{
		Format:       "}",
		BeforeIndent: -1,
	})

	return f
}

// Parameter is a convenient method to append a parameter to the function
func (f *FuncSpec) Parameter(name string, spec TypeReference) *FuncSpec {
	f.Parameters = append(f.Parameters, IdentifierParameter{
		Identifier: Identifier{
			Name: name,
			Type: spec,
		},
	})

	return f
}

// VariadicParameter is a convenient method to append a parameter to the function
func (f *FuncSpec) VariadicParameter(name string, spec TypeReference) *FuncSpec {
	f.Parameters = append(f.Parameters, IdentifierParameter{
		Identifier: Identifier{
			Name: name,
			Type: spec,
		},
		Variadic: true,
	})

	return f
}

// ResultParameter is a convenient method to append a result parameter to the function
func (f *FuncSpec) ResultParameter(name string, spec TypeReference) *FuncSpec {
	f.ResultParameters = append(f.ResultParameters, IdentifierParameter{
		Identifier: Identifier{
			Name: name,
			Type: spec,
		},
	})

	return f
}

// FunctionComment adds a comment to the function
func (f *FuncSpec) FunctionComment(comment string) *FuncSpec {
	f.Comment = comment

	return f
}
<commit_msg>Factored out duplication in FuncSpec's Signature() and added comments<commit_after>package poet

import "bytes"

// FuncSpec represents information needed to write a function
type FuncSpec struct {
	Name             string
	Comment          string
	Parameters       []IdentifierParameter
	ResultParameters []IdentifierParameter
	Statements       []statement
}

var _ CodeBlock = (*FuncSpec)(nil)

// NewFuncSpec returns a FuncSpec with the given name
func NewFuncSpec(name string) *FuncSpec {
	return &FuncSpec{
		Name:             name,
		Parameters:       []IdentifierParameter{},
		ResultParameters: []IdentifierParameter{},
		Statements:       []statement{},
	}
}

// String returns a string representation of the function
func (f *FuncSpec) String() string {
	writer := newCodeWriter()

	if f.Comment != "" {
		writer.WriteCode("// " + f.Comment + "\n")
	}

	writer.WriteStatement(f.createSignature())

	for _, st := range f.Statements {
		writer.WriteStatement(st)
	}

	writer.WriteStatement(statement{
		BeforeIndent: -1,
		Format:       "}",
	})

	return writer.String()
}

// createSignature generates the function's signature as a statement, starting from "func" and ending with
// the opening curly brace.
func (f *FuncSpec) createSignature() statement {
	formatStr := bytes.Buffer{}
	signature, args := f.Signature()

	formatStr.WriteString("func ")
	formatStr.WriteString(signature)
	formatStr.WriteString(" {")

	return statement{
		AfterIndent: 1,
		Format:      formatStr.String(),
		Arguments:   args,
	}
}

// Signature returns a format string and slice of arguments for the function's signature, not
// including the starting "func" or opening curly brace
func (f *FuncSpec) Signature() (string, []interface{}) {
	// create a buffer for the format string and a slice for the arguments to the format string
	b := bytes.Buffer{}
	arguments := []interface{}{}

	// write the function name
	b.WriteString(f.Name)
	b.WriteString("(")

	// write each parameter and collect any arguments
	format, args := writeParameters(f.Parameters)
	b.WriteString(format)
	b.WriteString(")")
	arguments = append(arguments, args...)

	format, args = writeParameters(f.ResultParameters)
	l := len(f.ResultParameters)

	// if there is only one parameter and the parameter is unnamed, do not wrap it in parens
	if l == 1 && f.ResultParameters[0].Name == "" {
		b.WriteString(" ")
		b.WriteString(format)
	} else if l >= 1 {
		b.WriteString(" (")
		b.WriteString(format)
		b.WriteString(")")
	}
	arguments = append(arguments, args...)

	return b.String(), arguments
}

func writeParameters(params []IdentifierParameter) (string, []interface{}) {
	b := bytes.Buffer{}
	args := []interface{}{}

	for i, p := range params {
		// if the argument is named, add its name to the format string
		if p.Name != "" {
			b.WriteString("$L ")
			args = append(args, p.Name)
		}

		// add its type
		b.WriteString("$T")
		args = append(args, p.Type)

		// if the argument is variadic, add the '...', will never happen for
		// result parameters
		if p.Variadic {
			b.WriteString("...")
		}

		// if its not the last parameter, add a comma
		if i != len(params)-1 {
			b.WriteString(", ")
		}
	}

	return b.String(), args
}

// GetImports returns a slice of imports that this function needs, including
// parameters, result parameters, and statements within the function
func (f *FuncSpec) GetImports() []Import {
	packages := []Import{}

	for _, st := range f.Statements {
		for _, arg := range st.Arguments {
			if asTypeRef, ok := arg.(TypeReference); ok {
				packages = append(packages, asTypeRef.GetImports()...)
			}
		}
	}

	for _, param := range f.Parameters {
		packages = append(packages, param.Type.GetImports()...)
	}

	for _, param := range f.ResultParameters {
		packages = append(packages, param.Type.GetImports()...)
	}

	return packages
}

// Statement is a convenient method to append a statement to the function
func (f *FuncSpec) Statement(format string, args ...interface{}) *FuncSpec {
	f.Statements = append(f.Statements, statement{
		Format:    format,
		Arguments: args,
	})

	return f
}

// BlockStart is a convenient method to append a statement that marks the start of a
// block of code.
func (f *FuncSpec) BlockStart(format string, args ...interface{}) *FuncSpec {
	f.Statements = append(f.Statements, statement{
		Format:      format + " {",
		Arguments:   args,
		AfterIndent: 1,
	})

	return f
}

// BlockEnd is a convenient method to append a statement that marks the end of a
// block of code.
func (f *FuncSpec) BlockEnd() *FuncSpec {
	f.Statements = append(f.Statements, statement{
		Format:       "}",
		BeforeIndent: -1,
	})

	return f
}

// Parameter is a convenient method to append a parameter to the function
func (f *FuncSpec) Parameter(name string, spec TypeReference) *FuncSpec {
	f.Parameters = append(f.Parameters, IdentifierParameter{
		Identifier: Identifier{
			Name: name,
			Type: spec,
		},
	})

	return f
}

// VariadicParameter is a convenient method to append a parameter to the function
func (f *FuncSpec) VariadicParameter(name string, spec TypeReference) *FuncSpec {
	f.Parameters = append(f.Parameters, IdentifierParameter{
		Identifier: Identifier{
			Name: name,
			Type: spec,
		},
		Variadic: true,
	})

	return f
}

// ResultParameter is a convenient method to append a result parameter to the function
func (f *FuncSpec) ResultParameter(name string, spec TypeReference) *FuncSpec {
	f.ResultParameters = append(f.ResultParameters, IdentifierParameter{
		Identifier: Identifier{
			Name: name,
			Type: spec,
		},
	})

	return f
}

// FunctionComment adds a comment to the function
func (f *FuncSpec) FunctionComment(comment string) *FuncSpec {
	f.Comment = comment

	return f
}
<|endoftext|>